summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb
+Unity Runtime codeHEADmaster
-rw-r--r--Runtime/Allocator/AllocationHeader.h159
-rw-r--r--Runtime/Allocator/BaseAllocator.cpp21
-rw-r--r--Runtime/Allocator/BaseAllocator.h81
-rw-r--r--Runtime/Allocator/DualThreadAllocator.cpp263
-rw-r--r--Runtime/Allocator/DualThreadAllocator.h55
-rw-r--r--Runtime/Allocator/DynamicHeapAllocator.cpp486
-rw-r--r--Runtime/Allocator/DynamicHeapAllocator.h81
-rw-r--r--Runtime/Allocator/FixedHeapAllocator.cpp68
-rw-r--r--Runtime/Allocator/FixedHeapAllocator.h31
-rw-r--r--Runtime/Allocator/FixedSizeAllocator.h341
-rw-r--r--Runtime/Allocator/LinearAllocator.cpp38
-rw-r--r--Runtime/Allocator/LinearAllocator.h391
-rw-r--r--Runtime/Allocator/LowLevelDefaultAllocator.cpp33
-rw-r--r--Runtime/Allocator/LowLevelDefaultAllocator.h25
-rw-r--r--Runtime/Allocator/MemoryMacros.cpp85
-rw-r--r--Runtime/Allocator/MemoryMacros.h237
-rw-r--r--Runtime/Allocator/MemoryManager.cpp1659
-rw-r--r--Runtime/Allocator/MemoryManager.h118
-rw-r--r--Runtime/Allocator/STLAllocator.h146
-rw-r--r--Runtime/Allocator/StackAllocator.cpp213
-rw-r--r--Runtime/Allocator/StackAllocator.h114
-rw-r--r--Runtime/Allocator/TLSAllocator.cpp198
-rw-r--r--Runtime/Allocator/TLSAllocator.h55
-rw-r--r--Runtime/Allocator/UnityDefaultAllocator.cpp284
-rw-r--r--Runtime/Allocator/UnityDefaultAllocator.h70
-rw-r--r--Runtime/Allocator/tlsf/tlsf.c966
-rw-r--r--Runtime/Allocator/tlsf/tlsf.h52
-rw-r--r--Runtime/Allocator/tlsf/tlsfbits.h184
-rw-r--r--Runtime/Animation/Animation.cpp2373
-rw-r--r--Runtime/Animation/Animation.h265
-rw-r--r--Runtime/Animation/AnimationBinder.cpp724
-rw-r--r--Runtime/Animation/AnimationBinder.h132
-rw-r--r--Runtime/Animation/AnimationClip.cpp1679
-rw-r--r--Runtime/Animation/AnimationClip.h346
-rw-r--r--Runtime/Animation/AnimationClipBindings.h89
-rw-r--r--Runtime/Animation/AnimationClipSettings.h75
-rw-r--r--Runtime/Animation/AnimationClipStats.h14
-rw-r--r--Runtime/Animation/AnimationClipUtility.cpp73
-rw-r--r--Runtime/Animation/AnimationClipUtility.h6
-rw-r--r--Runtime/Animation/AnimationCurveUtility.cpp1009
-rw-r--r--Runtime/Animation/AnimationCurveUtility.h67
-rw-r--r--Runtime/Animation/AnimationEvent.cpp202
-rw-r--r--Runtime/Animation/AnimationEvent.h29
-rw-r--r--Runtime/Animation/AnimationManager.cpp53
-rw-r--r--Runtime/Animation/AnimationManager.h39
-rw-r--r--Runtime/Animation/AnimationModule.jam169
-rw-r--r--Runtime/Animation/AnimationModuleRegistration.cpp53
-rw-r--r--Runtime/Animation/AnimationSetBinding.cpp563
-rw-r--r--Runtime/Animation/AnimationSetBinding.h74
-rw-r--r--Runtime/Animation/AnimationState.cpp824
-rw-r--r--Runtime/Animation/AnimationState.h269
-rw-r--r--Runtime/Animation/AnimationStateNetworkProvider.cpp51
-rw-r--r--Runtime/Animation/AnimationStateNetworkProvider.h6
-rw-r--r--Runtime/Animation/AnimationUtility.cpp271
-rw-r--r--Runtime/Animation/AnimationUtility.h17
-rw-r--r--Runtime/Animation/Animator.cpp2674
-rw-r--r--Runtime/Animation/Animator.h537
-rw-r--r--Runtime/Animation/AnimatorController.cpp693
-rw-r--r--Runtime/Animation/AnimatorController.h121
-rw-r--r--Runtime/Animation/AnimatorGenericBindings.cpp1100
-rw-r--r--Runtime/Animation/AnimatorGenericBindings.h95
-rw-r--r--Runtime/Animation/AnimatorManager.cpp110
-rw-r--r--Runtime/Animation/AnimatorManager.h28
-rw-r--r--Runtime/Animation/AnimatorOverrideController.cpp302
-rw-r--r--Runtime/Animation/AnimatorOverrideController.h97
-rw-r--r--Runtime/Animation/Avatar.cpp678
-rw-r--r--Runtime/Animation/Avatar.h149
-rw-r--r--Runtime/Animation/AvatarBuilder.cpp807
-rw-r--r--Runtime/Animation/AvatarBuilder.h256
-rw-r--r--Runtime/Animation/AvatarPlayback.cpp118
-rw-r--r--Runtime/Animation/AvatarPlayback.h51
-rw-r--r--Runtime/Animation/BaseAnimationTrack.cpp15
-rw-r--r--Runtime/Animation/BaseAnimationTrack.h21
-rw-r--r--Runtime/Animation/BoundCurve.h51
-rw-r--r--Runtime/Animation/BoundCurveDeprecated.h60
-rw-r--r--Runtime/Animation/CalculateAnimatorSkinMatrices.cpp85
-rw-r--r--Runtime/Animation/CalculateAnimatorSkinMatrices.h18
-rw-r--r--Runtime/Animation/CharacterTestFixture.h202
-rw-r--r--Runtime/Animation/DenseClipBuilder.cpp97
-rw-r--r--Runtime/Animation/DenseClipBuilder.h9
-rw-r--r--Runtime/Animation/EditorCurveBinding.h34
-rw-r--r--Runtime/Animation/GenericAnimationBindingCache.cpp839
-rw-r--r--Runtime/Animation/GenericAnimationBindingCache.h118
-rw-r--r--Runtime/Animation/KeyframeReducer.cpp359
-rw-r--r--Runtime/Animation/KeyframeReducer.h10
-rw-r--r--Runtime/Animation/MecanimAnimation.cpp78
-rw-r--r--Runtime/Animation/MecanimAnimation.h23
-rw-r--r--Runtime/Animation/MecanimArraySerialization.h178
-rw-r--r--Runtime/Animation/MecanimClipBuilder.cpp349
-rw-r--r--Runtime/Animation/MecanimClipBuilder.h128
-rw-r--r--Runtime/Animation/MecanimUtility.cpp45
-rw-r--r--Runtime/Animation/MecanimUtility.h123
-rw-r--r--Runtime/Animation/Motion.cpp28
-rw-r--r--Runtime/Animation/Motion.h42
-rw-r--r--Runtime/Animation/NewAnimationTrack.cpp80
-rw-r--r--Runtime/Animation/NewAnimationTrack.h74
-rw-r--r--Runtime/Animation/OptimizeTransformHierarchy.cpp303
-rw-r--r--Runtime/Animation/OptimizeTransformHierarchy.h50
-rw-r--r--Runtime/Animation/OptimizeTransformHierarchyTests.cpp293
-rw-r--r--Runtime/Animation/PPtrKeyframes.h12
-rw-r--r--Runtime/Animation/RuntimeAnimatorController.cpp66
-rw-r--r--Runtime/Animation/RuntimeAnimatorController.h81
-rw-r--r--Runtime/Animation/ScriptBindings/Animations.txt721
-rw-r--r--Runtime/Animation/ScriptBindings/AnimatorBindings.txt677
-rw-r--r--Runtime/Animation/ScriptBindings/AnimatorOverrideControllerBindings.txt138
-rw-r--r--Runtime/Animation/ScriptBindings/Avatar.txt340
-rw-r--r--Runtime/Animation/ScriptBindings/AvatarBuilderBindings.txt250
-rw-r--r--Runtime/Animation/ScriptBindings/RuntimeAnimatorControllerBindings.txt26
-rw-r--r--Runtime/Animation/StreamedClipBuilder.cpp272
-rw-r--r--Runtime/Animation/StreamedClipBuilder.h16
-rw-r--r--Runtime/Audio/AudioBehaviour.cpp16
-rw-r--r--Runtime/Audio/AudioBehaviour.h21
-rw-r--r--Runtime/Audio/AudioChorusFilter.cpp86
-rw-r--r--Runtime/Audio/AudioChorusFilter.h51
-rw-r--r--Runtime/Audio/AudioClip.Callbacks.cpp270
-rw-r--r--Runtime/Audio/AudioClip.cpp1346
-rw-r--r--Runtime/Audio/AudioClip.h10
-rw-r--r--Runtime/Audio/AudioClip_FMOD.h347
-rw-r--r--Runtime/Audio/AudioClip_Flash.cpp122
-rw-r--r--Runtime/Audio/AudioClip_Flash.h73
-rw-r--r--Runtime/Audio/AudioCustomFilter.cpp210
-rw-r--r--Runtime/Audio/AudioCustomFilter.h68
-rw-r--r--Runtime/Audio/AudioDistortionFilter.cpp52
-rw-r--r--Runtime/Audio/AudioDistortionFilter.h26
-rw-r--r--Runtime/Audio/AudioEchoFilter.cpp72
-rw-r--r--Runtime/Audio/AudioEchoFilter.h41
-rw-r--r--Runtime/Audio/AudioHighPassFilter.cpp61
-rw-r--r--Runtime/Audio/AudioHighPassFilter.h33
-rw-r--r--Runtime/Audio/AudioListener.cpp177
-rw-r--r--Runtime/Audio/AudioListener.h62
-rw-r--r--Runtime/Audio/AudioLowPassFilter.cpp111
-rw-r--r--Runtime/Audio/AudioLowPassFilter.h41
-rw-r--r--Runtime/Audio/AudioManager.Callbacks.cpp37
-rw-r--r--Runtime/Audio/AudioManager.cpp1773
-rw-r--r--Runtime/Audio/AudioManager.h296
-rw-r--r--Runtime/Audio/AudioModule.cpp142
-rw-r--r--Runtime/Audio/AudioModule.jam137
-rw-r--r--Runtime/Audio/AudioModuleRegistration.cpp65
-rw-r--r--Runtime/Audio/AudioParameters.h48
-rw-r--r--Runtime/Audio/AudioReverbFilter.cpp194
-rw-r--r--Runtime/Audio/AudioReverbFilter.h96
-rw-r--r--Runtime/Audio/AudioReverbZone.cpp260
-rw-r--r--Runtime/Audio/AudioReverbZone.h109
-rw-r--r--Runtime/Audio/AudioScriptBufferManager.cpp138
-rw-r--r--Runtime/Audio/AudioScriptBufferManager.h48
-rw-r--r--Runtime/Audio/AudioSource.Callbacks.cpp100
-rw-r--r--Runtime/Audio/AudioSource.cpp1738
-rw-r--r--Runtime/Audio/AudioSource.h351
-rw-r--r--Runtime/Audio/AudioSourceFilter.cpp68
-rw-r--r--Runtime/Audio/AudioSourceFilter.h36
-rw-r--r--Runtime/Audio/AudioTypes.h16
-rw-r--r--Runtime/Audio/OggReader.h51
-rw-r--r--Runtime/Audio/ScriptBindings/AudioBindings.txt996
-rw-r--r--Runtime/Audio/Utilities/Conversion.h291
-rw-r--r--Runtime/Audio/WavReader.h182
-rw-r--r--Runtime/Audio/correct_fmod_includer.h39
-rw-r--r--Runtime/BaseClasses/BaseObject.cpp1393
-rw-r--r--Runtime/BaseClasses/BaseObject.h1162
-rw-r--r--Runtime/BaseClasses/BitField.h26
-rw-r--r--Runtime/BaseClasses/ClassIDs.h240
-rw-r--r--Runtime/BaseClasses/ClassRegistration.cpp267
-rw-r--r--Runtime/BaseClasses/ClassRegistration.h22
-rw-r--r--Runtime/BaseClasses/CleanupManager.cpp65
-rw-r--r--Runtime/BaseClasses/CleanupManager.h37
-rw-r--r--Runtime/BaseClasses/Cursor.cpp152
-rw-r--r--Runtime/BaseClasses/Cursor.h105
-rw-r--r--Runtime/BaseClasses/EditorExtension.cpp95
-rw-r--r--Runtime/BaseClasses/EditorExtension.h58
-rw-r--r--Runtime/BaseClasses/EventIDs.h9
-rw-r--r--Runtime/BaseClasses/EventManager.cpp216
-rw-r--r--Runtime/BaseClasses/EventManager.h59
-rw-r--r--Runtime/BaseClasses/GameManager.cpp58
-rw-r--r--Runtime/BaseClasses/GameManager.h69
-rw-r--r--Runtime/BaseClasses/GameObject.cpp1189
-rw-r--r--Runtime/BaseClasses/GameObject.h535
-rw-r--r--Runtime/BaseClasses/GameObjectTests.cpp274
-rw-r--r--Runtime/BaseClasses/IsPlaying.cpp14
-rw-r--r--Runtime/BaseClasses/IsPlaying.h9
-rw-r--r--Runtime/BaseClasses/ManagerContext.cpp107
-rw-r--r--Runtime/BaseClasses/ManagerContext.h61
-rw-r--r--Runtime/BaseClasses/ManagerContextLoading.cpp285
-rw-r--r--Runtime/BaseClasses/ManagerContextLoading.h17
-rw-r--r--Runtime/BaseClasses/MessageHandler.cpp196
-rw-r--r--Runtime/BaseClasses/MessageHandler.h112
-rw-r--r--Runtime/BaseClasses/MessageIdentifier.cpp86
-rw-r--r--Runtime/BaseClasses/MessageIdentifier.h109
-rw-r--r--Runtime/BaseClasses/MessageIdentifiers.h86
-rw-r--r--Runtime/BaseClasses/NamedObject.cpp33
-rw-r--r--Runtime/BaseClasses/NamedObject.h23
-rw-r--r--Runtime/BaseClasses/ObjectDefines.h202
-rw-r--r--Runtime/BaseClasses/RefCounted.h36
-rw-r--r--Runtime/BaseClasses/SupportedMessageOptimization.h17
-rw-r--r--Runtime/BaseClasses/Tags.cpp604
-rw-r--r--Runtime/BaseClasses/Tags.h104
-rw-r--r--Runtime/BaseClasses/TagsTests.cpp109
-rw-r--r--Runtime/Camera/BaseRenderer.cpp158
-rw-r--r--Runtime/Camera/BaseRenderer.h148
-rw-r--r--Runtime/Camera/Camera.cpp2340
-rw-r--r--Runtime/Camera/Camera.h503
-rw-r--r--Runtime/Camera/CameraCullingParameters.h36
-rw-r--r--Runtime/Camera/CameraUtil.cpp452
-rw-r--r--Runtime/Camera/CameraUtil.h61
-rw-r--r--Runtime/Camera/CullResults.cpp81
-rw-r--r--Runtime/Camera/CullResults.h153
-rw-r--r--Runtime/Camera/Culler.cpp261
-rw-r--r--Runtime/Camera/Culler.h16
-rw-r--r--Runtime/Camera/CullingParameters.h140
-rw-r--r--Runtime/Camera/Flare.cpp611
-rw-r--r--Runtime/Camera/Flare.h169
-rw-r--r--Runtime/Camera/GraphicsSettings.cpp79
-rw-r--r--Runtime/Camera/GraphicsSettings.h48
-rw-r--r--Runtime/Camera/HaloManager.cpp282
-rw-r--r--Runtime/Camera/HaloManager.h69
-rw-r--r--Runtime/Camera/ImageFilters.cpp613
-rw-r--r--Runtime/Camera/ImageFilters.h46
-rw-r--r--Runtime/Camera/IntermediateRenderer.cpp284
-rw-r--r--Runtime/Camera/IntermediateRenderer.h148
-rw-r--r--Runtime/Camera/IntermediateUsers.cpp21
-rw-r--r--Runtime/Camera/IntermediateUsers.h26
-rw-r--r--Runtime/Camera/LODGroup.cpp395
-rw-r--r--Runtime/Camera/LODGroup.h105
-rw-r--r--Runtime/Camera/LODGroupManager.cpp462
-rw-r--r--Runtime/Camera/LODGroupManager.h116
-rw-r--r--Runtime/Camera/Light.cpp668
-rw-r--r--Runtime/Camera/Light.h254
-rw-r--r--Runtime/Camera/LightCulling.cpp669
-rw-r--r--Runtime/Camera/LightCulling.h7
-rw-r--r--Runtime/Camera/LightManager.cpp635
-rw-r--r--Runtime/Camera/LightManager.h62
-rw-r--r--Runtime/Camera/LightProbes.cpp350
-rw-r--r--Runtime/Camera/LightProbes.h98
-rw-r--r--Runtime/Camera/LightTypes.h28
-rw-r--r--Runtime/Camera/Lighting.h34
-rw-r--r--Runtime/Camera/OcclusionArea.cpp67
-rw-r--r--Runtime/Camera/OcclusionArea.h46
-rw-r--r--Runtime/Camera/OcclusionPortal.cpp60
-rw-r--r--Runtime/Camera/OcclusionPortal.h37
-rw-r--r--Runtime/Camera/Projector.cpp313
-rw-r--r--Runtime/Camera/Projector.h74
-rw-r--r--Runtime/Camera/RenderLayers/GUIElement.cpp31
-rw-r--r--Runtime/Camera/RenderLayers/GUIElement.h30
-rw-r--r--Runtime/Camera/RenderLayers/GUILayer.cpp104
-rw-r--r--Runtime/Camera/RenderLayers/GUILayer.h33
-rw-r--r--Runtime/Camera/RenderLayers/GUIText.cpp303
-rw-r--r--Runtime/Camera/RenderLayers/GUIText.h93
-rw-r--r--Runtime/Camera/RenderLayers/GUITexture.cpp524
-rw-r--r--Runtime/Camera/RenderLayers/GUITexture.h73
-rw-r--r--Runtime/Camera/RenderLoops/BuiltinShaderParamUtility.cpp19
-rw-r--r--Runtime/Camera/RenderLoops/BuiltinShaderParamUtility.h11
-rw-r--r--Runtime/Camera/RenderLoops/ForwardShaderRenderLoop.cpp1403
-rw-r--r--Runtime/Camera/RenderLoops/ForwardVertexRenderLoop.cpp637
-rw-r--r--Runtime/Camera/RenderLoops/GlobalLayeringData.h26
-rw-r--r--Runtime/Camera/RenderLoops/PrePassRenderLoop.cpp1958
-rw-r--r--Runtime/Camera/RenderLoops/RenderLoop.h25
-rw-r--r--Runtime/Camera/RenderLoops/RenderLoopEnums.h29
-rw-r--r--Runtime/Camera/RenderLoops/RenderLoopPrivate.cpp469
-rw-r--r--Runtime/Camera/RenderLoops/RenderLoopPrivate.h86
-rw-r--r--Runtime/Camera/RenderLoops/ReplacementRenderLoop.cpp245
-rw-r--r--Runtime/Camera/RenderLoops/ReplacementRenderLoop.h11
-rw-r--r--Runtime/Camera/RenderManager.cpp272
-rw-r--r--Runtime/Camera/RenderManager.h83
-rw-r--r--Runtime/Camera/RenderSettings.cpp262
-rw-r--r--Runtime/Camera/RenderSettings.h94
-rw-r--r--Runtime/Camera/Renderable.h30
-rw-r--r--Runtime/Camera/Renderqueue.cpp268
-rw-r--r--Runtime/Camera/Renderqueue.h23
-rw-r--r--Runtime/Camera/SceneCulling.cpp412
-rw-r--r--Runtime/Camera/SceneCulling.h18
-rw-r--r--Runtime/Camera/SceneNode.h23
-rw-r--r--Runtime/Camera/SceneSettings.cpp140
-rw-r--r--Runtime/Camera/SceneSettings.h94
-rw-r--r--Runtime/Camera/ShaderReplaceData.h14
-rw-r--r--Runtime/Camera/ShadowCulling.cpp885
-rw-r--r--Runtime/Camera/ShadowCulling.h121
-rw-r--r--Runtime/Camera/ShadowSettings.cpp14
-rw-r--r--Runtime/Camera/ShadowSettings.h33
-rw-r--r--Runtime/Camera/Shadows.cpp1227
-rw-r--r--Runtime/Camera/Shadows.h48
-rw-r--r--Runtime/Camera/Skybox.cpp199
-rw-r--r--Runtime/Camera/Skybox.h27
-rw-r--r--Runtime/Camera/UmbraBackwardsCompatibility.cpp37
-rw-r--r--Runtime/Camera/UmbraBackwardsCompatibility.h85
-rw-r--r--Runtime/Camera/UmbraBackwardsCompatibilityDefine.h4
-rw-r--r--Runtime/Camera/UmbraTomeData.h22
-rw-r--r--Runtime/Camera/UnityScene.cpp505
-rw-r--r--Runtime/Camera/UnityScene.h154
-rw-r--r--Runtime/ClusterRenderer/ClusterNetwork.cpp118
-rw-r--r--Runtime/ClusterRenderer/ClusterNetwork.h40
-rw-r--r--Runtime/ClusterRenderer/ClusterNode.cpp128
-rw-r--r--Runtime/ClusterRenderer/ClusterNode.h64
-rw-r--r--Runtime/ClusterRenderer/ClusterRendererDefines.h13
-rw-r--r--Runtime/ClusterRenderer/ClusterRendererModule.cpp109
-rw-r--r--Runtime/ClusterRenderer/ClusterRendererModule.h32
-rw-r--r--Runtime/ClusterRenderer/ClusterRendererModule.jam177
-rw-r--r--Runtime/ClusterRenderer/ClusterRendererModuleRegistration.cpp10
-rw-r--r--Runtime/ClusterRenderer/ClusterTransfer.cpp93
-rw-r--r--Runtime/ClusterRenderer/ClusterTransfer.h16
-rw-r--r--Runtime/Containers/ConstantString.cpp123
-rw-r--r--Runtime/Containers/ConstantString.h82
-rw-r--r--Runtime/Containers/ConstantStringManager.cpp91
-rw-r--r--Runtime/Containers/ConstantStringManager.h38
-rw-r--r--Runtime/Containers/ConstantStringSerialization.h18
-rw-r--r--Runtime/Containers/ExtendedRingbuffer.h146
-rw-r--r--Runtime/Containers/GrowingRingbuffer.h121
-rw-r--r--Runtime/Containers/Ringbuffer.h94
-rw-r--r--Runtime/Containers/TransactionalRingbuffer.h37
-rw-r--r--Runtime/Core/Callbacks/CallbackArray.cpp40
-rw-r--r--Runtime/Core/Callbacks/CallbackArray.h21
-rw-r--r--Runtime/Core/Callbacks/GlobalCallbacks.cpp9
-rw-r--r--Runtime/Core/Callbacks/GlobalCallbacks.h30
-rw-r--r--Runtime/Core/Callbacks/PlayerLoopCallbacks.cpp9
-rw-r--r--Runtime/Core/Callbacks/PlayerLoopCallbacks.h39
-rw-r--r--Runtime/Dynamics/BoxCollider.cpp256
-rw-r--r--Runtime/Dynamics/BoxCollider.h45
-rw-r--r--Runtime/Dynamics/CapsuleCollider.cpp369
-rw-r--r--Runtime/Dynamics/CapsuleCollider.h59
-rw-r--r--Runtime/Dynamics/CharacterController.cpp567
-rw-r--r--Runtime/Dynamics/CharacterController.h112
-rw-r--r--Runtime/Dynamics/CharacterJoint.cpp340
-rw-r--r--Runtime/Dynamics/CharacterJoint.h83
-rw-r--r--Runtime/Dynamics/Cloth.cpp402
-rw-r--r--Runtime/Dynamics/Cloth.h101
-rw-r--r--Runtime/Dynamics/ClothRenderer.cpp273
-rw-r--r--Runtime/Dynamics/ClothRenderer.h54
-rw-r--r--Runtime/Dynamics/Collider.cpp598
-rw-r--r--Runtime/Dynamics/Collider.h168
-rw-r--r--Runtime/Dynamics/CollisionMeshData.cpp121
-rw-r--r--Runtime/Dynamics/CollisionMeshData.h87
-rw-r--r--Runtime/Dynamics/ConfigurableJoint.cpp479
-rw-r--r--Runtime/Dynamics/ConfigurableJoint.h180
-rw-r--r--Runtime/Dynamics/ConstantForce.cpp66
-rw-r--r--Runtime/Dynamics/ConstantForce.h30
-rw-r--r--Runtime/Dynamics/DeformableMesh.cpp721
-rw-r--r--Runtime/Dynamics/DeformableMesh.h146
-rw-r--r--Runtime/Dynamics/ExtractDataFromMesh.cpp56
-rw-r--r--Runtime/Dynamics/ExtractDataFromMesh.h8
-rw-r--r--Runtime/Dynamics/FixedJoint.cpp61
-rw-r--r--Runtime/Dynamics/FixedJoint.h32
-rw-r--r--Runtime/Dynamics/HingeJoint.cpp280
-rw-r--r--Runtime/Dynamics/HingeJoint.h69
-rw-r--r--Runtime/Dynamics/Joint.cpp325
-rw-r--r--Runtime/Dynamics/Joint.h148
-rw-r--r--Runtime/Dynamics/JointDescriptions.h215
-rw-r--r--Runtime/Dynamics/Joints.h6
-rw-r--r--Runtime/Dynamics/MeshCollider.cpp326
-rw-r--r--Runtime/Dynamics/MeshCollider.h63
-rw-r--r--Runtime/Dynamics/NxMeshCreation.cpp115
-rw-r--r--Runtime/Dynamics/NxMeshCreation.h11
-rw-r--r--Runtime/Dynamics/NxWrapperUtility.h14
-rw-r--r--Runtime/Dynamics/PhysXRaycast.cpp195
-rw-r--r--Runtime/Dynamics/PhysXRaycast.h42
-rw-r--r--Runtime/Dynamics/PhysicMaterial.cpp340
-rw-r--r--Runtime/Dynamics/PhysicMaterial.h99
-rw-r--r--Runtime/Dynamics/PhysicsManager.cpp1508
-rw-r--r--Runtime/Dynamics/PhysicsManager.h235
-rw-r--r--Runtime/Dynamics/PhysicsModule.cpp141
-rw-r--r--Runtime/Dynamics/PhysicsModule.h3
-rw-r--r--Runtime/Dynamics/PhysicsModule.jam108
-rw-r--r--Runtime/Dynamics/PhysicsModuleRegistration.cpp67
-rw-r--r--Runtime/Dynamics/PhysicsTest.cpp93
-rw-r--r--Runtime/Dynamics/PrimitiveCollider.h7
-rw-r--r--Runtime/Dynamics/RaycastCollider.cpp212
-rw-r--r--Runtime/Dynamics/RaycastCollider.h49
-rw-r--r--Runtime/Dynamics/RaycastHit.cpp48
-rw-r--r--Runtime/Dynamics/RaycastHit.h6
-rw-r--r--Runtime/Dynamics/RigidBody.h266
-rw-r--r--Runtime/Dynamics/Rigidbody.cpp1097
-rw-r--r--Runtime/Dynamics/ScriptBindings/NewDynamics.txt1797
-rw-r--r--Runtime/Dynamics/SkinnedCloth.cpp347
-rw-r--r--Runtime/Dynamics/SkinnedCloth.h82
-rw-r--r--Runtime/Dynamics/SphereCollider.cpp272
-rw-r--r--Runtime/Dynamics/SphereCollider.h56
-rw-r--r--Runtime/Dynamics/SpringJoint.cpp161
-rw-r--r--Runtime/Dynamics/SpringJoint.h51
-rw-r--r--Runtime/Dynamics/TerrainCollider.cpp238
-rw-r--r--Runtime/Dynamics/TerrainCollider.h46
-rw-r--r--Runtime/Dynamics/WheelCollider.cpp464
-rw-r--r--Runtime/Dynamics/WheelCollider.h142
-rw-r--r--Runtime/Dynamics/nxmemorystream.cpp134
-rw-r--r--Runtime/Dynamics/nxmemorystream.h34
-rw-r--r--Runtime/Export/AndroidInput.txt85
-rw-r--r--Runtime/Export/AndroidJNI.txt918
-rw-r--r--Runtime/Export/AndroidJNISafe.cs514
-rw-r--r--Runtime/Export/AndroidJava.txt114
-rw-r--r--Runtime/Export/AndroidJavaImpl.cs1147
-rw-r--r--Runtime/Export/AssemblyInfo.cs15
-rw-r--r--Runtime/Export/AssetBundleBindings.txt175
-rw-r--r--Runtime/Export/AttributeHelperEngine.cs84
-rw-r--r--Runtime/Export/BaseClass.txt1174
-rw-r--r--Runtime/Export/CanConvertToFlash.cs17
-rw-r--r--Runtime/Export/ClassLibraryInitializer.cs17
-rw-r--r--Runtime/Export/Coroutines.cs36
-rw-r--r--Runtime/Export/CppAttributes.cs52
-rw-r--r--Runtime/Export/CrashReporter.txt150
-rw-r--r--Runtime/Export/CursorBindings.txt44
-rw-r--r--Runtime/Export/EventBindings.txt1066
-rw-r--r--Runtime/Export/FlashHelper.cs32
-rw-r--r--Runtime/Export/GUI.txt1723
-rw-r--r--Runtime/Export/GUIContentBindings.txt132
-rw-r--r--Runtime/Export/GUILayout.txt517
-rw-r--r--Runtime/Export/GUILayoutUtility.txt1420
-rw-r--r--Runtime/Export/GUISkinBindings.txt341
-rw-r--r--Runtime/Export/GUIStateObjects.cs36
-rw-r--r--Runtime/Export/GUIStyleBindings.txt922
-rw-r--r--Runtime/Export/GUIUtility.txt476
-rw-r--r--Runtime/Export/GameCenterServices.cs451
-rw-r--r--Runtime/Export/GameCenterServices.txt367
-rw-r--r--Runtime/Export/GameObjectExport.cpp89
-rw-r--r--Runtime/Export/GameObjectExport.h17
-rw-r--r--Runtime/Export/GizmoBindings.txt180
-rw-r--r--Runtime/Export/GradientBindings.txt231
-rw-r--r--Runtime/Export/GradientUtility.txt30
-rw-r--r--Runtime/Export/Graphics.txt3325
-rw-r--r--Runtime/Export/GraphicsEnums.cs185
-rw-r--r--Runtime/Export/Handheld.txt424
-rw-r--r--Runtime/Export/ImplementedInActionScript.cs20
-rw-r--r--Runtime/Export/Internal/DefaultValueAttribute.cs43
-rw-r--r--Runtime/Export/Internal/ExcludeFromDocs.cs16
-rw-r--r--Runtime/Export/JPEGMemsrc.c174
-rw-r--r--Runtime/Export/JPEGMemsrc.h16
-rw-r--r--Runtime/Export/LODBindings.txt117
-rw-r--r--Runtime/Export/LightProbeBindings.txt35
-rw-r--r--Runtime/Export/LocalService.cs655
-rw-r--r--Runtime/Export/Math.txt2391
-rw-r--r--Runtime/Export/MonoICallRegistration.cpp343
-rw-r--r--Runtime/Export/MonoICallRegistration.h2
-rw-r--r--Runtime/Export/MotionBindings.txt43
-rw-r--r--Runtime/Export/MouseEvents.cs187
-rw-r--r--Runtime/Export/NetworkServices.cs231
-rw-r--r--Runtime/Export/Networking.txt921
-rw-r--r--Runtime/Export/ParticleSystemBindings.txt413
-rw-r--r--Runtime/Export/PlayerPrefsBindings.txt115
-rw-r--r--Runtime/Export/PropertyAttribute.cs46
-rw-r--r--Runtime/Export/ScriptAssets.txt33
-rw-r--r--Runtime/Export/ScriptRefImages/AlignPosition.pngbin0 -> 18116 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/Aligner.pngbin0 -> 19091 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/ArrowCap.pngbin0 -> 60793 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/BeginEndGUI.pngbin0 -> 36201 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/BeginEndHorizontalExample.pngbin0 -> 13705 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/BeginEndScrollView.pngbin0 -> 18117 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/BeginEndVerticalExample.pngbin0 -> 13698 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/ButtonHandle.pngbin0 -> 43850 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/CameraViewer.pngbin0 -> 42845 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/CircleCap.pngbin0 -> 66261 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/ClearEditorPrefs.pngbin0 -> 34207 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/CloneObjects.pngbin0 -> 20989 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/ConeCap.pngbin0 -> 54854 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/CubeCap.pngbin0 -> 60841 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/CustomEditor.pngbin0 -> 28191 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/CustomPropertyDrawer_Class.pngbin0 -> 36867 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/CylinderCap.pngbin0 -> 63220 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/DiscHandle.pngbin0 -> 41855 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/DotCap.pngbin0 -> 59784 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/DrawAAPolyLine.pngbin0 -> 72807 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/DrawBezier.pngbin0 -> 49656 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/DrawLine.pngbin0 -> 71823 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/DrawPolyLine.pngbin0 -> 72807 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/DrawSolidArc.pngbin0 -> 55909 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/DrawSolidDisc.pngbin0 -> 67116 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/DrawSolidRectangle.pngbin0 -> 63536 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/DrawWireArc.pngbin0 -> 50461 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/DrawWireDisc.pngbin0 -> 73127 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIActionKey.pngbin0 -> 15025 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIBoundsField.pngbin0 -> 10684 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIColorField.pngbin0 -> 32652 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUICurveField.pngbin0 -> 29026 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIDrawPreviewTexture.pngbin0 -> 30330 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIDrawTextureAlpha.pngbin0 -> 30353 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIDropShadowLabel.pngbin0 -> 16506 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIEnumPopup.pngbin0 -> 20504 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIFloatField.pngbin0 -> 14235 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIFoldout.pngbin0 -> 15541 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIIndent.pngbin0 -> 19786 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIInspectorTitlebar.pngbin0 -> 24330 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIIntField.pngbin0 -> 14576 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIIntPopup.pngbin0 -> 20652 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIIntSlider.pngbin0 -> 16487 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILabelField.pngbin0 -> 14695 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILayerField.pngbin0 -> 24627 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILayoutEnumPopup.pngbin0 -> 16764 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILayoutFloatField.pngbin0 -> 12075 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILayoutIntField.pngbin0 -> 12655 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILayoutIntPopup.pngbin0 -> 16658 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILayoutIntSlider.pngbin0 -> 13053 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILayoutLabel.pngbin0 -> 13775 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILayoutLayerField.pngbin0 -> 20580 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILayoutMinMaxSlider.pngbin0 -> 13310 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILayoutPasswordField.pngbin0 -> 13807 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILayoutPopup.pngbin0 -> 14219 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILayoutSlider.pngbin0 -> 12068 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILayoutTagField.pngbin0 -> 23025 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILayoutTextArea.pngbin0 -> 26314 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILayoutTextField.pngbin0 -> 14966 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILayoutToggle.pngbin0 -> 14949 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILayoutVector2Field.pngbin0 -> 15667 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUILayoutVector3Field.pngbin0 -> 27550 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIMinMaxSlider.pngbin0 -> 17985 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIObjectField.pngbin0 -> 18003 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIPasswordField.pngbin0 -> 16026 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIPopup.pngbin0 -> 23399 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIPrefixLabel.pngbin0 -> 16607 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIProgressBar.pngbin0 -> 16360 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIRectField.pngbin0 -> 24963 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUISlider.pngbin0 -> 13723 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUITagField.pngbin0 -> 27881 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUITextArea.pngbin0 -> 12884 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUITextField.pngbin0 -> 19733 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIToggle.pngbin0 -> 17750 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIUtilityFindTexture.pngbin0 -> 13278 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIUtilityLookLikeControls.pngbin0 -> 16652 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIUtilityLookLikeInspector.pngbin0 -> 16455 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIUtilityObjectContent.pngbin0 -> 15104 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIUtilitySystemCopyBuffer.pngbin0 -> 28339 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIUtilityWhiteTexture.pngbin0 -> 18230 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIVector2Field.pngbin0 -> 24026 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIVector3Field.pngbin0 -> 23978 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorGUIVector4Field.pngbin0 -> 23850 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorPrefsBool.pngbin0 -> 15730 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorUtility CreateGameObjectWithHideFlags.pngbin0 -> 21586 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorUtilityCollectDependencies.pngbin0 -> 15841 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorUtilityDisplayCancelableProgressBar.pngbin0 -> 21039 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorUtilityDisplayDialogComplex.pngbin0 -> 37350 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorUtilityDisplayProgressBar.pngbin0 -> 20575 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorUtilityFocusProjectWindow.pngbin0 -> 13200 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorUtilityInstanceIDToObject.pngbin0 -> 13013 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorUtilityOpenFilePanel.pngbin0 -> 53843 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorUtilityOpenFolderPanel.pngbin0 -> 53185 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorUtilitySaveFilePanel.pngbin0 -> 53944 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorUtilitySaveFilePanelInProject.pngbin0 -> 58500 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorUtilitySaveFolderPanel.pngbin0 -> 54281 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorUtilitySetSelectedWireframeHidden.pngbin0 -> 20081 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/EditorWindowPosition.pngbin0 -> 11423 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/ErrorString.pngbin0 -> 18382 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/FinishCompiling.pngbin0 -> 11581 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/FoldoutUsage.pngbin0 -> 13450 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/FollowCurve.pngbin0 -> 29920 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/ForceSync.pngbin0 -> 11056 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/FreeMoveHandle.pngbin0 -> 52962 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/FreeRotateHandle.pngbin0 -> 27475 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUIBackgroundColor.pngbin0 -> 16212 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUIColor.pngbin0 -> 18710 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUIContentColor.pngbin0 -> 16402 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUIDepth.pngbin0 -> 21701 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUIEnabled.pngbin0 -> 27247 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILabel.pngbin0 -> 15177 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILabelTexture.pngbin0 -> 18603 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutArea.pngbin0 -> 19605 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutBox.pngbin0 -> 18873 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutButton.pngbin0 -> 20507 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutFlexibleSpace.pngbin0 -> 19590 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutHeight.pngbin0 -> 17242 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutHorizontal.pngbin0 -> 18340 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutHorizontalScrollBar.pngbin0 -> 17888 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutHorizontalSlider.pngbin0 -> 17643 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutLabel.pngbin0 -> 18917 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutMaxHeight.pngbin0 -> 16382 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutMaxWidth.pngbin0 -> 16363 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutMinHeight.pngbin0 -> 16342 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutMinWidth.pngbin0 -> 16339 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutPasswordField.pngbin0 -> 13734 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutScrollView.pngbin0 -> 22942 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutSelectionGrid.pngbin0 -> 19906 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutSpace.pngbin0 -> 18708 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutTextArea.pngbin0 -> 16242 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutTextField.pngbin0 -> 14765 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutToggle.pngbin0 -> 20746 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutToolbar.pngbin0 -> 17152 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutVertical.pngbin0 -> 19377 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutVerticalScrollBar.pngbin0 -> 16665 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutVerticalSlider.pngbin0 -> 16369 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutWidth.pngbin0 -> 16819 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUILayoutWindow.pngbin0 -> 21610 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUITooltip.pngbin0 -> 20145 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUIWindowDemo.pngbin0 -> 15852 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GUIWindowDemo2.pngbin0 -> 19494 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GetWindowEx.pngbin0 -> 12293 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/GetWindowRectEx.pngbin0 -> 11271 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/HandlesLabel.pngbin0 -> 49741 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/HelpString.pngbin0 -> 13773 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/InspectorTitlebarUsage.pngbin0 -> 20943 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/InspectorTitlebarUsageSpace.pngbin0 -> 20704 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/LeftHandRuleDiagram.pngbin0 -> 49228 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/MaskField.pngbin0 -> 2856 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/MassiveColorChange.pngbin0 -> 36686 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/MaterialPropertyDrawer_UI.pngbin0 -> 22400 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/ModifyQuaternionDirectly.pngbin0 -> 16701 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/MoveResizeSelectedWindow.pngbin0 -> 17520 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/OrthographicPreviewer.pngbin0 -> 48746 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/PerformVariousRedo.pngbin0 -> 17929 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/PerformVariousUndo.pngbin0 -> 18186 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/PerlinExample.pngbin0 -> 59956 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/PlaceSelectionOnSurface.pngbin0 -> 31106 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/Plane3Points.pngbin0 -> 9021 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/PlaneNormalOffset.pngbin0 -> 34277 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/PlaneNormalOrigin.pngbin0 -> 35505 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/PlayerSettingsCustomSettings.pngbin0 -> 23111 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/PositionHandle.pngbin0 -> 64225 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/QuickHelper.pngbin0 -> 13093 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/QuickHelperObjectField.pngbin0 -> 12422 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/QuickNotes.pngbin0 -> 17213 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/RadiusHandle.pngbin0 -> 61939 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/RandomizeInSelection.pngbin0 -> 14956 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/RectXMinYMin.pngbin0 -> 5125 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/RectXY.pngbin0 -> 4325 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/RectangleCap.pngbin0 -> 63226 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/RemoveSpecificEditorPrefs.pngbin0 -> 13453 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/RotationHandle.pngbin0 -> 70817 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/ScaleHandle.pngbin0 -> 65367 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/ScaleSliderHandle.pngbin0 -> 62301 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/ScaleValueHandle.pngbin0 -> 58240 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/ScriptAdder.pngbin0 -> 18859 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/ScriptableWizardDisplayWizard.pngbin0 -> 20250 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/ScriptableWizardOnDrawGizmos.pngbin0 -> 35614 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/ScriptableWizardOnWizardCreate.pngbin0 -> 16297 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/ScriptableWizardOnWizardOtherButton.pngbin0 -> 21335 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/SelectAllOfTag.pngbin0 -> 16895 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/SelectionChange.pngbin0 -> 13099 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/ShowPopupEx.pngbin0 -> 10715 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/ShowRemoveNotification.pngbin0 -> 22756 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/SimpleAutoSave.pngbin0 -> 12959 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/SimplePrefixLabelUsage.pngbin0 -> 12247 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/SimpleRecorder.pngbin0 -> 14699 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/SliderHandle.pngbin0 -> 50325 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/SphereCap.pngbin0 -> 63417 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/TearMeshApart.pngbin0 -> 32704 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/Vec3ProjectDiagram.pngbin0 -> 8018 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/Vec3ReflectDiagram.pngbin0 -> 14364 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/WantsMouseMoveEx.pngbin0 -> 12127 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/WheelFrictionCurve.pngbin0 -> 4434 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/Window1.pngbin0 -> 16360 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/focusedWindowEx.pngbin0 -> 11665 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/isValidScriptableWizard.pngbin0 -> 23560 bytes
-rw-r--r--Runtime/Export/ScriptRefImages/mouseFocusedWindowEx.pngbin0 -> 13167 bytes
-rw-r--r--Runtime/Export/ScrollWaitDefinitions.cs8
-rw-r--r--Runtime/Export/Security.cs122
-rw-r--r--Runtime/Export/SecurityPublic.txt61
-rw-r--r--Runtime/Export/Serialization/IManagedLivenessAnalysis.cs11
-rw-r--r--Runtime/Export/Serialization/IPPtrRemapper.cs9
-rw-r--r--Runtime/Export/Serialization/ISerializedStateReader.cs25
-rw-r--r--Runtime/Export/Serialization/ISerializedStateWriter.cs25
-rw-r--r--Runtime/Export/Serialization/IUnityAssetsReferenceHolder.cs9
-rw-r--r--Runtime/Export/Serialization/IUnitySerializable.cs11
-rw-r--r--Runtime/Export/Serialization/ManagedLivenessAnalysis.txt67
-rw-r--r--Runtime/Export/Serialization/PPtrRemapper.txt53
-rw-r--r--Runtime/Export/Serialization/SerializedStateReader.txt214
-rw-r--r--Runtime/Export/Serialization/SerializedStateWriter.txt220
-rw-r--r--Runtime/Export/Serialization/UnitySurrogateSelector.cs115
-rw-r--r--Runtime/Export/ShaderBindings.txt415
-rw-r--r--Runtime/Export/SliderHandler.cs330
-rw-r--r--Runtime/Export/SpritesBindings.txt191
-rw-r--r--Runtime/Export/StackTrace.cs368
-rw-r--r--Runtime/Export/StaticBatching/CombineForStaticBatching.cs230
-rw-r--r--Runtime/Export/StaticBatching/MeshSubsetCombineUtility.cs30
-rw-r--r--Runtime/Export/SubstanceUtility.txt436
-rw-r--r--Runtime/Export/SyntaxDefs/template-csharp.plist140
-rw-r--r--Runtime/Export/SyntaxDefs/template-javascript.plist97
-rw-r--r--Runtime/Export/SystemClock.cs10
-rw-r--r--Runtime/Export/TextEditor.cs1203
-rw-r--r--Runtime/Export/TextureBindings.txt675
-rw-r--r--Runtime/Export/TrackedReference.cs39
-rw-r--r--Runtime/Export/UnityEngineApplication.txt657
-rw-r--r--Runtime/Export/UnityEngineAsyncOperation.txt81
-rw-r--r--Runtime/Export/UnityEngineBehaviour.txt31
-rw-r--r--Runtime/Export/UnityEngineCamera.txt420
-rw-r--r--Runtime/Export/UnityEngineComponent.txt329
-rw-r--r--Runtime/Export/UnityEngineComputeShader.txt216
-rw-r--r--Runtime/Export/UnityEngineDebug.txt134
-rw-r--r--Runtime/Export/UnityEngineDisplay.txt127
-rw-r--r--Runtime/Export/UnityEngineFlash.txt80
-rw-r--r--Runtime/Export/UnityEngineGameObject.txt439
-rw-r--r--Runtime/Export/UnityEngineInput.txt700
-rw-r--r--Runtime/Export/UnityEngineInternal/TypeInferenceRuleAttribute.cs52
-rw-r--r--Runtime/Export/UnityEngineInternal/WrappedTypes.cs20
-rw-r--r--Runtime/Export/UnityEngineLight.txt133
-rw-r--r--Runtime/Export/UnityEngineMonoBehaviour.txt350
-rw-r--r--Runtime/Export/UnityEngineObject.txt226
-rw-r--r--Runtime/Export/UnityEngineRandom.txt70
-rw-r--r--Runtime/Export/UnityEngineTime.txt65
-rw-r--r--Runtime/Export/UnityEngineTransform.txt302
-rw-r--r--Runtime/Export/UnityEngineYieldOperation.txt14
-rw-r--r--Runtime/Export/UserAuthorizationDialog.cs166
-rw-r--r--Runtime/Export/Utils.txt1235
-rw-r--r--Runtime/Export/WP8.cs20
-rw-r--r--Runtime/Export/WP8/WindowsPhone.txt32
-rw-r--r--Runtime/Export/WSA/WSAApplication.txt130
-rw-r--r--Runtime/Export/WSA/WSATiles.txt848
-rw-r--r--Runtime/Export/WWW.cpp1575
-rw-r--r--Runtime/Export/WWW.cs168
-rw-r--r--Runtime/Export/WWW.h420
-rw-r--r--Runtime/Export/WinRT/AppTrial.txt56
-rw-r--r--Runtime/Export/Windows/WindowsCrypto.txt46
-rw-r--r--Runtime/Export/Windows/WindowsDirectory.txt86
-rw-r--r--Runtime/Export/Windows/WindowsFile.txt69
-rw-r--r--Runtime/Export/XboxKeyboard.txt51
-rw-r--r--Runtime/Export/XboxServices.cs794
-rw-r--r--Runtime/Export/XboxVideoMode.txt85
-rw-r--r--Runtime/Export/common_include617
-rw-r--r--Runtime/Export/common_structs29
-rw-r--r--Runtime/Export/docs.css461
-rw-r--r--Runtime/Export/iOS/CocoaIntegration.cs89
-rw-r--r--Runtime/Export/iOS/iAD.cs187
-rw-r--r--Runtime/Export/iOS/iAD.txt91
-rw-r--r--Runtime/Export/iPhoneInput.txt1020
-rw-r--r--Runtime/Export/style.css407
-rw-r--r--Runtime/File/ApplicationSpecificPersistentDataPath.cpp171
-rw-r--r--Runtime/File/ApplicationSpecificPersistentDataPath.h9
-rw-r--r--Runtime/Filters/AABBUtility.cpp136
-rw-r--r--Runtime/Filters/AABBUtility.h18
-rw-r--r--Runtime/Filters/Deformation/BlendShapeAnimationBinding.cpp141
-rw-r--r--Runtime/Filters/Deformation/BlendShapeAnimationBinding.h4
-rw-r--r--Runtime/Filters/Deformation/SkinnedMeshFilter.cpp1535
-rw-r--r--Runtime/Filters/Deformation/SkinnedMeshFilter.h213
-rw-r--r--Runtime/Filters/Mesh/CompressedMesh.cpp755
-rw-r--r--Runtime/Filters/Mesh/CompressedMesh.h175
-rw-r--r--Runtime/Filters/Mesh/LodMesh.cpp2344
-rw-r--r--Runtime/Filters/Mesh/LodMesh.h509
-rw-r--r--Runtime/Filters/Mesh/LodMeshFilter.cpp96
-rw-r--r--Runtime/Filters/Mesh/LodMeshFilter.h38
-rw-r--r--Runtime/Filters/Mesh/Mesh.h76
-rw-r--r--Runtime/Filters/Mesh/MeshBlendShape.cpp234
-rw-r--r--Runtime/Filters/Mesh/MeshBlendShape.h115
-rw-r--r--Runtime/Filters/Mesh/MeshBlendShaping.cpp184
-rw-r--r--Runtime/Filters/Mesh/MeshBlendShaping.h12
-rw-r--r--Runtime/Filters/Mesh/MeshCombiner.cpp502
-rw-r--r--Runtime/Filters/Mesh/MeshCombiner.h33
-rw-r--r--Runtime/Filters/Mesh/MeshOptimizer.cpp359
-rw-r--r--Runtime/Filters/Mesh/MeshOptimizer.h13
-rw-r--r--Runtime/Filters/Mesh/MeshPartitioner.cpp346
-rw-r--r--Runtime/Filters/Mesh/MeshPartitioner.h5
-rw-r--r--Runtime/Filters/Mesh/MeshRenderer.cpp664
-rw-r--r--Runtime/Filters/Mesh/MeshRenderer.h87
-rw-r--r--Runtime/Filters/Mesh/MeshSkinning.cpp165
-rw-r--r--Runtime/Filters/Mesh/MeshSkinning.h64
-rw-r--r--Runtime/Filters/Mesh/MeshSkinningGenericSIMD.h212
-rw-r--r--Runtime/Filters/Mesh/MeshSkinningMobile.h160
-rw-r--r--Runtime/Filters/Mesh/MeshSkinningNEON.asm527
-rw-r--r--Runtime/Filters/Mesh/MeshSkinningNEON.s183
-rw-r--r--Runtime/Filters/Mesh/MeshSkinningNeon_Loop.h487
-rw-r--r--Runtime/Filters/Mesh/MeshSkinningSSE2.asm323
-rw-r--r--Runtime/Filters/Mesh/MeshSkinningSSE2.h129
-rw-r--r--Runtime/Filters/Mesh/MeshSkinningTests.cpp228
-rw-r--r--Runtime/Filters/Mesh/MeshSkinningVFP.s187
-rw-r--r--Runtime/Filters/Mesh/MeshSkinningVFP_Loop.h335
-rw-r--r--Runtime/Filters/Mesh/MeshUtility.cpp58
-rw-r--r--Runtime/Filters/Mesh/MeshUtility.h42
-rw-r--r--Runtime/Filters/Mesh/SkinGeneric.h338
-rw-r--r--Runtime/Filters/Mesh/SpriteRenderer.cpp338
-rw-r--r--Runtime/Filters/Mesh/SpriteRenderer.h60
-rw-r--r--Runtime/Filters/Mesh/SpriteRendererAnimationBinding.cpp68
-rw-r--r--Runtime/Filters/Mesh/SpriteRendererAnimationBinding.h2
-rw-r--r--Runtime/Filters/Mesh/TransformVertex.cpp205
-rw-r--r--Runtime/Filters/Mesh/TransformVertex.h175
-rw-r--r--Runtime/Filters/Mesh/TransformVertexNEON.asm694
-rw-r--r--Runtime/Filters/Mesh/TransformVertexNEON.s224
-rw-r--r--Runtime/Filters/Mesh/TransformVertexNEON_Loop.h254
-rw-r--r--Runtime/Filters/Mesh/TransformVertexVFP.s250
-rw-r--r--Runtime/Filters/Mesh/TransformVertexVFP_Loop.h252
-rw-r--r--Runtime/Filters/Mesh/VertexData.cpp559
-rw-r--r--Runtime/Filters/Mesh/VertexData.h253
-rw-r--r--Runtime/Filters/Misc/DynamicFontFreeType.cpp510
-rw-r--r--Runtime/Filters/Misc/DynamicFontFreeType.h47
-rw-r--r--Runtime/Filters/Misc/Font.cpp845
-rw-r--r--Runtime/Filters/Misc/Font.h318
-rw-r--r--Runtime/Filters/Misc/GetFonts.cpp338
-rw-r--r--Runtime/Filters/Misc/LineBuilder.cpp95
-rw-r--r--Runtime/Filters/Misc/LineBuilder.h88
-rw-r--r--Runtime/Filters/Misc/LineRenderer.cpp195
-rw-r--r--Runtime/Filters/Misc/LineRenderer.h61
-rw-r--r--Runtime/Filters/Misc/MiniCoreText.h38
-rw-r--r--Runtime/Filters/Misc/TextMesh.cpp298
-rw-r--r--Runtime/Filters/Misc/TextMesh.h99
-rw-r--r--Runtime/Filters/Misc/TrailRenderer.cpp197
-rw-r--r--Runtime/Filters/Misc/TrailRenderer.h62
-rw-r--r--Runtime/Filters/Particles/EllipsoidParticleEmitter.cpp110
-rw-r--r--Runtime/Filters/Particles/EllipsoidParticleEmitter.h35
-rw-r--r--Runtime/Filters/Particles/MeshParticleEmitter.cpp353
-rw-r--r--Runtime/Filters/Particles/MeshParticleEmitter.h42
-rw-r--r--Runtime/Filters/Particles/ParticleAnimator.cpp216
-rw-r--r--Runtime/Filters/Particles/ParticleAnimator.h57
-rw-r--r--Runtime/Filters/Particles/ParticleEmitter.cpp455
-rw-r--r--Runtime/Filters/Particles/ParticleEmitter.h151
-rw-r--r--Runtime/Filters/Particles/ParticleRenderer.cpp630
-rw-r--r--Runtime/Filters/Particles/ParticleRenderer.h103
-rw-r--r--Runtime/Filters/Particles/ParticleStruct.h59
-rw-r--r--Runtime/Filters/Particles/WorldParticleCollider.cpp197
-rw-r--r--Runtime/Filters/Particles/WorldParticleCollider.h33
-rw-r--r--Runtime/Filters/Pipeline.cpp20
-rw-r--r--Runtime/Filters/Pipeline.h16
-rw-r--r--Runtime/Filters/Renderer.cpp663
-rw-r--r--Runtime/Filters/Renderer.h234
-rw-r--r--Runtime/Filters/RendererAnimationBinding.cpp407
-rw-r--r--Runtime/Filters/RendererAnimationBinding.h2
-rw-r--r--Runtime/GameCode/Behaviour.cpp259
-rw-r--r--Runtime/GameCode/Behaviour.h87
-rw-r--r--Runtime/GameCode/CallDelayed.cpp203
-rw-r--r--Runtime/GameCode/CallDelayed.h102
-rw-r--r--Runtime/GameCode/CloneObject.cpp303
-rw-r--r--Runtime/GameCode/CloneObject.h29
-rw-r--r--Runtime/GameCode/DestroyDelayed.cpp16
-rw-r--r--Runtime/GameCode/DestroyDelayed.h10
-rw-r--r--Runtime/GameCode/RootMotionData.h9
-rw-r--r--Runtime/Geometry/AABB.cpp241
-rw-r--r--Runtime/Geometry/AABB.h202
-rw-r--r--Runtime/Geometry/BoundingUtils.cpp443
-rw-r--r--Runtime/Geometry/BoundingUtils.h30
-rw-r--r--Runtime/Geometry/BoundingVolumeConversion.h32
-rw-r--r--Runtime/Geometry/ComputionalGeometry.cpp304
-rw-r--r--Runtime/Geometry/ComputionalGeometry.h19
-rw-r--r--Runtime/Geometry/Intersection.cpp940
-rw-r--r--Runtime/Geometry/Intersection.h95
-rw-r--r--Runtime/Geometry/IntersectionTests.cpp288
-rw-r--r--Runtime/Geometry/Plane.h169
-rw-r--r--Runtime/Geometry/Ray.cpp17
-rw-r--r--Runtime/Geometry/Ray.h32
-rw-r--r--Runtime/Geometry/Ray2D.h32
-rw-r--r--Runtime/Geometry/Sphere.cpp57
-rw-r--r--Runtime/Geometry/Sphere.h79
-rw-r--r--Runtime/Geometry/SpriteMeshGenerator.cpp973
-rw-r--r--Runtime/Geometry/SpriteMeshGenerator.h269
-rw-r--r--Runtime/Geometry/TangentSpaceCalculation.cpp534
-rw-r--r--Runtime/Geometry/TangentSpaceCalculation.h13
-rw-r--r--Runtime/Geometry/TextureAtlas.cpp588
-rw-r--r--Runtime/Geometry/TextureAtlas.h12
-rw-r--r--Runtime/Geometry/TriTriIntersect.cpp719
-rw-r--r--Runtime/Geometry/TriTriIntersect.h9
-rw-r--r--Runtime/GfxDevice/BatchRendering.cpp279
-rw-r--r--Runtime/GfxDevice/BatchRendering.h34
-rw-r--r--Runtime/GfxDevice/BuiltinShaderParams.cpp100
-rw-r--r--Runtime/GfxDevice/BuiltinShaderParams.h73
-rw-r--r--Runtime/GfxDevice/BuiltinShaderParamsNames.cpp259
-rw-r--r--Runtime/GfxDevice/BuiltinShaderParamsNames.h444
-rw-r--r--Runtime/GfxDevice/ChannelAssigns.cpp225
-rw-r--r--Runtime/GfxDevice/ChannelAssigns.h52
-rw-r--r--Runtime/GfxDevice/GLDataBufferCommon.h24
-rw-r--r--Runtime/GfxDevice/GLESChannels.h84
-rw-r--r--Runtime/GfxDevice/GLESCommon.h105
-rw-r--r--Runtime/GfxDevice/GLRTCommon.h370
-rw-r--r--Runtime/GfxDevice/GPUSkinningInfo.h61
-rw-r--r--Runtime/GfxDevice/GfxDevice.cpp926
-rw-r--r--Runtime/GfxDevice/GfxDevice.h813
-rw-r--r--Runtime/GfxDevice/GfxDeviceConfigure.h40
-rw-r--r--Runtime/GfxDevice/GfxDeviceObjects.h145
-rw-r--r--Runtime/GfxDevice/GfxDeviceRecreate.cpp291
-rw-r--r--Runtime/GfxDevice/GfxDeviceRecreate.h10
-rw-r--r--Runtime/GfxDevice/GfxDeviceResources.h129
-rw-r--r--Runtime/GfxDevice/GfxDeviceSetup.cpp396
-rw-r--r--Runtime/GfxDevice/GfxDeviceSetup.h30
-rw-r--r--Runtime/GfxDevice/GfxDeviceStats.cpp170
-rw-r--r--Runtime/GfxDevice/GfxDeviceStats.h154
-rw-r--r--Runtime/GfxDevice/GfxDeviceTypes.h616
-rw-r--r--Runtime/GfxDevice/GfxDeviceWindow.cpp55
-rw-r--r--Runtime/GfxDevice/GfxDeviceWindow.h36
-rw-r--r--Runtime/GfxDevice/GfxDisplayList.h9
-rw-r--r--Runtime/GfxDevice/GfxPatchInfo.cpp99
-rw-r--r--Runtime/GfxDevice/GfxPatchInfo.h78
-rw-r--r--Runtime/GfxDevice/GfxTimerQuery.h23
-rw-r--r--Runtime/GfxDevice/GpuProgram.cpp695
-rw-r--r--Runtime/GfxDevice/GpuProgram.h278
-rw-r--r--Runtime/GfxDevice/GpuProgramParamsApply.h130
-rw-r--r--Runtime/GfxDevice/ShaderConstantCache.h142
-rw-r--r--Runtime/GfxDevice/TextureIdMap.cpp31
-rw-r--r--Runtime/GfxDevice/TextureIdMap.h70
-rw-r--r--Runtime/GfxDevice/TextureUploadUtils.h254
-rw-r--r--Runtime/GfxDevice/TransformState.h64
-rw-r--r--Runtime/GfxDevice/VramLimits.cpp39
-rw-r--r--Runtime/GfxDevice/VramLimits.h11
-rw-r--r--Runtime/GfxDevice/d3d/CombinerD3D.cpp600
-rw-r--r--Runtime/GfxDevice/d3d/CombinerD3D.h37
-rw-r--r--Runtime/GfxDevice/d3d/D3D9Context.cpp629
-rw-r--r--Runtime/GfxDevice/d3d/D3D9Context.h44
-rw-r--r--Runtime/GfxDevice/d3d/D3D9Enumeration.cpp344
-rw-r--r--Runtime/GfxDevice/d3d/D3D9Enumeration.h64
-rw-r--r--Runtime/GfxDevice/d3d/D3D9Includes.h7
-rw-r--r--Runtime/GfxDevice/d3d/D3D9Utils.cpp169
-rw-r--r--Runtime/GfxDevice/d3d/D3D9Utils.h69
-rw-r--r--Runtime/GfxDevice/d3d/D3D9VBO.cpp815
-rw-r--r--Runtime/GfxDevice/d3d/D3D9VBO.h86
-rw-r--r--Runtime/GfxDevice/d3d/D3D9Window.cpp272
-rw-r--r--Runtime/GfxDevice/d3d/D3D9Window.h39
-rw-r--r--Runtime/GfxDevice/d3d/GfxDeviceD3D9.cpp3009
-rw-r--r--Runtime/GfxDevice/d3d/GfxDeviceD3D9.h361
-rw-r--r--Runtime/GfxDevice/d3d/GpuProgramsD3D.cpp474
-rw-r--r--Runtime/GfxDevice/d3d/GpuProgramsD3D.h40
-rw-r--r--Runtime/GfxDevice/d3d/GraphicsCapsD3D9.cpp384
-rw-r--r--Runtime/GfxDevice/d3d/RenderTextureD3D.cpp583
-rw-r--r--Runtime/GfxDevice/d3d/RenderTextureD3D.h17
-rw-r--r--Runtime/GfxDevice/d3d/ShaderGenerator.cpp948
-rw-r--r--Runtime/GfxDevice/d3d/ShaderGenerator.h100
-rw-r--r--Runtime/GfxDevice/d3d/ShaderPatchingD3D9.cpp376
-rw-r--r--Runtime/GfxDevice/d3d/ShaderPatchingD3D9.h7
-rw-r--r--Runtime/GfxDevice/d3d/TexturesD3D9.cpp696
-rw-r--r--Runtime/GfxDevice/d3d/TexturesD3D9.h90
-rw-r--r--Runtime/GfxDevice/d3d/TimerQueryD3D9.cpp196
-rw-r--r--Runtime/GfxDevice/d3d/TimerQueryD3D9.h67
-rw-r--r--Runtime/GfxDevice/d3d/VertexDeclarations.cpp124
-rw-r--r--Runtime/GfxDevice/d3d/VertexDeclarations.h26
-rw-r--r--Runtime/GfxDevice/d3d/VertexPipeD3D9.cpp705
-rw-r--r--Runtime/GfxDevice/d3d/VertexPipeD3D9.h139
-rw-r--r--Runtime/GfxDevice/d3d11/ConstantBuffersD3D11.cpp368
-rw-r--r--Runtime/GfxDevice/d3d11/ConstantBuffersD3D11.h74
-rw-r--r--Runtime/GfxDevice/d3d11/D3D11ByteCode.cpp2237
-rw-r--r--Runtime/GfxDevice/d3d11/D3D11ByteCode.h856
-rw-r--r--Runtime/GfxDevice/d3d11/D3D11Compiler.cpp2
-rw-r--r--Runtime/GfxDevice/d3d11/D3D11Compiler.h117
-rw-r--r--Runtime/GfxDevice/d3d11/D3D11Context.cpp610
-rw-r--r--Runtime/GfxDevice/d3d11/D3D11Context.h65
-rw-r--r--Runtime/GfxDevice/d3d11/D3D11Context_Metro.cpp484
-rw-r--r--Runtime/GfxDevice/d3d11/D3D11Context_WP8.cpp233
-rw-r--r--Runtime/GfxDevice/d3d11/D3D11Debug.cpp282
-rw-r--r--Runtime/GfxDevice/d3d11/D3D11Debug.h54
-rw-r--r--Runtime/GfxDevice/d3d11/D3D11Hash.cpp326
-rw-r--r--Runtime/GfxDevice/d3d11/D3D11Includes.h49
-rw-r--r--Runtime/GfxDevice/d3d11/D3D11Utils.cpp85
-rw-r--r--Runtime/GfxDevice/d3d11/D3D11Utils.h20
-rw-r--r--Runtime/GfxDevice/d3d11/D3D11VBO.cpp1193
-rw-r--r--Runtime/GfxDevice/d3d11/D3D11VBO.h99
-rw-r--r--Runtime/GfxDevice/d3d11/D3D11Window.cpp220
-rw-r--r--Runtime/GfxDevice/d3d11/D3D11Window.h28
-rw-r--r--Runtime/GfxDevice/d3d11/FixedFunctionStateD3D11.h98
-rw-r--r--Runtime/GfxDevice/d3d11/GfxDeviceD3D11.cpp3133
-rw-r--r--Runtime/GfxDevice/d3d11/GfxDeviceD3D11.h357
-rw-r--r--Runtime/GfxDevice/d3d11/GpuProgramsD3D11.cpp703
-rw-r--r--Runtime/GfxDevice/d3d11/GpuProgramsD3D11.h125
-rw-r--r--Runtime/GfxDevice/d3d11/GraphicsCapsD3D11.cpp363
-rw-r--r--Runtime/GfxDevice/d3d11/InternalShaders/CompileShaderLib/CompileShaderLib.cpp185
-rw-r--r--Runtime/GfxDevice/d3d11/InternalShaders/CompileShaderLib/CompileShaderLib.exebin0 -> 80384 bytes
-rw-r--r--Runtime/GfxDevice/d3d11/InternalShaders/CompileShaderLib/d3dcompiler_47.dllbin0 -> 3453312 bytes
-rw-r--r--Runtime/GfxDevice/d3d11/InternalShaders/FFShaderLib.h11341
-rw-r--r--Runtime/GfxDevice/d3d11/InternalShaders/FFShaderLib.hlsl172
-rw-r--r--Runtime/GfxDevice/d3d11/InternalShaders/builtin.h19755
-rw-r--r--Runtime/GfxDevice/d3d11/InternalShaders/compile_all.bat82
-rw-r--r--Runtime/GfxDevice/d3d11/InternalShaders/internalshaders.hlsl119
-rw-r--r--Runtime/GfxDevice/d3d11/RenderTextureD3D11.cpp805
-rw-r--r--Runtime/GfxDevice/d3d11/ShaderGeneratorD3D11.cpp1504
-rw-r--r--Runtime/GfxDevice/d3d11/ShaderGeneratorD3D11.h10
-rw-r--r--Runtime/GfxDevice/d3d11/ShaderGeneratorLinkD3D11.cpp985
-rw-r--r--Runtime/GfxDevice/d3d11/ShaderPatchingD3D11.cpp702
-rw-r--r--Runtime/GfxDevice/d3d11/ShaderPatchingD3D11.h24
-rw-r--r--Runtime/GfxDevice/d3d11/StreamOutSkinnedMesh.cpp416
-rw-r--r--Runtime/GfxDevice/d3d11/StreamOutSkinnedMesh.h42
-rw-r--r--Runtime/GfxDevice/d3d11/TexturesD3D11.cpp1067
-rw-r--r--Runtime/GfxDevice/d3d11/TexturesD3D11.h201
-rw-r--r--Runtime/GfxDevice/d3d11/TimerQueryD3D11.cpp209
-rw-r--r--Runtime/GfxDevice/d3d11/TimerQueryD3D11.h67
-rw-r--r--Runtime/GfxDevice/d3d11/VertexDeclarationsD3D11.cpp282
-rw-r--r--Runtime/GfxDevice/d3d11/VertexDeclarationsD3D11.h51
-rw-r--r--Runtime/GfxDevice/null/GfxDeviceNull.cpp251
-rw-r--r--Runtime/GfxDevice/null/GfxDeviceNull.h158
-rw-r--r--Runtime/GfxDevice/null/GfxNullVBO.cpp107
-rw-r--r--Runtime/GfxDevice/null/GfxNullVBO.h55
-rw-r--r--Runtime/GfxDevice/opengl/ARBVBO.cpp611
-rw-r--r--Runtime/GfxDevice/opengl/ARBVBO.h75
-rw-r--r--Runtime/GfxDevice/opengl/ArbGpuProgamGL.cpp458
-rw-r--r--Runtime/GfxDevice/opengl/ArbGpuProgamGL.h19
-rw-r--r--Runtime/GfxDevice/opengl/ChannelsGL.cpp252
-rw-r--r--Runtime/GfxDevice/opengl/ChannelsGL.h17
-rw-r--r--Runtime/GfxDevice/opengl/CombinerGL.cpp201
-rw-r--r--Runtime/GfxDevice/opengl/CombinerGL.h18
-rw-r--r--Runtime/GfxDevice/opengl/GLAssert.cpp42
-rw-r--r--Runtime/GfxDevice/opengl/GLAssert.h25
-rw-r--r--Runtime/GfxDevice/opengl/GLContext.cpp506
-rw-r--r--Runtime/GfxDevice/opengl/GLContext.h195
-rw-r--r--Runtime/GfxDevice/opengl/GLExtensionDefs.h247
-rw-r--r--Runtime/GfxDevice/opengl/GLExtensionDefs.txt145
-rw-r--r--Runtime/GfxDevice/opengl/GLWindow.h29
-rw-r--r--Runtime/GfxDevice/opengl/GenerateGLExtensionDef.pl48
-rw-r--r--Runtime/GfxDevice/opengl/GfxDeviceGL.cpp3079
-rw-r--r--Runtime/GfxDevice/opengl/GfxDeviceGL.h191
-rw-r--r--Runtime/GfxDevice/opengl/GpuProgramsGL.cpp562
-rw-r--r--Runtime/GfxDevice/opengl/GpuProgramsGL.h28
-rw-r--r--Runtime/GfxDevice/opengl/GraphicsCapsGL.cpp1176
-rw-r--r--Runtime/GfxDevice/opengl/NullVBO.cpp168
-rw-r--r--Runtime/GfxDevice/opengl/NullVBO.h27
-rw-r--r--Runtime/GfxDevice/opengl/RenderTextureGL.cpp751
-rw-r--r--Runtime/GfxDevice/opengl/TextureIdMapGL.h16
-rw-r--r--Runtime/GfxDevice/opengl/TexturesGL.cpp494
-rw-r--r--Runtime/GfxDevice/opengl/TexturesGL.h25
-rw-r--r--Runtime/GfxDevice/opengl/TimerQueryGL.cpp162
-rw-r--r--Runtime/GfxDevice/opengl/TimerQueryGL.h63
-rw-r--r--Runtime/GfxDevice/opengl/UnityGL.h176
-rw-r--r--Runtime/GfxDevice/opengl/unity_gl.h1914
-rw-r--r--Runtime/GfxDevice/opengl/unity_glext.h6704
-rw-r--r--Runtime/GfxDevice/opengles/ExtensionsGLES.cpp30
-rw-r--r--Runtime/GfxDevice/opengles/ExtensionsGLES.h9
-rw-r--r--Runtime/GfxDevice/opengles/IncludesGLES.h71
-rw-r--r--Runtime/GfxDevice/opengles20/AssertGLES20.cpp58
-rw-r--r--Runtime/GfxDevice/opengles20/AssertGLES20.h29
-rw-r--r--Runtime/GfxDevice/opengles20/CombinerGLES20.cpp21
-rw-r--r--Runtime/GfxDevice/opengles20/CombinerGLES20.h10
-rw-r--r--Runtime/GfxDevice/opengles20/ContextGLES20.cpp237
-rw-r--r--Runtime/GfxDevice/opengles20/ContextGLES20.h43
-rw-r--r--Runtime/GfxDevice/opengles20/DebugGLES20.cpp38
-rw-r--r--Runtime/GfxDevice/opengles20/DebugGLES20.h139
-rw-r--r--Runtime/GfxDevice/opengles20/FixedFunctionStateGLES20.cpp73
-rw-r--r--Runtime/GfxDevice/opengles20/FixedFunctionStateGLES20.h66
-rw-r--r--Runtime/GfxDevice/opengles20/GfxDeviceGLES20.cpp2815
-rw-r--r--Runtime/GfxDevice/opengles20/GfxDeviceGLES20.h190
-rw-r--r--Runtime/GfxDevice/opengles20/GpuProgramsGLES20.cpp1120
-rw-r--r--Runtime/GfxDevice/opengles20/GpuProgramsGLES20.h78
-rw-r--r--Runtime/GfxDevice/opengles20/GpuProgramsGLES20_UniformCache.cpp72
-rw-r--r--Runtime/GfxDevice/opengles20/GpuProgramsGLES20_UniformCache.h51
-rw-r--r--Runtime/GfxDevice/opengles20/GpuPropertiesGLES20.cpp70
-rw-r--r--Runtime/GfxDevice/opengles20/GpuPropertiesGLES20.h3
-rw-r--r--Runtime/GfxDevice/opengles20/IncludesGLES20.h1
-rw-r--r--Runtime/GfxDevice/opengles20/RenderTextureGLES20.cpp958
-rw-r--r--Runtime/GfxDevice/opengles20/RenderTextureGLES20.h40
-rw-r--r--Runtime/GfxDevice/opengles20/ShaderGeneratorGLES20.cpp749
-rw-r--r--Runtime/GfxDevice/opengles20/ShaderGeneratorGLES20.h24
-rw-r--r--Runtime/GfxDevice/opengles20/TextureIdMapGLES20.h17
-rw-r--r--Runtime/GfxDevice/opengles20/TexturesGLES20.cpp633
-rw-r--r--Runtime/GfxDevice/opengles20/TexturesGLES20.h21
-rw-r--r--Runtime/GfxDevice/opengles20/TimerQueryGLES20.cpp109
-rw-r--r--Runtime/GfxDevice/opengles20/TimerQueryGLES20.h47
-rw-r--r--Runtime/GfxDevice/opengles20/UnityGLES20Ext.cpp40
-rw-r--r--Runtime/GfxDevice/opengles20/UnityGLES20Ext.h342
-rw-r--r--Runtime/GfxDevice/opengles20/VBOGLES20.cpp1559
-rw-r--r--Runtime/GfxDevice/opengles20/VBOGLES20.h147
-rw-r--r--Runtime/GfxDevice/opengles20/_DebugStuffGLES20.cpp438
-rw-r--r--Runtime/GfxDevice/opengles30/AssertGLES30.cpp58
-rw-r--r--Runtime/GfxDevice/opengles30/AssertGLES30.h40
-rw-r--r--Runtime/GfxDevice/opengles30/CombinerGLES30.cpp21
-rw-r--r--Runtime/GfxDevice/opengles30/CombinerGLES30.h10
-rw-r--r--Runtime/GfxDevice/opengles30/ConstantBuffersGLES30.cpp338
-rw-r--r--Runtime/GfxDevice/opengles30/ConstantBuffersGLES30.h72
-rw-r--r--Runtime/GfxDevice/opengles30/ContextGLES30.cpp288
-rw-r--r--Runtime/GfxDevice/opengles30/ContextGLES30.h37
-rw-r--r--Runtime/GfxDevice/opengles30/DataBuffersGLES30.cpp429
-rw-r--r--Runtime/GfxDevice/opengles30/DataBuffersGLES30.h137
-rw-r--r--Runtime/GfxDevice/opengles30/DebugGLES30.cpp38
-rw-r--r--Runtime/GfxDevice/opengles30/DebugGLES30.h139
-rw-r--r--Runtime/GfxDevice/opengles30/FixedFunctionStateGLES30.cpp71
-rw-r--r--Runtime/GfxDevice/opengles30/FixedFunctionStateGLES30.h65
-rw-r--r--Runtime/GfxDevice/opengles30/GfxDeviceGLES30.cpp3328
-rw-r--r--Runtime/GfxDevice/opengles30/GfxDeviceGLES30.h191
-rw-r--r--Runtime/GfxDevice/opengles30/GpuProgramsGLES30.cpp1176
-rw-r--r--Runtime/GfxDevice/opengles30/GpuProgramsGLES30.h88
-rw-r--r--Runtime/GfxDevice/opengles30/GpuProgramsGLES30_UniformCache.cpp62
-rw-r--r--Runtime/GfxDevice/opengles30/GpuProgramsGLES30_UniformCache.h50
-rw-r--r--Runtime/GfxDevice/opengles30/GpuPropertiesGLES30.cpp70
-rw-r--r--Runtime/GfxDevice/opengles30/GpuPropertiesGLES30.h3
-rw-r--r--Runtime/GfxDevice/opengles30/IncludesGLES30.h1
-rw-r--r--Runtime/GfxDevice/opengles30/RenderTextureGLES30.cpp551
-rw-r--r--Runtime/GfxDevice/opengles30/RenderTextureGLES30.h267
-rw-r--r--Runtime/GfxDevice/opengles30/ShaderGeneratorGLES30.cpp824
-rw-r--r--Runtime/GfxDevice/opengles30/ShaderGeneratorGLES30.h24
-rw-r--r--Runtime/GfxDevice/opengles30/TextureIdMapGLES30.h17
-rw-r--r--Runtime/GfxDevice/opengles30/TexturesGLES30.cpp535
-rw-r--r--Runtime/GfxDevice/opengles30/TexturesGLES30.h22
-rw-r--r--Runtime/GfxDevice/opengles30/TimerQueryGLES30.cpp100
-rw-r--r--Runtime/GfxDevice/opengles30/TimerQueryGLES30.h47
-rw-r--r--Runtime/GfxDevice/opengles30/TransformFeedbackSkinnedMesh.cpp762
-rw-r--r--Runtime/GfxDevice/opengles30/TransformFeedbackSkinnedMesh.h47
-rw-r--r--Runtime/GfxDevice/opengles30/UnityGLES30Ext.cpp14
-rw-r--r--Runtime/GfxDevice/opengles30/UnityGLES30Ext.h276
-rw-r--r--Runtime/GfxDevice/opengles30/UtilsGLES30.cpp181
-rw-r--r--Runtime/GfxDevice/opengles30/UtilsGLES30.h37
-rw-r--r--Runtime/GfxDevice/opengles30/VBOGLES30.cpp1351
-rw-r--r--Runtime/GfxDevice/opengles30/VBOGLES30.h262
-rw-r--r--Runtime/GfxDevice/threaded/ClientIDMapper.h46
-rw-r--r--Runtime/GfxDevice/threaded/GfxCommands.h669
-rw-r--r--Runtime/GfxDevice/threaded/GfxDeviceClient.cpp3922
-rw-r--r--Runtime/GfxDevice/threaded/GfxDeviceClient.h423
-rw-r--r--Runtime/GfxDevice/threaded/GfxDeviceWorker.cpp2161
-rw-r--r--Runtime/GfxDevice/threaded/GfxDeviceWorker.h135
-rw-r--r--Runtime/GfxDevice/threaded/GfxReturnStructs.cpp239
-rw-r--r--Runtime/GfxDevice/threaded/GfxReturnStructs.h122
-rw-r--r--Runtime/GfxDevice/threaded/ThreadedDeviceStates.h134
-rw-r--r--Runtime/GfxDevice/threaded/ThreadedDisplayList.cpp442
-rw-r--r--Runtime/GfxDevice/threaded/ThreadedDisplayList.h85
-rw-r--r--Runtime/GfxDevice/threaded/ThreadedTimerQuery.cpp102
-rw-r--r--Runtime/GfxDevice/threaded/ThreadedTimerQuery.h29
-rw-r--r--Runtime/GfxDevice/threaded/ThreadedVBO.cpp511
-rw-r--r--Runtime/GfxDevice/threaded/ThreadedVBO.h104
-rw-r--r--Runtime/GfxDevice/threaded/ThreadedWindow.cpp97
-rw-r--r--Runtime/GfxDevice/threaded/ThreadedWindow.h36
-rw-r--r--Runtime/GfxDevice/threaded/WorkerIDMapper.h33
-rw-r--r--Runtime/Graphics/CubemapProcessor.cpp515
-rw-r--r--Runtime/Graphics/CubemapProcessor.h35
-rw-r--r--Runtime/Graphics/CubemapTexture.cpp186
-rw-r--r--Runtime/Graphics/CubemapTexture.h30
-rw-r--r--Runtime/Graphics/DXTCompression.cpp595
-rw-r--r--Runtime/Graphics/DXTCompression.h13
-rw-r--r--Runtime/Graphics/DisplayManager.h54
-rw-r--r--Runtime/Graphics/DrawSplashScreenAndWatermarks.cpp449
-rw-r--r--Runtime/Graphics/DrawSplashScreenAndWatermarks.h18
-rw-r--r--Runtime/Graphics/DrawUtil.cpp173
-rw-r--r--Runtime/Graphics/DrawUtil.h51
-rw-r--r--Runtime/Graphics/ETC2Decompression.cpp487
-rw-r--r--Runtime/Graphics/ETC2Decompression.h39
-rw-r--r--Runtime/Graphics/FlashATFDecompression.h7
-rw-r--r--Runtime/Graphics/GeneratedTextures.cpp258
-rw-r--r--Runtime/Graphics/GeneratedTextures.h28
-rw-r--r--Runtime/Graphics/GraphicsHelper.cpp103
-rw-r--r--Runtime/Graphics/GraphicsHelper.h98
-rw-r--r--Runtime/Graphics/Image.cpp1657
-rw-r--r--Runtime/Graphics/Image.h178
-rw-r--r--Runtime/Graphics/ImageConversion.cpp621
-rw-r--r--Runtime/Graphics/ImageConversion.h28
-rw-r--r--Runtime/Graphics/LightProbeGroup.cpp50
-rw-r--r--Runtime/Graphics/LightProbeGroup.h41
-rw-r--r--Runtime/Graphics/LightmapSettings.cpp153
-rw-r--r--Runtime/Graphics/LightmapSettings.h101
-rw-r--r--Runtime/Graphics/LowerResBlitTexture.h61
-rw-r--r--Runtime/Graphics/MatrixStack.cpp72
-rw-r--r--Runtime/Graphics/MatrixStack.h32
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/ClampVelocityModule.cpp102
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/ClampVelocityModule.h29
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/CollisionModule.cpp603
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/CollisionModule.h62
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/ColorByVelocityModule.cpp60
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/ColorByVelocityModule.h27
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/ColorModule.cpp51
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/ColorModule.h25
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/EmissionModule.cpp124
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/EmissionModule.h34
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/ExternalForcesModule.cpp118
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/ExternalForcesModule.h25
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/ForceModule.cpp164
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/ForceModule.h36
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/InitialModule.cpp193
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/InitialModule.h66
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/ParticleSystemModule.cpp141
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/ParticleSystemModule.h238
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/RotationByVelocityModule.cpp51
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/RotationByVelocityModule.h27
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/RotationModule.cpp83
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/RotationModule.h27
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/ShapeModule.cpp650
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/ShapeModule.h80
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/SizeByVelocityModule.cpp50
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/SizeByVelocityModule.h27
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/SizeModule.cpp41
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/SizeModule.h27
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/SubModule.cpp148
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/SubModule.h38
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/UVModule.cpp105
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/UVModule.h36
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/VelocityModule.cpp127
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/VelocityModule.h36
-rw-r--r--Runtime/Graphics/ParticleSystem/ParticleCollisionEvents.cpp122
-rw-r--r--Runtime/Graphics/ParticleSystem/ParticleCollisionEvents.h42
-rw-r--r--Runtime/Graphics/ParticleSystem/ParticleSystem.cpp2110
-rw-r--r--Runtime/Graphics/ParticleSystem/ParticleSystem.h298
-rw-r--r--Runtime/Graphics/ParticleSystem/ParticleSystemCommon.h48
-rw-r--r--Runtime/Graphics/ParticleSystem/ParticleSystemCurves.cpp196
-rw-r--r--Runtime/Graphics/ParticleSystem/ParticleSystemCurves.h187
-rw-r--r--Runtime/Graphics/ParticleSystem/ParticleSystemGradients.cpp27
-rw-r--r--Runtime/Graphics/ParticleSystem/ParticleSystemGradients.h87
-rw-r--r--Runtime/Graphics/ParticleSystem/ParticleSystemParticle.cpp323
-rw-r--r--Runtime/Graphics/ParticleSystem/ParticleSystemParticle.h116
-rw-r--r--Runtime/Graphics/ParticleSystem/ParticleSystemRenderer.cpp1241
-rw-r--r--Runtime/Graphics/ParticleSystem/ParticleSystemRenderer.h134
-rw-r--r--Runtime/Graphics/ParticleSystem/ParticleSystemRendererTests.cpp29
-rw-r--r--Runtime/Graphics/ParticleSystem/ParticleSystemUtils.cpp113
-rw-r--r--Runtime/Graphics/ParticleSystem/ParticleSystemUtils.h48
-rw-r--r--Runtime/Graphics/ParticleSystem/PolynomialCurve.cpp405
-rw-r--r--Runtime/Graphics/ParticleSystem/PolynomialCurve.h229
-rw-r--r--Runtime/Graphics/ParticleSystem/PolynomialCurveTests.cpp259
-rw-r--r--Runtime/Graphics/Polygon2D.cpp145
-rw-r--r--Runtime/Graphics/Polygon2D.h66
-rw-r--r--Runtime/Graphics/ProceduralCache.cpp154
-rw-r--r--Runtime/Graphics/ProceduralLinker.cpp521
-rw-r--r--Runtime/Graphics/ProceduralMaterial.cpp1339
-rw-r--r--Runtime/Graphics/ProceduralMaterial.h289
-rw-r--r--Runtime/Graphics/ProceduralPreset.cpp143
-rw-r--r--Runtime/Graphics/ProceduralTexture.cpp372
-rw-r--r--Runtime/Graphics/ProceduralTexture.h190
-rw-r--r--Runtime/Graphics/RenderBufferManager.cpp251
-rw-r--r--Runtime/Graphics/RenderBufferManager.h58
-rw-r--r--Runtime/Graphics/RenderSurface.h40
-rw-r--r--Runtime/Graphics/RenderTexture.cpp889
-rw-r--r--Runtime/Graphics/RenderTexture.h210
-rw-r--r--Runtime/Graphics/S3Decompression.cpp1882
-rw-r--r--Runtime/Graphics/S3Decompression.h18
-rw-r--r--Runtime/Graphics/ScreenManager.cpp201
-rw-r--r--Runtime/Graphics/ScreenManager.h257
-rw-r--r--Runtime/Graphics/SpriteFrame.cpp272
-rw-r--r--Runtime/Graphics/SpriteFrame.h190
-rw-r--r--Runtime/Graphics/SpriteUtility.cpp155
-rw-r--r--Runtime/Graphics/SpriteUtility.h22
-rw-r--r--Runtime/Graphics/SubstanceArchive.cpp121
-rw-r--r--Runtime/Graphics/SubstanceArchive.h75
-rw-r--r--Runtime/Graphics/SubstanceInput.h243
-rw-r--r--Runtime/Graphics/SubstanceSystem.cpp876
-rw-r--r--Runtime/Graphics/SubstanceSystem.h160
-rw-r--r--Runtime/Graphics/Texture.cpp332
-rw-r--r--Runtime/Graphics/Texture.h165
-rw-r--r--Runtime/Graphics/Texture2D.cpp1422
-rw-r--r--Runtime/Graphics/Texture2D.h208
-rw-r--r--Runtime/Graphics/Texture3D.cpp322
-rw-r--r--Runtime/Graphics/Texture3D.h67
-rw-r--r--Runtime/Graphics/TextureFormat.cpp248
-rw-r--r--Runtime/Graphics/TextureFormat.h96
-rw-r--r--Runtime/Graphics/TextureGenerator.h114
-rw-r--r--Runtime/Graphics/TextureSettings.cpp64
-rw-r--r--Runtime/Graphics/TextureSettings.h42
-rw-r--r--Runtime/Graphics/Transform.cpp1695
-rw-r--r--Runtime/Graphics/Transform.h327
-rw-r--r--Runtime/Graphics/TransformTests.cpp102
-rw-r--r--Runtime/Graphics/TriStripper.cpp112
-rw-r--r--Runtime/Graphics/TriStripper.h22
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/AnimationNodeLibrary.cs22
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/Attributes.cs96
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/DefaultValueAttribute.cs33
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/GraphBehaviour.cs121
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Animation/AnimationNodes.cs76
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Animation/SimpleAnimationPlayer.cs78
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/AudioSource/AudioSourceNodes.cs19
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/CharacterController/SimpleCharacterControllerNodes.cs24
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Collections/CollectionsNodes.cs128
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Collider/OnCollisionEvent.cs74
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Collider/OnMouseEvent.cs22
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Collider/OnTriggerEvent.cs48
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Component/ComponentNodes.cs10
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/InputNodes.cs82
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnAxis.cs37
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnButton.cs38
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnInputNode.cs50
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnKey.cs38
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnMouseButton.cs38
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/LogicNodeUtility.cs5
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Material/MaterialNodes.cs113
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/NodeLibrary.cs94
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/NodeLibraryForTesting.txt124
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Rigidbody/RigidbodyNodes.cs104
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/LookAt.cs46
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/MoveTo.cs45
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/RotateTo.cs45
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/TransformNodes.cs122
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/YieldedTransformNodeBase.cs27
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/YieldedNodeBase.cs54
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/LogicNodeTestLibrary.cs359
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/ColliderDummyBase.cs25
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/OnAnimationEventDummy.cs51
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/OnCollisionEventDummy.cs43
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/OnMouseEventDummy.cs41
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/OnTriggerEventDummy.cs40
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/TestHelpers.cs33
-rw-r--r--Runtime/Graphs/UnityEngine.Graphs/UnityEngine.Graphs.csproj92
-rw-r--r--Runtime/IMGUI/GUIButton.cpp77
-rw-r--r--Runtime/IMGUI/GUIButton.h16
-rw-r--r--Runtime/IMGUI/GUIClip.cpp419
-rw-r--r--Runtime/IMGUI/GUIClip.h107
-rw-r--r--Runtime/IMGUI/GUIContent.cpp58
-rw-r--r--Runtime/IMGUI/GUIContent.h36
-rw-r--r--Runtime/IMGUI/GUIContentTests.cpp28
-rw-r--r--Runtime/IMGUI/GUILabel.cpp29
-rw-r--r--Runtime/IMGUI/GUILabel.h15
-rw-r--r--Runtime/IMGUI/GUIManager.cpp571
-rw-r--r--Runtime/IMGUI/GUIManager.h135
-rw-r--r--Runtime/IMGUI/GUIState.cpp519
-rw-r--r--Runtime/IMGUI/GUIState.h231
-rw-r--r--Runtime/IMGUI/GUIStyle.cpp1154
-rw-r--r--Runtime/IMGUI/GUIStyle.h297
-rw-r--r--Runtime/IMGUI/GUITest.cpp209
-rw-r--r--Runtime/IMGUI/GUIToggle.cpp76
-rw-r--r--Runtime/IMGUI/GUIToggle.h16
-rw-r--r--Runtime/IMGUI/GUIWindows.cpp748
-rw-r--r--Runtime/IMGUI/GUIWindows.h95
-rw-r--r--Runtime/IMGUI/IDList.cpp164
-rw-r--r--Runtime/IMGUI/IDList.h69
-rw-r--r--Runtime/IMGUI/IMGUIUtils.cpp101
-rw-r--r--Runtime/IMGUI/IMGUIUtils.h64
-rw-r--r--Runtime/IMGUI/NamedKeyControlList.cpp27
-rw-r--r--Runtime/IMGUI/NamedKeyControlList.h40
-rw-r--r--Runtime/IMGUI/TextFormatting.cpp337
-rw-r--r--Runtime/IMGUI/TextFormatting.h85
-rw-r--r--Runtime/IMGUI/TextMeshGenerator2.cpp871
-rw-r--r--Runtime/IMGUI/TextMeshGenerator2.h96
-rw-r--r--Runtime/IMGUI/TextUtil.cpp277
-rw-r--r--Runtime/IMGUI/TextUtil.h110
-rw-r--r--Runtime/Input/GetInput.h288
-rw-r--r--Runtime/Input/InputAxis.cpp226
-rw-r--r--Runtime/Input/InputAxis.h151
-rw-r--r--Runtime/Input/InputManager.cpp1329
-rw-r--r--Runtime/Input/InputManager.h470
-rw-r--r--Runtime/Input/LocationService.h51
-rw-r--r--Runtime/Input/OnScreenKeyboard.h47
-rw-r--r--Runtime/Input/SimulateInputEvents.cpp222
-rw-r--r--Runtime/Input/SimulateInputEvents.h10
-rw-r--r--Runtime/Input/TimeManager.cpp489
-rw-r--r--Runtime/Input/TimeManager.h149
-rw-r--r--Runtime/Input/TouchPhaseEmulation.cpp719
-rw-r--r--Runtime/Input/TouchPhaseEmulation.h68
-rw-r--r--Runtime/Interfaces/IAnimation.cpp13
-rw-r--r--Runtime/Interfaces/IAnimation.h43
-rw-r--r--Runtime/Interfaces/IAnimationBinding.h69
-rw-r--r--Runtime/Interfaces/IAnimationStateNetworkProvider.cpp14
-rw-r--r--Runtime/Interfaces/IAnimationStateNetworkProvider.h22
-rw-r--r--Runtime/Interfaces/IAudio.cpp14
-rw-r--r--Runtime/Interfaces/IAudio.h53
-rw-r--r--Runtime/Interfaces/IClusterRenderer.cpp14
-rw-r--r--Runtime/Interfaces/IClusterRenderer.h16
-rw-r--r--Runtime/Interfaces/IGfxDevice.cpp54
-rw-r--r--Runtime/Interfaces/IGfxDevice.h28
-rw-r--r--Runtime/Interfaces/IPhysics.cpp14
-rw-r--r--Runtime/Interfaces/IPhysics.h72
-rw-r--r--Runtime/Interfaces/IPhysics2D.cpp23
-rw-r--r--Runtime/Interfaces/IPhysics2D.h29
-rw-r--r--Runtime/Interfaces/IRaycast.cpp56
-rw-r--r--Runtime/Interfaces/IRaycast.h73
-rw-r--r--Runtime/Interfaces/ITerrainManager.cpp14
-rw-r--r--Runtime/Interfaces/ITerrainManager.h46
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.csproj71
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.jam46
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.sln41
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/AddressFamily.cs73
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/BaseDomainPolicy.cs147
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/CrossDomainPolicyManager.cs245
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/FlashCrossDomainPolicy.cs213
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/FlashCrossDomainPolicyParser.cs265
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/ICrossDomainPolicy.cs42
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/IPAddress.cs503
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/IPv6Address.cs478
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/Locale.cs14
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/MiniParser.cs628
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/NoAccessPolicy.cs46
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/PolicyDownloadPolicy.cs59
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/SiteOfOriginPolicy.cs55
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UnityExtra.cs48
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/Uri.cs2225
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriFormatException.cs72
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriHostNameType.cs62
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriKind.cs42
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriPartial.cs44
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Properties/AssemblyInfo.cs37
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Tests/CrossDomainPolicyParserTests.csproj71
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Tests/FlashPolicyParserSocketTests.cs124
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Tests/FlashPolicyParserTests.cs209
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Tests/Properties/AssemblyInfo.cs36
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/Tests/UriToolsTests.cs23
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/UnityCrossDomainHelper.cs213
-rw-r--r--Runtime/Managed/CrossDomainPolicyParser/UriTools.cs20
-rw-r--r--Runtime/Math/AnimationCurve.cpp964
-rw-r--r--Runtime/Math/AnimationCurve.h324
-rw-r--r--Runtime/Math/Color.h293
-rw-r--r--Runtime/Math/ColorSpaceConversion.cpp17
-rw-r--r--Runtime/Math/ColorSpaceConversion.h123
-rw-r--r--Runtime/Math/FloatConversion.cpp98
-rw-r--r--Runtime/Math/FloatConversion.h696
-rw-r--r--Runtime/Math/FloatExceptions.cpp18
-rw-r--r--Runtime/Math/FloatExceptions.h6
-rw-r--r--Runtime/Math/Gradient.cpp310
-rw-r--r--Runtime/Math/Gradient.h219
-rw-r--r--Runtime/Math/MathTests.cpp726
-rw-r--r--Runtime/Math/Matrix3x3.cpp596
-rw-r--r--Runtime/Math/Matrix3x3.h119
-rw-r--r--Runtime/Math/Matrix4x4.cpp805
-rw-r--r--Runtime/Math/Matrix4x4.h410
-rw-r--r--Runtime/Math/Matrix4x4_NEON.asm197
-rw-r--r--Runtime/Math/Matrix4x4_NEON.s375
-rw-r--r--Runtime/Math/Matrix4x4_REF.cpp60
-rw-r--r--Runtime/Math/Matrix4x4_VFP.s149
-rw-r--r--Runtime/Math/PodMathTypes.h24
-rw-r--r--Runtime/Math/Polynomials.h93
-rw-r--r--Runtime/Math/Quaternion.cpp449
-rw-r--r--Runtime/Math/Quaternion.h405
-rw-r--r--Runtime/Math/Random/Random.h184
-rw-r--r--Runtime/Math/Random/rand.h81
-rw-r--r--Runtime/Math/Rect.h184
-rw-r--r--Runtime/Math/Simd/Matrix4x4Simd.h175
-rw-r--r--Runtime/Math/Simd/SimdMath.h240
-rw-r--r--Runtime/Math/Simd/SimdTest.cpp814
-rw-r--r--Runtime/Math/Simd/bool1.h42
-rw-r--r--Runtime/Math/Simd/bool4.h61
-rw-r--r--Runtime/Math/Simd/float1.h232
-rw-r--r--Runtime/Math/Simd/float4.h397
-rw-r--r--Runtime/Math/Simd/fpu.h245
-rw-r--r--Runtime/Math/Simd/intrinsic.h184
-rw-r--r--Runtime/Math/Simd/math.h678
-rw-r--r--Runtime/Math/Simd/neon.h548
-rw-r--r--Runtime/Math/Simd/ppu.h1944
-rw-r--r--Runtime/Math/Simd/quaternion.h253
-rw-r--r--Runtime/Math/Simd/sse.h237
-rw-r--r--Runtime/Math/Simd/xenon.h275
-rw-r--r--Runtime/Math/Simd/xform.h153
-rw-r--r--Runtime/Math/SphericalHarmonics.h83
-rw-r--r--Runtime/Math/Vector2.cpp13
-rw-r--r--Runtime/Math/Vector2.h126
-rw-r--r--Runtime/Math/Vector3.cpp361
-rw-r--r--Runtime/Math/Vector3.h205
-rw-r--r--Runtime/Math/Vector4.h55
-rw-r--r--Runtime/Misc/Allocator.cpp18
-rw-r--r--Runtime/Misc/Allocator.h17
-rw-r--r--Runtime/Misc/AllocatorLabelNames.h107
-rw-r--r--Runtime/Misc/AllocatorLabels.cpp63
-rw-r--r--Runtime/Misc/AllocatorLabels.h72
-rw-r--r--Runtime/Misc/AssetBundle.cpp183
-rw-r--r--Runtime/Misc/AssetBundle.h127
-rw-r--r--Runtime/Misc/AssetBundleUtility.cpp692
-rw-r--r--Runtime/Misc/AssetBundleUtility.h76
-rw-r--r--Runtime/Misc/AsyncOperation.cpp47
-rw-r--r--Runtime/Misc/AsyncOperation.h38
-rw-r--r--Runtime/Misc/BatchDeleteObjects.cpp221
-rw-r--r--Runtime/Misc/BatchDeleteObjects.h31
-rw-r--r--Runtime/Misc/BuildSettings.cpp188
-rw-r--r--Runtime/Misc/BuildSettings.h80
-rw-r--r--Runtime/Misc/CPUInfo.cpp120
-rw-r--r--Runtime/Misc/CPUInfo.h117
-rw-r--r--Runtime/Misc/CachingManager.cpp1207
-rw-r--r--Runtime/Misc/CachingManager.h215
-rw-r--r--Runtime/Misc/CaptureScreenshot.cpp605
-rw-r--r--Runtime/Misc/CaptureScreenshot.h30
-rw-r--r--Runtime/Misc/ComponentRequirement.cpp451
-rw-r--r--Runtime/Misc/ComponentRequirement.h33
-rw-r--r--Runtime/Misc/DebugUtility.cpp156
-rw-r--r--Runtime/Misc/DebugUtility.h30
-rw-r--r--Runtime/Misc/DeveloperConsole.cpp475
-rw-r--r--Runtime/Misc/DeveloperConsole.h154
-rw-r--r--Runtime/Misc/GOCreation.cpp95
-rw-r--r--Runtime/Misc/GOCreation.h18
-rw-r--r--Runtime/Misc/GOCreationTests.cpp54
-rw-r--r--Runtime/Misc/GameObjectUtility.cpp1276
-rw-r--r--Runtime/Misc/GameObjectUtility.h91
-rw-r--r--Runtime/Misc/GameObjectUtilityTests.cpp118
-rw-r--r--Runtime/Misc/GarbageCollectSharedAssets.cpp1188
-rw-r--r--Runtime/Misc/GarbageCollectSharedAssets.h31
-rw-r--r--Runtime/Misc/GraphicsDevicesDB.cpp1322
-rw-r--r--Runtime/Misc/GraphicsDevicesDB.h18
-rw-r--r--Runtime/Misc/GraphicsScriptingUtility.cpp36
-rw-r--r--Runtime/Misc/GraphicsScriptingUtility.h12
-rw-r--r--Runtime/Misc/GuiManager.cpp605
-rw-r--r--Runtime/Misc/InputEvent.cpp162
-rw-r--r--Runtime/Misc/InputEvent.h146
-rw-r--r--Runtime/Misc/MeshWelding.cpp249
-rw-r--r--Runtime/Misc/MeshWelding.h17
-rw-r--r--Runtime/Misc/MessageParameters.h35
-rw-r--r--Runtime/Misc/Player.cpp2228
-rw-r--r--Runtime/Misc/Player.h153
-rw-r--r--Runtime/Misc/PlayerSettings.cpp436
-rw-r--r--Runtime/Misc/PlayerSettings.h458
-rw-r--r--Runtime/Misc/Plugins.cpp231
-rw-r--r--Runtime/Misc/Plugins.h25
-rw-r--r--Runtime/Misc/PreloadManager.cpp1013
-rw-r--r--Runtime/Misc/PreloadManager.h209
-rw-r--r--Runtime/Misc/QualitySettings.cpp644
-rw-r--r--Runtime/Misc/QualitySettings.h133
-rw-r--r--Runtime/Misc/RegisterAllClasses.h6
-rw-r--r--Runtime/Misc/ReproductionLog.cpp706
-rw-r--r--Runtime/Misc/ReproductionLog.h80
-rw-r--r--Runtime/Misc/ResourceManager.cpp1292
-rw-r--r--Runtime/Misc/ResourceManager.h224
-rw-r--r--Runtime/Misc/ResourceManagerGUIDs.h5
-rw-r--r--Runtime/Misc/ResourceManagerUtility.cpp55
-rw-r--r--Runtime/Misc/ResourceManagerUtility.h21
-rw-r--r--Runtime/Misc/SaveAndLoadHelper.cpp1106
-rw-r--r--Runtime/Misc/SaveAndLoadHelper.h58
-rw-r--r--Runtime/Misc/SceneUnloading.cpp69
-rw-r--r--Runtime/Misc/SceneUnloading.h1
-rw-r--r--Runtime/Misc/SystemInfo.h320
-rw-r--r--Runtime/Misc/UTF8.cpp376
-rw-r--r--Runtime/Misc/UTF8.h12
-rw-r--r--Runtime/Misc/UserList.cpp195
-rw-r--r--Runtime/Misc/UserList.h63
-rw-r--r--Runtime/Misc/WWWCached.cpp319
-rw-r--r--Runtime/Misc/WWWCached.h59
-rw-r--r--Runtime/Modules/ExportModules.h32
-rw-r--r--Runtime/Modules/LoadDylib.cpp230
-rw-r--r--Runtime/Modules/LoadDylib.h13
-rw-r--r--Runtime/Modules/ModuleRegistration.cpp93
-rw-r--r--Runtime/Modules/ModuleRegistration.h21
-rw-r--r--Runtime/Modules/RegisterStaticallyLinkedModules.cpp73
-rw-r--r--Runtime/Modules/RegisterStaticallyLinkedModules.h3
-rw-r--r--Runtime/Mono/Coroutine.cpp379
-rw-r--r--Runtime/Mono/Coroutine.h45
-rw-r--r--Runtime/Mono/MonoAttributeHelpers.cpp191
-rw-r--r--Runtime/Mono/MonoAttributeHelpers.h19
-rw-r--r--Runtime/Mono/MonoBehaviour.cpp1894
-rw-r--r--Runtime/Mono/MonoBehaviour.h373
-rw-r--r--Runtime/Mono/MonoBehaviourAnimationBinding.cpp66
-rw-r--r--Runtime/Mono/MonoBehaviourAnimationBinding.h4
-rw-r--r--Runtime/Mono/MonoBehaviourSerialization.cpp1745
-rw-r--r--Runtime/Mono/MonoBehaviourSerialization.h51
-rw-r--r--Runtime/Mono/MonoBehaviourSerialization_Array.h583
-rw-r--r--Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.cpp269
-rw-r--r--Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.h58
-rw-r--r--Runtime/Mono/MonoBehaviourSerialization_YamlBackup.cpp201
-rw-r--r--Runtime/Mono/MonoBehaviourSerialization_flash.cpp239
-rw-r--r--Runtime/Mono/MonoBehaviourSerialization_metro.cpp178
-rw-r--r--Runtime/Mono/MonoExportUtility.cpp49
-rw-r--r--Runtime/Mono/MonoExportUtility.h14
-rw-r--r--Runtime/Mono/MonoFunctions.h418
-rw-r--r--Runtime/Mono/MonoHeapShot.cpp510
-rw-r--r--Runtime/Mono/MonoHeapShot.h81
-rw-r--r--Runtime/Mono/MonoHeapShotWriter.cpp267
-rw-r--r--Runtime/Mono/MonoHeapShotWriter.h64
-rw-r--r--Runtime/Mono/MonoIncludes.h40
-rw-r--r--Runtime/Mono/MonoManager.cpp2456
-rw-r--r--Runtime/Mono/MonoManager.h240
-rw-r--r--Runtime/Mono/MonoScopedThreadAttach.cpp72
-rw-r--r--Runtime/Mono/MonoScopedThreadAttach.h52
-rw-r--r--Runtime/Mono/MonoScript.cpp443
-rw-r--r--Runtime/Mono/MonoScript.h124
-rw-r--r--Runtime/Mono/MonoScriptCache.cpp556
-rw-r--r--Runtime/Mono/MonoScriptCache.h50
-rw-r--r--Runtime/Mono/MonoScriptManager.cpp150
-rw-r--r--Runtime/Mono/MonoScriptManager.h40
-rw-r--r--Runtime/Mono/MonoScriptType.h19
-rw-r--r--Runtime/Mono/MonoTypeSignatures.h170
-rw-r--r--Runtime/Mono/MonoTypes.h128
-rw-r--r--Runtime/Mono/MonoUtility.cpp685
-rw-r--r--Runtime/Mono/MonoUtility.h556
-rw-r--r--Runtime/Mono/tabledefs.h231
-rw-r--r--Runtime/NavMesh/DynamicMesh.cpp693
-rw-r--r--Runtime/NavMesh/DynamicMesh.h121
-rw-r--r--Runtime/NavMesh/DynamicMeshTests.cpp280
-rw-r--r--Runtime/NavMesh/HeightMeshQuery.cpp134
-rw-r--r--Runtime/NavMesh/HeightMeshQuery.h28
-rw-r--r--Runtime/NavMesh/HeightmapData.h26
-rw-r--r--Runtime/NavMesh/NavMesh.cpp446
-rw-r--r--Runtime/NavMesh/NavMesh.h110
-rw-r--r--Runtime/NavMesh/NavMeshAgent.cpp1129
-rw-r--r--Runtime/NavMesh/NavMeshAgent.h313
-rw-r--r--Runtime/NavMesh/NavMeshCarving.cpp206
-rw-r--r--Runtime/NavMesh/NavMeshCarving.h45
-rw-r--r--Runtime/NavMesh/NavMeshLayers.cpp156
-rw-r--r--Runtime/NavMesh/NavMeshLayers.h63
-rw-r--r--Runtime/NavMesh/NavMeshManager.cpp388
-rw-r--r--Runtime/NavMesh/NavMeshManager.h96
-rw-r--r--Runtime/NavMesh/NavMeshModule.jam114
-rw-r--r--Runtime/NavMesh/NavMeshObstacle.cpp349
-rw-r--r--Runtime/NavMesh/NavMeshObstacle.h155
-rw-r--r--Runtime/NavMesh/NavMeshPath.cpp19
-rw-r--r--Runtime/NavMesh/NavMeshPath.h90
-rw-r--r--Runtime/NavMesh/NavMeshProfiler.h71
-rw-r--r--Runtime/NavMesh/NavMeshSettings.cpp103
-rw-r--r--Runtime/NavMesh/NavMeshSettings.h69
-rw-r--r--Runtime/NavMesh/NavMeshTileCarving.cpp176
-rw-r--r--Runtime/NavMesh/NavMeshTileCarving.h12
-rw-r--r--Runtime/NavMesh/NavMeshTileConversion.cpp408
-rw-r--r--Runtime/NavMesh/NavMeshTileConversion.h11
-rw-r--r--Runtime/NavMesh/NavMeshTypes.h76
-rw-r--r--Runtime/NavMesh/NavigationModuleRegistration.cpp40
-rw-r--r--Runtime/NavMesh/OffMeshLink.cpp315
-rw-r--r--Runtime/NavMesh/OffMeshLink.h170
-rw-r--r--Runtime/NavMesh/ScriptBindings/NavMeshAgentBindings.txt263
-rw-r--r--Runtime/NavMesh/ScriptBindings/NavMeshBindings.txt265
-rw-r--r--Runtime/NavMesh/ScriptBindings/NavMeshObstacleBindings.txt40
-rw-r--r--Runtime/NavMesh/ScriptBindings/NavMeshPathBindings.txt133
-rw-r--r--Runtime/Network/BitStreamPacker.cpp556
-rw-r--r--Runtime/Network/BitStreamPacker.h76
-rw-r--r--Runtime/Network/DummyNetwork.cpp736
-rw-r--r--Runtime/Network/MasterServerInterface.cpp692
-rw-r--r--Runtime/Network/MasterServerInterface.h88
-rw-r--r--Runtime/Network/MulticastSocket.cpp224
-rw-r--r--Runtime/Network/MulticastSocket.h59
-rw-r--r--Runtime/Network/NetworkEnums.h196
-rw-r--r--Runtime/Network/NetworkManager.cpp2825
-rw-r--r--Runtime/Network/NetworkManager.h358
-rw-r--r--Runtime/Network/NetworkStubs.h77
-rw-r--r--Runtime/Network/NetworkUtility.cpp1076
-rw-r--r--Runtime/Network/NetworkUtility.h74
-rw-r--r--Runtime/Network/NetworkView.cpp733
-rw-r--r--Runtime/Network/NetworkView.h116
-rw-r--r--Runtime/Network/NetworkViewID.cpp193
-rw-r--r--Runtime/Network/NetworkViewID.h56
-rw-r--r--Runtime/Network/NetworkViewIDAllocator.cpp130
-rw-r--r--Runtime/Network/NetworkViewIDAllocator.h58
-rw-r--r--Runtime/Network/OnlineServices.h10
-rw-r--r--Runtime/Network/PackMonoRPC.cpp488
-rw-r--r--Runtime/Network/PackMonoRPC.h19
-rw-r--r--Runtime/Network/PackStateSpecialized.cpp124
-rw-r--r--Runtime/Network/PackStateSpecialized.h23
-rw-r--r--Runtime/Network/PlayerCommunicator/EditorConnection.cpp335
-rw-r--r--Runtime/Network/PlayerCommunicator/EditorConnection.h77
-rw-r--r--Runtime/Network/PlayerCommunicator/GeneralConnection.cpp666
-rw-r--r--Runtime/Network/PlayerCommunicator/GeneralConnection.h266
-rw-r--r--Runtime/Network/PlayerCommunicator/GeneralConnectionInternals.h56
-rw-r--r--Runtime/Network/PlayerCommunicator/PlayerConnection.cpp535
-rw-r--r--Runtime/Network/PlayerCommunicator/PlayerConnection.h111
-rw-r--r--Runtime/Network/ServerSocket.cpp117
-rw-r--r--Runtime/Network/ServerSocket.h31
-rw-r--r--Runtime/Network/SocketConsts.h89
-rw-r--r--Runtime/Network/SocketStreams.cpp411
-rw-r--r--Runtime/Network/SocketStreams.h94
-rw-r--r--Runtime/Network/SocketUtils.h56
-rw-r--r--Runtime/Network/Sockets.cpp384
-rw-r--r--Runtime/Network/Sockets.h92
-rw-r--r--Runtime/Physics2D/BoxCollider2D.cpp167
-rw-r--r--Runtime/Physics2D/BoxCollider2D.h38
-rw-r--r--Runtime/Physics2D/CircleCollider2D.cpp157
-rw-r--r--Runtime/Physics2D/CircleCollider2D.h39
-rw-r--r--Runtime/Physics2D/Collider2D.cpp441
-rw-r--r--Runtime/Physics2D/Collider2D.h90
-rw-r--r--Runtime/Physics2D/CollisionListener2D.cpp400
-rw-r--r--Runtime/Physics2D/CollisionListener2D.h123
-rw-r--r--Runtime/Physics2D/DistanceJoint2D.cpp161
-rw-r--r--Runtime/Physics2D/DistanceJoint2D.h46
-rw-r--r--Runtime/Physics2D/EdgeCollider2D.cpp176
-rw-r--r--Runtime/Physics2D/EdgeCollider2D.h45
-rw-r--r--Runtime/Physics2D/HingeJoint2D.cpp211
-rw-r--r--Runtime/Physics2D/HingeJoint2D.h61
-rw-r--r--Runtime/Physics2D/Joint2D.cpp201
-rw-r--r--Runtime/Physics2D/Joint2D.h59
-rw-r--r--Runtime/Physics2D/JointDescriptions2D.h101
-rw-r--r--Runtime/Physics2D/Physics2DManager.cpp1228
-rw-r--r--Runtime/Physics2D/Physics2DManager.h113
-rw-r--r--Runtime/Physics2D/Physics2DMaterial.cpp65
-rw-r--r--Runtime/Physics2D/Physics2DMaterial.h36
-rw-r--r--Runtime/Physics2D/Physics2DModule.jam178
-rw-r--r--Runtime/Physics2D/Physics2DModuleRegistration.cpp53
-rw-r--r--Runtime/Physics2D/Physics2DSettings.cpp167
-rw-r--r--Runtime/Physics2D/Physics2DSettings.h62
-rw-r--r--Runtime/Physics2D/PolygonCollider2D.cpp134
-rw-r--r--Runtime/Physics2D/PolygonCollider2D.h36
-rw-r--r--Runtime/Physics2D/PolygonColliderBase2D.cpp336
-rw-r--r--Runtime/Physics2D/PolygonColliderBase2D.h33
-rw-r--r--Runtime/Physics2D/RigidBody2D.h125
-rw-r--r--Runtime/Physics2D/Rigidbody2D.cpp700
-rw-r--r--Runtime/Physics2D/ScriptBindings/Physics2DBindings.txt820
-rw-r--r--Runtime/Physics2D/SliderJoint2D.cpp231
-rw-r--r--Runtime/Physics2D/SliderJoint2D.h65
-rw-r--r--Runtime/Physics2D/SpringJoint2D.cpp161
-rw-r--r--Runtime/Physics2D/SpringJoint2D.h54
-rw-r--r--Runtime/Physics2D/SpriteCollider2D.cpp91
-rw-r--r--Runtime/Physics2D/SpriteCollider2D.h37
-rw-r--r--Runtime/Profiler/CollectProfilerStats.cpp193
-rw-r--r--Runtime/Profiler/CollectProfilerStats.h18
-rw-r--r--Runtime/Profiler/DeprecatedFrameStatsProfiler.cpp23
-rw-r--r--Runtime/Profiler/DeprecatedFrameStatsProfiler.h28
-rw-r--r--Runtime/Profiler/ExternalGraphicsProfiler.h20
-rw-r--r--Runtime/Profiler/ExtractLoadedObjectInfo.cpp76
-rw-r--r--Runtime/Profiler/ExtractLoadedObjectInfo.h26
-rw-r--r--Runtime/Profiler/GPUProfiler.cpp100
-rw-r--r--Runtime/Profiler/GPUProfiler.h24
-rw-r--r--Runtime/Profiler/IntelGPAProfiler.cpp14
-rw-r--r--Runtime/Profiler/IntelGPAProfiler.h25
-rw-r--r--Runtime/Profiler/MemoryProfiler.cpp1003
-rw-r--r--Runtime/Profiler/MemoryProfiler.h237
-rw-r--r--Runtime/Profiler/MemoryProfilerStats.cpp158
-rw-r--r--Runtime/Profiler/MemoryProfilerStats.h75
-rw-r--r--Runtime/Profiler/MemoryProfilerTests.cpp73
-rw-r--r--Runtime/Profiler/ObjectMemoryProfiler.cpp245
-rw-r--r--Runtime/Profiler/ObjectMemoryProfiler.h19
-rw-r--r--Runtime/Profiler/Profiler.h187
-rw-r--r--Runtime/Profiler/ProfilerConnection.cpp353
-rw-r--r--Runtime/Profiler/ProfilerConnection.h91
-rw-r--r--Runtime/Profiler/ProfilerFrameData.cpp230
-rw-r--r--Runtime/Profiler/ProfilerFrameData.h134
-rw-r--r--Runtime/Profiler/ProfilerHistory.cpp560
-rw-r--r--Runtime/Profiler/ProfilerHistory.h113
-rw-r--r--Runtime/Profiler/ProfilerImpl.cpp1682
-rw-r--r--Runtime/Profiler/ProfilerImpl.h304
-rw-r--r--Runtime/Profiler/ProfilerProperty.cpp789
-rw-r--r--Runtime/Profiler/ProfilerProperty.h162
-rw-r--r--Runtime/Profiler/ProfilerStats.cpp432
-rw-r--r--Runtime/Profiler/ProfilerStats.h323
-rw-r--r--Runtime/Profiler/SerializationUtility.cpp24
-rw-r--r--Runtime/Profiler/SerializationUtility.h41
-rw-r--r--Runtime/Profiler/SharkProfiler.cpp93
-rw-r--r--Runtime/Profiler/SharkProfiler.h7
-rw-r--r--Runtime/Profiler/TimeHelper.cpp190
-rw-r--r--Runtime/Profiler/TimeHelper.h121
-rw-r--r--Runtime/Scripting/AS3Utility.cpp31
-rw-r--r--Runtime/Scripting/AS3Utility.h144
-rw-r--r--Runtime/Scripting/Backend/Flash/ScriptingBackendApi_Flash.cpp287
-rw-r--r--Runtime/Scripting/Backend/Flash/ScriptingBackendApi_Flash.h45
-rw-r--r--Runtime/Scripting/Backend/Flash/ScriptingMethodFactory_Flash.h40
-rw-r--r--Runtime/Scripting/Backend/Flash/ScriptingTypeProvider_Flash.h53
-rw-r--r--Runtime/Scripting/Backend/IScriptingTypeProvider.h16
-rw-r--r--Runtime/Scripting/Backend/Mono/ScriptingBackendApi_Mono.cpp280
-rw-r--r--Runtime/Scripting/Backend/Mono/ScriptingBackendApi_Mono.h15
-rw-r--r--Runtime/Scripting/Backend/Mono/ScriptingMethodFactory_Mono.h83
-rw-r--r--Runtime/Scripting/Backend/ScriptingArguments.cpp218
-rw-r--r--Runtime/Scripting/Backend/ScriptingArguments.h79
-rw-r--r--Runtime/Scripting/Backend/ScriptingBackendApi.h72
-rw-r--r--Runtime/Scripting/Backend/ScriptingInvocation.cpp223
-rw-r--r--Runtime/Scripting/Backend/ScriptingInvocation.h73
-rw-r--r--Runtime/Scripting/Backend/ScriptingInvocationNoArgs.cpp123
-rw-r--r--Runtime/Scripting/Backend/ScriptingInvocationNoArgs.h31
-rw-r--r--Runtime/Scripting/Backend/ScriptingMethodFactory.h18
-rw-r--r--Runtime/Scripting/Backend/ScriptingMethodRegistry.cpp165
-rw-r--r--Runtime/Scripting/Backend/ScriptingMethodRegistry.h60
-rw-r--r--Runtime/Scripting/Backend/ScriptingMethodRegistryTests.cpp237
-rw-r--r--Runtime/Scripting/Backend/ScriptingTypeRegistry.cpp214
-rw-r--r--Runtime/Scripting/Backend/ScriptingTypeRegistry.h33
-rw-r--r--Runtime/Scripting/Backend/ScriptingTypes.h202
-rw-r--r--Runtime/Scripting/Backend/Tests/ScriptingBackendApi_Tests.cpp35
-rw-r--r--Runtime/Scripting/Backend/Tests/ScriptingBackendApi_Tests.h30
-rw-r--r--Runtime/Scripting/CommonScriptingClasses.cpp268
-rw-r--r--Runtime/Scripting/CommonScriptingClasses.h206
-rw-r--r--Runtime/Scripting/DelayedCallUtility.cpp73
-rw-r--r--Runtime/Scripting/DelayedCallUtility.h16
-rw-r--r--Runtime/Scripting/GetComponent.cpp260
-rw-r--r--Runtime/Scripting/GetComponent.h16
-rw-r--r--Runtime/Scripting/ICallString.cpp40
-rw-r--r--Runtime/Scripting/ICallString.h91
-rw-r--r--Runtime/Scripting/MonoManager_Flash.cpp41
-rw-r--r--Runtime/Scripting/MonoManager_Flash.h27
-rw-r--r--Runtime/Scripting/MonoManager_WinRT.cpp105
-rw-r--r--Runtime/Scripting/MonoManager_WinRT.h27
-rw-r--r--Runtime/Scripting/ReadOnlyScriptingObjectOfType.h151
-rw-r--r--Runtime/Scripting/ScriptLanguagePortUtility.cpp11
-rw-r--r--Runtime/Scripting/ScriptLanguagePortUtility.h10
-rw-r--r--Runtime/Scripting/ScriptPopupMenus.cpp142
-rw-r--r--Runtime/Scripting/ScriptPopupMenus.h11
-rw-r--r--Runtime/Scripting/Scripting.cpp768
-rw-r--r--Runtime/Scripting/Scripting.h197
-rw-r--r--Runtime/Scripting/ScriptingExportUtility.h265
-rw-r--r--Runtime/Scripting/ScriptingManager.cpp150
-rw-r--r--Runtime/Scripting/ScriptingManager.h63
-rw-r--r--Runtime/Scripting/ScriptingObjectOfType.h142
-rw-r--r--Runtime/Scripting/ScriptingObjectWithIntPtrField.h139
-rw-r--r--Runtime/Scripting/ScriptingUtility.h48
-rw-r--r--Runtime/Scripting/Scripting_Flash.cpp176
-rw-r--r--Runtime/Scripting/Scripting_Mono.cpp247
-rw-r--r--Runtime/Scripting/Scripting_WinRT.cpp175
-rw-r--r--Runtime/Scripting/TextAsset.cpp70
-rw-r--r--Runtime/Scripting/TextAsset.h40
-rw-r--r--Runtime/Scripting/WinRTHelper.h29
-rw-r--r--Runtime/Scripting/WinRTMarshalers.cpp306
-rw-r--r--Runtime/Scripting/WinRTMarshalers.h66
-rw-r--r--Runtime/Serialize/AwakeFromLoadQueue.cpp395
-rw-r--r--Runtime/Serialize/AwakeFromLoadQueue.h83
-rw-r--r--Runtime/Serialize/Blobification/BlobSize.h161
-rw-r--r--Runtime/Serialize/Blobification/BlobTests.cpp358
-rw-r--r--Runtime/Serialize/Blobification/BlobWrite.cpp148
-rw-r--r--Runtime/Serialize/Blobification/BlobWrite.h220
-rw-r--r--Runtime/Serialize/Blobification/BlobWriteTargetSupport.cpp31
-rw-r--r--Runtime/Serialize/Blobification/BlobWriteTargetSupport.h6
-rw-r--r--Runtime/Serialize/Blobification/OffsetPtrTest.cpp31
-rw-r--r--Runtime/Serialize/Blobification/ReduceCopyData.h8
-rw-r--r--Runtime/Serialize/Blobification/offsetptr.h257
-rw-r--r--Runtime/Serialize/BuildTargetVerification.h78
-rw-r--r--Runtime/Serialize/CacheWrap.cpp461
-rw-r--r--Runtime/Serialize/CacheWrap.h229
-rw-r--r--Runtime/Serialize/DumpSerializedDataToText.cpp372
-rw-r--r--Runtime/Serialize/DumpSerializedDataToText.h24
-rw-r--r--Runtime/Serialize/FileCache.cpp461
-rw-r--r--Runtime/Serialize/FileCache.h360
-rw-r--r--Runtime/Serialize/FloatStringConversion.cpp80
-rw-r--r--Runtime/Serialize/FloatStringConversion.h16
-rw-r--r--Runtime/Serialize/IterateTypeTree.h155
-rw-r--r--Runtime/Serialize/LoadProgress.h24
-rw-r--r--Runtime/Serialize/PathNamePersistentManager.cpp51
-rw-r--r--Runtime/Serialize/PathNamePersistentManager.h31
-rw-r--r--Runtime/Serialize/PersistentManager.cpp2291
-rw-r--r--Runtime/Serialize/PersistentManager.h473
-rw-r--r--Runtime/Serialize/Remapper.h297
-rw-r--r--Runtime/Serialize/SerializationMetaFlags.h292
-rw-r--r--Runtime/Serialize/SerializationTests.cpp242
-rw-r--r--Runtime/Serialize/SerializeConversion.h34
-rw-r--r--Runtime/Serialize/SerializeTraits.h533
-rw-r--r--Runtime/Serialize/SerializeTraitsBase.h61
-rw-r--r--Runtime/Serialize/SerializeUtility.h95
-rw-r--r--Runtime/Serialize/SerializedFile.cpp1520
-rw-r--r--Runtime/Serialize/SerializedFile.h264
-rw-r--r--Runtime/Serialize/SerializedFileTests.cpp39
-rw-r--r--Runtime/Serialize/SwapEndianArray.h27
-rw-r--r--Runtime/Serialize/SwapEndianBytes.h89
-rw-r--r--Runtime/Serialize/TransferFunctionFwd.h14
-rw-r--r--Runtime/Serialize/TransferFunctions/ProxyTransfer.cpp250
-rw-r--r--Runtime/Serialize/TransferFunctions/ProxyTransfer.h159
-rw-r--r--Runtime/Serialize/TransferFunctions/ProxyTransferTests.cpp216
-rw-r--r--Runtime/Serialize/TransferFunctions/RemapPPtrTransfer.cpp31
-rw-r--r--Runtime/Serialize/TransferFunctions/RemapPPtrTransfer.h178
-rw-r--r--Runtime/Serialize/TransferFunctions/RemapPPtrTransferTests.cpp57
-rw-r--r--Runtime/Serialize/TransferFunctions/SafeBinaryRead.cpp487
-rw-r--r--Runtime/Serialize/TransferFunctions/SafeBinaryRead.h290
-rw-r--r--Runtime/Serialize/TransferFunctions/SerializeTransfer.h14
-rw-r--r--Runtime/Serialize/TransferFunctions/StreamedBinaryRead.cpp54
-rw-r--r--Runtime/Serialize/TransferFunctions/StreamedBinaryRead.h174
-rw-r--r--Runtime/Serialize/TransferFunctions/StreamedBinaryWrite.cpp76
-rw-r--r--Runtime/Serialize/TransferFunctions/StreamedBinaryWrite.h187
-rw-r--r--Runtime/Serialize/TransferFunctions/TransferBase.h177
-rw-r--r--Runtime/Serialize/TransferFunctions/TransferNameConversions.cpp76
-rw-r--r--Runtime/Serialize/TransferFunctions/TransferNameConversions.h56
-rw-r--r--Runtime/Serialize/TransferFunctions/YAMLRead.cpp238
-rw-r--r--Runtime/Serialize/TransferFunctions/YAMLRead.h422
-rw-r--r--Runtime/Serialize/TransferFunctions/YAMLSerializeTraits.cpp139
-rw-r--r--Runtime/Serialize/TransferFunctions/YAMLSerializeTraits.h236
-rw-r--r--Runtime/Serialize/TransferFunctions/YAMLWrite.cpp183
-rw-r--r--Runtime/Serialize/TransferFunctions/YAMLWrite.h340
-rw-r--r--Runtime/Serialize/TransferFunctions/YAMLWriteTests.cpp58
-rw-r--r--Runtime/Serialize/TransferUtility.cpp461
-rw-r--r--Runtime/Serialize/TransferUtility.h100
-rw-r--r--Runtime/Serialize/TypeTree.cpp531
-rw-r--r--Runtime/Serialize/TypeTree.h130
-rw-r--r--Runtime/Serialize/WriteData.h27
-rw-r--r--Runtime/Serialize/WriteTypeToBuffer.h21
-rw-r--r--Runtime/Shaders/BufferedVBO.cpp142
-rw-r--r--Runtime/Shaders/BufferedVBO.h35
-rw-r--r--Runtime/Shaders/ComputeShader.cpp425
-rw-r--r--Runtime/Shaders/ComputeShader.h164
-rw-r--r--Runtime/Shaders/GraphicsCaps.cpp610
-rw-r--r--Runtime/Shaders/GraphicsCaps.h435
-rw-r--r--Runtime/Shaders/Material.cpp1096
-rw-r--r--Runtime/Shaders/Material.h233
-rw-r--r--Runtime/Shaders/MaterialIsTransparent.h14
-rw-r--r--Runtime/Shaders/MaterialProperties.cpp195
-rw-r--r--Runtime/Shaders/MaterialProperties.h85
-rw-r--r--Runtime/Shaders/NameToObjectMap.h153
-rw-r--r--Runtime/Shaders/Shader.cpp734
-rw-r--r--Runtime/Shaders/Shader.h174
-rw-r--r--Runtime/Shaders/ShaderKeywords.cpp90
-rw-r--r--Runtime/Shaders/ShaderKeywords.h43
-rw-r--r--Runtime/Shaders/ShaderNameRegistry.cpp235
-rw-r--r--Runtime/Shaders/ShaderNameRegistry.h54
-rw-r--r--Runtime/Shaders/ShaderSupportScripts.cpp19
-rw-r--r--Runtime/Shaders/ShaderSupportScripts.h26
-rw-r--r--Runtime/Shaders/ShaderTags.h27
-rw-r--r--Runtime/Shaders/ShaderTests.cpp285
-rw-r--r--Runtime/Shaders/UnityPropertySheet.cpp144
-rw-r--r--Runtime/Shaders/UnityPropertySheet.h67
-rw-r--r--Runtime/Shaders/VBO.cpp170
-rw-r--r--Runtime/Shaders/VBO.h308
-rw-r--r--Runtime/Terrain/DetailDatabase.cpp1106
-rw-r--r--Runtime/Terrain/DetailDatabase.h270
-rw-r--r--Runtime/Terrain/DetailRenderer.cpp303
-rw-r--r--Runtime/Terrain/DetailRenderer.h56
-rw-r--r--Runtime/Terrain/Heightmap.cpp892
-rw-r--r--Runtime/Terrain/Heightmap.h199
-rw-r--r--Runtime/Terrain/ImposterRenderTexture.cpp229
-rw-r--r--Runtime/Terrain/ImposterRenderTexture.h65
-rw-r--r--Runtime/Terrain/PerlinNoise.cpp73
-rw-r--r--Runtime/Terrain/PerlinNoise.h16
-rw-r--r--Runtime/Terrain/QuadTreeNodeRenderer.cpp124
-rw-r--r--Runtime/Terrain/QuadTreeNodeRenderer.h40
-rw-r--r--Runtime/Terrain/ScriptBindings/TerrainDataBindings.txt406
-rw-r--r--Runtime/Terrain/ScriptBindings/Terrains.txt675
-rw-r--r--Runtime/Terrain/ScriptBindings/WindZoneBindings.txt183
-rw-r--r--Runtime/Terrain/SplatDatabase.cpp600
-rw-r--r--Runtime/Terrain/SplatDatabase.h111
-rw-r--r--Runtime/Terrain/SplatMaterials.cpp228
-rw-r--r--Runtime/Terrain/SplatMaterials.h41
-rw-r--r--Runtime/Terrain/TerrainData.cpp169
-rw-r--r--Runtime/Terrain/TerrainData.h66
-rw-r--r--Runtime/Terrain/TerrainIndexGenerator.cpp379
-rw-r--r--Runtime/Terrain/TerrainIndexGenerator.h12
-rw-r--r--Runtime/Terrain/TerrainInstance.cpp344
-rw-r--r--Runtime/Terrain/TerrainInstance.h132
-rw-r--r--Runtime/Terrain/TerrainManager.cpp280
-rw-r--r--Runtime/Terrain/TerrainManager.h47
-rw-r--r--Runtime/Terrain/TerrainModule.jam67
-rw-r--r--Runtime/Terrain/TerrainModuleRegistration.cpp37
-rw-r--r--Runtime/Terrain/TerrainRenderer.cpp798
-rw-r--r--Runtime/Terrain/TerrainRenderer.h168
-rw-r--r--Runtime/Terrain/Tree.cpp78
-rw-r--r--Runtime/Terrain/Tree.h36
-rw-r--r--Runtime/Terrain/TreeDatabase.cpp283
-rw-r--r--Runtime/Terrain/TreeDatabase.h152
-rw-r--r--Runtime/Terrain/TreeRenderer.cpp1223
-rw-r--r--Runtime/Terrain/TreeRenderer.h90
-rw-r--r--Runtime/Terrain/Wind.cpp149
-rw-r--r--Runtime/Terrain/Wind.h103
-rw-r--r--Runtime/Testing/ConsoleTestReporter.cpp216
-rw-r--r--Runtime/Testing/ConsoleTestReporter.h63
-rw-r--r--Runtime/Testing/HighLevelTest.cpp537
-rw-r--r--Runtime/Testing/HighLevelTest.h6
-rw-r--r--Runtime/Testing/JobSchedulerTest/Test.sln21
-rw-r--r--Runtime/Testing/JobSchedulerTest/Test.vcproj162
-rw-r--r--Runtime/Testing/JobSchedulerTest/main.cpp82
-rw-r--r--Runtime/Testing/MathPerformanceTest/MatrixMultiplyTest.cpp166
-rw-r--r--Runtime/Testing/TestFixtures.h163
-rw-r--r--Runtime/Testing/Testing.cpp344
-rw-r--r--Runtime/Testing/Testing.h56
-rw-r--r--Runtime/Threads/AtomicOps.h180
-rw-r--r--Runtime/Threads/AtomicRefCounter.h30
-rw-r--r--Runtime/Threads/Event.h20
-rw-r--r--Runtime/Threads/JobGroupRecycler.h59
-rw-r--r--Runtime/Threads/JobScheduler.cpp447
-rw-r--r--Runtime/Threads/JobScheduler.h100
-rw-r--r--Runtime/Threads/Mutex.cpp70
-rw-r--r--Runtime/Threads/Mutex.h86
-rw-r--r--Runtime/Threads/Posix/PlatformMutex.cpp51
-rw-r--r--Runtime/Threads/Posix/PlatformMutex.h29
-rw-r--r--Runtime/Threads/Posix/PlatformSemaphore.h69
-rw-r--r--Runtime/Threads/Posix/PlatformThread.cpp185
-rw-r--r--Runtime/Threads/Posix/PlatformThread.h56
-rw-r--r--Runtime/Threads/Posix/PlatformThreadSpecificValue.h63
-rw-r--r--Runtime/Threads/ProfilerMutex.h118
-rw-r--r--Runtime/Threads/Semaphore.h67
-rw-r--r--Runtime/Threads/SimpleLock.h53
-rw-r--r--Runtime/Threads/SpinlockMutex.h63
-rw-r--r--Runtime/Threads/Thread.cpp123
-rw-r--r--Runtime/Threads/Thread.h112
-rw-r--r--Runtime/Threads/ThreadHelper.cpp195
-rw-r--r--Runtime/Threads/ThreadHelper.h30
-rw-r--r--Runtime/Threads/ThreadSharedObject.h30
-rw-r--r--Runtime/Threads/ThreadSpecificValue.cpp7
-rw-r--r--Runtime/Threads/ThreadSpecificValue.h108
-rw-r--r--Runtime/Threads/ThreadUtility.h91
-rw-r--r--Runtime/Threads/ThreadedStreamBuffer.cpp437
-rw-r--r--Runtime/Threads/ThreadedStreamBuffer.h250
-rw-r--r--Runtime/Threads/Winapi/PlatformEvent.h70
-rw-r--r--Runtime/Threads/Winapi/PlatformMutex.cpp54
-rw-r--r--Runtime/Threads/Winapi/PlatformMutex.h28
-rw-r--r--Runtime/Threads/Winapi/PlatformSemaphore.h59
-rw-r--r--Runtime/Threads/Winapi/PlatformThread.cpp140
-rw-r--r--Runtime/Threads/Winapi/PlatformThread.h67
-rw-r--r--Runtime/Threads/Winapi/PlatformThreadSpecificValue.h61
-rw-r--r--Runtime/Utilities/Annotations.h52
-rw-r--r--Runtime/Utilities/Argv.cpp129
-rw-r--r--Runtime/Utilities/Argv.h30
-rw-r--r--Runtime/Utilities/ArrayUtility.h9
-rw-r--r--Runtime/Utilities/BitSetSerialization.h78
-rw-r--r--Runtime/Utilities/BitUtility.h189
-rw-r--r--Runtime/Utilities/CStringHash.h52
-rw-r--r--Runtime/Utilities/CopyPaste.h10
-rw-r--r--Runtime/Utilities/DateTime.cpp45
-rw-r--r--Runtime/Utilities/DateTime.h30
-rw-r--r--Runtime/Utilities/EditorPrefsTests.cpp160
-rw-r--r--Runtime/Utilities/EndianHelper.h148
-rw-r--r--Runtime/Utilities/EnumFlags.h13
-rw-r--r--Runtime/Utilities/ErrorExit.cpp165
-rw-r--r--Runtime/Utilities/ErrorExit.h77
-rw-r--r--Runtime/Utilities/File.cpp831
-rw-r--r--Runtime/Utilities/File.h194
-rw-r--r--Runtime/Utilities/FileStripped.h10
-rw-r--r--Runtime/Utilities/FileUtilities.cpp27
-rw-r--r--Runtime/Utilities/FileUtilities.h113
-rw-r--r--Runtime/Utilities/GLSLUtilities.cpp835
-rw-r--r--Runtime/Utilities/GLSLUtilities.h30
-rw-r--r--Runtime/Utilities/GUID.cpp241
-rw-r--r--Runtime/Utilities/GUID.h73
-rw-r--r--Runtime/Utilities/GlobalPreferences.cpp151
-rw-r--r--Runtime/Utilities/GlobalPreferences.h15
-rw-r--r--Runtime/Utilities/Hash128.cpp33
-rw-r--r--Runtime/Utilities/Hash128.h49
-rw-r--r--Runtime/Utilities/HashFunctions.h87
-rw-r--r--Runtime/Utilities/InitializeAndCleanup.cpp51
-rw-r--r--Runtime/Utilities/InitializeAndCleanup.h12
-rw-r--r--Runtime/Utilities/LODUtility.cpp86
-rw-r--r--Runtime/Utilities/LODUtility.h7
-rw-r--r--Runtime/Utilities/LinkedList.h385
-rw-r--r--Runtime/Utilities/LogAssert.cpp1294
-rw-r--r--Runtime/Utilities/LogAssert.h340
-rw-r--r--Runtime/Utilities/LogUtility.cpp4
-rw-r--r--Runtime/Utilities/LogUtility.h28
-rw-r--r--Runtime/Utilities/MemoryPool.cpp210
-rw-r--r--Runtime/Utilities/MemoryPool.h280
-rw-r--r--Runtime/Utilities/MemoryUtilities.cpp15
-rw-r--r--Runtime/Utilities/MemoryUtilities.h3
-rw-r--r--Runtime/Utilities/NonCopyable.h18
-rw-r--r--Runtime/Utilities/OptimizationUtility.h20
-rw-r--r--Runtime/Utilities/PathNameUtility.cpp601
-rw-r--r--Runtime/Utilities/PathNameUtility.h153
-rw-r--r--Runtime/Utilities/PlayerPrefs.h65
-rw-r--r--Runtime/Utilities/Prefetch.h52
-rw-r--r--Runtime/Utilities/RecursionLimit.h54
-rw-r--r--Runtime/Utilities/ReportHardware.cpp391
-rw-r--r--Runtime/Utilities/ReportHardware.h19
-rw-r--r--Runtime/Utilities/SpatialHash.cpp90
-rw-r--r--Runtime/Utilities/SpatialHash.h50
-rw-r--r--Runtime/Utilities/Stacktrace.cpp246
-rw-r--r--Runtime/Utilities/Stacktrace.h12
-rw-r--r--Runtime/Utilities/StaticAssert.h55
-rw-r--r--Runtime/Utilities/StrideIterator.h89
-rw-r--r--Runtime/Utilities/TypeUtilities.h20
-rw-r--r--Runtime/Utilities/URLUtility.h8
-rw-r--r--Runtime/Utilities/UniqueIDGenerator.cpp34
-rw-r--r--Runtime/Utilities/UniqueIDGenerator.h18
-rw-r--r--Runtime/Utilities/UnityString.h10
-rw-r--r--Runtime/Utilities/UserAuthorizationManager.cpp118
-rw-r--r--Runtime/Utilities/UserAuthorizationManager.h47
-rw-r--r--Runtime/Utilities/Utility.h214
-rw-r--r--Runtime/Utilities/UtilityTests.cpp629
-rw-r--r--Runtime/Utilities/VFPUtility.h43
-rw-r--r--Runtime/Utilities/ValidateArgs.h81
-rw-r--r--Runtime/Utilities/WavFileUtility.cpp54
-rw-r--r--Runtime/Utilities/WavFileUtility.h6
-rw-r--r--Runtime/Utilities/Word.cpp659
-rw-r--r--Runtime/Utilities/Word.h227
-rw-r--r--Runtime/Utilities/WordTests.cpp151
-rw-r--r--Runtime/Utilities/algorithm_utility.h115
-rw-r--r--Runtime/Utilities/delayed_set.h45
-rw-r--r--Runtime/Utilities/dense_hash_map.h243
-rw-r--r--Runtime/Utilities/densehashtable.h899
-rw-r--r--Runtime/Utilities/dynamic_array.h340
-rw-r--r--Runtime/Utilities/dynamic_array_tests.cpp254
-rw-r--r--Runtime/Utilities/dynamic_bitset.h1144
-rw-r--r--Runtime/Utilities/dynamic_block_vector.h135
-rw-r--r--Runtime/Utilities/fixed_bitset.h53
-rw-r--r--Runtime/Utilities/sorted_vector.h358
-rw-r--r--Runtime/Utilities/triple.h46
-rw-r--r--Runtime/Utilities/type_traits.h250
-rw-r--r--Runtime/Utilities/vector_map.h268
-rw-r--r--Runtime/Utilities/vector_set.h229
-rw-r--r--Runtime/Utilities/vector_utility.h95
-rw-r--r--Runtime/Video/BaseVideoTexture.cpp517
-rw-r--r--Runtime/Video/BaseVideoTexture.h110
-rw-r--r--Runtime/Video/MoviePlayback.cpp879
-rw-r--r--Runtime/Video/MoviePlayback.h212
-rw-r--r--Runtime/Video/MovieTexture.cpp262
-rw-r--r--Runtime/Video/MovieTexture.h77
-rw-r--r--Runtime/Video/ScriptBindings/MovieTextureBindings.txt92
-rw-r--r--Runtime/Video/ScriptBindings/UnityEngineWebCamTexture.txt200
-rw-r--r--Runtime/Video/VideoTexture.h252
-rw-r--r--Runtime/mecanim/animation/avatar.cpp1799
-rw-r--r--Runtime/mecanim/animation/avatar.h706
-rw-r--r--Runtime/mecanim/animation/blendtree.cpp967
-rw-r--r--Runtime/mecanim/animation/blendtree.h285
-rw-r--r--Runtime/mecanim/animation/clipmuscle.cpp1366
-rw-r--r--Runtime/mecanim/animation/clipmuscle.h264
-rw-r--r--Runtime/mecanim/animation/constantclip.cpp34
-rw-r--r--Runtime/mecanim/animation/constantclip.h36
-rw-r--r--Runtime/mecanim/animation/curvedata.cpp118
-rw-r--r--Runtime/mecanim/animation/curvedata.h82
-rw-r--r--Runtime/mecanim/animation/damp.cpp24
-rw-r--r--Runtime/mecanim/animation/damp.h42
-rw-r--r--Runtime/mecanim/animation/denseclip.cpp63
-rw-r--r--Runtime/mecanim/animation/denseclip.h47
-rw-r--r--Runtime/mecanim/animation/poseblender.cpp228
-rw-r--r--Runtime/mecanim/animation/poseblender.h113
-rw-r--r--Runtime/mecanim/animation/streamedclip.cpp196
-rw-r--r--Runtime/mecanim/animation/streamedclip.h93
-rw-r--r--Runtime/mecanim/bind.h833
-rw-r--r--Runtime/mecanim/bitset.h155
-rw-r--r--Runtime/mecanim/defs.h116
-rw-r--r--Runtime/mecanim/generic/crc32.h208
-rw-r--r--Runtime/mecanim/generic/stringtable.cpp131
-rw-r--r--Runtime/mecanim/generic/stringtable.h128
-rw-r--r--Runtime/mecanim/generic/typetraits.h47
-rw-r--r--Runtime/mecanim/generic/valuearray.cpp925
-rw-r--r--Runtime/mecanim/generic/valuearray.h256
-rw-r--r--Runtime/mecanim/graph/binarynode.h291
-rw-r--r--Runtime/mecanim/graph/factory.cpp172
-rw-r--r--Runtime/mecanim/graph/factory.h35
-rw-r--r--Runtime/mecanim/graph/genericnode.h1093
-rw-r--r--Runtime/mecanim/graph/graph.cpp476
-rw-r--r--Runtime/mecanim/graph/graph.h188
-rw-r--r--Runtime/mecanim/graph/node.cpp11
-rw-r--r--Runtime/mecanim/graph/node.h346
-rw-r--r--Runtime/mecanim/graph/plug.h123
-rw-r--r--Runtime/mecanim/graph/plugbinder.cpp180
-rw-r--r--Runtime/mecanim/graph/plugbinder.h85
-rw-r--r--Runtime/mecanim/graph/quaternionnode.h283
-rw-r--r--Runtime/mecanim/graph/unarynode.h112
-rw-r--r--Runtime/mecanim/graph/xformnode.h348
-rw-r--r--Runtime/mecanim/human/hand.cpp351
-rw-r--r--Runtime/mecanim/human/hand.h124
-rw-r--r--Runtime/mecanim/human/handle.h40
-rw-r--r--Runtime/mecanim/human/human.cpp2405
-rw-r--r--Runtime/mecanim/human/human.h390
-rw-r--r--Runtime/mecanim/math/axes.h128
-rw-r--r--Runtime/mecanim/math/collider.h55
-rw-r--r--Runtime/mecanim/memory.h618
-rw-r--r--Runtime/mecanim/skeleton/skeleton.cpp515
-rw-r--r--Runtime/mecanim/skeleton/skeleton.h186
-rw-r--r--Runtime/mecanim/statemachine/statemachine.cpp967
-rw-r--r--Runtime/mecanim/statemachine/statemachine.h547
-rw-r--r--Runtime/mecanim/string.h96
-rw-r--r--Runtime/mecanim/types.h221
2043 files changed, 497373 insertions, 0 deletions
diff --git a/Runtime/Allocator/AllocationHeader.h b/Runtime/Allocator/AllocationHeader.h
new file mode 100644
index 0000000..8d66159
--- /dev/null
+++ b/Runtime/Allocator/AllocationHeader.h
@@ -0,0 +1,159 @@
+#ifndef ALLOCATION_HEADER_H_
+#define ALLOCATION_HEADER_H_
+
+#if ENABLE_MEMORY_MANAGER
+
+#include "Runtime/Profiler/MemoryProfiler.h"
+
+#define USE_MEMORY_DEBUGGING (!UNITY_RELEASE && !(UNITY_LINUX && UNITY_64))
+#if UNITY_LINUX
+#pragma warning "FIXME LINUX: 64bit memory debugging"
+#endif
+
+/*
+Allocation Header:
+(12345hhhhhhhhmmmm____________) (1-n:paddingcount, h: header, m:memoryprofiler)
+hasPadding: 1 bit
+size: 31 bit
+*********USE_MEMORY_DEBUGGING*********
+ allocator: 16 bit
+ magicValue: 12 bit
+*********USE_MEMORY_DEBUGGING*********
+---------------
+Followed by x bytes requested by MemoryProfiler
+----------------------------------------
+Actual Allocated Data
+----------------------------------------
+*********USE_MEMORY_DEBUGGING*********
+Followed by a footer if memory guarding is enabled
+*********USE_MEMORY_DEBUGGING*********
+*/
+
+struct AllocationHeader
+{
+public:
+ static void Set(void* ptr, int id, int size, int padCount, int align);
+ static AllocationHeader* GetHeader(const void* ptr);
+ static ProfilerAllocationHeader* GetProfilerHeader(const void* ptr);
+
+ UInt32 GetPadding () const { return m_HasPadding ? *(((UInt32*)(this))-1) : 0; }
+ size_t GetRequestedSize () const { return m_AllocationSize; }
+
+ static size_t CalculateNeededAllocationSize( size_t size, int align );
+ static void ValidateIntegrity(void* ptr, int id, int align = -1);
+ static void* GetRealPointer( void* ptr );
+ static int GetRequiredPadding(void* realptr, int align);
+ static int GetHeaderSize();
+ static AllocationHeader* GetHeaderFromRealPointer( const void* realptr );
+
+ int GetOverheadSize();
+
+private:
+
+ // prev byte is padding count, if there is padding
+ UInt32 m_HasPadding : 1;
+ UInt32 m_AllocationSize : 31;
+
+#if USE_MEMORY_DEBUGGING
+ // DEBUG:
+ SInt16 m_AllocatorIdentifier;
+ UInt16 m_Magic : 12;
+ static const UInt32 kMagicValue = 0xDFA;
+ static const UInt32 kFooterSize = 4;
+#else
+ static const UInt32 kFooterSize = 0;
+#endif
+
+ // followed by x bytes used for memory profiling header (0 if no memory profiling)
+};
+
+inline void AllocationHeader::Set(void* ptr, int id, int size, int padCount, int align)
+{
+ AllocationHeader* header = GetHeader(ptr);
+ header->m_AllocationSize = size;
+ header->m_HasPadding = padCount != 0;
+ if(header->m_HasPadding)
+ *(((UInt32*)(header))-1) = padCount;
+#if USE_MEMORY_DEBUGGING
+ // set header
+ if(header->m_HasPadding) // set leading bytes
+ memset(((char*)header)-padCount,0xAA,padCount-4);
+
+ header->m_AllocatorIdentifier = id;
+ header->m_Magic = kMagicValue;
+ // set footer
+ memset(((char*)ptr)+size,0xFD,kFooterSize);
+#endif
+}
+
+inline ProfilerAllocationHeader* AllocationHeader::GetProfilerHeader( const void* ptr )
+{
+ return (ProfilerAllocationHeader*)( (const char*)ptr - MemoryProfiler::GetHeaderSize() );
+}
+
+inline AllocationHeader* AllocationHeader::GetHeader( const void* ptr )
+{
+ return (AllocationHeader*)( (const char*)ptr - GetHeaderSize() );
+}
+
+inline int AllocationHeader::GetHeaderSize()
+{
+ return sizeof(AllocationHeader) + MemoryProfiler::GetHeaderSize();
+}
+
+inline int AllocationHeader::GetRequiredPadding( void* realptr, int align )
+{
+ return align - ((((int)realptr + GetHeaderSize() - 1)&(align - 1)) + 1);
+}
+
+inline size_t AllocationHeader::CalculateNeededAllocationSize( size_t size, int align )
+{
+ int alignMask = align-1;
+ return size + GetHeaderSize() + kFooterSize + alignMask;
+}
+
+inline void* AllocationHeader::GetRealPointer( void* ptr )
+{
+ AllocationHeader* header = GetHeader(ptr);
+ int padCount = header->GetPadding();
+ return ((char*)header) - padCount;
+}
+
+inline int AllocationHeader::GetOverheadSize()
+{
+ int alignMask = kDefaultMemoryAlignment-1; // estimate
+ return GetHeaderSize() + kFooterSize + alignMask;
+}
+
+inline AllocationHeader* AllocationHeader::GetHeaderFromRealPointer( const void* realptr )
+{
+#if USE_MEMORY_DEBUGGING
+ unsigned char* ptr = (unsigned char*)realptr;
+ while(*ptr == 0xAA)
+ ptr++;
+ AllocationHeader* header = (AllocationHeader*)ptr;
+ if(header->m_Magic != kMagicValue)
+ header = (AllocationHeader*)(ptr+4);
+ return header;
+#else
+ return NULL;
+#endif
+}
+
+inline void AllocationHeader::ValidateIntegrity( void* ptr, int id, int /*align*/ )
+{
+#if USE_MEMORY_DEBUGGING
+ AllocationHeader* header = GetHeader(ptr);
+ Assert(header->m_Magic == AllocationHeader::kMagicValue);
+ Assert(id == -1 || id == header->m_AllocatorIdentifier);
+
+ int size = header->m_AllocationSize;
+ unsigned char* footer = ((unsigned char*)ptr)+size;
+ for(int i = 0; i < kFooterSize; i++, footer++)
+ Assert(*footer == 0xFD);
+#endif
+}
+
+#endif
+
+#endif
diff --git a/Runtime/Allocator/BaseAllocator.cpp b/Runtime/Allocator/BaseAllocator.cpp
new file mode 100644
index 0000000..6ce5d0e
--- /dev/null
+++ b/Runtime/Allocator/BaseAllocator.cpp
@@ -0,0 +1,21 @@
+#include "UnityPrefix.h"
+#include "BaseAllocator.h"
+
+void* BaseAllocator::Reallocate( void* p, size_t size, int align )
+{
+// ErrorString("Not implemented");
+ return 0;
+}
+
+static UInt32 g_IncrementIdentifier = 0x10;
+BaseAllocator::BaseAllocator(const char* name)
+: m_TotalRequestedBytes(0)
+, m_TotalReservedMemory(0)
+, m_BookKeepingMemoryUsage(0)
+, m_PeakRequestedBytes(0)
+, m_NumAllocations(0)
+, m_Name(name)
+{
+ m_AllocatorIdentifier = g_IncrementIdentifier++;
+}
+
diff --git a/Runtime/Allocator/BaseAllocator.h b/Runtime/Allocator/BaseAllocator.h
new file mode 100644
index 0000000..a60f95d
--- /dev/null
+++ b/Runtime/Allocator/BaseAllocator.h
@@ -0,0 +1,81 @@
+#ifndef BASE_ALLOCATOR_H_
+#define BASE_ALLOCATOR_H_
+
+#if UNITY_OSX || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+#include <stddef.h> // for size_t
+#endif
+
+#include "Runtime/Misc/AllocatorLabels.h"
+
+class BaseAllocator
+{
+public:
+ BaseAllocator(const char* name);
+ virtual ~BaseAllocator () {}
+
+ virtual void* Allocate (size_t size, int align) = 0;
+ virtual void* Reallocate (void* p, size_t size, int align);
+ virtual void Deallocate (void* p) = 0;
+
+ virtual bool Contains (const void* p) = 0;
+ virtual bool IsAssigned() const { return true; }
+ virtual bool CheckIntegrity() { return true; }
+ virtual bool ValidatePointer(void* /*ptr*/) { return true; }
+ // return the actual number of requests bytes
+ virtual size_t GetAllocatedMemorySize() const { return m_TotalRequestedBytes; }
+
+ // get total used size (including overhead allocations)
+ virtual size_t GetAllocatorSizeTotalUsed() const { return m_TotalRequestedBytes + m_BookKeepingMemoryUsage; }
+
+ // get the reserved size of the allocator (including all overhead memory allocated)
+ virtual size_t GetReservedSizeTotal() const { return m_TotalReservedMemory; }
+
+ // get the peak allocated size of the allocator
+ virtual size_t GetPeakAllocatedMemorySize() const { return m_PeakRequestedBytes; }
+
+ // return the free block count for each pow2
+ virtual void GetFreeBlockCount(int* /*freeCount*/, int /*size*/) { return; }
+ // return the used block count for each pow2
+ virtual void GetUsedBlockCount(int* /*usedCount*/, int /*size*/) { return; }
+
+ virtual size_t GetPtrSize(const void* /*ptr*/) const {return 0;}
+ // return NULL if allocator does not allocate the memory profile header
+ virtual ProfilerAllocationHeader* GetProfilerHeader(const void* /*ptr*/) const { return NULL; }
+
+ virtual const char* GetName() const { return m_Name; }
+
+ virtual void ThreadInitialize(BaseAllocator* /*allocator*/) {}
+ virtual void ThreadCleanup() {}
+
+ virtual void FrameMaintenance(bool /*cleanup*/) {}
+
+protected:
+ void RegisterAllocationData(size_t requestedSize, size_t overhead);
+ void RegisterDeallocationData(size_t requestedSize, size_t overhead);
+
+ const char* m_Name;
+ UInt32 m_AllocatorIdentifier;
+ size_t m_TotalRequestedBytes; // Memory requested by the allocator
+ size_t m_TotalReservedMemory; // All memory reserved by the allocator
+ size_t m_BookKeepingMemoryUsage; // memory used for bookkeeping (headers etc.)
+ size_t m_PeakRequestedBytes; // Memory requested by the allocator
+ UInt32 m_NumAllocations; // Allocation count
+
+};
+
+inline void BaseAllocator::RegisterAllocationData(size_t requestedSize, size_t overhead)
+{
+ m_TotalRequestedBytes += requestedSize;
+ m_BookKeepingMemoryUsage += overhead;
+ m_PeakRequestedBytes = m_TotalRequestedBytes > m_PeakRequestedBytes ? m_TotalRequestedBytes : m_PeakRequestedBytes;
+ m_NumAllocations++;
+}
+
+inline void BaseAllocator::RegisterDeallocationData(size_t requestedSize, size_t overhead)
+{
+ m_TotalRequestedBytes -= requestedSize;
+ m_BookKeepingMemoryUsage -= overhead;
+ m_NumAllocations--;
+}
+
+#endif
diff --git a/Runtime/Allocator/DualThreadAllocator.cpp b/Runtime/Allocator/DualThreadAllocator.cpp
new file mode 100644
index 0000000..921ecf9
--- /dev/null
+++ b/Runtime/Allocator/DualThreadAllocator.cpp
@@ -0,0 +1,263 @@
+#include "UnityPrefix.h"
+#include "DualThreadAllocator.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Allocator/DynamicHeapAllocator.h"
+
+#if ENABLE_MEMORY_MANAGER
+
+class DelayedPointerDeletionManager
+{
+public:
+ DelayedPointerDeletionManager(BaseAllocator* mainAlloc, BaseAllocator* threadAlloc) :
+ m_HasPendingDeletes (0),
+ m_MainAlloctor (mainAlloc),
+ m_ThreadAlloctor (threadAlloc),
+ m_MainThreadPendingPointers(NULL),
+ m_MainThreadPendingCount (0),
+ m_MainThreadPendingReserved (0) {}
+ ~DelayedPointerDeletionManager(){ CleanupPendingMainThreadPointers(); }
+
+ bool HasPending() {return m_HasPendingDeletes != 0;}
+ void AddPointerToMainThreadDealloc( void* ptr) ;
+ void CleanupPendingMainThreadPointers() ;
+ void DeallocateLocalMemory();
+private:
+ void GrowPendingBuffer();
+ void** m_MainThreadPendingPointers;
+ UInt32 m_MainThreadPendingCount;
+ UInt32 m_MainThreadPendingReserved;
+
+ volatile int m_HasPendingDeletes;
+ BaseAllocator* m_MainAlloctor;
+ BaseAllocator* m_ThreadAlloctor;
+ Mutex m_MainThreadPendingPointersMutex;
+};
+
+void DelayedPointerDeletionManager::AddPointerToMainThreadDealloc( void* ptr)
+{
+ Mutex::AutoLock autolock(m_MainThreadPendingPointersMutex);
+ if(++m_MainThreadPendingCount > m_MainThreadPendingReserved)
+ GrowPendingBuffer();
+ m_MainThreadPendingPointers[m_MainThreadPendingCount-1] = ptr;
+ m_HasPendingDeletes = 1;
+}
+
+void DelayedPointerDeletionManager::CleanupPendingMainThreadPointers()
+{
+ Mutex::AutoLock autolock(m_MainThreadPendingPointersMutex);
+ Assert(Thread::CurrentThreadIsMainThread());
+ m_HasPendingDeletes = 0;
+
+ for(UInt32 i = 0; i < m_MainThreadPendingCount; ++i)
+ m_MainAlloctor->Deallocate(m_MainThreadPendingPointers[i]);
+ m_MainThreadPendingCount = 0;
+}
+
+void DelayedPointerDeletionManager::DeallocateLocalMemory()
+{
+ Assert(!m_HasPendingDeletes && m_MainThreadPendingCount == 0);
+ m_ThreadAlloctor->Deallocate(m_MainThreadPendingPointers);
+ m_MainThreadPendingPointers = NULL;
+ m_MainThreadPendingReserved = 0;
+};
+
+void DelayedPointerDeletionManager::GrowPendingBuffer()
+{
+ const UInt32 kInitialBufferSize = 128;
+ m_MainThreadPendingReserved = std::max(m_MainThreadPendingReserved*2, kInitialBufferSize);
+ m_MainThreadPendingPointers = (void**) m_ThreadAlloctor->Reallocate(m_MainThreadPendingPointers, m_MainThreadPendingReserved*sizeof(void*), kDefaultMemoryAlignment);
+}
+
+
+template <class UnderlyingAllocator>
+DualThreadAllocator<UnderlyingAllocator>::DualThreadAllocator(const char* name, BaseAllocator* mainAllocator, BaseAllocator* threadAllocator)
+: BaseAllocator(name)
+{
+ m_MainAllocator = (UnderlyingAllocator*)mainAllocator;
+ m_ThreadAllocator = (UnderlyingAllocator*)threadAllocator;
+ m_DelayedDeletion = NULL;
+}
+
+template <class UnderlyingAllocator>
+void DualThreadAllocator<UnderlyingAllocator>::ThreadCleanup()
+{
+ if(Thread::CurrentThreadIsMainThread())
+ UNITY_DELETE(m_DelayedDeletion, kMemManager);
+}
+
+template <class UnderlyingAllocator>
+void DualThreadAllocator<UnderlyingAllocator>::FrameMaintenance(bool cleanup)
+{
+ if(m_DelayedDeletion)
+ {
+ m_DelayedDeletion->CleanupPendingMainThreadPointers();
+ if(cleanup)
+ m_DelayedDeletion->DeallocateLocalMemory();
+ }
+}
+
+template <class UnderlyingAllocator>
+UnderlyingAllocator* DualThreadAllocator<UnderlyingAllocator>::GetCurrentAllocator()
+{
+ if(Thread::CurrentThreadIsMainThread())
+ return m_MainAllocator;
+ else
+ return m_ThreadAllocator;
+}
+
+
+template <class UnderlyingAllocator>
+void* DualThreadAllocator<UnderlyingAllocator>::Allocate( size_t size, int align )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+ bool isMainThread = alloc == m_MainAllocator;
+ if(isMainThread && m_DelayedDeletion && m_DelayedDeletion->HasPending())
+ m_DelayedDeletion->CleanupPendingMainThreadPointers();
+
+ return alloc->UnderlyingAllocator::Allocate(size, align);
+}
+
+template <class UnderlyingAllocator>
+void* DualThreadAllocator<UnderlyingAllocator>::Reallocate( void* p, size_t size, int align )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+
+ if(alloc->UnderlyingAllocator::Contains(p))
+ return alloc->UnderlyingAllocator::Reallocate(p, size, align);
+
+ UnderlyingAllocator* containingAlloc = NULL;
+ if (alloc == m_MainAllocator)
+ {
+ Assert(m_ThreadAllocator->UnderlyingAllocator::Contains(p));
+ containingAlloc = m_ThreadAllocator;
+ }
+ else
+ {
+ Assert(m_MainAllocator->UnderlyingAllocator::Contains(p));
+ containingAlloc = m_MainAllocator;
+ }
+
+ size_t oldSize = containingAlloc->UnderlyingAllocator::GetPtrSize(p);
+ void* ptr = alloc->UnderlyingAllocator::Allocate(size, align);
+ memcpy(ptr, p, std::min(size,oldSize));
+ Deallocate(p);
+ return ptr;
+}
+
+template <class UnderlyingAllocator>
+void DualThreadAllocator<UnderlyingAllocator>::Deallocate( void* p )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+
+ if(alloc->UnderlyingAllocator::Contains(p))
+ return alloc->UnderlyingAllocator::Deallocate(p);
+
+ if (alloc == m_MainAllocator)
+ {
+ DebugAssert(m_ThreadAllocator->UnderlyingAllocator::Contains(p));
+ m_ThreadAllocator->UnderlyingAllocator::Deallocate(p);
+ }
+ else
+ {
+ DebugAssert(m_MainAllocator->UnderlyingAllocator::Contains(p));
+ if(!m_DelayedDeletion)
+ {
+ SET_ALLOC_OWNER(NULL);
+ m_DelayedDeletion = UNITY_NEW(DelayedPointerDeletionManager(m_MainAllocator, m_ThreadAllocator), kMemManager);
+ }
+ m_DelayedDeletion->AddPointerToMainThreadDealloc(p);
+ }
+}
+
+template <class UnderlyingAllocator>
+bool DualThreadAllocator<UnderlyingAllocator>::TryDeallocate( void* p )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+
+ if(alloc->UnderlyingAllocator::Contains(p))
+ {
+ alloc->UnderlyingAllocator::Deallocate(p);
+ return true;
+ }
+
+ if (m_ThreadAllocator->UnderlyingAllocator::Contains(p))
+ {
+ m_ThreadAllocator->UnderlyingAllocator::Deallocate(p);
+ return true;
+ }
+ if(m_MainAllocator->UnderlyingAllocator::Contains(p))
+ {
+ if(!m_DelayedDeletion)
+ m_DelayedDeletion = UNITY_NEW(DelayedPointerDeletionManager(m_MainAllocator, m_ThreadAllocator), kMemManager);
+ m_DelayedDeletion->AddPointerToMainThreadDealloc(p);
+ return true;
+ }
+ return false;
+}
+
+
+template <class UnderlyingAllocator>
+bool DualThreadAllocator<UnderlyingAllocator>::Contains( const void* p )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+ if(alloc->UnderlyingAllocator::Contains(p))
+ return true;
+ if(m_ThreadAllocator->UnderlyingAllocator::Contains(p))
+ return true;
+ if(m_MainAllocator->UnderlyingAllocator::Contains(p))
+ return true;
+ return false;
+}
+
+template <class UnderlyingAllocator>
+size_t DualThreadAllocator<UnderlyingAllocator>::GetAllocatedMemorySize( ) const
+{
+ return m_MainAllocator->GetAllocatedMemorySize() + (m_ThreadAllocator?m_ThreadAllocator->GetAllocatedMemorySize():0);
+}
+
+template <class UnderlyingAllocator>
+size_t DualThreadAllocator<UnderlyingAllocator>::GetAllocatorSizeTotalUsed() const
+{
+ return m_MainAllocator->GetAllocatorSizeTotalUsed() + (m_ThreadAllocator?m_ThreadAllocator->GetAllocatorSizeTotalUsed():0);
+}
+
+template <class UnderlyingAllocator>
+size_t DualThreadAllocator<UnderlyingAllocator>::GetReservedSizeTotal() const
+{
+ return m_MainAllocator->GetReservedSizeTotal() + (m_ThreadAllocator?m_ThreadAllocator->GetReservedSizeTotal():0);
+}
+
+template <class UnderlyingAllocator>
+size_t DualThreadAllocator<UnderlyingAllocator>::GetPtrSize( const void* ptr ) const
+{
+ // all allocators have the same allocation header
+ return m_MainAllocator->UnderlyingAllocator::GetPtrSize(ptr);
+}
+
+template <class UnderlyingAllocator>
+ProfilerAllocationHeader* DualThreadAllocator<UnderlyingAllocator>::GetProfilerHeader( const void* ptr ) const
+{
+ return m_MainAllocator->UnderlyingAllocator::GetProfilerHeader(ptr);
+}
+
+
+template <class UnderlyingAllocator>
+bool DualThreadAllocator<UnderlyingAllocator>::CheckIntegrity()
+{
+ bool valid = m_ThreadAllocator->UnderlyingAllocator::CheckIntegrity();
+ if(Thread::CurrentThreadIsMainThread())
+ valid &= m_MainAllocator->UnderlyingAllocator::CheckIntegrity();
+ Assert(valid);
+ return valid;
+}
+
+template <class UnderlyingAllocator>
+bool DualThreadAllocator<UnderlyingAllocator>::ValidatePointer(void* ptr)
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+ return alloc->UnderlyingAllocator::ValidatePointer(ptr);
+}
+
+template class DualThreadAllocator< DynamicHeapAllocator< LowLevelAllocator > >;
+
+#endif // #if ENABLE_MEMORY_MANAGER
diff --git a/Runtime/Allocator/DualThreadAllocator.h b/Runtime/Allocator/DualThreadAllocator.h
new file mode 100644
index 0000000..089b4ca
--- /dev/null
+++ b/Runtime/Allocator/DualThreadAllocator.h
@@ -0,0 +1,55 @@
+#ifndef DUALTHREAD_ALLOCATOR_H_
+#define DUALTHREAD_ALLOCATOR_H_
+
+#if ENABLE_MEMORY_MANAGER
+
+#include "Runtime/Allocator/BaseAllocator.h"
+#include "Runtime/Threads/ThreadSpecificValue.h"
+
+class DelayedPointerDeletionManager;
+
+// Dual Thread Allocator is an indirection to a real allocator
+
+// Has pointer to the main allocator (nonlocking)
+// Has pointer to the shared thread allocator (locking)
+
+template <class UnderlyingAllocator>
+class DualThreadAllocator : public BaseAllocator
+{
+public:
+ // when constructing it will be from the main thread
+ DualThreadAllocator(const char* name, BaseAllocator* mainAllocator, BaseAllocator* threadAllocator);
+ virtual ~DualThreadAllocator() {}
+
+ virtual void* Allocate(size_t size, int align);
+ virtual void* Reallocate (void* p, size_t size, int align);
+ virtual void Deallocate (void* p);
+ virtual bool Contains (const void* p);
+
+ virtual size_t GetAllocatedMemorySize() const;
+ virtual size_t GetAllocatorSizeTotalUsed() const;
+ virtual size_t GetReservedSizeTotal() const;
+
+ virtual size_t GetPtrSize(const void* ptr) const;
+ virtual ProfilerAllocationHeader* GetProfilerHeader(const void* ptr) const;
+
+ virtual void ThreadCleanup();
+
+ virtual bool CheckIntegrity();
+ virtual bool ValidatePointer(void* ptr);
+
+ bool TryDeallocate (void* p);
+
+ virtual void FrameMaintenance(bool cleanup);
+
+private:
+ UnderlyingAllocator* GetCurrentAllocator();
+
+ UnderlyingAllocator* m_MainAllocator;
+ UnderlyingAllocator* m_ThreadAllocator;
+
+ DelayedPointerDeletionManager* m_DelayedDeletion;
+};
+
+#endif
+#endif
diff --git a/Runtime/Allocator/DynamicHeapAllocator.cpp b/Runtime/Allocator/DynamicHeapAllocator.cpp
new file mode 100644
index 0000000..84ef6ef
--- /dev/null
+++ b/Runtime/Allocator/DynamicHeapAllocator.cpp
@@ -0,0 +1,486 @@
+#include "UnityPrefix.h"
+#include "DynamicHeapAllocator.h"
+#if ENABLE_MEMORY_MANAGER
+
+#include "Runtime/Allocator/AllocationHeader.h"
+#include "tlsf/tlsf.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+#if UNITY_XENON
+#include "PlatformDependent/Xbox360/Source/XenonMemory.h"
+#endif
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/AtomicOps.h"
+
+template<class LLAllocator>
+DynamicHeapAllocator<LLAllocator>::DynamicHeapAllocator(UInt32 poolIncrementSize, size_t splitLimit, bool useLocking, const char* name)
+: BaseAllocator(name), m_UseLocking(useLocking)
+{
+ m_SplitLimit = splitLimit;
+ m_RequestedPoolSize = poolIncrementSize;
+ m_FirstLargeAllocation = NULL;
+}
+
+template<class LLAllocator>
+DynamicHeapAllocator<LLAllocator>::~DynamicHeapAllocator()
+{
+ Mutex::AutoLock m(m_DHAMutex);
+
+ for(ListIterator<PoolElement> i=m_SmallTLSFPools.begin();i != m_SmallTLSFPools.end();i++)
+ {
+ PoolElement& pool = *i;
+ tlsf_destroy(pool.tlsfPool);
+ LLAllocator::Free(pool.memoryBase);
+ }
+ for(ListIterator<PoolElement> i=m_LargeTLSFPools.begin();i != m_LargeTLSFPools.end();i++)
+ {
+ PoolElement& pool = *i;
+ tlsf_destroy(pool.tlsfPool);
+ LLAllocator::Free(pool.memoryBase);
+ }
+}
+
+template<class LLAllocator>
+typename DynamicHeapAllocator<LLAllocator>::PoolElement& DynamicHeapAllocator<LLAllocator>::GetActivePool( size_t size )
+{
+ return GetPoolList( size ).front();
+}
+
+template<class LLAllocator>
+typename DynamicHeapAllocator<LLAllocator>::PoolList& DynamicHeapAllocator<LLAllocator>::GetPoolList( size_t size )
+{
+ return size < m_SplitLimit? m_SmallTLSFPools: m_LargeTLSFPools;
+}
+
+template<class LLAllocator>
+void* DynamicHeapAllocator<LLAllocator>::Allocate(size_t size, int align)
+{
+ if(m_UseLocking)
+ m_DHAMutex.Lock();
+
+ DebugAssert(align > 0 && align <= 16*1024 && IsPowerOfTwo(align));
+
+ size_t realSize = AllocationHeader::CalculateNeededAllocationSize(size, align);;
+
+ /// align size to tlsf block requirements
+ if(realSize > 32)
+ {
+ int tlsfalign = (1 << HighestBit(realSize >> 5))-1;
+ realSize = (realSize + tlsfalign) & ~tlsfalign;
+ }
+
+ char* ptr = NULL;
+ if(!GetPoolList(realSize).empty())
+ ptr = (char*)tlsf_malloc(GetActivePool(realSize).tlsfPool, realSize);
+ LargeAllocations* largeAlloc = NULL;
+ if(ptr == NULL)
+ {
+ // only try to make new tlsfBlocks if the amount is less than a 16th of the blocksize - else spill to LargeAllocations
+ if(size < m_RequestedPoolSize/4)
+ {
+ // not enough space in the current block.
+ // Iterate from the back, and find one that fits the allocation
+ // put the found block at the head of the list
+ PoolList& poolList = GetPoolList(realSize);
+ ListIterator<PoolElement> pool = poolList.end();
+ --pool;
+ while(pool != poolList.end()) // List wraps around so end is the element just before the head of the list
+ {
+ ptr = (char*)tlsf_malloc(pool->tlsfPool, realSize);
+ if(ptr != NULL)
+ {
+ // push_front removes the node from the list and reinserts it at the start
+ Mutex::AutoLock m(m_DHAMutex);
+ poolList.push_front(*pool);
+ break;
+ }
+ --pool;
+ }
+
+ if(ptr == 0)
+ {
+ int allocatePoolSize = m_RequestedPoolSize;
+ void* memoryBlock = NULL;
+
+ #if UNITY_ANDROID
+ // HACK:
+ // on android we reload libunity (and keep activity around)
+ // this results in leaks, as MemoryManager::m_InitialFallbackAllocator is never freed actually
+ // more to it: even if we free the mem - the hole left is taken by other mallocs
+ // so we cant reuse it on re-init, effectively allocing anew
+ // this hack can be removed when unity can be cleanly reloaded (sweet dreams)
+ static const int _InitialFallbackAllocMemBlock_Size = 1024*1024;
+ static bool _InitialFallbackAllocMemBlock_Taken = false;
+ static char _InitialFallbackAllocMemBlock[_InitialFallbackAllocMemBlock_Size];
+
+ if(!_InitialFallbackAllocMemBlock_Taken)
+ {
+ Assert(_InitialFallbackAllocMemBlock_Size == m_RequestedPoolSize);
+ _InitialFallbackAllocMemBlock_Taken = true;
+ memoryBlock = _InitialFallbackAllocMemBlock;
+ }
+ #endif
+
+ while(!memoryBlock && allocatePoolSize > size*2)
+ {
+ memoryBlock = LLAllocator::Malloc(allocatePoolSize);
+ if(!memoryBlock)
+ allocatePoolSize /= 2;
+ }
+
+ if(memoryBlock)
+ {
+ m_TotalReservedMemory += allocatePoolSize;
+ PoolElement* newPoolPtr = (PoolElement*)LLAllocator::Malloc(sizeof(PoolElement));
+ PoolElement& newPool = *new (newPoolPtr) PoolElement();
+ newPool.memoryBase = (char*)memoryBlock;
+ newPool.memorySize = allocatePoolSize;
+ newPool.tlsfPool = tlsf_create(memoryBlock, allocatePoolSize);
+ newPool.allocationCount = 0;
+ newPool.allocationSize = 0;
+
+ {
+ Mutex::AutoLock lock(m_DHAMutex);
+ poolList.push_front(newPool);
+ }
+
+ ptr = (char*)tlsf_malloc(GetActivePool(realSize).tlsfPool, realSize);
+ }
+ }
+ }
+ if(ptr == 0)
+ {
+ // large allocation that don't fit on a clean block
+ largeAlloc = (LargeAllocations*)LLAllocator::Malloc(sizeof(LargeAllocations));
+ largeAlloc->allocation = (char*)LLAllocator::Malloc(realSize);
+ if(largeAlloc->allocation == NULL)
+ {
+ printf_console("DynamicHeapAllocator out of memory - Could not get memory for large allocation");
+ if(m_UseLocking)
+ m_DHAMutex.Unlock();
+ return NULL;
+ }
+ largeAlloc->next = m_FirstLargeAllocation;
+ largeAlloc->size = size;
+ m_TotalReservedMemory += size;
+ {
+ Mutex::AutoLock lock(m_DHAMutex);
+ m_FirstLargeAllocation = largeAlloc;
+ }
+ ptr = largeAlloc->allocation;
+ }
+ }
+
+ if(!largeAlloc)
+ {
+ GetActivePool(realSize).allocationCount++;
+ GetActivePool(realSize).allocationSize+=size;
+ }
+
+ void* realPtr = AddHeaderAndFooter(ptr, size, align);
+ RegisterAllocation(realPtr);
+
+ if (largeAlloc)
+ largeAlloc->returnedPtr = realPtr;
+
+ if(m_UseLocking)
+ m_DHAMutex.Unlock();
+
+ return realPtr;
+}
+
+template<class LLAllocator>
+void* DynamicHeapAllocator<LLAllocator>::Reallocate (void* p, size_t size, int align)
+{
+ if (p == NULL)
+ return Allocate(size, align);
+
+ if(m_UseLocking)
+ m_DHAMutex.Lock();
+
+ AllocationHeader::ValidateIntegrity(p, m_AllocatorIdentifier, align);
+ RegisterDeallocation(p);
+
+ size_t oldSize = GetPtrSize(p);
+ size_t oldPadCount = AllocationHeader::GetHeader(p)->GetPadding();
+
+ void* realPtr = AllocationHeader::GetRealPointer(p);
+ size_t realSize = AllocationHeader::CalculateNeededAllocationSize(size, align);
+
+ PoolElement* allocedPool = FindPoolFromPtr(realPtr);
+ char* realNewPtr = NULL;
+ if(allocedPool != NULL)
+ {
+ realNewPtr = (char*)tlsf_realloc(allocedPool->tlsfPool, realPtr, realSize);
+ }
+
+ if(realNewPtr == NULL)
+ {
+ // if we didn't succeed doing a tlsf reallocation, just allocate, copy and delete
+ RegisterAllocation(p); // Reregister the allocation again
+ if(m_UseLocking)
+ m_DHAMutex.Unlock();
+ void* newPtr = Allocate(size, align);
+ if (newPtr != NULL)
+ memcpy(newPtr, p, ( oldSize < size ? oldSize : size ));
+ Deallocate(p);
+ return newPtr;
+ }
+
+ int newPadCount = AllocationHeader::GetRequiredPadding(realNewPtr, align);
+
+ if (newPadCount != oldPadCount){
+ // new ptr needs different align padding. move memory and repad
+ char* srcptr = realNewPtr + AllocationHeader::GetHeaderSize() + oldPadCount;
+ char* dstptr = realNewPtr + AllocationHeader::GetHeaderSize() + newPadCount;
+ memmove(dstptr, srcptr, ( oldSize < size ? oldSize : size ) );
+ }
+ void* newptr = AddHeaderAndFooter(realNewPtr, size, align);
+ RegisterAllocation(newptr);
+
+ if(m_UseLocking)
+ m_DHAMutex.Unlock();
+ return newptr;
+}
+
+template<class LLAllocator>
+void DynamicHeapAllocator<LLAllocator>::Deallocate (void* p)
+{
+ if (p == NULL)
+ return;
+
+ if(m_UseLocking)
+ m_DHAMutex.Lock();
+
+ AllocationHeader::ValidateIntegrity(p, m_AllocatorIdentifier);
+ RegisterDeallocation(p);
+
+ void* realpointer = AllocationHeader::GetRealPointer(p);
+
+ PoolElement* allocedPool = FindPoolFromPtr(realpointer);
+ if(allocedPool != NULL)
+ {
+ allocedPool->allocationCount--;
+ allocedPool->allocationSize-=GetPtrSize(p);
+ tlsf_free(allocedPool->tlsfPool, realpointer);
+ if(allocedPool->allocationCount == 0)
+ {
+ {
+ Mutex::AutoLock lock(m_DHAMutex);
+ allocedPool->RemoveFromList();
+ }
+ tlsf_destroy(allocedPool->tlsfPool);
+ LLAllocator::Free(allocedPool->memoryBase);
+ m_TotalReservedMemory -= allocedPool->memorySize;
+ allocedPool->~PoolElement();
+ LLAllocator::Free(allocedPool);
+ }
+ }
+ else
+ {
+ // is this a largeAllocation
+ LargeAllocations* alloc = m_FirstLargeAllocation;
+ LargeAllocations* prev = NULL;
+ while (alloc != NULL)
+ {
+ if (alloc->allocation == realpointer)
+ {
+ LLAllocator::Free(realpointer);
+ m_TotalReservedMemory -= alloc->size;
+ alloc->allocation = NULL;
+ alloc->size = 0;
+ {
+ Mutex::AutoLock lock(m_DHAMutex);
+ if(prev == NULL)
+ m_FirstLargeAllocation = alloc->next;
+ else
+ prev->next = alloc->next;
+ }
+ LLAllocator::Free(alloc);
+ if(m_UseLocking)
+ m_DHAMutex.Unlock();
+ return;
+ }
+ prev = alloc;
+ alloc = alloc->next;
+ }
+ ErrorString("Could not find reallocpointer in LargeAllocationlist");
+ }
+ if(m_UseLocking)
+ m_DHAMutex.Unlock();
+}
+
+template<class LLAllocator>
+bool DynamicHeapAllocator<LLAllocator>::ValidatePointer(void* ptr)
+{
+ AllocationHeader::ValidateIntegrity(ptr, -1, -1);
+ return true;
+}
+
+void AllocBlockValidate (void* memptr, size_t /*size*/, int isused, void* /*userptr*/)
+{
+ if (isused )
+ AllocationHeader::ValidateIntegrity(((char*)AllocationHeader::GetHeaderFromRealPointer(memptr))+AllocationHeader::GetHeaderSize(),-1,-1);
+}
+
+template<class LLAllocator>
+bool DynamicHeapAllocator<LLAllocator>::CheckIntegrity()
+{
+ Mutex::AutoLock m(m_DHAMutex);
+ for(ListIterator<PoolElement> i=m_SmallTLSFPools.begin();i != m_SmallTLSFPools.end();i++)
+ tlsf_check_heap(i->tlsfPool);
+ for(ListIterator<PoolElement> i=m_LargeTLSFPools.begin();i != m_LargeTLSFPools.end();i++)
+ tlsf_check_heap(i->tlsfPool);
+
+ for(ListIterator<PoolElement> i=m_SmallTLSFPools.begin();i != m_SmallTLSFPools.end();i++)
+ tlsf_walk_heap (i->tlsfPool, &AllocBlockValidate, NULL);
+ for(ListIterator<PoolElement> i=m_LargeTLSFPools.begin();i != m_LargeTLSFPools.end();i++)
+ tlsf_walk_heap (i->tlsfPool, &AllocBlockValidate, NULL);
+
+ return true;
+}
+
+struct BlockCounter
+{
+ int* blockCount;
+ int size;
+};
+
+void FreeBlockCount (void* /*memptr*/, size_t size, int isused, void* userptr)
+{
+ BlockCounter* counter = (BlockCounter*)userptr;
+ if (!isused )
+ {
+ int index = HighestBit(size);
+ index = index >= counter->size ? counter->size-1 : index;
+ counter->blockCount[index]++;
+ }
+}
+
+template<class LLAllocator>
+void DynamicHeapAllocator<LLAllocator>::GetFreeBlockCount( int* freeCount, int size )
+{
+ Mutex::AutoLock m(m_DHAMutex);
+ memset(freeCount, 0, size*sizeof(int));
+ BlockCounter counter = {freeCount, size };
+ for(ListIterator<PoolElement> i=m_SmallTLSFPools.begin();i != m_SmallTLSFPools.end();i++)
+ tlsf_walk_heap (i->tlsfPool, &FreeBlockCount, &counter);
+ for(ListIterator<PoolElement> i=m_LargeTLSFPools.begin();i != m_LargeTLSFPools.end();i++)
+ tlsf_walk_heap (i->tlsfPool, &FreeBlockCount, &counter);
+}
+
+void UsedBlockCount (void* /*memptr*/, size_t size, int isused, void* userptr)
+{
+ BlockCounter* counter = (BlockCounter*)userptr;
+ if (isused )
+ {
+ int index = HighestBit(size);
+ index = index >= counter->size ? counter->size-1 : index;
+ counter->blockCount[index]++;
+ }
+}
+
+template<class LLAllocator>
+void DynamicHeapAllocator<LLAllocator>::GetUsedBlockCount( int* usedCount, int size )
+{
+ Mutex::AutoLock m(m_DHAMutex);
+ BlockCounter counter = {usedCount, size };
+ for(ListIterator<PoolElement> i=m_SmallTLSFPools.begin();i != m_SmallTLSFPools.end();i++)
+ tlsf_walk_heap (i->tlsfPool, &UsedBlockCount, &counter);
+ for(ListIterator<PoolElement> i=m_LargeTLSFPools.begin();i != m_LargeTLSFPools.end();i++)
+ tlsf_walk_heap (i->tlsfPool, &UsedBlockCount, &counter);
+}
+
+template<class LLAllocator>
+typename DynamicHeapAllocator<LLAllocator>::PoolElement* DynamicHeapAllocator<LLAllocator>::FindPoolFromPtr( const void* ptr )
+{
+ for(ListIterator<PoolElement> i=m_SmallTLSFPools.begin();i != m_SmallTLSFPools.end();i++)
+ {
+ if (i->Contains(ptr))
+ return &*i;
+ }
+ for(ListIterator<PoolElement> i=m_LargeTLSFPools.begin();i != m_LargeTLSFPools.end();i++)
+ {
+ if (i->Contains(ptr))
+ return &*i;
+ }
+ return NULL;
+}
+
+
+template<class LLAlloctor>
+bool DynamicHeapAllocator<LLAlloctor>::Contains (const void* p)
+{
+ bool useLocking = m_UseLocking || !Thread::CurrentThreadIsMainThread();
+ if(useLocking)
+ m_DHAMutex.Lock();
+
+ if(FindPoolFromPtr(p) != NULL)
+ {
+ if(useLocking)
+ m_DHAMutex.Unlock();
+ return true;
+ }
+
+ // is this a largeAllocation
+ LargeAllocations* alloc = m_FirstLargeAllocation;
+ while (alloc != NULL)
+ {
+ if (alloc->returnedPtr == p)
+ {
+ if(useLocking)
+ m_DHAMutex.Unlock();
+ return true;
+ }
+ alloc = alloc->next;
+ }
+ if(useLocking)
+ m_DHAMutex.Unlock();
+ return false;
+
+}
+
+template<class LLAlloctor>
+void* DynamicHeapAllocator<LLAlloctor>::AddHeaderAndFooter( void* ptr, size_t size, int align ) const
+{
+ DebugAssert(align >= kDefaultMemoryAlignment && align <= 16*1024 && IsPowerOfTwo(align));
+ // calculate required padding for ptr to be aligned after header addition
+ // ppppppppHHHH***********
+ int padCount = AllocationHeader::GetRequiredPadding(ptr, align);
+ void* realPtr = ((char*)ptr) + (padCount + AllocationHeader::GetHeaderSize());
+ AllocationHeader::Set(realPtr, m_AllocatorIdentifier, size, padCount, align);
+ return realPtr;
+}
+
+template<class LLAlloctor>
+void DynamicHeapAllocator<LLAlloctor>::RegisterAllocation( const void* p )
+{
+ RegisterAllocationData(GetPtrSize(p), AllocationHeader::GetHeader(p)->GetOverheadSize());
+}
+
+template<class LLAlloctor>
+void DynamicHeapAllocator<LLAlloctor>::RegisterDeallocation( const void* p )
+{
+ RegisterDeallocationData(GetPtrSize(p), AllocationHeader::GetHeader(p)->GetOverheadSize());
+}
+
+template<class LLAlloctor>
+size_t DynamicHeapAllocator<LLAlloctor>::GetPtrSize( const void* ptr ) const
+{
+ return AllocationHeader::GetHeader(ptr)->GetRequestedSize();
+}
+
+template<class LLAlloctor>
+ProfilerAllocationHeader* DynamicHeapAllocator<LLAlloctor>::GetProfilerHeader(const void* ptr) const
+{
+ // LocalHeader:ProfilerHeader:Data
+ return AllocationHeader::GetProfilerHeader(ptr);
+}
+
+template class DynamicHeapAllocator<LowLevelAllocator>;
+
+#if UNITY_XENON && XBOX_USE_DEBUG_MEMORY
+template class DynamicHeapAllocator<LowLevelAllocatorDebugMem>;
+#endif
+#endif
+
diff --git a/Runtime/Allocator/DynamicHeapAllocator.h b/Runtime/Allocator/DynamicHeapAllocator.h
new file mode 100644
index 0000000..29aed9d
--- /dev/null
+++ b/Runtime/Allocator/DynamicHeapAllocator.h
@@ -0,0 +1,81 @@
+#ifndef DYNAMIC_HEAP_ALLOCATOR_H_
+#define DYNAMIC_HEAP_ALLOCATOR_H_
+
+#if ENABLE_MEMORY_MANAGER
+
+#include "BaseAllocator.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Allocator/LowLevelDefaultAllocator.h"
+#include "Runtime/Utilities/LinkedList.h"
+
+template<class LLAllocator>
+class DynamicHeapAllocator : public BaseAllocator
+{
+public:
+
+ DynamicHeapAllocator( UInt32 poolIncrementSize, size_t splitLimit, bool useLocking, const char* name);
+ ~DynamicHeapAllocator();
+
+ virtual void* Allocate (size_t size, int align);
+ virtual void* Reallocate (void* p, size_t size, int align);
+ virtual void Deallocate (void* p);
+ virtual bool Contains (const void* p);
+
+ virtual bool CheckIntegrity();
+ virtual bool ValidatePointer(void* ptr);
+
+ virtual size_t GetPtrSize(const void* ptr) const;
+
+ virtual ProfilerAllocationHeader* GetProfilerHeader(const void* ptr) const;
+
+ // return the free block count for each pow2
+ virtual void GetFreeBlockCount(int* freeCount, int size);
+ // return the used block count for each pow2
+ virtual void GetUsedBlockCount(int* usedCount, int size);
+
+private:
+ struct PoolElement : public ListElement
+ {
+ bool Contains(const void* ptr) const
+ {
+ return ptr >= memoryBase && ptr < memoryBase + memorySize;
+ }
+ void* tlsfPool;
+ char* memoryBase;
+ UInt32 memorySize;
+ UInt32 allocationCount;
+ UInt32 allocationSize;
+ };
+
+ typedef List<PoolElement> PoolList;
+
+ size_t m_SplitLimit;
+ PoolElement& GetActivePool(size_t size);
+ PoolList& GetPoolList(size_t size);
+
+ PoolList m_SmallTLSFPools;
+ PoolList m_LargeTLSFPools;
+
+ Mutex m_DHAMutex;
+ bool m_UseLocking;
+ size_t m_RequestedPoolSize;
+
+ struct LargeAllocations
+ {
+ LargeAllocations* next;
+ char* allocation;
+ void* returnedPtr;
+ size_t size;
+ };
+ LargeAllocations* m_FirstLargeAllocation;
+
+ PoolElement* FindPoolFromPtr(const void* ptr);
+
+ void RegisterAllocation(const void* p);
+ void RegisterDeallocation(const void* p);
+
+ void* AddHeaderAndFooter( void* ptr, size_t size, int align ) const;
+};
+
+#endif
+#endif
diff --git a/Runtime/Allocator/FixedHeapAllocator.cpp b/Runtime/Allocator/FixedHeapAllocator.cpp
new file mode 100644
index 0000000..1cc3c71
--- /dev/null
+++ b/Runtime/Allocator/FixedHeapAllocator.cpp
@@ -0,0 +1,68 @@
+#include "UnityPrefix.h"
+#include "FixedHeapAllocator.h"
+#include "tlsf/tlsf.h"
+
+#if FIXED_HEAP_ALLOC_COUNT_USED
+#define ALLOC_USED_MEM_INC(ptr) if (ptr) { m_nSizeUsed += tlsf_block_size(ptr); }
+#define ALLOC_USED_MEM_DEC(ptr) if (ptr) { m_nSizeUsed -= tlsf_block_size(ptr); }
+#else
+#define ALLOC_USED_MEM_INC(ptr)
+#define ALLOC_USED_MEM_DEC(ptr)
+#endif
+
+FixedHeapAllocator::FixedHeapAllocator(void* pMemoryBase, UInt32 nMemorySize, const char* name)
+: BaseAllocator(name)
+, m_TlsfPool(0)
+, m_pMemoryBase(pMemoryBase)
+, m_nMemorySize(nMemorySize)
+#if FIXED_HEAP_ALLOC_COUNT_USED
+, m_nSizeUsed(0)
+#endif
+{
+ m_TlsfPool = tlsf_create(pMemoryBase, nMemorySize);
+}
+
+
+FixedHeapAllocator::~FixedHeapAllocator()
+{
+ tlsf_destroy(m_TlsfPool);
+}
+
+void* FixedHeapAllocator::Allocate (size_t size, int align)
+{
+ void* addr = tlsf_memalign(m_TlsfPool, align, size);
+ ALLOC_USED_MEM_INC(addr);
+ return addr;
+}
+
+void* FixedHeapAllocator::Reallocate(void* p, size_t size, size_t align)
+{
+ ALLOC_USED_MEM_DEC(p);
+ void* addr = tlsf_realloc(m_TlsfPool, p, size);
+ ALLOC_USED_MEM_INC(addr);
+ return addr;
+}
+
+void FixedHeapAllocator::Deallocate(void* p)
+{
+ ALLOC_USED_MEM_DEC(p);
+ return tlsf_free(m_TlsfPool, p);
+}
+
+UInt32 FixedHeapAllocator::GetPtrSize(void* p)
+{
+ return (UInt32)tlsf_block_size(p);
+}
+
+bool FixedHeapAllocator::Contains(const void* p)
+{
+ return (p >= m_pMemoryBase && p < (char*)m_pMemoryBase + m_nMemorySize);
+}
+
+bool FixedHeapAllocator::CheckIntegrity()
+{
+ return tlsf_check_heap(m_TlsfPool);
+}
+
+#undef ALLOC_USED_MEM_INC
+#undef ALLOC_USED_MEM_DEC
diff --git a/Runtime/Allocator/FixedHeapAllocator.h b/Runtime/Allocator/FixedHeapAllocator.h
new file mode 100644
index 0000000..1dc48b5
--- /dev/null
+++ b/Runtime/Allocator/FixedHeapAllocator.h
@@ -0,0 +1,31 @@
+#ifndef HEAP_ALLOCATOR_H_
+#define HEAP_ALLOCATOR_H_
+
+#include "BaseAllocator.h"
+
+#define FIXED_HEAP_ALLOC_COUNT_USED !MASTER_BUILD
+
+class FixedHeapAllocator : public BaseAllocator
+{
+ void* m_TlsfPool;
+ void* m_pMemoryBase;
+ UInt32 m_nMemorySize;
+#if FIXED_HEAP_ALLOC_COUNT_USED
+ UInt32 m_nSizeUsed;
+#endif
+
+public:
+ FixedHeapAllocator(void* pMemoryBase, UInt32 nMemorySize, const char* name);
+ ~FixedHeapAllocator();
+
+ virtual void* Allocate (size_t size, int align);
+ virtual void* Reallocate (void* p, size_t size, size_t align);
+ virtual void Deallocate (void* p);
+
+ virtual bool Contains (const void* p);
+ virtual bool CheckIntegrity();
+
+ virtual UInt32 GetPtrSize(void* p);
+};
+
+#endif
diff --git a/Runtime/Allocator/FixedSizeAllocator.h b/Runtime/Allocator/FixedSizeAllocator.h
new file mode 100644
index 0000000..efc61b3
--- /dev/null
+++ b/Runtime/Allocator/FixedSizeAllocator.h
@@ -0,0 +1,341 @@
+
+#ifndef _ALLOCATOR_FIXEDSIZE_ALLOCATOR
+#define _ALLOCATOR_FIXEDSIZE_ALLOCATOR
+
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Misc/AllocatorLabels.h"
+
+
+template <unsigned BlockSize>
+class
+FixedSizeAllocator
+{
+public:
+ FixedSizeAllocator(MemLabelId memLabel);
+ ~FixedSizeAllocator();
+
+ void* alloc();
+ void free( void* mem );
+
+
+ void reset();
+ void free_memory();
+
+ unsigned total_allocated() const;
+ unsigned total_free() const;
+ unsigned capacity() const;
+
+
+private:
+
+
+ // we can do this template parameter
+ static const UInt8 BlocksInChunk = 255;
+
+ struct
+ Chunk
+ {
+ UInt8 data[BlocksInChunk*BlockSize];
+
+ Chunk* next;
+
+ UInt8 first_available;
+ UInt8 total_available;
+ };
+
+ Chunk* m_Chunk;
+
+ // we can store pointers -- they will be updated anyway should the new chunk be added
+ Chunk* m_AllocChunk;
+ Chunk* m_DeallocChunk;
+
+ MemLabelId m_MemLabel;
+
+
+ void create_chunk();
+ void reset_chunk( Chunk* chunk );
+
+ void* alloc_from( Chunk* chunk );
+ void dealloc_from( void* mem, Chunk* chunk );
+
+ bool mem_from( void* mem, Chunk* chunk );
+};
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline void
+FixedSizeAllocator<BlockSize>::reset_chunk( Chunk* chunk )
+{
+ AssertBreak(chunk);
+
+ chunk->first_available = 0;
+ chunk->total_available = BlocksInChunk;
+
+ // store index of next available block in-place, as first byte of the block
+
+ UInt8 next_available_i = 1;
+ UInt8* next_available_mem = (UInt8*)chunk->data;
+
+ while( next_available_i != BlocksInChunk )
+ {
+ *next_available_mem = next_available_i;
+
+ ++next_available_i;
+ next_available_mem += BlockSize;
+ }
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline void
+FixedSizeAllocator<BlockSize>::create_chunk()
+{
+ Chunk* newChunk = (Chunk*)UNITY_MALLOC(m_MemLabel, sizeof(Chunk));
+
+ reset_chunk(newChunk);
+ newChunk->next = 0;
+
+ if( m_Chunk )
+ {
+ Chunk* tail_chunk = m_Chunk;
+
+ while( tail_chunk->next )
+ tail_chunk = tail_chunk->next;
+
+ tail_chunk->next = newChunk;
+ }
+ else
+ {
+ m_Chunk = newChunk;
+ }
+
+ m_AllocChunk = m_DeallocChunk = newChunk;
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline
+FixedSizeAllocator<BlockSize>::FixedSizeAllocator(MemLabelId memLabel)
+ : m_Chunk(0),
+ m_AllocChunk(0),
+ m_DeallocChunk(0),
+ m_MemLabel(memLabel)
+{
+ // TODO: create chunk right away?
+ //create_chunk()
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline
+FixedSizeAllocator<BlockSize>::~FixedSizeAllocator()
+{
+ free_memory();
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline bool
+FixedSizeAllocator<BlockSize>::mem_from( void* mem, Chunk* chunk )
+{
+ return ( (UInt8*)mem >= (UInt8*)chunk->data
+ // TODO: align into account
+ && (UInt8*)mem < (UInt8*)chunk->data + BlocksInChunk*BlockSize );
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline void*
+FixedSizeAllocator<BlockSize>::alloc_from( Chunk* chunk )
+{
+ AssertBreak( chunk );
+ AssertBreak( chunk->total_available );
+
+ UInt8* ret = (UInt8*)chunk->data + chunk->first_available*BlockSize;
+
+ chunk->first_available = *ret;
+ --chunk->total_available;
+
+ return ret;
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline void
+FixedSizeAllocator<BlockSize>::dealloc_from( void* mem, Chunk* chunk )
+{
+ AssertBreak( chunk );
+ AssertBreak( mem_from(mem, chunk) );
+
+ UInt8* release_ptr = (UInt8*)mem;
+ AssertBreak( (release_ptr - (UInt8*)chunk->data) % BlockSize == 0 );
+
+ *release_ptr = chunk->first_available;
+ chunk->first_available = (release_ptr - (UInt8*)chunk->data) / BlockSize;
+
+ ++chunk->total_available;
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline void*
+FixedSizeAllocator<BlockSize>::alloc()
+{
+ if( m_AllocChunk == 0 || m_AllocChunk->total_available == 0 )
+ {
+ // fallback to linear search
+
+ m_AllocChunk = m_Chunk;
+ while( m_AllocChunk )
+ {
+ if( m_AllocChunk->total_available )
+ break;
+
+ m_AllocChunk = m_AllocChunk->next;
+ }
+
+ if( !m_AllocChunk )
+ create_chunk();
+ }
+
+ return alloc_from(m_AllocChunk);
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline void
+FixedSizeAllocator<BlockSize>::free( void* mem )
+{
+ AssertBreak(m_DeallocChunk);
+
+ if( !mem )
+ return;
+
+ if( !mem_from(mem, m_DeallocChunk) )
+ {
+ // we want to exploit possible locality
+ // but for now we store chunks in single-linked list
+ // fallback to simple linear search;
+
+ m_DeallocChunk = m_Chunk;
+ while( m_DeallocChunk )
+ {
+ if( mem_from(mem, m_DeallocChunk) )
+ break;
+
+ m_DeallocChunk = m_DeallocChunk->next;
+ }
+ }
+
+ AssertBreak(m_DeallocChunk);
+ dealloc_from( mem, m_DeallocChunk );
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline void
+FixedSizeAllocator<BlockSize>::reset()
+{
+ Chunk* target_chunk = m_Chunk;
+ while( target_chunk )
+ {
+ reset_chunk(target_chunk);
+ target_chunk = target_chunk->next;
+ }
+
+ m_AllocChunk = m_DeallocChunk = m_Chunk;
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline void
+FixedSizeAllocator<BlockSize>::free_memory()
+{
+ Chunk* target_chunk = m_Chunk;
+ while( target_chunk )
+ {
+ Chunk* nextChunk = target_chunk->next;
+ UNITY_FREE(m_MemLabel, target_chunk);
+
+ target_chunk = nextChunk;
+ }
+
+ m_AllocChunk = m_DeallocChunk = m_Chunk = 0;
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline unsigned
+FixedSizeAllocator<BlockSize>::capacity() const
+{
+ unsigned ret = 0;
+
+ Chunk* target_chunk = m_Chunk;
+ while( target_chunk )
+ {
+ ret += BlocksInChunk*BlockSize;
+ target_chunk = target_chunk->next;
+ }
+
+ return ret;
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline unsigned
+FixedSizeAllocator<BlockSize>::total_free() const
+{
+ unsigned ret = 0;
+
+ Chunk* target_chunk = m_Chunk;
+ while( target_chunk )
+ {
+ ret += target_chunk->total_available * BlockSize;
+ target_chunk = target_chunk->next;
+ }
+
+ return ret;
+
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline unsigned
+FixedSizeAllocator<BlockSize>::total_allocated() const
+{
+ return capacity() - total_free();
+}
+
+
+//==============================================================================
+
+#endif // _ALLOCATOR_FIXEDSIZE_ALLOCATOR
+
diff --git a/Runtime/Allocator/LinearAllocator.cpp b/Runtime/Allocator/LinearAllocator.cpp
new file mode 100644
index 0000000..9d0b23b
--- /dev/null
+++ b/Runtime/Allocator/LinearAllocator.cpp
@@ -0,0 +1,38 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "LinearAllocator.h"
+
+
+#if 0
+
+struct FwdAllocatorMarker {
+ FwdAllocatorMarker (ForwardLinearAllocator& al) : al_(al), p_(al.current ()) {}
+ ~FwdAllocatorMarker () { al_.rewind (p_); }
+
+ void* p_;
+ ForwardLinearAllocator& al_;
+};
+
+
+void TestLinearAllocator ()
+{
+ ForwardLinearAllocator la (32);
+
+ void* p0 = la.allocate (16);
+ void* p1 = la.allocate (16);
+
+ {
+ FwdAllocatorMarker m (la);
+ void* p2 = la.allocate (32);
+ }
+
+ void* c1 = la.current ();
+ {
+ FwdAllocatorMarker m (la);
+ void* p3 = la.allocate (8);
+ void* p4 = la.allocate (32);
+ void* p5 = la.allocate (32);
+ }
+ la.rewind (c1);
+}
+#endif
diff --git a/Runtime/Allocator/LinearAllocator.h b/Runtime/Allocator/LinearAllocator.h
new file mode 100644
index 0000000..7c742e4
--- /dev/null
+++ b/Runtime/Allocator/LinearAllocator.h
@@ -0,0 +1,391 @@
+#ifndef LINEAR_ALLOCATOR_H_
+#define LINEAR_ALLOCATOR_H_
+
+#include <cstddef>
+#include <list>
+#include "assert.h"
+#include "Configuration/UnityConfigure.h"
+#if UNITY_XENON
+#include <malloc.h>
+#endif
+#if UNITY_LINUX
+#include <stdint.h> // uintptr_t
+#endif
+
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+#include "Runtime/Threads/Thread.h"
+#endif
+#include "Runtime/Allocator/MemoryMacros.h"
+
+struct LinearAllocatorBase
+{
+ static const int kMinimalAlign = 4;
+
+ struct Block
+ {
+ char* m_Begin;
+ char* m_Current;
+ size_t m_Size;
+ MemLabelId m_Label;
+
+ void initialize (size_t size, MemLabelId label)
+ {
+ m_Label = label;
+ m_Current = m_Begin = (char*)UNITY_MALLOC(label,size);
+ m_Size = size;
+ }
+
+ void reset ()
+ {
+ m_Current = m_Begin;
+ }
+
+ void purge ()
+ {
+ UNITY_FREE(m_Label, m_Begin);
+ }
+
+ size_t used () const
+ {
+ return m_Current - m_Begin;
+ }
+
+ void* current () const
+ {
+ return m_Current;
+ }
+
+ size_t available () const
+ {
+ return m_Size - used ();
+ }
+
+ size_t padding (size_t alignment) const
+ {
+ size_t pad = ((uintptr_t)m_Current - 1 | alignment - 1) + 1 - (uintptr_t)m_Current;
+ return pad;
+ }
+
+ void* bump (size_t size)
+ {
+ Assert (size <= available());
+ char* p = m_Current;
+ m_Current += size;
+ return p;
+ }
+
+ void roll_back (size_t size)
+ {
+ Assert (used () >= size);
+ m_Current -= size;
+ }
+
+ bool belongs (const void* p)
+ {
+ //if (p >= m_Begin && p <= m_Begin + m_Size)
+ // return true;
+ //return false;
+
+ //return p >= m_Begin && p <= m_Begin + m_Size;
+
+ return (uintptr_t)p - (uintptr_t)m_Begin <= (uintptr_t)m_Size;
+ }
+
+ void set (void* p)
+ {
+ Assert (p >= m_Begin && p < m_Begin + m_Size);
+ m_Current = (char*)p;
+ }
+ };
+
+ typedef std::list<Block, STL_ALLOCATOR(kMemPoolAlloc, Block) > block_container;
+
+ LinearAllocatorBase (size_t blockSize, MemLabelId label)
+ : m_Blocks(), m_BlockSize (blockSize), m_AllocLabel (label)
+ {
+ }
+
+ void add_block (size_t size)
+ {
+ m_Blocks.push_back (Block());
+ size_t blockSize = size > m_BlockSize ? size : m_BlockSize;
+ m_Blocks.back ().initialize (blockSize, m_AllocLabel);
+ }
+
+ void purge (bool releaseAllBlocks = false)
+ {
+ if (m_Blocks.empty ())
+ return;
+
+ block_container::iterator begin = m_Blocks.begin ();
+
+ if (!releaseAllBlocks)
+ begin++;
+
+ for (block_container::iterator it = begin, end = m_Blocks.end (); it != end; ++it)
+ it->purge ();
+
+ m_Blocks.erase (begin, m_Blocks.end ());
+
+ if (!releaseAllBlocks)
+ m_Blocks.back ().reset ();
+ }
+
+ bool belongs (const void* p)
+ {
+ for (block_container::iterator it = m_Blocks.begin (), end = m_Blocks.end (); it != end; ++it)
+ {
+ if (it->belongs (p))
+ return true;
+ }
+
+ return false;
+ }
+
+ void* current () const
+ {
+ return m_Blocks.empty () ? 0 : m_Blocks.back ().current ();
+ }
+
+ void rewind (void* mark)
+ {
+ for (block_container::iterator it = m_Blocks.end (); it != m_Blocks.begin (); --it)
+ {
+ block_container::iterator tit = it;
+ --tit;
+
+ if (tit->belongs (mark)) {
+ tit->set (mark);
+
+ for (block_container::iterator temp = it; temp != m_Blocks.end (); ++temp)
+ temp->purge ();
+
+ m_Blocks.erase (it, m_Blocks.end ());
+ break;
+ }
+ }
+ }
+
+protected:
+ block_container m_Blocks;
+ size_t m_BlockSize;
+ MemLabelId m_AllocLabel;
+};
+
+
+struct ForwardLinearAllocator : public LinearAllocatorBase
+{
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ Thread::ThreadID m_AllocThread, m_DeallocThread;
+ int m_Allocated;
+ bool m_RequireDeallocation;
+
+ void SetThreadIDs (Thread::ThreadID allocThread, Thread::ThreadID deallocThread)
+ {
+ m_AllocThread = allocThread;
+ m_DeallocThread = deallocThread;
+ }
+
+ void SetRequireDeallocation (bool v)
+ {
+ m_RequireDeallocation = v;
+ }
+#endif
+
+ ForwardLinearAllocator (size_t blockSize, MemLabelId label)
+ : LinearAllocatorBase (blockSize, label)
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ , m_AllocThread (0), m_DeallocThread (0), m_Allocated(0), m_RequireDeallocation(false)
+#endif
+ {
+ }
+
+ ~ForwardLinearAllocator ()
+ {
+ purge (true);
+ }
+
+ size_t GetAllocatedBytes() const
+ {
+ size_t s = 0;
+ for (block_container::const_iterator it = m_Blocks.begin (); it != m_Blocks.end(); ++it)
+ s += it->used();
+ return s;
+ }
+
+ void* allocate (size_t size, size_t alignment = 4)
+ {
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ ErrorIf (!Thread::EqualsCurrentThreadIDForAssert (m_AllocThread));
+ m_Allocated++;
+#endif
+// Assert (size == AlignUIntPtr (size, kMinimalAlign));
+
+ if (m_Blocks.empty ())
+ add_block (size);
+
+ Block* block = &m_Blocks.back ();
+ size_t padding = block->padding (alignment);
+
+ if (size + padding > block->available ()) {
+ add_block (size);
+ block = &m_Blocks.back ();
+ }
+
+ uintptr_t p = (uintptr_t)block->bump (size + padding);
+
+ return (void*)(p + padding);
+ }
+
+ void deallocate (void* dealloc)
+ {
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ ErrorIf (Thread::GetCurrentThreadID () != m_DeallocThread);
+ m_Allocated--;
+#endif
+ }
+
+ void deallocate_no_thread_check (void* dealloc)
+ {
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ m_Allocated--;
+#endif
+ }
+
+ void purge (bool releaseAllBlocks = false)
+ {
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ ErrorIf (Thread::GetCurrentThreadID () != m_DeallocThread && m_DeallocThread != 0);
+ ErrorIf (m_RequireDeallocation && m_Allocated != 0);
+#endif
+ LinearAllocatorBase::purge (releaseAllBlocks);
+ }
+
+ void rewind (void* mark)
+ {
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ ErrorIf (Thread::GetCurrentThreadID () != m_DeallocThread);
+#endif
+ LinearAllocatorBase::rewind (mark);
+ }
+
+ using LinearAllocatorBase::current;
+ using LinearAllocatorBase::belongs;
+};
+/*
+// std::allocator concept implementation for ForwardLinearAllocator objects.
+// use it to make STL use your *locally* created ForwardLinearAllocator object
+// example:
+// void HeavyMemoryAllocationFuncion ()
+// {
+// ForwardLinearAllocator fwdalloc (1024);
+// std::vector<int, forward_linear_allocator<int> > container (forward_linear_allocator<int>(fwdalloc));
+//
+// // use vector
+//
+// // memory is clean up automatically
+// }
+
+template<class T>
+class forward_linear_allocator
+{
+public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T value_type;
+
+ template <class U> struct rebind { typedef forward_linear_allocator<U> other; };
+
+ forward_linear_allocator(ForwardLinearAllocator& al) throw() : m_LinearAllocator (al) {}
+ forward_linear_allocator(const forward_linear_allocator& al) throw() : m_LinearAllocator (al.m_LinearAllocator) {}
+ template <class U> forward_linear_allocator(const forward_linear_allocator<U>& al) throw() : m_LinearAllocator (al.m_LinearAllocator) {}
+ ~forward_linear_allocator() throw() {}
+
+ pointer address(reference x) const { return &x; }
+ const_pointer address(const_reference x) const { return &x; }
+
+ pointer allocate(size_type count, void const* hint = 0)
+ { return (pointer)m_LinearAllocator.allocate (count * sizeof(T)); }
+ void deallocate(pointer p, size_type n)
+ { m_LinearAllocator.deallocate(p); }
+
+ size_type max_size() const throw()
+ { return 0x80000000; }
+
+ void construct(pointer p, const T& val)
+ { new (p) T( val ); }
+
+ void destroy(pointer p)
+ { p->~T(); }
+
+private:
+ ForwardLinearAllocator& m_LinearAllocator;
+};
+
+// this a global ForwardLinearAllocator object that can be used to allocate (and release) memory from
+// anywhere in the program. Caller is responsible for tracking memory used in total
+// (use ForwardLinearAllocator::current/ForwardLinearAllocator::rewind to save and restore memory pointer)
+extern ForwardLinearAllocator g_ForwardFrameAllocator;
+
+// std::allocator concept for global ForwardLinearAllocator object
+// this is just to save one indirection, otherwise forward_linear_allocator referencing a global ForwardLinearAllocator object could be used
+template<class T>
+class global_linear_allocator
+{
+public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T value_type;
+
+ template <class U> struct rebind { typedef global_linear_allocator<U> other; };
+
+ global_linear_allocator() {}
+ global_linear_allocator(const global_linear_allocator&) throw() {}
+ template <class U> global_linear_allocator(const global_linear_allocator<U>&) throw() {}
+ ~global_linear_allocator() throw() {}
+
+ pointer address(reference x) const { return &x; }
+ const_pointer address(const_reference x) const { return &x; }
+
+ pointer allocate(size_type count, void const* hint = 0)
+ { return (pointer)g_ForwardFrameAllocator.allocate (count * sizeof(T)); }
+ void deallocate(pointer p, size_type n)
+ { g_ForwardFrameAllocator.deallocate(p); }
+
+ bool operator==(global_linear_allocator const& a) const
+ { return true; }
+ bool operator!=(global_linear_allocator const& a) const
+ { return false; }
+
+ size_type max_size() const throw()
+ { return 0x7fffffff; }
+
+ void construct(pointer p, const T& val)
+ { new (p) T( val ); }
+
+ void destroy(pointer p)
+ { p->~T(); }
+};
+
+#define DECLARE_GLOBAL_LINEAR_ALLOCATOR_MEMBER_NEW_DELETE \
+public: \
+ inline void* operator new( size_t size ) { return g_ForwardFrameAllocator.allocate(size); } \
+ inline void operator delete( void* p ) { g_ForwardFrameAllocator.deallocate(p); }
+
+inline void* operator new (size_t size, ForwardLinearAllocator& al) { return al.allocate (size); }
+inline void* operator new [] (size_t size, ForwardLinearAllocator& al) { return al.allocate (size); }
+
+inline void operator delete (void* p, ForwardLinearAllocator& al) { }
+inline void operator delete [] (void* p, ForwardLinearAllocator& al) { }
+*/
+
+
+#endif
diff --git a/Runtime/Allocator/LowLevelDefaultAllocator.cpp b/Runtime/Allocator/LowLevelDefaultAllocator.cpp
new file mode 100644
index 0000000..010406c
--- /dev/null
+++ b/Runtime/Allocator/LowLevelDefaultAllocator.cpp
@@ -0,0 +1,33 @@
+#include "UnityPrefix.h"
+#include "LowLevelDefaultAllocator.h"
+#include "Runtime/Allocator/MemoryManager.h"
+
+#if ENABLE_MEMORY_MANAGER
+
+
+void* LowLevelAllocator::Malloc (size_t size) { return MemoryManager::LowLevelAllocate(size); }
+void* LowLevelAllocator::Realloc (void* ptr, size_t size) { return MemoryManager::LowLevelReallocate(ptr, size); }
+void LowLevelAllocator::Free (void* ptr) { MemoryManager::LowLevelFree(ptr); }
+
+
+#if UNITY_XENON
+#include "PlatformDependent/Xbox360/Source/XenonMemory.h"
+
+#if XBOX_USE_DEBUG_MEMORY
+// Uses debug memory on compatible devkits
+void* LowLevelAllocatorDebugMem::Malloc(size_t size)
+{
+ return DmDebugAlloc(size);
+}
+void* LowLevelAllocatorDebugMem::Realloc (void* ptr, size_t size)
+{
+ ErrorString("LowLevelAllocatorDebugMem::Realloc is not implemented.");
+ return 0;
+}
+void LowLevelAllocatorDebugMem::Free (void* ptr)
+{
+ DmDebugFree(ptr);
+}
+#endif // XBOX_USE_DEBUG_MEMORY
+#endif
+#endif
diff --git a/Runtime/Allocator/LowLevelDefaultAllocator.h b/Runtime/Allocator/LowLevelDefaultAllocator.h
new file mode 100644
index 0000000..ce9b852
--- /dev/null
+++ b/Runtime/Allocator/LowLevelDefaultAllocator.h
@@ -0,0 +1,25 @@
+#ifndef LOW_LEVEL_DEFAULT_ALLOCATOR_H_
+#define LOW_LEVEL_DEFAULT_ALLOCATOR_H_
+
+#if ENABLE_MEMORY_MANAGER
+
+class LowLevelAllocator
+{
+public:
+ static void* Malloc(size_t size);
+ static void* Realloc(void* ptr, size_t size);
+ static void Free(void* ptr);
+};
+
+#if UNITY_XENON
+class LowLevelAllocatorDebugMem
+{
+public:
+ static void* Malloc(size_t size);
+ static void* Realloc(void* ptr, size_t size);
+ static void Free(void* ptr);
+};
+#endif
+
+#endif
+#endif // LOW_LEVEL_DEFAULT_ALLOCATOR_H_
diff --git a/Runtime/Allocator/MemoryMacros.cpp b/Runtime/Allocator/MemoryMacros.cpp
new file mode 100644
index 0000000..75daeb8
--- /dev/null
+++ b/Runtime/Allocator/MemoryMacros.cpp
@@ -0,0 +1,85 @@
+#include "UnityPrefix.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+#include "Runtime/Allocator/MemoryManager.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+
+#if ENABLE_MEM_PROFILER
+
+bool push_allocation_root(void* root, bool forcePush)
+{
+ return GetMemoryProfiler()?GetMemoryProfiler()->PushAllocationRoot(root, forcePush):false;
+}
+
+void pop_allocation_root()
+{
+ GetMemoryProfiler()->PopAllocationRoot();
+}
+
+ProfilerAllocationHeader* get_current_allocation_root_header_internal()
+{
+ return GetMemoryProfiler()?GetMemoryProfiler()->GetCurrentRootHeader():NULL;
+}
+
+ProfilerAllocationHeader* get_allocation_header_internal(void* ptr, MemLabelRef label)
+{
+ BaseAllocator* alloc = GetMemoryManager().GetAllocator(label);
+ return alloc->GetProfilerHeader(ptr);
+}
+
+void transfer_ownership_root_header(void* source, MemLabelRef label, ProfilerAllocationHeader* newRootHeader)
+{
+ if(GetMemoryManager().IsTempAllocatorLabel(label))
+ return;
+ GetMemoryProfiler()->TransferOwnership(source, GetMemoryManager().GetAllocator(label), newRootHeader);
+}
+
+void transfer_ownership(void* source, MemLabelRef label, const void* newroot)
+{
+ BaseAllocator* rootAlloc = GetMemoryManager().GetAllocatorContainingPtr(newroot);
+ ProfilerAllocationHeader* rootHeader = rootAlloc->GetProfilerHeader(newroot);
+ transfer_ownership_root_header(source, label, rootHeader);
+}
+
+void set_root_allocation(void* root, MemLabelRef label, const char* areaName, const char* objectName)
+{
+ pop_allocation_root();
+ GetMemoryProfiler ()->RegisterRootAllocation (root, GetMemoryManager().GetAllocator(label), areaName, objectName);
+}
+
+void assign_allocation_root(void* root, MemLabelRef label, const char* areaName, const char* objectName)
+{
+ GetMemoryProfiler ()->RegisterRootAllocation (root, GetMemoryManager().GetAllocator(label), areaName, objectName);
+}
+
+AllocationRootReference* get_root_reference_from_header(ProfilerAllocationHeader* root)
+{
+ return MemoryProfiler::GetRootReferenceFromHeader(root);
+}
+
+AllocationRootReference* copy_root_reference(AllocationRootReference* rootRef)
+{
+ if (rootRef)
+ rootRef->Retain();
+ return rootRef;
+}
+
+void release_root_reference(AllocationRootReference* rootRef)
+{
+ if (rootRef)
+ rootRef->Release();
+}
+
+ProfilerAllocationHeader* get_root_header_from_reference(AllocationRootReference* rootref)
+{
+ return rootref? rootref->root: NULL;
+}
+
+#endif
+
+void ValidateAllocatorIntegrity(MemLabelId label)
+{
+#if ENABLE_MEMORY_MANAGER
+ GetMemoryManager().GetAllocator(label)->CheckIntegrity();
+#endif
+}
+
diff --git a/Runtime/Allocator/MemoryMacros.h b/Runtime/Allocator/MemoryMacros.h
new file mode 100644
index 0000000..f7a85c6
--- /dev/null
+++ b/Runtime/Allocator/MemoryMacros.h
@@ -0,0 +1,237 @@
+#ifndef _MEMORY_MACROS_H_
+#define _MEMORY_MACROS_H_
+
+#include <new>
+#include "Runtime/Utilities/FileStripped.h"
+#include "Runtime/Misc/AllocatorLabels.h"
+
+
+#if defined(__GNUC__)
+ #define ALIGN_OF(T) __alignof__(T)
+ #define ALIGN_TYPE(val) __attribute__((aligned(val)))
+ #define FORCE_INLINE inline __attribute__ ((always_inline))
+#elif defined(_MSC_VER)
+ #define ALIGN_OF(T) __alignof(T)
+ #define ALIGN_TYPE(val) __declspec(align(val))
+ #define FORCE_INLINE __forceinline
+#else
+ #define ALIGN_TYPE(size)
+ #define FORCE_INLINE inline
+#endif
+
+
+#if ENABLE_MEMORY_MANAGER
+// These methods are added to be able to make some initial allocations that does not use the memory manager
+extern void* GetPreallocatedMemory(int size);
+# define HEAP_NEW(cls) new (GetPreallocatedMemory(sizeof(cls))) cls
+# define HEAP_DELETE(obj, cls) obj->~cls();
+#else
+# define HEAP_NEW(cls) new (UNITY_LL_ALLOC(kMemDefault,sizeof(cls),kDefaultMemoryAlignment)) cls
+# define HEAP_DELETE(obj, cls) {obj->~cls();UNITY_LL_FREE(kMemDefault,(void*)obj);}
+#endif
+
+enum
+{
+#if UNITY_OSX || UNITY_IPHONE || UNITY_ANDROID || UNITY_PS3 || UNITY_XENON || UNITY_BB10 || UNITY_TIZEN
+ kDefaultMemoryAlignment = 16
+#else
+ kDefaultMemoryAlignment = sizeof(void*)
+#endif
+};
+
+enum
+{
+ kAllocateOptionNone = 0, // Fatal: Show message box with out of memory error and quit application
+ kAllocateOptionReturnNullIfOutOfMemory = 1 // Returns null if allocation fails (doesn't show message box)
+};
+
+
+#if ENABLE_MEMORY_MANAGER
+
+// new override does not work on mac together with pace
+#if !(UNITY_OSX && UNITY_EDITOR) && !(UNITY_PLUGIN) && !UNITY_WEBGL && !UNITY_BB10 && !UNITY_TIZEN
+void* operator new (size_t size) throw();
+#if UNITY_PS3
+void* operator new (size_t size, size_t alignment) throw();
+void* operator new [] (size_t size, size_t alignment) throw();
+#endif
+void* operator new [] (size_t size) throw();
+void operator delete (void* p) throw();
+void operator delete [] (void* p) throw();
+
+void* operator new (size_t size, const std::nothrow_t&) throw();
+void* operator new [] (size_t size, const std::nothrow_t&) throw();
+void operator delete (void* p, const std::nothrow_t&) throw();
+void operator delete [] (void* p, const std::nothrow_t&) throw();
+#endif
+
+#endif
+
+#if ENABLE_MEM_PROFILER
+
+EXPORT_COREMODULE bool push_allocation_root(void* root, bool forcePush);
+EXPORT_COREMODULE void pop_allocation_root();
+EXPORT_COREMODULE ProfilerAllocationHeader* get_current_allocation_root_header_internal();
+EXPORT_COREMODULE void set_root_allocation(void* root, MemLabelRef label, const char* areaName, const char* objectName);
+EXPORT_COREMODULE void assign_allocation_root(void* root, MemLabelRef label, const char* areaName, const char* objectName);
+EXPORT_COREMODULE ProfilerAllocationHeader* get_allocation_header_internal(void* ptr, MemLabelRef label);
+EXPORT_COREMODULE AllocationRootReference* get_root_reference_from_header(ProfilerAllocationHeader* root);
+EXPORT_COREMODULE AllocationRootReference* copy_root_reference(AllocationRootReference* rootref);
+EXPORT_COREMODULE void release_root_reference(AllocationRootReference* rootRef);
+EXPORT_COREMODULE ProfilerAllocationHeader* get_root_header_from_reference(AllocationRootReference* rootref);
+
+class AutoScopeRoot
+{
+public:
+ AutoScopeRoot(void* root) { pushed = push_allocation_root(root, false); }
+ ~AutoScopeRoot() { if(pushed) pop_allocation_root(); }
+ bool pushed;
+};
+
+#define GET_CURRENT_ALLOC_ROOT_HEADER() get_current_allocation_root_header_internal()
+#define GET_ALLOC_HEADER(ptr, label) get_allocation_header_internal(ptr, label)
+#define SET_ALLOC_OWNER(root) AutoScopeRoot autoScopeRoot(root)
+#define UNITY_TRANSFER_OWNERSHIP(source, label, newroot) transfer_ownership(source, label, newroot)
+#define UNITY_TRANSFER_OWNERSHIP_TO_HEADER(source, label, newrootheader) transfer_ownership_root_header(source, label, newrootheader)
+
+template<typename T>
+inline T* set_allocation_root(T* root, MemLabelRef label, const char* areaName, const char* objectName)
+{
+ set_root_allocation(root, label, areaName, objectName);
+ return root;
+}
+
+#else
+
+#define GET_CURRENT_ALLOC_ROOT_HEADER() NULL
+#define GET_ALLOC_HEADER(ptr, label) NULL
+#define SET_ALLOC_OWNER(root) {}
+#define UNITY_TRANSFER_OWNERSHIP(source, label, newroot) {}
+#define UNITY_TRANSFER_OWNERSHIP_TO_HEADER(source, label, newrootheader) {}
+
+#endif
+
+EXPORT_COREMODULE void* operator new (size_t size, MemLabelRef label, bool set_root, int align, const char* file, int line);
+void* operator new [] (size_t size, MemLabelRef label, bool set_root, int align, const char* file, int line);
+EXPORT_COREMODULE void operator delete (void* p, MemLabelRef label, bool set_root, int align, const char* file, int line);
+void operator delete [] (void* p, MemLabelRef label, bool set_root, int align, const char* file, int line);
+
+EXPORT_COREMODULE void* malloc_internal(size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line);
+EXPORT_COREMODULE void* calloc_internal(size_t count, size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line);
+EXPORT_COREMODULE void* realloc_internal(void* ptr, size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line);
+void free_internal(void* ptr);
+void EXPORT_COREMODULE free_alloc_internal(void* ptr, MemLabelRef label);
+
+void transfer_ownership(void* source, MemLabelRef label, const void* newroot);
+void transfer_ownership_root_header(void* source, MemLabelRef label, ProfilerAllocationHeader* newRootHeader);
+
+void register_external_gfx_allocation(void* ptr, size_t size, size_t related, const char* file, int line);
+void register_external_gfx_deallocation(void* ptr, const char* file, int line);
+
+#define UNITY_MALLOC(label, size) malloc_internal(size, kDefaultMemoryAlignment, label, kAllocateOptionNone, __FILE_STRIPPED__, __LINE__)
+#define UNITY_MALLOC_NULL(label, size) malloc_internal(size, kDefaultMemoryAlignment, label, kAllocateOptionReturnNullIfOutOfMemory, __FILE_STRIPPED__, __LINE__)
+#define UNITY_MALLOC_ALIGNED(label, size, align) malloc_internal(size, align, label, kAllocateOptionNone, __FILE_STRIPPED__, __LINE__)
+#define UNITY_MALLOC_ALIGNED_NULL(label, size, align) malloc_internal(size, align, label, kAllocateOptionReturnNullIfOutOfMemory, __FILE_STRIPPED__, __LINE__)
+#define UNITY_CALLOC(label, count, size) calloc_internal(count, size, kDefaultMemoryAlignment, label, kAllocateOptionNone, __FILE_STRIPPED__, __LINE__)
+#define UNITY_REALLOC_(label, ptr, size) realloc_internal(ptr, size, kDefaultMemoryAlignment, label, kAllocateOptionNone, __FILE_STRIPPED__, __LINE__)
+#define UNITY_REALLOC_ALIGNED(label, ptr, size, align) realloc_internal(ptr, size, align, label, kAllocateOptionNone, __FILE_STRIPPED__, __LINE__)
+#define UNITY_FREE(label, ptr) free_alloc_internal(ptr, label)
+
+#define REGISTER_EXTERNAL_GFX_ALLOCATION_REF(ptr, size, related) register_external_gfx_allocation((void*)ptr, size, (size_t)related, __FILE_STRIPPED__, __LINE__)
+#define REGISTER_EXTERNAL_GFX_DEALLOCATION(ptr) register_external_gfx_deallocation((void*)ptr, __FILE_STRIPPED__, __LINE__)
+
+template<typename T>
+inline void delete_internal(T* ptr, MemLabelRef label) { if (ptr) ptr->~T(); UNITY_FREE(label,ptr); }
+
+#define UNITY_NEW(type, label) new (label, false, kDefaultMemoryAlignment, __FILE_STRIPPED__, __LINE__) type
+#define UNITY_NEW_ALIGNED(type, label, align) new (label, false, align, __FILE_STRIPPED__, __LINE__) type
+#define UNITY_DELETE(ptr, label) { delete_internal(ptr, label); ptr = NULL; }
+
+#if ENABLE_MEM_PROFILER
+ #define UNITY_NEW_AS_ROOT(type, label, areaName, objectName) set_allocation_root(new (label, true, kDefaultMemoryAlignment, __FILE_STRIPPED__, __LINE__) type, label, areaName, objectName)
+ #define UNITY_NEW_AS_ROOT_ALIGNED(type, label, align, areaName, objectName) set_allocation_root(new (label, true, align, __FILE_STRIPPED__, __LINE__) type, label, areaName, objectName)
+ #define SET_PTR_AS_ROOT(ptr, label, areaName, objectName) assign_allocation_root(ptr, label, areaName, objectName)
+#else
+ #define UNITY_NEW_AS_ROOT(type, label, areaName, objectName) new (label, true, kDefaultMemoryAlignment, __FILE_STRIPPED__, __LINE__) type
+ #define UNITY_NEW_AS_ROOT_ALIGNED(type, label, align, areaName, objectName) set_allocation_root(new (label, true, align, __FILE_STRIPPED__, __LINE__) type, label, areaName, objectName)
+ #define SET_PTR_AS_ROOT(ptr, label, areaName, objectName) {}
+#endif
+
+// Deprecated -> Move to new Macros
+#define UNITY_ALLOC(label, size, align) UNITY_MALLOC_ALIGNED(label, size, align)
+#define UNITY_REALLOC(label, ptr, size, align) UNITY_REALLOC_ALIGNED(label, ptr, size, align)
+
+// Check the integrity of the allocator backing a label. Use this to track down memory overwrites
+void ValidateAllocatorIntegrity(MemLabelId label);
+
+#include "STLAllocator.h"
+
+/// ALLOC_TEMP allocates temporary memory that stays alive only inside the block it was allocated in.
+/// It will automatically get freed!
+/// (Watch out that you dont place ALLOC_TEMP inside an if block and use the memory after the if block.
+///
+/// eg.
+/// float* data;
+/// ALLOC_TEMP(data,float,500, kMemSkinning);
+
+#define kMAX_TEMP_STACK_SIZE 2000
+
+// Metrowerks debugger fucks up when we use alloca
+#if defined(__MWERKS__) && !UNITY_RELEASE
+#undef kMAX_TEMP_STACK_SIZE
+#define kMAX_TEMP_STACK_SIZE 0
+#endif
+
+inline void* AlignPtr (void* p, size_t alignment)
+{
+ size_t a = alignment - 1;
+ return (void*)(((size_t)p + a) & ~a);
+}
+
+struct FreeTempMemory
+{
+ FreeTempMemory() : m_Memory (NULL) { }
+ ~FreeTempMemory() { if (m_Memory) UNITY_FREE(kMemTempAlloc, m_Memory); }
+ void* m_Memory;
+};
+
+#define ALLOC_TEMP_ALIGNED(ptr,type,count,alignment) \
+ FreeTempMemory freeTempMemory_##ptr; \
+ { \
+ size_t allocSize = (count) * sizeof(type) + (alignment)-1; \
+ void* allocPtr = NULL; \
+ if (allocSize < kMAX_TEMP_STACK_SIZE) { \
+ if ((count) != 0) \
+ allocPtr = alloca(allocSize); \
+ } else { \
+ if ((count) != 0) { \
+ allocPtr = UNITY_MALLOC_ALIGNED(kMemTempAlloc, allocSize, kDefaultMemoryAlignment); \
+ freeTempMemory_##ptr.m_Memory = allocPtr; \
+ } \
+ } \
+ ptr = reinterpret_cast<type*> (AlignPtr(allocPtr, alignment)); \
+ } \
+ ANALYSIS_ASSUME(ptr)
+
+#define ALLOC_TEMP(ptr, type, count) \
+ ALLOC_TEMP_ALIGNED(ptr,type,count,kDefaultMemoryAlignment)
+
+#define MALLOC_TEMP(ptr, size) \
+ ALLOC_TEMP_ALIGNED(ptr,char,size,kDefaultMemoryAlignment)
+
+#define ALLOC_TEMP_MANUAL(type,count) \
+ (type*)UNITY_MALLOC_ALIGNED(kMemTempAlloc, (count) * sizeof (type), kDefaultMemoryAlignment)
+
+#define FREE_TEMP_MANUAL(ptr) \
+ UNITY_FREE(kMemTempAlloc, ptr)
+
+
+#if UNITY_XENON
+// Copies a specified number of bytes from a region of cached memory to a region of memory of an unspecified type.
+#define UNITY_MEMCPY(dest, src, count) XMemCpyStreaming(dest, src, count)
+#else
+#define UNITY_MEMCPY(dest, src, count) memcpy(dest, src, count)
+#endif
+
+
+#endif
diff --git a/Runtime/Allocator/MemoryManager.cpp b/Runtime/Allocator/MemoryManager.cpp
new file mode 100644
index 0000000..11d98b4
--- /dev/null
+++ b/Runtime/Allocator/MemoryManager.cpp
@@ -0,0 +1,1659 @@
+#include "UnityPrefix.h"
+#include "Runtime/Allocator/MemoryManager.h"
+#include "Runtime/Utilities/MemoryUtilities.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Threads/AtomicOps.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+
+// under new clang -fpermissive is no longer available, and it is quite strict about proper signatures of global new/delete
+// eg throw() is reserved for nonthrow only
+#define STRICTCPP_NEW_DELETE_SIGNATURES UNITY_OSX || UNITY_IPHONE
+#if STRICTCPP_NEW_DELETE_SIGNATURES
+ #define THROWING_NEW_THROW throw(std::bad_alloc)
+#else
+ #define THROWING_NEW_THROW throw()
+#endif
+
+
+
+#if UNITY_PS3
+#include "PlatformDependent/PS3Player/Allocator/PS3DlmallocAllocator.h"
+#endif
+
+#define STOMP_MEMORY !UNITY_PS3 && (!UNITY_RELEASE)
+
+#define kMemoryManagerOverhead ((sizeof(int) + kDefaultMemoryAlignment - 1) & ~(kDefaultMemoryAlignment-1))
+
+#if UNITY_XENON
+
+#include "PlatformDependent/Xbox360/Source/XenonMemory.h"
+
+#define UNITY_LL_ALLOC(l, s, a) xenon::trackedMalloc(s, a)
+#define UNITY_LL_REALLOC(l, p, s, a) xenon::trackedRealloc(p, s, a)
+#define UNITY_LL_FREE(l, p) xenon::trackedFree(p)
+
+#elif UNITY_PS3
+
+void* operator new (size_t size, size_t align) throw() { return GetMemoryManager().Allocate (size == 0 ? 4 : size, align, kMemNewDelete); }
+void* operator new [] (size_t size, size_t align) throw() { return GetMemoryManager().Allocate (size == 0 ? 4 : size, align, kMemNewDelete); }
+
+#include "PlatformDependent/PS3Player/Allocator/PS3Memory.h"
+
+#define UNITY_LL_ALLOC(l,s,a) PS3Memory::Alloc(l,s,a)
+#define UNITY_LL_REALLOC(l,p,s, a) PS3Memory::Realloc(l,p,s,a)
+#define UNITY_LL_FREE(l,p) PS3Memory::Free(l, p)
+
+#elif UNITY_ANDROID
+
+#define UNITY_LL_ALLOC(l,s,a) ::memalign(a, s)
+#define UNITY_LL_REALLOC(l,p,s,a) ::realloc(p, s)
+#define UNITY_LL_FREE(l,p) ::free(p)
+
+#else
+
+#define UNITY_LL_ALLOC(l,s,a) ::malloc(s)
+#define UNITY_LL_REALLOC(l,p,s,a) ::realloc(p, s)
+#define UNITY_LL_FREE(l,p) ::free(p)
+
+#endif
+
+void* operator new (size_t size, MemLabelRef label, bool set_root, int align, const char* file, int line)
+{
+#if ENABLE_MEM_PROFILER
+ bool root_was_set = false;
+ if (set_root) root_was_set = push_allocation_root(NULL, false);
+#endif
+ void* p = malloc_internal (size, align, label, kAllocateOptionNone, file, line);
+#if ENABLE_MEM_PROFILER
+ if (root_was_set) pop_allocation_root();
+ if (set_root) {
+ GetMemoryProfiler()->RegisterRootAllocation(p, GetMemoryManager().GetAllocator(label), NULL, NULL);
+ push_allocation_root(p, true);
+ }
+#endif
+ return p;
+}
+void* operator new [] (size_t size, MemLabelRef label, bool set_root, int align, const char* file, int line)
+{
+#if ENABLE_MEM_PROFILER
+ bool root_was_set = false;
+ if (set_root) root_was_set = push_allocation_root(NULL, false);
+#endif
+ void* p = malloc_internal (size, align, label, kAllocateOptionNone, file, line);
+#if ENABLE_MEM_PROFILER
+ if (root_was_set) pop_allocation_root();
+ if (set_root) {
+ GetMemoryProfiler()->RegisterRootAllocation(p, GetMemoryManager().GetAllocator(label), NULL, NULL);
+ push_allocation_root(p, true);
+ }
+#endif
+ return p;
+}
+void operator delete (void* p, MemLabelRef label, bool /*set_root*/, int /*align*/, const char* /*file*/, int /*line*/) { free_alloc_internal (p, label); }
+void operator delete [] (void* p, MemLabelRef label, bool /*set_root*/, int /*align*/, const char* /*file*/, int /*line*/) { free_alloc_internal (p, label); }
+
+#if ENABLE_MEMORY_MANAGER
+#include "Runtime/Allocator/UnityDefaultAllocator.h"
+#include "Runtime/Allocator/DynamicHeapAllocator.h"
+#include "Runtime/Allocator/LowLevelDefaultAllocator.h"
+#include "Runtime/Allocator/StackAllocator.h"
+#include "Runtime/Allocator/TLSAllocator.h"
+#include "Runtime/Allocator/DualThreadAllocator.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Threads/ThreadSpecificValue.h"
+
+#if UNITY_IPHONE
+ #include "PlatformDependent/iPhonePlayer/iPhoneNewLabelAllocator.h"
+#endif
+
+#include <map>
+
+typedef DualThreadAllocator< DynamicHeapAllocator< LowLevelAllocator > > MainThreadAllocator;
+typedef TLSAllocator< StackAllocator > TempTLSAllocator;
+
+static MemoryManager* g_MemoryManager = NULL;
+
+#if UNITY_FLASH
+ extern "C" void NativeExt_FreeMemManager(void* p){
+ GetMemoryManager().Deallocate (p, kMemNewDelete);
+ }
+#endif
+
+// new override does not work on mac together with pace
+#if !((UNITY_OSX || UNITY_LINUX) && UNITY_EDITOR) && !(UNITY_WEBGL) && !(UNITY_BB10) && !(UNITY_TIZEN)
+void* operator new (size_t size) THROWING_NEW_THROW { return GetMemoryManager().Allocate (size==0?4:size, kDefaultMemoryAlignment, kMemNewDelete, kAllocateOptionNone, "Overloaded New"); }
+void* operator new [] (size_t size) THROWING_NEW_THROW { return GetMemoryManager().Allocate (size==0?4:size, kDefaultMemoryAlignment, kMemNewDelete, kAllocateOptionNone, "Overloaded New[]"); }
+void operator delete (void* p) throw() { GetMemoryManager().Deallocate (p, kMemNewDelete); }
+void operator delete [] (void* p) throw() { GetMemoryManager().Deallocate (p, kMemNewDelete); }
+
+void* operator new (size_t size, const std::nothrow_t&) throw() { return GetMemoryManager().Allocate (size, kDefaultMemoryAlignment, kMemNewDelete, kAllocateOptionNone, "Overloaded New"); }
+void* operator new [] (size_t size, const std::nothrow_t&) throw() { return GetMemoryManager().Allocate (size, kDefaultMemoryAlignment, kMemNewDelete, kAllocateOptionNone, "Overloaded New[]"); };
+void operator delete (void* p, const std::nothrow_t&) throw() { GetMemoryManager().Deallocate (p, kMemNewDelete); }
+void operator delete [] (void* p, const std::nothrow_t&) throw() { GetMemoryManager().Deallocate (p, kMemNewDelete); }
+#endif
+
+#if UNITY_EDITOR
+static size_t kDynamicHeapChunkSize = 16*1024*1024;
+static size_t kTempAllocatorMainSize = 4*1024*1024;
+static size_t kTempAllocatorThreadSize = 64*1024;
+#elif UNITY_IPHONE || UNITY_ANDROID || UNITY_WII || UNITY_XENON || UNITY_PS3 || UNITY_FLASH || UNITY_WEBGL || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN
+# if !UNITY_IPHONE && !UNITY_WP8
+static size_t kDynamicHeapChunkSize = 1*1024*1024;
+# endif
+static size_t kTempAllocatorMainSize = 128*1024;
+static size_t kTempAllocatorThreadSize = 64*1024;
+#else
+// Win/osx/linux players
+static size_t kDynamicHeapChunkSize = 4*1024*1024;
+static size_t kTempAllocatorMainSize = 512*1024;
+static size_t kTempAllocatorThreadSize = 64*1024;
+#endif
+
+#if ENABLE_MEMORY_MANAGER
+
+
+void PrintShortMemoryStats(TEMP_STRING& str, MemLabelRef label);
+
+static int AlignUp(int value, int alignment)
+{
+ UInt32 ptr = value;
+ UInt32 bitMask = (alignment - 1);
+ UInt32 lowBits = ptr & bitMask;
+ UInt32 adjust = ((alignment - lowBits) & bitMask);
+ return adjust;
+}
+
+void* GetPreallocatedMemory(int size)
+{
+ const size_t numAllocators = 20; // should be configured per platform
+ const size_t additionalStaticMem = sizeof(UnityDefaultAllocator<void>) * numAllocators;
+
+ // preallocated memory for memorymanager and allocators
+ static const int preallocatedSize = sizeof(MemoryManager) + additionalStaticMem;
+#if UNITY_PS3
+ static char __attribute__((aligned(16))) g_MemoryBlockForMemoryManager[preallocatedSize];
+#elif UNITY_XENON
+ static char __declspec(align(16)) g_MemoryBlockForMemoryManager[preallocatedSize];
+#else
+ static char g_MemoryBlockForMemoryManager[preallocatedSize];
+#endif
+ static char* g_MemoryBlockPtr = g_MemoryBlockForMemoryManager;
+
+ size += AlignUp(size, 16);
+
+ void* ptr = g_MemoryBlockPtr;
+ g_MemoryBlockPtr+=size;
+ // Ensure that there is enough space on the preallocated block
+ if(g_MemoryBlockPtr > g_MemoryBlockForMemoryManager + preallocatedSize)
+ return NULL;
+ return ptr;
+}
+#endif
+
+#if UNITY_WIN && ENABLE_MEM_PROFILER
+#define _CRTBLD
+#include <..\crt\src\dbgint.h>
+_CRT_ALLOC_HOOK pfnOldCrtAllocHook;
+int catchMemoryAllocHook(int allocType, void *userData, size_t size, int blockType, long requestNumber, const unsigned char *filename, int lineNumber);
+#endif
+
+void* malloc_internal(size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line)
+{
+ return GetMemoryManager ().Allocate(size, align, label, allocateOptions, file, line);
+}
+
+void* calloc_internal(size_t count, size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line)
+{
+ void* ptr = GetMemoryManager ().Allocate(size*count, align, label, allocateOptions, file, line);
+ if (ptr) memset (ptr, 0, size*count);
+ return ptr;
+}
+
+void* realloc_internal(void* ptr, size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line)
+{
+ return GetMemoryManager ().Reallocate(ptr, size, align, label, allocateOptions, file, line);
+}
+
+void free_internal(void* ptr)
+{
+ // used for mac, since malloc is not hooked from the start, so a number of mallocs will have passed through
+ // before we wrap. This results in pointers being freed, that were not allocated with the memorymanager
+ // therefore we need the alloc->Contains check()
+ GetMemoryManager().Deallocate (ptr);
+}
+
+void free_alloc_internal(void* ptr, MemLabelRef label)
+{
+ GetMemoryManager().Deallocate (ptr, label);
+}
+
+#if (UNITY_OSX && UNITY_EDITOR)
+
+#include <malloc/malloc.h>
+#include <mach/vm_map.h>
+void *(*systemMalloc)(malloc_zone_t *zone, size_t size);
+void *(*systemCalloc)(malloc_zone_t *zone, size_t num_items, size_t size);
+void *(*systemValloc)(malloc_zone_t *zone, size_t size);
+void *(*systemRealloc)(malloc_zone_t *zone, void* ptr, size_t size);
+void *(*systemMemalign)(malloc_zone_t *zone, size_t align, size_t size);
+void (*systemFree)(malloc_zone_t *zone, void *ptr);
+void (*systemFreeSize)(malloc_zone_t *zone, void *ptr, size_t size);
+
+void* my_malloc(malloc_zone_t *zone, size_t size)
+{
+ void* ptr = (*systemMalloc)(zone,size);
+ MemoryManager::m_LowLevelAllocated+=(*malloc_default_zone()->size)(zone, ptr);
+ return ptr;
+
+}
+
+void* my_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
+{
+ void* ptr = (*systemCalloc)(zone,num_items,size);
+ MemoryManager::m_LowLevelAllocated+=(*malloc_default_zone()->size)(zone, ptr);
+ return ptr;
+
+}
+
+void* my_valloc(malloc_zone_t *zone, size_t size)
+{
+ void* ptr = (*systemValloc)(zone,size);
+ MemoryManager::m_LowLevelAllocated+=(*malloc_default_zone()->size)(zone, ptr);
+ return ptr;
+}
+
+void* my_realloc(malloc_zone_t *zone, void* ptr, size_t size)
+{
+ MemoryManager::m_LowLevelAllocated-=(*malloc_default_zone()->size)(zone, ptr);
+ void* newptr = (*systemRealloc)(zone,ptr,size);
+ MemoryManager::m_LowLevelAllocated+=(*malloc_default_zone()->size)(zone, newptr);
+ return newptr;
+}
+
+void* my_memalign(malloc_zone_t *zone, size_t align, size_t size)
+{
+ void* ptr = (*systemMemalign)(zone,align,size);
+ MemoryManager::m_LowLevelAllocated+=(*malloc_default_zone()->size)(zone, ptr);
+ return ptr;
+}
+
+void my_free(malloc_zone_t *zone, void *ptr)
+{
+ int oldsize = (*malloc_default_zone()->size)(zone,ptr);
+ MemoryManager::m_LowLevelAllocated-=oldsize;
+ systemFree(zone,ptr);
+}
+
+void my_free_definite_size(malloc_zone_t *zone, void *ptr, size_t size)
+{
+ MemoryManager::m_LowLevelAllocated-=(*malloc_default_zone()->size)(zone,ptr);
+ systemFreeSize(zone,ptr,size);
+}
+
+#endif
+
+void InitializeMemory()
+{
+ InitializeMemoryLabels();
+
+#if (UNITY_OSX && UNITY_EDITOR)
+
+ UInt32 osxversion = 0;
+ Gestalt(gestaltSystemVersion, (MacSInt32 *) &osxversion);
+
+ // overriding malloc_zone on osx 10.5 causes unity not to start up.
+ if(osxversion >= 0x01060)
+ {
+ malloc_zone_t* dz = malloc_default_zone();
+
+ systemMalloc = dz->malloc;
+ systemCalloc = dz->calloc;
+ systemValloc = dz->valloc;
+ systemRealloc = dz->realloc;
+ systemMemalign = dz->memalign;
+ systemFree = dz->free;
+ systemFreeSize = dz->free_definite_size;
+
+ if(dz->version>=8)
+ vm_protect(mach_task_self(), (uintptr_t)dz, sizeof(malloc_zone_t), 0, VM_PROT_READ | VM_PROT_WRITE);//remove the write protection
+
+ dz->malloc=&my_malloc;
+ dz->calloc=&my_calloc;
+ dz->valloc=&my_valloc;
+ dz->realloc=&my_realloc;
+ dz->memalign=&my_memalign;
+ dz->free=&my_free;
+ dz->free_definite_size=&my_free_definite_size;
+
+ if(dz->version>=8)
+ vm_protect(mach_task_self(), (uintptr_t)dz, sizeof(malloc_zone_t), 0, VM_PROT_READ);//put the write protection back
+
+ }
+#endif
+}
+
+MemoryManager& GetMemoryManager()
+{
+ if (g_MemoryManager == NULL){
+ InitializeMemory();
+ g_MemoryManager = HEAP_NEW(MemoryManager)();
+ }
+ return *g_MemoryManager ;
+}
+
+volatile long MemoryManager::m_LowLevelAllocated = 0;
+volatile long MemoryManager::m_RegisteredGfxDriverMemory = 0;
+
+#if ENABLE_MEMORY_MANAGER
+
+#if _DEBUG || UNITY_EDITOR
+UNITY_TLS_VALUE(bool) s_DisallowAllocationsOnThread;
+inline void MemoryManager::CheckDisalowAllocation()
+{
+ DebugAssert(IsActive());
+
+ // Some codepaths in Unity disallow allocations. For example when a GC is running all threads are stopped.
+ // If we allowed allocations we would allow for very hard to find race conditions.
+ // Thus we explicitly check for it in debug builds.
+ if (s_DisallowAllocationsOnThread)
+ {
+ s_DisallowAllocationsOnThread = false;
+ FatalErrorMsg("CheckDisalowAllocation. Allocating memory when it is not allowed to allocate memory.\n");
+ }
+}
+
+#else
+inline void MemoryManager::CheckDisalowAllocation()
+{
+}
+#endif
+
+MemoryManager::MemoryManager()
+: m_NumAllocators(0)
+, m_FrameTempAllocator(NULL)
+, m_IsInitialized(false)
+, m_IsActive(false)
+{
+#if UNITY_WIN && ENABLE_MEM_PROFILER
+// pfnOldCrtAllocHook = _CrtSetAllocHook(catchMemoryAllocHook);
+#endif
+
+ memset (m_Allocators, 0, sizeof(m_Allocators));
+ memset (m_MainAllocators, 0, sizeof(m_MainAllocators));
+ memset (m_ThreadAllocators, 0, sizeof(m_ThreadAllocators));
+ memset (m_AllocatorMap, 0, sizeof(m_AllocatorMap));
+
+ // Main thread will not have a valid TLSAlloc until ThreadInitialize() is called!
+#if UNITY_FLASH || UNITY_WEBGL
+ m_InitialFallbackAllocator = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>) ("ALLOC_FALLBACK");
+#else
+ m_InitialFallbackAllocator = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (1024*1024, 0, true,"ALLOC_FALLBACK");
+#endif
+
+ for (int i = 0; i < kMemLabelCount; i++)
+ m_AllocatorMap[i].alloc = m_InitialFallbackAllocator;
+}
+
+void MemoryManager::StaticInitialize()
+{
+ GetMemoryManager().ThreadInitialize();
+}
+
+void MemoryManager::StaticDestroy()
+{
+#if !UNITY_OSX
+ // not able to destroy profiler and memorymanager on osx because apple.coreaudio is still running a thread.
+ // FMOD is looking into shutting this down properly. Untill then, don't cleanup
+#if ENABLE_MEM_PROFILER
+ MemoryProfiler::StaticDestroy();
+#endif
+ GetMemoryManager().ThreadCleanup();
+#endif
+}
+
+void MemoryManager::InitializeMainThreadAllocators()
+{
+ m_FrameTempAllocator = HEAP_NEW(TempTLSAllocator)("ALLOC_TEMP_THREAD");
+
+#if (UNITY_WIN && !UNITY_WP8) || UNITY_OSX
+ BaseAllocator* defaultThreadAllocator = NULL;
+ m_MainAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (kDynamicHeapChunkSize, 1024, false,"ALLOC_DEFAULT_MAIN");
+ m_ThreadAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (1024*1024,1024, true,"ALLOC_DEFAULT_THREAD");
+ BaseAllocator* defaultAllocator = m_Allocators[m_NumAllocators] = HEAP_NEW(MainThreadAllocator)("ALLOC_DEFAULT", m_MainAllocators[m_NumAllocators], m_ThreadAllocators[m_NumAllocators]);
+ defaultThreadAllocator = m_ThreadAllocators[m_NumAllocators];
+ m_NumAllocators++;
+#else
+ BaseAllocator* defaultAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_DEFAULT");
+#endif
+
+ for (int i = 0; i < kMemLabelCount; i++)
+ m_AllocatorMap[i].alloc = defaultAllocator;
+
+ m_AllocatorMap[kMemTempAllocId].alloc = m_FrameTempAllocator;
+ m_AllocatorMap[kMemStaticStringId].alloc = m_InitialFallbackAllocator;
+
+#if UNITY_IPHONE
+ m_AllocatorMap[kMemNewDeleteId].alloc = m_Allocators[m_NumAllocators++] = HEAP_NEW(IphoneNewLabelAllocator);
+#endif
+
+#if (UNITY_WIN && !UNITY_WP8) || UNITY_OSX
+ m_MainAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (kDynamicHeapChunkSize,0, false,"ALLOC_GFX_MAIN");
+ m_ThreadAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (1024*1024,0, true,"ALLOC_GFX_THREAD");
+ BaseAllocator* gfxAllocator = m_Allocators[m_NumAllocators] = HEAP_NEW(MainThreadAllocator)("ALLOC_GFX", m_MainAllocators[m_NumAllocators], m_ThreadAllocators[m_NumAllocators]);
+ BaseAllocator* gfxThreadAllocator = m_ThreadAllocators[m_NumAllocators];
+ m_NumAllocators++;
+
+ m_MainAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (8*1024*1024,0, false,"ALLOC_CACHEOBJECTS_MAIN");
+ m_ThreadAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (2*1024*1024,0, true,"ALLOC_CACHEOBJECTS_THREAD");
+ BaseAllocator* cacheAllocator = m_Allocators[m_NumAllocators] = HEAP_NEW(MainThreadAllocator)("ALLOC_CACHEOBJECTS", m_MainAllocators[m_NumAllocators], m_ThreadAllocators[m_NumAllocators]);
+ m_NumAllocators++;
+
+ m_MainAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (kDynamicHeapChunkSize,0, false,"ALLOC_TYPETREE_MAIN");
+ m_ThreadAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (1024*1024,0, true,"ALLOC_TYPETREE_THREAD");
+ BaseAllocator* typetreeAllocator = m_Allocators[m_NumAllocators] = HEAP_NEW(MainThreadAllocator)("ALLOC_TYPETREE", m_MainAllocators[m_NumAllocators], m_ThreadAllocators[m_NumAllocators]);
+ m_NumAllocators++;
+
+ m_MainAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (4*1024*1024,0, false,"ALLOC_PROFILER_MAIN");
+ m_ThreadAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (4*1024*1024,0, true,"ALLOC_PROFILER_THREAD");
+ BaseAllocator* profilerAllocator = m_Allocators[m_NumAllocators] = HEAP_NEW(MainThreadAllocator)("ALLOC_PROFILER", m_MainAllocators[m_NumAllocators], m_ThreadAllocators[m_NumAllocators]);
+ m_NumAllocators++;
+
+ m_AllocatorMap[kMemDynamicGeometryId].alloc
+ = m_AllocatorMap[kMemImmediateGeometryId].alloc
+ = m_AllocatorMap[kMemGeometryId].alloc
+ = m_AllocatorMap[kMemVertexDataId].alloc
+ = m_AllocatorMap[kMemBatchedGeometryId].alloc
+ = m_AllocatorMap[kMemTextureId].alloc = gfxAllocator;
+
+ m_AllocatorMap[kMemTypeTreeId].alloc = typetreeAllocator;
+
+ m_AllocatorMap[kMemThreadId].alloc = defaultThreadAllocator;
+ m_AllocatorMap[kMemGfxThreadId].alloc = gfxThreadAllocator;
+
+ m_AllocatorMap[kMemTextureCacheId].alloc = cacheAllocator;
+ m_AllocatorMap[kMemSerializationId].alloc = cacheAllocator;
+ m_AllocatorMap[kMemFileId].alloc = cacheAllocator;
+
+ m_AllocatorMap[kMemProfilerId].alloc = profilerAllocator;
+ m_AllocatorMap[kMemMemoryProfilerId].alloc = profilerAllocator;
+ m_AllocatorMap[kMemMemoryProfilerStringId].alloc = profilerAllocator;
+
+#elif UNITY_XENON
+#if 1
+ // DynamicHeapAllocator uses TLSF pools which are O(1) constant time for alloc/free
+ // It should be used for high alloc/dealloc traffic
+ BaseAllocator* dynAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>)(32*1024*1024, 0, true, "ALLOC_TINYBLOCKS");
+ m_AllocatorMap[kMemBaseObjectId].alloc = dynAllocator;
+ m_AllocatorMap[kMemAnimationId].alloc = dynAllocator;
+ m_AllocatorMap[kMemSTLId].alloc = dynAllocator;
+ m_AllocatorMap[kMemNewDeleteId].alloc = dynAllocator;
+#else
+ BaseAllocator* gameObjectAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_GAMEOBJECT");
+ BaseAllocator* gfxAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_GFX");
+
+ BaseAllocator* profilerAllocator;
+#if XBOX_USE_DEBUG_MEMORY
+ if (xenon::GetIsDebugMemoryEnabled())
+ profilerAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocatorDebugMem>)(16*1024*1024, 0, true, "ALLOC_PROFILER");
+ else
+#endif
+ profilerAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_PROFILER");
+
+ m_AllocatorMap[kMemDynamicGeometryId].alloc
+ = m_AllocatorMap[kMemImmediateGeometryId].alloc
+ = m_AllocatorMap[kMemGeometryId].alloc
+ = m_AllocatorMap[kMemVertexDataId].alloc
+ = m_AllocatorMap[kMemBatchedGeometryId].alloc
+ = m_AllocatorMap[kMemTextureId].alloc = gfxAllocator;
+
+ m_AllocatorMap[kMemBaseObjectId].alloc = gameObjectAllocator;
+
+ m_AllocatorMap[kMemProfilerId].alloc = profilerAllocator;
+ m_AllocatorMap[kMemMemoryProfilerId].alloc = profilerAllocator;
+ m_AllocatorMap[kMemMemoryProfilerStringId].alloc = profilerAllocator;
+#endif
+
+#elif UNITY_PS3
+
+ BaseAllocator* gameObjectAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_GAMEOBJECT");
+ BaseAllocator* gfxAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_GFX");
+ BaseAllocator* profilerAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_PROFILER");
+ BaseAllocator* vertexDataAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(PS3DelayedReleaseAllocator("PS3_DELAYED_RELEASE_ALLOCATOR_PROXY", HEAP_NEW(PS3DlmallocAllocator)("PS3_DLMALLOC_ALLOCATOR")));
+ BaseAllocator* delayedReleaseAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(PS3DelayedReleaseAllocator("PS3_DELAYED_RELEASE_ALLOCATOR_PROXY", HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("PS3_DELAYED_RELEASE_ALLOCATOR")));
+ BaseAllocator* ioMappedAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(PS3DlmallocAllocator("PS3_IOMAPPED_ALLOCATOR"));
+
+ m_AllocatorMap[kMemDynamicGeometryId].alloc
+ = m_AllocatorMap[kMemImmediateGeometryId].alloc
+ = m_AllocatorMap[kMemGeometryId].alloc
+ = m_AllocatorMap[kMemVertexDataId].alloc
+ = m_AllocatorMap[kMemBatchedGeometryId].alloc
+ = m_AllocatorMap[kMemPS3RingBuffers.label].alloc
+ = m_AllocatorMap[kMemPS3RSXBuffers.label].alloc
+ = m_AllocatorMap[kMemTextureId].alloc = ioMappedAllocator;
+
+ m_AllocatorMap[kMemBaseObjectId].alloc = gameObjectAllocator;
+ m_AllocatorMap[kMemProfilerId].alloc =
+ m_AllocatorMap[kMemMemoryProfilerId].alloc =
+ m_AllocatorMap[kMemMemoryProfilerStringId].alloc = profilerAllocator;
+
+ m_AllocatorMap[kMemSkinningId].alloc =
+// m_AllocatorMap[kMemSkinningTempId].alloc =
+ m_AllocatorMap[kMemPS3DelayedReleaseId].alloc = delayedReleaseAllocator;
+
+ m_AllocatorMap[kMemVertexDataId].alloc = vertexDataAllocator;
+
+#else
+
+ BaseAllocator* gameObjectAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_GAMEOBJECT");
+ BaseAllocator* gfxAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_GFX");
+ BaseAllocator* profilerAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_PROFILER");
+
+ m_AllocatorMap[kMemDynamicGeometryId].alloc
+ = m_AllocatorMap[kMemImmediateGeometryId].alloc
+ = m_AllocatorMap[kMemGeometryId].alloc
+ = m_AllocatorMap[kMemVertexDataId].alloc
+ = m_AllocatorMap[kMemBatchedGeometryId].alloc
+ = m_AllocatorMap[kMemTextureId].alloc = gfxAllocator;
+
+ m_AllocatorMap[kMemBaseObjectId].alloc = gameObjectAllocator;
+
+ m_AllocatorMap[kMemProfilerId].alloc
+ = m_AllocatorMap[kMemMemoryProfilerId].alloc
+ = m_AllocatorMap[kMemMemoryProfilerStringId].alloc = profilerAllocator;
+
+#endif
+
+ m_IsInitialized = true;
+ m_IsActive = true;
+
+#if ENABLE_MEM_PROFILER
+ MemoryProfiler::StaticInitialize();
+#endif
+
+ Assert(m_FrameTempAllocator);
+}
+
+MemoryManager::~MemoryManager()
+{
+ for(int i = 0; i < m_NumAllocators; i++)
+ {
+ Assert(m_Allocators[i]->GetAllocatedMemorySize() == 0);
+ }
+
+ ThreadCleanup();
+
+ for (int i = 0; i < m_NumAllocators; i++){
+ HEAP_DELETE (m_Allocators[i], BaseAllocator);
+ }
+#if UNITY_WIN
+#if ENABLE_MEM_PROFILER
+// _CrtSetAllocHook(pfnOldCrtAllocHook);
+#endif
+#endif
+}
+
+class MemoryManagerAutoDestructor
+{
+public:
+ MemoryManagerAutoDestructor(){}
+ ~MemoryManagerAutoDestructor()
+ {
+ //HEAP_DELETE(g_MemoryManager, MemoryManager);
+ }
+};
+
+MemoryManagerAutoDestructor g_MemoryManagerAutoDestructor;
+
+
+#else
+
+
+MemoryManager::MemoryManager()
+: m_NumAllocators(0)
+, m_FrameTempAllocator(NULL)
+{
+}
+
+MemoryManager::~MemoryManager()
+{
+}
+
+#endif
+
+#if UNITY_WIN
+#include <winnt.h>
+#define ON_WIN(x) x
+#else
+#define ON_WIN(x)
+#endif
+
+#if ENABLE_MEMORY_MANAGER
+
+void* MemoryManager::LowLevelAllocate(size_t size)
+{
+ ON_WIN( InterlockedExchangeAdd(&m_LowLevelAllocated, size) );
+ int* ptr = (int*)UNITY_LL_ALLOC(kMemDefault, size + kMemoryManagerOverhead, kDefaultMemoryAlignment);
+ if(ptr != NULL)
+ {
+ *ptr = size;
+ ptr += (kMemoryManagerOverhead >> 2);
+ }
+
+ return ptr;
+}
+
+void* MemoryManager::LowLevelCAllocate(size_t count, size_t size)
+{
+ ON_WIN( InterlockedExchangeAdd(&m_LowLevelAllocated, count*size) );
+ int allocSize = count*size + kMemoryManagerOverhead;
+ int* ptr = (int*)UNITY_LL_ALLOC(kMemDefault, allocSize, kDefaultMemoryAlignment);
+ if(ptr != NULL)
+ {
+ memset(ptr, 0, allocSize);
+ *ptr = count*size;
+ ptr += (kMemoryManagerOverhead >> 2);
+ }
+ return ptr;
+}
+
+void* MemoryManager::LowLevelReallocate( void* p, size_t size )
+{
+ int* ptr = (int*) p;
+ ptr -= kMemoryManagerOverhead >> 2;
+ ON_WIN( InterlockedExchangeAdd(&m_LowLevelAllocated, -*ptr) );
+ int* newptr = (int*)UNITY_LL_REALLOC(kMemDefault, ptr, size + kMemoryManagerOverhead, kDefaultMemoryAlignment);
+ if(newptr != NULL)
+ {
+ *newptr = size;
+ newptr += (kMemoryManagerOverhead >> 2);
+ }
+ ON_WIN( InterlockedExchangeAdd(&m_LowLevelAllocated, size) );
+ return newptr;
+}
+
+void MemoryManager::LowLevelFree(void* p){
+ if(p == NULL)
+ return;
+ int* ptr = (int*) p;
+ ptr -= kMemoryManagerOverhead >> 2;
+ ON_WIN( InterlockedExchangeAdd(&m_LowLevelAllocated, -*ptr) );
+ UNITY_LL_FREE(kMemDefault,ptr);
+}
+
+void MemoryManager::ThreadInitialize(size_t tempSize)
+{
+ int tempAllocatorSize = kTempAllocatorThreadSize;
+ if(Thread::CurrentThreadIsMainThread() && !m_IsInitialized)
+ {
+ InitializeMainThreadAllocators();
+ tempAllocatorSize = kTempAllocatorMainSize;
+ }
+
+ if(tempSize != 0)
+ tempAllocatorSize = tempSize;
+
+ StackAllocator* tempAllocator = UNITY_NEW(StackAllocator(tempAllocatorSize, "ALLOC_TEMP_THREAD"), kMemManager);
+ m_FrameTempAllocator->ThreadInitialize(tempAllocator);
+}
+
+void MemoryManager::ThreadCleanup()
+{
+ for(int i = 0; i < m_NumAllocators; i++)
+ m_Allocators[i]->ThreadCleanup();
+
+ if(Thread::CurrentThreadIsMainThread())
+ {
+ m_FrameTempAllocator->ThreadCleanup();
+ m_FrameTempAllocator = NULL;
+
+ m_IsActive = false;
+
+#if !UNITY_EDITOR
+ for(int i = 0; i < m_NumAllocators; i++)
+ {
+ HEAP_DELETE(m_Allocators[i], BaseAllocator);
+ if(m_MainAllocators[i])
+ HEAP_DELETE(m_MainAllocators[i], BaseAllocator);
+ if(m_ThreadAllocators[i])
+ HEAP_DELETE(m_ThreadAllocators[i], BaseAllocator);
+ m_Allocators[i] = 0;
+ m_MainAllocators[i] = 0;
+ m_ThreadAllocators[i] = 0;
+ }
+ m_NumAllocators = 0;
+#else
+ for(int i = 0; i < m_NumAllocators; i++)
+ {
+ if(m_MainAllocators[i])
+ m_Allocators[i] = m_MainAllocators[i];
+ }
+#endif
+ for (int i = 0; i < kMemLabelCount; i++)
+ m_AllocatorMap[i].alloc = m_InitialFallbackAllocator;
+
+ return;
+ }
+#if ENABLE_MEM_PROFILER
+ GetMemoryProfiler()->ThreadCleanup();
+#endif
+ m_FrameTempAllocator->ThreadCleanup();
+}
+
+void MemoryManager::FrameMaintenance(bool cleanup)
+{
+ m_FrameTempAllocator->FrameMaintenance(cleanup);
+ for(int i = 0; i < m_NumAllocators; i++)
+ m_Allocators[i]->FrameMaintenance(cleanup);
+}
+
+#else
+
+void* MemoryManager::LowLevelAllocate(int size)
+{
+ return UNITY_LL_ALLOC(kMemDefault,size+sizeof(int), 4);
+}
+
+void* MemoryManager::LowLevelCAllocate(int count, int size)
+{
+ int* ptr = (int*)UNITY_LL_ALLOC(kMemDefault,count*size, 4);
+ memset(ptr,0,count*size);
+ return ptr;
+}
+
+void* MemoryManager::LowLevelReallocate( void* p, int size )
+{
+ return UNITY_LL_REALLOC(kMemDefault,p, size,4);
+}
+
+void MemoryManager::LowLevelFree(void* p){
+ UNITY_LL_FREE(kMemDefault,p);
+}
+
+void MemoryManager::ThreadInitialize(size_t tempSize)
+{}
+
+void MemoryManager::ThreadCleanup()
+{}
+
+#endif
+
+void OutOfMemoryError( size_t size, int align, MemLabelRef label, int line, const char* file )
+{
+ TEMP_STRING str;
+ str.reserve(30*1024);
+ str += FormatString<TEMP_STRING>("Could not allocate memory: System out of memory!\n");
+ str += FormatString<TEMP_STRING>("Trying to allocate: %" PRINTF_SIZET_FORMAT "B with %d alignment. MemoryLabel: %s\n", size, align, GetMemoryManager().GetMemcatName(label));
+ str += FormatString<TEMP_STRING>("Allocation happend at: Line:%d in %s\n", line, file);
+ PrintShortMemoryStats(str, label);
+ // first call the plain printf_console, to make a printout that doesn't do callstack and other allocations
+ printf_console("%s", str.c_str());
+ // Then do a FatalErroString, that brings up a dialog and launches the bugreporter.
+ FatalErrorString(str.c_str());
+}
+
+inline void* CheckAllocation(void* ptr, size_t size, int align, MemLabelRef label, const char* file, int line)
+{
+ if(ptr == NULL)
+ OutOfMemoryError(size, align, label, line, file);
+ return ptr;
+}
+
+void MemoryManager::DisallowAllocationsOnThisThread()
+{
+#if _DEBUG || UNITY_EDITOR
+ s_DisallowAllocationsOnThread = true;
+#endif
+}
+void MemoryManager::ReallowAllocationsOnThisThread()
+{
+#if _DEBUG || UNITY_EDITOR
+ s_DisallowAllocationsOnThread = false;
+#endif
+}
+
+void* MemoryManager::Allocate(size_t size, int align, MemLabelRef label, int allocateOptions/* = kNone*/, const char* file /* = NULL */, int line /* = 0 */)
+{
+ DebugAssert(IsPowerOfTwo(align));
+ DebugAssert(align != 0);
+
+ align = ((align-1) | (kDefaultMemoryAlignment-1)) + 1; // Max(align, kDefaultMemoryAlignment)
+
+ // Fallback to backup allocator if we have not yet initialized the MemoryManager
+ if(!IsActive())
+ {
+ void* ptr = m_InitialFallbackAllocator->Allocate(size, align);
+ #if ENABLE_MEM_PROFILER
+ if (ptr)
+ MemoryProfiler::InitAllocation(ptr, m_InitialFallbackAllocator);
+ #endif
+ return ptr;
+ }
+
+ if (IsTempAllocatorLabel(label))
+ {
+ void* ptr = ((TempTLSAllocator*)m_FrameTempAllocator)->TempTLSAllocator::Allocate(size, align);
+ if(ptr)
+ return ptr;
+ // if tempallocator thread has not been initialized fallback to defualt
+ return Allocate(size, align, kMemDefault, allocateOptions, file, line);
+ }
+
+ BaseAllocator* alloc = GetAllocator(label);
+ CheckDisalowAllocation();
+
+ void* ptr = alloc->Allocate(size, align);
+
+ if ((allocateOptions & kAllocateOptionReturnNullIfOutOfMemory) && !ptr)
+ return NULL;
+
+ CheckAllocation( ptr, size, align, label, file, line );
+
+ #if ENABLE_MEM_PROFILER
+ RegisterAllocation(ptr, size, alloc, label, "Allocate", file, line);
+ #endif
+
+ DebugAssert(((int)ptr & (align-1)) == 0);
+
+#if STOMP_MEMORY
+ memset(ptr, 0xcd, size);
+#endif
+
+ return ptr;
+}
+
+void* MemoryManager::Reallocate(void* ptr, size_t size, int align, MemLabelRef label, int allocateOptions/* = kNone*/, const char* file /* = NULL */, int line /* = 0 */)
+{
+ DebugAssert(IsPowerOfTwo(align));
+ DebugAssert(align != 0);
+
+ if(ptr == NULL)
+ return Allocate(size,align,label,allocateOptions,file,line);
+
+ align = ((align-1) | (kDefaultMemoryAlignment-1)) + 1; // Max(align, kDefaultMemoryAlignment)
+
+ // Fallback to backup allocator if we have not yet initialized the MemoryManager
+ if(!IsActive())
+ return m_InitialFallbackAllocator->Reallocate(ptr, size, align);
+
+ if (IsTempAllocatorLabel(label))
+ {
+ void* newptr = ((TempTLSAllocator*)m_FrameTempAllocator)->TempTLSAllocator::Reallocate(ptr, size, align);
+ if(newptr)
+ return newptr;
+ // if tempallocator thread has not been initialized fallback to defualt
+ return Reallocate( ptr, size, align, kMemDefault, allocateOptions, file, line);
+ }
+
+ BaseAllocator* alloc = GetAllocator(label);
+ CheckDisalowAllocation();
+
+ if(ptr != NULL && !alloc->Contains(ptr))
+ {
+ // It wasn't the expected allocator that contained the pointer.
+ // allocate on the expected allocator and move the memory there
+ void* newptr = Allocate(size,align,label,allocateOptions,file,line);
+ if ((allocateOptions & kAllocateOptionReturnNullIfOutOfMemory) && !newptr)
+ return NULL;
+
+ int oldSize = GetAllocatorContainingPtr(ptr)->GetPtrSize(ptr);
+ memcpy(newptr, ptr, size<oldSize?size:oldSize);
+
+ Deallocate(ptr);
+ return newptr;
+ }
+
+ #if ENABLE_MEM_PROFILER
+ // register the deletion of the old allocation and extract the old root owner into the label
+ ProfilerAllocationHeader* root = RegisterDeallocation(ptr, alloc, label, "Reallocate");
+ #endif
+
+ void* newptr = alloc->Reallocate(ptr, size, align);
+
+ if ((allocateOptions & kAllocateOptionReturnNullIfOutOfMemory) && !newptr)
+ return NULL;
+
+ CheckAllocation( newptr, size, align, label, file, line );
+
+ #if ENABLE_MEM_PROFILER
+ RegisterAllocation(newptr, size, alloc, MemLabelId(label.label, root), "Reallocate", file, line);
+ #endif
+
+ DebugAssert(((int)newptr & (align-1)) == 0);
+
+ return newptr;
+}
+
+void MemoryManager::Deallocate(void* ptr, MemLabelRef label)
+{
+ if(ptr == NULL)
+ return;
+
+ if(!IsActive()) // if we are outside the scope of Initialize/Destroy - fallback
+ return Deallocate(ptr);
+
+ if (IsTempAllocatorLabel(label))
+ {
+ if ( ((TempTLSAllocator*)m_FrameTempAllocator)->TempTLSAllocator::TryDeallocate(ptr) )
+ return;
+ // If not found, Fallback to do a search for what allocator has the pointer
+ return Deallocate(ptr);
+ }
+
+ BaseAllocator* alloc = GetAllocator(label);
+ CheckDisalowAllocation();
+
+ if(!alloc->Contains(ptr))
+ return Deallocate(ptr);
+
+#if ENABLE_MEM_PROFILER
+ RegisterDeallocation(ptr, alloc, label, "Deallocate");
+#endif
+
+#if STOMP_MEMORY
+ memset32(ptr, 0xdeadbeef, alloc->GetPtrSize(ptr));
+#endif
+
+ alloc->Deallocate(ptr);
+}
+
+void MemoryManager::Deallocate(void* ptr)
+{
+ if (ptr == NULL)
+ return;
+
+ BaseAllocator* alloc = GetAllocatorContainingPtr(ptr);
+
+ if (alloc)
+ {
+ Assert (alloc != m_FrameTempAllocator);
+#if ENABLE_MEM_PROFILER
+ if(GetMemoryProfiler() && alloc != m_InitialFallbackAllocator)
+ {
+ size_t oldsize = alloc->GetPtrSize(ptr);
+ GetMemoryProfiler()->UnregisterAllocation(ptr, alloc, oldsize, NULL, kMemDefault);
+ //RegisterDeallocation(MemLabelId(labelid), oldsize); // since we don't have the label, we will not register this deallocation
+ if(m_LogAllocations)
+ printf_console("Deallocate (%8X): %11d\tTotal: %.2fMB (%d)\n", (unsigned int)ptr, -(int)oldsize, GetTotalAllocatedMemory() / (1024.0f * 1024.0f), (int)GetTotalAllocatedMemory());
+ }
+#endif
+#if STOMP_MEMORY
+ memset32(ptr, 0xdeadbeef, alloc->GetPtrSize(ptr));
+#endif
+ alloc->Deallocate(ptr);
+ }
+ else
+ {
+ if(IsActive())
+ UNITY_LL_FREE (kMemDefault, ptr);
+ //else ignore the deallocation, because the allocator is no longer around
+ }
+}
+
+struct ExternalAllocInfo
+{
+ size_t size;
+ size_t relatedID;
+ const char* file;
+ int line;
+};
+typedef UNITY_MAP(kMemMemoryProfiler,void*,ExternalAllocInfo ) ExternalAllocationMap;
+static ExternalAllocationMap* g_ExternalAllocations = NULL;
+Mutex g_ExternalAllocationLock;
+void register_external_gfx_allocation(void* ptr, size_t size, size_t related, const char* file, int line)
+{
+ Mutex::AutoLock autolock(g_ExternalAllocationLock);
+ if (g_ExternalAllocations == NULL)
+ {
+ SET_ALLOC_OWNER(NULL);
+ g_ExternalAllocations = new ExternalAllocationMap();
+ }
+ ExternalAllocationMap::iterator it = g_ExternalAllocations->find(ptr);
+ if (it != g_ExternalAllocations->end())
+ {
+ ErrorStringMsg("allocation 0x%p already registered @ %s:l%d size %d; now calling from %s:l%d size %d?",
+ ptr,
+ it->second.file, it->second.line, it->second.size,
+ file, line, size);
+ }
+
+ if (related == 0)
+ related = (size_t)ptr;
+
+ ExternalAllocInfo info;
+ info.size = size;
+ info.relatedID = related;
+ info.file = file;
+ info.line = line;
+ g_ExternalAllocations->insert(std::make_pair(ptr, info));
+ MemoryManager::m_RegisteredGfxDriverMemory += size;
+#if ENABLE_MEM_PROFILER
+ GetMemoryProfiler()->RegisterMemoryToID(related, size);
+#endif
+}
+
+void register_external_gfx_deallocation(void* ptr, const char* file, int line)
+{
+ if(ptr == NULL)
+ return;
+ Mutex::AutoLock autolock(g_ExternalAllocationLock);
+ if (g_ExternalAllocations == NULL)
+ g_ExternalAllocations = new ExternalAllocationMap();
+
+ ExternalAllocationMap::iterator it = g_ExternalAllocations->find(ptr);
+ if (it == g_ExternalAllocations->end())
+ {
+ // not registered
+ return;
+ }
+ size_t size = it->second.size;
+ size_t related = it->second.relatedID;
+ MemoryManager::m_RegisteredGfxDriverMemory -= size;
+ g_ExternalAllocations->erase(it);
+#if ENABLE_MEM_PROFILER
+ GetMemoryProfiler()->UnregisterMemoryToID(related,size);
+#endif
+}
+
+
+typedef UNITY_MAP(kMemDefault,MemLabelIdentifier, BaseAllocator*) CustomAllocators;
+CustomAllocators* g_CustomAllocators = NULL;
+int nextCustomAllocatorIndex = (MemLabelIdentifier)0x1000;
+
+BaseAllocator* MemoryManager::GetAllocatorContainingPtr(const void* ptr)
+{
+ if(m_FrameTempAllocator && m_FrameTempAllocator->Contains(ptr))
+ return m_FrameTempAllocator;
+
+ for(int i = 0; i < m_NumAllocators ; i++)
+ {
+ if(m_Allocators[i]->IsAssigned() && m_Allocators[i]->Contains(ptr))
+ return m_Allocators[i];
+ }
+
+ if(m_InitialFallbackAllocator->Contains(ptr))
+ return m_InitialFallbackAllocator;
+
+ if(g_CustomAllocators)
+ {
+ CustomAllocators::iterator it = g_CustomAllocators->begin();
+ for(;it != g_CustomAllocators->end(); ++it)
+ if(it->second->Contains(ptr))
+ return it->second;
+ }
+ return NULL;
+}
+
+const char* MemoryManager::GetAllocatorName( int i )
+{
+ return i < m_NumAllocators ? m_Allocators[i]->GetName() : "Custom";
+}
+
+const char* MemoryManager::GetMemcatName( MemLabelRef label )
+{
+ return label.label < kMemLabelCount ? MemLabelName[label.label] : "Custom";
+}
+
+MemLabelId MemoryManager::AddCustomAllocator(BaseAllocator* allocator)
+{
+ Assert(allocator->Contains(NULL) == false); // to assure that Contains does not just return true
+ MemLabelIdentifier label =(MemLabelIdentifier)(nextCustomAllocatorIndex++);
+ if(!g_CustomAllocators)
+ g_CustomAllocators = UNITY_NEW(CustomAllocators,kMemDefault);
+ (*g_CustomAllocators)[label] = allocator;
+ return MemLabelId(label, NULL);
+}
+
+void MemoryManager::RemoveCustomAllocator(BaseAllocator* allocator)
+{
+ if(!g_CustomAllocators)
+ return;
+
+ for(CustomAllocators::iterator it = g_CustomAllocators->begin();
+ it != g_CustomAllocators->end();
+ ++it)
+ {
+ if (it->second == allocator)
+ {
+ g_CustomAllocators->erase(it);
+ break;
+ }
+ }
+ if(g_CustomAllocators->empty())
+ UNITY_DELETE(g_CustomAllocators, kMemDefault);
+}
+
+BaseAllocator* MemoryManager::GetAllocatorAtIndex( int index )
+{
+ return m_Allocators[index];
+}
+
+BaseAllocator* MemoryManager::GetAllocator( MemLabelRef label )
+{
+ DebugAssert(!IsTempAllocatorLabel(label));
+ if(label.label < kMemLabelCount)
+ {
+ BaseAllocator* alloc = m_AllocatorMap[label.label].alloc;
+ return alloc;
+ }
+ else if(label.label == kMemLabelCount)
+ return NULL;
+
+ if(g_CustomAllocators)
+ {
+ CustomAllocators::iterator it = g_CustomAllocators->find(label.label);
+ if(it != g_CustomAllocators->end())
+ return (*it).second;
+ }
+
+ return NULL;
+}
+
+int MemoryManager::GetAllocatorIndex( BaseAllocator* alloc )
+{
+ for(int i = 0; i < m_NumAllocators; i++)
+ if(alloc == m_Allocators[i])
+ return i;
+
+ return m_NumAllocators;
+}
+
+size_t MemoryManager::GetTotalAllocatedMemory()
+{
+ size_t total = m_FrameTempAllocator->GetAllocatedMemorySize();
+ for(int i = 0; i < m_NumAllocators ; i++)
+ total += m_Allocators[i]->GetAllocatedMemorySize();
+ return total;
+}
+
+size_t MemoryManager::GetTotalReservedMemory()
+{
+ size_t total = m_FrameTempAllocator->GetReservedSizeTotal();
+ for(int i = 0; i < m_NumAllocators ; i++)
+ total += m_Allocators[i]->GetReservedSizeTotal();
+ return total;
+}
+
+size_t MemoryManager::GetTotalUnusedReservedMemory()
+{
+ return GetTotalReservedMemory() - GetTotalAllocatedMemory();;
+}
+
+int MemoryManager::GetAllocatorCount( )
+{
+ return m_NumAllocators;
+}
+
+size_t MemoryManager::GetAllocatedMemory( MemLabelRef label )
+{
+ return m_AllocatorMap[label.label].allocatedMemory;
+}
+
+size_t MemoryManager::GetTotalProfilerMemory()
+{
+ return GetAllocator(kMemProfiler)->GetAllocatedMemorySize();
+}
+
+int MemoryManager::GetAllocCount( MemLabelRef label )
+{
+ return m_AllocatorMap[label.label].numAllocs;
+}
+
+size_t MemoryManager::GetLargestAlloc( MemLabelRef label )
+{
+ return m_AllocatorMap[label.label].largestAlloc;
+}
+
+void MemoryManager::StartLoggingAllocations(size_t logAllocationsThreshold)
+{
+ m_LogAllocations = true;
+ m_LogAllocationsThreshold = logAllocationsThreshold;
+};
+
+void MemoryManager::StopLoggingAllocations()
+{
+ m_LogAllocations = false;
+};
+
+#if ENABLE_MEM_PROFILER
+void MemoryManager::RegisterAllocation(void* ptr, size_t size, BaseAllocator* alloc, MemLabelRef label, const char* function, const char* file, int line)
+{
+ if (GetMemoryProfiler())
+ {
+ DebugAssert(!IsTempAllocatorLabel(label));
+ if(label.label < kMemLabelCount)
+ {
+ m_AllocatorMap[label.label].allocatedMemory += size;
+ m_AllocatorMap[label.label].numAllocs++;
+ m_AllocatorMap[label.label].largestAlloc = std::max(m_AllocatorMap[label.label].largestAlloc, size);
+ }
+ GetMemoryProfiler()->RegisterAllocation(ptr, label, file, line, size);
+ if (m_LogAllocations && size >= m_LogAllocationsThreshold)
+ {
+ size_t totalAllocatedMemoryAfterAllocation = GetTotalAllocatedMemory();
+ printf_console( "%s (%p): %11" PRINTF_SIZET_FORMAT "\tTotal: %.2fMB (%" PRINTF_SIZET_FORMAT ") in %s:%d\n",
+ function, ptr, size, totalAllocatedMemoryAfterAllocation / (1024.0f * 1024.0f), totalAllocatedMemoryAfterAllocation, file, line
+ );
+ }
+ }
+}
+
+ProfilerAllocationHeader* MemoryManager::RegisterDeallocation(void* ptr, BaseAllocator* alloc, MemLabelRef label, const char* function)
+{
+ ProfilerAllocationHeader* relatedHeader = NULL;
+ if (ptr != NULL && GetMemoryProfiler())
+ {
+ DebugAssert(!IsTempAllocatorLabel(label));
+ size_t oldsize = alloc->GetPtrSize(ptr);
+ GetMemoryProfiler()->UnregisterAllocation(ptr, alloc, oldsize, &relatedHeader, label);
+ if(label.label < kMemLabelCount)
+ {
+ m_AllocatorMap[label.label].allocatedMemory -= oldsize;
+ m_AllocatorMap[label.label].numAllocs--;
+ }
+
+ if (m_LogAllocations && oldsize >= m_LogAllocationsThreshold)
+ {
+ size_t totalAllocatedMemoryAfterDeallocation = GetTotalAllocatedMemory() - oldsize;
+ printf_console( "%s (%p): %11" PRINTF_SIZET_FORMAT "\tTotal: %.2fMB (%" PRINTF_SIZET_FORMAT ")\n",
+ function, ptr, -(int)oldsize, totalAllocatedMemoryAfterDeallocation / (1024.0f * 1024.0f), totalAllocatedMemoryAfterDeallocation);
+ }
+ }
+ return relatedHeader;
+}
+#endif
+
+void PrintShortMemoryStats(TEMP_STRING& str, MemLabelRef label)
+{
+#if ENABLE_MEMORY_MANAGER
+ MemoryManager& mm = GetMemoryManager();
+ str += "Memory overview\n\n";
+ for(int i = 0; i < mm.GetAllocatorCount() ; i++)
+ {
+ BaseAllocator* alloc = mm.GetAllocatorAtIndex(i);
+ if(alloc == NULL)
+ continue;
+ str += FormatString<TEMP_STRING>( "\n[ %s ] used: %" PRINTF_SIZET_FORMAT "B | peak: %" PRINTF_SIZET_FORMAT "B | reserved: %" PRINTF_SIZET_FORMAT "B \n",
+ alloc->GetName(), alloc->GetAllocatedMemorySize(), alloc->GetPeakAllocatedMemorySize(), alloc->GetReservedSizeTotal());
+ }
+#endif
+}
+
+#if UNITY_WIN && ENABLE_MEM_PROFILER
+
+// this is only here to catch stray mallocs - UNITY_MALLOC should be used instead
+// use tls to mark if we are in our own malloc or not. register stray mallocs.
+int catchMemoryAllocHook(int allocType, void *userData, size_t size, int blockType, long requestNumber, const unsigned char *filename, int lineNumber)
+{
+ if(!GetMemoryProfiler())
+ return TRUE;
+
+ if (allocType == _HOOK_FREE)
+ {
+#ifdef _DEBUG
+ int headersize = sizeof(_CrtMemBlockHeader);
+ _CrtMemBlockHeader* header = (_CrtMemBlockHeader*)((size_t) userData - headersize);
+ GetMemoryProfiler()->UnregisterAllocation(NULL, NULL, header->nDataSize, NULL, kMemDefault);
+#endif
+ }
+ else
+ GetMemoryProfiler()->RegisterAllocation(NULL, MemLabelId(kMemLabelCount, NULL), NULL, 0, size);
+ return TRUE;
+}
+
+#endif
+
+
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (AtomicOpsTests)
+{
+ struct AtomicOpsFixture
+ {
+ AtomicOpsFixture()
+ {
+ }
+ ~AtomicOpsFixture()
+ {
+ }
+ };
+
+ TEST_FIXTURE(AtomicOpsFixture, AtomicExchange)
+ {
+#if UNITY_WIN || UNITY_XENON || UNITY_ANDROID
+ int i = 2;
+ CHECK_EQUAL( 2, AtomicExchange( &i, 3 ) );
+#endif // UNITY_WIN || UNITY_XENON || UNITY_ANDROID
+ }
+
+ TEST_FIXTURE(AtomicOpsFixture, AtomicCompareExchange)
+ {
+ int i = 1;
+ CHECK_EQUAL( true, AtomicCompareExchange( &i, 2, 1 ) );
+ }
+
+ TEST_FIXTURE(AtomicOpsFixture, AtomicDecrement)
+ {
+ int i = 2;
+ CHECK_EQUAL( 1, AtomicDecrement( &i ) );
+ }
+
+ TEST_FIXTURE(AtomicOpsFixture, AtomicIncrement)
+ {
+ int i = 0;
+ CHECK_EQUAL( 1, AtomicIncrement( &i ) );
+ }
+
+ TEST_FIXTURE(AtomicOpsFixture, AtomicSub)
+ {
+ int i = 2;
+ CHECK_EQUAL( 1, AtomicSub( &i, 1 ) );
+ }
+
+ TEST_FIXTURE(AtomicOpsFixture, AtomicAdd)
+ {
+ int i = 1;
+ CHECK_EQUAL( 2, AtomicAdd( &i, 1 ) );
+ }
+}
+
+#if ENABLE_MEMORY_MANAGER
+#include "Runtime/Profiler/TimeHelper.h"
+SUITE (MemoryManagerTests)
+{
+ struct MemoryManagerFixture
+ {
+ MemoryManagerFixture()
+ {
+ }
+ ~MemoryManagerFixture()
+ {
+ }
+ };
+
+
+ struct VirtualTestStructA{
+ VirtualTestStructA() {ptrA = UNITY_MALLOC(kMemDefault,1024*1024);}
+ virtual ~VirtualTestStructA() {UNITY_FREE(kMemDefault,ptrA);}
+ void* ptrA;
+ };
+ struct VirtualTestStructB : public VirtualTestStructA{
+ VirtualTestStructB() {ptrB = UNITY_MALLOC(kMemDefault,1024*1024);}
+ virtual ~VirtualTestStructB() {UNITY_FREE(kMemDefault,ptrB);}
+ void* ptrB;
+ };
+
+
+ struct TestStruct{
+ TestStruct() {ptr = UNITY_MALLOC(kMemDefault,1024*1024);}
+ ~TestStruct(){UNITY_FREE(kMemDefault,ptr);}
+ void* ptr;
+ };
+
+ TEST_FIXTURE(MemoryManagerFixture, NewDelete)
+ {
+ size_t memoryBefore = GetMemoryManager().GetAllocator(kMemDefault)->GetAllocatedMemorySize();
+
+ TestStruct* test = UNITY_NEW(TestStruct, kMemDefault);
+ // less or equal, since some platforms have larger default alignment that sizeof(void*)
+ CHECK(memoryBefore + sizeof(TestStruct) + 1024*1024 <= GetMemoryManager().GetAllocator(kMemDefault)->GetAllocatedMemorySize());
+ UNITY_DELETE (test, kMemDefault);
+
+ CHECK_EQUAL(memoryBefore,GetMemoryManager().GetAllocator(kMemDefault)->GetAllocatedMemorySize());
+
+ VirtualTestStructA* testA = (VirtualTestStructA*)UNITY_NEW(VirtualTestStructB, kMemDefault);
+ UNITY_DELETE (testA, kMemDefault);
+
+ CHECK_EQUAL(memoryBefore, GetMemoryManager().GetAllocator(kMemDefault)->GetAllocatedMemorySize());
+
+ /* This will fail the tests because of theFatalError in out of mem - but good for testing the OOM message
+ void* outofmem = UNITY_MALLOC(kMemDefault,((size_t)-1) - 1024); // has to be room for headers etc
+ CHECK_EQUAL((int)outofmem, 0);
+ CHECK_EQUAL(memoryBefore, GetMemoryManager().GetAllocator(kMemDefault)->GetAllocatedMemorySize());
+ UNITY_FREE(kMemDefault, outofmem);*/
+ }
+
+ TEST_FIXTURE(MemoryManagerFixture, CanAllocate)
+ {
+ UnityDefaultAllocator<LowLevelAllocator>* testAlloc = new UnityDefaultAllocator<LowLevelAllocator>("TestAlloc");
+ MemLabelId testAllocLabel = GetMemoryManager().AddCustomAllocator(testAlloc);
+ void* ptr = GetMemoryManager().Allocate(1024, 1, testAllocLabel);
+
+ int requestedSize = testAlloc->GetAllocatedMemorySize();
+ int overhead = testAlloc->GetAllocatorSizeTotalUsed() - requestedSize;
+ int overheadSize = testAlloc->GetOverheadSize(ptr);
+
+ CHECK_EQUAL(1024, requestedSize);
+ int page4size = 1<<(8-StaticLog2<kDefaultMemoryAlignment>::value);
+ CHECK_EQUAL(overheadSize + (128+1 + 128+1 + 32+1 + page4size+1)*sizeof(int*), overhead);
+
+ GetMemoryManager().Deallocate(ptr);
+
+ requestedSize = testAlloc->GetAllocatedMemorySize();
+ overhead = testAlloc->GetAllocatorSizeTotalUsed() - requestedSize;
+
+ CHECK_EQUAL(0, requestedSize);
+ CHECK_EQUAL(0, overhead);
+
+ GetMemoryManager().RemoveCustomAllocator(testAlloc);
+ }
+
+ TEST_FIXTURE(MemoryManagerFixture, CanAllocateAligned)
+ {
+ BaseAllocator* testAlloc = new UnityDefaultAllocator<LowLevelAllocator>("TestAlloc");
+ MemLabelId testAllocLabel = GetMemoryManager().AddCustomAllocator(testAlloc);
+ for(int i = 0; i < 100; i++)
+ {
+ int size = 1024 + ((i*20457)&1023);
+ int align = 1<<(1+((i*3)&7));
+ void* ptr = GetMemoryManager().Allocate(size, align, testAllocLabel);
+ ((int*)ptr)[0] = 0x89ABCDEF;
+ int requestedSize = testAlloc->GetAllocatedMemorySize();
+
+ CHECK_EQUAL(size, requestedSize);
+ CHECK_EQUAL(0, ((int)ptr)&(align-1));
+
+ int newsize = 1024 + ((i*236047)&1023);
+ ptr = GetMemoryManager().Reallocate(ptr, newsize, align, testAllocLabel);
+
+ requestedSize = testAlloc->GetAllocatedMemorySize();
+
+ CHECK_EQUAL(0x89ABCDEF, ((int*)ptr)[0]);
+ CHECK_EQUAL(newsize, requestedSize);
+ CHECK_EQUAL(0, ((int)ptr)&(align-1));
+
+ GetMemoryManager().Deallocate(ptr);
+
+ requestedSize = testAlloc->GetAllocatedMemorySize();
+
+ CHECK_EQUAL(0, requestedSize);
+ }
+ int requestedSize = testAlloc->GetAllocatedMemorySize();
+ int overhead = testAlloc->GetAllocatorSizeTotalUsed() - requestedSize;
+
+ CHECK_EQUAL(0, requestedSize);
+ CHECK_EQUAL(0, overhead);
+
+ GetMemoryManager().RemoveCustomAllocator(testAlloc);
+ }
+
+ TEST_FIXTURE(MemoryManagerFixture, CanTempAllocate)
+ {
+ GetMemoryManager().FrameMaintenance();
+ void* fillptr = UNITY_MALLOC(kMemTempAlloc, 128);
+ for(int i = 0; i < 1000; i++)
+ {
+ void* ptr = UNITY_MALLOC_ALIGNED(kMemTempAlloc, 128, 16);
+ UNITY_FREE(kMemTempAlloc, ptr);
+ }
+
+ void** ptrArray = (void**)UNITY_MALLOC(kMemTempAlloc,256*sizeof(void*));
+ for(int i = 0; i < 256; i++)
+ {
+ ptrArray[i] = UNITY_MALLOC_ALIGNED(kMemTempAlloc,16*1024,32);
+ }
+
+ for(int i = 0; i < 256; i++)
+ {
+ UNITY_FREE(kMemTempAlloc, ptrArray[i]);
+ }
+ UNITY_FREE(kMemTempAlloc, fillptr);
+ UNITY_FREE(kMemTempAlloc, ptrArray);
+ GetMemoryManager().FrameMaintenance();
+ }
+
+
+ #if !UNITY_64 //@TODO: seems to use TLSF which is disabled in 64 bit?
+ TEST_FIXTURE(MemoryManagerFixture, DynamicHeapReallocate)
+ {
+ DynamicHeapAllocator<LowLevelAllocator>* testAlloc = new DynamicHeapAllocator<LowLevelAllocator>(100*1024,0,true,"TestAlloc");
+ MemLabelId testAllocLabel = GetMemoryManager().AddCustomAllocator(testAlloc);
+ void* ptr = GetMemoryManager().Allocate(1024, 1, testAllocLabel);
+
+ CHECK_EQUAL(100*1024, testAlloc->GetReservedSizeTotal());
+ CHECK_EQUAL(1024, testAlloc->GetAllocatedMemorySize());
+
+ void* ptr2 = UNITY_MALLOC( testAllocLabel, 50*1024 );
+ CHECK_EQUAL(100*1024, testAlloc->GetReservedSizeTotal());
+ memset(ptr2,0x3B,50*1024);
+ void* ptr3 = UNITY_REALLOC_( testAllocLabel, ptr2, 100*1024 );
+ CHECK_EQUAL(200*1024, testAlloc->GetReservedSizeTotal());
+ for(int i = 0; i < 1024;i++)
+ CHECK_EQUAL(0x3B,((char*)ptr3)[i]);
+ for(int i = 49*1024; i < 50*1024;i++)
+ CHECK_EQUAL(0x3B,((char*)ptr3)[i]);
+ memset(ptr3,0x4C,100*1024);
+ void* ptr4 = UNITY_REALLOC_( testAllocLabel, ptr3, 101*1024 );
+ CHECK_EQUAL(201*1024, testAlloc->GetReservedSizeTotal());
+ for(int i = 0; i < 1024;i++)
+ CHECK_EQUAL(0x4C,((char*)ptr4)[i]);
+ for(int i = 99*1024; i < 100*1024;i++)
+ CHECK_EQUAL(0x4C,((char*)ptr4)[i]);
+
+ CHECK_EQUAL(1024 + 101*1024, testAlloc->GetAllocatedMemorySize());
+
+ GetMemoryManager().Deallocate(ptr4);
+ GetMemoryManager().Deallocate(ptr);
+
+ // empty pools are freed, so no memory should be left
+ CHECK_EQUAL(0, testAlloc->GetReservedSizeTotal());
+ CHECK_EQUAL(0, testAlloc->GetAllocatedMemorySize());
+
+ GetMemoryManager().RemoveCustomAllocator(testAlloc);
+ }
+ #endif
+
+ /*
+ TEST_FIXTURE(MemoryManagerFixture, TempAllocatePerformance)
+ {
+ int allocSizes[16] = {1234,345,763,34768,343,2345,45643,85335,3453,7843,2346,437,3475,23789,423743,4537};
+ GetMemoryManager().FrameMaintenance();
+ {
+ ABSOLUTE_TIME start = START_TIME;
+ for(int j = 0; j < 100000; j++)
+ {
+ void* fillptr1 = UNITY_MALLOC(kMemTempAlloc, 128);
+ void* fillptr2 = UNITY_MALLOC(kMemTempAlloc, 54);
+ void* fillptr3 = UNITY_MALLOC(kMemTempAlloc, 158);
+ for(int i = 0; i < 1000; i++)
+ {
+ void* ptr = UNITY_MALLOC_ALIGNED(kMemTempAlloc, allocSizes[i%16], 16);
+ UNITY_FREE(kMemTempAlloc, ptr);
+ }
+ UNITY_FREE(kMemTempAlloc, fillptr1);
+ UNITY_FREE(kMemTempAlloc, fillptr2);
+ UNITY_FREE(kMemTempAlloc, fillptr3);
+ }
+ printf_console("Elapsed time for 1M Temp allocs %.5fs\n", GetElapsedTimeInSeconds (start)/100.f);
+ }
+ {
+ ABSOLUTE_TIME start = START_TIME;
+ for(int j = 0; j < 1000; j++)
+ {
+ void* fillptr1 = UNITY_MALLOC(kMemDefault, 128);
+ void* fillptr2 = UNITY_MALLOC(kMemDefault, 54);
+ void* fillptr3 = UNITY_MALLOC(kMemDefault, 158);
+ for(int i = 0; i < 1000; i++)
+ {
+ void* ptr = UNITY_MALLOC_ALIGNED(kMemDefault, allocSizes[i%16], 16);
+ UNITY_FREE(kMemDefault, ptr);
+ }
+ UNITY_FREE(kMemDefault, fillptr1);
+ UNITY_FREE(kMemDefault, fillptr2);
+ UNITY_FREE(kMemDefault, fillptr3);
+ }
+ printf_console("Elapsed time for 1M DynHeap allocs %.5f s\n", GetElapsedTimeInSeconds (start));
+ }
+ {
+ ABSOLUTE_TIME start = START_TIME;
+ for(int j = 0; j < 10; j++)
+ {
+ void* fillptr1 = malloc(128);
+ void* fillptr2 = malloc(154);
+ void* fillptr3 = malloc(158);
+ for(int i = 0; i < 1000; i++)
+ {
+ void* ptr = malloc(allocSizes[i%16]);
+ free(ptr);
+ }
+ free(fillptr1);
+ free(fillptr2);
+ free(fillptr3);
+ }
+ printf_console("Elapsed time for 1M malloc/free %.5f s\n", GetElapsedTimeInSeconds (start)*100.f);
+ }
+ GetMemoryManager().FrameMaintenance();
+ }
+ */
+
+}
+#endif
+#endif
+
+
+#else // !ENABLE_MEMORY_MANAGER
+
+void register_external_gfx_allocation(void* ptr, size_t size, size_t related, const char* file, int line) { }
+void register_external_gfx_deallocation(void* ptr, const char* file, int line) { }
+
+
+void* malloc_internal(size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line)
+{
+ return UNITY_LL_ALLOC(label, size, align);
+}
+
+void* calloc_internal(size_t count, size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line)
+{
+ void* ptr = UNITY_LL_ALLOC(label, size * count, align);
+ memset (ptr, 0, size * count);
+ return ptr;
+}
+
+void* realloc_internal(void* ptr, size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line)
+{
+ return UNITY_LL_REALLOC(label, ptr, size, align);
+}
+
+void free_alloc_internal(void* ptr, MemLabelRef label)
+{
+ UNITY_LL_FREE(label, ptr);
+}
+
+#if !((UNITY_OSX || UNITY_LINUX) && UNITY_EDITOR)
+void* operator new (size_t size) THROWING_NEW_THROW { return UNITY_LL_ALLOC (kMemNewDelete, size, kDefaultMemoryAlignment); }
+void* operator new [] (size_t size) THROWING_NEW_THROW { return UNITY_LL_ALLOC (kMemNewDelete, size, kDefaultMemoryAlignment); }
+void operator delete (void* p) throw() { UNITY_LL_FREE (kMemNewDelete, p); } // can't make allocator assumption, since ptr can be newed by other operator new
+void operator delete [] (void* p) throw() {UNITY_LL_FREE (kMemNewDelete, p); }
+
+void* operator new (size_t size, const std::nothrow_t&) throw() { return UNITY_LL_ALLOC (kMemNewDelete, size, kDefaultMemoryAlignment); }
+void* operator new [] (size_t size, const std::nothrow_t&) throw() { return UNITY_LL_ALLOC (kMemNewDelete, size, kDefaultMemoryAlignment); };
+void operator delete (void* p, const std::nothrow_t&) throw() { UNITY_LL_FREE (kMemNewDelete, p); }
+void operator delete [] (void* p, const std::nothrow_t&) throw() { UNITY_LL_FREE (kMemNewDelete, p); }
+#endif
+
+#endif
+
diff --git a/Runtime/Allocator/MemoryManager.h b/Runtime/Allocator/MemoryManager.h
new file mode 100644
index 0000000..b37292c
--- /dev/null
+++ b/Runtime/Allocator/MemoryManager.h
@@ -0,0 +1,118 @@
+#ifndef _MEMORY_MANAGER_H_
+#define _MEMORY_MANAGER_H_
+
+#include "Runtime/Misc/AllocatorLabels.h"
+#include "Runtime/Allocator/BaseAllocator.h"
+#include "Runtime/Utilities/FileStripped.h"
+
+#if UNITY_XENON
+#include "PlatformDependent/Xbox360/Source/XenonMemory.h"
+#endif
+
+#if ENABLE_MEMORY_MANAGER
+
+class MemoryManager
+{
+public:
+ MemoryManager();
+ ~MemoryManager();
+ static void StaticInitialize();
+ static void StaticDestroy();
+
+ void ThreadInitialize(size_t tempSize = 0);
+ void ThreadCleanup();
+
+ bool IsInitialized() {return m_IsInitialized;}
+ bool IsActive() {return m_IsActive;}
+
+ void* Allocate(size_t size, int align, MemLabelRef label, int allocateOptions = kAllocateOptionNone, const char* file = NULL, int line = 0);
+ void* Reallocate(void* ptr, size_t size, int align, MemLabelRef label, int allocateOptions = kAllocateOptionNone, const char* file = NULL, int line = 0);
+ void Deallocate(void* ptr);
+ void Deallocate(void* ptr, MemLabelRef label);
+
+ BaseAllocator* GetAllocator(MemLabelRef label);
+ int GetAllocatorIndex(BaseAllocator* alloc);
+ BaseAllocator* GetAllocatorAtIndex( int index );
+
+ MemLabelId AddCustomAllocator(BaseAllocator* allocator);
+ void RemoveCustomAllocator(BaseAllocator* allocator);
+
+ void FrameMaintenance(bool cleanup = false);
+
+ static void* LowLevelAllocate( size_t size );
+ static void* LowLevelCAllocate( size_t count, size_t size );
+ static void* LowLevelReallocate( void* p, size_t size );
+ static void LowLevelFree( void* p );
+
+ const char* GetAllocatorName( int i );
+ const char* GetMemcatName( MemLabelRef label );
+
+ size_t GetTotalAllocatedMemory();
+ size_t GetTotalUnusedReservedMemory();
+ size_t GetTotalReservedMemory();
+
+ size_t GetTotalProfilerMemory();
+
+ int GetAllocatorCount( );
+
+ size_t GetAllocatedMemory( MemLabelRef label );
+ int GetAllocCount( MemLabelRef label );
+ size_t GetLargestAlloc(MemLabelRef label);
+
+#if UNITY_XENON
+ size_t GetRegisteredGFXDriverMemory(){ return xenon::GetGfxMemoryAllocated();}
+#else
+ size_t GetRegisteredGFXDriverMemory(){ return m_RegisteredGfxDriverMemory;}
+#endif
+
+ void StartLoggingAllocations(size_t logAllocationsThreshold = 0);
+ void StopLoggingAllocations();
+
+ void DisallowAllocationsOnThisThread();
+ void ReallowAllocationsOnThisThread();
+ void CheckDisalowAllocation();
+
+ BaseAllocator* GetAllocatorContainingPtr(const void* ptr);
+
+ static inline bool IsTempAllocatorLabel( MemLabelRef label ) { return label.label == kMemTempAllocId; }
+
+ volatile static long m_LowLevelAllocated;
+ volatile static long m_RegisteredGfxDriverMemory;
+
+private:
+#if ENABLE_MEM_PROFILER
+ void RegisterAllocation(void* ptr, size_t size, BaseAllocator* alloc, MemLabelRef label, const char* function, const char* file, int line);
+ ProfilerAllocationHeader* RegisterDeallocation(void* ptr, BaseAllocator* alloc, MemLabelRef label, const char* function);
+#endif
+ void InitializeMainThreadAllocators();
+
+ static const int kMaxAllocators = 16;
+
+ BaseAllocator* m_FrameTempAllocator;
+ BaseAllocator* m_InitialFallbackAllocator;
+
+ BaseAllocator* m_Allocators[kMaxAllocators];
+ BaseAllocator* m_MainAllocators[kMaxAllocators];
+ BaseAllocator* m_ThreadAllocators[kMaxAllocators];
+
+ int m_NumAllocators;
+ bool m_LogAllocations;
+ bool m_IsInitialized;
+ bool m_IsActive;
+
+
+ size_t m_LogAllocationsThreshold;
+
+ struct LabelInfo
+ {
+ BaseAllocator* alloc;
+ size_t allocatedMemory;
+ int numAllocs;
+ size_t largestAlloc;
+ };
+ LabelInfo m_AllocatorMap[kMemLabelCount];
+};
+MemoryManager& GetMemoryManager();
+
+#endif
+#endif
diff --git a/Runtime/Allocator/STLAllocator.h b/Runtime/Allocator/STLAllocator.h
new file mode 100644
index 0000000..e0a6dd9
--- /dev/null
+++ b/Runtime/Allocator/STLAllocator.h
@@ -0,0 +1,146 @@
+#ifndef STL_ALLOCATOR_H_
+#define STL_ALLOCATOR_H_
+
+#include "Runtime/Allocator/BaseAllocator.h"
+#include "Runtime/Misc/AllocatorLabels.h"
+
+// Use STL_ALLOCATOR macro when declaring custom std::containers
+#define STL_ALLOCATOR(label, type) stl_allocator<type, label##Id>
+#define STL_ALLOCATOR_ALIGNED(label, type, align) stl_allocator<type, label##Id, align>
+
+#if UNITY_EXTERNAL_TOOL
+#define TEMP_STRING std::string
+#else
+#define TEMP_STRING std::basic_string<char, std::char_traits<char>, STL_ALLOCATOR(kMemTempAlloc, char) >
+#endif
+
+#define UNITY_STRING(label) std::basic_string<char, std::char_traits<char>, STL_ALLOCATOR(label, char) >
+#define UNITY_WSTRING(label) std::basic_string<wchar_t, std::char_traits<wchar_t>, STL_ALLOCATOR(label, wchar_t) >
+
+#define UNITY_LIST(label, type) std::list<type, STL_ALLOCATOR(label, type) >
+
+#define UNITY_SET(label, type) std::set<type, std::less<type>, STL_ALLOCATOR(label, type) >
+
+#define UNITY_MAP(label, key, value) std::map<key, value, std::less<key>, stl_allocator< std::pair< key const, value>, label##Id > >
+
+#define UNITY_VECTOR(label, type) std::vector<type, STL_ALLOCATOR(label, type) >
+#define UNITY_VECTOR_ALIGNED(label, type, align) std::vector<type, STL_ALLOCATOR_ALIGNED(label, type, align) >
+
+#define UNITY_TEMP_VECTOR(type) std::vector<type, STL_ALLOCATOR(kMemTempAlloc, type) >
+
+
+template<typename T, MemLabelIdentifier memlabel = kMemSTLId, int align = kDefaultMemoryAlignment >
+class stl_allocator
+{
+public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T value_type;
+
+#if ENABLE_MEM_PROFILER
+ AllocationRootReference* rootref;
+ ProfilerAllocationHeader* get_root_header() const { return get_root_header_from_reference(rootref); }
+#else
+ ProfilerAllocationHeader* get_root_header () const { return NULL; }
+#endif
+ template <typename U> struct rebind { typedef stl_allocator<U, memlabel, align> other; };
+
+ stl_allocator ()
+ {
+ IF_MEMORY_PROFILER_ENABLED( rootref = get_root_reference_from_header(GET_CURRENT_ALLOC_ROOT_HEADER()) );
+ }
+ stl_allocator (const stl_allocator& alloc) throw()
+ {
+ IF_MEMORY_PROFILER_ENABLED( rootref = copy_root_reference(alloc.rootref) );
+ }
+ template <typename U, MemLabelIdentifier _memlabel, int _align> stl_allocator (const stl_allocator<U, _memlabel, _align>& alloc) throw()
+ {
+ IF_MEMORY_PROFILER_ENABLED( rootref = copy_root_reference(alloc.rootref) );
+ }
+ ~stl_allocator () throw()
+ {
+ IF_MEMORY_PROFILER_ENABLED( release_root_reference(rootref) );
+ }
+
+ pointer address (reference x) const { return &x; }
+ const_pointer address (const_reference x) const { return &x; }
+
+ pointer allocate (size_type count, void const* /*hint*/ = 0)
+ {
+ return (pointer)UNITY_MALLOC_ALIGNED( MemLabelId(memlabel, get_root_header()), count * sizeof(T), align);
+ }
+ void deallocate (pointer p, size_type /*n*/)
+ {
+ UNITY_FREE(MemLabelId(memlabel, get_root_header()), p);
+ }
+
+ template <typename U, MemLabelIdentifier _memlabel, int _align>
+ bool operator== (stl_allocator<U, _memlabel, _align> const& a) const { return _memlabel == memlabel IF_MEMORY_PROFILER_ENABLED( && get_root_header() == a.get_root_header()); }
+ template <typename U, MemLabelIdentifier _memlabel, int _align>
+ bool operator!= (stl_allocator<U, _memlabel, _align> const& a) const { return _memlabel != memlabel IF_MEMORY_PROFILER_ENABLED( || get_root_header() != a.get_root_header()); }
+
+ size_type max_size () const throw() { return 0x7fffffff; }
+
+ void construct (pointer p, const T& val) { new (p) T(val); }
+
+ void destroy (pointer p) { p->~T(); }
+};
+
+#if !UNITY_EXTERNAL_TOOL && UNITY_WIN && !WEBPLUG
+#define string mystlstring
+#define wstring mystlwstring
+#include <string>
+#undef string
+#undef wstring
+
+namespace std{
+ typedef UNITY_STRING(kMemString) string;
+ typedef UNITY_WSTRING(kMemString) wstring;
+}
+#else
+#include <string>
+#endif
+
+#define UNITY_STR_IMPL(StringName,label) \
+class StringName : public UNITY_STRING(label) \
+{ \
+public: \
+ StringName():UNITY_STRING(label)(){} \
+ StringName(const char* str):UNITY_STRING(label)(str){} \
+ StringName(const char* str, int length):UNITY_STRING(label)(str,length){} \
+ StringName(const StringName& str):UNITY_STRING(label)(str.c_str(),str.length()){} \
+ template<typename alloc> \
+ StringName(const std::basic_string<char, std::char_traits<char>, alloc >& str):UNITY_STRING(label)(str.c_str(),str.length()){} \
+ template<typename alloc> \
+ operator std::basic_string<char, std::char_traits<char>, alloc > () const \
+ { \
+ return std::basic_string<char, std::char_traits<char>, alloc >(this->c_str(), this->length()); \
+ } \
+ template<typename alloc> \
+ StringName& operator=(const std::basic_string<char, std::char_traits<char>, alloc >& rhs) \
+ { \
+ assign(rhs.c_str(), rhs.length()); \
+ return *this; \
+ } \
+ template<typename alloc> \
+ bool operator==(const std::basic_string<char, std::char_traits<char>, alloc >& rhs) const \
+ { \
+ return length() == rhs.length() && strncmp(c_str(), rhs.c_str(), length()) == 0; \
+ } \
+ template<typename alloc> \
+ bool operator!=(const std::basic_string<char, std::char_traits<char>, alloc >& rhs) const \
+ { \
+ return length() != rhs.length() || strncmp(c_str(), rhs.c_str(), length()) != 0; \
+ } \
+};
+
+UNITY_STR_IMPL(UnityStr, kMemString);
+UNITY_STR_IMPL(StaticString, kMemStaticString);
+
+#define ProfilerString UNITY_STRING(kMemMemoryProfilerString)
+
+#endif
diff --git a/Runtime/Allocator/StackAllocator.cpp b/Runtime/Allocator/StackAllocator.cpp
new file mode 100644
index 0000000..ca874c0
--- /dev/null
+++ b/Runtime/Allocator/StackAllocator.cpp
@@ -0,0 +1,213 @@
+#include "UnityPrefix.h"
+#include "StackAllocator.h"
+
+#include "Runtime/Allocator/MemoryManager.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+
+StackAllocator::StackAllocator(int blockSize, const char* name)
+: BaseAllocator(name)
+, m_Block(NULL)
+, m_LastAlloc(NULL)
+, m_BlockSize(blockSize)
+{
+ #if ENABLE_MEMORY_MANAGER
+ m_Block = (char*)MemoryManager::LowLevelAllocate(m_BlockSize);
+ #else
+ m_Block = (char*)malloc(m_BlockSize);
+ #endif
+
+ m_TotalReservedMemory = blockSize;
+}
+
+StackAllocator::~StackAllocator()
+{
+ while(m_LastAlloc)
+ Deallocate(m_LastAlloc);
+
+ #if ENABLE_MEMORY_MANAGER
+ MemoryManager::LowLevelFree(m_Block);
+ #else
+ free(m_Block);
+ #endif
+}
+
+void* StackAllocator::Allocate (size_t size, int align)
+{
+ //1 byte alignment doesn't work for webgl; this is a fix(ish)....
+
+#if UNITY_WEBGL || UNITY_BB10
+ if(align % 8 != 0)
+ align = 8;
+#endif
+
+ size_t alignmask = align - 1;
+
+ // make header size a multiple
+ int alignedHeaderSize = (GetHeaderSize() + alignmask) & ~alignmask;
+ int paddedSize = (size + alignedHeaderSize + alignmask) & ~alignmask;
+
+ char* realPtr;
+
+ char* freePtr = (char*)AlignPtr(GetBufferFreePtr(), align);
+ size_t freeSize = m_BlockSize - (int)(freePtr - m_Block);
+
+ if ( InBlock(freePtr) && paddedSize < freeSize )
+ {
+ realPtr = freePtr;
+ }
+ else
+ {
+ // Spilled over. We have to allocate the memory default alloc
+ realPtr = (char*)UNITY_MALLOC_ALIGNED(kMemTempOverflow, paddedSize, align);
+ }
+ if(realPtr == NULL)
+ return NULL;
+
+ char* ptr = realPtr + alignedHeaderSize;
+ Header* h = ( (Header*)ptr )-1;
+ h->prevPtr = m_LastAlloc;
+ h->size = size;
+ h->deleted = 0;
+ h->realPtr = realPtr;
+ m_LastAlloc = ptr;
+
+ return ptr;
+}
+
+void* StackAllocator::Reallocate (void* p, size_t size, int align)
+{
+ if (p == NULL)
+ return Allocate(size, align);
+
+ char* freePtr = (char*)AlignPtr(GetBufferFreePtr(), align);
+ size_t freeSize = m_BlockSize - (int)(freePtr - m_Block);
+ size_t oldSize = GetPtrSize(p);
+
+ if ((p == m_LastAlloc || oldSize >= size) && InBlock(p)
+ && AlignPtr(p,align) == p
+ && oldSize + freeSize > size)
+ {
+ // just expand the top allocation of the stack to the realloc amount
+ Header* h = ( (Header*)p )-1;
+ h->size = size;
+ return p;
+ }
+ void* newPtr = NULL;
+ if (!InBlock(p))
+ {
+ size_t alignmask = align - 1;
+ int alignedHeaderSize = (GetHeaderSize() + alignmask) & ~alignmask;
+ int paddedSize = (size + alignedHeaderSize + alignmask) & ~alignmask;
+
+ char* realPtr = (char*)UNITY_REALLOC_ALIGNED(kMemTempOverflow, GetRealPtr(p), paddedSize, align);
+ if(realPtr == NULL)
+ return NULL;
+
+ newPtr = realPtr + alignedHeaderSize;
+ Header* h = ( (Header*)newPtr ) - 1;
+ h->size = size;
+ h->deleted = 0;
+ h->realPtr = realPtr;
+
+ if(m_LastAlloc == p)
+ m_LastAlloc = (char*)newPtr;
+ else
+ UpdateNextHeader(p, newPtr);
+ }
+ else
+ {
+ newPtr = Allocate(size, align);
+ if(newPtr != NULL)
+ memcpy(newPtr, p, std::min(size, oldSize));
+ Deallocate(p);
+ }
+ return newPtr;
+}
+
+void StackAllocator::Deallocate (void* p)
+{
+ if (p == m_LastAlloc){
+ m_LastAlloc = GetPrevAlloc(p);
+ if ( !InBlock(p) )
+ {
+ UNITY_FREE(kMemTempOverflow, GetRealPtr(p));
+ }
+
+ if (IsDeleted(m_LastAlloc))
+ Deallocate(m_LastAlloc);
+ }
+ else
+ {
+ SetDeleted(p);
+ }
+}
+
+bool StackAllocator::ContainsInternal (const void* p)
+{
+ // if no temp allocations (should hit here most often)
+ if (m_LastAlloc == NULL)
+ return false;
+
+ // test inblock
+ if (InBlock(p))
+ return true;
+
+ // test overflow allocations (should almost never happen)
+ void* ptr = m_LastAlloc;
+ while (ptr != NULL && !InBlock(ptr))
+ {
+ if (p == ptr)
+ return true;
+ ptr = GetPrevAlloc(ptr);
+ }
+ return false;
+}
+
+void StackAllocator::UpdateNextHeader(void* before, void* after)
+{
+ if (before == m_LastAlloc)
+ return;
+ void* ptr = m_LastAlloc;
+ while (ptr != NULL && !InBlock(ptr))
+ {
+ void* prevAlloc = GetPrevAlloc(ptr);
+ if (before == prevAlloc)
+ {
+ Header* h = ((Header*)ptr)-1;
+ h->prevPtr = (char*)after;
+ return;
+ }
+ ptr = prevAlloc;
+ }
+ FatalErrorString("Allocation no found in temp allocation list");
+}
+
+
+size_t StackAllocator::GetAllocatedMemorySize() const
+{
+ int total = 0;
+ void* ptr = m_LastAlloc;
+ while (ptr != NULL)
+ {
+ total += GetPtrSize(ptr);
+ ptr = GetPrevAlloc(ptr);
+ }
+ return total;
+}
+
+size_t StackAllocator::GetAllocatorSizeTotalUsed() const
+{
+ int total = 0;
+ void* ptr = m_LastAlloc;
+ while (ptr != NULL)
+ {
+ total += GetPtrSize(ptr)+GetHeaderSize();
+ ptr = GetPrevAlloc(ptr);
+ }
+ return total;
+}
+
+size_t StackAllocator::GetReservedSizeTotal() const
+{
+ return m_TotalReservedMemory;
+}
diff --git a/Runtime/Allocator/StackAllocator.h b/Runtime/Allocator/StackAllocator.h
new file mode 100644
index 0000000..96860f5
--- /dev/null
+++ b/Runtime/Allocator/StackAllocator.h
@@ -0,0 +1,114 @@
+#ifndef STACK_ALLOCATOR_H_
+#define STACK_ALLOCATOR_H_
+
+#include "BaseAllocator.h"
+
+class StackAllocator : public BaseAllocator
+{
+public:
+ StackAllocator(int blocksize, const char* name);
+ virtual ~StackAllocator ();
+
+ virtual void* Allocate (size_t size, int align);
+ virtual void* Reallocate (void* p, size_t size, int align);
+ virtual void Deallocate (void* p);
+ virtual bool Contains (const void* p);
+
+ virtual size_t GetPtrSize(const void* ptr) const;
+
+ virtual size_t GetAllocatedMemorySize() const;
+ virtual size_t GetAllocatorSizeTotalUsed() const;
+ virtual size_t GetReservedSizeTotal() const;
+
+private:
+ struct Header{
+ int deleted:1;
+ int size:31;
+ char* prevPtr;
+ void* realPtr;
+ };
+ char* m_Block;
+ int m_BlockSize;
+
+ char* m_LastAlloc;
+
+ //ThreadID owningThread;
+ bool InBlock ( const void* ptr ) const;
+ bool IsDeleted ( const void* ptr ) const;
+ void SetDeleted ( const void* ptr );
+ char* GetPrevAlloc ( const void* ptr ) const;
+ void* GetRealPtr( void* ptr ) const;
+ char* GetBufferFreePtr() const;
+
+ bool ContainsInternal (const void* p);
+
+ UInt32 GetHeaderSize() const;
+
+ void UpdateNextHeader(void* before, void* after);
+};
+
+inline bool StackAllocator::Contains (const void* p)
+{
+ // most common case. pointer being queried is the one about to be destroyed
+ if(p != NULL && p == m_LastAlloc)
+ return true;
+
+ return ContainsInternal(p);
+}
+
+inline char* StackAllocator::GetBufferFreePtr() const
+{
+ if (m_LastAlloc == NULL)
+ return m_Block;
+ Header* h = ((Header*)m_LastAlloc)-1;
+ return m_LastAlloc + h->size;
+}
+
+inline size_t StackAllocator::GetPtrSize( const void* ptr ) const
+{
+ Header* header = ( (Header*)ptr )-1;
+ return header->size;
+}
+
+inline void* StackAllocator::GetRealPtr( void* ptr ) const
+{
+ Header* header = ( (Header*)ptr )-1;
+ return header->realPtr;
+}
+
+inline char* StackAllocator::GetPrevAlloc(const void* ptr ) const
+{
+ if (ptr == NULL)
+ return NULL;
+ Header* h = ((Header*)ptr)-1;
+ return h->prevPtr;
+}
+
+inline UInt32 StackAllocator::GetHeaderSize() const
+{
+ return sizeof(Header);
+}
+
+inline bool StackAllocator::InBlock(const void* ptr) const
+{
+ return ptr >= m_Block && ptr < (m_Block + m_BlockSize);
+}
+
+inline bool StackAllocator::IsDeleted(const void* ptr ) const
+{
+ if (ptr == NULL)
+ return false;
+ Header* h = ((Header*)ptr)-1;
+ return h->deleted != 0;
+}
+
+inline void StackAllocator::SetDeleted(const void* ptr )
+{
+ if (ptr == NULL)
+ return;
+ Header* h = ((Header*)ptr)-1;
+ h->deleted = 1;
+}
+
+
+#endif
diff --git a/Runtime/Allocator/TLSAllocator.cpp b/Runtime/Allocator/TLSAllocator.cpp
new file mode 100644
index 0000000..00bf88b
--- /dev/null
+++ b/Runtime/Allocator/TLSAllocator.cpp
@@ -0,0 +1,198 @@
+#include "UnityPrefix.h"
+#include "TLSAllocator.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Allocator/StackAllocator.h"
+
+#if ENABLE_MEMORY_MANAGER
+
+template <class UnderlyingAllocator>
+int TLSAllocator<UnderlyingAllocator>::s_NumberOfInstances = 0;
+
+template <class UnderlyingAllocator>
+UNITY_TLS_VALUE(UnderlyingAllocator*) TLSAllocator<UnderlyingAllocator>::m_UniqueThreadAllocator;
+
+template <class UnderlyingAllocator>
+TLSAllocator<UnderlyingAllocator>::TLSAllocator(const char* name)
+: BaseAllocator(name)
+{
+ if(s_NumberOfInstances != 0)
+ ErrorString("Only one instance of the TLS allocator is allowed because of TLS implementation");
+ s_NumberOfInstances++;
+ memset (m_ThreadTempAllocators, 0, sizeof(m_ThreadTempAllocators));
+}
+
+template <class UnderlyingAllocator>
+TLSAllocator<UnderlyingAllocator>::~TLSAllocator()
+{
+ s_NumberOfInstances--;
+}
+
+template <class UnderlyingAllocator>
+void TLSAllocator<UnderlyingAllocator>::ThreadInitialize(BaseAllocator *allocator)
+{
+ m_UniqueThreadAllocator = (UnderlyingAllocator*)allocator;
+
+ for(int i = 0; i < kMaxThreadTempAllocators; i++)
+ {
+ if(m_ThreadTempAllocators[i] == NULL)
+ {
+ m_ThreadTempAllocators[i] = (UnderlyingAllocator*) allocator;
+ break;
+ }
+ }
+
+}
+
+template <class UnderlyingAllocator>
+void TLSAllocator<UnderlyingAllocator>::ThreadCleanup()
+{
+ UnderlyingAllocator* allocator = m_UniqueThreadAllocator;
+ m_UniqueThreadAllocator = NULL;
+
+ for(int i = 0; i < kMaxThreadTempAllocators; i++)
+ {
+ if(m_ThreadTempAllocators[i] == allocator)
+ {
+ m_ThreadTempAllocators[i] = NULL;
+ break;
+ }
+ }
+ UNITY_DELETE(allocator, kMemManager);
+}
+
+template <class UnderlyingAllocator>
+void TLSAllocator<UnderlyingAllocator>::FrameMaintenance(bool cleanup)
+{
+ Assert(m_UniqueThreadAllocator->GetAllocatedMemorySize() == 0);
+}
+
+template <class UnderlyingAllocator>
+bool TLSAllocator<UnderlyingAllocator>::IsAssigned() const
+{
+ return m_UniqueThreadAllocator != NULL;
+}
+
+template <class UnderlyingAllocator>
+UnderlyingAllocator* TLSAllocator<UnderlyingAllocator>::GetCurrentAllocator()
+{
+ return m_UniqueThreadAllocator;
+}
+
+
+template <class UnderlyingAllocator>
+void* TLSAllocator<UnderlyingAllocator>::Allocate( size_t size, int align )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+ return alloc ? alloc->UnderlyingAllocator::Allocate(size, align) : NULL;
+}
+
+template <class UnderlyingAllocator>
+void* TLSAllocator<UnderlyingAllocator>::Reallocate( void* p, size_t size, int align )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+ if(!alloc)
+ return NULL;
+ if(alloc->UnderlyingAllocator::Contains(p))
+ return alloc->UnderlyingAllocator::Reallocate(p, size, align);
+
+ return NULL;
+}
+
+template <class UnderlyingAllocator>
+void TLSAllocator<UnderlyingAllocator>::Deallocate( void* p )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+ DebugAssert(alloc);
+ DebugAssert(alloc->UnderlyingAllocator::Contains(p));
+ return alloc->UnderlyingAllocator::Deallocate(p);
+}
+
+template <class UnderlyingAllocator>
+bool TLSAllocator<UnderlyingAllocator>::TryDeallocate( void* p )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+ if(!alloc)
+ return false;
+
+ if(!alloc->UnderlyingAllocator::Contains(p))
+ return false;
+
+ alloc->UnderlyingAllocator::Deallocate(p);
+ return true;
+}
+
+
+template <class UnderlyingAllocator>
+bool TLSAllocator<UnderlyingAllocator>::Contains( const void* p )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+ if(alloc && alloc->UnderlyingAllocator::Contains(p))
+ return true;
+ return false;
+}
+
+template <class UnderlyingAllocator>
+size_t TLSAllocator<UnderlyingAllocator>::GetAllocatedMemorySize( ) const
+{
+ size_t allocated = 0;
+ for(int i = 0; i < kMaxThreadTempAllocators; i++)
+ {
+ if(m_ThreadTempAllocators[i] != NULL)
+ allocated += m_ThreadTempAllocators[i]->UnderlyingAllocator::GetAllocatedMemorySize();
+ }
+ return allocated;
+}
+
+template <class UnderlyingAllocator>
+size_t TLSAllocator<UnderlyingAllocator>::GetAllocatorSizeTotalUsed() const
+{
+ size_t total = 0;
+ for(int i = 0; i < kMaxThreadTempAllocators; i++)
+ {
+ if(m_ThreadTempAllocators[i] != NULL)
+ total += m_ThreadTempAllocators[i]->UnderlyingAllocator::GetAllocatorSizeTotalUsed();
+ }
+ return total;
+}
+
+template <class UnderlyingAllocator>
+size_t TLSAllocator<UnderlyingAllocator>::GetReservedSizeTotal() const
+{
+ size_t total = 0;
+ for(int i = 0; i < kMaxThreadTempAllocators; i++)
+ {
+ if(m_ThreadTempAllocators[i] != NULL)
+ total += m_ThreadTempAllocators[i]->UnderlyingAllocator::GetReservedSizeTotal();
+ }
+ return total;
+}
+
+template <class UnderlyingAllocator>
+size_t TLSAllocator<UnderlyingAllocator>::GetPtrSize( const void* ptr ) const
+{
+ // all allocators have the same allocation header
+ return m_ThreadTempAllocators[0]->UnderlyingAllocator::GetPtrSize(ptr);
+}
+
+template <class UnderlyingAllocator>
+ProfilerAllocationHeader* TLSAllocator<UnderlyingAllocator>::GetProfilerHeader( const void* ptr ) const
+{
+ return m_ThreadTempAllocators[0]->UnderlyingAllocator::GetProfilerHeader(ptr);
+}
+
+
+template <class UnderlyingAllocator>
+bool TLSAllocator<UnderlyingAllocator>::CheckIntegrity()
+{
+ bool succes = true;
+ for(int i = 0; i < kMaxThreadTempAllocators; i++)
+ {
+ if(m_ThreadTempAllocators[i] != NULL)
+ succes &= m_ThreadTempAllocators[i]->UnderlyingAllocator::CheckIntegrity();
+ }
+ return succes;
+}
+
+template class TLSAllocator< StackAllocator >;
+
+#endif // #if ENABLE_MEMORY_MANAGER
diff --git a/Runtime/Allocator/TLSAllocator.h b/Runtime/Allocator/TLSAllocator.h
new file mode 100644
index 0000000..a7520ff
--- /dev/null
+++ b/Runtime/Allocator/TLSAllocator.h
@@ -0,0 +1,55 @@
+#ifndef TLS_ALLOCATOR_H_
+#define TLS_ALLOCATOR_H_
+
+#if ENABLE_MEMORY_MANAGER
+
+#include "Runtime/Allocator/BaseAllocator.h"
+#include "Runtime/Threads/ThreadSpecificValue.h"
+
+
+// TLS Allocator is an indirection to a real allocator
+// Has a tls value pointing to the threadspecific allocator if unique per thread.
+
+template <class UnderlyingAllocator>
+class TLSAllocator : public BaseAllocator
+{
+public:
+ // when constructing it will be from the main thread
+ TLSAllocator(const char* name);
+ virtual ~TLSAllocator();
+
+ virtual void* Allocate(size_t size, int align);
+ virtual void* Reallocate (void* p, size_t size, int align);
+ virtual void Deallocate (void* p);
+ virtual bool Contains (const void* p);
+
+ virtual size_t GetAllocatedMemorySize() const;
+ virtual size_t GetAllocatorSizeTotalUsed() const;
+ virtual size_t GetReservedSizeTotal() const;
+
+ virtual size_t GetPtrSize(const void* ptr) const;
+ virtual ProfilerAllocationHeader* GetProfilerHeader(const void* ptr) const;
+
+ virtual void ThreadInitialize(BaseAllocator* allocator);
+ virtual void ThreadCleanup();
+
+ virtual bool CheckIntegrity();
+
+ virtual bool IsAssigned() const;
+ bool TryDeallocate (void* p);
+
+ UnderlyingAllocator* GetCurrentAllocator();
+ virtual void FrameMaintenance(bool cleanup);
+
+private:
+ // because TLS values have to be static on some platforms, this is made static
+ // and only one instance of the TLS is allowed
+ static UNITY_TLS_VALUE(UnderlyingAllocator*) m_UniqueThreadAllocator; // the memorymanager holds the list of allocators
+ static int s_NumberOfInstances;
+
+ static const int kMaxThreadTempAllocators = 128;
+ UnderlyingAllocator* m_ThreadTempAllocators[kMaxThreadTempAllocators];
+};
+
+#endif
+#endif
diff --git a/Runtime/Allocator/UnityDefaultAllocator.cpp b/Runtime/Allocator/UnityDefaultAllocator.cpp
new file mode 100644
index 0000000..a2ec30c
--- /dev/null
+++ b/Runtime/Allocator/UnityDefaultAllocator.cpp
@@ -0,0 +1,284 @@
+#include "UnityPrefix.h"
+#include "UnityDefaultAllocator.h"
+
+#if ENABLE_MEMORY_MANAGER
+
+#include "Runtime/Allocator/AllocationHeader.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Allocator/MemoryManager.h"
+
+template<class LLAlloctor>
+UnityDefaultAllocator<LLAlloctor>::UnityDefaultAllocator(const char* name)
+: BaseAllocator(name)
+{
+ memset(m_PageAllocationList,0,sizeof(m_PageAllocationList));
+}
+
+template<class LLAlloctor>
+void* UnityDefaultAllocator<LLAlloctor>::Allocate (size_t size, int align)
+{
+ size_t realSize = AllocationHeader::CalculateNeededAllocationSize(size, align);
+ void* rawPtr = LLAlloctor::Malloc( realSize );
+ if(rawPtr == NULL)
+ return NULL;
+
+ void* realPtr = AddHeaderAndFooter(rawPtr, size, align);
+ RegisterAllocation(realPtr);
+
+ return realPtr;
+}
+
+template<class LLAlloctor>
+void* UnityDefaultAllocator<LLAlloctor>::Reallocate( void* p, size_t size, int align)
+{
+ if (p == NULL)
+ return Allocate(size, align);
+
+ AllocationHeader::ValidateIntegrity(p, m_AllocatorIdentifier, align);
+ RegisterDeallocation(p);
+
+ size_t oldSize = GetPtrSize(p);
+ size_t oldPadCount = AllocationHeader::GetHeader(p)->GetPadding();
+
+ void* realPtr = AllocationHeader::GetRealPointer(p);
+ size_t realSize = AllocationHeader::CalculateNeededAllocationSize(size, align);
+ char* rawPtr = (char*)LLAlloctor::Realloc(realPtr, realSize);
+ if(rawPtr == NULL)
+ return NULL;
+
+ int newPadCount = AllocationHeader::GetRequiredPadding(rawPtr, align);
+
+ if (newPadCount != oldPadCount){
+ // new ptr needs different align padding. move memory and repad
+ char* srcptr = rawPtr + AllocationHeader::GetHeaderSize() + oldPadCount;
+ char* dstptr = rawPtr + AllocationHeader::GetHeaderSize() + newPadCount;
+ memmove(dstptr, srcptr, ( oldSize < size ? oldSize : size ) );
+ }
+
+ void* newptr = AddHeaderAndFooter(rawPtr, size, align);
+ RegisterAllocation(newptr);
+
+ return newptr;
+}
+
+template<class LLAlloctor>
+void UnityDefaultAllocator<LLAlloctor>::Deallocate (void* p)
+{
+ if (p == NULL)
+ return;
+
+ AllocationHeader::ValidateIntegrity(p, m_AllocatorIdentifier);
+ RegisterDeallocation(p);
+
+ void* realpointer = AllocationHeader::GetRealPointer(p);
+
+ LLAlloctor::Free(realpointer);
+}
+
+template<class LLAlloctor>
+template<RequestType requestType>
+bool UnityDefaultAllocator<LLAlloctor>::AllocationPage(const void* p){
+
+ // A memory and performance optimization could be to register lone pointers in the array instead of setting the bit.
+ // when multiple pointers arrive, pull the pointer out, register it, and register the next. Requires some bookkeeping.
+ // bottom 2 bits of the pointer can be used for flags.
+ int pageAllocationListIndex = 0;
+ UInt32 val = (UInt32)p;
+
+ if(sizeof(void*) > sizeof(UInt32))
+ {
+ Assert(sizeof(void*) == sizeof(UInt64));
+ UInt32 highbits = (UInt32)((UInt64)(uintptr_t)(p) >> 32);
+ if(highbits != 0)
+ {
+ pageAllocationListIndex = -1;
+ for(int i = 0; i < kNumPageAllocationBlocks; i++)
+ if(m_PageAllocationList[i].m_HighBits == highbits)
+ pageAllocationListIndex = i;
+
+ if(pageAllocationListIndex == -1)
+ {
+ // highbits not found in the list. find a free list element
+ for(int i = 0; i < kNumPageAllocationBlocks; i++)
+ {
+ if(m_PageAllocationList[i].m_PageAllocations == NULL)
+ {
+ m_PageAllocationList[i].m_HighBits = highbits;
+ pageAllocationListIndex = i;
+ break;
+ }
+ }
+ if(requestType == kTest)
+ {
+ return false;
+ }
+ else
+ {
+ if(pageAllocationListIndex == -1)
+ ErrorString("Using memoryadresses from more that 16GB of memory");
+ }
+ }
+ }
+ }
+
+ int ****& pageAllocations = m_PageAllocationList[pageAllocationListIndex].m_PageAllocations;
+ int page1 = (val >> (32-kPage1Bits)) & ((1<<kPage1Bits)-1);
+ int page2 = (val >> (32-kPage1Bits-kPage2Bits)) & ((1<<kPage2Bits)-1);
+ int page3 = (val >> (32-kPage1Bits-kPage2Bits-kPage3Bits)) & ((1<<kPage3Bits)-1);
+ int page4 = (val >> (32-kPage1Bits-kPage2Bits-kPage3Bits-kPage4Bits)) & ((1<<kPage4Bits)-1);
+ int bitindex = (val >> kTargetBitsRepresentedPerBit) & 0x1F;
+
+ if(requestType == kUnregister){
+ Assert(pageAllocations != NULL);
+ Assert(pageAllocations[page1] != NULL);
+ Assert(pageAllocations[page1][page2] != NULL);
+ Assert(pageAllocations[page1][page2][page3] != NULL);
+
+ pageAllocations[page1][page2][page3][page4] &= ~(1<<bitindex);
+ if(--pageAllocations[page1][page2][page3][(1<<kPage4Bits)] == 0)
+ {
+ m_BookKeepingMemoryUsage -= ((1<<kPage4Bits)+1)*sizeof(int*);
+ MemoryManager::LowLevelFree (pageAllocations[page1][page2][page3]);
+ pageAllocations[page1][page2][page3] = NULL;
+ }
+ if(--pageAllocations[page1][page2][(1<<kPage3Bits)] == 0)
+ {
+ m_BookKeepingMemoryUsage -= ((1<<kPage3Bits)+1)*sizeof(int**);
+ MemoryManager::LowLevelFree (pageAllocations[page1][page2]);
+ pageAllocations[page1][page2] = NULL;
+ }
+ if(--pageAllocations[page1][(1<<kPage2Bits)] == 0)
+ {
+ m_BookKeepingMemoryUsage -= ((1<<kPage2Bits)+1)*sizeof(int***);
+ MemoryManager::LowLevelFree (pageAllocations[page1]);
+ pageAllocations[page1] = NULL;
+ }
+ if(--pageAllocations[(1<<kPage1Bits)] == 0)
+ {
+ m_BookKeepingMemoryUsage -= ((1<<kPage1Bits)+1)*sizeof(int***);
+ MemoryManager::LowLevelFree (pageAllocations);
+ pageAllocations = NULL;
+ }
+ return true;
+ }
+
+ if(pageAllocations == NULL)
+ {
+ if(requestType == kRegister)
+ {
+ pageAllocations = (int****)MemoryManager::LowLevelCAllocate((1<<kPage1Bits)+1,sizeof(int****));
+ m_BookKeepingMemoryUsage += ((1<<kPage1Bits)+1)*sizeof(int****);
+ pageAllocations[(1<<kPage1Bits)] = 0;
+ }
+ else
+ return false;
+ }
+ if(pageAllocations[page1] == NULL)
+ {
+ if(requestType == kRegister)
+ {
+ pageAllocations[page1] = (int***)MemoryManager::LowLevelCAllocate((1<<kPage2Bits)+1,sizeof(int***));
+ m_BookKeepingMemoryUsage += ((1<<kPage2Bits)+1)*sizeof(int***);
+ pageAllocations[page1][(1<<kPage2Bits)] = 0;
+ }
+ else
+ return false;
+ }
+ if(pageAllocations[page1][page2] == NULL)
+ {
+ if(requestType == kRegister)
+ {
+ pageAllocations[page1][page2] = (int**)MemoryManager::LowLevelCAllocate((1<<kPage3Bits)+1,sizeof(int**));
+ m_BookKeepingMemoryUsage += ((1<<kPage3Bits)+1)*sizeof(int**);
+ pageAllocations[page1][page2][(1<<kPage3Bits)] = 0;
+ }
+ else
+ return false;
+ }
+ if(pageAllocations[page1][page2][page3] == NULL)
+ {
+ if(requestType == kRegister)
+ {
+ pageAllocations[page1][page2][page3] = (int*)MemoryManager::LowLevelCAllocate((1<<kPage4Bits)+1,sizeof(int*));
+ m_BookKeepingMemoryUsage += ((1<<kPage4Bits)+1)*sizeof(int*);
+ pageAllocations[page1][page2][page3][(1<<kPage4Bits)] = 0;
+ }
+ else
+ return false;
+ }
+ if(requestType == kTest)
+ return (pageAllocations[page1][page2][page3][page4] & (1<<bitindex)) != 0;
+ pageAllocations[page1][page2][page3][(1<<kPage4Bits)]++;
+ pageAllocations[page1][page2][(1<<kPage3Bits)]++;
+ pageAllocations[page1][(1<<kPage2Bits)]++;
+ pageAllocations[(1<<kPage1Bits)]++;
+ Assert((pageAllocations[page1][page2][page3][page4] & (1<<bitindex)) == 0); // the bit for this pointer should not be set yet
+ pageAllocations[page1][page2][page3][page4] |= (1<<bitindex);
+ return true;
+
+}
+
+template<class LLAlloctor>
+void* UnityDefaultAllocator<LLAlloctor>::AddHeaderAndFooter( void* ptr, size_t size, int align ) const
+{
+ Assert(align >= kDefaultMemoryAlignment && align <= 16*1024 && IsPowerOfTwo(align));
+ // calculate required padding for ptr to be aligned after header addition
+ // ppppppppHHHH***********
+ int padCount = AllocationHeader::GetRequiredPadding(ptr, align);
+ void* realPtr = ((char*)ptr) + (padCount + AllocationHeader::GetHeaderSize());
+ AllocationHeader::Set(realPtr, m_AllocatorIdentifier, size, padCount, align);
+ return realPtr;
+}
+
+template<class LLAlloctor>
+void UnityDefaultAllocator<LLAlloctor>::RegisterAllocation( const void* p )
+{
+ Mutex::AutoLock lock(m_AllocLock);
+ const size_t ptrSize = GetPtrSize(p);
+ const int overheadSize = AllocationHeader::GetHeader(p)->GetOverheadSize();
+ RegisterAllocationData(ptrSize, overheadSize);
+ m_TotalReservedMemory += ptrSize + overheadSize;
+ AllocationPage<kRegister>(p);
+}
+
+template<class LLAlloctor>
+void UnityDefaultAllocator<LLAlloctor>::RegisterDeallocation( const void* p )
+{
+ Mutex::AutoLock lock(m_AllocLock);
+ const size_t ptrSize = GetPtrSize(p);
+ const int overheadSize = AllocationHeader::GetHeader(p)->GetOverheadSize();
+ RegisterDeallocationData(ptrSize, overheadSize);
+ m_TotalReservedMemory -= ptrSize + overheadSize;
+ AllocationPage<kUnregister>(p);
+}
+
+template<class LLAlloctor>
+bool UnityDefaultAllocator<LLAlloctor>::Contains (const void* p)
+{
+ Mutex::AutoLock lock(m_AllocLock);
+ return AllocationPage<kTest>(p);
+}
+
+template<class LLAlloctor>
+size_t UnityDefaultAllocator<LLAlloctor>::GetPtrSize( const void* ptr ) const
+{
+ return AllocationHeader::GetHeader(ptr)->GetRequestedSize();
+}
+
+template<class LLAlloctor>
+ProfilerAllocationHeader* UnityDefaultAllocator<LLAlloctor>::GetProfilerHeader(const void* ptr) const
+{
+ // LocalHeader:ProfilerHeader:Data
+ return AllocationHeader::GetProfilerHeader(ptr);
+}
+
+template<class LLAlloctor>
+int UnityDefaultAllocator<LLAlloctor>::GetOverheadSize(void* ptr)
+{
+ return AllocationHeader::GetHeader(ptr)->GetOverheadSize();
+}
+
+template class UnityDefaultAllocator<LowLevelAllocator>;
+
+#endif
diff --git a/Runtime/Allocator/UnityDefaultAllocator.h b/Runtime/Allocator/UnityDefaultAllocator.h
new file mode 100644
index 0000000..b26ce6b
--- /dev/null
+++ b/Runtime/Allocator/UnityDefaultAllocator.h
@@ -0,0 +1,70 @@
+#ifndef UNITY_DEFAULT_ALLOCATOR_H_
+#define UNITY_DEFAULT_ALLOCATOR_H_
+
+#if ENABLE_MEMORY_MANAGER
+
+#include "BaseAllocator.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Allocator/LowLevelDefaultAllocator.h"
+#include "Runtime/Utilities/BitUtility.h"
+
+enum RequestType
+{
+ kRegister,
+ kUnregister,
+ kTest
+};
+
+template<class LLAlloctor>
+class UnityDefaultAllocator : public BaseAllocator
+{
+public:
+ UnityDefaultAllocator(const char* name);
+
+ virtual void* Allocate (size_t size, int align);
+ virtual void* Reallocate (void* p, size_t size, int align);
+ virtual void Deallocate (void* p);
+ virtual bool Contains (const void* p);
+
+ virtual size_t GetPtrSize(const void* ptr) const;
+
+ virtual ProfilerAllocationHeader* GetProfilerHeader(const void* ptr) const;
+
+ static int GetOverheadSize(void* ptr);
+private:
+ // needs 30 bit (4byte aligned allocs and packed as bitarray) ( 1 byte -> 32Bytes, 4bytes rep 128Bytes(7bit))
+ enum
+ {
+ kTargetBitsRepresentedPerBit = StaticLog2<kDefaultMemoryAlignment>::value,
+ kTargetBitsRepresentedPerByte = 5 + kTargetBitsRepresentedPerBit,
+ kPage1Bits = 7, // 128*4Bytes = 512Bytes (page: 4GB/128 = 32MB per pointer)
+ kPage2Bits = 7, // 128*4Bytes = 512Bytes (page: 32MB/128 = 256KB per pointer)
+ kPage3Bits = 5, // 32*4Bytes = 128Bytes (page: 256K/32 = 8K per pointer)
+ kPage4Bits = 32-kPage1Bits-kPage2Bits-kPage3Bits-kTargetBitsRepresentedPerByte
+ };
+
+ struct PageAllocationElement
+ {
+ PageAllocationElement() : m_HighBits(0), m_PageAllocations(NULL){}
+ UInt32 m_HighBits;
+ int**** m_PageAllocations;
+ };
+
+ static const int kNumPageAllocationBlocks = 5; //each block represents 4gb of memory;
+ PageAllocationElement m_PageAllocationList[kNumPageAllocationBlocks];
+
+ template<RequestType requestType>
+ bool AllocationPage(const void* p);
+
+ Mutex m_AllocLock;
+
+ void RegisterAllocation(const void* p);
+ void RegisterDeallocation(const void* p);
+
+ void* AddHeaderAndFooter( void* ptr, size_t size, int align ) const;
+};
+
+#endif
+
+#endif
+
diff --git a/Runtime/Allocator/tlsf/tlsf.c b/Runtime/Allocator/tlsf/tlsf.c
new file mode 100644
index 0000000..9c3f32b
--- /dev/null
+++ b/Runtime/Allocator/tlsf/tlsf.c
@@ -0,0 +1,966 @@
+#include <assert.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tlsf.h"
+#include "tlsfbits.h"
+
+/*
+** Constants.
+*/
+
+/* Public constants: may be modified. */
+enum tlsf_public
+{
+ /* log2 of number of linear subdivisions of block sizes. */
+ SL_INDEX_COUNT_LOG2 = 5,
+};
+
+/* Private constants: do not modify. */
+enum tlsf_private
+{
+#if defined (TLSF_64BIT)
+ /* All allocation sizes and addresses are aligned to 8 bytes. */
+ ALIGN_SIZE_LOG2 = 3,
+#else
+ /* All allocation sizes and addresses are aligned to 4 bytes. */
+ ALIGN_SIZE_LOG2 = 2,
+#endif
+ ALIGN_SIZE = (1 << ALIGN_SIZE_LOG2),
+
+ /*
+ ** We support allocations of sizes up to (1 << FL_INDEX_MAX) bits.
+ ** However, because we linearly subdivide the second-level lists, and
+ ** our minimum size granularity is 4 bytes, it doesn't make sense to
+ ** create first-level lists for sizes smaller than SL_INDEX_COUNT * 4,
+ ** or (1 << (SL_INDEX_COUNT_LOG2 + 2)) bytes, as there we will be
+ ** trying to split size ranges into more slots than we have available.
+ ** Instead, we calculate the minimum threshold size, and place all
+ ** blocks below that size into the 0th first-level list.
+ */
+
+#if defined (TLSF_64BIT)
+ /*
+ ** TODO: We can increase this to support larger sizes, at the expense
+ ** of more overhead in the TLSF structure.
+ */
+ FL_INDEX_MAX = 32,
+#else
+ FL_INDEX_MAX = 30,
+#endif
+ SL_INDEX_COUNT = (1 << SL_INDEX_COUNT_LOG2),
+ FL_INDEX_SHIFT = (SL_INDEX_COUNT_LOG2 + ALIGN_SIZE_LOG2),
+ FL_INDEX_COUNT = (FL_INDEX_MAX - FL_INDEX_SHIFT + 1),
+
+ SMALL_BLOCK_SIZE = (1 << FL_INDEX_SHIFT),
+};
+
+/*
+** Cast and min/max macros.
+*/
+
+#define tlsf_cast(t, exp) ((t) (exp))
+#define tlsf_min(a, b) ((a) < (b) ? (a) : (b))
+#define tlsf_max(a, b) ((a) > (b) ? (a) : (b))
+
+/*
+** Set assert macro, if it has not been provided by the user.
+*/
+#if !defined (tlsf_assert)
+#if defined(__flash__)
+//extern void tlsf_assert(int condition);
+#define tlsf_assert
+#else
+#define tlsf_assert assert
+#endif
+#endif
+
+/*
+** Static assertion mechanism.
+*/
+
+#define _tlsf_glue2(x, y) x ## y
+#define _tlsf_glue(x, y) _tlsf_glue2(x, y)
+#define tlsf_static_assert(exp) \
+ typedef char _tlsf_glue(static_assert, __LINE__) [(exp) ? 1 : -1]
+
+/* This code has been tested on 32- and 64-bit (LP/LLP) architectures. */
+tlsf_static_assert(sizeof(int) * CHAR_BIT == 32);
+tlsf_static_assert(sizeof(size_t) * CHAR_BIT >= 32);
+tlsf_static_assert(sizeof(size_t) * CHAR_BIT <= 64);
+
+/* SL_INDEX_COUNT must be <= number of bits in sl_bitmap's storage type. */
+tlsf_static_assert(sizeof(unsigned int) * CHAR_BIT >= SL_INDEX_COUNT);
+
+/* Ensure we've properly tuned our sizes. */
+tlsf_static_assert(ALIGN_SIZE == SMALL_BLOCK_SIZE / SL_INDEX_COUNT);
+
+/*
+** Data structures and associated constants.
+*/
+
+/*
+** Block header structure.
+**
+** There are several implementation subtleties involved:
+** - The prev_phys_block field is only valid if the previous block is free.
+** - The prev_phys_block field is actually stored at the end of the
+** previous block. It appears at the beginning of this structure only to
+** simplify the implementation.
+** - The next_free / prev_free fields are only valid if the block is free.
+*/
+typedef struct block_header_t
+{
+ /* Points to the previous physical block. */
+ struct block_header_t* prev_phys_block;
+
+ /* The size of this block, excluding the block header. */
+ size_t size;
+
+ /* Next and previous free blocks. */
+ struct block_header_t* next_free;
+ struct block_header_t* prev_free;
+} block_header_t;
+
+/*
+** Since block sizes are always at least a multiple of 4, the two least
+** significant bits of the size field are used to store the block status:
+** - bit 0: whether block is busy or free
+** - bit 1: whether previous block is busy or free
+*/
+static const size_t block_header_free_bit = 1 << 0;
+static const size_t block_header_prev_free_bit = 1 << 1;
+
+/*
+** The size of the block header exposed to used blocks is the size field.
+** The prev_phys_block field is stored *inside* the previous free block.
+*/
+static const size_t block_header_overhead = sizeof(size_t);
+
+/* User data starts directly after the size field in a used block. */
+static const size_t block_start_offset =
+ offsetof(block_header_t, size) + sizeof(size_t);
+
+/*
+** A free block must be large enough to store its header minus the size of
+** the prev_phys_block field, and no larger than the number of addressable
+** bits for FL_INDEX.
+*/
+static const size_t block_size_min =
+ sizeof(block_header_t) - sizeof(block_header_t*);
+static const size_t block_size_max = tlsf_cast(size_t, 1) << FL_INDEX_MAX;
+
+
+/* The TLSF pool structure. */
+typedef struct pool_t
+{
+ /* Empty lists point at this block to indicate they are free. */
+ block_header_t block_null;
+
+ /* Bitmaps for free lists. */
+ unsigned int fl_bitmap;
+ unsigned int sl_bitmap[FL_INDEX_COUNT];
+
+ /* Head of free lists. */
+ block_header_t* blocks[FL_INDEX_COUNT][SL_INDEX_COUNT];
+} pool_t;
+
+/* A type used for casting when doing pointer arithmetic. */
+typedef ptrdiff_t tlsfptr_t;
+
+/*
+** block_header_t member functions.
+*/
+
+static size_t block_size(const block_header_t* block)
+{
+ return block->size & ~(block_header_free_bit | block_header_prev_free_bit);
+}
+
+static void block_set_size(block_header_t* block, size_t size)
+{
+ const size_t oldsize = block->size;
+ block->size = size | (oldsize & (block_header_free_bit | block_header_prev_free_bit));
+}
+
+static int block_is_last(const block_header_t* block)
+{
+ return 0 == block_size(block);
+}
+
+static int block_is_free(const block_header_t* block)
+{
+ return tlsf_cast(int, block->size & block_header_free_bit);
+}
+
+static void block_set_free(block_header_t* block)
+{
+ block->size |= block_header_free_bit;
+}
+
+static void block_set_used(block_header_t* block)
+{
+ block->size &= ~block_header_free_bit;
+}
+
+static int block_is_prev_free(const block_header_t* block)
+{
+ return tlsf_cast(int, block->size & block_header_prev_free_bit);
+}
+
+static void block_set_prev_free(block_header_t* block)
+{
+ block->size |= block_header_prev_free_bit;
+}
+
+static void block_set_prev_used(block_header_t* block)
+{
+ block->size &= ~block_header_prev_free_bit;
+}
+
+static block_header_t* block_from_ptr(const void* ptr)
+{
+ return tlsf_cast(block_header_t*,
+ tlsf_cast(unsigned char*, ptr) - block_start_offset);
+}
+
+static void* block_to_ptr(const block_header_t* block)
+{
+ return tlsf_cast(void*,
+ tlsf_cast(unsigned char*, block) + block_start_offset);
+}
+
+/* Return location of next block after block of given size. */
+static block_header_t* offset_to_block(const void* ptr, size_t size)
+{
+ return tlsf_cast(block_header_t*, tlsf_cast(tlsfptr_t, ptr) + size);
+}
+
+/* Return location of previous block. */
+static block_header_t* block_prev(const block_header_t* block)
+{
+ return block->prev_phys_block;
+}
+
+/* Return location of next existing block. */
+static block_header_t* block_next(const block_header_t* block)
+{
+ block_header_t* next = offset_to_block(block_to_ptr(block),
+ block_size(block) - block_header_overhead);
+ tlsf_assert(!block_is_last(block));
+ return next;
+}
+
+/* Link a new block with its physical neighbor, return the neighbor. */
+static block_header_t* block_link_next(block_header_t* block)
+{
+ block_header_t* next = block_next(block);
+ next->prev_phys_block = block;
+ return next;
+}
+
+static void block_mark_as_free(block_header_t* block)
+{
+ /* Link the block to the next block, first. */
+ block_header_t* next = block_link_next(block);
+ block_set_prev_free(next);
+ block_set_free(block);
+}
+
+static void block_mark_as_used(block_header_t* block)
+{
+ block_header_t* next = block_next(block);
+ block_set_prev_used(next);
+ block_set_used(block);
+}
+
+static size_t align_up(size_t x, size_t align)
+{
+ tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two");
+ return (x + (align - 1)) & ~(align - 1);
+}
+
+static size_t align_down(size_t x, size_t align)
+{
+ tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two");
+ return x - (x & (align - 1));
+}
+
+static void* align_ptr(const void* ptr, size_t align)
+{
+ const tlsfptr_t aligned =
+ (tlsf_cast(tlsfptr_t, ptr) + (align - 1)) & ~(align - 1);
+ tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two");
+ return tlsf_cast(void*, aligned);
+}
+
+/*
+** Adjust an allocation size to be aligned to word size, and no smaller
+** than internal minimum.
+*/
+static size_t adjust_request_size(size_t size, size_t align)
+{
+ size_t adjust = 0;
+ if (size && size < block_size_max)
+ {
+ const size_t aligned = align_up(size, align);
+ adjust = tlsf_max(aligned, block_size_min);
+ }
+ return adjust;
+}
+
+/*
+** TLSF utility functions. In most cases, these are direct translations of
+** the documentation found in the white paper.
+*/
+
+static void mapping_insert(size_t size, int* fli, int* sli)
+{
+ int fl, sl;
+ if (size < SMALL_BLOCK_SIZE)
+ {
+ /* Store small blocks in first list. */
+ fl = 0;
+ sl = tlsf_cast(int, size) / (SMALL_BLOCK_SIZE / SL_INDEX_COUNT);
+ }
+ else
+ {
+ fl = tlsf_fls_sizet(size);
+ sl = tlsf_cast(int, size >> (fl - SL_INDEX_COUNT_LOG2)) ^ (1 << SL_INDEX_COUNT_LOG2);
+ fl -= (FL_INDEX_SHIFT - 1);
+ }
+ *fli = fl;
+ *sli = sl;
+}
+
+/* This version rounds up to the next block size (for allocations) */
+static void mapping_search(size_t size, int* fli, int* sli)
+{
+ if (size >= (1 << SL_INDEX_COUNT_LOG2))
+ {
+ const size_t round = (1 << (tlsf_fls_sizet(size) - SL_INDEX_COUNT_LOG2)) - 1;
+ size += round;
+ }
+ mapping_insert(size, fli, sli);
+}
+
+static block_header_t* search_suitable_block(pool_t* pool, int* fli, int* sli)
+{
+ int fl = *fli;
+ int sl = *sli;
+
+ /*
+ ** First, search for a block in the list associated with the given
+ ** fl/sl index.
+ */
+ unsigned int sl_map = pool->sl_bitmap[fl] & (~0 << sl);
+ if (!sl_map)
+ {
+ /* No block exists. Search in the next largest first-level list. */
+ const unsigned int fl_map = pool->fl_bitmap & (~0 << (fl + 1));
+ if (!fl_map)
+ {
+ /* No free blocks available, memory has been exhausted. */
+ return 0;
+ }
+
+ fl = tlsf_ffs(fl_map);
+ *fli = fl;
+ sl_map = pool->sl_bitmap[fl];
+ }
+ tlsf_assert(sl_map && "internal error - second level bitmap is null");
+ sl = tlsf_ffs(sl_map);
+ *sli = sl;
+
+ /* Return the first block in the free list. */
+ return pool->blocks[fl][sl];
+}
+
+/* Remove a free block from the free list.*/
+static void remove_free_block(pool_t* pool, block_header_t* block, int fl, int sl)
+{
+ block_header_t* prev = block->prev_free;
+ block_header_t* next = block->next_free;
+ tlsf_assert(prev && "prev_free field can not be null");
+ tlsf_assert(next && "next_free field can not be null");
+ next->prev_free = prev;
+ prev->next_free = next;
+
+ /* If this block is the head of the free list, set new head. */
+ if (pool->blocks[fl][sl] == block)
+ {
+ pool->blocks[fl][sl] = next;
+
+ /* If the new head is null, clear the bitmap. */
+ if (next == &pool->block_null)
+ {
+ pool->sl_bitmap[fl] &= ~(1 << sl);
+
+ /* If the second bitmap is now empty, clear the fl bitmap. */
+ if (!pool->sl_bitmap[fl])
+ {
+ pool->fl_bitmap &= ~(1 << fl);
+ }
+ }
+ }
+}
+
+/* Insert a free block into the free block list. */
+static void insert_free_block(pool_t* pool, block_header_t* block, int fl, int sl)
+{
+ block_header_t* current = pool->blocks[fl][sl];
+ tlsf_assert(current && "free list cannot have a null entry");
+ tlsf_assert(block && "cannot insert a null entry into the free list");
+ block->next_free = current;
+ block->prev_free = &pool->block_null;
+ current->prev_free = block;
+
+ tlsf_assert(block_to_ptr(block) == align_ptr(block_to_ptr(block), ALIGN_SIZE)
+ && "block not aligned properly");
+ /*
+ ** Insert the new block at the head of the list, and mark the first-
+ ** and second-level bitmaps appropriately.
+ */
+ pool->blocks[fl][sl] = block;
+ pool->fl_bitmap |= (1 << fl);
+ pool->sl_bitmap[fl] |= (1 << sl);
+}
+
+/* Remove a given block from the free list. */
+static void block_remove(pool_t* pool, block_header_t* block)
+{
+ int fl, sl;
+ mapping_insert(block_size(block), &fl, &sl);
+ remove_free_block(pool, block, fl, sl);
+}
+
+/* Insert a given block into the free list. */
+static void block_insert(pool_t* pool, block_header_t* block)
+{
+ int fl, sl;
+ mapping_insert(block_size(block), &fl, &sl);
+ insert_free_block(pool, block, fl, sl);
+}
+
+static int block_can_split(block_header_t* block, size_t size)
+{
+ return block_size(block) >= sizeof(block_header_t) + size;
+}
+
+/* Split a block into two, the second of which is free. */
+static block_header_t* block_split(block_header_t* block, size_t size)
+{
+ /* Calculate the amount of space left in the remaining block. */
+ block_header_t* remaining =
+ offset_to_block(block_to_ptr(block), size - block_header_overhead);
+
+ const size_t remain_size = block_size(block) - (size + block_header_overhead);
+
+ tlsf_assert(block_to_ptr(remaining) == align_ptr(block_to_ptr(remaining), ALIGN_SIZE)
+ && "remaining block not aligned properly");
+
+ tlsf_assert(block_size(block) == remain_size + size + block_header_overhead);
+ block_set_size(remaining, remain_size);
+ tlsf_assert(block_size(remaining) >= block_size_min && "block split with invalid size");
+
+ block_set_size(block, size);
+ block_mark_as_free(remaining);
+
+ return remaining;
+}
+
+/* Absorb a free block's storage into an adjacent previous free block. */
+static block_header_t* block_absorb(block_header_t* prev, block_header_t* block)
+{
+ tlsf_assert(!block_is_last(prev) && "previous block can't be last!");
+ /* Note: Leaves flags untouched. */
+ prev->size += block_size(block) + block_header_overhead;
+ block_link_next(prev);
+ return prev;
+}
+
+/* Merge a just-freed block with an adjacent previous free block. */
+static block_header_t* block_merge_prev(pool_t* pool, block_header_t* block)
+{
+ if (block_is_prev_free(block))
+ {
+ block_header_t* prev = block_prev(block);
+ tlsf_assert(prev && "prev physical block can't be null");
+ tlsf_assert(block_is_free(prev) && "prev block is not free though marked as such");
+ block_remove(pool, prev);
+ block = block_absorb(prev, block);
+ }
+
+ return block;
+}
+
+/* Merge a just-freed block with an adjacent free block. */
+static block_header_t* block_merge_next(pool_t* pool, block_header_t* block)
+{
+ block_header_t* next = block_next(block);
+ tlsf_assert(next && "next physical block can't be null");
+
+ if (block_is_free(next))
+ {
+ tlsf_assert(!block_is_last(block) && "previous block can't be last!");
+ block_remove(pool, next);
+ block = block_absorb(block, next);
+ }
+
+ return block;
+}
+
+/* Trim any trailing block space off the end of a block, return to pool. */
+static void block_trim_free(pool_t* pool, block_header_t* block, size_t size)
+{
+ tlsf_assert(block_is_free(block) && "block must be free");
+ if (block_can_split(block, size))
+ {
+ block_header_t* remaining_block = block_split(block, size);
+ block_link_next(block);
+ block_set_prev_free(remaining_block);
+ block_insert(pool, remaining_block);
+ }
+}
+
+/* Trim any trailing block space off the end of a used block, return to pool. */
+static void block_trim_used(pool_t* pool, block_header_t* block, size_t size)
+{
+ tlsf_assert(!block_is_free(block) && "block must be used");
+ if (block_can_split(block, size))
+ {
+ /* If the next block is free, we must coalesce. */
+ block_header_t* remaining_block = block_split(block, size);
+ block_set_prev_used(remaining_block);
+
+ remaining_block = block_merge_next(pool, remaining_block);
+ block_insert(pool, remaining_block);
+ }
+}
+
+static block_header_t* block_trim_free_leading(pool_t* pool, block_header_t* block, size_t size)
+{
+ block_header_t* remaining_block = block;
+ if (block_can_split(block, size))
+ {
+ /* We want the 2nd block. */
+ remaining_block = block_split(block, size - block_header_overhead);
+ block_set_prev_free(remaining_block);
+
+ block_link_next(block);
+ block_insert(pool, block);
+ }
+
+ return remaining_block;
+}
+
+static block_header_t* block_locate_free(pool_t* pool, size_t size)
+{
+ int fl = 0, sl = 0;
+ block_header_t* block = 0;
+
+ if (size)
+ {
+ mapping_search(size, &fl, &sl);
+ block = search_suitable_block(pool, &fl, &sl);
+ }
+
+ if (block)
+ {
+ tlsf_assert(block_size(block) >= size);
+ remove_free_block(pool, block, fl, sl);
+ }
+
+ return block;
+}
+
+static void* block_prepare_used(pool_t* pool, block_header_t* block, size_t size)
+{
+ void* p = 0;
+ if (block)
+ {
+ block_trim_free(pool, block, size);
+ block_mark_as_used(block);
+ p = block_to_ptr(block);
+ }
+ return p;
+}
+
+/* Clear structure and point all empty lists at the null block. */
+static void pool_construct(pool_t* pool)
+{
+ int i, j;
+
+ pool->block_null.next_free = &pool->block_null;
+ pool->block_null.prev_free = &pool->block_null;
+
+ pool->fl_bitmap = 0;
+ for (i = 0; i < FL_INDEX_COUNT; ++i)
+ {
+ pool->sl_bitmap[i] = 0;
+ for (j = 0; j < SL_INDEX_COUNT; ++j)
+ {
+ pool->blocks[i][j] = &pool->block_null;
+ }
+ }
+}
+
+/*
+** Debugging utilities.
+*/
+
+typedef struct integrity_t
+{
+ int prev_status;
+ int status;
+} integrity_t;
+
+#define tlsf_insist(x) { tlsf_assert(x); if (!(x)) { status--; } }
+
+static void integrity_walker(void* ptr, size_t size, int used, void* user)
+{
+ block_header_t* block = block_from_ptr(ptr);
+ integrity_t* integ = tlsf_cast(integrity_t*, user);
+ const int this_prev_status = block_is_prev_free(block) ? 1 : 0;
+ const int this_status = block_is_free(block) ? 1 : 0;
+ const size_t this_block_size = block_size(block);
+
+ int status = 0;
+ tlsf_insist(integ->prev_status == this_prev_status && "prev status incorrect");
+ tlsf_insist(size == this_block_size && "block size incorrect");
+
+ integ->prev_status = this_status;
+ integ->status += status;
+}
+
+int tlsf_check_heap(tlsf_pool tlsf)
+{
+ int i, j;
+
+ pool_t* pool = tlsf_cast(pool_t*, tlsf);
+ int status = 0;
+
+ /* Check that the blocks are physically correct. */
+ integrity_t integ = { 0, 0 };
+ tlsf_walk_heap(tlsf, integrity_walker, &integ);
+ status = integ.status;
+
+ /* Check that the free lists and bitmaps are accurate. */
+ for (i = 0; i < FL_INDEX_COUNT; ++i)
+ {
+ for (j = 0; j < SL_INDEX_COUNT; ++j)
+ {
+ const int fl_map = pool->fl_bitmap & (1 << i);
+ const int sl_list = pool->sl_bitmap[i];
+ const int sl_map = sl_list & (1 << j);
+ const block_header_t* block = pool->blocks[i][j];
+
+ /* Check that first- and second-level lists agree. */
+ if (!fl_map)
+ {
+ tlsf_insist(!sl_map && "second-level map must be null");
+ }
+
+ if (!sl_map)
+ {
+ tlsf_insist(block == &pool->block_null && "block list must be null");
+ continue;
+ }
+
+ /* Check that there is at least one free block. */
+ tlsf_insist(sl_list && "no free blocks in second-level map");
+ tlsf_insist(block != &pool->block_null && "block should not be null");
+
+ while (block != &pool->block_null)
+ {
+ int fli, sli;
+ tlsf_insist(block_is_free(block) && "block should be free");
+ tlsf_insist(!block_is_prev_free(block) && "blocks should have coalesced");
+ tlsf_insist(!block_is_free(block_next(block)) && "blocks should have coalesced");
+ tlsf_insist(block_is_prev_free(block_next(block)) && "block should be free");
+ tlsf_insist(block_size(block) >= block_size_min && "block not minimum size");
+
+ mapping_insert(block_size(block), &fli, &sli);
+ tlsf_insist(fli == i && sli == j && "block size indexed in wrong list");
+ block = block->next_free;
+ }
+ }
+ }
+
+ return status;
+}
+
+#undef tlsf_insist
+
+static void default_walker(void* ptr, size_t size, int used, void* user)
+{
+ (void)user;
+ printf("\t%p %s size: %x (%p)\n", ptr, used ? "used" : "free", (unsigned int)size, block_from_ptr(ptr));
+}
+
+void tlsf_walk_heap(tlsf_pool pool, tlsf_walker walker, void* user)
+{
+ tlsf_walker heap_walker = walker ? walker : default_walker;
+ block_header_t* block =
+ offset_to_block(pool, sizeof(pool_t) - block_header_overhead);
+
+ while (block && !block_is_last(block))
+ {
+ heap_walker(
+ block_to_ptr(block),
+ block_size(block),
+ !block_is_free(block),
+ user);
+ block = block_next(block);
+ }
+}
+
+size_t tlsf_block_size(void* ptr)
+{
+ size_t size = 0;
+ if (ptr)
+ {
+ const block_header_t* block = block_from_ptr(ptr);
+ size = block_size(block);
+ }
+ return size;
+}
+
+/*
+** Overhead of the TLSF structures in a given memory block passed to
+** tlsf_create, equal to the size of a pool_t plus overhead of the initial
+** free block and the sentinel block.
+*/
+size_t tlsf_overhead()
+{
+ const size_t pool_overhead = sizeof(pool_t) + 2 * block_header_overhead;
+ return pool_overhead;
+}
+
+/*
+** TLSF main interface. Right out of the white paper.
+*/
+
+tlsf_pool tlsf_create(void* mem, size_t bytes)
+{
+ block_header_t* block;
+ block_header_t* next;
+
+ const size_t pool_overhead = tlsf_overhead();
+ const size_t pool_bytes = align_down(bytes - pool_overhead, ALIGN_SIZE);
+ pool_t* pool = tlsf_cast(pool_t*, mem);
+
+#if _DEBUG
+ /* Verify ffs/fls work properly. */
+ int rv = 0;
+ rv += (tlsf_ffs(0) == -1) ? 0 : 0x1;
+ rv += (tlsf_fls(0) == -1) ? 0 : 0x2;
+ rv += (tlsf_ffs(1) == 0) ? 0 : 0x4;
+ rv += (tlsf_fls(1) == 0) ? 0 : 0x8;
+ rv += (tlsf_ffs(0x80000000) == 31) ? 0 : 0x10;
+ rv += (tlsf_ffs(0x80008000) == 15) ? 0 : 0x20;
+ rv += (tlsf_fls(0x80000008) == 31) ? 0 : 0x40;
+ rv += (tlsf_fls(0x7FFFFFFF) == 30) ? 0 : 0x80;
+
+#if defined (TLSF_64BIT)
+ rv += (tlsf_fls_sizet(0x80000000) == 31) ? 0 : 0x100;
+ rv += (tlsf_fls_sizet(0x100000000) == 32) ? 0 : 0x200;
+ rv += (tlsf_fls_sizet(0xffffffffffffffff) == 63) ? 0 : 0x400;
+ if (rv)
+ {
+ printf("tlsf_create: %x ffs/fls tests failed!\n", rv);
+ return 0;
+ }
+#endif
+#endif
+
+ if (pool_bytes < block_size_min || pool_bytes > block_size_max)
+ {
+#if defined (TLSF_64BIT)
+ printf("tlsf_create: Pool size must be at least %d bytes.\n",
+ (unsigned int)(pool_overhead + block_size_min));
+#else
+ printf("tlsf_create: Pool size must be between %u and %u bytes.\n",
+ (unsigned int)(pool_overhead + block_size_min),
+ (unsigned int)(pool_overhead + block_size_max));
+#endif
+ return 0;
+ }
+
+ /* Construct a valid pool object. */
+ pool_construct(pool);
+
+ /*
+ ** Create the main free block. Offset the start of the block slightly
+ ** so that the prev_phys_block field falls inside of the pool
+ ** structure - it will never be used.
+ */
+ block = offset_to_block(
+ tlsf_cast(void*, pool), sizeof(pool_t) - block_header_overhead);
+ block_set_size(block, pool_bytes);
+ block_set_free(block);
+ block_set_prev_used(block);
+ block_insert(pool, block);
+
+ /* Split the block to create a zero-size pool sentinel block. */
+ next = block_link_next(block);
+ block_set_size(next, 0);
+ block_set_used(next);
+ block_set_prev_free(next);
+
+ return tlsf_cast(tlsf_pool, pool);
+}
+
+void tlsf_destroy(tlsf_pool pool)
+{
+ /* Nothing to do. */
+ pool = pool;
+}
+
+void* tlsf_malloc(tlsf_pool tlsf, size_t size)
+{
+ pool_t* pool = tlsf_cast(pool_t*, tlsf);
+ const size_t adjust = adjust_request_size(size, ALIGN_SIZE);
+ block_header_t* block = block_locate_free(pool, adjust);
+ return block_prepare_used(pool, block, adjust);
+}
+
+void* tlsf_memalign(tlsf_pool tlsf, size_t align, size_t size)
+{
+ pool_t* pool = tlsf_cast(pool_t*, tlsf);
+ const size_t adjust = adjust_request_size(size, ALIGN_SIZE);
+
+ /*
+ ** We must allocate an additional minimum block size bytes so that if
+ ** our free block will leave an alignment gap which is smaller, we can
+ ** trim a leading free block and release it back to the heap. We must
+ ** do this because the previous physical block is in use, therefore
+ ** the prev_phys_block field is not valid, and we can't simply adjust
+ ** the size of that block.
+ */
+ const size_t gap_minimum = sizeof(block_header_t);
+ const size_t size_with_gap = adjust_request_size(adjust + align + gap_minimum, align);
+
+ /* If alignment is less than or equals base alignment, we're done. */
+ const size_t aligned_size = (align <= ALIGN_SIZE) ? adjust : size_with_gap;
+
+ block_header_t* block = block_locate_free(pool, aligned_size);
+
+ /* This can't be a static assert. */
+ tlsf_assert(sizeof(block_header_t) == block_size_min + block_header_overhead);
+
+ if (block)
+ {
+ void* ptr = block_to_ptr(block);
+ void* aligned = align_ptr(ptr, align);
+ size_t gap = tlsf_cast(size_t,
+ tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr));
+
+ /* If gap size is too small, offset to next aligned boundary. */
+ if (gap && gap < gap_minimum)
+ {
+ const size_t gap_remain = gap_minimum - gap;
+ const size_t offset = tlsf_max(gap_remain, align);
+ const void* next_aligned = tlsf_cast(void*,
+ tlsf_cast(tlsfptr_t, aligned) + offset);
+
+ aligned = align_ptr(next_aligned, align);
+ gap = tlsf_cast(size_t,
+ tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr));
+ }
+
+ if (gap)
+ {
+ tlsf_assert(gap >= gap_minimum && "gap size too small");
+ block = block_trim_free_leading(pool, block, gap);
+ }
+ }
+
+ return block_prepare_used(pool, block, adjust);
+}
+
+void tlsf_free(tlsf_pool tlsf, void* ptr)
+{
+ /* Don't attempt to free a NULL pointer. */
+ if (ptr)
+ {
+ pool_t* pool = tlsf_cast(pool_t*, tlsf);
+ block_header_t* block = block_from_ptr(ptr);
+ block_mark_as_free(block);
+ block = block_merge_prev(pool, block);
+ block = block_merge_next(pool, block);
+ block_insert(pool, block);
+ }
+}
+
+/*
+** The TLSF block information provides us with enough information to
+** provide a reasonably intelligent implementation of realloc, growing or
+** shrinking the currently allocated block as required.
+**
+** This routine handles the somewhat esoteric edge cases of realloc:
+** - a non-zero size with a null pointer will behave like malloc
+** - a zero size with a non-null pointer will behave like free
+** - a request that cannot be satisfied will leave the original buffer
+** untouched
+** - an extended buffer size will leave the newly-allocated area with
+** contents undefined
+*/
+void* tlsf_realloc(tlsf_pool tlsf, void* ptr, size_t size)
+{
+ pool_t* pool = tlsf_cast(pool_t*, tlsf);
+ void* p = 0;
+
+ /* Zero-size requests are treated as free. */
+ if (ptr && size == 0)
+ {
+ tlsf_free(tlsf, ptr);
+ }
+ /* Requests with NULL pointers are treated as malloc. */
+ else if (!ptr)
+ {
+ p = tlsf_malloc(tlsf, size);
+ }
+ else
+ {
+ block_header_t* block = block_from_ptr(ptr);
+ block_header_t* next = block_next(block);
+
+ const size_t cursize = block_size(block);
+ const size_t combined = cursize + block_size(next) + block_header_overhead;
+ const size_t adjust = adjust_request_size(size, ALIGN_SIZE);
+
+ /*
+ ** If the next block is used, or when combined with the current
+ ** block, does not offer enough space, we must reallocate and copy.
+ */
+ if (adjust > cursize && (!block_is_free(next) || adjust > combined))
+ {
+ p = tlsf_malloc(tlsf, size);
+ if (p)
+ {
+ const size_t minsize = tlsf_min(cursize, size);
+ memcpy(p, ptr, minsize);
+ tlsf_free(tlsf, ptr);
+ }
+ }
+ else
+ {
+ /* Do we need to expand to the next block? */
+ if (adjust > cursize)
+ {
+ block_merge_next(pool, block);
+ block_mark_as_used(block);
+ }
+
+ /* Trim the resulting block and return the original pointer. */
+ block_trim_used(pool, block, adjust);
+ p = ptr;
+ }
+ }
+
+ return p;
+}
diff --git a/Runtime/Allocator/tlsf/tlsf.h b/Runtime/Allocator/tlsf/tlsf.h
new file mode 100644
index 0000000..de7f90b
--- /dev/null
+++ b/Runtime/Allocator/tlsf/tlsf.h
@@ -0,0 +1,52 @@
+#ifndef INCLUDED_tlsf
+#define INCLUDED_tlsf
+
+/*
+** Two Level Segregated Fit memory allocator, version 1.9.
+** Written by Matthew Conte, and placed in the Public Domain.
+** http://tlsf.baisoku.org
+**
+** Based on the original documentation by Miguel Masmano:
+** http://rtportal.upv.es/rtmalloc/allocators/tlsf/index.shtml
+**
+** Please see the accompanying Readme.txt for implementation
+** notes and caveats.
+**
+** This implementation was written to the specification
+** of the document, therefore no GPL restrictions apply.
+*/
+
+#include <stddef.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Create/destroy a memory pool. */
+typedef void* tlsf_pool;
+tlsf_pool tlsf_create(void* mem, size_t bytes);
+void tlsf_destroy(tlsf_pool pool);
+
+/* malloc/memalign/realloc/free replacements. */
+void* tlsf_malloc(tlsf_pool pool, size_t bytes);
+void* tlsf_memalign(tlsf_pool pool, size_t align, size_t bytes);
+void* tlsf_realloc(tlsf_pool pool, void* ptr, size_t size);
+void tlsf_free(tlsf_pool pool, void* ptr);
+
+/* Debugging. */
+typedef void (*tlsf_walker)(void* ptr, size_t size, int used, void* user);
+void tlsf_walk_heap(tlsf_pool pool, tlsf_walker walker, void* user);
+/* Returns nonzero if heap check fails. */
+int tlsf_check_heap(tlsf_pool pool);
+
+/* Returns internal block size, not original request size */
+size_t tlsf_block_size(void* ptr);
+
+/* Overhead of per-pool internal structures. */
+size_t tlsf_overhead();
+
+#if defined(__cplusplus)
+};
+#endif
+
+#endif
diff --git a/Runtime/Allocator/tlsf/tlsfbits.h b/Runtime/Allocator/tlsf/tlsfbits.h
new file mode 100644
index 0000000..a41a959
--- /dev/null
+++ b/Runtime/Allocator/tlsf/tlsfbits.h
@@ -0,0 +1,184 @@
+#ifndef INCLUDED_tlsfbits
+#define INCLUDED_tlsfbits
+
+#if defined(__cplusplus)
+#define tlsf_decl inline
+#else
+#define tlsf_decl static
+#endif
+
+/*
+** Architecture-specific bit manipulation routines.
+**
+** TLSF achieves O(1) cost for malloc and free operations by limiting
+** the search for a free block to a free list of guaranteed size
+** adequate to fulfill the request, combined with efficient free list
+** queries using bitmasks and architecture-specific bit-manipulation
+** routines.
+**
+** Most modern processors provide instructions to count leading zeroes
+** in a word, find the lowest and highest set bit, etc. These
+** specific implementations will be used when available, falling back
+** to a reasonably efficient generic implementation.
+**
+** NOTE: TLSF spec relies on ffs/fls returning value 0..31.
+** ffs/fls return 1-32 by default, returning 0 for error.
+*/
+
+/*
+** Detect whether or not we are building for a 32- or 64-bit (LP/LLP)
+** architecture. There is no reliable portable method at compile-time.
+*/
+
+// native client uses ILP32, even when building for x86_64, so make sure
+// that pointer sizes are treated as 32 bits.
+#if !defined(__native_client__)
+#if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) \
+ || defined (_WIN64) || defined (__LP64__) || defined (__LLP64__)
+#define TLSF_64BIT
+#endif
+#endif
+/*
+** gcc 3.4 and above have builtin support, specialized for architecture.
+** Some compilers masquerade as gcc; patchlevel test filters them out.
+*/
+#if defined (__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) \
+ && defined (__GNUC_PATCHLEVEL__)
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ return __builtin_ffs(word) - 1;
+}
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ const int bit = word ? 32 - __builtin_clz(word) : 0;
+ return bit - 1;
+}
+
+#elif defined (_MSC_VER) && defined (_M_IX86) && (_MSC_VER >= 1400)
+/* Microsoft Visual C++ 2005 support on x86 architectures. */
+
+#include <intrin.h>
+
+#pragma intrinsic(_BitScanReverse)
+#pragma intrinsic(_BitScanForward)
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ unsigned long index;
+ return _BitScanReverse(&index, word) ? index : -1;
+}
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ unsigned long index;
+ return _BitScanForward(&index, word) ? index : -1;
+}
+
+#elif defined (_MSC_VER) && defined (_M_PPC)
+/* Microsoft Visual C++ support on PowerPC architectures. */
+
+#include <ppcintrinsics.h>
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ const int bit = 32 - _CountLeadingZeros(word);
+ return bit - 1;
+}
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ const unsigned int reverse = word & (~word + 1);
+ const int bit = 32 - _CountLeadingZeros(reverse);
+ return bit - 1;
+}
+
+#elif defined (__ARMCC_VERSION)
+/* RealView Compilation Tools for ARM */
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ const unsigned int reverse = word & (~word + 1);
+ const int bit = 32 - __clz(reverse);
+ return bit - 1;
+}
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ const int bit = word ? 32 - __clz(word) : 0;
+ return bit - 1;
+}
+
+#elif defined (__ghs__)
+/* Green Hills support for PowerPC */
+
+#include <ppc_ghs.h>
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ const unsigned int reverse = word & (~word + 1);
+ const int bit = 32 - __CLZ32(reverse);
+ return bit - 1;
+}
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ const int bit = word ? 32 - __CLZ32(word) : 0;
+ return bit - 1;
+}
+
+#else
+/* Fall back to generic implementation. */
+
+tlsf_decl int tlsf_fls_generic(unsigned int word)
+{
+ int bit = 32;
+
+ if (!word) bit -= 1;
+ if (!(word & 0xffff0000)) { word <<= 16; bit -= 16; }
+ if (!(word & 0xff000000)) { word <<= 8; bit -= 8; }
+ if (!(word & 0xf0000000)) { word <<= 4; bit -= 4; }
+ if (!(word & 0xc0000000)) { word <<= 2; bit -= 2; }
+ if (!(word & 0x80000000)) { word <<= 1; bit -= 1; }
+
+ return bit;
+}
+
+/* Implement ffs in terms of fls. */
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ return tlsf_fls_generic(word & (~word + 1)) - 1;
+}
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ return tlsf_fls_generic(word) - 1;
+}
+
+#endif
+
+/* Possibly 64-bit version of tlsf_fls. */
+#if defined (TLSF_64BIT)
+tlsf_decl int tlsf_fls_sizet(size_t size)
+{
+ int high = (int)(size >> 32);
+ int bits = 0;
+ if (high)
+ {
+ bits = 32 + tlsf_fls(high);
+ }
+ else
+ {
+ bits = tlsf_fls((int)size & 0xffffffff);
+
+ }
+ return bits;
+}
+#else
+#define tlsf_fls_sizet tlsf_fls
+#endif
+
+#undef tlsf_decl
+
+#endif
diff --git a/Runtime/Animation/Animation.cpp b/Runtime/Animation/Animation.cpp
new file mode 100644
index 0000000..a2d7cda
--- /dev/null
+++ b/Runtime/Animation/Animation.cpp
@@ -0,0 +1,2373 @@
+#include "UnityPrefix.h"
+#include "Animation.h"
+#include "AnimationManager.h"
+#include "AnimationClip.h"
+#include "AnimationClipUtility.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Utilities/Utility.h"
+#include "AnimationBinder.h"
+#include "NewAnimationTrack.h"
+#include "AnimationState.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Shaders/Material.h"
+#include "AnimationCurveUtility.h"
+#include "Runtime/Serialize/TransferUtility.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Utilities/dense_hash_map.h"
+#include "Runtime/Utilities/Prefetch.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Animation/AnimationUtility.h"
+#include "Runtime/BaseClasses/EventIDs.h"
+
+using std::make_pair;
+using std::max;
+
+PROFILER_INFORMATION (gBuildAnimationState, "Animation.RebuildInternalState", kProfilerAnimation);
+PROFILER_INFORMATION (gUpdateAnimation, "Animation.Update", kProfilerAnimation);
+PROFILER_INFORMATION (gSampleAnimation, "Animation.Sample", kProfilerAnimation);
+PROFILER_WARNING (gDidDestroyObjectNotification, "Animation.DestroyAnimationClip [Triggers RebuildInternalState]", kProfilerAnimation);
+PROFILER_WARNING (gAddClip, "Animation.AddClip [Triggers RebuildInternalState]", kProfilerAnimation);
+PROFILER_WARNING (gRemoveClip, "Animation.RemoveClip [Triggers RebuildInternalState]", kProfilerAnimation);
+PROFILER_WARNING (gCloneAnimationState, "Animation.Clone [Triggers RebuildInternalState]", kProfilerAnimation);
+PROFILER_WARNING (gAnimationDeactivate, "Animation.Deactivate [Triggers RebuildInternalState]", kProfilerAnimation);
+PROFILER_INFORMATION (gValidate, "ValidateBoundCurves", kProfilerAnimation);
+
+inline int CombineWrapMode (int clipWrapMode, int animationWrapMode)
+{
+ if (clipWrapMode != 0)
+ return clipWrapMode;
+ else
+ return animationWrapMode;
+}
+
+Animation::Animation (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_AnimationManagerNode(this)
+, m_ActiveAnimationStatesSize(0)
+, m_CullingType(kCulling_AlwaysAnimate)
+, m_BoundCurves (label)
+{
+ m_WrapMode = 0;
+
+ m_PlayAutomatically = true;
+ m_AnimatePhysics = false;
+ m_DirtyMask = 0;
+ m_Visible = false;
+ memset (m_ActiveAnimationStates, 0, sizeof(m_ActiveAnimationStates));
+}
+
+Animation::~Animation ()
+{
+ ClearContainedRenderers ();
+ ReleaseAnimationStates ();
+ CleanupBoundCurves();
+}
+
+
+void Animation::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+#if UNITY_EDITOR
+ LoadOldAnimations();
+#endif
+
+ // We don't know what kind of culling we used before, so we clear all culling related data
+ ClearContainedRenderers();
+
+ CheckIsCullingBasedOnBoundsDeprecated();
+
+ if (m_CullingType == kCulling_BasedOnRenderers && !m_AnimationStates.empty())
+ RecomputeContainedRenderers ();
+
+ if (m_PlayAutomatically && (awakeMode & (kDidLoadFromDisk | kInstantiateOrCreateFromCodeAwakeFromLoad | kActivateAwakeFromLoad)) && IsActive () && IsWorldPlaying())
+ Play (kStopAll);
+}
+
+void Animation::Deactivate (DeactivateOperation operation)
+{
+ if (operation != kDeprecatedDeactivateToggleForLevelLoad)
+ {
+ PROFILER_AUTO(gAnimationDeactivate, this)
+ //ReleaseAnimationStates ();
+ Stop();
+ CleanupBoundCurves();
+ }
+}
+
+AnimationClip* Animation::GetClipWithNameSerialized (const std::string& name)
+{
+ for (Animations::iterator i=m_Animations.begin();i != m_Animations.end();i++)
+ {
+ AnimationClip* clip = *i;
+ if (clip && clip->GetName() == name)
+ return clip;
+ }
+ return NULL;
+}
+
+void Animation::SetClip (PPtr<AnimationClip> anim)
+{
+ m_Animation = anim;
+
+ SetDirty ();
+}
+
+void Animation::SetClips (const Animations& anims)
+{
+ m_Animations = anims;
+
+ CheckIsCullingBasedOnBoundsDeprecated();
+
+ SetDirty ();
+}
+
+bool Animation::IsPlayingLayer (int layer)
+{
+ if (m_AnimationStates.empty ())
+ return false;
+
+ for (iterator i=begin();i != end();i++)
+ {
+ AnimationState& state = **i;
+ if (state.GetLayer() == layer && state.GetEnabled())
+ return true;
+ }
+
+ return false;
+}
+
+
+bool Animation::IsPlaying ()
+{
+ if (m_AnimationStates.empty ())
+ return false;
+
+ for (iterator i=begin();i != end();i++)
+ {
+ AnimationState& state = **i;
+ if (state.GetEnabled())
+ return true;
+ }
+
+ return false;
+}
+
+bool Animation::IsPlaying (const string& clip)
+{
+ AnimationState* state = GetState(clip);
+
+ if ( state && state->GetEnabled() )
+ return true;
+ else // check for clones
+ {
+ for ( int s = 0; s < m_AnimationStates.size(); s++ )
+ {
+ AnimationState* st = m_AnimationStates[s];
+
+ if ( st->IsClone()
+ && st->GetParentName() == clip
+ && st->GetEnabled() )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+
+void Animation::Stop ()
+{
+ for (iterator i=begin();i != end();i++)
+ {
+ (**i).Stop();
+ }
+
+ // Remove all queued animations.
+ m_Queued.clear();
+}
+
+void Animation::Stop( const string& name )
+{
+ for ( int s = 0; s < m_AnimationStates.size(); s++ )
+ {
+ AnimationState* state = m_AnimationStates[s];
+
+ bool stopState = false;
+ if ( state->IsClone() && state->GetParentName() == name )
+ stopState = true;
+ else if ( !state->IsClone() && state->GetName() == name )
+ stopState = true;
+
+ if ( stopState )
+ Stop( *state );
+ }
+
+ // Remove any queued animations that were cloned from the named state.
+ QueuedAnimations::iterator q, qnext;
+ for (q=m_Queued.begin();q != m_Queued.end();q=qnext)
+ {
+ qnext = q;
+
+ if ( q->state->GetParentName() != name )
+ qnext++;
+ else
+ m_Queued.erase( q );
+ }
+
+}
+
+void Animation::Stop (AnimationState& state)
+{
+ state.Stop();
+}
+
+void Animation::Rewind ()
+{
+ for (iterator i=begin();i != end();i++)
+ {
+ Rewind(**i);
+ }
+}
+
+void Animation::Rewind (const string& name)
+{
+ if (!m_AnimationStates.empty())
+ {
+ AnimationState* state = GetState(name);
+ if (state)
+ Rewind(*state);
+ }
+}
+
+void Animation::Rewind (AnimationState& state)
+{
+ state.SetTime(0.0F);
+}
+
+void Animation::SetWrapMode (int mode)
+{
+ m_WrapMode = mode;
+ for (iterator i=begin ();i != end ();i++)
+ {
+ AnimationState& state = **i;
+ state.SetWrapMode(mode);
+ }
+ SetDirty();
+}
+
+#if UNITY_EDITOR
+/// Deprecated with 1.5
+void Animation::LoadOldAnimations ()
+{
+ // Load old animations into new animation array
+ for (OldAnimations::iterator i=m_OldAnimations.begin ();i != m_OldAnimations.end ();i++)
+ {
+ AnimationClip* clip = i->second;
+ if (clip)
+ {
+ AddClip (*clip, i->first, INT_MIN, INT_MAX, false);
+ }
+ }
+ m_OldAnimations.clear();
+
+
+ // Make sure we have the default animation in the animations list
+ AnimationClip* defaultAnim = m_Animation;
+ if (defaultAnim)
+ {
+ for (Animations::iterator i=m_Animations.begin();i!=m_Animations.end();i++)
+ {
+ if (*i == m_Animation)
+ return;
+ }
+
+ AddClip (*defaultAnim, defaultAnim->GetName(), INT_MIN, INT_MAX, false);
+ }
+}
+#endif
+
+IMPLEMENT_CLASS_HAS_INIT (Animation)
+IMPLEMENT_OBJECT_SERIALIZE (Animation)
+///////////////////***************************
+
+void Animation::AddToManager ()
+{
+ // This method is named AddToManager, but it is used to remove from manager too...
+ m_AnimationManagerNode.RemoveFromList();
+ if (IsWorldPlaying())
+ {
+ if (GetEnabled() && (m_Visible || m_CullingType == kCulling_AlwaysAnimate) && IsActive() && !m_AnimationStates.empty())
+ {
+ if (!m_AnimatePhysics)
+ GetAnimationManager().AddDynamic(m_AnimationManagerNode);
+ else
+ GetAnimationManager().AddFixed(m_AnimationManagerNode);
+ }
+ }
+ else
+ {
+ // Insert all in edit mode - for sampling animation with the timeline
+ if (IsActive())
+ GetAnimationManager().AddDynamic(m_AnimationManagerNode);
+ }
+}
+
+void Animation::RemoveFromManager ()
+{
+ m_AnimationManagerNode.RemoveFromList();
+}
+
+void Animation::SetAnimatePhysics (bool anim)
+{
+ m_AnimatePhysics = anim;
+ SetDirty();
+ if (m_AnimationManagerNode.IsInList())
+ {
+ m_AnimationManagerNode.RemoveFromList();
+ if (!m_AnimatePhysics)
+ GetAnimationManager().AddDynamic(m_AnimationManagerNode);
+ else
+ GetAnimationManager().AddFixed(m_AnimationManagerNode);
+ }
+}
+
+#define ScriptErrorStringObject ErrorStringObject
+
+const char* kAnimationNotFoundError =
+"The animation state %s could not be played because it couldn't be found!\n"
+"Please attach an animation clip with the name '%s' or call this function only for existing animations.";
+#define CANT_PLAY_ERROR { ScriptErrorStringObject(Format(kAnimationNotFoundError, name.c_str(), name.c_str()), this); }
+
+
+const char* kWrongStateError =
+"The animation state %s could not be played because it is not attached to the animation component!\n"
+"You have to provide an animation state that is attached to the same animation component.";
+#define WRONG_STATE_ERROR(x) { ScriptErrorStringObject(Format(kWrongStateError, x.GetName().c_str()), this); }
+
+
+bool Animation::Play(const std::string& name, int playMode)
+{
+ // Deprecated support for play with queueing
+ if (playMode == kPlayQueuedDeprecated)
+ {
+ QueueCrossFade(name, 0.0F, CompleteOthers, kStopSameLayer);
+ return true;
+ }
+
+ CrossFade(name, 0.0F, playMode);
+ return true;
+}
+
+void Animation::Play(AnimationState& fadeIn, int playMode)
+{
+ // Deprecated support for play with queueing
+ if (playMode == kPlayQueuedDeprecated)
+ {
+ QueueCrossFade(fadeIn, 0.0F, CompleteOthers, kStopSameLayer);
+ return;
+ }
+
+ CrossFade(fadeIn, 0.0F, playMode, true);
+ return;
+}
+
+
+bool Animation::Play (int mode)
+{
+ AnimationClip* clip = m_Animation;
+ if (clip)
+ {
+ AnimationState* state = GetState(clip);
+ if (state)
+ {
+ Play(*state, mode);
+ return true;
+ }
+ else
+ {
+ LogStringObject("Default clip could not be found in attached animations list.", this);
+ return false;
+ }
+ }
+ else
+ return false;
+}
+
+void Animation::Blend(const std::string& name, float targetWeight, float time) {
+ AnimationState* state = GetState(name);
+ if (state)
+ Blend(*state, targetWeight, time);
+ else
+ CANT_PLAY_ERROR
+}
+
+void Animation::CrossFade(const std::string& name, float time, int mode) {
+ AnimationState* state = GetState(name);
+ if (state)
+ CrossFade(*state, time, mode, true);
+ else
+ CANT_PLAY_ERROR
+}
+
+AnimationState* Animation::QueueCrossFade(const std::string& name, float time, int queue, int mode) {
+ AnimationState* state = GetState(name);
+ if (state)
+ return QueueCrossFade(*state, time, queue, mode);
+ else
+ {
+ CANT_PLAY_ERROR
+ return NULL;
+ }
+}
+
+/// - Should Blend and CrossFade Rewind animations before fading in?
+/// - Should CrossFade Rewind animations after they are faded out?
+
+void Animation::Blend(AnimationState& playState, float targetWeight, float time)
+{
+ bool found = false;
+ for (iterator i=begin();i!=end();i++)
+ {
+ AnimationState& state = **i;
+
+ if (&playState == &state)
+ {
+ state.SetEnabled(true);
+ state.SetWeightTarget(targetWeight, time, false);
+ state.SetupFadeout(time);
+ found = true;
+ }
+ }
+ if (!found)
+ WRONG_STATE_ERROR(playState)
+}
+
+void Animation::CrossFade(AnimationState& playState, float time, int mode, bool clearQueuedAnimations )
+{
+ bool found = false;
+ for (iterator i=begin();i!=end();i++)
+ {
+ AnimationState& state = **i;
+
+ // Don't touch animations in other layers!
+ if ((mode & kStopAll) == 0 && state.GetLayer() != playState.GetLayer())
+ continue;
+
+ if (&playState == &state)
+ {
+ state.SetEnabled(true);
+ if (time > kReallySmallFadeTime)
+ state.SetWeightTarget(1.0F, time, false);
+ else
+ {
+ state.SetWeightTargetImmediate(1.0F, false);
+ }
+
+ state.SetupFadeout(time);
+ found = true;
+ }
+ else
+ {
+ if (time > kReallySmallFadeTime)
+ state.SetWeightTarget(0.0F, time, true);
+ else
+ {
+ state.Stop();
+ state.SetWeight(0.0F);
+ }
+ }
+ }
+
+ if ( clearQueuedAnimations )
+ {
+ // Clear out queued animations on the same channel as the state
+ // we are cross fading to or all queued animations if we are
+ // stopping all.
+
+ // Fixed bug: https://fogbugz.unity3d.com/default.asp?470484, on metro std vector are somehow implemented differently
+ // If you remove an element from the list, you have to reinitialize all the iterators, to fix the problem, I removed iterators at all
+ for (int i = 0; i < m_Queued.size();)
+ {
+ if ( !(mode & kStopAll) && m_Queued[i].state->GetLayer() != playState.GetLayer() )
+ {
+ i++;
+ continue;
+ }
+
+ m_Queued[i].state->Stop();
+ m_Queued[i].state->ForceAutoCleanup();
+ m_Queued.erase(m_Queued.begin() + i);
+ }
+ }
+
+ if ( !found )
+ WRONG_STATE_ERROR(playState)
+}
+
+AnimationState* Animation::QueueCrossFade(AnimationState& originalState, float time, int queueMode, int mode)
+{
+ AnimationState* cloned = CloneAnimation(&originalState);
+ if (!cloned)
+ {
+ WRONG_STATE_ERROR(originalState)
+ return NULL;
+ }
+
+ AnimationState& playState = *cloned;
+ playState.SetAutoCleanup();
+
+ // Queue the animation for real!
+ if (queueMode == CompleteOthers)
+ {
+ QueuedAnimation queue;
+ queue.mode = mode;
+ queue.queue = queueMode;
+ queue.fadeTime = time;
+ queue.state = &playState;
+ m_Queued.push_back(queue);
+ return &playState;
+ }
+ else
+ {
+ CrossFade(playState, time, mode, true);
+ return &playState;
+ }
+}
+
+/// * All animation states have an array with pointers to the curves used when sampling. (AnimationState.m_Curves)
+/// The index in the AnimationState.m_Curves is the same as the BoundCurves index.
+/// If a curve is not available or excluded because of mixing the pointer is NULL.
+
+void InsertAnimationClipCurveIDs(AnimationBinder::CurveIDLookup& curveIDLookup, AnimationClip& clip)
+{
+ AnimationClip::QuaternionCurves& rot = clip.GetRotationCurves();
+ AnimationClip::Vector3Curves& pos = clip.GetPositionCurves();
+ AnimationClip::Vector3Curves& scale = clip.GetScaleCurves();
+ AnimationClip::FloatCurves& floats = clip.GetFloatCurves();
+ CurveID curveID;
+
+ for (AnimationClip::QuaternionCurves::iterator i=rot.begin();i != rot.end();i++)
+ {
+ AnimationClip::QuaternionCurve& element = *i;
+
+ curveID = CurveID (element.path.c_str(), ClassID(Transform), NULL, "m_LocalRotation", element.hash);
+ if (element.hash == 0)
+ {
+ curveID.CalculateHash();
+ element.hash = curveID.hash;
+ }
+
+ AnimationBinder::InsertCurveIDIntoLookup(curveIDLookup, curveID);
+ }
+
+ for (AnimationClip::Vector3Curves::iterator i=pos.begin();i != pos.end();i++)
+ {
+ AnimationClip::Vector3Curve& element = *i;
+
+ curveID = CurveID (element.path.c_str(), ClassID(Transform), NULL, "m_LocalPosition", element.hash);
+ if (element.hash == 0)
+ {
+ curveID.CalculateHash();
+ element.hash = curveID.hash;
+ }
+ AnimationBinder::InsertCurveIDIntoLookup(curveIDLookup, curveID);
+ }
+
+ for (AnimationClip::Vector3Curves::iterator i=scale.begin();i != scale.end();i++)
+ {
+ AnimationClip::Vector3Curve& element = *i;
+
+ curveID = CurveID (element.path.c_str(), ClassID(Transform), NULL, "m_LocalScale", element.hash);
+ if (element.hash == 0)
+ {
+ curveID.CalculateHash();
+ element.hash = curveID.hash;
+ }
+
+
+ AnimationBinder::InsertCurveIDIntoLookup(curveIDLookup, curveID);
+ }
+
+ for (AnimationClip::FloatCurves::iterator i=floats.begin();i != floats.end();i++)
+ {
+ AnimationClip::FloatCurve& element = *i;
+ curveID = CurveID (element.path.c_str(), element.classID, element.script, element.attribute.c_str(), element.hash);
+ if (element.hash == 0)
+ {
+ curveID.CalculateHash();
+ element.hash = curveID.hash;
+ }
+
+ AnimationBinder::InsertCurveIDIntoLookup(curveIDLookup, curveID);
+ }
+}
+
+static bool IsMixedIn (const Animation::BoundCurves& boundCurves, int index, AnimationState& state)
+{
+ // We only mix transform animation
+ if (boundCurves[index].targetType == kBindTransformRotation || boundCurves[index].targetType == kBindTransformPosition || boundCurves[index].targetType == kBindTransformScale)
+ {
+ DebugAssertIf(dynamic_pptr_cast<Transform*> (boundCurves[index].targetObject) == NULL);
+ Transform* transform = static_cast<Transform*> (boundCurves[index].targetObject);
+
+ return state.ShouldMixTransform(*transform);
+ }
+ return true;
+}
+
+
+static void AssignBoundCurve (const AnimationBinder::CurveIDLookup& curveIDLookup, const CurveID& curveID, AnimationCurveBase* curve, const Animation::BoundCurves& boundCurves, AnimationState& state)
+{
+ AnimationBinder::CurveIDLookup::const_iterator found;
+ found = curveIDLookup.find(curveID);
+ if (found == curveIDLookup.end())
+ return;
+
+ if (!IsMixedIn (boundCurves, found->second, state))
+ return;
+
+ AnimationState::Curves curves = state.GetCurves();
+ curves[found->second] = curve;
+}
+
+static void CalculateAnimationClipCurves (const AnimationBinder::CurveIDLookup& curveIDLookup, AnimationClip& clip, const Animation::BoundCurves& boundCurves, AnimationState& state)
+{
+ AnimationClip::QuaternionCurves& rot = clip.GetRotationCurves();
+ AnimationClip::Vector3Curves& pos = clip.GetPositionCurves();
+ AnimationClip::Vector3Curves& scale = clip.GetScaleCurves();
+ AnimationClip::FloatCurves& floats = clip.GetFloatCurves();
+
+ CurveID curveID;
+
+ for (AnimationClip::QuaternionCurves::iterator i=rot.begin();i != rot.end();i++)
+ {
+ if (!i->curve.IsValid ())
+ continue;
+ AssertIf(i->hash == 0);
+ curveID = CurveID (i->path.c_str(), ClassID(Transform), NULL, "m_LocalRotation", i->hash);
+ AssignBoundCurve(curveIDLookup, curveID, (AnimationCurveBase*)&i->curve, boundCurves, state);
+ }
+
+ for (AnimationClip::Vector3Curves::iterator i=pos.begin();i != pos.end();i++)
+ {
+ if (!i->curve.IsValid ())
+ continue;
+
+ AssertIf(i->hash == 0);
+ curveID = CurveID (i->path.c_str(), ClassID(Transform), NULL, "m_LocalPosition", i->hash);
+ AssignBoundCurve(curveIDLookup, curveID, (AnimationCurveBase*)&i->curve, boundCurves, state);
+ }
+
+ for (AnimationClip::Vector3Curves::iterator i=scale.begin();i != scale.end();i++)
+ {
+ if (!i->curve.IsValid ())
+ continue;
+
+ AssertIf(i->hash == 0);
+ curveID = CurveID (i->path.c_str(), ClassID(Transform), NULL, "m_LocalScale", i->hash);
+ AssignBoundCurve(curveIDLookup, curveID, (AnimationCurveBase*)&i->curve, boundCurves, state);
+ }
+
+ for (AnimationClip::FloatCurves::iterator i=floats.begin();i != floats.end();i++)
+ {
+ if (!i->curve.IsValid ())
+ continue;
+
+ AssertIf(i->hash == 0);
+ curveID = CurveID (i->path.c_str(), i->classID, i->script, i->attribute.c_str(), i->hash);
+ AssignBoundCurve(curveIDLookup, curveID, (AnimationCurveBase*)&i->curve, boundCurves, state);
+ }
+}
+
+void Animation::ValidateBoundCurves ()
+{
+ PROFILER_AUTO(gValidate, this);
+ BoundCurves::const_iterator end = m_BoundCurves.end();
+ for (BoundCurves::const_iterator i=m_BoundCurves.begin(); i != end; ++i)
+ {
+ const BoundCurveDeprecated& bound = *i;
+
+#if 0
+ // We are accessing potentially deleted memory and validating it if the instanceID matches.
+ // This is unsafe, but will work in almost all cases. ValidateBoundCurves does not trigger in normal circumstances.
+ // Yet we have to pay a large cost for the safety provided... This seems not fair.
+ if (bound.targetObject->GetInstanceID () != bound.targetInstanceID)
+#else
+ if (Object::IDToPointer(bound.targetInstanceID) != bound.targetObject)
+#endif
+ {
+ PROFILER_AUTO(gDidDestroyObjectNotification, this);
+ CleanupBoundCurves ();
+ return;
+ }
+ }
+}
+
+void Animation::CleanupBoundCurves ()
+{
+ if (m_BoundCurves.empty ())
+ {
+ return;
+ }
+
+ m_BoundCurves.clear();
+ m_DirtyMask |= kRebindDirtyMask;
+}
+
+
+void Animation::RebuildStateForEverything ()
+{
+ PROFILER_AUTO(gBuildAnimationState, this)
+
+ //@TODO: we should make sure that the curves are aligned for better cache utilization
+ // -> Simply modify curveIDLookup::iterator->second to be in the right cache order, after building the curveIDLookup!
+ // Possibly caching is already pretty good if the first curve contains all curves!
+
+ AnimationBinder::CurveIDLookup curveIDLookup;
+ AnimationBinder::InitCurveIDLookup(curveIDLookup);
+
+ /// Build curveIDLookup
+ /// * walk through all animation states and insert the curve into curveIDLookup
+ /// * curveCount gets increased when we encounter a new curve
+ CurveID curveID ("", 0, NULL, "", 0);
+ AnimationState* state;
+ AnimationBinder::CurveIDLookup::iterator found;
+
+ string attribute;
+
+ Transform* transform = QueryComponent (Transform);
+
+ if (transform)
+ {
+ for (int s=0;s<m_AnimationStates.size();s++)
+ {
+ state = m_AnimationStates[s];
+ AnimationClip* clip = state->m_Clip;
+ if (clip == NULL)
+ continue;
+
+ InsertAnimationClipCurveIDs(curveIDLookup, *clip);
+ }
+
+ // Bind the global curves.
+ GetAnimationBinder().BindCurves(curveIDLookup, *transform, m_BoundCurves, m_CachedAffectedSendToRootTransform, m_CachedTransformMessageMask);
+
+ // This will compact any curves that couldn't be bound
+ // - Faster at runtime (Can remove some ifs)
+ // - Slower at load time
+ AnimationBinder::RemoveUnboundCurves(curveIDLookup, m_BoundCurves);
+
+
+ /// * Build state.m_Curves arrays
+ /// - We go through all curves in the every state/clip look it up in the curve map
+ for (int s=0;s<m_AnimationStates.size();s++)
+ {
+ state = m_AnimationStates[s];
+ // Initialize all states to have no curves assigned
+ // Those that aren't used or excluded for mixing will remain null after this loop
+ state->CleanupCurves();
+ state->AllocateCurves(curveIDLookup.size());
+
+ AnimationClip* clip = state->m_Clip;
+ if (clip == NULL)
+ continue;
+
+ CalculateAnimationClipCurves(curveIDLookup, *clip, m_BoundCurves, *state);
+ }
+ }
+
+ // Force Bound curves mask recalculation
+ m_ActiveAnimationStatesSize = 0;
+
+ m_DirtyMask &= ~kRebindDirtyMask;
+}
+
+inline float InverseWeight(float weight)
+{
+ return weight > kReallySmallWeight ? (1.0F / weight) : 0.0F;
+}
+
+/*** This calculates the blend weights for one curve.
+
+Weights are distributed so that the top layer gets everything.
+If it doesn't use the full weight then the next layer gets to distribute the remaining
+weights and so on. Once all weights are used by the top layers,
+no weights will be available for lower layers anymore
+We use fair weighting, which means if a lower layer wants 80% and 50% have already been used up, the layer will NOT use up all weights.
+instead it will take up 80% of the 50%.
+
+Example:
+a upper body which is affected by wave, walk and idle
+a lower body which is affected by only walk and idle
+
+weight name layer lower upper
+ 20% wave 2 0% 20%
+ 50% walk 1 50% 40%
+ 100% idle 0 50% 40%
+
+- Blend weights can change per animated value because of mixing.
+ Even without mixing, sometimes a curve is just not defined. Still you want the blend weights to add up to 1.
+ Most of the time weights are similar between curves. So there is a lot of caching one can do.
+*/
+
+// We use fair weighting, which means if a lower layer wants 80% and 50% have already been used up, the layer will NOT use up all weights.
+// Instead it will take up 80% of the 50%.
+#define FAIR_DISTRIBUTION 1
+
+template<bool optimize32States>
+void CalculateWeights( AnimationState** states, int stateCount, int curveIndex, OUTPUT_OPTIONAL float* outWeights, int mask )
+{
+ Assert( outWeights != NULL );
+
+ // state index -> layer index
+ int* layerIndices;
+ ALLOC_TEMP(layerIndices, int, stateCount);
+ // summed weights for each layer - we'll never have more layers than affectors
+ float* layerSummedWeights;
+ ALLOC_TEMP(layerSummedWeights, float, stateCount);
+
+ const AnimationState* state = states[0];
+ int prevLayer = state->GetLayer();
+ int layerIndex = 0;
+
+ // Clear summed layer weights
+ for( int i = 0; i < stateCount; ++i )
+ layerSummedWeights[i] = 0.0F;
+
+ // sum weights for each layer
+ UInt32 stateBit = 1;
+ for( int i = 0; i < stateCount; ++i )
+ {
+ if (optimize32States)
+ {
+ if (mask & stateBit)
+ {
+ state = states[i];
+
+ AssertIf( prevLayer < state->GetLayer()); // Algorithm requires sorted states by layer
+
+ if( prevLayer != state->GetLayer() )
+ ++layerIndex;
+
+ layerSummedWeights[layerIndex] += state->GetWeight();
+ layerIndices[i] = layerIndex;
+ outWeights[i] = state->GetWeight();
+ }
+ else
+ {
+ outWeights[i] = 0.0F;
+ layerIndices[i] = 0;
+ }
+ }
+ else
+ {
+ state = states[i];
+ if (state->ShouldUse() && state->GetCurves()[curveIndex] != NULL && state->GetBlendMode() == AnimationState::kBlend)
+ {
+ AssertIf( prevLayer < state->GetLayer()); // Algorithm requires sorted states by layer
+
+ if( prevLayer != state->GetLayer() )
+ ++layerIndex;
+
+ layerSummedWeights[layerIndex] += state->GetWeight();
+ layerIndices[i] = layerIndex;
+ outWeights[i] = state->GetWeight();
+ }
+ else
+ {
+ outWeights[i] = 0.0F;
+ layerIndices[i] = 0;
+ }
+ }
+ stateBit <<= 1;
+ prevLayer = states[i]->GetLayer();
+ }
+ int layerCount = layerIndex + 1;
+
+ // Distribute weights so that the top layers get everything up to 1.
+ // If they use less, the remainder goes to the lower layers.
+ float* layerInvSummedWeights;
+ ALLOC_TEMP(layerInvSummedWeights, float, stateCount);
+ float remainderWeight = 1.0F;
+ for( int i = 0; i < layerCount; ++i )
+ {
+ float layerWeight = max(1.0F, layerSummedWeights[i]);
+
+ layerInvSummedWeights[i] = InverseWeight(layerWeight) * remainderWeight;
+
+ #if FAIR_DISTRIBUTION
+ remainderWeight -= layerSummedWeights[i] * remainderWeight;
+ #else
+ remainderWeight -= layerSummedWeights[i];
+ #endif
+
+ remainderWeight = max(0.0F, remainderWeight);
+ }
+
+ // - Apply the layer inverse weights
+ // - Normalize the weights once again, just in case
+
+ // @TODO: Renormalization is only necessary if the remainderWeight is larger than zero as far as i can see
+ float summedWeight = 0.0F;
+ for( int i = 0; i < stateCount; ++i )
+ {
+ outWeights[i] *= layerInvSummedWeights[layerIndices[i]];
+ summedWeight += outWeights[i];
+ }
+
+ summedWeight = InverseWeight(summedWeight);
+ for( int i = 0; i < stateCount; ++i )
+ outWeights[i] *= summedWeight;
+}
+
+void Animation::SampleDefaultClip (double time)
+{
+ AnimationClip* clip = m_Animation;
+ if (!clip)
+ return;
+
+ SampleAnimation(GetGameObject(), *clip, time, CombineWrapMode(clip->GetWrapMode(), m_WrapMode));
+}
+
+// Calculates a bitmask for every bound curve, containing which curve state affects it.
+// - The recalculation is only necessary if a state got enabled or disabled (Either through manually disabling it or through a too low blend weight)
+bool Animation::RebuildBoundStateMask()
+{
+ int activeStateCount = 0;
+ bool requireRebuild = false;
+ int s;
+ for (s=0;s<m_AnimationStates.size() && activeStateCount < 32;s++)
+ {
+ AnimationState& state = *m_AnimationStates[s];
+ if (state.ShouldUse() && state.GetBlendMode() == AnimationState::kBlend)
+ {
+ requireRebuild |= m_ActiveAnimationStates[activeStateCount] != &state;
+ m_ActiveAnimationStates[activeStateCount] = &state;
+ DebugAssertIf(activeStateCount != 0 && m_ActiveAnimationStates[activeStateCount-1]->GetLayer() < m_ActiveAnimationStates[activeStateCount]->GetLayer());
+ activeStateCount++;
+ }
+ }
+
+ // Too many active animation states
+ if (s != m_AnimationStates.size())
+ return false;
+
+ requireRebuild |= activeStateCount != m_ActiveAnimationStatesSize;
+ // early out if nothing in which animations are currently playing has changed.
+ if (!requireRebuild)
+ return true;
+
+ m_ActiveAnimationStatesSize = activeStateCount;
+
+ for (int i=0;i<m_BoundCurves.size();i++)
+ {
+ m_BoundCurves[i].affectedStateMask = 0;
+ for (int s=0;s<m_ActiveAnimationStatesSize;s++)
+ {
+ AnimationState& state = *m_ActiveAnimationStates[s];
+ if (state.m_Curves[i])
+ m_BoundCurves[i].affectedStateMask |= 1 << s;
+ }
+ }
+
+ return true;
+}
+
+inline void AwakeAndDirty (Object* o)
+{
+ if (o)
+ {
+ o->AwakeFromLoad(kDefaultAwakeFromLoad);
+ o->SetDirty();
+ }
+}
+
+static void UpdateLastNonTransformObject(Object*& lastNonTransformObject, Object* const targetObject)
+{
+ if (lastNonTransformObject != targetObject)
+ {
+ AwakeAndDirty(lastNonTransformObject);
+ lastNonTransformObject = targetObject;
+ }
+}
+
+// Optimized for 32 animation states
+void Animation::BlendOptimized()
+{
+ AssertIf(m_BoundCurves.empty());
+
+ ///@TODO: Keep a cache for all curves and pass it into animationcurve.Evaluate.
+ // So that many characters dont kill the cache of shared animation curves.
+
+ int curveCount = m_BoundCurves.size();
+
+ AnimationState** activeStates = m_ActiveAnimationStates;
+ int stateSize = m_ActiveAnimationStatesSize;
+ float* weights;
+ ALLOC_TEMP(weights, float, stateSize);
+
+ float weight;
+ const AnimationState* state;
+
+ Object* lastNonTransformObject = NULL;
+ UInt32 lastAffectedStateMask = m_BoundCurves[0].affectedStateMask;
+ CalculateWeights<true>(activeStates, stateSize, 0, weights, lastAffectedStateMask);
+ BoundCurveDeprecated* boundCurves = &m_BoundCurves[0];
+
+ for( int c = 0; c < curveCount; ++c )
+ {
+ BoundCurveDeprecated& bind = boundCurves[c];
+
+ // Only recalculate weights if the state mask changes!
+ // This happens very rarely. (If you dont use mixing it never happens)
+ if (lastAffectedStateMask != bind.affectedStateMask)
+ {
+ lastAffectedStateMask = bind.affectedStateMask;
+ CalculateWeights<true>(activeStates, stateSize, c, weights, lastAffectedStateMask);
+ }
+
+ if (lastAffectedStateMask == 0)
+ continue;
+
+ const UInt32 targetType = bind.targetType;
+
+ // Sample quaternion
+ if (targetType == kBindTransformRotation)
+ {
+ Prefetch(bind.targetPtr);
+ Quaternionf& result = *(Quaternionf*)bind.targetPtr;
+ result.Set(0,0,0,0);
+ UInt32 stateBit = 1;
+ for (int i=0;i<stateSize;i++)
+ {
+ if (lastAffectedStateMask & stateBit)
+ {
+ state = activeStates[i];
+ const AnimationCurveQuat* quatCurve = reinterpret_cast<AnimationCurveQuat*>(state->GetCurves()[c]);
+
+ Quaternionf sample = quatCurve->EvaluateClamp(state->m_WrappedTime);
+ DebugAssertIf(!IsFinite(sample));
+
+ /// Not necessary because we make sure when sampling that our curves are sampled and almost normalized
+ /// @todo: If people animate inside unity this might be a problem.
+ /// Hopefully no one does that in combination with blending.
+ // sample = NormalizeFastEpsilonZero(sample);
+ result += Sign(Dot (sample, result)) * sample * weights[i];
+ }
+ stateBit <<= 1;
+ }
+
+ result = NormalizeSafe(result);
+ DebugAssertIf(!IsFinite(result));
+ }
+ // Sample vector 3
+ else if (targetType == kBindTransformPosition)
+ {
+ Vector3f result = Vector3f(0.0F, 0.0F, 0.0F);
+ UInt32 stateBit = 1;
+ for (int i=0;i<stateSize;i++)
+ {
+ if (lastAffectedStateMask & stateBit)
+ {
+ state = activeStates[i];
+ const AnimationCurveVec3* vec3Curve = reinterpret_cast<AnimationCurveVec3*>(state->GetCurves()[c]);
+
+ Vector3f sample = vec3Curve->EvaluateClamp(state->m_WrappedTime);
+ weight = weights[i];
+ result += sample * weight;
+ }
+ stateBit <<= 1;
+ }
+
+ DebugAssertIf(!IsFinite(result));
+ *reinterpret_cast<Vector3f*>(bind.targetPtr) = result;
+ }
+ else if (targetType == kBindTransformScale)
+ {
+ Vector3f result = Vector3f(0.0F, 0.0F, 0.0F);
+ UInt32 stateBit = 1;
+ for (int i=0;i<stateSize;i++)
+ {
+ if (lastAffectedStateMask & stateBit)
+ {
+ state = activeStates[i];
+ const AnimationCurveVec3* vec3Curve = reinterpret_cast<AnimationCurveVec3*>(state->GetCurves()[c]);
+
+ Vector3f sample = vec3Curve->EvaluateClamp(state->m_WrappedTime);
+ weight = weights[i];
+ result += sample * weight;
+ }
+ stateBit <<= 1;
+ }
+
+ DebugAssertIf(!IsFinite(result));
+ *reinterpret_cast<Vector3f*>(bind.targetPtr) = result;
+ Transform* targetTransform = reinterpret_cast<Transform*> (bind.targetObject);
+ targetTransform->RecalculateTransformType ();
+ }
+ // Sample float
+ else if (targetType > kUnbound)
+ {
+ float result = 0.0F;
+ UInt32 stateBit = 1;
+ for (int i=0;i<stateSize;i++)
+ {
+ if (lastAffectedStateMask & stateBit)
+ {
+ state = activeStates[i];
+ const AnimationCurve* floatCurve = reinterpret_cast<AnimationCurve*>(state->GetCurves()[c]);
+
+ float sample = floatCurve->EvaluateClamp(state->m_WrappedTime);
+ weight = weights[i];
+ result += sample * weight;
+ }
+ stateBit <<= 1;
+ }
+
+ DebugAssertIf(!IsFinite(result));
+
+ AnimationBinder::SetFloatValue(bind, result);
+
+ if (AnimationBinder::ShouldAwakeGeneric (bind))
+ UpdateLastNonTransformObject(lastNonTransformObject, bind.targetObject);
+
+ }
+ else
+ {
+ #if COMPACT_UNBOUND_CURVES
+ AssertString("Unbound curves should be compacted!");
+ #endif
+ continue;
+ }
+
+ }
+
+ AwakeAndDirty(lastNonTransformObject);
+}
+
+
+// Unlimited amount of animation states
+void Animation::BlendGeneric()
+{
+ AssertIf(m_BoundCurves.empty());
+
+ ///@TODO: Keep a cache for all curves and pass it into animationcurve.Evaluate.
+ // So that many characters dont kill the cache of shared animation curves.
+
+ int curveCount = m_BoundCurves.size();
+
+ int stateSize = m_AnimationStates.size();
+ float* weights;
+ ALLOC_TEMP(weights, float, stateSize);
+
+ float weight;
+ const AnimationState* state;
+
+ Object* lastNonTransformObject = NULL;
+ for( int c = 0; c < curveCount; ++c )
+ {
+ BoundCurveDeprecated& bind = m_BoundCurves[c];
+
+ CalculateWeights<false>(&m_AnimationStates[0], stateSize, c, weights, 0);
+
+ bool didSample = false;
+
+ int targetType = bind.targetType;
+
+ // Sample quaternion
+ if (targetType == kBindTransformRotation)
+ {
+ Quaternionf result = Quaternionf(0.0F, 0.0F, 0.0F, 0.0F);
+
+ for (int i=0;i<stateSize;i++)
+ {
+ state = m_AnimationStates[i];
+ const AnimationCurveQuat* quatCurve = reinterpret_cast<AnimationCurveQuat*>(state->GetCurves()[c]);
+ if (quatCurve && weights[i] > kReallySmallWeight)
+ {
+ Quaternionf sample = quatCurve->EvaluateClamp(state->m_WrappedTime);
+
+ /// Not necessary because we make sure when sampling that our curves are sampled and almost normalized
+ /// @todo: If people animate inside unity this might be a problem.
+ /// Hopefully no one does that in combination with blending.
+
+ // sample = NormalizeFastEpsilonZero(sample);
+ weight = weights[i];
+ if (Dot (sample, result) < 0.0F)
+ weight = -weight;
+ result += sample * weight;
+ didSample = true;
+ }
+ }
+
+ result = NormalizeSafe(result);
+ DebugAssertIf(!IsFinite(result));
+ if (didSample)
+ *reinterpret_cast<Quaternionf*>(bind.targetPtr) = result;
+ }
+ // Sample vector 3
+ else if (targetType == kBindTransformPosition)
+ {
+ Vector3f result = Vector3f(0.0F, 0.0F, 0.0F);
+ for (int i=0;i<stateSize;i++)
+ {
+ state = m_AnimationStates[i];
+ const AnimationCurveVec3* vec3Curve = reinterpret_cast<AnimationCurveVec3*>(state->GetCurves()[c]);
+ if (vec3Curve && weights[i] > kReallySmallWeight)
+ {
+ Vector3f sample = vec3Curve->EvaluateClamp(state->m_WrappedTime);
+ weight = weights[i];
+ result += sample * weight;
+ didSample = true;
+ }
+ }
+
+ DebugAssertIf(!IsFinite(result));
+ if (didSample)
+ {
+ *reinterpret_cast<Vector3f*>(bind.targetPtr) = result;
+ }
+ }
+ // Sample vector 3
+ else if (targetType == kBindTransformScale)
+ {
+ Vector3f result = Vector3f(0.0F, 0.0F, 0.0F);
+ for (int i=0;i<stateSize;i++)
+ {
+ state = m_AnimationStates[i];
+ const AnimationCurveVec3* vec3Curve = reinterpret_cast<AnimationCurveVec3*>(state->GetCurves()[c]);
+ if (vec3Curve && weights[i] > kReallySmallWeight)
+ {
+ Vector3f sample = vec3Curve->EvaluateClamp(state->m_WrappedTime);
+ weight = weights[i];
+ result += sample * weight;
+ didSample = true;
+ }
+ }
+
+ DebugAssertIf(!IsFinite(result));
+
+ if (didSample)
+ {
+ *reinterpret_cast<Vector3f*>(bind.targetPtr) = result;
+ Transform* targetTransform = reinterpret_cast<Transform*> (bind.targetObject);
+ targetTransform->RecalculateTransformType ();
+ }
+ }
+ // Sample float
+ else if (targetType > kUnbound)
+ {
+ float result = 0.0F;
+ for (int i=0;i<stateSize;i++)
+ {
+ state = m_AnimationStates[i];
+ const AnimationCurve* floatCurve = reinterpret_cast<AnimationCurve*>(state->GetCurves()[c]);
+ if (floatCurve && weights[i] > kReallySmallWeight)
+ {
+ float sample = floatCurve->EvaluateClamp(state->m_WrappedTime);
+ weight = weights[i];
+ result += sample * weight;
+ didSample = true;
+ }
+ }
+
+ DebugAssertIf(!IsFinite(result));
+
+ if (didSample)
+ {
+ AnimationBinder::SetFloatValue(bind, result);
+
+ if (AnimationBinder::ShouldAwakeGeneric (bind))
+ UpdateLastNonTransformObject(lastNonTransformObject, bind.targetObject);
+ }
+ }
+ else
+ {
+ #if COMPACT_UNBOUND_CURVES
+ AssertString("Unbound curves should be compacted!");
+ #endif
+ continue;
+ }
+
+ }
+
+ AwakeAndDirty(lastNonTransformObject);
+}
+
+void Animation::BlendAdditive()
+{
+ AssertIf(m_BoundCurves.empty());
+ int stateSize = m_AnimationStates.size();
+ AnimationState* state;
+
+ AnimationState** activeStates;
+ ALLOC_TEMP(activeStates, AnimationState*, stateSize);
+ int activeStateSize = 0;
+
+ ///@TODO: We can also compare if the current time matches the first frame, then the animation has no effect!
+ for (int i=0;i<stateSize;i++)
+ {
+ state = m_AnimationStates[i];
+ if (state->GetBlendMode() == AnimationState::kAdditive && state->ShouldUse ())
+ {
+ activeStates[activeStateSize] = state;
+ activeStateSize++;
+ }
+ }
+ // early out if we have no additive animations playing
+ if (activeStateSize == 0)
+ return;
+
+ int curveCount = m_BoundCurves.size();
+ BoundCurveDeprecated* boundCurves = &m_BoundCurves[0];
+// float* weights;
+// ALLOC_TEMP(weights, float, stateSize, kMemAnimation);
+// float* time;
+// ALLOC_TEMP(time, float, stateSize, kMemAnimation);
+
+ float weight;
+
+ Object* lastNonTransformObject = NULL;
+ for( int c = 0; c < curveCount; ++c )
+ {
+ BoundCurveDeprecated& bind = boundCurves[c];
+
+ bool didSample = false;
+
+ int targetType = bind.targetType;
+
+ // Sample quaternion
+ if (targetType == kBindTransformRotation)
+ {
+ Quaternionf result = *reinterpret_cast<Quaternionf*>(bind.targetPtr);
+
+ for (int i=0;i<activeStateSize;i++)
+ {
+ state = activeStates[i];
+ weight = clamp01 (state->GetWeight());
+ const AnimationCurveQuat* quatCurve = reinterpret_cast<AnimationCurveQuat*>(state->GetCurves()[c]);
+ if (quatCurve)
+ {
+ Quaternionf sample = Inverse(quatCurve->GetKey(0).value) * quatCurve->EvaluateClamp(state->m_WrappedTime);
+ sample = Lerp(Quaternionf::identity(), sample, weight);
+ result *= sample;
+ didSample = true;
+ }
+ }
+
+ result = NormalizeSafe(result);
+ DebugAssertIf(!IsFinite(result));
+ if (didSample)
+ *reinterpret_cast<Quaternionf*>(bind.targetPtr) = result;
+ }
+ // Sample vector 3
+ else if (targetType == kBindTransformPosition)
+ {
+ Vector3f result = *reinterpret_cast<Vector3f*>(bind.targetPtr);
+
+ for (int i=0;i<activeStateSize;i++)
+ {
+ state = activeStates[i];
+ weight = clamp01 (state->GetWeight());
+ const AnimationCurveVec3* vec3Curve = reinterpret_cast<AnimationCurveVec3*>(state->GetCurves()[c]);
+
+ if (vec3Curve)
+ {
+ Vector3f sample = vec3Curve->EvaluateClamp(state->m_WrappedTime) - vec3Curve->GetKey(0).value;
+ result += sample * weight;
+ didSample = true;
+ }
+ }
+ DebugAssertIf(!IsFinite(result));
+ if (didSample)
+ *reinterpret_cast<Vector3f*>(bind.targetPtr) = result;
+ }
+ // Sample vector 3
+ else if (targetType == kBindTransformScale)
+ {
+ Vector3f result = *reinterpret_cast<Vector3f*>(bind.targetPtr);
+
+ for (int i=0;i<activeStateSize;i++)
+ {
+ state = activeStates[i];
+ weight = clamp01 (state->GetWeight());
+ const AnimationCurveVec3* vec3Curve = reinterpret_cast<AnimationCurveVec3*>(state->GetCurves()[c]);
+
+ if (vec3Curve)
+ {
+ Vector3f sample = vec3Curve->EvaluateClamp(state->m_WrappedTime) - vec3Curve->GetKey(0).value;
+ result += sample * weight;
+ didSample = true;
+ }
+ }
+ DebugAssertIf(!IsFinite(result));
+ if (didSample)
+ {
+ *reinterpret_cast<Vector3f*>(bind.targetPtr) = result;
+ Transform* targetTransform = reinterpret_cast<Transform*> (bind.targetObject);
+ targetTransform->RecalculateTransformType ();
+ }
+ }
+ }
+
+ AwakeAndDirty(lastNonTransformObject);
+}
+
+inline Animation::CullingType RemapDeprecatedCullingType (Animation::CullingType type)
+{
+ if (type == Animation::kDeprecatedCulling_BasedOnClipBounds || type == Animation::kDeprecatedCulling_BasedOnUserBounds)
+ return Animation::kCulling_BasedOnRenderers;
+ else
+ return type;
+}
+
+void Animation::SetCullingType(CullingType type)
+{
+ type = RemapDeprecatedCullingType(type);
+
+ CheckIsCullingBasedOnBoundsDeprecated ();
+
+ // Clearing culling related data
+ if (m_CullingType == kCulling_BasedOnRenderers)
+ {
+ ClearContainedRenderers();
+ }
+ else if (m_CullingType == kCulling_AlwaysAnimate)
+ RemoveFromManager();
+
+ m_CullingType = type;
+
+ // Building new culling data
+ if (m_CullingType == kCulling_BasedOnRenderers && !m_AnimationStates.empty())
+ RecomputeContainedRenderers ();
+ else if (m_CullingType == kCulling_AlwaysAnimate && !m_AnimationManagerNode.IsInList())
+ AddToManager();
+
+ SetDirty();
+}
+
+void Animation::RemoveContainedRenderer (Renderer* renderer)
+{
+ ContainedRenderers::iterator end = m_ContainedRenderers.end();
+ for (ContainedRenderers::iterator i = m_ContainedRenderers.begin();i != end;++i)
+ {
+ Renderer* cur = *i;
+ if (cur == renderer)
+ {
+ *i = m_ContainedRenderers.back();
+ m_ContainedRenderers.resize(m_ContainedRenderers.size() - 1);
+ return;
+ }
+ }
+}
+
+static void AnimationVisibilityCallback (void* userData, void* senderUserData, int visibilityEvent)
+{
+ Animation& animation = *reinterpret_cast<Animation*> (userData);
+
+ if (visibilityEvent == kBecameVisibleEvent)
+ animation.SetVisibleRenderers(true);
+ else if (visibilityEvent == kBecameInvisibleEvent)
+ animation.CheckRendererVisibleState ();
+ else if (visibilityEvent == kWillDestroyEvent)
+ {
+ animation.RemoveContainedRenderer(reinterpret_cast<Renderer*> (senderUserData));
+
+ animation.CheckRendererVisibleState ();
+ }
+}
+
+void Animation::ClearContainedRenderers ()
+{
+ ContainedRenderers::iterator end = m_ContainedRenderers.end();
+ for (ContainedRenderers::iterator i = m_ContainedRenderers.begin();i != end;++i)
+ {
+ Renderer* renderer = *i;
+ renderer->RemoveEvent(AnimationVisibilityCallback, this);
+ }
+ m_ContainedRenderers.clear();
+}
+
+void Animation::RecomputeContainedRenderers ()
+{
+ Assert(m_CullingType == kCulling_BasedOnRenderers);
+
+ ClearContainedRenderers ();
+
+ Transform& transform = GetComponent (Transform);
+ RecomputeContainedRenderersRecurse(transform);
+
+ Assert(m_CullingType == kCulling_BasedOnRenderers);
+
+ CheckRendererVisibleState ();
+}
+
+void Animation::CheckRendererVisibleState ()
+{
+ Assert(m_CullingType == kCulling_BasedOnRenderers);
+
+ ContainedRenderers::iterator end = m_ContainedRenderers.end();
+ for (ContainedRenderers::iterator i = m_ContainedRenderers.begin();i != end;++i)
+ {
+ Renderer* renderer = *i;
+ Assert(renderer->HasEvent(AnimationVisibilityCallback, this));
+ if (renderer->IsVisibleInScene())
+ {
+ SetVisibleRenderers(true);
+ return;
+ }
+ }
+
+ SetVisibleRenderers(false);
+}
+
+void Animation::SetVisibleInternal(bool visible)
+{
+ Assert(m_CullingType != kCulling_AlwaysAnimate);
+ m_Visible = visible;
+
+ if (IsWorldPlaying())
+ {
+ const bool wasAttached = m_AnimationManagerNode.IsInList();
+ // Method is called AddToManager, but it does removal too...
+ AddToManager();
+
+ // Culling is after AnimationManager Update,
+ // so when we pop into visibility we should make sure we are rendering the right frame!
+ if (m_AnimationManagerNode.IsInList() && wasAttached == false)
+ UpdateAnimation(GetCurTime());
+ }
+}
+
+
+void Animation::SetVisibleRenderers(bool visible)
+{
+ //LogString(Format("SetVisibleRenderers %d %s", visible ? 1 : 0, GetGameObject().GetName()));
+
+ Assert(m_CullingType == kCulling_BasedOnRenderers);
+ SetVisibleInternal(visible);
+}
+
+void Animation::SetVisibleBounds(bool visible)
+{
+ //LogString(Format("SetVisibleBounds %d %s", visible ? 1 : 0, GetGameObject().GetName()));
+
+ CheckIsCullingBasedOnBoundsDeprecated ();
+ SetVisibleInternal(visible);
+}
+
+///@TODO: We must ensure that there are not two animation components in a hierarchy!!!. Otherwise SetAnimationPtr will break!
+
+void Animation::RecomputeContainedRenderersRecurse (Transform& transform)
+{
+ Renderer* renderer = transform.QueryComponent(Renderer);
+ if (renderer)
+ {
+ m_ContainedRenderers.push_back(renderer);
+ renderer->AddEvent(AnimationVisibilityCallback, this);
+ }
+ Transform::iterator end = transform.end();
+ for (Transform::iterator i = transform.begin();i != end;++i)
+ {
+ RecomputeContainedRenderersRecurse(**i);
+ }
+}
+
+void Animation::SendTransformChangedToCachedTransform()
+{
+ int physicsMask = m_AnimatePhysics ? Transform::kAnimatePhysics : 0;
+ int size = m_CachedAffectedSendToRootTransform.size();
+ for (int i=0;i<size;i++)
+ {
+ m_CachedAffectedSendToRootTransform[i]->SendTransformChanged(m_CachedTransformMessageMask | physicsMask);
+ }
+}
+
+void Animation::SyncLayerTime (int layer)
+{
+ float normalizedSpeed = 0.0F;
+ float normalizedTime = 0.0F;
+ float summedLayerWeight = 0.0F;
+
+ for (AnimationStates::iterator i=m_AnimationStates.begin();i!=m_AnimationStates.end();i++)
+ {
+ AnimationState& state = **i;
+ if (state.GetLayer() != layer || !state.GetEnabled())
+ continue;
+
+ float weight = max(state.GetWeight(), 0.0F);
+ normalizedSpeed += state.GetNormalizedSpeed() * weight;
+ normalizedTime += state.GetNormalizedTime() * weight;
+ summedLayerWeight += weight;
+ }
+
+ if (summedLayerWeight > kReallySmallWeight)
+ {
+ normalizedSpeed = normalizedSpeed / summedLayerWeight;
+ normalizedTime = normalizedTime / summedLayerWeight;
+
+ for (AnimationStates::iterator i=m_AnimationStates.begin();i!=m_AnimationStates.end();i++)
+ {
+ AnimationState& state = **i;
+ if (state.GetLayer() != layer || !state.GetEnabled())
+ continue;
+
+ state.SetNormalizedSyncedSpeed(normalizedSpeed);
+ state.SetNormalizedTime(normalizedTime);
+ }
+ }
+}
+
+void Animation::Sample ()
+{
+ bool needsUpdate = false;
+
+ // Update animation state
+ for (int i=0;i<m_AnimationStates.size();i++)
+ {
+ AnimationState& state = *m_AnimationStates[i];
+
+ // Do we actually use any of the animation states?
+ if (state.ShouldUse())
+ needsUpdate = true;
+
+ m_DirtyMask |= state.GetDirtyMask();
+ state.ClearDirtyMask();
+ }
+
+ if (needsUpdate)
+ SampleInternal();
+}
+
+void Animation::SampleInternal()
+{
+ PROFILER_AUTO(gSampleAnimation, this)
+
+ ValidateBoundCurves ();
+
+ if (m_DirtyMask != 0)
+ {
+ if (m_DirtyMask & kRebindDirtyMask)
+ RebuildStateForEverything();
+
+ if (m_DirtyMask & kLayersDirtyMask)
+ SortAnimationStates();
+ }
+
+ AssertIf(m_DirtyMask != 0);
+
+ if (!m_BoundCurves.empty())
+ {
+ if (RebuildBoundStateMask())
+ {
+ if (m_ActiveAnimationStatesSize != 0)
+ BlendOptimized();
+ }
+ else
+ {
+ /// More than 32 animation states active at the same time!
+ /// @TODO: 3.0 take this out. Possibly sort animation states by maximum weight.
+ /// Or maybe supporting more than 32 states at once is really pointless
+ BlendGeneric();
+ }
+
+ BlendAdditive();
+
+ SendTransformChangedToCachedTransform();
+ }
+}
+
+void Animation::UpdateAnimation (double time)
+{
+ if (AnimationState::UseUnity32AnimationFixes())
+ UpdateAnimationInternal(time);
+ else
+ UpdateAnimationInternal_Before32(time);
+}
+
+// Calculates remaining play-times for all animations and for specified layer
+static void GetQueueTimes(const Animation::AnimationStates& states, const int targetLayer, float& allQueueTime, float& layerQueueTime)
+{
+ allQueueTime = 0;
+ layerQueueTime = 0;
+
+ for (Animation::AnimationStates::const_iterator it = states.begin(), end = states.end(); it != end; ++it)
+ {
+ const AnimationState& state = **it;
+
+ if (state.GetEnabled())
+ {
+ const int layer = state.GetLayer();
+
+ const int wrapMode = state.GetWrapMode();
+ if (wrapMode != kDefaultWrapMode && wrapMode != kClamp)
+ {
+ // for "infinite" animations (Loop, Repeat, ClampForever) we mark layer as "occupied"
+ allQueueTime = std::numeric_limits<float>::infinity();
+ if (layer == targetLayer)
+ layerQueueTime = std::numeric_limits<float>::infinity();
+ }
+ else
+ {
+ const float dt = state.GetLength() - state.GetTime();
+ Assert(dt >= 0);
+
+ allQueueTime = std::max(allQueueTime, dt);
+ if (layer == targetLayer)
+ layerQueueTime = std::max(layerQueueTime, dt);
+ }
+ }
+ }
+}
+
+// This function starts Queued animations (if it's already time to start).
+// We always blend animations based on QueuedAnimation::fadeTime, i.e. it doesn't
+// matter if currently playing animation(s) will finish in shorter time we will blend
+// in the new one in QueuedAnimation::fadeTime. We leave up to a user to specify
+// sufficient blend times.
+void Animation::UpdateQueuedAnimations(bool& needsUpdate)
+{
+ int lastLayer = -1;
+ float allQueueTime, lastLayerQueueTime;
+ allQueueTime = lastLayerQueueTime = -1;
+
+ // Update queued animations
+ for (QueuedAnimations::iterator q = m_Queued.begin(); q != m_Queued.end(); )
+ {
+ const QueuedAnimation& qa = *q;
+ const float fadeTime = qa.fadeTime;
+
+ const int layer = qa.state->GetLayer();
+
+ bool startNow = false;
+ if (qa.mode == kStopAll)
+ {
+ // queuing after all animations
+
+ if (allQueueTime < 0)
+ {
+ // allQueueTime must to be recalculated
+ GetQueueTimes(m_AnimationStates, layer, allQueueTime, lastLayerQueueTime);
+ lastLayer = layer;
+ }
+
+ startNow = fadeTime >= allQueueTime;
+ }
+ else
+ {
+ // queuing after animations in specific layer
+
+ if (lastLayer != layer || lastLayerQueueTime < 0)
+ {
+ // lastLayerQueueTime must to be recalculated
+ GetQueueTimes(m_AnimationStates, layer, allQueueTime, lastLayerQueueTime);
+ lastLayer = layer;
+ }
+
+ startNow = fadeTime >= lastLayerQueueTime;
+ }
+
+ if (startNow)
+ {
+ // This crossfade logic is framerate specific. We know when this animation had
+ // to be started, so in theory we should advance time and blending value (and execute events),
+ // but we don't do that because it would be an over-complication.
+ AnimationState& state = *qa.state;
+
+ CrossFade(state, fadeTime, qa.mode, false);
+ q = m_Queued.erase(q);
+ needsUpdate = true;
+
+ Assert(state.GetEnabled());
+ // we need to recalculate queue times, because we just started an animation
+ allQueueTime = lastLayerQueueTime = -1;
+ }
+ else
+ ++q;
+ }
+}
+
+void Animation::UpdateQueuedAnimations_Before34(bool& needsUpdate)
+{
+ // Update queued animations
+ for (QueuedAnimations::iterator q = m_Queued.begin(); q != m_Queued.end(); )
+ {
+ const QueuedAnimation& qa = *q;
+ if ((qa.mode == kStopAll && !IsPlaying()) || (qa.mode != kStopAll && !IsPlayingLayer(qa.state->GetLayer())))
+ {
+ CrossFade(*qa.state, qa.fadeTime, qa.mode, false);
+ q = m_Queued.erase(q);
+ needsUpdate = true;
+ }
+ else
+ ++q;
+ }
+}
+
+void Animation::UpdateAnimationInternal(double time)
+{
+ PROFILER_AUTO(gUpdateAnimation, this)
+
+ bool needsUpdate = false;
+
+ // Sync animations
+ for (SyncedLayers::iterator sync=m_SyncedLayers.begin();sync != m_SyncedLayers.end();sync++)
+ SyncLayerTime(*sync);
+
+ int stoppedAnimationCount = 0;
+ AnimationState** stoppedAnimations = NULL;
+ ALLOC_TEMP(stoppedAnimations, AnimationState*, m_AnimationStates.size());
+
+ // Update animation state
+ for (int i=0;i<m_AnimationStates.size();)
+ {
+ AnimationState& state = *m_AnimationStates[i];
+
+ // Update state
+ if (state.GetEnabled())
+ {
+ if (state.UpdateAnimationState(time, *this))
+ {
+ if (!state.ShouldAutoCleanupNow())
+ stoppedAnimations[stoppedAnimationCount++] = &state;
+ }
+ }
+
+ // Do we actually use any of the animation states?
+ if (state.ShouldUse())
+ needsUpdate = true;
+
+ m_DirtyMask |= state.GetDirtyMask();
+ state.ClearDirtyMask();
+
+ // Cleanup queued animations that have finished playing
+ if (state.ShouldAutoCleanupNow())
+ {
+ delete &state;
+ m_DirtyMask |= kLayersDirtyMask;
+ m_AnimationStates.erase(m_AnimationStates.begin() + i);
+ m_ActiveAnimationStatesSize = 0;
+ }
+ else
+ {
+ i++;
+ }
+ }
+
+ if (AnimationState::UseUnity34AnimationFixes())
+ UpdateQueuedAnimations(needsUpdate);
+ else
+ UpdateQueuedAnimations_Before34(needsUpdate);
+
+ if (stoppedAnimationCount > 0)
+ {
+ for (int i = 0; i < stoppedAnimationCount; ++i)
+ stoppedAnimations[i]->SetupUnstoppedState();
+
+ needsUpdate = true;
+ }
+
+ // Only do blending if it is really necessary
+ if (needsUpdate)
+ {
+ SampleInternal();
+ }
+
+ for (int i = 0; i < stoppedAnimationCount; ++i)
+ stoppedAnimations[i]->CleanupUnstoppedState();
+}
+
+// This is for backwards compatibility. If you need to make changes,
+// then make them in UpdateAnimationInternal which is used with Unity 3.2 and later content
+void Animation::UpdateAnimationInternal_Before32(double time)
+{
+ PROFILER_AUTO(gUpdateAnimation, this)
+
+ bool needsUpdate = false;
+ int activeLastStateCount = 0;
+
+ // Sync animations
+ for (SyncedLayers::iterator sync=m_SyncedLayers.begin();sync != m_SyncedLayers.end();sync++)
+ SyncLayerTime(*sync);
+
+ AnimationState* stoppedAnimation = NULL;
+
+ // Update animation state
+ for (int i=0;i<m_AnimationStates.size();)
+ {
+ AnimationState& state = *m_AnimationStates[i];
+
+ if (state.ShouldUse())
+ activeLastStateCount++;
+
+ // Update state
+ if (state.GetEnabled())
+ {
+ if (state.UpdateAnimationState(time, *this))
+ {
+ if (!state.ShouldAutoCleanupNow())
+ stoppedAnimation = &state;
+ }
+ }
+
+ // Do we actually use any of the animation states?
+ if (state.ShouldUse())
+ needsUpdate = true;
+
+ m_DirtyMask |= state.GetDirtyMask();
+ state.ClearDirtyMask();
+
+ // Cleanup queued animations that have finished playing
+ if (state.ShouldAutoCleanupNow())
+ {
+ delete &state;
+ m_DirtyMask |= kLayersDirtyMask;
+ m_AnimationStates.erase(m_AnimationStates.begin() + i);
+ m_ActiveAnimationStatesSize = 0;
+ }
+ else
+ {
+ i++;
+ }
+ }
+
+ UpdateQueuedAnimations_Before34(needsUpdate);
+
+ bool revertWrappedTimeToBeforeStop = false;
+ if (activeLastStateCount == 1 && needsUpdate == false && stoppedAnimation)
+ {
+ stoppedAnimation->SetupUnstoppedState();
+ revertWrappedTimeToBeforeStop = true;
+ }
+
+ // Only do blending if it is really necessary
+ if (needsUpdate)
+ {
+ SampleInternal();
+ }
+
+ if (revertWrappedTimeToBeforeStop)
+ stoppedAnimation->CleanupUnstoppedState();
+}
+
+
+struct GreaterLayer : std::binary_function<AnimationState*, AnimationState*, std::size_t>
+{
+ bool operator () (AnimationState* lhs, AnimationState* rhs) const
+ {
+ if (lhs->GetLayer() != rhs->GetLayer())
+ return lhs->GetLayer() > rhs->GetLayer();
+ else
+ return lhs->GetName() > rhs->GetName();
+ }
+};
+
+void Animation::SortAnimationStates ()
+{
+ sort(m_AnimationStates.begin(), m_AnimationStates.end(), GreaterLayer());
+ m_DirtyMask &= ~kLayersDirtyMask;
+ m_ActiveAnimationStatesSize = 0;
+}
+
+void Animation::ReleaseAnimationStates ()
+{
+ for (AnimationStates::iterator i=m_AnimationStates.begin();i!=m_AnimationStates.end();i++)
+ {
+ delete *i;
+ }
+ m_AnimationStates.clear();
+}
+
+AnimationClip* Animation::GetClipLegacyWarning (AnimationClip* clip)
+{
+ if (clip == NULL)
+ return NULL;
+ else
+ {
+ if (clip->GetAnimationType () == AnimationClip::kLegacy)
+ return clip;
+ else
+ {
+ WarningStringObject(Format("The AnimationClip '%s' used by the Animation component '%s' must be marked as Legacy.", clip->GetName(), GetName()), clip);
+ return NULL;
+ }
+ }
+}
+
+
+void Animation::BuildAnimationStates()
+{
+ if (!m_AnimationStates.empty())
+ return;
+ if (m_Animations.empty ())
+ return;
+
+ PROFILER_AUTO(gBuildAnimationState, this)
+
+ ReleaseAnimationStates();
+
+ m_AnimationStates.reserve(m_Animations.size());
+
+ double time = GetCurTime();
+ for (int i=0;i<m_Animations.size();i++)
+ {
+ AnimationClip* clip = GetClipLegacyWarning(m_Animations[i]);
+ if (clip != NULL)
+ {
+ m_AnimationStates.push_back(new AnimationState());
+ m_AnimationStates.back()->Init(clip->GetName(), clip, time, CombineWrapMode(clip->GetWrapMode(), m_WrapMode));
+ }
+ }
+
+ if (m_CullingType == kCulling_BasedOnRenderers)
+ RecomputeContainedRenderers();
+
+ m_DirtyMask |= kRebindDirtyMask;
+
+ AddToManager();
+}
+
+AnimationState* Animation::CloneAnimation (AnimationState* state)
+{
+ // The animation state needs to be attached to this animation
+ if (GetState(state) == NULL)
+ return NULL;
+
+ PROFILER_AUTO(gCloneAnimationState, this)
+
+ // Clone the state and reference all it's bound curves
+ AnimationState* clone = new AnimationState();
+ clone->Init( state->GetName() + " - Queued Clone", state->GetClip(), GetCurTime(), state->GetWrapMode(), true );
+ clone->SetParentName( state->GetName() );
+ clone->SetLayer(state->GetLayer());
+ clone->SetClonedCurves(*state);
+ clone->ClearDirtyMask();
+ m_AnimationStates.push_back(clone);
+
+ m_DirtyMask |= kLayersDirtyMask;
+ return clone;
+}
+
+void Animation::AddClip (AnimationClip& clip, const std::string& newName, int firstFrame, int lastFrame, bool loop)
+{
+ PROFILER_AUTO (gAddClip, this)
+
+ if (GetClipLegacyWarning (&clip) == NULL)
+ return;
+
+ AnimationClip* newClip = &clip;
+ // Do we really need to create a duplicate clip?
+ if (loop || firstFrame != INT_MIN || lastFrame != INT_MAX || newName != clip.GetName())
+ {
+ newClip = NEW_OBJECT (AnimationClip);
+
+ CopySerialized(clip, *newClip);
+ newClip->SetName(newName.c_str());
+
+ if (loop || firstFrame != INT_MIN || lastFrame != INT_MAX)
+ {
+ // [case 504486] need to clear curve because function ClipAnimation() will add all these clipped curves from source clip.
+ newClip->ClearCurves();
+ ClipAnimation(clip, *newClip, FrameToTime(firstFrame, clip.GetSampleRate()), FrameToTime(lastFrame, clip.GetSampleRate()), loop);
+ }
+ }
+
+
+ // Replace clips with duplicate names
+ Animations::iterator i;
+ for (i=m_Animations.begin();i != m_Animations.end();i++)
+ {
+ AnimationClip* cur = *i;
+ if (cur && cur->GetName() == newName)
+ break;
+ }
+ if (i == m_Animations.end())
+ m_Animations.push_back(newClip);
+ else
+ *i = newClip;
+
+ if (!m_AnimationStates.empty())
+ {
+ m_DirtyMask |= kRebindDirtyMask;
+
+ // Remove states with duplicate names
+ for (AnimationStates::iterator s=begin();s != end();s++)
+ {
+ if ((**s).GetName() == newName)
+ {
+ delete *s;
+ m_AnimationStates.erase(s);
+ break;
+ }
+ }
+
+ m_AnimationStates.push_back(new AnimationState());
+ m_AnimationStates.back()->Init(newName, newClip, GetCurTime(), CombineWrapMode(newClip->GetWrapMode(), m_WrapMode));
+ }
+
+ CheckIsCullingBasedOnBoundsDeprecated();
+
+ SetDirty();
+}
+
+void Animation::AddClip (AnimationClip& clip)
+{
+ AddClip(clip, clip.GetName(), std::numeric_limits<int>::min(), std::numeric_limits<int>::max(), false);
+}
+
+void Animation::RemoveClip (AnimationClip& clip)
+{
+ PROFILER_AUTO(gRemoveClip, this)
+
+ // Find the clip to remove
+ // We might have the same clip multiple times in the animation list.
+ // We are removing elements, so we iterate over it backwards.
+ {
+ bool found = false;
+ int i = m_Animations.size();
+ while (i--)
+ {
+ AnimationClip* cur = m_Animations[i];
+ if (cur && cur == &clip)
+ {
+ found = true;
+ Animations::iterator j = m_Animations.begin() + i;
+ m_Animations.erase(j);
+ }
+ }
+ if (!found)
+ {
+ AssertStringObject (Format ("Unable to remove Animation Clip '%s' - clip not found in animation list", clip.GetName()), this);
+ return;
+ }
+ }
+
+ {
+ // Find the animation state(s) to remove
+ // We are removing elements, so we iterate over it backwards.
+ int i = m_AnimationStates.size();
+ while (i--)
+ {
+ if (m_AnimationStates[i]->m_Clip == &clip)
+ {
+ delete m_AnimationStates[i];
+ iterator j = m_AnimationStates.begin() + i;
+ m_AnimationStates.erase(j);
+ }
+ }
+ }
+
+ CheckIsCullingBasedOnBoundsDeprecated ();
+
+ m_DirtyMask |= kRebindDirtyMask;
+}
+
+void Animation::RemoveClip (const std::string &clipName)
+{
+ PROFILER_AUTO(gRemoveClip, this)
+
+ // Find the clip to remove
+ // We might have the same clip multiple times in the animation list.
+ // We are removing elements, so we iterate over it backwards.
+ {
+ bool found = false;
+ int i = m_Animations.size();
+ while (i--)
+ {
+ AnimationClip* cur = m_Animations[i];
+ if (cur && cur->GetName() == clipName)
+ {
+ found = true;
+ Animations::iterator j = m_Animations.begin() + i;
+ m_Animations.erase(j);
+ }
+ }
+ if (!found)
+ {
+ AssertStringObject (Format ("Unable to remove Animation Clip '%s' - clip not found in animation list", clipName.c_str()), this);
+ return;
+ }
+ }
+
+ {
+ // Find the animation state(s) to remove
+ // We are removing elements, so we iterate over it backwards.
+ int i = m_AnimationStates.size();
+ while (i--)
+ {
+ AnimationState *cur = m_AnimationStates[i];
+ if (cur && cur->GetName() == clipName)
+ {
+ delete cur;
+ iterator j = m_AnimationStates.begin() + i;
+ m_AnimationStates.erase(j);
+ }
+ }
+ }
+
+ CheckIsCullingBasedOnBoundsDeprecated();
+
+ m_DirtyMask |= kRebindDirtyMask;
+}
+
+int Animation::GetClipCount () const {
+ return m_Animations.size ();
+}
+
+AnimationState* Animation::GetState(const string& name)
+{
+ BuildAnimationStates();
+
+ for (AnimationStates::iterator i=m_AnimationStates.begin();i!=m_AnimationStates.end();i++)
+ {
+ AnimationState& state = **i;
+ if (state.m_Name == name)
+ return &state;
+ }
+ return NULL;
+}
+
+AnimationState* Animation::GetState(AnimationClip* clip)
+{
+ BuildAnimationStates();
+ for (iterator i=begin();i != end();i++)
+ {
+
+ if ((**i).GetClip() == clip)
+ return *i;
+ }
+ return NULL;
+}
+
+AnimationState* Animation::GetState(AnimationState* state)
+{
+ BuildAnimationStates();
+ for (iterator i=begin();i != end();i++)
+ {
+ if (*i == state)
+ return state;
+ }
+ return NULL;
+}
+
+void Animation::InitializeClass ()
+{
+ AnimationState::InitializeClass();
+ AnimationManager::InitializeClass();
+
+ RegisterAllowNameConversion("Animation", "m_PlayFixedFrameRate", "m_AnimatePhysics");
+ RegisterAllowNameConversion("Animation", "m_AnimateIfVisible", "m_AnimateOnlyIfVisible");
+}
+
+void Animation::CleanupClass ()
+{
+ AnimationState::CleanupClass();
+ AnimationManager::CleanupClass();
+}
+
+
+template<class TransferFunction>
+void Animation::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ transfer.SetVersion (3);
+
+#if UNITY_EDITOR
+ if (transfer.IsOldVersion(1))
+ {
+ TRANSFER_SIMPLE (m_Animation);
+ transfer.Transfer (m_OldAnimations, "m_Animations");
+ TRANSFER_SIMPLE (m_WrapMode);
+ TRANSFER_SIMPLE (m_PlayAutomatically);
+ transfer.Transfer (m_AnimatePhysics, "m_PlayFixedFrameRate");
+ return;
+ }
+#endif
+
+
+ TRANSFER_SIMPLE (m_Animation);
+ TRANSFER_SIMPLE (m_Animations);
+
+ // Hide the wrapmode in the inspector if the user has not changed it already... We are in the process of deprecating it
+#if UNITY_EDITOR
+ bool hide = (transfer.GetFlags () & kSerializeForInspector) != 0 && (transfer.GetFlags () & kSerializeDebugProperties) == 0 && m_WrapMode == 0;
+ transfer.Transfer (m_WrapMode, "m_WrapMode", hide ? kHideInEditorMask : kNoTransferFlags);
+#else
+ transfer.Transfer (m_WrapMode, "m_WrapMode", kNoTransferFlags);
+#endif
+
+ // In Unity 3.4 we switched to the m_CullingType enum. Previously we serialized animateOnlyIfVisible.
+ if (transfer.IsOldVersion(2))
+ {
+ bool animateOnlyIfVisible = false;
+ transfer.Transfer(animateOnlyIfVisible, "m_AnimateOnlyIfVisible");
+ m_CullingType = animateOnlyIfVisible ? kCulling_BasedOnRenderers : kCulling_AlwaysAnimate;
+ }
+
+ TRANSFER_SIMPLE (m_PlayAutomatically);
+ TRANSFER (m_AnimatePhysics);
+
+ transfer.Align();
+
+ // We hide these two fields here, because they are displayed by custom inspector (AnimationEditor)
+ TRANSFER_ENUM(m_CullingType);
+ if (transfer.IsReading())
+ m_CullingType = RemapDeprecatedCullingType(m_CullingType);
+
+
+ TRANSFER_DEBUG(m_AnimationStates);
+}
+
+/*
+
+ TODO:
+
+ * Allow for reimport animations while in playmode!
+
+- Make a list of all mesh users and invalidate them when the mesh changes!
+- implement all missing functions
+- Implement mixing properly (Based on which curves have changed)
+- support delay cross fade (Starts time advancing only when the animation has faded in completely )
+- Don't always add animation component to all game objects! But what if people want to animate a prefab???
+
+- Make the animation system store amount of loops and not one huge float for time?
+- Keep a cache for all curves and pass it into animationcurve.Evaluate.
+ So that many characters dont kill the cache of shared animation curves.
+
+ - automatically Reduce curves that dont affect anything!
+ 1. If all animation clips change that curve to the same value and all curves dont actually modify the value!
+ 2. The current state when loaded is the same as in all curves
+ 3. Only do it for animation clips that got imported from a fbx file
+ - Unity made animations are more likely to be modified by scripting as well
+ so it should not play any tricks on the user
+ 4. Have an option to turn the optimization off. eg someone wants to do IK etc.
+
+---
+- Handle bake simulation better when using clipped animations!
+- auto cleanup of queued animations
+- search for !IsWorldPlaying () those are hacks to get the timeline working
+- timeline doesnt work anymore!
+
+ - Store optimized animation curves in animation clip instead of animation state.
+ - CRASHBUG: Check when animated objects are removed -> rebuild
+ - CRASHBUG: Check when animations change -> rebuild
+
+- Remove normalize fast before blending, not necessary for sampled animations!
+ But what about unity made animations?
+
+
+///@TODO: EXPOSE AnimationState too!(cspreprocess fucks up at the moment)
+- implement manual clipping of animations from scripts
+- Handle stopping and fading out of animations!
+- Add named clip will fail when called at runtime
+
+*/
+
diff --git a/Runtime/Animation/Animation.h b/Runtime/Animation/Animation.h
new file mode 100644
index 0000000..e3091cc
--- /dev/null
+++ b/Runtime/Animation/Animation.h
@@ -0,0 +1,265 @@
+#ifndef ANIMATION_H
+#define ANIMATION_H
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "BoundCurveDeprecated.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Utilities/vector_set.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Camera/UnityScene.h"
+
+class AnimationState;
+class Transform;
+
+class AnimationClip;
+
+struct QueuedAnimation
+{
+ int mode;
+ int queue;
+ float fadeTime;
+
+ AnimationState* state;
+};
+
+class Animation : public Behaviour
+{
+public:
+ enum CullingType { kCulling_AlwaysAnimate, kCulling_BasedOnRenderers, kDeprecatedCulling_BasedOnClipBounds, kDeprecatedCulling_BasedOnUserBounds };
+
+public:
+ typedef UNITY_VECTOR(kMemAnimation, PPtr<AnimationClip>) Animations;
+ typedef dynamic_array<BoundCurveDeprecated> BoundCurves;
+ typedef UNITY_VECTOR(kMemAnimation, AnimationState*) AnimationStates;
+ typedef AnimationStates::iterator iterator;
+ typedef vector_set<int> SyncedLayers;
+ typedef UNITY_VECTOR(kMemAnimation, Renderer*) ContainedRenderers;
+ typedef UNITY_VECTOR(kMemAnimation, QueuedAnimation) QueuedAnimations;
+ typedef UNITY_VECTOR(kMemAnimation, Transform*) AffectedRootTransforms;
+
+ // Tag class as sealed, this makes QueryComponent faster.
+ static bool IsSealedClass () { return true; }
+
+private:
+ int m_WrapMode;///< enum { Default = 0, Once = 1, Loop = 2, PingPong = 4, ClampForever = 8 }
+ bool m_PlayAutomatically;
+ bool m_AnimatePhysics;
+ bool m_Visible;
+ CullingType m_CullingType; ///< enum { Always Animate = 0, Based On Renderers = 1 }
+
+ /// When we are animating transforms we cache the affect root transforms
+ /// (The top most transform that covers all SendTransformChanged messages that need to sent)
+ AffectedRootTransforms m_CachedAffectedSendToRootTransform;
+ int m_CachedTransformMessageMask;
+ ContainedRenderers m_ContainedRenderers;
+
+ // new stuff
+ BoundCurves m_BoundCurves;
+ AnimationStates m_AnimationStates;
+ AnimationState* m_ActiveAnimationStates[32];
+ int m_ActiveAnimationStatesSize;
+
+ UInt32 m_DirtyMask;
+ ListNode<Animation> m_AnimationManagerNode;
+ SyncedLayers m_SyncedLayers;
+
+ PPtr<AnimationClip> m_Animation;
+ Animations m_Animations;
+
+ QueuedAnimations m_Queued;
+
+#if UNITY_EDITOR
+ typedef std::vector<std::pair<UnityStr, PPtr<AnimationClip> > > OldAnimations;
+ OldAnimations m_OldAnimations;
+#endif
+
+ void PlayClip (AnimationClip& animation, int mode);
+ void RecomputeContainedRenderers ();
+ void RecomputeContainedRenderersRecurse (Transform& transform);
+ void ClearContainedRenderers ();
+ void SampleInternal();
+public:
+
+ REGISTER_DERIVED_CLASS (Animation, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (Animation)
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ Animation (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~Animation (); declared-by-macro
+
+ /// Are any animation states playing?
+ /// (Returns true even if the animation state has a zero blend weight)
+ /// (Unaffected by animation.enabled or animation.visible)
+ bool IsPlaying ();
+
+ /// Is the
+ /// (Returns true even if the animation state has a zero blend weight)
+ /// (Unaffected by animation.enabled or animation.visible)
+ bool IsPlaying (const string& name);
+
+ /// Is the animation state playing?
+ /// (Returns true even if the animation state has a zero blend weight)
+ /// (Unaffected by animation.enabled or animation.visible)
+ bool IsPlaying (const AnimationState& state);
+
+ /// Is any animation in the layer playing?
+ /// (Returns true even if the animation state has a zero blend weight)
+ /// (Unaffected by animation.enabled or animation.visible)
+ bool IsPlayingLayer (int layer);
+
+ /// Stops all animations that were started from this component with play or play named!
+ void Stop ();
+ /// Stops all animations that were started from this component with name!
+ void Stop (const string& name);
+ void Stop (AnimationState& state);
+
+
+ void Rewind ();
+ void Rewind (const string& name);
+ void Rewind (AnimationState& state);
+
+ void SyncLayer (int layer) { m_SyncedLayers.insert(layer); }
+
+ void SetWrapMode (int mode);
+ int GetWrapMode () { return m_WrapMode; }
+
+ PPtr<AnimationClip> GetClip () const { return m_Animation; }
+ void SetClip (PPtr<AnimationClip> anim);
+
+ const Animations& GetClips () const { return m_Animations; }
+ void SetClips (const Animations& anims);
+
+// PPtr<AnimationClip> GetNamedClip (const string& name);
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+ virtual void Deactivate (DeactivateOperation operation);
+
+ bool GetPlayAutomatically () const { return m_PlayAutomatically; }
+ void SetPlayAutomatically (bool b) { m_PlayAutomatically = b; SetDirty (); }
+
+ void SetCullingType(CullingType type);
+ CullingType GetCullingType() const { return m_CullingType; }
+
+ void CheckRendererVisibleState ();
+
+ // Exposed for Renderer
+ void SetVisibleRenderers(bool visible);
+ // Exposed for UnityScene
+ void SetVisibleBounds(bool visible);
+
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+
+ enum PlayMode { kStopSameLayer = 0, kPlayQueuedDeprecated = 1, kPlayMixedDeprecated = 2, kStopAll = 4 };
+// enum PlayMode { kStopAll = 0, kPlayQueued = 1, kPlayMixedDeprecated = 2 };
+
+ bool Play(const std::string& name, int playMode);
+ void Play(AnimationState& fadeIn, int playMode);
+ bool Play(int playMode);
+
+ ///
+ void Blend(const std::string& name, float targetWeight, float time);
+ void Blend(AnimationState& fadeIn, float targetWeight, float time);
+
+ void CrossFade(const std::string& name, float time, int mode);
+ void CrossFade(AnimationState& fadeIn, float time, int mode, bool clearQueuedAnimations );
+
+ enum QueueMode { CompleteOthers = 0, PlayNow = 2 };
+
+ AnimationState* QueueCrossFade(const std::string& name, float time, int queue, int mode);
+ AnimationState* QueueCrossFade(AnimationState& originalState, float time, int queue, int mode);
+
+ AnimationState* QueuePlay(const std::string& name, int queue, int mode) { return QueueCrossFade(name, 0.0F, queue, mode); }
+ AnimationState* QueuePlay(AnimationState& originalState, int queue, int mode) { return QueueCrossFade(originalState, 0.0F, queue, mode); }
+
+ /// Adds an animation clip with name newName to the animation.
+ /// - If newName is not the clip's name or the animation clip needs to be clipped a new instance of the clip will be created.
+ void AddClip (AnimationClip& clip, const std::string& newName, int firstFrame, int lastFrame, bool loop);
+
+ /// Adds an animation clip to the animation. If it already exists this will do nothing.
+ void AddClip (AnimationClip& clip);
+
+ /// Removes an named clip
+ void RemoveClip (AnimationClip& clip);
+ void RemoveClip (const std::string &clipName);
+
+ /// Get the number of clips in the animation
+ int GetClipCount () const;
+
+ void SyncLayerTime (int layer);
+
+ iterator begin () { return m_AnimationStates.begin(); }
+ iterator end () { return m_AnimationStates.end(); }
+
+ /// State management
+ AnimationState& GetAnimationStateAtIndex(int index) { BuildAnimationStates(); return *m_AnimationStates[index]; }
+ int GetAnimationStateCount () { BuildAnimationStates(); return m_AnimationStates.size(); }
+
+ AnimationState* GetState(const std::string& name);
+ AnimationState* GetState(AnimationClip* clip);
+ AnimationState* GetState(AnimationState* state);
+
+ AnimationState* CloneAnimation (AnimationState* state);
+
+ /// Returns the animation clip named name
+ /// - This will onyl search the serialized animation array and not touch animation states at all!
+ AnimationClip* GetClipWithNameSerialized (const std::string& name);
+
+ void UpdateAnimation (double time);
+ void SampleDefaultClip (double time);
+
+ void RebuildStateForEverything();
+ bool RebuildBoundStateMask();
+
+ void Sample();
+
+ void BlendOptimized();
+ void BlendGeneric();
+ void BlendAdditive();
+
+ void SortAnimationStates();
+ void ReleaseAnimationStates();
+
+ void ApplyObjectSlow(Object* lastObject);
+
+#if UNITY_EDITOR
+ void LoadOldAnimations (); // Deprecated with 1.5
+#endif
+ void SendTransformChangedToCachedTransform();
+ void RemoveContainedRenderer (Renderer* renderer);
+
+ void SetAnimatePhysics (bool anim);
+ bool GetAnimatePhysics () { return m_AnimatePhysics; }
+
+ void CleanupBoundCurves ();
+ void ValidateBoundCurves ();
+
+ void EnsureDefaultAnimationIsAdded ();
+
+ AABB GetLocalAABB () const { return AABB::zero; }
+ void SetLocalAABB (const AABB& aabb) { }
+
+ #if UNITY_EDITOR
+ /// Forces the inspector auto refresh without setdirty being called but only in debug mode
+ virtual bool HasDebugmodeAutoRefreshInspector () { return true; }
+ #endif
+
+private:
+
+ AnimationClip* GetClipLegacyWarning (AnimationClip* clip);
+
+ void CheckIsCullingBasedOnBoundsDeprecated() const { Assert(m_CullingType != kDeprecatedCulling_BasedOnClipBounds); Assert(m_CullingType != kDeprecatedCulling_BasedOnUserBounds); }
+ void SetVisibleInternal(bool visible);
+ void BuildAnimationStates();
+
+ void UpdateAnimationInternal_Before32(double time);
+ void UpdateAnimationInternal(double time);
+
+ void UpdateQueuedAnimations_Before34(bool& needsUpdate);
+ void UpdateQueuedAnimations(bool& needsUpdate);
+};
+
+#endif
diff --git a/Runtime/Animation/AnimationBinder.cpp b/Runtime/Animation/AnimationBinder.cpp
new file mode 100644
index 0000000..cd59062
--- /dev/null
+++ b/Runtime/Animation/AnimationBinder.cpp
@@ -0,0 +1,724 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "AnimationBinder.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferUtility.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Serialize/TypeTree.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Animation/Animator.h"
+#include "Runtime/Filters/Deformation/SkinnedMeshFilter.h"
+#include "Runtime/Animation/AnimatorController.h"
+#include "Runtime/mecanim/animation/avatar.h"
+#if ENABLE_MONO
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoScript.h"
+#endif
+#include "External/shaderlab/Library/FastPropertyName.h"
+#include "External/shaderlab/Library/properties.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+
+using namespace ShaderLab;
+using namespace std;
+
+void AnimationBinder::InitCurveIDLookup (AnimationBinder::CurveIDLookup& curveIDLookup)
+{
+ curveIDLookup.set_empty_key(CurveID("", -1, NULL, "", 0));
+ curveIDLookup.set_deleted_key(CurveID("", -1, NULL, "", 1));
+ curveIDLookup.resize(1024);
+}
+
+inline bool IsAnimatableProperty (const TypeTree* variable, bool isScript, Object* targetObject)
+{
+ if (variable && isScript)
+ {
+ #if ENABLE_MONO
+ MonoBehaviour* behaviour = static_cast<MonoBehaviour*> (targetObject);
+ MonoObject* instance = behaviour->GetInstance();
+ if (instance)
+ {
+ UInt32 offset = reinterpret_cast<UInt8*> (variable->m_DirectPtr) - reinterpret_cast<UInt8*> (instance);
+ UInt32 size = mono_class_instance_size(behaviour->GetClass());
+ return offset < size || variable->m_ByteOffset != -1;
+ }
+ #endif
+ return false;
+ }
+ else if (variable)
+ {
+ return variable->m_ByteOffset != -1;
+ }
+ else
+ return false;
+}
+
+AnimationBinder::~AnimationBinder ()
+{
+ for (TypeTreeCache::iterator i=m_TypeTreeCache.begin();i!=m_TypeTreeCache.end();i++)
+ delete i->second;
+}
+
+inline int GetAnimatableBindType (const TypeTree& variable)
+{
+ if (variable.m_Type == "float")
+ {
+ return kBindFloat;
+ }
+ else if (variable.m_Type == "bool" || (variable.m_Type == "UInt8" && (variable.m_MetaFlag & kEditorDisplaysCheckBoxMask)))
+ {
+ return kBindFloatToBool;
+ }
+ else if (variable.m_Type == "PPtr<Material>")
+ {
+ return kBindMaterialPPtrToRenderer;
+ }
+#if ENABLE_SPRITES
+ else if (variable.m_Type == "PPtr<Sprite>")
+ {
+ return kBindSpritePPtrToSpriteRenderer;
+ }
+#endif
+ else
+ return kUnbound;
+}
+
+
+#if UNITY_EDITOR
+bool AnimationBinder::IsAnimatablePropertyOrHasAnimatableChild (const TypeTree& variable, bool isScript, Object* targetObject)
+{
+ if (variable.m_Children.empty())
+ {
+ if ( IsAnimatableProperty(&variable, isScript, targetObject))
+ {
+ return GetAnimatableBindType(variable) != kUnbound;
+ }
+ }
+
+ for (TypeTree::const_iterator i=variable.begin();i!=variable.end();++i)
+ {
+ if (IsAnimatablePropertyOrHasAnimatableChild(*i, isScript, targetObject))
+ return true;
+ }
+
+ return false;
+}
+#endif
+
+
+static const char* ParseBlendShapeWeightName (const char* attribute)
+{
+ const char* prefix = "blendShape.";
+ if (BeginsWith(attribute, prefix))
+ return attribute + strlen(prefix);
+ else
+ return NULL;
+}
+
+static bool BlendShapeCalculateTargetPtr(Object* targetObject, const std::string& attribute, void** targetPtr, int* type)
+{
+ const char* name = ParseBlendShapeWeightName (attribute.c_str());
+ if (name == NULL)
+ return false;
+
+ SkinnedMeshRenderer* renderer = static_cast<SkinnedMeshRenderer*>(targetObject);
+ Assert(renderer);
+
+ const Mesh* mesh = renderer->GetMesh();
+ if (mesh == NULL)
+ return false;
+
+ const BlendShapeData& blendShapes = mesh->GetBlendShapeData();
+ int index = GetChannelIndex (blendShapes, name);
+ if (index == -1)
+ return false;
+
+ // Encode targetType
+ *type = kBindFloatToBlendShapeWeight | (index << BoundCurveDeprecated::kBindTypeBitCount);
+ *targetPtr = renderer;
+
+ Assert((*type >> BoundCurveDeprecated::kBindTypeBitCount) == index);
+
+ return true;
+}
+
+bool AnimationBinder::CalculateTargetPtr(int classID, Object* targetObject, const char* attribute, void** targetPtr, int* type)
+{
+ Assert(kBindTypeCount <= (1 << BoundCurveDeprecated::kBindTypeBitCount));
+ AssertIf(targetObject == NULL);
+
+ if (classID == ClassID(Transform))
+ {
+ Transform* transformTarget = static_cast<Transform*> (targetObject);
+
+ if (strcmp(attribute, "m_LocalPosition") == 0)
+ {
+ *type = kBindTransformPosition;
+ *targetPtr = &transformTarget->m_LocalPosition;
+ return true;
+ }
+ else if (strcmp(attribute, "m_LocalScale") == 0)
+ {
+ *type = kBindTransformScale;
+ *targetPtr = &transformTarget->m_LocalScale;
+ return true;
+ }
+ else if (strcmp (attribute, "m_LocalRotation") == 0)
+ {
+ *type = kBindTransformRotation;
+ *targetPtr = &transformTarget->m_LocalRotation;
+ return true;
+ }
+ }
+ else if (classID == ClassID(Material))
+ {
+// Renderer* renderer = static_cast<Transform*> (targetObject);
+
+ // [0].mainTex.offset.x
+ // [0].mainTex.offset.y
+ // [0].mainTex.scale.y
+ // [0].mainTex.rotation
+ // [0].mainColor.r
+ // [0].mainColor.x
+ // [0].floatPropertyName
+
+ int materialIndex = 0;
+ int shaderPropertyIndex = 0;
+ int newTargetType = kUnbound;
+ int targetIndex = 0;
+
+ // Grab material index "[3]."
+ const char* a = attribute;
+ if (*a == '[')
+ {
+ while (*a != 0 && *a != '.')
+ a++;
+
+ if (*a == '.')
+ materialIndex = StringToInt(attribute + 1);
+ else
+ return false;
+ attribute = a + 1;
+ }
+
+
+ // Find shader propertyname
+ int dotIndex = -1;
+ const char* lastCharacter;
+ while (*a != 0)
+ {
+ if (*a == '.' && dotIndex == -1)
+ dotIndex = a - attribute;
+ a++;
+ }
+ lastCharacter = a - 1;
+
+ // No . must be float property
+ if (dotIndex == -1)
+ {
+ newTargetType = kBindFloatToMaterial;
+ shaderPropertyIndex = Property(attribute).index;
+ }
+ // Calculate different property types
+ else
+ {
+ shaderPropertyIndex = Property(string(attribute, attribute + dotIndex)).index;
+ attribute += dotIndex + 1;
+
+ switch (*attribute)
+ {
+ // g color or y vector
+ case 'g':
+ case 'y':
+ newTargetType = kBindFloatToColorMaterial;
+ targetIndex = 1;
+ break;
+
+ // b or z vector
+ case 'b':
+ case 'z':
+ newTargetType = kBindFloatToColorMaterial;
+ targetIndex = 2;
+ break;
+
+ // alpha or w vector
+ case 'a':
+ case 'w':
+ newTargetType = kBindFloatToColorMaterial;
+ targetIndex = 3;
+ break;
+
+ // uv scale
+ case 's':
+ newTargetType = kBindFloatToMaterialScaleAndOffset;
+ targetIndex = *lastCharacter == 'x' ? 0 : 1;
+ break;
+ // uv offset
+ case 'o':
+ newTargetType = kBindFloatToMaterialScaleAndOffset;
+ targetIndex = *lastCharacter == 'x' ? 2 : 3;
+ break;
+
+ // r color
+ case 'r':
+ if (lastCharacter == attribute)
+ {
+ newTargetType = kBindFloatToColorMaterial;
+ targetIndex = 0;
+ }
+ break;
+ // x vector
+ case 'x':
+ newTargetType = kBindFloatToColorMaterial;
+ targetIndex = 0;
+ break;
+ }
+ }
+
+ if (newTargetType != kUnbound)
+ {
+ Assert(BoundCurveDeprecated::kBindTypeBitCount + BoundCurveDeprecated::kBindMaterialShaderPropertyNameBitCount < 32);
+ Assert(newTargetType < (1 << BoundCurveDeprecated::kBindTypeBitCount));
+ Assert(shaderPropertyIndex < (1 << BoundCurveDeprecated::kBindMaterialShaderPropertyNameBitCount));
+ Assert(targetIndex < (1 << (32 - BoundCurveDeprecated::kBindTypeBitCount - BoundCurveDeprecated::kBindMaterialShaderPropertyNameBitCount)));
+
+ // Encode targetType
+ newTargetType |= targetIndex << (BoundCurveDeprecated::kBindMaterialShaderPropertyNameBitCount + BoundCurveDeprecated::kBindTypeBitCount);
+ newTargetType |= shaderPropertyIndex << BoundCurveDeprecated::kBindTypeBitCount;
+ *targetPtr = reinterpret_cast<void*> (materialIndex);
+ *type = newTargetType;
+ return true;
+ }
+ else
+ {
+ *targetPtr = NULL;
+ *type = kUnbound;
+ return false;
+ }
+ }
+ else if (classID == ClassID(GameObject))
+ {
+ if (strcmp(attribute, "m_IsActive") == 0)
+ {
+ *type = kBindFloatToGameObjectActivate;
+ *targetPtr = targetObject;
+ return true;
+ }
+ }
+ else if (classID == ClassID(SkinnedMeshRenderer))
+ {
+ // We do not return on false, because this paths handles only animation for "blendShapeWeights[i]"
+ // and user might want to animate something else
+ if (BlendShapeCalculateTargetPtr(targetObject, attribute, targetPtr, type))
+ return true;
+ }
+
+ bool isScript =
+ #if ENABLE_MONO
+ classID == ClassID(MonoBehaviour);
+ #else
+ false;
+ #endif
+
+ TypeTree* typeTree = NULL;
+ if (m_TypeTreeCache.count(classID))
+ typeTree = m_TypeTreeCache.find(classID)->second;
+ else
+ {
+ // Build proxy
+ typeTree = new TypeTree();
+ GenerateTypeTree (*targetObject, typeTree);
+ if (!isScript)
+ m_TypeTreeCache[classID] = typeTree;
+ }
+
+ *type = kUnbound;
+ *targetPtr = NULL;
+
+ // Find attribute
+ // * Check if we support binding that value
+ // * scripts use direct ptrs but it only works reliable at the root level, because other variables may be moved/deleted arbitrarily
+ const TypeTree* variable = FindAttributeInTypeTreeNoArrays (*typeTree, attribute);
+
+ if (IsAnimatableProperty(variable, isScript, targetObject))
+ {
+ *type = GetAnimatableBindType(*variable);
+
+ if (*type != kUnbound)
+ {
+ if (variable->m_ByteOffset != -1)
+ *targetPtr = reinterpret_cast<UInt8*>(targetObject) + variable->m_ByteOffset;
+ else
+ *targetPtr = variable->m_DirectPtr;
+ }
+ }
+
+ if (isScript)
+ delete typeTree;
+
+ return *type != kUnbound;
+}
+
+
+//@TODO: Stop supporting this. Only support batched BindCurve
+bool AnimationBinder::BindCurve (const CurveID& curveID, BoundCurveDeprecated& bound, Transform& transform)
+{
+ // Lookup without path
+ Object* targetObject = NULL;
+ Transform* child = &transform;
+ if (curveID.path[0] != '\0')
+ {
+ child = FindRelativeTransformWithPath(*child, curveID.path);
+ if (child == NULL)
+ return false;
+ }
+
+ // Lookup gameobject
+ if (curveID.classID == ClassID(GameObject))
+ {
+ targetObject = &child->GetGameObject();
+ }
+ // Lookup material
+ else if (curveID.classID == ClassID(Material))
+ {
+ targetObject = GetComponentWithScript(child->GetGameObject(), ClassID(Renderer), curveID.script);
+ if (targetObject == NULL)
+ return false;
+ }
+ // Lookup component
+ else if (curveID.classID != ClassID(Material))
+ {
+ targetObject = GetComponentWithScript(child->GetGameObject(), curveID.classID, curveID.script);
+ if (targetObject == NULL)
+ return false;
+ }
+
+ int type;
+ void* targetPtr;
+ if (!CalculateTargetPtr(curveID.classID, targetObject, curveID.attribute, &targetPtr, &type))
+ return false;
+
+ bound.targetPtr = reinterpret_cast<UInt8*>(targetPtr);
+ bound.targetType = type;
+ bound.targetObject = targetObject;
+ bound.targetInstanceID = targetObject->GetInstanceID();
+
+ return true;
+}
+
+static void ClearTransformTemporaryFlag (Transform& transform)
+{
+ transform.SetTemporaryFlags(0);
+ Transform::iterator end = transform.end();
+ for (Transform::iterator i=transform.begin();i!=end;i++)
+ ClearTransformTemporaryFlag(**i);
+}
+
+static void CalculateTransformRoots (Transform& transform, AnimationBinder::AffectedRootTransforms& affectedRootTransforms)
+{
+ if (transform.GetTemporaryFlags())
+ {
+ affectedRootTransforms.push_back(&transform);
+ }
+ else
+ {
+ Transform::iterator end = transform.end();
+ for (Transform::iterator i=transform.begin();i!=end;i++)
+ CalculateTransformRoots(**i, affectedRootTransforms);
+ }
+}
+
+void AnimationBinder::RemoveUnboundCurves (CurveIDLookup& lookup, BoundCurves& outBoundCurves)
+{
+ CurveIDLookup::iterator i;
+ // Some curves couldn't be bound. We want to avoid the runtime check so we remove from the lookup and boundcurves array completely.
+ // - we already removed them from the lookup table
+ // - Now we need to remap the bound curves and
+ if (lookup.size() != outBoundCurves.size())
+ {
+ if (lookup.empty())
+ {
+ outBoundCurves.clear();
+ return;
+ }
+
+ BoundCurves tempBoundCurves;
+ tempBoundCurves.resize_uninitialized(lookup.size());
+
+ // Build a remap table that will compact the array - erasing the curves that are undefined
+ vector<int> remap;
+ remap.resize(outBoundCurves.size());
+ int validCount = 0;
+ for (int j=0;j<outBoundCurves.size();j++)
+ {
+ remap[j] = validCount;
+ if (outBoundCurves[j].targetType != kUnbound)
+ {
+ tempBoundCurves[validCount] = outBoundCurves[j];
+ validCount++;
+ }
+ }
+
+ for (i=lookup.begin();i != lookup.end();i++)
+ i->second = remap[i->second];
+
+ tempBoundCurves.swap(outBoundCurves);
+ }
+}
+
+void AnimationBinder::BindCurves (const CurveIDLookup& lookup, GameObject& rootGameObject, BoundCurves& outBoundCurves)
+{
+ AffectedRootTransforms affectedRoot;
+ int transformMessageMask = 0;
+ BindCurves(lookup, rootGameObject.GetComponent(Transform), outBoundCurves, affectedRoot, transformMessageMask);
+}
+
+
+
+void AnimationBinder::BindCurves (const CurveIDLookup& lookup, Transform& transform, BoundCurves& outBoundCurves, AffectedRootTransforms& affectedRootTransforms, int& transformChangedMask)
+{
+ outBoundCurves.resize_uninitialized(lookup.size());
+ affectedRootTransforms.clear();
+ transformChangedMask = 0;
+ ClearTransformTemporaryFlag(transform);
+
+ // Go through all lookups. Find their binder and assign it.
+ CurveIDLookup::const_iterator next, i;
+ for (i=lookup.begin();i != lookup.end();i=next)
+ {
+ next = i;
+ next++;
+
+ const CurveID& curveID = i->first;
+ int bindIndex = i->second;
+
+ outBoundCurves[bindIndex].targetPtr = NULL;
+ outBoundCurves[bindIndex].targetObject = NULL;
+ outBoundCurves[bindIndex].targetInstanceID = 0;
+ outBoundCurves[bindIndex].targetType = kUnbound;
+
+ // Lookup without path
+ Object* targetObject = NULL;
+ GameObject* go = NULL;
+ if (curveID.path[0] != '\0')
+ {
+ // Lookup child
+ Transform* child = FindRelativeTransformWithPath(transform, curveID.path);
+ if (child == NULL)
+ {
+ #if DEBUG_ANIMATIONS
+ LogString(Format("Animation bind couldn't find transform child %s", curveID.path));
+ #endif
+ #if COMPACT_UNBOUND_CURVES
+ lookup.erase(i);
+ #endif
+ continue;
+ }
+
+ go = &child->GetGameObject();
+
+ }
+ else
+ {
+ go = &transform.GetGameObject();
+ }
+
+ // Lookup component
+
+ if (curveID.classID == ClassID(GameObject))
+ {
+ targetObject = go;
+ }
+ else if (curveID.classID != ClassID(Material))
+ {
+ targetObject = GetComponentWithScript(*go, curveID.classID, curveID.script);
+ if (targetObject == NULL)
+ {
+ #if DEBUG_ANIMATIONS
+ LogString(Format("Animation couldn't find %s", Object::ClassIDToString(curveID.classID)));
+ #endif
+ #if COMPACT_UNBOUND_CURVES
+ lookup.erase(i);
+ #endif
+ continue;
+ }
+ }
+ // Lookup material
+ else
+ {
+ targetObject = GetComponentWithScript(*go, ClassID(Renderer), curveID.script);
+ if (targetObject == NULL)
+ {
+ #if DEBUG_ANIMATIONS
+ LogString(Format("Animation couldn't find %s", Object::ClassIDToString(curveID.classID)));
+ #endif
+ #if COMPACT_UNBOUND_CURVES
+ lookup.erase(i);
+ #endif
+ continue;
+ }
+ }
+
+
+ int type;
+ void* targetPtr;
+ if (!CalculateTargetPtr(curveID.classID, targetObject, curveID.attribute, &targetPtr, &type))
+ {
+ #if DEBUG_ANIMATIONS
+ LogString(Format("Couldn't bind animation attribute %s.%s", Object::ClassIDToString(curveID.classID).c_str(), curveID.attribute));
+ #endif
+ #if COMPACT_UNBOUND_CURVES
+ lookup.erase(i);
+ #endif
+ continue;
+ }
+
+ // - Precalculate affected root transform (Where do we send the transform changed message to)
+ // - Precalculated transform changed mask (What part of the transform has changed)
+ if (curveID.classID == ClassID(Transform))
+ {
+ targetObject->SetTemporaryFlags(1);
+
+ if ((transformChangedMask & Transform::kRotationChanged) == 0 && BeginsWith(curveID.attribute, "m_LocalRotation"))
+ transformChangedMask |= Transform::kRotationChanged;
+ if ((transformChangedMask & Transform::kPositionChanged) == 0 && BeginsWith(curveID.attribute, "m_LocalPosition"))
+ transformChangedMask |= Transform::kPositionChanged;
+ if ((transformChangedMask & Transform::kScaleChanged) == 0 && BeginsWith(curveID.attribute, "m_LocalScale"))
+ transformChangedMask |= Transform::kScaleChanged;
+ }
+
+ outBoundCurves[bindIndex].targetPtr = targetPtr;
+ outBoundCurves[bindIndex].targetType = type;
+ outBoundCurves[bindIndex].targetObject = targetObject;
+ outBoundCurves[bindIndex].targetInstanceID = targetObject->GetInstanceID();
+
+ #if DEBUG_ANIMATIONS
+ outBoundCurves[bindIndex].attribute = curveID.attribute;
+ outBoundCurves[bindIndex].path = curveID.path;
+ outBoundCurves[bindIndex].klass = Object::ClassIDToString(curveID.classID);
+ #endif
+ }
+
+ CalculateTransformRoots (transform, affectedRootTransforms);
+}
+
+AnimationBinder* AnimationBinder::s_Instance = NULL;
+
+void AnimationBinder::StaticInitialize()
+{
+ s_Instance = UNITY_NEW(AnimationBinder, kMemAnimation);
+}
+
+void AnimationBinder::StaticDestroy()
+{
+ UNITY_DELETE(s_Instance, kMemAnimation);
+}
+
+static RegisterRuntimeInitializeAndCleanup s_AnimationBinderCallbacks(AnimationBinder::StaticInitialize, AnimationBinder::StaticDestroy);
+
+AnimationBinder& GetAnimationBinder()
+{
+ return *AnimationBinder::s_Instance;
+}
+
+
+inline Material* GetInstantiatedMaterial (const BoundCurveDeprecated& bind)
+{
+ unsigned int materialIndex = reinterpret_cast<intptr_t> (bind.targetPtr);
+ Renderer* renderer = static_cast<Renderer*> (bind.targetObject);
+
+ if (materialIndex < renderer->GetMaterialCount())
+ return renderer->GetAndAssignInstantiatedMaterial(materialIndex, true);
+ else
+ return NULL;
+}
+
+bool AnimationBinder::SetFloatValue (const BoundCurveDeprecated& bind, float value)
+{
+ UInt32 targetType = bind.targetType;
+ Assert(bind.targetType != kUnbound && bind.targetType != kBindTransformRotation && bind.targetType != kBindTransformPosition && bind.targetType != kBindTransformScale);
+
+ targetType &= BoundCurveDeprecated::kBindTypeMask;
+
+ if( targetType == kBindFloat )
+ {
+ *reinterpret_cast<float*>(bind.targetPtr) = value;
+ return true;
+ }
+ else if( targetType == kBindFloatToGameObjectActivate )
+ {
+ bool activeState = AnimationFloatToBool(value);
+ GameObject* go = static_cast<GameObject*> (bind.targetObject);
+ go->SetSelfActive (activeState);
+
+ return true;
+ }
+ else if( targetType == kBindFloatToBool )
+ {
+ *reinterpret_cast<UInt8*>(bind.targetPtr) = AnimationFloatToBool(value);
+ return true;
+ }
+ else if (targetType == kBindFloatToBlendShapeWeight)
+ {
+ SkinnedMeshRenderer* renderer = reinterpret_cast<SkinnedMeshRenderer*>(bind.targetObject);
+
+ const int shapeIndex = bind.targetType >> BoundCurveDeprecated::kBindTypeBitCount;
+ renderer->SetBlendShapeWeight(shapeIndex, value);
+ return true;
+ }
+ else
+ {
+ Material* material = GetInstantiatedMaterial (bind);
+ if (material != NULL)
+ {
+ targetType = bind.targetType;
+
+ // Extract value index, shader property name and real targetType
+ int valueIndex = (targetType >> 28) & 0xF;
+ int propertyName = (targetType >> 4) & 0xFFFFF;
+ targetType = targetType & 0xF;
+
+ ShaderLab::FastPropertyName name;
+ name.index = propertyName;
+ if (targetType == kBindFloatToMaterial)
+ material->SetFloat(name, value);
+ else if (targetType == kBindFloatToMaterialScaleAndOffset)
+ material->SetTextureScaleAndOffsetIndexed(name, valueIndex, value);
+ else if (targetType == kBindFloatToColorMaterial)
+ material->SetColorIndexed(name, valueIndex, value);
+ else
+ {
+ AssertString("Unsupported bind mode!");
+ return false;
+ }
+
+ return true;
+ }
+ return false;
+ }
+}
+
+void AnimationBinder::SetValueAwakeGeneric (const BoundCurveDeprecated& bind)
+{
+ if (ShouldAwakeGeneric(bind))
+ {
+ bind.targetObject->AwakeFromLoad(kDefaultAwakeFromLoad);
+ bind.targetObject->SetDirty();
+ }
+}
+
+int AnimationBinder::InsertCurveIDIntoLookup (CurveIDLookup& curveIDLookup, const CurveID& curveID)
+{
+ return curveIDLookup.insert(std::make_pair(curveID, curveIDLookup.size())).first->second;
+}
+
+void CurveID::CalculateHash ()
+{
+ hash_cstring h;
+ hash = max(h (path) ^ ClassID(Transform) ^ h (attribute), (unsigned)2);
+}
+
+
+
diff --git a/Runtime/Animation/AnimationBinder.h b/Runtime/Animation/AnimationBinder.h
new file mode 100644
index 0000000..c5a4f13
--- /dev/null
+++ b/Runtime/Animation/AnimationBinder.h
@@ -0,0 +1,132 @@
+#pragma once
+
+#include "BoundCurveDeprecated.h"
+
+class Transform;
+class TypeTree;
+class MonoScript;
+namespace Unity { class GameObject; class Material; }
+
+#include <string>
+#include <map>
+#include <vector>
+#include "Runtime/Utilities/dense_hash_map.h"
+#include "Runtime/Utilities/CStringHash.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Mono/MonoScript.h"
+
+struct CurveID
+{
+ const char* path;
+ int classID;
+ const char* attribute;
+ MonoScriptPtr script;
+ unsigned hash;
+
+ CurveID () {}
+ CurveID (const char* inPath, int inClassID, MonoScriptPtr inScript, const char* inAttribute, unsigned inHash)
+ {
+ path = inPath;
+ attribute = inAttribute;
+ classID = inClassID;
+ script = inScript;
+ hash = inHash;
+ }
+
+ friend bool operator == (const CurveID& lhs, const CurveID& rhs)
+ {
+ if (lhs.hash == rhs.hash && lhs.classID == rhs.classID)
+ {
+ int pathCompare = strcmp(lhs.path, rhs.path);
+ if (pathCompare == 0)
+ {
+ int attributeCompare = strcmp(lhs.attribute, rhs.attribute);
+ if (attributeCompare == 0)
+ return lhs.script == rhs.script;
+ else
+ return false;
+ }
+ else
+ return false;
+ }
+ return false;
+ }
+
+ void CalculateHash ();
+};
+
+struct hash_curve
+{
+ unsigned operator()(const CurveID& curve) const
+ {
+ return curve.hash;
+ }
+};
+
+
+class AnimationBinder
+{
+ typedef std::map<int, TypeTree*> TypeTreeCache;
+ TypeTreeCache m_TypeTreeCache;
+
+
+ friend AnimationBinder& GetAnimationBinder();
+ static AnimationBinder* s_Instance;
+
+public:
+ typedef std::pair<const CurveID, unsigned> CurveIntPair;
+ typedef STL_ALLOCATOR(kMemTempAlloc, CurveIntPair) TempCurveIDAllocator;
+ typedef dense_hash_map<CurveID, unsigned, hash_curve, std::equal_to<CurveID>, TempCurveIDAllocator > CurveIDLookup;
+ typedef dynamic_array<BoundCurveDeprecated> BoundCurves;
+ typedef UNITY_VECTOR(kMemAnimation, Transform*) AffectedRootTransforms;
+
+ AnimationBinder () { }
+ ~AnimationBinder ();
+
+ static void StaticInitialize ();
+ static void StaticDestroy ();
+
+ bool CalculateTargetPtr(int classID, Object* targetObject, const char* attribute, void** targetPtr, int* type);
+
+ // Builds outBoundCurves and generates all binding information
+ // NOTE: If a curves can not be bound to the target objects, the entry will be removed from the lookup and will also not be in outBoundCurves
+ void BindCurves (const CurveIDLookup& lookup, Transform& transform, BoundCurves& outBoundCurves, AffectedRootTransforms& affectedRootTransforms, int& transformChangedMask);
+ void BindCurves (const CurveIDLookup& lookup, Unity::GameObject& rootGameObject, BoundCurves& outBoundCurves);
+
+ static void RemoveUnboundCurves (CurveIDLookup& lookup, BoundCurves& outBoundCurves);
+
+ static void InitCurveIDLookup (CurveIDLookup& lookup);
+ static int InsertCurveIDIntoLookup (CurveIDLookup& lookup, const CurveID& curveIDLookup);
+
+ // Simplified curve binding. No support for materials
+ bool BindCurve (const CurveID& curveID, BoundCurveDeprecated& bound, Transform& transform);
+
+
+ // Sets the value on the bound curve.
+ // Does not call AwakeFromLoad or SetDirty. You can call SetValueAwakeGeneric or do it yourself.
+ static bool SetFloatValue (const BoundCurveDeprecated& bind, float value);
+
+ // Calls AwakeFromLoad or SetDirty on the target
+ static void SetValueAwakeGeneric (const BoundCurveDeprecated& bind);
+
+ static bool ShouldAwakeGeneric (const BoundCurveDeprecated& bind) { return bind.targetType == kBindFloat || bind.targetType == kBindFloatToBool; }
+
+ static inline bool AnimationFloatToBool (float result)
+ {
+ return result > 0.001F || result < -0.001F;
+ }
+
+ static inline float AnimationBoolToFloat (bool value)
+ {
+ return value ? 1.0F : 0.0F;
+ }
+
+ #if UNITY_EDITOR
+
+ static bool IsAnimatablePropertyOrHasAnimatableChild (const TypeTree& variable, bool isScript, Object* targetObject);
+
+ #endif
+};
+
+AnimationBinder& GetAnimationBinder();
diff --git a/Runtime/Animation/AnimationClip.cpp b/Runtime/Animation/AnimationClip.cpp
new file mode 100644
index 0000000..e97098c
--- /dev/null
+++ b/Runtime/Animation/AnimationClip.cpp
@@ -0,0 +1,1679 @@
+#include "UnityPrefix.h"
+#include "Runtime/Math/AnimationCurve.h"
+#include "AnimationClip.h"
+#include "AnimationBinder.h"
+#include "NewAnimationTrack.h"
+#include "AnimationCurveUtility.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Utilities/algorithm_utility.h"
+#include <limits>
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Filters/Mesh/CompressedMesh.h"
+#include "Runtime/mecanim/generic/stringtable.h"
+#include "Runtime/mecanim/generic/crc32.h"
+#include "Runtime/Serialize/Blobification/BlobWrite.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "GenericAnimationBindingCache.h"
+#include "AnimationClipStats.h"
+#include "MecanimClipBuilder.h"
+#include "MecanimUtility.h"
+
+
+
+#if UNITY_EDITOR
+#include "KeyframeReducer.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#endif
+
+using namespace UnityEngine::Animation;
+
+static AnimationClip::DidModifyClipCallback* gDidModifyClipCallback = NULL;
+
+#if UNITY_EDITOR
+static AnimationClip::OnAnimationClipAwake* gOnAnimationClipAwake = NULL;
+void AnimationClip::SetOnAnimationClipAwake (OnAnimationClipAwake* callback)
+{
+ gOnAnimationClipAwake = callback;
+}
+
+#endif
+
+using namespace std;
+
+AnimationClip::AnimationClip(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode),
+m_Bounds(Vector3f::zero, Vector3f::zero),
+m_AnimationType(kLegacy),
+m_MuscleClipSize(0),
+m_MuscleClip(0),
+m_UseHighQualityCurve(true),
+m_ClipAllocator(4*1024)
+{
+ m_SampleRate = 60.0F;
+ m_Compressed = false;
+ m_WrapMode = 0;
+}
+
+void AnimationClip::AwakeFromLoad(AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad(mode);
+ #if UNITY_EDITOR
+
+ ConvertToNewCurveFormat();
+ if (gOnAnimationClipAwake)
+ gOnAnimationClipAwake (this);
+
+
+ // Generate muscle clip data from editor curves (Assetbundles already have a muscle clip baked in, we dont want to regenerate that)
+ if ((mode & kDidLoadFromDisk) && GetRuntimeAsset() != NULL)
+ {
+ // case 476382, need to populate m_AnimationClipSettings manually because it is not transfer for game release asset
+ CstToAnimationClipSettings(m_MuscleClip, m_AnimationClipSettings);
+ }
+ // When loading scenes etc it's a good idea not to have the hiccups all on first access
+ else if ((mode & kDidLoadFromDisk) && GetRuntimeAsset() == NULL)
+ GenerateMuscleClip();
+ else
+ CleanupMecanimData();
+
+ #endif
+
+ ClipWasModified (false);
+}
+
+void AnimationClip::ClipWasModifiedAndUpdateMuscleRange ()
+{
+ ClipWasModified();
+ UpdateMuscleClipRange ();
+}
+
+void AnimationClip::UpdateMuscleClipRange ()
+{
+#if UNITY_EDITOR
+ m_CachedRange = make_pair (std::numeric_limits<float>::infinity (), -std::numeric_limits<float>::infinity ());
+ pair<float,float> range = GetRange();
+
+ AnimationClipSettings settings = GetAnimationClipSettings ();
+ settings.m_StartTime = 0.0F;
+ settings.m_StopTime = range.second;
+
+ SetAnimationClipSettingsNoDirty(settings);
+#endif
+}
+
+void AnimationClip::SetSampleRate (float s)
+{
+ m_SampleRate = s;
+ // Stop time depends on the sample rate
+ // because we add an extra frame at the end to cover pptr curves
+ UpdateMuscleClipRange ();
+ SetDirty();
+}
+
+void AnimationClip::CheckConsistency ()
+{
+ Super::CheckConsistency();
+
+ if(kLegacy > m_AnimationType || m_AnimationType > kHumanoid)
+ {
+ CleanupMecanimData();
+ m_AnimationType = kLegacy;
+ }
+}
+
+void AnimationClip::ClipWasModified (bool cleanupMecanimData)
+{
+ if (cleanupMecanimData)
+ CleanupMecanimData();
+
+ NotifyObjectUsers(kDidModifyMotion);
+
+ m_CachedRange = make_pair (std::numeric_limits<float>::infinity (), -std::numeric_limits<float>::infinity ());
+ gDidModifyClipCallback (this, m_AnimationStates);
+}
+
+void AnimationClip::ClearCurves ()
+{
+ m_RotationCurves.clear();
+ m_PositionCurves.clear();
+ m_ScaleCurves.clear();
+ m_FloatCurves.clear();
+ m_PPtrCurves.clear();
+ #if UNITY_EDITOR
+ m_EditorCurves.clear();
+ m_EulerEditorCurves.clear();
+ #endif
+
+ ClipWasModifiedAndUpdateMuscleRange ();
+ SetDirty();
+}
+
+void AnimationClip::EnsureQuaternionContinuity ()
+{
+ for (QuaternionCurves::iterator i=m_RotationCurves.begin();i != m_RotationCurves.end();i++)
+ ::EnsureQuaternionContinuityAndRecalculateSlope(i->curve);
+
+ gDidModifyClipCallback (this, m_AnimationStates);
+ SetDirty();
+}
+
+bool AnimationClip::HasAnimationEvents ()
+{
+ return m_Events.size() > 0;
+}
+
+void AnimationClip::FireAnimationEvents (float lastTime, float now, Unity::Component& source)
+{
+ AnimationClip::Events& events = GetEvents();
+ Assert(!events.empty());
+
+ if (lastTime == now)
+ return;
+
+ ///@TODO:
+ // * AnimationEvents with blendWeight = 0 will not be fired (In a blendtree they should always be fired, irregardless of blendweight)
+
+
+ int eventCount = events.size();
+
+ // Simple forward playback.
+ if (lastTime < now)
+ {
+ // Special case for first frame in the clip, when just playing a simple clip
+ // We want to make sure the event on the first frame will be fired.
+ // (We can't have this in the general codepath otherwise we end up firing events twice,
+ // if the event matches the time exactly)
+ if (lastTime == 0.0F && events[0].time == 0.0F)
+ FireEvent(events[0], 0, source);
+
+ // Play all events that
+ for (int eventIter = 0; eventIter < eventCount; eventIter++)
+ {
+ if (lastTime < events[eventIter].time && now >= events[eventIter].time)
+ FireEvent(events[eventIter], 0, source);
+ }
+ }
+ // Looping
+ // - Play all events to the end of the clip
+ // - then from first event up to now
+ else
+ {
+ for (int eventIter = 0; eventIter < eventCount; eventIter++)
+ {
+ if (lastTime < events[eventIter].time)
+ FireEvent(events[eventIter], 0, source);
+ }
+
+ for (int eventIter = 0; eventIter < eventCount; eventIter++)
+ {
+ if (now > events[eventIter].time)
+ FireEvent(events[eventIter], 0, source);
+ }
+ }
+}
+
+#if UNITY_EDITOR
+void AnimationClip::ReloadEditorEulerCurves (const string& path)
+{
+ SET_ALLOC_OWNER(this);
+ // Find the euler curves we will use to build the quaternion curves
+ AnimationCurve* curves[4] = { NULL, NULL, NULL, NULL };
+ for (FloatCurves::iterator i=m_EditorCurves.begin();i != m_EditorCurves.end();i++)
+ {
+ if (BeginsWith(i->attribute, "m_LocalRotation") && i->path == path)
+ {
+ char last = i->attribute[i->attribute.size()-1];
+ if (last == 'x')
+ curves[0] = &i->curve;
+ else if (last == 'y')
+ curves[1] = &i->curve;
+ else if (last == 'z')
+ curves[2] = &i->curve;
+ else if (last == 'w')
+ curves[3] = &i->curve;
+ else
+ {
+ ErrorString("Can't set curve because " + i->attribute + " is not a valid Transform property.");
+ continue;
+ }
+ }
+ }
+
+ // Remove existing euler curves at path
+ for (int i=0;i != m_EulerEditorCurves.size();i++)
+ {
+ if (m_EulerEditorCurves[i].classID == ClassID(Transform) && m_EulerEditorCurves[i].path == path)
+ {
+ m_EulerEditorCurves[i] = m_EulerEditorCurves.back();
+ m_EulerEditorCurves.resize(m_EulerEditorCurves.size() - 1);
+ i--;
+ }
+ }
+
+ if (curves[0] && curves[1] && curves[2] && curves[3])
+ {
+ // Create combined quaternion curve
+ AnimationCurveQuat quatCurve;
+ CombineCurve (*curves[0], 0, quatCurve);
+ CombineCurve (*curves[1], 1, quatCurve);
+ CombineCurve (*curves[2], 2, quatCurve);
+ CombineCurve (*curves[3], 3, quatCurve);
+
+ // Create euler curves
+ FloatCurve curve;
+ curve.path = path;
+ curve.classID = ClassID(Transform);
+ int first = m_EulerEditorCurves.size();
+ curve.attribute = "localEulerAngles.x";
+ m_EulerEditorCurves.push_back(curve);
+ curve.attribute = "localEulerAngles.y";
+ m_EulerEditorCurves.push_back(curve);
+ curve.attribute = "localEulerAngles.z";
+ m_EulerEditorCurves.push_back(curve);
+ AnimationCurve* eulerCurves[3] = { &m_EulerEditorCurves[first].curve, &m_EulerEditorCurves[first+1].curve, &m_EulerEditorCurves[first+2].curve };
+
+ QuaternionCurveToEulerCurve(quatCurve, eulerCurves);
+ }
+}
+
+void AnimationClip::ReloadEditorQuaternionCurves (const string& path)
+{
+ SET_ALLOC_OWNER(this);
+ int bakedEulerCurves = -1;
+
+ // Find the euler curves we will use to build the quaternion curves
+ AnimationCurve* curves[3] = { NULL, NULL, NULL };
+ for (FloatCurves::iterator i=m_EulerEditorCurves.begin();i != m_EulerEditorCurves.end();i++)
+ {
+ char last = i->attribute[i->attribute.size()-1];
+
+ if (i->path == path)
+ {
+ DebugAssertIf(!BeginsWith(i->attribute, "localEulerAngles") && !BeginsWith(i->attribute, "localEulerAnglesBaked"));
+ int curBaked = BeginsWith(i->attribute, "localEulerAnglesBaked");
+ if (bakedEulerCurves != -1 && bakedEulerCurves != curBaked)
+ {
+ ErrorString("localEulerAnglesBaked and localEulerAngles exist at the same time. Please ensure that there is always only one curve type in use on a single transform.");
+ continue;
+ }
+
+ bakedEulerCurves = curBaked;
+
+ if (last == 'x')
+ curves[0] = &i->curve;
+ else if (last == 'y')
+ curves[1] = &i->curve;
+ else if (last == 'z')
+ curves[2] = &i->curve;
+ else
+ {
+ ErrorString("Can't set curve because " + i->attribute + " is not a valid Transform property.");
+ continue;
+ }
+ }
+ }
+
+ // Remove existing quaternion curves at path
+ for (int i=0;i != m_EditorCurves.size();i++)
+ {
+ if (m_EditorCurves[i].classID == ClassID(Transform) && BeginsWith(m_EditorCurves[i].attribute, "m_LocalRotation") && m_EditorCurves[i].path == path)
+ {
+ m_EditorCurves[i] = m_EditorCurves.back();
+ m_EditorCurves.resize(m_EditorCurves.size() - 1);
+ i--;
+ }
+ }
+
+ // If all 3 euler curves exist, add the quaternion editor curve
+ if (curves[0] && curves[1] && curves[2])
+ {
+ // Create temporary quaternion curve
+ AnimationCurveQuat quatCurve;
+
+ // TODO : these both should use tangents or fitting
+ if (bakedEulerCurves)
+ EulerToQuaternionCurveBake(*curves[0], *curves[1], *curves[2], quatCurve, GetSampleRate());
+ else
+ EulerToQuaternionCurve(*curves[0], *curves[1], *curves[2], quatCurve);
+
+ // Create quaternion editor curves from single quaternion curve
+ FloatCurve curve;
+ curve.path = path;
+ curve.classID = ClassID(Transform);
+
+ int first = m_EditorCurves.size();
+ curve.attribute = "m_LocalRotation.x";
+ m_EditorCurves.push_back(curve);
+ curve.attribute = "m_LocalRotation.y";
+ m_EditorCurves.push_back(curve);
+ curve.attribute = "m_LocalRotation.z";
+ m_EditorCurves.push_back(curve);
+ curve.attribute = "m_LocalRotation.w";
+ m_EditorCurves.push_back(curve);
+
+ AnimationCurve* quaternionCurves[4] = { &m_EditorCurves[first].curve, &m_EditorCurves[first+1].curve, &m_EditorCurves[first+2].curve, &m_EditorCurves[first+3].curve };
+ ExpandQuaternionCurve(quatCurve, quaternionCurves);
+ }
+}
+
+void AnimationClip::SetEditorCurve (const string& path, int classID, MonoScriptPPtr script, const std::string& attribute, const AnimationCurve* curve, bool syncEditorCurves)
+{
+ SET_ALLOC_OWNER(this);
+ GetEditorCurvesSync();
+
+ FloatCurves* curveArray = &m_EditorCurves;
+ if (classID == ClassID (Transform) && BeginsWith (attribute, "localEulerAngles"))
+ curveArray = &m_EulerEditorCurves;
+
+ // Find existing curve
+ FloatCurves::iterator i;
+ for (i=curveArray->begin();i != curveArray->end();i++)
+ {
+ if (i->classID == classID && i->path == path && i->attribute == attribute && i->script == script)
+ break;
+ }
+
+ // Shall we remove a curve?
+ if (curve == NULL)
+ {
+ if (i != curveArray->end ())
+ {
+ curveArray->erase(i);
+
+ // Modified euler angle curve, reload editor quaternion curves
+ if (classID == ClassID (Transform) && (BeginsWith (attribute, "localEulerAngles")) )
+ ReloadEditorQuaternionCurves(path);
+ // Modified quaternion curve, reload editor euler angle curves
+ else if (classID == ClassID (Transform) && BeginsWith (attribute, "m_LocalRotation"))
+ ReloadEditorEulerCurves(path);
+
+ if (syncEditorCurves)
+ SyncEditorCurves();
+ }
+ return;
+ }
+
+ // Add or replace the curve
+ AnimationCurve* comboCurve = i != curveArray->end() ? &i->curve : NULL;
+ if (comboCurve == NULL)
+ {
+ curveArray->push_back(FloatCurve());
+ curveArray->back().path = path;
+ curveArray->back().attribute = attribute;
+ curveArray->back().classID = classID;
+ curveArray->back().script = script;
+ curveArray->back().curve = *curve;
+ }
+ else
+ *comboCurve = *curve;
+
+ // Modified euler angle curve, reload editor quaternion curves
+ if (classID == ClassID (Transform) && (BeginsWith (attribute, "localEulerAngles")))
+ ReloadEditorQuaternionCurves(path);
+ // Modified quaternion curve, reload editor euler angle curves
+ else if (classID == ClassID (Transform) && BeginsWith (attribute, "m_LocalRotation"))
+ ReloadEditorEulerCurves(path);
+
+ SyncEditorCurves();
+}
+
+bool AnimationClip::GetEditorCurve (const string& path, int classID, MonoScriptPPtr script, const std::string& attribute, AnimationCurve* curve)
+{
+ GetEditorCurvesSync();
+
+ // Find existing curve
+ FloatCurves::iterator i;
+ for (i=m_EditorCurves.begin();i != m_EditorCurves.end();i++)
+ {
+ if (i->classID == classID && i->path == path && i->attribute == attribute && i->script == script)
+ {
+ if (curve != NULL)
+ *curve = i->curve;
+ return true;
+ }
+ }
+
+ // Find existing curve in euler editor curves array
+ for (i=m_EulerEditorCurves.begin();i != m_EulerEditorCurves.end();i++)
+ {
+ if (i->classID == classID && i->path == path && i->attribute == attribute && i->script == script)
+ {
+ if (curve != NULL)
+ *curve = i->curve;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+AnimationClip::FloatCurves& AnimationClip::GetEditorCurvesSync ()
+{
+ SET_ALLOC_OWNER(this);
+ if (m_EditorCurves.empty() && m_EulerEditorCurves.empty())
+ {
+ m_EulerEditorCurves.clear();
+ m_EditorCurves = m_FloatCurves;
+
+ for (QuaternionCurves::iterator i=m_RotationCurves.begin ();i != m_RotationCurves.end ();i++)
+ {
+ FloatCurve curve;
+ curve.path = i->path;
+ curve.classID = ClassID(Transform);
+ AssertMsg(i->curve.GetKeyCount() >= 2, "Key count: %d on curve '%s'", i->curve.GetKeyCount(), i->path.c_str());
+
+ // Create quaternion curves
+ int first = m_EditorCurves.size();
+ curve.attribute = "m_LocalRotation.x";
+ m_EditorCurves.push_back(curve);
+ curve.attribute = "m_LocalRotation.y";
+ m_EditorCurves.push_back(curve);
+ curve.attribute = "m_LocalRotation.z";
+ m_EditorCurves.push_back(curve);
+ curve.attribute = "m_LocalRotation.w";
+ m_EditorCurves.push_back(curve);
+
+ AnimationCurve* curves[4] = { &m_EditorCurves[first].curve, &m_EditorCurves[first+1].curve, &m_EditorCurves[first+2].curve, &m_EditorCurves[first+3].curve };
+ ExpandQuaternionCurve(i->curve, curves);
+
+ // Create euler curves
+ first = m_EulerEditorCurves.size();
+ curve.attribute = "localEulerAngles.x";
+ m_EulerEditorCurves.push_back(curve);
+ curve.attribute = "localEulerAngles.y";
+ m_EulerEditorCurves.push_back(curve);
+ curve.attribute = "localEulerAngles.z";
+ m_EulerEditorCurves.push_back(curve);
+
+ AnimationCurve* eulerCurves[3] = { &m_EulerEditorCurves[first].curve, &m_EulerEditorCurves[first+1].curve, &m_EulerEditorCurves[first+2].curve };
+ QuaternionCurveToEulerCurve(i->curve, eulerCurves);
+ }
+
+ for (Vector3Curves::iterator i=m_PositionCurves.begin ();i != m_PositionCurves.end ();i++)
+ {
+ FloatCurve curve;
+ curve.path = i->path;
+ curve.classID = ClassID(Transform);
+
+ int first = m_EditorCurves.size();
+ curve.attribute = "m_LocalPosition.x";
+ m_EditorCurves.push_back(curve);
+ curve.attribute = "m_LocalPosition.y";
+ m_EditorCurves.push_back(curve);
+ curve.attribute = "m_LocalPosition.z";
+ m_EditorCurves.push_back(curve);
+
+ AnimationCurve* curves[3] = { &m_EditorCurves[first].curve, &m_EditorCurves[first+1].curve, &m_EditorCurves[first+2].curve };
+ ExpandVector3Curve(i->curve, curves);
+ }
+
+ // Create scale curves
+ for (Vector3Curves::iterator i=m_ScaleCurves.begin ();i != m_ScaleCurves.end ();i++)
+ {
+ FloatCurve curve;
+ curve.path = i->path;
+ curve.classID = ClassID(Transform);
+
+ int first = m_EditorCurves.size();
+ curve.attribute = "m_LocalScale.x";
+ m_EditorCurves.push_back(curve);
+ curve.attribute = "m_LocalScale.y";
+ m_EditorCurves.push_back(curve);
+ curve.attribute = "m_LocalScale.z";
+ m_EditorCurves.push_back(curve);
+
+ AnimationCurve* curves[3] = { &m_EditorCurves[first].curve, &m_EditorCurves[first+1].curve, &m_EditorCurves[first+2].curve };
+ ExpandVector3Curve(i->curve, curves);
+ }
+ }
+ return m_EditorCurves;
+}
+
+
+
+void AnimationClip::SyncEditorCurves ()
+{
+ SET_ALLOC_OWNER(this);
+ m_RotationCurves.clear();
+ m_PositionCurves.clear();
+ m_ScaleCurves.clear();
+ m_FloatCurves.clear();
+
+ for (FloatCurves::iterator i=m_EditorCurves.begin ();i != m_EditorCurves.end ();i++)
+ {
+ SetCurve(i->path, i->classID, i->script, i->attribute, &i->curve, false);
+ }
+
+ ClipWasModifiedAndUpdateMuscleRange ();
+ SetDirty();
+
+}
+
+struct CurveHasAnimatorAttributePredicate
+{
+ CurveHasAnimatorAttributePredicate(UnityStr &attr) : m_Attribute(attr) {}
+
+ bool operator()(const AnimationClip::FloatCurve &curve)
+ {
+ return curve.attribute == m_Attribute && curve.classID == ClassID(Animator);
+ }
+
+ UnityStr& m_Attribute;
+};
+
+void AnimationClip::SyncMuscleCurvesBackwardCompatibility()
+{
+ for (FloatCurves::iterator i=m_EditorCurves.begin ();i != m_EditorCurves.end ();i++)
+ {
+ if(i->classID == ClassID(Animator))
+ {
+ // check if attribute is already in m_FloatCurves, if not add it.
+ if(std::find_if(m_FloatCurves.begin(),m_FloatCurves.end(), CurveHasAnimatorAttributePredicate( i->attribute )) == m_FloatCurves.end())
+ {
+ m_FloatCurves.push_back(FloatCurve());
+ m_FloatCurves.back().path = i->path;
+ m_FloatCurves.back().attribute = i->attribute;
+ m_FloatCurves.back().classID = i->classID;
+ m_FloatCurves.back().script = i->script;
+ m_FloatCurves.back().curve = i->curve;
+ }
+ }
+ }
+}
+
+struct EventSorter
+{
+ bool operator()( const AnimationEvent& ra, const AnimationEvent& rb ) const
+ {
+ return ra.time < rb.time;
+ }
+};
+
+static void SortEvents (AnimationClip::Events& events)
+{
+ std::sort (events.begin(), events.end(), EventSorter());
+}
+
+void AnimationClip::SetEvents (const AnimationEvent* events, int size, bool sort)
+{
+ m_Events.assign(events, events + size);
+ if (sort)
+ SortEvents(m_Events);
+ m_EditModeEvents = m_Events;
+
+ ClipWasModifiedAndUpdateMuscleRange ();
+
+ SetDirty();
+}
+
+void AnimationClip::ClearEvents ()
+{
+ m_Events.clear();
+ m_EditModeEvents.clear();
+
+ ClipWasModifiedAndUpdateMuscleRange ();
+
+ SetDirty();
+}
+
+void AnimationClip::CloneAdditionalEditorProperties (Object& src)
+{
+ Super::CloneAdditionalEditorProperties(src);
+ m_Events = static_cast<AnimationClip&> (src).m_Events;
+}
+
+void AnimationClip::RevertAllPlaymodeAnimationEvents ()
+{
+ vector<AnimationClip*> clips;
+ Object::FindObjectsOfType(&clips);
+ for (int i=0;i<clips.size();i++)
+ {
+ AnimationClip& clip = *clips[i];
+ if (clip.m_Events.size() != clip.m_EditModeEvents.size())
+ {
+ clip.m_Events = clip.m_EditModeEvents;
+ clip.ClipWasModifiedAndUpdateMuscleRange ();
+ }
+ }
+}
+
+#endif
+
+
+void AnimationClip::AddRuntimeEvent(AnimationEvent& event)
+{
+ Events::iterator i = lower_bound (m_Events.begin(), m_Events.end(), event);
+ m_Events.insert(i, event);
+ ClipWasModifiedAndUpdateMuscleRange ();
+
+ #if UNITY_EDITOR
+ if (!IsWorldPlaying())
+ {
+ ErrorString("Please use Editor.AnimationUtility to add persistent animation events to an animation clip");
+ }
+ #endif
+}
+
+#if UNITY_EDITOR
+bool AnimationClip::GetEditorPPtrCurve (const std::string& path, int classID, MonoScriptPPtr script, const std::string& attribute, PPtrKeyframes* outKeyframes)
+{
+ PPtrCurves::iterator i;
+ for (i = m_PPtrCurves.begin(); i != m_PPtrCurves.end(); ++i)
+ {
+ if (i->path == path && i->classID == classID && i->script == script && i->attribute == attribute)
+ {
+ *outKeyframes = i->curve;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void AnimationClip::SetEditorPPtrCurve (const std::string& path, int classID, MonoScriptPPtr script, const std::string& attribute, PPtrKeyframes* keyframes)
+{
+ SET_ALLOC_OWNER(this);
+
+ if (classID == -1)
+ {
+ ErrorString("Can't assign curve because the type does not inherit from Component.");
+ return;
+ }
+
+ // Find existing curve
+ PPtrCurves::iterator i;
+ for (i = m_PPtrCurves.begin(); i != m_PPtrCurves.end(); ++i)
+ {
+ if (i->path == path && i->classID == classID && i->script == script && i->attribute == attribute)
+ break;
+ }
+
+ // Shall we remove a curve?
+ if (keyframes == NULL)
+ {
+ if (i != m_PPtrCurves.end())
+ {
+ m_PPtrCurves.erase(i);
+ ClipWasModifiedAndUpdateMuscleRange ();
+ SetDirty();
+ }
+ return;
+ }
+
+ // Add or replace the curve
+ PPtrCurve* comboCurve = (i != m_PPtrCurves.end()) ? &(*i) : NULL;
+ if (comboCurve == NULL)
+ {
+ m_PPtrCurves.push_back(PPtrCurve());
+ comboCurve = &m_PPtrCurves.back();
+ }
+
+ comboCurve->path = path;
+ comboCurve->attribute = attribute;
+ comboCurve->classID = classID;
+ comboCurve->script = script;
+ comboCurve->curve = *keyframes;
+
+ ClipWasModifiedAndUpdateMuscleRange ();
+ SetDirty();
+}
+#endif
+
+bool AnimationClip::GetCurve (const string& path, int classID, MonoScriptPPtr script, const std::string& attribute, AnimationCurve* curve)
+{
+ AnimationCurve dummy0, dummy1, dummy2, dummy3;
+
+ // Get rotation curve
+ if (classID == ClassID(Transform) && BeginsWith (attribute, "m_LocalRotation"))
+ {
+ // Find existing curve
+ QuaternionCurves::iterator i;
+ for (i=m_RotationCurves.begin();i != m_RotationCurves.end();i++)
+ {
+ if (i->path == path)
+ break;
+ }
+
+ if (i == m_RotationCurves.end())
+ return false;
+
+ AnimationCurve* curves[4] = { &dummy0, &dummy1, &dummy2, &dummy3 };
+
+ char last = attribute[attribute.size()-1];
+ if (last == 'x')
+ curves[0] = curve;
+ else if (last == 'y')
+ curves[1] = curve;
+ else if (last == 'z')
+ curves[2] = curve;
+ else if (last == 'w')
+ curves[3] = curve;
+ else
+ {
+ ErrorString("Can't get curve because " + attribute + " is not a valid Transform property.");
+ return false;
+ }
+
+ ExpandQuaternionCurve(i->curve, curves);
+
+ return true;
+ }
+ // Get Local position / local scale curve
+ else if (classID == ClassID(Transform) && (BeginsWith (attribute, "m_LocalPosition") || BeginsWith (attribute, "m_LocalScale")))
+ {
+ Vector3Curves::iterator i;
+
+ if (BeginsWith (attribute, "m_LocalPosition"))
+ {
+ // Find existing curve
+ for (i=m_PositionCurves.begin();i != m_PositionCurves.end();i++)
+ {
+ if (i->path == path)
+ break;
+ }
+ if (i == m_PositionCurves.end())
+ return false;
+ }
+ else
+ {
+ // Find existing curve
+ for (i=m_ScaleCurves.begin();i != m_ScaleCurves.end();i++)
+ {
+ if (i->path == path)
+ break;
+ }
+ if (i == m_ScaleCurves.end())
+ return false;
+ }
+
+
+ AnimationCurve* curves[3] = { &dummy0, &dummy1, &dummy2 };
+
+ char last = attribute[attribute.size()-1];
+ if (last == 'x')
+ curves[0] = curve;
+ else if (last == 'y')
+ curves[1] = curve;
+ else if (last == 'z')
+ curves[2] = curve;
+ else
+ {
+ ErrorString("Can't get curve because " + attribute + " is not a valid Transform property.");
+ return false;
+ }
+
+ ExpandVector3Curve(i->curve, curves);
+
+ return true;
+ }
+ // Get any other type of curve
+ else
+ {
+ // Find existing curve
+ FloatCurves::iterator i;
+ for (i=m_FloatCurves.begin();i != m_FloatCurves.end();i++)
+ {
+ if (classID == i->classID && i->path == path && i->attribute == attribute && i->script == script)
+ break;
+ }
+
+ if (i == m_FloatCurves.end())
+ return false;
+
+ *curve = i->curve;
+ return true;
+ }
+}
+
+void AnimationClip::SetCurve (const string& path, int classID, MonoScriptPPtr script, const std::string& attribute, AnimationCurve* curve, bool syncEditorCurves)
+{
+ SET_ALLOC_OWNER(this);
+
+ #if UNITY_EDITOR
+ if (syncEditorCurves)
+ {
+ m_EditorCurves.clear();
+ m_EulerEditorCurves.clear();
+ }
+ #endif
+
+ if (classID == -1)
+ {
+ ErrorString("Can't assign curve because the type does not inherit from Component.");
+ return;
+ }
+
+ if (classID == ClassID(Transform) && (BeginsWith (attribute, "m_LocalRotation") || BeginsWith (attribute, "localRotation")))
+ {
+ // Find existing curve
+ QuaternionCurves::iterator i;
+ for (i=m_RotationCurves.begin();i != m_RotationCurves.end();i++)
+ {
+ if (i->path == path)
+ break;
+ }
+
+ // Shall we remove a curve?
+ if (curve == NULL)
+ {
+ if (attribute == "m_LocalRotation" || attribute == "localRotation")
+ {
+ if (i != m_RotationCurves.end())
+ {
+ m_RotationCurves.erase(i);
+ ClipWasModified ();
+
+ SetDirty();
+ }
+ return;
+ }
+ else
+ {
+ ErrorString("Can't remove individual animation rotation curve " + attribute + " you must remove the entire animation curve with m_LocalRotation.");
+ return;
+ }
+ }
+
+ // Add the curve if it doesnt exist already
+ AnimationCurveQuat* comboCurve = i != m_RotationCurves.end() ? &i->curve : NULL;
+
+ if (comboCurve == NULL)
+ {
+ m_RotationCurves.push_back(QuaternionCurve());
+ m_RotationCurves.back().path = path;
+ comboCurve = &m_RotationCurves.back().curve;
+ }
+
+ // Combine the curve into a rotation curve
+ char last = attribute[attribute.size()-1];
+ if (last == 'x')
+ CombineCurve(*curve, 0, *comboCurve);
+ else if (last == 'y')
+ CombineCurve(*curve, 1, *comboCurve);
+ else if (last == 'z')
+ CombineCurve(*curve, 2, *comboCurve);
+ else if (last == 'w')
+ CombineCurve(*curve, 3, *comboCurve);
+ else
+ {
+ ErrorString("Can't assign curve because " + attribute + " is not a valid Transform property.");
+ }
+ }
+ else if (classID == ClassID(Transform) && (BeginsWith (attribute, "m_LocalPosition") || BeginsWith (attribute, "localPosition")))
+ {
+ // Find existing curve
+ Vector3Curves::iterator i;
+ for (i=m_PositionCurves.begin();i != m_PositionCurves.end();i++)
+ {
+ if (i->path == path)
+ break;
+ }
+
+ // Shall we remove a curve?
+ if (curve == NULL)
+ {
+ if (attribute == "m_LocalPosition" || attribute == "localPosition")
+ {
+ if (i != m_PositionCurves.end())
+ {
+ m_PositionCurves.erase(i);
+ ClipWasModified ();
+ SetDirty();
+ }
+ return;
+ }
+ else
+ {
+ ErrorString("Can't remove individual position animation curve " + attribute + " you must remove the entire animation curve with m_LocalPosition.");
+ return;
+ }
+ }
+
+ // Add the curve if it doesnt exist already
+ AnimationCurveVec3* comboCurve = i != m_PositionCurves.end() ? &i->curve : NULL;
+ if (comboCurve == NULL)
+ {
+ m_PositionCurves.push_back(Vector3Curve());
+ m_PositionCurves.back().path = path;
+ comboCurve = &m_PositionCurves.back().curve;
+ }
+
+ // Combine the curve into a rotation curve
+ char last = attribute[attribute.size()-1];
+ if (last == 'x')
+ CombineCurve(*curve, 0, *comboCurve);
+ else if (last == 'y')
+ CombineCurve(*curve, 1, *comboCurve);
+ else if (last == 'z')
+ CombineCurve(*curve, 2, *comboCurve);
+ else
+ {
+ ErrorString("Can't assign curve because " + attribute + " is not a valid Transform property.");
+ }
+ }
+ else if (classID == ClassID(Transform) && (BeginsWith (attribute, "m_LocalScale") || BeginsWith (attribute, "localScale")))
+ {
+ // Find existing curve
+ Vector3Curves::iterator i;
+ for (i=m_ScaleCurves.begin();i != m_ScaleCurves.end();i++)
+ {
+ if (i->path == path)
+ break;
+ }
+
+ // Shall we remove a curve?
+ if (curve == NULL)
+ {
+ if (attribute == "m_LocalScale" || attribute == "localScale")
+ {
+ if (i != m_ScaleCurves.end())
+ {
+ m_ScaleCurves.erase(i);
+ ClipWasModified ();
+ SetDirty();
+ }
+ return;
+ }
+ else
+ {
+ ErrorString("Can't remove individual scale animation curve " + attribute + " you must remove the entire animation curve with m_LocalScale.");
+ return;
+ }
+ }
+
+ // Add the curve if it doesnt exist already
+ AnimationCurveVec3* comboCurve = i != m_ScaleCurves.end() ? &i->curve : NULL;
+ if (comboCurve == NULL)
+ {
+ m_ScaleCurves.push_back(Vector3Curve());
+ m_ScaleCurves.back().path = path;
+ comboCurve = &m_ScaleCurves.back().curve;
+ }
+
+ // Combine the curve into a rotation curve
+ char last = attribute[attribute.size()-1];
+ if (last == 'x')
+ CombineCurve(*curve, 0, *comboCurve);
+ else if (last == 'y')
+ CombineCurve(*curve, 1, *comboCurve);
+ else if (last == 'z')
+ CombineCurve(*curve, 2, *comboCurve);
+ else
+ {
+ ErrorString("Can't assign curve because " + attribute + " is not a valid Transform property.");
+ }
+ }
+ else
+ {
+ // Find existing curve
+ FloatCurves::iterator i;
+ for (i=m_FloatCurves.begin();i != m_FloatCurves.end();i++)
+ {
+ if (i->classID == classID && i->path == path && i->attribute == attribute && i->script == script)
+ break;
+ }
+
+ // Shall we remove a curve?
+ if (curve == NULL)
+ {
+ if (i != m_FloatCurves.end ())
+ {
+ m_FloatCurves.erase(i);
+ ClipWasModified ();
+ SetDirty();
+ }
+
+ return;
+ }
+
+ // Add or replace the curve
+ AnimationCurve* comboCurve = i != m_FloatCurves.end() ? &i->curve : NULL;
+ if (comboCurve == NULL)
+ {
+ m_FloatCurves.push_back(FloatCurve());
+ m_FloatCurves.back().path = path;
+ m_FloatCurves.back().attribute = attribute;
+ m_FloatCurves.back().classID = classID;
+ m_FloatCurves.back().script = script;
+ m_FloatCurves.back().curve = *curve;
+ }
+ else
+ *comboCurve = *curve;
+ }
+
+ ClipWasModified ();
+ SetDirty();
+}
+
+#if UNITY_EDITOR
+void AnimationClip::ConvertToNewCurveFormat (NewAnimationTrack& track, int classID, const string& path)
+{
+ NewAnimationTrack::Curves& curves = track.m_Curves;
+ for (NewAnimationTrack::Curves::iterator i=curves.begin();i!= curves.end();i++)
+ {
+ AnimationCurve& curve = i->curve;
+ SetCurve(path, classID, NULL, i->attributeName, &curve, true);
+ }
+}
+
+void AnimationClip::ConvertToNewCurveFormat ()
+{
+ for (ClassIDToTrack::iterator i=m_ClassIDToTrack.begin ();i != m_ClassIDToTrack.end ();i++)
+ {
+ NewAnimationTrack* track = dynamic_pptr_cast<NewAnimationTrack*> (i->second);
+ if (track)
+ ConvertToNewCurveFormat(*track, i->first, "");
+
+ SetDirty();
+ }
+
+ for (ChildTracks::iterator i=m_ChildTracks.begin ();i != m_ChildTracks.end ();i++)
+ {
+ NewAnimationTrack* track = dynamic_pptr_cast<NewAnimationTrack*> (i->track);
+ if (track)
+ ConvertToNewCurveFormat(*track, i->classID, i->path);
+
+ SetDirty();
+ }
+
+ // Just leak the actual animation track objects!
+ m_ClassIDToTrack.clear();
+ m_ChildTracks.clear();
+}
+#endif
+
+
+mecanim::animation::ClipMuscleConstant* AnimationClip::GetRuntimeAsset()
+{
+ #if UNITY_EDITOR
+ if (m_MuscleClip == NULL)
+ GenerateMuscleClip();
+ #endif
+
+ if (m_MuscleClip != 0 && m_MuscleClipSize != 0)
+ return m_MuscleClip;
+
+ return NULL;
+}
+
+void AnimationClip::GetStats(AnimationClipStats& stats)
+{
+ memset(&stats, 0, sizeof(stats));
+ stats.size = m_MuscleClipSize;
+
+ if (GetRuntimeAsset())
+ {
+ stats.totalCurves = 0;
+ for (int i=0;i<m_ClipBindingConstant.genericBindings.size();i++)
+ {
+ if (m_ClipBindingConstant.genericBindings[i].classID == ClassID(Transform))
+ {
+ switch (m_ClipBindingConstant.genericBindings[i].attribute)
+ {
+ case UnityEngine::Animation::kBindTransformPosition:
+ stats.positionCurves++;
+ break;
+ case UnityEngine::Animation::kBindTransformRotation:
+ stats.rotationCurves++;
+ break;
+ case UnityEngine::Animation::kBindTransformScale:
+ stats.scaleCurves++;
+ break;
+ }
+ }
+ else if (m_ClipBindingConstant.genericBindings[i].isPPtrCurve)
+ stats.pptrCurves++;
+ else if (IsMuscleBinding(m_ClipBindingConstant.genericBindings[i]))
+ stats.muscleCurves++;
+ else
+ stats.genericCurves++;
+
+ stats.totalCurves++;
+ }
+ }
+}
+
+bool AnimationClip::IsHumanMotion()
+{
+ return m_AnimationType == kHumanoid;
+}
+
+void AnimationClip::CleanupMecanimData()
+{
+ // Since the m_MuscleClip and all its data is placed on the m_Allocator, the destructor is not needed
+ m_MuscleClip = 0;
+ m_MuscleClipSize = 0;
+ m_ClipAllocator.Reset();
+
+ ///@TODO: Make destory function.
+ m_ClipBindingConstant.genericBindings.clear();
+ m_ClipBindingConstant.pptrCurveMapping.clear();
+}
+
+bool AnimationClip::IsAnimatorMotion()const
+{
+ return m_AnimationType == kHumanoid || m_AnimationType == kGeneric;
+}
+
+
+#if UNITY_EDITOR
+
+void AnimationClip::SetAnimationType(AnimationType type)
+{
+ m_AnimationType = type;
+}
+
+
+void AnimationClip::SetAnimationClipSettingsNoDirty(AnimationClipSettings &clipInfo)
+{
+ m_AnimationClipSettings = clipInfo;
+
+ if (GetRuntimeAsset())
+ PatchMuscleClipWithInfo (m_AnimationClipSettings, IsHumanMotion(), m_MuscleClip);
+}
+
+void AnimationClip::GenerateMuscleClip()
+{
+ CleanupMecanimData();
+
+ if (m_AnimationType == kLegacy)
+ return;
+
+ MecanimClipBuilder clipBuilder;
+ GenericAnimationBindingCache& binder = GetGenericAnimationBindingCache();
+
+ //// collect all curves, and count keys
+
+ // Position curves
+ for (Vector3Curves::iterator positionCurveIter = m_PositionCurves.begin (); positionCurveIter != m_PositionCurves.end (); positionCurveIter++)
+ AddPositionCurveToClipBuilder (positionCurveIter->curve, positionCurveIter->path, clipBuilder, m_UseHighQualityCurve);
+
+ // Rotation curves
+ for (QuaternionCurves::iterator rotationCurveIter = m_RotationCurves.begin (); rotationCurveIter != m_RotationCurves.end (); rotationCurveIter++)
+ AddRotationCurveToClipBuilder (rotationCurveIter->curve, rotationCurveIter->path, clipBuilder, m_UseHighQualityCurve);
+
+ // Scale curves
+ for (Vector3Curves::iterator scaleCurveIter = m_ScaleCurves.begin (); scaleCurveIter != m_ScaleCurves.end (); scaleCurveIter++)
+ AddScaleCurveToClipBuilder (scaleCurveIter->curve, scaleCurveIter->path, clipBuilder, m_UseHighQualityCurve);
+
+ // Dynamic binded curves
+ for (FloatCurves::iterator dynamicCurveIter = m_FloatCurves.begin(); dynamicCurveIter != m_FloatCurves.end (); dynamicCurveIter++)
+ {
+ GenericBinding binding;
+ binder.CreateGenericBinding (dynamicCurveIter->path, dynamicCurveIter->classID, dynamicCurveIter->script, dynamicCurveIter->attribute, false, binding);
+ AddGenericCurveToClipBuilder (dynamicCurveIter->curve, binding, clipBuilder, m_UseHighQualityCurve);
+ }
+
+ // PPtr curves
+ for (PPtrCurves::iterator pptrCurveIter = m_PPtrCurves.begin(); pptrCurveIter != m_PPtrCurves.end(); ++pptrCurveIter)
+ {
+ GenericBinding binding;
+ binder.CreateGenericBinding (pptrCurveIter->path, pptrCurveIter->classID, pptrCurveIter->script, pptrCurveIter->attribute, true, binding);
+ AddPPtrCurveToClipBuilder(pptrCurveIter->curve, binding, clipBuilder);
+ }
+
+ clipBuilder.hasAnimationEvents = HasAnimationEvents ();
+ clipBuilder.sampleRate = GetSampleRate();
+
+ if (!PrepareClipBuilder (clipBuilder))
+ return;
+
+ m_MuscleClip = BuildMuscleClip (clipBuilder, m_AnimationClipSettings, IsHumanMotion (), m_ClipBindingConstant, m_ClipAllocator);
+ if (m_MuscleClip)
+ {
+ BlobWrite::container_type blob;
+ BlobWrite blobWrite (blob, kNoTransferInstructionFlags, kBuildNoTargetPlatform);
+ blobWrite.Transfer( *m_MuscleClip, "Base");
+
+ m_MuscleClipSize = blob.size();
+ }
+}
+
+
+
+
+bool AnimationClip::ValidateIfRetargetable(bool showWarning)
+{
+ if(!IsAnimatorMotion())
+ {
+ if(showWarning)
+ WarningString (Format("Animation clip '%s' is not retargetable. Animation clips used within the Animator Controller need to have Muscle Definition set up in the Asset Importer Inspector", GetName()));
+ return false;
+ }
+ return true;
+}
+
+bool AnimationClip::IsLooping()
+{
+ return m_AnimationClipSettings.m_LoopTime;
+}
+
+
+float AnimationClip::GetAverageDuration()
+{
+ return m_AnimationClipSettings.m_StopTime - m_AnimationClipSettings.m_StartTime;
+}
+
+float AnimationClip::GetAverageAngularSpeed()
+{
+ return m_MuscleClip != 0 ? m_MuscleClip->m_AverageAngularSpeed : 0;
+}
+
+Vector3f AnimationClip::GetAverageSpeed()
+{
+ return m_MuscleClip != 0 ? float4ToVector3f(m_MuscleClip->m_AverageSpeed) : Vector3f::zero;
+}
+
+float AnimationClip::GetApparentSpeed()
+{
+ // approximation of equivalent rectilinear motion speed that conserves kinectic energy ... i'll work on something better
+ return Magnitude(GetAverageSpeed()) * (1 + pow(GetAverageAngularSpeed()/2,2));
+}
+
+#endif
+
+AnimationClip::~AnimationClip ()
+{
+ gDidModifyClipCallback (NULL, m_AnimationStates);
+ m_MuscleClip = 0;
+ NotifyObjectUsers(kDidModifyMotion);
+}
+
+pair<float, float> AnimationClip::GetRange ()
+{
+ pair<float, float> range = make_pair (std::numeric_limits<float>::infinity (), -std::numeric_limits<float>::infinity ());
+ if (range != m_CachedRange)
+ return m_CachedRange;
+ for (QuaternionCurves::iterator i=m_RotationCurves.begin ();i != m_RotationCurves.end ();i++)
+ {
+ pair<float, float> curRange = i->curve.GetRange ();
+ range.first = min (curRange.first, range.first);
+ range.second = max (curRange.second, range.second);
+ }
+
+ for (Vector3Curves::iterator i=m_PositionCurves.begin ();i != m_PositionCurves.end ();i++)
+ {
+ pair<float, float> curRange = i->curve.GetRange ();
+ range.first = min (curRange.first, range.first);
+ range.second = max (curRange.second, range.second);
+ }
+
+ for (Vector3Curves::iterator i=m_ScaleCurves.begin ();i != m_ScaleCurves.end ();i++)
+ {
+ pair<float, float> curRange = i->curve.GetRange ();
+ range.first = min (curRange.first, range.first);
+ range.second = max (curRange.second, range.second);
+ }
+
+ for (FloatCurves::iterator i=m_FloatCurves.begin ();i != m_FloatCurves.end ();i++)
+ {
+ pair<float, float> curRange = i->curve.GetRange ();
+ range.first = min (curRange.first, range.first);
+ range.second = max (curRange.second, range.second);
+ }
+
+ for (PPtrCurves::iterator i=m_PPtrCurves.begin ();i != m_PPtrCurves.end ();i++)
+ {
+ if (i->curve.empty())
+ continue;
+
+ range.first = min (i->curve.front().time, range.first);
+ range.second = max (i->curve.back().time + 1.0F / m_SampleRate, range.second);
+ }
+
+ #if UNITY_EDITOR
+ // get a valid range for muscle clip when importing
+ for (FloatCurves::iterator i=m_EditorCurves.begin ();i != m_EditorCurves.end ();i++)
+ {
+ pair<float, float> curRange = i->curve.GetRange ();
+ range.first = min (curRange.first, range.first);
+ range.second = max (curRange.second, range.second);
+ }
+ #endif
+
+ if (!m_Events.empty())
+ {
+ range.first = min (m_Events.front().time, range.first);
+ range.second = max (m_Events.back().time, range.second);
+ }
+
+ if (range.first == std::numeric_limits<float>::infinity() && range.second == -std::numeric_limits<float>::infinity())
+ {
+ // arbitrary range - the length shouldn't matter because it doesn't have any keys or events anyway
+ // TODO : do not allow to create clips without any keys or events or least it show a warning (LocomotionSystem creates such clip now)
+ range.first = 0;
+ range.second = 1;
+ }
+
+ m_CachedRange = range;
+ AssertIf(!IsFinite(m_CachedRange.first) || !IsFinite(m_CachedRange.second));
+
+ return m_CachedRange;
+}
+
+IMPLEMENT_CLASS_HAS_INIT (AnimationClip)
+IMPLEMENT_OBJECT_SERIALIZE (AnimationClip)
+
+void AnimationClip::InitializeClass ()
+{
+ // 2.6 beta -> 2.6 final compatibility
+ RegisterAllowNameConversion("AnimationClip", "m_UseCompression", "m_Compressed");
+ // 4.3
+ RegisterAllowNameConversion("AnimationClip", "m_MuscleClipInfo", "m_AnimationClipSettings");
+}
+
+#if UNITY_EDITOR
+class StripCurvesForMecanimClips
+{
+public:
+
+ AnimationClip* clip;
+ AnimationClip::QuaternionCurves rotationCurves;
+ AnimationClip::Vector3Curves positionCurves;
+ AnimationClip::Vector3Curves scaleCurves;
+ AnimationClip::FloatCurves floatCurves;
+ AnimationClip::PPtrCurves pptrCurves;
+ bool stripCurves;
+
+ StripCurvesForMecanimClips (AnimationClip& inputClip, bool inStripCurves)
+ {
+ if (inStripCurves)
+ {
+ clip = &inputClip;
+ rotationCurves.swap (clip->m_RotationCurves);
+ positionCurves.swap (clip->m_PositionCurves);
+ scaleCurves.swap (clip->m_ScaleCurves);
+ floatCurves.swap (clip->m_FloatCurves);
+ pptrCurves.swap (clip->m_PPtrCurves);
+ }
+ else
+ {
+ clip = NULL;
+ }
+ }
+
+ ~StripCurvesForMecanimClips ()
+ {
+ if (clip)
+ {
+ rotationCurves.swap (clip->m_RotationCurves);
+ positionCurves.swap (clip->m_PositionCurves);
+ scaleCurves.swap (clip->m_ScaleCurves);
+ floatCurves.swap (clip->m_FloatCurves);
+ pptrCurves.swap (clip->m_PPtrCurves);
+ }
+ }
+};
+#endif
+
+static void ConvertDeprecatedValueArrayConstantBindingToGenericBinding (const mecanim::animation::ClipMuscleConstant* muscleClip, UnityEngine::Animation::AnimationClipBindingConstant& bindings)
+{
+ if (muscleClip == NULL || muscleClip->m_Clip.IsNull() || muscleClip->m_Clip->m_DeprecatedBinding.IsNull())
+ return;
+
+ const mecanim::ValueArrayConstant& values = *muscleClip->m_Clip->m_DeprecatedBinding;
+
+ for (int i=0;i<values.m_Count;)
+ {
+ mecanim::uint32_t curveID = values.m_ValueArray[i].m_ID;
+ mecanim::uint32_t curveTypeID = values.m_ValueArray[i].m_TypeID;
+
+ bindings.genericBindings.push_back(GenericBinding());
+ GenericBinding& binding = bindings.genericBindings.back();
+
+ if(curveTypeID == mecanim::CRCKey(mecanim::ePositionX))
+ {
+ binding.path = curveID;
+ binding.attribute = UnityEngine::Animation::kBindTransformPosition;
+ binding.classID = ClassID(Transform);
+ i+=3;
+ }
+ else if(curveTypeID == mecanim::CRCKey(mecanim::eQuaternionX))
+ {
+ binding.path = curveID;
+ binding.attribute = UnityEngine::Animation::kBindTransformRotation;
+ binding.classID = ClassID(Transform);
+ i+=4;
+ }
+ else if(curveTypeID == mecanim::CRCKey(mecanim::eScaleX))
+ {
+ binding.path = curveID;
+ binding.attribute = UnityEngine::Animation::kBindTransformScale;
+ binding.classID = ClassID(Transform);
+ i+=3;
+ }
+ else
+ {
+ if(curveTypeID == mecanim::CRCKey(mecanim::ePositionY) || curveTypeID == mecanim::CRCKey(mecanim::ePositionZ) || curveTypeID == mecanim::CRCKey(mecanim::eQuaternionY) || curveTypeID == mecanim::CRCKey(mecanim::eQuaternionZ) || curveTypeID == mecanim::CRCKey(mecanim::eQuaternionW) || curveTypeID == mecanim::CRCKey(mecanim::eScaleY) || curveTypeID == mecanim::CRCKey(mecanim::eScaleZ))
+ {
+ AssertString("Invalid value array data");
+ }
+ //
+ binding.classID = ClassID(Animator);
+ binding.path = 0;
+ binding.attribute = curveID;
+
+ i++;
+ }
+ }
+}
+
+template<class TransferFunction>
+void AnimationClip::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion (4);
+
+ TRANSFER_ENUM(m_AnimationType);
+
+ // Strip mecanim curve data for non-legacy clips
+ #if UNITY_EDITOR
+ StripCurvesForMecanimClips revert (*this, transfer.IsWritingGameReleaseData() && m_AnimationType != kLegacy);
+ #endif
+
+ // Backwards compatibility with ancient tracks
+ #if UNITY_EDITOR
+ if (transfer.IsOldVersion(2) || transfer.IsOldVersion(1))
+ {
+ transfer.Transfer (m_ClassIDToTrack, "m_ClassIDToTrack", kHideInEditorMask);
+ transfer.Transfer (m_ChildTracks, "m_ChildTracks", kHideInEditorMask);
+ }
+ #endif
+
+ // Rotation curves / potentially compressed
+ transfer.Transfer (m_Compressed, "m_Compressed", kNotEditableMask);
+ transfer.Transfer (m_UseHighQualityCurve, "m_UseHighQualityCurve", kNotEditableMask);
+ transfer.Align();
+
+ if(!m_Compressed)
+ {
+ transfer.Transfer (m_RotationCurves, "m_RotationCurves", kHideInEditorMask);
+ CompressedQuaternionCurves empty;
+ transfer.Transfer (empty, "m_CompressedRotationCurves", kHideInEditorMask);
+ }
+ else
+ {
+ QuaternionCurves empty;
+ transfer.Transfer (empty, "m_RotationCurves", kHideInEditorMask);
+
+ TRANSFER_WITH_CUSTOM_GET_SET (CompressedQuaternionCurves, "m_CompressedRotationCurves",
+ CompressCurves(value),
+ DecompressCurves(value),
+ kHideInEditorMask);
+ }
+
+ transfer.Transfer (m_PositionCurves, "m_PositionCurves", kHideInEditorMask);
+ transfer.Transfer (m_ScaleCurves, "m_ScaleCurves", kHideInEditorMask);
+ transfer.Transfer (m_FloatCurves, "m_FloatCurves", kHideInEditorMask);
+ transfer.Transfer (m_PPtrCurves, "m_PPtrCurves", kHideInEditorMask);
+ transfer.Transfer (m_SampleRate, "m_SampleRate");
+ transfer.Transfer (m_WrapMode, "m_WrapMode");
+ transfer.Transfer (m_Bounds, "m_Bounds");
+
+ if (transfer.IsSerializingForGameRelease())
+ {
+ TRANSFER(m_MuscleClipSize);
+
+ if(m_MuscleClip == 0)
+ m_ClipAllocator.Reserve(m_MuscleClipSize);
+
+ // Enforce that there is always a valid muscle clip when building for the player.
+ if (transfer.IsWritingGameReleaseData ())
+ GetRuntimeAsset ();
+
+ transfer.SetUserData(&m_ClipAllocator);
+ TRANSFER_NULLABLE(m_MuscleClip, mecanim::animation::ClipMuscleConstant);
+ TRANSFER(m_ClipBindingConstant);
+
+ if (transfer.IsReadingBackwardsCompatible())
+ ConvertDeprecatedValueArrayConstantBindingToGenericBinding (m_MuscleClip, m_ClipBindingConstant);
+ }
+
+
+ // Editor curves
+ #if UNITY_EDITOR
+ if (!transfer.IsSerializingForGameRelease())
+ {
+ transfer.Transfer (m_AnimationClipSettings, "m_AnimationClipSettings");
+ transfer.Transfer (m_EditorCurves, "m_EditorCurves", kHideInEditorMask);
+ transfer.Transfer (m_EulerEditorCurves, "m_EulerEditorCurves", kHideInEditorMask);
+ }
+ #endif
+
+ // Events
+ #if UNITY_EDITOR
+ transfer.Transfer (m_EditModeEvents, "m_Events", kHideInEditorMask);
+ if (transfer.IsReading())
+ m_Events = m_EditModeEvents;
+ #else
+ transfer.Transfer (m_Events, "m_Events", kHideInEditorMask);
+ #endif
+
+ #if UNITY_EDITOR
+ if (transfer.IsVersionSmallerOrEqual(3))
+ {
+ SyncMuscleCurvesBackwardCompatibility();
+ }
+ #endif
+}
+
+void AnimationClip::CompressCurves (CompressedQuaternionCurves& compressedRotationCurves)
+{
+ bool didShowError = false;
+ compressedRotationCurves.resize(m_RotationCurves.size());
+
+ for(int i=0;i<compressedRotationCurves.size();i++)
+ {
+ compressedRotationCurves[i].CompressQuatCurve(m_RotationCurves[i]);
+ if( m_RotationCurves[i].curve.GetKeyCount() > 0 && !didShowError)
+ {
+ if( m_RotationCurves[i].curve.GetKey(0).time < -kCurveTimeEpsilon)
+ {
+ LogStringObject(Format("Animation Clip %s contains negative time keys. This may cause your animation to look wrong, as negative time keys are not supported in compressed animation clips!",this->GetName()),this);
+ didShowError = true;
+ }
+ }
+ }
+}
+
+void AnimationClip::DecompressCurves (CompressedQuaternionCurves& compressedRotationCurves)
+{
+ SET_ALLOC_OWNER(this);
+ m_RotationCurves.resize(compressedRotationCurves.size());
+
+ for(int i=0;i<compressedRotationCurves.size();i++)
+ compressedRotationCurves[i].DecompressQuatCurve(m_RotationCurves[i]);
+}
+
+
+#if UNITY_EDITOR
+template<class TransferFunction>
+void AnimationClip::ChildTrack::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (path);
+ TRANSFER (classID);
+ TRANSFER (track);
+}
+#endif
+
+template<class TransferFunction>
+void AnimationClip::QuaternionCurve::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (curve);
+ TRANSFER (path);
+}
+
+template<class TransferFunction>
+void AnimationClip::Vector3Curve::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (curve);
+ TRANSFER (path);
+}
+
+template<class TransferFunction>
+void AnimationClip::FloatCurve::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (curve);
+ TRANSFER (attribute);
+ TRANSFER (path);
+ TRANSFER (classID);
+ TRANSFER (script);
+}
+
+template<class TransferFunction>
+void AnimationClip::PPtrCurve::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (curve);
+ TRANSFER (attribute);
+ TRANSFER (path);
+ TRANSFER (classID);
+ TRANSFER (script);
+}
+
+template<class TransferFunction>
+void PPtrKeyframe::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (time);
+ TRANSFER (value);
+}
+
+void AnimationClip::SetDidModifyClipCallback(DidModifyClipCallback* callback)
+{
+ gDidModifyClipCallback = callback;
+}
+
+void AnimationClip::AddRotationCurve (const AnimationCurveQuat& quat, const std::string& path)
+{
+ SET_ALLOC_OWNER(this);
+ m_RotationCurves.push_back(QuaternionCurve());
+ m_RotationCurves.back().curve = quat;
+ m_RotationCurves.back().path = path;
+}
+
+void AnimationClip::AddPositionCurve (const AnimationCurveVec3& quat, const std::string& path)
+{
+ SET_ALLOC_OWNER(this);
+ m_PositionCurves.push_back(Vector3Curve());
+ m_PositionCurves.back().curve = quat;
+ m_PositionCurves.back().path = path;
+}
+
+void AnimationClip::AddScaleCurve (const AnimationCurveVec3& quat, const std::string& path)
+{
+ SET_ALLOC_OWNER(this);
+ m_ScaleCurves.push_back(Vector3Curve());
+ m_ScaleCurves.back().curve = quat;
+ m_ScaleCurves.back().path = path;
+}
+
+void AnimationClip::AddFloatCurve (const AnimationCurve& curve, const std::string& path, int classID, const std::string& attribute)
+{
+ m_FloatCurves.push_back(FloatCurve());
+ FloatCurve& fc = m_FloatCurves.back();
+ fc.curve = curve;
+ fc.path = path;
+ fc.classID = classID;
+ fc.attribute = attribute;
+}
diff --git a/Runtime/Animation/AnimationClip.h b/Runtime/Animation/AnimationClip.h
new file mode 100644
index 0000000..1c4c1c2
--- /dev/null
+++ b/Runtime/Animation/AnimationClip.h
@@ -0,0 +1,346 @@
+#ifndef ANIMATIONCLIP_H
+#define ANIMATIONCLIP_H
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/Serialize/SerializeTraits.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Math/AnimationCurve.h"
+#include "AnimationEvent.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/mecanim/memory.h"
+#include "AnimationClipBindings.h"
+#include "PPtrKeyframes.h"
+
+#include "Motion.h"
+
+#if UNITY_EDITOR
+#include "AnimationClipSettings.h"
+#endif
+
+namespace mecanim
+{
+ namespace animation
+ {
+ struct ClipMuscleConstant;
+ struct ClipMuscleInput;
+ }
+}
+
+struct AnimationClipStats;
+
+namespace Unity { class GameObject; }
+using namespace Unity;
+
+class BaseAnimationTrack;
+class NewAnimationTrack;
+class MonoScript;
+class Animation;
+class AnimationState;
+class CompressedAnimationCurve;
+
+/*
+ TODO:
+ * We currently don't handle double cover operator automatically for rotation curves
+ * We are not synchronizing animation state cached range correctly
+ * GetCurve is not implemented yet
+*/
+
+class AnimationClip : public Motion
+{
+public:
+ struct QuaternionCurve
+ {
+ UnityStr path;
+ AnimationCurveQuat curve;
+ int hash;
+
+ QuaternionCurve () { hash = 0; }
+ void CopyWithoutCurve(QuaternionCurve& other) const
+ {
+ other.path = path;
+ other.hash = hash;
+ }
+
+ DECLARE_SERIALIZE (QuaternionCurve)
+ };
+
+ struct Vector3Curve
+ {
+ UnityStr path;
+ AnimationCurveVec3 curve;
+ int hash;
+
+ Vector3Curve () { hash = 0; }
+ void CopyWithoutCurve(Vector3Curve& other) const
+ {
+ other.path = path;
+ other.hash = hash;
+ }
+
+ DECLARE_SERIALIZE (Vector3Curve)
+ };
+
+public:
+
+
+ struct PPtrCurve
+ {
+ UnityStr path;
+ UnityStr attribute;
+ int classID;
+ MonoScriptPPtr script;
+ PPtrKeyframes curve;
+
+ PPtrCurve () { }
+
+ DECLARE_SERIALIZE (PPtrCurve)
+ };
+
+ struct FloatCurve
+ {
+ UnityStr path;
+ UnityStr attribute;
+ int classID;
+ MonoScriptPPtr script;
+ AnimationCurve curve;
+ int hash;
+
+ FloatCurve () { hash = 0; }
+ void CopyWithoutCurve(FloatCurve& other) const
+ {
+ other.path = path;
+ other.attribute = attribute;
+ other.classID = classID;
+ other.script = script;
+ other.hash = hash;
+ }
+
+ DECLARE_SERIALIZE (FloatCurve)
+ };
+
+ typedef UNITY_VECTOR(kMemAnimation, QuaternionCurve) QuaternionCurves;
+ typedef UNITY_VECTOR(kMemAnimation, CompressedAnimationCurve) CompressedQuaternionCurves;
+ typedef UNITY_VECTOR(kMemAnimation, Vector3Curve) Vector3Curves;
+ typedef UNITY_VECTOR(kMemAnimation, FloatCurve) FloatCurves;
+ typedef UNITY_VECTOR(kMemAnimation, PPtrCurve) PPtrCurves;
+ typedef UNITY_VECTOR(kMemAnimation, AnimationEvent) Events;
+
+ enum AnimationType
+ {
+ kLegacy = 1,
+ kGeneric = 2,
+ kHumanoid = 3
+ };
+
+
+
+ REGISTER_DERIVED_CLASS (AnimationClip, Motion)
+ DECLARE_OBJECT_SERIALIZE (AnimationClip)
+
+ static void InitializeClass ();
+ static void CleanupClass () { }
+
+ AnimationClip (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~AnimationClip(); declared-by-macro
+
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+ virtual void CheckConsistency();
+
+
+ /// Assigns curve to the curve defined by path, classID and attribute
+ /// If curve is null the exisiting curve will be removed.
+ void SetCurve (const std::string& path, int classID, MonoScriptPPtr script, const std::string& attribute, AnimationCurve* curve, bool syncEditorCurves);
+ bool GetCurve (const std::string& path, int classID, MonoScriptPPtr script, const std::string& attribute, AnimationCurve* outCurve);
+
+ void ClearCurves ();
+ void EnsureQuaternionContinuity ();
+
+ bool HasAnimationEvents ();
+ void FireAnimationEvents (float lastTime, float now, Unity::Component& source);
+
+ #if UNITY_EDITOR
+ void SetEditorCurve (const std::string& path, int classID, MonoScriptPPtr script, const std::string& attribute, const AnimationCurve* curve, bool syncEditorCurves = true);
+ bool GetEditorCurve (const std::string& path, int classID, MonoScriptPPtr script, const std::string& attribute, AnimationCurve* outCurve);
+ FloatCurves& GetEditorCurvesSync();
+
+ void SetEditorPPtrCurve (const std::string& path, int classID, MonoScriptPPtr script, const std::string& attribute, PPtrKeyframes* keyframes);
+ bool GetEditorPPtrCurve (const std::string& path, int classID, MonoScriptPPtr script, const std::string& attribute, PPtrKeyframes* outKeyframes);
+ PPtrCurves& GetEditorPPtrCurves() { return m_PPtrCurves; }
+
+ // Callback for telling animation window when an animclip got reloaded
+ typedef void OnAnimationClipAwake(AnimationClip* clip);
+ static void SetOnAnimationClipAwake (OnAnimationClipAwake *callback);
+
+ void SyncEditorCurves();
+ void SyncMuscleCurvesBackwardCompatibility();
+
+ void SetEvents (const AnimationEvent* events, int size, bool sort = false);
+ void ClearEvents ();
+ FloatCurves& GetEulerEditorCurves() { return m_EulerEditorCurves; }
+ virtual void CloneAdditionalEditorProperties (Object& src);
+ FloatCurves& GetEditorCurvesNoConversion () { return m_EditorCurves; }
+
+
+ const AnimationClipSettings& GetAnimationClipSettings() const { return m_AnimationClipSettings; }
+ void SetAnimationClipSettingsNoDirty (AnimationClipSettings &clipInfo);
+ void SetAnimationClipSettings (const AnimationClipSettings&clipInfo) { m_AnimationClipSettings = clipInfo; SetDirty(); }
+
+ void GenerateMuscleClip();
+
+ void SetAnimationType (AnimationType type);
+
+
+ // overloads from Motion
+ virtual float GetAverageDuration();
+ virtual float GetAverageAngularSpeed();
+ virtual Vector3f GetAverageSpeed();
+ virtual float GetApparentSpeed();
+
+ virtual bool ValidateIfRetargetable(bool showWarning = true);
+ virtual bool IsLooping();
+
+ #endif
+
+ virtual bool IsAnimatorMotion()const;
+
+ // Returns the smallest and largest keyframe time of any channel in the animation
+ // if no keyframes are contained make_pair (infinity, -infinity) is returned
+ std::pair<float, float> GetRange ();
+
+ // Animation State support
+ typedef List< ListNode<AnimationState> > AnimationStateList;
+ typedef void DidModifyClipCallback(AnimationClip* clip, AnimationStateList& states);
+
+ static void SetDidModifyClipCallback(DidModifyClipCallback* callback);
+ void AddAnimationState(ListNode<AnimationState>& node) { m_AnimationStates.push_back(node); }
+
+ void SetSampleRate (float s);
+ float GetSampleRate () { return m_SampleRate; }
+
+ void SetCompressionEnabled (bool s) { m_Compressed = s; }
+ bool GetCompressionEnabled () { return m_Compressed; }
+#if UNITY_EDITOR
+ void SetUseHighQualityCurve (bool s) { m_UseHighQualityCurve = s; }
+ bool GetUseHighQualityCurve ()const { return m_UseHighQualityCurve; }
+#endif
+ void SetWrapMode (int wrap) { m_WrapMode = wrap; SetDirty (); }
+ int GetWrapMode () { return m_WrapMode; }
+
+ Events& GetEvents () { return m_Events; }
+ void AddRuntimeEvent (AnimationEvent& event);
+
+ void AddRotationCurve (const AnimationCurveQuat& quat, const std::string& path);
+ void AddPositionCurve (const AnimationCurveVec3& quat, const std::string& path);
+ void AddScaleCurve (const AnimationCurveVec3& quat, const std::string& path);
+ void AddFloatCurve (const AnimationCurve& curve, const std::string& path, int classID, const std::string& attribute);
+
+ QuaternionCurves& GetRotationCurves() { return m_RotationCurves; }
+ Vector3Curves& GetPositionCurves() { return m_PositionCurves; }
+ Vector3Curves& GetScaleCurves() { return m_ScaleCurves; }
+ FloatCurves& GetFloatCurves() { return m_FloatCurves; }
+
+ const QuaternionCurves& GetRotationCurves() const { return m_RotationCurves; }
+ const Vector3Curves& GetPositionCurves() const { return m_PositionCurves; }
+ const Vector3Curves& GetScaleCurves() const { return m_ScaleCurves; }
+ const FloatCurves& GetFloatCurves() const { return m_FloatCurves; }
+
+ void SetBounds(const AABB& bounds) { m_Bounds = bounds; SetDirty(); }
+ const AABB& GetBounds() const { return m_Bounds; }
+
+ static void RevertAllPlaymodeAnimationEvents ();
+
+ bool IsHumanMotion();
+
+ AnimationType GetAnimationType () const { return m_AnimationType; }
+
+
+ UnityEngine::Animation::AnimationClipBindingConstant* GetBindingConstant () { return &m_ClipBindingConstant; }
+ mecanim::animation::ClipMuscleConstant* GetRuntimeAsset();
+
+ void GetStats(AnimationClipStats& stats);
+
+ void ClipWasModifiedAndUpdateMuscleRange ();
+ void UpdateMuscleClipRange ();
+
+
+private:
+ void CompressCurves (CompressedQuaternionCurves& compressedRotationCurves);
+ void DecompressCurves (CompressedQuaternionCurves& compressedRotationCurves);
+
+ void ClipWasModified (bool cleanupMecanimData = true);
+
+ void ReloadEditorEulerCurves (const string& path);
+ void ReloadEditorQuaternionCurves (const string& path);
+
+ void ConvertToNewCurveFormat (NewAnimationTrack& curves, int classID, const std::string& path);
+ void ConvertToNewCurveFormat ();
+
+ void CleanupMecanimData();
+
+ mecanim::memory::ChainedAllocator m_ClipAllocator;
+
+private:
+ AnimationStateList m_AnimationStates;
+
+ float m_SampleRate;
+ bool m_Compressed;
+ bool m_UseHighQualityCurve;
+ int m_WrapMode;///< enum { Default = 0, Once = 1, Loop = 2, PingPong = 4, ClampForever = 8 }
+
+ QuaternionCurves m_RotationCurves;
+ Vector3Curves m_PositionCurves;
+ Vector3Curves m_ScaleCurves;
+ FloatCurves m_FloatCurves;
+ PPtrCurves m_PPtrCurves;
+ Events m_Events;
+ AnimationType m_AnimationType;
+
+#if UNITY_EDITOR
+ AnimationClipSettings m_AnimationClipSettings;
+#endif//#if UNITY_EDITOR
+
+ mecanim::animation::ClipMuscleConstant* m_MuscleClip;
+ mecanim::uint32_t m_MuscleClipSize;
+ UnityEngine::Animation::AnimationClipBindingConstant m_ClipBindingConstant;
+
+ /// TODO: Serialiaze and do not compute it at all on startup
+ std::pair<float, float> m_CachedRange;
+
+ AABB m_Bounds;
+
+ #if UNITY_EDITOR
+
+ // Keep a copy of events setup from edit mode so we can safely revert events after playmode
+ Events m_EditModeEvents;
+ FloatCurves m_EditorCurves;
+ FloatCurves m_EulerEditorCurves;
+
+ struct ChildTrack
+ {
+ UnityStr path;
+ int classID;
+ PPtr<BaseAnimationTrack> track;
+ DECLARE_SERIALIZE (ChildTrack)
+ };
+
+ typedef vector_map<SInt32, PPtr<BaseAnimationTrack> > ClassIDToTrack;
+ typedef ClassIDToTrack::iterator iterator;
+ typedef std::vector<ChildTrack> ChildTracks;
+ typedef ChildTracks::iterator child_iterator;
+
+ ClassIDToTrack m_ClassIDToTrack;
+ ChildTracks m_ChildTracks;
+
+ friend class StripCurvesForMecanimClips;
+
+ #endif
+
+ friend class AnimationManager;
+};
+
+typedef std::vector<PPtr<AnimationClip> > AnimationClipVector;
+
+#endif
diff --git a/Runtime/Animation/AnimationClipBindings.h b/Runtime/Animation/AnimationClipBindings.h
new file mode 100644
index 0000000..b91362c
--- /dev/null
+++ b/Runtime/Animation/AnimationClipBindings.h
@@ -0,0 +1,89 @@
+#pragma once
+
+#include "Runtime/BaseClasses/BaseObject.h"
+
+typedef UInt32 BindingHash;
+
+namespace UnityEngine
+{
+namespace Animation
+{
+
+struct GenericBinding
+{
+ BindingHash path;
+ BindingHash attribute;
+ PPtr<Object> script;
+ UInt16 classID;
+ UInt8 customType;
+ UInt8 isPPtrCurve;
+
+ GenericBinding () : path (0), attribute(0), classID(0), customType(0), isPPtrCurve(0)
+ {
+
+ }
+
+
+ DECLARE_SERIALIZE (GenericBinding)
+};
+
+struct AnimationClipBindingConstant
+{
+ dynamic_array<GenericBinding> genericBindings;
+ dynamic_array<PPtr<Object> > pptrCurveMapping;
+
+ DECLARE_SERIALIZE (AnimationClipBindingConstant)
+};
+
+template<class TransferFunc> inline
+void GenericBinding::Transfer (TransferFunc& transfer)
+{
+ TRANSFER(path);
+ TRANSFER(attribute);
+ TRANSFER(script);
+ TRANSFER(classID);
+ TRANSFER(customType);
+ TRANSFER(isPPtrCurve);
+}
+
+template<class TransferFunc> inline
+void AnimationClipBindingConstant::Transfer (TransferFunc& transfer)
+{
+ TRANSFER(genericBindings);
+ TRANSFER(pptrCurveMapping);
+}
+
+inline bool operator < (const GenericBinding& lhs, const GenericBinding& rhs)
+{
+ // Transform components are sorted first by attribute, then by path.
+ // This is because when creating the ValueArrayConstant.
+ // We want scale curves at the end. This is because scale curves are most likely to not actually be changed
+ // Thus more likely to be culled away by the constant clip optimization code.
+ if (lhs.classID == ClassID (Transform) && rhs.classID == ClassID (Transform))
+ {
+ if (lhs.attribute != rhs.attribute)
+ return lhs.attribute < rhs.attribute;
+
+ return lhs.path < rhs.path;
+ }
+
+ // All transform bindings always come first
+ int lhsClassID = lhs.classID == ClassID (Transform) ? -1 : lhs.classID;
+ int rhsClassID = rhs.classID == ClassID (Transform) ? -1 : rhs.classID;
+
+ if (lhsClassID != rhsClassID)
+ return lhsClassID < rhsClassID;
+ else if (lhs.isPPtrCurve != rhs.isPPtrCurve)
+ return lhs.isPPtrCurve < rhs.isPPtrCurve;
+ else if (lhs.customType != rhs.customType)
+ return lhs.customType < rhs.customType;
+ else if (lhs.path != rhs.path)
+ return lhs.path < rhs.path;
+ else if (lhs.script != rhs.script)
+ return lhs.script < rhs.script;
+ else
+ return lhs.attribute < rhs.attribute;
+}
+
+}
+}
diff --git a/Runtime/Animation/AnimationClipSettings.h b/Runtime/Animation/AnimationClipSettings.h
new file mode 100644
index 0000000..c866b9f
--- /dev/null
+++ b/Runtime/Animation/AnimationClipSettings.h
@@ -0,0 +1,75 @@
+#pragma once
+
+#include "Runtime/Misc/BuildSettings.h"
+
+struct AnimationClipSettings
+{
+ float m_StartTime;
+ float m_StopTime;
+ float m_OrientationOffsetY;
+ float m_Level;
+ float m_CycleOffset;
+
+ bool m_LoopTime;
+ bool m_LoopBlend;
+ bool m_LoopBlendOrientation;
+ bool m_LoopBlendPositionY;
+ bool m_LoopBlendPositionXZ;
+ bool m_KeepOriginalOrientation;
+ bool m_KeepOriginalPositionY;
+ bool m_KeepOriginalPositionXZ;
+ bool m_HeightFromFeet;
+ bool m_Mirror;
+
+ DEFINE_GET_TYPESTRING (MuscleClipInfo)
+
+ AnimationClipSettings()
+ {
+ m_StartTime = 0;
+ m_StopTime = 1;
+ m_OrientationOffsetY = 0;
+ m_Level = 0;
+ m_CycleOffset = 0;
+
+ m_LoopTime = false;
+ m_LoopBlend = false;
+ m_LoopBlendOrientation = false;
+ m_LoopBlendPositionY = false;
+ m_LoopBlendPositionXZ = false;
+ m_KeepOriginalOrientation = false;
+ m_KeepOriginalPositionY = true;
+ m_KeepOriginalPositionXZ = false;
+ m_HeightFromFeet = false;
+ m_Mirror = false;
+ }
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer)
+ {
+ transfer.SetVersion(2);
+
+ TRANSFER(m_StartTime);
+ TRANSFER(m_StopTime);
+ TRANSFER(m_OrientationOffsetY);
+ TRANSFER(m_Level);
+ TRANSFER(m_CycleOffset);
+
+ TRANSFER(m_LoopTime);
+ TRANSFER(m_LoopBlend);
+
+ TRANSFER(m_LoopBlendOrientation);
+ TRANSFER(m_LoopBlendPositionY);
+ TRANSFER(m_LoopBlendPositionXZ);
+ TRANSFER(m_KeepOriginalOrientation);
+ TRANSFER(m_KeepOriginalPositionY);
+ TRANSFER(m_KeepOriginalPositionXZ);
+ TRANSFER(m_HeightFromFeet);
+ TRANSFER(m_Mirror);
+ transfer.Align();
+
+ // Backwards compatibility (4.3 introduced seperate loopTime / loopBlend)
+ if (transfer.IsVersionSmallerOrEqual(1))
+ m_LoopTime = m_LoopBlend;
+ }
+};
+
diff --git a/Runtime/Animation/AnimationClipStats.h b/Runtime/Animation/AnimationClipStats.h
new file mode 100644
index 0000000..a1a46ba
--- /dev/null
+++ b/Runtime/Animation/AnimationClipStats.h
@@ -0,0 +1,14 @@
+#pragma once
+
+// Must be kept in sync with AnimationClipStats in AnimationUtility.txt
+struct AnimationClipStats
+{
+ int size;
+ int positionCurves;
+ int rotationCurves;
+ int scaleCurves;
+ int muscleCurves;
+ int genericCurves;
+ int pptrCurves;
+ int totalCurves;
+}; \ No newline at end of file
diff --git a/Runtime/Animation/AnimationClipUtility.cpp b/Runtime/Animation/AnimationClipUtility.cpp
new file mode 100644
index 0000000..7ea1963
--- /dev/null
+++ b/Runtime/Animation/AnimationClipUtility.cpp
@@ -0,0 +1,73 @@
+#include "UnityPrefix.h"
+#include "AnimationClipUtility.h"
+#include "AnimationClip.h"
+#include "AnimationCurveUtility.h"
+
+template <class T>
+void EnsureLoopFrameContinuity (AnimationCurveTpl<T>& curve) {}
+
+template <>
+void EnsureLoopFrameContinuity<Quaternionf> (AnimationCurveTpl<Quaternionf>& curve)
+{
+ EnsureQuaternionContinuityLoopFrame(curve);
+}
+
+template <class U, class T, typename A>
+void ClipAnimations (const std::vector<T, A>& curves, float startTime, float endTime, float sampleRate, bool duplicateLastFrame, std::vector<T, A>& destinationCurves)
+{
+ for (typename std::vector<T, A>::const_iterator it = curves.begin(); it != curves.end(); ++it)
+ {
+ T newCurve;
+ AssertMsg(it->curve.GetKeyCount() >= 2, "Key count: %d on curve %s", it->curve.GetKeyCount(), it->path.c_str());
+
+ if (ClipAnimationCurve (it->curve, newCurve.curve, startTime, endTime))
+ {
+ it->CopyWithoutCurve(newCurve);
+
+ newCurve.curve.SetPostInfinity(kClamp);
+ newCurve.curve.SetPreInfinity(kClamp);
+
+ if (duplicateLastFrame)
+ {
+ AddLoopingFrame(newCurve.curve, endTime - startTime + 1.0f/sampleRate);
+ EnsureLoopFrameContinuity(newCurve.curve);
+ }
+
+ AssertMsg(newCurve.curve.GetKeyCount() >= 2, "Key count: %d on curve %s", newCurve.curve.GetKeyCount(), it->path.c_str());
+
+ destinationCurves.push_back(newCurve);
+ }
+ }
+}
+
+void ClipAnimation (AnimationClip& sourceClip, AnimationClip& destinationClip, float startTimeSeconds, float endTimeSeconds, bool duplicateLastFrame)
+{
+ if (startTimeSeconds > endTimeSeconds)
+ std::swap(endTimeSeconds, startTimeSeconds);
+
+ ClipAnimations<Quaternionf>(sourceClip.GetRotationCurves(), startTimeSeconds, endTimeSeconds, sourceClip.GetSampleRate(), duplicateLastFrame, destinationClip.GetRotationCurves());
+ ClipAnimations<Vector3f>(sourceClip.GetPositionCurves(), startTimeSeconds, endTimeSeconds, sourceClip.GetSampleRate(), duplicateLastFrame, destinationClip.GetPositionCurves());
+ ClipAnimations<Vector3f>(sourceClip.GetScaleCurves(), startTimeSeconds, endTimeSeconds, sourceClip.GetSampleRate(), duplicateLastFrame, destinationClip.GetScaleCurves());
+ ClipAnimations<float>(sourceClip.GetFloatCurves(), startTimeSeconds, endTimeSeconds, sourceClip.GetSampleRate(), duplicateLastFrame, destinationClip.GetFloatCurves());
+#if UNITY_EDITOR
+ ClipAnimations<float>(sourceClip.GetEditorCurvesNoConversion(), startTimeSeconds, endTimeSeconds, sourceClip.GetSampleRate(), duplicateLastFrame, destinationClip.GetEditorCurvesNoConversion());
+ AssertIf(sourceClip.GetEditorCurvesNoConversion().size() < destinationClip.GetEditorCurvesNoConversion().size());
+#endif // #if UNITY_EDITOR
+
+ AssertIf(sourceClip.GetRotationCurves().size() < destinationClip.GetRotationCurves().size());
+ AssertIf(sourceClip.GetPositionCurves().size() < destinationClip.GetPositionCurves().size());
+ AssertIf(sourceClip.GetScaleCurves().size() < destinationClip.GetScaleCurves().size());
+ AssertIf(sourceClip.GetFloatCurves().size() < destinationClip.GetFloatCurves().size());
+}
+
+
+void CopyAnimation (AnimationClip& sourceClip, AnimationClip& destinationClip)
+{
+ destinationClip.GetRotationCurves() = sourceClip.GetRotationCurves();
+ destinationClip.GetPositionCurves() = sourceClip.GetPositionCurves();
+ destinationClip.GetScaleCurves() = sourceClip.GetScaleCurves();
+ destinationClip.GetFloatCurves() = sourceClip.GetFloatCurves();
+#if UNITY_EDITOR
+ destinationClip.GetEditorCurvesNoConversion() = sourceClip.GetEditorCurvesNoConversion();
+#endif // #if UNITY_EDITOR
+}
diff --git a/Runtime/Animation/AnimationClipUtility.h b/Runtime/Animation/AnimationClipUtility.h
new file mode 100644
index 0000000..1e7bf5f
--- /dev/null
+++ b/Runtime/Animation/AnimationClipUtility.h
@@ -0,0 +1,6 @@
+#pragma once
+
+class AnimationClip;
+
+void ClipAnimation (AnimationClip& sourceClip, AnimationClip& destinationClip, float startTimeSeconds, float endTimeSeconds, bool duplicateLastFrame);
+void CopyAnimation (AnimationClip& sourceClip, AnimationClip& destinationClip);
diff --git a/Runtime/Animation/AnimationCurveUtility.cpp b/Runtime/Animation/AnimationCurveUtility.cpp
new file mode 100644
index 0000000..79c9b38
--- /dev/null
+++ b/Runtime/Animation/AnimationCurveUtility.cpp
@@ -0,0 +1,1009 @@
+#include "UnityPrefix.h"
+#include "AnimationCurveUtility.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Utilities/Utility.h"
+
+template<class T>
+T SafeDeltaDivide (T y, float x)
+{
+ if (Abs(x) > kCurveTimeEpsilon)
+ return y / x;
+ else
+ return Zero<T>();
+}
+
+
+
+
+template<class T>
+inline T HermiteInterpolateDerived (float t, T p0, T m0, T m1, T p1)
+{
+ float t2 = t * t;
+
+ float a = 6.0F * t2 - 6.0F * t;
+ float b = 3.0F * t2 - 4.0F * t + 1.0F;
+ float c = 3.0F * t2 - 2.0F * t;
+ float d = -6.0F * t2 + 6.0F * t;
+
+ return a * p0 + b * m0 + c * m1 + d * p1;
+}
+
+template<class T>
+void RecalculateSplineSlopeT(AnimationCurveTpl<T>& curve, int key, float bias = 0.0F);
+
+using namespace std;
+
+// TODO : maybe we can remove it?
+// this function is used by ImportFBX
+void EnsureQuaternionContinuity (AnimationCurve** curves)
+{
+ if (!curves[0] || !curves[1] || !curves[2] || !curves[3])
+ return;
+
+ int keyCount = curves[0]->GetKeyCount ();
+ if (keyCount != curves[1]->GetKeyCount () || keyCount != curves[2]->GetKeyCount () || keyCount != curves[3]->GetKeyCount ())
+ return;
+
+ if (keyCount == 0)
+ return;
+
+ Quaternionf last (curves[0]->GetKey (keyCount-1).value, curves[1]->GetKey (keyCount-1).value, curves[2]->GetKey (keyCount-1).value, curves[3]->GetKey (keyCount-1).value);
+ for (int i=0;i<keyCount;i++)
+ {
+ Quaternionf cur (curves[0]->GetKey (i).value, curves[1]->GetKey (i).value, curves[2]->GetKey (i).value, curves[3]->GetKey (i).value);
+ if (Dot (cur, last) < 0.0F)
+ cur = Quaternionf (-cur.x, -cur.y, -cur.z, -cur.w);
+ last = cur;
+ curves[0]->GetKey (i).value = cur.x;
+ curves[1]->GetKey (i).value = cur.y;
+ curves[2]->GetKey (i).value = cur.z;
+ curves[3]->GetKey (i).value = cur.w;
+ }
+
+ for (int j=0;j<4;j++)
+ {
+ for (int i=0;i<keyCount;i++)
+ RecalculateSplineSlopeT (*curves[j], i);
+ }
+}
+
+
+void ExpandQuaternionCurve (AnimationCurveQuat& quat, AnimationCurve* outCurves[4])
+{
+ int size = quat.GetKeyCount();
+
+ for (int c=0;c<4;c++)
+ outCurves[c]->ResizeUninitialized(size);
+
+ for (int i=0;i<size;i++)
+ {
+ AnimationCurve::Keyframe key;
+ const AnimationCurveQuat::Keyframe& src = quat.GetKey(i);
+ key.time = src.time;
+ for (int c=0;c<4;c++)
+ {
+ key.value = src.value[c];
+ key.inSlope = src.inSlope[c];
+ key.outSlope = src.outSlope[c];
+ outCurves[c]->GetKey(i) = key;
+ }
+ }
+
+ for (int c=0;c<4;c++)
+ {
+ outCurves[c]->SetPreInfinity(quat.GetPreInfinity());
+ outCurves[c]->SetPostInfinity(quat.GetPostInfinity());
+ outCurves[c]->InvalidateCache();
+ }
+}
+
+void ExpandVector3Curve (AnimationCurveVec3& inCurve, AnimationCurve* outCurves[3])
+{
+ int size = inCurve.GetKeyCount();
+
+ for (int c=0;c<3;c++)
+ outCurves[c]->ResizeUninitialized(size);
+
+ for (int i=0;i<size;i++)
+ {
+ AnimationCurve::Keyframe key;
+ const AnimationCurveVec3::Keyframe& src = inCurve.GetKey(i);
+ key.time = src.time;
+ for (int c=0;c<3;c++)
+ {
+ key.value = src.value[c];
+ key.inSlope = src.inSlope[c];
+ key.outSlope = src.outSlope[c];
+ outCurves[c]->GetKey (i) = key;
+ }
+ }
+
+ for (int c=0;c<3;c++)
+ {
+ outCurves[c]->SetPreInfinity(inCurve.GetPreInfinity());
+ outCurves[c]->SetPostInfinity(inCurve.GetPostInfinity());
+ outCurves[c]->InvalidateCache();
+ }
+}
+
+template<class T>
+int AddInbetweenKey (AnimationCurveTpl<T>& curve, float curveT)
+{
+ int index = curve.FindIndex (curveT);
+ if (index == -1)
+ return -1;
+ const KeyframeTpl<T>& lhs = curve.GetKey (index);
+ const KeyframeTpl<T>& rhs = curve.GetKey (min(index+1, curve.GetKeyCount()-1));
+
+ return curve.AddKey(CalculateInbetweenKey(lhs, rhs, curveT));
+}
+
+
+template<class T>
+KeyframeTpl<T> CalculateInbetweenKey(const AnimationCurveTpl<T>& curve, float curveT)
+{
+ int index = curve.FindIndex (curveT);
+ const typename AnimationCurveTpl<T>::Keyframe& lhs = curve.GetKey (index);
+ const typename AnimationCurveTpl<T>::Keyframe& rhs = curve.GetKey (index+1);
+ return CalculateInbetweenKey(lhs, rhs, curveT);
+}
+
+
+template<class T>
+KeyframeTpl<T> CalculateInbetweenKey(const KeyframeTpl<T>& lhs, const KeyframeTpl<T>& rhs, float curveT)
+{
+ typename AnimationCurveTpl<T>::Keyframe key;
+ float dx = rhs.time - lhs.time;
+
+ AssertIf(dx == 0.0F);
+
+ float t = (curveT - lhs.time) / dx;
+
+ if (t < -kCurveTimeEpsilon)
+ {
+ key = lhs;
+ key.time = curveT;
+ key.inSlope = Zero<T>();
+ key.outSlope = Zero<T>();
+
+ return key;
+ }
+ else if (t > 1.0F + kCurveTimeEpsilon)
+ {
+ key = rhs;
+ key.time = curveT;
+ key.inSlope = Zero<T>();
+ key.outSlope = Zero<T>();
+ return key;
+ }
+
+ T m1 = lhs.outSlope * dx;
+ T m2 = rhs.inSlope * dx;
+
+ // Calculate the slope at t. This is simply done by deriving the Hermite basis functions
+ // and feeding the derived hermite interpolator normal curve values
+ T slope = HermiteInterpolateDerived (t, lhs.value, m1, m2, rhs.value);
+ if (dx > 1.0F / MaxTan<float>())
+ slope /= dx;
+ else
+ slope = MaxTan<T> ();
+
+ HandleSteppedTangent(lhs, rhs, slope);
+
+ key.inSlope = slope;
+ key.outSlope = slope;
+
+ // the value of the key is just interpolated
+ key.time = curveT;
+ key.value = HermiteInterpolate (t, lhs.value, m1, m2, rhs.value);
+
+ HandleSteppedCurve(lhs, rhs, key.value);
+
+ AssertIf(!IsFinite(key.value));
+
+ return key;
+}
+
+void QuaternionCurveToEulerCurve (AnimationCurveQuat& quat, AnimationCurve* outCurves[3])
+{
+ int size = quat.GetKeyCount();
+
+ for (int c=0;c<3;c++)
+ outCurves[c]->ResizeUninitialized(size);
+
+ for (int i=0;i<size;i++)
+ {
+ AnimationCurve::Keyframe key;
+ const AnimationCurveQuat::Keyframe& src = quat.GetKey(i);
+ key.time = src.time;
+
+ float idt = i > 0 ? key.time - quat.GetKey(i-1).time : quat.GetKey(i+1).time - key.time;
+ float odt = i < size-1 ? quat.GetKey(i+1).time - key.time : key.time - quat.GetKey(i-1).time;
+
+ Quaternionf quat = src.value;
+ Quaternionf iquat = quat + src.inSlope * idt / 3;
+ Quaternionf oquat = quat + src.outSlope * odt / 3;
+
+ quat = NormalizeSafe(quat);
+ iquat = NormalizeSafe(iquat);
+ oquat = NormalizeSafe(oquat);
+
+ Vector3f euler = QuaternionToEuler(quat) * Rad2Deg(1.0F);
+ Vector3f ieuler = QuaternionToEuler(iquat) * Rad2Deg(1.0F);
+ Vector3f oeuler = QuaternionToEuler(oquat) * Rad2Deg(1.0F);
+
+ for (int c=0;c<3;c++)
+ {
+ ieuler[c] = Repeat(ieuler[c] - euler[c] + 180.0F, 360.0F) + euler[c] - 180.0F;
+ oeuler[c] = Repeat(oeuler[c] - euler[c] + 180.0F, 360.0F) + euler[c] - 180.0F;
+
+ key.value = euler[c];
+ key.inSlope = 3 * (ieuler[c] - euler[c]) / idt;
+ key.outSlope = 3 * (oeuler[c] - euler[c]) / odt;
+ outCurves[c]->GetKey (i) = key;
+ }
+ }
+
+ for (int c=0;c<3;c++)
+ {
+ outCurves[c]->SetPreInfinity(quat.GetPreInfinity());
+ outCurves[c]->SetPostInfinity(quat.GetPostInfinity());
+ outCurves[c]->InvalidateCache();
+ }
+}
+
+Quaternionf EvaluateQuaternionFromEulerCurves (const AnimationCurve& curveX, const AnimationCurve& curveY, const AnimationCurve& curveZ, float time)
+{
+ Vector3f euler;
+ euler.x = curveX.Evaluate(time);
+ euler.y = curveY.Evaluate(time);
+ euler.z = curveZ.Evaluate(time);
+ return EulerToQuaternion (euler * Deg2Rad(1.0F));
+}
+
+void EulerToQuaternionCurve (const AnimationCurve& curveX, const AnimationCurve& curveY, const AnimationCurve& curveZ, AnimationCurveQuat& collapsed)
+{
+ int size, foundIndex;
+
+ float errorDelta = 0.002F;
+
+ const AnimationCurve* curves[3] = { &curveX, &curveY, &curveZ };
+
+ // Create keyframes in collapsed array with filled out time values based on keyframes of all 3 curves
+ for (int c=0;c<3;c++)
+ {
+ const AnimationCurve& curve = *curves[c];
+ size = curve.GetKeyCount();
+ for (int i=0;i<size;i++)
+ {
+ const float srcTime = curve.GetKey(i).time;
+
+ // Just add keyframe if there are not enough keys in curve
+ ///@TODO: incorrect when one curve has only one key, because it will just call AddKey multiple times on the same key potentially.
+ /// Some other code further down is doing the same thing...
+ bool addKey = true;
+
+ if (collapsed.IsValid())
+ {
+ foundIndex = collapsed.FindIndex(srcTime);
+
+ bool addKey = foundIndex < 0;
+
+ if (!addKey)
+ {
+ // Do we have a key that is in the curve but not in the collapsed curve?
+ // We check keys on the left and on the right of the found key
+ addKey =
+ !CompareApproximately(srcTime, collapsed.GetKey(foundIndex).time, errorDelta) &&
+ (foundIndex + 1 < collapsed.GetKeyCount() || !CompareApproximately(srcTime, collapsed.GetKey(foundIndex + 1).time, errorDelta));
+ }
+ }
+
+ if (addKey)
+ {
+ KeyframeTpl<Quaternionf> dst;
+ dst.time = srcTime;
+ collapsed.AddKey(dst);
+ }
+ }
+ }
+
+ // Evaluate values at keys
+ size = collapsed.GetKeyCount();
+ for (int i=0; i<size; i++)
+ {
+ // This part would be semi incorrect if the euler curves didn't all have the same keyframes,
+ // But luckily the UI enforces them to always be together.
+ float time = collapsed.GetKey(i).time;
+ KeyframeTpl<Quaternionf>& dst = collapsed.GetKey(i);
+ dst.value = EvaluateQuaternionFromEulerCurves(curveX, curveY, curveZ, time);
+ }
+
+ // Determine tangents in quaternion space by sampling deltas
+ // TODO: Use better way of sampling tangents
+ for (int i=0; i<size-1; i++)
+ {
+ float lTime = collapsed.GetKey(i).time;
+ float rTime = collapsed.GetKey(i+1).time;
+
+ // Sample euler curves epsilon time efter left key and get quaternion
+ Quaternionf quat = EvaluateQuaternionFromEulerCurves(curveX, curveY, curveZ, lTime*0.999F + rTime * 0.001F);
+ KeyframeTpl<Quaternionf>& dst = collapsed.GetKey(i);
+ Quaternionf qDelta ((quat.x - dst.value.x) * 1000/(rTime-lTime),
+ (quat.y - dst.value.y) * 1000/(rTime-lTime),
+ (quat.z - dst.value.z) * 1000/(rTime-lTime),
+ (quat.w - dst.value.w) * 1000/(rTime-lTime));
+ dst.outSlope = qDelta;
+
+ // Sample euler curves epsilon time before right key and get quaternion
+ Quaternionf quat2 = EvaluateQuaternionFromEulerCurves(curveX, curveY, curveZ, lTime*0.001F + rTime * 0.999F);
+ KeyframeTpl<Quaternionf>& dst2 = collapsed.GetKey(i+1);
+ Quaternionf qDelta2 ((dst2.value.x - quat2.x) * 1000/(rTime-lTime),
+ (dst2.value.y - quat2.y) * 1000/(rTime-lTime),
+ (dst2.value.z - quat2.z) * 1000/(rTime-lTime),
+ (dst2.value.w - quat2.w) * 1000/(rTime-lTime));
+ dst2.inSlope = qDelta2;
+ }
+
+ collapsed.SetPreInfinity(curveX.GetPreInfinity());
+ collapsed.SetPostInfinity(curveX.GetPostInfinity());
+
+ collapsed.InvalidateCache();
+ EnsureQuaternionContinuityPreserveSlope(collapsed);
+}
+
+template<class T>
+void CombineCurve (const AnimationCurve& curve, int index, AnimationCurveTpl<T>& collapsed)
+{
+ int size, foundIndex;
+ if (index == 0)
+ {
+ collapsed.SetPreInfinity(curve.GetPreInfinity());
+ collapsed.SetPostInfinity(curve.GetPostInfinity());
+ }
+
+ // There is nothing in the collapsed curve yet
+ // We will build it from scratch
+ if (collapsed.GetKeyCount() == 0)
+ {
+ collapsed.ResizeUninitialized(curve.GetKeyCount());
+
+ size = collapsed.GetKeyCount();
+ for (int i=0;i<size;i++)
+ {
+ const AnimationCurve::Keyframe& src = curve.GetKey(i);
+ KeyframeTpl<T>& dst = collapsed.GetKey(i);
+
+ dst.time = src.time;
+
+ dst.value = Zero<T>();
+ dst.inSlope = Zero<T>();
+ dst.outSlope = Zero<T>();
+ // Write value into x,y,z,w axis based on index
+ dst.value[index] = src.value;
+ dst.inSlope[index] = src.inSlope;
+ dst.outSlope[index] = src.outSlope;
+ }
+
+ collapsed.InvalidateCache();
+
+ return;
+ }
+
+ // TODO : it has to be smaller than 0.002, because keyframes migth be closer to each other than that
+ // we need some more advance technique to get this errorDelta or smarter way to match keyframes between curves
+ float errorDelta = 0.000002F;
+ //float errorDelta = 0.002F;
+
+ AnimationCurve::Cache curveCache;
+ typename AnimationCurveTpl<T>::Cache collapseCache;
+
+ // Insert any new keys that are defined in the curve but are not in collapsed
+ size = curve.GetKeyCount();
+ for (int i=0;i<size;i++)
+ {
+ const AnimationCurve::Keyframe& src = curve.GetKey(i);
+ if (collapsed.IsValid())
+ {
+ foundIndex = collapsed.FindIndex(collapseCache, src.time);
+ const KeyframeTpl<T>& lhs = collapsed.GetKey (foundIndex);
+ collapseCache.index = foundIndex;
+ collapseCache.time = lhs.time;
+ const KeyframeTpl<T>& rhs = collapsed.GetKey (foundIndex+1);
+
+ // Do we have a key that is in the curve but not in the collapsed curve?
+ // -> Add an inbetween
+ if (!CompareApproximately(src.time, lhs.time, errorDelta) && !CompareApproximately(src.time, rhs.time, errorDelta))
+ {
+ collapsed.AddKey(CalculateInbetweenKey(lhs, rhs, src.time));
+ collapseCache.Invalidate();
+ }
+ }
+ else
+ {
+ // We need at least two keyframes, for the AddInBetween key function to work
+ AssertIf(collapsed.GetKeyCount () != 1);
+ KeyframeTpl<T> copyKey = collapsed.GetKey (0);
+ copyKey.time = src.time;
+ collapsed.AddKey(copyKey);
+ collapseCache.Invalidate();
+ }
+ }
+
+ // Go through the dst keys.
+ // Either copy from a key at the same time
+ // or Calculate inbetween
+ size = collapsed.GetKeyCount();
+ for (int i=0;i<size;i++)
+ {
+ KeyframeTpl<T>& dst = collapsed.GetKey(i);
+ KeyframeTpl<float> inbetween;
+
+ if (curve.IsValid())
+ {
+ foundIndex = curve.FindIndex(curveCache, dst.time);
+ const KeyframeTpl<float>& lhs = curve.GetKey (foundIndex);
+ curveCache.index = foundIndex;
+ curveCache.time = lhs.time;
+ const KeyframeTpl<float>& rhs = curve.GetKey (foundIndex+1);
+
+ if (CompareApproximately(dst.time, lhs.time, errorDelta))
+ inbetween = lhs;
+ else if (CompareApproximately(dst.time, rhs.time, errorDelta))
+ inbetween = rhs;
+ else
+ inbetween = CalculateInbetweenKey(lhs, rhs, dst.time);
+ }
+ else
+ {
+ inbetween.value = curve.GetKeyCount() == 1 ? curve.GetKey (0).value : 0.0F;
+ inbetween.inSlope = 0;
+ inbetween.outSlope = 0;
+ }
+
+ dst.value[index] = inbetween.value;
+ dst.inSlope[index] = inbetween.inSlope;
+ dst.outSlope[index] = inbetween.outSlope;
+ }
+
+ collapsed.InvalidateCache();
+}
+
+
+#define CLIPPING_EPSILON (1.0F / 1000.0F)
+
+template<class T>
+void ValidateCurve (AnimationCurveTpl<T>& curve)
+{
+ if (!curve.IsValid())
+ return;
+
+ // validating that time of keyframes is increasing
+ float t = -100000.0F;
+ for (typename AnimationCurveTpl<T>::iterator i=curve.begin();i!=curve.end();i++)
+ {
+ AssertMsg(t < i->time, "Key frame placement is not increasing" ); // Would love to be able to specify the model here
+ t = i->time;
+ }
+}
+
+template<class T>
+int FindClipKey (const AnimationCurveTpl<T>& curve, float time)
+{
+ const KeyframeTpl<T>* i = std::lower_bound (curve.begin (), curve.end (), time, KeyframeCompare());
+
+ if (i == curve.end())
+ {
+ return curve.GetKeyCount() - 1;
+ }
+ else
+ {
+ int indexH = distance (curve.begin (), i);
+ int indexL = max(indexH-1,0);
+
+ float diffH = fabs(curve.GetKey(indexH).time - time);
+ float diffL = fabs(curve.GetKey(indexL).time - time);
+
+ if(diffH < diffL)
+ {
+ return indexH;
+ }
+ else
+ {
+ return indexL;
+ }
+ }
+}
+
+template<class T>
+bool ClipAnimationCurve (const AnimationCurveTpl<T>& sourceCurve, AnimationCurveTpl<T>& curve, float begin, float end)
+{
+ AssertIf(begin > end);
+ dynamic_array<typename AnimationCurveTpl<T>::Keyframe> scratch;
+
+ if (!sourceCurve.IsValid ())
+ {
+ return false;
+ }
+
+ pair<float, float> range = sourceCurve.GetRange();
+
+ float offset = -begin;
+
+ begin = clamp(begin, range.first, range.second);
+ end = clamp(end, range.first, range.second);
+
+ // Contains no frames
+ if (CompareApproximately(begin, end, CLIPPING_EPSILON))
+ {
+ return false;
+ }
+
+ int firstIndex = FindClipKey(sourceCurve, begin);
+ int lastIndex = FindClipKey(sourceCurve, end);
+
+ // 2 for the possible interpolated ones and one for extra nicenecess because usually we might
+ // add an extra looping frame later on
+ scratch.reserve(std::max(lastIndex - firstIndex, 0) + 3);
+
+ if (CompareApproximately (begin, sourceCurve.GetKey(firstIndex).time, CLIPPING_EPSILON))
+ {
+ scratch.push_back(sourceCurve.GetKey(firstIndex));
+ firstIndex++;
+ }
+ else
+ {
+ scratch.push_back(CalculateInbetweenKey(sourceCurve, begin));
+ if(begin > sourceCurve.GetKey(firstIndex).time) firstIndex++;
+ }
+
+ if (CompareApproximately (end, sourceCurve.GetKey(lastIndex).time, CLIPPING_EPSILON))
+ {
+ scratch.push_back(sourceCurve.GetKey(lastIndex));
+ }
+ else
+ {
+ scratch.push_back(CalculateInbetweenKey(sourceCurve, end));
+ if(end > sourceCurve.GetKey(lastIndex).time) lastIndex++;
+ }
+
+ // Insert all inbetween keys
+ if (lastIndex > firstIndex)
+ scratch.insert(scratch.begin()+1, sourceCurve.begin() + firstIndex, sourceCurve.begin() + lastIndex);
+
+ // Zero base the clipped animation
+ for (unsigned int i=0;i<scratch.size();i++)
+ scratch[i].time += offset;
+
+ curve.Assign(scratch.begin(), scratch.end());
+ curve.InvalidateCache();
+
+ ValidateCurve(curve);
+
+ AssertMsg(curve.GetKeyCount() >= 2, "Key count: %d", curve.GetKeyCount());
+
+ return true;
+}
+
+void EnsureQuaternionContinuityPreserveSlope (AnimationCurveQuat& curve)
+{
+ if (!curve.IsValid())
+ return;
+
+ int keyCount = curve.GetKeyCount ();
+
+ Quaternionf last (curve.GetKey (keyCount-1).value);
+ for (int i=0;i<keyCount;i++)
+ {
+ Quaternionf cur (curve.GetKey (i).value);
+ if (Dot (cur, last) < 0.0F)
+ {
+ cur = Quaternionf (-cur.x, -cur.y, -cur.z, -cur.w);
+ curve.GetKey (i).value = cur;
+ curve.GetKey (i).inSlope = -curve.GetKey (i).inSlope;
+ curve.GetKey (i).outSlope = -curve.GetKey (i).outSlope;
+ }
+ last = cur;
+ }
+}
+
+void EnsureQuaternionContinuityAndRecalculateSlope (AnimationCurveQuat& curve)
+{
+ if (!curve.IsValid())
+ return;
+
+ int keyCount = curve.GetKeyCount ();
+
+ Quaternionf last (curve.GetKey (keyCount-1).value);
+ for (int i=0;i<keyCount;i++)
+ {
+ Quaternionf cur (curve.GetKey (i).value);
+ if (Dot (cur, last) < 0.0F)
+ cur = Quaternionf (-cur.x, -cur.y, -cur.z, -cur.w);
+ last = cur;
+ curve.GetKey (i).value = cur;
+ }
+
+ for (int i=0;i<keyCount;i++)
+ RecalculateSplineSlopeT (curve, i);
+}
+
+template<class T>
+void RecalculateSplineSlope (AnimationCurveTpl<T>& curve)
+{
+ for (int i=0;i<curve.GetKeyCount ();i++)
+ RecalculateSplineSlopeT (curve, i);
+}
+
+template<class T>
+void RecalculateSplineSlopeLinear (AnimationCurveTpl<T>& curve)
+{
+ if (curve.GetKeyCount () < 2)
+ return;
+
+ for (int i=0;i<curve.GetKeyCount () - 1;i++)
+ {
+ RecalculateSplineSlopeLinear( curve, i );
+ }
+}
+
+template<class T>
+void RecalculateSplineSlopeLinear (AnimationCurveTpl<T>& curve, int key)
+{
+ AssertIf(key < 0 || key >= curve.GetKeyCount() - 1);
+ if (curve.GetKeyCount () < 2)
+ return;
+
+ float dx = curve.GetKey (key).time - curve.GetKey (key+1).time;
+ T dy = curve.GetKey (key).value - curve.GetKey (key+1).value;
+ T m = dy / dx;
+ curve.GetKey (key).outSlope = m;
+ curve.GetKey (key+1).inSlope = m;
+}
+
+void RecalculateSplineSlope (AnimationCurveTpl<float>& curve, int key, float bias)
+{
+ RecalculateSplineSlopeT<float>(curve, key, bias);
+}
+
+template<class T>
+void RecalculateSplineSlopeT (AnimationCurveTpl<T>& curve, int key, float b)
+{
+ AssertIf(key < 0 || key >= curve.GetKeyCount());
+ if (curve.GetKeyCount () < 2)
+ return;
+
+ // First keyframe
+ // in and out slope are set to be the slope from this to the right key
+ if (key == 0)
+ {
+ float dx = curve.GetKey (1).time - curve.GetKey (0).time;
+ T dy = curve.GetKey (1).value - curve.GetKey (0).value;
+ T m = dy / dx;
+ curve.GetKey (key).inSlope = m; curve.GetKey (key).outSlope = m;
+ }
+ // last keyframe
+ // in and out slope are set to be the slope from this to the left key
+ else if (key == curve.GetKeyCount () - 1)
+ {
+ float dx = curve.GetKey (key).time - curve.GetKey (key-1).time;
+ T dy = curve.GetKey (key).value - curve.GetKey (key-1).value;
+ T m = dy / dx;
+ curve.GetKey (key).inSlope = m; curve.GetKey (key).outSlope = m;
+ }
+ // Keys are on the left and right
+ // Calculates the slopes from this key to the left key and the right key.
+ // Then blend between them using the bias
+ // A bias of zero doesn't bend in any direction
+ // a positive bias bends to the right
+ else
+ {
+ float dx1 = curve.GetKey (key).time - curve.GetKey (key-1).time;
+ T dy1 = curve.GetKey (key).value - curve.GetKey (key-1).value;
+
+ float dx2 = curve.GetKey (key+1).time - curve.GetKey (key).time;
+ T dy2 = curve.GetKey (key+1).value - curve.GetKey (key).value;
+
+ T m1 = SafeDeltaDivide(dy1, dx1);
+ T m2 = SafeDeltaDivide(dy2, dx2);
+
+ T m = (1.0F + b) * 0.5F * m1 + (1.0F - b) * 0.5F * m2;
+ curve.GetKey (key).inSlope = m; curve.GetKey (key).outSlope = m;
+ }
+
+ curve.InvalidateCache ();
+}
+
+
+template<class T>
+void RecalculateSplineSlopeLoop (AnimationCurveTpl<T>& curve, int key, float b)
+{
+ AssertIf(key < 0 || key >= curve.GetKeyCount());
+ if (curve.GetKeyCount () < 2)
+ return;
+
+ int keyPrev = key - 1;
+ int keyNext = key + 1;
+ if (key == 0)
+ keyPrev = curve.GetKeyCount() - 2;
+ else if (key+1 == curve.GetKeyCount())
+ keyNext = 1;
+ else
+ AssertString("Not supported");
+
+ // Keys are on the left and right
+ // Calculates the slopes from this key to the left key and the right key.
+ // Then blend between them using the bias
+ // A bias of zero doesn't bend in any direction
+ // a positive bias bends to the right
+ float dx1 = curve.GetKey (key).time - curve.GetKey (keyPrev).time;
+ T dy1 = curve.GetKey (key).value - curve.GetKey (keyPrev).value;
+
+ float dx2 = curve.GetKey (keyNext).time - curve.GetKey (key).time;
+ T dy2 = curve.GetKey (keyNext).value - curve.GetKey (key).value;
+
+ T m1 = SafeDeltaDivide(dy1, dx1);
+ T m2 = SafeDeltaDivide(dy2, dx2);
+
+ T m = (1.0F + b) * 0.5F * m1 + (1.0F - b) * 0.5F * m2;
+ curve.GetKey (key).inSlope = m; curve.GetKey (key).outSlope = m;
+
+ curve.InvalidateCache ();
+}
+
+
+template<class T>
+void AddLoopingFrame (AnimationCurveTpl<T>& curve, float time)
+{
+ if (!curve.IsValid())
+ return;
+
+ KeyframeTpl<T> key;
+ key.time = time;
+ key.value = curve.GetKey(0).value;
+ key.inSlope = curve.GetKey(0).outSlope;
+ key.outSlope = curve.GetKey(0).outSlope;
+
+ curve.AddKey(key);
+
+ RecalculateSplineSlopeLoop(curve, 0, 0);
+ RecalculateSplineSlopeLoop(curve, curve.GetKeyCount()-1, 0);
+}
+
+void EnsureQuaternionContinuityLoopFrame (AnimationCurveQuat& curve)
+{
+ if( curve.GetKeyCount () < 2 )
+ return;
+
+ int keyCount = curve.GetKeyCount ();
+
+ Quaternionf last (curve.GetKey (keyCount-2).value);
+ Quaternionf cur (curve.GetKey (keyCount-1).value);
+ if (Dot (cur, last) < 0.0F)
+ cur = Quaternionf (-cur.x, -cur.y, -cur.z, -cur.w);
+ curve.GetKey (keyCount-1).value = cur;
+
+ RecalculateSplineSlopeLoop(curve, keyCount-1, 0);
+}
+
+
+int AddKeySmoothTangents (AnimationCurve& curve, float time, float value)
+{
+ AnimationCurve::Keyframe key;
+ key.time = time;
+ key.value = value;
+ int index = curve.AddKey (key);
+ if (index == -1)
+ return -1;
+
+ // Recalculate spline slope of this and the two keyframes around us!
+ if (index > 0)
+ RecalculateSplineSlope (curve, index - 1, 0.0F);
+ RecalculateSplineSlope (curve, index, 0.0F);
+ if (index + 1 < curve.GetKeyCount ())
+ RecalculateSplineSlope (curve, index + 1, 0.0F);
+
+ return index;
+}
+
+
+
+template<class T>
+T InterpolateKeyframe (const KeyframeTpl<T>& lhs, const KeyframeTpl<T>& rhs, float curveT)
+{
+ float dx = rhs.time - lhs.time;
+ T m1;
+ T m2;
+ float t;
+
+ if (dx != 0.0F)
+ {
+ t = (curveT - lhs.time) / dx;
+ m1 = lhs.outSlope * dx;
+ m2 = rhs.inSlope * dx;
+ }
+ else
+ {
+ t = 0.0F;
+ m1 = Zero<T>();
+ m2 = Zero<T>();
+ }
+
+ return HermiteInterpolate (t, lhs.value, m1, m2, rhs.value);
+}
+
+int UpdateCurveKey (AnimationCurve& curve, int index, const AnimationCurve::Keyframe& value)
+{
+ float time = curve.GetKey(index).time;
+ if ((index-1 < 0 || index+1 < curve.GetKeyCount()) &&
+ time > curve.GetKey(index-1).time &&
+ time < curve.GetKey(index+1).time)
+ {
+ curve.GetKey(index) = value;
+ return index;
+ }
+ else
+ {
+ curve.RemoveKeys(curve.begin() + index, curve.begin() + index + 1);
+ return curve.AddKey(value);
+ }
+}
+
+int MoveCurveKey (AnimationCurve& curve, int index, AnimationCurve::Keyframe value)
+{
+ float time = curve.GetKey(index).time;
+
+ curve.RemoveKeys(curve.begin() + index, curve.begin() + index + 1);
+ int newCloseIndex = curve.FindIndex(value.time);
+
+ if (newCloseIndex >= 0)
+ {
+ Assert(curve.GetKeyCount() > 0);
+
+ // Too close to some keyframes -> Keep time of the old time value
+ if((newCloseIndex - 1 >= 0 && Abs(value.time - curve.GetKey(clamp(newCloseIndex-1, 0, curve.GetKeyCount()-1)).time) < kCurveTimeEpsilon) ||
+ Abs(value.time - curve.GetKey(clamp(newCloseIndex , 0, curve.GetKeyCount()-1)).time) < kCurveTimeEpsilon ||
+ (newCloseIndex + 1 < curve.GetKeyCount() && Abs(value.time - curve.GetKey(clamp(newCloseIndex+1, 0, curve.GetKeyCount()-1)).time) < kCurveTimeEpsilon) ||
+ Abs(value.time - curve.GetKey(curve.GetKeyCount()-1).time) < kCurveTimeEpsilon)
+ {
+ value.time = time;
+ }
+ }
+
+ return curve.AddKey(value);
+}
+
+// Calculates Hermite curve coefficients
+void HermiteCooficients (double t, double& a, double& b, double& c, double& d)
+{
+ double t2 = t * t;
+ double t3 = t2 * t;
+
+ a = 2.0F * t3 - 3.0F * t2 + 1.0F;
+ b = t3 - 2.0F * t2 + t;
+ c = t3 - t2;
+ d = -2.0F * t3 + 3.0F * t2;
+}
+
+namespace TToArray
+{
+ template <class T> float& Index(T& value, int index) { return value[index]; }
+ template <class T> float Index(const T& value, int index) { return value[index]; }
+
+ template <> float& Index<float>(float& value, int index)
+ {
+ AssertIf(index != 0);
+ return value;
+ }
+ template <> float Index<float>(const float& value, int index)
+ {
+ AssertIf(index != 0);
+ return value;
+ }
+
+ template <class T> int CoordinateCount();
+ template <> int CoordinateCount<float>() { return 1; }
+ template <> int CoordinateCount<Vector3f>() { return 3; }
+ template <> int CoordinateCount<Quaternionf>() { return 4; }
+}
+
+template <class T>
+void FitTangents(KeyframeTpl<T>& key0, KeyframeTpl<T>& key1, float time1, float time2, const T& value1, const T& value2)
+{
+ AssertIf(fabsf(time1) < std::numeric_limits<float>::epsilon());
+ AssertIf(fabsf(time2) < std::numeric_limits<float>::epsilon());
+
+ const float dt = key1.time - key0.time;
+
+ const int coordinateCount = TToArray::CoordinateCount<T>();
+
+ if (fabsf(dt) < std::numeric_limits<float>::epsilon())
+ {
+ for (int i = 0; i < coordinateCount; ++i)
+ {
+ TToArray::Index(key0.outSlope, i) = 0;
+ TToArray::Index(key1.inSlope, i) = 0;
+ }
+ }
+ else
+ {
+ // p0 and p1 for Hermite curve interpolation equation
+ const T p0 = key0.value;
+ const T p1 = key1.value;
+
+ // Hermite coefficients at points time1 and time2
+ double a1, b1, c1, d1;
+ double a2, b2, c2, d2;
+
+ // TODO : try using doubles, because it doesn't work well when p0==p1==v0==v1
+ HermiteCooficients(time1, a1, b1, c1, d1);
+ HermiteCooficients(time2, a2, b2, c2, d2);
+
+ for (int i = 0; i < coordinateCount; ++i)
+ {
+ // we need to solve these two equations in order to find m0 and m1
+ // b1 * m0 + c1 * m1 = v0 - a1 * p0 - d1 * p1;
+ // b2 * m0 + c2 * m1 = v1 - a2 * p0 - d2 * p1;
+
+ // c1, c2 is never equal 0, because time1 and time2 not equal to 0
+
+ // divide by c1 and c2
+ // b1 / c1 * m0 + m1 = (v0 - a1 * p0 - d1 * p1) / c1;
+ // b2 / c2 * m0 + m1 = (v1 - a2 * p0 - d2 * p1) / c2;
+
+ // subtract one from another
+ // b1 / c1 * m0 - b2 / c2 * m0 = (v0 - a1 * p0 - d1 * p1) / c1 - (v1 - a2 * p0 - d2 * p1) / c2;
+
+ // solve for m0
+ // (b1 / c1 - b2 / c2) * m0 = (v0 - a1 * p0 - d1 * p1) / c1 - (v1 - a2 * p0 - d2 * p1) / c2;
+
+ const double v0 = TToArray::Index(value1, i);
+ const double v1 = TToArray::Index(value2, i);
+ const double pp0 = TToArray::Index(p0, i);
+ const double pp1 = TToArray::Index(p1, i);
+
+ // calculate m0
+ const double m0 = ((v0 - a1 * pp0 - d1 * pp1) / c1 - (v1 - a2 * pp0 - d2 * pp1) / c2) / (b1 / c1 - b2 / c2);
+
+ // solve for m1 using m0
+ // c1 * m1 = p0 - a1 * p0 - d1 * p1 - b1 * m0;
+
+ // calculate m1
+ const double m1 = (v0 - a1 * pp0 - d1 * pp1 - b1 * m0) / c1;
+
+ TToArray::Index(key0.outSlope, i) = static_cast<float>(m0 / dt);
+ TToArray::Index(key1.inSlope, i) = static_cast<float>(m1 / dt);
+ }
+ }
+}
+
+
+// Instantiate templates
+template void RecalculateSplineSlope (AnimationCurveTpl<float>& curve);
+
+template bool ClipAnimationCurve (const AnimationCurveTpl<float>& sourceCurve, AnimationCurveTpl<float>& curve, float begin, float end);
+template bool ClipAnimationCurve (const AnimationCurveTpl<Quaternionf>& sourceCurve, AnimationCurveTpl<Quaternionf>& curve, float begin, float end);
+template bool ClipAnimationCurve (const AnimationCurveTpl<Vector3f>& sourceCurve, AnimationCurveTpl<Vector3f>& curve, float begin, float end);
+
+template void CombineCurve (const AnimationCurve& curve, int index, AnimationCurveTpl<Vector3f>& collapsed);
+template void CombineCurve (const AnimationCurve& curve, int index, AnimationCurveTpl<Quaternionf>& collapsed);
+
+template void AddLoopingFrame (AnimationCurveTpl<float>& curve, float time);
+template void AddLoopingFrame (AnimationCurveTpl<Quaternionf>& curve, float time);
+template void AddLoopingFrame (AnimationCurveTpl<Vector3f>& curve, float time);
+
+template void RecalculateSplineSlopeLoop (AnimationCurveTpl<float>& curve, int key, float b);
+template void RecalculateSplineSlopeLoop (AnimationCurveTpl<Quaternionf>& curve, int key, float b);
+template void RecalculateSplineSlopeLoop (AnimationCurveTpl<Vector3f>& curve, int key, float b);
+
+template void RecalculateSplineSlopeLinear (AnimationCurveTpl<float>& curve);
+template void RecalculateSplineSlopeLinear (AnimationCurveTpl<float>& curve, int key);
+
+template float InterpolateKeyframe (const KeyframeTpl<float>& lhs, const KeyframeTpl<float>& rhs, float curveT);
+template Vector3f InterpolateKeyframe (const KeyframeTpl<Vector3f>& lhs, const KeyframeTpl<Vector3f>& rhs, float curveT);
+template Quaternionf InterpolateKeyframe (const KeyframeTpl<Quaternionf>& lhs, const KeyframeTpl<Quaternionf>& rhs, float curveT);
+
+template void FitTangents(KeyframeTpl<float>& key0, KeyframeTpl<float>& key1, float time1, float time2, const float& value1, const float& value2);
+template void FitTangents(KeyframeTpl<Vector3f>& key0, KeyframeTpl<Vector3f>& key1, float time1, float time2, const Vector3f& value1, const Vector3f& value2);
+template void FitTangents(KeyframeTpl<Quaternionf>& key0, KeyframeTpl<Quaternionf>& key1, float time1, float time2, const Quaternionf& value1, const Quaternionf& value2);
+
diff --git a/Runtime/Animation/AnimationCurveUtility.h b/Runtime/Animation/AnimationCurveUtility.h
new file mode 100644
index 0000000..c464338
--- /dev/null
+++ b/Runtime/Animation/AnimationCurveUtility.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include "Runtime/Math/AnimationCurve.h"
+
+
+void EulerToQuaternionCurve (const AnimationCurve& curveX, const AnimationCurve& curveY, const AnimationCurve& curveZ, AnimationCurveQuat& collapsed);
+void QuaternionCurveToEulerCurve (AnimationCurveQuat& quat, AnimationCurve* outCurves[3]);
+
+/// /curve/ is the input float curve
+/// /index/ is the component index in the collapsed curve (x,y,z,w -> usually between 0 and 3)
+/// /collapsed/ is the curve
+/// This functioncall is usually called as many times as there are components (for a quaternion curve 4 times, Vector3 curve 3 times)
+template<class T>
+void CombineCurve (const AnimationCurve& curve, int index, AnimationCurveTpl<T>& collapsed);
+
+void ExpandVector3Curve (AnimationCurveVec3& quat, AnimationCurve* outCurves[3]);
+void ExpandQuaternionCurve (AnimationCurveQuat& quat, AnimationCurve* outCurves[4]);
+
+template<class T>
+void AddLoopingFrame (AnimationCurveTpl<T>& curve, float time);
+
+template<class T>
+bool ClipAnimationCurve (const AnimationCurveTpl<T>& sourceCurve, AnimationCurveTpl<T>& curve, float begin, float end);
+
+int UpdateCurveKey (AnimationCurve& curve, int index, const AnimationCurve::Keyframe& value);
+int MoveCurveKey (AnimationCurve& curve, int index, AnimationCurve::Keyframe value);
+
+// Calculates the keyframes in/out slope based on the bias parameter creating a smooth spline.
+// a bias of zero is default.
+// a positive bias bends the curve to the next key.
+// a negative bias bends the curve to the previous key.
+void RecalculateSplineSlope (AnimationCurveTpl<float>& curve, int key, float bias = 0.0F);
+
+template<class T>
+T InterpolateKeyframe (const KeyframeTpl<T>& lhs, const KeyframeTpl<T>& rhs, float curveT);
+
+int AddInbetweenKey (AnimationCurve& curve, float curveT);
+
+template<class T>
+KeyframeTpl<T> CalculateInbetweenKey(const KeyframeTpl<T>& lhs, const KeyframeTpl<T>& rhs, float curveT);
+
+template<class T>
+KeyframeTpl<T> CalculateInbetweenKey(const AnimationCurveTpl<T>& curve, float curveT);
+
+int AddKeySmoothTangents (AnimationCurve& curve, float time, float value);
+
+template<class T>
+void RecalculateSplineSlope (AnimationCurveTpl<T>& curve);
+
+template<class T>
+void RecalculateSplineSlopeLinear (AnimationCurveTpl<T>& curve, int key);
+
+template<class T>
+void RecalculateSplineSlopeLinear (AnimationCurveTpl<T>& curve);
+
+void EnsureQuaternionContinuityLoopFrame (AnimationCurveQuat& curve);
+void EnsureQuaternionContinuityPreserveSlope (AnimationCurveQuat& curve);
+void EnsureQuaternionContinuityAndRecalculateSlope (AnimationCurveQuat& curve);
+
+template<class T>
+void RecalculateSplineSlopeLoop (AnimationCurveTpl<T>& curve, int key, float b);
+
+// Fits tangents key0.outSlope and key1.inSlope to the point value1 and value2
+// value1 and value2 - points to fit (at time1 and time2)
+template <class T>
+void FitTangents(KeyframeTpl<T>& key0, KeyframeTpl<T>& key1, float time1, float time2, const T& value1, const T& value2);
+
diff --git a/Runtime/Animation/AnimationEvent.cpp b/Runtime/Animation/AnimationEvent.cpp
new file mode 100644
index 0000000..2613d11
--- /dev/null
+++ b/Runtime/Animation/AnimationEvent.cpp
@@ -0,0 +1,202 @@
+#include "UnityPrefix.h"
+#include "AnimationEvent.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingArguments.h"
+#include "Runtime/Scripting/CommonScriptingClasses.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+#if ENABLE_MONO
+#endif
+
+
+INSTANTIATE_TEMPLATE_TRANSFER_EXPORTED (AnimationEvent)
+
+template<class TransferFunction>
+void AnimationEvent::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (time);
+ TRANSFER (functionName);
+ transfer.Transfer (stringParameter, "data");
+ transfer.Transfer (objectReferenceParameter, "objectReferenceParameter");
+ transfer.Transfer (floatParameter, "floatParameter");
+ transfer.Transfer (intParameter, "intParameter");
+
+ TRANSFER (messageOptions);
+}
+
+#if ENABLE_SCRIPTING
+
+
+static ScriptingObjectPtr s_ManagedAnimationEvent;
+
+static bool SetupInvokeArgument(ScriptingMethodPtr method, AnimationEvent& event, ScriptingArguments& parameters)
+{
+ int argCount = scripting_method_get_argument_count(method, GetScriptingTypeRegistry());
+
+ // Fast path - method takes no arguments
+ if (argCount == 0)
+ return true;
+
+ if (argCount > 1)
+ return false;
+
+ ScriptingTypePtr typeOfFirstArgument = scripting_method_get_nth_argumenttype(method,0,GetScriptingTypeRegistry());
+
+ const CommonScriptingClasses& cc = GetScriptingManager().GetCommonClasses();
+
+ if (typeOfFirstArgument == cc.floatSingle)
+ {
+ parameters.AddFloat(event.floatParameter);
+ return true;
+ }
+
+ if (typeOfFirstArgument == cc.int_32)
+ {
+ parameters.AddInt(event.intParameter);
+ return true;
+ }
+
+ if (typeOfFirstArgument == cc.string)
+ {
+ parameters.AddString(event.stringParameter.c_str());
+ return true;
+ }
+
+ if (typeOfFirstArgument == cc.animationEvent)
+ {
+ ScriptingObjectWithIntPtrField<AnimationEvent> scriptingAnimationEvent = scripting_object_new(GetScriptingManager().GetCommonClasses().animationEvent);
+ scriptingAnimationEvent.SetPtr(&event);
+
+ s_ManagedAnimationEvent = scriptingAnimationEvent.object;
+ parameters.AddObject(scriptingAnimationEvent.object);
+ return true;
+ }
+
+ if (scripting_class_is_subclass_of(typeOfFirstArgument,cc.unityEngineObject))
+ {
+ parameters.AddObject(Scripting::ScriptingWrapperFor(event.objectReferenceParameter));
+ return true;
+ }
+
+ if (scripting_class_is_enum(typeOfFirstArgument))
+ {
+ parameters.AddInt(event.intParameter);
+ return true;
+ }
+
+ return false;
+}
+
+static void CleanupManagedAnimationEventIfRequired()
+{
+ if (s_ManagedAnimationEvent == SCRIPTING_NULL)
+ return;
+
+ AnimationEvent* nativeAnimationEvent = NULL;
+ MarshallNativeStructIntoManaged(nativeAnimationEvent, s_ManagedAnimationEvent);
+ s_ManagedAnimationEvent = SCRIPTING_NULL;
+}
+
+static bool FireEventTo(MonoBehaviour& behaviour, AnimationEvent& event, AnimationState* state)
+{
+ ScriptingObjectPtr instance = behaviour.GetInstance ();
+ if (instance == SCRIPTING_NULL)
+ return false;
+
+ ScriptingMethodPtr method = behaviour.FindMethod (event.functionName.c_str());
+ if (method == SCRIPTING_NULL)
+ return false;
+
+
+ ScriptingInvocation invocation(method);
+
+ if (!SetupInvokeArgument(method, event, invocation.Arguments()))
+ {
+ ErrorStringObject (Format ("Failed to call AnimationEvent %s of class %s.\nThe function must have either 0 or 1 parameters and the parameter can only be: string, float, int, enum, Object and AnimationEvent.", scripting_method_get_name (method), behaviour.GetScriptClassName ().c_str ()), &behaviour);
+ return true;
+ }
+
+ // Suppress immediate destruction during the event callback to disallow
+ // the object killing itself directly or indirectly in there (would do bad
+ // things to the still updating animation state).
+ const bool disableImmediateDestruction = IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1);
+ bool oldDisableImmediateDestruction = false;
+ if (disableImmediateDestruction)
+ {
+ oldDisableImmediateDestruction = GetDisableImmediateDestruction ();
+ SetDisableImmediateDestruction (true);
+ }
+
+ event.stateSender = state;
+
+ ScriptingExceptionPtr exception = NULL;
+ invocation.object = instance;
+ invocation.logException = true;
+ invocation.objectInstanceIDContextForException = behaviour.GetInstanceID();
+ ScriptingObjectPtr returnValue = invocation.Invoke();
+
+ if (disableImmediateDestruction)
+ SetDisableImmediateDestruction (oldDisableImmediateDestruction);
+
+ if (returnValue && exception == NULL)
+ behaviour.HandleCoroutineReturnValue (method, returnValue);
+
+ event.stateSender = NULL;
+ CleanupManagedAnimationEventIfRequired();
+
+ return true;
+}
+
+static bool EventRequiresReceiver(const AnimationEvent& event)
+{
+ return event.messageOptions == 0;
+}
+
+#endif
+
+bool FireEvent (AnimationEvent& event, AnimationState* state, Unity::Component& animation)
+{
+ #if ENABLE_SCRIPTING
+ GameObject& go = animation.GetGameObject();
+ if (!go.IsActive ())
+ return false;
+
+ bool sent = false;
+
+ for (int i=0;i<go.GetComponentCount ();i++)
+ {
+ if (go.GetComponentClassIDAtIndex (i) != ClassID (MonoBehaviour))
+ continue;
+
+ MonoBehaviour& behaviour = static_cast<MonoBehaviour&> (go.GetComponentAtIndex (i));
+ if (FireEventTo(behaviour,event,state))
+ sent = true;
+ }
+
+ if (DEPLOY_OPTIMIZED)
+ return true;
+
+ if (sent)
+ return true;
+
+ if (!EventRequiresReceiver(event))
+ return true;
+
+ std::string warning = event.functionName.empty()
+ ? Format ("'%s' AnimationEvent has no function name specified!", go.GetName())
+ : Format ("'%s' AnimationEvent '%s' has no receiver! Are you missing a component?", go.GetName(), event.functionName.c_str());
+
+ ErrorStringObject (warning.c_str(), animation.GetGameObjectPtr());
+ return true;
+ #else
+ return false;
+ #endif
+}
diff --git a/Runtime/Animation/AnimationEvent.h b/Runtime/Animation/AnimationEvent.h
new file mode 100644
index 0000000..541d719
--- /dev/null
+++ b/Runtime/Animation/AnimationEvent.h
@@ -0,0 +1,29 @@
+#ifndef ANIMATIONEVENT_H
+#define ANIMATIONEVENT_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+class AnimationState;
+
+struct AnimationEvent
+{
+ DECLARE_SERIALIZE (AnimationEvent)
+
+ float time;
+ UnityStr functionName;
+ UnityStr stringParameter;
+ PPtr<Object> objectReferenceParameter;
+ float floatParameter;
+ int intParameter;
+
+ int messageOptions;
+ mutable AnimationState* stateSender;
+
+ AnimationEvent() { messageOptions = 0; stateSender = NULL; floatParameter = 0.0F; intParameter = 0; }
+
+ friend bool operator < (const AnimationEvent& lhs, const AnimationEvent& rhs) { return lhs.time < rhs.time; }
+};
+
+bool FireEvent (AnimationEvent& event, AnimationState* state, Unity::Component& animation);
+
+
+#endif
diff --git a/Runtime/Animation/AnimationManager.cpp b/Runtime/Animation/AnimationManager.cpp
new file mode 100644
index 0000000..c4c3e71
--- /dev/null
+++ b/Runtime/Animation/AnimationManager.cpp
@@ -0,0 +1,53 @@
+#include "UnityPrefix.h"
+#include "AnimationManager.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Animation.h"
+#include "AnimationState.h"
+#include "AnimationStateNetworkProvider.h"
+#include "Runtime/Core/Callbacks/PlayerLoopCallbacks.h"
+
+static AnimationManager* gAnimationManager = NULL;
+
+void AnimationManager::Update ()
+{
+ // Update animations
+ double time = GetCurTime();
+ AnimationList* animations = NULL;
+ if (GetTimeManager ().IsUsingFixedTimeStep ())
+ animations = &m_FixedAnimations;
+ else
+ animations = &m_Animations;
+
+ // Animation List node can be destroyed in UpdateAnimation, if somebody writes an AnimationEvent
+ // to do that. So we have to use SafeListIterator
+ SafeIterator<AnimationList> j (*animations);
+ while (j.Next())
+ {
+ Animation& animation = **j;
+ animation.UpdateAnimation(time);
+ }
+}
+
+void AnimationManager::InitializeClass ()
+{
+ Assert(gAnimationManager == NULL);
+ gAnimationManager = UNITY_NEW_AS_ROOT (AnimationManager, kMemAnimation, "AnimationManager", "");
+
+ REGISTER_PLAYERLOOP_CALL (LegacyFixedAnimationUpdate, GetAnimationManager().Update());
+ REGISTER_PLAYERLOOP_CALL (LegacyAnimationUpdate, GetAnimationManager().Update ());
+
+ InitializeAnimationStateNetworkProvider();
+}
+
+void AnimationManager::CleanupClass ()
+{
+ Assert(gAnimationManager != NULL);
+ UNITY_DELETE(gAnimationManager, kMemAnimation);
+
+ CleanupAnimationStateNetworkProvider();
+}
+
+AnimationManager& GetAnimationManager ()
+{
+ return *gAnimationManager;
+}
diff --git a/Runtime/Animation/AnimationManager.h b/Runtime/Animation/AnimationManager.h
new file mode 100644
index 0000000..51c9ff3
--- /dev/null
+++ b/Runtime/Animation/AnimationManager.h
@@ -0,0 +1,39 @@
+#ifndef ANIMATIONMANAGER_H
+#define ANIMATIONMANAGER_H
+
+#include "Runtime/Utilities/LinkedList.h"
+
+class Animation;
+
+class AnimationManager
+{
+ public:
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ /// Animates all registered objects according to the registered parameters
+ /// Removes Animations if the animated object or the track is gone or if the time
+ /// of the track end time of the animation is reached
+ void Update ();
+
+ public:
+
+ void AddDynamic(ListNode<Animation>& node) { m_Animations.push_back(node); }
+ void AddFixed (ListNode<Animation>& node) { m_FixedAnimations.push_back(node); }
+
+#if ENABLE_PROFILER
+ int GetUpdatedAnimationCount () { return m_Animations.size_slow() + m_FixedAnimations.size_slow(); }
+#endif
+
+ private:
+
+ typedef List< ListNode<Animation> > AnimationList;
+
+ AnimationList m_Animations;
+ AnimationList m_FixedAnimations;
+};
+
+AnimationManager& GetAnimationManager ();
+
+#endif
diff --git a/Runtime/Animation/AnimationModule.jam b/Runtime/Animation/AnimationModule.jam
new file mode 100644
index 0000000..45e282a
--- /dev/null
+++ b/Runtime/Animation/AnimationModule.jam
@@ -0,0 +1,169 @@
+rule AnimationModule_ReportCpp
+{
+ local animationSources =
+ AnimationModule.jam
+ AnimationModuleRegistration.cpp
+ AnimationStateNetworkProvider.cpp
+ AnimationStateNetworkProvider.h
+ Animation.cpp
+ Animation.h
+ AnimationBinder.cpp
+ AnimationBinder.h
+ AnimatorGenericBindings.cpp
+ AnimatorGenericBindings.h
+ GenericAnimationBindingCache.cpp
+ GenericAnimationBindingCache.h
+ AnimationSetBinding.cpp
+ AnimationSetBinding.h
+ AnimatorOverrideController.cpp
+ AnimatorOverrideController.h
+ AnimationClipBindings.h
+ AnimationClipSettings.h
+ Motion.cpp
+ Motion.h
+ AnimationClip.cpp
+ AnimationClip.h
+ AnimationClipUtility.cpp
+ AnimationClipUtility.h
+ AnimationCurveUtility.cpp
+ AnimationCurveUtility.h
+ AnimationEvent.cpp
+ AnimationEvent.h
+ AnimationManager.cpp
+ AnimationManager.h
+ AnimationState.cpp
+ AnimationState.h
+ BaseAnimationTrack.cpp
+ BaseAnimationTrack.h
+ BoundCurveDeprecated.h
+ BoundCurve.h
+ MecanimUtility.cpp
+ MecanimUtility.h
+ NewAnimationTrack.cpp
+ NewAnimationTrack.h
+ Avatar.cpp
+ Avatar.h
+ AvatarBuilder.cpp
+ AvatarBuilder.h
+ Animator.cpp
+ Animator.h
+ AnimatorManager.cpp
+ AnimatorManager.h
+ AvatarPlayback.cpp
+ AvatarPlayback.h
+ RuntimeAnimatorController.cpp
+ RuntimeAnimatorController.h
+ AnimatorController.cpp
+ AnimatorController.h
+ MecanimArraySerialization.h
+ AnimationUtility.cpp
+ AnimationUtility.h
+ StreamedClipBuilder.cpp
+ StreamedClipBuilder.h
+ DenseClipBuilder.cpp
+ DenseClipBuilder.h
+ MecanimClipBuilder.cpp
+ MecanimClipBuilder.h
+ AnimationClipSettings.h
+ CalculateAnimatorSkinMatrices.cpp
+ CalculateAnimatorSkinMatrices.h
+ CharacterTestFixture.h
+ MecanimAnimation.h
+ MecanimAnimation.cpp
+ OptimizeTransformHierarchy.cpp
+ OptimizeTransformHierarchyTests.cpp
+ OptimizeTransformHierarchy.h
+ ;
+
+ local mecanimSources =
+ bind.h
+ bitset.h
+ defs.h
+ memory.h
+ string.h
+ types.h
+ vector.h
+ animation/avatar.h
+ animation/avatar.cpp
+ animation/clipmuscle.h
+ animation/clipmuscle.cpp
+ animation/blendtree.h
+ animation/blendtree.cpp
+ animation/curvedata.h
+ animation/curvedata.cpp
+ animation/denseclip.h
+ animation/denseclip.cpp
+ animation/streamedclip.h
+ animation/streamedclip.cpp
+ animation/constantclip.h
+ animation/constantclip.cpp
+ animation/damp.h
+ animation/damp.cpp
+ # animation/poseblender.h
+ # animation/poseblender.cpp
+ generic/crc32.h
+ generic/stringtable.h
+ generic/stringtable.cpp
+ generic/typetraits.h
+ generic/valuearray.cpp
+ generic/valuearray.h
+ # graph/binarynode.h
+ # graph/factory.cpp
+ # graph/factory.h
+ # graph/genericnode.h
+ # graph/graph.cpp
+ # graph/graph.h
+ # graph/node.cpp
+ # graph/node.h
+ # graph/plug.h
+ # graph/plugbinder.cpp
+ # graph/plugbinder.h
+ # graph/quaternionnode.h
+ # graph/unarynode.h
+ # graph/xformnode.h
+ math/collider.h
+ math/axes.h
+ human/hand.cpp
+ human/hand.h
+ human/handle.h
+ human/human.cpp
+ human/human.h
+ skeleton/skeleton.cpp
+ skeleton/skeleton.h
+ statemachine/statemachine.cpp
+ statemachine/statemachine.h
+ ;
+
+ local modulesources =
+ Runtime/Animation/$(animationSources)
+ Runtime/mecanim/$(mecanimSources)
+ ;
+
+ return $(modulesources) ;
+}
+
+rule AnimationModule_ReportTxt
+{
+ return
+ Runtime/Animation/ScriptBindings/AnimatorOverrideControllerBindings.txt
+ Runtime/Animation/ScriptBindings/Animations.txt
+ Runtime/Animation/ScriptBindings/AnimatorBindings.txt
+ Runtime/Animation/ScriptBindings/AvatarBuilderBindings.txt
+ Runtime/Animation/ScriptBindings/RuntimeAnimatorControllerBindings.txt
+ Runtime/Animation/ScriptBindings/Avatar.txt
+ ;
+}
+
+## For Coexist with old system
+rule AnimationModule_ReportIncludes
+{
+
+}
+
+rule AnimationModule_Init
+{
+ OverrideModule Animation : GetModule_Cpp : byOverridingWithMethod : AnimationModule_ReportCpp ;
+ OverrideModule Animation : GetModule_Txt : byOverridingWithMethod : AnimationModule_ReportTxt ;
+}
+
+#RegisterModule Animation ;
diff --git a/Runtime/Animation/AnimationModuleRegistration.cpp b/Runtime/Animation/AnimationModuleRegistration.cpp
new file mode 100644
index 0000000..c95c4bd
--- /dev/null
+++ b/Runtime/Animation/AnimationModuleRegistration.cpp
@@ -0,0 +1,53 @@
+#include "UnityPrefix.h"
+#include "Runtime/BaseClasses/ClassRegistration.h"
+#include "Runtime/Modules/ModuleRegistration.h"
+
+static void RegisterAnimationClasses (ClassRegistrationContext& context)
+{
+ REGISTER_CLASS (Animation)
+ REGISTER_CLASS (AnimationClip)
+ REGISTER_CLASS (RuntimeAnimatorController)
+ REGISTER_CLASS (AnimatorController)
+ REGISTER_CLASS (Animator)
+ REGISTER_CLASS (Avatar)
+ REGISTER_CLASS (Motion)
+#if !UNITY_WEBGL
+ REGISTER_CLASS (AnimatorOverrideController)
+#endif
+#if UNITY_EDITOR
+ ///@TODO: Lets remove those. It's been deprecated since Unity 1.6
+ REGISTER_CLASS (BaseAnimationTrack)
+ REGISTER_CLASS (NewAnimationTrack)
+#endif
+}
+
+#if ENABLE_MONO || UNITY_WINRT
+void ExportRuntimeAnimatorControllerBindings();
+void ExportAnimatorBindings();
+void ExportAnimations();
+void ExportAvatarBuilderBindings ();
+void ExportAvatar ();
+void ExportAnimatorOverrideControllerBindings();
+
+static void RegisterAnimationICallModule ()
+{
+#if !INTERNAL_CALL_STRIPPING
+ ExportRuntimeAnimatorControllerBindings ();
+ ExportAnimatorBindings();
+ ExportAnimations ();
+ ExportAvatarBuilderBindings ();
+ ExportAvatar ();
+ ExportAnimatorOverrideControllerBindings ();
+#endif
+}
+#endif
+
+extern "C" EXPORT_MODULE void RegisterModule_Animation ()
+{
+ ModuleRegistrationInfo info;
+ info.registerClassesCallback = &RegisterAnimationClasses;
+#if ENABLE_MONO || UNITY_WINRT
+ info.registerIcallsCallback = &RegisterAnimationICallModule;
+#endif
+ RegisterModuleInfo (info);
+}
diff --git a/Runtime/Animation/AnimationSetBinding.cpp b/Runtime/Animation/AnimationSetBinding.cpp
new file mode 100644
index 0000000..234f64c
--- /dev/null
+++ b/Runtime/Animation/AnimationSetBinding.cpp
@@ -0,0 +1,563 @@
+#include "UnityPrefix.h"
+#include "AnimationSetBinding.h"
+#include "RuntimeAnimatorController.h"
+#include "AnimationClipBindings.h"
+#include "GenericAnimationBindingCache.h"
+#include "AnimationClip.h"
+#include "Runtime/mecanim/generic/stringtable.h"
+#include "Runtime/mecanim/animation/avatar.h"
+#include "Runtime/mecanim/animation/curvedata.h"
+
+namespace UnityEngine
+{
+namespace Animation
+{
+static mecanim::ValueArrayConstant* CreateBindingValueArrayConstant (const GenericBinding* genericBindings, size_t genericBindingsSize, const GenericBinding* genericPPtrBindings, size_t genericPPtrBindingsSize, const TransformBinding* transformBindings, size_t transformBindingsSize, mecanim::memory::Allocator& alloc);
+static void CombineUniqueGeneric (AnimationClipBindingConstant** constants, mecanim::animation::AnimationSet::Clip** animationSetClips, size_t clipCount, dynamic_array<TransformBinding>& outputCombinedTransformBinding, int& outputNonConstantTransformCount, dynamic_array<GenericBinding>& outputCombinedGenericBinding, dynamic_array<GenericBinding>& outputCombinedGenericPPtrBinding, dynamic_array<float>& constantValues, mecanim::memory::Allocator& alloc);
+static void BindAdditionalCurves(mecanim::ValueArrayConstant const &valueConstant, dynamic_array<GenericBinding> const& genericBindings, size_t bindingOffset, mecanim::int32_t *additionalIndexArray);
+static void BindSkeletonMask(mecanim::ValueArrayConstant const &valueConstant, mecanim::skeleton::SkeletonMask const &skeletonMask, mecanim::ValueArrayMask &mask);
+
+
+bool IsPPtrAnimation (const GenericBinding& binding)
+{
+ return binding.isPPtrCurve;
+}
+
+void DestroyAnimationSetBindings (AnimationSetBindings* bindings, mecanim::memory::Allocator& allocator)
+{
+ if (bindings != NULL)
+ {
+ allocator.Deallocate (bindings->genericBindings);
+ allocator.Deallocate (bindings->genericPPtrBindings);
+ allocator.Deallocate (bindings->transformBindings);
+ DestroyAnimationSet (bindings->animationSet, allocator);
+ allocator.Deallocate (bindings);
+ }
+}
+
+static void BindGravityWeight (mecanim::animation::AnimationSet* animationSet, const mecanim::ValueArrayConstant* dynamicValuesConstant)
+{
+ animationSet->m_GravityWeightIndex = mecanim::FindValueIndex(dynamicValuesConstant, CRCKey(mecanim::eGravityWeight));
+ if (animationSet->m_GravityWeightIndex != -1)
+ animationSet->m_GravityWeightIndex = dynamicValuesConstant->m_ValueArray[animationSet->m_GravityWeightIndex].m_Index;
+}
+
+AnimationSetBindings* CreateAnimationSetBindings (mecanim::animation::ControllerConstant const* controller, AnimationClipVector const& clips, mecanim::memory::Allocator& allocator)
+{
+ if (controller == NULL)
+ return NULL;
+
+ SETPROFILERLABEL(AnimationSetBindings);
+
+
+ size_t clipCount = clips.size();
+
+ mecanim::animation::AnimationSet::Clip** animationSetClips;
+ ALLOC_TEMP(animationSetClips, mecanim::animation::AnimationSet::Clip*, clipCount);
+
+ AnimationClipBindingConstant** animationClipBindings;
+ ALLOC_TEMP(animationClipBindings, AnimationClipBindingConstant*, clipCount);
+
+ mecanim::animation::ClipMuscleConstant** clipConstants;
+ ALLOC_TEMP(clipConstants, mecanim::animation::ClipMuscleConstant*, clipCount);
+
+
+ // Create Animation Set
+ mecanim::animation::AnimationSet* animationSet = CreateAnimationSet(controller, allocator);
+ animationSet->m_IntegerRemapStride = sizeof(PPtr<Object>);
+
+
+ size_t clipArrayIndex = 0;
+ for(int layerIter=0; layerIter<animationSet->m_LayerCount; layerIter++)
+ {
+ for(int clipIter=0; clipIter < animationSet->m_ClipPerLayer[layerIter]; clipIter++)
+ {
+ AnimationClip* animationClip = clips[clipArrayIndex];
+ mecanim::animation::ClipMuscleConstant* clipConstant = animationClip != NULL ? animationClip->GetRuntimeAsset() : 0;
+
+ mecanim::animation::AnimationSet::Clip& setClip = animationSet->m_ClipConstant[layerIter][clipIter];
+ setClip.m_Clip = clipConstant;
+ setClip.m_ClipIndex = clipArrayIndex;
+
+ if (clipConstant != NULL)
+ {
+ animationSetClips[clipArrayIndex] = &setClip;
+ animationClipBindings[clipArrayIndex] = animationClip->GetBindingConstant();
+
+ // Setup pptrCurve mapping (mecanim accesses it through m_IntegerRemap & m_IntegerRemapStride)
+ setClip.m_Bindings.m_IntegerRemap = reinterpret_cast<mecanim::int32_t*> (animationClipBindings[clipArrayIndex]->pptrCurveMapping.begin());
+ }
+ else
+ {
+ animationSetClips[clipArrayIndex] = NULL;
+ animationClipBindings[clipArrayIndex] = NULL;
+ setClip.m_Bindings.m_IntegerRemap = NULL;
+ }
+
+ clipArrayIndex++;
+ }
+ }
+
+ assert(clipCount == clipArrayIndex);
+
+
+ // Bind to unique curves
+ dynamic_array<GenericBinding> genericBindings (kMemTempAlloc);
+ dynamic_array<GenericBinding> genericPPtrBindings (kMemTempAlloc);
+ dynamic_array<TransformBinding> transformBindings (kMemTempAlloc);
+ dynamic_array<float> constantValues (kMemTempAlloc);
+ int transformBindingsNonConstants;
+
+ CombineUniqueGeneric (animationClipBindings, animationSetClips, clipCount, transformBindings, transformBindingsNonConstants, genericBindings, genericPPtrBindings, constantValues, allocator);
+
+ animationSet->m_DynamicFullValuesConstant = CreateBindingValueArrayConstant (genericBindings.begin(), genericBindings.size(), genericPPtrBindings.begin(), genericPPtrBindings.size(), transformBindings.begin(), transformBindings.size(), allocator);
+
+ for(int layerIter=0; layerIter<animationSet->m_LayerCount; layerIter++)
+ {
+ animationSet->m_DynamicValuesMaskArray[layerIter] = mecanim::CreateValueArrayMask(animationSet->m_DynamicFullValuesConstant,allocator);
+ BindSkeletonMask(*animationSet->m_DynamicFullValuesConstant, *controller->m_LayerArray[layerIter]->m_SkeletonMask, *animationSet->m_DynamicValuesMaskArray[layerIter]);
+ }
+
+ BindAdditionalCurves(*controller->m_Values.Get(), genericBindings, 0, animationSet->m_AdditionalIndexArray);
+
+ BindGravityWeight (animationSet, animationSet->m_DynamicFullValuesConstant);
+
+ // Generate output bindings
+ AnimationSetBindings* outputBindings = allocator.Construct<AnimationSetBindings> ();
+ outputBindings->animationSet = animationSet;
+
+ outputBindings->transformBindingsNonConstantSize = transformBindingsNonConstants;
+ outputBindings->transformBindingsSize = transformBindings.size();
+ outputBindings->transformBindings = allocator.ConstructArray<TransformBinding> (transformBindings.begin(), transformBindings.size());
+
+ outputBindings->genericBindingsSize = genericBindings.size();
+ outputBindings->genericBindings = allocator.ConstructArray<GenericBinding> (genericBindings.begin(), genericBindings.size());
+
+ outputBindings->genericPPtrBindingsSize = genericPPtrBindings.size();
+ outputBindings->genericPPtrBindings = allocator.ConstructArray<GenericBinding> (genericPPtrBindings.begin(), genericPPtrBindings.size());
+
+ outputBindings->constantCurveValueCount = constantValues.size();
+ outputBindings->constantCurveValues = allocator.ConstructArray<float> (constantValues.begin(), constantValues.size());
+
+ return outputBindings;
+}
+
+size_t GetCurveCountForBindingType (UInt32 targetType)
+{
+ if (targetType == kBindTransformRotation)
+ return 4;
+ else if (targetType == kBindTransformPosition || targetType == kBindTransformScale)
+ return 3;
+ else
+ return 1;
+}
+
+static size_t GetCurveCountForBinding (const GenericBinding& binding)
+{
+ if (binding.classID == ClassID(Transform))
+ return GetCurveCountForBindingType(binding.attribute);
+ else
+ return 1;
+}
+
+static mecanim::ValueArrayConstant* CreateBindingValueArrayConstant (const GenericBinding* genericBindings, size_t genericBindingsSize, const GenericBinding* genericPPtrBindings, size_t genericPPtrBindingsSize, const TransformBinding* transformBindings, size_t transformBindingsSize, mecanim::memory::Allocator& alloc)
+{
+ size_t valueCount = genericBindingsSize + transformBindingsSize + genericPPtrBindingsSize;
+ mecanim::ValueArrayConstant* constant = CreateValueArrayConstant (mecanim::kFloatType, valueCount, alloc);
+
+ mecanim::ValueConstant* valueArray = constant->m_ValueArray.Get();
+
+ mecanim::uint32_t valueIndex = 0;
+ mecanim::uint32_t positionCount = 0;
+ mecanim::uint32_t rotationCount = 0;
+ mecanim::uint32_t scaleCount = 0;
+
+ for(int i=0;i<genericBindingsSize;i++,valueIndex++)
+ {
+ valueArray[valueIndex].m_ID = genericBindings[i].attribute;
+ valueArray[valueIndex].m_Index = i;
+ valueArray[valueIndex].m_Type = mecanim::kFloatType;
+ }
+
+ for(int i=0;i<genericPPtrBindingsSize;i++,valueIndex++)
+ {
+ valueArray[valueIndex].m_ID = genericPPtrBindings[i].attribute;
+ valueArray[valueIndex].m_Index = i;
+ valueArray[valueIndex].m_Type = mecanim::kInt32Type;
+ }
+
+ // Currently transform bindings are the only ones with constant curve optimization.
+ // They have to be placed at the end of the ValueArrayConstant so we can reduce it.
+ for(int i=0;i<transformBindingsSize;i++,valueIndex++)
+ {
+ valueArray[valueIndex].m_ID = transformBindings[i].path;
+
+ switch( transformBindings[i].bindType)
+ {
+ case kBindTransformPosition:
+ valueArray[valueIndex].m_Index = positionCount++;
+ valueArray[valueIndex].m_Type = mecanim::kPositionType;
+ break;
+
+ case kBindTransformRotation:
+ valueArray[valueIndex].m_Index = rotationCount++;
+ valueArray[valueIndex].m_Type = mecanim::kQuaternionType;
+ break;
+
+ case kBindTransformScale:
+ valueArray[valueIndex].m_Index = scaleCount++;
+ valueArray[valueIndex].m_Type = mecanim::kScaleType;
+ break;
+
+ default:
+ AssertString("Unsupported");
+ }
+ }
+
+ return constant;
+}
+
+struct BoundIndex
+{
+ enum { kNotInitialized = 0, kIsConstant = 2, kIsNotConstant = 3 };
+
+ mecanim::ValueType type;
+ int index;
+ int curveCount;
+
+ int constantType;
+ float constantValue[4];
+
+ BoundIndex ()
+ {
+ constantType = kNotInitialized;
+ type = mecanim::kLastType;
+ index = -1;
+ curveCount = -1;
+ }
+};
+
+// Currently constant curve optimization is only supported for Transforms
+bool DoesBindingSupportConstantCurveOptimization (const GenericBinding& binding)
+{
+ return binding.classID == ClassID(Transform);
+}
+
+typedef UNITY_MAP(kMemTempAlloc, GenericBinding, BoundIndex) BindingMap;
+
+static void GenerateUniqueBindingMap (AnimationClipBindingConstant** constants, mecanim::animation::AnimationSet::Clip** animationSetClips, size_t clipCount, BindingMap& bindingMap)
+{
+ for (int c=0;c<clipCount;c++)
+ {
+ if (constants[c] == NULL)
+ continue;
+
+ const dynamic_array<GenericBinding>& genericBindingArray = constants[c]->genericBindings;
+
+ const mecanim::animation::Clip& clipData = *animationSetClips[c]->m_Clip->m_Clip;
+ size_t curveIndex = 0;
+ size_t firstConstantIndex = GetClipCurveCount(clipData) - clipData.m_ConstantClip.curveCount;
+ for (int i=0;i<genericBindingArray.size();i++)
+ {
+ const GenericBinding& binding = genericBindingArray[i];
+
+ size_t curveCountForBinding = GetCurveCountForBinding(binding);
+ BoundIndex& boundIndex = bindingMap.insert(std::make_pair (binding, BoundIndex ())).first->second;
+
+ // Detect constant curve values
+ if (curveIndex >= firstConstantIndex && DoesBindingSupportConstantCurveOptimization (binding))
+ {
+ const float* constantCurveValue = &clipData.m_ConstantClip.data[curveIndex - firstConstantIndex];
+ if (boundIndex.constantType == BoundIndex::kNotInitialized)
+ {
+ boundIndex.constantType = BoundIndex::kIsConstant;
+
+ memcpy(boundIndex.constantValue, constantCurveValue, curveCountForBinding * sizeof(float));
+ }
+ else if (boundIndex.constantType == BoundIndex::kIsConstant)
+ {
+ //@TODO: Use approximately??? also in the instance check?
+
+ if (memcmp(boundIndex.constantValue, constantCurveValue, curveCountForBinding * sizeof(float)) != 0)
+ boundIndex.constantType = BoundIndex::kIsNotConstant;
+ }
+ }
+ else
+ {
+ boundIndex.constantType = BoundIndex::kIsNotConstant;
+ }
+
+ curveIndex += curveCountForBinding;
+ }
+ }
+}
+
+static void CombineUniqueGeneric (AnimationClipBindingConstant** constants, mecanim::animation::AnimationSet::Clip** animationSetClips, size_t clipCount, dynamic_array<TransformBinding>& outputCombinedTransformBinding, int& outputNonConstantTransformCount, dynamic_array<GenericBinding>& outputCombinedGenericBinding, dynamic_array<GenericBinding>& outputCombinedGenericPPtrBinding, dynamic_array<float>& constantValues, mecanim::memory::Allocator& alloc)
+{
+ BindingMap bindingMap;
+
+ // Generate unique set of all properties in all clips
+ GenerateUniqueBindingMap (constants, animationSetClips, clipCount, bindingMap);
+
+ // Sorting binding array, normal curves first, constant curves at the end
+ typedef pair<GenericBinding, BoundIndex*> BindingType;
+ dynamic_array<BindingType> bindingsSorted;
+ bindingsSorted.reserve(bindingMap.size());
+
+ for (BindingMap::iterator i=bindingMap.begin();i != bindingMap.end();i++)
+ {
+ if (i->second.constantType == BoundIndex::kIsNotConstant)
+ bindingsSorted.push_back(std::make_pair(i->first, &i->second));
+ }
+ for (BindingMap::iterator i=bindingMap.begin();i != bindingMap.end();i++)
+ {
+ if (i->second.constantType == BoundIndex::kIsConstant)
+ bindingsSorted.push_back(std::make_pair(i->first, &i->second));
+ }
+
+ // Fill in BoundIndex in the bindingMap
+ int positionCount = 0;
+ int rotationCount = 0;
+ int scaleCount = 0;
+ int genericCount = 0;
+ int genericPPtrCount = 0;
+ for (int i=0;i<bindingsSorted.size();i++)
+ {
+ BindingType& binding = bindingsSorted[i];
+
+ if (binding.first.classID == ClassID (Transform))
+ {
+ switch( binding.first.attribute)
+ {
+ case kBindTransformPosition:
+ binding.second->type = mecanim::kPositionType;
+ binding.second->index = positionCount++;
+ binding.second->curveCount = 3;
+ break;
+
+ case kBindTransformRotation:
+ binding.second->type = mecanim::kQuaternionType;
+ binding.second->index = rotationCount++;
+ binding.second->curveCount = 4;
+ break;
+
+ case kBindTransformScale:
+ binding.second->type = mecanim::kScaleType;
+ binding.second->index = scaleCount++;
+ binding.second->curveCount = 3;
+ break;
+
+ default:
+ Assert("Unsupported");
+ }
+ }
+ else if (IsPPtrAnimation(binding.first))
+ {
+ binding.second->type = mecanim::kInt32Type;
+ binding.second->index = genericPPtrCount++;
+ binding.second->curveCount = 1;
+ }
+ else if (!IsMuscleBinding(binding.first))
+ {
+ binding.second->type = mecanim::kFloatType;
+ binding.second->index = genericCount++;
+ binding.second->curveCount = 1;
+ }
+ else
+ {
+ binding.second->type = mecanim::kLastType;
+ binding.second->index = -1;
+ binding.second->curveCount = 1;
+ }
+ }
+
+ // Convert to outputCombinedArray
+ outputCombinedGenericBinding.reserve(genericCount);
+ outputCombinedGenericPPtrBinding.reserve(genericPPtrCount);
+ outputCombinedTransformBinding.reserve(positionCount + rotationCount + scaleCount);
+
+ outputNonConstantTransformCount = 0;
+
+ for (int i=0;i<bindingsSorted.size();i++)
+ {
+ const BindingType& binding = bindingsSorted[i];
+
+ if (binding.first.classID == ClassID (Transform))
+ {
+ TransformBinding& transformBinding = outputCombinedTransformBinding.push_back();
+ transformBinding.path = binding.first.path;
+ transformBinding.bindType = binding.first.attribute;
+
+ if (binding.second->constantType == BoundIndex::kIsNotConstant)
+ outputNonConstantTransformCount = outputCombinedTransformBinding.size();
+ else
+ {
+ for (int k=0;k<binding.second->curveCount;k++)
+ constantValues.push_back(binding.second->constantValue[k]);
+ }
+ }
+ else if (IsPPtrAnimation(binding.first))
+ {
+ Assert(binding.second->constantType == BoundIndex::kIsNotConstant);
+ outputCombinedGenericPPtrBinding.push_back(binding.first);
+ }
+ else if (!IsMuscleBinding(binding.first))
+ {
+ Assert(binding.second->constantType == BoundIndex::kIsNotConstant);
+ outputCombinedGenericBinding.push_back(binding.first);
+ }
+ }
+
+
+ // Generate remap from ValueArray to curveIndex
+ for (int c=0;c<clipCount;c++)
+ {
+ if (animationSetClips[c] == NULL)
+ continue;
+
+ const dynamic_array<GenericBinding>& genericBindingArray = constants[c]->genericBindings;
+
+ mecanim::animation::AnimationSet::Clip* clip = animationSetClips[c];
+ mecanim::animation::ClipBindings& clipBindings = clip->m_Bindings;
+
+ clipBindings.m_PositionIndex = alloc.ConstructArray<mecanim::int16_t>(positionCount);
+ clipBindings.m_QuaternionIndex = alloc.ConstructArray<mecanim::int16_t>(rotationCount);
+ clipBindings.m_ScaleIndex = alloc.ConstructArray<mecanim::int16_t>(scaleCount);
+ clipBindings.m_FloatIndex = alloc.ConstructArray<mecanim::int16_t>(genericCount);
+ clipBindings.m_IntIndex = alloc.ConstructArray<mecanim::int16_t>(genericPPtrCount);
+
+ for (int i=0;i<positionCount;i++)
+ clipBindings.m_PositionIndex[i] = -1;
+ for (int i=0;i<rotationCount;i++)
+ clipBindings.m_QuaternionIndex[i] = -1;
+ for (int i=0;i<scaleCount;i++)
+ clipBindings.m_ScaleIndex[i] = -1;
+ for (int i=0;i<genericCount;i++)
+ clipBindings.m_FloatIndex[i] = -1;
+ for (int i=0;i<genericPPtrCount;i++)
+ clipBindings.m_IntIndex[i] = -1;
+
+ int curveIndex = 0;
+ int totalUsedOptimizedCurveCount = 0;
+ for (int i=0;i<genericBindingArray.size();i++)
+ {
+ const GenericBinding& genericBinding = genericBindingArray[i];
+
+ BindingMap::iterator found = bindingMap.find(genericBinding);
+ Assert(found != bindingMap.end());
+
+ switch (found->second.type)
+ {
+ case mecanim::kPositionType:
+ clipBindings.m_PositionIndex[found->second.index] = curveIndex;
+ break;
+
+ case mecanim::kQuaternionType:
+ clipBindings.m_QuaternionIndex[found->second.index] = curveIndex;
+ break;
+
+ case mecanim::kScaleType:
+ clipBindings.m_ScaleIndex[found->second.index] = curveIndex;
+ break;
+
+ case mecanim::kFloatType:
+ clipBindings.m_FloatIndex[found->second.index] = curveIndex;
+ break;
+
+ case mecanim::kInt32Type:
+ clipBindings.m_IntIndex[found->second.index] = curveIndex;
+ break;
+
+ default:
+ Assert("Unsupported");
+ break;
+ }
+
+ curveIndex += found->second.curveCount;
+
+ if (found->second.constantType == BoundIndex::kIsNotConstant)
+ totalUsedOptimizedCurveCount = curveIndex;
+ }
+
+ // The total used curve count is based on the properties we bind.
+
+ // Muscle clip properties are not bound through the animation set they are pre-bound when building the clip
+ // But we still have to allocate memory for them in the clipoutputs for sampling
+ const mecanim::animation::ClipMuscleConstant& clipMuscleConstant = *clip->m_Clip;
+ for (int m=0;m<mecanim::animation::s_ClipMuscleCurveCount;m++)
+ totalUsedOptimizedCurveCount = std::max<mecanim::int32_t> (clipMuscleConstant.m_IndexArray[m] + 1, totalUsedOptimizedCurveCount);
+
+ clip->m_TotalUsedOptimizedCurveCount = totalUsedOptimizedCurveCount;
+ }
+}
+
+static void BindAdditionalCurves(mecanim::ValueArrayConstant const &valueConstant, dynamic_array<GenericBinding> const& genericBindings, size_t bindingOffset, mecanim::int32_t *additionalIndexArray)
+{
+ for(int genericIter = 0; genericIter < genericBindings.size(); genericIter++)
+ {
+ if (genericBindings[genericIter].classID == ClassID(Animator))
+ {
+ //@TODO: Watch out when switching hash to 64 bit
+ mecanim::int32_t valueIndex = mecanim::FindValueIndex(&valueConstant, mecanim::uint32_t(genericBindings[genericIter].attribute));
+
+ if (valueIndex != -1)
+ {
+ additionalIndexArray[valueIndex] = bindingOffset + genericIter;
+ }
+ }
+ }
+}
+
+static void BindSkeletonMask(const mecanim::ValueArrayConstant& valueConstant, const mecanim::skeleton::SkeletonMask& skeletonMask, mecanim::ValueArrayMask& mask)
+{
+ bool emptyMask = skeletonMask.m_Count == 0;
+
+ for(int maskIter = 0; maskIter < valueConstant.m_Count; maskIter++)
+ {
+ bool maskValue = false;
+
+ if (emptyMask || (valueConstant.m_ValueArray[maskIter].m_Type == mecanim::kFloatType))
+ {
+ maskValue = true;
+ }
+ else
+ {
+ bool found = false;
+
+ for(int skIter = 0; !found && skIter < skeletonMask.m_Count; skIter++)
+ {
+ if(skeletonMask.m_Data[skIter].m_Weight > 0)
+ {
+ found = valueConstant.m_ValueArray[maskIter].m_ID == skeletonMask.m_Data[skIter].m_PathHash;
+ }
+ }
+
+ maskValue = found;
+ }
+
+
+ int index = valueConstant.m_ValueArray[maskIter].m_Index;
+ switch (valueConstant.m_ValueArray[maskIter].m_Type)
+ {
+ case mecanim::kPositionType:
+ mask.m_PositionValues[index] = maskValue;
+ break;
+ case mecanim::kQuaternionType:
+ mask.m_QuaternionValues[index] = maskValue;
+ break;
+ case mecanim::kScaleType:
+ mask.m_ScaleValues[index] = maskValue;
+ break;
+ case mecanim::kFloatType:
+ mask.m_FloatValues[index] = maskValue;
+ break;
+ case mecanim::kInt32Type:
+ mask.m_IntValues[index] = maskValue;
+ break;
+ default:
+ Assert("Unsupported");
+ }
+ }
+}
+}
+}
diff --git a/Runtime/Animation/AnimationSetBinding.h b/Runtime/Animation/AnimationSetBinding.h
new file mode 100644
index 0000000..971df71
--- /dev/null
+++ b/Runtime/Animation/AnimationSetBinding.h
@@ -0,0 +1,74 @@
+#pragma once
+
+#include "AnimationClipBindings.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+
+class RuntimeAnimatorController;
+
+class AnimationClip;
+
+namespace mecanim { namespace animation { struct AnimationSet; struct ControllerConstant; } }
+namespace mecanim { namespace memory { class Allocator; } }
+typedef std::vector<PPtr<AnimationClip> > AnimationClipVector;
+
+namespace UnityEngine
+{
+namespace Animation
+{
+
+
+struct TransformBinding
+{
+ BindingHash path;
+ int bindType;
+};
+
+struct AnimationSetBindings
+{
+ size_t genericBindingsSize;
+ GenericBinding* genericBindings;
+
+ size_t genericPPtrBindingsSize;
+ GenericBinding* genericPPtrBindings;
+
+ size_t transformBindingsNonConstantSize;
+ size_t transformBindingsSize;
+ TransformBinding* transformBindings;
+
+
+ // See ConstantCurveOptimization below:
+ size_t constantCurveValueCount;
+ float* constantCurveValues;
+
+ mecanim::animation::AnimationSet* animationSet;
+};
+
+AnimationSetBindings* CreateAnimationSetBindings (mecanim::animation::ControllerConstant const* controller, AnimationClipVector const& clips, mecanim::memory::Allocator& allocator);
+void DestroyAnimationSetBindings (AnimationSetBindings* bindings, mecanim::memory::Allocator& allocator);
+size_t GetCurveCountForBindingType (UInt32 targetType);
+
+
+/*
+ *** Constant Curve optimization overview ***
+
+We found that animationclips for generic characters in most cases contain a lot of scale curves and often also translation curves which are completely constant.
+This has a massive negative impact on performance. StreamedClip sampling is negatively affected because all the coefficients need to be evaluated.
+But more importantly the ValueArray used for blending becomes very big. Especially since blending scale curves is pretty expensive (log scale blend)
+
+It would have been easy to simply remove the curves that are constant when importing the clips,
+but there is no good automatic default for it because in some cases the user actually makes constant curves intentionally to get an effect when blending between clips.
+
+So when creating the mecanim clip we classify all curves as constant curves and streamedclip curves. Constant Curves are put at the end.
+
+Constantclip evaluation is trivial, simply a memcpy.
+
+When binding we also detect if all the clips have the same constant values. If they do we put the constant bindings at the end of the ValueArrayConstant.
+On the instance we then need to verify if the constant curves on the clip matches the default values on the instance.
+This is because defaultValues are used for blending. If they are used for blending and different between defaults and clip values, then they must be represented in the ValueArray.
+
+Thus on the instance we have a subset of the value array and on the instance we can simply reduce the value array by cutting of the end of the ValueArray.
+All data is laid out to be able to do this in the AnimationSetBinding.
+
+*/
+}
+}
diff --git a/Runtime/Animation/AnimationState.cpp b/Runtime/Animation/AnimationState.cpp
new file mode 100644
index 0000000..d5efe04
--- /dev/null
+++ b/Runtime/Animation/AnimationState.cpp
@@ -0,0 +1,824 @@
+#include "UnityPrefix.h"
+#include "AnimationState.h"
+#include "Runtime/Math/AnimationCurve.h"
+#include "AnimationClip.h"
+#include "AnimationEvent.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+PROFILER_INFORMATION (gAddMixingTransform, "Animation.AddMixingTransform [Triggers RebuildInternalState]", kProfilerAnimation);
+PROFILER_INFORMATION (gRemoveMixingTransform, "Animation.RemoveMixingTransform [Triggers RebuildInternalState]", kProfilerAnimation);
+PROFILER_INFORMATION (gModifyAnimationClip, "Animation.ModifyAnimationClip [Triggers RebuildInternalState]", kProfilerAnimation);
+
+void AnimationState::InitializeClass ()
+{
+ AnimationClip::SetDidModifyClipCallback(DidModifyAnimationClip);
+}
+
+void AnimationState::CleanupClass ()
+{
+ AnimationClip::SetDidModifyClipCallback(NULL);
+}
+
+AnimationState::AnimationState ()
+: m_AnimationClipNode (this)
+{
+ // We use only 2 bits for m_AnimationEventState
+ // This assert guards against kAnimationEventStates running out of these bits
+ Assert(kAnimationEventState__Count <= 4);
+
+ m_Clip = NULL;
+ m_Curves = NULL;
+ m_IsClone = false;
+}
+
+AnimationState::~AnimationState ()
+{
+ m_Clip = NULL;
+ m_MixingTransforms.clear();
+ m_Name.clear();
+ CleanupCurves ();
+ m_AnimationClipNode.RemoveFromList();
+}
+
+void AnimationState::AllocateCurves(int count)
+{
+ AssertIf(m_Curves);
+ m_OwnsCurves = 1;
+
+ m_Curves = new AnimationCurveBase*[count];
+ for (int i=0;i<count;i++)
+ m_Curves[i] = NULL;
+}
+
+
+void AnimationState::SetClonedCurves(AnimationState& state)
+{
+ AssertIf(m_Curves);
+ m_OwnsCurves = 0;
+
+ m_Curves = state.m_Curves;
+// m_CurvesCount = state.m_CurvesCount;
+}
+
+
+void AnimationState::CleanupCurves ()
+{
+ if (!m_OwnsCurves || m_Curves == NULL)
+ {
+ m_Curves = NULL;
+ return;
+ }
+
+ delete[] m_Curves;
+ m_Curves = NULL;
+}
+
+void AnimationState::SetWeightTarget (float target, float length, bool stopWhenFaded)
+{
+ // TODO : this m_WeightDelta approach doesn't work very well when length is 0
+ // Current approach might lead to precision problems.
+ // The blend should happen instantly, but with very small deltaTimes it might need several frames.
+
+ AssertFiniteParameter(target);
+
+ float newWeightDelta;
+ if (length > 0.001)
+ newWeightDelta = (target - m_Weight) / length;
+ else
+ newWeightDelta = (target - m_Weight) * 100000.0F;
+
+ // If the current weight delta is going to reach the target faster than the new one, don't update it!
+ bool ignoreWeightDelta = m_FadeBlend && CompareApproximately(m_WeightTarget, target, kReallySmallWeight) && Abs(m_WeightDelta) > Abs(newWeightDelta);
+ if (!ignoreWeightDelta)
+ m_WeightDelta = newWeightDelta;
+
+ // We need to make sure that the weight delta is never zero, otherwise the stop condition in UpdateAnimationState is never reached!
+ if (CompareApproximately(m_WeightDelta, 0.0F, kReallySmallWeight))
+ m_WeightDelta = 100000.0F;
+
+ m_WeightTarget = target;
+
+ m_FadeBlend = true;
+ m_StopWhenFadedOut = stopWhenFaded;
+ m_IsFadingOut = false;
+}
+
+void AnimationState::SetWeightTargetImmediate (float target, bool stopWhenFaded)
+{
+ AssertFiniteParameter(target);
+
+ m_Weight = target;
+ m_StopWhenFadedOut = stopWhenFaded;
+ m_FadeBlend = false;
+ m_IsFadingOut = false;
+}
+
+
+///@TODO: Doesn't using the stop time conflict with using stop time for something else, like when queueing?
+void AnimationState::SetupFadeout (float length)
+{
+ m_FadeOutLength = length;
+}
+
+/*
+void AnimationState::ApplyWeightDeltaFraction ()
+{
+
+}
+*/
+/*
+void AnimationState::Delay (float time)
+{
+
+}
+*/
+
+void AnimationState::SetTime (float time)
+{
+ AssertFiniteParameter(time);
+ m_Time = time;
+ m_WrappedTime = WrapTime(time, m_CachedRange, m_WrapMode);
+ DebugAssertIf(!IsFinite(m_Time) || !IsFinite(m_WrappedTime));
+ m_AnimationEventState = kAnimationEventState_Search; // Re-search for animation event index
+}
+
+float WrapTime (float curveT, const std::pair<float, float>& range, int m_WrapMode)
+{
+ float begTime = range.first;
+ float endTime = range.second;
+
+ if (curveT >= endTime)
+ {
+ if (m_WrapMode == kRepeat)
+ curveT = Repeat (curveT, begTime, endTime);
+ else if (m_WrapMode == kClamp || m_WrapMode == kClampForever)
+ {
+ curveT = endTime;
+ }
+ else if (m_WrapMode == kPingPong)
+ curveT = PingPong (curveT, begTime, endTime);
+ }
+ else if (curveT < begTime)
+ {
+ if (m_WrapMode == kRepeat)
+ curveT = Repeat (curveT, begTime, endTime);
+ if (m_WrapMode == kClamp || m_WrapMode == kClampForever)
+ curveT = begTime;
+ else if (m_WrapMode == kPingPong)
+ curveT = PingPong (curveT, begTime, endTime);
+ }
+ return curveT;
+}
+
+namespace
+{
+ // returns -1, 0, 1 depending on sign/value of v
+ int GetDirection(float v)
+ {
+ if (v == 0) return 0;
+ else return v > 0 ? 1 : -1;
+ }
+}
+
+void AnimationState::SetSpeed (float speed)
+{
+ // When reversing speed. Recalculate animation event index
+ if (m_AnimationEventState == kAnimationEventState_PausedOnEvent)
+ {
+ // Handling special case when speed=0 and m_AnimationEventIndex is valid,
+ // bug the event that m_AnimationEventIndex is pointing to has been triggered before pause,
+ // so wee need to trigger event on the left or on the right
+ Assert(GetDirection(m_Speed) == 0);
+
+ int newSpeedDirection = GetDirection(speed);
+ if (newSpeedDirection != 0)
+ {
+ // In PinPong mode event are sometimes executed in the oposite direction of speed
+ // so we need to reverse newSpeedDirection based on that
+ if (m_WrapMode == kPingPong)
+ {
+ const float begTime = m_CachedRange.first;
+ const float endTime = m_CachedRange.second;
+
+ // Get index of how many times we are playing this back and forth
+ const int newWrapIndex = FloorfToInt((m_Time - begTime) / (endTime - begTime));
+
+ // Switch going forward based on the PingPong direction
+ if (newWrapIndex % 2 != 0)
+ newSpeedDirection = -newSpeedDirection;
+ }
+
+ m_AnimationEventState = kAnimationEventState_HasEvent;
+ m_AnimationEventIndex += newSpeedDirection > 0 ? 1 : -1;
+ }
+ }
+ else
+ {
+ const int oldSpeedDirection = GetDirection(m_Speed);
+ const int newSpeedDirection = GetDirection(speed);
+
+ // TODO : if we want a minor optimization, we could not trigger search
+ // if state is paused and then resumed in same direction, but triggering
+ // search should work just fine as long as we're not on event
+ if (oldSpeedDirection != newSpeedDirection)
+ m_AnimationEventState = kAnimationEventState_Search;
+ }
+
+ m_SyncedSpeed = m_Speed = speed;
+ AssertFiniteParameter(speed);
+
+ if (UseUnity32AnimationFixes())
+ SetupStopTime();
+}
+
+// offsetTime - time from which newWrappedTime starts (from which newWrappedTime is wrapped)
+// it is used for recalculating m_Time when event modifies playback direction
+bool AnimationState::FireEvents (const float deltaTime, float newWrappedTime, bool forward, Unity::Component& animation, const float beginTime, const float offsetTime, const bool reverseOffsetTime)
+{
+ AnimationClip::Events& events = m_Clip->GetEvents();
+
+ // Find initial event
+ if (m_AnimationEventState == kAnimationEventState_Search)
+ {
+ const float oldWrappedTime = m_WrappedTime;
+
+ if (forward)
+ {
+ AssertIf(oldWrappedTime > newWrappedTime);
+
+ for (int i=0;i<events.size();i++)
+ {
+ if (events[i].time >= oldWrappedTime)
+ {
+ m_AnimationEventIndex = i;
+ m_AnimationEventState = kAnimationEventState_HasEvent;
+ break;
+ }
+ }
+ }
+ else
+ {
+ AssertIf(oldWrappedTime < newWrappedTime);
+
+ for (int i=events.size()-1;i>=0;i--)
+ {
+ if (events[i].time <= oldWrappedTime)
+ {
+ m_AnimationEventIndex = i;
+ m_AnimationEventState = kAnimationEventState_HasEvent;
+ break;
+ }
+ }
+ }
+
+ if (m_AnimationEventState == kAnimationEventState_Search)
+ m_AnimationEventState = kAnimationEventState_NotFound;
+ }
+
+ const float oldSyncedSpeed = m_SyncedSpeed;
+ const float oldWrappedTime = m_WrappedTime;
+
+ while (true)
+ {
+ if (m_AnimationEventIndex < 0 || m_AnimationEventIndex >= events.size())
+ break;
+
+ float eventTime = events[m_AnimationEventIndex].time;
+
+ if (forward && eventTime > newWrappedTime)
+ break;
+ if (!forward && eventTime < newWrappedTime)
+ break;
+
+ const int currentAnimationEventIndex = m_AnimationEventIndex;
+
+ FireEvent (events[m_AnimationEventIndex], this, animation);
+ DebugAssertIf(!IsFinite(m_Time));
+ DebugAssertIf(!IsFinite(m_WrappedTime));
+
+ if (m_AnimationEventState == kAnimationEventState_Search)
+ {
+ const int oldSpeedDirection = GetDirection(oldSyncedSpeed);
+ const int newSpeedDirection = GetDirection(m_SyncedSpeed);
+
+ // handling special case when speed direction was changed
+ // we want to continue time from eventTime and events from next event (we do not want to trigger this event immediately again)
+ if (oldSpeedDirection != newSpeedDirection)
+ {
+ // do not modify m_WrappedTime if it has been modified inside of event
+ if (m_WrappedTime == oldWrappedTime)
+ {
+ const float timeDelta = eventTime - beginTime;
+ float newTime = offsetTime + (reverseOffsetTime ? -timeDelta : timeDelta);
+ AssertMsg(Abs(newTime - m_Time) <= deltaTime + std::numeric_limits<float>::epsilon(), "Abs(%f - %f) <= %f\n%f <= %f\n%e <= 0", newTime, m_Time, deltaTime, Abs(newTime - m_Time), deltaTime, Abs(newTime - m_Time) - deltaTime);
+ m_Time = newTime;
+
+ m_WrappedTime = eventTime;
+
+ if (newSpeedDirection == 0)
+ {
+ // if time is paused we need to continue from "right" or "left" event
+ // after time is unpaused, but we will know next event only when time is unpaused,
+ // so we set AnimationEventState to PausedOnEvent.
+ // We can just set state to Searh, because it would find and trigger same event
+ m_AnimationEventIndex = currentAnimationEventIndex;
+ m_AnimationEventState = kAnimationEventState_PausedOnEvent;
+ }
+ else
+ {
+ // if time is reversed we need to continue from "right" or "left" event
+ m_AnimationEventIndex = currentAnimationEventIndex + (!forward ? 1 : -1);
+ m_AnimationEventState = kAnimationEventState_HasEvent;
+ }
+ }
+ }
+
+ DebugAssertIf(!IsFinite(m_Time) || !IsFinite(m_WrappedTime));
+ return false;
+ }
+
+ if (forward)
+ m_AnimationEventIndex++;
+ else
+ m_AnimationEventIndex--;
+ }
+
+ return true;
+}
+
+
+bool AnimationState::UpdateAnimationState (double globalTime, Unity::Component& animationComponent)
+{
+ DebugAssertIf(!IsFinite(m_Time) || !IsFinite(m_WrappedTime));
+
+ // Update time
+ const float deltaTime = globalTime - m_LastGlobalTime;
+ m_LastGlobalTime = globalTime;
+
+ //deltaTime = 0.166667f;
+
+ //LogString(Format("globalTime: %f; deltaTime: %f; m_Time: %f", globalTime, deltaTime, m_Time));
+
+ float syncedSpeedDeltaTime = deltaTime * m_SyncedSpeed;
+ // Do not trigger events if time is stopped
+ if (syncedSpeedDeltaTime != 0)
+ {
+ double lastTime = m_Time;
+ m_Time += syncedSpeedDeltaTime;
+ // if syncedSpeedDeltaTime is big it might cross whole loop,
+ // so we would have to fire all events in that loop, but we ignore cases like this for now...
+ float newWrappedTime = m_WrappedTime + syncedSpeedDeltaTime;
+ const float oldWrappedTime = m_WrappedTime;
+
+
+ /// Wrap time and Fire Animation Events
+ float begTime = m_CachedRange.first;
+ float endTime = m_CachedRange.second;
+
+ const bool forward = m_SyncedSpeed >= 0.0F;
+
+ ///@TODO: How should repeat behave when the first key is not at zero???
+ /// - Animations should always start playback at zero and also go back to zero, regardless of where the first frame is. (Rune)
+ ///@TODO: How do we stop the animation when we play backwards?
+ /// - The animation should stop when it has reached zero. See functional test AnimationOnceSamplesAtEndAndResetsTimeAndGetsDisabled (Rune)
+ ///@TODO: Repeat currently doesnt start at the first key frame it might enter anywhere depending on begin / end Time (Repeat (m_Time, begTime, endTime);)
+ /// - The animation should not go back to the exact start of the range when looping, but rather subtract the range length. (Rune)
+ /// - However, the range should always start at zero, not at the first key. (Rune)
+
+ // Repeat
+ if (m_WrapMode == kRepeat)
+ {
+ // Reached end of time range - wrap around and fire animation events
+ if (newWrappedTime >= endTime)
+ {
+ newWrappedTime = RepeatD (m_Time, begTime, endTime);
+ if (m_HasAnimationEvent)
+ {
+ float offsetTime = m_Time - (newWrappedTime - begTime) - (endTime - begTime);
+ if (FireEvents (deltaTime, endTime, forward, animationComponent, begTime, offsetTime, false))
+ {
+ offsetTime += (endTime - begTime);
+ m_AnimationEventIndex = 0;
+ m_AnimationEventState = kAnimationEventState_HasEvent;
+ FireEvents (deltaTime, newWrappedTime, forward, animationComponent, begTime, offsetTime, false);
+ }
+ }
+ }
+ else if (newWrappedTime < begTime)
+ {
+ newWrappedTime = RepeatD (m_Time, begTime, endTime);
+ if (m_HasAnimationEvent)
+ {
+ float offsetTime = m_Time + (endTime - newWrappedTime);
+ if (FireEvents (deltaTime, begTime, forward, animationComponent, begTime, offsetTime, false))
+ {
+ offsetTime -= (endTime - begTime);
+ m_AnimationEventIndex = m_Clip->GetEvents().size() - 1;
+ m_AnimationEventState = kAnimationEventState_HasEvent;
+ FireEvents (deltaTime, newWrappedTime, forward, animationComponent, begTime, offsetTime, false);
+ }
+ }
+ }
+ // Inside of begin / end time range -> Fire animation event only
+ else if (m_HasAnimationEvent)
+ {
+ const float offsetTime = m_Time - (newWrappedTime - begTime);
+ FireEvents (deltaTime, newWrappedTime, forward, animationComponent, begTime, offsetTime, false);
+ }
+
+
+ // It's important to used RepeatD, because we get an assert otherwise (for exmaple: 1.9999999 double is rounded to 2.0f, which results in 0 when wrapped)
+ DebugAssertIf(m_WrappedTime == oldWrappedTime && !CompareApproximately(newWrappedTime, RepeatD (m_Time, begTime, endTime), 0.01F));
+ }
+ // Clamp
+ else if (m_WrapMode == kClamp || m_WrapMode == kClampForever)
+ {
+ if (m_Time < begTime)
+ newWrappedTime = begTime;
+ else if (m_Time > endTime)
+ newWrappedTime = endTime;
+ else
+ newWrappedTime = m_Time;
+
+ if (m_HasAnimationEvent)
+ {
+ FireEvents (deltaTime, m_Time, forward, animationComponent, begTime, begTime, false);
+ }
+ }
+ // Default
+ else if (m_WrapMode == kDefaultWrapMode)
+ {
+ if (m_HasAnimationEvent)
+ {
+ FireEvents (deltaTime, newWrappedTime, forward, animationComponent, begTime, begTime, false);
+ }
+ }
+ // Ping Pong
+ else if (m_WrapMode == kPingPong)
+ {
+ newWrappedTime = PingPong(m_Time, begTime, endTime);
+
+ if (m_HasAnimationEvent)
+ {
+ const AnimationClip::Events& events = m_Clip->GetEvents();
+
+ AssertIf(Abs(endTime - begTime) < std::numeric_limits<float>::epsilon());
+
+ // Get index of how many times we are playing this back and forth
+ int wrapIndex = FloorfToInt((lastTime - begTime) / (endTime - begTime));
+ int newWrapIndex = FloorfToInt((m_Time - begTime) / (endTime - begTime));
+
+ bool forwardPlayback = m_SyncedSpeed >= 0.0F;
+
+ // Switch going forward based on the pingpong direction
+ if (newWrapIndex % 2 != 0)
+ forwardPlayback = !forwardPlayback;
+
+ // Inside of begin / end boundary
+ if (wrapIndex == newWrapIndex)
+ {
+ const float offsetTime = m_Time - (newWrappedTime - begTime);
+ FireEvents (deltaTime, newWrappedTime, forwardPlayback, animationComponent, begTime, offsetTime, false);
+ }
+ // Crossing boundary
+ else
+ {
+ if (forwardPlayback)
+ {
+ float offsetTime = forward ?
+ m_Time - (newWrappedTime - begTime) :
+ m_Time + (newWrappedTime - begTime);
+ if (FireEvents (deltaTime, begTime, false, animationComponent, begTime, offsetTime, forward))
+ {
+ // if event is right at the begin time - we want to play it once, otherwise twice
+ // we might get m_AnimationEventIndex=events.size() in this statement, but that's acceptable situation
+ // it indicates that there are no events until we reverse the time
+ m_AnimationEventIndex = (events.front().time == begTime ? 1 : 0);
+ m_AnimationEventState = kAnimationEventState_HasEvent;
+ // Events should never be searched in next call of FireEvents
+ FireEvents (deltaTime, newWrappedTime, true, animationComponent, endTime, offsetTime, !forward);
+ }
+ }
+ else
+ {
+ float offsetTime = forward ?
+ m_Time - (endTime - newWrappedTime) :
+ m_Time + (endTime - newWrappedTime);
+ if (FireEvents (deltaTime, endTime, true, animationComponent, endTime, offsetTime, !forward))
+ {
+ // if event is right at the end time - we want to play it once, otherwise twice
+ // we might get m_AnimationEventIndex=-1 in this statement, but that's acceptable situation
+ // it indicates that there are no events until we reverse the time
+ m_AnimationEventIndex = events.size() - (events.back().time == endTime ? 2 : 1);
+ m_AnimationEventState = kAnimationEventState_HasEvent;
+ // Events should never be searched in next call of FireEvents
+ FireEvents (deltaTime, newWrappedTime, false, animationComponent, begTime, offsetTime, forward);
+ }
+ }
+ }
+ }
+ }
+ else
+ ErrorString("Unknown wrapMode");
+
+ DebugAssertIf(!IsFinite(newWrappedTime));
+
+ // do not set m_WrappedTime if it was altered in one of events
+ if (m_WrappedTime == oldWrappedTime)
+ m_WrappedTime = newWrappedTime;
+ DebugAssertIf(!IsFinite(m_Time) || !IsFinite(m_WrappedTime));
+ }
+
+ return UseUnity32AnimationFixes() ? UpdateFading(deltaTime) : UpdateFading_Before32(deltaTime);
+}
+
+// This is for backwards compatibility. If you need to make changes,
+// then make them in UpdateFading which is used with Unity 3.2 and later content
+bool AnimationState::UpdateFading_Before32(float deltaTime)
+{
+ // We are now fading out!
+ if (m_Time > m_StopTime - m_FadeOutLength && !m_IsFadingOut)
+ {
+ SetWeightTarget(0.0F, m_FadeOutLength, true);
+ m_IsFadingOut = true;
+
+ ///@TODO: Apply fractional delta based on current time!
+ // A fadeout should have been started at m_StopTime - m_FadeOutLength but we exceeded it by some time
+ // We should apply the fraction by what we exceeded it to the weight!
+ // ApplyWeightDeltaFraction(m_Time - m_FadeOutLength);
+ }
+
+ bool didStopAtEnd = false;
+ // Update blend target
+ if (m_FadeBlend)
+ didStopAtEnd = UpdateBlendingWeight(deltaTime, false);
+
+ return didStopAtEnd;
+}
+
+bool AnimationState::UpdateFading(float deltaTime)
+{
+ bool didStopAtEnd = false;
+
+ // We are now fading out!
+ if (!m_IsFadingOut && UseStopTime())
+ {
+ Assert(m_FadeOutLength >= 0);
+
+ const bool forward = m_Speed >= 0;
+ const float dt = forward ? m_Time - (m_StopTime - m_FadeOutLength) : (m_StopTime + m_FadeOutLength) - m_Time;
+ if (dt > 0)
+ {
+ SetWeightTarget(0.0F, m_FadeOutLength, true);
+ m_IsFadingOut = true;
+
+ if (UseUnity35AnimationFixes())
+ {
+ // Applying fractional delta based on current time!
+ // A fadeout has been started at m_StopTime - m_FadeOutLength but we exceeded it by some time,
+ // so we need to apply delta on m_Weight
+ didStopAtEnd = UpdateBlendingWeight(dt, m_FadeOutLength == 0);
+ }
+ }
+ }
+
+ // Update blend target
+ if (m_FadeBlend)
+ didStopAtEnd = UpdateBlendingWeight(deltaTime, false);
+
+ return didStopAtEnd;
+}
+
+bool AnimationState::UpdateBlendingWeight(const float deltaTime, const bool instantBlend)
+{
+ bool didStopAtEnd = false;
+
+ m_Weight += deltaTime * m_WeightDelta;
+
+ // Stop blending and clamp when we reach the target
+ if (instantBlend ||
+ (m_WeightDelta > 0.0F && m_Weight > m_WeightTarget) ||
+ (m_WeightDelta <= 0.0F && m_Weight < m_WeightTarget))
+ {
+ m_Weight = m_WeightTarget;
+ m_FadeBlend = 0;
+ m_IsFadingOut = 0;
+ if (m_StopWhenFadedOut)
+ {
+ m_UnstoppedLastWrappedTime = m_WrappedTime;
+ Stop();
+ didStopAtEnd = true;
+ }
+ }
+ else
+ {
+ #if !UNITY_RELEASE
+ AssertMsg((m_WrapMode != kDefaultWrapMode && m_WrapMode != kClamp) || m_Time < GetLength() + 0.05F, "Time is out of range: %f < %f", m_Time, GetLength());
+ #endif
+ }
+
+ return didStopAtEnd;
+}
+
+void AnimationState::DidModifyAnimationClip (AnimationClip* clip, AnimationStateList& states)
+{
+ AnimationStateList::iterator i;
+ for (i=states.begin();i!=states.end();i++)
+ {
+ AnimationState& state = **i;
+ if (clip == NULL)
+ {
+ state.m_Clip = NULL;
+ state.m_HasAnimationEvent = 0;
+ }
+ else
+ {
+ AssertIf (state.m_Clip != clip);
+ state.m_CachedRange = state.m_Clip->GetRange();
+ AssertIf(!IsFinite(state.m_CachedRange.first) || !IsFinite(state.m_CachedRange.second));
+ state.m_HasAnimationEvent = !state.m_Clip->GetEvents().empty();
+ }
+
+ PROFILER_AUTO(gModifyAnimationClip, NULL)
+
+ state.m_DirtyMask |= kRebindDirtyMask;
+ }
+
+ if (clip == NULL)
+ states.clear();
+}
+
+///@TODO: Import pipeline should allow reimporting clips while in playmode. For this we just need to make them reuse the animation clip asset
+/// and call DidChangeClip
+
+void AnimationState::Init(const UnityStr& name, AnimationClip* clip, double globalTime, int wrap, bool isClone )
+{
+ AssertIf (m_Clip);
+ AssertIf (m_Curves);
+ AssertIf (m_AnimationClipNode.IsInList());
+
+ m_IsClone = isClone;
+ m_Clip = clip;
+ m_HasAnimationEvent = 0;
+ if (m_Clip)
+ {
+ m_CachedRange = m_Clip->GetRange();
+ AssertIf(!IsFinite(m_CachedRange.first) || !IsFinite(m_CachedRange.second));
+ m_Clip->AddAnimationState(m_AnimationClipNode);
+ m_Name = name;
+ m_HasAnimationEvent = !m_Clip->GetEvents().empty();
+ }
+
+ m_BlendMode = kBlend;
+ m_Weight = 0.0F;
+ m_FadeBlend = 0;
+ m_StopWhenFadedOut = 0;
+ m_IsFadingOut = 0;
+ m_AutoCleanup = 0;
+ m_ShouldCleanup = 0;
+ m_FadeOutLength = 0.0F;
+ m_WrappedTime = 0.0F;
+ m_AnimationEventIndex = -1;
+ m_AnimationEventState = kAnimationEventState_Search;
+ m_Time = 0;
+// m_WeightDelta = 0.0F;
+// m_UnstoppedLastWeight = 0.0F;
+// m_UnstoppedLastWrappedTime = 0.0F;
+// m_WeightTarget = 1.0F;
+// m_WeightDelta = 0.0F;
+ m_LastGlobalTime = globalTime;
+
+ m_Layer = 0;
+ m_SyncedSpeed = m_Speed = 1.0F;
+ m_Enabled = false;
+
+ SetWrapMode(wrap);
+ SetTime(0.0F);
+
+// m_GlobalStopTime = std::numeric_limits<float>::infinity();
+
+ m_DirtyMask = kRebindDirtyMask | kLayersDirtyMask;
+}
+
+void AnimationState::SetupStopTime()
+{
+ bool forward = UseUnity32AnimationFixes() ? m_Speed >= 0 : true;
+
+ m_StopTime = UseStopTime() ?
+ (forward ? m_CachedRange.second : m_CachedRange.first) :
+ (forward ? std::numeric_limits<float>::infinity() : -std::numeric_limits<float>::infinity());
+}
+
+void AnimationState::SetWrapMode (int wrap)
+{
+ m_WrapMode = wrap;
+
+ // we need to update m_WrappedTime, because we can set time first and then wrap mode
+ m_WrappedTime = WrapTime(m_Time, m_CachedRange, m_WrapMode);
+ DebugAssertIf(!IsFinite(m_Time) || !IsFinite(m_WrappedTime));
+
+ SetupStopTime();
+}
+
+void AnimationState::SetEnabled (bool enabled)
+{
+ if (enabled && !m_Enabled)
+ m_LastGlobalTime = GetCurTime();
+ m_Enabled = enabled;
+}
+
+void AnimationState::AddMixingTransform(Transform& transform, bool recursive)
+{
+ m_MixingTransforms.insert(std::make_pair(PPtr<Transform> (&transform), recursive));
+ m_DirtyMask |= kRebindDirtyMask;
+
+ PROFILER_AUTO(gAddMixingTransform, NULL)
+}
+
+void AnimationState::RemoveMixingTransform(Transform& transform)
+{
+ MixingTransforms::iterator it = m_MixingTransforms.find(PPtr<Transform>(&transform));
+ if (it != m_MixingTransforms.end())
+ m_MixingTransforms.erase(it);
+ else
+ {
+ ErrorStringMsg("RemoveMixingTransform couldn't find transform '%s' in a list of mixing transforms. "
+ "You can only remove transforms that have been added through AddMixingTransform", transform.GetName());
+ }
+ m_DirtyMask |= kRebindDirtyMask;
+
+ PROFILER_AUTO(gRemoveMixingTransform, NULL)
+}
+
+
+// TODO : this looks a bit expensive: it's iterating hierarchy every time
+bool AnimationState::ShouldMixTransform (Transform& transform)
+{
+ if (m_MixingTransforms.empty())
+ return true;
+
+ for (MixingTransforms::iterator i=m_MixingTransforms.begin();i != m_MixingTransforms.end();i++)
+ {
+ if (i->second)
+ {
+ Transform* root = i->first;
+ if (root && IsChildOrSameTransform(transform, *root))
+ return true;
+ }
+ else
+ {
+ if (i->first == PPtr<Transform> (&transform))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void AnimationState::Stop ()
+{
+ if ( m_Enabled && m_AutoCleanup )
+ m_ShouldCleanup = true;
+
+ m_Enabled = false;
+ SetTime(0.0F);
+ m_FadeBlend = 0;
+ m_StopWhenFadedOut = 0;
+}
+
+void AnimationState::SetupUnstoppedState()
+{
+ std::swap(m_WrappedTime, m_UnstoppedLastWrappedTime);
+ m_UnstoppedLastWeight = m_Weight;
+ // HACK: VERY ugly way of making sure the last frame of an animation gets accounted into the final position. was 1.0f but this
+ // gave problems when crossfading since the state that should no longer contribute, suddently had a weight of 1 for one frame
+ // We need to redo some of the logic in here at some point
+ m_Weight = kReallySmallWeight*1.001f;
+ m_Enabled = true;
+}
+
+void AnimationState::CleanupUnstoppedState()
+{
+ std::swap(m_WrappedTime, m_UnstoppedLastWrappedTime);
+ m_Weight = m_UnstoppedLastWeight;
+ m_Enabled = false;
+}
+
+bool AnimationState::UseUnity32AnimationFixes()
+{
+ return IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_2_a1);
+}
+
+bool AnimationState::UseUnity34AnimationFixes()
+{
+ return IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_4_a1);
+}
+
+bool AnimationState::UseUnity35AnimationFixes()
+{
+ return IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_5_a1);
+}
+
diff --git a/Runtime/Animation/AnimationState.h b/Runtime/Animation/AnimationState.h
new file mode 100644
index 0000000..c9c92ee
--- /dev/null
+++ b/Runtime/Animation/AnimationState.h
@@ -0,0 +1,269 @@
+#pragma once
+#include <vector>
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/BaseClasses/RefCounted.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Math/AnimationCurve.h"
+
+template<class T> class AnimationCurveTpl; typedef AnimationCurveTpl<float> AnimationCurve;
+template<class T> class AnimationCurveTpl; typedef AnimationCurveTpl<float> AnimationCurveBase;
+class AnimationClip;
+class Transform;
+class AABB;
+namespace Unity { class Component; }
+
+#define GET_SET_REF(a,b,c) void Set##b (a& val) { c = val; } a& Get##b () const {return c; }
+
+#define kReallySmallWeight 0.0001F
+#define kReallySmallFadeTime 0.001F
+
+enum {
+ kRebindDirtyMask = 1 << 0,
+ kLayersDirtyMask = 1 << 1
+};
+
+
+class AnimationState : public TrackedReferenceBase
+{
+private:
+ // Indicates state of m_AnimationEventIndex
+ enum {
+ // m_AnimationEventIndex is valid
+ kAnimationEventState_HasEvent = 0,
+ // m_AnimationEventIndex need to be researched
+ kAnimationEventState_Search,
+ // m_AnimationEventIndex there are no more keys available, no need to search
+ kAnimationEventState_NotFound,
+ // This is very special case:
+ // m_AnimationEventIndex points to a valid index and AnimationState is paused,
+ // but this event has been triggered already, so when AnimationState state is unpaused
+ // we have to continue triggering event on left or right side of this event
+ // (depending on the sign of AnimationState speed)
+ kAnimationEventState_PausedOnEvent,
+
+ // Used for assert
+ kAnimationEventState__Count
+ };
+
+public:
+
+ enum { kBlend = 0, kAdditive };
+
+ AnimationState ();
+ ~AnimationState ();
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ /// Makes the animation state reach a /target/ blend weight in length seconds.
+ /// If the animation is already fading towards target and it would reach the target
+ /// faster, then the weight speed will not be modified.
+ /// if stopWhenFaded is enabled, the animation will stop when the target is reached.
+ void SetWeightTarget (float target, float length, bool stopWhenFaded);
+
+ void SetWeightTargetImmediate (float target, bool stopWhenFaded);
+
+ void Stop ();
+
+ // This automatically sets m_LastGlobalTime from globa
+ void SetEnabled (bool enabled);
+
+ bool GetEnabled () const { return m_Enabled; }
+
+ void SetTime (float time);
+ const float GetTime() const { return m_Time; }
+
+ int GetWrapMode () const { return m_WrapMode; }
+ void SetWrapMode (int mode);
+
+ GET_SET_REF(const UnityStr, Name, m_Name)
+ GET_SET_REF(const UnityStr, ParentName, m_ParentName)
+
+ void SetLayer (int layer) { m_Layer = layer; m_DirtyMask |= kLayersDirtyMask; }
+ int GetLayer () const { return m_Layer; }
+
+ void SetWeight (float val) { m_Weight = val; }
+ float GetWeight () const { return m_Weight; }
+
+ void SetNormalizedSpeed (float speed) { m_SyncedSpeed = m_Speed = speed * GetLength(); }
+ float GetNormalizedSpeed () const { return m_Speed / GetLength(); }
+
+ void SetSpeed (float speed);
+ float GetSpeed () const { return m_Speed; }
+ float GetSyncedSpeed () const { return m_SyncedSpeed; }
+
+ void SetNormalizedTime (float time) { SetTime( time * GetLength()); }
+ float GetNormalizedTime () const { return m_Time / GetLength(); }
+
+ float GetLength () const { return m_CachedRange.second; }
+
+ /// Sets the fadeout length and stop time based on the used wrapmode.
+ void SetupFadeout (float length);
+
+ int GetBlendMode() const { return m_BlendMode; }
+ void SetBlendMode(int mode) { m_BlendMode = mode; }
+
+ AnimationClip* GetClip() { return m_Clip; }
+
+ void ClearDirtyMask() { m_DirtyMask = 0; }
+ UInt32 GetDirtyMask () const { return m_DirtyMask; }
+
+ // Returns true if the state is enabled and has a reasonably high weight
+ inline bool ShouldUse() const;
+
+ // Used for layer syncing
+ void SetNormalizedSyncedSpeed (float speed) { m_SyncedSpeed = speed * GetLength(); }
+
+ bool UpdateAnimationState (double globalTime, Unity::Component& animation);
+
+ void Init(const UnityStr& name, AnimationClip* clip, double globalTime, int wrap, bool isClone = false);
+
+ void CleanupCurves ();
+ void SetAutoCleanup(){ m_AutoCleanup = 1; }
+ void ForceAutoCleanup() { SetAutoCleanup(); m_ShouldCleanup = true; }
+ bool ShouldAutoCleanupNow () { return m_ShouldCleanup; }
+ bool IsClone() { return m_IsClone; }
+
+ typedef AnimationCurveBase** Curves;
+ Curves GetCurves() { return m_Curves; }
+ Curves const GetCurves() const { return m_Curves; }
+
+ void AllocateCurves(int count);
+ void SetClonedCurves(AnimationState& state);
+
+ bool ShouldMixTransform (Transform& transform);
+ void AddMixingTransform(Transform& transform, bool recursive);
+ void RemoveMixingTransform(Transform& transform);
+
+ /// When an animation is stopped and it is the only animation playing.
+ /// Then you usually want the last frame to display before the animation stops.
+ /// For example an elevator moving up. It should always make sure the last frame gets sampled.
+ /// -> Now unfortunately we stop time during UpdateAnimationState which means the time gets reset
+ /// -> Which wrapped time and weight will be set to zero when actually sampling the animation.
+ /// -> So we just fix the very specific, single animation playing and stopping case,
+ /// by storing the wrap mode prior to animation stop and then afterwards reverting it again.
+ /// *** I guess the right way to solve this would be to make animation stopping happen after sampling or something...
+ void SetupUnstoppedState ();
+ void CleanupUnstoppedState ();
+
+ bool FireEvents (const float deltaTime, float newWrappedTime, bool reverse, Unity::Component& animation, const float beginTime, const float offsetTime, const bool reverseOffsetTime);
+
+ // We made a bunch of fixes in Unity 3.2, but we couldn't use them since it breaks backwards compatibility,
+ // so we enable the fixes only for 3.2 content
+ // TODO : get rid of this function and all related backwards compatible functions as soon as we are allowed to break backwards compatibility
+ static bool UseUnity32AnimationFixes();
+
+ // Same story (see above) with animation fixes in Unity 3.4
+ static bool UseUnity34AnimationFixes();
+
+ // Same story (see above) with animation fixes in Unity 3.5
+ static bool UseUnity35AnimationFixes();
+
+private:
+ bool UseStopTime() const { return m_WrapMode == kClamp || m_WrapMode == kDefaultWrapMode; }
+ void SetupStopTime();
+
+ typedef List< ListNode<AnimationState> > AnimationStateList;
+ static void DidModifyAnimationClip (AnimationClip* clip, AnimationStateList& states);
+
+ bool UpdateFading_Before32(float deltaTime);
+ bool UpdateFading(float deltaTime);
+
+ bool UpdateBlendingWeight(const float deltaTime, const bool instantBlend);
+
+private:
+ Curves m_Curves;
+// int m_CurvesCount;
+
+ float m_Weight;
+ float m_WrappedTime; // Always keep in animation clip length range
+
+ double m_Time; // Keeps on increasing forever -> higher precision
+ double m_LastGlobalTime; // Keeps on increasing forever -> higher precision
+
+ int m_Layer;
+ float m_Speed;
+ float m_SyncedSpeed;
+
+ float m_StopTime;
+
+ float m_FadeOutLength;
+ float m_WeightTarget;
+
+ UInt32 m_FadeBlend : 1;
+ UInt32 m_Enabled : 1;
+ UInt32 m_StopWhenFadedOut : 1;
+ UInt32 m_AutoCleanup : 1;
+ UInt32 m_OwnsCurves : 1;
+ UInt32 m_IsFadingOut : 1;
+ UInt32 m_ShouldCleanup : 1;
+ UInt32 m_HasAnimationEvent : 1;
+ UInt32 m_IsClone : 1;
+ // make sure that there are enough bits to store kAnimationEventStates
+ UInt32 m_AnimationEventState : 2;
+ int m_AnimationEventIndex;
+
+ UInt32 m_DirtyMask;
+
+ int m_WrapMode; ///< enum { Default = 0, Once = 1, Loop = 2, PingPong = 4, ClampForever = 8 }
+ int m_BlendMode; ///< enum { Blend = 0, Additive = 1 }
+
+ float m_WeightDelta;
+
+ ///@TODO: FIXME HACKED Time stopping
+ float m_UnstoppedLastWrappedTime;
+ float m_UnstoppedLastWeight;
+
+ std::pair<float, float> m_CachedRange;
+
+ AnimationClip* m_Clip;
+ ListNode<AnimationState> m_AnimationClipNode;
+
+ UnityStr m_Name;
+ UnityStr m_ParentName;
+
+ typedef std::map<PPtr<Transform>, bool> MixingTransforms;
+ MixingTransforms m_MixingTransforms;
+
+ friend class Animation;
+};
+
+float WrapTime (float time, const std::pair<float, float>& range, int m_WrapMode);
+
+inline bool AnimationState::ShouldUse() const
+{
+ return m_Clip && m_Enabled && m_Weight > kReallySmallWeight;
+}
+
+// For debugging purposes display some of the animation state information!
+#if UNITY_EDITOR
+#include "Runtime/Serialize/SerializeTraits.h"
+template<>
+class SerializeTraits<AnimationState*> : public SerializeTraitsBase<AnimationState*>
+{
+ public:
+
+ typedef AnimationState* value_type;
+ inline static const char* GetTypeString (void*) { return "AnimationState"; }
+ inline static bool IsAnimationChannel () { return false; }
+ inline static bool MightContainPPtr () { return true; }
+ inline static bool AllowTransferOptimization () { return false; }
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ TRANSFER_PROPERTY_DEBUG(UnityStr, m_Name, data->GetName)
+ TRANSFER_PROPERTY_DEBUG(bool, m_Enabled, data->GetEnabled)
+ transfer.Align();
+ TRANSFER_PROPERTY_DEBUG(float, m_Weight, data->GetWeight)
+ TRANSFER_PROPERTY_DEBUG(float, m_Time, data->GetTime)
+ TRANSFER_PROPERTY_DEBUG(float, m_Speed, data->GetSpeed)
+ TRANSFER_PROPERTY_DEBUG(float, m_SyncedSpeed, data->GetSyncedSpeed)
+ TRANSFER_PROPERTY_DEBUG(int, m_WrapMode, data->GetWrapMode)
+ TRANSFER_PROPERTY_DEBUG(int, m_BlendMode, data->GetBlendMode)
+ TRANSFER_PROPERTY_DEBUG(PPtr<AnimationClip>, m_Clip, data->GetClip)
+ }
+};
+
+#endif
diff --git a/Runtime/Animation/AnimationStateNetworkProvider.cpp b/Runtime/Animation/AnimationStateNetworkProvider.cpp
new file mode 100644
index 0000000..3393736
--- /dev/null
+++ b/Runtime/Animation/AnimationStateNetworkProvider.cpp
@@ -0,0 +1,51 @@
+#include "UnityPrefix.h"
+#include "AnimationStateNetworkProvider.h"
+#include "Runtime/Interfaces/IAnimationStateNetworkProvider.h"
+#include "AnimationState.h"
+#include "Animation.h"
+
+class AnimationStateNetworkProvider : public IAnimationStateNetworkProvider
+{
+public:
+
+ virtual int GetNetworkAnimationStateCount (Animation& animation)
+ {
+ return animation.GetAnimationStateCount();
+ }
+
+ virtual void GetNetworkAnimationState (Animation& animation, AnimationStateForNetwork* output, int count)
+ {
+ for (int i=0;i<count;i++)
+ {
+ AnimationState& state = animation.GetAnimationStateAtIndex(i);
+
+ output[i].enabled = state.GetEnabled ();
+ output[i].weight = state.GetWeight ();
+ output[i].time = state.GetTime ();
+ }
+ }
+
+ virtual void SetNetworkAnimationState (Animation& animation, const AnimationStateForNetwork* serialize, int count)
+ {
+ for (int i=0;i<count;i++)
+ {
+ AnimationState& state = animation.GetAnimationStateAtIndex(i);
+
+ state.SetEnabled (serialize[i].enabled);
+ state.SetWeight (serialize[i].weight);
+ state.SetTime (serialize[i].time);
+ }
+ }
+};
+
+void InitializeAnimationStateNetworkProvider ()
+{
+ SetIAnimationStateNetworkProvider(UNITY_NEW_AS_ROOT(AnimationStateNetworkProvider, kMemPhysics, "AnimationStateNetworkInterface", ""));
+}
+
+void CleanupAnimationStateNetworkProvider ()
+{
+ AnimationStateNetworkProvider* module = reinterpret_cast<AnimationStateNetworkProvider*> (GetIAnimationStateNetworkProvider ());
+ UNITY_DELETE(module, kMemPhysics);
+ SetIAnimationStateNetworkProvider(NULL);
+} \ No newline at end of file
diff --git a/Runtime/Animation/AnimationStateNetworkProvider.h b/Runtime/Animation/AnimationStateNetworkProvider.h
new file mode 100644
index 0000000..e7abd05
--- /dev/null
+++ b/Runtime/Animation/AnimationStateNetworkProvider.h
@@ -0,0 +1,6 @@
+#pragma once
+
+class Animation;
+
+void InitializeAnimationStateNetworkProvider ();
+void CleanupAnimationStateNetworkProvider(); \ No newline at end of file
diff --git a/Runtime/Animation/AnimationUtility.cpp b/Runtime/Animation/AnimationUtility.cpp
new file mode 100644
index 0000000..4ae7dc0
--- /dev/null
+++ b/Runtime/Animation/AnimationUtility.cpp
@@ -0,0 +1,271 @@
+#include "UnityPrefix.h"
+
+#include "AnimationUtility.h"
+#include "AnimationClip.h"
+#include "BoundCurveDeprecated.h"
+#include "Animator.h"
+#include "AnimationState.h"
+#include "AnimationBinder.h"
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Graphics/Transform.h"
+
+#include "Runtime/BaseClasses/IsPlaying.h"
+
+#include "Runtime/mecanim/generic/crc32.h"
+#include "Runtime/mecanim/skeleton/skeleton.h"
+
+#include "Runtime/Misc/GameObjectUtility.h"
+
+PROFILER_INFORMATION (gSampleAnimationClip, "GameObject.SampleAnimation", kProfilerAnimation);
+
+void SampleEulerHint (Transform& transform, AnimationClip& clip, float time)
+{
+ // Sample euler hint curves, only in edit mode
+ // This is necessary for euler angles to look sensible when sampling animations
+#if UNITY_EDITOR
+ if (!IsWorldPlaying())
+ {
+ AnimationClip::FloatCurves& eulerCurves = clip.GetEulerEditorCurves();
+ for (AnimationClip::FloatCurves::iterator i=eulerCurves.begin();i != eulerCurves.end();i++)
+ {
+ if (!i->curve.IsValid ())
+ continue;
+
+ // Lookup without path
+ Transform* child = &transform;
+ if (!i->path.empty())
+ {
+ child = FindRelativeTransformWithPath(*child, i->path.c_str());
+ if (child == NULL)
+ continue;
+ }
+
+ if (!i->attribute.empty())
+ {
+ int c = i->attribute[i->attribute.size()-1] - 'x';
+ c = clamp(c, 0, 2);
+ float value = i->curve.EvaluateClamp(time);
+ child->m_LocalEulerAnglesHint[c] = value;
+ }
+ }
+ }
+#endif
+}
+
+
+void SampleAnimation (Unity::GameObject& go, AnimationClip& clip, float inTime, int wrapMode)
+{
+ PROFILER_AUTO(gSampleAnimationClip, &go)
+
+ AnimationClip::QuaternionCurves& rot = clip.GetRotationCurves();
+ AnimationClip::Vector3Curves& pos = clip.GetPositionCurves();
+ AnimationClip::Vector3Curves& scale = clip.GetScaleCurves();
+ AnimationClip::FloatCurves& floats = clip.GetFloatCurves();
+
+ CurveID curveID;
+ BoundCurveDeprecated bind;
+
+ float time = WrapTime(inTime, clip.GetRange(), wrapMode);
+
+ AnimationBinder& binder = GetAnimationBinder();
+ Transform& transform = go.GetComponent(Transform);
+
+ Animator* animator = go.QueryComponent(Animator);
+ if (animator)
+ {
+ if (animator->Sample (clip, inTime))
+ {
+ SampleEulerHint (transform, clip, time);
+ return;
+ }
+ }
+
+ // Sample rotations
+ for (AnimationClip::QuaternionCurves::iterator i=rot.begin();i != rot.end();i++)
+ {
+ AnimationCurveQuat& curve = i->curve;
+ if (!curve.IsValid ())
+ continue;
+
+ curveID = CurveID (i->path.c_str(), ClassID(Transform), NULL, "m_LocalRotation", 0);
+ if (binder.BindCurve(curveID, bind, transform))
+ {
+ Quaternionf result = NormalizeSafe(curve.EvaluateClamp(time));
+ DebugAssertIf(!IsFinite(result));
+
+ *reinterpret_cast<Quaternionf*>(bind.targetPtr) = result;
+ bind.targetObject->AwakeFromLoad(kDefaultAwakeFromLoad);
+ bind.targetObject->SetDirty();
+ }
+ }
+
+ // Sample positions
+ for (AnimationClip::Vector3Curves::iterator i=pos.begin();i != pos.end();i++)
+ {
+ if (!i->curve.IsValid ())
+ continue;
+
+ curveID = CurveID (i->path.c_str(), ClassID(Transform), NULL, "m_LocalPosition", 0);
+ if (binder.BindCurve(curveID, bind, transform))
+ {
+ Vector3f result = i->curve.EvaluateClamp(time);
+ DebugAssertIf(!IsFinite(result));
+
+ *reinterpret_cast<Vector3f*>(bind.targetPtr) = result;
+ bind.targetObject->AwakeFromLoad(kDefaultAwakeFromLoad);
+ bind.targetObject->SetDirty();
+ }
+ }
+
+
+ // Sample scale
+ for (AnimationClip::Vector3Curves::iterator i=scale.begin();i != scale.end();i++)
+ {
+ if (!i->curve.IsValid ())
+ continue;
+
+ curveID = CurveID (i->path.c_str(), ClassID(Transform), NULL, "m_LocalScale", 0);
+ if (binder.BindCurve(curveID, bind, transform))
+ {
+ Vector3f result = i->curve.EvaluateClamp(time);
+ DebugAssertIf(!IsFinite(result));
+
+ *reinterpret_cast<Vector3f*>(bind.targetPtr) = result;
+ bind.targetObject->AwakeFromLoad(kDefaultAwakeFromLoad);
+ bind.targetObject->SetDirty();
+ }
+ }
+
+ // Sample arbitrary floats
+ for (AnimationClip::FloatCurves::iterator i=floats.begin();i != floats.end();i++)
+ {
+ if (!i->curve.IsValid ())
+ continue;
+
+ curveID = CurveID (i->path.c_str(), i->classID, i->script, i->attribute.c_str(), 0);
+ if (binder.BindCurve(curveID, bind, transform))
+ {
+ float result = i->curve.EvaluateClamp(time);
+ DebugAssertIf(!IsFinite(result));
+
+ AnimationBinder::SetFloatValue(bind, result);
+ AnimationBinder::SetValueAwakeGeneric(bind);
+ }
+ }
+
+ SampleEulerHint (transform, clip, time);
+}
+
+
+mecanim::crc32 AppendPathToHash(const mecanim::crc32& nameHash, const char* path)
+{
+ mecanim::crc32 childNameHash = nameHash;
+ if (childNameHash.checksum() != 0)
+ childNameHash.process_bytes("/", 1);
+ childNameHash.process_bytes(path, strlen(path));
+ return childNameHash;
+}
+
+Transform* FindChildWithID( Transform* transform, const mecanim::crc32& nameHash, mecanim::uint32_t id, mecanim::crc32& outFoundNameHash, int childStartHint)
+{
+ int childCount = transform->GetChildrenCount();
+ for (int i = 0 ; i < childCount ; ++i)
+ {
+ mecanim::crc32 currentHash = AppendPathToHash(nameHash, transform->GetChild((i+childStartHint)%childCount).GetName());
+ if(id == currentHash.checksum())
+ {
+ outFoundNameHash = currentHash;
+ return &transform->GetChild((i+childStartHint)%childCount);
+ }
+ }
+
+ return 0;
+}
+
+
+
+int HiearchyMatches(Transform* transform, const mecanim::skeleton::Skeleton* skeleton, int skeletonIndex, const mecanim::crc32& nameHash)
+{
+ int currentMatchCount = 0 ;
+ int childStartHint = 0 ;
+
+ for(int i = skeletonIndex ; i < skeleton->m_Count ; i++)
+ {
+ if(skeleton->m_Node[i].m_ParentId == skeletonIndex)
+ {
+ mecanim::crc32 childHash;
+ Transform* childTransform = FindChildWithID(transform, nameHash, skeleton->m_ID[i], childHash, childStartHint);
+
+ if(childTransform)
+ {
+ currentMatchCount++;
+ currentMatchCount += HiearchyMatches(childTransform, skeleton, i, childHash);
+ childStartHint++;
+ }
+
+ }
+ }
+
+ return currentMatchCount;
+}
+
+int HiearchyMatchesOpt(Transform* transform, const mecanim::skeleton::Skeleton* skeleton, const mecanim::uint32_t* nameArray)
+{
+ int matchCount = 0;
+
+ for (Transform::iterator transformIt = transform->begin(); transformIt != transform->end(); ++transformIt)
+ {
+ for(mecanim::int32_t i = 0 ; i < skeleton->m_Count ; ++i)
+ {
+ if(nameArray[i] == mecanim::processCRC32((*transformIt)->GetName()))
+ {
+ matchCount++;
+ continue;
+ }
+ }
+ }
+
+ return matchCount;
+}
+
+
+void BuildTransformList(Transform& root, dynamic_array<Transform*>& outTransforms)
+{
+ outTransforms.push_back(&root);
+ int childCount = root.GetChildrenCount();
+ for (int i = 0 ; i < childCount ; ++i)
+ BuildTransformList(root.GetChild(i), outTransforms);
+}
+
+Transform* FindAvatarRoot(const mecanim::skeleton::Skeleton* skeleton, const mecanim::uint32_t* nameArray, Transform& root, bool hasTransformHierarchy)
+{
+ int bestMatchCount = 0;
+ Transform* animationRoot = 0;
+
+ dynamic_array<Transform*> allTransforms (kMemTempAlloc);
+ allTransforms.reserve(skeleton->m_Count*2);
+ BuildTransformList(root, allTransforms);
+
+ for(int i = 0 ; i < allTransforms.size() ; ++i)
+ {
+ int currentMatchCount = 0 ;
+
+ if(hasTransformHierarchy)
+ currentMatchCount = HiearchyMatches(allTransforms[i], skeleton, 0, mecanim::crc32()); // find the transform that matches best the Avatar Hierarchy
+ else
+ currentMatchCount = HiearchyMatchesOpt(allTransforms[i], skeleton, nameArray); // find the Transform that has the most direct childs in the Avatar Skeleton
+
+ if(currentMatchCount > bestMatchCount)
+ {
+ bestMatchCount = currentMatchCount;
+ animationRoot = allTransforms[i];
+ }
+
+ if( bestMatchCount >= (allTransforms.size() - 1 - i) ) // early out, since we cant have a better score than the remaning transform count.
+ return animationRoot;
+ }
+
+ return animationRoot;
+}
diff --git a/Runtime/Animation/AnimationUtility.h b/Runtime/Animation/AnimationUtility.h
new file mode 100644
index 0000000..7a92ef8
--- /dev/null
+++ b/Runtime/Animation/AnimationUtility.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "Runtime/mecanim/types.h"
+
+class AnimationClip;
+class Transform;
+namespace Unity { class GameObject; }
+namespace mecanim { class crc32 ; namespace skeleton { struct Skeleton; } }
+
+
+void SampleAnimation (Unity::GameObject& go, AnimationClip& clip, float inTime, int wrapMode);
+
+
+mecanim::crc32 AppendPathToHash(const mecanim::crc32& nameHash, const char* path);
+Transform* FindChildWithID( Transform* transform, const mecanim::crc32& nameHash, mecanim::uint32_t id);
+
+Transform* FindAvatarRoot(const mecanim::skeleton::Skeleton* skeleton, const mecanim::uint32_t* nameArray, Transform& root, bool hasTransformHierarchy);
diff --git a/Runtime/Animation/Animator.cpp b/Runtime/Animation/Animator.cpp
new file mode 100644
index 0000000..a8db798
--- /dev/null
+++ b/Runtime/Animation/Animator.cpp
@@ -0,0 +1,2674 @@
+#include "UnityPrefix.h"
+
+#include "Animator.h"
+
+#include "Runtime/Animation/OptimizeTransformHierarchy.h"
+
+#include "Runtime/mecanim/animation/avatar.h"
+#include "Runtime/mecanim/animation/damp.h"
+#include "Runtime/mecanim/generic/stringtable.h"
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "AnimatorManager.h"
+#include "Runtime/Threads/JobScheduler.h"
+#include "AnimationClip.h"
+#include "AnimationSetBinding.h"
+#include "MecanimAnimation.h"
+
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/GameCode/RootMotionData.h"
+#include "Runtime/BaseClasses/SupportedMessageOptimization.h"
+#include "Runtime/BaseClasses/EventIDs.h"
+
+#include "Runtime/Graphics/Transform.h"
+
+#include "Runtime/Math/Vector4.h"
+
+#include "AnimatorController.h"
+#include "GenericAnimationBindingCache.h"
+#include "Avatar.h"
+#include "AnimatorOverrideController.h"
+
+#include "Runtime/mecanim/generic/typetraits.h"
+
+#include "Runtime/Serialize/SerializeTraits.h"
+#include "AnimatorGenericBindings.h"
+
+#include "Runtime/Animation/AnimationClip.h"
+#include "Runtime/Animation/AnimationUtility.h"
+
+#define ENABLE_DETAILED_SINGLE_THREAD_PROFILER 0
+
+#define ENABLE_MULTITHREADED_ANIMATION ENABLE_MULTITHREADED_CODE && (!ENABLE_DETAILED_SINGLE_THREAD_PROFILER)
+using namespace UnityEngine::Animation;
+Animator::Animator(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode),
+m_BehaviourIndex(-1),
+m_FixedBehaviourIndex(-1),
+m_DeltaPosition(Vector3f::zero),
+m_DeltaRotation(Quaternionf::identity()),
+m_PivotPosition(0,0,0),
+m_MatchPosition(Vector3f::zero),
+m_MatchRotation(Quaternionf::identity()),
+m_MatchStartTime(-1),
+m_MatchStateID(-1),
+m_MustCompleteMatch(false),
+m_MatchTargetMask(Vector3f::one, 0),
+m_ApplyRootMotion(true),
+m_AnimatePhysics(false),
+m_Visible(false),
+m_CullingMode (kCullAlwaysAnimate),
+m_Speed(1),
+m_FireEvents(true),
+m_LogWarnings(true),
+m_AvatarPlayback(label),
+m_RecorderMode(eNormal),
+m_PlaybackDeltaTime(0),
+m_PlaybackTime(0),
+m_HasTransformHierarchy(true),
+m_AnimatorAvatarNode(this),
+m_AnimatorControllerNode(this),
+m_SamplingDataSet(256*1024),
+mAlloc(kMemAnimation)
+{
+
+}
+
+Animator::~Animator()
+{
+ Assert(m_EvaluationDataSet.m_AvatarMemory == NULL && m_EvaluationDataSet.m_ControllerConstant == NULL && m_EvaluationDataSet.m_GenericBindingConstant == NULL && m_ContainedRenderers.size() == 0);
+}
+
+void Animator::AwakeFromLoad(AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad(mode);
+ CreateObject();
+ InitializeVisibilityCulling();
+
+ UpdateInManager();
+}
+
+void Animator::CheckConsistency()
+{
+}
+
+void Animator::Reset ()
+{
+ Super::Reset();
+
+ m_CullingMode = kCullAlwaysAnimate;
+ m_ApplyRootMotion = true;
+ m_AnimatePhysics = false;
+ m_HasTransformHierarchy = true;
+}
+
+IMPLEMENT_OBJECT_SERIALIZE (Animator)
+IMPLEMENT_CLASS_HAS_INIT (Animator)
+
+template<class TransferFunction>
+void Animator::Transfer (TransferFunction& transfer)
+{
+ transfer.SetVersion(2);
+
+ Super::Transfer (transfer);
+
+ transfer.Transfer (m_Avatar, "m_Avatar");
+ transfer.Transfer (m_Controller, "m_Controller");
+
+ TRANSFER_ENUM (m_CullingMode);
+ transfer.Transfer (m_ApplyRootMotion, "m_ApplyRootMotion", kDontAnimate);
+ transfer.Transfer (m_AnimatePhysics, "m_AnimatePhysics", kDontAnimate);
+ transfer.Transfer (m_HasTransformHierarchy, "m_HasTransformHierarchy", kDontAnimate);
+}
+
+void Animator::AddToManager ()
+{
+ GetAnimatorManager().AddAnimator(*this);
+}
+
+void Animator::RemoveFromManager ()
+{
+ GetAnimatorManager().RemoveAnimator(*this);
+}
+
+void Animator::Deactivate (DeactivateOperation operation)
+{
+ Super::Deactivate(operation);
+ ClearObject();
+ ClearContainedRenderers();
+}
+
+
+void Animator::TransformChanged (int change)
+{
+ // No need to initialize it
+ if (!IsInitialize())
+ return;
+
+ // Teleport
+ Transform& avatarTransform = GetComponent(Transform);
+ if (change & Transform::kPositionChanged)
+ SetAvatarPosition(avatarTransform.GetPosition());
+
+ if (change & Transform::kRotationChanged)
+ SetAvatarRotation(avatarTransform.GetRotation());
+
+ if(change & Transform::kScaleChanged)
+ SetAvatarScale(avatarTransform.GetWorldScaleLossy());
+}
+
+void Animator::OnAddComponent(Component* com)
+{
+ Renderer* renderer = dynamic_pptr_cast<Renderer*>(com);
+ if(renderer)
+ InitializeVisibilityCulling ();
+}
+
+void Animator::InitializeClass ()
+{
+ mecanim::memory::Profiler::StaticInitialize();
+
+ REGISTER_MESSAGE (Animator, kTransformChanged, TransformChanged, int);
+ REGISTER_MESSAGE_VOID(Animator, kDidModifyAnimatorController, ClearObject);
+ REGISTER_MESSAGE_VOID(Animator, kDidModifyMotion, ClearObject); // an animationClip sends this when deleted
+ REGISTER_MESSAGE_VOID(Animator, kDidModifyAvatar, ClearObject);
+
+ ///@TODO: This doesn't really cover any real world cases...
+ /// adding new chidlren is not covered, adding component to childeren is not covered.
+ REGISTER_MESSAGE_PTR (Animator, kDidAddComponent, OnAddComponent, Component);
+
+ AnimatorManager::InitializeClass();
+ MecanimAnimation::InitializeClass();
+ mecanim::animation::ControllerConstant::InitializeClass();
+ mecanim::animation::AvatarConstant::InitializeClass();
+ mecanim::statemachine::StateConstant::InitializeClass();
+
+ mecanim::crc32::crc32_table_type::init_table();
+ mecanim::animation::InitializeMuscleClipTables ();
+
+ Assert(mecanim::animation::FindMuscleIndex(0) == -1);
+ Assert(mecanim::animation::FindMuscleIndex(mecanim::processCRC32 ("MotionT.x")) == 0);
+}
+
+void Animator::CleanupClass ()
+{
+ AnimatorManager::CleanupClass();
+ MecanimAnimation::CleanupClass();
+ mecanim::memory::Profiler::StaticDestroy();
+}
+
+PROFILER_INFORMATION (gAnimatorUpdate, "Animator.Update", kProfilerAnimation);
+PROFILER_INFORMATION (gAnimatorInitialize, "Animator.Initialize", kProfilerAnimation);
+
+PROFILER_INFORMATION (gProfileApplyRootMotion, "Apply Root Motion", kProfilerAnimation);
+PROFILER_INFORMATION (gProfileFKStep, "FK & Statemachine", kProfilerAnimation);
+PROFILER_INFORMATION (gProfileRetarget, "Retarget", kProfilerAnimation);
+PROFILER_INFORMATION (gRetargetAndPrepareIK, "Retarget", kProfilerAnimation);
+PROFILER_INFORMATION (gProfileAvatarIK, "IK & Final Pose Computation", kProfilerAnimation);
+PROFILER_INFORMATION (gProfileAvatarWrite, "Write", kProfilerAnimation);
+
+PROFILER_INFORMATION (gAnimatorSendTransformChanged, "Animator.SendTransformChanged", kProfilerAnimation);
+PROFILER_INFORMATION (gAnimatorSetGenericProperties, "Animator.ApplyGenericAnimatedProperties", kProfilerAnimation);
+PROFILER_INFORMATION (gAnimatorSetTransformDirty, "Animator.EditorOnlySetDirty", kProfilerAnimation);
+PROFILER_INFORMATION (gAnimatorWriteSkeletonPose, "Animator.WriteSkeletonPose", kProfilerAnimation);
+
+PROFILER_INFORMATION (gProfileDetailSM, "EvaluateAvatarSM", kProfilerAnimation);
+PROFILER_INFORMATION (gProfileDetailFK, "EvaluateAvatarFK", kProfilerAnimation);
+PROFILER_INFORMATION (gProfileDetailAvatarIK, "EvaluateAvatarIK", kProfilerAnimation);
+PROFILER_INFORMATION (gProfileDetailAvatarEnd, "EvaluateAvatarEnd", kProfilerAnimation);
+PROFILER_INFORMATION (gProfileSample, "Animator.Sample", kProfilerAnimation);
+
+PROFILER_INFORMATION (gProfileSetupDataSet, "Animator.SetupDataSet", kProfilerAnimation);
+
+#if ENABLE_DETAILED_SINGLE_THREAD_PROFILER
+#define PROFILER_AUTO_DETAIL(x,o) PROFILER_AUTO(x, o)
+#else
+#define PROFILER_AUTO_DETAIL(x,o)
+#endif
+
+
+static bool DoesLayerHaveIKPass(int layerIndex, const mecanim::animation::ControllerConstant& controller)
+{
+ return layerIndex < controller.m_LayerCount && controller.m_LayerArray[layerIndex]->m_IKPass;
+}
+
+// lazy search to find the right evaluation context for the given clip
+static bool FindClipInController(AnimationClip &clip, AnimationClipVector const& clips, AnimationSetBindings const& animationSetBindings, int &layerIndex, int &clipLayerIndex)
+{
+ layerIndex = -1;
+ clipLayerIndex = -1;
+
+
+ int clipIndex = -1;
+ for(int clipIter = 0; clipIndex == -1 && clipIter < clips.size(); clipIter++)
+ {
+ AnimationClip* currentClip = clips[clipIter];
+ if (&clip == currentClip)
+ {
+ clipIndex = clipIter;
+ break;
+ }
+ }
+
+ if (clipIndex == -1)
+ return false;
+
+ for(int layerIter = 0; layerIndex == -1 && layerIter < animationSetBindings.animationSet->m_LayerCount; layerIter++)
+ {
+ for(int clipLayerIter = 0; clipLayerIndex == -1 && clipLayerIter < animationSetBindings.animationSet->m_ClipPerLayer[layerIter]; clipLayerIter++)
+ {
+ const mecanim::animation::AnimationSet::Clip& clip = animationSetBindings.animationSet->m_ClipConstant[layerIter][clipLayerIter];
+ if (clip.m_ClipIndex == clipIndex && clip.m_Clip != NULL)
+ {
+ layerIndex = layerIter;
+ clipLayerIndex = clipLayerIter;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+namespace
+{
+ mecanim::animation::ControllerConstant* BuildController(AnimationClip const& clip, mecanim::memory::Allocator& allocator)
+ {
+ mecanim::uint32_t id = mecanim::processCRC32(clip.GetName());
+ mecanim::animation::BlendTreeConstant* blendTreeCst = mecanim::animation::CreateBlendTreeConstant(id, allocator);
+ mecanim::statemachine::StateConstant* stateCst = mecanim::statemachine::CreateStateConstant(0, 0, 1, true,false, 0, &blendTreeCst, 1, id, id, 0, true, allocator);
+ mecanim::statemachine::StateMachineConstant* stateMachinCst = CreateStateMachineConstant(&stateCst,1 , 0, 0, 0, 1, allocator);
+ mecanim::animation::LayerConstant* layer = mecanim::animation::CreateLayerConstant(0, 0, allocator);
+ layer->m_BodyMask = mecanim::human::FullBodyMask();
+ layer->m_SkeletonMask = 0;
+
+ mecanim::ValueArrayConstant* values = mecanim::CreateValueArrayConstant(0, 0, allocator);
+ mecanim::ValueArray* defaultValues = mecanim::CreateValueArray(values, allocator);
+
+ return mecanim::animation::CreateControllerConstant(1, &layer,1, &stateMachinCst, values, defaultValues, allocator);
+ }
+}
+
+Animator::AutoMecanimDataSet::~AutoMecanimDataSet()
+{
+ Reset();
+}
+
+void Animator::AutoMecanimDataSet::Reset()
+{
+ if(m_MecanimDataSet.m_AvatarBindingConstant != NULL)
+ UnregisterAvatarBindingObjects(m_MecanimDataSet.m_AvatarBindingConstant);
+ if(m_MecanimDataSet.m_GenericBindingConstant != NULL)
+ UnregisterGenericBindingObjects(m_MecanimDataSet.m_GenericBindingConstant);
+
+ m_MecanimDataSet.Reset();
+ m_Alloc.Reset();
+}
+
+bool Animator::Sample(AnimationClip& clip, float inTime)
+{
+ PROFILER_AUTO(gProfileSample, this);
+
+ if(!clip.IsAnimatorMotion())
+ return NULL;
+
+
+ m_SamplingDataSet.Reset();
+
+ SetupAvatarMecanimDataSet(m_Avatar.IsValid () ? m_Avatar->GetAsset() : NULL, m_SamplingDataSet.m_Alloc, *m_SamplingDataSet);
+
+ // muscle clip can be NULL when there is not curve at all in the clip.
+ mecanim::animation::ClipMuscleConstant* muscleConstant = clip.GetRuntimeAsset();
+ if(muscleConstant == NULL)
+ {
+ // in this case we want to set a human rig into relax pose to start keyframing
+ if(m_SamplingDataSet->m_AvatarConstant->isHuman())
+ {
+ mecanim::human::HumanPose pose;
+
+ mecanim::human::Human const* human = m_SamplingDataSet->m_AvatarConstant->m_Human.Get();
+ mecanim::human::RetargetTo(human, &pose, 0, math::xformIdentity(), m_SamplingDataSet->m_AvatarOutput->m_HumanPoseOutput, m_SamplingDataSet->m_AvatarWorkspace->m_BodySkeletonPoseWs, m_SamplingDataSet->m_AvatarWorkspace->m_BodySkeletonPoseWsA);
+ mecanim::animation::EvaluateAvatarEnd(m_SamplingDataSet->m_AvatarConstant, m_SamplingDataSet->m_AvatarInput, m_SamplingDataSet->m_AvatarOutput, m_SamplingDataSet->m_AvatarMemory, m_SamplingDataSet->m_AvatarWorkspace, NULL);
+
+ SetHumanTransformPropertyValues (*m_SamplingDataSet->m_AvatarBindingConstant, *m_SamplingDataSet->m_AvatarOutput->m_SkeletonPoseOutput);
+ SetTransformPropertyApplyMainThread (GetComponent (Transform), *m_SamplingDataSet->m_AvatarBindingConstant, false); // when sampling we don't skip apply root
+ }
+ return false;
+ }
+
+
+ AnimationClipVector clips;
+ clips.push_back( PPtr<AnimationClip>(&clip) );
+
+ mecanim::animation::ControllerConstant const* controllerConstant = ::BuildController(clip, m_SamplingDataSet.m_Alloc);
+ UnityEngine::Animation::AnimationSetBindings const* animationSetBindings = UnityEngine::Animation::CreateAnimationSetBindings(controllerConstant, clips, m_SamplingDataSet.m_Alloc);
+
+ SetupControllerMecanimDataSet(controllerConstant, animationSetBindings, m_SamplingDataSet.m_Alloc, *m_SamplingDataSet);
+
+ int layerIndex = 0;
+ int clipLayerIndex = 0;
+
+ // prepare the evaluation context for clip and evaluate in float array
+ mecanim::ValueArray* valuesDefault = m_SamplingDataSet->m_GenericBindingConstant->controllerBindingConstant->m_DynamicValuesDefault;
+ mecanim::ValueArrayConstant* valuesDefaultConstant = m_SamplingDataSet->m_GenericBindingConstant->controllerBindingConstant->m_DynamicValuesConstant;
+ mecanim::ValueArrayMask& readMask = *m_SamplingDataSet->m_AvatarWorkspace->m_ControllerWorkspace->m_ReadMask;
+
+ mecanim::animation::AnimationSet::Clip* setClip = &animationSetBindings->animationSet->m_ClipConstant[layerIndex][clipLayerIndex];
+ mecanim::animation::ClipMemory* clipMemory = m_SamplingDataSet->m_AnimationSetMemory->m_ClipMemory[layerIndex][clipLayerIndex];
+ mecanim::animation::ClipOutput* clipOutput = m_SamplingDataSet->m_AnimationSetMemory->m_ClipOutput;
+
+ mecanim::animation::ClipInput in;
+ in.m_Time = inTime;
+
+ mecanim::animation::EvaluateClip (muscleConstant->m_Clip.Get(), &in, clipMemory, clipOutput);
+
+ // load values and retarget
+ mecanim::SetValueMask (&readMask, false);
+ ValuesFromClip (*valuesDefault, *muscleConstant, *clipOutput, setClip->m_Bindings, animationSetBindings->animationSet->m_IntegerRemapStride, *m_SamplingDataSet->m_AvatarOutput->m_DynamicValuesOutput, readMask);
+ if (m_SamplingDataSet->m_AvatarConstant->isHuman())
+ {
+ mecanim::animation::GetHumanPose (*muscleConstant,clipOutput->m_Values,*m_SamplingDataSet->m_AvatarOutput->m_HumanPoseBaseOutput);
+ mecanim::human::HumanPoseCopy (*m_SamplingDataSet->m_AvatarOutput->m_HumanPoseOutput,*m_SamplingDataSet->m_AvatarOutput->m_HumanPoseBaseOutput);
+ mecanim::animation::EvaluateAvatarRetarget (m_SamplingDataSet->m_AvatarConstant, m_SamplingDataSet->m_AvatarInput, m_SamplingDataSet->m_AvatarOutput, m_SamplingDataSet->m_AvatarMemory, m_SamplingDataSet->m_AvatarWorkspace, m_SamplingDataSet->m_ControllerConstant);
+ mecanim::animation::EvaluateAvatarEnd (m_SamplingDataSet->m_AvatarConstant, m_SamplingDataSet->m_AvatarInput, m_SamplingDataSet->m_AvatarOutput, m_SamplingDataSet->m_AvatarMemory, m_SamplingDataSet->m_AvatarWorkspace, m_SamplingDataSet->m_ControllerConstant);
+ }
+
+ // Controller animated values
+ ValueArrayCopy (valuesDefaultConstant, m_SamplingDataSet->m_AvatarOutput->m_DynamicValuesOutput, m_SamplingDataSet->m_ControllerConstant->m_Values.Get(), m_SamplingDataSet->m_AvatarMemory->m_ControllerMemory->m_Values.Get(), animationSetBindings->animationSet->m_AdditionalIndexArray);
+
+ // set transform values
+ if (m_SamplingDataSet->m_AvatarConstant->isHuman())
+ SetHumanTransformPropertyValues (*m_SamplingDataSet->m_AvatarBindingConstant, *m_SamplingDataSet->m_AvatarOutput->m_SkeletonPoseOutput);
+ SetGenericTransformPropertyValues (*m_SamplingDataSet->m_GenericBindingConstant, *m_SamplingDataSet->m_AvatarOutput->m_DynamicValuesOutput,0); // when sampling set root transform if needed
+
+ SetTransformPropertyApplyMainThread (GetComponent (Transform), *m_SamplingDataSet->m_GenericBindingConstant, *m_SamplingDataSet->m_AvatarBindingConstant, false); // when sampling we don't skip apply root
+
+ // set generic values
+ SetGenericFloatPropertyValues (*m_SamplingDataSet->m_GenericBindingConstant, *m_SamplingDataSet->m_AvatarOutput->m_DynamicValuesOutput);
+ SetGenericPPtrPropertyValues (*m_SamplingDataSet->m_GenericBindingConstant, *m_SamplingDataSet->m_AvatarOutput->m_DynamicValuesOutput);
+
+ return true;
+}
+
+void Animator::UpdateAvatars (Animator** inputAvatars, size_t inputSize, float deltaTime, bool doFKMove, bool doRetargetIKWrite)
+{
+ PROFILER_AUTO(gAnimatorUpdate, NULL)
+
+ #if ENABLE_MULTITHREADED_ANIMATION
+
+ JobScheduler& scheduler = GetJobScheduler();
+ JobScheduler::JobGroupID jobGroup;
+
+ #define AVATAR_LOOP(x,list,profile) \
+ { \
+ PROFILER_AUTO(profile, NULL); \
+ size_t avatarJobCount = list.size(); \
+ jobGroup = scheduler.BeginGroup(avatarJobCount); \
+ for (size_t i = 0; i < avatarJobCount; ++i) \
+ { Animator& avatar = *list[i]; scheduler.SubmitJob (jobGroup, x, &avatar, NULL); } \
+ scheduler.WaitForGroup (jobGroup); \
+ }
+ #else
+
+ #define AVATAR_LOOP(x,list, profile) \
+ for (size_t i=0;i<list.size();i++) \
+ { Animator& avatar = *list[i]; PROFILER_AUTO(profile, &avatar); x (&avatar); }
+
+ #endif
+
+ if(doFKMove)
+ {
+ // invisible & visible animators
+ dynamic_array<Animator*> activeAvatars (kMemTempAlloc);
+ activeAvatars.reserve(inputSize);
+
+ for (size_t i=0;i<inputSize;i++)
+ {
+ Animator& avatar = *inputAvatars[i];
+
+ if (avatar.Prepare())
+ {
+ activeAvatars.push_back(&avatar);
+ }
+ }
+
+ // Init delta time
+ for (size_t i=0;i<activeAvatars.size();i++)
+ {
+ Animator& avatar = *activeAvatars[i];
+ avatar.InitStep(deltaTime);
+ }
+
+
+ // Multithreaded FK Step
+ AVATAR_LOOP(FKStepStatic, activeAvatars, gProfileFKStep)
+
+ // MainThread AnimationEvents and OnAnimatorMove script call
+ for (size_t i=0;i<activeAvatars.size();i++)
+ {
+ Animator& avatar = *activeAvatars[i];
+ avatar.FireAnimationEvents();
+ avatar.ApplyOnAnimatorMove();
+ }
+ }
+
+ if (doRetargetIKWrite)
+ {
+ int layerCount = 0;
+
+ // only visible animators
+ dynamic_array<Animator*> visibleAvatars (kMemTempAlloc);
+ visibleAvatars.reserve(inputSize);
+
+ // human visible animators
+ dynamic_array<Animator*> humanAvatars (kMemTempAlloc);
+ humanAvatars.reserve(inputSize);
+
+ for (size_t i=0;i<inputSize;i++)
+ {
+ Animator& avatar = *inputAvatars[i];
+
+ if (avatar.Prepare() && avatar.m_Visible)
+ {
+ layerCount = avatar.GetLayerCount() > layerCount ? avatar.GetLayerCount() : layerCount;
+ visibleAvatars.push_back(&avatar);
+
+ if(avatar.IsHuman())
+ {
+ humanAvatars.push_back(&avatar);
+
+ avatar.m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_DoIK = true;
+ }
+
+ avatar.m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_DoWrite = true;
+ }
+ }
+
+
+ // Multithreaded Retarget & PrepareIK
+ AVATAR_LOOP(RetargetStepStatic, humanAvatars, gProfileRetarget)
+
+ // Default IK Pass
+ AVATAR_LOOP(AvatarIKAndEndStepStatic, humanAvatars, gProfileAvatarIK)
+ AVATAR_LOOP(AvatarWriteStepStatic, humanAvatars, gProfileAvatarWrite)
+
+ // Layered IK Pass
+ for(int layerIter = 0; layerIter < layerCount; layerIter++)
+ {
+ // MainThread OnApplyAvatarIK
+ for (size_t i = 0; i < humanAvatars.size(); i++)
+ {
+ Animator& avatar = *humanAvatars[i];
+
+ bool ikPass = DoesLayerHaveIKPass (layerIter, *avatar.m_EvaluationDataSet.m_ControllerConstant);
+ if (ikPass)
+ {
+ avatar.ApplyOnAnimatorIK (layerIter);
+ }
+
+ avatar.m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_DoIK = ikPass;
+ avatar.m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_DoWrite = ikPass;
+ }
+
+ AVATAR_LOOP(AvatarIKAndEndStepStatic,humanAvatars, gProfileAvatarIK)
+ AVATAR_LOOP(AvatarWriteStepStatic, humanAvatars, gProfileAvatarWrite)
+ }
+
+ // Write to those that were not written already
+ AVATAR_LOOP(AvatarWriteStepStatic, visibleAvatars, gProfileAvatarWrite)
+
+ // MainThread Apply skeleton to transform components
+ for (size_t i=0;i<visibleAvatars.size();i++)
+ {
+ Animator& animator = *visibleAvatars[i];
+ if(animator.IsActive()) // animator can be turned Inactive by a parent animator -> 566794
+ {
+ {
+ if (animator.m_HasTransformHierarchy)
+ {
+ PROFILER_AUTO(gAnimatorSendTransformChanged, &animator)
+ SetTransformPropertyApplyMainThread (animator.GetComponent(Transform), *animator.m_EvaluationDataSet.m_GenericBindingConstant, *animator.m_EvaluationDataSet.m_AvatarBindingConstant, animator.IsHuman() || animator.HasRootMotion()); // if it has root motion we skip root
+ }
+ else
+ {
+ const mecanim::skeleton::SkeletonPose* pose = animator.GetGlobalSpaceSkeletonPose();
+ if (pose && animator.m_EvaluationDataSet.m_AvatarConstant)
+ SetFlattenedSkeletonTransformsMainThread (*animator.m_EvaluationDataSet.m_AvatarBindingConstant, *pose, *animator.m_EvaluationDataSet.m_AvatarConstant);
+ }
+ }
+
+ {
+
+ PROFILER_AUTO(gAnimatorSetGenericProperties, &animator)
+ SetGenericFloatPropertyValues (*animator.m_EvaluationDataSet.m_GenericBindingConstant, *animator.m_EvaluationDataSet.m_AvatarOutput->m_DynamicValuesOutput);
+ SetGenericPPtrPropertyValues (*animator.m_EvaluationDataSet.m_GenericBindingConstant, *animator.m_EvaluationDataSet.m_AvatarOutput->m_DynamicValuesOutput);
+ }
+
+ animator.Record(deltaTime);
+
+ UInt8 deltaTimeIs0 = (deltaTime==0) ? 1 : 0;
+ animator.m_EvaluationDataSet.m_AvatarMemory->m_FirstEval &= deltaTimeIs0;
+ }
+ }
+ }
+}
+
+bool Animator::Prepare()
+{
+ if (!IsInitialize())
+ CreateObject();
+
+ return IsInitialize();
+}
+
+void Animator::InitStep(float deltaTime)
+{
+ if(IsAutoPlayingBack())
+ {
+ SetPlaybackTimeInternal(m_AvatarPlayback.CursorTime() + (deltaTime * GetSpeed()));
+ }
+ else
+ {
+ m_EvaluationDataSet.m_AvatarInput->m_DeltaTime = deltaTime * GetSpeed();
+ }
+
+ if(IsPlayingBack())
+ {
+ m_EvaluationDataSet.m_AvatarInput->m_DeltaTime = m_PlaybackDeltaTime;
+ m_PlaybackDeltaTime = 0;
+ }
+}
+
+void Animator::EvaluateSM()
+{
+ if(Prepare())
+ {
+ mecanim::animation::EvaluateAvatarSM(m_EvaluationDataSet.m_AvatarConstant, m_EvaluationDataSet.m_AvatarInput, m_EvaluationDataSet.m_AvatarOutput, m_EvaluationDataSet.m_AvatarMemory, m_EvaluationDataSet.m_AvatarWorkspace, m_EvaluationDataSet.m_ControllerConstant);
+}
+}
+
+void Animator::FKStep()
+{
+ m_DeltaPosition = Vector3f::zero;
+ m_DeltaRotation = Quaternionf::identity();
+
+ {
+ PROFILER_AUTO_DETAIL (gProfileDetailSM, this)
+ mecanim::animation::EvaluateAvatarSM(m_EvaluationDataSet.m_AvatarConstant, m_EvaluationDataSet.m_AvatarInput, m_EvaluationDataSet.m_AvatarOutput, m_EvaluationDataSet.m_AvatarMemory, m_EvaluationDataSet.m_AvatarWorkspace, m_EvaluationDataSet.m_ControllerConstant);
+ }
+
+ {
+ PROFILER_AUTO_DETAIL(gProfileDetailFK, this);
+
+ mecanim::animation::EvaluateAvatarLayers(m_EvaluationDataSet.m_GenericBindingConstant->controllerBindingConstant, m_EvaluationDataSet.m_AvatarInput, m_EvaluationDataSet.m_AvatarOutput, m_EvaluationDataSet.m_AvatarMemory, m_EvaluationDataSet.m_AvatarWorkspace, m_EvaluationDataSet.m_AnimationSetMemory);
+
+ if(m_EvaluationDataSet.m_AvatarConstant->isHuman())
+ {
+ // Prepare for OnAnimatorMove
+ m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_TargetX.t *= math::float1(m_EvaluationDataSet.m_AvatarConstant->m_Human->m_Scale);
+
+ TargetMatch(m_MustCompleteMatch);
+ m_MustCompleteMatch = false;
+
+ m_DeltaPosition = RotateVectorByQuat(GetAvatarRotation(), float4ToVector3f(m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_DX.t*m_EvaluationDataSet.m_AvatarMemory->m_AvatarX.s*math::float1(m_EvaluationDataSet.m_AvatarConstant->m_Human->m_Scale)));
+ m_DeltaRotation = float4ToQuaternionf(m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_DX.q);
+ }
+ else if(m_EvaluationDataSet.m_AvatarConstant->m_RootMotionBoneIndex != -1)
+ {
+ m_DeltaPosition = RotateVectorByQuat(GetAvatarRotation(), float4ToVector3f(m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_DX.t*m_EvaluationDataSet.m_AvatarMemory->m_AvatarX.s));
+ m_DeltaRotation = float4ToQuaternionf(m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_DX.q);
+ }
+
+ if(m_EvaluationDataSet.m_AvatarConstant->isHuman() || m_EvaluationDataSet.m_AvatarConstant->m_RootMotionBoneIndex != -1)
+ {
+ mecanim::animation::EvaluateAvatarX(m_EvaluationDataSet.m_AvatarConstant, m_EvaluationDataSet.m_AvatarInput, m_EvaluationDataSet.m_AvatarOutput, m_EvaluationDataSet.m_AvatarMemory, m_EvaluationDataSet.m_AvatarWorkspace);
+ }
+
+ if(m_EvaluationDataSet.m_AvatarConstant->isHuman())
+ {
+ m_TargetPosition = float4ToVector3f(math::xformMulVec(m_EvaluationDataSet.m_AvatarMemory->m_AvatarX, m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_TargetX.t));
+ m_TargetRotation = float4ToQuaternionf(math::normalize(math::quatMul(m_EvaluationDataSet.m_AvatarMemory->m_AvatarX.q, m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_TargetX.q)));
+ m_PivotPosition = float4ToVector3f(math::xformMulVec(m_EvaluationDataSet.m_AvatarMemory->m_AvatarX, m_EvaluationDataSet.m_AvatarMemory->m_Pivot));
+ }
+ }
+}
+
+void Animator::RetargetStep()
+{
+ mecanim::animation::EvaluateAvatarRetarget(m_EvaluationDataSet.m_AvatarConstant, m_EvaluationDataSet.m_AvatarInput, m_EvaluationDataSet.m_AvatarOutput, m_EvaluationDataSet.m_AvatarMemory, m_EvaluationDataSet.m_AvatarWorkspace, m_EvaluationDataSet.m_ControllerConstant);
+}
+
+void Animator::AvatarIKAndEndStep()
+{
+ if (!m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_DoIK)
+ return;
+
+ {
+ PROFILER_AUTO_DETAIL(gProfileDetailAvatarIK, this)
+ mecanim::animation::EvaluateAvatarIK(m_EvaluationDataSet.m_AvatarConstant, m_EvaluationDataSet.m_AvatarInput, m_EvaluationDataSet.m_AvatarOutput, m_EvaluationDataSet.m_AvatarMemory, m_EvaluationDataSet.m_AvatarWorkspace, m_EvaluationDataSet.m_ControllerConstant);
+ }
+
+ {
+ PROFILER_AUTO_DETAIL(gProfileDetailAvatarEnd, this)
+
+ mecanim::animation::EvaluateAvatarEnd(m_EvaluationDataSet.m_AvatarConstant, m_EvaluationDataSet.m_AvatarInput, m_EvaluationDataSet.m_AvatarOutput, m_EvaluationDataSet.m_AvatarMemory, m_EvaluationDataSet.m_AvatarWorkspace, m_EvaluationDataSet.m_ControllerConstant);
+ }
+}
+
+void Animator::AvatarWriteStep()
+{
+ if (!m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_DoWrite)
+ return;
+
+ m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_DoWrite = false;
+
+ const bool hasRootMotion = m_EvaluationDataSet.m_AvatarConstant->isHuman() || (m_EvaluationDataSet.m_AvatarConstant->m_RootMotionBoneIndex != -1) ;
+
+ if (m_HasTransformHierarchy)
+ {
+ // Write Transforms from humanoid skeleton
+ if (m_EvaluationDataSet.m_AvatarConstant->isHuman())
+ /// review this function to just write what it needs
+ SetHumanTransformPropertyValues (*m_EvaluationDataSet.m_AvatarBindingConstant, *m_EvaluationDataSet.m_AvatarOutput->m_SkeletonPoseOutput);
+
+ // Write Transforms for generic binding
+ SetGenericTransformPropertyValues (*m_EvaluationDataSet.m_GenericBindingConstant, *m_EvaluationDataSet.m_AvatarOutput->m_DynamicValuesOutput, hasRootMotion ? &GetComponent(Transform) : 0);
+ }
+ else if (m_EvaluationDataSet.m_AvatarConstant->m_AvatarSkeleton->m_Count > 0)
+ {
+ mecanim::animation::SkeletonPoseFromValue( *m_EvaluationDataSet.m_AvatarConstant->m_AvatarSkeleton.Get(),
+ *m_EvaluationDataSet.m_AvatarConstant->m_AvatarSkeletonPose.Get(),
+ *m_EvaluationDataSet.m_AvatarOutput->m_DynamicValuesOutput,
+ m_EvaluationDataSet.m_GenericBindingConstant->controllerBindingConstant->m_SkeletonTQSMap,
+ *m_EvaluationDataSet.m_AvatarOutput->m_SkeletonPoseOutput,
+ m_EvaluationDataSet.m_AvatarConstant->isHuman() ? m_EvaluationDataSet.m_AvatarConstant->m_HumanSkeletonReverseIndexArray.Get() : 0,
+ hasRootMotion );
+
+ if (!m_EvaluationDataSet.m_AvatarConstant->isHuman())
+ {
+ m_EvaluationDataSet.m_AvatarOutput->m_SkeletonPoseOutput->m_X[0] = m_EvaluationDataSet.m_AvatarMemory->m_AvatarX;
+ }
+
+ mecanim::skeleton::SkeletonPoseComputeGlobal (m_EvaluationDataSet.m_AvatarConstant->m_AvatarSkeleton.Get(), m_EvaluationDataSet.m_AvatarOutput->m_SkeletonPoseOutput, m_EvaluationDataSet.m_AvatarOutput->m_SkeletonPoseOutput);
+
+ m_EvaluationDataSet.m_AvatarMemory->m_SkeletonPoseOutputReady = true;
+ }
+}
+
+const mecanim::skeleton::SkeletonPose* Animator::GetGlobalSpaceSkeletonPose() const
+{
+ if (m_EvaluationDataSet.m_AvatarConstant && m_EvaluationDataSet.m_AvatarMemory->m_SkeletonPoseOutputReady)
+ return m_EvaluationDataSet.m_AvatarOutput->m_SkeletonPoseOutput;
+ return NULL;
+}
+
+void Animator::ApplyOnAnimatorMove()
+{
+ if(!IsPlayingBack())
+ {
+ if (SupportsOnAnimatorMove ())
+ {
+ SendMessage (kAnimatorMove);
+ }
+ else
+ {
+ if (m_ApplyRootMotion)
+ {
+ ApplyBuiltinRootMotion();
+ }
+ }
+
+ // force feedback of transform in avatarX
+ // avatarX is always modified by deltaTransform in FK step
+ // if transform is not modified then avatarX won't be sync with it
+ // case 498101
+ // TODO: we could skip this most of the time with a simple dirty check
+ Transform& avatarTransform = GetComponent(Transform);
+ SetAvatarPosition(avatarTransform.GetPosition());
+ SetAvatarRotation(avatarTransform.GetRotation());
+ }
+ else
+ {
+ Transform& avatarTransform = GetComponent(Transform);
+ avatarTransform.SetPositionAndRotation(GetAvatarPosition(), GetAvatarRotation());
+ }
+}
+
+void Animator::ApplyBuiltinRootMotion()
+{
+ PROFILER_AUTO(gProfileApplyRootMotion, this);
+
+ // Builtin move (For example rigidbodies & Character Controllers)
+ RootMotionData motionData;
+ motionData.deltaPosition = GetDeltaPosition();
+ motionData.targetRotation = GetAvatarRotation();
+ motionData.gravityWeight = GetGravityWeight();
+ motionData.didApply = false;
+ SendMessage (kAnimatorMoveBuiltin, &motionData, ClassID(RootMotionData));
+
+ // Fallback to just moving the transform
+ if (!motionData.didApply)
+ {
+ Transform& avatarTransform = GetComponent(Transform);
+ avatarTransform.SetPositionAndRotation(GetAvatarPosition(), motionData.targetRotation);
+ }
+}
+
+void Animator::FireAnimationEvents()
+{
+ if(!m_FireEvents) return; // we dont want to fire events in previewers
+
+ AnimationClipVector const allClips = GetAnimationClips();
+ for(int i = 0; i < m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_BlendingClipCount; i++)
+ {
+ mecanim::animation::BlendingClip &blendingClip = m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_BlendingClipArray[i];
+
+ AnimationClip *clip = allClips[blendingClip.m_ClipIndex];
+
+ if (clip->HasAnimationEvents())
+ {
+ float prevTime = blendingClip.m_Reverse ? blendingClip.m_Time : blendingClip.m_PrevTime;
+ float time = blendingClip.m_Reverse ? blendingClip.m_PrevTime : blendingClip.m_Time;
+
+ clip->FireAnimationEvents (prevTime, time, *this);
+ }
+ }
+}
+
+bool Animator::SupportsOnAnimatorMove ()
+{
+ return GetGameObject().GetSupportedMessages() & kHasOnAnimatorMove;
+}
+
+void Animator::ApplyOnAnimatorIK(int layerIndex)
+{
+ if (GetGameObject().GetSupportedMessages() & kHasOnAnimatorIK)
+ SendMessage (kAnimatorIK, layerIndex, ClassID(int));
+}
+
+void Animator::Record(float deltaTime)
+{
+ if(m_RecorderMode == eRecord && GetSpeed() >= 0)
+ {
+ m_AvatarPlayback.RecordFrame(deltaTime*GetSpeed(), m_EvaluationDataSet.m_AvatarMemory);
+ }
+}
+
+void* Animator::FKStepStatic (void* userData)
+{
+ PROFILER_AUTO(gProfileFKStep,NULL);
+ static_cast<Animator*> (userData)->FKStep();
+ return NULL;
+}
+
+void* Animator::RetargetStepStatic (void* userData)
+{
+ PROFILER_AUTO(gProfileRetarget,NULL);
+ static_cast<Animator*> (userData)->RetargetStep();
+ return NULL;
+}
+
+void* Animator::AvatarIKAndEndStepStatic (void* userData)
+{
+ PROFILER_AUTO(gProfileAvatarIK,NULL);
+ static_cast<Animator*> (userData)->AvatarIKAndEndStep();
+ return NULL;
+}
+
+void* Animator::AvatarWriteStepStatic (void* userData)
+{
+ PROFILER_AUTO(gProfileAvatarWrite,NULL);
+ static_cast<Animator*> (userData)->AvatarWriteStep();
+ return NULL;
+}
+
+void Animator::Update(float deltaTime)
+{
+ Animator* avatar = this;
+ UpdateAvatars (&avatar, 1, deltaTime, true, true);
+}
+
+bool Animator::IsAvatarInitialize() const
+{
+ return m_EvaluationDataSet.m_AvatarMemory;
+}
+
+bool Animator::IsInitialize() const
+{
+ return IsAvatarInitialize() && m_EvaluationDataSet.m_ControllerMemory;
+}
+
+void Animator::SetLayersAffectMassCenter(bool value)
+{
+ if(!IsAvatarInitialize())
+ return;
+
+ m_EvaluationDataSet.m_AvatarInput->m_LayersAffectMassCenter = value;
+}
+
+bool Animator::GetLayersAffectMassCenter() const
+{
+ if(!IsAvatarInitialize())
+ return false;
+
+ return m_EvaluationDataSet.m_AvatarInput->m_LayersAffectMassCenter ;
+}
+
+bool Animator::IsInManagerList() const
+{
+ return m_BehaviourIndex != -1 || m_FixedBehaviourIndex != -1;
+}
+
+bool Animator::IsValid() const
+{
+ return m_Controller.GetInstanceID() != 0;
+}
+
+
+RuntimeAnimatorController* Animator::GetRuntimeAnimatorController() const
+{
+ return m_Controller;
+}
+
+void Animator::SetRuntimeAnimatorController(RuntimeAnimatorController* controller)
+{
+ if(m_Controller != PPtr<RuntimeAnimatorController>(controller))
+ {
+ m_Controller = controller;
+
+ UpdateInManager();
+
+ CreateObject();
+ SetDirty();
+ }
+}
+
+
+AnimatorController* Animator::GetAnimatorController()const
+{
+ return dynamic_pptr_cast<AnimatorController*>(m_Controller);
+}
+
+
+AnimatorOverrideController* Animator::GetAnimatorOverrideController()const
+{
+ return dynamic_pptr_cast<AnimatorOverrideController*>(m_Controller);
+}
+
+void Animator::UpdateInManager()
+{
+ bool isValid = IsValid() && IsAddedToManager() ;
+
+ bool isEffectivelyAddedToManager = IsInManagerList();
+
+ if(!isValid && isEffectivelyAddedToManager)
+ {
+ RemoveFromManager();
+ }
+ else if(isValid && !isEffectivelyAddedToManager)
+ {
+ AddToManager();
+ }
+}
+
+
+
+
+Avatar* Animator::GetAvatar()
+{
+ return m_Avatar;
+}
+
+void Animator::SetAvatar(Avatar* avatar)
+{
+ if(m_Avatar != PPtr<Avatar>(avatar))
+ {
+ m_Avatar = avatar;
+
+ UpdateInManager();
+
+ CreateObject();
+ SetDirty();
+ }
+}
+
+const mecanim::animation::AvatarConstant* Animator::GetAvatarConstant()
+{
+ if (!IsAvatarInitialize())
+ CreateObject();
+ return m_EvaluationDataSet.m_AvatarConstant;
+}
+
+
+void Animator::SetCullingMode (CullingMode mode)
+{
+ if (m_CullingMode == mode)
+ return;
+
+ m_CullingMode = mode;
+
+ InitializeVisibilityCulling ();
+}
+
+void Animator::SetVisibleRenderers(bool visible)
+{
+ Assert(m_CullingMode == kCullBasedOnRenderers);
+
+ bool becameVisible = visible && !m_Visible;
+ m_Visible = visible;
+
+ if (!IsWorldPlaying())
+ return;
+
+ // Perform Retarget & IK step during culling to ensure the animator
+ // will be rendered correctly in the same frame.
+ if (becameVisible )
+ {
+ float deltaTime = GetDeltaTime();
+
+ if(!IsInManagerList() || deltaTime == 0)
+ return;
+
+ if(Prepare())
+ {
+ Animator* animator[1] = { this };
+
+ bool firstEval = m_EvaluationDataSet.m_AvatarMemory->m_FirstEval;
+ m_EvaluationDataSet.m_AvatarMemory->m_FirstEval = 1;
+ m_EvaluationDataSet.m_AvatarMemory->m_SkeletonPoseOutputReady = 0;
+ UpdateAvatars (animator, 1, deltaTime, firstEval, true);
+ }
+ }
+}
+
+void Animator::AnimatorVisibilityCallback (void* userData, void* renderer, int visibilityEvent)
+{
+ Animator& animator = *reinterpret_cast<Animator*> (userData);
+
+ if (visibilityEvent == kBecameVisibleEvent)
+ animator.SetVisibleRenderers(true);
+ else if (visibilityEvent == kBecameInvisibleEvent)
+ {
+ animator.SetVisibleRenderers(animator.IsAnyRendererVisible ());
+ }
+ else if (visibilityEvent == kWillDestroyEvent)
+ {
+ animator.RemoveContainedRenderer(renderer);
+
+ // Check if we are visible
+ animator.SetVisibleRenderers(animator.IsAnyRendererVisible ());
+ }
+}
+
+void Animator::RemoveContainedRenderer (void* renderer)
+{
+ for (int i=0;i<m_ContainedRenderers.size();i++)
+ {
+ if (m_ContainedRenderers[i] == renderer)
+ {
+ m_ContainedRenderers[i] = m_ContainedRenderers.back();
+ m_ContainedRenderers.pop_back();
+ return;
+ }
+ }
+}
+
+bool Animator::IsAnyRendererVisible () const
+{
+ Assert(m_CullingMode == kCullBasedOnRenderers);
+
+ ContainedRenderers::const_iterator end = m_ContainedRenderers.end();
+ for (ContainedRenderers::const_iterator i = m_ContainedRenderers.begin();i != end;++i)
+ {
+ const Renderer* renderer = *i;
+ Assert(renderer->HasEvent(AnimatorVisibilityCallback, this));
+
+ if (renderer->IsVisibleInScene())
+ return true;
+ }
+
+ return false;
+}
+
+void Animator::ClearContainedRenderers ()
+{
+ ContainedRenderers::iterator end = m_ContainedRenderers.end();
+ for (ContainedRenderers::iterator i = m_ContainedRenderers.begin();i != end;++i)
+ {
+ Renderer* renderer = *i;
+ renderer->RemoveEvent(AnimatorVisibilityCallback, this);
+ }
+ m_ContainedRenderers.clear();
+}
+
+void Animator::RecomputeContainedRenderersRecurse (Transform& transform)
+{
+
+ Renderer* renderer = transform.QueryComponent(Renderer);
+ if (renderer)
+ {
+ m_ContainedRenderers.push_back(renderer);
+ renderer->AddEvent(AnimatorVisibilityCallback, this);
+ }
+ Transform::iterator end = transform.end();
+ for (Transform::iterator i = transform.begin();i != end;++i)
+ {
+ RecomputeContainedRenderersRecurse(**i);
+ }
+}
+
+void Animator::InitializeVisibilityCulling ()
+{
+ if(!IsActive())
+ return;
+
+ ClearContainedRenderers ();
+
+ if (m_CullingMode == kCullBasedOnRenderers)
+ {
+ Transform& transform = GetComponent (Transform);
+ RecomputeContainedRenderersRecurse(transform);
+
+ if (m_ContainedRenderers.empty())
+ m_Visible = IsAnyRendererVisible();
+ }
+ else
+ {
+ m_Visible = true;
+ }
+}
+
+
+void Animator::ClearObject()
+{
+ InvokeEvent(kAnimatorClearEvent);
+
+ mecanim::animation::DestroyAnimationSetMemory(m_EvaluationDataSet.m_AnimationSetMemory, mAlloc);
+ mecanim::animation::DestroyControllerMemory(m_EvaluationDataSet.m_ControllerMemory, mAlloc);
+ DestroyAnimatorGenericBindings (m_EvaluationDataSet.m_GenericBindingConstant, mAlloc);
+ DestroyAvatarBindingConstant (m_EvaluationDataSet.m_AvatarBindingConstant, mAlloc);
+ if(m_EvaluationDataSet.m_AvatarWorkspace)
+ mecanim::animation::DestroyControllerWorkspace(m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace, mAlloc);
+ mecanim::animation::DestroyAvatarOutput(m_EvaluationDataSet.m_AvatarOutput, mAlloc);
+ mecanim::animation::DestroyAvatarInput(m_EvaluationDataSet.m_AvatarInput, mAlloc);
+ mecanim::animation::DestroyAvatarMemory(m_EvaluationDataSet.m_AvatarMemory, mAlloc);
+
+ mecanim::animation::DestroyAvatarWorkspace(m_EvaluationDataSet.m_AvatarWorkspace, mAlloc);
+
+ if(m_EvaluationDataSet.m_OwnsAvatar)
+ {
+ mecanim::animation::DestroyAvatarConstant( const_cast<mecanim::animation::AvatarConstant*>(m_EvaluationDataSet.m_AvatarConstant), mAlloc);
+ m_EvaluationDataSet.m_OwnsAvatar = false;
+ }
+
+ m_EvaluationDataSet.m_AvatarConstant = 0;
+ m_EvaluationDataSet.m_AvatarMemory = 0;
+ // It is very important to reset the memory size to zero because we use this member to determine if the data is blobified and ready to be copy by recorder
+ m_EvaluationDataSet.m_AvatarMemorySize = 0;
+ m_EvaluationDataSet.m_AvatarInput = 0;
+ m_EvaluationDataSet.m_AvatarOutput = 0;
+ m_EvaluationDataSet.m_AvatarWorkspace = 0;
+ m_EvaluationDataSet.m_ControllerConstant = 0;
+
+ m_EvaluationDataSet.m_GenericBindingConstant = 0;
+ m_EvaluationDataSet.m_AvatarBindingConstant = 0;
+
+ m_EvaluationDataSet.m_ControllerMemory = 0;
+ m_EvaluationDataSet.m_AnimationSetMemory = 0;
+
+ m_AnimatorControllerNode.Clear();
+ m_AnimatorAvatarNode.Clear();
+
+ m_SamplingDataSet.Reset();
+}
+
+void Animator::SetupAvatarMecanimDataSet(mecanim::animation::AvatarConstant const* avatarConstant, mecanim::memory::Allocator& allocator, Animator::MecanimDataSet& outMecanimDataSet)
+{
+ PROFILER_AUTO(gProfileSetupDataSet, this);
+
+ outMecanimDataSet.m_AvatarConstant = avatarConstant;
+
+ if (outMecanimDataSet.m_AvatarConstant == NULL)
+ {
+ outMecanimDataSet.m_OwnsAvatar = true;
+ outMecanimDataSet.m_AvatarConstant = mecanim::animation::CreateAvatarConstant(0,0,0,0,0,-1,math::xformIdentity(), allocator);
+ }
+
+ // It is very important to reset the memory size to zero because we use this member to determine if the data is blobified and ready to be copy by recorder
+ outMecanimDataSet.m_AvatarMemorySize = 0;
+ outMecanimDataSet.m_AvatarMemory = mecanim::animation::CreateAvatarMemory(outMecanimDataSet.m_AvatarConstant, allocator);
+ outMecanimDataSet.m_AvatarInput = mecanim::animation::CreateAvatarInput(outMecanimDataSet.m_AvatarConstant, allocator);
+ outMecanimDataSet.m_AvatarWorkspace = mecanim::animation::CreateAvatarWorkspace(outMecanimDataSet.m_AvatarConstant, allocator);
+ outMecanimDataSet.m_AvatarOutput = mecanim::animation::CreateAvatarOutput(outMecanimDataSet.m_AvatarConstant, m_HasTransformHierarchy, allocator);
+
+ Transform *effectiveRoot = GetAvatarRoot();
+ if (m_HasTransformHierarchy)
+ outMecanimDataSet.m_AvatarBindingConstant = CreateAvatarBindingConstant (*effectiveRoot, outMecanimDataSet.m_AvatarConstant, allocator);
+ else
+ outMecanimDataSet.m_AvatarBindingConstant = CreateAvatarBindingConstantOpt (*effectiveRoot, outMecanimDataSet.m_AvatarConstant, allocator);
+
+ // Setup AvatarX based on current transform
+ Transform& transform = GetComponent(Transform);
+ SetAvatarPosition(transform.GetPosition());
+ SetAvatarRotation(transform.GetRotation());
+ SetAvatarScale(transform.GetWorldScaleLossy());
+}
+
+void Animator::SetupControllerMecanimDataSet(mecanim::animation::ControllerConstant const* controllerConstant, UnityEngine::Animation::AnimationSetBindings const* animationSetBindings, mecanim::memory::Allocator& allocator, Animator::MecanimDataSet& outMecanimDataSet)
+{
+ outMecanimDataSet.m_ControllerConstant = controllerConstant;
+
+ Transform *effectiveRoot = GetAvatarRoot();
+ if (m_HasTransformHierarchy)
+ outMecanimDataSet.m_GenericBindingConstant = CreateAnimatorGenericBindings (*animationSetBindings, *effectiveRoot, outMecanimDataSet.m_AvatarConstant, outMecanimDataSet.m_ControllerConstant, allocator);
+ else
+ outMecanimDataSet.m_GenericBindingConstant = CreateAnimatorGenericBindingsOpt (*animationSetBindings, *effectiveRoot, outMecanimDataSet.m_AvatarConstant, outMecanimDataSet.m_ControllerConstant, allocator);
+
+ const mecanim::ValueArrayConstant* dynamicValuesConstant = outMecanimDataSet.m_GenericBindingConstant->controllerBindingConstant->m_DynamicValuesConstant;
+
+ outMecanimDataSet.m_ControllerMemory = mecanim::animation::CreateControllerMemory(outMecanimDataSet.m_ControllerConstant, outMecanimDataSet.m_AvatarConstant, animationSetBindings->animationSet, dynamicValuesConstant, allocator);
+ outMecanimDataSet.m_AvatarMemory->m_ControllerMemory = outMecanimDataSet.m_ControllerMemory;
+
+ outMecanimDataSet.m_AnimationSetMemory = mecanim::animation::CreateAnimationSetMemory(animationSetBindings->animationSet, outMecanimDataSet.m_GenericBindingConstant->allowConstantClipSamplingOptimization, allocator);
+
+ outMecanimDataSet.m_AvatarWorkspace->m_ControllerWorkspace = mecanim::animation::CreateControllerWorkspace(outMecanimDataSet.m_ControllerConstant, outMecanimDataSet.m_AvatarConstant, animationSetBindings->animationSet, dynamicValuesConstant, allocator);
+ outMecanimDataSet.m_AvatarOutput->m_DynamicValuesOutput = mecanim::CreateValueArray(dynamicValuesConstant, allocator);
+ outMecanimDataSet.m_AvatarInput->m_GotoStateInfos = allocator.ConstructArray<mecanim::statemachine::GotoStateInfo>(outMecanimDataSet.m_ControllerConstant->m_LayerCount);
+
+ UpdateLeafNodeDuration(*outMecanimDataSet.m_ControllerConstant,*animationSetBindings->animationSet,*outMecanimDataSet.m_ControllerMemory);
+}
+
+void Animator::CreateObject()
+{
+ if(!IsActive())
+ return;
+
+ SET_ALLOC_OWNER(this);
+ PROFILER_AUTO(gAnimatorInitialize, this);
+
+ ClearObject();
+
+ SETPROFILERLABEL(Animator);
+
+ // ///////////////////////////////////////////////////
+ // Setup is split in two part: Avatar and controller
+ // both part are independant
+
+ // ///////////////////////////////////////////////////
+ // 1. Avatar setup and binding
+ mecanim::animation::AvatarConstant* avatarConstant = NULL;
+ if(m_Avatar.IsValid())
+ {
+ avatarConstant = m_Avatar->GetAsset();
+ m_Avatar->AddObjectUser(m_AnimatorAvatarNode);
+ }
+ SetupAvatarMecanimDataSet(avatarConstant, mAlloc, m_EvaluationDataSet);
+
+ // ///////////////////////////////////////////////////
+ // 2. Controller setup and binding
+ if(m_Controller.IsNull())
+ return;
+
+ m_Controller->AddObjectUser(m_AnimatorControllerNode);
+
+ mecanim::animation::ControllerConstant* controllerConstant = m_Controller->GetAsset();
+
+ UnityEngine::Animation::AnimationSetBindings* animationSetBindings = m_Controller->GetAnimationSetBindings();
+ if (animationSetBindings == NULL)
+ return;
+
+ SetupControllerMecanimDataSet(controllerConstant, animationSetBindings, mAlloc, m_EvaluationDataSet);
+}
+
+std::string Animator::GetPerformanceHints()
+{
+ if (m_EvaluationDataSet.m_GenericBindingConstant == NULL)
+ return "Not initialized";
+
+ std::string info;
+ ///@TODO: more accuaracy...
+
+// if (!m_EvaluationDataSet.m_GenericBindingConstant->allowConstantClipSamplingOptimization)
+// info += "Constant curve optimization disabled (Instance default values differ from constant curve values)\n";
+// else
+// info += Format ("Constant curve optimization enabled. %d of %d were eliminated.\n", m_EvaluationDataSet.m_GenericBindingConstant->controllerBindingConstant->m_AnimationSet->m_DynamicReducedValuesConstant->m_Count, GetAnimationSet()->m_DynamicFullValuesConstant->m_Count);
+
+ //@TODO: Bound curves overview like animationclip stats...
+
+ info += "Instance memory: " + FormatBytes (GetRuntimeMemorySize());
+
+ return info;
+}
+
+void Animator::PrepareForPlayback()
+{
+ if(m_EvaluationDataSet.m_AvatarMemorySize == 0)
+ {
+ /// Blobify memory to be able to use inplace allocator during playback
+ mecanim::animation::AvatarMemory *mem = m_EvaluationDataSet.m_AvatarMemory;
+ m_EvaluationDataSet.m_AvatarMemory = CopyBlob(*mem,mAlloc,m_EvaluationDataSet.m_AvatarMemorySize);
+ mecanim::animation::DestroyAvatarMemory(mem, mAlloc);
+ }
+}
+
+void Animator::StartRecording(int frameCount)
+{
+ PrepareForPlayback();
+
+ if(m_RecorderMode == ePlayback)
+ {
+ WarningStringIfLoggingActive("Can't call StartRecording while in playback mode. You must call StopPlayback.");
+ return;
+ }
+
+ m_AvatarPlayback.Init(frameCount);
+ m_RecorderMode = eRecord;
+}
+void Animator::StopRecording()
+{
+ m_RecorderMode = eNormal;
+}
+
+float Animator::GetRecorderStartTime()
+{
+ return m_AvatarPlayback.StartTime();
+}
+
+float Animator::GetRecorderStopTime()
+{
+ return m_AvatarPlayback.StopTime();
+}
+
+void Animator::StartPlayback()
+{
+ if(m_RecorderMode == eRecord)
+ {
+ WarningStringIfLoggingActive("Can't call StartPlayback while in record mode. You must call StopRecording.");
+ return;
+ }
+
+ m_RecorderMode = ePlayback;
+}
+
+
+void Animator::SetPlaybackTimeInternal(float time)
+{
+ float effectiveTime = 0;
+ mecanim::animation::AvatarMemory* memory = m_AvatarPlayback.PlayFrame(time, effectiveTime);
+
+ if(memory != 0 )
+ {
+ if(effectiveTime > time )
+ {
+ if(IsAutoPlayingBack())
+ WarningStringIfLoggingActive ("Cannot rewind Animator Recorder anymore. No more recorded data");
+ else
+ WarningStringIfLoggingActive("Animator Recorder does not have recorded data at given time");
+ }
+ else if(time > m_AvatarPlayback.StopTime())
+ {
+ WarningStringIfLoggingActive("Animator Recorder does not have recorded data at given time, Animator will update based on current AnimatorParameters");
+ }
+
+ PrepareForPlayback();
+
+ m_PlaybackTime = time;
+ mecanim::memory::InPlaceAllocator inPlaceAlloc(m_EvaluationDataSet.m_AvatarMemory, m_EvaluationDataSet.m_AvatarMemorySize);
+ mecanim::animation::AvatarMemory* memoryCopy = CopyBlob(*memory, inPlaceAlloc, m_EvaluationDataSet.m_AvatarMemorySize);
+ if(memoryCopy != 0)
+ m_EvaluationDataSet.m_AvatarMemory = memoryCopy;
+ else
+ {
+ // We don't have enough memory to fulfill the request with the current m_EvaluationDataSet.m_AvatarMemory memory block
+ // Let's try another time with a memory block big enough.
+ mecanim::animation::DestroyAvatarMemory(m_EvaluationDataSet.m_AvatarMemory, mAlloc );
+
+ UInt8* ptr = reinterpret_cast<UInt8*>(mAlloc.Allocate(m_EvaluationDataSet.m_AvatarMemorySize, ALIGN_OF(mecanim::animation::AvatarMemory)));
+ mecanim::memory::InPlaceAllocator inPlaceAlloc(ptr, m_EvaluationDataSet.m_AvatarMemorySize);
+ m_EvaluationDataSet.m_AvatarMemory = CopyBlob(*memory, inPlaceAlloc, m_EvaluationDataSet.m_AvatarMemorySize);
+ if(m_EvaluationDataSet.m_AvatarMemory == 0 )
+ {
+ WarningStringIfLoggingActive ("Can't playback from recorder, cannot allocate memory for recorded data.");
+ m_PlaybackDeltaTime = 0;
+ m_PlaybackTime = 0;
+ return;
+ }
+ }
+ m_PlaybackDeltaTime = time-effectiveTime;
+ }
+ else
+ {
+ WarningStringIfLoggingActive ("Can't playback from recorder, no recorded data found");
+ m_PlaybackDeltaTime = 0;
+ m_PlaybackTime = 0;
+ }
+}
+
+float Animator::GetPlaybackTime()
+{
+
+ if(m_RecorderMode != ePlayback)
+ {
+ WarningStringIfLoggingActive("Can't call GetPlaybackTime while not in playback mode. You must call StartPlayback before.");
+ return -1;
+ }
+
+ return m_PlaybackTime;
+}
+
+void Animator::SetPlaybackTime(float time)
+{
+ if(m_RecorderMode != ePlayback)
+ {
+ WarningStringIfLoggingActive("Can't call SetPlaybackTime while not in playback mode. You must call StartPlayback before.");
+ return ;
+ }
+
+ SetPlaybackTimeInternal(time);
+}
+
+void Animator::StopPlayback()
+{
+ m_RecorderMode = eNormal;
+}
+
+bool Animator::IsOptimizable() const
+{
+ return m_Avatar.IsValid();
+}
+
+bool Animator::IsHuman() const
+{
+ return m_Avatar.IsValid() && m_Avatar->IsHuman();
+}
+
+bool Animator::HasRootMotion() const
+{
+ return m_Avatar.IsValid() && m_Avatar->HasRootMotion();
+}
+
+float Animator::GetHumanScale() const
+{
+ return m_Avatar.IsValid() ? m_Avatar->GetHumanScale() : 1.f;
+}
+
+void Animator::SetApplyRootMotion (bool rootMotion)
+{
+ if (m_ApplyRootMotion != rootMotion)
+ {
+ m_ApplyRootMotion = rootMotion;
+ SetDirty();
+ }
+}
+
+void Animator::SetAnimatePhysics (bool animatePhysics)
+{
+ if (m_AnimatePhysics != animatePhysics)
+ {
+ m_AnimatePhysics = animatePhysics;
+ RemoveFromManager();
+ AddToManager();
+ SetDirty();
+ }
+}
+
+void Animator::SetHasTransformHierarchy (bool value)
+{
+ if (m_HasTransformHierarchy != value)
+ {
+ m_HasTransformHierarchy = value;
+
+ // I need to validate this
+ CreateObject();
+ InitializeVisibilityCulling();
+
+ SetDirty();
+ }
+}
+
+GetSetValueResult Animator::SetFloatDamp(int id, float value, float dampTime, float deltaTime)
+{
+ if (dampTime > 0)
+ {
+ mecanim::dynamics::ScalDamp damper;
+ GetValue(id, damper.m_Value);
+ damper.m_DampTime = dampTime;
+ damper.Evaluate(value, deltaTime);
+
+ return SetValue(id, damper.m_Value);
+ }
+ else
+ {
+ return SetValue(id, value);
+ }
+}
+
+GetSetValueResult Animator::SetFloat(int id, float value)
+{
+ return SetValue(id, value);
+}
+
+GetSetValueResult Animator::SetBool(int id, bool value)
+{
+ return SetValue(id, value);
+}
+
+GetSetValueResult Animator::SetInteger(int id, int value)
+{
+ return SetValue(id, (mecanim::int32_t)value);
+}
+
+GetSetValueResult Animator::GetFloat(int id, float& output)
+{
+ return GetValue(id, output);
+}
+
+GetSetValueResult Animator::GetBool(int id, bool& output)
+{
+ return GetValue(id, output);
+}
+
+GetSetValueResult Animator::GetInteger(int id, int& output)
+{
+ mecanim::int32_t temp;
+ GetSetValueResult res = GetValue(id, temp);
+ output = temp;
+ return res;
+}
+
+GetSetValueResult Animator::ResetTrigger(int id)
+{
+ return SetValue(id, false);
+}
+
+GetSetValueResult Animator::SetTrigger(int id)
+{
+ return SetValue(id, true);
+}
+
+bool Animator::HasParameter(int id)
+{
+ if(!IsInitialize())
+ return false;
+
+ mecanim::int32_t i = mecanim::FindValueIndex(m_EvaluationDataSet.m_ControllerConstant->m_Values.Get(), id);
+ return i != -1;
+}
+
+bool Animator::GetMuscleValue(int id, float *value)
+{
+ *value = 0;
+ if (this->m_Avatar.IsNull() || !this->m_Avatar->IsHuman())
+ return false;
+
+ // it is the desire behavior to return true if the Animator is not initialized correctly because the AnimationWindows is using this function to
+ // find what is the newly added curve type.
+ // Also has a side effect adding any kind of curve like MotionT trigger a ClearObject() on the animator because we are adding new curve to a clip
+ // used by the animator.
+ mecanim::int32_t muscleIndex = mecanim::animation::FindMuscleIndex(id);
+ bool ret = muscleIndex != -1;
+
+ if(m_SamplingDataSet->m_GenericBindingConstant == NULL)
+ return ret;
+
+ if (ret)
+ *value = mecanim::animation::GetMuscleCurveValue(*m_SamplingDataSet->m_AvatarOutput->m_HumanPoseOutput, m_SamplingDataSet->m_AvatarOutput->m_MotionOutput->m_MotionX,muscleIndex);
+
+ return ret;
+}
+
+GetSetValueResult Animator::ParameterControlledByCurve(int id)
+{
+ if (!IsInitialize())
+ return kAnimatorNotInitialized;
+
+ mecanim::int32_t index = mecanim::FindValueIndex(m_EvaluationDataSet.m_ControllerConstant->m_Values.Get(), id);
+ if (index == -1)
+ return kParameterDoesNotExist;
+
+ if (m_EvaluationDataSet.m_GenericBindingConstant->controllerBindingConstant->m_AnimationSet->m_AdditionalIndexArray[index] != -1)
+ return kParameterIsControlledByCurve;
+ else
+ return kGetSetSuccess;
+}
+
+Vector3f Animator::GetAvatarPosition()
+{
+ if(!IsAvatarInitialize())
+ return Vector3f(0,0,0);
+
+ return float4ToVector3f(m_EvaluationDataSet.m_AvatarMemory->m_AvatarX.t);
+}
+
+void Animator::SetAvatarPosition(const Vector3f& pos)
+{
+ ///@TODO: Give error messages...
+ if (!IsAvatarInitialize())
+ return ;
+
+ m_EvaluationDataSet.m_AvatarMemory->m_AvatarX.t = Vector3fTofloat4(pos);
+}
+
+Quaternionf Animator::GetAvatarRotation()
+{
+ if(!IsAvatarInitialize())
+ return Quaternionf(0,0,0,1);
+
+
+ return float4ToQuaternionf(m_EvaluationDataSet.m_AvatarMemory->m_AvatarX.q);
+}
+
+void Animator::SetAvatarRotation(const Quaternionf& q)
+{
+ if(!IsAvatarInitialize())
+ return;
+
+ m_EvaluationDataSet.m_AvatarMemory->m_AvatarX.q = QuaternionfTofloat4(q);
+}
+
+Vector3f Animator::GetAvatarScale()
+{
+ if(!IsAvatarInitialize())
+ return Vector3f(1,1,1);
+
+ return float4ToVector3f(m_EvaluationDataSet.m_AvatarMemory->m_AvatarX.s);
+}
+
+void Animator::SetAvatarScale(const Vector3f& scale)
+{
+ if (!IsAvatarInitialize())
+ return ;
+
+ m_EvaluationDataSet.m_AvatarMemory->m_AvatarX.s = Vector3fTofloat4(scale,1);
+}
+
+Vector3f Animator::GetDeltaPosition()
+{
+ return m_DeltaPosition;
+}
+
+Quaternionf Animator::GetDeltaRotation()
+{
+ return m_DeltaRotation;
+}
+
+Vector3f Animator::GetBodyPosition()
+{
+ if(!IsAvatarInitialize())
+ return Vector3f();
+
+ mecanim::animation::AvatarConstant const* avatar = m_EvaluationDataSet.m_AvatarConstant;
+
+ if(avatar->isHuman())
+ {
+ return float4ToVector3f(m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_RootX.t);
+ }
+
+ return Vector3f();
+}
+
+Quaternionf Animator::GetBodyRotation()
+{
+ if(!IsAvatarInitialize())
+ return Quaternionf();
+
+ mecanim::animation::AvatarConstant const * avatar = m_EvaluationDataSet.m_AvatarConstant;
+
+ if(avatar->isHuman())
+ {
+ return float4ToQuaternionf(m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_RootX.q);
+ }
+
+ return Quaternionf();
+}
+
+void Animator::SetBodyPosition(Vector3f const& bodyPosition)
+{
+ if(!IsAvatarInitialize() || !m_EvaluationDataSet.m_AvatarConstant->isHuman())
+ return;
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_RootX.t = Vector3fTofloat4(bodyPosition);
+}
+
+void Animator::SetBodyRotation(Quaternionf const& bodyRotation)
+{
+ if(!IsAvatarInitialize() || !m_EvaluationDataSet.m_AvatarConstant->isHuman())
+ return;
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_RootX.q = QuaternionfTofloat4(bodyRotation);
+}
+
+Vector3f Animator::GetPivotPosition()
+{
+ return m_PivotPosition;
+}
+
+Vector3f Animator::GetTargetPosition()
+{
+ return m_TargetPosition;
+}
+
+Quaternionf Animator::GetTargetRotation()
+{
+ if (m_EvaluationDataSet.m_AvatarInput->m_TargetIndex >= mecanim::animation::kTargetLeftFoot && m_EvaluationDataSet.m_AvatarInput->m_TargetIndex <= mecanim::animation::kTargetRightHand)
+ {
+ return m_TargetRotation * float4ToQuaternionf(mecanim::human::HumanGetGoalOrientationOffset(mecanim::human::Goal(m_EvaluationDataSet.m_AvatarInput->m_TargetIndex - mecanim::animation::kTargetLeftFoot)));
+ }
+
+ return m_TargetRotation;
+}
+
+float Animator::GetGravityWeight()
+{
+ if(m_EvaluationDataSet.m_AvatarOutput && m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput)
+ {
+ return m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_GravityWeight;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+bool Animator::IsBoneTransform(Transform *transform)
+{
+ if(!IsAvatarInitialize())
+ return false;
+
+ bool ret = false;
+
+ if (m_HasTransformHierarchy)
+ {
+ for(int boneIter = 0; !ret && boneIter < m_EvaluationDataSet.m_AvatarBindingConstant->skeletonBindingsCount; boneIter++)
+ {
+ ret = m_EvaluationDataSet.m_AvatarBindingConstant->skeletonBindings[boneIter] == transform;
+ }
+ }
+ else
+ {
+ for (int exposedIndex = 0; !ret && exposedIndex < m_EvaluationDataSet.m_AvatarBindingConstant->exposedTransformCount; exposedIndex++)
+ {
+ ret = (m_EvaluationDataSet.m_AvatarBindingConstant->exposedTransforms[exposedIndex].transform == transform) &&
+ (m_EvaluationDataSet.m_AvatarBindingConstant->exposedTransforms[exposedIndex].skeletonIndex != -1);
+ }
+ }
+
+ return ret;
+}
+
+Transform* Animator::GetBoneTransform(int humanId)
+{
+ if(!IsAvatarInitialize())
+ return 0;
+
+ Transform *ret = 0;
+
+ mecanim::animation::AvatarConstant* cst = GetAvatar()->GetAsset();
+
+ if(cst && cst->isHuman())
+ {
+ int humanBoneId = HumanTrait::GetBoneId(*GetAvatar(),humanId);
+
+ if( humanBoneId == -1)
+ return NULL;
+
+ if (m_HasTransformHierarchy)
+ {
+ ret = m_EvaluationDataSet.m_AvatarBindingConstant->skeletonBindings[cst->m_HumanSkeletonIndexArray[humanBoneId]];
+ }
+ else
+ {
+ int skeletonIndex = cst->m_HumanSkeletonIndexArray[humanBoneId];
+ for (int exposedIndex = 0; exposedIndex < m_EvaluationDataSet.m_AvatarBindingConstant->exposedTransformCount; exposedIndex++)
+ {
+ const ExposedTransform& exposedTransform = m_EvaluationDataSet.m_AvatarBindingConstant->exposedTransforms[exposedIndex];
+ if (exposedTransform.skeletonIndex == skeletonIndex)
+ {
+ ret = exposedTransform.transform;
+ break;
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+void Animator::MatchTarget(Vector3f const& matchPosition, Quaternionf const& matchRotation, int targetIndex, const MatchTargetWeightMask & mask, float startNormalizedTime, float targetNormalizedTime)
+{
+ // It's only possible to match a target on the first layer
+ const int layerIndex = 0;
+
+ if(!ValidateTargetIndex(targetIndex) || IsMatchingTarget() || !IsInitialize())
+ return;
+
+ if(IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1) && IsInTransitionInternal(layerIndex))
+ {
+ WarningStringIfLoggingActive("Calling Animator.MatchTarget while in transition does not have any effect");
+ return;
+ }
+
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+ mecanim::statemachine::StateMachineMemory const* apStateMachineMem = m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex].Get();
+
+ float internalTime = apStateMachineMem->m_StateMemoryArray[apStateMachineMem->m_CurrentStateIndex]->m_PreviousTime;
+
+ float intTime = 0 ;
+ float currentStateTime = math::modf(internalTime, intTime);
+
+ float effectiveStartTime = 0;
+ float effectiveTargetTime = targetNormalizedTime + intTime;
+
+ if(currentStateTime <= startNormalizedTime)
+ effectiveStartTime = startNormalizedTime + intTime;
+ else if( currentStateTime > startNormalizedTime && currentStateTime < targetNormalizedTime)
+ effectiveStartTime = currentStateTime + intTime;
+ else
+ {
+ // Passed target time, wait for next loop
+ effectiveStartTime = startNormalizedTime + intTime + 1.0f;
+ effectiveTargetTime += 1.0f;
+ }
+
+ AnimatorStateInfo animatorInfo;
+ GetAnimatorStateInfo(layerIndex, true, animatorInfo);
+ if (!animatorInfo.loop != 0 && targetNormalizedTime < effectiveStartTime )
+ return;
+
+ m_MatchTargetMask = mask;
+
+ m_MatchStartTime = effectiveStartTime;
+ m_MatchStateID = animatorInfo.nameHash;
+ m_MatchPosition = matchPosition;
+ m_MatchRotation = SqrMagnitude(matchRotation) > 0 ? matchRotation : Quaternionf::identity();
+ m_EvaluationDataSet.m_AvatarInput->m_TargetIndex = targetIndex;
+ m_EvaluationDataSet.m_AvatarInput->m_TargetTime = effectiveTargetTime < effectiveStartTime ? effectiveTargetTime + 1.f : effectiveTargetTime;
+}
+
+void Animator::InterruptMatchTarget(bool completeMatch)
+{
+ if(completeMatch)
+ m_MustCompleteMatch = true;
+ else
+ {
+ m_MatchStartTime = -1;
+ m_MatchStateID = -1;
+ }
+}
+
+bool Animator::IsMatchingTarget()const
+{
+ if(!IsInitialize())
+ return false;
+
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[0]->m_StateMachineIndex;
+ mecanim::statemachine::StateMachineConstant const* apStateMachineConst = m_EvaluationDataSet.m_ControllerConstant->m_StateMachineArray[stateMachineIndex].Get();
+ mecanim::statemachine::StateMachineMemory const* apStateMachineMem = m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex].Get();
+ mecanim::statemachine::StateConstant const* state = apStateMachineConst->m_StateConstantArray[apStateMachineMem->m_CurrentStateIndex].Get();
+
+ return m_MatchStartTime >= 0 && CompareStateID (state, m_MatchStateID);
+}
+
+void Animator::SetTarget(int targetIndex, float targetNormalizedTime)
+{
+ if(!ValidateTargetIndex(targetIndex) || !IsInitialize())
+ return;
+
+ if(IsMatchingTarget())
+ {
+ ErrorString("Calling Animator::SetTarget while already Matching Target does not have any effect");
+ }
+
+ m_EvaluationDataSet.m_AvatarInput->m_TargetIndex = targetIndex;
+ m_EvaluationDataSet.m_AvatarInput->m_TargetTime = targetNormalizedTime;
+}
+
+void Animator::SetSpeed(float speed)
+{
+
+ m_Speed = speed;
+}
+
+float Animator::GetSpeed() const
+{
+ return m_Speed;
+}
+
+bool GetLayerAndStateIndex(mecanim::animation::ControllerConstant const* controllerConstant, mecanim::uint32_t id, int* outLayer, int* outStateIndex)
+{
+ for (int i=0;i < controllerConstant->m_LayerCount;i++)
+ {
+ int index = controllerConstant->m_LayerArray[i]->m_StateMachineIndex;
+
+ // Ignore synced layers
+ if (controllerConstant->m_LayerArray[i]->m_StateMachineMotionSetIndex != 0)
+ continue;
+
+ mecanim::int32_t stateIndex = mecanim::statemachine::GetStateIndex(controllerConstant->m_StateMachineArray[index].Get(), id);
+ if (stateIndex != -1)
+ {
+ *outStateIndex = stateIndex;
+ *outLayer = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+void Animator::GotoState(int layerIndex, int stateId, float normalizedTime, float transitionDuration, float transitionTime)
+{
+ if(!IsInitialize() )
+ return ;
+
+ // Automatically find a good layer index
+ if (layerIndex == -1)
+ {
+ int stateIndex;
+ // StateId = 0 means active state
+ if (stateId == 0)
+ layerIndex = 0;
+ else if (!GetLayerAndStateIndex(m_EvaluationDataSet.m_ControllerConstant, stateId, &layerIndex, &stateIndex))
+ {
+ ErrorString("Animator.GotoState: State could not be found");
+ }
+ }
+
+ if(!ValidateLayerIndex(layerIndex))
+ return;
+
+ const mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+
+ if(stateMachineIndex == -1)
+ return;
+
+ if(stateMachineIndex >= m_EvaluationDataSet.m_ControllerConstant->m_StateMachineCount)
+ {
+ ErrorString("Animator.GotoState: Cannot find statemachine");
+ return;
+ }
+
+ const mecanim::uint32_t motionSetIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineMotionSetIndex;
+ if(motionSetIndex != 0)
+ {
+ ErrorString("Calling Animator.GotoState on Synchronize layer");
+ return;
+ }
+
+ #if DEBUGMODE
+ if (stateId != 0 && mecanim::statemachine::GetStateIndex(m_EvaluationDataSet.m_ControllerConstant->m_StateMachineArray[stateMachineIndex].Get(), stateId) == -1)
+ {
+ ErrorString("Animator.GotoState: State could not be found");
+ }
+ #endif
+
+ // When no explicit normalizedTime is specified we will simply start the clip at the start if it is not already playing.
+ if (normalizedTime == -std::numeric_limits<float>::infinity())
+ {
+ AnimatorStateInfo info;
+ GetAnimatorStateInfo(layerIndex, true, info);
+
+ // If the state is not currently playing -> Start Playing it
+ bool nameMatches = info.pathHash == stateId || info.nameHash == stateId;
+ if (nameMatches)
+ return;
+ normalizedTime = 0.0F;
+ }
+
+ m_EvaluationDataSet.m_ControllerMemory->m_StateMachineMemory[stateMachineIndex]->m_ActiveGotoState = true;
+
+ m_EvaluationDataSet.m_AvatarInput->m_GotoStateInfos[layerIndex].m_StateID = stateId;
+ m_EvaluationDataSet.m_AvatarInput->m_GotoStateInfos[layerIndex].m_NormalizedTime = normalizedTime;
+ m_EvaluationDataSet.m_AvatarInput->m_GotoStateInfos[layerIndex].m_TransitionDuration = transitionDuration;
+ m_EvaluationDataSet.m_AvatarInput->m_GotoStateInfos[layerIndex].m_TransitionTime = transitionTime;
+}
+
+#define VALIDATE_IK_GOAL(x) \
+if (!GetBuildSettings().hasAdvancedVersion) \
+ return x; \
+if(!ValidateGoalIndex(index) || !IsAvatarInitialize()) \
+ return x; \
+
+#define VALIDATE_IK_GOAL_VOID() \
+if (!GetBuildSettings().hasAdvancedVersion)\
+ return; \
+if(!ValidateGoalIndex(index) || !IsAvatarInitialize()) \
+ return; \
+
+
+Vector3f Animator::GetGoalPosition(int index)
+{
+ VALIDATE_IK_GOAL(Vector3f::zero)
+
+ return float4ToVector3f(m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[index].m_X.t);
+}
+
+void Animator::SetGoalPosition(int index, Vector3f const& pos)
+{
+ VALIDATE_IK_GOAL_VOID()
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[index].m_X.t = Vector3fTofloat4(pos);
+}
+
+Quaternionf Animator::GetGoalRotation(int index)
+{
+ VALIDATE_IK_GOAL(Quaternionf::identity())
+
+ return float4ToQuaternionf(math::normalize(math::quatMul(m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[index].m_X.q,mecanim::human::HumanGetGoalOrientationOffset(mecanim::human::Goal(index)))));
+}
+
+void Animator::SetGoalRotation(int index, Quaternionf const& rot)
+{
+ VALIDATE_IK_GOAL_VOID()
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[index].m_X.q = math::normalize(math::quatMul(QuaternionfTofloat4(rot),math::quatConj(mecanim::human::HumanGetGoalOrientationOffset(mecanim::human::Goal(index)))));
+}
+
+void Animator::SetGoalWeightPosition(int index, float value)
+{
+ VALIDATE_IK_GOAL_VOID()
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[index].m_WeightT = value;
+}
+
+void Animator::SetGoalWeightRotation(int index, float value)
+{
+ VALIDATE_IK_GOAL_VOID()
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[index].m_WeightR = value;
+}
+
+float Animator::GetGoalWeightPosition(int index)
+{
+ VALIDATE_IK_GOAL(0.0F)
+
+ return m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[index].m_WeightT;
+}
+
+float Animator::GetGoalWeightRotation(int index)
+{
+ VALIDATE_IK_GOAL(0.0F)
+
+ return m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[index].m_WeightR;
+}
+
+#define VALIDATE_LOOKAT if (!GetBuildSettings().hasAdvancedVersion || !IsAvatarInitialize()) return;
+
+
+void Animator::SetLookAtPosition(Vector3f lookAtPosition)
+{
+ VALIDATE_LOOKAT
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_LookAtPosition = Vector3fTofloat4(lookAtPosition);
+}
+
+void Animator::SetLookAtClampWeight(float weight)
+{
+ VALIDATE_LOOKAT
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_LookAtWeight.x() = weight;
+}
+
+void Animator::SetLookAtBodyWeight(float weight)
+{
+ VALIDATE_LOOKAT
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_LookAtWeight.y() = weight;
+}
+
+void Animator::SetLookAtHeadWeight(float weight)
+{
+ VALIDATE_LOOKAT
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_LookAtWeight.z() = weight;
+}
+
+void Animator::SetLookAtEyesWeight(float weight)
+{
+ VALIDATE_LOOKAT
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_LookAtWeight.w() = weight;
+}
+
+int Animator::GetLayerCount()const
+{
+ if(!IsInitialize())
+ return 0;
+
+ return m_EvaluationDataSet.m_ControllerConstant->m_LayerCount;
+}
+
+std::string Animator::GetLayerName(int layerIndex)
+{
+ if(!ValidateLayerIndex(layerIndex))
+ return "";
+
+ return m_Controller->StringFromID(m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_Binding);
+}
+
+float Animator::GetLayerWeight(int layerIndex)
+{
+ if(!ValidateLayerIndex(layerIndex))
+ return layerIndex == 0 ? 1 : 0 ;
+
+ return m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_LayerWeights[layerIndex];
+}
+
+void Animator::SetLayerWeight(int layerIndex, float w)
+{
+ if(!ValidateLayerIndex(layerIndex))
+ return;
+
+ m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_LayerWeights[layerIndex] = w;
+}
+
+float Animator::GetPivotWeight()
+{
+ if(!IsAvatarInitialize())
+ return 0;
+
+ return m_EvaluationDataSet.m_AvatarMemory->m_PivotWeight;
+}
+
+bool Animator::IsInTransitionInternal(int layerIndex)const
+{
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+
+ if(stateMachineIndex == mecanim::DISABLED_SYNCED_LAYER_IN_NON_PRO)
+ return false;
+
+ mecanim::statemachine::StateMachineMemory const* apStateMachineMem = m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex].Get();
+ return apStateMachineMem->m_InTransition;
+}
+
+bool Animator::IsInTransition(int layerIndex)const
+{
+ if(!ValidateLayerIndex(layerIndex) )
+ return false;
+
+ return IsInTransitionInternal(layerIndex);
+}
+
+AnimationClipVector Animator::GetAnimationClips()const
+{
+ return m_Controller->GetAnimationClips();
+}
+
+UnityEngine::Animation::AnimationSetBindings* Animator::GetAnimationSetBindings()const
+{
+ return m_Controller->GetAnimationSetBindings();
+}
+
+bool Animator::GetAnimationClipState (int layerIndex, bool currentState, dynamic_array<AnimationInfo>& output)
+{
+ if (!ValidateLayerIndex(layerIndex))
+ return false;
+
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+
+ mecanim::uint32_t motionSetIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineMotionSetIndex;
+ mecanim::statemachine::StateMachineMemory const* apStateMachineMem = m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex].Get();
+
+ float blendFactor = currentState ? 1 - m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_StateMachineOutput[stateMachineIndex]->m_BlendFactor : m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_StateMachineOutput[stateMachineIndex]->m_BlendFactor;
+
+ mecanim::statemachine::BlendNodeLayer *blendNodeLayer = currentState ? &m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_StateMachineOutput[stateMachineIndex]->m_Left.m_BlendNodeLayer[motionSetIndex] :
+ apStateMachineMem->m_InTransition ? &m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_StateMachineOutput[stateMachineIndex]->m_Right.m_BlendNodeLayer[motionSetIndex] : 0;
+
+ int clipCount = blendNodeLayer ? blendNodeLayer->m_OutputCount : 0 ;
+
+ AnimationClipVector const allClips = GetAnimationClips();
+
+ int layerClipOffset = GetLayerClipOffset(layerIndex);
+
+ output.resize_uninitialized(clipCount);
+ for(int i = 0 ; i < clipCount ; i++)
+ {
+ int index = blendNodeLayer->m_OutputIndexArray[i];
+
+ AnimationInfo& animInfo = output[i];
+ animInfo.clip = allClips[index+layerClipOffset];
+ animInfo.weight = blendNodeLayer->m_OutputBlendArray[i] * blendFactor;
+ }
+
+ return true;
+}
+
+
+bool Animator::GetAnimatorStateInfo (int layerIndex, bool currentState, AnimatorStateInfo& output)
+{
+ if (!ValidateLayerIndex(layerIndex))
+ return false;
+
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+ const mecanim::statemachine::StateMachineConstant* apStateMachineConst = m_EvaluationDataSet.m_ControllerConstant->m_StateMachineArray[stateMachineIndex].Get();
+ mecanim::statemachine::StateMachineMemory const* apStateMachineMem = m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex].Get();
+
+ mecanim::uint32_t stateIndex = currentState ? apStateMachineMem->m_CurrentStateIndex : apStateMachineMem->m_InTransition ? apStateMachineMem->m_NextStateIndex : apStateMachineMem->m_StateMemoryCount;
+ if (stateIndex < apStateMachineMem->m_StateMemoryCount)
+ {
+ output.nameHash = apStateMachineConst->m_StateConstantArray[stateIndex]->m_NameID;
+ output.pathHash = apStateMachineConst->m_StateConstantArray[stateIndex]->m_PathID;
+ output.normalizedTime = apStateMachineMem->m_StateMemoryArray[stateIndex]->m_PreviousTime;
+ output.length = apStateMachineMem->m_StateMemoryArray[stateIndex]->m_Duration;
+ output.tagHash = apStateMachineConst->m_StateConstantArray[stateIndex]->m_TagID;
+ output.loop = apStateMachineConst->m_StateConstantArray[stateIndex]->m_Loop ? 1 : 0;
+
+ return true;
+
+ }
+ else
+ return false;
+}
+
+
+bool Animator::GetAnimatorTransitionInfo(int layerIndex, AnimatorTransitionInfo& info)
+{
+ if(!ValidateLayerIndex(layerIndex))
+ return false;
+
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+ const mecanim::statemachine::StateMachineConstant* apStateMachineConst = m_EvaluationDataSet.m_ControllerConstant->m_StateMachineArray[stateMachineIndex].Get();
+
+ mecanim::statemachine::StateMachineMemory const* apStateMachineMem = m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex].Get();
+
+ if (apStateMachineMem->m_InTransition)
+ {
+ mecanim::statemachine::TransitionConstant const* transition = mecanim::statemachine::GetTransitionConstant(apStateMachineConst, apStateMachineConst->m_StateConstantArray[apStateMachineMem->m_CurrentStateIndex].Get(), apStateMachineMem->m_TransitionId);
+ if(transition)
+ {
+ info.nameHash = transition->m_ID;
+ info.userNameHash = transition->m_UserID;
+ }
+ // Dynamic transition doesn't exist, they are created on demand by user.
+ else
+ {
+ info.nameHash = 0;
+ info.userNameHash = 0;
+ }
+ info.normalizedTime = apStateMachineMem->m_TransitionTime;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+string Animator::GetAnimatorStateName (int layerIndex, bool currentState)
+{
+ if (!ValidateLayerIndex(layerIndex))
+ return "";
+
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+ const mecanim::statemachine::StateMachineConstant* apStateMachineConst = m_EvaluationDataSet.m_ControllerConstant->m_StateMachineArray[stateMachineIndex].Get();
+ mecanim::statemachine::StateMachineMemory const* apStateMachineMem = m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex].Get();
+
+ mecanim::uint32_t stateIndex = currentState ? apStateMachineMem->m_CurrentStateIndex : apStateMachineMem->m_InTransition ? apStateMachineMem->m_NextStateIndex : apStateMachineMem->m_StateMemoryCount;
+ if (stateIndex < apStateMachineMem->m_StateMemoryCount)
+ {
+ return m_Controller->StringFromID(apStateMachineConst->m_StateConstantArray[stateIndex]->m_PathID);
+ }
+
+ return "";
+}
+
+void Animator::GetRootBlendTreeConstantAndWorkspace (int layerIndex, int stateHash, mecanim::animation::BlendTreeNodeConstant const* & constant, mecanim::animation::BlendTreeWorkspace*& workspace)
+{
+ if(!ValidateLayerIndex(layerIndex))
+ return;
+
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+ mecanim::uint32_t motionSetIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineMotionSetIndex;
+
+ mecanim::statemachine::StateMachineConstant const* apStateMachineConst = m_EvaluationDataSet.m_ControllerConstant->m_StateMachineArray[stateMachineIndex].Get();
+ mecanim::statemachine::StateMachineWorkspace* apStateMachineWorkspace = m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_StateMachineWorkspace[stateMachineIndex];
+
+ for (int i=0; i<apStateMachineConst->m_StateConstantCount; i++)
+ {
+ if (CompareStateID (apStateMachineConst->m_StateConstantArray[i].Get(), stateHash))
+ {
+ constant = apStateMachineConst->m_StateConstantArray[i]->m_BlendTreeConstantArray[motionSetIndex]->m_NodeArray[0].Get();
+ workspace = apStateMachineWorkspace->m_StateWorkspaceArray[i]->m_BlendTreeWorkspaceArray[motionSetIndex];
+ }
+ }
+}
+
+float Animator::GetFeetPivotActive()
+{
+ if(!IsAvatarInitialize())
+ return false;
+
+ return m_EvaluationDataSet.m_AvatarInput->m_FeetPivotActive;
+}
+void Animator::SetFeetPivotActive(float value)
+{
+ if(!IsAvatarInitialize())
+ return ;
+
+ m_EvaluationDataSet.m_AvatarInput->m_FeetPivotActive = value;
+}
+
+bool Animator::GetStabilizeFeet()
+{
+ if(!IsAvatarInitialize())
+ return false;
+
+ return m_EvaluationDataSet.m_AvatarInput->m_StabilizeFeet;
+
+}
+void Animator::SetStabilizeFeet(bool value)
+{
+ if(!IsAvatarInitialize())
+ return ;
+
+ m_EvaluationDataSet.m_AvatarInput->m_StabilizeFeet = value;
+}
+
+float Animator::GetLeftFeetBottomHeight()
+{
+ if(!m_Avatar.IsValid())
+ return 0.f;
+
+ return m_Avatar->GetLeftFeetBottomHeight();
+}
+
+float Animator::GetRightFeetBottomHeight()
+{
+ if(!m_Avatar.IsValid())
+ return 0.f;
+
+ return m_Avatar->GetRightFeetBottomHeight();
+}
+
+#if UNITY_EDITOR
+
+void Animator::WriteSkeletonPose(mecanim::skeleton::SkeletonPose& pose)
+{
+ PROFILER_AUTO(gAnimatorWriteSkeletonPose, this)
+
+ if (m_EvaluationDataSet.m_GenericBindingConstant != NULL)
+ {
+ SetHumanTransformPropertyValues (*m_EvaluationDataSet.m_AvatarBindingConstant, pose);
+ SetTransformPropertyApplyMainThread (GetComponent(Transform), *m_EvaluationDataSet.m_GenericBindingConstant, *m_EvaluationDataSet.m_AvatarBindingConstant,false); // we don't skip root for ui update pose stuff
+ }
+ else
+ {
+ ErrorString("Failed to write skeleton pose");
+ }
+}
+
+void Animator::WriteHumanPose(mecanim::human::HumanPose &pose)
+{
+ // this function is only called from the editor.
+ // always fetch the avatar constant from the Avatar asset in case it been updated.
+ // Looking if the pptr is valid
+ if(!m_Avatar.IsValid())
+ return;
+
+ // Looking if the mecanim avatar constant is valid
+ if(!m_Avatar->IsValid())
+ return;
+
+ if(!Prepare())
+ return;
+
+ mecanim::animation::AvatarConstant *avatar = m_Avatar->GetAsset();
+
+ if(avatar && avatar->isHuman())
+ {
+ mecanim::human::Human *human = avatar->m_Human.Get();
+ mecanim::human::RetargetTo(human,&pose,0,math::xformIdentity(),m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput,m_EvaluationDataSet.m_AvatarWorkspace->m_BodySkeletonPoseWs,m_EvaluationDataSet.m_AvatarWorkspace->m_BodySkeletonPoseWsA);
+ mecanim::animation::EvaluateAvatarEnd(avatar,m_EvaluationDataSet.m_AvatarInput,m_EvaluationDataSet.m_AvatarOutput,m_EvaluationDataSet.m_AvatarMemory,m_EvaluationDataSet.m_AvatarWorkspace,m_EvaluationDataSet.m_ControllerConstant);
+
+ WriteSkeletonPose(*m_EvaluationDataSet.m_AvatarOutput->m_SkeletonPoseOutput);
+ }
+ else
+ {
+ ErrorString("Failed to write human pose");
+ }
+}
+
+void Animator::WriteDefaultPose()
+{
+ // Looking if the pptr is valid
+ if(!m_Avatar.IsValid())
+ return;
+
+ // Looking if the mecanim avatar constant is valid
+ if(!m_Avatar->IsValid())
+ return;
+
+ if(!Prepare())
+ return;
+
+ mecanim::animation::AvatarConstant *avatar = m_Avatar->GetAsset();
+
+ WriteSkeletonPose(*avatar->m_AvatarSkeletonPose);
+}
+#endif
+
+bool Animator::IsAutoPlayingBack()
+{
+ return m_RecorderMode == eRecord && GetSpeed() < 0;
+}
+
+bool Animator::IsPlayingBack()
+{
+ return m_RecorderMode == ePlayback || IsAutoPlayingBack();
+}
+
+int Animator::GetLayerClipOffset(int layerIndex)
+{
+ int layerClipOffset = 0;
+ for(int i = 0; i < layerIndex ; i++)
+ {
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[i]->m_StateMachineIndex;
+ mecanim::uint32_t motionSetIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[i]->m_StateMachineMotionSetIndex;
+
+ if(stateMachineIndex != mecanim::DISABLED_SYNCED_LAYER_IN_NON_PRO)
+ {
+ const mecanim::statemachine::StateMachineConstant* apStateMachineConst = m_EvaluationDataSet.m_ControllerConstant->m_StateMachineArray[stateMachineIndex].Get();
+ int j;
+ for( j = 0 ; j < apStateMachineConst->m_StateConstantCount; j++)
+ {
+ layerClipOffset += apStateMachineConst->m_StateConstantArray[j]->m_LeafInfoArray[motionSetIndex].m_Count ;
+ }
+ }
+ }
+
+ return layerClipOffset;
+}
+
+bool Animator::ShouldInterruptMatchTarget()const
+{
+ if(IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1))
+ return false;
+
+ // case 516805: Match target must be interrupted if we begin a transition.
+ if(IsInTransitionInternal(0))
+ return true;
+
+ // case 542102: when the frame rate drop we miss the end of the match target and the last match target parameter stick.
+ // detect such case in TargetMatch by watching the initial state id
+ if(!IsMatchingTarget())
+ return true;
+
+ return false;
+}
+
+void Animator::TargetMatch(bool matchCurrentFrame)
+{
+ if(ShouldInterruptMatchTarget())
+ InterruptMatchTarget(false);
+
+ if(IsInitialize() && IsMatchingTarget())
+ {
+ math::xform avatarX = m_EvaluationDataSet.m_AvatarMemory->m_AvatarX;
+
+ math::float1 scale(m_EvaluationDataSet.m_AvatarConstant->m_Human->m_Scale);
+ math::xform dx = m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_DX;
+ dx.t *= scale;
+ avatarX = math::xformMul(avatarX, dx);
+
+ float stateTime = 0 ;
+ float stateDuration = 1 ;
+ if( m_EvaluationDataSet.m_ControllerConstant->m_LayerCount > 0)
+ {
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[0]->m_StateMachineIndex;
+ mecanim::statemachine::StateMachineMemory const* stateMachineMem = m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex].Get();
+ if(stateMachineMem->m_StateMemoryCount > 0 )
+ {
+ stateTime = stateMachineMem->m_StateMemoryArray[stateMachineMem->m_CurrentStateIndex]->m_PreviousTime;
+ stateDuration = stateMachineMem->m_StateMemoryArray[stateMachineMem->m_CurrentStateIndex]->m_Duration;
+ }
+ }
+
+ if(stateTime >= m_MatchStartTime)
+ {
+ float endTime = m_EvaluationDataSet.m_AvatarInput->m_TargetTime;
+
+ float normalizeDeltaTime = m_EvaluationDataSet.m_AvatarInput->m_DeltaTime / stateDuration;
+ float remainingTime = max(0.0f, endTime - stateTime );
+ float w = remainingTime != 0 ? normalizeDeltaTime / remainingTime : 1;
+ w = math::saturate(w);
+
+ math::float4 targetT = m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_TargetX.t;
+ math::float4 targetQ = m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_TargetX.q;
+
+ mecanim::uint32_t targetIndex = m_EvaluationDataSet.m_AvatarInput->m_TargetIndex;
+
+ if(matchCurrentFrame)
+ {
+ w = 1;
+ if(targetIndex == mecanim::animation::kTargetReference)
+ {
+ targetT = math::float4::zero();
+ targetQ = math::quatIdentity();
+ }
+ else if(targetIndex == mecanim::animation::kTargetRoot)
+ {
+ targetT = m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_RootX.t;
+ targetQ = m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_RootX.q;
+ }
+ else if (targetIndex >= mecanim::animation::kTargetLeftFoot && targetIndex <= mecanim::animation::kTargetRightHand)
+ {
+ targetT = m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[targetIndex-2].m_X.t;
+ targetQ = m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[targetIndex-2].m_X.q;
+ }
+ }
+
+
+ if (targetIndex >= mecanim::animation::kTargetLeftFoot && targetIndex <= mecanim::animation::kTargetRightHand)
+ targetQ = math::normalize(math::quatMul(targetQ,mecanim::human::HumanGetGoalOrientationOffset(mecanim::human::Goal(targetIndex - mecanim::animation::kTargetLeftFoot))));
+
+
+ // from muscleclips, local to avatarX
+ math::xform targetXLocal(targetT,targetQ, math::scaleIdentity());
+
+ // from user, where we need to be, in world space
+ math::xform matchX(Vector3fTofloat4(m_MatchPosition), QuaternionfTofloat4(m_MatchRotation), math::scaleIdentity());
+
+ // make match local to avatar X
+ math::xform matchXLocal = math::xformInvMul(avatarX, matchX); // @Sonny: any problem here for match target bug on IOS
+
+ // m_EvaluationDataSet.m_AvatarOutput->m_DX is in normalized space, so dx musy be computed in normalized space too.
+ math::xform dx;
+ dx.t = matchXLocal.t - targetXLocal.t; // dt not influenced by rotation
+ dx.t *= rcp(scale);
+ dx.q = quatMul(matchXLocal.q, quatConj(targetXLocal.q));
+ dx.t *= Vector3fTofloat4(w*m_MatchTargetMask.m_PositionXYZWeight);
+ dx.q = math::quatLerp( math::quatIdentity(), dx.q, math::float1(w*m_MatchTargetMask.m_RotationWeight));
+
+ m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_DX = math::xformMul(m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_DX,dx);
+
+ if(w >= 1)
+ {
+ InterruptMatchTarget(false);
+ }
+ }
+ }
+}
+
+bool Animator::ValidateGoalIndex(int index)
+{
+ if(index < mecanim::human::kLeftFootGoal || index > mecanim::human::kRightHandGoal )
+ {
+ ErrorString("Invalid Goal Index");
+ return false;
+ }
+
+ return true;
+}
+
+bool Animator::ValidateTargetIndex(int index)
+{
+ if(index < mecanim::animation::kTargetReference || index > mecanim::animation::kTargetRightHand )
+ {
+ ErrorString("Invalid Target Index");
+ return false;
+ }
+
+ return true;
+}
+
+bool Animator::ValidateLayerIndex(int index)const
+{
+ if(!IsInitialize() )
+ return false;
+
+ if(index < 0 || index >= GetLayerCount())
+ {
+ ErrorString("Invalid Layer Index");
+ return false;
+ }
+
+ if(m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[index]->m_StateMachineIndex == mecanim::DISABLED_SYNCED_LAYER_IN_NON_PRO)
+ {
+ ErrorString("Sync Layer is only supported in Unity Pro");
+ return false;
+ }
+
+ return true;
+}
+
+void Animator::ValidateParameterID (GetSetValueResult result, int identifier)
+{
+ ValidateParameterString (result, Format("Hash %d", identifier));
+}
+
+void Animator::ValidateParameterString (GetSetValueResult result, const std::string& name)
+{
+
+ if (result == kParameterMismatchFailure)
+ {
+ WarningStringIfLoggingActive(Format ("Parameter type '%s' does not match.", name.c_str()));
+ }
+ else if (result == kParameterDoesNotExist)
+ {
+ WarningStringIfLoggingActive(Format ("Parameter '%s' does not exist.", name.c_str()));
+ }
+ else if (result == kAnimatorNotInitialized)
+ {
+ WarningStringIfLoggingActive("Animator has not been initialized.");
+ }
+ else if (result == kParameterIsControlledByCurve)
+ {
+ WarningStringIfLoggingActive(Format("Parameter '%s' is controlled by a curve.", name.c_str()));
+ }
+}
+
+template<typename T> inline bool IsTypeMatching(mecanim::ValueConstant const& valueConstant, T const& value)
+{
+ return valueConstant.m_Type == mecanim::traits<T>::type() ||
+ (valueConstant.m_Type == mecanim::kTriggerType && mecanim::traits<T>::type() == mecanim::kBoolType);
+}
+
+template<typename T> inline
+GetSetValueResult Animator::SetValue(mecanim::uint32_t id, T const& value)
+{
+ if(IsPlayingBack())
+ return kAnimatorInPlaybackMode;
+
+ if (!IsInitialize())
+ return kAnimatorNotInitialized;
+
+ mecanim::int32_t i = mecanim::FindValueIndex(m_EvaluationDataSet.m_ControllerConstant->m_Values.Get(), id);
+ if( i == -1)
+ return kParameterDoesNotExist;
+
+ bool isCurve = m_EvaluationDataSet.m_GenericBindingConstant->controllerBindingConstant->m_AnimationSet->m_AdditionalIndexArray[i] != -1;
+ if (isCurve)
+ return kParameterIsControlledByCurve;
+
+ if (!IsTypeMatching(m_EvaluationDataSet.m_ControllerConstant->m_Values->m_ValueArray[i], value))
+ return kParameterMismatchFailure;
+
+ m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_Values->WriteData(value, m_EvaluationDataSet.m_ControllerConstant->m_Values->m_ValueArray[i].m_Index);
+ return kGetSetSuccess;
+}
+
+template<typename T> inline
+GetSetValueResult Animator::GetValue(mecanim::uint32_t id, T& value)
+{
+ if (!IsInitialize())
+ {
+ value = T();
+ return kAnimatorNotInitialized;
+ }
+
+ mecanim::int32_t i = mecanim::FindValueIndex(m_EvaluationDataSet.m_ControllerConstant->m_Values.Get(), id);
+ if ( i == -1)
+ {
+ value = T();
+ return kParameterDoesNotExist;
+ }
+
+ if (! IsTypeMatching(m_EvaluationDataSet.m_ControllerConstant->m_Values->m_ValueArray[i], value) )
+ {
+ value = T();
+ return kParameterMismatchFailure;
+ }
+
+
+ m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_Values->ReadData(value, m_EvaluationDataSet.m_ControllerConstant->m_Values->m_ValueArray[i].m_Index);
+
+ return kGetSetSuccess;
+}
+
+
+void Animator::WarningStringIfLoggingActive(const std::string& warning) const
+{
+ WarningStringIfLoggingActive(warning.c_str());
+}
+
+/// add or modify curve for a read only imported clip ... put Animation Window modification to curve in .meta
+
+void Animator::WarningStringIfLoggingActive(const char* warning) const
+{
+ if(m_LogWarnings)
+ {
+ WarningStringObject(warning ,this);
+ }
+}
+
+Transform* Animator::GetAvatarRoot()
+{
+ Transform *root = &GetComponent(Transform);
+ if(IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_3_a1))
+ {
+ if(m_Avatar.IsValid())
+ {
+ Transform* effectiveRoot = 0;
+ if(m_Avatar->GetAsset()&& m_Avatar->GetAsset()->m_AvatarSkeleton.Get())
+ {
+ effectiveRoot = FindAvatarRoot(m_Avatar->GetAsset()->m_AvatarSkeleton.Get(), m_Avatar->GetAsset()->m_SkeletonNameIDArray.Get(), *root, m_HasTransformHierarchy) ;
+ }
+ if(effectiveRoot) root = effectiveRoot;
+ }
+ }
+
+ return root;
+}
+
+
+///@TODO:
+
+/// * How does a user setup the animation range in the animation window?
+/// * Figure out how we want to move loop blend feel intuitive.
+
+/// * LivePreview pretends that non-looping animations loop. At least that looks like what it is visualizing...
+/// * Blending of dynamic values does not treat default values correctly
+/// muscle clip info for animation window created clips
+/// visible avatar for some objects like lights
+/// * Figure out how we will handle startTime / stopTime from AnimationWindow.
+
+///@TODO: Write Test
+/// Create cube with light attached
+/// * Create Animation clip with transform changes -> Automatically applies root motion
+/// * Animated child object is animated as normal position movement
+/// * Blending between one clip that has a property and one that doesn't
diff --git a/Runtime/Animation/Animator.h b/Runtime/Animation/Animator.h
new file mode 100644
index 0000000..c481187
--- /dev/null
+++ b/Runtime/Animation/Animator.h
@@ -0,0 +1,537 @@
+#pragma once
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Animation/AvatarPlayback.h"
+#include "Runtime/Misc/UserList.h"
+#include "Runtime/mecanim/statemachine/statemachine.h"
+
+namespace math
+{
+ struct xform;
+}
+class Avatar;
+class Renderer;
+class Transform;
+class AnimationClip;
+class AnimatorController;
+class RuntimeAnimatorController;
+class AnimatorOverrideController;
+
+namespace mecanim
+{
+ namespace animation
+ {
+ struct AvatarConstant;
+ struct AvatarInput;
+ struct AvatarOutput;
+ struct AvatarMemory;
+ struct AvatarWorkspace;
+ struct ControllerConstant;
+ struct ControllerMemory;
+ struct AnimatorOverrideController;
+ struct AnimationSetMemory;
+ struct BlendTreeNodeConstant;
+ struct BlendTreeWorkspace;
+ }
+
+ namespace skeleton
+ {
+ struct SkeletonPose;
+ }
+ namespace human
+ {
+ struct HumanPose;
+ }
+}
+
+namespace UnityEngine
+{
+ namespace Animation
+ {
+ struct AvatarBindingConstant;
+ struct AnimatorGenericBindingConstant;
+ struct AnimatorTransformBindingConstant;
+ struct AnimationSetBindings;
+ }
+}
+
+
+
+enum GetSetValueResult { kGetSetSuccess = 0, kParameterMismatchFailure = 1, kParameterDoesNotExist = 2, kAnimatorNotInitialized = 3, kParameterIsControlledByCurve = 4, kAnimatorInPlaybackMode = 5 };
+
+struct AnimationInfo
+{
+ PPtr<AnimationClip> clip;
+ float weight;
+
+ AnimationInfo()
+ {
+ Clear();
+ }
+
+ void Clear()
+ {
+ weight = 0;
+ }
+};
+
+/// This struct must be kept in sync with the C# version in AnimatorBindings.txt
+struct AnimatorTransitionInfo
+{
+ int nameHash;
+ int userNameHash;
+ float normalizedTime;
+
+ AnimatorTransitionInfo()
+ {
+ Clear();
+ }
+
+ void Clear()
+ {
+ nameHash = 0;
+ userNameHash = 0;
+ normalizedTime = 0;
+ }
+};
+
+/// This struct must be kept in sync with the C# version in AnimatorBindings.txt
+struct AnimatorStateInfo
+{
+ int nameHash;
+ int pathHash;
+ float normalizedTime;
+ float length;
+ int tagHash;
+ int loop;
+
+ AnimatorStateInfo()
+ {
+ Clear();
+ }
+
+ void Clear()
+ {
+ pathHash = 0;
+ nameHash = 0;
+ normalizedTime = 0;
+ length = 0;
+ tagHash = 0;
+ loop = 0;
+ }
+};
+
+/// This struct must be kept in sync with the C# version in AnimatorBindings.txt
+struct MatchTargetWeightMask
+{
+ MatchTargetWeightMask(Vector3f positionXYZWeight, float rotationWeight)
+ : m_PositionXYZWeight(positionXYZWeight), m_RotationWeight(rotationWeight) {}
+
+ Vector3f m_PositionXYZWeight;
+ float m_RotationWeight;
+};
+
+enum RecorderMode
+{
+ eNormal = 0,
+ ePlayback= 1,
+ eRecord =2
+};
+
+typedef std::vector<PPtr<AnimationClip> > AnimationClipVector;
+
+class Animator: public Behaviour
+{
+public:
+
+ enum CullingMode { kCullAlwaysAnimate = 0, kCullBasedOnRenderers = 1 };
+
+ REGISTER_DERIVED_CLASS (Animator, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (Animator)
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ Animator (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+ virtual void Deactivate (DeactivateOperation operation);
+ virtual void Reset ();
+ virtual void CheckConsistency();
+
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+
+ void Update (float deltaTime);
+ bool Sample (AnimationClip& clip, float inTime);
+
+ virtual void TransformChanged (int changeMask);
+ void OnAddComponent(Component* com);
+
+ bool IsValid() const;
+ bool IsInManagerList() const;
+
+ RuntimeAnimatorController* GetRuntimeAnimatorController() const;
+ void SetRuntimeAnimatorController(RuntimeAnimatorController* animation) ;
+
+ AnimatorController* GetAnimatorController() const;
+ AnimatorOverrideController* GetAnimatorOverrideController() const;
+
+ Avatar* GetAvatar();
+ void SetAvatar(Avatar* avatar);
+
+ const mecanim::animation::AvatarConstant* GetAvatarConstant();
+
+ bool IsOptimizable() const;
+ bool IsHuman() const;
+ bool HasRootMotion() const;
+ float GetHumanScale() const;
+
+ GetSetValueResult GetFloat(int id, float& value);
+ GetSetValueResult SetFloat(int id, float value);
+ GetSetValueResult SetFloatDamp(int id, float value, float dampTime, float deltaTime);
+
+ GetSetValueResult GetInteger(int id, int& output);
+ GetSetValueResult SetInteger(int id, int integer);
+
+ GetSetValueResult GetBool(int id, bool& output);
+ GetSetValueResult SetBool(int id, bool value);
+
+ GetSetValueResult ResetTrigger(int id);
+ GetSetValueResult SetTrigger(int id);
+
+ bool HasParameter(int id);
+
+ bool GetMuscleValue(int id, float *value);
+
+ GetSetValueResult ParameterControlledByCurve(int id);
+
+ Vector3f GetAvatarPosition();
+ Quaternionf GetAvatarRotation();
+ Vector3f GetAvatarScale();
+
+ void SetAvatarPosition(const Vector3f& rootPosition);
+ void SetAvatarRotation(const Quaternionf& rootRotation);
+ void SetAvatarScale(const Vector3f&rootScale);
+
+ Vector3f GetDeltaPosition();
+ Quaternionf GetDeltaRotation();
+
+ Vector3f GetBodyPosition();
+ Quaternionf GetBodyRotation();
+
+ void SetBodyPosition(const Vector3f& rootPosition);
+ void SetBodyRotation(const Quaternionf& rootRotation);
+
+ float GetPivotWeight();
+ Vector3f GetPivotPosition();
+
+ bool GetApplyRootMotion () const { return m_ApplyRootMotion; }
+ void SetApplyRootMotion (bool rootMotion);
+
+ bool GetAnimatePhysics () const { return m_AnimatePhysics; }
+ void SetAnimatePhysics (bool animatePhysics);
+
+ float GetGravityWeight();
+
+ bool SupportsOnAnimatorMove();
+
+ void MatchTarget(Vector3f const& matchPosition, Quaternionf const& matchRotation, int targetIndex, const MatchTargetWeightMask& mask, float startNormalizedTime, float targetNormalizedTime);
+ void InterruptMatchTarget(bool completeMatch = true);
+ bool IsMatchingTarget()const;
+
+ void SetSpeed(float speed);
+ float GetSpeed() const ;
+
+ void GotoState(int layer, int stateId, float normalizedTime, float transitionDuration, float transitionTime = 0.0F);
+
+ void SetTarget(int targetIndex, float targetNormalizedTime);
+ Vector3f GetTargetPosition();
+ Quaternionf GetTargetRotation();
+
+ bool IsBoneTransform(Transform *transform);
+ Transform* GetBoneTransform(int humanBoneId);
+
+ Vector3f GetGoalPosition(int index);
+ void SetGoalPosition(int index, Vector3f const& pos);
+
+ Quaternionf GetGoalRotation(int index);
+ void SetGoalRotation(int index, Quaternionf const& rot);
+
+ void SetGoalWeightPosition(int index, float value);
+ void SetGoalWeightRotation(int index, float value);
+
+ float GetGoalWeightPosition(int index);
+ float GetGoalWeightRotation(int index);
+
+ void SetLookAtPosition(Vector3f lookAtPosition);
+ void SetLookAtClampWeight(float weight);
+ void SetLookAtBodyWeight(float weight);
+ void SetLookAtHeadWeight(float weight);
+ void SetLookAtEyesWeight(float weight);
+
+ int GetLayerCount()const;
+ std::string GetLayerName(int layerIndex);
+ float GetLayerWeight(int layerIndex);
+ void SetLayerWeight(int layerIndex, float w);
+
+ void SetCullingMode (CullingMode mode);
+ CullingMode GetCullingMode () const { return m_CullingMode; }
+
+ bool IsInTransition(int layerIndex)const;
+
+ bool ShouldInterruptMatchTarget()const;
+
+ bool GetAnimatorStateInfo (int layerIndex, bool currentState, AnimatorStateInfo& output);
+ bool GetAnimatorTransitionInfo (int layerIndex, AnimatorTransitionInfo& output);
+
+ string GetAnimatorStateName (int layerIndex, bool currentState);
+
+ bool GetAnimationClipState(int layerIndex, bool currentState, dynamic_array<AnimationInfo>& output);
+
+ void GetRootBlendTreeConstantAndWorkspace (int layerIndex, int stateHash, mecanim::animation::BlendTreeNodeConstant const* & constant, mecanim::animation::BlendTreeWorkspace*& workspace);
+
+ float GetFeetPivotActive();
+ void SetFeetPivotActive(float value);
+
+ bool GetStabilizeFeet();
+ void SetStabilizeFeet(bool value);
+
+ float GetLeftFeetBottomHeight();
+ float GetRightFeetBottomHeight();
+
+ void WriteHumanPose(mecanim::human::HumanPose &pose);
+ void WriteDefaultPose();
+
+ const mecanim::skeleton::SkeletonPose* GetGlobalSpaceSkeletonPose () const;
+
+ void StartPlayback();
+ void SetPlaybackTime(float time);
+ float GetPlaybackTime();
+ void StopPlayback();
+
+ void PrepareForPlayback();
+ void StartRecording(int frameCount = 0);
+ void StopRecording();
+
+ float GetRecorderStartTime();
+ float GetRecorderStopTime();
+
+ int GetBehaviourIndex () { return m_BehaviourIndex; }
+ void SetBehaviourIndex (int index) { m_BehaviourIndex = index; }
+
+ int GetFixedBehaviourIndex () { return m_FixedBehaviourIndex; }
+ void SetFixedBehaviourIndex (int index) { m_FixedBehaviourIndex = index; }
+
+ static void UpdateAvatars (Animator** inputAvatars, size_t inputSize, float deltaTime, bool doFKMove, bool doRetargetIKWrite);
+
+ bool IsAvatarInitialize() const;
+ bool IsInitialize() const;
+
+ void ValidateParameterString (GetSetValueResult result, const std::string& parameterName);
+ void ValidateParameterID (GetSetValueResult result, int identifier);
+
+
+ void SetLayersAffectMassCenter(bool value);
+ bool GetLayersAffectMassCenter() const ;
+
+ void SetHasTransformHierarchy (bool value);
+ bool GetHasTransformHierarchy () const { return m_HasTransformHierarchy; }
+
+ void EvaluateSM();
+
+ GET_SET(bool, LogWarnings, m_LogWarnings);
+ GET_SET(bool, FireEvents, m_FireEvents);
+
+ std::string GetPerformanceHints();
+
+ AnimationClipVector GetAnimationClips()const;
+ UnityEngine::Animation::AnimationSetBindings* GetAnimationSetBindings()const;
+
+
+ Transform* GetAvatarRoot();
+protected:
+
+ struct MecanimDataSet
+ {
+ MecanimDataSet():
+ m_AvatarConstant(0),
+ m_AvatarInput(0),
+ m_AvatarOutput(0),
+ m_AvatarMemory(0),
+ m_AvatarWorkspace(0),
+ m_ControllerConstant(0),
+ m_ControllerMemory(0),
+ m_AnimationSetMemory(0),
+ m_GenericBindingConstant(0),
+ m_AvatarBindingConstant(0),
+ m_AvatarMemorySize(0),
+ m_OwnsAvatar(false)
+ {
+ }
+
+ mecanim::animation::AvatarConstant const* m_AvatarConstant;
+ mecanim::animation::AvatarInput* m_AvatarInput;
+ mecanim::animation::AvatarOutput* m_AvatarOutput;
+ mecanim::animation::AvatarMemory* m_AvatarMemory;
+ mecanim::animation::AvatarWorkspace* m_AvatarWorkspace;
+ mecanim::animation::ControllerConstant const* m_ControllerConstant;
+ mecanim::animation::ControllerMemory* m_ControllerMemory;
+ mecanim::animation::AnimationSetMemory* m_AnimationSetMemory;
+
+ UnityEngine::Animation::AnimatorGenericBindingConstant* m_GenericBindingConstant;
+ UnityEngine::Animation::AvatarBindingConstant* m_AvatarBindingConstant;
+
+ size_t m_AvatarMemorySize;
+
+ bool m_OwnsAvatar;
+
+ void Reset()
+ {
+ m_AvatarConstant=0;
+ m_AvatarInput=0;
+ m_AvatarOutput=0;
+ m_AvatarMemory=0;
+ m_AvatarWorkspace=0;
+ m_ControllerConstant=0;
+ m_ControllerMemory=0;
+ m_AnimationSetMemory=0;
+ m_GenericBindingConstant=0;
+ m_AvatarBindingConstant=0;
+ m_AvatarMemorySize=0;
+ m_OwnsAvatar = false;
+ }
+ };
+
+ // Used by Animator::Sample to auto unregister bindings.
+ struct AutoMecanimDataSet
+ {
+ mecanim::memory::ChainedAllocator m_Alloc;
+ MecanimDataSet m_MecanimDataSet;
+
+ AutoMecanimDataSet(size_t size):m_Alloc(size){}
+ ~AutoMecanimDataSet();
+
+ Animator::MecanimDataSet const* operator ->() const{ return &m_MecanimDataSet; }
+ Animator::MecanimDataSet * operator ->(){ return &m_MecanimDataSet; }
+ Animator::MecanimDataSet const& operator *()const{ return m_MecanimDataSet; }
+ Animator::MecanimDataSet& operator *(){ return m_MecanimDataSet; }
+
+ void Reset();
+ };
+
+
+ void ClearObject();
+ void CreateObject();
+
+ void SetupAvatarMecanimDataSet(mecanim::animation::AvatarConstant const* avatarConstant, mecanim::memory::Allocator& allocator, Animator::MecanimDataSet& outMecanimDataSet);
+ void SetupControllerMecanimDataSet(mecanim::animation::ControllerConstant const* controllerConstant, UnityEngine::Animation::AnimationSetBindings const* animationSetBindings, mecanim::memory::Allocator& allocator, Animator::MecanimDataSet& outMecanimDataSet);
+
+ void WriteSkeletonPose(mecanim::skeleton::SkeletonPose& pose);
+
+ bool IsInTransitionInternal(int layerIndex)const;
+
+ bool Prepare();
+ void InitStep(float deltaTime);
+ void FKStep();
+ void RetargetStep();
+ void AvatarIKAndEndStep();
+ void AvatarWriteStep();
+ void ApplyOnAnimatorIK(int layerIndex);
+ void ApplyOnAnimatorMove();
+ void ApplyBuiltinRootMotion();
+ void FireAnimationEvents();
+
+ void Record(float deltaTime);
+
+
+ // Visibility culling
+ void ClearContainedRenderers ();
+ void RecomputeContainedRenderersRecurse (Transform& transform);
+ void InitializeVisibilityCulling ();
+ void SetVisibleRenderers(bool visible);
+ bool IsAnyRendererVisible () const;
+ void RemoveContainedRenderer (void* renderer);
+
+ static void AnimatorVisibilityCallback (void* userData, void* sender, int visibilityEvent);
+
+ static void* FKStepStatic (void* userData);
+ static void* RetargetStepStatic (void* userData);
+ static void* AvatarIKAndEndStepStatic (void* userData);
+ static void* AvatarWriteStepStatic (void* userData);
+
+ int m_BehaviourIndex;
+ int m_FixedBehaviourIndex;
+ bool m_Visible;
+ CullingMode m_CullingMode; ///< enum { Always Animate = 0, Based On Renderers = 1 }
+ PPtr<Avatar> m_Avatar;
+ PPtr<RuntimeAnimatorController> m_Controller;
+
+ mecanim::memory::MecanimAllocator mAlloc;
+
+ MecanimDataSet m_EvaluationDataSet;
+
+ AutoMecanimDataSet m_SamplingDataSet;
+
+ Vector3f m_DeltaPosition;
+ Quaternionf m_DeltaRotation;
+
+ Vector3f m_PivotPosition;
+
+ Vector3f m_TargetPosition;
+ Quaternionf m_TargetRotation;
+
+ float m_MatchStartTime;
+ int m_MatchStateID;
+ Vector3f m_MatchPosition;
+ Quaternionf m_MatchRotation;
+ MatchTargetWeightMask m_MatchTargetMask;
+ bool m_MustCompleteMatch;
+
+ bool m_ApplyRootMotion;
+ bool m_AnimatePhysics;
+
+ float m_Speed;
+
+ bool m_LogWarnings;
+ bool m_FireEvents;
+
+
+ typedef dynamic_array<Renderer*> ContainedRenderers;
+ ContainedRenderers m_ContainedRenderers;
+
+ UserListNode m_AnimatorAvatarNode;
+ UserListNode m_AnimatorControllerNode;
+
+ AvatarPlayback m_AvatarPlayback;
+ RecorderMode m_RecorderMode;
+ float m_PlaybackDeltaTime;
+ float m_PlaybackTime; // for query back
+ bool IsPlayingBack();
+ bool IsAutoPlayingBack();
+
+ bool m_HasTransformHierarchy;
+
+ void SetPlaybackTimeInternal(float time);
+
+ void UpdateInManager();
+
+ int GetLayerClipOffset(int layerIndex);
+
+ void TargetMatch(bool matchCurrentFrame = false);
+
+ bool ValidateGoalIndex(int index);
+ bool ValidateTargetIndex(int index);
+ bool ValidateLayerIndex(int index)const;
+ bool ValidateSubLayerIndex(int index, int subLayerIndex);
+
+ template<typename T>
+ GetSetValueResult SetValue(mecanim::uint32_t id, T const& value);
+
+ template<typename T>
+ GetSetValueResult GetValue(mecanim::uint32_t id, T& value);
+
+ void WarningStringIfLoggingActive(const char* warning) const;
+ void WarningStringIfLoggingActive(const std::string& warning) const;
+};
+
diff --git a/Runtime/Animation/AnimatorController.cpp b/Runtime/Animation/AnimatorController.cpp
new file mode 100644
index 0000000..d633364
--- /dev/null
+++ b/Runtime/Animation/AnimatorController.cpp
@@ -0,0 +1,693 @@
+
+#include "UnityPrefix.h"
+
+#include "AnimatorController.h"
+
+#include "Runtime/Animation/RuntimeAnimatorController.h"
+
+#include "Runtime/mecanim/animation/avatar.h"
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/Blobification/BlobWrite.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/mecanim/generic/stringtable.h"
+#include "Runtime/Animation/AnimationClip.h"
+#include "Runtime/Animation/AnimationSetBinding.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/Animation/StateMachine.h"
+#include "Editor/Src/Animation/AvatarMask.h"
+#include "Runtime/Scripting/Backend/ScriptingInvocation.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Editor/Src/AssetPipeline/AssetImporter.h"
+#endif
+
+#include "Runtime/Scripting/Backend/ScriptingInvocation.h"
+
+
+#define DIRTY_AND_INVALIDATE() OnInvalidateAnimatorController(); SetDirty();
+
+
+
+IMPLEMENT_OBJECT_SERIALIZE (AnimatorController)
+IMPLEMENT_CLASS_HAS_INIT(AnimatorController)
+
+
+
+AnimatorController::AnimatorController(MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode),
+ m_Allocator(1024*4),
+ m_Controller(0),
+ m_ControllerSize(0),
+ m_AnimationSetBindings(0),
+ m_IsAssetBundled(true)
+#if UNITY_EDITOR
+ ,
+ m_Dependencies(this)
+#endif
+{
+
+}
+
+AnimatorController::~AnimatorController()
+{
+#if UNITY_EDITOR
+ m_Dependencies.Clear();
+#endif
+
+ NotifyObjectUsers( kDidModifyAnimatorController );
+}
+
+void AnimatorController::InitializeClass ()
+{
+ REGISTER_MESSAGE_VOID(AnimatorController, kDidModifyMotion, OnInvalidateAnimatorController);
+
+#if UNITY_EDITOR
+ RegisterAllowNameConversion (AnimatorController::GetClassStringStatic(), "m_Layers", "m_AnimatorLayers");
+ RegisterAllowNameConversion (AnimatorController::GetClassStringStatic(), "m_AnimatorEvents", "m_AnimatorParameters");
+ RegisterAllowTypeNameConversion( "AnimatorEvent", "AnimatorControllerParameter") ;
+#endif
+}
+
+void AnimatorController::AwakeFromLoad(AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad(mode);
+
+#if UNITY_EDITOR
+ OnInvalidateAnimatorController();
+
+ // Force load the AnimatorController
+ // This ensures that when we build a player the Controller is fully initialized.
+ // @TODO: This is kind of a hack. It would be better if we make sure when building a player we will ensure that the ControllerConstant has been created.
+ GetAsset();
+
+#endif
+
+ if (m_AnimationSetBindings == NULL && m_Controller != NULL)
+ {
+ RegisterAnimationClips();
+ m_AnimationSetBindings = UnityEngine::Animation::CreateAnimationSetBindings(m_Controller, GetAnimationClips(), m_Allocator);
+ }
+}
+
+
+void AnimatorController::CheckConsistency ()
+{
+ Super::CheckConsistency();
+#if UNITY_EDITOR
+
+ AnimatorControllerParameterVector toRemove;
+ for(int i=0;i<m_AnimatorParameters.size();++i)
+ {
+ // This is the old Vector type which is not supported anymore
+ if(m_AnimatorParameters[i].GetType() == 0)
+ toRemove.push_back(m_AnimatorParameters[i]);
+ }
+
+ for(int i=0;i<toRemove.size();++i)
+ {
+ for(int j=0;j<m_AnimatorParameters.size();++j)
+ {
+ if( strcmp(m_AnimatorParameters[j].GetName(), toRemove[i].GetName()) == 0 )
+ {
+ RemoveParameter(i);
+ break;
+ }
+ }
+ }
+
+ for(int i=0;i<m_AnimatorLayers.size();++i)
+ {
+ m_AnimatorLayers[i].SetController(this);
+
+ if( m_AnimatorLayers[i].GetSyncedLayerIndex() >= static_cast<int>(m_AnimatorLayers.size()))
+ {
+ m_AnimatorLayers[i].SetSyncedLayerIndexInternal(-1);
+ m_AnimatorLayers[i].SetStateMachineMotionSetIndex(0);
+ }
+
+ if( m_AnimatorLayers[i].GetSyncedLayerIndex() != -1)
+ {
+ StateMachine* stateMachine = m_AnimatorLayers[i].GetStateMachine();
+ int motionSetCount = stateMachine->GetMotionSetCount();
+
+ if(m_AnimatorLayers[i].GetStateMachineMotionSetIndex() >= motionSetCount)
+ {
+ // if there is only 2 motion set and only one layer is synchronize we can reconnect
+ if(motionSetCount == 2)
+ {
+ bool valid = true;
+ for(int j=0;j<m_AnimatorLayers.size() && valid;++j)
+ {
+ if( i!=j && m_AnimatorLayers[i].GetSyncedLayerIndex() == m_AnimatorLayers[j].GetSyncedLayerIndex())
+ valid = false;
+ }
+
+ if(valid)
+ m_AnimatorLayers[i].SetStateMachineMotionSetIndex(1);
+ else
+ {
+ m_AnimatorLayers[i].SetSyncedLayerIndexInternal(-1);
+ m_AnimatorLayers[i].SetStateMachineMotionSetIndex(0);
+ }
+ }
+ else
+ {
+ m_AnimatorLayers[i].SetSyncedLayerIndexInternal(-1);
+ m_AnimatorLayers[i].SetStateMachineMotionSetIndex(0);
+ }
+ }
+ }
+ }
+ for(int i=0;i<m_AnimatorParameters.size();++i)
+ {
+ m_AnimatorParameters[i].SetController(this);
+ }
+#endif
+}
+
+template <typename TransferFunction>
+bool IsLoadingFromAssetBundle (TransferFunction& transfer)
+{
+ return transfer.IsReading () && transfer.IsSerializingForGameRelease();
+}
+
+
+template<class TransferFunction>
+void AnimatorController::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion(2);
+
+ if (transfer.IsSerializingForGameRelease())
+ {
+ TRANSFER(m_ControllerSize);
+
+ if(m_Controller == 0)
+ m_Allocator.Reserve(m_ControllerSize);
+
+ transfer.SetUserData(&m_Allocator);
+ TRANSFER_NULLABLE(m_Controller, mecanim::animation::ControllerConstant);
+ TRANSFER(m_TOS);
+
+ transfer.Transfer (m_AnimationClips, "m_AnimationClips");
+ }
+
+ TRANSFER_EDITOR_ONLY_HIDDEN(m_AnimatorParameters);
+ transfer.Align();
+ TRANSFER_EDITOR_ONLY_HIDDEN(m_AnimatorLayers);
+
+ // case 491674 Crash when avatar is selected in the Hiererchy when in play mode
+ // cannot display a controller in UI if it come from an asset bundle
+#if UNITY_EDITOR
+ if(transfer.IsReading ())
+ m_IsAssetBundled = IsLoadingFromAssetBundle(transfer) && m_AnimatorLayers.size() == 0;
+
+ if (transfer.IsRemapPPtrTransfer() && !transfer.IsSerializingForGameRelease())
+ {
+ transfer.Transfer (m_AnimationClips, "m_AnimationClips");
+ }
+#endif
+
+}
+
+
+#if UNITY_EDITOR
+
+void AnimatorController::BuildAsset()
+{
+ ClearAsset();
+
+ m_Dependencies.Clear();
+ m_TOS.clear();
+
+ // Insert all reserve keyword
+ mecanim::ReserveKeyword* staticTable = mecanim::ReserveKeywordTable();
+ int i;
+ for(i=0;i<mecanim::eLastString;i++)
+ {
+ m_TOS.insert( std::make_pair(staticTable[i].m_ID, std::string(staticTable[i].m_Keyword) ) );
+ }
+
+ /// Parameters
+ dynamic_array<mecanim::uint32_t> types (kMemTempAlloc);
+ dynamic_array<int> eventIds (kMemTempAlloc);
+ for(i = 0 ; i < GetParameterCount() ; i++)
+ {
+ AnimatorControllerParameter* parameter = GetParameter(i);
+ eventIds.push_back( mecanim::processCRC32( mecanim::String(parameter->GetName())));
+ m_TOS.insert( std::make_pair(eventIds[i], parameter->GetName()) );
+ types.push_back(parameter->GetType());
+ }
+
+ mecanim::ValueArrayConstant* values = mecanim::CreateValueArrayConstant(types.begin(), types.size(), m_Allocator);
+
+ for(i = 0 ; i < GetParameterCount() ; i++)
+ values->m_ValueArray[i].m_ID = eventIds[i];
+
+
+ mecanim::ValueArray* defaultValues = mecanim::CreateValueArray(values, m_Allocator);
+
+ for(i = 0 ; i < GetParameterCount() ; i++)
+ {
+ AnimatorControllerParameter* parameter = GetParameter(i);
+ switch(parameter->GetType())
+ {
+ case AnimatorControllerParameterTypeFloat:
+ {
+ float val = parameter->GetDefaultFloat();
+ defaultValues->WriteData(val, values->m_ValueArray[i].m_Index);
+ break;
+ }
+ case AnimatorControllerParameterTypeInt:
+ {
+ mecanim::int32_t val = parameter->GetDefaultInt();
+ defaultValues->WriteData(val, values->m_ValueArray[i].m_Index);
+ break;
+ }
+ case AnimatorControllerParameterTypeTrigger:
+ case AnimatorControllerParameterTypeBool:
+ {
+ bool val = parameter->GetDefaultBool();
+ defaultValues->WriteData(val, values->m_ValueArray[i].m_Index);
+ break;
+ }
+ }
+ }
+
+
+ /// Layers
+
+ std::vector<mecanim::animation::LayerConstant*> layerVector;
+ std::vector<mecanim::statemachine::StateMachineConstant*> stateMachineVector;
+ std::vector<int> stateMachineIndexVector;
+
+ int stateMachineIndex = 0;
+ for(int i = 0 ; i < GetLayerCount() ; i++)
+ {
+ if(m_AnimatorLayers[i].GetSyncedLayerIndex() == -1)
+ stateMachineIndexVector.push_back(stateMachineIndex++);
+ else
+ stateMachineIndexVector.push_back(-1);
+ }
+
+ for(int i = 0 ; i < GetLayerCount() ; i++)
+ {
+ int stateMachineIndex = 0;
+
+ StateMachine* editorStateMachine = m_AnimatorLayers[i].GetStateMachine();
+
+ if(m_AnimatorLayers[i].GetSyncedLayerIndex() == -1)
+ {
+ if(editorStateMachine)
+ {
+ mecanim::statemachine::StateMachineConstant* stateMachine = editorStateMachine->BuildRuntimeAsset(m_Dependencies, m_TOS, i, m_Allocator);
+ AssertIf(stateMachine == 0);
+ if(stateMachine)
+ {
+ editorStateMachine->GetAnimationClips(m_AnimationClips, m_AnimatorLayers[i].GetStateMachineMotionSetIndex());
+ }
+
+ stateMachineVector.push_back(stateMachine);
+ stateMachineIndex = stateMachineIndexVector[i];
+ }
+ }
+ else
+ {
+ if (!GetBuildSettings().hasAdvancedVersion)
+ {
+ ErrorString("Sync Layer is only supported in Unity Pro. Layer will be discarded in game");
+ stateMachineIndex = mecanim::DISABLED_SYNCED_LAYER_IN_NON_PRO;
+ }
+
+ else
+ {
+ stateMachineIndex = stateMachineIndexVector[m_AnimatorLayers[i].GetSyncedLayerIndex()];
+ if(editorStateMachine)
+ {
+ editorStateMachine->GetAnimationClips(m_AnimationClips, m_AnimatorLayers[i].GetStateMachineMotionSetIndex());
+ }
+ }
+ }
+
+ AnimatorControllerLayer &animatorLayer = m_AnimatorLayers[i];
+
+ mecanim::animation::LayerConstant* layer = mecanim::animation::CreateLayerConstant(stateMachineIndex, animatorLayer.GetStateMachineMotionSetIndex(), m_Allocator);
+ layer->m_Binding = mecanim::processCRC32( mecanim::String( animatorLayer.GetName()));
+ m_TOS.insert( std::make_pair(layer->m_Binding, animatorLayer.GetName()) );
+ layer->m_IKPass = animatorLayer.GetIKPass();
+ layer->m_LayerBlendingMode = animatorLayer.GetBlendingMode();
+ layer->m_DefaultWeight = animatorLayer.GetDefaultWeight();
+ layer->m_SyncedLayerAffectsTiming = animatorLayer.GetBlendingMode() == AnimatorLayerBlendingModeOverride ? animatorLayer.GetSyncedLayerAffectsTiming() : false;
+
+ AvatarMask* mask = animatorLayer.GetMask();
+ layer->m_BodyMask = mask != NULL ? mask->GetHumanPoseMask(m_Dependencies) : mecanim::human::FullBodyMask();
+ layer->m_SkeletonMask = mask != NULL ? mask->GetSkeletonMask(m_Dependencies, m_Allocator) : 0;
+
+ layerVector.push_back(layer);
+
+ }
+
+ // Early exit if no layer or statemachine
+ if(layerVector.size() == 0 || stateMachineVector.size() == 0)
+ return;
+
+
+ mecanim::animation::ControllerConstant* controllerConstant =
+ mecanim::animation::CreateControllerConstant( layerVector.size(), layerVector.size() ? &layerVector.front() : 0,
+ stateMachineVector.size(), stateMachineVector.size() ? &stateMachineVector.front() :0,
+ values, defaultValues, m_Allocator);
+ AssertIf(controllerConstant == 0);
+ if(controllerConstant)
+ {
+ m_Controller = controllerConstant;
+
+ BlobWrite::container_type data;
+ BlobWrite blobWrite (data, kNoTransferInstructionFlags, kBuildNoTargetPlatform);
+ blobWrite.Transfer(*m_Controller, "Base");
+
+ m_ControllerSize = data.size();
+
+ RegisterAnimationClips();
+ m_AnimationSetBindings = UnityEngine::Animation::CreateAnimationSetBindings(m_Controller, GetAnimationClips(), m_Allocator);
+ }
+
+}
+
+
+AnimatorControllerLayer* AnimatorController::GetLayer(int index)
+{
+ if(ValidateLayerIndex(index))
+ return &m_AnimatorLayers[index];
+
+ return 0;
+}
+
+const AnimatorControllerLayer* AnimatorController::GetLayer(int index) const
+{
+ if(ValidateLayerIndex(index))
+ return &m_AnimatorLayers[index];
+
+ return 0;
+}
+
+int AnimatorController::GetLayerCount() const
+{
+ return m_AnimatorLayers.size();
+}
+
+void AnimatorController::AddLayer(const std::string& name)
+{
+ m_IsAssetBundled = false;
+ AnimatorControllerLayer layer;
+ layer.SetController(this);
+
+ StateMachine *stateMachine = CreateObjectFromCode<StateMachine>();
+
+ stateMachine->SetHideFlags( this->TestHideFlag(kDontSave) ? kHideInHierarchy | kHideInspector | kDontSave : kHideInHierarchy | kHideInspector);
+ if(IsPersistent())
+ AddAssetToSameFile(*stateMachine, *this, true);
+
+ layer.SetStateMachine(stateMachine);
+ layer.SetName(name.c_str());
+
+ m_AnimatorLayers.push_back(layer);
+ DIRTY_AND_INVALIDATE();
+}
+
+void AnimatorController::RemoveLayer(int index)
+{
+ if(ValidateLayerIndex(index))
+ {
+ if(GetLayer(index)->GetSyncedLayerIndex() != -1)
+ GetLayer(index)->SetSyncedLayerIndex(-1); // this will remove motion set and ensure consistency
+
+ // Update sync layer index
+ for(int i=0;i<m_AnimatorLayers.size();++i)
+ {
+ AnimatorControllerLayer *layer = GetLayer(i);
+ if( i!=index && layer->GetSyncedLayerIndex() > index)
+ layer->SetSyncedLayerIndexInternal(layer->GetSyncedLayerIndex()-1);
+
+ // If a layer is sync on this layer break the synchronization
+ if( i!=index && layer->GetSyncedLayerIndex() == index)
+ layer->SetSyncedLayerIndex(-1);
+ }
+ m_AnimatorLayers.erase(m_AnimatorLayers.begin() + index);
+
+ DIRTY_AND_INVALIDATE();
+ }
+}
+
+
+int AnimatorController::GetParameterCount() const
+{
+ return m_AnimatorParameters.size();
+}
+
+
+void AnimatorController::AddParameter(const std::string& name, AnimatorControllerParameterType type)
+{
+ m_AnimatorParameters.push_back(AnimatorControllerParameter());
+ AnimatorControllerParameter *animatorParameter = GetParameter(GetParameterCount()-1);
+ animatorParameter->SetController(this);
+ animatorParameter->SetName(name.c_str());
+ animatorParameter->SetType(type);
+
+ int count = 0;
+ for(int i = 0 ; i < GetParameterCount(); i++)
+ if(GetParameter(i)->GetType() == type) count++;
+
+ if(count == 1)
+ {
+ for(int i = 0 ; i < GetLayerCount(); i++)
+ {
+ if(GetLayer(i)->GetStateMachine())
+ GetLayer(i)->GetStateMachine()->AddFirstParameterOfType(animatorParameter->GetName(),type);
+ }
+ }
+ DIRTY_AND_INVALIDATE();
+}
+
+void AnimatorController::RemoveParameter(int index)
+{
+ if(ValidateParameterIndex(index))
+ {
+ AnimatorControllerParameterType eventType = GetParameter(index)->GetType();
+
+ int otherSameTypeParameterIndex = -1;
+ //find other event of type
+ for(int i = 0 ; i < GetParameterCount() && otherSameTypeParameterIndex == -1; i++)
+ {
+ if( i != index && GetParameter(i)->GetType() == eventType)
+ otherSameTypeParameterIndex = i;
+ }
+
+ for(int i = 0 ; i < GetLayerCount(); i++)
+ {
+ if(GetLayer(i)->GetStateMachine())
+ GetLayer(i)->GetStateMachine()->RenameParameter(otherSameTypeParameterIndex != -1 ? GetParameter(otherSameTypeParameterIndex)->GetName() : "", GetParameter(index)->GetName());
+ }
+
+ m_AnimatorParameters.erase(m_AnimatorParameters.begin() + index);
+ DIRTY_AND_INVALIDATE();
+ }
+}
+
+int AnimatorController::FindParameter(const std::string&name) const
+{
+ int ret = -1;
+
+ for(int i = 0; i < m_AnimatorParameters.size() && ret == -1; i++)
+ {
+ if(strcmp (m_AnimatorParameters[i].GetName(), name.c_str()) == 0)
+ {
+ ret = i;
+ }
+ }
+
+ return ret;
+}
+
+AnimatorControllerParameter* AnimatorController::GetParameter(int index)
+{
+ if(ValidateParameterIndex(index))
+ return &m_AnimatorParameters[index];
+
+ return 0;
+}
+const AnimatorControllerParameter* AnimatorController::GetParameter(int index) const
+{
+ if(ValidateParameterIndex(index))
+ return &m_AnimatorParameters[index];
+
+ return 0;
+}
+
+
+std::vector<PPtr<Object> > AnimatorController::CollectObjectsUsingParameter(const string& parameterName)
+{
+ std::vector<PPtr<Object> > ret;
+ for(int i = 0 ; i < GetLayerCount() ; i++)
+ {
+ StateMachine* stateMachine = GetLayer(i)->GetStateMachine();
+
+ if(stateMachine)
+ {
+ std::vector<PPtr<Object> > currentRet = stateMachine->CollectObjectsUsingParameter(parameterName);
+ ret.insert(ret.end(), currentRet.begin(), currentRet.end());
+ }
+ }
+
+ return ret;
+}
+
+string AnimatorController::MakeUniqueParameterName(const string& newName) const
+{
+ string attemptName = newName;
+ int attempt = 0;
+ while (true)
+ {
+ int i = 0;
+ for (i = 0; i < GetParameterCount(); i++)
+ {
+ if (attemptName == GetParameter(i)->GetName())
+ {
+ attemptName = newName;
+ attemptName += Format(" %d", attempt);
+ attempt++;
+ break;
+ }
+ }
+ if (i == GetParameterCount())
+ break;
+ }
+
+ return attemptName;
+}
+
+string AnimatorController::MakeUniqueLayerName(const string& newName) const
+{
+ string attemptName = newName;
+ int attempt = 0;
+ while (true)
+ {
+ int i = 0;
+ for (i = 0; i < GetLayerCount(); i++)
+ {
+ if (attemptName == GetLayer(i)->GetName())
+ {
+ attemptName = newName;
+ attemptName += Format(" %d", attempt);
+ attempt++;
+ break;
+ }
+ }
+ if (i == GetLayerCount())
+ break;
+ }
+
+ return attemptName;
+
+}
+
+
+bool AnimatorController::ValidateLayerIndex(int index) const
+{
+ if(index >= 0 && index < GetLayerCount())
+ {
+ return true;
+ }
+
+ ErrorString("Invalid Layer index");
+ return false;
+}
+
+bool AnimatorController::ValidateParameterIndex(int index) const
+{
+ if(index >= 0 && index < GetParameterCount())
+ {
+ return true;
+ }
+
+ ErrorString("Invalid Parameter index");
+ return false;
+}
+
+
+#endif
+
+
+AnimationClipVector AnimatorController::GetAnimationClips() const
+{
+ const_cast<AnimatorController*>(this)->GetAsset(); // @TODO: Force load the AnimatorController. This is kind of a hack.
+
+ return m_AnimationClips;
+}
+
+AnimationClipVector AnimatorController::GetAnimationClipsToRegister() const
+{
+ return GetAnimationClips();
+}
+
+
+
+void AnimatorController::OnInvalidateAnimatorController()
+{
+#if UNITY_EDITOR
+ if( !m_IsAssetBundled)
+ {
+ ClearAsset();
+
+ ScriptingInvocation invocation("UnityEditorInternal","AnimatorController", "OnInvalidateAnimatorController");
+ invocation.AddObject(Scripting::ScriptingWrapperFor(this));
+ invocation.Invoke();
+ }
+
+#endif
+
+ NotifyObjectUsers( kDidModifyAnimatorController );
+}
+
+mecanim::animation::ControllerConstant* AnimatorController::GetAsset()
+{
+#if UNITY_EDITOR
+ if (m_Controller == 0)
+ BuildAsset();
+#endif
+
+ return m_Controller;
+}
+
+UnityEngine::Animation::AnimationSetBindings* AnimatorController::GetAnimationSetBindings()
+{
+#if UNITY_EDITOR
+ if (m_AnimationSetBindings == 0)
+ BuildAsset();
+#endif
+
+ return m_AnimationSetBindings;
+}
+
+
+void AnimatorController::ClearAsset()
+{
+ m_AnimationSetBindings = NULL;
+ m_Controller = NULL;
+ m_TOS.clear();
+ m_Allocator.Reset();
+
+ m_AnimationClips.clear();
+}
+
+std::string AnimatorController::StringFromID(unsigned int id) const
+{
+ TOSVector::const_iterator it = m_TOS.find(id);
+ if(it != m_TOS.end())
+ return it->second;
+ return "";
+}
+
+
+#undef DIRTY_AND_INVALIDATE
diff --git a/Runtime/Animation/AnimatorController.h b/Runtime/Animation/AnimatorController.h
new file mode 100644
index 0000000..493657f
--- /dev/null
+++ b/Runtime/Animation/AnimatorController.h
@@ -0,0 +1,121 @@
+
+#ifndef EDITABLEAVATARCONTROLLER_H
+#define EDITABLEAVATARCONTROLLER_H
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/BaseClasses/MessageIdentifier.h"
+#include "Runtime/BaseClasses/RefCounted.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/Animation/MecanimUtility.h"
+#include "Runtime/Animation/RuntimeAnimatorController.h"
+#include "Runtime/Misc/UserList.h"
+
+
+#if UNITY_EDITOR
+#include "Editor/Src/Animation/AnimatorControllerParameter.h"
+#include "Editor/Src/Animation/AnimatorControllerLayer.h"
+
+typedef std::vector<AnimatorControllerLayer> AnimatorControllerLayerVector;
+typedef std::vector<AnimatorControllerParameter> AnimatorControllerParameterVector;
+
+#endif
+
+template<class T>
+class PPtr;
+class AnimationClip;
+class StateMachine;
+class AvatarMask;
+
+class AnimatorController : public RuntimeAnimatorController
+{
+
+public :
+ REGISTER_DERIVED_CLASS (AnimatorController, RuntimeAnimatorController)
+ DECLARE_OBJECT_SERIALIZE (AnimatorController)
+
+ AnimatorController (MemLabelId label, ObjectCreationMode mode);
+
+ static void InitializeClass ();
+
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+ virtual void CheckConsistency ();
+
+#if UNITY_EDITOR
+
+ //////////////////////////////////////////
+ // AnimatorControllerLayers
+ AnimatorControllerLayer* GetLayer(int index);
+ const AnimatorControllerLayer* GetLayer(int index)const ;
+ int GetLayerCount()const;
+ void AddLayer(const std::string& name);
+ void RemoveLayer(int index);
+
+ //////////////////////////////////////////
+ // AnimatorControllerParameter
+ AnimatorControllerParameter* GetParameter(int index) ;
+ const AnimatorControllerParameter* GetParameter(int index) const;
+ int GetParameterCount() const ;
+ void AddParameter(const std::string& name, AnimatorControllerParameterType type);
+ void RemoveParameter(int index);
+ int FindParameter(const std::string& name) const;
+
+
+ std::vector<PPtr<Object> > CollectObjectsUsingParameter(const string& parameterName);
+ /////////////////////////////////////////////
+
+ string MakeUniqueParameterName(const string& newName) const;
+ string MakeUniqueLayerName(const string& newName) const;
+
+ bool ValidateLayerIndex(int index) const;
+ bool ValidateParameterIndex(int index) const;
+
+
+private:
+
+ bool ValidAnimationSet();
+ void BuildAsset();
+
+
+ AnimatorControllerLayerVector m_AnimatorLayers;
+ AnimatorControllerParameterVector m_AnimatorParameters;
+ UserList m_Dependencies;
+
+
+ template<class T>
+ void ParametersAndLayersBackwardsCompatibility (T& transfer);
+
+ AnimatorControllerLayer* CreateAnimatorControllerLayer();
+ AnimatorControllerParameter* CreateAnimatorControllerParameter();
+
+#endif // UNITY_EDITOR
+
+public:
+
+ virtual UnityEngine::Animation::AnimationSetBindings* GetAnimationSetBindings();
+ virtual AnimationClipVector GetAnimationClips() const ;
+ virtual mecanim::animation::ControllerConstant* GetAsset();
+
+
+ virtual std::string StringFromID(unsigned int ID) const;
+ void OnInvalidateAnimatorController();
+ bool IsAssetBundled() { return m_IsAssetBundled; }
+
+private :
+
+ void ClearAsset();
+ void OnAnimationClipDeleted();
+ virtual AnimationClipVector GetAnimationClipsToRegister() const;
+
+
+ AnimationClipVector m_AnimationClips;
+ bool m_IsAssetBundled;
+
+ mecanim::memory::ChainedAllocator m_Allocator;
+ mecanim::animation::ControllerConstant* m_Controller;
+ UInt32 m_ControllerSize;
+ UnityEngine::Animation::AnimationSetBindings* m_AnimationSetBindings;
+ TOSVector m_TOS;
+};
+
+
+#endif //EDITABLEAVATARCONTROLLER_H
diff --git a/Runtime/Animation/AnimatorGenericBindings.cpp b/Runtime/Animation/AnimatorGenericBindings.cpp
new file mode 100644
index 0000000..78d884f
--- /dev/null
+++ b/Runtime/Animation/AnimatorGenericBindings.cpp
@@ -0,0 +1,1100 @@
+#include "UnityPrefix.h"
+#include "AnimatorGenericBindings.h"
+#include "AnimationClipBindings.h"
+#include "AnimationSetBinding.h"
+#include "Runtime/mecanim/generic/crc32.h"
+#include "Runtime/mecanim/skeleton/skeleton.h"
+#include "Runtime/Animation/MecanimUtility.h"
+#include "Runtime/Animation/AnimationUtility.h"
+#include "Runtime/Filters/Deformation/SkinnedMeshFilter.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "GenericAnimationBindingCache.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/BaseClasses/EventIDs.h"
+
+namespace UnityEngine { namespace Animation
+{
+ static void InitializeDefaultValues (const UnityEngine::Animation::AnimatorGenericBindingConstant& genericBinding, const mecanim::animation::AvatarConstant* avatar, bool hasTransformHierarchy, mecanim::animation::ControllerBindingConstant& controllerBindingConstant);
+
+ struct BoundTransform
+ {
+ BindingHash pathHash;
+
+ Transform* transform;
+ int bindIndexForSkeleton;
+ };
+
+ #define IS_DEPRECATED_NAME_BASED_BINDING (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1))
+
+ mecanim::int32_t SkeletonFindNodeIndexByNameID(const mecanim::animation::AvatarConstant* avatar, mecanim::uint32_t aNameID)
+ {
+ mecanim::int32_t ret = -1;
+
+ mecanim::int32_t i;
+ for(i = 0; ret == -1 && i < avatar->m_SkeletonNameIDCount; i++)
+ {
+ if(avatar->m_SkeletonNameIDArray[i] == aNameID)
+ {
+ ret = i;
+ }
+ }
+
+ return ret;
+ }
+
+
+
+ void GenerateTransformBindingMapRecursive (Transform& transform, const mecanim::crc32& nameHash, dynamic_array<BoundTransform>& bindings, const mecanim::animation::AvatarConstant* avatar, bool hasTransformHierarchy)
+ {
+ const mecanim::skeleton::Skeleton* skeleton = avatar->m_AvatarSkeleton.Get();
+
+ BoundTransform& binding = bindings.push_back();
+ binding.pathHash = nameHash.checksum();
+ binding.transform = &transform;
+
+ if (IS_DEPRECATED_NAME_BASED_BINDING)
+ {
+ binding.pathHash = mecanim::processCRC32(transform.GetName());
+ }
+
+ if (hasTransformHierarchy)
+ {
+ // binding.pathHash : full path
+ binding.bindIndexForSkeleton = skeleton ? mecanim::skeleton::SkeletonFindNode(skeleton, binding.pathHash) : -1;
+ }
+ else
+ {
+ // binding.pathHash : flattened path
+ binding.bindIndexForSkeleton = SkeletonFindNodeIndexByNameID(avatar, binding.pathHash);
+ }
+
+ Transform::iterator end=transform.end();
+ for (Transform::iterator i=transform.begin();i != end;++i)
+ {
+ Transform* child = *i;
+ const char* name = child->GetName();
+
+ mecanim::crc32 childNameHash = AppendPathToHash(nameHash, name);
+ GenerateTransformBindingMapRecursive(*child, childNameHash, bindings, avatar, hasTransformHierarchy);
+ }
+ }
+
+ int FindTransformBindingIndexByBindingHash (const dynamic_array<BoundTransform>& bindings, BindingHash pathHash)
+ {
+ for (int i=0;i<bindings.size();++i)
+ {
+ if (bindings[i].pathHash == pathHash)
+ return i;
+ }
+
+ return -1;
+ }
+
+ int FindTransformBindingIndexBySkeletonIndex (const dynamic_array<BoundTransform>& bindings, int skeletonIndex)
+ {
+ for (int i=0;i<bindings.size();++i)
+ {
+ if (bindings[i].bindIndexForSkeleton == skeletonIndex)
+ return i;
+ }
+
+ return -1;
+ }
+
+ static void BindControllerRootMotionMask(const mecanim::animation::AvatarConstant &avatar,const mecanim::animation::ControllerConstant &controller, bool *rootMotionLayerMask)
+ {
+ mecanim::uint32_t rootMotionNodePathHash = 0;
+
+ if(avatar.m_RootMotionBoneIndex != -1)
+ {
+ rootMotionNodePathHash = avatar.m_AvatarSkeleton->m_ID[avatar.m_RootMotionBoneIndex];
+ }
+
+ for(int layerIter = 0; layerIter < controller.m_LayerCount; layerIter++)
+ {
+ bool mask = false;
+ bool found = false;
+
+ for(int maskIter = 0; !found && maskIter < controller.m_LayerArray[layerIter]->m_SkeletonMask->m_Count; maskIter++)
+ {
+ found = controller.m_LayerArray[layerIter]->m_SkeletonMask->m_Data[maskIter].m_PathHash == rootMotionNodePathHash;
+
+ if(found)
+ {
+ mask = controller.m_LayerArray[layerIter]->m_SkeletonMask->m_Data[maskIter].m_Weight > 0;
+ }
+ }
+
+ rootMotionLayerMask[layerIter] = mask;
+ }
+ }
+
+ mecanim::animation::ControllerBindingConstant *CreateControllerBindingConstant(const mecanim::animation::ControllerConstant* controller, const mecanim::animation::AnimationSet* animationSet, mecanim::ValueArrayConstant* valueArrayConstant, mecanim::uint32_t valueArrayConstantSize, const mecanim::animation::AvatarConstant* avatar, mecanim::memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(ControllerBindingConstant);
+
+ mecanim::animation::ControllerBindingConstant* controllerBindingConstant = alloc.Construct<mecanim::animation::ControllerBindingConstant>();
+ controllerBindingConstant->m_Avatar = avatar;
+ controllerBindingConstant->m_Controller = controller;
+ controllerBindingConstant->m_AnimationSet = animationSet;
+
+ int skeletonCount = !avatar->m_AvatarSkeleton.IsNull() ? avatar->m_AvatarSkeleton->m_Count : 0;
+ if (skeletonCount > 0)
+ controllerBindingConstant->m_SkeletonTQSMap = alloc.ConstructArray<mecanim::animation::SkeletonTQSMap> (avatar->m_AvatarSkeleton->m_Count);
+
+ controllerBindingConstant->m_DynamicValuesConstant = CreateValueArrayConstantCopy (valueArrayConstant, valueArrayConstantSize, alloc);
+ controllerBindingConstant->m_DynamicValuesDefault = CreateValueArray(controllerBindingConstant->m_DynamicValuesConstant, alloc);
+
+ controllerBindingConstant->m_RootMotionLayerMask = alloc.ConstructArray<bool>(controller->m_LayerCount);
+ BindControllerRootMotionMask(*avatar,*controller,controllerBindingConstant->m_RootMotionLayerMask);
+
+ return controllerBindingConstant;
+ }
+
+ void DestroyControllerBindingConstant(mecanim::animation::ControllerBindingConstant* controllerBindingConstant, mecanim::memory::Allocator& alloc)
+ {
+ if(controllerBindingConstant)
+ {
+ DestroyValueArray (controllerBindingConstant->m_DynamicValuesDefault, alloc);
+ DestroyValueArrayConstant (controllerBindingConstant->m_DynamicValuesConstant, alloc);
+ alloc.Deallocate (controllerBindingConstant->m_SkeletonTQSMap);
+ alloc.Deallocate (controllerBindingConstant->m_RootMotionLayerMask);
+ alloc.Deallocate (controllerBindingConstant);
+ }
+ }
+
+ static void BindControllerTQSMap(const AnimationSetBindings& animationSetBindings,
+ const mecanim::skeleton::Skeleton& skeleton,
+ int nonConstantTransformBindingCount,
+ const int* genericTransformBindingToBindingCache,
+ const BoundTransform* bindingCache,
+ bool hasTransformHierarchy,
+ mecanim::animation::ControllerBindingConstant *binding,
+ mecanim::memory::Allocator& alloc)
+ {
+ if (binding->m_SkeletonTQSMap == NULL)
+ return;
+
+ int rotationCount = -1;
+ int positionCount = -1;
+ int scaleCount = -1;
+
+ for (int transformIter = 0; transformIter < nonConstantTransformBindingCount; transformIter++)
+ {
+ const TransformBinding& transformBinding = animationSetBindings.transformBindings[transformIter];
+ int targetType = transformBinding.bindType;
+
+ if (targetType == kBindTransformScale)
+ scaleCount++;
+ else if (targetType == kBindTransformRotation)
+ rotationCount++;
+ else if (targetType == kBindTransformPosition)
+ positionCount++;
+
+ int skIndex = -1;
+ if (hasTransformHierarchy)
+ {
+ int transformIndex = genericTransformBindingToBindingCache[transformIter];
+ if (transformIndex == -1)
+ continue;
+ skIndex = bindingCache[transformIndex].bindIndexForSkeleton;
+ }
+ else
+ skIndex = SkeletonFindNode(&skeleton, transformBinding.path);
+
+ if (skIndex == -1)
+ continue;
+
+ if(targetType == kBindTransformScale)
+ binding->m_SkeletonTQSMap[skIndex].m_SIndex = scaleCount;
+ else if(targetType == kBindTransformRotation)
+ binding->m_SkeletonTQSMap[skIndex].m_QIndex = rotationCount;
+ else if(targetType == kBindTransformPosition)
+ binding->m_SkeletonTQSMap[skIndex].m_TIndex = positionCount;
+ }
+ }
+
+ static void GetDefaultTransformValues (Transform& targetTransform, int bindType, float* defaults)
+ {
+ if (bindType == kBindTransformPosition)
+ {
+ Vector3f pos = targetTransform.GetLocalPosition();
+ memcpy(defaults, &pos, sizeof(pos));
+ }
+ else if (bindType == kBindTransformRotation)
+ {
+ Quaternionf rot = targetTransform.GetLocalRotation();
+ memcpy(defaults, &rot, sizeof(rot));
+ }
+ else if (bindType == kBindTransformScale)
+ {
+ Vector3f scale = targetTransform.GetLocalScale();
+ memcpy(defaults, &scale, sizeof(scale));
+ }
+ else
+ {
+ AssertString("Bad");
+ }
+ }
+
+ static void GetDefaultSkeletonPoseValues (const math::xform& x, int bindType, float* defaults)
+ {
+ if (bindType == kBindTransformPosition)
+ math::store(x.t, defaults);
+ else if (bindType == kBindTransformRotation)
+ math::store(x.q, defaults);
+ else if (bindType == kBindTransformScale)
+ math::store(x.s, defaults);
+ else
+ {
+ AssertString("Bad");
+ }
+ }
+
+ static int CalculateTransformBindingSizeBasedOnConstantOptimization (const AnimationSetBindings& animationSet, dynamic_array<BoundTransform> const& transformBindingCache, const int* genericTransformBindingToBindingCache, const mecanim::animation::AvatarConstant* avatar, bool hasTransformHierarchy)
+ {
+ // Generate Constant Default values
+ ATTRIBUTE_ALIGN(ALIGN4F) float values[4];
+ const mecanim::skeleton::Skeleton* skeleton = hasTransformHierarchy ? NULL : avatar->m_AvatarSkeleton.Get();
+ const mecanim::skeleton::SkeletonPose* defaultPose = hasTransformHierarchy ? NULL : avatar->m_AvatarSkeletonPose.Get();
+
+ int highestMismatch = animationSet.transformBindingsNonConstantSize;
+
+ int constantDefaultValueIndex = 0;
+ for (int i=animationSet.transformBindingsNonConstantSize;i<animationSet.transformBindingsSize;i++)
+ {
+ const TransformBinding& transformBinding = animationSet.transformBindings[i];
+ int count = GetCurveCountForBindingType (transformBinding.bindType);
+
+ if (hasTransformHierarchy)
+ {
+ // If we can't write the value, then we don't need it to be sampled either...
+ // Thus we can simply assume it matches.
+ int transformIndex = genericTransformBindingToBindingCache[i];
+ if (transformIndex == -1)
+ {
+ constantDefaultValueIndex += count;
+ continue;
+ }
+ Transform* targetTransform = transformBindingCache[transformIndex].transform;
+ GetDefaultTransformValues(*targetTransform, transformBinding.bindType, values);
+ }
+ else
+ {
+ // get the default value from skeleton
+ int skeletonIndex = SkeletonFindNode(skeleton, transformBinding.path);
+ if (skeletonIndex == -1)
+ {
+ constantDefaultValueIndex += count;
+ continue;
+ }
+ GetDefaultSkeletonPoseValues(defaultPose->m_X[skeletonIndex], transformBinding.bindType, values);
+ }
+
+ for (int k=0;k<count;k++)
+ {
+ float clipConstant = animationSet.constantCurveValues[constantDefaultValueIndex];
+
+ if (!CompareApproximately(clipConstant, values[k], 0.00001F))
+ {
+ // printf_console("mismatch index: %d type: %d clipconstant/instance: %f vs %f.\n", i, bindType, animationSet.constantCurveValues[constantDefaultValueIndex], values[k]);
+ highestMismatch = i + 1;
+ }
+
+ constantDefaultValueIndex++;
+ }
+ }
+
+ Assert(constantDefaultValueIndex == animationSet.constantCurveValueCount);
+ return highestMismatch;
+ }
+
+ static void InvalidateBoundCurveArray(BoundCurve *boundCurveArray, int boundCurveCount, Object *object)
+ {
+ for(int iter = 0; iter < boundCurveCount; iter++)
+ {
+ if(boundCurveArray[iter].targetObject == object)
+ {
+ boundCurveArray[iter] = BoundCurve();
+ }
+ }
+ }
+
+ static void InvalidateTransformArray(Transform **transformArray, int transformCount, Object *object)
+ {
+ for(int iter = 0; iter < transformCount; iter++)
+ {
+ if(transformArray[iter] == object)
+ {
+ transformArray[iter] = 0;
+ }
+ }
+ }
+
+ void InvalidateAvatarBindingObject(AvatarBindingConstant* bindingConstant, Object *object)
+ {
+ InvalidateTransformArray(bindingConstant->skeletonBindings,bindingConstant->skeletonBindingsCount,object);
+ for (int i = 0; i < bindingConstant->exposedTransformCount; i++)
+ {
+ if (bindingConstant->exposedTransforms[i].transform == object)
+ bindingConstant->exposedTransforms[i].transform = NULL;
+ }
+ }
+
+
+ static void InvalidateGenericBindingObject(AnimatorGenericBindingConstant* bindingConstant, Object *object)
+ {
+ InvalidateBoundCurveArray(bindingConstant->transformBindings,bindingConstant->transformBindingsCount,object);
+ InvalidateBoundCurveArray(bindingConstant->genericBindings,bindingConstant->genericBindingsCount,object);
+ InvalidateBoundCurveArray(bindingConstant->genericPPtrBindings,bindingConstant->genericPPtrBindingsCount,object);
+ }
+
+ static void GenericBindingCallback(void *userData, void *sender,int eventType)
+ {
+ if(eventType == kWillDestroyEvent)
+ {
+ InvalidateGenericBindingObject(reinterpret_cast<AnimatorGenericBindingConstant *>(userData),reinterpret_cast<Object *>(sender));
+ }
+ }
+
+ void AvatarBindingCallback(void *userData, void *sender,int eventType)
+ {
+ if(eventType == kWillDestroyEvent)
+ {
+ InvalidateAvatarBindingObject(reinterpret_cast<AvatarBindingConstant *>(userData),reinterpret_cast<Object *>(sender));
+ }
+ }
+
+ static void RegisterBoundCurveArray(BoundCurve *boundCurveArray, int boundCurveCount, AnimatorGenericBindingConstant* bindingConstant)
+ {
+ for(int iter = 0; iter < boundCurveCount; iter++)
+ {
+ if(boundCurveArray[iter].targetObject != 0)
+ {
+ if(!boundCurveArray[iter].targetObject->HasEvent(GenericBindingCallback,bindingConstant))
+ {
+ boundCurveArray[iter].targetObject->AddEvent(GenericBindingCallback,bindingConstant);
+ }
+ }
+ }
+ }
+
+ template <typename TYPE> static void RegisterTransformArray(Transform **transformArray, int transformCount, TYPE* bindingConstant, Object::EventCallback* callback)
+ {
+ for(int iter = 0; iter < transformCount; iter++)
+ {
+ if(transformArray[iter] != 0)
+ {
+ if(!transformArray[iter]->HasEvent(callback, bindingConstant))
+ {
+ transformArray[iter]->AddEvent(callback, bindingConstant);
+ }
+ }
+ }
+ }
+
+ void RegisterAvatarBindingObjects(AvatarBindingConstant* bindingConstant)
+ {
+ RegisterTransformArray(bindingConstant->skeletonBindings,bindingConstant->skeletonBindingsCount,bindingConstant, AvatarBindingCallback);
+ for (int i = 0; i < bindingConstant->exposedTransformCount; i++)
+ {
+ if (bindingConstant->exposedTransforms[i].transform &&
+ !bindingConstant->exposedTransforms[i].transform->HasEvent(AvatarBindingCallback, bindingConstant))
+ bindingConstant->exposedTransforms[i].transform->AddEvent(AvatarBindingCallback, bindingConstant);
+ }
+ }
+
+ static void RegisterGenericBindingObjects(AnimatorGenericBindingConstant* bindingConstant)
+ {
+ RegisterBoundCurveArray(bindingConstant->transformBindings,bindingConstant->transformBindingsCount,bindingConstant);
+ RegisterBoundCurveArray(bindingConstant->genericBindings,bindingConstant->genericBindingsCount,bindingConstant);
+ RegisterBoundCurveArray(bindingConstant->genericPPtrBindings,bindingConstant->genericPPtrBindingsCount,bindingConstant);
+ }
+
+ static void UnregisterBoundCurveArray(BoundCurve *boundCurveArray, int boundCurveCount, AnimatorGenericBindingConstant* bindingConstant)
+ {
+ for(int iter = 0; iter < boundCurveCount; iter++)
+ {
+ if(boundCurveArray[iter].targetObject != 0)
+ {
+ boundCurveArray[iter].targetObject->RemoveEvent(GenericBindingCallback,bindingConstant);
+ }
+ }
+ }
+
+ template<typename TYPE> static void UnregisterTransformArray(Transform **transformArray, int transformCount, TYPE* bindingConstant, Object::EventCallback* callback)
+ {
+ for(int iter = 0; iter < transformCount; iter++)
+ {
+ if(transformArray[iter] != 0)
+ {
+ transformArray[iter]->RemoveEvent(callback,bindingConstant);
+ }
+ }
+ }
+
+ void UnregisterAvatarBindingObjects(AvatarBindingConstant* bindingConstant)
+ {
+ UnregisterTransformArray(bindingConstant->skeletonBindings,bindingConstant->skeletonBindingsCount,bindingConstant, AvatarBindingCallback);
+ for (int i = 0; i < bindingConstant->exposedTransformCount; i++)
+ {
+ if (bindingConstant->exposedTransforms[i].transform != NULL)
+ bindingConstant->exposedTransforms[i].transform->RemoveEvent(AvatarBindingCallback, bindingConstant);
+ }
+ }
+
+ void UnregisterGenericBindingObjects(AnimatorGenericBindingConstant* bindingConstant)
+ {
+ UnregisterBoundCurveArray(bindingConstant->transformBindings,bindingConstant->transformBindingsCount,bindingConstant);
+ UnregisterBoundCurveArray(bindingConstant->genericBindings,bindingConstant->genericBindingsCount,bindingConstant);
+ UnregisterBoundCurveArray(bindingConstant->genericPPtrBindings,bindingConstant->genericPPtrBindingsCount,bindingConstant);
+ }
+
+ static Transform *humanMark = reinterpret_cast<Transform *>(std::numeric_limits<size_t>::max());
+
+ static void humanMarkUp(mecanim::skeleton::Skeleton const &sk, int nodeIndex, Transform** bindings)
+ {
+ if(nodeIndex != -1)
+ {
+ bindings[nodeIndex] = humanMark;
+
+ humanMarkUp(sk,sk.m_Node[nodeIndex].m_ParentId,bindings);
+ }
+ }
+
+ AvatarBindingConstant* CreateAvatarBindingConstant (Transform& root, mecanim::animation::AvatarConstant const* avatar, mecanim::memory::Allocator& allocator)
+ {
+ SETPROFILERLABEL(AvatarBindingConstant);
+
+ // Generate binding cache
+ dynamic_array<BoundTransform> transformBindingCache (kMemTempAlloc);
+
+ const mecanim::skeleton::Skeleton* skeleton = avatar->m_AvatarSkeleton.Get();
+
+ GenerateTransformBindingMapRecursive(root, mecanim::crc32(), transformBindingCache, avatar, true);
+
+ AvatarBindingConstant* constant = allocator.Construct<AvatarBindingConstant> ();
+ constant->exposedTransformCount = 0;
+ constant->exposedTransforms = NULL;
+
+ constant->skeletonBindingsCount = skeleton ? skeleton->m_Count : 0;
+ constant->skeletonBindings = allocator.ConstructArray<Transform*> (constant->skeletonBindingsCount);
+
+ int transformChangedMask = 0;
+
+ // just bind what human will effectively affect
+
+ if(IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_3_a1))
+ {
+ if (constant->skeletonBindingsCount != 0)
+ {
+ memset(constant->skeletonBindings, 0, sizeof(Transform*) * constant->skeletonBindingsCount);
+
+ if(avatar->m_HumanSkeletonIndexCount > 0)
+ {
+ humanMarkUp(*skeleton,avatar->m_HumanSkeletonIndexArray[0],constant->skeletonBindings);
+
+ for(int humanSkIndexIter = 0; humanSkIndexIter < avatar->m_HumanSkeletonIndexCount; humanSkIndexIter++)
+ {
+ int humanSkIndex = avatar->m_HumanSkeletonIndexArray[humanSkIndexIter];
+
+ if(humanSkIndex != -1)
+ {
+ constant->skeletonBindings[humanSkIndex] = humanMark;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ for(int i = 0 ; i < constant->skeletonBindingsCount; ++i)
+ constant->skeletonBindings[i] = humanMark;
+ }
+
+ for (int i=0;i<transformBindingCache.size();i++)
+ {
+ int skeletonIndex = transformBindingCache[i].bindIndexForSkeleton;
+
+ if (skeletonIndex != -1)
+ {
+ if(constant->skeletonBindings[skeletonIndex] == humanMark)
+ {
+ constant->skeletonBindings[skeletonIndex] = transformBindingCache[i].transform;
+ transformChangedMask |= Transform::kPositionChanged | Transform::kRotationChanged;
+ }
+ }
+ }
+
+ for(int i = 0 ; i < constant->skeletonBindingsCount; ++i)
+ {
+ if(constant->skeletonBindings[i] == humanMark)
+ constant->skeletonBindings[i] = 0;
+ }
+
+ constant->transformChangedMask = transformChangedMask;
+
+ RegisterAvatarBindingObjects(constant);
+
+ return constant;
+ }
+
+ AvatarBindingConstant* CreateAvatarBindingConstantOpt (Transform& root, mecanim::animation::AvatarConstant const* avatar, mecanim::memory::Allocator& allocator)
+ {
+ SETPROFILERLABEL(AvatarBindingConstant);
+
+ // Generate binding cache
+ dynamic_array<BoundTransform> transformBindingCache (kMemTempAlloc);
+
+ GenerateTransformBindingMapRecursive(root, mecanim::crc32(), transformBindingCache, avatar, false);
+
+ mecanim::skeleton::Skeleton const* skeleton = avatar->m_AvatarSkeleton.Get();
+
+ AvatarBindingConstant* constant = allocator.Construct<AvatarBindingConstant> ();
+ constant->skeletonBindingsCount = 0;
+ constant->skeletonBindings = NULL;
+ constant->transformChangedMask = 0;
+
+ // For the flattened transform, it's impossible to tell if the transform will be modified just by the curve which
+ // is binded to it, because all its parent transforms will also affect it.
+ // Since normally, there are only a few exposed transforms, the performance penalty will not be huge
+ // if we don't care which exact properties of the transform are modified.
+ int exposedCount = 0;
+ int maxExposeCount = transformBindingCache.size();
+ dynamic_array<ExposedTransform> exposedTransforms(maxExposeCount, kMemTempAlloc);
+ for (int i = 0; i < maxExposeCount; ++i)
+ {
+ BoundTransform& boundTransform = transformBindingCache[i];
+ bool isChildOfRoot = (boundTransform.transform->GetParent() == &root);
+ if (!isChildOfRoot)
+ continue;
+
+ ExposedTransform& exposedTransform = exposedTransforms[exposedCount];
+ exposedTransform.transform = boundTransform.transform;
+ exposedTransform.skeletonIndex = -1;
+ exposedTransform.skeletonIndexForUpdateTransform = -1;
+
+ if (boundTransform.bindIndexForSkeleton != -1)
+ {
+ exposedTransform.skeletonIndex = boundTransform.bindIndexForSkeleton;
+ exposedTransform.skeletonIndexForUpdateTransform = boundTransform.bindIndexForSkeleton;
+ }
+
+ // Handle special case: SkinnedMeshRenderer
+ // We directly update the root bone to the Transform of the SkinnedMeshRenderer.
+ SkinnedMeshRenderer* skin = boundTransform.transform->QueryComponent(SkinnedMeshRenderer);
+ if (skin)
+ {
+ const Mesh* mesh = skin->GetMesh();
+ if (mesh && mesh->GetRootBonePathHash() != 0)
+ {
+ int skinRootIndex = skeleton ? mecanim::skeleton::SkeletonFindNode(
+ skeleton, mesh->GetRootBonePathHash()) : -1;
+ if (skinRootIndex != -1)
+ exposedTransform.skeletonIndexForUpdateTransform = skinRootIndex;
+ }
+ }
+ if (exposedTransform.skeletonIndexForUpdateTransform != -1)
+ exposedCount++;
+ }
+
+ constant->exposedTransformCount = exposedCount;
+ constant->exposedTransforms = allocator.ConstructArray<ExposedTransform> (constant->exposedTransformCount);
+ for (int i=0; i<exposedCount; i++)
+ constant->exposedTransforms[i] = exposedTransforms[i];
+
+ RegisterAvatarBindingObjects(constant);
+
+ return constant;
+ }
+
+ void DestroyAvatarBindingConstant (AvatarBindingConstant* bindingConstant, mecanim::memory::Allocator& allocator)
+ {
+ if (bindingConstant != NULL)
+ {
+ UnregisterAvatarBindingObjects(bindingConstant);
+
+ allocator.Deallocate(bindingConstant->skeletonBindings);
+ allocator.Deallocate(bindingConstant->exposedTransforms);
+ allocator.Deallocate(bindingConstant);
+ }
+ }
+
+ AnimatorGenericBindingConstant* CreateAnimatorGenericBindings (const AnimationSetBindings& animationSet, Transform& root, const mecanim::animation::AvatarConstant* avatar, const mecanim::animation::ControllerConstant* controller, mecanim::memory::Allocator& allocator)
+ {
+ SETPROFILERLABEL(AnimatorGenericBindingConstant);
+
+ GenericAnimationBindingCache& bindingCache = GetGenericAnimationBindingCache ();
+
+ const mecanim::skeleton::Skeleton* skeleton = avatar->m_AvatarSkeleton.Get();
+
+ // Generate binding cache
+ dynamic_array<BoundTransform> transformBindingCache (kMemTempAlloc);
+ dynamic_array<int> genericTransformBindingToBindingCache (kMemTempAlloc);
+
+ GenerateTransformBindingMapRecursive(root, mecanim::crc32(), transformBindingCache, avatar, true);
+
+ // Map from animation set to binding cache index
+ genericTransformBindingToBindingCache.resize_uninitialized(animationSet.transformBindingsSize);
+ for (int i=0;i<animationSet.transformBindingsSize;i++)
+ genericTransformBindingToBindingCache[i] = FindTransformBindingIndexByBindingHash (transformBindingCache, animationSet.transformBindings[i].path);
+
+ // Calculate Transform bindings that are actually animating (Constant curve values can be removed if they match the default values)
+ // Generate new reduced ValueArrayCount from it.
+ int nonConstantTransformBindingCount = CalculateTransformBindingSizeBasedOnConstantOptimization (animationSet, transformBindingCache, genericTransformBindingToBindingCache.begin(), avatar, true);
+ int optimizedValueArrayConstantCount = animationSet.animationSet->m_DynamicFullValuesConstant->m_Count - (animationSet.transformBindingsSize - nonConstantTransformBindingCount);
+ bool allowConstantClipSamplingOptimization = nonConstantTransformBindingCount == animationSet.transformBindingsNonConstantSize;
+
+ AnimatorGenericBindingConstant* constant = allocator.Construct<AnimatorGenericBindingConstant> ();
+
+ constant->transformBindingsCount = nonConstantTransformBindingCount;
+ constant->transformBindings = allocator.ConstructArray<BoundCurve> (constant->transformBindingsCount);
+
+ constant->genericBindingsCount = animationSet.genericBindingsSize;
+ constant->genericBindings = allocator.ConstructArray<BoundCurve> (constant->genericBindingsCount);
+
+ constant->genericPPtrBindingsCount = animationSet.genericPPtrBindingsSize;
+ constant->genericPPtrBindings = allocator.ConstructArray<BoundCurve> (constant->genericPPtrBindingsCount);
+
+ constant->allowConstantClipSamplingOptimization = allowConstantClipSamplingOptimization;
+
+ int transformChangedMask = 0;
+
+ // Bind Transforms
+ for (int i=0;i<constant->transformBindingsCount;i++)
+ {
+ int transformIndex = genericTransformBindingToBindingCache[i];
+ constant->transformBindings[i].targetType = animationSet.transformBindings[i].bindType;
+ if (transformIndex != -1)
+ {
+ constant->transformBindings[i].targetObject = transformBindingCache[transformIndex].transform;
+
+ transformChangedMask |= Transform::kPositionChanged | Transform::kRotationChanged;
+ if (animationSet.transformBindings[i].bindType == kBindTransformScale)
+ transformChangedMask |= Transform::kScaleChanged;
+ }
+ else
+ {
+ constant->transformBindings[i].targetObject = NULL;
+ }
+ }
+
+ constant->transformChangedMask = transformChangedMask;
+
+ // Bind Generic properties
+ for (int i=0;i<constant->genericBindingsCount;i++)
+ {
+ constant->genericBindings[i].targetObject = NULL;
+ constant->genericBindings[i].targetType = kUnbound;
+
+ int index = FindTransformBindingIndexByBindingHash (transformBindingCache, animationSet.genericBindings[i].path);
+ if (index != -1)
+ bindingCache.BindGeneric(animationSet.genericBindings[i], *transformBindingCache[index].transform, constant->genericBindings[i]);
+ }
+
+ // Bind Generic PPtr properties
+ for (int i=0;i<constant->genericPPtrBindingsCount;i++)
+ {
+ constant->genericPPtrBindings[i].targetObject = NULL;
+ constant->genericPPtrBindings[i].targetType = kUnbound;
+
+ int index = FindTransformBindingIndexByBindingHash (transformBindingCache, animationSet.genericPPtrBindings[i].path);
+ if (index != -1)
+ bindingCache.BindPPtrGeneric(animationSet.genericPPtrBindings[i], *transformBindingCache[index].transform, constant->genericPPtrBindings[i]);
+ }
+
+ constant->controllerBindingConstant = CreateControllerBindingConstant (controller, animationSet.animationSet, animationSet.animationSet->m_DynamicFullValuesConstant, optimizedValueArrayConstantCount, avatar, allocator);
+
+ // Gravity weight should must be in both optimized and non-optimized ValueArray
+ Assert(animationSet.animationSet->m_GravityWeightIndex == -1 || animationSet.animationSet->m_GravityWeightIndex < constant->controllerBindingConstant->m_DynamicValuesDefault->m_FloatCount);
+
+ // Bind Controller skeleton to dynamic value array
+ BindControllerTQSMap(animationSet, *skeleton, nonConstantTransformBindingCount, genericTransformBindingToBindingCache.begin(), transformBindingCache.begin(), true, constant->controllerBindingConstant, allocator);
+
+ RegisterGenericBindingObjects(constant);
+
+ InitializeDefaultValues (*constant, avatar, true, *constant->controllerBindingConstant);
+
+ return constant;
+ }
+
+ AnimatorGenericBindingConstant* CreateAnimatorGenericBindingsOpt ( const AnimationSetBindings& animationSet, Transform& root, const mecanim::animation::AvatarConstant* avatar, const mecanim::animation::ControllerConstant* controller, mecanim::memory::Allocator& allocator)
+ {
+ GenericAnimationBindingCache& bindingCache = GetGenericAnimationBindingCache ();
+ const mecanim::skeleton::Skeleton* skeleton = avatar->m_AvatarSkeleton.Get();
+
+ dynamic_array<BoundTransform> transformBindingCache (kMemTempAlloc);
+ // BoundTransform.pathHash: hash of flattened path
+ // BoundTransform.bindIndexForSkeleton: corresponding skeleton index
+ GenerateTransformBindingMapRecursive(root, mecanim::crc32(), transformBindingCache, avatar, false);
+
+ // Calculate Transform bindings that are actually animating (Constant curve values can be removed if they match the default values)
+ // Generate new reduced ValueArrayCount from it.
+ int nonConstantTransformBindingCount = CalculateTransformBindingSizeBasedOnConstantOptimization (animationSet, transformBindingCache, NULL, avatar, false);
+ int optimizedValueArrayConstantCount = animationSet.animationSet->m_DynamicFullValuesConstant->m_Count - (animationSet.transformBindingsSize - nonConstantTransformBindingCount);
+ bool allowConstantClipSamplingOptimization = nonConstantTransformBindingCount == animationSet.transformBindingsNonConstantSize;
+
+ AnimatorGenericBindingConstant* constant = allocator.Construct<AnimatorGenericBindingConstant> ();
+
+ constant->transformBindingsCount = 0;
+ constant->transformBindings = NULL;
+
+ constant->genericBindingsCount = animationSet.genericBindingsSize;
+ constant->genericBindings = allocator.ConstructArray<BoundCurve> (constant->genericBindingsCount);
+
+ constant->genericPPtrBindingsCount = animationSet.genericPPtrBindingsSize;
+ constant->genericPPtrBindings = allocator.ConstructArray<BoundCurve> (constant->genericPPtrBindingsCount);
+
+ constant->allowConstantClipSamplingOptimization = allowConstantClipSamplingOptimization;
+
+
+ // Bind Generic properties
+ for (int i=0; i<constant->genericBindingsCount; i++)
+ {
+ constant->genericBindings[i].targetObject = NULL;
+ constant->genericBindings[i].targetType = kUnbound;
+
+ BindingHash fullPath = animationSet.genericBindings[i].path;
+ int skeletonIndex = mecanim::skeleton::SkeletonFindNode(skeleton, fullPath);
+
+ int transformIndex = FindTransformBindingIndexBySkeletonIndex (transformBindingCache, skeletonIndex);
+ if (transformIndex != -1)
+ bindingCache.BindGeneric(animationSet.genericBindings[i], *transformBindingCache[transformIndex].transform, constant->genericBindings[i]);
+ }
+
+ // Bind Generic PPtr properties
+ for (int i=0; i<constant->genericPPtrBindingsCount; i++)
+ {
+ constant->genericPPtrBindings[i].targetObject = NULL;
+ constant->genericPPtrBindings[i].targetType = kUnbound;
+
+ BindingHash fullPath = animationSet.genericPPtrBindings[i].path;
+ int skeletonIndex = mecanim::skeleton::SkeletonFindNode(skeleton, fullPath);
+
+ int transformIndex = FindTransformBindingIndexBySkeletonIndex (transformBindingCache, skeletonIndex);
+ if (transformIndex != -1)
+ bindingCache.BindPPtrGeneric(animationSet.genericPPtrBindings[i], *transformBindingCache[transformIndex].transform, constant->genericPPtrBindings[i]);
+ }
+
+ constant->controllerBindingConstant = CreateControllerBindingConstant (controller, animationSet.animationSet, animationSet.animationSet->m_DynamicFullValuesConstant, optimizedValueArrayConstantCount, avatar, allocator);
+
+ // Gravity weight should must be in both optimized and non-optimized ValueArray
+ Assert(animationSet.animationSet->m_GravityWeightIndex == -1 || animationSet.animationSet->m_GravityWeightIndex < constant->controllerBindingConstant->m_DynamicValuesDefault->m_FloatCount);
+
+ // Bind Controller skeleton to dynamic value array
+ BindControllerTQSMap(animationSet, *skeleton, nonConstantTransformBindingCount, NULL, transformBindingCache.begin(), false, constant->controllerBindingConstant, allocator);
+
+ RegisterGenericBindingObjects(constant);
+
+ InitializeDefaultValues (*constant, avatar, false, *constant->controllerBindingConstant);
+
+ return constant;
+ }
+
+ void DestroyAnimatorGenericBindings (AnimatorGenericBindingConstant* bindingConstant, mecanim::memory::Allocator& allocator)
+ {
+ if (bindingConstant != NULL)
+ {
+ UnregisterGenericBindingObjects(bindingConstant);
+
+ DestroyControllerBindingConstant(bindingConstant->controllerBindingConstant, allocator);
+ allocator.Deallocate(bindingConstant->transformBindings);
+ allocator.Deallocate(bindingConstant->genericBindings);
+ allocator.Deallocate(bindingConstant);
+ }
+ }
+
+ ////// Get & Set Values
+ void SetGenericPPtrPropertyValues (const AnimatorGenericBindingConstant& bindings, const mecanim::ValueArray &values)
+ {
+ for (int bindIndex = 0;bindIndex != bindings.genericPPtrBindingsCount;bindIndex++)
+ {
+ const BoundCurve& binding = bindings.genericPPtrBindings[bindIndex];
+ int targetType = binding.targetType;
+
+ if (targetType == kUnbound)
+ continue;
+
+ Assert (targetType >= kMinSinglePropertyBinding);
+
+ mecanim::int32_t value = 0;
+ values.ReadData(value, bindIndex);
+ SetBoundCurveIntValue (binding, value);
+ }
+ }
+
+ void SetGenericFloatPropertyValues (const AnimatorGenericBindingConstant& bindings, const mecanim::ValueArray &values)
+ {
+ Object* lastAwakeFromLoadObject = NULL;
+ for (int bindIndex = 0;bindIndex != bindings.genericBindingsCount;bindIndex++)
+ {
+ const BoundCurve& binding = bindings.genericBindings[bindIndex];
+ int targetType = binding.targetType;
+
+ if (targetType == kUnbound)
+ continue;
+
+ // When applying multiple properties to the same object in a row.
+ // Call AwakeFromLoad / SetDirty only once.
+ if (ShouldAwakeGeneric(binding))
+ {
+ if (lastAwakeFromLoadObject != binding.targetObject)
+ {
+ if (lastAwakeFromLoadObject != NULL)
+ BoundCurveValueAwakeGeneric(*lastAwakeFromLoadObject);
+ lastAwakeFromLoadObject = binding.targetObject;
+ }
+ }
+
+ Assert (targetType >= kMinSinglePropertyBinding);
+
+ float value = 0.0F;
+ values.ReadData(value, bindIndex);
+
+ SetBoundCurveFloatValue (binding, value);
+ }
+
+ if (lastAwakeFromLoadObject != NULL)
+ BoundCurveValueAwakeGeneric(*lastAwakeFromLoadObject);
+ }
+
+
+ void SetTransformPropertyApplyMainThread (Transform& root, const AvatarBindingConstant& avatarBindings, bool skipRoot, int mask)
+ {
+ // Send TransformChanged message
+ int transformChangedMask = avatarBindings.transformChangedMask & mask;
+ if (transformChangedMask != 0)
+ {
+ if(skipRoot)
+ {
+ for(int childIter = 0; childIter < root.m_Children.size(); childIter++)
+ {
+ root.m_Children[childIter]->SendTransformChanged(transformChangedMask);
+ }
+ }
+ else
+ {
+ root.SendTransformChanged(transformChangedMask);
+ }
+ }
+
+ // In The editor we set dirty so the inspector UI is always up to date
+ #if UNITY_EDITOR
+ for (int bindIndex = 0;bindIndex != avatarBindings.skeletonBindingsCount;bindIndex++)
+ {
+ Transform* targetTransform = avatarBindings.skeletonBindings[bindIndex];
+ if (targetTransform)
+ targetTransform->SetDirty();
+ }
+ #endif
+ }
+
+ void SetTransformPropertyApplyMainThread (Transform& root, const AnimatorGenericBindingConstant& bindings, const AvatarBindingConstant& avatarBindings, bool skipRoot)
+ {
+ // Send TransformChanged message
+ if (bindings.transformChangedMask != 0)
+ {
+ if(skipRoot)
+ {
+ for(int childIter = 0; childIter < root.m_Children.size(); childIter++)
+ {
+ root.m_Children[childIter]->SendTransformChanged(bindings.transformChangedMask);
+ }
+ }
+ else
+ {
+ root.SendTransformChanged(bindings.transformChangedMask);
+ }
+ }
+
+ SetTransformPropertyApplyMainThread(root, avatarBindings, skipRoot, ~bindings.transformChangedMask);
+
+ // In The editor we set dirty so the inspector UI is always up to date
+ #if UNITY_EDITOR
+ for (int bindIndex = 0;bindIndex != bindings.transformBindingsCount;bindIndex++)
+ {
+ const BoundCurve& binding = bindings.transformBindings[bindIndex];
+ Transform* targetTransform = reinterpret_cast<Transform*> (binding.targetObject);
+ if (targetTransform)
+ targetTransform->SetDirty();
+ }
+ #endif
+ }
+
+
+ void SetHumanTransformPropertyValues (const AvatarBindingConstant& bindings, const mecanim::skeleton::SkeletonPose& pose)
+ {
+ int transformCount = bindings.skeletonBindingsCount;
+ Assert(pose.m_Count == transformCount);
+
+ // skip root node i = 1
+ for(int i = 1; i < transformCount; i++)
+ {
+ Transform* transform = bindings.skeletonBindings[i];
+ if (transform != NULL)
+ {
+ Vector3f t = float4ToVector3f(pose.m_X[i].t);
+ transform->SetLocalPositionWithoutNotification(t);
+
+ Quaternionf q = float4ToQuaternionf(pose.m_X[i].q);
+ transform->SetLocalRotationWithoutNotification(q);
+ }
+ }
+ }
+
+ void SetFlattenedSkeletonTransformsMainThread (const AvatarBindingConstant& bindings, const mecanim::skeleton::SkeletonPose& globalSpacePose, const mecanim::animation::AvatarConstant& avatar)
+ {
+ for (size_t i=0;i < bindings.exposedTransformCount;i++)
+ {
+ ExposedTransform& exposedTransform = bindings.exposedTransforms[i];
+ if (exposedTransform.transform)
+ {
+ const math::xform& globalXForm = globalSpacePose.m_X[exposedTransform.skeletonIndexForUpdateTransform];
+ exposedTransform.transform->SetPositionAndRotation(float4ToVector3f(globalXForm.t), float4ToQuaternionf(globalXForm.q));
+ }
+ }
+ }
+
+ void SetGenericTransformPropertyValues (const AnimatorGenericBindingConstant& bindings, const mecanim::ValueArray &values, Transform *skipTransform)
+ {
+ int rotationIndex = 0;
+ int positionIndex = 0;
+ int scaleIndex = 0;
+
+ for (int bindIndex = 0;bindIndex != bindings.transformBindingsCount;bindIndex++)
+ {
+ const BoundCurve& binding = bindings.transformBindings[bindIndex];
+ int targetType = binding.targetType;
+
+ Transform* targetTransform = reinterpret_cast<Transform*> (binding.targetObject);
+
+ if (targetType == kBindTransformRotation)
+ {
+ if(targetTransform && (targetTransform != skipTransform ))
+ {
+ math::float4 value = values.ReadQuaternion(rotationIndex);
+ targetTransform->SetLocalRotationWithoutNotification(float4ToQuaternionf(value));
+ }
+ rotationIndex++;
+ }
+ else if (targetType == kBindTransformPosition)
+ {
+ if(targetTransform && (targetTransform != skipTransform ))
+ {
+ math::float4 value = values.ReadPosition(positionIndex);
+ targetTransform->SetLocalPositionWithoutNotification(float4ToVector3f(value));
+ }
+
+ positionIndex++;
+ }
+ else if (targetType == kBindTransformScale)
+ {
+ if(targetTransform && (targetTransform != skipTransform ))
+ {
+ math::float4 value = values.ReadScale(scaleIndex);
+ targetTransform->SetLocalScaleWithoutNotification(float4ToVector3f(value));
+ }
+
+ scaleIndex++;
+ }
+ }
+ }
+
+
+ static void GetDefaultTransformValues (const BoundCurve* transformBindings, size_t transformBindingsCount, mecanim::ValueArray &values)
+ {
+ int positionIndex = 0;
+ int rotationIndex = 0;
+ int scaleIndex = 0;
+
+ for (int bindIndex = 0; bindIndex < transformBindingsCount; bindIndex++)
+ {
+ const BoundCurve& binding = transformBindings[bindIndex];
+
+ Transform* targetTransform = reinterpret_cast<Transform*> (binding.targetObject);
+ if (binding.targetType == kBindTransformPosition)
+ {
+ if(targetTransform)
+ values.WritePosition(Vector3fTofloat4(targetTransform->GetLocalPosition()), positionIndex);
+
+ positionIndex++;
+ }
+ else if (binding.targetType == kBindTransformRotation)
+ {
+ if(targetTransform)
+ values.WriteQuaternion(QuaternionfTofloat4(targetTransform->GetLocalRotation()), rotationIndex);
+
+ rotationIndex++;
+ }
+ else if (binding.targetType == kBindTransformScale)
+ {
+ if(targetTransform)
+ values.WriteScale(Vector3fTofloat4(targetTransform->GetLocalScale()), scaleIndex);
+
+ scaleIndex++;
+ }
+ }
+ }
+
+ static void GetDefaultGenericFloatValues (const AnimatorGenericBindingConstant& bindings, mecanim::ValueArray &values)
+ {
+ for (int bindIndex = 0; bindIndex < bindings.genericBindingsCount; bindIndex++)
+ {
+ const BoundCurve& binding = bindings.genericBindings[bindIndex];
+ int targetType = binding.targetType;
+ if (targetType == kUnbound)
+ continue;
+
+ Assert (targetType >= kMinSinglePropertyBinding);
+
+ float value = GetBoundCurveFloatValue (binding);
+
+ values.WriteData(value, bindIndex);
+ }
+ }
+
+ static void GetDefaultGenericPPtrValues (const AnimatorGenericBindingConstant& bindings, mecanim::ValueArray &values)
+ {
+ for (int bindIndex = 0; bindIndex < bindings.genericPPtrBindingsCount; bindIndex++)
+ {
+ const BoundCurve& binding = bindings.genericPPtrBindings[bindIndex];
+ int targetType = binding.targetType;
+ if (targetType == kUnbound)
+ continue;
+
+ Assert (targetType >= kMinSinglePropertyBinding);
+
+ mecanim::int32_t value = GetBoundCurveIntValue (binding);
+
+ values.WriteData(value, bindIndex);
+ }
+ }
+
+ static void InitializeDefaultValues (const UnityEngine::Animation::AnimatorGenericBindingConstant& genericBinding, const mecanim::animation::AvatarConstant* avatar, bool hasTransformHierarchy, mecanim::animation::ControllerBindingConstant& controllerBindingConstant)
+ {
+ const mecanim::skeleton::Skeleton* skeleton = avatar->m_AvatarSkeleton.Get();
+ const mecanim::skeleton::SkeletonPose* skeletonPose = avatar->m_AvatarSkeletonPose.Get();
+ const mecanim::animation::AnimationSet& animSet = *controllerBindingConstant.m_AnimationSet;
+
+ if (hasTransformHierarchy)
+ {
+ // Get default values from transform
+ GetDefaultTransformValues (genericBinding.transformBindings, genericBinding.transformBindingsCount, *controllerBindingConstant.m_DynamicValuesDefault);
+ }
+ else
+ {
+ // When there is no transform & game object hierarchy, get it from the skeleton.
+ if (skeleton != NULL && skeletonPose != NULL)
+ ValueFromSkeletonPose (*skeleton, *skeletonPose, controllerBindingConstant.m_SkeletonTQSMap, *controllerBindingConstant.m_DynamicValuesDefault);
+ }
+
+ // Get default values from generic bindings
+ GetDefaultGenericFloatValues (genericBinding, *controllerBindingConstant.m_DynamicValuesDefault);
+ GetDefaultGenericPPtrValues (genericBinding, *controllerBindingConstant.m_DynamicValuesDefault);
+
+ // Copy default parameters from controller defaults to m_DynamicValuesDefault
+ const mecanim::animation::ControllerConstant* controller = controllerBindingConstant.m_Controller;
+ mecanim::ValueArrayReverseCopy(controller->m_Values.Get(), controller->m_DefaultValues.Get(), controllerBindingConstant.m_DynamicValuesConstant, controllerBindingConstant.m_DynamicValuesDefault, animSet.m_AdditionalIndexArray);
+ }
+
+} }
diff --git a/Runtime/Animation/AnimatorGenericBindings.h b/Runtime/Animation/AnimatorGenericBindings.h
new file mode 100644
index 0000000..4e2023b
--- /dev/null
+++ b/Runtime/Animation/AnimatorGenericBindings.h
@@ -0,0 +1,95 @@
+#pragma once
+
+#include <limits>
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/mecanim/generic/valuearray.h"
+#include "Runtime/mecanim/skeleton/skeleton.h"
+#include "Runtime/mecanim/animation/avatar.h"
+
+typedef UInt32 BindingHash;
+
+namespace Unity { class GameObject; }
+
+namespace UnityEngine
+{
+namespace Animation
+{
+ struct AnimationClipBindingConstant;
+ struct AnimationSetBindings;
+ struct BoundCurve;
+
+
+ struct ExposedTransform
+ {
+ Transform* transform;
+ int skeletonIndex;
+ int skeletonIndexForUpdateTransform;
+ };
+
+ struct AvatarBindingConstant
+ {
+ // For non-optimized mode (the Transform hierarchy is there)
+ size_t skeletonBindingsCount;
+ Transform** skeletonBindings;
+
+ int transformChangedMask;
+
+ // For optimized mode
+ size_t exposedTransformCount;
+ ExposedTransform* exposedTransforms;
+ };
+
+ struct AnimatorGenericBindingConstant
+ {
+ size_t transformBindingsCount;
+ BoundCurve* transformBindings;
+
+ size_t genericBindingsCount;
+ BoundCurve* genericBindings;
+
+ size_t genericPPtrBindingsCount;
+ BoundCurve* genericPPtrBindings;
+
+ int transformChangedMask;
+
+ mecanim::animation::ControllerBindingConstant* controllerBindingConstant;
+
+ bool allowConstantClipSamplingOptimization;
+ };
+
+ AvatarBindingConstant* CreateAvatarBindingConstant (Transform& root, const mecanim::animation::AvatarConstant* avatar, mecanim::memory::Allocator& allocator);
+ AvatarBindingConstant* CreateAvatarBindingConstantOpt (Transform& root, const mecanim::animation::AvatarConstant* avatar, mecanim::memory::Allocator& allocator);
+ void DestroyAvatarBindingConstant (AvatarBindingConstant* bindingConstant, mecanim::memory::Allocator& allocator);
+
+ /// Bind multiple animation clips against a GameObject
+ /// outputCurveIndexToBindingIndex is an array of arrays that remaps from the curve index of each clip into the bound curves array
+ /// returns a binding constant.
+ AnimatorGenericBindingConstant* CreateAnimatorGenericBindings (const AnimationSetBindings& animationSet, Transform& root, const mecanim::animation::AvatarConstant* avatar, const mecanim::animation::ControllerConstant* controller, mecanim::memory::Allocator& allocator);
+ AnimatorGenericBindingConstant* CreateAnimatorGenericBindingsOpt (const AnimationSetBindings& animationSet, Transform& root, const mecanim::animation::AvatarConstant* avatar, const mecanim::animation::ControllerConstant* controller, mecanim::memory::Allocator& allocator);
+ void DestroyAnimatorGenericBindings (AnimatorGenericBindingConstant* bindingConstant, mecanim::memory::Allocator& allocator);
+
+ /// Batch assign an array of values to the bound locations (Also calls AwakeFromLoad)
+ /// Must always be called from the main thread
+ void SetGenericFloatPropertyValues (const AnimatorGenericBindingConstant& bindings, const mecanim::ValueArray& values);
+ void SetGenericPPtrPropertyValues (const AnimatorGenericBindingConstant& bindings, const mecanim::ValueArray& values);
+
+ /// Batch assign transform properties to the bound Transform components
+ /// Can be invoked from another thread if we are sure no one will delete Transforms at the same time on another thread.
+ void SetGenericTransformPropertyValues (const AnimatorGenericBindingConstant& bindings, const mecanim::ValueArray& values, Transform *skipTransform);
+ void SetHumanTransformPropertyValues (const AvatarBindingConstant& bindings, const mecanim::skeleton::SkeletonPose& pose);
+
+ /// Invoke TransformChanged callbacks and SetDirty
+ /// Must always be called from the main thread
+ void SetTransformPropertyApplyMainThread (Transform& root, const AnimatorGenericBindingConstant& bindings, const AvatarBindingConstant& avatarBindings, bool skipRoot);
+ void SetTransformPropertyApplyMainThread (Transform& root, const AvatarBindingConstant& avatarBindings, bool skipRoot, int mask = std::numeric_limits<int>::max() );
+
+ /// Set transforms for optimized characters
+ /// Must always be called from the main thread
+ void SetFlattenedSkeletonTransformsMainThread (const AvatarBindingConstant& bindings, const mecanim::skeleton::SkeletonPose& globalSpacePose, const mecanim::animation::AvatarConstant& avatar);
+
+ void UnregisterAvatarBindingObjects(AvatarBindingConstant* bindingConstant);
+ void UnregisterGenericBindingObjects(AnimatorGenericBindingConstant* bindingConstant);
+
+}
+}
diff --git a/Runtime/Animation/AnimatorManager.cpp b/Runtime/Animation/AnimatorManager.cpp
new file mode 100644
index 0000000..2f76077
--- /dev/null
+++ b/Runtime/Animation/AnimatorManager.cpp
@@ -0,0 +1,110 @@
+#include "UnityPrefix.h"
+#include "AnimatorManager.h"
+#include "Animator.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Core/Callbacks/PlayerLoopCallbacks.h"
+
+static AnimatorManager* gAnimatorManager = NULL;
+
+void AnimatorManager::FixedUpdateFKMove()
+{
+ if(m_FixedUpdateAvatars.size() > 0 && GetTimeManager().GetFixedDeltaTime())
+ {
+ Animator::UpdateAvatars(m_FixedUpdateAvatars.begin(), m_FixedUpdateAvatars.size(), GetTimeManager().GetFixedDeltaTime(),true,false);
+ }
+}
+
+void AnimatorManager::FixedUpdateRetargetIKWrite()
+{
+ //@TODO: Why do we do FixedUpdateRetargetIKWrite?
+ // This visually wrong when you use physics interpolation to get smooth root motion, but then the animation of the characters limbs only updates at a very low physics framerate.
+ // It would be better to run UpdateRetargetWrite in the dynamic framerate.
+
+ if(m_FixedUpdateAvatars.size() > 0 && GetTimeManager().GetFixedDeltaTime())
+ {
+ Animator::UpdateAvatars(m_FixedUpdateAvatars.begin(), m_FixedUpdateAvatars.size(), GetTimeManager().GetFixedDeltaTime(),false,true);
+ }
+}
+
+void AnimatorManager::UpdateFKMove ()
+{
+ if(m_UpdateAvatars.size() > 0 && GetTimeManager().GetDeltaTime())
+ {
+ Animator::UpdateAvatars(m_UpdateAvatars.begin(), m_UpdateAvatars.size(), GetDeltaTime(),true,false);
+ }
+}
+
+void AnimatorManager::UpdateRetargetIKWrite ()
+{
+ if(m_UpdateAvatars.size() > 0 && GetTimeManager().GetDeltaTime())
+ {
+ Animator::UpdateAvatars(m_UpdateAvatars.begin(), m_UpdateAvatars.size(), GetDeltaTime(),false,true);
+ }
+}
+
+void AnimatorManager::AddAnimator (Animator& animator)
+{
+ if (animator.GetEnabled() && animator.IsActive() && animator.IsValid())
+ {
+ if(animator.GetAnimatePhysics())
+ {
+ animator.SetFixedBehaviourIndex(m_FixedUpdateAvatars.size());
+ m_FixedUpdateAvatars.push_back(&animator);
+ }
+ else
+ {
+ animator.SetBehaviourIndex(m_UpdateAvatars.size());
+ m_UpdateAvatars.push_back(&animator);
+ }
+ }
+}
+
+void AnimatorManager::RemoveAnimator (Animator& animator)
+{
+ int index = animator.GetBehaviourIndex();
+
+ if(index != -1)
+ {
+ Animator* backBehaviour = m_UpdateAvatars.back();
+ m_UpdateAvatars[index] = backBehaviour;
+ backBehaviour->SetBehaviourIndex(index);
+ animator.SetBehaviourIndex(-1);
+ m_UpdateAvatars.pop_back();
+ }
+
+ int fixedIndex = animator.GetFixedBehaviourIndex();
+
+ if(fixedIndex != -1)
+ {
+ Animator* backBehaviour = m_FixedUpdateAvatars.back();
+ m_FixedUpdateAvatars[fixedIndex] = backBehaviour;
+ backBehaviour->SetFixedBehaviourIndex(fixedIndex);
+ animator.SetFixedBehaviourIndex(-1);
+ m_FixedUpdateAvatars.pop_back();
+ }
+}
+
+void AnimatorManager::InitializeClass ()
+{
+ Assert(gAnimatorManager == NULL);
+ gAnimatorManager = UNITY_NEW_AS_ROOT (AnimatorManager, kMemAnimation, "AnimatorManager", "");
+
+ REGISTER_PLAYERLOOP_CALL (AnimatorFixedUpdateRetargetIKWrite, GetAnimatorManager().FixedUpdateRetargetIKWrite());
+ REGISTER_PLAYERLOOP_CALL (AnimatorUpdateRetargetIKWrite, GetAnimatorManager().UpdateRetargetIKWrite ());
+ REGISTER_PLAYERLOOP_CALL (AnimatorUpdateFKMove, GetAnimatorManager().UpdateFKMove ());
+ REGISTER_PLAYERLOOP_CALL (AnimatorFixedUpdateFKMove, GetAnimatorManager().FixedUpdateFKMove ());
+}
+
+void AnimatorManager::CleanupClass ()
+{
+ UNITY_DELETE (gAnimatorManager, kMemAnimation);
+ gAnimatorManager = NULL;
+
+
+}
+
+AnimatorManager& GetAnimatorManager ()
+{
+ Assert(gAnimatorManager != NULL);
+ return *gAnimatorManager;
+} \ No newline at end of file
diff --git a/Runtime/Animation/AnimatorManager.h b/Runtime/Animation/AnimatorManager.h
new file mode 100644
index 0000000..5e13463
--- /dev/null
+++ b/Runtime/Animation/AnimatorManager.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "Runtime/Utilities/dynamic_array.h"
+class Animator;
+
+class AnimatorManager
+{
+ public:
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ void AddAnimator(Animator& animator);
+ void RemoveAnimator(Animator& animator);
+
+ virtual void FixedUpdateFKMove();
+ virtual void FixedUpdateRetargetIKWrite();
+
+ virtual void UpdateFKMove();
+ virtual void UpdateRetargetIKWrite();
+
+private:
+
+ dynamic_array<Animator*> m_UpdateAvatars;
+ dynamic_array<Animator*> m_FixedUpdateAvatars;
+};
+
+AnimatorManager& GetAnimatorManager (); \ No newline at end of file
diff --git a/Runtime/Animation/AnimatorOverrideController.cpp b/Runtime/Animation/AnimatorOverrideController.cpp
new file mode 100644
index 0000000..2633ab4
--- /dev/null
+++ b/Runtime/Animation/AnimatorOverrideController.cpp
@@ -0,0 +1,302 @@
+#include "UnityPrefix.h"
+#include "AnimatorOverrideController.h"
+#include "AnimationSetBinding.h"
+#include "RuntimeAnimatorController.h"
+#include "AnimationClipBindings.h"
+#include "GenericAnimationBindingCache.h"
+#include "AnimationClip.h"
+
+#if UNITY_EDITOR
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/Backend/ScriptingInvocation.h"
+#endif
+
+#define DIRTY_AND_INVALIDATE() ClearAsset(); SetDirty(); NotifyObjectUsers( kDidModifyAnimatorController )
+
+struct FindClip
+{
+ char const* m_ClipName;
+ FindClip(char const* clipName):m_ClipName(clipName){}
+
+ bool operator()(PPtr<AnimationClip> const& clip){ return strcmp(clip->GetName(), m_ClipName) == 0 ; }
+};
+
+struct FindOriginalClipByName
+{
+ char const* m_ClipName;
+ FindOriginalClipByName(char const* clipName):m_ClipName(clipName){}
+
+ bool operator()(AnimationClipOverride const& overrideClip){ return strcmp(overrideClip.m_OriginalClip->GetName(), m_ClipName) == 0 ; }
+};
+
+struct FindOriginalClip
+{
+ PPtr<AnimationClip> const& m_Clip;
+ FindOriginalClip(PPtr<AnimationClip> const& clip):m_Clip(clip){}
+
+ bool operator()(AnimationClipOverride const& overrideClip){ return overrideClip.m_OriginalClip == m_Clip ; }
+};
+
+PPtr<AnimationClip> return_original(AnimationClipOverride const& overrideClip){ return overrideClip.m_OriginalClip; }
+PPtr<AnimationClip> return_override(AnimationClipOverride const& overrideClip){ return overrideClip.m_OverrideClip; }
+PPtr<AnimationClip> return_effective(AnimationClipOverride const& overrideClip){ return overrideClip.GetEffectiveClip(); }
+
+IMPLEMENT_OBJECT_SERIALIZE (AnimatorOverrideController)
+IMPLEMENT_CLASS_HAS_INIT (AnimatorOverrideController)
+
+INSTANTIATE_TEMPLATE_TRANSFER(AnimatorOverrideController)
+
+AnimatorOverrideController::AnimatorOverrideController(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode),
+m_AnimationSetBindings(0),
+m_AnimationSetNode(this),
+m_Allocator(label)
+{
+}
+
+AnimatorOverrideController::~AnimatorOverrideController()
+{
+}
+
+void AnimatorOverrideController::AwakeFromLoad(AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad(mode);
+
+ if(!m_Controller.IsNull())
+ m_Controller->AddObjectUser(m_AnimationSetNode);
+
+ NotifyObjectUsers( kDidModifyAnimatorController );
+}
+
+void AnimatorOverrideController::InitializeClass ()
+{
+ REGISTER_MESSAGE_VOID(AnimatorOverrideController, kDidModifyAnimatorController, ClearAsset);
+ REGISTER_MESSAGE_VOID(AnimatorOverrideController, kDidModifyMotion, ClearAsset);
+}
+
+template<class Functor> PPtr<AnimationClip> AnimatorOverrideController::FindAnimationClipInMap(PPtr<AnimationClip> const& clip, Functor functor, PPtr<AnimationClip> const& defaultClip)const
+{
+ AnimationClipOverrideVector::const_iterator it = std::find_if(m_Clips.begin(), m_Clips.end(), FindOriginalClip(clip) );
+ return it != m_Clips.end() ? functor(*it) : defaultClip;
+}
+
+// Return the clip list from controller
+AnimationClipVector AnimatorOverrideController::GetOriginalClips()const
+{
+ AnimationClipVector clips;
+ if(m_Controller.IsNull())
+ return clips;
+
+ return m_Controller->GetAnimationClips();
+}
+
+// Return the merged clip list that should be used to drive the animator
+// Always start from original clip list and search for each clip if there is a match in m_Clips
+AnimationClipVector AnimatorOverrideController::GetAnimationClips() const
+{
+ AnimationClipVector controllerClips = GetOriginalClips();
+ AnimationClipVector clips;
+ clips.reserve(controllerClips.size());
+
+ for(AnimationClipVector::const_iterator clipIt = controllerClips.begin(); clipIt != controllerClips.end() ; ++clipIt)
+ clips.push_back( FindAnimationClipInMap(*clipIt, return_effective, *clipIt) );
+
+ return clips;
+}
+
+AnimationClipVector AnimatorOverrideController::GetOverrideClips()const
+{
+ AnimationClipVector controllerClips = GetOriginalClips();
+ AnimationClipVector clips;
+ clips.reserve(controllerClips.size());
+
+ for(AnimationClipVector::const_iterator clipIt = controllerClips.begin(); clipIt != controllerClips.end() ; ++clipIt)
+ clips.push_back( FindAnimationClipInMap(*clipIt, return_override, *clipIt) );
+
+ return clips;
+}
+
+mecanim::animation::ControllerConstant* AnimatorOverrideController::GetAsset()
+{
+ if(m_Controller.IsNull())
+ return 0;
+
+ return m_Controller->GetAsset();
+}
+
+void AnimatorOverrideController::BuildAsset()
+{
+ ClearAsset();
+
+ if(m_Controller.IsNull())
+ {
+ m_Clips.clear();
+ return;
+ }
+
+ mecanim::animation::ControllerConstant* controller = m_Controller->GetAsset();
+ if(controller == 0)
+ {
+ m_Clips.clear();
+ return;
+ }
+
+ RegisterAnimationClips();
+ AnimationClipVector clips = GetAnimationClips();
+ m_AnimationSetBindings = UnityEngine::Animation::CreateAnimationSetBindings(controller, clips, m_Allocator);
+}
+
+std::string AnimatorOverrideController::StringFromID(unsigned int ID) const
+{
+ if(m_Controller.IsNull())
+ return "";
+
+ return m_Controller->StringFromID(ID);
+}
+
+void AnimatorOverrideController::ClearAsset()
+{
+ DestroyAnimationSetBindings(m_AnimationSetBindings, m_Allocator);
+ m_AnimationSetBindings = 0;
+
+#if UNITY_EDITOR
+ // This is needed to update AnimatorOverrideController's inspector clip list.
+ // If a user modify the controller by adding or removing a clip we cannot dirty AnimatorOverrideController asset because the user didn't modify it but still we need to update the UI
+ // which is based on the controller clip list.
+ ScriptingInvocation invocation("UnityEngine", "AnimatorOverrideController", "OnInvalidateOverrideController");
+ invocation.AddObject(Scripting::ScriptingWrapperFor(this));
+ invocation.Invoke();
+#endif
+}
+
+template<class TransferFunction>
+void AnimatorOverrideController::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER(m_Controller);
+ TRANSFER(m_Clips);
+}
+
+
+PPtr<RuntimeAnimatorController> AnimatorOverrideController::GetAnimatorController()const
+{
+ return m_Controller;
+}
+
+void AnimatorOverrideController::SetAnimatorController(PPtr<RuntimeAnimatorController> controller)
+{
+ if(m_Controller != controller)
+ {
+ m_AnimationSetNode.Clear();
+
+ m_Controller = controller;
+
+ if(!m_Controller.IsNull())
+ m_Controller->AddObjectUser(m_AnimationSetNode);
+
+ DIRTY_AND_INVALIDATE();
+ }
+}
+
+PPtr<AnimationClip> AnimatorOverrideController::GetClip(std::string const& name, bool returnEffectiveClip)const
+{
+ // if clip 'name' is not an original clip bailout
+ PPtr<AnimationClip> clip = GetOriginalClip(name);
+ if(clip.IsNull())
+ return NULL;
+
+ return returnEffectiveClip ? FindAnimationClipInMap(clip, return_effective) : FindAnimationClipInMap(clip, return_override);
+}
+
+PPtr<AnimationClip> AnimatorOverrideController::GetClip(PPtr<AnimationClip> originalClip, bool returnEffectiveClip)const
+{
+ if(originalClip.IsNull())
+ return NULL;
+
+ return GetClip( originalClip->GetName(), returnEffectiveClip );
+}
+
+void AnimatorOverrideController::SetClip(PPtr<AnimationClip> originalClip, PPtr<AnimationClip> overrideClip)
+{
+ if(originalClip.IsNull())
+ return;
+
+ SetClip(originalClip->GetName(), overrideClip);
+}
+
+void AnimatorOverrideController::SetClip(std::string const& name, PPtr<AnimationClip> clip)
+{
+ // if clip 'name' is not an original clip bailout
+ PPtr<AnimationClip> originalClip = GetOriginalClip(name);
+ if(originalClip.IsNull())
+ return;
+
+ AnimationClipOverrideVector::iterator it = std::find_if(m_Clips.begin(), m_Clips.end(), FindOriginalClip(originalClip) );
+ if(it != m_Clips.end())
+ {
+ it->m_OverrideClip = clip;
+ DIRTY_AND_INVALIDATE();
+ }
+ else
+ {
+ AnimationClipOverride clipOverride;
+ clipOverride.m_OriginalClip = originalClip;
+ clipOverride.m_OverrideClip = clip;
+
+ m_Clips.push_back(clipOverride);
+ DIRTY_AND_INVALIDATE();
+ }
+}
+
+PPtr<AnimationClip> AnimatorOverrideController::GetOriginalClip(std::string const& name)const
+{
+ AnimationClipVector controllerClips = GetOriginalClips();
+ AnimationClipVector::const_iterator it = std::find_if(controllerClips.begin(), controllerClips.end(), FindClip( name.c_str() ) );
+ return it != controllerClips.end() ? *it : NULL;
+}
+
+UnityEngine::Animation::AnimationSetBindings* AnimatorOverrideController::GetAnimationSetBindings()
+{
+ if(m_AnimationSetBindings == 0)
+ BuildAsset();
+
+ return m_AnimationSetBindings;
+}
+
+AnimationClipVector AnimatorOverrideController::GetAnimationClipsToRegister() const
+{
+ return GetOverrideClips();
+}
+
+void AnimatorOverrideController::PerformOverrideClipListCleanup()
+{
+ AnimationClipVector clips = GetOriginalClips();
+
+ AnimationClipOverrideVector clipsToRemove;
+ AnimationClipOverrideVector::iterator it;
+ for(it = m_Clips.begin(); it != m_Clips.end(); ++it)
+ {
+ if(it->m_OriginalClip.IsNull() || it->m_OverrideClip.IsNull())
+ clipsToRemove.push_back(*it);
+ else
+ {
+ AnimationClipVector::const_iterator it2 = std::find_if(clips.begin(), clips.end(), FindClip( it->m_OriginalClip->GetName() ) );
+ if(it2 == clips.end())
+ clipsToRemove.push_back(*it);
+ }
+ }
+
+ for(it = clipsToRemove.begin(); it != clipsToRemove.end(); ++it)
+ {
+ AnimationClipOverrideVector::iterator it2 = std::find_if(m_Clips.begin(), m_Clips.end(), FindOriginalClip( it->m_OriginalClip ) );
+ if(it2 != m_Clips.end())
+ m_Clips.erase(it2);
+ }
+
+ if(clipsToRemove.size() > 0 )
+ DIRTY_AND_INVALIDATE();
+}
+
+
+#undef DIRTY_AND_INVALIDATE
diff --git a/Runtime/Animation/AnimatorOverrideController.h b/Runtime/Animation/AnimatorOverrideController.h
new file mode 100644
index 0000000..48d2905
--- /dev/null
+++ b/Runtime/Animation/AnimatorOverrideController.h
@@ -0,0 +1,97 @@
+#pragma once
+
+
+#include "Runtime/Animation/RuntimeAnimatorController.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/Misc/UserList.h"
+#include <vector>
+
+
+class AnimationClip;
+
+typedef std::vector<PPtr<AnimationClip> > AnimationClipVector;
+
+namespace UnityEngine{namespace Animation{struct AnimationSetBindings;}}
+
+struct AnimationClipOverride
+{
+ DEFINE_GET_TYPESTRING(AnimationClipOverride)
+
+ PPtr<AnimationClip> m_OriginalClip;
+ PPtr<AnimationClip> m_OverrideClip;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_OriginalClip);
+ TRANSFER(m_OverrideClip);
+ }
+
+ bool operator== (AnimationClipOverride const& other){return m_OriginalClip == other.m_OriginalClip && m_OverrideClip == other.m_OverrideClip; }
+
+ PPtr<AnimationClip> GetEffectiveClip() const {return !m_OverrideClip.IsNull() ? m_OverrideClip : m_OriginalClip; }
+};
+
+class AnimatorOverrideController : public RuntimeAnimatorController
+{
+public:
+ REGISTER_DERIVED_CLASS (AnimatorOverrideController, RuntimeAnimatorController)
+ DECLARE_OBJECT_SERIALIZE (AnimatorOverrideController)
+
+ static void InitializeClass ();
+ static void CleanupClass () {}
+
+ AnimatorOverrideController (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ virtual mecanim::animation::ControllerConstant* GetAsset();
+ virtual void BuildAsset();
+ virtual void ClearAsset ();
+
+ virtual UnityEngine::Animation::AnimationSetBindings* GetAnimationSetBindings();
+ virtual AnimationClipVector GetAnimationClips()const;
+
+ virtual std::string StringFromID(unsigned int ID) const ;
+
+ PPtr<RuntimeAnimatorController> GetAnimatorController()const;
+ void SetAnimatorController(PPtr<RuntimeAnimatorController> controller);
+
+ AnimationClipVector GetOriginalClips()const;
+ AnimationClipVector GetOverrideClips()const;
+
+ PPtr<AnimationClip> GetClip(std::string const& name, bool returnEffectiveClip)const;
+ void SetClip(std::string const& name, PPtr<AnimationClip> clip);
+ PPtr<AnimationClip> GetClip(PPtr<AnimationClip> originalClip, bool returnEffectiveClip)const;
+ void SetClip(PPtr<AnimationClip> originalClip, PPtr<AnimationClip> overrideClip);
+
+ void PerformOverrideClipListCleanup();
+protected:
+
+ typedef dynamic_array<AnimationClipOverride> AnimationClipOverrideVector;
+
+ PPtr<RuntimeAnimatorController> m_Controller;
+
+ // This list is a map between m_Controller clips and override clips.
+ // We should never rely on this list to return m_Controller clip list because this list may become
+ // offsync when an user edit the controller's clip list(either adding or removing a state).
+ //
+ // The map should only be updated by PerformOverrideClipListCleanup() when user edit the
+ // AnimatorOverrideController in the inspector.
+ AnimationClipOverrideVector m_Clips;
+
+ UnityEngine::Animation::AnimationSetBindings* m_AnimationSetBindings;
+ mecanim::memory::MecanimAllocator m_Allocator;
+ UserListNode m_AnimationSetNode;
+
+private:
+
+ virtual AnimationClipVector GetAnimationClipsToRegister() const;
+
+ PPtr<AnimationClip> GetOriginalClip(std::string const& name)const;
+
+ template<class Functor> PPtr<AnimationClip> FindAnimationClipInMap(PPtr<AnimationClip> const& clip, Functor functor, PPtr<AnimationClip> const& defaultClip = PPtr<AnimationClip>() )const;
+};
+
+
diff --git a/Runtime/Animation/Avatar.cpp b/Runtime/Animation/Avatar.cpp
new file mode 100644
index 0000000..7e62c5d
--- /dev/null
+++ b/Runtime/Animation/Avatar.cpp
@@ -0,0 +1,678 @@
+#include "UnityPrefix.h"
+
+#include "Avatar.h"
+
+
+#include "Runtime/mecanim/human/hand.h"
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/Blobification/BlobWrite.h"
+
+#include "MecanimUtility.h"
+
+
+Avatar::Avatar(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode),
+ m_Avatar(0),
+ m_Allocator(1024*4),
+ m_AvatarSize(0),
+ m_ObjectUsers(this)
+{
+
+}
+
+Avatar::~Avatar()
+{
+ NotifyObjectUsers( kDidModifyAvatar );
+}
+
+void Avatar::NotifyObjectUsers(const MessageIdentifier& msg)
+{
+ m_ObjectUsers.SendMessage(msg);
+}
+
+void Avatar::AwakeFromLoad(AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad(mode);
+ NotifyObjectUsers( kDidModifyAvatar );
+}
+
+
+void Avatar::CheckConsistency ()
+{
+ Super::CheckConsistency();
+ mecanim::animation::AvatarConstant* cst = GetAsset();
+ if(cst != 0)
+ {
+ //@TODO: It looks like CheckConsistency is being abused to generate m_HumanSkeletonIndexArray??? Shouldn't the model importer do this or whatever generates the avatar constant
+
+ // [case 522188] This is an old maya file with avatar already created.
+ // At this time m_HumanSkeletonIndexArray was not there and in this particular case the user did open
+ // the file on a pc without maya so we couldn't reimport the file and create a valid avatar.
+ if(cst->isHuman() && cst->m_HumanSkeletonIndexCount != cst->m_Human->m_Skeleton->m_Count)
+ {
+ cst->m_HumanSkeletonIndexCount = cst->m_Human->m_Skeleton->m_Count;
+
+ // No need to deallocate memory here since we are using the ChainedAllocator.
+ // the allocator will release the memory on next reset().
+ cst->m_HumanSkeletonIndexArray = m_Allocator.ConstructArray<mecanim::int32_t>(cst->m_HumanSkeletonIndexCount);
+
+ mecanim::skeleton::SkeletonBuildIndexArray(cst->m_HumanSkeletonIndexArray.Get(), cst->m_Human->m_Skeleton.Get(), cst->m_AvatarSkeleton.Get());
+ }
+ }
+}
+
+
+IMPLEMENT_OBJECT_SERIALIZE (Avatar)
+IMPLEMENT_CLASS (Avatar)
+
+template<class TransferFunction>
+void Avatar::Transfer (TransferFunction& transfer)
+{
+ SETPROFILERLABEL(AvatarConstant);
+
+ Super::Transfer (transfer);
+
+ TRANSFER(m_AvatarSize);
+
+ if(m_Avatar == 0 )
+ m_Allocator.Reserve(m_AvatarSize);
+
+ transfer.SetUserData(&m_Allocator);
+ TRANSFER_NULLABLE(m_Avatar, mecanim::animation::AvatarConstant);
+ TRANSFER(m_TOS);
+}
+
+void Avatar::SetAsset (mecanim::animation::AvatarConstant* avatarConstant, TOSVector const& tos)
+{
+ // Free previously allocated memory
+ m_Allocator.Reset();
+
+ size_t size = m_AvatarSize;
+ m_Avatar = CopyBlob(*avatarConstant, m_Allocator, size );
+ m_AvatarSize = size;
+ m_TOS = tos;
+
+ NotifyObjectUsers( kDidModifyAvatar );
+}
+
+mecanim::animation::AvatarConstant* Avatar::GetAsset()
+{
+ return m_Avatar;
+}
+
+const mecanim::animation::AvatarConstant* Avatar::GetAsset() const
+{
+ return m_Avatar;
+}
+
+TOSVector const& Avatar::GetTOS() const
+{
+ return m_TOS;
+}
+
+bool Avatar::IsValid()const
+{
+ return GetAsset() != 0;
+}
+
+bool Avatar::IsHuman() const
+{
+ return m_Avatar && m_Avatar->isHuman();
+}
+
+bool Avatar::HasRootMotion() const
+{
+ return m_Avatar && m_Avatar->m_RootMotionBoneIndex != -1;
+}
+
+float Avatar::GetHumanScale() const
+{
+ return IsHuman() ? m_Avatar->m_Human->m_Scale : 1;
+}
+
+float Avatar::GetLeftFeetBottomHeight() const
+{
+ if(!IsHuman())
+ return 0.f;
+
+ return HumanGetFootHeight(m_Avatar->m_Human.Get(), true);
+}
+
+float Avatar::GetRightFeetBottomHeight() const
+{
+ if(!IsHuman())
+ return 0.f;
+
+ return HumanGetFootHeight(m_Avatar->m_Human.Get(), false);
+}
+
+
+void Avatar::SetParameter(int parameterId, float value)
+{
+ mecanim::animation::AvatarConstant* avatar = GetAsset();
+ if(avatar)
+ {
+ switch(parameterId)
+ {
+ case UpperArmTwist: avatar->m_Human->m_ArmTwist = value; break;
+ case LowerArmTwist: avatar->m_Human->m_ForeArmTwist = value; break;
+ case UpperLegTwist: avatar->m_Human->m_UpperLegTwist = value; break;
+ case LowerLegTwsit: avatar->m_Human->m_LegTwist = value; break;
+ case ArmStretch: avatar->m_Human->m_ArmStretch = value; break;
+ case LegStretch: avatar->m_Human->m_LegStretch = value; break;
+ case FeetSpacing: avatar->m_Human->m_FeetSpacing = value; break;
+ default: break;
+ }
+ }
+}
+
+void Avatar::SetMuscleMinMax(int muscleId, float min, float max)
+{
+ int humanId = HumanTrait::BoneFromMuscle(muscleId);
+
+ mecanim::animation::AvatarConstant* avatar = GetAsset();
+
+ int boneId = HumanTrait::GetBoneId(*this, humanId);
+ if(boneId != -1)
+ {
+ int axesId = avatar->m_Human->m_Skeleton->m_Node[boneId].m_AxesId;
+ if(axesId != -1)
+ {
+ math::float4 maxv = avatar->m_Human->m_Skeleton->m_AxesArray[axesId].m_Limit.m_Max;
+ math::float4 minv = avatar->m_Human->m_Skeleton->m_AxesArray[axesId].m_Limit.m_Min;
+
+ int musclex = HumanTrait::MuscleFromBone(humanId, 0);
+ int muscley = HumanTrait::MuscleFromBone(humanId, 1);
+ int musclez = HumanTrait::MuscleFromBone(humanId, 2);
+
+ if(musclex == muscleId)
+ {
+ minv.x() = math::radians(min);
+ maxv.x() = math::radians(max);
+ }
+ else if(muscley == muscleId)
+ {
+ minv.y() = math::radians(min);
+ maxv.y() = math::radians(max);
+ }
+ else if(musclez == muscleId)
+ {
+ minv.z() = math::radians(min);
+ maxv.z() = math::radians(max);
+ }
+
+ avatar->m_Human->m_Skeleton->m_AxesArray[axesId].m_Limit.m_Max = maxv;
+ avatar->m_Human->m_Skeleton->m_AxesArray[axesId].m_Limit.m_Min = minv;
+ }
+ }
+}
+
+float Avatar::GetAxisLength(int humanId)const
+{
+ float ret = 0.0f;
+
+ mecanim::animation::AvatarConstant const* avatar = GetAsset();
+
+ int boneId = HumanTrait::GetBoneId(*this, humanId);
+ if(boneId != -1)
+ {
+ int axesId = avatar->m_Human->m_Skeleton->m_Node[boneId].m_AxesId;
+ if(axesId != -1)
+ {
+ ret = avatar->m_Human->m_Skeleton->m_AxesArray[axesId].m_Length;
+ }
+ }
+
+ return ret;
+}
+
+Quaternionf Avatar::GetPreRotation(int humanId)const
+{
+ math::float4 ret = math::quatIdentity();
+
+ mecanim::animation::AvatarConstant const* avatar = GetAsset();
+
+ int boneId = HumanTrait::GetBoneId(*this, humanId);
+ if(boneId != -1)
+ {
+ int axesId = avatar->m_Human->m_Skeleton->m_Node[boneId].m_AxesId;
+ if(axesId != -1)
+ {
+ ret = avatar->m_Human->m_Skeleton->m_AxesArray[axesId].m_PreQ;
+ }
+ }
+
+ return float4ToQuaternionf(ret);
+}
+Quaternionf Avatar::GetPostRotation(int humanId)const
+{
+ math::float4 ret = math::quatIdentity();
+
+ mecanim::animation::AvatarConstant const* avatar = GetAsset();
+
+ int boneId = HumanTrait::GetBoneId(*this, humanId);
+ if(boneId != -1)
+ {
+ int axesId = avatar->m_Human->m_Skeleton->m_Node[boneId].m_AxesId;
+ if(axesId != -1)
+ {
+ ret = avatar->m_Human->m_Skeleton->m_AxesArray[axesId].m_PostQ;
+ }
+ }
+
+ return float4ToQuaternionf(ret);
+}
+
+Quaternionf Avatar::GetZYPostQ(int index, Quaternionf const& parentQ, Quaternionf const& q)const
+{
+ mecanim::animation::AvatarConstant const* cst = GetAsset();
+
+ math::float4 qzypost = math::quatIdentity();
+
+ int id = HumanTrait::GetBoneId(*this, index);
+ if( id != -1)
+ {
+ int axesId = cst->m_Human->m_Skeleton->m_Node[id].m_AxesId;
+ if(axesId != -1)
+ {
+ math::float4 mpq = QuaternionfTofloat4(parentQ);
+ math::float4 mq = QuaternionfTofloat4(q);
+
+ math::Axes const& axes = cst->m_Human->m_Skeleton->m_AxesArray[axesId];
+
+ math::float4 lq = normalize(quatMul(quatConj(mpq),mq));
+ math::float4 dofzy = ToAxes(axes,lq);
+ dofzy.x() = 0;
+ qzypost = normalize(quatMul(mpq,quatMul( FromAxes(axes,dofzy),axes.m_PostQ)));
+ }
+ }
+ return float4ToQuaternionf(qzypost);
+}
+
+Quaternionf Avatar::GetZYRoll(int index, Vector3f const& v)const
+{
+ mecanim::animation::AvatarConstant const* cst = GetAsset();
+
+ math::float4 qzyroll = math::quatIdentity();
+
+ int id = HumanTrait::GetBoneId(*this, index);
+
+ if( id != -1)
+ {
+ int axesId = cst->m_Human->m_Skeleton->m_Node[id].m_AxesId;
+ if(axesId != -1)
+ {
+ ATTRIBUTE_ALIGN(ALIGN4F) float buf[4] = {v.x, v.y, v.z, 0};
+
+ math::Axes const& axes = cst->m_Human->m_Skeleton->m_AxesArray[axesId];
+ math::float4 uvw = math::load(buf);
+ qzyroll = ZYRoll2Quat(halfTan(LimitUnproject(axes.m_Limit,uvw))*sgn(axes.m_Sgn));
+ }
+ }
+ return float4ToQuaternionf(qzyroll);
+}
+
+Vector3f Avatar::GetLimitSign(int index)const
+{
+ mecanim::animation::AvatarConstant const* cst = GetAsset();
+ int id = HumanTrait::GetBoneId(*this, index);
+
+ Vector3f sign = Vector3f::one;
+ if( id != -1)
+ {
+ int axesId = cst->m_Human->m_Skeleton->m_Node[id].m_AxesId;
+ if(axesId != -1)
+ {
+ sign.x = cst->m_Human->m_Skeleton->m_AxesArray[axesId].m_Sgn.x().tofloat();
+ sign.y = cst->m_Human->m_Skeleton->m_AxesArray[axesId].m_Sgn.y().tofloat();
+ sign.z = cst->m_Human->m_Skeleton->m_AxesArray[axesId].m_Sgn.z().tofloat();
+ }
+ }
+ return sign;
+}
+
+std::string HumanTrait::GetFingerMuscleName(int index, bool left)
+{
+ std::string fingerName = left ? "Left " : "Right ";
+ if(0 <= index && index < mecanim::hand::s_DoFCount)
+ {
+ int fingerIndex = index / mecanim::hand::kLastFingerDoF;
+ int dofIndex = index % mecanim::hand::kLastFingerDoF;
+
+ fingerName += mecanim::hand::FingerName(fingerIndex);
+ fingerName += " ";
+ fingerName += mecanim::hand::FingerDoFName(dofIndex);
+ }
+ return fingerName;
+}
+
+std::string HumanTrait::GetFingerName(int index, bool left)
+{
+ std::string fingerName = left ? "Left " : "Right ";
+ if(0 <= index && index < mecanim::hand::s_BoneCount)
+ {
+ int fingerIndex = index / mecanim::hand::kLastPhalange;
+ int phalangesIndex = index % mecanim::hand::kLastPhalange;
+
+ fingerName += mecanim::hand::FingerName(fingerIndex);
+ fingerName += " ";
+ fingerName += mecanim::hand::PhalangeName(phalangesIndex);
+ }
+ return fingerName;
+}
+
+int HumanTrait::Body::GetBoneCount()
+{
+ return mecanim::human::kLastBone;
+}
+
+std::string HumanTrait::Body::GetBoneName(int index)
+{
+ return std::string( mecanim::human::BoneName(index) );
+}
+
+int HumanTrait::Body::GetMuscleCount()
+{
+ return mecanim::human::kLastDoF;
+}
+
+std::string HumanTrait::Body::GetMuscleName(int index)
+{
+ return std::string(mecanim::human::MuscleName(index));
+}
+
+int HumanTrait::LeftFinger::GetBoneCount()
+{
+ return mecanim::hand::s_BoneCount;
+}
+
+std::string HumanTrait::LeftFinger::GetBoneName(int index)
+{
+ return HumanTrait::GetFingerName(index, IsLeftHand());
+}
+
+int HumanTrait::LeftFinger::GetMuscleCount()
+{
+ return mecanim::hand::s_DoFCount;
+}
+
+std::string HumanTrait::LeftFinger::GetMuscleName(int index)
+{
+ return HumanTrait::GetFingerMuscleName(index, IsLeftHand());
+}
+
+bool HumanTrait::LeftFinger::IsLeftHand()
+{
+ return true;
+}
+
+
+int HumanTrait::RightFinger::GetBoneCount()
+{
+ return mecanim::hand::s_BoneCount;
+}
+
+std::string HumanTrait::RightFinger::GetBoneName(int index)
+{
+ return HumanTrait::GetFingerName(index, IsLeftHand());
+}
+
+int HumanTrait::RightFinger::GetMuscleCount()
+{
+ return mecanim::hand::s_DoFCount;
+}
+
+std::string HumanTrait::RightFinger::GetMuscleName(int index)
+{
+ return HumanTrait::GetFingerMuscleName(index, IsLeftHand());
+}
+
+bool HumanTrait::RightFinger::IsLeftHand()
+{
+ return false;
+}
+
+std::vector<string> HumanTrait::GetMuscleName()
+{
+ static std::vector<string> muscles = InternalGetMuscleName();
+ return muscles;
+}
+
+std::vector<string> HumanTrait::GetBoneName()
+{
+ static std::vector<string> bones = InternalGetBoneName();
+ return bones;
+}
+
+int HumanTrait::MuscleFromBone(int i, int dofIndex)
+{
+ if(i < LastBone)
+ return mecanim::human::MuscleFromBone(i, dofIndex);
+ else if(i < LastLeftFingerBone)
+ {
+ int muscle = mecanim::hand::MuscleFromBone(i-LastBone, dofIndex);
+ return muscle != -1 ? LastDoF + muscle : -1;
+ }
+ else if(i < LastRightFingerBone)
+ {
+ int muscle = mecanim::hand::MuscleFromBone(i-LastLeftFingerBone, dofIndex);
+ return muscle != -1 ? LastLeftFingerDoF + muscle : -1;
+ }
+ return -1;
+}
+
+int HumanTrait::BoneFromMuscle(int i)
+{
+ if(i < LastDoF)
+ return mecanim::human::BoneFromMuscle(i);
+ else if(i < LastLeftFingerDoF)
+ {
+ int bone = mecanim::hand::BoneFromMuscle(i-LastDoF);
+ return bone != -1 ? LastBone + bone : -1;
+ }
+ else if(i < LastRightFingerDoF)
+ {
+ int bone = mecanim::hand::BoneFromMuscle(i-LastLeftFingerDoF);
+ return bone != -1 ? LastLeftFingerBone + bone : -1;
+ }
+ return -1;
+}
+
+int HumanTrait::GetBoneId(Avatar const& avatar, int humanId)
+{
+ mecanim::animation::AvatarConstant const* cst = avatar.GetAsset();
+
+ int index = -1;
+ if(humanId < LastBone && cst->isHuman())
+ index = cst->m_Human->m_HumanBoneIndex[humanId];
+ else if(humanId < LastLeftFingerBone && cst->isHuman() && !cst->m_Human->m_LeftHand.IsNull())
+ index = cst->m_Human->m_LeftHand->m_HandBoneIndex[humanId - LastBone];
+ else if(humanId < LastRightFingerBone && cst->isHuman() && !cst->m_Human->m_RightHand.IsNull())
+ index = cst->m_Human->m_RightHand->m_HandBoneIndex[humanId - LastLeftFingerBone];
+
+ return index;
+}
+
+bool HumanTrait::RequiredBone(int humanId)
+{
+ if(humanId < LastBone)
+ return mecanim::human::RequiredBone(humanId);
+ return false;
+}
+
+int HumanTrait::RequiredBoneCount()
+{
+ int count = 0;
+ for(int i=0;i<LastBone;i++)
+ {
+ count = RequiredBone(i) ? count + 1 : count;
+ }
+ return count;
+}
+
+bool HumanTrait::HasCollider(Avatar& avatar, int humanId)
+{
+ mecanim::animation::AvatarConstant* cst = avatar.GetAsset();
+
+ bool ret = false;
+ if(humanId < LastBone && cst->isHuman())
+ ret = cst->m_Human->m_ColliderIndex[humanId] != -1;
+ return ret;
+}
+
+int HumanTrait::GetColliderId(Avatar& avatar, int humanId)
+{
+ mecanim::animation::AvatarConstant* cst = avatar.GetAsset();
+
+ int ret = -1;
+ if(humanId < LastBone && cst->isHuman())
+ ret = cst->m_Human->m_ColliderIndex[humanId];
+ return ret;
+}
+
+int HumanTrait::GetParent(int humanId)
+{
+ #define FINGER_INDEX(finger, phalanges) (mecanim::hand::finger * mecanim::hand::kLastPhalange) + mecanim::hand::phalanges
+
+ const int LeftFingerStart = LastBone;
+ const int RightFingerStart = LastLeftFingerBone;
+
+ static int humanParent[] = {
+ -1,
+ mecanim::human::kHips,
+ mecanim::human::kHips,
+ mecanim::human::kLeftUpperLeg,
+ mecanim::human::kRightUpperLeg,
+ mecanim::human::kLeftLowerLeg,
+ mecanim::human::kRightLowerLeg,
+ mecanim::human::kHips,
+ mecanim::human::kSpine,
+ mecanim::human::kChest,
+ mecanim::human::kNeck,
+ mecanim::human::kChest,
+ mecanim::human::kChest,
+ mecanim::human::kLeftShoulder,
+ mecanim::human::kRightShoulder,
+ mecanim::human::kLeftUpperArm,
+ mecanim::human::kRightUpperArm,
+ mecanim::human::kLeftLowerArm,
+ mecanim::human::kRightLowerArm,
+ mecanim::human::kLeftFoot,
+ mecanim::human::kRightFoot,
+ mecanim::human::kHead,
+ mecanim::human::kHead,
+ mecanim::human::kHead,
+ mecanim::human::kLeftHand, LeftFingerStart + FINGER_INDEX(kThumb, kProximal), LeftFingerStart + FINGER_INDEX(kThumb, kIntermediate),
+ mecanim::human::kLeftHand, LeftFingerStart + FINGER_INDEX(kIndex, kProximal), LeftFingerStart + FINGER_INDEX(kIndex, kIntermediate),
+ mecanim::human::kLeftHand, LeftFingerStart + FINGER_INDEX(kMiddle, kProximal), LeftFingerStart + FINGER_INDEX(kMiddle, kIntermediate),
+ mecanim::human::kLeftHand, LeftFingerStart + FINGER_INDEX(kRing, kProximal), LeftFingerStart + FINGER_INDEX(kRing, kIntermediate),
+ mecanim::human::kLeftHand, LeftFingerStart + FINGER_INDEX(kLittle, kProximal), LeftFingerStart + FINGER_INDEX(kLittle, kIntermediate),
+ mecanim::human::kRightHand, RightFingerStart + FINGER_INDEX(kThumb, kProximal), RightFingerStart + FINGER_INDEX(kThumb, kIntermediate),
+ mecanim::human::kRightHand, RightFingerStart + FINGER_INDEX(kIndex, kProximal), RightFingerStart + FINGER_INDEX(kIndex, kIntermediate),
+ mecanim::human::kRightHand, RightFingerStart + FINGER_INDEX(kMiddle, kProximal), RightFingerStart + FINGER_INDEX(kMiddle, kIntermediate),
+ mecanim::human::kRightHand, RightFingerStart + FINGER_INDEX(kRing, kProximal), RightFingerStart + FINGER_INDEX(kRing, kIntermediate),
+ mecanim::human::kRightHand, RightFingerStart + FINGER_INDEX(kLittle, kProximal), RightFingerStart + FINGER_INDEX(kLittle, kIntermediate),
+ };
+
+ Assert(0 <= humanId && humanId < BoneCount);
+ return humanParent[humanId];
+}
+
+float HumanTrait::GetMuscleDefaultMin(int i)
+{
+ int bone = HumanTrait::BoneFromMuscle(i);
+ int dx = HumanTrait::MuscleFromBone (bone, 0);
+ int dy = HumanTrait::MuscleFromBone (bone, 1);
+ int dz = HumanTrait::MuscleFromBone (bone, 2);
+
+ if(i < LastDoF)
+ {
+ mecanim::skeleton::SetupAxesInfo const& axeInfo = mecanim::human::GetAxeInfo(bone);
+ if(i == dx) return axeInfo.m_Min[0];
+ else if(i == dy) return axeInfo.m_Min[1];
+ else if(i == dz) return axeInfo.m_Min[2];
+ }
+ else if(i < LastLeftFingerDoF)
+ {
+ mecanim::skeleton::SetupAxesInfo const& axeInfo = mecanim::hand::GetAxeInfo(bone-LastBone);
+ if(i == dx) return axeInfo.m_Min[0];
+ else if(i == dy) return axeInfo.m_Min[1];
+ else if(i == dz) return axeInfo.m_Min[2];
+ }
+ else if(i < LastRightFingerDoF)
+ {
+ mecanim::skeleton::SetupAxesInfo const& axeInfo = mecanim::hand::GetAxeInfo(bone-LastLeftFingerBone);
+ if(i == dx) return axeInfo.m_Min[0];
+ else if(i == dy) return axeInfo.m_Min[1];
+ else if(i == dz) return axeInfo.m_Min[2];
+ }
+ return 0.f;
+}
+
+float HumanTrait::GetMuscleDefaultMax(int i)
+{
+ int bone = HumanTrait::BoneFromMuscle(i);
+ int dx = HumanTrait::MuscleFromBone (bone, 0);
+ int dy = HumanTrait::MuscleFromBone (bone, 1);
+ int dz = HumanTrait::MuscleFromBone (bone, 2);
+
+ if(i < LastDoF)
+ {
+ mecanim::skeleton::SetupAxesInfo const& axeInfo = mecanim::human::GetAxeInfo(bone);
+ if(i == dx) return axeInfo.m_Max[0];
+ else if(i == dy) return axeInfo.m_Max[1];
+ else if(i == dz) return axeInfo.m_Max[2];
+ }
+ else if(i < LastLeftFingerDoF)
+ {
+ mecanim::skeleton::SetupAxesInfo const& axeInfo = mecanim::hand::GetAxeInfo(bone-LastBone);
+ if(i == dx) return axeInfo.m_Max[0];
+ else if(i == dy) return axeInfo.m_Max[1];
+ else if(i == dz) return axeInfo.m_Max[2];
+ }
+ else if(i < LastRightFingerDoF)
+ {
+ mecanim::skeleton::SetupAxesInfo const& axeInfo = mecanim::hand::GetAxeInfo(bone-LastLeftFingerBone);
+ if(i == dx) return axeInfo.m_Max[0];
+ else if(i == dy) return axeInfo.m_Max[1];
+ else if(i == dz) return axeInfo.m_Max[2];
+ }
+ return 0.f;
+}
+
+std::vector<string> HumanTrait::InternalGetMuscleName()
+{
+ std::vector<string> muscles;
+
+ muscles.reserve(MuscleCount);
+ for (int i = 0; i < MuscleCount; i++)
+ {
+ if(i < LastDoF)
+ muscles.push_back( Body::GetMuscleName(i) );
+ else if(i < LastLeftFingerDoF)
+ muscles.push_back( LeftFinger::GetMuscleName(i-LastDoF) );
+ else if(i < LastRightFingerDoF)
+ muscles.push_back( RightFinger::GetMuscleName(i-LastLeftFingerDoF) );
+ }
+ return muscles;
+}
+
+std::vector<string> HumanTrait::InternalGetBoneName()
+{
+ std::vector<string> bones;
+
+ bones.reserve(BoneCount);
+ for (int i = 0; i < BoneCount; i++)
+ {
+ if(i < LastBone)
+ bones.push_back( Body::GetBoneName(i) );
+ else if(i < LastLeftFingerBone)
+ bones.push_back( LeftFinger::GetBoneName(i-LastBone) );
+ else if(i < LastRightFingerBone)
+ bones.push_back( RightFinger::GetBoneName(i-LastLeftFingerBone) );
+ }
+
+ return bones;
+}
diff --git a/Runtime/Animation/Avatar.h b/Runtime/Animation/Avatar.h
new file mode 100644
index 0000000..d99dbd0
--- /dev/null
+++ b/Runtime/Animation/Avatar.h
@@ -0,0 +1,149 @@
+#ifndef AVATAR_H
+#define AVATAR_H
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/Misc/UserList.h"
+#include "Runtime/BaseClasses/MessageIdentifier.h"
+#include "Runtime/Serialize/SerializeTraits.h"
+#include "Runtime/mecanim/animation/avatar.h"
+
+#include "Runtime/Animation/MecanimUtility.h"
+
+namespace mecanim { namespace animation { struct AvatarConstant; } }
+
+enum HumanParameter
+{
+ UpperArmTwist = 0,
+ LowerArmTwist,
+ UpperLegTwist,
+ LowerLegTwsit,
+ ArmStretch,
+ LegStretch,
+ FeetSpacing
+};
+
+class Avatar : public NamedObject
+{
+public:
+ REGISTER_DERIVED_CLASS (Avatar, NamedObject)
+ DECLARE_OBJECT_SERIALIZE (Avatar)
+
+ static void InitializeClass (){};
+ static void CleanupClass () {}
+
+ Avatar (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+ virtual void CheckConsistency ();
+
+ void SetAsset (mecanim::animation::AvatarConstant* avatarConstant, TOSVector const& tos);
+
+ mecanim::animation::AvatarConstant* GetAsset();
+ const mecanim::animation::AvatarConstant* GetAsset() const;
+ TOSVector const& GetTOS() const;
+
+ bool IsValid()const;
+
+
+ void SetMuscleMinMax(int muscleId, float min, float max);
+ void SetParameter(int parameterId, float value);
+
+ void NotifyObjectUsers(const MessageIdentifier& msg);
+ void AddObjectUser( UserListNode& node ) { m_ObjectUsers.AddUser(node); }
+
+ bool IsHuman() const;
+ bool HasRootMotion() const;
+ float GetHumanScale() const;
+ float GetLeftFeetBottomHeight() const;
+ float GetRightFeetBottomHeight() const;
+
+ float GetAxisLength(int humanId)const;
+ Quaternionf GetPreRotation(int humanId)const;
+ Quaternionf GetPostRotation(int humanId)const;
+ Quaternionf GetZYPostQ(int index, Quaternionf const& parentQ, Quaternionf const& q)const;
+ Quaternionf GetZYRoll(int index, Vector3f const& v)const;
+ Vector3f GetLimitSign(int index)const;
+
+protected:
+
+ mecanim::memory::ChainedAllocator m_Allocator;
+ mecanim::animation::AvatarConstant* m_Avatar;
+ TOSVector m_TOS;
+
+ UInt32 m_AvatarSize;
+
+ UserList m_ObjectUsers;
+};
+
+class HumanTrait
+{
+public:
+ enum {
+ LastDoF = mecanim::human::kLastDoF,
+ LastLeftFingerDoF = LastDoF + mecanim::hand::s_DoFCount,
+ LastRightFingerDoF = LastLeftFingerDoF + mecanim::hand::s_DoFCount,
+ MuscleCount = LastRightFingerDoF
+ };
+
+ enum {
+ LastBone = mecanim::human::kLastBone,
+ LastLeftFingerBone = LastBone + mecanim::hand::s_BoneCount,
+ LastRightFingerBone = LastLeftFingerBone + mecanim::hand::s_BoneCount,
+ BoneCount = LastRightFingerBone
+ };
+
+ static std::string GetFingerMuscleName(int index, bool left);
+ static std::string GetFingerName(int index, bool left);
+
+ class Body
+ {
+ public:
+ static int GetBoneCount();
+ static std::string GetBoneName(int index);
+ static int GetMuscleCount();
+ static std::string GetMuscleName(int index);
+ };
+
+ class LeftFinger
+ {
+ public:
+ static int GetBoneCount();
+ static std::string GetBoneName(int index);
+ static int GetMuscleCount();
+ static std::string GetMuscleName(int index);
+ static bool IsLeftHand();
+ };
+
+ class RightFinger
+ {
+ public:
+ static int GetBoneCount();
+ static std::string GetBoneName(int index);
+ static int GetMuscleCount();
+ static std::string GetMuscleName(int index);
+ static bool IsLeftHand();
+ };
+
+ static std::vector<string> GetMuscleName();
+ static std::vector<string> GetBoneName();
+ static int MuscleFromBone(int i, int dofIndex);
+ static int BoneFromMuscle(int i);
+ static int GetBoneId(Avatar const& avatar, int humanId);
+ static bool RequiredBone(int humanId);
+ static int RequiredBoneCount();
+ static bool HasCollider(Avatar& avatar, int humanId);
+ static int GetColliderId(Avatar& avatar, int humanId);
+
+ static int GetParent(int humanId);
+
+ static float GetMuscleDefaultMin(int i);
+ static float GetMuscleDefaultMax(int i);
+
+protected:
+ static std::vector<string> InternalGetMuscleName();
+ static std::vector<string> InternalGetBoneName();
+};
+
+
+#endif
+
diff --git a/Runtime/Animation/AvatarBuilder.cpp b/Runtime/Animation/AvatarBuilder.cpp
new file mode 100644
index 0000000..abfbfac
--- /dev/null
+++ b/Runtime/Animation/AvatarBuilder.cpp
@@ -0,0 +1,807 @@
+#include "UnityPrefix.h"
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Filters/Deformation/SkinnedMeshFilter.h"
+
+#include "Runtime/Profiler/Profiler.h"
+
+#include "AvatarBuilder.h"
+#include "Avatar.h"
+
+
+
+namespace
+{
+ int Find( AvatarBuilder::NamedTransforms const& transforms, Transform* parent )
+ {
+ for(int i=0;i<transforms.size();i++)
+ if(transforms[i].transform == parent) return i;
+ return -1;
+ }
+
+ template<typename TYPE>
+ int GetIndexArray(HumanDescription const& humanDescription, AvatarBuilder::NamedTransforms const& namedTransform, std::vector<int> &indexArray)
+ {
+ int ret = 0;
+
+ for(int i = 0 ; i < TYPE::GetBoneCount(); ++i)
+ {
+ HumanBoneList::const_iterator it = std::find_if(humanDescription.m_Human.begin(), humanDescription.m_Human.end(), FindHumanBone(TYPE::GetBoneName(i)) );
+
+ if(it != humanDescription.m_Human.end())
+ {
+ for(int j = 0; j < namedTransform.size(); j++)
+ {
+ if((*it).m_BoneName == namedTransform[j].name)
+ {
+ indexArray[j] = i;
+ ret++;
+ }
+ }
+ }
+ }
+
+ return ret;
+ }
+
+
+ mecanim::skeleton::Skeleton* BuildSkeleton(const AvatarBuilder::NamedTransforms & transform, TOSVector& tos, mecanim::memory::Allocator& alloc)
+ {
+ mecanim::skeleton::Skeleton * skel = mecanim::skeleton::CreateSkeleton(transform.size(), 0, alloc);
+
+ mecanim::uint32_t i;
+ for(i=0;i<skel->m_Count;i++)
+ {
+ // Find return -1 if transform is not found exactly like our data representation
+ // when node doesn't have any parent
+ skel->m_Node[i].m_ParentId = Find( transform, transform[i].transform->GetParent() );
+ skel->m_Node[i].m_AxesId = -1;
+ skel->m_ID[i] = ProccessString(tos, transform[i].path);
+ }
+ return skel;
+ }
+
+ void MarkBoneUp(mecanim::skeleton::Skeleton *avatarSkeleton, std::vector<bool> &isMark, int index, int stopIndex)
+ {
+ isMark[index] = true;
+
+ if(index != stopIndex)
+ {
+ MarkBoneUp(avatarSkeleton,isMark,avatarSkeleton->m_Node[index].m_ParentId,stopIndex);
+ }
+ }
+
+ mecanim::skeleton::Skeleton* BuildHumanSkeleton(mecanim::skeleton::Skeleton *avatarSkeleton, std::vector<int> humanBoneIndexArray, std::vector<int> leftHandIndexArray, std::vector<int> rightHandIndexArray, mecanim::memory::Allocator& alloc)
+ {
+ int humanCount = 0;
+ int leftHandCount = 0;
+ int rightHandCount = 0;
+ int hipsIndex = -1;
+
+ for(int i = 0; i < avatarSkeleton->m_Count; i++)
+ {
+ humanCount += humanBoneIndexArray[i] != -1 ? 1 : 0;
+ leftHandCount += leftHandIndexArray[i] != -1 ? 1 : 0;
+ rightHandCount += rightHandIndexArray[i] != -1 ? 1 : 0;
+ hipsIndex = humanBoneIndexArray[i] == mecanim::human::kHips ? i : hipsIndex;
+ }
+
+ std::vector<bool> isHumanBone(avatarSkeleton->m_Count,false);
+
+ for(int i = 0; i < avatarSkeleton->m_Count; i++)
+ {
+ if(humanBoneIndexArray[i] != -1)
+ {
+ MarkBoneUp(avatarSkeleton,isHumanBone,i,hipsIndex);
+ }
+
+ if(leftHandIndexArray[i] != -1)
+ {
+ MarkBoneUp(avatarSkeleton,isHumanBone,i,hipsIndex);
+ }
+
+ if(rightHandIndexArray[i] != -1)
+ {
+ MarkBoneUp(avatarSkeleton,isHumanBone,i,hipsIndex);
+ }
+ }
+
+ int boneCount = 0;
+
+ for(int i = 0; i < isHumanBone.size(); i++)
+ {
+ boneCount += isHumanBone[i] ? 1 : 0;
+ }
+
+ mecanim::skeleton::Skeleton *skel = mecanim::skeleton::CreateSkeleton(1+boneCount,humanCount+leftHandCount+rightHandCount,alloc);
+
+ int nodeIndex = 0;
+ int axesIndex = 0;
+
+ skel->m_ID[nodeIndex] = avatarSkeleton->m_ID[avatarSkeleton->m_Node[hipsIndex].m_ParentId];
+ skel->m_Node[nodeIndex].m_ParentId = -1;
+ skel->m_Node[nodeIndex].m_AxesId = -1;
+
+ nodeIndex++;
+
+ for(int i = 0; i < avatarSkeleton->m_Count; i++)
+ {
+ if(isHumanBone[i])
+ {
+ skel->m_ID[nodeIndex] = avatarSkeleton->m_ID[i];
+ skel->m_Node[nodeIndex].m_ParentId = mecanim::skeleton::SkeletonFindNode(skel,avatarSkeleton->m_ID[avatarSkeleton->m_Node[i].m_ParentId]);
+ skel->m_Node[nodeIndex].m_AxesId = humanBoneIndexArray[i] != -1 || leftHandIndexArray[i] != -1 || rightHandIndexArray[i] != -1 ? axesIndex++ : -1;
+
+ nodeIndex++;
+ }
+ }
+
+ return skel;
+ }
+
+ mecanim::skeleton::Skeleton* BuildRootMotionSkeleton(mecanim::skeleton::Skeleton *avatarSkeleton, int rootMotionIndex, mecanim::memory::Allocator& alloc)
+ {
+
+ std::vector<bool> isRootMotionBone(avatarSkeleton->m_Count,false);
+
+ MarkBoneUp(avatarSkeleton,isRootMotionBone,rootMotionIndex,0);
+
+ int rootMotionCount = 0;
+
+ for(int i = 0; i < isRootMotionBone.size(); i++)
+ {
+ rootMotionCount += isRootMotionBone[i] ? 1 : 0;
+ }
+
+ mecanim::skeleton::Skeleton *skel = mecanim::skeleton::CreateSkeleton(rootMotionCount,0,alloc);
+
+ int nodeIndex = 0;
+
+ for(int i = 0; i < avatarSkeleton->m_Count; i++)
+ {
+ if(isRootMotionBone[i])
+ {
+ skel->m_ID[nodeIndex] = avatarSkeleton->m_ID[i];
+ skel->m_Node[nodeIndex].m_ParentId = nodeIndex-1;
+
+ nodeIndex++;
+ }
+ }
+
+ return skel;
+ }
+
+ void SetAxes(mecanim::human::Human* human, SkeletonBoneLimit const& skeletonBoneLimit, int id)
+ {
+ if(id != -1 && skeletonBoneLimit.m_Modified)
+ {
+ int axesId = human->m_Skeleton->m_Node[id].m_AxesId;
+ if(axesId != -1)
+ {
+ math::Axes& axes = human->m_Skeleton->m_AxesArray[axesId];
+
+ math::float4 minValue = math::radians(math::float4(skeletonBoneLimit.m_Min[0], skeletonBoneLimit.m_Min[1], skeletonBoneLimit.m_Min[2], 0.f));
+ math::float4 maxValue = math::radians(math::float4(skeletonBoneLimit.m_Max[0], skeletonBoneLimit.m_Max[1], skeletonBoneLimit.m_Max[2], 0.f));
+ axes.m_Limit.m_Min = minValue;
+ axes.m_Limit.m_Max = maxValue;
+ }
+ }
+ }
+
+ class SetupAxesHelper
+ {
+ public:
+ SetupAxesHelper(mecanim::human::Human* human, bool exist, mecanim::int32_t* index): mHuman(human),mExist(exist),mIndex(index){}
+
+ void operator()(HumanBone const& humanBone, int i)
+ {
+ int index = mExist ? mIndex[i] : -1;
+ SetAxes(mHuman, humanBone.m_Limit, index);
+ }
+ protected:
+ mecanim::human::Human* mHuman;
+ mecanim::skeleton::SkeletonPose* mPose;
+ bool mExist;
+ mecanim::int32_t* mIndex;
+ };
+
+ template<class Trait, class List, class Function> void for_each(List const& list, Function f)
+ {
+ for (int i = 0; i < Trait::GetBoneCount(); ++i )
+ {
+ typename List::const_iterator it = std::find_if(list.begin(), list.end(), FindHumanBone(Trait::GetBoneName(i)) );
+ if(it != list.end())
+ {
+ f(*it, i);
+ }
+ }
+ }
+
+ void SetupAxes(mecanim::human::Human* human, HumanDescription const& humanDescription)
+ {
+ for_each<HumanTrait::Body>(humanDescription.m_Human, SetupAxesHelper(human, true, human->m_HumanBoneIndex));
+
+ for_each<HumanTrait::LeftFinger>(humanDescription.m_Human, SetupAxesHelper(human, human->m_HasLeftHand, human->m_HasLeftHand ? human->m_LeftHand->m_HandBoneIndex : 0));
+
+ for_each<HumanTrait::RightFinger>(humanDescription.m_Human, SetupAxesHelper(human, human->m_HasRightHand, human->m_HasRightHand ? human->m_RightHand->m_HandBoneIndex : 0));
+ }
+
+ struct IndexFromBoneName
+ {
+ UnityStr m_Predicate;
+ IndexFromBoneName(const UnityStr& predicate):m_Predicate(predicate){}
+
+ bool operator()(AvatarBuilder::NamedTransform const& namedTransform){return namedTransform.name == m_Predicate;}
+ };
+
+ static int GetIndexFromBoneName (AvatarBuilder::NamedTransforms::const_iterator start, AvatarBuilder::NamedTransforms::const_iterator stop, const UnityStr& boneName)
+ {
+ AvatarBuilder::NamedTransforms::const_iterator it = std::find_if(start, stop, IndexFromBoneName(boneName) );
+ if(it!=stop)
+ return it - start;
+
+ return -1;
+ }
+
+ static void OverwriteTransforms(mecanim::skeleton::Skeleton* skeleton, mecanim::skeleton::SkeletonPose* pose, const HumanDescription& humanDescription, const AvatarBuilder::NamedTransforms& namedTransforms, bool force)
+ {
+ if( humanDescription.m_Skeleton.size() > 0)
+ {
+ const SkeletonBone& skeletonBone = humanDescription.m_Skeleton[0];
+ if(skeletonBone.m_TransformModified || force)
+ {
+ if(namedTransforms[0].name == skeletonBone.m_Name)
+ {
+ pose->m_X[0] = xformFromUnity(skeletonBone.m_Position,skeletonBone.m_Rotation,skeletonBone.m_Scale);
+ }
+ }
+
+ for (int i = 1; i < humanDescription.m_Skeleton.size(); ++i )
+ {
+ SkeletonBone const& skeletonBone = humanDescription.m_Skeleton[i];
+ if(skeletonBone.m_TransformModified || force)
+ {
+ int skeletonIndex = GetIndexFromBoneName (namedTransforms.begin()+1, namedTransforms.end(), skeletonBone.m_Name) + 1;
+ if (skeletonIndex != 0)
+ {
+ pose->m_X[skeletonIndex] = xformFromUnity(skeletonBone.m_Position,skeletonBone.m_Rotation,skeletonBone.m_Scale);
+ }
+ }
+ }
+ }
+ }
+}
+
+HumanBone::HumanBone():
+m_HumanName(""),
+m_BoneName("")
+//m_ColliderPosition(Vector3f::zero),
+//m_ColliderRotation(Quaternionf::identity()),
+//m_ColliderScale(Vector3f::one)
+{
+}
+
+HumanBone::HumanBone(std::string const& humanName):
+m_HumanName(humanName),
+m_BoneName("")
+//m_ColliderPosition(Vector3f::zero),
+//m_ColliderRotation(Quaternionf::identity()),
+//m_ColliderScale(Vector3f::one)
+
+{
+}
+
+class FindBone
+{
+protected:
+ UnityStr mName;
+public:
+ FindBone(const UnityStr& name):mName(name){}
+ bool operator() (const AvatarBuilder::NamedTransform & bone){ return mName == bone.name; }
+};
+
+class FindBonePath
+{
+protected:
+ UnityStr mName;
+public:
+ FindBonePath(const UnityStr& name):mName(name){}
+
+ bool operator() (const AvatarBuilder::NamedTransform & bone){ return mName == bone.path; }
+};
+
+PROFILER_INFORMATION (gAvatarBuilderBuildAvatar, "AvatarBuilder.BuildAvatar", kProfilerAnimation);
+
+std::string AvatarBuilder::BuildAvatar(Avatar& avatar, const Unity::GameObject& go, bool doOptimizeTransformHierarchy, const HumanDescription& humanDescription, Options options)
+{
+ PROFILER_AUTO(gAvatarBuilderBuildAvatar, NULL)
+
+ NamedTransforms namedTransform;
+
+ std::string error = AvatarBuilder::GenerateAvatarMap(go, namedTransform, humanDescription, doOptimizeTransformHierarchy, options.avatarType, options.useMask);
+ if(!error.empty())
+ return Format("AvatarBuilder '%s': %s", go.GetName(), error.c_str());
+
+ mecanim::memory::ChainedAllocator alloc(30*1024);
+
+ TOSVector tos;
+
+ // build avatar skeleton
+ mecanim::skeleton::Skeleton* avatarSK = BuildSkeleton(namedTransform, tos, alloc);
+ mecanim::skeleton::SkeletonPose* avatarPose = mecanim::skeleton::CreateSkeletonPose(avatarSK, alloc);
+ mecanim::skeleton::SkeletonPose* avatarGPose = mecanim::skeleton::CreateSkeletonPose(avatarSK, alloc);
+ mecanim::uint32_t * nameIDArray = alloc.ConstructArray<mecanim::uint32_t>(namedTransform.size());
+
+ for(int i = 0; i < namedTransform.size(); i++)
+ {
+ nameIDArray[i] = mecanim::processCRC32(GetLastPathNameComponent(namedTransform[i].path.c_str(), namedTransform[i].path.size()));
+ }
+
+ if(options.readTransform)
+ ReadFromLocalTransformToSkeletonPose(avatarPose, namedTransform);
+
+ // Overwrite transform that has been set by the user
+ OverwriteTransforms(avatarSK, avatarPose, humanDescription, namedTransform, true);
+
+ mecanim::skeleton::SkeletonPoseComputeGlobal(avatarSK, avatarPose, avatarGPose);
+
+ // Fill avatarDefaultPose. This pose will be used in optimized mode, when the character is not animated.
+ // Please note that, although this value is only used in optimized mode, we initialize it here all the time.
+ // Because, optimization/de-optimization should can be applied on the fly.
+ // We don't want to trigger re-import (rebuild avatar).
+ mecanim::skeleton::SkeletonPose* avatarDefaultPose = mecanim::skeleton::CreateSkeletonPose(avatarSK, alloc);
+ ReadFromLocalTransformToSkeletonPose(avatarDefaultPose, namedTransform);
+
+ mecanim::human::Human* human = 0;
+ mecanim::skeleton::Skeleton* humanSK = 0;
+ mecanim::skeleton::SkeletonPose* humanPose = 0;
+ mecanim::skeleton::SkeletonPose* humanGPose = 0;
+
+ int rootMotionIndex = -1;
+ math::xform rootMotionX;
+ mecanim::skeleton::Skeleton *rootMotionSK = 0;
+
+ if(options.avatarType == kHumanoid)
+ {
+ // build human skeleton
+ std::vector<int> humanIndexArray(avatarSK->m_Count,-1);
+ std::vector<int> leftHandIndexArray(avatarSK->m_Count,-1);
+ std::vector<int> rightHandIndexArray(avatarSK->m_Count,-1);
+
+ GetIndexArray<HumanTrait::Body>(humanDescription,namedTransform,humanIndexArray);
+ bool leftHandValid = GetIndexArray<HumanTrait::LeftFinger>(humanDescription,namedTransform,leftHandIndexArray) > 0;
+ bool rightHandValid = GetIndexArray<HumanTrait::RightFinger>(humanDescription,namedTransform,rightHandIndexArray) > 0;
+
+ humanSK = BuildHumanSkeleton(avatarSK, humanIndexArray, leftHandIndexArray, rightHandIndexArray,alloc);
+ humanPose = mecanim::skeleton::CreateSkeletonPose(humanSK, alloc);
+ humanGPose = mecanim::skeleton::CreateSkeletonPose(humanSK, alloc);
+
+ // build human. Setup human with 'pose', which will be initialized when HumanSetupAxes will be call
+
+ //@TODO: SHould we remove support for handles in the runtime too?
+ human = mecanim::human::CreateHuman(humanSK, humanPose, 0, humanSK->m_AxesCount, alloc);
+ mecanim::hand::Hand* leftHand = leftHandValid ? mecanim::hand::CreateHand(alloc) : 0;
+ mecanim::hand::Hand* rightHand = rightHandValid ? mecanim::hand::CreateHand(alloc) : 0;
+ human->m_LeftHand = leftHand;
+ human->m_HasLeftHand = leftHandValid;
+ human->m_RightHand = rightHand;
+ human->m_HasRightHand = rightHandValid;
+
+ for(int i = 0; i < avatarSK->m_Count; i++)
+ {
+ if(humanIndexArray[i] != -1)
+ human->m_HumanBoneIndex[humanIndexArray[i]] = mecanim::skeleton::SkeletonFindNode(humanSK,avatarSK->m_ID[i]);
+
+ if(leftHandValid && leftHandIndexArray[i] != -1)
+ leftHand->m_HandBoneIndex[leftHandIndexArray[i]] = mecanim::skeleton::SkeletonFindNode(humanSK,avatarSK->m_ID[i]);
+
+ if(rightHandValid && rightHandIndexArray[i] != -1)
+ rightHand->m_HandBoneIndex[rightHandIndexArray[i]] = mecanim::skeleton::SkeletonFindNode(humanSK,avatarSK->m_ID[i]);
+ }
+
+ human->m_ArmTwist = humanDescription.m_ArmTwist;
+ human->m_ForeArmTwist = humanDescription.m_ForeArmTwist;
+ human->m_UpperLegTwist = humanDescription.m_UpperLegTwist;
+ human->m_LegTwist = humanDescription.m_LegTwist;
+ human->m_ArmStretch = humanDescription.m_ArmStretch;
+ human->m_LegStretch = humanDescription.m_LegStretch;
+ human->m_FeetSpacing = humanDescription.m_FeetSpacing;
+
+ mecanim::skeleton::SkeletonPoseCopy(avatarSK,avatarGPose,humanSK,humanGPose);
+ humanGPose->m_X[0] = math::xformIdentity();
+
+ mecanim::human::HumanAdjustMass(human);
+ mecanim::human::HumanSetupAxes(human, humanGPose);
+ mecanim::human::HumanSetupCollider(human, humanGPose);
+
+ if(leftHandValid) mecanim::hand::HandSetupAxes(leftHand, humanGPose, humanSK, true);
+ if(rightHandValid) mecanim::hand::HandSetupAxes(rightHand, humanGPose, humanSK, false);
+
+ SetupAxes(human, humanDescription);
+ }
+ else
+ {
+ rootMotionIndex = GetIndexFromBoneName (namedTransform.begin(), namedTransform.end(), humanDescription.m_RootMotionBoneName);
+
+ if(rootMotionIndex != -1)
+ {
+ rootMotionX = avatarGPose->m_X[rootMotionIndex];
+ rootMotionSK = BuildRootMotionSkeleton(avatarSK,rootMotionIndex,alloc);
+ }
+ }
+
+ mecanim::animation::AvatarConstant* avatarConstant = mecanim::animation::CreateAvatarConstant( avatarSK, avatarPose, avatarDefaultPose, human, rootMotionSK, rootMotionIndex, rootMotionX, alloc);
+ avatarConstant->m_SkeletonNameIDCount = namedTransform.size();
+ avatarConstant->m_SkeletonNameIDArray = nameIDArray;
+ avatar.SetAsset(avatarConstant, tos);
+ return std::string();
+}
+
+Transform* AvatarBuilder::GetTransform(int id, HumanDescription const& humanDescription, NamedTransforms const& namedTransform, std::vector<string> const& boneName)
+{
+ HumanBoneList::const_iterator it1 = std::find_if(humanDescription.m_Human.begin(), humanDescription.m_Human.end(), FindHumanBone(boneName[id]) );
+ if(it1!=humanDescription.m_Human.end())
+ {
+ NamedTransforms::const_iterator it2 = std::find_if(namedTransform.begin(), namedTransform.end(), FindBone( (*it1).m_BoneName ) );
+ if(it2 != namedTransform.end())
+ {
+ return it2->transform;
+ }
+ }
+ return 0;
+}
+
+bool AvatarBuilder::IsValidHuman(HumanDescription const& humanDescription, NamedTransforms const& namedTransform, std::string& error)
+{
+ int i;
+ for(i = 0 ; i < HumanTrait::Body::GetBoneCount(); ++i)
+ {
+ if(HumanTrait::RequiredBone(i))
+ {
+ HumanBoneList::const_iterator it1 = std::find_if(humanDescription.m_Human.begin(), humanDescription.m_Human.end(), FindHumanBone(HumanTrait::Body::GetBoneName(i)) );
+ if(it1!=humanDescription.m_Human.end())
+ {
+ NamedTransforms::const_iterator it2 = std::find_if(namedTransform.begin(), namedTransform.end(), FindBone( (*it1).m_BoneName ) );
+ if(it2 == namedTransform.end())
+ {
+ error = Format("Transform '%s' for human bone '%s' not found", (*it1).m_BoneName.c_str(), HumanTrait::Body::GetBoneName(i).c_str() );
+ return false;
+ }
+ }
+ else
+ {
+ error = Format("Required human bone '%s' not found", HumanTrait::Body::GetBoneName(i).c_str() );
+ return false;
+ }
+ }
+ }
+
+ // Look if all the bone hierarchy parenting is valid
+ std::vector<string> boneName = HumanTrait::GetBoneName();
+ Transform* hips = GetTransform(0, humanDescription, namedTransform, boneName);
+ if(hips && !hips->GetParent())
+ {
+ error = Format("Hips bone '%s' must have a parent", hips->GetName() );
+ return false;
+ }
+ else if(hips && hips->GetParent())
+ {
+ if(std::find_if(namedTransform.begin(), namedTransform.end(), FindBone( hips->GetParent()->GetName() )) == namedTransform.end())
+ {
+ error = Format("Hips bone parent '%s' must be included in the HumanDescription Skeleton", hips->GetParent()->GetName());
+ return false;
+ }
+ }
+
+ for(i = 0 ; i < HumanTrait::BoneCount; ++i)
+ {
+ Transform* child = GetTransform(i, humanDescription, namedTransform, boneName);
+ if(child)
+ {
+ // find out next required parent bone
+ int parentId = HumanTrait::GetParent(i);
+ while(parentId != -1 && !HumanTrait::RequiredBone(parentId))
+ parentId = HumanTrait::GetParent(parentId);
+
+ if(parentId != -1)
+ {
+ Transform* parent = GetTransform(parentId, humanDescription, namedTransform, boneName);
+ if(!IsChildOrSameTransform(*child, *parent))
+ {
+ error = Format("Transform '%s' is not an ancestor of '%s'", parent->GetName(), child->GetName() );
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool AvatarBuilder::IsValidHumanDescription(HumanDescription const& humanDescription, std::string& error)
+{
+ int i ;
+ for(i = 0 ; i < HumanTrait::Body::GetBoneCount(); ++i)
+ {
+ if(HumanTrait::RequiredBone(i))
+ {
+ HumanBoneList::const_iterator it1 = std::find_if(humanDescription.m_Human.begin(), humanDescription.m_Human.end(), FindHumanBone(HumanTrait::Body::GetBoneName(i)) );
+ if(it1 == humanDescription.m_Human.end())
+ {
+ error = Format("Required human bone '%s' not found", HumanTrait::Body::GetBoneName(i).c_str() );
+ return false;
+ }
+ }
+ }
+
+ for(i = 0 ; i < humanDescription.m_Human.size() ; i++)
+ {
+ if(!humanDescription.m_Human[i].m_BoneName.empty())
+ {
+ HumanBoneList::const_iterator foundDuplicated = std::find_if(humanDescription.m_Human.begin() + i +1, humanDescription.m_Human.end(), FindHumanBone(humanDescription.m_Human[i].m_HumanName) );
+ if(foundDuplicated != humanDescription.m_Human.end())
+ {
+ error = Format("Found duplicate human bone '%s' with transform '%s' and '%s'", humanDescription.m_Human[i].m_HumanName.c_str(), foundDuplicated->m_BoneName.c_str(), humanDescription.m_Human[i].m_BoneName.c_str() );
+ return false;
+ }
+ }
+ }
+
+
+ for(i = 0 ; i < humanDescription.m_Human.size() ; i++)
+ {
+ if(!humanDescription.m_Human[i].m_BoneName.empty())
+ {
+ HumanBoneList::const_iterator foundDuplicated = std::find_if(humanDescription.m_Human.begin() + i +1, humanDescription.m_Human.end(), FindBoneName(humanDescription.m_Human[i].m_BoneName) );
+ if(foundDuplicated != humanDescription.m_Human.end())
+ {
+ error = Format("Found duplicate transform '%s' for human bone '%s' and '%s'", humanDescription.m_Human[i].m_BoneName.c_str(), foundDuplicated->m_HumanName.c_str(), humanDescription.m_Human[i].m_HumanName.c_str() );
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+std::string AvatarBuilder::GenerateAvatarMap(GameObject const& go, NamedTransforms& namedTransform, const HumanDescription& humanDescription, bool doOptimizeTransformHierarchy, AvatarType avatarType, bool useMask)
+{
+ Assert(avatarType == kHumanoid || avatarType == kGeneric);
+
+ std::string error;
+ if (avatarType == kHumanoid)
+ {
+ if (!AvatarBuilder::IsValidHumanDescription(humanDescription, error) )
+ return error;
+ }
+
+ Transform& rootTransform = go.GetComponent(Transform);
+
+ // Get all the transform below root transform
+ NamedTransforms namedAllTransform;
+ GetAllChildren(rootTransform, namedAllTransform);
+
+ Transform* root = &rootTransform;
+ if(avatarType == kHumanoid)
+ {
+ root = GetHipsNode(humanDescription, namedAllTransform);
+ if(root == 0)
+ {
+ HumanBoneList::const_iterator it = std::find_if(humanDescription.m_Human.begin(), humanDescription.m_Human.end(), FindHumanBone(HumanTrait::Body::GetBoneName(mecanim::human::kHips)) );
+
+ return Format("Transform '%s' for human bone '%s' not found", it->m_BoneName.c_str(), HumanTrait::Body::GetBoneName(mecanim::human::kHips).c_str() );
+ }
+ }
+ else if(avatarType == kGeneric && !humanDescription.m_RootMotionBoneName.empty())
+ {
+ root = GetRootMotionNode(humanDescription, namedAllTransform);
+ if(root == 0)
+ return Format("Cannot find root motion transform '%s'", humanDescription.m_RootMotionBoneName.c_str() );
+ }
+
+
+ // Mask is mainly used for API call, when user want to select only a sub part of a hierarchy
+ std::vector<UnityStr> mask;
+ if(useMask)
+ {
+ SkeletonBoneList::const_iterator it;
+ for(it = humanDescription.m_Skeleton.begin(); it != humanDescription.m_Skeleton.end(); ++it)
+ mask.push_back(UnityStr(it->m_Name.c_str()));
+ }
+
+ GetAllChildren(rootTransform, namedTransform, mask);
+
+ if(avatarType == kHumanoid)
+ {
+ if (!AvatarBuilder::IsValidHuman(humanDescription, namedTransform, error))
+ return error;
+ }
+
+ return std::string();
+}
+
+template <class InputIterator1> bool Include(InputIterator1 first1, InputIterator1 last1, UnityStr const& e)
+{
+ while(first1!=last1)
+ {
+ if( (*first1) == e) return true;
+ first1++;
+ }
+ return false;
+}
+
+void AvatarBuilder::GetAllChildren (Transform& node, NamedTransforms& transforms, std::vector<UnityStr> const& mask)
+{
+ UnityStr path = CalculateTransformPath (node, &node.GetRoot());
+ GetAllChildren (node, path, transforms, mask);
+}
+
+void AvatarBuilder::GetAllChildren (Transform& node, UnityStr& path, NamedTransforms& transforms, std::vector<UnityStr> const& mask)
+{
+ // @TODO: Does it make sense that you can exclude a node but it's child can still be included?
+ // That seems like it can only break stuff...
+ bool isIncluded = mask.size() == 0 || Include(mask.begin(), mask.end(), UnityStr(node.GetName()));
+ if(isIncluded)
+ {
+ transforms.push_back(NamedTransform());
+ transforms.back().transform = &node;
+ transforms.back().path = path;
+ transforms.back().name = node.GetName();
+ }
+
+ for (int i=0;i<node.GetChildrenCount();i++)
+ {
+ Transform& child = node.GetChild(i);
+ size_t pathLength = path.size();
+ AppendTransformPath (path, child.GetName());
+
+ GetAllChildren(child, path, transforms, mask);
+
+ path.resize(pathLength);
+ }
+}
+
+void AvatarBuilder::GetAllParent (Transform& node, NamedTransforms& transforms, std::vector<UnityStr> const& mask, bool includeSelf)
+{
+ GetAllParent (node.GetRoot(), node, transforms, mask, includeSelf);
+}
+
+void AvatarBuilder::GetAllParent (Transform& root, Transform& node, NamedTransforms& transforms, std::vector<UnityStr> const& mask, bool includeSelf)
+{
+ if(node.GetParent() != NULL)
+ {
+ Transform& parent = *node.GetParent();
+
+ // Insertion order matter, top most node must be inserted first
+ GetAllParent(root, parent, transforms, mask);
+
+ bool isIncluded = mask.size() == 0 || Include(mask.begin(), mask.end(), UnityStr(parent.GetName()));
+ if(isIncluded)
+ {
+ transforms.push_back(NamedTransform());
+ transforms.back().transform = &parent;
+ transforms.back().path = CalculateTransformPath(parent, &root);
+ transforms.back().name = parent.GetName();
+ }
+ }
+
+ if(includeSelf)
+ {
+ bool isIncluded = mask.size() == 0 || Include(mask.begin(), mask.end(), UnityStr(node.GetName()));
+ if(isIncluded)
+ {
+ transforms.push_back(NamedTransform());
+ transforms.back().transform = &node;
+ transforms.back().path = CalculateTransformPath(node, &root);
+ transforms.back().name = node.GetName();
+ }
+ }
+}
+
+Transform* AvatarBuilder::GetHipsNode(const HumanDescription& humanDescription, NamedTransforms const& transforms)
+{
+ HumanBoneList::const_iterator it = std::find_if(humanDescription.m_Human.begin(), humanDescription.m_Human.end(), FindHumanBone(HumanTrait::Body::GetBoneName(mecanim::human::kHips)) );
+ if(it != humanDescription.m_Human.end())
+ {
+ NamedTransforms::const_iterator it2 = std::find_if(transforms.begin(), transforms.end(), FindBone( it->m_BoneName ) );
+ if(it2 != transforms.end())
+ return it2->transform;
+ }
+ return 0;
+}
+
+Transform* AvatarBuilder::GetRootMotionNode(const HumanDescription& humanDescription, NamedTransforms const& transforms)
+{
+ NamedTransforms::const_iterator it = std::find_if(transforms.begin(), transforms.end(), FindBone( humanDescription.m_RootMotionBoneName ) );
+ return it != transforms.end() ? it->transform : 0;
+}
+
+bool AvatarBuilder::RemoveAllNoneHumanLeaf(NamedTransforms& namedTransform, HumanDescription const & humanDescription)
+{
+ bool didRemove = false;
+
+ for (int n = 0; n < namedTransform.size(); n++)
+ {
+ Transform& transfom = *namedTransform[n].transform;
+ bool hasAnyChildMapped = false;
+ for(int i=0;i < transfom.GetChildrenCount() && !hasAnyChildMapped; i++)
+ {
+ NamedTransforms::const_iterator it2 = std::find_if(namedTransform.begin(), namedTransform.end(), FindBone( transfom.GetChild(i).GetName() ) );
+ hasAnyChildMapped = it2 != namedTransform.end();
+ }
+
+ if( !hasAnyChildMapped)
+ {
+ HumanBoneList::const_iterator it2 = std::find_if(humanDescription.m_Human.begin(), humanDescription.m_Human.end(), FindBoneName( transfom.GetName() ));
+ if(it2==humanDescription.m_Human.end())
+ {
+ namedTransform.erase(namedTransform.begin() + n);
+ n--;
+ didRemove = true;
+ }
+ }
+ }
+
+ return didRemove;
+}
+
+void AvatarBuilder::ReadFromLocalTransformToSkeletonPose(mecanim::skeleton::SkeletonPose* pose, NamedTransforms const& namedTransform)
+{
+ int j;
+ for(j=0;j<namedTransform.size();j++)
+ {
+ Transform& transform = *namedTransform[j].transform;
+ pose->m_X[j] = xformFromUnity(transform.GetLocalPosition(), transform.GetLocalRotation(), transform.GetLocalScale());
+ }
+}
+
+bool AvatarBuilder::TPoseMatch(mecanim::animation::AvatarConstant const& avatar, NamedTransforms const& namedTransform, std::string& warning)
+{
+ bool ret = true;
+ if(avatar.isHuman())
+ {
+ mecanim::memory::MecanimAllocator alloc(kMemTempAlloc);
+
+ mecanim::skeleton::SkeletonPose* avatarSKPose = mecanim::skeleton::CreateSkeletonPose(avatar.m_AvatarSkeleton.Get(), alloc);
+ mecanim::skeleton::SkeletonPose* avatarHumanPose = mecanim::skeleton::CreateSkeletonPose(avatar.m_Human->m_Skeleton.Get(), alloc);
+ mecanim::skeleton::SkeletonPose* avatarGPose1 = mecanim::skeleton::CreateSkeletonPose(avatar.m_Human->m_Skeleton.Get(), alloc);
+ mecanim::skeleton::SkeletonPose* avatarGPose2 = mecanim::skeleton::CreateSkeletonPose(avatar.m_Human->m_Skeleton.Get(), alloc);
+
+ ReadFromLocalTransformToSkeletonPose(avatarSKPose, namedTransform);
+ mecanim::skeleton::SkeletonPoseCopy(avatar.m_AvatarSkeleton.Get(),avatarSKPose,avatar.m_Human->m_Skeleton.Get(), avatarHumanPose);
+
+ mecanim::skeleton::SkeletonPoseComputeGlobal(avatar.m_Human->m_Skeleton.Get(), avatarHumanPose, avatarGPose1);
+ mecanim::skeleton::SkeletonPoseComputeGlobal(avatar.m_Human->m_Skeleton.Get(), avatar.m_Human->m_SkeletonPose.Get(), avatarGPose2);
+
+ // Do not check for human hips bone lenght, because an animation is not guarantee to start at origin.
+ mecanim::uint32_t i = avatar.m_Human->m_HumanBoneIndex[mecanim::human::kHips + 1];
+ for(;i<avatar.m_Human->m_Skeleton->m_Count;++i)
+ {
+ mecanim::int32_t parentId = avatar.m_Human->m_Skeleton->m_Node[i].m_ParentId;
+ if(parentId != -1)
+ {
+ float len1 = math::length(avatarGPose1->m_X[i].t - avatarGPose1->m_X[parentId].t).tofloat();
+ float len2 = math::length(avatarGPose2->m_X[i].t - avatarGPose2->m_X[parentId].t).tofloat();
+
+ if( math::abs(len1-len2) > M_EPSF && math::abs(len2) > M_EPSF)
+ {
+ float ratio = len1/len2;
+
+ if( math::abs(1.f-ratio) > 0.30f)
+ {
+ warning += Format("'%s' : avatar = %.2f, animation = %.2f\n", namedTransform[avatar.m_HumanSkeletonIndexArray[i]].transform->GetName(), len2, len1);
+ ret = false;
+ }
+ }
+ }
+ }
+
+ mecanim::skeleton::DestroySkeletonPose(avatarSKPose, alloc);
+ mecanim::skeleton::DestroySkeletonPose(avatarHumanPose, alloc);
+ mecanim::skeleton::DestroySkeletonPose(avatarGPose1, alloc);
+ mecanim::skeleton::DestroySkeletonPose(avatarGPose2, alloc);
+ }
+
+ return ret;
+}
diff --git a/Runtime/Animation/AvatarBuilder.h b/Runtime/Animation/AvatarBuilder.h
new file mode 100644
index 0000000..03eb8ae
--- /dev/null
+++ b/Runtime/Animation/AvatarBuilder.h
@@ -0,0 +1,256 @@
+#ifndef AVATARBUILDER_H
+#define AVATARBUILDER_H
+
+#include "Runtime/BaseClasses/NamedObject.h"
+
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/mecanim/types.h"
+
+#include "Runtime/Animation/MecanimUtility.h"
+
+#include "Runtime/Serialize/SerializeTraits.h"
+
+#include <vector>
+
+namespace Unity { class GameObject; }
+class Avatar;
+class Transform;
+
+namespace mecanim
+{
+ namespace skeleton
+ {
+ struct SkeletonPose;
+ struct Skeleton;
+ }
+
+ namespace animation
+ {
+ struct AvatarConstant;
+ }
+}
+
+struct SkeletonBone
+{
+ DEFINE_GET_TYPESTRING(SkeletonBone)
+
+ SkeletonBone()
+ :m_Position(Vector3f::zero),
+ m_Rotation(Quaternionf::identity()),
+ m_Scale(Vector3f::one),
+ m_TransformModified(false)
+ {
+ }
+
+ UnityStr m_Name;
+ Vector3f m_Position;
+ Quaternionf m_Rotation;
+ Vector3f m_Scale;
+ bool m_TransformModified;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_Name);
+ TRANSFER(m_Position);
+ TRANSFER(m_Rotation);
+ TRANSFER(m_Scale);
+ TRANSFER(m_TransformModified);
+ transfer.Align();
+ }
+};
+
+struct SkeletonBoneLimit
+{
+ DEFINE_GET_TYPESTRING(SkeletonBoneLimit)
+
+ SkeletonBoneLimit()
+ :m_Min(Vector3f::zero),
+ m_Max(Vector3f::zero),
+ m_Value(Vector3f::zero),
+ m_Length(0.f),
+ m_Modified(false)
+ {
+ }
+
+ Vector3f m_Min;
+ Vector3f m_Max;
+ Vector3f m_Value;
+ float m_Length;
+ bool m_Modified;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_Min);
+ TRANSFER(m_Max);
+ TRANSFER(m_Value);
+ TRANSFER(m_Length);
+ TRANSFER(m_Modified);
+ transfer.Align();
+ }
+};
+
+
+struct HumanBone
+{
+ DEFINE_GET_TYPESTRING(HumanBone)
+
+ HumanBone();
+ HumanBone(std::string const& humanName);
+
+ UnityStr m_BoneName;
+ UnityStr m_HumanName;
+ SkeletonBoneLimit m_Limit;
+ //Vector3f m_ColliderPosition;
+ //Quaternionf m_ColliderRotation;
+ //Vector3f m_ColliderScale;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_BoneName);
+ TRANSFER(m_HumanName);
+ TRANSFER(m_Limit);
+
+ //TRANSFER(m_ColliderPosition);
+ //TRANSFER(m_ColliderRotation);
+ //TRANSFER(m_ColliderScale);
+ }
+};
+
+class FindHumanBone
+{
+protected:
+ UnityStr mName;
+public:
+ FindHumanBone(UnityStr const& name):mName(name){}
+ bool operator()(HumanBone const& bone){ return mName == bone.m_HumanName;}
+};
+
+class FindBoneName
+{
+protected:
+ UnityStr mName;
+public:
+ FindBoneName(UnityStr const& name):mName(name){}
+ bool operator()(HumanBone const& bone){ return mName == bone.m_BoneName;}
+};
+
+typedef std::vector<HumanBone> HumanBoneList;
+typedef std::vector<SkeletonBone> SkeletonBoneList;
+
+struct HumanDescription
+{
+ HumanBoneList m_Human;
+ SkeletonBoneList m_Skeleton;
+
+ float m_ArmTwist;
+ float m_ForeArmTwist;
+ float m_UpperLegTwist;
+ float m_LegTwist;
+
+ float m_ArmStretch;
+ float m_LegStretch;
+
+ float m_FeetSpacing;
+
+ UnityStr m_RootMotionBoneName;
+
+ void Reset()
+ {
+ m_Human.clear();
+ m_Skeleton.clear();
+
+ m_ArmTwist = 0.5f;
+ m_ForeArmTwist = 0.5f;
+ m_UpperLegTwist = 0.5f;
+ m_LegTwist = 0.5f;
+ m_ArmStretch = 0.05f;
+ m_LegStretch = 0.05f;
+
+ m_FeetSpacing = 0.0f;
+
+ m_RootMotionBoneName = "";
+ }
+
+ DEFINE_GET_TYPESTRING(HumanDescription)
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_Human);
+ TRANSFER(m_Skeleton);
+
+ TRANSFER(m_ArmTwist);
+ TRANSFER(m_ForeArmTwist);
+ TRANSFER(m_UpperLegTwist);
+ TRANSFER(m_LegTwist);
+ TRANSFER(m_ArmStretch);
+ TRANSFER(m_LegStretch);
+ TRANSFER(m_FeetSpacing);
+ TRANSFER(m_RootMotionBoneName);
+ }
+};
+
+// The type of Avatar to create
+enum AvatarType
+{
+ kGeneric = 2,
+ kHumanoid = 3
+};
+
+class AvatarBuilder
+{
+public:
+ struct Options
+ {
+ Options():avatarType(kGeneric), readTransform(false), useMask(false){}
+
+ AvatarType avatarType;
+ bool readTransform;
+ bool useMask;
+ };
+
+ struct NamedTransform
+ {
+ UnityStr name;
+ UnityStr path;
+ Transform* transform;
+ };
+
+ typedef std::vector<NamedTransform> NamedTransforms;
+
+ // readTransform is mainly used by importer. It does read the Default pose from the first frame pose found in the file.
+ // useMask is mainly used for API call, when suer want to select only a sub part of a hierarchy.
+ static std::string BuildAvatar(Avatar& avatar, const Unity::GameObject& go, bool doOptimizeTransformHierarchy, const HumanDescription& humanDescription, Options options = Options() );
+
+ static bool IsValidHuman(HumanDescription const& humanDescription, NamedTransforms const& namedTransform, std::string& error);
+ static bool IsValidHumanDescription(HumanDescription const& humanDescription, std::string& error);
+
+ static std::string GenerateAvatarMap(Unity::GameObject const& go, NamedTransforms& namedTransform, const HumanDescription& humanDescription, bool doOptimizeTransformHierarchy, AvatarType avatarType, bool useMask = false);
+
+ static void GetAllChildren(Transform& node, NamedTransforms& transforms, std::vector<UnityStr> const& mask = std::vector<UnityStr>() );
+ static void GetAllParent(Transform& node, NamedTransforms& transforms, std::vector<UnityStr> const& mask = std::vector<UnityStr>(), bool includeSelf = false );
+
+ static void ReadFromLocalTransformToSkeletonPose(mecanim::skeleton::SkeletonPose* pose, NamedTransforms const& namedTransform);
+
+ static bool TPoseMatch(mecanim::animation::AvatarConstant const& avatar, NamedTransforms const& namedTransform, std::string& warning);
+protected:
+
+ static void GetAllChildren (Transform& node, UnityStr& path, NamedTransforms& transforms, std::vector<UnityStr> const& mask);
+ static void GetAllParent(Transform& root, Transform& node, NamedTransforms& transforms, std::vector<UnityStr> const& mask = std::vector<UnityStr>(), bool includeSelf = false );
+
+ static Transform* GetTransform(int id, HumanDescription const& humanDescription, NamedTransforms const& namedTransform, std::vector<string> const& boneName);
+
+
+ static Transform* GetHipsNode(const HumanDescription& humanDescription, NamedTransforms const& transforms);
+ static Transform* GetRootMotionNode(const HumanDescription& humanDescription, NamedTransforms const& transforms);
+ static bool RemoveAllNoneHumanLeaf(NamedTransforms& namedTransform, HumanDescription const& humanDescription);
+};
+
+
+
+
+#endif
diff --git a/Runtime/Animation/AvatarPlayback.cpp b/Runtime/Animation/AvatarPlayback.cpp
new file mode 100644
index 0000000..1fd09af
--- /dev/null
+++ b/Runtime/Animation/AvatarPlayback.cpp
@@ -0,0 +1,118 @@
+#include "UnityPrefix.h"
+
+#include "AvatarPlayback.h"
+
+#include "Runtime/mecanim/animation/avatar.h"
+#include "Runtime/Serialize/Blobification/BlobWrite.h"
+#include "Runtime/Animation/MecanimUtility.h"
+#include "Runtime/Utilities/LogAssert.h"
+
+enum { kMaxFrameCount = 10000};
+
+AvatarPlayback::AvatarPlayback(MemLabelId label)
+: m_Alloc(label),
+ m_FrameCount(-1),
+ m_CursorIndex(-1),
+ m_StartIndex(-1),
+ m_StopIndex(-1)
+{
+}
+
+void AvatarPlayback::Init(int frameCount)
+{
+ Clear();
+ if(frameCount > 0 )
+ {
+ if(frameCount > kMaxFrameCount )
+ WarningString("Could not allocate requested frameCount for Animator Recording. 10000 frames where allocated.");
+
+ m_Frames.resize(min<unsigned int>(frameCount,kMaxFrameCount));
+ m_FrameCount = m_Frames.size();
+ }
+ else
+ {
+ m_FrameCount = 0 ;
+ }
+
+ m_CursorIndex = -1;
+ m_StartIndex = -1 ;
+ m_StopIndex = -1;
+}
+
+void AvatarPlayback::Clear()
+{
+ for(int i = 0 ; i < m_Frames.size() ; i++)
+ {
+ mecanim::animation::DestroyAvatarMemory(m_Frames[i].m_AvatarMemory, m_Alloc);
+ }
+ m_Frames.clear();
+}
+
+int AvatarPlayback::NextIndex(int index)
+{
+ return m_FrameCount > 0 ? (index+1)%m_FrameCount : index+1;
+}
+
+mecanim::animation::AvatarMemory* AvatarPlayback::PlayFrame(float time, float &effectiveTime)
+{
+ int frameIndex = m_StopIndex;
+ bool found = false;
+
+ if(m_StartIndex == -1)
+ return 0;
+
+ int i = m_StartIndex;
+ int prevIndex = i;
+
+ int endIndex = NextIndex(m_StopIndex);
+ do // at this point, we have a least one frame recorded
+ {
+ if(m_Frames[i].m_CurrentTime > time)
+ {
+ frameIndex = prevIndex;
+ found = true;
+ }
+
+ prevIndex = i;
+ i = NextIndex(i);
+ } while(i != endIndex && !found);
+
+
+ effectiveTime = m_Frames[frameIndex].m_CurrentTime;
+ m_CursorIndex = frameIndex;
+
+ return m_Frames[frameIndex].m_AvatarMemory;
+}
+
+void AvatarPlayback::RecordFrame(float deltaTime, const mecanim::animation::AvatarMemory* srcMemory)
+{
+ if(m_FrameCount == -1)
+ {
+ WarningString("Could not record Animator. Frame allocation has failed.");
+ return;
+ }
+
+ AvatarFrame newFrame;
+ if(m_StartIndex != -1)
+ newFrame.m_CurrentTime = m_Frames[m_CursorIndex].m_CurrentTime + deltaTime;
+
+ size_t size=0;
+ newFrame.m_AvatarMemory = CopyBlob( *srcMemory, m_Alloc, size);
+
+ m_CursorIndex = NextIndex(m_CursorIndex);
+ if(m_StartIndex == m_CursorIndex|| m_StartIndex == -1) // increment startIndex when the cursor is writing on it (the buffer is full)
+ {
+ if(m_StartIndex != -1)
+ mecanim::animation::DestroyAvatarMemory(m_Frames[m_CursorIndex].m_AvatarMemory, m_Alloc);
+ m_StartIndex = NextIndex(m_StartIndex);
+ }
+ m_StopIndex = m_CursorIndex;
+ if(m_FrameCount > 0)
+ m_Frames[m_CursorIndex] = newFrame;
+ else
+ m_Frames.push_back(newFrame);
+}
+
+float AvatarPlayback::CursorTime() {return m_CursorIndex != -1 ? m_Frames[m_CursorIndex].m_CurrentTime : -1;}
+float AvatarPlayback::StartTime() {return m_CursorIndex != -1 ? m_Frames[m_StartIndex].m_CurrentTime : -1;}
+float AvatarPlayback::StopTime() {return m_CursorIndex != -1 ? m_Frames[m_StopIndex].m_CurrentTime : -1;}
diff --git a/Runtime/Animation/AvatarPlayback.h b/Runtime/Animation/AvatarPlayback.h
new file mode 100644
index 0000000..75e9ff4
--- /dev/null
+++ b/Runtime/Animation/AvatarPlayback.h
@@ -0,0 +1,51 @@
+#ifndef AVATARPLAYBACK_H
+#define AVATARPLAYBACK_H
+
+#include "Runtime/Animation/MecanimUtility.h"
+
+namespace mecanim
+{
+ namespace animation
+ {
+ struct AvatarMemory ;
+ }
+}
+
+class AvatarFrame
+{
+public:
+ AvatarFrame() : m_AvatarMemory(0), m_CurrentTime(0) {}
+ mecanim::animation::AvatarMemory* m_AvatarMemory;
+ float m_CurrentTime;
+};
+
+class AvatarPlayback
+{
+public:
+
+ AvatarPlayback(MemLabelId label);
+
+ void Clear();
+ mecanim::animation::AvatarMemory* PlayFrame(float time, float& effectiveTime);
+ void RecordFrame(float deltaTime, const mecanim::animation::AvatarMemory* srcMemory);
+
+ void Init(int frameCount);
+
+ float StartTime();
+ float StopTime();
+ float CursorTime();
+
+
+private:
+ std::vector<AvatarFrame> m_Frames;
+
+ int m_FrameCount;
+ int m_StartIndex;
+ int m_StopIndex;
+ int m_CursorIndex;
+
+ int NextIndex(int index);
+ mecanim::memory::MecanimAllocator m_Alloc;
+};
+
+#endif // AVATARPLAYBACK_H
diff --git a/Runtime/Animation/BaseAnimationTrack.cpp b/Runtime/Animation/BaseAnimationTrack.cpp
new file mode 100644
index 0000000..6dafca6
--- /dev/null
+++ b/Runtime/Animation/BaseAnimationTrack.cpp
@@ -0,0 +1,15 @@
+#include "UnityPrefix.h"
+
+#if UNITY_EDITOR
+#include "BaseAnimationTrack.h"
+
+IMPLEMENT_CLASS (BaseAnimationTrack)
+
+BaseAnimationTrack::BaseAnimationTrack(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{}
+
+BaseAnimationTrack::~BaseAnimationTrack()
+{}
+
+#endif
diff --git a/Runtime/Animation/BaseAnimationTrack.h b/Runtime/Animation/BaseAnimationTrack.h
new file mode 100644
index 0000000..c3f2b40
--- /dev/null
+++ b/Runtime/Animation/BaseAnimationTrack.h
@@ -0,0 +1,21 @@
+#if UNITY_EDITOR
+#ifndef BASEANIMATIONTRACK_H
+#define BASEANIMATIONTRACK_H
+
+#include "Runtime/BaseClasses/NamedObject.h"
+
+template<class T> class AnimationCurveTpl;
+typedef AnimationCurveTpl<float> AnimationCurve;
+
+class BaseAnimationTrack : public NamedObject
+{
+ public:
+
+ REGISTER_DERIVED_ABSTRACT_CLASS (BaseAnimationTrack, NamedObject)
+
+ BaseAnimationTrack(MemLabelId label, ObjectCreationMode mode);
+ // ~BaseAnimationTrack (); declared-by-macro
+};
+
+#endif
+#endif
diff --git a/Runtime/Animation/BoundCurve.h b/Runtime/Animation/BoundCurve.h
new file mode 100644
index 0000000..5a73169
--- /dev/null
+++ b/Runtime/Animation/BoundCurve.h
@@ -0,0 +1,51 @@
+#pragma once
+
+class IAnimationBinding;
+
+namespace UnityEngine
+{
+namespace Animation
+{
+
+
+// These values can can never be changed, when a system is deprecated,
+// it must be kept commented out and the index is not to be reused!
+// The enum value can not be reused. Otherwise built data (assetbundles) will break.
+enum BindType
+{
+ kUnbound = 0,
+
+ // Builtin transform bindings
+ kBindTransformPosition = 1, // This enum may not be changed. It is used in GenericClipBinding.
+ kBindTransformRotation = 2, // This enum may not be changed. It is used in GenericClipBinding.
+ kBindTransformScale = 3, // It is used in GenericClipBinding.
+
+ // Builtin float bindings
+ kMinSinglePropertyBinding = 5,
+ kBindFloat = 5,
+ kBindFloatToBool = 6,
+ kBindGameObjectActive = 7,
+ kBindMuscle = 8,
+
+
+ // Custom bindings
+ kBlendShapeWeightBinding = 20,
+ kRendererMaterialPPtrBinding = 21,
+ kRendererMaterialPropertyBinding = 22,
+ kSpriteRendererPPtrBinding = 23,
+ kMonoBehaviourPropertyBinding = 24,
+ kAllBindingCount
+};
+
+struct BoundCurve
+{
+ void* targetPtr;
+ UInt32 targetType;
+ IAnimationBinding* customBinding;
+ Object* targetObject;
+
+ BoundCurve () { targetObject = 0; targetPtr = 0; targetType = 0; customBinding = 0; }
+};
+
+}
+}
diff --git a/Runtime/Animation/BoundCurveDeprecated.h b/Runtime/Animation/BoundCurveDeprecated.h
new file mode 100644
index 0000000..0edc24e
--- /dev/null
+++ b/Runtime/Animation/BoundCurveDeprecated.h
@@ -0,0 +1,60 @@
+#pragma once
+
+
+struct BoundCurveDeprecated
+{
+ int targetInstanceID;
+
+ // The ptr to the data we are writing into
+ void* targetPtr;
+
+ // type of the data we will write into kBindFloatValue, kBindQuaternion etc.
+ // - Materials: 4 bits targetType, 24 bits shader property name, 4 bits sub property index (x, y, z, w)
+ // - SkinnedMeshRenderer: 4 bits targetType, the rest BlendShape weight index
+ UInt32 targetType;
+
+ // Which states affect the bound curve?
+ UInt32 affectedStateMask;
+
+ Object* targetObject;
+
+ BoundCurveDeprecated () { targetObject = NULL; targetType = 0; targetInstanceID = 0; targetPtr = 0; affectedStateMask = 0; }
+
+ enum
+ {
+ kBindTypeBitCount = 4,
+ kBindMaterialShaderPropertyNameBitCount = 24,
+
+ kBindTypeMask = (1 << kBindTypeBitCount) - 1,
+ kBindMaterialShaderPropertyNameMask = ((1 << kBindMaterialShaderPropertyNameBitCount) - 1) << kBindTypeBitCount,
+ kBindMaterialShaderSubpropertyMask = ~(kBindTypeMask | kBindMaterialShaderPropertyNameMask),
+ };
+};
+
+enum
+{
+ kUnbound = 0,
+ kBindTransformPosition = 1, // This enum may not be changed. It is used in GenericClipBinding.
+ kBindTransformRotation = 2, // This enum may not be changed. It is used in GenericClipBinding.
+ kBindTransformScale = 3, // It is used in GenericClipBinding.
+ kMinGenericBinding = 4,
+
+ kBindFloat,
+ kBindFloatToBool,
+ kBindFloatToBlendShapeWeight,
+ kBindFloatToGameObjectActivate,
+
+ kMinIntCurveBinding,
+ kBindMaterialPPtrToRenderer = kMinIntCurveBinding,
+#if ENABLE_SPRITES
+ kBindSpritePPtrToSpriteRenderer,
+#endif
+ kMaxIntCurveBinding,
+
+ // These must always come last since materials do special bit masking magic.
+ kBindFloatToMaterial = kMaxIntCurveBinding,
+ kBindFloatToColorMaterial,
+ kBindFloatToMaterialScaleAndOffset,
+
+ kBindTypeCount
+};
diff --git a/Runtime/Animation/CalculateAnimatorSkinMatrices.cpp b/Runtime/Animation/CalculateAnimatorSkinMatrices.cpp
new file mode 100644
index 0000000..4e2091a
--- /dev/null
+++ b/Runtime/Animation/CalculateAnimatorSkinMatrices.cpp
@@ -0,0 +1,85 @@
+#include "UnityPrefix.h"
+
+#include "Runtime/Animation/CalculateAnimatorSkinMatrices.h"
+#include "Runtime/Animation/Avatar.h"
+#include "Runtime/Animation/Animator.h"
+#include "Runtime/Animation/MecanimUtility.h"
+#include "Runtime/Filters/Mesh/MeshSkinning.h"
+#include "Runtime/mecanim/animation/avatar.h"
+#include "Runtime/mecanim/skeleton/skeleton.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Profiler/Profiler.h"
+
+PROFILER_INFORMATION(gMeshSkinningPullMatrices, "MeshSkinning.CalculateSkinningMatrices", kProfilerRender)
+
+void* DoCalculateAnimatorSkinMatrices (void* userData)
+{
+ PROFILER_AUTO(gMeshSkinningPullMatrices, NULL);
+
+ CalculateSkinMatricesTask& task = *static_cast<CalculateSkinMatricesTask*>(userData);
+ const mecanim::skeleton::SkeletonPose* animatedPose =
+ reinterpret_cast<const mecanim::skeleton::SkeletonPose*>(task.skeletonPose);
+
+ for (int i=0; i<task.bindPoseCount; i++)
+ {
+ UInt16 skeletonIndex = task.skeletonIndices[i];
+ xform2unity (animatedPose->m_X[skeletonIndex], task.outPose[i]);
+ }
+
+ MultiplyMatrixArrayWithBase4x4 (&task.rootPose, task.outPose, task.bindPose, task.outPose, task.bindPoseCount);
+ return NULL;
+}
+
+static void CalculateDefaultPoseWorldSpaceMatrices (
+ const Transform& transform,
+ const mecanim::animation::AvatarConstant* avatarConstant,
+ const UInt16* skeletonIndices,
+ Matrix4x4f* outWorldSpaceMatrices,
+ size_t size)
+{
+ // Extract default skeleton pose from avatar and setup root position according to transform.
+ Assert (avatarConstant);
+
+ mecanim::memory::MecanimAllocator tempAlloc (kMemTempAlloc);
+ mecanim::skeleton::SkeletonPose* tempDefaultGPose = mecanim::skeleton::CreateSkeletonPose(avatarConstant->m_AvatarSkeleton.Get(), tempAlloc);
+
+ SkeletonPoseCopy (avatarConstant->m_DefaultPose.Get(), tempDefaultGPose);
+ tempDefaultGPose->m_X[0] = xformFromUnity (transform.GetPosition(), transform.GetRotation(), transform.GetWorldScaleLossy());
+ mecanim::skeleton::SkeletonPoseComputeGlobal (avatarConstant->m_AvatarSkeleton.Get(), tempDefaultGPose, tempDefaultGPose);
+ for (int i=0;i<size;i++)
+ {
+ UInt16 skeletonIndex = skeletonIndices[i];
+ xform2unity (tempDefaultGPose->m_X[skeletonIndex], outWorldSpaceMatrices[i]);
+ }
+
+ mecanim::skeleton::DestroySkeletonPose(tempDefaultGPose, tempAlloc);
+}
+
+bool CalculateWordSpaceMatrices (Animator* animator, const UInt16* skeletonIndices, Matrix4x4f* outWorldSpaceMatrices, size_t size)
+{
+ const mecanim::skeleton::SkeletonPose* animatedPose = animator->GetGlobalSpaceSkeletonPose ();
+
+ if (animatedPose)
+ {
+ for (int i=0;i<size;i++)
+ {
+ UInt16 skeletonIndex = skeletonIndices[i];
+ xform2unity (animatedPose->m_X[skeletonIndex], outWorldSpaceMatrices[i]);
+ }
+ }
+ else
+ {
+ const mecanim::animation::AvatarConstant* avatarConstant = animator->GetAvatarConstant ();
+ if (avatarConstant == NULL)
+ {
+ // Slow code path:
+ Avatar* avatar = animator->GetAvatar ();
+ if (avatar)
+ avatarConstant = avatar->GetAsset ();
+ if (avatarConstant == NULL)
+ return false;
+ }
+ CalculateDefaultPoseWorldSpaceMatrices (animator->GetComponent(Transform), avatarConstant, skeletonIndices, outWorldSpaceMatrices, size);
+ }
+ return true;
+}
diff --git a/Runtime/Animation/CalculateAnimatorSkinMatrices.h b/Runtime/Animation/CalculateAnimatorSkinMatrices.h
new file mode 100644
index 0000000..a0ee939
--- /dev/null
+++ b/Runtime/Animation/CalculateAnimatorSkinMatrices.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "Runtime/Interfaces/IAnimation.h"
+
+class Animator;
+
+// Multi-threaded.
+// Can only get animated pose.
+// Bindpose included.
+void* DoCalculateAnimatorSkinMatrices (void* userData);
+
+// Main thread.
+// Can handle both cases: animated pose & default pose.
+// Bindpose not included.
+bool CalculateWordSpaceMatrices (Animator* animator, const UInt16* skeletonIndices, Matrix4x4f* outWorldSpaceMatrices, size_t size);
+
+
+
diff --git a/Runtime/Animation/CharacterTestFixture.h b/Runtime/Animation/CharacterTestFixture.h
new file mode 100644
index 0000000..cd72c5e
--- /dev/null
+++ b/Runtime/Animation/CharacterTestFixture.h
@@ -0,0 +1,202 @@
+#pragma once
+
+#if ENABLE_UNIT_TESTS
+
+#include "Runtime/Testing/Testing.h"
+#include "Runtime/Testing/TestFixtures.h"
+
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Animation/Avatar.h"
+#include "Runtime/Animation/AvatarBuilder.h"
+#include "Runtime/Animation/Animator.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Filters/Deformation/SkinnedMeshFilter.h"
+
+#include "Runtime/Animation/OptimizeTransformHierarchy.h"
+
+
+// y
+// |
+// |
+// 3 b1_2_2
+// | | mr2
+// | | |
+// 2 b1_2----b1_2_1 b2_1_2-----b2_1_2_1
+// | | mr1 |
+// | | | |
+// 1 b1-----b1_1---b1_1_1 b2-----b2_1------b2_1_1
+// |
+// 0____1_______2_______3_________4_______5_________6_________ x
+//
+// smr1 {b1(root), b1_1, b1_1_1, b1_2, b1_2_1, b1_2_2}
+// smr2 {b2_1_1, b2_1_2, b2_1_2_1}
+
+struct TransformDescriptor {
+ const char* path;
+ float t[3];
+ float r[3];
+ float s[3];
+};
+const TransformDescriptor BONE_ARRAY[] = {
+ {"b1", {1,1,0}, {0,0,0}, {1,1,1} },
+ {"b1/b1_1", {2,1,0}, {0,0,0}, {1,1,1} },
+ {"b1/b1_1/b1_1_1", {3,1,0}, {0,0,0}, {1,1,1} },
+ {"b1/b1_2", {1,2,0}, {0,0,0}, {1,1,1} },
+ {"b1/b1_2/b1_2_1", {2,2,0}, {0,0,0}, {1,1,1} },
+ {"b1/b1_2/b1_2_1/tobeStrip1", {5,5,5}, {0,0,0}, {1,1,1} },
+ {"b1/b1_2/b1_2_2", {1,3,0}, {0,0,0}, {1,1,1} },
+ {"b2", {4,1,0}, {0,0,0}, {1,1,1} },
+ {"b2/b2_1", {5,1,0}, {0,0,0}, {1,1,1} },
+ {"b2/b2_1/b2_1_1", {6,1,0}, {0,0,0}, {1,1,1} },
+ {"b2/b2_1/b2_1_2", {5,2,0}, {0,0,0}, {1,1,1} },
+ {"b2/b2_1/b2_1_2/b2_1_2_1", {6,2,0}, {0,0,0}, {1,1,1} },
+ {"b2/b2_1/b2_1_2/b2_1_2_1/tobeStrip3", {6,6,6}, {0,0,0}, {1,1,1} },
+ {"tobeStrip2", {7,7,7}, {0,0,0}, {1,1,1} },
+};
+const int BONE_COUNT = sizeof(BONE_ARRAY)/sizeof(BONE_ARRAY[0]);
+const TransformDescriptor MESH_RENDERER_ARRAY[] = {
+ {"b1/b1_1/b1_1_1/mr1", {3,1.5,0}, {0,0,0}, {1,1,1} },
+ {"b1/b1_2/b1_2_1/mr2", {2,2.5,0}, {0,0,0}, {1,1,1} },
+};
+const int MESH_RENDERER_COUNT = sizeof(MESH_RENDERER_ARRAY)/sizeof(MESH_RENDERER_ARRAY[0]);
+const TransformDescriptor SKINNED_MESH_RENDERER_ARRAY[] = {
+ {"smr1", {8,8,8}, {0,0,0}, {1,1,1} },
+ {"b2/b2_1/smr2", {9,9,9}, {0,0,0}, {1,1,1} },
+};
+const int SKINNED_MESH_RENDERER_COUNT = sizeof(SKINNED_MESH_RENDERER_ARRAY)/sizeof(SKINNED_MESH_RENDERER_ARRAY[0]);
+
+class CharacterTestFixture : public TestFixtureBase
+{
+protected:
+ Unity::GameObject* root;
+ Avatar* avatar;
+ Avatar* unstrippedAvatar;
+
+ CharacterTestFixture():root(NULL), avatar(NULL), unstrippedAvatar(NULL) {}
+
+ ~CharacterTestFixture()
+ {
+ DestroyObjects();
+ }
+
+ void MakeCharacter(const UnityStr* extraExposedPaths = NULL, int extraExposedPathCount = 0)
+ {
+ root = &CreateGameObjectWithHideFlags("root", true, 0, "Transform", "Animator", NULL);
+ Transform& rootTr = root->GetComponent(Transform);
+ AttachGameObjects(rootTr);
+
+ CreateAvatars(extraExposedPaths, extraExposedPathCount);
+
+ Animator& animator = root->GetComponent(Animator);
+ animator.SetAvatar(avatar);
+ animator.AwakeFromLoad(kDefaultAwakeFromLoad);
+ }
+
+ void DestroyObjects()
+ {
+ if (root)
+ {
+ DestroyObjectHighLevel(root);
+ root = NULL;
+ }
+ if (avatar)
+ {
+ DestroyObjectHighLevel(avatar);
+ avatar = NULL;
+ }
+ if (unstrippedAvatar)
+ {
+ DestroyObjectHighLevel(unstrippedAvatar);
+ unstrippedAvatar = NULL;
+ }
+ }
+
+ void CreateAvatars(const UnityStr* extraExposedPaths = NULL, int extraExposedPathCount = 0)
+ {
+ HumanDescription dummyHd;
+ std::string errStr;
+
+ AvatarBuilder::Options options;
+ options.avatarType = kGeneric;
+ options.readTransform = true;
+
+ //// 1. create unstripped avatar
+ //unstrippedAvatar = NEW_OBJECT (Avatar);
+ //unstrippedAvatar->Reset();
+ //errStr = AvatarBuilder::BuildAvatar(*unstrippedAvatar, *root, true, dummyHd, options);
+ //CHECK_EQUAL (string(""), errStr);
+ //unstrippedAvatar->AwakeFromLoad(kDefaultAwakeFromLoad);
+
+ //// 2. strip
+ //RemoveUnnecessaryTransforms(*root,
+ // &dummyHd,
+ // extraExposedPaths,
+ // extraExposedPathCount,
+ // true);
+
+ // 3. create stripped avatar
+ avatar = NEW_OBJECT (Avatar);
+ avatar->Reset();
+
+ errStr = AvatarBuilder::BuildAvatar(*avatar,
+ *root, true, dummyHd, options);
+ CHECK_EQUAL (string(""), errStr);
+ avatar->AwakeFromLoad(kDefaultAwakeFromLoad);
+ }
+
+ void AttachGameObjects(Transform& rootTr)
+ {
+ dynamic_array<PPtr<Transform> > smrBones[SKINNED_MESH_RENDERER_COUNT];
+
+ dynamic_array<TransformDescriptor> transforms;
+ for (int i = 0; i < BONE_COUNT; i++) transforms.push_back(BONE_ARRAY[i]);
+ for (int i = 0; i < MESH_RENDERER_COUNT; i++) transforms.push_back(MESH_RENDERER_ARRAY[i]);
+ for (int i = 0; i < SKINNED_MESH_RENDERER_COUNT; i++) transforms.push_back(SKINNED_MESH_RENDERER_ARRAY[i]);
+ for (int i = 0; i < transforms.size(); i++)
+ {
+ string path(transforms[i].path);
+
+ string parentPath = DeleteLastPathNameComponent(path);
+ Transform* parent = FindRelativeTransformWithPath(rootTr, parentPath.c_str());
+ CHECK(parent != NULL);
+
+ string goName = GetLastPathNameComponent(path);
+ GameObject& go = CreateGameObjectWithHideFlags(goName, true, 0, "Transform", NULL);
+ Transform& tr = go.GetComponent(Transform);
+ tr.SetPosition(Vector3f(transforms[i].t[0], transforms[i].t[1], transforms[i].t[2]));
+ tr.SetParent(parent);
+
+ if (goName.compare(0, strlen("b1"), "b1") == 0)
+ smrBones[0].push_back(&tr);
+ else if (goName.compare(0, strlen("b2_1_"), "b2_1_") == 0)
+ smrBones[1].push_back(&tr);
+ }
+
+ for (int i = 0; i < MESH_RENDERER_COUNT; i++)
+ {
+ Transform* tr = FindRelativeTransformWithPath(rootTr, MESH_RENDERER_ARRAY[i].path);
+ AddComponent(tr->GetGameObject(), "MeshRenderer");
+ }
+
+ for (int i = 0; i < SKINNED_MESH_RENDERER_COUNT; i++)
+ {
+ Transform* tr = FindRelativeTransformWithPath(rootTr, SKINNED_MESH_RENDERER_ARRAY[i].path);
+ AddComponent(tr->GetGameObject(), "SkinnedMeshRenderer");
+ SkinnedMeshRenderer& smr = tr->GetComponent(SkinnedMeshRenderer);
+ if (i == 0)
+ smr.SetRootBone(smrBones[i][0]);
+ smr.SetBones(smrBones[i]);
+ }
+ }
+
+ int GetAllChildrenCount(Transform& tr)
+ {
+ int count = tr.GetChildrenCount();
+ for (int i = 0; i < tr.GetChildrenCount(); i++)
+ count += GetAllChildrenCount(tr.GetChild(i));
+ return count;
+ }
+
+};
+
+#endif
diff --git a/Runtime/Animation/DenseClipBuilder.cpp b/Runtime/Animation/DenseClipBuilder.cpp
new file mode 100644
index 0000000..4fd800d
--- /dev/null
+++ b/Runtime/Animation/DenseClipBuilder.cpp
@@ -0,0 +1,97 @@
+#include "UnityPrefix.h"
+#include "DenseClipBuilder.h"
+#include "Runtime/mecanim/memory.h"
+
+void CreateDenseClip(mecanim::animation::DenseClip& clip, UInt32 curveCount, float begin, float end, float sampleRate, mecanim::memory::Allocator& alloc)
+{
+ // We always need at least one frame to sample the clip
+ clip.m_FrameCount = std::max(CeilfToInt ((end - begin) * sampleRate), 1);
+ clip.m_CurveCount = curveCount;
+ clip.m_SampleRate = sampleRate;
+ clip.m_BeginTime = begin;
+
+ clip.m_SampleArraySize = clip.m_FrameCount * clip.m_CurveCount;
+ clip.m_SampleArray = alloc.ConstructArray<float>(clip.m_SampleArraySize);
+}
+
+template<class T>
+void AddCurveToDenseClip(mecanim::animation::DenseClip& clip, int curveIndex, const AnimationCurveTpl<T>& curve)
+{
+ for (int i=0;i<clip.m_FrameCount;i++)
+ {
+ float time = clip.m_BeginTime + ((float)i / clip.m_SampleRate);
+
+ float* dst = &clip.m_SampleArray[i * clip.m_CurveCount + curveIndex];
+
+ T value = curve.EvaluateClamp(time);
+ *reinterpret_cast<T*> (dst) = value;
+
+ }
+}
+
+template void AddCurveToDenseClip<float>(mecanim::animation::DenseClip& clip, int curveIndex, const AnimationCurveTpl<float>& curve);
+template void AddCurveToDenseClip<Vector3f>(mecanim::animation::DenseClip& clip, int curveIndex, const AnimationCurveTpl<Vector3f>& curve);
+template void AddCurveToDenseClip<Quaternionf>(mecanim::animation::DenseClip& clip, int curveIndex, const AnimationCurveTpl<Quaternionf>& curve);
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+typedef AnimationCurveVec3::Keyframe KeyframeVec3;
+static Vector3f Evaluate3 (const mecanim::animation::DenseClip& clip, float time)
+{
+ float output[4];
+ SampleClip(clip, time, output);
+
+ return Vector3f(output[1], output[2], output[3]);
+}
+
+typedef AnimationCurve::Keyframe Keyframe;
+static float Evaluate1 (const mecanim::animation::DenseClip& clip, float time)
+{
+ float output;
+ output = SampleClipAtIndex(clip, time, 0);
+ return output;
+}
+
+
+SUITE (DenseClipBuilderTests)
+{
+TEST (DenseClipBuilder_EvaluationVector3)
+{
+ mecanim::memory::MecanimAllocator alloc(kMemTempAlloc);
+
+ AnimationCurveVec3 curve;
+ curve.AddKeyBackFast(KeyframeVec3(0.5F, Vector3f(0.0,1.0,2.0)));
+ curve.AddKeyBackFast(KeyframeVec3(1.0F, Vector3f(3.0,0.0,4.0)));
+ curve.AddKeyBackFast(KeyframeVec3(2.0F, Vector3f(0.0,-1.0,-2.0)));
+
+ AnimationCurve curve2;
+ curve2.AddKeyBackFast(Keyframe(0.3F, 1.0));
+ curve2.AddKeyBackFast(Keyframe(1.2F, 20.0));
+ curve2.AddKeyBackFast(Keyframe(2.3F, 5.0));
+
+
+ mecanim::animation::DenseClip clip;
+ CreateDenseClip (clip, 3+1, std::min(curve.GetRange().first, curve2.GetRange().first), std::max(curve.GetRange().second, curve2.GetRange().second), 30.0F, alloc);
+ AddCurveToDenseClip(clip, 0, curve2);
+ AddCurveToDenseClip(clip, 1, curve);
+
+ float kEpsilon = 0.001F;
+
+ CHECK(CompareApproximately(curve.EvaluateClamp(1.5F), Evaluate3(clip, 1.5F), kEpsilon));
+
+
+ CHECK(CompareApproximately(curve.EvaluateClamp(-5.0), Evaluate3(clip, -5.0F), kEpsilon));
+ CHECK(CompareApproximately(curve.EvaluateClamp(-5.0), Evaluate3(clip, -5.0F), kEpsilon));
+ CHECK(CompareApproximately(curve.EvaluateClamp(0.0F), Evaluate3(clip, 0.0F), kEpsilon));
+ CHECK(CompareApproximately(curve.EvaluateClamp(1.439F), Evaluate3(clip, 1.439F), kEpsilon));
+ CHECK(CompareApproximately(curve.EvaluateClamp(2.0F), Evaluate3(clip, 2.0F), kEpsilon));
+ CHECK(CompareApproximately(curve.EvaluateClamp(0.1F), Evaluate3(clip, 0.1F), kEpsilon));
+ CHECK(CompareApproximately(curve.EvaluateClamp(100.0F), Evaluate3(clip, 100.0F), kEpsilon));
+ CHECK(CompareApproximately(curve.EvaluateClamp(-19), Evaluate3(clip, -19.0F), kEpsilon));
+
+ DestroyDenseClip (clip, alloc);
+}
+}
+#endif
diff --git a/Runtime/Animation/DenseClipBuilder.h b/Runtime/Animation/DenseClipBuilder.h
new file mode 100644
index 0000000..dff9864
--- /dev/null
+++ b/Runtime/Animation/DenseClipBuilder.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "Runtime/Math/AnimationCurve.h"
+#include "Runtime/mecanim/animation/denseclip.h"
+
+void CreateDenseClip(mecanim::animation::DenseClip& clip, UInt32 curveCount, float begin, float end, float sampleRate, mecanim::memory::Allocator& alloc);
+
+template<class T>
+void AddCurveToDenseClip(mecanim::animation::DenseClip& clip, int curveIndex, const AnimationCurveTpl<T>& curve);
diff --git a/Runtime/Animation/EditorCurveBinding.h b/Runtime/Animation/EditorCurveBinding.h
new file mode 100644
index 0000000..c98ec7e
--- /dev/null
+++ b/Runtime/Animation/EditorCurveBinding.h
@@ -0,0 +1,34 @@
+#pragma once
+
+class MonoScript;
+
+struct EditorCurveBinding
+{
+ std::string path;
+ std::string attribute; ///@TODO: Rename to propertyName, thats how it is called in C#
+ int classID;
+ MonoScript* script;
+ bool isPPtrCurve;
+
+ EditorCurveBinding ()
+ {
+ script = NULL;
+ isPPtrCurve = false;
+ classID = 0;
+ }
+
+
+ EditorCurveBinding (const std::string& inPath, int inClassID, MonoScript* inScript, const std::string& inAttribute, bool inIsPPtrCurve)
+ {
+ path = inPath;
+ classID = inClassID;
+ script = inScript;
+ attribute = inAttribute;
+ isPPtrCurve = inIsPPtrCurve;
+ }
+
+ friend bool operator == (const EditorCurveBinding& lhs, const EditorCurveBinding& rhs)
+ {
+ return lhs.path == rhs.path && lhs.attribute == rhs.attribute && lhs.classID == rhs.classID && lhs.script == rhs.script && lhs.isPPtrCurve == rhs.isPPtrCurve;
+ }
+};
diff --git a/Runtime/Animation/GenericAnimationBindingCache.cpp b/Runtime/Animation/GenericAnimationBindingCache.cpp
new file mode 100644
index 0000000..fe6688f
--- /dev/null
+++ b/Runtime/Animation/GenericAnimationBindingCache.cpp
@@ -0,0 +1,839 @@
+#include "UnityPrefix.h"
+#include "GenericAnimationBindingCache.h"
+#include "AnimationClipBindings.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/Serialize/TransferUtility.h"
+#include "Runtime/mecanim/generic/crc32.h"
+#include "Runtime/mecanim/animation/clipmuscle.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Interfaces/IAnimationBinding.h"
+#include "Animator.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+#include "AnimatorController.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+
+const static char* kIsActive = "m_IsActive";
+
+typedef UInt32 BindingHash;
+
+namespace UnityEngine
+{
+namespace Animation
+{
+
+bool IsMuscleBinding(const GenericBinding& binding)
+{
+ return binding.classID == ClassID(Animator) && binding.customType == kBindMuscle;
+}
+
+struct CachedBinding
+{
+ BindingHash propertyHash;
+ int offset;
+ int bindType;
+};
+
+struct CachedComponentBindings
+{
+ ScriptingClassPtr scriptingClass;
+ int classID;
+ size_t bindingSize;
+ CachedBinding* bindings;
+};
+
+
+static GenericAnimationBindingCache* gGenericBindingCache = NULL;
+
+static CachedComponentBindings* GenerateComponentBinding (int classID, ScriptingObjectPtr scriptingInstance, ScriptingClassPtr scriptingClass, Object* targetObject);
+static const CachedBinding* FindBinding (const CachedComponentBindings& componentBinding, BindingHash attribute);
+static void DestroyBinding (CachedComponentBindings* binding);
+
+bool operator < (const CachedBinding& lhs, const CachedBinding& rhs)
+{
+ return lhs.propertyHash < rhs.propertyHash;
+}
+
+
+static ClassIDType BindCurve (const CachedComponentBindings& cachedBinding, const GenericBinding& inputBinding, Object* targetObject, void* targetPtr, BoundCurve& bound)
+{
+ const CachedBinding* found = FindBinding (cachedBinding, inputBinding.attribute);
+ if (found == NULL)
+ {
+ bound.targetType = kUnbound;
+ return ClassID(Undefined);
+ }
+
+ bound.targetObject = targetObject;
+ bound.targetPtr = reinterpret_cast<UInt8*> (targetPtr) + found->offset;
+ bound.targetType = found->bindType;
+
+ if (found->bindType == kBindFloatToBool)
+ return ClassID(bool);
+ else
+ return ClassID(float);
+}
+
+static int GetTypeTreeBindType (const TypeTree& variable)
+{
+ if (variable.m_MetaFlag & kDontAnimate)
+ return kUnbound;
+
+ if (variable.m_Type == "float")
+ return kBindFloat;
+ else if (variable.m_Type == "bool" || (variable.m_Type == "UInt8" && (variable.m_MetaFlag & kEditorDisplaysCheckBoxMask)))
+ return kBindFloatToBool;
+ else
+ return kUnbound;
+}
+
+static int GetAnimatablePropertyOffset (const TypeTree* variable, ScriptingObjectPtr scriptingInstance)
+{
+ if (variable && scriptingInstance)
+ {
+#if ENABLE_MONO
+ if (scriptingInstance)
+ {
+ UInt32 offset = reinterpret_cast<UInt8*> (variable->m_DirectPtr) - reinterpret_cast<UInt8*> (scriptingInstance);
+ UInt32 size = mono_class_instance_size(mono_object_get_class(scriptingInstance));
+ if (offset < size)
+ return offset;
+ }
+#endif
+ return -1;
+ }
+ else if (variable)
+ return variable->m_ByteOffset;
+ else
+ return -1;
+}
+
+static void GetGenericAnimatablePropertiesRecurse (const TypeTree& typeTree, std::string& path, ScriptingObjectPtr scriptingInstance, const EditorCurveBinding& baseBinding, std::vector<EditorCurveBinding>& outProperties)
+{
+ size_t previousSize = path.size();
+ if (!path.empty())
+ path += '.';
+ path += typeTree.m_Name;
+
+ int offset = GetAnimatablePropertyOffset (&typeTree, scriptingInstance);
+ if (offset != -1)
+ {
+ int bindType = GetTypeTreeBindType (typeTree);
+ if (bindType != kUnbound)
+ {
+ outProperties.push_back(baseBinding);
+ outProperties.back().attribute = path;
+ }
+ }
+
+ for (TypeTree::const_iterator i=typeTree.begin();i != typeTree.end();++i)
+ {
+ GetGenericAnimatablePropertiesRecurse (*i, path, scriptingInstance, baseBinding, outProperties);
+ }
+
+ path.resize(previousSize);
+}
+
+static void GetGenericAnimatableProperties (int classID, Object& targetObject, std::vector<EditorCurveBinding>& outProperties)
+{
+ //@TODO: Use temp TypeTree mem?
+ TypeTree typeTree;
+ GenerateTypeTree (targetObject, &typeTree);
+
+ EditorCurveBinding baseBinding;
+ baseBinding.classID = classID;
+
+ MonoBehaviour* behaviour = dynamic_pptr_cast<MonoBehaviour*> (&targetObject);
+ ScriptingObjectPtr scriptingInstance = SCRIPTING_NULL;;
+ if (behaviour)
+ {
+ scriptingInstance = behaviour->GetInstance();
+ baseBinding.script = behaviour->GetScript();
+ }
+
+ // Create cached bindings (We don't want to include the root name, so iterate over the children directly)
+ std::string path;
+ for (TypeTree::const_iterator i=typeTree.begin();i != typeTree.end();++i)
+ GetGenericAnimatablePropertiesRecurse (*i, path, scriptingInstance, baseBinding, outProperties);
+}
+
+static void GenerateBindingRecurse (const TypeTree& typeTree, ScriptingObjectPtr scriptingInstance, mecanim::crc32 attributeHash, dynamic_array<CachedBinding>& bindings)
+{
+ // Update hash recursively
+ if (attributeHash.checksum() != 0)
+ attributeHash.process_bytes(".", 1);
+ attributeHash.process_bytes(typeTree.m_Name.c_str(), strlen(typeTree.m_Name.c_str()));
+
+ int offset = GetAnimatablePropertyOffset (&typeTree, scriptingInstance);
+ if (offset != -1)
+ {
+ int bindType = GetTypeTreeBindType (typeTree);
+ if (bindType != kUnbound)
+ {
+ CachedBinding& binding = bindings.push_back();
+ binding.propertyHash = attributeHash.checksum();
+ binding.offset = offset;
+ binding.bindType = bindType;
+ }
+ }
+
+ for (TypeTree::const_iterator i=typeTree.begin();i != typeTree.end();++i)
+ GenerateBindingRecurse (*i, scriptingInstance, attributeHash, bindings);
+}
+
+
+template<class T>
+bool has_duplicate_sorted (const T* begin, size_t count)
+{
+ if (count == 0)
+ return false;
+
+ const T* previous = begin;
+ const T* i = begin;
+ const T* end = begin + count;
+ i++;
+ for (; i != end;++i)
+ {
+ if (!(*previous < *i))
+ return true;
+
+ previous = i;
+ }
+
+ return false;
+}
+
+// Find a value in a sorted array
+// Returns NULL if there is no value in the array
+template<class T>
+const T* find_binary_search (const T* begin, size_t count, const T& value)
+{
+ const T* found = std::lower_bound (begin, begin + count, value);
+ if (found == begin + count || value < *found)
+ return NULL;
+ else
+ return found;
+}
+
+
+static const CachedBinding* FindBinding (const CachedComponentBindings& componentBinding, BindingHash attribute)
+{
+ CachedBinding proxy;
+ proxy.propertyHash = attribute;
+
+ return find_binary_search(componentBinding.bindings, componentBinding.bindingSize, proxy);
+}
+
+static void DestroyBinding (CachedComponentBindings* binding)
+{
+ UNITY_FREE(kMemAnimation, binding);
+}
+
+
+static CachedComponentBindings* GenerateComponentBinding (int classID, ScriptingObjectPtr scriptingInstance, ScriptingClassPtr scriptingClass, Object* targetObject)
+{
+ //@TODO: Use temp TypeTree mem?
+ TypeTree typeTree;
+ GenerateTypeTree (*targetObject, &typeTree);
+
+ dynamic_array<CachedBinding> bindings (kMemTempAlloc);
+
+ // Create cached bindings (We don't want to include the root name, so iterate over the children directly)
+ for (TypeTree::const_iterator i=typeTree.begin();i != typeTree.end();++i)
+ GenerateBindingRecurse (*i, scriptingInstance, mecanim::crc32(), bindings);
+
+ std::sort (bindings.begin(), bindings.end());
+
+#if DEBUGMODE
+ if (has_duplicate_sorted (bindings.begin(), bindings.size()))
+ {
+ ///@TODO: make a nice fullclassname...
+
+
+ WarningString(Format("Animation bindings for %s are not unique. Some properties might get bound incorrectly.", targetObject->GetClassName().c_str()));
+ }
+#endif
+
+ size_t size = sizeof (CachedComponentBindings) + sizeof (CachedBinding) * bindings.size();
+ mecanim::memory::InPlaceAllocator allocator (UNITY_MALLOC(kMemAnimation, size), size);
+
+ CachedComponentBindings* binding = allocator.Construct<CachedComponentBindings> ();
+
+ binding->classID = classID;
+ binding->scriptingClass = scriptingClass;
+ binding->bindingSize = bindings.size();
+ binding->bindings = allocator.ConstructArray<CachedBinding> (bindings.begin(), bindings.size());
+
+ return binding;
+}
+
+void GenericAnimationBindingCache::DidReloadDomain ()
+{
+ if (gGenericBindingCache != NULL)
+ Clear(gGenericBindingCache->m_Scripts);
+}
+
+void InitializeGenericAnimationBindingCache ()
+{
+ mecanim::crc32::crc32_table_type::init_table();
+
+ gGenericBindingCache = UNITY_NEW_AS_ROOT(GenericAnimationBindingCache, kMemAnimation, "AnimationBindingCache", "");
+
+ GlobalCallbacks::Get().didReloadMonoDomain.Register(GenericAnimationBindingCache::DidReloadDomain);
+}
+
+void CleanupGenericAnimationBindingCache ()
+{
+ UNITY_DELETE(gGenericBindingCache, kMemAnimation);
+
+ GlobalCallbacks::Get().didReloadMonoDomain.Unregister(GenericAnimationBindingCache::DidReloadDomain);
+}
+
+static RegisterRuntimeInitializeAndCleanup s_RegisterBindingCache (InitializeGenericAnimationBindingCache, CleanupGenericAnimationBindingCache);
+
+GenericAnimationBindingCache& GetGenericAnimationBindingCache ()
+{
+ return *gGenericBindingCache;
+}
+
+GenericAnimationBindingCache::GenericAnimationBindingCache ()
+{
+ m_IsActiveHash = mecanim::processCRC32 (kIsActive);
+ m_Classes.resize_initialized (kLargestRuntimeClassID, NULL);
+ m_CustomBindingInterfaces.resize_initialized (kAllBindingCount, NULL);
+}
+
+GenericAnimationBindingCache::~GenericAnimationBindingCache ()
+{
+ Clear(m_Classes);
+ Clear(m_Scripts);
+}
+
+
+void CreateTransformBinding (const UnityStr& path, int bindType, GenericBinding& outputBinding)
+{
+ outputBinding.path = mecanim::processCRC32(path.c_str());
+ outputBinding.attribute = bindType;
+ outputBinding.classID = ClassID(Transform);
+ outputBinding.customType = kUnbound;
+ outputBinding.isPPtrCurve = false;
+ outputBinding.script = NULL;
+}
+
+void GenericAnimationBindingCache::CreateGenericBinding (const UnityStr& path, int classID, PPtr<MonoScript> script, const UnityStr& attribute, bool pptrCurve, GenericBinding& outputBinding) const
+{
+ outputBinding.path = mecanim::processCRC32(path.c_str());
+ outputBinding.attribute = mecanim::processCRC32(attribute.c_str());
+ outputBinding.classID = classID;
+ outputBinding.customType = kUnbound;
+ outputBinding.isPPtrCurve = pptrCurve;
+ outputBinding.script = script;
+
+ // Pre-bind muscle indices
+ //@TODO: is this really worth doing? Maybe we should just make it more consistent?
+ if (!pptrCurve)
+ {
+ if (classID == ClassID(Animator))
+ {
+ mecanim::int32_t muscleIndex = mecanim::animation::FindMuscleIndex(outputBinding.attribute);
+ if (muscleIndex != -1)
+ {
+ outputBinding.attribute = muscleIndex;
+ outputBinding.customType = kBindMuscle;
+ return;
+ }
+ }
+ }
+
+ // Search custom bindings
+ for (int i=0;i<m_CustomBindings.size();i++)
+ {
+ int customBindingType = m_CustomBindings[i].customBindingType;
+ const IAnimationBinding* bindingInterface = m_CustomBindingInterfaces[customBindingType];
+ if (Object::IsDerivedFromClassID (classID, m_CustomBindings[i].classID) && bindingInterface->GenerateBinding (attribute, pptrCurve, outputBinding))
+ {
+ outputBinding.customType = customBindingType;
+ return;
+ }
+ }
+}
+
+static inline MonoBehaviour* GetComponentWithScript (Transform& transform, PPtr<Object> target)
+{
+ MonoScript* script = dynamic_instanceID_cast<MonoScript*> (target.GetInstanceID());
+ return static_cast<MonoBehaviour*> (GetComponentWithScript (transform.GetGameObject(), ClassID(MonoBehaviour), script));
+}
+
+#if ENABLE_MONO
+
+ClassIDType GenericAnimationBindingCache::BindScript (const GenericBinding& inputBinding, Transform& transform, BoundCurve& bound)
+{
+ MonoBehaviour* behaviour = GetComponentWithScript (transform, inputBinding.script);
+
+ ScriptingObjectPtr instance = behaviour ? behaviour->GetInstance() : SCRIPTING_NULL;
+ // No valid instance -> no bound curve
+ if (instance == NULL)
+ {
+ bound.targetType = kUnbound;
+ return ClassID(Undefined);
+ }
+
+ ScriptingClassPtr klass = behaviour->GetClass();
+
+ // Find cached binding stored by ScriptingClassPtr
+ CachedComponentBindings* bindings = NULL;
+ for (int i=0;i<m_Scripts.size();i++)
+ {
+ if (m_Scripts[i]->scriptingClass == klass)
+ {
+ bindings = m_Scripts[i];
+ break;
+ }
+ }
+
+ // Create a new binding for this ScriptingClassPtr
+ if (bindings == NULL)
+ {
+ bindings = GenerateComponentBinding(inputBinding.classID, instance, klass, behaviour);
+ m_Scripts.push_back(bindings);
+ }
+
+ // Bind the specific curve
+ return BindCurve (*bindings, inputBinding, behaviour, instance, bound);
+}
+#endif
+
+ClassIDType GenericAnimationBindingCache::BindGenericComponent (const GenericBinding& inputBinding, Transform& transform, BoundCurve& bound)
+{
+ Unity::Component* target = transform.GetGameObject().QueryComponentT<Unity::Component>(inputBinding.classID);
+ if (target == NULL)
+ return ClassID(Undefined);
+
+ if (m_Classes[inputBinding.classID] == NULL)
+ m_Classes[inputBinding.classID] = GenerateComponentBinding(inputBinding.classID, SCRIPTING_NULL, SCRIPTING_NULL, target);
+
+ return BindCurve (*m_Classes[inputBinding.classID], inputBinding, target, target, bound);
+}
+
+Object* FindAnimatedObject (Unity::GameObject& root, const EditorCurveBinding& inputBinding)
+{
+ Transform* transform = FindRelativeTransformWithPath (root.GetComponent(Transform), inputBinding.path.c_str());
+ if (transform == NULL)
+ return NULL;
+
+ if (inputBinding.classID == ClassID(GameObject))
+ {
+ return &transform->GetGameObject();
+ }
+ else if (inputBinding.classID == ClassID(MonoBehaviour))
+ {
+ return GetComponentWithScript (*transform, inputBinding.script);
+ }
+ else
+ {
+ return transform->GetGameObject().QueryComponentT<Unity::Component>(inputBinding.classID);
+ }
+}
+
+ClassIDType GenericAnimationBindingCache::BindPPtrGeneric (const GenericBinding& inputBinding, Transform& transform, BoundCurve& bound)
+{
+ if (inputBinding.customType == kUnbound)
+ return ClassID(Undefined);
+
+ return BindCustom (inputBinding, transform, bound);
+}
+
+ClassIDType GenericAnimationBindingCache::BindCustom (const GenericBinding& inputBinding, Transform& transform, BoundCurve& bound) const
+{
+ Assert(inputBinding.customType != kUnbound);
+
+ Unity::Component* target;
+ if (inputBinding.classID == ClassID(MonoBehaviour))
+ target = GetComponentWithScript (transform, inputBinding.script);
+ else
+ target = transform.GetGameObject().QueryComponentT<Unity::Component>(inputBinding.classID);
+
+ IAnimationBinding* customBinding = m_CustomBindingInterfaces[inputBinding.customType];
+ if (customBinding != NULL && target != NULL)
+ {
+ BoundCurve tempBound;
+ tempBound.targetType = inputBinding.customType;
+ tempBound.customBinding = customBinding;
+ tempBound.targetObject = target;
+
+ ClassIDType type = customBinding->BindValue (*target, inputBinding, tempBound);
+ if (type != ClassID (Undefined))
+ bound = tempBound;
+
+ return type;
+ }
+
+ return ClassID(Undefined);
+}
+
+
+ClassIDType GenericAnimationBindingCache::BindGeneric (const GenericBinding& inputBinding, Transform& transform, BoundCurve& bound)
+{
+ // Bind game object active state
+ if (inputBinding.classID == ClassID(GameObject))
+ {
+ if (inputBinding.attribute == m_IsActiveHash && inputBinding.path != 0)
+ {
+ bound.targetPtr = NULL;
+ bound.targetType = kBindGameObjectActive;
+ bound.targetObject = transform.GetGameObjectPtr();
+ return ClassID(bool);
+ }
+
+ return ClassID(Undefined);
+ }
+ // Custom animator bindings
+ else if (inputBinding.classID == ClassID(Animator))
+ {
+ // We bind these in mecanim internally.
+ return ClassID(float);
+ }
+ // Custom binding
+ else if (inputBinding.customType != kUnbound)
+ return BindCustom (inputBinding, transform, bound);
+ // Script bindings
+ else if (inputBinding.classID == ClassID(MonoBehaviour))
+ {
+#if ENABLE_MONO
+ return BindScript(inputBinding, transform, bound);
+#else
+ return ClassID(Undefined);
+#endif
+ }
+ // Generic bindings
+ else
+ {
+ return BindGenericComponent(inputBinding, transform, bound);
+ }
+}
+
+void GenericAnimationBindingCache::RegisterIAnimationBinding (ClassIDType classID, int customBindingType, IAnimationBinding* customBinding)
+{
+ CustomBinding bind;
+ bind.classID = classID;
+ bind.customBindingType = customBindingType;
+ m_CustomBindings.push_back(bind);
+
+ Assert(m_CustomBindingInterfaces[customBindingType] == NULL);
+ m_CustomBindingInterfaces[customBindingType] = customBinding;
+}
+
+std::string GenericAnimationBindingCache::SerializedPropertyPathToCurveAttribute (Object& target, const char* propertyPath) const
+{
+ ClassIDType classID = target.GetClassID();
+ // Search custom bindings
+ for (int i=0;i<m_CustomBindings.size();i++)
+ {
+ if (!Object::IsDerivedFromClassID (classID, m_CustomBindings[i].classID))
+ continue;
+
+ int customBindingType = m_CustomBindings[i].customBindingType;
+ const IAnimationBinding* customBinding = m_CustomBindingInterfaces[customBindingType];
+
+ string attributeName = customBinding->SerializedPropertyPathToCurveAttribute (target, propertyPath);
+ if (!attributeName.empty())
+ return attributeName;
+ }
+
+ return string();
+}
+
+std::string GenericAnimationBindingCache::CurveAttributeToSerializedPath (Unity::GameObject& root, const EditorCurveBinding& binding) const
+{
+ Transform* transform = FindRelativeTransformWithPath (root.GetComponent(Transform), binding.path.c_str());
+ if (transform == NULL)
+ return std::string();
+
+ GenericBinding genericBinding;
+ CreateGenericBinding (binding.path, binding.classID, binding.script, binding.attribute, binding.isPPtrCurve, genericBinding);
+
+ if (genericBinding.customType == kUnbound)
+ return std::string();
+
+ BoundCurve bound;
+ if (BindCustom (genericBinding, *transform, bound) == ClassID(Undefined))
+ return std::string();
+
+ if (bound.customBinding != NULL)
+ return bound.customBinding->CurveAttributeToSerializedPath (bound);
+ else
+ return string();
+}
+
+void GenericAnimationBindingCache::Clear (CachedComponentBindingArray& array)
+{
+ for (int i=0;i<array.size();i++)
+ UNITY_FREE(kMemAnimation, array[i]);
+ array.clear();
+}
+
+int GetBoundCurveIntValue (const BoundCurve& bind)
+{
+ return bind.customBinding->GetPPtrValue (bind);
+}
+
+void SetBoundCurveIntValue (const BoundCurve& bind, int value)
+{
+ bind.customBinding->SetPPtrValue (bind, value);
+}
+
+void SetBoundCurveFloatValue (const BoundCurve& bind, float value)
+{
+ UInt32 targetType = bind.targetType;
+ Assert(bind.targetType != kUnbound && bind.targetType != kBindTransformRotation && bind.targetType != kBindTransformPosition && bind.targetType != kBindTransformScale);
+
+ if ( targetType == kBindFloat )
+ {
+ *reinterpret_cast<float*>(bind.targetPtr) = value;
+ }
+ else if ( targetType == kBindFloatToBool )
+ {
+ *reinterpret_cast<UInt8*>(bind.targetPtr) = AnimationFloatToBool(value);
+ }
+ else if ( targetType == kBindGameObjectActive)
+ {
+ GameObject* go = static_cast<GameObject*> (bind.targetObject);
+ go->SetSelfActive (AnimationFloatToBool(value));
+ }
+ else
+ bind.customBinding->SetFloatValue (bind, value);
+}
+
+float GetBoundCurveFloatValue (const BoundCurve& bind)
+{
+ UInt32 targetType = bind.targetType;
+ Assert(bind.targetType >= kMinSinglePropertyBinding);
+
+ if ( targetType == kBindFloat )
+ {
+ return *reinterpret_cast<float*>(bind.targetPtr);
+ }
+ else if ( targetType == kBindFloatToBool )
+ {
+ return AnimationBoolToFloat(*reinterpret_cast<UInt8*>(bind.targetPtr));
+ }
+ else if ( targetType == kBindGameObjectActive )
+ {
+ GameObject* go = static_cast<GameObject*> (bind.targetObject);
+ return AnimationBoolToFloat (go->IsSelfActive ());
+ }
+ else
+ return bind.customBinding->GetFloatValue (bind);
+}
+
+
+bool ShouldAwakeGeneric (const BoundCurve& bind)
+{
+ return bind.targetType == kBindFloat || bind.targetType == kBindFloatToBool;
+}
+
+void BoundCurveValueAwakeGeneric (Object& targetObject)
+{
+ targetObject.AwakeFromLoad(kDefaultAwakeFromLoad);
+ targetObject.SetDirty();
+}
+
+#if UNITY_EDITOR
+
+static void ExtractGameObjectIsActiveBindings (std::vector<EditorCurveBinding>& outProperties)
+{
+ AddBinding (outProperties, ClassID(GameObject), kIsActive);
+}
+
+static void ExtractAllAnimatorBindings (Unity::Component& targetObject, vector<EditorCurveBinding>& attributes)
+{
+ Animator& animator = static_cast<Animator&> (targetObject);
+ if (animator.IsHuman())
+ {
+ for(int curveIter = 0; curveIter < mecanim::animation::s_ClipMuscleCurveCount; curveIter++)
+ AddBinding (attributes, ClassID(Animator), mecanim::animation::GetMuscleCurveName(curveIter).c_str());
+ }
+
+ if(animator.GetAnimatorController())
+ {
+ int eventCount = animator.GetAnimatorController()->GetParameterCount();
+
+ for(int eventIter = 0; eventIter < eventCount; eventIter++)
+ {
+ AnimatorControllerParameter* parameter = animator.GetAnimatorController()->GetParameter(eventIter);
+ if (parameter->GetType() == 1)
+ AddBinding (attributes, ClassID(Animator), parameter->GetName());
+ }
+ }
+}
+
+static void ProcessRelativePath (Unity::GameObject& gameObject, Unity::GameObject& root, std::vector<EditorCurveBinding>& outProperties)
+{
+ string path = CalculateTransformPath (gameObject.GetComponent(Transform), root.QueryComponent(Transform));
+ for (int i=0;i<outProperties.size();i++)
+ outProperties[i].path = path;
+}
+
+void GenericAnimationBindingCache::GetAllAnimatableProperties (Unity::GameObject& go, Unity::GameObject& root, std::vector<EditorCurveBinding>& outProperties)
+{
+ Assert(outProperties.empty());
+
+ if (&go != &root)
+ ExtractGameObjectIsActiveBindings(outProperties);
+
+ for (int i=0;i<go.GetComponentCount ();i++)
+ {
+ Unity::Component& com = go.GetComponentAtIndex (i);
+ int classID = com.GetClassID();
+
+ // Search custom bindings
+ for (int c=0;c<m_CustomBindings.size();c++)
+ {
+ int customBindingType = m_CustomBindings[c].customBindingType;
+ if (Object::IsDerivedFromClassID (classID, m_CustomBindings[c].classID))
+ m_CustomBindingInterfaces[customBindingType]->GetAllAnimatableProperties (com, outProperties);
+ }
+
+ if (classID == ClassID(Animator))
+ ExtractAllAnimatorBindings (com, outProperties);
+
+ GetGenericAnimatableProperties (classID, go.GetComponentAtIndex(i), outProperties);
+ }
+
+ ProcessRelativePath (go, root, outProperties);
+}
+
+static bool GetFloatValueAnimatorBinding (Transform& transform, const EditorCurveBinding& binding, float* value)
+{
+ Animator* animator = transform.QueryComponent(Animator);
+
+ if (animator == NULL)
+ return false;
+
+ BindingHash hash = mecanim::processCRC32 (binding.attribute.c_str());
+
+ if(animator->GetMuscleValue(hash,value))
+ return true;
+
+ GetSetValueResult result = animator->GetFloat (hash, *value);
+ return result == kGetSetSuccess;
+}
+
+static bool GetFloatValueTransformBinding (Transform& transform, const EditorCurveBinding& binding, float* value)
+{
+ int axis = -1;
+ char lastCharacter = binding.attribute[binding.attribute.size()-1];
+ if (lastCharacter == 'w')
+ axis = 3;
+ else if (lastCharacter >= 'x' && lastCharacter <= 'z')
+ axis = lastCharacter - 'x';
+ else
+ return false;
+
+ if (BeginsWith(binding.attribute, "m_LocalPosition") && axis < 3)
+ {
+ *value = transform.GetLocalPosition()[axis];
+ return true;
+ }
+ else if (BeginsWith(binding.attribute, "m_LocalScale") && axis < 3)
+ {
+ *value = transform.GetLocalScale()[axis];
+ return true;
+ }
+ else if (BeginsWith(binding.attribute, "m_LocalRotation") && axis < 4)
+ {
+ *value = transform.GetLocalRotation()[axis];
+ return true;
+ }
+ else if (BeginsWith(binding.attribute, "localEulerAngles") && axis < 3)
+ {
+ *value = transform.GetLocalEulerAngles()[axis];
+ return true;
+ }
+ else
+ return false;
+}
+
+ClassIDType GetFloatValue (Unity::GameObject& root, const EditorCurveBinding& binding, float* value)
+{
+ *value = 0.0F;
+
+ if (binding.isPPtrCurve)
+ return ClassID(Undefined);
+
+ Transform* transform = FindRelativeTransformWithPath (root.GetComponent(Transform), binding.path.c_str());
+ if (transform == NULL)
+ return ClassID(Undefined);
+
+ // Transform has a special codepath for setting T/R/S as Vector3
+ if (binding.classID == ClassID(Transform))
+ {
+ if (GetFloatValueTransformBinding (*transform, binding, value))
+ return ClassID(float);
+ }
+ // Animator bindings are handled through a special more integrated code path in mecanim
+ else if (binding.classID == ClassID(Animator))
+ {
+ if (GetFloatValueAnimatorBinding (*transform, binding, value))
+ return ClassID(float);
+ }
+ else
+ {
+ GenericBinding genericBinding;
+ GetGenericAnimationBindingCache().CreateGenericBinding (binding.path, binding.classID, binding.script, binding.attribute, binding.isPPtrCurve, genericBinding);
+
+ BoundCurve bound;
+ ClassIDType bindType = GetGenericAnimationBindingCache().BindGeneric (genericBinding, *transform, bound);
+ if (bound.targetType == kUnbound)
+ return ClassID(Undefined);
+
+ *value = GetBoundCurveFloatValue (bound);
+
+ return bindType;
+ }
+
+ return ClassID(Undefined);
+}
+
+ClassIDType GetPPtrValue (Unity::GameObject& root, const EditorCurveBinding& binding, int* instanceID)
+{
+ *instanceID = 0;
+
+ if (!binding.isPPtrCurve)
+ return ClassID(Undefined);
+
+ Transform* transform = FindRelativeTransformWithPath (root.GetComponent(Transform), binding.path.c_str());
+ if (transform == NULL)
+ return ClassID(Undefined);
+
+ GenericBinding genericBinding;
+ GetGenericAnimationBindingCache().CreateGenericBinding (binding.path, binding.classID, binding.script, binding.attribute, binding.isPPtrCurve, genericBinding);
+
+ BoundCurve bound;
+ ClassIDType boundClassID = GetGenericAnimationBindingCache().BindPPtrGeneric (genericBinding, *transform, bound);
+ if (bound.targetType == kUnbound)
+ return ClassID(Undefined);
+
+ *instanceID = GetBoundCurveIntValue (bound);
+ return boundClassID;
+}
+
+ClassIDType GetEditorCurveValueClassID (Unity::GameObject& root, const EditorCurveBinding& binding)
+{
+ // Try if it's a PPtr curve
+ int tempInstanceID;
+ ClassIDType boundClassID = GetPPtrValue (root, binding, &tempInstanceID);
+ if (boundClassID != ClassID(Undefined))
+ return boundClassID;
+
+ // Otherwise find the type of float curve it is
+ float tempFloat;
+ return GetFloatValue (root, binding, &tempFloat);
+}
+
+#endif
+
+}
+}
diff --git a/Runtime/Animation/GenericAnimationBindingCache.h b/Runtime/Animation/GenericAnimationBindingCache.h
new file mode 100644
index 0000000..38fba09
--- /dev/null
+++ b/Runtime/Animation/GenericAnimationBindingCache.h
@@ -0,0 +1,118 @@
+#pragma once
+
+#include "Runtime/Utilities/dynamic_array.h"
+#include "EditorCurveBinding.h"
+#include "Runtime/BaseClasses/ClassIDs.h"
+#include "BoundCurve.h"
+
+typedef UInt32 BindingHash;
+
+namespace Unity { class GameObject; }
+class Transform;
+class Object;
+class MonoScript;
+template<class T> class PPtr;
+
+class IAnimationBinding;
+
+namespace UnityEngine
+{
+namespace Animation
+{
+
+ struct CachedComponentBindings;
+ struct GenericBinding;
+
+ bool IsMuscleBinding (const GenericBinding& binding);
+
+ inline bool AnimationFloatToBool (float result)
+ {
+ //@TODO: Maybe we should change the behaviour to be that > 0.01F means enabled instead of the close to zero logic....
+ return result > 0.001F || result < -0.001F;
+ }
+
+ inline float AnimationBoolToFloat (bool value)
+ {
+ return value ? 1.0F : 0.0F;
+ }
+
+ class GenericAnimationBindingCache
+ {
+ public:
+
+ GenericAnimationBindingCache ();
+ ~GenericAnimationBindingCache ();
+
+ ClassIDType BindGeneric (const GenericBinding& inputBinding, Transform& transform, BoundCurve& bound);
+ ClassIDType BindPPtrGeneric (const GenericBinding& inputBinding, Transform& transform, BoundCurve& bound);
+
+ void CreateGenericBinding (const UnityStr& path, int classID, PPtr<MonoScript> script, const UnityStr& attribute, bool pptrCurve, GenericBinding& outputBinding) const;
+
+ static void DidReloadDomain ();
+
+ void RegisterIAnimationBinding (ClassIDType classID, int inCustomType, IAnimationBinding* bindingInterface);
+
+ // Editor API
+ void GetAnimatableProperties (Transform& transform, int classID, const PPtr<MonoScript>& script, std::vector<EditorCurveBinding>& outProperties);
+ void GetAllAnimatableProperties (Unity::GameObject& gameObject, Unity::GameObject& root, std::vector<EditorCurveBinding>& outProperties);
+
+ std::string SerializedPropertyPathToCurveAttribute (Object& target, const char* propertyPath) const;
+ std::string CurveAttributeToSerializedPath (Unity::GameObject& root, const EditorCurveBinding& binding) const;
+
+ private:
+
+ typedef dynamic_array<CachedComponentBindings*> CachedComponentBindingArray;
+
+ ClassIDType BindGenericComponent (const GenericBinding& inputBinding, Transform& transform, BoundCurve& bound);
+ ClassIDType BindScript (const GenericBinding& inputBinding, Transform& transform, BoundCurve& bound);
+ ClassIDType BindCustom (const GenericBinding& inputBinding, Transform& transform, BoundCurve& bound) const;
+
+ static void Clear (CachedComponentBindingArray& array);
+
+ struct CustomBinding
+ {
+ int classID;
+ int customBindingType;
+ };
+
+ dynamic_array<CustomBinding> m_CustomBindings;
+ dynamic_array<IAnimationBinding*> m_CustomBindingInterfaces;
+
+ CachedComponentBindingArray m_Classes;
+ CachedComponentBindingArray m_Scripts;
+ BindingHash m_IsActiveHash;
+ };
+
+ GenericAnimationBindingCache& GetGenericAnimationBindingCache ();
+
+ void InitializeGenericAnimationBindingCache ();
+ void CleanupGenericAnimationBindingCache ();
+
+ // Runtime API
+ // NOt sure if i like this???
+// void CreateGenericBinding (const UnityStr& path, int classID, PPtr<MonoScript> script, const UnityStr& attribute, bool pptrCurve, GenericBinding& outputBinding);
+ void CreateTransformBinding (const UnityStr& path, int bindType, GenericBinding& outputBinding);
+
+ float GetBoundCurveFloatValue (const BoundCurve& bind);
+ void SetBoundCurveFloatValue (const BoundCurve& bind, float value);
+
+ void SetBoundCurveIntValue (const BoundCurve& bind, int value);
+ int GetBoundCurveIntValue (const BoundCurve& bind);
+
+ void BoundCurveValueAwakeGeneric (Object& targetObject);
+ bool ShouldAwakeGeneric (const BoundCurve& bind);
+
+#if UNITY_EDITOR
+ // Editor API.
+ // Returns the ClassID of the bound value. (ClassID(Undefined) if it could not be bound)
+ ClassIDType GetFloatValue (Unity::GameObject& root, const EditorCurveBinding& binding, float* value);
+ ClassIDType GetPPtrValue (Unity::GameObject& root, const EditorCurveBinding& binding, int* instanceID);
+
+ bool BindEditorCurve (Unity::GameObject& root, const EditorCurveBinding& binding, BoundCurve& boundCurve);
+
+ Object* FindAnimatedObject (Unity::GameObject& root, const EditorCurveBinding& inputBinding);
+
+ ClassIDType GetEditorCurveValueClassID (Unity::GameObject& root, const EditorCurveBinding& binding);
+#endif
+}
+}
diff --git a/Runtime/Animation/KeyframeReducer.cpp b/Runtime/Animation/KeyframeReducer.cpp
new file mode 100644
index 0000000..ac3650e
--- /dev/null
+++ b/Runtime/Animation/KeyframeReducer.cpp
@@ -0,0 +1,359 @@
+#include "UnityPrefix.h"
+#include "KeyframeReducer.h"
+#include "AnimationClip.h"
+#include "Runtime/Math/Quaternion.h"
+#include "AnimationCurveUtility.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Animation/MecanimClipBuilder.h"
+#include <sstream>
+#include <vector>
+
+using namespace std;
+
+#define DEBUG_COMPRESSION 0
+
+const float kPositionMinValue = 0.00001F;
+const float kQuaternionNormalizationError = 0.001F;/// The sampled quaternion must be almost normalized.
+
+/// - We allow reduction if the reduced magnitude doesn't go off very far
+/// - And the angle between the two rotations is similar
+inline bool QuaternionDistanceError (Quaternionf value, Quaternionf reduced, const float quaternionDotError)
+{
+ float magnitude = Magnitude(reduced);
+ if (!CompareApproximately(1.0F, magnitude, kQuaternionNormalizationError))
+ {
+ return false;
+ }
+
+ value = NormalizeSafe(value);
+ reduced = reduced / magnitude;
+
+ // float angle = Rad2Deg(acos (Dot(value, reduced))) * 2.0F;
+ // if (dot > kQuaternionAngleError)
+ // return false;
+ if (Dot(value, reduced) < quaternionDotError)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool DeltaError(const float value, const float reducedValue, const float delta, const float percentage, const float minValue)
+{
+ const float absValue = Abs(value);
+ // (absValue > minValue || Abs(reducedValue) > minValue) part is necessary for reducing values which have tiny fluctuations around 0
+ return (absValue > minValue || Abs(reducedValue) > minValue) && (delta > absValue * percentage);
+}
+
+/// We allow reduction
+// - the distance of the two vectors is low
+// - the distance of each axis is low
+inline bool PositionDistanceError (Vector3f value, Vector3f reduced, const float distancePercentageError)
+{
+ const float percentage = distancePercentageError;
+ const float minValue = kPositionMinValue * percentage;
+
+ // Vector3 distance as a percentage
+ float distance = SqrMagnitude(value - reduced);
+ float length = SqrMagnitude(value);
+ float lengthReduced = SqrMagnitude(reduced);
+ //if (distance > length * Sqr(percentage))
+ if (DeltaError(length, lengthReduced, distance, Sqr(percentage), Sqr(minValue)))
+ return false;
+
+ // Distance of each axis
+ float distanceX = Abs(value.x - reduced.x);
+ float distanceY = Abs(value.y - reduced.y);
+ float distanceZ = Abs(value.z - reduced.z);
+
+ //if (distanceX > Abs(value.x) * percentage)
+ if (DeltaError(value.x, reduced.x, distanceX, percentage, minValue))
+ return false;
+ //if (distanceY > Abs(value.y) * percentage)
+ if (DeltaError(value.y, reduced.y, distanceY, percentage, minValue))
+ return false;
+ //if (distanceZ > Abs(value.z) * percentage)
+ if (DeltaError(value.z, reduced.z, distanceZ, percentage, minValue))
+ return false;
+
+ return true;
+}
+
+/// We allow reduction if the distance between the two values is low
+inline bool FloatDistanceError (float value, float reduced, const float distancePercentageError)
+{
+ const float percentage = distancePercentageError;
+ const float minValue = kPositionMinValue * percentage;
+
+ float distance = Abs(value - reduced);
+ //if (distance > Abs(value) * percentage)
+ if (DeltaError(value, reduced, distance, percentage, minValue))
+ return false;
+
+ return true;
+}
+
+// Checks if reduced curve is valid at time "time"
+template<class T, class ErrorFunction>
+bool CanReduce(AnimationCurveTpl<T>& curve, const KeyframeTpl<T>& key0, const KeyframeTpl<T>& key1, float time, ErrorFunction canReduceFunction, const float allowedError)
+{
+ T value = curve.Evaluate(time);
+ T reduced_value = InterpolateKeyframe(key0, key1, time);
+
+ return canReduceFunction(value, reduced_value, allowedError);
+}
+
+/*template<class T>
+void FitTangentsToCurve(AnimationCurveTpl<T>& curve, KeyframeTpl<T>& key0, KeyframeTpl<T>& key1)
+{
+ // perform curve fitting
+
+ const float t0 = key0.time;
+ const float dt = key1.time - key0.time;
+
+ float time1 = 0.3f;
+ float time2 = 1 - time1;
+
+ // points on the curve at time1 and time2
+ const T v0 = curve.Evaluate(t0 + time1 * dt);
+ const T v1 = curve.Evaluate(t0 + time2 * dt);
+
+ FitTangents(key0, key1, time1, time2, v0, v1);
+}*/
+
+template<class T, class ErrorFunction>
+bool CanReduce(const KeyframeTpl<T>& fromKey, const KeyframeTpl<T>& toKey, AnimationCurveTpl<T>& curve, ErrorFunction canReduceFunction, const float allowedError, const float delta,
+ int firstKey, int lastKey, bool useKeyframeLimit)
+{
+ const float beginTime = fromKey.time;
+ const float endTime = toKey.time;
+
+ // We simply sample every frame and compare the original curve against, if we simply removed one key.
+ // If the error between the curves is not too big, we just remove the key
+ bool canReduce = true;
+
+ for (float t = beginTime + delta; t < endTime; t += delta)
+ {
+ if (!(canReduce = CanReduce(curve, fromKey, toKey, t, canReduceFunction, allowedError)))
+ break;
+ }
+
+ // we need to check that all keys can be reduced, because keys might be closer to each other than delta
+ // this happens when we have steps in curve
+ // TODO : we could skip the loop above if keyframes are close enough
+
+ float lastTime = beginTime;
+
+ for (int j = firstKey; canReduce && j < lastKey; ++j)
+ {
+ const float time = curve.GetKey(j).time;
+
+ // validates point at keyframe (j) and point between (j) and and (j-1) keyframes
+ // TODO : For checking point at "time" it could just use keys[j].value instead - that would be faster than sampling the curve
+ canReduce =
+ CanReduce(curve, fromKey, toKey, time, canReduceFunction, allowedError) &&
+ CanReduce(curve, fromKey, toKey, (lastTime + time) / 2, canReduceFunction, allowedError);
+
+ lastTime = time;
+ }
+
+ if (canReduce)
+ {
+ // validate point between last two keyframes
+ float time = curve.GetKey(lastKey).time;
+ canReduce = CanReduce(curve, fromKey, toKey, (lastTime + time) / 2, canReduceFunction, allowedError);
+ }
+
+ // Don't reduce if we are about to reduce more than 50 samples at
+ // once to prevent n^2 performance impact
+ canReduce = canReduce && (!useKeyframeLimit || (endTime - beginTime < 50.0F * delta));
+
+ return canReduce;
+}
+
+template<class T, class ErrorFunction>
+float ReduceKeyframes (AnimationCurveTpl<T>& curve, float sampleRate, ErrorFunction canReduceFunction, const float allowedError, T zeroValue, bool optimalCurveRepresentation)
+{
+ AssertMsg(curve.GetKeyCount() >= 2, "Key count: %d", curve.GetKeyCount());
+
+ if (curve.GetKeyCount() <= 2)
+ return 100.0F;
+
+ dynamic_array<typename AnimationCurveTpl<T>::Keyframe> output;
+ output.reserve(curve.GetKeyCount());
+
+ float delta = 1.f / sampleRate;
+
+ // at first try to reduce to const curve
+ typename AnimationCurveTpl<T>::Keyframe firstKey = curve.GetKey(0);
+ typename AnimationCurveTpl<T>::Keyframe lastKey = curve.GetKey(curve.GetKeyCount() - 1);
+ firstKey.inSlope = firstKey.outSlope = zeroValue;
+ lastKey.inSlope = lastKey.outSlope = zeroValue;
+ lastKey.value = firstKey.value;
+
+ const bool canReduceToConstCurve = CanReduce(firstKey, lastKey, curve, canReduceFunction, allowedError, delta, 0, curve.GetKeyCount() - 1, false);
+ if (canReduceToConstCurve)
+ {
+ output.reserve(2);
+ output.push_back(firstKey);
+ output.push_back(lastKey);
+ }
+ else
+ {
+ output.reserve(curve.GetKeyCount());
+ // We always add the first key
+ output.push_back(curve.GetKey(0));
+
+ int lastUsedKey = 0;
+
+ for (int i=1;i<curve.GetKeyCount() - 1;i++)
+ {
+ typename AnimationCurveTpl<T>::Keyframe fromKey = curve.GetKey(lastUsedKey);
+ typename AnimationCurveTpl<T>::Keyframe toKey = curve.GetKey(i + 1);
+
+ //FitTangentsToCurve(curve, fromKey, toKey);
+
+ const bool canReduce = CanReduce(fromKey, toKey, curve, canReduceFunction, allowedError, delta, lastUsedKey + 1, i + 1, true);
+
+ if (!canReduce)
+ {
+ output.push_back(curve.GetKey(i));
+ // fitting tangents between last two keys
+ //FitTangentsToCurve(curve, *(output.end() - 2), output.back());
+
+ lastUsedKey = i;
+ }
+ }
+
+ // We always add the last key
+ output.push_back(curve.GetKey(curve.GetKeyCount() - 1));
+ // fitting tangents between last and the one before last keys
+ //FitTangentsToCurve(curve, *(output.end() - 2), output.back());
+ }
+
+ float reduction = (float)output.size() / (float)curve.GetKeyCount() * 100.0F;
+
+ curve.Swap(output);
+
+ // if we want optimal curve representation and reduced curve can be represeted with a dense curve
+ // keep original curve for better quality sampling
+ if (optimalCurveRepresentation && IsDenseCurve(curve))
+ {
+ curve.Swap(output);
+ reduction = 100.0F;
+ }
+
+ return reduction;
+}
+
+template <class T, class U, typename ReductionFunction>
+float ReduceKeyframes (const float sampleRate, T& curves, ReductionFunction reductionFunction, const float allowedError, const U zeroValue, bool optimalCurveRepresentation)
+{
+ float totalRatio = 0;
+ for (typename T::iterator it = curves.begin(), end = curves.end(); it != end; ++it)
+ {
+ float compressionRatio = ReduceKeyframes(it->curve, sampleRate, reductionFunction, allowedError, zeroValue, optimalCurveRepresentation);
+ #if DEBUG_COMPRESSION
+ printf_console ("Compression %f%% of rotation %s\n", compressionRatio, i->path.c_str());
+ #endif
+
+ totalRatio += compressionRatio;
+ }
+
+ return totalRatio;
+}
+
+void ReduceKeyframes (AnimationClip& clip, float rotationError, float positionError, float scaleError, float floatError)
+{
+ AnimationClip::QuaternionCurves& rot = clip.GetRotationCurves();
+ AnimationClip::Vector3Curves& pos = clip.GetPositionCurves();
+ AnimationClip::Vector3Curves& scale = clip.GetScaleCurves();
+ AnimationClip::FloatCurves& floats = clip.GetFloatCurves();
+ AnimationClip::FloatCurves& editorCurves = clip.GetEditorCurvesNoConversion();
+
+ rotationError = cos(Deg2Rad(rotationError) / 2.0F);
+ positionError = positionError / 100.0F;
+ scaleError = scaleError / 100.0F;
+ floatError = floatError / 100.0F;
+
+ float time = GetTimeSinceStartup();
+ float averageCompressionRatio = 0;
+
+ const float sampleRate = clip.GetSampleRate();
+
+ averageCompressionRatio += ReduceKeyframes (sampleRate, rot, QuaternionDistanceError, rotationError, Quaternionf(0, 0, 0, 0), clip.IsAnimatorMotion() && !clip.GetUseHighQualityCurve() );
+ averageCompressionRatio += ReduceKeyframes (sampleRate, pos, PositionDistanceError, positionError, Vector3f::zero, clip.IsAnimatorMotion() && !clip.GetUseHighQualityCurve());
+ averageCompressionRatio += ReduceKeyframes (sampleRate, scale, PositionDistanceError, scaleError, Vector3f::zero, clip.IsAnimatorMotion() && !clip.GetUseHighQualityCurve());
+ averageCompressionRatio += ReduceKeyframes (sampleRate, floats, FloatDistanceError, floatError, 0.f, clip.IsAnimatorMotion() && !clip.GetUseHighQualityCurve());
+ averageCompressionRatio += ReduceKeyframes (sampleRate, editorCurves, FloatDistanceError, floatError, 0.f, clip.IsAnimatorMotion() && !clip.GetUseHighQualityCurve());
+
+ // If all the curves are empty, we end up with zero reduction.
+ if (averageCompressionRatio == 0)
+ return;
+
+ averageCompressionRatio /= (rot.size() + pos.size() + scale.size() + floats.size() + editorCurves.size());
+ time = GetTimeSinceStartup() - time;
+
+ {
+ std::ostringstream oss;
+ oss << "Keyframe reduction: Ratio: " << averageCompressionRatio << "%; Time: " << time << "s;\n";
+ printf_console("%s", oss.str().c_str());
+ }
+}
+
+// TODO : use tangents from editor or perform curve fitting
+// TODO : these should be removed eventually and custom settings should be used instead
+const float kQuaternionAngleError = 0.5F;// The maximum angle deviation allowed in degrees
+const float kQuaternionDotError = cos(Deg2Rad(kQuaternionAngleError) / 2.0F);
+///@TODO: * Support step curves
+/// * Improve keyframe reduction
+/// * Make keyframe reduction faster
+void EulerToQuaternionCurveBake (const AnimationCurve& curveX, const AnimationCurve& curveY, const AnimationCurve& curveZ, AnimationCurveQuat& collapsed, float sampleRate)
+{
+ float begin = std::numeric_limits<float>::infinity ();
+ float end = -std::numeric_limits<float>::infinity ();
+
+ float delta = 1.0F / sampleRate;
+
+ const AnimationCurve* curves[3] = { &curveX, &curveY, &curveZ };
+
+ for (int i=0;i<3;i++)
+ {
+ if (curves[i]->GetKeyCount() >= 1)
+ {
+ begin = min(curves[i]->GetKey(0).time, begin);
+ end = max(curves[i]->GetKey(curves[i]->GetKeyCount()-1).time, end);
+ }
+ }
+ if (!IsFinite(begin) || !IsFinite(end))
+ return;
+
+
+ float deg2rad = Deg2Rad(1.0F);
+ for (float i=begin;i < end + delta;i += delta)
+ {
+ if (i + delta / 2.0F > end)
+ i = end;
+
+ Vector3f euler = Vector3f (curveX.Evaluate(i), curveY.Evaluate(i), curveZ.Evaluate(i)) * deg2rad;
+ Quaternionf q = EulerToQuaternion(euler);
+ //Vector3f eulerBack = QuaternionToEuler (q) * Rad2Deg(1.0F);
+ //printf_console("%f : %f, %f, %f ----- %f, %f, %f\n", i, euler.x, euler.y, euler.z, eulerBack.x, eulerBack.y, eulerBack.z);
+
+ KeyframeTpl<Quaternionf> key;
+ key.time = i;
+ key.value = q;
+ key.inSlope = key.outSlope = Quaternionf(0,0,0,0);
+ collapsed.AddKeyBackFast(key);
+
+ if (i == end)
+ break;
+ }
+
+ EnsureQuaternionContinuityAndRecalculateSlope (collapsed);
+
+ //@TODO: Keyframe reduction is disabled for now, with keyframe reduction enabled iteration time
+ // in the animation window becomes unbearable
+ // ReduceKeyframes(collapsed, sampleRate, QuaternionDistanceError, kQuaternionDotError, Quaternionf(0, 0, 0, 0), false);
+}
diff --git a/Runtime/Animation/KeyframeReducer.h b/Runtime/Animation/KeyframeReducer.h
new file mode 100644
index 0000000..5fb6949
--- /dev/null
+++ b/Runtime/Animation/KeyframeReducer.h
@@ -0,0 +1,10 @@
+#pragma once
+
+class AnimationClip;
+#include "Runtime/Math/AnimationCurve.h"
+
+// Rotation error is defined as maximum angle deviation allowed in degrees
+// For others it is defined as maximum distance/delta deviation allowed in percents
+void ReduceKeyframes (AnimationClip& clip, float rotationError, float positionError, float scaleError, float floatError);
+
+void EulerToQuaternionCurveBake (const AnimationCurve& curveX, const AnimationCurve& curveY, const AnimationCurve& curveZ, AnimationCurveQuat& collapsed, float sampleRate);
diff --git a/Runtime/Animation/MecanimAnimation.cpp b/Runtime/Animation/MecanimAnimation.cpp
new file mode 100644
index 0000000..8d8ca20
--- /dev/null
+++ b/Runtime/Animation/MecanimAnimation.cpp
@@ -0,0 +1,78 @@
+#include "UnityPrefix.h"
+
+#include "MecanimAnimation.h"
+
+#include "Animator.h"
+#include "Avatar.h"
+#include "CalculateAnimatorSkinMatrices.h"
+#include "Runtime/mecanim/animation/avatar.h"
+#include "Runtime/mecanim/skeleton/skeleton.h"
+
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Utilities/Word.h"
+
+void MecanimAnimation::InitializeClass ()
+{
+ SetAnimationInterface(new MecanimAnimation());
+}
+
+void MecanimAnimation::CleanupClass ()
+{
+ MecanimAnimation* animation = reinterpret_cast<MecanimAnimation*>(GetAnimationInterface ());
+ delete animation;
+ SetAnimationInterface(NULL);
+}
+
+const void* MecanimAnimation::GetGlobalSpaceSkeletonPose(const Unity::Component& animatorComponent)
+{
+ const Animator& animator = static_cast<const Animator&>(animatorComponent);
+ return animator.GetGlobalSpaceSkeletonPose();
+}
+
+CalculateAnimatorSkinMatricesFunc MecanimAnimation::GetCalculateAnimatorSkinMatricesFunc()
+{
+ return DoCalculateAnimatorSkinMatrices;
+}
+
+bool MecanimAnimation::CalculateWorldSpaceMatricesMainThread(Unity::Component& animatorComponent, const UInt16* indices, size_t count, Matrix4x4f* outMatrices)
+{
+ Animator& animator = static_cast<Animator&>(animatorComponent);
+ AssertIf(animator.GetHasTransformHierarchy());
+
+ return CalculateWordSpaceMatrices(&animator, indices, outMatrices, count);
+}
+
+bool MecanimAnimation::PathHashesToIndices(Unity::Component& animatorComponent, const BindingHash* bonePathHashes, size_t count, UInt16* outIndices)
+{
+ Animator& animator = static_cast<Animator&>(animatorComponent);
+ if (animator.GetHasTransformHierarchy())
+ return false;
+
+ const mecanim::animation::AvatarConstant* avatarConstant = animator.GetAvatarConstant();
+ if (!avatarConstant)
+ return false;
+
+ const mecanim::skeleton::Skeleton* skel = avatarConstant->m_AvatarSkeleton.Get();
+ if (!skel)
+ return false;
+
+ bool doMatchSkeleton = true;
+ for (int i = 0; i < count && doMatchSkeleton; i++)
+ {
+ int skeletonIndex = mecanim::skeleton::SkeletonFindNode(skel, bonePathHashes[i]);
+ doMatchSkeleton = (skeletonIndex != -1);
+ outIndices[i] = (UInt16)skeletonIndex;
+ }
+
+ if (!doMatchSkeleton)
+ {
+ const Avatar* avatar = animator.GetAvatar();
+ Assert(avatar);
+ ErrorStringObject(Format("The input bones do not match the skeleton of the Avatar(%s).\n"
+ "Please check if the Avatar is generated in optimized mode, or if the Avatar is valid for the attached SkinnedMeshRenderer.",
+ avatar->GetName()).c_str(),
+ avatar);
+ }
+
+ return doMatchSkeleton;
+}
diff --git a/Runtime/Animation/MecanimAnimation.h b/Runtime/Animation/MecanimAnimation.h
new file mode 100644
index 0000000..aea8190
--- /dev/null
+++ b/Runtime/Animation/MecanimAnimation.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "Runtime/Interfaces/IAnimation.h"
+
+
+class MecanimAnimation : public IAnimation
+{
+public:
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ MecanimAnimation() {}
+ ~MecanimAnimation() {}
+
+ virtual const void* GetGlobalSpaceSkeletonPose(const Unity::Component& animator);
+
+ virtual bool CalculateWorldSpaceMatricesMainThread(Unity::Component& animator, const UInt16* indices, size_t count, Matrix4x4f* outMatrices);
+
+ virtual CalculateAnimatorSkinMatricesFunc GetCalculateAnimatorSkinMatricesFunc();
+
+ virtual bool PathHashesToIndices(Unity::Component& animator, const BindingHash* bonePathHashes, size_t count, UInt16* outIndices);
+
+}; \ No newline at end of file
diff --git a/Runtime/Animation/MecanimArraySerialization.h b/Runtime/Animation/MecanimArraySerialization.h
new file mode 100644
index 0000000..204e134
--- /dev/null
+++ b/Runtime/Animation/MecanimArraySerialization.h
@@ -0,0 +1,178 @@
+#pragma once
+
+#include "Runtime/mecanim/memory.h"
+
+template<typename T, int SIZE>
+struct StaticArrayTransfer
+{
+ enum
+ {
+ m_ArraySize = SIZE
+ };
+
+ size_t m_Size;
+
+ typedef T* iterator;
+ typedef T value_type;
+
+ T (&m_Data)[SIZE];
+
+ StaticArrayTransfer (T (&data)[SIZE]):m_Data(data),m_Size(m_ArraySize)
+ {
+ }
+
+ void reserve(size_t size)
+ {
+ m_Size = std::min<size_t>(size, m_ArraySize);
+ }
+
+ iterator begin () { return &m_Data[0]; }
+ iterator end () { return &m_Data[m_Size]; }
+ size_t size() { return m_Size; }
+};
+
+template<typename T, int SIZE>
+class SerializeTraits< StaticArrayTransfer<T, SIZE> > : public SerializeTraitsBase< StaticArrayTransfer<T, SIZE> >
+{
+public:
+
+ DEFINE_GET_TYPESTRING_CONTAINER (staticvector)
+
+ typedef StaticArrayTransfer<T, SIZE> value_type;
+
+ static size_t GetAlignOf() {return ALIGN_OF(T);}
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ transfer.TransferSTLStyleArray (data);
+ }
+
+ static bool IsContinousMemoryArray () { return true; }
+ static void ResizeSTLStyleArray (value_type& data, int rs)
+ {
+ data.reserve(rs);
+ }
+
+ static void resource_image_assign_external (value_type& data, void* begin, void* end)
+ {
+ }
+};
+
+#define STATIC_ARRAY_TRANSFER(TYPE,DATA,SIZE) StaticArrayTransfer<TYPE, SIZE> DATA##ArrayTransfer (DATA); transfer.Transfer(DATA##ArrayTransfer, #DATA);
+
+
+template<class T, class TransferFunction>
+struct ManualArrayTransfer
+{
+ typedef T* iterator;
+ typedef T value_type;
+
+ T** m_Data;
+ mecanim::uint32_t* m_ArraySize;
+ void* m_Allocator;
+ TransferFunction& m_Transfer;
+
+ ManualArrayTransfer (T*& data, mecanim::uint32_t& size, void* allocator, TransferFunction& transfer):m_Transfer(transfer)
+ {
+ m_Allocator = allocator;
+ m_Data = &data;
+ m_ArraySize = &size;
+ }
+
+ T* begin () { return *m_Data; }
+ T* end () { return *m_Data + *m_ArraySize; }
+ size_t size() { return *m_ArraySize; }
+
+ void resize (int size)
+ {
+ if(m_Transfer.IsReading() || m_Transfer.IsWriting() || m_Transfer.IsRemapPPtrTransfer())
+ {
+ mecanim::memory::ChainedAllocator* allocator = static_cast<mecanim::memory::ChainedAllocator*> (m_Allocator);
+ Assert(allocator != NULL);
+
+ *m_Data = allocator->ConstructArray<T> (size);
+ *m_ArraySize = size;
+ }
+ }
+};
+
+template<class T, class TransferFunction>
+struct ManualArrayTransfer<T*, TransferFunction>
+{
+ typedef T** iterator;
+ typedef T* value_type;
+
+ value_type** m_Data;
+ mecanim::uint32_t* m_ArraySize;
+ void* m_Allocator;
+ TransferFunction& m_Transfer;
+
+ ManualArrayTransfer (value_type *& data, mecanim::uint32_t& size, void* allocator, TransferFunction& transfer):m_Transfer(transfer)
+ {
+ m_Allocator = allocator;
+ m_Data = &data;
+ m_ArraySize = &size;
+ }
+
+ value_type* begin () { return *m_Data; }
+ value_type* end () { return *m_Data + *m_ArraySize; }
+ size_t size() { return *m_ArraySize; }
+
+ void resize (int size)
+ {
+ if(m_Transfer.IsReading() || m_Transfer.IsWriting() || m_Transfer.IsRemapPPtrTransfer())
+ {
+ mecanim::memory::ChainedAllocator* allocator = static_cast<mecanim::memory::ChainedAllocator*> (m_Allocator);
+ Assert(allocator != NULL);
+
+ *m_Data = allocator->ConstructArray<value_type> (size);
+ memset(*m_Data, 0, sizeof(value_type)*size);
+ *m_ArraySize = size;
+ }
+ }
+};
+
+
+
+template<class T, class TransferFunction2>
+class SerializeTraits<ManualArrayTransfer<T, TransferFunction2> > : public SerializeTraitsBase<ManualArrayTransfer<T, TransferFunction2> >
+{
+public:
+
+ typedef ManualArrayTransfer<T, TransferFunction2> value_type;
+ DEFINE_GET_TYPESTRING_CONTAINER (vector)
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ transfer.TransferSTLStyleArray (data);
+ }
+
+ static bool IsContinousMemoryArray () { return true; }
+ static void ResizeSTLStyleArray (value_type& data, int rs)
+ {
+ data.resize(rs);
+ }
+
+ static void resource_image_assign_external (value_type& data, void* begin, void* end)
+ {
+ }
+};
+
+#define TRANSFER_NULLABLE(x,TYPE) \
+if (transfer.IsReading () || transfer.IsWriting ()) \
+{ \
+ if (x == NULL) \
+ { \
+ mecanim::memory::ChainedAllocator* allocator = static_cast<mecanim::memory::ChainedAllocator*> (transfer.GetUserData()); \
+ x = allocator->Construct<TYPE>(); \
+ } \
+ transfer.Transfer(*x, #x); \
+} \
+else \
+{ \
+ TYPE p; \
+ transfer.Transfer(p, #x); \
+}
+
diff --git a/Runtime/Animation/MecanimClipBuilder.cpp b/Runtime/Animation/MecanimClipBuilder.cpp
new file mode 100644
index 0000000..72565ee
--- /dev/null
+++ b/Runtime/Animation/MecanimClipBuilder.cpp
@@ -0,0 +1,349 @@
+#include "UnityPrefix.h"
+#include "MecanimClipBuilder.h"
+#include "Runtime/mecanim/animation/clipmuscle.h"
+#include "StreamedClipBuilder.h"
+#include "DenseClipBuilder.h"
+#include "GenericAnimationBindingCache.h"
+#include "AnimationClipSettings.h"
+
+MecanimClipBuilder::MecanimClipBuilder ()
+ :hasAnimationEvents(false),
+ startTime(std::numeric_limits<float>::infinity()),
+ stopTime(-std::numeric_limits<float>::infinity()),
+ sampleRate (30.0F)
+{
+ // Muscle curves
+ for(mecanim::uint32_t muscleIter = 0; muscleIter < mecanim::animation::s_ClipMuscleCurveCount; muscleIter++)
+ muscleIndexArray[muscleIter] = -1;
+}
+
+void PatchMuscleClipWithInfo (const AnimationClipSettings& clipInfo, bool isHumanoid, mecanim::animation::ClipMuscleConstant *cst)
+{
+ cst->m_StartTime = clipInfo.m_StartTime;
+ cst->m_StopTime = clipInfo.m_StopTime;
+ cst->m_OrientationOffsetY = clipInfo.m_OrientationOffsetY;
+ cst->m_Level = clipInfo.m_Level;
+ cst->m_CycleOffset = clipInfo.m_CycleOffset;
+ cst->m_LoopTime = clipInfo.m_LoopTime;
+ cst->m_LoopBlend = clipInfo.m_LoopBlend;
+ cst->m_LoopBlendOrientation = clipInfo.m_LoopBlendOrientation;
+ cst->m_LoopBlendPositionY = clipInfo.m_LoopBlendPositionY;
+ cst->m_LoopBlendPositionXZ = clipInfo.m_LoopBlendPositionXZ;
+ cst->m_KeepOriginalOrientation = clipInfo.m_KeepOriginalOrientation;
+ cst->m_KeepOriginalPositionY = clipInfo.m_KeepOriginalPositionY;
+ cst->m_KeepOriginalPositionXZ = clipInfo.m_KeepOriginalPositionXZ;
+ cst->m_HeightFromFeet = clipInfo.m_HeightFromFeet;
+ cst->m_Mirror = clipInfo.m_Mirror;
+
+ if (isHumanoid)
+ {
+ mecanim::animation::InitClipMuscleDeltaPose (*cst);
+ mecanim::animation::InitClipMuscleAverageSpeed (*cst);
+ }
+ mecanim::animation::InitClipMuscleDeltaValues (*cst);
+}
+
+void CstToAnimationClipSettings (mecanim::animation::ClipMuscleConstant const *cst, AnimationClipSettings &clipInfo)
+{
+ clipInfo.m_StartTime = cst->m_StartTime;
+ clipInfo.m_StopTime = cst->m_StopTime;
+ clipInfo.m_OrientationOffsetY = cst->m_OrientationOffsetY;
+ clipInfo.m_Level = cst->m_Level;
+ clipInfo.m_CycleOffset = cst->m_CycleOffset;
+ clipInfo.m_LoopTime = cst->m_LoopTime;
+ clipInfo.m_LoopBlend = cst->m_LoopBlend;
+ clipInfo.m_LoopBlendOrientation = cst->m_LoopBlendOrientation;
+ clipInfo.m_LoopBlendPositionY = cst->m_LoopBlendPositionY;
+ clipInfo.m_LoopBlendPositionXZ = cst->m_LoopBlendPositionXZ;
+ clipInfo.m_KeepOriginalOrientation = cst->m_KeepOriginalOrientation;
+ clipInfo.m_KeepOriginalPositionY = cst->m_KeepOriginalPositionY;
+ clipInfo.m_KeepOriginalPositionXZ = cst->m_KeepOriginalPositionXZ;
+ clipInfo.m_HeightFromFeet = cst->m_HeightFromFeet;
+ clipInfo.m_Mirror = cst->m_Mirror;
+}
+
+///@TODO: On the runs.fbx there are a ton of curves. Check up on what is going on...
+
+template<class T>
+static ClipOptType ClassifyCurve (AnimationCurveTpl<T>& curve, bool useHighQualityCurve)
+{
+ if (curve.GetKeyCount() == 0)
+ return kInvalidCurve;
+
+ if (IsConstantCurve(curve))
+ return kConstantClip;
+
+ if (!useHighQualityCurve && IsDenseCurve(curve))
+ return kDenseClip;
+
+ return kStreamedClip;
+}
+
+template<class T>
+static void AddCurveToConstantClip (mecanim::animation::ConstantClip& clip, int index, AnimationCurveTpl<T>& curve)
+{
+ memcpy(&clip.data[index], &curve.GetKey(0).value, sizeof(T));
+}
+
+static void AddMappedPPtrCurveToStreamedClip (StreamedClipBuilder* builder, int curveIter, UnityEngine::Animation::AnimationClipBindingConstant& clipBindings, const PPtrKeyframes& pptrCurve)
+{
+ const size_t keyframeCount = pptrCurve.size();
+
+ float* inTime;
+ int* inValue;
+ ALLOC_TEMP(inTime, float, keyframeCount);
+ ALLOC_TEMP(inValue, int, keyframeCount);
+
+ const int mapOffset = clipBindings.pptrCurveMapping.size();
+ for (int i = 0; i < keyframeCount; ++i)
+ {
+ inTime[i] = pptrCurve[i].time;
+ // Map Object to index
+ inValue[i] = mapOffset + i;
+ clipBindings.pptrCurveMapping.push_back(pptrCurve[i].value);
+ }
+
+ AddIntegerCurveToStreamedClip(builder, curveIter, inTime, inValue, keyframeCount);
+}
+
+template<typename TYPE> void for_each_curve(MecanimClipBuilder& clipBuilder, TYPE const& curves )
+{
+ for (int i=0;i<curves.size();i++)
+ {
+ std::pair<float, float> range = curves[i]->GetRange ();
+ clipBuilder.startTime = std::min(range.first, clipBuilder.startTime);
+ clipBuilder.stopTime = std::max(range.second, clipBuilder.stopTime);
+ }
+}
+
+void ComputeDenseClipRange(MecanimClipBuilder& clipBuilder)
+{
+ MecanimClipBuilder::Curves& curves = clipBuilder.curves[kDenseClip];
+ for_each_curve(clipBuilder, curves.positionCurves);
+ for_each_curve(clipBuilder, curves.rotationCurves);
+ for_each_curve(clipBuilder, curves.scaleCurves);
+ for_each_curve(clipBuilder, curves.genericCurves);
+
+ for (int i=0;i<curves.pptrCurves.size();i++)
+ {
+ PPtrKeyframes& keyFrames = *curves.pptrCurves[i];
+ for(int j=0;j<keyFrames.size();j++)
+ {
+ clipBuilder.startTime = std::min(keyFrames[j].time, clipBuilder.startTime);
+ clipBuilder.stopTime = std::max(keyFrames[j].time, clipBuilder.stopTime);
+ }
+ }
+
+ clipBuilder.startTime = !IsFinite(clipBuilder.startTime) ? 0.0f : clipBuilder.startTime;
+ clipBuilder.stopTime = !IsFinite(clipBuilder.stopTime) ? 0.0f : clipBuilder.stopTime;
+}
+
+bool PrepareClipBuilder (MecanimClipBuilder& clipBuilder)
+{
+ size_t previousTypesCurveCount = 0;
+
+ ComputeDenseClipRange(clipBuilder);
+
+ for (int t=0;t<kClipOptCount;t++)
+ {
+ MecanimClipBuilder::Curves& curves = clipBuilder.curves[t];
+
+ size_t keyCount = 0;
+ size_t genericBindingIndex = 0;
+ size_t curveCount = 0;
+ for (int i=0;i<curves.positionCurves.size();i++)
+ {
+ keyCount += curves.positionCurves[i]->GetKeyCount() * 3;
+ curveCount += 3;
+ genericBindingIndex++;
+ }
+
+ for (int i=0;i<curves.rotationCurves.size();i++)
+ {
+ keyCount += curves.rotationCurves[i]->GetKeyCount() * 4;
+ curveCount += 4;
+ genericBindingIndex++;
+ }
+
+ for (int i=0;i<curves.scaleCurves.size();i++)
+ {
+ keyCount += curves.scaleCurves[i]->GetKeyCount() * 3;
+ curveCount += 3;
+ genericBindingIndex++;
+ }
+
+ for (int i=0;i<curves.genericCurves.size();i++)
+ {
+ if (IsMuscleBinding(curves.bindings[genericBindingIndex]))
+ clipBuilder.muscleIndexArray[curves.bindings[genericBindingIndex].attribute] = curveCount + previousTypesCurveCount;
+
+ keyCount += curves.genericCurves[i]->GetKeyCount();
+ genericBindingIndex++;
+ curveCount++;
+ }
+
+ for (int i=0;i<curves.pptrCurves.size();i++)
+ {
+ keyCount += curves.pptrCurves[i]->size();
+ curveCount++;
+ }
+
+ curves.totalKeyCount = keyCount;
+ curves.totalCurveCount = curveCount;
+ previousTypesCurveCount += curveCount;
+ }
+
+ clipBuilder.totalCurveCount = 0;
+ clipBuilder.totalBindingCount = 0;
+ for (int t=0;t<kClipOptCount;t++)
+ {
+ MecanimClipBuilder::Curves& curves = clipBuilder.curves[t];
+ clipBuilder.totalBindingCount += curves.bindings.size();
+ clipBuilder.totalCurveCount += curves.totalCurveCount;
+ }
+
+ return clipBuilder.totalCurveCount != 0 || clipBuilder.hasAnimationEvents;
+}
+
+mecanim::animation::ClipMuscleConstant* BuildMuscleClip (const MecanimClipBuilder& clipBuilder, const AnimationClipSettings& animationClipSettings, bool isHumanClip, UnityEngine::Animation::AnimationClipBindingConstant& outClipBindings, mecanim::memory::Allocator& allocator)
+{
+ SETPROFILERLABEL(ClipMuscleConstant);
+
+ // Total binding count
+ outClipBindings.genericBindings.clear();
+ outClipBindings.genericBindings.reserve(clipBuilder.totalBindingCount);
+ outClipBindings.pptrCurveMapping.clear();
+
+ // Combine into a single binding array
+ outClipBindings.genericBindings.reserve(clipBuilder.totalBindingCount);
+ for (int i=0;i<kClipOptCount;i++)
+ outClipBindings.genericBindings.insert(outClipBindings.genericBindings.end(), clipBuilder.curves[i].bindings.begin(), clipBuilder.curves[i].bindings.end());
+
+ mecanim::animation::Clip* clip = mecanim::animation::CreateClipSimple (clipBuilder.totalCurveCount, allocator);
+
+ // Streamed clip
+ const MecanimClipBuilder::Curves& streamedCurves = clipBuilder.curves[kStreamedClip];
+ StreamedClipBuilder* builder = NULL;
+ builder = CreateStreamedClipBuilder(streamedCurves.totalCurveCount, streamedCurves.totalKeyCount);
+
+ // Constant clip
+ const MecanimClipBuilder::Curves& constantCurves = clipBuilder.curves[kConstantClip];
+ CreateConstantClip (clip->m_ConstantClip, constantCurves.totalCurveCount, allocator);
+
+ // Dense clip
+ const MecanimClipBuilder::Curves& denseCurves = clipBuilder.curves[kDenseClip];
+ CreateDenseClip (clip->m_DenseClip, denseCurves.totalCurveCount, clipBuilder.startTime, clipBuilder.stopTime, clipBuilder.sampleRate, allocator);
+
+ for (int t=0;t<kClipOptCount;t++)
+ {
+ const MecanimClipBuilder::Curves& curves = clipBuilder.curves[t];
+
+ #define AddCurveByType(CURVE_TYPE) \
+ if (t == kStreamedClip) \
+ AddCurveToStreamedClip(builder, curveIter, *curves.CURVE_TYPE[i]); \
+ else if (t == kDenseClip) \
+ AddCurveToDenseClip(clip->m_DenseClip, curveIter, *curves.CURVE_TYPE[i]); \
+ else if (t == kConstantClip) \
+ AddCurveToConstantClip (clip->m_ConstantClip, curveIter, *curves.CURVE_TYPE[i]);
+
+ size_t curveIter = 0;
+ for (int i=0;i<curves.positionCurves.size();i++, curveIter+=3)
+ {
+ AddCurveByType(positionCurves)
+ }
+
+ for (int i=0;i<curves.rotationCurves.size();i++, curveIter+=4)
+ {
+ AddCurveByType(rotationCurves)
+ }
+
+ for (int i=0;i<curves.scaleCurves.size();i++,curveIter+=3)
+ {
+ AddCurveByType(scaleCurves)
+ }
+
+ for (int i=0;i<curves.genericCurves.size();i++,curveIter++)
+ {
+ AddCurveByType(genericCurves)
+ }
+
+ for (int i=0;i<curves.pptrCurves.size();i++,curveIter++)
+ {
+ Assert(t == kStreamedClip);
+ AddMappedPPtrCurveToStreamedClip(builder, curveIter, outClipBindings, *streamedCurves.pptrCurves[i]);
+ }
+ }
+
+ if (builder)
+ {
+ CreateStreamClipConstant(builder, clip->m_StreamedClip, allocator);
+ DestroyStreamedClipBuilder(builder);
+ }
+
+ mecanim::animation::ClipMuscleConstant* muscleClip = mecanim::animation::CreateClipMuscleConstant(clip, allocator);
+ for(mecanim::uint32_t muscleIter = 0; muscleIter < mecanim::animation::s_ClipMuscleCurveCount; muscleIter++)
+ muscleClip->m_IndexArray[muscleIter] = clipBuilder.muscleIndexArray[muscleIter];
+
+ PatchMuscleClipWithInfo (animationClipSettings, isHumanClip, muscleClip);
+
+ return muscleClip;
+}
+
+void AddPositionCurveToClipBuilder (AnimationCurveVec3& curve, const UnityStr& path, MecanimClipBuilder& clipBuilder, bool useHighQualityCurve)
+{
+ ClipOptType type = ClassifyCurve (curve, useHighQualityCurve);
+ if (type == kInvalidCurve)
+ return;
+
+ MecanimClipBuilder::Curves& curves = clipBuilder.curves[type];
+ curves.positionCurves.push_back(&curve);
+ CreateTransformBinding (path, UnityEngine::Animation::kBindTransformPosition, curves.bindings.push_back());
+}
+
+void AddRotationCurveToClipBuilder (AnimationCurveQuat& curve, const UnityStr& path, MecanimClipBuilder& clipBuilder, bool useHighQualityCurve)
+{
+ ClipOptType type = ClassifyCurve (curve, useHighQualityCurve);
+ if (type == kInvalidCurve)
+ return;
+
+ MecanimClipBuilder::Curves& curves = clipBuilder.curves[type];
+ curves.rotationCurves.push_back(&curve);
+ CreateTransformBinding (path, UnityEngine::Animation::kBindTransformRotation, curves.bindings.push_back());
+}
+
+void AddScaleCurveToClipBuilder (AnimationCurveVec3& curve, const UnityStr& path, MecanimClipBuilder& clipBuilder, bool useHighQualityCurve)
+{
+ ClipOptType type = ClassifyCurve (curve, useHighQualityCurve);
+ if (type == kInvalidCurve)
+ return;
+
+ MecanimClipBuilder::Curves& curves = clipBuilder.curves[type];
+ curves.scaleCurves.push_back(&curve);
+ CreateTransformBinding (path, UnityEngine::Animation::kBindTransformScale, curves.bindings.push_back());
+}
+
+void AddGenericCurveToClipBuilder (AnimationCurve& curve, const UnityEngine::Animation::GenericBinding& binding, MecanimClipBuilder& clipBuilder, bool useHighQualityCurve)
+{
+ ClipOptType type = ClassifyCurve (curve, useHighQualityCurve);
+ if (type == kInvalidCurve)
+ return;
+
+ MecanimClipBuilder::Curves& curves = clipBuilder.curves[type];
+ curves.genericCurves.push_back(&curve);
+ curves.bindings.push_back(binding);
+}
+
+void AddPPtrCurveToClipBuilder (PPtrKeyframes& curve, const UnityEngine::Animation::GenericBinding& binding, MecanimClipBuilder& clipBuilder)
+{
+ if (curve.empty())
+ return;
+
+ // The runtime binding code is not able to handle when a Transform component incorrectly has pptr curve.
+ // Reject it here..
+ if (binding.classID == ClassID(Transform))
+ return;
+
+ MecanimClipBuilder::Curves& curves = clipBuilder.curves[kStreamedClip];
+ curves.pptrCurves.push_back(&curve);
+ curves.bindings.push_back(binding);
+}
diff --git a/Runtime/Animation/MecanimClipBuilder.h b/Runtime/Animation/MecanimClipBuilder.h
new file mode 100644
index 0000000..d79dd20
--- /dev/null
+++ b/Runtime/Animation/MecanimClipBuilder.h
@@ -0,0 +1,128 @@
+#pragma once
+
+#include "PPtrKeyframes.h"
+#include "Runtime/Math/AnimationCurve.h"
+#include "AnimationClipBindings.h"
+#include "Runtime/mecanim/animation/clipmuscle.h"
+
+/// Builds a full mecanim clip from source curve data
+/// When building a mecanim clip we classify all curves into:
+/// streamedclip: hermite curve polynomials
+/// denseclip: linearly interpolated non-sparse keyframes
+/// constantclip: value doesn't change over time
+
+
+struct AnimationClipSettings;
+
+enum ClipOptType { kInvalidCurve = -1, kStreamedClip = 0, kDenseClip, kConstantClip, kClipOptCount };
+
+struct MecanimClipBuilder
+{
+ struct Curves
+ {
+ dynamic_array<AnimationCurveVec3*> positionCurves;
+ dynamic_array<AnimationCurveQuat*> rotationCurves;
+ dynamic_array<AnimationCurveVec3*> scaleCurves;
+ dynamic_array<AnimationCurve*> genericCurves;
+ dynamic_array<PPtrKeyframes*> pptrCurves;
+
+ size_t totalCurveCount;
+ size_t totalKeyCount;
+
+ dynamic_array<UnityEngine::Animation::GenericBinding> bindings;
+ };
+
+ MecanimClipBuilder ();
+
+ mecanim::uint32_t muscleIndexArray[mecanim::animation::s_ClipMuscleCurveCount];
+
+ Curves curves[kClipOptCount];
+ size_t totalBindingCount;
+ size_t totalCurveCount;
+ bool hasAnimationEvents;
+ float startTime;
+ float stopTime;
+ float sampleRate;
+};
+
+void AddPositionCurveToClipBuilder (AnimationCurveVec3& curve, const UnityStr& path, MecanimClipBuilder& clipBuilder, bool useHighQualityCurve);
+void AddRotationCurveToClipBuilder (AnimationCurveQuat& curve, const UnityStr& path, MecanimClipBuilder& clipBuilder, bool useHighQualityCurve);
+void AddScaleCurveToClipBuilder (AnimationCurveVec3& curve, const UnityStr& path, MecanimClipBuilder& clipBuilder, bool useHighQualityCurve);
+void AddGenericCurveToClipBuilder (AnimationCurve& curve, const UnityEngine::Animation::GenericBinding& binding, MecanimClipBuilder& clipBuilder, bool useHighQualityCurve);
+void AddPPtrCurveToClipBuilder (PPtrKeyframes& curve, const UnityEngine::Animation::GenericBinding& binding, MecanimClipBuilder& clipBuilder);
+
+bool PrepareClipBuilder (MecanimClipBuilder& clipBuilder);
+mecanim::animation::ClipMuscleConstant* BuildMuscleClip (const MecanimClipBuilder& clipBuilder, const AnimationClipSettings& muslceClipInfo, bool isHumanClip, UnityEngine::Animation::AnimationClipBindingConstant& outClipBindings, mecanim::memory::Allocator& allocator);
+
+void PatchMuscleClipWithInfo (const AnimationClipSettings& clipInfo, bool isHumanoid, mecanim::animation::ClipMuscleConstant *cst);
+void CstToAnimationClipSettings (mecanim::animation::ClipMuscleConstant const *cst, AnimationClipSettings &clipInfo);
+
+template<class T>
+static bool IsConstantCurve (AnimationCurveTpl<T>& curve)
+{
+ Assert(curve.GetKeyCount() != 0);
+
+ KeyframeTpl<T> firstKey = curve.GetKey(0);
+ for (int i=0;i<curve.GetKeyCount();i++)
+ {
+ if (!CompareApproximately(curve.GetKey(i).value, firstKey.value))
+ return false;
+ if (!CompareApproximately(curve.GetKey(i).inSlope, Zero<T> ()))
+ return false;
+ if (!CompareApproximately(curve.GetKey(i).outSlope, Zero<T> ()))
+ return false;
+ }
+
+ return true;
+}
+
+template<class T>
+static bool IsStepKey(KeyframeTpl<T> const& key)
+{
+ return !IsFinite(key.inSlope) || !IsFinite(key.outSlope);
+}
+
+template<class T>
+static bool IsTooDense(KeyframeTpl<T> const& key, KeyframeTpl<T> const& previousKey, float sampleStep)
+{
+ float delta = std::abs(key.time - previousKey.time);
+
+ // epsilon is too small here, use a bigger threshold
+ return (delta - sampleStep) < -1e-5f /*-std::numeric_limits<float>::epsilon()*/;
+}
+
+template<class T>
+static bool IsDenseCurve (AnimationCurveTpl<T> const& curve)
+{
+ Assert(curve.GetKeyCount() != 0);
+
+ const float samplePerSec = 30.f;
+ const float sampleStep = 1.0f/samplePerSec;
+
+ // Remember that default curve classification is Streamed curve,
+ // which are ~8 time bigger in memory than a Dense curve( ~8x = constant cost + memory const)
+ //
+ std::pair<float, float> range = curve.GetRange();
+ float diff = range.second - range.first;
+ if(diff * samplePerSec > curve.GetKeyCount() * 8)
+ return false;
+
+ if( IsStepKey(curve.GetKey(0)) )
+ return false;
+
+ // Look for step curve, they cannot be represented by a dense curve
+ for (int i=1;i<curve.GetKeyCount();i++)
+ {
+ KeyframeTpl<T> const& previousKey = curve.GetKey(i-1);
+ KeyframeTpl<T> const& key = curve.GetKey(i);
+
+ if( IsStepKey(key) )
+ return false;
+
+ // For now if there is more key than sampling rate, revert back to streamed clip.
+ if( IsTooDense( key, previousKey, sampleStep) )
+ return false;
+ }
+
+ return true;
+}
diff --git a/Runtime/Animation/MecanimUtility.cpp b/Runtime/Animation/MecanimUtility.cpp
new file mode 100644
index 0000000..2b6da3f
--- /dev/null
+++ b/Runtime/Animation/MecanimUtility.cpp
@@ -0,0 +1,45 @@
+#include "UnityPrefix.h"
+
+#include "MecanimUtility.h"
+
+#include "Runtime/mecanim/generic/crc32.h"
+
+#include "Runtime/Utilities/File.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Allocator/MemoryManager.h"
+
+std::string BuildTransitionName(std::string srcStateName, std::string dstStateName)
+{
+ return srcStateName + " -> " + dstStateName;
+}
+
+std::string FileName(const std::string &fullpath)
+{
+ std::string fullpathCopy(fullpath);
+ ConvertSeparatorsToUnity(fullpathCopy);
+ return GetLastPathNameComponent(StandardizePathName(fullpathCopy));
+}
+std::string FileNameNoExt(const std::string &fullpath)
+{
+ return DeletePathNameExtension(FileName(fullpath));
+}
+
+unsigned int ProccessString(TOSVector& tos, std::string const& str)
+{
+ unsigned int crc32 = mecanim::processCRC32(str.c_str());
+ TOSVector::iterator it = tos.find(crc32);
+ if(it == tos.end())
+ {
+ tos.insert( std::make_pair(crc32, str) );
+ }
+ return crc32;
+}
+
+std::string FindString(TOSVector const& tos, unsigned int crc32)
+{
+ TOSVector::const_iterator it = tos.find(crc32);
+ if(it!=tos.end())
+ return it->second;
+
+ return std::string("");
+}
diff --git a/Runtime/Animation/MecanimUtility.h b/Runtime/Animation/MecanimUtility.h
new file mode 100644
index 0000000..e6bfded
--- /dev/null
+++ b/Runtime/Animation/MecanimUtility.h
@@ -0,0 +1,123 @@
+#ifndef MECANIM_UTILITY_H
+#define MECANIM_UTILITY_H
+
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Allocator/BaseAllocator.h"
+#include "Runtime/Misc/AllocatorLabels.h"
+
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/Math/Simd/xform.h"
+
+#include "Runtime/Serialize/Blobification/BlobWrite.h"
+
+#include <stack>
+#include <algorithm>
+
+std::string BuildTransitionName(std::string srcStateName, std::string dstStateName);
+
+typedef std::map<mecanim::uint32_t, UnityStr> TOSVector;
+
+static inline Vector3f float4ToVector3f(math::float4 const& v)
+{
+ ATTRIBUTE_ALIGN(ALIGN4F) float buf[4];
+ math::store(v, buf);
+ //return Vector3f(-buf[0],buf[1],buf[2]);
+ return Vector3f(buf[0],buf[1],buf[2]);
+}
+
+static inline Quaternionf float4ToQuaternionf(math::float4 const& q)
+{
+ ATTRIBUTE_ALIGN(ALIGN4F) float buf[4];
+ math::store(math::normalize(q), buf);
+ //return Quaternionf(-buf[0],buf[1],buf[2],-buf[3]);
+ return Quaternionf(buf[0],buf[1],buf[2],buf[3]);
+}
+
+static inline Quaternionf float4ToQuaternionfNoNormalize(math::float4 const& q)
+{
+ ATTRIBUTE_ALIGN(ALIGN4F) float buf[4];
+ math::store(q, buf);
+ return Quaternionf(buf[0],buf[1],buf[2],buf[3]);
+}
+
+static inline void xform2unity(math::xform const& x, Vector3f& t, Quaternionf& q, Vector3f& s)
+{
+ ATTRIBUTE_ALIGN(ALIGN4F) float bufS[4];
+
+ math::store(x.s, bufS);
+
+ t = float4ToVector3f(x.t);
+ q = float4ToQuaternionf(x.q);
+ s.Set(bufS[0], bufS[1], bufS[2]);
+}
+
+static inline void xform2unityNoNormalize(math::xform const& x, Vector3f& t, Quaternionf& q, Vector3f& s)
+{
+ ATTRIBUTE_ALIGN(ALIGN4F) float bufS[4];
+
+ math::store(x.s, bufS);
+
+ t = float4ToVector3f(x.t);
+ q = float4ToQuaternionfNoNormalize(x.q);
+ s.Set(bufS[0], bufS[1], bufS[2]);
+}
+
+static inline void xform2unity(math::xform const& x, Matrix4x4f& matrix)
+{
+ ATTRIBUTE_ALIGN(ALIGN4F) float bufS[4];
+
+ Vector3f t;
+ Quaternionf q;
+ Vector3f s;
+
+ math::store(x.s, bufS);
+
+ t = float4ToVector3f(x.t);
+ q = float4ToQuaternionf(x.q);
+ s.Set(bufS[0], bufS[1], bufS[2]);
+ matrix.SetTRS(t, q, s);
+}
+
+
+static inline math::float4 Vector3fTofloat4(Vector3f const& v, float w = 0)
+{
+ //ATTRIBUTE_ALIGN(ALIGN4F) float buf[4] = {-v.x, v.y, v.z, 0};
+ ATTRIBUTE_ALIGN(ALIGN4F) float buf[4] = {v.x, v.y, v.z, w};
+ return math::load(buf);
+}
+
+static inline math::float4 QuaternionfTofloat4(Quaternionf const& q)
+{
+ //ATTRIBUTE_ALIGN(ALIGN4F) float buf[4] = {-q.x, q.y, q.z, -q.w};
+ ATTRIBUTE_ALIGN(ALIGN4F) float buf[4] = {q.x, q.y, q.z, q.w};
+ return math::load(buf);
+}
+
+static inline math::xform xformFromUnity(Vector3f const& t, Quaternionf const& q, Vector3f const& s)
+{
+ ATTRIBUTE_ALIGN(ALIGN4F) float bufS[4];
+
+ bufS[0] = s.x; bufS[1] = s.y; bufS[2] = s.z; bufS[3] = 1.f;
+ return math::xform(Vector3fTofloat4(t), QuaternionfTofloat4(q), math::load(bufS));
+}
+
+std::string FileName(const std::string &fullpath);
+std::string FileNameNoExt(const std::string &fullpath);
+
+unsigned int ProccessString(TOSVector& tos, std::string const& str);
+std::string FindString(TOSVector const& tos, unsigned int crc32);
+template <typename T> inline T* CopyBlob(T const& data, mecanim::memory::Allocator& allocator, size_t& size)
+{
+ BlobWrite::container_type blob;
+ BlobWrite blobWrite (blob, kNoTransferInstructionFlags, kBuildNoTargetPlatform);
+ blobWrite.Transfer( const_cast<T&>(data), "Base");
+
+ UInt8* ptr = reinterpret_cast<UInt8*>(allocator.Allocate(blob.size(), ALIGN_OF(T)));
+ if(ptr != 0)
+ memcpy(ptr, blob.begin(), blob.size());
+ size = blob.size();
+ return reinterpret_cast<T*>(ptr);
+}
+
+#endif
diff --git a/Runtime/Animation/Motion.cpp b/Runtime/Animation/Motion.cpp
new file mode 100644
index 0000000..6a30490
--- /dev/null
+++ b/Runtime/Animation/Motion.cpp
@@ -0,0 +1,28 @@
+#include "UnityPrefix.h"
+#include "Motion.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/BaseClasses/MessageIdentifier.h"
+#include "Runtime/Misc/UserList.h"
+
+Motion::Motion (MemLabelId label, ObjectCreationMode mode) : Super (label, mode), m_ObjectUsers(this)
+{ }
+
+Motion::~Motion ()
+{
+
+}
+
+
+void Motion::NotifyObjectUsers(const MessageIdentifier& msg)
+{
+ m_ObjectUsers.SendMessage(msg);
+}
+
+void Motion::AddObjectUser( UserList& user )
+{
+ m_ObjectUsers.AddUser(user);
+}
+
+IMPLEMENT_CLASS (Motion)
+
diff --git a/Runtime/Animation/Motion.h b/Runtime/Animation/Motion.h
new file mode 100644
index 0000000..bffc91e
--- /dev/null
+++ b/Runtime/Animation/Motion.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/Misc/UserList.h"
+#include "Runtime/Math/Vector3.h"
+
+class MessageIdentifier;
+class AnimationClip;
+
+class Motion : public NamedObject
+{
+public:
+ REGISTER_DERIVED_ABSTRACT_CLASS (Motion, NamedObject)
+
+ Motion (MemLabelId label, ObjectCreationMode mode);
+
+ void NotifyObjectUsers(const MessageIdentifier& msg);
+ void AddObjectUser( UserList& user );
+
+#if UNITY_EDITOR
+ virtual float GetAverageDuration() = 0;
+ virtual float GetAverageAngularSpeed() = 0;
+ virtual Vector3f GetAverageSpeed() = 0;
+ virtual float GetApparentSpeed() = 0;
+
+ virtual bool ValidateIfRetargetable(bool showWarning = true) = 0;
+
+ virtual bool IsLooping() = 0 ;
+
+ virtual bool IsAnimatorMotion()const = 0;
+ virtual bool IsHumanMotion() = 0;
+
+#endif
+
+ virtual void AddUser(UserList& dependencies) { dependencies.AddUser(GetUserList ());}
+
+ UserList& GetUserList () { return m_ObjectUsers; }
+
+private:
+
+ UserList m_ObjectUsers;
+};
diff --git a/Runtime/Animation/NewAnimationTrack.cpp b/Runtime/Animation/NewAnimationTrack.cpp
new file mode 100644
index 0000000..0489f59
--- /dev/null
+++ b/Runtime/Animation/NewAnimationTrack.cpp
@@ -0,0 +1,80 @@
+#include "UnityPrefix.h"
+#if UNITY_EDITOR
+#include "NewAnimationTrack.h"
+#include "Runtime/Math/AnimationCurve.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferUtility.h"
+#include "Runtime/Graphics/Transform.h"
+
+using namespace std;
+
+enum { kModifiesRotation = 1 << 0, kModifiesScale = 1 << 1 };
+
+vector<string> NewAnimationTrack::GetCurves ()
+{
+ vector<string> curves;
+ curves.reserve (m_Curves.size ());
+ for (Curves::iterator i=m_Curves.begin ();i!=m_Curves.end ();i++)
+ curves.push_back (i->attributeName);
+ return curves;
+}
+
+NewAnimationTrack::NewAnimationTrack (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_ClassID = 0;
+}
+
+NewAnimationTrack::~NewAnimationTrack ()
+{}
+
+/*
+template<class T> inline
+float GetValue (Object& o, int offset)
+{
+ return *reinterpret_cast<T*> (reinterpret_cast<char*> (&o) + offset);
+}
+
+bool NewAnimationTrack::ExtractFloatValue (Object* src, const TypeTree* value, float* f)
+{
+ if (value == NULL)
+ return false;
+ if (value->m_ByteOffset == -1)
+ return false;
+
+ if (value->m_Type == "float")
+ {
+ if (src && f)
+ *f = GetValue<float> (*src, value->m_ByteOffset);
+ return true;
+ }
+ else if (value->m_Type == "bool")
+ {
+ if (src && f)
+ *f = GetValue<bool> (*src, value->m_ByteOffset);
+ return true;
+ }
+ else
+ return false;
+}
+*/
+template<class TransferFunction>
+void NewAnimationTrack::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER (m_Curves);
+ TRANSFER (m_ClassID);
+}
+
+template<class TransferFunction>
+void NewAnimationTrack::Channel::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (byteOffset);
+ TRANSFER (curve);
+ TRANSFER (attributeName);
+}
+
+IMPLEMENT_CLASS (NewAnimationTrack)
+IMPLEMENT_OBJECT_SERIALIZE (NewAnimationTrack)
+#endif
diff --git a/Runtime/Animation/NewAnimationTrack.h b/Runtime/Animation/NewAnimationTrack.h
new file mode 100644
index 0000000..a50ee57
--- /dev/null
+++ b/Runtime/Animation/NewAnimationTrack.h
@@ -0,0 +1,74 @@
+
+/// BACKWARDS COMPATIBILITY ONLY
+#if UNITY_EDITOR
+#ifndef NEWANIMATIONTRACK_H
+#define NEWANIMATIONTRACK_H
+
+#include "BaseAnimationTrack.h"
+#include "Runtime/Math/AnimationCurve.h"
+
+/// The animationTrack2 class is currently specialized to handling only
+/// transformcomponents. It will be expanded to animate any data that is serialized.
+
+class NewAnimationTrack : public BaseAnimationTrack
+{
+ public:
+
+ REGISTER_DERIVED_CLASS (NewAnimationTrack, BaseAnimationTrack)
+ DECLARE_OBJECT_SERIALIZE (NewAnimationTrack)
+
+ NewAnimationTrack (MemLabelId label, ObjectCreationMode mode);
+ // ~NewAnimationTrack (); declared-by-macro
+ /// The attributename is generated from the serialization system
+ /// and is the path to the property. "." is used as the path seperator.
+ /// It is supposed to be similar how you access the variable in C++
+ /// A transformcomponent that transfers a Vector3 using TRANSFER (m_LocalPosition)
+ /// and a Vector3 that transfers using TRANSFER (x)
+ /// would have a attribute name "m_LocalPosition.x"
+/*
+ /// Returns the curve for an attribute.
+ Channel* GetChannel (const std::string& attributeName);
+ int GetChannelCount () { m_Curves.size (); }
+ Channel& GetChannelAtIndex (int index) { return m_Curves[index]; }
+*/
+
+ /// Inserts a curve for an attribute always returns true
+// bool InsertCurve (const std::string& attributeName, const AnimationCurve& curve);
+// bool RemoveCurve (const std::string& attributeName);
+ AnimationCurve* GetCurve (const std::string& attributeName);
+ std::vector<std::string> GetCurves ();
+
+// virtual void SampleAnimation (Object& o, float time, int /*wrapmode*/);
+// virtual std::pair<float, float> GetRange () const;
+// virtual std::pair<float, float> GetPlayableRange () const;
+// int GetAnimationClassID () const { return m_ClassID; }
+// void SetAnimationClassID (int classID) { m_ClassID = classID; }
+
+ struct Channel
+ {
+ int byteOffset;
+// int type;
+ AnimationCurve curve;
+ UnityStr attributeName;
+
+ bool operator == (const string& name) { return attributeName == name; }
+
+ DECLARE_SERIALIZE (Channel)
+ };
+
+ /// Returns whether the animation system supports the value!
+ /// if src and f are non-null extracts the value into f.
+// static bool ExtractFloatValue (Object* src, const TypeTree* value, float* f);
+ typedef std::vector<Channel> Curves;
+
+ Curves m_Curves;
+ int m_ClassID;
+// int m_Optimizations;
+
+// void RebuildByteOffsets ();
+
+ friend class Animation;
+};
+
+#endif
+#endif
diff --git a/Runtime/Animation/OptimizeTransformHierarchy.cpp b/Runtime/Animation/OptimizeTransformHierarchy.cpp
new file mode 100644
index 0000000..85e30c5
--- /dev/null
+++ b/Runtime/Animation/OptimizeTransformHierarchy.cpp
@@ -0,0 +1,303 @@
+#include "UnityPrefix.h"
+
+#include "OptimizeTransformHierarchy.h"
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Animation/Animator.h"
+#include "Runtime/Filters/Deformation/SkinnedMeshFilter.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Animation/Avatar.h"
+#include "Runtime/Animation/AvatarBuilder.h"
+#include "Runtime/mecanim/animation/avatar.h"
+#include "Runtime/mecanim/skeleton/skeleton.h"
+
+#include <map>
+
+using namespace std;
+using namespace mecanim::animation;
+using namespace mecanim::skeleton;
+using namespace mecanim;
+
+// static function forward declarations
+static void FlattenHierarchy (GameObject& gameObject);
+static void FlattenHierarchyRecurse (Transform& transform, Transform& root);
+
+void OptimizeTransformHierarchy(GameObject& root, const UnityStr* exposedTransforms, size_t exposedTransformCount)
+{
+ Animator* animator = root.QueryComponent(Animator);
+ if (!animator)
+ return;
+
+ if(!animator->IsOptimizable())
+ return;
+
+ Transform* rootTransform = animator->GetAvatarRoot();
+ if(!rootTransform)
+ return;
+
+ GameObject& effectiveRoot = rootTransform->GetGameObject();
+
+ // 1. Take care of the SkinnedMeshRenderers, so that the will work without the transform hierarchy.
+ dynamic_array<SkinnedMeshRenderer*> skins (kMemTempAlloc);
+ GetComponentsInChildren(effectiveRoot, true, ClassID(SkinnedMeshRenderer), reinterpret_cast<dynamic_array<Unity::Component*>&>(skins));
+ for (int i=0;i<skins.size();i++)
+ {
+ SkinnedMeshRenderer& skin = *skins[i];
+
+ // The root bone of the SkinnedMeshRenderer will always be itself in optimized mode.
+ // We'll directly set the correct value to it through the transform binding.
+ if (skin.GetRootBone())
+ {
+ const Transform& skinRoot = *skin.GetRootBone();
+ Transform& skinTransform = skin.GetComponent(Transform);
+ skinTransform.SetPositionAndRotation(skinRoot.GetPosition(), skinRoot.GetRotation());
+ // TODO: handle scale if needed later
+ }
+ skin.SetRootBone(NULL);
+
+ // Clear the Transform array, we'll use skeleton indices array instead.
+ skin.SetBones(dynamic_array<PPtr<Transform> > ());
+ }
+
+ // 2. Flatten the transform hierarchy
+ FlattenHierarchy (effectiveRoot);
+
+ // 3. Remove the unnecessary transforms
+ // Here, we can safely remove transforms that are:
+ // a) human bones, because the Avatar will take care of them
+ // b) referred by SkinnedMeshRenderer, because the SkinnedMeshRenderer can work with skeleton indices.
+ UNITY_VECTOR(kMemTempAlloc, UnityStr) exposedTransformNames(exposedTransformCount);
+ for (int i = 0; i < exposedTransformCount; i++)
+ exposedTransformNames[i] = GetLastPathNameComponent(exposedTransforms[i].c_str(), exposedTransforms[i].size());
+ RemoveUnnecessaryTransforms (root, NULL,
+ exposedTransformCount ? &(exposedTransformNames[0]) : NULL,
+ exposedTransformCount, false);
+
+ // Finally, set the Animator to be optimized mode.
+ animator->SetHasTransformHierarchy(false);
+}
+
+void DeoptimizeTransformHierarchy(Unity::GameObject& root)
+{
+ Animator* animator = root.QueryComponent(Animator);
+ if (!animator)
+ return;
+
+ if(!animator->IsOptimizable())
+ return;
+
+ Transform* avatarRoot = animator->GetAvatarRoot();
+ if(!avatarRoot)
+ return;
+ GameObject& effectiveRoot = avatarRoot->GetGameObject();
+
+ // 1. Figure out the skeletonPaths from the unstripped avatar
+ const Avatar& unstrippedAvatar = *animator->GetAvatar();
+ const TOSVector& tos = unstrippedAvatar.GetTOS();
+ const AvatarConstant* avatarConstant = unstrippedAvatar.GetAsset();
+ const Skeleton& skeleton = *avatarConstant->m_AvatarSkeleton;
+ const SkeletonPose& skeletonPose = *avatarConstant->m_DefaultPose;
+
+ UNITY_VECTOR(kMemTempAlloc, UnityStr) skeletonPaths;
+ for (int i = 0; i < skeleton.m_Count; i++)
+ {
+ UnityStr path = "";
+ TOSVector::const_iterator it = tos.find(skeleton.m_ID[i]);
+ if (it != tos.end())
+ path = it->second;
+ skeletonPaths.push_back(path.c_str());
+ }
+
+ // 2. Restore the original transform hierarchy
+ // Prerequisite: skeletonPaths follow pre-order traversal
+ Transform& rootTransform = effectiveRoot.GetComponent(Transform);
+ for (int i = 1; i < skeletonPaths.size(); i++) // start from 1, skip the root transform because it will always be there.
+ {
+ const UnityStr& unflattenPath = skeletonPaths[i];
+ UnityStr transformName = GetLastPathNameComponent(unflattenPath);
+ Transform* curTransform = FindTransformWithName(&rootTransform, transformName.c_str());
+ if (curTransform == NULL)
+ {
+ // Create a new GameObject with just transform component
+ GameObject& go = CreateGameObjectWithHideFlags (transformName, true, 0, "Transform", NULL);
+ curTransform = go.QueryComponent(Transform);
+ }
+
+ // insert it at the right position of the hierarchy
+ Transform* parentTransform = &rootTransform;
+ UnityStr parentPath = DeleteLastPathNameComponent(unflattenPath);
+ if (parentPath.length() > 0)
+ parentTransform = FindRelativeTransformWithPath(rootTransform, parentPath.c_str());
+ curTransform->SetParent(parentTransform);
+
+ Vector3f t, s; Quaternionf q;
+ xform2unityNoNormalize(skeletonPose.m_X[i], t, q, s);
+ curTransform->SetLocalPositionWithoutNotification(t);
+ curTransform->SetLocalRotationWithoutNotification(q);
+ curTransform->SetLocalScaleWithoutNotification(s);
+ }
+
+ // 3. Restore the values in SkinnedMeshRenderer
+ dynamic_array<SkinnedMeshRenderer*> skins (kMemTempAlloc);
+ GetComponentsInChildren(effectiveRoot, true, ClassID(SkinnedMeshRenderer), reinterpret_cast<dynamic_array<Unity::Component*>&>(skins));
+ for (int i = 0; i < skins.size(); i++)
+ {
+ SkinnedMeshRenderer& skin = *skins[i];
+
+ // a. root bone
+ Transform* rootBoneTransform = NULL;
+ BindingHash rootPathHash = 0;
+ const Mesh* mesh = skin.GetMesh();
+ if (mesh)
+ rootPathHash = mesh->GetRootBonePathHash();
+ if (rootPathHash)
+ {
+ int skeletonIndex = mecanim::skeleton::SkeletonFindNode(&skeleton, rootPathHash);
+ const UnityStr& rootPath = skeletonPaths[skeletonIndex];
+ rootBoneTransform = FindRelativeTransformWithPath(rootTransform, rootPath.c_str());
+ }
+ if (rootBoneTransform != skin.QueryComponent(Transform))
+ skin.SetRootBone(rootBoneTransform);
+
+ // b. skeleton
+ if (mesh)
+ {
+ const dynamic_array<BindingHash>& bonePathHashes = mesh->GetBonePathHashes();
+ dynamic_array<PPtr<Transform> > boneTransforms;
+ boneTransforms.resize_initialized (bonePathHashes.size());
+ for (int j = 0; j < boneTransforms.size(); j++)
+ {
+ BindingHash bonePathHash = bonePathHashes[j];
+ int boneIndexInUnstrippedSkeleton = SkeletonFindNode(&skeleton, bonePathHash);
+ AssertIf(boneIndexInUnstrippedSkeleton == -1);
+ const UnityStr& bonePath = skeletonPaths[boneIndexInUnstrippedSkeleton];
+ Transform* boneTransform = FindRelativeTransformWithPath(rootTransform, bonePath.c_str());
+ boneTransforms[j] = boneTransform;
+ }
+ skin.SetBones(boneTransforms);
+ }
+ }
+
+ // 4. Animator
+ animator->SetHasTransformHierarchy(true);
+}
+
+static void FlattenHierarchyRecurse (Transform& transform, Transform& root)
+{
+ while (transform.GetChildrenCount () > 0)
+ {
+ Transform& child = transform.GetChild (0);
+ child.SetParent (&root);
+ FlattenHierarchyRecurse (child, root);
+ }
+}
+
+static void FlattenHierarchy (GameObject& gameObject)
+{
+ Transform& root = gameObject.GetComponent(Transform);
+ int size = root.GetChildrenCount ();
+ dynamic_array<Transform*> children(size, kMemTempAlloc);
+ for (int i=0;i < size;++i)
+ children[i] = &root.GetChild(i);
+
+ for (int i=0;i < size;++i)
+ FlattenHierarchyRecurse (*children[i], root);
+}
+
+void RemoveUnnecessaryTransforms (Unity::GameObject& gameObject, const HumanDescription* human, const UnityStr* exposedTransforms, size_t exposedTransformCount, bool doKeepSkeleton)
+{
+ // false : unnecessary
+ map<Transform*, bool> transformToMark;
+ Transform& rootTransform = gameObject.GetComponent(Transform);
+
+ // 1. Initialize the map
+ dynamic_array<Transform*> allTransforms (kMemTempAlloc);
+ GetComponentsInChildren(gameObject, true, ClassID(Transform), reinterpret_cast<dynamic_array<Unity::Component*>&>(allTransforms));
+ for (int i=0; i<allTransforms.size(); i++)
+ {
+ Transform* transform = allTransforms[i];
+ bool isNecessary = (transform->GetGameObject().GetComponentCount() > 1);
+ transformToMark.insert(pair<Transform*, bool>(transform, isNecessary));
+ }
+
+ // 2. Handle human bones
+ if (human)
+ {
+ for (int i=0; i<allTransforms.size(); i++)
+ {
+ Transform* transform = allTransforms[i];
+ map<Transform*, bool>::iterator it = transformToMark.find(transform);
+ AssertIf(it == transformToMark.end());
+
+ // human bones should not be removed
+ if (std::find_if(human->m_Human.begin(), human->m_Human.end(), FindBoneName( transform->GetName() )) != human->m_Human.end() )
+ it->second = true;
+ else if (human->m_RootMotionBoneName.length() > 0)
+ {
+ // root bone should not be removed (for Humanoid & Generic root motion)
+ if (human->m_RootMotionBoneName.compare(transform->GetName()) == 0)
+ it->second = true;
+ }
+ }
+ }
+
+ // 3. Handle exposed transforms
+ for (int i=0; i<exposedTransformCount; i++)
+ {
+ const UnityStr& path = exposedTransforms[i];
+ Transform* transform = FindRelativeTransformWithPath(rootTransform, path.c_str());
+ map<Transform*, bool>::iterator it = transformToMark.find(transform);
+ AssertIf(it == transformToMark.end());
+ it->second = true;
+ }
+
+ // 4. Handle SkinnedMeshRenderers
+ if (doKeepSkeleton)
+ {
+ dynamic_array<SkinnedMeshRenderer*> skins (kMemTempAlloc);
+ GetComponentsInChildren(gameObject, true, ClassID(SkinnedMeshRenderer), reinterpret_cast<dynamic_array<Unity::Component*>&>(skins));
+
+ for (int i=0; i<skins.size(); i++)
+ {
+ const SkinnedMeshRenderer& skin = *skins[i];
+ const dynamic_array<PPtr<Transform> >& bones = skin.GetBones();
+ for (int j=0; j<bones.size(); j++)
+ {
+ Transform* transform = bones[j];
+ map<Transform*, bool>::iterator it = transformToMark.find(transform);
+ AssertIf(it == transformToMark.end());
+ it->second = true;
+ }
+ }
+ }
+
+ // 5. Handle parent transforms
+ for (int i=allTransforms.size()-1; i>=0; i--)
+ {
+ Transform* transform = allTransforms[i];
+ map<Transform*, bool>::const_iterator it = transformToMark.find(transform);
+ AssertIf(it == transformToMark.end());
+ if (!it->second)
+ continue;
+
+ Transform* parent = transform->GetParent();
+ if (!parent)
+ continue;
+ map<Transform*, bool>::iterator itParent = transformToMark.find(parent);
+ AssertIf(itParent == transformToMark.end());
+ itParent->second = true;
+ }
+
+ // Remove unnecessary transforms
+ for (int i=allTransforms.size()-1; i>=0; i--)
+ {
+ Transform* transform = allTransforms[i];
+ map<Transform*, bool>::const_iterator it = transformToMark.find(transform);
+ AssertIf(it == transformToMark.end());
+ if (it->second)
+ continue;
+ DestroyObjectHighLevel(&(transform->GetGameObject()));
+ }
+}
+
diff --git a/Runtime/Animation/OptimizeTransformHierarchy.h b/Runtime/Animation/OptimizeTransformHierarchy.h
new file mode 100644
index 0000000..fcf5ef2
--- /dev/null
+++ b/Runtime/Animation/OptimizeTransformHierarchy.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Allocator/STLAllocator.h"
+
+/// Forward declaration
+class Avatar;
+struct HumanDescription;
+
+/// Optimize the transform hierarchy of the GameObject.
+///
+/// Briefly, it will:
+/// 1. Flatten the Transform hierarchy.
+/// 2. Remove all the Transforms which are not necessary.
+/// 3. The SkinnedMeshRenderers will then use skeleton indices to query bone matrices instead of using Transforms.
+void OptimizeTransformHierarchy(Unity::GameObject& root, const UnityStr* exposedTransforms = NULL, size_t exposedTransformCount = 0);
+
+/// De-optimize the transform hierarchy of the GameObject.
+void DeoptimizeTransformHierarchy(Unity::GameObject& root);
+
+/// Remove unnecessary transforms.
+///
+/// 1. If 'human' isn't NULL, the human bones will be kept.
+/// 2. All the 'exposedTransforms' will be kept.
+/// 3. If 'doKeepSkeleton' is true, the Transforms that are used by SkinnedMeshRenderers will be kept.
+///
+/// If one Transform is kept, its ancestors will also be kept.
+void RemoveUnnecessaryTransforms (Unity::GameObject& gameObject, const HumanDescription* human, const UnityStr* exposedTransforms, size_t exposedTransformCount, bool doKeepSkeleton);
+
+/// Query the useful transform paths from the transform hierarchy.
+///
+/// 1. The output paths will be relative paths to 'root'
+/// 2. If there are more than one Components attached to a GameObject, then the corresponding Transform will be regarded as *Useful*.
+template <typename Alloc>
+void GetUsefulTransformPaths (const Transform& root, const Transform& transform, std::vector<UnityStr, Alloc>& outPaths)
+{
+ for (int i=0; i<transform.GetChildrenCount(); i++)
+ {
+ const Transform& child = transform.GetChild(i);
+ const GameObject& gameObject = child.GetGameObject();
+ if (gameObject.GetComponentCount() > 1)
+ {
+ UnityStr path = CalculateTransformPath(child, &root);
+ outPaths.push_back(path);
+ }
+
+ GetUsefulTransformPaths(root, child, outPaths);
+ }
+}
diff --git a/Runtime/Animation/OptimizeTransformHierarchyTests.cpp b/Runtime/Animation/OptimizeTransformHierarchyTests.cpp
new file mode 100644
index 0000000..98d4d58
--- /dev/null
+++ b/Runtime/Animation/OptimizeTransformHierarchyTests.cpp
@@ -0,0 +1,293 @@
+#include "UnityPrefix.h"
+
+#include "OptimizeTransformHierarchy.h"
+
+#include "Runtime/Animation/CharacterTestFixture.h"
+
+#include "Runtime/Testing/Testing.h"
+#include "Runtime/Testing/TestFixtures.h"
+
+#include <string>
+#include <vector>
+#include <algorithm>
+
+#if ENABLE_UNIT_TESTS
+
+using namespace Unity;
+using namespace std;
+
+SUITE (OptimizeTransformHierarchy)
+{
+ TEST_FIXTURE (CharacterTestFixture, OptimizeTransformHierarchy_Remove_All_GameObjects_With_Transform_Only)
+ {
+ // Arrange
+ MakeCharacter();
+
+ // Act
+ OptimizeTransformHierarchy(*root);
+
+ // Assert
+ Transform& rootTr = root->GetComponent(Transform);
+ CHECK_EQUAL(MESH_RENDERER_COUNT+SKINNED_MESH_RENDERER_COUNT, GetAllChildrenCount(rootTr));
+
+ CHECK(FindRelativeTransformWithPath(rootTr, "mr1") != NULL);
+ CHECK(FindRelativeTransformWithPath(rootTr, "mr2") != NULL);
+ CHECK(FindRelativeTransformWithPath(rootTr, "smr1") != NULL);
+ CHECK(FindRelativeTransformWithPath(rootTr, "smr2") != NULL);
+ }
+
+ TEST_FIXTURE (CharacterTestFixture, OptimizeTransformHierarchy_Expose_Certain_Transforms)
+ {
+ // Arrange
+ const UnityStr exposedPaths[] = {
+ "b1/b1_1/b1_1_1",
+ "b2/b2_1",
+ };
+ const int EXPOSED_COUNT = sizeof(exposedPaths)/sizeof(UnityStr);
+ MakeCharacter(exposedPaths, EXPOSED_COUNT);
+
+ // Act
+ OptimizeTransformHierarchy(*root, exposedPaths, EXPOSED_COUNT);
+
+ // Assert
+ Transform& rootTr = root->GetComponent(Transform);
+ CHECK_EQUAL(MESH_RENDERER_COUNT+SKINNED_MESH_RENDERER_COUNT+EXPOSED_COUNT, GetAllChildrenCount(rootTr));
+
+ CHECK(FindRelativeTransformWithPath(rootTr, "mr1") != NULL);
+ CHECK(FindRelativeTransformWithPath(rootTr, "mr2") != NULL);
+ CHECK(FindRelativeTransformWithPath(rootTr, "smr1") != NULL);
+ CHECK(FindRelativeTransformWithPath(rootTr, "smr2") != NULL);
+ CHECK(FindRelativeTransformWithPath(rootTr, "b1_1_1") != NULL);
+ CHECK(FindRelativeTransformWithPath(rootTr, "b2_1") != NULL);
+ }
+
+ TEST_FIXTURE (CharacterTestFixture, OptimizeTransformHierarchy_Flattened_Transforms_Have_Correct_TRS)
+ {
+ // Arrange
+ const UnityStr exposedPaths[] = {
+ "b1/b1_1/b1_1_1",
+ };
+ const int EXPOSED_COUNT = sizeof(exposedPaths)/sizeof(UnityStr);
+ MakeCharacter(exposedPaths, EXPOSED_COUNT);
+
+ // Act
+ OptimizeTransformHierarchy(*root, exposedPaths, EXPOSED_COUNT);
+
+ // Assert
+ Transform& rootTr = root->GetComponent(Transform);
+ Transform* mr1 = FindRelativeTransformWithPath(rootTr, "mr1");
+ Transform* b1_1_1 = FindRelativeTransformWithPath(rootTr, "b1_1_1");
+ CHECK(CompareApproximately(mr1->GetPosition(), Vector3f(3,1.5,0), Vector3f::epsilon));
+ CHECK(CompareApproximately(b1_1_1->GetPosition(), Vector3f(3,1,0), Vector3f::epsilon));
+ }
+
+ TEST_FIXTURE (CharacterTestFixture, OptimizeTransformHierarchy_Set_Animator_HasTransformHierarchy_False)
+ {
+ // Arrange
+ MakeCharacter();
+
+ // Act
+ OptimizeTransformHierarchy(*root);
+
+ // Assert
+ Animator& animator = root->GetComponent(Animator);
+ CHECK(!animator.GetHasTransformHierarchy());
+ }
+
+ TEST_FIXTURE (CharacterTestFixture, OptimizeTransformHierarchy_Set_Bones_And_RootBone_of_SkinnedMeshRenderers)
+ {
+ // Arrange
+ MakeCharacter();
+
+ // Act
+ OptimizeTransformHierarchy(*root);
+
+ // Assert
+ dynamic_array<SkinnedMeshRenderer*> skins;
+ GetComponentsInChildren(*root, true, ClassID(SkinnedMeshRenderer), reinterpret_cast<dynamic_array<Unity::Component*>&>(skins));
+ CHECK_EQUAL(2, skins.size());
+ for (int i = 0; i < skins.size(); i++)
+ {
+ SkinnedMeshRenderer& skin = *skins[i];
+ CHECK(NULL == skin.GetRootBone());
+ CHECK_EQUAL(0, skin.GetBones().size());
+ }
+ }
+
+ TEST_FIXTURE (CharacterTestFixture, DeoptimizeTransformHierarchy_Restore_Unstripped_Hierarchy)
+ {
+ // Arrange
+ MakeCharacter();
+
+ // Act
+ OptimizeTransformHierarchy(*root);
+ DeoptimizeTransformHierarchy(*root);
+
+ // Assert
+ Transform& rootTr = root->GetComponent(Transform);
+ for (int i = 0; i < BONE_COUNT; i++)
+ CHECK(FindRelativeTransformWithPath(rootTr, BONE_ARRAY[i].path) != NULL);
+ for (int i = 0; i < MESH_RENDERER_COUNT; i++)
+ CHECK(FindRelativeTransformWithPath(rootTr, MESH_RENDERER_ARRAY[i].path) != NULL);
+ for (int i = 0; i < SKINNED_MESH_RENDERER_COUNT; i++)
+ CHECK(FindRelativeTransformWithPath(rootTr, SKINNED_MESH_RENDERER_ARRAY[i].path) != NULL);
+ }
+
+ TEST_FIXTURE (CharacterTestFixture, DeoptimizeTransformHierarchy_Set_Bones_And_RootBone_of_SkinnedMeshRenderers)
+ {
+ // Arrange
+ MakeCharacter();
+
+ // Act
+ OptimizeTransformHierarchy(*root);
+ DeoptimizeTransformHierarchy(*root);
+
+ // Assert
+ dynamic_array<SkinnedMeshRenderer*> skins;
+ GetComponentsInChildren(*root, true, ClassID(SkinnedMeshRenderer), reinterpret_cast<dynamic_array<Unity::Component*>&>(skins));
+ CHECK_EQUAL(2, skins.size());
+ for (int i = 0; i < skins.size(); i++)
+ {
+ SkinnedMeshRenderer* skin = skins[i];
+ string boneNames("");
+ for (int b = 0; b < skin->GetBones().size(); b++)
+ boneNames += (skin->GetBones()[b]->GetName() + string(","));
+
+ if (skin->GetName() == "smr1")
+ {
+ CHECK(skin->GetRootBone()->GetName() == string("b1"));
+ CHECK(skin->GetBones().size() == 6);
+ CHECK(boneNames.find("b1") != string::npos);
+ CHECK(boneNames.find("b1_1") != string::npos);
+ CHECK(boneNames.find("b1_1_1") != string::npos);
+ CHECK(boneNames.find("b1_2") != string::npos);
+ CHECK(boneNames.find("b1_2_1") != string::npos);
+ CHECK(boneNames.find("b1_2_2") != string::npos);
+ }
+ else if (skin->GetName() == "smr2")
+ {
+ CHECK(skin->GetRootBone() == NULL);
+ CHECK(skin->GetBones().size() == 3);
+ CHECK(boneNames.find("b2_1_1") != string::npos);
+ CHECK(boneNames.find("b2_1_2") != string::npos);
+ CHECK(boneNames.find("b2_1_2_1") != string::npos);
+ }
+ }
+ }
+
+ TEST_FIXTURE (CharacterTestFixture, DeoptimizeTransformHierarchy_Restore_Transforms_With_Correct_TRS)
+ {
+ // Arrange
+ MakeCharacter();
+
+ // Act
+ OptimizeTransformHierarchy(*root);
+ DeoptimizeTransformHierarchy(*root);
+
+ // Assert
+ Transform& rootTr = root->GetComponent(Transform);
+ Transform* tobeStrip1 = FindRelativeTransformWithPath(rootTr, "b1/b1_2/b1_2_1/tobeStrip1");
+ Transform* b1_1_1 = FindRelativeTransformWithPath(rootTr, "b1/b1_1/b1_1_1");
+ Transform* mr2 = FindRelativeTransformWithPath(rootTr, "b1/b1_2/b1_2_1/mr2");
+ Transform* smr2 = FindRelativeTransformWithPath(rootTr, "b2/b2_1/smr2");
+ CHECK(CompareApproximately(tobeStrip1->GetPosition(), Vector3f(5,5,5), Vector3f::epsilon));
+ CHECK(CompareApproximately(b1_1_1->GetPosition(), Vector3f(3,1,0), Vector3f::epsilon));
+ CHECK(CompareApproximately(mr2->GetPosition(), Vector3f(2,2.5,0), Vector3f::epsilon));
+ CHECK(CompareApproximately(smr2->GetPosition(), Vector3f(9,9,9), Vector3f::epsilon));
+ }
+
+ TEST_FIXTURE (CharacterTestFixture, DeoptimizeTransformHierarchy_Set_Animator_HasTransformHierarchy_True)
+ {
+ // Arrange
+ MakeCharacter();
+
+ // Act
+ OptimizeTransformHierarchy(*root);
+ DeoptimizeTransformHierarchy(*root);
+
+ // Assert
+ Animator& animator = root->GetComponent(Animator);
+ CHECK(animator.GetHasTransformHierarchy());
+ }
+
+ TEST_FIXTURE (CharacterTestFixture, RemoveUnnecessaryTransforms_Keep_Skeleton)
+ {
+ // Arrange
+ MakeCharacter();
+
+ // Act
+ RemoveUnnecessaryTransforms(*root, NULL, NULL, 0, true);
+
+ // Assert
+ Transform& rootTr = root->GetComponent(Transform);
+ CHECK_EQUAL(BONE_COUNT+MESH_RENDERER_COUNT+SKINNED_MESH_RENDERER_COUNT-3, GetAllChildrenCount(rootTr));
+ }
+
+ TEST_FIXTURE (CharacterTestFixture, RemoveUnnecessaryTransforms_Not_Keep_Skeleton)
+ {
+ // Arrange
+ MakeCharacter();
+
+ // Act
+ RemoveUnnecessaryTransforms(*root, NULL, NULL, 0, false);
+
+ // Assert
+ Transform& rootTr = root->GetComponent(Transform);
+ CHECK_EQUAL(BONE_COUNT+MESH_RENDERER_COUNT+SKINNED_MESH_RENDERER_COUNT-3-4, GetAllChildrenCount(rootTr));
+ }
+
+ TEST_FIXTURE (CharacterTestFixture, RemoveUnnecessaryTransforms_Consider_HumanDescription)
+ {
+ // Arrange
+ MakeCharacter();
+ HumanBone hb;
+ hb.m_BoneName = "b1_2_2";
+ HumanDescription hd;
+ hd.m_Human.push_back(hb);
+
+ // Act
+ RemoveUnnecessaryTransforms(*root, &hd, NULL, 0, false);
+
+ // Assert
+ Transform& rootTr = root->GetComponent(Transform);
+ CHECK_EQUAL(BONE_COUNT+MESH_RENDERER_COUNT+SKINNED_MESH_RENDERER_COUNT-3-3, GetAllChildrenCount(rootTr));
+ }
+
+ TEST_FIXTURE (CharacterTestFixture, RemoveUnnecessaryTransforms_Expose_Certain_Transforms)
+ {
+ // Arrange
+ MakeCharacter();
+ const UnityStr exposedPaths[] = {
+ "b1/b1_2/b1_2_2",
+ "b2/b2_1/b2_1_2",
+ };
+ const int EXPOSED_COUNT = sizeof(exposedPaths)/sizeof(UnityStr);
+
+ // Act
+ RemoveUnnecessaryTransforms(*root, NULL, exposedPaths, EXPOSED_COUNT, false);
+
+ // Assert
+ Transform& rootTr = root->GetComponent(Transform);
+ CHECK_EQUAL(BONE_COUNT+MESH_RENDERER_COUNT+SKINNED_MESH_RENDERER_COUNT-3-2, GetAllChildrenCount(rootTr));
+ }
+
+ TEST_FIXTURE (CharacterTestFixture, GetUsefulTransformPaths)
+ {
+ // Arrange
+ MakeCharacter();
+
+ // Act
+ Transform& rootTr = root->GetComponent(Transform);
+ UNITY_VECTOR(kMemTempAlloc, UnityStr) outPaths;
+ GetUsefulTransformPaths(rootTr, rootTr, outPaths);
+
+ // Assert
+ CHECK_EQUAL(MESH_RENDERER_COUNT+SKINNED_MESH_RENDERER_COUNT, outPaths.size());
+ for (int i = 0; i < MESH_RENDERER_COUNT; ++i)
+ CHECK(std::find(outPaths.begin(), outPaths.end(), MESH_RENDERER_ARRAY[i].path) != outPaths.end());
+ for (int i = 0; i < SKINNED_MESH_RENDERER_COUNT; ++i)
+ CHECK(std::find(outPaths.begin(), outPaths.end(), SKINNED_MESH_RENDERER_ARRAY[i].path) != outPaths.end());
+ }
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/Animation/PPtrKeyframes.h b/Runtime/Animation/PPtrKeyframes.h
new file mode 100644
index 0000000..1620d7c
--- /dev/null
+++ b/Runtime/Animation/PPtrKeyframes.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "Runtime/BaseClasses/BaseObject.h"
+
+struct PPtrKeyframe
+{
+ float time;
+ PPtr<Object> value;
+
+ DECLARE_SERIALIZE(PPtrKeyframe)
+};
+typedef dynamic_array<PPtrKeyframe> PPtrKeyframes;
diff --git a/Runtime/Animation/RuntimeAnimatorController.cpp b/Runtime/Animation/RuntimeAnimatorController.cpp
new file mode 100644
index 0000000..c92e976
--- /dev/null
+++ b/Runtime/Animation/RuntimeAnimatorController.cpp
@@ -0,0 +1,66 @@
+#include "UnityPrefix.h"
+
+#include "RuntimeAnimatorController.h"
+#include "AnimationSetBinding.h"
+
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/Blobification/BlobWrite.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "AnimationClip.h"
+
+#if UNITY_EDITOR
+#include "Runtime/Scripting/Backend/ScriptingInvocation.h"
+#include "Runtime/Mono/MonoManager.h"
+#endif
+
+#include "Runtime/Scripting/Backend/ScriptingInvocation.h"
+#include "Runtime/Scripting/Scripting.h"
+
+IMPLEMENT_OBJECT_SERIALIZE (RuntimeAnimatorController)
+IMPLEMENT_CLASS(RuntimeAnimatorController)
+INSTANTIATE_TEMPLATE_TRANSFER(RuntimeAnimatorController)
+
+
+RuntimeAnimatorController::RuntimeAnimatorController(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode),
+ m_ObjectUsers(this),
+ m_DependencyList(this)
+{
+
+}
+
+RuntimeAnimatorController::~RuntimeAnimatorController()
+{
+ NotifyObjectUsers( kDidModifyAnimatorController );
+}
+
+
+template<class TransferFunction>
+void RuntimeAnimatorController::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+}
+
+
+void RuntimeAnimatorController::NotifyObjectUsers(const MessageIdentifier& msg)
+{
+ m_ObjectUsers.SendMessage(msg);
+}
+
+void RuntimeAnimatorController::RegisterAnimationClips()
+{
+ AnimationClipVector clips = GetAnimationClipsToRegister();
+ m_DependencyList.Clear();
+ m_DependencyList.Reserve(clips.size()); // Reserve space just for niceness
+ for(int i = 0 ; i < clips.size() ; i++)
+ {
+ AnimationClip* clip = clips[i];
+ if (clip)
+ {
+ // We could do this either way, adding is symmetrical
+ clip->GetUserList().AddUser(m_DependencyList);
+ }
+ }
+}
+
diff --git a/Runtime/Animation/RuntimeAnimatorController.h b/Runtime/Animation/RuntimeAnimatorController.h
new file mode 100644
index 0000000..058891a
--- /dev/null
+++ b/Runtime/Animation/RuntimeAnimatorController.h
@@ -0,0 +1,81 @@
+#ifndef AVATARCONTROLLER_H
+#define AVATARCONTROLLER_H
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/BaseClasses/MessageIdentifier.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/Animation/MecanimUtility.h"
+#include "Runtime/mecanim/animation/avatar.h"
+
+#include "Runtime/Misc/UserList.h"
+
+template<class T>
+class PPtr;
+class AnimationClip;
+class StateMachine;
+class AvatarMask;
+
+class RuntimeAnimatorController;
+
+typedef std::vector<PPtr<AnimationClip> > AnimationClipVector;
+
+namespace UnityEngine
+{
+ namespace Animation
+ {
+ struct AnimationSetBindings;
+ }
+}
+
+namespace mecanim
+{
+ namespace animation
+ {
+ struct ControllerConstant;
+ }
+}
+
+class RuntimeAnimatorController : public NamedObject
+{
+
+public:
+
+ REGISTER_DERIVED_ABSTRACT_CLASS (RuntimeAnimatorController, NamedObject)
+ DECLARE_OBJECT_SERIALIZE (RuntimeAnimatorController)
+
+ RuntimeAnimatorController(MemLabelId label, ObjectCreationMode mode);
+
+
+ static void InitializeClass ();
+ static void CleanupClass () {}
+
+ virtual mecanim::animation::ControllerConstant* GetAsset() = 0 ;
+ virtual UnityEngine::Animation::AnimationSetBindings* GetAnimationSetBindings() = 0 ;
+
+ virtual AnimationClipVector GetAnimationClips() const = 0;
+
+ virtual std::string StringFromID(unsigned int ID) const = 0;
+
+ void AddObjectUser( UserList& user ) { m_ObjectUsers.AddUser(user); }
+ void AddObjectUser( UserListNode& user ) { m_ObjectUsers.AddUser(user); }
+
+ void NotifyObjectUsers(const MessageIdentifier& msg);
+ UserList& GetUserList () { return m_ObjectUsers; }
+
+
+protected:
+
+ virtual void RegisterAnimationClips() ;
+
+ UserList m_ObjectUsers; // for animatorControllers
+ UserList m_DependencyList; // for animationclips
+
+private:
+
+ virtual AnimationClipVector GetAnimationClipsToRegister() const = 0;
+
+
+};
+
+
+#endif
diff --git a/Runtime/Animation/ScriptBindings/Animations.txt b/Runtime/Animation/ScriptBindings/Animations.txt
new file mode 100644
index 0000000..ddeb57e
--- /dev/null
+++ b/Runtime/Animation/ScriptBindings/Animations.txt
@@ -0,0 +1,721 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Animation/Animation.h"
+#include "Runtime/Animation/AnimationClip.h"
+#include "Runtime/Animation/AnimationManager.h"
+#include "Runtime/Animation/AnimationState.h"
+#include "Runtime/Animation/Animator.h"
+#include "Runtime/Animation/Motion.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Animation/AnimationCurveUtility.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+#include "Runtime/mecanim/human/human.h"
+#include "Runtime/mecanim/generic/crc32.h"
+#include "Runtime/Animation/AnimationUtility.h"
+#include "Runtime/Scripting/Scripting.h"
+
+using namespace Unity;
+
+CSRAW
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+// Determines how time is treated outside of the keyframed range of an [[AnimationClip]] or [[AnimationCurve]].
+ENUM WrapMode
+ // When time reaches the end of the animation clip, the clip will automatically stop playing and time will be reset to beginning of the clip.
+ Once = 1,
+
+ // When time reaches the end of the animation clip, time will continue at the beginning.
+ Loop = 2,
+
+ // When time reaches the end of the animation clip, time will ping pong back between beginning and end.
+ PingPong = 4,
+
+ // Reads the default repeat mode set higher up.
+ Default = 0,
+
+ // Plays back the animation. When it reaches the end, it will keep playing the last frame and never stop playing.
+ ClampForever = 8,
+
+ //*undocumented*
+ Clamp = 1,
+END
+
+// AnimationEvent lets you call a script function similar to SendMessage as part of playing back an animation.
+CSRAW [StructLayout (LayoutKind.Sequential)]
+CLASS AnimationEvent
+ CSRAW [NotRenamed]
+ CSRAW internal IntPtr m_Ptr;
+ CSRAW int m_OwnsData;
+
+ C++RAW
+
+ struct AnimationEventMono
+ {
+ AnimationEvent* m_Ptr;
+ int m_OwnsData;
+ };
+
+ C++RAW
+ void VerifyReadOnly (ScriptingObjectWithIntPtrField<AnimationEvent>& self)
+ {
+#if ENABLE_MONO
+ if (ExtractMonoObjectData<AnimationEventMono> (self.object).m_OwnsData != 1)
+ Scripting::RaiseMonoException("AnimationEvents sent by an Animation Event callback may not modify the AnimationEvent data");
+#endif
+ }
+
+ C++RAW
+ inline AnimationEvent* GetAnimationEvent (ScriptingObjectWithIntPtrField<AnimationEvent>& self)
+ {
+ AnimationEvent* event = self.GetPtr();
+ if (!event)
+ Scripting::RaiseNullException("Animation Event is out of scope");
+
+ return event;
+ }
+
+ // Creates a new animation event
+ CSRAW public AnimationEvent ()
+ {
+ m_OwnsData = 1;
+ Create();
+ }
+
+ THREAD_SAFE
+ CUSTOM private void Create ()
+ {
+ self.SetPtr(new AnimationEvent());
+ }
+
+ CSRAW ~AnimationEvent ()
+ {
+ if (m_OwnsData != 0)
+ Destroy();
+ }
+
+ THREAD_SAFE
+ CUSTOM private void Destroy ()
+ {
+ delete self.GetPtr();
+ }
+
+ OBSOLETE warning Use stringParameter instead
+ CUSTOM_PROP string data { return scripting_string_new(GetAnimationEvent(self)->stringParameter); } { VerifyReadOnly(self); GetAnimationEvent(self)->stringParameter = value.AsUTF8(); }
+
+ // String parameter that is stored in the event and will be sent to the function.
+ CUSTOM_PROP string stringParameter { return scripting_string_new(GetAnimationEvent(self)->stringParameter); } { VerifyReadOnly(self); GetAnimationEvent(self)->stringParameter = value.AsUTF8(); }
+
+ // Float parameter that is stored in the event and will be sent to the function.
+ CUSTOM_PROP float floatParameter { return GetAnimationEvent(self)->floatParameter; } { VerifyReadOnly(self); GetAnimationEvent(self)->floatParameter = value; }
+
+ // int parameter that is stored in the event and will be sent to the function.
+ CUSTOM_PROP int intParameter { return GetAnimationEvent(self)->intParameter; } { VerifyReadOnly(self); GetAnimationEvent(self)->intParameter = value; }
+
+ // Object reference parameter that is stored in the event and will be sent to the function.
+ CUSTOM_PROP Object objectReferenceParameter { return Scripting::ScriptingWrapperFor(GetAnimationEvent(self)->objectReferenceParameter); } { VerifyReadOnly(self); GetAnimationEvent(self)->objectReferenceParameter = (Object*)value; }
+
+ // The name of the function that will be called.
+ CUSTOM_PROP string functionName { return scripting_string_new(GetAnimationEvent(self)->functionName); } { VerifyReadOnly(self);GetAnimationEvent(self)->functionName = value.AsUTF8(); }
+
+ // The time at which the event will be fired off.
+ CUSTOM_PROP float time { return GetAnimationEvent(self)->time; } { VerifyReadOnly(self); GetAnimationEvent(self)->time = value; }
+
+ // Function call options.
+ CUSTOM_PROP SendMessageOptions messageOptions { return GetAnimationEvent(self)->messageOptions; } { VerifyReadOnly(self); GetAnimationEvent(self)->messageOptions = value; }
+
+ // The animation state that fired this event (RO).
+ CUSTOM_PROP AnimationState animationState
+ {
+ return TrackedReferenceBaseToScriptingObject(GetAnimationEvent(self)->stateSender, animationState);
+ }
+
+END
+
+// Stores keyframe based animations.
+CLASS AnimationClip : Motion
+
+ // Creates a new animation clip
+ CSRAW public AnimationClip()
+ {
+ Internal_CreateAnimationClip(this);
+ }
+
+ CUSTOM private static void Internal_CreateAnimationClip ([Writable]AnimationClip self)
+ {
+ Object* animClip = NEW_OBJECT(AnimationClip);
+ animClip->Reset();
+ Scripting::ConnectScriptingWrapperToObject (self.GetScriptingObject(), animClip);
+ animClip->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+ }
+
+ // Animation length in seconds (RO)
+ CUSTOM_PROP float length { return self->GetRange ().second; }
+
+
+ CUSTOM_PROP internal float startTime { return self->GetRange ().first; }
+ CUSTOM_PROP internal float stopTime { return self->GetRange ().second; }
+
+
+ // Frame rate at which keyframes are sampled (RO)
+ AUTO_PROP float frameRate GetSampleRate SetSampleRate
+
+
+ // Assigns the curve to animate a specific property.
+ CUSTOM void SetCurve (string relativePath, Type type, string propertyName, AnimationCurve curve)
+ {
+#if ENABLE_MONO
+ Scripting::RaiseIfNull(type);
+ MonoClass* klass = GetScriptingTypeRegistry().GetType(type);
+ MonoScript* script = NULL;
+ int classID = Scripting::GetClassIDFromScriptingClass(klass);
+ if (classID == ClassID(MonoBehaviour))
+ {
+ script = GetMonoScriptManager().FindRuntimeScript(klass);
+ if (script == NULL)
+ {
+ ErrorString("The script class couldn't be found");
+ return;
+ }
+ }
+
+ self->SetCurve(relativePath, classID, script, propertyName, curve.GetPtr(), true);
+#endif
+ }
+
+ //*undocumented*
+ AUTO void EnsureQuaternionContinuity();
+
+ // Clears all curves from the clip.
+ AUTO void ClearCurves();
+
+ // Sets the default wrap mode used in the animation state.
+ AUTO_PROP WrapMode wrapMode GetWrapMode SetWrapMode
+
+ // Adds an animation event to the clip.
+ CUSTOM void AddEvent (AnimationEvent evt)
+ {
+ Scripting::RaiseIfNull(evt.GetPtr());
+ self->AddRuntimeEvent(*GetAnimationEvent(evt));
+ }
+
+ // AABB of this Animation Clip in local space of Animation component that it is attached too.
+ AUTO_PROP Bounds localBounds GetBounds SetBounds
+
+END
+
+// A single keyframe that can be injected into an animation curve.
+STRUCT Keyframe
+ CSRAW float m_Time;
+ CSRAW float m_Value;
+ CSRAW float m_InTangent;
+ CSRAW float m_OutTangent;
+
+ CSRAW
+ #if UNITY_EDITOR
+ int m_TangentMode;
+ #endif
+
+ // Create a keyframe.
+ CSRAW public Keyframe (float time, float value)
+ {
+ m_Time = time;
+ m_Value = value;
+ m_InTangent = 0;
+ m_OutTangent = 0;
+ #if UNITY_EDITOR
+ m_TangentMode = 0;
+ #endif
+ }
+
+ // Create a keyframe.
+ CSRAW public Keyframe (float time, float value, float inTangent, float outTangent)
+ {
+ m_Time = time;
+ m_Value = value;
+ m_InTangent = inTangent;
+ m_OutTangent = outTangent;
+ #if UNITY_EDITOR
+ m_TangentMode = 0;
+ #endif
+ }
+
+ // The time of the keyframe.
+ CSRAW public float time { get { return m_Time; } set { m_Time = value; } }
+
+ // The value of the curve at keyframe.
+ CSRAW public float value { get { return m_Value; } set { m_Value = value; } }
+
+ // Describes the tangent when approaching this point from the previous point in the curve.
+ CSRAW public float inTangent { get { return m_InTangent; } set { m_InTangent = value; } }
+
+ // Describes the tangent when leaving this point towards the next point in the curve.
+ CSRAW public float outTangent { get { return m_OutTangent; } set { m_OutTangent = value; } }
+
+ // The tangent mode of the keyframe.
+ // This is used only in the editor and will always return 0 in the player.
+ CSRAW public int tangentMode
+ {
+ get {
+ #if UNITY_EDITOR
+ return m_TangentMode;
+ #else
+ return 0;
+ #endif
+ }
+ set {
+ #if UNITY_EDITOR
+ m_TangentMode = value;
+ #endif
+ }
+ }
+
+END
+
+CSRAW #pragma warning disable 414
+
+// An animation curve. Lets you add keyframes and evaluate the curve at a given time.
+C++RAW
+ static void CleanupAnimationCurve(void* animationCurve){ delete ((AnimationCurve*)animationCurve); };
+// A collection of curves form an [[AnimationClip]].
+CSRAW [StructLayout (LayoutKind.Sequential)]
+THREAD_SAFE
+CLASS AnimationCurve
+ CSRAW
+ internal IntPtr m_Ptr;
+
+ THREAD_SAFE
+ CUSTOM private void Cleanup () { CleanupAnimationCurve(self.GetPtr()); }
+
+ CSRAW
+ ~AnimationCurve()
+ {
+ Cleanup ();
+ }
+
+ // Evaluate the curve at /time/.
+ CUSTOM float Evaluate (float time)
+ {
+ return self->Evaluate(time);
+ }
+
+ // All keys defined in the animation curve.
+ CSRAW public Keyframe[] keys { get { return GetKeys(); } set { SetKeys(value); } }
+
+ // Add a new key to the curve.
+ CUSTOM int AddKey (float time, float value) { return AddKeySmoothTangents(*self, time, value); }
+
+ // Add a new key to the curve.
+ CSRAW public int AddKey (Keyframe key) { return AddKey_Internal(key); }
+
+ CUSTOM private int AddKey_Internal (Keyframe key) { return self->AddKey (key); }
+
+ // Removes the keyframe at /index/ and inserts key.
+ CUSTOM int MoveKey (int index, Keyframe key)
+ {
+ if (index >= 0 && index < self->GetKeyCount())
+ return MoveCurveKey(*self, index, key);
+ else {
+ Scripting::RaiseOutOfRangeException("");
+ return 0;
+ }
+ }
+
+ // Removes a key
+ CUSTOM void RemoveKey (int index)
+ {
+ if (index >= 0 && index < self->GetKeyCount())
+ self->RemoveKeys(self->begin() + index, self->begin() + index + 1);
+ else
+ Scripting::RaiseOutOfRangeException("");
+ }
+
+ // Retrieves the key at index (RO)
+ CSRAW public Keyframe this [int index]
+ {
+ get { return GetKey_Internal(index); }
+ }
+
+ // The number of keys in the curve (RO)
+ CUSTOM_PROP int length { return self->GetKeyCount(); }
+
+ // Replace all keyframes with the /keys/ array.
+ CUSTOM private void SetKeys (Keyframe[] keys)
+ {
+ KeyframeTpl<float>* first = Scripting::GetScriptingArrayStart<KeyframeTpl<float> > (keys);
+ self->Assign(first, first + GetScriptingArraySize(keys));
+ self->Sort();
+ }
+
+ CUSTOM private Keyframe GetKey_Internal (int index)
+ {
+ if (index >= 0 && index < self->GetKeyCount())
+ {
+ return self->GetKey(index);
+ }
+ else
+ {
+ Scripting::RaiseOutOfRangeException("");
+ return KeyframeTpl<float>();
+ }
+ }
+
+ CUSTOM private Keyframe[] GetKeys ()
+ {
+ if (self->GetKeyCount() <= 0)
+ return CreateEmptyStructArray(MONO_COMMON.keyframe);
+ return CreateScriptingArray(&self->GetKey(0), self->GetKeyCount(), MONO_COMMON.keyframe);
+ }
+
+ // Smooth the in and out tangents of the keyframe at /index/.
+ CUSTOM void SmoothTangents (int index, float weight)
+ {
+ if (index >= 0 && index < self->GetKeyCount())
+ RecalculateSplineSlope(*self, index, weight);
+ else
+ Scripting::RaiseOutOfRangeException("");
+ }
+
+ // A straight Line starting at /timeStart/, /valueStart/ and ending at /timeEnd/, /valueEnd/
+ CSRAW public static AnimationCurve Linear (float timeStart, float valueStart, float timeEnd, float valueEnd)
+ {
+ float tangent = (valueEnd - valueStart) / (timeEnd - timeStart);
+ Keyframe[] keys = { new Keyframe(timeStart, valueStart, 0.0F, tangent), new Keyframe(timeEnd, valueEnd, tangent, 0.0F) };
+ return new AnimationCurve(keys);
+ }
+
+ // An ease-in and out curve starting at /timeStart/, /valueStart/ and ending at /timeEnd/, /valueEnd/.
+ CSRAW public static AnimationCurve EaseInOut (float timeStart, float valueStart, float timeEnd, float valueEnd)
+ {
+ Keyframe[] keys = { new Keyframe(timeStart, valueStart, 0.0F, 0.0F), new Keyframe(timeEnd, valueEnd, 0.0F, 0.0F) };
+ return new AnimationCurve(keys);
+ }
+
+ // The behaviour of the animation before the first keyframe
+ CUSTOM_PROP WrapMode preWrapMode { return self->GetPreInfinity(); } { self->SetPreInfinity(value); }
+ // The behaviour of the animation after the last keyframe
+ CUSTOM_PROP WrapMode postWrapMode { return self->GetPostInfinity(); } { self->SetPostInfinity(value); }
+
+ // Creates an animation curve from arbitrary number of keyframes.
+ CSRAW public AnimationCurve (params Keyframe[] keys) { Init(keys); }
+
+ CONDITIONAL UNITY_FLASH || UNITY_WINRT
+ // *undocumented*
+ CSRAW public AnimationCurve(IntPtr nativeptr) { m_Ptr = nativeptr; }
+
+ // Creates an empty animation curve
+ CSRAW public AnimationCurve () { Init(null); }
+
+ THREAD_SAFE
+ CUSTOM private void Init (Keyframe[] keys)
+ {
+ self.SetPtr(new AnimationCurve(), CleanupAnimationCurve);
+ #if UNITY_WINRT
+ if (keys != SCRIPTING_NULL) AnimationCurve_CUSTOM_SetKeys(self.object, keys);
+ #else
+ if (keys != SCRIPTING_NULL) AnimationCurve_CUSTOM_SetKeys(self, keys);
+ #endif
+ }
+
+END
+
+CSRAW #pragma warning restore 414
+
+
+// Used by Animation.Play function.
+ENUM PlayMode
+ // Will stop all animations that were started in the same layer. This is the default when playing animations.
+ StopSameLayer = 0,
+ // Will stop all animations that were started with this component before playing
+ StopAll = 4,
+END
+
+// Used by Animation.Play function.
+ENUM QueueMode
+ // Will start playing after all other animations have stopped playing
+ CompleteOthers = 0,
+
+ // Starts playing immediately. This can be used if you just want to quickly create a duplicate animation.
+ PlayNow = 2
+END
+
+// Used by Animation.Play function.
+ENUM AnimationBlendMode
+ // Animations will be blended
+ Blend = 0,
+ // Animations will be added
+ Additive = 1
+END
+
+
+//*undocumented* - deprecated
+CSRAW public enum AnimationPlayMode { Stop = 0, Queue = 1, Mix = 2 }
+
+// This enum controlls culling of Animation component.
+ENUM AnimationCullingType
+ // Animation culling is disabled - object is animated even when offscreen.
+ AlwaysAnimate = 0,
+
+ // Animation is disabled when renderers are not visible.
+ BasedOnRenderers = 1,
+
+ // Animation is disabled when Animation::ref::localBounds are not visible.
+ BasedOnClipBounds = 2,
+
+ // Animation is disabled when Animation::ref::localBounds are not visible.
+ BasedOnUserBounds = 3
+END
+
+// The animation component is used to play back animations.
+CLASS Animation : Behaviour, IEnumerable
+ // The default animation.
+ AUTO_PTR_PROP AnimationClip clip GetClip SetClip
+
+ // Should the default animation clip (Animation.clip) automatically start playing on startup.
+ AUTO_PROP bool playAutomatically GetPlayAutomatically SetPlayAutomatically
+
+ // How should time beyond the playback range of the clip be treated?
+ AUTO_PROP WrapMode wrapMode GetWrapMode SetWrapMode
+
+ // Stops all playing animations that were started with this Animation.
+ AUTO void Stop ();
+
+ // Stops an animation named /name/.
+ CSRAW public void Stop (string name) { Internal_StopByName(name); }
+ CUSTOM private void Internal_StopByName (string name) { return self->Stop (name); }
+
+ // Rewinds the animation named /name/.
+ CSRAW public void Rewind (string name) { Internal_RewindByName(name); }
+ CUSTOM private void Internal_RewindByName (string name) { self->Rewind(name); }
+
+ // Rewinds all animations
+ AUTO void Rewind ();
+
+ // Samples animations at the current state.
+ AUTO void Sample ();
+
+ // Are we playing any animations?
+ AUTO_PROP bool isPlaying IsPlaying
+
+ // Is the animation named /name/ playing?
+ CUSTOM bool IsPlaying (string name) { return self->IsPlaying (name); }
+
+
+ // Returns the animation state named /name/.
+ CSRAW public AnimationState this [string name]
+ {
+ get { return GetState(name); }
+ }
+
+ /// *listonly*
+ CSRAW public bool Play (PlayMode mode = PlayMode.StopSameLayer) { return PlayDefaultAnimation (mode); }
+
+ // Plays animation without any blending.
+ CUSTOM bool Play (string animation, PlayMode mode = PlayMode.StopSameLayer) { return self->Play(animation, mode); }
+
+ // Fades the animation with name /animation/ in over a period of /time/ seconds and fades other animations out.
+ CUSTOM void CrossFade (string animation, float fadeLength = 0.3F, PlayMode mode = PlayMode.StopSameLayer) { self->CrossFade(animation, fadeLength, mode); }
+
+ // Blends the animation named /animation/ towards /targetWeight/ over the next /time/ seconds.
+ CUSTOM void Blend (string animation, float targetWeight = 1.0F, float fadeLength = 0.3F) { self->Blend(animation, targetWeight, fadeLength); }
+
+
+ // Cross fades an animation after previous animations has finished playing.
+ CUSTOM AnimationState CrossFadeQueued (string animation, float fadeLength = 0.3F, QueueMode queue = QueueMode.CompleteOthers, PlayMode mode = PlayMode.StopSameLayer)
+ {
+ AnimationState* as = self->QueueCrossFade(animation, fadeLength, queue, mode);
+ return TrackedReferenceBaseToScriptingObject(as,animationState);
+ }
+
+
+ // Plays an animation after previous animations has finished playing.
+ CUSTOM AnimationState PlayQueued (string animation, QueueMode queue = QueueMode.CompleteOthers, PlayMode mode = PlayMode.StopSameLayer)
+ {
+ AnimationState* as = self->QueuePlay(animation, queue, mode);
+ return TrackedReferenceBaseToScriptingObject(as, animationState);
+ }
+
+
+ // Adds a /clip/ to the animation with name /newName/.
+ CSRAW public void AddClip (AnimationClip clip, string newName) { AddClip (clip, newName, Int32.MinValue, Int32.MaxValue); }
+
+ // Adds /clip/ to the only play between /firstFrame/ and /lastFrame/. The new clip will also be added to the animation with name /newName/.
+ CUSTOM void AddClip (AnimationClip clip, string newName, int firstFrame, int lastFrame, bool addLoopFrame = false) { self->AddClip(*clip, newName, firstFrame, lastFrame, addLoopFrame); }
+
+ // Remove clip from the animation list.
+ CUSTOM void RemoveClip (AnimationClip clip) { self->RemoveClip (*clip); }
+
+ // Remove clip from the animation list.
+ CSRAW public void RemoveClip (string clipName) { RemoveClip2(clipName); }
+
+ // Get the number of clips currently assigned to this animation
+ CUSTOM int GetClipCount () { return self->GetClipCount(); }
+
+ CUSTOM private void RemoveClip2 (string clipName) { self->RemoveClip (clipName); }
+
+ CUSTOM private bool PlayDefaultAnimation (PlayMode mode) { return self->Play(mode); }
+
+ //*undocumented* deprecated
+ OBSOLETE warning use PlayMode instead of AnimationPlayMode.
+ CSRAW public bool Play (AnimationPlayMode mode) { return PlayDefaultAnimation((PlayMode)mode); }
+ //*undocumented* deprecated
+ OBSOLETE warning use PlayMode instead of AnimationPlayMode.
+ CSRAW public bool Play (string animation, AnimationPlayMode mode) { return Play(animation, (PlayMode)mode); }
+
+
+ // Synchronizes playback speed of all animations in the /layer/.
+ AUTO void SyncLayer(int layer);
+
+ //*undocumented* Documented separately
+ CSRAW public IEnumerator GetEnumerator ()
+ {
+ return new Animation.Enumerator (this);
+ }
+ //*undocumented*
+ CLASS private Enumerator : IEnumerator
+ CSRAW
+ private Animation m_Outer;
+ private int m_CurrentIndex = -1;
+
+ internal Enumerator (Animation outer) { m_Outer = outer; }
+ //*undocumented*
+ public object Current
+ {
+ get { return m_Outer.GetStateAtIndex (m_CurrentIndex); }
+ }
+
+ //*undocumented*
+ public bool MoveNext ()
+ {
+ int childCount = m_Outer.GetStateCount();
+ m_CurrentIndex++;
+ return m_CurrentIndex < childCount;
+ }
+
+ //*undocumented*
+ public void Reset () { m_CurrentIndex = -1; }
+ END
+
+ CUSTOM internal AnimationState GetState(string name)
+ {
+ AnimationState* state = self->GetState(name);
+ return TrackedReferenceBaseToScriptingObject(state, animationState);
+ }
+
+ CUSTOM internal AnimationState GetStateAtIndex (int index)
+ {
+
+ Animation& selfRef = *self;
+ if (index >= 0 || index < selfRef.GetAnimationStateCount())
+ {
+ return TrackedReferenceBaseToScriptingObject(&selfRef.GetAnimationStateAtIndex (index), animationState);
+ }
+ Scripting::RaiseMonoException("Animation State out of bounds!");
+ return SCRIPTING_NULL;
+ }
+
+ CUSTOM internal int GetStateCount () { return self->GetAnimationStateCount(); }
+
+ OBSOLETE planned Returns the animation clip named /name/.
+ CSRAW public AnimationClip GetClip (string name) {
+ AnimationState state = GetState(name);
+ if (state)
+ return state.clip;
+ else
+ return null;
+ }
+
+
+ // When turned on, animations will be executed in the physics loop. This is only useful in conjunction with kinematic rigidbodies.
+ AUTO_PROP bool animatePhysics GetAnimatePhysics SetAnimatePhysics
+
+ // When turned on, Unity might stop animating if it thinks that the results of the animation won't be visible to the user.
+ OBSOLETE warning Use cullingType instead
+ CUSTOM_PROP bool animateOnlyIfVisible
+ {
+ Animation::CullingType type = self->GetCullingType();
+ AssertMsg(type == Animation::kCulling_AlwaysAnimate || type == Animation::kCulling_BasedOnRenderers,
+ "Culling type %d cannot be converted to animateOnlyIfVisible. animateOnlyIfVisible is obsolete, please use cullingType instead.", type);
+ return type == Animation::kCulling_BasedOnRenderers;
+ }
+ {
+ self->SetCullingType(value ? Animation::kCulling_BasedOnRenderers : Animation::kCulling_AlwaysAnimate);
+ }
+
+ // Controls culling of this Animation component.
+ AUTO_PROP AnimationCullingType cullingType GetCullingType SetCullingType
+
+ // AABB of this Animation animation component in local space.
+ AUTO_PROP Bounds localBounds GetLocalAABB SetLocalAABB
+
+END
+
+// The AnimationState gives full control over animation blending.
+CLASS AnimationState : TrackedReference
+
+ // Enables / disables the animation.
+
+ AUTO_PROP bool enabled GetEnabled SetEnabled
+
+ // The weight of animation
+ AUTO_PROP float weight GetWeight SetWeight
+
+ // Wrapping mode of the animation.
+ AUTO_PROP WrapMode wrapMode GetWrapMode SetWrapMode
+
+ // The current time of the animation
+ AUTO_PROP float time GetTime SetTime
+
+ // The normalized time of the animation.
+ AUTO_PROP float normalizedTime GetNormalizedTime SetNormalizedTime
+
+ // The playback speed of the animation. 1 is normal playback speed.
+ AUTO_PROP float speed GetSpeed SetSpeed
+
+ // The normalized playback speed.
+ AUTO_PROP float normalizedSpeed GetNormalizedSpeed SetNormalizedSpeed
+
+ // The length of the animation clip in seconds.
+ AUTO_PROP float length GetLength
+
+ // The layer of the animation. When calculating the final blend weights, animations in higher layers will get their weights
+ AUTO_PROP int layer GetLayer SetLayer
+
+ // The clip that is being played by this animation state.
+ AUTO_PTR_PROP AnimationClip clip GetClip
+
+ // Adds a transform which should be animated. This allows you to reduce the number of animations you have to create.
+ CUSTOM void AddMixingTransform (Transform mix, bool recursive = true) { self->AddMixingTransform(*mix, recursive); }
+
+ // Removes a transform which should be animated.
+ CUSTOM void RemoveMixingTransform (Transform mix) { self->RemoveMixingTransform(*mix); }
+
+ // The name of the animation
+ CUSTOM_PROP string name { return scripting_string_new(self->GetName()); } { self->SetName(value.AsUTF8()); }
+
+ // Which blend mode should be used?
+ AUTO_PROP AnimationBlendMode blendMode GetBlendMode SetBlendMode
+END
+
+CLASS GameObject : Object
+
+ // Samples an animation at a given time for any animated properties.
+ CUSTOM void SampleAnimation (AnimationClip animation, float time) { SampleAnimation (*self, *animation, time, animation->GetWrapMode()); }
+
+END
+
+
+CSRAW }
diff --git a/Runtime/Animation/ScriptBindings/AnimatorBindings.txt b/Runtime/Animation/ScriptBindings/AnimatorBindings.txt
new file mode 100644
index 0000000..d729bc7
--- /dev/null
+++ b/Runtime/Animation/ScriptBindings/AnimatorBindings.txt
@@ -0,0 +1,677 @@
+
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Animation/Animator.h"
+#include "Runtime/Animation/RuntimeAnimatorController.h"
+#include "Runtime/Animation/Avatar.h"
+#include "Runtime/mecanim/human/human.h"
+#include "Runtime/mecanim/generic/crc32.h"
+#include "Runtime/Animation/AnimationClip.h"
+#include "Runtime/Animation/AnimatorOverrideController.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Scripting.h"
+
+using namespace Unity;
+
+CSRAW
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+// Target
+ENUM AvatarTarget
+ // The root, the position of the game object
+ Root = 0,
+ // The body, center of mass
+ Body = 1,
+ // The left foot
+ LeftFoot = 2,
+ // The right foot
+ RightFoot = 3,
+ // The left hand
+ LeftHand = 4,
+ // The right hand
+ RightHand = 5,
+END
+
+// IK Goal
+ENUM AvatarIKGoal
+ // The left foot
+ LeftFoot = 0,
+ // The right foot
+ RightFoot = 1,
+ // The left hand
+ LeftHand = 2,
+ // The right hand
+ RightHand = 3
+END
+
+// Information about what animation clips is played and its weight
+STRUCT AnimationInfo
+
+ // Animation clip that is played
+ CSRAW public AnimationClip clip { get {return m_ClipInstanceID != 0 ? ClipInstanceToScriptingObject(m_ClipInstanceID) : null; } }
+
+ // The weight of the animation clip
+ CSRAW public float weight { get { return m_Weight;}}
+
+ CUSTOM private static AnimationClip ClipInstanceToScriptingObject(int instanceID)
+ {
+ return Scripting::ScriptingWrapperFor(PPtr<AnimationClip>(instanceID));
+ }
+
+ CSRAW private int m_ClipInstanceID;
+ CSRAW private float m_Weight;
+END
+
+C++RAW
+
+struct MonoAnimationInfo
+{
+ int clipInstanceID;
+ float weight;
+};
+
+void AnimationInfoToMono ( const AnimationInfo& src, MonoAnimationInfo &dest)
+{
+ dest.clipInstanceID = src.clip.IsValid() ? src.clip.GetInstanceID() : 0;
+ dest.weight = src.weight;
+}
+
+static int ScriptingStringToCRC32 (ICallString& stringValue);
+
+
+// Culling mode for the Animator
+ENUM AnimatorCullingMode
+
+ // Always animate the entire character. Object is animated even when offscreen.
+ AlwaysAnimate = 0,
+
+ // Animation is disabled when renderers are not visible.
+ BasedOnRenderers = 1
+END
+
+// Information about the current or next state
+STRUCT AnimatorStateInfo
+
+ // Does /name/ match the name of the active state in the statemachine.
+ CSRAW public bool IsName (string name) { int hash = Animator.StringToHash (name); return hash == m_Name || hash == m_Path; }
+
+ // For backwards compatibility this is actually the path...
+ // In the future it would be good to come up with a new name for the name & path.
+ CSRAW public int nameHash { get { return m_Path; } }
+
+ // Normalized time of the State
+ CSRAW public float normalizedTime { get { return m_NormalizedTime; } }
+
+ // Current duration of the state
+ CSRAW public float length { get { return m_Length; } }
+
+ // The Tag of the State
+ CSRAW public int tagHash { get { return m_Tag; } }
+
+ // Does /tag/ match the tag of the active state in the statemachine.
+ CSRAW public bool IsTag (string tag) { return Animator.StringToHash (tag) == m_Tag; }
+
+ // Is the state looping
+ CSRAW public bool loop { get { return m_Loop != 0;} }
+
+ CSRAW private int m_Name;
+ CSRAW private int m_Path;
+ CSRAW private float m_NormalizedTime;
+ CSRAW private float m_Length;
+ CSRAW private int m_Tag;
+ CSRAW private int m_Loop;
+END
+
+// Information about the current transition
+STRUCT AnimatorTransitionInfo
+
+ // Does /name/ match the name of the active Transition.
+ CSRAW public bool IsName (string name) { return Animator.StringToHash (name) == m_Name ; }
+
+ // Does /userName/ match the name of the active Transition.
+ CSRAW public bool IsUserName (string name) { return Animator.StringToHash (name) == m_UserName ; }
+
+ // The unique name of the Transition
+ CSRAW public int nameHash { get { return m_Name; } }
+
+ // The user-specidied name of the Transition
+ CSRAW public int userNameHash { get { return m_UserName; } }
+
+ // Normalized time of the Transition
+ CSRAW public float normalizedTime { get { return m_NormalizedTime; } }
+
+
+ CSRAW private int m_Name;
+ CSRAW private int m_UserName;
+ CSRAW private float m_NormalizedTime;
+
+END
+
+
+
+// To specify position and rotation weight mask for Animator::MatchTarget
+STRUCT MatchTargetWeightMask
+
+ // MatchTargetWeightMask contructor
+ CSRAW public MatchTargetWeightMask(Vector3 positionXYZWeight, float rotationWeight)
+ {
+ m_PositionXYZWeight = positionXYZWeight;
+ m_RotationWeight = rotationWeight;
+ }
+
+ // Position XYZ weight
+ CSRAW public Vector3 positionXYZWeight
+ {
+ get { return m_PositionXYZWeight;}
+ set { m_PositionXYZWeight = value; }
+ }
+
+ // Rotation weight
+ CSRAW public float rotationWeight
+ {
+ get { return m_RotationWeight;}
+ set { m_RotationWeight =value;}
+ }
+
+ CSRAW private Vector3 m_PositionXYZWeight;
+ CSRAW private float m_RotationWeight;
+END
+
+C++RAW
+ enum HumanBodyBones { Hips=0, LeftUpperLeg, RightUpperLeg, LeftLowerLeg, RightLowerLeg, LeftFoot, RightFoot, Spine, Chest, Neck, Head, LeftShoulder, RightShoulder, LeftUpperArm, RightUpperArm, LeftLowerArm, RightLowerArm, LeftHand, RightHand, LeftToes, RightToes, LeftEye, RightEye, Jaw, LastBone};
+
+
+// Interface to control the Mecanim animation system
+CLASS Animator : Behaviour
+
+ // Returns true if the current rig is optimizable
+ CUSTOM_PROP bool isOptimizable { return self->IsOptimizable(); }
+
+ // Returns true if the current rig is ''humanoid'', false if it is ''generic''
+ CUSTOM_PROP bool isHuman { return self->IsHuman(); }
+
+ // Returns true if the current generic rig has a root motion
+ CUSTOM_PROP bool hasRootMotion { return self->HasRootMotion(); }
+
+ // Returns the scale of the current Avatar for a humanoid rig, (1 by default if the rig is generic)
+ CUSTOM_PROP float humanScale { return self->GetHumanScale(); }
+
+ // Gets the value of a float parameter
+ CSRAW public float GetFloat(string name) { return GetFloatString(name); }
+ // Gets the value of a float parameter
+ CSRAW public float GetFloat(int id) { return GetFloatID(id); }
+ // Sets the value of a float parameter
+ CSRAW public void SetFloat(string name, float value) { SetFloatString(name, value);}
+ // Sets the value of a float parameter
+ CSRAW public void SetFloat(string name, float value, float dampTime, float deltaTime) { SetFloatStringDamp(name, value, dampTime, deltaTime);}
+
+ // Sets the value of a float parameter
+ CSRAW public void SetFloat(int id, float value) { SetFloatID(id, value);}
+ // Sets the value of a float parameter
+ CSRAW public void SetFloat(int id, float value, float dampTime, float deltaTime) { SetFloatIDDamp(id, value, dampTime, deltaTime); }
+
+ // Gets the value of a bool parameter
+ CSRAW public bool GetBool(string name) { return GetBoolString(name);}
+ // Gets the value of a bool parameter
+ CSRAW public bool GetBool(int id) { return GetBoolID(id);}
+ // Sets the value of a bool parameter
+ CSRAW public void SetBool(string name, bool value) { SetBoolString(name, value);}
+ // Sets the value of a bool parameter
+ CSRAW public void SetBool(int id, bool value) { SetBoolID(id, value);}
+
+ // Gets the value of an integer parameter
+ CSRAW public int GetInteger(string name) { return GetIntegerString(name);}
+ // Gets the value of an integer parameter
+ CSRAW public int GetInteger(int id) { return GetIntegerID(id);}
+ // Sets the value of an integer parameter
+ CSRAW public void SetInteger(string name, int value) { SetIntegerString(name, value);}
+
+ // Sets the value of an integer parameter
+ CSRAW public void SetInteger(int id, int value) { SetIntegerID(id, value); }
+
+ // Sets the trigger parameter on
+ CSRAW public void SetTrigger(string name) { SetTriggerString(name); }
+
+ // Sets the trigger parameter at on
+ CSRAW public void SetTrigger(int id) { SetTriggerID(id); }
+
+ // Resets the trigger parameter at off
+ CSRAW public void ResetTrigger(string name) { ResetTriggerString(name); }
+
+ // Resets the trigger parameter at off
+ CSRAW public void ResetTrigger(int id) { ResetTriggerID(id); }
+
+ // Returns true if a parameter is controlled by an additional curve on an animation
+ CSRAW public bool IsParameterControlledByCurve(string name) { return IsParameterControlledByCurveString(name); }
+ // Returns true if a parameter is controlled by an additional curve on an animation
+ CSRAW public bool IsParameterControlledByCurve(int id) { return IsParameterControlledByCurveID(id); }
+
+ // Gets the avatar delta position for the last evaluated frame
+ CUSTOM_PROP Vector3 deltaPosition { return self->GetDeltaPosition(); }
+ // Gets the avatar delta rotation for the last evaluated frame
+ CUSTOM_PROP Quaternion deltaRotation { return self->GetDeltaRotation(); }
+
+ // The root position, the position of the game object
+ CUSTOM_PROP Vector3 rootPosition { return self->GetAvatarPosition();} { self->SetAvatarPosition(value);}
+ // The root rotation, the rotation of the game object
+ CUSTOM_PROP Quaternion rootRotation { return self->GetAvatarRotation();} { self->SetAvatarRotation(value);}
+
+ // Root is controlled by animations
+ CUSTOM_PROP bool applyRootMotion { return self->GetApplyRootMotion(); } { self->SetApplyRootMotion(value); }
+
+ // When turned on, animations will be executed in the physics loop. This is only useful in conjunction with kinematic rigidbodies.
+ CUSTOM_PROP bool animatePhysics { return self->GetAnimatePhysics(); } { self->SetAnimatePhysics(value); }
+
+ // Tell if the corresponding Character has transform hierarchy.
+ CUSTOM_PROP bool hasTransformHierarchy { return self->GetHasTransformHierarchy(); }
+
+ // The current gravity weight based on current animations that are played
+ CUSTOM_PROP float gravityWeight { return self->GetGravityWeight(); }
+
+ // The position of the body center of mass
+ CUSTOM_PROP Vector3 bodyPosition { return self->GetBodyPosition(); } { self->SetBodyPosition(value); }
+ // The rotation of the body center of mass
+ CUSTOM_PROP Quaternion bodyRotation { return self->GetBodyRotation(); } { self->SetBodyRotation(value); }
+
+ // Gets the position of an IK goal
+ CUSTOM Vector3 GetIKPosition( AvatarIKGoal goal) { return self->GetGoalPosition(goal) ;}
+ // Sets the position of an IK goal
+ CUSTOM void SetIKPosition( AvatarIKGoal goal, Vector3 goalPosition) { self->SetGoalPosition(goal,goalPosition); }
+
+ // Gets the rotation of an IK goal
+ CUSTOM Quaternion GetIKRotation( AvatarIKGoal goal) { return self->GetGoalRotation(goal); }
+ // Sets the rotation of an IK goal
+ CUSTOM void SetIKRotation( AvatarIKGoal goal, Quaternion goalRotation) { self->SetGoalRotation(goal, goalRotation); }
+
+ // Gets the translative weight of an IK goal (0 = at the original animation before IK, 1 = at the goal)
+ CUSTOM float GetIKPositionWeight ( AvatarIKGoal goal) { return self->GetGoalWeightPosition(goal); }
+ // Sets the translative weight of an IK goal (0 = at the original animation before IK, 1 = at the goal)
+ CUSTOM void SetIKPositionWeight ( AvatarIKGoal goal, float value) { self->SetGoalWeightPosition(goal,value); }
+
+ // Gets the rotational weight of an IK goal (0 = rotation before IK, 1 = rotation at the IK goal)
+ CUSTOM float GetIKRotationWeight( AvatarIKGoal goal) { return self->GetGoalWeightRotation(goal); }
+ // Sets the rotational weight of an IK goal (0 = rotation before IK, 1 = rotation at the IK goal)
+ CUSTOM void SetIKRotationWeight( AvatarIKGoal goal, float value) { self->SetGoalWeightRotation(goal,value); }
+
+ // Sets the look at position
+ CUSTOM void SetLookAtPosition(Vector3 lookAtPosition) { self->SetLookAtPosition(lookAtPosition); }
+
+ //Set look at weights
+ CUSTOM void SetLookAtWeight(float weight, float bodyWeight = 0.00f, float headWeight = 1.00f, float eyesWeight = 0.00f, float clampWeight = 0.50f)
+ {
+ self->SetLookAtBodyWeight(weight*bodyWeight);
+ self->SetLookAtHeadWeight(weight*headWeight);
+ self->SetLookAtEyesWeight(weight*eyesWeight);
+ self->SetLookAtClampWeight(clampWeight);
+ }
+
+ // Automatic stabilization of feet during transition and blending
+ CUSTOM_PROP bool stabilizeFeet { return self->GetStabilizeFeet(); } { self->SetStabilizeFeet(value);}
+
+ // The AnimatorController layer count
+ CUSTOM_PROP int layerCount { return self->GetLayerCount(); }
+ // Gets name of the layer
+ CUSTOM string GetLayerName( int layerIndex) { return scripting_string_new(self->GetLayerName(layerIndex)) ; }
+ // Gets the layer's current weight
+ CUSTOM float GetLayerWeight( int layerIndex) { return self->GetLayerWeight(layerIndex) ; }
+ // Sets the layer's current weight
+ CUSTOM void SetLayerWeight( int layerIndex, float weight) { self->SetLayerWeight(layerIndex, weight); }
+
+
+ // Gets the current State information on a specified AnimatorController layer
+ CUSTOM AnimatorStateInfo GetCurrentAnimatorStateInfo(int layerIndex)
+ {
+ AnimatorStateInfo info;
+ self->GetAnimatorStateInfo (layerIndex, true, info);
+ return info;
+ }
+
+ // Gets the next State information on a specified AnimatorController layer
+ CUSTOM AnimatorStateInfo GetNextAnimatorStateInfo(int layerIndex)
+ {
+ AnimatorStateInfo info;
+ self->GetAnimatorStateInfo(layerIndex, false, info);
+ return info;
+ }
+
+ // Gets the Transition information on a specified AnimatorController layer
+ CUSTOM AnimatorTransitionInfo GetAnimatorTransitionInfo(int layerIndex)
+ {
+ AnimatorTransitionInfo info;
+ self->GetAnimatorTransitionInfo(layerIndex, info);
+ return info;
+ }
+
+ CONDITIONAL !UNITY_FLASH
+ // Gets the list of AnimationInfo currently played by the current state
+ CUSTOM AnimationInfo[] GetCurrentAnimationClipState (int layerIndex)
+ {
+ dynamic_array<AnimationInfo> clips (kMemTempAlloc);
+ self->GetAnimationClipState(layerIndex, true, clips);
+ return DynamicArrayToScriptingStructArray(clips, GetMonoManager().GetCommonClasses().animationInfo, AnimationInfoToMono);
+ }
+
+ CONDITIONAL !UNITY_FLASH
+ // Gets the list of AnimationInfo currently played by the next state
+ CUSTOM AnimationInfo[] GetNextAnimationClipState (int layerIndex)
+ {
+ dynamic_array<AnimationInfo> clips (kMemTempAlloc);
+ self->GetAnimationClipState(layerIndex, false, clips);
+ return DynamicArrayToScriptingStructArray(clips, GetMonoManager().GetCommonClasses().animationInfo, AnimationInfoToMono);
+ }
+
+ // Is the specified AnimatorController layer in a transition
+ CUSTOM bool IsInTransition(int layerIndex) { return self->IsInTransition(layerIndex); }
+
+
+ // Blends pivot point between body center of mass and feet pivot. At 0%, the blending point is body center of mass. At 100%, the blending point is feet pivot
+ CUSTOM_PROP float feetPivotActive { return self->GetFeetPivotActive(); } { self->SetFeetPivotActive(value);}
+ // Gets the pivot weight
+ CUSTOM_PROP float pivotWeight { return self->GetPivotWeight(); }
+ // Get the current position of the pivot
+ CUSTOM_PROP Vector3 pivotPosition { return self->GetPivotPosition(); }
+
+
+ // Automatically adjust the gameobject position and rotation so that the AvatarTarget reaches the matchPosition when the current state is at the specified progress
+ CUSTOM void MatchTarget(Vector3 matchPosition, Quaternion matchRotation, AvatarTarget targetBodyPart, MatchTargetWeightMask weightMask, float startNormalizedTime, float targetNormalizedTime = 1)
+ {
+ self->MatchTarget(matchPosition, matchRotation, (int)targetBodyPart, weightMask, startNormalizedTime, targetNormalizedTime);
+ }
+
+ // Interrupts the automatic target matching
+ CUSTOM void InterruptMatchTarget(bool completeMatch = true) { self->InterruptMatchTarget(completeMatch);}
+ // If automatic matching is active
+ CUSTOM_PROP bool isMatchingTarget{ return self->IsMatchingTarget();}
+
+
+ // The playback speed of the Animator. 1 is normal playback speed
+ CUSTOM_PROP float speed { return self->GetSpeed() ; } { self->SetSpeed(value);}
+
+ // Force the normalized time of a state to a user defined value
+ OBSOLETE warning ForceStateNormalizedTime is deprecated. Please use Play or CrossFade instead.
+ CSRAW public void ForceStateNormalizedTime(float normalizedTime) { Play(0, 0, normalizedTime); }
+
+
+ CSRAW public void CrossFade (string stateName, float transitionDuration, int layer = -1, float normalizedTime = float.NegativeInfinity)
+ {
+ CrossFade (StringToHash(stateName), transitionDuration, layer, normalizedTime);
+ }
+ CUSTOM void CrossFade (int stateNameHash, float transitionDuration, int layer = -1, float normalizedTime = float.NegativeInfinity)
+ {
+ self->GotoState(layer, stateNameHash, normalizedTime, transitionDuration);
+ }
+
+ CSRAW public void Play (string stateName, int layer = -1, float normalizedTime = float.NegativeInfinity)
+ {
+ Play (StringToHash(stateName), layer, normalizedTime);
+ }
+ CUSTOM void Play (int stateNameHash, int layer = -1, float normalizedTime = float.NegativeInfinity)
+ {
+ self->GotoState(layer, stateNameHash, normalizedTime, 0.0F);
+ }
+
+
+
+ // Sets an AvatarTarget and a targetNormalizedTime for the current state
+ CUSTOM void SetTarget(AvatarTarget targetIndex, float targetNormalizedTime) {self->SetTarget((int)targetIndex, targetNormalizedTime);}
+ // Returns the position of the target specified by SetTarget(AvatarTarget targetIndex, float targetNormalizedTime))
+ CUSTOM_PROP Vector3 targetPosition { return self->GetTargetPosition(); }
+ // Returns the rotation of the target specified by SetTarget(AvatarTarget targetIndex, float targetNormalizedTime))
+ CUSTOM_PROP Quaternion targetRotation { return self->GetTargetRotation(); }
+
+
+ OBSOLETE error use mask and layers to control subset of transfroms in a skeleton
+ CUSTOM bool IsControlled(Transform transform) {return false;}
+
+ // Returns ture if a transform a bone controlled by human
+ CUSTOM internal bool IsBoneTransform(Transform transform) {return self->IsBoneTransform(transform);}
+
+ CUSTOM_PROP internal Transform avatarRoot {return Scripting::ScriptingWrapperFor(self->GetAvatarRoot());}
+
+ // Returns transform mapped to this human bone id
+ CUSTOM Transform GetBoneTransform(HumanBodyBones humanBoneId) {return Scripting::ScriptingWrapperFor(self->GetBoneTransform((int)humanBoneId));}
+
+ // Controls culling of this Animator component.
+ AUTO_PROP AnimatorCullingMode cullingMode GetCullingMode SetCullingMode
+
+ // Sets the animator in playback mode
+ CUSTOM void StartPlayback() { self->StartPlayback();}
+
+ // Stops animator playback mode
+ CUSTOM void StopPlayback() { self->StopPlayback();}
+
+ // Plays recorded data
+ CUSTOM_PROP float playbackTime {return self->GetPlaybackTime(); } { self->SetPlaybackTime(value);}
+
+ // Sets the animator in record mode
+ CUSTOM void StartRecording(int frameCount) { self->StartRecording(frameCount);}
+
+ // Stops animator record mode
+ CUSTOM void StopRecording() { self->StopRecording();}
+
+ // The time at which the recording data starts
+ CUSTOM_PROP float recorderStartTime { return self->GetRecorderStartTime();} {}
+
+ // The time at which the recoding data stops
+ CUSTOM_PROP float recorderStopTime { return self->GetRecorderStopTime();} {}
+
+ // The runtime representation of AnimatorController that controls the Animator
+ CUSTOM_PROP RuntimeAnimatorController runtimeAnimatorController {return Scripting::ScriptingWrapperFor(self->GetRuntimeAnimatorController());} { self->SetRuntimeAnimatorController(value);}
+
+ C++RAW static int ScriptingStringToCRC32 (ICallString& stringValue)
+ {
+ if(stringValue.IsNull()) return 0;
+ #if ENABLE_MONO
+ const gunichar2* chars = mono_string_chars(stringValue.str);
+ const size_t length = stringValue.Length();
+
+ if (IsUtf16InAsciiRange (chars, length))
+ {
+ return mecanim::processCRC32UTF16Ascii (chars, length);
+ }
+ else
+ #endif
+ {
+ return mecanim::processCRC32 (stringValue.AsUTF8().c_str());
+ }
+ }
+
+ // Generates an parameter id from a string
+ THREAD_SAFE
+ CUSTOM static int StringToHash (string name) { return ScriptingStringToCRC32(name); }
+
+ // Gets/Sets the current Avatar
+ CUSTOM_PROP Avatar avatar {return Scripting::ScriptingWrapperFor(self->GetAvatar());} { self->SetAvatar(value);}
+
+C++RAW
+
+ #define GET_NAME_IMPL(Func,x) \
+ x value; \
+ GetSetValueResult result = self->Func(ScriptingStringToCRC32(name), value); \
+ if (result != kGetSetSuccess) \
+ self->ValidateParameterString (result, name); \
+ return value;
+
+ #define SET_NAME_IMPL(Func) \
+ GetSetValueResult result = self->Func(ScriptingStringToCRC32(name), value); \
+ if (result != kGetSetSuccess) \
+ self->ValidateParameterString (result, name);
+
+
+ #define GET_ID_IMPL(Func,x) \
+ x value; \
+ GetSetValueResult result = self->Func(id, value); \
+ if (result != kGetSetSuccess) \
+ self->ValidateParameterID (result, id); \
+ return value;
+
+ #define SET_ID_IMPL(Func) \
+ GetSetValueResult result = self->Func(id, value); \
+ if (result != kGetSetSuccess) \
+ self->ValidateParameterID (result, id);
+
+
+ // Internal
+ CUSTOM private void SetFloatString(string name, float value) { SET_NAME_IMPL(SetFloat) }
+ CUSTOM private void SetFloatID(int id, float value) { SET_ID_IMPL (SetFloat) }
+
+ CUSTOM private float GetFloatString(string name) { GET_NAME_IMPL(GetFloat, float) }
+ CUSTOM private float GetFloatID(int id) { GET_ID_IMPL (GetFloat, float) }
+
+ CUSTOM private void SetBoolString(string name, bool value) { SET_NAME_IMPL(SetBool) }
+ CUSTOM private void SetBoolID(int id, bool value) { SET_ID_IMPL (SetBool) }
+
+ CUSTOM private bool GetBoolString(string name) { GET_NAME_IMPL(GetBool, bool) }
+ CUSTOM private bool GetBoolID(int id) { GET_ID_IMPL (GetBool, bool) }
+
+
+ CUSTOM private void SetIntegerString(string name, int value) { SET_NAME_IMPL(SetInteger) }
+ CUSTOM private void SetIntegerID(int id, int value) { SET_ID_IMPL (SetInteger)}
+
+ CUSTOM private int GetIntegerString(string name) { GET_NAME_IMPL(GetInteger, int) }
+ CUSTOM private int GetIntegerID(int id) { GET_ID_IMPL (GetInteger, int) }
+
+ CUSTOM private void SetTriggerString(string name)
+ {
+ GetSetValueResult result = self->SetTrigger(ScriptingStringToCRC32(name));
+ if (result != kGetSetSuccess)
+ self->ValidateParameterString (result, name);
+ }
+
+ CUSTOM private void SetTriggerID(int id)
+ {
+ GetSetValueResult result = self->SetTrigger(id);
+ if (result != kGetSetSuccess)
+ self->ValidateParameterID (result, id);
+ }
+
+ CUSTOM private void ResetTriggerString(string name)
+ {
+ GetSetValueResult result = self->ResetTrigger(ScriptingStringToCRC32(name));
+ if (result != kGetSetSuccess)
+ self->ValidateParameterString (result, name);
+ }
+
+ CUSTOM private void ResetTriggerID(int id)
+ {
+ GetSetValueResult result = self->ResetTrigger(id);
+ if (result != kGetSetSuccess)
+ self->ValidateParameterID (result, id);
+ }
+
+ CUSTOM private bool IsParameterControlledByCurveString(string name)
+ {
+ GetSetValueResult result = self->ParameterControlledByCurve(ScriptingStringToCRC32(name));
+ if (result == kParameterIsControlledByCurve)
+ return true;
+ else if (result == kGetSetSuccess)
+ return false;
+ else
+ {
+ self->ValidateParameterString (result, name);
+ return false;
+ }
+ }
+
+ CUSTOM private bool IsParameterControlledByCurveID(int id)
+ {
+ GetSetValueResult result = self->ParameterControlledByCurve(id);
+ if (result == kParameterIsControlledByCurve)
+ return true;
+ else if (result == kGetSetSuccess)
+ return false;
+ else
+ {
+ self->ValidateParameterID (result, id);
+ return false;
+ }
+ }
+
+ CUSTOM private void SetFloatStringDamp(string name, float value, float dampTime, float deltaTime)
+ {
+ GetSetValueResult result = self->SetFloatDamp(ScriptingStringToCRC32(name), value, dampTime, deltaTime);
+ if (result != kGetSetSuccess)
+ self->ValidateParameterString (result, name);
+ }
+
+ CUSTOM private void SetFloatIDDamp(int id, float value, float dampTime, float deltaTime)
+ {
+ GetSetValueResult result = self->SetFloatDamp(id, value, dampTime, deltaTime);
+ if (result != kGetSetSuccess)
+ self->ValidateParameterID (result, id);
+ }
+
+ // True if additional layers affect the center of mass
+ CUSTOM_PROP bool layersAffectMassCenter {return self->GetLayersAffectMassCenter();} { self->SetLayersAffectMassCenter(value);}
+
+
+ // Get left foot bottom height.
+ CUSTOM_PROP float leftFeetBottomHeight {return self->GetLeftFeetBottomHeight();}
+
+ // Get right foot bottom height.
+ CUSTOM_PROP float rightFeetBottomHeight {return self->GetRightFeetBottomHeight();}
+
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM_PROP internal bool supportsOnAnimatorMove { return self->SupportsOnAnimatorMove (); }
+
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM internal void WriteDefaultPose() { self->WriteDefaultPose(); }
+
+ CUSTOM void Update(float deltaTime) { self->Update(deltaTime);}
+
+ CONDITIONAL UNITY_EDITOR
+ // Evalutes only the StateMachine, does not write into transforms, uses previous deltaTime
+ // Mostly used for editor previews ( BlendTrees )
+ CUSTOM internal void EvaluateSM() {self->EvaluateSM();}
+
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM internal string GetCurrentStateName(int layerIndex) { return scripting_string_new(self->GetAnimatorStateName(layerIndex,true));}
+
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM internal string GetNextStateName(int layerIndex) { return scripting_string_new(self->GetAnimatorStateName(layerIndex,false));}
+
+ CUSTOM_PROP private bool isInManagerList { return self->IsInManagerList();}
+
+ AUTO_PROP bool logWarnings GetLogWarnings SetLogWarnings
+ AUTO_PROP bool fireEvents GetFireEvents SetFireEvents
+
+
+
+
+ OBSOLETE warning GetVector is deprecated.
+ CSRAW public Vector3 GetVector(string name) { return Vector3.zero; }
+ OBSOLETE warning GetVector is deprecated.
+ CSRAW public Vector3 GetVector(int id) { return Vector3.zero; }
+ OBSOLETE warning SetVector is deprecated.
+ CSRAW public void SetVector(string name, Vector3 value) { }
+ OBSOLETE warning SetVector is deprecated.
+ CSRAW public void SetVector(int id, Vector3 value) { }
+
+ OBSOLETE warning GetQuaternion is deprecated.
+ CSRAW public Quaternion GetQuaternion(string name) { return Quaternion.identity;}
+ OBSOLETE warning GetQuaternion is deprecated.
+ CSRAW public Quaternion GetQuaternion(int id) { return Quaternion.identity; }
+ OBSOLETE warning SetQuaternion is deprecated.
+ CSRAW public void SetQuaternion(string name, Quaternion value) { }
+ OBSOLETE warning SetQuaternion is deprecated.
+ CSRAW public void SetQuaternion(int id, Quaternion value) { }
+
+END
+
+
+
+
+CSRAW }
diff --git a/Runtime/Animation/ScriptBindings/AnimatorOverrideControllerBindings.txt b/Runtime/Animation/ScriptBindings/AnimatorOverrideControllerBindings.txt
new file mode 100644
index 0000000..9f73f79
--- /dev/null
+++ b/Runtime/Animation/ScriptBindings/AnimatorOverrideControllerBindings.txt
@@ -0,0 +1,138 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Animation/AnimatorOverrideController.h"
+#include "Runtime/Animation/AnimationClip.h"
+#include "Runtime/Animation/RuntimeAnimatorController.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+
+using namespace Unity;
+
+CSRAW
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+CSRAW [System.Serializable]
+CSRAW [StructLayout (LayoutKind.Sequential)]
+CLASS AnimationClipPair
+ public AnimationClip originalClip;
+ public AnimationClip overrideClip;
+END
+
+// AnimatorOverrideController definition
+CLASS AnimatorOverrideController : RuntimeAnimatorController
+
+ // Creates a new animation clip
+ CSRAW public AnimatorOverrideController()
+ {
+ Internal_CreateAnimationSet(this);
+ }
+
+ CUSTOM private static void Internal_CreateAnimationSet ([Writable]AnimatorOverrideController self)
+ {
+ Object* animationSet = NEW_OBJECT(AnimatorOverrideController);
+ animationSet->Reset();
+ Scripting::ConnectScriptingWrapperToObject (self.GetScriptingObject(), animationSet);
+ animationSet->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+ }
+
+ // The runtime representation of AnimatorController that controls the Animator
+ CUSTOM_PROP RuntimeAnimatorController runtimeAnimatorController {return Scripting::ScriptingWrapperFor(self->GetAnimatorController());} { self->SetAnimatorController(value);}
+
+ // Returns the animation clip named /name/.
+ CSRAW public AnimationClip this [string name]
+ {
+ get { return Internal_GetClipByName(name, true); }
+ set { Internal_SetClipByName(name, value); }
+ }
+
+ CUSTOM private AnimationClip Internal_GetClipByName(string name, bool returnEffectiveClip)
+ {
+ return Scripting::ScriptingWrapperFor(self->GetClip( name, returnEffectiveClip ));
+ }
+
+ CUSTOM private void Internal_SetClipByName(string name, AnimationClip clip)
+ {
+ return self->SetClip( name, clip );
+ }
+
+ // Returns the animation clip named /name/.
+ CSRAW public AnimationClip this [AnimationClip clip]
+ {
+ get { return Internal_GetClip(clip, true); }
+ set { Internal_SetClip(clip, value); }
+ }
+
+ CUSTOM private AnimationClip Internal_GetClip(AnimationClip originalClip, bool returnEffectiveClip)
+ {
+ return Scripting::ScriptingWrapperFor(self->GetClip( originalClip, returnEffectiveClip));
+ }
+
+ CUSTOM private void Internal_SetClip(AnimationClip originalClip, AnimationClip overrideClip)
+ {
+ return self->SetClip( originalClip, overrideClip );
+ }
+
+ CONDITIONAL UNITY_EDITOR
+ CSRAW internal delegate void OnOverrideControllerDirtyCallback();
+
+ CONDITIONAL UNITY_EDITOR
+ CSRAW internal OnOverrideControllerDirtyCallback OnOverrideControllerDirty;
+
+ CONDITIONAL UNITY_EDITOR
+ CSRAW internal static void OnInvalidateOverrideController(AnimatorOverrideController controller)
+ {
+ if(controller.OnOverrideControllerDirty != null)
+ controller.OnOverrideControllerDirty();
+ }
+
+ CSRAW public AnimationClipPair[] clips
+ {
+ get
+ {
+ AnimationClip[] originalAnimationClips = GetOriginalClips();
+
+ AnimationClipPair[] clipPair = new AnimationClipPair[originalAnimationClips.Length];
+ for(int i=0;i<originalAnimationClips.Length;i++)
+ {
+ clipPair[i] = new AnimationClipPair();
+ clipPair[i].originalClip = originalAnimationClips[i];
+ clipPair[i].overrideClip = Internal_GetClip(originalAnimationClips[i], false);
+ }
+
+ return clipPair;
+ }
+ set
+ {
+ for(int i=0;i<value.Length;i++)
+ Internal_SetClip(value[i].originalClip, value[i].overrideClip);
+ }
+ }
+
+ CUSTOM private AnimationClip[] GetOriginalClips()
+ {
+ return CreateScriptingArrayFromUnityObjects(self->GetOriginalClips(), ClassID(AnimationClip));
+ }
+
+ CUSTOM private AnimationClip[] GetOverrideClips()
+ {
+ return CreateScriptingArrayFromUnityObjects(self->GetOverrideClips(), ClassID(AnimationClip));
+ }
+
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM public void PerformOverrideClipListCleanup()
+ {
+ return self->PerformOverrideClipListCleanup();
+ }
+END
+
+CSRAW }
diff --git a/Runtime/Animation/ScriptBindings/Avatar.txt b/Runtime/Animation/ScriptBindings/Avatar.txt
new file mode 100644
index 0000000..52b0e93
--- /dev/null
+++ b/Runtime/Animation/ScriptBindings/Avatar.txt
@@ -0,0 +1,340 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Animation/Avatar.h"
+#include "Runtime/mecanim/human/human.h"
+
+using namespace Unity;
+
+CSRAW
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+ENUM internal BodyDoF
+
+ SpineFrontBack = 0,
+ SpineLeftRight,
+ SpineRollLeftRight,
+ ChestFrontBack,
+ ChestLeftRight,
+ ChestRollLeftRight,
+ LastBodyDoF
+END
+
+ENUM internal HeadDoF
+ NeckFrontBack = 0,
+ NeckLeftRight,
+ NeckRollLeftRight,
+ HeadFrontBack,
+ HeadLeftRight,
+ HeadRollLeftRight,
+ LeftEyeDownUp,
+ LeftEyeInOut,
+ RightEyeDownUp,
+ RightEyeInOut,
+ JawDownUp,
+ JawLeftRight,
+ LastHeadDoF
+END
+
+ENUM internal LegDoF
+ UpperLegFrontBack = 0,
+ UpperLegInOut,
+ UpperLegRollInOut,
+ LegCloseOpen,
+ LegRollInOut,
+ FootCloseOpen,
+ FootInOut,
+ ToesUpDown,
+ LastLegDoF
+END
+
+ENUM internal ArmDoF
+ ShoulderDownUp = 0,
+ ShoulderFrontBack,
+ ArmDownUp,
+ ArmFrontBack,
+ ArmRollInOut,
+ ForeArmCloseOpen,
+ ForeArmRollInOut,
+ HandDownUp,
+ HandInOut,
+ LastArmDoF
+END
+
+ENUM internal FingerDoF
+ ProximalDownUp = 0,
+ ProximalInOut,
+ IntermediateCloseOpen,
+ DistalCloseOpen,
+ LastFingerDoF
+END
+
+ENUM internal DoF
+ BodyDoFStart = 0,
+ HeadDoFStart = (int)BodyDoFStart + (int)BodyDoF.LastBodyDoF,
+ LeftLegDoFStart = (int)HeadDoFStart + (int)HeadDoF.LastHeadDoF,
+ RightLegDoFStart = (int)LeftLegDoFStart + (int)LegDoF.LastLegDoF,
+ LeftArmDoFStart = (int)RightLegDoFStart + (int)LegDoF.LastLegDoF,
+ RightArmDoFStart = (int)LeftArmDoFStart + (int)ArmDoF.LastArmDoF,
+
+ LeftThumbDoFStart = (int)RightArmDoFStart + (int)ArmDoF.LastArmDoF,
+ LeftIndexDoFStart = (int)LeftThumbDoFStart + (int)FingerDoF.LastFingerDoF,
+ LeftMiddleDoFStart = (int)LeftIndexDoFStart + (int)FingerDoF.LastFingerDoF,
+
+ LeftRingDoFStart = (int)LeftMiddleDoFStart + (int)FingerDoF.LastFingerDoF,
+ LeftLittleDoFStart = (int)LeftRingDoFStart + (int)FingerDoF.LastFingerDoF,
+
+ RightThumbDoFStart = (int)LeftLittleDoFStart + (int)FingerDoF.LastFingerDoF,
+ RightIndexDoFStart = (int)RightThumbDoFStart + (int)FingerDoF.LastFingerDoF,
+ RightMiddleDoFStart = (int)RightIndexDoFStart + (int)FingerDoF.LastFingerDoF,
+ RightRingDoFStart = (int)RightMiddleDoFStart + (int)FingerDoF.LastFingerDoF,
+ RightLittleDoFStart = (int)RightRingDoFStart + (int)FingerDoF.LastFingerDoF,
+
+ LastDoF = (int)RightLittleDoFStart + (int)FingerDoF.LastFingerDoF
+END
+
+// Human Body Bones
+ENUM HumanBodyBones
+ // This is the Hips bone
+ Hips = 0,
+
+ // This is the Left Upper Leg bone
+ LeftUpperLeg = 1,
+
+ // This is the Right Upper Leg bone
+ RightUpperLeg = 2,
+
+ // This is the Left Knee bone
+ LeftLowerLeg = 3,
+
+ // This is the Right Knee bone
+ RightLowerLeg = 4,
+
+ // This is the Left Ankle bone
+ LeftFoot = 5,
+
+ // This is the Right Ankle bone
+ RightFoot = 6,
+
+ // This is the first Spine bone
+ Spine = 7,
+
+ // This is the Chest bone
+ Chest = 8,
+
+ // This is the Neck bone
+ Neck = 9,
+
+ // This is the Head bone
+ Head = 10,
+
+ // This is the Left Shoulder bone
+ LeftShoulder = 11,
+
+ // This is the Right Shoulder bone
+ RightShoulder = 12,
+
+ // This is the Left Upper Arm bone
+ LeftUpperArm = 13,
+
+ // This is the Right Upper Arm bone
+ RightUpperArm = 14,
+
+ // This is the Left Elbow bone
+ LeftLowerArm = 15,
+
+ // This is the Right Elbow bone
+ RightLowerArm = 16,
+
+ // This is the Left Wrist bone
+ LeftHand = 17,
+
+ // This is the Right Wrist bone
+ RightHand = 18,
+
+ // This is the Left Toes bone
+ LeftToes = 19,
+
+ // This is the Right Toes bone
+ RightToes = 20,
+
+ // This is the Left Eye bone
+ LeftEye = 21,
+
+ // This is the Right Eye bone
+ RightEye = 22,
+
+ // This is the Jaw bone
+ Jaw = 23,
+
+ // This is the Last bone index delimiter
+ LastBone = 24
+END
+
+ENUM internal HumanFingerBones
+ ThumbProximal = 0,
+ ThumbIntermediate,
+ ThumbDistal,
+
+ IndexProximal,
+ IndexIntermediate,
+ IndexDistal,
+
+ MiddleProximal,
+ MiddleIntermediate,
+ MiddleDistal,
+
+ RingProximal,
+ RingIntermediate,
+ RingDistal,
+
+ LittleProximal,
+ LittleIntermediate,
+ LittleDistal,
+ LastBone
+END
+
+ENUM internal HumanBodyPart
+ BodyStart = 0,
+ LeftFingerStart = (int)BodyStart + (int)HumanBodyBones.LastBone,
+ RightFingerStart = (int)LeftFingerStart + (int)HumanFingerBones.LastBone
+END
+
+ENUM internal HumanParameter
+ UpperArmTwist = 0,
+ LowerArmTwist,
+ UpperLegTwist,
+ LowerLegTwist,
+ ArmStretch,
+ LegStretch,
+ FeetSpacing
+END
+
+
+// Avatar definition
+CLASS Avatar : Object
+ // Return true if this avatar is a valid mecanim avatar. It can be a generic avatar or a human avatar.
+ CUSTOM_PROP bool isValid {
+ return self->IsValid();
+ }
+
+ // Return true if this avatar is a valid human avatar.
+ CUSTOM_PROP bool isHuman
+ {
+ return self->IsValid() && self->GetAsset()->isHuman();
+ }
+
+ CUSTOM internal void SetMuscleMinMax(int muscleId, float min, float max)
+ {
+ self->SetMuscleMinMax(muscleId, min, max);
+ }
+
+ CUSTOM internal void SetParameter(int parameterId, float value)
+ {
+ self->SetParameter(parameterId, value);
+ }
+
+ CUSTOM internal float GetAxisLength(int humanId)
+ {
+ return self->GetAxisLength(humanId);
+ }
+
+ CUSTOM internal Quaternion GetPreRotation(int humanId)
+ {
+ return self->GetPreRotation(humanId);
+ }
+
+ CUSTOM internal Quaternion GetPostRotation(int humanId)
+ {
+ return self->GetPostRotation(humanId);
+ }
+
+ CUSTOM internal Quaternion GetZYPostQ(int humanId, Quaternion parentQ, Quaternion q) {
+ return self->GetZYPostQ(humanId, parentQ, q);
+ }
+
+ CUSTOM internal Quaternion GetZYRoll(int humanId, Vector3 uvw) {
+ return self->GetZYRoll(humanId, uvw);
+ }
+
+ CUSTOM internal Vector3 GetLimitSign(int humanId){
+ return self->GetLimitSign(humanId);
+ }
+
+END
+
+// Humanoid definition
+CLASS HumanTrait : Object
+
+ // Number of muscles
+ CUSTOM_PROP static int MuscleCount
+ {
+ return HumanTrait::MuscleCount;
+ }
+
+ // Muscle's name
+ CUSTOM_PROP static string[] MuscleName
+ {
+ return Scripting::StringVectorToMono( HumanTrait::GetMuscleName() );
+ }
+
+ // Number of bones
+ CUSTOM_PROP static int BoneCount
+ {
+ return HumanTrait::BoneCount;
+ }
+
+ // Bone's name
+ CUSTOM_PROP static string[] BoneName
+ {
+ return Scripting::StringVectorToMono( HumanTrait::GetBoneName() );
+ }
+
+ // Return muscle index linked to bone i, dofIndex allow you to choose between X, Y and Z muscle's axis
+ CUSTOM static int MuscleFromBone(int i, int dofIndex){
+ return HumanTrait::MuscleFromBone(i, dofIndex);
+ }
+
+ // Return bone index linked to muscle i
+ CUSTOM static int BoneFromMuscle(int i){
+ return HumanTrait::BoneFromMuscle(i);
+ }
+
+ // Return true if bone i is a required bone.
+ CUSTOM static bool RequiredBone(int i){
+ return HumanTrait::RequiredBone(i);
+ }
+
+ // Number of required bones.
+ CUSTOM_PROP static int RequiredBoneCount
+ {
+ return HumanTrait::RequiredBoneCount();
+ }
+
+ CUSTOM internal static bool HasCollider(Avatar avatar, int i){
+ return HumanTrait::HasCollider(*avatar, i);
+ }
+
+ // Return default minimum values for muscle.
+ CUSTOM static float GetMuscleDefaultMin(int i){
+ return HumanTrait::GetMuscleDefaultMin(i);
+ }
+
+ // Return default maximum values for muscle.
+ CUSTOM static float GetMuscleDefaultMax(int i){
+ return HumanTrait::GetMuscleDefaultMax(i);
+ }
+
+END
+
+CSRAW }
diff --git a/Runtime/Animation/ScriptBindings/AvatarBuilderBindings.txt b/Runtime/Animation/ScriptBindings/AvatarBuilderBindings.txt
new file mode 100644
index 0000000..641af63
--- /dev/null
+++ b/Runtime/Animation/ScriptBindings/AvatarBuilderBindings.txt
@@ -0,0 +1,250 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoScript.h"
+
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Animation/AvatarBuilder.h"
+#include "Runtime/Animation/Avatar.h"
+#include "Runtime/Scripting/Scripting.h"
+
+using namespace Unity;
+
+CSRAW
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+STRUCT SkeletonBone
+ CSRAW public string name;
+
+ CSRAW public Vector3 position;
+ CSRAW public Quaternion rotation;
+ CSRAW public Vector3 scale;
+ CSRAW public int transformModified;
+END
+
+C++RAW
+
+struct MonoSkeletonBone {
+ ScriptingStringPtr name;
+ Vector3f position;
+ Quaternionf rotation;
+ Vector3f scale;
+ int transformModified;
+};
+
+void SkeletonBoneToMono (const SkeletonBone &src, MonoSkeletonBone &dest) {
+ dest.name = scripting_string_new(src.m_Name);
+ dest.position = src.m_Position;
+ dest.rotation = src.m_Rotation;
+ dest.scale = src.m_Scale;
+ dest.transformModified = src.m_TransformModified ? 1 : 0;
+}
+
+void SkeletonBoneFromMono (const MonoSkeletonBone &src, SkeletonBone &dest) {
+ dest.m_Name = scripting_cpp_string_for(src.name);
+ dest.m_Position = src.position;
+ dest.m_Rotation = src.rotation;
+ dest.m_Scale = src.scale;
+ dest.m_TransformModified = src.transformModified != 0;
+}
+
+STRUCT HumanLimit
+ CSRAW
+
+ Vector3 m_Min;
+ Vector3 m_Max;
+ Vector3 m_Center;
+ float m_AxisLength;
+ int m_UseDefaultValues;
+
+ public bool useDefaultValues { get { return m_UseDefaultValues != 0; } set { m_UseDefaultValues = value ? 1 : 0; } }
+ public Vector3 min { get { return m_Min; } set { m_Min = value; } }
+ public Vector3 max { get { return m_Max; } set { m_Max = value; } }
+ public Vector3 center { get { return m_Center; } set { m_Center = value; } }
+ public float axisLength { get { return m_AxisLength; } set { m_AxisLength = value; } }
+END
+
+C++RAW
+
+struct MonoHumanLimit {
+
+ Vector3f m_Min;
+ Vector3f m_Max;
+ Vector3f m_Center;
+ float m_AxisLength;
+ int m_UseDefaultValues;
+};
+
+void HumanLimitToMono (const SkeletonBoneLimit &src, MonoHumanLimit &dest) {
+ dest.m_UseDefaultValues = src.m_Modified ? 0 : 1;
+ dest.m_Min = src.m_Min;
+ dest.m_Max = src.m_Max;
+ dest.m_Center = src.m_Value;
+ dest.m_AxisLength = src.m_Length;
+}
+
+void HumanLimitFromMono (const MonoHumanLimit &src, SkeletonBoneLimit &dest) {
+ dest.m_Modified = src.m_UseDefaultValues == 1 ? false : true;
+ dest.m_Min = src.m_Min;
+ dest.m_Max = src.m_Max;
+ dest.m_Value = src.m_Center;
+ dest.m_Length = src.m_AxisLength;
+}
+
+STRUCT HumanBone
+ CSRAW
+ string m_BoneName;
+ string m_HumanName;
+ public HumanLimit limit;
+
+ public string boneName { get { return m_BoneName; } set { m_BoneName = value; } }
+ public string humanName { get { return m_HumanName; } set { m_HumanName = value; } }
+END
+
+C++RAW
+
+struct MonoHumanBone {
+ ScriptingStringPtr m_BoneName;
+ ScriptingStringPtr m_HumanName;
+ MonoHumanLimit m_Limit;
+};
+
+void HumanBoneToMono (const HumanBone &src, MonoHumanBone &dest)
+{
+ dest.m_BoneName = scripting_string_new(src.m_BoneName);
+ dest.m_HumanName = scripting_string_new(src.m_HumanName);
+ HumanLimitToMono(src.m_Limit, dest.m_Limit);
+}
+
+void HumanBoneFromMono (const MonoHumanBone &src, HumanBone &dest)
+{
+ dest.m_BoneName = scripting_cpp_string_for(src.m_BoneName);
+ dest.m_HumanName = scripting_cpp_string_for(src.m_HumanName);
+ HumanLimitFromMono(src.m_Limit, dest.m_Limit);
+}
+
+STRUCT HumanDescription
+ CSRAW
+ public HumanBone[] human;
+ public SkeletonBone[] skeleton;
+
+ float m_ArmTwist;
+ float m_ForeArmTwist;
+ float m_UpperLegTwist;
+ float m_LegTwist;
+ float m_ArmStretch;
+ float m_LegStretch;
+ float m_FeetSpacing;
+
+ public float upperArmTwist { get { return m_ArmTwist; } set { m_ArmTwist = value;} }
+ public float lowerArmTwist { get { return m_ForeArmTwist; } set { m_ForeArmTwist = value;} }
+ public float upperLegTwist { get { return m_UpperLegTwist; } set { m_UpperLegTwist = value;} }
+ public float lowerLegTwist { get { return m_LegTwist; } set { m_LegTwist = value;} }
+ public float armStretch { get { return m_ArmStretch; } set { m_ArmStretch = value;} }
+ public float legStretch { get { return m_LegStretch; } set { m_LegStretch = value;} }
+ public float feetSpacing { get { return m_FeetSpacing; } set { m_FeetSpacing = value;} }
+END
+
+C++RAW
+
+struct MonoHumanDescription {
+ ScriptingArrayPtr m_Human;
+ ScriptingArrayPtr m_Skeleton;
+
+ float m_ArmTwist;
+ float m_ForeArmTwist;
+ float m_UpperLegTwist;
+ float m_LegTwist;
+ float m_ArmStretch;
+ float m_LegStretch;
+ float m_FeetSpacing;
+};
+
+
+void HumanDescriptionToMono (const HumanDescription &src, MonoHumanDescription &dest)
+{
+ if (src.m_Skeleton.size() <= 0)
+ dest.m_Skeleton = CreateEmptyStructArray(MONO_COMMON.skeletonBone);
+ else
+ dest.m_Skeleton = CreateScriptingArray(&src.m_Skeleton[0], src.m_Skeleton.size(), MONO_COMMON.skeletonBone);
+
+ if (src.m_Human.size() <= 0)
+ dest.m_Human = CreateEmptyStructArray(MONO_COMMON.humanBone);
+ else
+ dest.m_Human = CreateScriptingArray(&src.m_Human[0], src.m_Human.size(), MONO_COMMON.humanBone);
+
+ dest.m_ArmTwist = src.m_ArmTwist;
+ dest.m_ForeArmTwist = src.m_ForeArmTwist;
+
+ dest.m_UpperLegTwist = src.m_UpperLegTwist;
+ dest.m_LegTwist = src.m_LegTwist;
+ dest.m_ArmStretch = src.m_ArmStretch;
+ dest.m_LegStretch = src.m_LegStretch;
+ dest.m_FeetSpacing = src.m_FeetSpacing;
+}
+
+void HumanDescriptionFromMono (const MonoHumanDescription &src, HumanDescription &dest)
+{
+ ScriptingStructArrayToVector<SkeletonBone, MonoSkeletonBone>(src.m_Skeleton, dest.m_Skeleton, SkeletonBoneFromMono);
+ ScriptingStructArrayToVector<HumanBone, MonoHumanBone>(src.m_Human, dest.m_Human, HumanBoneFromMono);
+
+ dest.m_ArmTwist = src.m_ArmTwist;
+ dest.m_ForeArmTwist = src.m_ForeArmTwist;
+
+ dest.m_UpperLegTwist = src.m_UpperLegTwist;
+ dest.m_LegTwist = src.m_LegTwist;
+ dest.m_ArmStretch = src.m_ArmStretch;
+ dest.m_LegStretch = src.m_LegStretch;
+ dest.m_FeetSpacing = src.m_FeetSpacing;
+}
+
+CLASS AvatarBuilder
+ CUSTOM static Avatar BuildHumanAvatar(GameObject go, HumanDescription monoHumanDescription) {
+ Avatar* avatar = NEW_OBJECT(Avatar);
+ avatar->Reset();
+
+ HumanDescription humanDescription;
+ HumanDescriptionFromMono(monoHumanDescription, humanDescription);
+
+ AvatarBuilder::Options options;
+ options.avatarType = kHumanoid;
+ options.useMask = true;
+
+ std::string error = AvatarBuilder::BuildAvatar(*avatar, *go, false, humanDescription, options);
+ if(!error.empty())
+ ErrorString(error);
+
+ avatar->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+
+ return Scripting::ScriptingWrapperFor(avatar);
+ }
+
+ CUSTOM static Avatar BuildGenericAvatar(GameObject go, string rootMotionTransformName) {
+ Avatar* avatar = NEW_OBJECT(Avatar);
+ avatar->Reset();
+
+ HumanDescription humanDescription;
+ humanDescription.m_RootMotionBoneName = rootMotionTransformName.AsUTF8().c_str();
+ std::string error = AvatarBuilder::BuildAvatar(*avatar, *go, false, humanDescription);
+ if(!error.empty())
+ ErrorString(error);
+
+ avatar->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+
+ return Scripting::ScriptingWrapperFor(avatar);
+ }
+END
+
+CSRAW }
diff --git a/Runtime/Animation/ScriptBindings/RuntimeAnimatorControllerBindings.txt b/Runtime/Animation/ScriptBindings/RuntimeAnimatorControllerBindings.txt
new file mode 100644
index 0000000..98ddbfd
--- /dev/null
+++ b/Runtime/Animation/ScriptBindings/RuntimeAnimatorControllerBindings.txt
@@ -0,0 +1,26 @@
+C++RAW
+#include "UnityPrefix.h"
+#include "Runtime/Mono/MonoManager.h"
+
+CSRAW
+using System;
+using UnityEngine;
+using Object=UnityEngine.Object;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+
+/// Runtime representation of the AnimatorController
+/// Used to change at runtime the AnimatorController of an Animator
+NONSEALED_CLASS public RuntimeAnimatorController : Object
+END
+
+
+
+
+
+CSRAW }
diff --git a/Runtime/Animation/StreamedClipBuilder.cpp b/Runtime/Animation/StreamedClipBuilder.cpp
new file mode 100644
index 0000000..415baa3
--- /dev/null
+++ b/Runtime/Animation/StreamedClipBuilder.cpp
@@ -0,0 +1,272 @@
+#include "UnityPrefix.h"
+#include "StreamedClipBuilder.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/mecanim/memory.h"
+
+struct BuildCurveKey
+{
+ float time;
+ int curveIndex;
+ float coeff[4];
+
+ friend bool operator < (const BuildCurveKey& lhs, const BuildCurveKey& rhs)
+ {
+ // Sort by time primarily
+ if (lhs.time != rhs.time)
+ return lhs.time < rhs.time;
+ // for same time, Sort by curve index. This reduces cache trashing when sampling the clip
+ else
+ return lhs.curveIndex < rhs.curveIndex;
+ }
+};
+
+struct StreamedClipBuilder
+{
+ StreamedClipBuilder(): allKeys(kMemTempAlloc) {}
+
+ dynamic_array<BuildCurveKey> allKeys;
+ int curveCount;
+};
+
+StreamedClipBuilder* CreateStreamedClipBuilder(UInt32 curveCount, UInt32 keyCount)
+{
+ StreamedClipBuilder* builder = UNITY_NEW(StreamedClipBuilder, kMemTempAlloc);
+ builder->allKeys.reserve(keyCount);
+ builder->curveCount = curveCount;
+ return builder;
+}
+
+void DestroyStreamedClipBuilder(StreamedClipBuilder* builder)
+{
+ UNITY_DELETE(builder, kMemTempAlloc);
+}
+
+template<typename T>
+void ConvertCacheToBuildKeys(typename AnimationCurveTpl<T>::Cache& cache, int curveIndex, StreamedClipBuilder* builder)
+{
+ int elements = sizeof(T)/sizeof(float);
+ //Loop over number of floats in curveType
+ for(int e = 0; e < elements; e++)
+ {
+ BuildCurveKey& key = builder->allKeys.push_back();
+
+ key.time = cache.time;
+ key.curveIndex = curveIndex+e;
+ key.coeff[0] = cache.coeff[0][e];
+ key.coeff[1] = cache.coeff[1][e];
+ key.coeff[2] = cache.coeff[2][e];
+ key.coeff[3] = cache.coeff[3][e];
+ }
+}
+
+template<>
+void ConvertCacheToBuildKeys<float>(AnimationCurveTpl<float>::Cache& cache, int curveIndex, StreamedClipBuilder* builder)
+{
+ BuildCurveKey& key = builder->allKeys.push_back();
+
+ key.time = cache.time;
+ key.curveIndex = curveIndex;
+ key.coeff[0] = cache.coeff[0];
+ key.coeff[1] = cache.coeff[1];
+ key.coeff[2] = cache.coeff[2];
+ key.coeff[3] = cache.coeff[3];
+}
+
+const float kFirstClampKeyframe = -FLT_MAX;
+
+template<class T>
+void AddCurveToStreamedClip(StreamedClipBuilder* builder, int curveIndex, const AnimationCurveTpl<T>& curve)
+{
+ for(int k = -1; k < curve.GetKeyCount(); k++)
+ {
+ typename AnimationCurveTpl<T>::Cache cache;
+
+ // Last key needs to be specifically prepared as a constant value
+ // Use clamp cache function to get same functionaliy as AnimationCurve.EvaluateClamp
+ if (k == curve.GetKeyCount()-1)
+ {
+ cache.time = curve.GetKey(k).time;
+ cache.coeff[0] = cache.coeff[1] = cache.coeff[2] = Zero<T>();
+ cache.coeff[3] = curve.GetKey(k).value;
+ }
+ // A special first key needs to be created so that all values can be sampled before their first keyframe.
+ else if (k == -1)
+ {
+ // We already have a key at the first firstClampKeyframe no need to duplicate it.
+ if (kFirstClampKeyframe == curve.GetKey(0).time)
+ continue;
+
+ cache.time = kFirstClampKeyframe;
+ cache.coeff[0] = cache.coeff[1] = cache.coeff[2] = Zero<T>();
+ cache.coeff[3] = curve.GetKey(0).value;
+ }
+ // Use CalculateCacheData for all normal curve segements
+ else
+ curve.CalculateCacheData(cache, k, k+1, 0.0F);
+
+ ConvertCacheToBuildKeys<T>(cache, curveIndex, builder);
+ }
+}
+
+void AddIntegerCurveToStreamedClip(StreamedClipBuilder* builder, int curveIndex, float* time, int* value, int count)
+{
+ for (int k = 0; k < count; ++k)
+ {
+ AnimationCurveTpl<float>::Cache cache;
+
+ // Last key needs to be specifically prepared as a constant value
+ // Use clamp cache function to get same functionaliy as AnimationCurve.EvaluateClamp
+ cache.time = (k == 0) ? kFirstClampKeyframe : time[k];
+ cache.coeff[0] = cache.coeff[1] = cache.coeff[2] = 0.0f;
+ cache.coeff[3] = value[k];
+
+ ConvertCacheToBuildKeys<float>(cache, curveIndex, builder);
+ }
+}
+
+
+
+template void AddCurveToStreamedClip<float>(StreamedClipBuilder* builder, int curveIndex, const AnimationCurveTpl<float>& curve);
+template void AddCurveToStreamedClip<Vector3f>(StreamedClipBuilder* builder, int curveIndex, const AnimationCurveTpl<Vector3f>& curve);
+template void AddCurveToStreamedClip<Quaternionf>(StreamedClipBuilder* builder, int curveIndex, const AnimationCurveTpl<Quaternionf>& curve);
+
+template<class T>
+T& PushData (dynamic_array<UInt8>& output)
+{
+ output.resize_uninitialized(output.size() + sizeof(T));
+ return *reinterpret_cast<T*> (&output[output.size() - sizeof(T)]);
+}
+
+void CreateStreamClipConstant (StreamedClipBuilder* builder, mecanim::animation::StreamedClip& clip, mecanim::memory::Allocator& alloc)
+{
+ Assert(clip.curveCount == 0);
+ Assert(clip.data.IsNull());
+
+ std::sort(builder->allKeys.begin(), builder->allKeys.end());
+
+ dynamic_array<UInt8> streamData;
+ streamData.reserve((builder->allKeys.size()+1) * (sizeof(mecanim::animation::CurveKey) + sizeof(mecanim::animation::CurveTimeData)));
+
+
+ // Generate the curvedata stream
+ float currentTime = -std::numeric_limits<float>::infinity();
+ for (int i=0;i<builder->allKeys.size();)
+ {
+ currentTime = builder->allKeys[i].time;
+ mecanim::animation::CurveTimeData& timeData = PushData<mecanim::animation::CurveTimeData> (streamData);
+ timeData.time = currentTime;
+
+ int count = 0;
+ while (i < builder->allKeys.size() && builder->allKeys[i].time == currentTime)
+ {
+ mecanim::animation::CurveKey& curveKey = PushData<mecanim::animation::CurveKey>(streamData);
+ curveKey.curveIndex = builder->allKeys[i].curveIndex;
+ memcpy(curveKey.coeff, builder->allKeys[i].coeff, sizeof(curveKey.coeff));
+
+ i++;
+ count++;
+ }
+
+ timeData.count = count;
+ }
+
+ // Make sure that we do not sample beyond the last actual key by adding an infinity key.
+ mecanim::animation::CurveTimeData& timeData = PushData<mecanim::animation::CurveTimeData> (streamData);
+ timeData.time = std::numeric_limits<float>::infinity();
+ timeData.count = 0;
+
+ clip.dataSize = streamData.size() / sizeof(mecanim::uint32_t);
+ clip.data = alloc.ConstructArray<mecanim::uint32_t> (clip.dataSize);
+ memcpy(clip.data.Get(), streamData.begin(), streamData.size());
+ clip.curveCount = builder->curveCount;
+}
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+typedef AnimationCurve::Keyframe Keyframe;
+
+static float Evaluate0 (const mecanim::animation::StreamedClip& clip, mecanim::animation::StreamedClipMemory& memory, float time)
+{
+ float output;
+ SampleClip(clip, memory, time, &output);
+
+ return output;
+}
+
+typedef AnimationCurveVec3::Keyframe KeyframeVec3;
+static Vector3f Evaluate3 (const mecanim::animation::StreamedClip& clip, mecanim::animation::StreamedClipMemory& memory, float time)
+{
+ Vector3f output;
+ SampleClip(clip, memory, time, reinterpret_cast<float*>(&output));
+
+ return output;
+}
+
+SUITE (StreamedClipBuilderTests)
+{
+TEST (StreamedClipBuilder_StreamedClipEvaluation)
+{
+ mecanim::memory::MecanimAllocator alloc(kMemTempAlloc);
+
+ AnimationCurve curve;
+ curve.AddKeyBackFast(Keyframe(0.5F, 0.0F));
+ curve.AddKeyBackFast(Keyframe(1.0F, 1.0F));
+ curve.AddKeyBackFast(Keyframe(2.0F, -1.0F));
+
+ StreamedClipBuilder* builder = CreateStreamedClipBuilder(1, curve.GetKeyCount());
+ AddCurveToStreamedClip(builder, 0, curve);
+ mecanim::animation::StreamedClip streamclip;
+ CreateStreamClipConstant (builder, streamclip, alloc);
+
+ mecanim::animation::StreamedClipMemory memory;
+ CreateStreamedClipMemory(streamclip, memory, alloc);
+
+ CHECK_EQUAL(curve.EvaluateClamp(-5.0), Evaluate0(streamclip, memory, -5.0F));
+ CHECK_EQUAL(curve.EvaluateClamp(1.0F), Evaluate0(streamclip, memory, 1.0F));
+ CHECK_EQUAL(curve.EvaluateClamp(0.0F), Evaluate0(streamclip, memory, 0.0F));
+ CHECK_EQUAL(curve.EvaluateClamp(1.5F), Evaluate0(streamclip, memory, 1.5F));
+ CHECK_EQUAL(curve.EvaluateClamp(2.0F), Evaluate0(streamclip, memory, 2.0F));
+ CHECK_EQUAL(curve.EvaluateClamp(0.1F), Evaluate0(streamclip, memory, 0.1F));
+ CHECK_EQUAL(curve.EvaluateClamp(100.0F), Evaluate0(streamclip, memory, 100.0F));
+ CHECK_EQUAL(curve.EvaluateClamp(-19), Evaluate0(streamclip, memory, -19.0F));
+
+ DestroyStreamedClipMemory (memory, alloc);
+ DestroyStreamedClip (streamclip, alloc);
+ DestroyStreamedClipBuilder (builder);
+}
+
+
+TEST (StreamedClipEvaluationVector3)
+{
+ mecanim::memory::MecanimAllocator alloc(kMemTempAlloc);
+
+ AnimationCurveVec3 curve;
+ curve.AddKeyBackFast(KeyframeVec3(0.5F, Vector3f(0.0,1.0,2.0)));
+ curve.AddKeyBackFast(KeyframeVec3(1.0F, Vector3f(3.0,0.0,4.0)));
+ curve.AddKeyBackFast(KeyframeVec3(2.0F, Vector3f(0.0,-1.0,-2.0)));
+
+ StreamedClipBuilder* builder = CreateStreamedClipBuilder(3, curve.GetKeyCount()*3);
+ AddCurveToStreamedClip(builder, 0, curve);
+ mecanim::animation::StreamedClip streamclip;
+ CreateStreamClipConstant (builder, streamclip, alloc);
+
+ mecanim::animation::StreamedClipMemory memory;
+ CreateStreamedClipMemory(streamclip, memory, alloc);
+
+ CHECK(curve.EvaluateClamp(-5.0) == Evaluate3(streamclip, memory, -5.0F));
+ CHECK(curve.EvaluateClamp(1.0F) == Evaluate3(streamclip, memory, 1.0F));
+ CHECK(curve.EvaluateClamp(0.0F) == Evaluate3(streamclip, memory, 0.0F));
+ CHECK(curve.EvaluateClamp(1.5F) == Evaluate3(streamclip, memory, 1.5F));
+ CHECK(curve.EvaluateClamp(2.0F) == Evaluate3(streamclip, memory, 2.0F));
+ CHECK(curve.EvaluateClamp(0.1F) == Evaluate3(streamclip, memory, 0.1F));
+ CHECK(curve.EvaluateClamp(100.0F) == Evaluate3(streamclip, memory, 100.0F));
+ CHECK(curve.EvaluateClamp(-19) == Evaluate3(streamclip, memory, -19.0F));
+
+ DestroyStreamedClipMemory (memory, alloc);
+ DestroyStreamedClip (streamclip, alloc);
+ DestroyStreamedClipBuilder (builder);
+}
+}
+#endif
diff --git a/Runtime/Animation/StreamedClipBuilder.h b/Runtime/Animation/StreamedClipBuilder.h
new file mode 100644
index 0000000..7ae55d1
--- /dev/null
+++ b/Runtime/Animation/StreamedClipBuilder.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "Runtime/Math/AnimationCurve.h"
+#include "Runtime/mecanim/animation/streamedclip.h"
+
+struct StreamedClipBuilder;
+
+StreamedClipBuilder* CreateStreamedClipBuilder(UInt32 curveCount, UInt32 keyCount);
+void DestroyStreamedClipBuilder(StreamedClipBuilder* builder);
+
+template<class T>
+void AddCurveToStreamedClip(StreamedClipBuilder* builder, int curveIndex, const AnimationCurveTpl<T>& curve);
+
+void AddIntegerCurveToStreamedClip(StreamedClipBuilder* builder, int curveIndex, float* time, int* value, int count);
+
+void CreateStreamClipConstant (StreamedClipBuilder* builder, mecanim::animation::StreamedClip& clip, mecanim::memory::Allocator& alloc);
diff --git a/Runtime/Audio/AudioBehaviour.cpp b/Runtime/Audio/AudioBehaviour.cpp
new file mode 100644
index 0000000..eac56cc
--- /dev/null
+++ b/Runtime/Audio/AudioBehaviour.cpp
@@ -0,0 +1,16 @@
+#include "UnityPrefix.h"
+#include "AudioBehaviour.h"
+
+#if ENABLE_AUDIO
+
+IMPLEMENT_CLASS (AudioBehaviour)
+
+AudioBehaviour::AudioBehaviour (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{}
+
+AudioBehaviour::~AudioBehaviour ()
+{
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/Audio/AudioBehaviour.h b/Runtime/Audio/AudioBehaviour.h
new file mode 100644
index 0000000..46275cb
--- /dev/null
+++ b/Runtime/Audio/AudioBehaviour.h
@@ -0,0 +1,21 @@
+#ifndef __AUDIOTYPES_H__
+#define __AUDIOTYPES_H__
+#if ENABLE_AUDIO
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+// macros/helpers
+#define UNITYVEC2FMODVEC(v) *(reinterpret_cast<FMOD_VECTOR*>(&v))
+#define UNITYVEC2FMODVECPTR(v) (reinterpret_cast<FMOD_VECTOR*>(&v))
+
+class AudioBehaviour : public Behaviour
+{
+public:
+ REGISTER_DERIVED_ABSTRACT_CLASS (AudioBehaviour, Behaviour)
+
+ AudioBehaviour (MemLabelId label, ObjectCreationMode mode);
+};
+
+#endif //ENABLE_AUDIO
+#endif // __AUDIOTYPES_H__
diff --git a/Runtime/Audio/AudioChorusFilter.cpp b/Runtime/Audio/AudioChorusFilter.cpp
new file mode 100644
index 0000000..ef1294c
--- /dev/null
+++ b/Runtime/Audio/AudioChorusFilter.cpp
@@ -0,0 +1,86 @@
+#include "UnityPrefix.h"
+#include "AudioChorusFilter.h"
+#include "Runtime/Utilities/Utility.h"
+
+#if ENABLE_AUDIO_FMOD
+
+AudioChorusFilter::AudioChorusFilter (MemLabelId label, ObjectCreationMode mode) :
+Super(label, mode),
+m_DryMix(0.5f),
+m_WetMix1(0.5f),
+m_WetMix2(0.5f),
+m_WetMix3(0.5f),
+m_Delay(40.0f),
+m_Rate(0.8f),
+m_Depth(0.03f)
+{
+ m_Type = FMOD_DSP_TYPE_CHORUS;
+}
+
+AudioChorusFilter::~AudioChorusFilter()
+{}
+
+
+void AudioChorusFilter::AddToManager()
+{
+ Super::AddToManager();
+}
+
+void AudioChorusFilter::Reset()
+{
+ Super::Reset();
+
+ m_DryMix = 0.5f;
+ m_WetMix1 = 0.5f;
+ m_WetMix2 = 0.5f;
+ m_WetMix3 = 0.5f;
+ m_Delay = 40.0f;
+ m_Rate = 0.8f;
+ m_Depth = 0.03f;
+}
+
+void AudioChorusFilter::CheckConsistency()
+{
+ Super::CheckConsistency();
+ m_DryMix = clamp(m_DryMix,0.0f,1.0f);
+ m_WetMix1 = clamp(m_WetMix1,0.0f,1.0f);
+ m_WetMix2 = clamp(m_WetMix2, 0.0f, 1.0f);
+ m_WetMix3 = clamp(m_WetMix3, 0.0f, 1.0f);
+ m_Delay = clamp(m_Delay, 0.1f, 100.0f);
+ m_Rate = clamp(m_Rate,0.0f,20.0f);
+ m_Depth = clamp(m_Depth,0.0f,1.0f);
+}
+
+void AudioChorusFilter::Update()
+{
+ if (m_DSP)
+ {
+ m_DSP->setParameter(FMOD_DSP_CHORUS_DRYMIX, m_DryMix);
+ m_DSP->setParameter(FMOD_DSP_CHORUS_WETMIX1, m_WetMix1);
+ m_DSP->setParameter(FMOD_DSP_CHORUS_WETMIX2, m_WetMix2);
+ m_DSP->setParameter(FMOD_DSP_CHORUS_WETMIX3, m_WetMix3);
+ m_DSP->setParameter(FMOD_DSP_CHORUS_DELAY, m_Delay);
+ m_DSP->setParameter(FMOD_DSP_CHORUS_RATE, m_Rate);
+ m_DSP->setParameter(FMOD_DSP_CHORUS_DEPTH, m_Depth);
+ }
+}
+
+
+template<class TransferFunc>
+void AudioChorusFilter::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER(m_DryMix);
+ TRANSFER(m_WetMix1);
+ TRANSFER(m_WetMix2);
+ TRANSFER(m_WetMix3);
+ TRANSFER(m_Delay);
+ TRANSFER(m_Rate);
+ TRANSFER(m_Depth);
+}
+
+
+IMPLEMENT_CLASS (AudioChorusFilter)
+IMPLEMENT_OBJECT_SERIALIZE (AudioChorusFilter)
+
+#endif //ENABLE_AUDIO
diff --git a/Runtime/Audio/AudioChorusFilter.h b/Runtime/Audio/AudioChorusFilter.h
new file mode 100644
index 0000000..c656149
--- /dev/null
+++ b/Runtime/Audio/AudioChorusFilter.h
@@ -0,0 +1,51 @@
+#ifndef __AUDIOCHORUS_FILTER_H__
+#define __AUDIOCHORUS_FILTER_H__
+
+#if ENABLE_AUDIO_FMOD
+#include "AudioSourceFilter.h"
+
+class AudioChorusFilter : public AudioFilter
+{
+public:
+ REGISTER_DERIVED_CLASS (AudioChorusFilter, AudioFilter)
+ DECLARE_OBJECT_SERIALIZE (AudioChorusFilter)
+ AudioChorusFilter (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void CheckConsistency ();
+ virtual void Update();
+ virtual void AddToManager();
+ virtual void Reset();
+
+ float GetDelay() const { return m_Delay; }
+ void SetDelay(const float delay) { m_Delay = delay; Update(); SetDirty(); }
+
+ float GetRate() const { return m_Rate; }
+ void SetRate(const float rate) { m_Rate = rate; Update(); SetDirty(); }
+
+ float GetDryMix() const { return m_DryMix; }
+ void SetDryMix(const float drymix) { m_DryMix = drymix; Update(); SetDirty(); }
+
+ float GetWetMix1() const { return m_WetMix1; }
+ void SetWetMix1(const float wetmix) { m_WetMix1 = wetmix; Update(); SetDirty(); }
+
+ float GetWetMix2() const { return m_WetMix2; }
+ void SetWetMix2(const float wetmix) { m_WetMix2 = wetmix; Update(); SetDirty();}
+
+ float GetWetMix3() const { return m_WetMix3; }
+ void SetWetMix3(const float wetmix) { m_WetMix3 = wetmix; Update(); SetDirty(); }
+
+ float GetDepth() const { return m_Depth; }
+ void SetDepth(const float depth) { m_Depth = depth; Update(); SetDirty();}
+
+private:
+ float m_DryMix; // FMOD_DSP_CHORUS_DRYMIX, Volume of original signal to pass to output. 0.0 to 1.0. Default = 0.5.
+ float m_WetMix1; // FMOD_DSP_CHORUS_WETMIX1, Volume of 1st chorus tap. 0.0 to 1.0. Default = 0.5.
+ float m_WetMix2; // FMOD_DSP_CHORUS_WETMIX2, Volume of 2nd chorus tap. This tap is 90 degrees out of phase of the first tap. 0.0 to 1.0. Default = 0.5.
+ float m_WetMix3; // FMOD_DSP_CHORUS_WETMIX3, Volume of 3rd chorus tap. This tap is 90 degrees out of phase of the second tap. 0.0 to 1.0. Default = 0.5.
+ float m_Delay; // FMOD_DSP_CHORUS_DELAY, Chorus delay in ms. 0.1 to 100.0. Default = 40.0 ms.
+ float m_Rate; // FMOD_DSP_CHORUS_RATE, Chorus modulation rate in hz. 0.0 to 20.0. Default = 0.8 hz.
+ float m_Depth; // FMOD_DSP_CHORUS_DEPTH, Chorus modulation depth. 0.0 to 1.0. Default = 0.03.
+};
+
+#endif //ENABLE_AUDIO
+#endif // __AUDIOSOURCE_FILTER_H__
diff --git a/Runtime/Audio/AudioClip.Callbacks.cpp b/Runtime/Audio/AudioClip.Callbacks.cpp
new file mode 100644
index 0000000..e76de98
--- /dev/null
+++ b/Runtime/Audio/AudioClip.Callbacks.cpp
@@ -0,0 +1,270 @@
+#include "UnityPrefix.h"
+#include "AudioClip.h"
+#include "OggReader.h"
+#include "Runtime/Audio/correct_fmod_includer.h"
+#include "Runtime/Mono/MonoScopedThreadAttach.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Profiler/TimeHelper.h"
+#include "Runtime/Audio/AudioManager.h"
+#if ENABLE_WWW
+#include "Runtime/Export/WWW.h"
+#endif
+
+#if UNITY_EDITOR
+#include "Editor/Src/AssetPipeline/MonoCompile.h"
+#endif
+
+#include "Runtime/Scripting/Scripting.h"
+
+#if ENABLE_AUDIO_FMOD
+
+
+
+/**
+ * Streaming callbacks
+ **/
+
+/**
+ * file callback function used by FMOD
+ * we're using this to stream data from WWW stream
+ **/
+
+
+#if ENABLE_WWW
+FMOD_RESULT F_CALLBACK AudioClip::WWWOpen(
+ const char * wwwstream,
+ int unicode,
+ unsigned int * filesize,
+ void ** handle,
+ void ** userdata
+)
+{
+ WWW* stream = (WWW*) wwwstream;
+
+ if (stream)
+ {
+ // predict filesize
+ stream->LockPartialData();
+ // make sure we've read enough to determine filesize
+ if (stream->GetPartialSize() == 0)
+ {
+ stream->UnlockPartialData();
+ return FMOD_ERR_NOTREADY;
+ }
+ *filesize = (unsigned int)((1.0 / stream->GetProgress()) * stream->GetPartialSize());
+ wwwUserData* ud = new wwwUserData();
+ ud->pos = 0;
+ ud->filesize = *filesize;
+ ud->stream = stream;
+ *userdata = (void*)ud;
+ *handle = (void*) wwwstream;
+ stream->UnlockPartialData();
+
+ return FMOD_OK;
+ }
+
+ return FMOD_ERR_INVALID_PARAM;
+}
+
+
+FMOD_RESULT F_CALLBACK AudioClip::WWWClose(
+ void * handle,
+ void * userdata
+)
+{
+ if (!handle)
+ {
+ return FMOD_ERR_INVALID_PARAM;
+ }
+
+ wwwUserData* ud = (wwwUserData*) userdata;
+ delete ud;
+ return FMOD_OK;
+}
+
+
+FMOD_RESULT F_CALLBACK AudioClip::WWWRead(
+ void * handle,
+ void * buffer,
+ unsigned int sizebytes,
+ unsigned int * bytesread,
+ void * userdata
+)
+{
+ if (!handle)
+ {
+ return FMOD_ERR_INVALID_PARAM;
+ }
+
+ wwwUserData* ud = (wwwUserData*) userdata;
+
+ Assert (ud->stream);
+ Assert (ud->stream->GetAudioClip());
+
+ ud->stream->LockPartialData();
+
+ // read sizebytes
+ const UInt8* bufferStart = ud->stream->GetPartialData();
+ unsigned avail = ud->stream->GetPartialSize();
+
+ if ( ud->pos > avail)
+ {
+ ud->stream->UnlockPartialData();
+ return FMOD_ERR_NOTREADY;
+ }
+
+ *bytesread = avail - ud->pos<sizebytes?(avail-ud->pos):sizebytes;
+ memcpy (buffer, bufferStart + ud->pos, *bytesread);
+
+ // update pos
+ ud->pos = ud->pos + *bytesread;
+
+ ud->stream->UnlockPartialData();
+
+ if (*bytesread < sizebytes)
+ return FMOD_ERR_FILE_EOF;
+
+ return FMOD_OK;
+}
+
+FMOD_RESULT F_CALLBACK AudioClip::WWWSeek(
+ void * handle,
+ unsigned int pos,
+ void * userdata
+)
+{
+ if (!handle)
+ {
+ return FMOD_ERR_INVALID_PARAM;
+ }
+
+ wwwUserData* ud = (wwwUserData*) userdata;
+
+ Assert( ud->stream );
+ Assert( ud->stream->GetAudioClip() );
+
+ if ( ud->stream->GetAudioClip()->IsWWWStreamed() || ud->filesize < pos)
+ return FMOD_ERR_FILE_COULDNOTSEEK;
+
+ ud->pos = pos;
+
+ return FMOD_OK;
+}
+
+#endif
+
+/**
+ * Buffer streaming callbacks
+ **/
+
+/**
+ * Callbacks for PCM streaming
+ **/
+FMOD_RESULT F_CALLBACK AudioClip::pcmread(
+ FMOD_SOUND * sound,
+ void * data,
+ unsigned int datalen
+ )
+{
+ FMOD::Sound* pSound = (FMOD::Sound*) sound;
+ AudioClip* ac = NULL;
+ pSound->getUserData((void**)&ac);
+
+ if (ac->GetQueuedAudioData(&data, datalen))
+ return FMOD_OK;
+
+ return FMOD_ERR_NOTREADY;
+}
+
+/**
+ * Callbacks for PCM streaming
+ **/
+FMOD_RESULT F_CALLBACK AudioClip::ScriptPCMReadCallback(
+ FMOD_SOUND * sound,
+ void * data,
+ unsigned int datalen
+ )
+{
+#if SUPPORT_MONO_THREADS || UNITY_WINRT
+
+#if UNITY_EDITOR
+ if (IsCompiling())
+ return FMOD_OK;
+#endif
+
+ FMOD::Sound* pSound = (FMOD::Sound*) sound;
+ AudioClip* ac = NULL;
+ pSound->getUserData((void**)&ac);
+
+ Assert (ac);
+#if ENABLE_MONO
+ ScopedThreadAttach attach(ac->monoDomain);
+#endif
+
+ // reuse mono array
+ ScriptingArrayPtr array = SCRIPTING_NULL;
+ GetAudioManager().GetScriptBufferManager().GetPCMReadArray(datalen / 4, array);
+
+ Assert(array != SCRIPTING_NULL);
+
+ // invoke
+ ScriptingObjectPtr instance = Scripting::ScriptingWrapperFor(ac);
+ ScriptingExceptionPtr exception;
+
+ ScriptingInvocation invocation(ac->m_CachedPCMReaderCallbackMethod);
+ invocation.AddArray(array);
+ invocation.object = instance;
+ invocation.Invoke(&exception);
+ if (exception)
+ {
+ // handle
+ Scripting::LogException(exception, Scripting::GetInstanceIDFromScriptingWrapper(instance));
+ }
+ else
+ {
+ memcpy(data, &Scripting::GetScriptingArrayElement<float>( array, 0 ), datalen);
+ }
+#endif // SUPPORT_MONO_THREADS
+
+ return FMOD_OK;
+}
+
+FMOD_RESULT F_CALLBACK AudioClip::ScriptPCMSetPositionCallback(
+ FMOD_SOUND * sound,
+ int subsound,
+ unsigned int position,
+ FMOD_TIMEUNIT postype)
+{
+#if SUPPORT_MONO_THREADS || UNITY_WINRT
+ Assert(postype == FMOD_TIMEUNIT_PCM);
+
+ FMOD::Sound* pSound = (FMOD::Sound*) sound;
+ AudioClip* ac = NULL;
+ pSound->getUserData((void**)&ac);
+
+ Assert (ac);
+#if ENABLE_MONO
+ ScopedThreadAttach attach(ac->monoDomain);
+#endif
+
+ // invoke
+ ScriptingObjectPtr instance = Scripting::ScriptingWrapperFor(ac);
+
+ ScriptingExceptionPtr exception;
+ ScriptingInvocation invocation(ac->m_CachedSetPositionCallbackMethod);
+ invocation.AddInt(position);
+ invocation.object = instance;
+ invocation.Invoke(&exception);
+
+ if (exception)
+ {
+ // handle
+ Scripting::LogException(exception, Scripting::GetInstanceIDFromScriptingWrapper(instance));
+ }
+#endif // SUPPORT_MONO_THREADS
+
+ return FMOD_OK;
+}
+
+#endif //ENABLE_AUDIO
+
diff --git a/Runtime/Audio/AudioClip.cpp b/Runtime/Audio/AudioClip.cpp
new file mode 100644
index 0000000..2f1f719
--- /dev/null
+++ b/Runtime/Audio/AudioClip.cpp
@@ -0,0 +1,1346 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_AUDIO_FMOD
+
+#include "AudioClip.h"
+#include "AudioSource.h"
+#include "Utilities/Conversion.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/SwapEndianArray.h"
+#include "AudioManager.h"
+#include "Runtime/Video/MoviePlayback.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Misc/UTF8.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+
+#include "WavReader.h"
+#include "OggReader.h"
+
+#if UNITY_EDITOR //in editor, include nonfmod vorbis first, to make sure we actually use that, and not the fmod version.
+#include "../../External/Audio/libvorbis/include/vorbis/codec.h"
+#include "Editor/Src/EditorUserBuildSettings.h"
+#include "Editor/Src/AssetPipeline/AudioImporter.h"
+#endif
+
+#include "Runtime/Audio/correct_fmod_includer.h"
+
+#if ENABLE_WWW
+#include "Runtime/Export/WWW.h"
+#endif
+
+#include "Runtime/Misc/BuildSettings.h"
+
+#if UNITY_EDITOR
+#include <fstream>
+#endif
+
+
+//kMono8 = 1, kMono16, kStereo8, kStereo16, kOggVorbis
+static FMOD_SOUND_FORMAT FORMAT_TO_FMOD_FORMAT[] = { FMOD_SOUND_FORMAT_NONE , FMOD_SOUND_FORMAT_PCM8, FMOD_SOUND_FORMAT_PCM16, FMOD_SOUND_FORMAT_PCM8, FMOD_SOUND_FORMAT_PCM16, FMOD_SOUND_FORMAT_PCM16 };
+static FMOD_SOUND_TYPE FORMAT_TO_FMOD_TYPE[] = {FMOD_SOUND_TYPE_UNKNOWN, FMOD_SOUND_TYPE_RAW, FMOD_SOUND_TYPE_RAW, FMOD_SOUND_TYPE_RAW, FMOD_SOUND_TYPE_RAW, FMOD_SOUND_TYPE_OGGVORBIS };
+
+
+#if ENABLE_PROFILER
+int AudioClip::s_AudioClipCount = 0;
+#endif
+
+
+void AudioClip::InitializeClass ()
+{
+ #if UNITY_EDITOR
+ RegisterAllowNameConversion (AudioClip::GetClassStringStatic(), "m_UseRuntimeDecompression", "m_DecompressOnLoad");
+ #endif
+}
+
+AudioClip::AudioClip (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode),
+m_Channels(0),
+m_Sound(NULL),
+m_Frequency(0),
+m_BitsPerSample(0),
+m_3D(true),
+m_UseHardware(false),
+m_Format(FMOD_SOUND_FORMAT_NONE),
+m_Type(FMOD_SOUND_TYPE_UNKNOWN),
+#if UNITY_EDITOR
+m_EditorSoundType(FMOD_SOUND_TYPE_UNKNOWN),
+m_EditorSoundFormat(FMOD_SOUND_FORMAT_NONE),
+#endif
+#if ENABLE_WWW
+m_StreamData(NULL),
+m_ReadAllowed(true),
+#endif
+m_ExternalStream(false),
+m_MoviePlayback(NULL),
+m_OpenState(FMOD_OPENSTATE_CONNECTING),
+m_LoadFlag(kDecompressOnLoad),
+m_WWWStreamed(false),
+m_PCMArray(SCRIPTING_NULL),
+m_PCMArrayGCHandle(0),
+m_CachedSetPositionCallbackMethod(SCRIPTING_NULL),
+m_CachedPCMReaderCallbackMethod(SCRIPTING_NULL),
+m_DecodeBufferSize(0),
+#if ENABLE_MONO
+monoDomain(NULL),
+#endif
+m_UserGenerated(false),
+m_UserLengthSamples(0),
+m_UserIsStream(true),
+m_AudioData(kMemAudio)
+{
+#if ENABLE_PROFILER
+ s_AudioClipCount++;
+#endif
+}
+
+#if ENABLE_WWW
+bool AudioClip::InitStream (WWW* streamData, MoviePlayback* movie, bool realStream /* = false */, FMOD_SOUND_TYPE fmodSoundType /* = FMOD_SOUND_TYPE_UNKNOWN */)
+{
+ AssertIf(m_MoviePlayback != NULL);
+ AssertIf(m_StreamData != NULL);
+
+ // Web streaming
+ if (streamData)
+ {
+ // If the audiotype isn't specified, guess the audio type from the url to avoid going thru all available codecs (this seriously hit performance for a net stream)
+ std::string ext = ToLower(GetPathNameExtension(streamData->GetUrl()));
+
+ if (fmodSoundType == FMOD_SOUND_TYPE_UNKNOWN)
+ m_Type = GetFormatFromExtension(ext);
+ else
+ m_Type = fmodSoundType;
+
+ if (m_Type == FMOD_SOUND_TYPE_UNKNOWN)
+ {
+ ErrorStringObject(Format("Unable to determine the audio type from the URL (%s) . Please specify the type.", streamData->GetUrl() ), this);
+ // right now we're trying to load sound in AwakeFromLoad (and do only that) - so we skip the call
+ HackSetAwakeWasCalled();
+ return false;
+ }
+
+ if (realStream && IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_1_a3))
+ {
+ if(m_Type == FMOD_SOUND_TYPE_XM || m_Type == FMOD_SOUND_TYPE_IT || m_Type == FMOD_SOUND_TYPE_MOD || m_Type == FMOD_SOUND_TYPE_S3M)
+ {
+ ErrorStringObject("Tracker files (XM/IT/MOD/S3M) cannot be streamed in realtime but must be fully downloaded before they can play.", this);
+ HackSetAwakeWasCalled(); // to avoid error message: "Awake has not been called '' (AudioClip). Figure out where the object gets created and call AwakeFromLoad correctly."
+ return false;
+ }
+ }
+
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ {
+#if UNITY_EDITOR
+ BuildTargetPlatform targetPlatform = GetEditorUserBuildSettings().GetActiveBuildTarget ();
+ if (
+ (m_Type == FMOD_SOUND_TYPE_OGGVORBIS && ((targetPlatform == kBuild_Android)||(targetPlatform == kBuild_iPhone)||(targetPlatform == kBuildBB10)||(targetPlatform == kBuildTizen)))
+ ||
+ (m_Type == FMOD_SOUND_TYPE_MPEG && !((targetPlatform == kBuild_Android)||(targetPlatform == kBuild_iPhone)||(targetPlatform == kBuildBB10)||(targetPlatform == kBuildTizen)))
+ )
+#else
+ if (
+ (m_Type == FMOD_SOUND_TYPE_OGGVORBIS && (UNITY_ANDROID | UNITY_IPHONE | UNITY_BB10 | UNITY_TIZEN) != 0)
+ ||
+ (m_Type == FMOD_SOUND_TYPE_MPEG && (UNITY_ANDROID | UNITY_IPHONE | UNITY_BB10 | UNITY_TIZEN) == 0)
+ )
+#endif
+ {
+ ErrorStringObject(Format("Streaming of '%s' on this platform is not supported", ext.c_str()), this);
+ // right now we're trying to load sound in AwakeFromLoad (and do only that) - so we skip the call
+ HackSetAwakeWasCalled();
+ return false;
+ }
+ }
+
+ m_StreamData = streamData;
+ m_StreamData->SetAudioClip( this );
+ m_StreamData->Retain(); // Make sure the WWW object doesn't dissappear if the mono side of the WWW object is deleted before we are done.
+ m_ExternalStream = true;
+ m_WWWStreamed = realStream;
+ // reserve queue space
+ m_AudioQueueMutex.Lock();
+ m_AudioBufferQueue.reserve(kAudioQueueSize);
+ m_AudioQueueMutex.Unlock();
+ LoadSound();
+ }
+
+ m_MoviePlayback = movie;
+
+ if (movie)
+ {
+ m_ExternalStream = true;
+ LoadSound();
+ }
+
+ #if !UNITY_RELEASE
+ // right now we're trying to load sound in AwakeFromLoad (and do only that) - so we skip the call
+ HackSetAwakeWasCalled();
+ #endif
+
+ return true;
+}
+#endif
+
+bool AudioClip::CreateUserSound(const std::string& name, unsigned lengthSamples, short channels, unsigned frequency, bool _3D, bool stream)
+{
+ Reset();
+ Cleanup();
+
+ m_UserGenerated = true;
+ m_UserLengthSamples = lengthSamples;
+ m_UserIsStream = stream;
+ m_Channels = channels;
+ m_Frequency = frequency;
+ m_3D = _3D;
+ m_Format = FMOD_SOUND_FORMAT_PCMFLOAT;
+ m_BitsPerSample = 32;
+ SetName(name.c_str());
+
+ CreateScriptCallback();
+
+ m_Sound = CreateSound();
+
+#if !UNITY_RELEASE
+ // right now we're trying to load sound in AwakeFromLoad (and do only that) - so we skip the call
+ HackSetAwakeWasCalled();
+#endif
+
+ return true;
+}
+
+bool AudioClip::InitWSound (FMOD::Sound* sound)
+{
+ Assert(sound);
+ Cleanup();
+ CreateScriptCallback();
+ m_Sound = sound;
+ m_OpenState = FMOD_OPENSTATE_READY;
+ GetSoundProps();
+ sound->setUserData((void*)this);
+
+ return true;
+}
+
+
+
+AudioClip::~AudioClip ()
+{
+#if ENABLE_PROFILER
+ s_AudioClipCount--;
+#endif
+
+ Cleanup();
+#if ENABLE_WWW
+ if (m_StreamData)
+ {
+ m_StreamData->SetAudioClip( NULL );
+ m_StreamData->Release();
+ }
+#endif
+ if (m_MoviePlayback)
+ m_MoviePlayback->SetMovieAudioClip(NULL);
+}
+
+FMOD_SOUND_TYPE AudioClip::GetFormatFromExtension(const std::string& ext)
+{
+ std::string ext_lower = ToLower(ext);
+ FMOD_SOUND_TYPE type = FMOD_SOUND_TYPE_UNKNOWN;
+ if (ext_lower == "ogg")
+ type = FMOD_SOUND_TYPE_OGGVORBIS;
+ else
+ if (ext_lower == "mp2" || ext_lower == "mp3")
+ type = FMOD_SOUND_TYPE_MPEG;
+ else
+ if (ext_lower == "wav")
+ type = FMOD_SOUND_TYPE_WAV;
+ else
+ if (ext_lower == "it")
+ type = FMOD_SOUND_TYPE_IT;
+ else
+ if (ext_lower == "xm")
+ type = FMOD_SOUND_TYPE_XM;
+ else
+ if (ext_lower == "s3m")
+ type = FMOD_SOUND_TYPE_S3M;
+ else
+ if (ext_lower == "mod")
+ type = FMOD_SOUND_TYPE_MOD;
+
+ return type;
+}
+
+bool AudioClip::IsFormatSupportedByPlatform(const std::string& ext)
+{
+ FMOD_SOUND_TYPE type = GetFormatFromExtension(ext);
+ if(type == FMOD_SOUND_TYPE_UNKNOWN)
+ return false;
+ if(type == FMOD_SOUND_TYPE_OGGVORBIS && (UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN))
+ return false;
+ if(type == FMOD_SOUND_TYPE_MPEG && !UNITY_IPHONE && !UNITY_ANDROID && !UNITY_BB10 && !UNITY_TIZEN)
+ return false;
+ return true;
+}
+
+void AudioClip::Cleanup()
+{
+ if (!m_CachedSounds.empty())
+ {
+ for (TFMODSounds::iterator it = m_CachedSounds.begin(); it != m_CachedSounds.end(); ++it)
+ {
+ if ((*it).second)
+ (*it).second->stop();
+ if ((*it).first)
+ (*it).first->release();
+ }
+ m_CachedSounds.clear();
+ }
+ else
+ {
+ if (m_Sound)
+ {
+ m_Sound->release();
+ }
+ }
+
+ m_Sound = NULL;
+}
+
+
+void AudioClip::CleanupClass()
+{
+
+}
+
+
+bool AudioClip::ReadyToPlay ()
+{
+ if(
+#if ENABLE_WWW
+ !m_StreamData &&
+#endif
+ !m_MoviePlayback)
+ return true;
+
+ if (m_OpenState == FMOD_OPENSTATE_READY)
+ return true;
+
+
+ // try to...
+ LoadSound();
+
+ if (!m_Sound)
+ {
+ m_OpenState = FMOD_OPENSTATE_CONNECTING;
+ return false;
+ }
+
+ m_OpenState = FMOD_OPENSTATE_READY;
+
+ return true;
+}
+
+void AudioClip::SetData(const float* data, unsigned lengthSamples, unsigned offsetSamples /*= 0*/)
+{
+ if (m_Sound)
+ {
+ if (IsFMODStream())
+ {
+ ErrorStringObject("Cannot set data on streamed sample", this);
+ return;
+ }
+
+ //Address of a pointer that will point to the first part of the locked data.
+ void *ptr1 = NULL;
+
+ //Address of a pointer that will point to the second part of the locked data. This will be null if the data locked hasn't wrapped at the end of the buffer.
+ void *ptr2 = NULL;
+
+ //Length of data in bytes that was locked for ptr1
+ unsigned len1 = 0;
+
+ // Length of data in bytes that was locked for ptr2. This will be 0 if the data locked hasn't wrapped at the end of the buffer.
+ unsigned len2 = 0;
+
+ unsigned samplesToCopy = lengthSamples;
+ unsigned clipSampleCount = GetSampleCount();
+ if (lengthSamples > clipSampleCount)
+ {
+ WarningString(Format("Data too long to fit the audioclip: %s. %i sample(s) discarded", GetName(), (lengthSamples - clipSampleCount)));
+ samplesToCopy = clipSampleCount;
+ }
+
+ //Offset in bytes to the position you want to lock in the sample buffer.
+ int offsetBytes = offsetSamples * m_Channels * (m_BitsPerSample / 8);
+
+ //Number of bytes you want to lock in the sample buffer.
+ int lengthBytes = samplesToCopy * m_Channels * (m_BitsPerSample / 8);
+
+ FMOD_RESULT result = m_Sound->lock(offsetBytes, lengthBytes, &ptr1, &ptr2, &len1, &len2);
+
+ FMOD_ASSERT(result);
+
+ if (ptr2 == NULL)
+ {
+ ArrayFromNormFloat(m_Format, data, data + samplesToCopy * m_Channels, ptr1);
+ }
+ else // wrap
+ {
+ ArrayFromNormFloat(m_Format, data, data + (len1 / sizeof(float)), ptr1);
+ ArrayFromNormFloat(m_Format, data + len1 / sizeof(float), data + (len1 + len2) / sizeof(float), ptr2);
+ }
+
+ m_Sound->unlock(ptr1, ptr2, len1, len2);
+ }
+}
+
+
+void AudioClip::GetData(float* data, unsigned lengthSamples, unsigned offsetSamples /*= 0*/) const
+{
+ if (m_Sound)
+ {
+ if (IsFMODStream())
+ {
+ ErrorStringObject("Cannot get data from streamed sample", this);
+ return;
+ }
+
+ //Address of a pointer that will point to the first part of the locked data.
+ void *ptr1 = NULL;
+
+ //Address of a pointer that will point to the second part of the locked data. This will be null if the data locked hasn't wrapped at the end of the buffer.
+ void *ptr2 = NULL;
+
+ //Length of data in bytes that was locked for ptr1
+ unsigned len1 = 0;
+
+ // Length of data in bytes that was locked for ptr2. This will be 0 if the data locked hasn't wrapped at the end of the buffer.
+ unsigned len2 = 0;
+
+ unsigned samplesToCopy = lengthSamples;
+ unsigned clipSampleCount = GetSampleCount();
+ if (lengthSamples > clipSampleCount)
+ {
+ WarningString(Format("Data longer than the AudioClip: %s. %i sample(s) copied", GetName(), clipSampleCount));
+ samplesToCopy = clipSampleCount;
+ }
+
+ //Offset in bytes to the position you want to lock in the sample buffer.
+ int offsetBytes = offsetSamples * m_Channels * (m_BitsPerSample / 8);
+
+ //Number of bytes you want to lock in the sample buffer.
+ int lengthBytes = samplesToCopy * m_Channels * (m_BitsPerSample / 8);
+
+ unsigned totalLengthBytes;
+ m_Sound->getLength(&totalLengthBytes, FMOD_TIMEUNIT_PCMBYTES);
+ Assert ( totalLengthBytes >= lengthBytes );
+
+ FMOD_RESULT result = m_Sound->lock(offsetBytes, lengthBytes, &ptr1, &ptr2, &len1, &len2);
+
+ FMOD_ASSERT(result);
+
+ if (ptr2 == NULL)
+ {
+ ArrayToNormFloat(m_Format, ptr1, ((char*)ptr1 + len1), data);
+ }
+ else // wrap
+ {
+ if (len1 + len2 > lengthBytes)
+ {
+ WarningString(Format("Array can not hold the number of samples (%d)", (len1 + len2) - lengthBytes));
+ }
+ else
+ {
+ ArrayToNormFloat(m_Format, ptr1, ((char*)ptr1 + len1), data);
+ ArrayToNormFloat(m_Format, ptr2, ((char*)ptr2 + len2), data + (len1 / 4));
+ }
+ }
+
+ m_Sound->unlock(ptr1, ptr2, len1, len2);
+ }
+}
+
+
+// Set the attached movie clip
+void AudioClip::SetMoviePlayback(MoviePlayback* movie)
+{
+ m_MoviePlayback = movie;
+
+ if (!movie)
+ return;
+
+ m_ExternalStream = true;
+
+ // unload any attached www data
+#if ENABLE_WWW
+ if (m_StreamData)
+ m_StreamData->Release();
+ m_StreamData = NULL;
+#endif
+
+ m_Channels = movie->GetMovieAudioChannelCount();
+ m_Frequency = movie->GetMovieAudioRate();
+ m_Format = FMOD_SOUND_FORMAT_PCM16;
+ m_Type = FMOD_SOUND_TYPE_RAW;
+ m_BitsPerSample = 16;
+
+ m_OpenState = FMOD_OPENSTATE_CONNECTING;
+}
+
+
+MoviePlayback* AudioClip::GetMoviePlayback() const
+{
+ return m_MoviePlayback;
+}
+
+
+/**
+ * Queue PCM data into clip
+ * This is read by streamed sound
+ * @param buffer Audio data to queue
+ * @param size Size of Audio data
+ **/
+bool AudioClip::QueueAudioData(void* buffer, unsigned size)
+{
+ Mutex::AutoLock lock (m_AudioQueueMutex);
+ if (m_AudioBufferQueue.size() + size < kAudioQueueSize)
+ {
+ m_AudioBufferQueue.insert ( m_AudioBufferQueue.end(), (UInt8*)buffer, (UInt8*)buffer+size );
+ return true;
+ }
+
+ return false;
+}
+
+
+/**
+ * Top audio data from the quene
+ * @note buf must allocate size bytes
+ * @param size The size in bytes you want to top
+ * @return The audio data
+ **/
+bool AudioClip::GetQueuedAudioData(void** buf, unsigned size)
+{
+ Mutex::AutoLock lock(m_AudioQueueMutex);
+ if (m_AudioBufferQueue.size() < size)
+ return false;
+
+ memcpy( *buf, &m_AudioBufferQueue[0], size);
+
+ m_AudioBufferQueue.erase(m_AudioBufferQueue.begin(), m_AudioBufferQueue.begin() + size);
+
+ return true;
+}
+
+
+void AudioClip::ClearQueue()
+{
+ Mutex::AutoLock lock(m_AudioQueueMutex);
+
+ m_AudioBufferQueue.clear();
+}
+
+
+
+unsigned int AudioClip::GetSampleCount() const
+{
+ if (m_MoviePlayback)
+ {
+ if (m_MoviePlayback->GetMovieTotalDuration() < 0)
+ return 0;
+ else
+ return (unsigned int)(m_MoviePlayback->GetMovieTotalDuration() * m_Frequency * m_Channels);
+ }
+
+ if (m_Sound == NULL)
+ return 0;
+ unsigned int sc = 0;
+ m_Sound->getLength(&sc, FMOD_TIMEUNIT_PCM);
+ return sc;
+}
+
+
+unsigned int AudioClip::GetChannelCount() const
+{
+ return m_Channels;
+}
+unsigned int AudioClip::GetBitRate() const
+{
+ return m_Frequency * m_BitsPerSample;
+}
+
+unsigned int AudioClip::GetBitsPerSample() const
+{
+ return m_BitsPerSample;
+}
+
+unsigned int AudioClip::GetFrequency() const
+{
+ return m_Frequency;
+}
+
+
+int AudioClip::GetRuntimeMemorySize() const
+{
+ return Super::GetRuntimeMemorySize() + GetSize();
+}
+
+// Return the total memory used by this audioclip for the current target
+unsigned int AudioClip::GetSize() const
+{
+ unsigned totalSize = 0;
+ unsigned fmodSize = 0;
+ if (m_Sound) m_Sound->getMemoryInfo(FMOD_MEMBITS_ALL, 0, &fmodSize, NULL);
+
+ // compressed in memory
+ if ( m_LoadFlag == kCompressedInMemory )
+ {
+ switch (m_Type) {
+ case FMOD_SOUND_TYPE_XMA:
+ case FMOD_SOUND_TYPE_GCADPCM:
+ totalSize += m_AudioData.capacity();
+ break;
+ case FMOD_SOUND_TYPE_OGGVORBIS:
+ totalSize += m_AudioData.capacity(); // stream from memory
+ default:
+ totalSize += fmodSize;
+ break;
+ }
+ }
+ else
+ if ( m_LoadFlag == kDecompressOnLoad )
+ // decompressed
+ {
+ totalSize += fmodSize;
+ }
+ else
+ // stream from disc
+ if ( m_LoadFlag == kStreamFromDisc )
+ {
+ unsigned streamBufferSize = 0;
+ GetAudioManager().GetFMODSystem()->getStreamBufferSize(&streamBufferSize, NULL);
+ totalSize += streamBufferSize;
+ }
+
+ return totalSize;
+}
+
+unsigned int AudioClip::GetLength() const
+{
+ if (m_MoviePlayback)
+ return (unsigned int)(m_MoviePlayback->GetMovieTotalDuration() * 1000.0f);
+
+ if (m_Sound == NULL)
+ return 0;
+ unsigned int sc = 0;
+ m_Sound->getLength(&sc, FMOD_TIMEUNIT_MS);
+ return sc;
+}
+
+float AudioClip::GetLengthSec() const
+{
+ if (m_MoviePlayback)
+ return m_MoviePlayback->GetMovieTotalDuration();
+
+ if (m_Sound == NULL)
+ return 0;
+ unsigned int sc = 0;
+ m_Sound->getLength(&sc, FMOD_TIMEUNIT_MS);
+ return sc / 1000.f;
+}
+
+MoviePlayback* AudioClip::GetMovie() const
+{
+ return m_MoviePlayback;
+}
+
+
+bool AudioClip::IsOggCompressible() const
+{
+ return (m_Type != FMOD_SOUND_TYPE_MOD) &&
+ (m_Type != FMOD_SOUND_TYPE_S3M) &&
+ (m_Type != FMOD_SOUND_TYPE_MIDI) &&
+ (m_Type != FMOD_SOUND_TYPE_XM) &&
+ (m_Type != FMOD_SOUND_TYPE_IT) &&
+ (m_Type != FMOD_SOUND_TYPE_SF2) &&
+ ((m_Type == FMOD_SOUND_TYPE_WAV) &&
+ (m_Format != FMOD_SOUND_FORMAT_PCM32));
+}
+
+bool AudioClip::IsFMODStream () const
+{
+ bool isStream = false;
+
+ if (m_Sound)
+ {
+ FMOD_MODE mode;
+ m_Sound->getMode(&mode);
+ isStream = (mode & FMOD_CREATESTREAM) != 0;
+ }
+
+ return isStream;
+}
+
+
+FMOD::Channel* AudioClip::GetCachedChannel()
+{
+ // Assert this is a stream
+ Assert(GetMode() & FMOD_CREATESTREAM);
+
+ TFMODSounds::iterator it = m_CachedSounds.begin();
+
+ for (; it != m_CachedSounds.end(); ++it)
+ {
+ m_Sound = (*it).first;
+ FMOD::Channel* channel = (*it).second;
+ FMOD_RESULT result = FMOD_OK;
+ bool isPlaying = false, isPaused = false;
+ if (channel)
+ {
+ result = channel->isPlaying(&isPlaying);
+ result = channel->getPaused(&isPaused);
+ }
+
+ if (!isPlaying && !isPaused)
+ {
+ if (channel != NULL)
+ {
+ // Detach from old audiosource
+ AudioSource* audioSource = AudioSource::GetAudioSourceFromChannel( channel );
+ if (audioSource)
+ {
+ audioSource->Stop(channel);
+ result = GetAudioManager().GetFMODSystem()->playSound(FMOD_CHANNEL_FREE, m_Sound, true, &channel);
+ }
+ else
+ result = GetAudioManager().GetFMODSystem()->playSound(FMOD_CHANNEL_FREE, m_Sound, true, &channel);
+ }
+ else
+ {
+ result = GetAudioManager().GetFMODSystem()->playSound(FMOD_CHANNEL_FREE, m_Sound, true, &channel);
+ }
+
+ Assert (result == FMOD_OK);
+ (*it).second = channel;
+
+ if (channel)
+ return channel;
+ }
+ }
+
+ // no sounds available in the pool - create one
+ m_Sound = CreateSound();
+
+ if (m_Sound)
+ {
+ FMOD::Channel* channel = GetAudioManager().GetFreeFMODChannel( m_Sound );
+ if (channel != NULL)
+ m_CachedSounds.push_back( std::make_pair( m_Sound, channel ) );
+ return channel;
+ }
+
+ return NULL;
+}
+
+#if UNITY_IPHONE
+// IPHONE hardware
+// Rationale: Lazy create sound on channel request (Play()) to not hold onto the hw decoder more than needed,
+// if the sound is already created, then release it, to give it a chance to get the hw decoder upon create.
+// if another stream is owing the hw decoder, fallback on software (CreateFMODSound() handles that)
+// This add some latency on Play (but that's acceptable for streams)
+FMOD::Channel* AudioClip::CreateIOSStreamChannel()
+{
+ FMOD::Channel* channel = NULL;
+ if (m_UseHardware)
+ {
+ if (m_Sound) m_Sound->release();
+ if (!m_StreamingInfo.IsValid())
+ {
+ m_Sound = GetAudioManager().CreateFMODSound((const char*)&m_AudioData[0], GetExInfo(), GetMode(), m_UseHardware);
+ }
+ else
+ {
+ m_Sound = GetAudioManager().CreateFMODSound(m_StreamingInfo.path.c_str(), GetExInfo(), GetMode(), m_UseHardware);
+ }
+
+ Assert(m_Sound);
+ channel = GetAudioManager().GetFreeFMODChannel(m_Sound);
+ }
+ else
+ channel = GetCachedChannel();
+
+ return channel;
+}
+#endif // UNITY_IPHONE
+
+FMOD::Channel* AudioClip::CreateChannel(AudioSource* forSource)
+{
+ FMOD::Channel* channel = NULL;
+ if (GetMode() & FMOD_CREATESTREAM)
+ {
+#if ENABLE_WWW
+ if (m_WWWStreamed)
+ {
+ // Recreate sound if it's streamed WWW/Custom (reusing a sound will trigger a seek - which is not supported)
+ if (m_Sound)
+ m_Sound->release();
+ m_Sound = CreateSound();
+ Assert(m_Sound);
+ channel = GetAudioManager().GetFreeFMODChannel(m_Sound);
+ }
+ else
+#endif // ENABLE_WWW
+#if UNITY_IPHONE
+ channel = CreateIOSStreamChannel();
+#else // UNITY_IPHONE
+ channel = GetCachedChannel();
+#endif // UNITY_IPHONE
+ }
+ else
+ {
+ channel = GetAudioManager().GetFreeFMODChannel(m_Sound);
+ }
+
+ if (channel) channel->setMode(Is3D()?FMOD_3D:FMOD_2D);
+
+ return channel;
+}
+
+FMOD_CREATESOUNDEXINFO AudioClip::GetExInfo() const
+{
+ FMOD_CREATESOUNDEXINFO exinfo;
+ memset(&exinfo, 0, sizeof(exinfo));
+ exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
+#if UNITY_EDITOR
+ exinfo.suggestedsoundtype = m_EditorAudioData.empty()?m_Type:m_EditorSoundType;
+ exinfo.format = m_EditorAudioData.empty()?m_Format:m_EditorSoundFormat;
+ exinfo.length = m_EditorAudioData.empty()?m_AudioData.size():m_EditorAudioData.size();
+#else
+ exinfo.suggestedsoundtype = m_Type;
+ exinfo.format = m_Format;
+ exinfo.length = m_AudioData.size();
+#endif
+
+ exinfo.defaultfrequency = m_Frequency;
+ exinfo.numchannels = m_Channels;
+ if (m_UserGenerated)
+ {
+ exinfo.length = m_UserLengthSamples * m_Channels * 4;
+ exinfo.pcmreadcallback = AudioClip::ScriptPCMReadCallback;
+ exinfo.pcmsetposcallback = AudioClip::ScriptPCMSetPositionCallback;
+ }
+
+ if (m_StreamingInfo.IsValid())
+ {
+ exinfo.length = m_StreamingInfo.size;
+ exinfo.fileoffset = m_StreamingInfo.offset;
+ }
+
+ exinfo.userdata = (void*)this;
+
+ return exinfo;
+}
+
+FMOD_MODE AudioClip::GetMode() const
+{
+ FMOD_MODE mode = (m_3D?FMOD_3D:FMOD_2D) | FMOD_LOOP_NORMAL | FMOD_3D_CUSTOMROLLOFF | FMOD_MPEGSEARCH;
+
+#if UNITY_WII
+ mode |= (m_UseHardware || m_Type == FMOD_SOUND_TYPE_GCADPCM) ? FMOD_HARDWARE : FMOD_SOFTWARE;
+#else
+ mode |= FMOD_SOFTWARE;
+#endif
+
+
+ if (m_ExternalStream)
+ {
+ mode |= FMOD_CREATESTREAM;
+ }
+ else
+ if (m_UserGenerated)
+ {
+ mode |= FMOD_OPENUSER;
+ if (m_UserIsStream)
+ mode |= FMOD_CREATESTREAM;
+ }
+ else
+ if (m_StreamingInfo.IsValid())
+ mode |= FMOD_CREATESTREAM;
+ else
+ {
+ mode |= FMOD_OPENMEMORY;
+
+ if (m_LoadFlag == kCompressedInMemory)
+ {
+ // if MP2/MP3, ADPCM, CELT, XMA then we can load the audio compressed directly into FMOD
+ if (m_Type == FMOD_SOUND_TYPE_MPEG ||
+ m_Type == FMOD_SOUND_TYPE_XMA ||
+ m_Type == FMOD_SOUND_TYPE_GCADPCM ||
+ m_Type == FMOD_SOUND_TYPE_WAV ||
+ m_Type == FMOD_SOUND_TYPE_AIFF ||
+ m_Type == FMOD_SOUND_TYPE_IT ||
+ m_Type == FMOD_SOUND_TYPE_XM ||
+ m_Type == FMOD_SOUND_TYPE_S3M ||
+ m_Type == FMOD_SOUND_TYPE_MOD )
+ {
+ // From docs - "...Can only be used in combination with FMOD_SOFTWARE...."
+ if ((mode & FMOD_SOFTWARE) != 0) mode |= FMOD_CREATECOMPRESSEDSAMPLE;
+ }
+ else
+ // if sound is ogg we have to stream it to FMOD to keep it compressed in memory
+ // @TODO use CELT instead of OGG. Soon.
+ {
+ mode |= FMOD_CREATESTREAM;
+ }
+ }
+ }
+
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ {
+ if(m_Type == FMOD_SOUND_TYPE_MPEG ||
+ m_Type == FMOD_SOUND_TYPE_MOD ||
+ m_Type == FMOD_SOUND_TYPE_IT ||
+ m_Type == FMOD_SOUND_TYPE_S3M ||
+ m_Type == FMOD_SOUND_TYPE_XM)
+ {
+ mode |= FMOD_ACCURATETIME;
+ }
+ }
+
+ return mode;
+}
+
+const UInt8* AudioClip::GetAudioData() const
+{
+#if UNITY_EDITOR
+ if (!m_EditorAudioData.empty())
+ return &m_EditorAudioData[0];
+ else
+#endif
+ return &m_AudioData[0];
+}
+
+FMOD::Sound* AudioClip::CreateSound()
+{
+ // if external streaming (WWW or movie)
+ if (m_ExternalStream)
+ {
+#if ENABLE_WWW
+ if (m_StreamData)
+ {
+ // Wait for the entire file to download before reporting ready
+ // @TODO we need a proper net stream solution
+ if (!m_WWWStreamed && ( m_StreamData->GetProgress() != 1.0f ))
+ return NULL;
+
+ return GetAudioManager().CreateFMODSoundFromWWW(m_StreamData,
+ m_3D, m_Type, m_Format, m_Frequency, m_Channels, m_WWWStreamed);
+ }
+ else
+#endif
+ if (m_MoviePlayback)
+ {
+ m_Frequency = m_MoviePlayback->GetMovieAudioRate();
+ m_Channels = m_MoviePlayback->GetMovieAudioChannelCount();
+ if (m_Frequency > 0 && m_Channels > 0)
+ return GetAudioManager().CreateFMODSoundFromMovie(this, m_3D);
+ }
+ }
+ else
+ {
+ if (m_UserGenerated)
+ {
+ m_Sound = GetAudioManager().CreateFMODSound(GetName(), GetExInfo(), GetMode(), m_UseHardware);
+ }
+ else
+ if (!m_StreamingInfo.IsValid())
+ {
+ if (m_AudioData.empty())
+ return NULL;
+
+ FMOD_MODE mode = GetMode();
+
+ m_Sound = GetAudioManager().CreateFMODSound(GetAudioData(), GetExInfo(), mode, m_UseHardware);
+
+ // Audio data is only loaded into FMOD once (if its not a stream). We can clear it after upload
+#if !UNITY_EDITOR
+ if (!(mode & FMOD_CREATESTREAM))
+ m_AudioData.clear();
+#endif
+ }
+ else
+ {
+ // assert that no audio data is loaded in memory
+ Assert (m_AudioData.empty());
+
+ m_Sound = GetAudioManager().CreateFMODSound(m_StreamingInfo.path.c_str(), GetExInfo(), GetMode(), m_UseHardware);
+ }
+ }
+
+ return m_Sound;
+}
+
+void AudioClip::GetSoundProps()
+{
+ if (!m_Sound)
+ return;
+
+ m_Sound->getFormat(m_Type == FMOD_SOUND_TYPE_UNKNOWN? &m_Type : NULL, m_Format == FMOD_SOUND_FORMAT_NONE ? &m_Format : NULL, &m_Channels, &m_BitsPerSample);
+
+ float ffrequency;
+ m_Sound->getDefaults( &ffrequency,NULL, NULL, NULL);
+
+ m_Frequency = (int)ffrequency;
+
+ FMOD_MODE mode;
+ m_Sound->getMode(&mode);
+ m_3D = (mode & FMOD_3D);
+}
+
+void AudioClip::Reload()
+{
+ if (m_AudioData.size() == 0)
+ {
+ GetPersistentManager().ReloadFromDisk(this);
+ // FYI: ReloadFromDisk() will call LoadSound() and calling LoadSound() twice is a bad thing.
+ }
+ else
+ LoadSound();
+}
+
+bool AudioClip::LoadSound()
+{
+ Cleanup();
+
+ AssertBreak(m_Sound == NULL);
+ m_Sound = CreateSound();
+
+ if (m_Sound == NULL)
+ {
+ return false;
+ }
+
+ m_OpenState = FMOD_OPENSTATE_READY;
+
+ if (m_ExternalStream&&!m_Sound)
+ {
+ m_OpenState = FMOD_OPENSTATE_CONNECTING;
+ return false;
+ }
+
+ if ((GetMode()&FMOD_CREATESTREAM)&&m_Sound&&!m_UseHardware&&!m_WWWStreamed)
+ {
+ m_CachedSounds.push_back( std::make_pair( m_Sound, (FMOD::Channel*)NULL) );
+ }
+
+ GetSoundProps();
+
+#if UNITY_IPHONE
+ // if it's a stream and it uses hardware, we release the sound here to relinqiush the hw decoder.
+ if (m_UseHardware&&(GetMode()&FMOD_CREATESTREAM))
+ {
+ m_Sound->release();
+ m_Sound = NULL;
+ }
+#endif
+
+
+ return true;
+}
+
+void AudioClip::ReleaseSound()
+{
+ if (m_Sound)
+ m_Sound->release();
+ m_Sound = NULL;
+}
+
+
+bool AudioClip::SetAudioDataSwap(dynamic_array<UInt8>& buffer,
+ bool threeD,
+ bool hardware,
+ int loadFlag,
+ bool externalStream,
+ FMOD_SOUND_TYPE type,
+ FMOD_SOUND_FORMAT format)
+{
+ m_ExternalStream = externalStream;
+ m_LoadFlag = loadFlag;
+ m_3D = threeD;
+ m_UseHardware = hardware;
+ m_Type = type;
+ m_Format = format;
+ Assert(buffer.owns_data());
+ m_AudioData.swap(buffer);
+
+ #if !UNITY_RELEASE
+ // right now we're trying to load sound in AwakeFromLoad (and do only that) - so we skip the call
+ HackSetAwakeWasCalled();
+ #endif
+
+ return LoadSound();
+}
+
+
+bool AudioClip::SetAudioData(const UInt8* buffer,
+ unsigned size,
+ bool threeD,
+ bool hardware,
+ int loadFlag,
+ bool externalStream,
+ FMOD_SOUND_TYPE type,
+ FMOD_SOUND_FORMAT format)
+{
+ m_ExternalStream = externalStream;
+ m_LoadFlag = loadFlag;
+ m_3D = threeD;
+ m_UseHardware = hardware;
+ m_Type = type;
+ m_Format = format;
+ m_AudioData.assign(buffer, buffer + size);
+ Assert (m_AudioData.size() == size);
+
+ #if !UNITY_RELEASE
+ // right now we're trying to load sound in AwakeFromLoad (and do only that) - so we skip the call
+ HackSetAwakeWasCalled();
+ #endif
+
+ return LoadSound();
+}
+
+
+
+#if UNITY_EDITOR
+
+void AudioClip::SetEditorAudioData( const dynamic_array<UInt8>& buffer, FMOD_SOUND_TYPE type, FMOD_SOUND_FORMAT format )
+{
+ m_EditorAudioData.assign(&buffer[0], &buffer[0] + buffer.size());
+ m_EditorSoundType = type;
+ m_EditorSoundFormat = format;
+}
+
+bool AudioClip::WriteRawDataToFile( const string& path ) const
+{
+ // first load entire file into byte array
+ // open the file
+ std::ofstream file(path.c_str(), std::ios::binary);
+
+ if(!file.is_open())
+ {
+ return false; // file problably doesnt exist
+ }
+
+ file.write((const char*)&m_AudioData[0], m_AudioData.size());
+
+ if (!file.good())
+ {
+ file.close();
+ return false;
+ }
+
+ file.close();
+
+ return true;
+}
+#endif
+
+void AudioClip::ConvertOldAsset(int frequency, int size, int channels, int bitsPerSample, UInt8* raw)
+{
+ UInt8* data;
+ UInt8* wav = CreateWAV(frequency, size, channels, bitsPerSample, &data);
+
+ // get header info
+ const int wav_size = GetWAVSize(wav);
+
+ // copy in raw data
+ memcpy(data, raw, size);
+
+ m_AudioData.clear();
+ m_AudioData.assign( wav, wav + wav_size);
+ Assert ( wav_size == m_AudioData.size() );
+ m_Channels = channels;
+ m_Frequency = frequency;
+ m_Format = FMOD_SOUND_FORMAT_PCM16;
+ m_Type = FMOD_SOUND_TYPE_WAV;
+
+ delete[] wav;
+}
+
+
+void AudioClip::CreateScriptCallback()
+{
+ #if ENABLE_SCRIPTING
+ #if ENABLE_MONO
+ monoDomain = mono_domain_get();
+ #endif
+
+ // cache script methods
+ ScriptingObjectPtr instance = Scripting::ScriptingWrapperFor(this);
+
+ if (instance)
+ {
+ // cache delegate invokers
+ m_CachedPCMReaderCallbackMethod = GetScriptingMethodRegistry().GetMethod( "UnityEngine", "AudioClip", "InvokePCMReaderCallback_Internal" );
+ m_CachedSetPositionCallbackMethod = GetScriptingMethodRegistry().GetMethod ( "UnityEngine", "AudioClip", "InvokePCMSetPositionCallback_Internal" );
+ }
+ #endif
+}
+
+
+template<class TransferFunc>
+void AudioClip::TransferToFlash(TransferFunc& transfer)
+{
+ Assert(transfer.IsWriting());
+ int count = GetSampleCount();
+ transfer.Transfer(count, "samplecount");
+ transfer.Transfer (m_3D, "m_3D");
+ if (transfer.GetBuildingTarget().platform==kBuildWebGL)
+ // Since we can't do unaligned
+ transfer.Align();
+ transfer.Transfer(m_AudioData, "m_AudioData");
+
+}
+
+template<class TransferFunc>
+void AudioClip::Transfer (TransferFunc& transfer) {
+ Super::Transfer (transfer);
+
+ //if we're exporting to flash, write out data completely differently
+ if (transfer.IsWritingGameReleaseData () && (transfer.GetBuildingTarget().platform==kBuildFlash || transfer.GetBuildingTarget().platform==kBuildWebGL))
+ {
+ TransferToFlash(transfer);
+ return;
+ }
+
+ // 10/06-11: v4: Added editor only audio data
+ // 25/05-10: v4: Stream replaces decompressOnLoad
+ // 18/04-09: v3: FMOD asset data
+ // : v1-2: OpenAL asset data
+ transfer.SetVersion (4);
+
+ transfer.Transfer ((SInt32&)m_Format, "m_Format", kNotEditableMask);
+
+ if (transfer.IsCurrentVersion())
+ {
+ transfer.Transfer ((SInt32&)m_Type, "m_Type", kNotEditableMask);
+ transfer.Transfer (m_3D, "m_3D", kNotEditableMask);
+ transfer.Transfer (m_UseHardware, "m_UseHardware", kNotEditableMask);
+
+ transfer.Align();
+
+ transfer.Transfer (m_LoadFlag, "m_Stream", kNotEditableMask);
+
+ if (m_LoadFlag == kStreamFromDisc)
+ transfer.EnableResourceImage(kStreamingResourceImage);
+
+ if (!transfer.ReadStreamingInfo (&m_StreamingInfo))
+ transfer.Transfer(m_AudioData, "m_AudioData");
+
+ TRANSFER_EDITOR_ONLY(m_EditorAudioData);
+ TRANSFER_EDITOR_ONLY((SInt32&)m_EditorSoundType);
+ TRANSFER_EDITOR_ONLY((SInt32&)m_EditorSoundFormat);
+ }
+ else if (transfer.IsOldVersion(1) || transfer.IsOldVersion(2))
+ { // old/openal data
+ SInt32 oldFormat = m_Format;
+ m_Format = FORMAT_TO_FMOD_FORMAT[ oldFormat ];
+ m_Type = FORMAT_TO_FMOD_TYPE[ oldFormat ];
+
+ if (transfer.IsOldVersion (2))
+ transfer.Transfer (m_Frequency, "m_Frequency", kNotEditableMask);
+ else
+ transfer.Transfer (m_Frequency, "m_Freq", kNotEditableMask);
+ unsigned size = 0;
+ transfer.Transfer (size, "m_Size", kNotEditableMask);
+
+
+ // assumes that m_size is the sizes in bytes
+ transfer.TransferTypeless (&size, "audio data", kHideInEditorMask);
+
+ // clear audio data
+ m_AudioData.clear();
+ m_AudioData.resize_uninitialized(size);
+
+ // transfer data
+ transfer.TransferTypelessData (size, &m_AudioData[0]);
+
+ // data always have to be in little endian (WAV format)
+#if !UNITY_BIG_ENDIAN
+ if (transfer.ConvertEndianess ())
+ {
+ if (m_Type != FMOD_SOUND_TYPE_OGGVORBIS && m_Format == FMOD_SOUND_FORMAT_PCM16)
+ SwapEndianArray (&m_AudioData[0], 2, size / 2);
+ }
+#else
+ if (!transfer.ConvertEndianess ())
+ {
+ if (m_Type != FMOD_SOUND_TYPE_OGGVORBIS && m_Format == FMOD_SOUND_FORMAT_PCM16)
+ SwapEndianArray (&m_AudioData[0], 2, size / 2);
+ }
+#endif
+
+ bool decompressOnLoad = false;
+ transfer.Transfer (decompressOnLoad, "m_DecompressOnLoad", kNotEditableMask);
+ m_LoadFlag = decompressOnLoad ? kDecompressOnLoad : kCompressedInMemory;
+
+ // make a qualified guess for old asset data
+ //kMono8 = 1, kMono16, kStereo8, kStereo16, kOggVorbis
+ m_Channels = 2;
+ if (oldFormat == 1 || oldFormat == 2)
+ m_Channels = 1;
+ if (oldFormat == 5)
+ {
+ // we don't have any option but parsing the ogg file to obtain the channel count
+ // use our own ogg reader for that
+ int vorbisChannels = 0;
+ if (CheckOggVorbisFile (&m_AudioData[0], m_AudioData.size(), &vorbisChannels))
+ m_Channels = vorbisChannels;
+ }
+
+ // mono sounds were always 3D in OpenAL (in <=Unity 2.5) and 2D sounds not
+ m_3D = m_Channels == 1;
+
+ // convert old data to new
+ if (oldFormat != 5)
+ ConvertOldAsset(m_Frequency, size, m_Channels, 16, &m_AudioData[0]);
+ }
+ else if (transfer.IsOldVersion(3)) // FMOD version
+ {
+ transfer.Transfer ((SInt32&)m_Type, "m_Type", kNotEditableMask);
+
+ transfer.Transfer (m_3D, "m_3D", kNotEditableMask);
+
+ transfer.Align();
+
+ transfer.Transfer(m_AudioData, "m_AudioData");
+
+ bool decompressOnLoad = false;
+ transfer.Transfer (decompressOnLoad, "m_DecompressOnLoad", kNotEditableMask);
+ m_LoadFlag = decompressOnLoad ? kDecompressOnLoad : kCompressedInMemory;
+ }
+}
+
+void AudioClip::AwakeFromLoadThreaded ()
+{
+ Super::AwakeFromLoadThreaded();
+ LoadSound();
+}
+
+
+void AudioClip::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ // Load data if not done from another thread already
+ if ((awakeMode & kDidLoadThreaded) == 0)
+ {
+ LoadSound();
+ }
+}
+
+IMPLEMENT_CLASS_HAS_INIT (AudioClip)
+IMPLEMENT_OBJECT_SERIALIZE (AudioClip)
+
+#endif //ENABLE_AUDIO
diff --git a/Runtime/Audio/AudioClip.h b/Runtime/Audio/AudioClip.h
new file mode 100644
index 0000000..09a397f
--- /dev/null
+++ b/Runtime/Audio/AudioClip.h
@@ -0,0 +1,10 @@
+#ifndef AUDIOCLIP_H
+#define AUDIOCLIP_H
+
+#if !(UNITY_FLASH || UNITY_WEBGL)
+#include "Runtime/Audio/AudioClip_FMOD.h"
+#else
+#include "Runtime/Audio/AudioClip_Flash.h"
+#endif // !UNITY_FLASH
+
+#endif // AUDIOCLIP_H \ No newline at end of file
diff --git a/Runtime/Audio/AudioClip_FMOD.h b/Runtime/Audio/AudioClip_FMOD.h
new file mode 100644
index 0000000..535ca8d
--- /dev/null
+++ b/Runtime/Audio/AudioClip_FMOD.h
@@ -0,0 +1,347 @@
+#ifndef AUDIOCLIP_FMOD_H
+#define AUDIOCLIP_FMOD_H
+
+#if ENABLE_AUDIO
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Audio/correct_fmod_includer.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Serialize/CacheWrap.h"
+#include "Runtime/Mono/MonoIncludes.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+
+#if ENABLE_WWW
+class WWW;
+#endif
+
+class AudioSource;
+class MoviePlayback;
+namespace FMOD { class Channel; class Sound; }
+
+const unsigned kAudioQueueSize = (4 * 16384);
+
+
+class AudioClip : public NamedObject
+{
+
+public:
+ REGISTER_DERIVED_CLASS (AudioClip, NamedObject)
+ DECLARE_OBJECT_SERIALIZE (AudioClip)
+
+ AudioClip (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~AudioClip (); - declared-by-macro
+
+#if ENABLE_WWW
+ // WARNING: don't call AwakeFromLoad if you use InitStream
+ bool InitStream (WWW* streamData, MoviePlayback* movie, bool realStream = false, FMOD_SOUND_TYPE fmodSoundType = FMOD_SOUND_TYPE_UNKNOWN );
+
+ // Is reading the data from this clip allowed by webplayer security
+ void SetReadAllowed (bool allowed) { m_ReadAllowed = allowed; }
+ bool GetReadAllowed () const { return m_ReadAllowed; }
+#endif
+ bool InitWSound (FMOD::Sound* sound);
+ bool CreateUserSound(const std::string& name, unsigned lengthSamples, short channels, unsigned frequency, bool _3D, bool stream);
+
+ void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+ void AwakeFromLoadThreaded ();
+
+
+public:
+ enum { kDecompressOnLoad = 0, kCompressedInMemory = 1, kStreamFromDisc };
+
+ FMOD_SOUND_TYPE GetType() const { return m_Type; }
+ void SetType(FMOD_SOUND_TYPE type) { m_Type = type; }
+ FMOD_SOUND_FORMAT GetFormat() const { return m_Format; }
+
+ bool Get3D() const { return m_3D; }
+ void Set3D(bool threeD) { m_3D = threeD; }
+
+ FMOD::Channel* CreateChannel(AudioSource* forSource = NULL);
+ FMOD::Sound* CreateSound();
+
+ unsigned int GetSampleCount() const;
+ unsigned int GetChannelCount() const;
+ unsigned int GetBitRate() const;
+ unsigned int GetFrequency() const;
+ unsigned int GetSize() const;
+ unsigned int GetLength() const;
+ float GetLengthSec() const;
+ unsigned int GetBitsPerSample() const;
+ MoviePlayback* GetMovie() const;
+
+ void SetData(const float* data, unsigned lengthSamples, unsigned offsetSamples = 0);
+ void GetData(float* data, unsigned lengthSamples, unsigned offsetSamples = 0) const;
+
+ virtual int GetRuntimeMemorySize () const;
+
+ void AddSyncPoint( string name, UInt32 atSample );
+
+ bool Is3D() const { return m_3D; }
+ bool IsMovieAudio() const { return m_MoviePlayback != NULL; }
+ FMOD::Sound* GetSound() const { return m_Sound; }
+ void ReleaseSound();
+
+ bool IsOggCompressible() const;
+
+ bool IsStream() const { return (GetMode()&FMOD_CREATESTREAM); }
+
+ bool IsWWWStreamed() const { return m_WWWStreamed; }
+
+ bool IsFMODStream() const;
+
+ bool ReadyToPlay();
+
+ int GetLoadFlag() const { return m_LoadFlag; }
+ void SetLoadFlag( unsigned flag ) { Assert(flag < 3); m_LoadFlag = flag; }
+
+ bool IsHardware() const { return m_UseHardware; }
+
+ // Attached movie clip
+ void SetMoviePlayback(MoviePlayback* movie);
+ MoviePlayback* GetMoviePlayback() const;
+
+ /**
+ * Queue data in to clip
+ * This is read by streamed sound
+ * @param buffer Audio data to queue
+ * @param size Size of Audio data
+ **/
+ bool QueueAudioData(void* buffer, unsigned size);
+
+ /**
+ * Top audio data from the quene
+ * @note buf must allocate size bytes
+ * @param size The size in bytes you want to top
+ * @return The audio data
+ **/
+ bool GetQueuedAudioData(void** buf, unsigned size);
+
+ void ClearQueue();
+
+ // Set audiodata
+ // WARNING: don't call AwakeFromLoad if you use SetAudioData
+ bool SetAudioDataSwap(dynamic_array<UInt8>& buffer,
+ bool threeD,
+ bool hardware,
+ int loadFlag,
+ bool externalStream,
+ FMOD_SOUND_TYPE type,
+ FMOD_SOUND_FORMAT format);
+
+
+ bool SetAudioData(const UInt8* buffer,
+ unsigned size,
+ bool threeD,
+ bool hardware,
+ int loadFlag,
+ bool externalStream,
+ FMOD_SOUND_TYPE type,
+ FMOD_SOUND_FORMAT format);
+
+ // Set audiodata for playing in the editor
+ void SetEditorAudioData(const dynamic_array<UInt8>& buffer, FMOD_SOUND_TYPE type, FMOD_SOUND_FORMAT);
+
+
+#if UNITY_EDITOR
+ bool WriteRawDataToFile(const std::string& path) const;
+#endif
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ void Cleanup();
+ void Reload();
+
+ static FMOD_SOUND_TYPE GetFormatFromExtension(const std::string& ext);
+ static bool IsFormatSupportedByPlatform(const std::string& ext);
+
+private:
+ // Serialized data
+ int m_Frequency;
+ FMOD_SOUND_TYPE m_Type;
+ FMOD_SOUND_FORMAT m_Format;
+ int m_Channels;
+ bool m_3D;
+ bool m_UseHardware; ///< IPhone only (for now)
+ int m_BitsPerSample;
+ typedef dynamic_array<UInt8> TAudioData;
+ TAudioData m_AudioData; // contains the entire audio file
+#if UNITY_EDITOR
+ TAudioData m_EditorAudioData; // if m_AudioData contains "undecodable" data (XMA, M4A) use this instead (usually OGGVORBIS)
+ FMOD_SOUND_TYPE m_EditorSoundType;
+ FMOD_SOUND_FORMAT m_EditorSoundFormat;
+#endif
+
+ // buffer
+ typedef UNITY_VECTOR(kMemAudioData, UInt8) TAudioQueue;
+ TAudioQueue m_AudioBufferQueue;
+ Mutex m_AudioQueueMutex;
+
+ // Load flag
+ int m_LoadFlag;
+
+ StreamingInfo m_StreamingInfo;
+
+ bool m_UserGenerated;
+ unsigned m_UserLengthSamples;
+ bool m_UserIsStream;
+
+#if ENABLE_WWW
+ WWW *m_StreamData;
+ bool m_ReadAllowed;
+#endif
+
+ bool m_ExternalStream;
+
+ //this is used, if this clip is tied to a movie to control playback
+ MoviePlayback *m_MoviePlayback;
+
+private:
+ FMOD::Sound* m_Sound;
+ FMOD_OPENSTATE m_OpenState;
+
+ // Script callbacks
+ #if ENABLE_MONO
+ MonoDomain* monoDomain;
+ #endif
+ ScriptingArrayPtr m_PCMArray;
+ int m_PCMArrayGCHandle;
+ ScriptingMethodPtr m_CachedPCMReaderCallbackMethod;
+ ScriptingMethodPtr m_CachedSetPositionCallbackMethod;
+
+ ScriptingArrayPtr GetScriptPCMArray(unsigned length);
+ unsigned m_DecodeBufferSize;
+
+private:
+ // Cached sounds for streamed audio.
+ // FMOD doesn't support multiple channels for a streamed sound (due to file ptrs, buffers)
+ typedef std::vector<std::pair<FMOD::Sound*, FMOD::Channel*> > TFMODSounds;
+ mutable TFMODSounds m_CachedSounds;
+
+ bool LoadSound();
+ void GetSoundProps();
+ FMOD::Channel* GetCachedChannel();
+ FMOD_CREATESOUNDEXINFO GetExInfo() const;
+ FMOD_MODE GetMode() const;
+ const UInt8* GetAudioData() const;
+
+#if UNITY_IPHONE
+ FMOD::Channel* CreateIOSStreamChannel();
+#endif
+
+ /**
+ * Convert old asset data to new (by reconstructing the a wav header)
+ * @param frequency Frequency
+ * @param size The size of the data
+ * @param data Array of raw data
+ **/
+ void ConvertOldAsset(int frequency, int size, int channels, int bitsPerSample, UInt8* raw);
+
+ template<class TransferFunc>
+ void TransferToFlash(TransferFunc& transfer);
+
+ void CreateScriptCallback();
+private:
+ // Streaming
+ bool m_WWWStreamed;
+
+ struct movieUserData {
+ MoviePlayback* movie;
+ unsigned read;
+ };
+
+#if ENABLE_WWW
+ struct wwwUserData {
+ bool seek;
+ WWW* stream;
+ unsigned pos;
+ unsigned filesize;
+ };
+
+
+ // Callbacks for WWW streaming
+ static FMOD_RESULT F_CALLBACK WWWOpen(
+ const char * www, // WWW class pointer
+ int unicode,
+ unsigned int * filesize,
+ void ** handle,
+ void ** userdata
+ );
+
+ static FMOD_RESULT F_CALLBACK WWWClose(
+ void * handle,
+ void * userdata
+ );
+ static FMOD_RESULT F_CALLBACK WWWRead(
+ void * handle,
+ void * buffer,
+ unsigned int sizebytes,
+ unsigned int * bytesread,
+ void * userdata
+ );
+ static FMOD_RESULT F_CALLBACK WWWSeek(
+ void * handle,
+ unsigned int pos,
+ void * userdata
+ );
+#endif
+ // Callbacks for movie streaming
+ static FMOD_RESULT F_CALLBACK movieopen(
+ const char * buffer,
+ int unicode,
+ unsigned int * filesize,
+ void ** handle,
+ void ** userdata
+ );
+
+ static FMOD_RESULT F_CALLBACK movieclose(
+ void * handle,
+ void * userdata
+ );
+ static FMOD_RESULT F_CALLBACK movieread(
+ void * handle,
+ void * buffer,
+ unsigned int sizebytes,
+ unsigned int * bytesread,
+ void * userdata
+ );
+ static FMOD_RESULT F_CALLBACK movieseek(
+ void * handle,
+ unsigned int pos,
+ void * userdata
+ );
+
+
+ // Callbacks for PCM streaming
+ static FMOD_RESULT F_CALLBACK pcmread(
+ FMOD_SOUND * sound,
+ void * data,
+ unsigned int datalen
+ );
+
+ // Callbacks for PCM streaming
+ static FMOD_RESULT F_CALLBACK ScriptPCMReadCallback(
+ FMOD_SOUND * sound,
+ void * data,
+ unsigned int datalen
+ );
+
+ static FMOD_RESULT F_CALLBACK ScriptPCMSetPositionCallback(
+ FMOD_SOUND * sound,
+ int subsound,
+ unsigned int position,
+ FMOD_TIMEUNIT postype);
+
+
+ friend class AudioManager;
+
+#if ENABLE_PROFILER
+ static int s_AudioClipCount;
+#endif
+};
+
+#endif //ENABLE_AUDIO
+#endif
diff --git a/Runtime/Audio/AudioClip_Flash.cpp b/Runtime/Audio/AudioClip_Flash.cpp
new file mode 100644
index 0000000..a5d9ebb
--- /dev/null
+++ b/Runtime/Audio/AudioClip_Flash.cpp
@@ -0,0 +1,122 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#if (UNITY_FLASH || UNITY_WEBGL) && ENABLE_AUDIO
+
+
+#include "AudioClip.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+#if ENABLE_WWW
+#include "Runtime/Export/WWW.h"
+#endif
+
+AudioClip::AudioClip (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_Mp3InMemoryLength = NULL;
+}
+
+AudioClip::~AudioClip()
+{
+#if UNITY_WEBGL
+ if (m_Mp3InMemoryLength)
+ UNITY_FREE (kMemAudio, m_Mp3InMemory);
+#endif
+}
+
+template<>
+void AudioClip::Transfer<StreamedBinaryRead<false> > (StreamedBinaryRead<false>& transfer) {
+ Super::Transfer (transfer);
+
+ transfer.Transfer(m_SampleCount,"samplecount");
+ transfer.Transfer (m_3D, "m_3D");
+#if UNITY_WEBGL
+ transfer.Align();
+#endif
+ transfer.Transfer(m_Mp3InMemoryLength,"mp3length");
+
+#if UNITY_WEBGL
+ m_Mp3InMemory = (UInt8*)UNITY_MALLOC(kMemAudio,m_Mp3InMemoryLength);
+ transfer.TransferTypelessData (m_Mp3InMemoryLength, &m_Mp3InMemory[0]);
+#else
+ CachedReader& reader = transfer.GetCachedReader();
+ UInt8* ptr = reader.GetCacher()->GetAddressOfMemory();
+ m_Mp3InMemory = ptr + reader.GetPosition();
+ reader.SetAbsoluteMemoryPosition(m_Mp3InMemory + m_Mp3InMemoryLength);
+#endif
+ transfer.Align();
+}
+
+template<class TransferFunc>
+void AudioClip::Transfer (TransferFunc& transfer) {
+ Super::Transfer (transfer);
+}
+
+bool AudioClip::InitStream (WWW* streamData, void* movie, bool realStream)
+{
+#if UNITY_WEBGL // No WWW yet on WebGL.
+ return false;
+#else
+ if(realStream)
+ ErrorString("Streaming MP3 is currently unsupported on Flash.");
+
+ bool succeeded = false;
+ if(streamData != NULL && streamData->GetError() == NULL){
+ m_StreamData = streamData;
+ if(LoadSoundFrom((void*)m_StreamData->GetData(), m_StreamData->GetSize())){
+ m_StreamData->SetAudioClip(this);
+ m_IsWWWAudioClip = true;
+ succeeded=true;
+ }
+ }
+
+ if(!succeeded){
+ m_StreamData = NULL;
+ ErrorString("Failed to load Audio from stream");
+ }
+
+ return succeeded;
+#endif
+}
+
+AudioChannel* AudioClip::CreateChannel(AudioSource* forSource)
+{
+ return new AudioChannel(m_SoundObject,forSource->GetLoop(),m_SampleCount);
+}
+
+void AudioClip::LoadSound()
+{
+ LoadSoundFrom(m_Mp3InMemory,m_Mp3InMemoryLength);
+}
+
+float AudioClip::GetLengthSec()
+{
+ if(m_SoundObject != NULL){
+ return (float)Ext_Sound_Get_Length(m_SoundObject);
+ }
+ return 0.0f;
+}
+
+bool AudioClip::LoadSoundFrom(void* ptr, size_t length)
+{
+ m_SoundObject = Ext_Sound_Load(ptr, length);
+ if(m_SoundObject != NULL){
+ double length = Ext_Sound_Get_Length(m_SoundObject);
+ if(length != 0){
+ return true;
+ }
+ }
+ return false;
+}
+
+void AudioClip::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ LoadSound();
+}
+
+IMPLEMENT_CLASS (AudioClip)
+IMPLEMENT_OBJECT_SERIALIZE (AudioClip)
+
+#endif //UNITY_FLASH
diff --git a/Runtime/Audio/AudioClip_Flash.h b/Runtime/Audio/AudioClip_Flash.h
new file mode 100644
index 0000000..b3a9f10
--- /dev/null
+++ b/Runtime/Audio/AudioClip_Flash.h
@@ -0,0 +1,73 @@
+#ifndef AUDIOCLIP_FLASH_H
+#define AUDIOCLIP_FLASH_H
+#include "Configuration/UnityConfigure.h"
+
+#if (UNITY_FLASH || UNITY_WEBGL) && ENABLE_AUDIO
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Serialize/CacheWrap.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Audio/AudioSource.h"
+#include "PlatformDependent/FlashSupport/cpp/AudioChannel.h"
+
+#if ENABLE_WWW
+class WWW;
+#endif
+
+class AudioClip : public NamedObject
+{
+
+public:
+ REGISTER_DERIVED_CLASS (AudioClip, NamedObject)
+ DECLARE_OBJECT_SERIALIZE (AudioClip)
+
+ AudioClip (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~AudioClip (); - declared-by-macro
+
+ void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+ float GetLengthSec();
+
+ bool ReadyToPlay() { return true; }
+ int GetFrequency() { return 44100; }
+ unsigned int GetSampleCount() { return m_SampleCount; }
+ int GetChannelCount() { return 2; }
+ AudioChannel* CreateChannel(AudioSource* forSource = NULL);
+
+ bool Get3D() const { return m_3D; }
+ void Set3D(bool threeD) { m_3D = threeD; }
+
+ void SetData(const float* data, unsigned lengthSamples, unsigned offsetSamples = 0) {}
+ void GetData(float* data, unsigned lengthSamples, unsigned offsetSamples = 0) const {}
+
+ void SetReadAllowed (bool allowed) { m_ReadAllowed = allowed; }
+ bool GetReadAllowed () const { return m_ReadAllowed; }
+
+ bool InitStream (WWW* streamData, void* movie, bool realStream = false);
+
+private:
+ void LoadSound();
+ bool LoadSoundFrom(void* ptr, size_t length);
+
+ bool m_IsWWWAudioClip;
+ bool m_ReadAllowed;
+ bool m_3D;
+ UInt8* m_Mp3InMemory;
+ int m_Mp3InMemoryLength;
+ unsigned int m_SampleCount;
+#if UNITY_FLASH
+ AS3Handle m_SoundObject;
+#elif UNITY_WEBGL
+ int m_SoundObject;
+#endif
+ WWW* m_StreamData;
+
+
+
+ friend class AudioManager;
+
+};
+
+#endif //UNITY_FLASH
+#endif
diff --git a/Runtime/Audio/AudioCustomFilter.cpp b/Runtime/Audio/AudioCustomFilter.cpp
new file mode 100644
index 0000000..4907c64
--- /dev/null
+++ b/Runtime/Audio/AudioCustomFilter.cpp
@@ -0,0 +1,210 @@
+/*
+ * AudioCustomFilter.cpp
+ * Xcode
+ *
+ * Created by Søren Christiansen on 9/12/10.
+ * Copyright 2010 Unity Technologies. All rights reserved.
+ *
+ */
+#include "UnityPrefix.h"
+#if ENABLE_AUDIO_FMOD
+#include "AudioCustomFilter.h"
+#include "AudioSource.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoIncludes.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Mono/MonoScriptCache.h"
+#include "Runtime/Mono/MonoScopedThreadAttach.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Profiler/TimeHelper.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/AssetPipeline/MonoCompile.h"
+#endif
+
+#include "Runtime/Scripting/Scripting.h"
+
+AudioCustomFilter::AudioCustomFilter(MonoBehaviour* behaviour) :
+m_behaviour(behaviour),
+#if ENABLE_MONO
+monoDomain(NULL),
+#endif
+m_DSP(NULL),
+m_InScriptCallback(false),
+m_playingSource(NULL)
+#if UNITY_EDITOR
+,
+processTime(0.0),
+channelCount(0)
+#endif
+{
+ Init();
+}
+
+AudioCustomFilter::~AudioCustomFilter()
+{
+ Cleanup();
+}
+
+void AudioCustomFilter::Init()
+{
+ // setup dsp/callbacks
+ if (!m_DSP)
+ {
+ FMOD_DSP_DESCRIPTION dspdesc;
+ FMOD_RESULT result;
+
+ memset(&dspdesc, 0, sizeof(FMOD_DSP_DESCRIPTION));
+
+#if UNITY_EDITOR
+ strcpy(dspdesc.name, m_behaviour->GetScriptClassName().c_str());
+#endif
+ dspdesc.channels = 0; // 0 = whatever comes in, else specify.
+ dspdesc.read = AudioCustomFilter::readCallback;
+ dspdesc.userdata = this;
+
+ result = GetAudioManager().GetFMODSystem()->createDSP(&dspdesc, &m_DSP);
+ FMOD_ASSERT(result);
+
+ m_DSP->setBypass(true);
+ }
+
+ #if ENABLE_MONO
+ monoDomain = mono_domain_get();
+ #endif
+}
+
+void AudioCustomFilter::Cleanup()
+{
+ if (m_DSP)
+ {
+ // is this a playing dsp?
+ if (m_playingSource)
+ {
+ // Doesn't matter whether argument is true or false, since sources playing DSP's don't play one-shots at the same time.
+ m_playingSource->Stop(true);
+ }
+
+ FMOD_RESULT result = m_DSP->release();
+ FMOD_ASSERT(result);
+ m_DSP = NULL;
+ }
+}
+
+FMOD::DSP* AudioCustomFilter::GetOrCreateDSP()
+{
+ if (!m_DSP)
+ Init();
+ return m_DSP;
+}
+
+FMOD::DSP* AudioCustomFilter::GetDSP()
+{
+ return m_DSP;
+}
+
+void AudioCustomFilter::WaitForScriptCallback()
+{
+ while (m_InScriptCallback)
+ Thread::Sleep(0.01); // wait 10ms
+
+}
+
+#if UNITY_EDITOR
+float AudioCustomFilter::GetMaxIn(short channel) const
+{
+ if (m_DSP)
+ {
+ bool active, bypass;
+ m_DSP->getActive(&active);
+ m_DSP->getBypass(&bypass);
+ return active&&!bypass?maxIn[channel]:0.0f;
+ }
+ else return 0.0f;
+}
+
+float AudioCustomFilter::GetMaxOut(short channel) const
+{
+ if (m_DSP)
+ {
+ bool active, bypass;
+ m_DSP->getActive(&active);
+ m_DSP->getBypass(&bypass);
+ return active&&!bypass?maxOut[channel]:0.0f;
+ }
+ else return 0.0f;
+}
+#endif // UNITY_EDITOR
+
+FMOD_RESULT F_CALLBACK AudioCustomFilter::readCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels)
+{
+#if SUPPORT_MONO_THREADS
+
+#if UNITY_EDITOR
+ if (IsCompiling())
+ return FMOD_OK;
+#endif
+
+ AudioCustomFilter* filter;
+ FMOD_RESULT result;
+ FMOD::DSP* fmod_dsp = (FMOD::DSP*)dsp_state->instance;
+ result = fmod_dsp->getUserData((void**)&filter);
+
+ Assert (filter);
+
+#if UNITY_EDITOR
+ ABSOLUTE_TIME start_time = START_TIME;
+#endif
+ if (!filter->m_behaviour->GetEnabled())
+ return FMOD_OK;
+
+ filter->m_InScriptCallback = true;
+ {
+ ScopedThreadAttach attach(filter->monoDomain);
+
+ // reuse mono array
+ MonoArray* array = NULL;
+ GetAudioManager().GetScriptBufferManager().GetDSPFilterArray(length * inchannels, array);
+
+ Assert(array);
+
+ memcpy( &GetMonoArrayElement<float>( array, 0 ), inbuffer, length * 4 * inchannels );
+
+ ScriptingObjectPtr instance = Scripting::ScriptingWrapperFor( filter->m_behaviour );
+ if (instance)
+ {
+ MonoMethod* method = filter->m_behaviour->GetMethod(MonoScriptCache::kAudioFilterRead)->monoMethod;
+ void* args[] = { array, &inchannels };
+ MonoException* exception;
+ mono_runtime_invoke( method, instance, args, &exception );
+ if (exception)
+ {
+ // handle
+ Scripting::LogException(exception, Scripting::GetInstanceIDFromScriptingWrapper(instance));
+ }
+ else
+ {
+ memcpy(outbuffer, &GetMonoArrayElement<float>( array, 0 ), length * 4 * inchannels);
+ }
+ }
+ }
+ filter->m_InScriptCallback = false;
+
+#if UNITY_EDITOR
+ filter->processTime = GetProfileTime(ELAPSED_TIME(start_time));
+ filter->channelCount = inchannels;
+ for (int c = 0; c < inchannels && c < MAX_CHANNELS; c++)
+ filter->maxOut[c] = filter->maxIn[c] = -1.0f;
+ for (int i = 0;i < length; i += inchannels)
+ for (int c = 0; c < inchannels && c < MAX_CHANNELS; c++)
+ {
+ if (filter->maxIn[c]<inbuffer[i+c]) filter->maxIn[c] = inbuffer[i+c];
+ if (filter->maxOut[c]<outbuffer[i+c]) filter->maxOut[c] = outbuffer[i+c];
+ }
+#endif
+#endif // SUPPORT_THREADS
+
+ return FMOD_OK;
+}
+#endif // ENABLE_AUDIO_FMOD
diff --git a/Runtime/Audio/AudioCustomFilter.h b/Runtime/Audio/AudioCustomFilter.h
new file mode 100644
index 0000000..650a74b
--- /dev/null
+++ b/Runtime/Audio/AudioCustomFilter.h
@@ -0,0 +1,68 @@
+/*
+ * AudioCustomFilter.h
+ * Xcode
+ *
+ * Created by Søren Christiansen on 9/12/10.
+ * Copyright 2010 Unity Technologies. All rights reserved.
+ *
+ */
+#ifndef __AUDIO_CUSTOM_FILTER_H__
+#define __AUDIO_CUSTOM_FILTER_H__
+
+#if ENABLE_AUDIO_FMOD
+
+#include "AudioSourceFilter.h"
+
+class MonoBehaviour;
+struct MonoThread;
+struct MonoDomain;
+class AudioSource;
+
+#define MAX_CHANNELS 8
+
+class AudioCustomFilter
+{
+public:
+ AudioCustomFilter(MonoBehaviour* behaviour);
+ virtual ~AudioCustomFilter();
+
+ FMOD::DSP* GetOrCreateDSP();
+ FMOD::DSP* GetDSP();
+public:
+ #if ENABLE_MONO
+ MonoDomain* monoDomain;
+ #endif
+
+ void SetPlayingSource(AudioSource* source) { m_playingSource = source; }
+ AudioSource* GetPlayingSource() const { return m_playingSource; }
+ void WaitForScriptCallback();
+ void Cleanup();
+private:
+ FMOD::DSP* m_DSP;
+
+ MonoBehaviour* m_behaviour;
+
+ bool m_InScriptCallback;
+ AudioSource* m_playingSource;
+private:
+ void Init();
+
+public:
+#if UNITY_EDITOR
+ float GetMaxIn(short channel) const;
+ float GetMaxOut(short channel) const;
+
+
+ int channelCount;
+ UInt64 processTime; // time in nanoseconds
+private:
+ float maxIn[MAX_CHANNELS];
+ float maxOut[MAX_CHANNELS];
+#endif
+
+private:
+ static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels);
+};
+
+#endif // ENABLE_AUDIO_FMOD
+#endif // __AUDIO_CUSTOM_FILTER_H__ \ No newline at end of file
diff --git a/Runtime/Audio/AudioDistortionFilter.cpp b/Runtime/Audio/AudioDistortionFilter.cpp
new file mode 100644
index 0000000..c59aa28
--- /dev/null
+++ b/Runtime/Audio/AudioDistortionFilter.cpp
@@ -0,0 +1,52 @@
+#include "UnityPrefix.h"
+#include "AudioDistortionFilter.h"
+#include "Runtime/Utilities/Utility.h"
+
+#if ENABLE_AUDIO_FMOD
+
+AudioDistortionFilter::AudioDistortionFilter (MemLabelId label, ObjectCreationMode mode) :
+Super(label, mode),
+m_DistortionLevel(0.5f)
+{
+ m_Type = FMOD_DSP_TYPE_DISTORTION;
+}
+
+void AudioDistortionFilter::Reset()
+{
+ Super::Reset();
+ m_DistortionLevel = 0.5f;
+}
+
+AudioDistortionFilter::~AudioDistortionFilter()
+{}
+
+void AudioDistortionFilter::AddToManager()
+{
+ Super::AddToManager();
+}
+
+void AudioDistortionFilter::CheckConsistency()
+{
+ Super::CheckConsistency();
+ m_DistortionLevel = clamp(m_DistortionLevel,0.0f,1.0f);
+}
+
+void AudioDistortionFilter::Update()
+{
+ if (m_DSP)
+ m_DSP->setParameter(FMOD_DSP_DISTORTION_LEVEL, m_DistortionLevel);
+}
+
+
+template<class TransferFunc>
+void AudioDistortionFilter::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER(m_DistortionLevel);
+}
+
+
+IMPLEMENT_CLASS (AudioDistortionFilter)
+IMPLEMENT_OBJECT_SERIALIZE (AudioDistortionFilter)
+
+#endif \ No newline at end of file
diff --git a/Runtime/Audio/AudioDistortionFilter.h b/Runtime/Audio/AudioDistortionFilter.h
new file mode 100644
index 0000000..1bc86ce
--- /dev/null
+++ b/Runtime/Audio/AudioDistortionFilter.h
@@ -0,0 +1,26 @@
+#ifndef __AUDIODISTORTION_FILTER_H__
+#define __AUDIODISTORTION_FILTER_H__
+
+#if ENABLE_AUDIO_FMOD
+#include "AudioSourceFilter.h"
+
+class AudioDistortionFilter : public AudioFilter {
+public:
+ REGISTER_DERIVED_CLASS (AudioDistortionFilter, AudioFilter)
+ DECLARE_OBJECT_SERIALIZE (AudioDistortionFilter)
+ AudioDistortionFilter (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void CheckConsistency ();
+ virtual void Update();
+ virtual void AddToManager();
+ virtual void Reset();
+
+ float GetDistortionLevel() const { return m_DistortionLevel; }
+ void SetDistortionLevel(const float distortionLevel) { m_DistortionLevel = distortionLevel; Update(); SetDirty(); }
+
+private:
+ float m_DistortionLevel; // Distortion value. 0.0 to 1.0. Default = 0.5.
+};
+
+#endif // ENABLE_AUDIO
+#endif // AUDIODISTORTION
diff --git a/Runtime/Audio/AudioEchoFilter.cpp b/Runtime/Audio/AudioEchoFilter.cpp
new file mode 100644
index 0000000..875ba7b
--- /dev/null
+++ b/Runtime/Audio/AudioEchoFilter.cpp
@@ -0,0 +1,72 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_AUDIO_FMOD
+
+#include "AudioEchoFilter.h"
+#include "Runtime/Utilities/Utility.h"
+
+
+AudioEchoFilter::AudioEchoFilter (MemLabelId label, ObjectCreationMode mode) :
+Super(label, mode),
+m_Delay(500),
+m_DecayRatio(0.5f),
+m_DryMix(1.0f),
+m_WetMix(1.0f)
+{
+ m_Type = FMOD_DSP_TYPE_ECHO;
+}
+
+AudioEchoFilter::~AudioEchoFilter()
+{}
+
+void AudioEchoFilter::AddToManager()
+{
+ Super::AddToManager();
+}
+
+void AudioEchoFilter::Reset()
+{
+ Super::Reset();
+
+ m_Delay = 500;
+ m_DecayRatio = 0.5f;
+ m_DryMix = 1.0f;
+ m_WetMix = 1.0f;
+}
+
+void AudioEchoFilter::CheckConsistency()
+{
+ Super::CheckConsistency();
+ m_Delay = clamp<int>(m_Delay,10,5000);
+ m_DecayRatio = clamp (m_DecayRatio,0.0f,1.0f);
+ m_DryMix = clamp(m_DryMix,0.0f,1.0f);
+ m_WetMix = clamp(m_WetMix,0.0f,1.0f);
+}
+
+void AudioEchoFilter::Update()
+{
+ if (m_DSP)
+ {
+ m_DSP->setParameter(FMOD_DSP_ECHO_DELAY, m_Delay);
+ m_DSP->setParameter(FMOD_DSP_ECHO_DECAYRATIO, m_DecayRatio);
+ m_DSP->setParameter(FMOD_DSP_ECHO_DRYMIX, m_DryMix);
+ m_DSP->setParameter(FMOD_DSP_ECHO_WETMIX, m_WetMix);
+ }
+}
+
+
+template<class TransferFunc>
+void AudioEchoFilter::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER(m_Delay);
+ TRANSFER(m_DecayRatio);
+ TRANSFER(m_WetMix);
+ TRANSFER(m_DryMix);
+}
+
+
+IMPLEMENT_CLASS (AudioEchoFilter)
+IMPLEMENT_OBJECT_SERIALIZE (AudioEchoFilter)
+
+#endif //ENABLE_AUDIO
diff --git a/Runtime/Audio/AudioEchoFilter.h b/Runtime/Audio/AudioEchoFilter.h
new file mode 100644
index 0000000..3bdc5f9
--- /dev/null
+++ b/Runtime/Audio/AudioEchoFilter.h
@@ -0,0 +1,41 @@
+#ifndef __AUDIOECHO_FILTER_H__
+#define __AUDIOECHO_FILTER_H__
+
+#if ENABLE_AUDIO_FMOD
+
+#include "AudioSourceFilter.h"
+
+class AudioEchoFilter : public AudioFilter
+{
+public:
+ REGISTER_DERIVED_CLASS (AudioEchoFilter, AudioFilter)
+ DECLARE_OBJECT_SERIALIZE (AudioEchoFilter)
+ AudioEchoFilter (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void CheckConsistency ();
+ virtual void AddToManager();
+ virtual void Reset();
+
+ void Update();
+
+ float GetDelay() const { return m_Delay; }
+ void SetDelay(const float delay) { m_Delay = (unsigned)delay; Update(); SetDirty(); }
+
+ float GetDecayRatio() const { return m_DecayRatio; }
+ void SetDecayRatio(const float decay) { m_DecayRatio = decay; Update(); SetDirty(); }
+
+ float GetDryMix() const { return m_DryMix; }
+ void SetDryMix(const float drymix) { m_DryMix = drymix; Update(); SetDirty(); }
+
+ float GetWetMix() const { return m_WetMix; }
+ void SetWetMix(const float wetmix) { m_WetMix = wetmix; Update(); SetDirty(); }
+
+private:
+ unsigned m_Delay; // Echo delay in ms. 10 to 5000. Default = 500.
+ float m_DecayRatio; // Echo decay per delay. 0 to 1. 1.0 = No decay, 0.0 = total decay (ie simple 1 line delay). Default = 0.5.
+ float m_DryMix; // Volume of original signal to pass to output. 0.0 to 1.0. Default = 1.0.
+ float m_WetMix; // Volume of echo signal to pass to output. 0.0 to 1.0. Default = 1.0.
+};
+
+#endif //ENABLE_AUDIO
+#endif // ___AUDIOECHO_FILTER_H__
diff --git a/Runtime/Audio/AudioHighPassFilter.cpp b/Runtime/Audio/AudioHighPassFilter.cpp
new file mode 100644
index 0000000..8e37717
--- /dev/null
+++ b/Runtime/Audio/AudioHighPassFilter.cpp
@@ -0,0 +1,61 @@
+#include "UnityPrefix.h"
+#if ENABLE_AUDIO_FMOD
+#include "AudioHighPassFilter.h"
+#include "Runtime/Utilities/Utility.h"
+
+
+AudioHighPassFilter::AudioHighPassFilter (MemLabelId label, ObjectCreationMode mode) :
+Super(label, mode),
+m_CutoffFrequency(5000.0f),
+m_HighpassResonanceQ(1.0f)
+{
+ m_Type = FMOD_DSP_TYPE_HIGHPASS;
+}
+
+AudioHighPassFilter::~AudioHighPassFilter()
+{}
+
+void AudioHighPassFilter::AddToManager()
+{
+ Super::AddToManager();
+}
+
+void AudioHighPassFilter::Reset()
+{
+ Super::Reset();
+
+ m_CutoffFrequency = 5000.0f;
+ m_HighpassResonanceQ = 1.0f;
+}
+
+void AudioHighPassFilter::CheckConsistency()
+{
+ Super::CheckConsistency();
+ // @TODO get output freq from audiomanager
+ m_CutoffFrequency = clamp(m_CutoffFrequency, 10.0f, 22000.0f);
+ m_HighpassResonanceQ = clamp(m_HighpassResonanceQ, 1.0f, 10.0f);
+}
+
+void AudioHighPassFilter::Update()
+{
+ if (m_DSP)
+ {
+ m_DSP->setParameter(FMOD_DSP_HIGHPASS_CUTOFF, m_CutoffFrequency);
+ m_DSP->setParameter(FMOD_DSP_HIGHPASS_RESONANCE, m_HighpassResonanceQ);
+ }
+}
+
+
+template<class TransferFunc>
+void AudioHighPassFilter::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER(m_CutoffFrequency);
+ TRANSFER(m_HighpassResonanceQ);
+}
+
+
+IMPLEMENT_CLASS (AudioHighPassFilter)
+IMPLEMENT_OBJECT_SERIALIZE (AudioHighPassFilter)
+
+#endif //ENABLE_AUDIO \ No newline at end of file
diff --git a/Runtime/Audio/AudioHighPassFilter.h b/Runtime/Audio/AudioHighPassFilter.h
new file mode 100644
index 0000000..8b228dd
--- /dev/null
+++ b/Runtime/Audio/AudioHighPassFilter.h
@@ -0,0 +1,33 @@
+#ifndef __AUDIOHIGHPASS_FILTER_H__
+#define __AUDIOHIGHPASS_FILTER_H__
+
+#include "AudioSourceFilter.h"
+#if ENABLE_AUDIO_FMOD
+
+class AudioHighPassFilter : public AudioFilter
+{
+public:
+ REGISTER_DERIVED_CLASS (AudioHighPassFilter, AudioFilter)
+ DECLARE_OBJECT_SERIALIZE (AudioHighPassFilter)
+
+ AudioHighPassFilter (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void CheckConsistency ();
+ virtual void AddToManager();
+ virtual void Reset();
+
+ void Update();
+
+ float GetCutoffFrequency() const { return m_CutoffFrequency; }
+ void SetCutoffFrequency(float value) { m_CutoffFrequency = value; Update(); SetDirty();}
+
+ float GetHighpassResonanceQ() const { return m_HighpassResonanceQ; }
+ void SetHighpassResonanceQ(float value) { m_HighpassResonanceQ = value; Update(); SetDirty();}
+
+private:
+ float m_CutoffFrequency;
+ float m_HighpassResonanceQ;
+};
+
+#endif //ENABLE_AUDIO_FMOD
+#endif // __AUDIOHIGHPASS_FILTER_H__
diff --git a/Runtime/Audio/AudioListener.cpp b/Runtime/Audio/AudioListener.cpp
new file mode 100644
index 0000000..5b083e5
--- /dev/null
+++ b/Runtime/Audio/AudioListener.cpp
@@ -0,0 +1,177 @@
+#include "UnityPrefix.h"
+#include "AudioManager.h"
+#include "AudioListener.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "AudioSourceFilter.h"
+#include "AudioEchoFilter.h"
+#include "AudioChorusFilter.h"
+#include "correct_fmod_includer.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+
+#if ENABLE_AUDIO
+
+AudioListener::AudioListener (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_Node(this)
+, m_VelocityUpdateMode (kVelocityUpdateModeAuto)
+, m_LastPosition(Vector3f(0,0,0))
+{
+}
+
+AudioListener::~AudioListener ()
+{
+ GetAudioManager().RemoveAudioListener(this);
+}
+
+void AudioListener::Cleanup()
+{
+#if ENABLE_AUDIO_FMOD
+ const GameObject* go = GetGameObjectPtr();
+ if (!go)
+ return;
+ for (int i=0;i<go->GetComponentCount();i++)
+ {
+ AudioFilter* filter = dynamic_pptr_cast<AudioFilter*> (&go->GetComponentAtIndex(i));
+ if (filter == NULL)
+ continue;
+
+ filter->Cleanup();
+ }
+#endif
+}
+
+void AudioListener::RemoveFromManager ()
+{
+ GetAudioManager().RemoveAudioListener(this);
+}
+
+void AudioListener::AddToManager ()
+{
+ m_LastPosition = GetCurrentTransform().GetPosition();
+ GetAudioManager().AddAudioListener(this);
+#if ENABLE_AUDIO_FMOD
+ ApplyFilters();
+#endif
+}
+
+void AudioListener::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+}
+
+void AudioListener::SetAlternativeTransform(Transform* t)
+{
+ m_AltTransform = t;
+}
+
+const Transform& AudioListener::GetCurrentTransform() const
+{
+#if UNITY_EDITOR
+ if (!IsWorldPlaying())
+ {
+ Transform* altTransform = m_AltTransform;
+ if (altTransform)
+ return *altTransform;
+ }
+#endif
+ return GetComponent(Transform);
+}
+
+void AudioListener::DoUpdate ()
+{
+ const Transform& transform = GetCurrentTransform();
+ const Vector3f pos = transform.GetPosition();
+
+ Vector3f vel = (pos - m_LastPosition) * GetInvDeltaTime ();
+ GetAudioManager().UpdateListener(
+ pos,
+ vel,
+ NormalizeSafe(transform.TransformDirection( Vector3f (0.0f, 1.0f, 0.0f) )),
+ NormalizeSafe(transform.TransformDirection( Vector3f (0.0f, 0.0f, 1.0f) ))
+ );
+ m_LastPosition = pos;
+}
+
+void AudioListener::Update()
+{
+ if(m_VelocityUpdateMode == kVelocityUpdateModeAuto)
+ m_VelocityUpdateMode = GetAudioManager().GetAutomaticUpdateMode( GetGameObjectPtr() );
+
+ if(m_VelocityUpdateMode==kVelocityUpdateModeDynamic)
+ DoUpdate();
+}
+
+void AudioListener::FixedUpdate()
+{
+ if(m_VelocityUpdateMode == kVelocityUpdateModeAuto)
+ m_VelocityUpdateMode = GetAudioManager().GetAutomaticUpdateMode( GetGameObjectPtr());
+
+ if(m_VelocityUpdateMode==kVelocityUpdateModeFixed)
+ DoUpdate();
+}
+
+// Apply filters
+#if ENABLE_AUDIO_FMOD
+void AudioListener::ApplyFilters()
+{
+ const GameObject& go = GetGameObject();
+ for (int i=0;i<go.GetComponentCount();i++)
+ {
+ FMOD::DSP* dsp = NULL;
+
+ AudioFilter* filter = NULL;
+ filter = dynamic_pptr_cast<AudioFilter*> (&go.GetComponentAtIndex(i));
+ if ( filter && GetBuildSettings().hasAdvancedVersion )
+ dsp = filter->GetDSP();
+
+ #if ENABLE_SCRIPTING
+ if (!dsp)
+ {
+ MonoBehaviour* behaviour = dynamic_pptr_cast<MonoBehaviour*> (&go.GetComponentAtIndex(i));
+ if ( behaviour ) dsp = behaviour->GetOrCreateDSP();
+ }
+ #endif
+
+ if (dsp == NULL)
+ continue;
+
+ FMOD_RESULT result;
+ result = dsp->remove();
+ FMOD_ASSERT(result);
+ result = GetAudioManager().GetChannelGroup_FX_IgnoreVolume()->addDSP(dsp, 0);
+ FMOD_ASSERT(result);
+ }
+}
+#endif
+
+void AudioListener::OnAddComponent()
+{
+#if ENABLE_AUDIO_FMOD
+ ApplyFilters();
+#endif
+}
+
+void AudioListener::InitializeClass ()
+{
+ REGISTER_MESSAGE_VOID (AudioListener, kDidAddComponent, OnAddComponent);
+}
+
+void AudioListener::CleanupClass()
+{
+}
+
+template<class TransferFunc>
+void AudioListener::Transfer (TransferFunc& transfer) {
+ Super::Transfer (transfer);
+}
+
+IMPLEMENT_CLASS_HAS_INIT (AudioListener)
+IMPLEMENT_OBJECT_SERIALIZE (AudioListener)
+
+#endif
diff --git a/Runtime/Audio/AudioListener.h b/Runtime/Audio/AudioListener.h
new file mode 100644
index 0000000..187ddb4
--- /dev/null
+++ b/Runtime/Audio/AudioListener.h
@@ -0,0 +1,62 @@
+#ifndef __AUDIOLISTENER_H__
+#define __AUDIOLISTENER_H__
+
+#if ENABLE_AUDIO
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Math/Vector3.h"
+#include "AudioBehaviour.h"
+
+class Transform;
+
+class AudioListener : public AudioBehaviour
+{
+public:
+
+ REGISTER_DERIVED_CLASS (AudioListener, AudioBehaviour)
+ DECLARE_OBJECT_SERIALIZE (AudioListener)
+
+ AudioListener (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~AudioListener (); declared-by-macro
+
+ void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+ int GetVelocityUpdateMode() const { return m_VelocityUpdateMode; }
+ void SetVelocityUpdateMode(int update) { m_VelocityUpdateMode=update; }
+
+ // Behaviour
+ virtual void Update();
+ virtual void FixedUpdate();
+
+ ListNode<AudioListener>& GetNode() { return m_Node; }
+
+ const Vector3f& GetPosition() const { return m_LastPosition; }
+
+ void OnAddComponent();
+
+ static void InitializeClass ();
+ static void CleanupClass();
+
+ void Cleanup();
+
+ void SetAlternativeTransform(Transform* t);
+
+private:
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+ void DoUpdate ();
+ void ApplyFilters();
+
+private:
+ Vector3f m_LastPosition;
+ int m_VelocityUpdateMode;
+
+ PPtr<Transform> m_AltTransform;
+ const Transform& GetCurrentTransform() const;
+
+ ListNode<AudioListener> m_Node;
+
+ friend class AudioManager;
+};
+
+#endif //ENABLE_AUDIO
+#endif // __AUDIOLISTENER_H__
diff --git a/Runtime/Audio/AudioLowPassFilter.cpp b/Runtime/Audio/AudioLowPassFilter.cpp
new file mode 100644
index 0000000..091405f
--- /dev/null
+++ b/Runtime/Audio/AudioLowPassFilter.cpp
@@ -0,0 +1,111 @@
+#include "UnityPrefix.h"
+#include "AudioLowPassFilter.h"
+#include "Runtime/Utilities/Utility.h"
+
+#if ENABLE_AUDIO_FMOD
+
+AudioLowPassFilter::AudioLowPassFilter (MemLabelId label, ObjectCreationMode mode) :
+Super(label, mode),
+m_CutoffFrequency(5000.0f),
+m_LowpassResonanceQ(1.0f),
+m_NeedToNormalizeCurve(false)
+{
+ m_Type = FMOD_DSP_TYPE_LOWPASS;
+}
+
+AudioLowPassFilter::~AudioLowPassFilter()
+{}
+
+void AudioLowPassFilter::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ if (m_NeedToNormalizeCurve)
+ {
+ AudioSource* source = QueryComponent(AudioSource);
+ if (source)
+ ScaleCurveTime(m_LowpassLevelCustomCurve, 1 / source->GetMaxDistance());
+ }
+}
+
+void AudioLowPassFilter::Reset()
+{
+ Super::Reset();
+
+ m_CutoffFrequency = 5000.0f;
+ m_LowpassResonanceQ = 1.0f;
+ m_NeedToNormalizeCurve = false;
+
+ // Curve initial values will be handled in CheckConsistency
+ m_LowpassLevelCustomCurve.ResizeUninitialized (0);
+ CheckConsistency ();
+}
+
+void AudioLowPassFilter::AddToManager()
+{
+ Super::AddToManager();
+}
+
+void AudioLowPassFilter::CheckConsistency()
+{
+ Super::CheckConsistency();
+ // @TODO get output freq from audiomanager
+ m_CutoffFrequency = clamp(m_CutoffFrequency, 10.0f, 22000.0f);
+ m_LowpassResonanceQ = clamp(m_LowpassResonanceQ, 1.0f, 10.0f);
+
+ if (m_LowpassLevelCustomCurve.GetKeyCount() < 1)
+ m_LowpassLevelCustomCurve.AddKey(AnimationCurve::Keyframe(0.0f, 1 - m_CutoffFrequency / 22000.0f));
+}
+
+void AudioLowPassFilter::Update()
+{
+ if (m_DSP)
+ {
+ m_DSP->setParameter(FMOD_DSP_LOWPASS_CUTOFF, m_CutoffFrequency);
+ m_DSP->setParameter(FMOD_DSP_LOWPASS_RESONANCE, m_LowpassResonanceQ);
+ }
+}
+/// Set/Get spread curve
+AnimationCurve& AudioLowPassFilter::GetCustomLowpassLevelCurve ()
+{
+ return m_LowpassLevelCustomCurve;
+}
+
+
+const AnimationCurve& AudioLowPassFilter::GetCustomLowpassLevelCurve () const
+{
+ return m_LowpassLevelCustomCurve;
+}
+
+void AudioLowPassFilter::SetCustomLowpassLevelCurve(const AnimationCurve& curve)
+{
+ m_LowpassLevelCustomCurve = curve;
+ SetDirty();
+}
+
+
+template<class TransferFunc>
+void AudioLowPassFilter::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+
+ // 3.5 3: Normalized curve values
+ // <3.5 2: Non-Normalized curves
+ transfer.SetVersion (3);
+
+ TRANSFER(m_CutoffFrequency);
+ TRANSFER(m_LowpassResonanceQ);
+
+ transfer.Transfer(m_LowpassLevelCustomCurve, "lowpassLevelCustomCurve");
+
+ if (transfer.IsVersionSmallerOrEqual(2))
+ {
+ m_NeedToNormalizeCurve = true;
+ }
+}
+
+
+IMPLEMENT_CLASS (AudioLowPassFilter)
+IMPLEMENT_OBJECT_SERIALIZE (AudioLowPassFilter)
+
+#endif //ENABLE_AUDIO \ No newline at end of file
diff --git a/Runtime/Audio/AudioLowPassFilter.h b/Runtime/Audio/AudioLowPassFilter.h
new file mode 100644
index 0000000..16e2d05
--- /dev/null
+++ b/Runtime/Audio/AudioLowPassFilter.h
@@ -0,0 +1,41 @@
+#ifndef __AUDIOLOWPASS_FILTER_H__
+#define __AUDIOLOWPASS_FILTER_H__
+#include "AudioSourceFilter.h"
+#include "Runtime/Audio/AudioSource.h"
+#include "Runtime/Animation/AnimationCurveUtility.h"
+#include "Runtime/Math/AnimationCurve.h"
+
+#if ENABLE_AUDIO_FMOD
+
+class AudioLowPassFilter : public AudioFilter {
+public:
+ REGISTER_DERIVED_CLASS (AudioLowPassFilter, AudioFilter)
+ DECLARE_OBJECT_SERIALIZE (AudioLowPassFilter)
+ AudioLowPassFilter (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ virtual void CheckConsistency ();
+ virtual void Update();
+ virtual void AddToManager();
+
+ float GetCutoffFrequency() const { return m_CutoffFrequency; }
+ void SetCutoffFrequency(float value) { m_CutoffFrequency = value; Update(); SetDirty();}
+ float GetLowpassResonanceQ() const { return m_LowpassResonanceQ; }
+ void SetLowpassResonanceQ(float value) { m_LowpassResonanceQ = value; Update(); SetDirty();}
+
+ AnimationCurve& GetCustomLowpassLevelCurve ();
+ const AnimationCurve& GetCustomLowpassLevelCurve () const;
+ void SetCustomLowpassLevelCurve(const AnimationCurve& curve);
+
+ virtual void Reset();
+private:
+ AnimationCurve m_LowpassLevelCustomCurve;
+ float m_CutoffFrequency;
+ float m_LowpassResonanceQ;
+ bool m_NeedToNormalizeCurve;
+};
+
+
+#endif // ENABLE_AUDIO
+#endif // __AUDIOLOWPASS_FILTER_H__
diff --git a/Runtime/Audio/AudioManager.Callbacks.cpp b/Runtime/Audio/AudioManager.Callbacks.cpp
new file mode 100644
index 0000000..6db2b9c
--- /dev/null
+++ b/Runtime/Audio/AudioManager.Callbacks.cpp
@@ -0,0 +1,37 @@
+#include "UnityPrefix.h"
+#if ENABLE_AUDIO_FMOD
+#include "AudioManager.h"
+#include "Runtime/Audio/correct_fmod_includer.h"
+
+FMOD_RESULT F_CALLBACK AudioManager::systemCallback(FMOD_SYSTEM* c_system, FMOD_SYSTEM_CALLBACKTYPE type, void* data1, void* data2)
+{
+ FMOD::System* system = (FMOD::System*)c_system;
+ FMOD_RESULT result = FMOD_OK;
+
+ switch (type)
+ {
+ case FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED:
+ // Get available sound cards
+ // If no device is found fall back on the NOSOUND driver
+ // @TODO Enable user to choose driver
+ int numDrivers;
+ result = system->getNumDrivers(&numDrivers);
+
+ if ((result == FMOD_OK) && (numDrivers != 0))
+ {
+ // set driver to the new default driver
+ // and autodetect output
+ result = system->setDriver(0);
+ if (result != FMOD_OK)
+ {
+ ErrorString(Format("Default audio device was changed, but the audio system failed to initialize it (%s). This may be because the audio device that Unity was started on and the device switched to have different sampling rates or speaker configurations. To get sound back you can either adjust the sampling rate on the new device (on Windows using the control panel, on Mac via the Audio MIDI Setup application), switch back to the old device or restart Unity.",FMOD_ErrorString(result)));
+ return result;
+ }
+ }
+ break;
+ default: break;
+ }
+
+ return result;
+}
+#endif //ENABLE_AUDIO \ No newline at end of file
diff --git a/Runtime/Audio/AudioManager.cpp b/Runtime/Audio/AudioManager.cpp
new file mode 100644
index 0000000..0f89245
--- /dev/null
+++ b/Runtime/Audio/AudioManager.cpp
@@ -0,0 +1,1773 @@
+#include "UnityPrefix.h"
+#include "Runtime/Misc/ReproductionLog.h"
+#if ENABLE_AUDIO
+#include "AudioManager.h"
+#include "AudioListener.h"
+#include "AudioSource.h"
+#include "AudioReverbZone.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Dynamics/RigidBody.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Audio/correct_fmod_includer.h"
+#include "Runtime/Video/MoviePlayback.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "WavReader.h"
+#include "Runtime/Profiler/ProfilerStats.h"
+#include "Runtime/Utilities/UserAuthorizationManager.h"
+#include "Runtime/Misc/UTF8.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Utilities/Argv.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+
+#if UNITY_IPHONE
+#include "External/Audio/FMOD/builds/iphone/include/fmodiphone.h"
+#include <AudioToolbox/AudioFile.h>
+#include <AudioToolbox/AudioQueue.h>
+#include <AudioToolbox/AudioServices.h>
+#include "Runtime/Misc/PlayerSettings.h"
+#endif
+
+#if UNITY_EDITOR
+#include "Runtime/BaseClasses/IsPlaying.h"
+#if UNITY_WIN
+#include "PlatformDependent/Win/WinUnicode.h"
+#endif
+#endif
+
+#if UNITY_PS3
+#include "External/Audio/FMOD/builds/ps3/include/fmodps3.h"
+struct CellSpurs2;
+extern CellSpurs2* g_pSpursInstance;
+extern uint8_t g_aFmodPriorities[8];
+#endif
+
+#if UNITY_ANDROID
+#include "PlatformDependent/AndroidPlayer/FMOD_FileIO.h"
+#endif
+
+#if UNITY_XENON
+#include "fmodxbox360.h"
+#include "PlatformDependent/Xbox360/Source/Services/Audio.h"
+#endif
+
+#if UNITY_METRO
+#include "PlatformDependent/MetroPlayer/AppCallbacks.h"
+#include "PlatformDependent/MetroPlayer/MetroCapabilities.h"
+#endif
+
+#if UNITY_WP8
+#include "PlatformDependent/WP8Player/WP8Capabilities.h"
+#endif
+
+void* F_CALLBACK FMODMemoryAlloc (unsigned int size, FMOD_MEMORY_TYPE type, const char *sourcestr)
+{
+#if UNITY_XENON
+ if(type & FMOD_MEMORY_XBOX360_PHYSICAL)
+ {
+ return XPhysicalAlloc(size, MAXULONG_PTR, 0, PAGE_READWRITE);
+ }
+ else
+#endif
+ {
+ SET_ALLOC_OWNER(GetAudioManagerPtr());
+ return UNITY_MALLOC_ALIGNED(kMemFMOD, size, 16);
+ }
+}
+
+void F_CALLBACK FMODMemoryFree(void *ptr, FMOD_MEMORY_TYPE type, const char *sourcestr);
+
+void* F_CALLBACK FMODMemoryRealloc (void *ptr, unsigned int size, FMOD_MEMORY_TYPE type, const char *sourcestr)
+{
+#if UNITY_XENON
+ if (type & FMOD_MEMORY_XBOX360_PHYSICAL)
+ {
+ char *newdata = (char *)FMODMemoryAlloc(size, type, sourcestr);
+ if (newdata && ptr)
+ {
+ int copylen;
+ int curlen = XPhysicalSize(ptr);
+ copylen = (size > curlen) ? curlen : size;
+ memcpy(newdata, ptr, copylen);
+ }
+ if (ptr)
+ {
+ FMODMemoryFree(ptr, type, sourcestr);
+ }
+ return newdata;
+ }
+ else
+#endif
+ {
+ return UNITY_REALLOC_ALIGNED(kMemFMOD, ptr, size, 16);
+ }
+}
+
+void F_CALLBACK FMODMemoryFree (void *ptr, FMOD_MEMORY_TYPE type, const char *sourcestr)
+{
+#if UNITY_XENON
+ if (type & FMOD_MEMORY_XBOX360_PHYSICAL)
+ {
+ XPhysicalFree(ptr);
+ }
+ else
+#endif
+ {
+ UNITY_FREE (kMemFMOD, ptr);
+ }
+}
+
+
+using namespace std;
+
+extern double GetTimeSinceStartup();
+
+// ---------------------------------------------------------------------------
+
+static void _InitScriptBufferManager()
+{
+#if ENABLE_AUDIO_FMOD
+ if (GetAudioManagerPtr())
+ GetAudioManager().InitScriptBufferManager();
+#endif
+}
+
+void AudioManager::InitializeClass ()
+{
+#if UNITY_EDITOR
+ RegisterAllowNameConversion (AudioManager::GetClassStringStatic(), "iOS DSP Buffer Size", "m_DSPBufferSize");
+#endif
+
+
+ //@TODO: Refactor this. Its ugly...
+ GlobalCallbacks::Get().managersWillBeReloadedHack.Register(_InitScriptBufferManager);
+}
+
+void AudioManager::CleanupClass ()
+{
+
+}
+
+AudioManager::AudioManager (MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode)
+ ,m_DefaultVolume(1.0f)
+ ,m_Volume(1.0f)
+ ,m_IsPaused(false)
+ ,m_Rolloffscale(1.0f)
+ ,m_DisableAudio(false)
+#if ENABLE_AUDIO_FMOD
+ ,m_SpeedOfSound(347.0f)
+ ,m_DopplerFactor(1.0f)
+ ,m_speakerMode(FMOD_SPEAKERMODE_STEREO)
+ ,m_activeSpeakerMode(FMOD_SPEAKERMODE_STEREO)
+ ,m_speakerModeCaps(FMOD_SPEAKERMODE_STEREO)
+ ,m_driverCaps(0)
+ ,m_FMODSystem(NULL)
+ ,m_ChannelGroup_FMODMaster(NULL)
+ ,m_ChannelGroup_FX_IgnoreVolume(NULL)
+ ,m_ChannelGroup_FX_UseVolume(NULL)
+ ,m_ChannelGroup_NoFX_IgnoreVolume(NULL)
+ ,m_ChannelGroup_NoFX_UseVolume(NULL)
+ ,m_DSPBufferSize(0)
+ ,m_ScriptBufferManager(NULL)
+ ,m_accPausedTicks(0)
+ ,m_pauseStartTicks(0)
+#endif
+{}
+
+AudioManager::~AudioManager ()
+{
+#if ENABLE_AUDIO_FMOD
+ CloseFMOD();
+ m_FMODSystem->release();
+#endif
+}
+
+#if ENABLE_AUDIO_FMOD
+// ----------------------------------------------------------------------
+// FMOD
+
+bool AudioManager::ValidateFMODResult(FMOD_RESULT result, const char* errmsg)
+{
+ if (result != FMOD_OK)
+ {
+ m_LastErrorString = FMOD_ErrorString(result);
+ m_LastFMODErrorResult = result;
+ ErrorString(std::string(errmsg) + m_LastErrorString);
+ return false;
+ }
+ return true;
+}
+
+
+#if UNITY_METRO
+namespace FMOD {
+
+extern Windows::UI::Core::CoreDispatcher^ (*GetAppcallbackCoreDispatcher)(); // Defined in fmod_output_wasapi.cpp
+
+} // namespace FMOD
+#endif // UNITY_METRO
+
+bool AudioManager::InitFMOD()
+{
+ FMOD_RESULT result;
+
+ string fmoddebuglevel = GetFirstValueForARGV("fmoddebuglevel");
+ if(!fmoddebuglevel.empty())
+ {
+ int level = atoi(fmoddebuglevel.c_str());
+ FMOD::Debug_SetLevel(level);
+ }
+ else
+ {
+#if DEBUGMODE && !UNITY_WINRT
+ // FMOD in verbose mode .. remember to link w. L versions of FMOD
+ FMOD::Debug_SetLevel( FMOD_DEBUG_ALL );
+#else
+ FMOD::Debug_SetLevel(FMOD_DEBUG_LEVEL_NONE);
+#endif
+ }
+
+ if (!m_FMODSystem) // not loaded yet
+ {
+ #if UNITY_METRO
+ // This serves as a getter for the dispatcher from AppCallbacks.cpp
+ FMOD::GetAppcallbackCoreDispatcher = &UnityPlayer::AppCallbacks::GetCoreDispatcher;
+ #endif
+
+ FMOD::Memory_Initialize(NULL, 0, FMODMemoryAlloc, FMODMemoryRealloc, FMODMemoryFree);
+ result = FMOD::System_Create(&m_FMODSystem); // Create the main system object.
+ if(!ValidateFMODResult(result, "FMOD failed to initialize ... ")) return false;
+ #if UNITY_ANDROID
+ m_FMODSystem->setFileSystem(FMOD_FileOpen, FMOD_FileClose, FMOD_FileRead, FMOD_FileSeek, 0,0,-1);
+ #elif UNITY_WII
+ # if WIIWARE
+ m_FMODSystem->setFileSystem(wii::FMOD_FileOpen, wii::FMOD_FileClose, wii::FMOD_FileRead, wii::FMOD_FileSeek, NULL, NULL, -1);
+ # endif
+ #endif
+ }
+
+#if DEBUGMODE
+ if (m_FMODSystem)
+ {
+ FMOD_ADVANCEDSETTINGS settings;
+ memset(&settings, 0, sizeof(settings));
+ settings.profileport = 9264;
+ }
+#endif
+
+#if SUPPORT_REPRODUCE_LOG
+ if (RunningReproduction())
+ {
+ if (!InitReproduction())
+ return false;
+ }
+ else
+ {
+ if (!InitNormal())
+ return false;
+ }
+#else
+ if (!InitNormal())
+ return false;
+#endif // SUPPORT_REPRODUCE_LOG
+
+
+ // 64k for streaming buffer sizes - we're streaming from memory so this should be sufficient
+ // when we're streaming from a www/movie class, buffer sizes are set independently
+ result = m_FMODSystem->setStreamBufferSize(64000, FMOD_TIMEUNIT_RAWBYTES);
+ if(!ValidateFMODResult(result, "FMOD failed to initialize ... ")) return false;
+
+ // Setup system callbacks
+ result = m_FMODSystem->setCallback(AudioManager::systemCallback);
+ if(!ValidateFMODResult(result, "FMOD failed to setup system callbacks ... ")) return false;
+
+ #if UNITY_EDITOR
+ m_EditorChannel = NULL;
+ #endif
+
+ // Setup channel callbacks
+ result = m_FMODSystem->set3DRolloffCallback(AudioSource::rolloffCallback);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel callbacks ... ")) return false;
+
+ // setup channel groups
+
+ Assert(m_ChannelGroup_FMODMaster == NULL);
+ result = m_FMODSystem->getMasterChannelGroup(&m_ChannelGroup_FMODMaster);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel groups ... ")) return false;
+
+ Assert(m_ChannelGroup_FX_IgnoreVolume == NULL);
+ result = m_FMODSystem->createChannelGroup("FX_IgnoreVol", &m_ChannelGroup_FX_IgnoreVolume);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel groups ... ")) return false;
+
+ Assert(m_ChannelGroup_FX_UseVolume == NULL);
+ result = m_FMODSystem->createChannelGroup("FX_UseVol", &m_ChannelGroup_FX_UseVolume);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel groups ... ")) return false;
+
+ Assert(m_ChannelGroup_NoFX_IgnoreVolume == NULL);
+ result = m_FMODSystem->createChannelGroup("NoFX_IgnoreVol", &m_ChannelGroup_NoFX_IgnoreVolume);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel groups ... ")) return false;
+
+ Assert(m_ChannelGroup_NoFX_UseVolume == NULL);
+ result = m_FMODSystem->createChannelGroup("NoFX_UseVol", &m_ChannelGroup_NoFX_UseVolume);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel groups ... ")) return false;
+
+ result = m_ChannelGroup_FMODMaster->addGroup(m_ChannelGroup_FX_IgnoreVolume);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel groups ... ")) return false;
+
+ result = m_ChannelGroup_FX_IgnoreVolume->addGroup(m_ChannelGroup_FX_UseVolume);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel groups ... ")) return false;
+
+ result = m_ChannelGroup_FMODMaster->addGroup(m_ChannelGroup_NoFX_IgnoreVolume);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel groups ... ")) return false;
+
+ result = m_ChannelGroup_NoFX_IgnoreVolume->addGroup(m_ChannelGroup_NoFX_UseVolume);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel groups ... ")) return false;
+
+ m_activeSpeakerMode = m_speakerMode;
+
+ return true;
+}
+void AudioManager::InitScriptBufferManager()
+{
+ if (m_ScriptBufferManager == 0)
+ {
+ unsigned DSPBufferSize;
+ int maxOutputChannels;
+ int maxInputChannels;
+ m_FMODSystem->getDSPBufferSize(&DSPBufferSize, NULL);
+ m_FMODSystem->getSoftwareFormat(NULL,
+ NULL,
+ &maxOutputChannels,
+ &maxInputChannels,
+ NULL,
+ NULL);
+ m_ScriptBufferManager = new AudioScriptBufferManager(16384 / 4, DSPBufferSize * std::max(maxOutputChannels, maxInputChannels));
+ }
+}
+void AudioManager::ReloadFMODSounds()
+{
+ CloseFMOD();
+ InitFMOD();
+ InitScriptBufferManager();
+
+ // reload any loaded audio clips
+ std::vector<AudioClip*> audioClips;
+ Object::FindObjectsOfType(&audioClips);
+ for(std::vector<AudioClip*>::iterator it = audioClips.begin(); it != audioClips.end(); ++it)
+ (*it)->Reload();
+
+ #if ENABLE_SCRIPTING
+ // Recreate Filters on Monobehaviours
+ std::vector<MonoBehaviour*> monoBehaviours;
+ Object::FindObjectsOfType(&monoBehaviours);
+ for(std::vector<MonoBehaviour*>::iterator it = monoBehaviours.begin(); it != monoBehaviours.end(); ++it)
+ {
+ MonoBehaviour* behaviour = *it;
+ FMOD::DSP* dsp = behaviour->GetOrCreateDSP();
+ if (dsp)
+ dsp->setBypass(!behaviour->GetEnabled());
+ }
+ #endif
+
+ // Awake sources
+ std::vector<AudioSource*> audioSources;
+ Object::FindObjectsOfType(&audioSources);
+ for(std::vector<AudioSource*>::iterator it = audioSources.begin(); it != audioSources.end(); ++it)
+ (*it)->AwakeFromLoad(kDefaultAwakeFromLoad);
+
+ // reload listener filters (if any)
+ TAudioListenersIterator i = m_Listeners.begin();
+ for (;i!=m_Listeners.end();++i)
+ {
+ AudioListener& curListener = **i;
+ curListener.ApplyFilters();
+ }
+
+ // reload reverb zones (if any)
+ TAudioReverbZonesIterator j = m_ReverbZones.begin();
+ for(;j!=m_ReverbZones.end();++j)
+ {
+ AudioReverbZone& curReverbZone = **j;
+ curReverbZone.Init();
+ }
+}
+
+
+void AudioManager::SetSpeakerMode(FMOD_SPEAKERMODE speakerMode)
+{
+ m_speakerMode = speakerMode;
+ if (m_activeSpeakerMode != m_speakerMode)
+ ReloadFMODSounds();
+}
+#endif //ENABLE_AUDIO_FMOD
+
+double AudioManager::GetDSPTime() const
+{
+#if ENABLE_AUDIO_FMOD
+ int sampleRate;
+ m_FMODSystem->getSoftwareFormat(&sampleRate, NULL, NULL, NULL, NULL, NULL);
+ if(m_IsPaused)
+ return (double)(m_pauseStartTicks - m_accPausedTicks) / (double)sampleRate;
+ unsigned clockLo, clockHi;
+ m_FMODSystem->getDSPClock(&clockHi, &clockLo);
+ return (double)(((UInt64)clockHi << 32) + clockLo - m_accPausedTicks) / (double)sampleRate;
+#else
+ return GetTimeSinceStartup();
+#endif
+}
+
+#if SUPPORT_REPRODUCE_LOG && ENABLE_AUDIO_FMOD
+bool AudioManager::InitReproduction( )
+{
+ // set a non-realtime wav writer up as output
+ FMOD_RESULT result = m_FMODSystem->setOutput(FMOD_OUTPUTTYPE_WAVWRITER_NRT);
+ if(!ValidateFMODResult(result, "[Reproduction]FMOD failed to initialize WAV writer output... ")) return false;
+
+ // set lowest possible mixing quality (stereo,11025hz, 8bit)
+ result = m_FMODSystem->setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
+ if(!ValidateFMODResult(result, "[Reproduction]FMOD failed to set speaker mode... ")) return false;
+
+ result = m_FMODSystem->setSoftwareFormat(22050, FMOD_SOUND_FORMAT_PCM8, 0, 6, FMOD_DSP_RESAMPLER_LINEAR);
+ if(!ValidateFMODResult(result, "[Reproduction]FMOD failed to set software format... ")) return false;
+
+ // init (with filename)
+ string reproductionPathTemp = AppendPathName( GetReproductionDirectory(), "/Audio/" );
+ string out = AppendPathName(reproductionPathTemp, "audio_output.wav");
+ result = m_FMODSystem->init(100, FMOD_INIT_STREAM_FROM_UPDATE | FMOD_INIT_SYNCMIXERWITHUPDATE, (void*)out.c_str()); // Initialize FMOD.
+ if(!ValidateFMODResult(result, "[Reproduction]FMOD failed to initialize ... ")) return false;
+
+ return true;
+}
+#endif //SUPPORT_REPRODUCE_LOG && ENABLE_AUDIO_FMODD
+
+#if UNITY_LINUX
+static void LogDriverDetails (FMOD::System *system, int driverID)
+{
+ char driverName[BUFSIZ];
+ FMOD_GUID guid;
+ FMOD_OUTPUTTYPE output;
+ char *outputName;
+
+ system->getOutput (&output);
+ system->getDriverInfo (driverID, driverName, BUFSIZ, &guid);
+
+ switch (output) {
+ case FMOD_OUTPUTTYPE_PULSEAUDIO:
+ outputName = "PulseAudio";
+ break;
+ case FMOD_OUTPUTTYPE_ALSA:
+ outputName = "ALSA";
+ break;
+ case FMOD_OUTPUTTYPE_OSS:
+ outputName = "OSS";
+ break;
+ case FMOD_OUTPUTTYPE_ESD:
+ outputName = "ESD";
+ break;
+ default:
+ outputName = "Unknown";
+ break;
+ }
+ printf_console ("AudioManager: Using %s: %s\n", outputName, driverName);
+}
+#endif
+
+
+#if ENABLE_AUDIO_FMOD
+bool AudioManager::InitNormal( )
+{
+ // Get available sound cards
+ // If no device is found fall back on the NOSOUND driver
+ // @TODO Enable user to choose driver
+ int numDrivers;
+ FMOD_RESULT result = m_FMODSystem->getNumDrivers(&numDrivers);
+ if(!ValidateFMODResult(result, "FMOD failed to get number of drivers ... ")) return false;
+
+ if (numDrivers == 0
+#if !UNITY_EDITOR
+ || m_DisableAudio
+#endif
+ ) // no suitable audio devices/drivers
+ {
+ result = m_FMODSystem->setOutput(FMOD_OUTPUTTYPE_NOSOUND);
+ if(!ValidateFMODResult(result, "FMOD failed to initialize nosound device ... ")) return false;
+ }
+
+ // get driver id
+ int driverID;
+ m_FMODSystem->getDriver(&driverID);
+
+ // setup speakermode
+ // check if current hw is capable of the current speakerMode
+ result = m_FMODSystem->getDriverCaps(driverID,&m_driverCaps,0,&m_speakerModeCaps);
+ if(!ValidateFMODResult(result, "FMOD failed to get driver capabilities ... ")) return false;
+
+ m_activeSpeakerMode = m_speakerMode;
+
+ if (m_speakerModeCaps < m_activeSpeakerMode)
+ {
+ if (m_activeSpeakerMode != FMOD_SPEAKERMODE_SRS5_1_MATRIX)
+ // hardware is not capable of the current speakerMode
+ m_activeSpeakerMode = m_speakerModeCaps;
+ }
+
+#if UNITY_EDITOR
+
+ int samplerate;
+ FMOD_SOUND_FORMAT format;
+ FMOD_DSP_RESAMPLER resampler;
+
+ result = m_FMODSystem->getSoftwareFormat(&samplerate, &format, NULL, NULL, &resampler, NULL);
+ if(!ValidateFMODResult(result, "FMOD failed to get driver capabilities ... ")) return false;
+
+ result = m_FMODSystem->setSoftwareFormat(samplerate, format, 0, 8, resampler);
+ if(!ValidateFMODResult(result, "FMOD failed to get driver capabilities ... ")) return false;
+
+#endif
+
+ result = m_FMODSystem->setSpeakerMode((FMOD_SPEAKERMODE)m_activeSpeakerMode);
+ if(result != FMOD_OK)
+ {
+ ErrorStringMsg("FMOD could not set speaker mode to the one specified in the project settings. Falling back to stereo.");
+ result = m_FMODSystem->setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
+ }
+ if(!ValidateFMODResult(result, "FMOD failed to set speaker mode ... ")) return false;
+
+ FMOD_INITFLAGS initFlags = FMOD_INIT_NORMAL;
+
+ if(HasARGV("fmodprofiler"))
+ initFlags |= FMOD_INIT_ENABLE_PROFILE;
+
+ // Initialize FMOD.
+#if UNITY_IPHONE
+ FMOD_IPHONE_EXTRADRIVERDATA extradriverdata;
+ memset(&extradriverdata, 0, sizeof(FMOD_IPHONE_EXTRADRIVERDATA));
+
+ if (GetPlayerSettings().prepareIOSForRecording)
+ extradriverdata.sessionCategory = FMOD_IPHONE_SESSIONCATEGORY_PLAYANDRECORD;
+ else if (GetPlayerSettings().overrideIPodMusic)
+ extradriverdata.sessionCategory = FMOD_IPHONE_SESSIONCATEGORY_SOLOAMBIENTSOUND;
+ else
+ extradriverdata.sessionCategory = FMOD_IPHONE_SESSIONCATEGORY_AMBIENTSOUND;
+
+ extradriverdata.forceMixWithOthers = !GetPlayerSettings().overrideIPodMusic;
+
+ if (m_DSPBufferSize != 0)
+ {
+ result = m_FMODSystem->setDSPBufferSize(m_DSPBufferSize, 4);
+ if(!ValidateFMODResult(result, "FMOD failed to set DSP Buffer size ... ")) return false;
+ }
+
+ result = m_FMODSystem->init(100, initFlags, &extradriverdata);
+#elif UNITY_PS3
+ if (!IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1) && m_activeSpeakerMode == FMOD_SPEAKERMODE_STEREO)
+ initFlags |= FMOD_INIT_PS3_FORCE2CHLPCM;
+
+ FMOD_PS3_EXTRADRIVERDATA extradriverdata;
+ memset(&extradriverdata, 0, sizeof(FMOD_PS3_EXTRADRIVERDATA));
+ extradriverdata.spurs = g_pSpursInstance;
+ extradriverdata.spursmode = FMOD_PS3_SPURSMODE_CREATECONTEXT;
+ extradriverdata.spurs_taskset_priorities = &g_aFmodPriorities[0];
+ result = m_FMODSystem->setSoftwareChannels(64);
+ result = m_FMODSystem->setOutput((!UNITY_EDITOR && m_DisableAudio) ? FMOD_OUTPUTTYPE_NOSOUND : FMOD_OUTPUTTYPE_PS3);
+ result = m_FMODSystem->setSpeakerMode(FMOD_SPEAKERMODE_7POINT1);
+ result = m_FMODSystem->init(100, initFlags | FMOD_INIT_VOL0_BECOMES_VIRTUAL, &extradriverdata);
+
+#elif UNITY_XENON
+ FMOD_360_EXTRADRIVERDATA extraDriverData;
+ ZeroMemory(&extraDriverData, sizeof(extraDriverData));
+ extraDriverData.xaudio2instance = xenon::Audio::GetXAudio();
+ result = m_FMODSystem->init(100, initFlags, &extraDriverData);
+#elif UNITY_ANDROID
+ result = m_FMODSystem->setOutput((!UNITY_EDITOR && m_DisableAudio) ? FMOD_OUTPUTTYPE_NOSOUND : FMOD_OUTPUTTYPE_AUDIOTRACK);
+ if(!ValidateFMODResult(result, "FMOD failed to force Java Audio Track output ... ")) return false;
+ if (m_DSPBufferSize != 0)
+ {
+ result = m_FMODSystem->setDSPBufferSize(m_DSPBufferSize, 4);
+ if(!ValidateFMODResult(result, "FMOD failed to set DSP Buffer size ... ")) return false;
+ }
+ result = m_FMODSystem->setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
+ result = m_FMODSystem->init(100, initFlags, NULL);
+#else
+ result = m_FMODSystem->init(100, initFlags, NULL);
+#endif
+ if(!ValidateFMODResult(result, "FMOD failed to initialize ... ")) return false;
+
+#if UNITY_LINUX
+ LogDriverDetails (m_FMODSystem, driverID);
+#endif
+
+ return true;
+}
+
+void AudioManager::CloseFMOD()
+{
+ if (m_FMODSystem)
+ {
+ // Cleanup sources
+ std::vector<AudioSource*> audioSources;
+ Object::FindObjectsOfType(&audioSources);
+ for(std::vector<AudioSource*>::iterator it = audioSources.begin(); it != audioSources.end(); ++it)
+ (*it)->Cleanup();
+
+ // Cleanup listener(s)
+ std::vector<AudioListener*> audioListeners;
+ Object::FindObjectsOfType(&audioListeners);
+ for(std::vector<AudioListener*>::iterator it = audioListeners.begin(); it != audioListeners.end(); ++it)
+ (*it)->Cleanup();
+
+ // Cleanup reverb zone(s)
+ std::vector<AudioReverbZone*> audioReverbZones;
+ Object::FindObjectsOfType(&audioReverbZones);
+ for(std::vector<AudioReverbZone*>::iterator it = audioReverbZones.begin(); it != audioReverbZones.end(); ++it)
+ (*it)->Cleanup();
+
+ if (m_ChannelGroup_FX_IgnoreVolume)
+ {
+ m_ChannelGroup_FX_IgnoreVolume->release();
+ m_ChannelGroup_FX_IgnoreVolume = NULL;
+ }
+
+ if (m_ChannelGroup_NoFX_IgnoreVolume)
+ {
+ m_ChannelGroup_NoFX_IgnoreVolume->release();
+ m_ChannelGroup_NoFX_IgnoreVolume = NULL;
+ }
+
+ if (m_ChannelGroup_FX_UseVolume)
+ {
+ m_ChannelGroup_FX_UseVolume->release();
+ m_ChannelGroup_FX_UseVolume = NULL;
+ }
+
+ if (m_ChannelGroup_NoFX_UseVolume)
+ {
+ m_ChannelGroup_NoFX_UseVolume->release();
+ m_ChannelGroup_NoFX_UseVolume = NULL;
+ }
+
+ // m_ChannelGroup_FMODMaster is the FMOD master group so we should not call release on it
+ m_ChannelGroup_FMODMaster = NULL;
+
+ // cleanup any loaded audio clips
+ std::vector<AudioClip*> audioClips;
+ Object::FindObjectsOfType(&audioClips);
+ for(std::vector<AudioClip*>::iterator it = audioClips.begin(); it != audioClips.end(); ++it)
+ (*it)->Cleanup();
+
+ m_FMODSystem->close();
+ }
+
+ delete m_ScriptBufferManager;
+ m_ScriptBufferManager = NULL;
+}
+
+
+
+int AudioManager::GetMemoryAllocated() const
+{
+ int a = 0;
+ FMOD::Memory_GetStats(&a, NULL);
+ return a;
+}
+
+float AudioManager::GetCPUUsage() const
+{
+ float c = 0.0f;
+ if (m_FMODSystem)
+ m_FMODSystem->getCPUUsage(NULL, NULL, NULL,NULL, &c);
+ return c;
+}
+
+#if ENABLE_PROFILER
+void AudioManager::GetProfilerData( AudioStats& audioStats )
+{
+ if (m_FMODSystem)
+ {
+ FMOD::Memory_GetStats(&audioStats.audioMemUsage, &audioStats.audioMaxMemUsage);
+ float cpuUsage;
+ m_FMODSystem->getCPUUsage(NULL, NULL, NULL, NULL, &cpuUsage);
+ audioStats.audioCPUusage = RoundfToInt(cpuUsage * 10.0F);
+ m_FMODSystem->getChannelsPlaying(&audioStats.audioVoices);
+ audioStats.pausedSources = m_PausedSources.size_slow();
+ audioStats.playingSources = m_Sources.size_slow();
+ audioStats.audioClipCount = AudioClip::s_AudioClipCount;
+ audioStats.audioSourceCount = AudioSource::s_AudioSourceCount;
+ FMOD_MEMORY_USAGE_DETAILS details;
+ m_FMODSystem->getMemoryInfo(FMOD_EVENT_MEMBITS_ALL, 0, &audioStats.audioMemDetailsUsage, &details);
+ audioStats.audioMemDetails.other = details.other; /* [out] Memory not accounted for by other types */
+ audioStats.audioMemDetails.string = details.string; /* [out] String data */
+ audioStats.audioMemDetails.system = details.system; /* [out] System object and various internals */
+ audioStats.audioMemDetails.plugins = details.plugins; /* [out] Plugin objects and internals */
+ audioStats.audioMemDetails.output = details.output; /* [out] Output module object and internals */
+ audioStats.audioMemDetails.channel = details.channel; /* [out] Channel related memory */
+ audioStats.audioMemDetails.channelgroup = details.channelgroup; /* [out] ChannelGroup objects and internals */
+ audioStats.audioMemDetails.codec = details.codec; /* [out] Codecs allocated for streaming */
+ audioStats.audioMemDetails.file = details.file; /* [out] File buffers and structures */
+ audioStats.audioMemDetails.sound = details.sound; /* [out] Sound objects and internals */
+ audioStats.audioMemDetails.secondaryram = details.secondaryram; /* [out] Sound data stored in secondary RAM */
+ audioStats.audioMemDetails.soundgroup = details.soundgroup; /* [out] SoundGroup objects and internals */
+ audioStats.audioMemDetails.streambuffer = details.streambuffer; /* [out] Stream buffer memory */
+ audioStats.audioMemDetails.dspconnection = details.dspconnection; /* [out] DSPConnection objects and internals */
+ audioStats.audioMemDetails.dsp = details.dsp; /* [out] DSP implementation objects */
+ audioStats.audioMemDetails.dspcodec = details.dspcodec; /* [out] Realtime file format decoding DSP objects */
+ audioStats.audioMemDetails.profile = details.profile; /* [out] Profiler memory footprint. */
+ audioStats.audioMemDetails.recordbuffer = details.recordbuffer; /* [out] Buffer used to store recorded data from microphone */
+ audioStats.audioMemDetails.reverb = details.reverb; /* [out] Reverb implementation objects */
+ audioStats.audioMemDetails.reverbchannelprops = details.reverbchannelprops; /* [out] Reverb channel properties structs */
+ audioStats.audioMemDetails.geometry = details.geometry; /* [out] Geometry objects and internals */
+ audioStats.audioMemDetails.syncpoint = details.syncpoint; /* [out] Sync point memory. */
+ audioStats.audioMemDetails.eventsystem = details.eventsystem; /* [out] EventSystem and various internals */
+ audioStats.audioMemDetails.musicsystem = details.musicsystem; /* [out] MusicSystem and various internals */
+ audioStats.audioMemDetails.fev = details.fev; /* [out] Definition of objects contained in all loaded projects e.g. events, groups, categories */
+ audioStats.audioMemDetails.memoryfsb = details.memoryfsb; /* [out] Data loaded with preloadFSB */
+ audioStats.audioMemDetails.eventproject = details.eventproject; /* [out] EventProject objects and internals */
+ audioStats.audioMemDetails.eventgroupi = details.eventgroupi; /* [out] EventGroup objects and internals */
+ audioStats.audioMemDetails.soundbankclass = details.soundbankclass; /* [out] Objects used to manage wave banks */
+ audioStats.audioMemDetails.soundbanklist = details.soundbanklist; /* [out] Data used to manage lists of wave bank usage */
+ audioStats.audioMemDetails.streaminstance = details.streaminstance; /* [out] Stream objects and internals */
+ audioStats.audioMemDetails.sounddefclass = details.sounddefclass; /* [out] Sound definition objects */
+ audioStats.audioMemDetails.sounddefdefclass = details.sounddefdefclass; /* [out] Sound definition static data objects */
+ audioStats.audioMemDetails.sounddefpool = details.sounddefpool; /* [out] Sound definition pool data */
+ audioStats.audioMemDetails.reverbdef = details.reverbdef; /* [out] Reverb definition objects */
+ audioStats.audioMemDetails.eventreverb = details.eventreverb; /* [out] Reverb objects */
+ audioStats.audioMemDetails.userproperty = details.userproperty; /* [out] User property objects */
+ audioStats.audioMemDetails.eventinstance = details.eventinstance; /* [out] Event instance base objects */
+ audioStats.audioMemDetails.eventinstance_complex = details.eventinstance_complex; /* [out] Complex event instance objects */
+ audioStats.audioMemDetails.eventinstance_simple = details.eventinstance_simple; /* [out] Simple event instance objects */
+ audioStats.audioMemDetails.eventinstance_layer = details.eventinstance_layer; /* [out] Event layer instance objects */
+ audioStats.audioMemDetails.eventinstance_sound = details.eventinstance_sound; /* [out] Event sound instance objects */
+ audioStats.audioMemDetails.eventenvelope = details.eventenvelope; /* [out] Event envelope objects */
+ audioStats.audioMemDetails.eventenvelopedef = details.eventenvelopedef; /* [out] Event envelope definition objects */
+ audioStats.audioMemDetails.eventparameter = details.eventparameter; /* [out] Event parameter objects */
+ audioStats.audioMemDetails.eventcategory = details.eventcategory; /* [out] Event category objects */
+ audioStats.audioMemDetails.eventenvelopepoint = details.eventenvelopepoint; /* [out] Event envelope point objects */
+ audioStats.audioMemDetails.eventinstancepool = details.eventinstancepool; /* [out] Event instance pool memory */
+ }
+}
+#endif
+#endif // ENABLE_AUDIO_FMOD
+
+#if ENABLE_WWW && ENABLE_AUDIO_FMOD
+FMOD::Sound* AudioManager::CreateFMODSoundFromWWW(WWW* webStream,
+ bool threeD,
+ FMOD_SOUND_TYPE suggestedtype,
+ FMOD_SOUND_FORMAT format,
+ unsigned freq,
+ unsigned channels,
+ bool stream)
+{
+ if (!m_FMODSystem)
+ return NULL;
+
+ FMOD::Sound* sound = NULL;
+ FMOD_CREATESOUNDEXINFO exInfo;
+ memset(&exInfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
+ exInfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
+ exInfo.decodebuffersize = 16384;
+ exInfo.suggestedsoundtype = suggestedtype;
+ exInfo.format = format;
+ exInfo.defaultfrequency = freq;
+ exInfo.numchannels = channels;
+ exInfo.useropen = AudioClip::WWWOpen;
+ exInfo.userclose = AudioClip::WWWClose;
+ exInfo.userread = AudioClip::WWWRead;
+ exInfo.userseek = AudioClip::WWWSeek;
+ exInfo.userdata = (void*)webStream;
+
+
+ FMOD_MODE mode = FMOD_SOFTWARE | (threeD?FMOD_3D:FMOD_2D) | (stream?FMOD_CREATESTREAM:FMOD_CREATESAMPLE) | (suggestedtype==FMOD_SOUND_TYPE_MPEG?FMOD_MPEGSEARCH:FMOD_IGNORETAGS) | FMOD_LOOP_NORMAL | FMOD_3D_CUSTOMROLLOFF ;
+
+ if (suggestedtype==FMOD_SOUND_TYPE_RAW)
+ mode |= FMOD_OPENRAW;
+
+ FMOD_RESULT err = m_FMODSystem->createSound(
+ (const char*)webStream,
+ mode,
+ &exInfo,
+ &sound );
+
+ if (err != FMOD_OK)
+ {
+ m_LastErrorString = FMOD_ErrorString(err);
+ m_LastFMODErrorResult = err;
+ return NULL;
+ }
+ else
+ {
+ // FMOD_LOOP_NORMAL is set on createSound() - this prepares the sound for looping
+ // now turn it off (and let the user set it later)
+ sound->setMode( FMOD_LOOP_OFF );
+ return sound;
+ }
+
+}
+#endif // ENABLE_WWW && ENABLE_AUDIO_FMOD
+
+#if ENABLE_AUDIO_FMOD
+FMOD::Sound* AudioManager::CreateFMODSoundFromMovie(AudioClip* clip, bool threeD)
+{
+ if (!m_FMODSystem)
+ return NULL;
+
+ Assert ( clip->GetMovie() );
+
+ FMOD::Sound* sound = NULL;
+ FMOD_CREATESOUNDEXINFO exInfo;
+ memset(&exInfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
+ exInfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
+ // @FIX FMODs position is increased even though the pcm reader thread is starving (FMOD just repeats the last buffer).
+ // @FIX This can lead to out-of-synch problems and that the audio is stopped before the movie is done.
+ // @FIX fix: double the length of the clip as a headroom (and stop the audio when video frames are done)
+ // @FIX duration: Theora format is really stupid for streams, so if we´re not lucky that the duration is in the meta-tags we set it to infinity
+ if (clip->GetMovie()->GetMovieTotalDuration() < 0)
+ exInfo.length = 0xffffffff;
+ else
+ exInfo.length = (unsigned int)(clip->GetMovie()->GetMovieTotalDuration() * clip->GetFrequency() * clip->GetChannelCount() * ( clip->GetBitsPerSample() / 8 ) * 2);
+ exInfo.decodebuffersize = 4096;
+ exInfo.format = FMOD_SOUND_FORMAT_PCM16;
+ exInfo.defaultfrequency = clip->GetFrequency();
+ exInfo.numchannels = clip->GetChannelCount();
+ exInfo.pcmreadcallback = AudioClip::pcmread;
+ exInfo.userdata = (void*)clip;
+
+
+ FMOD_MODE mode = FMOD_SOFTWARE | (threeD?FMOD_3D:FMOD_2D) | FMOD_CREATESTREAM | FMOD_OPENUSER | FMOD_IGNORETAGS | FMOD_LOOP_NORMAL | FMOD_3D_CUSTOMROLLOFF ;
+
+ FMOD_RESULT err = m_FMODSystem->createSound(
+ 0,
+ mode,
+ &exInfo,
+ &sound );
+
+ if (err != FMOD_OK)
+ {
+ m_LastErrorString = FMOD_ErrorString(err);
+ m_LastFMODErrorResult = err;
+ return NULL;
+ }
+ else
+ {
+ // FMOD_LOOP_NORMAL is set on createSound() - this prepares the sound for looping
+ // now turn it off (and let the user set it later)
+ sound->setMode( FMOD_LOOP_OFF );
+ return sound;
+ }
+}
+
+
+FMOD::Sound* AudioManager::CreateFMODSound(const void* buffer, FMOD_CREATESOUNDEXINFO exInfo, FMOD_MODE mode, bool useHardwareDecoder)
+{
+ Assert(exInfo.cbsize == sizeof(FMOD_CREATESOUNDEXINFO));
+
+#if !(UNITY_ANDROID || UNITY_XENON || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN)
+ // On Android the file paths have been converted to ascii and fmod doesn't seem to accept unicode strings
+ dynamic_array<UnicodeChar> unicodeBuf (kMemTempAlloc);
+ if((mode & (FMOD_OPENMEMORY | FMOD_OPENMEMORY_POINT | FMOD_OPENUSER)) == 0 && (mode & FMOD_UNICODE) == 0)
+ {
+ ConvertUTF8toUTF16((const char*)buffer, unicodeBuf);
+ unicodeBuf.push_back(0);
+ unicodeBuf.push_back(0); // FMOD reads unicode strings as shorts, so it expects 2 bytes
+ buffer = (const char*)&unicodeBuf[0];
+ mode |= FMOD_UNICODE;
+ }
+#endif
+
+#if UNITY_IPHONE
+ if (mode & FMOD_CREATESTREAM)
+ {
+ exInfo.suggestedsoundtype = exInfo.suggestedsoundtype==FMOD_SOUND_TYPE_MPEG?FMOD_SOUND_TYPE_AUDIOQUEUE:exInfo.suggestedsoundtype;
+ FMOD_AUDIOQUEUE_CODECPOLICY policy = FMOD_AUDIOQUEUE_CODECPOLICY_SOFTWAREONLY;
+ if (useHardwareDecoder)
+ policy = FMOD_AUDIOQUEUE_CODECPOLICY_DEFAULT; // try hardware, if it fails then try software
+ exInfo.audioqueuepolicy = policy;
+ }
+#endif
+ FMOD::Sound* sound = NULL;
+ FMOD_RESULT result = m_FMODSystem->createSound(
+ (const char*)buffer,
+ mode,
+ &exInfo,
+ &sound );
+#if UNITY_IPHONE
+ // if the Apple's AudioQueue codec(sf/hw) fails completely (this can happen with some obscure mp3s that FMOD software MP3 codec can read/import, but Apple's decoder can't)
+ // then use FMOD's SOFTWARE codec instead
+ // @TODO Wait for FMOD to handle this internally
+ if (FMOD_ERR_FORMAT == result)
+ {
+ exInfo.suggestedsoundtype = FMOD_SOUND_TYPE_MPEG;
+ result = m_FMODSystem->createSound(
+ (const char*)buffer,
+ mode,
+ &exInfo,
+ &sound );
+ }
+#endif
+
+ if (result != FMOD_OK)
+ {
+ m_LastErrorString = FMOD_ErrorString(result);
+ m_LastFMODErrorResult = result;
+#if UNITY_WII
+ ErrorStringMsg ("ERROR: %s\n", m_LastErrorString.c_str());
+#endif
+ return NULL;
+ }
+ else
+ {
+ // FMOD_LOOP_NORMAL is set on createSound() - this prepares the sound for looping
+ // now turn it off (and let the user set it later)
+ sound->setMode( FMOD_LOOP_OFF );
+ return sound;
+ }
+}
+#endif //ENABLE_AUDIO_FMOD
+
+#if UNITY_EDITOR && ENABLE_AUDIO_FMOD
+FMOD::Sound* AudioManager::CreateFMODSound(const std::string& path, bool threeD, bool hardware, bool openOnly /* = false */)
+{
+ FMOD::Sound* sound = NULL;
+
+ if (!m_FMODSystem)
+ return NULL;
+
+ Assert( m_FMODSystem );
+
+ const char* strPtr = path.c_str();
+ FMOD_MODE mode = (openOnly?FMOD_OPENONLY:0x0) | FMOD_SOFTWARE | (threeD?FMOD_3D:FMOD_2D) | FMOD_LOOP_OFF | FMOD_MPEGSEARCH;
+#if !UNITY_ANDROID
+ // On Android the file paths have been converted to ascii and fmod doesn't seem to accept unicode strings
+ dynamic_array<UnicodeChar> unicodeBuf (kMemTempAlloc);
+ if((mode & (FMOD_OPENMEMORY | FMOD_OPENMEMORY_POINT | FMOD_OPENUSER)) == 0 && (mode & FMOD_UNICODE) == 0)
+ {
+ ConvertUTF8toUTF16(strPtr, unicodeBuf);
+ unicodeBuf.push_back(0);
+ unicodeBuf.push_back(0); // FMOD reads unicode strings as shorts, so it expects 2 bytes
+ strPtr = (const char*)&unicodeBuf[0];
+ mode |= FMOD_UNICODE;
+ }
+#endif
+
+ FMOD_RESULT err = m_FMODSystem->createSound(strPtr, mode, 0, &sound);
+ if (err != FMOD_OK)
+ {
+ m_LastErrorString = FMOD_ErrorString(err);
+ m_LastFMODErrorResult = err;
+ return NULL;
+ }
+ else
+ return sound;
+}
+
+#endif // UNITY_EDITOR && ENABLE_AUDIO_FMOD
+
+
+#if ENABLE_AUDIO_FMOD
+FMOD::Channel* AudioManager::GetFreeFMODChannel(FMOD::Sound* sound, bool paused /*=true*/)
+{
+ if (!m_FMODSystem)
+ return NULL;
+
+ FMOD::Channel* channel;
+ FMOD_RESULT err = m_FMODSystem->playSound(FMOD_CHANNEL_FREE, sound, paused || (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a3) && m_IsPaused), &channel);
+ if (err != FMOD_OK)
+ {
+ m_LastErrorString = FMOD_ErrorString(err);
+ m_LastFMODErrorResult = err;
+ return NULL;
+ }
+ else
+ return channel;
+
+}
+#endif //ENABLE_AUDIO_FMOD
+
+
+void AudioManager::UpdateListener (
+ const Vector3f& position,
+ const Vector3f& velocity,
+ const Vector3f& up,
+ const Vector3f& forward)
+{
+#if ENABLE_AUDIO_FMOD
+ if (!m_FMODSystem)
+ return;
+
+ m_FMODSystem->set3DListenerAttributes(
+ 0,
+ reinterpret_cast<const FMOD_VECTOR*>( &position ),
+ reinterpret_cast<const FMOD_VECTOR*>( &velocity ),
+ reinterpret_cast<const FMOD_VECTOR*>( &forward ),
+ reinterpret_cast<const FMOD_VECTOR*>( &up )
+ );
+#endif //ENABLE_AUDIO_FMOD
+
+}
+
+int AudioManager::GetAutomaticUpdateMode(GameObject *go)
+{
+ Rigidbody* body = go->QueryComponent (Rigidbody);
+ if (body)
+ return kVelocityUpdateModeFixed;
+
+ Transform* parent = go->GetComponent (Transform).GetParent ();
+ while (parent)
+ {
+ go = parent->GetGameObjectPtr ();
+ if (go)
+ body = go->QueryComponent (Rigidbody);
+ else
+ body = NULL;
+ if (body)
+ return kVelocityUpdateModeFixed;
+
+ parent = parent->GetParent ();
+ }
+ return kVelocityUpdateModeDynamic;
+}
+
+void AudioManager::SetPause (bool pause)
+{
+ if(m_IsPaused == pause)
+ return;
+ m_IsPaused = pause;
+#if ENABLE_AUDIO_FMOD
+ unsigned clockLo, clockHi;
+ m_FMODSystem->getDSPClock(&clockHi, &clockLo);
+ UInt64 dspTicks = ((UInt64)clockHi << 32) + clockLo;
+#else
+ UInt64 dspTicks = 0;
+#endif
+ if (m_IsPaused)
+ {
+ m_pauseStartTicks = dspTicks;
+ // Pause all audio sources and put them in the m_PausedSources, when resuming we start playing them again
+ for (TAudioSourcesIterator i=m_Sources.begin();i != m_Sources.end();)
+ {
+ AudioSource& source = **(i);
+ i++;
+
+ if (source.IsPlaying())
+ {
+ source.Pause();
+ m_PausedSources.push_back(source.m_Node);
+ }
+ source.PauseOneShots();
+ }
+ }
+ else
+ {
+ UInt64 pauseDuration = dspTicks - m_pauseStartTicks;
+ m_accPausedTicks += pauseDuration;
+ // Resume all paused audio sources
+ for (TAudioSourcesIterator i=m_PausedSources.begin();i != m_PausedSources.end();)
+ {
+ AudioSource& source = **(i);
+ ++i;
+
+ // Play is pushing the source to the m_Sources list
+ // don't play if editor is pause
+ source.Play();
+ if(source.HasScheduledTime())
+ source.CorrectScheduledTimeAfterUnpause(pauseDuration);
+ }
+
+ for (TAudioSourcesIterator i=m_Sources.begin();i != m_Sources.end();)
+ {
+ AudioSource& source = **(i);
+ ++i;
+ source.ResumeOneShots();
+ }
+ Assert( m_PausedSources.empty() );
+ }
+}
+
+void AudioManager::SetVolume (float volume)
+{
+#if ENABLE_AUDIO_FMOD
+ if (!m_FMODSystem)
+ return;
+ m_ChannelGroup_FX_UseVolume->setVolume(volume);
+ m_ChannelGroup_NoFX_UseVolume->setVolume(volume);
+#endif
+#if UNITY_FLASH
+ __asm __volatile__("SoundMixer.soundTransform = new SoundTransform(Math.min(Math.max(0.0, %0), 1.0));"::"f"(volume));
+#endif
+ m_Volume = volume;
+}
+
+float AudioManager::GetVolume () const
+{
+ return m_Volume;
+}
+
+
+template<class TransferFunction>
+void AudioManager::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ transfer.Transfer (m_DefaultVolume, "m_Volume", kSimpleEditorMask);
+ transfer.Transfer (m_Rolloffscale, "Rolloff Scale");
+
+#if ENABLE_AUDIO_FMOD
+ if (!(transfer.IsWritingGameReleaseData () && transfer.GetBuildingTarget().platform==kBuildFlash || transfer.GetBuildingTarget().platform==kBuildWebGL)){
+ transfer.Transfer (m_SpeedOfSound, "m_SpeedOfSound");
+ transfer.Transfer (m_DopplerFactor, "Doppler Factor");
+
+ transfer.Transfer ((SInt32&)m_speakerMode, "Default Speaker Mode"); // Remember to specify the TransferName in the doxygen comment in the .h file
+
+ TRANSFER (m_DSPBufferSize);
+ }
+#endif
+
+ TRANSFER (m_DisableAudio);
+}
+
+
+void AudioManager::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+#if ENABLE_AUDIO_FMOD
+ if (!m_FMODSystem)
+ {
+ InitFMOD();
+ m_IsPaused = false;
+ }
+
+ if (!m_FMODSystem)
+ return;
+
+ // has the speakermode changed?
+ if (m_activeSpeakerMode != m_speakerMode)
+ {
+ // bootstrap FMOD
+ ReloadFMODSounds();
+ if (!m_FMODSystem)
+ return;
+ }
+ m_Volume = m_DefaultVolume;
+ m_ChannelGroup_FX_UseVolume->setVolume(m_Volume);
+ m_ChannelGroup_NoFX_UseVolume->setVolume(m_Volume);
+ m_FMODSystem->set3DSettings(m_DopplerFactor, 1, m_Rolloffscale);
+#endif
+}
+
+PROFILER_INFORMATION(gAudioUpdateProfile, "AudioManager.Update", kProfilerAudio);
+PROFILER_INFORMATION(gAudioFixedUpdateProfile, "AudioManager.FixedUpdate", kProfilerAudio);
+
+#if UNITY_EDITOR
+void AudioManager::ListenerCheck()
+{
+ if (!IsWorldPlaying())
+ return;
+
+ int listenerCount = m_Listeners.size_slow();
+ // @TODO enable multiple listeners
+ if (listenerCount == 0 && !m_Sources.empty())
+ {
+ LogString("There are no audio listeners in the scene. Please ensure there is always one audio listener in the scene");
+ m_ChannelGroup_FX_UseVolume->setVolume(0.0f);
+ m_ChannelGroup_NoFX_UseVolume->setVolume(0.0f);
+ }
+ else
+ {
+ if (listenerCount > 1)
+ LogString(Format("There are %d audio listeners in the scene. Please ensure there is always exactly one audio listener in the scene.", listenerCount));
+ m_ChannelGroup_FX_UseVolume->setVolume(m_Volume);
+ m_ChannelGroup_NoFX_UseVolume->setVolume(m_Volume);
+ }
+}
+#endif
+
+
+#define Unity_HiWord(x) ((UInt32)((UInt64)(x) >> 32))
+#define Unity_LoWord(x) ((UInt32)(x))
+#if UNITY_WII
+void AudioManager::UpdateOnDiskEject()
+{
+ for (TAudioSourcesIterator i = m_Sources.begin(); i != m_Sources.end(); i++)
+ {
+ AudioSource& curSource = **i;
+ curSource.Update();
+ }
+ m_FMODSystem->update();
+}
+#endif
+void AudioManager::ProcessScheduledSources()
+{
+#if ENABLE_AUDIO_FMOD
+ // start scheduled sources
+ // Get mixer clock
+ unsigned hiclock, loclock;
+ m_FMODSystem->getDSPClock(&hiclock, &loclock);
+#endif
+ for (TScheduledSourcesIterator s = m_ScheduledSources.begin(); s != m_ScheduledSources.end(); s++)
+ {
+ const AudioScheduledSource& p = *s;
+ AudioSource* curSource = p.source;
+
+ Assert(curSource != NULL);
+ Assert(curSource->m_Channel != NULL);
+
+
+#if ENABLE_AUDIO_FMOD
+ if (p.time != 0.0)
+ {
+ int sampleRate;
+ m_FMODSystem->getSoftwareFormat(&sampleRate, NULL, NULL, NULL, NULL, NULL);
+ if(p.time > 0.0)
+ {
+ // exact scheduled
+ UInt64 sample = (UInt64)(p.time * sampleRate) + m_accPausedTicks;
+ curSource->m_Channel->setDelay(FMOD_DELAYTYPE_DSPCLOCK_START, Unity_HiWord(sample), Unity_LoWord(sample));
+ }
+ else
+ {
+ UInt64 sample = ((UInt64)hiclock << 32) + loclock + (UInt64)(-p.time * sampleRate);
+ curSource->m_Channel->setDelay(FMOD_DELAYTYPE_DSPCLOCK_START, Unity_HiWord(sample), Unity_LoWord(sample));
+ }
+ curSource->m_HasScheduledStartDelay = true;
+ }
+#endif
+ // play (TODO: if paused in the same frame - pause here)
+ bool paused = curSource->m_pause || (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a3) && m_IsPaused && !curSource->m_AudioParameters.ignoreListenerPause);
+ curSource->m_Channel->setPaused(paused);
+ AddAudioSource(curSource, paused); // to make sure source is put into active or paused sources lists
+ }
+ // clean queue
+ m_ScheduledSources.clear();
+}
+
+void AudioManager::Update()
+{
+ PROFILER_AUTO (gAudioUpdateProfile, NULL);
+
+#if ENABLE_AUDIO_FMOD
+ if (!m_FMODSystem)
+ return;
+#endif
+
+ ProcessScheduledSources();
+
+#if UNITY_EDITOR
+ ListenerCheck();
+#endif
+
+ for (TAudioListenersIterator l = m_Listeners.begin(); l != m_Listeners.end(); l++)
+ {
+ AudioListener& curListener = **l;
+ curListener.Update();
+ }
+
+ for (TAudioSourcesIterator i = m_Sources.begin(); i != m_Sources.end(); i++)
+ {
+ AudioSource& curSource = **i;
+#if UNITY_EDITOR
+ if (!IsWorldPlaying() && curSource.m_Channel)
+ {
+ if (curSource.GetGameObject().IsMarkedVisible())
+ curSource.m_Channel->setMute(curSource.GetMute());
+ else
+ curSource.m_Channel->setMute(true);
+ }
+#endif
+ curSource.Update();
+ }
+#if ENABLE_AUDIO_FMOD
+ // update reverb zones position
+ for (TAudioReverbZonesIterator r = m_ReverbZones.begin(); r != m_ReverbZones.end(); ++r)
+ {
+ AudioReverbZone& curReverbZone = **r;
+ curReverbZone.Update();
+ }
+
+ m_FMODSystem->update();
+#endif
+}
+
+
+void AudioManager::FixedUpdate()
+{
+#if ENABLE_AUDIO_FMOD
+ if (!m_FMODSystem)
+ return;
+#endif
+ PROFILER_AUTO (gAudioFixedUpdateProfile, NULL);
+
+ #if UNITY_EDITOR
+ ListenerCheck();
+ #endif
+
+ for (TAudioListenersIterator l = m_Listeners.begin(); l != m_Listeners.end(); l++)
+ {
+ AudioListener& curListener = **l;
+ curListener.FixedUpdate();
+ }
+
+ TAudioSourcesIterator i;
+ for (i = m_Sources.begin(); i != m_Sources.end(); i++)
+ {
+ AudioSource& curSource = **i;
+ curSource.FixedUpdate();
+ }
+}
+
+void AudioManager::AddAudioSource( AudioSource* s, bool paused )
+{
+ Assert(s);
+ if(paused)
+ m_PausedSources.push_back(s->m_Node);
+ else
+ m_Sources.push_back(s->m_Node);
+}
+
+void AudioManager::RemoveAudioSource( AudioSource* s )
+{
+ Assert(s);
+ UnScheduleSource(s);
+ s->m_Node.RemoveFromList(); // note: removes either from m_Sources or m_PausedSources
+}
+
+void AudioManager::StopSources()
+{
+ TAudioSourcesIterator i = m_Sources.begin();
+ while (!m_Sources.empty())
+ {
+ AudioSource& curSource = **i;
+ ++i;
+ curSource.Stop(true);
+ }
+ i = m_PausedSources.begin();
+ while (!m_PausedSources.empty())
+ {
+ AudioSource& curSource = **i;
+ ++i;
+ curSource.Stop(true);
+ }
+}
+
+void AudioManager::AddAudioListener (AudioListener* s)
+{
+ Assert(s);
+ m_Listeners.push_back( s->GetNode() );
+}
+
+void AudioManager::RemoveAudioListener (AudioListener* s)
+{
+ Assert(s);
+ s->GetNode().RemoveFromList();
+}
+
+AudioListener* AudioManager::GetAudioListener() const
+{
+ if (!m_Listeners.empty())
+ return m_Listeners.back().GetData();
+ else
+ return NULL;
+}
+
+/// Schedule source to be played in sync. In this frame if time==0, delayed by -time if time<0 or scheduled at time
+void AudioManager::ScheduleSource(AudioSource* s, double time)
+{
+ s->m_ScheduledSource.RemoveFromList();
+ s->m_ScheduledSource.time = time;
+ m_ScheduledSources.push_back(s->m_ScheduledSource);
+}
+
+void AudioManager::UnScheduleSource(AudioSource* s)
+{
+ s->m_ScheduledSource.RemoveFromList();
+}
+
+#if ENABLE_AUDIO_FMOD
+void AudioManager::AddAudioReverbZone(AudioReverbZone* zone)
+{
+ Assert(zone);
+ m_ReverbZones.push_back(zone->m_Node);
+}
+
+void AudioManager::RemoveAudioReverbZone(AudioReverbZone* zone)
+{
+ Assert(zone);
+ zone->m_Node.RemoveFromList();
+}
+
+
+
+#endif
+
+#if ENABLE_MICROPHONE
+// Microphone(s)
+bool HasMicrophoneAuthorization ()
+{
+ #if UNITY_WINRT
+ #if UNITY_METRO
+ namespace Capabilities = metro::Capabilities;
+ #elif UNITY_WP8
+ namespace Capabilities = WP8::Capabilities;
+ #else
+ #error Unknown WinRT flavour (did you implement capability detection for the OS?)
+ #endif
+
+ Capabilities::IsSupported(Capabilities::kMicrophone, "because you're using Microphone functionality");
+ #endif // UNITY_WINRT
+
+ return GetUserAuthorizationManager().HasUserAuthorization(UserAuthorizationManager::kMicrophone);
+}
+
+const std::vector<std::string> AudioManager::GetRecordDevices() const
+{
+ std::vector<std::string> devices;
+ m_MicrophoneNameToIDMap.clear();
+
+ if (!m_FMODSystem)
+ return devices;
+
+ if (!HasMicrophoneAuthorization())
+ return devices;
+
+ int numDevices;
+ FMOD_RESULT result = m_FMODSystem->getRecordNumDrivers(&numDevices);
+
+ if (result != FMOD_OK)
+ return devices;
+
+ if (numDevices > 0)
+ {
+ for (int i=0; i < numDevices; ++i)
+ {
+ char name[255];
+
+ m_FMODSystem->getRecordDriverInfo(i, name, 255, NULL);
+
+ std::string strName = (char*)name;
+ // update map with a unique name
+ std::string origName = strName;
+ int no = 0;
+ while (m_MicrophoneNameToIDMap.find(strName) != m_MicrophoneNameToIDMap.end())
+ {
+ char post[3];
+ sprintf(post, " %i", ++no);
+ strName = origName + post;
+ }
+ devices.push_back(strName);
+ m_MicrophoneNameToIDMap[strName] = i;
+ }
+ }
+
+ return devices;
+}
+
+int AudioManager::GetMicrophoneDeviceIDFromName(const std::string& name) const
+{
+ if ( m_MicrophoneNameToIDMap.empty() )
+ GetRecordDevices();
+
+ // Double lookup on return is totally unnecessary, because we can cache the iterator
+ std::map<std::string, int>::const_iterator devit = m_MicrophoneNameToIDMap.find( name );
+ if ( devit != m_MicrophoneNameToIDMap.end() )
+ return devit->second;
+ else
+ return 0; // the default device is always 0.
+}
+
+
+void ReportError (char const* msg, FMOD_RESULT result)
+{
+ ErrorString (Format ("%s. result=%d (%s)", msg, result, FMOD_ErrorString (result)));
+}
+
+void CapsToSoundFormat (FMOD_CAPS caps, FMOD_SOUND_FORMAT *soundFormat, int *sampleSizeInBytes)
+{
+ *soundFormat = FMOD_SOUND_FORMAT_PCM16;
+ *sampleSizeInBytes = 2;
+
+ if ((caps & FMOD_CAPS_OUTPUT_FORMAT_PCM16) == FMOD_CAPS_OUTPUT_FORMAT_PCM16)
+ {
+ *soundFormat = FMOD_SOUND_FORMAT_PCM16;
+ *sampleSizeInBytes = 2;
+ }
+ else
+ if ((caps & FMOD_CAPS_OUTPUT_FORMAT_PCM8) == FMOD_CAPS_OUTPUT_FORMAT_PCM8)
+ {
+ *soundFormat = FMOD_SOUND_FORMAT_PCM8;
+ *sampleSizeInBytes = 1;
+ }
+ else
+ if ((caps & FMOD_CAPS_OUTPUT_FORMAT_PCM24) == FMOD_CAPS_OUTPUT_FORMAT_PCM24)
+ {
+ *soundFormat = FMOD_SOUND_FORMAT_PCM24;
+ *sampleSizeInBytes = 3;
+ }
+ else if ((caps & FMOD_CAPS_OUTPUT_FORMAT_PCM32) == FMOD_CAPS_OUTPUT_FORMAT_PCM32)
+ {
+ *soundFormat = FMOD_SOUND_FORMAT_PCM32;
+ *sampleSizeInBytes = 4;
+ }
+ else if ((caps & FMOD_CAPS_OUTPUT_FORMAT_PCMFLOAT) == FMOD_CAPS_OUTPUT_FORMAT_PCMFLOAT)
+ {
+ *soundFormat = FMOD_SOUND_FORMAT_PCMFLOAT;
+ *sampleSizeInBytes = sizeof (float);
+ }
+}
+
+void AudioManager::GetDeviceCaps(int deviceID, int *minFreq, int *maxFreq) const
+{
+ FMOD_CAPS caps = 0;
+ FMOD_RESULT result = m_FMODSystem->getRecordDriverCaps (deviceID, &caps, minFreq, maxFreq);
+
+ if (result != FMOD_OK)
+ {
+ ReportError ("Failed to get record driver caps", result);
+ }
+}
+
+FMOD::Sound* AudioManager::CreateSound (int deviceID, int lengthSec, int frequency)
+{
+ FMOD::Sound* sound;
+ FMOD_CAPS caps = 0;
+ FMOD_RESULT result = m_FMODSystem->getRecordDriverCaps (deviceID, &caps, NULL, NULL);
+
+ if (result != FMOD_OK)
+ {
+ ReportError ("Failed to get record driver caps", result);
+ return NULL;
+ }
+
+ FMOD_SOUND_FORMAT soundFormat = FMOD_SOUND_FORMAT_NONE;
+ int sampleSizeInBytes = 1;
+ CapsToSoundFormat (caps, &soundFormat, &sampleSizeInBytes);
+
+ FMOD_CREATESOUNDEXINFO exinfo;
+ memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
+
+ exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
+ exinfo.numchannels = 1;
+ exinfo.format = soundFormat;
+ exinfo.defaultfrequency = frequency;
+ exinfo.length = exinfo.defaultfrequency * sampleSizeInBytes * exinfo.numchannels * lengthSec;
+
+ FMOD_MODE mode = FMOD_2D | FMOD_SOFTWARE | FMOD_OPENUSER;
+ result = m_FMODSystem->createSound (0, mode, &exinfo, &sound);
+
+ if (result != FMOD_OK)
+ {
+ ReportError ("Failed to create sound clip for recording", result);
+ return NULL;
+ }
+
+ return sound;
+}
+
+PPtr<AudioClip> AudioManager::StartRecord(int deviceID, bool loop, int lengthSec, int frequency)
+{
+ if (!m_FMODSystem)
+ return NULL;
+
+ if (!HasMicrophoneAuthorization())
+ return NULL;
+
+ if (lengthSec <= 0)
+ {
+ ErrorString("Length of the recording must be greater than zero (0)");
+ return NULL;
+ }
+
+ if (frequency <= 0)
+ {
+ ErrorString("Frequency must be greater than zero (0)");
+ return NULL;
+ }
+
+ FMOD::Sound* sound = CreateSound (deviceID, lengthSec, frequency);
+
+ if (sound == NULL)
+ return NULL;
+
+ FMOD_RESULT result = m_FMODSystem->recordStart (deviceID, sound, loop);
+
+ if (result == FMOD_OK)
+ {
+ PPtr<AudioClip> audioClip = NEW_OBJECT(AudioClip);
+ audioClip->Reset();
+ audioClip->HackSetAwakeWasCalled();
+
+ audioClip->InitWSound(sound);
+ audioClip->SetName("Microphone");
+
+ return audioClip;
+ }
+ else
+ {
+ ReportError ("Starting Microphone failed", result);
+ return NULL;
+ }
+}
+
+bool AudioManager::EndRecord(int deviceID)
+{
+ if (!m_FMODSystem)
+ return false;
+
+ m_FMODSystem->recordStop(deviceID);
+
+ return true;
+}
+
+bool AudioManager::IsRecording(int deviceID) const
+{
+ if (!m_FMODSystem)
+ return false;
+
+ bool isRecording;
+ m_FMODSystem->isRecording(deviceID, &isRecording);
+ return isRecording;
+}
+
+unsigned AudioManager::GetRecordPosition(int deviceID) const
+{
+ if (!m_FMODSystem)
+ return 0;
+
+ unsigned pos;
+ m_FMODSystem->getRecordPosition(deviceID, &pos);
+ return pos;
+}
+
+#endif // ENABLE_MICROPHONE
+
+#if ENABLE_AUDIO_FMOD
+AudioScriptBufferManager& AudioManager::GetScriptBufferManager()
+{
+ return *GetScriptBufferManagerPtr();
+}
+
+AudioScriptBufferManager* AudioManager::GetScriptBufferManagerPtr()
+{
+ if (m_ScriptBufferManager == 0)
+ {
+ InitScriptBufferManager ();
+ }
+
+ return m_ScriptBufferManager;
+}
+#endif
+
+#if UNITY_EDITOR
+void AudioManager::PlayClip(AudioClip& clip, int startSample, bool loop, bool twoD)
+{
+ if (!m_FMODSystem)
+ return;
+
+ // update FMOD to get any device changes
+ m_FMODSystem->update();
+
+ if (m_EditorChannel)
+ m_EditorChannel->stop();
+
+ m_EditorChannel = clip.CreateChannel();
+
+ if(m_EditorChannel != NULL)
+ {
+ if (twoD && clip.Is3D())
+ {
+ m_EditorChannel->setMode(FMOD_2D);
+ }
+
+ m_EditorChannel->setChannelGroup(m_ChannelGroup_NoFX_IgnoreVolume);
+
+ FMOD_REVERB_CHANNELPROPERTIES rev;
+ memset(&rev, 0, sizeof(rev));
+ rev.Room = -10000;
+ m_EditorChannel->setReverbProperties(&rev);
+
+ if (loop)
+ m_EditorChannel->setMode(FMOD_LOOP_NORMAL);
+
+ // movie audio
+ if (clip.GetMovie())
+ clip.GetMovie()->SetAudioChannel(m_EditorChannel);
+
+ m_EditorChannel->setPaused(false);
+ }
+}
+
+
+void AudioManager::LoopClip(const AudioClip& clip, bool loop)
+{
+ if (m_EditorChannel)
+ m_EditorChannel->setMode(loop?FMOD_LOOP_NORMAL:FMOD_LOOP_OFF);
+}
+
+
+void AudioManager::StopClip(const AudioClip& clip)
+{
+ if (m_EditorChannel)
+ m_EditorChannel->stop();
+
+ if (clip.GetMovie())
+ clip.GetMovie()->SetAudioChannel(NULL);
+}
+
+void AudioManager::StopAllClips()
+{
+ if (m_EditorChannel)
+ m_EditorChannel->stop();
+}
+void AudioManager::PauseClip(const AudioClip& clip)
+{
+ if (m_EditorChannel)
+ m_EditorChannel->setPaused(true);
+
+ if (clip.GetMovie())
+ clip.GetMovie()->SetAudioChannel(NULL);
+}
+void AudioManager::ResumeClip(const AudioClip& clip)
+{
+ if (m_EditorChannel)
+ m_EditorChannel->setPaused(false);}
+
+bool AudioManager::IsClipPlaying(const AudioClip& clip)
+{
+ bool isPlaying = false;
+ if (m_EditorChannel)
+ m_EditorChannel->isPlaying(&isPlaying);
+ return isPlaying;
+}
+
+float AudioManager::GetClipPosition(const AudioClip& clip)
+{
+ unsigned int pos = 0;
+ if (m_EditorChannel)
+ m_EditorChannel->getPosition(&pos, FMOD_TIMEUNIT_MS);
+ return (float)pos / 1000.f;
+}
+
+unsigned int AudioManager::GetClipSamplePosition(const AudioClip& clip)
+{
+ unsigned int pos = 0;
+ if (m_EditorChannel)
+ m_EditorChannel->getPosition(&pos, FMOD_TIMEUNIT_PCM);
+ return pos;
+}
+
+void AudioManager::SetClipSamplePosition(const AudioClip& clip, unsigned int iSamplePosition)
+{
+ if (m_EditorChannel)
+ m_EditorChannel->setPosition(iSamplePosition, FMOD_TIMEUNIT_PCM);
+}
+
+#endif // UNITY_EDITOR
+
+
+
+IMPLEMENT_CLASS_HAS_INIT (AudioManager)
+IMPLEMENT_OBJECT_SERIALIZE (AudioManager)
+GET_MANAGER (AudioManager)
+GET_MANAGER_PTR (AudioManager)
+
+#endif //ENABLE_AUDIO
diff --git a/Runtime/Audio/AudioManager.h b/Runtime/Audio/AudioManager.h
new file mode 100644
index 0000000..6e0d185
--- /dev/null
+++ b/Runtime/Audio/AudioManager.h
@@ -0,0 +1,296 @@
+#ifndef AUDIOMANAGER_H
+#define AUDIOMANAGER_H
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Profiler/ProfilerStats.h"
+#include "Runtime/Misc/Player.h"
+#include "Runtime/Audio/AudioTypes.h"
+#include "Runtime/Mono/MonoIncludes.h"
+#if ENABLE_AUDIO_FMOD
+#include "Runtime/Audio/AudioScriptBufferManager.h"
+#endif
+
+#if ENABLE_AUDIO
+
+#if UNITY_FLASH || UNITY_WEBGL
+#include "PlatformDependent/FlashSupport/cpp/AudioChannel.h"
+#endif
+
+#ifndef FMOD_ASSERT
+#define FMOD_ASSERT(x) {Assert(x == FMOD_OK);\
+if (x != FMOD_OK){ ErrorString(FMOD_ErrorString(x));}}
+#endif
+
+
+#include "Runtime/Audio/correct_fmod_includer.h" // can't forward declare enums (@TODO use ints?)
+
+class AudioSource;
+class AudioListener;
+class AudioClip;
+class WWW;
+class AudioFilter;
+class AudioReverbZone;
+struct MonoDomain;
+struct MonoThread;
+#if ENABLE_AUDIO_FMOD
+namespace FMOD { class System; class Sound; class Channel; class ChannelGroup; }
+#endif
+namespace Unity { class GameObject; }
+
+
+enum{
+ kVelocityUpdateModeAuto = 0,
+ kVelocityUpdateModeFixed = 1,
+ kVelocityUpdateModeDynamic = 2,
+};
+
+
+class AudioManager : public GlobalGameManager
+{
+public:
+ REGISTER_DERIVED_CLASS (AudioManager, GlobalGameManager)
+ DECLARE_OBJECT_SERIALIZE (AudioManager)
+
+ AudioManager (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~AudioManager (); declared-by-macro
+
+#if UNITY_WII
+ void UpdateOnDiskEject();
+#endif
+ void Update ();
+ void FixedUpdate ();
+
+ void AddAudioSource (AudioSource* s, bool paused);
+ void RemoveAudioSource (AudioSource* s);
+ void StopSources();
+
+ void AddAudioListener (AudioListener* newListener);
+ void RemoveAudioListener (AudioListener* newListener);
+ AudioListener* GetAudioListener() const;
+
+
+
+ void SetVolume (float volume);
+ float GetVolume () const;
+ void SetPause (bool pause);
+ bool GetPause () const { return m_IsPaused; }
+ int GetAutomaticUpdateMode(Unity::GameObject *go);
+
+ // Manager implementation
+ void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ void UpdateListener(
+ const Vector3f& position,
+ const Vector3f& velocity,
+ const Vector3f& up,
+ const Vector3f& forward);
+
+ struct AudioScheduledSource : public ListElement
+ {
+ AudioScheduledSource(AudioSource* src) : source(src), time(0.0) {};
+ AudioSource* source;
+ double time;
+ };
+
+private:
+
+ float m_DefaultVolume;
+ float m_Volume;
+ float m_Rolloffscale;
+ bool m_IsPaused;
+
+ typedef List< ListNode<AudioSource> > TAudioSources;
+ typedef List< ListNode<AudioListener> > TAudioListeners;
+ typedef List<AudioScheduledSource> TScheduledSources;
+ typedef TAudioSources::iterator TAudioSourcesIterator;
+ typedef TAudioListeners::iterator TAudioListenersIterator;
+ typedef TScheduledSources::iterator TScheduledSourcesIterator;
+
+ TAudioSources m_Sources;
+ TAudioSources m_PausedSources;
+ TAudioListeners m_Listeners;
+ TScheduledSources m_ScheduledSources;
+
+ void ProcessScheduledSources();
+#if ENABLE_MICROPHONE
+ FMOD::Sound* CreateSound (int deviceID, int lengthSec, int frequency);
+#endif // ENABLE_MICROPHONE
+
+public:
+ float GetRolloffScale() const { return m_Rolloffscale; }
+ bool IsAudioDisabled() const { return m_DisableAudio; }
+
+ /// Schedule source to be played in sync. In this frame (if delay==0) or later
+ inline UInt64 GetAccumulatedPauseTicks() const { return m_accPausedTicks; }
+ void ScheduleSource(AudioSource* s, double time);
+ void UnScheduleSource(AudioSource* s);
+ double GetDSPTime() const;
+
+#if ENABLE_AUDIO_FMOD
+ void AddAudioReverbZone(AudioReverbZone* z);
+ void RemoveAudioReverbZone(AudioReverbZone* z);
+
+ // FMOD
+ bool ValidateFMODResult(FMOD_RESULT result, const char* errmsg);
+ bool InitFMOD();
+ void InitScriptBufferManager();
+ void ReloadFMODSounds();
+
+ bool InitNormal();
+#ifdef SUPPORT_REPRODUCE_LOG
+ bool InitReproduction();
+#endif
+
+ void CloseFMOD();
+ FMOD::System* GetFMODSystem() const { return m_FMODSystem;}
+ static FMOD_RESULT F_CALLBACK systemCallback(FMOD_SYSTEM* c_system, FMOD_SYSTEM_CALLBACKTYPE type, void* data1, void* data2);
+
+ // groups
+ FMOD::ChannelGroup* GetChannelGroup_FX_IgnoreVolume() const { return m_ChannelGroup_FX_IgnoreVolume; }
+ FMOD::ChannelGroup* GetChannelGroup_FX_UseVolume() const { return m_ChannelGroup_FX_UseVolume; }
+ FMOD::ChannelGroup* GetChannelGroup_NoFX_IgnoreVolume() const { return m_ChannelGroup_NoFX_IgnoreVolume; }
+ FMOD::ChannelGroup* GetChannelGroup_NoFX_UseVolume() const { return m_ChannelGroup_NoFX_UseVolume; }
+
+
+ // FMOD profiling
+ int GetMemoryAllocated() const;
+ float GetCPUUsage() const;
+ #if ENABLE_PROFILER
+ void GetProfilerData( AudioStats& audioStats );
+ #endif
+
+
+ FMOD::Sound* CreateFMODSound(const void* buffer, FMOD_CREATESOUNDEXINFO exInfo, FMOD_MODE mode, bool useHardwareDecoder);
+
+ #if UNITY_EDITOR
+ FMOD::Sound* CreateFMODSound(const std::string& path, bool threeD,
+ bool hardware, bool openOnly = false );
+ #endif
+
+
+ FMOD::Sound* CreateFMODSoundFromWWW(WWW* webStream,
+ bool threeD,
+ FMOD_SOUND_TYPE suggestedtype,
+ FMOD_SOUND_FORMAT format,
+ unsigned freq,
+ unsigned channels,
+ bool stream);
+
+ FMOD::Sound* CreateFMODSoundFromMovie(AudioClip* clip, bool threeD);
+
+ FMOD::Channel* GetFreeFMODChannel(FMOD::Sound* sound, bool paused = true);
+
+ unsigned GetPlayingChannelsForSound(FMOD::Sound* sound);
+ const std::string& GetLastError() const { return m_LastErrorString; }
+
+ FMOD_SPEAKERMODE GetSpeakerMode() const { return m_speakerMode; }
+ FMOD_SPEAKERMODE GetSpeakerModeCaps() const { return m_speakerModeCaps; }
+ void SetSpeakerMode(FMOD_SPEAKERMODE speakerMode);
+
+public:
+
+#if ENABLE_MICROPHONE
+ // Microphone
+ const std::vector<std::string> GetRecordDevices() const;
+ bool EndRecord(int deviceID);
+ PPtr<AudioClip> StartRecord(int deviceID, bool loop, int lengthSec, int frequency);
+ bool IsRecording(int deviceID) const;
+ unsigned GetRecordPosition(int deviceID) const;
+ int GetMicrophoneDeviceIDFromName(const std::string& name) const;
+ void GetDeviceCaps(int deviceID, int *minFreq, int *maxFreq) const;
+
+ mutable std::map<std::string, int> m_MicrophoneNameToIDMap;
+#endif // ENABLE_MICROPHONE
+
+
+
+private:
+ float m_SpeedOfSound;
+ float m_DopplerFactor;
+
+ typedef List< ListNode<AudioReverbZone> > TAudioReverbZones;
+ typedef TAudioReverbZones::iterator TAudioReverbZonesIterator;
+ TAudioReverbZones m_ReverbZones;
+
+ // FMOD
+ FMOD::System* m_FMODSystem;
+
+ /* +----------------+
+ * | NoFX_UseVolume |
+ * /+----------------+
+ * _+-------------------+/
+ * / | NoFX_IgnoreVolume |<---EditorChannel plays directly into this group
+ * +-----------+/ +-------------------+
+ * |FMOD Master| ...............
+ * +-----------+\ +------------------+ : AudioSource :
+ * ^ \_| FX_IgnoreVolume | __:__+-----+ :
+ * | +------------------+\ +--------------+ / : | Wet | :
+ * | \| FX_UseVolume |/ : +-----+ :
+ * | +--------------+\ : :
+ * Zone reverbs \__:__+-----+ :
+ * : | Dry | :
+ * : +-----+ :
+ * ...............
+ * The AudioSource's Wet and Dry groups can be attached to the Listener, IgnoreVolume, IgnoreVolNoFX, or ListenerNoFX groups depending on the combinations of the bypassEffects and bypassListenerEffects properties.
+ * Note that zone reverbs are applied by FMOD after the downmix of the master channel group, so in order to avoid reverb on previews the ignoreVolumeGroupNoFX uses FMOD's overrideReverbProperties to turn off the reverb.
+ */
+
+ FMOD::ChannelGroup* m_ChannelGroup_FMODMaster;
+
+ FMOD::ChannelGroup* m_ChannelGroup_FX_IgnoreVolume;
+ FMOD::ChannelGroup* m_ChannelGroup_FX_UseVolume;
+ FMOD::ChannelGroup* m_ChannelGroup_NoFX_IgnoreVolume;
+ FMOD::ChannelGroup* m_ChannelGroup_NoFX_UseVolume;
+
+ FMOD_SPEAKERMODE m_speakerMode; ///< TransferName{Default Speaker Mode} enum { Raw = 0, Mono = 1, Stereo = 2, Quad = 3, Surround = 4, Surround 5.1 = 5, Surround 7.1 = 6, Prologic DTS = 7 }
+ FMOD_SPEAKERMODE m_activeSpeakerMode;
+ FMOD_CAPS m_driverCaps;
+ FMOD_SPEAKERMODE m_speakerModeCaps;
+
+ // Phone DSP buffer size
+ int m_DSPBufferSize; ///< enum { Default = 0, Best latency = 256, Good latency = 512, Best performance = 1024 }
+
+ // error handling
+ std::string m_LastErrorString;
+ FMOD_RESULT m_LastFMODErrorResult;
+
+private:
+ AudioScriptBufferManager* m_ScriptBufferManager;
+
+public:
+ AudioScriptBufferManager& GetScriptBufferManager();
+ AudioScriptBufferManager* GetScriptBufferManagerPtr();
+
+#if UNITY_EDITOR
+public:
+ void PlayClip(AudioClip& clip, int startSample = 0, bool loop = false, bool twoD = true);
+ void StopClip(const AudioClip& clip);
+ void PauseClip(const AudioClip& clip);
+ void ResumeClip(const AudioClip& clip);
+ bool IsClipPlaying(const AudioClip& clip);
+ void StopAllClips();
+ float GetClipPosition(const AudioClip& clip);
+ unsigned int GetClipSamplePosition(const AudioClip& clip);
+ void SetClipSamplePosition(const AudioClip& clip, unsigned int iSamplePosition);
+ void LoopClip(const AudioClip& clip, bool loop);
+ void ListenerCheck();
+
+private:
+ FMOD::Channel* m_EditorChannel;
+#endif //UNITY_EDITOR
+#endif //ENABLE_AUDIO_FMOD
+ UInt64 m_accPausedTicks;
+ UInt64 m_pauseStartTicks;
+ bool m_DisableAudio; // Completely disable audio (in standalone builds only)
+};
+
+AudioManager& GetAudioManager ();
+AudioManager* GetAudioManagerPtr ();
+
+#endif // ENABLE_AUDIO
+#endif // AUDIOMANAGER_H
diff --git a/Runtime/Audio/AudioModule.cpp b/Runtime/Audio/AudioModule.cpp
new file mode 100644
index 0000000..e71aa12
--- /dev/null
+++ b/Runtime/Audio/AudioModule.cpp
@@ -0,0 +1,142 @@
+#include "UnityPrefix.h"
+#include "Runtime/Interfaces/IAudio.h"
+#include "AudioManager.h"
+#include "Runtime/Video/BaseVideoTexture.h"
+#include "Runtime/Video/MovieTexture.h"
+#include "Runtime/Audio/AudioClip.h"
+#include "Runtime/Export/WWW.h"
+#include "Runtime/Audio/AudioCustomFilter.h"
+
+class AudioModule : public IAudio
+{
+ virtual void SetPause( bool paused )
+ {
+ GetAudioManager().SetPause(paused);
+ }
+
+ virtual void FixedUpdate()
+ {
+ GetAudioManager().FixedUpdate();
+ }
+
+ virtual void Update()
+ {
+ GetAudioManager().Update();
+ }
+
+ virtual void StopVideoTextures()
+ {
+ BaseVideoTexture::StopVideoTextures();
+ }
+
+ virtual void PauseVideoTextures()
+ {
+ BaseVideoTexture::PauseVideoTextures();
+ }
+
+ virtual void UpdateVideoTextures()
+ {
+ BaseVideoTexture::UpdateVideoTextures();
+ }
+#if ENABLE_WWW
+#if ENABLE_MOVIES
+ virtual MovieTexture* CreateMovieTextureFromWWW(WWW& www)
+ {
+ MovieTexture* tex = NEW_OBJECT(MovieTexture);
+ tex->Reset();
+ tex->InitStream(&www);
+ return tex;
+ }
+#endif
+ virtual AudioClip* CreateAudioClipFromWWW(WWW& www, bool threeD, bool stream, FMOD_SOUND_TYPE audioType)
+ {
+ AudioClip* clip = NEW_OBJECT(AudioClip);
+
+ // only allow sample read if the security policy allows it
+ WWW::SecurityPolicy policy = www.GetSecurityPolicy();
+ if (policy != WWW::kSecurityPolicyAllowAccess)
+ clip->SetReadAllowed(false);
+
+ clip->Reset();
+ clip->Set3D(threeD);
+
+#if !UNITY_FLASH
+ if (!clip->InitStream(&www, NULL, stream, audioType))
+#else
+ if (!clip->InitStream(&www, NULL, stream))
+#endif
+
+ {
+ DestroySingleObject(clip);
+ return NULL;
+ }
+ return clip;
+ }
+#endif
+
+ virtual AudioClip* CreateAudioClipOGGFromWWW(WWW& www)
+ {
+#if ENABLE_AUDIO_FMOD
+ AudioClip* clip = NEW_OBJECT (AudioClip);
+ clip->Reset();
+
+ clip->SetName(GetLastPathNameComponent(www.GetUrl()).c_str());
+
+ if (!clip->SetAudioData(www.GetData(), www.GetSize(), false, false,true, false, FMOD_SOUND_TYPE_OGGVORBIS, FMOD_SOUND_FORMAT_PCM16))
+ {
+ DestroySingleObject(clip);
+ clip = NULL;
+ }
+ return clip;
+#else
+ return 0;
+#endif
+ }
+
+ virtual bool IsFormatSupportedByPlatform(const char* type)
+ {
+#if ENABLE_AUDIO_FMOD
+ return AudioClip::IsFormatSupportedByPlatform(type);
+#endif
+ }
+
+#if ENABLE_AUDIO_FMOD
+ virtual FMOD::DSP* GetOrCreateDSPFromCustomFilter(AudioCustomFilter* filter)
+ {
+ return filter->GetOrCreateDSP();
+ }
+
+ virtual AudioCustomFilter* CreateAudioCustomFilter(MonoBehaviour* mb)
+ {
+ return new AudioCustomFilter(mb);
+ }
+
+ virtual FMOD::DSP* GetDSPFromAudioCustomFilter(AudioCustomFilter* filter)
+ {
+ return filter->GetDSP();
+ }
+
+ virtual void SetBypassOnDSP(FMOD::DSP* dsp, bool state)
+ {
+ dsp->setBypass(state);
+ }
+#endif
+
+#if ENABLE_PROFILER
+ virtual void GetProfilerStats(AudioStats& stats)
+ {
+ GetAudioManager().GetProfilerData(stats);
+ }
+#endif
+
+ virtual void AudioManagerAwakeFromLoad(AwakeFromLoadMode mode)
+ {
+ GetAudioManager().AwakeFromLoad(mode);
+ }
+};
+
+
+IAudio* CreateAudioModule()
+{
+ return new AudioModule();
+} \ No newline at end of file
diff --git a/Runtime/Audio/AudioModule.jam b/Runtime/Audio/AudioModule.jam
new file mode 100644
index 0000000..6c36e8d
--- /dev/null
+++ b/Runtime/Audio/AudioModule.jam
@@ -0,0 +1,137 @@
+rule AudioModule_ReportCpp
+{
+ local audiosources =
+ AudioBehaviour.cpp
+ AudioBehaviour.h
+ AudioChorusFilter.cpp
+ AudioChorusFilter.h
+ AudioClip.Callbacks.cpp
+ AudioClip.cpp
+ AudioClip.h
+ AudioClip_Flash.h
+ AudioClip_FMOD.h
+ AudioClip_Flash.cpp
+ AudioDistortionFilter.cpp
+ AudioDistortionFilter.h
+ AudioEchoFilter.cpp
+ AudioEchoFilter.h
+ AudioHighPassFilter.cpp
+ AudioHighPassFilter.h
+ AudioListener.cpp
+ AudioListener.h
+ AudioLowPassFilter.cpp
+ AudioLowPassFilter.h
+ AudioManager.Callbacks.cpp
+ AudioManager.cpp
+ AudioManager.h
+ AudioParameters.h
+ AudioReverbFilter.cpp
+ AudioReverbFilter.h
+ AudioReverbZone.cpp
+ AudioReverbZone.h
+ AudioSource.Callbacks.cpp
+ AudioSource.cpp
+ AudioSource.h
+ AudioSourceFilter.cpp
+ AudioSourceFilter.h
+ OggReader.h
+ WavReader.h
+ Utilities/Conversion.h
+ AudioCustomFilter.cpp
+ AudioCustomFilter.h
+ AudioScriptBufferManager.h
+ AudioScriptBufferManager.cpp
+
+ AudioModuleRegistration.cpp
+ AudioModule.cpp
+ ;
+
+ local videosources =
+ MoviePlayback.cpp
+ MoviePlayback.h
+ MovieTexture.cpp
+ MovieTexture.h
+ BaseVideoTexture.h
+ BaseVideoTexture.cpp
+ VideoTexture.h
+ ;
+
+ local modulesources =
+ Runtime/Audio/$(audiosources)
+ Runtime/Video/$(videosources)
+ ;
+
+ if $(PLATFORM) in macosx32 macosx64 macosxppc
+ {
+ modulesources += PlatformDependent/OSX/VideoTexture.mm ;
+ }
+
+ if $(PLATFORM) in macosx32 macosx64 macosxppc iphone ipad iphonesimulator
+ {
+ modulesources += PlatformDependent/OSX/DecodeCVImageBufferFrame.cpp ;
+ }
+
+ # add other platforms here
+
+ return $(modulesources) ;
+}
+
+rule AudioModule_ReportTxt
+{
+ return
+ Runtime/Audio/ScriptBindings/AudioBindings.txt
+ Runtime/Video/ScriptBindings/MovieTextureBindings.txt
+ Runtime/Video/ScriptBindings/UnityEngineWebCamTexture.txt
+ ;
+}
+
+rule AudioModule_ReportIncludes
+{
+ return
+ Projects/PrecompiledHeaders/
+ External/Audio/FMOD/builds/macosx/include
+ External/Audio/common_ogg
+ ;
+}
+
+rule AudioModule_ReportLibraries
+{
+ local libs = ;
+ if $(currentTarget) in MacEditor MacStandalonePlayer
+ {
+ libs +=
+ External/Theora/libs/Release/libtheoradec.a
+ External/Audio/FMOD/builds/macosx/lib/libfmodex.a
+ ;
+ }
+ return $(libs) ;
+}
+
+rule AudioModule_ReportLinkFlags
+{
+ local flags = ;
+ if $(currentTarget) in MacEditor MacStandalonePlayer iPhonePlayer iPhoneSimulatorPlayer
+ {
+ flags +=
+ -framework CoreAudio
+ -framework CoreVideo
+ -framework QuickTime
+ -framework AudioUnit
+ -framework Carbon
+ -framework QTKit
+ ;
+ }
+ return $(flags) ;
+}
+
+
+rule AudioModule_Init
+{
+ OverrideModule Audio : GetModule_Cpp : byOverridingWithMethod : AudioModule_ReportCpp ;
+ OverrideModule Audio : GetModule_Txt : byOverridingWithMethod : AudioModule_ReportTxt ;
+ OverrideModule Audio : GetModule_Inc : byOverridingWithMethod : AudioModule_ReportIncludes ;
+ OverrideModule Audio : GetModule_Lib : byOverridingWithMethod : AudioModule_ReportLibraries ;
+ OverrideModule Audio : GetModule_Flags : byOverridingWithMethod : AudioModule_ReportLinkFlags ;
+}
+
+#RegisterModule Audio ;
diff --git a/Runtime/Audio/AudioModuleRegistration.cpp b/Runtime/Audio/AudioModuleRegistration.cpp
new file mode 100644
index 0000000..e43d694
--- /dev/null
+++ b/Runtime/Audio/AudioModuleRegistration.cpp
@@ -0,0 +1,65 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_AUDIO
+#include "Runtime/BaseClasses/ClassRegistration.h"
+#include "Runtime/Modules/ModuleRegistration.h"
+#include "Runtime/Interfaces/IAudio.h"
+
+static void RegisterAudioClasses (ClassRegistrationContext& context)
+{
+ REGISTER_CLASS (AudioManager)
+ REGISTER_CLASS (AudioListener)
+ REGISTER_CLASS (AudioSource)
+ REGISTER_CLASS (AudioClip)
+ REGISTER_CLASS (AudioBehaviour)
+
+#if ENABLE_AUDIO_FMOD
+ REGISTER_CLASS (AudioReverbFilter)
+ REGISTER_CLASS (AudioHighPassFilter)
+ REGISTER_CLASS (AudioChorusFilter)
+ REGISTER_CLASS (AudioReverbZone)
+ REGISTER_CLASS (AudioEchoFilter)
+ REGISTER_CLASS (AudioLowPassFilter)
+ REGISTER_CLASS (AudioDistortionFilter)
+ REGISTER_CLASS (AudioFilter)
+#endif
+
+#if ENABLE_MOVIES
+ REGISTER_CLASS (MovieTexture)
+#endif
+
+#if ENABLE_WEBCAM
+ REGISTER_CLASS (WebCamTexture)
+#endif
+
+}
+
+void ExportAudioBindings ();
+void ExportUnityEngineWebCamTexture ();
+void ExportMovieTextureBindings();
+
+static void RegisterAudioICallModule ()
+{
+#if !INTERNAL_CALL_STRIPPING
+ ExportAudioBindings ();
+ #if ENABLE_WEBCAM
+ ExportUnityEngineWebCamTexture ();
+ #endif
+ #if ENABLE_MOVIES
+ ExportMovieTextureBindings();
+ #endif
+#endif
+}
+
+extern "C" EXPORT_MODULE void RegisterModule_Audio ()
+{
+ ModuleRegistrationInfo info;
+ info.registerClassesCallback = &RegisterAudioClasses;
+#if ENABLE_MONO || UNITY_WINRT
+ info.registerIcallsCallback = &RegisterAudioICallModule;
+#endif
+ RegisterModuleInfo (info);
+
+ SetIAudio(CreateAudioModule());
+}
+#endif \ No newline at end of file
diff --git a/Runtime/Audio/AudioParameters.h b/Runtime/Audio/AudioParameters.h
new file mode 100644
index 0000000..e4b49be
--- /dev/null
+++ b/Runtime/Audio/AudioParameters.h
@@ -0,0 +1,48 @@
+#ifndef ___AUDIOPARAMETERS_H__
+#define ___AUDIOPARAMETERS_H__
+
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Audio/correct_fmod_includer.h"
+#include "Runtime/Math/AnimationCurve.h"
+
+enum RolloffMode { kRolloffLogarithmic=0, kRolloffLinear, kRolloffCustom };
+
+struct AudioParameters
+{
+ // Animated props
+ AnimationCurve panLevelCustomCurve;
+ AnimationCurve spreadCustomCurve;
+ AnimationCurve rolloffCustomCurve;
+
+ float insideConeAngle;
+ float outsideConeAngle;
+ float outsideConeVolume;
+
+ int priority;
+ float dopplerLevel;
+ float minDistance;
+ float maxDistance;
+ float pan;
+
+ float pitch;
+ float volume;
+
+ // rolloff
+ RolloffMode rolloffMode; ///< enum { kRolloffLogarithmic=0, kRolloffLinear, kRolloffCustom }
+
+ bool loop; // <-- this will be replaced by a loop node
+ bool mute;
+
+#if UNITY_WII
+ bool starving; // For streaming sounds, when data isn't coming due disk eject
+#endif
+
+ bool bypassEffects; // Bypass/ignore any applied effects from AudioSource
+ bool bypassListenerEffects; // Bypass/ignore any applied effects from AudioListener
+ bool bypassReverbZones; // Bypass/ignore any applied effects from reverb zones
+ bool ignoreListenerPause;
+};
+
+
+
+#endif // ___AUDIOPARAMETERS_H__
diff --git a/Runtime/Audio/AudioReverbFilter.cpp b/Runtime/Audio/AudioReverbFilter.cpp
new file mode 100644
index 0000000..fe1e34d
--- /dev/null
+++ b/Runtime/Audio/AudioReverbFilter.cpp
@@ -0,0 +1,194 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#if ENABLE_AUDIO_FMOD
+#include "AudioReverbFilter.h"
+#include "Runtime/Utilities/Utility.h"
+#include "AudioTypes.h"
+
+/* Inst Env Diffus Room RoomHF RmLF DecTm DecHF DecLF Refl RefDel Revb RevDel ModTm ModDp HFRef LFRef Diffus Densty FLAGS */
+#define FMOD_PRESET_DRUGGED { 0, 23, 0.50f, -1000, 0, 0, 8.39f, 1.39f, 1.0f, -115, 0.002f, 985, 0.030f, 0.250f, 0.00f, 5000.0f, 250.0f, 100.0f, 100.0f, 0x1f }
+#define FMOD_PRESET_DIZZY { 0, 24, 0.60f, -1000, -400, 0, 17.23f, 0.56f, 1.0f, -1713, 0.020f, -613, 0.030f, 0.250f, 0.310f, 5000.0f, 250.0f, 100.0f, 100.0f, 0x1f }
+#define FMOD_PRESET_PSYCHOTIC { 0, 25, 0.50f, -1000, -151, 0, 7.56f, 0.91f, 1.0f, -626, 0.020f, 774, 0.030f, 0.250f, 0.00f, 5000.0f, 250.0f, 100.0f, 100.0f, 0x1f }
+
+
+FMOD_REVERB_PROPERTIES ReverbPresets [] =
+{
+ FMOD_PRESET_OFF,
+ FMOD_PRESET_GENERIC,
+ FMOD_PRESET_PADDEDCELL,
+ FMOD_PRESET_ROOM,
+ FMOD_PRESET_BATHROOM,
+ FMOD_PRESET_LIVINGROOM,
+ FMOD_PRESET_STONEROOM,
+ FMOD_PRESET_AUDITORIUM,
+ FMOD_PRESET_CONCERTHALL,
+ FMOD_PRESET_CAVE,
+ FMOD_PRESET_ARENA,
+ FMOD_PRESET_HANGAR,
+ FMOD_PRESET_CARPETTEDHALLWAY,
+ FMOD_PRESET_HALLWAY,
+ FMOD_PRESET_STONECORRIDOR,
+ FMOD_PRESET_ALLEY,
+ FMOD_PRESET_FOREST,
+ FMOD_PRESET_CITY,
+ FMOD_PRESET_MOUNTAINS,
+ FMOD_PRESET_QUARRY,
+ FMOD_PRESET_PLAIN,
+ FMOD_PRESET_PARKINGLOT,
+ FMOD_PRESET_SEWERPIPE,
+ FMOD_PRESET_UNDERWATER,
+ FMOD_PRESET_DRUGGED,
+ FMOD_PRESET_DIZZY,
+ FMOD_PRESET_PSYCHOTIC
+};
+
+AudioReverbFilter::AudioReverbFilter (MemLabelId label, ObjectCreationMode mode) :
+Super(label, mode),
+m_DryLevel(0.0f), // Dry Level : Mix level of dry signal in output in mB. Ranges from -10000.0 to 0.0. Default is 0.
+m_Room(0.0f), // Room : Room effect level at low frequencies in mB. Ranges from -10000.0 to 0.0. Default is 0.0.
+m_RoomHF(0.0f), // Room HF : Room effect high-frequency level re. low frequency level in mB. Ranges from -10000.0 to 0.0. Default is 0.0.
+m_RoomRolloff(10.0f), // Room Rolloff : Like DS3D flRolloffFactor but for room effect. Ranges from 0.0 to 10.0. Default is 10.0
+m_DecayTime(1.0f), // Reverberation decay time at low-frequencies in seconds. Ranges from 0.1 to 20.0. Default is 1.0.
+m_DecayHFRatio(0.5f), // Decay HF Ratio : High-frequency to low-frequency decay time ratio. Ranges from 0.1 to 2.0. Default is 0.5.
+m_ReflectionsLevel(-10000.0f), // Early reflections level relative to room effect in mB. Ranges from -10000.0 to 1000.0. Default is -10000.0.
+m_ReverbLevel(0.0f), // Reverb : Late reverberation level relative to room effect in mB. Ranges from -10000.0 to 2000.0. Default is 0.0.
+m_ReverbDelay(0.04f), // Late reverberation delay time relative to first reflection in seconds. Ranges from 0.0 to 0.1. Default is 0.04.
+m_Diffusion(100.0f), // Reverberation diffusion (echo density) in percent. Ranges from 0.0 to 100.0. Default is 100.0.
+m_Density(100.0f), // Reverberation density (modal density) in percent. Ranges from 0.0 to 100.0. Default is 100.0.
+m_HFReference(5000.0f), // HF Reference : Reference high frequency in Hz. Ranges from 20.0 to 20000.0. Default is 5000.0.
+m_RoomLF(0.0f), // Room effect low-frequency level in mB. Ranges from -10000.0 to 0.0. Default is 0.0.
+m_LFReference(250.0f), // Reference low-frequency in Hz. Ranges from 20.0 to 1000.0. Default is 250.0.
+m_ReflectionsDelay(0.0f), // Late reverberation level relative to room effect in mB. Ranges from -10000.0 to 2000.0. Default is 0.0.
+m_ReverbPreset(27)
+{
+ m_Type = FMOD_DSP_TYPE_SFXREVERB;
+}
+
+AudioReverbFilter::~AudioReverbFilter()
+{}
+
+void AudioReverbFilter::AddToManager()
+{
+ Super::AddToManager();
+}
+
+void AudioReverbFilter::CheckConsistency()
+{
+ Super::CheckConsistency();
+ m_DryLevel = clamp(m_DryLevel,-10000.0f, 0.0f);
+ m_Room = clamp(m_Room,-10000.0f, 0.0f);
+ m_RoomHF = clamp(m_RoomHF,-10000.0f, 0.0f);
+ m_RoomRolloff = clamp(m_RoomRolloff,-10000.0f, 0.0f);
+ m_DecayTime = clamp(m_DecayTime,0.1f, 20.0f);
+ m_DecayHFRatio = clamp(m_DecayHFRatio, 0.1f, 2.0f);
+ m_ReflectionsLevel = clamp(m_ReflectionsLevel, -10000.0f, 1000.0f);
+ m_ReverbLevel = clamp(m_ReverbLevel, -10000.0f, 2000.0f);
+ m_ReverbDelay = clamp(m_ReverbDelay, 0.0f, 0.1f);
+ m_Diffusion = clamp(m_Diffusion, 0.0f, 100.0f);
+ m_Density = clamp(m_Density,0.0f, 100.0f);
+ m_HFReference = clamp(m_HFReference, 20.0f, 20000.0f);
+ m_RoomLF = clamp(m_RoomLF, -10000.0f, 0.0f);
+ m_LFReference = clamp(m_LFReference, 20.0f, 10000.0f);
+}
+
+void AudioReverbFilter::Reset()
+{
+ Super::Reset();
+
+ m_DryLevel = 0.0f;
+ m_Room = 0.0f;
+ m_RoomHF = 0.0f;
+ m_RoomRolloff = 10.0f;
+ m_DecayTime = 1.0f;
+ m_DecayHFRatio = 0.5f;
+ m_ReflectionsLevel = -10000.0f;
+ m_ReverbLevel = 0.0f;
+ m_ReverbDelay = 0.04f;
+ m_Diffusion = 100.0f;
+ m_Density = 100.0f;
+ m_HFReference = 5000.0f;
+ m_RoomLF = 0.0f;
+ m_LFReference = 250.0f;
+ m_ReflectionsDelay = 0.0f;
+ m_ReverbPreset = 27;
+}
+
+void AudioReverbFilter::Update()
+{
+ ChangeProperties();
+ if (m_DSP)
+ {
+ m_DSP->setParameter(FMOD_DSP_SFXREVERB_DRYLEVEL, m_DryLevel);
+ m_DSP->setParameter(FMOD_DSP_SFXREVERB_ROOM, m_Room);
+ m_DSP->setParameter(FMOD_DSP_SFXREVERB_ROOMHF, m_RoomHF);
+ //m_DSP->setParameter(FMOD_DSP_SFXREVERB_ROOMROLLOFFFACTOR, m_RoomRolloff);
+ m_DSP->setParameter(FMOD_DSP_SFXREVERB_DECAYTIME, m_DecayTime);
+ m_DSP->setParameter(FMOD_DSP_SFXREVERB_DECAYHFRATIO, m_DecayHFRatio);
+ m_DSP->setParameter(FMOD_DSP_SFXREVERB_REFLECTIONSLEVEL, m_ReflectionsLevel);
+ m_DSP->setParameter(FMOD_DSP_SFXREVERB_REFLECTIONSDELAY, m_ReflectionsDelay);
+ m_DSP->setParameter(FMOD_DSP_SFXREVERB_REVERBLEVEL, m_ReverbLevel);
+ m_DSP->setParameter(FMOD_DSP_SFXREVERB_REVERBDELAY, m_ReverbDelay);
+ m_DSP->setParameter(FMOD_DSP_SFXREVERB_DIFFUSION, m_Diffusion);
+ m_DSP->setParameter(FMOD_DSP_SFXREVERB_DENSITY, m_Density);
+ m_DSP->setParameter(FMOD_DSP_SFXREVERB_HFREFERENCE, m_HFReference);
+ m_DSP->setParameter(FMOD_DSP_SFXREVERB_ROOMLF, m_RoomLF);
+ m_DSP->setParameter(FMOD_DSP_SFXREVERB_LFREFERENCE, m_LFReference);
+ }
+}
+
+void AudioReverbFilter::ChangeProperties()
+{
+ if (m_ReverbPreset < 27)
+ {
+ FMOD_REVERB_PROPERTIES prop = ReverbPresets[m_ReverbPreset];
+ m_Room = prop.Room;
+ m_RoomHF = prop.RoomHF;
+ //m_RoomRolloff = prop.RoomRolloffFactor;
+ m_DecayTime = prop.DecayTime;
+ m_DecayHFRatio = prop.DecayHFRatio;
+ m_ReflectionsLevel = prop.Reflections;
+ m_ReverbLevel = prop.Reverb;
+ m_ReverbDelay = prop.ReverbDelay;
+ m_Diffusion = prop.Diffusion;
+ m_Density = prop.Density;
+ m_HFReference = prop.HFReference;
+ m_RoomLF = prop.RoomLF;
+ m_LFReference = prop.LFReference;
+ SetDirty();
+ }
+}
+
+void AudioReverbFilter::SetReverbPreset(const int reverbPreset)
+{
+ m_ReverbPreset = reverbPreset;
+ ChangeProperties();
+ Update();
+}
+
+template<class TransferFunc>
+void AudioReverbFilter::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER(m_DryLevel);
+ TRANSFER(m_Room);
+ TRANSFER(m_RoomHF);
+ TRANSFER(m_RoomRolloff);
+ TRANSFER(m_DecayTime);
+ TRANSFER(m_DecayHFRatio);
+ TRANSFER(m_ReflectionsLevel);
+ TRANSFER(m_ReverbLevel);
+ TRANSFER(m_ReverbDelay);
+ TRANSFER(m_Diffusion);
+ TRANSFER(m_Density);
+ TRANSFER(m_HFReference);
+ TRANSFER(m_RoomLF);
+ TRANSFER(m_LFReference);
+ TRANSFER(m_ReflectionsDelay);
+ TRANSFER(m_ReverbPreset);
+}
+
+
+IMPLEMENT_CLASS (AudioReverbFilter)
+IMPLEMENT_OBJECT_SERIALIZE (AudioReverbFilter)
+
+#endif //ENABLE_AUDI
diff --git a/Runtime/Audio/AudioReverbFilter.h b/Runtime/Audio/AudioReverbFilter.h
new file mode 100644
index 0000000..df2a7ba
--- /dev/null
+++ b/Runtime/Audio/AudioReverbFilter.h
@@ -0,0 +1,96 @@
+#ifndef __AUDIOREVERBFILTER_H__
+#define __AUDIOREVERBFILTER_H__
+
+#include "Configuration/UnityConfigure.h"
+#if ENABLE_AUDIO_FMOD
+#include "AudioSourceFilter.h"
+#include "Runtime/Utilities/Utility.h"
+
+/////@TODO: Why does this not have the same parameters as Reverb zone???
+
+class AudioReverbFilter : public AudioFilter
+{
+public:
+ REGISTER_DERIVED_CLASS (AudioReverbFilter, AudioFilter)
+ DECLARE_OBJECT_SERIALIZE (AudioReverbFilter)
+
+ AudioReverbFilter (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void CheckConsistency ();
+ virtual void Update();
+ virtual void AddToManager();
+ virtual void Reset();
+
+ float GetDryLevel() const { return m_DryLevel; }
+ void SetDryLevel(const float drylevel) { m_DryLevel = drylevel; Update(); SetDirty();}
+
+ float GetRoom() const { return m_Room; }
+ void SetRoom(const float room) { m_Room = room; Update(); SetDirty();}
+
+ float GetRoomHF() const { return m_RoomHF; }
+ void SetRoomHF(const float roomhf) { m_RoomHF = roomhf; Update(); SetDirty();}
+
+ float GetRoomLF() const { return m_RoomLF; }
+ void SetRoomLF(const float roomlf) { m_RoomLF = roomlf; Update(); SetDirty();}
+
+ float GetRoomRolloff() const { return m_RoomRolloff; }
+ void SetRoomRolloff(const float rolloffscale) { m_RoomRolloff = rolloffscale; Update(); SetDirty();}
+
+ float GetDecayTime() const { return m_DecayTime; }
+ void SetDecayTime(const float decayTime) { m_DecayTime = decayTime; Update(); SetDirty();}
+
+ float GetDecayHFRatio() const { return m_DecayHFRatio; }
+ void SetDecayHFRatio(const float decayHFRatio) { m_DecayHFRatio = decayHFRatio; Update(); SetDirty(); }
+
+ float GetReflectionsLevel() const { return m_ReflectionsLevel; }
+ void SetReflectionsLevel(const float reflectionsLevel) { m_ReflectionsLevel = reflectionsLevel; Update(); SetDirty(); }
+
+ float GetReflectionsDelay() const { return m_ReflectionsDelay; }
+ void SetReflectionsDelay(const float reflectionsDelay) { m_ReflectionsDelay = reflectionsDelay; Update(); SetDirty();}
+
+ float GetReverbLevel() const { return m_ReverbLevel; }
+ void SetReverbLevel(const float reverbLevel) { m_ReverbLevel = reverbLevel; Update(); SetDirty();}
+
+ float GetReverbDelay() const { return m_ReverbDelay; }
+ void SetReverbDelay(const float reverbDelay) { m_ReverbDelay = reverbDelay; Update(); SetDirty();}
+
+ float GetDiffusion() const { return m_Diffusion; }
+ void SetDiffusion(const float diffusion) { m_Diffusion = diffusion; Update(); SetDirty();}
+
+ float GetDensity() const { return m_Density; }
+ void SetDensity(const float density) { m_Density = density; Update(); SetDirty();}
+
+ float GetHFReference() const { return m_HFReference; }
+ void SetHFReference(const float hfReference) { m_HFReference = hfReference; Update(); SetDirty();}
+
+ float GetLFReference() const { return m_LFReference; }
+ void SetLFReference(const float lfReference) { m_LFReference = lfReference; Update(); SetDirty();}
+
+ int GetReverbPreset() const { return m_ReverbPreset; }
+ void SetReverbPreset(const int reverbPreset);
+
+private:
+ float m_DryLevel; // Dry Level : Mix level of dry signal in output in mB. Ranges from -10000.0 to 0.0. Default is 0.
+ float m_Room; // Room : Room effect level at low frequencies in mB. Ranges from -10000.0 to 0.0. Default is 0.0.
+ float m_RoomHF; // Room HF : Room effect high-frequency level re. low frequency level in mB. Ranges from -10000.0 to 0.0. Default is 0.0.
+ float m_RoomRolloff; // Room Rolloff : Like DS3D flRolloffFactor but for room effect. Ranges from 0.0 to 10.0. Default is 10.0
+ float m_DecayTime; // Reverberation decay time at low-frequencies in seconds. Ranges from 0.1 to 20.0. Default is 1.0.
+ float m_DecayHFRatio; // Decay HF Ratio : High-frequency to low-frequency decay time ratio. Ranges from 0.1 to 2.0. Default is 0.5.
+ float m_ReflectionsLevel; // Early reflections level relative to room effect in mB. Ranges from -10000.0 to 1000.0. Default is -10000.0.
+ float m_ReflectionsDelay; // Late reverberation level relative to room effect in mB. Ranges from -10000.0 to 2000.0. Default is 0.0.
+ float m_ReverbLevel; // Reverb : Late reverberation level relative to room effect in mB. Ranges from -10000.0 to 2000.0. Default is 0.0.
+ float m_ReverbDelay; // Late reverberation delay time relative to first reflection in seconds. Ranges from 0.0 to 0.1. Default is 0.04.
+ float m_Diffusion; // Reverberation diffusion (echo density) in percent. Ranges from 0.0 to 100.0. Default is 100.0.
+ float m_Density; // Reverberation density (modal density) in percent. Ranges from 0.0 to 100.0. Default is 100.0.
+ float m_HFReference; // HF Reference : Reference high frequency in Hz. Ranges from 20.0 to 20000.0. Default is 5000.0.
+ float m_RoomLF; // Room effect low-frequency level in mB. Ranges from -10000.0 to 0.0. Default is 0.0.
+ float m_LFReference; // Reference low-frequency in Hz. Ranges from 20.0 to 1000.0. Default is 250.0.
+ int m_ReverbPreset; ///< enum { Off = 0, Generic = 1, PaddedCell = 2, Room = 3, Bathroom = 4, Livingroom = 5, Stoneroom = 6, Auditorium = 7, Concerthall = 8, Cave = 9, Arena = 10, Hangar = 11, CarpettedHallway = 12, Hallway = 13, StoneCorridor = 14, Alley = 15, Forest = 16, City = 17, Mountains = 18, Quarry = 19, Plain = 20, Parkinglot = 21, Sewerpipe = 22, Underwater = 23, Drugged = 24, Dizzy = 25, Psychotic = 26, User = 27 }
+
+ void ChangeProperties();
+};
+
+#endif //ENABLE_AUDIO
+#endif // __AUDIOREVERBFILTER_H__
+
+
diff --git a/Runtime/Audio/AudioReverbZone.cpp b/Runtime/Audio/AudioReverbZone.cpp
new file mode 100644
index 0000000..b76080d
--- /dev/null
+++ b/Runtime/Audio/AudioReverbZone.cpp
@@ -0,0 +1,260 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#if ENABLE_AUDIO_FMOD
+#include "AudioReverbZone.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "AudioManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "AudioBehaviour.h"
+#include "Runtime/Audio/correct_fmod_includer.h"
+#include "Runtime/Utilities/Utility.h"
+#include "AudioTypes.h"
+
+extern FMOD_REVERB_PROPERTIES ReverbPresets [];
+
+AudioReverbZone::AudioReverbZone (MemLabelId label, ObjectCreationMode mode) :
+ Super(label, mode),
+ m_MinDistance(10.0f),
+ m_MaxDistance(15.0f),
+ m_FMODReverb(NULL),
+ m_ReverbPreset(1),
+ m_Node(this)
+{
+ ChangeProperties();
+}
+
+AudioReverbZone::~AudioReverbZone ()
+{
+ Cleanup();
+ GetAudioManager().RemoveAudioReverbZone(this);
+}
+
+void AudioReverbZone::Cleanup()
+{
+ if (m_FMODReverb)
+ {
+ m_FMODReverb->release();
+ m_FMODReverb = NULL;
+ }
+}
+
+void AudioReverbZone::VerifyValues()
+{
+ if (m_MinDistance < 0)
+ m_MinDistance = 0;
+ if (m_MaxDistance < m_MinDistance)
+ m_MaxDistance = m_MinDistance;
+ m_Room = clamp(m_Room, -10000, 0);
+ m_RoomHF = clamp(m_RoomHF, -10000, 0);
+ m_RoomLF = clamp(m_RoomLF, -10000, 0);
+ m_DecayTime = clamp(m_DecayTime, 0.1f, 20.0f);
+ m_DecayHFRatio = clamp(m_DecayHFRatio, 0.1f, 2.0f);
+ m_Reflections = clamp(m_Reflections, -10000, 1000);
+ m_ReflectionsDelay = clamp(m_ReflectionsDelay,0.0f, 0.3f);
+ m_Reverb = clamp(m_Reverb, -10000, 2000);
+ m_ReverbDelay = clamp(m_ReverbDelay, 0.0f, 0.1f);
+ m_HFReference = clamp(m_HFReference, 1000.0f, 20000.0f);
+ m_LFReference = clamp(m_LFReference, 20.0f, 1000.0f);
+ m_RoomRolloffFactor = clamp(m_RoomRolloffFactor, 0.0f, 10.0f);
+ m_Diffusion = clamp(m_Diffusion, 0.0f, 100.0f);
+ m_Density = clamp(m_Density, 0.0f, 100.0f);
+}
+
+
+void AudioReverbZone::Reset()
+{
+ Super::Reset();
+
+ m_MinDistance = 10.0f;
+ m_MaxDistance = 15.0f;
+ m_ReverbPreset = 1;
+
+ ChangeProperties();
+}
+
+void AudioReverbZone::CheckConsistency ()
+{
+ Super::CheckConsistency();
+ VerifyValues();
+}
+
+
+void AudioReverbZone::RemoveFromManager()
+{
+ if (m_FMODReverb)
+ m_FMODReverb->setActive(false);
+ GetAudioManager().RemoveAudioReverbZone(this);
+}
+
+
+void AudioReverbZone::AddToManager()
+{
+ Init();
+ GetAudioManager().AddAudioReverbZone(this);
+}
+
+void AudioReverbZone::Init()
+{
+ if (m_FMODReverb == NULL)
+ {
+ FMOD_RESULT result = GetAudioManager().GetFMODSystem()->createReverb( &m_FMODReverb );
+ if (FMOD_OK != result)
+ ErrorString(FMOD_ErrorString(result));
+ }
+
+ m_FMODReverb->setActive(true);
+
+ SetFMODValues();
+
+ // needed, otherwise sounds playing in the very first frame may not be connected to the master reverb dsp unit
+ GetAudioManager().GetFMODSystem()->update();
+}
+
+void AudioReverbZone::SetFMODValues ()
+{
+ if (m_FMODReverb)
+ {
+ Vector3f p = GetComponent(Transform).GetPosition();
+ FMOD_RESULT result;
+ result = m_FMODReverb->set3DAttributes(reinterpret_cast<FMOD_VECTOR*> (&p), m_MinDistance, m_MaxDistance);
+ Assert(FMOD_OK == result);
+ FMOD_REVERB_PROPERTIES prop = GetReverbProperty(m_ReverbPreset);
+ result = m_FMODReverb->setProperties(&prop);
+ Assert(FMOD_OK == result);
+ }
+}
+
+void AudioReverbZone::ChangeProperties()
+{
+ FMOD_REVERB_PROPERTIES prop = GetReverbProperty(m_ReverbPreset);
+ // read back values
+ m_Room = prop.Room;
+ m_RoomHF = prop.RoomHF;
+ m_DecayTime = prop.DecayTime;
+ m_DecayHFRatio = prop.DecayHFRatio;
+ m_Reflections = prop.Reflections;
+ m_ReflectionsDelay = prop.ReflectionsDelay;
+ m_Reverb = prop.Reverb;
+ m_ReverbDelay = prop.ReverbDelay;
+ m_HFReference = prop.HFReference;
+ m_RoomRolloffFactor = 0.0F;
+ m_Diffusion = prop.Diffusion;
+ m_Density = prop.Density;
+ m_RoomLF = prop.RoomLF;
+ m_LFReference = prop.LFReference;
+
+}
+
+void AudioReverbZone::AwakeFromLoad(AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad(mode);
+ ChangeProperties();
+ SetFMODValues();
+ SetDirty(); // ??
+}
+
+float AudioReverbZone::GetMinDistance() const
+{
+ return m_MinDistance;
+}
+
+float AudioReverbZone::GetMaxDistance() const
+{
+ return m_MaxDistance;
+}
+
+void AudioReverbZone::Update()
+{
+ Assert (m_FMODReverb);
+
+ // Position
+ Vector3f p = GetComponent(Transform).GetPosition();
+ FMOD_VECTOR& pos = UNITYVEC2FMODVEC(p);
+ m_FMODReverb->set3DAttributes(&pos, m_MinDistance, m_MaxDistance);
+}
+
+void AudioReverbZone::SetMinDistance(float minDistance)
+{
+ m_MinDistance = minDistance;
+ VerifyValues();
+ SetFMODValues();
+ SetDirty();
+}
+
+void AudioReverbZone::SetMaxDistance(float maxDistance)
+{
+ m_MaxDistance = maxDistance;
+ VerifyValues();
+ SetFMODValues();
+ SetDirty();
+}
+
+void AudioReverbZone::SetReverbPreset(int reverbPreset)
+{
+ if (m_ReverbPreset != reverbPreset)
+ {
+ m_ReverbPreset = reverbPreset;
+ ChangeProperties();
+ SetDirty();
+ }
+}
+
+FMOD_REVERB_PROPERTIES AudioReverbZone::GetReverbProperty(int preset)
+{
+ if (preset < 27)
+ return ReverbPresets [preset];
+ else
+ {
+ FMOD_REVERB_PROPERTIES prop = FMOD_PRESET_OFF;
+ prop.Room = m_Room; // room effect level (at mid frequencies)
+ prop.RoomHF = m_RoomHF; // relative room effect level at high frequencies
+ prop.DecayTime = m_DecayTime; // reverberation decay time at mid frequencies
+ prop.DecayHFRatio = m_DecayHFRatio; // high-frequency to mid-frequency decay time ratio
+ prop.Reflections = m_Reflections; // early reflections level relative to room effect
+ prop.ReflectionsDelay = m_ReflectionsDelay; // initial reflection delay time
+ prop.Reverb = m_Reverb; // late reverberation level relative to room effect
+ prop.ReverbDelay = m_ReverbDelay; // late reverberation delay time relative to initial reflection
+ prop.HFReference = m_HFReference; // reference high frequency (hz)
+ //prop.RoomRolloffFactor = m_RoomRolloffFactor; // like rolloffscale in global settings, but for reverb room size effect
+ prop.Diffusion = m_Diffusion; // Value that controls the echo density in the late reverberation decay
+ prop.Density = m_Density; // Value that controls the modal density in the late reverberation decay
+ prop.RoomLF = m_RoomLF;
+ prop.LFReference = m_LFReference; // reference low frequency (hz)
+ return prop;
+ }
+}
+
+
+int AudioReverbZone::GetReverbPreset() const
+{
+ return m_ReverbPreset;
+}
+
+
+template<class TransferFunc>
+void AudioReverbZone::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER(m_MinDistance);
+ TRANSFER(m_MaxDistance);
+ TRANSFER(m_ReverbPreset);
+ TRANSFER(m_Room); // room effect level (at mid frequencies)
+ TRANSFER(m_RoomHF); // relative room effect level at high frequencies
+ TRANSFER(m_DecayTime); // reverberation decay time at mid frequencies
+ TRANSFER(m_DecayHFRatio); // high-frequency to mid-frequency decay time ratio
+ TRANSFER(m_Reflections); // early reflections level relative to room effect
+ TRANSFER(m_ReflectionsDelay); // initial reflection delay time
+ TRANSFER(m_Reverb); // late reverberation level relative to room effect
+ TRANSFER(m_ReverbDelay); // late reverberation delay time relative to initial reflection
+ TRANSFER(m_HFReference); // reference high frequency (hz)
+ TRANSFER(m_RoomRolloffFactor); // like rolloffscale in global settings, but for reverb room size effect
+ TRANSFER(m_Diffusion); // Value that controls the echo density in the late reverberation decay
+ TRANSFER(m_Density); // Value that controls the modal density in the late reverberation decay
+ TRANSFER(m_LFReference); // reference low frequency (hz)
+ TRANSFER(m_RoomLF);
+}
+
+
+IMPLEMENT_CLASS (AudioReverbZone)
+IMPLEMENT_OBJECT_SERIALIZE (AudioReverbZone)
+#endif //ENABLE_AUDIO
diff --git a/Runtime/Audio/AudioReverbZone.h b/Runtime/Audio/AudioReverbZone.h
new file mode 100644
index 0000000..8e2c084
--- /dev/null
+++ b/Runtime/Audio/AudioReverbZone.h
@@ -0,0 +1,109 @@
+#ifndef __AUDIOREVERBZONE_H__
+#define __AUDIOREVERBZONE_H__
+
+#if ENABLE_AUDIO_FMOD
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Audio/correct_fmod_includer.h"
+#include "Runtime/Utilities/LinkedList.h"
+
+
+class AudioReverbZone : public Behaviour
+{
+public:
+ REGISTER_DERIVED_CLASS (AudioReverbZone, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (AudioReverbZone)
+
+ /**
+ * Construction/Destruction
+ **/
+ AudioReverbZone (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~AudioSource (); declared-by-macro
+
+ virtual void CheckConsistency ();
+ virtual void Update();
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+ virtual void AddToManager();
+ virtual void RemoveFromManager();
+
+ float GetMinDistance() const;
+ void SetMinDistance(float minDistance);
+
+ float GetMaxDistance() const;
+ void SetMaxDistance(float maxDistance);
+
+ void SetReverbPreset(int preset);
+ int GetReverbPreset() const;
+
+ void SetRoom(int room) { m_Room = room; VerifyValues(); SetFMODValues(); SetDirty(); }
+ int GetRoom() const { return m_Room; }
+ void SetRoomHF(int roomHF) { m_RoomHF = roomHF; VerifyValues(); SetFMODValues(); SetDirty(); }
+ int GetRoomHF() const { return m_RoomHF; }
+ void SetDecayTime(float decayTime) { m_DecayTime = decayTime; VerifyValues(); SetFMODValues(); SetDirty();}
+ float GetDecayTime() const { return m_DecayTime; }
+ void SetDecayHFRatio(float decayHFRatio) { m_DecayHFRatio = decayHFRatio; VerifyValues(); SetFMODValues(); SetDirty();}
+ float GetDecayHFRatio() const { return m_DecayHFRatio; }
+ void SetReflectionsDelay(float reflections) { m_ReflectionsDelay = (int)reflections; VerifyValues(); SetFMODValues(); SetDirty();}
+ void SetReflectionsDelay(int reflections) { m_ReflectionsDelay = reflections; VerifyValues(); SetFMODValues(); SetDirty();}
+ int GetReflectionsDelay() const { return (int)m_ReflectionsDelay; }
+ void SetReflections(int reflections) { m_Reflections = reflections; VerifyValues(); SetFMODValues(); SetDirty();}
+ int GetReflections() const { return m_Reflections; }
+ void SetReverb(int reverb) { m_Reverb = reverb; VerifyValues(); SetFMODValues(); SetDirty();}
+ int GetReverb() const { return m_Reverb; }
+ void SetReverbDelay(float reverbDelay) { m_ReverbDelay = reverbDelay; VerifyValues(); SetFMODValues(); SetDirty();}
+ int GetReverbDelay() const { return (int)m_ReverbDelay; }
+ void SetHFReference(float hfReference) { m_HFReference = hfReference; VerifyValues(); SetFMODValues(); SetDirty();}
+ float GetHFReference() const { return m_HFReference; }
+ void SetRoomRolloffFactor(float rolloffFactor) { m_RoomRolloffFactor = rolloffFactor; VerifyValues(); SetFMODValues(); SetDirty();}
+ float GetRoomRolloffFactor() const { return m_RoomRolloffFactor; }
+ void SetDiffusion(float diffusion) { m_Diffusion = diffusion; VerifyValues(); SetFMODValues(); SetDirty();}
+ float GetDiffusion() const { return m_Diffusion; }
+ void SetDensity(float density) { m_Density = density; VerifyValues(); SetFMODValues(); SetDirty();}
+ float GetDensity() const { return m_Density; }
+ void SetRoomLF(int roomLF) { m_RoomLF = roomLF; VerifyValues(); SetFMODValues(); SetDirty();}
+ int GetRoomLF() const { return m_RoomLF; }
+ void SetLFReference(float lfReference) { m_LFReference = lfReference; VerifyValues(); SetFMODValues(); SetDirty();}
+ float GetLFReference() const { return m_LFReference; }
+
+ void Cleanup();
+ void Init();
+
+ virtual void Reset();
+
+private:
+ ListNode<AudioReverbZone> m_Node;
+
+ void SetFMODValues ();
+ void VerifyValues();
+ void ChangeProperties();
+
+ float m_MinDistance;
+ float m_MaxDistance;
+ int m_ReverbPreset; ///< enum { Off = 0, Generic = 1, PaddedCell = 2, Room = 3, Bathroom = 4, Livingroom = 5, Stoneroom = 6, Auditorium = 7, Concerthall = 8, Cave = 9, Arena = 10, Hangar = 11, CarpettedHallway = 12, Hallway = 13, StoneCorridor = 14, Alley = 15, Forest = 16, City = 17, Mountains = 18, Quarry = 19, Plain = 20, Parkinglot = 21, Sewerpipe = 22, Underwater = 23, Drugged = 24, Dizzy = 25, Psychotic = 26, User = 27 }
+
+ int m_Room; // room effect level (at mid frequencies)
+ int m_RoomHF; // relative room effect level at high frequencies
+ int m_RoomLF; // relative room effect level at low frequencies
+
+ float m_DecayTime; // reverberation decay time at mid frequencies
+ float m_DecayHFRatio; // high-frequency to mid-frequency decay time ratio
+ int m_Reflections; // early reflections level relative to room effect
+ float m_ReflectionsDelay; // initial reflection delay time
+ int m_Reverb; // late reverberation level relative to room effect
+ float m_ReverbDelay; // late reverberation delay time relative to initial reflection
+ float m_HFReference; // reference high frequency (hz)
+ float m_LFReference; // reference low frequency (hz)
+ float m_RoomRolloffFactor; // like rolloffscale in global settings, but for reverb room size effect
+ float m_Diffusion; // Value that controls the echo density in the late reverberation decay
+ float m_Density; // Value that controls the modal density in the late reverberation decay
+
+
+
+ FMOD::Reverb* m_FMODReverb;
+
+ FMOD_REVERB_PROPERTIES GetReverbProperty(int preset);
+
+ friend class AudioManager;
+};
+
+#endif //ENABLE_AUDIO_FMOD
+#endif // __AUDIOREVERBZONE_H__
diff --git a/Runtime/Audio/AudioScriptBufferManager.cpp b/Runtime/Audio/AudioScriptBufferManager.cpp
new file mode 100644
index 0000000..4ec57da
--- /dev/null
+++ b/Runtime/Audio/AudioScriptBufferManager.cpp
@@ -0,0 +1,138 @@
+/*
+ * AudioScriptBufferManager.cpp
+ * AllTargets.workspace
+ *
+ * Created by Søren Christiansen on 8/22/11.
+ * Copyright 2011 Unity Technologies. All rights reserved.
+ *
+ */
+#include "UnityPrefix.h"
+#include "AudioScriptBufferManager.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+
+#if ENABLE_AUDIO_FMOD
+
+AudioScriptBufferManager::AudioScriptBufferManager(unsigned PCMArraySize, unsigned DSPFilterArraySize) :
+ m_PCMReadArray(SCRIPTING_NULL)
+ ,m_DSPFilterArray(SCRIPTING_NULL)
+ ,m_PCMReadArrayGCHandle(0)
+ ,m_DSPFilterArrayGCHandle(0)
+ ,m_DSPFilterArrayOrigLength(0)
+ ,m_PCMReadArrayOrigLength(0)
+{
+ Init(PCMArraySize, DSPFilterArraySize);
+}
+
+AudioScriptBufferManager::~AudioScriptBufferManager()
+{
+}
+
+void AudioScriptBufferManager::DidReloadDomain()
+{
+ Cleanup();
+ Init(m_PCMReadArrayOrigLength, m_DSPFilterArrayOrigLength);
+}
+
+void AudioScriptBufferManager::Init(unsigned PCMArraySize, unsigned DSPFilterArraySize)
+{
+ // Create shared MonoArray for callbacks and DSPs
+ m_PCMReadArrayOrigLength = PCMArraySize;
+ #if ENABLE_MONO
+ ScriptingClassPtr klass = mono_class_from_name (mono_get_corlib (), "System", "Single");
+ m_PCMReadArray = mono_array_new (mono_domain_get (), klass, m_PCMReadArrayOrigLength);
+ m_PCMReadArrayGCHandle = mono_gchandle_new((MonoObject*)m_PCMReadArray, 1);
+ #else
+ ScriptingClassPtr klass = GetScriptingTypeRegistry().GetType("System", "Single");
+ m_PCMReadArray = CreateScriptingArray<float>(klass, m_PCMReadArrayOrigLength);
+ m_PCMReadArrayGCHandle = scripting_gchandle_new(m_PCMReadArray);
+ #endif
+
+ m_DSPFilterArrayOrigLength = DSPFilterArraySize;
+ #if ENABLE_MONO
+ m_DSPFilterArray = mono_array_new (mono_domain_get (), klass, m_DSPFilterArrayOrigLength );
+ m_DSPFilterArrayGCHandle = mono_gchandle_new((MonoObject*)m_DSPFilterArray, 1);
+ #else
+ m_DSPFilterArray = CreateScriptingArray<float>(klass, m_DSPFilterArrayOrigLength);
+ m_DSPFilterArrayGCHandle = scripting_gchandle_new(m_DSPFilterArray);
+ #endif
+}
+
+void AudioScriptBufferManager::Cleanup()
+{
+ #if ENABLE_SCRIPTING
+ // Cleanup mono arrays
+ if (m_PCMReadArray)
+ {
+ #if ENABLE_MONO
+ PatchLength(m_PCMReadArray, m_PCMReadArrayOrigLength);
+ #endif
+ scripting_gchandle_free(m_PCMReadArrayGCHandle);
+ m_PCMReadArray = SCRIPTING_NULL;
+ m_PCMReadArrayGCHandle = 0;
+ }
+ if (m_DSPFilterArray)
+ {
+ #if ENABLE_MONO
+ PatchLength(m_DSPFilterArray, m_DSPFilterArrayOrigLength);
+ #endif
+ scripting_gchandle_free(m_DSPFilterArrayGCHandle);
+ m_DSPFilterArray = SCRIPTING_NULL;
+ m_DSPFilterArrayGCHandle = 0;
+ }
+ #endif
+}
+
+void AudioScriptBufferManager::GetPCMReadArray(unsigned length, ScriptingArrayPtr &array)
+{
+ #if ENABLE_SCRIPTING
+ Assert(length <= m_PCMReadArrayOrigLength);
+ unsigned curLength = GetScriptingArraySize(m_PCMReadArray);
+ if (length != curLength)
+ {
+ #if ENABLE_MONO
+ PatchLength(m_PCMReadArray, length);
+ #else
+ ScriptingClassPtr klass = GetMonoManager().GetCommonClasses().floatSingle;
+ array = CreateScriptingArray(Scripting::GetScriptingArrayStart<float>(m_PCMReadArray), length, klass);
+ return;
+ #endif
+ }
+ array = m_PCMReadArray;
+ #endif
+}
+
+void AudioScriptBufferManager::GetDSPFilterArray(unsigned length, ScriptingArrayPtr &array)
+{
+ #if ENABLE_SCRIPTING
+ Assert(length <= m_DSPFilterArrayOrigLength);
+ unsigned curLength = GetScriptingArraySize(m_DSPFilterArray);
+ if (length != curLength)
+ {
+ #if ENABLE_MONO
+ PatchLength(m_DSPFilterArray, length);
+ #else
+ ScriptingClassPtr klass = GetMonoManager().GetCommonClasses().floatSingle;
+ array = CreateScriptingArray(Scripting::GetScriptingArrayStart<float>(m_DSPFilterArray), length, klass);
+ return;
+ #endif
+ }
+ array = m_DSPFilterArray;
+ #endif
+}
+
+
+#if ENABLE_MONO
+
+void AudioScriptBufferManager::PatchLength(ScriptingArrayPtr array, unsigned newlength)
+{
+ char* pos = sizeof(uintptr_t)*3 + (char*)array;
+ *((UInt32*)pos) = (UInt32)newlength;
+}
+
+#endif
+
+#endif // ENABLE_AUDIO_FMOD
diff --git a/Runtime/Audio/AudioScriptBufferManager.h b/Runtime/Audio/AudioScriptBufferManager.h
new file mode 100644
index 0000000..12ef84d
--- /dev/null
+++ b/Runtime/Audio/AudioScriptBufferManager.h
@@ -0,0 +1,48 @@
+/*
+ * AudioScriptBufferManager.h
+ * AllTargets.workspace
+ *
+ * Created by Søren Christiansen on 8/22/11.
+ * Copyright 2011 Unity Technologies. All rights reserved.
+ *
+ */
+#ifndef UNITY_AUDIOSCRIPTBUFFERMANAGER_H
+#define UNITY_AUDIOSCRIPTBUFFERMANAGER_H
+
+#if ENABLE_AUDIO_FMOD
+
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+
+struct MonoArray;
+
+
+class AudioScriptBufferManager {
+public:
+ AudioScriptBufferManager(unsigned PCMArraySize, unsigned DSPFilterArraySize);
+ ~AudioScriptBufferManager();
+
+ void Init(unsigned PCMArraySize, unsigned DSPFilterArraySize);
+ void Cleanup();
+ void DidReloadDomain();
+
+ void GetDSPFilterArray(unsigned length, ScriptingArrayPtr &array);
+ void GetPCMReadArray(unsigned length, ScriptingArrayPtr &array);
+
+private:
+ ScriptingArrayPtr m_PCMReadArray;
+ int m_PCMReadArrayGCHandle;
+ unsigned m_PCMReadArrayOrigLength;
+
+ ScriptingArrayPtr m_DSPFilterArray;
+ int m_DSPFilterArrayGCHandle;
+ unsigned m_DSPFilterArrayOrigLength;
+
+ #if ENABLE_MONO
+ inline void PatchLength(ScriptingArrayPtr array, unsigned newlength);
+ #endif
+};
+
+#endif // ENABLE_AUDIO_FMOD
+
+#endif // UNITY_AUDIOSCRIPTBUFFERMANAGER_H
+
diff --git a/Runtime/Audio/AudioSource.Callbacks.cpp b/Runtime/Audio/AudioSource.Callbacks.cpp
new file mode 100644
index 0000000..246450d
--- /dev/null
+++ b/Runtime/Audio/AudioSource.Callbacks.cpp
@@ -0,0 +1,100 @@
+/*
+ * AudioSource.Callbacks.cpp
+ * Xcode
+ *
+ * Created by Søren Christiansen on 10/12/09.
+ * Copyright 2009 Unity Technologies. All rights reserved.
+ *
+ */
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#if ENABLE_AUDIO
+#include "AudioSource.h"
+#include "AudioManager.h"
+#include "Runtime/Utilities/Utility.h"
+
+/**
+ * Do log, linear and custom rolloff calculation to avoid interpolating the (animation)curve
+ **/
+float AudioSource::CalculateVolumeModifierForDistance(float distance)
+{
+ float maxDistance, minDistance, rolloffScale;
+ RolloffMode rolloffMode;
+
+ rolloffScale = GetAudioManager().GetRolloffScale();
+
+ maxDistance = GetMaxDistance();
+ minDistance = GetMinDistance();
+ rolloffMode = GetRolloffMode();
+
+ register float gain = 1.0f;
+
+ switch (rolloffMode) {
+ case kRolloffLinear:
+ {
+ float range = maxDistance-minDistance;
+ if (range<=0)
+ gain=1.0f;
+ else
+ {
+ distance = maxDistance - distance;
+ gain = distance / range;
+ }
+ }
+ break;
+ case kRolloffLogarithmic:
+ {
+ if ((distance > minDistance) && (rolloffScale != 1.0f))
+ {
+ distance -= minDistance;
+ distance *= rolloffScale;
+ distance += minDistance;
+ }
+
+ if (distance < .000001f)
+ {
+ distance = .000001f;
+ }
+ gain = minDistance / distance;
+ }
+ break;
+ case kRolloffCustom:
+ {
+ //@TODO: maxDistance can be 0.0F in that case the audio will play back incorrectly because gain becomes nan
+ if (maxDistance > 0.0F)
+ {
+ const AnimationCurve& curve = GetCustomRolloffCurve();
+ gain = curve.Evaluate(distance / maxDistance);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ Assert(IsFinite(gain));
+
+ if (gain < 0.0f)
+ gain = 0.0f;
+ if (gain > 1.0f)
+ gain = 1.0f;
+
+ return gain;
+}
+
+#if ENABLE_AUDIO_FMOD
+float F_CALLBACK AudioSource::rolloffCallback(
+ FMOD_CHANNEL * c_channel,
+ float distance)
+{
+ FMOD::Channel* channel = (FMOD::Channel*)c_channel;
+ AudioSource* audioSource = AudioSource::GetAudioSourceFromChannel(channel);
+
+ if (audioSource == NULL)
+ return 0.0f;
+
+ return audioSource->CalculateVolumeModifierForDistance(distance);
+}
+
+#endif
+#endif //ENABLE_AUDIO
diff --git a/Runtime/Audio/AudioSource.cpp b/Runtime/Audio/AudioSource.cpp
new file mode 100644
index 0000000..2128d43
--- /dev/null
+++ b/Runtime/Audio/AudioSource.cpp
@@ -0,0 +1,1738 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#if ENABLE_AUDIO
+#include "AudioSource.h"
+#include "AudioManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "AudioClip.h"
+#include "Runtime/Video/MoviePlayback.h"
+#include "Runtime/Dynamics/RigidBody.h"
+#include "Runtime/Animation/AnimationCurveUtility.h"
+#include "AudioListener.h"
+#include "AudioSourceFilter.h"
+#include "AudioLowPassFilter.h"
+#include "AudioEchoFilter.h"
+#include "AudioChorusFilter.h"
+#include "AudioCustomFilter.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/GameCode/CallDelayed.h"
+#include "Runtime/Audio/correct_fmod_includer.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Animation/AnimationCurveUtility.h"
+#include "Runtime/Interfaces/IPhysics.h"
+
+#if SUPPORT_REPRODUCE_LOG
+#include "Runtime/Misc/ReproductionLog.h"
+#include <fstream>
+#endif
+
+#define Unity_HiWord(x) ((UInt32)((UInt64)(x) >> 32))
+#define Unity_LoWord(x) ((UInt32)(x))
+
+inline void ReplaceOrAddSingleCurveValue(float value, AnimationCurve& curve)
+{
+ AnimationCurve::Keyframe key(0.0f,value);
+ curve.Assign(&key, &key + 1);
+}
+
+// ------------------------------------------------------------------------
+
+
+#if ENABLE_PROFILER
+int AudioSource::s_AudioSourceCount = 0;
+#endif
+
+
+AudioSource::AudioSource (MemLabelId label, ObjectCreationMode mode) :
+ Super(label, mode)
+ ,m_PlayOnAwake(true)
+ ,m_VelocityUpdateMode(kVelocityUpdateModeAuto)
+ ,m_LastUpdatePosition(Vector3f(0,0,0))
+ ,m_Channel(NULL)
+ ,m_Node (this)
+ ,m_IgnoreListenerVolume(false)
+ ,m_samplePosition(0)
+ ,m_pause(true)
+ ,m_ScheduledSource(this)
+ ,m_HasScheduledStartDelay(false)
+ ,m_HasScheduledEndDelay(false)
+#if ENABLE_AUDIO_FMOD
+ ,m_dryGroup(NULL)
+ ,m_wetGroup(NULL)
+ ,m_PlayingDSP(NULL)
+#endif
+{
+#if ENABLE_PROFILER
+ s_AudioSourceCount++;
+#endif
+
+ m_AudioParameters.pitch = 1.0;
+ m_AudioParameters.volume = 1.0f;
+ m_AudioParameters.priority = 128;
+ m_AudioParameters.loop = false;
+ m_AudioParameters.pan = 0.0f;
+ m_AudioParameters.dopplerLevel = 1.0f;
+ m_AudioParameters.minDistance = 1.0f;
+ m_AudioParameters.maxDistance = 500.0f;
+ m_AudioParameters.insideConeAngle = 360.0f;
+ m_AudioParameters.outsideConeAngle = 360.0f;
+ m_AudioParameters.outsideConeVolume = 1.0f;
+ m_AudioParameters.mute = false;
+ m_AudioParameters.rolloffMode = kRolloffLogarithmic;
+#if UNITY_WII
+ m_AudioParameters.starving = false;
+#endif
+ m_AudioParameters.bypassEffects = false;
+ m_AudioParameters.bypassListenerEffects = false;
+ m_AudioParameters.bypassReverbZones = false;
+ m_AudioParameters.ignoreListenerPause = false;
+ // curves
+ ReplaceOrAddSingleCurveValue(1.0f, m_AudioParameters.panLevelCustomCurve);
+ ReplaceOrAddSingleCurveValue(0.0f, m_AudioParameters.spreadCustomCurve);
+
+ if(GetAudioManagerPtr() != NULL)
+ AssignProps();
+}
+
+void AudioSource::Reset()
+{
+ Super::Reset();
+
+ m_AudioParameters.pitch = 1.0;
+ m_AudioParameters.volume = 1.0f;
+ m_AudioParameters.priority = 128;
+ m_AudioParameters.loop = false;
+ m_AudioParameters.pan = 0.0f;
+ m_AudioParameters.dopplerLevel = 1.0f;
+ m_AudioParameters.minDistance = 1.0f;
+ m_AudioParameters.maxDistance = 500.0f;
+ m_AudioParameters.insideConeAngle = 360.0f;
+ m_AudioParameters.outsideConeAngle = 360.0f;
+ m_AudioParameters.outsideConeVolume = 1.0f;
+ m_AudioParameters.mute = false;
+ m_AudioParameters.rolloffMode = kRolloffLogarithmic;
+ m_AudioParameters.bypassEffects = false;
+ m_AudioParameters.bypassListenerEffects = false;
+ m_AudioParameters.bypassReverbZones = false;
+ m_AudioParameters.ignoreListenerPause = false;
+
+ m_PlayOnAwake = true;
+
+ // Curves initial values will be handled in CheckConsistency
+ m_AudioParameters.rolloffCustomCurve.ResizeUninitialized (0);
+ m_AudioParameters.panLevelCustomCurve.ResizeUninitialized (0);
+ m_AudioParameters.spreadCustomCurve.ResizeUninitialized (0);
+ CheckConsistency ();
+}
+
+AudioSource::~AudioSource ()
+{
+#if ENABLE_PROFILER
+ s_AudioSourceCount--;
+#endif
+
+#if ENABLE_AUDIO_FMOD
+ TearDownGroups();
+
+ Assert (m_dryGroup == NULL);
+ Assert (m_wetGroup == NULL);
+#endif
+}
+
+void AudioSource::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ if (IsActive())
+ SetupGroups();
+
+ AssignProps();
+
+ if (IsActive() && awakeMode & (kDidLoadFromDisk | kInstantiateOrCreateFromCodeAwakeFromLoad | kActivateAwakeFromLoad))
+ m_LastUpdatePosition = GetComponent(Transform).GetPosition();
+
+ // This calls AddToManager which performs PlayOnAwake
+ // Thus we call it at the end after properties have been setup.
+ Super::AwakeFromLoad (awakeMode);
+}
+
+void AudioSource::CheckConsistency ()
+{
+ const float epsilon = 0.000001f;
+
+ m_AudioParameters.volume = clamp01(m_AudioParameters.volume);
+ m_AudioParameters.priority = clamp(m_AudioParameters.priority,0,255);
+ m_AudioParameters.pitch = clamp(m_AudioParameters.pitch,-3.0f,3.0f);
+ m_AudioParameters.dopplerLevel = clamp(m_AudioParameters.dopplerLevel,0.0f,5.0f);
+ m_AudioParameters.minDistance = m_AudioParameters.minDistance < 0.0f?0.0f:m_AudioParameters.minDistance;
+ m_AudioParameters.maxDistance = m_AudioParameters.maxDistance < m_AudioParameters.minDistance + epsilon ? m_AudioParameters.minDistance + epsilon : m_AudioParameters.maxDistance;
+
+ if (m_AudioParameters.rolloffCustomCurve.GetKeyCount() < 1)
+ {
+ m_AudioParameters.rolloffCustomCurve.AddKey(AnimationCurve::Keyframe(0.0f,1.0f));
+ m_AudioParameters.rolloffCustomCurve.AddKey(AnimationCurve::Keyframe(1.0f,0.0f));
+ }
+ if (m_AudioParameters.rolloffCustomCurve.GetKeyCount() == 1)
+ m_AudioParameters.rolloffCustomCurve.GetKey(0).value = clamp(m_AudioParameters.rolloffCustomCurve.GetKey(0).value, 0.0f, 1.0f);
+
+ if (m_AudioParameters.panLevelCustomCurve.GetKeyCount() < 1)
+ ReplaceOrAddSingleCurveValue(1.0f, m_AudioParameters.panLevelCustomCurve);
+ if (m_AudioParameters.panLevelCustomCurve.GetKeyCount() == 1)
+ m_AudioParameters.panLevelCustomCurve.GetKey(0).value = clamp(m_AudioParameters.panLevelCustomCurve.GetKey(0).value, 0.0f, 1.0f);
+
+ if (m_AudioParameters.spreadCustomCurve.GetKeyCount() < 1)
+ ReplaceOrAddSingleCurveValue(0.0f, m_AudioParameters.spreadCustomCurve);
+ if (m_AudioParameters.spreadCustomCurve.GetKeyCount() == 1)
+ m_AudioParameters.spreadCustomCurve.GetKey(0).value = clamp(m_AudioParameters.spreadCustomCurve.GetKey(0).value, 0.0f, 1.0f);
+}
+
+
+void AudioSource::AssignProps()
+{
+#if ENABLE_AUDIO_FMOD
+ SetOutsideConeAngle(m_AudioParameters.outsideConeAngle);
+ SetInsideConeAngle(m_AudioParameters.insideConeAngle);
+ SetOutsideConeVolume(m_AudioParameters.outsideConeVolume);
+ SetupGroups();
+ SetDopplerLevel(m_AudioParameters.dopplerLevel);
+ SetPitch(m_AudioParameters.pitch);
+#endif
+
+ SetPriority(m_AudioParameters.priority);
+ SetMinDistance(m_AudioParameters.minDistance);
+ SetMaxDistance(m_AudioParameters.maxDistance);
+ SetPan(m_AudioParameters.pan);
+
+ SetVolume(m_AudioParameters.volume);
+ SetLoop(m_AudioParameters.loop);
+ SetMute(m_AudioParameters.mute);
+
+ if (m_AudioClip && m_Channel)
+ m_Channel->setMode(m_AudioClip->Get3D()?FMOD_3D:FMOD_2D);
+
+ SetRolloffMode(m_AudioParameters.rolloffMode);
+}
+
+void AudioSource::CorrectScheduledTimeAfterUnpause(UInt64 delay)
+{
+#if ENABLE_AUDIO_FMOD
+ if(m_Channel != NULL)
+ {
+ unsigned hiclock, loclock;
+ if(m_HasScheduledStartDelay)
+ {
+ m_Channel->getDelay(FMOD_DELAYTYPE_DSPCLOCK_START, &hiclock, &loclock);
+ FMOD_64BIT_ADD(hiclock, loclock, Unity_HiWord(delay), Unity_LoWord(delay));
+ m_Channel->setDelay(FMOD_DELAYTYPE_DSPCLOCK_START, hiclock, loclock);
+ }
+ if(m_HasScheduledEndDelay)
+ {
+ m_Channel->getDelay(FMOD_DELAYTYPE_DSPCLOCK_END, &hiclock, &loclock);
+ FMOD_64BIT_ADD(hiclock, loclock, Unity_HiWord(delay), Unity_LoWord(delay));
+ m_Channel->setDelay(FMOD_DELAYTYPE_DSPCLOCK_END, hiclock, loclock);
+ }
+ }
+#endif
+}
+
+
+void AudioSource::SetAudioClip(AudioClip *clip)
+{
+ if(m_AudioClip == PPtr<AudioClip> (clip))
+ return;
+ Stop(true);
+ m_AudioClip = clip;
+ SetDirty ();
+}
+
+void AudioSource::Deactivate (DeactivateOperation operation)
+{
+ // We don't want audio to stop playing when in loading another level,
+ // so we just ignore the ignore deactivate completely
+ if (operation != kDeprecatedDeactivateToggleForLevelLoad)
+ {
+ Super::Deactivate (operation);
+ TearDownGroups ();
+ }
+}
+
+/**
+ * Setup effect and non-effect groups
+ **/
+void AudioSource::SetupGroups()
+{
+#if ENABLE_AUDIO_FMOD
+ Assert (GetAudioManagerPtr());
+
+ FMOD_RESULT result;
+
+ if (!m_dryGroup)
+ {
+ result = GetAudioManager().GetFMODSystem()->createChannelGroup("Dry Group", &m_dryGroup);
+ FMOD_ASSERT(result);
+
+ }
+ if (!m_wetGroup)
+ {
+ result = GetAudioManager().GetFMODSystem()->createChannelGroup("Wet Group", &m_wetGroup);
+ FMOD_ASSERT(result);
+ }
+
+ Assert(m_wetGroup);
+ Assert(m_dryGroup);
+
+ FMOD::ChannelGroup* newParentGroup;
+ if(m_AudioParameters.bypassListenerEffects)
+ newParentGroup = (m_IgnoreListenerVolume) ? GetAudioManager().GetChannelGroup_NoFX_IgnoreVolume() : GetAudioManager().GetChannelGroup_NoFX_UseVolume();
+ else
+ newParentGroup = (m_IgnoreListenerVolume) ? GetAudioManager().GetChannelGroup_FX_IgnoreVolume() : GetAudioManager().GetChannelGroup_FX_UseVolume();
+
+ if(m_AudioParameters.bypassEffects)
+ {
+ // Connect dry group directly to new parent group (thus bypassing wet group)
+ FMOD_RESULT result;
+ FMOD::ChannelGroup* parentGroup;
+ result = m_dryGroup->getParentGroup(&parentGroup); FMOD_ASSERT(result);
+ if(parentGroup != newParentGroup)
+ {
+ result = newParentGroup->addGroup(m_dryGroup); FMOD_ASSERT(result);
+ }
+ FMOD::DSP* dsphead = NULL;
+ result = m_wetGroup->getDSPHead(&dsphead); FMOD_ASSERT(result);
+ result = dsphead->disconnectAll(false, true); FMOD_ASSERT(result);
+ }
+ else
+ {
+ // Connect dry group to wet group (where DSP effects are attached) and add wet group to new parent group
+ FMOD_RESULT result;
+ FMOD::ChannelGroup* parentGroup;
+ result = m_dryGroup->getParentGroup(&parentGroup); FMOD_ASSERT(result);
+ if(parentGroup != m_wetGroup)
+ {
+ result = m_wetGroup->addGroup(m_dryGroup); FMOD_ASSERT(result);
+ }
+ // addGroup must be called in any case since getParentGroup will still return old parent group even though the DSP is no longer connected (and is not visible in the FMOD profiler)
+ result = newParentGroup->addGroup(m_wetGroup); FMOD_ASSERT(result);
+ }
+
+ FMOD_REVERB_CHANNELPROPERTIES rev;
+ memset(&rev, 0, sizeof(rev));
+ if(m_AudioParameters.bypassReverbZones)
+ rev.Room = -10000;
+ if(m_Channel != NULL)
+ {
+ result = m_Channel->setReverbProperties(&rev);
+ //FMOD_ASSERT(result);
+ }
+ for(TOneShots::iterator it = m_OneShots.begin(); it != m_OneShots.end(); ++it)
+ {
+ FMOD::Channel* channel = (*it)->channel;
+ if(channel != NULL)
+ {
+ result = channel->setReverbProperties(&rev);
+ //FMOD_ASSERT(result);
+ }
+ }
+
+#endif //ENABLE_AUDIO_FMOD
+}
+
+/**
+ * tear down effect and non-effect groups
+ **/
+void AudioSource::TearDownGroups()
+{
+#if ENABLE_AUDIO_FMOD
+ FMOD_RESULT result;
+
+ if (m_dryGroup)
+ {
+ result = m_dryGroup->release();
+ FMOD_ASSERT(result);
+ m_dryGroup = NULL;
+ }
+ if (m_wetGroup)
+ {
+ result = m_wetGroup->release();
+ FMOD_ASSERT(result);
+ m_wetGroup = NULL;
+ }
+#endif
+}
+
+void AudioSource::Play(double time)
+{
+ if(GetAudioManager().IsAudioDisabled())
+ return;
+
+ if (! (GetEnabled() && IsActive()) )
+ {
+ WarningStringObject("Can not play a disabled audio source", this);
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ return;
+ }
+
+ SetupGroups();
+
+ /**
+ * Play/unpause main clip, if any
+ **/
+ AudioClip* clip = m_AudioClip;
+ if (clip != NULL && !clip->ReadyToPlay())
+ return;
+
+ if (m_Channel)
+ {
+ FMOD_RESULT result;
+
+#if ENABLE_AUDIO_FMOD
+ result = m_Channel->setChannelGroup(m_dryGroup);
+#endif
+
+ bool paused;
+ result = m_Channel->getPaused(&paused);
+ if (result == FMOD_OK && paused)
+ {
+ AssignProps();
+ paused = IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a3) && GetAudioManager().GetPause() && !m_AudioParameters.ignoreListenerPause; // overwrite to restart sound
+ m_Channel->setPaused(paused);
+ GetAudioManager ().AddAudioSource (this, paused); // to make sure source is put into active or paused sources lists
+ return;
+ }
+ else
+ Stop(!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a1));
+ }
+
+ if (!m_Channel)
+ {
+ if (clip != NULL)
+ m_Channel = clip->CreateChannel(this);
+ else
+ {
+ #if ENABLE_AUDIO_FMOD
+ TFilters filters;
+ if ( GetFilterComponents(filters, true) )
+ {
+ AudioCustomFilter* customFilter = NULL;
+ filters[0]->getUserData((void**)&customFilter);
+ if (customFilter)
+ {
+ customFilter->SetPlayingSource(this);
+ m_PlayingDSP = filters[0];
+ m_PlayingDSP->remove();
+ FMOD_RESULT result = GetAudioManager().GetFMODSystem()->playDSP(FMOD_CHANNEL_FREE, filters[0], true, &m_Channel);
+ FMOD_ASSERT( result );
+ result = m_Channel->setMode ( FMOD_3D );
+ FMOD_ASSERT ( result );
+ }
+ else
+ WarningString(Format("Only custom filters can be played. Please add a custom filter or an audioclip to the audiosource (%s).", GetGameObjectPtr()?GetGameObjectPtr()->GetName():""));
+ }
+ #endif
+ }
+ }
+
+ if (m_Channel)
+ {
+ FMOD_RESULT result;
+
+#if ENABLE_AUDIO_FMOD
+ result = m_Channel->setChannelGroup(m_dryGroup);
+ ApplyFilters();
+#endif
+ m_Channel->setUserData(this);
+ AssignProps();
+ UpdateParameters(m_Channel);
+
+#if ENABLE_MOVIES
+ // is this a movie playback - note movieplayback (this is *so* ugly)
+ if ( clip&&clip->GetMovie() )
+ clip->GetMovie()->SetAudioChannel(m_Channel);
+#endif
+ // set cached position
+ m_Channel->setPosition(m_samplePosition, FMOD_TIMEUNIT_PCM);
+
+ // play in sync (this will add it to the manager as well)
+ GetAudioManager().ScheduleSource(this, time);
+
+ m_pause = false;
+ bool paused = IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a3) && GetAudioManager().GetPause() && !m_AudioParameters.ignoreListenerPause;
+ m_Channel->setPaused(paused);
+ GetAudioManager ().AddAudioSource (this, paused); // to make sure source is put into active or paused sources lists
+ }
+}
+
+void AudioSource::Pause()
+{
+ m_pause = true;
+ if (m_Channel)
+ {
+ m_Channel->setPaused(true);
+ }
+}
+void AudioSource::PauseOneShots ()
+{
+ // pause one shots
+ TOneShots::iterator it = m_OneShots.begin();
+ for (; it != m_OneShots.end();++it)
+ {
+ OneShot& os = **it;
+ os.channel->setPaused(true);
+ }
+}
+
+void AudioSource::ResumeOneShots ()
+{
+ /**
+ * play paused oneshots
+ **/
+ // play one shots
+ TOneShots::iterator it = m_OneShots.begin();
+ for (; it != m_OneShots.end();++it)
+ {
+ OneShot& os = **it;
+ os.channel->setPaused(IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a3) && GetAudioManager().GetPause() && !m_AudioParameters.ignoreListenerPause);
+ }
+}
+
+void AudioSource::Cleanup()
+{
+ Stop(true);
+#if ENABLE_AUDIO_FMOD
+ // cleanup filters
+ const GameObject* go = GetGameObjectPtr();
+
+ if (go)
+ {
+ for (int i=0;i<go->GetComponentCount();i++)
+ {
+ AudioFilter* filter = NULL;
+ filter = dynamic_pptr_cast<AudioFilter*> (&go->GetComponentAtIndex(i));
+ if ( filter )
+ filter->Cleanup();
+ else
+ {
+ MonoBehaviour* behaviour = dynamic_pptr_cast<MonoBehaviour*> (&go->GetComponentAtIndex(i));
+ if ( behaviour )
+ {
+ AudioCustomFilter* filter = behaviour->GetAudioCustomFilter();
+ if ( filter ) filter->Cleanup();
+ }
+ }
+ }
+ }
+
+ TearDownGroups();
+
+#endif
+}
+
+
+
+void AudioSource::Stop(bool stopOneShots)
+{
+ m_HasScheduledStartDelay = false;
+ m_HasScheduledEndDelay = false;
+
+ if (m_Channel)
+ m_Channel->stop();
+
+ m_Channel = NULL;
+
+ if(stopOneShots)
+ {
+ // stop one shots (and delete them from the list)
+ TOneShots::iterator it = m_OneShots.begin();
+ for (; it != m_OneShots.end(); ++it)
+ {
+ OneShot* os = *it;
+ os->channel->stop();
+ delete os;
+ }
+ m_OneShots.clear();
+ }
+
+#if ENABLE_AUDIO_FMOD
+ if (m_PlayingDSP)
+ m_PlayingDSP->remove();
+
+ AudioCustomFilter* filter = NULL;
+ m_PlayingDSP->getUserData((void**)&filter);
+ if (filter)
+ filter->SetPlayingSource(NULL);
+
+ m_PlayingDSP = NULL;
+#endif
+
+ if(m_OneShots.empty())
+ {
+#if ENABLE_AUDIO_FMOD
+ // remove filters
+ TFilters filters;
+ if (GetFilterComponents(filters, false))
+ {
+ for (TFilters::const_iterator it=filters.begin();it!=filters.end();it++)
+ {
+ FMOD::DSP* dsp = *it;
+ FMOD_RESULT result;
+ result = dsp->remove();
+ FMOD_ASSERT(result);
+ }
+
+ filters.clear();
+ }
+#endif
+
+ GetAudioManager ().RemoveAudioSource (this);
+ }
+}
+
+
+void AudioSource::Stop(AudioChannel* channel)
+{
+ if (m_Channel == channel)
+ {
+ m_HasScheduledStartDelay = false;
+ m_HasScheduledEndDelay = false;
+
+ m_Channel->stop();
+ m_Channel = NULL;
+
+#if ENABLE_AUDIO_FMOD
+ if (m_PlayingDSP)
+ m_PlayingDSP->remove();
+
+ AudioCustomFilter* filter = NULL;
+ m_PlayingDSP->getUserData((void**)&filter);
+ if (filter)
+ filter->SetPlayingSource(NULL);
+ m_PlayingDSP = NULL;
+#endif
+ }
+ else
+ {
+ // stop one shots (and delete them from the list)
+ TOneShots::iterator it = m_OneShots.begin();
+ for (; it != m_OneShots.end(); ++it)
+ {
+ OneShot* os = *it;
+ if(os->channel == channel)
+ {
+ m_OneShots.erase(it);
+ os->channel->stop();
+ delete os;
+ break;
+ }
+ }
+ }
+
+ if(m_Channel == NULL && m_OneShots.empty())
+ {
+#if ENABLE_AUDIO_FMOD
+ // remove filters
+ TFilters filters;
+ if (GetFilterComponents(filters, false))
+ {
+ for (TFilters::const_iterator it=filters.begin();it!=filters.end();it++)
+ {
+ FMOD::DSP* dsp = *it;
+ FMOD_RESULT result;
+ result = dsp->remove();
+ FMOD_ASSERT(result);
+ }
+
+ filters.clear();
+ }
+#endif
+ GetAudioManager ().RemoveAudioSource (this);
+ }
+}
+
+
+bool AudioSource::IsPlayingScripting ()
+{
+ bool res = IsPlaying ();
+#if SUPPORT_REPRODUCE_LOG
+ if (GetReproduceOutStream())
+ *GetReproduceOutStream() << "Audio_IsPlaying " << (int)res << std::endl;
+ else if (GetReproduceInStream() && GetReproduceVersion() >= 4)
+ {
+ if (!CheckReproduceTag("Audio_IsPlaying", *GetReproduceInStream()))
+ {
+ ErrorString("Grabbing Audio.IsPlaying but there are Audio_IsPlaying calls recorded");
+ return res;
+ }
+
+ int temp = 0;
+ *GetReproduceInStream() >> temp;
+ res = temp;
+ }
+#endif
+ return res;
+}
+
+bool AudioSource::IsPlaying () const
+{
+ if (m_Channel)
+ {
+ FMOD_RESULT result;
+
+ bool isPlaying, isPaused;
+
+ result = m_Channel->isPlaying(&isPlaying);
+ result = m_Channel->getPaused(&isPaused);
+
+ return (result == FMOD_OK) && ((!m_pause) || ((isPlaying) && (!isPaused)));
+
+ }
+ return false;
+}
+
+bool AudioSource::IsPaused () const
+{
+ bool isPaused = false;
+ if (m_Channel)
+ m_Channel->getPaused(&isPaused);
+ return isPaused;
+}
+
+
+/**
+ * Update channel properties
+ * @param channel The channel to update
+ * @param oneshot Is this a oneshot?
+ **/
+
+inline bool AudioSource::UpdateParameters(AudioChannel* channel, OneShot* oneshot /* = NULL */)
+{
+ // is this a valid channel?
+ bool isPlaying = false;
+
+ FMOD_RESULT result = channel==NULL?FMOD_ERR_INVALID_HANDLE:channel->isPlaying(&isPlaying);
+
+ if (result == FMOD_ERR_INVALID_HANDLE)
+ return false;
+
+ // Position
+ Vector3f p = GetComponent(Transform).GetPosition();
+
+ // Velocity
+ // get velocity from rigidbody if present
+ Vector3f v;
+#if ENABLE_PHYSICS
+ Rigidbody* rb = QueryComponent(Rigidbody);
+ if (rb)
+ v = GetIPhysics()->GetRigidBodyVelocity(*rb);
+ else
+#endif
+ v = (p - m_LastUpdatePosition) * GetInvDeltaTime ();
+
+ // Orientate cone
+ Vector3f orient = NormalizeSafe( QuaternionToEuler( GetComponent(Transform).GetRotation() ) );
+
+ channel->set3DConeOrientation(reinterpret_cast<FMOD_VECTOR*> (&orient));
+
+
+#if ENABLE_AUDIO_FMOD
+ FMOD_VECTOR& pos = UNITYVEC2FMODVEC(p);
+ FMOD_VECTOR& vel = UNITYVEC2FMODVEC(v);
+ // Update position and velocity
+ channel->set3DAttributes(&pos,&vel);
+#endif
+ // update distance animated props
+ // distance to listener
+ // @TODO refactor when we support more than one listener
+ const AudioListener* listener = GetAudioManager().GetAudioListener();
+ if (listener != NULL)
+ {
+ const Vector3f& position = listener->GetPosition();
+ float distance = Magnitude(p - position);
+
+ // Pan Level
+ const AnimationCurve& curve = m_AudioParameters.panLevelCustomCurve;
+ if (curve.GetKeyCount() != 1)
+ {
+ std::pair<float, float> curverange = curve.GetRange();
+ float panLevel = curve.Evaluate(clamp(distance / m_AudioParameters.maxDistance, curverange.first, curverange.second));
+ channel->set3DPanLevel(panLevel);
+ }
+ else
+ {
+ channel->set3DPanLevel(curve.GetKey(0).value);
+ }
+
+#if UNITY_FLASH || UNITY_WEBGL
+ if (GetAudioClip()->Get3D())
+ channel->SetDistanceVolumeMultiplier(CalculateVolumeModifierForDistance(distance));
+#endif
+
+ // Spread
+ if (!m_AudioClip.IsNull())
+ {
+
+ const AnimationCurve& curve = m_AudioParameters.spreadCustomCurve;
+ if (curve.GetKeyCount() != 1)
+ {
+ std::pair<float, float> curverange = curve.GetRange();
+ float spread = curve.Evaluate(clamp(distance / m_AudioParameters.maxDistance, curverange.first, curverange.second));
+ channel->set3DSpread(spread * 360.0f);
+ }
+ else
+ {
+ channel->set3DSpread(curve.GetKey(0).value * 360.0f);
+ }
+ }
+
+#if ENABLE_AUDIO_FMOD
+ // Low pass filter
+ AudioLowPassFilter* lowPassFilter = QueryComponent(AudioLowPassFilter);
+ if (lowPassFilter != NULL)
+ {
+ const AnimationCurve& curve = lowPassFilter->GetCustomLowpassLevelCurve();
+ // Only use the curve if there's more than 1 key
+ if (curve.GetKeyCount() > 1)
+ {
+ std::pair<float, float> curverange = curve.GetRange();
+ float lowpassLevel = curve.Evaluate(clamp(distance / m_AudioParameters.maxDistance, curverange.first, curverange.second));
+ lowpassLevel = 22000.0f - (21990.0f*lowpassLevel);
+ lowPassFilter->SetCutoffFrequency(lowpassLevel);
+ }
+ }
+#endif
+ }
+
+ // update last position
+ m_LastUpdatePosition = p;
+ return true;
+}
+
+void AudioSource::DoUpdate()
+{
+ if (! (GetEnabled() && IsActive() ) )
+ return;
+
+#if UNITY_WII
+ // Check if we're not starving for data, starving can occur during disk eject
+ FMOD_OPENSTATE openState;
+ bool starving;
+ bool diskbusy;
+ m_AudioClip->GetSound()->getOpenState(&openState, NULL, &starving, &diskbusy);
+ if (starving != m_AudioParameters.starving)
+ {
+ m_AudioParameters.starving = starving;
+ if (starving)
+ {
+ // Mute all channels if we're starving for data, don't use AudioSource::SetMute because it will overwrite m_AudioParameters.mute
+ if (m_Channel) m_Channel->setMute(true);
+ for (TOneShots::iterator it = m_OneShots.begin(); it != m_OneShots.end();++it)
+ (*it)->channel->setMute(true);
+ }
+ else
+ {
+ // Restore muteness
+ SetMute(m_AudioParameters.mute);
+ }
+ }
+#endif
+
+ /**
+ * One shots
+ **/
+ TOneShots::iterator it = m_OneShots.begin();
+ for (; it != m_OneShots.end();)
+ {
+ OneShot* os = *it;
+ bool playing = true;
+ os->channel->isPlaying(&playing);
+
+ if (!playing)
+ {
+ os->channel->stop();
+ delete os;
+ it = m_OneShots.erase(it);
+ }
+ else
+ {
+ if (!UpdateParameters(os->channel, os))
+ {
+ os->channel->stop();
+ delete os;
+ it = m_OneShots.erase(it);
+ }
+ else
+ ++it;
+ }
+ }
+
+ if (!m_Channel)
+ return;
+
+ //// why is this necessary???
+ // check if channel is still valid and/or paused
+ bool isPaused = false;
+
+ FMOD_RESULT result = m_Channel->getPaused(&isPaused);
+
+ if (result == FMOD_ERR_INVALID_HANDLE)
+ {
+ m_HasScheduledStartDelay = false;
+ m_HasScheduledEndDelay = false;
+ m_Channel = NULL;
+ return;
+ }
+
+
+ // @FIX Should we update parameters even though its paused!?
+ if (isPaused)
+ return;
+
+ /**
+ * Source
+ **/
+ UpdateParameters(m_Channel);
+}
+
+void AudioSource::AddToManager ()
+{
+ if (m_PlayOnAwake && IsWorldPlaying() && !IsPlaying() && IsActive() && GetEnabled())
+ Play();
+}
+
+void AudioSource::RemoveFromManager ()
+{
+ Stop(true);
+}
+
+PROFILER_INFORMATION(gAudioSourceUpdateProfile, "AudioSource.Update", kProfilerAudio)
+
+void AudioSource::Update()
+{
+ PROFILER_AUTO (gAudioSourceUpdateProfile, NULL);
+
+ if(m_VelocityUpdateMode == kVelocityUpdateModeAuto)
+ m_VelocityUpdateMode = GetAudioManager().GetAutomaticUpdateMode( GetGameObjectPtr() );
+
+ if(m_VelocityUpdateMode==kVelocityUpdateModeDynamic)
+ DoUpdate();
+}
+
+void AudioSource::FixedUpdate()
+{
+ if(m_VelocityUpdateMode == kVelocityUpdateModeAuto)
+ m_VelocityUpdateMode = GetAudioManager().GetAutomaticUpdateMode( GetGameObjectPtr());
+
+ if(m_VelocityUpdateMode==kVelocityUpdateModeFixed)
+ DoUpdate();
+}
+
+
+// PROPERTIES
+
+#define SetFMODParam(setFunction, value)\
+if(m_Channel)m_Channel->setFunction(value);\
+for (TOneShots::iterator it = m_OneShots.begin(); it != m_OneShots.end();++it)\
+ (*it)->channel->setFunction(value);
+
+#define SetFMODParam2(setFunction, value1, value2)\
+if(m_Channel)m_Channel->setFunction(value1, value2);\
+for (TOneShots::iterator it = m_OneShots.begin(); it != m_OneShots.end();++it)\
+ (*it)->channel->setFunction(value1, value2);
+
+#define SetFMODParam3(setFunction, value1, value2, value3)\
+if(m_Channel)m_Channel->setFunction(value1, value2, value2);\
+for (TOneShots::iterator it = m_OneShots.begin(); it != m_OneShots.end();++it)\
+ (*it)->channel->setFunction(value1, value2, value3);
+
+
+/// Get/Set pitch of the sound
+float AudioSource::GetPitch() const
+{
+ return m_AudioParameters.pitch;
+}
+void AudioSource::SetPitch(float pitch)
+{
+ AudioClip* clip = m_AudioClip;
+ if(!IsFinite(pitch))
+ {
+ ErrorString("Attempt to set pitch to infinite value in AudioSource::SetPitch ignored!");
+ return;
+ }
+ if(IsNAN(pitch))
+ {
+ ErrorString("Attempt to set pitch to NaN value in AudioSource::SetPitch ignored!");
+ return;
+ }
+#if ENABLE_AUDIO_FMOD
+ if (clip&&clip->IsMovieAudio())
+ pitch = clamp(pitch,0.0f,3.0f);
+#endif
+ m_AudioParameters.pitch = pitch;
+ if (m_Channel&&clip)
+ {
+#if ENABLE_AUDIO_FMOD
+ if(IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a1))
+ {
+ FMOD::Sound* sound = NULL;
+ if(m_Channel->getCurrentSound(&sound) == FMOD_OK)
+ {
+ float frequency;
+ if(sound->getDefaults(&frequency, NULL, NULL, NULL) == FMOD_OK)
+ {
+ frequency *= m_AudioParameters.pitch;
+ Assert(IsFinite(frequency));
+ Assert(!IsNAN(frequency));
+ m_Channel->setFrequency(frequency);
+ }
+ }
+ }
+ else
+#endif
+ {
+ float frequency = m_AudioParameters.pitch * clip->GetFrequency();
+ Assert(IsFinite(frequency));
+ Assert(!IsNAN(frequency));
+ m_Channel->setFrequency(frequency);
+ }
+ }
+ for (TOneShots::iterator it = m_OneShots.begin(); it != m_OneShots.end();++it)
+ {
+#if ENABLE_AUDIO_FMOD
+ if(IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a1))
+ {
+ FMOD::Channel* channel = (*it)->channel;
+ FMOD::Sound* sound = NULL;
+ if(channel->getCurrentSound(&sound) == FMOD_OK)
+ {
+ float frequency;
+ if(sound->getDefaults(&frequency, NULL, NULL, NULL) == FMOD_OK)
+ channel->setFrequency(m_AudioParameters.pitch * frequency);
+ }
+ }
+ else
+#endif
+ (*it)->channel->setFrequency(m_AudioParameters.pitch * (*it)->clip->GetFrequency());
+ }
+}
+
+void AudioSource::SetVolume (float gain)
+{
+ m_AudioParameters.volume = clamp01(gain);
+
+ if(m_Channel)m_Channel->setVolume(m_AudioParameters.volume);
+ for (TOneShots::iterator it = m_OneShots.begin(); it != m_OneShots.end();++it)
+ (*it)->channel->setVolume(m_AudioParameters.volume * (*it)->volumeScale);
+}
+
+
+// Get/Set volume of the sound
+float AudioSource::GetVolume() const
+{
+ return m_AudioParameters.volume;
+}
+
+void AudioSource::SetPlayOnAwake(bool playOnAwake)
+{
+ m_PlayOnAwake = playOnAwake;
+ SetDirty();
+}
+
+void AudioSource::SetIgnoreListenerPause(bool ignoreListenerPause)
+{
+ m_AudioParameters.ignoreListenerPause = ignoreListenerPause;
+ SetDirty();
+}
+
+bool AudioSource::GetLoop () const
+{
+ return m_AudioParameters.loop;
+}
+
+void AudioSource::SetLoop (bool loop)
+{
+
+ if (m_Channel)
+ {
+ m_Channel->setMode( loop?FMOD_LOOP_NORMAL:FMOD_LOOP_OFF );
+ }
+ // always turn looping off for oneshots
+ for (TOneShots::iterator it = m_OneShots.begin(); it != m_OneShots.end();++it)
+ (*it)->channel->setMode( FMOD_LOOP_OFF );
+
+ m_AudioParameters.loop = loop;
+}
+
+// Sets how much the 3d engine has an effect on the channel.
+float AudioSource::GetPanLevel() const
+{
+ return m_AudioParameters.panLevelCustomCurve.GetKey(0).value;
+}
+void AudioSource::SetPanLevel(float level)
+{
+ // As panlevel is a curve
+ // setting level resets the curve to 1 key
+ level = clamp01(level);
+ ReplaceOrAddSingleCurveValue(level, m_AudioParameters.panLevelCustomCurve);
+}
+
+// Sets the doppler scale for this AudioSource
+float AudioSource::GetDopplerLevel() const
+{
+#if ENABLE_AUDIO_FMOD
+ return m_AudioParameters.dopplerLevel;
+#else
+ return 0;
+#endif
+
+}
+void AudioSource::SetDopplerLevel(float level)
+{
+#if ENABLE_AUDIO_FMOD
+ m_AudioParameters.dopplerLevel = clamp(level, 0.0F, 5.0F);
+ SetFMODParam(set3DDopplerLevel, m_AudioParameters.dopplerLevel);
+#endif
+}
+
+// Sets the spread angle a 3d stereo ogr multichannel cound in speaker space.
+// 0 = all sound channels are located at the same speaker location and is 'mono'.
+// 360 = all subchannels are located at the opposite speaker location to the speaker location that it should be according to 3D position. Default = 0.
+// Sets how much the 3d engine has an effect on the channel.
+float AudioSource::GetSpread() const
+{
+ return m_AudioParameters.spreadCustomCurve.GetKey(0).value * 360.0f;
+}
+void AudioSource::SetSpread(float spread)
+{
+ // As spread is a curve
+ // setting level resets the curve to 1 key
+ spread = clamp(spread,0.0f, 360.0f);
+ ReplaceOrAddSingleCurveValue(spread / 360.0f, m_AudioParameters.spreadCustomCurve);
+}
+
+// Sets the priority of the [[AudioSource]]
+// Unity is virtualizing AudioSources, when there's more AudioSources playing than available hardware channels.
+// The AudioSources with lowest priority (and audibility) is virtualized first.
+// Priority is an integer between 0 and 256. 0=highest priority, 256=lowest priority
+int AudioSource::GetPriority() const
+{
+ return m_AudioParameters.priority;
+}
+
+void AudioSource::SetPriority(int priority)
+{
+ m_AudioParameters.priority = clamp(priority, 0, 255);
+ SetFMODParam(setPriority, m_AudioParameters.priority);
+}
+
+// Un- / Mutes the AudioSource. Mute sets the volume=0, Un-Mute restore the original volume.
+bool AudioSource::GetMute() const
+{
+ return m_AudioParameters.mute;
+}
+void AudioSource::SetMute(bool mute)
+{
+ m_AudioParameters.mute = mute;
+#if UNITY_WII
+ // If we're starving for data, auto mute channels
+ mute |= m_AudioParameters.starving;
+#endif
+ if (m_Channel)
+ m_Channel->setMute(mute);
+
+ for (TOneShots::iterator it = m_OneShots.begin(); it != m_OneShots.end();++it)
+ (*it)->channel->setMute(mute);
+}
+
+// Within the Min distance the AudioSource will cease to grow louder in volume.
+// Outside the min distance the volume starts to attenuate.
+float AudioSource::GetMinDistance() const
+{
+ return m_AudioParameters.minDistance;
+}
+void AudioSource::SetMinDistance(float minDistance)
+{
+ m_AudioParameters.minDistance = max(minDistance, 0.0F);
+ SetFMODParam2(set3DMinMaxDistance, m_AudioParameters.minDistance, m_AudioParameters.maxDistance);
+}
+
+// (Logarithmic rolloff) MaxDistance is the distance a sound stops attenuating at.
+// (Linear rolloff) MaxDistance is the distance where the sound is completely inaudible.
+float AudioSource::GetMaxDistance() const
+{
+ return m_AudioParameters.maxDistance;
+}
+void AudioSource::SetMaxDistance(float maxDistance)
+{
+ m_AudioParameters.maxDistance = max(maxDistance, m_AudioParameters.minDistance);
+ SetFMODParam2(set3DMinMaxDistance, m_AudioParameters.minDistance, m_AudioParameters.maxDistance);
+}
+
+#if ENABLE_AUDIO_FMOD
+// Inside cone angle, in degrees. This is the angle within which the sound is at its normal volume.
+// Must not be greater than outsideconeangle. Default = 360.
+float AudioSource::GetInsideConeAngle() const
+{
+ return m_AudioParameters.insideConeAngle;
+}
+void AudioSource::SetInsideConeAngle(float angle)
+{
+ m_AudioParameters.insideConeAngle = angle;
+ SetFMODParam3(set3DConeSettings, m_AudioParameters.insideConeAngle, m_AudioParameters.outsideConeAngle, m_AudioParameters.outsideConeVolume);
+}
+
+/// Outside cone angle, in degrees. This is the angle outside of which the sound is at its outside volume.
+/// Must not be less than insideconeangle. Default = 360.
+float AudioSource::GetOutsideConeAngle() const
+{
+ return m_AudioParameters.outsideConeAngle;
+}
+void AudioSource::SetOutsideConeAngle(float angle)
+{
+ m_AudioParameters.outsideConeAngle = angle;
+
+ SetFMODParam3(set3DConeSettings, m_AudioParameters.insideConeAngle, m_AudioParameters.outsideConeAngle, m_AudioParameters.outsideConeVolume);
+}
+
+/// Cone outside volume, from 0 to 1.0. Default = 1.0.
+float AudioSource::GetOutsideConeVolume() const
+{
+ return m_AudioParameters.outsideConeVolume;
+}
+void AudioSource::SetOutsideConeVolume(float volume)
+{
+ m_AudioParameters.outsideConeVolume = clamp01(volume);
+
+ SetFMODParam3(set3DConeSettings, m_AudioParameters.insideConeAngle, m_AudioParameters.outsideConeAngle, m_AudioParameters.outsideConeVolume);
+}
+#endif
+/// Set/Get rolloff mode
+RolloffMode AudioSource::GetRolloffMode() const
+{
+ return m_AudioParameters.rolloffMode;
+}
+
+void AudioSource::SetRolloffMode(RolloffMode mode)
+{
+ m_AudioParameters.rolloffMode = mode;
+}
+
+/// Set/Get Custom rolloff curve
+AnimationCurve& AudioSource::GetCustomRolloffCurve()
+{
+ return m_AudioParameters.rolloffCustomCurve;
+}
+
+const AnimationCurve& AudioSource::GetCustomRolloffCurve() const
+{
+ return m_AudioParameters.rolloffCustomCurve;
+}
+
+void AudioSource::SetCustomRolloffCurve(const AnimationCurve& curve)
+{
+ m_AudioParameters.rolloffCustomCurve = curve;
+}
+
+/// Set/Get Pan Level curve
+AnimationCurve& AudioSource::GetCustomPanLevelCurve()
+{
+ return m_AudioParameters.panLevelCustomCurve;
+}
+
+const AnimationCurve& AudioSource::GetCustomPanLevelCurve() const
+{
+ return m_AudioParameters.panLevelCustomCurve;
+}
+
+void AudioSource::SetCustomPanLevelCurve(const AnimationCurve& curve)
+{
+ m_AudioParameters.panLevelCustomCurve = curve;
+}
+
+/// Set/Get Pan Level curve
+AnimationCurve& AudioSource::GetCustomSpreadCurve()
+{
+ return m_AudioParameters.spreadCustomCurve;
+}
+
+const AnimationCurve& AudioSource::GetCustomSpreadCurve() const
+{
+ return m_AudioParameters.spreadCustomCurve;
+}
+
+void AudioSource::SetCustomSpreadCurve(const AnimationCurve& curve)
+{
+ m_AudioParameters.spreadCustomCurve = curve;
+}
+
+/// Sets a channels pan position linearly. Only works for 2D clips.
+/// -1.0 to 1.0. -1.0 is full left. 0.0 is center. 1.0 is full right.
+/// Only sounds that are mono or stereo can be panned. Multichannel sounds (ie >2 channels) cannot be panned.
+float AudioSource::GetPan() const
+{
+ return m_AudioParameters.pan;
+}
+void AudioSource::SetPan(float pan)
+{
+ m_AudioParameters.pan = clamp(pan, -1.0f, 1.0f);
+ SetFMODParam(setPan, m_AudioParameters.pan);
+}
+
+/// Bypass/ignore any applied effects on AudioSource
+bool AudioSource::GetBypassEffects() const
+{
+ return m_AudioParameters.bypassEffects;
+}
+
+void AudioSource::SetBypassEffects(bool bypassEffect)
+{
+ m_AudioParameters.bypassEffects = bypassEffect;
+ SetupGroups();
+}
+
+/// Bypass/ignore any applied effects on AudioSource
+bool AudioSource::GetBypassListenerEffects() const
+{
+ return m_AudioParameters.bypassListenerEffects;
+}
+
+void AudioSource::SetBypassListenerEffects(bool bypassListenerEffect)
+{
+ m_AudioParameters.bypassListenerEffects = bypassListenerEffect;
+ SetupGroups();
+}
+
+bool AudioSource::GetBypassReverbZones() const
+{
+ return m_AudioParameters.bypassReverbZones;
+}
+
+/// Bypass effect of reverb zones on this AudioSource
+void AudioSource::SetBypassReverbZones(bool bypassReverbZones)
+{
+ m_AudioParameters.bypassReverbZones = bypassReverbZones;
+ SetupGroups();
+}
+
+float AudioSource::GetSecPosition() const
+{
+
+ float time = 0.0f;
+ if (m_Channel)
+ {
+ unsigned position = 0;
+
+ m_Channel->getPosition(&position, FMOD_TIMEUNIT_MS);
+ time = (float)position / 1000.f;
+ }
+ else
+ time = m_AudioClip.IsNull()?0.0f:(float)m_samplePosition / (float)m_AudioClip->GetFrequency();
+
+ #if SUPPORT_REPRODUCE_LOG
+ if (GetReproduceOutStream())
+ {
+ *GetReproduceOutStream() << "Audio_Position ";
+ WriteFloat(*GetReproduceOutStream(), time);
+ *GetReproduceOutStream() << std::endl;
+ }
+ else if (GetReproduceInStream() && GetReproduceVersion() >= 4)
+ {
+ if (!CheckReproduceTag("Audio_Position", *GetReproduceInStream()))
+ {
+ ErrorString("Grabbing Audio.IsPlaying but there are no realtime calls recorded");
+ return time;
+ }
+ ReadFloat(*GetReproduceInStream(), time);
+ }
+ #endif
+ return time;
+
+}
+
+
+
+void AudioSource::SetSecPosition(float secPosition)
+{
+ if (m_Channel)
+ m_Channel->setPosition((unsigned int)(secPosition * 1000.f), FMOD_TIMEUNIT_MS);
+ m_samplePosition = (unsigned int)(m_AudioClip.IsNull() ? 0.0f : (float)(secPosition * m_AudioClip->GetFrequency()));
+}
+
+
+
+UInt32 AudioSource::GetSamplePosition() const
+{
+ unsigned position = m_samplePosition;
+ if (m_Channel)
+ m_Channel->getPosition(&position, FMOD_TIMEUNIT_PCM);
+ return position;
+
+}
+
+
+
+void AudioSource::SetSamplePosition(UInt32 position)
+{
+ if (m_Channel)
+ m_Channel->setPosition(position, FMOD_TIMEUNIT_PCM);
+ m_samplePosition = position;
+}
+
+
+void AudioSource::SetScheduledStartTime(double time)
+{
+#if ENABLE_AUDIO_FMOD
+ if (m_Channel)
+ {
+ m_HasScheduledStartDelay = true;
+ int sampleRate;
+ GetAudioManager().GetFMODSystem()->getSoftwareFormat(&sampleRate, NULL, NULL, NULL, NULL, NULL);
+ UInt64 sample = (UInt64)(time * sampleRate) + GetAudioManager().GetAccumulatedPauseTicks();
+ unsigned hiclock = sample >> 32;
+ unsigned loclock = sample & 0xFFFFFFFF;
+ m_Channel->setDelay(FMOD_DELAYTYPE_DSPCLOCK_START, hiclock, loclock);
+ }
+#endif
+}
+
+
+void AudioSource::SetScheduledEndTime(double time)
+{
+#if ENABLE_AUDIO_FMOD
+ if (m_Channel)
+ {
+ m_HasScheduledEndDelay = true;
+ int sampleRate;
+ GetAudioManager().GetFMODSystem()->getSoftwareFormat(&sampleRate, NULL, NULL, NULL, NULL, NULL);
+ UInt64 sample = (UInt64)(time * sampleRate) + GetAudioManager().GetAccumulatedPauseTicks();
+ unsigned hiclock = sample >> 32;
+ unsigned loclock = sample & 0xFFFFFFFF;
+ m_Channel->setDelay(FMOD_DELAYTYPE_DSPCLOCK_END, hiclock, loclock);
+ }
+#endif
+}
+
+
+#if ENABLE_AUDIO_FMOD
+
+void AudioSource::GetOutputData(float* samples, int numSamples, int channelOffset)
+{
+ if (m_Channel)
+ {
+ m_Channel->getWaveData(samples, numSamples, channelOffset);
+ }
+}
+
+/// Gets the current spectrum data
+void AudioSource::GetSpectrumData(float* samples, int numSamples, int channelOffset, FMOD_DSP_FFT_WINDOW windowType)
+{
+ if (m_Channel)
+ {
+ m_Channel->getSpectrum(samples, numSamples, channelOffset, windowType);
+ }
+}
+
+// Apply filters
+void AudioSource::ApplyFilters()
+{
+ if (!m_wetGroup)
+ return;
+
+ TFilters filters;
+ GetFilterComponents(filters, true);
+
+ for (TFilters::const_iterator it=filters.begin();it!=filters.end();it++)
+ {
+ FMOD::DSP* dsp = *it;
+
+ if (dsp == m_PlayingDSP)
+ continue;
+
+ FMOD_RESULT result;
+ result = dsp->remove();
+ FMOD_ASSERT(result);
+ result = m_wetGroup->addDSP(dsp, 0);
+ FMOD_ASSERT(result);
+ }
+}
+
+#endif //ENABLE_AUDIO_FMOD
+
+void AudioSource::PlayOneShot( AudioClip& clip, float volumeScale )
+{
+ if(GetAudioManager().IsAudioDisabled())
+ return;
+
+ if (! (GetEnabled() && IsActive()) )
+ {
+ WarningStringObject("Can not play a disabled audio source", this);
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ return;
+ }
+
+ SetupGroups();
+
+ OneShot* oneshot = new OneShot();
+ oneshot->channel = clip.CreateChannel(this);
+ Assert(oneshot->channel);
+#if ENABLE_AUDIO_FMOD
+ ApplyFilters();
+#endif
+
+ uintptr_t ptr = reinterpret_cast<uintptr_t>(oneshot) | 1;
+ oneshot->channel->setUserData(reinterpret_cast<void*>(ptr));
+ oneshot->clip = const_cast<AudioClip*>(&clip);
+ oneshot->volumeScale = volumeScale;
+ oneshot->audioSource = this;
+
+ if (oneshot->channel)
+ {
+#if ENABLE_AUDIO_FMOD
+ if(m_dryGroup != NULL)
+ oneshot->channel->setChannelGroup(m_dryGroup);
+
+ Vector3f pos = GetComponent(Transform).GetPosition() ;
+ FMOD_VECTOR fpos = UNITYVEC2FMODVEC(pos);
+ oneshot->channel->set3DAttributes(
+ &fpos,
+ 0 );
+#endif
+
+ // start!
+ m_OneShots.push_back(oneshot);
+ AssignProps(); // Apply all parameters
+ bool paused = IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a3) && GetAudioManager().GetPause() && !m_AudioParameters.ignoreListenerPause;
+ oneshot->channel->setPaused(paused);
+ GetAudioManager ().AddAudioSource (this, paused); // to make sure source is put into active or paused sources lists
+ }
+ else
+ {
+ delete oneshot;
+ }
+}
+
+void AudioSource::SetIgnoreListenerVolume(bool ignore)
+{
+ if (m_IgnoreListenerVolume == ignore)
+ return;
+
+ m_IgnoreListenerVolume = ignore;
+ SetupGroups();
+}
+
+float inline CalcOALGain(float rolloffFactor, float volume, float minVolume, float maxVolume, float distance)
+{
+ float gain = 1.0f;
+ if (1.0f + rolloffFactor * (distance - 1.0f) > 0.0f)
+ gain = 1.0f / ( 1.0f + rolloffFactor * (distance - 1.0f));
+
+ gain *= volume;
+ gain = std::min(gain, maxVolume);
+ gain = std::max(gain, minVolume);
+ return gain;
+}
+
+AudioSource* AudioSource::GetAudioSourceFromChannel(AudioChannel* channel)
+{
+ Assert(channel);
+
+ void* userData = NULL;
+ AudioSource* audioSource = NULL;
+ channel->getUserData(&userData);
+
+ if (!userData) return NULL;
+
+ uintptr_t ptr = reinterpret_cast<uintptr_t> (userData);
+
+ // is it a OneShot?
+ if (ptr & 1)
+ {
+ ptr = ptr & (~1);
+ OneShot* oneshot = reinterpret_cast<OneShot*>(ptr);
+ Assert (oneshot->audioSource);
+ audioSource = oneshot->audioSource;
+ }
+ else // AudioSource
+ audioSource = (AudioSource*)userData;
+
+ return audioSource;
+}
+
+
+
+/**
+ * Create a custom rolloff curve from old <3.0 parameters
+ **/
+
+void AudioSource::CreateOpenALRolloff(float rolloffFactor, float minVolume, float maxVolume)
+{
+
+#if ENABLE_AUDIO_FMOD
+ AnimationCurve& curve = m_AudioParameters.rolloffCustomCurve;
+ curve.RemoveKeys(curve.begin(), curve.end());
+
+ // insert first key
+ AnimationCurve::Keyframe key;
+ key.time = 0.0f;
+ key.value = CalcOALGain(rolloffFactor, m_AudioParameters.volume, minVolume, maxVolume, 0.0f);
+ curve.AddKey (key);
+
+ for (float distance=0.1f;distance<m_AudioParameters.maxDistance;distance*=2)
+ {
+ AnimationCurve::Keyframe key;
+ key.time = distance;
+ key.value = CalcOALGain(rolloffFactor, m_AudioParameters.volume, minVolume, maxVolume, distance);
+ // add some sensible in/out slopes
+ float s = distance/10.0f;
+ key.inSlope = (key.value - CalcOALGain(rolloffFactor, m_AudioParameters.volume, minVolume, maxVolume, distance-s)) / s;
+ key.outSlope = (CalcOALGain(rolloffFactor, m_AudioParameters.volume, minVolume, maxVolume,distance+s) - key.value) / s;
+ curve.AddKey (key);
+ }
+
+ key.time = m_AudioParameters.maxDistance;
+ key.value = CalcOALGain(rolloffFactor, m_AudioParameters.volume, minVolume, maxVolume, m_AudioParameters.maxDistance);
+ curve.AddKey (key);
+#endif
+}
+
+#if ENABLE_AUDIO_FMOD
+
+bool AudioSource::GetFilterComponents(TFilters& filters, bool create) const
+{
+ const GameObject* go = GetGameObjectPtr();
+
+ if (!go)
+ return false;
+
+ for (int i=0;i<go->GetComponentCount();i++)
+ {
+ AudioFilter* filter = NULL;
+ FMOD::DSP* dsp = NULL;
+ filter = dynamic_pptr_cast<AudioFilter*> (&go->GetComponentAtIndex(i));
+ // built-in filter are only available in PRO
+ if ( filter && GetBuildSettings().hasAdvancedVersion )
+ dsp = filter->GetDSP();
+
+ #if ENABLE_SCRIPTING
+ if (!dsp)
+ {
+ MonoBehaviour* behaviour = dynamic_pptr_cast<MonoBehaviour*> (&go->GetComponentAtIndex(i));
+ if ( behaviour )
+ {
+ if (create)
+ dsp = behaviour->GetOrCreateDSP();
+ else
+ dsp = behaviour->GetDSP();
+ }
+ }
+ #endif
+
+ if (dsp)
+ filters.push_back( dsp );
+ }
+
+ return !filters.empty();
+}
+
+#endif
+
+template<class TransferFunc>
+void AudioSource::Transfer (TransferFunc& transfer) {
+ Super::Transfer (transfer);
+
+ // 3.5 3: Normalized curve values
+ // 3.0 2: FMOD custom rolloff data
+ // <3.0 1: OpenAL rolloff data
+ transfer.SetVersion (3);
+
+ if (transfer.IsOldVersion(2) || transfer.IsCurrentVersion ())
+ {
+ transfer.Transfer (m_AudioClip, "m_audioClip");
+ TRANSFER_SIMPLE (m_PlayOnAwake);
+ transfer.Align();
+
+ transfer.Transfer (m_AudioParameters.volume, "m_Volume");
+ transfer.Transfer (m_AudioParameters.pitch, "m_Pitch");
+
+ transfer.Transfer (m_AudioParameters.loop, "Loop"); // repeat sound
+ transfer.Transfer(m_AudioParameters.mute, "Mute");
+ transfer.Align();
+
+ transfer.Transfer(m_AudioParameters.priority, "Priority");
+ transfer.Transfer(m_AudioParameters.dopplerLevel, "DopplerLevel");
+
+ transfer.Transfer(m_AudioParameters.minDistance, "MinDistance");
+ transfer.Transfer(m_AudioParameters.maxDistance, "MaxDistance");
+
+ transfer.Transfer(m_AudioParameters.pan, "Pan2D");
+
+ SInt32 mode = (SInt32)m_AudioParameters.rolloffMode;
+ transfer.Transfer(mode, "rolloffMode");
+ m_AudioParameters.rolloffMode = (RolloffMode)mode;
+
+ transfer.Transfer(m_AudioParameters.bypassEffects, "BypassEffects");
+ transfer.Transfer(m_AudioParameters.bypassListenerEffects, "BypassListenerEffects");
+ transfer.Transfer(m_AudioParameters.bypassReverbZones, "BypassReverbZones");
+
+ transfer.Align();
+
+ // @TODO strip these from the player build if they're empty?
+ transfer.Transfer(m_AudioParameters.rolloffCustomCurve,"rolloffCustomCurve");
+ transfer.Transfer(m_AudioParameters.panLevelCustomCurve, "panLevelCustomCurve");
+ transfer.Transfer(m_AudioParameters.spreadCustomCurve, "spreadCustomCurve");
+
+ if (transfer.IsOldVersion(2))
+ {
+ // Normalize curves so time goes from 0 to 1
+ ScaleCurveTime (m_AudioParameters.rolloffCustomCurve, 1.0f / m_AudioParameters.maxDistance);
+ ScaleCurveTime (m_AudioParameters.panLevelCustomCurve, 1.0f / m_AudioParameters.maxDistance);
+ ScaleCurveTime (m_AudioParameters.spreadCustomCurve, 1.0f / m_AudioParameters.maxDistance);
+ }
+ }
+ else
+ {
+ transfer.Transfer (m_AudioClip, "m_audioClip");
+ TRANSFER_SIMPLE (m_PlayOnAwake);
+ transfer.Align();
+
+ transfer.Transfer (m_AudioParameters.volume, "m_Volume");
+ transfer.Transfer (m_AudioParameters.pitch, "m_Pitch");
+
+ float minVolume, maxVolume;
+ transfer.Transfer (minVolume,"m_MinVolume");
+ transfer.Transfer (maxVolume,"m_MaxVolume");
+ float rolloffFactor;
+ transfer.Transfer (rolloffFactor, "m_RolloffFactor");
+
+ transfer.Transfer (m_AudioParameters.loop, "Loop"); // repeat sound
+#if ENABLE_AUDIO_FMOD
+ CreateOpenALRolloff(rolloffFactor, minVolume, maxVolume);
+#endif
+ m_AudioParameters.rolloffMode = kRolloffCustom;
+ }
+}
+
+
+void AudioSource::OnAddComponent()
+{
+#if ENABLE_AUDIO_FMOD
+ ApplyFilters();
+#endif
+}
+
+
+void AudioSource::InitializeClass ()
+{
+ REGISTER_MESSAGE_VOID (AudioSource, kDidAddComponent, OnAddComponent);
+}
+
+void AudioSource::CleanupClass()
+{
+}
+
+IMPLEMENT_CLASS_HAS_INIT (AudioSource)
+IMPLEMENT_OBJECT_SERIALIZE (AudioSource)
+
+#endif //ENABLE_AUDIO
diff --git a/Runtime/Audio/AudioSource.h b/Runtime/Audio/AudioSource.h
new file mode 100644
index 0000000..cd5d23d
--- /dev/null
+++ b/Runtime/Audio/AudioSource.h
@@ -0,0 +1,351 @@
+#ifndef AUDIOSOURCE_H
+#define AUDIOSOURCE_H
+
+#if ENABLE_AUDIO
+#include "Runtime/GameCode/Behaviour.h"
+#include "AudioParameters.h"
+#include "AudioSourceFilter.h"
+#include "Runtime/Math/Vector3.h"
+#include <vector>
+#include "AudioClip.h"
+#include "AudioLowPassFilter.h"
+#include "AudioBehaviour.h"
+#if UNITY_FLASH
+#include "PlatformDependent/FlashSupport/cpp/AudioChannel.h"
+#endif
+
+
+class AudioSource : public AudioBehaviour
+{
+public:
+
+ REGISTER_DERIVED_CLASS (AudioSource, AudioBehaviour)
+ DECLARE_OBJECT_SERIALIZE (AudioSource)
+
+ /**
+ * Construction/Destruction
+ **/
+ AudioSource (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~AudioSource (); declared-by-macro
+
+
+ /**
+ * Transport
+ **/
+ /// Plays a sound one time, then forgets about it (You cannot stop a one shot sound)
+ void PlayOneShot (AudioClip& clip, float volumeMultiplier = 1.0F);
+ /// Pause one shot sounds
+ void PauseOneShots ();
+ /// Resumes one shot sounds
+ void ResumeOneShots ();
+ /// Plays the active audioclip at (future) scheduled time. If time < 0 it specifies a delay
+ void Play(double time = 0.0);
+ /// Pauses the active audioclip
+ void Pause ();
+ /// Stops the active audio clip
+ void Stop (bool stopOneShots);
+ /// Stops a specific channel
+ void Stop(AudioChannel* channel);
+ /// Is the audio source currently playing? (Only looks at the main audio source, OneShots are ignored)
+ bool IsPlaying () const;
+ /// Is the audio source currently paused? (Only looks at the main audio source, OneShots are ignored)
+ bool IsPaused () const;
+
+ bool IsPlayingScripting ();
+
+ // positions
+ // seconds
+ float GetSecPosition() const;
+ void SetSecPosition(float secPosition);
+
+ UInt32 GetSamplePosition() const;
+ void SetSamplePosition(UInt32 position);
+ void SetScheduledStartTime(double time);
+ void SetScheduledEndTime(double time);
+ void CorrectScheduledTimeAfterUnpause(UInt64 delay);
+
+ // Get Length
+ float GetLength() const;
+
+ /// Get/Set PlayOnAwake
+ bool GetPlayOnAwake() const { return m_PlayOnAwake; }
+ void SetPlayOnAwake(bool playOnAwake);
+ bool GetIgnoreListenerPause() const { return m_AudioParameters.ignoreListenerPause; }
+ void SetIgnoreListenerPause(bool ignoreListenerPause);
+ bool HasScheduledStartDelay() const { return m_HasScheduledStartDelay; }
+ bool HasScheduledEndDelay() const { return m_HasScheduledEndDelay; }
+ bool HasScheduledTime() const { return m_HasScheduledStartDelay | m_HasScheduledEndDelay; }
+
+ /**
+ * Behaviour implementation
+ **/
+ virtual void Deactivate (DeactivateOperation operation);
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+ virtual void CheckConsistency ();
+ virtual void Update ();
+ virtual void FixedUpdate ();
+
+ static void InitializeClass ();
+ static void CleanupClass();
+
+ /**
+ *
+ **/
+ void AddToManager();
+ void RemoveFromManager();
+
+ void OnAddComponent();
+
+
+ virtual void Reset();
+
+ void Cleanup();
+
+public:
+ // GET/SETTERS
+ bool GetLoop () const;
+ void SetLoop (bool loop);
+
+ /// Get/Set pitch of the sound
+ float GetPitch() const;
+ void SetPitch(float pitch);
+
+ // Get/Set volume of the sound
+ float GetVolume() const;
+ void SetVolume(float volume);
+
+ // Sets how much the 3d engine has an effect on the channel.
+ float GetPanLevel() const;
+ void SetPanLevel(float level);
+
+ // Sets the doppler scale for this AudioSource
+ float GetDopplerLevel() const;
+ void SetDopplerLevel(float level);
+
+ // Sets the spread angle of a 3d stereo or multichannel sound in speaker space.
+ // 0 = all sound channels are located at the same speaker location and is 'mono'.
+ // 360 = all subchannels are located at the opposite speaker location to the speaker location that it should be according to 3D position. Default = 0.
+ float GetSpread() const;
+ void SetSpread(float spread);
+
+ // Sets the priority of the [[AudioSource]]
+ // Unity is virtualizing AudioSources, when there's more AudioSources playing than available hardware channels.
+ // The AudioSources with lowest priority (and audibility) is virtualized first.
+ // Priority is an integer between 0 and 256. 0=highest priority, 256=lowest priority
+ int GetPriority() const;
+ void SetPriority(int priority);
+
+ // Un- / Mutes the AudioSource. Mute sets the volume=0, Un-Mute restore the original volume.
+ bool GetMute() const;
+ void SetMute(bool mute);
+
+ // Within the Min distance the AudioSource will cease to grow louder in volume.
+ // Outside the min distance the volume starts to attenuate.
+ float GetMinDistance() const;
+ void SetMinDistance(float minDistance);
+
+ // (Logarithmic rolloff) MaxDistance is the distance a sound stops attenuating at.
+ // (Linear rolloff) MaxDistance is the distance where the sound is completely inaudible.
+ float GetMaxDistance() const;
+ void SetMaxDistance(float maxDistance);
+
+ // Inside cone angle, in degrees. This is the angle within which the sound is at its normal volume.
+ // Must not be greater than outsideconeangle. Default = 360.
+ float GetInsideConeAngle() const;
+ void SetInsideConeAngle(float angle);
+
+ /// Outside cone angle, in degrees. This is the angle outside of which the sound is at its outside volume.
+ /// Must not be less than insideconeangle. Default = 360.
+ float GetOutsideConeAngle() const;
+ void SetOutsideConeAngle(float angle);
+
+ /// Cone outside volume, from 0 to 1.0. Default = 1.0.
+ float GetOutsideConeVolume() const;
+ void SetOutsideConeVolume(float volume);
+
+ /// Set/Get rolloff mode
+ RolloffMode GetRolloffMode() const;
+ void SetRolloffMode(RolloffMode mode);
+
+ /// Set/Get Custom rolloff curve
+ AnimationCurve& GetCustomRolloffCurve();
+ const AnimationCurve& GetCustomRolloffCurve() const;
+ void SetCustomRolloffCurve(const AnimationCurve&);
+
+ /// Set/Get PanLevel distance curve
+ AnimationCurve& GetCustomPanLevelCurve();
+ const AnimationCurve& GetCustomPanLevelCurve() const;
+ void SetCustomPanLevelCurve(const AnimationCurve&);
+
+ /// Set/Get spread distance curve
+ AnimationCurve& GetCustomSpreadCurve();
+ const AnimationCurve& GetCustomSpreadCurve() const;
+ void SetCustomSpreadCurve(const AnimationCurve&);
+
+ /// Sets a audiosource pan position linearly. Only works for 2D clips.
+ /// -1.0 to 1.0. -1.0 is full left. 0.0 is center. 1.0 is full right.
+ /// Only sounds that are mono or stereo can be panned. Multichannel sounds (ie >2 channels) cannot be panned.
+ float GetPan() const;
+ void SetPan(float pan);
+
+ /// Bypass/ignore any applied effects on AudioSource
+ bool GetBypassEffects() const;
+ void SetBypassEffects(bool bypassEffect);
+
+ /// Bypass/ignore any applied effects on listener
+ bool GetBypassListenerEffects() const;
+ void SetBypassListenerEffects(bool bypassListenerEffects);
+
+ /// Bypass effect of reverb zones on this AudioSource
+ bool GetBypassReverbZones() const;
+ void SetBypassReverbZones(bool bypassReverbZones);
+
+#if ENABLE_AUDIO_FMOD
+ /// Gets the current output pcm data
+ void GetOutputData(float* samples, int numSamples, int channelOffset);
+ /// Gets the current spectrum data
+ void GetSpectrumData(float* samples, int numSamples, int channelOffset, FMOD_DSP_FFT_WINDOW windowType);
+#endif
+ /// Sets the currently active audio clip
+ void SetAudioClip(AudioClip *clip);
+ AudioClip *GetAudioClip () const {return m_AudioClip; }
+
+ int GetVelocityUpdateMode() const { return m_VelocityUpdateMode; }
+ void SetVelocityUpdateMode(int update) { m_VelocityUpdateMode=update; }
+
+ bool GetIgnoreListenerVolume() const { return m_IgnoreListenerVolume; }
+ void SetIgnoreListenerVolume(bool ignore);
+
+private:
+#if DOXYGEN
+ int Priority; ///< Sets the priority of the source. A sound with a lower priority will more likely be stolen by high priorities sounds.
+ float DopplerLevel; ///< Sets the specific doppler scale for the source.
+ float MinDistance; ///< Within the minDistance, the volume will stay at the loudest possible. Outside of this mindistance it begins to attenuate.
+ float MaxDistance; ///< MaxDistance is the distance a sound stops attenuating at.
+ float Pan2D; ///< Sets a source's pan position linearly. Only applicable on 2D sounds.
+
+ float m_Pitch; ///< Sets the frequency of the sound. Use this to slow down or speed up the sound.
+ float m_Volume; ///< Sets the volume of the sound.
+
+ // rolloff
+ RolloffMode rolloffMode; ///< enum { Logarithmic Rolloff=0, Linear Rolloff, Custom Rolloff }
+
+ bool Loop; ///< Set the source to loop. If loop points are defined in the clip, these will be respected.
+ bool Mute; ///< Mutes the sound.
+
+ bool BypassEffects; ///< Bypass/ignore any applied effects on AudioSource
+ bool BypassListenerEffects; ///< Bypass/ignore any applied effects from listener
+ bool BypassReverbZones; ///< Bypass/ignore any reverb zones
+ bool IgnoreListenerPause; ///< Allow source to play even though AudioListener is paused (for GUI sounds)
+
+#else
+ AudioParameters m_AudioParameters;
+#endif
+
+
+private:
+ /**
+ * OneShots
+ **/
+ struct OneShot
+ {
+ AudioChannel* channel;
+ AudioClip* clip;
+ float volumeScale;
+ AudioSource* audioSource;
+ };
+
+ typedef std::vector<OneShot*> TOneShots;
+
+ TOneShots m_OneShots;
+
+ /**
+ * Update channel properties
+ * @param channel The channel to update
+ * @param oneshot Is this a oneshot?
+ * @return True if channel was update. False if the channel is invalid
+ **/
+ inline bool UpdateParameters(AudioChannel* channel, OneShot* oneshot = NULL);
+
+ /**
+ * Create a custom rolloff curve from old <3.0 parameters
+ **/
+ void CreateOpenALRolloff(float rolloffFactor, float minVolume, float maxVolume);
+
+ /**
+ * Setup effect and non-effect groups
+ **/
+ void SetupGroups();
+ void TearDownGroups();
+ void SetChannelGroup(AudioChannel* channel);
+
+#if ENABLE_AUDIO_FMOD
+ /**
+ * Apply filters
+ **/
+ void ApplyFilters();
+#endif
+
+private:
+ PPtr<AudioClip> m_AudioClip;
+ ListNode<AudioSource> m_Node;
+ AudioChannel* m_Channel;
+
+
+ AudioManager::AudioScheduledSource m_ScheduledSource;
+
+#if ENABLE_AUDIO_FMOD
+ // channel group, filter/non-filter group and for oneshot
+ FMOD::ChannelGroup* m_dryGroup; // No Effect unit
+ FMOD::ChannelGroup* m_wetGroup; // Effect unit
+#endif
+ /**
+ * backward compatibility props
+ **/
+ bool m_IgnoreListenerVolume;
+
+ bool m_PlayOnAwake; ///<Play the sound when the scene loads.
+ bool m_HasScheduledStartDelay;
+ bool m_HasScheduledEndDelay;
+ int m_VelocityUpdateMode;
+ Vector3f m_LastUpdatePosition;
+
+ // cached position
+ unsigned m_samplePosition;
+ // cache pause
+ bool m_pause;
+
+ void DoUpdate();
+ void UpdateQueue();
+ void SetupQueue();
+ void AssignProps();
+ float CalculateVolumeModifierForDistance(float distance);
+
+#if ENABLE_AUDIO_FMOD
+ FMOD::DSP* m_PlayingDSP;
+ typedef std::vector<FMOD::DSP*> TFilters;
+ bool GetFilterComponents(TFilters &filters, bool create) const;
+#endif
+
+ friend class AudioManager;
+
+#if ENABLE_PROFILER
+ static int s_AudioSourceCount;
+#endif
+
+#if ENABLE_AUDIO_FMOD
+private: // callbacks
+ //static FMOD_RESULT F_CALLBACK channelCallback(
+ // FMOD_CHANNEL *channel, FMOD_CHANNEL_CALLBACKTYPE type, void *commanddata1, void *commanddata2);
+ static float F_CALLBACK rolloffCallback(
+ FMOD_CHANNEL * channel,
+ float distance
+ );
+#endif //ENABLE_AUDIO_FMOD
+
+public: // static helper functions
+ static AudioSource* GetAudioSourceFromChannel(AudioChannel* channel);
+};
+
+#endif //ENABLE_AUDIO
+#endif
diff --git a/Runtime/Audio/AudioSourceFilter.cpp b/Runtime/Audio/AudioSourceFilter.cpp
new file mode 100644
index 0000000..21a072f
--- /dev/null
+++ b/Runtime/Audio/AudioSourceFilter.cpp
@@ -0,0 +1,68 @@
+#include "UnityPrefix.h"
+#if ENABLE_AUDIO_FMOD
+#include "AudioSourceFilter.h"
+
+IMPLEMENT_CLASS(AudioFilter)
+IMPLEMENT_OBJECT_SERIALIZE (AudioFilter)
+INSTANTIATE_TEMPLATE_TRANSFER (AudioFilter)
+
+AudioFilter::~AudioFilter ()
+{
+ Cleanup();
+}
+
+void AudioFilter::Cleanup()
+{
+ if (m_DSP)
+ {
+ m_DSP->release();
+ m_DSP = NULL;
+ }
+}
+
+void AudioFilter::AddToManager()
+{
+ if (!m_DSP)
+ Init();
+ Update();
+ Assert(m_DSP);
+ Assert(m_Type != FMOD_DSP_TYPE_UNKNOWN);
+ m_DSP->setBypass(false);
+}
+
+void AudioFilter::RemoveFromManager()
+{
+ if (m_DSP)
+ m_DSP->setBypass(true);
+}
+
+void AudioFilter::AwakeFromLoad(AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad(mode);
+ Update();
+}
+
+void AudioFilter::Init()
+{
+ if (m_DSP == NULL && m_Type != FMOD_DSP_TYPE_FORCEINT)
+ {
+ FMOD_RESULT result = GetAudioManager().GetFMODSystem()->createDSPByType(m_Type, &m_DSP);
+ Assert (result == FMOD_OK);
+ result = m_DSP->setBypass(!GetEnabled());
+ Assert (result == FMOD_OK);
+ }
+}
+
+FMOD::DSP* AudioFilter::GetDSP()
+{
+ if (!m_DSP)
+ Init();
+ return m_DSP;
+}
+
+template<class TransferFunction>
+void AudioFilter::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+}
+#endif //ENABLE_AUDIO \ No newline at end of file
diff --git a/Runtime/Audio/AudioSourceFilter.h b/Runtime/Audio/AudioSourceFilter.h
new file mode 100644
index 0000000..72795b3
--- /dev/null
+++ b/Runtime/Audio/AudioSourceFilter.h
@@ -0,0 +1,36 @@
+#ifndef __AUDIOSOURCE_FILTER_H__
+#define __AUDIOSOURCE_FILTER_H__
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "AudioManager.h"
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Audio/correct_fmod_includer.h"
+
+#if ENABLE_AUDIO_FMOD
+
+using namespace Unity;
+
+class AudioFilter : public Behaviour
+{
+public:
+ REGISTER_DERIVED_ABSTRACT_CLASS (AudioFilter, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (AudioFilter)
+
+ AudioFilter(MemLabelId label, ObjectCreationMode mode) : Behaviour(label, mode), m_DSP(NULL), m_Type(FMOD_DSP_TYPE_UNKNOWN) {}
+
+ FMOD::DSP* GetDSP();
+
+ virtual void RemoveFromManager();
+ virtual void AddToManager();
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ void Init();
+ void Cleanup();
+protected:
+ FMOD_DSP_TYPE m_Type;
+ FMOD::DSP* m_DSP;
+ friend class AudioManager;
+};
+
+#endif //ENABLE_AUDIO
+#endif // __AUDIOSOURCE_FILTER_H__
diff --git a/Runtime/Audio/AudioTypes.h b/Runtime/Audio/AudioTypes.h
new file mode 100644
index 0000000..38bbc66
--- /dev/null
+++ b/Runtime/Audio/AudioTypes.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "Runtime/Audio/correct_fmod_includer.h" // can't forward declare enums (@TODO use ints?)
+
+// macros/helpers
+#if ENABLE_AUDIO_FMOD
+#define UNITYVEC2FMODVEC(v) *(reinterpret_cast<FMOD_VECTOR*>(&v))
+#define UNITYVEC2FMODVECPTR(v) (reinterpret_cast<FMOD_VECTOR*>(&v))
+
+#define FMOD_ASSERT(x) {Assert(x == FMOD_OK);\
+if (x != FMOD_OK){ ErrorString(FMOD_ErrorString(result));}}
+
+typedef FMOD::Channel AudioChannel;
+
+
+#endif //ENABLE_AUDIO_FMOD
diff --git a/Runtime/Audio/OggReader.h b/Runtime/Audio/OggReader.h
new file mode 100644
index 0000000..fca185a
--- /dev/null
+++ b/Runtime/Audio/OggReader.h
@@ -0,0 +1,51 @@
+#ifndef __OGG_READER_H__
+#define __OGG_READER_H__
+
+
+ inline bool CheckOggVorbisFile (const UInt8* ogg_memory, size_t size, int* outChannels)
+ {
+ *outChannels = 0;
+ if (size < 40) return false;
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1| Byte
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | capture_pattern: Magic number for page start "OggS" | 0-3
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | version | header_type | granule_position | 4-7
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | | 8-11
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | | bitstream_serial_number | 12-15
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | | page_sequence_number | 16-19
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | | CRC_checksum | 20-23
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |page_segments | segment_table | 24-27
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ... | 28-
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ // capture pattern (OggS)
+ if (0x4f != ogg_memory[0]) return false;
+ if (0x67 != ogg_memory[1]) return false;
+ if (0x67 != ogg_memory[2]) return false;
+ if (0x53 != ogg_memory[3]) return false;
+ // version
+ if (0 != ogg_memory[4]) return false;
+ // ensure its the first page(2) (and not last(1) and not a continued page(4))
+ const UInt8 page_flag = ogg_memory[5];
+ if (!((2 & page_flag) && !(1 & page_flag) && !(4 & page_flag))) return false;
+ // vorbis packet id (must be #1)
+ if (1 != ogg_memory[28]) return false;
+ // vorbis header
+ if (memcmp((void*)&ogg_memory[29], "vorbis", 6) != 0) return false;
+ // vorbis version [35-38]
+ *outChannels = ogg_memory[39];
+
+ return true;
+}
+
+#endif // __OGG_READER_H__
diff --git a/Runtime/Audio/ScriptBindings/AudioBindings.txt b/Runtime/Audio/ScriptBindings/AudioBindings.txt
new file mode 100644
index 0000000..57ed241
--- /dev/null
+++ b/Runtime/Audio/ScriptBindings/AudioBindings.txt
@@ -0,0 +1,996 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Profiler/ProfilerHistory.h"
+#include "Runtime/Allocator/MemoryManager.h"
+#include "Runtime/Audio/AudioClip.h"
+#if ENABLE_AUDIO
+#include "Runtime/Audio/AudioSource.h"
+#include "Runtime/Audio/AudioListener.h"
+#include "Runtime/Audio/AudioManager.h"
+#include "Runtime/Audio/AudioReverbZone.h"
+#include "Runtime/Audio/AudioReverbFilter.h"
+#include "Runtime/Audio/AudioHighPassFilter.h"
+#include "Runtime/Audio/AudioLowPassFilter.h"
+#include "Runtime/Audio/AudioChorusFilter.h"
+#include "Runtime/Audio/AudioDistortionFilter.h"
+#include "Runtime/Audio/AudioEchoFilter.h"
+#endif
+#include "Runtime/Animation/Animation.h"
+#include "Runtime/Utilities/PlayerPrefs.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/GetComponent.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/Scripting.h"
+
+using namespace Unity;
+
+/*
+ Mono defines a bool as either 1 or 2 bytes.
+ On windows a bool on the C++ side needs to be 2 bytes.
+ We use the typemap to map bool's to short's.
+ When using the C++ keyword and you want to export a bool value
+ to mono you have to use a short on the C++ side.
+*/
+
+
+void PauseEditor ();
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngineInternal;
+
+namespace UnityEngine
+{
+
+
+
+// These are speaker types defined for use with [[AudioSettings.speakerMode]].
+CONDITIONAL ENABLE_AUDIO_FMOD
+ENUM AudioSpeakerMode
+ // Channel count is unaffected.
+ Raw = 0,
+ // Channel count is set to 1. The speakers are monaural.
+ Mono = 1,
+ // Channel count is set to 2. The speakers are stereo. This is the editor default.
+ Stereo = 2,
+ // Channel count is set to 4. 4 speaker setup. This includes front left, front right, rear left, rear right.
+ Quad = 3,
+ // Channel count is set to 5. 5 speaker setup. This includes front left, front right, center, rear left, rear right.
+ Surround = 4,
+ // Channel count is set to 6. 5.1 speaker setup. This includes front left, front right, center, rear left, rear right and a subwoofer.
+ Mode5point1 = 5,
+ // Channel count is set to 8. 7.1 speaker setup. This includes front left, front right, center, rear left, rear right, side left, side right and a subwoofer.
+ Mode7point1 = 6,
+ // Channel count is set to 2. Stereo output, but data is encoded in a way that is picked up by a Prologic/Prologic2 decoder and split into a 5.1 speaker setup.
+ Prologic = 7
+END
+
+// Controls the global audio settings from script.
+CONDITIONAL ENABLE_AUDIO_FMOD
+CLASS AudioSettings
+ // Returns the speaker mode capability of the current audio driver. (RO)
+ CUSTOM_PROP static AudioSpeakerMode driverCaps
+ {
+ return GetAudioManager().GetSpeakerModeCaps();
+ }
+
+ // Sets or gets the current speaker mode. Default is 2 channel stereo.
+ CUSTOM_PROP static AudioSpeakerMode speakerMode
+ {
+ return GetAudioManager().GetSpeakerMode();
+ }
+ {
+ GetAudioManager().SetSpeakerMode(value);
+ }
+
+ // Returns the current time of the audio system. This is based on the number of samples the audio system processes and is therefore more exact than the time obtained via the Time.time property.
+ // It is constant while Unity is paused.
+ THREAD_SAFE CUSTOM_PROP static double dspTime
+ {
+ return GetAudioManager().GetDSPTime();
+ }
+
+ // Get and set the mixer's current output rate.
+ CUSTOM_PROP static int outputSampleRate
+ {
+ int sampleRate;
+ GetAudioManager().GetFMODSystem()->getSoftwareFormat(
+ &sampleRate,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ );
+ return sampleRate;
+ }
+ {
+ int currentSampleRate = AudioSettings_Get_Custom_PropOutputSampleRate();
+ if (currentSampleRate != value)
+ {
+ GetAudioManager().CloseFMOD();
+ FMOD_RESULT result = GetAudioManager().GetFMODSystem()->setSoftwareFormat(
+ value,
+ FMOD_SOUND_FORMAT_PCM16,
+ 0,
+ 8,
+ FMOD_DSP_RESAMPLER_LINEAR
+ );
+ if (result != FMOD_OK)
+ {
+ ErrorString(Format("%dHz is an invalid output samplerate for this platform", value));
+ }
+
+ GetAudioManager().ReloadFMODSounds();
+ }
+ }
+
+ // Get or set the mixer's buffer size in samples.
+ CUSTOM static void SetDSPBufferSize(int bufferLength, int numBuffers)
+ {
+ bufferLength = clamp(bufferLength, 64, 4096);
+
+ GetAudioManager().CloseFMOD();
+ FMOD_RESULT result = GetAudioManager().GetFMODSystem()->setDSPBufferSize(bufferLength, numBuffers);
+ if (result != FMOD_OK)
+ {
+ ErrorString(Format("DSP ringbuffer of %d samples (x %d) is invalid for this platform", bufferLength, numBuffers));
+ }
+ GetAudioManager().ReloadFMODSounds();
+ }
+
+ // Get or set the mixer's buffer size in samples.
+ CUSTOM static void GetDSPBufferSize(out int bufferLength, out int numBuffers)
+ {
+ FMOD_RESULT result = GetAudioManager().GetFMODSystem()->getDSPBufferSize((unsigned int*)bufferLength, numBuffers);
+ FMOD_ASSERT( result );
+ }
+
+
+END
+
+// Type of the imported(native) data
+ENUM public AudioType
+ // 3rd party / unknown plugin format.
+ UNKNOWN = 0,
+ //acc - not supported
+ ACC = 1, /* [Unity] Not supported/used. But kept here to keep the order of the enum in sync. */
+ //aiff
+ AIFF = 2,
+// ASF = 3, /* Microsoft Advanced Systems Format (ie WMA/ASF/WMV). */
+// AT3 = 4, /* Sony ATRAC 3 format */
+// CDDA = 5, /* Digital CD audio. */
+// DLS = 6, /* Sound font / downloadable sound bank. */
+// FLAC = 7, /* FLAC lossless codec. */
+// FSB = 8, /* FMOD Sample Bank. */
+ //game cube ADPCM
+ GCADPCM = 9,
+ //impulse tracker
+ IT = 10,
+// MIDI = 11, /* MIDI. */
+ //Protracker / Fasttracker MOD.
+ MOD = 12,
+ //MP2/MP3 MPEG.
+ MPEG = 13,
+ //ogg vorbis
+ OGGVORBIS = 14,
+// PLAYLIST = 15, /* Information only from ASX/PLS/M3U/WAX playlists */
+// RAW = 16, /* Raw PCM data. */
+ // ScreamTracker 3.
+ S3M = 17,
+// SF2 = 18, /* Sound font 2 format. */
+// USER = 19, /* User created sound. */
+ //Microsoft WAV.
+ WAV = 20,
+ // FastTracker 2 XM.
+ XM = 21,
+ // Xbox360 XMA
+ XMA = 22,
+// VAG = 23, /* PlayStation 2 / PlayStation Portable adpcm VAG format. */
+ //iPhone hardware decoder, supports AAC, ALAC and MP3. Extracodecdata is a pointer to an FMOD_AUDIOQUEUE_EXTRACODECDATA structure.
+ AUDIOQUEUE = 24,
+// XWMA = 25, /* Xbox360 XWMA */
+// BCWAV = 26, /* 3DS BCWAV container format for DSP ADPCM and PCM */
+// AT9 = 27, /* NGP ATRAC 9 format */
+END
+
+// A container for audio data.
+CONDITIONAL ENABLE_AUDIO
+CLASS AudioClip : Object
+
+ // Check if reading of the audioclip is allowed by crossdomain security and throw if not
+ C++RAW
+ static void CheckReadAllowedAndThrow(AudioClip *clip)
+ {
+#if ENABLE_MONO && ENABLE_SECURITY && ENABLE_WWW
+ if ( clip&&!clip->GetReadAllowed() )
+ Scripting::RaiseSecurityException("No read access to the audioclip data: %s", clip->GetName());
+#endif
+ }
+
+ // The length of the audio clip in seconds (RO)
+ AUTO_PROP float length GetLengthSec
+
+ // The length of the audio clip in samples (RO)
+ // Prints how many samples the attached audio source has
+ //
+ AUTO_PROP int samples GetSampleCount
+
+ // Channels in audio clip (RO)
+ AUTO_PROP int channels GetChannelCount
+
+ // Sample frequency (RO)
+ AUTO_PROP int frequency GetFrequency
+
+ // Is a streamed audio clip ready to play? (RO)
+ AUTO_PROP bool isReadyToPlay ReadyToPlay
+
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ // Fills an array with sample data from the clip. The samples are floats ranging from -1.0f to 1.0f. The sample count is determined by the length of the float array.
+ CUSTOM void GetData(float[] data, int offsetSamples)
+ {
+ CheckReadAllowedAndThrow(self);
+ self->GetData(&Scripting::GetScriptingArrayElement<float>(data, 0), GetScriptingArraySize (data) / self->GetChannelCount(), offsetSamples);
+ }
+
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ // Set sample data in a clip. The samples should be floats ranging from 0.0f to 1.0f (exceeding these limits will lead to artifacts and undefined behaviour).
+ CUSTOM void SetData(float[] data, int offsetSamples)
+ {
+ self->SetData(&Scripting::GetScriptingArrayElement<float>(data, 0), GetScriptingArraySize (data) / self->GetChannelCount(), offsetSamples);
+ }
+
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ /// *listonly*
+ CSRAW public static AudioClip Create(string name, int lengthSamples, int channels, int frequency, bool _3D, bool stream)
+ {
+ AudioClip clip = Create (name, lengthSamples, channels, frequency, _3D, stream, null, null);
+ return clip;
+ }
+
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ /// *listonly*
+ CSRAW public static AudioClip Create(string name, int lengthSamples, int channels, int frequency, bool _3D, bool stream, PCMReaderCallback pcmreadercallback)
+ {
+ AudioClip clip = Create (name, lengthSamples, channels, frequency, _3D, stream, pcmreadercallback, null);
+ return clip;
+ }
+
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ // Creates a user AudioClip with a name and with the given length in samples, channels and frequency.
+ CSRAW public static AudioClip Create(string name, int lengthSamples, int channels, int frequency, bool _3D, bool stream, PCMReaderCallback pcmreadercallback, PCMSetPositionCallback pcmsetpositioncallback)
+ {
+ if(name == null) throw new NullReferenceException();
+
+ AudioClip clip = Construct_Internal();
+ if ( pcmreadercallback != null)
+ clip.m_PCMReaderCallback += pcmreadercallback;
+ if ( pcmsetpositioncallback != null)
+ clip.m_PCMSetPositionCallback += pcmsetpositioncallback;
+
+ clip.Init_Internal( name, lengthSamples, channels, frequency, _3D, stream );
+
+ return clip;
+ }
+
+ /// *listonly*
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ CSRAW public delegate void PCMReaderCallback(float[] data);
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ CSRAW private event PCMReaderCallback m_PCMReaderCallback = null;
+ /// *listonly*
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ CSRAW public delegate void PCMSetPositionCallback(int position);
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ CSRAW private event PCMSetPositionCallback m_PCMSetPositionCallback = null;
+
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ CSRAW private void InvokePCMReaderCallback_Internal(float[] data)
+ {
+ if (m_PCMReaderCallback != null)
+ m_PCMReaderCallback( data );
+ }
+
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ CSRAW private void InvokePCMSetPositionCallback_Internal(int position)
+ {
+ if (m_PCMSetPositionCallback != null)
+ m_PCMSetPositionCallback( position );
+ }
+
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ CUSTOM private static AudioClip Construct_Internal()
+ {
+ AudioClip* clip = NEW_OBJECT(AudioClip);
+ return Scripting::ScriptingWrapperFor ( clip );
+ }
+
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ CUSTOM private void Init_Internal(string name, int lengthSamples, int channels, int frequency, bool _3D, bool stream)
+ {
+ self->CreateUserSound( name, lengthSamples, channels, frequency, _3D, stream );
+ }
+
+END
+
+// Describes when an [[AudioSource]] or [[AudioListener]] is updated.
+ENUM AudioVelocityUpdateMode
+ // Updates the source or listener in the fixed update loop if it is attached to a [[Rigidbody]], dynamic otherwise.
+ Auto = 0,
+ // Updates the source or listener in the fixed update loop.
+ Fixed = 1,
+ // Updates the source or listener in the dynamic update loop.
+ Dynamic = 2,
+END
+
+
+// Representation of a listener in 3D space.
+CONDITIONAL ENABLE_AUDIO
+CLASS AudioListener : Behaviour
+ // Controls the game sound volume (0.0 to 1.0)
+ CUSTOM_PROP static float volume { return GetAudioManager ().GetVolume (); } { GetAudioManager ().SetVolume (value); }
+
+ // The paused state of the audio. If set to True, the listener will not generate sound.
+ CUSTOM_PROP static bool pause { return GetAudioManager ().GetPause (); } { GetAudioManager ().SetPause (value); }
+
+ // This lets you set whether the Audio Listener should be updated in the fixed or dynamic update.
+
+ AUTO_PROP AudioVelocityUpdateMode velocityUpdateMode GetVelocityUpdateMode SetVelocityUpdateMode
+
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ CUSTOM static private void GetOutputDataHelper(float[] samples, int channel)
+ {
+ FMOD::ChannelGroup* channelGroup;
+ FMOD_RESULT result = GetAudioManager().GetFMODSystem()->getMasterChannelGroup(&channelGroup);
+
+ if (result == FMOD_OK && channelGroup)
+ {
+ channelGroup->getWaveData(Scripting::GetScriptingArrayStart<float>(samples), GetScriptingArraySize(samples), channel);
+ }
+ }
+
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ CUSTOM static private void GetSpectrumDataHelper(float[] samples, int channel, FFTWindow window)
+ {
+ FMOD::ChannelGroup* channelGroup;
+ FMOD_RESULT result = GetAudioManager().GetFMODSystem()->getMasterChannelGroup(&channelGroup);
+
+ if (result == FMOD_OK && channelGroup)
+ {
+ channelGroup->getSpectrum(Scripting::GetScriptingArrayStart<float>(samples), GetScriptingArraySize(samples), channel, (FMOD_DSP_FFT_WINDOW)window);
+ }
+ }
+
+ // Returns a block of the listener (master)'s output data
+ CSRAW
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ OBSOLETE warning GetOutputData returning a float[] is deprecated, use GetOutputData and pass a pre allocated array instead.
+ public static float[] GetOutputData(int numSamples, int channel)
+ {
+ float[] samples = new float[numSamples];
+ GetOutputDataHelper(samples, channel);
+ return samples;
+ }
+
+ // Returns a block of the listener (master)'s output data
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ CSRAW static public void GetOutputData(float[] samples, int channel)
+ {
+ GetOutputDataHelper(samples, channel);
+ }
+
+ // Returns a block of the listener (master)'s spectrum data
+ CSRAW
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ OBSOLETE warning GetSpectrumData returning a float[] is deprecated, use GetOutputData and pass a pre allocated array instead.
+ public static float[] GetSpectrumData(int numSamples, int channel, FFTWindow window)
+ {
+ float[] samples = new float[numSamples];
+ GetSpectrumDataHelper(samples, channel, window);
+ return samples;
+ }
+
+ // Returns a block of the listener (master)'s spectrum data
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ CSRAW static public void GetSpectrumData(float[] samples, int channel, FFTWindow window)
+ {
+ GetSpectrumDataHelper(samples, channel, window);
+ }
+
+END
+
+
+// Spectrum analysis windowing types
+CONDITIONAL ENABLE_AUDIO_FMOD
+ENUM FFTWindow
+ // w[n] = 1.0
+ Rectangular = 0,
+ // w[n] = TRI(2n/N)
+ Triangle = 1,
+ // w[n] = 0.54 - (0.46 * COS(n/N) )
+ Hamming = 2,
+ // w[n] = 0.5 * (1.0 - COS(n/N) )
+ Hanning = 3,
+ // w[n] = 0.42 - (0.5 * COS(n/N) ) + (0.08 * COS(2.0 * n/N) )
+ Blackman = 4,
+ // w[n] = 0.35875 - (0.48829 * COS(1.0 * n/N)) + (0.14128 * COS(2.0 * n/N)) - (0.01168 * COS(3.0 * n/N))
+ BlackmanHarris = 5
+END
+
+// Rolloff modes that a 3D sound can have in an audio source.
+CONDITIONAL ENABLE_AUDIO
+ENUM AudioRolloffMode
+ // Use this mode when you want a real-world rolloff.
+ Logarithmic = 0,
+
+
+ // Use this mode when you want to lower the volume of your sound over the distance
+ Linear = 1,
+
+
+ // Use this when you want to use a custom rolloff.
+ Custom = 2
+END
+
+// A representation of audio sources in 3D.
+CONDITIONAL ENABLE_AUDIO
+CLASS AudioSource : Behaviour
+ // The volume of the audio source (0.0 to 1.0)
+ AUTO_PROP float volume GetVolume SetVolume
+
+ // The pitch of the audio source.
+ CUSTOM_PROP float pitch
+ {
+ return self->GetPitch();
+ }
+ {
+ if(!IsFinite(value))
+ {
+ WarningStringObject("Attempt to set pitch to infinite value from script ignored!", self);
+ return;
+ }
+ if(IsNAN(value))
+ {
+ WarningStringObject("Attempt to set pitch to NaN value from script ignored!", self);
+ return;
+ }
+ self->SetPitch(value);
+ }
+
+ // Playback position in seconds.
+ CONDITIONAL ENABLE_AUDIO
+ AUTO_PROP float time GetSecPosition SetSecPosition
+
+ // Playback position in PCM samples.
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ THREAD_SAFE AUTO_PROP int timeSamples GetSamplePosition SetSamplePosition
+
+ // The default [[AudioClip]] to play
+ AUTO_PTR_PROP AudioClip clip GetAudioClip SetAudioClip
+
+ // Plays the ::ref::clip with a certain delay (the optional delay argument is deprecated since 4.1a3) and the functionality has been replated by PlayDelayed.
+
+ CUSTOM void Play (UInt64 delay=0)
+ {
+ if (delay > 0 && IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_1_a3))
+ {
+ WarningStringObject("Delayed playback via the optional argument of Play is deprecated. Use PlayDelayed instead!", self);
+ }
+ self->Play((double)delay * (const double)(-1.0 / 44100.0));
+ }
+
+ // Plays the ::ref::clip with a delay specified in seconds. Users are advised to use this function instead of the old Play(delay) function that took a delay specified in samples relative to a reference rate of 44.1 kHz as an argument.
+ CUSTOM void PlayDelayed (float delay)
+ {
+ self->Play((delay < 0.0f) ? 0.0 : -(double)delay);
+ }
+
+ // Schedules the ::ref::clip to play at the specified absolute time. This is the preferred way to stitch AudioClips in music players because it is independent of the frame rate and gives the audio system enough time to prepare the playback of the sound to fetch it from media where the opening and buffering takes a lot of time (streams) without causing sudden performance peaks.
+ CUSTOM void PlayScheduled (double time)
+ {
+ self->Play((time < 0.0) ? 0.0 : time);
+ }
+
+ // Changes the time at which a sound that has already been scheduled to play will start. Notice that depending on the timing not all rescheduling requests can be fulfilled.
+ CUSTOM void SetScheduledStartTime (double time)
+ {
+ self->SetScheduledStartTime(time);
+ }
+
+ // Changes the time at which a sound that has already been scheduled to play will end. Notice that depending on the timing not all rescheduling requests can be fulfilled.
+ CUSTOM void SetScheduledEndTime (double time)
+ {
+ self->SetScheduledEndTime(time);
+ }
+
+ // Stops playing the ::ref::clip.
+ CUSTOM void Stop()
+ {
+ self->Stop(true);
+ }
+
+ // Pauses playing the ::ref::clip.
+ AUTO void Pause ();
+
+ // Is the ::ref::clip playing right now (RO)?
+ AUTO_PROP bool isPlaying IsPlayingScripting
+
+ // Plays an [[AudioClip]], and scales the [[AudioSource]] volume by volumeScale.
+ CONDITIONAL ENABLE_AUDIO
+ CUSTOM void PlayOneShot (AudioClip clip, float volumeScale = 1.0F) { if (clip) self->PlayOneShot (*clip, volumeScale); }
+
+ // Plays the clip at position. Automatically cleans up the audio source after it has finished playing.
+
+ CONDITIONAL ENABLE_AUDIO
+ CSRAW public static void PlayClipAtPoint (AudioClip clip, Vector3 position, float volume = 1.0F)
+ {
+ GameObject go = new GameObject ("One shot audio");
+ go.transform.position = position;
+ AudioSource source = (AudioSource)go.AddComponent (typeof(AudioSource));
+ source.clip = clip;
+ source.volume = volume;
+ source.Play ();
+ // Note: timeScale > 1 means that game time is accelerated. However, the sounds play at their normal speed, so we need to postpone the point in time, when the sound is stopped.
+ Destroy (go, clip.length * Time.timeScale);
+ }
+
+ // Is the audio clip looping?
+ AUTO_PROP bool loop GetLoop SetLoop
+
+ // This makes the audio source not take into account the volume of the audio listener.
+ CUSTOM_PROP bool ignoreListenerVolume
+ {
+ return self->GetIgnoreListenerVolume();
+ }
+ {
+ self->SetIgnoreListenerVolume(value);
+ }
+
+ // If set to true, the audio source will automatically start playing on awake
+ AUTO_PROP bool playOnAwake GetPlayOnAwake SetPlayOnAwake
+
+ // If set to true, the audio source will be playable while the AudioListener is paused
+ AUTO_PROP bool ignoreListenerPause GetIgnoreListenerPause SetIgnoreListenerPause
+
+ // Whether the Audio Source should be updated in the fixed or dynamic update.
+ AUTO_PROP AudioVelocityUpdateMode velocityUpdateMode GetVelocityUpdateMode SetVelocityUpdateMode
+
+
+ // Sets how much the 3d engine has an effect on the channel.
+ AUTO_PROP float panLevel GetPanLevel SetPanLevel
+
+ // Bypass effects
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ AUTO_PROP bool bypassEffects GetBypassEffects SetBypassEffects
+
+ // Bypass listener effects
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ AUTO_PROP bool bypassListenerEffects GetBypassListenerEffects SetBypassListenerEffects
+ // Bypass reverb zones
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ AUTO_PROP bool bypassReverbZones GetBypassReverbZones SetBypassReverbZones
+
+ // Sets the Doppler scale for this AudioSource
+ AUTO_PROP float dopplerLevel GetDopplerLevel SetDopplerLevel
+
+ // Sets the spread angle a 3d stereo or multichannel sound in speaker space.
+ AUTO_PROP float spread GetSpread SetSpread
+
+ // Sets the priority of the [[AudioSource]]
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ AUTO_PROP int priority GetPriority SetPriority
+
+ // Un- / Mutes the AudioSource. Mute sets the volume=0, Un-Mute restore the original volume.
+ AUTO_PROP bool mute GetMute SetMute
+
+ // Within the Min distance the AudioSource will cease to grow louder in volume.
+ AUTO_PROP float minDistance GetMinDistance SetMinDistance
+
+ // (Logarithmic rolloff) MaxDistance is the distance a sound stops attenuating at.
+ AUTO_PROP float maxDistance GetMaxDistance SetMaxDistance
+
+ // Sets a channels pan position linearly. Only works for 2D clips.
+ AUTO_PROP float pan GetPan SetPan
+
+ // Sets/Gets how the AudioSource attenuates over distance
+
+ AUTO_PROP AudioRolloffMode rolloffMode GetRolloffMode SetRolloffMode
+
+ CUSTOM private void GetOutputDataHelper(float[] samples, int channel)
+ {
+#if ENABLE_AUDIO_FMOD
+ self->GetOutputData(Scripting::GetScriptingArrayStart<float>(samples), GetScriptingArraySize(samples), channel);
+#endif
+ }
+
+ // Returns a block of the currently playing source's output data
+ CSRAW
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ OBSOLETE warning GetOutputData return a float[] is deprecated, use GetOutputData passing a pre allocated array instead.
+ public float[] GetOutputData(int numSamples, int channel)
+ {
+ float[] samples = new float[numSamples];
+ GetOutputDataHelper(samples, channel);
+ return samples;
+ }
+
+ // Returns a block of the currently playing source's output data
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ CSRAW public void GetOutputData(float[] samples, int channel)
+ {
+ GetOutputDataHelper(samples, channel);
+ }
+
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ CUSTOM private void GetSpectrumDataHelper(float[] samples, int channel, FFTWindow window)
+ {
+ self->GetSpectrumData(Scripting::GetScriptingArrayStart<float>(samples), GetScriptingArraySize(samples), channel, (FMOD_DSP_FFT_WINDOW) window);
+ }
+
+ // Returns a block of the currently playing source's spectrum data
+ CSRAW
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ OBSOLETE warning GetSpectrumData returning a float[] is deprecated, use GetSpectrumData passing a pre allocated array instead.
+ public float[] GetSpectrumData(int numSamples, int channel, FFTWindow window)
+ {
+ float[] samples = new float[numSamples];
+ GetSpectrumDataHelper(samples, channel, window);
+ return samples;
+ }
+
+ // Returns a block of the currently playing source's spectrum data
+ CONDITIONAL ENABLE_AUDIO_FMOD
+ CSRAW public void GetSpectrumData(float[] samples, int channel, FFTWindow window)
+ {
+ GetSpectrumDataHelper(samples, channel, window);
+ }
+
+ FLUSHCONDITIONS
+
+ OBSOLETE error minVolume is not supported anymore. Use min-, maxDistance and rolloffMode instead.
+ CUSTOM_PROP float minVolume
+ {
+ ErrorString("minVolume is not supported anymore. Use min-, maxDistance and rolloffMode instead.");
+ return 0.0f;
+ }
+ {
+ ErrorString("minVolume is not supported anymore. Use min-, maxDistance and rolloffMode instead.");
+ }
+ OBSOLETE error maxVolume is not supported anymore. Use min-, maxDistance and rolloffMode instead.
+ CUSTOM_PROP float maxVolume
+ {
+ ErrorString("maxVolume is not supported anymore. Use min-, maxDistance and rolloffMode instead.");
+ return 0.0f;
+ }
+ {
+ ErrorString("maxVolume is not supported anymore. Use min-, maxDistance and rolloffMode instead.");
+ }
+ OBSOLETE error rolloffFactor is not supported anymore. Use min-, maxDistance and rolloffMode instead.
+ CUSTOM_PROP float rolloffFactor
+ {
+ ErrorString("rolloffFactor is not supported anymore. Use min-, maxDistance and rolloffMode instead.");
+ return 0.0f;
+ }
+ {
+ ErrorString("rolloffFactor is not supported anymore. Use min-, maxDistance and rolloffMode instead.");
+ }
+END
+// Reverb presets used by the Reverb Zone class and the audio reverb filter
+ENUM AudioReverbPreset
+ // No reverb preset selected
+ Off = 0,
+
+ // Generic preset.
+ Generic = 1,
+
+ // Padded cell preset.
+ PaddedCell = 2,
+
+ // Room preset.
+ Room = 3,
+
+ // Bathroom preset.
+ Bathroom = 4,
+
+ // Livingroom preset
+ Livingroom = 5,
+
+ // Stoneroom preset
+ Stoneroom = 6,
+
+ // Auditorium preset.
+ Auditorium = 7,
+
+ // Concert hall preset.
+ Concerthall = 8,
+
+ // Cave preset.
+ Cave = 9,
+
+ // Arena preset.
+ Arena = 10,
+
+ // Hangar preset.
+ Hangar = 11,
+
+ // Carpeted hallway preset.
+ CarpetedHallway = 12,
+
+ // Hallway preset.
+ Hallway = 13,
+
+ // Stone corridor preset.
+ StoneCorridor = 14,
+
+ // Alley preset.
+ Alley = 15,
+
+ // Forest preset.
+ Forest = 16,
+
+ // City preset.
+ City = 17,
+
+ // Mountains preset.
+ Mountains = 18,
+
+ // Quarry preset.
+ Quarry = 19,
+
+ // Plain preset.
+ Plain = 20,
+
+ // Parking Lot preset
+ ParkingLot = 21,
+
+ // Sewer pipe preset.
+ SewerPipe = 22,
+
+ // Underwater presset
+ Underwater = 23,
+
+ // Drugged preset
+ Drugged = 24,
+
+ // Dizzy preset.
+ Dizzy = 25,
+
+ // Psychotic preset.
+ Psychotic = 26,
+
+ // User defined preset.
+ User = 27
+END
+
+// Reverb Zones are used when you want to gradually change from a point
+CONDITIONAL ENABLE_AUDIO_FMOD
+CLASS AudioReverbZone : Behaviour
+ // The distance from the centerpoint that the reverb will have full effect at. Default = 10.0.
+ AUTO_PROP float minDistance GetMinDistance SetMinDistance
+
+ // The distance from the centerpoint that the reverb will not have any effect. Default = 15.0.
+ AUTO_PROP float maxDistance GetMaxDistance SetMaxDistance
+
+ // Set/Get reverb preset properties
+ AUTO_PROP AudioReverbPreset reverbPreset GetReverbPreset SetReverbPreset
+
+ // room effect level (at mid frequencies)
+ AUTO_PROP int room GetRoom SetRoom
+ // relative room effect level at high frequencies
+ AUTO_PROP int roomHF GetRoomHF SetRoomHF
+ // relative room effect level at low frequencies
+ AUTO_PROP int roomLF GetRoomLF SetRoomLF
+ // reverberation decay time at mid frequencies
+ AUTO_PROP float decayTime GetDecayTime SetDecayTime
+ // high-frequency to mid-frequency decay time ratio
+ AUTO_PROP float decayHFRatio GetDecayHFRatio SetDecayHFRatio
+ // early reflections level relative to room effect
+ AUTO_PROP int reflections GetReflections SetReflections
+ // initial reflection delay time
+ AUTO_PROP float reflectionsDelay GetReflectionsDelay SetReflectionsDelay
+ // late reverberation level relative to room effect
+ AUTO_PROP int reverb GetReverb SetReverb
+ // late reverberation delay time relative to initial reflection
+ AUTO_PROP float reverbDelay GetReverbDelay SetReverbDelay
+ // reference high frequency (hz)
+ AUTO_PROP float HFReference GetHFReference SetHFReference
+ // reference low frequency (hz)
+ AUTO_PROP float LFReference GetLFReference SetLFReference
+ // like rolloffscale in global settings, but for reverb room size effect
+ AUTO_PROP float roomRolloffFactor GetRoomRolloffFactor SetRoomRolloffFactor
+ // Value that controls the echo density in the late reverberation decay
+ AUTO_PROP float diffusion GetDiffusion SetDiffusion
+ // Value that controls the modal density in the late reverberation decay
+ AUTO_PROP float density GetDensity SetDensity
+END
+
+
+// The Audio Low Pass Filter filter passes low frequencies of an
+CONDITIONAL ENABLE_AUDIO_FMOD
+CLASS AudioLowPassFilter : Behaviour
+ // Lowpass cutoff frequency in hz. 10.0 to 22000.0. Default = 5000.0.
+ AUTO_PROP float cutoffFrequency GetCutoffFrequency SetCutoffFrequency
+
+ // Determines how much the filter's self-resonance is dampened.
+ AUTO_PROP float lowpassResonaceQ GetLowpassResonanceQ SetLowpassResonanceQ
+END
+// The Audio High Pass Filter passes high frequencies of an AudioSource and
+CONDITIONAL ENABLE_AUDIO_FMOD
+CLASS AudioHighPassFilter : Behaviour
+ // Highpass cutoff frequency in hz. 10.0 to 22000.0. Default = 5000.0.
+ AUTO_PROP float cutoffFrequency GetCutoffFrequency SetCutoffFrequency
+
+
+ // Determines how much the filter's self-resonance isdampened.
+ AUTO_PROP float highpassResonaceQ GetHighpassResonanceQ SetHighpassResonanceQ
+END
+// The Audio Distortion Filter distorts the sound from an AudioSource or
+CONDITIONAL ENABLE_AUDIO_FMOD
+CLASS AudioDistortionFilter : Behaviour
+ // Distortion value. 0.0 to 1.0. Default = 0.5.
+ AUTO_PROP float distortionLevel GetDistortionLevel SetDistortionLevel
+END
+
+// The Audio Echo Filter repeats a sound after a given Delay, attenuating
+CONDITIONAL ENABLE_AUDIO_FMOD
+CLASS AudioEchoFilter : Behaviour
+ // Echo delay in ms. 10 to 5000. Default = 500.
+ AUTO_PROP float delay GetDelay SetDelay
+
+
+ // Echo decay per delay. 0 to 1. 1.0 = No decay, 0.0 = total decay (i.e. simple 1 line delay). Default = 0.5.
+ AUTO_PROP float decayRatio GetDecayRatio SetDecayRatio
+
+
+ // Volume of original signal to pass to output. 0.0 to 1.0. Default = 1.0.
+ AUTO_PROP float dryMix GetDryMix SetDryMix
+
+
+ // Volume of echo signal to pass to output. 0.0 to 1.0. Default = 1.0.
+ AUTO_PROP float wetMix GetWetMix SetWetMix
+END
+
+// The Audio Chorus Filter takes an Audio Clip and processes it creating a chorus effect.
+CONDITIONAL ENABLE_AUDIO_FMOD
+CLASS AudioChorusFilter : Behaviour
+ // Volume of original signal to pass to output. 0.0 to 1.0. Default = 0.5.
+ AUTO_PROP float dryMix GetDryMix SetDryMix
+
+
+ // Volume of 1st chorus tap. 0.0 to 1.0. Default = 0.5.
+ AUTO_PROP float wetMix1 GetWetMix1 SetWetMix1
+
+
+ // Volume of 2nd chorus tap. This tap is 90 degrees out of phase of the first tap. 0.0 to 1.0. Default = 0.5.
+ AUTO_PROP float wetMix2 GetWetMix2 SetWetMix2
+
+
+ // Volume of 3rd chorus tap. This tap is 90 degrees out of phase of the second tap. 0.0 to 1.0. Default = 0.5.
+ AUTO_PROP float wetMix3 GetWetMix3 SetWetMix3
+
+
+ // Chorus delay in ms. 0.1 to 100.0. Default = 40.0 ms.
+ AUTO_PROP float delay GetDelay SetDelay
+
+
+ // Chorus modulation rate in hz. 0.0 to 20.0. Default = 0.8 hz.
+ AUTO_PROP float rate GetRate SetRate
+
+
+ // Chorus modulation depth. 0.0 to 1.0. Default = 0.03.
+ AUTO_PROP float depth GetDepth SetDepth
+
+ /// Chorus feedback. Controls how much of the wet signal gets fed back into the chorus buffer. 0.0 to 1.0. Default = 0.0.
+ OBSOLETE warning feedback is deprecated, this property does nothing.
+ CUSTOM_PROP float feedback
+ {
+ return 0.0f;
+ }
+ {}
+
+END
+// The Audio Reverb Filter takes an Audio Clip and distortionates it in a
+CONDITIONAL ENABLE_AUDIO_FMOD
+CLASS AudioReverbFilter : Behaviour
+ // Set/Get reverb preset properties
+ AUTO_PROP AudioReverbPreset reverbPreset GetReverbPreset SetReverbPreset
+ // Mix level of dry signal in output in mB. Ranges from -10000.0 to 0.0. Default is 0.
+ AUTO_PROP float dryLevel GetDryLevel SetDryLevel
+ // Room effect level at low frequencies in mB. Ranges from -10000.0 to 0.0. Default is 0.0.
+ AUTO_PROP float room GetRoom SetRoom
+ // Room effect high-frequency level re. low frequency level in mB. Ranges from -10000.0 to 0.0. Default is 0.0.
+ AUTO_PROP float roomHF GetRoomHF SetRoomHF
+ // Rolloff factor for room effect. Ranges from 0.0 to 10.0. Default is 10.0
+ AUTO_PROP float roomRolloff GetRoomRolloff SetRoomRolloff
+ // Reverberation decay time at low-frequencies in seconds. Ranges from 0.1 to 20.0. Default is 1.0.
+ AUTO_PROP float decayTime GetDecayTime SetDecayTime
+ // Decay HF Ratio : High-frequency to low-frequency decay time ratio. Ranges from 0.1 to 2.0. Default is 0.5.
+ AUTO_PROP float decayHFRatio GetDecayHFRatio SetDecayHFRatio
+ // Early reflections level relative to room effect in mB. Ranges from -10000.0 to 1000.0. Default is -10000.0.
+ AUTO_PROP float reflectionsLevel GetReflectionsLevel SetReflectionsLevel
+ // Late reverberation level relative to room effect in mB. Ranges from -10000.0 to 2000.0. Default is 0.0.
+ AUTO_PROP float reflectionsDelay GetReflectionsDelay SetReflectionsDelay
+ // Late reverberation level relative to room effect in mB. Ranges from -10000.0 to 2000.0. Default is 0.0.
+ AUTO_PROP float reverbLevel GetReverbLevel SetReverbLevel
+ // Late reverberation delay time relative to first reflection in seconds. Ranges from 0.0 to 0.1. Default is 0.04.
+ AUTO_PROP float reverbDelay GetReverbDelay SetReverbDelay
+ // Reverberation diffusion (echo density) in percent. Ranges from 0.0 to 100.0. Default is 100.0.
+ AUTO_PROP float diffusion GetDiffusion SetDiffusion
+ // Reverberation density (modal density) in percent. Ranges from 0.0 to 100.0. Default is 100.0.
+ AUTO_PROP float density GetDensity SetDensity
+ // Reference high frequency in Hz. Ranges from 20.0 to 20000.0. Default is 5000.0.
+ AUTO_PROP float hfReference GetHFReference SetHFReference
+ // Room effect low-frequency level in mB. Ranges from -10000.0 to 0.0. Default is 0.0.
+ AUTO_PROP float roomLF GetRoomLF SetRoomLF
+ // Reference low-frequency in Hz. Ranges from 20.0 to 1000.0. Default is 250.0.
+ AUTO_PROP float lFReference GetLFReference SetLFReference
+END
+
+
+
+CONDITIONAL ENABLE_MICROPHONE
+// Use this class to record to an [[AudioClip|audio clip]] using a connected microphone.
+CLASS Microphone
+ // Start Recording with device
+
+ CUSTOM static AudioClip Start(string deviceName, bool loop, int lengthSec, int frequency)
+ {
+ return Scripting::ScriptingWrapperFor( GetAudioManager().StartRecord( GetAudioManager().GetMicrophoneDeviceIDFromName ( deviceName ) , loop, lengthSec, frequency ) );
+ }
+
+ // Stops recording
+ CUSTOM static void End(string deviceName)
+ {
+ GetAudioManager().EndRecord( GetAudioManager().GetMicrophoneDeviceIDFromName ( deviceName ) );
+ }
+
+ // Gives you a list microphone devices, identified by name.
+ CUSTOM_PROP static string[] devices
+ {
+ std::vector<std::string> names;
+ names = GetAudioManager().GetRecordDevices();
+
+ ScriptingArrayPtr array = CreateScriptingArray<ScriptingStringPtr> (MONO_COMMON.string, names.size ());
+ for (int i=0;i<names.size ();i++)
+ Scripting::SetScriptingArrayElement (array, i, scripting_string_new ( (const char*)names[i].c_str() ));
+
+ return array;
+ }
+
+ // Query if a device is currently recording.
+ CUSTOM static bool IsRecording(string deviceName)
+ {
+ return GetAudioManager().IsRecording( GetAudioManager().GetMicrophoneDeviceIDFromName ( deviceName ) );
+ }
+
+ // Get the position in samples of the recording.
+ THREAD_SAFE CUSTOM static int GetPosition(string deviceName)
+ {
+ return GetAudioManager().GetRecordPosition( GetAudioManager().GetMicrophoneDeviceIDFromName ( deviceName ) );
+ }
+
+ // Get the frequency capabilities of a device.
+ CUSTOM static void GetDeviceCaps(string deviceName, out int minFreq, out int maxFreq)
+ {
+ GetAudioManager().GetDeviceCaps( GetAudioManager().GetMicrophoneDeviceIDFromName ( deviceName ), minFreq, maxFreq );
+ }
+
+END
+
+
+CSRAW }
+
diff --git a/Runtime/Audio/Utilities/Conversion.h b/Runtime/Audio/Utilities/Conversion.h
new file mode 100644
index 0000000..4bd8fad
--- /dev/null
+++ b/Runtime/Audio/Utilities/Conversion.h
@@ -0,0 +1,291 @@
+/*
+ * AudioConversion.h
+ * audio-utils
+ *
+ * Created by Søren Christiansen on 2/9/11.
+ * Copyright 2011 Unity Technologies. All rights reserved.
+ *
+ */
+#pragma once
+
+#include <algorithm>
+#include <functional>
+#include <assert.h>
+#include "Runtime/Audio/correct_fmod_includer.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+using std::unary_function;
+using std::transform;
+
+typedef union {
+ UInt8 int8[3];
+} SInt24;
+
+template<typename InType, typename OutType >
+struct Reformat : public unary_function<InType, OutType> {
+ float operator()(const InType x) { assert("NO REFORMAT BETWEEN THESE TYPES"); return OutType(); }
+};
+
+// --> normalized float
+template<typename InType >
+struct Reformat<InType, float> : public unary_function<InType, float> {
+ float operator()(const InType x) { return ( (float) x ) / ( 1 << ( ( sizeof(InType) * 8) - 1 ) ) ; }
+};
+
+template< >
+struct Reformat<SInt16, float> : public unary_function<SInt16, float> {
+ float operator()(const SInt16 x) { return ( ((float)x) / ( 1 << 15)) ; }
+};
+
+template< >
+struct Reformat<float, float> : public unary_function<float, float> {
+ float operator()(const float x) { return x; }
+};
+
+template< >
+struct Reformat<SInt24, float> : public unary_function<SInt24, float> {
+ float operator()(const SInt24 x)
+ {
+ int val;
+ val = ((unsigned int)x.int8[0] << 8);
+ val |= ((unsigned int)x.int8[1] << 16);
+ val |= ((unsigned int)x.int8[2] << 24);
+ val >>= 8;
+ return (float)(val) * (1.0f / (float)(1<<23));
+ }
+};
+
+// --> 16 bit
+template< >
+struct Reformat<UInt8, SInt16> {
+ SInt16 operator()(const UInt8 x) { return (( ( UInt16 ) x << 8 )); }
+};
+
+template< >
+struct Reformat<SInt24, SInt16> {
+ SInt16 operator()(const SInt24 x) {
+ SInt16 out = ( x.int8[2] << 8 );
+ out |= ( x.int8[1] );
+ return out;
+ }
+};
+
+template<typename T>
+struct Reformat<float, T> {
+ T operator()(const float x) { return (T)(x * ( 1 << ( ( sizeof(T) * 8) - 1 ))); }
+};
+
+template<>
+struct Reformat<float, SInt16> {
+ SInt16 operator()(const float x) { return SInt16(x * (1 << 15)); }
+};
+
+template<typename T>
+struct NOP : public unary_function<T, T> {
+ T operator()(const T x) { return x; }
+};
+
+// --> helper functors
+template <typename FO1, typename FO2>
+class Composer : public unary_function<typename FO1::argument_type, typename FO2::result_type> {
+private:
+ typedef typename FO2::result_type FO2Result;
+ typedef typename FO1::argument_type FO1Arg;
+
+public:
+ FO1 fo1; // first/inner function object to call
+ FO2 fo2; // second/outer function object to call
+
+ // constructor: initialize function objects
+ Composer (FO1 f1, FO2 f2)
+ : fo1(f1), fo2(f2) {
+ }
+
+ // ''function call'': nested call of function objects
+ FO2Result operator() (const FO1Arg v) {
+ return fo2(fo1(v));
+ }
+};
+
+template <typename FO1, typename FO2>
+inline
+Composer<FO1,FO2> compose (FO1 f1, FO2 f2) {
+ return Composer<FO1,FO2> (f1, f2);
+}
+
+template<typename _InputIterator, typename _Function>
+_Function
+for_each_channel(_InputIterator __first, _InputIterator __last, _Function __f, int channel, int channels)
+{
+ for ( __first = __first + channel; __first < __last - channel; __first = __first+channels)
+ __f(*__first);
+ return __f;
+}
+
+// --> helper functions interface
+typedef Reformat<SInt8, float> SInt8ToFloat;
+typedef Reformat<SInt16, float> SInt16ToFloat;
+typedef Reformat<SInt24, float> SInt24ToFloat;
+typedef Reformat<SInt32, float> SInt32ToFloat;
+typedef Reformat<float, float> FloatToFloat;
+typedef Reformat<float, SInt8> FloatToSInt8;
+typedef Reformat<float, SInt16> FloatToSInt16;
+typedef Reformat<float, SInt24> FloatToSInt24;
+typedef Reformat<float, SInt32> FloatToSInt32;
+
+template<typename _Function>
+inline void ArrayToNormFloat(FMOD_SOUND_FORMAT inFormat, const void* beginIterator, const void* endIterator, dynamic_array<float>& v, _Function __f)
+{
+ if (inFormat == FMOD_SOUND_FORMAT_PCM8)
+ std::transform((const SInt8*) beginIterator, (const SInt8*) endIterator, std::back_inserter(v), compose( SInt8ToFloat(), __f ));
+ else if (inFormat == FMOD_SOUND_FORMAT_PCM16)
+ std::transform((const SInt16*) beginIterator, (const SInt16*) endIterator, std::back_inserter(v), compose ( SInt16ToFloat(), __f));
+ else if (inFormat == FMOD_SOUND_FORMAT_PCM24)
+ std::transform((const SInt24*) beginIterator, (const SInt24*) endIterator, std::back_inserter(v), compose ( SInt24ToFloat(), __f));
+ else if (inFormat == FMOD_SOUND_FORMAT_PCM32)
+ std::transform((const SInt32*) beginIterator, (const SInt32*) endIterator, std::back_inserter(v), compose ( SInt32ToFloat(), __f));
+ else if (inFormat == FMOD_SOUND_FORMAT_PCMFLOAT)
+ std::transform((const float*) beginIterator, (const float*) endIterator, std::back_inserter(v), compose ( FloatToFloat(), __f));
+}
+
+inline void ArrayToNormFloat(FMOD_SOUND_FORMAT inFormat, const void* beginIterator, const void* endIterator, dynamic_array<float>& v)
+{
+ if (inFormat == FMOD_SOUND_FORMAT_PCM8)
+ std::transform((const SInt8*) beginIterator, (const SInt8*) endIterator, std::back_inserter(v), SInt8ToFloat());
+ else if (inFormat == FMOD_SOUND_FORMAT_PCM16)
+ std::transform((const SInt16*) beginIterator, (const SInt16*) endIterator, std::back_inserter(v), SInt16ToFloat());
+ else if (inFormat == FMOD_SOUND_FORMAT_PCM24)
+ std::transform((const SInt24*) beginIterator, (const SInt24*) endIterator, std::back_inserter(v), SInt24ToFloat());
+ else if (inFormat == FMOD_SOUND_FORMAT_PCM32)
+ std::transform((const SInt32*) beginIterator, (const SInt32*) endIterator, std::back_inserter(v), SInt32ToFloat());
+ else if (inFormat == FMOD_SOUND_FORMAT_PCMFLOAT)
+ std::transform((const float*) beginIterator, (const float*) endIterator, std::back_inserter(v), FloatToFloat());
+}
+
+template<typename T>
+inline void ArrayFromNormFloat(FMOD_SOUND_FORMAT outFormat, const float* beginIterator, const float* endIterator, dynamic_array<T>& v)
+{
+ if (outFormat == FMOD_SOUND_FORMAT_PCM8)
+ std::transform(beginIterator, endIterator, std::back_inserter(v), FloatToSInt8());
+ else if (outFormat == FMOD_SOUND_FORMAT_PCM16)
+ std::transform( beginIterator, endIterator, std::back_inserter(v), FloatToSInt16());
+ //else if (outFormat == FMOD_SOUND_FORMAT_PCM24)
+ // std::transform( beginIterator, endIterator, std::back_inserter(v), FloatToSInt24());
+ else if (outFormat == FMOD_SOUND_FORMAT_PCM32)
+ std::transform( beginIterator, endIterator, std::back_inserter(v), FloatToSInt32());
+ else if (outFormat == FMOD_SOUND_FORMAT_PCMFLOAT)
+ std::transform( beginIterator, endIterator, std::back_inserter(v), FloatToFloat());
+}
+
+inline void ArrayFromNormFloat(FMOD_SOUND_FORMAT outFormat, const float* beginIterator, const float* endIterator, void* outbuffer)
+{
+ if (outFormat == FMOD_SOUND_FORMAT_PCM8)
+ {
+ SInt8* dstPtr = (SInt8*)outbuffer;
+ while (beginIterator != endIterator)
+ {
+ *dstPtr = FloatToSInt8()(*beginIterator);
+ beginIterator++;
+ dstPtr++;
+ }
+ }
+ else
+ if (outFormat == FMOD_SOUND_FORMAT_PCM16)
+ {
+ SInt16* dstPtr = (SInt16*)outbuffer;
+ while (beginIterator != endIterator)
+ {
+ *dstPtr = FloatToSInt16()(*beginIterator);
+ beginIterator++;
+ dstPtr++;
+ }
+ }
+ else
+ if (outFormat == FMOD_SOUND_FORMAT_PCM32)
+ {
+ SInt32* dstPtr = (SInt32*)outbuffer;
+ while (beginIterator != endIterator)
+ {
+ *dstPtr = FloatToSInt32()(*beginIterator);
+ beginIterator++;
+ dstPtr++;
+ }
+ }
+ else
+ if (outFormat == FMOD_SOUND_FORMAT_PCMFLOAT)
+ {
+ memcpy (outbuffer, beginIterator, (endIterator - beginIterator) * sizeof(float));
+ }
+ else {
+ Assert("Conversion NOT supported");
+ }
+
+}
+
+
+inline void ArrayToNormFloat(FMOD_SOUND_FORMAT inFormat, const void* beginIterator, const void* endIterator, float* outbuffer)
+{
+ if (inFormat == FMOD_SOUND_FORMAT_PCM8)
+ {
+ SInt8* srcPtr = (SInt8*)beginIterator;
+ while (srcPtr != (SInt8*)endIterator)
+ {
+ *outbuffer = SInt8ToFloat()(*srcPtr);
+ srcPtr++;
+ outbuffer++;
+ }
+ }
+ else
+ if (inFormat == FMOD_SOUND_FORMAT_PCM16)
+ {
+ SInt16* srcPtr = (SInt16*)beginIterator;
+ while (srcPtr != (SInt16*)endIterator)
+ {
+ *outbuffer = SInt16ToFloat()(*srcPtr);
+ srcPtr++;
+ outbuffer++;
+ }
+ }
+ else
+ if (inFormat == FMOD_SOUND_FORMAT_PCM24)
+ {
+ SInt24* srcPtr = (SInt24*)beginIterator;
+ while (srcPtr != (SInt24*)endIterator)
+ {
+ *outbuffer = SInt24ToFloat()(*srcPtr);
+ srcPtr++;
+ outbuffer++;
+ }
+ }
+ else
+ if (inFormat == FMOD_SOUND_FORMAT_PCM32)
+ {
+ SInt32* srcPtr = (SInt32*)beginIterator;
+ while (srcPtr != (SInt32*)endIterator)
+ {
+ *outbuffer = SInt32ToFloat()(*srcPtr);
+ srcPtr++;
+ outbuffer++;
+ }
+ }
+ else
+ if (inFormat == FMOD_SOUND_FORMAT_PCMFLOAT)
+ {
+ memcpy (outbuffer, beginIterator, ((float*)endIterator - (float*)beginIterator) * sizeof(float));
+ }
+ else {
+ ErrorString("Conversion from this format NOT supported");
+ }
+}
+
+
+
+template<typename InType, typename OutType>
+inline void ReformatArray(const InType* inArray, const unsigned size, OutType* outArray)
+{
+ Reformat<InType, OutType> reformater;
+ std::transform( inArray, inArray + size, outArray, reformater );
+}
+
+
+
diff --git a/Runtime/Audio/WavReader.h b/Runtime/Audio/WavReader.h
new file mode 100644
index 0000000..370ca4e
--- /dev/null
+++ b/Runtime/Audio/WavReader.h
@@ -0,0 +1,182 @@
+#ifndef __WAVREADER_H__
+#define __WAVREADER_H__
+
+typedef UInt32 FOURCC;
+
+struct RIFF_TAG {
+ FOURCC RIFF;
+ UInt32 size;
+};
+
+struct WAV_HEADER {
+ FOURCC RIFF;
+ UInt32 size;
+ FOURCC type;
+};
+
+struct WAV_FORMAT {
+ FOURCC ID;
+ UInt32 size;
+ UInt16 format;
+ UInt16 channels;
+ UInt32 samplerate;
+ UInt32 avgBytesSec;
+ UInt16 blockalign;
+ UInt16 bitsPerSample;
+};
+
+struct WAV_DATA {
+ FOURCC ID;
+ UInt32 size;
+};
+
+#if !UNITY_BIG_ENDIAN
+#ifndef MAKEFOURCC
+#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
+ ((UInt32)(UInt8)(ch0) | ((UInt32)(UInt8)(ch1) << 8) | \
+ ((UInt32)(UInt8)(ch2) << 16) | ((UInt32)(UInt8)(ch3) << 24 ))
+#endif
+#define btoll(x) (x)
+#define btols(x) (x)
+#else
+#ifndef MAKEFOURCC
+#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
+((UInt32)(UInt8)(ch0) << 24 | ((UInt32)(UInt8)(ch1) << 16) | \
+((UInt32)(UInt8)(ch2) << 8) | ((UInt32)(UInt8)(ch3) ))
+#endif
+#define btoll(x) (((x) >> 24) | (((x)&0x00ff0000) >> 8) | (((x)&0x0000ff00) << 8) | ((x) << 24))
+#define btols(x) (((x) >> 8) | ((x&0xff) << 8))
+#endif
+
+#define RIFF_RIFF MAKEFOURCC('R','I','F','F')
+#define RIFF_WAVE MAKEFOURCC('W','A','V','E')
+#define RIFF_FORMAT MAKEFOURCC('f','m','t',' ')
+#define RIFF_DATA MAKEFOURCC('d','a','t','a')
+
+static const UInt8* GetRIFFChunk ( FOURCC fourcc, const UInt8* wavRiff )
+{
+ UInt8* p = const_cast<UInt8*>(wavRiff);
+ UInt32 next = *((UInt32*)p);
+
+ if (next != RIFF_RIFF) // not a RIFF file
+ return NULL;
+
+ UInt32 totalSize = (UInt32) *((UInt32*)(p + 4));
+
+ // next chunk
+ p += 12;
+ next = *((UInt32*)p);
+
+ while (next != fourcc)
+ {
+ UInt32 size = (UInt32) *((UInt32*)(p + 4));
+
+ // next chunk
+ p += 8;
+ p += size;
+ next = *((UInt32*)p);
+
+ if (p - wavRiff >= totalSize)
+ return NULL;
+ }
+
+ return p;
+}
+
+static bool IsWAV(const UInt8* wavRiff)
+{
+ return ((*((UInt32*)wavRiff) == RIFF_RIFF) &&
+ (((WAV_HEADER*)(wavRiff))->type == RIFF_WAVE));
+}
+
+// @note return in little endian
+static const WAV_HEADER* GetWAVHeader(const UInt8* wavRiff)
+{
+ return (WAV_HEADER*)wavRiff;
+}
+
+// @note return in little endian
+static const WAV_FORMAT* GetWAVFormat(const UInt8* wavRiff)
+{
+ return (WAV_FORMAT*)GetRIFFChunk(RIFF_FORMAT, wavRiff);
+}
+
+// @note return in little endian
+static const WAV_DATA* GetWAVData(const UInt8* wavRiff)
+{
+ return (WAV_DATA*)GetRIFFChunk(RIFF_DATA, wavRiff);
+}
+
+// endian-safe
+static bool IsNormalWAV(const UInt8* wavRiff)
+{
+ return IsWAV(wavRiff) && (GetWAVFormat(wavRiff)->format == 1);
+}
+
+// endian-safe
+static UInt32 GetWAVSize(const UInt8* wavRiff)
+{
+ return btoll(GetWAVHeader(wavRiff)->size);
+}
+
+static void CreateRIFFTag(RIFF_TAG& tag, FOURCC fourCC, int size)
+{
+ tag.RIFF = fourCC;
+ tag.size = btoll(size);
+}
+
+
+static void CreateWAVHeader( WAV_HEADER &header, int size, int additionalTagSize = 0 )
+{
+ header.RIFF = MAKEFOURCC('R','I','F','F');
+ header.size = btoll ( sizeof (WAV_HEADER) + sizeof (WAV_FORMAT) + sizeof (WAV_DATA) + size + additionalTagSize );
+ header.type = MAKEFOURCC('W','A','V','E');
+}
+
+static void CreateFMTTag( WAV_FORMAT &format, int channels, int frequency, int bitsPerSample )
+{
+ format.ID = MAKEFOURCC('f','m','t',' ');;
+ format.size = btoll(16);
+ format.format = btols(1);
+ format.channels = btols(channels);
+ format.samplerate = btoll(frequency);
+ format.avgBytesSec = btoll((frequency * bitsPerSample) / 8);
+ format.blockalign = btols(bitsPerSample / 8);
+ format.bitsPerSample = btols(bitsPerSample);
+}
+
+
+
+/**
+* reconstructing a wav header + alloc size for data
+* @param frequency Frequency
+* @param size The size of the data
+* @param channels channels
+* @param bitsPerSamples Bits pr. sample
+* @param ppData ptr to data chunk
+* @return ptr to header
+
+**/
+static UInt8* CreateWAV(int frequency, int size, int channels, int bitsPerSample, UInt8** ppData)
+{
+ WAV_HEADER header;
+ WAV_FORMAT format;
+ WAV_DATA data;
+
+ CreateWAVHeader(header, size);
+ CreateFMTTag(format, channels, frequency, bitsPerSample);
+ CreateRIFFTag((RIFF_TAG&)data, MAKEFOURCC('d','a','t','a'), size);
+
+ UInt8* wav = new UInt8[ sizeof (WAV_HEADER) + sizeof (WAV_FORMAT) + sizeof (WAV_DATA) + size ];
+
+ memcpy(wav, &header, sizeof(WAV_HEADER));
+ memcpy(wav + sizeof(WAV_HEADER), &format, sizeof(WAV_FORMAT));
+ memcpy(wav + sizeof(WAV_HEADER) + sizeof(WAV_FORMAT), &data, sizeof(WAV_DATA));
+
+ *ppData = (UInt8*)(wav + sizeof(WAV_HEADER) + sizeof(WAV_FORMAT)) + 8;
+
+ return wav;
+}
+
+
+#endif // __WAVREADER_H__
diff --git a/Runtime/Audio/correct_fmod_includer.h b/Runtime/Audio/correct_fmod_includer.h
new file mode 100644
index 0000000..76c893d
--- /dev/null
+++ b/Runtime/Audio/correct_fmod_includer.h
@@ -0,0 +1,39 @@
+#if ENABLE_AUDIO_FMOD
+ #if UNITY_WIN
+ #include "External/Audio/FMOD/builds/win32/include/fmod.hpp"
+ #include "External/Audio/FMOD/builds/win32/include/fmod_errors.h"
+ #include "External/Audio/FMOD/builds/win32/include/fmod_types.h"
+ #elif UNITY_LINUX
+ #if defined(__LP64__) || defined(_LP64)
+ #include "External/Audio/FMOD/builds/linux64/include/fmod.hpp"
+ #include "External/Audio/FMOD/builds/linux64/include/fmod_errors.h"
+ #include "External/Audio/FMOD/builds/linux64/include/fmod_types.h"
+ #else
+ #include "External/Audio/FMOD/builds/linux32/include/fmod.hpp"
+ #include "External/Audio/FMOD/builds/linux32/include/fmod_errors.h"
+ #include "External/Audio/FMOD/builds/linux32/include/fmod_types.h"
+ #endif
+ #elif UNITY_IPHONE
+ #include "External/Audio/FMOD/builds/iphone/include/fmod.hpp"
+ #include "External/Audio/FMOD/builds/iphone/include/fmod_errors.h"
+ #include "External/Audio/FMOD/builds/iphone/include/fmod_types.h"
+ #elif UNITY_PS3
+ #include "External/Audio/FMOD/builds/ps3/include/fmod.hpp"
+ #include "External/Audio/FMOD/builds/ps3/include/fmod_errors.h"
+ #include "External/Audio/FMOD/builds/ps3/include/fmod_types.h"
+ #elif UNITY_BB10
+ #include "External/Audio/FMOD/builds/bb10/include/fmod.hpp"
+ #include "External/Audio/FMOD/builds/bb10/include/fmod_errors.h"
+ #include "External/Audio/FMOD/builds/bb10/include/fmod_types.h"
+ #elif UNITY_TIZEN
+ #include "External/Audio/FMOD/builds/tizen/include/fmod.hpp"
+ #include "External/Audio/FMOD/builds/tizen/include/fmod_errors.h"
+ #include "External/Audio/FMOD/builds/tizen/include/fmod_types.h"
+ #else
+ #include <fmod.hpp>
+ #include <fmod_errors.h>
+ #include <fmod_types.h>
+ #endif
+#elif UNITY_FLASH || UNITY_WEBGL
+ #include "External/Audio/FMOD/builds/win32/include/fmod.h"
+#endif
diff --git a/Runtime/BaseClasses/BaseObject.cpp b/Runtime/BaseClasses/BaseObject.cpp
new file mode 100644
index 0000000..1f4c88a
--- /dev/null
+++ b/Runtime/BaseClasses/BaseObject.cpp
@@ -0,0 +1,1393 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "BaseObject.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Utilities/dynamic_bitset.h"
+#include "Runtime/Utilities/CStringHash.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Profiler/MemoryProfilerStats.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "EventManager.h"
+#include "EventIDs.h"
+#if ENABLE_MONO
+#include "Runtime/Mono/MonoIncludes.h"
+#endif
+
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+#if defined(__MWERKS__)
+#include <hash_map>
+#endif
+
+#include "Configuration/UnityConfigure.h"
+#if THREADED_LOADING
+#include "Runtime/Threads/ThreadSpecificValue.h"
+#include "Runtime/Threads/ProfilerMutex.h"
+#endif
+
+#if !UNITY_EXTERNAL_TOOL
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+#endif
+#include "Runtime/Allocator/MemoryManager.h"
+
+using namespace std;
+
+#define SHOW_REGISTERED_CLASS_INFO 0
+
+
+#if THREADED_LOADING
+#define CHECK_IN_MAIN_THREAD DebugAssertIf(!Thread::EqualsCurrentThreadID(GetPersistentManager().GetMainThreadID()));
+#define DEBUG_CHECK_IN_MAIN_THREAD AssertIf(!Thread::EqualsCurrentThreadID(GetPersistentManager().GetMainThreadID()));
+#else
+#define CHECK_IN_MAIN_THREAD
+#define DEBUG_CHECK_IN_MAIN_THREAD
+#endif
+
+
+static bool IsDerivedFromRTTI (const Object::RTTI* klass, const Object::RTTI* derivedFrom)
+{
+ const Object::RTTI* i = klass;
+ while (i)
+ {
+ if (derivedFrom == i)
+ return true;
+
+ i = i->base;
+ }
+ return false;
+}
+
+struct RegisterClassCallbackStruct
+{
+ RegisterClassCallback* registerClass;
+ RegisterClassCallback* initClassEarly;
+ RegisterClassCallback* initClass;
+ RegisterClassCallback* postInitClass;
+ RegisterClassCallback* cleanupClass;
+
+ RegisterClassCallbackStruct()
+ {
+ registerClass = initClassEarly = initClass = postInitClass = cleanupClass = NULL;
+ }
+};
+
+typedef UNITY_VECTOR(kMemPermanent, RegisterClassCallbackStruct) RegisterClassCallbacks;
+
+Object::IDToPointerMap* Object::ms_IDToPointer = NULL;
+UInt32* Object::ms_IsDerivedFromBitMap = NULL;
+unsigned Object::ms_MaxClassID = 0;
+#if USE_NEW_IS_DERIVED_FROM
+UInt32 gClassIDMask[32];
+UInt32* Object::ms_ClassIDMask = 0;
+UInt32* Object::ms_ClassIsDerivedFrom = 0;
+#endif
+
+static RegisterClassCallbacks* gRegisterClassCallbacks = NULL;
+
+#if defined(__MWERKS__)
+#error("Metrowerks should not be used")
+#endif
+
+#if DEBUGMODE
+RegisteredClassSet* gVerifyRegisteredClasses = NULL;
+#endif
+
+typedef map<char*, SInt32, smaller_cstring> StringToClassIDMap;
+typedef pair<const SInt32, Object::RTTI> SInt32RTTIPair;
+typedef map<SInt32, Object::RTTI, less<SInt32>, STL_ALLOCATOR(kMemPermanent, SInt32RTTIPair) > RTTIMap;
+
+static StringToClassIDMap* gStringToClassID = NULL;
+static RTTIMap* gRTTI = NULL;
+static dynamic_bitset* gRegisteredClassIDs = NULL;
+static dynamic_bitset* gIsDerivedFromBitMap = NULL;
+static Object::ObjectDestroyCallbackFunction* gDestroyedCallbackFunc = NULL;
+
+static int* gBaseObjectManagerContainer = NULL;
+
+namespace BaseObjectManager
+{
+ void StaticInitialize()
+ {
+ gBaseObjectManagerContainer = UNITY_NEW_AS_ROOT(int, kMemBaseObject, "Managers", "BaseObjectManager");
+ SET_ALLOC_OWNER(gBaseObjectManagerContainer);
+ gStringToClassID = UNITY_NEW(StringToClassIDMap, kMemBaseObject);
+ gRTTI = UNITY_NEW(RTTIMap, kMemBaseObject);
+ gRegisteredClassIDs = UNITY_NEW(dynamic_bitset, kMemBaseObject);
+ gIsDerivedFromBitMap = UNITY_NEW(dynamic_bitset, kMemBaseObject);
+ Object::StaticInitialize();
+ }
+ void StaticDestroy()
+ {
+ Object::StaticDestroy();
+ UNITY_DELETE(gStringToClassID, kMemBaseObject);
+ UNITY_DELETE(gRTTI, kMemBaseObject);
+ UNITY_DELETE(gRegisteredClassIDs, kMemBaseObject);
+ UNITY_DELETE(gIsDerivedFromBitMap, kMemBaseObject);
+#if DEBUGMODE
+ UNITY_DELETE(gVerifyRegisteredClasses, kMemBaseObject); // allocated on first access
+#endif
+ UNITY_DELETE(gBaseObjectManagerContainer, kMemBaseObject);
+ }
+}
+
+static RegisterRuntimeInitializeAndCleanup s_BaseObjectManagerCallbacks(BaseObjectManager::StaticInitialize, BaseObjectManager::StaticDestroy);
+
+#if UNITY_EDITOR
+static Object::ObjectDirtyCallbackFunction* gSetDirtyCallbackFunc = NULL;
+#endif
+
+PROFILER_INFORMATION (gObjectCreationMutexLockInfo, "Object.CreateObject mutex lock", kProfilerLoading)
+
+#if THREADED_LOADING
+Mutex gCreateObjectMutex;
+#if DEBUGMODE
+static UNITY_TLS_VALUE(int) gCheckObjectCreationMutex;
+#endif
+#endif
+
+void LockObjectCreation ()
+{
+ #if THREADED_LOADING
+ LOCK_MUTEX (gCreateObjectMutex, gObjectCreationMutexLockInfo);
+ #if DEBUGMODE
+ ++gCheckObjectCreationMutex;
+ #endif
+ #endif
+}
+
+void UnlockObjectCreation ()
+{
+ #if THREADED_LOADING
+ gCreateObjectMutex.Unlock();
+ #if DEBUGMODE
+ --gCheckObjectCreationMutex;
+ #endif
+ #endif
+}
+
+
+static SInt32 gLowestInstanceID = -10;
+static bool gDisableImmediateDestruction = false;
+
+#if UNITY_EDITOR
+InstanceIDResolveCallback* gInstanceIDResolveCallback = NULL;
+const void* gInstanceIDResolveContext = NULL;
+
+void SetInstanceIDResolveCallback (InstanceIDResolveCallback callback, const void* context)
+{
+ gInstanceIDResolveCallback = callback;
+ gInstanceIDResolveContext = context;
+}
+#endif
+
+void InstanceIDToLocalSerializedObjectIdentifier (SInt32 id, LocalSerializedObjectIdentifier& localIdentifier)
+{
+ #if UNITY_EDITOR
+ // Early out if referenced object is null
+ if (id == 0)
+ {
+ localIdentifier.localSerializedFileIndex = 0;
+ localIdentifier.localIdentifierInFile = 0;
+ return;
+ }
+
+ if (gInstanceIDResolveCallback == NULL)
+ {
+ GetPersistentManager ().InstanceIDToLocalSerializedObjectIdentifierInternal (id, localIdentifier);
+ return;
+ }
+ else
+ {
+ gInstanceIDResolveCallback (id, localIdentifier, const_cast<void*>(gInstanceIDResolveContext));
+ }
+ #else
+ GetPersistentManager ().InstanceIDToLocalSerializedObjectIdentifierInternal (id, localIdentifier);
+ #endif
+}
+
+void LocalSerializedObjectIdentifierToInstanceID (const LocalSerializedObjectIdentifier& localIdentifier, SInt32& instanceID)
+{
+ GetPersistentManager ().LocalSerializedObjectIdentifierToInstanceIDInternal (localIdentifier, instanceID);
+}
+
+Object* ReadObjectFromPersistentManager (int id)
+{
+ if (id == 0)
+ return NULL;
+ else
+ {
+ // In the Player it is not possible to call MakeObjectPersistent,
+ // thus instance id's that are positive are the only ones that can be loaded from disk
+ #if !UNITY_EDITOR
+ if (id < 0)
+ {
+ #if DEBUGMODE
+ //AssertIf(GetPersistentManager ().ReadObject (id));
+ #endif
+ return NULL;
+ }
+ #endif
+
+ Object* o = GetPersistentManager ().ReadObject (id);
+ return o;
+ }
+}
+
+void DestroyWithoutLoadingButDontDestroyFromFile (int instanceID)
+{
+ GetPersistentManager ().MakeObjectUnpersistent (instanceID, kDontDestroyFromFile);
+ UnloadObject(Object::IDToPointer(instanceID));
+}
+
+void DestroySingleObject (Object* o)
+{
+ if (o == NULL)
+ return;
+
+ if (o->IsPersistent())
+ GetPersistentManager ().MakeObjectUnpersistent (o->GetInstanceID (), kDestroyFromFile);
+
+ // Lock changes to IDToPointer so that we can safely lookup pointers using IDToPointerThreadSafe
+ LockObjectCreation();
+
+ delete_object_internal (o);
+
+ UnlockObjectCreation();
+}
+
+
+Object::Object (MemLabelId label, ObjectCreationMode mode)
+{
+ Assert(label.label < (1 << kMemLabelBits));
+ m_MemLabel = label.label;
+ m_InstanceID = 0;
+ m_EventIndex = NULL;
+
+ #if ENABLE_SCRIPTING
+ m_MonoReference = 0;
+ m_ScriptingObjectPointer = SCRIPTING_NULL;
+ #endif
+
+ #if !UNITY_RELEASE
+ m_AwakeCalled = 0;
+ m_ResetCalled = 0;
+ m_AwakeThreadedCalled = 0;
+ m_AwakeDidLoadThreadedCalled = 0;
+ #endif
+
+
+ #if UNITY_EDITOR
+ m_DirtyIndex = 0;
+ m_FileIDHint = 0;
+ #endif
+ m_HideFlags = 0;
+ m_TemporaryFlags = 0;
+ m_IsPersistent = false;
+
+ DebugAssert(GetMemoryManager().GetAllocator(GetMemoryLabel())->Contains(this));
+ m_IsRootOwner = GetMemoryManager().GetAllocator(GetMemoryLabel())->GetProfilerHeader(this) != NULL;
+ #if UNITY_WINRT
+ m_TemporaryUnusedAssetsFlags = 0;
+ #endif
+}
+
+void Object::CalculateCachedClassID (Object* obj)
+{
+ Assert(obj->GetClassIDVirtualInternal() < (1 << kCachedClassIDBits));
+ obj->m_CachedClassID = obj->GetClassIDVirtualInternal();
+}
+
+void Object::InsertObjectInMap( Object* obj )
+{
+ SET_ALLOC_OWNER(gBaseObjectManagerContainer);
+ Assert (ms_IDToPointer->find (obj->GetInstanceID ()) == ms_IDToPointer->end ());
+ ms_IDToPointer->insert (make_pair (obj->GetInstanceID (), obj));
+
+ PROFILER_REGISTER_OBJECT(obj);
+}
+
+void Object::RegisterInstanceID (Object* obj)
+{
+ CHECK_IN_MAIN_THREAD
+
+ LockObjectCreation();
+ Assert (obj != NULL);
+ AssertIf(obj->m_InstanceID == 0);
+ InsertObjectInMap (obj);
+
+ UnlockObjectCreation();
+}
+
+void Object::RegisterInstanceIDNoLock (Object* obj)
+{
+ CHECK_IN_MAIN_THREAD
+ Assert (obj != NULL);
+ AssertIf (obj->m_InstanceID == 0);
+ CalculateCachedClassID (obj);
+ InsertObjectInMap (obj);
+}
+
+
+Object* Object::AllocateAndAssignInstanceID (Object* obj)
+{
+ CHECK_IN_MAIN_THREAD
+ AssertIf(obj->m_InstanceID != 0);
+
+ LockObjectCreation();
+
+ // Create a new unique instanceID for this Object.
+ // The created id will be negative beginning with -1
+ // Ids loaded from a file will be positive beginning with 1
+ gLowestInstanceID-=2;
+ obj->SetInstanceID (gLowestInstanceID);
+ AssertIf (obj->GetInstanceID () & 1);
+
+ CalculateCachedClassID (obj);
+ InsertObjectInMap (obj);
+
+ UnlockObjectCreation();
+
+ obj->SetDirty();
+
+ return obj;
+}
+
+Object* Object::AllocateAndAssignInstanceIDNoLock (Object* obj)
+{
+ CHECK_IN_MAIN_THREAD
+ AssertIf(obj->m_InstanceID != 0);
+
+ // Create a new unique instanceID for this Object.
+ // The created id will be negative beginning with -1
+ // Ids loaded from a file will be positive beginning with 1
+ gLowestInstanceID-=2;
+ obj->SetInstanceID (gLowestInstanceID);
+ AssertIf (obj->GetInstanceID () & 1);
+
+ CalculateCachedClassID (obj);
+
+ InsertObjectInMap (obj);
+
+ obj->SetDirty();
+
+ return obj;
+}
+
+enum { kMonoObjectCachedPtrOffset = 12 };
+
+
+// This must be executed on the main thread
+void delete_object_internal_step1 (Object* object)
+{
+ PROFILER_UNREGISTER_OBJECT(object);
+
+#if !UNITY_RELEASE
+ object->CheckCorrectAwakeUsage();
+#endif
+
+#if THREADED_LOADING && DEBUGMODE
+ Assert(gCheckObjectCreationMutex >= 1);
+#endif
+
+ // Send destroy message & clear event index
+ if (object->m_EventIndex != NULL)
+ {
+ GetEventManager().InvokeEvent(object->m_EventIndex, object, kWillDestroyEvent);
+ GetEventManager().RemoveEvent(object->m_EventIndex);
+ object->m_EventIndex = NULL;
+ }
+
+ // Remove this objects instanceID from the table.
+ AssertIf (Object::ms_IDToPointer->find (object->GetInstanceID ()) == Object::ms_IDToPointer->end ());
+ Object::ms_IDToPointer->erase (object->GetInstanceID ());
+
+ if (gDestroyedCallbackFunc)
+ gDestroyedCallbackFunc (object->GetInstanceID ());
+
+ object->m_InstanceID = 0;
+}
+
+Object::~Object ()
+{
+ // Ensure PreCleanupObject was called
+ #if DEBUGMODE
+ Assert(m_InstanceID == 0);
+ #endif
+
+#if ENABLE_MONO //if unity3.5 succesfully shipped with this assert it may be removed. here to verify an assumption made in a refactor.
+ Assert((m_MonoReference==0) == (m_ScriptingObjectPointer==NULL));
+#endif
+
+#if ENABLE_SCRIPTING
+ if (m_ScriptingObjectPointer)
+ SetCachedScriptingObject(SCRIPTING_NULL);
+#endif
+}
+
+bool Object::MainThreadCleanup ()
+{
+ AssertString("MainThreadCleanup is not implemented for this class. See DoesClassRequireMainThreadDeallocation");
+ return false;
+}
+
+void Object::SetIsPersistent( bool p )
+{
+ PROFILER_CHANGE_PERSISTANCY(GetInstanceID(), m_IsPersistent, p);
+ m_IsPersistent = p;
+}
+
+#if ENABLE_SCRIPTING
+
+#if UNITY_PS3 || UNITY_XENON
+ extern "C" char* GC_clear_stack(char*);
+#endif
+
+void Object::SetupWeakHandle ()
+{
+ if (m_MonoReference != 0)
+ {
+ register ScriptingObjectPtr object = scripting_gchandle_get_target(m_MonoReference);
+ UInt32 weakref = scripting_gchandle_weak_new (object);
+ SetCachedScriptingObject(SCRIPTING_NULL);
+
+#if UNITY_PS3 || UNITY_XENON
+ // we need to make sure the object doesn't continue living on the stack. // fixes http://fogbugz.unity3d.com/default.asp?444901#1065992062
+ GC_clear_stack((char*)object);
+#endif
+ object = SCRIPTING_NULL;
+ m_MonoReference = weakref;
+ }
+}
+
+bool Object::RevertWeakHandle ()
+{
+ if (m_MonoReference != 0)
+ {
+ ScriptingObjectPtr target = scripting_gchandle_get_target (m_MonoReference);
+ scripting_gchandle_free(m_MonoReference);
+ m_MonoReference = 0;
+ if (target)
+ {
+ SetCachedScriptingObject(target);
+#if UNITY_WINRT
+ // Restore cached ptr for managed object, not sure why we don't do this for Mono as well, because we reset cachedPtr in SetupWeakHandle
+ // But on Mono it seems cachedPtr persists ?!
+ // Maybe it's related to cachedPtr optimization
+ register ScriptingObjectOfType<Object> instance(m_ScriptingObjectPointer);
+ instance.SetCachedPtr(this);
+#endif
+ }
+ return target != SCRIPTING_NULL;
+ }
+ else
+ return false;
+}
+
+
+void Object::SetCachedScriptingObject (ScriptingObjectPtr object)
+{
+ if (object)
+ {
+ AssertIf(m_MonoReference != 0);
+ m_MonoReference = scripting_gchandle_new (object);
+ m_ScriptingObjectPointer = object;
+ return;
+ }
+
+ if (m_ScriptingObjectPointer == SCRIPTING_NULL)
+ {
+ AssertString("Dont do this");
+ return;
+ }
+
+ register ScriptingObjectOfType<Object> instance(m_ScriptingObjectPointer);
+ instance.SetCachedPtr(0);
+
+ scripting_gchandle_free (m_MonoReference);
+ m_MonoReference = 0;
+
+ m_ScriptingObjectPointer = SCRIPTING_NULL;
+#if UNITY_WINRT
+ instance = ScriptingObjectPtr(SCRIPTING_NULL);
+#else
+ instance = SCRIPTING_NULL;
+#endif
+}
+#endif
+
+void Object::RegisterDestroyedCallback (ObjectDestroyCallbackFunction* callback)
+{
+ gDestroyedCallbackFunc = callback;
+}
+
+// Register base class
+void Object::RegisterClass ()
+{
+ RegisterClass (ClassID (Object), -1, "Object", sizeof (Object), NULL, true);
+}
+
+void Object::RegisterClass (int inClassID, int inBaseClass, const string& inName, int byteSize, FactoryFunction* inFunc, bool isAbstract)
+{
+ if (ClassIDToRTTI (inClassID))
+ return;
+
+ // Store ClassID -> RTTI
+ AssertIf (gRTTI->find (inClassID) != gRTTI->end ());
+ RTTIMap::iterator baseClass = gRTTI->find (inBaseClass);
+ AssertIf (baseClass == gRTTI->end () && inBaseClass != -1);
+ Object::RTTI& classInfo = (*gRTTI)[inClassID];
+ classInfo.base = baseClass == gRTTI->end () ? NULL : &baseClass->second;
+ classInfo.factory = inFunc;
+ classInfo.className = inName;
+ classInfo.classID = inClassID;
+ classInfo.isAbstract = isAbstract;
+ classInfo.size = byteSize;
+
+ // Store String -> ClassID
+ AssertIf (gStringToClassID->find (const_cast<char*> (inName.c_str ())) != gStringToClassID->end ());
+ (*gStringToClassID)[const_cast<char*> (classInfo.className.c_str ())] = inClassID;
+}
+
+Object* Object::Produce (int classID, int instanceID, MemLabelId memLabel, ObjectCreationMode mode)
+{
+ // Object is already loaded assert
+ AssertIf (mode == kCreateObjectDefault && IDToPointer (instanceID) != NULL);
+ AssertIf (instanceID == 0 && mode == kCreateObjectFromNonMainThread);
+ #if THREADED_LOADING
+ AssertIf (!Thread::EqualsCurrentThreadID(GetPersistentManager().GetMainThreadID()) && mode != kCreateObjectFromNonMainThread);
+ #endif
+ AssertIf (instanceID & 1);
+
+ // Find the appropriate Factory.
+ RTTIMap::iterator i;
+ i = gRTTI->find (classID);
+ if (i == gRTTI->end () || i->second.factory == NULL)
+ {
+ return NULL;
+ }
+
+ Object* o;
+ if (instanceID != 0)
+ {
+ o = i->second.factory (memLabel, mode);
+ if (o == NULL)
+ return NULL;
+ o->SetInstanceID(instanceID);
+ CalculateCachedClassID (o);
+
+ // Register instanceID and set dirty
+ if (mode == kCreateObjectDefault)
+ {
+ RegisterInstanceID(o);
+ o->SetDirty();
+ }
+ else if (mode == kCreateObjectDefaultNoLock)
+ {
+ RegisterInstanceIDNoLock(o);
+ o->SetDirty();
+ }
+
+ return o;
+ }
+ else
+ {
+ AssertIf(mode != kCreateObjectDefaultNoLock && mode != kCreateObjectDefault);
+ o = i->second.factory (memLabel, mode);
+
+ if (mode == kCreateObjectDefaultNoLock)
+ AllocateAndAssignInstanceIDNoLock(o);
+ else
+ AllocateAndAssignInstanceID(o);
+ return o;
+ }
+}
+
+void Object::CheckInstanceIDsLoaded (SInt32* instanceIDs, int size)
+{
+ for (int i=0;i<size;i++)
+ {
+ if (ms_IDToPointer->count (instanceIDs[i]))
+ instanceIDs[i] = 0;
+ }
+}
+
+#if !UNITY_RELEASE
+Object* Object::IDToPointer (int inInstanceID)
+{
+ DEBUG_CHECK_IN_MAIN_THREAD
+
+ return Object::IDToPointerNoThreadCheck (inInstanceID);
+}
+#endif
+
+Object* Object::IDToPointerThreadSafe (int inInstanceID)
+{
+ LockObjectCreation();
+ Object* obj = Object::IDToPointerNoThreadCheck (inInstanceID);
+ UnlockObjectCreation();
+ return obj;
+}
+
+Object* Object::IDToPointerNoThreadCheck (int inInstanceID)
+{
+ if( !ms_IDToPointer) return NULL;
+
+ IDToPointerMap::const_iterator i = ms_IDToPointer->find (inInstanceID);
+ if (i != ms_IDToPointer->end ())
+ {
+ return i->second;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+#if THREADED_LOADING
+Object* InstanceIDToObjectThreadSafe (int instanceID)
+{
+ if (Thread::EqualsCurrentThreadID (GetPersistentManager().GetMainThreadID()))
+ return PPtr<Object> (instanceID);
+ else
+ {
+ Object* obj = Object::IDToPointerThreadSafe(instanceID);
+ if (obj == NULL)
+ return GetPersistentManager().ReadObjectThreaded(instanceID);
+ else
+ return obj;
+ }
+}
+#endif
+
+void Object::FindAllDerivedClasses (int classID, vector<SInt32>* derivedClasses, bool onlyNonAbstract)
+{
+ AssertIf (derivedClasses == NULL);
+ RTTIMap::iterator i;
+ for (i=gRTTI->begin ();i!=gRTTI->end ();i++)
+ {
+ if (IsDerivedFromClassID (i->first, classID) && (!onlyNonAbstract || !i->second.isAbstract))
+ derivedClasses->push_back (i->first);
+ }
+}
+
+struct GetConstFirst
+{
+ template<typename T1, typename T2>
+ const T1& operator()(const std::pair<T1, T2>& p) const {
+ return p.first;
+ }
+};
+
+struct GetConstSecond
+{
+ template<typename T1, typename T2>
+ const T2& operator()(const std::pair<T1, T2>& p) const {
+ return p.second;
+ }
+};
+
+struct IsDerivedFromClass
+{
+ IsDerivedFromClass(int classID): m_ClassID(classID) {}
+ bool operator()(const Object::IDToPointerMap::value_type& el) const
+ {
+ return el.second->IsDerivedFrom (m_ClassID);
+ }
+private:
+ int m_ClassID;
+};
+
+inline int DerivedObjectCount(const Object::IDToPointerMap& objmap, int classID)
+{
+ return std::count_if(objmap.begin(), objmap.end(), IsDerivedFromClass(classID));
+}
+
+template<typename ObjectGetter, typename Container, typename Predicate>
+inline int FindAllDerivedObjectsImpl (const Object::IDToPointerMap& objmap, int classID,
+ ObjectGetter getter, Container* derivedObjects, bool sorted, Predicate pred)
+{
+ if (NULL == derivedObjects)
+ return DerivedObjectCount(objmap, classID);
+
+ int count = 0;
+ for (typename Object::IDToPointerMap::const_iterator i = objmap.begin();
+ i != objmap.end(); ++i)
+ {
+ if (i->second->IsDerivedFrom (classID))
+ {
+ derivedObjects->push_back (getter(*i));
+ count++;
+ }
+ }
+
+ if (sorted && count)
+ std::sort(derivedObjects->begin(), derivedObjects->end(), pred);
+
+ return count;
+}
+
+int Object::FindAllDerivedObjects (int classID, vector<SInt32>* derivedObjects, bool sorted)
+{
+ return FindAllDerivedObjectsImpl (*ms_IDToPointer, classID,
+ GetConstFirst(), derivedObjects, sorted, std::less<SInt32>());
+}
+
+struct CompareInstanceID
+{
+ bool operator () (const Object* lhs, const Object* rhs) const
+ {
+ return lhs->GetInstanceID() < rhs->GetInstanceID();
+ }
+};
+
+int Object::FindObjectsOfType (int classID, vector<Object*>* derivedObjects, bool sorted)
+{
+ return FindAllDerivedObjectsImpl (*ms_IDToPointer, classID,
+ GetConstSecond(), derivedObjects, sorted, CompareInstanceID());
+}
+
+int Object::FindObjectsOfType (int classID, dynamic_array<Object*>* derivedObjects, bool sorted)
+{
+ int count = 0;
+ IDToPointerMap::iterator i;
+ for (i=ms_IDToPointer->begin ();i!=ms_IDToPointer->end ();i++)
+ {
+ if (i->second->IsDerivedFrom (classID))
+ {
+ if (derivedObjects != NULL)
+ derivedObjects->push_back (i->second);
+ count++;
+ }
+ }
+
+
+ if (sorted && derivedObjects != NULL)
+ {
+ CompareInstanceID compare;
+ sort(derivedObjects->begin(), derivedObjects->end(), compare);
+ }
+
+
+ return count;
+}
+
+
+const std::string& Object::ClassIDToString (int ID)
+{
+ static std::string emptyString;
+ RTTIMap::iterator i = gRTTI->find (ID);
+ if (i == gRTTI->end ())
+ return emptyString;
+ else
+ return i->second.className;
+}
+
+int Object::StringToClassID (const string& classString)
+{
+ StringToClassIDMap::iterator i;
+ i = gStringToClassID->find (const_cast<char*> (classString.c_str ()));
+ if (i == gStringToClassID->end ())
+ return -1;
+ else
+ return i->second;
+}
+
+int Object::StringToClassIDCaseInsensitive (const string& classString)
+{
+ StringToClassIDMap::iterator i;
+ string lowerClass = ToLower(classString);
+ for (StringToClassIDMap::iterator i = gStringToClassID->begin(); i!=gStringToClassID->end(); i++)
+ {
+ if (ToLower(string(i->first)) == lowerClass)
+ return i->second;
+ }
+ return -1;
+}
+
+int Object::StringToClassID (const char* classString)
+{
+ StringToClassIDMap::iterator i;
+ i = gStringToClassID->find (const_cast<char*> (classString));
+ if (i == gStringToClassID->end ())
+ return -1;
+ else
+ return i->second;
+}
+
+const std::string& Object::GetClassName () const
+{
+ return Object::ClassIDToString (GetClassID ());
+}
+
+int Object::GetSuperClassID (int classID)
+{
+ RTTIMap::iterator i = gRTTI->find (classID);
+ AssertIf (i == gRTTI->end ());
+ if (i->second.base)
+ return i->second.base->classID;
+ else
+ return ClassID (Object);
+}
+
+Object::RTTI* Object::ClassIDToRTTI (int classID)
+{
+ RTTIMap::iterator i = gRTTI->find (classID);
+ if (i == gRTTI->end ())
+ return NULL;
+ else
+ return &i->second;
+}
+
+struct BuildClassInfo
+{
+ Object::RTTI* klass;
+ Object::RTTI* base;
+ UInt32 id;
+ UInt32 subclasses;
+ UInt32 level;
+ UInt32 newId;
+ UInt32 subClassIdAssign;
+ void Clear(){
+ base = 0;
+ klass = 0;
+ id = (UInt32)-1;
+ subclasses = 0;
+ level = 0xffffffff;
+ newId = -1;
+ subClassIdAssign = 0;
+ }
+ void Init(Object::RTTI* klass, Object::RTTI* base)
+ {
+ this->klass = klass;
+ this->base = base;
+ }
+ bool operator <( const BuildClassInfo& o) const
+ {
+ return level < o.level;
+ }
+};
+int bitsRequired(int subclasses)
+{
+ int r = 0;
+ while(subclasses)
+ {
+ r++;
+ subclasses >>= 1;
+ }
+ return r;
+}
+
+void Object::StaticInitialize()
+{
+ SET_ALLOC_OWNER(gBaseObjectManagerContainer);
+ Object::ms_IDToPointer = UNITY_NEW(Object::IDToPointerMap (1024 * 128), kMemBaseObject);
+}
+
+void Object::StaticDestroy()
+{
+ UNITY_DELETE(Object::ms_IDToPointer,kMemBaseObject);
+}
+
+void Object::InitializeAllClasses ()
+{
+ SET_ALLOC_OWNER(gBaseObjectManagerContainer);
+
+ if (gRegisterClassCallbacks == NULL)
+ return;
+
+#if SHOW_REGISTERED_CLASS_INFO
+ int registeredClasses = 0;
+#endif
+
+ RegisterClassCallbacks& callbacks = *gRegisterClassCallbacks;
+ // The callback is the RegisterClass function defined in ObjectDefines.h
+ // It calls the static Object::RegisterClass function which sets up the rtti system
+ for (int i=0;i<callbacks.size ();i++)
+ {
+ if (callbacks[i].registerClass)
+ {
+ callbacks[i].registerClass ();
+#if SHOW_REGISTERED_CLASS_INFO
+ ++registeredClasses;
+#endif
+ }
+ }
+
+#if SHOW_REGISTERED_CLASS_INFO
+ printf_console ("Object::InitializeAllClasses: %d total, %d registered\n", callbacks.size(), registeredClasses);
+#endif
+
+ AssertIf (gRTTI->empty ());
+
+ // Setup ms_IsDerivedFrom lookup bitmap
+ if (UNITY_EDITOR)
+ {
+ ms_MaxClassID = (--gRTTI->end ())->first + 1;
+ Assert(kLargestEditorClassID == ms_MaxClassID);
+ }
+ else
+ {
+ ms_MaxClassID = kLargestRuntimeClassID;
+ }
+
+ gIsDerivedFromBitMap->resize (ms_MaxClassID * ms_MaxClassID, false);
+ ms_IsDerivedFromBitMap = (UInt32*) gIsDerivedFromBitMap->m_bits;
+ gRegisteredClassIDs->resize (ms_MaxClassID, false);
+ for (int i=0;i<ms_MaxClassID;i++)
+ {
+ RTTIMap::iterator iRTTI = gRTTI->find (i);
+ (*gRegisteredClassIDs)[i] = iRTTI != gRTTI->end ();
+ if ((*gRegisteredClassIDs)[i])
+ {
+ for (int j=0;j<ms_MaxClassID;j++)
+ {
+ RTTIMap::iterator jRTTI = gRTTI->find (j);
+ if (jRTTI != gRTTI->end ())
+ (*gIsDerivedFromBitMap)[i * ms_MaxClassID + j] = IsDerivedFromRTTI (&iRTTI->second, &jRTTI->second);
+ }
+ }
+ }
+
+#if USE_NEW_IS_DERIVED_FROM
+ ms_ClassIDMask = &gClassIDMask[0];
+ ms_ClassIsDerivedFrom = new UInt32[ms_MaxClassID];
+ memset(ms_ClassIsDerivedFrom, 0xffffffff, sizeof(UInt32) * ms_MaxClassID);
+ ms_ClassIsDerivedFrom[0] = 1;
+ BuildClassInfo* info = (BuildClassInfo*)alloca(sizeof(BuildClassInfo) * ms_MaxClassID);
+ for(int i = 0; i < ms_MaxClassID; ++i)
+ info[i].Clear();
+ int maxSubClasses[32];
+ int bitShift[32];
+ memset(maxSubClasses, 0, sizeof(maxSubClasses));
+ memset(bitShift, 0, sizeof(bitShift));
+ //search for the base class
+ typedef RTTIMap::iterator itr;
+ for(itr i = gRTTI->begin(); i != gRTTI->end(); ++i)
+ {
+ Object::RTTI& r = i->second;
+ SInt32 oldId = i->first;
+ info[oldId].Init(&r, r.base);
+ info[oldId].id = oldId;
+ int level = 0;
+ Object::RTTI* base = r.base;
+ while(base)
+ {
+ ++level;
+ base = base->base;
+ }
+ info[oldId].level = level;
+ if(r.base)
+ info[r.base->classID].subclasses++;
+ }
+ std::sort(&info[0], ms_MaxClassID + &info[0]);
+ for(int i = 0; i < ms_MaxClassID; ++i)
+ {
+ int level = info[i].level;
+ maxSubClasses[level] = info[i].subclasses > maxSubClasses[level] ? info[i].subclasses : maxSubClasses[level];
+ }
+ int totalBits = 1;
+ UInt32 mask = 1;
+ bitShift[0] = 0;
+ gClassIDMask[0] = 1;
+ for(int i = 1; i < 32; ++i)
+ {
+ int bitsReq = bitsRequired(maxSubClasses[i-1]);
+ int bitsReq1 = bitsReq;
+ while(bitsReq--)
+ mask = (mask<<1) | 1;
+
+ bitShift[i] = totalBits;
+ totalBits += bitsReq1;
+ AssertIf(totalBits > 32 - CLASS_ID_MASK_BITS); //OUT OF BITS
+ gClassIDMask[i] = mask;
+ }
+#define VERIFY_CLASS_IDS 0
+#if VERIFY_CLASS_IDS
+ std::set<UInt32> ClassIdSet;
+#endif
+ int lastIndex = 0;
+ for(int i = 0; i < ms_MaxClassID; ++i)
+ {
+ Object::RTTI* klass = info[i].klass;
+ Object::RTTI* base = info[i].base;
+ int level = info[i].level;
+ int parentId = 0;
+ int parentIndex = -1;
+ int parentSubIndex = 1;
+ if(base)
+ {
+ for(int j = 0; j < ms_MaxClassID; ++j)
+ {
+ if(info[j].klass == base)
+ {
+ parentIndex = j;
+ break;
+ }
+ }
+ AssertIf(parentIndex >= i);
+ parentId = info[parentIndex].newId & CLASS_ID_MASK_IDS;
+ AssertIf(-1 == parentId);
+ parentSubIndex = info[parentIndex].subClassIdAssign++;
+ }
+ else
+ {
+ parentSubIndex = 1;
+ if(i != 0)
+ {
+ lastIndex = i;
+ break;
+ }
+ }
+ int shift = bitShift[level];
+ int id = (parentSubIndex << shift) | parentId;
+ int fullId = id | (level<<(32-CLASS_ID_MASK_BITS));
+#if VERIFY_CLASS_IDS
+ AssertIf(ClassIdSet.find(fullId) != ClassIdSet.end() || ClassIdSet.find(id) != ClassIdSet.end() ); // DUPE CLASSID. should never happen
+ ClassIdSet.insert(id);
+ ClassIdSet.insert(fullId);
+#endif
+ info[i].newId = fullId;
+ ms_ClassIsDerivedFrom[info[i].id] = fullId;
+ AssertIf(info[i].subClassIdAssign != 0);
+ info[i].subClassIdAssign = 1;
+ }
+#endif
+}
+
+void Object::CallInitializeClass()
+{
+ RegisterClassCallbacks& callbacks = *gRegisterClassCallbacks;
+
+ // Call the IntializeClass function for classes that registered for it (IMPLEMENT_CLASS_HAS_INIT)
+ // This is done after all classes are registered and the rtti setup
+ // so that the rtti system can be used insie InitializeClass ()
+ for (int i=0;i<callbacks.size ();i++)
+ {
+ if (callbacks[i].initClass)
+ {
+ callbacks[i].initClass ();
+ }
+ }
+}
+
+void Object::CallPostInitializeClass()
+{
+ RegisterClassCallbacks& callbacks = *gRegisterClassCallbacks;
+
+ // Call the PostIntializeClass function for classes that registered for it (IMPLEMENT_CLASS_HAS_POSTINIT)
+ // This is done after all classes are registered and the rtti setup
+ // so that the rtti system can be used inside PostInitializeClass ()
+ for (int i=0;i<callbacks.size ();i++)
+ {
+ if (callbacks[i].postInitClass)
+ {
+ callbacks[i].postInitClass ();
+ }
+ }
+}
+
+void Object::AddEvent (EventCallback* callback, void* userData)
+{
+ m_EventIndex = GetEventManager().AddEvent(callback, userData, m_EventIndex);
+}
+
+void Object::RemoveEvent (EventCallback* callback, void* userData)
+{
+ m_EventIndex = GetEventManager().RemoveEvent(m_EventIndex, callback, userData);
+}
+
+bool Object::HasEvent (EventCallback* callback, const void* userData) const
+{
+ return EventManager::HasEvent(m_EventIndex, callback, userData);
+}
+
+void Object::InvokeEvent (int eventType)
+{
+ EventManager::InvokeEvent(m_EventIndex, this, eventType);
+}
+
+bool IsObjectAvailable (int instanceID)
+{
+ Object* temp = Object::IDToPointer (instanceID);
+ if (temp != NULL)
+ return true;
+
+ return GetPersistentManager ().IsObjectAvailable (instanceID);
+}
+#if !USE_NEW_IS_DERIVED_FROM
+#if !UNITY_RELEASE
+
+bool Object::IsDerivedFromClassID (int classID, int compareClass)
+{
+ if (classID >= ms_MaxClassID || classID < 0)
+ {
+ char buffy[512];
+ sprintf (buffy, "The class with classID: %d out of bounds", classID);
+ AssertString (buffy);
+ return false;
+ }
+ if (compareClass >= ms_MaxClassID || compareClass < 0)
+ {
+ /*
+ char buffy[512];
+ sprintf (buffy, "The compare class with classID: %d out of bounds", compareClass);
+ AssertString (buffy);
+ */
+ return false;
+ }
+
+ AssertIf (classID >= ms_MaxClassID || classID < 0);
+ AssertIf (compareClass >= ms_MaxClassID || compareClass < 0);
+
+
+ if (!(*gRegisteredClassIDs)[classID])
+ {
+ char buffy[512];
+ sprintf (buffy, "The class with classID: %d is not registered (see ClassIDs.h)", classID);
+ AssertString (buffy);
+
+ }
+
+ // When doing classID stripping
+ #if !ALLOW_CLASS_ID_STRIPPING
+ if (!(*gRegisteredClassIDs)[compareClass])
+ {
+ char buffy[512];
+ sprintf (buffy, "The class with classID: %d is not registered (see ClassIDs.h)", compareClass);
+ AssertString (buffy);
+ }
+ #endif
+
+ int index = classID * ms_MaxClassID + compareClass;
+ int block = index >> 5;
+ int bit = index - (block << 5);
+ return (ms_IsDerivedFromBitMap[block]) & (1 << bit);
+}
+
+#endif
+#endif
+
+INSTANTIATE_TEMPLATE_TRANSFER_WITH_DECL (Object, EXPORTDLL);
+
+template<class TransferFunction>
+void Object::Transfer (TransferFunction& transfer)
+{
+#if UNITY_EDITOR
+ if (!transfer.IsSerializingForGameRelease() && SerializePrefabIgnoreProperties(transfer))
+ {
+ UInt32 flags = m_HideFlags;
+ transfer.Transfer(flags, "m_ObjectHideFlags", kHideInEditorMask);
+ m_HideFlags = flags;
+ }
+
+ if (transfer.GetFlags () & kSerializeDebugProperties)
+ {
+ SInt32 instanceID = GetInstanceID ();
+ transfer.Transfer (instanceID, "m_InstanceID");
+
+ LocalIdentifierInFileType fileID;
+ if (IsPersistent ())
+ fileID = GetPersistentManager ().GetLocalFileID (instanceID);
+ else
+ fileID = GetFileIDHint ();
+
+ transfer.Transfer (fileID, "m_LocalIdentfierInFile");
+ }
+#endif
+}
+
+#if UNITY_EDITOR
+
+void Object::RegisterDirtyCallback (ObjectDirtyCallbackFunction* callback)
+{
+ gSetDirtyCallbackFunc = callback;
+}
+
+Object::ObjectDirtyCallbackFunction* Object::GetDirtyCallback ()
+{
+ return gSetDirtyCallbackFunc;
+}
+
+void Object::SetDirty ()
+{
+ // When we run out of dirty indices, make sure it stays at 1
+ m_DirtyIndex++;
+ if (m_DirtyIndex == 0)
+ m_DirtyIndex = 1;
+
+ if (gSetDirtyCallbackFunc)
+ gSetDirtyCallbackFunc (this);
+
+ #if !UNITY_RELEASE
+ m_DEBUGCLASSID = GetClassID ();
+ #endif
+}
+
+void Object::ClearPersistentDirty ()
+{
+ m_DirtyIndex = 0;
+}
+
+void Object::SetPersistentDirtyIndex (UInt32 dirtyValue)
+{
+ m_DirtyIndex = dirtyValue;
+}
+
+
+#endif
+
+#if DEBUGMODE
+void AddVerifyClassRegistration (int classID)
+{
+ if (gVerifyRegisteredClasses == NULL)
+ {
+ SET_ALLOC_OWNER(gBaseObjectManagerContainer);
+ gVerifyRegisteredClasses = UNITY_NEW(RegisteredClassSet, kMemManager)();
+ }
+ gVerifyRegisteredClasses->insert(classID);
+}
+const RegisteredClassSet& GetVerifyClassRegistration ()
+{
+ if (gVerifyRegisteredClasses == NULL)
+ {
+ SET_ALLOC_OWNER(gBaseObjectManagerContainer);
+ gVerifyRegisteredClasses = UNITY_NEW(RegisteredClassSet, kMemManager)();
+ }
+ return *gVerifyRegisteredClasses;
+}
+
+#endif
+
+
+void RegisterInitializeClassCallback (int classID,
+ RegisterClassCallback* registerClass,
+ RegisterClassCallback* initClass,
+ RegisterClassCallback* postInitClass,
+ RegisterClassCallback* cleanupClass)
+{
+ if (gRegisterClassCallbacks == NULL)
+ {
+ SET_ALLOC_OWNER(gBaseObjectManagerContainer);
+ gRegisterClassCallbacks = UNITY_NEW(RegisterClassCallbacks,kMemBaseObject);
+ }
+ if (gRegisterClassCallbacks->size () <= classID)
+ gRegisterClassCallbacks->resize (classID + 1);
+
+ RegisterClassCallbacks& callbacks = *gRegisterClassCallbacks;
+ if (callbacks[classID].registerClass != NULL || callbacks[classID].initClass != NULL || callbacks[classID].postInitClass != NULL || callbacks[classID].cleanupClass != NULL)
+ {
+ char buffer[512];
+ sprintf (buffer, "ClassID: %d is already registered. ClassID's have to be unique", classID);
+ FatalErrorString (buffer);
+ AssertBreak(false);
+ }
+ callbacks[classID].registerClass = registerClass;
+ callbacks[classID].initClass = initClass;
+ callbacks[classID].postInitClass = postInitClass;
+ callbacks[classID].cleanupClass = cleanupClass;
+}
+
+void Object::CleanupAllClasses ()
+{
+ AssertIf(!ms_IDToPointer->empty());
+
+ if (!gRegisterClassCallbacks)
+ return;
+
+ RegisterClassCallbacks& callbacks = *gRegisterClassCallbacks;
+ for (int i=0;i<callbacks.size ();i++)
+ {
+ if (callbacks[i].cleanupClass)
+ callbacks[i].cleanupClass ();
+ }
+
+ UNITY_DELETE(gRegisterClassCallbacks, kMemBaseObject);
+}
+
+void SetDisableImmediateDestruction (bool disable)
+{
+ gDisableImmediateDestruction = disable;
+}
+
+bool GetDisableImmediateDestruction ()
+{
+ return gDisableImmediateDestruction;
+}
+
+void delete_object_internal (Object* p)
+{
+ if (!p)
+ return;
+
+ delete_object_internal_step1 (p);
+ delete_object_internal_step2 (p);
+}
+
+// This can be execute on any thread.
+void delete_object_internal_step2 (Object* p)
+{
+ MemLabelId label = p->GetMemoryLabel();
+ p->~Object ();
+ UNITY_FREE(label, p);
+}
+
+void UnloadObject (Object* p)
+{
+ if (!p)
+ return;
+
+ LockObjectCreation();
+ delete_object_internal(p);
+ UnlockObjectCreation();
+}
+
+void Object::DoneLoadingManagers()
+{
+ // We are done loading managers. Start instance IDs from a high constant value here,
+ // so new managers and built-in resources can be added without changed instanceIDs
+ // used by the content.
+ if (gLowestInstanceID > -10000)
+ {
+ gLowestInstanceID = -10000;
+ }
+}
+
+MemLabelId Object::GetMemoryLabel() const
+{
+ MemLabelIdentifier id = (MemLabelIdentifier)m_MemLabel;
+ MemLabelId label(id, NULL);
+ if(m_IsRootOwner)
+ label.SetRootHeader(GET_ALLOC_HEADER((void*)this, label));
+ return label;
+}
+
+int Object::GetRuntimeMemorySize() const
+{
+#if ENABLE_MEM_PROFILER
+ return GetMemoryProfiler()->GetRelatedMemorySize((void*)this);
+#else
+ return 0;
+#endif
+}
+
+
+#if !UNITY_RELEASE
+
+void Object::CheckCorrectAwakeUsage()
+{
+ // check only if saw that object already to allow delayed awake and immediate destroy
+ if ( m_AwakeCalled == 0 )
+ AssertStringObject(Format("Awake has not been called '%s' (%s). Figure out where the object gets created and call AwakeFromLoad correctly.", GetName(), GetClassName().c_str()), this);
+
+ if ( m_ResetCalled == 0 )
+ AssertStringObject(Format("Reset has not been called '%s' (%s). Figure out where the object gets created and call Reset correctly.", GetName(), GetClassName().c_str()), this);
+
+ if ( m_AwakeThreadedCalled && !m_AwakeDidLoadThreadedCalled )
+ AssertStringObject(Format("AwakeFromLoadThreaded has not been called '%s' (%s). Figure out where the object gets created and call AwakeFromLoadThreaded correctly.", GetName(), GetClassName().c_str()), this);
+}
+
+
+#endif // !UNITY_RELEASE
diff --git a/Runtime/BaseClasses/BaseObject.h b/Runtime/BaseClasses/BaseObject.h
new file mode 100644
index 0000000..52613be
--- /dev/null
+++ b/Runtime/BaseClasses/BaseObject.h
@@ -0,0 +1,1162 @@
+#ifndef BASEOBJECT_H
+#define BASEOBJECT_H
+
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Serialize/SerializationMetaFlags.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Utilities/Prefetch.h"
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+#include <string>
+#include <vector>
+
+#include "Runtime/Utilities/dense_hash_map.h"
+#include "Runtime/Utilities/HashFunctions.h"
+
+#include "Runtime/BaseClasses/ClassIDs.h"
+
+class ProxyTransfer;
+class SafeBinaryRead;
+template<bool kSwap>
+class StreamedBinaryRead;
+template<bool kSwap>
+class StreamedBinaryWrite;
+class RemapPPtrTransfer;
+class TypeTree;
+class Object;
+struct EventEntry;
+#if SUPPORT_TEXT_SERIALIZATION
+class YAMLRead;
+class YAMLWrite;
+#endif
+
+#include "ObjectDefines.h"
+#include <string>
+#include <typeinfo>
+
+//#define DefineClassID( x, classID )
+//#define ClassID( x )
+
+// Every non-abstract class that is derived from object has to place this inside the class Declaration
+// (REGISTER_DERIVED_CLASS (Foo, Object))
+
+// Every abstract class that is derived from object has to place this inside the class Declaration
+// (REGISTER_DERIVED_ABSTRACT_CLASS (Foo, Object))
+
+//In the cpp file of every object derived class you have to place eg. IMPLEMENT_CLASS (Foo)
+//#define IMPLEMENT_CLASS(x)
+// or IMPLEMENT_CLASS_HAS_INIT (x) which will call the static class Function InitializeClass (); on startup.
+
+using std::string;
+
+template<class T>
+class PPtr
+{
+ SInt32 m_InstanceID;
+ #if !UNITY_RELEASE
+ mutable T* m_DEBUGPtr;
+ #endif
+
+ protected:
+
+ inline void AssignObject (const Object* o);
+
+ private:
+ static string s_TypeString;
+
+ public:
+
+ static const char* GetTypeString ();
+ static bool IsAnimationChannel () { return false; }
+ static bool MightContainPPtr () { return true; }
+ static bool AllowTransferOptimization () { return false; }
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+ // Assignment
+ explicit PPtr (int instanceID)
+ {
+ m_InstanceID = instanceID;
+ #if !UNITY_RELEASE
+ m_DEBUGPtr = NULL;
+ #endif
+ }
+ PPtr (const T* o) { AssignObject (o); }
+ PPtr (const PPtr<T>& o)
+ {
+ m_InstanceID = o.m_InstanceID;
+ #if !UNITY_RELEASE
+ m_DEBUGPtr = NULL;
+ #endif
+ }
+
+ PPtr ()
+ {
+ #if !UNITY_RELEASE
+ m_DEBUGPtr = NULL;
+ #endif
+ m_InstanceID = 0;
+ }
+
+ PPtr& operator = (const T* o) { AssignObject (o); return *this; }
+ PPtr& operator = (const PPtr<T>& o)
+ {
+ #if !UNITY_RELEASE
+ m_DEBUGPtr = NULL;
+ #endif
+ m_InstanceID = o.m_InstanceID; return *this;
+ }
+
+ void SetInstanceID (int instanceID) { m_InstanceID = instanceID; }
+ int GetInstanceID ()const { return m_InstanceID; }
+
+ // Comparison
+ bool operator < (const PPtr& p)const { return m_InstanceID < p.m_InstanceID; }
+ bool operator == (const PPtr& p)const { return m_InstanceID == p.m_InstanceID; }
+ bool operator != (const PPtr& p)const { return m_InstanceID != p.m_InstanceID; }
+
+ // MSVC gets confused whether it should use operator bool(), or operator T* with implicit
+ // comparison to NULL. So we add explicit functions and use them instead.
+ bool IsNull() const;
+ bool IsValid() const;
+
+ operator T* () const;
+ T* operator -> () const;
+ T& operator * () const;
+};
+
+template<class T>
+class ImmediatePtr
+{
+ mutable intptr_t m_Ptr;
+ #if !UNITY_RELEASE
+ mutable T* m_DEBUGPtr;
+ #endif
+
+ void AssignInstanceID (int instanceID)
+ {
+ AssertIf (instanceID & 1); m_Ptr = instanceID | 1; AssertIf ((m_Ptr & 1) == 0);
+ #if !UNITY_RELEASE
+ m_DEBUGPtr = NULL;
+ #endif
+ }
+ void AssignObject (const T* o)
+ {
+ m_Ptr = (intptr_t)o; AssertIf (m_Ptr & 1);
+ #if !UNITY_RELEASE
+ m_DEBUGPtr = const_cast<T*>(o);
+ #endif
+ }
+ void Load () const
+ {
+ AssertIf ((m_Ptr & 1) == 0);
+ T* loaded = PPtr<T> (m_Ptr & (~1));
+ m_Ptr = (intptr_t)(loaded);
+ AssertIf (m_Ptr & 1);
+ #if !UNITY_RELEASE
+ m_DEBUGPtr = loaded;
+ #endif
+ }
+
+ inline T* GetPtr () const
+ {
+ if ((m_Ptr & 1) == 0)
+ {
+ return (T*)(m_Ptr);
+ }
+ else
+ {
+ Load ();
+ return (T*)(m_Ptr);
+ }
+ }
+
+ static string s_TypeString;
+
+ public:
+
+ bool IsLoaded () const;
+
+ static const char* GetTypeString ();
+ static bool IsAnimationChannel () { return false; }
+ static bool MightContainPPtr () { return true; }
+ static bool AllowTransferOptimization () { return false; }
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+ // Assignment
+ ImmediatePtr (const T* o) { AssignObject (o); }
+ ImmediatePtr (const ImmediatePtr<T>& o) { m_Ptr = o.m_Ptr; }
+ ImmediatePtr () { m_Ptr = 0; }
+
+ ImmediatePtr& operator = (const T* o) { AssignObject (o); return *this; }
+
+ void SetInstanceID (int instanceID) { AssignInstanceID (instanceID); }
+ int GetInstanceID ()const
+ {
+ if ((m_Ptr & 1) == 0 && m_Ptr != 0)
+ {
+ T* o = (T*)(m_Ptr);
+ SInt32 instanceID = o->GetInstanceID ();
+ AssertIf (instanceID & 1);
+ return instanceID;
+ }
+ else
+ return m_Ptr & (~1);
+ }
+
+ inline bool operator == (const T* p)const { return GetPtr () == p; }
+ inline bool operator != (const T* p)const { return GetPtr () != p; }
+
+ inline operator T* () const { return GetPtr (); }
+ inline T* operator -> () const { T* o = GetPtr (); AssertIf (o == NULL); return o; }
+ inline T& operator * () const { T* o = GetPtr (); AssertIf (o == NULL); ANALYSIS_ASSUME(o); return *o; }
+};
+
+template<typename T> class PtrToType;
+template<typename T> class PtrToType<T*>
+{
+public:
+ typedef T value_type;
+};
+
+template<class T, class U>
+T dynamic_pptr_cast (U* ptr)
+{
+ typedef typename PtrToType<T>::value_type Type;
+ T castedPtr = (T)(ptr);
+ if (castedPtr && castedPtr->IsDerivedFrom ( Type::GetClassIDStatic ()))
+ return castedPtr;
+ else
+ return NULL;
+}
+
+template<class T, class U>
+T dynamic_pptr_cast (const PPtr<U>& ptr)
+{
+ U* o = ptr;
+ return dynamic_pptr_cast<T> (o);
+}
+
+template<class T> inline
+T dynamic_instanceID_cast (int instanceID)
+{
+ Object* o = PPtr<Object> (instanceID);
+ return dynamic_pptr_cast<T> (o);
+}
+
+template<class T, class U>
+PPtr<T> assert_pptr_cast (const PPtr<U>& ptr)
+{
+ #if DEBUGMODE
+ U* u = ptr;
+ AssertIf (dynamic_pptr_cast<U*> (u) == NULL && u != NULL);
+ #endif
+ return PPtr<T> (ptr.GetInstanceID ());
+}
+
+// Enables boost::mem_fn to use PPtr properly, needed for boost::bind
+template<typename T> inline T * get_pointer(PPtr<T> const & p)
+{
+ return p;
+}
+
+
+enum ObjectCreationMode
+{
+ // Create the object from the main thread in a perfectly normal way
+ kCreateObjectDefault = 0,
+ // Create the object from another thread. Might assign an instance ID but will not register with IDToPointer map.
+ // Objects created like this, need to call, AwakeFromLoadThraded, and Object::RegisterInstanceID and AwakeFromLoad (kDidLoadThreaded); from the main thread
+ kCreateObjectFromNonMainThread = 1,
+ // Create the object and register the instance id but do not lock the object
+ // creation mutex because the code calling it already called LockObjectCreation mutex.
+ kCreateObjectDefaultNoLock = 2
+};
+
+
+enum AwakeFromLoadMode
+{
+ // This is the default, usually called from the inspector or various serialization methods
+ kDefaultAwakeFromLoad = 0,
+ // The object was loaded from disk
+ kDidLoadFromDisk = 1 << 0,
+ // The object was loaded from a loading thread (in almost all cases through loading from disk asynchronously)
+ kDidLoadThreaded = 1 << 1,
+ // Object was instantiated and is now gettings it's first Awake function or it was created from code and gets the Awake function called
+ kInstantiateOrCreateFromCodeAwakeFromLoad = 1 << 2,
+ // GameObject was made active or a component was added to an active game object
+ kActivateAwakeFromLoad = 1 << 3,
+
+ kDefaultAwakeFromLoadInvalid = -1
+};
+
+class BaseAllocator;
+
+enum ObjectDeleteMode
+{
+ kUnknownMode = 0
+};
+
+class EXPORT_COREMODULE Object
+{
+ protected:
+ virtual ~Object ();
+
+ public:
+
+ Object (MemLabelId label, ObjectCreationMode mode);
+
+
+ /// By default the destructor might get executed on another thread.
+ /// This lets us distribute large level unloads to another thread and thus avoid hiccups.
+ /// Some classes need to deallocate resources on the main thread.
+ /// You can implement this function to delete the resources.
+ ///
+ /// The destructor will still be called in this case, thus you need to ensure that values are set to NULL.
+ /// MainThreadCleanup is only called if the deallocations are done on another thread.
+ /// Thus the destructor needs to handle the case where it is not called correctly.
+ ///
+ /// If you override the function return true, since it indicates that the class requires the function to be called.
+ /// SA: DoesClassRequireMainThreadDeallocation
+ virtual bool MainThreadCleanup ();
+
+ /// To destroy objects use delete_object instead of delete operator
+ /// The default way to destroy objects is using the DestroyObject Function, which also destroys the object from it's file
+ /// Must be protected by LockObjectCreation / UnlockObjectCreation
+ friend void delete_object_internal_step1 (Object* p);
+ friend void delete_object_internal_step2 (Object* p);
+
+ /// AwakeFromLoad is called after an object was read using Transfer (Either from disk or a vector)
+ /// This means it is called after the inspector has been updated, after it is loaded from disk, after a prefab has been modified or
+ /// the animation system has changed values behind your back.
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode)
+ {
+ #if !UNITY_RELEASE
+ m_AwakeCalled = 1;
+
+ if( awakeMode & kDidLoadThreaded )
+ m_AwakeDidLoadThreadedCalled = 1;
+ #endif
+ }
+
+ virtual void AwakeFromLoadThreaded ()
+ {
+ #if !UNITY_RELEASE
+ m_AwakeThreadedCalled = 1;
+ m_AwakeCalled = 0;
+ m_AwakeDidLoadThreadedCalled = 0;
+ #endif
+ }
+
+ /// For Subclasses: Makes sure that persistent variables are correct and if not corrects them
+ /// It is called after Prefab propagation, SafeBinaryRead and PropertyEditor changes.
+ /// It is called before AwakeFromLoad
+ virtual void CheckConsistency () { }
+
+ /// Override Reset in order to setup default values for the Object
+
+ /// The difference between setting up default values in the constructor
+ /// and Reset is that Reset is only called when the editor creates a new object
+ /// or when the object uses SerializeSafeBinary read.
+ /// Thus Reset can be used as a performance optimization for touching variables which are serialized only once during load.
+ /// * All variables that are serialized and Reset in the Reset function, do not have to be initialized in the constructor*
+ /// Reset functions might get called from different threads during loading, thus they may not derefence other objects, in that case use SmartReset.
+ /// You can always rely on that AwakeFromLoad is called after Reset has been called.
+ virtual void Reset ()
+ {
+ #if !UNITY_RELEASE
+ m_ResetCalled = 1;
+ m_AwakeCalled = 0;
+ m_AwakeThreadedCalled = 0;
+ m_AwakeDidLoadThreadedCalled = 0;
+ #endif
+ }
+
+ // Smart Reset is called when Reset is selected or when AddComponent is called and a new ScriptableObject is created.
+ // If you want to for example adjust a collider bounding volume by the renderers mesh, use SmartReset, you can not use Reset for this!
+ virtual void SmartReset ()
+ {
+ #if !UNITY_RELEASE
+ m_ResetCalled = 1;
+ m_AwakeCalled = 0;
+ m_AwakeThreadedCalled = 0;
+ m_AwakeDidLoadThreadedCalled = 0;
+ #endif
+ }
+
+#if !UNITY_RELEASE
+ // use it to check AwakeFromLoad/AwakeFromLoadThreaded/Reset/SmartReset were correctly called
+ void CheckCorrectAwakeUsage();
+
+ // hacks to set debug flags in cases you REALLY know what you are doing
+
+ // call when you don't want to Reset in case of object fully inited and don't need to be reset to default state
+ // e.g. if you de-serialize object - you don't need reset
+ inline void HackSetResetWasCalled() { m_ResetCalled = 1; }
+
+ // call when AwakeFromLoad has some side-effects so you need to postpone that call for indefinite time
+ // e.g. AudioClip will try to load sound in AwakeFromLoad, so you better do this only when needed
+ inline void HackSetAwakeWasCalled() { m_AwakeCalled = 1; }
+
+ // same as HackSetAwakeWasCalled but for Awake with kDidLoadThreaded param
+ inline void HackSetAwakeDidLoadThreadedWasCalled () { m_AwakeDidLoadThreadedCalled = true; }
+ #else
+ inline void HackSetResetWasCalled() {}
+ inline void HackSetAwakeWasCalled() {}
+ inline void HackSetAwakeDidLoadThreadedWasCalled() {}
+ #endif
+
+ /// Get and set the name
+ virtual char const* GetName () const { return ""; };
+ virtual void SetName (char const* /*name*/) { }
+ void SetNameCpp (const std::string& name) { SetName(name.c_str()); }
+
+ #if UNITY_EDITOR
+ /// Return true if you want the inspector to automatically refresh without SetDirty being called.
+ virtual bool HasDebugmodeAutoRefreshInspector () { return false; }
+ virtual void WarnInstantiateDisallowed () {}
+ #endif
+
+ /// Returns the classID of the class
+ static int GetClassIDStatic () { return ClassID (Object); }
+
+ // Is the class sealed (No other class can inherit from it)
+ // A sealed class can perform a GetComponent call faster,
+ // since it can compare the ClassID directly instead of using the RTTI system.
+ static bool IsSealedClass () { return false; }
+
+ /// Returns true if the class is abstract
+ static bool IsAbstract () { return true; }
+
+ /// Creates an object of type classID.
+ /// if instanceID is 0 a unique id will be generated if its non 0 the object will have the specified instanceID
+ static Object* Produce (int classID, int instanceID = 0, MemLabelId = kMemBaseObject, ObjectCreationMode mode = kCreateObjectDefault);
+
+
+ // Static initializa and destroy for BaseObject
+ static void StaticInitialize();
+ static void StaticDestroy();
+
+ /// Registers instance id with IDToPointerMap
+ /// useful for thread loading with delayed activation from main thread
+ /// Can only be called from main thead
+ static void RegisterInstanceID (Object* obj);
+ static void RegisterInstanceIDNoLock (Object* obj);
+
+ /// Allocates new instanceID and registers it with IDToPointerMap
+ /// Can only be called from main thead
+ static Object* AllocateAndAssignInstanceID (Object* obj);
+ static Object* AllocateAndAssignInstanceIDNoLock (Object* obj);
+
+ #if UNITY_EDITOR
+ virtual void CloneAdditionalEditorProperties (Object& /*source*/) { }
+
+ /// Can assign variable allows you to do additional type checking when assiging a variable
+ /// in the property inspector.
+ /// Eg. MonoBehaviours checks if a monobehaviour can be assigned based on the actual Mono class
+ virtual bool CanAssignMonoVariable (const char* /*property*/, Object* /*object*/) { return false; }
+
+ #endif
+
+ virtual bool ShouldIgnoreInGarbageDependencyTracking () { return false; }
+
+ /// Gets the class ID
+ ClassIDType GetClassID () const { Assert(m_CachedClassID != 0); return (ClassIDType)m_CachedClassID; }
+ /// Gets the instance ID
+ int GetInstanceID () const { AssertIf(m_InstanceID == 0); return m_InstanceID; }
+ bool IsInstanceIDCreated () const { return m_InstanceID != 0; }
+
+ /// Is this instance derived from compareClassID
+ bool IsDerivedFrom (int compareClassID)const { return IsDerivedFromClassID (GetClassID (), compareClassID); }
+
+ #if UNITY_EDITOR
+
+ /// Has this object been synced with the PersistentManager
+ bool IsPersistentDirty () const { return m_DirtyIndex != 0; }
+
+ void SetPersistentDirtyIndex (UInt32 dirtyIndex);
+ UInt32 GetPersistentDirtyIndex () { return m_DirtyIndex; }
+
+ ////@TODO: Rename this to SetPersistentDirty
+
+ /// Whenever variables that are being serialized in Transfer change, SetDirty () should be called
+ /// This will allow tracking of objects that have changed since the last saving to disk or over the network
+ void SetDirty ();
+
+ /// This method can be called if you need to unload an object from memory even if it's dirty.
+ void ClearPersistentDirty ();
+
+ // Callback support for callbacks when SetDirty is called
+ typedef void ObjectDirtyCallbackFunction (Object* ptr);
+ static void RegisterDirtyCallback (ObjectDirtyCallbackFunction* callback);
+ static ObjectDirtyCallbackFunction* GetDirtyCallback ();
+
+ void SetFileIDHint (LocalIdentifierInFileType hint) { m_FileIDHint = hint; }
+ LocalIdentifierInFileType GetFileIDHint () const { return m_FileIDHint; }
+
+ #else
+ void SetDirty () { }
+ void ClearPersistentDirty () { }
+
+ #endif
+
+
+ // The name of the class
+ const std::string& GetClassName () const;
+
+ enum
+ {
+ kHideInHierarchy = 1 << 0,
+ kHideInspector = 1 << 1,
+ kDontSave = 1 << 2,
+ kNotEditable = 1 << 3,
+ kHideAndDontSave = kDontSave | kHideInHierarchy | kNotEditable
+ };
+
+ int GetHideFlags () const { return m_HideFlags; }
+ bool TestHideFlag (int mask) const { return (m_HideFlags & mask) == mask; }
+ bool TestHideFlagAny (int mask) const { return (m_HideFlags & mask) != 0; }
+
+ virtual void SetHideFlags (int flags) { m_HideFlags = flags; }
+ void SetHideFlagsObjectOnly (int flags) { Assert(flags < (1 << kHideFlagsBits)); m_HideFlags = flags; }
+
+ /// You must document all usage here in order to provide clear overview and avoid overlaps
+ /// - Transform root calculation for animation component, when binding animation states (Runtime only)
+ void SetTemporaryFlags (int flags) { Assert(flags < (1 << kTemporaryFlagsBits)); m_TemporaryFlags = flags; }
+ int GetTemporaryFlags () const { return m_TemporaryFlags; }
+#if UNITY_WINRT
+ /// Used by WinRT's GarbageCollectSharedAssets
+ void SetTemporaryUnusedAssetsFlags (int flags) { m_TemporaryUnusedAssetsFlags = flags; }
+ int GetTemporaryUnusedAssetsFlags () const { return m_TemporaryUnusedAssetsFlags; }
+#endif
+
+#if ENABLE_SCRIPTING
+ int GetGCHandle () const { return m_MonoReference; }
+#endif
+
+ /// Overall memory allocated for this object. Should calculate any memory allocated by other subystems as well.
+ /// For example if OpenGL allocates memory for a texture it must return how much memory we "think" OpenGL will allocate for the texture.
+ virtual int GetRuntimeMemorySize () const;
+
+ /// Is this object persistent?
+ bool IsPersistent () const { return m_IsPersistent; }
+
+ typedef InstanceIdToObjectPtrHashMap IDToPointerMap;
+
+ /// How many objects are there in memory?
+ static IDToPointerMap::size_type GetLoadedObjectCount () { return ms_IDToPointer->size (); }
+
+ // Finds the pointer to the object referenced by instanceID (NULL if none found in memory)
+ static Object* IDToPointer (int inInstanceID);
+ static Object* IDToPointerThreadSafe (int inInstanceID);
+
+ /// This function may not be called unless you use LockObjectCreation / UnlockObjectCreation from another thread first...
+ /// If you don't know 100% what you are doing use: IDToPointerThreadSafe instead
+ static Object* IDToPointerNoThreadCheck (int inInstanceID);
+
+ /// Finds out if classID is derived from compareClassID
+ static bool IsDerivedFromClassID (int classID, int derivedFromClassID);
+
+ /// Returns the super Class ID of classID.
+ /// if classID doesnt have any super Class it will return ClassID (Object)
+ static int GetSuperClassID (int classID);
+
+ /// Returns all classIDs that are derived from ClassID
+ static void FindAllDerivedClasses (int classID, std::vector<SInt32>* allDerivedClasses, bool returnOnlyNonAbstractClasses = true);
+ /// Returns how many objects are derived from classID
+ /// If allDerivedObjects != NULL, adds all derived object instanceIDs to the container
+ static int FindAllDerivedObjects (int classID, std::vector<SInt32>* derivedObjects, bool sorted = false);
+
+ static int FindObjectsOfType (int classID, std::vector<Object*>* derivedObjects, bool sorted = false);
+ template<class T>
+ static int FindObjectsOfType (std::vector<T*>* derivedObjects)
+ {
+ std::vector<Object*>* casted = reinterpret_cast<std::vector<Object*>*> (derivedObjects);
+ return FindObjectsOfType (T::GetClassIDStatic (), casted);
+ }
+
+ static int FindObjectsOfType (int classID, dynamic_array<Object*>* derivedObjects, bool sorted = false);
+ template<class T>
+ static int FindObjectsOfType (dynamic_array<T*>* derivedObjects)
+ {
+ dynamic_array<Object*>* casted = reinterpret_cast<dynamic_array<Object*>*> (derivedObjects);
+ return FindObjectsOfType (T::GetClassIDStatic (), casted);
+ }
+
+ template<class T>
+ static int FindObjectsOfTypeSorted (std::vector<T*>* derivedObjects)
+ {
+ std::vector<Object*>* casted = reinterpret_cast<std::vector<Object*>*> (derivedObjects);
+ return FindObjectsOfType (T::GetClassIDStatic (), casted, true);
+ }
+
+
+
+ /// Get the class name from the classID
+ static const std::string& ClassIDToString (int classID);
+ /// Get the classID from the class name, returns -1 if no classID was found
+ static int StringToClassID (const std::string& classString);
+ static int StringToClassIDCaseInsensitive (const std::string& classString);
+ static int StringToClassID (const char* classString);
+
+ /// Callback support for callbacks when an object is destroyed
+ typedef void ObjectDestroyCallbackFunction (int instanceID);
+ static void RegisterDestroyedCallback (ObjectDestroyCallbackFunction* callback);
+
+ /// Sets up the rtti for all classes that are derived from Object and
+ /// use the macro IMPLEMENT_CLASS or IMPLEMENT_CLASS_HAS_INIT
+ /// Calls the static function InitializeClass on every class that used
+ /// IMPLEMENT_CLASS_HAS_INIT instead of IMPLEMENT_CLASS
+ static void InitializeAllClasses ();
+ static void CallInitializeClassEarly();
+ static void CallInitializeClass();
+ static void CallPostInitializeClass();
+
+ static void CleanupAllClasses ();
+
+ /// Checks if an array of instance id's are loaded.
+ /// If an instanceID is loaded it is set to 0.
+ static void CheckInstanceIDsLoaded (SInt32* instanceIDs, int size);
+
+
+ typedef Object* FactoryFunction (MemLabelId label, ObjectCreationMode mode);
+ struct RTTI
+ {
+ RTTI* base;// super rtti class
+ Object::FactoryFunction* factory;// the factory function of the class
+ int classID;// the class ID of the class
+ std::string className;// the name of the class
+ int size;// sizeof (Class)
+ bool isAbstract;// is the class Abstract?
+ };
+
+ /// Returns the RTTI information for a classID
+ static RTTI* ClassIDToRTTI (int classID);
+
+ MemLabelId GetMemoryLabel () const;
+
+ static void DoneLoadingManagers ();
+
+ static IDToPointerMap& GetIDToPointerMapInternal () { return *ms_IDToPointer; }
+
+ virtual int GetClassIDVirtualInternal () const { AssertString("Bad"); return ClassID(Object); }
+ void PreCleanupObject ();
+
+ // Generic Event callback support.
+ typedef void EventCallback (void* userData, void* sender, int eventType);
+
+ void AddEvent (EventCallback* callback, void* userData);
+ void RemoveEvent (EventCallback* callback, void* userData);
+ bool HasEvent (EventCallback* callback, const void* userData) const;
+ void InvokeEvent (int eventType);
+
+private:
+
+ static UInt32* ms_IsDerivedFromBitMap;
+ static unsigned ms_MaxClassID;
+ static IDToPointerMap* ms_IDToPointer;
+ static UInt32* ms_ClassIDMask;
+ static UInt32* ms_ClassIsDerivedFrom;
+
+ SInt32 m_InstanceID;
+
+ enum Bits
+ {
+ kMemLabelBits = 13,
+ kIsRootOwnerBits = 1,
+ kTemporaryFlagsBits = 1,
+ kHideFlagsBits = 4,
+ kIsPersistentBits = 1,
+ kCachedClassIDBits = 12
+ };
+
+
+ UInt32 m_MemLabel : kMemLabelBits; // 13 bits
+ UInt32 m_IsRootOwner : kIsRootOwnerBits; // 14 bits
+ UInt32 m_TemporaryFlags: kTemporaryFlagsBits; // 15 bits
+ UInt32 m_HideFlags : kHideFlagsBits; // 19 bits
+ UInt32 m_IsPersistent : kIsPersistentBits; // 20 bits
+ UInt32 m_CachedClassID : kCachedClassIDBits; // 32 bits
+
+ EventEntry* m_EventIndex;
+
+
+ #if !UNITY_RELEASE
+ UInt32 m_DEBUGCLASSID:16;
+ UInt32 m_AwakeCalled:1;
+ UInt32 m_ResetCalled:1;
+ UInt32 m_AwakeThreadedCalled:1;
+ UInt32 m_AwakeDidLoadThreadedCalled:1;
+ #endif
+
+ #if ENABLE_MONO
+ UInt32 m_MonoReference;
+ #elif UNITY_FLASH
+ SInt32 m_MonoReference;
+ #elif UNITY_WINRT
+ SInt32 m_MonoReference;
+ UInt32 m_TemporaryUnusedAssetsFlags;
+ #endif
+ #if ENABLE_SCRIPTING
+ ScriptingObjectPtr m_ScriptingObjectPointer;
+ #endif
+
+ #if UNITY_EDITOR
+ UInt32 m_DirtyIndex;
+ LocalIdentifierInFileType m_FileIDHint;
+ #endif
+
+ public:
+
+ #if ENABLE_SCRIPTING
+ void SetupWeakHandle ();
+ bool RevertWeakHandle ();
+
+ void SetCachedScriptingObject (ScriptingObjectPtr cachedPointer);
+ ScriptingObjectPtr GetCachedScriptingObject () { return m_ScriptingObjectPointer; }
+ #endif
+
+private:
+
+ static void CalculateCachedClassID (Object* obj);
+ static void InsertObjectInMap (Object* obj);
+
+ void SetIsPersistent (bool p);
+
+ Object (const Object& o); // Disallow copy constructor
+ Object& operator = (const Object& o); // Disallow assignment
+
+ void SetInstanceID (int inID) { m_InstanceID = inID; }
+
+ protected:
+
+ static void RegisterClass (int inClassID, int inBaseClass, const std::string& inName, int size, FactoryFunction* inFunc, bool isAbstract);
+
+ static Object* PRODUCE (MemLabelId /*label*/, ObjectCreationMode /*mode*/) { AssertString ("Can't produce abstract class"); return NULL; }
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+ public:
+
+ /// Returns whether or not the class needs one typetree per object, not per classID
+ /// Having a per object typetree makes serialization considerably slower because safeBinaryTransfer is always used
+ /// Since no TypeTree can be generated before reading the object.
+ /// The File size will also increase because the typetree is not shared among the same classes.
+ /// It is used for example in PythonBehaviour
+ /// Also for one class you have to always returns true or always false.
+ virtual bool GetNeedsPerObjectTypeTree () const { return false; }
+
+ // Sets up RTTI, the object factory (Produce) and string <-> classID
+ // conversion. RegisterClass() has to be called once for every class
+ // derived from object, before any Objects are allocated
+ static void RegisterClass ();
+
+ // Required by serialization
+ virtual void VirtualRedirectTransfer (StreamedBinaryWrite<false>&){ AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); }
+ virtual void VirtualRedirectTransfer (StreamedBinaryRead<false>&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); }
+ virtual void VirtualRedirectTransfer (RemapPPtrTransfer&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); }
+ virtual void VirtualRedirectTransfer (ProxyTransfer&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); }
+#if SUPPORT_SERIALIZED_TYPETREES
+ virtual void VirtualRedirectTransfer (StreamedBinaryRead<true>&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); }
+ virtual void VirtualRedirectTransfer (SafeBinaryRead&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); }
+ virtual void VirtualRedirectTransfer (StreamedBinaryWrite<true>&){ AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); }
+#endif
+#if SUPPORT_TEXT_SERIALIZATION
+ virtual void VirtualRedirectTransfer (YAMLRead&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); }
+ virtual void VirtualRedirectTransfer (YAMLWrite&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); }
+ virtual void VirtualStrippedRedirectTransfer (YAMLWrite& t) { VirtualRedirectTransfer(t); }
+#endif
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+ virtual void DoLivenessCheck (RemapPPtrTransfer&) { AssertString ("DoLivenessCheck not implemented for type " + Object::ClassIDToString (GetClassID ())); }
+#endif
+ static const char* GetClassStringStatic (){ return "Object"; }
+ static const char* GetPPtrTypeString (){ return "PPtr<Object>"; }
+
+ friend class PersistentManager;
+ friend class SerializedFile;
+};
+
+struct LocalSerializedObjectIdentifier
+{
+ SInt32 localSerializedFileIndex;
+ #if LOCAL_IDENTIFIER_IN_FILE_SIZE == 64
+ UInt64 localIdentifierInFile;
+ #else
+ SInt32 localIdentifierInFile;
+ #endif
+
+ LocalSerializedObjectIdentifier()
+ {
+ localIdentifierInFile = 0;
+ localSerializedFileIndex = 0;
+ }
+};
+
+typedef void InstanceIDResolveCallback (SInt32 id, LocalSerializedObjectIdentifier& localIdentifier, void* context);
+void SetInstanceIDResolveCallback (InstanceIDResolveCallback* callback, const void* context = NULL);
+
+void EXPORT_COREMODULE InstanceIDToLocalSerializedObjectIdentifier (SInt32 id, LocalSerializedObjectIdentifier& localIdentifier);
+void EXPORT_COREMODULE LocalSerializedObjectIdentifierToInstanceID (const LocalSerializedObjectIdentifier& fileID, SInt32& memoryID);
+EXPORT_COREMODULE Object* ReadObjectFromPersistentManager (int instanceID);
+
+#if THREADED_LOADING
+EXPORT_COREMODULE Object* InstanceIDToObjectThreadSafe (int instanceID);
+#else
+# define InstanceIDToObjectThreadSafe PPtr<Object>
+#endif
+
+// This is used by the build game process. When writing for game release
+// we want to null all pptrs that can't be loaded anymore.
+// And when building default resources (culls all external references)
+enum { kWriteNULLWhenNotLoaded = 1 << 0, kConstrainedExternalReferences = 1 << 1 };
+void SetSerializeWritePPtrFlags (int flags, const std::set<string>& paths);
+
+void EXPORT_COREMODULE SetDisableImmediateDestruction (bool disable);
+bool EXPORT_COREMODULE GetDisableImmediateDestruction ();
+
+
+/// Returns if the object can possibly be loaded or is already in memory, without actually performing the loading.
+bool IsObjectAvailable (int instanceID);
+
+//Implementation
+#if UNITY_RELEASE
+# if UNITY_PS3
+ __attribute__((always_inline)) inline Object* Object::IDToPointer (int inInstanceID)
+#else
+ inline Object* Object::IDToPointer (int inInstanceID)
+# endif
+{
+ if( !ms_IDToPointer ) return NULL;
+ IDToPointerMap::const_iterator i = ms_IDToPointer->find (inInstanceID);
+ if (i != ms_IDToPointer->end ())
+ return i->second;
+ else
+ return NULL;
+}
+#endif
+
+template<class T>
+inline void PPtr<T>::AssignObject (const Object* o)
+{
+ if (o == NULL)
+ m_InstanceID = 0;
+ else
+ m_InstanceID = o->GetInstanceID ();
+ #if !UNITY_RELEASE
+ m_DEBUGPtr = (T*) (o);
+ #endif
+}
+
+template<class T> inline
+PPtr<T>::operator T* () const
+{
+ if (GetInstanceID () == 0)
+ return NULL;
+
+ Object* temp = Object::IDToPointer (GetInstanceID ());
+ if (temp == NULL)
+ temp = ReadObjectFromPersistentManager (GetInstanceID ());
+
+ #if !UNITY_RELEASE
+ m_DEBUGPtr = (T*) (temp);
+ #endif
+
+ #if DEBUGMODE || UNITY_EDITOR
+ T* casted = dynamic_pptr_cast<T*> (temp);
+ if (casted == temp)
+ return casted;
+ else
+ {
+ ErrorStringObject ("PPtr cast failed when dereferencing! Casting from " + temp->GetClassName () + " to " + T::GetClassStringStatic () + "!", temp);
+ return casted;
+ }
+ #else
+ return static_cast<T*> (temp);
+ #endif
+}
+
+template<class T> inline
+T* PPtr<T>::operator -> () const
+{
+ Object* temp = Object::IDToPointer (GetInstanceID ());
+ if (temp == NULL)
+ temp = ReadObjectFromPersistentManager (GetInstanceID ());
+
+ #if !UNITY_RELEASE
+ m_DEBUGPtr = (T*) (temp);
+ #endif
+
+ #if DEBUGMODE || !GAMERELEASE
+ T* casted = dynamic_pptr_cast<T*> (temp);
+ if (casted != NULL)
+ return casted;
+ else
+ {
+ if (temp != NULL)
+ {
+ ErrorStringObject ("PPtr cast failed when dereferencing! Casting from " + temp->GetClassName () + " to " + T::GetClassStringStatic () + "!", temp);
+ }
+ else
+ {
+ ErrorString ("Dereferencing NULL PPtr!");
+ }
+ return casted;
+ }
+ #else
+ return static_cast<T*> (temp);
+ #endif
+}
+
+template<class T> inline
+T& PPtr<T>::operator * () const
+{
+ Object* temp = Object::IDToPointer (GetInstanceID ());
+ if (temp == NULL)
+ temp = ReadObjectFromPersistentManager (GetInstanceID ());
+
+ #if !UNITY_RELEASE
+ m_DEBUGPtr = (T*) (temp);
+ #endif
+
+ #if DEBUGMODE || !GAMERELEASE
+ T* casted = dynamic_pptr_cast<T*> (temp);
+ if (casted != NULL)
+ return *casted;
+ else
+ {
+ if (temp != NULL)
+ {
+ ErrorStringObject ("PPtr cast failed when dereferencing! Casting from " + temp->GetClassName () + " to " + T::GetClassStringStatic () + "!", temp);
+ }
+ else
+ {
+ ErrorString ("Dereferencing NULL PPtr!");
+ }
+ ANALYSIS_ASSUME(casted);
+ return *casted;
+ }
+ #else
+ return *static_cast<T*> (temp);
+ #endif
+}
+
+template<class T> inline
+bool PPtr<T>::IsNull() const
+{
+ T* casted = *this;
+ return casted == NULL;
+}
+
+template<class T> inline
+bool PPtr<T>::IsValid() const
+{
+ T* casted = *this;
+ return casted != NULL;
+}
+
+template<class T>
+string PPtr<T>::s_TypeString;
+
+template<class T> inline
+const char* PPtr<T>::GetTypeString ()
+{
+ return T::GetPPtrTypeString ();
+}
+
+template<class T>
+template<class TransferFunction> inline
+void PPtr<T>::Transfer (TransferFunction& transfer)
+{
+ LocalSerializedObjectIdentifier localIdentifier;
+
+ if (transfer.NeedsInstanceIDRemapping ())
+ {
+ AssertIf (!transfer.IsWriting () && !transfer.IsReading ());
+
+ if (transfer.IsReading ())
+ {
+ transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask);
+ transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask);
+ LocalSerializedObjectIdentifierToInstanceID (localIdentifier, m_InstanceID);
+ }
+ else if (transfer.IsWriting ())
+ {
+ InstanceIDToLocalSerializedObjectIdentifier (m_InstanceID, localIdentifier);
+ transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask);
+ transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask);
+ }
+ else
+ {
+ transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask);
+ transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask);
+ }
+ }
+ else
+ {
+ transfer.Transfer (m_InstanceID, "m_FileID", kHideInEditorMask);
+ transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask);
+ }
+}
+
+template<class T> inline
+bool ImmediatePtr<T>::IsLoaded () const
+{
+ if (m_Ptr & 1)
+ {
+ return Object::IDToPointer(m_Ptr & (~1)) != NULL;
+ }
+ else
+ {
+ AssertIf(Object::IDToPointer(GetInstanceID()) == NULL);
+ return true;
+ }
+}
+template<class T>
+string ImmediatePtr<T>::s_TypeString;
+
+template<class T> inline
+const char* ImmediatePtr<T>::GetTypeString ()
+{
+ if(s_TypeString.empty())
+ {
+ SET_ALLOC_OWNER(NULL);
+ s_TypeString = string ("PPtr<") + T::GetClassStringStatic () + ">";
+ }
+ return s_TypeString.c_str ();
+}
+
+template<class T>
+template<class TransferFunction> inline
+void ImmediatePtr<T>::Transfer (TransferFunction& transfer)
+{
+ LocalSerializedObjectIdentifier localIdentifier;
+
+ if (transfer.NeedsInstanceIDRemapping ())
+ {
+ AssertIf (!transfer.IsWriting () && !transfer.IsReading ());
+
+ if (transfer.IsReading ())
+ {
+ transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask);
+ transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask);
+ SInt32 instanceID;
+ LocalSerializedObjectIdentifierToInstanceID (localIdentifier, instanceID);
+ AssignInstanceID (instanceID);
+ }
+ else if (transfer.IsWriting ())
+ {
+ InstanceIDToLocalSerializedObjectIdentifier (GetInstanceID (), localIdentifier);
+ transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask);
+ transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask);
+ }
+ else
+ {
+ transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask);
+ transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask);
+ }
+ }
+ else
+ {
+ if (transfer.IsReading ())
+ {
+ transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask);
+ transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask);
+ SetInstanceID (localIdentifier.localSerializedFileIndex);
+ }
+ else if (transfer.IsWriting ())
+ {
+ localIdentifier.localSerializedFileIndex = GetInstanceID ();
+ localIdentifier.localIdentifierInFile = 0;
+ transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask);
+ transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask);
+ AssertIf (localIdentifier.localSerializedFileIndex != GetInstanceID ());
+ }
+ else
+ {
+ transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask);
+ transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask);
+ }
+ }
+}
+#if UNITY_PS3
+#define USE_NEW_IS_DERIVED_FROM 1
+#else
+#define USE_NEW_IS_DERIVED_FROM 0
+#endif
+
+#if !USE_NEW_IS_DERIVED_FROM
+#if UNITY_RELEASE
+inline bool Object::IsDerivedFromClassID (int classID, int compareClass)
+{
+ AssertIf (classID >= ms_MaxClassID || classID < 0);
+ AssertIf (compareClass >= ms_MaxClassID || compareClass < 0);
+ int index = classID * ms_MaxClassID + compareClass;
+ int block = index >> 5;
+ int bit = index - (block << 5);
+ return (ms_IsDerivedFromBitMap[block]) & (1 << bit);
+}
+#endif
+#else
+#define CLASS_ID_MASK_BITS 4
+#define CLASS_ID_MASK_IDS 0x0fffffff
+inline bool Object::IsDerivedFromClassID(int klass, int base)
+{
+ int klassId = ms_ClassIsDerivedFrom[klass];
+ int baseId = ms_ClassIsDerivedFrom[base];
+ int mask = ms_ClassIDMask[ 0xf&(baseId >> (32-CLASS_ID_MASK_BITS))];
+ return (klassId&mask) == (baseId&CLASS_ID_MASK_IDS);
+}
+#endif
+
+void LockObjectCreation ();
+void UnlockObjectCreation ();
+
+// Destroys a Object removing from memory and disk when needed.
+// Might load the object as part of destruction which is probably unwanted.
+// @TODO: Refactor code to not do that
+void EXPORT_COREMODULE DestroySingleObject (Object* o);
+void UnloadObject (Object* o);
+
+/// Destroys the object if it is loaded. (Will not load the object from disk if it is not loaded at the moment)
+/// Will remove it from any remapping tables
+/// Will not removed it from the actual serialized file, with the assumption that the file will be unloaded from disk later.
+void DestroyWithoutLoadingButDontDestroyFromFile (int instanceID);
+
+#if DEBUGMODE
+typedef std::set<int, std::less<int>, STL_ALLOCATOR(kMemPermanent,int) > VerifyRegisteredClass;
+void EXPORT_COREMODULE AddVerifyClassRegistration (int classID);
+typedef std::set<int, std::less<int>, STL_ALLOCATOR(kMemBaseObject, int) > RegisteredClassSet;
+const RegisteredClassSet& GetVerifyClassRegistration ();
+#endif
+
+/// Helper to create object correctly from code. Will call Reset and AwakeFromLoad
+template <typename T> T* CreateObjectFromCode( AwakeFromLoadMode awakeMode=kInstantiateOrCreateFromCodeAwakeFromLoad, MemLabelId label = kMemBaseObject )
+{
+ Assert(Object::ClassIDToRTTI(T::GetClassIDStatic()) != NULL);
+ T* obj = NEW_OBJECT_USING_MEMLABEL(T, label);
+ SET_ALLOC_OWNER(obj);
+ obj->Reset();
+ obj->AwakeFromLoad(awakeMode);
+ return obj;
+}
+
+template<typename T>
+inline T* ResetAndAwake (T* object)
+{
+ object->Reset();
+ object->AwakeFromLoad (kDefaultAwakeFromLoad);
+ return object;
+}
+
+void delete_object_internal (Object* p);
+void delete_object_internal_step1 (Object* object);
+void delete_object_internal_step2 (Object* object);
+
+#endif
diff --git a/Runtime/BaseClasses/BitField.h b/Runtime/BaseClasses/BitField.h
new file mode 100644
index 0000000..1ead092
--- /dev/null
+++ b/Runtime/BaseClasses/BitField.h
@@ -0,0 +1,26 @@
+#ifndef BITFIELD_H
+#define BITFIELD_H
+#include "Runtime/Serialize/SerializeUtility.h"
+
+struct BitField
+{
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (BitField)
+ UInt32 m_Bits;
+};
+
+enum { kPreUnity2UnusedLayerMask = 1 << 5 };
+
+template<class TransferFunc>
+void BitField::Transfer (TransferFunc& transfer)
+{
+ transfer.SetVersion (2);
+ transfer.Transfer (m_Bits, "m_Bits", kHideInEditorMask | kGenerateBitwiseDifferences);
+
+ if (transfer.IsOldVersion(1))
+ {
+ if (m_Bits & kPreUnity2UnusedLayerMask)
+ m_Bits |= 0xFFFF << 16;
+ }
+}
+
+#endif
diff --git a/Runtime/BaseClasses/ClassIDs.h b/Runtime/BaseClasses/ClassIDs.h
new file mode 100644
index 0000000..aa991e2
--- /dev/null
+++ b/Runtime/BaseClasses/ClassIDs.h
@@ -0,0 +1,240 @@
+#ifndef CLASSIDS_H_
+#define CLASSIDS_H_
+
+#define ClassID(x) CLASS_##x
+#define DefineClassID(x,classID) ClassID(x) = classID,
+
+// Runtime classIDs are kept intentionally small.
+enum ClassIDType
+{
+DefineClassID (Undefined, -1)
+DefineClassID (Object, 0)
+DefineClassID (GameObject, 1)
+DefineClassID (Component, 2)
+DefineClassID (LevelGameManager, 3)
+DefineClassID (Transform, 4)
+DefineClassID (TimeManager, 5)
+DefineClassID (GlobalGameManager, 6)
+DefineClassID (Behaviour, 8)
+DefineClassID (GameManager, 9)
+DefineClassID (AudioManager, 11)
+DefineClassID (ParticleAnimator, 12)
+DefineClassID (InputManager, 13)
+DefineClassID (EllipsoidParticleEmitter, 15)
+DefineClassID (Pipeline, 17)
+DefineClassID (EditorExtension, 18)
+DefineClassID (Physics2DSettings, 19)
+DefineClassID (Camera, 20)
+DefineClassID (Material, 21)
+DefineClassID (MeshRenderer, 23)
+DefineClassID (Renderer, 25)
+DefineClassID (ParticleRenderer, 26)
+DefineClassID (Texture, 27)
+DefineClassID (Texture2D, 28)
+DefineClassID (SceneSettings, 29)
+DefineClassID (GraphicsSettings, 30)
+DefineClassID (MeshFilter, 33)
+DefineClassID (OcclusionPortal, 41)
+DefineClassID (Mesh, 43)
+DefineClassID (Skybox, 45)
+DefineClassID (QualitySettings, 47)
+DefineClassID (Shader, 48)
+DefineClassID (TextAsset, 49)
+DefineClassID (Rigidbody2D, 50)
+DefineClassID (Physics2DManager, 51)
+DefineClassID (Collider2D, 53)
+DefineClassID (Rigidbody, 54)
+DefineClassID (PhysicsManager, 55)
+DefineClassID (Collider, 56)
+DefineClassID (Joint, 57)
+DefineClassID (CircleCollider2D, 58)
+DefineClassID (HingeJoint, 59)
+DefineClassID (PolygonCollider2D, 60)
+DefineClassID (BoxCollider2D, 61)
+DefineClassID (PhysicsMaterial2D, 62)
+DefineClassID (MeshCollider, 64)
+DefineClassID (BoxCollider, 65)
+DefineClassID (SpriteCollider2D, 66)
+DefineClassID (EdgeCollider2D, 68)
+DefineClassID (PolygonColliderBase2D, 69)
+DefineClassID (ComputeShader, 72)
+DefineClassID (AnimationClip, 74)
+DefineClassID (ConstantForce, 75)
+DefineClassID (WorldParticleCollider, 76)
+DefineClassID (TagManager, 78)
+DefineClassID (AudioListener, 81)
+DefineClassID (AudioSource, 82)
+DefineClassID (AudioClip, 83)
+DefineClassID (RenderTexture, 84)
+DefineClassID (MeshParticleEmitter, 87)
+DefineClassID (ParticleEmitter, 88)
+DefineClassID (Cubemap, 89)
+DefineClassID (Avatar, 90)
+DefineClassID (AnimatorController, 91)
+DefineClassID (GUILayer, 92)
+DefineClassID (RuntimeAnimatorController, 93)
+DefineClassID (ScriptMapper, 94)
+DefineClassID (Animator, 95)
+DefineClassID (TrailRenderer, 96)
+DefineClassID (DelayedCallManager, 98)
+DefineClassID (TextMesh, 102)
+DefineClassID (RenderSettings, 104)
+DefineClassID (Light, 108)
+DefineClassID (CGProgram, 109)
+DefineClassID (BaseAnimationTrack, 110)
+DefineClassID (Animation, 111)
+DefineClassID (MonoBehaviour, 114)
+DefineClassID (MonoScript, 115)
+DefineClassID (MonoManager, 116)
+DefineClassID (Texture3D, 117)
+DefineClassID (NewAnimationTrack, 118)
+DefineClassID (Projector, 119)
+DefineClassID (LineRenderer, 120)
+DefineClassID (Flare, 121)
+DefineClassID (Halo, 122)
+DefineClassID (LensFlare, 123)
+DefineClassID (FlareLayer, 124)
+DefineClassID (HaloLayer, 125)
+DefineClassID (NavMeshLayers, 126)
+DefineClassID (HaloManager, 127)
+DefineClassID (Font, 128)
+DefineClassID (PlayerSettings, 129)
+DefineClassID (NamedObject, 130)
+DefineClassID (GUITexture, 131)
+DefineClassID (GUIText, 132)
+DefineClassID (GUIElement, 133)
+DefineClassID (PhysicMaterial, 134)
+DefineClassID (SphereCollider, 135)
+DefineClassID (CapsuleCollider, 136)
+DefineClassID (SkinnedMeshRenderer, 137)
+DefineClassID (FixedJoint, 138)
+DefineClassID (RaycastCollider, 140)
+DefineClassID (BuildSettings, 141)
+DefineClassID (AssetBundle, 142)
+DefineClassID (CharacterController, 143)
+DefineClassID (CharacterJoint, 144)
+DefineClassID (SpringJoint, 145)
+DefineClassID (WheelCollider, 146)
+DefineClassID (ResourceManager, 147)
+DefineClassID (NetworkView, 148)
+DefineClassID (NetworkManager, 149)
+DefineClassID (PreloadData, 150)
+DefineClassID (MovieTexture, 152)
+DefineClassID (ConfigurableJoint, 153)
+DefineClassID (TerrainCollider, 154)
+DefineClassID (MasterServerInterface, 155)
+DefineClassID (TerrainData, 156)
+DefineClassID (LightmapSettings, 157)
+DefineClassID (WebCamTexture, 158)
+DefineClassID (EditorSettings, 159)
+DefineClassID (InteractiveCloth, 160)
+DefineClassID (ClothRenderer, 161)
+DefineClassID (EditorUserSettings, 162)
+DefineClassID (SkinnedCloth, 163)
+DefineClassID (AudioReverbFilter, 164)
+DefineClassID (AudioHighPassFilter, 165)
+DefineClassID (AudioChorusFilter, 166)
+DefineClassID (AudioReverbZone, 167)
+DefineClassID (AudioEchoFilter, 168)
+DefineClassID (AudioLowPassFilter, 169)
+DefineClassID (AudioDistortionFilter, 170)
+DefineClassID (AudioBehaviour, 180)
+DefineClassID (AudioFilter, 181)
+DefineClassID (WindZone, 182)
+DefineClassID (Cloth, 183)
+DefineClassID (SubstanceArchive, 184)
+DefineClassID (ProceduralMaterial, 185)
+DefineClassID (ProceduralTexture, 186)
+DefineClassID (OffMeshLink, 191)
+DefineClassID (OcclusionArea, 192)
+DefineClassID (Tree, 193)
+DefineClassID (NavMesh, 194)
+DefineClassID (NavMeshAgent, 195)
+DefineClassID (NavMeshSettings, 196)
+DefineClassID (LightProbes, 197)
+DefineClassID (ParticleSystem, 198)
+DefineClassID (ParticleSystemRenderer, 199)
+DefineClassID (LODGroup, 205)
+DefineClassID (BlendTree, 206)
+DefineClassID (Motion, 207)
+DefineClassID (NavMeshObstacle, 208)
+DefineClassID (TerrainInstance, 210)
+
+DefineClassID (SpriteRenderer, 212)
+DefineClassID (Sprite, 213)
+DefineClassID (CachedSpriteAtlas, 214)
+
+DefineClassID (LightProbeGroup, 220)
+DefineClassID (AnimatorOverrideController, 221)
+
+DefineClassID (Joint2D, 230)
+DefineClassID (SpringJoint2D, 231)
+DefineClassID (DistanceJoint2D, 232)
+DefineClassID (HingeJoint2D, 233)
+DefineClassID (SliderJoint2D, 234)
+// Reserved 235-238 for new joints.
+//DefineClassID (WheelJoint2D, 235)
+//DefineClassID (FrictionJoint2D, 236)
+//DefineClassID (PulleyJoint2D, 237)
+//DefineClassID (GearJoint2D, 238)
+
+kLargestRuntimeClassID,
+
+DefineClassID (SmallestEditorClassID, 1000)
+DefineClassID (Prefab, 1001)
+DefineClassID (EditorExtensionImpl, 1002)
+DefineClassID (AssetImporter, 1003)
+DefineClassID (AssetDatabase, 1004)
+DefineClassID (Mesh3DSImporter, 1005)
+DefineClassID (TextureImporter, 1006)
+DefineClassID (ShaderImporter, 1007)
+DefineClassID (ComputeShaderImporter, 1008)
+DefineClassID (AvatarMask, 1011)
+DefineClassID (AudioImporter, 1020)
+DefineClassID (HierarchyState, 1026)
+DefineClassID (GUIDSerializer, 1027)
+DefineClassID (AssetMetaData, 1028)
+DefineClassID (DefaultAsset, 1029)
+DefineClassID (DefaultImporter, 1030)
+DefineClassID (TextScriptImporter, 1031)
+DefineClassID (SceneAsset, 1032)
+DefineClassID (NativeFormatImporter, 1034)
+DefineClassID (MonoImporter, 1035)
+DefineClassID (AssetServerCache, 1037)
+DefineClassID (LibraryAssetImporter, 1038)
+DefineClassID (ModelImporter, 1040)
+DefineClassID (FBXImporter, 1041)
+DefineClassID (TrueTypeFontImporter, 1042)
+DefineClassID (MovieImporter, 1044)
+DefineClassID (EditorBuildSettings, 1045)
+DefineClassID (DDSImporter, 1046)
+DefineClassID (InspectorExpandedState, 1048)
+DefineClassID (AnnotationManager, 1049)
+DefineClassID (MonoAssemblyImporter, 1050)
+DefineClassID (EditorUserBuildSettings, 1051)
+DefineClassID (PVRImporter, 1052)
+DefineClassID (Transition, 1101)
+DefineClassID (State, 1102)
+DefineClassID (HumanTemplate, 1105)
+DefineClassID (StateMachine, 1107)
+DefineClassID (PreviewAssetType, 1108)
+DefineClassID (SubstanceImporter, 1112)
+
+kLargestEditorClassID,
+
+kClassIdOutOfHierarchy = 100000,
+
+DefineClassID (int, kClassIdOutOfHierarchy)
+DefineClassID (bool, kClassIdOutOfHierarchy + 1)
+DefineClassID (float, kClassIdOutOfHierarchy + 2)
+DefineClassID (MonoObject, kClassIdOutOfHierarchy + 3)
+DefineClassID (Collision, kClassIdOutOfHierarchy + 4)
+DefineClassID (Vector3f, kClassIdOutOfHierarchy + 5)
+DefineClassID (RootMotionData, kClassIdOutOfHierarchy + 6)
+DefineClassID (Collision2D, kClassIdOutOfHierarchy + 7)
+};
+
+//make sure people dont accidentally define classids in other files:
+#undef DefineClassID
+
+#endif
diff --git a/Runtime/BaseClasses/ClassRegistration.cpp b/Runtime/BaseClasses/ClassRegistration.cpp
new file mode 100644
index 0000000..80b44ec
--- /dev/null
+++ b/Runtime/BaseClasses/ClassRegistration.cpp
@@ -0,0 +1,267 @@
+#include "UnityPrefix.h"
+#include "ClassRegistration.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Modules/ModuleRegistration.h"
+
+// IPhone platform with stripping overwrites the "RegisterAllClasses" function.
+// See GenerateRegisterClassesForStripping in MonoInternalCallGenerator.cs.
+// This is why the iPhone builds overwrite the function name here.
+#if UNITY_IPHONE
+#define RegisterAllClasses RegisterAllClassesIPhone
+#endif
+
+using namespace std;
+#if DEBUGMODE
+static void VerifyThatAllClassesHaveBeenRegistered(const RegisteredClassSet& explicitlyRegistered);
+#endif
+
+#define RESERVE_CLASSID(klass,classID) ValidateRegisteredClassID (context,classID, #klass);
+#define RESERVE_DEPRECATED_CLASSID(klass,classID) ValidateRegisteredClassID (context,classID, #klass);
+
+void ValidateRegisteredClassID (ClassRegistrationContext& context, int classID, const char* className)
+{
+#if DEBUGMODE
+ RegisteredClassSet& explicitlyRegistered = *reinterpret_cast<RegisteredClassSet*> (context.explicitlyRegistered);
+ bool didNotExist = explicitlyRegistered.insert(classID).second;
+
+ if (!didNotExist) FatalErrorString(Format("ClassID %d conflicts with that of another class. Please resolve the conflict. (%s)", classID, className));
+#endif
+}
+
+
+#if DEBUGMODE
+void RegisterDeprecatedClassIDs (ClassRegistrationContext& context)
+{
+ /// DO NOT REMOVE CLASS IDS FROM THIS LIST TO MAKE ROOM FOR A NEW ONE. IT WILL RESULT IN CLASSID CONFLICTS ON OLD PROJECTS
+
+ RESERVE_DEPRECATED_CLASSID (BehaviourManager, 7) // Removed in Unity 3.2
+ RESERVE_DEPRECATED_CLASSID (Filter, 16) // Removed ages ago
+ RESERVE_DEPRECATED_CLASSID (PipelineManager, 31) // Removed in Unity 3.2
+ RESERVE_DEPRECATED_CLASSID (BaseBehaviourManager, 34) // Removed in Unity 3.2
+ RESERVE_DEPRECATED_CLASSID (LateBehaviourManager, 35) // Removed in Unity 3.2
+ RESERVE_DEPRECATED_CLASSID (FixedBehaviourManager, 46) // Removed in Unity 3.2
+ RESERVE_DEPRECATED_CLASSID (UpdateManager, 63) // Removed in Unity 3.2
+ RESERVE_DEPRECATED_CLASSID (RenderLayer, 67) // Intermediate abstract class refactored away post-Unity 3.2
+ RESERVE_DEPRECATED_CLASSID (AnimationTrack2, 112)
+ RESERVE_DEPRECATED_CLASSID (ResourceManagerOLD, 113)
+ RESERVE_DEPRECATED_CLASSID (GooballCollider, 77)
+ RESERVE_DEPRECATED_CLASSID (VertexSnapper, 79)
+ RESERVE_DEPRECATED_CLASSID (LightManager, 85) // -> changed id DO NOT REUSE
+ //RESERVE_DEPRECATED_CLASSID (PreloadManager, 90) // Now used by Avatar
+ //RESERVE_DEPRECATED_CLASSID (ScaleFilter, 91) // deprecated pre 1.0, now used by AnimatorController
+ //RESERVE_DEPRECATED_CLASSID (TextureRect, 93) // Now used by RuntimeAnimatorController
+ //RESERVE_DEPRECATED_CLASSID (MotorJoint, 95) // Now used by Animator
+ RESERVE_DEPRECATED_CLASSID (Decal, 97) // Pre 1.0
+ RESERVE_DEPRECATED_CLASSID (EulerRotationMotor, 139)
+ RESERVE_DEPRECATED_CLASSID (ParticleCloudColor, 103) // Pre 1.0
+ RESERVE_DEPRECATED_CLASSID (TextScript, 105)
+ RESERVE_DEPRECATED_CLASSID (VertexProgram, 106)
+ RESERVE_DEPRECATED_CLASSID (FragmentProgram, 107)
+ RESERVE_DEPRECATED_CLASSID (GooStickyness, 151)
+ RESERVE_DEPRECATED_CLASSID (ClothAnimator, 99) // PRE 1.0
+ RESERVE_DEPRECATED_CLASSID (PatchRenderer, 100) // PRE 1.0
+ RESERVE_DEPRECATED_CLASSID (Stretcher, 101) // PRE 1.0
+ RESERVE_DEPRECATED_CLASSID (AudioManager, 80) // -> changed id DO NOT REUSE
+ //RESERVE_DEPRECATED_CLASSID (AxisRenderer, 1008) // REMOVED in 2.0, now used by ComputeShaderImporter
+ RESERVE_DEPRECATED_CLASSID (BBoxRenderer, 1009) // REMOVED in 2.0
+ RESERVE_DEPRECATED_CLASSID (CopyTransform, 1010) // REMOVED in 2.1
+ //RESERVE_DEPRECATED_CLASSID (DotRenderer, 1011) // REMOVED in 2.0, now used by AvatarMask
+ RESERVE_DEPRECATED_CLASSID (SphereRenderer, 1012) // Not here anymore
+ RESERVE_DEPRECATED_CLASSID (WireRenderer, 1024) // Removed in 2.0
+ RESERVE_DEPRECATED_CLASSID (AnimationManager, 71) // Removed in 4.3
+}
+
+void RegisterReservedClassIDs (ClassRegistrationContext& context)
+{
+ RESERVE_CLASSID (PreviewAssetType, 1108);
+ RESERVE_CLASSID (GUITransform, 187)
+ RESERVE_CLASSID (GUIButton, 188)
+ RESERVE_CLASSID (GUIGroup, 189)
+ RESERVE_CLASSID (GUIComponent, 190)
+ RESERVE_CLASSID (GUICanvas, 219)
+ RESERVE_CLASSID (GUIToggle, 200)
+ RESERVE_CLASSID (GUIImage, 201)
+ RESERVE_CLASSID (GUILabel, 202)
+ RESERVE_CLASSID (GUISlider, 211)
+ RESERVE_CLASSID (GUITextField, 204)
+ RESERVE_CLASSID (GUIKeyboardControl, 210)
+ RESERVE_CLASSID (InWorldGUI, 209)
+ RESERVE_CLASSID (GUICamera, 217)
+ RESERVE_CLASSID (TextureAtlas, 203)
+}
+
+static void VerifyThatAllClassesHaveBeenRegistered(const RegisteredClassSet& explicitlyRegistered)
+{
+ const RegisteredClassSet& classes = GetVerifyClassRegistration ();
+
+ for (RegisteredClassSet::const_iterator i=classes.begin();i != classes.end();++i)
+ {
+ if (explicitlyRegistered.count (*i) == 0)
+ {
+ FatalErrorString(Format("ClassID %d has not been registered but is included in the build. You must add the class to RegisterAllClasses.", *i));
+ }
+ }
+
+ Assert(classes == explicitlyRegistered);
+}
+#endif
+
+void RegisterAllClasses()
+{
+ ClassRegistrationContext context;
+#if DEBUGMODE
+ RegisteredClassSet explicitlyRegistered;
+ context.explicitlyRegistered = &explicitlyRegistered;
+#endif
+
+ REGISTER_CLASS (GameObject)
+ REGISTER_CLASS (Component)
+ REGISTER_CLASS (LevelGameManager)
+ REGISTER_CLASS (Transform)
+ REGISTER_CLASS (TimeManager)
+ REGISTER_CLASS (GlobalGameManager)
+ REGISTER_CLASS (Behaviour)
+ REGISTER_CLASS (GameManager)
+ REGISTER_CLASS (ParticleAnimator)
+ REGISTER_CLASS (InputManager)
+ REGISTER_CLASS (EllipsoidParticleEmitter)
+ REGISTER_CLASS (Pipeline)
+ REGISTER_CLASS (EditorExtension)
+ REGISTER_CLASS (Camera)
+ REGISTER_CLASS (Material)
+ REGISTER_CLASS (Mesh)
+ REGISTER_CLASS (MeshRenderer)
+ REGISTER_CLASS (MeshFilter)
+ REGISTER_CLASS (Renderer)
+ REGISTER_CLASS (ParticleRenderer)
+ REGISTER_CLASS (Texture)
+ REGISTER_CLASS (Texture2D)
+ REGISTER_CLASS (SceneSettings)
+ REGISTER_CLASS (OcclusionPortal)
+ REGISTER_CLASS (Skybox)
+ REGISTER_CLASS (QualitySettings)
+ REGISTER_CLASS (Shader)
+ REGISTER_CLASS (TextAsset)
+ REGISTER_CLASS (ComputeShader)
+ REGISTER_CLASS (WorldParticleCollider)
+ REGISTER_CLASS (TagManager)
+ REGISTER_CLASS (RenderTexture)
+ REGISTER_CLASS (MeshParticleEmitter)
+ REGISTER_CLASS (ParticleEmitter)
+ REGISTER_CLASS (Cubemap)
+ REGISTER_CLASS (GUILayer)
+ REGISTER_CLASS (ScriptMapper)
+ REGISTER_CLASS (TrailRenderer)
+ REGISTER_CLASS (DelayedCallManager)
+ REGISTER_CLASS (TextMesh)
+ REGISTER_CLASS (Light)
+ REGISTER_CLASS (CGProgram)
+ REGISTER_CLASS (LightProbes)
+ REGISTER_CLASS (ResourceManager)
+ REGISTER_CLASS (Texture3D)
+ REGISTER_CLASS (Projector)
+ REGISTER_CLASS (LineRenderer)
+ REGISTER_CLASS (Flare)
+ REGISTER_CLASS (Halo)
+ REGISTER_CLASS (LensFlare)
+ REGISTER_CLASS (FlareLayer)
+ REGISTER_CLASS (HaloLayer)
+ REGISTER_CLASS (HaloManager)
+ REGISTER_CLASS (PreloadData)
+ REGISTER_CLASS (LightmapSettings)
+ REGISTER_CLASS (RenderSettings)
+ REGISTER_CLASS (NamedObject)
+ REGISTER_CLASS (GUIText)
+ REGISTER_CLASS (GUITexture)
+ REGISTER_CLASS (Font)
+ REGISTER_CLASS (GUIElement)
+ REGISTER_CLASS (SkinnedMeshRenderer)
+ REGISTER_CLASS (BuildSettings)
+ REGISTER_CLASS (AssetBundle)
+ REGISTER_CLASS (OcclusionArea)
+ REGISTER_CLASS (ParticleSystem)
+ REGISTER_CLASS (ParticleSystemRenderer)
+ REGISTER_CLASS (GraphicsSettings)
+ REGISTER_CLASS (PlayerSettings)
+ REGISTER_CLASS (SubstanceArchive)
+ REGISTER_CLASS (ProceduralMaterial)
+ REGISTER_CLASS (ProceduralTexture)
+ REGISTER_CLASS (LODGroup)
+ REGISTER_CLASS (LightProbeGroup)
+ REGISTER_CLASS (WindZone)
+
+#if ENABLE_SCRIPTING
+ REGISTER_CLASS (MonoScript)
+ REGISTER_CLASS (MonoManager)
+ REGISTER_CLASS (MonoBehaviour)
+#endif
+
+#if ENABLE_NETWORK
+ REGISTER_CLASS (NetworkView)
+ REGISTER_CLASS (NetworkManager)
+ REGISTER_CLASS (MasterServerInterface)
+#endif
+
+#if ENABLE_SPRITES
+ REGISTER_CLASS (SpriteRenderer)
+ REGISTER_CLASS (Sprite)
+ #if UNITY_EDITOR
+ REGISTER_CLASS (CachedSpriteAtlas)
+ #endif
+#endif
+
+ RegisterAllAvailableModuleClasses (context);
+
+ // Editor Only classes following:
+#if UNITY_EDITOR
+ REGISTER_CLASS (EditorSettings)
+ REGISTER_CLASS (EditorUserSettings)
+ REGISTER_CLASS (Prefab)
+ REGISTER_CLASS (EditorExtensionImpl)
+ REGISTER_CLASS (AssetImporter)
+ REGISTER_CLASS (AssetDatabase)
+ REGISTER_CLASS (Mesh3DSImporter)
+ REGISTER_CLASS (TextureImporter)
+ REGISTER_CLASS (ShaderImporter)
+ REGISTER_CLASS (ComputeShaderImporter)
+ REGISTER_CLASS (AudioImporter)
+ REGISTER_CLASS (GUIDSerializer)
+ REGISTER_CLASS (AssetMetaData)
+ REGISTER_CLASS (DefaultAsset)
+ REGISTER_CLASS (DefaultImporter)
+ REGISTER_CLASS (TextScriptImporter)
+ REGISTER_CLASS (SceneAsset)
+ REGISTER_CLASS (NativeFormatImporter)
+ REGISTER_CLASS (MonoImporter)
+ REGISTER_CLASS (MonoAssemblyImporter)
+ REGISTER_CLASS (AssetServerCache)
+ REGISTER_CLASS (LibraryAssetImporter)
+ REGISTER_CLASS (ModelImporter)
+ REGISTER_CLASS (FBXImporter)
+ REGISTER_CLASS (TrueTypeFontImporter)
+ REGISTER_CLASS (EditorBuildSettings)
+ REGISTER_CLASS (DDSImporter)
+ REGISTER_CLASS (InspectorExpandedState)
+ REGISTER_CLASS (AnnotationManager)
+ REGISTER_CLASS (EditorUserBuildSettings)
+ REGISTER_CLASS (PVRImporter)
+ REGISTER_CLASS (HierarchyState)
+ REGISTER_CLASS (Transition)
+ REGISTER_CLASS (State)
+ REGISTER_CLASS (HumanTemplate)
+ REGISTER_CLASS (StateMachine)
+ REGISTER_CLASS (AvatarMask)
+ REGISTER_CLASS (BlendTree)
+ REGISTER_CLASS (SubstanceImporter)
+
+#if !UNITY_LINUX
+ REGISTER_CLASS (MovieImporter)
+#endif
+#endif // UNITY_EDITOR
+
+#if DEBUGMODE
+ VerifyThatAllClassesHaveBeenRegistered(explicitlyRegistered);
+ RegisterDeprecatedClassIDs(context);
+ RegisterReservedClassIDs(context);
+#endif
+}
diff --git a/Runtime/BaseClasses/ClassRegistration.h b/Runtime/BaseClasses/ClassRegistration.h
new file mode 100644
index 0000000..cf016db
--- /dev/null
+++ b/Runtime/BaseClasses/ClassRegistration.h
@@ -0,0 +1,22 @@
+#pragma once
+#include "ClassIDs.h"
+
+struct ClassRegistrationContext
+{
+ void* explicitlyRegistered;
+};
+
+#if DEBUGMODE
+#define REGISTER_CLASS(x) \
+{ \
+ extern void RegisterClass_##x(); \
+ RegisterClass_##x(); \
+ ValidateRegisteredClassID(context, ClassID(x), #x); \
+}
+#else
+#define REGISTER_CLASS(x) \
+{ extern void RegisterClass_##x(); RegisterClass_##x(); }
+#endif
+
+
+EXPORT_COREMODULE void ValidateRegisteredClassID (ClassRegistrationContext& context, int classID, const char* className);
diff --git a/Runtime/BaseClasses/CleanupManager.cpp b/Runtime/BaseClasses/CleanupManager.cpp
new file mode 100644
index 0000000..7adb1e7
--- /dev/null
+++ b/Runtime/BaseClasses/CleanupManager.cpp
@@ -0,0 +1,65 @@
+#include "UnityPrefix.h"
+#if UNITY_EDITOR
+#include "CleanupManager.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Graphics/Transform.h"
+#include <algorithm>
+
+using namespace std;
+
+void CleanupManager::MarkForDeletion( PPtr<Unity::Component> comp, std::string const& reason )
+{
+ // Ignore component that is already marked for deletion
+ list<struct MarkedComponent>::iterator a = std::find(m_markedComponents.begin(), m_markedComponents.end(), static_cast<Unity::Component*>(comp));
+ if (a != m_markedComponents.end())
+ return;
+
+ struct MarkedComponent marker;
+ m_markedComponents.push_back (marker);
+ m_markedComponents.back ().component = comp;
+ m_markedComponents.back ().reason = reason;
+}
+
+void CleanupManager::Flush()
+{
+ while (m_markedComponents.size() > 0)
+ {
+ struct MarkedComponent& marked_component = m_markedComponents.front ();
+ PPtr<Unity::Component> comp = marked_component.component;
+
+ Unity::Component* compPtr = comp;
+ if (compPtr)
+ {
+ LogString(Format("%s component deleted: %s", comp->GetClassName ().c_str (), marked_component.reason.c_str()));
+ if (marked_component.component->GetGameObjectPtr () != NULL)
+ {
+ DestroyObjectHighLevel (comp);
+ }
+ else
+ {
+ // if the component is a transform remove the references
+ if (comp->GetClassID () == ClassID(Transform))
+ {
+ DestroyTransformComponentAndChildHierarchy(static_cast<Transform&>(*comp));
+ }
+
+ DestroySingleObject (comp);
+ }
+ }
+
+ m_markedComponents.pop_front ();
+ }
+}
+
+static CleanupManager* singleton = NULL;
+CleanupManager& GetCleanupManager ()
+{
+ if (singleton == NULL)
+ {
+ singleton = new CleanupManager();
+ }
+
+ return *singleton;
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/BaseClasses/CleanupManager.h b/Runtime/BaseClasses/CleanupManager.h
new file mode 100644
index 0000000..23c76c8
--- /dev/null
+++ b/Runtime/BaseClasses/CleanupManager.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#if UNITY_EDITOR
+
+#include "Runtime/BaseClasses/GameObject.h"
+
+#include <string>
+#include <list>
+
+class CleanupManager
+{
+private:
+ struct MarkedComponent {
+ PPtr<Unity::Component> component;
+ std::string reason;
+
+ bool operator==(PPtr<Unity::Component> const& comp)
+ {
+ return this->component == comp;
+ }
+ };
+
+public:
+ CleanupManager() {}
+
+ void MarkForDeletion(PPtr<Unity::Component> comp, std::string const& reason);
+ void Flush();
+
+ static void DidDestroyObjectNotification (Object* comp, void* userData);
+
+private:
+ std::list<struct MarkedComponent> m_markedComponents;
+};
+
+CleanupManager& GetCleanupManager ();
+
+#endif \ No newline at end of file
diff --git a/Runtime/BaseClasses/Cursor.cpp b/Runtime/BaseClasses/Cursor.cpp
new file mode 100644
index 0000000..3fb45ca
--- /dev/null
+++ b/Runtime/BaseClasses/Cursor.cpp
@@ -0,0 +1,152 @@
+#include "Cursor.h"
+
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Graphics/RenderBufferManager.h"
+#include "Runtime/Camera/ImageFilters.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Camera/RenderLayers/GUITexture.h"
+#include "Runtime/Input/InputManager.h"
+#include "Runtime/Misc/PlayerSettings.h"
+
+namespace Cursors
+{
+void RenderSoftwareCursor ()
+{
+ Texture2D* softCursor = GetSoftwareCursor();
+ if (softCursor && GetScreenManager().GetShowCursor())
+ {
+ DeviceMVPMatricesState preserveMVP;
+ SetupPixelCorrectCoordinates();
+
+ Vector2f pos = GetInputManager().GetMousePosition();
+ Vector2f hotSpotOffset = GetCursorHotspot();
+
+ pos.x -= hotSpotOffset.x;
+ pos.y += hotSpotOffset.y;
+
+ // the color is set to 0.5f because the gui-texture shader multiplies the vertex color by 2 for some reason
+ // pos is floored to match the behaviour of the hardware cursors
+ DrawGUITexture (Rectf ((int)pos.x, (int)pos.y, softCursor->GetGLWidth(), -softCursor->GetGLHeight()), softCursor, ColorRGBAf(0.5f, 0.5f, 0.5f, 0.5f));
+ }
+}
+
+#if !PLATFORM_SUPPORTS_HARDWARE_CURSORS
+
+
+typedef UnityCursor<int> SoftCursor;
+typedef CursorManager<SoftCursor> SoftCursorManager;
+
+template<> SoftCursorManager* SoftCursorManager::s_CursorManager = NULL;
+
+static SoftCursor GenerateCursor (Texture2D* texture, Vector2f hotSpot)
+{
+ // if this is null you are doing it wrong
+ assert(texture);
+
+ SoftCursor c;
+ c.sCursor = texture;
+ c.hotspot = hotSpot;
+ return c;
+}
+
+void SetCursor (Texture2D* texture, Vector2f hotSpot, CursorMode forceHardware)
+{
+ SoftCursorManager& manager = SoftCursorManager::Instance();
+ if (!texture)
+ {
+ manager.m_CurrentCursor = manager.m_DefaultCursor;
+ return;
+ }
+
+ // try and find the cursor in the cache
+ SoftCursorManager::CursorCache::iterator found = manager.m_CursorCache.find (texture->GetTextureID());
+ SoftCursor cursorToSet;
+ bool shouldGenerateCursor = true;
+ if (manager.m_CursorCache.end() != found)
+ {
+ // see if old hotspot
+ // is the same as the one requested now...
+ // if it's not then delete the old cursor and recreate it!
+ cursorToSet = found->second;
+
+ if (!CompareApproximately (hotSpot.x, cursorToSet.hotspot.x)
+ || !CompareApproximately (hotSpot.y, cursorToSet.hotspot.y))
+ {
+ manager.m_CursorCache.erase(found);
+ }
+ else
+ {
+ shouldGenerateCursor = false;
+ }
+ }
+
+ if (shouldGenerateCursor)
+ {
+ cursorToSet = GenerateCursor (texture, hotSpot);
+ manager.m_CursorCache[texture->GetTextureID()] = cursorToSet;
+ }
+
+ manager.m_CurrentCursor = cursorToSet;
+}
+
+Texture2D* GetSoftwareCursor()
+{
+ return SoftCursorManager::Instance().m_CurrentCursor.sCursor;
+}
+
+Vector2f GetCursorHotspot()
+{
+ return SoftCursorManager::Instance().m_CurrentCursor.hotspot;
+}
+
+void InitializeCursors(Texture2D* defaultCursorTexture, Vector2f defaultCursorHotSpot)
+{
+ SoftCursorManager& manager = SoftCursorManager::Instance ();
+ if (defaultCursorTexture)
+ {
+ manager.m_DefaultCursor = GenerateCursor (defaultCursorTexture, defaultCursorHotSpot);
+ manager.m_CurrentCursor = manager.m_DefaultCursor;
+ manager.m_UsingBuiltinDefaultCursor = true;
+ }
+}
+
+void CleanupCursors()
+{
+ SoftCursorManager::Instance().Cleanup ();
+}
+
+// needed for windows linkage with hardware cursors disabled
+#if UNITY_WIN
+void ResetCursor ()
+{}
+
+bool HandleMouseCursor (UINT message, LPARAM lParam)
+{
+ return false;
+}
+
+HCURSOR GetHardwareCursor ()
+{
+ return NULL;
+}
+#endif
+
+// needed for osx linkage with hardware cursors disabled
+#if UNITY_OSX
+void ResetCursor ()
+{}
+
+NSCursor* GetCurrentCursor ()
+{
+ return NULL;
+}
+#endif
+
+#endif //!PLATFORM_SUPPORTS_HARDWARE_CURSORS
+
+}; //namespace
+
diff --git a/Runtime/BaseClasses/Cursor.h b/Runtime/BaseClasses/Cursor.h
new file mode 100644
index 0000000..b0072fb
--- /dev/null
+++ b/Runtime/BaseClasses/Cursor.h
@@ -0,0 +1,105 @@
+#pragma once
+
+#include "UnityPrefix.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Math/Vector2.h"
+
+#include <string>
+
+#define PLATFORM_SUPPORTS_HARDWARE_CURSORS (!UNITY_PEPPER && ((UNITY_WIN && !UNITY_WINRT) || UNITY_OSX || UNITY_LINUX || UNITY_FLASH))
+
+enum CursorMode
+{
+ kAutoHardwareCursor = 0,
+ kHardwareCursorOff = 1
+};
+
+#if UNITY_OSX
+ #ifdef __OBJC__
+ @class NSCursor;
+ #else
+ typedef struct objc_object NSCursor;
+ #endif
+#endif
+
+namespace Cursors
+{
+
+template <typename T>
+struct UnityCursor
+{
+ UnityCursor ()
+ {
+ hCursor = NULL;
+ sCursor = NULL;
+ }
+ T hCursor;
+ PPtr<Texture2D> sCursor;
+ Vector2f hotspot;
+
+ typedef T HCursorType;
+};
+
+template <typename T>
+struct CursorManager
+{
+ T m_DefaultCursor;
+ T m_CurrentCursor;
+
+ bool m_UsingBuiltinDefaultCursor;
+
+ typedef std::map<TextureID, T > CursorCache;
+ CursorCache m_CursorCache;
+
+ Texture2D* GetSoftwareCursor ()
+ {
+ return m_CurrentCursor.sCursor;
+ }
+
+ Vector2f GetCursorHotspot ()
+ {
+ return m_CurrentCursor.hotspot;
+ }
+
+ typename T::HCursorType GetHardwareCursor ()
+ {
+ return m_CurrentCursor.hCursor;
+ }
+
+ static CursorManager<T>* s_CursorManager;
+ static CursorManager<T>& Instance ()
+ {
+ if (s_CursorManager == NULL)
+ {
+ s_CursorManager = new CursorManager<T>();
+ }
+
+ return *s_CursorManager;
+ }
+
+ static void Cleanup ()
+ {
+ delete s_CursorManager;
+ s_CursorManager = NULL;
+ }
+};
+
+void SetCursor (Texture2D* texture, Vector2f hotSpot, CursorMode forceHardware);
+void RenderSoftwareCursor ();
+Texture2D* GetSoftwareCursor ();
+Vector2f GetCursorHotspot ();
+void InitializeCursors (Texture2D* defaultCursorTexture, Vector2f defaultCursorHotSpot);
+void CleanupCursors ();
+
+#if UNITY_WIN
+void ResetCursor ();
+// returns true if the event is 'handled' WM_SETCURSOR in this case
+bool HandleMouseCursor (UINT message, LPARAM lParam);
+HCURSOR GetHardwareCursor ();
+#endif
+
+#if UNITY_OSX
+void ResetCursor ();
+NSCursor* GetHardwareCursor ();
+#endif
+};
diff --git a/Runtime/BaseClasses/EditorExtension.cpp b/Runtime/BaseClasses/EditorExtension.cpp
new file mode 100644
index 0000000..a55c92b
--- /dev/null
+++ b/Runtime/BaseClasses/EditorExtension.cpp
@@ -0,0 +1,95 @@
+#include "UnityPrefix.h"
+#include "EditorExtension.h"
+
+#if !GAMERELEASE
+
+#include "Editor/Src/EditorExtensionImpl.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferUtility.h"
+#include "Runtime/Serialize/SerializationMetaFlags.h"
+#include "Runtime/Utilities/dynamic_bitset.h"
+#include "Editor/Src/Prefabs/Prefab.h"
+#include "Editor/Src/Prefabs/PrefabBackwardsCompatibility.h"
+
+EditorExtension::EditorExtension (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+EditorExtension::~EditorExtension ()
+{
+ Assert(m_DeprecatedExtensionPtr.GetInstanceID() == 0);
+}
+
+bool EditorExtension::IsPrefabParent () const
+{
+ Prefab* prefab = m_Prefab;
+ return prefab != NULL && prefab->IsPrefabParent();
+}
+
+template<class TransferFunction>
+void EditorExtension::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ if (!transfer.IsSerializingForGameRelease ())
+ {
+ if (SerializePrefabIgnoreProperties(transfer))
+ {
+ transfer.Transfer (m_PrefabParentObject, "m_PrefabParentObject", kHideInEditorMask | kIgnoreWithInspectorUndoMask);
+ transfer.Transfer (m_Prefab, "m_PrefabInternal", kHideInEditorMask | kIgnoreWithInspectorUndoMask);
+ }
+
+ if (transfer.IsReadingBackwardsCompatible())
+ transfer.Transfer (m_DeprecatedExtensionPtr, "m_ExtensionPtr", kHideInEditorMask | kIgnoreWithInspectorUndoMask);
+ }
+}
+
+PPtr<EditorExtensionImpl> GetDeprecatedExtensionPtrIfExists (const Object& o)
+{
+ EditorExtension* extension = dynamic_pptr_cast<EditorExtension*> (&o);
+ if (extension)
+ return extension->m_DeprecatedExtensionPtr;
+ else
+ return NULL;
+}
+
+void EditorExtension::PatchPrefabBackwardsCompatibility ()
+{
+ if (m_DeprecatedExtensionPtr.IsValid ())
+ {
+ m_Prefab = m_DeprecatedExtensionPtr->m_DataTemplate;
+ if (m_DeprecatedExtensionPtr->m_TemplateFather.IsValid())
+ m_PrefabParentObject = m_DeprecatedExtensionPtr->m_TemplateFather;
+
+ if (m_Prefab.IsValid() && m_PrefabParentObject.IsValid() && m_DeprecatedExtensionPtr->m_Object == PPtr<EditorExtension> (this))
+ ReadOldPrefabFormat (*m_Prefab, *this, *m_PrefabParentObject, *m_DeprecatedExtensionPtr);
+
+ ///@TODO: DESTROY!!!
+ /// DestroyObject(m_DeprecatedExtensionPtr)
+ }
+ m_DeprecatedExtensionPtr = NULL;
+
+}
+
+
+void EditorExtension::AwakeFromLoad (AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad(mode);
+ PatchPrefabBackwardsCompatibility ();
+}
+
+IMPLEMENT_OBJECT_SERIALIZE (EditorExtension)
+IMPLEMENT_CLASS (EditorExtension)
+INSTANTIATE_TEMPLATE_TRANSFER (EditorExtension)
+
+#else
+
+IMPLEMENT_CLASS (EditorExtension)
+
+EditorExtension::~EditorExtension ()
+{
+
+}
+
+#endif
+
diff --git a/Runtime/BaseClasses/EditorExtension.h b/Runtime/BaseClasses/EditorExtension.h
new file mode 100644
index 0000000..5e2830a
--- /dev/null
+++ b/Runtime/BaseClasses/EditorExtension.h
@@ -0,0 +1,58 @@
+#ifndef EDITOREXTENSION_H
+#define EDITOREXTENSION_H
+
+#include "BaseObject.h"
+class TypeTree;
+class Prefab;
+class EditorExtensionImpl;
+
+#if UNITY_EDITOR
+
+class EXPORT_COREMODULE EditorExtension : public Object
+{
+ public:
+
+ PPtr<EditorExtension> m_PrefabParentObject;
+ PPtr<Prefab> m_Prefab;
+
+ PPtr<EditorExtensionImpl> m_DeprecatedExtensionPtr;
+
+ REGISTER_DERIVED_ABSTRACT_CLASS (EditorExtension, Object)
+ DECLARE_OBJECT_SERIALIZE (EditorExtension)
+
+ EditorExtension (MemLabelId label, ObjectCreationMode mode);
+ // ~EditorExtension (); declared-by-macro
+
+ friend PPtr<EditorExtensionImpl> GetDeprecatedExtensionPtrIfExists (const Object& o);
+
+ virtual bool IsPrefabParent () const;
+
+ PPtr<Prefab> GetPrefab () { return m_Prefab; }
+ PPtr<EditorExtension> GetPrefabParentObject () { return m_PrefabParentObject; }
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode mode);
+
+ void PatchPrefabBackwardsCompatibility ();
+
+
+ //std::string ExtractDeprecatedNameString ();
+};
+
+#else
+
+class EXPORT_COREMODULE EditorExtension : public Object
+{
+ public:
+
+ EditorExtension (MemLabelId label, ObjectCreationMode mode) : Super(label, mode) {}
+ // virtual ~EditorExtension (); declared-by-macro
+
+ REGISTER_DERIVED_CLASS (EditorExtension, Object)
+
+ virtual bool IsPrefabParent () const { return false; }
+};
+
+
+#endif
+
+#endif
diff --git a/Runtime/BaseClasses/EventIDs.h b/Runtime/BaseClasses/EventIDs.h
new file mode 100644
index 0000000..a37acdb
--- /dev/null
+++ b/Runtime/BaseClasses/EventIDs.h
@@ -0,0 +1,9 @@
+#pragma once
+
+enum EventIDs
+{
+ kBecameVisibleEvent,
+ kBecameInvisibleEvent,
+ kWillDestroyEvent,
+ kAnimatorClearEvent,
+}; \ No newline at end of file
diff --git a/Runtime/BaseClasses/EventManager.cpp b/Runtime/BaseClasses/EventManager.cpp
new file mode 100644
index 0000000..365faf0
--- /dev/null
+++ b/Runtime/BaseClasses/EventManager.cpp
@@ -0,0 +1,216 @@
+#include "UnityPrefix.h"
+#include "EventManager.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+
+////@TODO: Assert on recursive calls...
+
+EventManager* EventManager::s_Instance = NULL;
+
+EventManager& GetEventManager ()
+{
+ return *EventManager::s_Instance;
+}
+
+void EventManager::StaticInitialize()
+{
+ s_Instance = UNITY_NEW(EventManager,kMemManager);
+}
+
+void EventManager::StaticDestroy()
+{
+ UNITY_DELETE(s_Instance, kMemManager);
+}
+
+static RegisterRuntimeInitializeAndCleanup s_EventManagerCallbacks(EventManager::StaticInitialize, EventManager::StaticDestroy);
+
+EventManager::EventManager ()
+: m_EventPool (false, "EventManager", sizeof(EventEntry), 1024 * 4)
+#if DEBUGMODE
+, m_InvokingEventList(NULL)
+, m_InvokingEventActiveNode(NULL)
+#endif
+{
+}
+
+EventManager::EventIndex EventManager::AddEvent (EventCallback* callback, void* userData, EventIndex previousIndex)
+{
+ if (previousIndex == NULL)
+ {
+ EventIndex event = (EventIndex)m_EventPool.Allocate();
+ event->userData = userData;
+ event->callback = callback;
+ event->next = NULL;
+
+ return event;
+ }
+ else
+ {
+ EventIndex event = (EventIndex)m_EventPool.Allocate();
+ event->callback = callback;
+ event->userData = userData;
+ event->next = previousIndex;
+
+ return event;
+ }
+}
+
+/// Removes all events with the event index.
+void EventManager::RemoveEvent (EventIndex index)
+{
+ #if DEBUGMODE
+ // We can not delete the event which we are currently invoking
+ Assert (m_InvokingEventList != index);
+ #endif
+
+ while (index != NULL)
+ {
+ EventIndex next = index->next;
+ m_EventPool.Deallocate(index);
+ index = next;
+ }
+}
+
+bool EventManager::HasEvent (const EventIndex index, EventCallback* callback, const void* userData)
+{
+ EventIndex curIndex = index;
+ while (curIndex != NULL)
+ {
+ if (curIndex->callback == callback && curIndex->userData == userData)
+ return true;
+
+ curIndex = curIndex->next;
+ }
+ return false;
+}
+
+
+/// Removes an event with a specific callback & userData
+/// Returns the new event or null if no events in that index exist anymore.
+EventManager::EventIndex EventManager::RemoveEvent (EventIndex index, EventCallback* callback, void* userData)
+{
+ EventIndex previousIndex = NULL;
+ EventIndex curEvent = index;
+ while (curEvent != NULL)
+ {
+ if (curEvent->callback == callback && curEvent->userData == userData)
+ {
+ // While invoking we are allowed to remove the event being invoked itself but no other events on the same chain.
+ #if DEBUGMODE
+ Assert (m_InvokingEventList != index || m_InvokingEventActiveNode == curEvent);
+ #endif
+
+ EventIndex nextEvent = curEvent->next;
+ m_EventPool.Deallocate(curEvent);
+
+ if (previousIndex)
+ previousIndex->next = nextEvent;
+
+ if (index == curEvent)
+ return nextEvent;
+ else
+ return index;
+ }
+
+ previousIndex = curEvent;
+
+ curEvent = curEvent->next;
+ }
+
+ return index;
+}
+
+void EventManager::InvokeEvent (EventIndex index, void* senderUserData, int eventType)
+{
+ #if DEBUGMODE
+ GetEventManager().m_InvokingEventList = index;
+ #endif
+
+ while (index != NULL)
+ {
+ EventIndex next = index->next;
+
+ #if DEBUGMODE
+ GetEventManager().m_InvokingEventActiveNode = index;
+ #endif
+
+ index->callback(index->userData, senderUserData, eventType);
+ index = next;
+ }
+
+ #if DEBUGMODE
+ GetEventManager().m_InvokingEventList = NULL;
+ GetEventManager().m_InvokingEventActiveNode = NULL;
+ #endif
+}
+
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+
+struct LoggingCounter
+{
+ int counter;
+};
+
+void LoggingCallback (void* userData, void* sender, int type)
+{
+ LoggingCounter* logging = (LoggingCounter*)userData;
+
+ logging->counter++;
+}
+
+SUITE (EventsManagerTest)
+{
+TEST (EventsManager_EventsSimple)
+{
+ EventManager manager;
+
+ LoggingCounter counter1;
+ counter1.counter = 0;
+
+ EventManager::EventIndex index = manager.AddEvent (LoggingCallback, &counter1, NULL);
+ manager.InvokeEvent(index, NULL, 0);
+ CHECK_EQUAL(1, counter1.counter);
+}
+
+// Test chaining (But not duplicating)
+TEST (EventsManager_EventsChaining)
+{
+ EventManager manager;
+
+ LoggingCounter counter1;
+ counter1.counter = 0;
+ LoggingCounter counter2;
+ counter2.counter = 0;
+ LoggingCounter counter3;
+ counter3.counter = 0;
+
+ EventManager::EventIndex index;
+
+ // Add chained event (add one duplicate which should not be added or invoked)
+ index = manager.AddEvent (LoggingCallback, &counter1, NULL);
+ index = manager.AddEvent (LoggingCallback, &counter2, index);
+ index = manager.AddEvent (LoggingCallback, &counter3, index);
+
+ manager.InvokeEvent(index, NULL, 0);
+ CHECK_EQUAL(1, counter1.counter);
+ CHECK_EQUAL(1, counter2.counter);
+ CHECK_EQUAL(1, counter3.counter);
+
+ // Remove 1 chained event
+ index = manager.RemoveEvent (index, LoggingCallback, &counter2);
+ counter1.counter = 0;
+ counter2.counter = 0;
+ counter3.counter = 0;
+
+ manager.InvokeEvent(index, NULL, 0);
+ CHECK_EQUAL(1, counter1.counter);
+ CHECK_EQUAL(0, counter2.counter);
+ CHECK_EQUAL(1, counter3.counter);
+}
+}
+
+#endif
+
diff --git a/Runtime/BaseClasses/EventManager.h b/Runtime/BaseClasses/EventManager.h
new file mode 100644
index 0000000..2f0b813
--- /dev/null
+++ b/Runtime/BaseClasses/EventManager.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#include "Runtime/Utilities/MemoryPool.h"
+
+typedef void EventCallback (void* userData, void* sender, int eventType);
+
+
+// Small event entry. Keep this tight.
+struct EventEntry
+{
+ void* userData;
+ EventEntry* next;
+ EventCallback* callback;
+};
+
+class EventManager
+{
+public:
+ typedef EventEntry* EventIndex;
+
+private:
+ ////@TODO: Memory pool has a minimum size of 32 bytes. This one fits in 12. WTF???
+ MemoryPool m_EventPool;
+
+ static EventManager* s_Instance;
+ friend EventManager& GetEventManager ();
+
+ #if DEBUGMODE
+ EventIndex m_InvokingEventList;
+ EventIndex m_InvokingEventActiveNode;
+ #endif
+
+public:
+ EventManager ();
+
+ static void StaticInitialize ();
+ static void StaticDestroy ();
+
+ /// Adds an event
+ /// If there is already a previous event registered, it will chain them.
+ /// The reference to the event is the returned eventIndex
+ EventIndex AddEvent (EventCallback* callback, void* userData, EventIndex previousIndex);
+
+ /// Removes all events with the event index.
+ void RemoveEvent (EventIndex index);
+
+ /// Removes an event with a specific callback & userData
+ /// Returns the new event or null if no events in that index exist anymore.
+ /// AddEvent and RemoveEvent calls must be balanced.
+ EventIndex RemoveEvent (EventIndex index, EventCallback* callback, void* userData);
+
+ /// Does the event with that specific callback and userData exist?
+ static bool HasEvent (const EventIndex index, EventCallback* callback, const void* userData);
+
+ static void InvokeEvent (EventIndex index, void* sender, int eventType);
+};
+
+EventManager& GetEventManager ();
+
diff --git a/Runtime/BaseClasses/GameManager.cpp b/Runtime/BaseClasses/GameManager.cpp
new file mode 100644
index 0000000..0003b1f
--- /dev/null
+++ b/Runtime/BaseClasses/GameManager.cpp
@@ -0,0 +1,58 @@
+#include "UnityPrefix.h"
+#include "GameManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "ManagerContext.h"
+
+GameManager::~GameManager ()
+{
+ for (int i=0;i<ManagerContext::kManagerCount;i++)
+ {
+ if (GetManagerContext().m_Managers[i] == this)
+ SetManagerPtrInContext(i, NULL);
+ }
+}
+
+LevelGameManager::~LevelGameManager () { }
+GlobalGameManager::~GlobalGameManager () { }
+
+template<class TransferFunction>
+void LevelGameManager::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+}
+
+template<class TransferFunction>
+void GlobalGameManager::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+}
+
+char const* GlobalGameManager::GetName () const
+{
+ return GetClassName ().c_str ();
+}
+
+GameManager* GetGameManagerIfExists (int index)
+{
+ GameManager* manager = static_cast<GameManager*> (GetManagerPtrFromContext(index));
+ Assert(manager == dynamic_pptr_cast<GameManager*> (GetManagerPtrFromContext(index)));
+ return manager;
+}
+
+LevelGameManager::LevelGameManager(MemLabelId label, ObjectCreationMode mode) : Super(label, mode)
+{ }
+
+GlobalGameManager::GlobalGameManager(MemLabelId label, ObjectCreationMode mode) : Super(label, mode)
+{ }
+
+
+
+IMPLEMENT_CLASS (LevelGameManager)
+IMPLEMENT_CLASS (GlobalGameManager)
+IMPLEMENT_CLASS (GameManager)
+
+IMPLEMENT_OBJECT_SERIALIZE (LevelGameManager)
+IMPLEMENT_OBJECT_SERIALIZE (GlobalGameManager)
+
+INSTANTIATE_TEMPLATE_TRANSFER_EXPORTED (LevelGameManager)
+INSTANTIATE_TEMPLATE_TRANSFER_EXPORTED (GlobalGameManager)
diff --git a/Runtime/BaseClasses/GameManager.h b/Runtime/BaseClasses/GameManager.h
new file mode 100644
index 0000000..8d194c3
--- /dev/null
+++ b/Runtime/BaseClasses/GameManager.h
@@ -0,0 +1,69 @@
+#ifndef GAMEMANAGER_H
+#define GAMEMANAGER_H
+
+#include "EditorExtension.h"
+
+/// Any game manager (eg. AudioManager, dynamicsmanager) that needs serialization
+/// has to derive from either LevelGameManager or GlobalGameManager.
+/// Every level contains its own GameManager for that Level (eg. Scene, PhysicsManager)
+/// LevelGameManagers are destroyed and reloaded from the new scene when loading a new scene.
+/// GlobalGameManagers are singletons and loaded on
+/// startup of the gameplayer/editor (eg. InputManager, TagManager)
+
+class EXPORT_COREMODULE GameManager : public Object
+{
+ public:
+
+ REGISTER_DERIVED_ABSTRACT_CLASS (GameManager, Object)
+ GameManager(MemLabelId label, ObjectCreationMode mode) : Super(label, mode) { }
+// virtual ~GameManager ();
+
+ ///@TODO: Get rid of this. I am not sure why this is not just done in the destructor / cleanup class
+ virtual void NetworkOnApplicationQuit () { AssertString("not implemented"); }
+ virtual void NetworkUpdate () { AssertString("not implemented"); }
+};
+
+
+class EXPORT_COREMODULE LevelGameManager : public GameManager
+{
+ public:
+
+ virtual char const* GetName () const { return GetClassName().c_str (); }
+
+ REGISTER_DERIVED_ABSTRACT_CLASS (LevelGameManager, GameManager)
+ DECLARE_OBJECT_SERIALIZE (GameManager)
+
+ LevelGameManager(MemLabelId label, ObjectCreationMode mode);
+
+// virtual ~LevelGameManager ();
+};
+
+
+class EXPORT_COREMODULE GlobalGameManager : public GameManager
+{
+ public:
+
+ REGISTER_DERIVED_ABSTRACT_CLASS (GlobalGameManager, GameManager)
+ DECLARE_OBJECT_SERIALIZE (GlobalGameManager)
+
+ GlobalGameManager(MemLabelId label, ObjectCreationMode mode);
+
+// virtual ~GlobalGameManager ();
+
+ virtual char const* GetName () const;
+};
+
+GameManager* GetGameManagerIfExists (int index);
+
+inline GameManager* CreateGameManager (int classID)
+{
+ Object* o = Object::Produce (classID);
+ o->Reset ();
+ o->AwakeFromLoad(kDefaultAwakeFromLoad);
+ o->SetNameCpp (Object::ClassIDToString (classID));
+ return static_cast<GameManager*> (o);
+}
+
+#define CALL_MANAGER_IF_EXISTS(x,func) { GameManager* _manager = GetGameManagerIfExists(x); if (_manager) _manager->func; }
+
+#endif
diff --git a/Runtime/BaseClasses/GameObject.cpp b/Runtime/BaseClasses/GameObject.cpp
new file mode 100644
index 0000000..833052a
--- /dev/null
+++ b/Runtime/BaseClasses/GameObject.cpp
@@ -0,0 +1,1189 @@
+#include "UnityPrefix.h"
+#include "GameObject.h"
+#include "CleanupManager.h"
+#include "Runtime/Serialize/AwakeFromLoadQueue.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/SerializationMetaFlags.h"
+#include "Tags.h"
+#include "MessageHandler.h"
+#include "Runtime/Misc/ReproductionLog.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Misc/ComponentRequirement.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Containers/ConstantStringSerialization.h"
+#include "Runtime/Profiler/Profiler.h"
+#if UNITY_EDITOR
+#include "Editor/Src/BuildPipeline/BuildTargetPlatformSpecific.h"
+#include "Editor/Src/Utility/StaticEditorFlags.h"
+#endif
+#if UNITY_WII
+#include <rvlaux/clib.h>
+#endif
+
+using namespace std;
+
+namespace Unity
+{
+
+PROFILER_INFORMATION (gActivateGameObjectProfiler, "GameObject.Activate", kProfilerScripts)
+PROFILER_INFORMATION (gDeactivateGameObjectProfiler, "GameObject.Deactivate", kProfilerScripts)
+
+Unity::GameObject::DestroyGOCallbackFunction* Unity::GameObject::s_GameObjectDestroyedCallback = NULL;
+Unity::GameObject::SetGONameFunction* Unity::GameObject::s_SetGONameCallback = NULL;
+MessageForwarders* Unity::GameObject::s_RegisteredMessageForwarders = NULL;
+MessageHandler* Unity::GameObject::s_MessageHandler = NULL;
+
+GameObject::GameObject (MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode),
+// m_Component (GameObject::Container::allocator_type (*baseAllocator)),
+ m_ActiveGONode (this)
+{
+ m_SupportedMessages = 0;
+ m_IsDestroying = false;
+ m_IsActivating = false;
+ m_Tag = 0;
+ m_IsActive = false;
+ m_IsActiveCached = -1;
+
+ #if UNITY_EDITOR
+ m_IsOldVersion = false;
+ m_StaticEditorFlags = 0;
+ m_IsMarkedVisible = kSelfVisible;
+ #endif
+}
+
+void GameObject::Reset ()
+{
+ Super::Reset ();
+ m_Layer = kDefaultLayer;
+ m_Tag = 0;
+ #if UNITY_EDITOR
+ m_StaticEditorFlags = 0;
+ m_TagString = TagToString (m_Tag);
+ m_NavMeshLayer = 0;
+ #endif
+}
+
+GameObject::~GameObject ()
+{
+ Assert(!m_ActiveGONode.IsInList());
+}
+
+void GameObject::WillDestroyGameObject ()
+{
+ Assert(!m_IsDestroying);
+ m_IsDestroying = true;
+
+ // Find a component with the requested ID
+ Container::const_iterator i;
+ Container::const_iterator end = m_Component.end ();
+ for (i=m_Component.begin ();i != end; ++i)
+ {
+ Component& com = *i->second;
+ com.WillDestroyComponent();
+ }
+}
+
+void GameObject::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ #if SUPPORT_LOG_ORDER_TRACE
+ if (IsActive() && RunningReproduction())
+ {
+ if (SUPPORT_LOG_ORDER_TRACE == 2)
+ {
+ LogString(Format("AwakeFromLoad %s (%s) [%d]", GetName(), GetClassName().c_str(), GetInstanceID()));
+ }
+ else
+ {
+ LogString(Format("AwakeFromLoad %s (%s)", GetName(), GetClassName().c_str()));
+ }
+ }
+ #endif
+
+ Super::AwakeFromLoad (awakeMode);
+ SetSupportedMessagesDirty ();
+ UpdateActiveGONode ();
+
+ if (s_SetGONameCallback)
+ s_SetGONameCallback(this);
+
+ #if UNITY_EDITOR
+ // When we are modifying the game object active state from the inspector
+ // We need to Activate / Deactivate the relevant components
+ // This never happens in the player.
+ if (awakeMode == kDefaultAwakeFromLoad)
+ ActivateAwakeRecursively();
+ #endif
+
+}
+
+int GameObject::CountDerivedComponents (int compareClassID)const
+{
+ int count = 0;
+ Container::const_iterator i;
+ for (i=m_Component.begin ();i != m_Component.end (); ++i)
+ count += Object::IsDerivedFromClassID (i->first, compareClassID);
+ return count;
+}
+
+
+Component* GameObject::FindConflictingComponentPtr (int classID) const
+{
+ const vector_set<int>& conflicts = FindConflictingComponents(classID);
+ if (conflicts.empty())
+ return NULL;
+
+ for (Container::const_iterator i = m_Component.begin(); i != m_Component.end(); ++i)
+ {
+ for (vector_set<int>::const_iterator c = conflicts.begin(); c != conflicts.end(); ++c)
+ {
+ if (Object::IsDerivedFromClassID(i->first, *c))
+ return i->second;
+ }
+ }
+
+ return NULL;
+}
+
+
+void GameObject::SetName (char const* name)
+{
+ m_Name.assign(name, GetMemoryLabel());
+ if (s_SetGONameCallback)
+ s_SetGONameCallback(this);
+ SetDirty ();
+}
+
+void GameObject::UpdateActiveGONode()
+{
+ m_ActiveGONode.RemoveFromList();
+ if (IsActive())
+ {
+ if (m_Tag != 0)
+ GetGameObjectManager().m_TaggedNodes.push_back(m_ActiveGONode);
+ else
+ GetGameObjectManager().m_ActiveNodes.push_back(m_ActiveGONode);
+ }
+}
+
+void GameObject::MarkActiveRecursively (bool state)
+{
+ Transform &transform = GetComponent (Transform);
+ for (Transform::iterator i=transform.begin ();i != transform.end ();i++)
+ (*i)->GetGameObject().MarkActiveRecursively (state);
+
+ m_IsActive = state;
+ SetDirty();
+}
+
+void GameObject::ActivateAwakeRecursivelyInternal (DeactivateOperation deactivateOperation, AwakeFromLoadQueue &queue)
+{
+ if (m_IsActivating)
+ {
+ ErrorStringObject("GameObject is already being activated or deactivated.", this);
+ return;
+ }
+ bool state;
+ bool changed;
+ m_IsActivating = true;
+ if (m_IsActiveCached != -1)
+ {
+ bool oldState = m_IsActiveCached;
+ m_IsActiveCached = -1;
+ state = IsActive();
+ changed = oldState != state;
+ }
+ else
+ {
+ state = IsActive();
+ changed = true;
+ }
+
+ Transform *transform = QueryComponent (Transform);
+ if (transform)
+ {
+ // use a loop by index rather than a iterator, as the children can adjust
+ // the child list during the Awake call, and invalidate the iterator
+ for (int i = 0; i < transform->GetChildrenCount(); i++)
+ transform->GetChild(i).GetGameObject().ActivateAwakeRecursivelyInternal (deactivateOperation, queue);
+ }
+
+ if (changed)
+ {
+ for (int i=0;i<m_Component.size ();i++)
+ {
+ Component& component = *m_Component[i].second;
+ if (state)
+ {
+ AssertIf (&*component.m_GameObject != this);
+ component.SetGameObjectInternal (this);
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ queue.Add(*m_Component[i].second);
+ else
+ component.AwakeFromLoad (kActivateAwakeFromLoad);
+ }
+ else
+ component.Deactivate (deactivateOperation);
+ }
+
+ if (state)
+ UpdateActiveGONode ();
+ else
+ m_ActiveGONode.RemoveFromList();
+ }
+ m_IsActivating = false;
+}
+
+void GameObject::ActivateAwakeRecursively (DeactivateOperation deactivateOperation)
+{
+ AwakeFromLoadQueue queue (kMemTempAlloc);
+ ActivateAwakeRecursivelyInternal (deactivateOperation, queue);
+ queue.AwakeFromLoad (kActivateAwakeFromLoad);
+}
+
+void GameObject::SetActiveRecursivelyDeprecated (bool state)
+{
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion3_5_a1))
+ {
+ if (IsPrefabParent ())
+ {
+ ErrorString(Format("Prefab GameObject's can not be made active! (%s)", GetName()));
+ return;
+ }
+
+ // First Mark all objects as active
+ MarkActiveRecursively (state);
+
+ // Then awake them.
+ ActivateAwakeRecursively ();
+ }
+ else
+ {
+ // Old versions used to mark active and awake each object after another.
+ // That would cause problems with colliders being created twice, once without the active
+ // parent rigibdodies, and once with, thus causing the unnecessary creation and destruction
+ // of static colliders (slow). So we fixed it (see above), but we keep the old behaviour for
+ // legacy content.
+ Transform &transform = GetComponent (Transform);
+ for (Transform::iterator i=transform.begin ();i != transform.end ();i++)
+ (*i)->GetGameObject().SetActiveRecursivelyDeprecated (state);
+
+ if (state)
+ Activate();
+ else
+ Deactivate();
+ }
+}
+
+void GameObject::AddComponentInternal (Component* com)
+{
+ AssertIf (com == NULL);
+ {
+ m_Component.push_back (std::make_pair (com->GetClassID (), ImmediatePtr<Component> (com)));
+ }
+ // Make sure it isn't already added to another GO
+ Assert ( com->m_GameObject.GetInstanceID() == 0 || com->GetGameObjectPtr() == this );
+
+ com->SetHideFlags(GetHideFlags());
+ com->m_GameObject = this;
+
+ if (IsActive ())
+ com->AwakeFromLoad (kActivateAwakeFromLoad);
+ else
+ com->AwakeFromLoad (kDefaultAwakeFromLoad);
+
+ com->SetDirty ();
+ SetDirty ();
+
+ SendMessage(kDidAddComponent, com, ClassID (Component));
+
+ SetSupportedMessagesDirty ();
+}
+
+Component* GameObject::QueryComponentExactTypeImplementation (int classID) const
+{
+ // Find a component with the requested ID
+ Container::const_iterator i;
+ Container::const_iterator end = m_Component.end ();
+ for (i=m_Component.begin ();i != end; ++i)
+ {
+ if (i->first == classID)
+ return i->second;
+ }
+
+ return NULL;
+}
+
+
+Component* GameObject::QueryComponentImplementation (int classID) const
+{
+ // Find a component with the requested ID
+ Container::const_iterator i;
+ Container::const_iterator end = m_Component.end ();
+ for (i=m_Component.begin ();i != end; ++i)
+ {
+ if (Object::IsDerivedFromClassID (i->first, classID))
+ return i->second;
+ }
+
+ return NULL;
+}
+
+void GameObject::RemoveComponentAtIndex (int index)
+{
+ Container::iterator i = m_Component.begin () + index;
+
+ Component* com = i->second;
+ AssertIf (com == NULL);
+
+ m_Component.erase (i);
+ com->m_GameObject = NULL;
+
+ com->SetDirty ();
+ SetDirty ();
+ SetSupportedMessagesDirty ();
+}
+
+void GameObject::SetComponentAtIndexInternal (PPtr<Component> component, int index)
+{
+ m_Component[index].first = component->GetClassID();
+ m_Component[index].second.SetInstanceID(component.GetInstanceID());
+}
+
+
+void GameObject::SetSupportedMessagesDirty ()
+{
+ Assert(!IsDestroying());
+
+ int oldSupportedMessage = m_SupportedMessages;
+ m_SupportedMessages = 0;
+ if (IsDestroying ())
+ return;
+
+ GetSupportedMessagesRecalculate ();
+ if (oldSupportedMessage != m_SupportedMessages)
+ {
+ for (Container::iterator i=m_Component.begin ();i != m_Component.end (); ++i)
+ if (i->second)
+ i->second->SupportedMessagesDidChange (m_SupportedMessages);
+ }
+}
+
+void GameObject::GetSupportedMessagesRecalculate ()
+{
+ Assert(!IsDestroying());
+
+ m_SupportedMessages = 0;
+ for (Container::iterator i=m_Component.begin ();i != m_Component.end (); ++i)
+ if (i->second)
+ m_SupportedMessages |= i->second->CalculateSupportedMessages ();
+}
+
+Component* GameObject::GetComponentPtrAtIndex (int i)const
+{
+ return m_Component[i].second;
+}
+
+int GameObject::GetComponentIndex (Component *component)
+{
+ Assert(!IsDestroying());
+
+ for (int i = 0; i < GetComponentCount (); i++)
+ {
+ if (&GetComponentAtIndex (i) == component)
+ return i;
+ }
+
+ return -1;
+}
+
+
+void GameObject::SwapComponents (int index1, int index2)
+{
+ AssertIf (index1 > m_Component.size() || index1 < 0);
+ AssertIf (index2 > m_Component.size() || index2 < 0);
+
+ ComponentPair tmp = m_Component[index1];
+ m_Component[index1] = m_Component[index2];
+ m_Component[index2] = tmp;
+
+ Component* comp1 = m_Component[index1].second;
+ Component* comp2 = m_Component[index2].second;
+ if (comp1 && comp1->IsDerivedFrom(ClassID(Behaviour)))
+ {
+ Behaviour* beh = static_cast<Behaviour*>(comp1);
+ if (beh->GetEnabled())
+ {
+ beh->SetEnabled (false);
+ beh->SetEnabled (true);
+ }
+ }
+ if (comp2 && comp2->IsDerivedFrom(ClassID(Behaviour)))
+ {
+ Behaviour* beh = static_cast<Behaviour*>(comp2);
+ if (beh->GetEnabled())
+ {
+ beh->SetEnabled (false);
+ beh->SetEnabled (true);
+ }
+ }
+ SetDirty();
+}
+
+bool GameObject::IsActive () const
+{
+ if (m_IsActiveCached != -1)
+ return m_IsActiveCached;
+
+ // For pre 4.0 content activate state is the same as m_IsActive.
+ if (!IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ m_IsActiveCached = m_IsActive;
+ else
+ {
+ // Calculate active state based on the hierarchy
+ m_IsActiveCached = m_IsActive && !(IsPersistent() || IsPrefabParent());
+ Transform *trs = QueryComponent (Transform);
+ if (trs)
+ {
+ Transform *parent = GetComponent (Transform).GetParent();
+ if (parent)
+ m_IsActiveCached = m_IsActiveCached && parent->GetGameObject().IsActive();
+ }
+ }
+
+ return m_IsActiveCached;
+}
+
+bool GameObject::IsActiveIgnoreImplicitPrefab ()
+{
+ // This function does not make sense to be called for old content.
+ Assert (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1));
+
+ Transform *trs = QueryComponent (Transform);
+ if (trs)
+ {
+ Transform *parent = GetComponent (Transform).GetParent();
+ if (parent)
+ return m_IsActive && parent->GetGameObject().IsActiveIgnoreImplicitPrefab();
+ }
+
+ return m_IsActive;
+}
+
+void GameObject::Activate ()
+{
+ if (IsActive())
+ return;
+
+ PROFILER_AUTO(gActivateGameObjectProfiler, this);
+
+ SetDirty ();
+
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ {
+ m_IsActive = true;
+ ActivateAwakeRecursively ();
+ // After AwakeFromLoad, 'this' could have been destroyed (if user is Destroying in OnEnable or Awake)
+ // So do not access it any further
+ }
+ else
+ {
+ if (IsPrefabParent ())
+ {
+ ErrorString(Format("Prefab GameObject's can not be made active! (%s)", GetName()));
+ return;
+ }
+
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_3_a1) && IsPersistent ())
+ {
+ ErrorString(Format("GameObjects stored in assets can not be made active! (%s)", GetName()));
+ return;
+ }
+
+ m_IsActive = true;
+ m_IsActiveCached = m_IsActive;
+ for (int i=0;i<m_Component.size ();i++)
+ {
+ Component& component = *m_Component[i].second;
+ AssertIf (&*component.m_GameObject != this);
+ component.m_GameObject = this;
+ component.AwakeFromLoad (kActivateAwakeFromLoad);
+ }
+
+ UpdateActiveGONode ();
+ }
+
+}
+
+void GameObject::Deactivate (DeactivateOperation operation)
+{
+ PROFILER_AUTO(gDeactivateGameObjectProfiler, this)
+
+ if (!IsActive())
+ {
+ if (m_IsActive)
+ {
+ m_IsActive = false;
+ SetDirty ();
+ }
+ return;
+ }
+
+ m_IsActive = false;
+
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ ActivateAwakeRecursively (operation);
+ else
+ {
+ m_IsActiveCached = m_IsActive;
+ for (int i=0;i<m_Component.size ();i++)
+ {
+ Component& com = *m_Component[i].second;
+ com.Deactivate (operation);
+ }
+
+ m_ActiveGONode.RemoveFromList();
+ }
+
+ SetDirty ();
+}
+
+void GameObject::SetSelfActive (bool state)
+{
+ if (state)
+ Activate();
+ else
+ Deactivate(kNormalDeactivate);
+}
+
+void GameObject::AddComponentInternal (GameObject& gameObject, Component& clone)
+{
+ SET_ALLOC_OWNER(&gameObject);
+ Assert(clone.m_GameObject == NULL);
+ gameObject.m_Component.push_back(make_pair(clone.GetClassID(), &clone));
+ clone.m_GameObject = &gameObject;
+}
+
+void GameObject::RemoveComponentFromGameObjectInternal (Component& clone)
+{
+ GameObject* go = clone.GetGameObjectPtr();
+ if (go == NULL)
+ return;
+
+ int index = go->GetComponentIndex(&clone);
+ if (index == -1)
+ return;
+
+ go->m_Component.erase(go->m_Component.begin() + index);
+ clone.m_GameObject = NULL;
+}
+
+void GameObject::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+
+ // Remove Components from m_Component if they vanished without deactivating.
+ // (eg. class hierarchy changed and the class doesn't exist anymore when loading from disk)
+ int i = 0;
+ while (i < m_Component.size ())
+ {
+ // Use is object available instead of normal comparison so that we dont load the object
+ // which might already trigger the component to query for other components.
+ int CurComponentInstanceID = m_Component[i].second.GetInstanceID ();
+ if (!IsObjectAvailable (CurComponentInstanceID))
+ {
+ ErrorStringObject (Format("Component %s could not be loaded when loading game object. Cleaning up!", ClassIDToString(m_Component[i].first).c_str()), this);
+ m_Component.erase (m_Component.begin () + i);
+ }
+ else
+ i++;
+ }
+
+ // Preload all the components!
+ // This is necessary to avoid recursion and awake functions calling remove component,
+ // When we are removing the wrong ones anyway.
+ i = 0;
+ while (i < m_Component.size ())
+ {
+ Component* com = m_Component[i].second;
+ UNUSED(com);
+ i++;
+ }
+
+ // Remove Components with wrong gameobject ptrs
+ i = 0;
+ while (i < m_Component.size ())
+ {
+ Component* com = m_Component[i].second;
+ if (com && com->GetGameObjectPtr () == this)
+ {
+ i++;
+ continue;
+ }
+
+ if (com)
+ {
+ if (com->GetGameObjectPtr () == NULL)
+ {
+ com->SetGameObjectInternal (this);
+ ErrorStringObject ("Component (" + com->GetClassName () + ") has a broken GameObject reference. Fixing!", this);
+ continue;
+ }
+ else
+ {
+ ErrorStringObject ("Failed to load component (" + com->GetClassName () + ")! Removing it!", this);
+ com->SetHideFlags(kHideAndDontSave);
+ }
+ }
+ else
+ {
+ ErrorStringObject ("Failed to load component (" + Object::ClassIDToString (m_Component[i].first) + ")! Removing it!", this);
+ }
+
+ m_Component.erase (m_Component.begin () + i);
+ }
+
+ // make sure we always have at least one transform on a gameobject
+ int transformCount = 0;
+ for (int i = 0; i < m_Component.size (); ++i)
+ {
+ if(m_Component[i].first == ClassID (Transform))
+ transformCount++;
+
+ if (transformCount > 1)
+ {
+ // More than one transform on object. If it's a scene object (transient),
+ // remove the extraneous transform. For prefabs (persistent), touching the
+ // transform hierarchy will lead to all kinds of troubles so we just leave
+ // it be.
+ if (!IsPersistent ())
+ {
+ Transform* com = static_cast<Transform*> (&*m_Component[i].second);
+ com->SetParent (NULL);
+
+ RemoveComponentAtIndex (i);
+ --i;
+
+ GameObject* dummyObject = CreateObjectFromCode<GameObject> ();
+ dummyObject->SetName ("!! ORPHAN TRANSFORM !!");
+ dummyObject->AddComponentInternal (com);
+
+ ErrorStringObject ("Object has multiple transform components. Created dummy GameObject and added transform to it!", this);
+ }
+ else
+ {
+ ErrorStringObject ("Object has multiple transform components!", this);
+ }
+ }
+ }
+ if(transformCount == 0)
+ {
+ ErrorStringObject (Format("Transform component could not be found on game object. Adding one!"), this);
+ AddComponentUnchecked(*this,ClassID(Transform), NULL, NULL);
+ }
+
+#if UNITY_EDITOR
+ if (m_IsOldVersion && m_IsActive && !IsPersistent() && !IsActiveIgnoreImplicitPrefab())
+ WarningStringObject ("GameObject is active but a parent is inactive. Active state is now inherited. Change the parenting to get back the old behaviour!", this);
+#endif
+
+ SetSupportedMessagesDirty ();
+}
+
+void GameObject::SetLayer (int layer)
+{
+ if (layer >= 0 && layer < 32)
+ {
+ m_Layer = layer;
+ MessageData data;
+ SendMessageAny (kLayerChanged, data);
+ SetDirty ();
+ }
+ else
+ ErrorString ("A game object can only be in one layer. The layer needs to be in the range [0...31]");
+}
+
+void GameObject::SetTag (UInt32 tag)
+{
+ #if UNITY_EDITOR
+ m_TagString = TagToString (tag);
+ #endif
+
+ m_Tag = tag;
+ UpdateActiveGONode();
+
+ AssertIf (tag != -1 && tag != m_Tag);
+ AssertIf (tag == -1 && m_Tag != 0xFFFF);
+ MessageData data;
+ SendMessageAny (kLayerChanged, data);
+ SetDirty ();
+}
+
+void GameObject::SetHideFlags (int flags)
+{
+ SetHideFlagsObjectOnly(flags);
+ for (int i=0;i<m_Component.size ();i++)
+ {
+ Component& com = *m_Component[i].second;
+ com.SetHideFlags(flags);
+ }
+}
+
+template<class TransferFunction>
+void GameObject::TransferComponents (TransferFunction& transfer)
+{
+ Container* components_to_serialize = &m_Component;
+
+ // When cloning objects for prefabs and instantiate, we don't use serialization to duplicate the hierarchy,
+ // we duplicate the hierarchy directly
+ if (!SerializePrefabIgnoreProperties(transfer))
+ return;
+
+#if UNITY_EDITOR
+ Container filtered_components;
+ if (transfer.IsWritingGameReleaseData ())
+ {
+ components_to_serialize = &filtered_components;
+ for (Container::iterator i = m_Component.begin(); i != m_Component.end(); i++)
+ {
+ if (IsClassSupportedOnBuildTarget(i->first, transfer.GetBuildingTarget().platform))
+ filtered_components.push_back (*i);
+ }
+ }
+#endif
+
+ transfer.Transfer (*components_to_serialize, "m_Component", kHideInEditorMask | kStrongPPtrMask | kIgnoreWithInspectorUndoMask);
+}
+
+
+
+
+
+template<class TransferFunction>
+void GameObject::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion (4);
+ TransferComponents(transfer);
+
+ TRANSFER (m_Layer);
+
+ #if GAMERELEASE
+ TransferConstantString(m_Name, "m_Name", kNoTransferFlags, GetMemoryLabel(), transfer);
+ TRANSFER (m_Tag);
+ transfer.Transfer (m_IsActive, "m_IsActive");
+ #else
+
+ #if UNITY_EDITOR
+ if (transfer.IsVersionSmallerOrEqual (3))
+ m_IsOldVersion = true;
+ #endif
+
+ if (transfer.IsOldVersion (3) || transfer.IsCurrentVersion ())
+ {
+ TransferConstantString(m_Name, "m_Name", kNoTransferFlags, GetMemoryLabel(), transfer);
+
+ if (transfer.IsSerializingForGameRelease ())
+ {
+ TRANSFER (m_Tag);
+ if (transfer.IsReading ())
+ m_TagString = TagToString (m_Tag);
+
+ transfer.Transfer (m_IsActive, "m_IsActive");
+ }
+ else
+ {
+ transfer.Transfer (m_TagString, "m_TagString");
+ if (transfer.IsReading ())
+ m_Tag = StringToTagAddIfUnavailable (m_TagString);
+
+ transfer.Transfer (m_Icon, "m_Icon", kNoTransferFlags);
+ transfer.Transfer (m_NavMeshLayer, "m_NavMeshLayer", kHideInEditorMask);
+
+ transfer.Transfer (m_StaticEditorFlags, "m_StaticEditorFlags", kNoTransferFlags | kGenerateBitwiseDifferences);
+
+ // Read deprecated static flag and set it up as m_StaticEditorFlags
+ if (transfer.IsReadingBackwardsCompatible ())
+ {
+ bool isStatic = false;
+ transfer.Transfer (isStatic, "m_IsStatic", kNoTransferFlags);
+ if (isStatic)
+ m_StaticEditorFlags = 0xFFFFFFFF;
+ }
+ transfer.Transfer (m_IsActive, "m_IsActive", kHideInEditorMask);
+ }
+ }
+ else if (transfer.IsOldVersion (2))
+ {
+ TRANSFER (m_TagString);
+ m_Tag = StringToTag (m_TagString);
+ transfer.Transfer (m_IsActive, "m_IsActive");
+ }
+ else if (transfer.IsOldVersion (1))
+ {
+ TRANSFER (m_Tag);
+ m_TagString = TagToString (m_Tag);
+ transfer.Transfer (m_IsActive, "m_IsActive");
+ }
+ #endif
+
+ // Make sure that old prefabs are always active.
+ if (transfer.IsVersionSmallerOrEqual (3) && IsPersistent() && IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ m_IsActive = true;
+}
+
+bool GameObject::GetIsStaticDeprecated ()
+{
+#if UNITY_EDITOR
+ return m_StaticEditorFlags != 0;
+#else
+ return false;
+#endif
+}
+
+void GameObject::SetIsStaticDeprecated(bool s)
+{
+#if UNITY_EDITOR
+ m_StaticEditorFlags = s ? 0xFFFFFFFF : 0;
+ SetDirty();
+#endif
+}
+
+bool GameObject::IsStaticBatchable () const
+{
+#if UNITY_EDITOR
+ return AreStaticEditorFlagsSet (kBatchingStatic);
+#else
+ return false;
+#endif
+}
+
+#if UNITY_EDITOR
+
+bool GameObject::AreStaticEditorFlagsSet (StaticEditorFlags flags) const
+{
+ return (m_StaticEditorFlags & (UInt32)flags) != 0;
+}
+
+StaticEditorFlags GameObject::GetStaticEditorFlags () const
+{
+ return (StaticEditorFlags)m_StaticEditorFlags;
+}
+
+void GameObject::SetStaticEditorFlags (StaticEditorFlags flags)
+{
+ m_StaticEditorFlags = (UInt32)flags;
+ SetDirty();
+}
+
+void GameObject::SetIcon (PPtr<Texture2D> icon)
+{
+ if (m_Icon != icon)
+ {
+ m_Icon = icon;
+ SetDirty ();
+ }
+}
+
+PPtr<Texture2D> GameObject::GetIcon () const
+{
+ return m_Icon;
+}
+#endif
+
+void GameObject::RegisterDestroyedCallback (DestroyGOCallbackFunction* callback)
+{
+ s_GameObjectDestroyedCallback = callback;
+}
+
+void GameObject::InvokeDestroyedCallback (GameObject* go)
+{
+ if (s_GameObjectDestroyedCallback)
+ s_GameObjectDestroyedCallback (go);
+}
+
+void GameObject::RegisterSetGONameCallback (SetGONameFunction* callback)
+{
+ s_SetGONameCallback = callback;
+}
+
+static int GetHighestGOComponentClassID ()
+{
+ static int highestGOComponentClassID = 0;
+ if (highestGOComponentClassID != 0)
+ return highestGOComponentClassID;
+
+ vector<SInt32> classes;
+ Object::FindAllDerivedClasses (ClassID (Component), &classes, false);
+ for (int i=0;i<classes.size ();i++)
+ highestGOComponentClassID = max<int> (highestGOComponentClassID, classes[i]);
+
+ return highestGOComponentClassID;
+}
+
+void GameObject::RegisterMessageHandler (int classID, const MessageIdentifier& messageIdentifier,
+ MessagePtr message, int typeId)
+{
+ Assert(s_RegisteredMessageForwarders);
+ s_RegisteredMessageForwarders->resize (max(classID, GetHighestGOComponentClassID ()) + 1);
+ (*s_RegisteredMessageForwarders)[classID].RegisterMessageCallback (messageIdentifier.messageID, message, typeId);
+}
+
+void GameObject::RegisterAllMessagesHandler (int classID, MessagePtr message, CanHandleMessagePtr canHandleNotification)
+{
+ Assert(s_RegisteredMessageForwarders);
+ s_RegisteredMessageForwarders->resize (max(classID, GetHighestGOComponentClassID ()) + 1);
+ (*s_RegisteredMessageForwarders)[classID].RegisterAllMessagesCallback (message, canHandleNotification);
+}
+
+static void PropagateNotificationsToDerivedClasses (MessageForwarders& notifications)
+{
+ vector<SInt32> classes;
+ Object::FindAllDerivedClasses (ClassID (Object), &classes, false);
+ int highestClassID = 0;
+ for (unsigned i=0;i<classes.size ();i++)
+ highestClassID = max<int> (classes[i], highestClassID);
+
+ notifications.resize (highestClassID + 1);
+
+ for (int classID=0;classID<notifications.size ();classID++)
+ {
+ if (Object::ClassIDToRTTI (classID) == NULL)
+ continue;
+
+ int superClassID = Object::GetSuperClassID (classID);
+ while (superClassID != ClassID (Object))
+ {
+ notifications[classID].AddBaseMessages (notifications[superClassID]);
+ superClassID = Object::GetSuperClassID (superClassID);
+ }
+ }
+}
+
+void GameObject::InitializeMessageHandlers ()
+{
+ Assert(s_MessageHandler && s_RegisteredMessageForwarders);
+ PropagateNotificationsToDerivedClasses (*s_RegisteredMessageForwarders);
+ s_MessageHandler->Initialize (*s_RegisteredMessageForwarders);
+ s_RegisteredMessageForwarders->clear ();
+}
+
+void GameObject::InitializeMessageIdentifiers ()
+{
+ Assert(s_MessageHandler == NULL);
+ s_MessageHandler = UNITY_NEW(MessageHandler,kMemNewDelete);
+ s_RegisteredMessageForwarders = UNITY_NEW(MessageForwarders,kMemNewDelete);
+ GetMessageHandler ().InitializeMessageIdentifiers ();
+}
+
+void GameObject::InitializeClass ()
+{
+ GameObjectManager::StaticInitialize();
+}
+
+void GameObject::CleanupClass ()
+{
+ GameObjectManager::StaticDestroy();
+ UNITY_DELETE(s_MessageHandler,kMemNewDelete);
+ UNITY_DELETE(s_RegisteredMessageForwarders,kMemNewDelete);
+}
+
+bool CheckMessageDataType (int messageIdentifier, MessageData& data)
+{
+ return GameObject::GetMessageHandler ().MessageIDToParameter (messageIdentifier) == data.type;
+}
+
+void GameObject::SendMessageAny (const MessageIdentifier& messageIdentifier, MessageData& messageData)
+{
+ int messageID = messageIdentifier.messageID;
+ AssertIf (messageIdentifier.messageID == -1);
+ #if DEBUGMODE
+ if (!CheckMessageDataType (messageID, messageData))
+ AssertString ("The messageData sent has an incorrect type.");
+ #endif
+
+ for (int i=0;i<m_Component.size ();i++)
+ {
+ int classID = m_Component[i].first;
+ if (s_MessageHandler->HasMessageCallback (classID, messageID))
+ {
+ Component& component = *m_Component[i].second;
+ s_MessageHandler->HandleMessage (&component, classID, messageID, messageData);
+ }
+ }
+}
+
+bool GameObject::WillHandleMessage (const MessageIdentifier& messageIdentifier)
+{
+ int messageID = messageIdentifier.messageID;
+ AssertIf (messageIdentifier.messageID == -1);
+
+ for (Container::iterator i=m_Component.begin ();i != m_Component.end ();i++)
+ {
+ int classID = i->first;
+ if (s_MessageHandler->HasMessageCallback (classID, messageID))
+ {
+ Component& component = *i->second;
+ if (s_MessageHandler->WillHandleMessage (&component, classID, messageID))
+ return true;
+ }
+ }
+ return false;
+}
+
+void GameObject::TransformParentHasChanged ()
+{
+ // Reactivate transform hieararchy, but only if it has been activated before,
+ // otherwise we change activation order.
+ if (m_IsActiveCached != -1)
+ ActivateAwakeRecursively ();
+}
+
+void SendMessageDirect (Object& target, const MessageIdentifier& messageIdentifier, MessageData& messageData)
+{
+ int classID = target.GetClassID();
+ if (GameObject::GetMessageHandler ().HasMessageCallback (classID, messageIdentifier.messageID))
+ {
+ GameObject::GetMessageHandler ().HandleMessage (&target, classID, messageIdentifier.messageID, messageData);
+ }
+}
+
+MessageHandler& GameObject::GetMessageHandler ()
+{
+ Assert(s_MessageHandler);
+ return *s_MessageHandler;
+}
+
+
+char const* Component::GetName () const
+{
+ if (m_GameObject)
+ return m_GameObject->m_Name.c_str();
+ else
+ return GetClassName().c_str();
+}
+
+void Component::SetName (char const* name)
+{
+ if (m_GameObject)
+ m_GameObject->SetName (name);
+}
+
+Component::Component (MemLabelId label, ObjectCreationMode mode) : Super(label, mode)
+{
+ m_GameObject = NULL;
+}
+
+Component::~Component ()
+{
+}
+
+void Component::SendMessageAny (const MessageIdentifier& messageID, MessageData& messageData)
+{
+ GameObject* go = GetGameObjectPtr ();
+ if (go)
+ go->SendMessageAny (messageID, messageData);
+}
+
+void Component::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad(awakeMode);
+ #if SUPPORT_LOG_ORDER_TRACE
+ if (IsActive() && RunningReproduction())
+ {
+ if (SUPPORT_LOG_ORDER_TRACE == 2)
+ {
+ LogString(Format("AwakeFromLoad %s (%s) [%d]", GetName(), GetClassName().c_str(), GetInstanceID()));
+ }
+ else
+ {
+ LogString(Format("AwakeFromLoad %s (%s)", GetName(), GetClassName().c_str()));
+ }
+ }
+ #endif
+
+ // Force load the game object. This is in order to prevent ImmediatePtrs not being dereferenced after loading.
+ // Which can cause a crash in Resources.UnloadUnusedAssets()
+ // Resources.Load used to store incorrect preload data which made this trigger.
+ GameObject* dereferenceGameObject = m_GameObject;
+ UNUSED(dereferenceGameObject);
+}
+
+template<class TransferFunction>
+void Component::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ if (SerializePrefabIgnoreProperties(transfer))
+ transfer.Transfer (m_GameObject, "m_GameObject", kHideInEditorMask | kStrongPPtrMask | kIgnoreWithInspectorUndoMask);
+}
+
+void Component::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+ GameObject* go = GetGameObjectPtr ();
+ if (go)
+ {
+ for (int i = 0; i < go->GetComponentCount(); i++)
+ {
+ if (&go->GetComponentAtIndex(i) == this)
+ return;
+ }
+
+ ErrorStringObject (Format("CheckConsistency: GameObject does not reference component %s. Fixing.", GetClassName().c_str()), go);
+ go->AddComponentInternal (this);
+ }
+
+ // MonoBehaviours are allowed to exists without a game object
+ if (IsDerivedFrom(ClassID(Behaviour)))
+ {
+ return;
+ }
+
+ #if UNITY_EDITOR
+ if (m_GameObject == NULL)
+ {
+ GetCleanupManager ().MarkForDeletion (this, "GameObject pointer is invalid");
+ }
+ #endif
+}
+
+GameObjectManager* GameObjectManager::s_Instance = NULL;
+void GameObjectManager::StaticInitialize()
+{
+ Assert(GameObjectManager::s_Instance == NULL);
+ GameObjectManager::s_Instance = UNITY_NEW(GameObjectManager,kMemBaseObject);
+}
+
+void GameObjectManager::StaticDestroy()
+{
+ Assert(GameObjectManager::s_Instance);
+ UNITY_DELETE(GameObjectManager::s_Instance,kMemBaseObject);
+}
+
+GameObjectManager& GetGameObjectManager()
+{
+ Assert(GameObjectManager::s_Instance);
+ return *GameObjectManager::s_Instance;
+}
+
+IMPLEMENT_OBJECT_SERIALIZE (GameObject)
+IMPLEMENT_OBJECT_SERIALIZE (Component)
+
+IMPLEMENT_CLASS_HAS_INIT (GameObject)
+IMPLEMENT_CLASS (Component)
+
+INSTANTIATE_TEMPLATE_TRANSFER_EXPORTED(GameObject)
+INSTANTIATE_TEMPLATE_TRANSFER_EXPORTED(Component)
+
+}
+
+// Hack to make register class work with name spaces. Optimally IMPLEMENT_CLASS / IMPLEMENT_OBJECT_SERIALIZE
+// could be moved out of the namespace but that gives compile errors on gcc
+void RegisterClass_Component () { Unity::RegisterClass_Component(); }
+void RegisterClass_GameObject () { Unity::RegisterClass_GameObject(); }
diff --git a/Runtime/BaseClasses/GameObject.h b/Runtime/BaseClasses/GameObject.h
new file mode 100644
index 0000000..ca4d376
--- /dev/null
+++ b/Runtime/BaseClasses/GameObject.h
@@ -0,0 +1,535 @@
+#ifndef GAMEOBJECT_H
+#define GAMEOBJECT_H
+
+#include <vector>
+#include "EditorExtension.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "MessageIdentifier.h"
+#include "MessageHandler.h"
+#include "BitField.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Containers/ConstantString.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/Utility/StaticEditorFlags.h"
+#endif
+
+class GameManager;
+class MessageHandler;
+class Texture2D;
+class AwakeFromLoadQueue;
+typedef UNITY_SET(kMemTempAlloc, Object*) TempSelectionSet;
+
+namespace Unity
+{
+
+/// A GameObject is basically a bag of GOComponents.
+/// GOComponents are added and removed at runtime.
+/// This allows GameObject to be composed of an arbitrary amount of GOComponents.
+
+/// You can query for any GOCOmponents using
+/// QueryComponent and GetComponent
+/// This will Ask every of the component in the GO if it is derived from the wanted class
+/// eg. from inside a Component you can query for a Transform:
+/// Transform* t = QueryComponent (Transform);
+/// If there is no Transform inside the GameObject, NULL is returned.
+/// The difference between QueryComponent and GetComponent is that
+/// QueryComponent returns a ptr, GetComponent returns a reference
+/// but asserts if no matching component could be found
+/// Also you are not allowed to Query for Component classes
+/// that are in the GameObject more than once.
+
+/// Querys for a component class, aksing every component if it is derived from wanted class
+#define QueryComponent(x) GetGameObject ().QueryComponentT<x> (ClassID (x))
+/// Same as above only that it returns a reference and asserts if no component derived from x can be found
+#define GetComponent(x) GetGameObject ().GetComponentT<x> (ClassID (x))
+
+/// Also GameObjects support messaging.
+/// MessageIdentifier kTransformChanged ("TransformChanged");
+/// MessageIdentifier kTestMessage ("Test", ClassID (float));
+/// In order to receive a message
+/// Register the notification inside the InitializeClass of the class
+/// Renderer::InitializeClass ()
+/// {
+/// REGISTER_NOTIFICATION_VOID (Renderer, kTransformChanged, TransformChanged);
+/// REGISTER_NOTIFICATION (Renderer, kTestMessage, TestMessage, float);
+/// }
+/// bool Renderer::TransformChanged () { ... }
+/// bool Renderer::TestMessage (float f) { ... }
+
+/// In order to send a message use:
+/// SendMessage (kTransformChanged);
+/// SendMessage (kTestMessage, 0.1f, ClassID (float));
+
+class Component;
+class GameObject;
+
+
+enum DeactivateOperation
+{
+ kNormalDeactivate = 0,
+ // Deactivate was called
+ kDeprecatedDeactivateToggleForLevelLoad = 1,
+
+ // Deactivate was called because the component will be destroyed
+ kWillDestroySingleComponentDeactivate = 2,
+ // Deactivate was called because the entire game object will be destroyed
+ kWillDestroyGameObjectDeactivate = 3
+};
+
+class EXPORT_COREMODULE GameObject : public EditorExtension
+{
+ public:
+
+ typedef std::pair<SInt32, ImmediatePtr<Component> > ComponentPair;
+ typedef UNITY_VECTOR(kMemBaseObject, ComponentPair) Container;
+
+ GameObject (MemLabelId label, ObjectCreationMode mode);
+ // ~GameObject (); declared-by-macro
+
+ REGISTER_DERIVED_CLASS (GameObject, EditorExtension)
+ DECLARE_OBJECT_SERIALIZE (GameObject)
+
+ /// An GameObject can either be active or inactive (Template GameObjects are always inactive)
+ /// If an GameObject is active/inactive all its components have the same state as well.
+ /// (Components that are not added to a gameobject are always inactive)
+ /// Querying and messaging still works for inactive GameObjects and Components
+ void Activate ();
+
+ /// Deactiates the game object and thus all it's components.
+ void Deactivate (DeactivateOperation operation = kNormalDeactivate);
+
+ bool IsActiveIgnoreImplicitPrefab ();
+ bool IsActive () const;
+ bool IsSelfActive () const { return m_IsActive; }
+ void SetSelfActive (bool state);
+
+ /// Set the GameObject Layer.
+ /// This is used for collisions and messaging
+ void SetLayer (int layerIndex);
+ int GetLayer () const { return m_Layer; }
+ UInt32 GetLayerMask () const { return 1 << m_Layer; }
+
+ /// Set the Tag of the gameobject
+ UInt32 GetTag () const { return m_Tag; }
+ void SetTag (UInt32 tag);
+
+ // Adds a new Component to the GameObject.
+ // Using the PersistentObject interface so that Components,
+ // which are not loaded at the moment can be added.
+ // Use GameObjectUtility instead, you must invoke specific callbacks etc.
+ void AddComponentInternal (Component* component);
+
+ // Removes a Component from the GameObject.
+ void RemoveComponentAtIndex (int index);
+
+ int GetComponentCount () const { return m_Component.size (); }
+ Component& GetComponentAtIndex (int i) const;
+ Component* GetComponentPtrAtIndex (int i) const;
+ bool GetComponentAtIndexIsLoaded (int i) const;
+ int GetComponentClassIDAtIndex (int i) const;
+ int CountDerivedComponents (int compareClassID)const;
+
+ /// Checks if GameObject has any components conflicting with the specified classID.
+ bool HasConflictingComponents (int classID) const { return FindConflictingComponentPtr (classID) != NULL; }
+
+ /// Find the first conflicting component classID for the specified classID.
+ Component* FindConflictingComponentPtr (int classID) const;
+
+ /// Swap two components in the vector.
+ void SwapComponents (int index1, int index2);
+
+ /// Get the index of a component.
+ int GetComponentIndex (Component *component);
+
+ /// Send a message identified by messageName to all components if they can handle it
+ void SendMessageAny (const MessageIdentifier& messageID, MessageData& messageData);
+
+ /// Send a message identified by messageName to all components if they can handle it
+ template<class T>
+ void SendMessage (const MessageIdentifier& messageID, T messageData, int classId);
+
+ /// Will this message be handled by any component in the gameobject?
+ bool WillHandleMessage (const MessageIdentifier& messageID);
+
+ // Use the QueryComponent macro
+ // Gives back a component by its classID.
+ // If the GameObject doesnt have such a Component, NULL is returned.
+ template<class T>
+ T* QueryComponentT (int inClassID) const;
+
+ // Use the GetComponent macro
+ // Gives back a component by its classID.
+ // If the GameObject doesnt have such a Component, the function will assert
+ template<class T>
+ T& GetComponentT (int inClassID) const;
+
+ // Use the GetComponent macro
+ // Gives back a component by its classID.
+ // If the GameObject doesnt have such a Component, the function will assert
+ template<class T>
+ T& GetComponentExactTypeT (int inClassID) const;
+
+ const GameObject& GetGameObject ()const { return *this; }
+ GameObject& GetGameObject () { return *this; }
+
+ virtual char const* GetName () const { return m_Name.c_str(); }
+ virtual void SetName (char const* name);
+
+ // Deprecated
+ void SetActiveRecursivelyDeprecated (bool state);
+ bool GetIsStaticDeprecated ();
+ void SetIsStaticDeprecated (bool s);
+
+ #if UNITY_EDITOR
+ bool AreStaticEditorFlagsSet (StaticEditorFlags flags) const;
+ StaticEditorFlags GetStaticEditorFlags () const;
+ void SetStaticEditorFlags (StaticEditorFlags flags);
+ #endif
+
+ //@TODO: When we rewrite static batching in C++ fix this up
+ bool IsStaticBatchable () const;
+
+ // Callback functions
+ typedef void DestroyGOCallbackFunction (GameObject* go);
+ static void RegisterDestroyedCallback (DestroyGOCallbackFunction* callback);
+ static void InvokeDestroyedCallback (GameObject* go);
+
+ typedef void SetGONameFunction (GameObject* go);
+ static void RegisterSetGONameCallback (SetGONameFunction* callback);
+
+ /// Registers an message callback. Used by the REGISTER_NOTIFICATION macros
+ typedef void (*MessagePtr)(void* receiver, int messageIndex, MessageData& data);
+ typedef bool (*CanHandleMessagePtr)(void* receiver, int messageIndex, MessageData& data);
+ static void RegisterMessageHandler (int classID, const MessageIdentifier& messageIdentifier, MessagePtr functor, int typeId);
+ static void RegisterAllMessagesHandler (int classID, MessagePtr message, CanHandleMessagePtr canHandleNotification);
+
+ virtual void Reset ();
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ // Initializes the message system
+ static void InitializeMessageHandlers ();
+ static void InitializeMessageIdentifiers ();
+ // Returns the message handler
+ static class MessageHandler& GetMessageHandler ();
+
+ // Internally used during object destruction to prevent double deletion etc.
+ bool IsDestroying () const { return m_IsDestroying; }
+ bool IsActivating () const { return m_IsActivating; }
+
+ void WillDestroyGameObject ();
+
+
+ inline UInt32 GetSupportedMessages ();
+ void SetSupportedMessagesDirty ();
+
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ virtual void SetHideFlags (int flags);
+
+ virtual void CheckConsistency ();
+
+
+ static void AddComponentInternal (GameObject& gameObject, Component& clone);
+ static void RemoveComponentFromGameObjectInternal (Component& clone);
+
+ #if UNITY_EDITOR
+ enum
+ {
+ kNotVisible = 0,
+ kSelfVisible = 1,
+ kVisibleAsChild = 2,
+ };
+ int IsMarkedVisible () const { return m_IsMarkedVisible; }
+ void SetMarkedVisible (int marked) { m_IsMarkedVisible = marked; }
+ void SetIcon (PPtr<Texture2D> icon);
+
+ // Get the custom icon for this gameobject. Does not scan components for their icons.
+ PPtr<Texture2D> GetIcon () const;
+
+ UInt32 GetNavMeshLayer () const { return m_NavMeshLayer; }
+ void SetNavMeshLayer (UInt32 layer) { m_NavMeshLayer = layer; SetDirty(); }
+ #endif
+
+ // Internal functions that you should never call unless you really understand all side effects.
+ void SetActiveBitInternal (bool value) { m_IsActive = value; }
+
+ void SetComponentAtIndexInternal (PPtr<Component> component, int index);
+
+ void UpdateActiveGONode();
+
+ void TransformParentHasChanged ();
+
+ Container& GetComponentContainerInternal () { return m_Component; }
+
+ void ActivateAwakeRecursively (DeactivateOperation deactivateOperation = kNormalDeactivate);
+ void ActivateAwakeRecursivelyInternal (DeactivateOperation deactivateOperation, AwakeFromLoadQueue &queue);
+
+ Component* QueryComponentImplementation (int classID) const;
+ Component* QueryComponentExactTypeImplementation (int classID) const;
+
+private:
+ void GetSupportedMessagesRecalculate ();
+
+ void MarkActiveRecursively (bool state);
+
+ template <class TransferFunction>
+ void TransferComponents(TransferFunction& transfer);
+
+ Container m_Component;
+
+ UInt32 m_Layer;
+ UInt16 m_Tag;
+ bool m_IsActive;
+ mutable SInt8 m_IsActiveCached;
+ UInt8 m_IsDestroying; //// OPTIMIZE THIS INTO A COMMON BITMASK!
+ UInt8 m_IsActivating;
+
+ UInt32 m_SupportedMessages;
+
+ ConstantString m_Name;
+
+ #if UNITY_EDITOR
+ UInt32 m_StaticEditorFlags;
+ UnityStr m_TagString;
+ int m_IsMarkedVisible;
+ PPtr<Texture2D> m_Icon;
+ UInt32 m_NavMeshLayer;
+ bool m_IsOldVersion;
+ #endif
+
+ ListNode<GameObject> m_ActiveGONode;
+
+ static DestroyGOCallbackFunction* s_GameObjectDestroyedCallback;
+ static SetGONameFunction* s_SetGONameCallback;
+ static MessageForwarders* s_RegisteredMessageForwarders;
+ static MessageHandler* s_MessageHandler;
+
+ friend class Component;
+};
+
+class EXPORT_COREMODULE Component : public EditorExtension
+{
+ private:
+
+ ImmediatePtr<GameObject> m_GameObject;
+
+ public:
+
+
+ DECLARE_OBJECT_SERIALIZE (Component)
+ REGISTER_DERIVED_CLASS (Component, EditorExtension)
+
+ Component (MemLabelId label, ObjectCreationMode mode);
+ // ~Component (); declared-by-macro
+
+ // Returns a reference to the GameObject holding this component
+ GameObject& GetGameObject () { return *m_GameObject; }
+ const GameObject& GetGameObject () const { return *m_GameObject; }
+ GameObject* GetGameObjectPtr () { return m_GameObject; }
+ GameObject* GetGameObjectPtr () const { return m_GameObject; }
+
+ /// Send a message identified by messageName to every components of the gameobject
+ /// that can handle it
+ void SendMessageAny (const MessageIdentifier& messageID, MessageData& messageData);
+
+ template<class T>
+ void SendMessage (const MessageIdentifier& messageID, T messageData, int classId);
+ void SendMessage (const MessageIdentifier& messageID);
+
+ /// Is this component active?
+ /// A component is always inactive if its not attached to a gameobject
+ /// A component is always inactive if its gameobject is inactive
+ /// If its a datatemplate, the gameobject and its components are always set to be inactive
+ bool IsActive () const;
+
+ virtual char const* GetName () const;
+ virtual void SetName (char const* name);
+
+ virtual UInt32 CalculateSupportedMessages () { return 0; }
+ virtual void SupportedMessagesDidChange (int /*newMask*/) { }
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ // Invoke any callbacks prior to component destruction.
+ virtual void WillDestroyComponent () { }
+
+ /// Deactivate will be called just before the Component is going to be removed from a GameObject
+ /// It can still communicate with other components at this point.
+ /// Deactivate will only be called when the component is remove from the GameObject,
+ /// not if the object is persistet to disk and removed from memory
+ /// Deactivate will only be called if the GameObject the Component is being removed from is active
+ /// YOU CAN NOT RELY ON IsActive returning false inside Deactivate
+ virtual void Deactivate (DeactivateOperation /*operation*/) { }
+
+ virtual void CheckConsistency ();
+
+ #if UNITY_EDITOR
+ // Some components always go together, e.g. when removing one you have
+ // to remove other. Example, ParticleSystem and ParticleSystemRenderer.
+ // Override and return class ID of that "dependent" component.
+ virtual int GetCoupledComponentClassID() const { return -1; }
+ #endif
+
+ public:
+
+ int GetGameObjectInstanceID () const { return m_GameObject.GetInstanceID(); }
+
+ /// SetGameObject is called whenever the GameObject of a component changes.
+ void SetGameObjectInternal (const GameObject* go) { m_GameObject = go; }
+
+ friend class GameObject;
+};
+
+typedef List< ListNode<GameObject> > GameObjectList;
+
+// A fast lookup for all tagged and active game objects
+class GameObjectManager
+{
+public:
+ static void StaticInitialize();
+ static void StaticDestroy();
+ // Nodes that are tagged and active
+ GameObjectList m_TaggedNodes;
+ // Nodes that are just active
+ // (If you want to get all active nodes you need to go through tagged and active nodes)
+ GameObjectList m_ActiveNodes;
+
+ static GameObjectManager* s_Instance;
+};
+GameObjectManager& GetGameObjectManager();
+
+
+template<class T> inline
+T* GameObject::QueryComponentT (int compareClassID) const
+{
+ Component* com;
+ if (T::IsSealedClass())
+ com = QueryComponentExactTypeImplementation(compareClassID);
+ else
+ com = QueryComponentImplementation (compareClassID);
+ DebugAssertIf (com != dynamic_pptr_cast<Component*> (com));
+ return static_cast<T*> (com);
+}
+
+template<class T> inline
+T& GameObject::GetComponentT (int compareClassID) const
+{
+ Component* com;
+ if (T::IsSealedClass())
+ com = QueryComponentExactTypeImplementation(compareClassID);
+ else
+ com = QueryComponentImplementation (compareClassID);
+ DebugAssertIf (dynamic_pptr_cast<T*> (com) == NULL);
+ return *static_cast<T*> (com);
+}
+
+inline Component& GameObject::GetComponentAtIndex (int i)const
+{
+ return *m_Component[i].second;
+}
+
+inline bool GameObject::GetComponentAtIndexIsLoaded (int i)const
+{
+ return m_Component[i].second.IsLoaded();
+}
+
+inline int GameObject::GetComponentClassIDAtIndex (int i) const
+{
+ return m_Component[i].first;
+}
+
+inline bool Component::IsActive () const
+{
+ GameObject* go = m_GameObject;
+ return go != NULL && go->IsActive ();
+}
+
+#define IMPLEMENT_SENDMESSAGE_FOR_CLASS(ClassName) \
+ template<class T> inline \
+ void ClassName::SendMessage (const MessageIdentifier& messageID, \
+ T messageData, int classId) \
+ { \
+ MessageData data; \
+ data.SetData (messageData, classId); \
+ SendMessageAny (messageID, data); \
+ }
+
+IMPLEMENT_SENDMESSAGE_FOR_CLASS (Component)
+IMPLEMENT_SENDMESSAGE_FOR_CLASS (GameObject)
+
+inline UInt32 GameObject::GetSupportedMessages ()
+{
+ return m_SupportedMessages;
+}
+
+inline void Component::SendMessage (const MessageIdentifier& messageID)
+{
+ MessageData data;
+ SendMessageAny (messageID, data);
+}
+
+void SendMessageDirect (Object& target, const MessageIdentifier& messageIdentifier, MessageData& messageData);
+
+// Compares the MessageData's type with the method signatures expected parameter type
+bool EXPORT_COREMODULE CheckMessageDataType (int messageIdentifier, MessageData& data);
+
+/// Creates a wrapper that calls a function with no parameter
+#define REGISTER_MESSAGE_VOID(ClassType,NotificationName,Function) \
+struct FunctorImpl_##ClassType##_##NotificationName { \
+ static void Call (void* object, int, MessageData&) { \
+ ClassType* castedObject = reinterpret_cast<ClassType*> (object); \
+ castedObject->Function (); \
+ } \
+}; \
+GameObject::RegisterMessageHandler (ClassID (ClassType), NotificationName, \
+ FunctorImpl_##ClassType##_##NotificationName::Call, 0)
+
+#if DEBUGMODE
+#define CHECK_MSG_DATA_TYPE \
+if (!CheckMessageDataType (messageID, messageData)) \
+ DebugStringToFile ("Check message data", 0, __FILE__, 0, kAssert);
+#else
+#define CHECK_MSG_DATA_TYPE
+#endif
+
+/// Creates a wrapper thats sends the specified DataType to the member functions
+#define REGISTER_MESSAGE(ClassType,NotificationName,Function,DataType) \
+struct FunctorImpl_##ClassType##_##NotificationName { \
+ static void Call (void* object, int messageID, MessageData& messageData) { \
+ CHECK_MSG_DATA_TYPE \
+ DataType data = messageData.GetData<DataType> (); \
+ ClassType* castedObject = reinterpret_cast<ClassType*> (object); \
+ castedObject->Function (data); \
+ } \
+}; \
+GameObject::RegisterMessageHandler (ClassID (ClassType), \
+ NotificationName, \
+ FunctorImpl_##ClassType##_##NotificationName::Call, \
+ ClassID (DataType))
+
+
+/// Creates a wrapper thats sends the specified DataType to the member functions
+#define REGISTER_MESSAGE_PTR(ClassType,NotificationName,Function,DataType) \
+struct FunctorImpl_##ClassType##_##NotificationName { \
+ static void Call (void* object, int messageID, MessageData& messageData) { \
+ CHECK_MSG_DATA_TYPE \
+ DataType* data = messageData.GetData<DataType*> (); \
+ ClassType* castedObject = reinterpret_cast<ClassType*> (object); \
+ castedObject->Function (data); \
+ } \
+}; \
+GameObject::RegisterMessageHandler (ClassID (ClassType), \
+ NotificationName, \
+ FunctorImpl_##ClassType##_##NotificationName::Call, \
+ ClassID (DataType))
+
+
+}
+
+using namespace Unity;
+
+#endif
+
diff --git a/Runtime/BaseClasses/GameObjectTests.cpp b/Runtime/BaseClasses/GameObjectTests.cpp
new file mode 100644
index 0000000..9c0362f
--- /dev/null
+++ b/Runtime/BaseClasses/GameObjectTests.cpp
@@ -0,0 +1,274 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/BaseClasses/SupportedMessageOptimization.h"
+#include "Runtime/Testing/Testing.h"
+
+class GameObjectFixture
+{
+protected:
+ GameObject* NewGameObject()
+ {
+ return NEW_OBJECT_RESET_AND_AWAKE(GameObject);
+ }
+
+ Unity::Component* NewComponent()
+ {
+ return NEW_OBJECT_RESET_AND_AWAKE(Unity::Component);
+ }
+};
+
+SUITE (GameObjectTests)
+{
+ TEST_FIXTURE (GameObjectFixture, AddandRemoveComponentTest)
+ {
+ GameObject* go = NewGameObject();
+
+ Unity::Component* component = NewComponent();
+ go->AddComponentInternal(component);
+ CHECK_EQUAL (go->GetComponentCount(), 1);
+
+ // Delete by RemoveComponentFromGameObjectInternal().
+ go->RemoveComponentFromGameObjectInternal(*component);
+ CHECK_EQUAL (go->GetComponentCount(), 0);
+
+ go->AddComponentInternal(component);
+ CHECK_EQUAL (go->GetComponentCount(), 1);
+
+ // Delete by RemoveComponentAtIndex().
+ go->RemoveComponentAtIndex(0);
+ CHECK_EQUAL (go->GetComponentCount(), 0);
+
+ go->AddComponentInternal(component);
+ go->AwakeFromLoad (kDefaultAwakeFromLoad);
+ DestroyObjectHighLevel(go);
+ }
+
+ TEST_FIXTURE (GameObjectFixture, HideFlagTest)
+ {
+ GameObject* go = NewGameObject();
+
+ Unity::Component* component = NewComponent();
+ go->AddComponentInternal(component);
+ CHECK_EQUAL (go->GetComponentCount(), 1);
+
+ int hideFlag = 2;
+ go->SetHideFlags(hideFlag);
+
+ CHECK_EQUAL (go->GetHideFlags(), hideFlag);
+
+ for(int i = 0; i < go->GetComponentCount() ; i++)
+ {
+ CHECK_EQUAL (go->GetComponentAtIndex(i).GetHideFlags(), hideFlag);
+ }
+
+ // Add another component, it should have the same hide flag as the game object.
+ Unity::Component* component1 = NewComponent();
+ go->AddComponentInternal(component1);
+ CHECK_EQUAL (go->GetComponentCount(), 2);
+
+ for(int i = 0; i < go->GetComponentCount() ; i++)
+ {
+ CHECK_EQUAL (go->GetComponentAtIndex(i).GetHideFlags(), hideFlag);
+ }
+
+ DestroyObjectHighLevel(go);
+ }
+
+ TEST_FIXTURE (GameObjectFixture, NameTest)
+ {
+ GameObject* go = NewGameObject();
+
+ AddComponents(*go, "Transform", "MeshRenderer", "MeshFilter", NULL);
+
+ const char* name = "Test";
+ go->SetName(name);
+
+ CHECK_EQUAL (go->GetName(), name);
+
+ for(int i = 0; i < go->GetComponentCount() ; i++)
+ {
+ CHECK_EQUAL (go->GetComponentAtIndex(i).GetName(), name);
+ }
+
+ // Set invalid value.
+ // We will not test NULL as it's rejected by UI.
+ go->SetName("");
+
+ CHECK_EQUAL (go->GetName(), "");
+
+ for(int i = 0; i < go->GetComponentCount() ; i++)
+ {
+ CHECK_EQUAL (go->GetComponentAtIndex(i).GetName(), "");
+ }
+
+ DestroyObjectHighLevel(go);
+ }
+
+ TEST_FIXTURE (GameObjectFixture, QueryComponentTest)
+ {
+ GameObject* go = NewGameObject();
+
+ AddComponents(*go, "Transform", "MeshRenderer", "MeshFilter", NULL);
+
+ // Go for QueryComponentExactTypeImplementation().
+ Transform* transform = go->QueryComponentT<Transform>(ClassID(Transform));
+ CHECK (transform != NULL);
+
+ // Go for QueryComponentImplementation().
+ Unity::Component* component = go->QueryComponentT<Unity::Component>(ClassID(Component));
+ CHECK (component != NULL);
+
+ DestroyObjectHighLevel(go);
+ }
+
+ TEST_FIXTURE (GameObjectFixture, SwapComponentTest)
+ {
+ GameObject* go = NewGameObject();
+
+ AddComponents(*go, "Transform", "MeshRenderer", "MeshFilter", NULL);
+
+ Unity::Component* component = go->GetComponentPtrAtIndex(0);
+ go->SwapComponents(0, 1);
+
+ CHECK_EQUAL (go->GetComponentIndex(component), 1);
+
+ DestroyObjectHighLevel(go);
+ }
+}
+
+SUITE (ComponentTests)
+{
+ TEST (GameObjectTest)
+ {
+ GameObject& go = CreateGameObject ("TestGameObject", "Transform", "MeshRenderer", NULL);
+
+ Unity::Component& component = go.GetComponentAtIndex(0);
+
+ CHECK(component.GetGameObjectPtr() == &go);
+ }
+
+ TEST_FIXTURE (GameObjectFixture, ActiveTest)
+ {
+ GameObject* go = NewGameObject();
+ go->Activate();
+
+ Unity::Component* component = NewComponent();
+ CHECK (!component->IsActive());
+
+ go->AddComponentInternal(component);
+ CHECK (component->IsActive());
+ }
+
+ TEST_FIXTURE (GameObjectFixture, NameTest)
+ {
+ GameObject* go = NewGameObject();
+
+ Unity::Component* component = NewComponent();
+ CHECK_EQUAL (component->GetName(), component->GetClassName());
+
+ go->AddComponentInternal(component);
+ CHECK_EQUAL (component->GetName(), go->GetName());
+
+ const char* name = "TestComponent";
+ component->SetName(name);
+
+ CHECK_EQUAL(go->GetName(), name);
+ }
+
+ TEST_FIXTURE (GameObjectFixture, CheckConsistencyTest)
+ {
+ GameObject* go = NewGameObject();
+
+ Unity::Component* component = NewComponent();
+ component->SetGameObjectInternal(go);
+
+ CHECK_EQUAL(go->GetComponentCount(), 0);
+
+ EXPECT (Error, "GameObject does not reference component");
+ component->CheckConsistency();
+
+ CHECK_EQUAL(go->GetComponentCount(), 1);
+ }
+
+ TEST (GameObjectMessagesCheckTest)
+ {
+#if !UNITY_RELEASE
+ {
+ GameObject& go = CreateGameObject ("test", "Transform", NULL);
+ CHECK_EQUAL(go.GetSupportedMessages(), 0);
+
+ //Add a Tree component
+ Unity::Component *cmp1 = AddComponentUnchecked (go, 193, NULL, NULL);
+ CHECK_EQUAL(go.GetSupportedMessages(), (int) kHasOnWillRenderObject);
+
+ go.Deactivate();
+ CHECK_EQUAL(go.GetSupportedMessages(), (int) kHasOnWillRenderObject);
+
+ //Add a NavMeshObstablce component
+ Unity::Component *cmp2 = AddComponentUnchecked (go, 208, NULL, NULL);
+ CHECK_EQUAL(go.GetSupportedMessages(), (kHasOnWillRenderObject | kSupportsVelocityChanged | kSupportsTransformChanged));
+ go.Activate();
+ CHECK_EQUAL(go.GetSupportedMessages(), (kHasOnWillRenderObject | kSupportsVelocityChanged | kSupportsTransformChanged));
+ DestroyObjectHighLevel(cmp1);
+ CHECK_EQUAL(go.GetSupportedMessages(), (kSupportsVelocityChanged | kSupportsTransformChanged));
+ go.Deactivate();
+ DestroyObjectHighLevel(cmp2);
+
+ CHECK_EQUAL(go.GetSupportedMessages(), 0);
+ DestroyObjectHighLevel(&go);
+ }
+#endif
+ }
+
+ TEST (AwakeFromLoadCheckTest)
+ {
+ // tests to check if checks of AwakeFromLoad behavior are working
+ // uncomment them when in doubt
+ #if !UNITY_RELEASE
+ // 1. simple object creation
+ /*
+ {
+ GameObject* obj = NEW_OBJECT(GameObject);
+ obj->CheckCorrectAwakeUsage();
+ obj->CheckCorrectAwakeUsage();
+ }
+ */
+
+ // 2. enforce awake after reset
+ /*
+ {
+ GameObject* obj = NEW_OBJECT(GameObject);
+ obj->AwakeFromLoad(kDefaultAwakeFromLoad);
+ obj->CheckCorrectAwakeUsage();
+ obj->CheckCorrectAwakeUsage();
+ obj->Reset();
+ obj->CheckCorrectAwakeUsage();
+ obj->CheckCorrectAwakeUsage();
+ }
+ */
+
+ // 3. check hacks are working
+ {
+ GameObject* obj = NEW_OBJECT(GameObject);
+ obj->Reset();
+ obj->HackSetAwakeWasCalled();
+ obj->CheckCorrectAwakeUsage();
+ obj->CheckCorrectAwakeUsage();
+ DestroyObjectHighLevel(obj);
+ }
+
+ {
+ GameObject* obj = NEW_OBJECT(GameObject);
+ obj->Reset();
+ obj->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+ obj->CheckCorrectAwakeUsage();
+ DestroyObjectHighLevel(obj);
+ }
+#endif
+ }
+}
+
+#endif
diff --git a/Runtime/BaseClasses/IsPlaying.cpp b/Runtime/BaseClasses/IsPlaying.cpp
new file mode 100644
index 0000000..184e830
--- /dev/null
+++ b/Runtime/BaseClasses/IsPlaying.cpp
@@ -0,0 +1,14 @@
+#include "UnityPrefix.h"
+#include "IsPlaying.h"
+
+#if GAMERELEASE
+static bool gIsWorldPlaying = true;
+#else
+static bool gIsWorldPlaying = false;
+#endif
+
+bool IsWorldPlaying () { return gIsWorldPlaying; }
+void SetIsWorldPlaying (bool isPlaying)
+{
+ gIsWorldPlaying = isPlaying;
+}
diff --git a/Runtime/BaseClasses/IsPlaying.h b/Runtime/BaseClasses/IsPlaying.h
new file mode 100644
index 0000000..740ffb8
--- /dev/null
+++ b/Runtime/BaseClasses/IsPlaying.h
@@ -0,0 +1,9 @@
+#ifndef ISPLAYING_H
+#define ISPLAYING_H
+
+/// Is the world playmode? (Otherwise we are in editor mode)
+bool EXPORT_COREMODULE IsWorldPlaying ();
+
+void SetIsWorldPlaying (bool isPlaying);
+
+#endif
diff --git a/Runtime/BaseClasses/ManagerContext.cpp b/Runtime/BaseClasses/ManagerContext.cpp
new file mode 100644
index 0000000..1824a84
--- /dev/null
+++ b/Runtime/BaseClasses/ManagerContext.cpp
@@ -0,0 +1,107 @@
+#include "UnityPrefix.h"
+#include "ManagerContext.h"
+#include "BaseObject.h"
+#include "Configuration/UnityConfigure.h"
+
+ManagerContext::ManagerContext ()
+{
+ for (int i=0;i<kManagerCount;i++)
+ m_Managers[i] = NULL;
+}
+
+void ManagerContext::InitializeClasses ()
+{
+ for (int i=0;i<kManagerCount;i++)
+ {
+ m_ManagerClassIDs[i] = -1;
+ #if DEBUGMODE
+ m_ManagerNames[i] = "Not initialized";
+ #endif
+ }
+
+#if DEBUGMODE
+ #define INIT_MANAGER_CLASS(x) Assert(m_ManagerClassIDs[k##x] == -1); m_ManagerClassIDs[k##x] = Object::StringToClassID (#x); m_ManagerNames[k##x] = #x;
+#else
+ #define INIT_MANAGER_CLASS(x) m_ManagerClassIDs[k##x] = Object::StringToClassID (#x);
+#endif
+
+ INIT_MANAGER_CLASS (PlayerSettings)
+ INIT_MANAGER_CLASS (InputManager)
+ INIT_MANAGER_CLASS (TagManager)
+ INIT_MANAGER_CLASS (AudioManager)
+ INIT_MANAGER_CLASS (ScriptMapper)
+ INIT_MANAGER_CLASS (MonoManager)
+ INIT_MANAGER_CLASS (GraphicsSettings)
+ INIT_MANAGER_CLASS (TimeManager)
+ INIT_MANAGER_CLASS (DelayedCallManager)
+ INIT_MANAGER_CLASS (PhysicsManager)
+ INIT_MANAGER_CLASS (BuildSettings)
+ INIT_MANAGER_CLASS (QualitySettings)
+ INIT_MANAGER_CLASS (ResourceManager)
+ INIT_MANAGER_CLASS (NetworkManager)
+ INIT_MANAGER_CLASS (MasterServerInterface)
+ INIT_MANAGER_CLASS (NavMeshLayers)
+ #if ENABLE_2D_PHYSICS
+ INIT_MANAGER_CLASS (Physics2DSettings)
+ #endif
+
+ INIT_MANAGER_CLASS (SceneSettings)
+ INIT_MANAGER_CLASS (RenderSettings)
+ INIT_MANAGER_CLASS (HaloManager)
+ INIT_MANAGER_CLASS (LightmapSettings)
+ INIT_MANAGER_CLASS (NavMeshSettings)
+
+#if UNITY_EDITOR
+ for (int i=0;i<kManagerCount;i++)
+ {
+ Assert (m_ManagerClassIDs[i] != -1);
+ }
+
+ std::vector<SInt32> allDerivedClasses;
+ Object::FindAllDerivedClasses(Object::StringToClassID("GameManager"), &allDerivedClasses);
+ if (allDerivedClasses.size() != kManagerCount)
+ {
+ ErrorString("Number of GameManager classes does not match number of game managers registered.");
+ }
+#endif
+
+}
+
+static ManagerContext gContext;
+
+Object& GetManagerFromContext (int index)
+{
+#if DEBUGMODE
+
+ if( index >= ManagerContext::kManagerCount )
+ FatalErrorString( "GetManagerFromContext: index for managers table is out of bounds" );
+
+ if( gContext.m_Managers[index] == NULL )
+ {
+ char const* managerName = gContext.m_ManagerNames[ index ];
+ FatalErrorString( Format("GetManagerFromContext: pointer to object of manager '%s' is NULL (table index %d)", managerName, index) );
+ }
+#endif
+
+ return *gContext.m_Managers[index];
+}
+
+void ManagerContextInitializeClasses()
+{
+ gContext.InitializeClasses();
+}
+
+Object* GetManagerPtrFromContext (int index)
+{
+ return gContext.m_Managers[index];
+}
+
+void SetManagerPtrInContext(int index, Object* ptr)
+{
+ gContext.m_Managers[index] = ptr;
+}
+
+const ManagerContext& GetManagerContext ()
+{
+ return gContext;
+}
diff --git a/Runtime/BaseClasses/ManagerContext.h b/Runtime/BaseClasses/ManagerContext.h
new file mode 100644
index 0000000..af4de7b
--- /dev/null
+++ b/Runtime/BaseClasses/ManagerContext.h
@@ -0,0 +1,61 @@
+#ifndef MANAGERCONTEXT_H
+#define MANAGERCONTEXT_H
+
+class Object;
+struct ManagerContext;
+
+EXPORT_COREMODULE Object& GetManagerFromContext (int index);
+EXPORT_COREMODULE Object* GetManagerPtrFromContext (int index);
+void SetManagerPtrInContext(int index, Object* ptr);
+void ManagerContextInitializeClasses();
+
+#define GET_MANAGER(x) x& Get##x () { return reinterpret_cast<x&> (GetManagerFromContext (ManagerContext::k##x)); }
+#define GET_MANAGER_PTR(x) x* Get##x##Ptr () { return reinterpret_cast<x*> (GetManagerPtrFromContext (ManagerContext::k##x)); }
+
+const ManagerContext& GetManagerContext ();
+
+struct ManagerContext
+{
+ enum Managers
+ {
+ // Global managers
+ kPlayerSettings = 0,
+ kInputManager,
+ kTagManager,
+ kAudioManager,
+ kScriptMapper,
+ kMonoManager,
+ kGraphicsSettings,
+ kTimeManager,
+ kDelayedCallManager,
+ kPhysicsManager,
+ kBuildSettings,
+ kQualitySettings,
+ kResourceManager,
+ kNetworkManager,
+ kMasterServerInterface,
+ kNavMeshLayers,
+ #if ENABLE_2D_PHYSICS
+ kPhysics2DSettings,
+ #endif
+ kGlobalManagerCount,
+
+ // Level managers
+ kSceneSettings = kGlobalManagerCount,
+ kRenderSettings,
+ kHaloManager,
+ kLightmapSettings,
+ kNavMeshSettings,
+ kManagerCount
+ };
+
+ ManagerContext ();
+ Object* m_Managers[kManagerCount];
+ int m_ManagerClassIDs[kManagerCount];
+ #if DEBUGMODE
+ const char* m_ManagerNames[kManagerCount];
+ #endif
+ void InitializeClasses ();
+};
+
+#endif
diff --git a/Runtime/BaseClasses/ManagerContextLoading.cpp b/Runtime/BaseClasses/ManagerContextLoading.cpp
new file mode 100644
index 0000000..76345d3
--- /dev/null
+++ b/Runtime/BaseClasses/ManagerContextLoading.cpp
@@ -0,0 +1,285 @@
+#include "UnityPrefix.h"
+#include "ManagerContextLoading.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Serialize/SerializedFile.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Serialize/AwakeFromLoadQueue.h"
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Utilities/vector_map.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+
+
+PROFILER_INFORMATION(gCollectGameManagers, "CollectGameManagers", kProfilerLoading)
+
+void CollectLevelGameManagers (InstanceIDArray& outputObjects)
+{
+ PROFILER_AUTO(gCollectGameManagers,NULL);
+ const ManagerContext& context = GetManagerContext ();
+ for (int i=0;i<ManagerContext::kManagerCount;i++)
+ {
+ #if !UNITY_EDITOR
+ // In players we might have stripped all managers
+ if (!context.m_Managers[i])
+ continue;
+ #endif
+
+ Object& object = *context.m_Managers[i];
+ if (object.IsDerivedFrom (ClassID (LevelGameManager)))
+ {
+ AssertIf (object.IsPersistent () || object.TestHideFlag (Object::kDontSave));
+ outputObjects.push_back(object.GetInstanceID ());
+ }
+ }
+}
+
+void DestroyLevelManagers ()
+{
+ InstanceIDArray loadedLevelManagers;
+ CollectLevelGameManagers (loadedLevelManagers);
+ for (InstanceIDArray::iterator i=loadedLevelManagers.begin ();i != loadedLevelManagers.end ();++i)
+ {
+ Object* o = Object::IDToPointer (*i);
+ AssertIf (o == NULL || o->IsPersistent ());
+ DestroyObjectHighLevel (o);
+ }
+}
+
+
+/// Setup all managers to be called for a given mode.
+/// When we're in edit mode, the Input, Dynamics, Fixed, Animation & Behaviour managers don't get
+/// Updated. In Play mode, everything runs.
+/// @param mode kPlayMode or kEditMode
+
+
+
+void RemoveDuplicateGameManagers ()
+{
+ const ManagerContext& context = GetManagerContext ();
+
+ for (int i=0;i<ManagerContext::kManagerCount;i++)
+ {
+ Assert(GetManagerPtrFromContext(i) != NULL);
+ }
+
+ vector<GameManager*> managers;
+ Object::FindObjectsOfType (&managers);
+
+ // Remove all managers that are not in the manager context!
+ for (int m=0;m<managers.size ();m++)
+ {
+ bool isUsed = false;
+ for (int i=0;i<ManagerContext::kManagerCount;i++)
+ {
+ if ((Object*)managers[m] == context.m_Managers[i])
+ isUsed = true;
+ }
+
+ if (!isUsed)
+ {
+ Object* obj = managers[m];
+ FatalErrorIf (obj->IsPersistent ());
+ ErrorString (Format("Removing duplicate game manager (%s)!", obj->GetClassName().c_str()));
+ FatalErrorIf (PPtr<Object> (managers[m])->IsPersistent ());
+ DestroyObjectHighLevel (managers[m]);
+ }
+ }
+
+ ErrorIf (Object::FindAllDerivedObjects (ClassID (GameManager), NULL) != ManagerContext::kManagerCount);
+}
+
+
+typedef vector_map<SInt32, SInt32> ClassIDToInstanceID;
+static void ExtractGlobalManagers (const std::string& path, ClassIDToInstanceID& managers)
+{
+ GetPersistentManager ().Lock();
+ SerializedFile* stream = GetPersistentManager ().GetSerializedFileInternal(path);
+ if (stream == NULL)
+ {
+ GetPersistentManager ().Unlock();
+ return;
+ }
+
+ vector<LocalIdentifierInFileType> sourceFileIDs;
+ stream->GetAllFileIDs(&sourceFileIDs);
+
+ for (int i=0;i<sourceFileIDs.size ();i++)
+ {
+ LocalIdentifierInFileType fileID = sourceFileIDs[i];
+ SInt32 classID = stream->GetClassID (fileID);
+
+ if (Object::IsDerivedFromClassID (classID, ClassID (GlobalGameManager)))
+ {
+ SInt32 instanceID = GetPersistentManager().GetInstanceIDFromPathAndFileID (path, fileID);
+ managers.push_unsorted (classID, instanceID);
+ }
+ }
+
+ managers.sort();
+
+ GetPersistentManager ().Unlock();
+}
+
+static Object* LoadManager (const ClassIDToInstanceID& managers, int classID)
+{
+ ClassIDToInstanceID::const_iterator i = managers.find(classID);
+ if (i == managers.end())
+ return NULL;
+
+ Object* obj = dynamic_instanceID_cast<Object*> (i->second);
+ Assert(dynamic_pptr_cast<GlobalGameManager*> (obj) != NULL);
+
+ return obj;
+}
+
+
+string PlayerLoadSettingsAndInput(const std::string& dataFile)
+{
+ ClassIDToInstanceID managers;
+ ExtractGlobalManagers (dataFile, managers);
+
+ ManagerContext::Managers loadManagers[] =
+ {
+ ManagerContext::kPlayerSettings,
+ ManagerContext::kInputManager,
+ ManagerContext::kBuildSettings,
+ ManagerContext::kGraphicsSettings,
+ ManagerContext::kQualitySettings,
+ };
+
+ const ManagerContext& ctx = GetManagerContext();
+ for( int i = 0; i < sizeof(loadManagers)/sizeof(loadManagers[0]); ++i )
+ {
+ int index = loadManagers[i];
+ int classID = ctx.m_ManagerClassIDs[index];
+ SetManagerPtrInContext(index, LoadManager(managers, classID));
+ if (ctx.m_Managers[index] == NULL || !ctx.m_Managers[index]->IsDerivedFrom(classID))
+ return Format("Could..... not preload global game manager #%i i=%i", index,i);
+ }
+
+ return string();
+}
+
+string PlayerLoadGlobalManagers (const char* dataFile)
+{
+ ClassIDToInstanceID managers;
+ ExtractGlobalManagers (dataFile, managers);
+
+ // Load all game managers! All global game managers are coming first in the main data
+ // (Global game managers have to be loaded before selecting the screen resolution.
+ // ProjectSettings i used by screen selector and RenderManager, InputManager by screen switching)
+ for (int i=0;i<ManagerContext::kGlobalManagerCount;i++)
+ {
+ int classID = GetManagerContext().m_ManagerClassIDs[i];
+
+ Object* manager = NULL;
+
+ // Manager is dead-code stripped
+ if (classID != -1)
+ {
+ // Try to load manager
+ manager = LoadManager(managers, classID);
+
+ // Manager could not be loaded, create it from code instead
+ if (classID != -1 && manager == NULL)
+ {
+ manager = CreateGameManager (classID);
+ printf_console("Loading manager failed, creating from code %d\n", classID);
+ }
+ }
+
+ // Assign manager as soon as it is created.
+ SetManagerPtrInContext(i, manager);
+ }
+
+ std::string resetError = ResetManagerContextFromLoaded();
+ if( !resetError.empty() )
+ return Format("PlayerLoadGlobalManagers: %s\n", resetError.c_str());
+
+ GetPersistentManager().DoneLoadingManagers();
+
+ return string();
+}
+
+string ResetManagerContextFromLoaded ()
+{
+ GlobalCallbacks::Get().managersWillBeReloadedHack.Invoke();
+
+ string error;
+ const ManagerContext& context = GetManagerContext ();
+
+ vector<GameManager*> allManagers;
+ Object::FindObjectsOfType (&allManagers);
+
+ for (int i=0;i<ManagerContext::kManagerCount;i++)
+ {
+ SetManagerPtrInContext(i, NULL);
+
+ if (context.m_ManagerClassIDs[i] == -1)
+ continue;
+
+ vector<GameManager*> specificManagers;
+ for (int j=0;j<allManagers.size();j++)
+ {
+ if (allManagers[j]->IsDerivedFrom(context.m_ManagerClassIDs[i]))
+ {
+ specificManagers.push_back(allManagers[j]);
+ }
+ }
+
+ if (specificManagers.size () == 1)
+ SetManagerPtrInContext(i, specificManagers[0]);
+ else if (specificManagers.size () == 0)
+ {
+ // missing global managers are serious errors
+ if( i < ManagerContext::kGlobalManagerCount )
+ error += " Missing " + Object::ClassIDToString (context.m_ManagerClassIDs[i]);
+ }
+ else
+ {
+ // missing global managers are serious errors
+ if( i < ManagerContext::kGlobalManagerCount )
+ error += " Too many instances of " + Object::ClassIDToString (context.m_ManagerClassIDs[i]);
+ }
+ }
+ return error;
+}
+
+void LoadManagers (AwakeFromLoadQueue& awakeFromLoadQueue)
+{
+ AwakeFromLoadMode awakeMode = (AwakeFromLoadMode)(kDidLoadFromDisk | kDidLoadThreaded);
+
+ awakeFromLoadQueue.PersistentManagerAwakeFromLoad(kManagersQueue, awakeMode, NULL);
+ awakeFromLoadQueue.ClearQueue(kManagersQueue);
+
+ // Get all managers that are in memory
+ map<int, set<GameManager*> > managers;
+ vector<GameManager*> temp;
+ Object::FindObjectsOfType (&temp);
+ for (int i=0;i<temp.size ();i++)
+ managers[temp[i]->GetClassID ()].insert (temp[i]);
+
+ const ManagerContext& context = GetManagerContext();
+ // Load the managers in the order defined by the context.
+ // Create new managers if necessary.
+ for (int i=0;i<ManagerContext::kManagerCount;i++)
+ {
+ SetManagerPtrInContext(i, NULL);
+
+ if (context.m_ManagerClassIDs[i] == -1)
+ continue;
+
+ const set<GameManager*>& manager = managers[context.m_ManagerClassIDs[i]];
+
+ if (manager.size () == 1)
+ SetManagerPtrInContext(i, *manager.begin ());
+ else if (manager.size () == 0)
+ SetManagerPtrInContext(i, CreateGameManager (context.m_ManagerClassIDs[i]));
+ else
+ {
+ ErrorString("Multiple managers are loaded of type: " + Object::ClassIDToString(context.m_ManagerClassIDs[i]));
+ SetManagerPtrInContext(i, *manager.begin ());
+ }
+ }
+}
diff --git a/Runtime/BaseClasses/ManagerContextLoading.h b/Runtime/BaseClasses/ManagerContextLoading.h
new file mode 100644
index 0000000..f8187d7
--- /dev/null
+++ b/Runtime/BaseClasses/ManagerContextLoading.h
@@ -0,0 +1,17 @@
+#ifndef _MANAGERCONTEXT_LOADING_H_
+#define _MANAGERCONTEXT_LOADING_H_
+
+#include "Runtime/Utilities/dynamic_array.h"
+
+typedef dynamic_array<int> InstanceIDArray;
+class AwakeFromLoadQueue;
+
+void CollectLevelGameManagers (InstanceIDArray& outputObjects);
+void DestroyLevelManagers ();
+void RemoveDuplicateGameManagers ();
+std::string PlayerLoadSettingsAndInput(const std::string& dataFile);
+std::string PlayerLoadGlobalManagers (const char* dataFile);
+std::string ResetManagerContextFromLoaded ();
+void LoadManagers (AwakeFromLoadQueue& awakeFromLoadQueue);
+
+#endif \ No newline at end of file
diff --git a/Runtime/BaseClasses/MessageHandler.cpp b/Runtime/BaseClasses/MessageHandler.cpp
new file mode 100644
index 0000000..71ef06e
--- /dev/null
+++ b/Runtime/BaseClasses/MessageHandler.cpp
@@ -0,0 +1,196 @@
+#include "UnityPrefix.h"
+#include "MessageHandler.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include <map>
+#include <list>
+
+using namespace std;
+
+MessageForwarder::MessageForwarder ()
+{
+ m_GeneralMessage = NULL;
+ m_CanHandleGeneralMessage = NULL;
+}
+
+void MessageForwarder::HandleMessage (void* receiver, int messageID, MessageData& messageData)
+{
+ MessageCallback messagePtr = m_GeneralMessage;
+ if (messageID < m_SupportedMessages.size () && m_SupportedMessages[messageID] != NULL)
+ messagePtr = m_SupportedMessages[messageID];
+
+ AssertIf (messagePtr == NULL);
+ messagePtr (receiver, messageID, messageData);
+}
+
+bool MessageForwarder::HasMessageCallback (const MessageIdentifier& identifier)
+{
+ if (identifier.messageID < m_SupportedMessages.size () && m_SupportedMessages[identifier.messageID])
+ return true;
+ else
+ return m_GeneralMessage != NULL && (identifier.options & MessageIdentifier::kSendToScripts);
+}
+
+bool MessageForwarder::WillHandleMessage (void* receiver, const MessageIdentifier& identifier)
+{
+ if (identifier.messageID < m_SupportedMessages.size () && m_SupportedMessages[identifier.messageID])
+ return true;
+
+ if (m_GeneralMessage && (identifier.options & MessageIdentifier::kSendToScripts))
+ {
+ AssertIf (m_CanHandleGeneralMessage == NULL);
+ MessageData data;
+ return m_CanHandleGeneralMessage (receiver, identifier.messageID, data);
+ }
+ return false;
+}
+
+int MessageForwarder::GetExpectedParameter (int messageID)
+{
+ if (messageID < m_SupportedMessages.size ())
+ return m_SupportedMessagesParameter[messageID];
+ else
+ return 0;
+}
+
+void MessageForwarder::RegisterMessageCallback (int messageID, MessageCallback message, int classId)
+{
+ AssertIf (messageID == -1);
+ if (messageID >= m_SupportedMessages.size ())
+ {
+ m_SupportedMessages.resize (messageID + 1, NULL);
+ m_SupportedMessagesParameter.resize (messageID + 1, 0);
+ }
+ m_SupportedMessages[messageID] = message;
+ m_SupportedMessagesParameter[messageID] = classId;
+}
+
+void MessageForwarder::RegisterAllMessagesCallback (MessageCallback message, CanHandleMessageCallback canHandle)
+{
+ m_GeneralMessage = message;
+ m_CanHandleGeneralMessage = canHandle;
+}
+
+void MessageForwarder::AddBaseMessages (const MessageForwarder& baseClass)
+{
+ int maxsize = max (m_SupportedMessages.size (), baseClass.m_SupportedMessages.size ());
+ m_SupportedMessages.resize (maxsize, NULL);
+ m_SupportedMessagesParameter.resize (maxsize, 0);
+ for (int i=0;i<m_SupportedMessages.size ();i++)
+ {
+ if (m_SupportedMessages[i] == NULL && i < baseClass.m_SupportedMessages.size ())
+ {
+ m_SupportedMessages[i] = baseClass.m_SupportedMessages[i];
+ m_SupportedMessagesParameter[i] = baseClass.m_SupportedMessagesParameter[i];
+ }
+ }
+
+ if (m_GeneralMessage == NULL)
+ m_GeneralMessage = baseClass.m_GeneralMessage;
+}
+
+void MessageHandler::InitializeMessageIdentifiers ()
+{
+ MessageIdentifier::RegisteredMessages& identifiers = MessageIdentifier::GetRegisteredMessages ();
+ MessageIdentifier::SortedMessages sortedMessages = MessageIdentifier::GetSortedMessages(false);
+
+ m_MessageIDToIdentifier.clear ();
+ m_MessageNameToIndex.clear ();
+
+ // Build m_MessageIDToName and m_MessageNameToIndex
+ // by copying the data from the sorted messages map
+ m_MessageIDToIdentifier.resize (sortedMessages.size ());
+ int index = 0;
+ MessageIdentifier::SortedMessages::iterator i;
+ for (i = sortedMessages.begin ();i != sortedMessages.end ();i++)
+ {
+ m_MessageNameToIndex[i->first] = index;
+ m_MessageIDToIdentifier[index] = i->second;
+ m_MessageIDToIdentifier[index].messageID = index;
+ index++;
+ }
+
+
+ // Setup the messageIDs of all registered message Identifiers
+ MessageIdentifier::RegisteredMessages::iterator j;
+ for (j = identifiers.begin ();j != identifiers.end ();j++)
+ {
+ AssertIf (*j == NULL);
+ MessageIdentifier& identifier = **j;
+ if (m_MessageNameToIndex.count (identifier.messageName))
+ {
+ identifier.messageID = m_MessageNameToIndex[identifier.messageName];
+ }
+ }
+}
+
+void MessageHandler::Initialize (const MessageForwarders& receivers)
+{
+ m_Forwarder = receivers;
+ m_MessageCount = m_MessageNameToIndex.size ();
+ m_ClassCount = receivers.size ();
+
+ // Precalculate supported messages
+ m_SupportedMessages.resize (m_ClassCount * m_MessageCount);
+ for (int c=0;c<m_ClassCount;c++)
+ {
+ for (int m=0;m<m_MessageCount;m++)
+ {
+ bool hasCallback = m_Forwarder[c].HasMessageCallback (m_MessageIDToIdentifier[m]);
+ if (hasCallback)
+ {
+ // Check if the parameter is correct and print an error if they dont match
+ int wantedParameter = m_Forwarder[c].GetExpectedParameter (m);
+ int messageParameter = m_MessageIDToIdentifier[m].parameterClassId;
+ if (wantedParameter != 0 && messageParameter != wantedParameter)
+ {
+ char buffy[4096];
+ char const format[] = "The message: %s in the class with "
+ "classID: %d uses a parameter type "
+ "that is different from the "
+ "message's parameter type: %d != %d.";
+ sprintf (buffy, format, m_MessageIDToIdentifier[m].messageName,
+ c, wantedParameter, messageParameter);
+ ErrorString (buffy);
+ hasCallback = false;
+ }
+ }
+ m_SupportedMessages[m * m_ClassCount + c] = hasCallback;
+ }
+ }
+}
+
+bool MessageHandler::WillHandleMessage (void* receiver, int classID, int messageID)
+{
+ AssertIf (!HasMessageCallback (classID, messageID));
+ return m_Forwarder[classID].WillHandleMessage (receiver, m_MessageIDToIdentifier[messageID]);
+}
+
+int MessageHandler::MessageNameToID (const string& name)
+{
+ MessageNameToIndex::iterator i = m_MessageNameToIndex.find (name);
+ if (i == m_MessageNameToIndex.end ())
+ return -1;
+ else
+ return i->second;
+}
+
+const char* MessageHandler::MessageIDToName (int messageID)
+{
+ AssertIf (messageID < 0);
+ AssertIf (messageID >= m_MessageIDToIdentifier.size ());
+ return m_MessageIDToIdentifier[messageID].messageName;
+}
+
+int MessageHandler::MessageIDToParameter (int messageID)
+{
+ AssertIf (messageID < 0);
+ AssertIf (messageID >= m_MessageIDToIdentifier.size ());
+ return m_MessageIDToIdentifier[messageID].parameterClassId;
+}
+
+MessageIdentifier MessageHandler::MessageIDToMessageIdentifier (int messageID)
+{
+ AssertIf (messageID < 0);
+ AssertIf (messageID >= m_MessageIDToIdentifier.size ());
+ return m_MessageIDToIdentifier[messageID];
+}
diff --git a/Runtime/BaseClasses/MessageHandler.h b/Runtime/BaseClasses/MessageHandler.h
new file mode 100644
index 0000000..5d0fa46
--- /dev/null
+++ b/Runtime/BaseClasses/MessageHandler.h
@@ -0,0 +1,112 @@
+#ifndef MESSAGEHANDLER_H
+#define MESSAGEHANDLER_H
+
+#include <vector>
+#include <map>
+#include <string>
+#include "Runtime/Utilities/dynamic_bitset.h"
+#include "Runtime/Misc/Allocator.h"
+#include "MessageIdentifier.h"
+
+/*
+ DOCUMENT_______________________________________
+*/
+
+class MessageForwarder
+{
+ typedef void (*MessageCallback)(void* Receiver, int messageID, MessageData& data);
+ typedef bool (*CanHandleMessageCallback)(void* Receiver, int messageID, MessageData& data);
+ std::vector<MessageCallback> m_SupportedMessages;
+ std::vector<int> m_SupportedMessagesParameter;
+ MessageCallback m_GeneralMessage;
+ CanHandleMessageCallback m_CanHandleGeneralMessage;
+
+ public:
+
+ MessageForwarder ();
+
+ // Returns true if a message callback exists for the class and the messageID
+ bool HasMessageCallback (const MessageIdentifier& messageID);
+
+ // Returns true a message callback exists and the message will actually be handled.
+ // This is used to find out if a message will *actually* be forwared, eg. HasMessageCallback will always return true
+ // for eg. ScriptBehaviours which checks at runtime if the message is supported by the script.
+ bool WillHandleMessage (void* receiver, const MessageIdentifier& messageID);
+
+ /// Calls the message
+ /// the notification can be handled using CanHandleNotification
+ void HandleMessage (void* receiver, int messageID, MessageData& notificationData);
+
+ void RegisterMessageCallback (int messageID, MessageCallback message, int classId);
+ void RegisterAllMessagesCallback (MessageCallback message, CanHandleMessageCallback canHandleMessage);
+
+ /// Returns the parameter that the receiver expects from a message. If
+ /// the method doesn't expect a parameter, is not supported, or uses a
+ /// general message handler, 0 is returned.
+ int GetExpectedParameter (int messageID);
+
+ /// Adds all messages that baseMessages contains but this MessageReceiver does not handle yet.
+ /// AddBaseNotifications is used to implement derivation by calling AddBaseNotifications for all base classes.
+ void AddBaseMessages (const MessageForwarder& baseMessages);
+};
+
+typedef std::vector<MessageForwarder, STL_ALLOCATOR_ALIGNED(kMemPermanent, MessageForwarder, 8) > MessageForwarders;
+
+class MessageHandler
+{
+ dynamic_bitset m_SupportedMessages;
+ MessageForwarders m_Forwarder;
+ int m_ClassCount;
+ int m_MessageCount;
+
+ typedef std::vector<MessageIdentifier> MessageIDToIdentifier;
+ MessageIDToIdentifier m_MessageIDToIdentifier;
+ typedef std::map<std::string, int> MessageNameToIndex;
+ MessageNameToIndex m_MessageNameToIndex;
+
+ public:
+
+ /// Initializes all message forwarders and precalculates the supporetedmessages bit array
+ void Initialize (const MessageForwarders& receivers);
+
+ // Generates the messageIndices for all MessageIdentifier's
+ // Gets the list of all message identifiers which are created by constructor of MessageIdentifier
+ // Sorts the messages by name and builds the MessageNameToIndex and m_MessageIDToIdentifier maps.
+ void InitializeMessageIdentifiers ();
+
+ // Returns true if a message callback exists for the class and the messageID
+ bool HasMessageCallback (int classID, int messageID) { return m_SupportedMessages.test (messageID * m_ClassCount + classID); }
+
+ // Returns true a message callback exists and the message will actually be handled.
+ // This is used to find out if a message will *actually* be forwared, eg. HasMessageCallback will always return true
+ // for eg. ScriptBehaviours which checks at runtime if the message is supported by the script.
+ bool WillHandleMessage (void* receiver, int classID, int messageID);
+
+ /// Forwards a message to the appropriate MessageForwarder
+ void HandleMessage (void* receiver, int classID, int messageID, MessageData& messageData)
+ {
+ AssertIf (classID >= m_ClassCount);
+ SET_ALLOC_OWNER(NULL);
+ m_Forwarder[classID].HandleMessage (receiver, messageID, messageData);
+ }
+
+ void SetMessageEnabled (int classID, int messageID, bool enabled)
+ {
+ // You are probably doing something wrong if you enable/disable a message twice
+ DebugAssertIf(m_SupportedMessages[messageID * m_ClassCount + classID] == enabled);
+ m_SupportedMessages[messageID * m_ClassCount + classID] = enabled;
+ }
+
+ // Converts a message name to an ID if the name is not registered returns -1
+ int MessageNameToID (const std::string& name);
+ // Converts a messageID to its message name. The messageID has to exist
+ const char* MessageIDToName (int messageID);
+ // Converts a messageID to its parameter eg. ClassID (float). The messageID has to exist
+ int MessageIDToParameter (int messageID);
+ MessageIdentifier MessageIDToMessageIdentifier (int messageID);
+
+ // Returns the number of registered messages
+ int GetMessageCount () { return m_MessageCount; }
+};
+
+#endif
diff --git a/Runtime/BaseClasses/MessageIdentifier.cpp b/Runtime/BaseClasses/MessageIdentifier.cpp
new file mode 100644
index 0000000..fc55509
--- /dev/null
+++ b/Runtime/BaseClasses/MessageIdentifier.cpp
@@ -0,0 +1,86 @@
+#include "UnityPrefix.h"
+#include "MessageIdentifier.h"
+using namespace std;
+
+MessageIdentifier::RegisteredMessages* gRegisteredMessageIdentifiers = NULL;
+
+MessageIdentifier::MessageIdentifier (const char* name, Options opts, int classId, const char* scriptParamName)
+{
+ AssertIf (name == NULL);
+
+ messageName = name;
+ parameterClassId = classId;
+ scriptParameterName = scriptParamName;
+ messageID = -1;
+ options = (int)opts;
+ if (gRegisteredMessageIdentifiers == NULL)
+ gRegisteredMessageIdentifiers = new RegisteredMessages();
+
+ gRegisteredMessageIdentifiers->push_back (this);
+}
+
+MessageIdentifier::RegisteredMessages& MessageIdentifier::GetRegisteredMessages ()
+{
+ AssertIf (gRegisteredMessageIdentifiers == NULL);
+ return *gRegisteredMessageIdentifiers;
+}
+
+MessageIdentifier::SortedMessages MessageIdentifier::GetSortedMessages (bool notificationMessages)
+{
+ // Build sorted Messages map. Which contains all messages sorted by name.
+ SortedMessages sortedMessages;
+ RegisteredMessages& messages = GetRegisteredMessages();
+ RegisteredMessages::iterator j;
+ for (j = messages.begin ();j != messages.end ();j++)
+ {
+ MessageIdentifier& identifier = **j;
+ AssertIf (identifier.messageName == NULL);
+
+ bool usesNotifications = identifier.options & kUseNotificationManager;
+ if (usesNotifications != notificationMessages)
+ continue;
+
+ SortedMessages::iterator found = sortedMessages.find (identifier.messageName);
+ if (found == sortedMessages.end ())
+ {
+ sortedMessages.insert (make_pair (string (identifier.messageName), identifier));
+ }
+ else
+ {
+ if (identifier.parameterClassId != found->second.parameterClassId)
+ {
+ string error = "There are conflicting definitions of the message: ";
+ error += identifier.messageName;
+ error += ". The parameter of one message has to be the same across all definitions of that message.";
+ ErrorString (error);
+ }
+
+ if (identifier.scriptParameterName != found->second.scriptParameterName)
+ {
+ string error = "There are conflicting definitions of the message: ";
+ error += identifier.messageName;
+ error += ". The parameter of one message has to be the same across all definitions of that message.";
+ ErrorString (error);
+ }
+
+ if (identifier.options != found->second.options)
+ {
+ string error = "There are conflicting options of the message: ";
+ error += identifier.messageName;
+ ErrorString (error);
+ }
+ }
+ }
+ return sortedMessages;
+}
+
+void MessageIdentifier::Cleanup ()
+{
+ delete gRegisteredMessageIdentifiers;
+}
+
+#include "Runtime/Dynamics/Collider.h"
+#undef MESSAGE_IDENTIFIER
+#define MESSAGE_IDENTIFIER(n,p) const EXPORT_COREMODULE MessageIdentifier n p
+#include "MessageIdentifiers.h"
+
diff --git a/Runtime/BaseClasses/MessageIdentifier.h b/Runtime/BaseClasses/MessageIdentifier.h
new file mode 100644
index 0000000..9469ef8
--- /dev/null
+++ b/Runtime/BaseClasses/MessageIdentifier.h
@@ -0,0 +1,109 @@
+#ifndef MESSAGEIDENTIFIER_H
+#define MESSAGEIDENTIFIER_H
+
+#include <list>
+#include <typeinfo>
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Misc/Allocator.h"
+#include <map>
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/BaseClasses/ClassIDs.h"
+
+struct MessageData
+{
+ int type;
+private:
+ // Note: on Metro WinRT types cannot be located in union, so don't use union!
+ intptr_t data;
+ ScriptingObjectPtr scriptingObjectData;
+public:
+ MessageData () : scriptingObjectData(SCRIPTING_NULL)
+ {
+ data = 0; type = 0;
+ }
+
+ template<class T>
+ void SetData (T inData, int classId)
+ {
+ // Check if SetData is used instead of SetScriptingObjectData
+ Assert (type != ClassID (MonoObject));
+ AssertIf (sizeof (T) > sizeof (data)); // increase the data size
+ *reinterpret_cast<T*> (&data) = *reinterpret_cast<T*> (&inData);
+ type = classId;
+ }
+
+ template<class T>
+ T GetData ()
+ {
+ // Check if GetData is used instead of GetScriptingObjectData
+ Assert (type != ClassID (MonoObject));
+ return *reinterpret_cast<T*> (&data);
+ }
+
+ intptr_t& GetGenericDataRef ()
+ {
+ // Check if GetGenericDataRef is used instead of GetScriptingObjectData
+ Assert (type != ClassID (MonoObject));
+ return data;
+ }
+
+ void SetScriptingObjectData (ScriptingObjectPtr inData)
+ {
+ scriptingObjectData = inData;
+ type = ClassID (MonoObject);
+ }
+
+ ScriptingObjectPtr GetScriptingObjectData ()
+ {
+ Assert (type == ClassID (MonoObject));
+ return scriptingObjectData;
+ }
+
+};
+
+// usage:
+// MessageIdentifier kOnTransformChanged ("OnTransformChanged", MessageIdentifier::kDontSendToScripts);
+// MessageIdentifier kOnTransformChanged ("OnTransformChanged", MessageIdentifier::kDontSendToScripts, ClassID (int));
+
+
+class MessageIdentifier
+{
+ public:
+
+ enum Options { kDontSendToScripts = 0, kSendToScripts = 1 << 0, kUseNotificationManager = 1 << 1, kDontSendToDisabled = 1 << 2 };
+
+ const char* messageName;
+ const char* scriptParameterName;
+ int messageID;
+ int parameterClassId;
+ int options;
+
+ /// Place the MessageIdentifier as a global variable!
+ /// The constructor must be called before InitializeEngine
+ explicit MessageIdentifier (const char* name, Options opt, int classId = 0, const char* scriptParamName = NULL);
+
+ typedef std::list<MessageIdentifier*, STL_ALLOCATOR(kMemPermanent, MessageIdentifier*) > RegisteredMessages;
+ typedef std::map<std::string, MessageIdentifier> SortedMessages;
+
+ // All registered message identifiers
+ static RegisteredMessages& GetRegisteredMessages ();
+ // Sorted list of message identifiers
+ // - Duplicate message identifiers are ignored
+ // - Only notification messages are returned if onlyNotificationMessages otherwise only normal messages are returned (See kUseNotificationManager)
+ static SortedMessages GetSortedMessages (bool onlyNotificationMessages);
+
+ static void Cleanup ();
+
+ // Only to be used by subclasses
+ MessageIdentifier ()
+ {
+ messageID = -1; scriptParameterName = NULL; messageName = NULL;
+ options = kSendToScripts;
+ }
+};
+
+#include "MessageIdentifiers.h"
+
+#endif
diff --git a/Runtime/BaseClasses/MessageIdentifiers.h b/Runtime/BaseClasses/MessageIdentifiers.h
new file mode 100644
index 0000000..3d285ce
--- /dev/null
+++ b/Runtime/BaseClasses/MessageIdentifiers.h
@@ -0,0 +1,86 @@
+#ifndef MESSAGE_IDENTIFIER
+#define MESSAGE_IDENTIFIER(n,p) const extern EXPORT_COREMODULE MessageIdentifier n
+#endif
+
+// NOTE: Whether messages are sent to scripts at all or sent to disabled scripts is determined on a per callback basis here,
+// make sure you set MessageIdentifier::Options appropriately for your message, leaving out kDontSendToDisabled will
+// mean that script code in the callback function will be executed regardless of whether the script is enabled or not.
+
+MESSAGE_IDENTIFIER(kEnterTrigger, ("OnTriggerEnter", MessageIdentifier::kSendToScripts, ClassID (Collider)));
+MESSAGE_IDENTIFIER(kExitTrigger, ("OnTriggerExit", MessageIdentifier::kSendToScripts, ClassID (Collider)));
+MESSAGE_IDENTIFIER(kStayTrigger, ("OnTriggerStay", MessageIdentifier::kSendToScripts, ClassID (Collider)));
+MESSAGE_IDENTIFIER(kEnterContact, ("OnCollisionEnter", MessageIdentifier::kSendToScripts, ClassID (Collision)));
+MESSAGE_IDENTIFIER(kExitContact, ("OnCollisionExit", MessageIdentifier::kSendToScripts, ClassID (Collision)));
+MESSAGE_IDENTIFIER(kStayContact, ("OnCollisionStay", MessageIdentifier::kSendToScripts, ClassID (Collision)));
+MESSAGE_IDENTIFIER(kCollisionEnter2D, ("OnCollisionEnter2D", MessageIdentifier::kSendToScripts, ClassID (Collision2D)));
+MESSAGE_IDENTIFIER(kCollisionExit2D, ("OnCollisionExit2D", MessageIdentifier::kSendToScripts, ClassID (Collision2D)));
+MESSAGE_IDENTIFIER(kCollisionStay2D, ("OnCollisionStay2D", MessageIdentifier::kSendToScripts, ClassID (Collision2D)));
+MESSAGE_IDENTIFIER(kTriggerEnter2D, ("OnTriggerEnter2D", MessageIdentifier::kSendToScripts, ClassID (Collider2D)));
+MESSAGE_IDENTIFIER(kTriggerExit2D, ("OnTriggerExit2D", MessageIdentifier::kSendToScripts, ClassID (Collider2D)));
+MESSAGE_IDENTIFIER(kTriggerStay2D, ("OnTriggerStay2D", MessageIdentifier::kSendToScripts, ClassID (Collider2D)));
+MESSAGE_IDENTIFIER(kJointBreak, ("OnJointBreak", MessageIdentifier::kSendToScripts, ClassID (float)));
+MESSAGE_IDENTIFIER(kTransformChanged, ("OnTransformChanged", MessageIdentifier::kDontSendToScripts, ClassID (int)));
+MESSAGE_IDENTIFIER(kParticleCollisionEvent, ("OnParticleCollision", MessageIdentifier::kSendToScripts, ClassID (GameObject)));
+MESSAGE_IDENTIFIER(kDidModifyValidity, ("OnDidModifyMeshValidity", MessageIdentifier::kDontSendToScripts));
+MESSAGE_IDENTIFIER(kDidDeleteMesh, ("OnDidModifyMeshDelete", MessageIdentifier::kDontSendToScripts, ClassID (Mesh)));
+MESSAGE_IDENTIFIER(kDidModifyBounds, ("OnDidModifyMeshBounds", MessageIdentifier::kDontSendToScripts));
+MESSAGE_IDENTIFIER(kDidModifyMesh, ("OnDidModifyMesh", MessageIdentifier::kDontSendToScripts));
+MESSAGE_IDENTIFIER(kLayerChanged, ("OnLayersChanged", MessageIdentifier::kDontSendToScripts));
+MESSAGE_IDENTIFIER(kDestroyedComponentNotification, ("OnDestroyedComponent", MessageIdentifier::kUseNotificationManager));
+MESSAGE_IDENTIFIER(kDidAddComponent , ("OnDidAddComponent", MessageIdentifier::kDontSendToScripts, ClassID (Component)));
+MESSAGE_IDENTIFIER(kBecameVisible, ("OnBecameVisible", MessageIdentifier::kSendToScripts));
+MESSAGE_IDENTIFIER(kBecameInvisible, ("OnBecameInvisible", MessageIdentifier::kSendToScripts));
+MESSAGE_IDENTIFIER(kOnWillRenderObject, ("OnWillRenderObject", (MessageIdentifier::Options)(MessageIdentifier::kSendToScripts|MessageIdentifier::kDontSendToDisabled)));
+MESSAGE_IDENTIFIER(kPreCull, ("OnPreCull", (MessageIdentifier::Options)(MessageIdentifier::kSendToScripts|MessageIdentifier::kDontSendToDisabled)));
+MESSAGE_IDENTIFIER(kPostRender, ("OnPostRender", (MessageIdentifier::Options)(MessageIdentifier::kSendToScripts|MessageIdentifier::kDontSendToDisabled)));
+MESSAGE_IDENTIFIER(kPreRender, ("OnPreRender", (MessageIdentifier::Options)(MessageIdentifier::kSendToScripts|MessageIdentifier::kDontSendToDisabled)));
+MESSAGE_IDENTIFIER(kControllerColliderHit, ("OnControllerColliderHit", MessageIdentifier::kSendToScripts, ClassID (MonoObject)));
+MESSAGE_IDENTIFIER(kAnimatorMove, ("OnAnimatorMove", (MessageIdentifier::Options)(MessageIdentifier::kSendToScripts|MessageIdentifier::kDontSendToDisabled)));
+MESSAGE_IDENTIFIER(kAnimatorMoveBuiltin, ("OnAnimatorMoveBuiltin", MessageIdentifier::kDontSendToScripts, ClassID(RootMotionData)));
+MESSAGE_IDENTIFIER(kAnimatorIK, ("OnAnimatorIK", (MessageIdentifier::Options)(MessageIdentifier::kSendToScripts|MessageIdentifier::kDontSendToDisabled), ClassID (int)));
+MESSAGE_IDENTIFIER(kForceRecreateCollider, ("ForceRecreateCollider", MessageIdentifier::kDontSendToScripts));
+MESSAGE_IDENTIFIER(kRecreateRigidBodyDependencies2D, ("RecreateRigidBodyDependencies2D", MessageIdentifier::kDontSendToScripts, ClassID (Rigidbody2D)));
+MESSAGE_IDENTIFIER(kServerInitialized, ("OnServerInitialized", MessageIdentifier::kSendToScripts, ClassID (int), "NetworkPlayer"));
+MESSAGE_IDENTIFIER(kPlayerConnected, ("OnPlayerConnected", MessageIdentifier::kSendToScripts, ClassID (int), "NetworkPlayer"));
+MESSAGE_IDENTIFIER(kConnectedToServer, ("OnConnectedToServer", MessageIdentifier::kSendToScripts));
+MESSAGE_IDENTIFIER(kPlayerDisconnected, ("OnPlayerDisconnected", MessageIdentifier::kSendToScripts, ClassID (int), "NetworkPlayer"));
+MESSAGE_IDENTIFIER(kDisconnectedFromServer, ("OnDisconnectedFromServer", MessageIdentifier::kSendToScripts, ClassID (int), "NetworkDisconnection"));
+MESSAGE_IDENTIFIER(kConnectionAttemptFailed, ("OnFailedToConnect", MessageIdentifier::kSendToScripts, ClassID (int), "NetworkConnectionError"));
+MESSAGE_IDENTIFIER(kMasterServerConnectionAttemptFailed, ("OnFailedToConnectToMasterServer", MessageIdentifier::kSendToScripts, ClassID (int), "NetworkConnectionError"));
+MESSAGE_IDENTIFIER(kDisconnectedFromMasterServer, ("OnDisconnectedFromMasterServer", MessageIdentifier::kSendToScripts, ClassID (int), "NetworkDisconnection"));
+MESSAGE_IDENTIFIER(kMasterServerEvent, ("OnMasterServerEvent", MessageIdentifier::kSendToScripts, ClassID (int), "MasterServerEvent"));
+MESSAGE_IDENTIFIER(kLevelWasLoaded, ("OnLevelWasLoaded", MessageIdentifier::kSendToScripts, ClassID (int)));
+MESSAGE_IDENTIFIER(kPlayerPause, ("OnApplicationPause", MessageIdentifier::kSendToScripts, ClassID (bool)));
+MESSAGE_IDENTIFIER(kPlayerFocus, ("OnApplicationFocus", MessageIdentifier::kSendToScripts, ClassID (bool)));
+MESSAGE_IDENTIFIER(kPlayerQuit, ("OnApplicationQuit", MessageIdentifier::kSendToScripts));
+MESSAGE_IDENTIFIER(kTerrainChanged, ("OnTerrainChanged", MessageIdentifier::kSendToScripts, ClassID (int), "TerrainChangedFlags")); // Investigate getting rid of these
+MESSAGE_IDENTIFIER(kSetLightmapIndex, ("SetLightmapIndex", MessageIdentifier::kSendToScripts, ClassID (int))); //
+MESSAGE_IDENTIFIER(kShiftLightmapIndex, ("ShiftLightmapIndex", MessageIdentifier::kSendToScripts, ClassID (int))); //
+MESSAGE_IDENTIFIER(kDidModifyAnimatorController, ("OnDidModifyAnimatorController", MessageIdentifier::kDontSendToScripts));
+MESSAGE_IDENTIFIER(kDidModifyMotion, ("OnDidModifyMotion", MessageIdentifier::kDontSendToScripts));
+MESSAGE_IDENTIFIER(kDidVelocityChange, ("OnDidVelocityChange", MessageIdentifier::kDontSendToDisabled, ClassID (Vector3f)));
+MESSAGE_IDENTIFIER(kDidModifyAvatar, ("OnDidModifyAvatar", MessageIdentifier::kDontSendToScripts));
+
+#if ENABLE_NEW_EVENT_SYSTEM
+
+#ifndef SEND_TO_SCRIPTS
+#define SEND_TO_SCRIPTS (MessageIdentifier::Options)(MessageIdentifier::kSendToScripts | MessageIdentifier::kDontSendToDisabled)
+#endif
+
+MESSAGE_IDENTIFIER(kOnMouseEnterEvent, ("OnMouseEnterEvent", SEND_TO_SCRIPTS));
+MESSAGE_IDENTIFIER(kOnMouseExitEvent, ("OnMouseExitEvent", SEND_TO_SCRIPTS));
+MESSAGE_IDENTIFIER(kOnMouseMoveEvent, ("OnMouseMoveEvent", SEND_TO_SCRIPTS));
+MESSAGE_IDENTIFIER(kOnPressEvent, ("OnPressEvent", SEND_TO_SCRIPTS));
+MESSAGE_IDENTIFIER(kOnReleaseEvent, ("OnReleaseEvent", SEND_TO_SCRIPTS));
+MESSAGE_IDENTIFIER(kOnDragEvent, ("OnDragEvent", SEND_TO_SCRIPTS));
+MESSAGE_IDENTIFIER(kOnDropEvent, ("OnDropEvent", SEND_TO_SCRIPTS));
+MESSAGE_IDENTIFIER(kOnDragEnterEvent, ("OnDragEnterEvent", SEND_TO_SCRIPTS));
+MESSAGE_IDENTIFIER(kOnDragExitEvent, ("OnDragExitEvent", SEND_TO_SCRIPTS));
+MESSAGE_IDENTIFIER(kOnClickEvent, ("OnClickEvent", SEND_TO_SCRIPTS));
+MESSAGE_IDENTIFIER(kOnKeyEvent, ("OnKeyEvent", SEND_TO_SCRIPTS));
+MESSAGE_IDENTIFIER(kOnSelectEvent, ("OnSelectEvent", SEND_TO_SCRIPTS));
+MESSAGE_IDENTIFIER(kOnDeselectEvent, ("OnDeselectEvent", SEND_TO_SCRIPTS));
+MESSAGE_IDENTIFIER(kOnScrollEvent, ("OnScrollEvent", SEND_TO_SCRIPTS));
+//MESSAGE_IDENTIFIER(kOnShowTooltip, ("OnShowTooltip", SEND_TO_SCRIPTS));
+//MESSAGE_IDENTIFIER(kOnHideTooltip, ("OnHideTooltip", SEND_TO_SCRIPTS));
+#endif
diff --git a/Runtime/BaseClasses/NamedObject.cpp b/Runtime/BaseClasses/NamedObject.cpp
new file mode 100644
index 0000000..41acc3a
--- /dev/null
+++ b/Runtime/BaseClasses/NamedObject.cpp
@@ -0,0 +1,33 @@
+#include "UnityPrefix.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "NamedObject.h"
+#include "Runtime/Containers/ConstantStringSerialization.h"
+
+NamedObject::NamedObject (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+NamedObject::~NamedObject ()
+{
+}
+
+template<class TransferFunction>
+void NamedObject::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ TransferConstantString(m_Name, "m_Name", kHideInEditorMask, GetMemoryLabel(), transfer);
+}
+
+void NamedObject::SetName (char const* name)
+{
+ if (strcmp (m_Name.c_str (), name) != 0)
+ {
+ m_Name.assign (name, GetMemoryLabel());
+ SetDirty ();
+ }
+}
+
+IMPLEMENT_CLASS (NamedObject)
+IMPLEMENT_OBJECT_SERIALIZE (NamedObject)
+INSTANTIATE_TEMPLATE_TRANSFER_EXPORTED(NamedObject) \ No newline at end of file
diff --git a/Runtime/BaseClasses/NamedObject.h b/Runtime/BaseClasses/NamedObject.h
new file mode 100644
index 0000000..7355dbc
--- /dev/null
+++ b/Runtime/BaseClasses/NamedObject.h
@@ -0,0 +1,23 @@
+#ifndef NAMEDOBJECT_H
+#define NAMEDOBJECT_H
+
+#include "EditorExtension.h"
+#include "Runtime/Containers/ConstantString.h"
+
+class EXPORT_COREMODULE NamedObject : public EditorExtension
+{
+ public:
+
+ virtual char const* GetName () const { return m_Name.c_str (); }
+ virtual void SetName (char const* name);
+
+ REGISTER_DERIVED_ABSTRACT_CLASS (NamedObject, EditorExtension)
+ DECLARE_OBJECT_SERIALIZE (NamedObject)
+
+ NamedObject (MemLabelId label, ObjectCreationMode mode);
+ protected:
+
+ ConstantString m_Name;
+};
+
+#endif
diff --git a/Runtime/BaseClasses/ObjectDefines.h b/Runtime/BaseClasses/ObjectDefines.h
new file mode 100644
index 0000000..71550e0
--- /dev/null
+++ b/Runtime/BaseClasses/ObjectDefines.h
@@ -0,0 +1,202 @@
+#ifndef OBJECTDEFINES_H
+#define OBJECTDEFINES_H
+
+#include "Runtime/Allocator/BaseAllocator.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+
+#define NEW_OBJECT(class_) reinterpret_cast<class_*> (::Object::AllocateAndAssignInstanceID( UNITY_NEW_AS_ROOT ( class_(kMemBaseObject, kCreateObjectDefault), kMemBaseObject, NULL, NULL) ))
+#define NEW_OBJECT_USING_MEMLABEL(class_, memlabel_) reinterpret_cast<class_*> (::Object::AllocateAndAssignInstanceID( UNITY_NEW_AS_ROOT ( class_(memlabel_, kCreateObjectDefault), memlabel_, NULL, NULL) ))
+#define NEW_OBJECT_MAIN_THREAD(class_) reinterpret_cast<class_*> (::Object::AllocateAndAssignInstanceID(UNITY_NEW_AS_ROOT ( class_(kMemBaseObject, kCreateObjectDefault), kMemBaseObject, NULL, NULL) ))
+#define NEW_OBJECT_FULL(class_,param) UNITY_NEW_AS_ROOT( class_(kMemBaseObject, param), kMemBaseObject, NULL, NULL)
+
+#define NEW_OBJECT_RESET_AND_AWAKE(class_) ResetAndAwake (NEW_OBJECT (class_))
+
+// Every non-abstract class that is derived from object has to place this inside the class Declaration
+// (REGISTER_DERIVED_CLASS (Foo, Object))
+#define REGISTER_DERIVED_CLASS(x, d) \
+public: \
+ virtual int GetClassIDVirtualInternal () const; \
+ static int GetClassIDStatic () { return ClassID (x); } \
+ static const char* GetClassStringStatic (){ return #x; } \
+ static const char* GetPPtrTypeString () { return "PPtr<"#x">"; } \
+ static bool IsAbstract () { return false; }\
+ static Object* PRODUCE (MemLabelId label, ObjectCreationMode mode) { return UNITY_NEW_AS_ROOT( x (label, mode), label, NULL, NULL); } \
+ typedef d Super; \
+ static void RegisterClass (); \
+ protected: \
+ ~x (); \
+ public:
+
+
+// Every abstract class that is derived from object has to place this inside the class Declaration
+// (REGISTER_DERIVED_ABSTRACT_CLASS (Foo, Object))
+#define REGISTER_DERIVED_ABSTRACT_CLASS(x, d) \
+ public: \
+ virtual int GetClassIDVirtualInternal () const; \
+ static int GetClassIDStatic () { return ClassID (x); } \
+ static Object* PRODUCE (MemLabelId, ObjectCreationMode) { AssertString ("Can't produce abstract class"); return NULL; } \
+ static bool IsAbstract () { return true; }\
+ static const char* GetClassStringStatic (){ return #x; } \
+ static const char* GetPPtrTypeString () { return "PPtr<"#x">"; } \
+ typedef d Super; \
+ static void RegisterClass (); \
+ protected: \
+ ~x (); \
+ public:
+
+typedef void RegisterClassCallback ();
+void EXPORT_COREMODULE RegisterInitializeClassCallback (int classID,
+ RegisterClassCallback* registerClass,
+ RegisterClassCallback* initClass,
+ RegisterClassCallback* postInitClass,
+ RegisterClassCallback* cleanupClass);
+
+
+#if DEBUGMODE
+#define VERIFY_OBJECT_IS_REGISTERED(x) \
+struct IMPLEMENT_CONSTRUCTOR_CLASS##x { IMPLEMENT_CONSTRUCTOR_CLASS##x () { AddVerifyClassRegistration (ClassID (x)); } }; \
+IMPLEMENT_CONSTRUCTOR_CLASS##x gVAR_CONSTRUCTOR_CLASS##x;
+
+#else
+#define VERIFY_OBJECT_IS_REGISTERED(x)
+#endif
+
+#define IMPLEMENT_CLASS_FULL(x, INIT, POSTINIT, CLEANUP) \
+VERIFY_OBJECT_IS_REGISTERED(x) \
+void RegisterClass_##x () { RegisterInitializeClassCallback (ClassID (x), x::RegisterClass, INIT, POSTINIT, CLEANUP); } \
+void x::RegisterClass () \
+{ \
+ Assert(!Super::IsSealedClass ()); \
+ if (Object::ClassIDToRTTI (Super::GetClassIDStatic ()) == NULL) \
+ Super::RegisterClass (); \
+ Object::RegisterClass (ClassID (x), Super::GetClassIDStatic (), #x, sizeof (x), PRODUCE, IsAbstract ()); \
+} \
+int x::GetClassIDVirtualInternal () const { return ClassID (x); }
+
+#define IMPLEMENT_CLASS(x) IMPLEMENT_CLASS_FULL(x, NULL, NULL, NULL)
+#define IMPLEMENT_CLASS_HAS_INIT(x) IMPLEMENT_CLASS_FULL(x, x::InitializeClass, NULL, x::CleanupClass)
+#define IMPLEMENT_CLASS_HAS_POSTINIT(x) IMPLEMENT_CLASS_FULL(x, x::InitializeClass, x::PostInitializeClass, x::CleanupClass)
+#define IMPLEMENT_CLASS_INIT_ONLY(x) IMPLEMENT_CLASS_FULL(x, x::InitializeClass, NULL, NULL)
+
+// Should be placed in every serializable object derived class (DECLARE_OBJECT_SERIALIZE (Transform))
+#if UNITY_EDITOR
+ #define DECLARE_OBJECT_SERIALIZE(x) \
+ static const char* GetTypeString () { return GetClassStringStatic(); } \
+ static bool IsAnimationChannel () { return false; } \
+ static bool MightContainPPtr () { return true; } \
+ static bool AllowTransferOptimization () { return false; } \
+ template<class TransferFunction> void Transfer (TransferFunction& transfer); \
+ virtual void VirtualRedirectTransfer (ProxyTransfer& transfer); \
+ virtual void VirtualRedirectTransfer (SafeBinaryRead& transfer); \
+ virtual void VirtualRedirectTransfer (StreamedBinaryWrite<false>& transfer);\
+ virtual void VirtualRedirectTransfer (StreamedBinaryWrite<true>& transfer);\
+ virtual void VirtualRedirectTransfer (StreamedBinaryRead<false>& transfer); \
+ virtual void VirtualRedirectTransfer (StreamedBinaryRead<true>& transfer); \
+ virtual void VirtualRedirectTransfer (RemapPPtrTransfer& transfer); \
+ virtual void VirtualRedirectTransfer (YAMLRead& transfer); \
+ virtual void VirtualRedirectTransfer (YAMLWrite& transfer);
+#elif SUPPORT_SERIALIZED_TYPETREES
+ #define DECLARE_OBJECT_SERIALIZE(x) \
+ static const char* GetTypeString () { return GetClassStringStatic(); } \
+ static bool IsAnimationChannel () { return false; } \
+ static bool MightContainPPtr () { return true; } \
+ static bool AllowTransferOptimization () { return false; } \
+ template<class TransferFunction> void Transfer (TransferFunction& transfer); \
+ virtual void VirtualRedirectTransfer (ProxyTransfer& transfer); \
+ virtual void VirtualRedirectTransfer (SafeBinaryRead& transfer); \
+ virtual void VirtualRedirectTransfer (StreamedBinaryRead<false>& transfer); \
+ virtual void VirtualRedirectTransfer (StreamedBinaryRead<true>& transfer); \
+ virtual void VirtualRedirectTransfer (StreamedBinaryWrite<false>& transfer); \
+ virtual void VirtualRedirectTransfer (RemapPPtrTransfer& transfer);
+#else
+ #define DECLARE_OBJECT_SERIALIZE(x) \
+ static const char* GetTypeString () { return GetClassStringStatic(); } \
+ static bool IsAnimationChannel () { return false; } \
+ static bool MightContainPPtr () { return true; } \
+ static bool AllowTransferOptimization () { return false; } \
+ template<class TransferFunction> void Transfer (TransferFunction& transfer); \
+ virtual void VirtualRedirectTransfer (ProxyTransfer& transfer); \
+ virtual void VirtualRedirectTransfer (StreamedBinaryRead<false>& transfer); \
+ virtual void VirtualRedirectTransfer (StreamedBinaryWrite<false>& transfer); \
+ virtual void VirtualRedirectTransfer (RemapPPtrTransfer& transfer);
+#endif
+
+// Has to be placed in the cpp file of a serializable class (IMPLEMENT_OBJECT_SERIALIZE (Transform))
+// you also have to #include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+#if UNITY_EDITOR // Editor needs to support swapped endian writing, player doesnt.
+
+#define INSTANTIATE_TEMPLATE_TRANSFER_WITH_DECL(x, decl) \
+template decl void x::Transfer(ProxyTransfer& transfer); \
+template decl void x::Transfer(SafeBinaryRead& transfer); \
+template decl void x::Transfer(StreamedBinaryRead<false>& transfer); \
+template decl void x::Transfer(StreamedBinaryRead<true>& transfer); \
+template decl void x::Transfer(StreamedBinaryWrite<false>& transfer); \
+template decl void x::Transfer(StreamedBinaryWrite<true>& transfer); \
+template decl void x::Transfer(RemapPPtrTransfer& transfer); \
+template decl void x::Transfer(YAMLRead& transfer); \
+template decl void x::Transfer(YAMLWrite& transfer);
+
+#define IMPLEMENT_OBJECT_SERIALIZE(x) \
+void x::VirtualRedirectTransfer (ProxyTransfer& transfer) { transfer.Transfer (*this, "Base"); } \
+void x::VirtualRedirectTransfer (SafeBinaryRead& transfer) { SET_ALLOC_OWNER(this); transfer.Transfer (*this, "Base"); } \
+void x::VirtualRedirectTransfer (StreamedBinaryRead<false>& transfer) { SET_ALLOC_OWNER(this); transfer.Transfer (*this, "Base"); } \
+void x::VirtualRedirectTransfer (StreamedBinaryRead<true>& transfer) { SET_ALLOC_OWNER(this); transfer.Transfer (*this, "Base"); } \
+void x::VirtualRedirectTransfer (RemapPPtrTransfer& transfer) { transfer.Transfer (*this, "Base"); } \
+void x::VirtualRedirectTransfer (StreamedBinaryWrite<false>& transfer) { transfer.Transfer (*this, "Base"); } \
+void x::VirtualRedirectTransfer (StreamedBinaryWrite<true>& transfer) { transfer.Transfer (*this, "Base"); } \
+void x::VirtualRedirectTransfer (YAMLRead& transfer) { SET_ALLOC_OWNER(this); transfer.Transfer (*this, "Base"); } \
+void x::VirtualRedirectTransfer (YAMLWrite& transfer) { transfer.Transfer (*this, "Base"); } \
+
+#elif SUPPORT_SERIALIZED_TYPETREES
+#define INSTANTIATE_TEMPLATE_TRANSFER_WITH_DECL(x, decl) \
+template decl void x::Transfer(ProxyTransfer& transfer); \
+template decl void x::Transfer(SafeBinaryRead& transfer); \
+template decl void x::Transfer(StreamedBinaryRead<false>& transfer); \
+template decl void x::Transfer(StreamedBinaryRead<true>& transfer); \
+template decl void x::Transfer(StreamedBinaryWrite<false>& transfer); \
+template decl void x::Transfer(RemapPPtrTransfer& transfer);
+
+ #define IMPLEMENT_OBJECT_SERIALIZE(x) \
+ void x::VirtualRedirectTransfer (ProxyTransfer& transfer) { transfer.Transfer (*this, "Base"); } \
+ void x::VirtualRedirectTransfer (SafeBinaryRead& transfer) { SET_ALLOC_OWNER(this); transfer.Transfer (*this, "Base"); } \
+ void x::VirtualRedirectTransfer (StreamedBinaryRead<false>& transfer) { SET_ALLOC_OWNER(this); transfer.Transfer (*this, "Base"); } \
+ void x::VirtualRedirectTransfer (StreamedBinaryRead<true>& transfer) { SET_ALLOC_OWNER(this); transfer.Transfer (*this, "Base"); } \
+ void x::VirtualRedirectTransfer (StreamedBinaryWrite<false>& transfer) { transfer.Transfer (*this, "Base"); } \
+ void x::VirtualRedirectTransfer (RemapPPtrTransfer& transfer) { transfer.Transfer (*this, "Base"); } \
+
+#else
+#define INSTANTIATE_TEMPLATE_TRANSFER_WITH_DECL(x, decl) \
+template decl void x::Transfer(ProxyTransfer& transfer); \
+template decl void x::Transfer(StreamedBinaryRead<false>& transfer); \
+template decl void x::Transfer(StreamedBinaryWrite<false>& transfer); \
+template decl void x::Transfer(RemapPPtrTransfer& transfer);
+
+ #define IMPLEMENT_OBJECT_SERIALIZE(x) \
+ void x::VirtualRedirectTransfer (ProxyTransfer& transfer) { transfer.Transfer (*this, "Base"); } \
+ void x::VirtualRedirectTransfer (StreamedBinaryRead<false>& transfer) { SET_ALLOC_OWNER(this); transfer.Transfer (*this, "Base"); } \
+ void x::VirtualRedirectTransfer (StreamedBinaryWrite<false>& transfer) { transfer.Transfer (*this, "Base"); } \
+ void x::VirtualRedirectTransfer (RemapPPtrTransfer& transfer) { transfer.Transfer (*this, "Base"); } \
+
+#endif
+
+#if UNITY_WIN
+#define EXPORTDLL __declspec(dllexport)
+#elif UNITY_OSX
+#define EXPORTDLL __attribute__((visibility("default")))
+#else
+#define EXPORTDLL
+#endif
+
+#define INSTANTIATE_TEMPLATE_TRANSFER(x) INSTANTIATE_TEMPLATE_TRANSFER_WITH_DECL(x, )
+#define INSTANTIATE_TEMPLATE_TRANSFER_EXPORTED(x) INSTANTIATE_TEMPLATE_TRANSFER_WITH_DECL(x, EXPORTDLL)
+
+
+// Use this to make a Generic C++ GET/SET function: GET_SET (float, Velocity, m_Velocity)
+// Implements GetVelocity, SetVelocity
+#define GET_SET(TYPE,PROP_NAME,VAR_NAME) void Set##PROP_NAME (TYPE val) { VAR_NAME = val; } const TYPE Get##PROP_NAME () const {return (const TYPE)VAR_NAME; }
+#define GET_SET_DIRTY(TYPE,PROP_NAME,VAR_NAME) void Set##PROP_NAME (TYPE val) { VAR_NAME = val; SetDirty(); } const TYPE Get##PROP_NAME () const {return (const TYPE)VAR_NAME; }
+#define GET_SET_COMPARE_DIRTY(TYPE,PROP_NAME,VAR_NAME) void Set##PROP_NAME (TYPE val) { if ((TYPE)VAR_NAME == val) return; VAR_NAME = val; SetDirty(); } const TYPE Get##PROP_NAME () const {return (const TYPE)VAR_NAME; }
+
+#endif
diff --git a/Runtime/BaseClasses/RefCounted.h b/Runtime/BaseClasses/RefCounted.h
new file mode 100644
index 0000000..cbe2934
--- /dev/null
+++ b/Runtime/BaseClasses/RefCounted.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoIncludes.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+
+class TrackedReferenceBase
+{
+public:
+ int m_MonoObjectReference;
+
+ TrackedReferenceBase ()
+ {
+ m_MonoObjectReference = 0;
+ }
+
+ ~TrackedReferenceBase ()
+ {
+#if ENABLE_SCRIPTING
+ if (m_MonoObjectReference)
+ {
+ ScriptingObjectPtr target = scripting_gchandle_get_target (m_MonoObjectReference);
+ if (target)
+ {
+ void* nativePointer = 0;
+ MarshallNativeStructIntoManaged(nativePointer,target);
+ target = SCRIPTING_NULL;
+ }
+
+ scripting_gchandle_free (m_MonoObjectReference);
+ m_MonoObjectReference = 0;
+ }
+#endif
+ }
+};
diff --git a/Runtime/BaseClasses/SupportedMessageOptimization.h b/Runtime/BaseClasses/SupportedMessageOptimization.h
new file mode 100644
index 0000000..b5a6501
--- /dev/null
+++ b/Runtime/BaseClasses/SupportedMessageOptimization.h
@@ -0,0 +1,17 @@
+#ifndef SUPPORTEDMESSAGEOPTIMIZATION_H
+#define SUPPORTEDMESSAGEOPTIMIZATION_H
+
+enum
+{
+ kHasCollisionStay = 1 << 0,
+ kHasCollisionEnterExit = 1 << 1,
+ kWantsCollisionData = 1 << 2,
+ kSupportsTransformChanged = 1 << 3,
+ kHasOnWillRenderObject = 1 << 4,
+ kSupportsVelocityChanged = 1 << 5,
+ kHasOnAnimatorMove = 1 << 6,
+ kHasOnAnimatorIK = 1 << 7,
+ kHasCollision2D = 1 << 8
+};
+
+#endif
diff --git a/Runtime/BaseClasses/Tags.cpp b/Runtime/BaseClasses/Tags.cpp
new file mode 100644
index 0000000..3a56c6f
--- /dev/null
+++ b/Runtime/BaseClasses/Tags.cpp
@@ -0,0 +1,604 @@
+#include "UnityPrefix.h"
+#include "Tags.h"
+#include "ManagerContext.h"
+#include "GameManager.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Utilities/algorithm_utility.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Utilities/GUID.h"
+#include "External/MurmurHash/MurmurHash2.h"
+#include <vector>
+
+static const char* kDefaultSortingLayerName = "Default";
+
+// -------------------------------------------------------------------
+
+
+static Object* GetTagManagerPtr ();
+
+
+typedef std::pair<const std::string, UInt32> StringUInt32Pair;
+typedef std::map<std::string, UInt32, std::less<std::string>, STL_ALLOCATOR(kMemPermanent, StringUInt32Pair) > StringToUnsigned;
+
+
+static StringToUnsigned* gStringToTag;
+static UnsignedToString* gTagToString;
+static int* gTagManagerContainer;
+
+static StringToUnsigned* gStringToMask;
+static std::string gMaskToString[32];
+static std::string gEmpty;
+
+namespace LayerTagManager
+{
+ void StaticInitialize()
+ {
+ gTagManagerContainer = UNITY_NEW_AS_ROOT(int, kMemResource, "LayerTagManager", "");
+ SET_ALLOC_OWNER(gTagManagerContainer);
+ gStringToTag = UNITY_NEW(StringToUnsigned,kMemResource);
+ gTagToString = UNITY_NEW(UnsignedToString,kMemResource);
+ gStringToMask = UNITY_NEW(StringToUnsigned,kMemResource);
+ }
+ void StaticDestroy()
+ {
+ UNITY_DELETE(gStringToTag, kMemResource);
+ UNITY_DELETE(gTagToString, kMemResource);
+ UNITY_DELETE(gStringToMask, kMemResource);
+ for(int i = 0; i < 32; i++)
+ gMaskToString[i] = std::string();
+ gEmpty = std::string();
+ UNITY_DELETE(gTagManagerContainer, kMemResource);
+ }
+}
+
+static RegisterRuntimeInitializeAndCleanup s_LayerTagManagerCallbacks(LayerTagManager::StaticInitialize, LayerTagManager::StaticDestroy);
+
+
+static void RegisterTag (UInt32 tag, const std::string& name);
+
+
+void RegisterTag (UInt32 tag, const std::string& name)
+{
+ SET_ALLOC_OWNER(gTagManagerContainer);
+ if (!gStringToTag->insert (make_pair (name, tag)).second && !name.empty ())
+ LogStringObject ("Default GameObject Tag: " + name + " already registered", GetTagManagerPtr ());
+
+ if (!gTagToString->insert (make_pair (tag, name)).second)
+ LogStringObject ("Default GameObject Tag for name: " + name + " already registered", GetTagManagerPtr ());
+}
+
+// In the editor we might add / remove tags from the gStringToTag array.
+// And we might do this from the loading thread and main thread at the same time.
+
+// In the player this map is completely fixed, thus it even if two threads access the data at the same time, the data always stays constant
+#if UNITY_EDITOR
+
+Mutex gTagToStringMutex;
+
+UInt32 StringToTagAddIfUnavailable (const std::string& tag)
+{
+ Mutex::AutoLock lock(gTagToStringMutex);
+ StringToUnsigned::iterator i = gStringToTag->find (tag);
+ if (i == gStringToTag->end ())
+ {
+ SET_ALLOC_OWNER(gTagManagerContainer);
+ int nextTagID = last_iterator(*gTagToString)->first + 1;
+ gTagToString->insert(make_pair(nextTagID, tag));
+ gStringToTag->insert(make_pair(tag, nextTagID));
+ return nextTagID;
+ }
+ else
+ return i->second;
+}
+
+#endif
+
+
+UInt32 StringToTag (const std::string& tag)
+{
+ #if UNITY_EDITOR
+ Mutex::AutoLock lock(gTagToStringMutex);
+ #endif
+
+ StringToUnsigned::iterator i = gStringToTag->find (tag);
+ if (i == gStringToTag->end ())
+ return -1;
+ else
+ return i->second;
+}
+
+const std::string& TagToString (UInt32 tag)
+{
+ #if UNITY_EDITOR
+ Mutex::AutoLock lock(gTagToStringMutex);
+ #endif
+ UnsignedToString::iterator i = gTagToString->find (tag);
+ if (i == gTagToString->end ())
+ {
+ static std::string empty;
+ return empty;
+ }
+ else
+ return i->second;
+}
+
+UInt32 StringToLayerMask (const std::string& tag)
+{
+ StringToUnsigned::iterator i = gStringToMask->find (tag);
+ if (i == gStringToMask->end ())
+ return 0;
+ else
+ return 1 << i->second;
+}
+
+UInt32 StringToLayer (const std::string& tag)
+{
+ StringToUnsigned::iterator i = gStringToMask->find (tag);
+ if (i == gStringToMask->end ())
+ return -1;
+ else
+ return i->second;
+}
+
+const std::string& LayerToString (UInt32 layer)
+{
+ if (layer >= 32)
+ {
+ ErrorString("Layer index out of bounds");
+ return gEmpty;
+ }
+ return gMaskToString[layer];
+}
+
+const std::string& LayerMaskToString (UInt32 layerMask)
+{
+ Assert (IsPowerOfTwo (layerMask));
+ if (layerMask == 0)
+ return gEmpty;
+ int layer = AnyBitFromMask (layerMask);
+ return gMaskToString[layer];
+}
+
+void RegisterLayer (UInt32 tag, const std::string& name)
+{
+ SET_ALLOC_OWNER(gTagManagerContainer);
+ if (!gStringToMask->insert (make_pair (name, tag)).second && !name.empty ())
+ LogStringObject ("Default GameObject BitMask: " + name + " already registered", GetTagManagerPtr ());
+
+ if (gMaskToString[tag].empty ())
+ gMaskToString[tag] = name;
+ else
+ LogStringObject ("Default GameObject BitMask for name: " + name + " already registered", GetTagManagerPtr ());
+}
+
+UnsignedToString GetTags ()
+{
+ // Cull out all empty string tags. (The user is allowed to add those but we dont want them to show up!)
+ UnsignedToString tags;
+ for (UnsignedToString::iterator i=gTagToString->begin ();i != gTagToString->end ();i++)
+ {
+ if (!i->second.empty ())
+ tags.insert (make_pair (i->first, i->second));
+ }
+ return tags;
+}
+
+
+// -------------------------------------------------------------------
+
+struct SortingLayerEntry
+{
+ DECLARE_SERIALIZE_NO_PPTR (SortingLayerEntry)
+
+ SortingLayerEntry() : userID(1), uniqueID(1), locked(false) { }
+ UnityStr name;
+ UInt32 userID;
+ UInt32 uniqueID;
+ bool locked;
+};
+
+template<class TransferFunc>
+void SortingLayerEntry::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (name);
+ TRANSFER (userID);
+ TRANSFER (uniqueID);
+ TRANSFER_EDITOR_ONLY (locked);
+ transfer.Align();
+}
+
+class TagManager : public GlobalGameManager
+{
+public:
+ DECLARE_OBJECT_SERIALIZE (TagManager)
+ REGISTER_DERIVED_CLASS (TagManager, GlobalGameManager)
+
+ TagManager (MemLabelId label, ObjectCreationMode mode) : Super(label, mode), m_DefaultLayerIndex(0) {}
+
+ bool ShouldIgnoreInGarbageDependencyTracking () { return true; }
+
+ virtual void Update () { }
+ // virtual ~TagManager () { } declared-by-macro
+
+ void AddDefaultLayerIfNeeded();
+ void FindDefaultLayerIndex();
+
+ std::vector<SortingLayerEntry> m_SortingLayers;
+ int m_DefaultLayerIndex;
+};
+
+TagManager::~TagManager ()
+{
+}
+
+void TagManager::FindDefaultLayerIndex()
+{
+ m_DefaultLayerIndex = 0;
+ for (size_t i = 0, n = m_SortingLayers.size(); i != n; ++i)
+ {
+ if (m_SortingLayers[i].userID == 0)
+ {
+ m_DefaultLayerIndex = i;
+ break;
+ }
+ }
+}
+
+void TagManager::AddDefaultLayerIfNeeded()
+{
+ // do we have a default layer?
+ for (size_t i = 0, n = m_SortingLayers.size(); i != n; ++i)
+ {
+ if (m_SortingLayers[i].userID == 0)
+ return;
+ }
+
+ // no default layer, add one in front
+ SortingLayerEntry layer;
+ layer.name = kDefaultSortingLayerName;
+ layer.uniqueID = 0;
+ layer.userID = 0;
+ m_SortingLayers.insert(m_SortingLayers.begin(), layer);
+ m_DefaultLayerIndex = 0;
+}
+
+
+
+void RegisterDefaultTagsAndLayerMasks ()
+{
+ SET_ALLOC_OWNER(gTagManagerContainer);
+ gStringToTag->clear (); gTagToString->clear ();
+ gStringToMask->clear ();
+ for (int i=0;i<32;i++)
+ gMaskToString[i].clear ();
+ if (GetTagManagerPtr())
+ {
+ TagManager& tags = (TagManager&)GetTagManager();
+ tags.m_SortingLayers.clear();
+ // add "Default" sorting layer
+ tags.m_SortingLayers.push_back(SortingLayerEntry());
+ SortingLayerEntry& layer = tags.m_SortingLayers[0];
+ layer.name = kDefaultSortingLayerName;
+ layer.userID = 0;
+ layer.uniqueID = 0;
+ tags.m_DefaultLayerIndex = 0;
+ }
+
+ RegisterTag (kUntagged, "Untagged");
+ RegisterTag (kRespawnTag, "Respawn");
+ RegisterTag (kFinishTag, "Finish");
+ RegisterTag (kEditorOnlyTag, "EditorOnly");
+ RegisterTag (kMainCameraTag, "MainCamera");
+ RegisterTag (kGameControllerTag, "GameController");
+ RegisterTag (kPlayerTag, "Player");
+
+ RegisterLayer (kDefaultLayer, "Default");
+ RegisterLayer (kNoFXLayer, "TransparentFX");
+ RegisterLayer (kIgnoreRaycastLayer, "Ignore Raycast");
+ RegisterLayer (kWaterLayer, "Water");
+}
+
+template<class TransferFunction>
+void TagManager::Transfer (TransferFunction& transfer)
+{
+ std::vector<UnityStr> tags;
+
+ // Build tags array
+ if (transfer.IsWriting ())
+ {
+ UnsignedToString::iterator begin = gTagToString->lower_bound (kFirstUserTag);
+ UnsignedToString::iterator end = gTagToString->upper_bound (kLastUserTag);
+ for (UnsignedToString::iterator i=begin;i != end;i++)
+ tags.push_back (i->second);
+ if (tags.empty () || !tags.back ().empty ())
+ tags.push_back ("");
+ }
+ else if (transfer.IsReading ())
+ {
+ RegisterDefaultTagsAndLayerMasks ();
+ }
+
+ TRANSFER_SIMPLE (tags);
+
+ // Register tags we've read (if there actually was tag data in the stream).
+ if (transfer.DidReadLastProperty ())
+ {
+ for (int i=0;i<tags.size ();i++)
+ RegisterTag (kFirstUserTag + i, tags[i]);
+ }
+
+ // Build bitnames array
+ UnityStr bitnames[32];
+ for (int i=0;i<32;i++)
+ {
+ char name[64];
+ bool editable = i >= kUserLayer;
+ if (editable)
+ sprintf (name, "User Layer %d", i);
+ else
+ sprintf (name, "Builtin Layer %d", i);
+
+ bitnames[i] = LayerToString (i);
+ transfer.Transfer (bitnames[i], name, editable ? kNoTransferFlags : kNotEditableMask);
+
+ if (transfer.DidReadLastProperty ())
+ {
+ if (i >= kUserLayer)
+ RegisterLayer (i, bitnames[i]);
+ }
+ }
+
+ // Sorting layers
+ TRANSFER (m_SortingLayers);
+ if (!transfer.IsWriting () && transfer.IsReading())
+ {
+ AddDefaultLayerIfNeeded();
+ FindDefaultLayerIndex();
+ }
+}
+
+IMPLEMENT_CLASS (TagManager)
+IMPLEMENT_OBJECT_SERIALIZE (TagManager)
+
+Object& GetTagManager ()
+{
+ return GetManagerFromContext (ManagerContext::kTagManager);
+}
+
+static Object* GetTagManagerPtr ()
+{
+ return GetManagerPtrFromContext (ManagerContext::kTagManager);
+}
+
+
+
+// -------------------------------------------------------------------
+
+
+
+UnityStr GetSortingLayerName(int index)
+{
+ TagManager& tags = (TagManager&)GetTagManager();
+ if (index < 0 || index >= tags.m_SortingLayers.size())
+ return UnityStr();
+ return tags.m_SortingLayers[index].name;
+}
+
+UnityStr GetSortingLayerNameFromValue(int layerValue)
+{
+ TagManager& tags = (TagManager&)GetTagManager();
+ int index = tags.m_DefaultLayerIndex + layerValue;
+ if (index < 0 || index >= tags.m_SortingLayers.size())
+ return UnityStr();
+ return tags.m_SortingLayers[index].name;
+}
+
+
+int GetSortingLayerUserID(int index)
+{
+ TagManager& tags = (TagManager&)GetTagManager();
+ if (index < 0 || index >= tags.m_SortingLayers.size())
+ return 0;
+ return tags.m_SortingLayers[index].userID;
+}
+
+int GetSortingLayerUniqueIDFromValue(int layerValue)
+{
+ TagManager& tags = (TagManager&)GetTagManager();
+ int index = tags.m_DefaultLayerIndex + layerValue;
+ if (index < 0 || index >= tags.m_SortingLayers.size())
+ return 0;
+ return tags.m_SortingLayers[index].uniqueID;
+}
+
+
+int GetSortingLayerUserIDFromValue(int layerValue)
+{
+ TagManager& tags = (TagManager&)GetTagManager();
+ int index = tags.m_DefaultLayerIndex + layerValue;
+ if (index < 0 || index >= tags.m_SortingLayers.size())
+ return 0;
+ return tags.m_SortingLayers[index].userID;
+}
+
+int GetSortingLayerIndexFromValue(int layerValue)
+{
+ TagManager& tags = (TagManager&)GetTagManager();
+ int index = tags.m_DefaultLayerIndex + layerValue;
+ if (index < 0 || index >= tags.m_SortingLayers.size())
+ index = 0;
+ return index;
+}
+
+
+
+int GetSortingLayerUniqueID(int index)
+{
+ TagManager& tags = (TagManager&)GetTagManager();
+ Assert (index >= 0 && index < tags.m_SortingLayers.size());
+ return tags.m_SortingLayers[index].uniqueID;
+}
+
+UnityStr GetSortingLayerNameFromUniqueID(int id)
+{
+ if (id == 0)
+ return UnityStr(kDefaultSortingLayerName);
+
+ TagManager& tags = (TagManager&)GetTagManager();
+ for (size_t i = 0; i < tags.m_SortingLayers.size(); ++i)
+ if (tags.m_SortingLayers[i].uniqueID == id)
+ return tags.m_SortingLayers[i].name;
+
+ return "<unknown layer>";
+}
+
+int GetSortingLayerValueFromUniqueID(int id)
+{
+ if (id == 0)
+ return 0;
+
+ TagManager& tags = (TagManager&)GetTagManager();
+ for (size_t i = 0; i < tags.m_SortingLayers.size(); ++i)
+ if (tags.m_SortingLayers[i].uniqueID == id)
+ return i - tags.m_DefaultLayerIndex;
+
+ return 0; // unknown layer: treat as if no layer is assigned
+}
+
+int GetSortingLayerValueFromUserID(int id)
+{
+ if (id == 0)
+ return 0;
+
+ TagManager& tags = (TagManager&)GetTagManager();
+ for (size_t i = 0; i < tags.m_SortingLayers.size(); ++i)
+ if (tags.m_SortingLayers[i].userID == id)
+ return i - tags.m_DefaultLayerIndex;
+
+ return 0; // unknown layer: treat as if no layer is assigned
+}
+
+int GetSortingLayerValueFromName(const UnityStr& name)
+{
+ TagManager& tags = (TagManager&)GetTagManager();
+ if (name.empty())
+ return 0;
+
+ for (size_t i = 0; i < tags.m_SortingLayers.size(); ++i)
+ if (tags.m_SortingLayers[i].name == name)
+ return i - tags.m_DefaultLayerIndex;
+
+ return 0; // unknown layer: treat as default layer
+}
+
+
+#if UNITY_EDITOR
+void AddSortingLayer()
+{
+ TagManager& tags = (TagManager&)GetTagManager();
+ SortingLayerEntry s;
+
+ // internal ID should be quite unique; generate a GUID and hash to and integer
+ UnityGUID guid;
+ guid.Init();
+ s.uniqueID = MurmurHash2A(&guid.data, sizeof(guid.data), 0x8f37154b);
+ s.uniqueID |= 1; // make sure it's never zero
+
+ // user-visible ID: smallest unused one
+ int id = 1;
+ while (true)
+ {
+ bool gotIt = false;
+ for (size_t i = 0; i < tags.m_SortingLayers.size(); ++i)
+ {
+ if (tags.m_SortingLayers[i].userID == id)
+ {
+ gotIt = true;
+ break;
+ }
+ }
+ if (!gotIt)
+ break;
+ ++id;
+ }
+
+ s.name = Format("New Layer %d", id);
+ s.userID = id;
+ s.locked = false;
+
+ tags.m_SortingLayers.push_back (s);
+ tags.SetDirty();
+}
+
+
+void UpdateSortingLayersOrder()
+{
+ TagManager& tags = (TagManager&)GetTagManager();
+ tags.FindDefaultLayerIndex();
+
+ vector<SInt32> objs;
+ Object::FindAllDerivedObjects (ClassID (Renderer), &objs);
+ for (size_t i = 0, n = objs.size(); i != n; ++i)
+ {
+ Renderer* r = PPtr<Renderer> (objs[i]);
+ r->SetupSortingOverride();
+ }
+}
+
+
+void SetSortingLayerName(int index, const std::string& name)
+{
+ TagManager& tags = (TagManager&)GetTagManager();
+ Assert (index >= 0 && index < tags.m_SortingLayers.size());
+ tags.m_SortingLayers[index].name = name;
+ tags.SetDirty();
+}
+
+void SwapSortingLayers(int idx1, int idx2)
+{
+ TagManager& tags = (TagManager&)GetTagManager();
+ Assert (idx1 >= 0 && idx1 < tags.m_SortingLayers.size());
+ Assert (idx2 >= 0 && idx2 < tags.m_SortingLayers.size());
+ std::swap(tags.m_SortingLayers[idx1], tags.m_SortingLayers[idx2]);
+ UpdateSortingLayersOrder();
+ tags.SetDirty();
+}
+
+
+void SetSortingLayerLocked(int index, bool locked)
+{
+ TagManager& tags = (TagManager&)GetTagManager();
+ Assert (index >= 0 && index < tags.m_SortingLayers.size());
+ tags.m_SortingLayers[index].locked = locked;
+ tags.SetDirty();
+}
+
+bool GetSortingLayerLocked(int index)
+{
+ TagManager& tags = (TagManager&)GetTagManager();
+ if (index < 0 || index >= tags.m_SortingLayers.size())
+ return false;
+ return tags.m_SortingLayers[index].locked;
+}
+
+bool IsSortingLayerDefault(int index)
+{
+ TagManager& tags = (TagManager&)GetTagManager();
+ return index == tags.m_DefaultLayerIndex;
+}
+
+
+int g_LockedPickingLayers = 0;
+
+#endif // #if UNITY_EDITOR
+
+
+int GetSortingLayerCount()
+{
+ TagManager& tags = (TagManager&)GetTagManager();
+ return tags.m_SortingLayers.size();
+}
diff --git a/Runtime/BaseClasses/Tags.h b/Runtime/BaseClasses/Tags.h
new file mode 100644
index 0000000..9d790a9
--- /dev/null
+++ b/Runtime/BaseClasses/Tags.h
@@ -0,0 +1,104 @@
+#pragma once
+
+#include <map>
+#include <string>
+#include "Runtime/Misc/Allocator.h"
+
+class Object;
+
+enum BitMasks
+{
+ // Can't modify these without breaking backwards compatibility!
+ kDefaultLayer = 0,
+ kNoFXLayer = 1,
+ kIgnoreRaycastLayer = 2,
+ kIgnoreCollisionLayer = 3,
+ kWaterLayer = 4,
+ kNumLayers = 32,
+
+ kDefaultLayerMask = 1 << kDefaultLayer,
+ kNoFXLayerMask = 1 << kNoFXLayer,
+ kIgnoreRaycastMask = 1 << kIgnoreRaycastLayer,
+ kIgnoreCollisionMask = 1 << kIgnoreCollisionLayer,
+ kPreUnity2UnusedLayer = 1 << 5,
+
+ kUserLayer = 8,
+};
+
+enum Tags
+{
+ kUntagged = 0,
+ kRespawnTag = 1,
+ kFinishTag = 2,
+ kEditorOnlyTag = 3,
+ kMainCameraTag = 5,
+ kPlayerTag = 6,
+ kGameControllerTag = 7,
+ kFirstUserTag = 20000,
+ kLastUserTag = 30000,
+ kUndefinedTag = -1
+};
+
+// converts tag to string
+UInt32 StringToTag (const std::string& tag);
+UInt32 StringToTagAddIfUnavailable (const std::string& name);
+const std::string& TagToString (UInt32 tag);
+
+// Converts between layer [0..31] and string
+UInt32 StringToLayerMask (const std::string& layerName);
+const std::string& LayerToString (UInt32 layer);
+UInt32 StringToLayer (const std::string& layer);
+// Converts a layer mask (1 << [0..31]) to a string
+const std::string& LayerMaskToString (UInt32 mask);
+
+void RegisterLayer (UInt32 layer, const std::string& name);
+
+typedef std::pair<const UInt32, std::string> UInt32StringPair;
+typedef std::map<UInt32, std::string, std::less<UInt32>, STL_ALLOCATOR(kMemPermanent, UInt32StringPair) > UnsignedToString;
+UnsignedToString GetTags ();
+
+void RegisterDefaultTagsAndLayerMasks ();
+
+Object& GetTagManager ();
+
+
+// -------------------------------------------------------------------
+// Global sorting layers:
+//
+// Defined globally, and can be reordered in the inspector. The drawing order is as shown in the inspector.
+// Internally each global sorting layer has "unique ID" (GUID hashed into an int), and in-editor Renderers that want to
+// use them refer to the layer by this ID.
+//
+// @TODO:
+// * Do we need this "user friendly ID"?
+
+int GetSortingLayerCount();
+UnityStr GetSortingLayerName(int index);
+UnityStr GetSortingLayerNameFromUniqueID(int id);
+int GetSortingLayerUniqueID(int index);
+int GetSortingLayerUserID(int index);
+
+UnityStr GetSortingLayerNameFromValue(int layerValue);
+int GetSortingLayerUserIDFromValue(int layerValue);
+int GetSortingLayerUniqueIDFromValue(int layerValue);
+int GetSortingLayerIndexFromValue(int layerValue);
+
+// these return final sorting layer values
+// (i.e. zero is always "default" - the returned value can be negative or positive)
+int GetSortingLayerValueFromUniqueID(int id);
+int GetSortingLayerValueFromUserID(int id);
+int GetSortingLayerValueFromName(const UnityStr& name);
+
+
+#if UNITY_EDITOR
+void SetSortingLayerName(int index, const std::string& name);
+void AddSortingLayer();
+void UpdateSortingLayersOrder();
+void SetSortingLayerLocked(int index, bool locked);
+bool GetSortingLayerLocked(int index);
+bool IsSortingLayerDefault(int index);
+void SwapSortingLayers(int idx1, int idx2);
+
+extern int g_LockedPickingLayers;
+
+#endif // #if UNITY_EDITOR
diff --git a/Runtime/BaseClasses/TagsTests.cpp b/Runtime/BaseClasses/TagsTests.cpp
new file mode 100644
index 0000000..38085d4
--- /dev/null
+++ b/Runtime/BaseClasses/TagsTests.cpp
@@ -0,0 +1,109 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "Tags.h"
+#include "Runtime/Testing/Testing.h"
+
+SUITE (TagsTests)
+{
+ TEST (StringToTag_TagToString_WithEmptyString_IsIdentityOperation)
+ {
+ CHECK_EQUAL ("", TagToString (StringToTag ("")));
+ }
+
+ TEST (StringToTag_TagToString_WithDefaultTag_IsIdentityOperation)
+ {
+ CHECK_EQUAL ("Untagged", TagToString (StringToTag ("Untagged")));
+ }
+
+# if UNITY_EDITOR
+ TEST (SortingLayer_UserID_Works)
+ {
+ CHECK_EQUAL (1, GetSortingLayerCount()); // only default layer initially
+
+ // add 3 layers
+ AddSortingLayer();
+ AddSortingLayer();
+ AddSortingLayer();
+ SetSortingLayerName(1, "A");
+ SetSortingLayerName(2, "B");
+ SetSortingLayerName(3, "C");
+ const int idA = GetSortingLayerUniqueID(1);
+ const int idB = GetSortingLayerUniqueID(2);
+ const int idC = GetSortingLayerUniqueID(3);
+
+ // now the order is: Default, A, B, C
+
+ // they should get 1,2,3 user IDs assigned
+ CHECK_EQUAL(1, GetSortingLayerUserID(1));
+ CHECK_EQUAL(2, GetSortingLayerUserID(2));
+ CHECK_EQUAL(3, GetSortingLayerUserID(3));
+ CHECK_EQUAL(0, GetSortingLayerUserIDFromValue(0));
+ CHECK_EQUAL(1, GetSortingLayerUserIDFromValue(1));
+ CHECK_EQUAL(2, GetSortingLayerUserIDFromValue(2));
+ CHECK_EQUAL(3, GetSortingLayerUserIDFromValue(3));
+
+ // find their values by user IDs
+ CHECK_EQUAL(1, GetSortingLayerValueFromUserID(1));
+ CHECK_EQUAL(2, GetSortingLayerValueFromUserID(2));
+ CHECK_EQUAL(3, GetSortingLayerValueFromUserID(3));
+
+ // find their values by names
+ CHECK_EQUAL(1, GetSortingLayerValueFromName("A"));
+ CHECK_EQUAL(2, GetSortingLayerValueFromName("B"));
+ CHECK_EQUAL(3, GetSortingLayerValueFromName("C"));
+
+ // check all the above for default layer
+ CHECK_EQUAL(0, GetSortingLayerUserID(0));
+ CHECK_EQUAL(0, GetSortingLayerValueFromUserID(0));
+ CHECK_EQUAL(0, GetSortingLayerValueFromName(""));
+ CHECK_EQUAL(0, GetSortingLayerValueFromName("Default"));
+ CHECK_EQUAL(0, GetSortingLayerValueFromUniqueID(0));
+
+ // reorder layers into: B, A, Default, C
+ SwapSortingLayers (0, 2);
+
+ // check user IDs
+ CHECK_EQUAL(2, GetSortingLayerUserID(0)); // B
+ CHECK_EQUAL(1, GetSortingLayerUserID(1));
+ CHECK_EQUAL(3, GetSortingLayerUserID(3));
+
+ CHECK_EQUAL(2, GetSortingLayerUserIDFromValue(-2)); // B
+ CHECK_EQUAL(1, GetSortingLayerUserIDFromValue(-1)); // A
+ CHECK_EQUAL(0, GetSortingLayerUserIDFromValue(0)); // Default
+ CHECK_EQUAL(3, GetSortingLayerUserIDFromValue(1)); // C
+
+ // find values by names
+ CHECK_EQUAL(-2, GetSortingLayerValueFromName("B"));
+ CHECK_EQUAL(-1, GetSortingLayerValueFromName("A"));
+ CHECK_EQUAL(1, GetSortingLayerValueFromName("C"));
+
+ // check all the above for default layer
+ CHECK_EQUAL(0, GetSortingLayerUserID(2));
+ CHECK_EQUAL(0, GetSortingLayerValueFromUserID(0));
+ CHECK_EQUAL(0, GetSortingLayerValueFromName(""));
+ CHECK_EQUAL(0, GetSortingLayerValueFromName("Default"));
+ CHECK_EQUAL(0, GetSortingLayerValueFromUniqueID(0));
+
+ RegisterDefaultTagsAndLayerMasks (); // cleanup
+ }
+# endif // if UNITY_EDITOR
+
+#if UNITY_EDITOR
+
+ TEST (StringToTagAddIfUnavailable_WithNewTag_SetsUpMappings)
+ {
+ UInt32 tag = StringToTagAddIfUnavailable ("foobar");
+
+ CHECK_EQUAL (tag, StringToTag ("foobar"));
+ CHECK_EQUAL ("foobar", TagToString (tag));
+
+ // Cleanup.
+ RegisterDefaultTagsAndLayerMasks ();
+ }
+
+#endif // UNITY_EDITOR
+}
+
+#endif // ENABLE_UNIT_TESTS
diff --git a/Runtime/Camera/BaseRenderer.cpp b/Runtime/Camera/BaseRenderer.cpp
new file mode 100644
index 0000000..a94c840
--- /dev/null
+++ b/Runtime/Camera/BaseRenderer.cpp
@@ -0,0 +1,158 @@
+#include "UnityPrefix.h"
+#include "BaseRenderer.h"
+#include "Runtime/Shaders/Material.h"
+#include "UnityScene.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Shaders/MaterialProperties.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "External/shaderlab/Library/subshader.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "External/MurmurHash/MurmurHash2.h"
+
+using namespace Unity;
+
+
+BaseRenderer::BaseRenderer (RendererType type)
+: m_RendererType(type)
+, m_LightmapST(1.0f,1.0f,0.0f,0.0f)
+, m_LightmapIndex (0xFF)
+, m_CastShadows(true)
+, m_ReceiveShadows(true)
+, m_IsVisibleInScene(false)
+, m_CustomProperties(NULL)
+, m_CustomPropertiesHash(0)
+, m_TransformDirty(true)
+, m_BoundsDirty(true)
+, m_GlobalLayeringData(GlobalLayeringDataCleared())
+{
+ Assert(type <= 0xFF);
+ #if UNITY_EDITOR
+ m_ScaleInLightmap = 1.0f;
+ #endif
+}
+
+BaseRenderer::~BaseRenderer ()
+{
+}
+
+void BaseRenderer::GetLocalAABB (AABB& aabb)
+{
+ GetTransformInfo (); // updates if needed
+ aabb = m_TransformInfo.localAABB;
+}
+
+void BaseRenderer::GetWorldAABB (AABB& aabb)
+{
+ GetTransformInfo (); // updates if needed
+ aabb = m_TransformInfo.worldAABB;
+}
+
+
+int BaseRenderer::GetLightmapIndexInt() const
+{
+ if (m_LightmapIndex == 0xFF)
+ return -1;
+ else
+ return m_LightmapIndex;
+}
+
+void BaseRenderer::SetLightmapIndexIntNoDirty(int index)
+{
+ if (index == -1)
+ m_LightmapIndex = 0xFF;
+ else if (index < 0 || index > 0xFF)
+ {
+ m_LightmapIndex = 0xFF;
+ ErrorString("Lightmap index must be less than 256");
+ }
+ else
+ m_LightmapIndex = index;
+}
+
+// Treats objects that actually _use_ lightmaps as lightmapped.
+bool BaseRenderer::IsLightmappedForRendering() const
+{
+ // Special indices:
+ // 0xFF: object does not use lightmaps
+ // 0xFE: object only influences lightmaps, but does not use them itself
+
+ return m_LightmapIndex != 0xFF && m_LightmapIndex != 0xFE;
+}
+
+// Treats objects that _influence_ lightmaps as lightmapped.
+bool BaseRenderer::IsLightmappedForShadows() const
+{
+ return m_LightmapIndex != 0xFF;
+}
+
+bool operator == (const TransformInfo& a, const TransformInfo& b){
+ return a.invScale == b.invScale
+ && a.localAABB.GetCenter() == b.localAABB.GetCenter()
+ && a.localAABB.GetExtent() == b.localAABB.GetExtent()
+ && a.transformType == b.transformType
+ && a.worldAABB.GetCenter() == b.worldAABB.GetCenter()
+ && a.worldAABB.GetExtent() == b.worldAABB.GetExtent()
+ && a.worldMatrix.GetAxisX() == b.worldMatrix.GetAxisX();
+}
+
+
+void BaseRenderer::ComputeCustomPropertiesHash()
+{
+ if (m_CustomProperties)
+ {
+ const float* buf = m_CustomProperties->GetBufferBegin();
+ const float* bufEnd = m_CustomProperties->GetBufferEnd();
+ m_CustomPropertiesHash = MurmurHash2A (buf, (const UInt8*)bufEnd - (const UInt8*)buf, 0x9747b28c);
+ }
+ else
+ {
+ m_CustomPropertiesHash = 0;
+ }
+}
+
+
+void BaseRenderer::ApplyCustomProperties (Unity::Material& mat, Shader* shader, int subshaderIndex) const
+{
+ if (!m_CustomProperties)
+ return;
+
+ // Hopefully most of per-instance custom properties only go into shader constants; those
+ // are applied later on in BeforeDrawCall. This is a fast path since it does not involve changing
+ // the material. Some properties however might affect fixed function state (alpha test reference, texture
+ // combiner colors, fixed function material etc.), those need to be applied here.
+
+ const dynamic_array<int>& fixedFunctionProps = shader->GetShaderLabShader()->GetSubShader(subshaderIndex).GetPropsAffectingFF();
+ if (fixedFunctionProps.empty())
+ return; // no props that affect fixed function state, great!
+
+ //@TODO: slow implementation for now just to get this working properly!
+ const MaterialPropertyBlock::Property* curProp = m_CustomProperties->GetPropertiesBegin();
+ const MaterialPropertyBlock::Property* propEnd = m_CustomProperties->GetPropertiesEnd();
+ const float* propBuffer = m_CustomProperties->GetBufferBegin();
+ for (; curProp != propEnd; ++curProp)
+ {
+ if (std::find(fixedFunctionProps.begin(), fixedFunctionProps.end(), curProp->nameIndex) == fixedFunctionProps.end())
+ continue; // this property does not affect fixed function state
+
+ ShaderLab::FastPropertyName name;
+ name.index = curProp->nameIndex;
+ const float* src = &propBuffer[curProp->offset];
+ if (curProp->rows == 1 && curProp->cols == 1)
+ {
+ mat.SetFloat (name, *src);
+ }
+ else if (curProp->rows == 1 && curProp->cols == 4)
+ {
+ mat.SetColor (name, ColorRGBAf(src));
+ }
+ else if (curProp->rows == 4 && curProp->cols == 4)
+ {
+ mat.SetMatrix (name, Matrix4x4f(src));
+ }
+ else
+ {
+ AssertString ("Unknown property dimensions");
+ }
+ }
+}
diff --git a/Runtime/Camera/BaseRenderer.h b/Runtime/Camera/BaseRenderer.h
new file mode 100644
index 0000000..26350fb
--- /dev/null
+++ b/Runtime/Camera/BaseRenderer.h
@@ -0,0 +1,148 @@
+#ifndef BASE_RENDERER_H
+#define BASE_RENDERER_H
+
+#include "Runtime/Math/Vector4.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Modules/ExportModules.h"
+#include "Runtime/Camera/RenderLoops/GlobalLayeringData.h"
+
+namespace Unity { class Material; }
+class ChannelAssigns;
+using namespace Unity;
+class AABB;
+class Matrix4x4f;
+class Vector3f;
+class Quaternionf;
+class MaterialPropertyBlock;
+class Shader;
+class Renderer;
+template<class T> class PPtr;
+
+enum RendererType {
+ kRendererUnknown,
+
+ kRendererMesh,
+ kRendererSkinnedMesh,
+ kRendererCloth,
+ kRendererSprite,
+
+ kRendererParticle,
+ kRendererTrail,
+ kRendererLine,
+ kRendererParticleSystem,
+
+ kRendererIntermediate,
+
+ kRendererTypeCount
+};
+
+struct BatchInstanceData
+{
+ Matrix4x4f xform; // 64
+ Renderer* renderer; // 4
+ int subsetIndex; // 4
+ int xformType; // 4
+ int dummy; // 4 byte padding
+};
+
+struct TransformInfo
+{
+ Matrix4x4f worldMatrix; // 64
+ AABB worldAABB; // 24
+ AABB localAABB; // 24 used by LightManager and Shadows
+ float invScale; // 4
+ TransformType transformType; // 4
+};
+
+// Abstract base class for renderers.
+class EXPORT_COREMODULE BaseRenderer {
+public:
+ BaseRenderer(RendererType type);
+ virtual ~BaseRenderer();
+
+ // The main Render Function. Implement this in order to draw the graphics
+ // When this is called, the correct material and transform have been set up.
+ virtual void Render (int materialIndex, const ChannelAssigns& channels) = 0;
+
+ void GetWorldAABB( AABB& result );
+ const TransformInfo& GetTransformInfo ();
+ void GetLocalAABB( AABB& result );
+
+ virtual void UpdateTransformInfo() = 0;
+
+ virtual void RendererBecameVisible() { m_IsVisibleInScene = true; }
+ virtual void RendererBecameInvisible() { m_IsVisibleInScene = false; }
+ virtual int GetLayer() const = 0;
+
+ virtual float GetSortingFudge () const { return 0.0f; }
+
+ virtual int GetMaterialCount() const = 0;
+ virtual PPtr<Material> GetMaterial(int i) const = 0;
+ virtual int GetSubsetIndex(int i) const { return i; }
+ virtual int GetStaticBatchIndex() const { return 0; }
+ virtual UInt32 GetMeshIDSmall() const { return 0; }
+
+ RendererType GetRendererType() const { return static_cast<RendererType>(m_RendererType); }
+
+ UInt32 GetLayerMask() const { return 1<<GetLayer(); }
+
+ bool GetCastShadows() const { return m_CastShadows; }
+ bool GetReceiveShadows() const { return m_ReceiveShadows; }
+
+ void ApplyCustomProperties(Unity::Material& mat, Shader* shader, int subshaderIndex) const;
+
+ const Vector4f& GetLightmapST() const { return m_LightmapST; }
+ // If the renderer's mesh is batched, UVs were already transformed by lightmapST, so return identity transform.
+ // The static batch index equal to 0 means there is no batched mesh, i.e. the renderer is not batched.
+ const Vector4f GetLightmapSTForRendering () const { return GetStaticBatchIndex() == 0 ? m_LightmapST : Vector4f(1,1,0,0); }
+
+ UInt8 GetLightmapIndex() const { return m_LightmapIndex; }
+ int GetLightmapIndexInt() const;
+
+ void SetLightmapIndexIntNoDirty(int index);
+
+ bool IsLightmappedForRendering() const;
+ bool IsLightmappedForShadows() const;
+
+ #if UNITY_EDITOR
+ float GetScaleInLightmap() const { return m_ScaleInLightmap; }
+ void SetScaleInLightmap(float scale) { m_ScaleInLightmap = scale; }
+ #endif
+
+ void ComputeCustomPropertiesHash();
+ UInt32 GetCustomPropertiesHash() const { return m_CustomPropertiesHash; }
+ const MaterialPropertyBlock* GetCustomProperties() const { return m_CustomProperties; }
+
+
+ GlobalLayeringData GetGlobalLayeringData () const { return m_GlobalLayeringData; }
+ void SetGlobalLayeringData (GlobalLayeringData data) { m_GlobalLayeringData = data; }
+protected:
+ Vector4f m_LightmapST; ///< Lightmap tiling and offset
+ UInt8 m_RendererType; // enum RendererType
+ UInt8 m_LightmapIndex;
+ bool m_CastShadows;
+ bool m_ReceiveShadows;
+ bool m_IsVisibleInScene;
+#if UNITY_EDITOR
+ float m_ScaleInLightmap; ///< A multiplier to object's area used in atlasing
+#endif
+ bool m_TransformDirty;
+ bool m_BoundsDirty;
+ TransformInfo m_TransformInfo;
+ MaterialPropertyBlock* m_CustomProperties;
+ UInt32 m_CustomPropertiesHash;
+ GlobalLayeringData m_GlobalLayeringData;
+};
+
+inline const TransformInfo& BaseRenderer::GetTransformInfo()
+{
+ if (m_TransformDirty || m_BoundsDirty)
+ {
+ UpdateTransformInfo();
+ m_TransformDirty = false;
+ m_BoundsDirty = false;
+ }
+ return m_TransformInfo;
+}
+#endif
diff --git a/Runtime/Camera/Camera.cpp b/Runtime/Camera/Camera.cpp
new file mode 100644
index 0000000..ef5fa4c
--- /dev/null
+++ b/Runtime/Camera/Camera.cpp
@@ -0,0 +1,2340 @@
+#include "UnityPrefix.h"
+#include "Camera.h"
+#include "Runtime/Graphics/GraphicsHelper.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "RenderLoops/RenderLoop.h"
+#include "UnityScene.h"
+#include "SceneSettings.h"
+#include "Runtime/Geometry/Ray.h"
+#include "Culler.h"
+#include "ImageFilters.h"
+#include "Runtime/Shaders/Material.h"
+#include "RenderSettings.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "RenderManager.h"
+#include "Skybox.h"
+#include "Flare.h"
+#include "Light.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Camera/RenderLayers/GUILayer.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "CameraUtil.h"
+#include "CameraCullingParameters.h"
+#include "Runtime/Graphics/CubemapTexture.h"
+#include "Runtime/Graphics/RenderBufferManager.h"
+#include "IntermediateRenderer.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+#include "Runtime/Misc/QualitySettings.h"
+#include "RenderLoops/ReplacementRenderLoop.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Misc/PlayerSettings.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/Shaders/ShaderKeywords.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Configuration/UnityConfigure.h"
+#include "LODGroupManager.h"
+#include "ShadowCulling.h"
+#include "External/Umbra/builds/interface/runtime/umbraQuery.hpp"
+#include "External/Umbra/builds/interface/runtime/umbraTome.hpp"
+#include "Runtime/Camera/RenderLoops/BuiltinShaderParamUtility.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Graphics/RenderSurface.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "Runtime/Interfaces/ITerrainManager.h"
+
+#if UNITY_EDITOR
+#include "Editor/Platform/Interface/EditorWindows.h"
+#include "Editor/Platform/Interface/RepaintController.h"
+#endif
+
+///@TODO: Ensure that we use cullingparameters.renderingPath, otherwise potential inconsistency when switching renderpath after culling.
+RenderingPath CalculateRenderingPath (RenderingPath rp);
+
+using namespace std;
+
+static SHADERPROP(CameraDepthTexture);
+static SHADERPROP(CameraDepthNormalsTexture);
+static SHADERPROP(Reflection);
+
+static ShaderKeyword kKeywordSoftParticles = keywords::Create ("SOFTPARTICLES_ON");
+
+/////***@TODO: Write test for stressing multithreaded breaking when OnWillRenderObjects does nasty things...
+
+
+void Camera::InitializeClass ()
+{
+ REGISTER_MESSAGE_VOID (Camera, kTransformChanged, TransformChanged);
+ RegisterAllowNameConversion (Camera::GetClassStringStatic(), "is ortho graphic", "orthographic");
+}
+
+
+Camera::Camera (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_DirtyProjectionMatrix(true)
+, m_DirtyWorldToCameraMatrix(true)
+, m_DirtyWorldToClipMatrix(true)
+, m_DepthTextureMode(0)
+, m_DepthTexture(NULL)
+, m_DepthNormalsTexture(NULL)
+, m_ClearStencilAfterLightingPass(false)
+#if UNITY_EDITOR
+, m_OnlyRenderIntermediateObjects(false)
+, m_IsSceneCamera(false)
+, m_FilterMode (0)
+, m_AnimateMaterials(false)
+, m_AnimateMaterialsTime(0.0f)
+#endif
+{
+ m_RenderLoop = CreateRenderLoop (*this);
+
+ m_CullingMask.m_Bits = 0xFFFFFFFF;
+ m_EventMask.m_Bits = 0xFFFFFFFF;
+
+ for(int i=0;i<32;i++)
+ m_LayerCullDistances[i] = 0;
+ m_LayerCullSpherical = false;
+ m_SortMode = kSortDefault;
+ m_ImplicitProjectionMatrix = m_ImplicitWorldToCameraMatrix = true;
+ m_ImplicitAspect = true;
+ m_HDR = false;
+ m_UsingHDR = false;
+ m_IsRendering = false;
+
+ m_Velocity = Vector3f::zero;
+ m_LastPosition = Vector3f::zero;
+ m_WorldToCameraMatrix = m_WorldToClipMatrix = m_ProjectionMatrix = Matrix4x4f::identity;
+ m_CurrentTargetTexture = NULL;
+ m_CurrentTargetFace = kCubeFaceUnknown;
+ m_OcclusionCulling = true;
+
+ m_TargetBuffersOriginatedFrom = 0;
+ m_TargetColorBufferCount = 1;
+ ::memset(m_TargetColorBuffer, 0x00, sizeof(m_TargetColorBuffer));
+
+ m_TargetColorBuffer[0] = GetUncheckedGfxDevice().GetBackBufferColorSurface();
+ m_TargetDepthBuffer = GetUncheckedGfxDevice().GetBackBufferDepthSurface();
+
+ m_IntermediateRenderers = UNITY_NEW(IntermediateRenderers, GetMemoryLabel());
+#if UNITY_EDITOR
+ m_OldCameraState.Reset();
+#endif
+}
+
+void Camera::Reset ()
+{
+ Super::Reset();
+
+ m_NormalizedViewPortRect = Rectf (0, 0, 1, 1);
+
+ m_BackGroundColor = ColorRGBA32 (49, 77, 121, 5); // very small alpha to not get "everything glows" by default
+ m_Depth = 0.0F;
+ m_NearClip = 0.3F;
+ m_FarClip = 1000.0F;
+ m_RenderingPath = -1;
+ m_Aspect = 1.0F;
+ m_Orthographic = false;
+ m_HDR = false;
+ m_SortMode = kSortDefault;
+
+ m_OrthographicSize = 5.0F;
+ m_FieldOfView = 60.0F;
+ m_ClearFlags = kSkybox;
+ m_DirtyWorldToCameraMatrix = m_DirtyProjectionMatrix = m_DirtyWorldToClipMatrix = true;
+}
+
+
+
+void Camera::CheckConsistency ()
+{
+ Super::CheckConsistency();
+ m_RenderingPath = clamp(m_RenderingPath, -1, kRenderPathCount-1);
+ if(!m_Orthographic && m_NearClip < 0.01F)
+ m_NearClip = 0.01F;
+ if(m_FarClip < m_NearClip + 0.01F)
+ m_FarClip = m_NearClip + 0.01F;
+}
+
+
+Camera::~Camera ()
+{
+ CleanupDepthTextures ();
+ m_IntermediateRenderers->Clear();
+ UNITY_DELETE(m_IntermediateRenderers, GetMemoryLabel());
+
+ DeleteRenderLoop (m_RenderLoop);
+}
+
+
+void Camera::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ if ((awakeMode & kDidLoadFromDisk) == 0 && IsAddedToManager ())
+ {
+ GetRenderManager().RemoveCamera (this);
+ GetRenderManager().AddCamera (this);
+ }
+ m_DirtyWorldToCameraMatrix = m_DirtyProjectionMatrix = m_DirtyWorldToClipMatrix = true;
+ WindowSizeHasChanged ();
+ if(m_HDR)
+ DisplayHDRWarnings();
+}
+
+
+void Camera::ClearIntermediateRenderers( size_t startIndex )
+{
+ m_IntermediateRenderers->Clear(startIndex);
+}
+
+void Camera::AddToManager ()
+{
+ GetRenderManager().AddCamera (this);
+ WindowSizeHasChanged ();
+ m_LastPosition = GetComponent (Transform).GetPosition ();
+ m_Velocity = Vector3f (0.0F, 0.0F, 0.0F);
+}
+
+void Camera::TransformChanged()
+{
+ m_DirtyWorldToCameraMatrix = true;
+ m_DirtyWorldToClipMatrix = true;
+}
+
+void Camera::RemoveFromManager ()
+{
+ GetRenderManager().RemoveCamera (this);
+}
+
+
+
+template<class TransferFunction>
+void Camera::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ // Note: transfer code for version 1 was just removed. It was around Unity 1.2 times,
+ // and now we're fine with losing project folder compatibility with that.
+ transfer.SetVersion (2);
+
+ TRANSFER_SIMPLE (m_ClearFlags);
+ TRANSFER_SIMPLE (m_BackGroundColor);
+
+ TRANSFER (m_NormalizedViewPortRect);
+ transfer.Transfer (m_NearClip, "near clip plane");
+ transfer.Transfer (m_FarClip, "far clip plane");
+ transfer.Transfer (m_FieldOfView, "field of view", kSimpleEditorMask);
+ transfer.Transfer (m_Orthographic, "orthographic");
+ transfer.Align();
+ transfer.Transfer (m_OrthographicSize, "orthographic size");
+
+ TRANSFER (m_Depth);
+ TRANSFER (m_CullingMask);
+ //TRANSFER (m_EventMask);
+ TRANSFER (m_RenderingPath);
+
+ transfer.Transfer (m_TargetTexture, "m_TargetTexture");
+ TRANSFER (m_HDR);
+ TRANSFER (m_OcclusionCulling);
+}
+
+
+static inline Rectf GetCameraTargetRect (const Camera& camera, bool zeroOrigin)
+{
+ RenderTexture* target = camera.GetTargetTexture();
+ if (target != NULL)
+ return Rectf(0, 0, target->GetWidth(), target->GetHeight());
+
+ RenderSurfaceHandle colorTarget = camera.GetTargetColorBuffer();
+ if(colorTarget.IsValid() && !colorTarget.object->backBuffer)
+ return Rectf(0, 0, colorTarget.object->width, colorTarget.object->height);
+
+ const RenderManager& renderMgr = GetRenderManager();
+ Rectf rect = renderMgr.GetWindowRect();
+
+#if UNITY_EDITOR
+ // In the editor, if we're trying to get rect of a regular camera (visible in hierarchy etc.),
+ // use game view size instead of "whatever editor window was processed last" size.
+ // Otherwise Camera.main.aspect would return aspect of inspector when repainting it, for example.
+ //
+ // Only do this for regular cameras however; keep hidden cameras (scene view, material preview etc.)
+ // using the old behavior.
+ Unity::GameObject* go = camera.GetGameObjectPtr();
+ if (go && (go->GetHideFlags() & Object::kHideAndDontSave) != Object::kHideAndDontSave)
+ {
+ // If the current guiview is a GameView then GetRenderManager().GetWindowRect() is already set up correctly and
+ // we do not need to find first available game view to get a valid rect. Fix for case 517158
+ bool isCurrentGUIViewAGameView = GUIView::GetCurrent() != NULL && GUIView::GetCurrent()->IsGameView();
+ if (!isCurrentGUIViewAGameView)
+ {
+ bool gameViewFocus;
+ Rectf gameViewRect;
+ GetScreenParamsFromGameView(false, false, &gameViewFocus, &gameViewRect, &rect);
+ }
+ }
+#endif
+
+ if (zeroOrigin)
+ rect.x = rect.y = 0.0f;
+ return rect;
+}
+
+
+Rectf Camera::GetCameraRect (bool zeroOrigin) const
+{
+ // Get the screen rect from either the target texture or the viewport we're inside
+ Rectf screenRect = GetCameraTargetRect (*this, zeroOrigin);
+
+ // Now figure out how large this camera is depending on the normalized viewRect.
+ Rectf viewRect = m_NormalizedViewPortRect;
+ viewRect.Scale (screenRect.width, screenRect.height);
+ viewRect.Move (screenRect.x, screenRect.y);
+ viewRect.Clamp (screenRect);
+ return viewRect;
+}
+
+
+void Camera::SetScreenViewportRect (const Rectf& pixelRect)
+{
+ // Get the screen rect from either the target texture or the viewport we're inside
+ // Use zero base the screen rect; all game code assumes that the visible viewport starts at zero.
+ Rectf screenRect = GetCameraTargetRect (*this, true);
+
+ // Now translate from pixel to viewport space
+ Rectf viewRect = pixelRect;
+ viewRect.Move (-screenRect.x, -screenRect.y);
+ if (screenRect.width > 0.0f && screenRect.height > 0.0f)
+ viewRect.Scale (1.0F / screenRect.width, 1.0F / screenRect.height);
+ else
+ viewRect.Reset();
+ SetNormalizedViewportRect(viewRect);
+}
+
+static void InitShaderReplaceData (Shader* replacementShader, const std::string& shaderReplaceTag, ShaderReplaceData& output)
+{
+ // Shader replacement might be passed explicitly (camera.RenderWithShader) OR shader replacement can be setup as camera's state (camera.SetReplacementShader)
+ if( replacementShader != NULL )
+ {
+ output.replacementShader = replacementShader;
+ output.replacementTagID = ShaderLab::GetShaderTagID(shaderReplaceTag);
+ output.replacementTagSet = !shaderReplaceTag.empty();
+ }
+ else
+ {
+ Assert(output.replacementShader == NULL);
+ }
+}
+
+bool Camera::IsValidToRender() const
+{
+ if( m_NormalizedViewPortRect.IsEmpty() )
+ return false;
+ if( m_NormalizedViewPortRect.x >= 1.0F || m_NormalizedViewPortRect.GetRight() <= 0.0F)
+ return false;
+ if( m_NormalizedViewPortRect.y >= 1.0F || m_NormalizedViewPortRect.GetBottom() <= 0.0F)
+ return false;
+
+ if( m_FarClip <= m_NearClip )
+ return false;
+ if( !m_Orthographic )
+ {
+ if( m_NearClip <= 0.0f )
+ return false; // perspective camera needs positive near plane
+ if( Abs(m_FieldOfView) < 1.0e-6f )
+ return false; // field of view has to be non zero
+ }
+ else
+ {
+ if( Abs(m_OrthographicSize) < 1.0e-6f )
+ return false; // orthographic size has to be non zero
+ }
+ return true;
+}
+
+bool Camera::GetUsesScreenForCompositing (bool forceIntoRT) const
+{
+ // If rendering into a texture, don't composite to screen
+ if (forceIntoRT || m_TargetTexture.IsValid() || !m_TargetColorBuffer[0].IsValid() || !m_TargetColorBuffer[0].object->backBuffer)
+ return false;
+
+ #if UNITY_OSX && WEBPLUG && !UNITY_PEPPER
+ // In CoreAnimation plugin, we use frame buffer blit extension for ImageFX with FSAA.
+ // I assume any mac which does core animation supports that, but better safe then sorry :)
+ if (GetScreenManager().IsUsingCoreAnimation() && !gGraphicsCaps.gl.hasFrameBufferBlit )
+ return false;
+ #endif
+
+ // If FSAA is used: composite to screen!
+ if (GetQualitySettings().GetCurrent().antiAliasing > 1 && gGraphicsCaps.hasMultiSample)
+ return true;
+
+ // If camera is part of multi-layer setup (does not clear): composite to screen!
+ // Except if this is a scene view camera; do not composite it to screen because it would break
+ // Image FX + AA + Shadows
+ #if UNITY_EDITOR
+ if (m_IsSceneCamera)
+ return false;
+ #endif
+ if (m_ClearFlags != kSkybox && m_ClearFlags != kSolidColor)
+ return true;
+
+ // Otherwise, it's a clearing camera with no AA used
+ return false;
+}
+
+
+void Camera::SetupRender( int renderFlags )
+{
+ GfxDevice& device = GetGfxDevice();
+
+ // Cache whether we use HDR for rendering.
+ m_UsingHDR = CalculateUsingHDR();
+
+ bool forceIntoRT = CalculateNeedsToRenderIntoRT();
+ int antiAliasing = CalculateAntiAliasingForRT();
+
+ if (renderFlags & kRenderFlagPrepareImageFilters)
+ {
+ // figure out if we need to render into a texture & prepare for that
+ GetRenderLoopImageFilters(*m_RenderLoop).Prepare (forceIntoRT, GetUsingHDR(), antiAliasing);
+ }
+
+ // Set the current target texture to be the one calculated.
+ m_CurrentTargetTexture = NULL;
+ if (!GetUsesScreenForCompositing(forceIntoRT))
+ {
+ ImageFilters& imageFilters = GetRenderLoopImageFilters(*m_RenderLoop);
+
+ // If kFlagSetRenderTargetFinal is set we want to set the current target to the one image filters blitted to.
+ // This is the target transparent objects were rendered to and the lens flare needs to be rendered to as well. (case 443687)
+ m_CurrentTargetTexture = (renderFlags & kRenderFlagSetRenderTargetFinal) ? imageFilters.GetTargetFinal() : imageFilters.GetTargetBeforeOpaque ();
+
+ if(!m_CurrentTargetTexture)
+ m_CurrentTargetTexture = m_TargetTexture;
+ }
+
+ // Compute the viewport rect in the window
+ // This is only used when setting current render texture to NULL,
+ // so that it can restore the viewport.
+ int* viewPortCoords = GetRenderManager().GetCurrentViewPortWriteable();
+ RectfToViewport( GetPhysicalViewportRect(), viewPortCoords );
+
+ if(renderFlags & kRenderFlagSetRenderTarget)
+ {
+ m_CurrentTargetTexture = EnsureRenderTextureIsCreated(m_CurrentTargetTexture);
+
+ CubemapFace curFace = kCubeFaceUnknown;
+ if(m_CurrentTargetTexture && m_CurrentTargetTexture->GetDimension() == kTexDimCUBE)
+ curFace = m_CurrentTargetFace;
+
+ // while we could return const ref (and grab address and use uniformly)
+ // we pass non const pointer to SetActive (because surfaces can be reset internally)
+ // so create local handle copy if we draw to real texture
+ RenderSurfaceHandle rtcolor = m_CurrentTargetTexture ? m_CurrentTargetTexture->GetColorSurfaceHandle() : RenderSurfaceHandle();
+
+ RenderSurfaceHandle* color = m_CurrentTargetTexture ? &rtcolor : m_TargetColorBuffer;
+ RenderSurfaceHandle depth = m_CurrentTargetTexture ? m_CurrentTargetTexture->GetDepthSurfaceHandle() : m_TargetDepthBuffer;
+ int count = m_CurrentTargetTexture ? 1 : m_TargetColorBufferCount;
+
+ if(!m_CurrentTargetTexture)
+ m_CurrentTargetTexture = m_TargetBuffersOriginatedFrom;
+
+ RenderTexture::SetActive(count, color, depth, m_CurrentTargetTexture, 0, curFace, RenderTexture::kFlagDontSetViewport);
+
+ int viewcoord[4];
+ if(color[0].IsValid() && color[0].object->backBuffer)
+ ::memcpy(viewcoord, viewPortCoords, 4*sizeof(int));
+ else
+ RectfToViewport(GetRenderRectangle(), viewcoord);
+
+ FlipScreenRectIfNeeded(device, viewcoord);
+ device.SetViewport(viewcoord[0], viewcoord[1], viewcoord[2], viewcoord[3]);
+ }
+
+ device.SetProjectionMatrix (GetProjectionMatrix());
+ device.SetViewMatrix( GetWorldToCameraMatrix().GetPtr() );
+ SetCameraShaderProps();
+}
+
+
+void Camera::SetCameraShaderProps()
+{
+ GfxDevice& device = GetGfxDevice();
+ BuiltinShaderParamValues& params = device.GetBuiltinParamValues();
+
+ Transform &tc = GetComponent (Transform);
+
+ Vector3f pos = tc.GetPosition ();
+ params.SetVectorParam(kShaderVecWorldSpaceCameraPos, Vector4f(pos, 0.0f));
+
+
+ Matrix4x4f temp;
+
+ // World to camera matrix
+ params.SetMatrixParam(kShaderMatWorldToCamera, tc.GetWorldToLocalMatrixNoScale());
+
+ // Camera to world matrix
+ temp = tc.GetLocalToWorldMatrixNoScale ();
+ params.SetMatrixParam(kShaderMatCameraToWorld, temp);
+
+ // Get the matrix to use for cubemap reflections.
+ // It's camera to world matrix; rotation only, and mirrored on Y.
+ temp.GetPtr()[12] = temp.GetPtr()[13] = temp.GetPtr()[14] = 0; // clear translation
+ Matrix4x4f invertY;
+ invertY.SetScale(Vector3f (1,-1,1));
+ Matrix4x4f reflMat;
+ MultiplyMatrices4x4 (&temp, &invertY, &reflMat);
+ ShaderLab::g_GlobalProperties->SetValueProp (kSLPropReflection, 16, reflMat.GetPtr());
+
+ // Camera clipping planes
+ SetClippingPlaneShaderProps();
+
+ // Setup time & misc properties
+
+ const TimeManager& timeMgr = GetTimeManager();
+ float time;
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_5_a1))
+ time = timeMgr.GetTimeSinceLevelLoad ();
+ else
+ time = timeMgr.GetCurTime ();
+
+#if UNITY_EDITOR
+ if (m_AnimateMaterials)
+ time = m_AnimateMaterialsTime;
+#endif
+
+ const float kMinDT = 0.005f;
+ const float kMaxDT = 0.2f;
+ const float deltaTime = clamp(timeMgr.GetDeltaTime(), kMinDT, kMaxDT);
+ const float smoothDeltaTime = clamp(timeMgr.GetSmoothDeltaTime(), kMinDT, kMaxDT);
+
+ // The 0.05 in kShaderVecTime is a typo, but can't change it now. There are water shaders out there that
+ // use exactly .x component :(
+
+ params.SetVectorParam(kShaderVecTime, Vector4f(0.05f*time, time, 2.0f*time, 3.0f*time));
+ params.SetVectorParam(kShaderVecSinTime, Vector4f(sinf(0.125f*time), sinf(0.25f*time), sinf(0.5f*time), sinf(time)));
+ params.SetVectorParam(kShaderVecCosTime, Vector4f(cosf(0.125f*time), cosf(0.25f*time), cosf(0.5f*time), cosf(time)));
+ params.SetVectorParam(kShaderVecPiTime, Vector4f(fmodf(time,kPI), fmodf(2.0f*time, kPI), fmodf(3.0f*time, kPI), fmodf(4.0f*time, kPI)));
+ params.SetVectorParam(kShaderVecDeltaTime, Vector4f(deltaTime, 1.0f/deltaTime, smoothDeltaTime, 1.0f/smoothDeltaTime));
+
+ float projNear = GetProjectionNear();
+ float projFar = GetProjectionFar();
+ params.SetVectorParam(kShaderVecProjectionParams, Vector4f(device.GetInvertProjectionMatrix() ? -1.0f : 1.0f, projNear, projFar, 1.0 / projFar));
+
+ Rectf view = GetScreenViewportRect();
+ params.SetVectorParam(kShaderVecScreenParams, Vector4f(view.width, view.height, 1.0f+1.0f/view.width, 1.0f+1.0f/view.height));
+
+ // From http://www.humus.name/temp/Linearize%20depth.txt
+ // But as depth component textures on OpenGL always return in 0..1 range (as in D3D), we have to use
+ // the same constants for both D3D and OpenGL here.
+ double zc0, zc1;
+ // OpenGL would be this:
+ // zc0 = (1.0 - projFar / projNear) / 2.0;
+ // zc1 = (1.0 + projFar / projNear) / 2.0;
+ // D3D is this:
+ zc0 = 1.0 - projFar / projNear;
+ zc1 = projFar / projNear;
+ params.SetVectorParam(kShaderVecZBufferParams, Vector4f(zc0, zc1, zc0/projFar, zc1/projFar));
+
+ // make sure we have a gamma correct grey value available
+ float correctGreyValue = GammaToActiveColorSpace(0.5f);
+ params.SetVectorParam(kShaderVecColorSpaceGrey, Vector4f(correctGreyValue,correctGreyValue,correctGreyValue,0.5f));
+}
+
+PROFILER_INFORMATION(gCameraClearProfile, "Clear", kProfilerRender)
+
+static const ColorRGBAf ConvertColorToActiveColorSpace(const ColorRGBAf& color)
+{
+#if UNITY_PS3
+ return color;
+#else
+ return GammaToActiveColorSpace(color);
+#endif
+}
+
+
+static void ClearFramebuffer(GfxClearFlags gfxClearFlags, Rectf rect, ColorRGBAf const& color)
+{
+ PROFILER_AUTO_GFX(gCameraClearProfile, NULL);
+ const float depth = 1.0f;
+ const int stencil = 0;
+
+ GfxDevice& device = GetGfxDevice();
+ // If we're rendering into a temporary texture, we always have (0,0) in the bottom-left corner
+ // No matter what the view coords say.
+ int si[4];
+ RectfToViewport( rect, si );
+ FlipScreenRectIfNeeded( device, si );
+ device.SetScissorRect( si[0], si[1], si[2], si[3] );
+
+ // seems like the best place to time clear
+ ABSOLUTE_TIME clearStart = START_TIME;
+#if UNITY_OSX && WEBPLUG && !UNITY_PEPPER
+ if (gGraphicsCaps.gl.mustWriteToDepthBufferBeforeClear && gfxClearFlags & kGfxClearDepth)
+ {
+ // Mac OS X 10.7.2 introduced a bug in the NVidia GPU drivers, where the depth buffer would
+ // contain garbage if used in a CoreAnimation content with enabled stencil buffer, if the
+ // depth buffer is not written to at least once between buffer clears. This breaks scenes which
+ // only read but never write to the depth buffer (such as a scene using only particle shaders).
+ // So we render an invisble, depth only triangle in that case.
+ DeviceMVPMatricesState preserveMVP;
+ LoadFullScreenOrthoMatrix();
+
+ static Material* s_UpdateDepthBufferMaterial = NULL;
+ if (!s_UpdateDepthBufferMaterial)
+ {
+ const char* kUpdateDepthBufferShader =
+ "Shader \"Hidden/UpdateDepthBuffer\" {\n"
+ "SubShader { Pass {\n"
+ " ZTest Always Cull Off Fog { Mode Off } ColorMask 0\n"
+ "}}}";
+ s_UpdateDepthBufferMaterial = Material::CreateMaterial (kUpdateDepthBufferShader, Object::kHideAndDontSave);
+ }
+
+ s_UpdateDepthBufferMaterial->SetPass (0);
+ device.ImmediateBegin (kPrimitiveTriangles);
+ device.ImmediateVertex (0.0f, 0.0f, 0.1f);
+ device.ImmediateVertex (0.0f, 0.1f, 0.1f);
+ device.ImmediateVertex (0.1f, 0.1f, 0.1f);
+ device.ImmediateEnd ();
+ }
+#endif
+ GraphicsHelper::Clear (gfxClearFlags, color.GetPtr(), depth, stencil);
+ GPU_TIMESTAMP();
+ GetGfxDevice().GetFrameStats().AddClear(ELAPSED_TIME(clearStart));
+
+ device.DisableScissor();
+}
+
+
+static void ClearFramebuffer(Camera::ClearMode clearMode, Rectf rect, ColorRGBAf const& color, bool hasSkybox)
+{
+ GfxClearFlags gfxClearFlags = kGfxClearAll;
+ switch (clearMode)
+ {
+ case Camera::kDontClear:
+ return;
+ case Camera::kDepthOnly:
+ gfxClearFlags = kGfxClearDepthStencil;
+ break;
+ case Camera::kSolidColor:
+ gfxClearFlags = kGfxClearAll;
+ break;
+ case Camera::kSkybox:
+ gfxClearFlags = hasSkybox ? kGfxClearDepthStencil : kGfxClearAll;
+ break;
+ }
+ ClearFramebuffer(gfxClearFlags, rect, color);
+}
+
+
+void Camera::Clear()
+{
+ //Do not need to convert background color to correct space as this is done in gamma space always.
+ ClearFramebuffer(GetClearFlags(), GetRenderRectangle(), m_BackGroundColor, GetSkyboxMaterial() != NULL);
+ RenderSkybox();
+}
+
+void Camera::ClearNoSkybox(bool noDepth)
+{
+ ClearMode clearMode = GetClearFlags();
+ UInt32 flags = kGfxClearAll;
+ switch (clearMode)
+ {
+ case Camera::kDontClear: flags = 0; break;
+ case Camera::kDepthOnly: flags = kGfxClearDepthStencil; break;
+ case Camera::kSolidColor: flags = kGfxClearAll; break;
+ case Camera::kSkybox: flags = kGfxClearAll; break;
+ }
+ if (noDepth)
+ flags &= ~kGfxClearDepthStencil;
+ if (flags == 0)
+ return;
+
+ ClearFramebuffer((GfxClearFlags)flags, GetRenderRectangle(), ConvertColorToActiveColorSpace(m_BackGroundColor));
+}
+
+void Camera::RenderSkybox()
+{
+ if (m_ClearFlags != kSkybox)
+ return;
+
+ Material* skybox = GetSkyboxMaterial();
+ if (!skybox)
+ return;
+
+ Skybox::RenderSkybox (skybox, *this);
+}
+
+Material *Camera::GetSkyboxMaterial () const
+{
+ Skybox *sb = QueryComponent (Skybox);
+ if (sb && sb->GetEnabled() && sb->GetMaterial())
+ return sb->GetMaterial();
+ else
+ return GetRenderSettings().GetSkyboxMaterial();
+}
+
+Rectf Camera::GetRenderRectangle() const
+{
+ if( m_CurrentTargetTexture && m_CurrentTargetTexture != (RenderTexture*)m_TargetTexture )
+ {
+ return Rectf (0, 0, m_CurrentTargetTexture->GetWidth(), m_CurrentTargetTexture->GetHeight());
+ }
+ else
+ {
+ return GetPhysicalViewportRect();
+ }
+}
+
+PROFILER_INFORMATION(gCameraRenderProfile, "Camera.Render", kProfilerRender)
+PROFILER_INFORMATION(gCameraRenderToCubemapProfile, "Camera.RenderToCubemap", kProfilerRender)
+PROFILER_INFORMATION(gCameraCullProfile, "Culling", kProfilerRender)
+PROFILER_INFORMATION(gCameraDrawProfile, "Drawing", kProfilerRender)
+PROFILER_INFORMATION(gCameraDepthTextureProfile, "UpdateDepthTexture", kProfilerRender)
+PROFILER_INFORMATION(gCameraDepthNormalsTextureProfile, "UpdateDepthNormalsTexture", kProfilerRender)
+
+void Camera::CalculateFrustumPlanes(Plane frustum[kPlaneFrustumNum], const Matrix4x4f& overrideWorldToClip, float overrideFarPlane, float& outBaseFarDistance, bool implicitNearFar) const
+{
+ ExtractProjectionPlanes (overrideWorldToClip, frustum);
+
+ Plane& nearPlane = frustum[kPlaneFrustumNear];
+ Plane& farPlane = frustum[kPlaneFrustumFar];
+
+ if (IsImplicitWorldToCameraMatrix() || implicitNearFar)
+ {
+ // Extracted near and far planes may be unsuitable for culling.
+ // E.g. oblique near plane for water refraction busts both planes.
+ // Also very large far/near ratio causes precision problems.
+ // Instead we calculate the planes from our position/direction.
+
+ Matrix4x4f cam2world = GetCameraToWorldMatrix();
+ Vector3f eyePos = cam2world.GetPosition();
+ Vector3f viewDir = -NormalizeSafe(cam2world.GetAxisZ());
+
+ nearPlane.SetNormalAndPosition(viewDir, eyePos);
+ nearPlane.distance -= m_NearClip;
+
+ farPlane.SetNormalAndPosition(-viewDir, eyePos);
+ outBaseFarDistance = farPlane.distance;
+ farPlane.distance += overrideFarPlane;
+ }
+ else
+ outBaseFarDistance = farPlane.distance - overrideFarPlane;
+}
+
+void Camera::CalculateCullingParameters(CullingParameters& cullingParameters) const
+{
+ Plane frustum[kPlaneFrustumNum];
+ float baseFarDistance;
+
+ Matrix4x4f worldToClipMatrix = GetWorldToClipMatrix();
+ cullingParameters.worldToClipMatrix = worldToClipMatrix;
+ cullingParameters.position = GetPosition();
+
+ CalculateFrustumPlanes(frustum, worldToClipMatrix, m_FarClip, baseFarDistance, false);
+ CalculateCustomCullingParameters(cullingParameters, frustum, kPlaneFrustumNum);
+
+ if (m_LayerCullSpherical)
+ {
+ std::copy(m_LayerCullDistances, m_LayerCullDistances + kNumLayers, cullingParameters.layerFarCullDistances);
+ cullingParameters.layerCull = CullingParameters::kLayerCullSpherical;
+ }
+ else
+ {
+ CalculateFarCullDistances(cullingParameters.layerFarCullDistances, baseFarDistance);
+ cullingParameters.layerCull = CullingParameters::kLayerCullPlanar;
+ }
+}
+
+void Camera::CalculateCustomCullingParameters(CullingParameters& cullingParameters, const Plane* planes, int planeCount) const
+{
+ cullingParameters.lodPosition = GetPosition();
+ cullingParameters.lodFieldOfView = m_FieldOfView;
+ cullingParameters.orthoSize = m_OrthographicSize;
+ cullingParameters.cameraPixelHeight = int(GetPhysicalViewportRect().height);
+
+ // Shadow code handles per-layer cull distances itself
+
+ Assert(planeCount <= CullingParameters::kMaxPlanes);
+ for (int i = 0; i < planeCount; i++)
+ cullingParameters.cullingPlanes[i] = planes[i];
+ cullingParameters.cullingPlaneCount = planeCount;
+ cullingParameters.layerCull = CullingParameters::kLayerCullNone;
+ cullingParameters.isOrthographic = GetOrthographic();
+ cullingParameters.cullingMask = m_CullingMask.m_Bits;
+
+ Matrix4x4f worldToClipMatrix = GetWorldToClipMatrix();
+ cullingParameters.worldToClipMatrix = worldToClipMatrix;
+ cullingParameters.position = GetPosition();
+}
+
+
+void Camera::StandaloneCull (Shader* replacementShader, const std::string& replacementTag, CullResults& results)
+{
+ CameraCullingParameters parameters (*this, kCullFlagNeedsLighting | kCullFlagForceEvenIfCameraIsNotActive);
+ if (GetUseOcclusionCulling())
+ parameters.cullFlag |= kCullFlagOcclusionCull;
+
+ InitShaderReplaceData(replacementShader, replacementTag, parameters.explicitShaderReplace);
+
+ CustomCull(parameters, results);
+}
+
+void Camera::Cull (CullResults& results)
+{
+ CameraCullingParameters parameters (*this, kCullFlagNeedsLighting);
+ if (GetUseOcclusionCulling())
+ parameters.cullFlag |= kCullFlagOcclusionCull;
+
+ CustomCull(parameters, results);
+}
+
+static bool HasValidUmbraShadowCullingData (const Umbra::Visibility* umbraVisibility, const Umbra::Tome* tome)
+{
+ // Exact visibility is useful for pixel lights only, so check rendering path.
+ // Only Portal culling mode support shadow culling
+
+ return umbraVisibility != NULL && tome != NULL;
+}
+
+bool static IsCullPerObjectLightsNeeded(SceneCullingParameters& sceneCullParameters, RenderingPath renderPath)
+{
+ if (renderPath != kRenderPathPrePass)
+ return true;
+
+ // Check whether there are objects/shaders that are not handled by deferred, but are rendered with forward path
+ for (int i=0; i<kVisibleListCount; i++)
+ {
+ RendererCullData& cullData = sceneCullParameters.renderers[i];
+ for( size_t i = 0; i < cullData.rendererCount; ++i )
+ {
+ BaseRenderer* r = cullData.nodes[i].renderer;
+
+ //Fix for case 570036. TODO: Look if we can catch the null Renderer case sooner
+ if (!r)
+ continue;
+
+ const int matCount = r->GetMaterialCount();
+ for (int mi = 0; mi < matCount; ++mi)
+ {
+ Material* mat = r->GetMaterial (mi);
+ // It is possible for this to be NULL if we have a missing reference.
+ if (mat)
+ {
+ Shader* shader = mat->GetShader();
+ int ss = shader->GetShaderLabShader()->GetDefaultSubshaderIndex (kRenderPathExtPrePass);
+ if (ss == -1)
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+////@TODO: Find a better name for this function
+
+#include "Runtime/Graphics/LightmapSettings.h"
+
+void Camera::PrepareSceneCullingParameters (const CameraCullingParameters& parameters, RenderingPath renderPath, CullResults& results)
+{
+ UmbraTomeData tomeData;
+ if ((parameters.cullFlag & kCullFlagOcclusionCull) != 0)
+ tomeData = GetScene().GetUmbraTome();
+
+ SceneCullingParameters& sceneCullParameters = results.sceneCullParameters;
+
+ if (parameters.cullingCamera->GetRenderImmediateObjects())
+ {
+ IntermediateRenderers& cameraIntermediate = parameters.cullingCamera->GetIntermediateRenderers();
+ sceneCullParameters.renderers[kCameraIntermediate].bounds = cameraIntermediate.GetBoundingBoxes();
+ sceneCullParameters.renderers[kCameraIntermediate].nodes = cameraIntermediate.GetSceneNodes();
+ sceneCullParameters.renderers[kCameraIntermediate].rendererCount = cameraIntermediate.GetRendererCount();
+ }
+ else
+ {
+ sceneCullParameters.renderers[kStaticRenderers].bounds = GetScene().GetStaticBoundingBoxes();
+ sceneCullParameters.renderers[kStaticRenderers].nodes = GetScene().GetStaticSceneNodes();
+ sceneCullParameters.renderers[kStaticRenderers].rendererCount = GetScene().GetStaticObjectCount();
+
+ sceneCullParameters.renderers[kDynamicRenderer].bounds = GetScene().GetDynamicBoundingBoxes();
+ sceneCullParameters.renderers[kDynamicRenderer].nodes = GetScene().GetDynamicSceneNodes();
+ sceneCullParameters.renderers[kDynamicRenderer].rendererCount = GetScene().GetDynamicObjectCount();
+
+ IntermediateRenderers& sceneIntermediate = GetScene().GetIntermediateRenderers();
+ sceneCullParameters.renderers[kSceneIntermediate].bounds = sceneIntermediate.GetBoundingBoxes();
+ sceneCullParameters.renderers[kSceneIntermediate].nodes = sceneIntermediate.GetSceneNodes();
+ sceneCullParameters.renderers[kSceneIntermediate].rendererCount = sceneIntermediate.GetRendererCount();
+
+ IntermediateRenderers& cameraIntermediate = parameters.cullingCamera->GetIntermediateRenderers();
+ sceneCullParameters.renderers[kCameraIntermediate].bounds = cameraIntermediate.GetBoundingBoxes();
+ sceneCullParameters.renderers[kCameraIntermediate].nodes = cameraIntermediate.GetSceneNodes();
+ sceneCullParameters.renderers[kCameraIntermediate].rendererCount = cameraIntermediate.GetRendererCount();
+
+#if ENABLE_TERRAIN
+ ITerrainManager* terrainManager = GetITerrainManager();
+ if (terrainManager != NULL)
+ {
+ terrainManager->CollectTreeRenderers(results.treeSceneNodes, results.treeBoundingBoxes);
+ }
+ sceneCullParameters.renderers[kTreeRenderer].bounds = results.treeBoundingBoxes.data();
+ sceneCullParameters.renderers[kTreeRenderer].nodes = results.treeSceneNodes.data();
+ sceneCullParameters.renderers[kTreeRenderer].rendererCount = results.treeBoundingBoxes.size();
+#endif
+
+ }
+
+ // Prepare cull results and allocate all culling memory
+ results.Init(tomeData, sceneCullParameters.renderers);
+
+ parameters.cullingCamera->CalculateCullingParameters(sceneCullParameters);
+
+ sceneCullParameters.useOcclusionCulling = tomeData.HasTome();
+ sceneCullParameters.sceneVisbilityForShadowCulling = &results.sceneCullingOutput;
+ sceneCullParameters.umbraDebugRenderer = parameters.umbraDebugRenderer;
+ sceneCullParameters.umbraDebugFlags = parameters.umbraDebugFlags;
+ sceneCullParameters.umbraTome = tomeData;
+ sceneCullParameters.umbraQuery = GetScene().GetUmbraQuery();
+#if UNITY_EDITOR
+ sceneCullParameters.filterMode = (CullFiltering)parameters.cullingCamera->m_FilterMode;
+#endif
+
+ // shadow culling is oly supported with the latest version of umbra (not with legacy umbra runtime)
+ sceneCullParameters.useShadowCasterCulling = sceneCullParameters.useOcclusionCulling && tomeData.tome != NULL;
+ sceneCullParameters.useLightOcclusionCulling = sceneCullParameters.useShadowCasterCulling;
+ sceneCullParameters.cullLights = parameters.cullFlag & kCullFlagNeedsLighting;
+ sceneCullParameters.excludeLightmappedShadowCasters = (renderPath == kRenderPathForward) ? !GetLightmapSettings().GetUseDualLightmapsInForward() : false;
+ sceneCullParameters.cullPerObjectLights = IsCullPerObjectLightsNeeded(sceneCullParameters, renderPath);
+ sceneCullParameters.renderPath = renderPath;
+
+ // Prepare LOD data
+ LODGroupManager& lodGroupManager = GetLODGroupManager();
+ size_t lodGroupCount = lodGroupManager.GetLODGroupCount();
+ results.lodMasks.resize_uninitialized(lodGroupCount);
+ results.lodFades.resize_uninitialized(lodGroupCount);
+ lodGroupManager.CalculateLODMasks(sceneCullParameters, results.lodMasks.begin(), results.lodFades.begin());
+
+ sceneCullParameters.lodMasks = results.lodMasks.begin();
+ sceneCullParameters.lodGroupCount = results.lodMasks.size();
+}
+
+
+void Camera::CustomCull (const CameraCullingParameters& parameters, CullResults& results)
+{
+ Assert(results.sceneCullingOutput.umbraVisibility == NULL);
+
+ PROFILER_AUTO(gCameraCullProfile, this)
+
+ // if camera's viewport rect is empty or invalid, do nothing
+ if( !IsValidToRender() )
+ {
+ return;
+ }
+
+ // Send cull message to game object
+ SendMessage (kPreCull);
+
+ // OnPreCull message might disable the camera!
+ // So we check one last time.
+ bool enabledAndActive = IsActive() && GetEnabled();
+ if (!enabledAndActive && (parameters.cullFlag & kCullFlagForceEvenIfCameraIsNotActive) == 0)
+ return;
+
+#if ENABLE_TERRAIN
+ UInt32 cullingMask = m_CullingMask.m_Bits;
+ ITerrainManager* terrainManager = GetITerrainManager();
+ if (cullingMask != 0 && terrainManager != NULL && !GetRenderImmediateObjects())
+ {
+ // Pass culllingParameters instead
+ terrainManager->CullAllTerrains(cullingMask);
+ }
+#endif // ENABLE_TERRAIN
+
+ // Update scene dirty bounds
+ GetScene().RecalculateDirtyBounds ();
+
+ // Calculate parameters after OnPreCull (case 401765)
+ // In case the user moves the camera in OnPreCull
+ PrepareSceneCullingParameters(parameters, CalculateRenderingPath(), results);
+
+ // Setup shader replacement
+ if (parameters.explicitShaderReplace.replacementShader != NULL)
+ results.shaderReplaceData = parameters.explicitShaderReplace;
+ else
+ InitShaderReplaceData (m_ReplacementShader, m_ReplacementTag, results.shaderReplaceData);
+
+ // Prepare light culling information
+ if (results.sceneCullParameters.cullLights)
+ {
+ ShadowCullData& shadowCullData = *UNITY_NEW(ShadowCullData, kMemTempAlloc);
+ SetupShadowCullData(*parameters.cullingCamera, parameters.cullingCamera->GetPosition(), results.shaderReplaceData, &results.sceneCullParameters, shadowCullData);
+
+ if (HasValidUmbraShadowCullingData (results.sceneCullingOutput.umbraVisibility, results.sceneCullParameters.umbraTome.tome))
+ shadowCullData.visbilityForShadowCulling = &results.sceneCullingOutput;
+
+ results.shadowCullData = &shadowCullData;
+ }
+
+ // Cull
+ if (GetRenderImmediateObjects())
+ CullIntermediateRenderersOnly(results.sceneCullParameters, results);
+ else
+ CullScene (results.sceneCullParameters, results);
+}
+
+
+void Camera::CalculateFarCullDistances (float* farCullDistances, float baseFarDistance) const
+{
+ // baseFarDistance is the distance of the far plane shifted to the camera position
+ // This is so layer distances work properly even if the far distance is very large
+ for(int i=0; i<kNumLayers; i++)
+ {
+ if(m_LayerCullDistances[i])
+ farCullDistances[i] = baseFarDistance + m_LayerCullDistances[i];
+ else
+ farCullDistances[i] = baseFarDistance + m_FarClip;
+ }
+}
+
+void Camera::DoRenderPostLayers ()
+{
+ FlareLayer* flareLayer = QueryComponent(FlareLayer);
+ if (flareLayer && flareLayer->GetEnabled())
+ {
+ GetFlareManager().RenderFlares();
+ }
+ GetRenderManager().InvokeOnRenderObjectCallbacks ();
+}
+
+void Camera::DoRenderGUILayer()
+{
+ GUILayer* guiLayer = QueryComponent(GUILayer);
+ if( guiLayer && guiLayer->GetEnabled())
+ guiLayer->RenderGUILayer ();
+}
+
+void Camera::DoRender (CullResults& cullResults, PerformRenderFunction* customRender, int renderFlags)
+{
+ if (!IsValidToRender())
+ return;
+
+ PROFILER_AUTO_GFX(gCameraDrawProfile, this)
+
+ // Shader replacement might be passed explicitly (camera.RenderWithShader), in which case we don't
+ // send Pre/Post render events, and don't render image effects.
+ // OR shader replacement can be setup as camera's state (camera.SetReplacementShader), in which case
+ // camera functions as usually, just with shaders replaced.
+ bool preAndPostRenderCallbacks = (renderFlags & kRenderFlagExplicitShaderReplace) == 0;
+
+ if (preAndPostRenderCallbacks)
+ SendMessage (kPreRender);
+
+
+ RenderingPath renderPath = (RenderingPath)cullResults.sceneCullParameters.renderPath;
+
+ // Render the culled contents
+ if( customRender )
+ customRender (*this, *m_RenderLoop, cullResults);
+ else if (cullResults.shaderReplaceData.replacementShader != NULL)
+ {
+ bool useLitReplace = IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a1);
+
+ if (useLitReplace)
+ {
+ DoRenderLoop (*m_RenderLoop, renderPath, cullResults, GetRenderImmediateObjects ());
+ }
+ else
+ {
+ RenderTexture* rt = RenderTexture::GetActive();
+ if(rt)
+ GetGfxDevice().SetSRGBWrite(rt->GetSRGBReadWrite());
+ else
+ GetGfxDevice().SetSRGBWrite(GetActiveColorSpace()==kLinearColorSpace);
+
+ ClearFramebuffer(GetClearFlags(),
+ GetRenderRectangle(),
+ ConvertColorToActiveColorSpace(m_BackGroundColor),
+ (cullResults.shaderReplaceData.replacementShader) ? NULL : GetSkyboxMaterial());
+ GetGfxDevice().SetSRGBWrite(false);
+
+ RenderSceneShaderReplacement (cullResults.nodes, cullResults.shaderReplaceData);
+ }
+ }
+ else
+ {
+ DoRenderLoop (*m_RenderLoop, renderPath, cullResults, GetRenderImmediateObjects ());
+ }
+
+ if (preAndPostRenderCallbacks)
+ SendMessage (kPostRender);
+
+ // The last renderer _might_ have toggled the back facing mode (to deal with mirrored geometry), so we reset this
+ // in order to make the back facing well-defined.
+ GetGfxDevice().SetNormalizationBackface( kNormalizationDisabled, false );
+
+}
+
+
+RenderingPath Camera::CalculateRenderingPath () const
+{
+ // Get rendering path from builds settings or per-camera params
+ RenderingPath rp = (m_RenderingPath==-1) ?
+ GetPlayerSettings().GetRenderingPathRuntime() :
+ static_cast<RenderingPath>(m_RenderingPath);
+
+ // Figure out what we can support on this hardware
+ if (rp == kRenderPathPrePass)
+ {
+ bool canDoPrePass =
+ gGraphicsCaps.hasPrePassRenderLoop && // basic GPU support
+ !m_Orthographic && // can't be ortho
+ RenderTexture::IsEnabled(); // render textures not disabled right now
+ if (!canDoPrePass)
+ rp = kRenderPathForward;
+ }
+ if (rp == kRenderPathForward)
+ {
+ bool canDoForwardShader = (gGraphicsCaps.shaderCaps >= kShaderLevel2);
+ if (!canDoForwardShader)
+ rp = kRenderPathVertex;
+ }
+ return rp;
+}
+
+bool Camera::CalculateNeedsToRenderIntoRT() const
+{
+ // Deferred needs to render into RT
+ if (CalculateRenderingPath() == kRenderPathPrePass)
+ return true;
+
+ // If we have image filters between opaque & transparent, but no AA: render into RT;
+ // much easier to share depth buffer between passes
+ const bool aa = gGraphicsCaps.hasMultiSample && GetQualitySettings().GetCurrent().antiAliasing > 1;
+ if (!aa && GetRenderLoopImageFilters(*m_RenderLoop).HasAfterOpaqueFilters())
+ return true;
+
+ return false;
+}
+
+int Camera::CalculateAntiAliasingForRT() const
+{
+ // Don't use MSAA for image effects if we're rendering to the back buffer
+ // Maybe we should find a way to enable this if people really want it?
+ // Previously there were no MSAA RTs so this wasn't an option
+ if (!m_TargetTexture)
+ return 1;
+
+ if (!gGraphicsCaps.hasMultiSample)
+ return 1;
+
+ // Deferred is not compatible with MSAA
+ if (CalculateRenderingPath() == kRenderPathPrePass)
+ return 1;
+
+ return m_TargetTexture->GetAntiAliasing();
+}
+
+
+void StoreRenderState (CameraRenderOldState& state)
+{
+ GfxDevice& device = GetGfxDevice();
+ device.GetViewport(state.viewport);
+
+ state.activeRT = RenderTexture::GetActive();
+ state.camera = GetCurrentCameraPtr ();
+
+ CopyMatrix(device.GetViewMatrix(), state.matView);
+ CopyMatrix(device.GetWorldMatrix(), state.matWorld);
+ CopyMatrix(device.GetProjectionMatrix(), state.matProj.GetPtr());
+}
+
+void RestoreRenderState (CameraRenderOldState& state)
+{
+ GfxDevice& device = GetGfxDevice();
+ Camera* oldCamera = state.camera;
+ GetRenderManager ().SetCurrentCamera (oldCamera);
+
+ // We should not pass "prepare image effects" flag here, because we're restoring previous render texture
+ // ourselves. I'm not sure if we should even call DoSetup on the camera; can't figure it out right now.
+ if (oldCamera)
+ oldCamera->SetupRender ();
+
+ RenderTexture::SetActive(state.activeRT);
+ device.SetViewport(state.viewport[0], state.viewport[1], state.viewport[2], state.viewport[3]);
+ device.SetViewMatrix(state.matView);
+ device.SetWorldMatrix(state.matWorld);
+ device.SetProjectionMatrix(state.matProj);
+ SetClippingPlaneShaderProps();
+}
+
+
+void Camera::StandaloneRender( UInt32 renderFlags, Shader* replacementShader, const std::string& replacementTag )
+{
+ PROFILER_AUTO_GFX(gCameraRenderProfile, this)
+
+ renderFlags |= kRenderFlagStandalone;
+
+ RenderManager::UpdateAllRenderers();
+
+ CameraRenderOldState state;
+ if( !(renderFlags & kRenderFlagDontRestoreRenderState) )
+ StoreRenderState(state);
+
+ GetRenderManager().SetCurrentCamera (this);
+ WindowSizeHasChanged ();
+
+ CullResults cullResults;
+
+ StandaloneCull(replacementShader, replacementTag, cullResults);
+
+ // We may need BeginFrame() if we're called from script outside rendering loop (case 464376)
+ AutoGfxDeviceBeginEndFrame frame;
+ if( !frame.GetSuccess() )
+ return;
+
+ // Shader replacement might be passed explicitly (camera.RenderWithShader), in which case we don't
+ // send Pre/Post render events, and don't render image effects.
+ // OR shader replacement can be setup as camera's state (camera.SetReplacementShader), in which case
+ // camera functions as usually, just with shaders replaced.
+ if (replacementShader != NULL)
+ renderFlags |= kRenderFlagExplicitShaderReplace;
+
+ // Render this camera
+ Render( cullResults, renderFlags );
+
+ if( !(renderFlags & kRenderFlagDontRestoreRenderState) )
+ RestoreRenderState(state);
+}
+
+
+static const Vector3f kCubemapOrthoBases[6*3] = {
+ Vector3f( 0, 0,-1), Vector3f( 0,-1, 0), Vector3f(-1, 0, 0),
+ Vector3f( 0, 0, 1), Vector3f( 0,-1, 0), Vector3f( 1, 0, 0),
+ Vector3f( 1, 0, 0), Vector3f( 0, 0, 1), Vector3f( 0,-1, 0),
+ Vector3f( 1, 0, 0), Vector3f( 0, 0,-1), Vector3f( 0, 1, 0),
+ Vector3f( 1, 0, 0), Vector3f( 0,-1, 0), Vector3f( 0, 0,-1),
+ Vector3f(-1, 0, 0), Vector3f( 0,-1, 0), Vector3f( 0, 0, 1),
+};
+
+
+bool Camera::StandaloneRenderToCubemap( RenderTexture* rt, int faceMask )
+{
+ PROFILER_AUTO_GFX(gCameraRenderToCubemapProfile, this)
+
+ if (rt->GetDimension() != kTexDimCUBE)
+ {
+ ErrorString( "Render texture must be a cubemap" );
+ return false;
+ }
+ if (!gGraphicsCaps.hasRenderToTexture || !gGraphicsCaps.hasRenderToCubemap || gGraphicsCaps.buggyCameraRenderToCubemap)
+ {
+ //ErrorString( "Render to cubemap is not supported on this hardware" );
+ // Do not print the message; if returns false that means unsupported. No need to spam the console.
+ return false;
+ }
+
+ CameraRenderOldState state;
+ StoreRenderState (state);
+
+ GetRenderManager().SetCurrentCamera (this);
+
+ PPtr<RenderTexture> oldTargetTexture = m_TargetTexture;
+ m_TargetTexture = rt;
+
+ Matrix4x4f viewMatrix;
+
+ // save FOV, aspect & render path (careful to not cause SetDirty)
+ CameraTemporarySettings settings;
+ GetTemporarySettings(settings);
+
+ m_FieldOfView = 90.0f;
+ m_Aspect = 1.0f;
+ m_ImplicitAspect = false;
+ m_DirtyProjectionMatrix = true;
+ m_DirtyWorldToClipMatrix = true;
+
+ // rendering into cubemap does not play well with deferred
+ if (CalculateRenderingPath() == kRenderPathPrePass)
+ m_RenderingPath = kRenderPathForward;
+ DebugAssert (CalculateRenderingPath() != kRenderPathPrePass);
+
+ // We may need BeginFrame() if we're called from script outside rendering loop (case 464376)
+ AutoGfxDeviceBeginEndFrame frame;
+ if( !frame.GetSuccess() )
+ return false;
+
+ GfxDevice& device = GetGfxDevice();
+
+ // render each face
+ Matrix4x4f translateMat;
+ translateMat.SetTranslate( -GetComponent(Transform).GetPosition() );
+ for( int i = 0; i < 6; ++i )
+ {
+ if( !(faceMask & (1<<i)) )
+ continue;
+ m_CurrentTargetFace = (CubemapFace)i;
+ RenderTexture::SetActive (rt, 0, (CubemapFace)i);
+ device.SetUserBackfaceMode( true ); // do this for each face (different contexts in GL!)
+ viewMatrix.SetOrthoNormalBasisInverse( kCubemapOrthoBases[i*3+0], kCubemapOrthoBases[i*3+1], kCubemapOrthoBases[i*3+2] );
+ viewMatrix *= translateMat;
+ SetWorldToCameraMatrix( viewMatrix );
+
+ CullResults cullResults;
+ StandaloneCull( NULL, "", cullResults );
+
+ Render( cullResults, kRenderFlagStandalone);
+ }
+
+ ResetWorldToCameraMatrix();
+
+ // restore FOV, aspect & render path (careful to not cause SetDirty)
+ SetTemporarySettings(settings);
+
+ m_TargetTexture = oldTargetTexture;
+
+ RestoreRenderState (state);
+ device.SetUserBackfaceMode( false );
+
+ return true;
+}
+
+bool Camera::StandaloneRenderToCubemap( Cubemap* cubemap, int faceMask )
+{
+ PROFILER_AUTO_GFX(gCameraRenderToCubemapProfile, this)
+
+ if( !cubemap )
+ {
+ ErrorString( "Cubemap is null" );
+ return false;
+ }
+ if( cubemap->GetTextureFormat() != kTexFormatARGB32 && cubemap->GetTextureFormat() != kTexFormatRGB24 )
+ {
+ ErrorString( "Unsupported cubemap format - needs to be ARGB32 or RGB24" );
+ return false;
+ }
+ if (!gGraphicsCaps.hasRenderToTexture)
+ {
+ //ErrorString( "Render to cubemap is not supported on this hardware" );
+ // Do not print the message; if returns false that means unsupported. No need to spam the console.
+ return false;
+ }
+
+ RenderManager::UpdateAllRenderers();
+
+ int size = cubemap->GetDataWidth();
+ //UInt32 flags = RenderBufferManager::kRBCreatedFromScript;
+ RenderTexture* rtt = GetRenderBufferManager().GetTempBuffer (size, size, kDepthFormat16, kRTFormatARGB32, 0, kRTReadWriteDefault);
+ if( !rtt )
+ {
+ ErrorString( "Error while rendering to cubemap - failed to get temporary render texture" );
+ return false;
+ }
+
+ CameraRenderOldState state;
+ StoreRenderState (state);
+
+ GetRenderManager().SetCurrentCamera (this);
+
+ PPtr<RenderTexture> oldTargetTexture = m_TargetTexture;
+ m_TargetTexture = rtt;
+
+ Matrix4x4f viewMatrix;
+
+ // save FOV, aspect & render path (careful to not cause SetDirty)
+ CameraTemporarySettings oldSettings;
+ GetTemporarySettings(oldSettings);
+ m_FieldOfView = 90.0f;
+ m_Aspect = 1.0f;
+ m_ImplicitAspect = false;
+ m_DirtyProjectionMatrix = true;
+ m_DirtyWorldToClipMatrix = true;
+ // rendering into cubemap does not play well with deferred
+ if (CalculateRenderingPath() == kRenderPathPrePass)
+ m_RenderingPath = kRenderPathForward;
+ DebugAssert (CalculateRenderingPath() != kRenderPathPrePass);
+
+ // We may need BeginFrame() if we're called from script outside rendering loop (case 464376)
+ AutoGfxDeviceBeginEndFrame frame;
+ if( !frame.GetSuccess() )
+ return false;
+
+ GfxDevice& device = GetGfxDevice();
+
+ // render each face
+ Matrix4x4f translateMat;
+ translateMat.SetTranslate( -GetComponent(Transform).GetPosition() );
+ RenderTexture::SetActive( rtt );
+ device.SetUserBackfaceMode( true );
+ for( int i = 0; i < 6; ++i )
+ {
+ if( !(faceMask & (1<<i)) )
+ continue;
+
+ // render the cubemap face
+ viewMatrix.SetOrthoNormalBasisInverse( kCubemapOrthoBases[i*3+0], kCubemapOrthoBases[i*3+1], kCubemapOrthoBases[i*3+2] );
+ viewMatrix *= translateMat;
+ SetWorldToCameraMatrix( viewMatrix );
+
+ CullResults cullResults;
+ StandaloneCull( NULL, "", cullResults );
+
+ Render( cullResults, kRenderFlagStandalone );
+
+ // Read back render texture into the cubemap face.
+ // If projection matrix is flipped (happens on D3D), we have to flip
+ // the image vertically so that result is correct.
+ cubemap->ReadPixels( i, 0, 0, size, size, 0, 0, device.GetInvertProjectionMatrix(), false );
+ }
+
+ ResetWorldToCameraMatrix();
+
+ SetTemporarySettings (oldSettings);
+
+ m_TargetTexture = oldTargetTexture;
+
+ RestoreRenderState (state);
+
+ device.SetUserBackfaceMode( false );
+
+ GetRenderBufferManager().ReleaseTempBuffer( rtt );
+
+ // Rendering cubemaps takes place for a sRGB color space:
+ cubemap->SetStoredColorSpace( kTexColorSpaceSRGB );
+
+ cubemap->UpdateImageData();
+
+ return true;
+}
+
+Shader* GetCameraDepthTextureShader ()
+{
+ Shader* depthShader = GetScriptMapper().FindShader("Hidden/Camera-DepthTexture");
+ if (depthShader && !depthShader->IsSupported())
+ depthShader = NULL;
+ return depthShader;
+}
+
+Shader* GetCameraDepthNormalsTextureShader ()
+{
+ Shader* depthShader = GetScriptMapper().FindShader("Hidden/Camera-DepthNormalTexture");
+ if (depthShader && !depthShader->IsSupported())
+ depthShader = NULL;
+ return depthShader;
+}
+
+
+void Camera::RenderDepthTexture (const CullResults& cullResults, RenderTexture** rt, RenderTextureFormat format, Shader* shader, const ColorRGBAf& clearColor, ShaderLab::FastPropertyName name)
+{
+ Assert (rt != NULL);
+
+ if (!shader)
+ return;
+
+ GPU_AUTO_SECTION(kGPUSectionShadowPass);
+
+ if (*rt)
+ {
+ GetRenderBufferManager().ReleaseTempBuffer (*rt);
+ *rt = NULL;
+ }
+
+ DepthBufferFormat depthFormat = UNITY_PS3 ? kDepthFormat24 : kDepthFormat16;
+ *rt = GetRenderBufferManager().GetTempBuffer (RenderBufferManager::kFullSize, RenderBufferManager::kFullSize, depthFormat, format, 0, kRTReadWriteLinear);
+ if (!*rt)
+ return;
+
+ GfxDevice& device = GetGfxDevice();
+ RenderTexture::SetActive (*rt);
+
+ GraphicsHelper::Clear (kGfxClearAll, clearColor.GetPtr(), 1.0f, 0);
+ GPU_TIMESTAMP();
+ SetupRender ();
+
+ RenderSceneShaderReplacement (cullResults.nodes, shader, "RenderType");
+
+ // The last renderer _might_ have toggled the back facing mode (to deal with mirrored geometry), so we reset this
+ // in order to make the back facing well-defined.
+ device.SetNormalizationBackface( kNormalizationDisabled, false );
+
+ ShaderLab::g_GlobalProperties->SetTexture (name, *rt);
+}
+
+void Camera::CleanupDepthTextures ()
+{
+ if (m_DepthTexture != NULL)
+ {
+ GetRenderBufferManager().ReleaseTempBuffer (m_DepthTexture);
+ m_DepthTexture = NULL;
+ }
+ if (m_DepthNormalsTexture != NULL)
+ {
+ GetRenderBufferManager().ReleaseTempBuffer (m_DepthNormalsTexture);
+ m_DepthNormalsTexture = NULL;
+ }
+}
+
+void Camera::UpdateDepthTextures (const CullResults& cullResults)
+{
+ g_ShaderKeywords.Disable (kKeywordSoftParticles);
+ bool softParticles = GetQualitySettings().GetCurrent().softParticles;
+
+ UInt32 depthTexMask = m_DepthTextureMode;
+ RenderingPath renderPath = CalculateRenderingPath();
+
+ if (softParticles && renderPath == kRenderPathPrePass)
+ g_ShaderKeywords.Enable (kKeywordSoftParticles);
+
+ if (!gGraphicsCaps.hasStencilInDepthTexture && renderPath == kRenderPathPrePass)
+ depthTexMask |= kDepthTexDepthBit; // prepass needs to generate depth texture if we don't have native capability
+
+ // In case we need depth texture:
+ // If HW supports native depth textures AND we're going to use light pre-pass:
+ // it comes for free, nothing extra to do.
+ if ((depthTexMask & kDepthTexDepthBit) && renderPath == kRenderPathPrePass && gGraphicsCaps.hasStencilInDepthTexture)
+ depthTexMask &= ~kDepthTexDepthBit;
+
+ // In case we need depth+normals texture:
+ // If we're going to use light pre-pass, it will built it for us. Nothing extra to do.
+ if ((depthTexMask & kDepthTexDepthNormalsBit) && renderPath == kRenderPathPrePass)
+ depthTexMask &= ~kDepthTexDepthNormalsBit;
+
+ // No depth textures needed
+ if (depthTexMask == 0)
+ return;
+
+ // Depth textures require some hardware support. We'll just say "need SM2.0+ and depth textures".
+ if (!RenderTexture::IsEnabled() || (int)gGraphicsCaps.shaderCaps < kShaderLevel2 || !gGraphicsCaps.supportsRenderTextureFormat[kRTFormatDepth])
+ return;
+
+ if (softParticles && (depthTexMask & kDepthTexDepthBit))
+ g_ShaderKeywords.Enable (kKeywordSoftParticles);
+
+ // if camera's viewport rect is empty or invalid, do nothing
+ if( !IsValidToRender() )
+ return;
+
+ Assert (depthTexMask != 0);
+ Assert (m_DepthTexture == NULL && m_DepthNormalsTexture == NULL);
+
+ if (depthTexMask & kDepthTexDepthBit)
+ {
+ PROFILER_AUTO_GFX(gCameraDepthTextureProfile, this)
+ RenderDepthTexture (cullResults, &m_DepthTexture, kRTFormatDepth, GetCameraDepthTextureShader(), ColorRGBAf(1,1,1,1), kSLPropCameraDepthTexture);
+
+ }
+ if (depthTexMask & kDepthTexDepthNormalsBit)
+ {
+ PROFILER_AUTO_GFX(gCameraDepthNormalsTextureProfile, this)
+ RenderDepthTexture (cullResults, &m_DepthNormalsTexture, kRTFormatARGB32, GetCameraDepthNormalsTextureShader(), ColorRGBAf(0.5f,0.5f,1,1), kSLPropCameraDepthNormalsTexture);
+ }
+
+#if GFX_SUPPORTS_OPENGLES20 || GFX_SUPPORTS_OPENGLES30
+ // when we are prepping image filters we need current rt info to determine formats of intermediate RTs
+ // so we should reset info from possible depth path
+ GfxDeviceRenderer renderer = GetGfxDevice().GetRenderer();
+ if (renderer == kGfxRendererOpenGLES20Desktop || renderer == kGfxRendererOpenGLES20Mobile || renderer == kGfxRendererOpenGLES30)
+ {
+ if (depthTexMask & (kDepthTexDepthBit | kDepthTexDepthNormalsBit))
+ RenderTexture::SetActive(m_CurrentTargetTexture);
+ }
+#endif
+}
+
+
+void Camera::StandaloneSetup ()
+{
+ GetRenderManager ().SetCurrentCamera (this);
+ // This does not setup image filters! The usage pattern (e.g. terrain engine impostors) is:
+ // Camera old = Camera.current;
+ // newcamera.RenderDontRestore();
+ // ... render our stuff
+ // Camera.SetupCurrent(old);
+ //
+ // So the last call should preserve whatever image filters were used before.
+ SetupRender( kRenderFlagStandalone | kRenderFlagSetRenderTarget );
+}
+
+void Camera::Render (CullResults& cullResults, int renderFlags)
+{
+ // if camera's viewport rect is empty or invalid, do nothing
+ if( !IsValidToRender () )
+ return;
+
+ if (m_IsRendering && IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_1_a3))
+ {
+ WarningStringObject("Attempting to render from a camera that is currently rendering. Create a copy of the camera (Camear.CopyFrom) if you wish to do this.", this);
+ return;
+ }
+
+ m_IsRendering = true;
+
+ Vector3f curPosition = GetPosition ();
+ m_Velocity = (curPosition - m_LastPosition) * GetInvDeltaTime ();
+ m_LastPosition = curPosition;
+// printf_console ("Rendering: %s\n", GetName().c_str());
+ GetRenderManager ().SetCurrentCamera (this);
+
+ // Update depth textures if needed
+ UpdateDepthTextures (cullResults);
+
+ const bool explicitReplacement = (renderFlags & kRenderFlagExplicitShaderReplace) != 0;
+
+ // Setup for rendering, also sets render texture!
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_1_a3))
+ SetupRender ( renderFlags | kRenderFlagPrepareImageFilters );
+ else
+ SetupRender ( renderFlags | (explicitReplacement ? 0 : kRenderFlagPrepareImageFilters) );
+
+ DoRender ( cullResults, NULL, renderFlags ); // Render all geometry
+
+ if ( (renderFlags & kRenderFlagStandalone) || GetEnabled() ) // camera may be already disabled here (OnPostRender)
+ {
+ //@TODO: This is inconsistent with renderGUILayer.
+ // Is there any reason for it?
+ bool renderPostLayers = cullResults.shaderReplaceData.replacementShader == NULL;
+
+ if (renderPostLayers)
+ DoRenderPostLayers (); // Handle any post-layer
+
+ if (!explicitReplacement || IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_1_a3))
+ RenderImageFilters (*m_RenderLoop, m_TargetTexture, false);
+ }
+ m_CurrentTargetTexture = m_TargetTexture;
+
+ m_IsRendering = false;
+
+ if ( (renderFlags & kRenderFlagStandalone) || GetEnabled() ) { // camera may be already disabled here (OnPostRender)
+ // When camera is rendering with replacement that is part of state (and not explicitly passed in),
+ // render GUI using regular shaders.
+ if (!explicitReplacement)
+ DoRenderGUILayer ();
+ }
+
+ // Clear camera's intermediate renderers here
+ ClearIntermediateRenderers ();
+
+ // Cleanup after all rendering
+ CleanupAfterRenderLoop (*m_RenderLoop);
+
+ CleanupDepthTextures ();
+}
+
+void Camera::AddImageFilter (const ImageFilter& filter)
+{
+ GetRenderLoopImageFilters(*m_RenderLoop).AddImageFilter (filter);
+}
+
+void Camera::RemoveImageFilter (const ImageFilter& filter)
+{
+ GetRenderLoopImageFilters(*m_RenderLoop).RemoveImageFilter (filter);
+}
+
+
+Ray Camera::ScreenPointToRay (const Vector2f& viewPortPos) const
+{
+ int viewPort[4];
+ RectfToViewport (GetScreenViewportRect (), viewPort);
+
+ Ray ray;
+ Vector3f out;
+ Matrix4x4f clipToWorld;
+ GetClipToWorldMatrix( clipToWorld );
+
+ const Matrix4x4f& camToWorld = GetCameraToWorldMatrix();
+ if( !CameraUnProject( Vector3f(viewPortPos.x, viewPortPos.y, m_NearClip), camToWorld, clipToWorld, viewPort, out ) )
+ {
+ if(viewPort[0] > 0 || viewPort[1] > 0 || viewPort[2] > 0 || viewPort[3] > 0)
+ {
+ AssertString (Format("Screen position out of view frustum (screen pos %f, %f) (Camera rect %d %d %d %d)", viewPortPos.x, viewPortPos.y, viewPort[0], viewPort[1], viewPort[2], viewPort[3]));
+ }
+ return Ray (GetPosition(), Vector3f(0, 0, 1));
+ }
+ ray.SetOrigin( out );
+ if( !CameraUnProject( Vector3f(viewPortPos.x, viewPortPos.y, m_NearClip + 1.0f), camToWorld, clipToWorld, viewPort, out ) )
+ {
+ if(viewPort[0] > 0 || viewPort[1] > 0 || viewPort[2] > 0 || viewPort[3] > 0)
+ {
+ AssertString (Format("Screen position out of view frustum (screen pos %f, %f) (Camera rect %d %d %d %d)", viewPortPos.x, viewPortPos.y, viewPort[0], viewPort[1], viewPort[2], viewPort[3]));
+ }
+ return Ray (GetPosition(), Vector3f(0, 0, 1));
+ }
+ Vector3f dir = out - ray.GetOrigin();
+ ray.SetDirection (Normalize (dir));
+
+ return ray;
+}
+
+Vector3f Camera::WorldToScreenPoint (const Vector3f& v, bool* canProject) const
+{
+ int viewPort[4];
+ RectfToViewport (GetScreenViewportRect (), viewPort);
+
+ Vector3f out;
+ bool ok = CameraProject( v, GetCameraToWorldMatrix(), GetWorldToClipMatrix(), viewPort, out );
+ if( canProject != NULL )
+ *canProject = ok;
+ return out;
+}
+
+Vector3f Camera::ScreenToWorldPoint (const Vector3f& v) const
+{
+ int viewPort[4];
+ RectfToViewport( GetScreenViewportRect(), viewPort );
+
+ Vector3f out;
+ Matrix4x4f clipToWorld;
+ GetClipToWorldMatrix( clipToWorld );
+ if( !CameraUnProject( v, GetCameraToWorldMatrix(), clipToWorld, viewPort, out ) )
+ {
+ AssertString (Format("Screen position out of view frustum (screen pos %f, %f, %f) (Camera rect %d %d %d %d)", v.x, v.y, v.z, viewPort[0], viewPort[1], viewPort[2], viewPort[3]));
+ }
+ return out;
+}
+
+Vector3f Camera::WorldToViewportPoint (const Vector3f &worldPoint) const {
+ bool tempBool;
+ Vector3f screenPoint = WorldToScreenPoint (worldPoint, &tempBool);
+ return ScreenToViewportPoint (screenPoint);
+}
+
+Vector3f Camera::ViewportToWorldPoint (const Vector3f &viewPortPoint) const {
+ Vector3f screenPoint = ViewportToScreenPoint (viewPortPoint);
+ return ScreenToWorldPoint (screenPoint);
+}
+
+Vector3f Camera::ViewportToCameraPoint (const Vector3f &viewPort) const {
+ Vector3f ndc;
+ Matrix4x4f invProjection;
+ Matrix4x4f::Invert_Full (GetProjectionMatrix(), invProjection);
+
+ ndc.x = Lerp (-1, 1, viewPort.x);
+ ndc.y = Lerp (-1, 1, viewPort.y);
+ ndc.z = Lerp (-1, 1, (viewPort.z - m_NearClip) / m_FarClip);
+
+ Vector3f cameraPoint;
+ invProjection.PerspectiveMultiplyPoint3 (ndc, cameraPoint);
+
+ cameraPoint.z = viewPort.z;
+
+ return cameraPoint;
+}
+
+Ray Camera::ViewportPointToRay (const Vector2f& viewPortPos) const {
+ Vector3f screenPos = ViewportToScreenPoint (Vector3f (viewPortPos.x, viewPortPos.y, 0.0F));
+ return ScreenPointToRay (Vector2f (screenPos.x, screenPos.y));
+}
+
+Vector3f Camera::ScreenToViewportPoint (const Vector3f& screenPos) const
+{
+ Rectf r = GetScreenViewportRect ();
+ float nx = (screenPos.x - r.x) / r.Width ();
+ float ny = (screenPos.y - r.y) / r.Height ();
+ return Vector3f (nx, ny, screenPos.z);
+}
+
+Vector3f Camera::ViewportToScreenPoint (const Vector3f& viewPos) const
+{
+ Rectf r = GetScreenViewportRect();
+ float nx = viewPos.x * r.Width () + r.x;
+ float ny = viewPos.y * r.Height () + r.y;
+ return Vector3f (nx, ny, viewPos.z);
+}
+
+float Camera::CalculateFarPlaneWorldSpaceLength () const
+{
+ Rectf screenRect = GetScreenViewportRect ();
+ Vector3f p0 = ScreenToWorldPoint (Vector3f (screenRect.x, screenRect.y, m_FarClip));
+ Vector3f p1 = ScreenToWorldPoint (Vector3f (screenRect.x + screenRect.width, screenRect.y, m_FarClip));
+
+ return Magnitude (p0 - p1);
+}
+
+float Camera::CalculateNearPlaneWorldSpaceLength () const
+{
+ Rectf screenRect = GetScreenViewportRect ();
+ Vector3f p0 = ScreenToWorldPoint (Vector3f (screenRect.x, screenRect.y, m_NearClip));
+ Vector3f p1 = ScreenToWorldPoint (Vector3f (screenRect.x + screenRect.width, screenRect.y, m_NearClip));
+ return Magnitude (p0 - p1);
+}
+
+void Camera::SetDepth (float depth)
+{
+ SetDirty();
+ m_Depth = depth;
+ if (IsActive () && GetEnabled ()) {
+ RemoveFromManager ();
+ AddToManager ();
+ }
+}
+
+void Camera::SetNormalizedViewportRect (const Rectf& normalizedRect) {
+ SetDirty();
+ m_NormalizedViewPortRect = normalizedRect;
+ WindowSizeHasChanged ();
+}
+
+void Camera::WindowSizeHasChanged () {
+ if (m_ImplicitAspect)
+ ResetAspect ();
+#if UNITY_WP8
+ else
+ {
+ // Screen rotation is handled when the projection matrix is generated, so
+ // it is necessary to mark it dirty here when a custom aspect ratio is set
+ m_DirtyProjectionMatrix = true;
+ m_DirtyWorldToClipMatrix = true;
+ }
+#endif
+}
+
+void Camera::SetAspect (float aspect)
+{
+ m_Aspect = aspect;
+ m_DirtyProjectionMatrix = true;
+ m_DirtyWorldToClipMatrix = true;
+ m_ImplicitAspect = false;
+}
+
+void Camera::ResetAspect ()
+{
+ Rectf r = GetScreenViewportRect();
+ if (r.Height () != 0)
+ m_Aspect = (r.Width () / r.Height ());
+ else
+ m_Aspect = 1.0f;
+
+ m_DirtyProjectionMatrix = true;
+ m_DirtyWorldToClipMatrix = true;
+ m_ImplicitAspect = true;
+}
+
+Vector3f Camera::GetPosition () const {
+ return GetComponent (Transform).GetPosition();
+}
+
+inline bool IsMatrixValid (const Matrix4x4f& m)
+{
+ for (int i=0;i<16;i++)
+ {
+ if (!IsFinite (m.GetPtr ()[i]))
+ return false;
+ }
+ return true;
+}
+
+const Matrix4x4f& Camera::GetWorldToCameraMatrix () const
+{
+ if( m_DirtyWorldToCameraMatrix && m_ImplicitWorldToCameraMatrix )
+ {
+ m_WorldToCameraMatrix.SetScale (Vector3f (1.0F, 1.0F, -1.0F));
+ m_WorldToCameraMatrix *= GetComponent (Transform).GetWorldToLocalMatrixNoScale ();
+ m_DirtyWorldToCameraMatrix = false;
+ }
+ return m_WorldToCameraMatrix;
+}
+
+Matrix4x4f Camera::GetCameraToWorldMatrix () const
+{
+ Matrix4x4f m;
+ Matrix4x4f::Invert_Full( GetWorldToCameraMatrix(), m );
+ return m;
+}
+
+const Matrix4x4f& Camera::GetProjectionMatrix () const
+{
+ if( m_DirtyProjectionMatrix && m_ImplicitProjectionMatrix )
+ {
+ if (!m_Orthographic)
+ m_ProjectionMatrix.SetPerspective( m_FieldOfView, m_Aspect, m_NearClip, m_FarClip );
+ else
+ m_ProjectionMatrix.SetOrtho( -m_OrthographicSize * m_Aspect, m_OrthographicSize * m_Aspect, -m_OrthographicSize, m_OrthographicSize, m_NearClip, m_FarClip );
+
+ #if UNITY_WP8
+ // Off-screen cameras don't need to be rotated with device orientation changes (case 561859)
+ if (GetTargetTexture() == NULL)
+ {
+ void RotateScreenIfNeeded(Matrix4x4f& mat);
+ RotateScreenIfNeeded(m_ProjectionMatrix);
+ }
+ #endif
+ m_DirtyProjectionMatrix = false;
+ }
+ return m_ProjectionMatrix;
+}
+
+void Camera::GetImplicitProjectionMatrix (float overrideNearPlane, Matrix4x4f& outMatrix) const
+{
+ if( !m_Orthographic )
+ outMatrix.SetPerspective( m_FieldOfView, m_Aspect, overrideNearPlane, m_FarClip );
+ else
+ outMatrix.SetOrtho( -m_OrthographicSize * m_Aspect, m_OrthographicSize * m_Aspect, -m_OrthographicSize, m_OrthographicSize, overrideNearPlane, m_FarClip );
+}
+
+
+void Camera::GetImplicitProjectionMatrix (float overrideNearPlane, float overrideFarPlane, Matrix4x4f& outMatrix) const
+{
+ if( !m_Orthographic )
+ outMatrix.SetPerspective( m_FieldOfView, m_Aspect, overrideNearPlane, overrideFarPlane );
+ else
+ outMatrix.SetOrtho( -m_OrthographicSize * m_Aspect, m_OrthographicSize * m_Aspect, -m_OrthographicSize, m_OrthographicSize, overrideNearPlane, overrideFarPlane );
+}
+
+
+void Camera::SetWorldToCameraMatrix (const Matrix4x4f& matrix)
+{
+ Assert (IsMatrixValid (matrix));
+ m_WorldToCameraMatrix = matrix;
+ m_ImplicitWorldToCameraMatrix = false;
+ m_DirtyWorldToClipMatrix = true;
+}
+
+void Camera::SetProjectionMatrix (const Matrix4x4f& matrix)
+{
+ Assert (IsMatrixValid (matrix));
+ m_ProjectionMatrix = matrix;
+ m_ImplicitProjectionMatrix = false;
+ m_DirtyWorldToClipMatrix = true;
+}
+
+const Matrix4x4f& Camera::GetWorldToClipMatrix() const
+{
+ if( m_DirtyWorldToClipMatrix )
+ {
+ MultiplyMatrices4x4 (&GetProjectionMatrix(), &GetWorldToCameraMatrix(), &m_WorldToClipMatrix);
+ m_DirtyWorldToClipMatrix = false;
+ }
+ return m_WorldToClipMatrix;
+}
+
+void Camera::GetClipToWorldMatrix( Matrix4x4f& outMatrix ) const
+{
+ Matrix4x4f::Invert_Full( GetWorldToClipMatrix(), outMatrix );
+}
+
+void Camera::SetTargetTextureBuffers(RenderTexture* tex, int colorCount, RenderSurfaceHandle* color, RenderSurfaceHandle depth, RenderTexture* rbOrigin)
+{
+ if (m_TargetTexture == PPtr<RenderTexture>(tex))
+ {
+ bool buffSame = colorCount == m_TargetColorBufferCount
+ && ::memcmp(color, m_TargetColorBuffer, colorCount*sizeof(RenderSurfaceHandle)) == 0
+ && depth == m_TargetDepthBuffer;
+
+ if(tex != 0 || buffSame)
+ return;
+ }
+
+ bool wasCurrent = GetRenderManager().GetCurrentCameraPtr() == this;
+ bool wasOffscreen = (RenderTexture*)m_TargetTexture != 0 || m_TargetBuffersOriginatedFrom != 0;
+
+ m_TargetTexture = tex;
+
+ ::memcpy(m_TargetColorBuffer, color, colorCount*sizeof(RenderSurfaceHandle));
+ if(colorCount < kMaxSupportedRenderTargets)
+ ::memset(m_TargetColorBuffer+colorCount, 0x00, (kMaxSupportedRenderTargets-colorCount)*sizeof(RenderSurfaceHandle));
+
+ m_TargetColorBufferCount = colorCount;
+ m_TargetDepthBuffer = depth;
+ m_TargetBuffersOriginatedFrom = rbOrigin;
+
+ SetDirty();
+ if (IsAddedToManager ()) {
+ GetRenderManager().RemoveCamera (this);
+ GetRenderManager().AddCamera (this);
+
+ // special case: if we were rendering to offscreen camera and changed rt in process - reactivate it
+ // other possible cases:
+ // wasn't current: nothing changes (? maybe check that was rendered already, what was the intention?)
+ // onscreen -> offscreen: we shouldn'd draw in here in that pass (and if we really wants?)
+ // offscreen -> onscreen: will be correctly drawn next time we draw onscreen cameras
+ if( wasCurrent && wasOffscreen && (tex || rbOrigin) )
+ GetRenderManager().SetCurrentCamera(this);
+ }
+}
+
+void Camera::SetTargetBuffers (int colorCount, RenderSurfaceHandle* color, RenderSurfaceHandle depth, RenderTexture* originatedFrom)
+{
+ SetTargetTextureBuffers(0, colorCount, color, depth, originatedFrom);
+}
+
+void Camera::SetTargetBuffersScript (int colorCount, const ScriptingRenderBuffer* colorScript, ScriptingRenderBuffer* depthScript)
+{
+ #define RETURN_WITH_ERROR(msg) do { ErrorString(msg); return; } while(0)
+
+ RenderSurfaceHandle color[kMaxSupportedRenderTargets];
+ for (int i = 0; i < colorCount; ++i)
+ color[i] = colorScript[i].m_BufferPtr ? RenderSurfaceHandle(colorScript[i].m_BufferPtr) : GetGfxDevice().GetBackBufferColorSurface();
+
+ RenderSurfaceHandle depth = depthScript->m_BufferPtr ? RenderSurfaceHandle(depthScript->m_BufferPtr) : GetGfxDevice().GetBackBufferDepthSurface();
+
+ // check rt/screen originated (cant mix)
+ // TODO: maybe we should simply check backBuffer flag?
+ PPtr<RenderTexture> originatedFrom(colorScript[0].m_RenderTextureInstanceID);
+ {
+ bool onScreen = originatedFrom.IsNull();
+ for(int i = 1 ; i < colorCount ; ++i)
+ {
+ if( PPtr<RenderTexture>(colorScript[i].m_RenderTextureInstanceID).IsNull() != onScreen )
+ RETURN_WITH_ERROR("You're trying to mix color buffers from RenderTexture and from screen.");
+ }
+
+ if( PPtr<RenderTexture>(depthScript->m_RenderTextureInstanceID).IsNull() != onScreen )
+ RETURN_WITH_ERROR("You're trying to mix color and depth buffers from RenderTexture and from screen.");
+ }
+
+ // check that we have matching exts
+ {
+ int colorW = color[0].object->width;
+ int colorH = color[0].object->height;
+ int depthW = depth.object->width;
+ int depthH = depth.object->height;
+
+ for(int i = 1 ; i < colorCount ; ++i)
+ {
+ int w = color[i].object->width;
+ int h = color[i].object->height;
+ if(colorW != w || colorH != h)
+ RETURN_WITH_ERROR("Camera.SetTargetBuffers can only accept RenderBuffers with same size.");
+ }
+
+ if(colorW != depthW || colorH != depthH)
+ RETURN_WITH_ERROR("Camera.SetTargetBuffers can only accept RenderBuffers with same size.");
+ }
+
+ SetTargetTextureBuffers(0, colorCount, color, depth, PPtr<RenderTexture>(colorScript[0].m_RenderTextureInstanceID));
+
+ return;
+ #undef RETURN_WITH_ERROR
+}
+
+
+void Camera::SetTargetTexture (RenderTexture *tex)
+{
+ RenderSurfaceHandle color = tex ? tex->GetColorSurfaceHandle() : GetGfxDevice().GetBackBufferColorSurface();
+ RenderSurfaceHandle depth = tex ? tex->GetDepthSurfaceHandle() : GetGfxDevice().GetBackBufferDepthSurface();
+ SetTargetTextureBuffers(tex, 1, &color, depth, 0);
+}
+
+void Camera::CopyFrom( const Camera& other )
+{
+ // copy transform from other
+ Transform& transform = GetComponent (Transform);
+ const Transform& otherTransform = other.GetComponent (Transform);
+ transform.SetLocalScale( otherTransform.GetLocalScale() );
+ transform.SetPosition( otherTransform.GetPosition() );
+
+ // normalize this... the camera can come from a very deep
+ // hierarchy. This can lead to float rounding errors :(
+ Quaternionf quat = Normalize (otherTransform.GetRotation());
+ transform.SetRotation(quat);
+
+ // copy layer of this gameobject from other
+ GetGameObject().SetLayer( other.GetGameObject().GetLayer() );
+
+ // copy camera's variables from other
+ m_ClearFlags = other.m_ClearFlags;
+ m_BackGroundColor = other.m_BackGroundColor;
+ m_NormalizedViewPortRect = other.m_NormalizedViewPortRect;
+ m_CullingMask = other.m_CullingMask;
+ m_EventMask = other.m_EventMask;
+
+ m_Depth = other.m_Depth;
+ m_Velocity = other.m_Velocity;
+ m_LastPosition = other.m_LastPosition;
+ m_OrthographicSize = other.m_OrthographicSize;
+ m_FieldOfView = other.m_FieldOfView;
+ m_NearClip = other.m_NearClip;
+ m_FarClip = other.m_FarClip;
+ m_Aspect = other.m_Aspect;
+
+ m_WorldToCameraMatrix = other.m_WorldToCameraMatrix;
+ m_ProjectionMatrix = other.m_ProjectionMatrix;
+ m_WorldToClipMatrix = other.m_WorldToClipMatrix;
+ m_DirtyWorldToCameraMatrix = other.m_DirtyWorldToCameraMatrix;
+ m_DirtyProjectionMatrix = other.m_DirtyProjectionMatrix;
+ m_DirtyWorldToClipMatrix = other.m_DirtyWorldToClipMatrix;
+ m_ImplicitWorldToCameraMatrix = other.m_ImplicitWorldToCameraMatrix;
+ m_ImplicitAspect = other.m_ImplicitAspect;
+ m_Orthographic = other.m_Orthographic;
+
+ m_TargetTexture = other.m_TargetTexture;
+ m_CurrentTargetTexture = other.m_CurrentTargetTexture;
+ m_TargetColorBufferCount = other.m_TargetColorBufferCount;
+ ::memcpy(m_TargetColorBuffer, other.m_TargetColorBuffer, sizeof(m_TargetColorBuffer));
+ m_TargetDepthBuffer = other.m_TargetDepthBuffer;
+ m_TargetBuffersOriginatedFrom = other.m_TargetBuffersOriginatedFrom;
+
+ m_ReplacementShader = other.m_ReplacementShader;
+ m_ReplacementTag = other.m_ReplacementTag;
+
+ m_DepthTextureMode = other.m_DepthTextureMode;
+ m_ClearStencilAfterLightingPass = other.m_ClearStencilAfterLightingPass;
+
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1))
+ {
+ m_RenderingPath = other.m_RenderingPath;
+ memcpy(m_LayerCullDistances, other.m_LayerCullDistances, sizeof(m_LayerCullDistances));
+ m_SortMode = other.m_SortMode;
+ m_LayerCullSpherical = other.m_LayerCullSpherical;
+ m_OcclusionCulling = other.m_OcclusionCulling;
+ m_ImplicitProjectionMatrix = other.m_ImplicitProjectionMatrix;
+ }
+
+#if UNITY_EDITOR
+ m_AnimateMaterials = other.m_AnimateMaterials;
+ m_AnimateMaterialsTime = other.m_AnimateMaterialsTime;
+#endif
+
+ SetDirty();
+}
+
+void Camera::GetTemporarySettings (CameraTemporarySettings& settings) const
+{
+ settings.renderingPath = m_RenderingPath;
+ settings.fieldOfView = m_FieldOfView;
+ settings.aspect = m_Aspect;
+ settings.implicitAspect = m_ImplicitAspect;
+}
+
+void Camera::SetTemporarySettings (const CameraTemporarySettings& settings)
+{
+ m_RenderingPath = settings.renderingPath;
+ m_FieldOfView = settings.fieldOfView;
+ m_Aspect = settings.aspect;
+ m_ImplicitAspect = settings.implicitAspect;
+ m_DirtyProjectionMatrix = true;
+ m_DirtyWorldToClipMatrix = true;
+}
+
+void Camera::SetReplacementShader( Shader* shader, const std::string& replacementTag )
+{
+ m_ReplacementShader = shader;
+ m_ReplacementTag = replacementTag;
+}
+
+void Camera::SetFov (float deg)
+{
+ SetDirty();
+ m_FieldOfView = deg;
+ m_DirtyProjectionMatrix = true;
+ m_DirtyWorldToClipMatrix = true;
+}
+
+void Camera::SetNear (float n)
+{
+ SetDirty();
+ m_NearClip = n;
+ m_DirtyProjectionMatrix = true;
+ m_DirtyWorldToClipMatrix = true;
+}
+
+void Camera::SetFar (float f)
+{
+ SetDirty();
+ m_FarClip = f;
+ m_DirtyProjectionMatrix = true;
+ m_DirtyWorldToClipMatrix = true;
+}
+
+static bool IsNonStandardProjection(const Matrix4x4f& mat)
+{
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 3; j++)
+ if (i != j && mat.Get(i, j) != 0.0f)
+ return true;
+ return false;
+}
+
+float Camera::GetProjectionNear () const
+{
+ if (m_ImplicitProjectionMatrix)
+ return m_NearClip;
+
+ const Matrix4x4f& proj = GetProjectionMatrix();
+ if (IsNonStandardProjection(proj))
+ return m_NearClip;
+
+ Vector4f nearPlane = proj.GetRow(3) + proj.GetRow(2);
+ Vector3f nearNormal(nearPlane.x, nearPlane.y, nearPlane.z);
+ return -nearPlane.w / Magnitude(nearNormal);
+}
+
+float Camera::GetProjectionFar () const
+{
+ if (m_ImplicitProjectionMatrix)
+ return m_FarClip;
+
+ const Matrix4x4f& proj = GetProjectionMatrix();
+ if (IsNonStandardProjection(proj))
+ return m_FarClip;
+
+ Vector4f farPlane = proj.GetRow(3) - proj.GetRow(2);
+ Vector3f farNormal(farPlane.x, farPlane.y, farPlane.z);
+ return farPlane.w / Magnitude(farNormal);
+}
+
+bool Camera::CalculateUsingHDR () const
+{
+ // some HDR sanity checks
+ return (m_HDR &&
+ GetBuildSettings().hasRenderTexture && gGraphicsCaps.supportsRenderTextureFormat[GetGfxDevice().GetDefaultHDRRTFormat()] &&
+ (!GetQualitySettings().GetCurrent().antiAliasing || (CalculateRenderingPath () == kRenderPathPrePass)));
+}
+
+void Camera::SetOrthographicSize (float f)
+{
+ SetDirty();
+ m_OrthographicSize = f;
+ m_DirtyProjectionMatrix = true;
+ m_DirtyWorldToClipMatrix = true;
+}
+
+void Camera::SetOrthographic (bool v)
+{
+ SetDirty();
+ m_Orthographic = v;
+ m_DirtyProjectionMatrix = true;
+ m_DirtyWorldToClipMatrix = true;
+}
+
+void Camera::SetBackgroundColor (const ColorRGBAf& color)
+{
+ m_BackGroundColor = color;
+ SetDirty();
+}
+
+void Camera::SetClearFlags (int flags)
+{
+ m_ClearFlags = flags;
+ SetDirty();
+}
+
+void Camera::SetCullingMask (UInt32 cullingMask)
+{
+ m_CullingMask.m_Bits = cullingMask;
+ SetDirty();
+}
+
+void Camera::SetEventMask (UInt32 eventMask)
+{
+ m_EventMask.m_Bits = eventMask;
+ SetDirty();
+}
+
+void Camera::DisplayHDRWarnings() const
+{
+ if((GetQualitySettings().GetCurrent().antiAliasing > 0) && (CalculateRenderingPath () == kRenderPathForward))
+ WarningStringObject("HDR and MultisampleAntiAliasing (in Forward Rendering Path) is not supported. This camera will render without HDR buffers. Disable Antialiasing in the Quality settings if you want to use HDR.", this);
+ if((!gGraphicsCaps.supportsRenderTextureFormat[GetGfxDevice().GetDefaultHDRRTFormat()]) || (!GetBuildSettings().hasRenderTexture))
+ WarningStringObject("HDR RenderTexture format is not supported on this platform. This camera will render without HDR buffers.", this);
+}
+
+bool Camera::GetRenderImmediateObjects () const
+{
+#if UNITY_EDITOR
+ return m_OnlyRenderIntermediateObjects;
+#else
+ return false;
+#endif
+}
+
+#if UNITY_EDITOR
+bool Camera::IsFiltered (Unity::GameObject& gameObject) const
+{
+ return IsGameObjectFiltered (gameObject, (CullFiltering)m_FilterMode);
+}
+#endif
+
+IMPLEMENT_CLASS_HAS_INIT (Camera)
+IMPLEMENT_OBJECT_SERIALIZE (Camera)
+
+
+void ClearWithSkybox (bool clearDepth, Camera const* camera)
+{
+ if (!camera)
+ return;
+
+ Material* skybox = camera->GetSkyboxMaterial ();
+ if (!skybox)
+ return;
+
+ GfxDevice& device = GetGfxDevice();
+ device.SetProjectionMatrix (camera->GetProjectionMatrix());
+ device.SetViewMatrix (camera->GetWorldToCameraMatrix().GetPtr());
+ SetClippingPlaneShaderProps();
+
+ if (clearDepth) {
+ float zero[4] = {0,0,0,0};
+ GraphicsHelper::Clear (kGfxClearDepthStencil, zero, 1.0f, 0);
+ GPU_TIMESTAMP();
+ }
+ Skybox::RenderSkybox (skybox, *camera);
+}
+
+
+Rectf GetCameraOrWindowRect (const Camera* camera)
+{
+ if (camera)
+ return camera->GetScreenViewportRect ();
+ else
+ {
+ Rectf rect = GetRenderManager().GetWindowRect();
+ rect.x = rect.y = 0.0f;
+ return rect;
+ }
+}
+
+/*
+Texture* Camera::GetUmbraOcclusionBufferTexture ()
+{
+ Umbra::OcclusionBuffer::BufferDesc SrcDesc;
+ Umbra::OcclusionBuffer* occlusionBuffer = m_UmbraVisibility->getOutputBuffer();
+ occlusionBuffer->getBufferDesc(SrcDesc);
+
+ if (!SrcDesc.width || !SrcDesc.height)
+ return NULL;
+
+ Assert(SrcDesc.width == 128 && SrcDesc.height == 128);
+
+ UInt8 TempBuffer[128*128];
+ occlusionBuffer->getBuffer(TempBuffer);
+
+ if (!m_OcclusionBufferTexture)
+ {
+ m_OcclusionBufferTexture = CreateObjectFromCode<Texture2D>();
+ m_OcclusionBufferTexture->InitTexture(SrcDesc.width, SrcDesc.height, kTexFormatRGBA32, 0);
+ }
+
+ ImageReference DstDesc;
+ m_OcclusionBufferTexture->GetWriteImageReference(&DstDesc, 0, 0);
+
+ UInt32* DstBuffer = (UInt32*)DstDesc.GetImageData();
+ int DstStride = DstDesc.GetRowBytes();
+
+ for (int y=0; y<DstDesc.GetHeight(); y++)
+ {
+ for (int x=0; x<DstDesc.GetWidth(); x++)
+ {
+ int c = (int)TempBuffer[y*128+x];
+ int a = 0xff;
+#if UNITY_LITTLE_ENDIAN
+ DstBuffer[y*DstDesc.GetWidth()+x] = (a<<24) + (c<<16) + (c<<8) + c;
+#else
+ DstBuffer[y*DstDesc.GetWidth()+x] = (c<<24) + (c<<16) + (c<<8) + a;
+#endif
+ }
+ }
+
+ return dynamic_pptr_cast<Texture*>(m_OcclusionBufferTexture);
+}
+ */
diff --git a/Runtime/Camera/Camera.h b/Runtime/Camera/Camera.h
new file mode 100644
index 0000000..184aa8e
--- /dev/null
+++ b/Runtime/Camera/Camera.h
@@ -0,0 +1,503 @@
+#ifndef CAMERA_H
+#define CAMERA_H
+
+#include "Runtime/GameCode/Behaviour.h"
+#include <vector>
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Geometry/Ray.h"
+#include "Runtime/Graphics/RenderTexture.h" //@TODO remove
+#include "Runtime/Camera/RenderLoops/RenderLoop.h" //@TODO remove
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Modules/ExportModules.h"
+
+struct ImageFilter;
+class Cubemap;
+class Plane;
+class IntermediateRenderers;
+class Shader;
+struct RenderLoop;
+struct ShadowCullData;
+struct CullResults;
+struct CullingParameters;
+struct ShaderReplaceData;
+struct SceneCullingParameters;
+struct CameraCullingParameters;
+class Vector2f;
+
+struct DrawGridParameters;
+
+namespace Unity {class Material;}
+namespace Umbra { class DebugRenderer; }
+
+struct CameraRenderOldState
+{
+ void Reset()
+ {
+ subshaderIndex = 0;
+ currentPass = 0;
+ memset (&viewport, 0, sizeof(viewport));
+ memset (&matWorld, 0, sizeof(matWorld));
+ memset (&matView, 0, sizeof(matView));
+ matProj = Matrix4x4f::identity;
+ }
+ PPtr<Material> material;
+ PPtr<Shader> shader;
+ int subshaderIndex;
+ int currentPass;
+
+ int viewport[4];
+ PPtr<Camera> camera;
+ PPtr<RenderTexture> activeRT;
+
+ float matWorld[16];
+ float matView[16];
+ Matrix4x4f matProj;
+};
+
+struct CameraTemporarySettings
+{
+ int renderingPath;
+ float fieldOfView;
+ float aspect;
+ bool implicitAspect;
+};
+
+// The Camera
+class EXPORT_COREMODULE Camera : public Behaviour {
+public:
+ enum SortMode {
+ kSortDefault = 0,
+ kSortPerspective = 1,
+ kSortOrthographic = 2,
+ };
+
+ enum { kNumLayers = 32 };
+
+public:
+ Camera (MemLabelId label, ObjectCreationMode mode);
+ // ~Camera (); declared-by-macro
+ REGISTER_DERIVED_CLASS (Camera, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (Camera)
+
+ // Tag class as sealed, this makes QueryComponent faster.
+ static bool IsSealedClass () { return true; }
+
+ virtual void Reset ();
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ virtual void CheckConsistency ();
+
+ enum RenderFlag {
+ kRenderFlagStandalone = (1<<0),
+ kRenderFlagSetRenderTarget = (1<<1),
+ kRenderFlagPrepareImageFilters = (1<<2),
+ kRenderFlagDontRestoreRenderState = (1<<3),
+ kRenderFlagSetRenderTargetFinal = (1<<4),
+ kRenderFlagExplicitShaderReplace = (1<<5),
+ };
+
+
+ // Set up the viewport, render target, load modelview & projection matrices
+ void SetupRender (int renderFlags = 0); // bitmask of kRenderFlagXXX
+
+ // Cull with default culling parameters
+ void Cull (CullResults& results);
+ void StandaloneCull (Shader* replacementShader, const std::string& replacementTag, CullResults& results);
+
+ // Cull With custom parameters
+ void CustomCull (const CameraCullingParameters& params, CullResults& cullResults);
+
+
+ static void PrepareSceneCullingParameters (const CameraCullingParameters& parameters, RenderingPath renderPath, CullResults& results);
+ void CalculateFrustumPlanes(Plane* frustum, const Matrix4x4f& overrideWorldToClip, float overrideFarPlane, float& outBaseFarDistance, bool implicitNearFar) const;
+ void CalculateCullingParameters(CullingParameters& cullingParameters) const;
+ void CalculateCustomCullingParameters(CullingParameters& cullingParameters, const Plane* planes, int planeCount) const;
+
+
+ // Clear the camera for how its set up - use skybox if neccessary
+ void Clear ();
+ void ClearNoSkybox (bool noDepth);
+ void RenderSkybox ();
+
+ // Set up the camera transform & render
+ void Render (CullResults& cullResults, int renderFlags); // bitmask of kRenderFlagXXX; kRenderFlagPrepareImageFilters always implied
+
+ // Out-of-order cull / rendering.
+ void StandaloneRender (UInt32 renderFlags, Shader* replacementShader, const std::string& replacementTag);
+ void StandaloneSetup ();
+ bool StandaloneRenderToCubemap (RenderTexture* rt, int faceMask);
+ bool StandaloneRenderToCubemap (Cubemap* cubemap, int faceMask);
+
+ void SetFov (float deg);
+ float GetFov () const { return m_FieldOfView; }
+ void SetNear (float n);
+ float GetNear () const { return m_NearClip; }
+ void SetFar (float f);
+ float GetFar () const { return m_FarClip; }
+
+ // Projection's near and far plane (can differ from GetNear/Far() for custom projection matrix)
+ float GetProjectionNear () const;
+ float GetProjectionFar () const;
+
+ void SetHDR (bool enable) { m_HDR = enable; SetDirty(); }
+ bool GetUsingHDR () const { return m_UsingHDR; } // Cached when setting up the render
+ // A recompute is needed if Camera.hdr is called from script before rendering (case 483603)
+ bool CalculateUsingHDR () const;
+
+ void SetRenderingPath (int rp) { m_RenderingPath = rp; SetDirty(); }
+ RenderingPath GetRenderingPath () const { return static_cast<RenderingPath>(m_RenderingPath); }
+
+ void SetOrthographicSize (float f);
+ float GetOrthographicSize () const { return m_OrthographicSize; }
+
+ bool GetOrthographic() const { return m_Orthographic; }
+ void SetOrthographic (bool v);
+
+ void GetTemporarySettings (CameraTemporarySettings& settings) const;
+ void SetTemporarySettings (const CameraTemporarySettings& settings);
+
+ float GetAspect() const { return m_Aspect; }
+ void SetAspect (float aspect);
+ void ResetAspect ();
+
+
+ bool IsValidToRender() const;
+
+ void SetNormalizedViewportRect (const Rectf& normalizedRect);
+ Rectf GetNormalizedViewportRect () const { return m_NormalizedViewPortRect; }
+
+ // The screen view port rect of the camera.
+ // If the cameras normalized viewport rect is set to be the fullscreen, then this will always go from
+ // 0, 0 to width, height.
+ Rectf GetScreenViewportRect () const { return GetCameraRect(true); }
+ // Similar to GetScreenViewportRect, except this can have non-zero origin even for fullscreen cameras.
+ // This only ever happens in editor's game view when using forced aspect ratio or size.
+ Rectf GetPhysicalViewportRect() const { return GetCameraRect(false); }
+
+ void SetScreenViewportRect (const Rectf& pixelRect);
+
+ // Get the final in-rendertarget render rectangle.
+ // This takes into account any render texture setup we may have.
+ Rectf GetRenderRectangle() const;
+
+
+ /// The Camera render order is determined by sorting all cameras by depth.
+ /// Small depth camera are rendered first, big depth cameras last.
+ float GetDepth () const { return m_Depth; }
+ void SetDepth (float depth);
+
+ /// Set the background color of the camera.
+ void SetBackgroundColor (const ColorRGBAf& color);
+ ColorRGBAf GetBackgroundColor () const { return m_BackGroundColor; }
+
+ // The clearing mode used for the camera.
+ enum ClearMode {
+ kSkybox = 1,
+ kSolidColor = 2,
+ kDepthOnly = 3,
+ kDontClear = 4
+ // Watch out for check consistency when changing this!
+ };
+
+ void SetClearFlags (int flags);
+ ClearMode GetClearFlags () const { return static_cast<ClearMode>(m_ClearFlags); }
+
+ void SetCullingMask (UInt32 cullingMask);
+ UInt32 GetCullingMask () const { return m_CullingMask.m_Bits; }
+
+ void SetEventMask (UInt32 cullingMask);
+ UInt32 GetEventMask () const { return m_EventMask.m_Bits; }
+
+ Vector3f GetPosition () const;
+
+ void SetUseOcclusionCulling (bool occlusionCull) { m_OcclusionCulling = occlusionCull; }
+ bool GetUseOcclusionCulling () const { return m_OcclusionCulling; }
+
+ /// A screen space point is defined in pixels.
+ /// The left-bottom of the screen is (0,0). The right-top is (screenWidth,screenHeight)
+ /// The z position is between 0...1. 0 is on the near plane. 1 is on the far plane
+
+ /// A viewport space point is normalized and relative to the camera
+ /// The left-bottom of the camera is (0,0). The top-right is (1,1)
+ /// The z position is between 0...1. 0 is on the near plane. 1 is on the far plane
+
+ /// Projects a World space point into screen space.
+ /// on return: canProject is true if the point could be projected to the screen (The point is inside the frustum)
+ Vector3f WorldToScreenPoint (const Vector3f& worldSpacePoint, bool* canProject = NULL) const;
+ /// Unprojects a screen space point into world space
+ Vector3f ScreenToWorldPoint (const Vector3f& screenSpacePoint) const;
+
+ /// Projects a world space point into viewport space
+ Vector3f WorldToViewportPoint (const Vector3f &worldSpace) const;
+ /// Unprojects a view port space into world space
+ Vector3f ViewportToWorldPoint (const Vector3f &viewPort) const;
+
+ /// Unprojects a view port space into camera space
+ Vector3f ViewportToCameraPoint (const Vector3f &viewPort) const;
+
+ // Converts a screen point into a world space ray
+ Ray ScreenPointToRay (const Vector2f& screenPos) const;
+ // Converts a viewport point into a world space ray
+ Ray ViewportPointToRay (const Vector2f& viewportPos) const;
+
+ // Converts a point between screen space and viewport space
+ Vector3f ScreenToViewportPoint (const Vector3f& screenPos) const;
+ Vector3f ViewportToScreenPoint (const Vector3f& viewPortPos) const;
+
+ // Calculates the distance between the left and right
+ // edges of the frustum pyramid at the far plane
+ float CalculateFarPlaneWorldSpaceLength () const;
+
+ // Calculates the distance between the left and right
+ // edges of the frustum pyramid at the near plane
+ float CalculateNearPlaneWorldSpaceLength () const;
+
+ void WindowSizeHasChanged ();
+
+ void AddImageFilter (const ImageFilter& filter);
+ void RemoveImageFilter (const ImageFilter& filter);
+
+ void ClearIntermediateRenderers( size_t startIndex = 0 );
+ IntermediateRenderers& GetIntermediateRenderers() { return *m_IntermediateRenderers; }
+
+ const Vector3f& GetVelocity () const { return m_Velocity; }
+
+ const Matrix4x4f& GetWorldToCameraMatrix () const;
+ Matrix4x4f GetCameraToWorldMatrix () const;
+
+ const Matrix4x4f& GetProjectionMatrix () const;
+ void GetImplicitProjectionMatrix (float overrideNearPlane, Matrix4x4f& outMatrix) const;
+ void GetImplicitProjectionMatrix (float overrideNearPlane, float overrideFarPlane, Matrix4x4f& outMatrix) const;
+
+ void GetClipToWorldMatrix( Matrix4x4f& outMatrix ) const;
+ const Matrix4x4f& GetWorldToClipMatrix() const;
+
+ void SetWorldToCameraMatrix (const Matrix4x4f& matrix);
+ void SetProjectionMatrix (const Matrix4x4f& matrix);
+
+ void ResetWorldToCameraMatrix () { m_ImplicitWorldToCameraMatrix = true; m_DirtyWorldToCameraMatrix = true; m_DirtyWorldToClipMatrix = true; }
+ void ResetProjectionMatrix () { m_ImplicitProjectionMatrix = true; m_DirtyProjectionMatrix = true; m_DirtyWorldToClipMatrix = true; }
+ bool IsImplicitWorldToCameraMatrix() const { return m_ImplicitWorldToCameraMatrix; }
+ bool IsImplicitProjectionMatrix() const { return m_ImplicitProjectionMatrix; }
+
+ void SetReplacementShader( Shader* shader, const std::string& replacementTag );
+ void ResetReplacementShader() { m_ReplacementShader.SetInstanceID(0); m_ReplacementTag.clear(); }
+ Shader *GetReplacementShader() const { return m_ReplacementShader; }
+ string GetReplacementShaderTag() const {return m_ReplacementTag; }
+
+ // Get/Set the texture to render into.
+ RenderTexture *GetTargetTexture () const { return m_TargetTexture; }
+ void SetTargetTexture (RenderTexture *tex);
+
+ void SetTargetBuffers (int colorCount, RenderSurfaceHandle* color, RenderSurfaceHandle depth, RenderTexture* originatedFrom);
+ void SetTargetBuffersScript (int colorCount, const ScriptingRenderBuffer* color, ScriptingRenderBuffer* depth);
+
+ // TODO: mrt support?
+ RenderSurfaceHandle GetTargetColorBuffer() const { return m_TargetColorBuffer[0]; }
+ RenderSurfaceHandle GetTargetDepthBuffer() const { return m_TargetDepthBuffer; }
+
+ const float *GetLayerCullDistances() const { return m_LayerCullDistances; }
+ void SetLayerCullDistances(float *layerCullDistances) {memcpy(m_LayerCullDistances,layerCullDistances,sizeof(float)*kNumLayers);}
+
+ bool GetLayerCullSpherical() const { return m_LayerCullSpherical; }
+ void SetLayerCullSpherical(bool enable) { m_LayerCullSpherical = enable; }
+
+ SortMode GetSortMode() const { return m_SortMode; }
+ void SetSortMode (SortMode m) { m_SortMode = m; }
+
+ // Get the current target. This can be different than the textureTarget if some image filters require
+ // A temporary buffer to render into.
+ RenderTexture *GetCurrentTargetTexture () const { return m_CurrentTargetTexture; }
+ void SetCurrentTargetTexture (RenderTexture* rt) { m_CurrentTargetTexture = rt; }
+
+ void TransformChanged ();
+ static void InitializeClass ();
+ static void CleanupClass () {}
+
+ void CopyFrom( const Camera& other );
+
+ void CalculateFarCullDistances (float* farCullDistances, float baseFarDistance) const;
+
+ enum DepthTextureModes {
+ kDepthTexDepthBit = (1<<0),
+ kDepthTexDepthNormalsBit = (1<<1),
+ };
+ UInt32 GetDepthTextureMode() const { return m_DepthTextureMode; }
+ void SetDepthTextureMode (UInt32 m) { m_DepthTextureMode = m; }
+
+ bool GetClearStencilAfterLightingPass() const { return m_ClearStencilAfterLightingPass; }
+ void SetClearStencilAfterLightingPass (bool clear) { m_ClearStencilAfterLightingPass = clear; }
+
+ RenderingPath CalculateRenderingPath() const;
+ bool CalculateNeedsToRenderIntoRT() const;
+ int CalculateAntiAliasingForRT() const;
+
+ bool GetUsesScreenForCompositing (bool forceIntoRT) const;
+
+
+ // Get the resolved skybox material we want to render with.
+ Material *GetSkyboxMaterial () const;
+
+ // Implementations in EditorCameraDrawing.cpp
+ #if UNITY_EDITOR
+
+ enum EditorDrawingMode {
+ kEditorDrawTextured = 0,
+ kEditorDrawWire = 1,
+ kEditorDrawTexturedWire = 2,
+ kEditorDrawRenderPaths = 3,
+ kEditorDrawLightmapResolution = 4,
+ kEditorDrawModeCount
+ };
+
+
+ void ClearEditorCamera (); // clears
+ void RenderEditorCamera (EditorDrawingMode mode, const DrawGridParameters* gridParam); // renders camera content (does not clear)
+ void FinishRenderingEditorCamera ();
+ void SetOnlyRenderIntermediateObjects() { m_OnlyRenderIntermediateObjects = true; }
+ static bool ShouldShowChannelErrors (const Camera* ptr);
+ void RenderEditorCameraFade (float fade);
+ void SetAnimateMaterials (bool animate);
+ bool GetAnimateMaterials () const { return m_AnimateMaterials; }
+ void SetAnimateMaterialsTime (float time);
+
+ bool IsFiltered (Unity::GameObject& gameObject) const;
+ void SetFilterMode (int filterMode) { m_FilterMode = filterMode; }
+ int GetFilterMode () const { return m_FilterMode; }
+
+ #endif
+
+// Texture* GetUmbraOcclusionBufferTexture ();
+
+private:
+
+ typedef void PerformRenderFunction (Camera& camera, RenderLoop& loop, CullResults& contents);
+ void DoRender( CullResults& cullResults, PerformRenderFunction* customRender, int renderFlags ); // Render all objects
+
+ void DoRenderPostLayers(); // Render any post-layers (before image effects)
+ void DoRenderGUILayer();
+
+ void DoClear (UInt32 gfxClearFlags);
+
+ // Behaviour stuff
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+
+ void SetCameraShaderProps();
+
+ void UpdateDepthTextures (const CullResults& cullResults);
+ void RenderDepthTexture (const CullResults& cullResults, RenderTexture** rt, RenderTextureFormat format, Shader* shader, const ColorRGBAf& clearColor, ShaderLab::FastPropertyName name);
+
+ void CleanupDepthTextures ();
+
+ void DisplayHDRWarnings() const;
+
+ Rectf GetCameraRect (bool zeroOrigin) const;
+
+ bool GetRenderImmediateObjects () const;
+
+private:
+
+ mutable Matrix4x4f m_WorldToCameraMatrix;
+ mutable Matrix4x4f m_ProjectionMatrix;
+ mutable Matrix4x4f m_WorldToClipMatrix;
+
+
+ // NOTE: whenever adding new camera properties, make sure they are copied as
+ // appropriate in CopyFrom(), and extend CameraCopyFromWorks runtime test
+ // to cover it.
+
+
+ RenderLoop* m_RenderLoop;
+
+ PPtr<RenderTexture> m_TargetTexture; ///< The texture to render this camera into
+
+ RenderSurfaceHandle m_TargetColorBuffer[kMaxSupportedRenderTargets];
+ int m_TargetColorBufferCount;
+ RenderSurfaceHandle m_TargetDepthBuffer;
+ // this is here to set as active RT along with render buffers
+ // dx uses current rt to check if we render onscreen (and tweak projection/offsets/etc)
+ // image filters use target RT in calculations of rt to draw to
+ // while ideally we want simple IsRenderingOnscreen + image effects to use render buffers
+ // we want it to work naow
+ RenderTexture* m_TargetBuffersOriginatedFrom;
+
+ RenderTexture* m_DepthTexture;
+ RenderTexture* m_DepthNormalsTexture;
+
+ RenderTexture* m_CurrentTargetTexture; // The texture we're rendering into _right now_
+
+ PPtr<Shader> m_ReplacementShader;
+ std::string m_ReplacementTag;
+
+ IntermediateRenderers* m_IntermediateRenderers;
+
+ unsigned int m_ClearFlags; ///< enum { Skybox = 1, Solid Color = 2, Depth only = 3, Don't Clear = 4 }
+ ColorRGBAf m_BackGroundColor; ///< The color to which camera clears the screen
+ Rectf m_NormalizedViewPortRect;
+
+ BitField m_CullingMask; ///< Which layers the camera does render
+ BitField m_EventMask; ///< Which layers receive events
+
+ float m_Depth; ///< A camera with a larger depth is drawn on top of a camera with a smaller depth range {-100, 100}
+ Vector3f m_Velocity;
+ Vector3f m_LastPosition;
+
+ float m_OrthographicSize;
+ float m_FieldOfView; ///< Field of view of the camera range { 0.00001, 179 }
+ float m_NearClip; ///< Near clipping plane
+ float m_FarClip; ///< Far clipping plane
+ int m_RenderingPath; ///< enum { Use Player Settings = -1, Vertex Lit=0, Forward=1, Deferred Lighting=2 } Rendering path to use.
+
+ float m_LayerCullDistances[kNumLayers];
+ float m_Aspect;
+ SortMode m_SortMode;
+
+ CubemapFace m_CurrentTargetFace; // current cubemap face we're rendering into (only used while rendering into a cubemap)
+
+ UInt32 m_DepthTextureMode;
+
+ mutable bool m_DirtyWorldToCameraMatrix;
+ mutable bool m_DirtyProjectionMatrix;
+ mutable bool m_DirtyWorldToClipMatrix;
+ bool m_ImplicitWorldToCameraMatrix;
+ bool m_ImplicitProjectionMatrix;
+ bool m_ImplicitAspect;
+ bool m_Orthographic; ///< Is camera orthographic?
+ bool m_OcclusionCulling;
+ bool m_LayerCullSpherical;
+ bool m_HDR;
+ bool m_UsingHDR;
+ bool m_IsRendering;
+ bool m_ClearStencilAfterLightingPass;
+
+
+ // NOTE: whenever adding new camera properties, make sure they are copied as
+ // appropriate in CopyFrom(), and extend CameraCopyFromWorks runtime test
+ // to cover it.
+
+
+ void SetTargetTextureBuffers(RenderTexture* tex, int colorCount, RenderSurfaceHandle* color, RenderSurfaceHandle depth, RenderTexture* rbOrigin);
+
+ #if UNITY_EDITOR
+ int m_FilterMode;
+ bool m_OnlyRenderIntermediateObjects;
+ bool m_IsSceneCamera;
+ CameraRenderOldState m_OldCameraState;
+ bool m_AnimateMaterials;
+ float m_AnimateMaterialsTime;
+ #endif
+};
+
+void StoreRenderState (CameraRenderOldState& state);
+void RestoreRenderState (CameraRenderOldState& state);
+
+Shader* GetCameraDepthTextureShader ();
+Shader* GetCameraDepthNormalsTextureShader ();
+
+void ClearWithSkybox (bool clearDepth, Camera const* camera);
+
+Rectf GetCameraOrWindowRect (const Camera* camera);
+
+#endif
diff --git a/Runtime/Camera/CameraCullingParameters.h b/Runtime/Camera/CameraCullingParameters.h
new file mode 100644
index 0000000..5936432
--- /dev/null
+++ b/Runtime/Camera/CameraCullingParameters.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "ShaderReplaceData.h"
+#include "Runtime/Utilities/EnumFlags.h"
+#include "UnityPrefix.h"
+#include "Camera.h"
+#include "External/shaderlab/Library/shaderlab.h"
+
+
+namespace Umbra { class DebugRenderer; }
+
+enum CullFlag
+{
+ kCullFlagForceEvenIfCameraIsNotActive = 1 << 0,
+ kCullFlagOcclusionCull = 1 << 1,
+ kCullFlagNeedsLighting = 1 << 2,
+};
+
+struct CameraCullingParameters
+{
+ Camera* cullingCamera;
+ ShaderReplaceData explicitShaderReplace;
+ CullFlag cullFlag;
+ Umbra::DebugRenderer* umbraDebugRenderer;
+ UInt32 umbraDebugFlags;
+
+ CameraCullingParameters (Camera& cam, CullFlag flag)
+ {
+ cullingCamera = &cam;
+ cullFlag = flag;
+ umbraDebugRenderer = NULL;
+ umbraDebugFlags = 0;
+ }
+};
+
+ENUM_FLAGS(CullFlag);
diff --git a/Runtime/Camera/CameraUtil.cpp b/Runtime/Camera/CameraUtil.cpp
new file mode 100644
index 0000000..1bdcd4b
--- /dev/null
+++ b/Runtime/Camera/CameraUtil.cpp
@@ -0,0 +1,452 @@
+#include "UnityPrefix.h"
+#include "CameraUtil.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Rect.h"
+#include "RenderManager.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Graphics/ScreenManager.h"
+
+void FlipScreenRectIfNeeded( const GfxDevice& device, int screenviewcoord[4] )
+{
+ // Flip viewport rect vertically for D3D or dashboard widgets.
+ // But only do it when rendering to screen, not a texture!
+ if( (!device.UsesOpenGLTextureCoords() || device.GetInvertProjectionMatrix()) && device.GetActiveRenderTexture() == NULL )
+ {
+ int height;
+ #if GFX_SUPPORTS_D3D9
+ // On D3D, always use height of current actual render target. Others may be off, particularly in editor's game view
+ if (device.GetRenderer() == kGfxRendererD3D9 || device.GetRenderer() == kGfxRendererD3D11)
+ height = device.GetCurrentTargetHeight();
+ else
+ #endif
+ // Use screen height, not render manager's window height. In editor's GameView, the window height might be smaller.
+ height = GetScreenManager().GetHeight();
+
+ int miny = height - screenviewcoord[1];
+ int maxy = height - (screenviewcoord[3] + screenviewcoord[1]);
+ if (maxy < miny)
+ std::swap(maxy, miny);
+ miny = std::max(miny, 0);
+ screenviewcoord[1] = miny;
+ screenviewcoord[3] = maxy - miny;
+ DebugAssertIf( screenviewcoord[1] < 0 || screenviewcoord[3] < 0 );
+ }
+ #if GFX_USES_VIEWPORT_OFFSET
+ if( device.GetActiveRenderTexture() == NULL )
+ {
+ float xOffs, yOffs;
+ device.GetViewportOffset(xOffs, yOffs);
+ screenviewcoord[0] += xOffs;
+ screenviewcoord[1] += yOffs;
+ }
+ #endif
+ #if UNITY_WP8
+ ScreenOrientation const screenOrientation = GetScreenManager().GetScreenOrientation();
+ bool renderToTexture = device.GetActiveRenderTexture() != NULL;
+ bool rotated = (screenOrientation == ScreenOrientation::kLandscapeLeft) || (screenOrientation == ScreenOrientation::kLandscapeRight);
+ if (rotated && !renderToTexture)
+ {
+ float x = screenviewcoord[0];
+ float y = screenviewcoord[1];
+
+ if (screenOrientation == ScreenOrientation::kLandscapeRight)
+ {
+ float width = screenviewcoord[2];
+ float targetWidth = device.GetActiveRenderTexture() ? device.GetCurrentTargetWidth() : GetScreenManager().GetWidth();
+ screenviewcoord[0] = y;
+ screenviewcoord[1] = targetWidth - x - width;
+ }
+ else if (screenOrientation == ScreenOrientation::kLandscapeLeft)
+ {
+ float height = screenviewcoord[3];
+ float targetHeight = device.GetActiveRenderTexture() ? device.GetCurrentTargetHeight() : GetScreenManager().GetHeight();
+ screenviewcoord[0] = targetHeight - y - height;
+ screenviewcoord[1] = x;
+ }
+
+ std::swap(screenviewcoord[2], screenviewcoord[3]);
+ }
+ #endif
+}
+
+#if UNITY_WP8
+
+void RotateScreenIfNeeded(Matrix4x4f& projection)
+{
+ ScreenOrientation const screenOrientation = GetScreenManager().GetScreenOrientation();
+ if (screenOrientation == ScreenOrientation::kLandscapeLeft)
+ {
+ Vector4f const x = projection.GetRow(0);
+ Vector4f const y = projection.GetRow(1);
+ projection.SetRow(0, y);
+ projection.SetRow(1, -x);
+ }
+ else if (screenOrientation == ScreenOrientation::kLandscapeRight)
+ {
+ Vector4f const x = projection.GetRow(0);
+ Vector4f const y = projection.GetRow(1);
+ projection.SetRow(0, -y);
+ projection.SetRow(1, x);
+ }
+}
+
+static void RotatePointIfNeeded(Vector3f& point, bool unrotate)
+{
+ ScreenOrientation const screenOrientation = GetScreenManager().GetScreenOrientation();
+ if (screenOrientation == ScreenOrientation::kLandscapeLeft)
+ {
+ auto const x = point.x;
+ auto const y = point.y;
+
+ if (unrotate)
+ {
+ point.x = y;
+ point.y = -x;
+ }
+ else
+ {
+ point.x = -y;
+ point.y = x;
+ }
+ }
+ else if (screenOrientation == ScreenOrientation::kLandscapeRight)
+ {
+ auto const x = point.x;
+ auto const y = point.y;
+
+ if (unrotate)
+ {
+ point.x = -y;
+ point.y = x;
+ }
+ else
+ {
+ point.x = y;
+ point.y = -x;
+ }
+ }
+}
+
+#endif
+
+void CalcPixelMatrix (const Rectf& screenRect, Matrix4x4f &out)
+{
+ out.SetOrtho( screenRect.x, screenRect.GetRight(), screenRect.y, screenRect.GetBottom(), -1.0f, 100.0f );
+ #if UNITY_WP8
+ RotateScreenIfNeeded(out);
+ #endif
+}
+
+void ApplyTexelOffsetsToPixelMatrix( bool invertYTexelOffset, Matrix4x4f& matrix )
+{
+ float offsetX, offsetY;
+ GetHalfTexelOffsets( offsetX, offsetY );
+ if( invertYTexelOffset )
+ offsetY = -offsetY;
+
+ matrix.Get(0,3) -= offsetX * matrix.Get(0,0);
+ matrix.Get(1,3) -= offsetY * matrix.Get(1,1);
+}
+
+
+void LoadPixelMatrix( const Rectf& screenRect, GfxDevice& device, bool setMatrix, bool invertYTexelOffset )
+{
+ Matrix4x4f ortho;
+ CalcPixelMatrix( screenRect, ortho );
+ ApplyTexelOffsetsToPixelMatrix( invertYTexelOffset, ortho );
+ device.SetProjectionMatrix (ortho);
+ if( setMatrix )
+ device.SetViewMatrix (Matrix4x4f::identity.GetPtr()); // implicitly sets world to identity
+}
+
+void GetHalfTexelOffsets( float& outx, float& outy )
+{
+ GfxDevice& device = GetGfxDevice();
+ if( device.UsesHalfTexelOffset() )
+ {
+ outx = 0.5f;
+ outy = 0.5f;
+ if( device.GetActiveRenderTexture() == NULL )
+ outy = -outy;
+ }
+ else
+ {
+ outx = outy = 0.0f;
+ }
+}
+
+void LoadFullScreenOrthoMatrix( float nearPlane, float farPlane, bool forceNoHalfTexelOffset )
+{
+ GfxDevice& device = GetGfxDevice();
+ float offsetX, offsetY;
+ if( device.UsesHalfTexelOffset() && !forceNoHalfTexelOffset)
+ {
+ // viewport of the device should always have the correct size of
+ // the render target (both when rendering to screen and to a
+ // render texture), so calc half texel offset from that.
+ int viewport[4];
+ device.GetViewport(viewport);
+ int width = viewport[2];
+ int height = viewport[3];
+ offsetX = width ? (0.5f / width) : 0.0f;
+ offsetY = height ? (0.5f / height) : 0.0f;
+ if( device.GetActiveRenderTexture() == NULL )
+ offsetY = -offsetY;
+ }
+ else
+ {
+ offsetX = offsetY = 0.0f;
+ }
+ Matrix4x4f matrix;
+ matrix.SetOrtho( offsetX, 1.0f + offsetX, offsetY, 1.0f + offsetY, nearPlane, farPlane );
+ device.SetProjectionMatrix (matrix);
+ device.SetViewMatrix (Matrix4x4f::identity.GetPtr()); // implicitly sets world to identity
+}
+
+void SetupPixelCorrectCoordinates()
+{
+ GfxDevice& device = GetGfxDevice();
+
+ int viewcoords[4];
+ Rectf r = GetRenderManager ().GetWindowRect();
+ RectfToViewport( r, viewcoords );
+ FlipScreenRectIfNeeded( device, viewcoords );
+ device.SetViewport( viewcoords[0], viewcoords[1], viewcoords[2], viewcoords[3] );
+
+ LoadPixelMatrix( r, device, true, false );
+}
+
+void RectfToViewport( const Rectf& r, int viewPort[4] )
+{
+ // We have to take care that the viewport doesn't exceed the buffer size (case 569703).
+ // Bad rounding to integer makes D3D11 crash and burn.
+ viewPort[0] = RoundfToInt (r.x);
+ viewPort[1] = RoundfToInt (r.y);
+ viewPort[2] = RoundfToIntPos (r.GetRight ()) - viewPort[0];
+ viewPort[3] = RoundfToIntPos (r.GetBottom ()) - viewPort[1];
+}
+
+
+bool CameraProject( const Vector3f& p, const Matrix4x4f& cameraToWorld, const Matrix4x4f& worldToClip, const int viewport[4], Vector3f& outP )
+{
+ Vector3f clipPoint;
+ if( worldToClip.PerspectiveMultiplyPoint3( p, clipPoint ) )
+ {
+ Vector3f cameraPos = cameraToWorld.GetPosition();
+ Vector3f dir = p - cameraPos;
+ // The camera/projection matrices follow OpenGL convention: positive Z is towards the viewer.
+ // So negate it to get into Unity convention.
+ Vector3f forward = -cameraToWorld.GetAxisZ();
+ float dist = Dot( dir, forward );
+
+ #if UNITY_WP8
+ RotatePointIfNeeded(clipPoint, false);
+ #endif
+
+ outP.x = viewport[0] + (1.0f + clipPoint.x) * viewport[2] * 0.5f;
+ outP.y = viewport[1] + (1.0f + clipPoint.y) * viewport[3] * 0.5f;
+ //outP.z = (1.0f + clipPoint.z) * 0.5f;
+ outP.z = dist;
+
+ return true;
+ }
+
+ outP.Set( 0.0f, 0.0f, 0.0f );
+ return false;
+}
+
+bool CameraUnProject( const Vector3f& p, const Matrix4x4f& cameraToWorld, const Matrix4x4f& clipToWorld, const int viewport[4], Vector3f& outP )
+{
+ // pixels to -1..1
+ Vector3f in;
+ in.x = (p.x - viewport[0]) * 2.0f / viewport[2] - 1.0f;
+ in.y = (p.y - viewport[1]) * 2.0f / viewport[3] - 1.0f;
+ // It does not matter where the point we unproject lies in depth; so we choose 0.95, which
+ // is further than near plane and closer than far plane, for precision reasons.
+ // In a perspective camera setup (near=0.1, far=1000), a point at 0.95 projected depth is about
+ // 5 units from the camera.
+ in.z = 0.95f;
+
+ #if UNITY_WP8
+ RotatePointIfNeeded(in, true);
+ #endif
+
+ Vector3f pointOnPlane;
+ if( clipToWorld.PerspectiveMultiplyPoint3( in, pointOnPlane ) )
+ {
+ // Now we have a point on the plane perpendicular to the viewing direction. We need to return the one that is on the line
+ // towards this point, and at p.z distance along camera's viewing axis.
+ Vector3f cameraPos = cameraToWorld.GetPosition();
+ Vector3f dir = pointOnPlane - cameraPos;
+
+ // The camera/projection matrices follow OpenGL convention: positive Z is towards the viewer.
+ // So negate it to get into Unity convention.
+ Vector3f forward = -cameraToWorld.GetAxisZ();
+ float distToPlane = Dot( dir, forward );
+ if( Abs(distToPlane) >= 1.0e-6f )
+ {
+ bool isPerspective = (clipToWorld.m_Data[3] != 0.0f || clipToWorld.m_Data[7] != 0.0f || clipToWorld.m_Data[11] != 0.0f || clipToWorld.m_Data[15] != 1.0f);
+ if( isPerspective )
+ {
+ dir *= p.z / distToPlane;
+ outP = cameraPos + dir;
+ }
+ else
+ {
+ outP = pointOnPlane - forward * (distToPlane - p.z);
+ }
+ return true;
+ }
+ }
+
+ outP.Set( 0.0f, 0.0f, 0.0f );
+ return false;
+}
+
+void SetGLViewport (const Rectf& pixelRect)
+{
+ Rectf tempPixelRect (pixelRect);
+ int viewport[4];
+
+ GfxDevice& device = GetGfxDevice();
+
+#if UNITY_EDITOR
+ // Handle game view's aspect ratio dropdown, but only if we're not rendering into a render
+ // texture.
+ if( device.GetActiveRenderTexture() == NULL )
+ {
+ Rectf renderRect = GetRenderManager().GetWindowRect();
+ tempPixelRect.x += renderRect.x;
+ tempPixelRect.y += renderRect.y;
+ tempPixelRect.Clamp (renderRect);
+ }
+#endif
+
+ viewport[0] = RoundfToInt( tempPixelRect.x );
+ viewport[1] = RoundfToInt( tempPixelRect.y );
+ viewport[2] = RoundfToIntPos( tempPixelRect.Width() );
+ viewport[3] = RoundfToIntPos( tempPixelRect.Height() );
+ FlipScreenRectIfNeeded( device, viewport );
+ device.SetViewport( viewport[0], viewport[1], viewport[2], viewport[3] );
+
+}
+
+void ExtractProjectionPlanes( const Matrix4x4f& finalMatrix, Plane* outPlanes )
+{
+ float tmpVec[4];
+ float otherVec[4];
+
+ tmpVec[0] = finalMatrix.Get (3, 0);
+ tmpVec[1] = finalMatrix.Get (3, 1);
+ tmpVec[2] = finalMatrix.Get (3, 2);
+ tmpVec[3] = finalMatrix.Get (3, 3);
+
+ otherVec[0] = finalMatrix.Get (0, 0);
+ otherVec[1] = finalMatrix.Get (0, 1);
+ otherVec[2] = finalMatrix.Get (0, 2);
+ otherVec[3] = finalMatrix.Get (0, 3);
+
+ // left & right
+ outPlanes[kPlaneFrustumLeft].SetABCD ( otherVec[0] + tmpVec[0], otherVec[1] + tmpVec[1], otherVec[2] + tmpVec[2], otherVec[3] + tmpVec[3]);
+ outPlanes[kPlaneFrustumLeft].NormalizeUnsafe();
+ outPlanes[kPlaneFrustumRight].SetABCD (-otherVec[0] + tmpVec[0], -otherVec[1] + tmpVec[1], -otherVec[2] + tmpVec[2], -otherVec[3] + tmpVec[3]);
+ outPlanes[kPlaneFrustumRight].NormalizeUnsafe();
+
+ // bottom & top
+ otherVec[0] = finalMatrix.Get (1, 0);
+ otherVec[1] = finalMatrix.Get (1, 1);
+ otherVec[2] = finalMatrix.Get (1, 2);
+ otherVec[3] = finalMatrix.Get (1, 3);
+
+ outPlanes[kPlaneFrustumBottom].SetABCD ( otherVec[0] + tmpVec[0], otherVec[1] + tmpVec[1], otherVec[2] + tmpVec[2], otherVec[3] + tmpVec[3]);
+ outPlanes[kPlaneFrustumBottom].NormalizeUnsafe();
+ outPlanes[kPlaneFrustumTop].SetABCD (-otherVec[0] + tmpVec[0], -otherVec[1] + tmpVec[1], -otherVec[2] + tmpVec[2], -otherVec[3] + tmpVec[3]);
+ outPlanes[kPlaneFrustumTop].NormalizeUnsafe();
+
+ otherVec[0] = finalMatrix.Get (2, 0);
+ otherVec[1] = finalMatrix.Get (2, 1);
+ otherVec[2] = finalMatrix.Get (2, 2);
+ otherVec[3] = finalMatrix.Get (2, 3);
+
+ // near & far
+ outPlanes[kPlaneFrustumNear].SetABCD ( otherVec[0] + tmpVec[0], otherVec[1] + tmpVec[1], otherVec[2] + tmpVec[2], otherVec[3] + tmpVec[3]);
+ outPlanes[kPlaneFrustumNear].NormalizeUnsafe();
+ outPlanes[kPlaneFrustumFar].SetABCD (-otherVec[0] + tmpVec[0], -otherVec[1] + tmpVec[1], -otherVec[2] + tmpVec[2], -otherVec[3] + tmpVec[3]);
+ outPlanes[kPlaneFrustumFar].NormalizeUnsafe();
+}
+
+void ExtractProjectionNearPlane( const Matrix4x4f& finalMatrix, Plane* outPlane )
+{
+ float tmpVec[4];
+ float otherVec[4];
+
+ tmpVec[0] = finalMatrix.Get (3, 0);
+ tmpVec[1] = finalMatrix.Get (3, 1);
+ tmpVec[2] = finalMatrix.Get (3, 2);
+ tmpVec[3] = finalMatrix.Get (3, 3);
+
+ otherVec[0] = finalMatrix.Get (2, 0);
+ otherVec[1] = finalMatrix.Get (2, 1);
+ otherVec[2] = finalMatrix.Get (2, 2);
+ otherVec[3] = finalMatrix.Get (2, 3);
+
+ // near
+ outPlane->SetABCD ( otherVec[0] + tmpVec[0], otherVec[1] + tmpVec[1], otherVec[2] + tmpVec[2], otherVec[3] + tmpVec[3]);
+ outPlane->NormalizeUnsafe();
+}
+
+
+void SetClippingPlaneShaderProps()
+{
+ GfxDevice& device = GetGfxDevice();
+ BuiltinShaderParamValues& params = device.GetBuiltinParamValues();
+
+ const Matrix4x4f* viewMatrix = (const Matrix4x4f*)device.GetViewMatrix();
+ const Matrix4x4f* deviceProjMatrix = (const Matrix4x4f*)device.GetDeviceProjectionMatrix();
+ Matrix4x4f viewProj;
+ MultiplyMatrices4x4 (deviceProjMatrix, viewMatrix, &viewProj);
+ Plane planes[6];
+ ExtractProjectionPlanes (viewProj, planes);
+ params.SetVectorParam (kShaderVecCameraWorldClipPlanes0, (const Vector4f&)planes[0]);
+ params.SetVectorParam (kShaderVecCameraWorldClipPlanes1, (const Vector4f&)planes[1]);
+ params.SetVectorParam (kShaderVecCameraWorldClipPlanes2, (const Vector4f&)planes[2]);
+ params.SetVectorParam (kShaderVecCameraWorldClipPlanes3, (const Vector4f&)planes[3]);
+ params.SetVectorParam (kShaderVecCameraWorldClipPlanes4, (const Vector4f&)planes[4]);
+ params.SetVectorParam (kShaderVecCameraWorldClipPlanes5, (const Vector4f&)planes[5]);
+}
+
+
+DeviceMVPMatricesState::DeviceMVPMatricesState()
+{
+ GfxDevice& device = GetGfxDevice();
+ CopyMatrix(device.GetViewMatrix(), m_View.GetPtr());
+ CopyMatrix(device.GetWorldMatrix(), m_World.GetPtr());
+ CopyMatrix(device.GetProjectionMatrix(), m_Proj.GetPtr());
+}
+
+DeviceMVPMatricesState::~DeviceMVPMatricesState()
+{
+ GfxDevice& device = GetGfxDevice();
+ device.SetViewMatrix(m_View.GetPtr());
+ device.SetWorldMatrix(m_World.GetPtr());
+ device.SetProjectionMatrix(m_Proj);
+ SetClippingPlaneShaderProps();
+}
+
+DeviceViewProjMatricesState::DeviceViewProjMatricesState()
+{
+ GfxDevice& device = GetGfxDevice();
+ CopyMatrix(device.GetViewMatrix(), m_View.GetPtr());
+ CopyMatrix(device.GetProjectionMatrix(), m_Proj.GetPtr());
+}
+
+DeviceViewProjMatricesState::~DeviceViewProjMatricesState()
+{
+ GfxDevice& device = GetGfxDevice();
+ device.SetViewMatrix(m_View.GetPtr());
+ device.SetProjectionMatrix(m_Proj);
+ SetClippingPlaneShaderProps();
+}
diff --git a/Runtime/Camera/CameraUtil.h b/Runtime/Camera/CameraUtil.h
new file mode 100644
index 0000000..ee747c2
--- /dev/null
+++ b/Runtime/Camera/CameraUtil.h
@@ -0,0 +1,61 @@
+#ifndef CAMERA_UTIL_H
+#define CAMERA_UTIL_H
+
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Modules/ExportModules.h"
+
+class GfxDevice;
+class Vector3f;
+class Plane;
+
+void LoadPixelMatrix( const Rectf& screenRect, GfxDevice& device, bool setMatrix, bool invertYTexelOffset );
+void CalcPixelMatrix (const Rectf& screenRect, Matrix4x4f &out);
+void ApplyTexelOffsetsToPixelMatrix( bool invertYTexelOffset, Matrix4x4f& matrix );
+void LoadFullScreenOrthoMatrix( float nearPlane = -1.0f, float farPlane = 100.0f, bool forceNoHalfTexelOffset = false );
+void GetHalfTexelOffsets( float& outx, float& outy );
+void SetupPixelCorrectCoordinates();
+
+void RectfToViewport( const Rectf& r, int viewPort[4] );
+void FlipScreenRectIfNeeded( const GfxDevice& device, int screenviewcoord[4] );
+void SetGLViewport (const Rectf& pixelRect);
+
+// World point to screen point
+// p = world point
+// outP = result (x, y = in pixels inside the viewport, z = world space distance from the camera)
+//
+// sets outP to (0,0,0) if fails.
+bool CameraProject( const Vector3f& p, const Matrix4x4f& cameraToWorld, const Matrix4x4f& worldToClip, const int viewport[4], Vector3f& outP );
+
+// Screen point to world point
+// p = screen point (x, y = in pixels inside the viewport, z = world space distance from the camera)
+//
+// sets outP to (0,0,0) if fails.
+bool CameraUnProject( const Vector3f& p, const Matrix4x4f& cameraToWorld, const Matrix4x4f& clipToWorld, const int viewport[4], Vector3f& outP );
+
+// Extract frustum planes from Projection matrix
+void EXPORT_COREMODULE ExtractProjectionPlanes (const Matrix4x4f& projection, Plane* planes);
+void EXPORT_COREMODULE ExtractProjectionNearPlane (const Matrix4x4f& projection, Plane* outPlane);
+
+void SetClippingPlaneShaderProps();
+
+
+class DeviceMVPMatricesState {
+public:
+ DeviceMVPMatricesState ();
+ ~DeviceMVPMatricesState();
+ const Matrix4x4f& GetView() const { return m_View; }
+ const Matrix4x4f& GetProj() const { return m_Proj; }
+private:
+ Matrix4x4f m_World, m_View, m_Proj;
+};
+
+class DeviceViewProjMatricesState {
+public:
+ DeviceViewProjMatricesState ();
+ ~DeviceViewProjMatricesState();
+private:
+ Matrix4x4f m_View, m_Proj;
+};
+
+#endif
diff --git a/Runtime/Camera/CullResults.cpp b/Runtime/Camera/CullResults.cpp
new file mode 100644
index 0000000..7b345b1
--- /dev/null
+++ b/Runtime/Camera/CullResults.cpp
@@ -0,0 +1,81 @@
+#include "UnityPrefix.h"
+#include "CullResults.h"
+#include "ShadowCulling.h"//@TODO: Remove
+#include "UmbraBackwardsCompatibility.h"
+
+CullResults::CullResults() : shadowCullData(NULL), treeSceneNodes(kMemTempAlloc), treeBoundingBoxes(kMemTempAlloc)
+{
+}
+
+CullResults::~CullResults()
+{
+ Umbra::Visibility* umbra = sceneCullingOutput.umbraVisibility;
+ if (umbra != NULL)
+ {
+ int* clusterBuffer = (int*)umbra->getOutputClusters()->getPtr();
+ UNITY_FREE(kMemTempAlloc, clusterBuffer);
+
+ Umbra::IndexList* outputObjects = umbra->getOutputObjects();
+ Umbra::OcclusionBuffer* outputBuffer = umbra->getOutputBuffer();
+ Umbra::IndexList* outputCluster = umbra->getOutputClusters();
+
+ UNITY_DELETE(outputObjects, kMemTempAlloc);
+ UNITY_DELETE(outputBuffer, kMemTempAlloc);
+ UNITY_DELETE(outputCluster, kMemTempAlloc);
+ UNITY_DELETE(umbra, kMemTempAlloc);
+ }
+
+ DestroyCullingOutput(sceneCullingOutput);
+
+ UNITY_DELETE(shadowCullData, kMemTempAlloc);
+}
+
+void InitIndexList (IndexList& list, size_t count)
+{
+ int* array = (int*)UNITY_MALLOC (kMemTempAlloc, count * sizeof(int));
+ list = IndexList (array, 0, count);
+}
+
+void DestroyIndexList (IndexList& list)
+{
+ UNITY_FREE(kMemTempAlloc, list.indices);
+ list.indices = NULL;
+}
+
+void CreateCullingOutput (const RendererCullData* rendererCullData, CullingOutput& cullingOutput)
+{
+ for(int i=0;i<kVisibleListCount;i++)
+ InitIndexList(cullingOutput.visible[i], rendererCullData[i].rendererCount);
+}
+
+void DestroyCullingOutput (CullingOutput& cullingOutput)
+{
+ for (int i=0;i<kVisibleListCount;i++)
+ DestroyIndexList (cullingOutput.visible[i]);
+}
+
+void CullResults::Init (const UmbraTomeData& tomeData, const RendererCullData* rendererCullData)
+{
+ CreateCullingOutput(rendererCullData, sceneCullingOutput);
+
+ if (tomeData.tome != NULL)
+ {
+ size_t staticCount = rendererCullData[kStaticRenderers].rendererCount;
+ Assert(staticCount == 0 || staticCount == UMBRA_TOME_METHOD(tomeData, getObjectCount()));
+ size_t clusterCount = UMBRA_TOME_METHOD(tomeData, getClusterCount());
+
+ int* clusterArray = (int*)UNITY_MALLOC(kMemTempAlloc, clusterCount * sizeof(int));
+
+ Umbra::IndexList* staticList = UNITY_NEW(Umbra::IndexList (sceneCullingOutput.visible[kStaticRenderers].indices, staticCount), kMemTempAlloc);
+ Umbra::OcclusionBuffer* occlusionBuffer = UNITY_NEW(Umbra::OcclusionBuffer, kMemTempAlloc);
+ Umbra::Visibility* umbraVisibility = UNITY_NEW(Umbra::Visibility(staticList, occlusionBuffer), kMemTempAlloc);
+ Umbra::IndexList* clusterList = UNITY_NEW(Umbra::IndexList (clusterArray, clusterCount), kMemTempAlloc);
+ umbraVisibility->setOutputClusters(clusterList);
+
+ sceneCullingOutput.umbraVisibility = umbraVisibility;
+ }
+ else
+ {
+ sceneCullingOutput.umbraVisibility = NULL;
+ }
+}
diff --git a/Runtime/Camera/CullResults.h b/Runtime/Camera/CullResults.h
new file mode 100644
index 0000000..4905de6
--- /dev/null
+++ b/Runtime/Camera/CullResults.h
@@ -0,0 +1,153 @@
+#pragma once
+
+#include "BaseRenderer.h"
+#include "Lighting.h"
+#include "ShaderReplaceData.h"
+#include "CullingParameters.h"
+#include "Runtime/Math/Rect.h"
+#include "UmbraTomeData.h"
+#include "SceneNode.h"
+
+class Light;
+struct ShadowCullData;
+struct ShadowedLight;
+namespace Umbra { class Visibility; class Tome; }
+
+
+struct VisibleNode : public TransformInfo
+{
+ void SetTransformInfo(const TransformInfo& src) { TransformInfo::operator=(src); }
+ BaseRenderer* renderer;
+ float lodFade;
+};
+
+struct ActiveLight
+{
+ Light* light;
+ ShadowedLight* shadowedLight;
+
+#if ENABLE_SHADOWS
+ bool insideShadowRange;
+#endif
+
+ // For vertex lights we have to keep all lights around when doing per-object culling.
+ // This is because vertex lights can still affect objects (vertex interpolation on large triangles) although the light itself might be completely invisible.
+ bool isVisibleInPrepass;
+ int lightmappingForRender;
+ UInt32 cullingMask;
+ bool intersectsNear;
+ bool intersectsFar;
+ AABB boundingBox;
+ Rectf screenRect;
+ bool hasCookie;
+ int lightRenderMode;
+ int lightType;
+
+ // Some lights are offscreen
+ bool isOffscreenVertexLight;
+ float visibilityFade;
+};
+
+template <typename T>
+struct CullingDynamicArray : public dynamic_array<T, AlignOfType<T>::align, kMemTempAllocId> {};
+
+struct ActiveLights
+{
+ typedef CullingDynamicArray<ActiveLight> Array;
+ Array lights;
+
+ // If there is a main directional light, it will be the first one in lights.
+ bool hasMainLight;
+
+ // Lights are sorted by type in the following order
+ size_t numDirLights;
+ size_t numSpotLights;
+ size_t numPointLights;
+ size_t numOffScreenSpotLights;
+ size_t numOffScreenPointLights;
+};
+
+struct ShadowedLight
+{
+ // Index into CullResults.activeLights array
+ int lightIndex;
+};
+
+typedef CullingDynamicArray<VisibleNode> VisibleNodes;
+typedef CullingDynamicArray<UInt32> ObjectLightIndices;
+typedef CullingDynamicArray<UInt32> ObjectLightOffsets;
+typedef dynamic_array<ShadowedLight> ShadowedLights;
+
+/// CullResults lives during the entire duration of rendering one frame.
+/// Shadow culling uses data from the frustum/occlusion cull pass
+/// The render loop uses CullResults to render the scene.
+struct CullResults
+{
+ CullResults();
+ ~CullResults();
+
+ void Init (const UmbraTomeData& tome, const RendererCullData* rendererCullData);
+
+ CullingOutput sceneCullingOutput;
+ VisibleNodes nodes;
+
+ // All lights that might affect any visible objects
+ ActiveLights activeLights;
+
+ // Forward rendering needs to know on a per renderer basis which lights affect it.
+ ObjectLightIndices forwardLightIndices; // index array for all objects
+ ObjectLightOffsets forwardLightOffsets; // offset for each object (in the forwardLightIndices array)
+
+ // All lights that cast shadows on any objects in the scene
+ ShadowedLights shadowedLights;
+
+ SceneCullingParameters sceneCullParameters;
+
+ CullingDynamicArray<UInt8> lodMasks;
+ CullingDynamicArray<float> lodFades;
+
+ dynamic_array<SceneNode> treeSceneNodes;
+ dynamic_array<AABB> treeBoundingBoxes;
+
+ ///@TODO: Whats up with this thing? Does seem related...
+ ShadowCullData* shadowCullData;
+
+ ShaderReplaceData shaderReplaceData;
+};
+
+void InitIndexList (IndexList& list, size_t count);
+void DestroyIndexList (IndexList& list);
+
+void CreateCullingOutput (const RendererCullData* rendererCullData, CullingOutput& cullingOutput);
+void DestroyCullingOutput (CullingOutput& cullingOutput);
+
+inline const ActiveLight* GetMainActiveLight(const ActiveLights& activeLights)
+{
+ return activeLights.hasMainLight ? &activeLights.lights[0] : NULL;
+}
+
+inline const UInt32* GetObjectLightIndices(const CullResults& cullResults, UInt32 roIndex)
+{
+ if (cullResults.forwardLightOffsets.size() == 0)
+ return 0;
+
+ DebugAssert(roIndex < (cullResults.forwardLightOffsets.size()-1));
+ // Get beginning of light indices for object
+ UInt32 lightOffset = cullResults.forwardLightOffsets[roIndex];
+ return cullResults.forwardLightIndices.data() + lightOffset;
+}
+
+inline UInt32 GetObjectLightCount(const CullResults& cullResults, UInt32 visibleNodeIndex)
+{
+ if (cullResults.forwardLightOffsets.size() == 0)
+ return 0;
+
+ DebugAssert(visibleNodeIndex < (cullResults.forwardLightOffsets.size()-1));
+ // We store an extra offset at the end of forwardLightOffsets
+ // This means it's always safe to check next offset to get the size
+ UInt32 lightOffset = cullResults.forwardLightOffsets[visibleNodeIndex];
+ UInt32 nextLightOffset = cullResults.forwardLightOffsets[visibleNodeIndex + 1];
+ DebugAssert(nextLightOffset >= lightOffset);
+ DebugAssert(nextLightOffset <= cullResults.forwardLightIndices.size());
+ return nextLightOffset - lightOffset;
+}
diff --git a/Runtime/Camera/Culler.cpp b/Runtime/Camera/Culler.cpp
new file mode 100644
index 0000000..5fc5305
--- /dev/null
+++ b/Runtime/Camera/Culler.cpp
@@ -0,0 +1,261 @@
+#include "UnityPrefix.h"
+#include "Culler.h"
+#include "SceneCulling.h"
+#include "UnityScene.h"
+#include "CullResults.h"
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "BaseRenderer.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Camera/IntermediateRenderer.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Graphics/Transform.h"
+#include "LightCulling.h"
+#include "External/shaderlab/Library/intshader.h"
+
+PROFILER_INFORMATION(gCulling, "Culling", kProfilerRender);
+PROFILER_INFORMATION(gCullActiveLights, "CullAllVisibleLights", kProfilerRender);
+PROFILER_INFORMATION(gCullPerObjectLights, "CullPerObjectLights", kProfilerRender);
+PROFILER_INFORMATION(gCullScene, "CullSceneObjects", kProfilerRender);
+PROFILER_INFORMATION(gCacheTransformInfo, "CacheTransformInfo.", kProfilerRender);
+
+
+#if UNITY_EDITOR
+
+inline bool IsRendererFiltered (BaseRenderer* r, CullFiltering filtering)
+{
+ if (filtering == kFilterModeOff)
+ return true;
+
+ if (r->GetRendererType() == kRendererIntermediate)
+ return true;
+
+ Renderer* casted = static_cast<Renderer*> (r);
+ return IsGameObjectFiltered (casted->GetGameObject(), filtering);
+}
+
+bool IsGameObjectFiltered (Unity::GameObject& go, CullFiltering filtering)
+{
+ if (filtering == kFilterModeOff)
+ return true;
+
+ if (go.IsMarkedVisible())
+ return filtering == kFilterModeShowFiltered;
+ else
+ {
+ Transform& trs = go.GetComponent (Transform);
+ if (trs.GetParent())
+ {
+ bool isFiltered = IsGameObjectFiltered(trs.GetParent()->GetGameObject(), filtering);
+ go.SetMarkedVisible((isFiltered == (filtering == kFilterModeShowFiltered))?GameObject::kVisibleAsChild:GameObject::kNotVisible);
+ return isFiltered;
+ }
+ else
+ return filtering == kFilterModeShowRest;
+ }
+}
+#endif
+
+void InvokeOnWillRenderObject (const dynamic_array<BaseRenderer*>& renderers)
+{
+ // Invoke OnWillRenderObject callbacks.
+ // These can only ever happen on non intermediate Renderers.
+ // Scene needs to know we are calling scripts (case 445226).
+ GetScene().SetPreventAddRemoveRenderer(true);
+ for( int i = 0; i < renderers.size(); ++i )
+ {
+ Assert (renderers[i]->GetRendererType() != kRendererIntermediate);
+ Renderer* r = static_cast<Renderer*>(renderers[i]);
+ r->SendMessage (kOnWillRenderObject);
+ }
+ GetScene().SetPreventAddRemoveRenderer(false);
+}
+
+
+static void PrepareSceneNodes (const IndexList& visible, CullFiltering filtering, const SceneNode* nodes, VisibleNodes& output, dynamic_array<BaseRenderer*>& needsCullCallback)
+{
+ // Generate Visible nodes from all static & dynamic objects
+ for (int i=0;i<visible.size;i++)
+ {
+ const SceneNode& node = nodes[visible.indices[i]];
+ if (node.needsCullCallback)
+ needsCullCallback.push_back(node.renderer);
+
+ #if UNITY_EDITOR
+ if (!IsRendererFiltered (node.renderer, filtering))
+ continue;
+ #endif
+
+ VisibleNode& visibleNode = output.push_back ();
+ visibleNode.renderer = node.renderer;
+ visibleNode.lodFade = 0.0F;
+ }
+}
+
+static void CacheTransformInfo (VisibleNodes& results)
+{
+ PROFILER_AUTO(gCacheTransformInfo, NULL)
+
+ for( int i = 0; i < results.size(); ++i )
+ {
+ VisibleNode& node = results[i];
+ node.SetTransformInfo (node.renderer->GetTransformInfo());
+ }
+}
+
+
+///////////@TODO: Move this shit to the right places....
+#include "Runtime/Graphics/LightmapSettings.h"
+#include "Runtime/Camera/LightManager.h"
+#include "Runtime/Camera/Light.h"
+#include "Runtime/Misc/QualitySettings.h"
+#include "Runtime/Camera/ShadowCulling.h"
+
+void CullShadowCasters (const Light& light, const ShadowCullData& cullData, bool excludeLightmapped, CullingOutput& cullingOutput );
+
+static void CullAllPerObjectLights (const VisibleNodes& visibleNodes, const ActiveLights& lights, const int renderPath, ObjectLightIndices& forwardLightIndices, ObjectLightIndices& forwardLightOffsets)
+{
+ PROFILER_AUTO(gCullPerObjectLights, NULL);
+
+ forwardLightIndices.reserve(visibleNodes.size() * 3);
+ forwardLightOffsets.resize_uninitialized(visibleNodes.size() + 1);
+
+ const bool dualLightmapsMode = (GetLightmapSettings().GetLightmapsMode() == LightmapSettings::kDualLightmapsMode);
+ const bool areLightProbesBaked = LightProbes::AreBaked();
+
+ for( int i = 0; i < visibleNodes.size(); ++i )
+ {
+ const VisibleNode& node = visibleNodes[i];
+ ///@TODO: Should use SceneNode for this???
+ Renderer* renderer = static_cast<Renderer*>(node.renderer);
+ UInt32 layerMask = renderer->GetLayerMask();
+ const bool isLightmapped = renderer->IsLightmappedForRendering();
+ const bool isUsingLightProbes = renderer->GetRendererType() != kRendererIntermediate && renderer->GetUseLightProbes() && areLightProbesBaked;
+ const bool directLightFromLightProbes = (renderPath == kRenderPathExtVertex) ? false : isUsingLightProbes && !dualLightmapsMode;
+
+ forwardLightOffsets[i] = forwardLightIndices.size();
+ CullPerObjectLights (lights, node.worldAABB, node.localAABB, node.worldMatrix, node.invScale, layerMask, isLightmapped || directLightFromLightProbes, dualLightmapsMode, forwardLightIndices);
+ }
+
+ forwardLightOffsets[visibleNodes.size()] = forwardLightIndices.size();
+}
+
+static void FindShadowCastingLights (ActiveLights& activeLights, ShadowedLights& shadowedLights)
+{
+ size_t index = 0;
+ size_t endIndex = activeLights.numDirLights + activeLights.numSpotLights + activeLights.numPointLights;
+
+ // Must reserve here otherwise the shadowlight pointer in activelight might become invalid...
+ shadowedLights.reserve(endIndex);
+
+ for ( ; index < endIndex; index++)
+ {
+ ActiveLight& light = activeLights.lights[index];
+ if (light.isVisibleInPrepass && light.insideShadowRange && light.light->GetShadows() != kShadowNone)
+ {
+ ShadowedLight& shadowedLight = shadowedLights.push_back();
+ shadowedLight.lightIndex = index;
+ light.shadowedLight = &shadowedLight;
+ }
+ else
+ light.shadowedLight = NULL;
+
+ }
+}
+
+static void CullLights (const SceneCullingParameters& cullingParameters, CullResults& results)
+{
+ PROFILER_BEGIN(gCullActiveLights, NULL)
+ FindAndCullActiveLights(cullingParameters, *results.shadowCullData, results.activeLights);
+ PROFILER_END
+
+ ///@TODO: This is not very awesome, we now cache transform info twice...
+ // Figure out how to not do that...
+ CacheTransformInfo (results.nodes);
+
+ if (cullingParameters.cullPerObjectLights)
+ CullAllPerObjectLights (results.nodes, results.activeLights, cullingParameters.renderPath, results.forwardLightIndices, results.forwardLightOffsets);
+
+ // Shadows might be disabled in quality setting
+ if (GetQualitySettings().GetCurrent().shadows == QualitySettings::kShadowsDisable)
+ return;
+
+ FindShadowCastingLights (results.activeLights, results.shadowedLights);
+}
+
+
+static void CullSendEvents (CullResults& results, dynamic_array<BaseRenderer*>& needsCullCallback)
+{
+ // Send OnBecameVisible / OnBecameInvisible callback
+ GetScene().NotifyVisible (results.sceneCullingOutput);
+
+ // Invoke renderer callbacks
+ // Invoke renderer callbacks (Only scene visible objects)
+ InvokeOnWillRenderObject(needsCullCallback);
+
+ // Cache the transform info.
+ // We do this after OnWillRenderObject since OnWillRenderObject might modify the transform positions.
+ // For example the pre-mecanim animation system will sample animations in OnWillRenderObject when using animation culling.
+ // Which can result in a change to transform. pos / rot / scale
+ CacheTransformInfo(results.nodes);
+}
+
+
+void CullScene (SceneCullingParameters& cullingParameters, CullResults& results)
+{
+ PROFILER_AUTO(gCulling, NULL);
+
+ CullingOutput& cullingOutput = results.sceneCullingOutput;
+
+ if (cullingParameters.useOcclusionCulling)
+ {
+ PROFILER_AUTO(gCullScene, NULL);
+ // Cull the scene, outputs index lists of visible renderers
+ CullSceneWithUmbra( cullingParameters, cullingOutput );
+ }
+ else
+ {
+ PROFILER_AUTO(gCullScene, NULL);
+ // Cull the scene, outputs index lists of visible renderers
+ CullSceneWithoutUmbra( cullingParameters, cullingOutput );
+ }
+
+ dynamic_array<BaseRenderer*> needsCullCallback (kMemTempAlloc);
+
+
+ // Extract visible nodes & the objects needing culling callbacks from the visible indices
+ CullFiltering cullFiltering = cullingParameters.filterMode;
+ for (int i=0;i<kVisibleListCount;i++)
+ PrepareSceneNodes(cullingOutput.visible[i], cullFiltering, cullingParameters.renderers[i].nodes, results.nodes, needsCullCallback);
+
+ if (cullingParameters.cullLights)
+ CullLights(cullingParameters, results);
+
+ CullSendEvents(results, needsCullCallback);
+}
+
+void CullIntermediateRenderersOnly (const SceneCullingParameters& cullingParameters, CullResults& results)
+{
+ Assert(cullingParameters.renderers[kDynamicRenderer].nodes == NULL);
+
+ const RendererCullData& cullData = cullingParameters.renderers[kCameraIntermediate];
+ for( size_t i = 0; i < cullData.rendererCount; ++i )
+ {
+ BaseRenderer* r = cullData.nodes[i].renderer;
+ Assert (r);
+
+ VisibleNode& visibleNode = results.nodes.push_back ();
+ visibleNode.renderer = r;
+ visibleNode.lodFade = 0.0F;
+
+ visibleNode.SetTransformInfo (r->GetTransformInfo());
+ }
+
+ if (cullingParameters.cullLights)
+ CullLights(cullingParameters, results);
+}
+
+///* Fix Lightmanager::FindActiveLights to not be ugly
+///* Move actual culling functions out of Lightmanager into their own LightCulling.cpp
+///* Make Terrains and terrain shadow culling work... \ No newline at end of file
diff --git a/Runtime/Camera/Culler.h b/Runtime/Camera/Culler.h
new file mode 100644
index 0000000..19eafbb
--- /dev/null
+++ b/Runtime/Camera/Culler.h
@@ -0,0 +1,16 @@
+#ifndef CULLER_H
+#define CULLER_H
+
+#include "CullingParameters.h"
+
+struct CullResults;
+struct SceneCullingParameters;
+class IntermediateRenderers;
+namespace Unity { class GameObject; }
+
+void CullScene (SceneCullingParameters& cullingParameters, CullResults& cullResults);
+void CullIntermediateRenderersOnly (const SceneCullingParameters& cullingParameters, CullResults& results);
+
+bool IsGameObjectFiltered (Unity::GameObject& go, CullFiltering cullFilterMode);
+
+#endif
diff --git a/Runtime/Camera/CullingParameters.h b/Runtime/Camera/CullingParameters.h
new file mode 100644
index 0000000..df4f43e
--- /dev/null
+++ b/Runtime/Camera/CullingParameters.h
@@ -0,0 +1,140 @@
+#ifndef CULLING_PARAMETERS_H
+#define CULLING_PARAMETERS_H
+
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/BaseClasses/Tags.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Math/Simd/math.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "UmbraTomeData.h"
+
+struct SceneNode;
+class AABB;
+
+namespace Umbra { class DebugRenderer; class Visibility; class Tome; class QueryExt; }
+
+enum CullingType { kNoOcclusionCulling = 0, kOcclusionCulling = 1, kShadowCasterOcclusionCulling = 2 };
+enum CullFiltering { kFilterModeOff = 0, kFilterModeShowFiltered = 1, kFilterModeShowRest = 2 };
+
+struct IndexList
+{
+ int* indices;
+ int size;
+ int reservedSize;
+
+ int& operator [] (int index)
+ {
+ DebugAssert(index < reservedSize);
+ return indices[index];
+ }
+
+ int operator [] (int index) const
+ {
+ DebugAssert(index < reservedSize);
+ return indices[index];
+ }
+
+ IndexList ()
+ {
+ indices = NULL;
+ reservedSize = size = 0;
+ }
+
+ IndexList (int* i, int sz, int rs)
+ {
+ indices = i;
+ size = sz;
+ reservedSize = rs;
+ }
+
+};
+
+enum
+{
+ kStaticRenderers = 0,
+ kDynamicRenderer,
+ kSceneIntermediate,
+ kCameraIntermediate,
+#if ENABLE_TERRAIN
+ kTreeRenderer,
+#endif
+ kVisibleListCount
+};
+
+// Output for all culling operations.
+// simple index list indexing into the different places where renderers can be found.
+struct CullingOutput
+{
+ IndexList visible[kVisibleListCount];
+
+ Umbra::Visibility* umbraVisibility;
+
+ CullingOutput () : umbraVisibility (NULL) { }
+};
+
+struct RendererCullData
+{
+ const AABB* bounds;
+ const SceneNode* nodes;
+ size_t rendererCount;
+
+ RendererCullData () { bounds = NULL; nodes = NULL; rendererCount = 0; }
+};
+
+struct CullingParameters
+{
+ enum { kMaxPlanes = 10 };
+ enum LayerCull
+ {
+ kLayerCullNone,
+ kLayerCullPlanar,
+ kLayerCullSpherical
+ };
+
+ Vector3f lodPosition;
+ float lodFieldOfView;
+ float orthoSize;
+ int cameraPixelHeight;
+ Plane cullingPlanes[kMaxPlanes];
+ int cullingPlaneCount;
+ UInt32 cullingMask;
+ float layerFarCullDistances[kNumLayers];
+ LayerCull layerCull;
+ bool isOrthographic;
+ Vector3f lightDir;
+
+ // Used for Umbra
+ Matrix4x4f worldToClipMatrix;
+ Vector3f position;
+};
+
+struct SceneCullingParameters : CullingParameters
+{
+ RendererCullData renderers[kVisibleListCount];
+
+ UInt8* lodMasks;
+ size_t lodGroupCount;
+
+ bool useOcclusionCulling;
+ bool useShadowCasterCulling;
+ bool useLightOcclusionCulling;
+ bool excludeLightmappedShadowCasters;
+ bool cullPerObjectLights;
+ bool cullLights;
+ int renderPath;
+ CullFiltering filterMode;
+
+ /// This stores the visibility of previous culling operations.
+ /// For example, shadow caster culling uses the visibility of the visible objects from the camera.
+ const CullingOutput* sceneVisbilityForShadowCulling;
+
+ UmbraTomeData umbraTome;
+ Umbra::DebugRenderer* umbraDebugRenderer;
+ Umbra::QueryExt* umbraQuery;
+ UInt32 umbraDebugFlags;
+};
+
+
+
+#endif
diff --git a/Runtime/Camera/Flare.cpp b/Runtime/Camera/Flare.cpp
new file mode 100644
index 0000000..1053f05
--- /dev/null
+++ b/Runtime/Camera/Flare.cpp
@@ -0,0 +1,611 @@
+#include "UnityPrefix.h"
+#include "Flare.h"
+#include "Runtime/Interfaces/IRaycast.h"
+#include "Camera.h"
+#include "RenderSettings.h"
+#include "RenderManager.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Graphics/Transform.h"
+#include "External/shaderlab/Library/properties.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/BaseClasses/Tags.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/Profiler/Profiler.h"
+
+static Material *s_FlareMaterial;
+
+static SHADERPROP (FlareTexture);
+
+
+static void CalculateFlareGetUVCoords (int layout, int imageIndex, const Vector2f& pixOffset, Vector2f& outUV0, Vector2f& outUV1 );
+
+
+Flare::Flare (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+
+}
+
+Flare::~Flare ()
+{
+}
+
+void Flare::Reset ()
+{
+ Super::Reset();
+ resize_trimmed (m_Elements, 1);
+ m_Elements[0].m_ImageIndex = 0;
+ m_Elements[0].m_Rotate = false;
+ m_Elements[0].m_Position = 0.0f;
+ m_Elements[0].m_Size = 0.5f;
+ m_Elements[0].m_Color = ColorRGBAf (1,1,1,0);
+ m_Elements[0].m_Zoom = true;
+ m_Elements[0].m_Fade = true;
+ m_Elements[0].m_UseLightColor = true;
+ m_UseFog = true;
+ m_TextureLayout = 0;
+}
+
+template<class TransferFunc>
+void Flare::Transfer (TransferFunc& transfer) {
+ Super::Transfer (transfer);
+ TRANSFER_SIMPLE (m_FlareTexture);
+ TRANSFER_SIMPLE (m_TextureLayout);
+ TRANSFER_SIMPLE (m_Elements);
+ TRANSFER (m_UseFog);
+}
+
+void Flare::AwakeFromLoad (AwakeFromLoadMode awakeMode) {
+ Super::AwakeFromLoad(awakeMode);
+
+ // mark pixel offset as "need to figure out"
+ m_PixOffset.Set( -1.0f, -1.0f );
+}
+
+struct FlareVertex {
+ Vector3f vert;
+ ColorRGBA32 color;
+ Vector2f uv;
+};
+PROFILER_INFORMATION(gSubmitVBOProfileFlare, "Mesh.SubmitVBO", kProfilerRender)
+
+void Flare::Render (Vector3f &pos, float visibility, const ColorRGBAf &tintColor, const ChannelAssigns& channels)
+{
+ // Figure out pixel offset if we have to.
+ // Have to do this just before rendering, because at load time texel sizes might not
+ // be set up for some textures yet (render textures in particular).
+ if( m_PixOffset.x == -1.0f )
+ {
+ Texture *tex = m_FlareTexture;
+ // Get the UV offsets to address the texture on texel boundaries
+ // Use this to work around OpenGL's feature that UVs are in the middle of a texel.
+ if( tex )
+ {
+ m_PixOffset.x = tex->GetTexelSizeX() * 0.5f;
+ m_PixOffset.y = tex->GetTexelSizeY() * 0.5f;
+ }
+ else
+ {
+ m_PixOffset.Set(0,0);
+ }
+ }
+
+ Vector2f p (pos.x, pos.y);
+ if (SqrMagnitude (p) > Vector2f::epsilon)
+ p = Normalize (p);
+ else
+ p = Vector2f (1,0);
+
+ if (m_UseFog)
+ visibility *= 1.0F-GetRenderSettings().CalcFogFactor (pos.z);
+
+ // Get VBO chunk
+ const int elemCount = m_Elements.size();
+ GfxDevice& device = GetGfxDevice();
+ DynamicVBO& vbo = device.GetDynamicVBO();
+ FlareVertex* vbPtr;
+ if( !vbo.GetChunk( (1<<kShaderChannelVertex) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelColor),
+ elemCount * 4, 0,
+ DynamicVBO::kDrawQuads,
+ (void**)&vbPtr, NULL ) )
+ {
+ return;
+ }
+
+ std::vector<FlareElement>::const_iterator it, itEnd = m_Elements.end();
+ for (it = m_Elements.begin(); it != itEnd; ++it)
+ {
+ Vector2f uv0, uv1;
+ CalculateFlareGetUVCoords (m_TextureLayout, it->m_ImageIndex, m_PixOffset, uv0, uv1);
+
+ // Had to invert uv's when we flipped all our textures to conform to opengl
+ // the actual code using the uv's should flip uv's instead of this fix.
+ uv0.y = 1.0F - uv0.y;
+ uv1.y = 1.0F - uv1.y;
+
+ float s = it->m_Size * pos.z * (it->m_Zoom ? visibility * .01f : .01f);
+ Vector2f size;
+ if (it->m_Rotate) {
+ size = p * (s * 1.4f);
+ } else {
+ size.x = size.y = s;
+ }
+ Vector3f v = Lerp (pos, Vector3f (0,0,pos.z), it->m_Position);
+ ColorRGBA32 color;
+
+ if (!it->m_UseLightColor) {
+ color = (it->m_Color * visibility);
+ } else {
+ ColorRGBAf col = it->m_Color;
+ col.r *= tintColor.r;
+ col.g *= tintColor.g;
+ col.b *= tintColor.b;
+ col.a *= tintColor.a;
+ if( it->m_Fade )
+ col = col * (visibility);
+ color = col;
+ }
+ // Swizzle color of the renderer requires it
+ color = device.ConvertToDeviceVertexColor(color);
+
+ // vertices
+ vbPtr[0].vert.Set( v.x - size.x, v.y - size.y, v.z );
+ vbPtr[0].color = color;
+ vbPtr[0].uv.Set( uv1.x, uv0.y );
+
+ vbPtr[1].vert.Set( v.x + size.y, v.y - size.x, v.z );
+ vbPtr[1].color = color;
+ vbPtr[1].uv.Set( uv0.x, uv0.y );
+
+ vbPtr[2].vert.Set( v.x + size.x, v.y + size.y, v.z );
+ vbPtr[2].color = color;
+ vbPtr[2].uv.Set( uv0.x, uv1.y );
+
+ vbPtr[3].vert.Set( v.x - size.y, v.y + size.x, v.z );
+ vbPtr[3].color = color;
+ vbPtr[3].uv.Set( uv1.x, uv1.y );
+
+ vbPtr += 4;
+ }
+
+ vbo.ReleaseChunk( elemCount * 4, 0 );
+
+ PROFILER_BEGIN(gSubmitVBOProfileFlare, this)
+
+ vbo.DrawChunk (channels);
+ GPU_TIMESTAMP();
+
+ PROFILER_END
+}
+
+// Matches the modes used in the inspector for texture layout:
+enum FlareLayout {
+ kLayoutLargeRestSmall = 0,
+ kLayoutMixed,
+ kLayout1x1,
+ kLayout2x2,
+ kLayout3x3,
+ kLayout4x4,
+};
+
+
+// Get the UV coords for a flare element.
+// Returns: outUV0 is top-left UV, outUV1 is bottom-right UV.
+static void CalculateFlareGetUVCoords (int layout, int imageIndex, const Vector2f& pixOffset, Vector2f& outUV0, Vector2f& outUV1 )
+{
+ switch (layout)
+ {
+ case kLayoutLargeRestSmall:
+ if (imageIndex != 0) {
+ imageIndex -= 1;
+ int xImg = (imageIndex & 1);
+ int yImg = (imageIndex >> 1);
+ float imgSize = .5f;
+ outUV0 = Vector2f( (float)(xImg+0) * imgSize, (float)(yImg+0) * imgSize * 0.5f + 0.5f) + pixOffset;
+ outUV1 = Vector2f( (float)(xImg+1) * imgSize, (float)(yImg+1) * imgSize * 0.5f + 0.5f) - pixOffset;
+ } else {
+ outUV0 = Vector2f( 0.0f, 0.0f ) + pixOffset;
+ outUV1 = Vector2f( 1.0f, 0.5f ) - pixOffset;
+ }
+ break;
+
+ case kLayoutMixed:
+ switch (imageIndex) {
+ case 0:
+ outUV0 = Vector2f( 0.0f, 0.0f ); // + pixOffset;
+ outUV1 = Vector2f( 1.0f, 0.5f ) - pixOffset;
+ break;
+ case 1:
+ outUV0 = Vector2f( 0.0f, 0.50f ); // + pixOffset;
+ outUV1 = Vector2f( 0.5f, 0.75f ) - pixOffset;
+ break;
+ case 2:
+ outUV0 = Vector2f( 0.0f, 0.75f ); // + pixOffset;
+ outUV1 = Vector2f( 0.5f, 1.00f ) - pixOffset;
+ break;
+ default:
+ imageIndex -= 3;
+ int xImg = (imageIndex & 1);
+ int yImg = (imageIndex >> 1);
+ const float imgSize = 0.25f;
+ outUV0 = Vector2f( (float)(xImg+0) * imgSize + 0.5f, (float)(yImg+0) * imgSize * 0.5f + 0.5f ) + pixOffset;
+ outUV1 = Vector2f( (float)(xImg+1) * imgSize + 0.5f, (float)(yImg+1) * imgSize * 0.5f + 0.5f ) - pixOffset;
+ break;
+ }
+ break;
+
+ default:
+ {
+ // the rest of layouts are regular grids
+ const int grid = layout - 1;
+ int xImg = imageIndex % grid;
+ int yImg = imageIndex / grid;
+ float imgSize = 1.0f / (float)grid;
+ outUV0 = Vector2f( (float)(xImg+0) * imgSize, (float)(yImg+0) * imgSize ) + pixOffset;
+ outUV1 = Vector2f( (float)(xImg+1) * imgSize, (float)(yImg+1) * imgSize ) - pixOffset;
+ }
+ break;
+ }
+}
+
+
+FlareManager &FlareManager::Get ()
+{
+ static FlareManager* s_FlareManager = NULL;
+
+ // Create the material used for the flares
+ if (!s_FlareMaterial)
+ {
+ Shader* shader = GetScriptMapper ().FindShader ("Hidden/Internal-Flare");
+ if(shader)
+ s_FlareMaterial = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+ }
+
+ if (!s_FlareManager)
+ s_FlareManager = new FlareManager();
+
+ return *s_FlareManager;
+}
+
+int FlareManager::AddFlare ()
+{
+ // Find unused flare, and put it there if there is one
+ FlareList::iterator it, itEnd = m_Flares.end();
+ int index = 0;
+ for (it = m_Flares.begin(); it != itEnd; ++it, ++index)
+ {
+ if (!it->used)
+ {
+ it->used = true;
+ // set brightness of this to zero in all cameras
+ for (RendererList::iterator rit = m_Renderers.begin(); rit != m_Renderers.end(); ++rit)
+ {
+ DebugAssert (rit->second.size() == m_Flares.size());
+ rit->second[index] = 0.0f;
+ }
+ return index;
+ }
+ }
+
+ // No unused flare found; add new one
+ index = m_Flares.size();
+ m_Flares.push_back (FlareEntry());
+ // add zero brightness to all cameras
+ for (RendererList::iterator rit = m_Renderers.begin(); rit != m_Renderers.end(); ++rit)
+ {
+ rit->second.push_back (0.0f);
+ DebugAssert (rit->second.size() == m_Flares.size());
+ }
+ return index;
+}
+
+void FlareManager::UpdateFlare( int handle, Flare *flare, const Vector3f &position,
+ bool infinite, float brightness, const ColorRGBAf &color,
+ float fadeSpeed, UInt32 layers, UInt32 ignoredLayers
+ )
+{
+ Assert (handle >= 0 && handle < m_Flares.size());
+ FlareEntry& i = m_Flares[handle];
+ i.position = position;
+ i.flare = flare;
+ i.infinite = infinite;
+ i.brightness = brightness;
+ i.color = color;
+ i.fadeSpeed = fadeSpeed;
+ i.layers = layers;
+ i.ignoredLayers = ignoredLayers;
+}
+
+void FlareManager::DeleteFlare (int handle)
+{
+ Assert (handle >= 0 && handle < m_Flares.size());
+ // mark it as unused
+ FlareEntry& flare = m_Flares[handle];
+ flare.used = false;
+}
+
+void FlareManager::AddCamera (Camera &camera) {
+ Assert (m_Renderers.find (&camera) == m_Renderers.end());
+ m_Renderers[&camera] = std::vector<float> ();
+ std::vector<float> &vec = m_Renderers[&camera];
+ vec.resize (m_Flares.size(), 0.0f);
+ Assert (vec.size() == m_Flares.size());
+}
+
+void FlareManager::RemoveCamera (Camera &camera) {
+ RendererList::iterator i = m_Renderers.find (&camera);
+ AssertIf (i == m_Renderers.end());
+ m_Renderers.erase (i);
+}
+
+void FlareManager::Update () {
+ Camera &cam = GetCurrentCamera();
+ RendererList::iterator it = m_Renderers.find (&cam);
+ if (it == m_Renderers.end())
+ {
+ AssertString ("Flare renderer to update not found");
+ return;
+ }
+
+ Assert (it->second.size() == m_Flares.size());
+ float *brightness = (it->second.size() ? &it->second[0] : NULL);
+
+ float camFar = cam.GetFar();
+
+ for (FlareList::iterator i = m_Flares.begin(); i != m_Flares.end(); ++i, ++brightness)
+ {
+ if (!i->used)
+ continue;
+
+ int layers = ~i->ignoredLayers;
+ if (!(i->layers & layers))
+ continue;
+
+ float fadeAmt = i->fadeSpeed * (IsWorldPlaying() ? GetDeltaTime() : 1.0f);
+ // We want flares to fade out at half speed of which they fade in
+ float fadeOutAmt = fadeAmt * .5f;
+
+ float targetVisible;
+ Vector3f pos;
+ if (!i->infinite)
+ {
+ pos = cam.WorldToViewportPoint (i->position);
+ if( pos.z < camFar && (pos.x > 0.0f && pos.x < 1.0f) && (pos.y > 0.0f && pos.y < 1.0f) )
+ targetVisible = 1.0f;
+ else
+ targetVisible = 0.0f;
+ }
+ else
+ {
+ pos = cam.WorldToViewportPoint( GetCurrentCamera().GetPosition () + i->position );
+ if( pos.x > 0.0F && pos.x < 1.0F && pos.y > 0.0F && pos.y < 1.0F )
+ targetVisible = 1.0f;
+ else
+ targetVisible = 0.0f;
+ }
+
+ if (targetVisible)
+ {
+ float t = 10000;
+ Ray r;
+ r.SetOrigin (cam.GetPosition());
+ if (!i->infinite) {
+ t = Magnitude (cam.GetPosition() - i->position);
+ r.SetDirection ((i->position - cam.GetPosition()) / t);
+ } else
+ r.SetDirection (-i->position);
+
+ IRaycast* raycast = GetRaycastInterface ();
+ HitInfo hit;
+ if (raycast && raycast->Raycast (r, t, layers, hit))
+ targetVisible = 0.0f;
+ }
+
+ if (targetVisible > *brightness) {
+ *brightness += fadeAmt;
+ if (*brightness > 1.0f)
+ *brightness = 1.0f;
+ } else if (targetVisible < *brightness) {
+ *brightness -= fadeOutAmt;
+ if (*brightness < 0.0f)
+ *brightness = 0.0f;
+ }
+ }
+}
+
+void FlareManager::RenderFlares ()
+{
+ if(!s_FlareMaterial)
+ return;
+ Shader* shader = s_FlareMaterial->GetShader();
+ if (!shader)
+ return;
+ if(!GetCurrentCameraPtr())
+ return;
+
+ Camera &cam = GetCurrentCamera();
+ float doubleNearDistance = cam.GetNear() * 2.0F;
+
+ Update ();
+
+ GfxDevice& device = GetGfxDevice();
+ float matWorld[16], matView[16];
+ CopyMatrix(device.GetViewMatrix(), matView);
+ CopyMatrix(device.GetWorldMatrix(), matWorld);
+ device.SetViewMatrix (Matrix4x4f::identity.GetPtr()); // implicitly sets world to identity
+
+ RendererList::iterator it = m_Renderers.find (&cam);
+ Assert (it->second.size() == m_Flares.size());
+ float *brightness = (it->second.size() ? &it->second[0] : NULL);
+
+ Texture *lastTex = NULL;
+ const ChannelAssigns* channels = NULL;
+ Matrix4x4f cameraMatrix = GetCurrentCamera().GetWorldToCameraMatrix ();
+ for (FlareList::iterator i = m_Flares.begin(); i != m_Flares.end(); ++i, ++brightness)
+ {
+ if (!i->used)
+ continue;
+
+ if ((*brightness) <= 0.0f)
+ continue;
+
+ Flare *fl = i->flare;
+ if (!fl)
+ continue;
+
+ Vector3f pos;
+ if (!i->infinite)
+ pos = cameraMatrix.MultiplyPoint3 (i->position);
+ else
+ pos = cameraMatrix.MultiplyVector3 (-i->position * doubleNearDistance);
+
+ Texture* flareTex = fl->GetTexture();
+
+ if (!flareTex)
+ continue;
+
+ if (lastTex != flareTex)
+ {
+ lastTex = flareTex;
+ ShaderLab::g_GlobalProperties->SetTexture(kSLPropFlareTexture, lastTex);
+ channels = s_FlareMaterial->SetPassWithShader( 0, shader, 0 );
+ }
+
+ fl->Render (pos, *brightness * i->brightness, i->color, *channels);
+ }
+
+ device.SetViewMatrix(matView);
+ device.SetWorldMatrix(matWorld);
+}
+
+
+IMPLEMENT_CLASS_HAS_INIT (LensFlare)
+IMPLEMENT_OBJECT_SERIALIZE (LensFlare)
+
+LensFlare::LensFlare (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_Handle = -1;
+ m_FadeSpeed = 3.0f;
+}
+
+LensFlare::~LensFlare ()
+{
+}
+
+void LensFlare::Reset () {
+ Super::Reset();
+ m_Brightness = 1.0f;
+ m_Color = ColorRGBAf (1,1,1,0);
+ m_Directional = false;
+ m_FadeSpeed = 3.0f;
+ m_IgnoreLayers.m_Bits = kNoFXLayerMask | kIgnoreRaycastMask;
+}
+
+void LensFlare::InitializeClass () {
+ REGISTER_MESSAGE_VOID (LensFlare, kTransformChanged, TransformChanged);
+}
+
+template<class TransferFunc>
+void LensFlare::Transfer (TransferFunc& transfer) {
+ Super::Transfer (transfer);
+ TRANSFER_SIMPLE (m_Flare);
+ TRANSFER_SIMPLE (m_Color);
+ TRANSFER (m_Brightness);
+ TRANSFER (m_FadeSpeed);
+ TRANSFER (m_IgnoreLayers);
+ TRANSFER (m_Directional);
+}
+
+inline void LensFlare::UpdateFlare () {
+ Vector3f pos;
+ if (!m_Directional)
+ pos = GetComponent(Transform).GetPosition();
+ else
+ pos = GetComponent(Transform).TransformDirection (Vector3f (0,0,1));
+
+ GetFlareManager().UpdateFlare ( m_Handle, m_Flare, pos, m_Directional,
+ m_Brightness, m_Color, m_FadeSpeed,
+ GetGameObject().GetLayerMask(), m_IgnoreLayers.m_Bits
+ );
+}
+
+void LensFlare::AwakeFromLoad (AwakeFromLoadMode awakeMode) {
+ Super::AwakeFromLoad (awakeMode);
+ if ((awakeMode & kDidLoadFromDisk) == 0 && m_Handle != -1)
+ UpdateFlare ();
+}
+
+/// Update the flare in the FlareManager when the GO moves.
+void LensFlare::TransformChanged () {
+ if (m_Handle != -1)
+ UpdateFlare ();
+}
+
+void LensFlare::SetBrightness (float brightness) {
+ m_Brightness = brightness;
+ SetDirty ();
+ if (m_Handle != -1)
+ UpdateFlare ();
+}
+
+void LensFlare::SetFadeSpeed (float fadeSpeed) {
+ m_FadeSpeed = fadeSpeed;
+ SetDirty ();
+ if (m_Handle != -1)
+ UpdateFlare ();
+}
+
+void LensFlare::SetColor (const ColorRGBAf& color) {
+ m_Color = color;
+ SetDirty ();
+ if (m_Handle != -1)
+ UpdateFlare ();
+}
+
+
+void LensFlare::SetFlare (Flare *flare) {
+ m_Flare = flare;
+ SetDirty ();
+ if (m_Handle != -1)
+ UpdateFlare ();
+}
+
+void LensFlare::AddToManager () {
+ m_Handle = GetFlareManager().AddFlare ();
+ UpdateFlare ();
+}
+
+void LensFlare::RemoveFromManager () {
+ GetFlareManager().DeleteFlare (m_Handle);
+ m_Handle = -1;
+}
+
+IMPLEMENT_CLASS (FlareLayer)
+
+FlareLayer::FlareLayer (MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode)
+{}
+
+FlareLayer::~FlareLayer ()
+{
+}
+
+void FlareLayer::AddToManager ()
+{
+ Camera &cam = GetComponent (Camera);
+ GetFlareManager().AddCamera (cam);
+}
+
+void FlareLayer::RemoveFromManager ()
+{
+ Camera &cam = GetComponent (Camera);
+ GetFlareManager().RemoveCamera (cam);
+}
+
+IMPLEMENT_CLASS (Flare)
+IMPLEMENT_OBJECT_SERIALIZE (Flare)
diff --git a/Runtime/Camera/Flare.h b/Runtime/Camera/Flare.h
new file mode 100644
index 0000000..8c8dc9e
--- /dev/null
+++ b/Runtime/Camera/Flare.h
@@ -0,0 +1,169 @@
+#ifndef FLARE_H
+#define FLARE_H
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Graphics/Texture.h"
+
+class Camera;
+class ChannelAssigns;
+
+// Source asset for a lens flare.
+// Essentially it has some standard settings and a list of flare elements that make up the flare.
+// All per-camera visibility is handled by the FlareManager, which is also responsible for rendering this flare.
+class Flare : public NamedObject {
+public:
+ REGISTER_DERIVED_CLASS (Flare, NamedObject)
+ DECLARE_OBJECT_SERIALIZE (Flare)
+
+ Flare(MemLabelId label, ObjectCreationMode mode);
+
+ virtual void Reset ();
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ // Render this flare with the center at pos, a visibility factor and a tint Color.
+ void Render (Vector3f &pos, float visibility, const ColorRGBAf &tintColor, const ChannelAssigns& channels);
+
+ // Set/Get the texture to use as a source for this flare
+ void SetTexture (Texture *texture) { m_FlareTexture = texture; }
+ Texture *GetTexture () const { return m_FlareTexture; }
+
+private:
+ struct FlareElement {
+ unsigned int m_ImageIndex; ///< The image index from the flare texture
+ float m_Position; ///< The position of the element (0 = light, 1 = screen center)
+ float m_Size; ///< The size of the element
+ ColorRGBAf m_Color; ///< Element color tint
+ bool m_UseLightColor; ///< Pick up the color from a light source?
+ bool m_Rotate; ///< Rotate the flare in respect to light angle?
+ bool m_Zoom; ///< Make the flare size dependent on visibility?
+ bool m_Fade; ///< Make the flare fade dependent on visibility?
+
+ FlareElement() {m_Fade=true;}
+
+ DECLARE_SERIALIZE (FlareElement)
+ };
+ std::vector<FlareElement> m_Elements; ///< The individual flare elements.
+ PPtr<Texture> m_FlareTexture; ///< The texture used for the flare elements.
+
+ int m_TextureLayout; ///< enum { 1 Large 4 Small = 0, 1 Large 2 Medium 8 Small, 1 Texture, 2x2 Grid, 3x3 Grid, 4x4 Grid } Flare element layout in the texture.
+
+ bool m_UseFog;
+ Vector2f m_PixOffset;
+};
+
+template<class TransferFunc>
+void Flare::FlareElement::Transfer (TransferFunc& transfer) {
+ TRANSFER_SIMPLE (m_ImageIndex);
+ TRANSFER_SIMPLE (m_Position);
+ TRANSFER_SIMPLE (m_Size);
+ TRANSFER_SIMPLE (m_Color);
+ TRANSFER (m_UseLightColor);
+ TRANSFER (m_Rotate);
+ TRANSFER (m_Zoom);
+ TRANSFER (m_Fade);
+}
+
+/// \todo Show flare outside screen option
+/// \todo fade non-inf flares dependant on fog settings
+class FlareManager {
+public:
+ struct FlareEntry {
+ ColorRGBAf color;
+ Vector3f position; // The world-space position of the flare, OR the direction vector if inf
+ PPtr<Flare> flare;
+ UInt32 layers;
+ UInt32 ignoredLayers;
+ float brightness;
+ float fadeSpeed;
+ bool infinite;
+ bool used;
+ FlareEntry ()
+ : position (Vector3f (0,0,0)), layers (-1), ignoredLayers(-1), brightness(0.0f), infinite (false), used (true), fadeSpeed (3.0f) {}
+ };
+ static FlareManager &Get ();
+
+ // Add a flare entry. returns the handle to the flare element.
+ int AddFlare ();
+ void UpdateFlare ( int handle, Flare *flare, const Vector3f &position,
+ bool infinite, float brightness, const ColorRGBAf &color,
+ float fadeSpeed, UInt32 layers, UInt32 ignoredLayers
+ );
+ void DeleteFlare (int handle);
+
+ /// Add and remove a camera from the list of cameras to track
+ /// Used by the flarelayer
+ void AddCamera (Camera &camera);
+ void RemoveCamera (Camera &camera);
+
+ void RenderFlares ();
+
+private:
+ /// The brightness for each camera for each flare.
+ typedef std::map <const Camera *, std::vector<float> > RendererList;
+ RendererList m_Renderers;
+
+ typedef std::vector<FlareEntry> FlareList;
+ FlareList m_Flares;
+ void Update ();
+};
+
+inline FlareManager &GetFlareManager () {
+ return FlareManager::Get();
+}
+
+class LensFlare : public Behaviour {
+public:
+ REGISTER_DERIVED_CLASS (LensFlare, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (LensFlare)
+
+ LensFlare(MemLabelId label, ObjectCreationMode mode);
+
+ static void InitializeClass ();
+ static void CleanupClass () {}
+
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+ virtual void Reset ();
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+ inline void UpdateFlare ();
+
+ void TransformChanged ();
+
+ void SetBrightness (float brightness);
+ float GetBrightness () const { return m_Brightness; }
+
+ void SetFadeSpeed (float m_FadeSpeed);
+ float GetFadeSpeed () const { return m_FadeSpeed; }
+
+ void SetColor (const ColorRGBAf& color);
+ const ColorRGBAf &GetColor () const {return m_Color; }
+
+
+ void SetFlare (Flare *flare);
+ Flare *GetFlare () { return m_Flare; }
+private:
+ PPtr<Flare> m_Flare; ///< Source flare asset to render.
+ ColorRGBAf m_Color; ///< Color of the flare.
+ float m_Brightness; ///< Brightness scale of the flare.
+ float m_FadeSpeed; ///< Fade speed of the flare.
+ BitField m_IgnoreLayers; ///< mask for layers that cannot hide flare
+ int m_Handle;
+ bool m_Directional; ///< Is this lensflare directional (true) or positional (false)
+};
+
+class FlareLayer : public Behaviour {
+public:
+ REGISTER_DERIVED_CLASS (FlareLayer, Behaviour)
+
+ FlareLayer (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+};
+
+
+#endif
diff --git a/Runtime/Camera/GraphicsSettings.cpp b/Runtime/Camera/GraphicsSettings.cpp
new file mode 100644
index 0000000..004f6ed
--- /dev/null
+++ b/Runtime/Camera/GraphicsSettings.cpp
@@ -0,0 +1,79 @@
+#include "UnityPrefix.h"
+#include "GraphicsSettings.h"
+#include "RenderManager.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#if UNITY_EDITOR
+#include "Runtime/Misc/ResourceManager.h"
+#endif
+
+
+GraphicsSettings::GraphicsSettings (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_NeedToInitializeDefaultShaders(false)
+{
+}
+
+GraphicsSettings::~GraphicsSettings ()
+{
+}
+
+
+void GraphicsSettings::InitializeClass ()
+{
+ RenderManager::InitializeClass();
+}
+
+void GraphicsSettings::CleanupClass ()
+{
+ RenderManager::CleanupClass();
+}
+
+
+void GraphicsSettings::SetDefaultAlwaysIncludedShaders()
+{
+ #if UNITY_EDITOR
+ m_AlwaysIncludedShaders.clear();
+ if (BuiltinResourceManager::AreResourcesInitialized())
+ m_AlwaysIncludedShaders.push_back(GetBuiltinExtraResource<Shader> ("Normal-Diffuse.shader"));
+ else
+ m_NeedToInitializeDefaultShaders = true;
+ SetDirty();
+ #endif
+}
+
+bool GraphicsSettings::IsAlwaysIncludedShader (PPtr<Shader> shader) const
+{
+ for (int i = 0; i < m_AlwaysIncludedShaders.size (); ++i)
+ if (m_AlwaysIncludedShaders[i] == shader)
+ return true;
+
+ return false;
+}
+
+#if UNITY_EDITOR
+
+void GraphicsSettings::AddAlwaysIncludedShader (PPtr<Shader> shader)
+{
+ m_AlwaysIncludedShaders.push_back (shader);
+ SetDirty ();
+}
+
+#endif
+
+void GraphicsSettings::Reset ()
+{
+ Super::Reset ();
+ SetDefaultAlwaysIncludedShaders ();
+}
+
+template<class TransferFunction>
+void GraphicsSettings::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.Transfer (m_AlwaysIncludedShaders, "m_AlwaysIncludedShaders");
+}
+
+IMPLEMENT_CLASS_HAS_INIT (GraphicsSettings)
+IMPLEMENT_OBJECT_SERIALIZE (GraphicsSettings)
+GET_MANAGER (GraphicsSettings)
diff --git a/Runtime/Camera/GraphicsSettings.h b/Runtime/Camera/GraphicsSettings.h
new file mode 100644
index 0000000..bfb95ce
--- /dev/null
+++ b/Runtime/Camera/GraphicsSettings.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Shaders/Shader.h"
+#include <vector>
+
+
+class GraphicsSettings : public GlobalGameManager
+{
+public:
+ typedef UNITY_VECTOR(kMemRenderer, PPtr<Shader>) ShaderArray;
+
+ REGISTER_DERIVED_CLASS (GraphicsSettings, GlobalGameManager)
+ DECLARE_OBJECT_SERIALIZE (GraphicsSettings)
+
+ GraphicsSettings (MemLabelId label, ObjectCreationMode mode);
+ // ~GraphicsSettings (); declared-by-macro
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ virtual void Reset ();
+
+ #if UNITY_EDITOR
+ bool DoesNeedToInitializeDefaultShaders() const { return m_NeedToInitializeDefaultShaders; }
+ #endif
+ void SetDefaultAlwaysIncludedShaders();
+
+ /// Return true if the given shader is in the list of shaders
+ /// that should always be included in builds.
+ bool IsAlwaysIncludedShader (PPtr<Shader> shader) const;
+
+#if UNITY_EDITOR
+
+ const ShaderArray& GetAlwaysIncludedShaders () const { return m_AlwaysIncludedShaders; }
+
+ /// Add a shader to the list of shaders that are aways included in builds.
+ /// NOTE: Does not check whether the shader is already on the list.
+ void AddAlwaysIncludedShader (PPtr<Shader> shader);
+
+#endif
+
+private:
+ ShaderArray m_AlwaysIncludedShaders;
+ bool m_NeedToInitializeDefaultShaders;
+};
+
+GraphicsSettings& GetGraphicsSettings();
diff --git a/Runtime/Camera/HaloManager.cpp b/Runtime/Camera/HaloManager.cpp
new file mode 100644
index 0000000..30feb4f
--- /dev/null
+++ b/Runtime/Camera/HaloManager.cpp
@@ -0,0 +1,282 @@
+#include "UnityPrefix.h"
+#include "HaloManager.h"
+#include "Runtime/Shaders/Material.h"
+#include "RenderManager.h"
+#include "Camera.h"
+#include "CullResults.h"
+#include "RenderLoops/RenderLoop.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/Profiler/Profiler.h"
+
+IMPLEMENT_CLASS_HAS_INIT (Halo)
+IMPLEMENT_OBJECT_SERIALIZE (Halo)
+
+static Material *s_HaloMaterial = NULL;
+
+Halo::Halo (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_Handle = 0;
+}
+
+Halo::~Halo ()
+{
+}
+
+void Halo::Reset ()
+{
+ Super::Reset();
+ m_Color = ColorRGBA32 (128, 128, 128, 255);
+ m_Size = 5.0f;
+}
+
+void Halo::InitializeClass () {
+ REGISTER_MESSAGE_VOID (Halo, kTransformChanged, TransformChanged);
+}
+
+void Halo::CleanupClass () {
+// s_HaloMaterial is clean up by UnloadAllObjects()
+}
+
+template<class TransferFunc>
+void Halo::Transfer (TransferFunc& transfer) {
+ Super::Transfer (transfer);
+ TRANSFER_SIMPLE (m_Color);
+ TRANSFER_SIMPLE (m_Size);
+}
+
+static void LoadHaloMaterial()
+{
+ if (s_HaloMaterial)
+ return;
+
+ SET_ALLOC_OWNER(NULL);
+ Shader* shader = GetScriptMapper ().FindShader ("Hidden/Internal-Halo");
+ if (shader)
+ s_HaloMaterial = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+}
+
+void Halo::AwakeFromLoad (AwakeFromLoadMode awakeMode) {
+ Super::AwakeFromLoad (awakeMode);
+ if ((awakeMode & kDidLoadFromDisk) == 0 && m_Handle)
+ GetHaloManager().UpdateHalo (m_Handle, GetComponent (Transform).GetPosition(), m_Color, m_Size, GetGameObject ().GetLayerMask());
+
+ LoadHaloMaterial();
+}
+
+void Halo::TransformChanged () {
+ if (m_Handle)
+ GetHaloManager().UpdateHalo (m_Handle, GetComponent (Transform).GetPosition(), m_Color, m_Size, GetGameObject ().GetLayerMask());
+}
+void Halo::AddToManager () {
+ m_Handle = GetHaloManager().AddHalo ();
+ GetHaloManager().UpdateHalo (m_Handle, GetComponent (Transform).GetPosition(), m_Color, m_Size, GetGameObject ().GetLayerMask());
+}
+void Halo::RemoveFromManager () {
+ GetHaloManager().DeleteHalo (m_Handle);
+ m_Handle = 0;
+}
+
+
+HaloManager::HaloManager(MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode)
+{
+}
+
+void HaloManager::AwakeFromLoad(AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad(mode);
+
+ GetRenderManager().AddCameraRenderable (this, kTransparentRenderQueue);
+}
+
+HaloManager::~HaloManager() {
+ RenderManager* mgr = GetRenderManagerPtr();
+ if (mgr) // render manager can be already gone
+ mgr->RemoveCameraRenderable (this);
+}
+
+HaloManager::Halo::Halo (int hdl)
+ : position (Vector3f (0,0,0)), color (ColorRGBAf(0,0,0)), size(1), handle(hdl), layers (1) {
+}
+HaloManager::Halo::Halo (const Vector3f &pos, const ColorRGBA32 &col, float s, int h, UInt32 _layers)
+ : position (pos), color (col), size(s), handle(h), layers (_layers) {
+}
+
+IMPLEMENT_CLASS (HaloManager)
+GET_MANAGER (HaloManager)
+
+struct HaloVertex {
+ Vector3f vert;
+ ColorRGBA32 color;
+ Vector2f uv;
+};
+PROFILER_INFORMATION(gHaloRenderProfile, "Halo.Render", kProfilerRender)
+PROFILER_INFORMATION(gSubmitVBOProfileHalo, "Mesh.SubmitVBO", kProfilerRender)
+
+void HaloManager::RenderRenderable (const CullResults& cullResults)
+{
+ // Just bail if we have no visible halos or using shader replace
+ if( m_Halos.empty() || !s_HaloMaterial || cullResults.shaderReplaceData.replacementShader != NULL )
+ return;
+
+ LoadHaloMaterial();
+ if (!s_HaloMaterial)
+ return;
+
+ Shader* shader = s_HaloMaterial->GetShader();
+
+ GfxDevice& device = GetGfxDevice();
+ #if UNITY_EDITOR
+ // don't draw when wireframe mode
+ if( device.GetWireframe() )
+ return;
+ #endif
+
+ PROFILER_AUTO(gHaloRenderProfile, this)
+
+
+ const int kHaloVertices = 21;
+ const int kMaxHalos = 65535 / kHaloVertices; // cap max halos to be rendered so we work on older DX hardware
+ int haloCount = m_Halos.size();
+ if( haloCount > kMaxHalos )
+ haloCount = kMaxHalos;
+
+ // Get VBO chunk
+ DynamicVBO& vbo = device.GetDynamicVBO();
+ HaloVertex* vbPtr;
+ if( !vbo.GetChunk( (1<<kShaderChannelVertex) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelColor),
+ haloCount * kHaloVertices, 0,
+ DynamicVBO::kDrawTriangleStrip,
+ (void**)&vbPtr, NULL ) )
+ {
+ return;
+ }
+
+
+ // Write halos into VBO
+ Camera &cam = GetCurrentCamera();
+ UInt32 layers = cam.GetCullingMask();
+ Matrix4x4f mat (cam.GetWorldToCameraMatrix());
+ int halosToRender = 0;
+ for( int i = 0; i < haloCount; ++i )
+ {
+ const Halo& halo = m_Halos[i];
+ Vector3f v = mat.MultiplyPoint3( halo.position );
+ const float s = halo.size;
+
+ // Skip this halo if behind the camera or layers don't match
+ if( v.z > -s || !(halo.layers & layers) )
+ continue;
+
+ // Dim this halo if near the camera (to avoid ugly intersection thingies).
+ ColorRGBA32 c;
+ if (v.z <= -s * 2.0f ) {
+ c = halo.color;
+ } else {
+ int fac = RoundfToInt((-v.z * 255.0f / s) - 255.0f);
+ c = halo.color * fac;
+ }
+ // Swizzle color of the renderer requires it
+ c = device.ConvertToDeviceVertexColor(c);
+
+ float z2 = v.z + s * 0.333f;
+
+ // Output 21 vertices
+ vbPtr->vert.Set( v.x ,v.y , z2); vbPtr->color = c; vbPtr->uv.Set( .5f, .5f ); ++vbPtr;
+ vbPtr->vert.Set( v.x ,v.y , z2); vbPtr->color = c; vbPtr->uv.Set( .5f, .5f ); ++vbPtr;
+ vbPtr->vert.Set( v.x-s,v.y ,v.z); vbPtr->color = c; vbPtr->uv.Set( 0.0f, .5f ); ++vbPtr;
+ vbPtr->vert.Set( v.x ,v.y , z2); vbPtr->color = c; vbPtr->uv.Set( .5f, .5f ); ++vbPtr;
+ vbPtr->vert.Set( v.x-s,v.y-s,v.z); vbPtr->color = c; vbPtr->uv.Set( 0, 0 ); ++vbPtr;
+ vbPtr->vert.Set( v.x ,v.y , z2); vbPtr->color = c; vbPtr->uv.Set( .5f, .5f ); ++vbPtr;
+ vbPtr->vert.Set( v.x ,v.y-s,v.z); vbPtr->color = c; vbPtr->uv.Set( .5f, 0 ); ++vbPtr;
+ vbPtr->vert.Set( v.x ,v.y , z2); vbPtr->color = c; vbPtr->uv.Set( .5f, .5f ); ++vbPtr;
+ vbPtr->vert.Set( v.x+s,v.y-s,v.z); vbPtr->color = c; vbPtr->uv.Set( 1, 0 ); ++vbPtr;
+ vbPtr->vert.Set( v.x ,v.y , z2); vbPtr->color = c; vbPtr->uv.Set( .5f, .5f ); ++vbPtr;
+ vbPtr->vert.Set( v.x+s,v.y ,v.z); vbPtr->color = c; vbPtr->uv.Set( 1, .5f ); ++vbPtr;
+ vbPtr->vert.Set( v.x ,v.y , z2); vbPtr->color = c; vbPtr->uv.Set( .5f, .5f ); ++vbPtr;
+ vbPtr->vert.Set( v.x+s,v.y+s,v.z); vbPtr->color = c; vbPtr->uv.Set( 1, 1 ); ++vbPtr;
+ vbPtr->vert.Set( v.x ,v.y , z2); vbPtr->color = c; vbPtr->uv.Set( .5f, .5f ); ++vbPtr;
+ vbPtr->vert.Set( v.x ,v.y+s,v.z); vbPtr->color = c; vbPtr->uv.Set( .5f, 1 ); ++vbPtr;
+ vbPtr->vert.Set( v.x ,v.y , z2); vbPtr->color = c; vbPtr->uv.Set( .5f, .5f ); ++vbPtr;
+ vbPtr->vert.Set( v.x-s,v.y+s,v.z); vbPtr->color = c; vbPtr->uv.Set( 0, 1 ); ++vbPtr;
+ vbPtr->vert.Set( v.x ,v.y , z2); vbPtr->color = c; vbPtr->uv.Set( .5f, .5f ); ++vbPtr;
+ vbPtr->vert.Set( v.x-s,v.y ,v.z); vbPtr->color = c; vbPtr->uv.Set( 0.0f, .5f ); ++vbPtr;
+ vbPtr->vert.Set( v.x ,v.y , z2); vbPtr->color = c; vbPtr->uv.Set( .5f, .5f ); ++vbPtr;
+ vbPtr->vert.Set( v.x ,v.y , z2); vbPtr->color = c; vbPtr->uv.Set( .5f, .5f ); ++vbPtr;
+
+ ++halosToRender;
+ }
+
+ vbo.ReleaseChunk( halosToRender * kHaloVertices, 0 );
+
+ float matWorld[16], matView[16];
+ CopyMatrix(device.GetViewMatrix(), matView);
+ CopyMatrix(device.GetWorldMatrix(), matWorld);
+ device.SetViewMatrix (Matrix4x4f::identity.GetPtr()); // implicitly sets world to identity
+
+ // Output halos
+ const ChannelAssigns* channels = s_HaloMaterial->SetPassWithShader( 0, shader, 0 );
+
+ PROFILER_BEGIN(gSubmitVBOProfileHalo, this)
+ vbo.DrawChunk (*channels);
+ GPU_TIMESTAMP();
+ PROFILER_END
+
+ device.SetViewMatrix(matView);
+ device.SetWorldMatrix(matWorld);
+}
+
+int HaloManager::AddHalo () {
+ int handle;
+ if (!m_Halos.empty())
+ handle = m_Halos.back().handle + 1;
+ else
+ handle = 1;
+ m_Halos.push_back (Halo (handle));
+ return handle;
+}
+
+void HaloManager::UpdateHalo (int h, Vector3f position,ColorRGBA32 color,float size, UInt32 layers)
+{
+ for (HaloList::iterator i = m_Halos.begin(); i != m_Halos.end(); i++) {
+ if (i->handle == h) {
+ i->position = position;
+ i->color = color;
+ i->size = size;
+ i->layers = layers;
+ return;
+ }
+ }
+ AssertString ("Unable to find Halo to update");
+}
+
+void HaloManager::DeleteHalo (int h)
+{
+ for (HaloList::iterator i = m_Halos.begin(); i != m_Halos.end(); i++) {
+ if (i->handle == h) {
+ m_Halos.erase (i);
+ return;
+ }
+ }
+ AssertString ("Unable to find Halo to be deleted");
+}
+
+IMPLEMENT_CLASS (HaloLayer)
+
+HaloLayer::HaloLayer (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+HaloLayer::~HaloLayer ()
+{
+}
diff --git a/Runtime/Camera/HaloManager.h b/Runtime/Camera/HaloManager.h
new file mode 100644
index 0000000..9be7395
--- /dev/null
+++ b/Runtime/Camera/HaloManager.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include "Renderable.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/GameCode/Behaviour.h"
+
+class Halo : public Behaviour {
+public:
+ REGISTER_DERIVED_CLASS (Halo, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (Halo)
+ Halo (MemLabelId label, ObjectCreationMode mode);
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+ virtual void Reset ();
+ void TransformChanged ();
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+private:
+ ColorRGBA32 m_Color;
+ float m_Size;
+ int m_Handle;
+};
+
+
+class HaloManager : public LevelGameManager, public Renderable {
+public:
+ REGISTER_DERIVED_CLASS (HaloManager, LevelGameManager)
+
+ int AddHalo ();
+ void UpdateHalo (int h, Vector3f position,ColorRGBA32 color,float size, UInt32 layers);
+ void DeleteHalo (int h);
+
+ // Renderable
+ virtual void RenderRenderable (const CullResults& CullResults);
+
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+
+private:
+ HaloManager (MemLabelId label, ObjectCreationMode mode);
+ // ~HaloManager (); declared-by-macro
+ struct Halo {
+ Vector3f position;
+ ColorRGBA32 color;
+ float size;
+ int handle;
+ UInt32 layers;
+ Halo (int hdl);
+ Halo (const Vector3f &pos, const ColorRGBA32 &col, float s, int h, UInt32 _layers);
+ };
+
+ typedef std::vector<Halo> HaloList;
+ HaloList m_Halos;
+};
+
+HaloManager& GetHaloManager();
+
+
+// DEPRECATED
+class HaloLayer : public Behaviour {
+public:
+ REGISTER_DERIVED_CLASS (HaloLayer, Behaviour)
+ HaloLayer (MemLabelId label, ObjectCreationMode mode);
+ virtual void AddToManager () {};
+ virtual void RemoveFromManager () {};
+};
diff --git a/Runtime/Camera/ImageFilters.cpp b/Runtime/Camera/ImageFilters.cpp
new file mode 100644
index 0000000..5c866c2
--- /dev/null
+++ b/Runtime/Camera/ImageFilters.cpp
@@ -0,0 +1,613 @@
+#include "UnityPrefix.h"
+#include "ImageFilters.h"
+#include "Renderable.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Shaders/Material.h"
+#include "CameraUtil.h"
+#include "Runtime/Graphics/RenderBufferManager.h"
+#include "Runtime/Graphics/RenderSurface.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+
+PROFILER_INFORMATION(gImageFxProfile, "Camera.ImageEffects", kProfilerRender);
+PROFILER_INFORMATION(gGraphicsBlitProfile, "Graphics.Blit", kProfilerRender);
+
+namespace ImageFilters_Static
+{
+
+static SHADERPROP(MainTex);
+
+} // namespace ImageFilters_Static
+
+static bool s_InsideFilterChain = false;
+static RenderTexture* s_CurrentSrcRT;
+static RenderTexture* s_CurrentFinalRT;
+
+
+// -----------------------------------------------------------------------------
+
+static int GetImageFilterSortIndex (Unity::Component* component)
+{
+ GameObject* go = component->GetGameObjectPtr();
+ int count = go ? go->GetComponentCount() : 0;
+ for (int i = 0; i < count; ++i)
+ {
+ if (&go->GetComponentAtIndex(i) == component)
+ return i;
+ }
+ return -1;
+}
+
+
+void ImageFilters::AddImageFilter (const ImageFilter& filter)
+{
+ // When importing a package over a live image filter, it does not get removed.
+ // So remove them explicitly instead of adding it multiple times.
+ #if UNITY_EDITOR
+ RemoveImageFilter (filter);
+ #endif
+
+ Filters& filters = filter.afterOpaque ? m_AfterOpaque : m_AfterEverything;
+
+ // Insert the image filter by sort index.
+ // Search backwards because in most cases the filters are added in sorted order.
+ int insertIndex = GetImageFilterSortIndex(filter.component);
+ for( int i = filters.size()-1; i >= 0; --i )
+ {
+ if (insertIndex >= GetImageFilterSortIndex(filters[i].component))
+ {
+ Filters::iterator insertion = filters.begin() + i + 1;
+ filters.insert (insertion, filter);
+ return;
+ }
+ }
+ filters.insert (filters.begin(), filter);
+}
+
+void ImageFilters::RemoveImageFilter (const ImageFilter& filter)
+{
+ for (Filters::iterator i = m_AfterOpaque.begin(); i != m_AfterOpaque.end(); /**/)
+ {
+ if (*i == filter)
+ i = m_AfterOpaque.erase (i);
+ else
+ ++i;
+ }
+ for (Filters::iterator i = m_AfterEverything.begin(); i != m_AfterEverything.end(); /**/)
+ {
+ if (*i == filter)
+ i = m_AfterEverything.erase (i);
+ else
+ ++i;
+ }
+}
+
+RenderTexture* ImageFilters::GetTargetBeforeOpaque ()
+{
+ return m_FirstTargetTexture;
+}
+
+RenderTexture* ImageFilters::GetTargetAfterOpaque (bool forceIntoRT, bool usingScreenToComposite)
+{
+ if (m_AfterOpaque.empty())
+ return m_FirstTargetTexture;
+ if (m_AfterEverything.empty() && !forceIntoRT)
+ return m_FirstTargetTexture;
+ if (usingScreenToComposite && !forceIntoRT)
+ return m_FirstTargetTexture;
+ return m_SecondTargetTexture;
+}
+
+RenderTexture* ImageFilters::GetTargetFinal ()
+{
+ return m_FinalTargetTexture;
+}
+
+static RenderTexture* GetTemporaryRT (bool depthBuffer, bool requestHDR = false, bool requestLinear = false, int antiAliasing = 1)
+{
+ RenderBufferManager& rbm = GetRenderBufferManager ();
+ RenderTexture* rt = rbm.GetTempBuffer (
+ RenderBufferManager::kFullSize,
+ RenderBufferManager::kFullSize,
+ depthBuffer ? kDepthFormat24 : kDepthFormatNone,
+ // by gl/gles spec blitting won't work if dst have components missing from src
+ // which is the case often on mobiles
+ requestHDR ? GetGfxDevice().GetDefaultHDRRTFormat() : GetGfxDevice().GetDefaultRTFormat(),
+ 0,
+ (requestLinear && !requestHDR) ? kRTReadWriteSRGB : kRTReadWriteLinear,
+ antiAliasing);
+
+ if (rt) rt->CorrectVerticalTexelSize(true);
+ return rt;
+}
+
+RenderTexture* ImageFilters::SwitchTargetToLDR (RenderTexture* oldRt, bool requestLinear)
+{
+ if(!oldRt)
+ return NULL;
+
+ RenderTexture* newRt = GetTemporaryRT (false, false, requestLinear);
+ if (oldRt == m_FirstTargetTexture) {
+ GetRenderBufferManager().ReleaseTempBuffer (oldRt);
+ m_FirstTargetTexture = newRt;
+ }
+ else if (oldRt == m_SecondTargetTexture) {
+ GetRenderBufferManager().ReleaseTempBuffer (oldRt);
+ m_SecondTargetTexture = newRt;
+ }
+ else {
+ GetRenderBufferManager().ReleaseTempBuffer (oldRt);
+ }
+
+ return newRt;
+}
+
+void ImageFilters::ReleaseTargetForLDR (RenderTexture** oldRt)
+{
+ if(!(*oldRt))
+ return;
+
+ RenderTexture* rt = *oldRt;
+
+ GetRenderBufferManager().ReleaseTempBuffer (rt);
+ if(rt == m_FirstTargetTexture)
+ m_FirstTargetTexture = NULL;
+ if(rt == m_SecondTargetTexture)
+ m_SecondTargetTexture = NULL;
+
+ *oldRt = NULL;
+}
+
+void ImageFilters::Prepare (bool forceIntoRT, bool hdr, int antiAliasing)
+{
+ Assert (!m_FirstTargetTexture && !m_SecondTargetTexture);
+
+ // Nothing to do if we have no image filters
+ if (!HasImageFilter() && !forceIntoRT)
+ return;
+
+ // Ignore image filters if we can't use them
+ if (!RenderTexture::IsEnabled() || (gGraphicsCaps.npotRT == kNPOTNone))
+ {
+ static bool errorShown = false;
+ if( !errorShown )
+ {
+ ErrorString("can't use image filters (npot-RT are not supported or RT are disabled completely)");
+ errorShown = true;
+ }
+ return;
+ }
+
+ bool linearColorSpace = GetActiveColorSpace() == kLinearColorSpace;
+
+ m_FirstTargetTexture = GetTemporaryRT (true, hdr, linearColorSpace, antiAliasing);
+
+ // find out if we're still rendering HDR after opaque
+ bool hdrAfterOpaque = hdr;
+ Filters& filters = m_AfterOpaque;
+ size_t n = filters.size();
+ for (size_t i = 0; i < n; ++i)
+ if (filters[i].transformsToLDR)
+ hdrAfterOpaque = false;
+
+ // we need second target only if have both after-opaque
+ //if (!m_AfterOpaque.empty())
+ m_SecondTargetTexture = GetTemporaryRT (false, hdrAfterOpaque, linearColorSpace, antiAliasing);
+}
+
+
+static void GetDestRenderTargetSurfaces (RenderTexture* dest, RenderSurfaceHandle& outColor, RenderSurfaceHandle& outDepth)
+{
+ if (dest && !dest->IsCreated())
+ dest->Create();
+
+ // Ugly hack: when we have image filters that are between opaque & transparent geometry,
+ // we really want to share the depth buffer for before/after rendering of that. However
+ // the current scripting API does not allow doing that!
+ //
+ // So try to detect this situation: if we're inside of image filters loop and destination
+ // is not the first target: use depth from the first one.
+ if (s_InsideFilterChain && dest && s_CurrentSrcRT != NULL && dest == s_CurrentFinalRT && dest->GetWidth()==s_CurrentSrcRT->GetWidth() && dest->GetHeight()==s_CurrentSrcRT->GetHeight())
+ {
+ // one more ugly hack (do we even need to mark ugly hacks?)
+ // RenderBufferManager returns non-created RT, so at this point we can end up with non-created s_CurrentSrcRT
+ // e.g. first run of image filters
+ // so create it before getting depth surface
+ if(!s_CurrentSrcRT->IsCreated())
+ s_CurrentSrcRT->Create();
+
+ outColor = dest->GetColorSurfaceHandle();
+ outDepth = s_CurrentSrcRT->GetDepthSurfaceHandle();
+ }
+ else if (dest)
+ {
+ outColor = dest->GetColorSurfaceHandle();
+ outDepth = dest->GetDepthSurfaceHandle();
+ }
+ else
+ {
+ outColor = GetGfxDevice().GetBackBufferColorSurface();
+ outDepth = GetGfxDevice().GetBackBufferDepthSurface();
+ }
+}
+
+
+void ImageFilters::DoRender (RenderTexture* finalRT, bool forceIntoRT, bool afterOpaque, bool usingScreenToComposite, bool hdr)
+{
+ // Ignore image filters if we can't use them
+ if (!RenderTexture::IsEnabled() || (gGraphicsCaps.npotRT == kNPOTNone))
+ return;
+
+ PROFILER_AUTO_GFX(gImageFxProfile, NULL)
+ GPU_AUTO_SECTION(kGPUSectionPostProcess);
+
+ bool buffersInHDR = hdr;
+ bool linearColorSpace = GetActiveColorSpace() == kLinearColorSpace;
+
+ RenderBufferManager& rbm = GetRenderBufferManager();
+ RenderTexture* srcRT = NULL;
+ RenderTexture* dstRT = NULL;
+
+ m_FinalTargetTexture = GetGfxDevice().GetActiveRenderTexture();
+
+ if (afterOpaque)
+ {
+ srcRT = m_FirstTargetTexture;
+ if ((!m_AfterEverything.empty() && !usingScreenToComposite) || forceIntoRT)
+ finalRT = m_SecondTargetTexture;
+ }
+ else
+ {
+ srcRT = GetTargetAfterOpaque(forceIntoRT, usingScreenToComposite);
+ }
+
+ // store current values of global state (to make re-entrancy work)
+ bool oldInside = s_InsideFilterChain;
+ RenderTexture *oldSrcRT = s_CurrentSrcRT;
+ RenderTexture *oldFinalRT = s_CurrentFinalRT;
+ s_InsideFilterChain = false;
+ s_CurrentSrcRT = srcRT;
+ s_CurrentFinalRT = finalRT;
+ GfxDevice& device = GetGfxDevice();
+
+ Filters& filters = afterOpaque ? m_AfterOpaque : m_AfterEverything;
+ size_t n = filters.size();
+ for (size_t i = 0; i < n; ++i)
+ {
+ RenderTexture* dst;
+ if (i == n-1)
+ dst = finalRT;
+ else
+ {
+ if (filters[i].transformsToLDR && buffersInHDR) {
+ buffersInHDR = false;
+ dstRT = SwitchTargetToLDR(dstRT, linearColorSpace);
+ }
+ if (!dstRT)
+ {
+ dstRT = GetTemporaryRT(false, buffersInHDR, linearColorSpace);
+ }
+ dst = dstRT;
+ }
+
+
+ // Render one image effect
+
+ s_InsideFilterChain = true;
+ PROFILER_AUTO_GFX(gImageFxProfile, filters[i].component);
+
+ // Discard any destination RT contents before rendering the effect into it
+ // NB: do not discard back buffer here
+ RenderSurfaceHandle dstRsColor, dstRsDepth;
+ GetDestRenderTargetSurfaces (dst, dstRsColor, dstRsDepth);
+ if(!dstRsColor.object->backBuffer)
+ device.DiscardContents (dstRsColor);
+ // However, do not discard depth if we're in the opaque image effects
+ // stage and it's our final destination depth - we will still need
+ // it for later alpha rendering.
+ if (dstRsDepth != s_CurrentSrcRT->GetDepthSurfaceHandle() && !dstRsDepth.object->backBuffer)
+ device.DiscardContents (dstRsDepth);
+ else
+ device.IgnoreNextUnresolveOnRS (dstRsDepth); // we'll have to un-resolve it, so silence up the warning
+
+ // Invoke actual image effect function
+ filters[i].renderFunc (filters[i].component, srcRT, dst);
+ s_InsideFilterChain = false;
+
+
+ // if we have just converted to LDR, we need to completely switch to LDR, so let's release src
+ if (filters[i].transformsToLDR && hdr && !buffersInHDR) {
+ //srcRT = SwitchTargetToLDR(srcRT, linearColorSpace);
+ if(srcRT)
+ ReleaseTargetForLDR(&srcRT);
+ }
+
+ // We are ping-ponging between textures when there are more than 2 image filters.
+ // If the very first one was AA-resolved, it's upside down flip is already handled.
+ if (srcRT) srcRT->CorrectVerticalTexelSize(true);
+
+ std::swap (srcRT, dstRT);
+ }
+
+ bool needsBlitIntoFinalRT = !afterOpaque && forceIntoRT && filters.empty();
+
+ if (needsBlitIntoFinalRT)
+ {
+ ImageFilters::Blit (srcRT, finalRT);
+ }
+
+ if (n > 0 || needsBlitIntoFinalRT)
+ {
+ // we actually rendered into finalRT
+ m_FinalTargetTexture = finalRT;
+ }
+
+ if (dstRT && dstRT != m_FirstTargetTexture && dstRT != m_SecondTargetTexture)
+ rbm.ReleaseTempBuffer (dstRT);
+
+ if (srcRT && srcRT != m_FirstTargetTexture && srcRT != m_SecondTargetTexture)
+ rbm.ReleaseTempBuffer (srcRT);
+
+ if (!afterOpaque)
+ {
+ if (m_FirstTargetTexture)
+ {
+ rbm.ReleaseTempBuffer (m_FirstTargetTexture);
+ m_FirstTargetTexture = NULL;
+ }
+ if (m_SecondTargetTexture)
+ {
+ rbm.ReleaseTempBuffer (m_SecondTargetTexture);
+ if (m_SecondTargetTexture == m_FinalTargetTexture)
+ m_FinalTargetTexture = NULL;
+ m_SecondTargetTexture = NULL;
+ }
+ }
+
+ GetGfxDevice().SetSRGBWrite(false);
+
+ // resstore old values of global state
+ s_InsideFilterChain = oldInside;
+ s_CurrentSrcRT = oldSrcRT;
+ s_CurrentFinalRT = oldFinalRT;
+}
+
+// -----------------------------------------------------------------------------
+
+void ImageFilters::Blit (Texture* source, RenderTexture* dest)
+{
+ static Material* s_BlitMaterial = NULL;
+ if (!s_BlitMaterial){
+ Shader* shader = GetScriptMapper().FindShader ("Hidden/BlitCopy");
+ s_BlitMaterial = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+ }
+ Blit (source, dest, s_BlitMaterial, -1, true);
+}
+
+void ImageFilters::DrawQuadNoGPUTimestamp (GfxDevice& device, bool invertY, float uvX, float uvY)
+{
+ device.ImmediateBegin (kPrimitiveQuads);
+ float y1, y2;
+ if (invertY) {
+ y1 = uvY; y2 = 0.0f;
+ } else {
+ y1 = 0.0f; y2 = uvY;
+ }
+
+ // set the vertex color to white, otherwise shader doing the blit might get some random color
+ device.ImmediateColor(1.0f, 1.0f, 1.0f, 1.0f);
+
+ device.ImmediateTexCoordAll (0.0f, y1, 0.0f); device.ImmediateVertex (0.0f, 0.0f, 0.1f);
+ device.ImmediateTexCoordAll (0.0f, y2, 0.0f); device.ImmediateVertex (0.0f, 1.0f, 0.1f);
+ device.ImmediateTexCoordAll (uvX, y2, 0.0f); device.ImmediateVertex (1.0f, 1.0f, 0.1f);
+ device.ImmediateTexCoordAll (uvX, y1, 0.0f); device.ImmediateVertex (1.0f, 0.0f, 0.1f);
+ device.ImmediateEnd ();
+}
+
+void ImageFilters::DrawQuad (GfxDevice& device, bool invertY, float uvX, float uvY)
+{
+ DrawQuadNoGPUTimestamp(device, invertY, uvX, uvY);
+ GPU_TIMESTAMP();
+}
+
+
+static void SetMultiTapTexCoords (GfxDevice& device, float invSourceSizeX, float invSourceSizeY, float x, float y, bool invertY, int count, const Vector2f* offsets)
+{
+ for (int i = 0; i < count; ++i)
+ {
+ Vector2f offset = offsets[i];
+ if (invertY)
+ offset.y = -offset.y;
+ offset.x *= invSourceSizeX;
+ offset.y *= invSourceSizeY;
+ device.ImmediateTexCoord (i, x + offset.x, y + offset.y, 0.0f);
+ }
+}
+
+void ImageFilters::SetCurrentRenderTarget (RenderTexture* dest, UInt32 flags)
+{
+ RenderSurfaceHandle rsColor, rsDepth;
+ GetDestRenderTargetSurfaces (dest, rsColor, rsDepth);
+ RenderTexture::SetActive (1, &rsColor, rsDepth, dest, 0, kCubeFaceUnknown, flags);
+ RenderTexture::FindAndSetSRGBWrite (dest);
+}
+
+static bool IsActiveRenderTextureMSAA ()
+{
+ RenderTexture* rt = RenderTexture::GetActive();
+ return (rt && rt->IsAntiAliased());
+}
+
+void ImageFilters::Blit (Texture* source, RenderTexture* dest, Unity::Material* mat, int pass, bool setRT)
+{
+ using namespace ImageFilters_Static;
+ PROFILER_AUTO(gGraphicsBlitProfile, mat->GetShader())
+
+ GfxDevice& device = GetGfxDevice();
+
+ UInt32 rtFlags = 0;
+#if UNITY_XENON
+ // Xbox 360 must resolve a render target before using it as a texture.
+ if (source == dest)
+ {
+ rtFlags |= RenderTexture::kFlagForceResolve;
+ }
+ else if (!setRT)
+ {
+ // Render target was set previously. Get it and compare.
+ if (source == device.GetActiveRenderTexture())
+ {
+ setRT = true;
+ rtFlags |= RenderTexture::kFlagForceResolve;
+ }
+ }
+#endif
+ // MSAA render targets must be resolved before they are used.
+ if (IsActiveRenderTextureMSAA())
+ {
+ setRT = true;
+ rtFlags |= RenderTexture::kFlagForceResolve;
+ }
+ if (setRT)
+ SetCurrentRenderTarget (dest, rtFlags);
+
+
+ bool setTexture = source && mat->HasProperty(kSLPropMainTex);
+ if (setTexture)
+ mat->SetTexture (kSLPropMainTex, source);
+ bool invertY = source && source->GetTexelSizeY() < 0.0f;
+
+ float uvX = 1.0f, uvY = 1.0f;
+ #if GFX_EMULATES_NPOT_RENDERTEXTURES
+ if (source)
+ {
+ int texWidth = source->GetGLWidth();
+ int texHeight = source->GetGLHeight();
+ uvX = (float)texWidth / (float)NextPowerOfTwo(texWidth);
+ uvY = (float)texHeight / (float)NextPowerOfTwo(texHeight);
+ }
+ #endif
+
+ DeviceMVPMatricesState preserveMVP;
+
+ LoadFullScreenOrthoMatrix();
+
+ int npasses = mat->GetPassCount ();
+ if (pass == -1)
+ {
+ for (int i = 0; i < npasses; ++i)
+ {
+ mat->SetPass (i);
+ DrawQuad (device, invertY, uvX, uvY);
+ }
+ }
+ else
+ {
+ if (pass >= 0 && pass < npasses)
+ {
+ mat->SetPass (pass);
+ DrawQuad (device, invertY, uvX, uvY);
+ }
+ else
+ {
+ ErrorString ("Invalid pass number for Graphics.Blit");
+ }
+ }
+
+ if (setTexture)
+ mat->SetTexture (kSLPropMainTex, NULL);
+}
+
+void ImageFilters::BlitMultiTap (Texture* source, RenderTexture* dest, Material* mat, int count, const Vector2f* offsets)
+{
+ using namespace ImageFilters_Static;
+
+ PROFILER_AUTO(gGraphicsBlitProfile, mat->GetShader())
+
+ UInt32 rtFlags = 0;
+#if UNITY_XENON
+ // Xbox 360 must resolve a render target before using it as a texture.
+ // MSAA render targets also need to be resolved before they are used.
+ if (source == dest)
+ rtFlags |= RenderTexture::kFlagForceResolve;
+#endif
+ // MSAA render targets must be resolved before they are used.
+ if (IsActiveRenderTextureMSAA())
+ {
+ rtFlags |= RenderTexture::kFlagForceResolve;
+ }
+ SetCurrentRenderTarget (dest, rtFlags);
+
+ bool setTexture = source && mat->HasProperty(kSLPropMainTex);
+ if (setTexture)
+ mat->SetTexture (kSLPropMainTex, source);
+ bool invertY = source && source->GetTexelSizeY() < 0.0f;
+
+ float uvX = 1.0f, uvY = 1.0f;
+ int texWidth = 0, texHeight = 0;
+ if (source)
+ {
+ texWidth = source->GetGLWidth();
+ texHeight = source->GetGLHeight();
+ #if GFX_EMULATES_NPOT_RENDERTEXTURES
+ int potWidth = NextPowerOfTwo(texWidth);
+ int potHeight = NextPowerOfTwo(texHeight);
+ uvX = (float)texWidth / (float)potWidth;
+ uvY = (float)texHeight / (float)potHeight;
+ texWidth = potWidth;
+ texHeight = potHeight;
+ #endif
+ }
+
+ GfxDevice& device = GetGfxDevice();
+ DeviceMVPMatricesState preserveMVP;
+ LoadFullScreenOrthoMatrix();
+
+ int npasses = mat->GetPassCount ();
+ for (int i = 0; i < npasses; ++i)
+ {
+ float y1, y2;
+ if (invertY)
+ {
+ y1 = uvY; y2 = 0.0f;
+ }
+ else
+ {
+ y1 = 0.0f; y2 = uvY;
+ }
+ float invSizeX = source ? 1.0f / texWidth : 0.0f;
+ float invSizeY = source ? 1.0f / texHeight : 0.0f;
+
+ mat->SetColor(ShaderLab::Property("_BlurOffsets"), ColorRGBAf(offsets[0].x, offsets[0].y, 0.0f, y1));
+
+ mat->SetPass (i);
+
+ device.ImmediateBegin (kPrimitiveQuads);
+
+ SetMultiTapTexCoords( device, invSizeX, invSizeY, 0.0f, y1, invertY, count, offsets );
+ device.ImmediateVertex (0.0f, 0.0f, 0.1f);
+
+ SetMultiTapTexCoords( device, invSizeX, invSizeY, 0.0f, y2, invertY, count, offsets );
+ device.ImmediateVertex (0.0f, 1.0f, 0.1f);
+
+ SetMultiTapTexCoords( device, invSizeX, invSizeY, uvX, y2, invertY, count, offsets );
+ device.ImmediateVertex (1.0f, 1.0f, 0.1f);
+
+ SetMultiTapTexCoords( device, invSizeX, invSizeY, uvX, y1, invertY, count, offsets );
+ device.ImmediateVertex (1.0f, 0.0f, 0.1f);
+
+ device.ImmediateEnd ();
+ GPU_TIMESTAMP();
+ }
+
+ if (setTexture)
+ mat->SetTexture (kSLPropMainTex, NULL);
+
+}
+
diff --git a/Runtime/Camera/ImageFilters.h b/Runtime/Camera/ImageFilters.h
new file mode 100644
index 0000000..36439af
--- /dev/null
+++ b/Runtime/Camera/ImageFilters.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include "Renderable.h"
+
+class RenderTexture;
+class Texture;
+class Vector2f;
+class GfxDevice;
+namespace Unity { class Material; }
+
+
+// Image filters functionality. Only used internally by the camera (and other minor places).
+class ImageFilters
+{
+public:
+ ImageFilters() : m_FirstTargetTexture(NULL), m_SecondTargetTexture(NULL), m_FinalTargetTexture(NULL) { }
+
+ void AddImageFilter (const ImageFilter& filter);
+ void RemoveImageFilter (const ImageFilter& filter);
+ bool HasImageFilter() const { return !(m_AfterOpaque.empty() && m_AfterEverything.empty()); }
+ bool HasAfterOpaqueFilters() const { return !m_AfterOpaque.empty(); }
+
+ void DoRender (RenderTexture* finalRT, bool forceIntoRT, bool afterOpaque, bool usingScreenToComposite, bool hdr = false);
+ void Prepare (bool forceIntoRT, bool hdr = false, int antiAliasing = 1);
+ RenderTexture* GetTargetBeforeOpaque ();
+ RenderTexture* GetTargetAfterOpaque (bool forceIntoRT, bool usingScreenToComposite);
+ RenderTexture* GetTargetFinal ();
+ RenderTexture* SwitchTargetToLDR (RenderTexture* oldRt, bool requestLinear);
+ void ReleaseTargetForLDR (RenderTexture** oldRt);
+
+ static void Blit (Texture* source, RenderTexture* dest);
+ static void Blit (Texture* source, RenderTexture* dest, Unity::Material* mat, int pass, bool setRT);
+ static void BlitMultiTap (Texture* source, RenderTexture* dest, Unity::Material* mat, int count, const Vector2f* offsets);
+ static void DrawQuadNoGPUTimestamp (GfxDevice& device, bool invertY, float uvX, float uvY);
+ static void DrawQuad (GfxDevice& device, bool invertY, float uvX, float uvY);
+private:
+ static void SetCurrentRenderTarget (RenderTexture* dest, UInt32 flags);
+
+private:
+ typedef std::vector<ImageFilter> Filters;
+ Filters m_AfterOpaque;
+ Filters m_AfterEverything;
+ RenderTexture* m_FirstTargetTexture; // has color & depth
+ RenderTexture* m_SecondTargetTexture; // has color only, reuses depth from first
+ RenderTexture* m_FinalTargetTexture;
+};
diff --git a/Runtime/Camera/IntermediateRenderer.cpp b/Runtime/Camera/IntermediateRenderer.cpp
new file mode 100644
index 0000000..cb2520e
--- /dev/null
+++ b/Runtime/Camera/IntermediateRenderer.cpp
@@ -0,0 +1,284 @@
+#include "UnityPrefix.h"
+#include "IntermediateRenderer.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Filters/Mesh/SpriteRenderer.h"
+#include "Camera.h"
+#include "Runtime/Graphics/DrawUtil.h"
+#include "Runtime/Graphics/SpriteFrame.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+#include "UnityScene.h"
+
+
+IntermediateRenderer::IntermediateRenderer()
+: BaseRenderer(kRendererIntermediate)
+, m_Node(this)
+{
+}
+
+void IntermediateRenderer::Initialize(const Matrix4x4f& matrix, const AABB& localAABB, PPtr<Material> material, int layer, bool castShadows, bool receiveShadows)
+{
+ #if UNITY_EDITOR
+ m_InstanceID = 0;
+ #endif
+ m_Material = material;
+
+ if (layer < 0 || layer >= 32)
+ {
+ AssertString ("DrawMesh layer has to be from in [0..31] range!");
+ layer = 0;
+ }
+ m_Layer = layer;
+
+ // TODO: check if render 2 texture required for this material
+ m_CastShadows = castShadows;
+ m_ReceiveShadows = receiveShadows;
+
+ m_TransformInfo.worldMatrix = matrix;
+ // detect uniform and non-uniform scale (ignoring non-affine)
+ float uniformScale = 1.0f;
+ m_TransformInfo.transformType = ComputeTransformType(matrix, uniformScale);
+ m_TransformInfo.invScale = 1.0f / uniformScale;
+ m_TransformInfo.localAABB = localAABB;
+ TransformAABB (localAABB, matrix, m_TransformInfo.worldAABB);
+
+ Assert (m_TransformInfo.localAABB.IsValid());
+ Assert (m_TransformInfo.worldAABB.IsValid());
+
+ #if UNITY_EDITOR
+ m_ScaleInLightmap = -1.0f;
+ #endif
+
+ RendererBecameVisible ();
+
+ m_TransformDirty = false;
+ m_BoundsDirty = false;
+}
+
+IntermediateRenderer::~IntermediateRenderer()
+{
+ RendererBecameInvisible ();
+}
+
+void IntermediateRenderer::OnAssetBoundsChanged()
+{
+ // Not supported. IntermediateRenderer live only for one frame.
+}
+
+
+// --------------------------------------------------------------------------
+
+
+DEFINE_POOLED_ALLOC(MeshIntermediateRenderer, 64 * 1024);
+
+void MeshIntermediateRenderer::StaticInitialize()
+{
+ STATIC_INITIALIZE_POOL(MeshIntermediateRenderer);
+}
+
+void MeshIntermediateRenderer::StaticDestroy()
+{
+ STATIC_DESTROY_POOL(MeshIntermediateRenderer);
+}
+
+static RegisterRuntimeInitializeAndCleanup s_MeshIntermediateRendererCallbacks(MeshIntermediateRenderer::StaticInitialize, MeshIntermediateRenderer::StaticDestroy);
+
+
+MeshIntermediateRenderer::MeshIntermediateRenderer()
+{
+}
+
+MeshIntermediateRenderer::~MeshIntermediateRenderer()
+{
+}
+
+void MeshIntermediateRenderer::OnAssetDeleted()
+{
+ m_Mesh = NULL;
+}
+
+void MeshIntermediateRenderer::Render(int subsetIndex, const ChannelAssigns& channels)
+{
+ if (m_Mesh == NULL)
+ return;
+ if (m_CustomProperties)
+ GetGfxDevice().SetMaterialProperties (*m_CustomProperties);
+ DrawUtil::DrawMeshRaw (channels, *m_Mesh, m_SubMeshIndex); //@TODO: why not use subsetIndex here?
+}
+
+void MeshIntermediateRenderer::Initialize( const Matrix4x4f& matrix, Mesh* mesh, const AABB& localAABB, PPtr<Material> material, int layer, bool castShadows, bool receiveShadows, int submeshIndex )
+{
+ m_Mesh = mesh;
+ if (m_Mesh)
+ {
+ m_Mesh->AddIntermediateUser(m_Node);
+
+ if (submeshIndex < 0 || submeshIndex >= m_Mesh->GetSubMeshCount())
+ {
+ AssertString("Submesh index in intermediate renderer is out of bounds");
+ submeshIndex = 0;
+ }
+ }
+
+ m_SubMeshIndex = submeshIndex;
+
+ IntermediateRenderer::Initialize(matrix, localAABB, material, layer, castShadows, receiveShadows);
+}
+
+
+// --------------------------------------------------------------------------
+
+#if ENABLE_SPRITES
+
+DEFINE_POOLED_ALLOC(SpriteIntermediateRenderer, 64 * 1024);
+
+void SpriteIntermediateRenderer::StaticInitialize()
+{
+ STATIC_INITIALIZE_POOL(SpriteIntermediateRenderer);
+}
+
+void SpriteIntermediateRenderer::StaticDestroy()
+{
+ STATIC_DESTROY_POOL(SpriteIntermediateRenderer);
+}
+
+static RegisterRuntimeInitializeAndCleanup s_SpriteIntermediateRendererCallbacks(SpriteIntermediateRenderer::StaticInitialize, SpriteIntermediateRenderer::StaticDestroy);
+
+
+SpriteIntermediateRenderer::SpriteIntermediateRenderer()
+{
+}
+
+SpriteIntermediateRenderer::~SpriteIntermediateRenderer()
+{
+}
+
+void SpriteIntermediateRenderer::OnAssetDeleted()
+{
+ m_Sprite = NULL;
+}
+
+void SpriteIntermediateRenderer::Render(int subsetIndex, const ChannelAssigns& channels)
+{
+ if (m_Sprite == NULL)
+ return;
+ if (m_CustomProperties)
+ GetGfxDevice().SetMaterialProperties(*m_CustomProperties);
+ DrawUtil::DrawSpriteRaw(channels, *m_Sprite, m_Color);
+}
+
+void SpriteIntermediateRenderer::Initialize(const Matrix4x4f& matrix, Sprite* sprite, const AABB& localAABB, PPtr<Material> material, int layer, const ColorRGBA32& color)
+{
+ m_Sprite = sprite;
+ if (m_Sprite)
+ m_Sprite->AddIntermediateUser(m_Node);
+
+ m_Color = color;
+
+ if (!material)
+ material = SpriteRenderer::GetDefaultSpriteMaterial();
+
+ // Patch sprite texture and apply material property block
+ PPtr<Texture2D> spriteTexture = m_Sprite->GetRenderData(false).texture; // Use non-atlased RenderData as input.
+ MaterialPropertyBlock block;
+ SpriteRenderer::SetupMaterialPropertyBlock(block, spriteTexture);
+ SetPropertyBlock(block);
+
+ IntermediateRenderer::Initialize(matrix, localAABB, material, layer, false, false);
+}
+
+#endif
+
+// --------------------------------------------------------------------------
+
+
+void IntermediateRenderers::Clear( size_t startIndex )
+{
+ size_t n = m_SceneNodes.size();
+ AssertIf( startIndex > n );
+
+ for( size_t i = startIndex; i < n; ++i )
+ {
+ IntermediateRenderer* renderer = static_cast<IntermediateRenderer*> (m_SceneNodes[i].renderer);
+ delete renderer;
+ }
+ m_SceneNodes.resize_uninitialized( startIndex );
+ m_BoundingBoxes.resize_uninitialized( startIndex );
+}
+
+const AABB* IntermediateRenderers::GetBoundingBoxes () const
+{
+ return m_BoundingBoxes.begin();
+}
+
+const SceneNode* IntermediateRenderers::GetSceneNodes () const
+{
+ return m_SceneNodes.begin();
+}
+
+void IntermediateRenderers::Add(IntermediateRenderer* renderer, int layer)
+{
+ m_SceneNodes.push_back(SceneNode ());
+
+ SceneNode& node = m_SceneNodes.back();
+ node.renderer = renderer;
+ node.layer = layer;
+
+ renderer->GetWorldAABB(m_BoundingBoxes.push_back());
+}
+
+IntermediateRenderer* AddMeshIntermediateRenderer( const Matrix4x4f& matrix, Mesh* mesh, PPtr<Material> material, int layer, bool castShadows, bool receiveShadows, int submeshIndex, Camera* camera )
+{
+ AABB bounds;
+ if (mesh)
+ bounds = mesh->GetBounds();
+ else
+ bounds.SetCenterAndExtent( Vector3f::zero, Vector3f::zero );
+
+ return AddMeshIntermediateRenderer (matrix, mesh, bounds, material, layer, castShadows, receiveShadows, submeshIndex, camera);
+}
+
+IntermediateRenderer* AddMeshIntermediateRenderer( const Matrix4x4f& matrix, Mesh* mesh, const AABB& localAABB, PPtr<Material> material, int layer, bool castShadows, bool receiveShadows, int submeshIndex , Camera* camera )
+{
+ MeshIntermediateRenderer* renderer = new MeshIntermediateRenderer();
+ renderer->Initialize(matrix, mesh, localAABB, material, layer, castShadows, receiveShadows, submeshIndex);
+
+ IntermediateRenderers* renderers;
+ if (camera != NULL)
+ renderers = &camera->GetIntermediateRenderers();
+ else
+ renderers = &GetScene().GetIntermediateRenderers();
+ renderers->Add(renderer, layer);
+
+ return renderer;
+}
+
+#if ENABLE_SPRITES
+IntermediateRenderer* AddSpriteIntermediateRenderer(const Matrix4x4f& matrix, Sprite* sprite, PPtr<Material> material, int layer, const ColorRGBA32& color, Camera* camera)
+{
+ AABB bounds;
+ if (sprite)
+ bounds = sprite->GetBounds();
+ else
+ bounds.SetCenterAndExtent( Vector3f::zero, Vector3f::zero );
+
+ return AddSpriteIntermediateRenderer (matrix, sprite, bounds, material, layer, color, camera);
+}
+
+IntermediateRenderer* AddSpriteIntermediateRenderer(const Matrix4x4f& matrix, Sprite* sprite, const AABB& localAABB, PPtr<Material> material, int layer, const ColorRGBA32& color, Camera* camera)
+{
+ SpriteIntermediateRenderer* renderer = new SpriteIntermediateRenderer();
+ renderer->Initialize(matrix, sprite, localAABB, material, layer, color);
+
+ IntermediateRenderers* renderers;
+ if (camera != NULL)
+ renderers = &camera->GetIntermediateRenderers();
+ else
+ renderers = &GetScene().GetIntermediateRenderers();
+ renderers->Add(renderer, layer);
+
+ return renderer;
+}
+#endif
diff --git a/Runtime/Camera/IntermediateRenderer.h b/Runtime/Camera/IntermediateRenderer.h
new file mode 100644
index 0000000..799cf47
--- /dev/null
+++ b/Runtime/Camera/IntermediateRenderer.h
@@ -0,0 +1,148 @@
+#ifndef INTERMEDIATE_RENDERER_H
+#define INTERMEDIATE_RENDERER_H
+
+#include "BaseRenderer.h"
+#include "Runtime/Shaders/MaterialProperties.h"
+#include "Runtime/Utilities/MemoryPool.h"
+#include "SceneNode.h"
+#include "IntermediateUsers.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Modules/ExportModules.h"
+
+class Mesh;
+class Camera;
+class Matrix4x4f;
+class Vector3f;
+class Quaternionf;
+class Sprite;
+
+class EXPORT_COREMODULE IntermediateRenderer : public BaseRenderer
+{
+public:
+ IntermediateRenderer ();
+ virtual ~IntermediateRenderer();
+
+ void Initialize(const Matrix4x4f& matrix, const AABB& localAABB, PPtr<Material> material, int layer, bool castShadows, bool receiveShadows);
+
+ // BaseRenderer
+ virtual UInt32 GetLayerMask() const { return 1<<m_Layer; }
+ virtual int GetLayer() const { return m_Layer; }
+ virtual int GetMaterialCount() const { return 1; }
+ virtual PPtr<Material> GetMaterial(int i) const { return m_Material; }
+
+ virtual void OnAssetDeleted() = 0;
+ virtual void OnAssetBoundsChanged();
+
+ void SetPropertyBlock( const MaterialPropertyBlock& block )
+ {
+ m_Properties = block;
+ m_CustomProperties = &m_Properties;
+ ComputeCustomPropertiesHash();
+ }
+
+ #if UNITY_EDITOR
+ SInt32 GetInstanceID() const { return m_InstanceID; }
+ void SetInstanceID (SInt32 id) { m_InstanceID = id; }
+ #endif
+
+ virtual void UpdateTransformInfo() {};
+ virtual void UpdateAABB() {Assert(false);}
+
+ const AABB& GetCachedWorldAABB () const { return m_TransformInfo.worldAABB; }
+
+protected:
+ ListNode<IntermediateRenderer> m_Node;
+ PPtr<Material> m_Material;
+ MaterialPropertyBlock m_Properties;
+ int m_Layer;
+
+ #if UNITY_EDITOR
+ SInt32 m_InstanceID;
+ #endif
+};
+
+
+
+class EXPORT_COREMODULE MeshIntermediateRenderer : public IntermediateRenderer
+{
+public:
+ MeshIntermediateRenderer();
+ virtual ~MeshIntermediateRenderer();
+
+ void Initialize(const Matrix4x4f& matrix, Mesh* mesh, const AABB& localAABB, PPtr<Material> material, int layer, bool castShadows, bool receiveShadows, int submeshIndex);
+
+ // BaseRenderer
+ virtual void Render(int materialIndex, const ChannelAssigns& channels);
+
+ virtual void OnAssetDeleted();
+
+ static void StaticInitialize ();
+ static void StaticDestroy ();
+
+private:
+ // Note: not using per-frame linear allocator, because in the editor
+ // it can render multiple frames using single player loop run (e.g. when editor is paused).
+ // Clearing per-frame data and then trying to use it later leads to Bad Things.
+ DECLARE_POOLED_ALLOC(MeshIntermediateRenderer);
+
+ Mesh* m_Mesh;
+ int m_SubMeshIndex;
+};
+
+
+
+#if ENABLE_SPRITES
+class EXPORT_COREMODULE SpriteIntermediateRenderer : public IntermediateRenderer
+{
+public:
+ SpriteIntermediateRenderer();
+ virtual ~SpriteIntermediateRenderer();
+
+ void Initialize(const Matrix4x4f& matrix, Sprite* sprite, const AABB& localAABB, PPtr<Material> material, int layer, const ColorRGBA32& color);
+
+ // BaseRenderer
+ virtual void Render(int materialIndex, const ChannelAssigns& channels);
+
+ virtual void OnAssetDeleted();
+
+ static void StaticInitialize ();
+ static void StaticDestroy ();
+
+private:
+ // Note: not using per-frame linear allocator, because in the editor
+ // it can render multiple frames using single player loop run (e.g. when editor is paused).
+ // Clearing per-frame data and then trying to use it later leads to Bad Things.
+ DECLARE_POOLED_ALLOC(SpriteIntermediateRenderer);
+
+ Sprite* m_Sprite;
+ ColorRGBAf m_Color;
+};
+#endif
+
+
+
+class IntermediateRenderers
+{
+public:
+ void Clear( size_t startIndex = 0 );
+
+ const AABB* GetBoundingBoxes () const;
+ const SceneNode* GetSceneNodes () const;
+ size_t GetRendererCount () const { return m_BoundingBoxes.size(); }
+
+ void Add(IntermediateRenderer* renderer, int layer);
+
+private:
+ dynamic_array<SceneNode> m_SceneNodes;
+ dynamic_array<AABB> m_BoundingBoxes;
+};
+
+IntermediateRenderer* AddMeshIntermediateRenderer( const Matrix4x4f& matrix, Mesh* mesh, PPtr<Material> material, int layer, bool castShadows, bool receiveShadows, int submeshIndex, Camera* camera );
+IntermediateRenderer* AddMeshIntermediateRenderer( const Matrix4x4f& matrix, Mesh* mesh, const AABB& localAABB, PPtr<Material> material, int layer, bool castShadows, bool receiveShadows, int submeshIndex , Camera* camera );
+
+#if ENABLE_SPRITES
+IntermediateRenderer* AddSpriteIntermediateRenderer(const Matrix4x4f& matrix, Sprite* sprite, PPtr<Material> material, int layer, const ColorRGBA32& color, Camera* camera);
+IntermediateRenderer* AddSpriteIntermediateRenderer(const Matrix4x4f& matrix, Sprite* sprite, const AABB& localAABB, PPtr<Material> material, int layer, const ColorRGBA32& color, Camera* camera);
+#endif
+
+#endif
diff --git a/Runtime/Camera/IntermediateUsers.cpp b/Runtime/Camera/IntermediateUsers.cpp
new file mode 100644
index 0000000..2422376
--- /dev/null
+++ b/Runtime/Camera/IntermediateUsers.cpp
@@ -0,0 +1,21 @@
+#include "UnityPrefix.h"
+#include "IntermediateUsers.h"
+#include "IntermediateRenderer.h"
+
+void IntermediateUsers::Notify(IntermediateNotify notify)
+{
+ IntermediateRendererList::iterator i;
+ switch (notify)
+ {
+ case kImNotifyAssetDeleted:
+ for (i = m_IntermediateUsers.begin(); i != m_IntermediateUsers.end(); ++i)
+ (*i)->OnAssetDeleted();
+ break;
+ case kImNotifyBoundsChanged:
+ for (i = m_IntermediateUsers.begin(); i != m_IntermediateUsers.end(); ++i)
+ (*i)->OnAssetBoundsChanged();
+ break;
+ default:
+ AssertString("unknown notification");
+ }
+}
diff --git a/Runtime/Camera/IntermediateUsers.h b/Runtime/Camera/IntermediateUsers.h
new file mode 100644
index 0000000..273d6e0
--- /dev/null
+++ b/Runtime/Camera/IntermediateUsers.h
@@ -0,0 +1,26 @@
+#ifndef INTERMEDIATE_USERS_H
+#define INTERMEDIATE_USERS_H
+
+#include "BaseRenderer.h"
+#include "Runtime/Utilities/LinkedList.h"
+
+class IntermediateRenderer;
+
+enum IntermediateNotify
+{
+ kImNotifyAssetDeleted,
+ kImNotifyBoundsChanged,
+};
+
+class IntermediateUsers
+{
+public:
+ void Notify(IntermediateNotify notify);
+ void AddUser(ListNode<IntermediateRenderer>& node) { m_IntermediateUsers.push_back(node); }
+
+protected:
+ typedef List< ListNode<IntermediateRenderer> > IntermediateRendererList;
+ IntermediateRendererList m_IntermediateUsers; // IntermediateRenderer users of this data
+};
+
+#endif
diff --git a/Runtime/Camera/LODGroup.cpp b/Runtime/Camera/LODGroup.cpp
new file mode 100644
index 0000000..c95b8e5
--- /dev/null
+++ b/Runtime/Camera/LODGroup.cpp
@@ -0,0 +1,395 @@
+#include "UnityPrefix.h"
+#include "LODGroup.h"
+#include "LODGroupManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "UnityScene.h"
+
+/*
+// @TODO:
+
+Ask aras:
+ RenderQueue.cpp
+ Create( "LIGHTMAP_OFF" );
+ Create( "LIGHTMAP_ON" );
+HUH???
+
+PRI 1
+ * Integrate with lightmaps? Probably need an option to reuse lightmap data if the uv's match up exactly between LOD's. Maybe we can automate it?
+ * multi_compile doesn't work. Seems like using more properties in a multicompile causes some shaders to drop.
+
+ ///@TODO: This should probably be 0. But for now we don't have proper ifdef support for switching to a different subshader.
+ #define LOD_FADE_DISABLED 0.999F
+
+ /////****** SHADER LOD FADING ****
+ ///@TODO: Make it so that fading is automatically disabled based on a shader tag or some shit like that.
+ ///@TODO: Expose the fade distance and visualize in inspector
+ ///@TODO: Add easy support for LOD fade in surface shaders
+ ///@TODO: Switch to a shader when it is not fading to reduce shader complexity
+
+PRI 2
+ ///@TODO: IntegrationTest: create lodgroup, attach renderer, delete lod group. Enable / disable renderer
+ ///@TODO: IntegrationTest: Make sure that m_LODs is never bigger than 8 (because of the lodIndex bitmask)
+
+ ///@TODO: GraphicsFunctionalTest / FunctionalTest: Write graphics functional test for LOD & layer based culling and projector especially when an object is being culled by the camera (Make sure it is also culled by projector)
+
+
+ ///@TODO: Does SceneManager really have to be recreated on every level load?
+ // This is probably related to the super weird behaviour of PlayerLoadLevel deactivate / activate ...
+
+
+
+PRI 3
+ * When calculating static objects pvs data. We could precalculate which static objects can be visible.
+ Have to be careful with runtime tweakable distance fudge...
+
+///@TODO: Use case:
+// "I assume a higher LOD can be triggered, for example, for an explosion effect,
+// swapping out the unbroken model for a broken one and passing the pieces to the physics engine with random velocities.."
+
+
+*/
+
+LODGroup::LODGroup (MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode)
+ , m_LODGroup (-1)
+{
+ m_Enabled = true;
+}
+
+LODGroup::~LODGroup ()
+{
+ Assert(m_LODGroup == kInvalidLODGroup);
+ Assert(m_CachedRenderers.empty());
+}
+
+void LODGroup::Reset ()
+{
+ Super::Reset();
+ m_LocalReferencePoint = Vector3f(0, 0, 0);
+ m_Size = 1.0F;
+ m_ScreenRelativeTransitionHeight = 0.0;
+ m_LODs.clear();
+}
+
+void LODGroup::SmartReset ()
+{
+ Super::SmartReset();
+
+ LODGroup::LOD lod;
+ lod.screenRelativeHeight = 0.6f;
+ m_LODs.push_back( lod );
+ lod.screenRelativeHeight = 0.3f;
+ m_LODs.push_back( lod );
+ lod.screenRelativeHeight = 0.1f;
+ m_LODs.push_back( lod );
+}
+
+void LODGroup::CheckConsistency ()
+{
+ Super::CheckConsistency();
+ m_LODs.resize(std::min<size_t>(m_LODs.size(), kMaximumLODLevels));
+}
+
+void LODGroup::AwakeFromLoad (AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad (mode);
+ UpdateEnabledState(IsActive());
+ SyncLODGroupManager();
+}
+
+void LODGroup::Deactivate (DeactivateOperation operation)
+{
+ UpdateEnabledState(false);
+ Super::Deactivate (operation);
+}
+
+void LODGroup::SetLocalReferencePoint (const Vector3f& ref)
+{
+ m_LocalReferencePoint = ref;
+ SyncLODGroupManager();
+ SetDirty();
+}
+
+void LODGroup::SetSize (float size)
+{
+ m_Size = size;
+ SyncLODGroupManager();
+ SetDirty();
+}
+
+void LODGroup::SyncLODGroupManager ()
+{
+ // Super inefficient...
+ if (m_LODGroup != kInvalidLODGroup)
+ {
+ Cleanup ();
+ Create ();
+ }
+}
+
+void LODGroup::NotifyLODGroupManagerIndexChange (int newIndex)
+{
+ m_LODGroup = newIndex;
+ for (int i=0;i<m_CachedRenderers.size();i++)
+ {
+ SceneHandle handle = m_CachedRenderers[i]->GetSceneHandle();
+ if (handle != kInvalidSceneHandle)
+ GetScene().SetRendererLODGroup(handle, newIndex);
+ }
+}
+
+bool DoesRendererSupportLODFade (Renderer& renderer)
+{
+ //@TODO:
+// MaterialArray& materials = renderer.GetMaterialArray();
+ return false;
+}
+
+// Goes through Renderers in the LODArray and sets up their LODGroup pointers & group indices and masks
+
+void LODGroup::RegisterCachedRenderers ()
+{
+ Assert(m_CachedRenderers.empty());
+ Assert(m_LODGroup != kInvalidLODGroup);
+
+ bool supportsLODFade = false;
+
+ Unity::Scene& scene = GetScene();
+
+ for (int i=0;i<m_LODs.size();i++)
+ {
+ LODRenderers& renderers = m_LODs[i].renderers;
+
+ for (int r=0;r<renderers.size();r++)
+ {
+ Renderer* renderer = renderers[r].renderer;
+ if (renderer == NULL)
+ continue;
+
+ supportsLODFade |= DoesRendererSupportLODFade (*renderer);
+
+ SceneHandle handle = renderer->GetSceneHandle();
+
+ // If the renderer has no LODGroup attached yet, then this is the first time that specific Renderer is used in this LODGroup.
+ // Thus we initialize the Group index & LODIndexMask with the current LOD Level
+ if (renderer->GetLODGroup () == NULL)
+ {
+ renderer->SetLODGroup (this);
+
+ // Initialize cull node lodgroup values
+ if (handle != kInvalidSceneHandle)
+ {
+ scene.SetRendererLODGroup(handle, m_LODGroup);
+ scene.SetRendererLODIndexMask(handle, 1 << i);
+ }
+ m_CachedRenderers.push_back(renderer);
+ }
+ // The renderer is attached to the same LOD group in a previous LOD level.
+ // Thus we add the current LOD level to the LODIndexMask
+ else if (renderer->GetLODGroup () == this)
+ {
+ if (handle != kInvalidSceneHandle)
+ {
+ UInt32 lodIndexMask = GetScene().GetRendererNode(handle).lodIndexMask;
+ lodIndexMask |= 1 << i;
+ scene.SetRendererLODIndexMask(handle, lodIndexMask);
+ }
+ }
+ // Fail (renderer is used in multiple LODGroups...)
+ else
+ {
+ string warningString = Format("Renderer '%s' is registered with more than one LODGroup ('%s' and '%s').", renderer->GetName(), GetName(), renderer->GetLODGroup ()->GetName());
+ WarningStringObject(warningString, renderer);
+ }
+ }
+ }
+}
+
+void LODGroup::ClearCachedRenderers ()
+{
+ for (int i=0;i<m_CachedRenderers.size();i++)
+ {
+ m_CachedRenderers[i]->SetLODGroup (NULL);
+ SceneHandle handle = m_CachedRenderers[i]->GetSceneHandle();
+ if (handle != kInvalidSceneHandle)
+ {
+ Unity::Scene& scene = GetScene();
+ scene.SetRendererLODGroup(handle, 0);
+ scene.SetRendererLODIndexMask(handle, 0);
+ }
+ }
+ m_CachedRenderers.resize_uninitialized(0);
+}
+
+void LODGroup::RemoveFromCachedRenderers (Renderer* renderer)
+{
+ for (int i=0;i<m_CachedRenderers.size();i++)
+ {
+ if (m_CachedRenderers[i] == renderer)
+ {
+ m_CachedRenderers[i] = m_CachedRenderers.back();
+ m_CachedRenderers.pop_back();
+ return;
+ }
+ }
+}
+
+void LODGroup::GetLODGroupIndexAndMask (Renderer* renderer, UInt32* outGroup, UInt32* outMask)
+{
+ Assert(m_LODGroup != kInvalidLODGroup);
+
+ PPtr<Renderer> rendererPPtr (renderer);
+
+ // Compute mask of which LOD
+ UInt32 mask = 0;
+ for (int i=0;i<m_LODs.size();i++)
+ {
+ LODRenderers& renderers = m_LODs[i].renderers;
+ for (int r=0;r<renderers.size();r++)
+ {
+ if (renderers[r].renderer == rendererPPtr)
+ mask |= 1 << i;
+ }
+ }
+
+ *outMask = mask;
+ *outGroup = m_LODGroup;
+}
+
+void LODGroup::SetLODArray (const LODArray& lodArray)
+{
+ m_LODs = lodArray;
+ SyncLODGroupManager();
+ SetDirty();
+}
+
+const LODGroup::LOD& LODGroup::GetLOD (int index)
+{
+ Assert (index < GetLODCount());
+ return m_LODs[index];
+}
+
+
+Vector3f LODGroup::GetWorldReferencePoint ()
+{
+ return GetComponent(Transform).TransformPoint(m_LocalReferencePoint);
+}
+
+float LODGroup::GetWorldSpaceScale ()
+{
+ Vector3f scale = GetComponent(Transform).GetWorldScaleLossy();
+ float largestAxis;
+ largestAxis = Abs(scale.x);
+ largestAxis = std::max (largestAxis, Abs(scale.y));
+ largestAxis = std::max (largestAxis, Abs(scale.z));
+ return largestAxis;
+}
+
+float LODGroup::GetWorldSpaceSize ()
+{
+ return GetWorldSpaceScale () * m_Size;
+}
+
+void LODGroup::UpdateEnabledState (bool active)
+{
+ Cleanup();
+ if (active)
+ Create();
+}
+
+void LODGroup::Create()
+{
+ if (m_Enabled)
+ {
+ GetLODGroupManager().AddLODGroup(*this, GetWorldReferencePoint(), GetWorldSpaceSize());
+ }
+ else
+ {
+ m_LODGroup = kDisabledLODGroup;
+ }
+
+ RegisterCachedRenderers();
+}
+
+
+bool LODGroup::GetEnabled()
+{
+ return m_Enabled;
+}
+
+void LODGroup::SetEnabled(bool enabled)
+{
+ if ((bool)m_Enabled == enabled)
+ return;
+ m_Enabled = enabled;
+ UpdateEnabledState (IsActive ());
+ SetDirty ();
+}
+
+void LODGroup::Cleanup ()
+{
+ if (m_LODGroup != kInvalidLODGroup)
+ {
+ ClearCachedRenderers();
+ if (m_LODGroup == kDisabledLODGroup)
+ m_LODGroup = kInvalidLODGroup;
+ else
+ GetLODGroupManager().RemoveLODGroup(*this);
+ }
+}
+
+void LODGroup::OnTransformChanged (int options)
+{
+ if (m_LODGroup != kInvalidLODGroup)
+ {
+ // Scale changed: update all parameters
+ if (options & Transform::kScaleChanged)
+ GetLODGroupManager().UpdateLODGroupParameters(m_LODGroup, *this, GetWorldReferencePoint(), GetWorldSpaceSize());
+ // rotation or position changed: fastpath for just changing the reference point
+ else
+ {
+ GetLODGroupManager().UpdateLODGroupPosition(m_LODGroup, GetWorldReferencePoint());
+ }
+ }
+}
+
+template<class TransferFunction> inline
+void LODGroup::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER (m_LocalReferencePoint);
+ TRANSFER (m_Size);
+ TRANSFER (m_ScreenRelativeTransitionHeight);
+ TRANSFER (m_LODs);
+ transfer.Transfer (m_Enabled, "m_Enabled", kHideInEditorMask);
+}
+
+template<class TransferFunction> inline
+void LODGroup::LODRenderer::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (renderer);
+}
+
+template<class TransferFunction> inline
+void LODGroup::LOD::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (screenRelativeHeight);
+ TRANSFER (renderers);
+}
+
+void LODGroup::InitializeClass ()
+{
+ REGISTER_MESSAGE (LODGroup, kTransformChanged, OnTransformChanged, int);
+
+ InitializeLODGroupManager();
+}
+
+void LODGroup::CleanupClass ()
+{
+ if (GetLODGroupManagerPtr())
+ CleanupLODGroupManager();
+}
+
+IMPLEMENT_CLASS_HAS_INIT(LODGroup)
+IMPLEMENT_OBJECT_SERIALIZE(LODGroup)
diff --git a/Runtime/Camera/LODGroup.h b/Runtime/Camera/LODGroup.h
new file mode 100644
index 0000000..5def8d1
--- /dev/null
+++ b/Runtime/Camera/LODGroup.h
@@ -0,0 +1,105 @@
+#pragma once
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Math/Vector3.h"
+
+class LODGroup : public Unity::Component
+{
+public:
+
+ struct LODRenderer
+ {
+ PPtr<Renderer> renderer;
+
+ DECLARE_SERIALIZE (LODRenderer)
+ };
+
+ typedef dynamic_array<LODRenderer> LODRenderers;
+ struct LOD
+ {
+ float screenRelativeHeight;
+ LODRenderers renderers;
+
+ LOD ()
+ : screenRelativeHeight (0.0F)
+ { }
+
+
+ DECLARE_SERIALIZE (LOD)
+ };
+ typedef std::vector<LOD> LODArray;
+
+ REGISTER_DERIVED_CLASS (LODGroup, Component)
+ DECLARE_OBJECT_SERIALIZE(LODGroup)
+
+ LODGroup (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~LODGroup (); declared-by-macro
+
+ virtual void Reset ();
+ virtual void SmartReset ();
+ virtual void CheckConsistency ();
+ virtual void AwakeFromLoad (AwakeFromLoadMode mode);
+ virtual void Deactivate (DeactivateOperation operation);
+
+ // Property get / set
+ Vector3f GetLocalReferencePoint () { return m_LocalReferencePoint; }
+ void SetLocalReferencePoint (const Vector3f& ref);
+ Vector3f GetWorldReferencePoint ();
+
+ float GetWorldSpaceSize ();
+
+
+ void UpdateEnabledState (bool active);
+ bool GetEnabled();
+ void SetEnabled(bool enabled);
+
+ float GetSize () const { return m_Size; }
+ void SetSize (float size);
+
+ int GetLODCount () const { return m_LODs.size(); }
+ const LOD& GetLOD (int index);
+ void SetLODArray (const LODArray& lodArray);
+ void GetLODArray (LODArray& lodArray) const { lodArray = m_LODs; }
+ int GetLODGroup () const { return m_LODGroup; }
+
+ // Interface for Renderer / Scene
+ void ClearCachedRenderers ();
+ void RegisterCachedRenderers ();
+ void RemoveFromCachedRenderers (Renderer* renderer);
+ void NotifyLODGroupManagerIndexChange (int newIndex);
+ void GetLODGroupIndexAndMask (Renderer* renderer, UInt32* outLODGroupIndex, UInt32* outActiveLODMask);
+
+ static void InitializeClass();
+ static void CleanupClass();
+
+ // Supported messages
+ void OnTransformChanged (int options);
+ float GetWorldSpaceScale ();
+
+private:
+ void Create();
+ void Cleanup();
+
+ void SyncLODGroupManager ();
+
+ Vector3f m_LocalReferencePoint;
+ float m_Size;
+ LODArray m_LODs;
+ int m_LODGroup;
+ bool m_Enabled;
+ float m_ScreenRelativeTransitionHeight;
+
+
+ typedef dynamic_array<Renderer*> CachedRenderers;
+ CachedRenderers m_CachedRenderers;
+
+ friend class LODGroupManager;
+};
+
+
+struct MonoLOD
+{
+ float screenRelativeTransitionHeight;
+ ScriptingArrayPtr renderers;
+}; \ No newline at end of file
diff --git a/Runtime/Camera/LODGroupManager.cpp b/Runtime/Camera/LODGroupManager.cpp
new file mode 100644
index 0000000..0463cf3
--- /dev/null
+++ b/Runtime/Camera/LODGroupManager.cpp
@@ -0,0 +1,462 @@
+#include "UnityPrefix.h"
+#include "LODGroupManager.h"
+#include "LODGroup.h"
+#include "CullingParameters.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Camera/RenderLoops/BuiltinShaderParamUtility.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+PROFILER_INFORMATION (gComputeLOD, "LOD.ComputeLOD", kProfilerRender)
+
+LODGroupManager* gLODGroupManager = NULL;
+
+LODGroupManager& GetLODGroupManager ()
+{
+ Assert(gLODGroupManager != NULL);
+ return *gLODGroupManager;
+}
+
+LODGroupManager* GetLODGroupManagerPtr ()
+{
+ return gLODGroupManager;
+}
+void CleanupLODGroupManager ()
+{
+ Assert(gLODGroupManager != NULL);
+ UNITY_DELETE (gLODGroupManager, kMemRenderer);
+}
+
+void InitializeLODGroupManager ()
+{
+ Assert(gLODGroupManager == NULL);
+ gLODGroupManager = UNITY_NEW_AS_ROOT(LODGroupManager(), kMemRenderer, "LODGroupManager", "");
+}
+
+
+LODGroupManager::LODGroupManager ()
+{
+ m_LODBias = 1.0F;
+ m_MaximumLOD = 0;
+
+ memset(&m_SelectionData.push_back(), 0, sizeof(LODSelectionData));
+}
+
+// The basic LOD distance check in orthomode:
+// * Pixel size check: if (array[i].pixelHeight < lodGroup.m_Size * 0.5F / parameters.orthoSize * parameters.cameraPixelHeight)
+// * Relative height check: if (array[i].relativeHeight < lodGroup.m_Size * 0.5F / parameters.orthoSize)
+
+// The basic LOD distance check in perspective:
+// ...
+
+// All LOD calculations are distance based "reference point in LOD group" to camera position.
+// - Rotating a camera never switches LOD
+// - Point based means it's very predictable behaviour that is easy to visualize accurately
+// - Fast to calculate
+// float distance = CalculateFOVDistanceFudge () * CaclulateLODDistance();
+
+
+float CalculateFOVHalfAngle (const CullingParameters& parameters)
+{
+ return tan(Deg2Rad (parameters.lodFieldOfView) * 0.5F);
+}
+
+enum { kScreenRelativeMetric = 0, kPixelMetric = 1, kMetricCount = 2 };
+
+void CalculateLODFudge (const CullingParameters& parameters, float* fudge)
+{
+ float screenRelativeMetric;
+ if (parameters.isOrthographic)
+ {
+ screenRelativeMetric = 2.0F * parameters.orthoSize;
+ }
+ else
+ {
+ // Half angle at 90 degrees is 1.0 (So we skip halfAngle / 1.0 calculation)
+ float halfAngle = CalculateFOVHalfAngle(parameters);
+ screenRelativeMetric = 2.0 * halfAngle;
+ }
+
+ fudge[kScreenRelativeMetric] = screenRelativeMetric;
+ fudge[kPixelMetric] = screenRelativeMetric / parameters.cameraPixelHeight;
+}
+
+float CalculateLODDistance (float relativeScreenHeight, float size)
+{
+ return size / relativeScreenHeight;
+}
+
+float DistanceToRelativeHeight (const CullingParameters& parameters, float distance, float size)
+{
+ if (parameters.isOrthographic)
+ {
+ return size * 0.5F / parameters.orthoSize;
+ }
+ else
+ {
+ float halfAngle = CalculateFOVHalfAngle(parameters);
+ return size * 0.5F / (distance * halfAngle);
+ }
+}
+
+
+void LODGroupManager::CalculatePerspectiveLODMask (const LODSelectionData& selection, const Vector3f& position, int maximumLOD, int currentMask, const float* fieldOfViewFudge, UInt8* output, float* fade)
+{
+ if (selection.forceLODLevelMask != 0)
+ {
+ *output = selection.forceLODLevelMask;
+ *fade = 1.0F;
+ return;
+ }
+
+ Vector3f offset = selection.worldReferencePoint - position;
+
+ float sqrDistance = SqrMagnitude(offset);
+ sqrDistance *= fieldOfViewFudge[kScreenRelativeMetric] * fieldOfViewFudge[kScreenRelativeMetric];
+
+ // Early out if the object is getting culled because it is too far away.
+ *output = 0;
+ *fade = 0.0F;
+
+ // Must use the same metric for everything.... Otherwise this will fail
+ if (sqrDistance > selection.maxDistanceSqr)
+ return;
+
+ int maxDistancesCount = selection.maxDistancesCount;
+ const float* distances = selection.maxDistances;
+
+ bool supportsFade = selection.fadeDistance != 0.0F;
+
+ for (int i=maximumLOD;i<maxDistancesCount;i++)
+ {
+ // Is camera closer than maximum LOD distance?
+ float lodMaxDistance = distances[i];
+ float lodMaxDistanceSqr = lodMaxDistance * lodMaxDistance;
+
+ if (sqrDistance < lodMaxDistanceSqr)
+ {
+ // @TODO: This if could be optimized out of the inner loop
+ if (supportsFade)
+ {
+ // Is the next LOD in the transition range?
+ float dif = lodMaxDistance - sqrt(sqrDistance);
+ if (dif < selection.fadeDistance)
+ {
+ currentMask |= currentMask << 1;
+ *output = currentMask;
+ *fade = dif / selection.fadeDistance;
+ }
+ else
+ {
+ ///@TODO: this should be zero, because when you are not fading it shouldn't use a shader that does fading.
+ *output = currentMask;
+ *fade = 1.0F;
+ }
+ }
+ else
+ {
+ ///@TODO: this should be zero, because when you are not fading it shouldn't use a shader that does fading.
+ *output = currentMask;
+ *fade = 1.0F;
+ }
+
+ return;
+ }
+
+ currentMask <<= 1;
+ }
+}
+
+void LODGroupManager::CalculateOrthoLODMask (const LODSelectionData& selection, int maximumLOD, int currentMask, const float* fudge, UInt8* output, float* fade)
+{
+ if (selection.forceLODLevelMask != 0)
+ {
+ *output = selection.forceLODLevelMask;
+ *fade = 1.0F;
+ return;
+ }
+
+ ///@TODO: DO IT
+ *output = 0;
+ *fade = 0.0F;
+
+ int maxDistancesCount = selection.maxDistancesCount;
+ const float* distances = selection.maxDistances;
+
+ float distance = fudge[kScreenRelativeMetric];
+
+ for (int i=maximumLOD;i<maxDistancesCount;i++)
+ {
+ if (distance < distances[i])
+ {
+ // Is the next LOD in the transition range?
+ float dif = distances[i] - distance;
+ if (dif < selection.fadeDistance)
+ {
+ currentMask |= currentMask << 1;
+ *output = currentMask;
+ *fade = dif / selection.fadeDistance;
+ }
+ else
+ {
+ ///@TODO: this should be zero, because when you are not fading it shouldn't use a shader that does fading.
+ *output = currentMask;
+ *fade = 1.0F;
+ }
+
+ return;
+ }
+
+ currentMask <<= 1;
+ }
+}
+
+void LODGroupManager::CalculateLODMasks (const CullingParameters& parameters, UInt8* outMasks, float* outFades)
+{
+ PROFILER_AUTO(gComputeLOD, NULL)
+
+ // Get field of view / pixel fudge values and sqr it so the inner loop doesn't have to do it.
+ float fieldOfViewFudge[kMetricCount];
+ CalculateLODFudge (parameters, fieldOfViewFudge);
+ for (int i=0;i<kMetricCount;i++)
+ fieldOfViewFudge[i] = fieldOfViewFudge[i] / m_LODBias;
+
+ int lodGroupCount = m_SelectionData.size();
+ DebugAssert(lodGroupCount > 0);
+ outMasks[0] = 0;
+ outFades[0] = 0;
+ int baseMask = 1 << m_MaximumLOD;
+ if (parameters.isOrthographic)
+ {
+ for (int i=1;i<lodGroupCount;i++)
+ CalculateOrthoLODMask(m_SelectionData[i], m_MaximumLOD, baseMask, fieldOfViewFudge, &outMasks[i], &outFades[i]);
+ }
+ else
+ {
+ for (int i=1;i<lodGroupCount;i++)
+ CalculatePerspectiveLODMask(m_SelectionData[i], parameters.lodPosition, m_MaximumLOD, baseMask, fieldOfViewFudge, &outMasks[i], &outFades[i]);
+ }
+}
+
+inline UInt32 LowestBit2Consecutive8Bit (UInt32 v)
+{
+ Assert(v == (v & 0xff));
+ UInt32 extra = (v & (v - 1)) == 0;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v++;
+
+ UInt32 table[] = { v >> 2, v >> 1 };
+ return table[extra];
+}
+
+// The shader isn't using clamp on the z value of the 3D dither texture.
+// Thus we have to ensure that it never actually becomes 1.0
+inline float ClampForGPURepeat (float fade)
+{
+ return clamp(fade, 0.0F, LOD_FADE_DISABLED);
+}
+
+float LODGroupManager::CalculateLODFade (UInt32 lodGroupIndex, UInt32 rendererActiveLODMask, const UInt8* lodMasks, const float* lodFades)
+{
+ if (rendererActiveLODMask == 0)
+ return LOD_FADE_DISABLED;
+
+ // rendererActiveLODMask:
+ // The mask of all LOD levels that this renderer participates in.
+
+ // The mask of all active LOD levels for this group.
+ // Eg. LOD 0 and LOD1 enabled -> 1 | 2
+ UInt8 activeMaskOfLODGroup = lodMasks[lodGroupIndex];
+
+ // If renderer is part of all active LOD groups, then it should be completely visible.
+ if ((rendererActiveLODMask & activeMaskOfLODGroup) == activeMaskOfLODGroup)
+ return LOD_FADE_DISABLED;
+
+ // If the renderer is part of the lowest bit then it is part of the highest LOD (duh!)
+ bool isPartOfHighLOD = LowestBit2Consecutive8Bit (activeMaskOfLODGroup) & rendererActiveLODMask;
+
+ // The highest LOD Level uses the computed fade value
+ if (isPartOfHighLOD)
+ {
+ return ClampForGPURepeat(lodFades[lodGroupIndex]);
+ }
+ // The lower lod level uses the inverse
+ else
+ {
+ return ClampForGPURepeat(1.0F - lodFades[lodGroupIndex]);
+ }
+}
+
+void LODGroupManager::ClearAllForceLODMask ()
+{
+ for (int i=0;i<m_SelectionData.size();i++)
+ m_SelectionData[i].forceLODLevelMask = 0;
+
+}
+
+#if UNITY_EDITOR
+static void AddRenderersToVisualizationStats (const LODGroup::LODRenderers& renderers, LODVisualizationInformation& information)
+{
+ // Calculate triangle & vertex & mesh count for the renderers in this LOD
+ for (int i=0;i<renderers.size();i++)
+ {
+ Renderer* renderer = renderers[i].renderer;
+ if (renderer)
+ {
+ RenderStats stats;
+ renderer->GetRenderStats (stats);
+
+ information.triangleCount += stats.triangleCount;
+ information.vertexCount += stats.vertexCount;
+ information.rendererCount += 1;
+ information.submeshCount += stats.submeshCount;
+ }
+ }
+}
+
+LODVisualizationInformation LODGroupManager::CalculateVisualizationData (const CullingParameters& cullingParameters, LODGroup& lodGroup, int lodLevel)
+{
+ LODVisualizationInformation information;
+ memset(&information, 0, sizeof(information));
+ information.activeLODLevel = kInvalidLODGroup;
+
+ // Calculate switch distance
+ float fudge[kMetricCount];
+ CalculateLODFudge (cullingParameters, fudge);
+ for (int i=0;i<kMetricCount;i++)
+ fudge[i] = fudge[i] / m_LODBias;
+
+ float sqrFudge[kMetricCount];
+ for (int i=0;i<kMetricCount;i++)
+ sqrFudge[i] = fudge[i];
+ float lodFade = 0.0F;
+
+ if (lodGroup.m_LODGroup != kInvalidLODGroup && lodGroup.m_LODGroup != kDisabledLODGroup)
+ {
+ UInt8 activeLODMask;
+ if (cullingParameters.isOrthographic)
+ CalculateOrthoLODMask (m_SelectionData[lodGroup.m_LODGroup], m_MaximumLOD, 1 << m_MaximumLOD, sqrFudge, &activeLODMask, &lodFade);
+ else
+ CalculatePerspectiveLODMask (m_SelectionData[lodGroup.m_LODGroup], cullingParameters.lodPosition, m_MaximumLOD, 1 << m_MaximumLOD, sqrFudge, &activeLODMask, &lodFade);
+
+ if (activeLODMask != 0)
+ information.activeLODLevel = LowestBit(activeLODMask);
+ else
+ information.activeLODLevel = -1;
+ }
+
+ if (lodLevel == -1)
+ lodLevel = information.activeLODLevel;
+
+ // Calculate current distances & bounding volume sizes
+ information.activeDistance = Magnitude(lodGroup.GetWorldReferencePoint() - cullingParameters.lodPosition);
+ information.activeRelativeScreenSize = DistanceToRelativeHeight(cullingParameters, information.activeDistance, lodGroup.GetWorldSpaceSize()) * m_LODBias;
+ information.activePixelSize = information.activeRelativeScreenSize * cullingParameters.cameraPixelHeight;
+ information.activeWorldSpaceSize = lodGroup.GetWorldSpaceSize ();
+ information.activeLODFade = lodFade;
+
+ if (lodLevel != -1)
+ {
+ // Calculate switch distance for the lod
+ const LODGroup::LOD& lod = lodGroup.m_LODs[lodLevel];
+
+ // Calculate triangle & vertex & mesh count for the renderers in this LOD
+ AddRenderersToVisualizationStats(lod.renderers, information);
+ }
+
+ return information;
+}
+#endif //UNITY_EDITOR
+
+void LODGroupManager::AddLODGroup (LODGroup& group, const Vector3f& position, float worldSpaceSize)
+{
+ // Add Group
+ int index = m_SelectionData.size();
+ m_SelectionData.push_back();
+ group.m_LODGroup = index;
+
+ // Initialize parameters
+ UpdateLODGroupParameters(index, group, position, worldSpaceSize);
+
+ m_SelectionData.back().forceLODLevelMask = 0;
+}
+
+void LODGroupManager::UpdateLODGroupParameters (int index, LODGroup& group, const Vector3f& position, float worldSpaceSize)
+{
+ LODSelectionData& data = m_SelectionData[index];
+
+ data.worldReferencePoint = position;
+ data.lodGroup = &group;
+ data.maxDistancesCount = group.m_LODs.size();
+ data.maxDistanceSqr = 0.0F;
+
+ Assert(group.m_LODs.size() <= kMaximumLODLevels);
+
+ float totalMaxDistance = 0.0F;
+
+ if (!GetBuildSettings ().hasAdvancedVersion)
+ {
+ data.maxDistancesCount = std::max (1, data.maxDistancesCount);
+ totalMaxDistance = data.maxDistances[0] = CalculateLODDistance(0.0001F, worldSpaceSize);
+ }
+ else
+ {
+
+ for (int i=0;i<group.m_LODs.size();i++)
+ {
+ float maxDistance = CalculateLODDistance(group.m_LODs[i].screenRelativeHeight, worldSpaceSize);
+
+ totalMaxDistance = std::max(maxDistance, totalMaxDistance);
+ data.maxDistances[i] = maxDistance;
+ }
+ }
+
+ data.maxDistanceSqr = totalMaxDistance * totalMaxDistance;
+
+ bool useLODFade = group.m_ScreenRelativeTransitionHeight > 0.00001F && !group.m_LODs.empty();
+ if (useLODFade)
+ {
+ float baseRelativeHeight = group.m_LODs.front().screenRelativeHeight;
+ data.fadeDistance = CalculateLODDistance (baseRelativeHeight - group.m_ScreenRelativeTransitionHeight, worldSpaceSize) - CalculateLODDistance (baseRelativeHeight, worldSpaceSize);
+ }
+ else
+ data.fadeDistance = 0.0F;
+}
+
+void LODGroupManager::RemoveLODGroup (LODGroup& group)
+{
+ // Remove from array by replacing with the last element.
+ Assert(group.m_CachedRenderers.empty());
+
+ int index = group.m_LODGroup;
+ Assert(index != 0);
+
+ // Update LODGroup index of the LODGroup we moved from the back
+ m_SelectionData.back().lodGroup->NotifyLODGroupManagerIndexChange(index);
+
+ m_SelectionData[index] = m_SelectionData.back();
+ m_SelectionData.pop_back();
+
+ group.m_LODGroup = kInvalidLODGroup;
+}
+
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (LODGroupManagerTests)
+{
+TEST (LODGroupManagerTests_PrevPowerOfTwoUInt8)
+{
+ for (int i=0;i<7;i++)
+ {
+ CHECK_EQUAL (1 << i, LowestBit2Consecutive8Bit(1 << i));
+ CHECK_EQUAL (1 << i, LowestBit2Consecutive8Bit((1 << i) | (1 << (i+1))));
+ }
+}
+}
+
+#endif
diff --git a/Runtime/Camera/LODGroupManager.h b/Runtime/Camera/LODGroupManager.h
new file mode 100644
index 0000000..7623b63
--- /dev/null
+++ b/Runtime/Camera/LODGroupManager.h
@@ -0,0 +1,116 @@
+#pragma once
+
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/BitUtility.h"
+
+class LODGroup;
+struct CullingParameters;
+
+enum
+{
+ kMaximumLODLevels = 8,
+ kInvalidLODGroup = -1,
+
+ // When an LOD group is disabled, we want to make all renderers be disabled.
+ // For this purpose we have a single shared LODGroup index, with a mask that has all renderers always disabled.
+ kDisabledLODGroup = 0
+};
+
+#if UNITY_EDITOR
+// LOD Visualization
+// (NOTE: Keep LODUtilityBindings.txt struct in sync)
+struct LODVisualizationInformation
+{
+ int triangleCount;
+ int vertexCount;
+ int rendererCount;
+ int submeshCount;
+
+ int activeLODLevel;
+ float activeLODFade;
+ float activeDistance;
+ float activeRelativeScreenSize;
+ float activePixelSize;
+ float activeWorldSpaceSize;
+};
+#endif
+
+class LODGroupManager
+{
+ struct LODSelectionData
+ {
+ // The point we measure the LOD distance against
+ Vector3f worldReferencePoint;
+ float maxDistanceSqr;
+
+ // LOD maximum distance values
+ float maxDistances[kMaximumLODLevels];
+ int maxDistancesCount;
+ float fadeDistance;
+
+ // The associated lod group
+ LODGroup* lodGroup;
+
+ UInt32 forceLODLevelMask;
+ };
+
+ dynamic_array<LODSelectionData> m_SelectionData;
+ float m_LODBias;
+ UInt32 m_MaximumLOD;
+
+public:
+
+ LODGroupManager ();
+
+ void AddLODGroup (LODGroup& group, const Vector3f& position, float worldSpaceSize);
+ void RemoveLODGroup (LODGroup& group);
+
+ void UpdateLODGroupParameters (int index, LODGroup& group, const Vector3f& position, float worldSpaceSize);
+ void UpdateLODGroupPosition (int index, const Vector3f& position) { m_SelectionData[index].worldReferencePoint = position; }
+
+ void SetLODBias (float b) { m_LODBias = b; }
+ float GetLODBias () const { return m_LODBias; }
+
+ void SetMaximumLODLevel (UInt32 b) { m_MaximumLOD = b; }
+ UInt32 GetMaximumLODLevel () const { return m_MaximumLOD; }
+
+ void ResetLODBias () { SetLODBias(1.0F); }
+
+ int GetLODGroupCount () const { return m_SelectionData.size(); }
+
+
+ // Used by scene culling to determine
+ // /lodGroupIndex/ is the index of LODGroup into m_SelectionData & m_ActiveLOD
+ // /activeLODMask/ is the LOD mask of the renderer.
+ // m_ActiveLOD[lodGroupIndex].activeMask is a bitmask specifying which LOD levels should be rendered.
+ // When cross-fading between two LOD's multiple LOD's might be active in the same LODGroup
+ static bool IsLODVisible (UInt32 lodGroupIndex, UInt32 activeLODMask, const UInt8* activeLOD)
+ {
+ if (activeLODMask == 0)
+ return true;
+
+ return activeLOD[lodGroupIndex] & activeLODMask;
+ }
+
+ void CalculateLODMasks (const CullingParameters& parameters, UInt8* outMasks, float* outFades);
+ static float CalculateLODFade (UInt32 lodGroupIndex, UInt32 rendererActiveLODMask, const UInt8* lodMasks, const float* lodFades);
+
+ static void CalculatePerspectiveLODMask (const LODSelectionData& selection, const Vector3f& position, int maximumLOD, int currentMask, const float* fieldOfViewFudge, UInt8* output, float* outputFade);
+ static void CalculateOrthoLODMask (const LODSelectionData& selection, int maximumLOD, int currentMask, const float* fudge, UInt8* output, float* outputFade);
+
+#if UNITY_EDITOR
+ LODVisualizationInformation CalculateVisualizationData (const CullingParameters& cullingParameters, LODGroup& lodGroup, int lod);
+
+#endif
+
+ void SetForceLODMask (int index, UInt32 forceEditorLODMask) { m_SelectionData[index].forceLODLevelMask = forceEditorLODMask; }
+ UInt32 GetForceLODMask (int index) { return m_SelectionData[index].forceLODLevelMask; }
+ void ClearAllForceLODMask ();
+
+};
+
+LODGroupManager* GetLODGroupManagerPtr ();
+LODGroupManager& GetLODGroupManager ();
+void CleanupLODGroupManager ();
+void InitializeLODGroupManager ();
diff --git a/Runtime/Camera/Light.cpp b/Runtime/Camera/Light.cpp
new file mode 100644
index 0000000..a17c209
--- /dev/null
+++ b/Runtime/Camera/Light.cpp
@@ -0,0 +1,668 @@
+#include "UnityPrefix.h"
+#include "Light.h"
+#include "Shadows.h"
+#include "RenderSettings.h"
+#include "HaloManager.h"
+#include "Runtime/BaseClasses/Tags.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Graphics/CubemapTexture.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Graphics/LightmapSettings.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Shaders/ShaderKeywords.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Misc/QualitySettings.h"
+#include "Runtime/Camera/LightManager.h"
+#if UNITY_EDITOR
+#include "Runtime/Misc/BuildSettings.h"
+#endif
+
+
+
+using namespace Unity;
+
+
+static SHADERPROP (LightTexture0);
+
+const UInt64 kAllLightKeywordsMask = 0x1F;
+
+
+// constants for opengl attenuation
+static const float kConstantFac = 1.000f;
+static const float kQuadraticFac = 25.0f;
+// where the falloff down to zero should start
+static const float kToZeroFadeStart = 0.8f * 0.8f;
+
+float Light::CalcQuadFac (float range)
+{
+ return kQuadraticFac / (range * range);
+}
+
+float Light::AttenuateNormalized(float distSqr)
+{
+ // match the vertex lighting falloff
+ float atten = 1 / (kConstantFac + CalcQuadFac (1.0f) * distSqr);
+
+ // ...but vertex one does not falloff to zero at light's range; it falls off to 1/26 which
+ // is then doubled by our shaders, resulting in 19/255 difference!
+ // So force it to falloff to zero at the edges.
+ if( distSqr >= kToZeroFadeStart )
+ {
+ if( distSqr > 1 )
+ atten = 0;
+ else
+ atten *= 1 - (distSqr - kToZeroFadeStart) / (1 - kToZeroFadeStart);
+ }
+
+ return atten;
+}
+
+Light::Light(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_GfxLightValid(false)
+, m_ActuallyLightmapped(false)
+{
+ m_KeywordMode = kLightKeywordDirectional;
+ m_HaloHandle = 0;
+ m_FlareHandle = -1;
+ m_World2Local = Matrix4x4f::identity;
+ m_WorldPosition = Vector3f::zero;
+}
+
+Light::~Light ()
+{
+}
+
+void Light::InitializeClass () {
+ REGISTER_MESSAGE_VOID (Light, kTransformChanged, TransformChanged);
+}
+
+void Light::CleanupClass ()
+{
+}
+
+float Light::AttenuateApprox (float sqrDist) const
+{
+ return 1.0f / (kConstantFac + CalcQuadFac(m_Range) * sqrDist);
+}
+
+
+void Light::TransformChanged ()
+{
+ if (IsAddedToManager ())
+ {
+ const Transform& transform = GetComponent(Transform);
+ m_World2Local = transform.GetWorldToLocalMatrixNoScale ();
+ m_WorldPosition = transform.GetPosition ();
+ Precalc ();
+ }
+ m_GfxLightValid = false;
+}
+
+Light::Lightmapping Light::GetLightmappingForBake() const
+{
+ if (m_Type == kLightArea)
+ return kLightmappingBakedOnly;
+
+ return static_cast<Lightmapping>(m_Lightmapping);
+}
+
+void Light::Reset ()
+{
+ Super::Reset();
+ m_Shadows.Reset();
+
+ m_Color = ColorRGBAf (1,1,1,1);
+ m_Intensity = 1.0f;
+ m_Range = 10.0f;
+ m_SpotAngle = 30.0f;
+ m_CookieSize = 10.0f;
+ m_Lightmapping = kLightmappingAuto;
+ UpdateSpotAngleValues ();
+ m_RenderMode = kRenderAuto;
+ m_DrawHalo = false;
+ m_Type = kLightPoint;
+ m_CullingMask.m_Bits = -1;
+ #if UNITY_EDITOR
+ m_ShadowSamples = 1;
+ m_ShadowRadius = 0.0f;
+ m_ShadowAngle = 0.0f;
+ m_IndirectIntensity = 1.0f;
+ m_AreaSize.Set (1,1);
+ #endif
+}
+
+void Light::CheckConsistency ()
+{
+ Texture *cookie = m_Cookie;
+ // If this is a point light and cookie is not a cubemap, remove the cookie
+ if( m_Type == kLightPoint && cookie && cookie->GetClassID () != ClassID (Cubemap) )
+ {
+ m_Cookie = NULL;
+ cookie = NULL;
+ }
+
+ // If this is not a point light and cookie is a cubemap, remove the cookie
+ if( m_Type != kLightPoint && cookie && cookie->GetClassID () == ClassID (Cubemap) )
+ {
+ m_Cookie = NULL;
+ cookie = NULL;
+ }
+
+ // I think this is to get cookie-to-cubemap working on Radeon 7000 path. Enforcing cookies
+ // to be square makes constructing cubemap much easier.
+ if( m_Type == kLightSpot && cookie && cookie->GetDataHeight() != cookie->GetDataWidth() )
+ {
+ ErrorStringObject ("Spotlight cookies must be square (width and height must be equal)", this);
+ m_Cookie = 0;
+ }
+
+ m_Range = std::max (m_Range, 0.0f);
+ m_SpotAngle = std::min (m_SpotAngle, 179.0f);
+ m_SpotAngle = std::max (m_SpotAngle, 1.0f);
+ m_CookieSize = std::max (m_CookieSize, 0.0f);
+ m_Shadows.m_Bias = clamp (m_Shadows.m_Bias, 0.0f, 10.0f);
+ m_Shadows.m_Softness = clamp (m_Shadows.m_Softness, 1.0f, 8.0f);
+ m_Shadows.m_SoftnessFade = clamp (m_Shadows.m_SoftnessFade, 0.1f, 5.0f);
+ #if UNITY_EDITOR
+ m_ShadowSamples = std::max<int> (m_ShadowSamples, 1);
+ m_IndirectIntensity = std::max (m_IndirectIntensity, 0.0f);
+ m_AreaSize.x = std::max(m_AreaSize.x, 0.0f);
+ m_AreaSize.y = std::max(m_AreaSize.y, 0.0f);
+ #endif
+}
+
+void Light::AddToManager ()
+{
+ DebugAssert (!IsInList());
+ const Transform& transform = GetComponent(Transform);
+ m_World2Local = transform.GetWorldToLocalMatrixNoScale ();
+ m_WorldPosition = transform.GetPosition ();
+ GetLightManager().AddLight(this);
+
+ SetupHalo ();
+ SetupFlare ();
+}
+
+void Light::RemoveFromManager ()
+{
+ if (IsInList())
+ {
+ GetLightManager().RemoveLight (this);
+ }
+ if (m_HaloHandle) {
+ GetHaloManager().DeleteHalo (m_HaloHandle);
+ m_HaloHandle = 0;
+ }
+ if (m_FlareHandle != -1)
+ {
+ GetFlareManager ().DeleteFlare (m_FlareHandle);
+ m_FlareHandle = -1;
+ }
+}
+
+void Light::Precalc ()
+{
+ // setup the light cookie/attenuation textures
+ Texture *cookie = m_Cookie;
+ switch (m_Type)
+ {
+ case kLightSpot:
+ if (!cookie)
+ cookie = GetRenderSettings().GetDefaultSpotCookie();
+
+ m_AttenuationTexture = cookie;
+ m_AttenuationMode = kSpotCookie;
+ m_KeywordMode = kLightKeywordSpot;
+ break;
+
+ case kLightPoint:
+ if (cookie) {
+ m_AttenuationTexture = cookie;
+ m_AttenuationMode = kPointFalloff;
+ m_KeywordMode = kLightKeywordPointCookie;
+ } else {
+ m_AttenuationTexture = builtintex::GetAttenuationTexture();
+ m_AttenuationMode = kPointFalloff;
+ m_KeywordMode = kLightKeywordPoint;
+ }
+ break;
+
+ case kLightDirectional:
+ if (cookie)
+ {
+ m_AttenuationTexture = cookie;
+ m_AttenuationMode = kDirectionalCookie;
+ m_KeywordMode = kLightKeywordDirectionalCookie;
+ }
+ else
+ {
+ m_AttenuationTexture = NULL;
+ m_AttenuationMode = kUnused;
+ m_KeywordMode = kLightKeywordDirectional;
+ }
+ break;
+ }
+
+ m_ConvertedFinalColor = GammaToActiveColorSpace (m_Color) * m_Intensity;
+
+ UpdateSpotAngleValues ();
+
+ SetupHalo();
+ SetupFlare();
+}
+
+
+
+
+void Light::SetPropsToShaderLab (float blend) const
+{
+ BuiltinShaderParamValues& params = GetGfxDevice().GetBuiltinParamValues();
+
+ params.SetVectorParam(kShaderVecLightColor0, Vector4f((m_ConvertedFinalColor * blend).GetPtr()));
+ Texture* attenTex = m_AttenuationTexture;
+ if (attenTex)
+ {
+ ShaderLab::PropertySheet* probs = ShaderLab::g_GlobalProperties;
+ probs->SetTexture (kSLPropLightTexture0, attenTex);
+ }
+}
+
+void Light::SetLightKeyword()
+{
+ UInt64 mask = g_ShaderKeywords.GetMask();
+ mask &= ~kAllLightKeywordsMask;
+ mask |= 1ULL << m_KeywordMode;
+ g_ShaderKeywords.SetMask (mask);
+}
+
+void Light::GetMatrix (const Matrix4x4f* __restrict object2light, Matrix4x4f* __restrict outMatrix) const
+{
+ Matrix4x4f temp1, temp2, temp3;
+ float scale;
+
+ switch (m_AttenuationMode) {
+ case kSpotCookie:
+ // we want out.w = 2.0 * in.z / m_CotanHalfSpotAngle
+ // c = m_CotanHalfSpotAngle
+ // 1 0 0 0
+ // 0 1 0 0
+ // 0 0 1 0
+ // 0 0 2/c 0
+ // the "2" will be used to scale .xy for the cookie as in .xy/2 + 0.5
+ temp3.SetIdentity();
+ temp3.Get(3,2) = 2.0f / m_CotanHalfSpotAngle;
+ temp3.Get(3,3) = 0;
+
+ scale = 1.0f / m_Range;
+ temp1.SetScale (Vector3f(scale,scale,scale));
+
+ // temp3 * temp1 * object2Light
+ MultiplyMatrices4x4 (&temp3, &temp1, &temp2);
+ MultiplyMatrices4x4 (&temp2, object2light, outMatrix);
+ break;
+ case kPointFalloff:
+ scale = 1.0f / m_Range;
+ temp1.SetScale (Vector3f(scale,scale,scale));
+ MultiplyMatrices4x4 (&temp1, object2light, outMatrix);
+ break;
+ case kDirectionalCookie:
+ scale = 1.0f / m_CookieSize;
+ temp1.SetScale (Vector3f (scale, scale, 0));
+ temp2.SetTranslate (Vector3f (.5f, .5f, 0));
+ // temp2 * temp1 * object2Light
+ MultiplyMatrices4x4 (&temp2, &temp1, &temp3);
+ MultiplyMatrices4x4 (&temp3, object2light, outMatrix);
+ break;
+ case kUnused:
+ break;
+ }
+}
+
+
+
+void Light::ComputeGfxLight (GfxVertexLight& gfxLight) const
+{
+ gfxLight.type = static_cast<LightType>(m_Type);
+
+ const Transform& tr = GetComponent(Transform);
+ switch( m_Type ) {
+ case kLightPoint:
+ {
+ Vector3f lightPos = tr.GetPosition();
+ gfxLight.position.Set( lightPos.x, lightPos.y, lightPos.z, 1.0f );
+ gfxLight.spotAngle = -1.0f;
+ gfxLight.quadAtten = CalcQuadFac(m_Range);
+ gfxLight.spotDirection.Set( 1.0f, 0.0f, 0.0f, 0.0f );
+ }
+ break;
+ case kLightDirectional:
+ {
+ Vector3f lightDir = tr.TransformDirection( Vector3f (0,0,1) );
+ gfxLight.position.Set( lightDir.x, lightDir.y, lightDir.z, 0.0f );
+ gfxLight.quadAtten = 0.0f;
+ gfxLight.spotAngle = -1.0f;
+ gfxLight.spotDirection.Set( 1.0f, 0.0f, 0.0f, 0.0f );
+ }
+ break;
+ case kLightSpot:
+ {
+ Vector3f lightPos = tr.GetPosition();
+ gfxLight.position.Set( lightPos.x, lightPos.y, lightPos.z, 1.0f );
+ Vector3f lightDir = tr.TransformDirection (Vector3f (0,0,1));
+ gfxLight.spotDirection.Set( lightDir.x, lightDir.y, lightDir.z, 0.0f );
+ gfxLight.spotAngle = m_SpotAngle;
+ gfxLight.quadAtten = CalcQuadFac(m_Range);
+ }
+ break;
+ case kLightArea:
+ break;
+ default:
+ ErrorStringObject( "Unsupported light type", this );
+ }
+
+ // Light color & range
+ gfxLight.color.Set( m_ConvertedFinalColor.GetPtr() );
+ gfxLight.range = m_Range;
+}
+
+void Light::SetupVertexLight (int lightNo, float visibilityFade)
+{
+ if (!m_GfxLightValid)
+ {
+ ComputeGfxLight (m_CachedGfxLight);
+ m_GfxLightValid = true;
+ }
+ GfxDevice& device = GetGfxDevice();
+
+ const ColorRGBAf color = GetConvertedFinalColor();
+ Vector4f fadedColor;
+ fadedColor.x = color.r * visibilityFade;
+ fadedColor.y = color.g * visibilityFade;
+ fadedColor.z = color.b * visibilityFade;
+
+ m_CachedGfxLight.color = fadedColor;
+ device.SetLight (lightNo, m_CachedGfxLight);
+}
+
+
+void Light::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ if ((awakeMode & kDidLoadFromDisk) == 0 && GetEnabled () && IsActive ())
+ {
+ const Transform& transform = GetComponent(Transform);
+ m_World2Local = transform.GetWorldToLocalMatrixNoScale ();
+ m_WorldPosition = transform.GetPosition ();
+ SetupHalo ();
+ SetupFlare ();
+ }
+ m_GfxLightValid = false;
+ Precalc ();
+}
+
+void Light::SetFlare (Flare *flare)
+{
+ if (m_Flare == PPtr<Flare> (flare))
+ return;
+ m_Flare = flare;
+ if (GetEnabled () && IsActive ())
+ SetupFlare();
+}
+
+void Light::SetType (LightType type)
+{
+ m_Type = type;
+ SetDirty(); m_GfxLightValid = false; Precalc();
+}
+
+void Light::SetColor (const ColorRGBAf& c)
+{
+ m_Color = c;
+ SetDirty(); m_GfxLightValid = false; Precalc();
+}
+
+void Light::SetIntensity( float i )
+{
+ m_Intensity = clamp(i, 0.0f, 8.0f);
+ SetDirty(); m_GfxLightValid = false; Precalc();
+}
+
+int Light::GetFinalShadowResolution() const
+{
+ int lightShadowResolution = GetShadowResolution();
+ if (lightShadowResolution == -1) // use global resolution?
+ {
+ const QualitySettings::QualitySetting& quality = GetQualitySettings().GetCurrent();
+ lightShadowResolution = quality.shadowResolution;;
+ }
+ return lightShadowResolution;
+}
+
+void Light::SetShadows( int v )
+{
+ m_Shadows.m_Type = v;
+ SetDirty();
+}
+
+void Light::SetActuallyLightmapped (bool v)
+{
+ if (m_ActuallyLightmapped != v)
+ {
+ m_ActuallyLightmapped = v;
+ SetDirty();
+ m_GfxLightValid = false;
+ }
+}
+
+void Light::SetCookie (Texture *tex)
+{
+ if (m_Cookie == PPtr<Texture> (tex))
+ return;
+
+ m_Cookie = tex;
+ SetDirty();
+ CheckConsistency ();
+ Precalc ();
+}
+
+template<class TransferFunc>
+void Light::Transfer (TransferFunc& transfer) {
+ Super::Transfer (transfer);
+ transfer.SetVersion(3);
+
+ TRANSFER_SIMPLE (m_Type);
+ TRANSFER_SIMPLE (m_Color);
+ TRANSFER (m_Intensity);
+ TRANSFER_SIMPLE (m_Range);
+ TRANSFER_SIMPLE (m_SpotAngle);
+ if (transfer.IsVersionSmallerOrEqual(2))
+ {
+ m_CookieSize = m_SpotAngle * 2.0f;
+ }
+ else
+ {
+ TRANSFER (m_CookieSize);
+ }
+
+ #if UNITY_EDITOR
+ if (transfer.IsVersionSmallerOrEqual(1))
+ {
+ transfer.Transfer(m_Shadows.m_Type, "m_Shadows");
+ transfer.Transfer(m_Shadows.m_Resolution, "m_ShadowResolution");
+ transfer.Transfer(m_Shadows.m_Strength, "m_ShadowStrength");
+ }
+ else
+ {
+ transfer.Transfer(m_Shadows, "m_Shadows");
+ }
+ #else
+ transfer.Transfer(m_Shadows, "m_Shadows");
+ #endif
+
+ TRANSFER (m_Cookie);
+ transfer.Transfer (m_DrawHalo, "m_DrawHalo", kSimpleEditorMask);
+ transfer.Transfer (m_ActuallyLightmapped, "m_ActuallyLightmapped", kDontAnimate);
+ transfer.Align();
+ TRANSFER (m_Flare);
+ TRANSFER (m_RenderMode);
+ TRANSFER (m_CullingMask);
+ TRANSFER (m_Lightmapping);
+
+ TRANSFER_EDITOR_ONLY (m_ShadowSamples);
+ TRANSFER_EDITOR_ONLY (m_ShadowRadius);
+ TRANSFER_EDITOR_ONLY (m_ShadowAngle);
+ TRANSFER_EDITOR_ONLY (m_IndirectIntensity);
+ TRANSFER_EDITOR_ONLY (m_AreaSize);
+}
+
+void Light::SetupHalo () {
+ if( m_DrawHalo && IsActive() && GetEnabled() )
+ {
+ float haloStr = GetRenderSettings().GetHaloStrength();
+ if (!m_HaloHandle)
+ m_HaloHandle = GetHaloManager().AddHalo();
+
+ if (m_HaloHandle) {
+ ///@TODO: Handle color conversion.
+ GetHaloManager().UpdateHalo( m_HaloHandle, GetComponent(Transform).GetPosition(), m_Color * (haloStr * m_Intensity * m_Color.a), haloStr * m_Range, GetGameObject().GetLayerMask() );
+ }
+ } else {
+ if (m_HaloHandle) {
+ GetHaloManager().DeleteHalo (m_HaloHandle);
+ m_HaloHandle = 0;
+ }
+ }
+}
+
+void Light::SetupFlare ()
+{
+ Flare *flare = m_Flare;
+ if (!flare || !IsActive() || !GetEnabled())
+ {
+ if (m_FlareHandle != -1)
+ {
+ GetFlareManager().DeleteFlare (m_FlareHandle);
+ m_FlareHandle = -1;
+ }
+ return;
+ }
+
+ bool inf;
+ Vector3f pos;
+
+ if (m_Type != kLightDirectional)
+ {
+ pos = GetComponent(Transform).GetPosition();
+ inf = false;
+ }
+ else
+ {
+ pos = GetComponent(Transform).TransformDirection (Vector3f (0,0,1));
+ inf = true;
+ }
+
+ if (m_FlareHandle == -1)
+ m_FlareHandle = GetFlareManager().AddFlare ();
+ GetFlareManager().UpdateFlare(
+ m_FlareHandle,
+ flare,
+ pos,
+ inf,
+ GetRenderSettings().GetFlareStrength(),
+ m_ConvertedFinalColor,
+ GetRenderSettings().GetFlareFadeSpeed(),
+ GetGameObject().GetLayerMask(),
+ kNoFXLayerMask|kIgnoreRaycastMask
+ );
+}
+
+
+bool Light::IsValidToRender() const
+{
+ // Spot lights with range lower than a pretty high value of 0.001f have to be culled already,
+ // as the code extracting projection planes for culling isn't smart enough to handle smaller values.
+ return !((m_Type == kLightSpot && (m_Range < 0.001f || m_SpotAngle < 0.001f)) ||
+ (m_Type == kLightPoint && m_Range < 0.00000001f));
+}
+
+
+IMPLEMENT_CLASS_HAS_INIT (Light)
+IMPLEMENT_OBJECT_SERIALIZE (Light)
+
+/// @TODO: Hack and should be removed before 2.0
+void SetupVertexLights(const std::vector<Light*>& lights)
+{
+ GfxDevice& device = GetGfxDevice();
+
+ /// @TODO: .a is multiplied differently only here. This is very inconsistent
+ /// Someone with guts please fix it or get rid of SetupVertexLights codepath completely.
+ ColorRGBAf ambient = GetRenderSettings().GetAmbientLightInActiveColorSpace();
+ ambient *= ColorRGBAf(0.5F, 0.5F, 0.5F, 1.0F);
+ device.SetAmbient( ambient.GetPtr() );
+
+
+ int lightNumber = 0;
+ for (int i = 0, size = lights.size(); i < size; ++i)
+ {
+ Light* light = lights[i];
+ if (light)
+ {
+ light->SetupVertexLight(lightNumber, 1.0f); // @TODO: Visibility fade does not work with this vertex light setup path
+ lightNumber++;
+ }
+ }
+ device.DisableLights (lightNumber);
+}
+
+
+void SetLightScissorRect (const Rectf& lightRect, const Rectf& viewPort, bool intoRT, GfxDevice& device)
+{
+ Rectf rect = lightRect;
+ rect.Scale (viewPort.width, viewPort.height);
+ if (!intoRT)
+ rect.Move (viewPort.x, viewPort.y);
+ int scissorRect[4];
+ RectfToViewport (rect, scissorRect);
+ FlipScreenRectIfNeeded (device, scissorRect);
+ device.SetScissorRect (scissorRect[0], scissorRect[1], scissorRect[2], scissorRect[3]);
+}
+
+void ClearScissorRect (bool oldScissor, const int oldRect[4], GfxDevice& device)
+{
+ if (oldScissor)
+ device.SetScissorRect (oldRect[0],oldRect[1],oldRect[2],oldRect[3]);
+ else
+ device.DisableScissor();
+}
+
+
+// --------------------------------------------------------------------------
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (LightTests)
+{
+ TEST(LightKeywordsHaveExpectedValues)
+ {
+ CHECK_EQUAL (kLightKeywordSpot, keywords::Create("SPOT"));
+ CHECK_EQUAL (kLightKeywordDirectional, keywords::Create("DIRECTIONAL"));
+ CHECK_EQUAL (kLightKeywordDirectionalCookie, keywords::Create("DIRECTIONAL_COOKIE"));
+ CHECK_EQUAL (kLightKeywordPoint, keywords::Create("POINT"));
+ CHECK_EQUAL (kLightKeywordPointCookie, keywords::Create("POINT_COOKIE"));
+ UInt32 mask = 0;
+ for (int i = 0; i < kLightKeywordCount; ++i)
+ mask += 1<<i;
+ CHECK_EQUAL (kAllLightKeywordsMask, mask);
+ }
+} // SUITE
+#endif // ENABLE_UNIT_TESTS
diff --git a/Runtime/Camera/Light.h b/Runtime/Camera/Light.h
new file mode 100644
index 0000000..c14a59c
--- /dev/null
+++ b/Runtime/Camera/Light.h
@@ -0,0 +1,254 @@
+#ifndef LIGHT_H
+#define LIGHT_H
+
+#include "Lighting.h"
+#include "Flare.h"
+#include "ShadowSettings.h"
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/GfxDevice/GfxDeviceObjects.h"
+#include "Runtime/GfxDevice/GfxDeviceConfigure.h"
+#include "Runtime/Utilities/LinkedList.h"
+#if UNITY_EDITOR
+#include "Editor/Src/LightmapVisualization.h"
+#endif
+#include "Runtime/Math/Rect.h"
+
+class Texture;
+class GfxDevice;
+
+class Light : public Behaviour, public ListElement
+{
+public:
+
+ REGISTER_DERIVED_CLASS (Light, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (Light)
+
+ Light (MemLabelId label, ObjectCreationMode mode);
+ // ~Light(); declared-by-macro
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ // Tag class as sealed, this makes QueryComponent faster.
+ static bool IsSealedClass () { return true; }
+
+ virtual void Reset ();
+
+ // System interaction
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+ virtual void CheckConsistency ();
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ void TransformChanged ();
+
+
+ LightType GetType() const { return static_cast<LightType>( m_Type ); }
+ void SetType (LightType type);
+
+
+ /// How to render this light.
+ enum RenderMode
+ {
+ kRenderAuto, ///< Automatic
+ kRenderImportant, ///< This light is important
+ kRenderNotImportant, ///< This light is not very important
+ kRenderModeCount // keep this last!
+ };
+ /// Get the render mode hint for this light
+ int GetRenderMode() const { return m_RenderMode; }
+ void SetRenderMode (int mode) { m_RenderMode = mode; SetDirty(); m_GfxLightValid = false; }
+
+ enum Lightmapping
+ {
+ kLightmappingRealtimeOnly = 0, // light is not baked, realtime only
+ kLightmappingAuto = 1, // light is baked in the dual lightmap style
+ kLightmappingBakedOnly = 2 // light is fully baked, not rendering in realtime at all
+ };
+
+ void SetLightmapping (int mode) { m_Lightmapping = mode; SetDirty(); m_GfxLightValid = false; }
+ inline Lightmapping GetLightmappingForRender() const;
+ Lightmapping GetLightmappingForBake() const;
+
+ void SetActuallyLightmapped (bool v);
+ bool GetActuallyLightmapped() const { return m_ActuallyLightmapped; }
+
+ /// Get the full range of the light.
+ float GetRange() const { return m_Range; }
+ void SetRange (float range) { m_Range = std::max(0.0F, range); SetDirty (); m_GfxLightValid = false; Precalc (); }
+
+ /// Get the spot angle in degrees.
+ float GetSpotAngle() const { return m_SpotAngle; }
+ void SetSpotAngle (float angle) { m_SpotAngle = angle; CheckConsistency(); SetDirty (); m_GfxLightValid = false; Precalc (); }
+
+ float GetCookieSize() const { return m_CookieSize; }
+ void SetCookieSize (float size) { m_CookieSize = size; CheckConsistency(); SetDirty (); m_GfxLightValid = false; Precalc (); }
+
+ // range * this value = end side length
+ float GetCotanHalfSpotAngle () const { return m_CotanHalfSpotAngle; }
+ // range * this value = diagonal side length
+ float GetInvCosHalfSpotAngle () const { return m_InvCosHalfSpotAngle; }
+
+ /// Get/set the cookie used for the light
+ void SetCookie (Texture *tex);
+ Texture *GetCookie () const { return m_Cookie; }
+
+ /// Get/set the lens flare used
+ void SetFlare (Flare *flare);
+ Flare *GetFlare () { return m_Flare; }
+
+ const ColorRGBAf& GetColor () const { return m_Color; }
+ void SetColor (const ColorRGBAf& c);
+ ColorRGBAf GetConvertedFinalColor() const { return m_ConvertedFinalColor; }
+
+ float GetIntensity() const { return m_Intensity; }
+ void SetIntensity( float i );
+
+ float GetShadowStrength() const { return m_Shadows.m_Strength; }
+ void SetShadowStrength( float f ) { m_Shadows.m_Strength = f; SetDirty(); }
+ ShadowType GetShadows() const { return static_cast<ShadowType>(m_Shadows.m_Type); }
+ void SetShadows( int v );
+ int GetShadowResolution() const { return m_Shadows.m_Resolution; }
+ void SetShadowResolution( int v ) { m_Shadows.m_Resolution = v; SetDirty(); }
+ float GetShadowBias() const { return m_Shadows.m_Bias; }
+ void SetShadowBias( float v ) { m_Shadows.m_Bias = v; SetDirty(); }
+ float GetShadowSoftness() const { return m_Shadows.m_Softness; }
+ void SetShadowSoftness (float v) { m_Shadows.m_Softness = v; SetDirty(); }
+ float GetShadowSoftnessFade() const { return m_Shadows.m_SoftnessFade; }
+ void SetShadowSoftnessFade (float v) { m_Shadows.m_SoftnessFade = v; SetDirty(); }
+
+ int GetFinalShadowResolution() const;
+
+ #if UNITY_EDITOR
+ int GetShadowSamples() const { return m_ShadowSamples; }
+ void SetShadowSamples (int samples) { m_ShadowSamples = samples; }
+ float GetShadowRadius() const { return m_ShadowRadius; }
+ void SetShadowRadius (float radius) { m_ShadowRadius = radius; }
+ float GetShadowAngle() const { return m_ShadowAngle; }
+ void SetShadowAngle (float angle) { m_ShadowAngle = angle; }
+ float GetIndirectIntensity() const { return m_IndirectIntensity; }
+ void SetIndirectIntensity (float intensity) { m_IndirectIntensity = intensity; }
+ Vector2f GetAreaSize() const { return m_AreaSize; }
+ void SetAreaSize(const Vector2f& areaSize) { m_AreaSize = areaSize; }
+ #endif
+
+ /// Calculate the quadratic attenuation factor for a light with a specified range
+ /// @param range the range of the light
+ /// @return the quadratic attenuation factor
+ static float CalcQuadFac (float range);
+
+ // Attenuation function: inverse quadratic. distSqr is over 0..1 range
+ static float AttenuateNormalized(float distSqr);
+
+ // Get the result of attenuation for a squared distance from the light
+ float AttenuateApprox (float sqrDist) const;
+
+ // Set up this light as GfxDevice fixed function per-vertex light.
+ // IMPORTANT: Assumes the modelview matrix has been set up to be the world2camera matrix.
+ void SetupVertexLight (int lightNo, float visibilityFade);
+
+ void SetLightKeyword();
+ void SetPropsToShaderLab (float blend) const;
+
+
+ // Set up the halo for the light.
+ void SetupHalo ();
+ // Set up the flare for the light.
+ void SetupFlare ();
+
+ void SetCullingMask (int mask) { m_CullingMask.m_Bits = mask; SetDirty(); }
+ int GetCullingMask () const { return m_CullingMask.m_Bits; }
+
+ // Precalc all non-changing shaderlab values.
+ void Precalc ();
+
+ const Vector3f& GetWorldPosition() const { return m_WorldPosition; }
+ const Matrix4x4f& GetWorldToLocalMatrix() const { return m_World2Local; }
+
+ enum AttenuationMode
+ {
+ kSpotCookie, // 2D cookie projected in spot light's cone
+ kPointFalloff, // Attenuation for a point light
+ kDirectionalCookie, // Cookie projected from a directional light
+ kUnused // The attenuation texture is not used
+ };
+ void GetMatrix (const Matrix4x4f* __restrict object2light, Matrix4x4f* __restrict outMatrix) const;
+
+ bool IsValidToRender() const;
+
+private:
+ void UpdateSpotAngleValues ()
+ {
+ float halfSpotRad = Deg2Rad(m_SpotAngle * 0.5f);
+ float cs = cosf(halfSpotRad);
+ float ss = sinf(halfSpotRad);
+ m_CotanHalfSpotAngle = cs / ss;
+ m_InvCosHalfSpotAngle = 1.0f / cs;
+ }
+
+ void ComputeGfxLight (GfxVertexLight& gfxLight) const;
+
+private:
+ Matrix4x4f m_World2Local;
+ Vector3f m_WorldPosition;
+ ShadowSettings m_Shadows; ///< Shadow settings.
+
+ ColorRGBAf m_Color;
+ ColorRGBAf m_ConvertedFinalColor;
+
+ PPtr<Flare> m_Flare; ///< Does the light have a flare?
+ PPtr<Texture> m_Cookie; ///< Custom cookie (optional).
+ BitField m_CullingMask; ///< The mask used for selectively lighting objects in the scene.
+ float m_Intensity; ///< Light intensity range {0.0, 8.0}
+ float m_Range; ///< Light range
+ float m_SpotAngle; ///< Angle of the spotlight cone.
+ float m_CookieSize; ///< Cookie size for directional lights.
+ float m_CotanHalfSpotAngle; // cotangent of half of the spot angle
+ float m_InvCosHalfSpotAngle; // 1/cos of half of the spot angle
+ int m_RenderMode; ///< enum { Auto, Important, Not Important } Rendering mode for the light.
+ int m_Lightmapping; ///< enum { RealtimeOnly, Auto, BakedOnly } Is light baked into lightmaps?
+ int m_Type; ///< enum { Spot, Directional, Point, Area (baked only) } Light type
+ bool m_DrawHalo; ///< Does the light have a halo?
+ bool m_ActuallyLightmapped; // Is it actually lightmapped already?
+ #if UNITY_EDITOR
+ int m_ShadowSamples; // number of samples for lightmapper shadow calculations
+ float m_ShadowRadius; // radius of the light source for lightmapper shadow calculations (point and spot lights)
+ float m_ShadowAngle; // angle of the cone for lightmapper shadow rays (directional lights)
+ float m_IndirectIntensity; // aka bounce intensity - a multiplier for the indirect light
+ Vector2f m_AreaSize; // size of area light's rectangle
+ #endif
+
+ PPtr<Texture> m_AttenuationTexture;
+ AttenuationMode m_AttenuationMode;
+ LightKeywordMode m_KeywordMode; // The current keyword used by the light
+
+ int m_HaloHandle, m_FlareHandle;
+
+ GfxVertexLight m_CachedGfxLight;
+ bool m_GfxLightValid;
+};
+
+void SetupVertexLights(const std::vector<Light*>& lights);
+void SetLightScissorRect (const Rectf& lightRect, const Rectf& viewPort, bool intoRT, GfxDevice& device);
+void ClearScissorRect (bool oldScissor, const int oldRect[4], GfxDevice& device);
+
+
+// Inline this; called a lot in light culling.
+inline Light::Lightmapping Light::GetLightmappingForRender() const
+{
+ if (m_Type == kLightArea)
+ return kLightmappingBakedOnly;
+
+ // all lights behave as realtime only if no lightmaps have been baked yet or if lightmaps are disabled
+ return m_ActuallyLightmapped
+ #if UNITY_EDITOR
+ && GetLightmapVisualization().GetUseLightmapsForRendering()
+ #endif
+ ? static_cast<Lightmapping>(m_Lightmapping) : kLightmappingRealtimeOnly;
+}
+
+
+#endif
diff --git a/Runtime/Camera/LightCulling.cpp b/Runtime/Camera/LightCulling.cpp
new file mode 100644
index 0000000..771ebc3
--- /dev/null
+++ b/Runtime/Camera/LightCulling.cpp
@@ -0,0 +1,669 @@
+#include "UnityPrefix.h"
+#include "LightManager.h"
+#include "CullResults.h"
+#include "Light.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "Runtime/Geometry/Sphere.h"
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Geometry/BoundingUtils.h"
+#include "Runtime/Math/Simd/math.h"
+#include "ShadowCulling.h"
+#include "Runtime/Profiler/Profiler.h"
+
+#include "External/Umbra/builds/interface/runtime/umbraTome.hpp"
+#include "External/Umbra/builds/interface/runtime/umbraQuery.hpp"
+
+PROFILER_INFORMATION(gCullLightConnectivity, "CullLightConnectivity", kProfilerRender);
+PROFILER_INFORMATION(gOcclusionCullLight, "OcclusionCullLight", kProfilerRender);
+
+struct LocalLightCullingParameters
+{
+ Plane eyePlane;
+ float farDistance;
+ bool enableShadows;
+ UInt32 cullingMask;
+};
+
+
+
+static float CalculateIntensityForMainLight (const Light& source)
+{
+ DebugAssert(source.GetType() == kLightDirectional);
+ if (source.GetRenderMode() == Light::kRenderNotImportant || source.GetLightmappingForRender() == Light::kLightmappingBakedOnly)
+ return 0.0f;
+
+ float lum = source.GetColor().GreyScaleValue() * source.GetIntensity();
+ if (source.GetShadows() != kShadowNone)
+ lum *= 16.0f;
+ return lum;
+}
+
+static void OcclusionCullLocalLights (const SceneCullingParameters& cullingParams, const Vector4f* lightBSpheres, IndexList& visible)
+{
+ // Umbra light culling is not supported. Just output
+ const Umbra::Visibility* umbraVisibility = cullingParams.sceneVisbilityForShadowCulling->umbraVisibility;
+ if (!cullingParams.useLightOcclusionCulling)
+ return;
+
+ PROFILER_BEGIN(gOcclusionCullLight, NULL)
+
+ Umbra::OcclusionBuffer* occlusionBuffer = umbraVisibility->getOutputBuffer();
+
+ // Mark lights visible and add them to lightVisibleBits
+ size_t visibleCount = 0;
+ for (int l = 0; l < visible.size; l++)
+ {
+ int lightNdx = visible[l];
+
+ const float* lightFloatParams = reinterpret_cast<const float*> (lightBSpheres + lightNdx);
+
+ float r = lightFloatParams[3];
+ Vector3f r3(r, r, r);
+ Vector3f lc = Vector3f(lightFloatParams[0], lightFloatParams[1], lightFloatParams[2]);
+
+ Vector3f mn = lc - r3;
+ Vector3f mx = lc + r3;
+
+ bool isLightVisible = occlusionBuffer->isAABBVisible((const Umbra::Vector3&)mn, (const Umbra::Vector3&)mx);
+ if (isLightVisible)
+ visible.indices[visibleCount++] = lightNdx;
+ }
+ visible.size = visibleCount;
+
+ PROFILER_END
+
+ PROFILER_BEGIN(gCullLightConnectivity, NULL)
+
+
+ ///@TODO: This doesn't make sense for non-shadowed lights...
+
+ // Use connectivity data to detect which lights are can touch any visible geometry
+ Umbra::IndexList visibleLightList(visible.indices, visible.size, visible.size);
+ Umbra::QueryExt* umbraQuery = (Umbra::QueryExt*)cullingParams.umbraQuery;
+
+ umbraQuery->queryLocalLights(
+ visibleLightList,
+ 0,
+ (const Umbra::SphereLight*)lightBSpheres,
+ visible.reservedSize,
+ *umbraVisibility->getOutputClusters(),
+ &visibleLightList);
+
+ visible.size = visibleLightList.getSize();
+
+ PROFILER_END
+}
+
+static void FrustumCullLocalLights (const CullingParameters& cullingParameters, const Vector4f* lightBSpheres, IndexList& visibleLights, IndexList& offScreenLights, float* visibilityFades)
+{
+ int visibleLightCount = 0;
+ int offScreenLightCount = 0;
+
+ for (int i=0;i<visibleLights.reservedSize;i++)
+ {
+ float distance = PointDistanceToFrustum(lightBSpheres[i], cullingParameters.cullingPlanes, cullingParameters.cullingPlaneCount);
+
+ // Light is inside or intersecting the frustum
+ if (distance < lightBSpheres[i].w)
+ {
+ // lights that intersect or are inside the frustum
+ DebugAssert(visibleLightCount < visibleLights.reservedSize);
+ visibleLights.indices[visibleLightCount++] = i;
+ }
+ // Light is outside of the frustum and must be faded out
+ else if (distance < lightBSpheres[i].w + lightBSpheres[i].w)
+ {
+ //off screen lights whose distance from frustum is less than light radius
+ DebugAssert(offScreenLightCount < offScreenLights.reservedSize);
+
+ offScreenLights.indices[offScreenLightCount] = i;
+
+ distance -= lightBSpheres[i].w;
+
+ distance = distance / lightBSpheres[i].w;
+ visibilityFades[offScreenLightCount++] = 1.0F - distance;
+ DebugAssert(distance > 0.0f);
+ DebugAssert(distance < 1.0f);
+ }
+ }
+ visibleLights.size = visibleLightCount;
+ offScreenLights.size = offScreenLightCount;
+}
+
+static bool IsValidRenderingLight (const Light& light, LightType lightType, UInt32 cullingMask)
+{
+ const Light::Lightmapping lightmappingMode = light.GetLightmappingForRender();
+ // If light is lightmap only - just skip it
+ if (lightmappingMode == Light::kLightmappingBakedOnly)
+ return false;
+
+ // If light not visible in camera's culling mask - just skip it
+ if ((light.GetCullingMask() & cullingMask) == 0)
+ return false;
+
+ // Light with zero intensity - just skip it
+ if (light.GetIntensity() < 0.01f)
+ return false;
+
+ // Check if light has valid properties
+ return light.IsValidToRender();
+}
+
+static void SetupActiveDirectionalLight (const Light& light, ActiveLight& outLight)
+{
+ const Light::Lightmapping lightmappingMode = light.GetLightmappingForRender();
+
+ outLight.light = const_cast<Light*> (&light);
+#if ENABLE_SHADOWS
+ outLight.insideShadowRange = true;
+#endif
+ outLight.boundingBox = AABB(Vector3f::zero, Vector3f::infinityVec);
+
+ outLight.lightmappingForRender = lightmappingMode;
+ outLight.isVisibleInPrepass = true;
+ outLight.screenRect = Rectf(0,0,1,1);
+ outLight.cullingMask = light.GetCullingMask();
+ outLight.hasCookie = light.GetCookie() ? true : false;
+ outLight.lightRenderMode = light.GetRenderMode();
+ outLight.lightType = light.GetType();
+ outLight.isOffscreenVertexLight = false;
+ outLight.visibilityFade = 1.0;
+}
+
+static int FindBestMainDirectionalLight (const Light** lights, size_t count)
+{
+ // Find main directional light based on intensity
+ int mainLightIndex = -1;
+ float bestMainLightIntensity = 0.0f;
+
+ for (int i=0;i<count;i++)
+ {
+ const Light& light = *lights[i];
+ float mainLightIntensity = CalculateIntensityForMainLight(light);
+ if (mainLightIntensity > bestMainLightIntensity)
+ {
+ mainLightIndex = i;
+ bestMainLightIntensity = mainLightIntensity;
+ }
+ }
+
+ return mainLightIndex;
+}
+
+static void AddDirectionalLights (const Light** lights, size_t count, ActiveLights& outLights)
+{
+ Assert(outLights.lights.size() == 0);
+
+ // Add main light as the first light!
+ int mainLightIndex = FindBestMainDirectionalLight(lights, count);
+ if (mainLightIndex != -1)
+ {
+ SetupActiveDirectionalLight (*lights[mainLightIndex], outLights.lights.push_back());
+ outLights.hasMainLight = true;
+ }
+ else
+ outLights.hasMainLight = false;
+
+ // Add any other lights
+ for (int i=0;i<count;i++)
+ {
+ if (i == mainLightIndex)
+ continue;
+
+ SetupActiveDirectionalLight (*lights[i], outLights.lights.push_back());
+ }
+ outLights.numDirLights = outLights.lights.size();
+}
+
+static void SetupActiveLocalLight (const LocalLightCullingParameters& params, const ShadowCullData& shadowCullData, const Light& light, const Vector4f& lightBSpheres, const Rectf lightScreenRectangle, bool isVisible, float visibilityFade, ActiveLight& outLight)
+{
+ const Transform& trans = light.GetComponent(Transform);
+ Matrix4x4f lightMatrix = trans.GetLocalToWorldMatrixNoScale();
+ float radius = lightBSpheres.w;
+ Vector3f center = Vector3f(lightBSpheres.x, lightBSpheres.y, lightBSpheres.z);
+ float nearDistanceFudged = shadowCullData.camera->GetNear() * 1.001f;
+ float farDistanceFudged = shadowCullData.camera->GetFar() * 0.999f;
+
+ // Add to spot or point lights
+ outLight.light = const_cast<Light*> (&light);
+ float viewDistance = params.eyePlane.GetDistanceToPoint(center);
+ float closestDistance = std::numeric_limits<float>::infinity();
+ float farthestDistance = -closestDistance;
+
+ outLight.isVisibleInPrepass = isVisible;
+ outLight.screenRect = lightScreenRectangle;
+ outLight.visibilityFade = visibilityFade;
+
+ // If light survived from culling, but is not visible
+ outLight.isOffscreenVertexLight = !isVisible;
+
+ // Baked-only lights are already rejected, so lightmaps are either off or auto
+ const Light::Lightmapping lightmappingMode = light.GetLightmappingForRender();
+ outLight.lightmappingForRender = lightmappingMode;
+
+ // Keep cached copy of culling mask for efficiency
+ outLight.cullingMask = light.GetCullingMask();
+ outLight.hasCookie = light.GetCookie() ? true : false;
+ outLight.lightRenderMode = light.GetRenderMode();
+
+ LightType lightType = light.GetType();
+ outLight.lightType = lightType;
+
+ if (lightType == kLightSpot)
+ {
+ // Find nearest point
+ SpotLightBounds spotBounds;
+ CalculateSpotLightBounds (light.GetRange(), light.GetCotanHalfSpotAngle(), lightMatrix, spotBounds);
+ const Vector3f* points = spotBounds.points;
+
+ for (int i = 0; i < SpotLightBounds::kPointCount; i++)
+ {
+ float dist = params.eyePlane.GetDistanceToPoint(points[i]);
+ closestDistance = std::min (closestDistance, dist);
+ farthestDistance = std::max (farthestDistance, dist);
+ }
+ outLight.intersectsNear = closestDistance <= nearDistanceFudged;
+ outLight.intersectsFar = farthestDistance >= farDistanceFudged;
+
+ // Nearest point is also bounded by light radius (cull by far plane distance)
+ float dist = viewDistance - radius;
+ closestDistance = std::max(closestDistance, dist);
+ if (closestDistance > params.farDistance)
+ {
+ outLight.isVisibleInPrepass = false;
+ outLight.screenRect = Rectf(0,0,0,0);
+ }
+
+ // Compute bounding box
+ MinMaxAABB bounds(points[0], points[0]);
+ for (int i = 1; i < SpotLightBounds::kPointCount; i++)
+ bounds.Encapsulate(points[i]);
+ outLight.boundingBox = AABB(bounds);
+ }
+ else
+ {
+ DebugAssert(lightType == kLightPoint);
+ closestDistance = viewDistance - radius;
+ Vector3f boxSize(radius, radius, radius);
+ outLight.boundingBox = AABB(center, boxSize);
+
+ #if GFX_USE_SPHERE_FOR_POINT_LIGHT
+ // If we're drawing an icosphere or icosahedron, check for the radius of a sphere
+ // circumscribed on an icosahedron, which was circumscribed on a unit sphere.
+ const float proxyMeshSize = 1.27f;
+ #else
+ // If we're drawing a bounding cube, check if the farthest corner would not cross the near plane
+ const float proxyMeshSize = 1.7321f;
+ #endif
+ const float intersectionRadius = radius * proxyMeshSize;
+ outLight.intersectsNear = (viewDistance - intersectionRadius) <= nearDistanceFudged;
+ outLight.intersectsFar = (viewDistance + intersectionRadius) >= farDistanceFudged;
+ }
+
+#if ENABLE_SHADOWS
+ // TODO: tighter shadow culling for spot lights
+ outLight.insideShadowRange = (closestDistance < shadowCullData.shadowDistance) && params.enableShadows;
+ if (outLight.insideShadowRange && shadowCullData.useSphereCulling)
+ {
+ float sumRadii = shadowCullData.shadowCullRadius + radius;
+ if (SqrMagnitude(center - shadowCullData.shadowCullCenter) > Sqr(sumRadii))
+ outLight.insideShadowRange = false;
+ else if (!IsObjectWithinShadowRange(shadowCullData, outLight.boundingBox))
+ outLight.insideShadowRange = false;
+ }
+
+ // If light is auto but behind shadow distance (so dual lightmaps normally) - just skip it
+ if ((lightmappingMode == Light::kLightmappingAuto) && !outLight.insideShadowRange)
+ {
+ outLight.isVisibleInPrepass = false;
+ outLight.screenRect = Rectf(0,0,0,0);
+ }
+#endif
+}
+
+// Function returns true if screen rectangle (outRect) is inside camera viewport
+// Returned rectangle is 0..1 coordinates
+bool CalculateLightScreenBounds (const Matrix4x4f& cameraWorldToClip, const Light& light, const Matrix4x4f& lightMatrix, Rectf& outRect)
+{
+ Assert ( light.GetType() != kLightDirectional );
+
+ // Compute the hull of light's bounds
+ Vector3f lightPos = lightMatrix.GetPosition();
+
+ UInt8 hullFaces;
+ UInt8 hullCounts[6]; // 6 faces
+ Vector3f hullPoints[24]; // this input hull has maximum of 6 faces x 4 points hence 24 vectors
+
+ switch( light.GetType() )
+ {
+ case kLightSpot:
+ // Spot light's hull is the light position and four points on the plane at Range
+ {
+ SpotLightBounds spotBounds;
+ CalculateSpotLightBounds(light.GetRange(), light.GetCotanHalfSpotAngle(), lightMatrix, spotBounds);
+ const Vector3f* points = spotBounds.points;
+
+ hullFaces = 5;
+ hullCounts[0] = 4;
+ hullCounts[1] = hullCounts[2] = hullCounts[3] = hullCounts[4] = 3;
+
+ // far plane
+ hullPoints[0] = points[4]; hullPoints[1] = points[3]; hullPoints[2] = points[2]; hullPoints[3] = points[1];
+
+ // sides
+ hullPoints[ 4] = points[0]; hullPoints[ 5] = points[1]; hullPoints[ 6] = points[2];
+ hullPoints[ 7] = points[0]; hullPoints[ 8] = points[2]; hullPoints[ 9] = points[3];
+ hullPoints[10] = points[0]; hullPoints[11] = points[3]; hullPoints[12] = points[4];
+ hullPoints[13] = points[0]; hullPoints[14] = points[4]; hullPoints[15] = points[1];
+ }
+ break;
+
+ case kLightPoint:
+ // Point light's hull is the cube at position with half-size Range
+ {
+ float r = light.GetRange();
+ Vector3f points[8];
+ points[0].Set( lightPos.x - r, lightPos.y - r, lightPos.z - r );
+ points[1].Set( lightPos.x + r, lightPos.y - r, lightPos.z - r );
+ points[2].Set( lightPos.x + r, lightPos.y + r, lightPos.z - r );
+ points[3].Set( lightPos.x - r, lightPos.y + r, lightPos.z - r );
+ points[4].Set( lightPos.x - r, lightPos.y - r, lightPos.z + r );
+ points[5].Set( lightPos.x + r, lightPos.y - r, lightPos.z + r );
+ points[6].Set( lightPos.x + r, lightPos.y + r, lightPos.z + r );
+ points[7].Set( lightPos.x - r, lightPos.y + r, lightPos.z + r );
+
+ hullFaces = 6;
+ hullCounts[0] = hullCounts[1] = hullCounts[2] = hullCounts[3] = hullCounts[4] = hullCounts[5] = 4;
+
+ hullPoints[ 0] = points[0]; hullPoints[ 1] = points[1]; hullPoints[ 2] = points[2]; hullPoints[ 3] = points[3];
+ hullPoints[ 4] = points[7]; hullPoints[ 5] = points[6]; hullPoints[ 6] = points[5]; hullPoints[ 7] = points[4];
+ hullPoints[ 8] = points[0]; hullPoints[ 9] = points[3]; hullPoints[10] = points[7]; hullPoints[11] = points[4];
+ hullPoints[12] = points[1]; hullPoints[13] = points[5]; hullPoints[14] = points[6]; hullPoints[15] = points[2];
+ hullPoints[16] = points[4]; hullPoints[17] = points[5]; hullPoints[18] = points[1]; hullPoints[19] = points[0];
+ hullPoints[20] = points[6]; hullPoints[21] = points[7]; hullPoints[22] = points[3]; hullPoints[23] = points[2];
+
+ }
+ break;
+
+ default:
+ hullFaces = 0;
+ AssertString( "Unknown light type" );
+ break;
+ }
+
+ // Clip hull by camera's near plane - needed because point behind near plane don't have
+ // proper projection on the screen.
+ Plane nearPlane;
+ ExtractProjectionNearPlane( cameraWorldToClip, &nearPlane );
+ // Push near plane forward a bit, by a small number proportional to plane's distance from
+ // the origin (precision gets worse at larger numbers).
+ nearPlane.d() = nearPlane.d() - Abs(nearPlane.d())*0.0001f;
+ DebugAssertIf(!IsNormalized(nearPlane.GetNormal()));
+
+ MinMaxAABB aabb;
+ CalcHullBounds(hullPoints, hullCounts, hullFaces, nearPlane, cameraWorldToClip, aabb);
+ outRect.Set (
+ (aabb.m_Min.x + 1.0f) * 0.5f,
+ (aabb.m_Min.y + 1.0f) * 0.5f,
+ (aabb.m_Max.x - aabb.m_Min.x) * 0.5f,
+ (aabb.m_Max.y - aabb.m_Min.y) * 0.5f
+ );
+
+ // Is screen rect inside viewport [0,1]
+ return ((aabb.m_Max.x > aabb.m_Min.x) || (aabb.m_Max.y > aabb.m_Min.y)) ? true : false;
+}
+
+void AddActiveLocalLights (const LocalLightCullingParameters& params, const ShadowCullData& shadowCullData, const Vector4f* lightBSpheres, const Light** lights, const IndexList& visibleLocalLights, float* visibilityFades, IndexList& offScreenLocalLights, ActiveLights& outLights)
+{
+ int offScreenLightCount = offScreenLocalLights.size;
+
+ //Add spot lights first, and point lights second
+ int lightTypes[2] = {kLightSpot, kLightPoint};
+ int lightCount[2] = {0, 0};
+ for (int j=0; j<2; j++)
+ for (int i=0;i<visibleLocalLights.size;i++)
+ {
+ int lightIndex = visibleLocalLights[i];
+ const Light &light = *lights[lightIndex];
+ if (light.GetType() == lightTypes[j])
+ {
+ // Calculate local light screen rectangle
+ const Transform& trans = lights[lightIndex]->GetComponent(Transform);
+ Matrix4x4f lightMatrix = trans.GetLocalToWorldMatrixNoScale();
+
+ Rectf lightScreenRect;
+ bool isInside = CalculateLightScreenBounds (shadowCullData.cameraWorldToClip, *lights[lightIndex], lightMatrix, lightScreenRect);
+
+ // Setup visible local light if it is inside camera viewport and has a valid screen rectangle
+ if (isInside && !lightScreenRect.IsEmpty())
+ {
+ SetupActiveLocalLight (params, shadowCullData, *lights[lightIndex], lightBSpheres[lightIndex], lightScreenRect, true, 1.0f, outLights.lights.push_back());
+ lightCount[j]++;
+ }
+ else if (!isInside) // change visible light to off screen light if it is outside camera viewport
+ {
+ visibilityFades[offScreenLightCount] = 1.0f; // don't fade as the local light is close to the frustum
+ offScreenLocalLights[offScreenLightCount++] = lightIndex;
+ }
+ }
+ }
+
+ outLights.numSpotLights = lightCount[0];
+ outLights.numPointLights = lightCount[1];
+
+ //Add off screen spot lights third, and off screen point lights fourth
+ lightCount[0] = lightCount[1] = 0;
+ for (int j=0; j<2; j++)
+ for (int i=0;i<offScreenLightCount;i++)
+ {
+ int lightIndex = offScreenLocalLights[i];
+ const Light &light = *lights[lightIndex];
+ if (light.GetType() == lightTypes[j])
+ {
+ SetupActiveLocalLight (params, shadowCullData, *lights[lightIndex], lightBSpheres[lightIndex], Rectf(0,0,0,0), false, visibilityFades[i], outLights.lights.push_back());
+ lightCount[j]++;
+ }
+ }
+
+ outLights.numOffScreenSpotLights = lightCount[0];
+ outLights.numOffScreenPointLights = lightCount[1];
+}
+
+void FindAndCullActiveLights (const SceneCullingParameters& sceneCullParameters, const ShadowCullData& cullData, ActiveLights& outLights)
+{
+ const List<Light>& allLights = GetLightManager().GetAllLights();
+
+ LocalLightCullingParameters localLightCullParameters;
+ localLightCullParameters.eyePlane.SetNormalAndPosition(cullData.viewDir, cullData.eyePos);
+ localLightCullParameters.farDistance = cullData.camera->GetFar();
+ localLightCullParameters.enableShadows = cullData.shadowDistance > cullData.camera->GetNear();
+ localLightCullParameters.cullingMask = cullData.camera->GetCullingMask();
+
+ size_t lightCount = allLights.size_slow();
+
+ dynamic_array<Vector4f> localLightBSpheres (kMemTempAlloc);
+ dynamic_array<const Light*> localLights (kMemTempAlloc);
+ dynamic_array<const Light*> directionalLights (kMemTempAlloc);
+ dynamic_array<float> offScreenLocalLightvisibilityFades (kMemTempAlloc);
+
+ localLightBSpheres.reserve(lightCount);
+ localLights.reserve(lightCount);
+ directionalLights.reserve(lightCount);
+ offScreenLocalLightvisibilityFades.reserve(lightCount);
+
+ LightManager::Lights::const_iterator it, itEnd = allLights.end();
+ for( it = allLights.begin(); it != itEnd; ++it)
+ {
+ const Light& light = *it;
+ LightType lightType = light.GetType();
+
+ if (!IsValidRenderingLight (light, lightType, localLightCullParameters.cullingMask))
+ continue;
+
+ // Add directional light
+ if (lightType == kLightDirectional)
+ {
+ directionalLights.push_back(&light);
+ }
+ // Setup data necessary for culling point / spot lights
+ else if (lightType == kLightPoint || lightType == kLightSpot)
+ {
+ float radius = light.GetRange();
+
+ if (lightType == kLightSpot)
+ radius *= light.GetInvCosHalfSpotAngle();
+
+ // Set radius to negative by default, use it to skip lights in the next loop
+ Vector3f lightPos = light.GetWorldPosition();
+ localLightBSpheres.push_back ( Vector4f(lightPos.x, lightPos.y, lightPos.z, radius) );
+ localLights.push_back (&light);
+ }
+ else
+ {
+ ErrorStringObject("Unsupported light type", &light);
+ }
+ }
+
+ IndexList visibleLocalLightIndices;
+ InitIndexList(visibleLocalLightIndices, localLightBSpheres.size());
+ IndexList offScreenLocalLightIndices;
+ InitIndexList(offScreenLocalLightIndices, localLightBSpheres.size());
+
+ // 1x and 2x light radius frustum culling for local lights
+ FrustumCullLocalLights (sceneCullParameters, localLightBSpheres.begin(), visibleLocalLightIndices, offScreenLocalLightIndices, offScreenLocalLightvisibilityFades.begin());
+
+ // Occlusion cull 1x local lights
+ OcclusionCullLocalLights (sceneCullParameters, localLightBSpheres.begin(), visibleLocalLightIndices);
+
+ // Reserve memory for lights...
+ outLights.lights.reserve (visibleLocalLightIndices.size + offScreenLocalLightIndices.size + directionalLights.size());
+
+ // Add directional lights to the outLights...
+ AddDirectionalLights (directionalLights.begin(), directionalLights.size(), outLights);
+
+ // Add visible local lights and off screen local lights to the outlights...
+ AddActiveLocalLights (localLightCullParameters, cullData, localLightBSpheres.begin(), localLights.begin(), visibleLocalLightIndices, offScreenLocalLightvisibilityFades.begin(), offScreenLocalLightIndices, outLights);
+
+ DestroyIndexList(visibleLocalLightIndices);
+ DestroyIndexList(offScreenLocalLightIndices);
+}
+
+
+static bool IsLightCulled (const ActiveLight& light, int layerMask, bool lightmappedObject, bool dualLightmapsMode)
+{
+ // Skip light if has lightmap for object
+ if ( (lightmappedObject && light.lightmappingForRender == Light::kLightmappingAuto) && (dualLightmapsMode == false) )
+ return true;
+
+ // Cull by layer mask
+ if ((layerMask & light.cullingMask) == 0)
+ return true;
+
+ return false;
+}
+
+static bool IsDirectionalLightCulled (const ActiveLight& light, int layerMask, bool lightmappedObject, bool dualLightmapsMode)
+{
+ DebugAssert(light.light->GetType() == kLightDirectional);
+
+ return IsLightCulled (light, layerMask, lightmappedObject, dualLightmapsMode);
+}
+
+static bool IsSpotLightCulled (const ActiveLight& light, int layerMask, bool lightmappedObject, bool dualLightmapsMode, const AABB& globalObjectAABB, const AABB& localObjectAABB, const Matrix4x4f& objectTransform)
+{
+ DebugAssert(light.light->GetType() == kLightSpot);
+
+ // Common code
+ if (IsLightCulled (light, layerMask, lightmappedObject, dualLightmapsMode))
+ return true;
+
+ // AABB vs AABB
+ if (!IntersectAABBAABB (globalObjectAABB, light.boundingBox))
+ return true;
+
+ const Light& source = *light.light;
+
+ // Detailed culling: frustum vs local AABB
+ Plane planes[6];
+ Matrix4x4f zscale, objectToLightMatrix, projectionMatrix;
+ zscale.SetScale (Vector3f (1.0F, 1.0F, -1.0F));
+
+ const float minNearDist = 0.0001F;
+ const float minNearFarRatio = 0.00001F;
+ float nearDist = std::max(minNearDist, source.GetRange() * minNearFarRatio);
+ projectionMatrix.SetPerspectiveCotan( source.GetCotanHalfSpotAngle(), nearDist, source.GetRange() );
+
+ // objectToLightMatrix = zscale * GetWorldToLocalMatrix * objectTransform
+ Matrix4x4f temp;
+ MultiplyMatrices4x4 (&zscale, &source.GetWorldToLocalMatrix(), &temp);
+ MultiplyMatrices4x4 (&temp, &objectTransform, &objectToLightMatrix);
+
+ // finalProjMatrix = projectionMatrix * objectToLightMatrix
+ Matrix4x4f finalProjMatrix;
+ MultiplyMatrices4x4 (&projectionMatrix, &objectToLightMatrix, &finalProjMatrix);
+ ExtractProjectionPlanes (finalProjMatrix, planes);
+
+ if (!IntersectAABBFrustumFull (localObjectAABB, planes))
+ return true;
+
+ return false;
+}
+
+static bool IsPointLightCulled (const ActiveLight& light, int layerMask, bool lightmappedObject, bool dualLightmapsMode, const AABB& globalObjectAABB, const AABB& localObjectAABB, const Matrix4x4f& objectTransform, float invScale)
+{
+ DebugAssert(light.light->GetType() == kLightPoint);
+
+ // Common code
+ if (IsLightCulled (light, layerMask, lightmappedObject, dualLightmapsMode))
+ return true;
+
+ // AABB vs AABB
+ if (!IntersectAABBAABB (globalObjectAABB, light.boundingBox))
+ return true;
+
+ const Light& source = *light.light;
+
+ // Detailed culling
+ // Test light sphere transformed into the object's local space
+ // against local aabb of the object
+ Vector3f objectRelativeLightPos = objectTransform.InverseMultiplyPoint3Affine (source.GetWorldPosition()) * invScale;
+ Sphere objectRelLightSphere (objectRelativeLightPos * invScale, source.GetRange () * invScale);
+
+ if (!IntersectAABBSphere (localObjectAABB, objectRelLightSphere))
+ return true;
+
+ return false;
+}
+
+void CullPerObjectLights (const ActiveLights& activeLights, const AABB& globalObjectAABB, const AABB& localObjectAABB, const Matrix4x4f& objectTransform, float invScale, int layerMask, bool lightmappedObject, bool dualLightmapsMode, ObjectLightIndices& outIndices)
+{
+ size_t index = 0;
+ size_t endIndex = activeLights.numDirLights;
+ for ( ; index < endIndex; index++)
+ if (!IsDirectionalLightCulled (activeLights.lights[index], layerMask, lightmappedObject, dualLightmapsMode))
+ outIndices.push_back (index);
+
+ endIndex += activeLights.numSpotLights;
+ for ( ; index < endIndex; index++)
+ if (!IsSpotLightCulled (activeLights.lights[index], layerMask, lightmappedObject, dualLightmapsMode, globalObjectAABB, localObjectAABB, objectTransform))
+ outIndices.push_back (index);
+
+ endIndex += activeLights.numPointLights;
+ for ( ; index < endIndex; index++)
+ if (!IsPointLightCulled (activeLights.lights[index], layerMask, lightmappedObject, dualLightmapsMode, globalObjectAABB, localObjectAABB, objectTransform, invScale))
+ outIndices.push_back (index);
+
+ endIndex += activeLights.numOffScreenSpotLights;
+ for ( ; index < endIndex; index++)
+ if (!IsSpotLightCulled (activeLights.lights[index], layerMask, lightmappedObject, dualLightmapsMode, globalObjectAABB, localObjectAABB, objectTransform))
+ outIndices.push_back (index);
+
+ endIndex += activeLights.numOffScreenPointLights;
+ for ( ; index < endIndex; index++)
+ if (!IsPointLightCulled (activeLights.lights[index], layerMask, lightmappedObject, dualLightmapsMode, globalObjectAABB, localObjectAABB, objectTransform, invScale))
+ outIndices.push_back (index);
+} \ No newline at end of file
diff --git a/Runtime/Camera/LightCulling.h b/Runtime/Camera/LightCulling.h
new file mode 100644
index 0000000..a55de69
--- /dev/null
+++ b/Runtime/Camera/LightCulling.h
@@ -0,0 +1,7 @@
+#pragma once
+
+// Figures out all lights that are visible in the scene
+void FindAndCullActiveLights (const SceneCullingParameters& sceneCullParameters, const ShadowCullData& cullData, ActiveLights& outLights);
+
+// Figures out lights for given object, no sorting.
+void CullPerObjectLights (const ActiveLights& activeLights, const AABB& globalObjectAABB, const AABB& localObjectAABB, const Matrix4x4f& objectTransform, float invScale, int layerMask, bool lightmappedObject, bool dualLightmapsMode, ObjectLightIndices& outIndices); \ No newline at end of file
diff --git a/Runtime/Camera/LightManager.cpp b/Runtime/Camera/LightManager.cpp
new file mode 100644
index 0000000..eaf5d89
--- /dev/null
+++ b/Runtime/Camera/LightManager.cpp
@@ -0,0 +1,635 @@
+#include "UnityPrefix.h"
+#include "LightManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Light.h"
+#include "UnityScene.h"
+#include "CullResults.h"
+#include "BaseRenderer.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Math/SphericalHarmonics.h"
+#include "Runtime/Shaders/ShaderKeywords.h"
+#include "Runtime/Camera/LightProbes.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+static ShaderKeyword kKeywordVertexLight = keywords::Create("VERTEXLIGHT_ON");
+
+static LightManager* s_LightManager = NULL;
+
+LightManager& GetLightManager()
+{
+ DebugAssert (s_LightManager != NULL);
+ return *s_LightManager;
+}
+
+void LightManager::InitializeClass()
+{
+ DebugAssert (s_LightManager == NULL);
+ s_LightManager = new LightManager();
+}
+
+void LightManager::CleanupClass()
+{
+ DebugAssert (s_LightManager != NULL);
+ delete s_LightManager;
+ s_LightManager = NULL;
+}
+
+
+
+
+static float CalculateLightIntensityAtPoint (const Light& light, const Vector3f& position)
+{
+ float lum = light.GetColor().GreyScaleValue() * light.GetIntensity();
+
+ if (light.GetType () == kLightDirectional)
+ {
+ if (light.GetShadows() != kShadowNone)
+ return lum * 16.0f;
+ else
+ return lum;
+ }
+ else
+ {
+ float sqrDistance = SqrMagnitude (position - light.GetWorldPosition());
+ float atten = light.AttenuateApprox (sqrDistance);
+ return lum * atten;
+ }
+}
+
+
+static ColorRGBAf CalculateLightColorAtPointForSH (const Light& light, const AABB& bounds, const AABB& localBounds, float scale)
+{
+ float lum = light.GetIntensity();
+
+ if (light.GetType () == kLightDirectional)
+ {
+ return lum * GammaToActiveColorSpace (light.GetColor());
+ }
+ else
+ {
+ // objects larger than lights should be affected less
+ float objectSizeSqr = SqrMagnitude(localBounds.GetExtent() * scale);
+ float lightSizeSqr = light.GetRange() * light.GetRange();
+ if (objectSizeSqr > lightSizeSqr)
+ {
+ lum *= lightSizeSqr / objectSizeSqr;
+ }
+
+ Vector3f pos = bounds.GetCenter();
+ // don't ever treat SH lights as coming closer to center than at object bounds
+ float sqrDistance = std::max (SqrMagnitude (pos - light.GetWorldPosition()), objectSizeSqr);
+ float atten = light.AttenuateApprox (sqrDistance);
+ return (lum * atten) * GammaToActiveColorSpace (light.GetColor());
+ }
+}
+
+
+LightManager::LightManager ()
+: m_LastMainLight(NULL)
+{
+}
+
+
+LightManager::~LightManager () {
+}
+
+
+
+
+void LightManager::SetupVertexLights (int lightCount, const ActiveLight* const* lights)
+{
+ GfxDevice& device = GetGfxDevice();
+
+ int maxVertexLights = gGraphicsCaps.maxLights;
+ AssertIf(lightCount > maxVertexLights);
+ lightCount = std::min(lightCount, maxVertexLights); // just a safeguard, should not normally happen
+
+ // setup vertex lights
+ for( int i = 0; i < lightCount; ++i )
+ lights[i]->light->SetupVertexLight (i, lights[i]->visibilityFade);
+
+ // disable rest of lights
+ device.DisableLights( lightCount );
+}
+
+void SetSHConstants (const float sh[9][3], BuiltinShaderParamValues& params)
+{
+ Vector4f vCoeff[3];
+
+ static const float s_fSqrtPI = ((float)sqrtf(kPI));
+ const float fC0 = 1.0f/(2.0f*s_fSqrtPI);
+ const float fC1 = (float)sqrt(3.0f)/(3.0f*s_fSqrtPI);
+ const float fC2 = (float)sqrt(15.0f)/(8.0f*s_fSqrtPI);
+ const float fC3 = (float)sqrt(5.0f)/(16.0f*s_fSqrtPI);
+ const float fC4 = 0.5f*fC2;
+
+ int iC;
+ for( iC=0; iC<3; iC++ )
+ {
+ vCoeff[iC].x = -fC1*sh[3][iC];
+ vCoeff[iC].y = -fC1*sh[1][iC];
+ vCoeff[iC].z = fC1*sh[2][iC];
+ vCoeff[iC].w = fC0*sh[0][iC] - fC3*sh[6][iC];
+ }
+
+ params.SetVectorParam(kShaderVecSHAr, vCoeff[0]);
+ params.SetVectorParam(kShaderVecSHAg, vCoeff[1]);
+ params.SetVectorParam(kShaderVecSHAb, vCoeff[2]);
+
+ for( iC=0; iC<3; iC++ )
+ {
+ vCoeff[iC].x = fC2*sh[4][iC];
+ vCoeff[iC].y = -fC2*sh[5][iC];
+ vCoeff[iC].z = 3.0f*fC3*sh[6][iC];
+ vCoeff[iC].w = -fC2*sh[7][iC];
+ }
+
+ params.SetVectorParam(kShaderVecSHBr, vCoeff[0]);
+ params.SetVectorParam(kShaderVecSHBg, vCoeff[1]);
+ params.SetVectorParam(kShaderVecSHBb, vCoeff[2]);
+
+ vCoeff[0].x = fC4*sh[8][0];
+ vCoeff[0].y = fC4*sh[8][1];
+ vCoeff[0].z = fC4*sh[8][2];
+ vCoeff[0].w = 1.0f;
+
+ params.SetVectorParam(kShaderVecSHC, vCoeff[0]);
+}
+
+void LightManager::SetupForwardBaseLights (const ForwardLightsBlock& lights)
+{
+ BuiltinShaderParamValues& params = GetGfxDevice().GetBuiltinParamValues();
+
+ // Setup SH constants
+ SetSHConstants (lights.sh, params);
+
+ // Setup per-vertex light constants
+ Assert (lights.vertexLightCount <= 4);
+ Vector4f packedPosX;
+ Vector4f packedPosY;
+ Vector4f packedPosZ;
+ Vector4f packedAtten;
+ Vector4f lightColors[4];
+
+ const ActiveLight* const* vertexLights = lights.GetLights() + lights.addLightCount;
+ for (int i = 0; i < lights.vertexLightCount; ++i)
+ {
+ Light& light = *vertexLights[i]->light;
+ Vector3f pos = light.GetWorldPosition();
+ float intensity = light.GetIntensity() * 2.0f;
+ if (i == 0 && lights.lastAddLightBlend != 1.0f)
+ intensity *= (1.0f-lights.lastAddLightBlend);
+ else if (i == lights.vertexLightCount-1)
+ intensity *= lights.lastVertexLightBlend;
+ ColorRGBAf color = GammaToActiveColorSpace (light.GetColor()) * intensity;
+ float attenSq = Light::CalcQuadFac (light.GetRange());
+
+ packedPosX.GetPtr()[i] = pos.x;
+ packedPosY.GetPtr()[i] = pos.y;
+ packedPosZ.GetPtr()[i] = pos.z;
+ packedAtten.GetPtr()[i] = attenSq;
+ lightColors[i].Set (color.r, color.g, color.b, color.a);
+ }
+ for (int i = lights.vertexLightCount; i < kMaxForwardVertexLights; ++i)
+ {
+ packedPosX.GetPtr()[i] = 0;
+ packedPosY.GetPtr()[i] = 0;
+ packedPosZ.GetPtr()[i] = 0;
+ packedAtten.GetPtr()[i] = 1;
+ lightColors[i].Set (0,0,0,0);
+ }
+ if (lights.vertexLightCount)
+ {
+ params.SetVectorParam(kShaderVecVertexLightPosX0, packedPosX);
+ params.SetVectorParam(kShaderVecVertexLightPosY0, packedPosY);
+ params.SetVectorParam(kShaderVecVertexLightPosZ0, packedPosZ);
+ params.SetVectorParam(kShaderVecVertexLightAtten0, packedAtten);
+ params.SetVectorParam(kShaderVecLight0Diffuse, lightColors[0]);
+ params.SetVectorParam(kShaderVecLight1Diffuse, lightColors[1]);
+ params.SetVectorParam(kShaderVecLight2Diffuse, lightColors[2]);
+ params.SetVectorParam(kShaderVecLight3Diffuse, lightColors[3]);
+
+ g_ShaderKeywords.Enable (kKeywordVertexLight);
+ }
+ else
+ {
+ g_ShaderKeywords.Disable (kKeywordVertexLight);
+ }
+
+
+ // Setup per-pixel main light constants
+ if (lights.mainLight == NULL)
+ {
+ params.SetVectorParam(kShaderVecLightColor0, Vector4f(0,0,0,0));
+ return;
+ }
+
+ Light* light = lights.mainLight->light;
+ Assert (light->GetType() == kLightDirectional);
+
+ const Transform& transform = light->GetComponent(Transform);
+
+ Vector3f lightDir = transform.TransformDirection (Vector3f(0,0,-1));
+ params.SetVectorParam(kShaderVecWorldSpaceLightPos0, Vector4f(lightDir.x, lightDir.y, lightDir.z, 0.0f));
+
+ Matrix4x4f world2Light = light->GetWorldToLocalMatrix();
+ light->GetMatrix (&world2Light, &params.GetWritableMatrixParam(kShaderMatLightMatrix));
+
+ light->SetLightKeyword ();
+ light->SetPropsToShaderLab (1.0f);
+}
+
+void LightManager::SetupForwardAddLight (Light* light, float blend)
+{
+ Assert (light);
+
+ BuiltinShaderParamValues& params = GetGfxDevice().GetBuiltinParamValues();
+ Vector4f lightInfo;
+ if (light->GetType() != kLightDirectional)
+ {
+ Vector3f worldPos = light->GetWorldPosition();
+ lightInfo.Set (worldPos.x, worldPos.y, worldPos.z, 1.0f);
+ }
+ else
+ {
+ const Transform& transform = light->GetComponent(Transform);
+ Vector3f worldDir = transform.TransformDirection (Vector3f(0,0,-1));
+ lightInfo.Set (worldDir.x, worldDir.y, worldDir.z, 0.0f);
+ }
+ params.SetVectorParam(kShaderVecWorldSpaceLightPos0, lightInfo);
+
+ Matrix4x4f world2Light = light->GetWorldToLocalMatrix();
+ light->GetMatrix (&world2Light, &params.GetWritableMatrixParam(kShaderMatLightMatrix));
+
+ light->SetLightKeyword ();
+ light->SetPropsToShaderLab (blend);
+}
+
+
+
+void LightManager::AddLight (Light* source)
+{
+ DebugAssert (source);
+
+ m_Lights.push_back(*source);
+}
+
+void LightManager::RemoveLight (Light* source)
+{
+ DebugAssert (source && source->IsInList());
+
+ m_Lights.erase(source);
+
+ // If this was the last main light, clear it
+ if (m_LastMainLight == source)
+ m_LastMainLight = NULL;
+}
+
+static float kRenderModeSortBias[Light::kRenderModeCount] = {
+ 0.0f,
+ 1000.0f,
+ -1000.0f,
+};
+
+
+
+
+// Figures out lights for given object, sorts them by intensity.
+// Fills them into an array of CulledLight.
+// Returns number of lights filled.
+
+struct CulledLight
+{
+ UInt32 lightIndex;
+ float sortIntensity;
+ friend bool operator< (const CulledLight& lhs, const CulledLight& rhs)
+ {
+ return lhs.sortIntensity > rhs.sortIntensity;
+ }
+};
+
+static void SortLights (dynamic_array<CulledLight>& outLights, const UInt32* lightIndices, UInt32 lightCount, const ActiveLights& activeLights, const Vector3f& objectCenter)
+{
+ outLights.resize_uninitialized(lightCount);
+ for (size_t i = 0; i < lightCount; i++)
+ {
+ // Light passed culling, add it
+ CulledLight& outLight = outLights[i];
+ outLight.lightIndex = lightIndices[i];
+ const Light& light = *activeLights.lights[outLight.lightIndex].light;
+ int lightRenderMode = light.GetRenderMode ();
+ float blend = CalculateLightIntensityAtPoint (light, objectCenter);
+ outLight.sortIntensity = blend + kRenderModeSortBias[lightRenderMode];
+ }
+
+ // Sort culled lights by intensity (most intensive lights first)
+ std::sort( outLights.begin(), outLights.end() );
+}
+
+UNITY_VECTOR(kMemRenderer, Light*) LightManager::GetLights(LightType type, int layer)
+{
+ UNITY_VECTOR(kMemRenderer, Light*) lights;
+ layer = 1 << layer;
+ for (LightManager::Lights::iterator i= m_Lights.begin();i != m_Lights.end();i++)
+ {
+ Light* light = &*i;
+ if (!light)
+ continue;
+ if (light->GetType() == type && (light->GetCullingMask() & layer) != 0)
+ lights.push_back(light);
+ }
+
+ return lights;
+}
+
+void LightManager::FindVertexLightsForObject (dynamic_array<UInt8>& dest, const UInt32* lightIndices, UInt32 lightCount, const ActiveLights& activeLights, const VisibleNode& node)
+{
+ DebugAssert (node.renderer);
+
+ dynamic_array<CulledLight> culledLights(kMemTempAlloc);
+ SortLights (culledLights, lightIndices, lightCount, activeLights, node.worldAABB.GetCenter ());
+
+ int resultLightCount = std::min<int> (lightCount, std::min<int>(gGraphicsCaps.maxLights,kMaxSupportedVertexLights));
+
+ // allocate block to hold light count & light pointers
+ size_t resultOffset = dest.size();
+ size_t requiredSize = sizeof(VertexLightsBlock) + resultLightCount * sizeof(Light*);
+ dest.resize_uninitialized(resultOffset + requiredSize);
+ VertexLightsBlock* outBlock = reinterpret_cast<VertexLightsBlock*>(&dest[resultOffset]);
+ const ActiveLight** outLights = reinterpret_cast<const ActiveLight**>(outBlock + 1);
+ outBlock->lightCount = resultLightCount;
+ for (int i = 0; i < resultLightCount; ++i)
+ outLights[i] = &activeLights.lights[culledLights[i].lightIndex];
+}
+
+
+
+static void AddLightToSH (const VisibleNode& node, const Light& source, ForwardLightsBlock* lights, float blend)
+{
+ Vector3f lightDir;
+ if (source.GetType() != kLightDirectional)
+ {
+ lightDir = NormalizeSafe(source.GetWorldPosition() - node.worldAABB.GetCenter());
+ }
+ else
+ {
+ const Transform& lightTransform = source.GetComponent (Transform);
+ lightDir = lightTransform.TransformDirection (Vector3f(0,0,-1));
+ }
+
+ ColorRGBAf color = CalculateLightColorAtPointForSH (source, node.worldAABB, node.localAABB, 1.0f/node.invScale) * (blend * 2.0f);
+
+ float shR[9], shG[9], shB[9];
+ SHEvalDirectionalLight9 (
+ lightDir.x, lightDir.y, lightDir.z,
+ color.r, color.g, color.b,
+ shR, shG, shB);
+ for (int i = 0; i < 9; ++i) {
+ lights->sh[i][0] += shR[i];
+ lights->sh[i][1] += shG[i];
+ lights->sh[i][2] += shB[i];
+ }
+}
+
+
+// Light we want to blend is L1 (with L0 the brighter one before it, and L2 the dimmer one after it).
+// Blend is: (L1-L2)/(L0-L2)
+static inline bool CalculateLightBlend (const CulledLight* lights, int lightsCount, int index, float* outBlend)
+{
+ if (index <= 0 || index >= lightsCount-1)
+ return false;
+
+ float l0 = lights[index-1].sortIntensity;
+ float l1 = lights[index ].sortIntensity;
+ float l2 = lights[index+1].sortIntensity;
+ if (l0 - l2 >= kRenderModeSortBias[Light::kRenderImportant])
+ return false;
+
+ *outBlend = clamp01 ((l1 - l2) / (l0 - l2 + 0.001f));
+ return true;
+}
+
+
+static void CrossBlendForwardLights (
+ CulledLight* culledLights,
+ const ActiveLights& activeLights,
+ int culledLightsCount,
+ int lastAutoAddLightIndex,
+ bool lightmappedObject,
+ dynamic_array<UInt8>& dest,
+ size_t resultOffset,
+ const VisibleNode& node
+ )
+{
+ ForwardLightsBlock* lights = reinterpret_cast<ForwardLightsBlock*>(&dest[resultOffset]);
+ int lastVertexLightIndex = lights->addLightCount + lights->vertexLightCount-1;
+
+ // How much we need to blend the last additive light?
+ lights->lastAddLightBlend = 1.0f;
+ bool blendLastAddLight = CalculateLightBlend (culledLights, culledLightsCount, lastAutoAddLightIndex, &lights->lastAddLightBlend);
+ if (blendLastAddLight && !lightmappedObject)
+ {
+ // For non-lightmapped objects, we need to add oppositely blended
+ // vertex or SH light.
+ UInt32 blendIndex = culledLights[lastAutoAddLightIndex].lightIndex;
+ const Light& blendLight = *activeLights.lights[blendIndex].light;
+ if (blendLight.GetType() != kLightDirectional)
+ {
+ // Non-directional light: insert one vertex light
+ dest.resize_uninitialized(dest.size() + sizeof(Light*));
+ lights = reinterpret_cast<ForwardLightsBlock*>(&dest[resultOffset]); // could result in reallocation; recalculate block pointer
+ const ActiveLight** lightPtrs = reinterpret_cast<const ActiveLight**>(lights + 1); // ActiveLight array begins at end of struct
+ // move all vertex lights
+ for (int i = lights->addLightCount+lights->vertexLightCount-1; i >= lights->addLightCount-1; --i)
+ lightPtrs[i+1] = lightPtrs[i];
+ Assert (lights->vertexLightCount <= kMaxForwardVertexLights);
+ ++lights->vertexLightCount;
+ if (lights->vertexLightCount > kMaxForwardVertexLights)
+ {
+ --lastVertexLightIndex;
+ lights->vertexLightCount = kMaxForwardVertexLights;
+ }
+ }
+ else
+ {
+ // Directional light: add to SH
+ AddLightToSH (node, blendLight, lights, 1.0f-lights->lastAddLightBlend);
+ }
+ }
+
+ // If we have vertex lights, we might want to blend last one
+ if (lights->vertexLightCount > 0)
+ {
+ float vertexBlend;
+ lights->lastVertexLightBlend = 1.0f;
+ bool blendLastVertexLight = CalculateLightBlend (culledLights, culledLightsCount, lastVertexLightIndex, &vertexBlend);
+ if (blendLastVertexLight)
+ lights->lastVertexLightBlend = vertexBlend;
+ }
+}
+
+static size_t CountNonOffscreenVertexLights (const UInt32* lightIndices, UInt32 lightCount, const ActiveLights& activeLights)
+{
+ size_t visibleLightCount = 0;
+ for (visibleLightCount=0;visibleLightCount<lightCount;visibleLightCount++)
+ {
+ if (activeLights.lights[lightIndices[visibleLightCount]].isOffscreenVertexLight)
+ break;
+ }
+
+ for (int j=visibleLightCount;j<lightCount;j++)
+ {
+ DebugAssert(activeLights.lights[lightIndices[j]].isOffscreenVertexLight);
+ }
+
+
+ return visibleLightCount;
+}
+
+void LightManager::FindForwardLightsForObject (dynamic_array<UInt8>& dest, const UInt32* lightIndices, UInt32 lightCount, const ActiveLights& activeLights, const VisibleNode& node, bool lightmappedObject, bool dualLightmapsMode, bool useVertexLights, int maxAutoAddLights, bool disableAddLights, const ColorRGBAf& ambient)
+{
+ DebugAssert (node.renderer);
+
+ // cull and sort lights into temporary memory block
+ Renderer* renderer = static_cast<Renderer*>(node.renderer);
+ UInt32 layerMask = renderer->GetLayerMask();
+ const bool isUsingLightProbes = node.renderer->GetRendererType() != kRendererIntermediate && renderer->GetUseLightProbes() && LightProbes::AreBaked();
+ const bool directLightFromLightProbes = isUsingLightProbes && !dualLightmapsMode;
+
+ dynamic_array<CulledLight> culledLights(kMemTempAlloc);
+
+ // If we don't support vertex lights we can skip rendering any offscreen lights completely (offscreen lights always come after visible lights in the index list)
+ if (!useVertexLights)
+ lightCount = CountNonOffscreenVertexLights (lightIndices, lightCount, activeLights);
+
+
+ SortLights (culledLights, lightIndices, lightCount, activeLights, node.worldAABB.GetCenter ());
+
+ // put ForwardLightsBlock header structure into buffer
+ size_t resultOffset = dest.size();
+ size_t arrayOffset = resultOffset + sizeof(ForwardLightsBlock);
+ dest.resize_uninitialized(arrayOffset);
+ ForwardLightsBlock* lights = reinterpret_cast<ForwardLightsBlock*>(&dest[resultOffset]);
+ lights->addLightCount = 0;
+ lights->vertexLightCount = 0;
+ lights->mainLight = NULL;
+ lights->lastAddLightBlend = 1.0f;
+ lights->lastVertexLightBlend = 1.0f;
+
+ if (useVertexLights)
+ {
+ // if we want vertex lights as result, just take N brightest ones
+ int resultLightCount = std::min<int> (lightCount, std::min<int>(gGraphicsCaps.maxLights,kMaxSupportedVertexLights));
+
+ // set SH to zero (not really used for rendering, but having garbage there would break batches)
+ memset (lights->sh, 0, sizeof(lights->sh));
+
+ // allocate block to hold light pointers
+ dest.resize_uninitialized(arrayOffset + sizeof(ActiveLight*) * resultLightCount);
+ // could result in reallocation; recalculate block pointer
+ lights = reinterpret_cast<ForwardLightsBlock*>(&dest[resultOffset]);
+ const ActiveLight** lightPtrs = reinterpret_cast<const ActiveLight**>(lights + 1);
+ lights->vertexLightCount = resultLightCount;
+ for (int i = 0; i < resultLightCount; ++i)
+ lightPtrs[i] = &activeLights.lights[culledLights[i].lightIndex];
+ }
+ else
+ {
+ // we want main light + SH + additive lights as result
+
+ // Main light if any will be first one in activeLights
+ const ActiveLight* mainLight = NULL;
+ if (activeLights.hasMainLight )
+ mainLight = &activeLights.lights[0];
+
+ // Take globally main light if that fits our layer mask, it does not have a cookie, and it's not an auto light while we're using probes
+ if (mainLight && (mainLight->cullingMask & layerMask) != 0 && !mainLight->hasCookie &&
+ !(directLightFromLightProbes && mainLight->lightmappingForRender == Light::kLightmappingAuto))
+ lights->mainLight = mainLight;
+
+ // put ambient into SH
+ SHEvalAmbientLight(ambient, &lights->sh[0][0]);
+ for (int i = 1; i < 9; ++i) {
+ lights->sh[i][0] = 0.0f;
+ lights->sh[i][1] = 0.0f;
+ lights->sh[i][2] = 0.0f;
+ }
+
+ // go over result lights and place them
+ int lastAutoAddLightIndex = -1;
+ for (int i = 0; i < lightCount; ++i)
+ {
+ UInt32 lightIndex = culledLights[i].lightIndex;
+ const ActiveLight& activeLight = activeLights.lights[lightIndex];
+ const int lightRenderMode = activeLight.lightRenderMode;
+ const int lightmappingMode = activeLight.lightmappingForRender;
+
+ Assert(!activeLight.isOffscreenVertexLight);
+
+ // BasePass for lightmapped objects has no code for run-time lighting
+ // therefore we have to promote RuntimeOnly directional lights to Additive pass
+ // (This bug was fixed in 3.5. Thus we keep behaviour in the webplayer)
+ bool forceAdditive = lightmappedObject && lightmappingMode == Light::kLightmappingRealtimeOnly;
+ forceAdditive &= IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_5_a1);
+
+ // If this is already set as main light: skip it
+ if (lights->mainLight && lightIndex == 0 && !forceAdditive)
+ {
+ // nothing
+ }
+ // If we don't have main light yet and this could be it: do it
+ else if (lights->mainLight == NULL && activeLight.lightType == kLightDirectional && lightRenderMode != Light::kRenderNotImportant && !activeLight.hasCookie && !forceAdditive)
+ {
+ lights->mainLight = &activeLight;
+ }
+ // If it's an important light, add it to additive lights
+ else if (((lightRenderMode == Light::kRenderImportant) || (lightRenderMode!=Light::kRenderNotImportant && lights->addLightCount < maxAutoAddLights)) && !disableAddLights)
+ {
+ // now that we know it will not be rendered as a vertex light, we can check if it's actually visible;
+ // can't do that for vertex lights, as they influence object past the range
+ size_t curOffset = dest.size();
+ dest.resize_uninitialized(curOffset + sizeof(ActiveLight*));
+ *reinterpret_cast<const ActiveLight**>(&dest[curOffset]) = &activeLight;
+ // could result in reallocation; recalculate block pointer
+ lights = reinterpret_cast<ForwardLightsBlock*>(&dest[resultOffset]);
+ ++lights->addLightCount;
+ if (lights->addLightCount == maxAutoAddLights && lightRenderMode != Light::kRenderImportant)
+ lastAutoAddLightIndex = i;
+ }
+ // All Vertex/SH lights for non-lightmapped objects
+ else if (!lightmappedObject)
+ {
+ // Add some non-directional lights as vertex lights
+ if (activeLight.lightType != kLightDirectional && lights->vertexLightCount < kMaxForwardVertexLights)
+ {
+ size_t curOffset = dest.size();
+ dest.resize_uninitialized(curOffset + sizeof(Light*));
+ *reinterpret_cast<const ActiveLight**>(&dest[curOffset]) = &activeLight;
+ // could result in reallocation; recalculate block pointer
+ lights = reinterpret_cast<ForwardLightsBlock*>(&dest[resultOffset]);
+ ++lights->vertexLightCount;
+ }
+ // Otherwise, add light to SH
+ else
+ {
+ AddLightToSH (node, *activeLight.light, lights, 1.0f);
+ }
+ }
+ }
+
+ // Blend light transitions: full to vertex lit; and vertex lit to SH.
+ CrossBlendForwardLights (culledLights.data(), activeLights, lightCount, lastAutoAddLightIndex, lightmappedObject, dest, resultOffset, node);
+
+ if (isUsingLightProbes)
+ {
+ float coefficients[kLightProbeCoefficientCount];
+ GetLightProbes()->GetInterpolatedLightProbe(renderer->GetLightProbeInterpolationPosition(node.worldAABB), renderer, &coefficients[0]);
+ for (int i = 0; i < kLightProbeCoefficientCount; i++)
+ {
+ lights->sh[0][i] += coefficients[i];
+ }
+ }
+ }
+}
diff --git a/Runtime/Camera/LightManager.h b/Runtime/Camera/LightManager.h
new file mode 100644
index 0000000..1a154f8
--- /dev/null
+++ b/Runtime/Camera/LightManager.h
@@ -0,0 +1,62 @@
+#pragma once
+
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/GfxDevice/GfxDeviceConfigure.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Camera/CullResults.h"
+#include "Runtime/Camera/LightTypes.h"
+
+class Light;
+struct ShadowCullData;
+class ColorRGBAf;
+class BuiltinShaderParamValues;
+namespace Umbra { class OcclusionBuffer; }
+
+const int kMaxForwardVertexLights = 4;
+
+// We reserve light buffers for this number of lights per object (in forward/vertex lit)
+const int kEstimatedLightsPerObject = 3;
+
+
+class LightManager
+{
+public:
+ LightManager ();
+ ~LightManager ();
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ // Figures out and sorts lights for the object
+ // Puts results in dest as VertexLightsBlock + variable number of lights
+ void FindVertexLightsForObject (dynamic_array<UInt8>& dest, const UInt32* lightIndices, UInt32 lightCount, const ActiveLights& activeLights, const VisibleNode& node);
+
+ // Figures out and sorts lights for the object
+ // Puts results in dest as ForwardLightsBlock + variable number of lights
+ void FindForwardLightsForObject (dynamic_array<UInt8>& dest, const UInt32* lightIndices, UInt32 lightCount, const ActiveLights& activeLights, const VisibleNode& node, bool lightmappedObject, bool dualLightmapsMode, bool useVertexLights, int maxAutoAddLights, bool disableAddLights, const ColorRGBAf& ambient);
+
+ static void SetupVertexLights (int lightCount, const ActiveLight* const* lights);
+
+ static void SetupForwardBaseLights (const ForwardLightsBlock& lights);
+ static void SetupForwardAddLight (Light* light, float blend);
+
+ void AddLight (Light* source);
+ void RemoveLight (Light* source);
+
+ typedef List<Light> Lights;
+ Lights& GetAllLights () { return m_Lights; }
+ const Lights& GetAllLights () const { return m_Lights; }
+
+ ///Terrain engine only
+ UNITY_VECTOR(kMemRenderer, Light*) GetLights (LightType type, int layer);
+
+ Light* GetLastMainLight() { return m_LastMainLight; }
+
+
+private:
+ Lights m_Lights;
+ Light* m_LastMainLight; // brightest directional light found in last render
+};
+
+void SetSHConstants (const float sh[9][3], BuiltinShaderParamValues& params);
+LightManager& GetLightManager();
diff --git a/Runtime/Camera/LightProbes.cpp b/Runtime/Camera/LightProbes.cpp
new file mode 100644
index 0000000..43f338c
--- /dev/null
+++ b/Runtime/Camera/LightProbes.cpp
@@ -0,0 +1,350 @@
+#include "UnityPrefix.h"
+#include "LightProbes.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Graphics/LightmapSettings.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Polynomials.h"
+#if UNITY_EDITOR
+#include "Editor/Src/LightmapperLightProbes.h"
+#endif
+
+template<class T>
+void LightProbeCoefficients::Transfer (T& transfer)
+{
+ TRANSFER(sh[0]);
+ TRANSFER(sh[1]);
+ TRANSFER(sh[2]);
+ TRANSFER(sh[3]);
+ TRANSFER(sh[4]);
+ TRANSFER(sh[5]);
+ TRANSFER(sh[6]);
+ TRANSFER(sh[7]);
+ TRANSFER(sh[8]);
+ TRANSFER(sh[9]);
+ TRANSFER(sh[10]);
+ TRANSFER(sh[11]);
+ TRANSFER(sh[12]);
+ TRANSFER(sh[13]);
+ TRANSFER(sh[14]);
+ TRANSFER(sh[15]);
+ TRANSFER(sh[16]);
+ TRANSFER(sh[17]);
+ TRANSFER(sh[18]);
+ TRANSFER(sh[19]);
+ TRANSFER(sh[20]);
+ TRANSFER(sh[21]);
+ TRANSFER(sh[22]);
+ TRANSFER(sh[23]);
+ TRANSFER(sh[24]);
+ TRANSFER(sh[25]);
+ TRANSFER(sh[26]);
+}
+
+template<class T>
+void Tetrahedron::Transfer (T& transfer)
+{
+ TRANSFER(indices[0]);
+ TRANSFER(indices[1]);
+ TRANSFER(indices[2]);
+ TRANSFER(indices[3]);
+ TRANSFER(neighbors[0]);
+ TRANSFER(neighbors[1]);
+ TRANSFER(neighbors[2]);
+ TRANSFER(neighbors[3]);
+ TRANSFER(matrix);
+}
+
+template<class T>
+void LightProbes::Transfer (T& transfer)
+{
+ Super::Transfer(transfer);
+ transfer.Transfer(m_Data.positions, "bakedPositions", kHideInEditorMask);
+ transfer.Transfer(m_Data.coefficients, "bakedCoefficients");
+ transfer.Transfer(m_Data.tetrahedra, "tetrahedra", kHideInEditorMask);
+ transfer.Transfer(m_Data.hullRays, "hullRays", kHideInEditorMask);
+}
+
+LightProbes::LightProbes(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+LightProbes::~LightProbes ()
+{
+}
+
+void LightProbes::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad(awakeMode);
+}
+
+#if UNITY_EDITOR
+void LightProbes::SetBakedData( const dynamic_array<Vector3f>& positions, const dynamic_array<LightProbeCoefficients>& coefficents )
+{
+ m_Data.coefficients = coefficents;
+ m_Data.positions = positions;
+ LightProbeUtils::Tetrahedralize(m_Data);
+ SetDirty();
+}
+#endif
+
+LightProbes* GetLightProbes ()
+{
+ return GetLightmapSettings().GetLightProbes();
+}
+
+static inline float TriArea2D(float x1, float y1, float x2, float y2, float x3, float y3)
+{
+ return (x1-x2)*(y2-y3) - (x2-x3)*(y1-y2);
+}
+
+// Taken from Real-Time Collision Detection
+static inline void BarycentricCoordinates3DTriangle(const Vector3f tri[3], const Vector3f& p, Vector4f& coords)
+{
+ // Unnormalized(!) triangle normal
+ Vector3f normal = Cross(tri[1] - tri[0], tri[2] - tri[0]);
+ // Nominators and one-over-denominator for u and v ratios
+ float nu, nv, ood;
+ // Absolute components for determining projection plane
+ float x = Abs(normal.x), y = Abs(normal.y), z = Abs(normal.z);
+
+ // Compute areas in plane of largest projection
+ if (x >= y && x >= z) {
+ // x is largest, project to the yz plane
+ nu = TriArea2D(p.y, p.z, tri[1].y, tri[1].z, tri[2].y, tri[2].z); // Area of PBC in yz plane
+ nv = TriArea2D(p.y, p.z, tri[2].y, tri[2].z, tri[0].y, tri[0].z); // Area of PCA in yz plane
+ ood = 1.0f / normal.x; // 1/(2*area of ABC in yz plane)
+ } else if (y >= x && y >= z) {
+ // y is largest, project to the xz plane
+ nu = TriArea2D(p.x, p.z, tri[1].x, tri[1].z, tri[2].x, tri[2].z);
+ nv = TriArea2D(p.x, p.z, tri[2].x, tri[2].z, tri[0].x, tri[0].z);
+ ood = 1.0f / -normal.y;
+ } else {
+ // z is largest, project to the xy plane
+ nu = TriArea2D(p.x, p.y, tri[1].x, tri[1].y, tri[2].x, tri[2].y);
+ nv = TriArea2D(p.x, p.y, tri[2].x, tri[2].y, tri[0].x, tri[0].y);
+ ood = 1.0f / normal.z;
+ }
+ coords.x = nu * ood;
+ coords.y = nv * ood;
+ coords.z = 1.0f - coords.x - coords.y;
+}
+
+// Warped prism is an outer open cell (a.k.a. outer tetrahedron) formed by a tri face of the convex hull and
+// 3 vertex normals. There are no additional constraints on the normals other than they have point in the
+// half-space above the face, so the prism can be warped and the function will still project correctly.
+// This is done by solving the equation
+// P = a*(V0*t+P0) + b*(V1*t+P1) + (1-a-b)*(V2*t+P2)
+// where V_ is the vertex normal, P_ the hull vertex, so V_*t+P_ is a hull ray for t >= 0
+// P is the point we want to find the three barycentric coordinates for, so a, b and c=1-a-b
+// If we substitute
+// A = P0 - P2, Ap = V0 - V2, B = P1 - P2, Bp = V1 - V2, C = P - P2, Cp = -V2,
+// we can rewrite as:
+// a*(A + t*Ap) + b*(B + t*Bp) = C + t*Cp
+// or, as a matrix equation:
+// A.x + t*Ap.x, B.x + t*Bp.x, C.x + t*Cp.x a 0
+// [ A.y + t*Ap.y, B.y + t*Bp.y, C.y + t*Cp.y ] [ b ] = [ 0 ]
+// A.z + t*Ap.z, B.z + t*Bp.z, C.z + t*Cp.z -1 0
+// which only has a solution, if the determinant is 0, which gives a cubic in t.
+// The geometrical interpretation shows that there should be exactly one positive root for that cubic
+// and that's our t. (There might be two real negative roots too, but we don't care).
+// Finally we can find a and b either by substituting t into the two equations, or plug t
+// into the hull rays V_*t+P_ and use BarycentricCoordinates3DTriangle()
+inline void GetBarycentricCoordinatesForOuterCell (const dynamic_array<Vector3f>& bakedPositions, const dynamic_array<Vector3f>& hullRays, const Vector3f& p, const Tetrahedron& tet, Vector4f& coords, float& t)
+{
+ // It's an outer tetrahedron;
+ // Take the position relative to one of the triangle points (say, 0th) and dot it with normal
+ const int (&ind)[4] = tet.indices;
+ const Vector3f& v0 = bakedPositions[ind[0]];
+ const Vector3f edge0 = bakedPositions[ind[1]] - v0;
+ const Vector3f edge1 = bakedPositions[ind[2]] - v0;
+ // We could cache the normal... or not.
+ const Vector3f normal = Cross(edge1, edge0);
+ t = Dot(p - v0, normal);
+ if (t < 0)
+ {
+ // p is below the hull surface of this tetrahedron, so let's just return the 4th barycentric coordinate
+ // as the lowest (and negative), so that the tetrahedron adjacent at the base gets tested next
+ coords.Set(0, 0, 0, -1);
+ return;
+ }
+
+ // CalculateOuterTetrahedraMatrices() prepares the Tetrahedron.matrix, so that
+ // the coefficients of the cubic can be found just like that:
+ Vector3f polyCoeffs = tet.matrix.MultiplyPoint3(p);
+ // If the polynomial degenerated to quadratic, the unused ind[3] will be set to -2 instead of -1
+ t = ind[3] == -1 ? CubicPolynomialRoot(polyCoeffs.x, polyCoeffs.y, polyCoeffs.z) : QuadraticPolynomialRoot(polyCoeffs.x, polyCoeffs.y, polyCoeffs.z);
+
+ // We could directly calculate the barycentric coords by plugging t into
+ // a*(A + t*Ap) + b*(B + t*Bp) = C + t*Cp, checking which coord to ignore
+ // and using the two other equations, but it's actually almost the same
+ // as using BarycentricCoordinates3DTriangle()
+
+ Vector3f tri[3];
+ tri[0] = bakedPositions[ind[0]] + hullRays[ind[0]]*t;
+ tri[1] = bakedPositions[ind[1]] + hullRays[ind[1]]*t;
+ tri[2] = bakedPositions[ind[2]] + hullRays[ind[2]]*t;
+ BarycentricCoordinates3DTriangle(tri, p, coords);
+ coords.w = 0;
+}
+
+inline void GetBarycentricCoordinatesForInnerTetrahedron (const dynamic_array<Vector3f>& bakedPositions, const Vector3f& p, const Tetrahedron& tet, Vector4f& coords)
+{
+ // It's an inner tetrahedron, just use the precalculated matrix to find the barycentric coordinates
+ Vector3f mult = tet.matrix.MultiplyVector3(p - bakedPositions[tet.indices[3]]);
+ coords.x = mult.x;
+ coords.y = mult.y;
+ coords.z = mult.z;
+ coords.w = 1.0f - mult.x - mult.y - mult.z;
+}
+
+void GetBarycentricCoordinates (const dynamic_array<Vector3f>& bakedPositions, const dynamic_array<Vector3f>& hullRays, const Vector3f& p, const Tetrahedron& tet, Vector4f& coords, float& t)
+{
+ if (tet.indices[3] >= 0)
+ GetBarycentricCoordinatesForInnerTetrahedron (bakedPositions, p, tet, coords);
+ else
+ GetBarycentricCoordinatesForOuterCell (bakedPositions, hullRays, p, tet, coords, t);
+}
+
+void GetLightProbeInterpolationWeights (const LightProbeData& data, const Vector3f& position, int& tetIndex, Vector4f& weights, float& t, int& steps)
+{
+ // If we don't have an initial guess, always start from tetrahedron 0.
+ // Tetrahedron 0 is picked to be roughly in the center of the probe cloud,
+ // to minimize the number of steps to any other tetrahedron.
+ const int tetCount = data.tetrahedra.size();
+ if (tetIndex < 0 || tetIndex >= tetCount)
+ tetIndex = 0;
+
+ steps = 0;
+ int prev = -1, prevprev = -1;
+ for (; steps < tetCount; steps++)
+ {
+ // Check if we're in the current "best guess" tetrahedron
+ const Tetrahedron& tet = data.tetrahedra[tetIndex];
+ GetBarycentricCoordinates(data.positions, data.hullRays, position, tet, weights, t);
+ if (weights.x >= 0.0f && weights.y >= 0.0f && weights.z >= 0.0f && weights.w >= 0.0f)
+ {
+ // Success!
+ return;
+ }
+
+
+ // There's a chance the position lies "between" two tetrahedra, i.e. both return a slightly negative weight
+ // due to numerical errors and we ping-pong between them.
+ if (tetIndex == prevprev)
+ return;
+
+ prevprev = prev;
+ prev = tetIndex;
+
+ // Otherwise find the smallest barycentric coord and move in that direction
+ if (weights.x < weights.y && weights.x < weights.z && weights.x < weights.w)
+ tetIndex = tet.neighbors[0];
+ else if (weights.y < weights.z && weights.y < weights.w)
+ tetIndex = tet.neighbors[1];
+ else if (weights.z < weights.w)
+ tetIndex = tet.neighbors[2];
+ else
+ tetIndex = tet.neighbors[3];
+ }
+}
+
+void InterpolateLightProbeCoefficients(const dynamic_array<LightProbeCoefficients>& bakedCoefficients, const Tetrahedron& tet, const Vector4f& weights, float* coefficients)
+{
+ // Outer tetrahedra don't have a fourth probe
+ int probeCount = tet.indices[3] < 0 ? 3 : 4;
+
+ for (int i = 0; i < probeCount; i++)
+ {
+ int probeIndex = tet.indices[i];
+ float probeWeight = weights[i];
+ for (int j = 0; j < kLightProbeCoefficientCount; j++)
+ {
+ coefficients[j] += bakedCoefficients[probeIndex].sh[j] * probeWeight;
+ }
+ }
+}
+
+void LightProbes::GetInterpolatedLightProbe (const Vector3f& position, Renderer* renderer, float* coefficients, int& tetIndex, Vector4f& weights, float& t)
+{
+ // Init to black
+ memset (coefficients, 0, sizeof(float)*kLightProbeCoefficientCount);
+
+ // If there are no probes baked
+ const int tetCount = m_Data.tetrahedra.size();
+ if (tetCount == 0)
+ {
+ tetIndex = -1;
+ weights.Set(0,0,0,0);
+ return;
+ }
+
+ // Use tetIndex as an initial guess; if it's not set, check for it in the renderer
+ if ((tetIndex < 0 || tetIndex >= tetCount) && renderer != NULL)
+ tetIndex = renderer->GetLastLightProbeTetIndex();
+
+ int steps;
+ GetLightProbeInterpolationWeights(m_Data, position, tetIndex, weights, t, steps);
+
+ // Return black if we're not within any tetrahedron. It should never happen.
+ if (tetIndex < 0 || tetIndex >= tetCount)
+ return;
+
+ // Tetrahedron found, set it's index to the renderer, to be used as a good guess for the next frame
+ if (renderer)
+ renderer->SetLastLightProbeTetIndex(tetIndex);
+
+ // Use the weights to actually interpolate the probes and get the coefficients
+ InterpolateLightProbeCoefficients(m_Data.coefficients, m_Data.tetrahedra[tetIndex], weights, coefficients);
+}
+
+void LightProbes::SetCoefficients( float* data, int size )
+{
+ int count = m_Data.positions.size();
+ if ( count * kLightProbeCoefficientCount != size || !data)
+ {
+ ErrorString(Format("Number of coefficient sets (%i) has to be equal to current light probe count (%i).", size/kLightProbeCoefficientCount, count));
+ return;
+ }
+ LightProbeCoefficients* lpData = reinterpret_cast<LightProbeCoefficients*>(data);
+ m_Data.coefficients.assign(lpData, lpData + count);
+ SetDirty();
+}
+
+IMPLEMENT_CLASS_HAS_INIT (LightProbes)
+IMPLEMENT_OBJECT_SERIALIZE (LightProbes)
+
+Matrix3x4f::Matrix3x4f( const Matrix3x3f& other )
+{
+ memcpy(&m_Data[0], &other.m_Data[0], sizeof(Matrix3x3f));
+ m_Data[9] = m_Data[10] = m_Data[11] = 0.0f;
+}
+
+inline Vector3f Matrix3x4f::MultiplyVector3( const Vector3f& v ) const
+{
+ Vector3f res;
+ res.x = m_Data[0] * v.x + m_Data[3] * v.y + m_Data[6] * v.z;
+ res.y = m_Data[1] * v.x + m_Data[4] * v.y + m_Data[7] * v.z;
+ res.z = m_Data[2] * v.x + m_Data[5] * v.y + m_Data[8] * v.z;
+ return res;
+}
+
+inline Vector3f Matrix3x4f::MultiplyPoint3( const Vector3f& v ) const
+{
+ Vector3f res;
+ res.x = m_Data[0] * v.x + m_Data[3] * v.y + m_Data[6] * v.z + m_Data[9];
+ res.y = m_Data[1] * v.x + m_Data[4] * v.y + m_Data[7] * v.z + m_Data[10];
+ res.z = m_Data[2] * v.x + m_Data[5] * v.y + m_Data[8] * v.z + m_Data[11];
+ return res;
+}
+
+template<class TransferFunction> inline
+void Matrix3x4f::Transfer (TransferFunction& t)
+{
+ t.Transfer (m_Data[0], "e00"); t.Transfer (m_Data[3], "e01"); t.Transfer (m_Data[6], "e02"); t.Transfer (m_Data[ 9], "e03");
+ t.Transfer (m_Data[1], "e10"); t.Transfer (m_Data[4], "e11"); t.Transfer (m_Data[7], "e12"); t.Transfer (m_Data[10], "e13");
+ t.Transfer (m_Data[2], "e20"); t.Transfer (m_Data[5], "e21"); t.Transfer (m_Data[8], "e22"); t.Transfer (m_Data[11], "e23");
+}
diff --git a/Runtime/Camera/LightProbes.h b/Runtime/Camera/LightProbes.h
new file mode 100644
index 0000000..cd59f9b
--- /dev/null
+++ b/Runtime/Camera/LightProbes.h
@@ -0,0 +1,98 @@
+#pragma once
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Vector4.h"
+#include "Runtime/Math/Matrix3x3.h"
+
+class Renderer;
+
+// column-major
+// m0 m3 m6 m9
+// m1 m4 m7 m10
+// m2 m5 m8 m11
+// So far only used for light probes, move it to Matrix3x4f.h if it ever grows beyond that
+class Matrix3x4f
+{
+public:
+ float m_Data[12];
+ ///@todo: Can't be Transfer optimized because Transfer doesn't write the same as memory layout
+ DECLARE_SERIALIZE_NO_PPTR (Matrix3x4f)
+ Matrix3x4f () {}
+ Matrix3x4f (const Matrix3x3f& other);
+ float& operator [] (int index) { return m_Data[index];}
+ Vector3f MultiplyVector3 (const Vector3f& v) const;
+ Vector3f MultiplyPoint3 (const Vector3f& v) const;
+};
+
+enum { kLightProbeBasisCount = 9};
+enum { kLightProbeCoefficientCount = kLightProbeBasisCount*3};
+
+struct LightProbeCoefficients
+{
+ float sh[kLightProbeBasisCount*3];
+ float& operator [] (int i) { return sh[i]; }
+ DECLARE_SERIALIZE(LightmapData)
+};
+
+struct Tetrahedron
+{
+ int indices[4];
+ int neighbors[4];
+ // For an inner tetrahedron: the matrix is the cached inverted matrix used for calculating barycentric coordinates
+ // For an outer tetrahedron: it's a matrix allowing the calculation of the cubic coefficients for the
+ // t parameter polynomial, used for calculating barycentric coordinates as well
+ Matrix3x4f matrix;
+ DECLARE_SERIALIZE(Tetrahedron)
+};
+
+struct LightProbeData
+{
+ dynamic_array<Vector3f> positions;
+ dynamic_array<LightProbeCoefficients> coefficients;
+ dynamic_array<Tetrahedron> tetrahedra;
+ // TODO: Sort probes so that all outer probes are at the beginning of m_BakedPositions. Thanks to that m_HullRays will be only
+ // as long as there are outer probes, not all probes.
+ dynamic_array<Vector3f> hullRays;
+};
+
+class LightProbes;
+LightProbes* GetLightProbes ();
+
+class LightProbes : public NamedObject
+{
+public:
+ REGISTER_DERIVED_CLASS (LightProbes, NamedObject)
+ DECLARE_OBJECT_SERIALIZE(LightProbes)
+
+ LightProbes(MemLabelId label, ObjectCreationMode mode);
+
+ void AwakeFromLoad(AwakeFromLoadMode mode);
+ Vector3f* GetPositions() { return m_Data.positions.size() > 0 ? &m_Data.positions[0] : NULL; }
+ int GetPositionsSize() { return m_Data.positions.size(); }
+ LightProbeCoefficients* GetCoefficients() { return m_Data.coefficients.size() > 0 ? &m_Data.coefficients[0] : NULL; }
+ void SetCoefficients( float* data, int size );
+ int GetTetrahedraSize() { return m_Data.tetrahedra.size();}
+#if UNITY_EDITOR
+ Tetrahedron* GetTetrahedra() { return m_Data.tetrahedra.size() > 0 ? &m_Data.tetrahedra[0] : NULL;}
+ Vector3f* GetHullRays() { return m_Data.hullRays.size() > 0 ? &m_Data.hullRays[0] : NULL; }
+ void SetBakedData (const dynamic_array<Vector3f>& positions, const dynamic_array<LightProbeCoefficients>& coefficents);
+#endif
+ inline void GetInterpolatedLightProbe(const Vector3f& position, Renderer* renderer, float* coefficients)
+ {
+ int tetIndex = -1;
+ Vector4f weights;
+ float t;
+ GetInterpolatedLightProbe(position, renderer, coefficients, tetIndex, weights, t);
+ }
+ void GetInterpolatedLightProbe(const Vector3f& position, Renderer* renderer, float* coefficients, int& tetIndex, Vector4f& weights, float& t);
+
+ static void InitializeClass() {}
+ static void CleanupClass() {}
+ inline static bool AreBaked() { LightProbes* lp = GetLightProbes(); return lp && lp->GetTetrahedraSize() > 0; }
+
+private:
+ LightProbeData m_Data;
+};
+
diff --git a/Runtime/Camera/LightTypes.h b/Runtime/Camera/LightTypes.h
new file mode 100644
index 0000000..189236d
--- /dev/null
+++ b/Runtime/Camera/LightTypes.h
@@ -0,0 +1,28 @@
+#pragma once
+
+struct ActiveLight;
+
+struct ForwardLightsBlock
+{
+ float sh[9][3];
+ const ActiveLight* mainLight;
+ int addLightCount;
+ int vertexLightCount;
+ float lastAddLightBlend;
+ float lastVertexLightBlend;
+ // followed by ActiveLight pointers; additive lights first, then vertex lights
+
+ const ActiveLight* const* GetLights() const {
+ return reinterpret_cast<const ActiveLight* const*>( reinterpret_cast<const UInt8*>(this) + sizeof(ForwardLightsBlock) );
+ }
+};
+
+struct VertexLightsBlock
+{
+ int lightCount;
+ // followed by ActiveLight pointers
+
+ const ActiveLight* const* GetLights() const {
+ return reinterpret_cast<const ActiveLight* const*>( reinterpret_cast<const UInt8*>(this) + sizeof(VertexLightsBlock) );
+ }
+};
diff --git a/Runtime/Camera/Lighting.h b/Runtime/Camera/Lighting.h
new file mode 100644
index 0000000..dfe491b
--- /dev/null
+++ b/Runtime/Camera/Lighting.h
@@ -0,0 +1,34 @@
+#ifndef LIGHTING_H
+#define LIGHTING_H
+
+// Light type
+enum LightType {
+ kLightSpot,
+ kLightDirectional,
+ kLightPoint,
+ kLightArea,
+ kLightTypeCount // keep this last
+};
+
+// Pixel lighting mode (keyword to use)
+enum LightKeywordMode {
+ kLightKeywordSpot,
+ kLightKeywordDirectional,
+ kLightKeywordDirectionalCookie,
+ kLightKeywordPoint,
+ kLightKeywordPointCookie,
+ kLightKeywordCount // keep this last
+};
+
+enum ShadowType {
+ kShadowNone = 0,
+ kShadowHard,
+ kShadowSoft,
+};
+
+inline bool IsSoftShadow(ShadowType shadowType)
+{
+ return (shadowType == kShadowSoft);
+};
+
+#endif
diff --git a/Runtime/Camera/OcclusionArea.cpp b/Runtime/Camera/OcclusionArea.cpp
new file mode 100644
index 0000000..c00d5b8
--- /dev/null
+++ b/Runtime/Camera/OcclusionArea.cpp
@@ -0,0 +1,67 @@
+#include "UnityPrefix.h"
+#include "OcclusionArea.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+
+OcclusionArea::OcclusionArea (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+OcclusionArea::~OcclusionArea ()
+{
+}
+
+void OcclusionArea::Reset ()
+{
+ Super::Reset();
+
+ m_Center = Vector3f::zero;
+ m_Size = Vector3f::one;
+ m_IsViewVolume = true;
+// m_OverrideResolution = false;
+// m_SmallestVoxelSize = 1.0F;
+// m_SmallestHoleSize = 0.1F;
+// m_BackfaceCulling = 0.0F;
+}
+
+Vector3f OcclusionArea::GetGlobalExtents () const
+{
+ Vector3f extents = GetComponent (Transform).GetWorldScaleLossy ();
+ extents.Scale (m_Size);
+ extents *= 0.5F;
+ extents = Abs (extents);
+ return extents;
+}
+
+Vector3f OcclusionArea::GetGlobalCenter () const
+{
+ return GetComponent (Transform).TransformPoint (m_Center);
+}
+
+using namespace Unity;
+
+template<class TransferFunction>
+void OcclusionArea::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion (2);
+
+ TRANSFER(m_Size);
+ TRANSFER(m_Center);
+ TRANSFER(m_IsViewVolume);
+// TRANSFER(m_OverrideResolution);
+ transfer.Align();
+
+// TRANSFER(m_SmallestVoxelSize);
+// TRANSFER(m_SmallestHoleSize);
+// TRANSFER(m_BackfaceCulling);
+}
+
+IMPLEMENT_OBJECT_SERIALIZE (OcclusionArea)
+IMPLEMENT_CLASS_HAS_INIT (OcclusionArea)
+
+void OcclusionArea::InitializeClass ()
+{
+}
diff --git a/Runtime/Camera/OcclusionArea.h b/Runtime/Camera/OcclusionArea.h
new file mode 100644
index 0000000..6610cb4
--- /dev/null
+++ b/Runtime/Camera/OcclusionArea.h
@@ -0,0 +1,46 @@
+#ifndef OCCLUSION_AREA_H
+#define OCCLUSION_AREA_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Math/Vector3.h"
+
+class OcclusionArea : public Unity::Component {
+public:
+ OcclusionArea ();
+
+ REGISTER_DERIVED_CLASS (OcclusionArea, Component)
+ DECLARE_OBJECT_SERIALIZE (OcclusionArea)
+
+ OcclusionArea (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void Reset();
+
+ void SetCenter (const Vector3f& center) { m_Center = center; SetDirty(); }
+ const Vector3f& GetCenter () const { return m_Center; }
+
+ void SetSize (const Vector3f& size) { m_Size = size; SetDirty(); }
+ const Vector3f& GetSize () const { return m_Size; }
+
+ void SetViewVolume (bool isViewVolume) { m_IsViewVolume = isViewVolume; SetDirty(); }
+ bool GetViewVolume () const { return m_IsViewVolume; }
+
+ Vector3f GetGlobalExtents () const;
+ Vector3f GetGlobalCenter () const;
+
+ static void InitializeClass ();
+ static void CleanupClass () { }
+
+private:
+
+ Vector3f m_Size;
+ Vector3f m_Center;
+
+ bool m_IsViewVolume;
+
+// bool m_OverrideResolution;
+// float m_SmallestVoxelSize;
+// float m_SmallestHoleSize;
+// float m_BackfaceCulling;
+};
+
+#endif
diff --git a/Runtime/Camera/OcclusionPortal.cpp b/Runtime/Camera/OcclusionPortal.cpp
new file mode 100644
index 0000000..ab741bd
--- /dev/null
+++ b/Runtime/Camera/OcclusionPortal.cpp
@@ -0,0 +1,60 @@
+#include "UnityPrefix.h"
+#include "OcclusionPortal.h"
+#include "UnityScene.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+OcclusionPortal::OcclusionPortal (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_Open = true;
+ m_PortalIndex = -1;
+ m_Center = Vector3f(0,0,0);
+ m_Size = Vector3f(1,1,1);
+}
+
+OcclusionPortal::~OcclusionPortal ()
+{
+}
+
+bool OcclusionPortal::CalculatePortalEnabled ()
+{
+ if (!IsActive())
+ return true;
+
+ return m_Open;
+}
+
+void OcclusionPortal::SetIsOpen (bool open)
+{
+ m_Open = open;
+
+ if (m_PortalIndex != -1)
+ GetScene().SetOcclusionPortalEnabled (m_PortalIndex, CalculatePortalEnabled());
+}
+
+void OcclusionPortal::Deactivate (DeactivateOperation operation)
+{
+ if (m_PortalIndex != -1)
+ GetScene().SetOcclusionPortalEnabled (m_PortalIndex, true);
+}
+
+void OcclusionPortal::AwakeFromLoad (AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad(mode);
+
+ if (m_PortalIndex != -1)
+ GetScene().SetOcclusionPortalEnabled (m_PortalIndex, CalculatePortalEnabled());
+}
+
+template<class TransferFunction>
+void OcclusionPortal::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER (m_Open);
+ transfer.Align();
+ TRANSFER (m_Center);
+ TRANSFER (m_Size);
+}
+
+IMPLEMENT_OBJECT_SERIALIZE (OcclusionPortal)
+IMPLEMENT_CLASS (OcclusionPortal) \ No newline at end of file
diff --git a/Runtime/Camera/OcclusionPortal.h b/Runtime/Camera/OcclusionPortal.h
new file mode 100644
index 0000000..48bface
--- /dev/null
+++ b/Runtime/Camera/OcclusionPortal.h
@@ -0,0 +1,37 @@
+#ifndef OCCLUSION_PORTAL_H
+#define OCCLUSION_PORTAL_H
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Math/Vector3.h"
+
+class OcclusionPortal : public Unity::Component
+{
+public:
+ REGISTER_DERIVED_CLASS (OcclusionPortal, Component)
+ DECLARE_OBJECT_SERIALIZE (OcclusionPortal)
+
+ OcclusionPortal (MemLabelId label, ObjectCreationMode mode);
+
+ void SetPortalIndex (int portalIndex) { m_PortalIndex = portalIndex; }
+
+ void SetIsOpen (bool opened);
+ bool GetIsOpen () { return m_Open; }
+
+
+ Vector3f GetCenter () { return m_Center; }
+ Vector3f GetSize () { return m_Size; }
+
+ bool CalculatePortalEnabled ();
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode mode);
+ virtual void Deactivate (DeactivateOperation operation);
+
+ private:
+
+ Vector3f m_Center;
+ Vector3f m_Size;
+ int m_PortalIndex;
+ bool m_Open;
+};
+
+#endif \ No newline at end of file
diff --git a/Runtime/Camera/Projector.cpp b/Runtime/Camera/Projector.cpp
new file mode 100644
index 0000000..d60ea62
--- /dev/null
+++ b/Runtime/Camera/Projector.cpp
@@ -0,0 +1,313 @@
+#include "UnityPrefix.h"
+#include "Projector.h"
+#include "BaseRenderer.h"
+#include "RenderManager.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Camera/Camera.h"
+#include "Renderqueue.h"
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "External/shaderlab/Library/properties.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Camera/CullResults.h"
+//#include "Runtime/Camera/RenderLoops/RenderLoopEnums.h"
+
+void Projector::InitializeClass ()
+{
+ RegisterAllowNameConversion (Projector::GetClassStringStatic(), "m_IsOrthoGraphic", "m_Orthographic");
+ RegisterAllowNameConversion (Projector::GetClassStringStatic(), "m_OrthoGraphicSize", "m_OrthographicSize");
+}
+
+Projector::Projector (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+Projector::~Projector ()
+{
+}
+
+void Projector::Reset ()
+{
+ Super::Reset();
+ m_NearClipPlane = .1F;
+ m_FarClipPlane = 100.0F;
+ m_FieldOfView = 60;
+ m_AspectRatio = 1.0F;
+ m_OrthographicSize = 10;
+ m_Orthographic = false;
+ m_IgnoreLayers.m_Bits = 0;
+}
+
+// chosen not to blow up when the user has set fov, aspect ratio and ortho size to 0 at the same time
+static const float kSmallValue = 1.0e-8f;
+
+// should probably be scaled with m_NearClipPlane, but it's fine for reasonable near clip values
+static const float kSmallestNearClipMargin = 1.0e-2f;
+
+void Projector::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ if (m_Orthographic)
+ {
+ float clipPlaneDiff = m_FarClipPlane - m_NearClipPlane;
+ if (Abs (clipPlaneDiff) < kSmallestNearClipMargin)
+ m_FarClipPlane = m_NearClipPlane + Sign (clipPlaneDiff) * kSmallestNearClipMargin;
+ }
+ else
+ {
+ if (m_NearClipPlane < kSmallestNearClipMargin)
+ m_NearClipPlane = kSmallestNearClipMargin;
+
+ if (m_FarClipPlane < m_NearClipPlane + kSmallestNearClipMargin)
+ m_FarClipPlane = m_NearClipPlane + kSmallestNearClipMargin;
+ }
+
+
+ if (Abs (m_FieldOfView) < kSmallValue)
+ m_FieldOfView = kSmallValue * Sign (m_FieldOfView);
+
+ if (Abs (m_AspectRatio) < kSmallValue)
+ m_AspectRatio = kSmallValue * Sign (m_AspectRatio);
+
+ if (Abs (m_OrthographicSize) < kSmallValue)
+ m_OrthographicSize = kSmallValue * Sign (m_OrthographicSize);
+}
+
+using namespace Unity;
+
+struct ProjectorRenderSettings
+{
+ Matrix4x4f projection;
+ Matrix4x4f distance;
+ Matrix4x4f clipping;
+ Matrix4x4f frustumMatrix;
+
+ Material* material;
+ Shader* shader;
+ int subshaderIndex;
+ int passCount;
+};
+
+static void RenderProjectorForRenderer ( BaseRenderer* renderer, const ProjectorRenderSettings& settings)
+{
+ const TransformInfo& xformInfo = renderer->GetTransformInfo ();
+ const Matrix4x4f& transform = xformInfo.worldMatrix;
+
+ // texture projection matrices
+ BuiltinShaderParamValues& params = GetGfxDevice().GetBuiltinParamValues();
+ MultiplyMatrices4x4 (&settings.projection, &transform, &params.GetWritableMatrixParam(kShaderMatProjector));
+ MultiplyMatrices4x4 (&settings.distance, &transform, &params.GetWritableMatrixParam(kShaderMatProjectorDistance));
+ MultiplyMatrices4x4 (&settings.clipping, &transform, &params.GetWritableMatrixParam(kShaderMatProjectorClip));
+
+ SetupObjectMatrix (transform, xformInfo.transformType);
+
+ for (int i=0;i<settings.passCount;i++)
+ {
+ const ChannelAssigns* channels = settings.material->SetPassWithShader( i, settings.shader, settings.subshaderIndex );
+ if (!channels) // pass should not be rendered
+ continue;
+
+ int rendererMatCount = renderer->GetMaterialCount();
+ for( int m = 0; m < rendererMatCount; ++m )
+ {
+ Material* rendererMat = renderer->GetMaterial(m);
+ if( rendererMat )
+ {
+ Shader* rendererShader = rendererMat->GetShader();
+ if( rendererShader && rendererShader->GetShaderLabShader()->GetNoProjector() )
+ continue;
+ }
+ renderer->Render (renderer->GetSubsetIndex(m), *channels);
+ }
+ }
+}
+
+void Projector::AddToManager ()
+{
+ RenderManager& renderManager = GetRenderManager();
+
+ // Remove first to make sure we don't have duplicates
+ // @todo: Eventually Behaviour should be fixed to always close a AddToManager with a RemoveFromManager
+ renderManager.RemoveCameraRenderable (this);
+
+ // The projector should default to "after all geometry" render queue.
+ // Unless the material defines a non-default render queue, in which case
+ // use that.
+ int renderQueue = kGeometryQueueIndexMax + 1;
+ Material* projectorMaterial = m_Material;
+ if (projectorMaterial)
+ {
+ int rq = projectorMaterial->GetActualRenderQueue();
+ if (rq != kGeometryRenderQueue)
+ renderQueue = rq;
+ }
+
+ renderManager.AddCameraRenderable (this, renderQueue);
+}
+
+
+void Projector::RemoveFromManager ()
+{
+ GetRenderManager ().RemoveCameraRenderable (this);
+}
+
+void Projector::SetupProjectorSettings (Material* projectorMaterial, ProjectorRenderSettings& projectorSettings)
+{
+ Matrix4x4f projectionMatrix = CalculateProjectionMatrix ();
+
+ Matrix4x4f zscale;
+ zscale.SetScale (Vector3f (1.0F, 1.0F, -1.0F));
+
+ Matrix4x4f projectorToWorld;
+ projectorToWorld = GetComponent (Transform).GetWorldToLocalMatrixNoScale ();
+
+ Matrix4x4f temp1, temp2,temp3, temp4;
+
+ // Setup the functor
+ // projection matrix
+ temp1.SetScale (Vector3f (.5f, .5f, 1.0f));
+ temp2.SetTranslate (Vector3f (.5f, .5f, 0.0f));
+ // functor.projection = temp2 * projectionMatrix * zscale * temp1 * projectorToWorld
+ MultiplyMatrices4x4 (&temp2, &projectionMatrix, &temp3);
+ MultiplyMatrices4x4 (&temp3, &zscale, &temp4);
+ MultiplyMatrices4x4 (&temp4, &temp1, &temp2);
+ MultiplyMatrices4x4 (&temp2, &projectorToWorld, &projectorSettings.projection);
+
+ // X-axis fadeout matrix
+ float scale = 1.0f / m_FarClipPlane;
+ temp1.SetScale (Vector3f (scale, scale, scale));
+ temp2.SetIdentity ();
+ temp2.Get(0,0) = 0; temp2.Get(0,1) = 0; temp2.Get(0,2) = 1; temp2.Get(0,0) = 0;
+ // functor.distance = temp2 * temp1 * projectorToWorld
+ MultiplyMatrices4x4 (&temp2, &temp1, &temp3);
+ MultiplyMatrices4x4 (&temp3, &projectorToWorld, &projectorSettings.distance);
+
+ // X-axis texture cull (use with an alpha map to do alpha-tested clip planes)
+ scale = 1.0f / (m_FarClipPlane - m_NearClipPlane);
+ temp1.SetScale (Vector3f (scale, scale, scale));
+ temp2.SetIdentity ();
+ temp3.SetTranslate (Vector3f (-m_NearClipPlane, -m_NearClipPlane, -m_NearClipPlane));
+ temp2.Get(0,0) = 0; temp2.Get(0,1) = 0; temp2.Get(0,2) = 1; temp2.Get(0,0) = 0;
+ // functor.clipping = temp2 * temp1 * temp3 * projectorToWorld
+ MultiplyMatrices4x4 (&temp2, &temp1, &temp4);
+ MultiplyMatrices4x4 (&temp4, &temp3, &temp1);
+ MultiplyMatrices4x4 (&temp1, &projectorToWorld, &projectorSettings.clipping);
+
+ Shader* shader = projectorMaterial->GetShader();
+ int subshaderIndex = 0;
+ projectorSettings.material = projectorMaterial;
+ projectorSettings.shader = shader;
+ projectorSettings.subshaderIndex = subshaderIndex;
+ const ShaderLab::SubShader& ss = shader->GetShaderLabShader()->GetSubShader(subshaderIndex);
+ projectorSettings.passCount = ss.GetValidPassCount();
+
+ /// Setup culling planes to be the projector area without any layer based distance culling.
+ // finalProj = projectionMatrix * zscale * projectorToWorld
+ MultiplyMatrices4x4 (&projectionMatrix, &zscale, &temp1);
+ MultiplyMatrices4x4 (&temp1, &projectorToWorld, &projectorSettings.frustumMatrix);
+}
+
+
+void Projector::RenderRenderable (const CullResults& cullResults)
+{
+ // early out if we have no material
+ Material* projectorMaterial = m_Material;
+ if( !projectorMaterial )
+ return;
+
+ // We dont't support projectors when doing shader replacement
+ if ( cullResults.shaderReplaceData.replacementShader != NULL)
+ return;
+
+ Camera& camera = GetCurrentCamera();
+ if( !(camera.GetCullingMask() & GetGameObject().GetLayerMask()) )
+ return; // current camera does not render our layer - exit
+
+ // save current view/world matrices
+ GfxDevice& device = GetGfxDevice();
+ float matWorld[16], matView[16];
+ CopyMatrix(device.GetViewMatrix(), matView);
+ CopyMatrix(device.GetWorldMatrix(), matWorld);
+
+
+ ProjectorRenderSettings projectorSettings;
+ SetupProjectorSettings (projectorMaterial, projectorSettings);
+
+ UInt32 projectorCullingMask = ~(m_IgnoreLayers.m_Bits);
+
+ // From the objects that we are rendering from this camera.
+ // Cull it further to the objects that overlap with the Projector frustum
+ Plane cullingPlanes[6];
+ ExtractProjectionPlanes (projectorSettings.frustumMatrix, cullingPlanes);
+
+ const VisibleNodes& nodes = cullResults.nodes;
+ for (int i=0;i<nodes.size();i++)
+ {
+ if (IntersectAABBFrustumFull (nodes[i].worldAABB, cullingPlanes))
+ {
+ UInt32 mask = nodes[i].renderer->GetLayerMask();
+ if (mask & projectorCullingMask)
+ RenderProjectorForRenderer (nodes[i].renderer, projectorSettings);
+ }
+ }
+
+ // restore view/world matrices
+ device.SetViewMatrix(matView);
+ device.SetWorldMatrix(matWorld);
+}
+
+Matrix4x4f Projector::GetProjectorToPerspectiveMatrix() const
+{
+ Matrix4x4f zscale;
+ zscale.SetScale (Vector3f (1.0F, 1.0F, -1.0F));
+
+ Matrix4x4f projectorToWorld = GetComponent (Transform).GetWorldToLocalMatrixNoScale ();
+
+ // CalculateProjectionMatrix() * zscale * projectorToWorld
+ Matrix4x4f proj, temp, res;
+ proj = CalculateProjectionMatrix();
+ MultiplyMatrices4x4 (&proj, &zscale, &temp);
+ MultiplyMatrices4x4 (&temp, &projectorToWorld, &res);
+ return res;
+}
+
+Matrix4x4f Projector::CalculateProjectionMatrix() const
+{
+ Matrix4x4f projection;
+ if( m_Orthographic )
+ projection.SetOrtho(-m_OrthographicSize * m_AspectRatio, m_OrthographicSize * m_AspectRatio, -m_OrthographicSize, m_OrthographicSize, m_NearClipPlane, m_FarClipPlane);
+ else
+ projection.SetPerspective(m_FieldOfView, m_AspectRatio, m_NearClipPlane, m_FarClipPlane);
+ return projection;
+}
+
+template<class TransferFunction>
+void Projector::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ // Note: transfer code for version 1 was just removed. It was around Unity 1.2 times,
+ // and now we're fine with losing project folder compatibility with that.
+ transfer.SetVersion (2);
+
+ TRANSFER_SIMPLE(m_NearClipPlane);
+ TRANSFER_SIMPLE(m_FarClipPlane);
+ TRANSFER_SIMPLE(m_FieldOfView);
+ TRANSFER(m_AspectRatio);
+ TRANSFER(m_Orthographic);
+ transfer.Align();
+ TRANSFER(m_OrthographicSize);
+ TRANSFER_SIMPLE(m_Material);
+ TRANSFER(m_IgnoreLayers);
+}
+
+IMPLEMENT_OBJECT_SERIALIZE (Projector)
+IMPLEMENT_CLASS_HAS_INIT (Projector)
diff --git a/Runtime/Camera/Projector.h b/Runtime/Camera/Projector.h
new file mode 100644
index 0000000..f805e1e
--- /dev/null
+++ b/Runtime/Camera/Projector.h
@@ -0,0 +1,74 @@
+#ifndef PROJECTOR_H
+#define PROJECTOR_H
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "Renderable.h"
+#include "Runtime/Math/Matrix4x4.h"
+
+namespace Unity { class Material; }
+struct CullResults;
+struct ProjectorRenderSettings;
+
+class Projector : public Behaviour, Renderable {
+public:
+ Projector ();
+
+ REGISTER_DERIVED_CLASS (Projector, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (Projector)
+
+ Projector (MemLabelId label, ObjectCreationMode mode);
+
+ // Renderable
+ virtual void RenderRenderable (const CullResults& cullResults);
+
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+
+ virtual void Reset();
+ virtual void CheckConsistency ();
+
+ void SetNearClipPlane (float inNear) { m_NearClipPlane = inNear; SetDirty(); }
+ float GetNearClipPlane () const { return m_NearClipPlane; }
+
+ void SetFarClipPlane (float farPlane) { m_FarClipPlane = farPlane; SetDirty(); }
+ float GetFarClipPlane () const { return m_FarClipPlane; }
+
+ void SetFieldOfView (float angle) { m_FieldOfView = angle; SetDirty(); }
+ float GetFieldOfView () const { return m_FieldOfView; }
+
+ void SetAspectRatio (float aspect) { m_AspectRatio = aspect; SetDirty(); }
+ float GetAspectRatio () const { return m_AspectRatio; }
+
+ void SetOrthographic (bool isOrtho) { m_Orthographic = isOrtho; SetDirty(); }
+ bool GetOrthographic () const { return m_Orthographic; }
+
+ void SetOrthographicSize (float size) { m_OrthographicSize = size; SetDirty(); }
+ float GetOrthographicSize () const { return m_OrthographicSize; }
+
+ PPtr<Material> GetMaterial () const { return m_Material; }
+ void SetMaterial (PPtr<Material> material) { m_Material = material; }
+
+ int GetIgnoreLayers () { return m_IgnoreLayers.m_Bits; }
+ void SetIgnoreLayers(int layers) { m_IgnoreLayers.m_Bits = layers; SetDirty(); }
+
+ Matrix4x4f GetProjectorToPerspectiveMatrix() const;
+ static void InitializeClass();
+ static void CleanupClass() { }
+
+private:
+
+ void SetupProjectorSettings (Material* material, ProjectorRenderSettings& settings);
+ Matrix4x4f CalculateProjectionMatrix() const;
+
+ float m_NearClipPlane;
+ float m_FarClipPlane;
+ float m_FieldOfView;
+ float m_AspectRatio;
+ bool m_Orthographic;
+ float m_OrthographicSize;
+
+ BitField m_IgnoreLayers;
+ PPtr<Material> m_Material; ///< Custom material to apply. If set it overrides, texture & blend mode settings
+};
+
+#endif
diff --git a/Runtime/Camera/RenderLayers/GUIElement.cpp b/Runtime/Camera/RenderLayers/GUIElement.cpp
new file mode 100644
index 0000000..73ff0e0
--- /dev/null
+++ b/Runtime/Camera/RenderLayers/GUIElement.cpp
@@ -0,0 +1,31 @@
+#include "UnityPrefix.h"
+#include "GUIElement.h"
+#include "GUILayer.h"
+#include "Runtime/Math/Vector2.h"
+
+GUIElement::GUIElement (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+GUIElement::~GUIElement ()
+{
+}
+
+void GUIElement::AddToManager ()
+{
+ GUILayer::ms_GUIElements->add_delayed (this);
+}
+
+void GUIElement::RemoveFromManager ()
+{
+ GUILayer::ms_GUIElements->remove_delayed (this);
+}
+
+bool GUIElement::HitTest (const Vector2f& screenSpacePosition, const Rectf& cameraRect)
+{
+ Rectf rect = GetScreenRect (cameraRect);
+ return rect.Contains (screenSpacePosition.x, screenSpacePosition.y);
+}
+
+IMPLEMENT_CLASS (GUIElement)
diff --git a/Runtime/Camera/RenderLayers/GUIElement.h b/Runtime/Camera/RenderLayers/GUIElement.h
new file mode 100644
index 0000000..0092da9
--- /dev/null
+++ b/Runtime/Camera/RenderLayers/GUIElement.h
@@ -0,0 +1,30 @@
+#ifndef GUIELEMENT_H
+#define GUIELEMENT_H
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Math/Rect.h"
+class Vector2f;
+
+
+// Base class for all GUI elements.
+// Registers itself with the GUILayer when enabled.
+class GUIElement : public Behaviour
+{
+public:
+
+ REGISTER_DERIVED_ABSTRACT_CLASS (GUIElement, Behaviour)
+
+ GUIElement (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void RenderGUIElement (const Rectf& cameraRect) = 0;
+ virtual Rectf GetScreenRect (const Rectf& cameraRect) = 0;
+
+ bool HitTest (const Vector2f& screenSpaceCoordinates, const Rectf& cameraRect);
+
+private:
+
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+};
+
+#endif
diff --git a/Runtime/Camera/RenderLayers/GUILayer.cpp b/Runtime/Camera/RenderLayers/GUILayer.cpp
new file mode 100644
index 0000000..69deeb8
--- /dev/null
+++ b/Runtime/Camera/RenderLayers/GUILayer.cpp
@@ -0,0 +1,104 @@
+#include "UnityPrefix.h"
+#include "GUILayer.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/BaseClasses/Tags.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+
+PROFILER_INFORMATION(gGUILayerProfile, "Camera.GUILayer", kProfilerRender);
+
+GUILayer::GUIElements* GUILayer::ms_GUIElements = NULL;
+
+GUILayer::GUILayer(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+GUILayer::~GUILayer ()
+{
+}
+
+inline bool SortGUIByDepth (GUIElement* lhs, GUIElement* rhs)
+{
+ return lhs->GetComponent (Transform).GetLocalPosition ().z < rhs->GetComponent (Transform).GetLocalPosition ().z;
+}
+
+void GUILayer::InitializeClass ()
+{
+ ms_GUIElements = new GUIElements;
+}
+
+void GUILayer::CleanupClass()
+{
+ delete ms_GUIElements;
+}
+
+
+void GUILayer::RenderGUILayer ()
+{
+ PROFILER_AUTO_GFX(gGUILayerProfile, this);
+
+ ms_GUIElements->apply_delayed ();
+ if (ms_GUIElements->empty())
+ return;
+
+ typedef UNITY_TEMP_VECTOR(GUIElement*) TempElements;
+ TempElements elements (ms_GUIElements->begin (), ms_GUIElements->end ());
+
+ std::sort (elements.begin (), elements.end (), SortGUIByDepth);
+
+ Camera& camera = GetComponent(Camera);
+ UInt32 cullingMask = camera.GetCullingMask ();
+ Rectf cameraRect = camera.GetScreenViewportRect();
+
+ for (TempElements::iterator it=elements.begin(), itEnd = elements.end(); it != itEnd; ++it)
+ {
+ GUIElement& element = **it;
+ if (element.GetGameObject ().GetLayerMask () & cullingMask)
+ element.RenderGUIElement (cameraRect);
+ }
+}
+
+GUIElement* GUILayer::HitTest (const Vector2f& screenPosition)
+{
+ Camera& camera = GetComponent (Camera);
+ Vector3f viewportPos3D = camera.ScreenToViewportPoint (Vector3f (screenPosition.x, screenPosition.y, camera.GetNear()));
+ Vector2f viewportPos (viewportPos3D.x, viewportPos3D.y);
+ Rectf normalized (0.0F,0.0F,1.0F,1.0F);
+
+ if (!normalized.Contains (viewportPos.x, viewportPos.y))
+ return NULL;
+
+ Rectf cameraRect = camera.GetScreenViewportRect();
+
+ Rectf windowRect = GetRenderManager ().GetWindowRect ();
+ viewportPos.x *= windowRect.Width ();
+ viewportPos.y *= windowRect.Height ();
+
+ GUIElement* topmost = NULL;
+ float topmostZ = -std::numeric_limits<float>::infinity ();
+
+ // GUI hit testing always ignores IgnoreRaycast layer
+ UInt32 cullingMask = camera.GetCullingMask() & ~(kIgnoreRaycastMask);
+
+ for (GUIElements::iterator it=ms_GUIElements->begin (), itEnd = ms_GUIElements->end (); it != itEnd; ++it)
+ {
+ GUIElement* element = *it;
+ if (element && (element->GetGameObject ().GetLayerMask () & cullingMask) && element->HitTest (viewportPos, cameraRect))
+ {
+ float z = element->GetComponent (Transform).GetLocalPosition ().z;
+ if (z > topmostZ)
+ {
+ topmost = element;
+ topmostZ = z;
+ }
+ }
+ }
+
+ return topmost;
+}
+
+IMPLEMENT_CLASS_HAS_INIT(GUILayer)
diff --git a/Runtime/Camera/RenderLayers/GUILayer.h b/Runtime/Camera/RenderLayers/GUILayer.h
new file mode 100644
index 0000000..925b070
--- /dev/null
+++ b/Runtime/Camera/RenderLayers/GUILayer.h
@@ -0,0 +1,33 @@
+#ifndef GUILAYER_H
+#define GUILAYER_H
+
+#include "GUIElement.h"
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Utilities/delayed_set.h"
+
+/// A GUI Layer is attached to the camera.
+/// It tracks all enabled GUIElements (eg. Text, GUITexture) and renders them
+class GUILayer : public Behaviour
+{
+public:
+ REGISTER_DERIVED_CLASS (GUILayer, Behaviour)
+
+ GUILayer (MemLabelId label, ObjectCreationMode mode);
+
+ void RenderGUILayer();
+ GUIElement* HitTest (const Vector2f& screenPosition);
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ // Behaviour
+ virtual void AddToManager() { };
+ virtual void RemoveFromManager() { };
+
+private:
+ typedef delayed_set <PPtr<GUIElement> > GUIElements;
+ static GUIElements* ms_GUIElements;
+ friend class GUIElement;
+};
+
+#endif
diff --git a/Runtime/Camera/RenderLayers/GUIText.cpp b/Runtime/Camera/RenderLayers/GUIText.cpp
new file mode 100644
index 0000000..9602bdd
--- /dev/null
+++ b/Runtime/Camera/RenderLayers/GUIText.cpp
@@ -0,0 +1,303 @@
+#include "UnityPrefix.h"
+#include "GUIText.h"
+#include "Runtime/Filters/Misc/Font.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/IMGUI/TextMeshGenerator2.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Filters/Mesh/Mesh.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "Runtime/Profiler/Profiler.h"
+
+static Font* gDefaultFont = NULL;
+
+GUIText::GUIText (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_FontSize = 0;
+ m_FontStyle = 0;
+ m_RichText = true;
+ m_Color = 0xffffffff;
+}
+
+GUIText::~GUIText ()
+{
+}
+
+Font * GUIText::GetFont () const {
+ Font *f = m_Font;
+ if (!f) {
+ if (!gDefaultFont)
+ gDefaultFont = GetBuiltinResource<Font> (kDefaultFontName);
+ return gDefaultFont;
+ }
+ else {
+ return f;
+ }
+
+}
+
+PROFILER_INFORMATION(gRenderGUIText, "GUIText.Render", kProfilerGUI)
+PROFILER_INFORMATION(gSubmitVBOProfileGUIText, "Mesh.SubmitVBO", kProfilerRender)
+
+void GUIText::RenderGUIElement (const Rectf& cameraRect)
+{
+ if (m_Text.empty ())
+ return;
+
+ PROFILER_AUTO(gRenderGUIText, this)
+
+ pair<Font*, Material*> temp = GetFontAndMaterial ();
+ Font* font = temp.first;
+ Material* material = temp.second;
+ if (font == NULL || material == NULL)
+ return;
+
+ TextMeshGenerator2 &tmgen = TextMeshGenerator2::Get (UTF16String(m_Text.c_str()), font, (TextAnchor)m_Anchor, (TextAlignment)m_Alignment, 0, m_TabSize, m_LineSpacing, m_RichText, m_PixelCorrect, m_Color, m_FontSize, m_FontStyle);
+
+ GfxDevice& device = GetGfxDevice();
+ DeviceMVPMatricesState preserveMVP; // save MVP matrices, restore when returning from this function!
+
+ Vector2f size = tmgen.GetSize ();
+ Vector2f offset = tmgen.GetTextOffset (Rectf (0, 0, -size.x, size.y * 2));
+ switch (m_Alignment)
+ {
+ case kRight: offset.x += size.x; break;
+ case kCenter:
+ if (m_PixelCorrect)
+ offset.x += Roundf(size.x * 0.5f);
+ else
+ offset.x += size.x * 0.5f;
+ break;
+ }
+ Matrix4x4f textMatrix;
+ if (!m_PixelCorrect)
+ {
+ Matrix4x4f ortho;
+ ortho.SetOrtho( 0, 1, 0, 1, -1, 100 );
+ device.SetProjectionMatrix (ortho);
+
+ Transform& transform = GetComponent (Transform);
+ Vector3f position = transform.GetPosition ();
+ position.z = 0.0F;
+ Vector3f scale = transform.GetWorldScaleLossy ();
+ scale.x *= 0.05F * font->GetDeprecatedPixelScale ();
+ scale.y *= -0.05F * font->GetDeprecatedPixelScale ();
+ scale.z = 1.0F;
+
+ position.x += offset.x * scale.x;
+ position.y -= offset.y * scale.y;
+
+ textMatrix.SetTranslate( position );
+ textMatrix.Scale( scale );
+ }
+ else {
+ // Find out how large a rect the font should be rendered into, so that it is pixel correct. call the generator with this size.
+ Rectf rectNoOffset = cameraRect;
+ rectNoOffset.Move( -rectNoOffset.x, -rectNoOffset.y );
+ LoadPixelMatrix( rectNoOffset, device, true, false );
+
+ Transform& transform = GetComponent (Transform);
+ Vector3f position = transform.GetPosition ();
+ position.z = 0.0F;
+
+ textMatrix.SetTranslate( Vector3f( Roundf (position.x * cameraRect.Width() + m_PixelOffset.x), Roundf(position.y * cameraRect.Height() + m_PixelOffset.y), 0.0f ) );
+ textMatrix.Scale( Vector3f( 1.0F, -1.0F, 1.0f ) );
+
+ textMatrix.Translate( Vector3f( offset.x, -offset.y, 0.0f ) );
+ }
+ device.SetViewMatrix( textMatrix.GetPtr() );
+
+ int passCount = material->GetPassCount ();
+ for (int i=0;i < passCount ;i++)
+ {
+ const ChannelAssigns* channels = material->SetPass (i);
+ tmgen.RenderRaw (*channels);
+ }
+}
+
+
+void DrawGUIText (const std::string& text, Font* font, Material* material)
+{
+ if (text.empty())
+ return;
+ if (font == NULL || material == NULL)
+ return;
+
+ PROFILER_AUTO(gRenderGUIText, NULL)
+
+ TextMeshGenerator2 &tmgen = TextMeshGenerator2::Get (UTF16String(text.c_str()), font, kUpperLeft, kAuto, 0, 0, 1.0f, false, true, 0xffffffff, 0, 0);
+
+ static SHADERPROP (MainTex);
+ Texture* fontTexture = font->GetTexture();
+ // In the case when font is a custum font, GetTexture() will return NULL, so in this case take the main texture from font's material
+ if (fontTexture == NULL && font->GetMaterial())
+ {
+ fontTexture = font->GetMaterial()->GetTexture(kSLPropMainTex);
+ }
+ material->SetTexture(kSLPropMainTex, fontTexture);
+
+ int passCount = material->GetPassCount ();
+ for (int i=0;i < passCount ;i++)
+ {
+ const ChannelAssigns* channels = material->SetPass (i);
+ tmgen.RenderRaw (*channels);
+ }
+}
+
+Rectf GUIText::GetScreenRect (const Rectf& cameraRect)
+{
+ if (m_Text.empty ())
+ return Rectf();
+
+ Font* font = GetFontAndMaterial ().first;
+ if (font == NULL)
+ return Rectf();
+
+ TextMeshGenerator2 &tmgen = TextMeshGenerator2::Get (UTF16String(m_Text.c_str()), font, (TextAnchor)m_Anchor, (TextAlignment)m_Alignment, 0, m_TabSize, m_LineSpacing, m_RichText, m_PixelCorrect, m_Color, m_FontSize, m_FontStyle);
+ Vector2f size = tmgen.GetSize ();
+ Vector2f offset = tmgen.GetTextOffset (Rectf (0, 0, -size.x, size.y * 2));
+ Rectf rect (offset.x, -offset.y, size.x, size.y);
+
+ Transform& transform = GetComponent (Transform);
+ if (!m_PixelCorrect)
+ {
+ Vector3f position = transform.GetPosition ();
+ position.z = 0.0F;
+ Vector3f scale = transform.GetWorldScaleLossy ();
+ scale.x *= 0.05F * font->GetDeprecatedPixelScale ();
+ scale.y *= -0.05F * font->GetDeprecatedPixelScale ();
+ scale.z = 1.0F;
+
+ rect.Scale (scale.x, scale.y);
+ rect.Move (position.x, position.y);
+ Rectf windowRect = GetRenderManager ().GetWindowRect ();
+ rect.Scale (windowRect.Width (), windowRect.Height ());
+ }
+ else
+ {
+ Rectf windowRect = GetRenderManager ().GetWindowRect ();
+
+ Vector3f position = transform.GetPosition ();
+ position.x = Roundf (position.x * windowRect.Width() + m_PixelOffset.x);
+ position.y = Roundf (position.y * windowRect.Height() + m_PixelOffset.y);
+
+ Vector3f scale = Vector3f (1.0F ,-1.0F,1);
+
+ rect.Scale (scale.x, scale.y);
+ rect.Move (position.x, position.y);
+ }
+ if (rect.height < 0)
+ {
+ rect.height = -rect.height;
+ rect.y -= rect.height;
+ }
+ return rect;
+}
+
+pair<Font*, Material*> GUIText::GetFontAndMaterial ()
+{
+ Font* font = m_Font;
+ Material* material = m_Material;
+ if (font != NULL && material == NULL)
+ material = font->GetMaterial ();
+
+ // Use default resource instead!
+ if (font == NULL || material == NULL)
+ {
+ if (gDefaultFont == NULL)
+ {
+ gDefaultFont = GetBuiltinResource<Font> (kDefaultFontName);
+ if (!gDefaultFont)
+ {
+ LogString ("Couldn't load default font!");
+ return std::make_pair<Font*, Material*> (NULL, NULL);
+ }
+ if (!gDefaultFont->GetMaterial())
+ {
+ LogString ("Couldn't load default font material!");
+ return std::make_pair<Font*, Material*> (NULL, NULL);
+ }
+ }
+
+ if (font == NULL)
+ font = gDefaultFont;
+ if (material == NULL)
+ material = gDefaultFont->GetMaterial();
+ }
+
+ return std::make_pair (font, material);
+}
+
+void GUIText::Reset ()
+{
+ Super::Reset ();
+ m_PixelCorrect = true;
+ m_Anchor = kUpperLeft;
+ m_Alignment = kLeft;
+ m_LineSpacing = 1.0F;
+ m_TabSize = 4.0F;
+ m_PixelOffset = Vector2f(0,0);
+}
+
+Material* GUIText::GetMaterial ()
+{
+ return GetFontAndMaterial ().second;
+}
+
+void GUIText::SetMaterial (Material* material)
+{
+ m_Material = material;
+ SetDirty ();
+}
+
+void GUIText::SetFont (PPtr<Font> font)
+{
+ m_Font = font;
+ SetDirty();
+}
+
+
+template<class TransferFunction> inline
+void GUIText::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion (3);
+ TRANSFER_SIMPLE(m_Text);
+ TRANSFER_SIMPLE(m_Anchor);
+ TRANSFER_SIMPLE(m_Alignment);
+ TRANSFER(m_PixelOffset);
+ TRANSFER(m_LineSpacing);
+ TRANSFER(m_TabSize);
+ TRANSFER(m_Font);
+ TRANSFER(m_Material);
+ TRANSFER(m_FontSize);
+ TRANSFER(m_FontStyle);
+ TRANSFER(m_Color);
+
+ TRANSFER(m_PixelCorrect);
+ TRANSFER(m_RichText);
+
+ #if UNITY_EDITOR
+ // Explanation: in verson 1.2.2 we added pixel correct drawing. By default it is on.
+ // for backwards compatbility we disable it
+ if(transfer.IsOldVersion(1))
+ m_PixelCorrect = false;
+
+ // In version 1.5.0 line spacing is multiplicative instead of additive
+ if(transfer.IsOldVersion(1) || transfer.IsOldVersion(2))
+ {
+ Font* font = GetFont();
+ m_LineSpacing = (font->GetLineSpacing() + m_LineSpacing) / font->GetLineSpacing();
+ }
+ #endif
+}
+
+IMPLEMENT_CLASS (GUIText)
+IMPLEMENT_OBJECT_SERIALIZE (GUIText)
diff --git a/Runtime/Camera/RenderLayers/GUIText.h b/Runtime/Camera/RenderLayers/GUIText.h
new file mode 100644
index 0000000..fdfeeb9
--- /dev/null
+++ b/Runtime/Camera/RenderLayers/GUIText.h
@@ -0,0 +1,93 @@
+#ifndef GUITEXT_H
+#define GUITEXT_H
+
+#include <string>
+#include "GUIElement.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Vector2.h"
+
+class Font;
+namespace Unity { class Material; }
+
+/// Can be Attached to any game object in the scene.
+/// Registers with GUILayer, GUILayer renders it.
+/// Position comes from transform.position.x,y
+/// size comes from transform.scale.x,y
+class GUIText : public GUIElement
+{
+ public:
+
+ REGISTER_DERIVED_CLASS (GUIText, GUIElement)
+ DECLARE_OBJECT_SERIALIZE (GUIText)
+
+ GUIText (MemLabelId label, ObjectCreationMode mode);
+ virtual void Reset ();
+
+ const UnityStr& GetText () const { return m_Text; }
+ void SetText (const std::string& text) { m_Text = text; }
+
+ // GUIElement
+ virtual void RenderGUIElement (const Rectf& cameraRect);
+ virtual Rectf GetScreenRect (const Rectf& cameraRect);
+
+ void SetFont (PPtr<Font> font);
+ Font * GetFont () const;
+
+ Material* GetMaterial ();
+ void SetMaterial (Material* material);
+
+ void SetPixelOffset (const Vector2f& offset) { m_PixelOffset = offset; SetDirty(); }
+ Vector2f GetPixelOffset () { return m_PixelOffset; }
+
+ void SetLineSpacing (float space) { m_LineSpacing = space; SetDirty(); }
+ float GetLineSpacing() const { return m_LineSpacing; }
+
+ void SetTabSize (float size) { m_TabSize = size; SetDirty(); }
+ float GetTabSize() const { return m_TabSize; }
+
+ void SetAlignment (int align) { m_Alignment = align; SetDirty(); }
+ int GetAlignment() const { return m_Alignment; }
+
+ void SetAnchor (int size) { m_Anchor = size; SetDirty(); }
+ int GetAnchor() const { return m_Anchor; }
+
+ void SetFontSize (int size) { m_FontSize = size; SetDirty(); }
+ int GetFontSize() const { return m_FontSize; }
+
+ void SetFontStyle (int style) { m_FontStyle = style; SetDirty(); }
+ int GetFontStyle() const { return m_FontStyle; }
+
+ void SetRichText (bool richText) { m_RichText = richText; SetDirty(); }
+ bool GetRichText() const { return m_RichText; }
+
+ void SetColor (ColorRGBA32 color) { m_Color = color; SetDirty(); }
+ ColorRGBA32 GetColor() const { return m_Color; }
+
+ private:
+
+ std::pair<Font*, Material*> GetFontAndMaterial ();
+
+ UnityStr m_Text;
+
+ short m_Alignment; ///< enum { left, center, right }
+ short m_Anchor; ///< Where the text-mesh is anchored related to local origo. enum { upper left, upper center, upper right, middle left, middle center, middle right, lower left, lower center, lower right }
+
+ float m_LineSpacing; ///< Spacing between lines as multiplum of height of a character.
+ float m_TabSize; ///< Length of one tab
+ bool m_PixelCorrect; ///< Place & scale the text to a pixel-correct position.
+ bool m_RichText;///< Enable HTML-style tags for text formatting.
+ Vector2f m_PixelOffset;
+
+ int m_FontSize; ///<The font size to use. Set to 0 to use default font size. Only applicable for dynamic fonts.
+ int m_FontStyle; ///<The font style to use. Only applicable for dynamic fonts. enum { Normal, Bold, Italic, Bold and Italic }
+
+ ColorRGBA32 m_Color;
+
+ PPtr<Font> m_Font;
+ PPtr<Material> m_Material;
+};
+
+
+void DrawGUIText (const std::string& text, Font* font, Material* material);
+
+#endif
diff --git a/Runtime/Camera/RenderLayers/GUITexture.cpp b/Runtime/Camera/RenderLayers/GUITexture.cpp
new file mode 100644
index 0000000..b30d7e4
--- /dev/null
+++ b/Runtime/Camera/RenderLayers/GUITexture.cpp
@@ -0,0 +1,524 @@
+#include "UnityPrefix.h"
+#include "GUITexture.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Shaders/Material.h"
+#include "External/shaderlab/Library/properties.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Misc/ResourceManager.h"
+
+extern bool IsNPOTTextureAllowed(bool hasMipMap);
+
+static SHADERPROP (MainTex);
+static Shader* gGUI2DShader = NULL;
+static Material* gGUI2DMaterial = NULL;
+
+
+// can't do in InitializeClass because graphics might not be created yet
+static void InitializeGUIShaders()
+{
+ if( !gGUI2DMaterial )
+ {
+ Shader* shader = GetBuiltinResource<Shader> ("Internal-GUITexture.shader");
+ gGUI2DMaterial = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+ gGUI2DShader = gGUI2DMaterial->GetShader ();
+ }
+}
+
+void GUITexture::InitializeClass ()
+{
+}
+
+void GUITexture::CleanupClass ()
+{
+ gGUI2DShader = NULL;
+ gGUI2DMaterial = NULL;
+}
+
+GUITexture::GUITexture (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_Sheet = NULL;
+ m_PrevTextureWidth = 0;
+ m_PrevTextureHeight = 0;
+ m_PrevTextureBaseLevel = Texture::GetMasterTextureLimit();
+}
+
+GUITexture::~GUITexture ()
+{
+ SAFE_RELEASE_LABEL(m_Sheet, kMemShader);
+}
+
+void GUITexture::BuildSheet()
+{
+ InitializeGUIShaders();
+
+ Texture* texture = m_Texture;
+ if( !texture )
+ return;
+
+ SAFE_RELEASE_LABEL(m_Sheet, kMemShader);
+ bool is2D = (texture->GetDimension () == kTexDim2D);
+ m_Sheet = gGUI2DShader->MakeProperties();
+ m_Sheet->SetTexture (kSLPropMainTex, texture);
+
+ ShaderLab::PropertySheet::TextureProperty* prop = m_Sheet->GetTextureProperty (kSLPropMainTex);
+ if (prop && prop->scaleOffsetValue)
+ {
+ // for NPOT textures (in case it is not supported) override the GL texture name and texture scale
+ // so that we use unscaled texture and ignore the padded dummy portion
+ bool isNPOT = !IsPowerOfTwo(m_PrevTextureWidth) || !IsPowerOfTwo(m_PrevTextureHeight);
+ if( is2D && isNPOT && !IsNPOTTextureAllowed(texture->HasMipMap()) )
+ {
+ // Need to take master texture limit into account because that
+ // changes scaling...
+ int baseMipLevel = Texture::GetMasterTextureLimit();
+ if( !texture->HasMipMap() )
+ baseMipLevel = 0;
+ int texWidth = texture->GetDataWidth() >> baseMipLevel;
+ int texHeight = texture->GetDataHeight() >> baseMipLevel;
+ int actualWidth = texture->GetGLWidth() >> baseMipLevel;
+ int actualHeight = texture->GetGLHeight() >> baseMipLevel;
+ // ...and shifting above might produce zeros
+ float scaleX = (actualWidth > 0) ? float(texWidth) / float(actualWidth) : 1.0f;
+ float scaleY = (actualHeight > 0) ? float(texHeight) / float(actualHeight) : 1.0f;
+ scaleX *= texture->GetUVScaleX();
+ scaleY *= texture->GetUVScaleY();
+ prop->texEnv->OverrideTextureInfo( texture->GetUnscaledTextureID(), scaleX, scaleY );
+ prop->scaleOffsetValue->Set (scaleX, scaleY, 0,0);
+ }
+ else
+ {
+ prop->scaleOffsetValue->Set (1,1,0,0);
+ }
+ }
+}
+
+void GUITexture::AwakeFromLoad (AwakeFromLoadMode awakeMode) {
+ Super::AwakeFromLoad (awakeMode);
+ BuildSheet ();
+}
+
+void GUITexture::SetTexture (Texture* tex)
+{
+ m_Texture = tex;
+ if( tex )
+ {
+ m_PrevTextureWidth = tex->GetDataWidth();
+ m_PrevTextureHeight = tex->GetDataHeight();
+ }
+ m_PrevTextureBaseLevel = Texture::GetMasterTextureLimit();
+ if( tex && !tex->HasMipMap() )
+ m_PrevTextureBaseLevel = 0;
+ BuildSheet ();
+}
+
+Texture* GUITexture::GetTexture ()
+{
+ return m_Texture;
+}
+
+void GUITexture::Reset ()
+{
+ Super::Reset ();
+ m_Color = ColorRGBAf (.5, .5, .5, .5);
+ m_LeftBorder = m_RightBorder = m_TopBorder = m_BottomBorder = 0;
+ m_PixelInset = Rectf (0,0,0,0);
+}
+
+void GUITexture::SetColor (const ColorRGBAf& color) {
+ m_Color = color;
+ BuildSheet ();
+ SetDirty ();
+}
+
+struct GUITextureVertex {
+ Vector3f vert;
+ ColorRGBA32 color;
+ Vector2f uv;
+};
+
+static bool FillGUITextureVBOChunk( const Rectf &screenRect, Texture* tex, const Rectf &sourceRect, int leftBorder, int rightBorder, int topBorder, int bottomBorder, ColorRGBA32 color )
+{
+ DebugAssertIf( !tex );
+ Vector2f texScale (1.0f / (float)tex->GetDataWidth(), 1.0f / (float)tex->GetDataHeight());
+
+ float x0 = RoundfToInt(screenRect.x);
+ float x3 = RoundfToInt(screenRect.GetRight());
+
+ int y0 = RoundfToInt(screenRect.y);
+ int y3 = RoundfToInt(screenRect.GetBottom());
+
+ float x1 = x0 + (float)leftBorder;
+ float x2 = x3 - (float)rightBorder;
+ int y1 = int(y0 + (float)bottomBorder);
+ int y2 = int(y3 - (float)topBorder);
+
+ float tx0 = sourceRect.x;
+ float tx1 = tx0 + texScale.x * (float)leftBorder;
+ float tx3 = sourceRect.GetRight();
+ float tx2 = tx3 - texScale.x * (float)rightBorder;
+
+ float ty0 = sourceRect.y;
+ float ty1 = ty0 + texScale.y * (float)bottomBorder;
+ float ty3 = sourceRect.GetBottom();
+ float ty2 = ty3 - texScale.y * (float)topBorder;
+
+ GUITextureVertex* vbPtr;
+ unsigned short* ibPtr;
+ DynamicVBO& vbo = GetGfxDevice().GetDynamicVBO();
+ if( !vbo.GetChunk(
+ (1<<kShaderChannelVertex) | (1<<kShaderChannelColor) | (1<<kShaderChannelTexCoord0),
+ 16, // 16 vertices
+ 9*6, // 9 quads
+ DynamicVBO::kDrawIndexedTriangles,
+ (void**)&vbPtr, (void**)&ibPtr ) )
+ {
+ return false;
+ }
+
+ vbPtr[ 0].vert.Set( x0, y0, 0.0f ); vbPtr[ 0].color = color; vbPtr[ 0].uv.Set( tx0, ty0 );
+ vbPtr[ 1].vert.Set( x1, y0, 0.0f ); vbPtr[ 1].color = color; vbPtr[ 1].uv.Set( tx1, ty0 );
+ vbPtr[ 2].vert.Set( x2, y0, 0.0f ); vbPtr[ 2].color = color; vbPtr[ 2].uv.Set( tx2, ty0 );
+ vbPtr[ 3].vert.Set( x3, y0, 0.0f ); vbPtr[ 3].color = color; vbPtr[ 3].uv.Set( tx3, ty0 );
+ vbPtr[ 4].vert.Set( x0, y1, 0.0f ); vbPtr[ 4].color = color; vbPtr[ 4].uv.Set( tx0, ty1 );
+ vbPtr[ 5].vert.Set( x1, y1, 0.0f ); vbPtr[ 5].color = color; vbPtr[ 5].uv.Set( tx1, ty1 );
+ vbPtr[ 6].vert.Set( x2, y1, 0.0f ); vbPtr[ 6].color = color; vbPtr[ 6].uv.Set( tx2, ty1 );
+ vbPtr[ 7].vert.Set( x3, y1, 0.0f ); vbPtr[ 7].color = color; vbPtr[ 7].uv.Set( tx3, ty1 );
+ vbPtr[ 8].vert.Set( x0, y2, 0.0f ); vbPtr[ 8].color = color; vbPtr[ 8].uv.Set( tx0, ty2 );
+ vbPtr[ 9].vert.Set( x1, y2, 0.0f ); vbPtr[ 9].color = color; vbPtr[ 9].uv.Set( tx1, ty2 );
+ vbPtr[10].vert.Set( x2, y2, 0.0f ); vbPtr[10].color = color; vbPtr[10].uv.Set( tx2, ty2 );
+ vbPtr[11].vert.Set( x3, y2, 0.0f ); vbPtr[11].color = color; vbPtr[11].uv.Set( tx3, ty2 );
+ vbPtr[12].vert.Set( x0, y3, 0.0f ); vbPtr[12].color = color; vbPtr[12].uv.Set( tx0, ty3 );
+ vbPtr[13].vert.Set( x1, y3, 0.0f ); vbPtr[13].color = color; vbPtr[13].uv.Set( tx1, ty3 );
+ vbPtr[14].vert.Set( x2, y3, 0.0f ); vbPtr[14].color = color; vbPtr[14].uv.Set( tx2, ty3 );
+ vbPtr[15].vert.Set( x3, y3, 0.0f ); vbPtr[15].color = color; vbPtr[15].uv.Set( tx3, ty3 );
+
+ static UInt16 ib[9*6] = {
+ 0, 4, 1, 1, 4, 5,
+ 1, 5, 2, 2, 5, 6,
+ 2, 6, 3, 3, 6, 7,
+ 4, 8, 5, 5, 8, 9,
+ 5, 9, 6, 6, 9, 10,
+ 6, 10, 7, 7, 10, 11,
+ 8, 12, 9, 9, 12, 13,
+ 9, 13, 10, 10, 13, 14,
+ 10, 14, 11, 11, 14, 15,
+ };
+ memcpy( ibPtr, ib, sizeof(ib) );
+ vbo.ReleaseChunk( 16, 9*6 );
+
+ return true;
+}
+
+PROFILER_INFORMATION(gRenderGUITexture, "GUITexture.Render", kProfilerGUI)
+PROFILER_INFORMATION(gSubmitVBOProfileGUITexture, "Mesh.SubmitVBO", kProfilerRender)
+
+void GUITexture::DrawGUITexture (const Rectf &bounds)
+{
+ PROFILER_AUTO(gRenderGUITexture, NULL)
+
+ InitializeGUIShaders();
+
+ Shader* shader = gGUI2DShader;
+
+ GfxDevice& device = GetGfxDevice();
+ DynamicVBO& vbo = device.GetDynamicVBO();
+
+ ColorRGBA32 color = m_Color;
+ color = device.ConvertToDeviceVertexColor(color);
+
+ if( !FillGUITextureVBOChunk( bounds, m_Texture, Rectf(0,0,1,1), m_LeftBorder, m_RightBorder, m_TopBorder, m_BottomBorder, color ) )
+ return;
+ const ShaderLab::SubShader& ss = shader->GetShaderLabShader()->GetActiveSubShader();
+ const int passCount = ss.GetValidPassCount();
+ for( int i = 0; i != passCount; ++i )
+ {
+ const ChannelAssigns* channels = shader->SetPass (0, i, 0, m_Sheet);
+
+ PROFILER_BEGIN(gSubmitVBOProfileGUITexture, this)
+ vbo.DrawChunk (*channels);
+ GPU_TIMESTAMP();
+ PROFILER_END
+ }
+}
+
+void GUITexture::RenderGUIElement (const Rectf& cameraRect)
+{
+ Texture* tex = m_Texture;
+ if( !tex )
+ return;
+
+ // Before rendering check whether something serious has changed:
+ // * texture could have changed from POT to NPOT, or the size may have changed (at least in the editor)
+ // * global texture limit might have changed
+ int texWidth = tex->GetDataWidth();
+ int texHeight = tex->GetDataHeight();
+ int masterTexLimit = Texture::GetMasterTextureLimit();
+ if( !tex->HasMipMap() )
+ masterTexLimit = 0;
+ if( texWidth != m_PrevTextureWidth || texHeight != m_PrevTextureHeight || m_PrevTextureBaseLevel != masterTexLimit )
+ {
+ m_PrevTextureWidth = texWidth;
+ m_PrevTextureHeight = texHeight;
+ m_PrevTextureBaseLevel = masterTexLimit;
+ BuildSheet();
+ }
+
+ GfxDevice& device = GetGfxDevice();
+ DeviceMVPMatricesState preserveMVP;
+
+ Rectf rectNoOffset = cameraRect;
+ rectNoOffset.Move( -rectNoOffset.x, -rectNoOffset.y );
+ LoadPixelMatrix( rectNoOffset, device, true, false );
+
+ Rectf drawBox = CalculateDrawBox (cameraRect);
+ DrawGUITexture (drawBox);
+}
+
+Rectf GUITexture::CalculateDrawBox (const Rectf& screenViewportRect)
+{
+ Transform& transform = GetComponent (Transform);
+ Rectf drawBox;
+
+ Vector3f position = transform.GetPosition ();
+ Vector3f scale = transform.GetWorldScaleLossy ();
+
+ float xmin = position.x - scale.x * 0.5F;
+ float xmax = position.x + scale.x * 0.5F;
+
+ float ymin = position.y - scale.y * 0.5F;
+ float ymax = position.y + scale.y * 0.5F;
+
+ drawBox.x = screenViewportRect.Width() * xmin + m_PixelInset.x;
+ drawBox.SetRight( screenViewportRect.Width() * xmax + m_PixelInset.GetRight() );
+ drawBox.y = screenViewportRect.Height() * ymin + m_PixelInset.y;
+ drawBox.SetBottom( screenViewportRect.Height() * ymax + m_PixelInset.GetBottom() );
+
+ return drawBox;
+}
+
+Rectf GUITexture::GetScreenRect (const Rectf& cameraRect)
+{
+ return CalculateDrawBox (cameraRect);
+}
+
+template<class TransferFunction>
+void GUITexture::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER_SIMPLE (m_Texture);
+ TRANSFER_SIMPLE (m_Color);
+
+ TRANSFER (m_PixelInset);
+
+ TRANSFER (m_LeftBorder);
+ TRANSFER (m_RightBorder);
+ TRANSFER (m_TopBorder);
+ TRANSFER (m_BottomBorder);
+}
+
+IMPLEMENT_CLASS_HAS_INIT (GUITexture)
+IMPLEMENT_OBJECT_SERIALIZE (GUITexture)
+
+void DrawGUITexture (const Rectf &screenRect, Texture* texture, ColorRGBA32 color, Material* material) {
+ DrawGUITexture (screenRect, texture, 0,0,0,0, color, material);
+}
+void DrawGUITexture (const Rectf &screenRect, Texture* texture, int leftBorder, int rightBorder, int topBorder, int bottomBorder, ColorRGBA32 color, Material* material) {
+ DrawGUITexture (screenRect, texture, Rectf (0,0,1,1), leftBorder, rightBorder, topBorder, bottomBorder, color, material);
+}
+
+void HandleGUITextureProps( ShaderLab::PropertySheet *sheet, Texture *texture )
+{
+ sheet->SetTexture (kSLPropMainTex, texture);
+
+ int texWidth = texture->GetDataWidth();
+ int texHeight = texture->GetDataHeight();
+
+ ShaderLab::PropertySheet::TextureProperty* prop = sheet->GetTextureProperty (kSLPropMainTex);
+ if (prop && prop->scaleOffsetValue)
+ {
+ float uvScaleX = texture->GetUVScaleX();
+ float uvScaleY = texture->GetUVScaleY();
+
+ // for NPOT textures (in case it is not supported) override the GL texture name and texture scale
+ // so that we use unscaled texture and ignore the padded dummy portion
+ bool isNPOT = !IsPowerOfTwo(texWidth) || !IsPowerOfTwo(texHeight);
+ if( texture->GetDimension() == kTexDim2D && isNPOT && !IsNPOTTextureAllowed(texture->HasMipMap()) )
+ {
+ // Need to take master texture limit into account because that
+ // changes scaling...
+ int baseMipLevel = Texture::GetMasterTextureLimit();
+ if (!texture->HasMipMap())
+ baseMipLevel = 0;
+ texWidth = texWidth >> baseMipLevel;
+ texHeight = texHeight >> baseMipLevel;
+ int actualWidth = texture->GetGLWidth() >> baseMipLevel;
+ int actualHeight = texture->GetGLHeight() >> baseMipLevel;
+ // ...and shifting above might produce zeros
+ float scaleX = (actualWidth > 0) ? float(texWidth) / float(actualWidth) : 1.0f;
+ float scaleY = (actualHeight > 0) ? float(texHeight) / float(actualHeight) : 1.0f;
+ scaleX *= uvScaleX;
+ scaleY *= uvScaleY;
+ prop->texEnv->OverrideTextureInfo( texture->GetUnscaledTextureID(), scaleX, scaleY );
+ prop->scaleOffsetValue->Set (scaleX, scaleY, 0, 0);
+ }
+ else
+ {
+ prop->scaleOffsetValue->Set (uvScaleX,uvScaleY,0,0);
+ }
+ }
+}
+
+// Fills out the VBO with the new inverted-coordinate GUI rectangle
+
+static bool FillGUITextureVBOChunkInverted( const Rectf &screenRect, Texture* tex, const Rectf &sourceRect, int leftBorder, int rightBorder, int topBorder, int bottomBorder, ColorRGBA32 color, UInt32* outTriangles )
+{
+ DebugAssertIf( !tex );
+ Vector2f texScale (1.0f / (float)tex->GetDataWidth(), 1.0f / (float)tex->GetDataHeight());
+
+ float x0 = RoundfToInt(screenRect.x);
+ float x3 = RoundfToInt(screenRect.GetRight());
+ float y0 = RoundfToInt(screenRect.GetBottom());
+ float y3 = RoundfToInt(screenRect.y);
+
+ float tx0 = sourceRect.x;
+ float tx3 = sourceRect.GetRight();
+ float ty0 = sourceRect.y;
+ float ty3 = sourceRect.GetBottom();
+
+ GUITextureVertex* vbPtr;
+ unsigned short* ibPtr;
+ DynamicVBO& vbo = GetGfxDevice().GetDynamicVBO();
+
+ if ((leftBorder|rightBorder|topBorder|bottomBorder) == 0)
+ {
+ // no borders, texture is just a quad
+ if( !vbo.GetChunk(
+ (1<<kShaderChannelVertex) | (1<<kShaderChannelColor) | (1<<kShaderChannelTexCoord0),
+ 4, // 4 vertices
+ 6, // 1 quad
+ DynamicVBO::kDrawIndexedTriangles,
+ (void**)&vbPtr, (void**)&ibPtr ) )
+ {
+ return false;
+ }
+
+ vbPtr[ 0].vert.Set( x0, y0, 0.0f ); vbPtr[ 0].color = color; vbPtr[ 0].uv.Set( tx0, ty0 );
+ vbPtr[ 1].vert.Set( x3, y0, 0.0f ); vbPtr[ 1].color = color; vbPtr[ 1].uv.Set( tx3, ty0 );
+ vbPtr[ 2].vert.Set( x0, y3, 0.0f ); vbPtr[ 2].color = color; vbPtr[ 2].uv.Set( tx0, ty3 );
+ vbPtr[ 3].vert.Set( x3, y3, 0.0f ); vbPtr[ 3].color = color; vbPtr[ 3].uv.Set( tx3, ty3 );
+ static UInt16 ib[1*6] = {
+ 0, 2, 1, 1, 2, 3,
+ };
+ memcpy( ibPtr, ib, sizeof(ib) );
+ vbo.ReleaseChunk( 4, 6 );
+ *outTriangles = 2;
+ }
+ else
+ {
+ // with borders, texture is a 3x3 quad grid
+ float x1 = x0 + (float)leftBorder;
+ float x2 = x3 - (float)rightBorder;
+ float y1 = y0 - (float)topBorder;
+ float y2 = y3 + (float)bottomBorder;
+
+ float tx1 = tx0 + texScale.x * (float)leftBorder;
+ float tx2 = tx3 - texScale.x * (float)rightBorder;
+ float ty1 = ty0 + texScale.y * (float)topBorder;
+ float ty2 = ty3 - texScale.y * (float)bottomBorder;
+
+ if( !vbo.GetChunk(
+ (1<<kShaderChannelVertex) | (1<<kShaderChannelColor) | (1<<kShaderChannelTexCoord0),
+ 16, // 16 vertices
+ 9*6, // 9 quads
+ DynamicVBO::kDrawIndexedTriangles,
+ (void**)&vbPtr, (void**)&ibPtr ) )
+ {
+ return false;
+ }
+
+ vbPtr[ 0].vert.Set( x0, y0, 0.0f ); vbPtr[ 0].color = color; vbPtr[ 0].uv.Set( tx0, ty0 );
+ vbPtr[ 1].vert.Set( x1, y0, 0.0f ); vbPtr[ 1].color = color; vbPtr[ 1].uv.Set( tx1, ty0 );
+ vbPtr[ 2].vert.Set( x2, y0, 0.0f ); vbPtr[ 2].color = color; vbPtr[ 2].uv.Set( tx2, ty0 );
+ vbPtr[ 3].vert.Set( x3, y0, 0.0f ); vbPtr[ 3].color = color; vbPtr[ 3].uv.Set( tx3, ty0 );
+ vbPtr[ 4].vert.Set( x0, y1, 0.0f ); vbPtr[ 4].color = color; vbPtr[ 4].uv.Set( tx0, ty1 );
+ vbPtr[ 5].vert.Set( x1, y1, 0.0f ); vbPtr[ 5].color = color; vbPtr[ 5].uv.Set( tx1, ty1 );
+ vbPtr[ 6].vert.Set( x2, y1, 0.0f ); vbPtr[ 6].color = color; vbPtr[ 6].uv.Set( tx2, ty1 );
+ vbPtr[ 7].vert.Set( x3, y1, 0.0f ); vbPtr[ 7].color = color; vbPtr[ 7].uv.Set( tx3, ty1 );
+ vbPtr[ 8].vert.Set( x0, y2, 0.0f ); vbPtr[ 8].color = color; vbPtr[ 8].uv.Set( tx0, ty2 );
+ vbPtr[ 9].vert.Set( x1, y2, 0.0f ); vbPtr[ 9].color = color; vbPtr[ 9].uv.Set( tx1, ty2 );
+ vbPtr[10].vert.Set( x2, y2, 0.0f ); vbPtr[10].color = color; vbPtr[10].uv.Set( tx2, ty2 );
+ vbPtr[11].vert.Set( x3, y2, 0.0f ); vbPtr[11].color = color; vbPtr[11].uv.Set( tx3, ty2 );
+ vbPtr[12].vert.Set( x0, y3, 0.0f ); vbPtr[12].color = color; vbPtr[12].uv.Set( tx0, ty3 );
+ vbPtr[13].vert.Set( x1, y3, 0.0f ); vbPtr[13].color = color; vbPtr[13].uv.Set( tx1, ty3 );
+ vbPtr[14].vert.Set( x2, y3, 0.0f ); vbPtr[14].color = color; vbPtr[14].uv.Set( tx2, ty3 );
+ vbPtr[15].vert.Set( x3, y3, 0.0f ); vbPtr[15].color = color; vbPtr[15].uv.Set( tx3, ty3 );
+ static UInt16 ib[9*6] = {
+ 0, 4, 1, 1, 4, 5, // Top-left
+ 1, 5, 2, 2, 5, 6, // Top-mid
+ 2, 6, 3, 3, 6, 7, // Top-right
+ 4, 8, 5, 5, 8, 9, // mid-left
+ 5, 9, 6, 6, 9, 10, // mid-center
+ 6, 10, 7, 7, 10, 11, // mid-right
+ 8, 12, 9, 9, 12, 13, // bottom-left
+ 9, 13, 10, 10, 13, 14, // bottom-mid
+ 10, 14, 11, 11, 14, 15, // bottom-right
+ };
+ memcpy( ibPtr, ib, sizeof(ib) );
+ vbo.ReleaseChunk( 16, 9*6 );
+ *outTriangles = 9 * 2;
+ }
+
+ return true;
+}
+
+void DrawGUITexture (const Rectf &screenRect, Texture* tex, const Rectf &sourceRect, int leftBorder, int rightBorder, int topBorder, int bottomBorder, ColorRGBA32 color, Material* material)
+{
+ InitializeGUIShaders();
+
+ if( !tex )
+ {
+ ErrorString ("DrawGUITexture: texture is null");
+ return;
+ }
+
+ GfxDevice& device = GetGfxDevice();
+ color = device.ConvertToDeviceVertexColor( color );
+ UInt32 triCount;
+
+ // temp workaround to flip Y-axis. should probably be done in FillGUITextureVBOChunk - but for now we do it here
+ if( !FillGUITextureVBOChunkInverted( screenRect, tex, sourceRect, leftBorder, rightBorder, bottomBorder, topBorder, color, &triCount ) )
+ return;
+
+ if (material)
+ {
+ HandleGUITextureProps( &material->GetWritableProperties(), tex );
+ }
+ else
+ {
+ HandleGUITextureProps( &gGUI2DMaterial->GetWritableProperties(), tex );
+ material = gGUI2DMaterial;
+ }
+
+ int passCount = material->GetPassCount();
+
+ DynamicVBO& vbo = device.GetDynamicVBO();
+
+ for( int i = 0; i < passCount; ++i)
+ {
+ const ChannelAssigns* channels = material->SetPass (i, 0, false);
+
+ PROFILER_BEGIN(gSubmitVBOProfileGUITexture, NULL);
+ vbo.DrawChunk (*channels);
+ GPU_TIMESTAMP();
+ PROFILER_END
+ }
+}
diff --git a/Runtime/Camera/RenderLayers/GUITexture.h b/Runtime/Camera/RenderLayers/GUITexture.h
new file mode 100644
index 0000000..55988ae
--- /dev/null
+++ b/Runtime/Camera/RenderLayers/GUITexture.h
@@ -0,0 +1,73 @@
+#ifndef GUITEXTURE_H
+#define GUITEXTURE_H
+
+#include "GUIElement.h"
+#include "Runtime/Graphics/Texture.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Rect.h"
+
+
+namespace Unity { class Material; }
+namespace ShaderLab { class PropertySheet; }
+
+// Attached to any game object in the scene.
+// Registers with GUILayer, GUILayer renders it.
+// Position comes from transform.position.x,y
+// size comes from transform.scale.x,y
+class GUITexture : public GUIElement
+{
+public:
+
+ REGISTER_DERIVED_CLASS (GUITexture, GUIElement)
+ DECLARE_OBJECT_SERIALIZE (GUITexture)
+
+ GUITexture (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void Reset ();
+
+ // GUIElement
+ virtual void RenderGUIElement (const Rectf& cameraRect);
+ virtual Rectf GetScreenRect (const Rectf& cameraRect);
+
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ void SetColor (const ColorRGBAf& color);
+ inline ColorRGBAf GetColor () { return m_Color; }
+
+ void SetTexture (Texture* tex);
+ Texture* GetTexture ();
+
+ int m_LeftBorder; ///< The border pixels - this part of texture is never scaled.
+ int m_RightBorder; ///< The border pixels - this part of texture is never scaled.
+ int m_TopBorder; ///< The border pixels - this part of texture is never scaled.
+ int m_BottomBorder; ///< The border pixels - this part of texture is never scaled.
+
+ PPtr<Texture> m_Texture; ///< The texture to use.
+ ColorRGBAf m_Color; ///< Tint color.
+
+ static void InitializeClass();
+ static void CleanupClass();
+
+ const Rectf &GetPixelInset() const { return m_PixelInset; }
+ void SetPixelInset(const Rectf &r) { m_PixelInset = r; SetDirty(); }
+
+private:
+ void DrawGUITexture (const Rectf& bounds);
+ void BuildSheet ();
+ Rectf CalculateDrawBox (const Rectf& screenViewportRect);
+
+ Rectf m_PixelInset;
+
+ ShaderLab::PropertySheet *m_Sheet;
+ int m_PrevTextureWidth;
+ int m_PrevTextureHeight;
+ int m_PrevTextureBaseLevel;
+};
+
+// Immediate mode DrawGUITexture API
+void DrawGUITexture (const Rectf &screenRect, Texture* texture, const Rectf &sourceRect, int leftBorder, int rightBorder, int topBorder, int bottomBorder, ColorRGBA32 color, Material* material = NULL);
+void DrawGUITexture (const Rectf &screenRect, Texture* texture, int leftBorder, int rightBorder, int topBorder, int bottomBorder, ColorRGBA32 color, Material* material = NULL);
+void DrawGUITexture (const Rectf &screenRect, Texture* texture, ColorRGBA32 color, Material* material = NULL);
+void HandleGUITextureProps (ShaderLab::PropertySheet *sheet, Texture *texture);
+
+#endif
diff --git a/Runtime/Camera/RenderLoops/BuiltinShaderParamUtility.cpp b/Runtime/Camera/RenderLoops/BuiltinShaderParamUtility.cpp
new file mode 100644
index 0000000..99e5d22
--- /dev/null
+++ b/Runtime/Camera/RenderLoops/BuiltinShaderParamUtility.cpp
@@ -0,0 +1,19 @@
+#include "UnityPrefix.h"
+#include "BuiltinShaderParamUtility.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/ShaderKeywords.h"
+
+static ShaderKeyword gSupportedLODFadeKeyword = keywords::Create("ENABLE_LOD_FADE");
+
+void SetObjectScale (GfxDevice& device, float lodFade, float invScale)
+{
+ device.SetInverseScale(invScale);
+
+ /////@TODO: Figure out why inverse scale is implemented in gfxdevice, and decide if we should do the same for lodFade?
+ device.GetBuiltinParamValues().SetInstanceVectorParam(kShaderInstanceVecScale, Vector4f(0,0,lodFade, invScale));
+
+ if (lodFade == LOD_FADE_DISABLED)
+ g_ShaderKeywords.Disable(gSupportedLODFadeKeyword);
+ else
+ g_ShaderKeywords.Enable(gSupportedLODFadeKeyword);
+}
diff --git a/Runtime/Camera/RenderLoops/BuiltinShaderParamUtility.h b/Runtime/Camera/RenderLoops/BuiltinShaderParamUtility.h
new file mode 100644
index 0000000..7b480ed
--- /dev/null
+++ b/Runtime/Camera/RenderLoops/BuiltinShaderParamUtility.h
@@ -0,0 +1,11 @@
+#pragma once
+
+
+///@TODO: This should probably be 0. But for now we don't have proper ifdef support for switching to a different subshader.
+#define LOD_FADE_DISABLED 0.999F
+
+#define LOD_FADE_BATCH_EPSILON 0.0625 // 1/16
+
+class GfxDevice;
+
+void SetObjectScale (GfxDevice& device, float lodFade, float invScale);
diff --git a/Runtime/Camera/RenderLoops/ForwardShaderRenderLoop.cpp b/Runtime/Camera/RenderLoops/ForwardShaderRenderLoop.cpp
new file mode 100644
index 0000000..44e91b8
--- /dev/null
+++ b/Runtime/Camera/RenderLoops/ForwardShaderRenderLoop.cpp
@@ -0,0 +1,1403 @@
+#include "UnityPrefix.h"
+#include "Runtime/GfxDevice/GfxDeviceConfigure.h"
+
+#include "RenderLoopPrivate.h"
+#include "RenderLoop.h"
+#include "Runtime/Camera/Renderqueue.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Camera/Renderable.h"
+#include "Runtime/Camera/Light.h"
+#include "Runtime/Camera/RenderSettings.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Camera/Shadows.h"
+#include "Runtime/Camera/LODGroupManager.h"
+#include "Runtime/Graphics/RenderBufferManager.h"
+#include "Runtime/Graphics/GraphicsHelper.h"
+#include "Runtime/Graphics/LightmapSettings.h"
+#include "Runtime/Graphics/Transform.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "External/shaderlab/Library/properties.h"
+#include "Runtime/Misc/QualitySettings.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/GfxDevice/BatchRendering.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "BuiltinShaderParamUtility.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Camera/LightManager.h"
+#include "External/MurmurHash/MurmurHash2.h"
+
+
+// Enable/disable hash based forward shader render loop sorting functionality.
+#define ENABLE_FORWARD_SHADER_LOOP_HASH_SORTING 0
+
+PROFILER_INFORMATION(gFwdOpaquePrepare, "RenderForwardOpaque.Prepare", kProfilerRender)
+PROFILER_INFORMATION(gFwdOpaqueSort, "RenderForwardOpaque.Sort", kProfilerRender)
+PROFILER_INFORMATION(gFwdOpaqueCollectShadows, "RenderForwardOpaque.CollectShadows", kProfilerRender)
+PROFILER_INFORMATION(gFwdOpaqueRender, "RenderForwardOpaque.Render", kProfilerRender)
+PROFILER_INFORMATION(gFwdAlphaPrepare, "RenderForwardAlpha.Prepare", kProfilerRender)
+PROFILER_INFORMATION(gFwdAlphaSort, "RenderForwardAlpha.Sort", kProfilerRender)
+PROFILER_INFORMATION(gFwdAlphaRender, "RenderForwardAlpha.Render", kProfilerRender)
+
+static SHADERPROP (ShadowMapTexture);
+
+
+static inline bool CompareLights (ForwardLightsBlock const* a, ForwardLightsBlock const* b)
+{
+ if (!a || !b)
+ return false;
+
+ if (a->mainLight != b->mainLight)
+ return false;
+ if (a->vertexLightCount != b->vertexLightCount)
+ return false;
+ if (a->addLightCount != b->addLightCount)
+ return false;
+
+ int totalLightCount = a->vertexLightCount + a->addLightCount;
+ const ActiveLight* const* lightsA = a->GetLights();
+ const ActiveLight* const* lightsB = b->GetLights();
+ for (int i = 0; i < totalLightCount; ++i)
+ if (lightsA[i] != lightsB[i])
+ return false;
+
+ if (memcmp(a->sh, b->sh, sizeof(a->sh)) != 0)
+ return false;
+
+ if (!CompareApproximately(a->lastAddLightBlend, b->lastAddLightBlend))
+ return false;
+ if (!CompareApproximately(a->lastVertexLightBlend, b->lastVertexLightBlend))
+ return false;
+
+ return true;
+}
+
+struct RenderObjectDataCold {
+ float invScale; // 4
+ float lodFade; // 4
+ size_t lightsDataOffset; // 4 into memory block with all light data chunks
+ int subshaderIndex; // 4
+ // 16 bytes
+};
+
+
+namespace ForwardShaderRenderLoop_Enum
+{
+// Render pass data here is 8 bytes each; an index of the render object and "the rest" packed
+// into 4 bytes.
+enum {
+ kPackPassShift = 0,
+ kPackPassMask = 0xFF,
+ kPackTypeShift = 8,
+ kPackTypeMask = 0xFF,
+ kPackFirstPassFlag = (1<<24),
+ kPackMultiPassFlag = (1<<25),
+};
+
+} // namespace ForwardShaderRenderLoop_Enum
+
+struct RenderPassData {
+ int roIndex;
+ // Packed into UInt32: pass number, pass type, first pass flag, multipass flag
+ UInt32 data;
+#if ENABLE_FORWARD_SHADER_LOOP_HASH_SORTING
+ // state hash for optimizing render object sorter
+ UInt32 hash;
+#endif
+};
+typedef dynamic_array<RenderPassData> RenderPasses;
+
+
+struct ForwardShaderRenderState
+{
+ int rendererType;
+ int transformType;
+
+ float invScale;
+ float lodFade;
+
+ Material* material;
+ Shader* shader;
+ int subshaderIndex;
+ ShaderPassType passType;
+ int passIndex;
+
+ const ForwardLightsBlock* lights;
+ int receiveShadows;
+
+ int lightmapIndex;
+ Vector4f lightmapST;
+
+ UInt32 customPropsHash;
+
+
+ void Invalidate()
+ {
+ rendererType = -1;
+ transformType = -1;
+ invScale = 0.0f;
+ lodFade = 0.0F;
+ material = 0; shader = 0; subshaderIndex = -1; passType = kShaderPassTypeCount; passIndex = -1;
+ lights = 0;
+ lightmapIndex = -1; lightmapST = Vector4f(0,0,0,0);
+ receiveShadows = -1;
+ customPropsHash = 0;
+ }
+
+ bool operator == (const ForwardShaderRenderState& rhs) const
+ {
+ if (this == &rhs)
+ return true;
+
+ return (
+ rendererType == rhs.rendererType &&
+ transformType == rhs.transformType &&
+ material == rhs.material &&
+ shader == rhs.shader &&
+ CompareLights(lights, rhs.lights) &&
+ subshaderIndex == rhs.subshaderIndex &&
+ passType == rhs.passType &&
+ passIndex == rhs.passIndex &&
+ CompareApproximately(invScale,rhs.invScale) &&
+ CompareApproximately(lodFade,rhs.lodFade, LOD_FADE_BATCH_EPSILON) &&
+ #if ENABLE_SHADOWS
+ receiveShadows == rhs.receiveShadows &&
+ #endif
+ lightmapIndex == rhs.lightmapIndex &&
+ lightmapST == rhs.lightmapST &&
+ customPropsHash == rhs.customPropsHash
+ );
+ }
+
+ bool operator != (const ForwardShaderRenderState& rhs) const
+ {
+ return !(rhs == *this);
+ }
+};
+
+
+struct ForwardShadowMap
+{
+ ForwardShadowMap() : light(NULL), texture(NULL) {}
+ const ActiveLight* light;
+ RenderTexture* texture;
+ Matrix4x4f shadowMatrix;
+ MinMaxAABB receiverBounds;
+};
+typedef dynamic_array<ForwardShadowMap> ForwardShadowMaps;
+
+struct CompactShadowCollectorSortData;
+
+struct ForwardShaderRenderLoop
+{
+ const RenderLoopContext* m_Context;
+ RenderObjectDataContainer* m_Objects;
+
+ dynamic_array<RenderObjectDataCold> m_RenderObjectsCold;
+ dynamic_array<UInt8> m_RenderObjectsLightData;
+
+ RenderPasses m_PlainRenderPasses;
+ #if ENABLE_SHADOWS
+ ForwardShadowMap m_MainShadowMap;
+ ForwardShadowMaps m_ShadowMaps;
+ // Render object indices of shadow receivers.
+ // This includes both shadow receivers and objects that have shadows off, but
+ // are within shadow distance. They should still participate in screenspace shadow
+ // gathering, otherwise shadows will be visible through them.
+ dynamic_array<int> m_ReceiverObjects;
+ #endif
+
+ BatchRenderer m_BatchRenderer;
+
+ ForwardShaderRenderLoop()
+ : m_RenderObjectsCold (kMemTempAlloc)
+ , m_RenderObjectsLightData (kMemTempAlloc)
+ , m_PlainRenderPasses (kMemTempAlloc)
+ #if ENABLE_SHADOWS
+ , m_ShadowMaps (kMemTempAlloc)
+ , m_ReceiverObjects (kMemTempAlloc)
+ #endif
+ { }
+
+ void PerformRendering (const ActiveLight* mainDirShadowLight, RenderTexture* existingShadowMap, const ShadowCullData& shadowCullData, bool disableDynamicBatching, bool sRGBrenderTarget, bool clearFrameBuffer);
+ #if ENABLE_SHADOWS
+ RenderTexture* CollectShadows (RenderTexture* inputShadowMap, const Light* light, const Matrix4x4f* shadowMatrices, const float* splitDistances, const Vector4f* splitSphereCentersAndSquaredRadii, bool enableSoftShadows, bool useDualInForward, bool clearFrameBuffer);
+ void RenderLightShadowMaps (ForwardShadowMap& shadowMap, ShadowCameraData& camData, bool enableSoftShadows, bool useDualInForward, bool clearFrameBuffer);
+ int SortShadowCollectorsCompact(CompactShadowCollectorSortData* _resultOrder);
+ #endif
+
+ template <bool opaque>
+ struct RenderObjectSorter
+ {
+ bool operator()( const RenderPassData& ra, const RenderPassData& rb ) const;
+ const ForwardShaderRenderLoop* queue;
+ };
+
+ template <bool opaque>
+ void SortRenderPassData( RenderPasses& passes )
+ {
+ RenderObjectSorter<opaque> sorter;
+ sorter.queue = this;
+ std::sort( passes.begin(), passes.end(), sorter );
+ }
+};
+
+
+template <bool opaque>
+bool ForwardShaderRenderLoop::RenderObjectSorter<opaque>::operator() (const RenderPassData& ra, const RenderPassData& rb) const
+{
+ using namespace ForwardShaderRenderLoop_Enum;
+
+ const RenderObjectData& dataa = (*queue->m_Objects)[ra.roIndex];
+ const RenderObjectData& datab = (*queue->m_Objects)[rb.roIndex];
+
+ // Sort by layering depth.
+ bool globalLayeringResult;
+ if (CompareGlobalLayeringData(dataa.globalLayeringData, datab.globalLayeringData, globalLayeringResult))
+ return globalLayeringResult;
+
+#if ENABLE_FORWARD_SHADER_LOOP_HASH_SORTING
+
+ if (!opaque)
+ {
+ // Sort by render queues first
+ if( dataa.queueIndex != datab.queueIndex )
+ return dataa.queueIndex < datab.queueIndex;
+
+#if DEBUGMODE
+ DebugAssertIf (dataa.queueIndex >= kQueueIndexMin && dataa.queueIndex <= kGeometryQueueIndexMax); // this is alpha loop!
+#endif
+
+ // Sort strictly by distance unless they are equal
+ if( dataa.distance != datab.distance )
+ return dataa.distance < datab.distance;
+ }
+
+ UInt64 keya = (0x0000ffff-((dataa.queueIndex)&0x0000ffff))<<16;
+ UInt64 keyb = (0x0000ffff-((datab.queueIndex)&0x0000ffff))<<16;
+
+ keya |= (ra.data & kPackFirstPassFlag)>>(24-8);
+ keyb |= (rb.data & kPackFirstPassFlag)>>(24-8);
+ keya |= (0x000000ff-((dataa.lightmapIndex)&0x000000ff));
+ keyb |= (0x000000ff-((datab.lightmapIndex)&0x000000ff));
+ keya = keya << 32;
+ keyb = keyb << 32;
+ keya |= ra.hash;
+ keyb |= rb.hash;
+
+ //Sort keys, TODO try to move the key generation outside the sorting loop
+ if( keya != keyb )
+ return (keya > keyb);
+
+#if DEBUGMODE
+ if (opaque)
+ {
+ DebugAssertIf (dataa.queueIndex < kQueueIndexMin || dataa.queueIndex > kGeometryQueueIndexMax); // this is opaque loop!
+ }
+#endif
+
+ //fall though distance, TODO insert distance into the key
+ return dataa.distance > datab.distance;
+
+#else
+
+ // Sort by render queues first
+ if( dataa.queueIndex != datab.queueIndex )
+ return dataa.queueIndex < datab.queueIndex;
+
+#if DEBUGMODE
+ if (opaque) {
+ DebugAssertIf (dataa.queueIndex < kQueueIndexMin || dataa.queueIndex > kGeometryQueueIndexMax); // this is opaque loop!
+ } else {
+ DebugAssertIf (dataa.queueIndex >= kQueueIndexMin && dataa.queueIndex <= kGeometryQueueIndexMax); // this is alpha loop!
+ }
+#endif
+
+ if (!opaque)
+ {
+ // Sort strictly by distance unless they are equal
+ if( dataa.distance != datab.distance )
+ return dataa.distance < datab.distance;
+ }
+
+ UInt32 flagsa = ra.data;
+ UInt32 flagsb = rb.data;
+
+ // render all first passes first
+ if( (flagsa & kPackFirstPassFlag) != (flagsb & kPackFirstPassFlag) )
+ return (flagsa & kPackFirstPassFlag) > (flagsb & kPackFirstPassFlag);
+
+ // sort by lightmap index (fine to do it before source material index
+ // since every part of same mesh will have the same lightmap index)
+ if( dataa.lightmapIndex != datab.lightmapIndex )
+ return dataa.lightmapIndex < datab.lightmapIndex;
+
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+ // if part of predefined static batch, then sort by static batch index
+ // prefer static batched first as they usually cover quite a lot
+ if( dataa.staticBatchIndex != datab.staticBatchIndex )
+ return dataa.staticBatchIndex > datab.staticBatchIndex;
+
+ // otherwise sort by material index. Some people are using multiple materials
+ // on a single mesh and expect them to be rendered in order.
+ if( dataa.staticBatchIndex == 0 && dataa.sourceMaterialIndex != datab.sourceMaterialIndex )
+ return dataa.sourceMaterialIndex < datab.sourceMaterialIndex;
+#else
+ // Sort by material index. Some people are using multiple materials
+ // on a single mesh and expect them to be rendered in order.
+ if( dataa.sourceMaterialIndex != datab.sourceMaterialIndex )
+ return dataa.sourceMaterialIndex < datab.sourceMaterialIndex;
+#endif
+
+ // sort by shader
+ if( dataa.shader != datab.shader )
+ return dataa.shader->GetInstanceID() < datab.shader->GetInstanceID(); // just compare instance IDs
+
+ // then sort by material
+ if( dataa.material != datab.material )
+ return dataa.material->GetInstanceID() < datab.material->GetInstanceID(); // just compare instance IDs
+
+ // inside same material: by pass
+ UInt32 passa = (flagsa >> kPackPassShift) & kPackPassMask;
+ UInt32 passb = (flagsb >> kPackPassShift) & kPackPassMask;
+ if( passa != passb )
+ return passa < passb;
+
+ if (opaque)
+ {
+ // Sort by distance in reverse order.
+ // That way we get consistency in render order, and more pixels not rendered due to z-testing,
+ // which benefits performance.
+ if( dataa.distance != datab.distance )
+ return dataa.distance > datab.distance;
+ }
+
+ // fall through: roIndex
+ return ra.roIndex < rb.roIndex;
+
+#endif // ENABLE_FORWARD_SHADER_LOOP_HASH_SORTING
+}
+
+#if ENABLE_SHADOWS
+static void SetLightShadowProps (const Camera& camera, const Light& light, Texture* shadowMap, const Matrix4x4f& shadowMatrix, bool useDualInForward)
+{
+ const float shadowStrength = light.GetShadowStrength();
+ DebugAssert (shadowMap);
+
+ ShaderLab::PropertySheet *props = ShaderLab::g_GlobalProperties;
+ BuiltinShaderParamValues& params = GetGfxDevice().GetBuiltinParamValues();
+
+ // shadow matrix
+ CopyMatrix (shadowMatrix.GetPtr(), params.GetWritableMatrixParam(kShaderMatWorldToShadow).GetPtr());
+
+ props->SetTexture( kSLPropShadowMapTexture, shadowMap );
+
+ if (light.GetType() == kLightPoint)
+ {
+ const Vector3f lightPos = light.GetWorldPosition();
+ params.SetVectorParam(kShaderVecLightPositionRange, Vector4f(lightPos.x, lightPos.y, lightPos.z, 1.0f/light.GetRange()));
+ }
+
+ // ambient & shadow fade out
+ Vector4f lightFade;
+ Vector4f fadeCenterAndType;
+ CalculateLightShadowFade (camera, shadowStrength, lightFade, fadeCenterAndType);
+ params.SetVectorParam(kShaderVecLightmapFade, lightFade);
+ if (useDualInForward)
+ lightFade.z = lightFade.w = 0.0f;
+ params.SetVectorParam(kShaderVecLightShadowData, lightFade);
+ params.SetVectorParam(kShaderVecShadowFadeCenterAndType, fadeCenterAndType);
+ // texel offsets for PCF
+ Vector4f offsets;
+ float offX = 0.5f / shadowMap->GetGLWidth();
+ float offY = 0.5f / shadowMap->GetGLHeight();
+ offsets.z = 0.0f; offsets.w = 0.0f;
+ offsets.x = -offX; offsets.y = -offY; params.SetVectorParam(kShaderVecShadowOffset0, offsets);
+ offsets.x = offX; offsets.y = -offY; params.SetVectorParam(kShaderVecShadowOffset1, offsets);
+ offsets.x = -offX; offsets.y = offY; params.SetVectorParam(kShaderVecShadowOffset2, offsets);
+ offsets.x = offX; offsets.y = offY; params.SetVectorParam(kShaderVecShadowOffset3, offsets);
+}
+static void SetLightShadowCollectProps (const Camera& camera, const Light& light, Texture* shadowMap, const Matrix4x4f* shadowMatrices, const float* splitDistances, const Vector4f* splitSphereCentersAndSquaredRadii, bool useDualInForward)
+{
+ DebugAssert (shadowMatrices && shadowMap);
+ SetLightShadowProps (camera, light, shadowMap, shadowMatrices[0], useDualInForward);
+ SetCascadedShadowShaderParams (shadowMatrices, splitDistances, splitSphereCentersAndSquaredRadii);
+}
+#endif // ENABLE_SHADOWS
+
+
+
+void ForwardShaderRenderLoop::PerformRendering (const ActiveLight* mainDirShadowLight, RenderTexture* existingShadowMap, const ShadowCullData& shadowCullData, bool disableDynamicBatching, bool sRGBrenderTarget, bool clearFrameBuffer)
+{
+ using namespace ForwardShaderRenderLoop_Enum;
+
+ const RenderManager::Renderables& renderables = GetRenderManager ().GetRenderables ();
+ RenderManager::Renderables::const_iterator renderablesBegin = renderables.begin(), renderablesEnd = renderables.end();
+
+ SetNoShadowsKeywords();
+
+ GfxDevice& device = GetGfxDevice();
+ // save current scissor params
+ int oldScissorRect[4];
+ device.GetScissorRect(oldScissorRect);
+ const bool oldScissor = device.IsScissorEnabled();
+
+ #if ENABLE_SHADOWS
+ const bool enableSoftShadows = GetSoftShadowsEnabled();
+ ShadowCameraData camData(shadowCullData);
+ ForwardShadowMap mainLightShadowMap;
+ const bool hasAnyShadows = (mainDirShadowLight != 0 || !m_ShadowMaps.empty());
+ const bool useDualInForward = GetLightmapSettings().GetUseDualLightmapsInForward();
+
+ // shadow map of main directional light
+ if (mainDirShadowLight != 0)
+ {
+ // Render shadow map
+ if (!existingShadowMap)
+ {
+ // Prevent receiver bounds to be zero size in any dimension;
+ // causes trouble with calculating intersection of frustum and bounds.
+ mainLightShadowMap.receiverBounds = m_MainShadowMap.receiverBounds;
+ mainLightShadowMap.receiverBounds.Expand (0.01f);
+ mainLightShadowMap.light = mainDirShadowLight;
+
+ // One directional light can have shadows in free version, so temporarily
+ // enable render textures just for that.
+ RenderTexture::SetTemporarilyAllowIndieRenderTexture (true);
+ RenderLightShadowMaps (mainLightShadowMap, camData, enableSoftShadows, useDualInForward, clearFrameBuffer);
+ RenderTexture::SetTemporarilyAllowIndieRenderTexture (false);
+
+ // There were no shadow casters - no shadowmap is produced
+ if (!mainLightShadowMap.texture)
+ mainDirShadowLight = 0;
+ }
+ else
+ {
+ mainLightShadowMap.texture = existingShadowMap;
+ }
+ }
+
+ // shadow maps of other lights
+ for (ForwardShadowMaps::iterator it = m_ShadowMaps.begin(), itEnd = m_ShadowMaps.end(); it != itEnd; ++it)
+ {
+ ForwardShadowMap& shadowMap = *it;
+
+ // Prevent receiver bounds to be zero size in any dimension;
+ // causes trouble with calculating intersection of frustum and bounds.
+ shadowMap.receiverBounds.Expand (0.01f);
+
+ RenderLightShadowMaps (shadowMap, camData, enableSoftShadows, false, clearFrameBuffer);
+ }
+
+ if (hasAnyShadows)
+ {
+ m_Context->m_Camera->SetupRender (Camera::kRenderFlagSetRenderTarget);
+ SetNoShadowsKeywords ();
+ }
+ #endif
+
+ const RenderSettings& renderSettings = GetRenderSettings();
+ const LightmapSettings& lightmapper = GetLightmapSettings();
+ size_t npasses = m_PlainRenderPasses.size();
+
+ int currentQueueIndex = m_Context->m_RenderQueueStart;
+
+ device.SetViewMatrix( m_Context->m_CurCameraMatrix.GetPtr() );
+
+ ForwardShaderRenderState prevRenderState;
+ prevRenderState.Invalidate();
+
+ //If we are in linear lighting enable sRGB writes here...
+ device.SetSRGBWrite(sRGBrenderTarget);
+ if (clearFrameBuffer)
+ m_Context->m_Camera->ClearNoSkybox(false);
+ else
+ device.IgnoreNextUnresolveOnCurrentRenderTarget();
+
+ const ChannelAssigns* channels = NULL;
+
+ for( size_t i = 0; i < npasses; ++i )
+ {
+ const RenderPassData& rpData = m_PlainRenderPasses[i];
+ const RenderObjectData& roDataH = (*m_Objects)[rpData.roIndex];
+ const RenderObjectDataCold& roDataC = m_RenderObjectsCold[rpData.roIndex];
+ const ForwardLightsBlock& roDataL = *reinterpret_cast<ForwardLightsBlock*>(&m_RenderObjectsLightData[roDataC.lightsDataOffset]);
+
+ // We're going over all things that need to be rendered in increasing
+ // render queue order. Whenever we switch to the new queue, we must
+ // invoke all "camera renderables" (halos, flares and so on).
+ const int roQueueIndex = roDataH.queueIndex;
+ DebugAssert (roQueueIndex >= currentQueueIndex);
+ if( roQueueIndex > currentQueueIndex )
+ {
+ m_BatchRenderer.Flush();
+
+ // Draw required renderables
+ if (!m_Context->m_DontRenderRenderables)
+ {
+ while( renderablesBegin != renderablesEnd && renderablesBegin->first <= roQueueIndex )
+ {
+ renderablesBegin->second->RenderRenderable(*m_Context->m_CullResults);
+ ++renderablesBegin;
+ }
+ }
+
+ currentQueueIndex = roQueueIndex;
+ }
+
+ const VisibleNode *node = roDataH.visibleNode;
+ const UInt16 subsetIndex = roDataH.subsetIndex;
+
+ ForwardShaderRenderState rs;
+ {
+ rs.rendererType = node->renderer->GetRendererType();
+ rs.transformType = node->transformType;
+ rs.invScale = roDataC.invScale;
+ rs.lodFade = roDataC.lodFade;
+
+ rs.material = roDataH.material;
+ rs.shader = roDataH.shader;
+ rs.subshaderIndex = roDataC.subshaderIndex;
+ rs.passType = (ShaderPassType)((rpData.data >> kPackTypeShift) & kPackTypeMask);
+ rs.passIndex = (rpData.data >> kPackPassShift) & kPackPassMask;
+
+ rs.lights = &roDataL;
+ #if ENABLE_SHADOWS
+ rs.receiveShadows = hasAnyShadows && node->renderer->GetReceiveShadows() && IsObjectWithinShadowRange (*m_Context->m_ShadowCullData, node->worldAABB);
+ #endif
+
+ rs.lightmapIndex = roDataH.lightmapIndex;
+ DebugAssert(rs.lightmapIndex == node->renderer->GetLightmapIndex());
+ rs.lightmapST = node->renderer->GetLightmapSTForRendering();
+ rs.customPropsHash = node->renderer->GetCustomPropertiesHash();
+ }
+
+
+ // multi-pass requires vertex position values to be EXACTLY the same for all passes
+ // therefore do NOT batch dynamic multi-pass nodes
+ // same for shadow casters
+ const bool multiPass = (rpData.data & kPackMultiPassFlag) == kPackMultiPassFlag;
+ const bool dynamicShouldNotBatch = (node->renderer->GetStaticBatchIndex() == 0) && (multiPass || disableDynamicBatching);
+
+ #if ENABLE_SHADOWS
+ const bool dynamicAndShadowCaster = (node->renderer->GetStaticBatchIndex() == 0) && (mainDirShadowLight != 0) && node->renderer->GetCastShadows();
+ #else
+ const bool dynamicAndShadowCaster = false;
+ #endif
+
+ bool shouldResetPass;
+ if (rs.passType == kPassForwardAdd || // rendering multiple different lights in a row - impossible to batch
+ prevRenderState != rs)
+ {
+ // break the batch
+ m_BatchRenderer.Flush();
+ prevRenderState = rs;
+ shouldResetPass = true;
+ }
+ // We can not use dynamic batching for shadow casting renderers or multipass renderers,
+ // because that will lead to zfighting due to slightly different vertex positions
+ else if (dynamicAndShadowCaster || dynamicShouldNotBatch)
+ {
+ m_BatchRenderer.Flush();
+ shouldResetPass = false;
+ }
+ else
+ shouldResetPass = false;
+
+ renderSettings.SetupAmbient();
+ SetObjectScale(device, roDataC.lodFade, roDataC.invScale);
+
+ node->renderer->ApplyCustomProperties(*roDataH.material, rs.shader, rs.subshaderIndex);
+
+ // non batchable and generally inefficient multi-pass path
+ if (rs.passType == kPassForwardAdd)
+ {
+ const int lightCount = rs.lights->addLightCount;
+ const ActiveLight* const* addLights = rs.lights->GetLights();
+ for( int lightNo = 0; lightNo < lightCount; ++lightNo )
+ {
+ const ActiveLight& activeLight = *addLights[lightNo];
+ Light* light = activeLight.light;
+ LightManager::SetupForwardAddLight (light, lightNo==lightCount-1 ? rs.lights->lastAddLightBlend : 1.0f);
+
+ if (light->GetType() != kLightDirectional)
+ SetLightScissorRect (activeLight.screenRect, m_Context->m_CameraViewport, false, device);
+
+
+ #if ENABLE_SHADOWS
+ if (rs.receiveShadows && light->GetShadows() != kShadowNone)
+ {
+ // find light among additional shadow lights
+ ForwardShadowMaps::iterator sl, slEnd = m_ShadowMaps.end();
+ for (sl = m_ShadowMaps.begin(); sl != slEnd; ++sl)
+ {
+ if (sl->light == &activeLight && sl->texture)
+ {
+ const Light& light = *activeLight.light;
+ SetLightShadowProps (*m_Context->m_Camera, light, sl->texture, sl->shadowMatrix, false);
+ SetShadowsKeywords (light.GetType(), light.GetShadows(), light.GetType()==kLightDirectional, enableSoftShadows);
+ break;
+ }
+ }
+ }
+ #endif
+
+ channels = rs.material->SetPassWithShader(rs.passIndex, rs.shader, rs.subshaderIndex);
+ if (channels)
+ {
+ SetupObjectMatrix (node->worldMatrix, rs.transformType);
+ node->renderer->Render( subsetIndex, *channels );
+ }
+
+ #if ENABLE_SHADOWS
+ if (rs.receiveShadows && light->GetShadows() != kShadowNone)
+ {
+ SetNoShadowsKeywords ();
+ }
+ #endif
+
+ if (light->GetType() != kLightDirectional)
+ ClearScissorRect (oldScissor, oldScissorRect, device);
+ }
+ }
+ else
+ {
+ // only setup lights & pass state when they're differ from previous
+ if (shouldResetPass)
+ {
+ // only setup lights & pass state when they're differ from previous
+ switch( rs.passType )
+ {
+ case kPassAlways:
+ {
+ // Disable all fixed function lights for consistency (so if user
+ // has accidentally Lighting On in an Always pass, it will not produce
+ // random results)
+ device.DisableLights (0);
+
+ // Reset SH lighting
+ float blackSH [9][3];
+ memset (blackSH, 0, (9 * 3)* sizeof(float));
+ SetSHConstants (blackSH, GetGfxDevice().GetBuiltinParamValues());
+
+ SetupObjectLightmaps (lightmapper, rs.lightmapIndex, rs.lightmapST, false);
+ }
+ break;
+
+ case kPassForwardBase:
+ {
+ // NOTE: identity matrix has to be set for GLSL & OpenGLES before vertex lights are set
+ // as lighting is specified in World space
+ device.SetWorldMatrix( Matrix4x4f::identity.GetPtr() );
+
+ LightManager::SetupForwardBaseLights (*rs.lights);
+ SetupObjectLightmaps (lightmapper, rs.lightmapIndex, rs.lightmapST, false);
+
+ #if ENABLE_SHADOWS
+ if (rs.receiveShadows && mainDirShadowLight && rs.lights->mainLight == mainDirShadowLight)
+ {
+ const Light& light = *mainDirShadowLight->light;
+ SetLightShadowProps (*m_Context->m_Camera, light, mainLightShadowMap.texture, mainLightShadowMap.shadowMatrix, false);
+ SetShadowsKeywords (light.GetType(), light.GetShadows(), true, enableSoftShadows);
+ }
+ #endif
+ }
+ break;
+
+ case kPassVertex:
+ case kPassVertexLM:
+ case kPassVertexLMRGBM:
+ {
+ // NOTE: identity matrix has to be set for GLSL & OpenGLES before vertex lights are set
+ // as lighting is specified in World space
+ device.SetWorldMatrix( Matrix4x4f::identity.GetPtr() );
+
+ SetupObjectLightmaps (lightmapper, rs.lightmapIndex, rs.lightmapST, true);
+ LightManager::SetupVertexLights( rs.lights->vertexLightCount, rs.lights->GetLights() );
+ }
+ break;
+
+ default:
+ {
+ AssertString ("This pass type should not happen");
+ break;
+ }
+ }
+
+ channels = roDataH.material->SetPassWithShader(rs.passIndex, rs.shader, rs.subshaderIndex);
+ }
+
+ if (channels)
+ m_BatchRenderer.Add(node->renderer, subsetIndex, channels, node->worldMatrix, rs.transformType);
+
+ if (ENABLE_SHADOWS && rs.passType == kPassForwardBase)
+ SetNoShadowsKeywords ();
+ }
+ }
+
+ m_BatchRenderer.Flush();
+
+ SetNoShadowsKeywords ();
+
+ // restore scissor
+ ClearScissorRect (oldScissor, oldScissorRect, device);
+
+ #if ENABLE_SHADOWS
+ if (mainLightShadowMap.texture && mainLightShadowMap.texture != existingShadowMap)
+ GetRenderBufferManager().ReleaseTempBuffer( mainLightShadowMap.texture );
+ for (ForwardShadowMaps::iterator it = m_ShadowMaps.begin(), itEnd = m_ShadowMaps.end(); it != itEnd; ++it)
+ {
+ ForwardShadowMap& sl = *it;
+ if (sl.texture)
+ GetRenderBufferManager().ReleaseTempBuffer (sl.texture);
+ }
+ #endif
+
+ // After everything we might still have renderables that should be drawn and the
+ // very end. Do it.
+ if (!m_Context->m_DontRenderRenderables)
+ {
+ while (renderablesBegin != renderablesEnd && renderablesBegin->first < m_Context->m_RenderQueueStart)
+ ++renderablesBegin;
+ while( renderablesBegin != renderablesEnd && renderablesBegin->first < m_Context->m_RenderQueueEnd )
+ {
+ renderablesBegin->second->RenderRenderable(*m_Context->m_CullResults);
+ ++renderablesBegin;
+ }
+ }
+ GetGfxDevice().SetSRGBWrite(false);
+ device.SetViewMatrix( m_Context->m_CurCameraMatrix.GetPtr() );
+}
+
+
+// ------------------------------------------------------------------------
+// collect cascaded shadows into screen-space texture; apply blur
+
+#if ENABLE_SHADOWS
+
+struct ShadowCollectorSorter
+{
+ bool operator() (int raIndex, int rbIndex) const;
+ const ForwardShaderRenderLoop* queue;
+};
+
+bool ShadowCollectorSorter::operator()(int raIndex, int rbIndex) const
+{
+ const RenderObjectData& ra = (*queue->m_Objects)[raIndex];
+ const RenderObjectData& rb = (*queue->m_Objects)[rbIndex];
+
+ // Sort by layering depth. //@TODO:should this be here?
+ bool globalLayeringResult;
+ if (CompareGlobalLayeringData(ra.globalLayeringData, rb.globalLayeringData, globalLayeringResult))
+ return globalLayeringResult;
+
+ // Sort front to back
+ return ra.distance > rb.distance;
+}
+
+struct CompactShadowCollectorSortData
+{
+ UInt64 key; // 64b key, stores full 32b material instance ID, 16b internal static batch ID, 2b for transform type, and 14b depth
+ int collectorIndex;
+
+ CompactShadowCollectorSortData(UInt32 _smallMeshIndex, UInt32 _instanceID, TransformType _transformType, float _depth, int _collectorIndex )
+ {
+ key=0;
+ UInt32 transformType = static_cast<UInt32>(_transformType);
+ UInt32 z = (UInt32)(16383.0f*_depth);
+
+ key |= (_instanceID);
+ key = key << 32;
+ key |= ((_smallMeshIndex&0x0000ffff)<<16)|((transformType&0x00000003)<<14)|(z&0x00003fff);
+
+ collectorIndex = _collectorIndex;
+ }
+};
+
+struct CompactShadowCollectorKeySorter
+{
+ inline bool operator()(const CompactShadowCollectorSortData& a, const CompactShadowCollectorSortData& b)
+ {
+ return a.key < b.key;
+ }
+};
+
+// Shadow collector sorting
+// Sorted shadow collector order is stored into m_ReceiverObjects
+// Output:
+// _resultOrder - Sorted shadow caster sort data
+// Returns:
+// Number of active collectors
+int ForwardShaderRenderLoop::SortShadowCollectorsCompact(CompactShadowCollectorSortData* _resultOrder)
+{
+ int activeShadowCollectors = 0;
+
+ // Generate key array for sorting
+ for( int i = 0; i < m_ReceiverObjects.size(); ++i )
+ {
+ int roIndex = m_ReceiverObjects[i];
+ const RenderObjectData& roDataH = (*m_Objects)[roIndex];
+ Shader* shader = roDataH.shader;
+
+ if( shader->HasShadowCollectorPass() )
+ {
+ const TransformInfo& xformInfo = roDataH.visibleNode->renderer->GetTransformInfo();
+
+ Matrix4x4f worldToClipMatrix = m_Context->m_Camera->GetWorldToClipMatrix();
+ const Vector3f& worldPos = roDataH.visibleNode->worldAABB.GetCenter();
+ float z = worldToClipMatrix.Get (2, 0) * worldPos.x + worldToClipMatrix.Get (2, 1) * worldPos.y + worldToClipMatrix.Get (2, 2) * worldPos.z + worldToClipMatrix.Get (2, 3);
+ float w = worldToClipMatrix.Get (3, 0) * worldPos.x + worldToClipMatrix.Get (3, 1) * worldPos.y + worldToClipMatrix.Get (3, 2) * worldPos.z + worldToClipMatrix.Get (3, 3);
+ float z_proj = z/w;
+ z_proj = max(z_proj,0.0f);
+ z_proj = min(z_proj,1.0f);
+
+ _resultOrder[activeShadowCollectors++] = CompactShadowCollectorSortData( roDataH.visibleNode->renderer->GetMeshIDSmall(), roDataH.material->GetShadowCollectorHash(),
+ xformInfo.transformType, z_proj, roIndex );
+
+ }
+ }
+
+ std::sort( _resultOrder, _resultOrder + activeShadowCollectors, CompactShadowCollectorKeySorter() );
+
+ return activeShadowCollectors;
+}
+
+RenderTexture* ForwardShaderRenderLoop::CollectShadows (RenderTexture* inputShadowMap, const Light* light, const Matrix4x4f* shadowMatrices, const float* splitDistances, const Vector4f* splitSphereCentersAndSquaredRadii, bool enableSoftShadows, bool useDualInForward, bool clearFrameBuffer)
+{
+ PROFILER_AUTO_GFX(gFwdOpaqueCollectShadows, m_Context->m_Camera)
+ GPU_AUTO_SECTION(kGPUSectionShadowPass);
+
+ DebugAssert (shadowMatrices && inputShadowMap && light && splitDistances);
+
+ //Sort shadow collectors
+#if GFX_ENABLE_SHADOW_BATCHING
+ CompactShadowCollectorSortData* sortOrder;
+ ALLOC_TEMP(sortOrder, CompactShadowCollectorSortData, m_ReceiverObjects.size());
+ int shadowColectors = SortShadowCollectorsCompact(sortOrder);
+#else
+ ShadowCollectorSorter sorter;
+ sorter.queue = this;
+ std::sort (m_ReceiverObjects.begin(), m_ReceiverObjects.end(), sorter);
+#endif
+
+ // If camera is rendering into a texture, we can share its depth buffer while collecting shadows.
+ // This doesn't apply if the target texture is antialiased (case 559079).
+ bool shareDepthBuffer = false;
+ RenderTexture* cameraRT = m_Context->m_Camera->GetCurrentTargetTexture();
+ if (cameraRT && cameraRT->GetDepthFormat() != kDepthFormatNone && !cameraRT->IsAntiAliased())
+ {
+ shareDepthBuffer = true;
+ if (!cameraRT->IsCreated())
+ cameraRT->Create();
+ }
+
+ // create screen-space render texture and collect shadows into it
+ RenderTexture* screenShadowMap = GetRenderBufferManager().GetTempBuffer (RenderBufferManager::kFullSize, RenderBufferManager::kFullSize, shareDepthBuffer ? kDepthFormatNone : kDepthFormat24, kRTFormatARGB32, 0, kRTReadWriteLinear);
+ if (shareDepthBuffer)
+ {
+ if (!screenShadowMap->IsCreated())
+ screenShadowMap->Create();
+ RenderSurfaceHandle rtSurfaceColor = screenShadowMap->GetColorSurfaceHandle();
+ RenderSurfaceHandle rtSurfaceDepth = cameraRT->GetDepthSurfaceHandle();
+ RenderTexture::SetActive (1, &rtSurfaceColor, rtSurfaceDepth, screenShadowMap);
+ }
+ else
+ {
+ RenderTexture::SetActive (screenShadowMap);
+ }
+
+ GfxDevice& device = GetGfxDevice();
+ // (case 555375)
+ // Clear all, expect in cases where depth buffer is shared and forward path is used to render deferred path shadow receiving objects
+ // (clearFrameBuffer variable is false in those cases)
+ bool clearColorOnly = shareDepthBuffer && !clearFrameBuffer;
+ device.Clear (clearColorOnly ? kGfxClearColor : kGfxClearAll, ColorRGBAf(1,1,1,0).GetPtr(), 1.0f, 0);
+ if (clearColorOnly)
+ device.IgnoreNextUnresolveOnCurrentRenderTarget();
+ GPU_TIMESTAMP();
+ m_Context->m_Camera->SetupRender ();
+
+ SetLightShadowCollectProps (*m_Context->m_Camera, *light, inputShadowMap, shadowMatrices, splitDistances, splitSphereCentersAndSquaredRadii, useDualInForward);
+ light->SetPropsToShaderLab (1.0f);
+
+ device.SetViewMatrix( m_Context->m_CurCameraMatrix.GetPtr() );
+
+#if GFX_ENABLE_SHADOW_BATCHING
+
+ device.SetInverseScale(1.0f);
+ m_BatchRenderer.Flush();
+
+ if (shadowColectors > 0)
+ {
+ UInt64 previousKey = ((sortOrder[0].key)&0xFFFFFFFFFFFFC000ULL); // depth component does not affect state change boundaries
+ UInt32 previousHash = 0;
+ int roIndex = sortOrder[0].collectorIndex;
+ const ChannelAssigns* channels = (*m_Objects)[roIndex].material->SetShadowCollectorPassWithShader ((*m_Objects)[roIndex].shader, m_RenderObjectsCold[roIndex].subshaderIndex);
+
+ for(int i=0; i<shadowColectors;i++)
+ {
+ UInt64 currentKey = ((sortOrder[i].key)&0xFFFFFFFFFFFFC000ULL);
+
+ roIndex = sortOrder[i].collectorIndex;
+ const RenderObjectData& roDataH = (*m_Objects)[roIndex];
+ Shader* shader = roDataH.shader;
+ const TransformInfo& xformInfo = roDataH.visibleNode->renderer->GetTransformInfo ();
+ const RenderObjectDataCold& roDataC = m_RenderObjectsCold[roIndex];
+
+ roDataH.visibleNode->renderer->ApplyCustomProperties(*roDataH.material, shader, roDataC.subshaderIndex);
+
+ UInt32 currentHash = roDataH.visibleNode->renderer->GetCustomPropertiesHash();
+
+ // different property hasah or shared depth buffer cause Flush(), state setup, and one non-batched draw call
+ if (currentHash != previousHash || shareDepthBuffer)
+ {
+ m_BatchRenderer.Flush(); // empty BatchRenderer
+ channels = roDataH.material->SetShadowCollectorPassWithShader(shader, roDataC.subshaderIndex);
+ SetupObjectMatrix(xformInfo.worldMatrix, xformInfo.transformType);
+ roDataH.visibleNode->renderer->Render( roDataH.subsetIndex, *channels );
+ }
+ else
+ {
+ if (previousKey != currentKey) // Flush() and update state when key changes
+ {
+ m_BatchRenderer.Flush();
+ channels = roDataH.material->SetShadowCollectorPassWithShader (shader, roDataC.subshaderIndex);
+ }
+
+ // if this pass needs to be rendered
+ if (channels)
+ m_BatchRenderer.Add(roDataH.visibleNode->renderer, roDataH.subsetIndex, channels, xformInfo.worldMatrix, xformInfo.transformType);
+ }
+ previousKey = currentKey;
+ previousHash = currentHash;
+ }
+ m_BatchRenderer.Flush();
+ }
+
+#else // GFX_ENABLE_SHADOW_BATCHING
+
+ size_t npasses = m_ReceiverObjects.size();
+ for( size_t i = 0; i < npasses; ++i )
+ {
+ int roIndex = m_ReceiverObjects[i];
+ const RenderObjectData& roDataH = (*m_Objects)[roIndex];
+ const RenderObjectDataCold& roDataC = m_RenderObjectsCold[roIndex];
+
+ Shader* shader = roDataH.shader;
+ if( !shader->HasShadowCollectorPass() )
+ continue;
+
+ const VisibleNode* node = roDataH.visibleNode;
+ BaseRenderer* renderer = node->renderer;
+ SetObjectScale(device, roDataC.lodFade, roDataC.invScale);
+
+ renderer->ApplyCustomProperties(*roDataH.material, shader, roDataC.subshaderIndex);
+
+ const ChannelAssigns* channels = roDataH.material->SetShadowCollectorPassWithShader(shader, roDataC.subshaderIndex);
+ SetupObjectMatrix (node->worldMatrix, node->transformType);
+ renderer->Render( roDataH.subsetIndex, *channels );
+ }
+
+#endif // GFX_ENABLE_SHADOW_BATCHING
+
+ GetRenderBufferManager().ReleaseTempBuffer( inputShadowMap );
+
+ //
+ // possibly blur into another screen-space render texture
+
+ if( IsSoftShadow(light->GetShadows()) && enableSoftShadows )
+ {
+ return BlurScreenShadowMap (screenShadowMap, light->GetShadows(), m_Context->m_Camera->GetFar(), light->GetShadowSoftness(), light->GetShadowSoftnessFade());
+ }
+
+ return screenShadowMap;
+}
+#endif // ENABLE_SHADOWS
+
+// ------------------------------------------------------------------------
+// render shadow maps for a single light
+
+
+#if ENABLE_SHADOWS
+
+void ForwardShaderRenderLoop::RenderLightShadowMaps (ForwardShadowMap& shadowMap, ShadowCameraData& camData, bool enableSoftShadows, bool useDualInForward, bool clearFrameBuffer)
+{
+ // Set correct keywords before rendering casters (caster passes use keywords for shader selection)
+ const Light* light = shadowMap.light->light;
+ SetShadowsKeywords( light->GetType(), light->GetShadows(), false, enableSoftShadows );
+ GetGfxDevice().SetViewMatrix( m_Context->m_CurCameraMatrix.GetPtr() );
+
+ Matrix4x4f shadowMatrices[kMaxShadowCascades];
+
+#if UNITY_EDITOR
+ bool useLightmaps = GetLightmapVisualization ().GetUseLightmapsForRendering ();
+#else
+ bool useLightmaps = true;
+#endif
+
+ bool excludeLightmapped = !useDualInForward && useLightmaps;
+ shadowMap.texture = RenderShadowMaps (camData, *shadowMap.light, shadowMap.receiverBounds, excludeLightmapped, shadowMatrices);
+ CopyMatrix (shadowMatrices[0].GetPtr(), shadowMap.shadowMatrix.GetPtr());
+
+ // Shadow map can be null if out of memory; or no shadow casters present
+ if (gGraphicsCaps.hasShadowCollectorPass && shadowMap.texture && light->GetType() == kLightDirectional)
+ {
+ SetShadowsKeywords( light->GetType(), light->GetShadows(), false, enableSoftShadows );
+ shadowMap.texture = CollectShadows (shadowMap.texture, light, shadowMatrices, camData.splitDistances, camData.splitSphereCentersAndSquaredRadii, enableSoftShadows, useDualInForward, clearFrameBuffer);
+ }
+ else
+ {
+ // If shadow map could not actually be created (out of VRAM, whatever), set the no shadows
+ // keywords and proceed. So there will be no shadows, but otherwise it will be ok.
+ SetNoShadowsKeywords();
+ }
+}
+
+#endif // ENABLE_SHADOWS
+
+
+// ------------------------------------------------------------------------
+// rendering entry points
+
+ForwardShaderRenderLoop* CreateForwardShaderRenderLoop()
+{
+ return new ForwardShaderRenderLoop();
+}
+
+void DeleteForwardShaderRenderLoop (ForwardShaderRenderLoop* queue)
+{
+ delete queue;
+}
+
+static bool IsPassSuitable (UInt32 currentRenderOptions, UInt32 passRenderOptions, ShaderPassType passType,
+ bool isLightmapped, bool useRGBM, bool useVertexLights, bool hasAddLights)
+{
+ // All options that a pass requires must be on
+ if( (currentRenderOptions & passRenderOptions) != passRenderOptions )
+ return false; // some options are off, skip this pass
+
+ if (useVertexLights)
+ {
+ if (passType != kPassAlways && passType != kPassVertex &&
+ passType != kPassVertexLM && passType != kPassVertexLMRGBM)
+ return false;
+
+ // Use either lightmapped or non-lightmapped pass
+ if ((passType == kPassVertex && isLightmapped) ||
+ ((passType == kPassVertexLM || passType == kPassVertexLMRGBM) && !isLightmapped))
+ return false;
+
+ // Use pass that can properly decode the lightmap
+ if ((passType == kPassVertexLM && useRGBM) ||
+ (passType == kPassVertexLMRGBM && !useRGBM))
+ return false;
+ }
+ else
+ {
+ if (passType != kPassAlways && passType != kPassForwardBase && passType != kPassForwardAdd)
+ return false; // pass does not belong to forward loop
+
+ if (!hasAddLights && passType == kPassForwardAdd)
+ return false; // additive pass but have no additive lights
+ }
+ return true;
+}
+
+// A point or spot light might be completely behind shadow distance,
+// so there's no point in doing shadows on them.
+static bool IsLightBeyondShadowDistance (const Light& light, const Matrix4x4f& cameraMatrix, float shadowDistance)
+{
+ if (light.GetType() == kLightDirectional)
+ return false;
+ const Vector3f lightPos = light.GetComponent(Transform).GetPosition();
+ float distanceToLight = -cameraMatrix.MultiplyPoint3 (lightPos).z;
+ if (distanceToLight - light.GetRange() > shadowDistance)
+ return true;
+ return false;
+}
+
+
+static void PutAdditionalShadowLights (const AABB& bounds, ForwardLightsBlock& lights, const Matrix4x4f& cameraMatrix, float shadowDistance, ForwardShadowMaps& outShadowMaps)
+{
+ const int lightCount = lights.addLightCount;
+ const ActiveLight* const* addLights = lights.GetLights();
+ for (int lightNo = 0; lightNo < lightCount; ++lightNo)
+ {
+ const ActiveLight* light = addLights[lightNo];
+ if (light->light->GetShadows() == kShadowNone)
+ continue;
+
+ // Find this light's shadow data
+ ForwardShadowMaps::iterator sl, slEnd = outShadowMaps.end();
+ ForwardShadowMap* found = NULL;
+ for (sl = outShadowMaps.begin(); sl != slEnd; ++sl)
+ {
+ if (sl->light == light)
+ {
+ found = &(*sl);
+ break;
+ }
+ }
+ if (sl == slEnd)
+ {
+ // Point/Spot light beyond shadow distance: no need to add
+ if (IsLightBeyondShadowDistance (*light->light, cameraMatrix, shadowDistance))
+ continue;
+
+ ForwardShadowMap& shadowMap = outShadowMaps.push_back ();
+ shadowMap.light = light;
+ shadowMap.receiverBounds = bounds;
+ shadowMap.texture = NULL;
+ }
+ else
+ {
+ found->receiverBounds.Encapsulate (bounds);
+ }
+ }
+}
+
+#if ENABLE_FORWARD_SHADER_LOOP_HASH_SORTING
+template<typename T>
+static UInt8* InsertIntoHashBuffer(const T* p, UInt8* buffer)
+{
+ Assert((sizeof(T) % 4) == 0); // unaligned write
+ *reinterpret_cast<T*>(buffer) = *p;
+ return buffer + sizeof(T);
+}
+#endif
+
+void DoForwardShaderRenderLoop (
+ RenderLoopContext& ctx,
+ RenderObjectDataContainer& objects,
+ bool opaque,
+ bool disableDynamicBatching,
+ RenderTexture* mainShadowMap,
+ ActiveLights& activeLights,
+ bool linearLighting,
+ bool clearFrameBuffer)
+{
+ GPU_AUTO_SECTION(opaque ? kGPUSectionOpaquePass : kGPUSectionTransparentPass);
+ using namespace ForwardShaderRenderLoop_Enum;
+
+ const QualitySettings::QualitySetting& quality = GetQualitySettings().GetCurrent();
+
+ // figure out hardware supports shadows
+ #if ENABLE_SHADOWS
+ float shadowDistance = QualitySettings::GetShadowDistanceForRendering();
+ const bool receiveShadows =
+ opaque &&
+ GetBuildSettings().hasShadows &&
+ CheckPlatformSupportsShadows() &&
+ (quality.shadows != QualitySettings::kShadowsDisable) &&
+ (shadowDistance > 0.0f);
+ const bool localLightShadows = receiveShadows && GetBuildSettings().hasLocalLightShadows;
+ #endif
+
+ bool useRGBM = gGraphicsCaps.SupportsRGBM();
+
+ // Allocated on the stack each time, uses temp allocators
+ ForwardShaderRenderLoop queue;
+ queue.m_Context = &ctx;
+ queue.m_Objects = &objects;
+ queue.m_RenderObjectsCold.reserve(objects.size());
+ const int kEstimatedLightDataPerObject = sizeof(ForwardLightsBlock) + kEstimatedLightsPerObject * sizeof(Light*);
+ queue.m_RenderObjectsLightData.reserve(objects.size() * kEstimatedLightDataPerObject);
+
+ const ActiveLight* mainDirShadowLight = NULL;
+
+ // figure out current rendering options
+ UInt32 currentRenderOptions = GetCurrentRenderOptions ();
+
+ RenderSettings& renderSettings = GetRenderSettings();
+ LightManager& lightManager = GetLightManager();
+ const int pixelLightCount = quality.pixelLightCount;
+ const bool dualLightmapsMode = (GetLightmapSettings().GetLightmapsMode() == LightmapSettings::kDualLightmapsMode);
+
+#if UNITY_EDITOR
+ bool useLightmaps = GetLightmapVisualization ().GetUseLightmapsForRendering ();
+#endif
+
+ const CullResults& cullResults = *ctx.m_CullResults;
+
+ // Figure everything out
+ {
+ PROFILER_AUTO((opaque?gFwdOpaquePrepare:gFwdAlphaPrepare), ctx.m_Camera);
+
+ RenderObjectDataContainer::iterator itEnd = objects.end();
+ size_t roIndex = 0;
+ for (RenderObjectDataContainer::iterator it = objects.begin(); it != itEnd; ++it, ++roIndex)
+ {
+ RenderObjectData& odata = *it;
+ const VisibleNode *node = odata.visibleNode;
+ size_t visibleNodeIndex = node - cullResults.nodes.begin();
+
+ BaseRenderer* renderer = node->renderer;
+
+#if UNITY_EDITOR
+ const bool isLightmapped = renderer->IsLightmappedForRendering() && useLightmaps;
+#else
+ const bool isLightmapped = renderer->IsLightmappedForRendering();
+#endif
+
+ ShaderLab::IntShader& slshader = *odata.shader->GetShaderLabShader();
+ RenderObjectDataCold& roDataC = queue.m_RenderObjectsCold.push_back();
+ bool useVertexLights = false;
+ if (odata.subShaderIndex == -1)
+ {
+ int ss = slshader.GetDefaultSubshaderIndex (kRenderPathExtForward);
+ if (ss == -1)
+ {
+ ss = slshader.GetDefaultSubshaderIndex (isLightmapped ? kRenderPathExtVertexLM : kRenderPathExtVertex);
+ useVertexLights = true;
+ }
+ if (ss == -1)
+ continue;
+ roDataC.subshaderIndex = ss;
+ }
+ else
+ {
+ roDataC.subshaderIndex = odata.subShaderIndex;
+ }
+ ShaderLab::SubShader& subshader = slshader.GetSubShader(roDataC.subshaderIndex);
+
+ bool disableAddLights = false;
+ if (!useVertexLights)
+ {
+ // If we only have ForwardBase pass and no ForwardAdd,
+ // disable additive lights completely. Only support main directional,
+ // vertex & SH.
+ disableAddLights = !subshader.GetSupportsForwardAddLights();
+ }
+
+ size_t objectLightsOffset = queue.m_RenderObjectsLightData.size();
+ roDataC.lightsDataOffset = objectLightsOffset;
+
+ lightManager.FindForwardLightsForObject (
+ queue.m_RenderObjectsLightData,
+ GetObjectLightIndices(cullResults, visibleNodeIndex),
+ GetObjectLightCount(cullResults, visibleNodeIndex),
+ activeLights,
+ *node,
+ isLightmapped,
+ dualLightmapsMode,
+ useVertexLights,
+ pixelLightCount,
+ disableAddLights,
+ renderSettings.GetAmbientLightInActiveColorSpace());
+
+ ForwardLightsBlock& roDataL = *reinterpret_cast<ForwardLightsBlock*>(&queue.m_RenderObjectsLightData[objectLightsOffset]);
+ const bool hasAddLights = (roDataL.addLightCount != 0);
+
+ #if ENABLE_SHADOWS
+ bool objectReceivesShadows = renderer->GetReceiveShadows();
+ bool withinShadowDistance = IsObjectWithinShadowRange (*ctx.m_ShadowCullData, node->worldAABB);
+ if (receiveShadows && withinShadowDistance)
+ {
+ queue.m_ReceiverObjects.push_back (roIndex);
+
+ if (objectReceivesShadows)
+ {
+ // deal with main directional shadow light
+ if (roDataL.mainLight && roDataL.mainLight->light->GetShadows() != kShadowNone)
+ {
+ if (!mainDirShadowLight)
+ mainDirShadowLight = roDataL.mainLight;
+ if (mainDirShadowLight == roDataL.mainLight)
+ queue.m_MainShadowMap.receiverBounds.Encapsulate (node->worldAABB);
+ }
+
+ // deal with additive shadow lights if needed
+ if (localLightShadows && subshader.GetSupportsFullForwardShadows())
+ {
+ PutAdditionalShadowLights (node->worldAABB, roDataL, ctx.m_CurCameraMatrix, shadowDistance, queue.m_ShadowMaps);
+ }
+ }
+ }
+ #endif
+
+ roDataC.invScale = node->invScale;
+ roDataC.lodFade = node->lodFade;
+
+ int shaderPassCount = subshader.GetValidPassCount();
+
+ // Determine if we will need more than a single pass
+ int suitablePasses = 0;
+ for( int pass = 0; pass < shaderPassCount && suitablePasses < 2; ++pass )
+ {
+ ShaderPassType passType; UInt32 passRenderOptions;
+ subshader.GetPass(pass)->GetPassOptions( passType, passRenderOptions );
+
+ if (IsPassSuitable (currentRenderOptions, passRenderOptions, passType, isLightmapped, useRGBM, useVertexLights, hasAddLights))
+ ++suitablePasses;
+ }
+
+ // Go over all passes in the shader
+ UInt32 firstPassFlag = kPackFirstPassFlag;
+ const UInt32 multiPassFlag = (suitablePasses > 1)? kPackMultiPassFlag: 0;
+ for( int pass = 0; pass < shaderPassCount; ++pass )
+ {
+ ShaderPassType passType; UInt32 passRenderOptions;
+ subshader.GetPass(pass)->GetPassOptions( passType, passRenderOptions );
+
+ if (!IsPassSuitable (currentRenderOptions, passRenderOptions, passType, isLightmapped, useRGBM, useVertexLights, hasAddLights))
+ continue; // skip this pass
+
+ RenderPassData rpData;
+ rpData.roIndex = roIndex;
+ rpData.data =
+ ((pass & kPackPassMask) << kPackPassShift) |
+ (passType << kPackTypeShift) |
+ firstPassFlag |
+ multiPassFlag;
+
+#if ENABLE_FORWARD_SHADER_LOOP_HASH_SORTING
+
+ //hash state information for render object sorter
+ const int kHashBufferSize = 64;
+ UInt8 hashBuffer[kHashBufferSize];
+ UInt8* hashPtr = hashBuffer;
+
+ // Always write 32b granularity into the hash buffer to avoid unaligned writes
+ if (opaque)
+ hashPtr = InsertIntoHashBuffer(&node->invScale, hashPtr);
+ int materialID = odata.material->GetInstanceID();
+ hashPtr = InsertIntoHashBuffer(&materialID, hashPtr);
+ hashPtr = InsertIntoHashBuffer(&roDataC.subshaderIndex, hashPtr);
+ UInt32 shaderPassType = (ShaderPassType)((rpData.data >> kPackTypeShift) & kPackTypeMask);
+ hashPtr = InsertIntoHashBuffer(&shaderPassType, hashPtr);
+ UInt32 passIndex = (rpData.data >> kPackPassShift) & kPackPassMask;
+ hashPtr = InsertIntoHashBuffer(&passIndex, hashPtr);
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+ hashPtr = InsertIntoHashBuffer(&odata.staticBatchIndex, hashPtr);
+#endif
+ Assert(hashPtr-hashBuffer <= kHashBufferSize);
+
+ rpData.hash = MurmurHash2A(hashBuffer, hashPtr-hashBuffer, 0x9747b28c);
+#endif
+ queue.m_PlainRenderPasses.push_back( rpData );
+
+ firstPassFlag = 0;
+ }
+ }
+ }
+
+ // sort everything
+ {
+ PROFILER_AUTO((opaque?gFwdOpaqueSort:gFwdAlphaSort), ctx.m_Camera);
+ if (opaque)
+ queue.SortRenderPassData<true> (queue.m_PlainRenderPasses);
+ else
+ queue.SortRenderPassData<false> (queue.m_PlainRenderPasses);
+ }
+
+ // Render everything. When transitioning to render queues,
+ // it will invoke camera renderables (halos, and so on)
+ {
+ PROFILER_AUTO_GFX((opaque?gFwdOpaqueRender:gFwdAlphaRender), ctx.m_Camera);
+ RenderTexture* rtMain = ctx.m_Camera->GetCurrentTargetTexture ();
+ queue.PerformRendering (mainDirShadowLight, mainShadowMap, *ctx.m_ShadowCullData, disableDynamicBatching, linearLighting && (!rtMain || rtMain->GetSRGBReadWrite()), clearFrameBuffer);
+ }
+}
diff --git a/Runtime/Camera/RenderLoops/ForwardVertexRenderLoop.cpp b/Runtime/Camera/RenderLoops/ForwardVertexRenderLoop.cpp
new file mode 100644
index 0000000..60889ad
--- /dev/null
+++ b/Runtime/Camera/RenderLoops/ForwardVertexRenderLoop.cpp
@@ -0,0 +1,637 @@
+#include "UnityPrefix.h"
+#include "RenderLoopPrivate.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Camera/Renderqueue.h"
+#include "Runtime/Graphics/Transform.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "Runtime/Camera/Renderable.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Camera/RenderSettings.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "RenderLoop.h"
+#include "Runtime/GfxDevice/GfxDeviceConfigure.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Graphics/LightmapSettings.h"
+#include "Runtime/GfxDevice/BatchRendering.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Camera/LightManager.h"
+#if UNITY_EDITOR
+#include "Editor/Src/LightmapVisualization.h"
+#endif
+#include "BuiltinShaderParamUtility.h"
+#include "External/MurmurHash/MurmurHash2.h"
+
+// Enable/disable hash based forward shader render loop sorting functionality.
+#define ENABLE_VERTEX_LOOP_HASH_SORTING 0
+
+static inline bool CompareLights (VertexLightsBlock const* a, VertexLightsBlock const* b)
+{
+ if (!a || !b)
+ return false;
+
+ if (a->lightCount != b->lightCount)
+ return false;
+
+ const ActiveLight* const* lightsA = a->GetLights();
+ const ActiveLight* const* lightsB = b->GetLights();
+ for (int i = 0; i < a->lightCount; ++i)
+ if (lightsA[i] != lightsB[i])
+ return false;
+
+ return true;
+}
+
+
+struct RODataVLit {
+ // help the compiler here a bit...
+ RODataVLit() { }
+ RODataVLit( const RODataVLit& rhs ) { memcpy(this, &rhs, sizeof(*this)); }
+
+ float invScale; // 4
+ float lodFade; // 4
+ size_t lightsDataOffset; // 4 into memory block with all light data chunks
+ int subshaderIndex; // 4
+
+ // 16 bytes
+};
+
+namespace ForwardVertexRenderLoop_Enum
+{
+// Render pass data here is 8 bytes each; an index of the render object and "the rest" packed
+// into 4 bytes.
+enum {
+ kPackPassShift = 0,
+ kPackPassMask = 0xFF,
+ kPackFirstPassFlag = (1<<16),
+ kPackMultiPassFlag = (1<<17),
+};
+} // namespace ForwardVertexRenderLoop_Enum
+
+struct RPDataVLit {
+ int roIndex;
+ // Packed into UInt32: pass number, first pass flag
+ UInt32 data;
+#if ENABLE_VERTEX_LOOP_HASH_SORTING
+ UInt32 hash;
+#endif
+};
+typedef dynamic_array<RPDataVLit> RenderPassesVLit;
+
+
+struct ForwardVertexRenderState
+{
+ int rendererType;
+ int transformType;
+ float invScale;
+ float lodFade;
+
+ Material* material;
+ Shader* shader;
+ int subshaderIndex;
+ int passIndex;
+
+ const VertexLightsBlock* lights;
+
+ int lightmapIndex;
+ Vector4f lightmapST;
+
+ UInt32 customPropsHash;
+
+ void Invalidate()
+ {
+ rendererType = -1;
+ transformType = -1;
+ invScale = 0.0f;
+ lodFade = 0.0f;
+ material = 0; shader = 0; subshaderIndex = -1; passIndex = -1;
+ lights = 0;
+ lightmapIndex = -1; lightmapST = Vector4f(0,0,0,0);
+ customPropsHash = 0;
+ }
+
+ bool operator == (const ForwardVertexRenderState& rhs) const
+ {
+ if (this == &rhs)
+ return true;
+
+ return (
+ rendererType == rhs.rendererType &&
+ transformType == rhs.transformType &&
+ material == rhs.material &&
+ shader == rhs.shader &&
+ CompareLights(lights, rhs.lights) &&
+ subshaderIndex == rhs.subshaderIndex &&
+ passIndex == rhs.passIndex &&
+ CompareApproximately(invScale,rhs.invScale) &&
+ CompareApproximately(lodFade,rhs.lodFade, LOD_FADE_BATCH_EPSILON) &&
+ lightmapIndex == rhs.lightmapIndex &&
+ CompareMemory(lightmapST, rhs.lightmapST) &&
+ customPropsHash == rhs.customPropsHash);
+ }
+
+ bool operator != (const ForwardVertexRenderState& rhs) const
+ {
+ return !(rhs == *this);
+ }
+};
+
+
+struct ForwardVertexRenderLoop
+{
+ ForwardVertexRenderLoop()
+ : m_RenderObjectsCold (kMemTempAlloc)
+ , m_RenderObjectsLightData (kMemTempAlloc)
+ , m_PlainRenderPasses (kMemTempAlloc)
+ { }
+
+ const RenderLoopContext* m_Context;
+ RenderObjectDataContainer* m_Objects;
+ dynamic_array<RODataVLit> m_RenderObjectsCold;
+ dynamic_array<UInt8> m_RenderObjectsLightData;
+ RenderPassesVLit m_PlainRenderPasses;
+ BatchRenderer m_BatchRenderer;
+
+ void PerformRendering (bool sSRGBRenderTarget, bool clearFrameBuffer);
+
+ template<bool opaque>
+ struct RenderObjectSorter
+ {
+ bool operator()( const RPDataVLit& ra, const RPDataVLit& rb ) const;
+ const ForwardVertexRenderLoop* queue;
+ };
+ template<bool opaque>
+ void SortRenderPassData( RenderPassesVLit& passes )
+ {
+ RenderObjectSorter<opaque> sorter;
+ sorter.queue = this;
+ std::sort( passes.begin(), passes.end(), sorter );
+ }
+};
+
+
+template<bool opaque>
+bool ForwardVertexRenderLoop::RenderObjectSorter<opaque>::operator() (const RPDataVLit& ra, const RPDataVLit& rb) const
+{
+ using namespace ForwardVertexRenderLoop_Enum;
+
+ const RenderObjectData& dataa = (*queue->m_Objects)[ra.roIndex];
+ const RenderObjectData& datab = (*queue->m_Objects)[rb.roIndex];
+
+ // Sort by layering depth.
+ bool globalLayeringResult;
+ if (CompareGlobalLayeringData(dataa.globalLayeringData, datab.globalLayeringData, globalLayeringResult))
+ return globalLayeringResult;
+
+#if ENABLE_VERTEX_LOOP_HASH_SORTING
+
+ // Sort by render queues first
+ if( dataa.queueIndex != datab.queueIndex )
+ return dataa.queueIndex < datab.queueIndex;
+
+ if (opaque) {
+ DebugAssertIf (dataa.queueIndex < kQueueIndexMin || dataa.queueIndex > kGeometryQueueIndexMax); // this is opaque loop!
+ } else {
+ DebugAssertIf (dataa.queueIndex >= kQueueIndexMin && dataa.queueIndex <= kGeometryQueueIndexMax); // this is alpha loop!
+ }
+
+ if (!opaque)
+ {
+ if( dataa.distance != datab.distance )
+ return dataa.distance < datab.distance;
+ }
+
+ UInt32 flagsa = ra.data;
+ UInt32 flagsb = rb.data;
+
+ // render all first passes first
+ if( (flagsa & kPackFirstPassFlag) != (flagsb & kPackFirstPassFlag) )
+ return (flagsa & kPackFirstPassFlag) > (flagsb & kPackFirstPassFlag);
+
+ if (ra.hash != rb.hash)
+ return ra.hash < rb.hash;
+
+ // then sort by material
+ if( dataa.material != datab.material )
+ return dataa.material->GetInstanceID() < datab.material->GetInstanceID(); // just compare instance IDs
+
+ // inside same material: by pass
+ UInt32 passa = (flagsa >> kPackPassShift) & kPackPassMask;
+ UInt32 passb = (flagsb >> kPackPassShift) & kPackPassMask;
+ if( passa != passb )
+ return passa < passb;
+
+ // Sort by distance in reverse order.
+ // That way we get consistency in render order, and more pixels not rendered due to z-testing,
+ // which benefits performance.
+ if (opaque)
+ {
+ if( dataa.distance != datab.distance )
+ return dataa.distance > datab.distance;
+ }
+
+ // fall through: roIndex
+ return ra.roIndex < rb.roIndex;
+
+#else
+
+ // Sort by render queues first
+ if( dataa.queueIndex != datab.queueIndex )
+ return dataa.queueIndex < datab.queueIndex;
+
+ if (opaque) {
+ DebugAssertIf (dataa.queueIndex < kQueueIndexMin || dataa.queueIndex > kGeometryQueueIndexMax); // this is opaque loop!
+ } else {
+ DebugAssertIf (dataa.queueIndex >= kQueueIndexMin && dataa.queueIndex <= kGeometryQueueIndexMax); // this is alpha loop!
+ }
+
+ if (!opaque)
+ {
+ if( dataa.distance != datab.distance )
+ return dataa.distance < datab.distance;
+ }
+
+ UInt32 flagsa = ra.data;
+ UInt32 flagsb = rb.data;
+
+ // render all first passes first
+ if( (flagsa & kPackFirstPassFlag) != (flagsb & kPackFirstPassFlag) )
+ return (flagsa & kPackFirstPassFlag) > (flagsb & kPackFirstPassFlag);
+
+ // sort by lightmap index (fine to do it before source material index
+ // since every part of same mesh will have the same lightmap index)
+ if( dataa.lightmapIndex != datab.lightmapIndex )
+ return dataa.lightmapIndex < datab.lightmapIndex;
+
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+ // if part of predefined static batch, then sort by static batch index
+ if( dataa.staticBatchIndex != datab.staticBatchIndex )
+ return dataa.staticBatchIndex < datab.staticBatchIndex;
+
+ // otherwise sort by material index. Some people are using multiple materials
+ // on a single mesh and expect them to be rendered in order.
+ if( dataa.staticBatchIndex == 0 && dataa.sourceMaterialIndex != datab.sourceMaterialIndex )
+ return dataa.sourceMaterialIndex < datab.sourceMaterialIndex;
+#else
+ // Sort by material index. Some people are using multiple materials
+ // on a single mesh and expect them to be rendered in order.
+ if( dataa.sourceMaterialIndex != datab.sourceMaterialIndex )
+ return dataa.sourceMaterialIndex < datab.sourceMaterialIndex;
+#endif
+
+ // then sort by material
+ if( dataa.material != datab.material )
+ return dataa.material->GetInstanceID() < datab.material->GetInstanceID(); // just compare instance IDs
+
+ // inside same material: by pass
+ UInt32 passa = (flagsa >> kPackPassShift) & kPackPassMask;
+ UInt32 passb = (flagsb >> kPackPassShift) & kPackPassMask;
+ if( passa != passb )
+ return passa < passb;
+
+ // Sort by distance in reverse order.
+ // That way we get consistency in render order, and more pixels not rendered due to z-testing,
+ // which benefits performance.
+ if (opaque)
+ {
+ if( dataa.distance != datab.distance )
+ return dataa.distance > datab.distance;
+ }
+
+ // fall through: roIndex
+ return ra.roIndex < rb.roIndex;
+
+#endif
+}
+
+void ForwardVertexRenderLoop::PerformRendering (bool sSRGBRenderTarget, bool clearFrameBuffer)
+{
+ using namespace ForwardVertexRenderLoop_Enum;
+
+ GfxDevice& device = GetGfxDevice();
+ const RenderSettings& renderSettings = GetRenderSettings();
+
+ const RenderManager::Renderables& renderables = GetRenderManager ().GetRenderables ();
+ RenderManager::Renderables::const_iterator renderablesBegin = renderables.begin(), renderablesEnd = renderables.end();
+
+ const LightmapSettings& lightmapper = GetLightmapSettings();
+
+ size_t npasses = m_PlainRenderPasses.size();
+
+ int currentQueueIndex = m_Context->m_RenderQueueStart;
+ device.SetViewMatrix( m_Context->m_CurCameraMatrix.GetPtr() );
+
+ ForwardVertexRenderState prevRenderState;
+ prevRenderState.Invalidate();
+
+ // SRGB read/write for vertexRenderLoop
+ device.SetSRGBWrite(sSRGBRenderTarget);
+ if (clearFrameBuffer)
+ m_Context->m_Camera->ClearNoSkybox(false);
+
+ const ChannelAssigns* channels = NULL;
+ int canBatch = 0;
+ StartRenderLoop();
+ for( size_t i = 0; i < npasses; ++i )
+ {
+ const RPDataVLit& rpData = m_PlainRenderPasses[i];
+ DebugAssertIf (rpData.roIndex < 0 || rpData.roIndex >= m_Objects->size() || rpData.roIndex >= m_RenderObjectsCold.size());
+ const RenderObjectData& roDataH = (*m_Objects)[rpData.roIndex];
+ const RODataVLit& roDataC = m_RenderObjectsCold[rpData.roIndex];
+
+ const VertexLightsBlock& roDataL = *reinterpret_cast<VertexLightsBlock*>(&m_RenderObjectsLightData[roDataC.lightsDataOffset]);
+
+ const int roQueueIndex = roDataH.queueIndex;
+ DebugAssertIf( roQueueIndex < currentQueueIndex );
+ if( roQueueIndex > currentQueueIndex )
+ {
+ m_BatchRenderer.Flush();
+ canBatch = 0;
+ EndRenderLoop();
+ // Draw required renderables
+ if (!m_Context->m_DontRenderRenderables)
+ {
+ while( renderablesBegin != renderablesEnd && renderablesBegin->first <= roQueueIndex )
+ {
+ renderablesBegin->second->RenderRenderable(*m_Context->m_CullResults);
+
+ ++renderablesBegin;
+ }
+ }
+
+ currentQueueIndex = roQueueIndex;
+ StartRenderLoop();
+ }
+
+ const VisibleNode* node = roDataH.visibleNode;
+ const UInt16 subsetIndex = roDataH.subsetIndex;
+
+ ForwardVertexRenderState rs;
+ {
+ rs.rendererType = node->renderer->GetRendererType();
+ rs.transformType = node->transformType;
+ rs.invScale = roDataC.invScale;
+ rs.lodFade = roDataC.lodFade;
+
+ rs.material = roDataH.material;
+ rs.shader = roDataH.shader;
+ rs.subshaderIndex = roDataC.subshaderIndex;
+ rs.passIndex = (rpData.data >> kPackPassShift) & kPackPassMask;
+
+ rs.lights = &roDataL;
+
+ rs.lightmapIndex = roDataH.lightmapIndex;
+ DebugAssert(rs.lightmapIndex == node->renderer->GetLightmapIndex());
+ rs.lightmapST = node->renderer->GetLightmapSTForRendering();
+ rs.customPropsHash = node->renderer->GetCustomPropertiesHash();
+ }
+
+ // multi-pass requires vertex position values to be EXACTLY the same for all passes
+ // therefore do NOT batch dynamic multi-pass nodes
+ const bool multiPass = (rpData.data & kPackMultiPassFlag) == kPackMultiPassFlag;
+ const bool dynamicAndMultiPass = (node->renderer->GetStaticBatchIndex() == 0) && multiPass;
+
+ if (dynamicAndMultiPass ||
+ prevRenderState != rs)
+ {
+ m_BatchRenderer.Flush();
+ prevRenderState = rs;
+ canBatch = 0;
+ }
+ else
+ ++canBatch;
+
+ // NOTE: identity matrix has to be set on OpenGLES before lights are set
+ // as lighting is specified in World space
+ device.SetWorldMatrix( Matrix4x4f::identity.GetPtr() );
+
+ renderSettings.SetupAmbient ();
+ SetObjectScale(device, roDataC.lodFade, roDataC.invScale);
+
+ node->renderer->ApplyCustomProperties(*rs.material, rs.shader, rs.subshaderIndex);
+
+ // only setup lights & pass when not batching
+ if (canBatch < 1)
+ {
+ SetupObjectLightmaps (lightmapper, rs.lightmapIndex, rs.lightmapST, true);
+
+ LightManager::SetupVertexLights(rs.lights->lightCount, rs.lights->GetLights());
+ channels = rs.material->SetPassWithShader(rs.passIndex, rs.shader, rs.subshaderIndex);
+ }
+ if (channels)
+ {
+ m_BatchRenderer.Add(node->renderer, subsetIndex, channels, node->worldMatrix, rs.transformType);
+ }
+ }
+
+ m_BatchRenderer.Flush();
+ EndRenderLoop();
+ device.SetSRGBWrite(false);
+ device.SetViewMatrix( m_Context->m_CurCameraMatrix.GetPtr() );
+
+
+ // After everything we might still have renderables that should be drawn at the
+ // very end. Do it.
+ if (!m_Context->m_DontRenderRenderables)
+ {
+ while (renderablesBegin != renderablesEnd && renderablesBegin->first < m_Context->m_RenderQueueStart)
+ ++renderablesBegin;
+ while( renderablesBegin != renderablesEnd && renderablesBegin->first < m_Context->m_RenderQueueEnd )
+ {
+ renderablesBegin->second->RenderRenderable(*m_Context->m_CullResults);
+
+ ++renderablesBegin;
+ }
+ }
+}
+
+
+ForwardVertexRenderLoop* CreateForwardVertexRenderLoop()
+{
+ return new ForwardVertexRenderLoop();
+}
+
+void DeleteForwardVertexRenderLoop (ForwardVertexRenderLoop* queue)
+{
+ delete queue;
+}
+
+
+static bool IsPassSuitable (UInt32 currentRenderOptions, UInt32 passRenderOptions, ShaderPassType passType,
+ bool isLightmapped, bool useRGBM)
+{
+ // All options that a pass requires must be on
+ if( (currentRenderOptions & passRenderOptions) != passRenderOptions )
+ return false; // some options are off, skip this pass
+
+ if (passType != kPassAlways && passType != kPassVertex &&
+ passType != kPassVertexLM && passType != kPassVertexLMRGBM)
+ return false; // unsuitable pass type
+
+ // Use either lightmapped or non-lightmapped pass
+ if ((passType == kPassVertex && isLightmapped) ||
+ ((passType == kPassVertexLM || passType == kPassVertexLMRGBM) && !isLightmapped))
+ return false;
+
+ // Use pass that can properly decode the lightmap
+ if ((passType == kPassVertexLM && useRGBM) ||
+ (passType == kPassVertexLMRGBM && !useRGBM))
+ return false;
+
+ return true;
+}
+
+#if ENABLE_VERTEX_LOOP_HASH_SORTING
+template<typename T>
+static UInt8* InsertIntoHashBufferVtx(const T* p, UInt8* buffer)
+{
+ Assert((sizeof(T) % 4) == 0); // unaligned write
+ *reinterpret_cast<T*>(buffer) = *p;
+ return buffer + sizeof(T);
+}
+#endif
+
+void DoForwardVertexRenderLoop (RenderLoopContext& ctx, RenderObjectDataContainer& objects, bool opaque, ActiveLights& activeLights, bool linearLighting, bool clearFrameBuffer)
+{
+ GPU_AUTO_SECTION(opaque ? kGPUSectionOpaquePass : kGPUSectionTransparentPass);
+
+ using namespace ForwardVertexRenderLoop_Enum;
+
+ // Allocated on the stack each time, uses temp allocators
+ ForwardVertexRenderLoop queue;
+ queue.m_Context = &ctx;
+ queue.m_Objects = &objects;
+ queue.m_RenderObjectsCold.reserve(objects.size());
+ queue.m_PlainRenderPasses.reserve(objects.size());
+ const int kEstimatedLightDataPerObject = sizeof(VertexLightsBlock) + kEstimatedLightsPerObject * sizeof(Light*);
+ queue.m_RenderObjectsLightData.reserve(objects.size() * kEstimatedLightDataPerObject);
+
+ const CullResults& cullResults = *ctx.m_CullResults;
+
+ // figure out current rendering options
+ UInt32 currentRenderOptions = GetCurrentRenderOptions ();
+
+ //RenderSettings& renderSettings = GetRenderSettings();
+ const LightmapSettings& lightmapper = GetLightmapSettings();
+#if UNITY_EDITOR
+ bool useLightmaps = GetLightmapVisualization().GetUseLightmapsForRendering();
+#endif
+
+ bool useRGBM = gGraphicsCaps.SupportsRGBM();
+
+ // Figure everything out
+ RenderObjectDataContainer::iterator itEnd = objects.end();
+ size_t roIndex = 0;
+ for (RenderObjectDataContainer::iterator it = objects.begin(); it != itEnd; ++it, ++roIndex)
+ {
+ RenderObjectData& odata = *it;
+
+ const VisibleNode* node = odata.visibleNode;
+ RODataVLit& roDataC = queue.m_RenderObjectsCold.push_back();
+ size_t visibleNodeIndex = node - cullResults.nodes.begin();
+
+ LightmapSettings::TextureTriple lmTextures = lightmapper.GetLightmapTexture (node->renderer->GetLightmapIndex());
+#if UNITY_EDITOR
+ bool isLightmapped = useLightmaps && lmTextures.first.m_ID;
+#else
+ bool isLightmapped = lmTextures.first.m_ID;
+#endif
+ ShaderLab::IntShader& slshader = *odata.shader->GetShaderLabShader();
+ int vlitSS = odata.subShaderIndex;
+ if (vlitSS == -1)
+ {
+ vlitSS = slshader.GetDefaultSubshaderIndex (isLightmapped ? kRenderPathExtVertexLM : kRenderPathExtVertex);
+ if (vlitSS == -1)
+ continue;
+ }
+ roDataC.subshaderIndex = vlitSS;
+
+ size_t objectLightsOffset = queue.m_RenderObjectsLightData.size();
+ roDataC.lightsDataOffset = objectLightsOffset;
+
+ GetLightManager().FindVertexLightsForObject (
+ queue.m_RenderObjectsLightData,
+ GetObjectLightIndices(cullResults, visibleNodeIndex),
+ GetObjectLightCount(cullResults, visibleNodeIndex),
+ activeLights, *node);
+
+ roDataC.invScale = node->invScale;
+ roDataC.lodFade = node->lodFade;
+
+ // Go over all passes in the shader and add suitable ones for rendering
+ ShaderLab::SubShader& subshader = slshader.GetSubShader(roDataC.subshaderIndex);
+ int shaderPassCount = subshader.GetValidPassCount();
+
+ // Determine if we will need more than a single pass
+ int suitablePasses = 0;
+ for( int pass = 0; pass < shaderPassCount && suitablePasses < 2; ++pass )
+ {
+ ShaderPassType passType; UInt32 passRenderOptions;
+ subshader.GetPass(pass)->GetPassOptions( passType, passRenderOptions );
+
+ if (IsPassSuitable (currentRenderOptions, passRenderOptions, passType, isLightmapped, useRGBM))
+ ++suitablePasses;
+ }
+
+ // Go over all passes in the shader
+ UInt32 firstPassFlag = kPackFirstPassFlag;
+ const UInt32 multiPassFlag = (suitablePasses > 1)? kPackMultiPassFlag: 0;
+ for (int pass = 0; pass < shaderPassCount; ++pass)
+ {
+ ShaderPassType passType;
+ UInt32 passRenderOptions;
+ subshader.GetPass(pass)->GetPassOptions( passType, passRenderOptions );
+
+ if (!IsPassSuitable (currentRenderOptions, passRenderOptions, passType, isLightmapped, useRGBM))
+ continue;
+
+ RPDataVLit& rpData = queue.m_PlainRenderPasses.push_back();
+ rpData.roIndex = roIndex;
+ rpData.data =
+ ((pass & kPackPassMask) << kPackPassShift) |
+ firstPassFlag |
+ multiPassFlag;
+ firstPassFlag = 0;
+
+#if ENABLE_VERTEX_LOOP_HASH_SORTING
+
+ //hash state information for render object sorter
+ const int kHashBufferSize = 64;
+ UInt8 hashBuffer[kHashBufferSize];
+ UInt8* hashPtr = hashBuffer;
+
+ // Always write 32b granularity into the hash buffer to avoid unaligned writes
+ UInt32 rendererType = static_cast<UInt32>(node->renderer->GetRendererType());
+ hashPtr = InsertIntoHashBufferVtx(&rendererType, hashPtr);
+ UInt32 lightmapIndex = odata.lightmapIndex;
+ hashPtr = InsertIntoHashBufferVtx(&lightmapIndex, hashPtr);
+ UInt32 sourceMaterialIndex = 0;
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+ hashPtr = InsertIntoHashBufferVtx(&odata.staticBatchIndex, hashPtr);
+ if (odata.staticBatchIndex == 0)
+ sourceMaterialIndex = odata.sourceMaterialIndex;
+#else
+ sourceMaterialIndex = odata.sourceMaterialIndex;
+#endif
+ hashPtr = InsertIntoHashBufferVtx(&sourceMaterialIndex, hashPtr);
+
+ Assert(hashPtr-hashBuffer <= kHashBufferSize);
+
+ Assert(hashPtr-hashBuffer <= kHashBufferSize);
+
+ rpData.hash = MurmurHash2A(hashBuffer, hashPtr-hashBuffer, 0x9747b28c);
+#endif
+ }
+ }
+
+ // sort everything
+ if (opaque)
+ queue.SortRenderPassData<true> (queue.m_PlainRenderPasses);
+ else
+ queue.SortRenderPassData<false> (queue.m_PlainRenderPasses);
+
+ // Render everything. When transitioning to render queues,
+ // it will invoke camera renderables (halos, and so on).
+ RenderTexture* rtMain = ctx.m_Camera->GetCurrentTargetTexture ();
+ queue.PerformRendering (linearLighting && (!rtMain || rtMain->GetSRGBReadWrite()), clearFrameBuffer);
+}
diff --git a/Runtime/Camera/RenderLoops/GlobalLayeringData.h b/Runtime/Camera/RenderLoops/GlobalLayeringData.h
new file mode 100644
index 0000000..176edde
--- /dev/null
+++ b/Runtime/Camera/RenderLoops/GlobalLayeringData.h
@@ -0,0 +1,26 @@
+#pragma once
+
+struct GlobalLayeringData
+{
+ // Per-renderer sorting data.
+ SInt16 layer; // Layer order.
+ SInt16 order; // In-layer order.
+};
+
+inline GlobalLayeringData GlobalLayeringDataCleared () { GlobalLayeringData data = {0,0}; return data; }
+
+inline bool CompareGlobalLayeringData(const GlobalLayeringData& lhs, const GlobalLayeringData& rhs, bool& result)
+{
+ if (lhs.layer != rhs.layer)
+ {
+ result = lhs.layer < rhs.layer;
+ return true;
+ }
+ else if (lhs.order != rhs.order)
+ {
+ result = lhs.order < rhs.order;
+ return true;
+ }
+
+ return false;
+}
diff --git a/Runtime/Camera/RenderLoops/PrePassRenderLoop.cpp b/Runtime/Camera/RenderLoops/PrePassRenderLoop.cpp
new file mode 100644
index 0000000..8aa3ab6
--- /dev/null
+++ b/Runtime/Camera/RenderLoops/PrePassRenderLoop.cpp
@@ -0,0 +1,1958 @@
+#include "UnityPrefix.h"
+#include "Runtime/GfxDevice/GfxDeviceConfigure.h"
+
+#if GFX_SUPPORTS_RENDERLOOP_PREPASS
+#include "RenderLoopPrivate.h"
+#include "Runtime/Camera/Renderqueue.h"
+#include "Runtime/Camera/BaseRenderer.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Camera/ImageFilters.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/Camera/RenderSettings.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Graphics/RenderBufferManager.h"
+#include "Runtime/Graphics/GraphicsHelper.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Camera/Light.h"
+#include "Runtime/Camera/Shadows.h"
+#include "Runtime/Graphics/LightmapSettings.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "Runtime/Misc/QualitySettings.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Graphics/GeneratedTextures.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/GfxDevice/BatchRendering.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+#include "ReplacementRenderLoop.h"
+#if UNITY_EDITOR
+#include "Runtime/BaseClasses/Tags.h"
+#endif
+#include "BuiltinShaderParamUtility.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Math/SphericalHarmonics.h"
+#include "Runtime/Camera/LightManager.h"
+#include "External/MurmurHash/MurmurHash2.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Graphics/DrawUtil.h"
+
+// Enable/disable hash based pre pass render loop sorting functionality.
+#define ENABLE_PRE_PASS_LOOP_HASH_SORTING 0
+
+#define SEPERATE_PREPASS_SPECULAR UNITY_XENON
+
+PROFILER_INFORMATION(gPrepassSort, "RenderPrePass.Sort", kProfilerRender)
+PROFILER_INFORMATION(gPrepassGeom, "RenderPrePass.GeometryPass", kProfilerRender)
+PROFILER_INFORMATION(gPrepassLighting, "RenderPrePass.Lighting", kProfilerRender)
+PROFILER_INFORMATION(gPrepassLight, "RenderPrePass.Light", kProfilerRender)
+PROFILER_INFORMATION(gPrepassFinal, "RenderPrePass.FinalPass", kProfilerRender)
+PROFILER_INFORMATION(gPrepassFwdDepth, "RenderPrePass.ForwardObjectsToDepth", kProfilerRender)
+PROFILER_INFORMATION(gPrepassCombineDepthNormals, "RenderPrePass.CombineDepthNormals", kProfilerRender)
+
+
+static SHADERPROP (LightPos);
+static SHADERPROP (LightDir);
+static SHADERPROP (LightColor);
+static SHADERPROP (LightTexture0);
+static SHADERPROP (LightBuffer);
+static SHADERPROP (LightAsQuad);
+
+// ShadowMapTexture must be in namespace or otherwise it conflicts with property in
+// ForwardShaderRenderLoop.cpp in batched Android build.
+namespace PrePassPrivate
+{
+static SHADERPROP (ShadowMapTexture);
+}
+
+#if SEPERATE_PREPASS_SPECULAR
+static SHADERPROP (LightSpecBuffer);
+#endif
+
+static Material* s_LightMaterial = NULL;
+static Material* s_CollectMaterial = NULL;
+
+static ShaderKeyword kKeywordHDRLightPrepassOn = keywords::Create ("HDR_LIGHT_PREPASS_ON");
+
+static PPtr<Mesh> s_Icosahedron = NULL;
+static PPtr<Mesh> s_Icosphere = NULL;
+static PPtr<Mesh> s_Pyramid = NULL;
+
+
+enum {
+ kLightingLayerCount = 4, // bits of stencil used for lighting layers
+
+ // 3 highest bits used for excluding lights for other reasons.
+ kStencilMaskSomething = (1<<7), // any object (i.e. not background)
+ kStencilMaskNonLightmapped = (1<<6), // non-lightmapped object
+ kStencilMaskBeyondShadowDistace = (1<<5), // beyond shadow distance
+ kStencilMaskLightBackface = (1<<4), // don't render light where it's backface passes z test
+
+ // Next 4 highest bits (3 down to 0) used for lighting layers.
+ kStencilBitLayerStart = 0, // start of lighting layer bits
+ kStencilMaskLayers = ((1<<kLightingLayerCount)-1) << kStencilBitLayerStart,
+
+ kStencilGeomWriteMask = kStencilMaskSomething | kStencilMaskNonLightmapped | kStencilMaskBeyondShadowDistace | kStencilMaskLayers,
+};
+
+
+
+// Lights can illuminate arbitrary layer masks. Say we have several lights:
+// La = XXXXXXXX
+// Lb = XXXXXXX-
+// Lc = XXXX-XXX
+// Ld = XXXX-X--
+// Layers used for excluding lights are then:
+// ----O-OO (3 in total)
+// In stencil buffer, we allocate 3 consecutive bits to handle this:
+// LaS = ---
+// LbS = --O
+// LcS = O--
+// LdS = OOO
+//
+// When rendering an object, set that bit if object belongs to one of light layers.
+//
+// When drawing a light, set stencil mask to light layer stencil mask, and stencil
+// test should be equal to zero in those bits.
+
+struct LightingLayers
+{
+ enum { kLayerCount = 32 };
+
+ LightingLayers (UInt32 lightMask)
+ : lightingLayerMask(lightMask)
+ {
+ for (int i = 0; i < kLayerCount; ++i)
+ layerToStencil[i] = -1;
+
+ int bit = kStencilBitLayerStart + kLightingLayerCount - 1;
+ lightLayerCount = 0;
+ UInt32 mask = 1;
+ for (int i = 0; i < kLayerCount; ++i, mask<<=1)
+ {
+ if (lightMask & mask)
+ {
+ if (lightLayerCount < kLightingLayerCount)
+ layerToStencil[i] = bit;
+ --bit;
+ ++lightLayerCount;
+ }
+ }
+ }
+
+ UInt32 lightingLayerMask;
+ int layerToStencil[kLayerCount];
+ int lightLayerCount;
+};
+
+struct PrePassRenderData {
+ int roIndex;
+#if ENABLE_PRE_PASS_LOOP_HASH_SORTING
+ UInt32 hash;
+#endif
+};
+typedef dynamic_array<PrePassRenderData> PreRenderPasses;
+
+
+struct PrePassRenderLoop
+{
+ const RenderLoopContext* m_Context;
+ RenderObjectDataContainer* m_Objects;
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ BatchRenderer m_BatchRenderer;
+ #endif
+
+ PreRenderPasses m_PlainRenderPasses;
+
+ RenderTexture* RenderBasePass (RenderTexture* rtMain, const LightingLayers& lightingLayers, RenderObjectDataContainer& outRemainingObjects, MinMaxAABB& receiverBounds);
+
+ void RenderLighting (
+ ActiveLights& activeLights,
+ RenderTexture* rtMain,
+ TextureID depthTextureID,
+ RenderTexture* rtNormalsSpec,
+ RenderTexture*& rtLight,
+
+#if SEPERATE_PREPASS_SPECULAR
+ RenderTexture*& rtLightSpec,
+#endif
+ const Vector4f& lightFade,
+ const LightingLayers& lightingLayers,
+ MinMaxAABB& receiverBounds,
+ RenderTexture** outMainShadowMap);
+
+ void RenderFinalPass (RenderTexture* rtMain,
+ RenderTexture* rtLight,
+#if SEPERATE_PREPASS_SPECULAR
+ RenderTexture* rtLightSpec,
+#endif
+ bool hdr,
+ bool linearLighting);
+
+ struct RenderPrePassObjectSorterHash
+ {
+ bool operator()( const PrePassRenderData& ra, const PrePassRenderData& rb ) const;
+ const PrePassRenderLoop* queue;
+ };
+
+ void SortPreRenderPassData( PreRenderPasses& passes )
+ {
+ RenderPrePassObjectSorterHash sorter;
+ sorter.queue = this;
+ std::sort( passes.begin(), passes.end(), sorter );
+ }
+};
+
+
+struct RenderPrePassObjectSorter {
+ bool operator()( const RenderObjectData& ra, const RenderObjectData& rb ) const;
+};
+
+
+
+
+bool RenderPrePassObjectSorter::operator()( const RenderObjectData& ra, const RenderObjectData& rb ) const
+{
+ // Sort by layering depth.
+ bool globalLayeringResult;
+ if (CompareGlobalLayeringData(ra.globalLayeringData, rb.globalLayeringData, globalLayeringResult))
+ return globalLayeringResult;
+
+ // Sort by render queues first
+ if( ra.queueIndex != rb.queueIndex )
+ return ra.queueIndex < rb.queueIndex;
+
+ // sort by lightmap index
+ if( ra.lightmapIndex != rb.lightmapIndex )
+ return ra.lightmapIndex < rb.lightmapIndex;
+
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+ // if part of predefined static batch, then sort by static batch index
+ if( ra.staticBatchIndex != rb.staticBatchIndex )
+ return ra.staticBatchIndex > rb.staticBatchIndex; // assuming that statically batched geometry occlude more - render it first
+#endif
+
+ // then sort by material (maybe better sort by shader?)
+ if( ra.material != rb.material )
+ return ra.material->GetInstanceID() < rb.material->GetInstanceID(); // just compare instance IDs
+
+ // Sort front to back
+ return ra.distance > rb.distance;
+}
+
+#if ENABLE_PRE_PASS_LOOP_HASH_SORTING
+bool PrePassRenderLoop::RenderPrePassObjectSorterHash::operator()( const PrePassRenderData& ra, const PrePassRenderData& rb ) const
+{
+ const RenderObjectData& dataa = (*queue->m_Objects)[ra.roIndex];
+ const RenderObjectData& datab = (*queue->m_Objects)[rb.roIndex];
+
+ // Sort by layering depth.
+ bool globalLayeringResult;
+ if (CompareGlobalLayeringData(dataa.globalLayeringData, datab.globalLayeringData, globalLayeringResult))
+ return globalLayeringResult;
+
+ // Sort by render queues first
+ if( dataa.queueIndex != datab.queueIndex )
+ return dataa.queueIndex < datab.queueIndex;
+
+ // sort by hash
+ if( ra.hash != rb.hash )
+ return ra.hash < rb.hash;
+
+ // Sort front to back
+ return dataa.distance > datab.distance;
+}
+#endif
+
+static Texture* defaultSpotCookie = NULL;
+
+static void AssignCookieToMaterial(const Light& light, Material* lightMaterial)
+{
+ //@TODO: when computing positions from screen space, mipmapping of cookie will really play against
+ // us, when some adjacent pixels will happen to have very similar UVs. It will sample high levels which
+ // will be mostly black!
+ // Proper fix would be manual derivatives based on something else in the shader, but that needs SM3.0 on D3D
+ // and GLSL in GL. So just use bad mip bias for now.
+
+ Texture* cookie = light.GetCookie();
+
+ if(cookie)
+ {
+ lightMaterial->SetTexture (kSLPropLightTexture0, cookie);
+ }
+ else if(light.GetType() == kLightSpot)
+ {
+ if(!defaultSpotCookie)
+ {
+ defaultSpotCookie = (Texture*)GetRenderSettings().GetDefaultSpotCookie();
+ }
+ lightMaterial->SetTexture (kSLPropLightTexture0, defaultSpotCookie);
+ }
+}
+
+
+// To properly collect & blur directional light's screen space shadow map,
+// we need to have shadow receivers that are forward-rendered in the depth buffer.
+// Also, if camera needs a depth texture, forward-rendered objects should be there
+// as well.
+static void RenderForwardObjectsIntoDepth (
+ const RenderLoopContext& ctx,
+ RenderTexture* rt,
+ RenderObjectDataContainer* forwardRenderedObjects,
+ RenderSurfaceHandle rtColorSurface,
+ RenderSurfaceHandle rtDepthSurface,
+ int width, int height,
+ bool cameraNeedsDepthTexture)
+{
+ Assert (rt);
+
+ if (!forwardRenderedObjects || forwardRenderedObjects->size() == 0)
+ return; // nothing to do
+
+ PROFILER_AUTO_GFX(gPrepassFwdDepth, ctx.m_Camera);
+ GPU_AUTO_SECTION(kGPUSectionOpaquePass);
+
+ Shader* depthShader = GetCameraDepthTextureShader ();
+ if (!depthShader)
+ return;
+
+ // If we do not need the depth texture, leave only the objects that will possibly receive shadows;
+ // no need to render all forward objects.
+ RenderObjectDataContainer forwardRenderedShadowReceivers;
+ if (!cameraNeedsDepthTexture)
+ {
+ size_t n = forwardRenderedObjects->size();
+ forwardRenderedShadowReceivers.reserve (n / 4);
+ for (size_t i = 0; i < n; ++i)
+ {
+ RenderObjectData& roData = (*forwardRenderedObjects)[i];
+ DebugAssert (roData.visibleNode);
+ BaseRenderer* renderer = roData.visibleNode->renderer;
+ DebugAssert (renderer);
+ if (!renderer->GetReceiveShadows())
+ continue; // does not receive shadows
+ Shader* shader = roData.shader;
+ int ss = shader->GetShaderLabShader()->GetDefaultSubshaderIndex (kRenderPathExtForward);
+ if (ss == -1)
+ continue; // is not forward rendered
+ forwardRenderedShadowReceivers.push_back (roData);
+ }
+
+ if (forwardRenderedShadowReceivers.size() == 0)
+ return; // nothing left to render
+ forwardRenderedObjects = &forwardRenderedShadowReceivers;
+ }
+
+ RenderTexture::SetActive (1, &rtColorSurface, rtDepthSurface, rt);
+ RenderSceneShaderReplacement (*forwardRenderedObjects, depthShader, "RenderType");
+}
+
+static RenderTexture* ComputeScreenSpaceShadowMap (
+ const RenderLoopContext& ctx,
+ RenderTexture* shadowMap,
+ float blurWidth,
+ float blurFade,
+ ShadowType shadowType)
+{
+ Assert (shadowMap);
+
+ GfxDevice& device = GetGfxDevice();
+
+ if (!s_CollectMaterial)
+ {
+ Shader* shader = GetScriptMapper().FindShader ("Hidden/Internal-PrePassCollectShadows");
+ s_CollectMaterial = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+ }
+
+ SetShadowsKeywords (kLightDirectional, shadowType, false, false);
+ RenderBufferManager& rbm = GetRenderBufferManager ();
+
+ RenderTexture* screenShadowMap = rbm.GetTempBuffer (RenderBufferManager::kFullSize, RenderBufferManager::kFullSize, kDepthFormatNone, kRTFormatARGB32, 0, kRTReadWriteLinear);
+ RenderTexture::SetActive (screenShadowMap);
+
+ // Clear so that tiled and multi-GPU systems don't do a RT unresolve
+ float clearColor[4] = {1,0,1,0};
+ device.Clear(kGfxClearColor, clearColor, 1.0f, 0);
+
+ LoadFullScreenOrthoMatrix ();
+ s_CollectMaterial->SetTexture (PrePassPrivate::kSLPropShadowMapTexture, shadowMap);
+ s_CollectMaterial->SetPass (0);
+
+ Vector3f ray;
+ device.ImmediateBegin (kPrimitiveQuads);
+
+ float x1 = 0.0f;
+ float x2 = 1.0f;
+ float y1 = 0.0f;
+ float y2 = 1.0f;
+ float f = ctx.m_Camera->GetProjectionFar();
+
+ const Transform& camtr = ctx.m_Camera->GetComponent(Transform);
+ Matrix4x4f cameraWorldToLocalNoScale = camtr.GetWorldToLocalMatrixNoScale();
+
+ device.ImmediateTexCoord (0, x1, y1, 0.0f);
+ ray = cameraWorldToLocalNoScale.MultiplyPoint3(ctx.m_Camera->ViewportToWorldPoint (Vector3f(x1, y1, f)));
+ device.ImmediateNormal (ray.x, ray.y, ray.z);
+ device.ImmediateVertex (x1, y1, 0.1f);
+
+ device.ImmediateTexCoord (0, x2, y1, 0.0f);
+ ray = cameraWorldToLocalNoScale.MultiplyPoint3(ctx.m_Camera->ViewportToWorldPoint (Vector3f(x2, y1, f)));
+ device.ImmediateNormal (ray.x, ray.y, ray.z);
+ device.ImmediateVertex (x2, y1, 0.1f);
+
+ device.ImmediateTexCoord (0, x2, y2, 0.0f);
+ ray = cameraWorldToLocalNoScale.MultiplyPoint3(ctx.m_Camera->ViewportToWorldPoint (Vector3f(x2, y2, f)));
+ device.ImmediateNormal (ray.x, ray.y, ray.z);
+ device.ImmediateVertex (x2, y2, 0.1f);
+
+ device.ImmediateTexCoord (0, x1, y2, 0.0f);
+ ray = cameraWorldToLocalNoScale.MultiplyPoint3(ctx.m_Camera->ViewportToWorldPoint (Vector3f(x1, y2, f)));
+ device.ImmediateNormal (ray.x, ray.y, ray.z);
+ device.ImmediateVertex (x1, y2, 0.1f);
+
+ device.ImmediateEnd ();
+ GPU_TIMESTAMP();
+
+ rbm.ReleaseTempBuffer (shadowMap);
+
+ // possibly blur into another screen-space render texture
+ SetShadowsKeywords (kLightDirectional, shadowType, true, true);
+ if (IsSoftShadow(shadowType) && GetSoftShadowsEnabled())
+ return BlurScreenShadowMap (screenShadowMap, shadowType, f, blurWidth, blurFade);
+
+ return screenShadowMap;
+}
+
+static void RenderLightGeom (const RenderLoopContext& ctx, const ActiveLight& light, const Vector3f& lightPos, const Matrix4x4f& lightMatrix, const bool renderAsQuad)
+{
+ // Spot and point lights: render as tight geometry. If it doesn't intersect near or far, stencil optimisation will be used
+ // (rendering the z tested back faces into stencil and then front faces will only pass for these pixels).
+ // If it intersects near, back faces with z test greater will be rendered (shouldn't use that when not intersecting near, because
+ // then there could be objects between the cam and the light, not touching the light).
+ // If it intersects far, render front faces without any gimmicks.
+ // If it intersects both near and far, render as a quad.
+
+ GfxDevice& device = GetGfxDevice();
+ Light& l = *light.light;
+ float r = l.GetRange();
+ float n = ctx.m_Camera->GetProjectionNear() * 1.001f;
+
+ if (l.GetType() == kLightPoint && !renderAsQuad)
+ {
+ #if GFX_USE_SPHERE_FOR_POINT_LIGHT
+ ChannelAssigns ch;
+ ch.Bind (kShaderChannelVertex, kVertexCompVertex);
+
+ // Older content might have included/overriden old Internal-PrePassLighting.shader,
+ // which relied on normals being zeros here. Light .fbx files have zero normals just for that.
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1))
+ ch.Bind (kShaderChannelNormal, kVertexCompNormal);
+
+ Matrix4x4f m;
+ m.SetTranslate (lightPos);
+ m.Get (0, 0) = r;
+ m.Get (1, 1) = r;
+ m.Get (2, 2) = r;
+ // Point lights bigger than 0.25 of the screen height can be rendered with high-poly, but tighter geometry.
+ DrawUtil::DrawMesh (ch, light.screenRect.height > 0.25f ? *s_Icosphere : *s_Icosahedron, m, -1);
+ #else
+ // PS3 is not the best at vertex processing, so stick to low-poly meshes
+ device.ImmediateShape(lightPos.x, lightPos.y, lightPos.z, r, GfxDevice::kShapeCube);
+ #endif
+ }
+ else if (l.GetType() == kLightSpot && !renderAsQuad)
+ {
+ Matrix4x4f m (lightMatrix);
+ ChannelAssigns ch;
+ ch.Bind (kShaderChannelVertex, kVertexCompVertex);
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1))
+ ch.Bind (kShaderChannelNormal, kVertexCompNormal);
+ float sideLength = r / l.GetCotanHalfSpotAngle ();
+ m.Scale (Vector3f(sideLength, sideLength, r));
+ DrawUtil::DrawMesh (ch, *s_Pyramid, m, -1);
+ }
+ else // Directional light or spot/point that needs to be rendered as a quad
+ {
+ DeviceViewProjMatricesState preserveViewProj;
+
+ const Camera* camera = ctx.m_Camera;
+ float nearPlane = 0;
+
+ float x1 = light.screenRect.x;
+ float x2 = light.screenRect.x + light.screenRect.width;
+ float y1 = light.screenRect.y;
+ float y2 = light.screenRect.y + light.screenRect.height;
+
+ // Calculate rays pointing from the camera to the near plane's corners in camera space
+ Vector3f ray1 = camera->ViewportToCameraPoint (Vector3f(x1, y1, n));
+ Vector3f ray2 = camera->ViewportToCameraPoint (Vector3f(x1, y2, n));
+ Vector3f ray3 = camera->ViewportToCameraPoint (Vector3f(x2, y2, n));
+ Vector3f ray4 = camera->ViewportToCameraPoint (Vector3f(x2, y1, n));
+
+ // Set up orthographic projection not to have to deal with precision problems
+ // that show up when drawing a full screen quad in perspective projection in world space.
+ LoadFullScreenOrthoMatrix (nearPlane, camera->GetProjectionFar(), true);
+
+ // Draw the fullscreen quad on the near plane
+ device.ImmediateBegin (kPrimitiveQuads);
+
+ device.ImmediateNormal (ray1.x, ray1.y, ray1.z);
+ device.ImmediateVertex (x1, y1, nearPlane);
+
+ device.ImmediateNormal (ray2.x, ray2.y, ray2.z);
+ device.ImmediateVertex (x1, y2, nearPlane);
+
+ device.ImmediateNormal (ray3.x, ray3.y, ray3.z);
+ device.ImmediateVertex (x2, y2, nearPlane);
+
+ device.ImmediateNormal (ray4.x, ray4.y, ray4.z);
+ device.ImmediateVertex (x2, y1, nearPlane);
+
+ device.ImmediateEnd ();
+ GPU_TIMESTAMP();
+ }
+}
+
+static UInt32 LightMask (const Light& l, const LightingLayers& lightingLayers)
+{
+ UInt32 mask = 0U;
+ UInt32 lightExcludeLayers = ~l.GetCullingMask();
+ int bit = 0;
+ while (lightExcludeLayers)
+ {
+ if (lightExcludeLayers & 1)
+ {
+ int layerStencilBit = lightingLayers.layerToStencil[bit];
+ if (layerStencilBit != -1)
+ mask |= 1 << layerStencilBit;
+ }
+ lightExcludeLayers >>= 1;
+ ++bit;
+ }
+ return mask;
+}
+
+static RenderTexture* RenderLight (
+ const RenderLoopContext& ctx,
+ const ShadowCullData& shadowCullData,
+ QualitySettings::ShadowQuality shadowQuality,
+ const LightmapSettings::LightmapsMode lightmapsMode,
+ RenderTexture*& rtLight,
+ RenderTexture* rtMain,
+ int width, int height,
+ DeviceStencilState* devStDisabled,
+ const MinMaxAABB& receiverBounds,
+ const DeviceMVPMatricesState& mvpState,
+ const Vector4f& lightFade,
+ const LightingLayers& lightingLayers,
+ const ActiveLight& light,
+#if SEPERATE_PREPASS_SPECULAR
+ bool specularPass,
+#endif
+ bool returnShadowMap)
+{
+ Light& l = *light.light;
+
+ PROFILER_AUTO_GFX(gPrepassLight, &l);
+
+ const Light::Lightmapping lightmappingMode = l.GetLightmappingForRender();
+ const Transform& trans = l.GetComponent(Transform);
+ Matrix4x4f lightMatrix = trans.GetLocalToWorldMatrixNoScale();
+ Vector3f lightPos = lightMatrix.GetPosition();
+
+ Assert(light.isVisibleInPrepass);
+ Assert(!light.screenRect.IsEmpty());
+
+ ShadowType lightShadows = l.GetShadows();
+ // Shadows on local lights are Pro only
+ if (lightShadows != kShadowNone && l.GetType() != kLightDirectional &&
+ !GetBuildSettings().hasLocalLightShadows)
+ lightShadows = kShadowNone;
+
+ // Check if soft shadows are allowed by license, quality settings etc.
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a1) &&
+ lightShadows > kShadowHard && !GetSoftShadowsEnabled())
+ lightShadows = kShadowHard;
+
+ GfxDevice& device = GetGfxDevice();
+ BuiltinShaderParamValues& params = device.GetBuiltinParamValues();
+
+ RenderSurfaceHandle rtSurfaceColor;
+ RenderSurfaceHandle rtSurfaceDepth = rtMain->GetDepthSurfaceHandle(); // re-use depth from final target
+ RenderSurfaceHandle rtSurfaceMainColor = rtMain->GetColorSurfaceHandle(); // will allocate color later (if any lights will actually be present)
+
+ bool hdr = ctx.m_Camera->GetUsingHDR();
+ float white[] = {1,1,1,1};
+ float black[] = {0,0,0,0};
+ UInt32 rtFlags = RenderTexture::kFlagDontRestoreColor;
+
+ if (!rtLight)
+ {
+ rtLight = GetRenderBufferManager().GetTempBuffer (RenderBufferManager::kFullSize, RenderBufferManager::kFullSize, kDepthFormatNone, hdr ? GetGfxDevice().GetDefaultHDRRTFormat() : kRTFormatARGB32, 0, kRTReadWriteLinear);
+
+ if (!rtLight->IsCreated())
+ rtLight->Create();
+
+ rtLight->SetFilterMode (kTexFilterNearest);
+ rtSurfaceColor = rtLight->GetColorSurfaceHandle();
+
+
+ RenderTexture::SetActive (1, &rtSurfaceColor, rtSurfaceDepth, rtLight, 0, kCubeFaceUnknown, rtFlags);
+ GraphicsHelper::Clear(kGfxClearColor, hdr ? black : white, 1.0f, 0);
+ GPU_TIMESTAMP();
+ }
+
+ rtSurfaceColor = rtLight->GetColorSurfaceHandle();
+
+ l.SetLightKeyword();
+
+ Vector3f lightDir = lightMatrix.GetAxisZ();
+ ColorRGBAf lightCol = GammaToActiveColorSpace (l.GetColor()) * l.GetIntensity() * 2.0f;
+
+ Matrix4x4f temp1, temp2, temp3;
+ if (l.GetType() == kLightSpot)
+ {
+ Matrix4x4f worldToLight = l.GetWorldToLocalMatrix();
+ {
+ temp1.SetScale (Vector3f (-.5f, -.5f, 1.0f));
+ temp2.SetTranslate (Vector3f (.5f, .5f, 0.0f));
+ temp3.SetPerspectiveCotan( l.GetCotanHalfSpotAngle(), 0.0f, l.GetRange() );
+ // temp2 * temp3 * temp1 * worldToLight
+ Matrix4x4f temp4;
+ MultiplyMatrices4x4 (&temp2, &temp3, &temp4);
+ MultiplyMatrices4x4 (&temp4, &temp1, &temp2);
+ MultiplyMatrices4x4 (&temp2, &worldToLight, &params.GetWritableMatrixParam(kShaderMatLightMatrix));
+ }
+ }
+ else if (l.GetCookie())
+ {
+ if (l.GetType() == kLightPoint)
+ {
+ params.SetMatrixParam(kShaderMatLightMatrix, l.GetWorldToLocalMatrix());
+ }
+ else if (l.GetType() == kLightDirectional)
+ {
+ float scale = 1.0f / l.GetCookieSize();
+ temp1.SetScale (Vector3f (scale, scale, 0));
+ temp2.SetTranslate (Vector3f (.5f, .5f, 0));
+ // temp2 * temp1 * l.GetWorldToLocalMatrix()
+ MultiplyMatrices4x4 (&temp2, &temp1, &temp3);
+ MultiplyMatrices4x4 (&temp3, &l.GetWorldToLocalMatrix(), &params.GetWritableMatrixParam(kShaderMatLightMatrix));
+ }
+ }
+
+ AssignCookieToMaterial(l, s_LightMaterial);
+
+ const bool renderAsQuad = light.intersectsNear && light.intersectsFar || l.GetType() == kLightDirectional;
+ ShaderLab::g_GlobalProperties->SetFloat(kSLPropLightAsQuad, renderAsQuad ? 1.0f : 0.0f);
+ ShaderLab::g_GlobalProperties->SetVector (kSLPropLightPos, lightPos.x, lightPos.y, lightPos.z, 1.0f / (l.GetRange() * l.GetRange()));
+ ShaderLab::g_GlobalProperties->SetVector (kSLPropLightDir, lightDir.x, lightDir.y, lightDir.z, 0.0f);
+ ShaderLab::g_GlobalProperties->SetVector (kSLPropLightColor, lightCol.GetPtr());
+ ///@TODO: cleanup, remove this from Internal-PrePassLighting shader
+ s_LightMaterial->SetTexture (ShaderLab::Property("_LightTextureB0"), builtintex::GetAttenuationTexture());
+
+ RenderTexture* shadowMap = NULL;
+ ShadowCameraData camData(shadowCullData);
+
+
+ if (light.shadowedLight != NULL && receiverBounds.IsValid() && shadowQuality != QualitySettings::kShadowsDisable)
+ {
+ Assert(light.insideShadowRange);
+
+ ShadowType lightShadows = l.GetShadows();
+
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a1))
+ {
+ if (shadowQuality == QualitySettings::kShadowsHardOnly && lightShadows != kShadowNone)
+ lightShadows = kShadowHard;
+ }
+
+ SetShadowsKeywords (l.GetType(), lightShadows, false, true);
+
+ Matrix4x4f shadowMatrices[kMaxShadowCascades];
+ device.SetViewMatrix (ctx.m_CurCameraMatrix.GetPtr());
+ device.SetStencilState (devStDisabled, 0);
+
+ // Rendering shadowmaps will switch away from the lighting buffer and then will switch back.
+ // Nothing we can do about it, so don't produce the warning.
+ device.IgnoreNextUnresolveOnCurrentRenderTarget();
+
+ shadowMap = RenderShadowMaps (camData, light, receiverBounds, false, shadowMatrices);
+
+ if (!shadowMap)
+ {
+ // If shadow map could not actually be created (no casters, out of VRAM, whatever),
+ // set the no shadows keywords and proceed. So there will be no shadows,
+ // but otherwise it will be ok.
+ SetNoShadowsKeywords();
+ }
+ else
+ {
+ Vector4f data;
+
+ // ambient & shadow fade out
+ data.x = 1.0f - l.GetShadowStrength(); // R = 1-strength
+ data.y = data.z = data.w = 0.0f;
+ params.SetVectorParam(kShaderVecLightShadowData, data);
+
+ if (l.GetType() == kLightDirectional)
+ {
+ params.SetMatrixParam(kShaderMatWorldToShadow, shadowMatrices[0]);
+ SetCascadedShadowShaderParams (shadowMatrices, camData.splitDistances, camData.splitSphereCentersAndSquaredRadii);
+
+ shadowMap = ComputeScreenSpaceShadowMap (
+ ctx,
+ shadowMap,
+ l.GetShadowSoftness(),
+ l.GetShadowSoftnessFade(),
+ lightShadows);
+ }
+ else if (l.GetType() == kLightSpot)
+ {
+ params.SetMatrixParam(kShaderMatWorldToShadow, shadowMatrices[0]);
+ }
+
+ // texel offsets for PCF
+ float offX = 0.5f / shadowMap->GetGLWidth();
+ float offY = 0.5f / shadowMap->GetGLHeight();
+ data.z = 0.0f; data.w = 0.0f;
+ data.x = -offX; data.y = -offY; params.SetVectorParam(kShaderVecShadowOffset0, data);
+ data.x = offX; data.y = -offY; params.SetVectorParam(kShaderVecShadowOffset1, data);
+ data.x = -offX; data.y = offY; params.SetVectorParam(kShaderVecShadowOffset2, data);
+ data.x = offX; data.y = offY; params.SetVectorParam(kShaderVecShadowOffset3, data);
+ s_LightMaterial->SetTexture (PrePassPrivate::kSLPropShadowMapTexture, shadowMap);
+
+ if (rtLight != NULL)
+ RenderTexture::SetActive (1, &rtSurfaceColor, rtSurfaceDepth, rtLight);
+ else
+ RenderTexture::SetActive (1, &rtSurfaceMainColor, rtSurfaceDepth, rtMain);
+ }
+ device.SetViewMatrix(mvpState.GetView().GetPtr());
+ device.SetProjectionMatrix(mvpState.GetProj());
+ SetClippingPlaneShaderProps();
+
+ // restore the cull mode, since it could be changed by a shadow caster with odd-negative scale
+ device.SetNormalizationBackface( kNormalizationDisabled, false );
+ }
+ else
+ {
+ SetNoShadowsKeywords ();
+ }
+
+ // Draw each light in two passes: illuminate non-lightmapped objects; illuminate lightmapped objects.
+ int lightPassCount = 2;
+ int lightPassAddBits[2] = { kStencilMaskNonLightmapped, 0 };
+ if (lightmappingMode == Light::kLightmappingRealtimeOnly)
+ {
+ // If light is realtime only, it's enough to draw one pass; that illuminates any object
+ // be it lightmapped or not.
+ lightPassCount = 1;
+ lightPassAddBits[0] = 0;
+ }
+ else if (lightmappingMode == Light::kLightmappingAuto && lightmapsMode != LightmapSettings::kDualLightmapsMode)
+ {
+ // If it's an auto light but we're in single lightmaps mode, draw in one pass only to illuminate
+ // non-lightmapped objects
+ // TODO: realtime shadows from auto lights won't be received by lightmapped objects. Do we want to fix it?
+ lightPassCount = 1;
+ lightPassAddBits[0] = kStencilMaskNonLightmapped;
+ }
+
+ //TODO: skip if smaller than certain size
+ const bool useStencilMask = !light.intersectsNear && !light.intersectsFar &&
+ (lightmappingMode == Light::kLightmappingRealtimeOnly) &&
+ (l.GetType() == kLightSpot || l.GetType() == kLightPoint );
+
+ const UInt32 lightmask = LightMask (l, lightingLayers);
+
+ // Render stencil mask, to discard all light pixels, at which the light is fully in front of scene geometry.
+ if (useStencilMask)
+ {
+ Material::GetDefault ()->SetPass (0);
+ #if UNITY_XENON
+ device.SetNullPixelShader ();
+ #endif
+
+ GfxBlendState blendstate;
+ blendstate.renderTargetWriteMask = 0U;
+ device.SetBlendState (device.CreateBlendState(blendstate), 0);
+
+ GfxRasterState rasterstate;
+ rasterstate.cullMode = kCullOff;
+ device.SetRasterState (device.CreateRasterState(rasterstate));
+
+ GfxDepthState depthstate;
+ depthstate.depthWrite = false;
+ depthstate.depthFunc = kFuncLEqual;
+ device.SetDepthState (device.CreateDepthState(depthstate));
+
+ GfxStencilState lightStencil;
+ lightStencil.stencilEnable = true;
+ lightStencil.readMask = 0xFFU;
+ lightStencil.writeMask = kStencilMaskLightBackface;
+ lightStencil.stencilZFailOpBack = kStencilOpInvert;
+ lightStencil.stencilZFailOpFront = kStencilOpInvert;
+ lightStencil.stencilPassOpBack = kStencilOpKeep;
+ lightStencil.stencilPassOpFront = kStencilOpKeep;
+ lightStencil.stencilFuncBack = (lightmask != 0 ) ? kFuncNotEqual : kFuncAlways;
+ lightStencil.stencilFuncFront = (lightmask != 0 ) ? kFuncNotEqual : kFuncAlways;
+ device.SetStencilState (device.CreateStencilState(lightStencil), lightmask|kStencilMaskSomething|kStencilMaskNonLightmapped);
+
+ #if UNITY_XENON
+ // Clear within light-geom, sets all HiS to cull.
+ // Set to cull where equal to background (to deal with lightmasks), unoptimal but works
+ if (useStencilMask)
+ device.SetHiStencilState (false, true, kStencilMaskSomething|kStencilMaskNonLightmapped, kFuncEqual);
+ #endif
+
+ RenderLightGeom (ctx, light, lightPos, lightMatrix, renderAsQuad);
+
+ blendstate.renderTargetWriteMask = KColorWriteAll;
+ device.SetBlendState (device.CreateBlendState(blendstate), 0);
+
+ #if UNITY_XENON
+ device.HiStencilFlush (kHiSflush_sync);
+ #endif
+ }
+
+ for (int pp = 0; pp < lightPassCount; ++pp)
+ {
+ Vector4f lightingFade = lightFade;
+ Vector4f shadowFade = lightFade;
+ shadowFade.x = 1.0f - l.GetShadowStrength();
+ if (pp == 0 || lightmappingMode == Light::kLightmappingRealtimeOnly)
+ lightingFade.z = lightingFade.w = 0.0f;
+ else
+ shadowFade.z = shadowFade.w = 0.0f;
+ params.SetVectorParam(kShaderVecLightmapFade, lightingFade);
+ params.SetVectorParam(kShaderVecLightShadowData, shadowFade);
+
+ // Disable mipmapping on light cookies
+ ShaderLab::TexEnv* cookieEnv = s_LightMaterial->GetProperties().GetTexEnv(kSLPropLightTexture0);
+ if (cookieEnv)
+ {
+ cookieEnv->TextureMipBiasChanged (-8);
+ }
+
+ #if SEPERATE_PREPASS_SPECULAR
+ if (s_LightMaterial->GetPassCount () > 2 && ctx.m_Camera->GetUsingHDR() && specularPass)
+ s_LightMaterial->SetPass (2);
+ else
+ #endif
+ if (s_LightMaterial->GetPassCount () > 1 && ctx.m_Camera->GetUsingHDR())
+ s_LightMaterial->SetPass (1);
+ else
+ s_LightMaterial->SetPass (0);
+
+ // Construct stencil read mask
+ GfxStencilState stencil;
+ stencil.stencilEnable = true;
+ stencil.stencilFuncFront = stencil.stencilFuncBack = kFuncEqual;
+ stencil.readMask = kStencilMaskSomething;
+ // Check lightmapped vs. non-lightmapped unless it's a realtime light that
+ // only cares about not illuminating non-something.
+ if (lightmappingMode != Light::kLightmappingRealtimeOnly)
+ stencil.readMask |= kStencilMaskNonLightmapped;
+
+ if (pp != 0 && lightmappingMode != Light::kLightmappingRealtimeOnly)
+ stencil.readMask |= kStencilMaskBeyondShadowDistace;
+
+ stencil.readMask |= lightmask;
+ int stencilRef = kStencilMaskSomething + lightPassAddBits[pp];
+
+ if (useStencilMask)
+ {
+ // Clear stencil while rendering
+ stencil.writeMask = kStencilMaskLightBackface;
+ stencil.stencilZFailOpBack = kStencilOpZero;
+ stencil.stencilZFailOpFront = kStencilOpZero;
+ stencil.stencilPassOpBack = kStencilOpZero;
+ stencil.stencilPassOpFront = kStencilOpZero;
+ // Clear the kStencilMaskLightBackface bit even if rejecting pixel due to stencil layer mask
+ stencil.stencilFailOpBack = kStencilOpZero;
+ stencil.stencilFailOpFront = kStencilOpZero;
+
+ stencil.readMask |= kStencilMaskLightBackface;
+ stencilRef |= kStencilMaskLightBackface;
+ }
+
+ DeviceStencilState* devStCheck = device.CreateStencilState (stencil);
+ device.SetStencilState (devStCheck, stencilRef);
+
+ #if UNITY_XENON
+ // Set to cull when all == background (to deal with lightmasks), unoptimal but works
+ if (useStencilMask)
+ device.SetHiStencilState (true, true, kStencilMaskSomething|kStencilMaskNonLightmapped, kFuncEqual);
+ #endif
+
+ // Draw light shape
+ GfxRasterState rasterstate;
+ GfxDepthState depthstate;
+ depthstate.depthWrite = false;
+ if (light.intersectsNear && !light.intersectsFar && (l.GetType() == kLightSpot || l.GetType() == kLightPoint))
+ {
+ // When near (but not far) plane intersects the light, render back faces (tighter than rendering a bounding quad).
+ // Can't use this when not intersecting, since it would waste processing for objects between
+ // the light and the cam, even when they don't touch the light.
+ rasterstate.cullMode = kCullFront;
+ depthstate.depthFunc = kFuncGreater;
+ }
+ else
+ {
+ depthstate.depthFunc = kFuncLEqual;
+ #if UNITY_XENON
+ device.SetHiZEnable (kHiZEnable);
+ #endif
+ }
+ device.SetRasterState (device.CreateRasterState (rasterstate));
+ device.SetDepthState (device.CreateDepthState (depthstate));
+
+ RenderLightGeom (ctx, light, lightPos, lightMatrix, renderAsQuad);
+
+ #if UNITY_XENON
+ device.SetHiZEnable (kHiZAuto);
+ if (useStencilMask)
+ device.HiStencilFlush (kHiSflush_async);
+ #endif
+ }
+
+ if (shadowMap && !returnShadowMap)
+ GetRenderBufferManager().ReleaseTempBuffer (shadowMap);
+
+ return returnShadowMap ? shadowMap : NULL;
+}
+
+
+void PrePassRenderLoop::RenderLighting (
+ ActiveLights& activeLights,
+ RenderTexture* rtMain,
+ TextureID depthTextureID,
+ RenderTexture* rtNormalsSpec,
+ RenderTexture*& rtLight,
+
+#if SEPERATE_PREPASS_SPECULAR
+ RenderTexture*& rtLightSpec,
+#endif
+ const Vector4f& lightFade,
+ const LightingLayers& lightingLayers,
+ MinMaxAABB& receiverBounds,
+ RenderTexture** outMainShadowMap)
+{
+ PROFILER_AUTO_GFX(gPrepassLighting, m_Context->m_Camera);
+ GPU_AUTO_SECTION(kGPUSectionDeferedLighting);
+ *outMainShadowMap = NULL;
+
+ Assert(rtLight == NULL);
+#if SEPERATE_PREPASS_SPECULAR
+ Assert(rtLightSpec == NULL);
+#endif
+ const QualitySettings::ShadowQuality shadowQuality = static_cast<QualitySettings::ShadowQuality>(GetQualitySettings().GetCurrent().shadows);
+ const LightmapSettings::LightmapsMode lightmapsMode = static_cast<LightmapSettings::LightmapsMode>(GetLightmapSettings().GetLightmapsMode());
+
+ ShadowCameraData camData(*m_Context->m_ShadowCullData);
+
+ // Prevent receiver bounds to be zero size in any dimension;
+ // causes trouble with calculating intersection of frustum and bounds.
+ receiverBounds.Expand( 0.01f );
+
+ const Rectf screenRect = m_Context->m_Camera->GetScreenViewportRect();
+
+ if (!s_LightMaterial) {
+ Shader* shader = GetScriptMapper().FindShader ("Hidden/Internal-PrePassLighting");
+ s_LightMaterial = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+ }
+
+ if (s_Icosahedron.IsNull ())
+ s_Icosahedron = GetBuiltinResource<Mesh> ("icosahedron.fbx");
+ if (s_Icosphere.IsNull ())
+ s_Icosphere = GetBuiltinResource<Mesh> ("icosphere.fbx");
+ if (s_Pyramid.IsNull ())
+ s_Pyramid = GetBuiltinResource<Mesh> ("pyramid.fbx");
+
+ static SHADERPROP (CameraDepthTexture);
+ static SHADERPROP (CameraNormalsTexture);
+ const int width = rtNormalsSpec->GetGLWidth();
+ const int height = rtNormalsSpec->GetGLHeight();
+ if (gGraphicsCaps.hasStencilInDepthTexture)
+ {
+ ShaderLab::g_GlobalProperties->SetRectTextureID (
+ kSLPropCameraDepthTexture,
+ depthTextureID,
+ width,
+ height,
+ rtMain->GetTexelSizeX(),
+ rtMain->GetTexelSizeY(),
+ rtMain->GetUVScaleX(),
+ rtMain->GetUVScaleY()
+ );
+ }
+
+ // set as _CameraNormalsTexture for external access
+ ShaderLab::g_GlobalProperties->SetTexture (kSLPropCameraNormalsTexture, rtNormalsSpec);
+
+ GfxDevice& device = GetGfxDevice();
+
+ SetAndRestoreWireframeMode setWireframeOff(false); // turn off wireframe; will restore old value in destructor
+ device.SetNormalizationBackface( kNormalizationDisabled, false );
+
+ DeviceStencilState* devStDisabled = device.CreateStencilState (GfxStencilState());
+
+
+ DeviceMVPMatricesState preserveMVP;
+
+ device.SetWorldMatrix (Matrix4x4f::identity.GetPtr());
+
+ RenderTexture** currentLightTex = &rtLight;
+#if SEPERATE_PREPASS_SPECULAR
+ //Do 2 passes for HDR prepass lighting on xenon
+ for (int lp = 0; lp < (m_Context->m_Camera->GetUsingHDR() ? 2 : 1); ++lp)
+ {
+ if (lp == 0)
+ currentLightTex = &rtLight;
+ else
+ currentLightTex = &rtLightSpec;
+#endif
+
+ const ActiveLight* mainActiveLight = GetMainActiveLight(activeLights);
+ ActiveLights::Array::iterator it, itEnd = activeLights.lights.end();
+ for (it = activeLights.lights.begin(); it != itEnd; ++it)
+ {
+ if (!it->isVisibleInPrepass)
+ continue;
+ if (&*it == mainActiveLight)
+ {
+ // skip main light now; will render it last
+ continue;
+ }
+ RenderLight (*m_Context, camData, shadowQuality, lightmapsMode,
+ *currentLightTex,
+ rtMain,
+ width, height, devStDisabled, receiverBounds,
+ preserveMVP, lightFade, lightingLayers, *it,
+#if SEPERATE_PREPASS_SPECULAR
+ lp == 1,
+#endif
+ false);
+ }
+
+ #if UNITY_XENON
+ device.SetStencilState (devStDisabled, 0);
+ device.SetHiStencilState (false, false, 0, kFuncEqual);
+ #endif
+
+ // render main light
+ if (mainActiveLight)
+ {
+ RenderTexture* shadowMap = RenderLight (
+ *m_Context, camData, shadowQuality, lightmapsMode,
+ *currentLightTex,
+ rtMain,
+ width, height, devStDisabled, receiverBounds,
+ preserveMVP, lightFade, lightingLayers, *mainActiveLight,
+#if SEPERATE_PREPASS_SPECULAR
+ lp == 1,
+#endif
+ true);
+ if (shadowMap)
+ {
+ AddRenderLoopTempBuffer (m_Context->m_RenderLoop, shadowMap);
+ *outMainShadowMap = shadowMap;
+ }
+ }
+#if SEPERATE_PREPASS_SPECULAR
+ }
+#endif
+ SetNoShadowsKeywords ();
+
+ Vector4f lightmapFade = lightFade;
+ // if we're not in dual lightmaps mode, always use the far lightmap, i.e. lightmapFade = 1
+ if (GetLightmapSettings().GetLightmapsMode() != LightmapSettings::kDualLightmapsMode)
+ lightmapFade.z = lightmapFade.w = 1.0f;
+
+ device.GetBuiltinParamValues().SetVectorParam(kShaderVecLightmapFade, lightmapFade);
+
+ device.SetStencilState (devStDisabled, 0);
+
+ #if !UNITY_XENON
+ // Ok, we didn't really have any lights worth rendering.
+ // Create a small render texture and clear it to white and pass it as the lighting buffer.
+ // Don't do that on 360; pointless and saves a resolve.
+ if (!rtLight)
+ {
+ rtLight = GetRenderBufferManager().GetTempBuffer (16, 16, kDepthFormatNone, kRTFormatARGB32, 0, kRTReadWriteLinear);
+ RenderTexture::SetActive (rtLight);
+ float white[] = {1,1,1,1};
+ float black[] = {0,0,0,0};
+ GraphicsHelper::Clear (kGfxClearColor, m_Context->m_Camera->GetUsingHDR() ? black : white, 1.0f, 0);
+ GPU_TIMESTAMP();
+
+ // We just switched away from a Z buffer (only in case when no lights were there!),
+ // and we'll switch back to it. So ignore the unresolve warning on it.
+ device.IgnoreNextUnresolveOnRS(rtMain->GetDepthSurfaceHandle());
+ }
+ #endif
+}
+
+
+static RenderTexture* CombineDepthNormalsTexture (const RenderLoopContext& ctx, RenderObjectDataContainer& remainingObjects)
+{
+ PROFILER_AUTO_GFX(gPrepassCombineDepthNormals, ctx.m_Camera);
+
+ static Material* s_CombineMaterial = NULL;
+ if (!s_CombineMaterial)
+ {
+ Shader* shader = GetScriptMapper ().FindShader ("Hidden/Internal-CombineDepthNormals");
+ if (shader)
+ s_CombineMaterial = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+ if (!s_CombineMaterial) {
+ AssertString ("Coult not find depth+normals combine shader");
+ return NULL;
+ }
+ }
+
+ RenderTexture* depthNormals = GetRenderBufferManager().GetTempBuffer (RenderBufferManager::kFullSize, RenderBufferManager::kFullSize, kDepthFormatNone, kRTFormatARGB32, 0, kRTReadWriteLinear);
+ RenderTexture::SetActive (depthNormals);
+ GraphicsHelper::Clear (kGfxClearColor, ColorRGBAf(0.5f,0.5f,1.0f,1.0f).GetPtr(), 1.0f, 0);
+ GPU_TIMESTAMP();
+
+ // Combine depth & normals into single texture
+ ImageFilters::Blit (NULL, depthNormals, s_CombineMaterial, 0, false);
+
+ AddRenderLoopTempBuffer (ctx.m_RenderLoop, depthNormals);
+
+ static SHADERPROP (CameraDepthNormalsTexture);
+ ShaderLab::g_GlobalProperties->SetTexture (kSLPropCameraDepthNormalsTexture, depthNormals);
+
+ return depthNormals;
+}
+
+
+
+// Separate pass to render depth into a separate target. Only used on Macs with Radeon HDs, since
+// only there doing it the regular way is broken.
+#if GFX_SUPPORTS_OPENGL
+static RenderTexture* RenderBasePassDepth (const RenderLoopContext& ctx, RenderObjectDataContainer& renderData, PreRenderPasses& plainRenderPasses)
+{
+ GPU_AUTO_SECTION(kGPUSectionDeferedPrePass);
+
+ GfxDevice& device = GetGfxDevice();
+
+ RenderTexture* rt = GetRenderBufferManager().GetTempBuffer (RenderBufferManager::kFullSize, RenderBufferManager::kFullSize, kDepthFormat24, kRTFormatDepth, 0, kRTReadWriteLinear);
+ rt->SetFilterMode (kTexFilterNearest);
+ if (!rt->IsCreated())
+ rt->Create();
+ RenderTexture::SetActive (rt);
+ AddRenderLoopTempBuffer (ctx.m_RenderLoop, rt);
+
+ float black[] = {0,0,0,0};
+ GraphicsHelper::Clear (kGfxClearAll, black, 1.0f, 0);
+ GPU_TIMESTAMP();
+
+ device.SetViewMatrix (ctx.m_CurCameraMatrix.GetPtr());
+
+ size_t ndata = renderData.size();
+
+ for( size_t i = 0; i < ndata; ++i )
+ {
+ const PrePassRenderData& rpData = plainRenderPasses[i];
+ const RenderObjectData& roData = renderData[rpData.roIndex];
+ Shader* shader = roData.shader;
+ int ss = shader->GetShaderLabShader()->GetDefaultSubshaderIndex(kRenderPathExtPrePass);
+ if (ss == -1)
+ continue;
+
+ const VisibleNode *node = roData.visibleNode;
+
+ SetObjectScale (device, node->lodFade, node->invScale);
+
+ //@TODO: if this returns true and we have any sort of batching, we'd have to break batches here
+ node->renderer->ApplyCustomProperties(*roData.material, shader, ss);
+
+ ShaderLab::SubShader& subshader = roData.shader->GetShaderLabShader()->GetSubShader (ss);
+ int shaderPassCount = subshader.GetValidPassCount();
+ for (int p = 0; p < shaderPassCount; ++p)
+ {
+ ShaderPassType passType;
+ UInt32 passRenderOptions;
+ subshader.GetPass(p)->GetPassOptions (passType, passRenderOptions);
+ if (passType != kPassLightPrePassBase)
+ continue;
+
+ const ChannelAssigns* channels = roData.material->SetPassWithShader(p, shader, ss);
+ if (channels)
+ {
+ SetupObjectMatrix (node->worldMatrix, node->transformType);
+ node->renderer->Render( roData.subsetIndex, *channels );
+ }
+ }
+ }
+
+ return rt;
+}
+#endif
+
+inline float MultiplyAbsVectorZ (const Matrix4x4f& m, const Vector3f& v)
+{
+ return Abs(m.m_Data[2]) * v.x + Abs(m.m_Data[6]) * v.y + Abs(m.m_Data[10]) * v.z;
+}
+
+
+RenderTexture* PrePassRenderLoop::RenderBasePass (
+ RenderTexture* rtMain,
+ const LightingLayers& lightingLayers,
+ RenderObjectDataContainer& outRemainingObjects,
+ MinMaxAABB& receiverBounds
+ )
+{
+ PROFILER_AUTO_GFX(gPrepassGeom, m_Context->m_Camera);
+ GPU_AUTO_SECTION(kGPUSectionDeferedPrePass);
+
+ const float shadowDistance = m_Context->m_ShadowCullData->shadowDistance;
+
+ GfxDevice& device = GetGfxDevice();
+ device.SetNormalizationBackface( kNormalizationDisabled, false );
+
+ GfxStencilState stRender;
+ stRender.writeMask = kStencilGeomWriteMask;
+ stRender.stencilEnable = true;
+ stRender.stencilPassOpFront = stRender.stencilPassOpBack = kStencilOpReplace;
+ DeviceStencilState* devStRender = device.CreateStencilState (stRender);
+
+ RenderTexture* rtNormalsSpec = GetRenderBufferManager().GetTempBuffer (RenderBufferManager::kFullSize, RenderBufferManager::kFullSize, kDepthFormatNone, kRTFormatARGB32, 0, kRTReadWriteLinear);
+ rtNormalsSpec->SetFilterMode (kTexFilterNearest);
+ if (!rtNormalsSpec->IsCreated())
+ rtNormalsSpec->Create();
+ RenderSurfaceHandle rtSurfaceColor = rtNormalsSpec->GetColorSurfaceHandle();
+ RenderSurfaceHandle rtSurfaceDepth = rtMain->GetDepthSurfaceHandle(); // reuse depth buffer from final pass
+
+ UInt32 rtFlags = RenderTexture::kFlagDontRestoreColor;
+ UInt32 gfxClearFlags = kGfxClearAll;
+ // do not clear depth/stencil if camera set to DontClear
+ if (m_Context->m_Camera->GetClearFlags() == Camera::kDontClear)
+ {
+ gfxClearFlags &= ~kGfxClearDepthStencil;
+ }
+ else
+ {
+ rtFlags |= RenderTexture::kFlagDontRestoreDepth;
+ }
+
+ // set base pass render texture
+ RenderTexture::SetActive (1, &rtSurfaceColor, rtSurfaceDepth, rtNormalsSpec, 0, kCubeFaceUnknown, rtFlags);
+
+ AddRenderLoopTempBuffer (m_Context->m_RenderLoop, rtNormalsSpec);
+
+ float black[] = {0,0,0,0};
+ GraphicsHelper::Clear (gfxClearFlags, black, 1.0f, 0);
+ GPU_TIMESTAMP();
+
+ device.SetViewMatrix (m_Context->m_CurCameraMatrix.GetPtr());
+
+ const ChannelAssigns* channels = NULL;
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+
+ int prevTransformType = -1;
+ Material* prevMaterial = 0;
+ Shader* prevShader = 0;
+ int prevSubshaderIndex = -1;
+ float prevInvScale = 0.0f;
+ float prevLodFade = 0.0f;
+ UInt32 prevCustomPropsHash = 0;
+ int prevPassIndex = -1;
+ int prevStencilRef = 0;
+
+ int canBatch = 0;
+
+ #endif
+
+ const bool directLightBakedInLightProbes = LightProbes::AreBaked() && GetLightmapSettings().GetLightmapsMode() != LightmapSettings::kDualLightmapsMode;
+
+ size_t ndata = m_Objects->size();
+ outRemainingObjects.reserve (ndata / 16);
+
+ for( size_t i = 0; i < ndata; ++i )
+ {
+ const PrePassRenderData& rpData = m_PlainRenderPasses[i];
+ const RenderObjectData& roData = (*m_Objects)[rpData.roIndex];
+ Shader* shader = roData.shader;
+
+ int ss = roData.subShaderIndex;
+ if (ss == -1)
+ ss = shader->GetShaderLabShader()->GetDefaultSubshaderIndex(kRenderPathExtPrePass);
+
+ const VisibleNode *node = roData.visibleNode;
+
+ bool withinShadowDistance = true;
+ float distanceAlongView = roData.distanceAlongView;
+ if (distanceAlongView > shadowDistance)
+ {
+ // check whether its bounds is actually further than shadow distance
+ // project extents onto camera forward axis
+ float z = MultiplyAbsVectorZ (m_Context->m_CurCameraMatrix, node->worldAABB.GetExtent());
+ Assert(z >= 0.0f);
+ if (distanceAlongView - z > shadowDistance)
+ withinShadowDistance = false;
+ }
+
+ if (ss == -1)
+ {
+ if (withinShadowDistance && node->renderer->GetReceiveShadows())
+ receiverBounds.Encapsulate (node->worldAABB);
+ outRemainingObjects.push_back() = roData;
+ continue;
+ }
+
+
+ const float invScale = node->invScale;
+ const float lodFade = node->lodFade;
+ const int transformType = node->transformType;
+ const UInt32 customPropsHash = node->renderer->GetCustomPropertiesHash();
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+
+ if (
+ node->renderer->GetStaticBatchIndex() == 0 ||
+ prevTransformType != transformType ||
+ prevMaterial != roData.material ||
+ prevShader != shader ||
+ prevSubshaderIndex != ss ||
+ !CompareApproximately(prevInvScale,invScale) ||
+ !CompareApproximately(prevLodFade,lodFade, LOD_FADE_BATCH_EPSILON) ||
+ prevCustomPropsHash != customPropsHash)
+ {
+ m_BatchRenderer.Flush();
+
+ prevTransformType = transformType;
+ prevMaterial = roData.material;
+ prevShader = shader;
+ prevSubshaderIndex = ss;
+ prevInvScale = invScale;
+ prevLodFade = lodFade;
+ prevCustomPropsHash = customPropsHash;
+
+ canBatch = 0;
+ }
+ else
+ ++canBatch;
+
+ #endif
+
+ SetObjectScale (device, lodFade, invScale);
+
+ node->renderer->ApplyCustomProperties(*roData.material, shader, ss);
+
+ const bool lightmapped = node->renderer->IsLightmappedForRendering();
+ const Renderer* renderer = static_cast<Renderer*>(node->renderer);
+ const bool directLightFromLightProbes = directLightBakedInLightProbes && node->renderer->GetRendererType() != kRendererIntermediate && renderer->GetUseLightProbes();
+
+ ShaderLab::SubShader& subshader = shader->GetShaderLabShader()->GetSubShader (ss);
+ int shaderPassCount = subshader.GetValidPassCount();
+ for (int p = 0; p < shaderPassCount; ++p)
+ {
+ ShaderPassType passType;
+ UInt32 passRenderOptions;
+ subshader.GetPass(p)->GetPassOptions (passType, passRenderOptions);
+ if (passType != kPassLightPrePassBase)
+ continue;
+
+ int stencilRef = kStencilMaskSomething;
+ if (!lightmapped && !directLightFromLightProbes)
+ {
+ stencilRef += kStencilMaskNonLightmapped;
+ }
+
+ if (!withinShadowDistance)
+ stencilRef += kStencilMaskBeyondShadowDistace;
+
+ int layerStencilBit = lightingLayers.layerToStencil[node->renderer->GetLayer()];
+ if (layerStencilBit != -1)
+ stencilRef |= 1<<layerStencilBit;
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+
+ if ((p != prevPassIndex) ||
+ (stencilRef != prevStencilRef))
+ {
+ m_BatchRenderer.Flush();
+ prevPassIndex = p;
+ prevStencilRef = stencilRef;
+ canBatch = 0;
+ }
+
+ if (canBatch <= 1)
+ #endif
+ {
+ device.SetStencilState (devStRender, stencilRef);
+ channels = roData.material->SetPassWithShader(p, shader, ss);
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ prevPassIndex = p;
+ prevStencilRef = stencilRef;
+ #endif
+ }
+
+ receiverBounds.Encapsulate (node->worldAABB);
+
+ if (channels)
+ {
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ m_BatchRenderer.Add (node->renderer, roData.subsetIndex, channels, node->worldMatrix, transformType);
+ #else
+ SetupObjectMatrix (node->worldMatrix, transformType);
+ node->renderer->Render (roData.subsetIndex, *channels);
+ #endif
+ }
+ }
+ }
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ m_BatchRenderer.Flush();
+ #endif
+
+ return rtNormalsSpec;
+}
+
+void PrePassRenderLoop::RenderFinalPass (RenderTexture* rtMain,
+ RenderTexture* rtLight,
+#if SEPERATE_PREPASS_SPECULAR
+ RenderTexture* rtLightSpec,
+#endif
+ bool hdr,
+ bool linearLighting)
+{
+ PROFILER_AUTO_GFX(gPrepassFinal, m_Context->m_Camera);
+ GPU_AUTO_SECTION(kGPUSectionOpaquePass);
+
+ GfxDevice& device = GetGfxDevice();
+ device.SetNormalizationBackface( kNormalizationDisabled, false );
+
+ RenderTexture::SetActive (rtMain);
+
+ // Clear with background. Do not clear depth since we need the already
+ // filled one from the base pass.
+ device.SetSRGBWrite(!hdr && linearLighting && (!rtMain || rtMain->GetSRGBReadWrite()) );
+ m_Context->m_Camera->ClearNoSkybox(true);
+
+ if(rtLight)
+ rtLight->SetGlobalProperty (kSLPropLightBuffer);
+ else
+ {
+ ShaderLab::TexEnv *te = ShaderLab::g_GlobalProperties->SetTexture (kSLPropLightBuffer, hdr ? builtintex::GetBlackTexture() : builtintex::GetWhiteTexture());
+ te->ClearMatrix();
+ }
+
+#if SEPERATE_PREPASS_SPECULAR
+ if(rtLightSpec)
+ rtLightSpec->SetGlobalProperty (kSLPropLightSpecBuffer);
+ else
+ {
+ ShaderLab::TexEnv *te = ShaderLab::g_GlobalProperties->SetTexture (kSLPropLightSpecBuffer, hdr ? builtintex::GetBlackTexture() : builtintex::GetWhiteTexture());
+ te->ClearMatrix();
+ }
+#endif
+
+ const ChannelAssigns* channels = NULL;
+ const LightmapSettings& lightmapper = GetLightmapSettings();
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+
+ int prevPassIndex = -1;
+
+ int prevLightmapIndex = -1;
+ Vector4f prevLightmapST (0,0,0,0);
+ int prevTransformType = -1;
+ Material* prevMaterial = 0;
+ Shader* prevShader = 0;
+ int prevSubshaderIndex = -1;
+ float prevInvScale = 0.0f;
+ float prevLodFade = 0.0f;
+ UInt32 prevCustomPropsHash = 0;
+
+ int canBatch = 0;
+
+ #endif
+
+ if (hdr)
+ g_ShaderKeywords.Enable (kKeywordHDRLightPrepassOn);
+ else
+ g_ShaderKeywords.Disable (kKeywordHDRLightPrepassOn);
+
+ LightProbes* lightProbes = GetLightProbes();
+ const bool areLightProbesBaked = LightProbes::AreBaked();
+ BuiltinShaderParamValues& builtinParamValues = GetGfxDevice().GetBuiltinParamValues();
+ Vector3f ambientSH;
+ SHEvalAmbientLight(GetRenderSettings().GetAmbientLightInActiveColorSpace(), &ambientSH[0]);
+
+ size_t ndata = m_Objects->size();
+ for( size_t i = 0; i < ndata; ++i )
+ {
+ const PrePassRenderData& rpData = m_PlainRenderPasses[i];
+ const RenderObjectData& roData = (*m_Objects)[rpData.roIndex];
+
+ const VisibleNode *node = roData.visibleNode;
+ Shader* shader = roData.shader;
+
+ int ss = roData.subShaderIndex;
+ if (ss == -1)
+ ss = shader->GetShaderLabShader()->GetDefaultSubshaderIndex(kRenderPathExtPrePass);
+ if (ss == -1)
+ continue;
+
+ const Vector4f lightmapST = node->renderer->GetLightmapSTForRendering();
+ const int lightmapIndex = roData.lightmapIndex;
+ DebugAssert(lightmapIndex == node->renderer->GetLightmapIndex());
+
+ const float invScale = node->invScale;
+ const float lodFade = node->lodFade;
+ const int transformType = node->transformType;
+ const UInt32 customPropsHash = node->renderer->GetCustomPropertiesHash();
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+
+ if (
+ node->renderer->GetStaticBatchIndex() == 0 ||
+ prevTransformType != transformType ||
+ prevMaterial != roData.material ||
+ prevShader != shader ||
+ prevSubshaderIndex != ss ||
+ prevLightmapIndex != lightmapIndex ||
+ !CompareMemory(prevLightmapST, lightmapST) ||
+ !CompareApproximately(prevInvScale,invScale) ||
+ !CompareApproximately(prevLodFade,lodFade) ||
+ prevCustomPropsHash != customPropsHash)
+ {
+ m_BatchRenderer.Flush();
+
+ prevLightmapIndex = lightmapIndex;
+ prevLightmapST = lightmapST;
+ prevTransformType = transformType;
+ prevMaterial = roData.material;
+ prevShader = shader;
+ prevSubshaderIndex = ss;
+ prevInvScale = invScale;
+ prevLodFade = lodFade;
+ prevCustomPropsHash = customPropsHash;
+
+ canBatch = 0;
+ }
+ else
+ ++canBatch;
+
+ #endif
+
+ SetObjectScale (device, lodFade, invScale);
+
+ node->renderer->ApplyCustomProperties(*roData.material, roData.shader, ss);
+
+ ShaderLab::SubShader& subshader = roData.shader->GetShaderLabShader()->GetSubShader (ss);
+ int shaderPassCount = subshader.GetValidPassCount();
+ for (int p = 0; p < shaderPassCount; ++p)
+ {
+ ShaderPassType passType;
+ UInt32 passRenderOptions;
+ subshader.GetPass(p)->GetPassOptions (passType, passRenderOptions);
+ if (passType != kPassLightPrePassFinal)
+ continue;
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ if (p != prevPassIndex)
+ {
+ m_BatchRenderer.Flush();
+ canBatch = 0;
+ }
+
+ if (canBatch <= 1)
+ #endif
+ {
+ // lightmap
+ SetupObjectLightmaps (lightmapper, lightmapIndex, lightmapST, false);
+
+ // light probes
+ // TODO: figure how does that interact with lightmaps and with batching;
+ // if we are about to use light probes and the renderer gets different coeffs (maybe a simpler check?) => can't batch
+ float lightProbeCoeffs[9][3];
+ memset (lightProbeCoeffs, 0, sizeof(lightProbeCoeffs));
+ if (areLightProbesBaked && node->renderer->GetRendererType() != kRendererIntermediate)
+ {
+ Renderer* renderer = static_cast<Renderer*>(node->renderer);
+ if (renderer && renderer->GetUseLightProbes())
+ lightProbes->GetInterpolatedLightProbe(renderer->GetLightProbeInterpolationPosition(node->worldAABB), renderer, &(lightProbeCoeffs[0][0]));
+ }
+ lightProbeCoeffs[0][0] += ambientSH[0];
+ lightProbeCoeffs[0][1] += ambientSH[1];
+ lightProbeCoeffs[0][2] += ambientSH[2];
+ SetSHConstants (lightProbeCoeffs, builtinParamValues);
+
+ // set pass
+ channels = roData.material->SetPassWithShader(p, shader, ss);
+ }
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ prevPassIndex = p;
+ #endif
+
+ if (channels)
+ {
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ m_BatchRenderer.Add (node->renderer, roData.subsetIndex, channels, node->worldMatrix, transformType);
+ #else
+ SetupObjectMatrix (node->worldMatrix, transformType);
+ node->renderer->Render (roData.subsetIndex, *channels);
+ #endif
+ }
+ }
+ }
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ m_BatchRenderer.Flush();
+ #endif
+
+ GetGfxDevice().SetSRGBWrite(false);
+}
+
+
+PrePassRenderLoop* CreatePrePassRenderLoop()
+{
+ return new PrePassRenderLoop();
+}
+
+void DeletePrePassRenderLoop (PrePassRenderLoop* queue)
+{
+ delete queue;
+}
+
+
+static UInt32 CalculateLightingLayers ()
+{
+ // TODO: Use active lights instead
+ const LightManager::Lights& lights = GetLightManager().GetAllLights();
+ LightManager::Lights::const_iterator it, itEnd = lights.end();
+ UInt32 layers = ~0;
+ for (it = lights.begin(); it != itEnd; ++it)
+ {
+ UInt32 mask = it->GetCullingMask();
+ if (mask == 0)
+ continue;
+ layers &= mask;
+ }
+ return ~layers;
+}
+
+
+#if UNITY_EDITOR
+static void CheckLightLayerUsage (const LightingLayers& layers)
+{
+ static bool s_UsageWasOK = true;
+ bool usageIsOK = (layers.lightLayerCount <= kLightingLayerCount);
+
+ // Only log/remove warning message when broken vs. okay has changed
+ if (usageIsOK == s_UsageWasOK)
+ return;
+
+ s_UsageWasOK = usageIsOK;
+
+ // Remove any previous error
+ // Use instanceID of QualitySettings as log identifier
+ RemoveErrorWithIdentifierFromConsole (GetQualitySettings().GetInstanceID());
+
+ if (!usageIsOK)
+ {
+ std::string msg = Format(
+ "Too many layers used to exclude objects from lighting. Up to %i layers can be used to exclude lights, while your lights use %i:",
+ kLightingLayerCount,
+ layers.lightLayerCount);
+ for (int i = 0; i < LightingLayers::kLayerCount; ++i)
+ {
+ if (layers.lightingLayerMask & (1<<i))
+ {
+ std::string layerName = LayerToString (i);
+ if (layerName.empty())
+ layerName = "Unnamed " + IntToString (i);
+ msg += " '" + layerName + "'";
+ }
+ }
+ // Use instanceID of QualitySettings as log identifier
+ DebugStringToFile (msg, 0, __FILE__, __LINE__, kScriptingWarning, 0, GetQualitySettings().GetInstanceID());
+ }
+}
+#endif
+
+static void ResolveDepthIntoTextureIfNeeded (
+ GfxDevice& device,
+ RenderLoop& renderLoop,
+ DepthBufferFormat depthFormat,
+ RenderTexture*& outDepthRT,
+ TextureID* outDepthTextureID,
+ bool* outDepthWasCopied)
+{
+ // TODO FIXME!! Should add GLES20 here as well, but it's missing GfxDevice::ResolveDepthIntoTexture!
+
+#if GFX_SUPPORTS_D3D9 || GFX_SUPPORTS_D3D11 || GFX_SUPPORTS_OPENGL || GFX_SUPPORTS_OPENGLES30
+ bool needsDepthResolve = false;
+#if GFX_SUPPORTS_D3D9
+ // If doing depth tests & sampling as INTZ is very slow,
+ // do a depth resolve into a separate texture first.
+ needsDepthResolve |= (device.GetRenderer() == kGfxRendererD3D9 && gGraphicsCaps.hasStencilInDepthTexture && gGraphicsCaps.d3d.hasDepthResolveRESZ && gGraphicsCaps.d3d.slowINTZSampling);
+#endif
+#if GFX_SUPPORTS_D3D11
+ // Always needs resolve on D3D11.
+ needsDepthResolve |= (device.GetRenderer() == kGfxRendererD3D11);
+#endif
+#if GFX_SUPPORTS_OPENGL
+ // Needs resolve on OpenGL, unless we did the slow RenderBasePassDepth().
+ // TODO: get rid of buggyPackedDepthStencil
+ needsDepthResolve |= (device.GetRenderer() == kGfxRendererOpenGL) && !gGraphicsCaps.gl.buggyPackedDepthStencil;
+#endif
+#if GFX_SUPPORTS_OPENGLES30
+ // Always needs resolve on GLES30.
+ needsDepthResolve |= (device.GetRenderer() == kGfxRendererOpenGLES30);
+#endif
+
+ if (needsDepthResolve)
+ {
+ DebugAssert (depthFormat != kDepthFormatNone);
+ RenderTexture* depthCopy = GetRenderBufferManager().GetTempBuffer (RenderBufferManager::kFullSize, RenderBufferManager::kFullSize, depthFormat, kRTFormatDepth, RenderBufferManager::kRBSampleOnlyDepth, kRTReadWriteLinear);
+ depthCopy->SetFilterMode (kTexFilterNearest);
+ if (!depthCopy->IsCreated())
+ depthCopy->Create();
+ AddRenderLoopTempBuffer (&renderLoop, depthCopy);
+
+ device.ResolveDepthIntoTexture (depthCopy->GetColorSurfaceHandle (), depthCopy->GetDepthSurfaceHandle ());
+
+ outDepthRT = depthCopy;
+ *outDepthTextureID = depthCopy->GetTextureID ();
+ *outDepthWasCopied = true;
+ }
+
+#endif
+}
+
+#if ENABLE_PRE_PASS_LOOP_HASH_SORTING
+template<typename T>
+static UInt8* InstertToHashBufferPreLoop(const T* p, UInt8* buffer)
+{
+ Assert((sizeof(T) % 4) == 0); // unaligned write
+ *reinterpret_cast<T*>(buffer) = *p;
+ return buffer + sizeof(T);
+ }
+#endif
+
+void DoPrePassRenderLoop (
+ RenderLoopContext& ctx,
+ RenderObjectDataContainer& objects,
+ RenderObjectDataContainer& outRemainingObjects,
+ RenderTexture*& outDepthRT,
+ RenderTexture*& outDepthNormalsRT,
+ RenderTexture*& outMainShadowMap,
+ ActiveLights& activeLights,
+ bool linearLighting,
+ bool* outDepthWasCopied)
+{
+ outDepthRT = NULL;
+ outDepthNormalsRT = NULL;
+ *outDepthWasCopied = false;
+
+ // Allocated on the stack each time, uses temp allocators
+ PrePassRenderLoop loop;
+ loop.m_Context = &ctx;
+ loop.m_Objects = &objects;
+
+ loop.m_PlainRenderPasses.resize_uninitialized(0);
+
+ RenderObjectDataContainer::iterator itEnd = objects.end();
+ size_t roIndex = 0;
+ for (RenderObjectDataContainer::iterator it = objects.begin(); it != itEnd; ++it, ++roIndex)
+ {
+ RenderObjectData& odata = *it;
+ const VisibleNode *node = odata.visibleNode;
+ BaseRenderer* renderer = node->renderer;
+
+ PrePassRenderData rpData;
+ rpData.roIndex = roIndex;
+
+#if ENABLE_PRE_PASS_LOOP_HASH_SORTING
+
+ //hash state information for render object sorter
+ const int kHashBufferSize = 64;
+ UInt8 hashBuffer[kHashBufferSize];
+ UInt8* hashPtr = hashBuffer;
+
+ // Always write 32b granularity into the hash buffer to avoid unaligned writes
+ UInt32 transformType = static_cast<UInt32>(renderer->GetTransformInfo().transformType);
+ hashPtr = InstertToHashBufferPreLoop(&transformType, hashPtr);
+ hashPtr = InstertToHashBufferPreLoop(&node->invScale, hashPtr);
+ hashPtr = InstertToHashBufferPreLoop(&node->lodFade, hashPtr);
+ int materialID = odata.material->GetInstanceID();
+ hashPtr = InstertToHashBufferPreLoop(&materialID, hashPtr);
+ int shaderID = odata.shader->GetInstanceID();
+ hashPtr = InstertToHashBufferPreLoop(&shaderID, hashPtr);
+ int ss = odata.shader->GetShaderLabShader()->GetDefaultSubshaderIndex(kRenderPathExtPrePass);
+ hashPtr = InstertToHashBufferPreLoop(&ss, hashPtr);
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ hashPtr = InstertToHashBufferPreLoop(&odata.staticBatchIndex, hashPtr);
+ #endif
+ Assert(hashPtr-hashBuffer <= kHashBufferSize);
+
+ rpData.hash = MurmurHash2A(hashBuffer, hashPtr-hashBuffer, 0x9747b28c);
+#endif
+ loop.m_PlainRenderPasses.push_back( rpData );
+ }
+
+ // Sort objects
+ {
+ PROFILER_AUTO(gPrepassSort, ctx.m_Camera);
+#if ENABLE_PRE_PASS_LOOP_HASH_SORTING
+ loop.SortPreRenderPassData(loop.m_PlainRenderPasses);
+#else
+ std::sort (objects.begin(), objects.end(), RenderPrePassObjectSorter());
+#endif
+ }
+
+ // Setup shadow distance, fade and ambient parameters
+ BuiltinShaderParamValues& params = GetGfxDevice().GetBuiltinParamValues();
+ Vector4f lightFade;
+ Vector4f fadeCenterAndType;
+ CalculateLightShadowFade (*ctx.m_Camera, 1.0f, lightFade, fadeCenterAndType);
+ params.SetVectorParam(kShaderVecLightmapFade, lightFade);
+ params.SetVectorParam(kShaderVecShadowFadeCenterAndType, fadeCenterAndType);
+ params.SetVectorParam(kShaderVecUnityAmbient, Vector4f(GetRenderSettings().GetAmbientLightInActiveColorSpace().GetPtr()));
+
+ GfxDevice& device = GetGfxDevice();
+
+ // Prepare for rendering
+ RenderTexture* rtMain = ctx.m_Camera->GetCurrentTargetTexture ();
+ Assert (rtMain);
+ if (!rtMain->IsCreated())
+ rtMain->Create();
+
+ LightingLayers lightingLayers (CalculateLightingLayers ());
+ #if UNITY_EDITOR
+ CheckLightLayerUsage (lightingLayers);
+ #endif
+
+ // Don't allow shaders to set their own stencil state from base pass until
+ // the end of light pass, since it would screw them up.
+ ShaderLab::g_GlobalAllowShaderStencil = false;
+
+ // Render Geometry base pass
+ MinMaxAABB receiverBounds;
+ RenderTexture* rtNormalsSpec = loop.RenderBasePass (rtMain, lightingLayers, outRemainingObjects, receiverBounds);
+ outDepthRT = rtNormalsSpec;
+
+ RenderSurfaceHandle colorSurfaceHandle = rtNormalsSpec->GetColorSurfaceHandle();
+ RenderSurfaceHandle depthTextureHandle = rtMain->GetDepthSurfaceHandle();
+ TextureID depthTextureID = rtMain->GetSecondaryTextureID();
+ DepthBufferFormat depthFormat = rtMain->GetDepthFormat();
+
+ #if GFX_SUPPORTS_OPENGL
+ if (device.GetRenderer() == kGfxRendererOpenGL && gGraphicsCaps.gl.buggyPackedDepthStencil)
+ {
+ // Separate pass to render depth into a separate target. And then use that texture to read depth
+ // in the lighting pass.
+ RenderTexture* rtDepth = RenderBasePassDepth (ctx, objects, loop.m_PlainRenderPasses);
+ depthTextureID = rtDepth->GetTextureID();
+ outDepthRT = rtDepth;
+ colorSurfaceHandle = rtDepth->GetColorSurfaceHandle();
+ depthTextureHandle = rtDepth->GetDepthSurfaceHandle();
+ *outDepthWasCopied = true;
+ }
+ #endif
+
+ if (gGraphicsCaps.hasStencilInDepthTexture)
+ {
+ const ActiveLight* mainActiveLight = GetMainActiveLight(activeLights);
+ Light* mainLight = mainActiveLight ? mainActiveLight->light : NULL;
+ const bool mainLightHasShadows = mainLight && mainLight->GetType() == kLightDirectional && mainLight->GetShadows() != kShadowNone;
+ const bool cameraNeedsDepthTexture = (ctx.m_Camera->GetDepthTextureMode() & Camera::kDepthTexDepthBit);
+ if (mainLightHasShadows || cameraNeedsDepthTexture)
+ {
+ RenderForwardObjectsIntoDepth (
+ ctx,
+ rtMain,
+ &outRemainingObjects,
+ colorSurfaceHandle,
+ depthTextureHandle,
+ rtMain->GetWidth(),
+ rtMain->GetHeight(),
+ cameraNeedsDepthTexture
+ );
+ }
+ }
+
+ ResolveDepthIntoTextureIfNeeded (device, *(ctx.m_RenderLoop), depthFormat, outDepthRT, &depthTextureID, outDepthWasCopied);
+
+ // Render Lighting pass
+ RenderTexture* rtLight = NULL;
+#if SEPERATE_PREPASS_SPECULAR
+ RenderTexture* rtLightSpec = NULL;
+#endif
+ loop.RenderLighting (activeLights,
+ rtMain,
+ depthTextureID,
+ rtNormalsSpec,
+ rtLight,
+#if SEPERATE_PREPASS_SPECULAR
+ rtLightSpec,
+#endif
+ lightFade,
+ lightingLayers,
+ receiverBounds,
+ &outMainShadowMap);
+
+ // It's again ok for shaders to set their stencil state now.
+ ShaderLab::g_GlobalAllowShaderStencil = true;
+
+ if (ctx.m_Camera->GetClearStencilAfterLightingPass())
+ {
+ float black[] = {0,0,0,0};
+ device.Clear (kGfxClearStencil, black, 1.0f, 0);
+ }
+
+ // Render final Geometry pass
+ loop.RenderFinalPass (rtMain,
+ rtLight,
+#if SEPERATE_PREPASS_SPECULAR
+ rtLightSpec,
+#endif
+ ctx.m_Camera->GetUsingHDR(),
+ linearLighting);
+
+ if (rtLight)
+ {
+ // Do not release the light buffer yet; so that image effects or whatever can access it later
+ // if needed (via _LightBuffer)
+ device.SetSurfaceFlags(rtLight->GetColorSurfaceHandle(), GfxDevice::kSurfaceDefault, ~GfxDevice::kSurfaceRestoreMask);
+ device.SetSurfaceFlags(rtLight->GetDepthSurfaceHandle(), GfxDevice::kSurfaceDefault, ~GfxDevice::kSurfaceRestoreMask);
+ AddRenderLoopTempBuffer (ctx.m_RenderLoop, rtLight);
+ }
+
+#if SEPERATE_PREPASS_SPECULAR
+ if (rtLightSpec)
+ {
+ device.SetSurfaceFlags(rtLightSpec->GetColorSurfaceHandle(), GfxDevice::kSurfaceDefault, ~GfxDevice::kSurfaceRestoreMask);
+ device.SetSurfaceFlags(rtLightSpec->GetDepthSurfaceHandle(), GfxDevice::kSurfaceDefault, ~GfxDevice::kSurfaceRestoreMask);
+ AddRenderLoopTempBuffer (ctx.m_RenderLoop, rtLightSpec);
+ }
+#endif
+
+ // Combine depth+normals if needed
+ if (ctx.m_Camera->GetDepthTextureMode() & Camera::kDepthTexDepthNormalsBit)
+ {
+ outDepthNormalsRT = CombineDepthNormalsTexture (ctx, outRemainingObjects);
+ RenderTexture::SetActive (rtMain);
+ }
+
+ device.SetViewMatrix( ctx.m_CurCameraMatrix.GetPtr() );
+ device.SetNormalizationBackface( kNormalizationDisabled, false );
+}
+
+#endif // GFX_SUPPORTS_RENDERLOOP_PREPASS
diff --git a/Runtime/Camera/RenderLoops/RenderLoop.h b/Runtime/Camera/RenderLoops/RenderLoop.h
new file mode 100644
index 0000000..8c54ed8
--- /dev/null
+++ b/Runtime/Camera/RenderLoops/RenderLoop.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "RenderLoopEnums.h"
+
+class Shader;
+struct RenderLoop;
+class Camera;
+class ImageFilters;
+class RenderTexture;
+struct ShadowCullData;
+struct CullResults;
+
+
+RenderLoop* CreateRenderLoop (Camera& camera);
+void DeleteRenderLoop (RenderLoop* loop);
+void DoRenderLoop (
+ RenderLoop& loop,
+ RenderingPath renderPath,
+ CullResults& contents,
+ // used in the editor for material previews - those should not render projectors, halos etc.
+ bool dontRenderRenderables
+);
+void CleanupAfterRenderLoop (RenderLoop& loop);
+ImageFilters& GetRenderLoopImageFilters (RenderLoop& loop);
+void RenderImageFilters (RenderLoop& loop, RenderTexture* targetTexture, bool afterOpaque);
diff --git a/Runtime/Camera/RenderLoops/RenderLoopEnums.h b/Runtime/Camera/RenderLoops/RenderLoopEnums.h
new file mode 100644
index 0000000..e6ec07e
--- /dev/null
+++ b/Runtime/Camera/RenderLoops/RenderLoopEnums.h
@@ -0,0 +1,29 @@
+#pragma once
+
+enum RenderingPath {
+ kRenderPathVertex = 0,
+ kRenderPathForward,
+ kRenderPathPrePass,
+ kRenderPathCount
+};
+
+enum OcclusionQueryType {
+ kOcclusionQueryTypeMostAccurate = 0,
+ kOcclusionQueryTypeFastest,
+ kOcclusionQueryTypeCount
+};
+
+enum
+{
+ kBackgroundRenderQueue = 1000,
+ kGeometryRenderQueue = 2000,
+ kAlphaTestRenderQueue = 2450, // we want it to be in the end of geometry queue
+ kTransparentRenderQueue = 3000,
+ kOverlayRenderQueue = 4000,
+
+ kQueueIndexMin = 0,
+ kQueueIndexMax = 5000,
+
+ kGeometryQueueIndexMin = kGeometryRenderQueue-500,
+ kGeometryQueueIndexMax = kGeometryRenderQueue+500,
+};
diff --git a/Runtime/Camera/RenderLoops/RenderLoopPrivate.cpp b/Runtime/Camera/RenderLoops/RenderLoopPrivate.cpp
new file mode 100644
index 0000000..823c8fa
--- /dev/null
+++ b/Runtime/Camera/RenderLoops/RenderLoopPrivate.cpp
@@ -0,0 +1,469 @@
+#include "UnityPrefix.h"
+#include "RenderLoopPrivate.h"
+#include "RenderLoop.h"
+#include "Runtime/Camera/UnityScene.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Camera/ShadowCulling.h"
+#include "Runtime/Camera/RenderSettings.h"
+#include "Runtime/Camera/ImageFilters.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Camera/BaseRenderer.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Camera/Renderqueue.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+#include "Runtime/GfxDevice/GfxDeviceConfigure.h"
+#include "Runtime/Shaders/Shader.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "Runtime/Graphics/RenderBufferManager.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "ReplacementRenderLoop.h"
+
+#if UNITY_EDITOR
+ #include "Editor/Src/EditorUserBuildSettings.h"
+#endif
+
+PROFILER_INFORMATION(gRenderPrepareObjects, "Render.Prepare", kProfilerRender)
+PROFILER_INFORMATION(gRenderOpaque, "Render.OpaqueGeometry", kProfilerRender)
+PROFILER_INFORMATION(gRenderTransparent, "Render.TransparentGeometry", kProfilerRender)
+PROFILER_INFORMATION(gPrePassFwdDepthTex, "RenderPrePass.FwdObjectsIntoDepth", kProfilerRender)
+PROFILER_INFORMATION(gPrePassFwdDepthNormalsTex, "RenderPrePass.FwdObjectsIntoDepthNormals", kProfilerRender)
+PROFILER_INFORMATION(gCameraResolveProfile, "Camera.AAResolve", kProfilerRender)
+
+
+namespace ShaderLab { void ClearGrabPassFrameState (); } // pass.cpp
+
+
+struct RenderLoop {
+public:
+ RenderLoop (Camera& camera);
+ ~RenderLoop ();
+
+ void PrepareFrame (bool dontRenderRenderables, bool renderingShaderReplace);
+
+public:
+ RenderLoopContext m_Context;
+ ShadowCullData m_ShadowCullData;
+ RenderObjectDataContainer m_Objects[kPartCount];
+ ImageFilters m_ImageFilters;
+
+ enum { kMaxCreatedTempBuffers = 8 };
+ RenderTexture* m_TempBuffers[kMaxCreatedTempBuffers];
+ int m_TempBufferCount;
+};
+
+
+RenderLoop* CreateRenderLoop (Camera& camera)
+{
+ return new RenderLoop(camera);
+}
+
+void DeleteRenderLoop (RenderLoop* loop)
+{
+ delete loop;
+}
+
+ImageFilters& GetRenderLoopImageFilters (RenderLoop& loop)
+{
+ return loop.m_ImageFilters;
+}
+
+
+RenderLoop::RenderLoop(Camera& camera)
+{
+ m_Context.m_Camera = &camera;
+ m_Context.m_RenderLoop = this;
+
+ for (int i = 0; i < kMaxCreatedTempBuffers; ++i) {
+ m_TempBuffers[i] = NULL;
+ }
+ m_TempBufferCount = 0;
+}
+
+RenderLoop::~RenderLoop()
+{
+ Assert (m_TempBufferCount == 0);
+}
+
+inline float MultiplyPointZ (const Matrix4x4f& m, const Vector3f& v)
+{
+ return m.m_Data[2] * v.x + m.m_Data[6] * v.y + m.m_Data[10] * v.z + m.m_Data[14];
+}
+
+// Both distances become smaller (more negative) when moving forward from the camera.
+// outDistanceForSort is for sorting only, and it can be square of the actual distance, and so on.
+// outDistnaceAlongView is projection of the center along camera's view.
+static void EvaluateObjectDepth (const RenderLoopContext& ctx, const TransformInfo& info, float& outDistanceForSort, float& outDistanceAlongView)
+{
+ Vector3f center = info.worldAABB.GetCenter();
+ if (ctx.m_SortOrthographic)
+ {
+ const float d = MultiplyPointZ (ctx.m_CurCameraMatrix, center);
+ outDistanceForSort = d;
+ outDistanceAlongView = d;
+ }
+ else
+ {
+ outDistanceAlongView = MultiplyPointZ (ctx.m_CurCameraMatrix, center);
+ center -= ctx.m_CurCameraPos;
+ outDistanceForSort = -SqrMagnitude(center);
+ }
+
+ // A distance of NaN can cause inconsistent sorting results, if input order is inconsistent.
+ Assert(IsFinite(outDistanceForSort));
+ Assert(IsFinite(outDistanceAlongView));
+}
+
+
+void RenderLoop::PrepareFrame (bool dontRenderRenderables, bool renderingShaderReplace)
+{
+ Camera& camera = *m_Context.m_Camera;
+ m_Context.m_CurCameraMatrix = camera.GetWorldToCameraMatrix();
+ m_Context.m_CurCameraPos = camera.GetComponent(Transform).GetPosition();
+ m_Context.m_CameraViewport = camera.GetRenderRectangle();
+ switch (camera.GetSortMode())
+ {
+ case Camera::kSortPerspective: m_Context.m_SortOrthographic = false; break;
+ case Camera::kSortOrthographic: m_Context.m_SortOrthographic = true; break;
+ default: m_Context.m_SortOrthographic = camera.GetOrthographic(); break;
+ }
+ m_Context.m_DontRenderRenderables = dontRenderRenderables;
+ m_Context.m_RenderingShaderReplace = renderingShaderReplace;
+
+ for (int i = 0; i < kPartCount; ++i)
+ m_Objects[i].resize_uninitialized(0);
+
+ #if DEBUGMODE
+ for (int i = 0; i < kMaxCreatedTempBuffers; ++i) {
+ Assert (m_TempBuffers[i] == NULL);
+ }
+ #endif
+ m_TempBufferCount = 0;
+}
+
+
+static RenderTexture* ResolveScreenToTextureIfNeeded (RenderLoop& loop, bool forceIntoRT, bool beforeOpaqueImageFx)
+{
+ // If we use screen to composite image effects, resolve screen into the render texture now
+ bool usingScreenToComposite = loop.m_ImageFilters.HasImageFilter() && loop.m_Context.m_Camera->GetUsesScreenForCompositing(forceIntoRT);
+ RenderTexture* rt = NULL;
+ if (usingScreenToComposite)
+ {
+ // Do a screen to RT resolve here.
+ rt = beforeOpaqueImageFx ? loop.m_ImageFilters.GetTargetBeforeOpaque () : loop.m_ImageFilters.GetTargetAfterOpaque (forceIntoRT, usingScreenToComposite);
+ if (!rt)
+ return NULL;
+
+ PROFILER_AUTO_GFX(gCameraResolveProfile, loop.m_Context.m_Camera)
+ GPU_AUTO_SECTION(kGPUSectionPostProcess);
+
+ // We should insert proper discard/clear/... on backbuffer when doing MSAA
+ // resolved off it. However that's for the future (case 549705),
+ // for now just silence the RT unresolve warning.
+ GetGfxDevice().IgnoreNextUnresolveOnCurrentRenderTarget();
+
+ Rectf r = loop.m_Context.m_Camera->GetPhysicalViewportRect();
+ int rect[4];
+ RectfToViewport( r, rect );
+ Assert (rect[2] == rt->GetGLWidth() && rect[3] == rt->GetGLHeight());
+ rt->GrabPixels (rect[0], rect[1], rect[2], rect[3]);
+
+ // D3D and GL use different notions of how Y texture coordinates go.
+ // In effect, we have to flip any sampling from the first texture in the image filters
+ // stack on D3D.
+ rt->CorrectVerticalTexelSize(false);
+ }
+
+ return rt;
+}
+
+
+void RenderImageFilters (RenderLoop& loop, RenderTexture* targetTexture, bool afterOpaque)
+{
+ bool forceIntoRT = loop.m_Context.m_Camera->CalculateNeedsToRenderIntoRT();
+ ResolveScreenToTextureIfNeeded (loop, forceIntoRT, afterOpaque);
+ bool usingScreenToComposite = loop.m_ImageFilters.HasImageFilter() && loop.m_Context.m_Camera->GetUsesScreenForCompositing(forceIntoRT);
+ loop.m_ImageFilters.DoRender (targetTexture, forceIntoRT, afterOpaque, usingScreenToComposite, loop.m_Context.m_Camera->GetUsingHDR());
+ if (afterOpaque && !usingScreenToComposite)
+ loop.m_Context.m_Camera->SetCurrentTargetTexture (loop.m_ImageFilters.GetTargetAfterOpaque(forceIntoRT,usingScreenToComposite));
+}
+
+
+static void UpdateCameraDepthTextures (Camera& camera, RenderTexture* rtDepth, RenderTexture* rtDepthNormals, RenderObjectDataContainer& objects, bool depthWasCopied, bool skipDepthTexture, bool afterOpaque)
+{
+ if (!rtDepth || objects.size() == 0)
+ return;
+
+ // use depth buffer from final target
+ RenderTexture* rtFinal = camera.GetCurrentTargetTexture();
+ Assert (rtFinal);
+ RenderSurfaceHandle rtSurfaceDepth = rtFinal->GetDepthSurfaceHandle();
+
+ int renderFlags = Camera::kRenderFlagSetRenderTarget;
+ if (!afterOpaque)
+ renderFlags |= Camera::kRenderFlagSetRenderTargetFinal;
+
+ if (!skipDepthTexture && gGraphicsCaps.hasStencilInDepthTexture && (camera.GetDepthTextureMode() & Camera::kDepthTexDepthBit))
+ {
+ Shader* shader = GetCameraDepthTextureShader ();
+ if (shader)
+ {
+ PROFILER_AUTO_GFX(gPrePassFwdDepthTex, &camera);
+ // If we did separate pass or depth resolve in deferred to work around depth+stencil texture bugs,
+ // render into the copy in that case.
+ if (depthWasCopied)
+ {
+ RenderTexture::SetActive (rtDepth);
+ }
+ else
+ {
+ RenderSurfaceHandle rtSurfaceColor = rtDepth->GetColorSurfaceHandle();
+ RenderTexture::SetActive (1, &rtSurfaceColor, rtSurfaceDepth, rtDepth);
+ }
+
+ RenderSceneShaderReplacement (objects, shader, "RenderType");
+ camera.SetupRender (renderFlags);
+ }
+ }
+
+ if (rtDepthNormals && (camera.GetDepthTextureMode() & Camera::kDepthTexDepthNormalsBit))
+ {
+ Shader* shader = GetCameraDepthNormalsTextureShader ();
+ if (shader)
+ {
+ PROFILER_AUTO_GFX(gPrePassFwdDepthNormalsTex, &camera);
+ RenderSurfaceHandle rtSurfaceColor = rtDepthNormals->GetColorSurfaceHandle();
+ RenderTexture::SetActive (1, &rtSurfaceColor, rtSurfaceDepth, rtDepthNormals);
+
+ RenderSceneShaderReplacement (objects, shader, "RenderType");
+ camera.SetupRender (renderFlags);
+ }
+ }
+}
+
+bool gInsideRenderLoop = false;
+void StartRenderLoop()
+{
+ Assert (!gInsideRenderLoop);
+ gInsideRenderLoop = true;
+}
+void EndRenderLoop()
+{
+ Assert (gInsideRenderLoop);
+ gInsideRenderLoop = false;
+}
+bool IsInsideRenderLoop()
+{
+ return gInsideRenderLoop;
+}
+
+void DoRenderLoop (
+ RenderLoop& loop,
+ RenderingPath renderPath,
+ CullResults& contents,
+ bool dontRenderRenderables)
+{
+ Assert (loop.m_TempBufferCount == 0);
+ Assert (contents.shadowCullData);
+
+ loop.m_Context.m_ShadowCullData = contents.shadowCullData;
+ loop.m_Context.m_CullResults = &contents;
+
+ // save wireframe state, restore at exit
+ SetAndRestoreWireframeMode saveAndRestoreWireframe;
+
+ const bool licenseAllowsStaticBatching = GetBuildSettings().hasAdvancedVersion;
+ Camera& camera = *loop.m_Context.m_Camera;
+
+ Shader* replacementShader = contents.shaderReplaceData.replacementShader;
+ const bool replacementTagSet = contents.shaderReplaceData.replacementTagSet;
+ const int replacementTagID = contents.shaderReplaceData.replacementTagID;
+
+
+ {
+ PROFILER_AUTO(gRenderPrepareObjects, &camera);
+
+ loop.PrepareFrame (dontRenderRenderables, replacementShader);
+
+ const bool useOldRenderQueueLogic = !IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_2_a1);
+
+ // sort out objects into opaque & alpha parts
+ VisibleNodes::iterator itEnd = contents.nodes.end();
+ for (VisibleNodes::iterator it = contents.nodes.begin(); it != itEnd; ++it)
+ {
+ if (!IsFinite(it->invScale))
+ continue;
+
+ BaseRenderer* renderer = it->renderer;
+
+ float distanceForSort, distanceAlongView;
+ EvaluateObjectDepth (loop.m_Context, *it, distanceForSort, distanceAlongView);
+ distanceForSort -= renderer->GetSortingFudge ();
+ distanceAlongView = -distanceAlongView; // make that so increases with distance
+
+ const int matCount = renderer->GetMaterialCount();
+ const int batchIndex = (licenseAllowsStaticBatching)? renderer->GetStaticBatchIndex(): 0;
+ const UInt16 lightmapIndex = renderer->GetLightmapIndex();
+
+ for (int mi = 0; mi < matCount; ++mi)
+ {
+ Material* mat = renderer->GetMaterial (mi);
+ if( mat == NULL )
+ mat = Material::GetDefault();
+ Shader* shader = mat->GetShader();
+
+ int usedSubshaderIndex = -1;
+ if (replacementShader)
+ {
+ if (replacementTagSet)
+ {
+ int subshaderTypeID = shader->GetShaderLabShader()->GetTag (replacementTagID, true);
+ if (subshaderTypeID < 0)
+ continue; // skip rendering
+ usedSubshaderIndex = replacementShader->GetSubShaderWithTagValue (replacementTagID, subshaderTypeID);
+ if (usedSubshaderIndex == -1)
+ continue; // skip rendering
+ }
+ else
+ {
+ usedSubshaderIndex = 0;
+ }
+ }
+
+ const int matIndex = renderer->GetSubsetIndex(mi);
+
+ // Figure out rendering queue to use
+ int queueIndex = mat->GetCustomRenderQueue(); // any per-material overriden queue takes priority
+ if (queueIndex < 0)
+ {
+ // When no shader replacement or old content, take queue from the shader
+ if (!replacementShader || useOldRenderQueueLogic)
+ {
+ queueIndex = shader->GetShaderLabShader()->GetRenderQueue();
+ }
+ // Otherwise take from replacement shader
+ else
+ {
+ queueIndex = replacementShader->GetShaderLabShader()->GetRenderQueue(usedSubshaderIndex);
+ }
+ }
+
+ RenderPart part;
+ if (queueIndex <= kGeometryQueueIndexMax)
+ part = kPartOpaque;
+ else
+ part = kPartAfterOpaque;
+
+ RenderObjectData& odata = loop.m_Objects[part].push_back ();
+ DebugAssertIf (!mat);
+ odata.material = mat;
+ odata.queueIndex = queueIndex;
+ odata.subsetIndex = matIndex;
+ odata.subShaderIndex = usedSubshaderIndex;
+ odata.sourceMaterialIndex = (UInt16)mi;
+ odata.lightmapIndex = lightmapIndex;
+ odata.staticBatchIndex = batchIndex;
+ odata.distance = distanceForSort;
+ odata.distanceAlongView = distanceAlongView;
+ odata.visibleNode = &*it;
+ odata.shader = replacementShader ? replacementShader : shader;
+ odata.globalLayeringData = renderer->GetGlobalLayeringData();
+ }
+ }
+ }
+
+ // want linear lighting?
+ bool linearLighting = GetActiveColorSpace() == kLinearColorSpace;
+
+ // opaque: deferred or forward
+ RenderTexture *rtDepth = NULL, *rtDepthNormals = NULL;
+ bool prepassDepthWasCopied = false;
+ {
+ PROFILER_AUTO_GFX(gRenderOpaque, &camera);
+
+ loop.m_Context.m_RenderQueueStart = kQueueIndexMin; loop.m_Context.m_RenderQueueEnd = kGeometryQueueIndexMax+1;
+ if (renderPath == kRenderPathPrePass)
+ {
+ #if GFX_SUPPORTS_RENDERLOOP_PREPASS
+ RenderTexture *rtShadowMap = NULL;
+ RenderObjectDataContainer remainingObjects;
+ DoPrePassRenderLoop (loop.m_Context, loop.m_Objects[kPartOpaque], remainingObjects, rtDepth, rtDepthNormals, rtShadowMap, contents.activeLights, linearLighting, &prepassDepthWasCopied);
+ if (remainingObjects.size() != 0)
+ {
+ // Objects/shaders that don't handle deferred: render with forward path, and pass main shadowmap to it
+ // Also disable dynamic batching of those objects. They are already rendered into
+ // the depth buffer, and dynamic batching would make them be rendered at slightly
+ // different positions, failing depth test at places.
+ DoForwardShaderRenderLoop (loop.m_Context, remainingObjects, true, true, rtShadowMap, contents.activeLights, linearLighting, false);
+
+ UpdateCameraDepthTextures (camera, rtDepth, rtDepthNormals, remainingObjects, prepassDepthWasCopied, true, true);
+ }
+ #else
+ ErrorString ("Pre-pass rendering loop should never happen on this platform!");
+ #endif
+ }
+ else if (renderPath == kRenderPathForward)
+ {
+ DoForwardShaderRenderLoop (loop.m_Context, loop.m_Objects[kPartOpaque], true, false, NULL, contents.activeLights, linearLighting, true);
+ }
+ else
+ {
+ DoForwardVertexRenderLoop (loop.m_Context, loop.m_Objects[kPartOpaque], true, contents.activeLights, linearLighting, true);
+ }
+ }
+
+ // render skybox after opaque (sRGB conversions needed if using linear rendering)
+ {
+ GetGfxDevice().SetSRGBWrite(linearLighting);
+ camera.RenderSkybox();
+ GetGfxDevice().SetSRGBWrite(false);
+ }
+
+ RenderImageFilters (loop, camera.GetTargetTexture(), true);
+
+ // after opaque: forward
+ {
+ PROFILER_AUTO_GFX(gRenderTransparent, &camera);
+
+ loop.m_Context.m_RenderQueueStart = kGeometryQueueIndexMax+1; loop.m_Context.m_RenderQueueEnd = kQueueIndexMax;
+ if (renderPath != kRenderPathVertex)
+ {
+ DoForwardShaderRenderLoop (loop.m_Context, loop.m_Objects[kPartAfterOpaque], false, false, NULL, contents.activeLights, linearLighting, false);
+ }
+ else
+ {
+ DoForwardVertexRenderLoop (loop.m_Context, loop.m_Objects[kPartAfterOpaque], false, contents.activeLights, linearLighting, false);
+ }
+
+ UpdateCameraDepthTextures (camera, rtDepth, rtDepthNormals, loop.m_Objects[kPartAfterOpaque], prepassDepthWasCopied, false, false);
+ }
+
+ loop.m_Context.m_ShadowCullData = NULL;
+ loop.m_Context.m_CullResults = NULL;
+}
+
+void CleanupAfterRenderLoop (RenderLoop& loop)
+{
+ Assert (loop.m_TempBufferCount >= 0 && loop.m_TempBufferCount < RenderLoop::kMaxCreatedTempBuffers);
+ RenderBufferManager& rbm = GetRenderBufferManager();
+ for (int i = 0; i < loop.m_TempBufferCount; ++i) {
+ Assert (loop.m_TempBuffers[i]);
+ rbm.ReleaseTempBuffer (loop.m_TempBuffers[i]);
+ loop.m_TempBuffers[i] = NULL;
+ }
+ loop.m_TempBufferCount = 0;
+ ShaderLab::ClearGrabPassFrameState();
+}
+
+void AddRenderLoopTempBuffer (RenderLoop* loop, RenderTexture* rt)
+{
+ Assert (loop && rt);
+ Assert (loop->m_TempBufferCount < RenderLoop::kMaxCreatedTempBuffers);
+
+ loop->m_TempBuffers[loop->m_TempBufferCount++] = rt;
+}
diff --git a/Runtime/Camera/RenderLoops/RenderLoopPrivate.h b/Runtime/Camera/RenderLoops/RenderLoopPrivate.h
new file mode 100644
index 0000000..c64927c
--- /dev/null
+++ b/Runtime/Camera/RenderLoops/RenderLoopPrivate.h
@@ -0,0 +1,86 @@
+#pragma once
+
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Camera/CullResults.h"
+#include "GlobalLayeringData.h"
+
+namespace Unity { class Material; }
+class Camera;
+class Shader;
+class RenderTexture;
+struct ShadowCullData;
+struct RenderLoop;
+
+struct RenderObjectData {
+ Unity::Material* material; // 4
+ SInt16 queueIndex; // 2
+ UInt16 subsetIndex; // 2
+ SInt16 subShaderIndex; // 2
+ UInt16 sourceMaterialIndex;// 2
+ UInt16 lightmapIndex; // 2
+ int staticBatchIndex; // 4
+ float distance; // 4
+
+ //@TODO: cold?
+ float distanceAlongView; // 4
+ VisibleNode* visibleNode; // 4
+ Shader* shader; // 4 shader to use
+ GlobalLayeringData
+ globalLayeringData; // 4
+ // 36 bytes
+};
+
+enum RenderPart { kPartOpaque, kPartAfterOpaque, kPartCount };
+
+typedef dynamic_array<RenderObjectData> RenderObjectDataContainer;
+
+struct RenderLoopContext
+{
+ Camera* m_Camera;
+
+ const CullResults* m_CullResults;
+ const ShadowCullData* m_ShadowCullData;
+ Matrix4x4f m_CurCameraMatrix;
+ Rectf m_CameraViewport;
+ Vector3f m_CurCameraPos;
+ bool m_SortOrthographic;
+ bool m_DontRenderRenderables;
+ bool m_RenderingShaderReplace;
+
+ int m_RenderQueueStart;
+ int m_RenderQueueEnd;
+
+ RenderLoop* m_RenderLoop;
+};
+
+void AddRenderLoopTempBuffer (RenderLoop* loop, RenderTexture* rt);
+
+void DoForwardVertexRenderLoop (RenderLoopContext& ctx, RenderObjectDataContainer& objects, bool opaque, ActiveLights& activeLights, bool linearLighting, bool clearFrameBuffer);
+void DoForwardShaderRenderLoop (
+ RenderLoopContext& ctx,
+ RenderObjectDataContainer& objects,
+ bool opaque,
+ bool disableDynamicBatching,
+ RenderTexture* mainShadowMap,
+ ActiveLights& activeLights,
+ bool linearLighting,
+ bool clearFrameBuffer);
+
+void DoPrePassRenderLoop (
+ RenderLoopContext& ctx,
+ RenderObjectDataContainer& objects,
+ RenderObjectDataContainer& outRemainingObjects,
+ RenderTexture*& outDepthRT,
+ RenderTexture*& outDepthNormalsRT,
+ RenderTexture*& outMainShadowMap,
+ ActiveLights& activeLights,
+ bool linearLighting,
+ bool* outDepthWasCopied);
+
+// This is only usable by GfxDeviceGLES, because GfxDeviceGLES only supports ForwardVertexRenderLoop, you'll only see these functions there
+// If IsInsideRenderLoop() == true, no state caching will be performed by GfxDeviceGLES
+void StartRenderLoop();
+void EndRenderLoop();
+bool IsInsideRenderLoop();
diff --git a/Runtime/Camera/RenderLoops/ReplacementRenderLoop.cpp b/Runtime/Camera/RenderLoops/ReplacementRenderLoop.cpp
new file mode 100644
index 0000000..fcba391
--- /dev/null
+++ b/Runtime/Camera/RenderLoops/ReplacementRenderLoop.cpp
@@ -0,0 +1,245 @@
+#include "UnityPrefix.h"
+#include "ReplacementRenderLoop.h"
+#include "Runtime/Camera/Renderqueue.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Camera/BaseRenderer.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Camera/Camera.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Camera/UnityScene.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+
+struct RODataReplacement {
+ float distance;
+ int subshaderIndex;
+ Material* material;
+ const VisibleNode* visibleNode;
+ Shader* shader;
+ int materialIndex;
+ GlobalLayeringData globalLayeringData;
+};
+
+typedef UNITY_TEMP_VECTOR(RODataReplacement) RenderObjects;
+
+struct ROSorterReplacement {
+ bool operator()( const RODataReplacement& ra, const RODataReplacement& rb ) const;
+};
+
+
+bool ROSorterReplacement::operator()( const RODataReplacement& ra, const RODataReplacement& rb ) const
+{
+ // Sort by layering depth. //@TODO:should this be here?
+ bool globalLayeringResult;
+ if (CompareGlobalLayeringData(ra.globalLayeringData, rb.globalLayeringData, globalLayeringResult))
+ return globalLayeringResult;
+
+ // Sort by subshader index used
+ if (ra.subshaderIndex != rb.subshaderIndex)
+ return ra.subshaderIndex < rb.subshaderIndex;
+
+ // Sort front to back
+ return ra.distance > rb.distance;
+}
+
+
+static inline float EvaluateObjectDepth (const Matrix4x4f& cameraMatrix, const TransformInfo& info)
+{
+ Vector3f center = info.worldAABB.GetCenter();
+ float d = cameraMatrix.MultiplyPoint3( center ).z;
+ Assert(IsFinite(d));
+ return d;
+}
+
+
+static void PerformRenderingReplacement (Camera& camera, const Matrix4x4f& curCameraMatrix, RenderObjects& renderData)
+{
+ // Sort
+ std::sort (renderData.begin(), renderData.end(), ROSorterReplacement());
+
+
+ GfxDevice& device = GetGfxDevice();
+ size_t ndata = renderData.size();
+ device.SetViewMatrix (curCameraMatrix.GetPtr());
+
+ for( size_t i = 0; i < ndata; ++i )
+ {
+ const RODataReplacement& roData = renderData[i];
+
+ const VisibleNode* node = roData.visibleNode;
+ Assert (node);
+ BaseRenderer* renderer = node->renderer;
+ Assert (renderer);
+ Shader* shader = roData.shader;
+
+ device.SetInverseScale(1.0f);
+
+ //@TODO: if this returns true and we have any sort of batching, we'd have to break batches here
+ renderer->ApplyCustomProperties(*roData.material, shader, roData.subshaderIndex);
+
+ ShaderLab::SubShader& subshader = roData.shader->GetShaderLabShader()->GetSubShader (roData.subshaderIndex);
+ int shaderPassCount = subshader.GetValidPassCount();
+ for (int p = 0; p < shaderPassCount; ++p)
+ {
+ const ChannelAssigns* channels = roData.material->SetPassWithShader(p, shader, roData.subshaderIndex);
+ if (channels)
+ {
+ SetupObjectMatrix (node->worldMatrix, node->transformType);
+ renderer->Render( renderer->GetSubsetIndex(roData.materialIndex), *channels );
+ }
+ }
+ }
+}
+
+static void AddReplacementObject (
+ RenderObjects& renderObjects,
+ Material* mat,
+ Shader* replacementShader,
+ bool noReplacementTag,
+ int replacementTagID,
+ const VisibleNode* visibleNode,
+ float distanceForSort,
+ int materialIndex,
+ GlobalLayeringData globalLayeringData
+
+ )
+{
+ if( mat == NULL )
+ mat = Material::GetDefault();
+ Shader *shader = mat->GetShader();
+
+ // Note: do not check whether object is in geometry queue range,
+ // let shader replacement handle that. E.g. terrain billboard shaders are actually
+ // beyond geometry queue, but still can output meaningful depth/normals information.
+
+ // Handle shader replacement
+ // Given a replacement shader and tag name:
+ // 1. if tag name is empty, then all objects are just rendered with replacement shader's first subshader
+ // 2. if tag name is given:
+ // * real object's subshader is queried for tag value.
+ // * if it does not have that tag, the object is not rendered.
+ // * subshader is found in the replacement shader, that has given tag with the given value. If no subshader found, object is not rendered.
+ // * that subshader is used instead to render the object.
+ int usedSubshaderIndex;
+ if (noReplacementTag)
+ {
+ usedSubshaderIndex = 0;
+ }
+ else
+ {
+ int subshaderTypeID = shader->GetShaderLabShader()->GetTag (replacementTagID, true);
+ if (subshaderTypeID < 0)
+ return; // skip rendering
+ usedSubshaderIndex = replacementShader->GetSubShaderWithTagValue (replacementTagID, subshaderTypeID);
+ if (usedSubshaderIndex == -1)
+ return; // skip rendering
+ }
+
+ renderObjects.push_back(RODataReplacement());
+ RODataReplacement& roData = renderObjects.back();
+ roData.visibleNode = visibleNode;
+ roData.distance = distanceForSort;
+
+ DebugAssertIf( !mat );
+ roData.material = mat;
+ roData.materialIndex = materialIndex;
+
+ roData.shader = replacementShader;
+ roData.subshaderIndex = usedSubshaderIndex;
+
+ roData.globalLayeringData = globalLayeringData;
+}
+
+void RenderSceneShaderReplacement (const VisibleNodes& contents, Shader* shader, const std::string& shaderReplaceTag)
+{
+ ShaderReplaceData replaceData;
+ replaceData.replacementShader = shader;
+ replaceData.replacementTagSet = !shaderReplaceTag.empty();
+ replaceData.replacementTagID = ShaderLab::GetShaderTagID(shaderReplaceTag);
+
+ RenderSceneShaderReplacement(contents, replaceData);
+}
+
+
+void RenderSceneShaderReplacement (const VisibleNodes& contents, const ShaderReplaceData& shaderReplace)
+{
+ Assert (shaderReplace.replacementShader != NULL);
+
+ const bool noReplacementTag = !shaderReplace.replacementTagSet;
+ const int replacementTagID = shaderReplace.replacementTagID;
+ Shader* replacementShader = shaderReplace.replacementShader;
+ Camera& camera = GetRenderManager().GetCurrentCamera();
+ Matrix4x4f curCameraMatrix = camera.GetWorldToCameraMatrix();
+
+ RenderObjects renderObjects;
+ renderObjects.reserve (contents.size()/4);
+
+ // Go over the objects
+ for( VisibleNodes::const_iterator i = contents.begin(); i != contents.end(); ++i )
+ {
+ float distanceForSort = EvaluateObjectDepth (curCameraMatrix, *i);
+
+ const BaseRenderer* renderer = i->renderer;
+
+ int matCount = renderer->GetMaterialCount();
+ for (int mi = 0; mi < matCount; ++mi)
+ {
+ Material* mat = renderer->GetMaterial(mi);
+ AddReplacementObject (
+ renderObjects,
+ mat,
+ replacementShader,
+ noReplacementTag,
+ replacementTagID,
+ &*i,
+ distanceForSort,
+ mi,
+ renderer->GetGlobalLayeringData()
+ );
+ }
+ }
+
+ // Render
+ PerformRenderingReplacement (camera, curCameraMatrix, renderObjects);
+}
+
+void RenderSceneShaderReplacement (const RenderObjectDataContainer& contents, Shader* replacementShader, const std::string& replacementTag)
+{
+ Assert (replacementShader);
+
+ const bool noReplacementTag = replacementTag.empty();
+ const int replacementTagID = ShaderLab::GetShaderTagID(replacementTag);
+
+ Camera& camera = GetRenderManager().GetCurrentCamera();
+ Matrix4x4f curCameraMatrix = camera.GetWorldToCameraMatrix();
+
+ RenderObjects renderObjects;
+ renderObjects.reserve (contents.size()/4);
+
+ // Go over the objects
+ for (RenderObjectDataContainer::const_iterator i = contents.begin(); i != contents.end(); ++i)
+ {
+ const RenderObjectData& ro = *i;
+ const BaseRenderer* renderer = ro.visibleNode->renderer;
+ Assert (renderer);
+ Material* mat = renderer->GetMaterial(ro.sourceMaterialIndex);
+ AddReplacementObject (
+ renderObjects,
+ mat,
+ replacementShader,
+ noReplacementTag,
+ replacementTagID,
+ ro.visibleNode,
+ ro.distance,
+ ro.sourceMaterialIndex,
+ renderer->GetGlobalLayeringData()
+ );
+ }
+
+ // Render
+ PerformRenderingReplacement (camera, curCameraMatrix, renderObjects);
+}
+
diff --git a/Runtime/Camera/RenderLoops/ReplacementRenderLoop.h b/Runtime/Camera/RenderLoops/ReplacementRenderLoop.h
new file mode 100644
index 0000000..0adce81
--- /dev/null
+++ b/Runtime/Camera/RenderLoops/ReplacementRenderLoop.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "Runtime/Camera/CullResults.h"
+#include "RenderLoopPrivate.h"
+#include <string>
+
+class Shader;
+
+void RenderSceneShaderReplacement (const VisibleNodes& contents, const ShaderReplaceData& shaderReplace);
+void RenderSceneShaderReplacement (const VisibleNodes& contents, Shader* shader, const std::string& shaderReplaceTag);
+void RenderSceneShaderReplacement (const RenderObjectDataContainer& contents, Shader* shader, const std::string& shaderReplaceTag);
diff --git a/Runtime/Camera/RenderManager.cpp b/Runtime/Camera/RenderManager.cpp
new file mode 100644
index 0000000..65301e5
--- /dev/null
+++ b/Runtime/Camera/RenderManager.cpp
@@ -0,0 +1,272 @@
+#include "UnityPrefix.h"
+#include "RenderManager.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Camera.h"
+#include "Renderable.h"
+#include <vector>
+#include "UnityScene.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystem.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Camera/CullResults.h"
+#include "Runtime/Camera/CameraCullingParameters.h"
+
+static RenderManager* gRenderManager = NULL;
+
+
+// NOTE: AddCamera/RemoveCamera defer the actual addition/removal when called from inside Culling/Rendering
+// loop. Reason: add/remove may invalidate iterators; and that happens in cases like disabling self (or some other)
+// camera from OnPostRender etc.
+
+RenderManager::RenderManager ()
+: m_InsideRenderOrCull(false)
+{
+ m_WindowRect = Rectf(0.0f, 0.0f, 128.0f, 128.0f );
+ m_CurrentViewPort[0] = m_CurrentViewPort[1] = 0;
+ m_CurrentViewPort[2] = m_CurrentViewPort[3] = 128;
+ m_CurrentCamera = NULL;
+}
+
+RenderManager::~RenderManager () {
+ Assert (m_Cameras.empty ());
+}
+
+
+PROFILER_INFORMATION(gCameraRenderManagerProfile, "Camera.Render", kProfilerRender)
+PROFILER_INFORMATION(gCameraUpdateRenderersProfile, "Rendering.UpdateDirtyRenderers", kProfilerRender)
+
+
+void RenderManager::RenderOffscreenCameras()
+{
+ m_InsideRenderOrCull = true;
+
+ // Make sure current viewport is fully preserved after rendering offscreen cameras
+ int savedViewport[4];
+ for (int i = 0; i < 4; ++i)
+ savedViewport[i] = m_CurrentViewPort[i];
+
+ // Render all offscreen cameras so they are ready for the real cameras.
+ for (CameraContainer::iterator i = m_OffScreenCameras.begin(); i != m_OffScreenCameras.end(); ++i )
+ {
+ Camera* cam = *i;
+ PROFILER_AUTO_GFX(gCameraRenderManagerProfile, cam)
+
+ m_CurrentCamera = cam;
+ CullResults cullResults;
+ if( cam && cam->GetEnabled() ) // might become NULL or disabled in OnPreCull
+ cam->Cull(cullResults);
+ if( cam && cam->GetEnabled() ) // check again, might get disabled in culling
+ cam->Render( cullResults, Camera::kRenderFlagSetRenderTarget);
+ }
+
+ for (int i = 0; i < 4; ++i)
+ m_CurrentViewPort[i] = savedViewport[i];
+
+ m_InsideRenderOrCull = false;
+ AddRemoveCamerasDelayed();
+}
+
+void RenderManager::RenderCameras()
+{
+ m_InsideRenderOrCull = true;
+
+ Unity::Scene& scene = GetScene();
+
+ // Render on-screen cameras.
+ for (CameraContainer::iterator i = m_Cameras.begin(); i != m_Cameras.end(); ++i )
+ {
+ Camera* cam = *i;
+ PROFILER_AUTO_GFX(gCameraRenderManagerProfile, cam)
+
+ /////@TODO: This is not reflected in the standalone render function...
+ scene.BeginCameraRender();
+ m_CurrentCamera = cam;
+ CullResults cullResults;
+ if( cam && cam->GetEnabled() ) // might become NULL or disabled in OnPreCull
+ cam->Cull(cullResults);
+ if( cam && cam->GetEnabled() ) // check again, might get disabled in culling
+ cam->Render( cullResults, Camera::kRenderFlagSetRenderTarget );
+ scene.EndCameraRender();
+ }
+
+ m_InsideRenderOrCull = false;
+ AddRemoveCamerasDelayed();
+}
+
+
+/** Get a render callback for each Camera */
+void RenderManager::AddCameraRenderable (Renderable *r, int depth) {
+// Assert (depth <= Camera::kRenderQueueCount);
+ #if DEBUGMODE
+ for (Renderables::iterator i = m_Renderables.begin (); i != m_Renderables.end();i++) {
+ if (i->second == r && i->first == depth)
+ AssertString ("RenderManager: renderable with same depth already added");
+ }
+ #endif
+
+ m_Renderables.insert (std::make_pair (depth, r));
+}
+
+void RenderManager::RemoveCameraRenderable (Renderable *r) {
+ Renderables::iterator next;
+ for (Renderables::iterator i = m_Renderables.begin (); i != m_Renderables.end();i=next) {
+ next = i;
+ next++;
+ if (i->second == r)
+ {
+ m_Renderables.erase (i);
+ }
+ }
+}
+
+
+void RenderManager::InvokeOnRenderObjectCallbacks ()
+{
+ if (m_OnRenderObjectCallbacks.empty())
+ return;
+
+#if ENABLE_MONO || UNITY_WINRT
+ SafeIterator<MonoBehaviourList> it (m_OnRenderObjectCallbacks);
+ while (it.Next())
+ {
+ MonoBehaviour& beh = **it;
+ beh.InvokeOnRenderObject ();
+ }
+#endif
+}
+
+
+#if UNITY_EDITOR
+
+bool RenderManager::HasFullscreenCamera () const
+{
+ for (CameraContainer::const_iterator i = m_Cameras.begin(); i != m_Cameras.end(); i++) {
+ Rectf viewRect = (*i)->GetNormalizedViewportRect ();
+ if (CompareApproximately(Rectf(0,0,1,1), viewRect))
+ return true;
+ }
+ return false;
+}
+
+#endif // UNITY_EDITOR
+
+
+void RenderManager::AddCamera (Camera *c)
+{
+ Assert (c != NULL);
+
+ PPtr<Camera> cam(c);
+ if( m_InsideRenderOrCull )
+ {
+ m_CamerasToRemove.remove( cam );
+ m_CamerasToAdd.push_back( cam );
+ return;
+ }
+
+ m_CamerasToAdd.remove(c);
+ m_CamerasToRemove.remove(c);
+
+ m_Cameras.remove( cam );
+ m_OffScreenCameras.remove( cam );
+ CameraContainer &queue = (c->GetTargetTexture() == NULL) ? m_Cameras : m_OffScreenCameras;
+
+ for (CameraContainer::iterator i=queue.begin ();i != queue.end ();i++)
+ {
+ Camera* curCamera = *i;
+ if (curCamera && curCamera->GetDepth () > c->GetDepth ())
+ {
+ queue.insert (i, c);
+ return;
+ }
+ }
+ queue.push_back (c);
+}
+
+void RenderManager::RemoveCamera (Camera *c)
+{
+ PPtr<Camera> cam(c);
+
+ m_CamerasToAdd.remove(c);
+ m_CamerasToRemove.remove(c);
+
+ if( m_InsideRenderOrCull )
+ {
+ m_CamerasToRemove.push_back( cam );
+ }
+ else
+ {
+ m_Cameras.remove( cam );
+ m_OffScreenCameras.remove( cam );
+ }
+
+ Camera* currentCamera = m_CurrentCamera;
+ if (currentCamera == c)
+ {
+ if (m_Cameras.empty ())
+ m_CurrentCamera = NULL;
+ else
+ m_CurrentCamera = m_Cameras.front (); // ??? maybe better choose next
+ }
+}
+
+void RenderManager::AddRemoveCamerasDelayed()
+{
+ DebugAssertIf( m_InsideRenderOrCull );
+ for( CameraContainer::iterator i = m_CamerasToRemove.begin(); i != m_CamerasToRemove.end(); /**/ )
+ {
+ Camera* cam = *i;
+ ++i; // increment iterator before removing camera; as it changes the list
+ RemoveCamera( cam );
+ }
+ m_CamerasToRemove.clear();
+ for( CameraContainer::iterator i = m_CamerasToAdd.begin(); i != m_CamerasToAdd.end(); /**/ )
+ {
+ Camera* cam = *i;
+ ++i; // increment iterator before adding camera; as it changes the list
+ AddCamera( cam );
+ }
+ m_CamerasToAdd.clear();
+}
+
+
+void RenderManager::SetWindowRect (const Rectf& r)
+{
+ m_WindowRect = r;
+ for( CameraContainer::iterator i=m_Cameras.begin ();i != m_Cameras.end (); ++i )
+ (**i).WindowSizeHasChanged();
+}
+
+
+void RenderManager::UpdateAllRenderers()
+{
+ ParticleSystem::SyncJobs();
+
+ PROFILER_AUTO(gCameraUpdateRenderersProfile, NULL)
+ Renderer::UpdateAllRenderersInternal();
+}
+
+
+RenderManager& GetRenderManager ()
+{
+ return *gRenderManager;
+}
+
+RenderManager* GetRenderManagerPtr ()
+{
+ return gRenderManager;
+}
+
+void RenderManager::InitializeClass ()
+{
+ Assert(gRenderManager == NULL);
+ gRenderManager = new RenderManager ();
+}
+
+void RenderManager::CleanupClass ()
+{
+ Assert(gRenderManager != NULL);
+ delete gRenderManager;
+ gRenderManager = NULL;
+}
diff --git a/Runtime/Camera/RenderManager.h b/Runtime/Camera/RenderManager.h
new file mode 100644
index 0000000..78c3e38
--- /dev/null
+++ b/Runtime/Camera/RenderManager.h
@@ -0,0 +1,83 @@
+#pragma once
+
+#include "Runtime/Utilities/LinkedList.h"
+#include <list>
+#include <map>
+#include "Runtime/Math/Rect.h"
+#include "Camera.h"
+
+class Renderable;
+class MonoBehaviour;
+typedef ListNode<MonoBehaviour> MonoBehaviourListNode;
+
+// Camera handling
+class RenderManager {
+public:
+ typedef std::list<PPtr<Camera> > CameraContainer;
+ typedef std::multimap<int, Renderable *> Renderables;
+
+ RenderManager ();
+ ~RenderManager ();
+
+ void RenderOffscreenCameras();
+ void RenderCameras();
+
+ Camera &GetCurrentCamera () { Assert (!m_CurrentCamera.IsNull ()); return *m_CurrentCamera; }
+ Camera* GetCurrentCameraPtr () { return m_CurrentCamera; }
+ void SetCurrentCamera (Camera *c) { m_CurrentCamera = c; }
+
+ void AddCamera (Camera *c);
+ void RemoveCamera (Camera *c);
+
+ // Add/Remove Renderable. The Renderable.Render() method is called before the RenderQueue with index beforeRenderqueueIndex
+ // is rendered in Camera.Render()
+ void AddCameraRenderable (Renderable *r, int beforeRenderqueueIndex);
+ void RemoveCameraRenderable (Renderable *r);
+ const Renderables& GetRenderables () { return m_Renderables; }
+
+ void AddOnRenderObject (MonoBehaviourListNode& beh) { m_OnRenderObjectCallbacks.push_back(beh); }
+ void InvokeOnRenderObjectCallbacks ();
+
+ CameraContainer& GetOnscreenCameras () { return m_Cameras; }
+ CameraContainer& GetOffscreenCameras () { return m_OffScreenCameras; }
+
+ // The window we're rendering into.
+ // Most often xmin/xmax of this window are zero, except when using fixed aspect
+ // in the game view.
+ const Rectf &GetWindowRect() const { return m_WindowRect; }
+ void SetWindowRect (const Rectf& r);
+
+ const int* GetCurrentViewPort() const { return m_CurrentViewPort; }
+ int* GetCurrentViewPortWriteable() { return m_CurrentViewPort; }
+
+ #if UNITY_EDITOR
+ bool HasFullscreenCamera () const;
+ #endif
+
+ static void UpdateAllRenderers();
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+private:
+ void AddRemoveCamerasDelayed();
+
+private:
+ PPtr<Camera> m_CurrentCamera;
+ CameraContainer m_Cameras, m_OffScreenCameras;
+ CameraContainer m_CamerasToAdd;
+ CameraContainer m_CamerasToRemove;
+ bool m_InsideRenderOrCull;
+
+ Rectf m_WindowRect;
+ int m_CurrentViewPort[4]; // left, bottom, width, height; in pixels into current render target
+
+ Renderables m_Renderables;
+ typedef List<MonoBehaviourListNode> MonoBehaviourList;
+ MonoBehaviourList m_OnRenderObjectCallbacks;
+};
+
+RenderManager& GetRenderManager ();
+RenderManager* GetRenderManagerPtr ();
+inline Camera &GetCurrentCamera () { return GetRenderManager().GetCurrentCamera(); }
+inline Camera* GetCurrentCameraPtr () { return GetRenderManager().GetCurrentCameraPtr(); }
diff --git a/Runtime/Camera/RenderSettings.cpp b/Runtime/Camera/RenderSettings.cpp
new file mode 100644
index 0000000..8c4d4ee
--- /dev/null
+++ b/Runtime/Camera/RenderSettings.cpp
@@ -0,0 +1,262 @@
+#include "UnityPrefix.h"
+#include "RenderSettings.h"
+#include "External/shaderlab/Library/properties.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Light.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "Runtime/Camera/LightManager.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+
+static SHADERPROP (LightTextureB0);
+static SHADERPROP (HaloFalloff);
+
+
+RenderSettings::RenderSettings (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_LinearFogStart = 0; m_LinearFogEnd = 300; m_FogDensity = .01f;
+ m_Fog = false;
+ m_FogMode = kFogExp2;
+ m_FlareFadeSpeed = 3.0f;
+}
+
+
+void RenderSettings::Reset ()
+{
+ Super::Reset();
+
+ m_FogColor.Set (.5,.5,.5,1);
+ m_LinearFogStart = 0; m_LinearFogEnd = 300; m_FogDensity = .01f;
+ m_Fog = false;
+ m_FogMode = kFogExp2;
+ m_AmbientLight.Set (.2f, .2f, .2f, 1);
+ m_FlareStrength = 1.0f;
+ m_FlareFadeSpeed = 3.0f;
+ m_HaloStrength = .5f;
+}
+
+float RenderSettings::CalcFogFactor (float distance) const
+{
+ if (m_Fog)
+ return 1.0F-exp(-(m_FogDensity * m_FogDensity * distance * distance));
+
+ return 0.0F;
+}
+
+Texture2D* RenderSettings::GetDefaultSpotCookie()
+{
+ Texture2D* tex = m_SpotCookie;
+ if (tex)
+ return tex;
+ else
+ {
+ static PPtr<Texture2D> fallback;
+ if (!fallback)
+ fallback = GetBuiltinResource<Texture2D> ("Soft.psd");
+ return fallback;
+ }
+}
+
+RenderSettings::~RenderSettings ()
+{
+}
+
+void RenderSettings::SetupAmbient () const
+{
+ ColorRGBAf amb = GetAmbientLightInActiveColorSpace() * 0.5F;
+ GetGfxDevice().SetAmbient( amb.GetPtr() );
+}
+
+void RenderSettings::AwakeFromLoad (AwakeFromLoadMode awakeMode) {
+ Super::AwakeFromLoad (awakeMode);
+
+ ShaderLab::g_GlobalProperties->SetTexture (kSLPropLightTextureB0, builtintex::GetAttenuationTexture());
+
+ ApplyFog();
+ ApplyHaloTexture();
+
+ // disable light 0 because GL state has it on by default (or something like that)
+ // This handles the wacky case where we have 0 vertex lights and a vertex lit shader (DOH!).
+ GetGfxDevice().DisableLights (0);
+
+ if ((awakeMode & kDidLoadFromDisk) == 0)
+ ApplyFlareAndHaloStrength ();
+}
+
+void RenderSettings::ApplyFlareAndHaloStrength ()
+{
+ // if we are editing from inside the editor, the Halo values might have changed,
+ // So all lights need to update their halos
+ std::vector<SInt32> obj;
+ Object::FindAllDerivedObjects(ClassID (Light), &obj);
+ for (std::vector<SInt32>::iterator i = obj.begin(); i != obj.end(); i++) {
+ Light *l = PPtr<Light> (*i);
+ l->SetupHalo ();
+ l->SetupFlare ();
+ }
+}
+
+void RenderSettings::CheckConsistency () {
+ m_FogDensity = std::min (std::max (m_FogDensity, 0.0f), 1.0f);
+ m_HaloStrength = std::min (std::max (m_HaloStrength, 0.0f), 1.0f);
+}
+
+void RenderSettings::ApplyHaloTexture()
+{
+ Texture2D *tex = m_HaloTexture;
+ ShaderLab::g_GlobalProperties->SetTexture (kSLPropHaloFalloff, tex?tex:builtintex::GetHaloTexture());
+}
+
+void RenderSettings::ApplyFog ()
+{
+ BuiltinShaderParamValues& params = GetGfxDevice().GetBuiltinParamValues();
+ ShaderLab::g_GlobalFogMode = m_Fog ? static_cast<FogMode>(m_FogMode) : kFogDisabled;
+ float fogDensity = m_FogDensity;
+ float fogStart = m_LinearFogStart;
+ float fogEnd = m_LinearFogEnd;
+ if (ShaderLab::g_GlobalFogMode == kFogDisabled)
+ {
+ fogDensity = 0.0f;
+ fogStart = 10000.0f;
+ fogEnd = 20000.0f;
+ }
+
+ params.SetVectorParam(kShaderVecUnityFogStart, Vector4f(fogStart, fogStart, fogStart, fogStart));
+ params.SetVectorParam(kShaderVecUnityFogEnd, Vector4f(fogEnd, fogEnd, fogEnd, fogEnd));
+ params.SetVectorParam(kShaderVecUnityFogDensity, Vector4f(fogDensity, fogDensity, fogDensity, fogDensity));
+
+ ColorRGBAf fogColor = GammaToActiveColorSpace(m_FogColor);
+ params.SetVectorParam(kShaderVecUnityFogColor, Vector4f(fogColor.GetPtr()));
+
+}
+
+template<class TransferFunc>
+void RenderSettings::Transfer (TransferFunc& transfer) {
+ TRANSFER_SIMPLE (m_Fog);
+ transfer.Align();
+ TRANSFER_SIMPLE (m_FogColor);
+ TRANSFER_SIMPLE (m_FogMode);
+ TRANSFER_SIMPLE (m_FogDensity);
+ TRANSFER_SIMPLE (m_LinearFogStart);
+ TRANSFER_SIMPLE (m_LinearFogEnd);
+ TRANSFER_SIMPLE (m_AmbientLight);
+ TRANSFER_SIMPLE (m_SkyboxMaterial);
+ TRANSFER (m_HaloStrength);
+ TRANSFER (m_FlareStrength);
+ TRANSFER (m_FlareFadeSpeed);
+ TRANSFER (m_HaloTexture);
+ TRANSFER (m_SpotCookie);
+ Super::Transfer (transfer);
+}
+
+void RenderSettings::InitializeClass ()
+{
+ RegisterAllowNameConversion(GetClassStringStatic (), "m_Ambient", "m_AmbientLight");
+ LightManager::InitializeClass();
+}
+
+void RenderSettings::PostInitializeClass ()
+{
+ builtintex::GenerateBuiltinTextures();
+}
+
+void RenderSettings::CleanupClass()
+{
+ LightManager::CleanupClass();
+}
+
+void RenderSettings::SetSkyboxMaterial (Material *mat) {
+ m_SkyboxMaterial = mat;
+ SetDirty();
+}
+
+void RenderSettings::SetFogColor (const ColorRGBAf& color)
+{
+ m_FogColor = color;
+ SetDirty();
+ ApplyFog ();
+}
+
+void RenderSettings::SetUseFog (bool fog)
+{
+ m_Fog = fog;
+ SetDirty();
+ ApplyFog ();
+}
+
+void RenderSettings::SetUseFogNoDirty (bool fog)
+{
+ m_Fog = fog;
+ ApplyFog ();
+}
+
+void RenderSettings::SetFogMode (FogMode fm)
+{
+ m_FogMode = fm;
+ SetDirty();
+ ApplyFog ();
+}
+
+void RenderSettings::SetFogDensity (float fog)
+{
+ m_FogDensity = fog;
+ SetDirty();
+ ApplyFog ();
+}
+
+void RenderSettings::SetLinearFogStart (float d)
+{
+ m_LinearFogStart = d;
+ SetDirty();
+ ApplyFog ();
+}
+
+void RenderSettings::SetLinearFogEnd (float d)
+{
+ m_LinearFogEnd = d;
+ SetDirty();
+ ApplyFog ();
+}
+
+void RenderSettings::SetAmbientLight (const ColorRGBAf &col)
+{
+ m_AmbientLight = col;
+ ApplyFog ();
+ SetDirty();
+}
+
+#if UNITY_EDITOR
+void RenderSettings::SetAmbientLightNoDirty (const ColorRGBAf &col)
+{
+ m_AmbientLight = col;
+ ApplyFog ();
+}
+#endif
+
+void RenderSettings::SetFlareStrength (float flare)
+{
+ m_FlareStrength = flare;
+ SetDirty();
+ ApplyFlareAndHaloStrength();
+}
+
+void RenderSettings::SetFlareFadeSpeed (float time)
+{
+ m_FlareFadeSpeed = time;
+ SetDirty();
+ ApplyFlareAndHaloStrength();
+}
+
+void RenderSettings::SetHaloStrength (float halo)
+{
+ m_HaloStrength = halo;
+ SetDirty();
+ ApplyFlareAndHaloStrength();
+}
+
+IMPLEMENT_CLASS_HAS_POSTINIT (RenderSettings)
+IMPLEMENT_OBJECT_SERIALIZE (RenderSettings)
+GET_MANAGER (RenderSettings)
diff --git a/Runtime/Camera/RenderSettings.h b/Runtime/Camera/RenderSettings.h
new file mode 100644
index 0000000..ac9f791
--- /dev/null
+++ b/Runtime/Camera/RenderSettings.h
@@ -0,0 +1,94 @@
+#pragma once
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Graphics/GeneratedTextures.h"
+#include "Runtime/Modules/ExportModules.h"
+
+class Texture2D;
+
+class EXPORT_COREMODULE RenderSettings : public LevelGameManager
+{
+public:
+ RenderSettings (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~RenderSettings(); declared-by-macro
+ REGISTER_DERIVED_CLASS (RenderSettings, LevelGameManager)
+ DECLARE_OBJECT_SERIALIZE (RenderSettings)
+
+ void CheckConsistency ();
+ virtual void Reset ();
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ /// Get the scene ambient light
+ const ColorRGBAf& GetAmbientLight () const { return m_AmbientLight; }
+
+ /// Get the ambient light in active color space
+ ColorRGBAf GetAmbientLightInActiveColorSpace () const { return GammaToActiveColorSpace(m_AmbientLight); }
+
+ void SetAmbientLight (const ColorRGBAf &col);
+ #if UNITY_EDITOR
+ void SetAmbientLightNoDirty (const ColorRGBAf &col);
+ #endif
+
+ /// Get the default spotlight cookie.
+ /// This is used if no cookies have been assigned to a spotlight.
+ Texture2D *GetDefaultSpotCookie();
+
+ void SetupAmbient () const;
+
+ // Fog
+ const ColorRGBAf& GetFogColor() const { return m_FogColor; }
+ void SetFogColor (const ColorRGBAf& color);
+ bool GetUseFog () const { return m_Fog; }
+ void SetUseFog (bool fog);
+ void SetUseFogNoDirty (bool fog);
+ FogMode GetFogMode () const { return static_cast<FogMode>(m_FogMode); }
+ void SetFogMode (FogMode fm);
+ float GetFogDensity () const { return m_FogDensity; }
+ void SetFogDensity (float fog);
+ float GetLinearFogStart() const { return m_LinearFogStart; }
+ void SetLinearFogStart (float d);
+ float GetLinearFogEnd() const { return m_LinearFogEnd; }
+ void SetLinearFogEnd (float d);
+
+ Material *GetSkyboxMaterial () const { return m_SkyboxMaterial; }
+ void SetSkyboxMaterial (Material *mat);
+ float GetHaloStrength () const { return m_HaloStrength; }
+ void SetHaloStrength (float halo);
+
+ float GetFlareStrength () const { return m_FlareStrength; }
+ void SetFlareStrength (float flare);
+ float GetFlareFadeSpeed () const { return m_FlareFadeSpeed; }
+ void SetFlareFadeSpeed (float flare);
+
+ float CalcFogFactor (float distance) const;
+
+ static void InitializeClass ();
+ static void PostInitializeClass ();
+ static void CleanupClass ();
+
+private:
+ void ApplyFlareAndHaloStrength ();
+ void ApplyHaloTexture();
+ void ApplyFog ();
+
+private:
+ ColorRGBAf m_AmbientLight; ///< Scene ambient color.
+ float m_HaloStrength; ///< Strength of light halos range {0, 1}
+ float m_FlareStrength; ///< Strength of light flares range {0, 1}
+ float m_FlareFadeSpeed; ///< Fade time for a flare
+ bool m_Fog; ///< Use fog in the scene?
+ int m_FogMode; ///< enum { Linear=1, Exponential, Exp2 } Fog mode to use.
+ ColorRGBAf m_FogColor; ///< Fog color.
+ float m_LinearFogStart; ///< Starting distance for linear fog.
+ float m_LinearFogEnd; ///< End distance for linear fog.
+ float m_FogDensity; ///< Density for exponential fog.
+ PPtr<Texture2D> m_SpotCookie; ///< The default spotlight cookie
+ PPtr<Texture2D> m_HaloTexture; ///< The light halo texture
+ PPtr<Material> m_HaloMaterial;
+ PPtr<Material> m_SkyboxMaterial; ///< The material used to render the skybox
+};
+
+EXPORT_COREMODULE RenderSettings& GetRenderSettings ();
diff --git a/Runtime/Camera/Renderable.h b/Runtime/Camera/Renderable.h
new file mode 100644
index 0000000..ed56577
--- /dev/null
+++ b/Runtime/Camera/Renderable.h
@@ -0,0 +1,30 @@
+#ifndef RENDERABLE_H
+#define RENDERABLE_H
+
+class RenderTexture;
+namespace Unity { class Component; }
+struct CullResults;
+
+class Renderable {
+public:
+ virtual void RenderRenderable (const CullResults& cullResults) = 0;
+};
+
+typedef void RenderImageFilterFunc (Unity::Component* component, RenderTexture* source, RenderTexture* dest);
+
+struct ImageFilter
+{
+ Unity::Component* component;
+ RenderImageFilterFunc* renderFunc;
+
+ bool transformsToLDR;
+ bool afterOpaque;
+
+ ImageFilter (Unity::Component* inComponent, RenderImageFilterFunc* inRenderFunc, bool inLDR, bool inAfterOpaque)
+ : component(inComponent), renderFunc(inRenderFunc), afterOpaque(inAfterOpaque), transformsToLDR(inLDR){ }
+
+ bool operator==(const ImageFilter& o) const { return component==o.component && renderFunc==o.renderFunc; }
+ bool operator!=(const ImageFilter& o) const { return component!=o.component || renderFunc!=o.renderFunc; }
+};
+
+#endif
diff --git a/Runtime/Camera/Renderqueue.cpp b/Runtime/Camera/Renderqueue.cpp
new file mode 100644
index 0000000..7e1baf2
--- /dev/null
+++ b/Runtime/Camera/Renderqueue.cpp
@@ -0,0 +1,268 @@
+#include "UnityPrefix.h"
+#include "Renderqueue.h"
+#include "BaseRenderer.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Camera.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/shaderstate.h"
+#include "UnityScene.h"
+#include "RenderSettings.h"
+#include "RenderManager.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/Shader.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "Runtime/Misc/QualitySettings.h"
+#include "Runtime/Graphics/LightmapSettings.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "Runtime/Shaders/VBO.h"
+#include "CameraUtil.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Profiler/Profiler.h"
+#if UNITY_EDITOR
+#include "Editor/Src/LightmapVisualization.h"
+#endif
+
+PROFILER_INFORMATION(gWarmupShadersProfile, "Shader.WarmupAllShaders", kProfilerRender)
+
+
+static ShaderKeyword kLightmapOffKeyword = keywords::Create("LIGHTMAP_OFF");
+static ShaderKeyword kLightmapOnKeyword = keywords::Create("LIGHTMAP_ON");
+
+static ShaderKeyword kDirLightmapOffKeyword = keywords::Create("DIRLIGHTMAP_OFF");
+static ShaderKeyword kDirLightmapOnKeyword = keywords::Create("DIRLIGHTMAP_ON");
+
+
+void SetupObjectMatrix (const Matrix4x4f& m, int transformType)
+{
+ GfxDevice& device = GetGfxDevice();
+ device.SetWorldMatrixAndType( m.GetPtr(), TransformType(transformType) );
+}
+
+
+UInt32 GetCurrentRenderOptions ()
+{
+ UInt32 currentRenderOptions = 0;
+ if( GetQualitySettings().GetCurrent().softVegetation )
+ currentRenderOptions |= (1 << kShaderOptionSoftVegetation);
+ return currentRenderOptions;
+}
+
+bool CheckShouldRenderPass (int pass, Material& material)
+{
+ UInt32 currentRenderOptions = GetCurrentRenderOptions ();
+
+ ShaderPassType passType;
+ UInt32 passRenderOptions;
+ ShaderLab::Pass* slpass = material.GetShader()->GetShaderLabShader()->GetActiveSubShader().GetPass(pass);
+ slpass->GetPassOptions (passType, passRenderOptions);
+
+ // All options that a pass requires must be on
+ if( (currentRenderOptions & passRenderOptions) != passRenderOptions )
+ return false; // some options are off, skip this pass
+
+ return true;
+}
+
+bool SetupObjectLightmaps (const LightmapSettings& lightmapper, UInt32 lightmapIndex, const Vector4f& lightmapST, bool setMatrix)
+{
+ // setup object's lightmap info
+ LightmapSettings::TextureTriple lmTextures = lightmapper.GetLightmapTexture (lightmapIndex);
+#if UNITY_EDITOR
+ if (lmTextures.first.m_ID && GetLightmapVisualization().GetUseLightmapsForRendering())
+#else
+ if (lmTextures.first.m_ID)
+#endif
+ {
+ g_ShaderKeywords.Enable( kLightmapOnKeyword );
+ g_ShaderKeywords.Disable( kLightmapOffKeyword );
+
+ if (lightmapper.GetLightmapsMode() == LightmapSettings::kDirectionalLightmapsMode)
+ {
+ g_ShaderKeywords.Enable( kDirLightmapOnKeyword );
+ g_ShaderKeywords.Disable( kDirLightmapOffKeyword );
+ }
+ else
+ {
+ g_ShaderKeywords.Enable( kDirLightmapOffKeyword );
+ g_ShaderKeywords.Disable( kDirLightmapOnKeyword );
+ }
+
+
+ #define INIT_LIGHTMAP_TEX(texI, tex) GetGfxDevice().GetBuiltinParamValues().GetWritableTexEnvParam(texI).SetTextureInfo(tex, kTexDim2D, NULL)
+
+ //@TODO: optimize
+ TextureID indTex = lmTextures.second.m_ID ? lmTextures.second : builtintex::GetBlackTextureID();
+ TextureID thirdTex = lmTextures.third.m_ID ? lmTextures.third : builtintex::GetBlackTextureID();
+ INIT_LIGHTMAP_TEX(kShaderTexEnvUnityLightmap, lmTextures.first);
+ INIT_LIGHTMAP_TEX(kShaderTexEnvUnityLightmapInd, indTex);
+ INIT_LIGHTMAP_TEX(kShaderTexEnvUnityLightmapThird, thirdTex);
+
+ #undef INIT_LIGHTMAP_TEX
+
+ GetGfxDevice().GetBuiltinParamValues().SetVectorParam(kShaderVecUnityLightmapST, lightmapST);
+ if (setMatrix)
+ {
+ Matrix4x4f mat;
+ mat.SetIdentity();
+ mat.Get(0,0) = lightmapST[0];
+ mat.Get(1,1) = lightmapST[1];
+ mat.Get(0,3) = lightmapST[2];
+ mat.Get(1,3) = lightmapST[3];
+ GetGfxDevice().GetBuiltinParamValues().SetMatrixParam(kShaderMatLightmapMatrix, mat);
+ }
+ return true;
+ }
+ else
+ {
+ g_ShaderKeywords.Enable( kLightmapOffKeyword );
+ g_ShaderKeywords.Disable( kLightmapOnKeyword );
+
+ g_ShaderKeywords.Enable( kDirLightmapOffKeyword );
+ g_ShaderKeywords.Disable( kDirLightmapOnKeyword );
+ return false;
+ }
+}
+
+
+typedef std::set<const void*> PointerSet;
+
+
+static int WarmupOnePass (ShaderLab::Pass& pass, const ShaderLab::PropertySheet* props, DynamicVBO& vbo, PointerSet& passes, PointerSet& programs)
+{
+ if (pass.GetPassType() != ShaderLab::Pass::kPassNormal)
+ return 0;
+
+ // check if this pass already done (we clone quite a few passes via Fallback/UsePass)
+ if (passes.find(&pass) != passes.end())
+ return 0;
+ passes.insert (&pass);
+
+
+ // If we have vertex or fragment program, pick the one with larger permutation count.
+ const ShaderLab::Program* vp = pass.GetState().GetProgram(kShaderVertex);
+ const ShaderLab::Program* fp = pass.GetState().GetProgram(kShaderFragment);
+
+ const ShaderLab::Program* prog = NULL;
+ if (vp && fp)
+ prog = vp->GetSubProgramCount() > fp->GetSubProgramCount() ? vp : fp;
+ else if (vp)
+ prog = vp;
+ else if (fp)
+ prog = fp;
+
+ if (!prog)
+ {
+ // Fixed function; just apply shader state
+ const ChannelAssigns* channels = pass.ApplyPass (0, props);
+ vbo.DrawChunk (*channels);
+ GPU_TIMESTAMP();
+ return 1;
+ }
+ else
+ {
+ // Check if this program already done (we cache identical programs)
+ if (programs.find(prog) != programs.end())
+ return 0;
+ programs.insert (prog);
+
+ // Go over all shader permutations in the program
+ int combos = 0;
+ for (int i = 0; i < prog->GetSubProgramCount(); ++i)
+ {
+ g_ShaderKeywords = prog->GetSubProgramKeywords (i);
+ const ChannelAssigns* channels = pass.ApplyPass (0, props);
+ vbo.DrawChunk (*channels);
+ GPU_TIMESTAMP();
+ ++combos;
+ }
+ return combos;
+ }
+}
+
+static int WarmupOneShader (ShaderLab::IntShader& shader, DynamicVBO& vbo, PointerSet& passes, PointerSet& programs)
+{
+ ShaderKeywordSet savedKeywords = g_ShaderKeywords;
+
+ int combos = 0;
+
+ const ShaderLab::PropertySheet* props = shader.GetDefaultProperties();
+ for (size_t is = 0; is < shader.GetSubShaders().size(); ++is)
+ {
+ ShaderLab::SubShader& ss = shader.GetSubShader(is);
+ for (size_t ip = 0; ip < ss.GetValidPassCount(); ++ip)
+ {
+ combos += WarmupOnePass (*ss.GetPass(ip), props, vbo, passes, programs);
+ }
+ }
+
+ g_ShaderKeywords = savedKeywords;
+
+ return combos;
+}
+
+void WarmupAllShaders ()
+{
+ PROFILER_AUTO(gWarmupShadersProfile, NULL);
+
+ double t0 = GetTimeSinceStartup();
+ int shaderCount = 0;
+ int comboCount = 0;
+
+ // Get a dynamic VBO chunk with one triangle
+ struct FullVertex {
+ Vector3f vert;
+ Vector3f normal;
+ ColorRGBA32 color;
+ Vector2f uv1;
+ Vector2f uv2;
+ Vector4f tangent;
+ };
+
+ GfxDevice& device = GetGfxDevice ();
+ DynamicVBO& vbo = device.GetDynamicVBO();
+ FullVertex* vbPtr;
+ if (!vbo.GetChunk(
+ (1<<kShaderChannelVertex) |
+ (1<<kShaderChannelNormal) |
+ (1<<kShaderChannelColor) |
+ (1<<kShaderChannelTexCoord0) |
+ (1<<kShaderChannelTexCoord1) |
+ (1<<kShaderChannelTangent),
+ 3, 0,
+ DynamicVBO::kDrawTriangleStrip,
+ (void**)&vbPtr, NULL))
+ {
+ return;
+ }
+ // Set all vertex data to zero; this will ensure nothing is changed on screen
+ // but is enough for the driver to actually issue a draw call with a fully
+ // prepared shader/state.
+ memset (vbPtr, 0, 3*sizeof(FullVertex));
+ vbo.ReleaseChunk (3, 0);
+
+ DeviceMVPMatricesState saveMVPMatrices;
+ LoadFullScreenOrthoMatrix ();
+
+ PointerSet passes, programs;
+
+ // Go over all currently loaded shaders
+ std::vector<SInt32> allShaderObjects;
+ Object::FindAllDerivedObjects (ClassID (Shader), &allShaderObjects);
+ for (std::vector<SInt32>::iterator i = allShaderObjects.begin(); i != allShaderObjects.end(); ++i)
+ {
+ Shader* s = PPtr<Shader>(*i);
+ if (!s)
+ continue;
+ ShaderLab::IntShader* shader = s->GetShaderLabShader();
+ if (!shader)
+ continue;
+
+ int combos = WarmupOneShader (*shader, vbo, passes, programs);
+ ++shaderCount;
+ comboCount += combos;
+ }
+
+ double t1 = GetTimeSinceStartup();
+ printf_console ("Shader warmup: %i shaders %i combinations %.3fs\n", shaderCount, comboCount, (t1-t0));
+}
diff --git a/Runtime/Camera/Renderqueue.h b/Runtime/Camera/Renderqueue.h
new file mode 100644
index 0000000..766caac
--- /dev/null
+++ b/Runtime/Camera/Renderqueue.h
@@ -0,0 +1,23 @@
+#ifndef RENDERQUEUE_H
+#define RENDERQUEUE_H
+
+class Transform;
+class Vector4f;
+class Matrix4x4f;
+class LightmapSettings;
+namespace Unity { class Material; }
+
+
+void SetupObjectMatrix (const Matrix4x4f& m, int transformType);
+
+UInt32 GetCurrentRenderOptions ();
+bool CheckShouldRenderPass (int pass, Unity::Material& material);
+
+bool SetupObjectLightmaps (const LightmapSettings& lightmapper, UInt32 lightmapIndex, const Vector4f& lightmapST, bool setMatrix);
+
+
+// does a dummy render with all shaders & their combinations, so that driver actually creates them
+void WarmupAllShaders ();
+
+
+#endif
diff --git a/Runtime/Camera/SceneCulling.cpp b/Runtime/Camera/SceneCulling.cpp
new file mode 100644
index 0000000..ad5e4ed
--- /dev/null
+++ b/Runtime/Camera/SceneCulling.cpp
@@ -0,0 +1,412 @@
+#include "UnityPrefix.h"
+#include "SceneCulling.h"
+#include "CullingParameters.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "UmbraBackwardsCompatibility.h"
+#include "SceneNode.h"
+#include "Runtime/Geometry/AABB.h"
+
+
+using Umbra::OcclusionBuffer;
+
+static bool IsLayerDistanceCulled (UInt32 layer, const AABB& aabb, const SceneCullingParameters& params);
+static void ExtractUmbraCameraTransformFromCullingParameters (const CullingParameters& params, Umbra::CameraTransform& output);
+static void CullSceneWithLegacyUmbraDeprecated (SceneCullingParameters& cullingParams, CullingOutput& output, Umbra::Query* q, const Umbra_3_0::Tome* t);
+
+// Reduce the index list using Scene::IsNodeVisible.
+// Takes the index list as input and output, after the function the indices that are not visible will have been removed.
+static void ProcessIndexListIsNodeVisible (IndexList& list, const SceneNode* nodes, const AABB* bounds, const SceneCullingParameters& params)
+{
+ int size = list.size;
+ // Check layers and cull distances
+ int visibleCountNodes = 0;
+ for (int i = 0; i < size; ++i)
+ {
+ int index = list.indices[i];
+ const SceneNode& node = nodes[index];
+ const AABB& aabb = bounds[index];
+ if (IsNodeVisible(node, aabb, params))
+ list.indices[visibleCountNodes++] = index;
+ }
+
+ list.size = visibleCountNodes;
+}
+
+
+static void CullDynamicObjectsWithoutUmbra (IndexList& visibleObjects, const SceneCullingParameters& cullingParams, const AABB* aabbs, size_t count)
+{
+ int visibleIndex = 0;
+ for (int i=0;i<count;i++)
+ {
+ const AABB& bounds = aabbs[i];
+ if (IntersectAABBPlaneBounds(bounds, cullingParams.cullingPlanes, cullingParams.cullingPlaneCount))
+ visibleObjects[visibleIndex++] = i;
+ }
+
+ visibleObjects.size = visibleIndex;
+}
+
+static void CullDynamicObjectsUmbra (IndexList& visibleObjects, const AABB* aabbs, size_t count, OcclusionBuffer* occlusionBuffer)
+{
+ // TODO dynamic objects with legacy Tomes properly
+ int visibleIndex = 0;
+ for (int i=0;i<count;i++)
+ {
+ const AABB& bounds = aabbs[i];
+
+ Vector3f mn = bounds.GetMin();
+ Vector3f mx = bounds.GetMax();
+ Umbra::OcclusionBuffer::VisibilityTestResult result = occlusionBuffer->testAABBVisibility((Umbra::Vector3&)mn, (Umbra::Vector3&)mx);
+
+ if (result != OcclusionBuffer::OCCLUDED)
+ visibleObjects[visibleIndex++] = i;
+ }
+
+ visibleObjects.size = visibleIndex;
+}
+
+void CullSceneWithoutUmbra (const SceneCullingParameters& cullingParams, CullingOutput& output)
+{
+ for (int i=0;i<kVisibleListCount;i++)
+ {
+ CullDynamicObjectsWithoutUmbra (output.visible[i], cullingParams, cullingParams.renderers[i].bounds, cullingParams.renderers[i].rendererCount);
+ ProcessIndexListIsNodeVisible (output.visible[i], cullingParams.renderers[i].nodes, cullingParams.renderers[i].bounds, cullingParams);
+ }
+}
+
+void CullSceneWithUmbra (SceneCullingParameters& cullingParams, CullingOutput& output)
+{
+#if SUPPORT_BACKWARDS_COMPATIBLE_UMBRA
+ if (cullingParams.umbraTome.IsLegacyTome())
+ {
+ CullSceneWithLegacyUmbraDeprecated(cullingParams, output, cullingParams.umbraQuery, cullingParams.umbraTome.legacyTome);
+ return;
+ }
+#endif
+
+ DebugAssert(cullingParams.useOcclusionCulling);
+
+ const Umbra::Tome* tome = cullingParams.umbraTome.tome;
+ Umbra::QueryExt* query = cullingParams.umbraQuery;
+
+ Assert(tome != NULL);
+
+ Umbra::CameraTransform camera;
+ ExtractUmbraCameraTransformFromCullingParameters(cullingParams, camera);
+
+ Umbra::Query::ErrorCode e;
+
+ Umbra::UINT32 umbraFlags = cullingParams.umbraDebugFlags;
+ query->setDebugRenderer(cullingParams.umbraDebugRenderer);
+
+ // Legacy Umbra queries for PVS Tomes
+ Assert(tome->getStatistic(Umbra::Tome::STAT_PORTAL_DATA_SIZE) != 0);
+ e = query->queryPortalVisibility(umbraFlags, *output.umbraVisibility, camera);
+
+ //If camera is out of generated view volumes (case 554981), fall back to view frustum culling
+ if (e == Umbra::Query::ERROR_OUTSIDE_SCENE)
+ {
+ cullingParams.useOcclusionCulling = false;
+ cullingParams.useShadowCasterCulling = false;
+ CullSceneWithoutUmbra( cullingParams, output );
+ return;
+ }
+
+ // Process static objects with Unity specific culling (cullingMask / LOD etc)
+ IndexList& staticObjects = output.visible[kStaticRenderers];
+ staticObjects.size = output.umbraVisibility->getOutputObjects()->getSize();
+
+ ProcessIndexListIsNodeVisible(output.visible[kStaticRenderers], cullingParams.renderers[kStaticRenderers].nodes, cullingParams.renderers[kStaticRenderers].bounds, cullingParams);
+ output.umbraVisibility->getOutputObjects()->setSize(staticObjects.size);
+
+ // Process dynamic objects
+ for (int i=kDynamicRenderer;i<kVisibleListCount;i++)
+ {
+ CullDynamicObjectsUmbra (output.visible[i], cullingParams.renderers[i].bounds, cullingParams.renderers[i].rendererCount, output.umbraVisibility->getOutputBuffer());
+ ProcessIndexListIsNodeVisible(output.visible[i], cullingParams.renderers[i].nodes, cullingParams.renderers[i].bounds, cullingParams);
+ }
+}
+
+static void SplitCombinedDynamicList (Umbra::IndexList& indices, CullingOutput& sceneCullingOutput)
+{
+ int startIndices[kVisibleListCount];
+ startIndices[kStaticRenderers] = -1;
+ int offset = 0;
+ for (int t=kDynamicRenderer;t<kVisibleListCount;t++)
+ {
+ startIndices[t] = offset;
+ offset += sceneCullingOutput.visible[t].reservedSize;
+ }
+
+ int *indexPtr = indices.getPtr();
+ for (int i=0;i<indices.getSize();i++)
+ {
+ int index = indexPtr[i];
+
+ for (int t=kVisibleListCount-1; t>= 0 ;t--)
+ {
+ DebugAssert(t != kStaticRenderers);
+ if (index >= startIndices[t])
+ {
+ IndexList& indices = sceneCullingOutput.visible[t];
+ indices[indices.size++] = index - startIndices[t];
+ break;
+ }
+ }
+ }
+}
+
+// Unity has multiple dynamic bounding volume arrays.
+// Umbra's shadow caster culling needs a single list of all dynamic renderers
+static void GenerateCombinedDynamicList (const CullingOutput& sceneCullingOutput, const RendererCullData* renderers, dynamic_array<int>& indexList, dynamic_array<Vector3f>& minMaxBounds)
+{
+ size_t visibleSize = 0;
+ size_t totalSize = 0;
+ for (int t=kDynamicRenderer;t<kVisibleListCount;t++)
+ {
+ visibleSize += sceneCullingOutput.visible[t].size;
+ totalSize += sceneCullingOutput.visible[t].reservedSize;
+ }
+
+ indexList.resize_uninitialized(visibleSize);
+ minMaxBounds.resize_uninitialized(totalSize * 2);
+
+ // Make one big index list indexing into the combined bounding volume array
+ int index = 0;
+ int baseOffset = 0;
+ for (int t=kDynamicRenderer;t<kVisibleListCount;t++)
+ {
+ int* visibleDynamic = sceneCullingOutput.visible[t].indices;
+ for (int i=0;i<sceneCullingOutput.visible[t].size;i++)
+ indexList[index++] = visibleDynamic[i] + baseOffset;
+
+ baseOffset += sceneCullingOutput.visible[t].reservedSize;
+ }
+
+ // Create one big bounding volume array
+ index = 0;
+ for (int t=kDynamicRenderer;t<kVisibleListCount;t++)
+ {
+ const AABB* aabbs = renderers[t].bounds;
+
+ Assert(renderers[t].rendererCount == sceneCullingOutput.visible[t].reservedSize);
+ for (int i=0;i<renderers[t].rendererCount;i++)
+ {
+ minMaxBounds[index++] = aabbs[i].GetMin();
+ minMaxBounds[index++] = aabbs[i].GetMax();
+ }
+ }
+}
+
+void CullShadowCastersWithUmbra (const SceneCullingParameters& cullingParams, CullingOutput& output)
+{
+ DebugAssert(cullingParams.useShadowCasterCulling && cullingParams.useOcclusionCulling);
+
+ Assert( cullingParams.umbraTome.tome != NULL);
+ Umbra::QueryExt* query = (Umbra::QueryExt*)cullingParams.umbraQuery;
+
+ Umbra::CameraTransform camera;
+ ExtractUmbraCameraTransformFromCullingParameters(cullingParams, camera);
+
+ const Umbra::Visibility* inputSceneVisibility = cullingParams.sceneVisbilityForShadowCulling->umbraVisibility;
+
+ dynamic_array<int> visibleSceneIndexListCombined (kMemTempAlloc);
+ dynamic_array<Vector3f> dynamicBounds (kMemTempAlloc);
+
+ ////@TODO: This extra copying could maybe be removed or moved to not be per light?
+ GenerateCombinedDynamicList (*cullingParams.sceneVisbilityForShadowCulling, cullingParams.renderers, visibleSceneIndexListCombined, dynamicBounds);
+
+ size_t totalDynamicCount = dynamicBounds.size() / 2;
+ size_t totalStaticCount = cullingParams.renderers[kStaticRenderers].rendererCount;
+
+ int* dynamicCasterIndices;
+ ALLOC_TEMP(dynamicCasterIndices, int, totalDynamicCount);
+
+ // The output visible static & dynamic caster index lists
+ Umbra::IndexList staticCasterList (output.visible[kStaticRenderers].indices, totalStaticCount);
+ Umbra::IndexList dynamicCasterList(dynamicCasterIndices, totalDynamicCount);
+
+ // The current scene visibility (input for umbra occlusion culling)
+ const Umbra::IndexList sceneVisibleDynamicIndexList (visibleSceneIndexListCombined.begin(), visibleSceneIndexListCombined.size(), visibleSceneIndexListCombined.size());
+
+
+ query->setDebugRenderer(cullingParams.umbraDebugRenderer);
+
+ // TODO: this can be done right after the visibility query and only needs to be done once
+ // per camera position
+ // TODO: store shadowCuller so that it can be shared across threads
+ Umbra::ShadowCullerExt* shadowCuller;
+ ALLOC_TEMP(shadowCuller, Umbra::ShadowCullerExt, 1);
+ new (shadowCuller) Umbra::ShadowCullerExt(
+ cullingParams.umbraTome.tome,
+ inputSceneVisibility,
+ (Umbra::Vector3&)cullingParams.lightDir,
+ (const Umbra::Vector3*)dynamicBounds.data(),
+ totalDynamicCount,
+ &sceneVisibleDynamicIndexList);
+
+ // The last two parameters: jobIdx, numTotalJobs. These can be called from mulitple threads
+ query->queryStaticShadowCasters (*shadowCuller, staticCasterList, 0, 1);
+ query->queryDynamicShadowCasters(*shadowCuller, dynamicCasterList, (const Umbra::Vector3*)dynamicBounds.data(), totalDynamicCount);
+
+ output.visible[kStaticRenderers].size = staticCasterList.getSize();
+ SplitCombinedDynamicList (dynamicCasterList, output);
+
+ for (int i=0;i<kVisibleListCount;i++)
+ ProcessIndexListIsNodeVisible (output.visible[i], cullingParams.renderers[i].nodes, cullingParams.renderers[i].bounds, cullingParams);
+}
+
+
+bool IsNodeVisible (const SceneNode& node, const AABB& aabb, const SceneCullingParameters& params)
+{
+ UInt32 layermask = 1 << node.layer;
+ if ((layermask & params.cullingMask) == 0)
+ return false;
+
+ // Static renderers are not deleted from the renderer objects list.
+ // This is because we ensure that static objects come first and the pvxIndex in umbra matches the index in m_RendererNodes array.
+ // However the layerMask is set to 0 thus it should be culled at this point
+ if (node.renderer == NULL)
+ return false;
+
+ // Check if node was deleted but not removed from list yet.
+ if (node.disable)
+ return false;
+
+ if (node.lodIndexMask != 0)
+ {
+ DebugAssert(node.lodGroup < params.lodGroupCount);
+ if ((params.lodMasks[node.lodGroup] & node.lodIndexMask) == 0)
+ return false;
+ }
+
+ if (IsLayerDistanceCulled(node.layer, aabb, params))
+ return false;
+
+ return true;
+}
+
+
+static bool IsLayerDistanceCulled (UInt32 layer, const AABB& aabb, const SceneCullingParameters& params)
+{
+ switch (params.layerCull)
+ {
+ case CullingParameters::kLayerCullPlanar:
+ {
+ Assert(params.cullingPlaneCount == kPlaneFrustumNum);
+ Plane farPlane = params.cullingPlanes[kPlaneFrustumFar];
+ farPlane.distance = params.layerFarCullDistances[layer];
+ return !IntersectAABBPlaneBounds (aabb, &farPlane, 1);
+ }
+ case CullingParameters::kLayerCullSpherical:
+ {
+ float cullDist = params.layerFarCullDistances[layer];
+ if (cullDist == 0.0f)
+ return false;
+ return (SqrMagnitude(aabb.GetCenter() - params.position) > Sqr(cullDist));
+ }
+ default:
+ return false;
+ }
+}
+
+void ExtractUmbraCameraTransformFromCullingParameters (const CullingParameters& params, Umbra::CameraTransform& output)
+{
+ if (params.cullingPlaneCount > 6)
+ output.setUserClipPlanes((Umbra::Vector4*)&params.cullingPlanes[6], params.cullingPlaneCount-6);
+ output.set((Umbra::Matrix4x4&)params.worldToClipMatrix, (Umbra::Vector3&)params.position);
+}
+
+#if SUPPORT_BACKWARDS_COMPATIBLE_UMBRA
+
+static void CullDynamicObjectsUmbraDeprecated (IndexList& visibleObjects, const AABB* aabbs, size_t count, Umbra_3_0::Region* region)
+{
+ // TODO dynamic objects with legacy Tomes properly
+ int visibleIndex = 0;
+ for (int i=0;i<count;i++)
+ {
+ const AABB& bounds = aabbs[i];
+
+ Vector3f mn = bounds.GetMin();
+ Vector3f mx = bounds.GetMax();
+
+ bool isVisible = region->isAABBVisible((Umbra_3_0::Vector3&)mn, (Umbra_3_0::Vector3&)mx);
+
+ if (isVisible)
+ visibleObjects[visibleIndex++] = i;
+ }
+
+ visibleObjects.size = visibleIndex;
+}
+
+static void CullDynamicObjectsWithoutUmbraInOut (IndexList& visibleObjects, const SceneCullingParameters& cullingParams, const AABB* aabbs)
+{
+ int visibleIndex = 0;
+ for (int i=0;i<visibleObjects.size;i++)
+ {
+ int index = visibleObjects[i];
+ const AABB& bounds = aabbs[index];
+ if (IntersectAABBPlaneBounds(bounds, cullingParams.cullingPlanes, cullingParams.cullingPlaneCount))
+ visibleObjects[visibleIndex++] = index;
+ }
+
+ visibleObjects.size = visibleIndex;
+}
+
+static void CullSceneWithLegacyUmbraDeprecated (SceneCullingParameters& cullingParams, CullingOutput& output, Umbra::Query* q, const Umbra_3_0::Tome* tome)
+{
+ ///@TODO: Review if output.umbraVisibility makes any sense????
+
+ Umbra_3_0::QueryExt* query = (Umbra_3_0::QueryExt*)q;
+
+ query->init(tome);
+
+ Umbra_3_0::Region* region;
+ ALLOC_TEMP(region, Umbra_3_0::Region, 1);
+ region->init();
+ query->setVisibilityRegionOutput(region);
+
+
+ Umbra_3_0::CameraTransform camera;
+ if (cullingParams.cullingPlaneCount > 6)
+ camera.setUserClipPlanes((Umbra_3_0::Vector4*)&cullingParams.cullingPlanes[6], cullingParams.cullingPlaneCount-6);
+
+ camera.set((Umbra_3_0::Matrix4x4&)cullingParams.worldToClipMatrix, (Umbra_3_0::Vector3&)cullingParams.position);
+
+ Umbra_3_0::IndexList staticObjs(output.visible[kStaticRenderers].indices, output.visible[kStaticRenderers].reservedSize);
+
+ Umbra_3_0::Query::ErrorCode e;
+
+ int flags = 0;
+ if (tome->getStatistic(Umbra_3_0::Tome::STAT_PORTAL_DATA_SIZE))
+ flags |= Umbra_3_0::Query::QUERYFLAG_PORTAL;
+ else // TODO: handle other cases?
+ flags |= Umbra_3_0::Query::QUERYFLAG_PVS;
+
+ e = query->queryCameraVisibility(flags, &staticObjs, NULL, camera);
+ //Fallback to view frustum culling if queryCameraVisibility failed
+ if (e != Umbra_3_0::Query::ERROR_OK)
+ {
+ cullingParams.useOcclusionCulling = false;
+ cullingParams.useShadowCasterCulling = false;
+ CullSceneWithoutUmbra(cullingParams, output);
+ return;
+ }
+ output.visible[kStaticRenderers].size = staticObjs.getSize();
+
+ // camera can move outside view volume Assert(e == Umbra::Query::ERROR_OK);
+ // Extract visible indices
+ if (!query->supportVFCulling())
+ CullDynamicObjectsWithoutUmbraInOut (output.visible[kStaticRenderers], cullingParams, cullingParams.renderers[kStaticRenderers].bounds);
+
+ //output.umbraVisibility->getOutputObjects()->setSize(output.visible[kStaticRenderers].size);
+
+ ProcessIndexListIsNodeVisible(output.visible[kStaticRenderers], cullingParams.renderers[kStaticRenderers].nodes, cullingParams.renderers[kStaticRenderers].bounds, cullingParams);
+
+ for (int i=kDynamicRenderer;i<kVisibleListCount;i++)
+ {
+ CullDynamicObjectsUmbraDeprecated (output.visible[i], cullingParams.renderers[i].bounds, cullingParams.renderers[i].rendererCount, region);
+ ProcessIndexListIsNodeVisible(output.visible[i], cullingParams.renderers[i].nodes, cullingParams.renderers[i].bounds, cullingParams);
+ }
+}
+#endif // SUPPORT_BACKWARDS_COMPATIBLE_UMBRA \ No newline at end of file
diff --git a/Runtime/Camera/SceneCulling.h b/Runtime/Camera/SceneCulling.h
new file mode 100644
index 0000000..baae99e
--- /dev/null
+++ b/Runtime/Camera/SceneCulling.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "Runtime/Utilities/dynamic_array.h"
+
+struct CullingOutput;
+struct SceneCullingParameters;
+class IntermediateRenderer;
+struct SceneNode;
+class AABB;
+class Sphere;
+class IntermediateRenderers;
+
+void CullSceneWithUmbra (SceneCullingParameters& cullingParams, CullingOutput& output);
+void CullSceneWithoutUmbra (const SceneCullingParameters& cullingParams, CullingOutput& output);
+
+bool IsNodeVisible (const SceneNode& node, const AABB& aabb, const SceneCullingParameters& params);
+
+void CullShadowCastersWithUmbra (const SceneCullingParameters& cullingParams, CullingOutput& output); \ No newline at end of file
diff --git a/Runtime/Camera/SceneNode.h b/Runtime/Camera/SceneNode.h
new file mode 100644
index 0000000..a7e9655
--- /dev/null
+++ b/Runtime/Camera/SceneNode.h
@@ -0,0 +1,23 @@
+#pragma once
+
+class BaseRenderer;
+
+struct SceneNode
+{
+ SceneNode() :
+ renderer(NULL), layer(0), pvsHandle(-1), lodGroup(0), lodIndexMask(0),
+ needsCullCallback(false), dirtyAABB(false), disable(false) {}
+
+ BaseRenderer* renderer;
+ UInt32 layer;
+ SInt32 pvsHandle;
+ UInt32 lodGroup;
+ UInt32 lodIndexMask;
+ bool needsCullCallback;
+ bool dirtyAABB;
+ ///@TODO: Maybe we can use Renderer* = NULL instead, we already set this to null for static objects...
+ bool disable;
+};
+
+typedef int SceneHandle;
+const int kInvalidSceneHandle = -1;
diff --git a/Runtime/Camera/SceneSettings.cpp b/Runtime/Camera/SceneSettings.cpp
new file mode 100644
index 0000000..9ee2d9d
--- /dev/null
+++ b/Runtime/Camera/SceneSettings.cpp
@@ -0,0 +1,140 @@
+#include "UnityPrefix.h"
+#include "SceneSettings.h"
+#include "UnityScene.h"
+#include "Runtime/Camera/OcclusionPortal.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "UmbraBackwardsCompatibility.h"
+
+SceneSettings::SceneSettings (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+SceneSettings::~SceneSettings ()
+{
+ if (GetScene().GetUmbraTome () == m_UmbraTome)
+ InvalidatePVSOnScene();
+
+ Cleanup();
+}
+
+void SceneSettings::InitializeClass ()
+{
+ Scene::InitializeClass();
+}
+
+void SceneSettings::CleanupClass ()
+{
+ Scene::CleanupClass();
+}
+
+void SceneSettings::Cleanup ()
+{
+ CleanupUmbraTomeData(m_UmbraTome);
+}
+
+void SceneSettings::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad(awakeMode);
+
+ InvalidatePVSOnScene();
+}
+
+int SceneSettings::GetUmbraTotalDataSize() const
+{
+ return m_UmbraTome.tome ? m_UmbraTome.tome->getStatistic(Umbra::Tome::STAT_TOTAL_DATA_SIZE) : 0;
+}
+
+int SceneSettings::GetPortalDataSize() const
+{
+ return m_UmbraTome.tome ? m_UmbraTome.tome->getStatistic(Umbra::Tome::STAT_PORTAL_DATA_SIZE) : 0;
+}
+
+void SceneSettings::InvalidatePVSOnScene ()
+{
+ GetScene().CleanupPVSAndRequestRebuild();
+}
+
+template<class TransferFunction> inline
+void SceneSettings::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ dynamic_array<UInt8> tempPVSData;
+ if (transfer.IsWriting () && m_UmbraTome.tome != NULL)
+ {
+ UInt8* tomeData = (UInt8*)(m_UmbraTome.tome);
+ tempPVSData.assign(tomeData, tomeData + UMBRA_TOME_METHOD(m_UmbraTome, getSize()));
+
+ // Strip tomeData when building player data
+// if (transfer.IsWritingGameReleaseData())
+// {
+// Umbra::Tome* tempTomeData = (Umbra::Tome*)tempPVSData.begin();
+// Umbra::Tome::stripDebugInfo(tempTomeData);
+// tempPVSData.resize_uninitialized(tempTomeData->getSize());
+// }
+ }
+
+ ///@TODO: make a fast path for loading tome data when we know that the data version matches. (Just alllocate&read tome data directly...)
+ transfer.Transfer(tempPVSData, "m_PVSData");
+
+ if (transfer.DidReadLastProperty ())
+ {
+ Cleanup();
+
+ // In free editor don't load occlusion data since there is no way to clear or rebake it.
+ if (GetBuildSettings().hasPROVersion && !tempPVSData.empty())
+ m_UmbraTome = LoadUmbraTome(tempPVSData.begin(), tempPVSData.size());
+ }
+
+ TRANSFER(m_PVSObjectsArray);
+ TRANSFER(m_PVSPortalsArray);
+
+ TRANSFER_EDITOR_ONLY(m_OcclusionBakeSettings);
+}
+
+template<class TransferFunction> inline
+void OcclusionBakeSettings::Transfer (TransferFunction& transfer)
+{
+ TRANSFER(smallestOccluder);
+ TRANSFER(smallestHole);
+ TRANSFER(backfaceThreshold);
+}
+
+
+/*
+ CheckConsistency...
+
+void ComputationParameterGUIChange()
+{
+ m_SmallestOccluder = Mathf.Max(m_SmallestOccluder, 0.1F);
+ StaticOcclusionCullingVisualization.smallestOccluder = m_SmallestOccluder;
+
+ m_SmallestHole = Mathf.Max(m_SmallestHole, 0.001F);
+ m_SmallestHole = Mathf.Min(m_SmallestHole, m_SmallestOccluder);
+ StaticOcclusionCullingVisualization.smallestHole = m_SmallestHole;
+*/
+
+void SceneSettings::SetUmbraTome(const dynamic_array<PPtr<Renderer> >& pvsObjectsArray, const dynamic_array<PPtr<OcclusionPortal> >& portalArray, const UInt8* visibilitybuffer, int size)
+{
+ InvalidatePVSOnScene();
+ Cleanup();
+
+ m_PVSObjectsArray = pvsObjectsArray;
+ m_PVSPortalsArray = portalArray;
+ if (size != 0)
+ m_UmbraTome.tome = Umbra::TomeLoader::loadFromBuffer(visibilitybuffer, size);
+ else
+ m_UmbraTome = UmbraTomeData();
+
+ SetDirty();
+}
+
+
+GET_MANAGER(SceneSettings)
+
+IMPLEMENT_CLASS_HAS_INIT (SceneSettings)
+IMPLEMENT_OBJECT_SERIALIZE (SceneSettings)
diff --git a/Runtime/Camera/SceneSettings.h b/Runtime/Camera/SceneSettings.h
new file mode 100644
index 0000000..8193f76
--- /dev/null
+++ b/Runtime/Camera/SceneSettings.h
@@ -0,0 +1,94 @@
+#ifndef UNITYSCENE_SETTINGS_H
+#define UNITYSCENE_SETTINGS_H
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "UmbraTomeData.h"
+
+class Renderer;
+class OcclusionPortal;
+
+const float defaultSmallestOccluderValue = 5.0F;;
+const float defaultSmallestHoleValue = 0.25F;
+const float defaultBackfaceThresholdValue = 100.0F;
+
+struct OcclusionBakeSettings
+{
+ float smallestOccluder;
+ float smallestHole;
+ float backfaceThreshold;
+
+ OcclusionBakeSettings()
+ {
+ smallestOccluder = defaultSmallestOccluderValue;
+ smallestHole = defaultSmallestHoleValue;
+ backfaceThreshold = defaultBackfaceThresholdValue;
+ }
+
+ void SetDefaultOcclusionBakeSettings()
+ {
+ smallestOccluder = defaultSmallestOccluderValue;
+ smallestHole = defaultSmallestHoleValue;
+ backfaceThreshold = defaultBackfaceThresholdValue;
+ }
+
+ friend bool operator == (const OcclusionBakeSettings& lhs, const OcclusionBakeSettings& rhs)
+ {
+ return memcmp(&lhs, &rhs, sizeof(lhs)) == 0;
+ }
+
+ DECLARE_SERIALIZE(OcclusionBakeSettings);
+};
+
+class SceneSettings : public LevelGameManager
+{
+public:
+
+ REGISTER_DERIVED_CLASS (SceneSettings, LevelGameManager)
+ DECLARE_OBJECT_SERIALIZE (SceneSettings)
+
+ SceneSettings (MemLabelId label, ObjectCreationMode mode);
+ // ~SceneSettings (); declared-by-macro
+
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+
+ void SetUmbraTome (const dynamic_array<PPtr<Renderer> >& pvsObjectsArray, const dynamic_array<PPtr<OcclusionPortal> >& portalArray, const UInt8* visibilitybuffer, int size);
+
+#if UNITY_EDITOR
+ const OcclusionBakeSettings& GetOcclusionBakeSettings () const { return m_OcclusionBakeSettings; }
+ void SetDefaultOcclusionBakeSettings () { SetDirty(); m_OcclusionBakeSettings.SetDefaultOcclusionBakeSettings(); }
+ OcclusionBakeSettings& GetOcclusionBakeSettingsSetDirty () { SetDirty(); return m_OcclusionBakeSettings; }
+#endif
+
+
+ const dynamic_array<PPtr<Renderer> >& GetPVSObjectArray () { return m_PVSObjectsArray; }
+ const dynamic_array<PPtr<OcclusionPortal> >& GetPortalsArray () { return m_PVSPortalsArray; }
+
+ const UmbraTomeData& GetUmbraTome () { return m_UmbraTome; }
+
+ int GetUmbraTotalDataSize () const;
+ int GetPortalDataSize () const;
+
+ void InvalidatePVSOnScene ();
+
+private:
+
+ void Cleanup ();
+
+ UmbraTomeData m_UmbraTome;
+ dynamic_array<PPtr<Renderer> > m_PVSObjectsArray;
+ dynamic_array<PPtr<OcclusionPortal> > m_PVSPortalsArray;
+
+ #if UNITY_EDITOR
+ OcclusionBakeSettings m_OcclusionBakeSettings;
+ #endif
+};
+
+SceneSettings& GetSceneSettings ();
+
+
+#endif
diff --git a/Runtime/Camera/ShaderReplaceData.h b/Runtime/Camera/ShaderReplaceData.h
new file mode 100644
index 0000000..51863f0
--- /dev/null
+++ b/Runtime/Camera/ShaderReplaceData.h
@@ -0,0 +1,14 @@
+#pragma once
+
+class Shader;
+
+// Shader replacement
+struct ShaderReplaceData
+{
+ Shader* replacementShader;
+ int replacementTagID;
+ bool replacementTagSet;
+
+
+ ShaderReplaceData () { replacementShader = NULL; replacementTagID = 0; replacementTagSet = false; }
+}; \ No newline at end of file
diff --git a/Runtime/Camera/ShadowCulling.cpp b/Runtime/Camera/ShadowCulling.cpp
new file mode 100644
index 0000000..f245547
--- /dev/null
+++ b/Runtime/Camera/ShadowCulling.cpp
@@ -0,0 +1,885 @@
+#include "UnityPrefix.h"
+#include "ShadowCulling.h"
+#include "Runtime/Camera/Light.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Geometry/Sphere.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Misc/QualitySettings.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "Runtime/Camera/BaseRenderer.h"
+#include "Runtime/Camera/CullResults.h"
+#include "Runtime/Camera/SceneCulling.h"
+#include "Runtime/Camera/SceneNode.h"
+#include "Runtime/Camera/UnityScene.h"
+#include "Runtime/Geometry/BoundingUtils.h"
+
+#include "SceneCulling.h"
+
+PROFILER_INFORMATION(gShadowsCullCastersPoint, "Shadows.CullCastersPoint", kProfilerRender)
+PROFILER_INFORMATION(gShadowsCullCastersSpot, "Shadows.CullCastersSpot", kProfilerRender)
+PROFILER_INFORMATION(gShadowsCullCastersDir, "Shadows.CullCastersDir", kProfilerRender)
+
+static const float kShadowFadeRange = 0.2f;
+
+static bool AddShadowCasterCullPlane(const Plane frustumCullPlanes[6], const Matrix4x4f& clipToWorld, const Vector3f& centerWorld, float nearPlaneScale, float farPlaneScale, int sideA, int sideB, int sideNeighbor, LightType lightType, const Vector3f& lightVec, Plane& outPlane)
+{
+ static Vector3f sideOffsets[] = {
+ Vector3f(-1.0f, 0.0f, 0.0f), // kPlaneFrustumLeft
+ Vector3f( 1.0f, 0.0f, 0.0f), // kPlaneFrustumRight
+ Vector3f( 0.0f, -1.0f, 0.0f), // kPlaneFrustumBottom
+ Vector3f( 0.0f, 1.0f, 0.0f), // kPlaneFrustumTop
+ Vector3f( 0.0f, 0.0f, -1.0f), // kPlaneFrustumNear
+ Vector3f( 0.0f, 0.0f, 1.0f) // kPlaneFrustumFar
+ };
+
+ const Plane& planeA = frustumCullPlanes[sideA];
+ const Plane& planeB = frustumCullPlanes[sideB];
+ Vector3f edgeNormal = planeA.GetNormal() + planeB.GetNormal(); // No need to normalize
+ Vector3f edgeCenter = sideOffsets[sideA] + sideOffsets[sideB];
+ Vector3f edgeDir = sideOffsets[sideNeighbor];
+ Vector3f posWorld1, posWorld2;
+ Vector3f posLocal1 = edgeCenter - edgeDir;
+ Vector3f posLocal2 = edgeCenter + edgeDir;
+ clipToWorld.PerspectiveMultiplyPoint3( posLocal1, posWorld1 );
+ clipToWorld.PerspectiveMultiplyPoint3( posLocal2, posWorld2 );
+ float scale1 = (posLocal1.z < 0.0f) ? nearPlaneScale : farPlaneScale;
+ float scale2 = (posLocal2.z < 0.0f) ? nearPlaneScale : farPlaneScale;
+ posWorld1 = centerWorld + (posWorld1 - centerWorld) * scale1;
+ posWorld2 = centerWorld + (posWorld2 - centerWorld) * scale2;
+
+ if( lightType == kLightDirectional )
+ {
+ /////@TODO: RUnning around in debug mode this seems to hit quite often in the shantytown scene...
+ Vector3f edgeDirWorld = (posWorld2 != posWorld1) ? Normalize( posWorld2 - posWorld1 ) : Vector3f(0.0f, 0.0f, 0.0f);
+ Vector3f clipNormal = Cross( edgeDirWorld, lightVec );
+ float len = Magnitude( clipNormal );
+ // Discard plane if edge is almost parallel with light dir
+ if (len < 0.001f)
+ return false;
+ clipNormal /= len;
+
+ // Flip the normal if we got the wrong direction
+ if( Dot( clipNormal, edgeNormal ) < 0.0f )
+ {
+ clipNormal *= -1.0f;
+ }
+ outPlane.SetNormalAndPosition( clipNormal, posWorld1 );
+ }
+ else
+ {
+ // We know the light is not too close to either plane
+ outPlane.Set3Points( posWorld1, posWorld2, lightVec );
+
+ if( Dot( outPlane.GetNormal(), edgeNormal ) < 0.0f )
+ {
+ outPlane *= -1.0f;
+ }
+ }
+ return true;
+}
+
+void CalculateShadowCasterCull(const Plane frustumCullPlanes[6], const Matrix4x4f& clipToWorld,
+ const Vector3f& centerWorld, float nearPlaneScale, float farPlaneScale,
+ LightType lightType, const Vector3f& lightVec, ShadowCasterCull& result,
+ const bool alreadyTested[kPlaneFrustumNum])
+{
+ // Create clip volume from the distant planes and silhouette edges of camera's frustum (as seen by the light)
+ // Maximum planes generated should be 10, but we check at runtime to be sure
+ result.planeCount = 0;
+ bool facingLight[kPlaneFrustumNum];
+ bool enablePlane[kPlaneFrustumNum];
+ float epsilon = 0.01f;
+ for( int i = 0; i < kPlaneFrustumNum; ++i )
+ {
+ Plane plane = frustumCullPlanes[i];
+ if( lightType == kLightDirectional )
+ {
+ // Compare light direction and normal
+ float d = Dot( plane.GetNormal(), lightVec );
+ facingLight[i] = d < 0.0f;
+ enablePlane[i] = true;
+ }
+ else
+ {
+ float dist = plane.GetDistanceToPoint( lightVec );
+ facingLight[i] = false;
+ enablePlane[i] = true;
+ if( dist > -epsilon )
+ {
+ // Light is inside or just outside the frustum
+ facingLight[i] = true;
+ if ( dist < epsilon )
+ {
+ // Point is too close to the plane to care about edges
+ enablePlane[i] = false;
+ }
+ if ( dist < 0.0f )
+ {
+ // Push plane slightly so the frustum encloses the light
+ plane.distance -= dist;
+ }
+ }
+ }
+ if( facingLight[i] && !alreadyTested[i] )
+ {
+ result.planes[result.planeCount++] = plane;
+ if( result.planeCount == ShadowCasterCull::kMaxPlanes )
+ return;
+ }
+ }
+
+ // Sides in running order (each plane touches previous)
+ static int camera4Sides[] = {
+ kPlaneFrustumLeft,
+ kPlaneFrustumBottom,
+ kPlaneFrustumRight,
+ kPlaneFrustumTop
+ };
+ static int cameraNearFar[] = {
+ kPlaneFrustumNear,
+ kPlaneFrustumFar
+ };
+
+ // Iterate over diagonal edges
+ for( int edge = 0; edge < 4; ++edge )
+ {
+ int sideA = camera4Sides[edge];
+ // If we already tested side A, we also tested B
+ if( alreadyTested[sideA] )
+ continue;
+ int sideB = camera4Sides[(edge + 1) % 4];
+ int sideNeighbor = kPlaneFrustumFar;
+ if( facingLight[sideA] != facingLight[sideB] &&
+ enablePlane[sideA] && enablePlane[sideB] )
+ {
+ Plane& plane = result.planes[result.planeCount];
+ bool success = AddShadowCasterCullPlane(frustumCullPlanes, clipToWorld,
+ centerWorld, nearPlaneScale, farPlaneScale,
+ sideA, sideB, sideNeighbor, lightType, lightVec, plane);
+ result.planeCount += success ? 1 : 0;
+ if( result.planeCount == ShadowCasterCull::kMaxPlanes )
+ return;
+ }
+ }
+
+ // Iterate over near and far edges
+ for( int nearFar = 0; nearFar < 2; ++nearFar )
+ {
+ int sideA = cameraNearFar[nearFar];
+ // If we already tested side A, we also tested B
+ if( alreadyTested[sideA] )
+ continue;
+ for( int edge = 0; edge < 4; ++edge )
+ {
+ int sideB = camera4Sides[edge];
+ int sideNeighbor = camera4Sides[(edge + 1) % 4];
+ if( facingLight[sideA] != facingLight[sideB] &&
+ enablePlane[sideA] && enablePlane[sideB] )
+ {
+ Plane& plane = result.planes[result.planeCount];
+ bool success = AddShadowCasterCullPlane(frustumCullPlanes, clipToWorld,
+ centerWorld, nearPlaneScale, farPlaneScale,
+ sideA, sideB, sideNeighbor, lightType, lightVec, plane);
+ result.planeCount += success ? 1 : 0;
+ if( result.planeCount == ShadowCasterCull::kMaxPlanes )
+ return;
+ }
+ }
+ }
+}
+
+static const bool s_AlreadyTestedNone[kPlaneFrustumNum] = { false, false, false, false, false, false };
+
+void CalculateShadowCasterCull(const Plane frustumCullPlanes[6], const Matrix4x4f& clipToWorld, const Vector3f& centerWorld, float nearPlaneScale, float farPlaneScale, LightType lightType, const Transform& trans, ShadowCasterCull& result)
+{
+ // Get position for positional lights and direction for directional lights
+ Vector3f lightVec;
+ if( lightType == kLightDirectional )
+ {
+ lightVec = trans.TransformDirection( Vector3f::zAxis );
+ }
+ else
+ {
+ Assert( lightType == kLightPoint || lightType == kLightSpot );
+ lightVec = trans.GetPosition();
+ }
+ CalculateShadowCasterCull(frustumCullPlanes, clipToWorld, centerWorld, nearPlaneScale, farPlaneScale, lightType, lightVec, result, s_AlreadyTestedNone);
+}
+
+static bool CanUseProjectionForShadows (const Matrix4x4f& clipToWorld, float farNearRatio, const Camera& camera, const Vector3f& cameraPos)
+{
+ // Check if eye position is the focal point of the camera projection
+ // and we can scale near plane from the eye pos to get the far plane.
+ // This works for asymmetric projections, not for oblique etc.
+ Vector3f cameraFrustum[8];
+ GetFrustumPoints(clipToWorld, cameraFrustum);
+ for (int i = 0; i < 4; i++)
+ {
+ const Vector3f& nearPos = cameraFrustum[i];
+ const Vector3f& farPos = cameraFrustum[i + 4];
+ Vector3f derivedFar = cameraPos + (nearPos - cameraPos) * farNearRatio;
+ float diff = SqrMagnitude(derivedFar - farPos);
+ float length = SqrMagnitude(farPos - nearPos);
+ if (!(diff <= length * 0.01f))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+void SetupShadowCullData (Camera& camera, const Vector3f& cameraPos, const ShaderReplaceData& shaderReplaceData, const SceneCullingParameters* sceneCullParams, ShadowCullData& cullData)
+{
+ const Rectf screenRect = camera.GetScreenViewportRect();
+ float shadowDistance = CalculateShadowDistance (camera);
+
+ Vector3f viewDir = -NormalizeSafe(camera.GetCameraToWorldMatrix().GetAxisZ());
+
+ cullData.camera = &camera;
+ cullData.eyePos = cameraPos;
+ cullData.viewDir = viewDir;
+ cullData.shadowDistance = shadowDistance;
+ cullData.projectionNear = camera.GetProjectionNear();
+ cullData.projectionFar = camera.GetProjectionFar();
+ cullData.farPlaneScale = shadowDistance / cullData.projectionFar;
+ cullData.viewWidth = screenRect.width;
+ cullData.viewHeight = screenRect.height;
+ cullData.actualWorldToClip = camera.GetWorldToClipMatrix();
+ cullData.visbilityForShadowCulling = NULL;
+ Matrix4x4f::Invert_Full(cullData.actualWorldToClip, cullData.cameraClipToWorld);
+
+ float farNearRatio = cullData.projectionFar / cullData.projectionNear;
+ if (!CanUseProjectionForShadows(cullData.cameraClipToWorld, farNearRatio, camera, cameraPos))
+ {
+ // Use implicit projection instead (which we used always before 3.5)
+ Matrix4x4f proj;
+ camera.GetImplicitProjectionMatrix(camera.GetNear(), camera.GetFar(), proj);
+ MultiplyMatrices4x4 (&proj, &camera.GetWorldToCameraMatrix(), &cullData.cameraWorldToClip);
+ Matrix4x4f::Invert_Full(cullData.cameraWorldToClip, cullData.cameraClipToWorld);
+ }
+ else
+ cullData.cameraWorldToClip = cullData.actualWorldToClip;
+
+ camera.CalculateFrustumPlanes(cullData.shadowCullPlanes, cullData.cameraWorldToClip, shadowDistance, cullData.baseFarDistance, true);
+ for( int i = 0; i < kPlaneFrustumNum; i++ )
+ cullData.cameraCullPlanes[i] = cullData.shadowCullPlanes[i];
+ cullData.cameraCullPlanes[kPlaneFrustumFar].distance = cullData.baseFarDistance + camera.GetFar();
+ cullData.shadowCullCenter = Vector3f::zero;
+ float cullRadius = 1e15f;
+ cullData.useSphereCulling = CalculateSphericalShadowRange(camera, cullData.shadowCullCenter, cullRadius);
+ cullData.shadowCullRadius = cullRadius;
+ cullData.shadowCullSquareRadius = cullRadius * cullRadius;
+ const float* layerCullDistances = camera.GetLayerCullDistances();
+ std::copy(layerCullDistances, layerCullDistances + kNumLayers, cullData.layerCullDistances);
+ cullData.layerCullSpherical = camera.GetLayerCullSpherical();
+ cullData.shaderReplace = shaderReplaceData;
+ cullData.sceneCullParameters = sceneCullParams;
+}
+
+float CalculateShadowDistance (const Camera& camera)
+{
+ return std::min (QualitySettings::GetShadowDistanceForRendering(), camera.GetFar());
+}
+
+float CalculateShadowSphereOffset (const Camera& camera)
+{
+ float fov = camera.GetFov();
+ static float maxDegrees = 180.0f;
+ const float maxOffset = (1.0f - kShadowFadeRange) * 0.5f;
+ float weight = clamp(1.0f - fov / maxDegrees, 0.0f, 1.0f);
+ return maxOffset * weight;
+}
+
+bool CalculateSphericalShadowRange (const Camera& camera, Vector3f& outCenter, float& outRadius)
+{
+ const QualitySettings::QualitySetting& quality = GetQualitySettings().GetCurrent();
+ if (quality.shadowProjection == kShadowProjCloseFit)
+ return false;
+
+ outCenter = camera.GetPosition();
+ outRadius = CalculateShadowDistance(camera);
+
+ float sphereOffset = CalculateShadowSphereOffset(camera);
+ Vector3f vectorOffset(0, 0, -sphereOffset * outRadius);
+ outCenter += camera.GetCameraToWorldMatrix().MultiplyVector3(vectorOffset);
+ outRadius *= (1.0f - sphereOffset);
+ return true;
+
+ Assert(quality.shadowProjection == kShadowProjStableFit);
+ return true;
+}
+
+void CalculateLightShadowFade (const Camera& camera, float shadowStrength, Vector4f& outParams, Vector4f& outCenterAndType)
+{
+ float shadowDistance = CalculateShadowDistance(camera);
+ float shadowRange = shadowDistance;
+ Vector3f center = camera.GetPosition();
+ bool spherical = CalculateSphericalShadowRange(camera, center, shadowRange);
+
+ // R = 1-strength
+ // G = 1.0 / shadowDistance
+ // B = 1.0 / (shadowDistance - shadowStartFade)
+ // A = -shadowStartFade / (shadowDistance - shadowStartFade)
+ outParams.x = 1.0f - shadowStrength; // R = 1-strength
+
+ if (shadowRange > 0)
+ {
+ outParams.y = camera.GetFar() / shadowDistance;
+ // fade factor = (x-start)/len = x * invlen - start*invlen
+ const float shadowStartFade = shadowRange - shadowDistance * kShadowFadeRange;
+ const float shadowFadeInvLen = 1.0f / (shadowRange - shadowStartFade);
+ outParams.z = shadowFadeInvLen;
+ outParams.w = -shadowStartFade * shadowFadeInvLen;
+ }
+ else
+ {
+ outParams.y = std::numeric_limits<float>::infinity();
+ outParams.z = 0;
+ outParams.w = 1;
+ }
+
+ outCenterAndType = Vector4f(center.x, center.y, center.z, spherical);
+}
+
+struct ShadowCullContext
+{
+ const ShadowCullData* camera;
+ bool excludeLightmapped;
+ UInt32 cullLayers;
+};
+
+
+static void AddShadowCaster (const SceneNode& node, const AABB& aabb, const ShadowCullData& context, bool expandCasterBounds, MinMaxAABB& casterBounds, ShadowCasters& casters, ShadowCasterParts& casterParts)
+{
+ const BaseRenderer* renderer = node.renderer;
+
+ Assert( renderer && renderer->GetCastShadows() );
+
+ // Find out which sub-materials do cast shadows
+ // and prefetch shader pointers.
+ size_t partsStartIndex = casterParts.size();
+ int matCount = renderer->GetMaterialCount();
+
+ Shader* replaceShader = context.shaderReplace.replacementShader;
+ const bool replacementTagSet = context.shaderReplace.replacementTagSet;
+ const int replacementTagID = context.shaderReplace.replacementTagID;
+
+ for( int i = 0; i < matCount; ++i )
+ {
+ Material* mat = renderer->GetMaterial(i);
+ if( !mat )
+ continue;
+
+ Shader* originalShader = mat->GetShader();
+ Shader* actualShader = replaceShader ? replaceShader : originalShader;
+
+ // Find the subshader...
+ int usedSubshaderIndex = -1;
+ if (replaceShader)
+ {
+ if (replacementTagSet)
+ {
+ int subshaderTypeID = originalShader->GetShaderLabShader()->GetTag (replacementTagID, true);
+ if (subshaderTypeID < 0)
+ continue; // skip rendering
+ usedSubshaderIndex = replaceShader->GetSubShaderWithTagValue (replacementTagID, subshaderTypeID);
+ if (usedSubshaderIndex == -1)
+ continue; // skip rendering
+ }
+ else
+ usedSubshaderIndex = 0;
+ }
+ else
+ usedSubshaderIndex = originalShader->GetActiveSubShaderIndex();
+
+ Assert (usedSubshaderIndex != -1);
+
+ if (actualShader->GetShadowCasterPassToUse(usedSubshaderIndex))
+ {
+ ShadowCasterPartData part;
+ part.subMeshIndex = renderer->GetSubsetIndex(i);
+ part.shader = actualShader;
+ part.subShaderIndex = usedSubshaderIndex;
+ part.material = mat;
+ casterParts.push_back (part);
+ }
+ }
+
+ // This object does cast shadows, store info
+ size_t partsEndIndex = casterParts.size();
+ if( partsEndIndex != partsStartIndex )
+ {
+ ShadowCasterData data;
+ data.node = &node;
+ data.worldAABB = &aabb;
+ data.visibleCascades = 1;
+ data.partsStartIndex = partsStartIndex;
+ data.partsEndIndex = partsEndIndex;
+
+ casters.push_back( data );
+
+ if( expandCasterBounds )
+ casterBounds.Encapsulate( aabb );
+ }
+}
+
+static bool CullCastersCommon(const ShadowCullContext& context, const SceneNode& node, const AABB& aabb)
+{
+ const BaseRenderer* renderer = node.renderer;
+ if (!renderer->GetCastShadows())
+ return false;
+
+ if (context.excludeLightmapped && renderer->IsLightmappedForShadows())
+ return false;
+
+ const int nodeLayer = node.layer;
+ const int nodeLayerMask = 1 << nodeLayer;
+ if (!(nodeLayerMask & context.cullLayers))
+ return false;
+
+ // For casters that use per-layer culling distance: check if they are behind cull distance.
+ // If they are, don't cast shadows.
+ const ShadowCullData& cullData = *context.camera;
+ float layerCullDist = cullData.layerCullDistances[nodeLayer];
+ if (layerCullDist)
+ {
+ if (cullData.layerCullSpherical)
+ {
+
+ float sqDist = SqrMagnitude(aabb.GetCenter() - cullData.eyePos);
+ if (sqDist > Sqr(layerCullDist))
+ return false;
+ }
+ else
+ {
+ Plane farPlane = cullData.shadowCullPlanes[kPlaneFrustumFar];
+ farPlane.distance = layerCullDist + cullData.baseFarDistance;
+ if( !IntersectAABBPlaneBounds( aabb, &farPlane, 1 ) )
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//
+// Point light shadow caster culling
+
+struct PointCullContext : public ShadowCullContext
+{
+ Sphere lightSphere;
+};
+
+static bool CullCastersPoint( PointCullContext* context, const SceneNode& node, const AABB& aabb )
+{
+ if (!CullCastersCommon(*context, node, aabb))
+ return false;
+
+ if (!IntersectAABBSphere( aabb, context->lightSphere ))
+ return false;
+
+ return true;
+}
+
+//
+// Spot light shadow caster culling
+
+struct SpotCullContext : public ShadowCullContext
+{
+ Matrix4x4f worldToLightMatrix, projectionMatrix;
+ Plane spotLightFrustum[6];
+};
+
+static bool CullCastersSpot( SpotCullContext* context, const SceneNode& node, const AABB& aabb )
+{
+ if (!CullCastersCommon(*context, node, aabb))
+ return false;
+
+ // World space light frustum vs. global AABB
+ if (!IntersectAABBFrustumFull (aabb, context->spotLightFrustum))
+ return false;
+
+ const TransformInfo& xformInfo = node.renderer->GetTransformInfo ();
+
+ Plane planes[6];
+ // NOTE: it's important to multiply world->light and node->world matrices
+ // before multiplying in the projection matrix. Otherwise when object/light
+ // coordinates will approach 10 thousands range, we'll get culling errors
+ // because of imprecision.
+ //Matrix4x4f proj = context->projectionMatrix * (context->worldToLightMatrix * node->worldMatrix);
+ Matrix4x4f temp, proj;
+ MultiplyMatrices4x4 (&context->worldToLightMatrix, &xformInfo.worldMatrix, &temp);
+ MultiplyMatrices4x4 (&context->projectionMatrix, &temp, &proj);
+ ExtractProjectionPlanes( proj, planes );
+
+ if (!IntersectAABBFrustumFull( xformInfo.localAABB, planes ) )
+ return false;
+
+ return true;
+}
+
+
+//
+// Directional light shadow caster culling
+
+struct DirectionalCullContext : public ShadowCullContext
+{
+};
+
+static bool CullCastersDirectional( DirectionalCullContext* context, const SceneNode& node, const AABB& aabb )
+{
+ return CullCastersCommon(*context, node, aabb);
+}
+
+
+static void CullDirectionalShadows (IndexList& visible, const SceneNode* nodes, const AABB* boundingBoxes, DirectionalCullContext &context )
+{
+ // Generate Visible nodes from all static & dynamic objects
+ int visibleCount = 0;
+ for (int i=0;i<visible.size;i++)
+ {
+ const SceneNode& node = nodes[visible.indices[i]];
+ const AABB& bounds = boundingBoxes[visible.indices[i]];
+
+ if (CullCastersDirectional( &context, node, bounds))
+ visible.indices[visibleCount++] = visible.indices[i];
+ }
+ visible.size = visibleCount;
+}
+
+static void CullSpotShadows (IndexList& visible, const SceneNode* nodes, const AABB* boundingBoxes, SpotCullContext &context )
+{
+ // Generate Visible nodes from all static & dynamic objects
+ int visibleCount = 0;
+ for (int i=0;i<visible.size;i++)
+ {
+ const SceneNode& node = nodes[visible.indices[i]];
+ const AABB& bounds = boundingBoxes[visible.indices[i]];
+
+ if (CullCastersSpot( &context, node, bounds))
+ visible.indices[visibleCount++] = visible.indices[i];
+ }
+ visible.size = visibleCount;
+}
+
+static void CullPointShadows (IndexList& visible, const SceneNode* nodes, const AABB* boundingBoxes, PointCullContext &context )
+{
+ // Generate Visible nodes from all static & dynamic objects
+ int visibleCount = 0;
+ for (int i=0;i<visible.size;i++)
+ {
+ const SceneNode& node = nodes[visible.indices[i]];
+ const AABB& bounds = boundingBoxes[visible.indices[i]];
+
+ if (CullCastersPoint( &context, node, bounds ))
+ visible.indices[visibleCount++] = visible.indices[i];
+ }
+ visible.size = visibleCount;
+}
+
+static void CullShadowCastersDetail( const Light& light, const ShadowCullData& cullData, bool excludeLightmapped, CullingOutput& visibleRenderers )
+{
+ const RendererCullData* rendererCullData = cullData.sceneCullParameters->renderers;
+
+ int cullLayers = light.GetCullingMask() & cullData.camera->GetCullingMask();
+ LightType lightType = light.GetType();
+ switch (lightType)
+ {
+ case kLightPoint:
+ {
+ PointCullContext context;
+ const Transform& lt = light.GetComponent (Transform);
+ Vector3f lightPos = lt.GetPosition();
+ context.lightSphere = Sphere( lightPos, light.GetRange() );
+ context.camera = &cullData;
+ context.cullLayers = cullLayers;
+ context.excludeLightmapped = excludeLightmapped;
+
+ for (int i=0;i<kVisibleListCount;i++)
+ CullPointShadows (visibleRenderers.visible[i], rendererCullData[i].nodes, rendererCullData[i].bounds, context);
+ }
+ break;
+
+ case kLightSpot:
+ {
+ SpotCullContext context;
+ Matrix4x4f zscale, proj;
+ zscale.SetScale (Vector3f (1.0F, 1.0F, -1.0F));
+ proj.SetPerspectiveCotan( light.GetCotanHalfSpotAngle(), 0.0001F, light.GetRange() );
+ MultiplyMatrices4x4 (&proj, &zscale, &context.projectionMatrix);
+ const Transform& lt = light.GetComponent (Transform);
+ context.worldToLightMatrix = lt.GetWorldToLocalMatrixNoScale();
+
+ Matrix4x4f temp;
+ MultiplyMatrices4x4 (&context.projectionMatrix, &context.worldToLightMatrix, &temp);
+ ExtractProjectionPlanes (temp, context.spotLightFrustum);
+
+ context.camera = &cullData;
+ context.cullLayers = cullLayers;
+ context.excludeLightmapped = excludeLightmapped;
+
+ for (int i=0;i<kVisibleListCount;i++)
+ CullSpotShadows (visibleRenderers.visible[i], rendererCullData[i].nodes, rendererCullData[i].bounds, context);
+ }
+ break;
+ case kLightDirectional:
+ {
+ DirectionalCullContext context;
+ context.camera = &cullData;
+ context.cullLayers = cullLayers;
+ context.excludeLightmapped = excludeLightmapped;
+
+ for (int i=0;i<kVisibleListCount;i++)
+ CullDirectionalShadows (visibleRenderers.visible[i], rendererCullData[i].nodes, rendererCullData[i].bounds, context);
+ }
+ break;
+ }
+}
+
+void CullShadowCasters (const Light& light, const ShadowCullData& cullData, bool excludeLightmapped, CullingOutput& cullingOutput )
+{
+ LightType lightType = light.GetType();
+
+ if (lightType == kLightPoint)
+ {
+ PROFILER_BEGIN(gShadowsCullCastersPoint,&light)
+ }
+ else if (lightType == kLightSpot)
+ {
+ PROFILER_BEGIN(gShadowsCullCastersSpot,&light)
+ }
+ else
+ {
+ PROFILER_BEGIN(gShadowsCullCastersDir,&light)
+ }
+
+ const Transform& lt = light.GetComponent (Transform);
+ ShadowCasterCull casterCull;
+
+ CalculateShadowCasterCull( cullData.shadowCullPlanes, cullData.cameraClipToWorld, cullData.eyePos,
+ 1.0, cullData.farPlaneScale, lightType, lt, casterCull );
+
+ SceneCullingParameters cullParams = *cullData.sceneCullParameters;
+ cullData.camera->CalculateCustomCullingParameters(cullParams, casterCull.planes, casterCull.planeCount);
+
+ if (lightType == kLightDirectional)
+ {
+ Vector3f lightDir = lt.TransformDirection (Vector3f(0,0,-1));
+ cullParams.lightDir = lightDir;
+ }
+
+ if (cullParams.useShadowCasterCulling && (lightType == kLightDirectional))
+ {
+ CullShadowCastersWithUmbra(cullParams, cullingOutput);
+ }
+ else
+ {
+ CullSceneWithoutUmbra (cullParams, cullingOutput);
+ }
+
+
+ CullShadowCastersDetail (light, cullData, excludeLightmapped, cullingOutput);
+
+ PROFILER_END
+}
+
+
+void GenerateShadowCasterParts (const Light& light, const ShadowCullData& cullData, const CullingOutput& visibleRenderers, MinMaxAABB& casterBounds, ShadowCasters& casters, ShadowCasterParts& casterParts)
+{
+ const bool isDirectionalLight = light.GetType() == kLightDirectional;
+
+ const RendererCullData* rendererCullData = cullData.sceneCullParameters->renderers;
+ for (int t=0;t<kVisibleListCount;t++)
+ {
+ const IndexList& visibleList = visibleRenderers.visible[t];
+ const RendererCullData& renderLists = rendererCullData[t];
+ for (int i=0;i<visibleList.size;i++)
+ {
+ int renderIndex = visibleList[i];
+ bool casterTouchesView = false;
+ if (isDirectionalLight)
+ {
+ // Only compute caster AABB for casters that touch the view frustum.
+ // Other casters might be valid casters, but behind near plane of resulting
+ // shadow camera. This is ok, we manually push those onto near plane in caster vertex shader.
+ //
+ // If we'd bound all casters we get the issue of insufficient depth precision when some caster
+ // has a valid caster, but is waaay far away (50000 units or so).
+ casterTouchesView = IntersectAABBFrustumFull( renderLists.bounds[renderIndex], cullData.cameraCullPlanes );
+ }
+
+ // Last argument expandCasterBounds should be false for point lights as they don't use casterBounds to adjust shadow map view
+ AddShadowCaster( renderLists.nodes[renderIndex], renderLists.bounds[renderIndex], cullData, casterTouchesView, casterBounds, casters, casterParts );
+ }
+ }
+}
+
+
+
+/*
+
+ static bool CullCastersDirectional( DirectionalCullContext* context, const SceneNode& node, const AABB& aabb, float lodFade )
+ {
+ if (!CullCastersCommon(*context, node, aabb))
+ return false;
+
+ // Only compute caster AABB for casters that touch the view frustum.
+ // Other casters might be valid casters, but behind near plane of resulting
+ // shadow camera. This is ok, we manually push those onto near plane in caster vertex shader.
+ //
+ // If we'd bound all casters we get the issue of insufficient depth precision when some caster
+ // has a valid caster, but is waaay far away (50000 units or so).
+ bool casterTouchesView = IntersectAABBFrustumFull( aabb, context->camera->cameraCullPlanes );
+ AddShadowCaster( node, aabb, *context, casterTouchesView );
+ return true;
+ }
+
+
+ */
+
+
+
+struct Circle2f
+{
+ Vector2f center;
+ float radius;
+};
+
+void CullDirectionalCascades(ShadowCasters& casters, const ShadowCascadeInfo cascades[kMaxShadowCascades], int cascadeCount,
+ const Quaternionf& lightRot, const Vector3f& lightDir, const ShadowCullData& cullData)
+{
+ Assert(cascadeCount <= kMaxShadowCascades);
+ const QualitySettings::QualitySetting& quality = GetQualitySettings().GetCurrent();
+ if (quality.shadowProjection == kShadowProjCloseFit && cascadeCount == 1)
+ {
+ // We already culled casters for one non-spherical cascade
+ return;
+ }
+ const Camera& camera = *cullData.camera;
+
+ // Split frustums extruded towards light
+ ShadowCasterCull cullPlanes[kMaxShadowCascades];
+
+ // Only objects inside a cylinder can cast shadows onto a sphere
+ // We cull in light space so the cylinders become circles
+ Circle2f cullCylinders[kMaxShadowCascades];
+
+ Matrix4x4f lightMat;
+ QuaternionToMatrix(lightRot, lightMat);
+
+ for (int casc = 0; casc < cascadeCount; casc++)
+ {
+ const ShadowCascadeInfo& cascade = cascades[casc];
+ if (!cascade.enabled)
+ continue;
+ if (quality.shadowProjection == kShadowProjStableFit)
+ {
+ // We use spherical splits so cull against a cylinder
+ const Vector3f& centerWorld = cascade.outerSphere.GetCenter();
+ cullCylinders[casc].center.x = Dot(centerWorld, lightMat.GetAxisX());
+ cullCylinders[casc].center.y = Dot(centerWorld, lightMat.GetAxisY());
+ cullCylinders[casc].radius = cascade.outerSphere.GetRadius();
+ }
+ if (cascadeCount == 1)
+ {
+ // We have already culled against non-cascaded frustum
+ cullPlanes[casc].planeCount = 0;
+ continue;
+ }
+
+ bool alreadyTested[kPlaneFrustumNum];
+ for (int p = 0; p < kPlaneFrustumNear; p++)
+ alreadyTested[p] = true;
+ alreadyTested[kPlaneFrustumNear] = (casc == 0);
+ alreadyTested[kPlaneFrustumFar] = ((casc + 1) == cascadeCount);
+
+ Plane splitPlanes[kPlaneFrustumNum];
+ memcpy(splitPlanes, cullData.shadowCullPlanes, sizeof(splitPlanes));
+ splitPlanes[kPlaneFrustumNear].distance += cascade.minViewDistance - camera.GetNear();
+ splitPlanes[kPlaneFrustumFar].distance += cascade.maxViewDistance - cullData.shadowDistance;
+ float nearPlaneScale = cascade.minViewDistance / cullData.projectionNear;
+ float farPlaneScale = cascade.maxViewDistance / cullData.projectionFar;
+ CalculateShadowCasterCull(splitPlanes, cullData.cameraClipToWorld, cullData.eyePos,
+ nearPlaneScale, farPlaneScale, kLightDirectional, lightDir, cullPlanes[casc], alreadyTested);
+ }
+
+ UInt32 allVisible = 0;
+ for (int casc = 0; casc < cascadeCount; casc++)
+ allVisible = (allVisible << 1) | 1;
+
+ // Go over casters and determine in which cascades they are visible
+ int casterCount = casters.size();
+ for (int i = 0; i < casterCount; i++)
+ {
+ ShadowCasterData& caster = casters[i];
+ const AABB& bounds = *caster.worldAABB;
+ caster.visibleCascades = allVisible;
+ UInt32 currentCascadeMask = 1;
+
+ if (quality.shadowProjection == kShadowProjStableFit)
+ {
+ float casterRadius = Magnitude(bounds.GetExtent());
+ Vector2f lightSpaceCenter;
+ lightSpaceCenter.x = Dot(bounds.GetCenter(), lightMat.GetAxisX());
+ lightSpaceCenter.y = Dot(bounds.GetCenter(), lightMat.GetAxisY());
+ currentCascadeMask = 1;
+ for (int casc = 0; casc < cascadeCount; casc++, currentCascadeMask <<= 1)
+ {
+ if (!cascades[casc].enabled)
+ continue;
+ // Do the caster bounds and cascade intersect in light space?
+ Vector2f vec = lightSpaceCenter - cullCylinders[casc].center;
+ float sqrDist = Sqr(vec.x) + Sqr(vec.y);
+ float cullRadius = cullCylinders[casc].radius;
+ if (sqrDist > Sqr(casterRadius + cullRadius))
+ {
+ // Circles do not intersect, mark as invisible
+ caster.visibleCascades &= ~currentCascadeMask;
+ }
+ }
+ }
+
+ if (cascadeCount > 1)
+ {
+ currentCascadeMask = 1;
+ for (int casc = 0; casc < cascadeCount; casc++, currentCascadeMask <<= 1)
+ {
+ if (!cascades[casc].enabled)
+ continue;
+ // Did we already cull this?
+ if (caster.visibleCascades & currentCascadeMask)
+ {
+ if (!IntersectAABBPlaneBounds(bounds, cullPlanes[casc].planes, cullPlanes[casc].planeCount))
+ {
+ // Outside cull planes, mark this as invisible
+ caster.visibleCascades &= ~currentCascadeMask;
+ }
+ }
+ }
+ }
+ }
+}
+
+bool IsObjectWithinShadowRange (const ShadowCullData& shadowCullData, const AABB& bounds)
+{
+ if (shadowCullData.useSphereCulling)
+ {
+ float sqrDist = SqrMagnitude(bounds.GetCenter() - shadowCullData.shadowCullCenter);
+ if (sqrDist < shadowCullData.shadowCullSquareRadius)
+ return true;
+ Sphere sphere(shadowCullData.shadowCullCenter, shadowCullData.shadowCullRadius);
+ return IntersectAABBSphere(bounds, sphere);
+ }
+ else
+ {
+ return IntersectAABBPlaneBounds(bounds, &shadowCullData.shadowCullPlanes[kPlaneFrustumFar], 1);
+ }
+}
diff --git a/Runtime/Camera/ShadowCulling.h b/Runtime/Camera/ShadowCulling.h
new file mode 100644
index 0000000..d54ca3c
--- /dev/null
+++ b/Runtime/Camera/ShadowCulling.h
@@ -0,0 +1,121 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+
+#include "Lighting.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Vector4.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/Geometry/Sphere.h"
+#include "Runtime/BaseClasses/Tags.h"
+#include "CullingParameters.h"
+#include "ShaderReplaceData.h"
+
+const int kMaxShadowCascades = 4;
+
+class Camera;
+class Light;
+class Transform;
+class AABB;
+class MinMaxAABB;
+struct SceneNode;
+struct CullingOutput;
+struct SceneCullState;
+struct SceneCullingParameters;
+class Shader;
+
+namespace Unity { class Material; }
+namespace Umbra { class Visibility; class Tome; }
+
+struct ShadowCullData
+{
+ Camera* camera;
+ Matrix4x4f cameraClipToWorld;
+ Matrix4x4f cameraWorldToClip;
+ Matrix4x4f actualWorldToClip;
+ Vector3f eyePos;
+ Vector3f viewDir;
+ float viewWidth, viewHeight; // in pixels
+ float layerCullDistances[kNumLayers];
+ bool layerCullSpherical;
+ float baseFarDistance;
+ Plane cameraCullPlanes[6];
+ Plane shadowCullPlanes[6];
+ Vector3f shadowCullCenter;
+ float shadowCullRadius;
+ float shadowCullSquareRadius;
+ bool useSphereCulling;
+ float shadowDistance;
+ float projectionNear;
+ float projectionFar;
+ float farPlaneScale;
+
+ const CullingOutput* visbilityForShadowCulling;
+ const SceneCullingParameters* sceneCullParameters;
+
+ ShaderReplaceData shaderReplace;
+};
+
+struct ShadowCasterCull
+{
+ enum { kMaxPlanes = 10 };
+ Plane planes[kMaxPlanes];
+ int planeCount;
+};
+
+struct ShadowCascadeInfo
+{
+ bool enabled;
+ Matrix4x4f lightMatrix;
+ Matrix4x4f viewMatrix;
+ Matrix4x4f projMatrix;
+ Matrix4x4f worldToClipMatrix;
+ Matrix4x4f shadowMatrix;
+ Sphere outerSphere;
+ float minViewDistance;
+ float maxViewDistance;
+ float nearPlane;
+ float farPlane;
+};
+
+struct ShadowCasterPartData
+{
+ int subMeshIndex; // 4
+ int subShaderIndex; // 4
+ Shader* shader; // 4
+ Unity::Material* material; // 4
+ // 16 bytes
+};
+
+struct ShadowCasterData
+{
+ const SceneNode* node; // 4
+ const AABB* worldAABB; // 4
+ size_t partsStartIndex; // 4
+ size_t partsEndIndex; // 4
+ UInt32 visibleCascades; // 4
+ // 20 bytes
+};
+
+typedef UNITY_TEMP_VECTOR(ShadowCasterData) ShadowCasters;
+typedef UNITY_TEMP_VECTOR(ShadowCasterPartData) ShadowCasterParts;
+
+void CalculateShadowCasterCull(const Plane frustumCullPlanes[6], const Matrix4x4f& clipToWorld, const Vector3f& centerWorld, float nearPlaneScale, float farPlaneScale, LightType lightType, const Vector3f& lightVec, ShadowCasterCull& result, const bool alreadyTested[kPlaneFrustumNum]);
+void CalculateShadowCasterCull (const Plane frustumClipPlanes[6], const Matrix4x4f& clipToWorld, const Vector3f& centerWorld, float nearPlaneScale, float farPlaneScale, LightType lightType, const Transform& trans, ShadowCasterCull& result);
+void SetupShadowCullData (Camera& camera, const Vector3f& cameraPos, const ShaderReplaceData& shaderReplaceData, const SceneCullingParameters* sceneCullParams, ShadowCullData& cullData);
+float CalculateShadowDistance (const Camera& camera);
+float CalculateShadowSphereOffset (const Camera& camera);
+bool CalculateSphericalShadowRange (const Camera& camera, Vector3f& outCenter, float& outRadius);
+void CalculateLightShadowFade (const Camera& camera, float shadowStrength, Vector4f& outParams, Vector4f& outCenterAndType);
+
+void CullDirectionalCascades(ShadowCasters& casters, const ShadowCascadeInfo cascades[kMaxShadowCascades], int cascadeCount,
+ const Quaternionf& lightRot, const Vector3f& lightDir, const ShadowCullData& cullData);
+
+void CullShadowCasters (const Light& light, const ShadowCullData& cullData, bool excludeLightmapped, CullingOutput& cullingOutput );
+
+void GenerateShadowCasterParts (const Light& light, const ShadowCullData& cullData, const CullingOutput& visibleRenderers, MinMaxAABB& casterBounds, ShadowCasters& casters, ShadowCasterParts& casterParts);
+
+
+bool IsObjectWithinShadowRange (const ShadowCullData& shadowCullData, const AABB& bounds);
diff --git a/Runtime/Camera/ShadowSettings.cpp b/Runtime/Camera/ShadowSettings.cpp
new file mode 100644
index 0000000..af1d3a6
--- /dev/null
+++ b/Runtime/Camera/ShadowSettings.cpp
@@ -0,0 +1,14 @@
+#include "UnityPrefix.h"
+#include "Runtime/Serialize/SerializationMetaFlags.h"
+#include "ShadowSettings.h"
+#include "Lighting.h"
+
+void ShadowSettings::Reset()
+{
+ m_Type = kShadowNone;
+ m_Resolution = -1; // auto
+ m_Strength = 1.0f;
+ m_Bias = 0.05f; // 5 cm
+ m_Softness = 4.0f;
+ m_SoftnessFade = 1.0f;
+}
diff --git a/Runtime/Camera/ShadowSettings.h b/Runtime/Camera/ShadowSettings.h
new file mode 100644
index 0000000..256ba83
--- /dev/null
+++ b/Runtime/Camera/ShadowSettings.h
@@ -0,0 +1,33 @@
+#ifndef SHADOW_SETTINGS_H
+#define SHADOW_SETTINGS_H
+
+#include "Runtime/Serialize/SerializeUtility.h"
+
+struct ShadowSettings
+{
+ DECLARE_SERIALIZE_NO_PPTR (ShadowSettings)
+
+ int m_Type; ///< enum { No Shadows, Hard Shadows, Soft Shadows } Shadow cast options
+ // -1 is auto; the rest must match the values in Quality Settings!
+ int m_Resolution; ///< enum { Use Quality Settings = -1, Low Resolution = 0, Medium Resolution = 1, High Resolution = 2, Very High Resolution = 3 } Shadow resolution
+ float m_Strength; ///< Shadow intensity range {0.0, 1.0}
+ float m_Bias; ///< Bias for shadows range {0.0, 10.0}
+ float m_Softness; ///< Shadow softness range {1.0, 8.0}
+ float m_SoftnessFade; ///< Shadow softness fadeout range {0.1, 5.0}
+
+ ShadowSettings () { Reset (); }
+ void Reset();
+};
+
+template<class TransferFunc>
+void ShadowSettings::Transfer (TransferFunc& transfer)
+{
+ TRANSFER_SIMPLE(m_Type);
+ TRANSFER (m_Resolution);
+ TRANSFER (m_Strength);
+ TRANSFER (m_Bias);
+ TRANSFER (m_Softness);
+ TRANSFER (m_SoftnessFade);
+}
+
+#endif
diff --git a/Runtime/Camera/Shadows.cpp b/Runtime/Camera/Shadows.cpp
new file mode 100644
index 0000000..8a7de2f
--- /dev/null
+++ b/Runtime/Camera/Shadows.cpp
@@ -0,0 +1,1227 @@
+#include "UnityPrefix.h"
+#include "Shadows.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Shaders/ShaderKeywords.h"
+
+#if ENABLE_SHADOWS
+
+#include "Runtime/Geometry/AABB.h"
+#include "Light.h"
+#include "RenderManager.h"
+#include "Runtime/Graphics/RenderBufferManager.h"
+#include "BaseRenderer.h"
+#include "External/shaderlab/Library/properties.h"
+#include "Runtime/Graphics/GraphicsHelper.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Renderqueue.h"
+#include "Runtime/Geometry/BoundingUtils.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Camera/Culler.h"
+#include "IntermediateRenderer.h"
+#include "Runtime/GfxDevice/VramLimits.h"
+#include "Runtime/Misc/QualitySettings.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/GraphicsDevicesDB.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+#include "Runtime/Interfaces/ITerrainManager.h"
+#include "Runtime/GfxDevice/BatchRendering.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "CullResults.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "UnityScene.h"
+#include "Runtime/Profiler/Profiler.h"
+
+#include "Configuration/UnityConfigure.h"
+#if ENABLE_MONO && ENABLE_TERRAIN
+#include "Runtime/Scripting/Backend/ScriptingInvocation.h"
+#endif
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+#if UNITY_PS3 || UNITY_XENON
+# define kShadowmapPointSizeMin 512
+# define kShadowmapPointSizeMax 1024
+# define kShadowmapSpotSizeMin 1024
+# define kShadowmapSpotSizeMax 2048
+# define kShadowmapDirSizeMin 1024
+# define kShadowmapDirSizeMax 4096
+#elif UNITY_WINRT
+# define kShadowmapPointSizeMin 512
+# define kShadowmapPointSizeMax 1024
+# define kShadowmapSpotSizeMin 1024
+# define kShadowmapSpotSizeMax 2048
+# define kShadowmapDirSizeMin 1024
+# define kShadowmapDirSizeMax 2048
+#else
+# define kShadowmapPointSizeMin 512
+# define kShadowmapPointSizeMax 1024
+# define kShadowmapSpotSizeMin 1024
+# define kShadowmapSpotSizeMax 2048
+# define kShadowmapDirSizeMin 2048
+# define kShadowmapDirSizeMax 4096
+#endif
+
+// 4000 is PS3 / Xbox360 and it should be fine on those.
+#define kShadowRotatedBlurFillrateThreshold (4000)
+
+
+#endif // ENABLE_SHADOWS
+
+// --------------------------------------------------------------------------
+
+static ShaderKeyword kShadowsOffKeyword = keywords::Create("SHADOWS_OFF");
+static ShaderKeyword kShadowsDepthKeyword = keywords::Create("SHADOWS_DEPTH");
+static ShaderKeyword kShadowsScreenKeyword = keywords::Create("SHADOWS_SCREEN");
+static ShaderKeyword kShadowsCubeKeyword = keywords::Create("SHADOWS_CUBE");
+static ShaderKeyword kShadowsSoftKeyword = keywords::Create("SHADOWS_SOFT");
+static ShaderKeyword kShadowsSplitSpheresKeyword = keywords::Create("SHADOWS_SPLIT_SPHERES");
+static ShaderKeyword kShadowsNativeKeyword = keywords::Create("SHADOWS_NATIVE");
+
+void SetNoShadowsKeywords()
+{
+ g_ShaderKeywords.Enable( kShadowsOffKeyword );
+ g_ShaderKeywords.Disable( kShadowsDepthKeyword );
+ g_ShaderKeywords.Disable( kShadowsScreenKeyword );
+ g_ShaderKeywords.Disable( kShadowsCubeKeyword );
+ g_ShaderKeywords.Disable( kShadowsSoftKeyword );
+ g_ShaderKeywords.Disable( kShadowsSplitSpheresKeyword );
+ g_ShaderKeywords.Disable( kShadowsNativeKeyword );
+}
+
+
+bool CheckPlatformSupportsShadows ()
+{
+ return
+ gGraphicsCaps.hasRenderToTexture &&
+ (gGraphicsCaps.shaderCaps >= kShaderLevel2) &&
+ gGraphicsCaps.supportsRenderTextureFormat[kRTFormatDepth] &&
+ #if !UNITY_FLASH && !UNITY_WINRT //@TODO: remove me
+ gGraphicsCaps.hasRenderToCubemap &&
+ #endif
+ (gGraphicsCaps.npotRT != kNPOTNone);
+}
+
+// --------------------------------------------------------------------------
+
+#if ENABLE_SHADOWS
+
+bool GetSoftShadowsEnabled ()
+{
+ // Check build settings
+ const BuildSettings& buildSettings = GetBuildSettings();
+ if( !buildSettings.hasShadows || !buildSettings.hasSoftShadows )
+ return false;
+
+ // Disabled by graphics caps?
+ if( gGraphicsCaps.disableSoftShadows )
+ return false;
+
+ // Check quality settings
+ const QualitySettings::QualitySetting& quality = GetQualitySettings().GetCurrent();
+ if( quality.shadows < QualitySettings::kShadowsAll )
+ return false;
+
+ const float shadowDistance = QualitySettings::GetShadowDistanceForRendering();
+ return shadowDistance > 0.0f;
+}
+
+void SetShadowsKeywords( LightType lightType, ShadowType shadowType, bool screen, bool enableSoftShadows )
+{
+ ShadowProjection proj = (ShadowProjection)GetQualitySettings().GetCurrent().shadowProjection;
+ g_ShaderKeywords.Disable( kShadowsOffKeyword );
+ g_ShaderKeywords.Disable (kShadowsNativeKeyword);
+
+ if( IsSoftShadow(shadowType) && enableSoftShadows )
+ g_ShaderKeywords.Enable( kShadowsSoftKeyword );
+ else
+ g_ShaderKeywords.Disable( kShadowsSoftKeyword );
+
+ if( lightType == kLightDirectional && shadowType != kShadowNone &&
+ proj == kShadowProjStableFit )
+ g_ShaderKeywords.Enable( kShadowsSplitSpheresKeyword );
+ else
+ g_ShaderKeywords.Disable( kShadowsSplitSpheresKeyword );
+
+ if( screen )
+ {
+ g_ShaderKeywords.Enable( kShadowsScreenKeyword );
+ g_ShaderKeywords.Disable( kShadowsDepthKeyword );
+ g_ShaderKeywords.Disable( kShadowsCubeKeyword );
+ if (gGraphicsCaps.hasNativeShadowMap && !gGraphicsCaps.hasShadowCollectorPass)
+ g_ShaderKeywords.Enable (kShadowsNativeKeyword);
+ }
+ else if( lightType == kLightPoint )
+ {
+ g_ShaderKeywords.Enable( kShadowsCubeKeyword );
+ g_ShaderKeywords.Disable( kShadowsDepthKeyword );
+ g_ShaderKeywords.Disable( kShadowsScreenKeyword );
+ }
+ else
+ {
+ g_ShaderKeywords.Enable( kShadowsDepthKeyword );
+ g_ShaderKeywords.Disable( kShadowsCubeKeyword );
+ g_ShaderKeywords.Disable( kShadowsScreenKeyword );
+ if (gGraphicsCaps.hasNativeShadowMap &&
+ !(lightType == kLightSpot && gGraphicsCaps.buggySpotNativeShadowMap))
+ {
+ g_ShaderKeywords.Enable (kShadowsNativeKeyword);
+ }
+ }
+}
+
+
+PROFILER_INFORMATION(gShadowsRender, "Shadows.RenderShadowmap", kProfilerRender)
+PROFILER_INFORMATION(gShadowsRenderPoint, "Shadows.RenderShadowmapPoint", kProfilerRender)
+PROFILER_INFORMATION(gShadowsRenderSpot, "Shadows.RenderShadowmapSpot", kProfilerRender)
+PROFILER_INFORMATION(gShadowsRenderDir, "Shadows.RenderShadowmapDir", kProfilerRender)
+PROFILER_INFORMATION(gCullShadowCasters, "CullShadowCasters", kProfilerRender);
+
+// --------------------------------------------------------------------------
+
+
+// from Parallel Split Shadow Maps paper: practical splitting scheme
+void CalculatePSSMDistances (float nearPlane, float shadowFarPlane, int splitCount, float* outDistances, float* outPercentages)
+{
+ AssertIf( !outDistances || !outPercentages || splitCount < 1 );
+
+ // very first & last ones are always these (deals with rounding issues as well)
+ outDistances[0] = nearPlane;
+ outDistances[splitCount] = shadowFarPlane;
+ outPercentages[0] = 0.0f;
+ outPercentages[splitCount] = 1.0f;
+
+ // Each next split is 2x larger than the previous one.
+ // Different from classic PSSM paper; split ratios don't depent on near plane at all.
+ // Dependance on near plane is not very intuitive anyway!
+ if( splitCount == 2 )
+ {
+ // 2 splits: 0, 1/3, 1
+ outPercentages[1] = 1.0f / 3.0f;
+ }
+ else if( splitCount == 4 )
+ {
+ // 4 splits: 0, 1/15, 3/15, 7/15, 1
+ outPercentages[1] = 1.0f / 15.0f;
+ outPercentages[2] = 3.0f / 15.0f;
+ outPercentages[3] = 7.0f / 15.0f;
+ }
+ for( int i = 1; i < splitCount; ++i )
+ outDistances[i] = nearPlane + (shadowFarPlane - nearPlane) * outPercentages[i];
+
+ //if( splitCount == 4 )
+ // printf_console("PSSM: splits=%i near=%g far=%g p=%g %g %g %g\n", splitCount, nearPlane, shadowFarPlane, outDistances[0], outDistances[1], outDistances[2], outDistances[3] );
+ //if( splitCount == 2 )
+ // printf_console("PSSM: splits=%i near=%g far=%g p=%g %g\n", splitCount, nearPlane, shadowFarPlane, outDistances[0], outDistances[1] );
+}
+
+
+// --------------------------------------------------------------------------
+
+static SHADERPROP(ShadowProjectionParams); // x = unused, y = near plane, z = far plane, w = unused
+
+
+// Shadow caster sort data structure
+struct CompactCasterSortData
+{
+ UInt64 key;
+ int casterIndex;
+ size_t partsIndex;
+
+ CompactCasterSortData(UInt32 _smallMeshIndex, UInt32 _hashOfShadowCasterPass, TransformType _transformType, float _depth, int _casterIndex, size_t _partsIndex )
+ {
+ // 64b key: 32 bit shadow caster pass hash, 16b mesh ID, 2b transform type, and 14b depth
+ key=0;
+ UInt32 transformType = static_cast<UInt32>(_transformType);
+ UInt32 z = (UInt32)(16383.0f*_depth);
+
+ key |= (_hashOfShadowCasterPass);
+ key = key << 32;
+ key |= ((_smallMeshIndex&0x0000ffff)<<16)|((transformType&0x00000003)<<14)|(z&0x00003fff);
+
+ casterIndex = _casterIndex;
+ partsIndex = _partsIndex;
+ }
+};
+
+
+struct CompactShadowCasterKeySorter
+{
+ inline bool operator()(const CompactCasterSortData& a, const CompactCasterSortData& b)
+{
+ return a.key < b.key;
+ }
+};
+
+// Shadow caster sorting
+// Input: _splitIndex - cascade index
+// _data - casters object related data
+// _dataParts - casters materials related data
+// Output: _resultOrder - sorted shadow caster draws
+// Returns: Number of active casters
+static int SortCastersCompact( int _splitIndex, ShadowCasters& _data, ShadowCasterParts& _dataParts, const ShadowCameraData& _cameraData, CompactCasterSortData* _resultOrder)
+{
+ int activeCasters = 0;
+
+ // Generate key array for sorting
+ int cascadeMask = 1 << _splitIndex;
+ for( int i = 0; i < _data.size(); ++i )
+ {
+ ShadowCasterData& caster = _data[i];
+
+ // This caster can be skipped for this shadow render pass (e.g. this face of cubemap or this split of directional shadow).
+ if( caster.visibleCascades & cascadeMask )
+ {
+ for( size_t m = caster.partsStartIndex; m < caster.partsEndIndex; ++m )
+ {
+ const TransformInfo& xformInfo = caster.node->renderer->GetTransformInfo ();
+ Matrix4x4f worldToClipMatrix = _cameraData.cameraWorldToClip;
+
+ const Vector3f& worldPos = caster.worldAABB->GetCenter();
+ float z = worldToClipMatrix.Get (2, 0) * worldPos.x + worldToClipMatrix.Get (2, 1) * worldPos.y + worldToClipMatrix.Get (2, 2) * worldPos.z + worldToClipMatrix.Get (2, 3);
+ float w = worldToClipMatrix.Get (3, 0) * worldPos.x + worldToClipMatrix.Get (3, 1) * worldPos.y + worldToClipMatrix.Get (3, 2) * worldPos.z + worldToClipMatrix.Get (3, 3);
+ float z_proj = z/w;
+ z_proj = max(z_proj,0.0f);
+ z_proj = min(z_proj,1.0f);
+ _resultOrder[activeCasters++] = CompactCasterSortData( caster.node->renderer->GetMeshIDSmall(), _dataParts[m].material->GetShadowCasterHash(),
+ xformInfo.transformType, z_proj, i, m );
+ }
+ }
+ }
+
+ std::sort( _resultOrder, _resultOrder + activeCasters, CompactShadowCasterKeySorter() );
+
+ return activeCasters;
+ }
+
+static void RenderCasters( int splitIndex, const Light& light, const Vector3f& lightPos, const Vector3f& lightDir, ShadowCasters& data, ShadowCasterParts& dataParts, const ShadowCameraData& cameraData )
+{
+ GfxDevice& device = GetGfxDevice();
+
+ float matWorld[16], matView[16];
+ CopyMatrix(device.GetViewMatrix(), matView);
+ CopyMatrix(device.GetWorldMatrix(), matWorld);
+
+ device.SetInverseScale(1.0f);
+
+#if GFX_ENABLE_SHADOW_BATCHING
+
+ CompactCasterSortData* sortOrder;
+ ALLOC_TEMP(sortOrder, CompactCasterSortData, dataParts.size());
+ int activeCasters = SortCastersCompact( splitIndex, data, dataParts, cameraData, sortOrder);
+ device.GetFrameStats().AddShadowCasters(activeCasters);
+
+ if (activeCasters == 0)
+ return;
+
+ BatchRenderer casterBatchRenderer;
+ UInt64 previousKey = ((sortOrder[0].key)&0xFFFFFFFFFFFFC000ULL); // depth component does not affect state change boundaries
+ UInt32 prevCustomPropsHash = 0;
+ const ShadowCasterPartData* part = &dataParts[sortOrder[0].partsIndex];
+ const ChannelAssigns* channels = part->material->SetShadowCasterPassWithShader(part->shader, part->subShaderIndex);
+
+ for(int i=0; i<activeCasters;i++)
+ {
+ UInt64 currentKey = ((sortOrder[i].key)&0xFFFFFFFFFFFFC000ULL);
+
+ BaseRenderer* renderer = data[sortOrder[i].casterIndex].node->renderer;
+ const TransformInfo& xformInfo = renderer->GetTransformInfo ();
+ part = &dataParts[sortOrder[i].partsIndex];
+
+ const UInt32 customPropsHash = renderer->GetCustomPropertiesHash();
+ renderer->ApplyCustomProperties(*part->material, part->shader, part->subShaderIndex);
+
+ if (previousKey != currentKey || prevCustomPropsHash != customPropsHash) // Flush() and update state when key changes
+ {
+ casterBatchRenderer.Flush();
+ channels = part->material->SetShadowCasterPassWithShader(part->shader, part->subShaderIndex);
+ }
+
+ // if this pass needs to be rendered
+ if (channels)
+ casterBatchRenderer.Add(renderer, part->subMeshIndex, channels, xformInfo.worldMatrix, xformInfo.transformType);
+
+ previousKey = currentKey;
+ prevCustomPropsHash = customPropsHash;
+ }
+ casterBatchRenderer.Flush();
+
+#else
+
+ int castersSize = data.size();
+ device.GetFrameStats().AddShadowCasters(castersSize);
+ int cascadeMask = 1 << splitIndex;
+ for( int i = 0; i < castersSize; ++i )
+ {
+ ShadowCasterData& caster = data[i];
+
+ // This caster can be skipped for this shadow render pass (e.g. this face of cubemap
+ // or this split of directional shadow).
+ if( caster.visibleCascades & cascadeMask )
+ {
+ BaseRenderer* renderer = caster.node->renderer;
+ const TransformInfo& xformInfo = renderer->GetTransformInfo ();
+
+ SetupObjectMatrix(xformInfo.worldMatrix, xformInfo.transformType);
+ size_t partsStartIndex = caster.partsStartIndex;
+ size_t partsEndIndex = caster.partsEndIndex;
+ for( size_t m = partsStartIndex; m < partsEndIndex; ++m )
+ {
+ ShadowCasterPartData& part = dataParts[m];
+
+ //@TODO: if this returns true and we have any sort of batching, we'd have to break batches here
+ renderer->ApplyCustomProperties(*(part.material), part.shader, part.subShaderIndex);
+
+ const ChannelAssigns* channels = part.material->SetShadowCasterPassWithShader(part.shader, part.subShaderIndex);
+ renderer->Render( part.subMeshIndex, *channels );
+ }
+ }
+ }
+
+#endif // GFX_ENABLE_SHADOW_BATCHING
+
+ device.SetViewMatrix(matView);
+ device.SetWorldMatrix(matWorld);
+}
+
+// return false if focus region is empty: no need to render anything in that case
+static bool SetupDirectionalLightShadowCamera(
+ const ShadowCameraData& cameraData,
+ const Light& light,
+ int splitIndex, int shadowSizeX, int shadowSizeY,
+ const MinMaxAABB& casterBounds, const MinMaxAABB& receiverBounds, const Transform& lt,
+ ShadowCascadeInfo& outCascade )
+{
+ DebugAssertIf( light.GetType() != kLightDirectional );
+ DebugAssertIf( splitIndex < 0 || splitIndex >= cameraData.splitCount );
+
+ const Camera& camera = *cameraData.camera;
+ const Matrix4x4f* frustumTransform = &cameraData.cameraClipToWorld;
+ Matrix4x4f localFrustumTransform;
+ float cameraFarZ = cameraData.projectionFar;
+ float shadowFarZ = cameraData.shadowDistance;
+ float farPlaneScale = 1.0f;
+
+ ShadowProjection projectionType = (ShadowProjection)GetQualitySettings().GetCurrent().shadowProjection;
+ if( projectionType == kShadowProjStableFit )
+ {
+ /////////////////@TODO: WTF? Static abuse??? static Matrix4x4f cameraProjection;
+
+ // Choose the camera-local frustum matrix to keep us numerically stable!
+ static Matrix4x4f cameraProjection;
+ camera.GetImplicitProjectionMatrix( cameraData.projectionNear, cameraData.projectionFar, cameraProjection );
+ Matrix4x4f::Invert_Full( cameraProjection, localFrustumTransform );
+ frustumTransform = &localFrustumTransform;
+
+ Vector3f cornerPos;
+ localFrustumTransform.PerspectiveMultiplyPoint3( Vector3f(1, 1, 1), cornerPos );
+ float cornerDist = Magnitude( cornerPos );
+
+ // We scale our frustum to unit size by dividing lengths by shadowDistance
+ // and intersect the sphere with center (0,0,ShadowSphereOffset) going through (0,0,1)
+ // We need to get the Z distance where the sphere intersects the frustum edge
+ // This is really a 2D problem in the plane between two opposite edges of the frustum
+ // Let's look at the right-angled triangle with sides b = 1, c = cornerDist / farPlaneZ
+ // Pythagoras gives us the length of a: a^2 + b^2 = c^2, b=1 -> a = sqrt(c^2 - 1)
+ // We want to intersect the line y = a*x -> y = sqrt(c^2 - 1) * x
+ // and the circle (x-p)^2 + y^2 = r^2 with radius r and center (p,0)
+ float c = cornerDist / cameraFarZ;
+ float p = CalculateShadowSphereOffset(camera);
+ float r = 1.0f - p;
+ // Wolfram Alpha solution for (x-p)^2 + (sqrt(c^2 - 1)*x)^2 = r^2
+ farPlaneScale = (sqrt(-c*c*p*p+c*c*r*r+p*p)+p)/(c*c);
+
+ #if !UNITY_RELEASE
+ // Check that the distance we calculate the frustum from is correct
+ Vector3f edgeVector = cornerPos / cameraFarZ;
+ Vector3f frustumIntersection = edgeVector * shadowFarZ * farPlaneScale;
+ float shadowRadius = r * shadowFarZ;
+ float centerDist = p * shadowFarZ;
+ Vector3f shadowCenter(0, 0, -centerDist);
+ float dist = Magnitude( frustumIntersection - shadowCenter );
+ DebugAssert( Abs(dist - shadowRadius) < 0.001f * shadowRadius );
+ #endif
+ }
+ float nearZ = cameraData.projectionNear;
+ float scaledShadowRange = shadowFarZ * farPlaneScale - nearZ;
+ float frustumScale = scaledShadowRange / (cameraFarZ - nearZ);
+ if( frustumScale <= Vector3f::epsilon )
+ {
+ return false;
+ }
+
+ // calculate frustum split corners
+ Vector3f cameraFrustum[8];
+ GetFrustumPoints( *frustumTransform, cameraFrustum );
+ Vector3f frustumSplit[8];
+ // split factors are relative to camera frustum (not shadow frustum)
+ float nearSplit = cameraData.splitPercentages[splitIndex] * frustumScale;
+ float farSplit = cameraData.splitPercentages[splitIndex+1] * frustumScale;
+ outCascade.minViewDistance = nearZ + nearSplit * (cameraFarZ - nearZ);
+ outCascade.maxViewDistance = nearZ + farSplit * (cameraFarZ - nearZ);
+ GetFrustumPortion( cameraFrustum, nearSplit, farSplit, frustumSplit );
+
+ std::vector<Vector3f> focusPoints;
+ if( projectionType == kShadowProjCloseFit )
+ {
+ // find the focused body: intersection of frustum & receiver bounds, extruded along
+ // light to include all casters
+ Vector3f lightDir = lt.TransformDirection(Vector3f(0,0,1));
+ CalculateFocusedLightHull( frustumSplit, lightDir, receiverBounds, focusPoints);
+ if( focusPoints.empty() )
+ {
+ outCascade.lightMatrix.SetIdentity();
+ outCascade.projMatrix.SetOrtho( -1.0f, 1.0f, -1.0f, 1.0f, 0.1f, 10.0f );
+ return false;
+ }
+ }
+ else
+ {
+ // TODO: if frustum does not intersect scene caster&receiver bounds, return false
+ }
+
+ // do initial light placement
+ Vector3f center = casterBounds.GetCenter();
+ float castersRadius = Magnitude(casterBounds.GetMax() - casterBounds.GetMin()) * 0.5f;
+ Vector3f axisX = lt.TransformDirection(Vector3f(1,0,0));
+ Vector3f axisY = lt.TransformDirection(Vector3f(0,1,0));
+ Vector3f axisZ = lt.TransformDirection(Vector3f(0,0,1));
+ Vector3f pos = center - axisZ * castersRadius * 1.2f;
+
+ outCascade.lightMatrix.SetPositionAndOrthoNormalBasis( pos, axisX, axisY, axisZ );
+
+ // In Z direction, the final light frustum must encapsulate both caster and receiver bounds.
+ // So take union of those, transform into light space and figure out min/max Z.
+ MinMaxAABB unionBounds = AddAABB( casterBounds, receiverBounds );
+ float minLightZ = std::numeric_limits<float>::infinity();
+ float maxLightZ = -std::numeric_limits<float>::infinity();
+ Vector3f unionPoints[8];
+ unionBounds.GetVertices( unionPoints );
+ for( int i = 0; i < 8; ++i )
+ {
+ Vector3f p = outCascade.lightMatrix.InverseMultiplyPoint3Affine( unionPoints[i] );
+ minLightZ = std::min( p.z, minLightZ );
+ maxLightZ = std::max( p.z, maxLightZ );
+ }
+ float centerLightSpaceZ = (minLightZ + maxLightZ) * 0.5f;
+ float lightDistanceZ = (maxLightZ - minLightZ) * 0.5f;
+
+ Vector3f boundsSize;
+
+ // calculate frustum bounds in light space
+ MinMaxAABB frustumBounds;
+ if( projectionType == kShadowProjCloseFit )
+ {
+ for( int i = 0; i < focusPoints.size(); ++i )
+ {
+ Vector3f p = outCascade.lightMatrix.InverseMultiplyPoint3Affine( focusPoints[i] );
+ p.z = centerLightSpaceZ;
+ frustumBounds.Encapsulate( p );
+ }
+ boundsSize = frustumBounds.GetMax() - frustumBounds.GetMin();
+ }
+ else if( projectionType == kShadowProjStableFit )
+ {
+ Vector3f sphereCenter;
+ float radius;
+ // Sphere is in camera space, so view vector is along negative Z
+ CalculateBoundingSphereFromFrustumPoints( frustumSplit, sphereCenter, radius );
+ float maxViewDist = Abs( sphereCenter.z ) + radius;
+ outCascade.maxViewDistance = std::min( maxViewDist, shadowFarZ );
+
+ // Now we transform our sphere center into world coordinates
+ sphereCenter = camera.GetCameraToWorldMatrix().MultiplyPoint3( sphereCenter );
+ outCascade.outerSphere.Set( sphereCenter, radius );
+
+ Vector3f p = outCascade.lightMatrix.InverseMultiplyPoint3Affine( sphereCenter );
+ p.z = centerLightSpaceZ;
+ frustumBounds.Encapsulate( p );
+ frustumBounds.Expand( radius );
+ boundsSize = Vector3f(radius, radius, radius) * 2.0f;
+ }
+ else
+ {
+ for( int i = 0; i < 8; ++i )
+ {
+ Vector3f p = outCascade.lightMatrix.InverseMultiplyPoint3Affine( frustumSplit[i] );
+ p.z = centerLightSpaceZ;
+ frustumBounds.Encapsulate( p );
+ }
+ boundsSize = frustumBounds.GetMax() - frustumBounds.GetMin();
+ }
+ Vector3f boundsCenter = frustumBounds.GetCenter();
+ Vector3f halfSize = boundsSize * 0.5f;
+
+ // add a small guard band to prevent sampling outside map
+ //static const float kGuardPixels = 1.0f;
+ //frustumBounds.Expand( Vector3f(kGuardPixels / shadowSizeX, kGuardPixels / shadowSizeY, 0) );
+
+ // quantize the position to shadow map texel size; gets rid of some "shadow swimming"
+ double texelSizeX = boundsSize.x / shadowSizeX;
+ double texelSizeY = boundsSize.y / shadowSizeY;
+ pos = outCascade.lightMatrix.MultiplyPoint3(boundsCenter);
+ double projX = axisX.x * (double)pos.x + axisX.y * (double)pos.y + axisX.z * (double)pos.z;
+ double projY = axisY.x * (double)pos.x + axisY.y * (double)pos.y + axisY.z * (double)pos.z;
+ float modX = float( fmod( projX, texelSizeX ) );
+ float modY = float( fmod( projY, texelSizeY ) );
+ pos -= axisX * modX;
+ pos -= axisY * modY;
+
+ // move position back so it encloses everything we need
+ pos -= axisZ * lightDistanceZ * 1.1f;
+ outCascade.lightMatrix.SetPosition( pos );
+
+ outCascade.nearPlane = lightDistanceZ*0.1f;
+ outCascade.farPlane = lightDistanceZ*2.2f;
+ outCascade.projMatrix.SetOrtho( -halfSize.x, halfSize.x, -halfSize.y, halfSize.y, outCascade.nearPlane, outCascade.farPlane );
+
+ outCascade.viewMatrix = outCascade.lightMatrix;
+ outCascade.viewMatrix.SetAxisZ( -outCascade.viewMatrix.GetAxisZ() );
+ outCascade.viewMatrix.Invert_Full();
+
+ Matrix4x4f texMatrix = Matrix4x4f::identity;
+ texMatrix.Get(0,0) = 0.5f;
+ texMatrix.Get(1,1) = 0.5f;
+ texMatrix.Get(2,2) = 0.5f;
+ texMatrix.Get(0,3) = 0.5f;
+ texMatrix.Get(1,3) = 0.5f;
+ texMatrix.Get(2,3) = 0.5f;
+
+ MultiplyMatrices4x4 (&outCascade.projMatrix, &outCascade.viewMatrix, &outCascade.worldToClipMatrix);
+ MultiplyMatrices4x4 (&texMatrix, &outCascade.worldToClipMatrix, &outCascade.shadowMatrix);
+ return true;
+}
+
+
+static bool PositionShadowSpotCamera( const ShadowCameraData& cameraData, const Light* light, Matrix4x4f& outShadowMatrix )
+{
+ DebugAssertIf( light->GetType() != kLightSpot );
+ GfxDevice& device = GetGfxDevice();
+
+ Matrix4x4f viewMatrix, projMatrix;
+ const Transform& lt = light->GetComponent(Transform);
+ // just use spotlight
+ Matrix4x4f s;
+ s.SetScale( Vector3f(1,1,-1) );
+
+ Matrix4x4f worldToLocalMatrixNoScale = lt.GetWorldToLocalMatrixNoScale();
+ MultiplyMatrices4x4 (&s, &worldToLocalMatrixNoScale, &viewMatrix);
+ // On NVIDIA cards in OpenGL using too low near plane results in shadow artifacts. Something like 0.02
+ // is when artifacts start to appear. So I set near plane to be 4% of the range, that seems to work ok.
+ float nearPlane = light->GetRange() * 0.04f;
+ float farPlane = light->GetRange();
+ projMatrix.SetPerspectiveCotan( light->GetCotanHalfSpotAngle(), nearPlane, farPlane );
+
+ device.SetProjectionMatrix (projMatrix);
+ device.SetViewMatrix( viewMatrix.GetPtr() );
+ SetClippingPlaneShaderProps();
+
+ // shadow bias
+ float bias = light->GetShadowBias ();
+ float clampVerts = 0.0f; // disable vertex clamping for spot lights
+ device.GetBuiltinParamValues().SetVectorParam(kShaderVecLightShadowBias, Vector4f(bias, clampVerts, 0, 0));
+
+ Matrix4x4f texMatrix = Matrix4x4f::identity;
+ texMatrix.Get(0,0) = 0.5f;
+ texMatrix.Get(1,1) = 0.5f;
+ texMatrix.Get(2,2) = 0.5f;
+ texMatrix.Get(0,3) = 0.5f;
+ texMatrix.Get(1,3) = 0.5f;
+ texMatrix.Get(2,3) = 0.5f;
+
+ Matrix4x4f temp;
+ // outShadowMatrix = texMatrix * projMatrix * viewMatrix
+ MultiplyMatrices4x4 (&texMatrix, &projMatrix, &temp);
+ MultiplyMatrices4x4 (&temp, &viewMatrix, &outShadowMatrix);
+
+ ShaderLab::g_GlobalProperties->SetVector( kSLPropShadowProjectionParams, 0.0f, nearPlane, farPlane, 0.0f );
+ return true;
+}
+
+
+static void PositionShadowPointCamera( const Vector3f& lightPos, float lightRange, CubemapFace face, Matrix4x4f& outWorldToClipMatrix, Vector3f& outViewDir )
+{
+ GfxDevice& device = GetGfxDevice();
+
+ Matrix4x4f viewMatrix, projMatrix;
+
+ switch( face ) {
+ case kCubeFacePX:
+ outViewDir = Vector3f( 1, 0, 0);
+ viewMatrix.SetOrthoNormalBasisInverse( Vector3f( 0, 0,-1), Vector3f( 0,-1, 0), Vector3f(-1, 0, 0) );
+ break;
+ case kCubeFaceNX:
+ outViewDir = Vector3f(-1, 0, 0);
+ viewMatrix.SetOrthoNormalBasisInverse( Vector3f( 0, 0, 1), Vector3f( 0,-1, 0), Vector3f( 1, 0, 0) );
+ break;
+ case kCubeFacePY:
+ outViewDir = Vector3f( 0, 1, 0);
+ viewMatrix.SetOrthoNormalBasisInverse( Vector3f( 1, 0, 0), Vector3f( 0, 0, 1), Vector3f( 0,-1, 0) );
+ break;
+ case kCubeFaceNY:
+ outViewDir = Vector3f( 0,-1, 0);
+ viewMatrix.SetOrthoNormalBasisInverse( Vector3f( 1, 0, 0), Vector3f( 0, 0,-1), Vector3f( 0, 1, 0) );
+ break;
+ case kCubeFacePZ:
+ outViewDir = Vector3f( 0, 0, 1);
+ viewMatrix.SetOrthoNormalBasisInverse( Vector3f( 1, 0, 0), Vector3f( 0,-1, 0), Vector3f( 0, 0,-1) );
+ break;
+ case kCubeFaceNZ:
+ outViewDir = Vector3f( 0, 0,-1);
+ viewMatrix.SetOrthoNormalBasisInverse( Vector3f(-1, 0, 0), Vector3f( 0,-1, 0), Vector3f( 0, 0, 1) );
+ break;
+ default:
+ AssertString("Invalid cube face!");
+ outViewDir = Vector3f( 0, 0, 0);
+ viewMatrix.SetIdentity();
+ break;
+ }
+
+ Matrix4x4f tr;
+ tr.SetTranslate( -lightPos );
+ viewMatrix *= tr;
+
+ float nearPlane = std::min(lightRange * 0.01f,0.01f);
+ float farPlane = lightRange * 1.01f;
+ projMatrix.SetPerspective( 90.0f, 1.0f, nearPlane, farPlane );
+ device.SetProjectionMatrix (projMatrix);
+ device.SetViewMatrix( viewMatrix.GetPtr() );
+ SetClippingPlaneShaderProps();
+ MultiplyMatrices4x4 (&projMatrix, &viewMatrix, &outWorldToClipMatrix);
+
+ ShaderLab::g_GlobalProperties->SetVector( kSLPropShadowProjectionParams, 0.0f, nearPlane, farPlane, 0.0f );
+}
+
+
+static int CalculateShadowMapSize( const ShadowCameraData& cameraData, const ActiveLight& activeLight )
+{
+ const Light* light = activeLight.light;
+ const Rectf& bounds = activeLight.screenRect;
+ int mapSize = 128;
+
+#if UNITY_PS3
+ // Always allow high quality shadows.
+ bool allowHighQualityShadows = true;
+#elif UNITY_XENON
+ // Only allow high quality shadows if shadow resolution is Very High or higher. This enables predicated tiling.
+ bool allowHighQualityShadows = (light->GetFinalShadowResolution() >= 3);
+#else
+ bool allowHighQualityShadows = (gGraphicsCaps.videoMemoryMB >= kVRAMEnoughForLargeShadowmaps);
+#endif
+
+ const float kMultPoint = 1.0f; // Assume "Very High" shadow map resolution is 1x screen size for point lights.
+ const float kMultSpot = 2.0f; // Assume "Very High" shadow map resolution is 2x screen size for spot lights.
+ const float kMultDir = 3.8f; // Assume "Very High" shadow map resolution is almost 4x of screen size for directional lights.
+
+ switch( light->GetType() )
+ {
+ case kLightPoint:
+ {
+ const int kMaxShadowSize = std::min( gGraphicsCaps.maxCubeMapSize, allowHighQualityShadows ? kShadowmapPointSizeMax : kShadowmapPointSizeMin );
+ // Based on light size on screen
+ float pixelSize = std::max( bounds.width * cameraData.viewWidth, bounds.height * cameraData.viewHeight );
+ mapSize = NextPowerOfTwo( int(pixelSize * kMultPoint) );
+ mapSize >>= cameraData.qualityShift;
+ mapSize = clamp<int>( mapSize, 16, kMaxShadowSize );
+ }
+ break;
+ case kLightSpot:
+ {
+ const int kMaxShadowSize = std::min( gGraphicsCaps.maxRenderTextureSize, allowHighQualityShadows ? kShadowmapSpotSizeMax : kShadowmapSpotSizeMin );
+ // Based on light size on screen
+ float pixelSize = std::max( bounds.width * cameraData.viewWidth, bounds.height * cameraData.viewHeight );
+ mapSize = NextPowerOfTwo( int( pixelSize * kMultSpot ) );
+ mapSize >>= cameraData.qualityShift;
+ mapSize = clamp<int>( mapSize, 16, kMaxShadowSize );
+ }
+ break;
+ case kLightDirectional:
+ {
+ const int kMaxShadowSize = std::min( gGraphicsCaps.maxRenderTextureSize, allowHighQualityShadows ? kShadowmapDirSizeMax : kShadowmapDirSizeMin );
+ int viewSize = int( std::max( cameraData.viewWidth, cameraData.viewHeight ) );
+ mapSize = NextPowerOfTwo( int( viewSize * kMultDir ) );
+ mapSize >>= cameraData.qualityShift;
+ mapSize = clamp<int>( mapSize, 32, kMaxShadowSize );
+ }
+ break;
+ default:
+ AssertString( "Unknown light type!" );
+ }
+
+ return mapSize;
+}
+
+static void PrepareShadowMapParams (ShadowCameraData& camData, const Light* light, Matrix4x4f outShadowMatrices[kMaxShadowCascades])
+{
+ // Use cascaded shadow maps for directional lights and perspective cameras only.
+ // (cascaded shadow maps lose their point for ortho cameras).
+ bool usePSSM = (light->GetType() == kLightDirectional) && (!camData.camera->GetOrthographic());
+
+ // Quality setting for shadow maps
+ const int lightShadowResolution = light->GetFinalShadowResolution();
+ camData.qualityShift = QualitySettings::kShadowResolutionCount - 1 - lightShadowResolution;
+
+
+ if( usePSSM )
+ {
+ if ( GetGfxDevice().GetRenderer() == kGfxRendererOpenGLES20Mobile || GetGfxDevice().GetRenderer() == kGfxRendererOpenGLES30 )
+ camData.splitCount = 1;
+ else
+ camData.splitCount = GetQualitySettings().GetCurrent().shadowCascades;
+
+ CalculatePSSMDistances( camData.camera->GetNear(), camData.shadowDistance, camData.splitCount, camData.splitDistances, camData.splitPercentages );
+ for( int i = camData.splitCount+1; i < kMaxShadowCascades+1; ++i )
+ {
+ camData.splitDistances[i] = camData.splitDistances[i-1] * 1.1f;
+ camData.splitPercentages[i] = camData.splitPercentages[i-1] * 1.1f;
+ }
+ }
+ else
+ {
+ camData.splitDistances[0] = camData.camera->GetNear();
+ camData.splitDistances[1] = camData.shadowDistance;
+ camData.splitPercentages[0] = 0.0f;
+ camData.splitPercentages[1] = 1.0f;
+ camData.splitCount = 1;
+ }
+ // Clear shadow split spheres
+ Vector4f unusedSphere(0, 0, 0, -std::numeric_limits<float>::infinity());
+ for( int i = 0; i < kMaxShadowCascades; ++i )
+ {
+ camData.splitSphereCentersAndSquaredRadii[i] = unusedSphere;
+ }
+ // Zero out unused shadow map matrices. Otherwise for some reason occasionally causes wrong rendering
+ // on D3D REF.
+ for (int i = camData.splitCount; i < kMaxShadowCascades; ++i)
+ {
+ memset (&outShadowMatrices[i].m_Data[0], 0, sizeof(outShadowMatrices[i]));
+ }
+}
+
+
+RenderTexture* RenderShadowMaps( ShadowCameraData& cameraData, const ActiveLight& activeLight, const MinMaxAABB& receiverBounds, bool excludeLightmapped, Matrix4x4f outShadowMatrices[kMaxShadowCascades] )
+{
+ const Light* light = activeLight.light;
+ PROFILER_AUTO_GFX(gShadowsRender, light);
+ GPU_AUTO_SECTION(kGPUSectionShadowPass);
+
+ DebugAssertIf( outShadowMatrices == NULL );
+
+ if (!receiverBounds.IsValid())
+ return NULL;
+
+ PrepareShadowMapParams (cameraData, light, outShadowMatrices);
+
+ RenderTexture* shadowmap = NULL;
+ GfxDevice& device = GetGfxDevice();
+
+ int shadowSize = CalculateShadowMapSize( cameraData, activeLight );
+ int shadowWidth = shadowSize;
+ int shadowHeight = shadowSize;
+ DepthBufferFormat depthFormat = kDepthFormat16;
+ RenderTextureFormat shadowFormat;
+ bool shadowCubeMap;
+ if( light->GetType() == kLightPoint )
+ {
+ shadowFormat = kRTFormatARGB32;
+ shadowCubeMap = true;
+ if (!gGraphicsCaps.hasRenderToCubemap)
+ return NULL;
+ }
+ else
+ {
+ // two splits cascaded shadow map should use 2:1 aspect texture
+ if( cameraData.splitCount == 2 )
+ shadowHeight /= 2;
+ shadowFormat = gGraphicsCaps.hasNativeShadowMap ? kRTFormatShadowMap : kRTFormatDepth;
+ if (gGraphicsCaps.buggySpotNativeShadowMap && light->GetType() == kLightSpot)
+ shadowFormat = kRTFormatDepth;
+
+ shadowCubeMap = false;
+ }
+
+ // Try to somewhat intelligently reduce shadow map resolution if we're getting close to VRAM limits.
+ // Only take into account things that can't be easily moved off-VRAM (screen + render textures).
+ // Allow shadowmap to take 1/3 of the available VRAM at max.
+ const int vramSizeKB = int(gGraphicsCaps.videoMemoryMB * 1024);
+ const GfxDeviceStats::MemoryStats& memoryStats = device.GetFrameStats().GetMemoryStats();
+ const int currentVramUsageKB = (memoryStats.screenBytes + memoryStats.renderTextureBytes) / 1024;
+ const int allowedVramUsageKB = int((vramSizeKB - currentVramUsageKB) * kVRAMMaxFreePortionForShadowMap);
+ int neededVramForShadowmapKB;
+ do {
+ neededVramForShadowmapKB = EstimateRenderTextureSize (shadowWidth, shadowHeight, 1, shadowFormat, depthFormat, shadowCubeMap?kTexDimCUBE:kTexDim2D, false) / 1024;
+ if( neededVramForShadowmapKB < allowedVramUsageKB )
+ break;
+ #if !UNITY_RELEASE
+ printf_console("Shadowmap %ix%i won't fit, reducing size (needed mem=%i used mem=%i allowedmem=%i)\n", shadowWidth, shadowHeight, neededVramForShadowmapKB, currentVramUsageKB, allowedVramUsageKB );
+ #endif
+ shadowWidth /= 2;
+ shadowHeight /= 2;
+ } while( shadowWidth > 4 && shadowHeight > 4 );
+ // We totally don't have VRAM for shadows left! Continue without shadows...
+ if( shadowWidth <= 4 || shadowHeight <= 4 )
+ return NULL;
+
+
+ ///////////////@TODO: Move creation of the shadow buffer until after we have determined if there is anything to be culled...
+
+
+ // Create the shadowmap
+ UInt32 flags = 0;
+ if (shadowCubeMap)
+ flags |= RenderBufferManager::kRBCubemap;
+ shadowmap = GetRenderBufferManager().GetTempBuffer (shadowWidth, shadowHeight, depthFormat, shadowFormat, flags, kRTReadWriteLinear);
+
+ // By default, enable PCF filtering for native shadow maps.
+ // However on mobile that's quite expensive, so only enable it if light has Soft
+ // shadows set.
+ bool enablePCFFilter = (shadowFormat==kRTFormatShadowMap);
+ if (!gGraphicsCaps.hasShadowCollectorPass && (light->GetShadows() < kShadowSoft))
+ enablePCFFilter = false;
+ // Disable PCF filtering if we need to due to driver issues
+ if (gGraphicsCaps.buggyShadowMapBilinearSampling)
+ enablePCFFilter = false;
+
+ shadowmap->GetSettings().m_FilterMode = enablePCFFilter ? kTexFilterBilinear : kTexFilterNearest;
+ shadowmap->ApplySettings();
+
+ // Check if shadow map can be actually created. If for some reason it can't, return NULL.
+ if( !shadowmap->IsCreated() )
+ {
+ if( !shadowmap->Create() )
+ {
+ GetRenderBufferManager().ReleaseTempBuffer( shadowmap );
+ return NULL;
+ }
+ }
+
+ MinMaxAABB casterBounds;
+ ShadowCasters casters;
+ ShadowCasterParts casterParts;
+
+ // Cull shadow casters
+ CullingOutput visibleShadowCasters;
+ {
+ PROFILER_AUTO(gCullShadowCasters, NULL);
+ CreateCullingOutput(cameraData.sceneCullParameters->renderers, visibleShadowCasters);
+ CullShadowCasters (*activeLight.light, cameraData, cameraData.sceneCullParameters->excludeLightmappedShadowCasters, visibleShadowCasters);
+
+ }
+ // Send OnBecameVisible / OnBecameInvisible callback for culled shadow caster renderers
+ GetScene().NotifyVisible (visibleShadowCasters);
+
+ casters.reserve(64);
+ casterParts.reserve(64);
+
+ GenerateShadowCasterParts (*light, cameraData, visibleShadowCasters, casterBounds, casters, casterParts);
+
+ DestroyCullingOutput(visibleShadowCasters);
+
+ int castersSize = casters.size();
+ if( castersSize == 0 )
+ {
+ // If there are no shadow casters, there will be no shadows. Return NULL shadowmap
+ // in this case, the render queue code will use non-shadowed path in then.
+ GetRenderBufferManager().ReleaseTempBuffer( shadowmap );
+ return NULL;
+ }
+
+
+ SetAndRestoreWireframeMode setWireframeOff(false); // turn off wireframe; will restore old value in destructor
+
+ // If all casters for directional light are outside the view frustum, then caster bounds
+ // will be invalid at this point. In that case, make them equal to receiver bounds (case 17871).
+ if( !casterBounds.IsValid() )
+ casterBounds = receiverBounds;
+
+ const Transform& lt = light->GetComponent(Transform);
+ Vector3f lightPos = lt.GetPosition();
+ Quaternionf lightRot = lt.GetRotation();
+ Vector3f lightDir = RotateVectorByQuat(lightRot, Vector3f(0,0,1));
+
+ if( light->GetType() == kLightPoint )
+ {
+ PROFILER_AUTO_GFX(gShadowsRenderPoint,light);
+ // point light: render into cube map
+ device.GetBuiltinParamValues().SetVectorParam(kShaderVecLightPositionRange, Vector4f(lightPos.x, lightPos.y, lightPos.z, 1.0f/light->GetRange()));
+ for( int f = 0; f < 6; ++f )
+ {
+ CubemapFace face = (CubemapFace)f;
+ // activate shadow render target
+ RenderTexture::SetActive (shadowmap, 0, face, RenderTexture::kFlagDontRestore);
+
+ GraphicsHelper::Clear (kGfxClearAll, ColorRGBAf(1,1,1,1).GetPtr(), 1.0f, 0);
+ GPU_TIMESTAMP();
+
+ // position the shadow camera
+ Matrix4x4f shadowWorldToClip;
+ Vector3f viewDir;
+ PositionShadowPointCamera( lightPos, light->GetRange(), face, shadowWorldToClip, viewDir );
+ Plane planes[6];
+ ExtractProjectionPlanes( shadowWorldToClip, planes );
+
+ // Go over casters and mark the ones that are not in our face pyramid as skipped.
+ // First four planes are the ones to check against (left, right, bottom, top).
+ int casterCount = casters.size();
+ for( int c = 0; c < casterCount; ++c )
+ {
+ ShadowCasterData& caster = casters[c];
+ if( IntersectAABBFrustum( *caster.worldAABB, planes, 15 ) )
+ caster.visibleCascades = 1;
+ else
+ caster.visibleCascades = 0;
+ }
+
+ RenderCasters( 0, *light, lightPos, viewDir, casters, casterParts, cameraData );
+ }
+ }
+ else if( light->GetType() == kLightDirectional )
+ {
+ PROFILER_AUTO_GFX(gShadowsRenderDir,light);
+ // directional light: render splits
+ RenderTexture::SetActive (shadowmap, 0, kCubeFaceUnknown, RenderTexture::kFlagDontRestore);
+ GraphicsHelper::Clear (kGfxClearAll, ColorRGBAf(1,1,1,1).GetPtr(), 1.0f, 0);
+ GPU_TIMESTAMP();
+
+ int tilesX, tilesY;
+ switch( cameraData.splitCount )
+ {
+ case 1: tilesX = 1; tilesY = 1; break;
+ case 2: tilesX = 2; tilesY = 1; break;
+ case 4: tilesX = 2; tilesY = 2; break;
+ default: tilesX = 1; tilesY = 1; AssertString( "Unknown split count!" );
+ }
+
+ int splitIndex = 0;
+ bool validMatrices[kMaxShadowCascades];
+ memset(validMatrices, 0, sizeof(validMatrices));
+ int lastValidMatrix = 0;
+ int tileSizeX = shadowWidth / tilesX;
+ int tileSizeY = shadowHeight / tilesY;
+ ShadowCascadeInfo cascades[4];
+ for( int ty = 0; ty < tilesY; ++ty )
+ {
+ for( int tx = 0; tx < tilesX; ++tx )
+ {
+ // position the shadow camera for this split
+ ShadowCascadeInfo& cascade = cascades[splitIndex];
+ cascade.shadowMatrix.SetIdentity();
+ cascade.outerSphere.Set( Vector3f::zero, -1e9f );
+ cascade.enabled = SetupDirectionalLightShadowCamera( cameraData, *light, splitIndex,
+ tileSizeX, tileSizeY, casterBounds, receiverBounds, lt, cascade );
+ const Sphere& sphere = cascade.outerSphere;
+ outShadowMatrices[splitIndex] = cascade.shadowMatrix;
+ cameraData.splitSphereCentersAndSquaredRadii[splitIndex] = Vector4f(sphere.GetCenter(), Sqr(sphere.GetRadius()));
+ ++splitIndex;
+ }
+ }
+ CullDirectionalCascades( casters, cascades, splitIndex, lightRot, lightDir, cameraData);
+ splitIndex = 0;
+ for( int ty = 0; ty < tilesY; ++ty )
+ {
+ for( int tx = 0; tx < tilesX; ++tx )
+ {
+ const ShadowCascadeInfo& cascade = cascades[splitIndex];
+ if( cascade.enabled )
+ {
+ device.SetProjectionMatrix(cascade.projMatrix);
+ device.SetViewMatrix( cascade.viewMatrix.GetPtr() );
+ SetClippingPlaneShaderProps();
+
+ // shadow bias
+ float bias = light->GetShadowBias ();
+ bias *= device.GetDeviceProjectionMatrix()[2*4+2] * -1.0f; // make bias constant in world space
+ float clampVerts = 1.0f; // enable vertex clamping for directional lights
+ device.GetBuiltinParamValues().SetVectorParam(kShaderVecLightShadowBias, Vector4f(bias, clampVerts, 0, 0));
+
+ ShaderLab::g_GlobalProperties->SetVector( kSLPropShadowProjectionParams, 0.0f, cascade.nearPlane, cascade.farPlane, 0.0f );
+
+ Matrix4x4f texMatrix = Matrix4x4f::identity;
+ texMatrix.Get(0,0) = 1.0f / tilesX;
+ texMatrix.Get(1,1) = 1.0f / tilesY;
+ texMatrix.Get(2,2) = 1.0f;
+ texMatrix.Get(0,3) = (float)tx / (float)tilesX;
+ texMatrix.Get(1,3) = (float)ty / (float)tilesY;
+
+ MultiplyMatrices4x4 (&texMatrix, &cascade.shadowMatrix, &outShadowMatrices[splitIndex]);
+ lastValidMatrix = splitIndex;
+ validMatrices[splitIndex] = true;
+
+ if( cameraData.splitCount == 1 )
+ device.SetViewport( tx * tileSizeX+1, ty * tileSizeY+1, tileSizeX-2, tileSizeY-2 );
+ else
+ device.SetViewport( tx * tileSizeX, ty * tileSizeY, tileSizeX, tileSizeY );
+
+ RenderCasters( splitIndex, *light, lightPos, lightDir, casters, casterParts, cameraData );
+ }
+ else
+ {
+ outShadowMatrices[splitIndex].SetIdentity();
+ }
+ ++splitIndex;
+ }
+ }
+ // make sure all matrices are valid, since depth comparisons are not exact
+ //for( int i = 0; i < kMaxPSSMSplits; i++ )
+ // if( !validMatrices[i] )
+ // outShadowMatrices[i] = outShadowMatrices[lastValidMatrix];
+ }
+ else
+ {
+ PROFILER_AUTO_GFX(gShadowsRenderSpot,light);
+ // spot light: render into single shadow map
+ RenderTexture::SetActive (shadowmap, 0, kCubeFaceUnknown, RenderTexture::kFlagDontRestore);
+ GraphicsHelper::Clear (kGfxClearAll, ColorRGBAf(1,1,1,1).GetPtr(), 1.0f, 0);
+ GPU_TIMESTAMP();
+
+ // position the shadow camera
+ if( PositionShadowSpotCamera( cameraData, light, outShadowMatrices[0] ) )
+ {
+ RenderCasters( 0, *light, lightPos, lightDir, casters, casterParts, cameraData );
+ }
+ }
+
+ return shadowmap;
+}
+
+RenderTexture* BlurScreenShadowMap (RenderTexture* screenShadowMap, ShadowType shadowType, float farPlane, float blurWidth, float blurFade)
+{
+ DebugAssert (shadowType != kShadowHard); // paranoia
+ DebugAssert (shadowType != kShadowNone); // paranoia
+
+ const float kBlurThreshold = 0.2f; // 20 cm. If needed, this could be exposed per-light and passed down here.
+
+ float shaderBlurThreshold = kBlurThreshold / farPlane;
+ float invFarMul4 = 4.0f / farPlane * blurFade;
+ ShaderLab::g_GlobalProperties->SetVector (ShaderLab::Property("unity_ShadowBlurParams"), shaderBlurThreshold, invFarMul4, 0, 0);
+
+ screenShadowMap->GetSettings().m_FilterMode = kTexFilterNearest;
+
+ RenderTexture* blurredShadowMap = GetRenderBufferManager().GetTempBuffer (RenderBufferManager::kFullSize, RenderBufferManager::kFullSize, kDepthFormatNone, kRTFormatARGB32, 0, kRTReadWriteLinear);
+ RenderTexture::SetActive (blurredShadowMap, 0, kCubeFaceUnknown, RenderTexture::kFlagDontRestore);
+ // no need to clear
+
+ SetAndRestoreWireframeMode setWireframeOff(false); // turn off wireframe; will restore old value in destructor
+
+ const int* viewport = GetRenderManager().GetCurrentViewPort();
+ const float fWidth = viewport[2];
+ const float fHeight = viewport[3];
+
+ static Material* shadowBlurDiscMaterial = NULL;
+ static Material* shadowBlurDiscRotatedMaterial = NULL;
+ if(shadowBlurDiscMaterial == NULL)
+ {
+ Shader* shader = GetScriptMapper().FindShader ("Hidden/Shadow-ScreenBlur");
+ if (shader)
+ shadowBlurDiscMaterial = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+ }
+ if(shadowBlurDiscRotatedMaterial == NULL)
+ {
+ Shader* shader = GetScriptMapper().FindShader ("Hidden/Shadow-ScreenBlurRotated");
+ if (shader)
+ shadowBlurDiscRotatedMaterial = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+ }
+
+ Material* material = NULL;
+ int fillrate = GetGraphicsPixelFillrate(gGraphicsCaps.vendorID, gGraphicsCaps.rendererID);
+ const bool shadowRotatedBlurFastEnough = (fillrate >= kShadowRotatedBlurFillrateThreshold) || (fillrate == -1); // if unknown, assume it's fast enough
+ if(shadowBlurDiscRotatedMaterial && shadowBlurDiscRotatedMaterial->GetShader()->IsSupported() && shadowRotatedBlurFastEnough)
+ {
+ // This value is for compensating that rotated shadows generally look softer than non rotated and makes them look more like the same.
+ const float kShadowBlurRotatedMultiplier = 0.8f;
+ blurWidth *= kShadowBlurRotatedMultiplier;
+ material = shadowBlurDiscRotatedMaterial;
+ }
+ else
+ {
+ material = shadowBlurDiscMaterial;
+ }
+
+ SHADERPROP (MainTex);
+ SHADERPROP (BlurOffsets0);
+ SHADERPROP (BlurOffsets1);
+ SHADERPROP (BlurOffsets2);
+ SHADERPROP (BlurOffsets3);
+ SHADERPROP (BlurOffsets4);
+ SHADERPROP (BlurOffsets5);
+ SHADERPROP (BlurOffsets6);
+ SHADERPROP (BlurOffsets7);
+
+ static ColorRGBAf kBlurTable[8] = {
+ // 9 tap Poisson disc
+ //ColorRGBAf( 0.098484f, 0.0951260f, 0.0f, 0.0f), // center tap, we always sample it on (0,0)
+ ColorRGBAf(-0.957152f, -0.3877980f, 0.0f, 0.0f),
+ ColorRGBAf(-0.799006f, 0.9533680f, 0.0f, 0.0f),
+ ColorRGBAf( 0.940856f, 0.7262480f, 0.0f, 0.0f),
+ ColorRGBAf( 0.599230f, -0.8810998f, 0.0f, 0.0f),
+ ColorRGBAf(-0.288248f, -0.8555254f, 0.0f, 0.0f),
+ ColorRGBAf( 0.038728f, 0.8900720f, 0.0f, 0.0f),
+ ColorRGBAf( 0.954100f, -0.1302840f, 0.0f, 0.0f),
+ ColorRGBAf(-0.455428f, 0.3171780f, 0.0f, 0.0f),
+ };
+
+ float kBlurRadius = fWidth * (1.f / 640.0f) * fHeight * (1.0f / 480.0f);
+ kBlurRadius = blurWidth * clamp(kBlurRadius, 1.0f, 2.0f);
+
+ ColorRGBAf multiplier(
+ kBlurRadius * screenShadowMap->GetTexelSizeX(),
+ kBlurRadius * screenShadowMap->GetTexelSizeY(),
+ 0.0f,
+ 0.0f );
+
+ DeviceMVPMatricesState preserveMVP;
+
+ GfxDevice& device = GetGfxDevice();
+ // Clear so that tiled and multi-GPU systems don't do a RT unresolve
+ float clearColor[4] = {1,0,1,0};
+ device.Clear(kGfxClearColor, clearColor, 1.0f, 0);
+
+ LoadFullScreenOrthoMatrix();
+ material->SetColor( kSLPropBlurOffsets0, kBlurTable[0] * multiplier );
+ material->SetColor( kSLPropBlurOffsets1, kBlurTable[1] * multiplier );
+ material->SetColor( kSLPropBlurOffsets2, kBlurTable[2] * multiplier );
+ material->SetColor( kSLPropBlurOffsets3, kBlurTable[3] * multiplier );
+ material->SetColor( kSLPropBlurOffsets4, kBlurTable[4] * multiplier );
+ material->SetColor( kSLPropBlurOffsets5, kBlurTable[5] * multiplier );
+ material->SetColor( kSLPropBlurOffsets6, kBlurTable[6] * multiplier );
+ material->SetColor( kSLPropBlurOffsets7, kBlurTable[7] * multiplier );
+ material->SetTexture( kSLPropMainTex, screenShadowMap );
+ material->SetPass( 0 );
+
+ device.ImmediateBegin( kPrimitiveQuads );
+ device.ImmediateTexCoord( 0, 0,0,0 ); device.ImmediateVertex( 0, 0, 0 );
+ device.ImmediateTexCoord( 0, 1,0,0 ); device.ImmediateVertex( 1, 0, 0 );
+ device.ImmediateTexCoord( 0, 1,1,0 ); device.ImmediateVertex( 1, 1, 0 );
+ device.ImmediateTexCoord( 0, 0,1,0 ); device.ImmediateVertex( 0, 1, 0 );
+ device.ImmediateEnd();
+ GPU_TIMESTAMP();
+
+ GetRenderBufferManager().ReleaseTempBuffer( screenShadowMap );
+ return blurredShadowMap;
+}
+
+void SetCascadedShadowShaderParams (const Matrix4x4f* shadowMatrices, const float* splitDistances, const Vector4f* splitSphereCentersAndSquaredRadii)
+{
+ BuiltinShaderParamValues& params = GetGfxDevice().GetBuiltinParamValues();
+
+ // Does not set first shadow matrix!
+ DebugAssert (shadowMatrices);
+ params.SetMatrixParam(kShaderMatWorldToShadow1, shadowMatrices[1]);
+ params.SetMatrixParam(kShaderMatWorldToShadow2, shadowMatrices[2]);
+ params.SetMatrixParam(kShaderMatWorldToShadow3, shadowMatrices[3]);
+
+ DebugAssert (splitDistances);
+ params.SetVectorParam(kShaderVecLightSplitsNear, Vector4f(splitDistances));
+ params.SetVectorParam(kShaderVecLightSplitsFar, Vector4f(splitDistances+1));
+ DebugAssert (splitSphereCentersAndSquaredRadii);
+ params.SetVectorParam(kShaderVecShadowSplitSpheres0, splitSphereCentersAndSquaredRadii[0]);
+ params.SetVectorParam(kShaderVecShadowSplitSpheres1, splitSphereCentersAndSquaredRadii[1]);
+ params.SetVectorParam(kShaderVecShadowSplitSpheres2, splitSphereCentersAndSquaredRadii[2]);
+ params.SetVectorParam(kShaderVecShadowSplitSpheres3, splitSphereCentersAndSquaredRadii[3]);
+ params.SetVectorParam(kShaderVecShadowSplitSqRadii, Vector4f(splitSphereCentersAndSquaredRadii[0].w, splitSphereCentersAndSquaredRadii[1].w, splitSphereCentersAndSquaredRadii[2].w, splitSphereCentersAndSquaredRadii[3].w));
+}
+
+#endif // ENABLE_SHADOWS
diff --git a/Runtime/Camera/Shadows.h b/Runtime/Camera/Shadows.h
new file mode 100644
index 0000000..19eed71
--- /dev/null
+++ b/Runtime/Camera/Shadows.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_SHADOWS
+
+#include "ShadowSettings.h"
+#include "ShadowCulling.h"
+
+class MinMaxAABB;
+class Transform;
+class Light;
+class RenderTexture;
+class Matrix4x4f;
+struct ShadowedLight;
+struct ActiveLight;
+struct CullResults;
+
+struct ShadowCameraData : public ShadowCullData
+{
+ ShadowCameraData(const ShadowCullData& cullData) : ShadowCullData(cullData) {}
+
+ int splitCount;
+ float splitDistances[kMaxShadowCascades+1];
+ float splitPercentages[kMaxShadowCascades+1];
+ Vector4f splitSphereCentersAndSquaredRadii[kMaxShadowCascades];
+ int qualityShift; // shadow quality (0 = best, larger numbers worse)
+};
+
+bool GetSoftShadowsEnabled ();
+
+void SetShadowsKeywords (LightType lightType, ShadowType shadowType, bool screen, bool enableSoftShadows);
+
+// outDistances is array of size [splitCount+1] with near&far distances for each split
+// outPercentages is the same, only percentages of shadowFarPlane distance (0.5 = 50% of distance)
+void CalculatePSSMDistances (float nearPlane, float shadowFarPlane, int splitCount, float* outDistances, float* outPercentages);
+
+RenderTexture* RenderShadowMaps (ShadowCameraData& cameraData, const ActiveLight& activeLight, const MinMaxAABB& receiverBounds, bool excludeLightmapped, Matrix4x4f outShadowMatrices[kMaxShadowCascades]);
+RenderTexture* BlurScreenShadowMap (RenderTexture* screenShadowMap, ShadowType shadowType, float farPlane, float blurWidth, float blurFade);
+
+// Does not set first shadow matrix!
+void SetCascadedShadowShaderParams (const Matrix4x4f* shadowMatrices, const float* splitDistances, const Vector4f* splitSphereCentersAndSquaredRadii);
+
+#endif // ENABLE_SHADOWS
+
+
+bool CheckPlatformSupportsShadows ();
+void SetNoShadowsKeywords ();
diff --git a/Runtime/Camera/Skybox.cpp b/Runtime/Camera/Skybox.cpp
new file mode 100644
index 0000000..528859e
--- /dev/null
+++ b/Runtime/Camera/Skybox.cpp
@@ -0,0 +1,199 @@
+#include "UnityPrefix.h"
+#include "Skybox.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Camera.h"
+#include "CameraUtil.h"
+#include "Runtime/Shaders/Material.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+
+PROFILER_INFORMATION(gRenderSkyboxProfile, "Camera.RenderSkybox", kProfilerRender);
+
+
+using namespace ShaderLab;
+
+enum
+{
+ kSkyboxVertexChannels = (1<<kShaderChannelVertex) | (1<<kShaderChannelTexCoord0)
+};
+
+struct SkyboxVertex {
+ float x, y, z;
+ float tu, tv;
+};
+
+const int kSkyboxVertexCount = 6*4;
+static const SkyboxVertex kSkyboxVB[kSkyboxVertexCount] = {
+ { -1, 1, 1, 0,1 }, { 1, 1, 1, 1,1 }, { 1, -1, 1, 1,0 }, { -1, -1, 1, 0,0 },
+ { 1, 1, -1, 0,1 }, { -1, 1, -1, 1,1 }, { -1, -1, -1, 1,0 }, { 1, -1, -1, 0,0 },
+ { 1, 1, 1, 0,1 }, { 1, 1, -1, 1,1 }, { 1, -1, -1, 1,0 }, { 1, -1, 1, 0,0 },
+ { -1, 1, -1, 0,1 }, { -1, 1, 1, 1,1 }, { -1, -1, 1, 1,0 }, { -1, -1, -1, 0,0 },
+ { -1, 1, -1, 0,1 }, { 1, 1, -1, 1,1 }, { 1, 1, 1, 1,0 }, { -1, 1, 1, 0,0 },
+ { -1, -1, 1, 0,1 }, { 1, -1, 1, 1,1 }, { 1, -1, -1, 1,0 }, { -1, -1, -1, 0,0 },
+};
+
+const int kSkyplaneVertexCount = 4;
+static const SkyboxVertex kSkyplaneVB[kSkyplaneVertexCount] = {
+ { 1, 1, -1, 0,1 }, { -1, 1, -1, 1,1 }, { -1, -1, -1, 1,0 }, { 1, -1, -1, 0,0 },
+};
+
+Skybox::Skybox (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+Skybox::~Skybox ()
+{
+}
+
+void Skybox::AddToManager ()
+{
+
+}
+
+void Skybox::RemoveFromManager ()
+{
+
+}
+
+void Skybox::RenderSkybox (Material* mat, const Camera& camera)
+{
+ if( !mat )
+ return;
+
+ PROFILER_AUTO_GFX(gRenderSkyboxProfile, &camera)
+
+ Shader* shader = mat->GetShader();
+ Assert (shader);
+
+ GfxDevice& device = GetGfxDevice();
+
+ DeviceMVPMatricesState preserveMVP;
+
+ #if UNITY_WP8
+ void RotateScreenIfNeeded(Matrix4x4f& mat);
+ #endif
+
+ if (camera.GetOrthographic())
+ {
+ const float epsilon = 1e-6f;
+ const float nearPlane = camera.GetNear () * 0.01f;
+ const float dist = camera.GetFar() * 10.0f;
+
+ // Make Ortho matrix which passes W to Z
+ Matrix4x4f projection = Matrix4x4f::identity;
+ //camera.GetImplicitProjectionMatrix( nearPlane, projection );
+ projection.Get(2, 2) = -1.0f + epsilon;
+ projection.Get(2, 3) = (-2.0f + epsilon) * nearPlane;
+ projection.Get(3, 2) = -1.0f;
+
+ #if UNITY_WP8
+ if (!RenderTexture::GetActive()) {
+ RotateScreenIfNeeded(projection);
+ }
+ #endif
+
+ Matrix4x4f matrix = Matrix4x4f::identity;
+ //device.SetViewMatrix(matrix.GetPtr());
+ matrix.SetScale(Vector3f(dist,dist,dist));
+ matrix.SetPosition( camera.GetPosition() );
+ device.SetWorldMatrix(matrix.GetPtr());
+ device.SetProjectionMatrix(projection);
+ }
+ else
+ {
+ // Modify Projection matrix to make Infinite Projection Matrix (which passes W into Z)
+ // perspective divide will lend ZBuffer value to always be 1.0 (all points on far plane)
+ // NOTE: However we need to compensate on floating point precision errors
+ // by bringing Z slightly closer to viewer by adding Epsilon
+ // See "Projection Matrix Tricks" by Eric Lengyel GDC2007
+ // http://www.terathon.com/gdc07_lengyel.ppt
+
+ // In order to avoid clipping of skybox polys we increase skybox size and pull NearPlane as close as possible
+ // Higher epsilon values that Z/W < 1.0, but drastically reduces zBuffer precision close to FarPlane
+ // Epsilon 1e-6 gives good results as long as NearPlane >= 0.05
+ const float epsilon = 1e-6f;
+ const float nearPlane = camera.GetNear () * 0.01f;
+ const float dist = camera.GetFar() * 10.0f;
+
+ Matrix4x4f projection;
+ camera.GetImplicitProjectionMatrix( nearPlane, projection );
+ projection.Get(2, 2) = -1.0f + epsilon;
+ projection.Get(2, 3) = (-2.0f + epsilon) * nearPlane;
+ projection.Get(3, 2) = -1.0f;
+
+ #if UNITY_WP8
+ if (!RenderTexture::GetActive()) {
+ RotateScreenIfNeeded(projection);
+ }
+ #endif
+
+ Matrix4x4f matrix = Matrix4x4f::identity;
+ matrix.SetScale( Vector3f(dist,dist,dist) );
+ matrix.SetPosition( camera.GetPosition() );
+ device.SetWorldMatrix( matrix.GetPtr() );
+ device.SetProjectionMatrix(projection);
+ }
+
+ ShaderLab::IntShader* slshader = shader->GetShaderLabShader();
+ const int passCount = slshader->GetActiveSubShader().GetValidPassCount();
+ if( passCount == 6 )
+ {
+ // regular skybox with 6 separate textures per face
+ DynamicVBO& vbo = device.GetDynamicVBO();
+ for( int j = 0; j < 6; j++ )
+ {
+ SkyboxVertex* vbPtr = 0;
+ if(vbo.GetChunk(kSkyboxVertexChannels, 4, 0, DynamicVBO::kDrawQuads, (void**)&vbPtr, NULL))
+ {
+ vbPtr[0] = kSkyboxVB[4*j+0];
+ vbPtr[1] = kSkyboxVB[4*j+1];
+ vbPtr[2] = kSkyboxVB[4*j+2];
+ vbPtr[3] = kSkyboxVB[4*j+3];
+
+ vbo.ReleaseChunk(4, 0);
+
+ const ChannelAssigns* channels = mat->SetPassWithShader( j, shader, 0 );
+ vbo.DrawChunk (*channels);
+ GPU_TIMESTAMP();
+ }
+ }
+ }
+ else
+ {
+ // cube mapped skybox
+ for (int pass = 0; pass < passCount; pass++)
+ {
+ mat->SetPassWithShader( pass, shader, 0 );
+ device.ImmediateBegin( kPrimitiveQuads );
+ const SkyboxVertex* verts = kSkyboxVB;
+ for ( int i = 0; i < kSkyboxVertexCount; i++ ) {
+ device.ImmediateTexCoordAll( verts->x, verts->y, verts->z );
+ device.ImmediateVertex( verts->x, verts->y, verts->z );
+ ++verts;
+ }
+ device.ImmediateEnd();
+ GPU_TIMESTAMP();
+ }
+ }
+}
+
+void Skybox::SetMaterial (Material* material) {
+ m_CustomSkybox = material;
+}
+
+Material* Skybox::GetMaterial ()const {
+ return m_CustomSkybox;
+}
+
+template<class TransferFunction>
+void Skybox::Transfer (TransferFunction& transfer) {
+ Super::Transfer (transfer);
+ TRANSFER_SIMPLE (m_CustomSkybox);
+}
+
+IMPLEMENT_CLASS (Skybox)
+IMPLEMENT_OBJECT_SERIALIZE (Skybox)
diff --git a/Runtime/Camera/Skybox.h b/Runtime/Camera/Skybox.h
new file mode 100644
index 0000000..3d59a27
--- /dev/null
+++ b/Runtime/Camera/Skybox.h
@@ -0,0 +1,27 @@
+#ifndef SKYBOX_H
+#define SKYBOX_H
+
+#include "Runtime/GameCode/Behaviour.h"
+
+namespace Unity { class Material; }
+class Camera;
+
+class Skybox : public Behaviour {
+public:
+ REGISTER_DERIVED_CLASS (Skybox, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (Skybox)
+
+ Skybox (MemLabelId label, ObjectCreationMode mode);
+ static void RenderSkybox (Material* material, const Camera& camera);
+
+ void SetMaterial (Material* material);
+ Material* GetMaterial ()const;
+
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+
+private:
+ PPtr<Material> m_CustomSkybox;
+};
+
+#endif
diff --git a/Runtime/Camera/UmbraBackwardsCompatibility.cpp b/Runtime/Camera/UmbraBackwardsCompatibility.cpp
new file mode 100644
index 0000000..f792ffb
--- /dev/null
+++ b/Runtime/Camera/UmbraBackwardsCompatibility.cpp
@@ -0,0 +1,37 @@
+#include "UnityPrefix.h"
+#include "UmbraBackwardsCompatibilityDefine.h"
+
+#if SUPPORT_BACKWARDS_COMPATIBLE_UMBRA
+
+#define UMBRA_DISABLE_SPU_QUERIES 1
+#define UMBRA_COMP_NO_EXCEPTIONS 1
+#define UMBRA_SUPPORT_LEGACY_DATA 1
+#define NO_SSE2_NAMESPACE
+
+#include "External/Umbra_3_0/source/source/runtime/umbraBSPTree_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/umbraBitOps_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/umbraConnectivity_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/umbraInstrumentation_GPA_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/umbraIntersect_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/umbraLegacyTome_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/umbraPVSCull_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/umbraPortalCull_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/umbraPortalCull2_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/umbraPortalRaster_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/umbraPortalRayTracer_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/umbraQueryApi_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/umbraQueryArgs_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/umbraQueryContext_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/umbraSIMD_SSE_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/umbraNoSSE2_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/umbraTome_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/umbraTomeApi_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/umbraTransformer_3_0.cpp"
+#include "External/Umbra_3_0/source/source/shared/umbraAABB_3_0.cpp"
+#include "External/Umbra_3_0/source/source/shared/umbraMatrix_3_0.cpp"
+#include "External/Umbra_3_0/source/source/shared/umbraPrivateDefs_3_0.cpp"
+#include "External/Umbra_3_0/source/source/shared/umbraPrivateVersion_3_0.cpp"
+#include "External/Umbra_3_0/source/source/shared/umbraRandom_3_0.cpp"
+#include "External/Umbra_3_0/source/source/runtime/xbox360/umbraSIMD_XBOX360_3_0.cpp"
+
+#endif \ No newline at end of file
diff --git a/Runtime/Camera/UmbraBackwardsCompatibility.h b/Runtime/Camera/UmbraBackwardsCompatibility.h
new file mode 100644
index 0000000..d3eb40b
--- /dev/null
+++ b/Runtime/Camera/UmbraBackwardsCompatibility.h
@@ -0,0 +1,85 @@
+#ifndef UMBRA_BACKWARDS_COMPATIBILITY_H
+#define UMBRA_BACKWARDS_COMPATIBILITY_H
+
+#include "UmbraBackwardsCompatibilityDefine.h"
+#include "UmbraTomeData.h"
+
+#include "External/Umbra/builds/interface/runtime/umbraTome.hpp"
+#include "External/Umbra/builds/interface/runtime/umbraQuery.hpp"
+
+#if SUPPORT_BACKWARDS_COMPATIBLE_UMBRA
+# include "External/Umbra_3_0/source/interface/runtime/umbraTome_3_0.hpp"
+# include "External/Umbra_3_0/source/interface/runtime/umbraQuery_3_0.hpp"
+#endif
+
+inline void CleanupUmbraTomeData (UmbraTomeData& tomeData)
+{
+ if (tomeData.tome)
+ {
+ Umbra::TomeLoader::freeTome(tomeData.tome);
+ tomeData.tome = NULL;
+ }
+
+#if SUPPORT_BACKWARDS_COMPATIBLE_UMBRA
+ if (tomeData.legacyTome)
+ {
+ Umbra_3_0::TomeLoader::freeTome(tomeData.legacyTome);
+ tomeData.legacyTome = NULL;
+ }
+#endif
+}
+inline const UmbraTomeData LoadUmbraTome (const UInt8* buf, size_t bytes)
+{
+ if (buf == NULL || bytes == 0)
+ return UmbraTomeData ();
+
+ const Umbra::Tome* tome = Umbra::TomeLoader::loadFromBuffer(buf, bytes);
+ if (tome->getStatus() == Umbra::Tome::STATUS_OK)
+ {
+ UmbraTomeData tomeData;
+ tomeData.tome = tome;
+ return tomeData;
+ }
+
+
+#if SUPPORT_BACKWARDS_COMPATIBLE_UMBRA
+ if (tome && tome->getStatus() == Umbra::Tome::STATUS_OLDER_VERSION)
+ {
+ const Umbra_3_0::Tome* legacyTome = Umbra_3_0::TomeLoader::loadFromBuffer(buf, bytes);
+ if (legacyTome && legacyTome->getStatus() == Umbra_3_0::Tome::STATUS_OK)
+ {
+ UmbraTomeData tomeData;
+ tomeData.legacyTome = legacyTome;
+ return tomeData;
+ }
+
+ ErrorString("Failed to load legacy tome data");
+ return UmbraTomeData();
+ }
+#endif
+
+ WarningString ("Loading deprecated Occlusion Culling is not supported. Please rebake the occlusion culling data.");
+ return UmbraTomeData ();
+}
+
+inline void SetGateStates( Umbra::Query* query, const UmbraTomeData& tomeData, Umbra::GateStateVector& gateVector)
+{
+#if SUPPORT_BACKWARDS_COMPATIBLE_UMBRA
+ if (tomeData.IsLegacyTome ())
+ {
+ ((Umbra_3_0::QueryExt*)query)->setGateStates((const Umbra_3_0::GateStateVector*)&gateVector);
+ return;
+ }
+#endif
+
+ query->setGateStates(&gateVector);
+}
+
+#if SUPPORT_BACKWARDS_COMPATIBLE_UMBRA
+ #define UMBRA_TOME_METHOD(TOME,method) (TOME.tome != NULL ? TOME.tome->method : TOME.legacyTome->method)
+#else
+ #define UMBRA_TOME_METHOD(TOME,method) (TOME.tome->method)
+#endif
+
+
+#endif \ No newline at end of file
diff --git a/Runtime/Camera/UmbraBackwardsCompatibilityDefine.h b/Runtime/Camera/UmbraBackwardsCompatibilityDefine.h
new file mode 100644
index 0000000..756f05c
--- /dev/null
+++ b/Runtime/Camera/UmbraBackwardsCompatibilityDefine.h
@@ -0,0 +1,4 @@
+#pragma once
+
+#define SUPPORT_BACKWARDS_COMPATIBLE_UMBRA WEBPLUG
+//#define SUPPORT_BACKWARDS_COMPATIBLE_UMBRA 1 \ No newline at end of file
diff --git a/Runtime/Camera/UmbraTomeData.h b/Runtime/Camera/UmbraTomeData.h
new file mode 100644
index 0000000..4e36363
--- /dev/null
+++ b/Runtime/Camera/UmbraTomeData.h
@@ -0,0 +1,22 @@
+#pragma once
+
+namespace Umbra { class Tome; };
+namespace Umbra_3_0 { class Tome; };
+
+struct UmbraTomeData
+{
+ const Umbra::Tome* tome;
+ const Umbra_3_0::Tome* legacyTome;
+
+ UmbraTomeData () { tome = NULL; legacyTome = NULL; }
+
+
+ bool HasTome () const { return tome != NULL || legacyTome != NULL; }
+ bool IsLegacyTome () const { return legacyTome != NULL; }
+
+
+ friend inline bool operator == (const UmbraTomeData& lhs, const UmbraTomeData& rhs)
+ {
+ return lhs.tome == rhs.tome && lhs.legacyTome == rhs.legacyTome;
+ }
+}; \ No newline at end of file
diff --git a/Runtime/Camera/UnityScene.cpp b/Runtime/Camera/UnityScene.cpp
new file mode 100644
index 0000000..84832d0
--- /dev/null
+++ b/Runtime/Camera/UnityScene.cpp
@@ -0,0 +1,505 @@
+#include "UnityPrefix.h"
+#include "UnityScene.h"
+#include "IntermediateRenderer.h"
+#include "SceneSettings.h"
+#include "CullingParameters.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Camera/OcclusionPortal.h"
+#include "UmbraBackwardsCompatibility.h"
+
+///@TODO: Test for removing static renderers in pvs.
+// @TODO: Test to check that enable / disable renderer keeps layer & bounding volume correctly.
+//@TODO: Test for destroying all renderers during OnBecameInvisible callback
+
+//* Should we use setDynamicObjects?
+//@TODO: When a gate is baked. Make sure that the majority of the area is not tagged as occluded. (Eg. someone tagged static data around it)
+//* Culling test when static objects have been deleted after baking...
+
+namespace Unity
+{
+
+static Scene* gScene = NULL;
+
+Scene::Scene ()
+{
+ m_UmbraQuery = NULL;
+ m_PreventAddRemoveRenderer = 0;
+ m_RequestStaticPVSRebuild = false;
+ m_GateState = NULL;
+}
+
+Scene::~Scene ()
+{
+ Assert(m_RendererNodes.empty());
+
+ ClearIntermediateRenderers();
+ CleanupUmbra();
+}
+
+void Scene::ClearIntermediateRenderers()
+{
+ m_IntermediateNodes.Clear();
+}
+
+void Scene::CleanupUmbra ()
+{
+ // The scene class does not own the tome (SceneSettings does)
+ m_UmbraTome = UmbraTomeData();
+
+ if (m_UmbraQuery)
+ delete m_UmbraQuery;
+ m_UmbraQuery = NULL;
+
+ delete[] m_GateState;
+ m_GateState = NULL;
+
+ // Cleanup references to PVS handle
+ dynamic_array<SceneNode>::iterator it, itEnd = m_RendererNodes.end();
+ for (it = m_RendererNodes.begin(); it != itEnd; ++it)
+ {
+ SceneNode& node = *it;
+ node.pvsHandle = -1;
+ }
+
+ ///////@TODO: Cleanup all OcclusionPortals gateIndices
+
+ // Cleanup any renderer nodes which have already been deleted, but were kept around to keep the indices of pvs renderers
+ // in sync with umbra
+ for (int i=0;i<m_RendererNodes.size();i++)
+ {
+ if (m_RendererNodes[i].renderer == NULL)
+ {
+ RemoveRenderer(i);
+ i--;
+ }
+ }
+}
+
+void Scene::CleanupPVSAndRequestRebuild ()
+{
+ CleanupUmbra();
+ m_RequestStaticPVSRebuild = true;
+}
+
+void Scene::InitializeUmbra()
+{
+ RecalculateDirtyBounds();
+ CleanupUmbra();
+
+ m_UmbraTome = GetSceneSettings().GetUmbraTome();
+
+ if (!m_UmbraTome.HasTome())
+ return;
+
+ Assert(m_PendingRemoval.empty());
+
+ const dynamic_array<PPtr<Renderer> >& pvsObjectArray = GetSceneSettings().GetPVSObjectArray();
+ const dynamic_array<PPtr<OcclusionPortal> >& portalsArray = GetSceneSettings().GetPortalsArray();
+
+ int objectCount = UMBRA_TOME_METHOD(m_UmbraTome, getObjectCount());
+ // Setup pvs handles in renderer nodes
+ for (int i = 0; i < objectCount; i++)
+ {
+ Umbra::UINT32 userId = UMBRA_TOME_METHOD(m_UmbraTome, getObjectUserID(i));
+ Assert(userId < pvsObjectArray.size());
+ if (userId >= pvsObjectArray.size())
+ continue;
+
+ // Don't do any loading from disk to avoid weird corner cases and recursion - You never know
+ Object* obj = Object::IDToPointer(pvsObjectArray[userId].GetInstanceID());
+ Renderer* renderer = dynamic_pptr_cast<Renderer*> (obj);
+ SceneHandle handle = -1;
+ if (renderer)
+ handle = renderer->GetSceneHandle();
+
+ // Objects might have been deleted after baking. In this case we need to create holes in the PVS object array
+ // For each pvs object that can't be found we add a NULL renderer
+ if (handle < 0 || handle >= m_RendererNodes.size())
+ handle = AddRendererInternal (NULL, 0, AABB());
+
+ m_RendererNodes[handle].pvsHandle = i;
+ }
+
+ Assert(objectCount <= m_RendererNodes.size());
+
+ // Make sure that the pvs handles match the index in the renderer nodes array
+ // This way we can do very fast lookups.
+ for (int i=0;i<m_RendererNodes.size();i++)
+ {
+ while (m_RendererNodes[i].pvsHandle != -1 && m_RendererNodes[i].pvsHandle != i)
+ {
+ SInt32 pvsIndex = m_RendererNodes[i].pvsHandle;
+
+ std::swap(m_RendererNodes[i], m_RendererNodes[pvsIndex]);
+ std::swap(m_BoundingBoxes[i], m_BoundingBoxes[pvsIndex]);
+ std::swap(m_VisibilityBits[i], m_VisibilityBits[pvsIndex]);
+
+ Renderer* rendererI = static_cast<Renderer*> (m_RendererNodes[i].renderer);
+ if (rendererI)
+ rendererI->NotifySceneHandleChange(i);
+
+ Renderer* rendererPVS = static_cast<Renderer*> (m_RendererNodes[pvsIndex].renderer);
+ if (rendererPVS)
+ rendererPVS->NotifySceneHandleChange(pvsIndex);
+ }
+ }
+
+ for (int i=0;i<m_RendererNodes.size();i++)
+ {
+ Assert(m_RendererNodes[i].pvsHandle == -1 || m_RendererNodes[i].pvsHandle == i);
+ Assert(m_RendererNodes[i].renderer == NULL || static_cast<Renderer*> (m_RendererNodes[i].renderer)->GetSceneHandle() == i);
+ }
+ // Create query
+ m_UmbraQuery = new Umbra::QueryExt(m_UmbraTome.tome);
+
+ // Setup portals by querying the renderer and grabbing the OcclusionPortal on the same game object
+ int gateCount = UMBRA_TOME_METHOD(m_UmbraTome, getGateCount());
+ if (gateCount != 0 && !portalsArray.empty())
+ {
+ m_GateState = new UInt8[UMBRA_TOME_METHOD(m_UmbraTome, getGateStateSize())];
+ memset(m_GateState, 0, UMBRA_TOME_METHOD(m_UmbraTome, getGateStateSize()));
+
+ Umbra::GateStateVector gateVector (m_GateState, 0, false);
+
+ SetGateStates (m_UmbraQuery, m_UmbraTome, gateVector);
+
+ for (int i = 0; i < gateCount; i++)
+ {
+ // Umbra userID's need to be unique. They are allocated to be after the static renderers/
+ Umbra::UINT32 userId = UMBRA_TOME_METHOD(m_UmbraTome, getGateUserID(i)) - pvsObjectArray.size();
+ Assert(userId < portalsArray.size());
+ if (userId >= portalsArray.size())
+ continue;
+
+ // Don't do any loading from disk to avoid weird corner cases and recursion - You never know
+ Object* obj = Object::IDToPointer(portalsArray[i].GetInstanceID());
+ OcclusionPortal* portal = dynamic_pptr_cast<OcclusionPortal*> (obj);
+ if (portal)
+ {
+ portal->SetPortalIndex(i);
+ gateVector.setState(i, portal->CalculatePortalEnabled());
+ }
+ }
+ }
+}
+
+
+size_t Scene::GetStaticObjectCount () const
+{
+ if (!m_UmbraTome.HasTome())
+ return 0;
+
+ return UMBRA_TOME_METHOD(m_UmbraTome, getObjectCount());
+}
+
+size_t Scene::GetDynamicObjectCount () const
+{
+ return GetRendererNodeCount() - GetStaticObjectCount();
+}
+
+size_t Scene::GetIntermediateObjectCount () const
+{
+ return m_IntermediateNodes.GetRendererCount();
+}
+
+const SceneNode* Scene::GetStaticSceneNodes () const
+{
+ return m_RendererNodes.begin();
+}
+
+const SceneNode* Scene::GetDynamicSceneNodes () const
+{
+ return m_RendererNodes.begin() + GetStaticObjectCount();
+}
+
+const AABB* Scene::GetStaticBoundingBoxes () const
+{
+ return m_BoundingBoxes.begin();
+}
+
+const AABB* Scene::GetDynamicBoundingBoxes () const
+{
+ return m_BoundingBoxes.begin() + GetStaticObjectCount();
+}
+
+
+#if DEBUGMODE
+bool Scene::HasNodeForRenderer( const BaseRenderer* r )
+{
+ for (dynamic_array<SceneNode>::iterator j = m_RendererNodes.begin(); j != m_RendererNodes.end(); ++j)
+ {
+ if (j->renderer == r)
+ return true;
+ }
+
+ return false;
+}
+
+#endif
+
+SceneHandle Scene::AddRendererInternal (Renderer *renderer, int layer, const AABB& aabb)
+{
+ SceneHandle handle = m_RendererNodes.size();
+ Assert(m_BoundingBoxes.size() == handle);
+ Assert(m_VisibilityBits.size() == handle);
+
+ SceneNode node;
+ node.renderer = renderer;
+ node.layer = layer;
+ m_RendererNodes.push_back(node);
+ m_BoundingBoxes.push_back(aabb);
+ m_VisibilityBits.push_back(0);
+ return handle;
+}
+
+
+SceneHandle Scene::AddRenderer (Renderer *renderer)
+{
+ Assert (renderer);
+#if DEBUGMODE
+ DebugAssertIf (HasNodeForRenderer(renderer));
+#endif
+ if (m_PreventAddRemoveRenderer != 0)
+ {
+ AssertString("Adding renderer during rendering is not allowed.");
+ return kInvalidSceneHandle;
+ }
+
+ AABB aabb;
+ renderer->GetWorldAABB(aabb);
+ Assert(aabb.IsValid());
+
+ return AddRendererInternal(renderer, renderer->GetLayer(), aabb);
+}
+
+BaseRenderer* Scene::RemoveRenderer (SceneHandle handle)
+{
+ if (handle < 0 || handle >= m_RendererNodes.size())
+ {
+ ErrorString("Invalid SceneHandle");
+ return NULL;
+ }
+ SceneNode& node = m_RendererNodes[handle];
+ BaseRenderer* renderer = node.renderer;
+
+ if (m_PreventAddRemoveRenderer != 0)
+ {
+ // The current node can be removed during NotifyVisible()
+ // This is due to the fact that Animations can be updated during culling and
+ // our animation system allows users to set m_Enabled = false which then
+ // results in our node being removed (see fogbugz case 378739).
+
+ // We can't actually remove this or reorder nodes until after the render.
+ // There are pointers to nodes and AABBs being used during rendering!
+ m_PendingRemoval.push_back(handle);
+ node.disable = true;
+ return renderer;
+ }
+
+ // Static objects can not be removed from the array
+ if (handle < GetStaticObjectCount())
+ {
+ m_VisibilityBits[handle] = 0;
+ node.renderer = NULL;
+ node.dirtyAABB = false;
+ return renderer;
+ }
+
+ // Swap with last element (if we are not the last element)
+ int lastIndex = m_RendererNodes.size() - 1;
+ const SceneNode& lastNode = m_RendererNodes[lastIndex];
+ if (handle != lastIndex && lastNode.renderer != NULL)
+ {
+ const AABB& lastAABB = m_BoundingBoxes[lastIndex];
+ bool lastVisibilityBits = m_VisibilityBits[lastIndex];
+ m_RendererNodes[handle] = lastNode;
+ m_BoundingBoxes[handle] = lastAABB;
+ m_VisibilityBits[handle] = lastVisibilityBits;
+ // We don't remove old handle from dirty list, just check for invalid ones
+ if (lastNode.dirtyAABB)
+ m_DirtyAABBList.push_back(handle);
+
+ Renderer* swapRenderer = static_cast<Renderer*>(lastNode.renderer);
+ swapRenderer->NotifySceneHandleChange(handle);
+ }
+ m_RendererNodes.pop_back();
+ m_BoundingBoxes.pop_back();
+ m_VisibilityBits.pop_back();
+ return renderer;
+}
+
+#if UNITY_EDITOR
+
+unsigned Scene::GetUmbraDataSize ()
+{
+ if (!m_UmbraTome.HasTome())
+ return 0;
+
+ return UMBRA_TOME_METHOD(m_UmbraTome, getSize());
+}
+
+bool Scene::IsPositionInPVSVolume (const Vector3f& position)
+{
+ if (m_UmbraQuery == NULL)
+ return false;
+
+ ////@TODO: This is no longer working!
+
+ return true;
+
+// Umbra::Query::ErrorCode e = m_UmbraQuery->queryPointVisibility(m_QueryMode, NULL, NULL, (Umbra::Vector3&)position);
+// return e == Umbra::Query::ERROR_OK;
+}
+
+#endif
+
+void Scene::SetOcclusionPortalEnabled (unsigned int portalIndex, bool enabled)
+{
+ if (m_UmbraQuery == NULL)
+ return;
+
+ if (portalIndex >= UMBRA_TOME_METHOD(m_UmbraTome, getGateStateSize()))
+ {
+ ErrorString("Invalid portal index");
+ return;
+ }
+
+ Umbra::GateStateVector gateVector (m_GateState, 0, false);
+ gateVector.setState(portalIndex, enabled);
+}
+
+
+void Scene::RecalculateDirtyBounds()
+{
+ int dirtyCount = m_DirtyAABBList.size();
+ for (int i = 0; i < dirtyCount; ++i)
+ {
+ SceneHandle handle = m_DirtyAABBList[i];
+ // List may have invalid entries so check range/dirty flag
+ if (handle < m_RendererNodes.size())
+ {
+ SceneNode& node = m_RendererNodes[handle];
+ if (node.dirtyAABB)
+ {
+ node.renderer->GetWorldAABB(m_BoundingBoxes[handle]);
+ node.dirtyAABB = false;
+ }
+ }
+ }
+ m_DirtyAABBList.resize_uninitialized(0);
+}
+
+void Scene::SetPreventAddRemoveRenderer(bool enable)
+{
+ // Culling can be nested, so we need a count to disable changes to scene
+ m_PreventAddRemoveRenderer += enable ? 1 : -1;
+}
+
+void Scene::NotifyVisible (const CullingOutput& visibleObjects)
+{
+ // Update visibility bits for static objects
+ for (int i = 0; i < visibleObjects.visible[kStaticRenderers].size; ++i)
+ {
+ int index = visibleObjects.visible[kStaticRenderers].indices[i];
+ m_VisibilityBits[index] |= kVisibleCurrentFrame;
+ }
+
+ // Update visibility bits for dynamic objects
+ size_t offset = GetStaticObjectCount();
+ for (int i = 0; i < visibleObjects.visible[kDynamicRenderer].size; ++i)
+ {
+ int index = visibleObjects.visible[kDynamicRenderer].indices[i] + offset;
+ m_VisibilityBits[index] |= kVisibleCurrentFrame;
+ }
+
+ // We prevent changes to scene here and in OnWillRenderObject(), case 445226.
+ // Since array indices and pointers must stay valid, adding nodes is not allowed.
+ // We disable nodes instead of removing them, then remove them later.
+ SetPreventAddRemoveRenderer(true);
+ int nodeCount = m_RendererNodes.size();
+ for (int i = 0; i < nodeCount; ++i)
+ {
+ SceneNode& node = m_RendererNodes[i];
+ UInt8& vbits = m_VisibilityBits[i];
+ if (vbits == kVisibleCurrentFrame)
+ {
+ node.renderer->RendererBecameVisible();
+ vbits |= kBecameVisibleCalled;
+ }
+ }
+ SetPreventAddRemoveRenderer(false);
+}
+
+void Scene::NotifyInvisible ()
+{
+ // Happens after rendering, so modifying scene is not a problem.
+ int nodeCount = m_RendererNodes.size();
+ for (int i = 0; i < nodeCount; ++i)
+ {
+ SceneNode& node = m_RendererNodes[i];
+ UInt8& vbits = m_VisibilityBits[i];
+ if (vbits == kVisiblePreviousFrame)
+ {
+ node.renderer->RendererBecameInvisible();
+ }
+ // Roll visibility over to next frame
+ vbits = (vbits & kVisibleCurrentFrame) ? kVisiblePreviousFrame : 0;
+ }
+}
+
+void Scene::BeginCameraRender ()
+{
+ // Prepare Static SceneNode array
+ if (m_RequestStaticPVSRebuild)
+ {
+ m_RequestStaticPVSRebuild = false;
+ InitializeUmbra();
+ }
+}
+
+void Scene::EndCameraRender ()
+{
+ // Removal is done after rendering since we keep pointers around to nodes and AABBs.
+ // We do it in reverse order to avoid moving the last entries before deleting them.
+ // Do not change this without careful thinking as it is easy to break...
+ if (!m_PendingRemoval.empty())
+ {
+ std::sort(m_PendingRemoval.begin(), m_PendingRemoval.end());
+#if DEBUGMODE
+ int validateUnique = -1;
+#endif
+ for (int i=m_PendingRemoval.size()-1; i >= 0;i--)
+ {
+#if DEBUGMODE
+ Assert(validateUnique != m_PendingRemoval[i]);
+ validateUnique = m_PendingRemoval[i];
+#endif
+
+ RemoveRenderer(m_PendingRemoval[i]);
+ }
+ m_PendingRemoval.clear();
+ }
+}
+
+
+
+Scene& GetScene ()
+{
+ return *gScene;
+}
+
+void Scene::InitializeClass ()
+{
+ Assert(gScene == NULL);
+ gScene = new Scene ();
+}
+
+void Scene::CleanupClass ()
+{
+ Assert(gScene != NULL);
+ delete gScene;
+ gScene = NULL;
+}
+
+
+} // namespace Unity
diff --git a/Runtime/Camera/UnityScene.h b/Runtime/Camera/UnityScene.h
new file mode 100644
index 0000000..51bb4ba
--- /dev/null
+++ b/Runtime/Camera/UnityScene.h
@@ -0,0 +1,154 @@
+#ifndef UNITYSCENE_H
+#define UNITYSCENE_H
+
+#include "Runtime/Geometry/AABB.h"
+#include "IntermediateRenderer.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "SceneNode.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "UmbraTomeData.h"
+
+struct SceneCullingParameters;
+struct CullingParameters;
+struct CullingOutput;
+class BaseRenderer;
+class OcclusionPortal;
+class Renderer;
+class IntermediateRenderer;
+class LODGroupManager;
+class MinMaxAABB;
+class Texture2D;
+
+namespace Unity { class Component; class Culler; }
+namespace Umbra { class QueryExt; class Tome; class CameraTransform; class Visibility; }
+
+typedef int UmbraInt32;
+
+// Function called when a node is determined to be visible.
+//typedef bool CullFunction( void* user, const SceneNode& node, const AABB& aabb, float lodFade );
+
+namespace Unity
+{
+
+// Simple dummy for the scene class, used by the cullers.
+class Scene
+{
+public:
+
+ Scene ();
+ ~Scene ();
+
+ void NotifyVisible(const CullingOutput& visibleObjects);
+ void NotifyInvisible();
+
+ void BeginCameraRender();
+ void EndCameraRender();
+
+ // Adds/removes from the scene
+ SceneHandle AddRenderer (Renderer *renderer);
+ BaseRenderer* RemoveRenderer (SceneHandle handle);
+
+ // Functions to set information about renderers
+ void SetDirtyAABB (SceneHandle handle) { SceneNode& node = m_RendererNodes[handle]; if (!node.dirtyAABB) { m_DirtyAABBList.push_back(handle); node.dirtyAABB = true; } }
+ void SetRendererAABB (SceneHandle handle, const AABB& aabb) { m_BoundingBoxes[handle] = aabb; m_RendererNodes[handle].dirtyAABB = false; }
+ void SetRendererLayer (SceneHandle handle, UInt32 layer) { m_RendererNodes[handle].layer = layer; }
+ void SetRendererLODGroup (SceneHandle handle, int group) { m_RendererNodes[handle].lodGroup = group; }
+ void SetRendererLODIndexMask (SceneHandle handle, UInt32 mask) { m_RendererNodes[handle].lodIndexMask = mask; }
+ void SetRendererNeedsCullCallback (SceneHandle handle, bool flag) { m_RendererNodes[handle].needsCullCallback = flag; }
+
+ // Const access only to SceneNode and AABB!
+ // Scene needs to be notified about changes and some data is private
+ const SceneNode& GetRendererNode (SceneHandle handle) { return m_RendererNodes[handle]; }
+ const AABB& GetRendererAABB (SceneHandle handle) { return m_BoundingBoxes[handle]; }
+
+ const SceneNode* GetStaticSceneNodes () const;
+ const SceneNode* GetDynamicSceneNodes () const;
+
+ const AABB* GetStaticBoundingBoxes () const;
+ const AABB* GetDynamicBoundingBoxes () const;
+
+ size_t GetRendererNodeCount () const { return m_RendererNodes.size(); }
+ size_t GetStaticObjectCount () const;
+ size_t GetDynamicObjectCount () const;
+ size_t GetIntermediateObjectCount () const;
+
+ void RecalculateDirtyBounds();
+
+ // Intermediate nodes
+ void ClearIntermediateRenderers();
+ IntermediateRenderers& GetIntermediateRenderers () { return m_IntermediateNodes; }
+
+ void SetOcclusionPortalEnabled (unsigned int portalIndex, bool enabled);
+
+
+ //@TODO: REview this function
+ void SetPreventAddRemoveRenderer(bool enable);
+
+
+ #if DEBUGMODE
+ bool HasNodeForRenderer( const BaseRenderer* r );
+ #endif
+
+ #if UNITY_EDITOR
+ void GetUmbraDebugLines (const CullingParameters& cullingParameters, dynamic_array<Vector3f>& lines, bool targetCells);
+
+ unsigned GetUmbraDataSize ();
+
+ bool IsPositionInPVSVolume (const Vector3f& pos);
+
+ #endif
+
+ Umbra::QueryExt* GetUmbraQuery () { return m_UmbraQuery; }
+ int GetNumRenderers() { return m_RendererNodes.size(); }
+
+ const UmbraTomeData& GetUmbraTome () { return m_UmbraTome; }
+
+ void CleanupPVSAndRequestRebuild ();
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+private:
+ void InitializeUmbra();
+ void CleanupUmbra ();
+ void CleanupUmbraNodesAndQuery ();
+
+ SceneHandle AddRendererInternal (Renderer *renderer, int layer, const AABB& aabb);
+
+ bool IsDirtyAABB (SceneHandle handle) const { return m_RendererNodes[handle].dirtyAABB; }
+
+ IntermediateRenderers m_IntermediateNodes;
+ dynamic_array<SceneHandle> m_PendingRemoval;
+
+ enum
+ {
+ kVisibleCurrentFrame = 1 << 0,
+ kVisiblePreviousFrame = 1 << 1,
+ kBecameVisibleCalled = 1 << 2
+ };
+
+ // These arrays are always kept in sync
+ dynamic_array<SceneNode> m_RendererNodes;
+ dynamic_array<AABB> m_BoundingBoxes;
+ dynamic_array<UInt8> m_VisibilityBits;
+
+ // Other data
+ dynamic_array<SceneHandle> m_DirtyAABBList;
+
+ Umbra::QueryExt* m_UmbraQuery;
+ UInt8* m_GateState;
+
+
+ UmbraTomeData m_UmbraTome;
+
+ int m_PreventAddRemoveRenderer;
+ bool m_RequestStaticPVSRebuild;
+};
+
+Scene& GetScene ();
+
+
+} // namespace Unity
+
+
+#endif
diff --git a/Runtime/ClusterRenderer/ClusterNetwork.cpp b/Runtime/ClusterRenderer/ClusterNetwork.cpp
new file mode 100644
index 0000000..e68fe38
--- /dev/null
+++ b/Runtime/ClusterRenderer/ClusterNetwork.cpp
@@ -0,0 +1,118 @@
+#include "UnityPrefix.h"
+#if ENABLE_CLUSTER_SYNC
+
+#include "ClusterNetwork.h"
+#include "External/zmq/include/zmq.h"
+
+void* CreateZMQContext()
+{
+ return zmq_ctx_new();
+}
+
+
+void DestroyZMQContext(void* context)
+{
+ zmq_term(context);
+}
+
+
+MasterSocket::MasterSocket(void* context, const char* mainUrl, const char* ctrlUrl)
+: m_Context(context)
+{
+ // create them
+ m_MainSocket = zmq_socket(m_Context, ZMQ_XPUB);
+ m_CtrlSocket = zmq_socket(m_Context, ZMQ_PULL);
+ // bind them
+ zmq_bind(m_MainSocket, mainUrl);
+ zmq_bind(m_CtrlSocket, ctrlUrl);
+}
+
+
+MasterSocket::~MasterSocket()
+{
+ if(m_MainSocket)
+ zmq_close(m_MainSocket);
+ if(m_CtrlSocket)
+ zmq_close(m_CtrlSocket);
+}
+
+
+void MasterSocket::Publish(dynamic_array<UInt8>& buffer)
+{
+ zmq_send(m_MainSocket, buffer.data(), buffer.size(), 0);
+}
+
+
+bool MasterSocket::WaitForSubscriber()
+{
+ UInt8 buffer[3];
+ int rc = zmq_recv(m_MainSocket, &buffer, 3, 0);
+ return (rc == 3 && buffer[0] == 1 && buffer[1] == 'S');
+}
+
+
+bool MasterSocket::CheckForUnsubscribe()
+{
+ UInt8 buffer[3];
+ int rc = zmq_recv(m_MainSocket, &buffer, 3, ZMQ_DONTWAIT);
+ return (rc == 3 && buffer[0] == 0 && buffer[1] == 'S');
+}
+
+
+bool MasterSocket::GetAck()
+{
+ UInt8 buffer[1];
+ int rc = zmq_recv(m_CtrlSocket, &buffer, 1, ZMQ_DONTWAIT);
+ return (rc == 1 && buffer[0] == 1);
+}
+
+
+SlaveSocket::SlaveSocket(void* context, const char* mainUrl, const char* ctrlUrl)
+: m_Context(context)
+{
+ // create them
+ m_MainSocket = zmq_socket(m_Context, ZMQ_XSUB);
+ m_CtrlSocket = zmq_socket(m_Context, ZMQ_PUSH);
+ // connect them
+ zmq_connect(m_MainSocket, mainUrl);
+ zmq_connect(m_CtrlSocket, ctrlUrl);
+}
+
+
+SlaveSocket::~SlaveSocket()
+{
+ if(m_MainSocket)
+ zmq_close(m_MainSocket);
+ if(m_CtrlSocket)
+ zmq_close(m_CtrlSocket);
+}
+
+
+void SlaveSocket::Listen(dynamic_array<UInt8>& buffer)
+{
+ zmq_recv(m_MainSocket, buffer.data(), buffer.size(), 0);
+}
+
+
+void SlaveSocket::Subscribe(int slaveId)
+{
+ UInt8 buffer[] = {1, 'S', slaveId};
+ zmq_send(m_MainSocket, buffer, 3, 0);
+ zmq_send(m_MainSocket, buffer, 1, 0);
+}
+
+
+void SlaveSocket::Unsubscribe(int slaveId)
+{
+ UInt8 buffer[] = {0, 'S', slaveId};
+ zmq_send(m_MainSocket, buffer, 3, 0);
+}
+
+
+void SlaveSocket::SendAck()
+{
+ UInt8 buffer[] = {1};
+ zmq_send(m_CtrlSocket, buffer, 1, 0);
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/ClusterRenderer/ClusterNetwork.h b/Runtime/ClusterRenderer/ClusterNetwork.h
new file mode 100644
index 0000000..0d84a08
--- /dev/null
+++ b/Runtime/ClusterRenderer/ClusterNetwork.h
@@ -0,0 +1,40 @@
+#pragma once
+#if ENABLE_CLUSTER_SYNC
+#include "Runtime/Utilities/dynamic_array.h"
+
+void* CreateZMQContext();
+void DestroyZMQContext(void* context);
+
+class MasterSocket
+{
+public:
+ MasterSocket(void* context, const char* mainUrl, const char* ctrlUrl);
+ ~MasterSocket();
+ void Publish(dynamic_array<UInt8>& buffer);
+ bool WaitForSubscriber();
+ bool CheckForUnsubscribe();
+ bool GetAck();
+private:
+ void* m_Context;
+ void* m_MainSocket;
+ void* m_CtrlSocket;
+};
+
+
+class SlaveSocket
+{
+public:
+ SlaveSocket(void* context, const char* mainUrl, const char* ctrlUrl);
+ ~SlaveSocket();
+ void Listen(dynamic_array<UInt8>& buffer);
+ void Subscribe(int slaveId);
+ void Unsubscribe(int slaveId);
+ void SendAck();
+private:
+ void* m_Context;
+ void* m_MainSocket;
+ void* m_CtrlSocket;
+};
+
+
+#endif \ No newline at end of file
diff --git a/Runtime/ClusterRenderer/ClusterNode.cpp b/Runtime/ClusterRenderer/ClusterNode.cpp
new file mode 100644
index 0000000..c17dd50
--- /dev/null
+++ b/Runtime/ClusterRenderer/ClusterNode.cpp
@@ -0,0 +1,128 @@
+#include "UnityPrefix.h"
+#if ENABLE_CLUSTER_SYNC
+#include "ClusterNode.h"
+#include "ClusterNetwork.h"
+#include "ClusterTransfer.h"
+
+#ifdef DEBUG
+#include "Runtime/Input/InputManager.h"
+#include "ClusterRendererModule.h"
+#endif
+
+MasterNode::~MasterNode()
+{
+ delete m_Socket;
+ delete m_Synchronizer;
+}
+
+
+void MasterNode::Sync()
+{
+#ifdef DEBUG
+ if(ClusterRendererModule::IsInClusterTestMode)
+ {
+ static int count = 0;
+ if(count++ == 400)
+ {
+ GetInputManager().QuitApplication();
+ return;
+ }
+ }
+#endif
+
+ // this may early out
+ WaitForSlavesToConnect();
+
+ // the write buffer
+ dynamic_array<UInt8> buffer(kMemTempAlloc);
+ // Transfer
+ m_Synchronizer->TransferToBuffer(buffer);
+ // in the end, send out the buffer via zmq
+ m_Socket->Publish(buffer);
+
+ // sync up, this should block
+ WaitForSlavesToAck();
+}
+
+
+void MasterNode::WaitForSlavesToConnect()
+{
+ // don't try if we have all initialy connected
+ if(m_AllSlavesConnected) return;
+ // check and wait for each slave
+ for(int i = 0; i < m_InitialSlaveCount;)
+ {
+ // This will block
+ if(m_Socket->WaitForSubscriber())
+ i++;
+ }
+ // only do it once
+ m_AllSlavesConnected = true;
+}
+
+
+void MasterNode::WaitForSlavesToAck()
+{
+ int checkedInCount = 0;
+ // while there are slaves unaccounted for
+ while((m_CurrentSlaveCount - checkedInCount) > 0)
+ {
+ // check for unsubscription
+ if(m_Socket->CheckForUnsubscribe())
+ m_CurrentSlaveCount--;
+
+ // check control signal
+ if(m_Socket->GetAck())
+ checkedInCount++;
+ }
+}
+
+
+SlaveNode::SlaveNode(SlaveSocket* socket, ClusterTransfer* sync, int param)
+: ClusterNode(false)
+, m_Socket(socket)
+, m_Synchronizer(sync)
+, m_SlaveId(param)
+{
+ // should this be here?
+ m_Socket->Subscribe(m_SlaveId);
+}
+
+
+SlaveNode::~SlaveNode()
+{
+ // send the ubsubscribe signal
+ m_Socket->Unsubscribe(m_SlaveId);
+ // destroy stuff
+ delete m_Socket;
+ delete m_Synchronizer;
+}
+
+
+void SlaveNode::Sync()
+{
+#ifdef DEBUG
+ if(ClusterRendererModule::IsInClusterTestMode)
+ {
+ static int count = 0;
+ if(count++ == 300)
+ {
+ m_Synchronizer->TransferToFile(m_SlaveId);
+ GetInputManager().QuitApplication();
+ return;
+ }
+ }
+#endif
+
+ // TODO: fix potential buffer overflow
+ // the read buffer
+ dynamic_array<UInt8> buffer(4096, kMemTempAlloc);
+ // wait for the server to send the buffer over
+ m_Socket->Listen(buffer);
+ // send control signal
+ m_Socket->SendAck();
+ // use the data
+ m_Synchronizer->TransferFromBuffer(buffer);
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/ClusterRenderer/ClusterNode.h b/Runtime/ClusterRenderer/ClusterNode.h
new file mode 100644
index 0000000..2545702
--- /dev/null
+++ b/Runtime/ClusterRenderer/ClusterNode.h
@@ -0,0 +1,64 @@
+#pragma once
+#if ENABLE_CLUSTER_SYNC
+
+class MasterSocket;
+class SlaveSocket;
+class ClusterTransfer;
+
+class ClusterNode
+{
+public:
+ virtual ~ClusterNode() {}
+ virtual void Sync() = 0;
+ bool IsMaster() { return m_Master; }
+
+protected:
+ ClusterNode(bool master)
+ : m_Master(master) {}
+
+private:
+ bool m_Master;
+};
+
+
+class MasterNode : public ClusterNode
+{
+public:
+ MasterNode(MasterSocket* socket, ClusterTransfer* sync, int param)
+ : ClusterNode(true)
+ , m_Socket(socket)
+ , m_Synchronizer(sync)
+ , m_AllSlavesConnected(false)
+ , m_InitialSlaveCount(param)
+ , m_CurrentSlaveCount(param) {}
+ ~MasterNode();
+
+ virtual void Sync();
+
+private:
+ void WaitForSlavesToConnect();
+ void WaitForSlavesToAck();
+
+ MasterSocket* m_Socket;
+ ClusterTransfer* m_Synchronizer;
+ bool m_AllSlavesConnected;
+ int m_InitialSlaveCount;
+ int m_CurrentSlaveCount;
+};
+
+
+class SlaveNode : public ClusterNode
+{
+public:
+ SlaveNode(SlaveSocket* socket, ClusterTransfer* sync, int param);
+ ~SlaveNode();
+
+ virtual void Sync();
+
+private:
+ int m_SlaveId;
+ SlaveSocket* m_Socket;
+ ClusterTransfer* m_Synchronizer;
+};
+
+#endif \ No newline at end of file
diff --git a/Runtime/ClusterRenderer/ClusterRendererDefines.h b/Runtime/ClusterRenderer/ClusterRendererDefines.h
new file mode 100644
index 0000000..9b6ccda
--- /dev/null
+++ b/Runtime/ClusterRenderer/ClusterRendererDefines.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#if ENABLE_CLUSTER_SYNC
+ #define DECLARE_CLUSTER_SERIALIZE(x) \
+ template<class TransferFunc> void ClusterTransfer (TransferFunc& transfer);
+ #define IMPLEMENT_CLUSTER_SERIALIZE(x) \
+ void x##UnusedClusterTemplateInitializer_() { \
+ x *a = NULL; StreamedBinaryWrite<false> *w = NULL; StreamedBinaryRead<false> *r = NULL; \
+ a->ClusterTransfer(*r); a->ClusterTransfer(*w); }
+#else
+ #define DECLARE_CLUSTER_SERIALIZE(x)
+ #define IMPLEMENT_CLUSTER_SERIALIZE(x)
+#endif
diff --git a/Runtime/ClusterRenderer/ClusterRendererModule.cpp b/Runtime/ClusterRenderer/ClusterRendererModule.cpp
new file mode 100644
index 0000000..86e349c
--- /dev/null
+++ b/Runtime/ClusterRenderer/ClusterRendererModule.cpp
@@ -0,0 +1,109 @@
+#include "UnityPrefix.h"
+#if ENABLE_CLUSTER_SYNC
+#include "ClusterRendererModule.h"
+#include "ClusterNode.h"
+#include "ClusterNetwork.h"
+#include "ClusterTransfer.h"
+#include "Runtime/Interfaces/IClusterRenderer.h"
+#include "Runtime/Utilities/Argv.h"
+
+using namespace std;
+
+bool ClusterRendererModule::IsInClusterTestMode;
+
+ClusterRendererModule::ClusterRendererModule()
+: m_Node(NULL)
+, m_Context(NULL)
+{
+
+}
+
+ClusterRendererModule::~ClusterRendererModule()
+{
+ if(m_Node != NULL)
+ delete m_Node;
+
+ if(m_Context != NULL)
+ DestroyZMQContext(m_Context);
+}
+
+void ClusterRendererModule::ProcessServerArgs(vector<string> args)
+{
+ m_Context = CreateZMQContext();
+ ClusterTransfer* sync = new ClusterTransfer();
+ MasterSocket* socket = new MasterSocket(m_Context, args[1].c_str(), args[2].c_str());
+ m_Node = new MasterNode(socket, sync, atoi(args[0].c_str()));
+}
+
+void ClusterRendererModule::ProcessClientArgs(vector<string> args)
+{
+ m_Context = CreateZMQContext();
+ ClusterTransfer* sync = new ClusterTransfer();
+ SlaveSocket* socket = new SlaveSocket(m_Context, args[1].c_str(), args[2].c_str());
+ m_Node = new SlaveNode(socket, sync, atoi(args[0].c_str()));
+}
+
+
+// TODO: user memory manager NEW
+void ClusterRendererModule::InitCluster()
+{
+ AssertIf(m_Node != NULL);
+ vector<string> values;
+
+ if(HasARGV("server"))
+ {
+ ProcessServerArgs(GetValuesForARGV("server"));
+ }
+ else if(HasARGV("client"))
+ {
+ ProcessClientArgs(GetValuesForARGV("client"));
+ }
+
+#ifdef DEBUG
+ IsInClusterTestMode = HasARGV("it");
+ // output some logs to indicate we are ready to go
+ // for IntegrationTest
+ LogString("ClusterReady");
+#endif
+}
+
+
+void ClusterRendererModule::SynchronizeCluster()
+{
+ // TODO check for NULL sockets
+ if(m_Node!= NULL)
+ m_Node->Sync();
+}
+
+bool ClusterRendererModule::IsMasterOfCluster()
+{
+ return (m_Node != NULL) ? m_Node->IsMaster() : true;
+}
+
+void ClusterRendererModule::ShutdownCluster()
+{
+ if(m_Node != NULL)
+ {
+ delete m_Node;
+ m_Node = NULL;
+ }
+
+ if(m_Context != NULL)
+ {
+ DestroyZMQContext(m_Context);
+ }
+}
+
+void InitializeClusterRendererModule ()
+{
+ SetIClusterRenderer(UNITY_NEW_AS_ROOT(ClusterRendererModule, kMemClusterRenderer, "ClusterRendererInterface", ""));
+}
+
+void CleanupClusterRendererModule ()
+{
+ ClusterRendererModule* module = reinterpret_cast<ClusterRendererModule*> (GetIClusterRenderer ());
+ UNITY_DELETE(module, kMemClusterRenderer);
+ SetIClusterRenderer (NULL);
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/ClusterRenderer/ClusterRendererModule.h b/Runtime/ClusterRenderer/ClusterRendererModule.h
new file mode 100644
index 0000000..d07aeb7
--- /dev/null
+++ b/Runtime/ClusterRenderer/ClusterRendererModule.h
@@ -0,0 +1,32 @@
+#pragma once
+#if ENABLE_CLUSTER_SYNC
+#include "Runtime/Interfaces/IClusterRenderer.h"
+
+class ClusterNode;
+
+using namespace std;
+
+class ClusterRendererModule : public IClusterRenderer
+{
+public:
+ ClusterRendererModule();
+ virtual ~ClusterRendererModule();
+ virtual void InitCluster();
+ virtual void SynchronizeCluster();
+ virtual bool IsMasterOfCluster();
+ virtual void ShutdownCluster();
+private:
+ void ProcessServerArgs(vector<string> args);
+ void ProcessClientArgs(vector<string> args);
+ ClusterNode* m_Node;
+ void* m_Context;
+#ifdef DEBUG
+public:
+ static bool IsInClusterTestMode;
+#endif
+
+};
+
+void InitializeClusterRendererModule ();
+void CleanupClusterRendererModule ();
+#endif \ No newline at end of file
diff --git a/Runtime/ClusterRenderer/ClusterRendererModule.jam b/Runtime/ClusterRenderer/ClusterRendererModule.jam
new file mode 100644
index 0000000..1baf137
--- /dev/null
+++ b/Runtime/ClusterRenderer/ClusterRendererModule.jam
@@ -0,0 +1,177 @@
+rule ClusterRendererModule_ReportCpp
+{
+ return
+ Runtime/ClusterRenderer/ClusterRendererDefines.h
+ Runtime/ClusterRenderer/ClusterNetwork.cpp
+ Runtime/ClusterRenderer/ClusterNetwork.h
+ Runtime/ClusterRenderer/ClusterTransfer.cpp
+ Runtime/ClusterRenderer/ClusterTransfer.h
+ Runtime/ClusterRenderer/ClusterNode.cpp
+ Runtime/ClusterRenderer/ClusterNode.h
+ Runtime/ClusterRenderer/ClusterRendererModule.cpp
+ Runtime/ClusterRenderer/ClusterRendererModule.h
+ Runtime/ClusterRenderer/ClusterRendererModuleRegistration.cpp
+
+ External/zmq/src/address.cpp
+ External/zmq/src/address.hpp
+ External/zmq/src/array.hpp
+ External/zmq/src/atomic_counter.hpp
+ External/zmq/src/atomic_ptr.hpp
+ External/zmq/src/blob.hpp
+ External/zmq/src/clock.cpp
+ External/zmq/src/clock.hpp
+ External/zmq/src/command.hpp
+ External/zmq/src/config.hpp
+ External/zmq/src/ctx.cpp
+ External/zmq/src/ctx.hpp
+ External/zmq/src/dealer.cpp
+ External/zmq/src/dealer.hpp
+ External/zmq/src/decoder.cpp
+ External/zmq/src/decoder.hpp
+ External/zmq/src/devpoll.cpp
+ External/zmq/src/devpoll.hpp
+ External/zmq/src/dist.cpp
+ External/zmq/src/dist.hpp
+ External/zmq/src/encoder.cpp
+ External/zmq/src/encoder.hpp
+ External/zmq/src/epoll.cpp
+ External/zmq/src/epoll.hpp
+ External/zmq/src/err.cpp
+ External/zmq/src/err.hpp
+ External/zmq/src/fd.hpp
+ External/zmq/src/fq.cpp
+ External/zmq/src/fq.hpp
+ External/zmq/src/i_decoder.hpp
+ External/zmq/src/i_encoder.hpp
+ External/zmq/src/i_engine.hpp
+ External/zmq/src/i_msg_sink.hpp
+ External/zmq/src/i_msg_source.hpp
+ External/zmq/src/i_poll_events.hpp
+ External/zmq/src/io_object.cpp
+ External/zmq/src/io_object.hpp
+ External/zmq/src/io_thread.cpp
+ External/zmq/src/io_thread.hpp
+ External/zmq/src/ip.cpp
+ External/zmq/src/ip.hpp
+ External/zmq/src/ipc_address.cpp
+ External/zmq/src/ipc_address.hpp
+ External/zmq/src/ipc_connecter.cpp
+ External/zmq/src/ipc_connecter.hpp
+ External/zmq/src/ipc_listener.cpp
+ External/zmq/src/ipc_listener.hpp
+ External/zmq/src/kqueue.cpp
+ External/zmq/src/kqueue.hpp
+ External/zmq/src/lb.cpp
+ External/zmq/src/lb.hpp
+ External/zmq/src/libzmq.pc.in
+ External/zmq/src/likely.hpp
+ External/zmq/src/mailbox.cpp
+ External/zmq/src/mailbox.hpp
+ External/zmq/src/msg.cpp
+ External/zmq/src/msg.hpp
+ External/zmq/src/mtrie.cpp
+ External/zmq/src/mtrie.hpp
+ External/zmq/src/mutex.hpp
+ External/zmq/src/object.cpp
+ External/zmq/src/object.hpp
+ External/zmq/src/options.cpp
+ External/zmq/src/options.hpp
+ External/zmq/src/own.cpp
+ External/zmq/src/own.hpp
+ External/zmq/src/pair.cpp
+ External/zmq/src/pair.hpp
+ External/zmq/src/pgm_receiver.cpp
+ External/zmq/src/pgm_receiver.hpp
+ External/zmq/src/pgm_sender.cpp
+ External/zmq/src/pgm_sender.hpp
+ External/zmq/src/pgm_socket.cpp
+ External/zmq/src/pgm_socket.hpp
+ External/zmq/src/pipe.cpp
+ External/zmq/src/pipe.hpp
+ External/zmq/src/platform.hpp.in
+ External/zmq/src/poll.cpp
+ External/zmq/src/poll.hpp
+ External/zmq/src/poller.hpp
+ External/zmq/src/poller_base.cpp
+ External/zmq/src/poller_base.hpp
+ External/zmq/src/precompiled.cpp
+ External/zmq/src/precompiled.hpp
+ External/zmq/src/proxy.cpp
+ External/zmq/src/proxy.hpp
+ External/zmq/src/pub.cpp
+ External/zmq/src/pub.hpp
+ External/zmq/src/pull.cpp
+ External/zmq/src/pull.hpp
+ External/zmq/src/push.cpp
+ External/zmq/src/push.hpp
+ External/zmq/src/random.cpp
+ External/zmq/src/random.hpp
+ External/zmq/src/reaper.cpp
+ External/zmq/src/reaper.hpp
+ External/zmq/src/rep.cpp
+ External/zmq/src/rep.hpp
+ External/zmq/src/req.cpp
+ External/zmq/src/req.hpp
+ External/zmq/src/router.cpp
+ External/zmq/src/router.hpp
+ External/zmq/src/select.cpp
+ External/zmq/src/select.hpp
+ External/zmq/src/session_base.cpp
+ External/zmq/src/session_base.hpp
+ External/zmq/src/signaler.cpp
+ External/zmq/src/signaler.hpp
+ External/zmq/src/socket_base.cpp
+ External/zmq/src/socket_base.hpp
+ External/zmq/src/stdint.hpp
+ External/zmq/src/stream_engine.cpp
+ External/zmq/src/stream_engine.hpp
+ External/zmq/src/sub.cpp
+ External/zmq/src/sub.hpp
+ External/zmq/src/tcp.cpp
+ External/zmq/src/tcp.hpp
+ External/zmq/src/tcp_address.cpp
+ External/zmq/src/tcp_address.hpp
+ External/zmq/src/tcp_connecter.cpp
+ External/zmq/src/tcp_connecter.hpp
+ External/zmq/src/tcp_listener.cpp
+ External/zmq/src/tcp_listener.hpp
+ External/zmq/src/thread.cpp
+ External/zmq/src/thread.hpp
+ External/zmq/src/trie.cpp
+ External/zmq/src/trie.hpp
+ External/zmq/src/v1_decoder.cpp
+ External/zmq/src/v1_decoder.hpp
+ External/zmq/src/v1_encoder.cpp
+ External/zmq/src/v1_encoder.hpp
+ External/zmq/src/v1_protocol.hpp
+ External/zmq/src/windows.hpp
+ External/zmq/src/wire.hpp
+ External/zmq/src/xpub.cpp
+ External/zmq/src/xpub.hpp
+ External/zmq/src/xsub.cpp
+ External/zmq/src/xsub.hpp
+ External/zmq/src/ypipe.hpp
+ External/zmq/src/yqueue.hpp
+ External/zmq/src/zmq.cpp
+ External/zmq/src/zmq_utils.cpp
+ ;
+}
+
+rule ClusterRendererModule_ReportIncludes
+{
+ return
+ External/zmq/include
+ ;
+}
+
+rule ClusterRendererModule_Init
+{
+ # enable this feature. This is only required now since modularization and licensing are not ON by default.
+ C.Defines : ENABLE_CLUSTER_SYNC=1 ;
+
+ # register other override
+ OverrideModule ClusterRenderer : GetModule_Cpp : byOverridingWithMethod : ClusterRendererModule_ReportCpp ;
+ OverrideModule ClusterRenderer : GetModule_Inc : byOverridingWithMethod : ClusterRendererModule_ReportIncludes ;
+}
+
+#RegisterModule ClusterRenderer ; \ No newline at end of file
diff --git a/Runtime/ClusterRenderer/ClusterRendererModuleRegistration.cpp b/Runtime/ClusterRenderer/ClusterRendererModuleRegistration.cpp
new file mode 100644
index 0000000..73be20d
--- /dev/null
+++ b/Runtime/ClusterRenderer/ClusterRendererModuleRegistration.cpp
@@ -0,0 +1,10 @@
+#include "UnityPrefix.h"
+#if ENABLE_CLUSTER_SYNC
+#include "ClusterRendererModule.h"
+
+extern "C" EXPORT_MODULE void RegisterModule_ClusterRenderer ()
+{
+ InitializeClusterRendererModule ();
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/ClusterRenderer/ClusterTransfer.cpp b/Runtime/ClusterRenderer/ClusterTransfer.cpp
new file mode 100644
index 0000000..40b6633
--- /dev/null
+++ b/Runtime/ClusterRenderer/ClusterTransfer.cpp
@@ -0,0 +1,93 @@
+#include "UnityPrefix.h"
+#if ENABLE_CLUSTER_SYNC
+#include "ClusterTransfer.h"
+#include "Runtime/Serialize/FileCache.h"
+#include "Runtime/Serialize/CacheWrap.h"
+#include "Runtime/Serialize/TransferFunctions/StreamedBinaryWrite.h"
+#include "Runtime/Serialize/TransferFunctions/StreamedBinaryRead.h"
+#include "Runtime/Input/InputManager.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Dynamics/PhysicsManager.h"
+
+#ifdef DEBUG
+#include "Runtime/Dynamics/Rigidbody.h"
+#include "Runtime/Graphics/Transform.h"
+#endif
+
+template<class TransferFunc>
+void TransferManagerStates (TransferFunc& transfer)
+{
+ // TODO: use macro incase manager is not present
+ GetInputManager().ClusterTransfer(transfer);
+ GetTimeManager().ClusterTransfer(transfer);
+ GetPhysicsManager().ClusterTransfer(transfer);
+}
+
+void ClusterTransfer::TransferToBuffer(dynamic_array<UInt8>& buffer)
+{
+ StreamedBinaryWrite<false> writeStream;
+ CachedWriter& writeCache = writeStream.Init (0, BuildTargetSelection::NoTarget());
+ MemoryCacheWriter memoryCache (buffer);
+ writeCache.InitWrite (memoryCache);
+ // transfer stuff
+ TransferManagerStates(writeStream);
+ // end of transfer
+ writeCache.CompleteWriting();
+}
+
+void ClusterTransfer::TransferFromBuffer(dynamic_array<UInt8>& buffer)
+{
+ // transfer setup
+ StreamedBinaryRead<false> readStream;
+ CachedReader& readCache = readStream.Init (0);
+ MemoryCacheReader memoryCache (buffer);
+ readCache.InitRead (memoryCache, 0, buffer.size());
+ // transfer stuff
+ TransferManagerStates(readStream);
+ // end of transfer
+ readCache.End();
+}
+
+#ifdef DEBUG
+void ClusterTransfer::TransferToFile(int slaveId)
+{
+ // create the buffer
+ dynamic_array<UInt8> buffer(kMemTempAlloc);
+
+ // create the stream
+ StreamedBinaryWrite<false> writeStream;
+ CachedWriter& writeCache = writeStream.Init (0, BuildTargetSelection::NoTarget());
+ MemoryCacheWriter memoryCache (buffer);
+ writeCache.InitWrite (memoryCache);
+
+ // now write the data out to a file
+ for (int level=0;level<PhysicsManager::kMaxSortedActorsDepth;level++)
+ {
+ PhysicsManager::RigidbodyList& bodies = GetPhysicsManager().m_SortedActors[level];
+ for (PhysicsManager::RigidbodyList::iterator i=bodies.begin();i != bodies.end();i++)
+ {
+ Rigidbody& body = **i;
+
+ if (body.m_DisableReadUpdateTransform == 0)
+ {
+ GameObject& go = body.GetGameObject();
+ Transform& transform = go.GetComponent (Transform);
+ transform.Transfer(writeStream);
+ }
+ }
+ }
+
+ // end of transfer
+ writeCache.CompleteWriting();
+
+
+ // now we have the buffer, we put it into a file, and compare that with other slaves.
+ char fileName[32];
+ sprintf(fileName, "dump%d.dat\0", slaveId);
+ FILE* dump = fopen(fileName, "wb");
+ fwrite(buffer.data(), sizeof(UInt8), buffer.size(), dump);
+ fclose(dump);
+}
+#endif // DEBUG
+
+#endif \ No newline at end of file
diff --git a/Runtime/ClusterRenderer/ClusterTransfer.h b/Runtime/ClusterRenderer/ClusterTransfer.h
new file mode 100644
index 0000000..3b87fbd
--- /dev/null
+++ b/Runtime/ClusterRenderer/ClusterTransfer.h
@@ -0,0 +1,16 @@
+#pragma once
+#if ENABLE_CLUSTER_SYNC
+#include "Runtime/Utilities/dynamic_array.h"
+
+class ClusterTransfer
+{
+public:
+ void TransferToBuffer(dynamic_array<UInt8>& buffer);
+ void TransferFromBuffer(dynamic_array<UInt8>& buffer);
+#ifdef DEBUG
+ void TransferToFile(int slaveId);
+#endif
+};
+
+
+#endif \ No newline at end of file
diff --git a/Runtime/Containers/ConstantString.cpp b/Runtime/Containers/ConstantString.cpp
new file mode 100644
index 0000000..9cda5af
--- /dev/null
+++ b/Runtime/Containers/ConstantString.cpp
@@ -0,0 +1,123 @@
+#include "UnityPrefix.h"
+#include "ConstantString.h"
+#include "ConstantStringManager.h"
+#include "Runtime/Threads/AtomicOps.h"
+
+///@TODO: Use 24 bits for refcount
+///@TODO: Handle ref count overflow
+
+void ConstantString::create_empty ()
+{
+ cleanup();
+ m_Buffer = GetConstantStringManager().GetEmptyString();
+ Assert(!owns_string());
+}
+
+// The header compressed the label and refcount into a 32 bits.
+// Refcount lives on the 0xFFFF, label lives on the higher bits.
+// We use atomic ops for thread safety on refcounting when deleting ConstantStrings.
+struct AllocatedStringHeader
+{
+ volatile int refCountAndLabel;
+};
+
+MemLabelId GetLabel (const AllocatedStringHeader& header)
+{
+ int intLabel = (header.refCountAndLabel & (0x0000FFFF)) << 16;
+ ProfilerAllocationHeader* root = GET_ALLOC_HEADER(&GetConstantStringManager(), kMemString);
+ MemLabelId label( (MemLabelIdentifier)intLabel, root);
+ return label;
+}
+
+void SetLabel (AllocatedStringHeader& header, MemLabelId label)
+{
+ int labelInt = label.label;
+ Assert(labelInt < 0xFFFF);
+ labelInt <<= 16;
+
+ header.refCountAndLabel &= 0x0000FFFF;
+ header.refCountAndLabel |= labelInt;
+}
+
+int GetRefCount (int refCountAndLabel)
+{
+ return refCountAndLabel & 0xFFFF;
+}
+
+
+inline static AllocatedStringHeader* GetHeader (const char* ptr)
+{
+ return reinterpret_cast<AllocatedStringHeader*> (const_cast<char*> (ptr) - sizeof(AllocatedStringHeader));
+}
+
+void ConstantString::operator = (const ConstantString& input)
+{
+ assign(input);
+}
+
+void ConstantString::assign (const ConstantString& input)
+{
+ cleanup();
+ m_Buffer = input.m_Buffer;
+ if (owns_string())
+ {
+ AtomicIncrement(&GetHeader (get_char_ptr_fast())->refCountAndLabel);
+ }
+}
+
+
+void ConstantString::assign (const char* str, MemLabelId label)
+{
+ cleanup();
+ const char* constantString = GetConstantStringManager().GetConstantString(str);
+ // Own Strings
+ if (constantString == NULL)
+ {
+ label.SetRootHeader(GET_ALLOC_HEADER(&GetConstantStringManager(), kMemString));
+ size_t length = strlen(str);
+
+ char* allocated = (char*)UNITY_MALLOC (label, length + 1 + sizeof(AllocatedStringHeader));
+ char* allocatedString = allocated + sizeof(AllocatedStringHeader);
+
+ AllocatedStringHeader& header = *GetHeader (allocatedString);
+ header.refCountAndLabel = 1;
+ SetLabel(header, label);
+
+ Assert(GetRefCount(header.refCountAndLabel) == 1);
+ memcpy(allocatedString, str, length);
+ allocatedString[length] = 0;
+
+
+ m_Buffer = reinterpret_cast<char*> (reinterpret_cast<size_t> (allocatedString) | 1);
+ Assert(owns_string());
+ }
+ else
+ {
+ m_Buffer = constantString;
+ Assert(!owns_string());
+ }
+}
+
+void ConstantString::cleanup ()
+{
+ if (owns_string())
+ {
+ AllocatedStringHeader* header = GetHeader(get_char_ptr_fast ());
+
+ int newRefCount = AtomicDecrement (&header->refCountAndLabel);
+ newRefCount = GetRefCount(newRefCount);
+
+ if (newRefCount == 0)
+ {
+ MemLabelId label = GetLabel(*header);
+ UNITY_FREE(label, header);
+ }
+ }
+
+ m_Buffer = NULL;
+}
+
+ConstantString::~ConstantString ()
+{
+ cleanup ();
+}
diff --git a/Runtime/Containers/ConstantString.h b/Runtime/Containers/ConstantString.h
new file mode 100644
index 0000000..5705147
--- /dev/null
+++ b/Runtime/Containers/ConstantString.h
@@ -0,0 +1,82 @@
+#pragma once
+
+/// Constant strings use the ConstantStringManager to reuse commonly used strings.
+/// Eg. GameObject names and asset names are often exactly the same.
+/// When a string is not available in ConstantStringManager, it is allocated on the heap and refcounted.
+/// operator = is used to assign refcounted strings. (operator = is always dirty cheap)
+/// ConstantString uses only 1 ptr in the struct directly, thus the simplest case (A shared constant string) has no allocations & only 1 pointer for storage.
+
+
+/// The ConstantStringManager is initialized at load time with all common strings and
+/// stays completely constant during runtime, thus it is thread safe. ConstantString looks up all strings there first and if it is in it, it will use those.
+/// Eg. when loading an asset bundle we can still reduce memory usage.
+/// When serializing strings on targets that dont need to worry about backwards compatibility or asset bundle compatibility, we can simply store an index to the ConstantStringManager and look it up by index on load.
+/// Thus also reducing size on disk too.
+
+struct ConstantString
+{
+ ConstantString (const char* str, MemLabelId label)
+ : m_Buffer (NULL)
+ {
+ assign (str, label);
+ }
+
+ ConstantString ()
+ : m_Buffer (NULL)
+ {
+ create_empty();
+ }
+
+ ConstantString (const ConstantString& input)
+ : m_Buffer (NULL)
+ {
+ assign(input);
+ }
+
+ ~ConstantString ();
+
+ void assign (const char* str, MemLabelId label);
+ void assign (const ConstantString& input);
+
+ void operator = (const ConstantString& input);
+
+ const char* c_str() const { return get_char_ptr_fast (); }
+ bool empty () const { return m_Buffer[0] == 0; }
+
+ friend bool operator == (const ConstantString& lhs, const ConstantString& rhs)
+ {
+ if (lhs.owns_string () || rhs.owns_string())
+ return strcmp(lhs.c_str(), rhs.c_str()) == 0;
+ else
+ return lhs.m_Buffer == rhs.m_Buffer;
+ }
+
+ friend bool operator == (const ConstantString& lhs, const char* rhs)
+ {
+ return strcmp(lhs.c_str(), rhs) == 0;
+ }
+
+ friend bool operator == (const char* rhs, const ConstantString& lhs)
+ {
+ return strcmp(lhs.c_str(), rhs) == 0;
+ }
+
+ friend bool operator != (const ConstantString& lhs, const char* rhs)
+ {
+ return strcmp(lhs.c_str(), rhs) != 0;
+ }
+
+ friend bool operator != (const char* rhs, const ConstantString& lhs)
+ {
+ return strcmp(lhs.c_str(), rhs) != 0;
+ }
+
+ private:
+
+ inline bool owns_string () const { return reinterpret_cast<size_t> (m_Buffer) & 1; }
+ inline const char* get_char_ptr_fast () const { return reinterpret_cast<const char*> (reinterpret_cast<size_t> (m_Buffer) & ~1); }
+ void cleanup ();
+ void create_empty ();
+
+ const char* m_Buffer;
+}; \ No newline at end of file
diff --git a/Runtime/Containers/ConstantStringManager.cpp b/Runtime/Containers/ConstantStringManager.cpp
new file mode 100644
index 0000000..157d98d
--- /dev/null
+++ b/Runtime/Containers/ConstantStringManager.cpp
@@ -0,0 +1,91 @@
+#include "UnityPrefix.h"
+#include "ConstantStringManager.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+
+static ConstantStringManager* gConstantStringManager = NULL;
+
+void ConstantStringManager::ProfileCommonString (const char* str)
+{
+#if PROFILE_COMMON_STRINGS
+ SET_ALLOC_OWNER(this);
+ m_CommonStringMutex.Lock();
+ m_CommonStringCounter[str]++;
+ m_CommonStringMutex.Unlock();
+#endif
+}
+
+void ConstantStringManager::DumpCommonStrings ()
+{
+#if PROFILE_COMMON_STRINGS
+ for(CommonStringCounter::iterator i=m_CommonStringCounter.begin();i != m_CommonStringCounter.end();++i)
+ {
+ printf_console("%d\t -> '%s'\n", i->second, i->first.c_str());
+ }
+#endif
+}
+
+ConstantStringManager::ConstantStringManager ()
+{
+ AddConstantString("");
+ AddConstantString("TextMesh");
+}
+
+ConstantStringManager::~ConstantStringManager ()
+{
+ for (int i=0;i<m_Strings.size();i++)
+ {
+ UNITY_FREE(kMemStaticString, (void*)m_Strings[i]);
+ }
+}
+
+const char* ConstantStringManager::GetEmptyString()
+{
+ return m_Strings[0];
+}
+
+const char* ConstantStringManager::GetConstantString(const char* str)
+{
+ ProfileCommonString (str);
+
+ for (int i=0;i<m_Strings.size();i++)
+ {
+ if (strcmp(m_Strings[i], str) == 0)
+ return m_Strings[i];
+ }
+
+ return NULL;
+}
+
+void ConstantStringManager::AddConstantString (const char* string )
+{
+ SET_ALLOC_OWNER(gConstantStringManager);
+ int length = strlen(string);
+ char* newString = (char*)UNITY_MALLOC(kMemStaticString, length+1);
+ memcpy(newString, string, length+1);
+ m_Strings.push_back(newString);
+}
+
+void ConstantStringManager::AddConstantStrings (const char** strings, size_t size )
+{
+ m_Strings.resize_uninitialized(size + m_Strings.size());
+ for (int i=0;i<size;++i)
+ AddConstantString(strings[i]);
+}
+
+void ConstantStringManager::StaticInitialize ()
+{
+ gConstantStringManager = UNITY_NEW_AS_ROOT(ConstantStringManager, kMemString, "SharedStrings", "");
+}
+
+void ConstantStringManager::StaticCleanup ()
+{
+ // gConstantStringManager->DumpCommonStrings ();
+ UNITY_DELETE(gConstantStringManager, kMemString);
+ gConstantStringManager = NULL;
+}
+static RegisterRuntimeInitializeAndCleanup s_ConstantStringManagerCallbacks(ConstantStringManager::StaticInitialize, ConstantStringManager::StaticCleanup);
+
+ConstantStringManager& GetConstantStringManager ()
+{
+ return *gConstantStringManager;
+}
diff --git a/Runtime/Containers/ConstantStringManager.h b/Runtime/Containers/ConstantStringManager.h
new file mode 100644
index 0000000..76354ed
--- /dev/null
+++ b/Runtime/Containers/ConstantStringManager.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Threads/Mutex.h"
+
+class ConstantStringManager
+{
+ #define PROFILE_COMMON_STRINGS !UNITY_RELEASE
+
+ #if PROFILE_COMMON_STRINGS
+ typedef std::map<std::string, int> CommonStringCounter;
+ CommonStringCounter m_CommonStringCounter;
+ Mutex m_CommonStringMutex;
+ #endif
+
+ dynamic_array<const char*> m_Strings;
+ void ProfileCommonString (const char* str);
+
+ public:
+
+ ConstantStringManager ();
+ ~ConstantStringManager ();
+
+ void AddConstantStrings (const char** strings, size_t size );
+ void AddConstantString (const char* strings );
+
+ const char* GetConstantString(const char* str);
+ const char* GetEmptyString();
+
+ // Init
+ static void StaticInitialize ();
+ static void StaticCleanup ();
+
+ void DumpCommonStrings ();
+
+};
+
+ConstantStringManager& GetConstantStringManager ();
diff --git a/Runtime/Containers/ConstantStringSerialization.h b/Runtime/Containers/ConstantStringSerialization.h
new file mode 100644
index 0000000..47fd4b7
--- /dev/null
+++ b/Runtime/Containers/ConstantStringSerialization.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "Runtime/Allocator/STLAllocator.h"
+#include "Runtime/Serialize/SerializeTraits.h"
+
+template<class TransferFunction>
+inline void TransferConstantString (ConstantString& constantString, const char* name, TransferMetaFlags transferFlags, MemLabelId label, TransferFunction& transfer)
+{
+ //@TODO: Make this string use temp data
+ UnityStr tempStr;
+ if (transfer.IsWriting())
+ tempStr = constantString.c_str();
+
+ transfer.Transfer(tempStr, name, transferFlags);
+
+ if (transfer.IsReading())
+ constantString.assign(tempStr.c_str(), label);
+}
diff --git a/Runtime/Containers/ExtendedRingbuffer.h b/Runtime/Containers/ExtendedRingbuffer.h
new file mode 100644
index 0000000..0eb6d7b
--- /dev/null
+++ b/Runtime/Containers/ExtendedRingbuffer.h
@@ -0,0 +1,146 @@
+#ifndef RUNTIME_CONTAINERS_RINGBUFFERS_H
+#define RUNTIME_CONTAINERS_RINGBUFFERS_H
+
+#include "GrowingRingbuffer.h"
+#include "Ringbuffer.h"
+#include "TransactionalRingbuffer.h"
+#include "Runtime/Threads/Semaphore.h"
+#include "Runtime/Utilities/NonCopyable.h"
+
+#if SUPPORT_THREADS
+namespace RingbufferTemplates
+{
+ // ------------------------------------------------------------------------
+ // Support base classes used to avoid casting to 'normal' ringbuffer impl
+ // ------------------------------------------------------------------------
+ template <class Ringbuffer>
+ class NonCastable : protected Ringbuffer
+ {
+ public:
+ using Ringbuffer::WritePtr;
+ using Ringbuffer::WritePtrUpdate;
+ using Ringbuffer::ReadPtr;
+ using Ringbuffer::ReadPtrUpdate;
+ using Ringbuffer::GetSize;
+ using Ringbuffer::GetFreeSize;
+ using Ringbuffer::GetAllocatedSize;
+ using Ringbuffer::GetAvailableSize;
+ using Ringbuffer::Reset;
+
+ protected:
+ NonCastable(MemLabelId label, UInt32 size) : Ringbuffer(label, size) {}
+ };
+
+ // ------------------------------------------------------------------------
+ // Adds support for notifications whenever data or free space is available
+ // ------------------------------------------------------------------------
+ template <class Ringbuffer>
+ class AbstractNotificationSupport : public NonCastable<Ringbuffer>, public NonCopyable
+ {
+ public:
+ AbstractNotificationSupport(MemLabelId label, UInt32 size) : NonCastable<Ringbuffer>(label, size) {}
+
+ void ReleaseBlockedThreads(bool indefinitely = false)
+ {
+ m_FreeSemaphore.Suspend(indefinitely);
+ m_AvailableSemaphore.Suspend(indefinitely);
+ }
+
+ void BlockUntilFree()
+ {
+ if (Ringbuffer::GetFreeSize())
+ return;
+
+ m_FreeSemaphore.Resume(true);
+ if (Ringbuffer::GetFreeSize() == 0)
+ m_FreeSemaphore.WaitForSignal();
+ m_FreeSemaphore.Suspend();
+ }
+
+ void BlockUntilAvailable()
+ {
+ if (Ringbuffer::GetAvailableSize())
+ return;
+
+ m_AvailableSemaphore.Resume(true);
+ if (Ringbuffer::GetAvailableSize() == 0)
+ m_AvailableSemaphore.WaitForSignal();
+ m_AvailableSemaphore.Suspend();
+ }
+
+ protected:
+ SuspendableSemaphore m_AvailableSemaphore;
+ SuspendableSemaphore m_FreeSemaphore;
+ };
+
+ template <class Ringbuffer>
+ class RNotificationSupport : public AbstractNotificationSupport<Ringbuffer>
+ {
+ public:
+ RNotificationSupport(MemLabelId label, UInt32 size) : AbstractNotificationSupport<Ringbuffer>(label, size) {}
+
+ void WritePtrUpdate(void* writePtr, UInt32 nBytesWritten)
+ {
+ Ringbuffer::WritePtrUpdate(writePtr, nBytesWritten);
+ AbstractNotificationSupport<Ringbuffer>::m_AvailableSemaphore.Signal();
+ }
+
+ void ReadPtrUpdate(const void* readPtr, UInt32 nBytesRead)
+ {
+ Ringbuffer::ReadPtrUpdate(readPtr, nBytesRead);
+ AbstractNotificationSupport<Ringbuffer>::m_FreeSemaphore.Signal();
+ }
+ };
+
+
+ template <class TransactionalRingbuffer>
+ class TRNotificationSupport : public AbstractNotificationSupport<TransactionalRingbuffer>
+ {
+ public:
+ TRNotificationSupport(MemLabelId label, UInt32 size) : AbstractNotificationSupport<TransactionalRingbuffer>(label, size) {}
+
+ using AbstractNotificationSupport<TransactionalRingbuffer>::ReadPtrReset;
+ using AbstractNotificationSupport<TransactionalRingbuffer>::WritePtrReset;
+
+ void WritePtrCommit()
+ {
+ AbstractNotificationSupport<TransactionalRingbuffer>::WritePtrCommit();
+ AbstractNotificationSupport<TransactionalRingbuffer>::m_AvailableSemaphore.Signal();
+ }
+
+ void ReadPtrCommit()
+ {
+ AbstractNotificationSupport<TransactionalRingbuffer>::ReadPtrCommit();
+ AbstractNotificationSupport<TransactionalRingbuffer>::m_FreeSemaphore.Signal();
+ }
+ };
+
+}
+
+typedef RingbufferTemplates::RNotificationSupport<Ringbuffer> ExtendedRingbuffer;
+typedef RingbufferTemplates::RNotificationSupport<GrowingRingbuffer> ExtendedGrowingRingbuffer;
+typedef RingbufferTemplates::TRNotificationSupport<TransactionalRingbuffer> ExtendedTransactionalRingbuffer;
+
+#else // no thread support
+
+namespace RingbufferTemplates
+{
+ template <class Ringbuffer>
+ class NotificationSupport : public Ringbuffer, public NonCopyable
+ {
+ public:
+ NotificationSupport(MemLabelId label, UInt32 size) : Ringbuffer(label, size) {}
+
+ void ReleaseBlockedThreads(bool indefinitely = false) {}
+ void BlockUntilFree() {}
+ void BlockUntilAvailable() {}
+ };
+}
+
+typedef RingbufferTemplates::NotificationSupport<Ringbuffer> ExtendedRingbuffer;
+typedef RingbufferTemplates::NotificationSupport<GrowingRingbuffer> ExtendedGrowingRingbuffer;
+typedef RingbufferTemplates::NotificationSupport<TransactionalRingbuffer> ExtendedTransactionalRingbuffer;
+
+#endif
+
+#endif //RUNTIME_CONTAINERS_RINGBUFFERS_H
diff --git a/Runtime/Containers/GrowingRingbuffer.h b/Runtime/Containers/GrowingRingbuffer.h
new file mode 100644
index 0000000..c4af135
--- /dev/null
+++ b/Runtime/Containers/GrowingRingbuffer.h
@@ -0,0 +1,121 @@
+#ifndef RUNTIME_CONTAINERS_GROWINGRINGBUFFER_H
+#define RUNTIME_CONTAINERS_GROWINGRINGBUFFER_H
+
+#include "Ringbuffer.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Threads/AtomicOps.h"
+
+// --------------------------------------------------------------------
+// A Ringbuffer with the ability to grow
+// Concurrently supports one consumer and one producer
+//
+// The functions in this class should be synchronized with
+// Ringbuffer to fit Ringbuffer template functions.
+// --------------------------------------------------------------------
+class GrowingRingbuffer
+{
+public:
+ GrowingRingbuffer(MemLabelId label, UInt32 maxSize, UInt32 initialSize = 1024)
+ : m_Label(label)
+ , m_MinSize(initialSize)
+ , m_MaxSize(maxSize)
+ {
+ InitializeBuffers();
+ }
+
+ ~GrowingRingbuffer() { DeleteBuffers(); }
+
+ void* WritePtr(UInt32* nBytes) const;
+ void WritePtrUpdate(void* writePtr, UInt32 nBytesWritten);
+
+ const void* ReadPtr(UInt32* nBytes) const;
+ void ReadPtrUpdate(const void* readPtr, UInt32 nBytesRead);
+
+ UInt32 GetAllocatedSize() const { return m_AllocatedSize; }
+ UInt32 GetAvailableSize() const { return m_AvailableSize; }
+ UInt32 GetFreeSize() const { return m_MaxSize - m_AvailableSize; }
+ UInt32 GetSize() const { return m_MaxSize; }
+
+ void Reset();
+
+private:
+ void InitializeBuffers();
+ void DeleteBuffers();
+
+ struct RingbufferLink:public Ringbuffer
+ {
+ RingbufferLink(MemLabelId label, UInt32 size) : Ringbuffer(label, size) , next(NULL) { }
+ RingbufferLink* volatile next;
+ };
+
+ UInt32 m_MaxSize;
+ UInt32 m_MinSize;
+ MemLabelId m_Label;
+ volatile UInt32 m_AllocatedSize;
+ volatile UInt32 m_AvailableSize;
+ RingbufferLink* volatile m_ReadBuffer;
+ RingbufferLink* volatile m_WriteBuffer;
+};
+
+inline void GrowingRingbuffer::InitializeBuffers()
+{
+ m_WriteBuffer = m_ReadBuffer = new RingbufferLink(m_Label, m_MinSize);
+ m_AllocatedSize = m_ReadBuffer->GetSize();
+ m_AvailableSize = 0;
+}
+
+inline void GrowingRingbuffer::DeleteBuffers()
+{
+ RingbufferLink* buffer = m_ReadBuffer;
+ while (buffer)
+ {
+ RingbufferLink* current = buffer;
+ buffer = current->next;
+ delete current;
+ }
+}
+
+inline void GrowingRingbuffer::Reset()
+{
+ DeleteBuffers();
+ InitializeBuffers();
+}
+
+FORCE_INLINE void* GrowingRingbuffer::WritePtr(UInt32* nBytes) const
+{
+ *nBytes = std::min(*nBytes, GetFreeSize());
+ return m_WriteBuffer->WritePtr(nBytes);
+}
+
+FORCE_INLINE const void* GrowingRingbuffer::ReadPtr(UInt32* nBytes) const
+{
+ return m_ReadBuffer->ReadPtr(nBytes);
+}
+
+FORCE_INLINE void GrowingRingbuffer::WritePtrUpdate(void* writePtr, UInt32 nBytesWritten)
+{
+ m_WriteBuffer->WritePtrUpdate(writePtr, nBytesWritten);
+ AtomicAdd((volatile int*) &m_AvailableSize, nBytesWritten);
+ if (m_WriteBuffer->GetFreeSize() == 0 && GetFreeSize() > 0)
+ {
+ RingbufferLink* emptyBuffer = new RingbufferLink(m_Label, m_AllocatedSize);
+ m_WriteBuffer->next = emptyBuffer;
+ m_WriteBuffer = emptyBuffer;
+ AtomicAdd((volatile int*) &m_AllocatedSize, emptyBuffer->GetSize());
+ }
+}
+
+FORCE_INLINE void GrowingRingbuffer::ReadPtrUpdate(const void* readPtr, UInt32 nBytesRead)
+{
+ m_ReadBuffer->ReadPtrUpdate(readPtr, nBytesRead);
+ AtomicSub((volatile int*) &m_AvailableSize, nBytesRead);
+ if (m_ReadBuffer->next && m_ReadBuffer->GetAvailableSize() == 0)
+ {
+ AtomicSub((volatile int*) &m_AllocatedSize, m_ReadBuffer->GetSize());
+ RingbufferLink* emptyBuffer = m_ReadBuffer;
+ m_ReadBuffer = m_ReadBuffer->next;
+ delete emptyBuffer;
+ }
+}
+
+#endif // RUNTIME_CONTAINERS_GROWINGRINGBUFFER_H
diff --git a/Runtime/Containers/Ringbuffer.h b/Runtime/Containers/Ringbuffer.h
new file mode 100644
index 0000000..7a34b5e
--- /dev/null
+++ b/Runtime/Containers/Ringbuffer.h
@@ -0,0 +1,94 @@
+#ifndef RUNTIME_CONTAINERS_RINGBUFFER_H
+#define RUNTIME_CONTAINERS_RINGBUFFER_H
+
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Allocator/tlsf/tlsfbits.h"
+#include "Runtime/Threads/AtomicOps.h"
+
+// --------------------------------------------------------------------
+// Ringbuffer
+// Concurrently supports one consumer and one producer
+// --------------------------------------------------------------------
+class Ringbuffer
+{
+public:
+ Ringbuffer(void* memory, UInt32 size);
+ Ringbuffer(MemLabelId label, UInt32 size);
+
+ ~Ringbuffer();
+
+ void* WritePtr(UInt32* nBytes) const { return Ptr(m_Put, GetFreeSize(), nBytes); }
+ void WritePtrUpdate(void* writePtr, UInt32 nBytesWritten);
+
+ const void* ReadPtr(UInt32* nBytes) const { return Ptr(m_Get, GetAvailableSize(), nBytes); }
+ void ReadPtrUpdate(const void* readPtr, UInt32 nBytesRead);
+
+ UInt32 GetSize() const { return m_Size; }
+ UInt32 GetFreeSize() const { return m_Size - GetAvailableSize(); }
+ UInt32 GetAvailableSize() const { return m_Put - m_Get; }
+ UInt32 GetAllocatedSize() const { return GetAvailableSize(); }
+
+ void Reset() { m_Get = m_Put = 0; }
+
+protected:
+ void* Ptr(UInt32 position, UInt32 availableBytes, UInt32* nBytes) const;
+
+protected:
+ char* m_Buffer;
+ bool m_OwnMemory;
+ MemLabelId m_OwnMemoryLabel;
+ UInt32 m_Size;
+ volatile UInt32 m_Get;
+ volatile UInt32 m_Put;
+};
+
+// ---------------------------------------------------------------------------
+inline Ringbuffer::Ringbuffer(void* memory, UInt32 size)
+: m_Get(0)
+, m_Put(0)
+{
+ m_Size = 1 << tlsf_fls(size); // use the biggest 2^n size that fits
+ m_Buffer = reinterpret_cast<char*>(memory);
+ m_OwnMemory = false;
+}
+
+inline Ringbuffer::Ringbuffer(MemLabelId label, UInt32 size)
+: m_Get(0)
+, m_Put(0)
+{
+ AssertBreak(size >> 31 == 0);
+ m_Size = 1 << tlsf_fls(size*2 - 1); // make sure we have _at least_ 'size' bytes
+ m_Buffer = reinterpret_cast<char*>(UNITY_MALLOC(label, m_Size));
+ m_OwnMemory = true;
+ m_OwnMemoryLabel = label;
+}
+
+inline Ringbuffer::~Ringbuffer()
+{
+ if (m_OwnMemory)
+ UNITY_FREE(m_OwnMemoryLabel, m_Buffer);
+}
+
+FORCE_INLINE void Ringbuffer::WritePtrUpdate(void* writePtr, UInt32 nBytesWritten)
+{
+ int result = AtomicAdd((volatile int*)&m_Put, nBytesWritten);
+ AssertBreak(writePtr == &m_Buffer[(result - nBytesWritten) & (m_Size - 1)]);
+}
+
+FORCE_INLINE void Ringbuffer::ReadPtrUpdate(const void* readPtr, UInt32 nBytesRead)
+{
+ int result = AtomicAdd((volatile int*)&m_Get, nBytesRead);
+ AssertBreak(readPtr == &m_Buffer[(result - nBytesRead) & (m_Size - 1)]);
+}
+
+FORCE_INLINE void* Ringbuffer::Ptr(UInt32 position, UInt32 bytesAvailable, UInt32* nBytesPtr) const
+{
+ UInt32& nBytes = *nBytesPtr;
+ UInt32 index = position & (m_Size - 1);
+ UInt32 spaceUntilEnd = m_Size - index;
+ UInt32 continousBytesAvailable = std::min(bytesAvailable, spaceUntilEnd);
+ nBytes = std::min(nBytes, continousBytesAvailable);
+ return &m_Buffer[index];
+}
+
+#endif // RUNTIME_CONTAINERS_RINGBUFFER_H
diff --git a/Runtime/Containers/TransactionalRingbuffer.h b/Runtime/Containers/TransactionalRingbuffer.h
new file mode 100644
index 0000000..503b963
--- /dev/null
+++ b/Runtime/Containers/TransactionalRingbuffer.h
@@ -0,0 +1,37 @@
+#ifndef RUNTIME_CONTAINERS_TRANSACTIONALRINGBUFFER_H
+#define RUNTIME_CONTAINERS_TRANSACTIONALRINGBUFFER_H
+
+#include "Ringbuffer.h"
+
+// --------------------------------------------------------------------
+// Transactional Ringbuffer (commit, reset)
+// Concurrently supports one consumer and one producer
+// --------------------------------------------------------------------
+class TransactionalRingbuffer : private Ringbuffer
+{
+public:
+ TransactionalRingbuffer(void* memory, UInt32 size) : Ringbuffer(memory, size), m_GetBarrier(m_Put), m_PutBarrier(m_Get) {}
+ TransactionalRingbuffer(MemLabelId label, UInt32 size) : Ringbuffer(label, size) , m_GetBarrier(m_Put), m_PutBarrier(m_Get) {}
+
+ using Ringbuffer::WritePtrUpdate;
+ using Ringbuffer::ReadPtrUpdate;
+ using Ringbuffer::GetSize;
+
+ void* WritePtr(UInt32* nBytes) const { return Ptr(m_Put, GetFreeSize(), nBytes); }
+ void WritePtrCommit() { m_GetBarrier = m_Put; }
+ void WritePtrReset() { m_Put = m_GetBarrier; }
+
+ const void* ReadPtr(UInt32* nBytes) const { return Ptr(m_Get, GetAvailableSize(), nBytes); }
+ void ReadPtrCommit() { m_PutBarrier = m_Get; }
+ void ReadPtrReset() { m_Get = m_PutBarrier; }
+
+ UInt32 GetFreeSize() const { return m_Size - (m_Put - m_PutBarrier); }
+ UInt32 GetAvailableSize() const { return m_GetBarrier - m_Get; }
+
+ void Reset() { Ringbuffer::Reset(); m_GetBarrier = m_PutBarrier = 0; }
+
+private:
+ volatile UInt32 m_GetBarrier;
+ volatile UInt32 m_PutBarrier;
+};
+#endif // RUNTIME_CONTAINERS_RINGBUFFER_H \ No newline at end of file
diff --git a/Runtime/Core/Callbacks/CallbackArray.cpp b/Runtime/Core/Callbacks/CallbackArray.cpp
new file mode 100644
index 0000000..adea73f
--- /dev/null
+++ b/Runtime/Core/Callbacks/CallbackArray.cpp
@@ -0,0 +1,40 @@
+#include "UnityPrefix.h"
+#include "CallbackArray.h"
+
+CallbackArray::CallbackArray ()
+{
+ for (int i=0;i<kMaxCallback;i++)
+ m_Callbacks[i] = NULL;
+}
+
+void CallbackArray::Register (SimpleCallback* callback)
+{
+ for (int i=0;i<kMaxCallback;i++)
+ {
+ if (m_Callbacks[i] == NULL)
+ {
+ m_Callbacks[i] = callback;
+ return;
+ }
+ }
+
+ AssertString("Callback registration failed. Not enough space.");
+}
+
+void CallbackArray::Unregister (SimpleCallback* callback)
+{
+ for (int i=0;i<kMaxCallback;i++)
+ {
+ if (m_Callbacks[i] == callback)
+ m_Callbacks[i] = NULL;
+ }
+}
+
+void CallbackArray::Invoke ()
+{
+ for (int i=0;i<kMaxCallback;i++)
+ {
+ if (m_Callbacks[i])
+ m_Callbacks[i] ();
+ }
+} \ No newline at end of file
diff --git a/Runtime/Core/Callbacks/CallbackArray.h b/Runtime/Core/Callbacks/CallbackArray.h
new file mode 100644
index 0000000..904769f
--- /dev/null
+++ b/Runtime/Core/Callbacks/CallbackArray.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "Runtime/Modules/ExportModules.h"
+
+class EXPORT_COREMODULE CallbackArray
+{
+ public:
+
+ typedef void SimpleCallback ();
+
+ CallbackArray ();
+
+ void Register (SimpleCallback* callback);
+ void Unregister (SimpleCallback* callback);
+ void Invoke ();
+
+ private:
+
+ enum { kMaxCallback = 6 };
+ SimpleCallback* m_Callbacks[kMaxCallback];
+};
diff --git a/Runtime/Core/Callbacks/GlobalCallbacks.cpp b/Runtime/Core/Callbacks/GlobalCallbacks.cpp
new file mode 100644
index 0000000..5e9534b
--- /dev/null
+++ b/Runtime/Core/Callbacks/GlobalCallbacks.cpp
@@ -0,0 +1,9 @@
+#include "UnityPrefix.h"
+#include "GlobalCallbacks.h"
+
+static GlobalCallbacks gGlobalCallback;
+
+GlobalCallbacks& GlobalCallbacks::Get()
+{
+ return gGlobalCallback;
+}
diff --git a/Runtime/Core/Callbacks/GlobalCallbacks.h b/Runtime/Core/Callbacks/GlobalCallbacks.h
new file mode 100644
index 0000000..ddfccca
--- /dev/null
+++ b/Runtime/Core/Callbacks/GlobalCallbacks.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "CallbackArray.h"
+#include "Runtime/Modules/ExportModules.h"
+
+struct EXPORT_COREMODULE GlobalCallbacks
+{
+ public:
+
+ // Called after loading the level.
+ // Resets all Random number seeds in the engine after loading a level.
+ CallbackArray resetRandomAfterLevelLoad;
+
+ CallbackArray didReloadMonoDomain;
+ CallbackArray didUnloadScene;
+
+ CallbackArray registerGizmos;
+
+ //only used by hacky audiocode, should be killed.
+ CallbackArray managersWillBeReloadedHack;
+
+ CallbackArray willSaveScene;
+ CallbackArray initializedEngineGraphics;
+
+ CallbackArray initialDomainReloadingComplete;
+
+ static GlobalCallbacks& Get();
+};
+
+#define REGISTER_GLOBAL_CALLBACK(eventName,body) struct eventName { static void Forward () { body; } }; GlobalCallbacks::Get().eventName.Register(eventName::Forward);
diff --git a/Runtime/Core/Callbacks/PlayerLoopCallbacks.cpp b/Runtime/Core/Callbacks/PlayerLoopCallbacks.cpp
new file mode 100644
index 0000000..072a427
--- /dev/null
+++ b/Runtime/Core/Callbacks/PlayerLoopCallbacks.cpp
@@ -0,0 +1,9 @@
+#include "UnityPrefix.h"
+#include "PlayerLoopCallbacks.h"
+
+PlayerLookCallbacks gPlayerLoopCallbacks;
+
+PlayerLookCallbacks::PlayerLookCallbacks ()
+{
+ memset(this, 0, sizeof(PlayerLookCallbacks));
+} \ No newline at end of file
diff --git a/Runtime/Core/Callbacks/PlayerLoopCallbacks.h b/Runtime/Core/Callbacks/PlayerLoopCallbacks.h
new file mode 100644
index 0000000..49dd86e
--- /dev/null
+++ b/Runtime/Core/Callbacks/PlayerLoopCallbacks.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "Runtime/Modules/ExportModules.h"
+
+struct PlayerLookCallbacks
+{
+ typedef void UpdateFunc ();
+
+ PlayerLookCallbacks ();
+
+ // Animators
+ UpdateFunc* AnimatorFixedUpdateRetargetIKWrite;
+ UpdateFunc* AnimatorUpdateRetargetIKWrite;
+ UpdateFunc* AnimatorUpdateFKMove;
+ UpdateFunc* AnimatorFixedUpdateFKMove;
+
+ // Physics
+ UpdateFunc* PhysicsFixedUpdate;
+ UpdateFunc* PhysicsUpdate;
+ UpdateFunc* PhysicsRefreshWhenPaused;
+ UpdateFunc* PhysicsSkinnedClothUpdate;
+ UpdateFunc* PhysicsResetInterpolatedTransformPosition;
+
+ // 2D Physics
+ UpdateFunc* Physics2DUpdate;
+ UpdateFunc* Physics2DFixedUpdate;
+ UpdateFunc* Physics2DResetInterpolatedTransformPosition;
+
+ // Navmesh
+ UpdateFunc* NavMeshUpdate;
+
+ // Legacy Animation
+ UpdateFunc* LegacyFixedAnimationUpdate;
+ UpdateFunc* LegacyAnimationUpdate;
+};
+EXPORT_COREMODULE extern PlayerLookCallbacks gPlayerLoopCallbacks;
+
+#define CALL_UPDATE_MODULAR(x) if (gPlayerLoopCallbacks.x) gPlayerLoopCallbacks.x ();
+#define REGISTER_PLAYERLOOP_CALL(name,body) struct name { static void Forward () { body; } }; gPlayerLoopCallbacks.name = name::Forward;
diff --git a/Runtime/Dynamics/BoxCollider.cpp b/Runtime/Dynamics/BoxCollider.cpp
new file mode 100644
index 0000000..c874f12
--- /dev/null
+++ b/Runtime/Dynamics/BoxCollider.cpp
@@ -0,0 +1,256 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "BoxCollider.h"
+#include "Runtime/Graphics/Transform.h"
+#include "RigidBody.h"
+#include "PhysicsManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "NxWrapperUtility.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+#define GET_SHAPE() static_cast<NxBoxShape*> (m_Shape)
+BoxCollider::BoxCollider (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+BoxCollider::~BoxCollider ()
+{
+}
+
+void BoxCollider::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ if (m_Shape)
+ {
+ // Apply changed values
+ SetSize (m_Size);
+ SetCenter (m_Center);
+ }
+
+ Super::AwakeFromLoad (awakeMode);
+}
+
+void BoxCollider::SmartReset ()
+{
+ Super::SmartReset();
+
+ AABB aabb;
+ if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb))
+ {
+ SetSize (aabb.GetExtent () * 2.0F);
+ SetCenter (aabb.GetCenter ());
+ }
+ else
+ {
+ SetSize (Vector3f::one);
+ SetCenter (Vector3f::zero);
+ }
+}
+
+void BoxCollider::Reset ()
+{
+ Super::Reset ();
+ m_Center = Vector3f::zero;
+ m_Size = Vector3f::one;
+}
+
+Vector3f BoxCollider::GetGlobalExtents () const
+{
+ Vector3f extents = GetComponent (Transform).GetWorldScaleLossy ();
+ extents.Scale (m_Size);
+ extents *= 0.5F;
+ extents = Abs (extents);
+ return extents;
+}
+
+Vector3f BoxCollider::GetGlobalCenter () const
+{
+ return GetComponent (Transform).TransformPoint (m_Center);
+}
+
+void BoxCollider::Create (const Rigidbody* ignoreRigidbody)
+{
+ if (m_Shape)
+ Cleanup ();
+
+ NxBoxShapeDesc shapeDesc;
+ (Vector3f&)shapeDesc.dimensions = GetGlobalExtents ();
+
+ FinalizeCreate (shapeDesc, true, ignoreRigidbody);
+}
+
+void BoxCollider::SetSize (const Vector3f& size)
+{
+ if (size != m_Size)
+ {
+ SetDirty ();
+ m_Size = size;
+ }
+
+ PROFILE_MODIFY_STATIC_COLLIDER
+
+ if (GET_SHAPE ())
+ {
+ GET_SHAPE ()->setDimensions (Vec3ToNx(GetGlobalExtents ()));
+ RigidbodyMassDistributionChanged ();
+ RefreshPhysicsInEditMode();
+ UpdateCCDSkeleton ();
+ }
+}
+
+void BoxCollider::SetCenter (const Vector3f& pos)
+{
+ if (pos != m_Center)
+ {
+ SetDirty ();
+ m_Center = pos;
+ }
+
+ if (GET_SHAPE ())
+ TransformChanged (Transform::kRotationChanged | Transform::kPositionChanged | kForceUpdateMass);
+}
+
+void BoxCollider::ScaleChanged ()
+{
+ PROFILE_MODIFY_STATIC_COLLIDER
+
+ NxBoxShape* shape = GET_SHAPE ();
+ shape->setDimensions (Vec3ToNx(GetGlobalExtents ()));
+
+ UpdateCCDSkeleton ();
+}
+
+void BoxCollider::FetchPoseFromTransform ()
+{
+ FetchPoseFromTransformUtility (m_Center);
+}
+
+bool BoxCollider::GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix)
+{
+ return GetRelativeToParentPositionAndRotationUtility (transform, anyParent, m_Center, matrix);
+}
+
+void BoxCollider::TransformChanged (int changeMask)
+{
+ Super::TransformChanged (changeMask);
+ if (m_Shape)
+ {
+ if (m_Shape->getActor ().userData == NULL)
+ {
+ PROFILER_AUTO(gStaticColliderMove, this)
+ FetchPoseFromTransformUtility (m_Center);
+/*
+ AssertIf (m_Shape == NULL);
+ AssertIf (HasActorRigidbody ());
+ Transform& transform = GetComponent (Transform);
+ Vector3f p = transform.TransformPoint (m_Center);
+ m_Shape->getActor ().setGlobalPosition ((const NxVec3&)p);
+ Matrix3x3f m = transform.GetWorldRotationAndScale ();
+ //OrthoNormalize (m);
+ m_Shape->getActor ().setGlobalOrientation ((NxMat33&)m);
+// m_Shape->getActor ().setGlobalOrientationQuat ((const NxQuat&)transform.GetRotation ());
+*/
+ }
+ else
+ {
+ Rigidbody* body = (Rigidbody*)m_Shape->getActor ().userData;
+ Matrix4x4f matrix;
+ if (GetRelativeToParentPositionAndRotationUtility (GetComponent (Transform), body->GetComponent (Transform), m_Center, matrix))
+ {
+ NxMat34 shapeMatrix;
+ shapeMatrix.setColumnMajor44 (matrix.GetPtr ());
+ m_Shape->setLocalPose (shapeMatrix);
+ }
+
+ if (body->GetGameObjectPtr() != GetGameObjectPtr() || changeMask & (Transform::kScaleChanged | kForceUpdateMass))
+ RigidbodyMassDistributionChanged ();
+ }
+
+ if (changeMask & Transform::kScaleChanged)
+ ScaleChanged ();
+
+ RefreshPhysicsInEditMode();
+ }
+}
+
+NxCCDSkeleton* BoxCollider::CreateCCDSkeleton(float scale)
+{
+ Vector3f size = Vector3f::one * scale;
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ size.Scale(GetGlobalExtents());
+ else
+ // Prior to 4.0 we incorrectly ignored object scale here. Keep this behaviour for backwards compatibility.
+ size.Scale(m_Size * 0.5f);
+
+ NxU32 triangles[3 * 12] = {
+ 0,1,3,
+ 0,3,2,
+ 3,7,6,
+ 3,6,2,
+ 1,5,7,
+ 1,7,3,
+ 4,6,7,
+ 4,7,5,
+ 1,0,4,
+ 5,1,4,
+ 4,0,2,
+ 4,2,6
+ };
+
+ NxVec3 points[8];
+ // Static mesh
+ points[0].set( size.x, -size.y, -size.z);
+ points[1].set( size.x, -size.y, size.z);
+ points[2].set( size.x, size.y, -size.z);
+ points[3].set( size.x, size.y, size.z);
+
+ points[4].set(-size.x, -size.y, -size.z);
+ points[5].set(-size.x, -size.y, size.z);
+ points[6].set(-size.x, size.y, -size.z);
+ points[7].set(-size.x, size.y, size.z);
+
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ {
+ // This is wrong, as the m_Center transformation is already applied at the shape matrix.
+ // Keep for backward compatibility.
+ NxVec3 center(m_Center.x, m_Center.y, m_Center.z);
+ for (int i=0;i<8;i++)
+ points[i] += center;
+ }
+
+ NxSimpleTriangleMesh stm;
+ stm.numVertices = 8;
+ stm.numTriangles = 6*2;
+ stm.pointStrideBytes = sizeof(NxVec3);
+ stm.triangleStrideBytes = sizeof(NxU32)*3;
+
+ stm.points = points;
+ stm.triangles = triangles;
+ return GetDynamicsSDK().createCCDSkeleton(stm);
+}
+
+template<class TransferFunction>
+void BoxCollider::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion (2);
+ transfer.Align();
+ if (transfer.IsCurrentVersion ())
+ {
+ TRANSFER_SIMPLE (m_Size);
+ }
+ else
+ {
+ transfer.Transfer (m_Size, "m_Extents");
+ m_Size*=2.0F;
+ }
+ TRANSFER (m_Center);
+}
+
+IMPLEMENT_CLASS (BoxCollider)
+IMPLEMENT_OBJECT_SERIALIZE (BoxCollider)
+
+#undef GET_SHAPE
+#endif //ENABLE_PHYSICS \ No newline at end of file
diff --git a/Runtime/Dynamics/BoxCollider.h b/Runtime/Dynamics/BoxCollider.h
new file mode 100644
index 0000000..078467a
--- /dev/null
+++ b/Runtime/Dynamics/BoxCollider.h
@@ -0,0 +1,45 @@
+#ifndef BOXCOLLIDER_H
+#define BOXCOLLIDER_H
+
+#include "Collider.h"
+#include "Runtime/Math/Vector3.h"
+
+class BoxCollider : public Collider
+{
+ public:
+ REGISTER_DERIVED_CLASS (BoxCollider, Collider)
+ DECLARE_OBJECT_SERIALIZE (BoxCollider)
+
+ BoxCollider (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void Reset ();
+ virtual void SmartReset ();
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ const Vector3f& GetSize () const { return m_Size; }
+ void SetSize (const Vector3f& extents);
+
+ const Vector3f& GetCenter () const { return m_Center; }
+ void SetCenter (const Vector3f& pos);
+
+ Vector3f GetGlobalExtents () const;
+ Vector3f GetGlobalCenter () const;
+
+ virtual void TransformChanged (int changeMask);
+
+ protected:
+
+ virtual void FetchPoseFromTransform ();
+ virtual bool GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix);
+
+
+ virtual void Create (const Rigidbody* ignoreRigidbody);
+ virtual void ScaleChanged ();
+
+ virtual NxCCDSkeleton* CreateCCDSkeleton(float scale);
+
+ Vector3f m_Center;
+ Vector3f m_Size;
+};
+
+#endif
diff --git a/Runtime/Dynamics/CapsuleCollider.cpp b/Runtime/Dynamics/CapsuleCollider.cpp
new file mode 100644
index 0000000..acfadde
--- /dev/null
+++ b/Runtime/Dynamics/CapsuleCollider.cpp
@@ -0,0 +1,369 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "CapsuleCollider.h"
+#include "Runtime/Graphics/Transform.h"
+#include "RigidBody.h"
+#include "PhysicsManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+#define GET_SHAPE() ((class NxCapsuleShape*)m_Shape)
+
+/*
+ - i am not sure about the getscaled extents calculation.
+
+*/
+
+using namespace std;
+
+CapsuleCollider::CapsuleCollider (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+CapsuleCollider::~CapsuleCollider ()
+{
+}
+
+void CapsuleCollider::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ if (m_Shape)
+ {
+ // Apply changed values
+ SetRadius(m_Radius);
+ SetHeight(m_Height);
+ SetCenter(m_Center);
+ SetDirection (m_Direction);
+ }
+
+ Super::AwakeFromLoad (awakeMode);
+}
+
+void CapsuleCollider::SmartReset ()
+{
+ Super::SmartReset();
+ AABB aabb;
+ if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb))
+ {
+ Vector3f extents = aabb.GetExtent ();
+ SetRadius (max (extents.x, extents.z));
+ SetHeight (extents.y * 2.0F);
+ SetCenter (aabb.GetCenter ());
+ }
+ else
+ {
+ SetRadius (0.5F);
+ SetHeight (1.0F);
+ SetCenter (Vector3f::zero);
+ }
+}
+
+void CapsuleCollider::Reset ()
+{
+ Super::Reset ();
+ m_Radius = 0.5F;
+ m_Height = 1.0F;
+ m_Center = Vector3f::zero;
+ m_Direction = 1;
+}
+
+
+Vector2f CapsuleCollider::GetGlobalExtents () const
+{
+ const float kMinSize = 0.00001F;
+ Vector3f scale = GetComponent (Transform).GetWorldScaleLossy ();
+
+ float absoluteHeight = max (Abs (m_Height * scale.y), kMinSize);
+ float absoluteRadius = max (Abs (scale.x), Abs (scale.z)) * m_Radius;
+
+ float height = absoluteHeight - absoluteRadius * 2.0F;
+
+ height = max (height, kMinSize);
+ absoluteRadius = max (absoluteRadius, kMinSize);
+
+ return Vector2f (absoluteRadius, height);
+}
+
+Vector3f CapsuleCollider::GetGlobalCenter () const
+{
+ return GetComponent (Transform).TransformPoint (m_Center);
+}
+
+AABB CapsuleCollider::GetBounds ()
+{
+ if (m_Shape)
+ {
+ // AABB reported by PhysX is inaccurate, as PhysX will just transform the local AABB.
+ // For Capsules it's very easy to do better.
+
+ Vector2f extents = GetGlobalExtents();
+ Matrix4x4f m = CalculateTransform ();
+
+ Vector3f center1 = m.MultiplyPoint3 (Vector3f(0, extents.y * 0.5, 0));
+ Vector3f center2 = m.MultiplyPoint3 (Vector3f(0, -extents.y * 0.5, 0));
+
+ // Make AABB of both global centers
+ AABB aabb (center1, Vector3f::zero);
+ aabb.Encapsulate (center2);
+
+ // Expand by global radius
+ aabb.m_Extent += Vector3f(extents.x, extents.x, extents.x);
+ return aabb;
+ }
+ else
+ return Super::GetBounds ();
+}
+
+void CapsuleCollider::Create (const Rigidbody* ignoreRigidbody)
+{
+ if (m_Shape)
+ Cleanup ();
+
+ NxCapsuleShapeDesc shapeDesc;
+ Vector2f extents = GetGlobalExtents ();
+ shapeDesc.radius = extents.x;
+ shapeDesc.height = extents.y;
+
+ FinalizeCreate (shapeDesc, true, ignoreRigidbody);
+}
+
+void CapsuleCollider::SetRadius (float radius)
+{
+ if (m_Radius != radius)
+ {
+ SetDirty ();
+ m_Radius = radius;
+ }
+
+ PROFILE_MODIFY_STATIC_COLLIDER
+
+ if (GET_SHAPE ())
+ {
+ GET_SHAPE ()->setRadius (GetGlobalExtents ().x);
+ RigidbodyMassDistributionChanged ();
+ RefreshPhysicsInEditMode();
+ UpdateCCDSkeleton ();
+ }
+}
+
+void CapsuleCollider::SetHeight (float height)
+{
+ if (m_Height != height)
+ {
+ SetDirty ();
+ m_Height = height;
+ }
+
+ PROFILE_MODIFY_STATIC_COLLIDER
+
+ if (GET_SHAPE ())
+ {
+ GET_SHAPE ()->setHeight (GetGlobalExtents ().y);
+ RigidbodyMassDistributionChanged ();
+ RefreshPhysicsInEditMode();
+ UpdateCCDSkeleton ();
+ }
+}
+
+void CapsuleCollider::SetCenter (const Vector3f& center)
+{
+ if (m_Center != center)
+ {
+ m_Center = center;
+ SetDirty ();
+ }
+
+ if (GET_SHAPE ())
+ {
+ TransformChanged (Transform::kRotationChanged | Transform::kPositionChanged | kForceUpdateMass);
+ RefreshPhysicsInEditMode();
+ }
+}
+
+void CapsuleCollider::ScaleChanged ()
+{
+ NxCapsuleShape* shape = GET_SHAPE ();
+ Vector2f extents = GetGlobalExtents ();
+ shape->setRadius (extents.x);
+ shape->setHeight (extents.y);
+
+ UpdateCCDSkeleton ();
+
+ PROFILE_MODIFY_STATIC_COLLIDER
+}
+
+void CapsuleCollider::SetDirection (int dir)
+{
+ if (m_Direction != dir)
+ {
+ SetDirty ();
+ m_Direction = dir;
+ }
+
+ TransformChanged (kForceUpdateMass);
+}
+
+Matrix4x4f CapsuleCollider::CalculateTransform () const
+{
+ Transform& transform = GetComponent (Transform);
+ Vector3f p = transform.TransformPoint (m_Center);
+
+ Quaternionf rotation = transform.GetRotation ();
+ if (m_Direction == 2)
+ rotation *= AxisAngleToQuaternion (Vector3f::xAxis, Deg2Rad (90));
+ else if (m_Direction == 0)
+ rotation *= AxisAngleToQuaternion (Vector3f::zAxis, Deg2Rad (90));
+ else
+ rotation *= AxisAngleToQuaternion (Vector3f::xAxis, Deg2Rad (180));
+
+ Matrix4x4f matrix;
+ matrix.SetTR (p, rotation);
+
+ return matrix;
+}
+
+void CapsuleCollider::FetchPoseFromTransform ()
+{
+ AssertIf (HasActorRigidbody ());
+
+ Transform& transform = GetComponent (Transform);
+ Vector3f p = transform.TransformPoint (m_Center);
+ m_Shape->getActor ().setGlobalPosition ((const NxVec3&)p);
+
+ Quaternionf rotation = transform.GetRotation ();
+ if (m_Direction == 2)
+ rotation *= AxisAngleToQuaternion (Vector3f::xAxis, Deg2Rad (90));
+ else if (m_Direction == 0)
+ rotation *= AxisAngleToQuaternion (Vector3f::zAxis, Deg2Rad (90));
+ else
+ rotation *= AxisAngleToQuaternion (Vector3f::xAxis, Deg2Rad (180));
+
+ m_Shape->getActor ().setGlobalOrientationQuat ((const NxQuat&)rotation);
+}
+
+bool CapsuleCollider::GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix)
+{
+ Matrix4x4f childMatrix = CalculateTransform ();
+ Matrix4x4f parentMatrix = anyParent.GetWorldToLocalMatrixNoScale ();
+ MultiplyMatrices4x4 (&parentMatrix, &childMatrix, &matrix);
+ ErrorFiniteParameterReturnFalse(matrix)
+ return true;
+}
+
+void CapsuleCollider::TransformChanged (int changeMask)
+{
+ Super::TransformChanged (changeMask);
+ if (m_Shape)
+ {
+ if (m_Shape->getActor ().userData == NULL)
+ {
+ PROFILER_AUTO(gStaticColliderMove, this)
+ FetchPoseFromTransform ();
+ }
+ else
+ {
+ Rigidbody* body = (Rigidbody*)m_Shape->getActor ().userData;
+ Matrix4x4f matrix;
+ if (GetRelativeToParentPositionAndRotation (GetComponent (Transform), body->GetComponent (Transform), matrix))
+ {
+ NxMat34 shapeMatrix;
+ shapeMatrix.setColumnMajor44 (matrix.GetPtr ());
+ m_Shape->setLocalPose (shapeMatrix);
+ }
+
+ if (body->GetGameObjectPtr() != GetGameObjectPtr() || changeMask & (Transform::kScaleChanged | kForceUpdateMass))
+ RigidbodyMassDistributionChanged ();
+ }
+
+ if (changeMask & Transform::kScaleChanged)
+ ScaleChanged ();
+
+ RefreshPhysicsInEditMode();
+ }
+}
+
+NxCCDSkeleton* CapsuleCollider::CreateCCDSkeleton(float scale)
+{
+ // This is a very simple "approximation" of a capuse, of only 10 vertices.
+ // Since CCD only kicks in when normal collisions fail, any is probably mostly
+ // interesting for small objects (as those tend to move faster), I believe that this
+ // is a reasonable choice for common expected use. NVidia even recommends using
+ // single vertex meshes for very small objects.
+ float radius;
+ float height;
+
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ {
+ Vector2f extents = GetGlobalExtents ();
+ radius = extents.x * scale;
+ height = extents.y * scale;
+ }
+ else
+ {
+ // Prior to 4.0 we incorrectly ignored object scale here. Keep this behaviour for backwards compatibility.
+ radius = m_Radius * scale;
+ height = m_Height * 0.5f * scale;
+ }
+
+ NxU32 triangles[3 * 16] = {
+ 0,1,2,
+ 0,2,3,
+ 0,3,4,
+ 0,4,1,
+
+ 1,5,6,
+ 1,6,2,
+ 2,6,7,
+ 2,7,3,
+ 3,7,8,
+ 3,8,4,
+ 4,8,5,
+ 4,5,1,
+
+ 9,6,5,
+ 9,7,6,
+ 9,8,7,
+ 9,5,8,
+ };
+
+ Vector3f points[10];
+ points[0].Set( 0, -radius - height, 0);
+ points[1].Set( -radius, -height, 0);
+ points[2].Set( 0, -height, -radius);
+ points[3].Set( radius, -height, 0);
+ points[4].Set( 0, -height, radius);
+ points[5].Set( -radius, height, 0);
+ points[6].Set( 0, height, -radius);
+ points[7].Set( radius, height, 0);
+ points[8].Set( 0, height, radius);
+ points[9].Set( 0, radius + height, 0);
+
+ NxSimpleTriangleMesh stm;
+ stm.numVertices = 10;
+ stm.numTriangles = 16;
+ stm.pointStrideBytes = sizeof(Vector3f);
+ stm.triangleStrideBytes = sizeof(NxU32)*3;
+
+ stm.points = points;
+ stm.triangles = triangles;
+ return GetDynamicsSDK().createCCDSkeleton(stm);
+}
+
+template<class TransferFunction>
+void CapsuleCollider::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.Align();
+ TRANSFER_SIMPLE (m_Radius);
+ TRANSFER_SIMPLE (m_Height);
+ TRANSFER (m_Direction);
+ TRANSFER (m_Center);
+}
+
+IMPLEMENT_CLASS (CapsuleCollider)
+IMPLEMENT_OBJECT_SERIALIZE (CapsuleCollider)
+
+#undef GET_SHAPE
+#endif //ENABLE_PHYSICS \ No newline at end of file
diff --git a/Runtime/Dynamics/CapsuleCollider.h b/Runtime/Dynamics/CapsuleCollider.h
new file mode 100644
index 0000000..9f579bf
--- /dev/null
+++ b/Runtime/Dynamics/CapsuleCollider.h
@@ -0,0 +1,59 @@
+#ifndef CAPSULECOLLIDER_H
+#define CAPSULECOLLIDER_H
+
+#include "Collider.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Vector2.h"
+
+class CapsuleCollider : public Collider
+{
+ public:
+ REGISTER_DERIVED_CLASS (CapsuleCollider, Collider)
+ DECLARE_OBJECT_SERIALIZE (CapsuleCollider)
+
+ CapsuleCollider (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void Reset ();
+ virtual void SmartReset ();
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ void SetRadius (float radius);
+ float GetRadius () const { return m_Radius; }
+
+ float GetHeight () const { return m_Height; }
+ void SetHeight (float height);
+
+ Vector3f GetCenter () const { return m_Center; }
+ void SetCenter (const Vector3f& center);
+
+ int GetDirection () const { return m_Direction; }
+ void SetDirection (int dir);
+
+ Vector2f GetGlobalExtents () const;
+ Vector3f GetGlobalCenter () const;
+
+ virtual void TransformChanged (int changeMask);
+
+ Matrix4x4f CalculateTransform () const;
+
+ virtual AABB GetBounds ();
+
+ protected:
+
+
+ virtual void FetchPoseFromTransform ();
+ virtual bool GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix);
+
+ virtual void Create (const Rigidbody* ignoreRigidbody);
+ void ScaleChanged ();
+
+ virtual NxCCDSkeleton* CreateCCDSkeleton(float scale);
+
+ float m_Radius;///< range { 0, infinity }
+ float m_Height;///< range { 0, infinity }
+ int m_Direction;///< enum { X-Axis = 0, Y-Axis = 1, Z-Axis = 2 }
+
+ Vector3f m_Center;
+};
+
+#endif
diff --git a/Runtime/Dynamics/CharacterController.cpp b/Runtime/Dynamics/CharacterController.cpp
new file mode 100644
index 0000000..a1ecf20
--- /dev/null
+++ b/Runtime/Dynamics/CharacterController.cpp
@@ -0,0 +1,567 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#if ENABLE_PHYSICS
+#include "../Dynamics/CharacterController.h"
+#include "External/PhysX/builds/SDKs/NxCharacter/include/NxController.h"
+#include "External/PhysX/builds/SDKs/NxCharacter/include/Controller.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "External/PhysX/builds/SDKs/NxCharacter/include/NxCapsuleController.h"
+#include "External/PhysX/builds/SDKs/NxCharacter/include/ControllerManager.h"
+#include "PhysicsManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Collider.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "NxWrapperUtility.h"
+#include "Runtime/BaseClasses/MessageHandler.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/GameCode/RootMotionData.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Scripting/Scripting.h"
+
+using namespace std;
+
+#include "Runtime/Mono/MonoBehaviour.h"
+
+///@TODO: Take advantage of sweep caching by storing when static objects colliders
+/// move and only in that case reset the sweep cache.
+
+///@TODO: DO A PROPER SendMessage for the character controller
+
+inline CharacterController* GetUnityController(NxController* controller)
+{
+ return (CharacterController*)((Controller*)controller->getUserData());
+}
+
+struct ControllerHitReport : NxUserControllerHitReport
+{
+ struct RecordedControllerColliderHit
+ {
+ Collider* collider;
+ Vector3f point;
+ Vector3f normal;
+ Vector3f motionDirection;
+ float motionLength;
+ };
+
+ typedef std::vector<RecordedControllerColliderHit> RecordedControllerColliderHits;
+ RecordedControllerColliderHits m_Record;
+
+ virtual NxControllerAction onShapeHit(const NxControllerShapeHit& hit)
+ {
+ CharacterController* controller = GetUnityController(hit.controller);
+ GameObject* go = controller->GetGameObjectPtr ();
+
+ // Do not allocate the mono message if the game object is not interested, to keep GC down.
+ // See Case #366569.
+ if (go && go->WillHandleMessage(kControllerColliderHit))
+ {
+ m_Record.push_back(RecordedControllerColliderHit());
+ RecordedControllerColliderHit& controllerHit = m_Record.back();
+
+ controllerHit.point = NxExtendedToVec3(hit.worldPos);
+ controllerHit.normal = (const Vector3f&)hit.worldNormal;
+ controllerHit.motionDirection = (const Vector3f&)hit.dir;
+ controllerHit.motionLength = hit.length;
+ controllerHit.collider = (Collider*)hit.shape->userData;
+ }
+ return NX_ACTION_NONE;
+ }
+
+ virtual NxControllerAction onControllerHit(const NxControllersHit& hit)
+ {
+ return NX_ACTION_NONE;
+ }
+};
+
+
+#if ENABLE_PHYSICS
+static ControllerHitReport gControllerHitReport;
+// This needs to be a pointer, so we can control initialization and destruction order.
+static ControllerManager *gControllerManager = NULL;
+#endif
+
+CharacterController::CharacterController(MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode)
+{
+ m_Controller = NULL;
+ m_VerticalSpeed = 0.0F;
+ m_LastCollisionFlags = 0;
+ m_Velocity = Vector3f(0,0,0);
+ m_LastSimpleVelocity = Vector3f(0,0,0);
+ m_DetectCollision = true;
+}
+
+CharacterController::~CharacterController ()
+{
+}
+
+void CharacterController::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ if(IsActive())
+ Create(NULL);
+
+ Super::AwakeFromLoad (awakeMode);
+}
+
+void CharacterController::SmartReset()
+{
+ Super::SmartReset();
+ AABB aabb;
+ if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb))
+ {
+ Vector3f extents = aabb.GetCenter () + aabb.GetExtent ();
+ SetRadius (max (extents.x, extents.z));
+ SetHeight (extents.y * 2.0F);
+ }
+ else
+ {
+ SetRadius (0.5F);
+ SetHeight (2.0F);
+ }
+}
+
+
+void CharacterController::Reset()
+{
+ Super::Reset();
+
+ m_Center = Vector3f::zero;
+ m_Radius = 0.5F;
+ m_Height = 2.0F;
+
+ m_MinMoveDistance = .00F;
+ m_SkinWidth = max(GetRadius() * 0.16F, 0.01F);
+ m_StepOffset = 0.3f;
+ m_Height = 2.0F;
+ m_Radius = 0.3f;
+ m_SlopeLimit = 45.0F;
+}
+
+int CharacterController::Move (const Vector3f& movement)
+{
+ if( !m_Controller )
+ {
+ ScriptWarning("CharacterController.Move called on inactive controller", GetGameObjectPtr());
+ return 0;
+ }
+ unsigned int touching = 0;
+ Vector3f oldPos = NxExtendedToVec3(m_Controller->getDebugPosition());
+
+ m_Controller->reportSceneChanged();
+
+ Assert(gControllerHitReport.m_Record.empty());
+
+ m_Controller->move((const NxVec3&)movement, GetPhysicsManager().GetLayerCollisionMask(GetGameObject ().GetLayer ()), m_MinMoveDistance, touching, 1.0F);
+
+ bool oldDisableDestruction = GetDisableImmediateDestruction();
+ SetDisableImmediateDestruction (true);
+
+ // Swap hits to get consistent callbacks
+ // CharacterController might call CharacterController.Move from inside a OnControllerColliderHit callback.
+ ControllerHitReport::RecordedControllerColliderHits hits;
+ hits.swap(gControllerHitReport.m_Record);
+ Assert(gControllerHitReport.m_Record.empty());
+
+ for (ControllerHitReport::RecordedControllerColliderHits::iterator i = hits.begin(); i != hits.end(); i++)
+ {
+ #if ENABLE_SCRIPTING
+ const ControllerHitReport::RecordedControllerColliderHit& recordedHit = *i;
+
+ ControllerColliderHit controllerHit;
+
+ controllerHit.point = recordedHit.point;
+ controllerHit.normal = recordedHit.normal;
+ controllerHit.motionDirection = recordedHit.motionDirection;
+ controllerHit.motionLength = recordedHit.motionLength;
+ controllerHit.controller = Scripting::ScriptingWrapperFor(this);
+ controllerHit.collider = Scripting::ScriptingWrapperFor(recordedHit.collider);
+ controllerHit.push = false;
+
+ ScriptingObjectPtr mono = CreateScriptingObjectFromNativeStruct(GetScriptingManager ().GetCommonClasses ().controllerColliderHit, controllerHit);
+
+ MessageData data;
+ data.SetScriptingObjectData(mono);
+
+ SendMessageAny (kControllerColliderHit, data);
+ #endif
+
+ if (!m_Controller)
+ {
+ SetDisableImmediateDestruction (oldDisableDestruction);
+ return touching;
+ }
+ }
+
+ SetDisableImmediateDestruction (oldDisableDestruction);
+
+ m_LastCollisionFlags = touching;
+
+ // Stop gravity when touching ground
+ if ((touching & NXCC_COLLISION_DOWN) && m_VerticalSpeed < 0.0F)
+ m_VerticalSpeed = 0.0F;
+
+ // At least sync the actual position. Update controller will then reset the position to the filtered position!
+ Transform& transform = GetComponent(Transform);
+ Vector3f pos = NxExtendedToVec3(m_Controller->getFilteredPosition());
+
+ m_Velocity = (pos - oldPos) * GetInvDeltaTime();
+
+ GameObject::GetMessageHandler ().SetMessageEnabled (ClassID(CharacterController), kTransformChanged.messageID, false);
+ transform.SetPositionWithLocalOffset (pos, m_Center);
+ GameObject::GetMessageHandler ().SetMessageEnabled (ClassID(CharacterController), kTransformChanged.messageID, true);
+
+ return touching;
+}
+
+bool CharacterController::IsGrounded ()
+{
+ return m_LastCollisionFlags & NXCC_COLLISION_DOWN;
+}
+
+Vector3f CharacterController::GetVelocity()
+{
+ return m_Velocity;
+}
+
+void CharacterController::Create (const Rigidbody* ignoreAttachRigidbody)
+{
+ NxCapsuleControllerDesc desc;
+ desc.slopeLimit = Cos (Deg2Rad (m_SlopeLimit));
+
+ if(desc.slopeLimit<0.0f)
+ desc.slopeLimit=0.0f;
+
+ desc.skinWidth = m_SkinWidth;
+ desc.stepOffset = m_StepOffset;
+ desc.userData = this;
+ Vector2f extents = GetGlobalExtents ();
+ desc.radius = extents.x;
+ desc.height = extents.y;
+ desc.interactionFlag = NXIF_INTERACTION_USE_FILTER;
+
+ desc.callback = &gControllerHitReport;
+ desc.position = Vec3ToNxExtended(GetWorldCenterPosition());
+ if (m_Controller)
+ {
+ gControllerManager->releaseController(*m_Controller);
+ }
+
+ m_Controller = (NxCapsuleController*)gControllerManager->createController(&GetDynamicsScene(), desc);
+ m_Shape = m_Controller->getActor()->getShapes()[0];
+ m_Shape->userData = this;
+ m_Shape->setGroup (GetGameObject ().GetLayer ());
+ m_Controller->setCollision(m_DetectCollision);
+
+// printf_console ("Create shape %d userData: %d (%s, %s)\n", m_Shape, m_Shape->userData, GetName().c_str(), GetClassName().c_str());
+
+}
+
+Vector3f CharacterController::GetWorldCenterPosition() const
+{
+ const Transform& transform = GetComponent(Transform);
+ return transform.TransformPoint (m_Center);
+
+}
+
+AABB CharacterController::GetBounds ()
+{
+ if (m_Shape)
+ {
+ // AABB reported by PhysX is inaccurate, as PhysX will just transform the local AABB.
+ // For Spheres it's very easy to do better. Also needed for Editor selection bounds,
+ // as PhysX only updates Character Controller bounds in play mode.
+ Transform& transform = GetComponent (Transform);
+ Vector3f p = transform.TransformPoint (m_Center);
+
+ Vector2f extents = GetGlobalExtents();
+
+ Vector3f center1 = p + Vector3f(0, extents.y * 0.5, 0);
+ Vector3f center2 = p + Vector3f(0, -extents.y * 0.5, 0);
+
+ // Make AABB of both global centers
+ AABB aabb (center1, Vector3f::zero);
+ aabb.Encapsulate (center2);
+
+ // Expand by global radius
+ aabb.m_Extent += Vector3f(extents.x, extents.x, extents.x);
+ return aabb;
+ }
+ else
+ return Super::GetBounds ();
+}
+
+void CharacterController::Cleanup ()
+{
+ if (m_Controller)
+ {
+ gControllerManager->releaseController(*m_Controller);
+ m_Controller = NULL;
+ m_Shape = NULL;
+ }
+}
+
+Vector2f CharacterController::GetGlobalExtents () const
+{
+ const float kMinSize = 0.00001F;
+
+ Vector3f scale = GetComponent (Transform).GetWorldScaleLossy ();
+
+ float absoluteHeight = max (Abs (m_Height * scale.y), kMinSize);
+ float absoluteRadius = max (Abs (scale.x), Abs (scale.z)) * m_Radius;
+
+ float height = absoluteHeight - absoluteRadius * 2.0F;
+
+ height = max (height, kMinSize);
+ absoluteRadius = max (absoluteRadius, kMinSize);
+
+ return Vector2f (absoluteRadius, height);
+}
+
+void CharacterController::TransformChanged(int change)
+{
+ // Do not call Super::TransformChanged (change) here. The Controller base class
+ // uses transformChanged to check for reparenting to a new rigidbody. In the CharacterController
+ // case there is no rigibody attachment, which causes it to always reparent, breaking
+ // trigger interactions.
+
+ if (m_Controller == NULL)
+ return;
+
+ if (change & Transform::kScaleChanged)
+ {
+ Vector2f extents = GetGlobalExtents ();
+ m_Controller->setRadius(extents.x);
+ m_Controller->setHeight(extents.y);
+ }
+
+ // Teleport
+ if (change & Transform::kPositionChanged)
+ {
+ m_Controller->setPosition (Vec3ToNxExtended(GetWorldCenterPosition()));
+ m_VerticalSpeed = 0.0F;
+ }
+}
+
+template<class TransferFunction>
+void CharacterController::Transfer(TransferFunction& transfer)
+{
+ Super::Super::Transfer (transfer);
+
+ transfer.SetVersion (2);
+
+ TRANSFER_SIMPLE (m_Height);
+ TRANSFER_SIMPLE (m_Radius);
+ TRANSFER_SIMPLE (m_SlopeLimit);
+ TRANSFER_SIMPLE (m_StepOffset);
+ TRANSFER_SIMPLE (m_SkinWidth);
+
+ TRANSFER (m_MinMoveDistance);
+ TRANSFER (m_Center);
+
+ if (transfer.IsVersionSmallerOrEqual (1))
+ {
+ // The PhysX character controller has been fixed so it works properly.
+ // Before the fix, the character controller was unable to climb any
+ // wall above 45 degress, regardless of the slope limit setting.
+ // The slope limit is therefore clamped to 45 degrees when loading old
+ // projects to mimic the old behaviour.
+ m_SlopeLimit = std::min (45.0F, m_SlopeLimit);
+ }
+}
+
+void CharacterController::ScaleChanged()
+{
+ if (m_Controller)
+ {
+ Vector2f extents = GetGlobalExtents();
+ m_Controller->setRadius(extents.x);
+ m_Controller->setHeight(extents.y);
+ }
+}
+
+void CharacterController::SetRadius(float radius)
+{
+ m_Radius = radius;
+ SetDirty();
+ if (m_Controller)
+ {
+ Vector2f extents = GetGlobalExtents();
+ m_Controller->setRadius(extents.x);
+ m_Controller->setHeight(extents.y);
+ }
+}
+
+void CharacterController::SetHeight(float height)
+{
+ m_Height = height;
+ SetDirty();
+ if (m_Controller)
+ {
+ Vector2f extents = GetGlobalExtents();
+ m_Controller->setRadius(extents.x);
+ m_Controller->setHeight(extents.y);
+ }
+}
+
+bool CharacterController::SimpleMove (const Vector3f& speed)
+{
+ float dt = GetDeltaTime();
+
+ m_VerticalSpeed += GetPhysicsManager().GetGravity().y * dt;
+ Vector3f offset;
+
+ if (IsGrounded())
+ {
+ offset = Vector3f(speed.x, m_VerticalSpeed, speed.z);
+ m_LastSimpleVelocity = offset;
+ }
+ else
+ offset = Vector3f(m_LastSimpleVelocity.x, m_VerticalSpeed, m_LastSimpleVelocity.z);
+
+ offset *= dt;
+ Move(offset);
+
+ return IsGrounded();
+}
+
+void CharacterController::ApplyRootMotionBuiltin (RootMotionData* rootMotion)
+{
+ if(!GetEnabled())
+ return;
+
+ float deltaTime = GetDeltaTime();
+
+ // Get the Y velocity according to rigidbody or own Y speed for CharacterController
+ m_VerticalSpeed += GetPhysicsManager().GetGravity().y * deltaTime;
+
+ // Get the velocity from root motion.
+ // Blend physics velocity with animation velocity on y-axis
+ Vector3f deltaMotion;
+ deltaMotion = rootMotion->deltaPosition;
+ deltaMotion.y = Lerp (deltaMotion.y, m_VerticalSpeed * deltaTime, clamp01(rootMotion->gravityWeight));
+
+ // Apply velocity and rotation
+ Move (deltaMotion);
+
+ GetComponent(Transform).SetRotation(rootMotion->targetRotation);
+
+ rootMotion->didApply = true;
+}
+
+void CharacterController::InitializeClass ()
+{
+ REGISTER_MESSAGE_PTR (CharacterController, kAnimatorMoveBuiltin, ApplyRootMotionBuiltin, RootMotionData);
+}
+
+void CharacterController::CleanupClass ()
+{
+}
+
+
+/*
+Jumpheight equation:
+v = -(currentVelocity + sqrt(2 * h * g));
+*/
+/*
+bool CharacterController::SimpleJump (const Vector3f& movement, float jumpHeight)
+{
+ float dt = GetDeltaTime();
+
+ bool didJump = IsGrounded();
+ if (didJump)
+ {
+// height = 0.5 * Sqr(GetPhysicsManager().GetGravity().y) + velocity;
+ m_LastCollisionFlags &= ~NXCC_COLLISION_DOWN;
+
+// 0 = g * t + velocity
+
+// velocity * time + 0.5 * GetPhysicsManager().GetGravity().y * t ^ 2
+ }
+
+ SimpleMove(movement);
+
+ if (didJump)
+ m_LastCollisionFlags &= ~NXCC_COLLISION_DOWN;
+
+ return didJump;
+}
+*/
+
+float CharacterController::GetSlopeLimit ()
+{
+ return m_SlopeLimit;
+}
+
+void CharacterController::SetSlopeLimit (float limit)
+{
+ m_SlopeLimit = limit;
+ SetDirty();
+ if (m_Controller)
+ Create (NULL);
+}
+
+float CharacterController::GetStepOffset ()
+{
+ return m_StepOffset;
+}
+
+void CharacterController::SetStepOffset (float limit)
+{
+ m_StepOffset = limit;
+ SetDirty();
+ if (m_Controller)
+ m_Controller->setStepOffset(limit);
+}
+
+Vector3f CharacterController::GetCenter ()
+{
+ return m_Center;
+}
+
+void CharacterController::SetCenter(const Vector3f& center)
+{
+ m_Center = center;
+ if (m_Controller)
+ Create(NULL);
+ SetDirty();
+}
+
+void CharacterController::SetDetectCollisions (bool detect)
+{
+ m_DetectCollision = detect;
+ if (m_Controller)
+ m_Controller->setCollision(m_DetectCollision);
+}
+
+void CharacterController::SetIsTrigger (bool trigger)
+{
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1))
+ {
+ if (trigger)
+ ErrorStringObject ("A Character Controller cannot be a trigger.", this);
+ m_IsTrigger = false;
+ }
+ else
+ Super::SetIsTrigger (trigger);
+}
+
+
+void CharacterController::CreateControllerManager ()
+{
+ Assert (gControllerManager == NULL);
+ gControllerManager = new ControllerManager();
+}
+
+void CharacterController::CleanupControllerManager ()
+{
+ Assert (gControllerManager != NULL);
+ delete gControllerManager;
+}
+
+IMPLEMENT_CLASS_HAS_INIT(CharacterController)
+IMPLEMENT_OBJECT_SERIALIZE(CharacterController)
+
+#endif
diff --git a/Runtime/Dynamics/CharacterController.h b/Runtime/Dynamics/CharacterController.h
new file mode 100644
index 0000000..d069c7d
--- /dev/null
+++ b/Runtime/Dynamics/CharacterController.h
@@ -0,0 +1,112 @@
+#pragma once
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Collider.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Vector2.h"
+
+class NxCapsuleController;
+struct MonoObject;
+struct RootMotionData;
+
+class CharacterController : public Collider
+{
+ public:
+
+ REGISTER_DERIVED_CLASS(CharacterController, Collider)
+ DECLARE_OBJECT_SERIALIZE(CharacterController)
+
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ CharacterController (MemLabelId label, ObjectCreationMode mode);
+ virtual void Reset();
+ virtual void SmartReset ();
+
+ int Move (const Vector3f& movement);
+
+ Vector2f GetGlobalExtents () const;
+
+ virtual void TransformChanged(int mask);
+
+ void SetRadius(float radius);
+ float GetRadius() { return m_Radius; }
+ void SetHeight(float height);
+ float GetHeight() { return m_Height; }
+
+ virtual AABB GetBounds ();
+
+ Vector3f GetCenter ();
+ void SetCenter(const Vector3f& center);
+
+ float GetSlopeLimit ();
+ void SetSlopeLimit (float limit);
+
+ float GetStepOffset ();
+ void SetStepOffset (float limit);
+
+ virtual void SetIsTrigger (bool trigger);
+
+ static void CreateControllerManager ();
+ static void CleanupControllerManager ();
+
+ Vector3f GetWorldCenterPosition() const;
+
+ bool SimpleMove (const Vector3f& movement);
+// bool SimpleJump (const Vector3f& movement, float jumpHeight);
+
+ Vector3f GetVelocity();
+
+ bool IsGrounded ();
+ int GetCollisionFlags() { return m_LastCollisionFlags; }
+
+ bool GetDetectCollisions () { return m_DetectCollision; }
+ void SetDetectCollisions (bool detect);
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+private:
+
+ virtual void Create(const Rigidbody* ignoreAttachRigidbody);
+ virtual void Cleanup();
+ virtual void ScaleChanged ();
+
+ void ApplyRootMotionBuiltin (RootMotionData* rootMotion);
+
+ NxCapsuleController* m_Controller;
+
+ float m_MinMoveDistance;///< range { 0, infinity }
+ float m_SkinWidth;///< range { 0.0001, infinity }
+ float m_SlopeLimit;///< range { 0, 180 }
+ float m_StepOffset;///< range { 0, infinity }
+ float m_Height;///< range { 0, infinity }
+ float m_Radius;///< range { 0, infinity }
+ Vector3f m_Center;
+ bool m_DetectCollision;
+
+ float m_VerticalSpeed;
+ Vector3f m_Velocity;
+ Vector3f m_LastSimpleVelocity;
+
+ int m_LastCollisionFlags;
+
+ friend class PhysicsManager;
+};
+
+struct ControllerColliderHit
+{
+ ScriptingObjectPtr controller;
+ ScriptingObjectPtr collider;
+ Vector3f point;
+ Vector3f normal;
+ Vector3f motionDirection;
+ float motionLength;
+ int push;
+};
+
+struct ControllerControllerHit
+{
+ ScriptingObjectPtr controller;
+ ScriptingObjectPtr other;
+ short push;
+};
diff --git a/Runtime/Dynamics/CharacterJoint.cpp b/Runtime/Dynamics/CharacterJoint.cpp
new file mode 100644
index 0000000..38dd0aa
--- /dev/null
+++ b/Runtime/Dynamics/CharacterJoint.cpp
@@ -0,0 +1,340 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "CharacterJoint.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "PhysicsManager.h"
+#include "Runtime/Utilities/Utility.h"
+
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+
+using namespace std;
+
+namespace Unity
+{
+
+#define GET_JOINT() static_cast<NxD6Joint*> (m_Joint)
+
+inline void FixupNovodexLimitBug (NxD6JointDesc& desc)
+{
+ desc.twistMotion = desc.swing2Motion = desc.swing1Motion = NX_D6JOINT_MOTION_LIMITED;
+}
+
+
+/*
+- We awake the hingejoint only once. (AwakeFromLoad)
+ At this point we setup the axes. They are never changed afterwards
+ -> The perfect solution remembers the old position/rotation of the rigid bodies.
+ Then when changing axis/anchor is changed it generates axes that are rleative to the old position/rotation state!
+*/
+
+CharacterJoint::CharacterJoint (MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode)
+{
+ m_TargetRotation = Quaternionf::identity();
+// m_TargetAngularVelocity = Vector3f::zero;
+ m_UseTargetRotation = false;
+ m_SwingAxis = Vector3f::yAxis;
+}
+
+CharacterJoint::~CharacterJoint ()
+{
+}
+
+void CharacterJoint::CalculateGlobalHingeSpace (Vector3f& globalAnchor, Vector3f& globalAxis, Vector3f& globalNormal) const
+{
+ const Transform& transform = GetComponent (Transform);
+
+ Vector3f localAxis = m_Axis;
+ if (SqrMagnitude (localAxis) < Vector3f::epsilon)
+ localAxis = Vector3f (1.0F, 0.0F, 0.0F);
+ Vector3f localNormal = m_SwingAxis;
+
+ OrthoNormalize (&localAxis, &localNormal);
+
+ globalAnchor = transform.TransformPoint (m_Anchor);
+// Vector3f globalRigidbodyPos = transform.GetPosition ();
+ globalAxis = transform.TransformDirection (localAxis);
+
+ globalNormal = transform.TransformDirection (localNormal);
+
+ Matrix3x3f m;
+ m.SetOrthoNormalBasisInverse(globalAxis, globalNormal, Cross (globalAxis, globalNormal));
+// m.SetOrthoNormalBasisInverse(Vector3f::xAxis, globalNormal, Cross (globalAxis, globalNormal));
+// m.SetOrthoNormalBasisInverse(globalNormal, Cross (globalAxis, globalNormal), globalAxis);
+// OrthoNormalize(m);
+ Quaternionf q;
+ MatrixToQuaternion(m, q);
+ m_ConfigurationSpace = q * Inverse(transform.GetRotation ());
+}
+
+void CharacterJoint::Reset ()
+{
+ Super::Reset();
+
+ m_RotationDrive.maximumForce = 20;
+ m_RotationDrive.positionSpring = 50;
+ m_RotationDrive.positionDamper = 5;
+
+ InitSoftJointLimit (m_LowTwistLimit);
+ InitSoftJointLimit (m_HighTwistLimit);
+ InitSoftJointLimit (m_Swing1Limit);
+ InitSoftJointLimit (m_Swing2Limit);
+
+ m_LowTwistLimit.limit = -20;
+ m_HighTwistLimit.limit = 70;
+
+ m_Swing1Limit.limit = 40;
+ m_Swing2Limit.limit = 0;
+}
+
+void CharacterJoint::CheckConsistency ()
+{
+ Super::CheckConsistency();
+ m_LowTwistLimit.limit = clamp<float> (m_LowTwistLimit.limit, -180, 180);
+ m_HighTwistLimit.limit = clamp<float> (m_HighTwistLimit.limit, -180, 180);
+ m_Swing1Limit.limit = clamp<float> (m_Swing1Limit.limit, 0, 180);
+ m_Swing2Limit.limit = clamp<float> (m_Swing2Limit.limit, 0, 180);
+}
+
+void CharacterJoint::UpdateTargetRotation ()
+{
+ NxD6Joint* joint = GET_JOINT ();
+ if (joint)
+ {
+
+ Quaternionf temp = m_ConfigurationSpace * Inverse(m_TargetRotation) * Inverse(m_ConfigurationSpace);
+ NxQuat targetRotation = (const NxQuat&)temp;
+// NxActor *a0, *a1;
+// joint->getActors(&a0, &a1);
+// if (a1)
+// targetRotation = a1->getGlobalOrientationQuat () * targetRotation;
+//NxQuat id;
+//id.id();
+ joint->setDriveOrientation(targetRotation);
+ }
+}
+
+Quaternionf CharacterJoint::GetTargetRotation ()
+{
+ return m_TargetRotation;
+}
+
+void CharacterJoint::SetTargetRotation (const Quaternionf& rot)
+{
+ SetDirty ();
+ if (m_Joint)
+ {
+ NxActor* a0, *a1;
+ m_Joint->getActors(&a0, &a1);
+/* if (a1)
+ {
+ Quaternionf q = (const Quaternionf&)a1->getGlobalOrientationQuat();
+ m_TargetRotation = NormalizeSafe(Inverse(q) * rot);
+ }
+ else*/
+ m_TargetRotation = NormalizeSafe (rot);
+ }
+
+ UpdateTargetRotation();
+}
+
+Vector3f CharacterJoint::GetTargetAngularVelocity ()
+{
+ return m_TargetAngularVelocity;
+}
+
+void CharacterJoint::SetTargetAngularVelocity (const Vector3f& rot)
+{
+ SetDirty ();
+ m_TargetAngularVelocity = rot;
+ if (m_Joint)
+ GET_JOINT ()->setDriveAngularVelocity ((const NxVec3&)m_TargetAngularVelocity);
+}
+
+JointDrive CharacterJoint::GetRotationDrive ()
+{
+ return m_RotationDrive;
+}
+
+void CharacterJoint::SetRotationDrive (const JointDrive& drive)
+{
+ SetDirty ();
+ m_RotationDrive = drive;
+ if (GET_JOINT())
+ {
+ NxD6JointDesc desc;
+ GET_JOINT()->saveToDesc (desc);
+ FixupNovodexLimitBug(desc);
+
+ int flag = 0;
+ if (m_UseTargetRotation)
+ flag |= NX_D6JOINT_DRIVE_POSITION;
+
+ ConvertDrive (m_RotationDrive, desc.slerpDrive, flag);
+ ConvertDrive (m_RotationDrive, desc.twistDrive, flag);
+ ConvertDrive (m_RotationDrive, desc.swingDrive, flag);
+ GET_JOINT()->loadFromDesc (desc);
+ }
+}
+
+void CharacterJoint::SetLowTwistLimit (const SoftJointLimit& limit)
+{
+ SetDirty ();
+ m_LowTwistLimit = limit;
+ if (GET_JOINT())
+ {
+ NxD6JointDesc desc;
+ GET_JOINT()->saveToDesc (desc);
+ FixupNovodexLimitBug(desc);
+
+ ConvertSoftLimit (m_LowTwistLimit, desc.twistLimit.low);
+ ConvertSoftLimit (m_HighTwistLimit, desc.twistLimit.high);
+ if (desc.twistLimit.low.value > desc.twistLimit.high.value)
+ swap (desc.twistLimit.low, desc.twistLimit.high);
+ GET_JOINT()->loadFromDesc (desc);
+ }
+}
+
+void CharacterJoint::SetHighTwistLimit (const SoftJointLimit& limit)
+{
+ SetDirty ();
+ m_HighTwistLimit = limit;
+ if (GET_JOINT())
+ {
+ NxD6JointDesc desc;
+ GET_JOINT()->saveToDesc (desc);
+ FixupNovodexLimitBug(desc);
+
+
+ ConvertSoftLimit (m_LowTwistLimit, desc.twistLimit.low);
+ ConvertSoftLimit (m_HighTwistLimit, desc.twistLimit.high);
+ if (desc.twistLimit.low.value > desc.twistLimit.high.value)
+ swap (desc.twistLimit.low, desc.twistLimit.high);
+ GET_JOINT()->loadFromDesc (desc);
+ }
+}
+
+void CharacterJoint::SetSwing1Limit (const SoftJointLimit& limit)
+{
+ SetDirty ();
+ m_Swing1Limit = limit;
+ if (GET_JOINT())
+ {
+ NxD6JointDesc desc;
+ GET_JOINT()->saveToDesc (desc);
+ FixupNovodexLimitBug(desc);
+
+ ConvertSoftLimit (m_Swing1Limit, desc.swing1Limit);
+ GET_JOINT()->loadFromDesc (desc);
+ }
+}
+
+void CharacterJoint::SetSwing2Limit (const SoftJointLimit& limit)
+{
+ SetDirty ();
+ m_Swing2Limit = limit;
+ if (GET_JOINT())
+ {
+ NxD6JointDesc desc;
+ GET_JOINT()->saveToDesc (desc);
+ FixupNovodexLimitBug(desc);
+
+ ConvertSoftLimit (m_Swing2Limit, desc.swing2Limit);
+ GET_JOINT()->loadFromDesc (desc);
+ }
+}
+
+/*
+- When the rigid body which is attached with the hinge joint is activated after the joint is loaded
+ It will not be connected with the joint!
+*/
+
+template<class TransferFunction>
+void CharacterJoint::Transfer (TransferFunction& transfer)
+{
+ JointTransferPre (transfer);
+ TRANSFER (m_SwingAxis);
+// TRANSFER (m_UseTargetRotation);
+// TRANSFER (m_RotationDrive);
+ TRANSFER (m_LowTwistLimit);
+ TRANSFER (m_HighTwistLimit);
+ TRANSFER (m_Swing1Limit);
+ TRANSFER (m_Swing2Limit);
+
+ JointTransferPost (transfer);
+}
+
+void CharacterJoint::Create ()
+{
+ AssertIf (!IsActive ());
+
+ NxD6JointDesc desc;
+
+ if (m_Joint && m_Joint->getState () == NX_JS_SIMULATING)
+ GET_JOINT()->saveToDesc (desc);
+
+ desc.xMotion = NX_D6JOINT_MOTION_LOCKED;
+ desc.yMotion = NX_D6JOINT_MOTION_LOCKED;
+ desc.zMotion = NX_D6JOINT_MOTION_LOCKED;
+
+ ConvertSoftLimit (m_LowTwistLimit, desc.twistLimit.low);
+ ConvertSoftLimit (m_HighTwistLimit, desc.twistLimit.high);
+
+ if (desc.twistLimit.low.value > desc.twistLimit.high.value)
+ swap (desc.twistLimit.low, desc.twistLimit.high);
+
+ ConvertSoftLimit (m_Swing1Limit, desc.swing1Limit);
+ ConvertSoftLimit (m_Swing2Limit, desc.swing2Limit);
+
+ desc.swing1Motion = NX_D6JOINT_MOTION_LIMITED;
+ desc.swing2Motion = NX_D6JOINT_MOTION_LIMITED;
+ desc.twistMotion = NX_D6JOINT_MOTION_LIMITED;
+
+// desc.driveOrientation = (const NxQuat&)m_TargetRotation;
+ desc.driveAngularVelocity = (const NxVec3&)m_TargetAngularVelocity;
+
+ desc.flags = NX_D6JOINT_SLERP_DRIVE;
+
+ int flag = 0;
+ if (m_UseTargetRotation)
+ flag |= NX_D6JOINT_DRIVE_POSITION;
+
+ ConvertDrive (m_RotationDrive, desc.slerpDrive, flag);
+ ConvertDrive (m_RotationDrive, desc.twistDrive, flag);
+ ConvertDrive (m_RotationDrive, desc.swingDrive, flag);
+
+ FINALIZE_CREATE (desc, NxD6Joint);
+
+ ///////// DO WE WANT THIS?????????
+ SetTargetRotation (GetComponent(Transform).GetRotation());
+}
+
+void CharacterJoint::SetSwingAxis (const Vector3f& axis)\
+{
+ SetDirty ();
+ m_SwingAxis = axis;
+ ApplySetupAxesToDesc (kChangeAxis);
+}
+
+void CharacterJoint::ApplySetupAxesToDesc (int option)
+{
+ if (IsActive () && m_Joint)
+ {
+ NxD6JointDesc desc;
+ AssertIf (m_Joint->getState () == NX_JS_BROKEN);
+ GET_JOINT()->saveToDesc (desc);
+ FixupNovodexLimitBug(desc);
+
+ SetupAxes (desc, option);
+ GET_JOINT()->loadFromDesc (desc);
+ AssertIf (m_Joint->getState () == NX_JS_BROKEN);
+ }
+}
+
+}
+
+
+IMPLEMENT_CLASS (CharacterJoint)
+IMPLEMENT_OBJECT_SERIALIZE (CharacterJoint)
+#endif //ENABLE_PHYSICS
diff --git a/Runtime/Dynamics/CharacterJoint.h b/Runtime/Dynamics/CharacterJoint.h
new file mode 100644
index 0000000..ce94155
--- /dev/null
+++ b/Runtime/Dynamics/CharacterJoint.h
@@ -0,0 +1,83 @@
+#ifndef CHARACTERJOINT_H
+#define CHARACTERJOINT_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Quaternion.h"
+#include "JointDescriptions.h"
+#include "Joint.h"
+class Rigidbody;
+class NxJointDesc;
+class NxRevoluteJoint;
+
+namespace Unity
+{
+
+class CharacterJoint : public Joint
+{
+ public:
+
+ REGISTER_DERIVED_CLASS (CharacterJoint, Joint)
+ DECLARE_OBJECT_SERIALIZE (CharacterJoint)
+
+ CharacterJoint (MemLabelId label, ObjectCreationMode mode);
+
+ JointDrive GetRotationDrive ();
+ void SetRotationDrive (const JointDrive& drive);
+
+ void SetTargetRotation (const Quaternionf& rot);
+ Quaternionf GetTargetRotation ();
+
+ void SetTargetAngularVelocity (const Vector3f& angular);
+ Vector3f GetTargetAngularVelocity ();
+
+ virtual void ApplySetupAxesToDesc (int option);
+
+ virtual void SetSwingAxis (const Vector3f& axis);
+ Vector3f GetSwingAxis () { return m_SwingAxis; }
+
+ void SetLowTwistLimit (const SoftJointLimit& limit);
+ SoftJointLimit GetLowTwistLimit () { return m_LowTwistLimit; }
+
+ void SetHighTwistLimit (const SoftJointLimit& limit);
+ SoftJointLimit GetHighTwistLimit () { return m_HighTwistLimit; }
+
+ void SetSwing1Limit (const SoftJointLimit& limit);
+ SoftJointLimit GetSwing1Limit () { return m_Swing1Limit; }
+
+ void SetSwing2Limit (const SoftJointLimit& limit);
+ SoftJointLimit GetSwing2Limit () { return m_Swing2Limit; }
+
+ virtual void Reset();
+
+ void UpdateTargetRotation ();
+ virtual void CheckConsistency ();
+
+ ////// THIS IS NOT GOOD!!!!!!!!!!!
+ void CalculateGlobalHingeSpace (Vector3f& globalAnchor, Vector3f& globalAxis, Vector3f& globalNormal) const;
+
+ private:
+
+ virtual void Create ();
+ void SetupDriveType ();
+
+ bool m_UseTargetRotation;
+ Quaternionf m_TargetRotation;
+ Vector3f m_TargetAngularVelocity;
+
+ mutable Quaternionf m_ConfigurationSpace;
+
+ ////// THIS IS NOT GOOD!!!!!!!!!!!
+ Vector3f m_SwingAxis;
+
+ JointDrive m_RotationDrive;
+
+ SoftJointLimit m_LowTwistLimit;
+ SoftJointLimit m_HighTwistLimit;
+ SoftJointLimit m_Swing1Limit;
+ SoftJointLimit m_Swing2Limit;
+};
+
+}
+
+#endif
diff --git a/Runtime/Dynamics/Cloth.cpp b/Runtime/Dynamics/Cloth.cpp
new file mode 100644
index 0000000..52a1ad7
--- /dev/null
+++ b/Runtime/Dynamics/Cloth.cpp
@@ -0,0 +1,402 @@
+#include "UnityPrefix.h"
+#include "Cloth.h"
+
+#if ENABLE_CLOTH
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "PhysicsManager.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "Collider.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+
+namespace Unity
+{
+
+InteractiveCloth::InteractiveCloth (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_FixedUpdateNode (this)
+{
+ // configuration
+ m_Friction = 0.5f;
+ m_Density = 1.0f;
+ m_Pressure = 0.0f;
+ m_CollisionResponse = 0.0f;
+ m_TearFactor = 0.0f;
+ m_AttachmentTearFactor = 0.5f;
+ m_AttachmentResponse = 0.2f;
+
+ // state
+ m_IsTeared = false;
+}
+
+InteractiveCloth::~InteractiveCloth ()
+{
+}
+
+void InteractiveCloth::Reset ()
+{
+ Super::Reset();
+
+ // configuration
+ m_Friction = 0.5f;
+ m_Density = 1.0f;
+ m_Pressure = 0.0f;
+ m_CollisionResponse = 0.0f;
+ m_TearFactor = 0.0f;
+ m_AttachmentTearFactor = 0.5f;
+ m_AttachmentResponse = 0.2f;
+}
+
+#if ENABLE_CLOTH
+
+void InteractiveCloth::Create()
+{
+ Cleanup ();
+
+ m_CachedMesh = m_Mesh;
+
+ if (!m_Mesh.IsValid())
+ return;
+
+ m_IsTeared = false;
+
+ int tearMemoryFactor = (m_TearFactor > 0.0f) ? 2 : 1;
+ if (!SetupMeshData (true, false, tearMemoryFactor))
+ return;
+
+#if UNITY_EDITOR
+ if (IsWorldPlaying())
+ {
+#endif
+ NxClothDesc clothDesc;
+ SetupClothDesc (clothDesc, true);
+ clothDesc.density = m_Density;
+ clothDesc.pressure = m_Pressure;
+ clothDesc.friction = m_Friction;
+ if (m_TearFactor > 0)
+ clothDesc.tearFactor = m_TearFactor+1;
+
+ clothDesc.collisionResponseCoefficient = m_CollisionResponse;
+ clothDesc.attachmentTearFactor = m_AttachmentTearFactor+1;
+ clothDesc.attachmentResponseCoefficient = m_AttachmentResponse;
+ if (m_Pressure > 0)
+ clothDesc.flags |= NX_CLF_PRESSURE;
+ if (m_CollisionResponse > 0)
+ clothDesc.flags |= NX_CLF_COLLISION_TWOWAY;
+ if (m_TearFactor > 0)
+ clothDesc.flags |= NX_CLF_TEARABLE;
+
+ m_ClothScene = &GetDynamicsScene ();
+ m_Cloth = m_ClothScene->createCloth(clothDesc);
+
+ if (m_Cloth)
+ {
+ for(std::vector<ClothAttachment>::iterator i = m_AttachedColliders.begin(); i != m_AttachedColliders.end(); i++)
+ {
+ ClothAttachment& attach = *i;
+ AttachToCollider(attach.m_Collider, attach.m_Tearable, attach.m_TwoWayInteraction);
+ }
+ }
+ else
+ GetDynamicsSDK().releaseClothMesh(*clothDesc.clothMesh);
+#if UNITY_EDITOR
+ }
+#endif
+}
+
+void InteractiveCloth::AddForceAtPosition (const Vector3f& force, const Vector3f& position, float radius, int mode)
+{
+ AssertFiniteParameter(force)
+ AssertFiniteParameter(position)
+ AssertIf (m_Cloth == NULL);
+
+ if (!m_IsSuspended)
+ m_Cloth->wakeUp();
+ m_Cloth->addDirectedForceAtPos ((const NxVec3&)position, (const NxVec3&)force, radius ,(NxForceMode)mode);
+}
+
+void InteractiveCloth::AttachToCollider (Collider *collider, bool tearable, bool twoWayInteraction)
+{
+ AssertIf (m_Cloth == NULL);
+ NxShape* shape = collider ? collider->CreateShapeIfNeeded() : NULL;
+ if (shape)
+ {
+ NxU32 attachmentFlags = 0;
+ if (twoWayInteraction)
+ attachmentFlags |= NX_CLOTH_ATTACHMENT_TWOWAY;
+ if (tearable)
+ attachmentFlags |= NX_CLOTH_ATTACHMENT_TEARABLE;
+ m_Cloth->attachToShape(shape, attachmentFlags);
+ m_NeedToWakeUp = true;
+ }
+}
+
+void InteractiveCloth::DetachFromCollider (Collider *collider)
+{
+ AssertIf (m_Cloth == NULL);
+ if (collider && collider->IsActive())
+ {
+ m_Cloth->detachFromShape(collider->m_Shape);
+ m_NeedToWakeUp = true;
+ }
+}
+
+void InteractiveCloth::CheckTearing()
+{
+ if (m_NumVerticesFromPhysX > m_NumVertices)
+ {
+ m_IsTeared = true;
+ m_Cloth->setFlags(m_Cloth->getFlags() & ~NX_CLF_PRESSURE);
+ }
+}
+
+void InteractiveCloth::ProcessMeshForRenderer ()
+{
+ CheckTearing();
+ Super::ProcessMeshForRenderer();
+}
+
+////@TODO: TEST IF THIS ACTUALLLY CAUSES THE CLOTH PIECE TO EVER FALL ASLEEP???
+void InteractiveCloth::FixedUpdate()
+{
+ Super::FixedUpdate ();
+ if (m_Cloth)
+ {
+ CheckTearing();
+ }
+}
+
+void InteractiveCloth::AddToManager ()
+{
+ GetFixedBehaviourManager ().AddBehaviour (m_FixedUpdateNode, -1);
+}
+
+void InteractiveCloth::RemoveFromManager ()
+{
+ GetFixedBehaviourManager ().RemoveBehaviour (m_FixedUpdateNode);
+}
+
+void InteractiveCloth::PauseSimulation () {
+ Super::PauseSimulation ();
+ m_Cloth->setFlags(m_Cloth->getFlags() | NX_CLF_DISABLE_COLLISION | NX_CLF_STATIC);
+}
+
+void InteractiveCloth::ResumeSimulation () {
+ Super::ResumeSimulation ();
+ m_Cloth->setFlags(m_Cloth->getFlags() & ~(NX_CLF_DISABLE_COLLISION | NX_CLF_STATIC));
+}
+
+#define ENFORCE_MINEQ(x) {if (value < x) { value = x; ErrorString("value must be greater than or equal to " #x);}}
+#define ENFORCE_MIN(x) {if (value <= x) { value = x; ErrorString("value must be greater than " #x);}}
+#define ENFORCE_MAXEQ(x) {if (value > x) { value = x; ErrorString("value must be smaller than or equal to " #x);}}
+#define ENFORCE_MAX(x) {if (value >= x) { value = x; ErrorString("value must be smaller than " #x);}}
+
+void InteractiveCloth::SetMesh (PPtr<Mesh> value)
+{
+ if (value != m_CachedMesh)
+ {
+ m_Mesh = value;
+ Create();
+ SetDirty();
+ }
+}
+
+void InteractiveCloth::SetFriction (float value)
+{
+ ENFORCE_MINEQ(0);
+ ENFORCE_MAXEQ(1);
+
+ if (value != m_Friction)
+ {
+ m_NeedToWakeUp = true;
+ m_Friction = value;
+ }
+ if (m_Cloth)
+ m_Cloth->setFriction(value);
+ SetDirty();
+}
+
+void InteractiveCloth::SetDensity (float value)
+{
+ ENFORCE_MIN(0);
+
+ if (value != m_Density)
+ {
+ m_NeedToWakeUp = true;
+ m_Density = value;
+ }
+ if (m_Cloth)
+ {
+ if (m_Density != m_Cloth->getDensity())
+ Create();
+ }
+ SetDirty();
+}
+
+void InteractiveCloth::SetPressure (float value)
+{
+ ENFORCE_MINEQ(0);
+
+ if (value != m_Pressure)
+ {
+ m_NeedToWakeUp = true;
+ m_Pressure = value;
+ }
+ if (m_Cloth)
+ {
+ if (value > 0 && !m_IsTeared)
+ {
+ m_Cloth->setPressure(value);
+ m_Cloth->setFlags(m_Cloth->getFlags() | NX_CLF_PRESSURE);
+ }
+ else
+ m_Cloth->setFlags(m_Cloth->getFlags() & ~(NX_CLF_PRESSURE));
+ }
+ SetDirty();
+}
+
+void InteractiveCloth::SetCollisionResponse (float value)
+{
+ ENFORCE_MINEQ(0);
+
+ if (value != m_CollisionResponse)
+ {
+ m_NeedToWakeUp = true;
+ m_CollisionResponse = value;
+ }
+ if (m_Cloth)
+ {
+ if (value > 0)
+ {
+ m_Cloth->setCollisionResponseCoefficient(value);
+ m_Cloth->setFlags(m_Cloth->getFlags() | NX_CLF_COLLISION_TWOWAY);
+ }
+ else
+ m_Cloth->setFlags(m_Cloth->getFlags() & ~(NX_CLF_COLLISION_TWOWAY));
+ }
+ SetDirty();
+}
+
+void InteractiveCloth::SetTearFactor (float value)
+{
+ ENFORCE_MINEQ(0);
+
+ if (value != m_TearFactor)
+ {
+ m_NeedToWakeUp = true;
+ m_TearFactor = value;
+ }
+ if (m_Cloth)
+ {
+ if ((m_TearFactor>0) != ((m_Cloth->getFlags() & NX_CLF_TEARABLE) != 0))
+ Create();
+ else if (m_TearFactor>0)
+ m_Cloth->setTearFactor(m_TearFactor + 1);
+ }
+ SetDirty();
+}
+
+void InteractiveCloth::SetAttachmentTearFactor (float value)
+{
+ ENFORCE_MINEQ(0);
+
+ if (value != m_AttachmentTearFactor)
+ {
+ m_NeedToWakeUp = true;
+ m_AttachmentTearFactor = value;
+ }
+ if (m_Cloth && m_AttachmentTearFactor>0)
+ m_Cloth->setAttachmentTearFactor(m_AttachmentTearFactor + 1);
+ SetDirty();
+}
+
+void InteractiveCloth::SetAttachmentResponse (float value)
+{
+ ENFORCE_MINEQ(0);
+ ENFORCE_MAXEQ(1);
+
+ if (value != m_AttachmentResponse)
+ {
+ m_NeedToWakeUp = true;
+ m_AttachmentResponse = value;
+ }
+ if (m_Cloth)
+ m_Cloth->setAttachmentResponseCoefficient(value);
+ SetDirty();
+}
+
+#else //ENABLE_CLOTH
+
+void InteractiveCloth::Create() {}
+void InteractiveCloth::AddForceAtPosition (const Vector3f& force, const Vector3f& position, float radius, int mode) {}
+void InteractiveCloth::AttachToCollider (Collider *collider, bool tearable, bool twoWayInteraction) {}
+void InteractiveCloth::DetachFromCollider (Collider *collider) {}
+void InteractiveCloth::CheckTearing() {}
+void InteractiveCloth::ProcessMeshForRenderer () {}
+void InteractiveCloth::FixedUpdate() {}
+void InteractiveCloth::PauseSimulation () {}
+void InteractiveCloth::ResumeSimulation () {}
+void InteractiveCloth::SetMesh (PPtr<Mesh> value) {}
+void InteractiveCloth::SetFriction (float value) {}
+void InteractiveCloth::SetDensity (float value) {}
+void InteractiveCloth::SetPressure (float value) {}
+void InteractiveCloth::SetCollisionResponse (float value) {}
+void InteractiveCloth::SetTearFactor (float value) {}
+void InteractiveCloth::SetAttachmentTearFactor (float value) {}
+void InteractiveCloth::SetAttachmentResponse (float value) {}
+void InteractiveCloth::AddToManager () {}
+void InteractiveCloth::RemoveFromManager (){}
+#endif //ENABLE_CLOTH
+
+void InteractiveCloth::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+#if ENABLE_CLOTH
+ if (m_Cloth)
+ {
+ // Apply changed values
+ SetMesh (m_Mesh);
+ SetFriction (m_Friction);
+ SetPressure (m_Pressure);
+ SetCollisionResponse (m_CollisionResponse);
+ SetAttachmentTearFactor (m_AttachmentTearFactor);
+ SetAttachmentResponse (m_AttachmentResponse);
+ SetTearFactor (m_TearFactor);
+ SetDensity (m_Density);
+ }
+#endif
+
+ Super::AwakeFromLoad (awakeMode);
+}
+
+template<class TransferFunction>
+void InteractiveCloth::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER (m_Mesh);
+ TRANSFER (m_Friction);
+ TRANSFER (m_Density);
+ TRANSFER (m_Pressure);
+ TRANSFER (m_CollisionResponse);
+ TRANSFER (m_AttachmentTearFactor);
+ TRANSFER (m_AttachmentResponse);
+ TRANSFER (m_TearFactor);
+ TRANSFER (m_AttachedColliders);
+}
+
+template<class TransferFunction> inline
+void ClothAttachment::Transfer (TransferFunction& transfer)
+{
+ TRANSFER(m_Collider);
+ TRANSFER(m_TwoWayInteraction);
+ TRANSFER(m_Tearable);
+}
+
+IMPLEMENT_CLASS (InteractiveCloth)
+IMPLEMENT_OBJECT_SERIALIZE (InteractiveCloth)
+
+}
+
+void RegisterClass_InteractiveCloth () { Unity::RegisterClass_InteractiveCloth(); }
+
+#endif // ENABLE_CLOTH
diff --git a/Runtime/Dynamics/Cloth.h b/Runtime/Dynamics/Cloth.h
new file mode 100644
index 0000000..06341c8
--- /dev/null
+++ b/Runtime/Dynamics/Cloth.h
@@ -0,0 +1,101 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_CLOTH || DOXYGEN
+
+#include "DeformableMesh.h"
+
+class Collider;
+
+namespace Unity
+{
+
+struct ClothAttachment
+{
+ PPtr<Collider> m_Collider;
+ bool m_TwoWayInteraction;
+ bool m_Tearable;
+
+ DECLARE_SERIALIZE (ClothAttachment)
+
+ ClothAttachment() :
+ m_TwoWayInteraction(false),
+ m_Tearable(false)
+ { }
+
+};
+
+class InteractiveCloth : public Cloth
+{
+public:
+ REGISTER_DERIVED_CLASS (InteractiveCloth, Cloth)
+ DECLARE_OBJECT_SERIALIZE (InteractiveCloth)
+
+ InteractiveCloth (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void ProcessMeshForRenderer ();
+
+ virtual void FixedUpdate ();
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+ virtual void Reset ();
+
+ void AddForceAtPosition (const Vector3f& force, const Vector3f& position, float radius, int mode);
+ void AttachToCollider (Collider *collider, bool tearable, bool twoWayInteraction);
+ void DetachFromCollider (Collider *collider);
+
+ PPtr<Mesh> GetMesh () { return m_Mesh; }
+ void SetMesh (PPtr<Mesh> value);
+
+ float GetFriction () { return m_Friction; }
+ void SetFriction (float value);
+
+ float GetDensity () { return m_Density; }
+ void SetDensity (float value);
+
+ float GetPressure () { return m_Pressure; }
+ void SetPressure (float value);
+
+ float GetCollisionResponse () { return m_CollisionResponse; }
+ void SetCollisionResponse (float value);
+
+ float GetTearFactor () { return m_TearFactor; }
+ void SetTearFactor (float value);
+
+ float GetAttachmentTearFactor () { return m_AttachmentTearFactor; }
+ void SetAttachmentTearFactor (float value);
+
+ float GetAttachmentResponse () { return m_AttachmentResponse; }
+ void SetAttachmentResponse (float value);
+
+ bool GetIsTeared () { return m_IsTeared; }
+
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+
+protected:
+
+ virtual void Create ();
+ virtual void PauseSimulation ();
+ virtual void ResumeSimulation ();
+
+ void CheckTearing();
+
+ // configuration
+ float m_Friction; ///<Friction. range { 0, 1 }
+ float m_Density; ///<Density (mass per area). range { 0.001, 10000 }
+ float m_Pressure; ///<Air pressure inside a closed cloth mesh. 0 = disabled. 1 = same pressure as outside atmosphere. range { 0, 10000 }
+ float m_CollisionResponse; ///<Force to apply back to colliding rigidbodies. 0 = disabled. range { 0, 10000 }
+ float m_TearFactor; ///<How far vertices need to stretch until they tear. 0 = disabled. range { 0, 10000 }
+ float m_AttachmentTearFactor; ///<How far vertices need to stretch until attachments tear off, if tearing is enabled for the attachment. range { 0.001, 10000 }
+ float m_AttachmentResponse; ///<Force to apply back to attached rigidbodies, if two way interaction is enabled for the attachment. range { 0, 1 }
+ std::vector<ClothAttachment> m_AttachedColliders;
+
+ // state
+ bool m_IsTeared;
+ PPtr<Mesh> m_CachedMesh;
+ BehaviourListNode m_FixedUpdateNode;
+};
+
+}
+
+#endif // ENABLE_CLOTH
diff --git a/Runtime/Dynamics/ClothRenderer.cpp b/Runtime/Dynamics/ClothRenderer.cpp
new file mode 100644
index 0000000..0217a09
--- /dev/null
+++ b/Runtime/Dynamics/ClothRenderer.cpp
@@ -0,0 +1,273 @@
+#include "UnityPrefix.h"
+#include "ClothRenderer.h"
+
+#if ENABLE_CLOTH
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Dynamics/Cloth.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "Runtime/BaseClasses/SupportedMessageOptimization.h"
+
+ClothRenderer::ClothRenderer (MemLabelId label, ObjectCreationMode mode)
+: Super(kRendererCloth, label, mode)
+, m_VBO(NULL)
+{
+ m_ChannelsInVBO = 0;
+ m_UnavailableInVBO = 0;
+ m_PauseWhenNotVisible = true;
+ m_IsPaused = false;
+ m_AABBDirty = true;
+ m_AABB.SetCenterAndExtent(Vector3f::zero, Vector3f::zero);
+}
+
+ClothRenderer::~ClothRenderer ()
+{
+ if (m_VBO)
+ GetGfxDevice().DeleteVBO(m_VBO);
+}
+
+void ClothRenderer::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ ReloadVBOToGfxDevice();
+}
+
+void ClothRenderer::Reset ()
+{
+ Super::Reset ();
+
+ m_PauseWhenNotVisible = true;
+}
+
+void ClothRenderer::UpdateClothVBOImmediate (int requiredChannels, UInt32& unavailableChannels)
+{
+ Unity::Cloth& cloth = GetComponent(InteractiveCloth);
+
+ int supportedChannels = VERTEX_FORMAT3(Vertex, Normal, Color);
+ if (cloth.m_UVs.size() == cloth.m_VerticesForRendering->size())
+ supportedChannels |= VERTEX_FORMAT1(TexCoord0);
+ if (cloth.m_UV1s.size() == cloth.m_VerticesForRendering->size())
+ supportedChannels |= VERTEX_FORMAT1(TexCoord1);
+ if (cloth.m_Tangents.size() == cloth.m_VerticesForRendering->size())
+ supportedChannels |= VERTEX_FORMAT1(Tangent);
+
+ // Silently create an all-white color array if shader wants colors, but mesh does not have them.
+ // On D3D, some runtime/driver combinations will crash if a vertex shader wants colors but does not
+ // have them (e.g. Vista drivers for Intel 965). In other cases it will default to white for fixed function
+ // pipe, and to undefined value for vertex shaders, which is not good either.
+
+ if (cloth.m_Colors.size() != cloth.m_VerticesForRendering->size())
+ cloth.m_Colors.resize_initialized( cloth.m_VerticesForRendering->size(), 0xFFFFFFFF );
+
+ int activeChannels = requiredChannels & supportedChannels;
+ unavailableChannels = requiredChannels & ~supportedChannels;
+
+ // Set up vertex buffer channels and streams
+ VertexBufferData vertexBuffer;
+ UInt32 stride = 0;
+ for (int i = 0; i < kShaderChannelCount; i++)
+ {
+ ChannelInfo& info = vertexBuffer.channels[i];
+ if (activeChannels & (1 << i))
+ {
+ info.stream = 0;
+ info.offset = stride;
+ info.format = VBO::GetDefaultChannelFormat(i);
+ info.dimension = VBO::GetDefaultChannelDimension(i);
+ stride += VBO::GetDefaultChannelByteSize(i);
+ }
+ else
+ info.Reset();
+ }
+ vertexBuffer.streams[0].channelMask = activeChannels;
+ vertexBuffer.streams[0].stride = stride;
+
+ // Don't pass a buffer since we use map/unmap for writing
+ vertexBuffer.buffer = NULL;
+ int vertexCount = cloth.m_NumVerticesForRendering;
+ vertexBuffer.bufferSize = stride * vertexCount;
+ vertexBuffer.vertexCount = vertexCount;
+ m_VBO->SetVertexStreamMode(0, VBO::kStreamModeWritePersist);
+ m_VBO->UpdateVertexData(vertexBuffer);
+
+ IndexBufferData indexBuffer;
+ indexBuffer.indices = &(*cloth.m_IndicesForRendering)[0];
+ indexBuffer.count = cloth.m_NumIndicesForRendering;
+ indexBuffer.hasTopologies = (1<<kPrimitiveTriangles);
+
+ VertexStreamData mappedStream;
+ if (!m_VBO->MapVertexStream(mappedStream, 0))
+ return;
+ UInt8* buffer = mappedStream.buffer;
+ for (int v = 0; v < vertexCount; v++)
+ {
+ if (activeChannels & (1 << kShaderChannelVertex))
+ {
+ *(Vector3f*)buffer = (*cloth.m_VerticesForRendering)[v];
+ buffer += sizeof(Vector3f);
+ }
+
+ if (activeChannels & (1 << kShaderChannelNormal))
+ {
+ *(Vector3f*)buffer = (*cloth.m_NormalsForRendering)[v];
+ buffer += sizeof(Vector3f);
+ }
+
+ if (activeChannels & (1 << kShaderChannelColor))
+ {
+ *(ColorRGBA32*)buffer = cloth.m_Colors[v];
+ buffer += sizeof(ColorRGBA32);
+ }
+
+ if (activeChannels & (1 << kShaderChannelTexCoord0))
+ {
+ *(Vector2f*)buffer = cloth.m_UVs[v];
+ buffer += sizeof(Vector2f);
+ }
+
+ if (activeChannels & (1 << kShaderChannelTexCoord1))
+ {
+ *(Vector2f*)buffer = cloth.m_UV1s[v];
+ buffer += sizeof(Vector2f);
+ }
+
+ if (activeChannels & (1 << kShaderChannelTangent))
+ {
+ *(Vector4f*)buffer = cloth.m_Tangents[v];
+ buffer += sizeof(Vector4f);
+ }
+ }
+ m_VBO->UnmapVertexStream(0);
+
+ m_ChannelsInVBO = activeChannels;
+ m_VBO->UpdateIndexData (indexBuffer);
+}
+
+void ClothRenderer::UpdateClothVerticesFromPhysics()
+{
+ Unity::Cloth& cloth = GetComponent(InteractiveCloth);
+ cloth.ProcessMeshForRenderer();
+ cloth.m_NumVerticesFromPhysX = 0;
+
+ UpdateAABB();
+}
+
+PROFILER_INFORMATION(gClothRenderProfile, "DeformableMeshRenderer.Render", kProfilerRender)
+
+void ClothRenderer::Render (int/* subsetIndex*/, const ChannelAssigns& channels)
+{
+ PROFILER_AUTO(gClothRenderProfile, this)
+
+ UInt32 requiredChannels = channels.GetSourceMap();
+
+ Unity::Cloth& cloth = GetComponent(InteractiveCloth);
+ if (cloth.m_NumVertices > 0 || cloth.m_NumVerticesFromPhysX > 0)
+ {
+ if ((requiredChannels | m_ChannelsInVBO) != m_ChannelsInVBO || cloth.m_NumVerticesFromPhysX || m_VBO->IsVertexBufferLost())
+ {
+ if (cloth.m_NumVerticesFromPhysX)
+ UpdateClothVerticesFromPhysics();
+
+ UpdateClothVBOImmediate(requiredChannels, m_UnavailableInVBO);
+ }
+
+ if (m_CustomProperties)
+ GetGfxDevice().SetMaterialProperties (*m_CustomProperties);
+ m_VBO->DrawVBO (channels, 0, cloth.m_NumIndices, kPrimitiveTriangles, 0, cloth.m_NumVerticesForRendering);
+ }
+ GPU_TIMESTAMP();
+}
+
+void ClothRenderer::UpdateAABB()
+{
+ Unity::Cloth& cloth = GetComponent(InteractiveCloth);
+ dynamic_array<Vector3f> &vertices = *cloth.m_VerticesForRendering;
+ if (cloth.m_NumVertices != 0)
+ {
+ m_AABB.SetCenterAndExtent(vertices[0], Vector3f::zero);
+ for (int i=0; i<cloth.m_NumVertices; i++)
+ m_AABB.Encapsulate(vertices[i]);
+ }
+ else
+ m_AABB.SetCenterAndExtent(Vector3f::zero, Vector3f::zero);
+
+ BoundsChanged();
+ m_AABBDirty = false;
+}
+
+void ClothRenderer::RendererBecameVisible ()
+{
+ Super::RendererBecameVisible();
+ Unity::Cloth& cloth = GetComponent(InteractiveCloth);
+ if (m_IsPaused)
+ {
+ m_IsPaused = false;
+ cloth.SetSuspended(false);
+ }
+}
+
+void ClothRenderer::RendererBecameInvisible ()
+{
+ Super::RendererBecameInvisible();
+ Unity::Cloth& cloth = GetComponent(InteractiveCloth);
+ if (m_PauseWhenNotVisible)
+ {
+ m_IsPaused = true;
+ cloth.SetSuspended(true);
+ }
+}
+
+void ClothRenderer::UpdateTransformInfo ()
+{
+ { // transform
+ m_TransformInfo.worldMatrix.SetIdentity ();
+ m_TransformInfo.transformType = kNoScaleTransform;
+ m_TransformInfo.invScale = 1.0F;
+ }
+ if (m_AABBDirty)
+ UpdateClothVerticesFromPhysics ();
+
+ m_TransformInfo.worldAABB = m_AABB;
+
+ Transform& t = GetComponent(Transform);
+ TransformAABB( m_AABB, t.GetWorldToLocalMatrixNoScale(), m_TransformInfo.localAABB );
+}
+
+
+void ClothRenderer::UnloadVBOFromGfxDevice()
+{
+ if (m_VBO)
+ {
+ GetGfxDevice().DeleteVBO (m_VBO);
+ }
+ m_VBO = NULL;
+}
+
+
+void ClothRenderer::ReloadVBOToGfxDevice()
+{
+ m_VBO = GetGfxDevice().CreateVBO();
+ m_VBO->SetVertexStreamMode(0, VBO::kStreamModeDynamic);
+ m_VBO->SetIndicesDynamic(true);
+ m_ChannelsInVBO = 0;
+ m_UnavailableInVBO = 0;
+}
+
+
+template<class TransferFunction>
+void ClothRenderer::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER (m_PauseWhenNotVisible);
+}
+
+IMPLEMENT_CLASS (ClothRenderer)
+IMPLEMENT_OBJECT_SERIALIZE (ClothRenderer)
+
+#endif // ENABLE_CLOTH
diff --git a/Runtime/Dynamics/ClothRenderer.h b/Runtime/Dynamics/ClothRenderer.h
new file mode 100644
index 0000000..06e4cd6
--- /dev/null
+++ b/Runtime/Dynamics/ClothRenderer.h
@@ -0,0 +1,54 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_CLOTH
+
+#include "Runtime/Filters/Renderer.h"
+
+class VBO;
+
+namespace Unity { class Cloth; }
+
+
+class ClothRenderer : public Renderer
+{
+public:
+ REGISTER_DERIVED_CLASS (ClothRenderer, Renderer)
+ DECLARE_OBJECT_SERIALIZE (ClothRenderer)
+
+ ClothRenderer (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ virtual void Reset ();
+ virtual void Render (int/* subsetIndex*/, const ChannelAssigns& channels);
+
+ virtual void UpdateTransformInfo();
+
+ virtual void RendererBecameVisible();
+ virtual void RendererBecameInvisible();
+
+ bool GetPauseWhenNotVisible() const { return m_PauseWhenNotVisible; }
+ void SetPauseWhenNotVisible(bool pause) { SetDirty(); m_PauseWhenNotVisible = pause; }
+
+ void UnloadVBOFromGfxDevice();
+ void ReloadVBOToGfxDevice();
+
+private:
+
+ void UpdateClothVBOImmediate (int requiredChannels, UInt32& unavailableChannels);
+ void UpdateClothVerticesFromPhysics ();
+ void UpdateAABB ();
+
+ VBO* m_VBO;
+ UInt32 m_ChannelsInVBO;
+ UInt32 m_UnavailableInVBO;
+ AABB m_AABB;
+ bool m_PauseWhenNotVisible;
+ bool m_IsPaused;
+ bool m_AABBDirty;
+
+ friend class DeformableMesh;
+};
+
+#endif // ENABLE_CLOTH
diff --git a/Runtime/Dynamics/Collider.cpp b/Runtime/Dynamics/Collider.cpp
new file mode 100644
index 0000000..0a1a10a
--- /dev/null
+++ b/Runtime/Dynamics/Collider.cpp
@@ -0,0 +1,598 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Collider.h"
+#include "RigidBody.h"
+#include "Runtime/Graphics/Transform.h"
+#include "PhysicMaterial.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "PhysicsManager.h"
+#include "Runtime/BaseClasses/SupportedMessageOptimization.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Scripting/Scripting.h"
+
+/* Problems and limitations
+
+- When a rigid body is added or activated after the colliders are setup it won't attach them automatically
+
+- When a rigid body is destroyed it will recreate all its colliders as colliders without rigidbody. (Wasteful)
+ We should have activity state be treated hierarchically and deactivating/destroying children before parents.
+
+- Implement automatic instantiation when accessing a material
+
+- Colliders should be enlarged by min penetration epsilon. This way we prevent interpentration completely.
+
+- Get rigidbody only works when the rigidbody is active!
+
+*/
+
+#if ENABLE_PROFILER
+ProfilerInformation gStaticColliderModify ("Static Collider.Modify (Expensive delayed cost)", kProfilerPhysics, true);
+ProfilerInformation gStaticColliderMove ("Static Collider.Move (Expensive delayed cost)", kProfilerPhysics, true);
+ProfilerInformation gStaticColliderCreate ("Static Collider.Create (Expensive delayed cost)", kProfilerPhysics, true);
+ProfilerInformation gDynamicColliderCreate ("Dynamic Collider.Create", kProfilerRender);
+#endif
+
+Collider::Collider (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_Shape = NULL;
+ m_IsTrigger = false;
+ m_Enabled = true;
+}
+
+Collider::~Collider ()
+{
+ Cleanup ();
+}
+
+void Collider::SetEnabled (bool enab)
+{
+ if ((bool)m_Enabled == enab)
+ return;
+ m_Enabled = enab;
+ Cleanup ();
+ CreateShapeIfNeeded ();
+ SetDirty ();
+}
+
+void Collider::RecreateCollider (const Rigidbody* ignoreCollider)
+{
+ AssertIf (GetClassID() == 143);// Charactercontroller
+
+ if (IsActive () && GetEnabled())
+ Create (ignoreCollider);
+}
+
+Rigidbody* Collider::GetRigidbody ()
+{
+ if (m_Shape)
+ {
+ Rigidbody* body = (Rigidbody*)m_Shape->getActor ().userData;
+ return body;
+ }
+ else
+ return NULL;
+}
+
+void Collider::Deactivate (DeactivateOperation operation)
+{
+ Super::Deactivate (operation);
+ Cleanup ();
+}
+
+NxShape* Collider::CreateShapeIfNeeded()
+{
+ if (m_Shape != NULL)
+ return m_Shape;
+ if (IsActive () && GetEnabled())
+ Create(NULL);
+
+ return m_Shape;
+}
+
+void Collider::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ if (IsActive () && GetEnabled())
+ {
+ if (m_Shape)
+ {
+ if (SupportsMaterial())
+ SetMaterial (m_Material);
+ SetIsTrigger (m_IsTrigger);
+ }
+ CreateShapeIfNeeded();
+ }
+ else
+ Cleanup ();
+}
+
+bool Collider::HasActorRigidbody ()
+{
+ return m_Shape && m_Shape->getActor ().userData;
+}
+
+bool Collider::GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix)
+{
+ if (&transform == &anyParent)
+ {
+ matrix.SetIdentity ();
+ return true;
+ }
+ else
+ {
+ Vector3f childPosition = transform.GetPosition ();
+ Quaternionf childRotation = transform.GetRotation ();
+
+ Matrix4x4f childMatrix, parentMatrix;
+
+ childMatrix.SetTR (childPosition, childRotation);
+ parentMatrix = anyParent.GetWorldToLocalMatrixNoScale ();
+
+ MultiplyMatrices4x4 (&parentMatrix, &childMatrix, &matrix);
+ ErrorFiniteParameterReturnFalse(matrix)
+ return true;
+ }
+}
+
+bool Collider::GetRelativeToParentPositionAndRotationUtility (Transform& transform, Transform& anyParent, const Vector3f& localOffset, Matrix4x4f& matrix)
+{
+ Vector3f childPosition = transform.TransformPoint (localOffset);
+ Quaternionf childRotation = transform.GetRotation ();
+
+ Matrix4x4f childMatrix, parentMatrix;
+
+ childMatrix.SetTR (childPosition, childRotation);
+ parentMatrix = anyParent.GetWorldToLocalMatrixNoScale ();
+
+ MultiplyMatrices4x4 (&parentMatrix, &childMatrix, &matrix);
+ ErrorFiniteParameterReturnFalse(matrix)
+ return true;
+}
+
+void Collider::FetchPoseFromTransformUtility (const Vector3f& offset)
+{
+ AssertIf (m_Shape == NULL);
+ AssertIf (HasActorRigidbody ());
+ Transform& transform = GetComponent (Transform);
+
+ Vector3f pos = transform.TransformPoint (offset);
+ Quaternionf rot = transform.GetRotation ();
+
+ AssertFiniteParameter(pos)
+ AssertFiniteParameter(rot)
+
+ NxMat34 shapeMatrix ((const NxQuat&)rot, (const NxVec3&)pos);
+ m_Shape->setGlobalPose(shapeMatrix);
+}
+
+void Collider::FetchPoseFromTransform ()
+{
+ AssertIf (m_Shape == NULL);
+ AssertIf (HasActorRigidbody ());
+ Transform& transform = GetComponent (Transform);
+
+ Quaternionf rot; Vector3f pos;
+ transform.GetPositionAndRotation(pos, rot);
+
+ AssertFiniteParameter(pos)
+ AssertFiniteParameter(rot)
+
+ NxMat34 shapeMatrix ((const NxQuat&)rot, (const NxVec3&)pos);
+ m_Shape->setGlobalPose(shapeMatrix);
+}
+
+void Collider::RigidbodyMassDistributionChanged ()
+{
+ if (m_Shape)
+ {
+ Rigidbody* body = (Rigidbody*)m_Shape->getActor ().userData;
+ if (body)
+ body->UpdateMassDistribution();
+ }
+}
+
+void Collider::CreateWithoutIgnoreAttach ()
+{
+ if ((IsActive () && GetEnabled()) || !IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ Create(NULL);
+}
+
+NxCCDSkeleton* Collider::CreateCCDSkeleton()
+{
+ float kCCDScale = 0.8f;
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_2_a1))
+ kCCDScale = 0.5f;
+ return CreateCCDSkeleton(kCCDScale);
+}
+
+void Collider::UpdateCCDSkeleton()
+{
+ if (m_Shape)
+ {
+ Rigidbody* body = (Rigidbody*)m_Shape->getActor ().userData;
+ if (body && body->GetCollisionDetectionMode() != Rigidbody::kCCDModeOff)
+ {
+ NxCCDSkeleton *skel = m_Shape->getCCDSkeleton();
+
+ m_Shape->setCCDSkeleton(CreateCCDSkeleton());
+
+ if (skel)
+ GetDynamicsSDK().releaseCCDSkeleton (*skel);
+ }
+ }
+}
+
+
+void Collider::FinalizeCreate( NxShapeDesc& shapeDesc, bool setMaterial, const Rigidbody* dontAttachToRigidbody )
+{
+ AssertIf (GetClassID() == 143);// Charactercontroller
+
+ AssertIf (m_Shape != NULL);
+ if (m_IsTrigger)
+ {
+ shapeDesc.shapeFlags |= NX_TRIGGER_ENABLE;
+ if (!GetPhysicsManager().GetRaycastsHitTriggers())
+ shapeDesc.shapeFlags |= NX_SF_DISABLE_RAYCASTING;
+ }
+
+ Rigidbody* body = FindNewAttachedRigidbody (dontAttachToRigidbody);
+
+ shapeDesc.userData = this;
+ // if !setMaterial is passed, the caller has the material set up (e.g. WheelCollider doesn't want it)
+ if( setMaterial )
+ {
+ shapeDesc.materialIndex = GetMaterialIndex ();
+ }
+ shapeDesc.group = GetGameObject ().GetLayer ();
+
+ if (!shapeDesc.isValid())
+ {
+ ErrorStringObject ("This collider has some illegal parameters. Choose 'Reset' in the component popup menu to fix it.", this);
+ return;
+ }
+
+ if (body)
+ {
+ PROFILER_AUTO(gDynamicColliderCreate, this)
+
+ if (body->GetCollisionDetectionMode() != Rigidbody::kCCDModeOff)
+ {
+ if (body->GetCollisionDetectionMode() == Rigidbody::kCCDModeDynamic)
+ shapeDesc.shapeFlags |= NX_SF_DYNAMIC_DYNAMIC_CCD;
+
+ // CCD skeletons should be smaller then the normal colliders so that collisions at normal speeds can
+ // be handled by those.
+ NxCCDSkeleton *skel = CreateCCDSkeleton();
+ shapeDesc.ccdSkeleton = skel;
+ }
+
+ body->Create (true);
+ Matrix4x4f matrix;
+ NxActor* actor = body->m_Actor;
+ if (actor == NULL)
+ {
+ ErrorStringObject ("Could not create actor. Maybe you are using too many colliders or rigidbodies in your scene?", this);
+ return;
+ }
+
+ if (GetRelativeToParentPositionAndRotation (GetComponent (Transform), body->GetComponent (Transform), matrix))
+ {
+ shapeDesc.localPose.setColumnMajor44 (matrix.GetPtr ());
+
+ m_Shape = actor->createShape (shapeDesc);
+ }
+
+ if (!m_IsTrigger && GetClassID()!= 140)
+ actor->updateMassFromShapes (0.0F, body->GetMass ());
+ }
+ else
+ {
+ PROFILER_AUTO(gStaticColliderCreate, this)
+
+ NxActorDesc actorDesc;
+ actorDesc.userData = NULL;
+ actorDesc.shapes.push_back (&shapeDesc);
+ NxActor* actor = GetDynamicsScene ().createActor (actorDesc);
+ if (actor == NULL)
+ {
+ ErrorStringObject ("Could not create actor. Maybe you are using too many colliders or rigidbodies in your scene?", this);
+ return;
+ }
+
+ m_Shape = actor->getShapes ()[0];
+ FetchPoseFromTransform ();
+
+ SupportedMessagesDidChange (GetGameObject ().GetSupportedMessages ());
+ }
+}
+
+void Collider::SupportedMessagesDidChange (int supported)
+{
+ // We only deal with colliders with no rigid body attached
+ if (m_Shape == NULL || m_Shape->getActor ().userData != NULL)
+ return;
+
+ if (supported & kHasCollisionStay)
+ m_Shape->getActor ().setGroup (kContactTouchGroup);
+ else if (supported & (kHasCollisionStay | kHasCollisionEnterExit))
+ m_Shape->getActor ().setGroup (kContactEnterExitGroup);
+ else
+ m_Shape->getActor ().setGroup (kContactNothingGroup);
+}
+
+void Collider::SetupLayer ()
+{
+ if (m_Shape)
+ m_Shape->setGroup (GetGameObject ().GetLayer ());
+}
+
+Rigidbody* Collider::FindNewAttachedRigidbody (const Rigidbody* ignoreAttachRigidbody)
+{
+ Rigidbody* body = QueryComponent (Rigidbody);
+ if (body && body->IsActive () && body != ignoreAttachRigidbody)
+ return body;
+
+ Transform* parent = GetComponent (Transform).GetParent ();
+ while (parent)
+ {
+ GameObject* go = parent->GetGameObjectPtr ();
+ if (go)
+ body = go->QueryComponent (Rigidbody);
+ else
+ body = NULL;
+ if (body && body->IsActive () && body != ignoreAttachRigidbody)
+ return body;
+
+ parent = parent->GetParent ();
+ }
+ return NULL;
+}
+
+int Collider::GetMaterialIndex ()
+{
+ PhysicMaterial* material = m_Material;
+ if (material)
+ return material->GetMaterialIndex ();
+ else
+ return 0;
+}
+
+void Collider::SetIsTrigger (bool trigger)
+{
+ if (m_IsTrigger != trigger)
+ {
+ SetDirty ();
+ m_IsTrigger = trigger;
+ }
+
+ if (m_Shape)
+ {
+ m_Shape->setFlag (NX_TRIGGER_ENABLE, trigger);
+ m_Shape->setFlag (NX_SF_DISABLE_RAYCASTING, trigger && !GetPhysicsManager().GetRaycastsHitTriggers());
+
+ RigidbodyMassDistributionChanged ();
+ }
+}
+
+AABB Collider::GetBounds ()
+{
+ if (m_Shape)
+ {
+ AABB aabb;
+ NxBounds3 bounds;
+ m_Shape->getWorldBounds(bounds);
+ bounds.getExtents ((NxVec3&)aabb.GetExtent ());
+ bounds.getCenter ((NxVec3&)aabb.GetCenter ());
+ return aabb;
+ }
+ else
+ {
+ return AABB (GetComponent (Transform).GetPosition (), Vector3f::zero);
+ }
+}
+
+void Collider::Cleanup ()
+{
+ if (m_Shape)
+ {
+ AssertIf (GetClassID() == 143);
+
+ NxCCDSkeleton *skel = m_Shape->getCCDSkeleton();
+
+ if (m_Shape->getActor ().userData)
+ m_Shape->getActor ().releaseShape (*m_Shape);
+ else
+ GetDynamicsScene ().releaseActor (m_Shape->getActor ());
+
+ // Need to release skeleton after the actor or shape using it is release by physics,
+ // so we don't get an error about releasing a ccd mesh which is in use.
+ if (skel)
+ GetDynamicsSDK().releaseCCDSkeleton (*skel);
+
+ m_Shape = NULL;
+ }
+}
+
+void Collider::ReCreate()
+{
+ if( !m_Shape )
+ return;
+ Cleanup();
+ Create(NULL);
+}
+
+PPtr<PhysicMaterial> Collider::GetMaterial ()
+{
+ return m_Material;
+}
+
+void Collider::SetMaterial (PPtr<PhysicMaterial> material)
+{
+ if (!SupportsMaterial())
+ ErrorStringObject ("Setting the Material property is not supported for Colliders of type " + GetClassName() + ".", this);
+
+ if (m_Material != material)
+ {
+ SetDirty ();
+ m_Material = material;
+ }
+
+ if (m_Shape)
+ m_Shape->setMaterial (GetMaterialIndex ());
+}
+
+void Collider::TransformChanged (int changeMask)
+{
+ if (m_Shape)
+ {
+ if (changeMask & Transform::kParentingChanged)
+ {
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_4_a1))
+ {
+ // Only recreate if the rigidbody attachement is actually changed by this.
+ // Otherwise we might unnecessarily cause trigger state to reset.
+ Rigidbody* body = (Rigidbody*)m_Shape->getActor ().userData;
+ Rigidbody* newBody = FindNewAttachedRigidbody (NULL);
+ if (newBody != body)
+ ReCreate();
+ }
+ else if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_2_a1))
+ ReCreate();
+ }
+ }
+}
+
+void Collider::ClosestPointOnBounds (const Vector3f& position, Vector3f& outPosition, float& outSqrDistance)
+{
+ outSqrDistance = std::numeric_limits<float>::infinity();
+
+ if (m_Shape)
+ {
+ NxBounds3 bounds;
+ m_Shape->getWorldBounds(bounds);
+ AABB aabb;
+ bounds.getCenter((NxVec3&)aabb.GetCenter());
+ bounds.getExtents((NxVec3&)aabb.GetExtent());
+
+ CalculateClosestPoint(position, aabb, outPosition, outSqrDistance);
+ }
+ else
+ {
+ outPosition = GetComponent(Transform).GetPosition();
+ outSqrDistance = SqrMagnitude(position - outPosition);
+ }
+}
+
+bool Collider::Raycast (const Ray& ray, float distance, RaycastHit& outHit)
+{
+ AssertIf (!IsNormalized (ray.GetDirection ()));
+
+ if (distance == std::numeric_limits<float>::infinity())
+ distance = NX_MAX_F32;
+
+ NxRaycastHit hit;
+ if (m_Shape && m_Shape->raycast ((NxRay&)ray, distance, 0xffffffff, hit, false))
+ {
+ NxToRaycastHit(hit, outHit);
+ return true;
+ }
+ else
+ return false;
+}
+
+#if UNITY_EDITOR
+void Collider::RefreshPhysicsInEditMode()
+{
+ if ( !IsWorldPlaying() )
+ {
+ GetPhysicsManager().RefreshWhenPaused();
+ }
+}
+#endif
+
+template<class TransferFunction>
+void Collider::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ if (SupportsMaterial())
+ TRANSFER_SIMPLE (m_Material);
+
+ TRANSFER_SIMPLE (m_IsTrigger);
+ transfer.Transfer (m_Enabled, "m_Enabled", kHideInEditorMask | kEditorDisplaysCheckBoxMask);
+ transfer.Align();
+}
+
+void Collider::InitializeClass ()
+{
+ REGISTER_MESSAGE_VOID (Collider, kLayerChanged, SetupLayer);
+ REGISTER_MESSAGE_VOID (Collider, kForceRecreateCollider, CreateWithoutIgnoreAttach);
+ REGISTER_MESSAGE (Collider, kTransformChanged, TransformChanged, int);
+}
+
+
+#if ENABLE_SCRIPTING
+ScriptingObjectPtr ConvertContactToMono (Collision* input)
+{
+ Collision& contact = *reinterpret_cast<Collision*> (input);
+ MonoCollision monoContact;
+ if (contact.flipped)
+ {
+ monoContact.rigidbody = Scripting::ScriptingWrapperFor (contact.thisRigidbody);
+ monoContact.collider = Scripting::ScriptingWrapperFor (contact.thisCollider);
+ monoContact.relativeVelocity = contact.relativeVelocity;
+ }
+ else
+ {
+ monoContact.rigidbody = Scripting::ScriptingWrapperFor (contact.otherRigidbody);
+ monoContact.collider = Scripting::ScriptingWrapperFor (contact.otherCollider);
+ monoContact.relativeVelocity = -contact.relativeVelocity;
+ }
+ ScriptingArrayPtr contacts = CreateScriptingArray<MonoContactPoint>(GetMonoManager ().GetCommonClasses ().contactPoint,contact.contacts.size());
+ monoContact.contacts = contacts;
+ int j = 0;
+ for (Collision::Contacts::iterator i=contact.contacts.begin ();i != contact.contacts.end ();i++)
+ {
+ #if UNITY_WINRT
+ MonoContactPoint contactPoint;
+ #else
+ MonoContactPoint& contactPoint = Scripting::GetScriptingArrayElement<MonoContactPoint> (contacts, j);
+ #endif
+ contactPoint.point = i->point;
+ if (contact.flipped)
+ {
+ contactPoint.thisCollider = Scripting::ScriptingWrapperFor (i->collider[1]);
+ contactPoint.otherCollider = Scripting::ScriptingWrapperFor (i->collider[0]);
+ contactPoint.normal = -i->normal;
+ }
+ else
+ {
+ contactPoint.thisCollider = Scripting::ScriptingWrapperFor (i->collider[0]);
+ contactPoint.otherCollider = Scripting::ScriptingWrapperFor (i->collider[1]);
+ contactPoint.normal = i->normal;
+ }
+ #if UNITY_WINRT
+ // A slower way to set a value in the array:
+ // * we create a scripting object;
+ // * then marshal data from contactPoint to that scripting object
+ // * and only then we're setting it in the array
+ // At the moment there's no other way, unless we remove all ScriptingObjectPtr from MonoContactPoint
+ Scripting::SetScriptingArrayElement(contacts, j, CreateScriptingObjectFromNativeStruct<MonoContactPoint>(GetMonoManager ().GetCommonClasses ().contactPoint, contactPoint));
+ #endif
+ j++;
+ }
+ return CreateScriptingObjectFromNativeStruct<MonoCollision>(GetMonoManager ().GetCommonClasses ().collision, monoContact);
+}
+#endif //ENABLE_SCRIPTING
+IMPLEMENT_CLASS_HAS_INIT (Collider)
+IMPLEMENT_OBJECT_SERIALIZE (Collider)
+INSTANTIATE_TEMPLATE_TRANSFER (Collider)
+
+#endif //ENABLE_PHYSICS
diff --git a/Runtime/Dynamics/Collider.h b/Runtime/Dynamics/Collider.h
new file mode 100644
index 0000000..ff109e1
--- /dev/null
+++ b/Runtime/Dynamics/Collider.h
@@ -0,0 +1,168 @@
+#ifndef COLLIDER_H
+#define COLLIDER_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Profiler/Profiler.h"
+
+class Transform;
+class Matrix4x4f;
+class Vector3f;
+
+class Rigidbody;
+class NxShape;
+class NxShapeDesc;
+class NxCCDSkeleton;
+class PhysicMaterial;
+namespace Unity { class InteractiveCloth; }
+struct RaycastHit;
+class Ray;
+
+#if ENABLE_PROFILER
+#define PROFILE_MODIFY_STATIC_COLLIDER if (m_Shape && m_Shape->getActor ().userData == NULL) { PROFILER_AUTO(gStaticColliderModify, this); }
+extern ProfilerInformation gStaticColliderModify;
+extern ProfilerInformation gStaticColliderMove;
+extern ProfilerInformation gStaticColliderCreate;
+extern ProfilerInformation gDynamicColliderCreate;
+#else
+#define PROFILE_MODIFY_STATIC_COLLIDER
+#endif
+
+class Collider : public Unity::Component
+{
+ public:
+ REGISTER_DERIVED_ABSTRACT_CLASS (Collider, Component)
+ DECLARE_OBJECT_SERIALIZE (Collider)
+
+ Collider (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~Collider (); declared-in-macro
+
+ virtual void Deactivate (DeactivateOperation operation);
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ /// Enable or disable updates of this behaviour
+ virtual void SetEnabled (bool enab);
+ bool GetEnabled () const { return m_Enabled; }
+
+ virtual bool SupportsMaterial () const { return true; }
+ PPtr<PhysicMaterial> GetMaterial ();
+ void SetMaterial (PPtr<PhysicMaterial> material);
+
+ virtual void SetIsTrigger (bool trigger);
+ bool GetIsTrigger () { return m_IsTrigger; }
+
+ Rigidbody* GetRigidbody ();
+
+ static void InitializeClass ();
+ static void CleanupClass () {}
+ void SetupLayer ();
+
+ virtual void SupportedMessagesDidChange (int supported);
+
+ virtual AABB GetBounds ();
+
+ void ClosestPointOnBounds (const Vector3f& position, Vector3f& outPosition, float& outSqrDistance);
+
+ void SetupIgnoreLayer ();
+
+ bool Raycast (const Ray& ray, float distance, RaycastHit& outHit);
+
+ public:
+
+ // SUBCLASSES OVERRIDE THIS
+ virtual void Create (const Rigidbody* ignoreAttachRigidbody) = 0;
+ virtual void ScaleChanged () = 0;
+ virtual void Cleanup ();
+ virtual void ReCreate();
+
+ void CreateWithoutIgnoreAttach ();
+
+ NxShape* CreateShapeIfNeeded();
+
+ // Testing API
+ NxShape* GetShape() { return m_Shape; }
+
+ protected:
+
+ enum { kForceUpdateMass = 1 << 31 };
+ virtual bool GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix);
+ static bool GetRelativeToParentPositionAndRotationUtility (Transform& transform, Transform& anyParent, const Vector3f& localOffset, Matrix4x4f& matrix);
+
+ bool HasActorRigidbody ();
+ int GetMaterialIndex ();
+
+ void RecreateCollider (const Rigidbody* ignoreRigidbody);
+
+ virtual void TransformChanged (int changeMask);
+
+ virtual void FetchPoseFromTransform ();
+ void FetchPoseFromTransformUtility (const Vector3f& offset);
+
+ void FinalizeCreate( NxShapeDesc& shape, bool dontSetMaterial, const Rigidbody* dontAttachToRigidbody );
+ virtual NxCCDSkeleton* CreateCCDSkeleton(float scale) { return NULL; }
+ NxCCDSkeleton* CreateCCDSkeleton();
+ void UpdateCCDSkeleton();
+
+ void RigidbodyMassDistributionChanged ();
+
+ #if UNITY_EDITOR
+ void RefreshPhysicsInEditMode();
+ #else
+ void RefreshPhysicsInEditMode() {}
+ #endif
+
+ /// Finds the rigid body we want this collider to attach to.
+ /// If this returns null the collider will create a kinematic actor.
+ Rigidbody* FindNewAttachedRigidbody (const Rigidbody* ignoreAttachRigidbody);
+
+ PPtr<PhysicMaterial> m_Material;
+ NxShape* m_Shape;
+ bool m_IsTrigger;
+ bool m_Enabled;
+
+ friend class Rigidbody;
+ friend class Unity::InteractiveCloth;
+ friend class PhysicsManager;
+};
+
+
+ /*
+ PhysicMaterial& GetInstantiatedMaterial ()
+ {
+ PhysicMaterial* material = m_Material;
+ if (material)
+ {
+ if (material->m_Owner == PPtr<Object> (this))
+ return material;
+
+
+ }
+ else
+ {
+ material = new PhysicMaterial ();
+ material->Reset ();
+ material->m_Owner = this;
+ m_Material = material;
+ }
+ }*/
+struct MonoContactPoint
+{
+ Vector3f point;
+ Vector3f normal;
+ ScriptingObjectPtr thisCollider;
+ ScriptingObjectPtr otherCollider;
+};
+
+struct MonoCollision
+{
+ Vector3f relativeVelocity;
+ ScriptingObjectPtr rigidbody;
+ ScriptingObjectPtr collider;
+ ScriptingArrayPtr contacts;
+};
+
+
+struct Collision;
+ScriptingObjectPtr ConvertContactToMono (Collision* input);
+
+#endif
diff --git a/Runtime/Dynamics/CollisionMeshData.cpp b/Runtime/Dynamics/CollisionMeshData.cpp
new file mode 100644
index 0000000..d267495
--- /dev/null
+++ b/Runtime/Dynamics/CollisionMeshData.cpp
@@ -0,0 +1,121 @@
+#include "UnityPrefix.h"
+#include "CollisionMeshData.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Profiler/Profiler.h"
+//#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+//#include "Runtime/Dynamics/PhysicsManager.h"
+#include "Runtime/Dynamics/nxmemorystream.h"
+//#include "External/PhysX/builds/SDKs/Cooking/include/NxCooking.h"
+#include "Runtime/Interfaces/IPhysics.h"
+//#include "Runtime/Dynamics/ExtractDataFromMesh.h"
+
+CollisionMeshData::CollisionMeshData ()
+{
+ m_NxConvexMesh = NULL;
+ m_NxTriangleMesh = NULL;
+ m_SharedPhysicsMeshDirty = false;
+}
+
+CollisionMeshData::~CollisionMeshData ()
+{
+ Cleanup ();
+}
+
+void CollisionMeshData::Cleanup ()
+{
+ if (m_NxTriangleMesh)
+ {
+ GetIPhysics()->ReleaseNxTriangleMesh(*reinterpret_cast<NxTriangleMesh*> (m_NxTriangleMesh));
+ m_NxTriangleMesh = NULL;
+ }
+
+ if (m_NxConvexMesh)
+ {
+ GetIPhysics()->ReleaseNxConvexMesh(*reinterpret_cast<NxConvexMesh*> (m_NxConvexMesh));
+ m_NxConvexMesh = NULL;
+ }
+}
+
+void CollisionMeshData::VertexDataHasChanged ()
+{
+ if (m_NxConvexMesh != NULL || m_NxTriangleMesh != NULL)
+ m_SharedPhysicsMeshDirty = true;
+}
+
+void* CollisionMeshData::GetSharedNxMesh (Mesh& meshData)
+{
+#if ENABLE_PHYSICS
+ if (m_NxTriangleMesh != NULL)
+ return m_NxTriangleMesh;
+ Matrix4x4f identity; identity.SetIdentity();
+ m_NxTriangleMesh = GetIPhysics()->CreateNxMeshFromUnityMesh(&meshData, false, identity, kNoScaleTransform);
+#endif
+ return m_NxTriangleMesh;
+}
+
+
+void* CollisionMeshData::GetSharedNxConvexMesh (Mesh& meshData)
+{
+#if ENABLE_PHYSICS
+ if (m_NxConvexMesh != NULL)
+ return m_NxConvexMesh;
+ Matrix4x4f identity; identity.SetIdentity();
+ m_NxConvexMesh = GetIPhysics()->CreateNxMeshFromUnityMesh(&meshData, true, identity, kNoScaleTransform);
+#endif
+ return m_NxConvexMesh;
+}
+
+void CollisionMeshData::AwakeFromLoadThreaded(Mesh& meshData)
+{
+#if !USE_PREBAKED_COLLISIONMESH && ENABLE_PHYSICS
+ int meshUsageFlags = meshData.GetMeshUsageFlags();
+ IPhysics* physics = GetIPhysics();
+
+ if (meshUsageFlags & kRequiresSharedTriangleCollisionMesh)
+ m_NxTriangleMesh = physics->CreateNxStreamFromUnityMesh(meshData, false);
+
+ if (meshUsageFlags & kRequiresSharedConvexCollisionMesh)
+ m_NxConvexMesh = physics->CreateNxStreamFromUnityMesh(meshData, true);
+#endif
+}
+
+void CollisionMeshData::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ // Mark the shared physics mesh dirty when we have a mesh representation already and we are modifying the mesh (as opposed to just loading it from disk)
+ if ((m_NxConvexMesh != NULL || m_NxTriangleMesh != NULL) && (awakeMode & kDidLoadFromDisk) == 0 )
+ m_SharedPhysicsMeshDirty = true;
+
+ IPhysics* physics = GetIPhysics();
+
+#if USE_PREBAKED_COLLISIONMESH
+ if (!m_BakedConvexCollisionMesh.empty())
+ {
+ m_NxConvexMesh = physics->CreateNxMeshFromByteStream(TRUE,m_BakedConvexCollisionMesh);
+ m_BakedConvexCollisionMesh.clear();
+ }
+ if (!m_BakedTriangleCollisionMesh.empty())
+ {
+ m_NxTriangleMesh = physics->CreateNxMeshFromByteStream(FALSE,m_BakedTriangleCollisionMesh);
+ m_BakedTriangleCollisionMesh.clear();
+ }
+#else
+
+ if (awakeMode & kDidLoadThreaded)
+ {
+ if (m_NxTriangleMesh)
+ {
+ MemoryStream* stream = reinterpret_cast<MemoryStream*>(m_NxTriangleMesh);
+ m_NxTriangleMesh = physics->CreateNxMeshFromNxStream (false, *stream);
+ physics->DeleteMemoryStream(stream);
+ }
+
+ if (m_NxConvexMesh)
+ {
+ MemoryStream* stream = reinterpret_cast<MemoryStream*>(m_NxConvexMesh);
+ m_NxConvexMesh = physics->CreateNxMeshFromNxStream (true, *stream);
+ physics->DeleteMemoryStream(stream);
+ }
+ }
+#endif
+}
diff --git a/Runtime/Dynamics/CollisionMeshData.h b/Runtime/Dynamics/CollisionMeshData.h
new file mode 100644
index 0000000..95aa6d9
--- /dev/null
+++ b/Runtime/Dynamics/CollisionMeshData.h
@@ -0,0 +1,87 @@
+#ifndef COLLISION_MESH_DATA
+#define COLLISION_MESH_DATA
+
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Editor/Interfaces/IPhysicsEditor.h"
+
+class Mesh;
+
+enum
+{
+ kMeshMustKeepVertexAndIndexData = 1 << 0,
+ kRequiresSharedConvexCollisionMesh = 1 << 1, // Mesh can be shared and should be precomputed on standalone platforms
+ kRequiresSharedTriangleCollisionMesh = 1 << 2, // Mesh can be shared and should be precomputed on standalone platforms
+ kRequiresScaledCollisionMesh = 1 << 3 // Mesh is used as mesh collider but is scaled
+};
+
+class EXPORT_COREMODULE CollisionMeshData
+{
+ void* m_NxConvexMesh;
+ void* m_NxTriangleMesh;
+ bool m_SharedPhysicsMeshDirty;
+
+public:
+
+#if USE_PREBAKED_COLLISIONMESH
+ dynamic_array<UInt8> m_BakedTriangleCollisionMesh;
+ dynamic_array<UInt8> m_BakedConvexCollisionMesh;
+#endif
+
+ CollisionMeshData ();
+ ~CollisionMeshData ();
+
+ void Cleanup ();
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer, Mesh& mesh);
+
+ void VertexDataHasChanged ();
+
+ void AwakeFromLoad (AwakeFromLoadMode awake);
+ void AwakeFromLoadThreaded(Mesh& meshData);
+
+ void* GetSharedNxMesh (Mesh& mesh);
+ void* GetSharedNxConvexMesh (Mesh& mesh);
+
+ bool IsSharedPhysicsMeshDirty () { return m_SharedPhysicsMeshDirty; }
+
+};
+
+EXPORT_COREMODULE void* CreateNxMeshFromUnityMesh (Mesh* mesh, bool convex, const Matrix4x4f& scalematrix, TransformType transformType );
+
+template<class TransferFunction>
+inline void CollisionMeshData::Transfer (TransferFunction& transfer, Mesh& mesh)
+{
+#if UNITY_EDITOR
+ // When building player we precalcuate mesh usage based on who uses the different MeshColliders in different scenes.
+ if (transfer.IsWritingGameReleaseData())
+ {
+ int buildMeshUsageFlags = transfer.GetBuildUsage().meshUsageFlags;
+
+ // Bake physX meshes
+ if (transfer.GetFlags() & kGenerateBakedPhysixMeshes)
+ {
+ IPhysicsEditor* physicsEditor = GetIPhysicsEditor();
+ Assert(physicsEditor != NULL) ;
+
+ dynamic_array<UInt8> bakedConvex;
+ if (buildMeshUsageFlags & kRequiresSharedConvexCollisionMesh)
+ physicsEditor->BakeMesh (&mesh, true, ShouldSerializeForBigEndian(transfer), bakedConvex);
+ transfer.Transfer (bakedConvex, "m_BakedConvexCollisionMesh", kHideInEditorMask);
+
+ dynamic_array<UInt8> bakedConcave;
+ if (buildMeshUsageFlags & kRequiresSharedTriangleCollisionMesh)
+ physicsEditor->BakeMesh (&mesh, false, ShouldSerializeForBigEndian(transfer), bakedConcave);
+ transfer.Transfer (bakedConcave, "m_BakedTriangleCollisionMesh", kHideInEditorMask);
+ }
+ }
+#endif
+#if USE_PREBAKED_COLLISIONMESH
+ transfer.Transfer (m_BakedConvexCollisionMesh, "m_BakedConvexCollisionMesh", kHideInEditorMask);
+ transfer.Transfer (m_BakedTriangleCollisionMesh, "m_BakedTriangleCollisionMesh", kHideInEditorMask);
+#endif
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/Dynamics/ConfigurableJoint.cpp b/Runtime/Dynamics/ConfigurableJoint.cpp
new file mode 100644
index 0000000..b036e24
--- /dev/null
+++ b/Runtime/Dynamics/ConfigurableJoint.cpp
@@ -0,0 +1,479 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "ConfigurableJoint.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "PhysicsManager.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "NxWrapperUtility.h"
+
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+
+using namespace std;
+
+namespace Unity
+{
+
+#define GET_JOINT() static_cast<NxD6Joint*> (m_Joint)
+
+
+/*
+- We awake the hingejoint only once. (AwakeFromLoad)
+ At this point we setup the axes. They are never changed afterwards
+ -> The perfect solution remembers the old position/rotation of the rigid bodies.
+ Then when changing axis/anchor is changed it generates axes that are rleative to the old position/rotation state!
+*/
+
+void ConfigurableJoint::InitializeClass ()
+{
+ #if UNITY_EDITOR
+ // Only introduced during 2.0 alpha
+ RegisterAllowNameConversion (ConfigurableJoint::GetClassStringStatic(), "m_ConfigureInWorldSpace", "m_ConfiguredInWorldSpace");
+ #endif
+}
+
+
+
+ConfigurableJoint::ConfigurableJoint (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+ConfigurableJoint::~ConfigurableJoint ()
+{
+}
+
+void ConfigurableJoint::CalculateGlobalHingeSpace (Vector3f& globalAnchor, Vector3f& globalAxis, Vector3f& globalNormal) const
+{
+ const Transform& transform = GetComponent (Transform);
+
+ Vector3f localAxis = m_Axis;
+ if (SqrMagnitude (localAxis) < Vector3f::epsilon)
+ localAxis = Vector3f (1.0F, 0.0F, 0.0F);
+ Vector3f localNormal = m_SecondaryAxis;
+
+ OrthoNormalize (&localAxis, &localNormal);
+
+ globalAnchor = transform.TransformPoint (m_Anchor);
+ //Vector3f globalRigidbodyPos = transform.GetPosition ();
+
+ if (m_ConfiguredInWorldSpace)
+ {
+ globalAxis = localAxis;
+ globalNormal = localNormal;
+ }
+ else
+ {
+ globalAxis = transform.TransformDirection (localAxis);
+ globalNormal = transform.TransformDirection (localNormal);
+ }
+// Matrix3x3f m;
+// m.SetOrthoNormalBasisInverse(globalAxis, globalNormal, Cross (globalAxis, globalNormal));
+// m.SetOrthoNormalBasisInverse(Vector3f::xAxis, globalNormal, Cross (globalAxis, globalNormal));
+// m.SetOrthoNormalBasisInverse(globalNormal, Cross (globalAxis, globalNormal), globalAxis);
+// OrthoNormalize(m);
+// Quaternionf q;
+// MatrixToQuaternion(m, q);
+// m_ConfigurationSpace = q * Inverse(transform.GetRotation ());
+}
+
+void ConfigurableJoint::Reset ()
+{
+ Super::Reset();
+
+ m_XMotion = NX_D6JOINT_MOTION_FREE;
+ m_YMotion = NX_D6JOINT_MOTION_FREE;
+ m_ZMotion = NX_D6JOINT_MOTION_FREE;
+
+ m_AngularXMotion = NX_D6JOINT_MOTION_FREE;
+ m_AngularYMotion = NX_D6JOINT_MOTION_FREE;
+ m_AngularZMotion = NX_D6JOINT_MOTION_FREE;
+
+ InitSoftJointLimit(m_LinearLimit);
+ InitSoftJointLimit(m_LowAngularXLimit);
+ InitSoftJointLimit(m_HighAngularXLimit);
+ InitSoftJointLimit(m_AngularYLimit);
+ InitSoftJointLimit(m_AngularZLimit);
+
+ InitJointDrive(m_XDrive);
+ InitJointDrive(m_YDrive);
+ InitJointDrive(m_ZDrive);
+ InitJointDrive(m_AngularXDrive);
+ InitJointDrive(m_AngularYZDrive);
+ InitJointDrive(m_SlerpDrive);
+
+ m_ProjectionMode = 0;
+ m_ProjectionDistance = 0.1F;
+ m_ProjectionAngle = 5.0F;
+// m_GearRatio = 1.0F;
+ m_RotationDriveMode = 0;
+// m_UseGear = false;
+ m_TargetAngularVelocity = m_TargetVelocity = m_TargetPosition = Vector3f(0.0F, 0.0F, 0.0F);
+ m_TargetRotation = Quaternionf::identity();
+ m_SecondaryAxis = Vector3f::yAxis;
+ m_ConfiguredInWorldSpace = false;
+ m_SwapBodies = false;
+}
+
+void ConfigurableJoint::CheckConsistency ()
+{
+ Super::CheckConsistency();
+}
+
+void ConfigurableJoint::SetXMotion (int motion)
+{
+ m_XMotion = motion;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetYMotion (int motion)
+{
+ m_YMotion = motion;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetAngularXMotion (int motion)
+{
+ m_AngularXMotion = motion;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetAngularYMotion (int motion)
+{
+ m_AngularYMotion = motion;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetAngularZMotion (int motion)
+{
+ m_AngularZMotion = motion;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetZMotion (int motion)
+{
+ m_ZMotion = motion;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetLinearLimit (const SoftJointLimit& limit)
+{
+ m_LinearLimit = limit;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetAngularZLimit (const SoftJointLimit& limit)
+{
+ m_AngularZLimit = limit;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetAngularYLimit (const SoftJointLimit& limit)
+{
+ m_AngularYLimit = limit;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetLowAngularXLimit (const SoftJointLimit& limit)
+{
+ m_LowAngularXLimit = limit;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetHighAngularXLimit (const SoftJointLimit& limit)
+{
+ m_HighAngularXLimit = limit;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetXDrive (const JointDrive& drive)
+{
+ m_XDrive = drive;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetYDrive (const JointDrive& drive)
+{
+ m_YDrive = drive;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetZDrive (const JointDrive& drive)
+{
+ m_ZDrive = drive;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetAngularXDrive (const JointDrive& drive)
+{
+ m_AngularXDrive = drive;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetAngularYZDrive (const JointDrive& drive)
+{
+ m_AngularYZDrive = drive;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetSlerpDrive (const JointDrive& drive)
+{
+ m_SlerpDrive = drive;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetRotationDriveMode (int mode)
+{
+ m_RotationDriveMode = mode;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetProjectionMode (int mode)
+{
+ m_ProjectionMode = mode;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetProjectionDistance (float dist)
+{
+ m_ProjectionDistance = dist;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetProjectionAngle (float angle)
+{
+ m_ProjectionAngle = angle;
+ ApplyKeepConfigurationSpace();
+}
+
+void ConfigurableJoint::SetConfiguredInWorldSpace (bool c)
+{
+ m_ConfiguredInWorldSpace = c;
+ ApplyRebuildConfigurationSpace();
+}
+
+void ConfigurableJoint::SetSwapBodies (bool c)
+{
+ m_SwapBodies = c;
+ ApplyRebuildConfigurationSpace();
+}
+
+void ConfigurableJoint::SetTargetPosition (const Vector3f& pos)
+{
+ m_TargetPosition = pos;
+ if (m_Joint)
+ GET_JOINT()->setDrivePosition(Vec3ToNx(pos));
+}
+
+void ConfigurableJoint::SetTargetRotation (const Quaternionf& rotation)
+{
+ m_TargetRotation = rotation;
+ if (m_Joint)
+ GET_JOINT()->setDriveOrientation((const NxQuat&)rotation);
+}
+
+void ConfigurableJoint::SetTargetVelocity (const Vector3f& vel)
+{
+ m_TargetVelocity = vel;
+ if (m_Joint)
+ GET_JOINT()->setDriveLinearVelocity(Vec3ToNx(vel));
+}
+
+void ConfigurableJoint::SetTargetAngularVelocity (const Vector3f& angular)
+{
+ m_TargetAngularVelocity = angular;
+ if (m_Joint)
+ GET_JOINT()->setDriveAngularVelocity(Vec3ToNx(angular));
+}
+
+/*
+- When the rigid body which is attached with the hinge joint is activated after the joint is loaded
+ It will not be connected with the joint!
+*/
+
+template<class TransferFunction>
+void ConfigurableJoint::Transfer (TransferFunction& transfer)
+{
+ JointTransferPre (transfer);
+ TRANSFER (m_SecondaryAxis);
+
+ TRANSFER(m_XMotion);
+ TRANSFER(m_YMotion);
+ TRANSFER(m_ZMotion);
+ TRANSFER(m_AngularXMotion);
+ TRANSFER(m_AngularYMotion);
+ TRANSFER(m_AngularZMotion);
+
+ TRANSFER(m_LinearLimit);
+ TRANSFER(m_LowAngularXLimit);
+ TRANSFER(m_HighAngularXLimit);
+ TRANSFER(m_AngularYLimit);
+ TRANSFER(m_AngularZLimit);
+
+ TRANSFER(m_TargetPosition);
+ TRANSFER(m_TargetVelocity);
+
+ TRANSFER(m_XDrive);
+ TRANSFER(m_YDrive);
+ TRANSFER(m_ZDrive);
+
+ TRANSFER(m_TargetRotation);
+ TRANSFER(m_TargetAngularVelocity);
+
+ TRANSFER(m_RotationDriveMode);
+
+ TRANSFER(m_AngularXDrive);
+ TRANSFER(m_AngularYZDrive);
+ TRANSFER(m_SlerpDrive);
+
+ TRANSFER(m_ProjectionMode);
+ TRANSFER(m_ProjectionDistance);
+ TRANSFER(m_ProjectionAngle);
+
+ TRANSFER(m_ConfiguredInWorldSpace);
+ TRANSFER(m_SwapBodies);
+ transfer.Align();
+
+ JointTransferPost (transfer);
+}
+
+void ConfigurableJoint::ApplyKeepConfigurationSpace()
+{
+ SetDirty();
+ if (m_Joint)
+ {
+ // Joint has NX_JS_UNBOUND state, when it's created, but physics aren't simulated yet.
+ // For ex., if you create ConfigurableJoint from a script, right after creation it will have such state.
+ if (m_Joint->getState () == NX_JS_SIMULATING || m_Joint->getState () == NX_JS_UNBOUND)
+ {
+ NxD6JointDesc desc;
+ GET_JOINT()->saveToDesc (desc);
+ SetupD6Desc(desc);
+ GET_JOINT()->loadFromDesc (desc);
+ }
+ }
+}
+
+void ConfigurableJoint::ApplyRebuildConfigurationSpace()
+{
+ SetDirty();
+ if (m_Joint)
+ {
+ GetDynamicsScene ().releaseJoint (*m_Joint);
+ m_Joint = NULL;
+ }
+ Create();
+}
+
+
+void ConfigurableJoint::SetupD6Desc (NxD6JointDesc& desc)
+{
+ desc.xMotion = (NxD6JointMotion)m_XMotion;
+ desc.yMotion = (NxD6JointMotion)m_YMotion;
+ desc.zMotion = (NxD6JointMotion)m_ZMotion;
+
+ desc.swing1Motion = (NxD6JointMotion)m_AngularYMotion;
+ desc.swing2Motion = (NxD6JointMotion)m_AngularZMotion;
+ desc.twistMotion = (NxD6JointMotion)m_AngularXMotion;
+
+ ConvertSoftLimitLinear(m_LinearLimit, desc.linearLimit);
+ ConvertSoftLimit(m_AngularYLimit, desc.swing1Limit);
+ ConvertSoftLimit(m_AngularZLimit, desc.swing2Limit);
+ ConvertSoftLimit(m_LowAngularXLimit, desc.twistLimit.low);
+ ConvertSoftLimit(m_HighAngularXLimit, desc.twistLimit.high);
+
+ if (desc.twistLimit.low.value > desc.twistLimit.high.value)
+ swap (desc.twistLimit.low, desc.twistLimit.high);
+
+ ConvertDrive(m_XDrive, desc.xDrive);
+ ConvertDrive(m_YDrive, desc.yDrive);
+ ConvertDrive(m_ZDrive, desc.zDrive);
+
+ ConvertDrive(m_AngularXDrive, desc.twistDrive);
+ ConvertDrive(m_AngularYZDrive, desc.swingDrive);
+ ConvertDrive(m_SlerpDrive, desc.slerpDrive);
+
+ desc.projectionMode = (NxJointProjectionMode)m_ProjectionMode;
+ desc.projectionDistance = m_ProjectionDistance;
+ desc.projectionAngle = Deg2Rad(m_ProjectionAngle);
+// desc.gearRatio = m_GearRatio;
+
+ desc.flags = 0;
+ if (m_RotationDriveMode)
+ desc.flags |= NX_D6JOINT_SLERP_DRIVE;
+
+// if (m_UseGear)
+// desc.flags |= NX_D6JOINT_GEAR_ENABLED;
+
+ desc.driveAngularVelocity = Vec3ToNx(m_TargetAngularVelocity);
+ desc.driveOrientation = (NxQuat&)m_TargetRotation;
+ desc.driveLinearVelocity = Vec3ToNx(m_TargetVelocity);
+ desc.drivePosition = Vec3ToNx(m_TargetPosition);
+}
+
+void ConfigurableJoint::Create ()
+{
+ AssertIf (!IsActive ());
+
+ NxD6JointDesc desc;
+
+ if (m_Joint && m_Joint->getState () == NX_JS_SIMULATING)
+ GET_JOINT()->saveToDesc (desc);
+
+ SetupD6Desc (desc);
+ FinalizeCreateD6 (desc);
+}
+
+void ConfigurableJoint::FinalizeCreateD6 (NxD6JointDesc& desc)
+{
+ bool swapBodies = m_SwapBodies || (m_ConfiguredInWorldSpace && !IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_4_a1));
+ FinalizeCreateImpl (desc, swapBodies);
+ if (swapBodies)
+ {
+ swap(desc.localNormal[0], desc.localNormal[1]);
+ swap(desc.localAxis[0], desc.localAxis[1]);
+ swap(desc.localAnchor[0], desc.localAnchor[1]);
+ }
+
+ if (GET_JOINT ())
+ {
+ GET_JOINT ()->loadFromDesc (desc);
+
+ // When actors change, we can't use loadFromDesc on the same joint.
+ // Thus recreate the joint
+ if (m_Joint && m_Joint->getState () == NX_JS_BROKEN)
+ {
+ GetDynamicsScene ().releaseJoint (*m_Joint);
+ m_Joint = GetDynamicsScene ().createJoint (desc);
+ }
+ }
+ else
+ m_Joint = GetDynamicsScene ().createJoint (desc);
+}
+
+
+void ConfigurableJoint::SetSecondaryAxis (const Vector3f& axis)
+{
+ SetDirty ();
+ m_SecondaryAxis = axis;
+ if (IsActive () && GET_JOINT())
+ {
+ NxD6JointDesc desc;
+ AssertIf (m_Joint->getState () == NX_JS_BROKEN);
+ GET_JOINT()->saveToDesc (desc);
+
+ SetupAxes (desc, kChangeAxis);
+ GET_JOINT()->loadFromDesc (desc);
+ AssertIf (m_Joint->getState () == NX_JS_BROKEN);
+ }
+}
+
+IMPLEMENT_AXIS_ANCHOR(ConfigurableJoint,NxD6JointDesc)
+
+}
+
+IMPLEMENT_CLASS_HAS_INIT (ConfigurableJoint)
+IMPLEMENT_OBJECT_SERIALIZE (ConfigurableJoint)
+#endif //ENABLE_PHSYICS
diff --git a/Runtime/Dynamics/ConfigurableJoint.h b/Runtime/Dynamics/ConfigurableJoint.h
new file mode 100644
index 0000000..9843f4d
--- /dev/null
+++ b/Runtime/Dynamics/ConfigurableJoint.h
@@ -0,0 +1,180 @@
+#ifndef CONFIGURABLEJOINT_H
+#define CONFIGURABLEJOINT_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Quaternion.h"
+#include "JointDescriptions.h"
+#include "Joint.h"
+class Rigidbody;
+class NxJointDesc;
+class NxD6JointDesc;
+class NxRevoluteJoint;
+
+namespace Unity
+{
+
+class ConfigurableJoint : public Joint
+{
+ public:
+
+ REGISTER_DERIVED_CLASS (ConfigurableJoint, Joint)
+ DECLARE_OBJECT_SERIALIZE (ConfigurableJoint)
+
+ ConfigurableJoint (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void Reset();
+
+ int GetXMotion() { return m_XMotion; }
+ void SetXMotion (int motion);
+
+ int GetYMotion() { return m_YMotion; }
+ void SetYMotion (int motion);
+
+ int GetZMotion() { return m_ZMotion; }
+ void SetZMotion (int motion);
+
+ int GetAngularXMotion() { return m_AngularXMotion; }
+ void SetAngularXMotion (int motion);
+
+ int GetAngularYMotion() { return m_AngularYMotion; }
+ void SetAngularYMotion (int motion);
+
+ int GetAngularZMotion() { return m_AngularZMotion; }
+ void SetAngularZMotion (int motion);
+
+ SoftJointLimit GetLinearLimit () { return m_LinearLimit; }
+ void SetLinearLimit (const SoftJointLimit& limit);
+
+
+ SoftJointLimit GetLowAngularXLimit () { return m_LowAngularXLimit; }
+ void SetLowAngularXLimit (const SoftJointLimit& limit);
+
+ SoftJointLimit GetHighAngularXLimit () { return m_HighAngularXLimit; }
+ void SetHighAngularXLimit (const SoftJointLimit& limit);
+
+ SoftJointLimit GetAngularYLimit () { return m_AngularYLimit; }
+ void SetAngularYLimit (const SoftJointLimit& limit);
+
+ SoftJointLimit GetAngularZLimit () { return m_AngularZLimit; }
+ void SetAngularZLimit (const SoftJointLimit& limit);
+
+ JointDrive GetXDrive () { return m_XDrive; }
+ void SetXDrive (const JointDrive& drive);
+
+ JointDrive GetYDrive () { return m_YDrive; }
+ void SetYDrive (const JointDrive& drive);
+
+ JointDrive GetZDrive () { return m_ZDrive; }
+ void SetZDrive (const JointDrive& drive);
+
+ JointDrive GetAngularXDrive () { return m_AngularXDrive; }
+ void SetAngularXDrive (const JointDrive& drive);
+
+ JointDrive GetAngularYZDrive () { return m_AngularYZDrive; }
+ void SetAngularYZDrive (const JointDrive& drive);
+
+ JointDrive GetSlerpDrive () { return m_SlerpDrive; }
+ void SetSlerpDrive (const JointDrive& drive);
+
+ int GetRotationDriveMode () { return m_RotationDriveMode; }
+ void SetRotationDriveMode (int drive);
+
+// void SetUseGear (bool gear);
+// bool GetUseGear () { return m_UseGear; }
+
+ int GetProjectionMode () { return m_ProjectionMode; }
+ void SetProjectionMode (int mode);
+
+ float GetProjectionDistance () { return m_ProjectionDistance; }
+ void SetProjectionDistance (float dist);
+
+ float GetProjectionAngle () { return m_ProjectionAngle; }
+ void SetProjectionAngle (float dist);
+
+// float GetGearRatio () { return m_GearRatio; }
+// void SetGearRatio (float ratio);
+
+ Vector3f GetTargetPosition () { return m_TargetPosition; }
+ void SetTargetPosition (const Vector3f& pos);
+
+ Quaternionf GetTargetRotation () { return m_TargetRotation; }
+ void SetTargetRotation (const Quaternionf& rotation);
+
+ Vector3f GetTargetVelocity () { return m_TargetVelocity; }
+ void SetTargetVelocity (const Vector3f& vel);
+
+ Vector3f GetTargetAngularVelocity () { return m_TargetAngularVelocity; }
+ void SetTargetAngularVelocity (const Vector3f& angular);
+
+ void SetConfiguredInWorldSpace (bool c);
+ bool GetConfiguredInWorldSpace () { return m_ConfiguredInWorldSpace; }
+
+ void SetSwapBodies (bool c);
+ bool GetSwapBodies () { return m_SwapBodies; }
+
+ ////// THIS IS NOT GOOD!!!!!!!!!!!
+ void CalculateGlobalHingeSpace (Vector3f& globalAnchor, Vector3f& globalAxis, Vector3f& globalNormal) const;
+
+ void CheckConsistency ();
+
+ virtual void ApplySetupAxesToDesc (int option);
+
+ virtual void SetSecondaryAxis (const Vector3f& axis);
+ Vector3f GetSecondaryAxis () { return m_SecondaryAxis; }
+
+ static void InitializeClass ();
+ static void CleanupClass() { }
+
+ private:
+ void SetupD6Desc (NxD6JointDesc& desc);
+ void FinalizeCreateD6 (NxD6JointDesc& desc);
+ void ApplyKeepConfigurationSpace();
+ void ApplyRebuildConfigurationSpace();
+
+ virtual void Create ();
+ void SetupDriveType ();
+
+ int m_XMotion; ///< enum { Locked=0, Limited=1, Free=2 }
+ int m_YMotion; ///< enum { Locked=0, Limited=1, Free=2 }
+ int m_ZMotion; ///< enum { Locked=0, Limited=1, Free=2 }
+
+ int m_AngularXMotion; ///< enum { Locked=0, Limited=1, Free=2 }
+ int m_AngularYMotion; ///< enum { Locked=0, Limited=1, Free=2 }
+ int m_AngularZMotion; ///< enum { Locked=0, Limited=1, Free=2 }
+
+ SoftJointLimit m_LinearLimit;
+ SoftJointLimit m_LowAngularXLimit;
+ SoftJointLimit m_HighAngularXLimit;
+ SoftJointLimit m_AngularYLimit;
+ SoftJointLimit m_AngularZLimit;
+
+ JointDrive m_XDrive;
+ JointDrive m_YDrive;
+ JointDrive m_ZDrive;
+
+ JointDrive m_AngularYZDrive;
+ JointDrive m_AngularXDrive;
+ JointDrive m_SlerpDrive;
+
+ int m_ProjectionMode;///< enum { None, Position and Rotation = 1, Position Only = 2 }
+
+ float m_ProjectionDistance;
+ float m_ProjectionAngle;
+
+ int m_RotationDriveMode; ///< enum { X & YZ = 0, Slerp = 1 }
+ bool m_ConfiguredInWorldSpace;
+ bool m_SwapBodies;
+
+ Vector3f m_TargetPosition;
+ Quaternionf m_TargetRotation;
+
+ Vector3f m_TargetVelocity;
+ Vector3f m_TargetAngularVelocity;
+
+ ////// THIS IS NOT GOOD!!!!!!!!!!!
+ Vector3f m_SecondaryAxis;
+};
+
+}
+#endif
diff --git a/Runtime/Dynamics/ConstantForce.cpp b/Runtime/Dynamics/ConstantForce.cpp
new file mode 100644
index 0000000..596b6b7
--- /dev/null
+++ b/Runtime/Dynamics/ConstantForce.cpp
@@ -0,0 +1,66 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "ConstantForce.h"
+#include "RigidBody.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+using namespace Unity;
+
+IMPLEMENT_OBJECT_SERIALIZE (ConstantForce)
+IMPLEMENT_CLASS (ConstantForce)
+
+ConstantForce::ConstantForce (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_FixedUpdateNode (this)
+{
+}
+
+ConstantForce::~ConstantForce ()
+{
+}
+
+template<class TransferFunction>
+void ConstantForce::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER (m_Force);
+ TRANSFER (m_RelativeForce);
+ TRANSFER (m_Torque);
+ TRANSFER (m_RelativeTorque);
+}
+
+void ConstantForce::Reset ()
+{
+ Super::Reset ();
+ m_Force = Vector3f::zero;
+ m_RelativeForce = Vector3f::zero;
+ m_Torque = Vector3f::zero;
+ m_RelativeTorque = Vector3f::zero;
+}
+
+void ConstantForce::FixedUpdate() {
+ Rigidbody* body = QueryComponent (Rigidbody);
+ if (!body)
+ {
+ // It should be impossible to reach this case, since Unity will not allow
+ // deleting components which are required by other components. But people
+ // have somehow managed (case 506613), so better check for it.
+ ErrorStringObject ("ConstantForce requires a Rigidbody component, but non is present.", this);
+ return;
+ }
+ body->AddForce (m_Force);
+ body->AddRelativeForce (m_RelativeForce);
+ body->AddTorque (m_Torque);
+ body->AddRelativeTorque (m_RelativeTorque);
+}
+
+void ConstantForce::AddToManager ()
+{
+ GetFixedBehaviourManager ().AddBehaviour (m_FixedUpdateNode, -1);
+}
+
+void ConstantForce::RemoveFromManager ()
+{
+ GetFixedBehaviourManager ().RemoveBehaviour (m_FixedUpdateNode);
+}
+#endif //ENABLE_PHSYICS \ No newline at end of file
diff --git a/Runtime/Dynamics/ConstantForce.h b/Runtime/Dynamics/ConstantForce.h
new file mode 100644
index 0000000..8bcd90b
--- /dev/null
+++ b/Runtime/Dynamics/ConstantForce.h
@@ -0,0 +1,30 @@
+#ifndef CONSTANTFORCE_H
+#define CONSTANTFORCE_H
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Math/Vector3.h"
+
+class ConstantForce : public Behaviour
+{
+ public:
+ REGISTER_DERIVED_CLASS (ConstantForce, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (ConstantForce)
+
+ ConstantForce (MemLabelId label, ObjectCreationMode mode);
+ virtual void Reset ();
+
+ virtual void FixedUpdate ();
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+
+ Vector3f m_Force; ///< Force applied globally
+ Vector3f m_RelativeForce; ///< Force applied locally
+ Vector3f m_Torque; ///< Torque applied globally
+ Vector3f m_RelativeTorque; ///< Torque applied locally
+
+
+ BehaviourListNode m_FixedUpdateNode;
+};
+
+
+#endif
diff --git a/Runtime/Dynamics/DeformableMesh.cpp b/Runtime/Dynamics/DeformableMesh.cpp
new file mode 100644
index 0000000..6fd3de0
--- /dev/null
+++ b/Runtime/Dynamics/DeformableMesh.cpp
@@ -0,0 +1,721 @@
+#include "UnityPrefix.h"
+#include "DeformableMesh.h"
+
+#if ENABLE_CLOTH
+
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "PhysicsManager.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "External/PhysX/builds/SDKs/Cooking/include/NxCooking.h"
+#include "nxmemorystream.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/Dynamics/ExtractDataFromMesh.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+
+Rand gClothRand (1);
+
+namespace Unity {
+
+Cloth::Cloth (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+ , m_FixedUpdateNode(this)
+ , m_Indices(kMemVertexData)
+ , m_Vertices(kMemVertexData)
+ , m_Normals(kMemVertexData)
+ , m_Tangents(kMemVertexData)
+ , m_UVs(kMemVertexData)
+ , m_UV1s(kMemVertexData)
+ , m_Colors(kMemVertexData)
+ , m_TranslatedVertices(kMemVertexData)
+ , m_TranslatedNormals(kMemVertexData)
+ , m_TranslatedIndices(kMemVertexData)
+{
+ // configuration
+ m_BendingStiffness = 0.0f;
+ m_StretchingStiffness = 1.0f;
+ m_Damping = 0.0f;
+ m_Thickness = 0.2f;
+ m_UseGravity = true;
+ m_SelfCollision = false;
+ m_ExternalAcceleration = Vector3f::zero;
+ m_RandomAcceleration = Vector3f::zero;
+
+ // state
+ m_Cloth = NULL;
+ m_ClothScene = NULL;
+ m_NumVertices = 0;
+ m_NumIndices = 0;
+ m_NumParentIndices = 0;
+ m_NumVerticesFromPhysX = 0;
+ m_NumIndicesFromPhysX = 0;
+ m_SuspendCount = 0;
+ m_IsSuspended = false;
+ m_NeedToWakeUp = false;
+ m_VerticesForRendering = &m_Vertices;
+ m_NormalsForRendering = &m_Normals;
+ m_IndicesForRendering = &m_Indices;
+ m_NumVerticesForRendering = 0;
+ m_NumIndicesForRendering = 0;
+}
+
+Cloth::~Cloth ()
+{
+ Cleanup();
+}
+
+void Cloth::Reset ()
+{
+ Super::Reset();
+
+ // configuration
+ m_BendingStiffness = 0.0f;
+ m_StretchingStiffness = 1.0f;
+ m_Damping = 0.0f;
+ m_Thickness = 0.2f;
+ m_UseGravity = true;
+ m_SelfCollision = false;
+ m_ExternalAcceleration = Vector3f::zero;
+ m_RandomAcceleration = Vector3f::zero;
+}
+
+void Cloth::Cleanup ()
+{
+ if (m_Cloth)
+ {
+ NxClothMesh* mesh = m_Cloth->getClothMesh();
+ m_ClothScene->releaseCloth(*m_Cloth);
+ GetDynamicsSDK().releaseClothMesh(*mesh);
+ m_Cloth = NULL;
+ }
+ m_NumVertices = 0;
+ m_NumVerticesFromPhysX = 0;
+ m_NumVerticesForRendering = 0;
+ m_NumIndices = 0;
+ m_NumIndicesFromPhysX = 0;
+ m_NumIndicesForRendering = 0;
+}
+
+void Cloth::ProcessMeshForRenderer ()
+{
+ if (m_NumVerticesFromPhysX > m_NumVertices)
+ {
+ int newVertices = m_NumVerticesFromPhysX - m_NumVertices;
+ int size = m_NumVertices;
+ if (m_VertexTranslationTable.size())
+ {
+ size = m_VertexTranslationTable.size();
+ m_VertexTranslationTable.resize_uninitialized(size + newVertices);
+ for (int i=0; i<newVertices; i++)
+ m_VertexTranslationTable[size + i] = m_NumVertices + i;
+ }
+
+ if (!m_UVs.empty())
+ {
+ for (int i=0; i<newVertices; i++)
+ m_UVs[size+i] = m_UVs[m_ParentIndices[m_NumVertices+i]];
+ }
+
+ if (!m_UV1s.empty())
+ {
+ for (int i=0; i<newVertices; i++)
+ m_UV1s[size+i] = m_UV1s[m_ParentIndices[m_NumVertices+i]];
+ }
+
+ if (!m_Colors.empty())
+ {
+ for (int i=0; i<newVertices; i++)
+ m_Colors[size+i] = m_Colors[m_ParentIndices[m_NumVertices+i]];
+ }
+
+ if (!m_Tangents.empty())
+ {
+ for (int i=0; i<newVertices; i++)
+ m_Tangents[size+i] = m_Tangents[m_ParentIndices[m_NumVertices+i]];
+ }
+
+ m_NumVertices = m_NumVerticesFromPhysX;
+ m_NumVerticesForRendering += newVertices;
+ }
+ if (m_VertexTranslationTable.size())
+ {
+ for (int i=0; i<m_VertexTranslationTable.size(); i++)
+ {
+ int index = m_VertexTranslationTable[i];
+ m_TranslatedVertices[i] = m_Vertices[index];
+ m_TranslatedNormals[i] = m_Normals[index];
+ }
+ if (m_NumIndicesFromPhysX > m_NumIndices)
+ {
+ int size = m_NumIndicesForRendering;
+ int newIndices = m_NumIndicesFromPhysX > m_NumIndices;
+ for (int i=0; i<newIndices; i++)
+ {
+ int newIndex = m_Indices[m_NumIndices + i];
+ if (newIndex < m_NumVertices)
+ m_TranslatedIndices[size + i] = newIndex;
+ else
+ m_TranslatedIndices[size + i] = m_VertexTranslationTable[newIndex];
+ }
+ m_NumIndicesForRendering += newIndices;
+ m_NumIndices = m_NumIndicesFromPhysX;
+ }
+ }
+ else if (m_NumIndicesFromPhysX > m_NumIndices)
+ {
+ m_NumIndices = m_NumIndicesFromPhysX;
+ m_NumIndicesForRendering = m_NumIndicesFromPhysX;
+ }
+}
+
+bool Cloth::SetupMeshData (bool transformToWorldSpace, bool externalVertexTranlation, int tearMemoryFactor)
+{
+ if (!ExtractDataFromMesh(*m_Mesh, m_Vertices, m_Indices, m_VertexTranslationTable))
+ return false;
+
+ m_NumVertices = m_Vertices.size();
+ m_NumIndices = m_Indices.size();
+
+ bool vertexTranlationIsNeeded = externalVertexTranlation;
+
+ StrideIterator<Vector2f> uvs = m_Mesh->GetUvBegin(0);
+ StrideIterator<Vector2f> uv1s = m_Mesh->GetUvBegin(1);
+ StrideIterator<ColorRGBA32> colors = m_Mesh->GetColorBegin();
+ if (!vertexTranlationIsNeeded)
+ {
+ // find out if vertex mapping really is needed. If there are UV or color seams, it is.
+ // If there are just some duplicate vertices because the mesh is stripified, then we just use the physX indices instead.
+ for (int i=m_NumVertices; i<m_VertexTranslationTable.size();i++)
+ {
+ if (!uvs.IsNull () && uvs[m_VertexTranslationTable[i]] != uvs[i])
+ {
+ vertexTranlationIsNeeded = true;
+ break;
+ }
+ if (!uv1s.IsNull () && uv1s[m_VertexTranslationTable[i]] != uv1s[i])
+ {
+ vertexTranlationIsNeeded = true;
+ break;
+ }
+ if (!colors.IsNull () && colors[m_VertexTranslationTable[i]] != colors[i])
+ {
+ vertexTranlationIsNeeded = true;
+ break;
+ }
+ // we don't check for normals and tangents.
+ // normals are regenerated by the cloth code anyways, so split normals are no use here.
+ }
+ }
+
+ if (!vertexTranlationIsNeeded)
+ m_VertexTranslationTable.clear();
+
+ UInt32 maxVertices = std::max (std::max (m_NumVertices, m_NumVerticesFromPhysX), m_NumVertices * tearMemoryFactor);
+ UInt32 maxIndices = std::max (m_NumIndices, m_NumIndices * tearMemoryFactor);
+
+ m_Vertices.resize_uninitialized(maxVertices);
+ m_Normals.resize_uninitialized(maxVertices);
+ m_Indices.resize_initialized(maxIndices, 0);
+ if (tearMemoryFactor>1)
+ m_ParentIndices.resize_initialized(maxVertices, 0);
+ else
+ m_ParentIndices.clear();
+
+ int maxVerticesForRendering;
+ if (vertexTranlationIsNeeded && !externalVertexTranlation)
+ {
+ Mesh::TemporaryIndexContainer triangles;
+ m_Mesh->GetTriangles(triangles);
+ m_NumIndicesForRendering = triangles.size();
+ m_TranslatedIndices.resize_uninitialized(m_NumIndicesForRendering + m_NumIndices * (tearMemoryFactor-1));
+ for (int i=0; i<m_NumIndicesForRendering; i++)
+ m_TranslatedIndices[i] = triangles[i];
+ maxVerticesForRendering = m_Mesh->GetVertexCount() + m_NumVertices * (tearMemoryFactor-1);
+ m_TranslatedVertices.resize_uninitialized(maxVerticesForRendering);
+ m_TranslatedNormals.resize_uninitialized(maxVerticesForRendering);
+ m_NumVerticesForRendering = m_Mesh->GetVertexCount();
+ m_VerticesForRendering = &m_TranslatedVertices;
+ m_NormalsForRendering = &m_TranslatedNormals;
+ m_IndicesForRendering = &m_TranslatedIndices;
+ }
+ else
+ {
+ maxVerticesForRendering = maxVertices;
+ m_TranslatedIndices.clear();
+ m_TranslatedVertices.clear();
+ m_TranslatedNormals.clear();
+ m_VerticesForRendering = &m_Vertices;
+ m_NormalsForRendering = &m_Normals;
+ m_IndicesForRendering = &m_Indices;
+ m_NumVerticesForRendering = m_NumVertices;
+ m_NumIndicesForRendering = m_NumIndices;
+ }
+
+ if (!m_Mesh->IsAvailable (kShaderChannelNormal))
+ {
+ WarningString("Cloth simulation requires normals!");
+ std::fill (m_Normals.begin (), m_Normals.end (), Vector3f(0,0,0));
+ }
+ else if (vertexTranlationIsNeeded)
+ {
+ StrideIterator<Vector3f> n = m_Mesh->GetNormalBegin ();
+ for (int i=0;i<m_VertexTranslationTable.size();++i, ++n)
+ {
+ int index = m_VertexTranslationTable[i];
+ m_Normals[index] = *n;
+ }
+ }
+ else
+ {
+ strided_copy (m_Mesh->GetNormalBegin (), m_Mesh->GetNormalBegin () + m_NumVertices, m_Normals.begin ());
+ }
+
+ if (!externalVertexTranlation)
+ {
+ if (m_Mesh->IsAvailable (kShaderChannelTexCoord0))
+ {
+ m_UVs.resize_uninitialized(maxVerticesForRendering);
+ strided_copy (m_Mesh->GetUvBegin (0), m_Mesh->GetUvBegin (0) + m_NumVerticesForRendering, m_UVs.begin ());
+ }
+ else
+ m_UVs.clear();
+
+ if (m_Mesh->IsAvailable (kShaderChannelTexCoord1))
+ {
+ m_UV1s.resize_uninitialized(maxVerticesForRendering);
+ strided_copy (m_Mesh->GetUvBegin (1), m_Mesh->GetUvBegin (1) + m_NumVerticesForRendering, m_UV1s.begin ());
+ }
+ else
+ m_UV1s.clear();
+
+ if (m_Mesh->IsAvailable(kShaderChannelColor))
+ {
+ m_Colors.resize_uninitialized(maxVerticesForRendering);
+ strided_copy (m_Mesh->GetColorBegin (), m_Mesh->GetColorBegin () + m_NumVerticesForRendering, m_Colors.begin ());
+ }
+ else
+ m_Colors.clear();
+
+ if (m_Mesh->IsAvailable(kShaderChannelTangent))
+ {
+ m_Tangents.resize_uninitialized(maxVerticesForRendering);
+ strided_copy (m_Mesh->GetTangentBegin (), m_Mesh->GetTangentBegin () + m_NumVerticesForRendering, m_Tangents.begin ());
+ }
+ else
+ m_Tangents.clear();
+ }
+
+ if (transformToWorldSpace)
+ {
+ Transform& transform = GetComponent (Transform);
+ TransformPoints3x4(transform.GetLocalToWorldMatrix(), &m_Vertices[0], &m_Vertices[0], m_NumVertices);
+ if (m_Mesh->IsAvailable (kShaderChannelNormal))
+ TransformPoints3x3(transform.GetLocalToWorldMatrixNoScale(), &m_Normals[0], &m_Normals[0], m_NumVertices);
+ if (m_Mesh->IsAvailable (kShaderChannelTangent))
+ {
+ Matrix4x4f m = transform.GetLocalToWorldMatrixNoScale();
+ for ( int i=0; i<m_NumVertices; i++)
+ {
+ Vector3f tangent = Vector3f(m_Tangents[i].x, m_Tangents[i].y, m_Tangents[i].z);
+ Vector3f normalized = NormalizeSafe (m.MultiplyVector3 (tangent));
+ m_Tangents[i] = Vector4f(normalized.x, normalized.y ,normalized.z, m_Tangents[i].w);
+ }
+ }
+
+ m_NumVerticesFromPhysX = m_NumVertices;
+ }
+
+ return true;
+}
+
+void Cloth::SetupMeshBuffers (NxMeshData &meshData)
+{
+ meshData.verticesPosBegin = m_Vertices.begin();
+ meshData.verticesNormalBegin = m_Normals.empty() ? NULL : m_Normals.begin();
+ meshData.verticesPosByteStride = sizeof (Vector3f);
+ meshData.verticesNormalByteStride = sizeof (Vector3f);
+ meshData.maxVertices = m_Vertices.size();
+ meshData.numVerticesPtr = (NxU32*)&m_NumVerticesFromPhysX;
+
+ meshData.indicesBegin = &m_Indices[0];
+ meshData.indicesByteStride = sizeof (UInt16);
+ meshData.maxIndices = m_Indices.size();
+ meshData.numIndicesPtr = (NxU32*)&m_NumIndicesFromPhysX;
+ meshData.flags = NX_MDF_16_BIT_INDICES;
+
+ if (m_ParentIndices.size())
+ {
+ meshData.parentIndicesBegin = &m_ParentIndices[0];
+ meshData.parentIndicesByteStride = sizeof (UInt16);
+ meshData.maxParentIndices = m_ParentIndices.size();
+ meshData.numParentIndicesPtr = (NxU32*)&m_NumParentIndices;
+ }
+}
+
+NxClothMesh *Cloth::CookClothMesh (bool tearable)
+{
+ NxClothMeshDesc clothMeshDesc;
+
+ clothMeshDesc.numVertices = m_NumVertices;
+ clothMeshDesc.numTriangles = m_NumIndices / 3;
+ clothMeshDesc.pointStrideBytes = sizeof (Vector3f);
+ clothMeshDesc.triangleStrideBytes = sizeof (m_Indices[0]) * 3;
+ clothMeshDesc.vertexMassStrideBytes = 0;
+ clothMeshDesc.vertexFlagStrideBytes = 0;
+ clothMeshDesc.points = &m_Vertices[0];
+ clothMeshDesc.triangles = &m_Indices[0];
+ clothMeshDesc.vertexMasses = NULL;
+ clothMeshDesc.vertexFlags = NULL;
+ clothMeshDesc.flags = NX_MF_16_BIT_INDICES;
+
+ if (tearable)
+ clothMeshDesc.flags |= NX_CLOTH_MESH_TEARABLE;
+
+ MemoryStream memoryStream(NULL, 0);
+ if (!NxCookClothMesh(clothMeshDesc, memoryStream))
+ {
+ ErrorString ("Failed cooking cloth");
+ return NULL;
+ }
+
+ return GetDynamicsSDK ().createClothMesh(memoryStream);
+}
+
+void Cloth::SetupClothDesc (NxClothDesc &clothDesc, bool tearable)
+{
+ SetupMeshBuffers (clothDesc.meshData);
+ clothDesc.collisionGroup = GetGameObject ().GetLayer ();
+ clothDesc.clothMesh = CookClothMesh(tearable);
+ clothDesc.bendingStiffness = m_BendingStiffness;
+ clothDesc.stretchingStiffness = m_StretchingStiffness;
+ clothDesc.dampingCoefficient = m_Damping;
+ clothDesc.thickness = m_Thickness;
+ clothDesc.selfCollisionThickness = m_Thickness;
+ clothDesc.userData = this;
+ if (m_BendingStiffness > 0)
+ clothDesc.flags |= NX_CLF_BENDING;
+ else
+ clothDesc.flags &= ~NX_CLF_BENDING;
+ if (m_Damping > 0)
+ clothDesc.flags |= NX_CLF_DAMPING;
+ else
+ clothDesc.flags &= ~NX_CLF_DAMPING;
+ if (m_UseGravity)
+ clothDesc.flags |= NX_CLF_GRAVITY;
+ else
+ clothDesc.flags &= ~NX_CLF_GRAVITY;
+ if (m_SelfCollision)
+ clothDesc.flags |= NX_CLF_SELFCOLLISION;
+ else
+ clothDesc.flags &= ~NX_CLF_SELFCOLLISION;
+}
+
+void Cloth::Deactivate (DeactivateOperation operation)
+{
+ Super::Deactivate (operation);
+ Cleanup ();
+}
+
+void Cloth::FixedUpdate()
+{
+ if (m_Cloth)
+ {
+ Vector3f accel = m_ExternalAcceleration+RandomPointInsideCube (gClothRand, m_RandomAcceleration);
+ m_Cloth->setExternalAcceleration((const NxVec3&)accel);
+ if (m_NeedToWakeUp && !m_IsSuspended)
+ m_Cloth->wakeUp();
+ }
+ m_NeedToWakeUp = false;
+}
+
+void Cloth::AddToManager ()
+{
+ GetFixedBehaviourManager().AddBehaviour (m_FixedUpdateNode, -1);
+}
+
+void Cloth::RemoveFromManager ()
+{
+ GetFixedBehaviourManager().RemoveBehaviour (m_FixedUpdateNode);
+}
+
+void Cloth::PauseSimulation ()
+{
+ if (!m_Cloth->isSleeping())
+ {
+ m_IsSuspended = true;
+ m_Cloth->putToSleep();
+ }
+}
+
+void Cloth::ResumeSimulation ()
+{
+ if (m_IsSuspended)
+ {
+ // only wake up the cloth when it becomes visible when it has been set to sleep by the renderer,
+ // not when it has been set to sleep by PhysX.
+ m_IsSuspended = false;
+ m_Cloth->wakeUp();
+ }
+}
+
+void Cloth::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ if (m_Cloth)
+ {
+ // Apply changed values
+ SetThickness (m_Thickness);
+ SetBendingStiffness (m_BendingStiffness);
+ SetStretchingStiffness (m_StretchingStiffness);
+ SetDamping (m_Damping);
+ SetExternalAcceleration (m_ExternalAcceleration);
+ SetRandomAcceleration (m_RandomAcceleration);
+ SetUseGravity (m_UseGravity);
+ SetSelfCollision (m_SelfCollision);
+ }
+
+ Super::AwakeFromLoad (awakeMode);
+ if (IsActive ())
+ {
+ if (!m_Cloth)
+ Create ();
+ else
+ m_NeedToWakeUp = true;
+ }
+ else
+ Cleanup ();
+
+ m_SuspendCount = 0;
+
+ if(!GetEnabled())
+ SetSuspended(true);
+}
+
+void Cloth::SetSuspended (bool suspended)
+{
+ if (suspended)
+ m_SuspendCount++;
+ else
+ m_SuspendCount--;
+
+ if (m_Cloth)
+ {
+ if (m_SuspendCount)
+ PauseSimulation();
+ else
+ ResumeSimulation();
+ }
+}
+
+void Cloth::SetEnabled (bool enabled)
+{
+ if (enabled != GetEnabled())
+ {
+ Super::SetEnabled(enabled);
+ SetSuspended(!enabled);
+ }
+}
+
+#define ENFORCE_MINEQ(x) {if (value < x) { value = x; ErrorString("value must be greater than or equal to " #x);}}
+#define ENFORCE_MIN(x) {if (value <= x) { value = x; ErrorString("value must be greater than " #x);}}
+#define ENFORCE_MAXEQ(x) {if (value > x) { value = x; ErrorString("value must be smaller than or equal to " #x);}}
+#define ENFORCE_MAX(x) {if (value >= x) { value = x; ErrorString("value must be smaller than " #x);}}
+
+void Cloth::SetBendingStiffness (float value)
+{
+ ENFORCE_MINEQ(0);
+ ENFORCE_MAXEQ(1);
+ if (value != m_BendingStiffness)
+ {
+ m_NeedToWakeUp = true;
+ m_BendingStiffness = value;
+ }
+ if (m_Cloth)
+ {
+ if (value > 0)
+ {
+ m_Cloth->setBendingStiffness(value);
+ m_Cloth->setFlags(m_Cloth->getFlags() | NX_CLF_BENDING);
+ }
+ else
+ m_Cloth->setFlags(m_Cloth->getFlags() & ~(NX_CLF_BENDING));
+ }
+ SetDirty();
+}
+
+void Cloth::SetStretchingStiffness (float value)
+{
+ ENFORCE_MIN(0);
+ ENFORCE_MAXEQ(1);
+ if (value != m_StretchingStiffness)
+ {
+ m_NeedToWakeUp = true;
+ m_StretchingStiffness = value;
+ }
+ if (m_Cloth)
+ m_Cloth->setStretchingStiffness(value);
+ SetDirty();
+}
+
+void Cloth::SetDamping (float value)
+{
+ ENFORCE_MINEQ(0);
+ ENFORCE_MAXEQ(1);
+ if (value != m_Damping)
+ {
+ m_NeedToWakeUp = true;
+ m_Damping = value;
+ }
+ if (m_Cloth)
+ {
+ if (value > 0)
+ {
+ m_Cloth->setDampingCoefficient(value);
+ m_Cloth->setFlags(m_Cloth->getFlags() | NX_CLF_DAMPING);
+ }
+ else
+ m_Cloth->setFlags(m_Cloth->getFlags() & ~(NX_CLF_DAMPING));
+ }
+ SetDirty();
+}
+
+void Cloth::SetThickness (float value)
+{
+ ENFORCE_MIN(0);
+
+ if (value != m_Thickness)
+ {
+ m_NeedToWakeUp = true;
+ m_Thickness = value;
+ }
+ if (m_Cloth)
+ {
+ m_Cloth->setThickness(value);
+ m_Cloth->setSelfCollisionThickness(value);
+ }
+ SetDirty();
+}
+
+
+void Cloth::SetUseGravity (bool value)
+{
+ if (value != m_UseGravity)
+ {
+ m_NeedToWakeUp = true;
+ m_UseGravity = value;
+ }
+ if (m_Cloth)
+ {
+ if (value)
+ m_Cloth->setFlags(m_Cloth->getFlags() | NX_CLF_GRAVITY);
+ else
+ m_Cloth->setFlags(m_Cloth->getFlags() & ~(NX_CLF_GRAVITY));
+ }
+ SetDirty();
+}
+
+void Cloth::SetSelfCollision (bool value)
+{
+ if (value != m_SelfCollision)
+ {
+ m_NeedToWakeUp = true;
+ m_SelfCollision = value;
+ }
+ if (m_Cloth)
+ {
+ if (value)
+ m_Cloth->setFlags(m_Cloth->getFlags() | NX_CLF_SELFCOLLISION);
+ else
+ m_Cloth->setFlags(m_Cloth->getFlags() & ~(NX_CLF_SELFCOLLISION));
+ }
+ SetDirty();
+}
+
+void Cloth::SetExternalAcceleration (const Vector3f &value)
+{
+ if (value != m_ExternalAcceleration)
+ {
+ m_NeedToWakeUp = true;
+ m_ExternalAcceleration = value;
+ }
+ SetDirty();
+}
+
+void Cloth::SetRandomAcceleration (const Vector3f &value)
+{
+ if (value != m_RandomAcceleration)
+ {
+ m_NeedToWakeUp = true;
+ m_RandomAcceleration = value;
+ }
+ SetDirty();
+}
+
+template<class TransferFunction>
+void Cloth::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER (m_BendingStiffness);
+ TRANSFER (m_StretchingStiffness);
+ TRANSFER (m_Damping);
+ TRANSFER (m_Thickness);
+ TRANSFER (m_UseGravity);
+ TRANSFER (m_SelfCollision);
+ transfer.Align();
+ TRANSFER (m_ExternalAcceleration);
+ TRANSFER (m_RandomAcceleration);
+}
+
+static void ResetRandSeed ()
+{
+ gClothRand.SetSeed (1);
+}
+
+void Cloth::InitializeClass ()
+{
+#if UNITY_EDITOR
+ REGISTER_MESSAGE (Cloth, kTransformChanged, TransformChanged, int);
+ REGISTER_MESSAGE_VOID (Cloth, kBecameVisible, BecameVisible);
+ REGISTER_MESSAGE_VOID (Cloth, kBecameInvisible, BecameInvisible);
+#endif
+
+ GlobalCallbacks::Get().resetRandomAfterLevelLoad.Register(ResetRandSeed);
+}
+
+void Cloth::CleanupClass ()
+{
+ GlobalCallbacks::Get().resetRandomAfterLevelLoad.Unregister(ResetRandSeed);
+}
+
+#if UNITY_EDITOR
+// When the mesh is updated by Phyics, it acts independently of it's Transform,
+// simulation is in world coordinates. In the editor, we use this code to allow
+// placement, though.
+void Cloth::TransformChanged( int changeMask )
+{
+ if (!IsWorldPlaying() && IsActive())
+ {
+ Create();
+ }
+}
+
+void Cloth::BecameVisible()
+{
+ if (GetSuspended())
+ SetSuspended (false);
+}
+
+void Cloth::BecameInvisible()
+{
+ SetSuspended (true);
+}
+
+#endif
+}
+
+IMPLEMENT_CLASS_HAS_INIT (Cloth)
+IMPLEMENT_OBJECT_SERIALIZE (Cloth)
+INSTANTIATE_TEMPLATE_TRANSFER (Cloth)
+
+#endif // ENABLE_CLOTH
diff --git a/Runtime/Dynamics/DeformableMesh.h b/Runtime/Dynamics/DeformableMesh.h
new file mode 100644
index 0000000..ba5c78e
--- /dev/null
+++ b/Runtime/Dynamics/DeformableMesh.h
@@ -0,0 +1,146 @@
+#ifndef DEFORMABLEMESH_H
+#define DEFORMABLEMESH_H
+
+#include "Configuration/UnityConfigure.h"
+#if ENABLE_CLOTH || DOXYGEN
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Vector4.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+class Mesh;
+class ClothRenderer;
+class NxMeshData;
+class NxScene;
+
+class NxClothMesh;
+class NxCloth;
+class NxClothDesc;
+
+namespace Unity {
+
+class Cloth : public Behaviour
+{
+public:
+ REGISTER_DERIVED_ABSTRACT_CLASS (Cloth, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (Cloth)
+
+ Cloth (MemLabelId label, ObjectCreationMode mode);
+
+ bool GetSuspended() { return m_SuspendCount != 0; }
+ void SetSuspended(bool suspended);
+ virtual void SetEnabled (bool enab);
+ virtual void Reset ();
+
+
+ virtual void FixedUpdate ();
+ virtual void Deactivate (DeactivateOperation operation);
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+ virtual void ProcessMeshForRenderer ();
+
+ static void InitializeClass();
+ static void CleanupClass ();
+
+#if UNITY_EDITOR
+ void TransformChanged( int changeMask );
+ void BecameVisible();
+ void BecameInvisible();
+#endif
+
+ float GetBendingStiffness () const { return m_BendingStiffness; }
+ void SetBendingStiffness (float value);
+
+ float GetStretchingStiffness () const { return m_StretchingStiffness; }
+ void SetStretchingStiffness (float value);
+
+ float GetDamping () const { return m_Damping; }
+ void SetDamping (float value);
+
+ bool GetUseGravity () const { return m_UseGravity; }
+ void SetUseGravity (bool value);
+
+ Vector3f GetExternalAcceleration () const { return m_ExternalAcceleration; }
+ void SetExternalAcceleration (const Vector3f &value);
+
+ Vector3f GetRandomAcceleration () const { return m_RandomAcceleration; }
+ void SetRandomAcceleration (const Vector3f &value);
+
+ bool GetSelfCollision () { return m_SelfCollision; }
+ void SetSelfCollision (bool value);
+
+ float GetThickness () const { return m_Thickness; }
+ void SetThickness (float value);
+
+ dynamic_array<Vector3f> &GetVertices () { return m_Vertices; }
+ dynamic_array<Vector3f> &GetNormals () { return m_Normals; }
+protected:
+
+ virtual void PauseSimulation ();
+ virtual void ResumeSimulation ();
+
+ virtual void Create () = 0;
+ virtual void Cleanup ();
+ bool SetupMeshData (bool transformToWorldSpace, bool externalVertexTranlation, int tearMemoryFactor);
+ void SetupMeshBuffers (NxMeshData &meshData);
+
+ void SetupClothDesc (NxClothDesc &clothDesc, bool tearable);
+ NxClothMesh *CookClothMesh (bool tearable);
+
+ // configuration
+ PPtr<Mesh> m_Mesh;
+ float m_BendingStiffness; ///<Bending stiffness. 0 = disabled. range { 0, 1 }
+ float m_StretchingStiffness; ///<Stretching stiffness. range { 0.001, 1 }
+ float m_Damping; ///<Motion damping coefficient. 0 = disabled. range { 0, 1 }
+ float m_Thickness; ///<Thickness of the Cloth surface. range { 0.001, 10000 }
+ bool m_UseGravity; ///<Should gravitational acceleration be applied to the cloth?
+ bool m_SelfCollision; ///<Can the cloth collide with itself?
+ Vector3f m_ExternalAcceleration; ///<External acceleration applied to the cloth.
+ Vector3f m_RandomAcceleration; ///<Random acceleration applied to the cloth.
+
+
+ // state
+ NxCloth* m_Cloth;
+
+ NxScene* m_ClothScene;
+
+ int m_SuspendCount;
+
+ bool m_IsSuspended;
+ bool m_NeedToWakeUp;
+
+ UInt32 m_NumVertices;
+ UInt32 m_NumVerticesFromPhysX;
+ UInt32 m_NumVerticesForRendering;
+ UInt32 m_NumIndices;
+ UInt32 m_NumIndicesFromPhysX;
+ UInt32 m_NumIndicesForRendering;
+ dynamic_array<Vector3f> m_Vertices;
+ dynamic_array<Vector3f> m_TranslatedVertices;
+ dynamic_array<Vector3f> *m_VerticesForRendering;
+ dynamic_array<Vector3f> m_Normals;
+ dynamic_array<Vector3f> m_TranslatedNormals;
+ dynamic_array<Vector3f> *m_NormalsForRendering;
+ dynamic_array<UInt16> m_Indices;
+ dynamic_array<UInt16> m_TranslatedIndices;
+ dynamic_array<UInt16> *m_IndicesForRendering;
+ dynamic_array<Vector4f> m_Tangents;
+ dynamic_array<Vector2f> m_UVs;
+ dynamic_array<Vector2f> m_UV1s;
+ dynamic_array<ColorRGBA32> m_Colors;
+ UInt32 m_NumParentIndices;
+ dynamic_array<UInt16> m_ParentIndices;
+ dynamic_array<UInt16> m_VertexTranslationTable;
+
+ BehaviourListNode m_FixedUpdateNode;
+
+ friend class ::ClothRenderer;
+};
+}
+
+#endif // ENABLE_CLOTH || DOXYGEN
+#endif // DEFORMABLEMESH_H
diff --git a/Runtime/Dynamics/ExtractDataFromMesh.cpp b/Runtime/Dynamics/ExtractDataFromMesh.cpp
new file mode 100644
index 0000000..a2cd9f4
--- /dev/null
+++ b/Runtime/Dynamics/ExtractDataFromMesh.cpp
@@ -0,0 +1,56 @@
+#include "UnityPrefix.h"
+#include "ExtractDataFromMesh.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Misc/MeshWelding.h"
+#include "Runtime/Graphics/TriStripper.h"
+
+///@TODO: Use dynamic_array temp allocator
+// Extracts unique welded vertices & triangle array indexing into the welded vertices.
+bool ExtractDataFromMesh (Mesh& mesh, dynamic_array<Vector3f>& vertices, dynamic_array<UInt16>& triangles, dynamic_array<UInt16>& remap)
+{
+ int vertexCount = mesh.GetVertexCount();
+ if (vertexCount == 0)
+ return false;
+
+ if (!mesh.HasVertexData())
+ {
+ ErrorStringObject("CollisionMeshData couldn't be created because the mesh has been marked as non-accessible", &mesh);
+ return false;
+ }
+
+ vertices.resize_uninitialized(vertexCount);
+ mesh.ExtractVertexArray(&vertices[0]);
+
+ triangles.clear();
+ for (unsigned submesh=0; submesh<mesh.GetSubMeshCount(); submesh++)
+ {
+ if (submesh >= mesh.GetSubMeshCount())
+ {
+ ErrorString("Failed getting triangles. Submesh index is out of bounds.");
+ return false;
+ }
+
+ UInt16* indices = mesh.GetSubMeshBuffer16(submesh);
+ SubMesh& sm = mesh.GetSubMeshFast(submesh);
+ if (sm.topology == kPrimitiveTriangleStripDeprecated)
+ {
+ const UInt32 startIndex = triangles.size();
+ int triCount = CountTrianglesInStrip (indices, sm.indexCount);
+ triangles.resize_uninitialized((triCount * 3) + startIndex);
+ Destripify(indices, sm.indexCount, triangles.begin() + startIndex, triCount);
+ }
+ else if (sm.topology == kPrimitiveTriangles)
+ {
+ triangles.insert(triangles.end(), indices, indices + sm.indexCount);
+ }
+ else
+ {
+ ErrorString("Failed to extract collision data: non-triangle mesh.");
+ return false;
+ }
+ }
+
+ WeldVertexArray(vertices, triangles, remap);
+ return true;
+} \ No newline at end of file
diff --git a/Runtime/Dynamics/ExtractDataFromMesh.h b/Runtime/Dynamics/ExtractDataFromMesh.h
new file mode 100644
index 0000000..7a68823
--- /dev/null
+++ b/Runtime/Dynamics/ExtractDataFromMesh.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Math/Vector3.h"
+
+class Mesh;
+
+bool ExtractDataFromMesh (Mesh& mesh, dynamic_array<Vector3f>& vertices, dynamic_array<UInt16>& triangles, dynamic_array<UInt16>& remap);
diff --git a/Runtime/Dynamics/FixedJoint.cpp b/Runtime/Dynamics/FixedJoint.cpp
new file mode 100644
index 0000000..d712724
--- /dev/null
+++ b/Runtime/Dynamics/FixedJoint.cpp
@@ -0,0 +1,61 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "FixedJoint.h"
+#include "PhysicsManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+
+using namespace std;
+
+namespace Unity
+{
+
+#define GET_JOINT() static_cast<NxD6Joint*> (m_Joint)
+
+/*
+- We awake the hingejoint only once. (AwakeFromLoad)
+ At this point we setup the axes. They are never changed afterwards
+ -> The perfect solution remembers the old position/rotation of the rigid bodies.
+ Then when changing axis/anchor is changed it generates axes that are rleative to the old position/rotation state!
+*/
+
+FixedJoint::FixedJoint (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+FixedJoint::~FixedJoint ()
+{
+}
+
+void FixedJoint::Create ()
+{
+ AssertIf (!IsActive ());
+
+ NxD6JointDesc desc;
+ desc.zMotion = desc.yMotion = desc.xMotion = NX_D6JOINT_MOTION_LOCKED;
+ desc.swing1Motion = desc.swing2Motion = desc.twistMotion = NX_D6JOINT_MOTION_LOCKED;
+
+ if (m_Joint && m_Joint->getState () == NX_JS_SIMULATING)
+ GET_JOINT()->saveToDesc (desc);
+
+ FINALIZE_CREATE (desc, NxFixedJointDesc);
+}
+
+IMPLEMENT_AXIS_ANCHOR(FixedJoint,NxD6JointDesc)
+
+template<class TransferFunction>
+void FixedJoint::Transfer (TransferFunction& transfer)
+{
+ Super::Super::Transfer (transfer);
+ TRANSFER_SIMPLE (m_ConnectedBody);
+ JointTransferPost (transfer);
+}
+
+}
+
+IMPLEMENT_CLASS (FixedJoint)
+IMPLEMENT_OBJECT_SERIALIZE (FixedJoint)
+
+#undef GET_JOINT
+#endif //ENABLE_PHYSICS \ No newline at end of file
diff --git a/Runtime/Dynamics/FixedJoint.h b/Runtime/Dynamics/FixedJoint.h
new file mode 100644
index 0000000..8bb9567
--- /dev/null
+++ b/Runtime/Dynamics/FixedJoint.h
@@ -0,0 +1,32 @@
+#ifndef FIXEDJOINT_H
+#define FIXEDJOINT_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Math/Vector3.h"
+#include "JointDescriptions.h"
+#include "Joint.h"
+class Rigidbody;
+class NxJointDesc;
+class NxRevoluteJoint;
+
+namespace Unity
+{
+
+class FixedJoint : public Joint
+{
+ public:
+
+ REGISTER_DERIVED_CLASS (FixedJoint, Joint)
+ DECLARE_OBJECT_SERIALIZE (FixedJoint)
+
+ FixedJoint (MemLabelId label, ObjectCreationMode mode);
+
+ private:
+
+ virtual void ApplySetupAxesToDesc (int option);
+ virtual void Create ();
+};
+
+}
+
+#endif
diff --git a/Runtime/Dynamics/HingeJoint.cpp b/Runtime/Dynamics/HingeJoint.cpp
new file mode 100644
index 0000000..9494960
--- /dev/null
+++ b/Runtime/Dynamics/HingeJoint.cpp
@@ -0,0 +1,280 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "Runtime/Utilities/StaticAssert.h"
+#include "HingeJoint.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "PhysicsManager.h"
+
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+
+using namespace std;
+
+namespace Unity
+{
+
+#define GET_JOINT() static_cast<NxRevoluteJoint*> (m_Joint)
+
+/*
+- We awake the hingejoint only once. (AwakeFromLoad)
+ At this point we setup the axes. They are never changed afterwards
+ -> The perfect solution remembers the old position/rotation of the rigid bodies.
+ Then when changing axis/anchor is changed it generates axes that are rleative to the old position/rotation state!
+*/
+
+HingeJoint::HingeJoint (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ InitJointLimits (m_Limits);
+ InitJointSpring (m_Spring);
+ InitJointMotor (m_Motor);
+
+ m_UseLimits = false;
+ m_UseMotor = false;
+ m_UseSpring = false;
+
+ CompileTimeAssert(sizeof(JointMotor) == sizeof(NxMotorDesc), "Unity JointMotor type has different size from physx one");
+ CompileTimeAssert(sizeof(JointSpring) == sizeof(NxSpringDesc), "Unity JointSpring type has different size from physx one");
+ CompileTimeAssert(sizeof(JointLimits) == sizeof(NxJointLimitPairDesc), "Unity JointLimits type has different size from physx one");
+}
+
+HingeJoint::~HingeJoint ()
+{
+}
+
+inline NxSpringDesc ToNovodex (const JointSpring& spring)
+{
+ JointSpring desc = spring;
+ desc.targetPosition = Deg2Rad (desc.targetPosition);
+ return (const NxSpringDesc&)desc;
+}
+
+inline NxMotorDesc ToNovodex (const JointMotor& motor)
+{
+ JointMotor desc = motor;
+ desc.targetVelocity = Deg2Rad (desc.targetVelocity);
+ return (const NxMotorDesc&)desc;
+}
+
+#define kHingeJointLimitError "Joint limits for hinge joint out of bounds. Limits need to be between -360 and 360 degrees."
+inline NxJointLimitPairDesc ToNovodex (const JointLimits& limits)
+{
+ NxJointLimitPairDesc l = (const NxJointLimitPairDesc&)limits;
+ if(IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1))
+ {
+ // PhysX specs limit hinge joints to [-180,180] degress. But it seems 360 works fine
+ // and is very useful in practice. Higher numbers produce broken results (case 492847)
+ if (l.low.value < -360)
+ {
+ l.low.value = -360;
+ ErrorString (kHingeJointLimitError);
+ }
+ if (l.low.value > 360)
+ {
+ l.low.value = 360;
+ ErrorString (kHingeJointLimitError);
+ }
+ if (l.high.value < -360)
+ {
+ l.high.value = -360;
+ ErrorString (kHingeJointLimitError);
+ }
+ if (l.high.value > 360)
+ {
+ l.high.value = 360;
+ ErrorString (kHingeJointLimitError);
+ }
+ }
+ l.low.value = Deg2Rad (l.low.value);
+ l.high.value = Deg2Rad (l.high.value);
+
+ /// DO SOME SPECIAL HANDLING WHEN LOW IS LARGER THAN HIGH!!!!!
+ /// MAKE IT MORE INTUITIVE
+
+ if (l.low.value > l.high.value)
+ swap (l.low, l.high);
+
+ return l;
+}
+
+void HingeJoint::SetUseMotor (bool enable)
+{
+ SetDirty ();
+ m_UseMotor = enable;
+ if (m_Joint)
+ {
+ UInt32 flags = GET_JOINT()->getFlags ();
+ if (enable)
+ flags |= NX_RJF_MOTOR_ENABLED;
+ else
+ flags &= ~NX_RJF_MOTOR_ENABLED;
+ GET_JOINT()->setFlags (flags);
+ }
+}
+
+bool HingeJoint::GetUseMotor () const
+{
+ return m_UseMotor;
+}
+
+void HingeJoint::SetUseLimits (bool enable)
+{
+ SetDirty ();
+ m_UseLimits = enable;
+ if (m_Joint)
+ {
+ UInt32 flags = GET_JOINT()->getFlags ();
+ if (enable)
+ flags |= NX_RJF_LIMIT_ENABLED;
+ else
+ flags &= ~NX_RJF_LIMIT_ENABLED;
+ GET_JOINT()->setFlags (flags);
+ }
+}
+
+bool HingeJoint::GetUseLimits () const
+{
+ return m_UseLimits;
+}
+
+void HingeJoint::SetUseSpring (bool enable)
+{
+ SetDirty ();
+ m_UseSpring = enable;
+ if (m_Joint)
+ {
+ UInt32 flags = GET_JOINT()->getFlags ();
+ if (enable)
+ flags |= NX_RJF_SPRING_ENABLED;
+ else
+ flags &= ~NX_RJF_SPRING_ENABLED;
+ GET_JOINT()->setFlags (flags);
+ }
+}
+
+bool HingeJoint::GetUseSpring () const
+{
+ return m_UseSpring;
+}
+
+JointMotor HingeJoint::GetMotor () const
+{
+ return m_Motor;
+}
+
+void HingeJoint::SetMotor (const JointMotor& motor)
+{
+ SetDirty ();
+ m_Motor = motor;
+ if (m_Joint)
+ {
+ GET_JOINT()->setMotor (ToNovodex (motor));
+ }
+}
+
+JointSpring HingeJoint::GetSpring () const
+{
+ return m_Spring;
+}
+
+void HingeJoint::SetSpring (const JointSpring& spring)
+{
+ SetDirty ();
+ m_Spring = spring;
+ if (m_Joint)
+ GET_JOINT()->setSpring (ToNovodex (spring));
+}
+
+JointLimits HingeJoint::GetLimits () const
+{
+ return m_Limits;
+}
+
+void HingeJoint::SetLimits (const JointLimits& limits)
+{
+ SetDirty ();
+ m_Limits = limits;
+ if (m_Joint)
+ {
+ GET_JOINT()->setLimits (ToNovodex (limits));
+
+ // Novodex seems to have a bug that it will not update HingeJoints when only the limits are changed.
+ // Also call setMotor to force it to update.
+ GET_JOINT()->setMotor (ToNovodex (m_Motor));
+ }
+}
+
+float HingeJoint::GetVelocity () const
+{
+ if (m_Joint)
+ return Rad2Deg (GET_JOINT()->getVelocity ());
+ else
+ return 0.0F;
+}
+
+float HingeJoint::GetAngle () const
+{
+ if (m_Joint)
+ return Rad2Deg (GET_JOINT()->getAngle ());
+ else
+ return 0.0F;
+}
+
+/*
+- When the rigid body which is attached with the hinge joint is activated after the joint is loaded
+ It will not be connected with the joint!
+*/
+
+template<class TransferFunction>
+void HingeJoint::Transfer (TransferFunction& transfer)
+{
+ JointTransferPre (transfer);
+
+ TRANSFER (m_UseSpring);
+ transfer.Align();
+ TRANSFER (m_Spring);
+
+ TRANSFER (m_UseMotor);
+ transfer.Align();
+ TRANSFER (m_Motor);
+
+ TRANSFER (m_UseLimits);
+ transfer.Align();
+ TRANSFER (m_Limits);
+
+ JointTransferPost (transfer);
+}
+
+void HingeJoint::Create ()
+{
+ AssertIf (!IsActive ());
+
+ NxRevoluteJointDesc desc;
+
+ if (m_Joint && m_Joint->getState () == NX_JS_SIMULATING)
+ GET_JOINT()->saveToDesc (desc);
+
+ desc.motor = ToNovodex (m_Motor);
+ desc.limit = ToNovodex (m_Limits);
+ desc.spring = ToNovodex (m_Spring);
+
+ desc.flags = 0;
+ if (m_UseMotor)
+ desc.flags |= NX_RJF_MOTOR_ENABLED;
+ if (m_UseLimits)
+ desc.flags |= NX_RJF_LIMIT_ENABLED;
+ if (m_UseSpring)
+ desc.flags |= NX_RJF_SPRING_ENABLED;
+
+ FINALIZE_CREATE (desc, NxRevoluteJoint);
+}
+
+IMPLEMENT_AXIS_ANCHOR(HingeJoint,NxRevoluteJointDesc)
+
+}
+
+IMPLEMENT_CLASS (HingeJoint)
+IMPLEMENT_OBJECT_SERIALIZE (HingeJoint)
+
+#undef GET_JOINT
+#endif //ENABLE_PHSYICS
diff --git a/Runtime/Dynamics/HingeJoint.h b/Runtime/Dynamics/HingeJoint.h
new file mode 100644
index 0000000..b1051e3
--- /dev/null
+++ b/Runtime/Dynamics/HingeJoint.h
@@ -0,0 +1,69 @@
+#ifndef HINGEJOINT_H
+#define HINGEJOINT_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Math/Vector3.h"
+#include "JointDescriptions.h"
+#include "Joint.h"
+class Rigidbody;
+class NxJointDesc;
+class NxRevoluteJoint;
+
+namespace Unity
+{
+
+class HingeJoint : public Joint
+{
+ public:
+
+ REGISTER_DERIVED_CLASS (HingeJoint, Joint)
+ DECLARE_OBJECT_SERIALIZE (HingeJoint)
+
+ HingeJoint (MemLabelId label, ObjectCreationMode mode);
+
+ JointMotor GetMotor () const;
+ void SetMotor (const JointMotor& motor);
+
+ JointLimits GetLimits () const;
+ void SetLimits (const JointLimits& limits);
+
+ JointSpring GetSpring () const;
+ void SetSpring (const JointSpring& spring);
+
+ void SetUseMotor (bool enable);
+ bool GetUseMotor () const;
+
+ void SetUseLimits (bool enable);
+ bool GetUseLimits () const;
+
+ void SetUseSpring (bool enable);
+ bool GetUseSpring () const ;
+
+ // The hinge angle's rate of change (angular velocity).
+ float GetVelocity () const;
+
+ // The hinge's angle
+ float GetAngle () const;
+
+ virtual void ApplySetupAxesToDesc (int option);
+
+ private:
+
+ virtual void Create ();
+
+ void SetSpringNoEnable (const JointSpring& spring);
+ void SetMotorNoEnable (const JointMotor& motor);
+ void SetLimitsNoEnable (const JointLimits& limits);
+
+ JointLimits m_Limits;
+ JointSpring m_Spring;
+ JointMotor m_Motor;
+
+ bool m_UseLimits;
+ bool m_UseMotor;
+ bool m_UseSpring;
+};
+
+}
+
+#endif
diff --git a/Runtime/Dynamics/Joint.cpp b/Runtime/Dynamics/Joint.cpp
new file mode 100644
index 0000000..eaa470e
--- /dev/null
+++ b/Runtime/Dynamics/Joint.cpp
@@ -0,0 +1,325 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "Joint.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "Runtime/Graphics/Transform.h"
+#include "PhysicsManager.h"
+#include "RigidBody.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "NxWrapperUtility.h"
+
+namespace Unity
+{
+
+Joint::Joint (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_DidSetupAxes = false;
+ m_Joint = NULL;
+ m_Anchor = Vector3f::zero;
+ m_AutoConfigureConnectedAnchor = true;
+ m_ConnectedAnchor = Vector3f::zero;
+ m_Axis = Vector3f::xAxis;
+
+ m_BreakForce = std::numeric_limits<float>::infinity ();
+ m_BreakTorque = std::numeric_limits<float>::infinity ();
+}
+
+
+Joint::~Joint ()
+{
+ Cleanup ();
+}
+
+
+void Joint::Reset ()
+{
+ // Anchor is at the edge of bounding volume .y !
+ Super::Reset ();
+ AABB aabb;
+ if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb))
+ m_Anchor = Vector3f (0, aabb.GetCenter ().y + aabb.GetExtent ().y, 0);
+ else
+ m_Anchor = Vector3f::zero;
+ m_ConnectedAnchor = Vector3f::zero;
+ m_AutoConfigureConnectedAnchor = true;
+ m_Axis = Vector3f::xAxis;
+}
+
+void Joint::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ Rigidbody* body = QueryComponent (Rigidbody);
+
+ Rigidbody* otherBody = m_ConnectedBody;
+
+ if (otherBody == body)
+ {
+ m_ConnectedBody = NULL;
+ }
+}
+
+inline Vector3f GlobalToLocalUnscaledSpace (Transform& transform, Vector3f& p)
+{
+ Vector3f localAnchor = p - transform.GetPosition ();
+ localAnchor = transform.InverseTransformDirection (localAnchor);
+ return localAnchor;
+}
+
+
+/// OPTIMIZE ALL THOSE TRANSFORM ACCESSES.
+/// Use a matrix instead.
+void Joint::SetupAxes (NxJointDesc& desc, int options)
+{
+ Vector3f globalAnchor, globalAxis, globalNormal;
+ CalculateGlobalHingeSpace (globalAnchor, globalAxis, globalNormal);
+ Vector3f globalConnectedAnchor = CalculateGlobalConnectedAnchor (m_AutoConfigureConnectedAnchor);
+
+ Transform& transform = GetComponent (Transform);
+ Transform* otherTransform = NULL;
+ Rigidbody* body;
+ body = m_ConnectedBody;
+ if (body)
+ otherTransform = body->QueryComponent (Transform);
+
+ // Setup anchor
+ if (options & kChangeAnchor)
+ {
+ desc.localAnchor[0] = Vec3ToNx(GlobalToLocalUnscaledSpace (transform, globalAnchor));
+ }
+ if (options & kChangeAxis)
+ {
+ desc.localNormal[0] = Vec3ToNx(transform.InverseTransformDirection (globalNormal));
+ desc.localAxis[0] = Vec3ToNx(transform.InverseTransformDirection (globalAxis));
+ }
+
+ if (otherTransform)
+ {
+ if (options & kChangeAnchor)
+ {
+ desc.localAnchor[1] = Vec3ToNx(GlobalToLocalUnscaledSpace (*otherTransform, globalConnectedAnchor));
+ }
+ if (options & kChangeAxis)
+ {
+ desc.localAxis[1] = Vec3ToNx(otherTransform->InverseTransformDirection (globalAxis));
+ desc.localNormal[1] = Vec3ToNx(otherTransform->InverseTransformDirection (globalNormal));
+ }
+ }
+ else
+ {
+ if (options & kChangeAnchor)
+ {
+ desc.localAnchor[1] = Vec3ToNx(globalConnectedAnchor);
+ }
+ if (options & kChangeAxis)
+ {
+ desc.localAxis[1] = Vec3ToNx(globalAxis);
+ desc.localNormal[1] = Vec3ToNx(globalNormal);
+ }
+ }
+}
+
+Vector3f Joint::CalculateGlobalConnectedAnchor (bool autoConfigureConnectedFrame)
+{
+ Vector3f globalConnectedAnchor;
+ Transform* otherTransform = NULL;
+ Rigidbody* body = m_ConnectedBody;
+ if (body)
+ otherTransform = body->QueryComponent (Transform);
+
+ if (autoConfigureConnectedFrame)
+ {
+ Vector3f globalAnchor = GetComponent (Transform).TransformPoint (m_Anchor);
+ if (otherTransform)
+ m_ConnectedAnchor = otherTransform->InverseTransformPoint (globalAnchor);
+ else
+ m_ConnectedAnchor = globalAnchor;
+ }
+
+ if (otherTransform)
+ globalConnectedAnchor = otherTransform->TransformPoint (m_ConnectedAnchor);
+ else
+ globalConnectedAnchor = m_ConnectedAnchor;
+
+ return globalConnectedAnchor;
+}
+
+void Joint::CalculateGlobalHingeSpace (Vector3f& globalAnchor, Vector3f& globalAxis, Vector3f& globalNormal) const
+{
+ const Transform& transform = GetComponent (Transform);
+
+ Vector3f localAxis = m_Axis;
+ if (SqrMagnitude (localAxis) < Vector3f::epsilon)
+ localAxis = Vector3f (1.0F, 0.0F, 0.0F);
+
+ globalAnchor = transform.TransformPoint (m_Anchor);
+
+ Vector3f localDirection = - m_Anchor;
+ Vector3f localNormal = Cross (localDirection, localAxis);
+ OrthoNormalize (&localAxis, &localNormal);
+
+ globalAxis = transform.TransformDirection (localAxis);
+ globalNormal = transform.TransformDirection (localNormal);
+}
+
+float ToNovodexInfinity (float f)
+{
+ if (f == std::numeric_limits<float>::infinity ())
+ return NX_MAX_REAL;
+ else
+ return f;
+}
+
+void Joint::SetBreakForce (float force)
+{
+ SetDirty ();
+ m_BreakForce = force;
+ if (m_Joint)
+ m_Joint->setBreakable (ToNovodexInfinity (m_BreakForce), GetBreakTorque ());
+}
+
+void Joint::SetBreakTorque (float torque)
+{
+ SetDirty ();
+ m_BreakTorque = torque;
+ if (m_Joint)
+ m_Joint->setBreakable (GetBreakForce (), ToNovodexInfinity (m_BreakTorque));
+}
+
+float Joint::GetBreakForce () const
+{
+ return m_BreakForce;
+}
+
+float Joint::GetBreakTorque () const
+{
+ return m_BreakTorque;
+}
+
+void Joint::FinalizeCreateImpl (NxJointDesc& desc, bool swapActors)
+{
+ desc.maxForce = ToNovodexInfinity (m_BreakForce);
+ desc.maxTorque = ToNovodexInfinity (m_BreakTorque);
+ desc.userData = this;
+
+ Rigidbody& body = GetComponent (Rigidbody);
+ body.Create (true);
+ body.FetchPoseFromTransform ();
+
+ AssertIf (!body.m_ActiveScene);
+ bool didActorsChange = false;
+
+ int actor0 = swapActors ? 1 : 0;
+ int actor1 = swapActors ? 0 : 1;
+
+ if (desc.actor[actor0] != body.m_Actor)
+ {
+ desc.actor[actor0] = body.m_Actor;
+ didActorsChange = true;
+ }
+
+ Rigidbody* otherBody = m_ConnectedBody;
+ if (otherBody != NULL && otherBody->IsActive ())
+ {
+ otherBody->Create (true);
+ otherBody->FetchPoseFromTransform ();
+
+ AssertIf (!otherBody->m_ActiveScene);
+ if (desc.actor[actor1] != otherBody->m_Actor)
+ {
+ desc.actor[actor1] = otherBody->m_Actor;
+ didActorsChange = true;
+ }
+ }
+ else
+ {
+ if (desc.actor[actor1] != NULL)
+ {
+ desc.actor[actor1] = NULL;
+ didActorsChange = true;
+ }
+ }
+
+ // If we don't recreate the joint novodex will mark the joint broken and then we get an assert.
+ // because we can't save to the desc anymore!
+ if (didActorsChange)
+ Cleanup ();
+
+ if (!m_DidSetupAxes || m_Joint == NULL)
+ {
+ SetupAxes (desc);
+ m_DidSetupAxes = true;
+ }
+}
+
+void Joint::Cleanup ()
+{
+ if (m_Joint)
+ {
+ GetDynamicsScene ().releaseJoint (*m_Joint);
+ m_Joint = NULL;
+ }
+}
+
+void Joint::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ if (IsActive ())
+ Create ();
+ else
+ Cleanup ();
+}
+
+void Joint::Deactivate (DeactivateOperation operation)
+{
+ Cleanup ();
+ Super::Deactivate (operation);
+}
+
+void Joint::SetConnectedBody (PPtr<Rigidbody> body)
+{
+ if (m_ConnectedBody != body)
+ {
+ SetDirty ();
+ m_ConnectedBody = body;
+ }
+
+ if (IsActive ())
+ Create ();
+}
+
+void Joint::SetAxis (const Vector3f& axis)
+{
+ SetDirty ();
+ m_Axis = axis;
+ ApplySetupAxesToDesc(kChangeAxis);
+}
+
+void Joint::SetAnchor (const Vector3f& anchor)
+{
+ SetDirty ();
+ m_Anchor = anchor;
+ ApplySetupAxesToDesc(kChangeAnchor);
+}
+
+void Joint::SetConnectedAnchor (const Vector3f& anchor)
+{
+ SetDirty ();
+ m_ConnectedAnchor = anchor;
+ ApplySetupAxesToDesc(kChangeAnchor);
+}
+
+void Joint::SetAutoConfigureConnectedAnchor (bool anchor)
+{
+ SetDirty ();
+ m_AutoConfigureConnectedAnchor = anchor;
+ ApplySetupAxesToDesc(kChangeAxis|kChangeAnchor);
+}
+
+}
+
+IMPLEMENT_CLASS (Joint)
+
+#endif //ENABLE_PHYSICS \ No newline at end of file
diff --git a/Runtime/Dynamics/Joint.h b/Runtime/Dynamics/Joint.h
new file mode 100644
index 0000000..e632116
--- /dev/null
+++ b/Runtime/Dynamics/Joint.h
@@ -0,0 +1,148 @@
+#ifndef JOINT_H
+#define JOINT_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Math/Vector3.h"
+#include "RigidBody.h"
+#include "JointDescriptions.h"
+class Rigidbody;
+class NxJointDesc;
+class NxJoint;
+
+namespace Unity
+{
+
+class Joint : public Component
+{
+ public:
+
+ REGISTER_DERIVED_ABSTRACT_CLASS (Joint, Component)
+
+ Joint (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~Joint (); declared-by-macro
+
+ Vector3f GetAxis () const { return m_Axis; }
+ virtual void SetAxis (const Vector3f& axis);
+
+ Vector3f GetAnchor () const { return m_Anchor; }
+ virtual void SetAnchor (const Vector3f& axis);
+
+ Vector3f GetConnectedAnchor () const { return m_ConnectedAnchor; }
+ virtual void SetConnectedAnchor (const Vector3f& axis);
+
+ bool GetAutoConfigureConnectedAnchor () const { return m_AutoConfigureConnectedAnchor; }
+ virtual void SetAutoConfigureConnectedAnchor (bool anchor);
+
+ void SetBreakForce (float force);
+ float GetBreakForce () const;
+ void SetBreakTorque (float torque);
+ float GetBreakTorque () const;
+
+ void SetConnectedBody (PPtr<Rigidbody> body);
+ PPtr<Rigidbody> GetConnectedBody () const { return m_ConnectedBody; }
+
+ virtual void CalculateGlobalHingeSpace (Vector3f& globalAnchor, Vector3f& globalAxis, Vector3f& globalNormal) const;
+ Vector3f CalculateGlobalConnectedAnchor (bool autoConfigureConnectedFrame);
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ virtual void Deactivate (DeactivateOperation operation);
+ virtual void Reset ();
+
+ virtual void CheckConsistency();
+
+ void NullJoint () { m_Joint = NULL; }
+
+ protected:
+
+ /// SUB CLASSES OVERRIDE THIS
+ virtual void Create () = 0;
+ virtual void ApplySetupAxesToDesc (int option) = 0;
+
+ template<class TransferFunction>
+ void JointTransferPre (TransferFunction& transfer)
+ {
+ Super::Transfer (transfer);
+
+ TRANSFER_SIMPLE (m_ConnectedBody);
+ TRANSFER_SIMPLE (m_Anchor);
+ TRANSFER_SIMPLE (m_Axis);
+ TRANSFER_SIMPLE (m_AutoConfigureConnectedAnchor);
+ transfer.Align();
+ TRANSFER_SIMPLE (m_ConnectedAnchor);
+
+ }
+
+ template<class TransferFunction>
+ void JointTransferPreNoAxis (TransferFunction& transfer)
+ {
+ Super::Transfer (transfer);
+
+ TRANSFER_SIMPLE (m_ConnectedBody);
+ TRANSFER_SIMPLE (m_Anchor);
+ TRANSFER_SIMPLE (m_AutoConfigureConnectedAnchor);
+ transfer.Align();
+ TRANSFER_SIMPLE (m_ConnectedAnchor);
+ }
+
+ template<class TransferFunction>
+ void JointTransferPost (TransferFunction& transfer)
+ {
+ TRANSFER (m_BreakForce);
+ TRANSFER (m_BreakTorque);
+ }
+
+ void FinalizeCreateImpl (NxJointDesc& desc, bool swapActors = false);
+
+ NxJoint* m_Joint;
+ bool m_AutoConfigureConnectedAnchor;
+ Vector3f m_Anchor;
+ Vector3f m_ConnectedAnchor;
+ Vector3f m_Axis;
+
+ enum { kChangeAxis = 1 << 0, kChangeAnchor = 1 << 1 };
+ void SetupAxes (NxJointDesc& desc, int options = kChangeAnchor|kChangeAxis);
+
+ private:
+
+ void Cleanup ();
+
+ bool m_DidSetupAxes;
+
+ float m_BreakForce;///< Maximum force the joint can withstand before breaking. Infinity means unbreakable. range { 0.001, infinity }
+ float m_BreakTorque; ///< Maximum torque the joint can withstand before breaking. Infinity means unbreakable. range { 0.001, infinity }
+
+ protected:
+ PPtr<Rigidbody> m_ConnectedBody;
+
+ friend class ::Rigidbody;
+};
+
+/// We need to to know the type of the desc so unfortunately this function must be inside the subclasses.
+/// For reuse of code we do it with macros
+#define IMPLEMENT_AXIS_ANCHOR(klass,nxdesc)\
+void klass::ApplySetupAxesToDesc (int option)\
+{\
+ if (IsActive () && m_Joint)\
+ {\
+ nxdesc desc;\
+ AssertIf (m_Joint->getState () == NX_JS_BROKEN);\
+ GET_JOINT()->saveToDesc (desc);\
+ SetupAxes (desc, option);\
+ GET_JOINT()->loadFromDesc (desc);\
+ AssertIf (m_Joint->getState () == NX_JS_BROKEN);\
+ }\
+}\
+
+#define FINALIZE_CREATE(desc, type)\
+ FinalizeCreateImpl (desc);\
+ if (GET_JOINT ())\
+ GET_JOINT ()->loadFromDesc (desc);\
+ else\
+ m_Joint = GetDynamicsScene ().createJoint (desc);\
+ AssertIf (m_Joint && m_Joint->getState () == NX_JS_BROKEN);\
+ Assert (!GetGameObject().IsDestroying ());
+
+}
+
+#endif
diff --git a/Runtime/Dynamics/JointDescriptions.h b/Runtime/Dynamics/JointDescriptions.h
new file mode 100644
index 0000000..9236509
--- /dev/null
+++ b/Runtime/Dynamics/JointDescriptions.h
@@ -0,0 +1,215 @@
+#ifndef JOINTDESCRIPTIONS_H
+#define JOINTDESCRIPTIONS_H
+
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+
+struct JointMotor
+{
+ /*
+ The relative velocity the motor is trying to achieve. The motor will only be able
+ to reach this velocity if the maxForce is sufficiently large. If the joint is
+ spinning faster than this velocity, the motor will actually try to brake. If you set this
+ to infinity then the motor will keep speeding up, unless there is some sort of resistance
+ on the attached bodies. The sign of this variable determines the rotation direction,
+ with positive values going the same way as positive joint angles.
+ Default is infinity.
+ */
+
+ float targetVelocity;///< target velocity of motor
+
+ /*
+ The maximum force (torque in this case) the motor can exert. Zero disables the motor.
+ Default is 0, should be >= 0. Setting this to a very large value if velTarget is also
+ very large may not be a good idea.
+ */
+ float force; ///< maximum motor force / torque range { 0, infinity }
+
+ /*
+ If true, motor will not brake when it spins faster than velTarget
+ default: false.
+ */
+ int freeSpin;///
+
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (JointMotor)
+};
+
+template<class TransferFunction>
+void JointMotor::Transfer (TransferFunction& transfer)
+{
+ TRANSFER_SIMPLE (targetVelocity);
+ TRANSFER (force);
+ transfer.Transfer (freeSpin, "freeSpin", kEditorDisplaysCheckBoxMask);
+}
+
+struct JointDrive
+{
+ int mode; ///< The mode of what this dirves enum { Disabled = 0, position = 1, velocity = 2, position and velocity = 3 }
+ float positionSpring;///< The spring used to reach the target range { 0, infinity }
+ float positionDamper;///< The damping used to reach the target range { 0, infinity }
+ float maximumForce;///< The maximum force the drive can exert to reach the target velocity. range {0, infinity}
+
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (JointDrive)
+};
+
+template<class TransferFunction>
+void JointDrive::Transfer (TransferFunction& transfer)
+{
+ transfer.SetVersion (2);
+
+ TRANSFER_SIMPLE (mode);
+ TRANSFER_SIMPLE (positionSpring);
+ TRANSFER_SIMPLE (positionDamper);
+ TRANSFER_SIMPLE (maximumForce);
+ if (transfer.IsOldVersion(1) && transfer.IsReading())
+ {
+ // This case used to be ignored in old versions of PhysX, but no longer is.
+ // If it is zero, set it to a working value instead.
+ if (mode == NX_D6JOINT_DRIVE_POSITION)
+ maximumForce = NX_MAX_F32;
+ }
+}
+struct SoftJointLimit
+{
+ float limit;
+ float bounciness;///< When the joint hits the limit. This will determine how bouncy it will be. { 0, 1 }
+ float spring;///< If greater than zero, the limit is soft. The spring will pull the joint back. { 0, infinity }
+ float damper;///< If spring is greater than zero, the limit is soft. This is the damping of spring. { 0, infinity }
+
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (SoftJointLimit)
+};
+
+template<class TransferFunction>
+void SoftJointLimit::Transfer (TransferFunction& transfer)
+{
+ TRANSFER_SIMPLE (limit);
+ TRANSFER_SIMPLE (bounciness);
+ TRANSFER_SIMPLE (spring);
+ TRANSFER_SIMPLE (damper);
+}
+
+
+
+
+// Describes a joint spring. The spring is implicitly integrated, so even high spring and damper
+// coefficients should be robust.
+struct JointSpring
+{
+ float spring; //!< spring coefficient range { 0, infinity }
+ float damper; //!< damper coefficient range { 0, infinity }
+ float targetPosition; //!< target value (angle/position) of spring where the spring force is zero.
+
+ bool operator != (const JointSpring& s)const
+ {
+ return spring != s.spring
+ || damper != s.damper
+ || targetPosition != s.targetPosition
+ ;
+ }
+
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (JointSpring)
+};
+
+template<class TransferFunction>
+void JointSpring::Transfer (TransferFunction& transfer)
+{
+ TRANSFER_SIMPLE (spring);
+ TRANSFER_SIMPLE (damper);
+ TRANSFER_SIMPLE (targetPosition);
+}
+
+struct JointLimits
+{
+ float min;
+ float minBounce;///< range {0, 1}
+ float minHardness;// unsupported
+
+ float max;
+ float maxBounce;///< range {0, 1}
+ float maxHardness;// unsupported
+ JointLimits () { minHardness = maxHardness = 0.0F; }
+
+ DECLARE_SERIALIZE_NO_PPTR (JointLimits)
+};
+
+template<class TransferFunction>
+void JointLimits::Transfer (TransferFunction& transfer)
+{
+ TRANSFER_SIMPLE (min);
+ TRANSFER_SIMPLE (max);
+ TRANSFER_SIMPLE (minBounce);
+ TRANSFER_SIMPLE (maxBounce);
+}
+
+inline void InitJointLimits (JointLimits& limits)
+{
+ limits.min = 0.0F;
+ limits.max = 0.0F;
+ limits.minBounce = 0.0F;
+ limits.maxBounce = 0.0F;
+ limits.minHardness = 0.0F;
+ limits.maxHardness = 0.0F;
+}
+
+inline void InitJointSpring (JointSpring& spring)
+{
+ spring.spring = 0.0F;
+ spring.damper = 0.0F;
+ spring.targetPosition = 0.0F;
+}
+
+inline void InitJointMotor (JointMotor& motor)
+{
+ motor.targetVelocity = 0.0F;
+ motor.force = 0.0F;
+ motor.freeSpin = 0;
+}
+
+inline void InitJointDrive (JointDrive& motor)
+{
+ motor.mode = 0;
+ motor.positionSpring = 0.0F;
+ motor.positionDamper = 0.0F;
+ motor.maximumForce = NX_MAX_F32;
+}
+
+inline void InitSoftJointLimit (SoftJointLimit& motor)
+{
+ motor.limit = 0.0F;
+ motor.bounciness = 0.0F;
+ motor.spring = 0.0F;
+ motor.damper = 0.0F;
+}
+
+inline void ConvertDrive (JointDrive& drive, NxJointDriveDesc& out)
+{
+ out.spring = drive.positionSpring;
+ out.damping = drive.positionDamper;
+ out.forceLimit = drive.maximumForce;
+ out.driveType = drive.mode;
+}
+
+inline void ConvertDrive (JointDrive& drive, NxJointDriveDesc& out, int mask)
+{
+ out.spring = drive.positionSpring;
+ out.damping = drive.positionDamper;
+ out.forceLimit = drive.maximumForce;
+ out.driveType = mask;
+}
+
+inline void ConvertSoftLimit (SoftJointLimit& limit, NxJointLimitSoftDesc& out)
+{
+ out.value = Deg2Rad (limit.limit);
+ out.restitution = limit.bounciness;
+ out.spring = limit.spring;
+ out.damping = limit.damper;
+}
+
+inline void ConvertSoftLimitLinear (SoftJointLimit& limit, NxJointLimitSoftDesc& out)
+{
+ out.value = limit.limit;
+ out.restitution = limit.bounciness;
+ out.spring = limit.spring;
+ out.damping = limit.damper;
+}
+
+#endif
diff --git a/Runtime/Dynamics/Joints.h b/Runtime/Dynamics/Joints.h
new file mode 100644
index 0000000..12a2e79
--- /dev/null
+++ b/Runtime/Dynamics/Joints.h
@@ -0,0 +1,6 @@
+#ifndef JOINTS_H
+#define JOINTS_H
+
+#include "HingeJoint.h"
+
+#endif
diff --git a/Runtime/Dynamics/MeshCollider.cpp b/Runtime/Dynamics/MeshCollider.cpp
new file mode 100644
index 0000000..124d532
--- /dev/null
+++ b/Runtime/Dynamics/MeshCollider.cpp
@@ -0,0 +1,326 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "MeshCollider.h"
+#include "Runtime/Graphics/Transform.h"
+#include "RigidBody.h"
+#include "PhysicsManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Filters/Mesh/LodMeshFilter.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "nxmemorystream.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "External/PhysX/builds/SDKs/Cooking/include/NxCooking.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+#define GET_CONVEX_SHAPE() ((class NxConvexShape*)m_Shape)
+#define GET_MESH_SHAPE() ((class NxTriangleMeshShape*)m_Shape)
+
+#if UNITY_EDITOR
+const NxConvexMesh* MeshCollider::GetConvexMesh() const
+{
+ if( !m_Shape )
+ return NULL;
+ if( !m_Shape->isConvexMesh() )
+ return NULL;
+ const NxConvexMesh& mesh = GET_CONVEX_SHAPE ()->getConvexMesh ();
+ return &mesh;
+}
+const NxTriangleMesh* MeshCollider::GetTriangleMesh() const
+{
+ if( !m_Shape )
+ return NULL;
+ if( m_Shape->isConvexMesh() )
+ return NULL;
+ const NxTriangleMesh& mesh = GET_MESH_SHAPE ()->getTriangleMesh ();
+ return &mesh;
+}
+#endif
+
+MeshCollider::MeshCollider (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_MeshNode(this)
+{
+ m_Shared = false;
+}
+
+MeshCollider::~MeshCollider ()
+{
+}
+
+void MeshCollider::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ if (m_Shape)
+ {
+ // Apply changed values
+ if (m_Convex != (m_Shape->isConvexMesh() != NULL))
+ SetConvex(m_Convex);
+
+ if (m_Shape)
+ {
+ bool smooth;
+ if (m_Shape->isConvexMesh())
+ {
+ NxConvexShapeDesc desc;
+ GET_CONVEX_SHAPE ()->saveToDesc(desc);
+ smooth = desc.meshFlags & NX_MESH_SMOOTH_SPHERE_COLLISIONS;
+ }
+ else
+ {
+ NxTriangleMeshShapeDesc desc;
+ GET_MESH_SHAPE ()->saveToDesc(desc);
+ smooth = desc.meshFlags & NX_MESH_SMOOTH_SPHERE_COLLISIONS;
+ }
+ if (smooth != m_SmoothSphereCollisions)
+ SetSmoothSphereCollisions(m_SmoothSphereCollisions);
+ SetSharedMesh(m_Mesh);
+ }
+ }
+
+ Super::AwakeFromLoad (awakeMode);
+}
+
+void MeshCollider::Cleanup ()
+{
+ m_MeshNode.RemoveFromList();
+ if (m_Shape)
+ {
+ if (m_Shape->isConvexMesh())
+ {
+ NxConvexMesh& mesh = GET_CONVEX_SHAPE ()->getConvexMesh ();
+ Super::Cleanup ();
+ if (!m_Shared)
+ GetDynamicsSDK ().releaseConvexMesh (mesh);
+ }
+ else
+ {
+ NxTriangleMesh& mesh = GET_MESH_SHAPE ()->getTriangleMesh ();
+ Super::Cleanup ();
+ if (!m_Shared)
+ GetDynamicsSDK ().releaseTriangleMesh (mesh);
+ }
+ }
+}
+
+void MeshCollider::DidDeleteMesh ()
+{
+ Cleanup ();
+}
+
+void MeshCollider::CreateShape( void* nxmesh, const Rigidbody* ignoreRigidbody )
+{
+ if ( !nxmesh )
+ return;
+
+ if (m_Convex)
+ {
+ NxConvexShapeDesc shapeDesc;
+ shapeDesc.meshData = (NxConvexMesh*)nxmesh;
+
+ if (m_SmoothSphereCollisions)
+ shapeDesc.meshFlags |= NX_MESH_SMOOTH_SPHERE_COLLISIONS;
+
+ FinalizeCreate (shapeDesc, true, ignoreRigidbody);
+ }
+ else
+ {
+ NxTriangleMeshShapeDesc shapeDesc;
+
+ if (m_SmoothSphereCollisions)
+ shapeDesc.meshFlags |= NX_MESH_SMOOTH_SPHERE_COLLISIONS;
+
+ shapeDesc.meshData = (NxTriangleMesh*)nxmesh;
+
+ FinalizeCreate (shapeDesc, true, ignoreRigidbody);
+ }
+}
+
+//@TODO: Updating physics mesh when reimporting no longer works!
+
+void MeshCollider::Create (const Rigidbody* ignoreRigidbody)
+{
+ if (m_Shape)
+ Cleanup ();
+
+ Mesh* mesh = m_Mesh;
+ m_CachedMesh = mesh;
+ if (mesh == NULL)
+ return;
+ // do not create anything if have no vertices or less-than-one triangle
+ if (mesh->GetVertexCount() == 0 || mesh->GetPrimitiveCount() == 0)
+ return;
+
+ /*
+ NxCookingParams params;
+ params.hintCollisionSpeed = true;
+ NxSetCookingParams (params);
+ */
+
+ Matrix4x4f scalematrix;
+
+ TransformType type = GetComponent (Transform).CalculateTransformMatrixScaleDelta (scalematrix);
+
+ void* nxmesh = NULL;
+ if (IsNoScaleTransform(type) && !mesh->IsSharedPhysicsMeshDirty())
+ {
+ m_Shared = true;
+
+ if (m_Convex)
+ {
+ nxmesh = mesh->GetSharedNxConvexMesh ();
+ }
+ else
+ {
+ nxmesh = mesh->GetSharedNxMesh ();
+ }
+ }
+ // For scaled meshes, create instance
+ else
+ {
+ m_Shared = false;
+ nxmesh = CreateNxMeshFromUnityMesh(mesh, m_Convex, scalematrix, type);
+ }
+
+ if (nxmesh == NULL)
+ return;
+
+ mesh->AddObjectUser( m_MeshNode );
+
+ CreateShape( nxmesh, ignoreRigidbody );
+}
+
+void MeshCollider::ReCreate()
+{
+ if( !m_Shape )
+ return;
+ // Re-creating the full mesh collider is expensive, so we only re-create the shape and not the mesh.
+ if ( m_Shape->isConvexMesh() )
+ {
+ NxConvexMesh& mesh = GET_CONVEX_SHAPE ()->getConvexMesh ();
+ Super::Cleanup();
+ CreateShape( &mesh, NULL );
+ }
+ else
+ {
+ NxTriangleMesh& mesh = GET_MESH_SHAPE ()->getTriangleMesh ();
+ Super::Cleanup();
+ CreateShape( &mesh, NULL );
+ }
+}
+
+void MeshCollider::ScaleChanged ()
+{
+ Create (NULL);
+}
+
+void MeshCollider::TransformChanged (int changeMask)
+{
+ Super::TransformChanged (changeMask);
+ if (m_Shape)
+ {
+ if (m_Shape->getActor ().userData == NULL)
+ {
+ PROFILER_AUTO(gStaticColliderMove, this)
+ FetchPoseFromTransform ();
+ }
+ else
+ {
+ Rigidbody* body = (Rigidbody*)m_Shape->getActor ().userData;
+ Matrix4x4f matrix;
+ if (GetRelativeToParentPositionAndRotation (GetComponent (Transform), body->GetComponent (Transform), matrix))
+ {
+ NxMat34 shapeMatrix;
+ shapeMatrix.setColumnMajor44 (matrix.GetPtr ());
+ m_Shape->setLocalPose (shapeMatrix);
+ }
+
+ if (body->GetGameObjectPtr() != GetGameObjectPtr() || changeMask & Transform::kScaleChanged)
+ RigidbodyMassDistributionChanged ();
+ }
+
+ if (changeMask & Transform::kScaleChanged)
+ ScaleChanged ();
+
+ RefreshPhysicsInEditMode();
+ }
+ else if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1) && IsActive() && GetEnabled())
+ Create (NULL);
+}
+
+void MeshCollider::InitializeClass ()
+{
+ REGISTER_MESSAGE_VOID(MeshCollider, kDidDeleteMesh, DidDeleteMesh);
+}
+
+void MeshCollider::SetSharedMesh (const PPtr<Mesh> m)
+{
+ if (m_CachedMesh != m)
+ {
+ SetDirty ();
+ m_Mesh = m;
+ if (IsActive())
+ Create(NULL);
+ }
+}
+
+PPtr<Mesh> MeshCollider::GetSharedMesh ()
+{
+ return m_Mesh;
+}
+
+void MeshCollider::Reset ()
+{
+ Super::Reset ();
+ if (GetGameObjectPtr ())
+ {
+ MeshFilter* filter = QueryComponent (MeshFilter);
+ if (filter && m_Mesh.GetInstanceID () == 0)
+ {
+ PPtr<Mesh> newMesh = filter->GetSharedMesh ();
+ if (newMesh != m_Mesh)
+ {
+ m_Mesh = newMesh;
+ if (IsActive())
+ Create(NULL);
+ }
+ }
+ }
+ m_SmoothSphereCollisions = false;
+ m_Convex = false;
+}
+
+template<class TransferFunction>
+void MeshCollider::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion (2);
+
+ TRANSFER (m_SmoothSphereCollisions);
+ transfer.Transfer (m_Convex, "m_Convex");
+ transfer.Align();
+ TRANSFER (m_Mesh);
+}
+
+void MeshCollider::SetConvex (bool convex)
+{
+ m_Convex = convex;
+ if (m_Shape)
+ Create(NULL);
+ SetDirty();
+ RefreshPhysicsInEditMode();
+}
+
+void MeshCollider::SetSmoothSphereCollisions (bool smooth)
+{
+ m_SmoothSphereCollisions = smooth;
+ if (m_Shape)
+ Create(NULL);
+ SetDirty();
+ RefreshPhysicsInEditMode();
+}
+
+IMPLEMENT_CLASS_HAS_INIT (MeshCollider)
+IMPLEMENT_OBJECT_SERIALIZE (MeshCollider)
+#endif //ENABLE_PHYSICS
diff --git a/Runtime/Dynamics/MeshCollider.h b/Runtime/Dynamics/MeshCollider.h
new file mode 100644
index 0000000..1773561
--- /dev/null
+++ b/Runtime/Dynamics/MeshCollider.h
@@ -0,0 +1,63 @@
+#ifndef MESHCOLLIDER_H
+#define MESHCOLLIDER_H
+
+#include "Collider.h"
+#include "Runtime/Math/Vector3.h"
+class Mesh;
+
+#if UNITY_EDITOR
+class NxConvexMesh;
+class NxTriangleMesh;
+#endif
+
+class MeshCollider : public Collider
+{
+public:
+ REGISTER_DERIVED_CLASS (MeshCollider, Collider)
+ DECLARE_OBJECT_SERIALIZE (MeshCollider)
+
+ MeshCollider (MemLabelId label, ObjectCreationMode mode);
+
+ void SetSharedMesh (const PPtr<Mesh> m);
+ PPtr<Mesh> GetSharedMesh ();
+
+ void SetConvex (bool convex);
+ bool GetConvex () const { return m_Convex; }
+
+ void SetSmoothSphereCollisions (bool convex);
+ bool GetSmoothSphereCollisions () const { return m_SmoothSphereCollisions; }
+
+ virtual void Reset ();
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ virtual void TransformChanged (int changeMask);
+ static void InitializeClass ();
+ static void CleanupClass () {}
+ void DidDeleteMesh ();
+
+ #if UNITY_EDITOR
+ const NxConvexMesh* GetConvexMesh() const;
+ const NxTriangleMesh* GetTriangleMesh() const;
+ #endif
+
+private:
+ void CreateShape( void* nxmesh, const Rigidbody* ignoreRigidbody );
+
+protected:
+
+ virtual void Create (const Rigidbody* ignoreRigidbody);
+ virtual void Cleanup ();
+ virtual void ReCreate();
+ void ScaleChanged ();
+
+ //virtual NxCCDSkeleton* CreateCCDSkeleton(float scale);
+
+ bool m_SmoothSphereCollisions;
+ bool m_Convex;
+ bool m_Shared;
+ PPtr<Mesh> m_Mesh;
+ PPtr<Mesh> m_CachedMesh;
+ ListNode<Object> m_MeshNode;
+};
+
+#endif
diff --git a/Runtime/Dynamics/NxMeshCreation.cpp b/Runtime/Dynamics/NxMeshCreation.cpp
new file mode 100644
index 0000000..7ecff3e
--- /dev/null
+++ b/Runtime/Dynamics/NxMeshCreation.cpp
@@ -0,0 +1,115 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_PHYSICS
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Dynamics/ExtractDataFromMesh.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "Runtime/Dynamics/PhysicsManager.h"
+#include "Runtime/Dynamics/nxmemorystream.h"
+#include "External/PhysX/builds/SDKs/Cooking/include/NxCooking.h"
+#include "Runtime/Interfaces/IPhysics.h"
+#include "Runtime/Profiler/Profiler.h"
+
+PROFILER_INFORMATION(gBakeCollisionMesh, "Mesh.Bake PhysX CollisionData", kProfilerPhysics)
+PROFILER_INFORMATION(gBakeCollisionScaledMesh, "Mesh.Bake Scaled Mesh PhysX CollisionData", kProfilerPhysics)
+
+bool CreateNxStreamFromUnityMesh (Mesh* mesh, bool convex, const Matrix4x4f& scalematrix, TransformType transformType, MemoryStream& stream )
+{
+ dynamic_array<Vector3f> vertices;
+ dynamic_array<Vector3f> normals;
+ dynamic_array<UInt16> triangles;
+ dynamic_array<UInt16> remap;
+ if (!ExtractDataFromMesh(*mesh, vertices, triangles, remap))
+ return false;
+
+ int vertexCount = vertices.size();
+ int inStride = sizeof(Vector3f);
+ if (!IsNoScaleTransform(transformType))
+ TransformPoints3x3 (scalematrix, &vertices[0], sizeof(Vector3f), &vertices[0], sizeof(Vector3f), vertexCount);
+
+ // PhysX crashes when using only 1 triangle
+ // So just duplicate the triangle...
+ if (triangles.size() == 3)
+ {
+ triangles.push_back(triangles[0]);
+ triangles.push_back(triangles[1]);
+ triangles.push_back(triangles[2]);
+ }
+
+ if (convex)
+ {
+ NxConvexMeshDesc desc;
+ desc.flags = NX_CF_COMPUTE_CONVEX;
+
+ desc.numVertices = vertexCount;
+ desc.points = &vertices[0];
+ desc.pointStrideBytes = inStride;
+
+ return NxCookConvexMesh (desc, stream);
+ }
+ else
+ {
+ NxTriangleMeshDesc desc;
+
+ desc.numVertices = vertexCount;
+ desc.points = &vertices[0];
+ desc.pointStrideBytes = inStride;
+
+ desc.numTriangles = triangles.size () / 3;
+ desc.triangles = &triangles[0];
+ desc.triangleStrideBytes = sizeof (triangles[0]) * 3;
+ desc.flags = NX_MF_16_BIT_INDICES;
+
+ if (transformType & kOddNegativeScaleTransform)
+ desc.flags |= NX_MF_FLIPNORMALS;
+
+ return NxCookTriangleMesh (desc, stream);
+ }
+}
+
+MemoryStream* CreateNxStreamFromUnityMesh(Mesh& meshData, bool convex)
+{
+ PROFILER_AUTO_THREAD_SAFE(gBakeCollisionMesh, &meshData)
+ Matrix4x4f identity; identity.SetIdentity();
+ MemoryStream* stream = new MemoryStream (NULL, 0);
+ CreateNxStreamFromUnityMesh(&meshData, convex, identity, kNoScaleTransform, *stream);
+ return stream;
+}
+
+void* CreateNxMeshFromUnityMesh (Mesh* mesh, bool convex, const Matrix4x4f& scalematrix, TransformType transformType )
+{
+#if ENABLE_PROFILER
+ if (IsNoScaleTransform(transformType))
+ {
+ PROFILER_BEGIN(gBakeCollisionMesh, mesh)
+ }
+ else
+ {
+ PROFILER_BEGIN(gBakeCollisionScaledMesh, mesh)
+ }
+#endif
+
+ MemoryStream stream (NULL, 0);
+
+ if (!CreateNxStreamFromUnityMesh(mesh, convex, scalematrix, transformType, stream))
+ {
+ PROFILER_END
+ return NULL;
+ }
+
+ if (convex)
+ {
+ NxConvexMesh* nxmesh = GetDynamicsSDK().createConvexMesh (stream);
+ PROFILER_END
+ return nxmesh;
+ }
+ else
+ {
+ NxTriangleMesh* nxmesh = GetDynamicsSDK().createTriangleMesh (stream);
+ PROFILER_END
+ return nxmesh;
+ }
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/Dynamics/NxMeshCreation.h b/Runtime/Dynamics/NxMeshCreation.h
new file mode 100644
index 0000000..1c9fbcb
--- /dev/null
+++ b/Runtime/Dynamics/NxMeshCreation.h
@@ -0,0 +1,11 @@
+#pragma once
+
+class Mesh;
+class Matrix4x4f;
+enum TransformType;
+class MemoryStream;
+
+bool CreateNxStreamFromUnityMesh (Mesh* mesh, bool convex, const Matrix4x4f& scalematrix, TransformType transformType, MemoryStream& stream );
+MemoryStream* CreateNxStreamFromUnityMesh(Mesh& meshData, bool convex);
+//static void* CreateNxMeshFromByteStream(bool convex,dynamic_array<UInt8>& mesh)
+void* CreateNxMeshFromUnityMesh (Mesh* mesh, bool convex, const Matrix4x4f& scalematrix, TransformType transformType );
diff --git a/Runtime/Dynamics/NxWrapperUtility.h b/Runtime/Dynamics/NxWrapperUtility.h
new file mode 100644
index 0000000..a9c159e
--- /dev/null
+++ b/Runtime/Dynamics/NxWrapperUtility.h
@@ -0,0 +1,14 @@
+#ifndef NXWRAPPERUTILITY_H
+#define NXWRAPPERUTILITY_H
+
+#include "External/PhysX/builds/SDKs/Foundation/include/NxVec3.h"
+#include "External/PhysX/builds/SDKs/Foundation/include/NxVersionNumber.h"
+#include "External/PhysX/builds/SDKs/NxCharacter/include/NxExtended.h"
+
+inline NxExtendedVec3 Vec3ToNxExtended (const Vector3f& v) { return NxExtendedVec3(v.x, v.y, v.z); }
+inline Vector3f NxExtendedToVec3 (const NxExtendedVec3& v) { return Vector3f(v.x, v.y, v.z); }
+
+inline NxVec3 Vec3ToNx (const Vector3f& v) { return NxVec3(v.x, v.y, v.z); }
+inline Vector3f Vec3FromNx (const NxVec3& v) { return Vector3f(v.x, v.y, v.z); }
+
+#endif
diff --git a/Runtime/Dynamics/PhysXRaycast.cpp b/Runtime/Dynamics/PhysXRaycast.cpp
new file mode 100644
index 0000000..8e16d02
--- /dev/null
+++ b/Runtime/Dynamics/PhysXRaycast.cpp
@@ -0,0 +1,195 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "PhysXRaycast.h"
+
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Dynamics/Collider.h"
+#include "Runtime/Dynamics/PhysicsManager.h"
+#include "Runtime/Dynamics/RigidBody.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Geometry/Plane.h"
+#include <limits>
+
+// a small value to expand the shape AABBs with in order to make sure there are not boundary issues when broadphase culling the raycasts
+static const float kAABBEpsilon = 0.00001f;
+static const float kEpsilon = 0.000001f;
+
+void PhysXRaycast::InitializeClass ()
+{
+ SetRaycastInterface (new PhysXRaycast ());
+}
+
+void PhysXRaycast::CleanupClass ()
+{
+ PhysXRaycast* raycast = reinterpret_cast<PhysXRaycast*> (GetRaycastInterface ());
+ delete raycast;
+ SetRaycastInterface (NULL);
+}
+
+static inline int GetAttachedRigidbodyOrColliderInstanceID (Collider& collider)
+{
+ Rigidbody* body = collider.GetRigidbody ();
+ if (body)
+ return body->GetInstanceID();
+ else
+ return collider.GetInstanceID();
+}
+
+bool PhysXRaycast::Raycast (const Ray& ray, float distance, int mask, HitInfo& output)
+{
+ RaycastHit hit;
+ bool result = GetPhysicsManager().Raycast(ray,distance,hit,mask);
+ if (!result)
+ return false;
+
+ output.intersection = hit.point;
+ output.normal = hit.normal;
+ output.colliderInstanceID = hit.collider->GetInstanceID();
+ output.rigidBodyOrColliderInstanceID = GetAttachedRigidbodyOrColliderInstanceID (*hit.collider);
+
+ return true;
+}
+
+
+// Cast a ray against a list of shapes shapes
+bool PhysXRaycast::Intersect(const Ray& ray, float maxDist, NxShape** shapes, AABB* shapeBounds, size_t shapeCount, HitInfo& hit) const
+{
+ Vector3f halfDir = ray.GetDirection() * maxDist * 0.5;
+ AABB rayBounds(ray.GetOrigin() + halfDir, Abs(halfDir));
+ rayBounds.Expand(kAABBEpsilon);
+ NxRay physicsRay ((const NxVec3&)ray.GetOrigin(), (const NxVec3&)ray.GetDirection());
+
+ NxRaycastHit nxhit;
+ NxU32 flags = NX_RAYCAST_IMPACT | NX_RAYCAST_NORMAL;
+
+ Vector3f collisionPos;
+ Vector3f collisionNormal;
+ float collisionSqrDist = std::numeric_limits<float>::infinity ();
+
+ for (size_t s = 0; s < shapeCount; ++s)
+ {
+ if (!IntersectAABBAABBInclusive(rayBounds, shapeBounds[s]))
+ continue;
+
+ NxShape* shape = shapes[s];
+ if (!shape) continue;
+
+#if ENABLE_MULTITHREADED_CODE
+ // We shouldn't need to do this. But it turns out that sometimes raycast will return a duff intersection normal when
+ // called from multiple threads intersection the same object at the same time, this causes graphical functional test
+ // 267 to fail for instance. Jesper has contacted NVidia about this.
+ SimpleLock::AutoLock lock(m_Lock);
+#endif
+
+ if (shape->raycast (physicsRay, maxDist, flags, nxhit, false))
+ {
+ Vector3f intersectionPoint = (const Vector3f&)nxhit.worldImpact;
+ float sqrDistance = SqrMagnitude(ray.GetOrigin() - intersectionPoint);
+
+ if (sqrDistance < collisionSqrDist)
+ {
+ Collider* collider = (Collider*)shape->userData;
+ if (!collider || collider->GetIsTrigger ())
+ continue;
+ hit.colliderInstanceID = collider->GetInstanceID ();
+ hit.rigidBodyOrColliderInstanceID = GetAttachedRigidbodyOrColliderInstanceID (*collider);
+ collisionSqrDist = sqrDistance;
+ hit.intersection = (const Vector3f&)nxhit.worldImpact;
+ hit.normal = (const Vector3f&)nxhit.worldNormal;
+ }
+ }
+ }
+
+ return collisionSqrDist < std::numeric_limits<float>::infinity ();
+}
+
+// Should probably be a param
+enum { kMaxParticleCollisionShapes = 256 };
+
+// Determines which shapes overlap the particle system and caches the bounding boxes of those shapes
+size_t PhysXRaycast::BroadPhaseCulling(dynamic_array<NxShape*>& nxShapes, dynamic_array<AABB>& aabbs, const MinMaxAABB& particleSystemAABB, const int filter, bool staticOnly) const
+{
+ size_t shapeCount = GetShapes(particleSystemAABB, kMaxParticleCollisionShapes, nxShapes.data(), filter, staticOnly);
+ if (shapeCount == 0)
+ return 0;
+
+ // Cache bounds for all shapes
+ for (int i = 0; i < shapeCount; ++i)
+ {
+ GetAABB( aabbs[i], *nxShapes[i] );
+ }
+ return shapeCount;
+}
+
+size_t PhysXRaycast::BatchIntersect( const dynamic_array<BatchedRaycast>& raycasts, dynamic_array<BatchedRaycastResult>& results, const UInt32 filter, bool staticOnly ) const
+{
+ // Get new aabb for particle system
+ MinMaxAABB aabb = ComputeBatchAABB(raycasts);
+ aabb.Expand(kAABBEpsilon);
+
+ // Get all shapes within bounds (aabb)
+ dynamic_array<NxShape*> nxShapes(kMaxParticleCollisionShapes, kMemTempAlloc);
+ dynamic_array<AABB> aabbs(kMaxParticleCollisionShapes, kMemTempAlloc);
+
+ size_t shapeCount = BroadPhaseCulling( nxShapes, aabbs, aabb, filter, staticOnly );
+
+ if (shapeCount == 0)
+ return 0;
+
+ // trace rays against the shapes
+ size_t numIntersections = 0;
+ for (size_t r = 0; r < raycasts.size(); r++)
+ {
+ const BatchedRaycast pointsRay = raycasts[r];
+
+ // attempt to cull based on ray extent
+ if ( !IntersectAny( pointsRay, aabbs.data(), shapeCount ) ) continue;
+
+ // build actual ray - this is relatively expensive due to Magnitude that has a sqrt and a dot product
+ // TODO: possibly do 4 rays at a time in SIMD
+ const Vector3f displacement = pointsRay.to-pointsRay.from;
+ float distance = Magnitude(displacement);
+ if (distance <= kEpsilon)
+ continue;
+ const Vector3f direction = displacement / distance;
+ const Ray ray(pointsRay.from, direction);
+
+ // intersect the shapes with the ray
+ HitInfo hit;
+ const bool isHit = Intersect(ray, distance, nxShapes.data(), aabbs.data(), shapeCount, hit);
+ if ( isHit )
+ {
+ results[numIntersections] = BatchedRaycastResult(r,hit);
+ numIntersections++;
+ }
+ }
+
+ return numIntersections;
+}
+
+// Perform PhysX broad phase culling
+size_t PhysXRaycast::GetShapes( const AABB& bounds, int maxResult, NxShape** outShapes, UInt32 groupMask, bool staticOnly ) const
+{
+ Vector3f min = bounds.GetMin();
+ Vector3f max = bounds.GetMax();
+
+ NxBounds3 physicsBounds;
+ physicsBounds.set((const NxVec3&)min, (const NxVec3&)max);
+
+ return GetDynamicsScene().overlapAABBShapes (physicsBounds, ( staticOnly ? NX_STATIC_SHAPES : NX_ALL_SHAPES ), maxResult, outShapes, NULL, groupMask, NULL, false);
+}
+
+void PhysXRaycast::GetAABB( AABB& bounds, const NxShape& shape ) const
+{
+ NxBounds3 physicsBounds;
+ shape.getWorldBounds(physicsBounds);
+ NxVec3 center, extents;
+ physicsBounds.getCenter(center);
+ physicsBounds.getExtents(extents);
+ bounds = AABB((const Vector3f&)center, (const Vector3f&)extents);
+ bounds.Expand(kAABBEpsilon);
+}
+
+#endif //ENABLE_PHYSICS
diff --git a/Runtime/Dynamics/PhysXRaycast.h b/Runtime/Dynamics/PhysXRaycast.h
new file mode 100644
index 0000000..298f051
--- /dev/null
+++ b/Runtime/Dynamics/PhysXRaycast.h
@@ -0,0 +1,42 @@
+#ifndef PHYSXRAYCAST_H
+#define PHYSXRAYCAST_H
+
+#include "UnityPrefix.h"
+#if ENABLE_MULTITHREADED_CODE
+#include "Runtime/Threads/SimpleLock.h"
+#endif
+#include "Runtime/Interfaces/IRaycast.h"
+#include "Runtime/Geometry/AABB.h"
+
+class Plane;
+class NxShape;
+
+/// PhysX thread safety:
+/// Unfortunately scene query + concurrent writes to the SDK are not thread safe. However, concurrent reads such as scene queries from multiple threads should be safe.
+/// But simulate calls count as writes, so is not OK at the same time.
+class PhysXRaycast : public IRaycast
+{
+public:
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ // virtual interface implementation
+ virtual size_t BatchIntersect( const dynamic_array<BatchedRaycast>& raycasts, dynamic_array<BatchedRaycastResult>& results, const UInt32 filter, bool staticOnly ) const;
+ virtual bool Raycast (const Ray& ray, float distance, int mask, HitInfo& hit);
+
+ PhysXRaycast() {}
+ ~PhysXRaycast() {}
+
+private:
+ // helper functions for working with the NxShapes
+ bool Intersect( const Ray& ray, float maxDist, NxShape** shapes, AABB* shapeBounds, size_t shapeCount, HitInfo& hit ) const;
+ size_t GetShapes( const AABB& bounds, int maxResult, NxShape** outShapes, UInt32 groupMask = 0xffffffff, bool staticOnly = false ) const;
+ void GetAABB( AABB& bounds, const NxShape& shape ) const;
+ size_t BroadPhaseCulling(dynamic_array<NxShape*>& nxShapes, dynamic_array<AABB>& aabbs, const MinMaxAABB& particleSystemAABB, const int filter, bool staticOnly) const;
+
+#if ENABLE_MULTITHREADED_CODE
+ mutable SimpleLock m_Lock;
+#endif
+};
+
+#endif
diff --git a/Runtime/Dynamics/PhysicMaterial.cpp b/Runtime/Dynamics/PhysicMaterial.cpp
new file mode 100644
index 0000000..6077d46
--- /dev/null
+++ b/Runtime/Dynamics/PhysicMaterial.cpp
@@ -0,0 +1,340 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "PhysicMaterial.h"
+#include "PhysicsManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+enum MonoBounceCombineMode {
+ kMonoAverage = 0,
+ kMonoMultiply = 1,
+ kMonoMinimum = 2,
+ kMonoMaximum = 3,
+};
+
+static int MonoCombineModeToNxCombineMode(int monoMode)
+{
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1)) {
+ return monoMode;
+ }
+
+ switch (monoMode) {
+ case kMonoAverage: return NX_CM_AVERAGE;
+ case kMonoMultiply: return NX_CM_MULTIPLY;
+ case kMonoMinimum: return NX_CM_MIN;
+ case kMonoMaximum: return NX_CM_MAX;
+ }
+
+ return 0;
+}
+
+static int NxCombineModeToMonoCombineMode(int nxMode)
+{
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1)) {
+ return nxMode;
+ }
+
+ switch (nxMode) {
+ case NX_CM_AVERAGE: return kMonoAverage;
+ case NX_CM_MULTIPLY: return kMonoMultiply;
+ case NX_CM_MIN: return kMonoMinimum;
+ case NX_CM_MAX: return kMonoMaximum;
+ }
+
+ return 0;
+}
+
+PhysicMaterial::PhysicMaterial (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_Material = NULL;
+ m_MaterialIndex = -1;
+}
+
+PhysicMaterial::~PhysicMaterial ()
+{
+ MainThreadCleanup ();
+}
+
+bool PhysicMaterial::MainThreadCleanup ()
+{
+ if (m_Material)
+ {
+ GetDynamicsScene ().releaseMaterial (*m_Material);
+ m_Material = NULL;
+ }
+
+ return true;
+}
+
+
+void PhysicMaterial::InitializeClass ()
+{
+ RegisterAllowNameConversion(PhysicMaterial::GetClassStringStatic(), "bouncyness", "bounciness");
+}
+void PhysicMaterial::Reset ()
+{
+ Super::Reset();
+ m_FrictionCombine = 0;
+ m_BounceCombine = 0;
+ m_DynamicFriction = 0.4f;
+ m_StaticFriction = 0.4f;
+ m_Bounciness = 0;
+ m_DynamicFriction2 = 0;
+ m_StaticFriction2 = 0;
+ m_FrictionDirection2 = Vector3f::zero;
+}
+
+bool PhysicMaterial::IsDefaultMaterial ()
+{
+ return GetPhysicsManager ().m_CachedDefaultMaterial == this;
+}
+
+template<class TransferFunction>
+void PhysicMaterial::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ transfer.Transfer (m_DynamicFriction, "dynamicFriction", kSimpleEditorMask);
+ transfer.Transfer (m_StaticFriction, "staticFriction", kSimpleEditorMask);
+ transfer.Transfer (m_Bounciness, "bounciness", kSimpleEditorMask);
+
+ transfer.Transfer (m_FrictionCombine, "frictionCombine");
+ transfer.Transfer (m_BounceCombine, "bounceCombine");
+
+ transfer.Transfer (m_FrictionDirection2, "frictionDirection2");
+ transfer.Transfer (m_DynamicFriction2, "dynamicFriction2");
+ transfer.Transfer (m_StaticFriction2, "staticFriction2");
+}
+
+void PhysicMaterial::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ NxMaterialDesc representation;
+ representation.dynamicFriction = m_DynamicFriction;
+ representation.staticFriction = m_StaticFriction;
+ representation.restitution = clamp<float>(m_Bounciness, 0.0f, 1.0f);
+ representation.frictionCombineMode = (NxCombineMode)m_FrictionCombine;
+ representation.restitutionCombineMode = (NxCombineMode)m_BounceCombine;
+ representation.dynamicFrictionV = m_DynamicFriction2;
+ representation.staticFrictionV = m_StaticFriction2;
+
+ if (m_Material == NULL)
+ {
+ m_Material = GetDynamicsScene ().createMaterial (representation);
+ m_MaterialIndex = m_Material->getMaterialIndex ();
+ }
+ else
+ m_Material->loadFromDesc (representation);
+
+ UpdateFrictionDirection2 ();
+
+ if (IsDefaultMaterial ())
+ CopyMaterialToDefault ();
+}
+
+float PhysicMaterial::GetDynamicFriction () const
+{
+ return m_DynamicFriction;
+}
+
+void PhysicMaterial::SetDynamicFriction (float friction)
+{
+ if (friction < 0)
+ {
+ ErrorString( Format("Physics material %s cannot have dynamicFriction = %f", GetName(), friction) );
+ friction = 0.0f;
+ }
+
+ m_DynamicFriction = friction;
+ m_Material->setDynamicFriction (friction);
+ SetDirty ();
+}
+
+float PhysicMaterial::GetStaticFriction () const
+{
+ return m_StaticFriction;
+}
+
+void PhysicMaterial::SetStaticFriction (float friction)
+{
+ if (friction < 0)
+ {
+ ErrorString( Format("Physics material %s cannot have staticFriction = %f", GetName(), friction) );
+ friction = 0.0f;
+ }
+
+ m_StaticFriction = friction;
+ m_Material->setStaticFriction (friction);
+ SetDirty ();
+}
+
+float PhysicMaterial::GetBounciness () const
+{
+ return m_Bounciness;
+}
+
+Vector3f PhysicMaterial::GetFrictionDirection2 () const
+{
+ return m_FrictionDirection2;
+}
+
+float PhysicMaterial::GetStaticFriction2 () const
+{
+ return m_StaticFriction2;
+}
+
+float PhysicMaterial::GetDynamicFriction2 () const
+{
+ return m_DynamicFriction2;
+}
+
+int PhysicMaterial::GetFrictionCombine ()
+{
+ return NxCombineModeToMonoCombineMode(m_FrictionCombine);
+}
+
+int PhysicMaterial::GetBounceCombine ()
+{
+ return NxCombineModeToMonoCombineMode(m_BounceCombine);
+}
+
+void PhysicMaterial::ChangedMaterial ()
+{
+ if (IsDefaultMaterial ())
+ CopyMaterialToDefault ();
+ SetDirty ();
+}
+
+void PhysicMaterial::SetBounciness (float bounce)
+{
+ if (bounce < 0 || bounce > 1)
+ {
+ ErrorString( Format("Physics material %s cannot have bounciness = %f", GetName(), bounce) );
+ bounce = clamp(bounce, 0.0f, 1.0f);
+ }
+
+ m_Bounciness = bounce;
+ m_Material->setRestitution (bounce);
+ ChangedMaterial ();
+}
+
+void PhysicMaterial::UpdateFrictionDirection2 ()
+{
+ Vector3f dir = m_FrictionDirection2;
+ float magnitude = Magnitude (dir);
+ if (magnitude < 0.1F)
+ {
+ m_Material->setDirOfAnisotropy (NxVec3 (0,0,0));
+ m_Material->setFlags (m_Material->getFlags () & (~NX_MF_ANISOTROPIC));
+ }
+ else
+ {
+ dir /= magnitude;
+ m_Material->setDirOfAnisotropy ((NxVec3&)dir);
+ m_Material->setFlags (m_Material->getFlags () | NX_MF_ANISOTROPIC);
+ }
+
+}
+
+void PhysicMaterial::SetFrictionDirection2 (Vector3f dir)
+{
+ m_FrictionDirection2 = dir;
+ UpdateFrictionDirection2 ();
+ ChangedMaterial ();
+}
+
+void PhysicMaterial::SetDynamicFriction2 (float fric)
+{
+ if (fric < 0)
+ {
+ ErrorString( Format("Physics material %s cannot have dynamicFriction2 = %f", GetName(), fric) );
+ fric = 0.0f;
+ }
+
+ m_DynamicFriction2 = fric;
+ m_Material->setDynamicFrictionV (fric);
+ ChangedMaterial ();
+}
+
+void PhysicMaterial::SetStaticFriction2 (float fric)
+{
+ if (fric < 0)
+ {
+ ErrorString( Format("Physics material %s cannot have staticFriction2 = %f", GetName(), fric) );
+ fric = 0.0f;
+ }
+
+ m_StaticFriction2 = fric;
+ m_Material->setStaticFrictionV (fric);
+ ChangedMaterial ();
+}
+
+void PhysicMaterial::SetFrictionCombine (int mode)
+{
+ m_FrictionCombine = MonoCombineModeToNxCombineMode(mode);
+ m_Material->setFrictionCombineMode ((NxCombineMode)m_FrictionCombine);
+ ChangedMaterial ();
+}
+
+void PhysicMaterial::SetBounceCombine (int mode)
+{
+ m_BounceCombine = MonoCombineModeToNxCombineMode(mode);
+ m_Material->setRestitutionCombineMode ((NxCombineMode)m_BounceCombine);
+ ChangedMaterial ();
+}
+
+void PhysicMaterial::CopyMaterialToDefault () const
+{
+ NxMaterialDesc desc;
+ m_Material->saveToDesc (desc);
+ GetDynamicsScene ().getMaterialFromIndex (0)->loadFromDesc (desc);
+}
+
+PhysicMaterial& PhysicMaterial::GetInstantiatedMaterial (PhysicMaterial* material, Object& owner)
+{
+ if (material)
+ {
+ if (material->m_Owner == PPtr<Object> (&owner))
+ return const_cast<PhysicMaterial&> (*material);
+ else
+ {
+ PhysicMaterial* instance = NEW_OBJECT(PhysicMaterial);
+ instance->Reset ();
+
+ instance->SetNameCpp (Append (material->GetName (), " (Instance)"));
+ instance->m_FrictionCombine = material->m_FrictionCombine;
+ instance->m_BounceCombine = material->m_BounceCombine;
+ instance->m_DynamicFriction = material->m_DynamicFriction;
+ instance->m_StaticFriction = material->m_StaticFriction;
+ instance->m_Bounciness = material->m_Bounciness;
+ instance->m_DynamicFriction2 = material->m_DynamicFriction2;
+ instance->m_StaticFriction2 = material->m_StaticFriction2;
+ instance->m_FrictionDirection2 = material->m_FrictionDirection2;
+ instance->m_Owner = &owner;
+
+ instance->AwakeFromLoad (kDefaultAwakeFromLoad);
+ return *instance;
+ }
+ }
+ else
+ {
+ PhysicMaterial* instance = NEW_OBJECT (PhysicMaterial);
+ instance->Reset ();
+
+ instance->SetName ("Default (Instance)");
+ instance->m_Owner = &owner;
+
+ instance->AwakeFromLoad (kDefaultAwakeFromLoad);
+ return *instance;
+ }
+}
+
+IMPLEMENT_CLASS_HAS_INIT (PhysicMaterial)
+IMPLEMENT_OBJECT_SERIALIZE (PhysicMaterial)
+#endif //ENABLE_PHYSICS
diff --git a/Runtime/Dynamics/PhysicMaterial.h b/Runtime/Dynamics/PhysicMaterial.h
new file mode 100644
index 0000000..b31cded
--- /dev/null
+++ b/Runtime/Dynamics/PhysicMaterial.h
@@ -0,0 +1,99 @@
+#ifndef DYNAMICSMATERIAL_H
+#define DYNAMICSMATERIAL_H
+
+#if ENABLE_PHYSICS
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/Math/Vector3.h"
+#include "JointDescriptions.h"
+
+class NxMaterial;
+
+class PhysicMaterial : public NamedObject
+{
+ public:
+
+ REGISTER_DERIVED_CLASS (PhysicMaterial, NamedObject)
+ DECLARE_OBJECT_SERIALIZE (PhysicMaterial)
+
+ PhysicMaterial (MemLabelId label, ObjectCreationMode mode);
+ // ~PhysicMaterial (); declared-by-macro
+
+ virtual bool MainThreadCleanup ();
+
+ static void InitializeClass ();
+ static void CleanupClass () {}
+
+ float GetDynamicFriction () const;
+ void SetDynamicFriction (float friction);
+
+ float GetStaticFriction () const;
+ void SetStaticFriction (float friction);
+
+ void SetBounciness (float bounce);
+ float GetBounciness () const;
+
+ void SetFrictionDirection2 (Vector3f dir);
+ Vector3f GetFrictionDirection2 () const;
+
+ float GetStaticFriction2 () const;
+ void SetStaticFriction2 (float fric);
+
+ float GetDynamicFriction2 () const;
+ void SetDynamicFriction2 (float fric);
+
+ void SetSpring (const JointSpring& spring);
+ JointSpring GetSpring () const;
+
+ void SetUseSpring (bool use);
+ bool GetUseSpring () const;
+
+ int GetMaterialIndex () const { Assert(m_MaterialIndex > 0); return m_MaterialIndex; }
+
+ virtual void Reset ();
+
+ static PhysicMaterial& GetInstantiatedMaterial (PhysicMaterial* material, Object& owner);
+
+ int GetFrictionCombine ();
+ void SetFrictionCombine (int mode);
+
+ int GetBounceCombine ();
+ void SetBounceCombine (int mode);
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ private:
+
+ void UpdateFrictionDirection2 ();
+ Vector3f m_FrictionDirection2;///< Friction
+ int m_FrictionCombine;///< enum { Average = 0, Minimum = 1, Multiply = 2, Maximum = 3 }
+ int m_BounceCombine;///< enum { Average = 0, Minimum = 1, Multiply = 2, Maximum = 3 }
+ float m_DynamicFriction;///< range { 0, 1 }
+ float m_StaticFriction;///< range { 0, infinity }
+ float m_Bounciness;///< range { 0, 1 }
+ float m_DynamicFriction2;///< range { 0, 1 }
+ float m_StaticFriction2;///< range { 0, infinity }
+
+#if DOXYGEN
+ Vector3f frictionDirection2;///< Friction
+ int frictionCombine;///< enum { Average = 0, Minimum = 1, Multiply = 2, Maximum = 3 }
+ int bounceCombine;///< enum { Average = 0, Minimum = 1, Multiply = 2, Maximum = 3 }
+ float dynamicFriction;///< range { 0, 1 }
+ float staticFriction;///< range { 0, infinity }
+ float bounciness;///< range { 0, 1 }
+ float dynamicFriction2;///< range { 0, 1 }
+ float staticFriction2;///< range { 0, infinity }
+#endif
+
+ void CopyMaterialToDefault () const;
+ bool IsDefaultMaterial ();
+ void ChangedMaterial ();
+
+ int m_MaterialIndex;
+ NxMaterial* m_Material;
+ PPtr<Object> m_Owner;
+
+ friend class PhysicsManager;
+};
+
+#endif //ENABLE_PHYSICS
+#endif
diff --git a/Runtime/Dynamics/PhysicsManager.cpp b/Runtime/Dynamics/PhysicsManager.cpp
new file mode 100644
index 0000000..1ba62fe
--- /dev/null
+++ b/Runtime/Dynamics/PhysicsManager.cpp
@@ -0,0 +1,1508 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "PhysicsManager.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Input/TimeManager.h"
+#include "RigidBody.h"
+#include "Collider.h"
+#include "Joint.h"
+#include "PhysicMaterial.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/BaseClasses/MessageHandler.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "External/PhysX/builds/SDKs/Cooking/include/NxCooking.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxIntersectionBoxBox.h"
+#include "Runtime/BaseClasses/Tags.h"
+#include "Runtime/Utilities/Utility.h"
+#include "NxWrapperUtility.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/BaseClasses/Tags.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Filters/Deformation/SkinnedMeshFilter.h"
+#include "Cloth.h"
+#include "SkinnedCloth.h"
+#include "CharacterController.h"
+#include "PhysXRaycast.h"
+#include "Runtime/Profiler/ProfilerStats.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/BaseClasses/SupportedMessageOptimization.h"
+#include "Runtime/Core/Callbacks/PlayerLoopCallbacks.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+#include "PhysicsModule.h"
+
+#if UNITY_PS3
+#include "PS3/NxCellConfiguration.h"
+
+extern uint8_t g_aPhysXPriorities[8];
+extern CellSpurs2* g_pSpursInstance;
+
+#endif
+
+inline void VerifyObjectPtr(Object* obj)
+{
+ #if !UNITY_RELEASE
+ if (obj == NULL)
+ return;
+ // OutputDebugString is windows/360 only!
+ //if (Object::IDToPointer(obj->GetInstanceID()) != obj)
+ // OutputDebugString(Format("%x\n", obj).c_str());
+ Assert(Object::IDToPointer(obj->GetInstanceID()) == obj);
+ #endif
+}
+
+using namespace std;
+using namespace Unity;
+
+PROFILER_INFORMATION(gRaycastProfile, "Physics.Raycast", kProfilerPhysics)
+PROFILER_INFORMATION(gRaycastAllProfile, "Physics.RaycastAll", kProfilerPhysics)
+PROFILER_INFORMATION(gCapsuleTestProfile, "Physics.CheckCapsule", kProfilerPhysics)
+PROFILER_INFORMATION(gSphereOverlapProfile, "Physics.OverlapSphere", kProfilerPhysics)
+PROFILER_INFORMATION(gSphereTestProfile, "Physics.TestSphere", kProfilerPhysics)
+PROFILER_INFORMATION(gCapsuleCastProfile, "Physics.CapsuleCast", kProfilerPhysics)
+PROFILER_INFORMATION(gCapsuleCastAllProfile, "Physics.CapsuleCastAll", kProfilerPhysics)
+
+
+#define RETURNIFNOPHYSX( v ) if(!gPhysicsSDK) return v;
+#define RETURNIFNOPHYSX_VOID() if(!gPhysicsSDK) return;
+
+
+static Vector3f CalculateRelativeVelocity (NxActor& a, NxActor& b);
+
+#define DEBUG_VISUALIZE_PHYSX 0
+
+enum
+{
+#if UNITY_WII
+ kNovodexMemoryAlignment = 8
+#else
+ kNovodexMemoryAlignment = kDefaultMemoryAlignment
+#endif
+};
+
+class NovodexAllocator : public NxUserAllocator
+{
+ public:
+
+ void* malloc(size_t size, NxMemoryType type)
+ {
+ return UNITY_MALLOC_ALIGNED(kMemPhysics, size, kNovodexMemoryAlignment);
+ }
+
+ void* malloc(size_t size)
+ {
+ return UNITY_MALLOC_ALIGNED(kMemPhysics, size, kNovodexMemoryAlignment);
+ }
+
+ void* mallocDEBUG(size_t size, const char* fileName, int line, const char* className, NxMemoryType type)
+ {
+ return UNITY_MALLOC_ALIGNED(kMemPhysics, size, kNovodexMemoryAlignment);
+ return 0;
+ }
+ void* mallocDEBUG(size_t size, const char* fileName, int line)
+ {
+ return UNITY_MALLOC_ALIGNED(kMemPhysics, size, kNovodexMemoryAlignment);
+ return 0;
+ }
+
+ void* realloc(void* memory, size_t size)
+ {
+ return UNITY_REALLOC_ALIGNED(kMemPhysics, memory, size, kNovodexMemoryAlignment);
+ }
+
+ void free(void* memory)
+ {
+ UNITY_FREE(kMemPhysics, memory);
+ }
+
+/* check is a bad name to choose. The MacOSX libraries already Carbon declare that function name.
+ void check()
+ {
+ }
+*/
+};
+
+
+struct ErrorStream : public NxUserOutputStream
+{
+ virtual void reportError(NxErrorCode code, const char * message, const char *file, int line)
+ {
+ if (code == NXE_DB_WARNING)
+ return;
+
+ DebugStringToFile (message, 0, file, line, kError);
+ }
+ /**
+ Reports an assertion violation. The user should return
+ */
+ virtual NxAssertResponse reportAssertViolation(const char * message, const char *file, int line)
+ {
+ DebugStringToFile (message, 0, file, line, kAssert);
+ return NX_AR_CONTINUE;
+ }
+ /**
+ Simply prints some debug text
+ */
+ virtual void print(const char * message)
+ {
+ LogString (message);
+ }
+};
+
+static void CreateDynamicsScene ();
+
+/*
+struct CollisionData
+{
+
+};
+*/
+
+class RaycastCollector : public NxUserRaycastReport
+{
+ public:
+ PhysicsManager::RaycastHits* hits;
+
+ virtual bool onHit(const NxRaycastHit& hit)
+ {
+ RaycastHit outHit;
+ NxToRaycastHit(hit, outHit);
+ hits->push_back(outHit);
+ return true;
+ }
+};
+
+
+class TriggerMessage : public NxUserTriggerReport
+{
+ public:
+ PhysicsManager::RecordedTriggers* record;
+
+ virtual void onTrigger(NxShape& triggerShape, NxShape& otherShape, NxTriggerFlag status)
+ {
+ Collider* trigger = (Collider*)triggerShape.userData;
+ Collider* collider = (Collider*)otherShape.userData;
+ record->push_back (PhysicsManager::RecordedTrigger ());
+ record->back ().trigger = trigger;
+ record->back ().collider = collider;
+ record->back ().status = status;
+ }
+};
+
+static Vector3f CalculateRelativeVelocity (NxActor& a, NxActor& b)
+{
+ NxVec3 vel0, vel1;
+ if (a.isDynamic ())
+ vel0 = a.getLinearVelocity ();
+ else
+ vel0.zero ();
+
+ if (b.isDynamic ())
+ vel1 = b.getLinearVelocity ();
+ else
+ vel1.zero ();
+ vel0 -= vel1;
+
+ return (const Vector3f&)vel0;
+}
+
+class ContactMessage : public NxUserContactReport
+{
+ public:
+ PhysicsManager::RecordedContacts* record;
+
+ virtual void onContactNotify (NxContactPair& pair, NxU32 status)
+ {
+
+ Rigidbody* thisRigidbody = (Rigidbody*)pair.actors[0]->userData;
+ Rigidbody* otherRigidbody = (Rigidbody*)pair.actors[1]->userData;
+
+ Collider* thisCollider = NULL;
+ if (pair.actors[0]->getNbShapes())
+ thisCollider = (Collider*)pair.actors[0]->getShapes ()[0]->userData;
+
+ Collider* otherCollider = NULL;
+ if (pair.actors[1]->getNbShapes())
+ otherCollider = (Collider*)pair.actors[1]->getShapes ()[0]->userData;
+
+ record->push_back (Collision ());
+ record->back ().thisRigidbody = thisRigidbody;
+ record->back ().otherRigidbody = otherRigidbody;
+ record->back ().thisCollider = thisCollider;
+ record->back ().otherCollider = otherCollider;
+ record->back ().status = status;
+ record->back ().impactForceSum = (const Vector3f&)pair.sumNormalForce;
+ record->back ().frictionForceSum = (const Vector3f&)pair.sumFrictionForce;
+ record->back ().relativeVelocity = CalculateRelativeVelocity (*pair.actors[0], *pair.actors[1]);
+
+ VerifyObjectPtr(record->back ().thisRigidbody);
+ VerifyObjectPtr(record->back ().otherRigidbody);
+ VerifyObjectPtr(record->back ().thisCollider);
+ VerifyObjectPtr(record->back ().otherCollider);
+
+ std::list<ContactPoint>& contacts = record->back ().contacts;
+ NxContactStreamIterator i (pair.stream);
+
+ //user can call getNumPairs() here
+ while(i.goNextPair())
+ {
+ Collider* c0 = i.isDeletedShape(0) ? NULL : (Collider*)i.getShape (0)->userData;
+ Collider* c1 = i.isDeletedShape(1) ? NULL : (Collider*)i.getShape (1)->userData;
+ //user can also call getShape() and getNumPatches() here
+ while(i.goNextPatch())
+ {
+ Vector3f normal = (const Vector3f&)i.getPatchNormal ();
+ //user can also call getPatchNormal() and getNumPoints() here
+ while(i.goNextPoint())
+ {
+ ContactPoint c;
+ c.collider[0] = c0;
+ c.collider[1] = c1;
+ c.point = (const Vector3f&)i.getPoint ();
+ c.normal = normal;
+ contacts.push_back (c);
+ }
+ }
+ }
+ }
+};
+
+class BrokenJointMessage : public NxUserNotify
+{
+ public:
+ PhysicsManager::RecordedJointBreaks* records;
+
+ virtual bool onJointBreak (NxReal breakingForce, NxJoint & brokenJoint)
+ {
+ Joint* joint = (Joint*)brokenJoint.userData;
+ joint->NullJoint();
+ PhysicsManager::RecordedJointBreak jointbreak;
+ jointbreak.impulse = breakingForce;
+ jointbreak.joint = joint;
+ records->push_back(jointbreak);
+ return true;
+ }
+
+ virtual void onWake(NxActor** actors, NxU32 count) {}
+ virtual void onSleep(NxActor** actors, NxU32 count) { }
+};
+
+static ErrorStream gErrorStream;
+static NovodexAllocator gAllocator;
+static NxPhysicsSDK* gPhysicsSDK = NULL;
+static NxScene* gPhysicsScene = NULL;
+static std::vector<NxScene*> gClothingScenes;
+static NxScene* gInactivePhysicsScene = NULL;
+static NxActor* gNULLActor = NULL;
+
+void PhysicsManager::InitializeClass ()
+{
+ RegisterAllowNameConversion (PhysicsManager::GetClassStringStatic(), "m_BounceTreshold", "m_BounceThreshold");
+
+ PhysXRaycast::InitializeClass();
+ InitializePhysicsModule();
+
+ gPhysicsSDK = NxCreatePhysicsSDK (NX_PHYSICS_SDK_VERSION, &gAllocator, &gErrorStream);
+ if (!gPhysicsSDK)
+ FatalErrorString ("Couldn't load physics");
+ if (!NxInitCooking (0, &gErrorStream))
+ FatalErrorString ("Couldn't load physics ");
+
+#if UNITY_PS3
+ NxCellSpursControl::initWithSpurs(g_pSpursInstance,5, g_aPhysXPriorities);
+#endif
+
+#if DEBUG_VISUALIZE_PHYSX
+ gPhysicsSDK->setParameter (NX_VISUALIZATION_SCALE, 1F);
+ gPhysicsSDK->setParameter (NX_VISUALIZE_COLLISION_SHAPES, 1F);
+// gPhysicsSDK->setParameter (NX_VISUALIZE_COLLISION_COMPOUNDS, 1F);
+#endif
+
+ gPhysicsSDK->setParameter(NX_IMPROVED_SPRING_SOLVER, 0);
+
+ CreateDynamicsScene ();
+ CharacterController::CreateControllerManager ();
+
+ REGISTER_PLAYERLOOP_CALL (PhysicsFixedUpdate, GetPhysicsManager().FixedUpdate());
+ REGISTER_PLAYERLOOP_CALL (PhysicsUpdate, GetPhysicsManager().Update ());
+ REGISTER_PLAYERLOOP_CALL (PhysicsRefreshWhenPaused, GetPhysicsManager().RefreshWhenPaused ());
+ REGISTER_PLAYERLOOP_CALL (PhysicsSkinnedClothUpdate, GetPhysicsManager().SkinnedClothUpdate ());
+ REGISTER_PLAYERLOOP_CALL (PhysicsResetInterpolatedTransformPosition, GetPhysicsManager().ResetInterpolatedTransformPosition ());
+
+ REGISTER_GLOBAL_CALLBACK (didUnloadScene, GetPhysicsManager().RecreateScene());
+}
+
+
+// If we use multiple scenes per CPU, we may get better parallelization, when scene load becomes
+// unbalances because cloth objects get disabled or destroyed.
+// Apparently, PhysX doesn't handle more then 64 scenes - and we use two for rigidbody physics.
+
+#if (UNITY_XENON || UNITY_PS3)
+# define NUMBER_OF_CLOTHING_SCENES_PER_CPU 1
+# define MAX_CLOTHING_SCENES 6
+#else
+# define NUMBER_OF_CLOTHING_SCENES_PER_CPU 4
+# define MAX_CLOTHING_SCENES 32
+#endif
+
+static void CreateDynamicsScene ()
+{
+ AssertIf (gPhysicsScene != NULL);
+ NxSceneDesc sceneDesc;
+ // Multi threading causes issues when simulating 1000 objects at the same location.
+ // We are not taking advantage of it anyway. So...
+ // Test is in exception unit test on rudolph
+ sceneDesc.flags &= ~NX_SF_SIMULATE_SEPARATE_THREAD;
+
+ if (gInactivePhysicsScene == NULL)
+ gInactivePhysicsScene = gPhysicsSDK->createScene(sceneDesc);
+
+
+ gPhysicsScene = gPhysicsSDK->createScene(sceneDesc);
+ gPhysicsScene->setGravity (NxVec3 (0.0F, -9.81F, 0.0F));
+ gPhysicsScene->setTiming (1.0F, 8, NX_TIMESTEP_VARIABLE);
+//broken?
+#if !UNITY_WINRT
+ if (systeminfo::GetProcessorCount() > 1)
+ {
+ int numClothingScenes = std::min(NUMBER_OF_CLOTHING_SCENES_PER_CPU * systeminfo::GetProcessorCount(), MAX_CLOTHING_SCENES);
+ gClothingScenes.resize (numClothingScenes);
+ }
+ else
+#endif
+ gClothingScenes.resize (1);
+ for (std::vector<NxScene*>::iterator i = gClothingScenes.begin(); i!=gClothingScenes.end(); i++)
+ *i = NULL;
+
+ // Setup actor group masks!
+ // We put every actor into a group depending on what contact notifications it wants.
+ // This way we don't send too many contact notify messages!
+ int masks[3] = {
+ 0,
+ NX_NOTIFY_ON_START_TOUCH | NX_NOTIFY_ON_END_TOUCH,
+ NX_NOTIFY_ON_TOUCH | NX_NOTIFY_ON_START_TOUCH | NX_NOTIFY_ON_END_TOUCH
+ };
+
+ for (int i=0;i<3;i++)
+ {
+ for (int j=0;j<3;j++)
+ GetDynamicsScene ().setActorGroupPairFlags (i, j, masks[i] | masks[j]);
+ }
+
+ GetDynamicsScene ().setGroupCollisionFlag (kIgnoreCollisionLayer, kIgnoreCollisionLayer, 0);
+
+ // Create hole material
+ NxMaterialDesc holematerial;
+ NxMaterial* material = GetDynamicsScene ().createMaterial (holematerial);
+ AssertIf(material->getMaterialIndex() != 1);
+
+ // Create wheel collider material - used by wheel collider
+ NxMaterialDesc wheelMaterial;
+ wheelMaterial.flags |= NX_MF_DISABLE_FRICTION;
+
+ material = GetDynamicsScene ().createMaterial (wheelMaterial);
+ AssertIf(material->getMaterialIndex() != 2);
+}
+
+void PhysicsManager::CleanupReports()
+{
+ // is there any point doing these allocations?
+
+ TriggerMessage* trigger = (TriggerMessage*)gPhysicsScene->getUserTriggerReport();
+ gPhysicsScene->setUserTriggerReport(NULL);
+ delete trigger;
+
+ ContactMessage* contact = (ContactMessage*)gPhysicsScene->getUserContactReport();
+ gPhysicsScene->setUserContactReport(NULL);
+ delete contact;
+
+ BrokenJointMessage* notify = (BrokenJointMessage*)gPhysicsScene->getUserNotify();
+ gPhysicsScene->setUserNotify(NULL);
+ delete notify;
+}
+
+void PhysicsManager::CreateReports()
+{
+ CleanupReports();
+
+ TriggerMessage* triggerMessage = new TriggerMessage ();
+ triggerMessage->record = &m_RecordedTriggers;
+ gPhysicsScene->setUserTriggerReport (triggerMessage);
+
+ ContactMessage* contactMessage = new ContactMessage ();
+ contactMessage->record = &m_RecordedContacts;
+ gPhysicsScene->setUserContactReport (contactMessage);
+
+ BrokenJointMessage* brokenJoint = new BrokenJointMessage ();
+ brokenJoint->records = &m_RecordedJointBreaks;
+ gPhysicsScene->setUserNotify (brokenJoint);
+}
+
+void PhysicsManager::CleanupClass ()
+{
+ PhysXRaycast::CleanupClass();
+ CleanupPhysicsModule();
+
+ if( !gPhysicsSDK )
+ return; // happens when user quits the screen selector
+
+ // Needs to be cleaned up before the physics scene, or we get a crash, if there
+ // are still controllers around (as is the case in the editor, which does not clean up all objects).
+#if ENABLE_PHYSICS
+ CharacterController::CleanupControllerManager ();
+#endif
+
+ vector<PhysicMaterial*> materialsTemp;
+ vector<NxMaterialDesc> materialDescsTemp;
+ ReleaseMaterials(materialsTemp, materialDescsTemp);
+
+ CleanupReports();
+
+ gPhysicsSDK->releaseScene (*gPhysicsScene);
+ gPhysicsSDK->releaseScene (*gInactivePhysicsScene);
+
+ for (std::vector<NxScene*>::iterator i = gClothingScenes.begin(); i!=gClothingScenes.end(); i++)
+ {
+ if (*i != NULL)
+ {
+ gPhysicsSDK->releaseScene (**i);
+ *i = NULL;
+ }
+ }
+ std::vector<NxScene*> emptyScenes;
+ gClothingScenes.swap(emptyScenes);
+
+ // We are not using NxReleasePhysicsSDK, because it doesn't release the sdk (a bug in our implementation of USE_STATIC_LIBS)
+ // it's fine to call the release on gPhysicsSDK directly, except that it doesn't set g_nxPhysicsSDK in PhysX to NULL
+ // NxReleasePhysicsSDK(gPhysicsSDK);
+
+ NxFoundationSDK* foundationSDK = NxGetFoundationSDK ();
+ gPhysicsSDK->release ();
+ gPhysicsSDK = NULL;
+
+ foundationSDK->release();
+
+#if UNITY_PS3
+ if(NxCellSpursControl::isSpursInitialized())
+ CELLCALL(NxCellSpursControl::terminate());
+#endif
+}
+
+
+PhysicsManager::PhysicsManager (MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode)
+{
+ m_RaycastsHitTriggers = true;
+
+ m_Gravity = Vector3f (0, -9.81F, 0.0F);
+ m_LayerCollisionMatrix.resize (kNumLayers, 0xffffffff);
+ m_DefaultIterationCount = 6;
+ m_RigidbodyTransformMessageEnabled = true;
+ m_SmoothedClothDeltaTime = 0.0f;
+
+ RETURNIFNOPHYSX_VOID();
+
+ GetDynamicsSDK ().setParameter (NX_SKIN_WIDTH, 0.01F);
+ GetDynamicsSDK ().setParameter (NX_CONTINUOUS_CD, true);
+ GetDynamicsSDK ().setParameter (NX_CCD_EPSILON, 0.01F);
+
+ SetupDefaultMaterial ();
+ CreateReports();
+ Object::FindAllDerivedClasses (ClassID (Collider), &m_DisableTransformMessage);
+}
+
+void PhysicsManager::Reset ()
+{
+ RETURNIFNOPHYSX_VOID();
+
+ Super::Reset();
+
+ m_Gravity = Vector3f (0, -9.81F, 0.0F);
+ m_LayerCollisionMatrix.resize (kNumLayers, 0xffffffff);
+ m_DefaultIterationCount = 6;
+ GetDynamicsSDK ().setParameter (NX_SKIN_WIDTH, 0.01F);
+
+
+}
+
+PhysicsManager::~PhysicsManager ()
+{
+ CleanupReports();
+ // delete objects after setting them to null on novodex - just in case
+}
+
+inline Unity::Component* AttachedRigidbodyOrCollider (Collider& collider)
+{
+ Rigidbody* body = collider.GetRigidbody ();
+ if (body)
+ return body;
+ else
+ return &collider;
+}
+
+
+inline Unity::Component* GetRigidbodyOrCollider (Collision& col)
+{
+ return col.thisRigidbody != NULL ? (Unity::Component*)col.thisRigidbody : (Unity::Component*)col.thisCollider;
+}
+
+inline Unity::Component* GetOtherRigidbodyOrCollider (Collision& col)
+{
+ return col.otherRigidbody != NULL ? (Unity::Component*)col.otherRigidbody : (Unity::Component*)col.otherCollider;
+}
+
+void PhysicsManager::SetTransformMessageEnabled(bool enable)
+{
+ // Disable rigid body / collider transform changed message handler
+ // This simply avoids setting the position/rotation in the rigidbody while we are fetching the novodex state.
+ for (int i=0;i<m_DisableTransformMessage.size ();i++)
+ {
+ GameObject::GetMessageHandler ().SetMessageEnabled (m_DisableTransformMessage[i], kTransformChanged.messageID, enable);
+ }
+
+ m_RigidbodyTransformMessageEnabled = enable;
+}
+
+void PhysicsManager::ReleaseMaterials(vector<PhysicMaterial*>& materials, vector<NxMaterialDesc>& materialDescs)
+{
+ materials.clear();
+ Object::FindObjectsOfType(&materials);
+
+ materialDescs.resize(materials.size());
+
+ for (int i=0;i<materials.size();i++)
+ {
+ materials[i]->m_Material->saveToDesc(materialDescs[i]);
+ GetDynamicsScene ().releaseMaterial (*materials[i]->m_Material);
+ materials[i]->m_Material = NULL;
+ }
+}
+
+void PhysicsManager::RecreateScene ()
+{
+ RETURNIFNOPHYSX_VOID();
+
+ if (GetDynamicsScene().getNbActors() == 0 && GetDynamicsScene().getNbStaticShapes() == 0 && GetDynamicsScene().getNbJoints() == 0)
+ {
+ vector<PhysicMaterial*> materials;
+ vector<NxMaterialDesc> materialDescs;
+ ReleaseMaterials(materials, materialDescs);
+
+ CleanupReports();
+ for (std::vector<NxScene*>::iterator i = gClothingScenes.begin(); i!=gClothingScenes.end(); i++)
+ {
+ if (*i != NULL)
+ {
+ gPhysicsSDK->releaseScene (**i);
+ *i = NULL;
+ }
+ }
+ gPhysicsSDK->releaseScene (*gPhysicsScene);
+ gPhysicsScene = NULL;
+ CreateDynamicsScene ();
+ CreateReports();
+
+ for (int i=0;i<materials.size();i++)
+ {
+ NxMaterial* material = GetDynamicsScene ().createMaterial (materialDescs[i]);
+
+ if (material)
+ {
+ materials[i]->m_MaterialIndex = material->getMaterialIndex ();
+ }
+ else
+ {
+ ErrorString(std::string("Invalid physics material ") + materials[i]->GetName());
+ materials[i]->m_MaterialIndex = 0;
+ }
+
+ materials[i]->m_Material = material;
+
+ materials[i]->AwakeFromLoad (kDefaultAwakeFromLoad);
+ }
+
+ GetPhysicsManager().AwakeFromLoad(kDefaultAwakeFromLoad);
+ }
+}
+
+PROFILER_INFORMATION(gPhysicsProfile, "Physics.Simulate", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysicsClothProfile, "Physics.UpdateSkinnedCloth", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysicsInterpolationProfile, "Physics.Interpolation", kProfilerPhysics)
+
+void PhysicsManager::UpdateSkinnedClothes ()
+{
+#if ENABLE_CLOTH
+ if (m_SmoothedClothDeltaTime == 0.0)
+ m_SmoothedClothDeltaTime = GetTimeManager().GetDeltaTime();
+ else
+ m_SmoothedClothDeltaTime = Lerp (m_SmoothedClothDeltaTime, GetTimeManager().GetDeltaTime(), 0.01F);
+
+ if(!gClothingScenes.size())
+ return;
+
+ PROFILER_AUTO(gPhysicsClothProfile, NULL)
+
+ Assert(m_ActiveSkinnedMeshes.empty());
+ SkinnedMeshRenderer::UpdateAllSkinnedMeshes(SkinnedMeshRenderer::kUpdateCloth, &m_ActiveSkinnedMeshes);
+
+#if ENABLE_MULTITHREADED_SKINNING
+ JobScheduler& js = GetJobScheduler();
+ m_ClothJobGroup = js.BeginGroup (gClothingScenes.size());
+#endif
+
+ for (std::vector<NxScene*>::iterator i = gClothingScenes.begin(); i!= gClothingScenes.end(); i++)
+ {
+ if (*i != NULL)
+ {
+ if ((*i)->getNbCloths() == 0)
+ {
+ gPhysicsSDK->releaseScene (**i);
+ (*i) = NULL;
+ }
+ else
+ {
+ #if ENABLE_MULTITHREADED_SKINNING
+ js.SubmitJob (m_ClothJobGroup, SimulateClothingScene, *i, NULL);
+ #else
+ SimulateClothingScene(*i);
+ #endif
+ }
+ }
+ }
+#endif
+}
+
+void PhysicsManager::FinishUpdatingSkinnedClothes ()
+{
+#if ENABLE_CLOTH
+#if ENABLE_MULTITHREADED_SKINNING
+ PROFILER_AUTO(gPhysicsClothProfile, NULL)
+
+ JobScheduler& js = GetJobScheduler();
+ js.WaitForGroup (m_ClothJobGroup);
+#endif
+
+ SkinnedMeshRenderer::UploadSkinnedClothes(m_ActiveSkinnedMeshes);
+ m_ActiveSkinnedMeshes.clear();
+#endif
+}
+
+void PhysicsManager::FixedUpdate ()
+{
+ PROFILER_AUTO(gPhysicsProfile, NULL)
+
+ RETURNIFNOPHYSX_VOID();
+
+ AssertIf (!m_RecordedTriggers.empty ());
+
+ // Store interpolated position
+ for (InterpolatedBodiesIterator i=m_InterpolatedBodies.begin();i!=m_InterpolatedBodies.end();i++)
+ {
+ Rigidbody* body = i->body;
+ i->disabled = 0;
+ if (body->GetInterpolation() == kInterpolate)
+ {
+ i->position = body->GetPosition();
+ i->rotation = body->GetRotation();
+ }
+ }
+
+ // Force inactive scene to deallocate memory for all released actors
+ // Requires PhysX code to be patched accordingly
+ // NOTE: we don't need to call it on active physics scene since such deallocation will happen inside simulate() method
+ gInactivePhysicsScene->flushCaches();
+
+ gPhysicsScene->simulate(GetTimeManager ().GetFixedDeltaTime ());
+
+ gPhysicsScene->flushStream();
+ gPhysicsScene->fetchResults(NX_RIGID_BODY_FINISHED, true);
+
+ // Disable rigid body / collider / controller transform changed message handler
+ // This simply avoids setting the position/rotation in the rigidbody while we are fetching the novodex state.
+ SetTransformMessageEnabled (false);
+
+ // Update position / rotation of all rigid bodies
+ MessageData velocityMessageData;
+ for (int level=0;level<kMaxSortedActorsDepth;level++)
+ {
+ RigidbodyList& bodies = m_SortedActors[level];
+ for (RigidbodyList::iterator i=bodies.begin();i != bodies.end();i++)
+ {
+ Rigidbody& body = **i;
+ NxActor* actor = body.m_Actor;
+
+ if (actor->isSleeping ())
+ continue;
+
+ if (body.m_DisableReadUpdateTransform == 0)
+ {
+ GameObject& go = body.GetGameObject();
+ Transform& transform = go.GetComponent (Transform);
+ NxVec3 pos = actor->getGlobalPosition ();
+ NxQuat rot = actor->getGlobalOrientationQuat ();
+
+ /// @TODO: DONT NORMALIZE. NOVODEX VALUES ARE PRETTY CLOSE TO UNIT.
+ /// INSTEAD MAKE OUR NORMALIZE ASSERT CHECKS MORE RELAXED! LOTS OF WORK!
+ transform.SetPositionAndRotationSafe ((const Vector3f&)pos, (const Quaternionf&)rot);
+
+
+ // Synchronize velocity with other components eg. NavMesh
+ if (go.GetSupportedMessages() & kSupportsVelocityChanged)
+ {
+ Vector3f velocity = Vec3FromNx(actor->getLinearVelocity ());
+ velocityMessageData.SetData(&velocity, ClassID(Vector3f));
+
+ go.SendMessageAny(kDidVelocityChange, velocityMessageData);
+ }
+ }
+ }
+ }
+
+ // Enable rigid body transform changed message handler
+ SetTransformMessageEnabled (true);
+
+ ProcessRecordedReports();
+}
+
+void PhysicsManager::ResetInterpolatedTransformPosition ()
+{
+ PROFILER_AUTO(gPhysicsInterpolationProfile, NULL)
+
+ for (InterpolatedBodiesIterator i=m_InterpolatedBodies.begin();i!=m_InterpolatedBodies.end();i++)
+ {
+ Rigidbody* body = i->body;
+ if (body->IsSleeping())
+ continue;
+
+ Transform& transform = body->GetComponent(Transform);
+
+ Vector3f pos = body->GetPosition();
+ Quaternionf rot = body->GetRotation();
+ transform.SetPositionAndRotationSafeWithoutNotification (pos, rot);
+ }
+}
+
+#if ENABLE_PROFILER
+void PhysicsManager::GetPerformanceStats(PhysicsStats& physicsStats)
+{
+ NxSceneStats stats;
+ GetDynamicsScene().getStats(stats);
+
+ physicsStats.activeRigidbodies = stats.numDynamicActorsInAwakeGroups;
+ physicsStats.sleepingRigidbodies = stats.numDynamicActors - stats.numDynamicActorsInAwakeGroups;
+
+ physicsStats.numberOfShapePairs = stats.numPairs;
+
+ physicsStats.numberOfStaticColliders = stats.numStaticShapes;
+ physicsStats.numberOfDynamicColliders = stats.numDynamicShapes;
+}
+#endif
+
+void PhysicsManager::SkinnedClothUpdate ()
+{
+ UpdateSkinnedClothes();
+ FinishUpdatingSkinnedClothes ();
+}
+
+void *PhysicsManager::SimulateClothingScene (void *voidScene)
+{
+#if ENABLE_CLOTH
+ NxScene *scene = (NxScene*)voidScene;
+
+ // For correct simulation, cloth should be updated in Fixed Update.
+ // However, dynamic update tends to behave better for performance - as cloth is a purely visible effect, we care more about
+ // performance, then about accuracy.
+ // The problem is that physX seems to apply external forces stronger when using a longer delta time for the simulation,
+ // resulting in cloth hanging down further at slower frame rates, because gravity is stronger. This can be fixed by adjusting
+ // the gravity and external forces accordingly.
+
+ // Also, we don't use the actual dynamic delta time, but a highly smoothed version of it.
+ // Otherwise, the changes in frame rate cause the cloth to jitter.
+ // This will make the cloth simulate to fast or too slow for a seconds or so, when the frame rate changes, resulting in
+ // the appearance of more or less damping. While this is technically incorrect, it likely won't be notced, and seems to be better
+ // then the alternative of running in fixed time.
+
+ float timeScale = GetTimeManager().GetFixedDeltaTime() / GetPhysicsManager().m_SmoothedClothDeltaTime;
+ Vector3f adjustedGravity = GetPhysicsManager().m_Gravity*timeScale;
+ scene->setGravity ((NxVec3&)adjustedGravity);
+
+ // same adjustment for external forces
+ int numCloths = scene->getNbCloths();
+ NxCloth **cloths = scene->getCloths();
+ for (int j=0; j<numCloths; j++)
+ cloths[j]->setExternalAcceleration(cloths[j]->getExternalAcceleration() * timeScale);
+
+ scene->simulate (GetPhysicsManager().m_SmoothedClothDeltaTime);
+ scene->flushStream();
+ scene->fetchResults(NX_RIGID_BODY_FINISHED, true);
+
+ for (int j=0; j<numCloths; j++)
+ ((SkinnedCloth*)(cloths[j]->userData))->ReadBackSkinnedBuffers();
+#endif
+ return NULL;
+}
+
+NxScene* PhysicsManager::GetClothingScene ()
+{
+#if ENABLE_CLOTH
+ int minVertices = std::numeric_limits<int>::max();
+ std::vector<NxScene*>::iterator found = gClothingScenes.begin();
+ for (std::vector<NxScene*>::iterator i = gClothingScenes.begin(); i!= gClothingScenes.end(); i++)
+ {
+ if ((*i) == NULL)
+ {
+ NxSceneDesc sceneDesc;
+ // after simulating the cloth, we still need to transform the results, which can also be threaded.
+ // so, we use our own threading wrapped around PhysX. No need to enable this, and upper comment suggests
+ // it has issues.
+ sceneDesc.flags &= ~NX_SF_SIMULATE_SEPARATE_THREAD;
+ // Create the scene if it doesn't exist
+ (*i) = gPhysicsSDK->createScene(sceneDesc);
+ (*i)->setGravity (NxVec3 (0.0F, -9.81F, 0.0F));
+ (*i)->setTiming (1.0F, 8, NX_TIMESTEP_VARIABLE);
+#if UNITY_PS3
+ NxCellConfig::setSceneParamInt((*i),NxCellConfig::NX_CELL_SCENE_PARAM_SPU_CLOTH, 0);
+#endif
+ }
+
+ int numVertices = 0;
+ int numCloths = (**i).getNbCloths();
+ NxCloth **cloths = (**i).getCloths();
+ for (int j=0; j<numCloths; j++)
+ numVertices += cloths[j]->getNumberOfParticles();
+ if (numVertices < minVertices)
+ {
+ found = i;
+ minVertices = numVertices;
+ }
+ // No need to look further - this one is empty.
+ if (numVertices == 0)
+ return *found;
+ }
+ return *found;
+#else
+ return NULL;
+#endif
+}
+
+void PhysicsManager::Update ()
+{
+ PROFILER_AUTO(gPhysicsInterpolationProfile, NULL)
+
+ SetTransformMessageEnabled (false);
+
+ // Also disable rigidbody transform changed message, otherwise interpolation will affect physics results.
+ // This is not done in SetTransformMessageEnabled, as we need the rigidbody messages in the physics fixed update
+ // so kinematic child rigidbodies are moved with their parents.
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ GameObject::GetMessageHandler ().SetMessageEnabled (ClassID(Rigidbody), kTransformChanged.messageID, false);
+
+ // Interpolation time is [0...1] between the two steps
+ // Extrapolation time the delta time since the last fixed step
+ float dynamicTime = GetTimeManager().GetCurTime();
+ float step = GetTimeManager().GetFixedDeltaTime();
+ float fixedTime = GetTimeManager().GetFixedTime();
+ float interpolationTime = clamp01 ((dynamicTime - fixedTime) / step);
+ float extrapolationTime = dynamicTime - fixedTime;
+// AssertIf (t < 0.0F || t > 1.0F);
+
+ // Update interpolated position
+ for (InterpolatedBodiesIterator i=m_InterpolatedBodies.begin();i!=m_InterpolatedBodies.end();i++)
+ {
+ Rigidbody* body = i->body;
+ if (i->disabled || body->IsSleeping())
+ continue;
+
+ Transform& transform = body->GetComponent(Transform);
+
+ Quaternionf rot;
+ Vector3f pos;
+ RigidbodyInterpolation interpolation = body->GetInterpolation();
+ // Interpolate between this physics and last physics frame
+ if (interpolation == kInterpolate)
+ {
+ pos = Lerp(i->position, body->GetPosition(), interpolationTime);
+ rot = Slerp(i->rotation, body->GetRotation(), interpolationTime);
+ transform.SetPositionAndRotationSafe (pos, rot);
+ }
+ // Extrapolate current position using velocity
+ else if (interpolation == kExtrapolate)
+ {
+ pos = body->GetPosition() + body->GetVelocity() * extrapolationTime;
+ rot = AngularVelocityToQuaternion(body->GetAngularVelocity(), extrapolationTime) * body->GetRotation();
+ transform.SetPositionAndRotationSafe (pos, rot);
+ }
+ }
+ SetTransformMessageEnabled (true);
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ GameObject::GetMessageHandler ().SetMessageEnabled (ClassID(Rigidbody), kTransformChanged.messageID, true);
+}
+
+template<class TransferFunction>
+void PhysicsManager::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER_SIMPLE (m_Gravity);
+ TRANSFER_SIMPLE (m_DefaultMaterial);
+ TRANSFER_PROPERTY (float, m_BounceThreshold, GetBounceThreshold, SetBounceThreshold);
+ TRANSFER_PROPERTY (float, m_SleepVelocity, GetSleepVelocity, SetSleepVelocity);
+ TRANSFER_PROPERTY (float, m_SleepAngularVelocity, GetSleepAngularVelocity, SetSleepAngularVelocity);
+ TRANSFER_PROPERTY (float, m_MaxAngularVelocity, GetMaxAngularVelocity, SetMaxAngularVelocity);
+ TRANSFER_PROPERTY (float, m_MinPenetrationForPenalty, GetMinPenetrationForPenalty, SetMinPenetrationForPenalty);
+ TRANSFER_PROPERTY (int, m_SolverIterationCount, GetSolverIterationCount, SetSolverIterationCount);
+ TRANSFER (m_RaycastsHitTriggers);
+ transfer.Align();
+ transfer.Transfer (m_LayerCollisionMatrix, "m_LayerCollisionMatrix", kHideInEditorMask);
+}
+
+#if ENABLE_CLUSTER_SYNC
+template<class TransferFunction>
+void PhysicsManager::ClusterTransfer(TransferFunction& transfer)
+{
+ TRANSFER(m_SmoothedClothDeltaTime);
+}
+#endif
+
+void PhysicsManager::SetupDefaultMaterial ()
+{
+ m_CachedDefaultMaterial = m_DefaultMaterial;
+ if (m_CachedDefaultMaterial)
+ m_CachedDefaultMaterial->CopyMaterialToDefault ();
+ else
+ {
+ NxMaterialDesc material;
+ material.dynamicFriction = 0.6F;
+ material.staticFriction = 0.6F;
+ GetDynamicsScene ().getMaterialFromIndex (0)->loadFromDesc (material);
+ }
+}
+
+void PhysicsManager::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ RETURNIFNOPHYSX_VOID();
+ GetDynamicsScene ().setGravity((const NxVec3&)m_Gravity);
+ SetupCollisionLayerMatrix ();
+
+ /// FIXME: We need to check this since when we load from disk we don't have a physicsmanager setup yet.
+ if (GetManagerPtrFromContext (ManagerContext::kPhysicsManager))
+ {
+ SetupDefaultMaterial ();
+ }
+}
+
+NxActor* PhysicsManager::GetNULLActor ()
+{
+ if (gNULLActor)
+ return gNULLActor;
+
+ NxBodyDesc bodyDesc;
+ NxActorDesc actorDesc;
+ bodyDesc.massSpaceInertia = NxVec3 (1.0F, 1.0F, 1.0F);
+ bodyDesc.mass = 1.0F;
+ actorDesc.body = &bodyDesc;
+
+ gNULLActor = GetInactiveDynamicsScene ().createActor (actorDesc);
+ return gNULLActor;
+}
+
+
+Vector3f PhysicsManager::GetGravity ()
+{
+ return m_Gravity;
+}
+
+void PhysicsManager::WakeUpScene ()
+{
+ NxU32 nbActors = GetDynamicsScene().getNbActors();
+ NxActor** actors = GetDynamicsScene().getActors();
+ for (int i =0; i<nbActors; i++)
+ {
+ if (actors[i]->isDynamic())
+ actors[i]->wakeUp();
+ }
+}
+
+void PhysicsManager::SetGravity (const Vector3f& value)
+{
+ if (m_Gravity != value)
+ {
+ m_Gravity = value;
+ GetDynamicsScene ().setGravity ((const NxVec3&)value);
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1))
+ WakeUpScene ();
+ SetDirty ();
+ }
+}
+
+void PhysicsManager::SetMinPenetrationForPenalty (float value)
+{
+ RETURNIFNOPHYSX_VOID();
+ GetDynamicsSDK ().setParameter (NX_SKIN_WIDTH, value);
+ SetDirty ();
+}
+
+float PhysicsManager::GetMinPenetrationForPenalty ()
+{
+ RETURNIFNOPHYSX(1.0f);
+ return GetDynamicsSDK ().getParameter (NX_SKIN_WIDTH);
+}
+
+void PhysicsManager::SetBounceThreshold (float value)
+{
+ RETURNIFNOPHYSX_VOID();
+ GetDynamicsSDK ().setParameter (NX_BOUNCE_THRESHOLD, -value);
+ SetDirty ();
+}
+
+float PhysicsManager::GetBounceThreshold ()
+{
+ return -GetDynamicsSDK ().getParameter (NX_BOUNCE_THRESHOLD);
+}
+
+void PhysicsManager::SetSleepVelocity (float value)
+{
+ RETURNIFNOPHYSX_VOID();
+ GetDynamicsSDK ().setParameter (NX_DEFAULT_SLEEP_LIN_VEL_SQUARED, value * value);
+ SetDirty ();
+}
+
+float PhysicsManager::GetSleepVelocity ()
+{
+ return Sqrt (GetDynamicsSDK ().getParameter (NX_DEFAULT_SLEEP_LIN_VEL_SQUARED));
+}
+
+void PhysicsManager::SetSleepAngularVelocity (float value)
+{
+ RETURNIFNOPHYSX_VOID();
+ GetDynamicsSDK ().setParameter (NX_DEFAULT_SLEEP_ANG_VEL_SQUARED, value * value);
+ SetDirty ();
+}
+
+float PhysicsManager::GetSleepAngularVelocity ()
+{
+ return Sqrt (GetDynamicsSDK ().getParameter (NX_DEFAULT_SLEEP_ANG_VEL_SQUARED));
+}
+
+void PhysicsManager::SetMaxAngularVelocity (float value)
+{
+ RETURNIFNOPHYSX_VOID();
+ GetDynamicsSDK ().setParameter (NX_MAX_ANGULAR_VELOCITY, value);
+ SetDirty ();
+}
+
+float PhysicsManager::GetMaxAngularVelocity ()
+{
+ return GetDynamicsSDK ().getParameter (NX_MAX_ANGULAR_VELOCITY);
+}
+
+void PhysicsManager::SetSolverIterationCount (int value)
+{
+ RETURNIFNOPHYSX_VOID();
+ m_DefaultIterationCount = clamp(value, 1, 100);
+ SetDirty ();
+}
+
+class CollideShapesReport : public NxUserEntityReport<NxShape*>
+{
+public:
+
+ NxSphere sphere;
+ bool checkShapes;
+ PhysicsManager::ColliderCache& cache;
+ CollideShapesReport (PhysicsManager::ColliderCache& c) : cache (c) {}
+
+ virtual bool onEvent(NxU32 nbEntities, NxShape** entities)
+ {
+ int offset = cache.size ();
+ cache.reserve (offset + nbEntities);
+ for (int i=0;i<nbEntities;i++)
+ {
+ if (entities[i]->checkOverlapSphere (sphere))
+ cache.push_back ((Collider*)entities[i]->userData);
+ }
+ return true;
+ }
+};
+
+PhysicsManager::ColliderCache& PhysicsManager::OverlapSphere (const Vector3f& p, float radius, int mask)
+{
+ PROFILER_AUTO(gSphereOverlapProfile, NULL)
+ m_ColliderCache.clear ();
+ CollideShapesReport report (m_ColliderCache);
+ report.sphere = NxSphere ((NxVec3&)p, radius);
+ report.checkShapes = true;
+
+ GetDynamicsScene ().overlapSphereShapes (report.sphere, NX_ALL_SHAPES, 0, NULL, &report, mask);
+ return m_ColliderCache;
+}
+
+bool PhysicsManager::SphereTest (const Vector3f& p, float radius, int mask)
+{
+ PROFILER_AUTO(gSphereTestProfile, NULL)
+ NxSphere sphere((NxVec3&)p, radius);
+ return GetDynamicsScene ().checkOverlapSphere (sphere, NX_ALL_SHAPES, mask);
+}
+
+
+bool PhysicsManager::RaycastTest (const Ray& ray, float distance, int mask)
+{
+ PROFILER_AUTO(gRaycastProfile, NULL)
+
+ AssertIf (!IsNormalized (ray.GetDirection ()));
+ if (distance == std::numeric_limits<float>::infinity())
+ distance = NX_MAX_F32;
+ return GetDynamicsScene ().raycastAnyShape ((NxRay&)ray, NX_ALL_SHAPES, mask, distance);
+}
+
+bool PhysicsManager::Raycast (const Ray& ray, float distance, RaycastHit& outHit, int mask)
+{
+ AssertIf (!IsNormalized (ray.GetDirection ()));
+ PROFILER_AUTO(gRaycastProfile, NULL)
+
+ if (distance == std::numeric_limits<float>::infinity())
+ distance = NX_MAX_F32;
+
+ NxRaycastHit hit;
+ NxShape* shape = GetDynamicsScene ().raycastClosestShape ((NxRay&)ray, NX_ALL_SHAPES, hit, mask, distance);
+ if (shape)
+ {
+ NxToRaycastHit(hit, outHit);
+ return true;
+ }
+ else
+ return false;
+}
+
+bool PhysicsManager::CapsuleCast (const Vector3f &p0, const Vector3f &p1, float radius, const Vector3f &direction, float distance, RaycastHit& outHit, int mask)
+{
+ AssertIf (!IsNormalized (direction));
+ PROFILER_AUTO(gCapsuleCastProfile, NULL)
+
+ if (distance == std::numeric_limits<float>::infinity())
+ // CapsuleCasts fail when using NX_MAX_F32 here.
+ // So pick a lower "high" number instead.
+ distance = 1000000.0f;
+
+ NxCapsule testCapsule;
+ testCapsule.radius = radius;
+ testCapsule.p0 = (const NxVec3&)p0;
+ testCapsule.p1 = (const NxVec3&)p1;
+
+ NxSweepQueryHit hit;
+ NxU32 nb = GetDynamicsScene ().linearCapsuleSweep (testCapsule, (const NxVec3&)direction * distance, NX_SF_DYNAMICS|NX_SF_STATICS, NULL, 1, &hit, NULL, mask);
+ if (nb)
+ {
+ NxToRaycastHit(hit, distance, outHit);
+ return true;
+ }
+ else
+ return false;
+}
+
+void NxToRaycastHit (const NxRaycastHit& hit, RaycastHit& outHit)
+{
+ outHit.collider = (Collider*)hit.shape->userData;
+ outHit.point = (const Vector3f&)hit.worldImpact;
+ outHit.normal = (const Vector3f&)hit.worldNormal;
+ outHit.faceID = hit.faceID;
+ outHit.distance = hit.distance;
+ outHit.uv.x = hit.u;
+ outHit.uv.y = hit.v;
+}
+
+void NxToRaycastHit (const NxSweepQueryHit& hit, float sweepDistance, RaycastHit& outHit)
+{
+ outHit.collider = (Collider*)hit.hitShape->userData;
+ outHit.point = (const Vector3f&)hit.point;
+ outHit.normal = (const Vector3f&)hit.normal;
+ outHit.faceID = hit.faceID;
+ outHit.distance = hit.t * sweepDistance;
+ outHit.uv.x = 0;
+ outHit.uv.y = 0;
+}
+
+bool PhysicsManager::CapsuleTest (Vector3f start, Vector3f end, float radius, int mask)
+{
+ PROFILER_AUTO(gCapsuleTestProfile, NULL)
+ NxCapsule capsule;
+ capsule.p0 = Vec3ToNx(start);
+ capsule.p1 = Vec3ToNx(end);
+ capsule.radius = radius;
+
+ return GetDynamicsScene ().checkOverlapCapsule (capsule, NX_ALL_SHAPES, mask);
+}
+
+
+const PhysicsManager::RaycastHits& PhysicsManager::RaycastAll (const Ray& ray, float distance, int mask)
+{
+ AssertIf (!IsNormalized (ray.GetDirection ()));
+ PROFILER_AUTO(gRaycastAllProfile, NULL)
+
+ if (distance == std::numeric_limits<float>::infinity())
+ distance = NX_MAX_F32;
+
+ static vector<RaycastHit> hits;
+ hits.resize(0);
+ RaycastCollector collector;
+ collector.hits = &hits;
+
+ GetDynamicsScene ().raycastAllShapes ((NxRay&)ray, collector, NX_ALL_SHAPES, mask, distance);
+
+ return hits;
+}
+
+#define kCapsuleCastMaxHits 128
+const PhysicsManager::RaycastHits& PhysicsManager::CapsuleCastAll (const Vector3f &p0, const Vector3f &p1, float radius, const Vector3f &direction, float distance, int mask)
+{
+ AssertIf (!IsNormalized (direction));
+ PROFILER_AUTO(gCapsuleCastAllProfile, NULL)
+
+ if (distance == std::numeric_limits<float>::infinity())
+ // CapsuleCasts fail when using NX_MAX_F32 here.
+ // So pick a lower "high" number instead.
+ distance = 1000000.0f;
+
+ static vector<RaycastHit> outHits;
+
+ NxCapsule testCapsule;
+ testCapsule.radius = radius;
+ testCapsule.p0 = (const NxVec3&)p0;
+ testCapsule.p1 = (const NxVec3&)p1;
+
+ NxSweepQueryHit hits[kCapsuleCastMaxHits];
+ NxU32 nb = GetDynamicsScene ().linearCapsuleSweep (testCapsule, (const NxVec3&)direction * distance, NX_SF_DYNAMICS|NX_SF_STATICS|NX_SF_ALL_HITS, NULL, kCapsuleCastMaxHits, hits, NULL, mask);
+
+ outHits.resize(nb);
+ for (int i=0; i<nb; i++)
+ NxToRaycastHit(hits[i], distance, outHits[i]);
+
+ return outHits;
+}
+
+void PhysicsManager::IgnoreCollision (Collider& lhs, Collider& rhs, bool ignore)
+{
+ if (lhs.m_Shape == NULL || rhs.m_Shape == NULL)
+ {
+ ErrorString ("Ignore collision failed. Both colliders need to be activated when calling this IgnoreCollision");
+ return;
+ }
+
+ if (ignore)
+ GetDynamicsScene().setShapePairFlags(*lhs.m_Shape, *rhs.m_Shape, NX_IGNORE_PAIR);
+ else
+ GetDynamicsScene().setShapePairFlags(*lhs.m_Shape, *rhs.m_Shape, 0);
+}
+
+void PhysicsManager::IgnoreCollision(int layer1, int layer2, bool ignore)
+{
+ if (layer1 >= kNumLayers || layer2 >= kNumLayers)
+ {
+ ErrorString(Format("layer numbers must be between 0 and %d", kNumLayers));
+ return;
+ }
+
+ Assert (kNumLayers <= m_LayerCollisionMatrix.size());
+ Assert (kNumLayers <= sizeof(m_LayerCollisionMatrix[0])*8);
+
+ GetDynamicsScene ().setGroupCollisionFlag(layer1, layer2, !ignore);
+ if (ignore)
+ {
+ m_LayerCollisionMatrix[layer1] &= ~(1<<layer2);
+ m_LayerCollisionMatrix[layer2] &= ~(1<<layer1);
+ }
+ else
+ {
+ m_LayerCollisionMatrix[layer1] |= 1<<layer2;
+ m_LayerCollisionMatrix[layer2] |= 1<<layer1;
+ }
+ SetDirty();
+}
+
+bool PhysicsManager::GetIgnoreCollision(int layer1, int layer2)
+{
+ if (layer1 >= kNumLayers || layer2 >= kNumLayers)
+ {
+ ErrorString(Format("layer numbers must be between 0 and %d", kNumLayers));
+ return false;
+ }
+
+ return !GetDynamicsScene ().getGroupCollisionFlag(layer1, layer2);
+}
+
+void PhysicsManager::SetupCollisionLayerMatrix()
+{
+ for (int i=0;i<kNumLayers;i++)
+ {
+ for (int j=0;j<kNumLayers;j++)
+ {
+ if (i <= j)
+ {
+ bool enabled = m_LayerCollisionMatrix[i] & (1<<j);
+ GetDynamicsScene().setGroupCollisionFlag(i, j, enabled);
+ }
+ }
+ }
+}
+
+
+void PhysicsManager::AddBody(int depth, ListNode<Rigidbody>& node)
+{
+ depth = min(kMaxSortedActorsDepth-1, depth);
+ if (depth >= kMaxSortedActorsDepth-1)
+ {
+ ErrorString("Too deep hierarchy to perform rigidbody ordering. Nested rigidbodies might look strange");
+ depth = kMaxSortedActorsDepth-1;
+ }
+ m_SortedActors[depth].push_back(node);
+}
+
+NxScene& GetDynamicsScene ()
+{
+ AssertIf (gPhysicsScene == NULL);
+ return *gPhysicsScene;
+}
+
+NxScene& GetInactiveDynamicsScene ()
+{
+ AssertIf (gInactivePhysicsScene == NULL);
+ return *gInactivePhysicsScene;
+}
+
+
+NxPhysicsSDK& GetDynamicsSDK ()
+{
+ AssertIf (gPhysicsSDK == NULL);
+ return *gPhysicsSDK;
+}
+
+void PhysicsManager::ProcessRecordedReports()
+{
+ SetDisableImmediateDestruction(true);
+
+ /// Report recorded triggers.
+ /// We can't call the recorded triggers immediately because novodex still keeps a write lock at that time.
+ /// So we can't do much with physics at that time.
+ for (int i=0;i<m_RecordedTriggers.size ();i++)
+ {
+ RecordedTrigger& trigger = m_RecordedTriggers[i];
+
+ VerifyObjectPtr(trigger.collider);
+ VerifyObjectPtr(trigger.trigger);
+
+ Unity::Component* masterOfCollider = AttachedRigidbodyOrCollider (*trigger.collider);
+ Unity::Component* masterOfTrigger = AttachedRigidbodyOrCollider (*trigger.trigger);
+ MessageIdentifier messageID;
+ if (trigger.status == NX_TRIGGER_ON_ENTER)
+ messageID = kEnterTrigger;
+ else if (trigger.status == NX_TRIGGER_ON_LEAVE)
+ messageID = kExitTrigger;
+ else
+ messageID = kStayTrigger;
+
+ trigger.trigger->SendMessage (messageID, trigger.collider, ClassID (Collider));
+ masterOfCollider->SendMessage (messageID, trigger.trigger, ClassID (Collider));
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ {
+ if (masterOfCollider->GetGameObjectPtr() != trigger.collider->GetGameObjectPtr() && trigger.collider->GetIsTrigger())
+ trigger.collider->SendMessage (messageID, trigger.trigger, ClassID (Collider));
+ if (masterOfTrigger->GetGameObjectPtr() != trigger.trigger->GetGameObjectPtr())
+ masterOfTrigger->SendMessage (messageID, trigger.collider, ClassID (Collider));
+ }
+ }
+ m_RecordedTriggers.clear ();
+
+ /// Report recorded contact events.
+ for (int i=0;i<m_RecordedContacts.size ();i++)
+ {
+ Collision& contact = m_RecordedContacts[i];
+ VerifyObjectPtr(contact.thisRigidbody);
+ VerifyObjectPtr(contact.otherRigidbody);
+ VerifyObjectPtr(contact.thisCollider);
+ VerifyObjectPtr(contact.otherCollider);
+
+ if (contact.status & NX_NOTIFY_ON_START_TOUCH)
+ {
+ contact.flipped = false;
+ GetRigidbodyOrCollider(contact)->SendMessage (kEnterContact, &contact, ClassID (Collision));
+ contact.flipped = true;
+ GetOtherRigidbodyOrCollider(contact)->SendMessage (kEnterContact, &contact, ClassID (Collision));
+ }
+ if (contact.status & NX_NOTIFY_ON_END_TOUCH)
+ {
+ contact.flipped = false;
+ GetRigidbodyOrCollider(contact)->SendMessage (kExitContact, &contact, ClassID (Collision));
+ contact.flipped = true;
+ GetOtherRigidbodyOrCollider(contact)->SendMessage (kExitContact, &contact, ClassID (Collision));
+ }
+ if (contact.status & NX_NOTIFY_ON_TOUCH)
+ {
+ contact.flipped = false;
+ GetRigidbodyOrCollider(contact)->SendMessage (kStayContact, &contact, ClassID (Collision));
+ contact.flipped = true;
+ GetOtherRigidbodyOrCollider(contact)->SendMessage (kStayContact, &contact, ClassID (Collision));
+ }
+ }
+ m_RecordedContacts.clear ();
+
+ /// Report recorded joint breaks
+ for (int i=0;i<m_RecordedJointBreaks.size ();i++)
+ {
+ RecordedJointBreak& record = m_RecordedJointBreaks[i];
+ Joint* joint = record.joint;
+ if (joint && joint->IsActive())
+ {
+ joint->GetGameObject().SendMessage (kJointBreak, record.impulse, ClassID (float));
+ }
+ joint = record.joint;
+ if (joint && joint->GetGameObjectPtr())
+ {
+ SetDisableImmediateDestruction(false);
+ DestroyObjectHighLevel(joint, true);
+ SetDisableImmediateDestruction(true);
+ }
+ }
+ m_RecordedJointBreaks.clear();
+
+ SetDisableImmediateDestruction(false);
+}
+
+// When using raycasts or other physics functionality in edit mode (such as is done when generating terrain lightmaps),
+// this needs to be called to make sure all updates to colliders in the scene are taken into account.
+void PhysicsManager::RefreshWhenPaused()
+{
+ gPhysicsScene->simulate(0);
+ gPhysicsScene->flushCaches();
+ gPhysicsScene->flushStream();
+ gPhysicsScene->fetchResults(NX_RIGID_BODY_FINISHED, true);
+ ProcessRecordedReports();
+}
+
+#if ENABLE_SCRIPTING
+ScriptingArrayPtr ConvertNativeRaycastHitsToManaged(const PhysicsManager::RaycastHits& hits)
+{
+ ScriptingArrayPtr arr = CreateScriptingArray(hits.size() > 0 ? &hits[0] : NULL, hits.size(), GetScriptingManager().GetCommonClasses().raycastHit);
+ RaycastHit* firstElement = Scripting::GetScriptingArrayStart<RaycastHit>(arr);
+ for (int i=0;i<hits.size();i++)
+ {
+ firstElement[i].collider = reinterpret_cast<Collider*>(ScriptingGetObjectReference (firstElement[i].collider));
+ }
+ return arr;
+}
+#endif
+
+GET_MANAGER (PhysicsManager)
+GET_MANAGER_PTR (PhysicsManager)
+IMPLEMENT_CLASS_HAS_INIT (PhysicsManager)
+IMPLEMENT_OBJECT_SERIALIZE (PhysicsManager)
+IMPLEMENT_CLUSTER_SERIALIZE (PhysicsManager)
+#endif //ENABLE_PHYSICS
diff --git a/Runtime/Dynamics/PhysicsManager.h b/Runtime/Dynamics/PhysicsManager.h
new file mode 100644
index 0000000..654ea8f
--- /dev/null
+++ b/Runtime/Dynamics/PhysicsManager.h
@@ -0,0 +1,235 @@
+#ifndef PHYSICSMANAGER_H
+#define PHYSICSMANAGER_H
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Geometry/Ray.h"
+#include "Runtime/Misc/MessageParameters.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Threads/JobScheduler.h"
+#include "Runtime/ClusterRenderer/ClusterRendererDefines.h"
+
+class Collider;
+class CharacterController;
+class SkinnedMeshRenderer;
+class PhysicMaterial;
+namespace Unity { class Component; }
+namespace Unity { class Joint; }
+class NxActor;
+class NxScene;
+class NxShape;
+class NxUserControllerHitReport;
+struct NxRaycastHit;
+struct NxSweepQueryHit;
+class NxMaterialDesc;
+struct PhysicsStats;
+struct RaycastHit
+{
+ Vector3f point;
+ Vector3f normal;
+ UInt32 faceID;
+ float distance;
+ Vector2f uv;
+ Collider* collider;
+};
+
+struct RigidbodyInterpolationInfo : public ListElement
+{
+ Vector3f position;
+ Quaternionf rotation;
+ Rigidbody* body;
+ int disabled;
+};
+
+class PhysicsManager : public GlobalGameManager {
+ public:
+
+ PhysicsManager (MemLabelId label, ObjectCreationMode mode);
+ // ~PhysicsManager (); declared-by-macro
+
+ REGISTER_DERIVED_CLASS (PhysicsManager, GlobalGameManager)
+ DECLARE_OBJECT_SERIALIZE (PhysicsManager)
+ DECLARE_CLUSTER_SERIALIZE (PhysicsManager)
+
+ void FixedUpdate ();
+ void Update ();
+ void SkinnedClothUpdate ();
+ void ResetInterpolatedTransformPosition ();
+ void RefreshWhenPaused ();
+ void RecreateScene ();
+
+ void GetPerformanceStats(PhysicsStats& physicsStats);
+
+ void UpdateSkinnedClothes();
+ void FinishUpdatingSkinnedClothes ();
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ Vector3f GetGravity ();
+ void SetGravity (const Vector3f& value);
+
+ void SetMinPenetrationForPenalty (float value);
+ float GetMinPenetrationForPenalty ();
+
+ void SetBounceThreshold (float value);
+ float GetBounceThreshold ();
+
+ void SetSleepVelocity (float value);
+ float GetSleepVelocity ();
+
+ void SetSleepAngularVelocity (float value);
+ float GetSleepAngularVelocity ();
+
+ void SetMaxAngularVelocity (float value);
+ float GetMaxAngularVelocity ();
+
+ void SetSolverIterationCount (int value);
+ int GetSolverIterationCount () { return m_DefaultIterationCount; }
+
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ // Overlap sphere
+ typedef std::vector<Collider*> ColliderCache;
+ ColliderCache& OverlapSphere (const Vector3f& p, float radius, int mask);
+ bool SphereTest (const Vector3f& p, float radius, int mask);
+
+ bool CapsuleTest (Vector3f start, Vector3f end, float radius, int mask);
+
+ typedef std::vector<RaycastHit> RaycastHits;
+
+ bool RaycastTest (const Ray& ray, float distance, int mask);
+ bool Raycast (const Ray& ray, float distance, RaycastHit& hit, int mask);
+ bool CapsuleCast (const Vector3f &p0, const Vector3f &p1, float radius, const Vector3f &direction, float distance, RaycastHit& outHit, int mask);
+ const RaycastHits& RaycastAll (const Ray& ray, float distance, int mask);
+ const RaycastHits& CapsuleCastAll (const Vector3f &p0, const Vector3f &p1, float radius, const Vector3f &direction, float distance, int mask);
+
+ NxActor* GetNULLActor ();
+
+ bool GetRaycastsHitTriggers () { return m_RaycastsHitTriggers; }
+
+ void IgnoreCollision (Collider& a, Collider& b, bool ignore);
+ void IgnoreCollision (int layer1, int layer2, bool ignore);
+ bool GetIgnoreCollision(int layer1, int layer2);
+ UInt32 GetLayerCollisionMask(int layer) {return m_LayerCollisionMatrix[layer];}
+
+ bool IsRigidbodyTransformMessageEnabled() {return m_RigidbodyTransformMessageEnabled;}
+ public:
+
+ void AddBody(int depth, ListNode<Rigidbody>& node);
+ NxScene* GetClothingScene ();
+
+ // Only for internal use
+ void SetTransformMessageEnabled(bool enable);
+
+ List<RigidbodyInterpolationInfo>& GetInterpolatedBodies() { return m_InterpolatedBodies; }
+
+ struct RecordedTrigger
+ {
+ int status;
+ Collider* trigger;
+ Collider* collider;
+ };
+
+ struct RecordedJointBreak
+ {
+ float impulse;
+ PPtr<Unity::Joint> joint;
+ };
+
+ typedef std::vector<RecordedTrigger> RecordedTriggers;
+ typedef std::vector<Collision> RecordedContacts;
+ typedef std::vector<RecordedJointBreak> RecordedJointBreaks;
+
+private:
+ static void CleanupReports();
+ void CreateReports();
+ void ProcessRecordedReports();
+ void SetupCollisionLayerMatrix();
+ void WakeUpScene ();
+ static void *SimulateClothingScene (void *voidIndex);
+
+ static void ReleaseMaterials(std::vector<PhysicMaterial*>& materials, std::vector<NxMaterialDesc>& materialDescs);
+
+
+ Vector3f m_Gravity;///< The gravity applied to all rigid bodies in the scene
+ virtual void Reset ();
+
+ #if DOXYGEN
+
+ /// The minimum contact penetration value in order to apply a penalty force. (Default 0.01) range { 0 , infinity }
+ float m_MinPenetrationForPenalty;
+ /// The penalty force applied to the bodies in an interpenetrating contact is scaled by this value. (Default 0.6) range { 0, 2 }
+ float m_PenetrationPenaltyForce;
+ /// A contact with a relative velocity below this will not bounce. (Default 2) range { 0, infinity }
+ float m_BounceThreshold;
+ /// The default linear velocity, below which objects start going to sleep. This value can be overridden per rigidbodies with scripting. (Default 0.15) range { 0, infinity }
+ float m_SleepVelocity;
+ /// The default linear velocity, below which objects start going to sleep. This value can be overridden per rigidbodies with scripting. (Default 0.14) range { 0, infinity }
+ float m_SleepAngularVelocity;
+ /// The maximum angular velocity permitted for any rigid bodies. This can be overridden per rigidbodies with scripting. (Default 7) range { 0, infinity }
+ float m_MaxAngularVelocity;
+
+ #endif
+
+ bool m_RaycastsHitTriggers;
+
+ bool m_RigidbodyTransformMessageEnabled;
+ void SetupDefaultMaterial ();
+
+
+ PPtr<PhysicMaterial> m_DefaultMaterial;
+ PhysicMaterial* m_CachedDefaultMaterial;
+
+ ColliderCache m_ColliderCache;
+
+// typedef std::set<PPtr<PhysicMaterial> > Materials;
+// Materials m_Materials;
+
+ RecordedTriggers m_RecordedTriggers;
+ RecordedContacts m_RecordedContacts;
+ RecordedJointBreaks m_RecordedJointBreaks;
+ /// Solver accuracy. Higher value costs more performance. (Default 4) range { 1, 30 }
+ int m_DefaultIterationCount;
+
+ std::vector<SInt32> m_DisableTransformMessage;
+ typedef List<RigidbodyInterpolationInfo> InterpolatedBodiesList;
+ typedef InterpolatedBodiesList::iterator InterpolatedBodiesIterator;
+ InterpolatedBodiesList m_InterpolatedBodies;
+ float m_SmoothedClothDeltaTime;
+
+ enum { kMaxSortedActorsDepth = 64 };
+ typedef List< ListNode<Rigidbody> > RigidbodyList;
+ RigidbodyList m_SortedActors[kMaxSortedActorsDepth];
+#if ENABLE_MULTITHREADED_SKINNING
+ JobScheduler::JobGroupID m_ClothJobGroup;
+#endif
+ dynamic_array<SkinnedMeshRenderer*> m_ActiveSkinnedMeshes;
+
+ std::vector<UInt32> m_LayerCollisionMatrix;
+ friend class PhysicMaterial;
+
+#if ENABLE_CLUSTER_SYNC
+#ifdef DEBUG
+ friend class ClusterTransfer;
+#endif // DEBUG
+#endif // ENABLE_CLUSTER_SYNC
+};
+
+enum { kContactNothingGroup = 0, kContactEnterExitGroup = 1, kContactTouchGroup = 2 };
+
+PhysicsManager &GetPhysicsManager ();
+PhysicsManager *GetPhysicsManagerPtr ();
+class NxScene& GetDynamicsScene ();
+class NxScene& GetInactiveDynamicsScene ();
+class NxPhysicsSDK& GetDynamicsSDK ();
+void NxToRaycastHit (const NxRaycastHit& hit, RaycastHit& outHit);
+void NxToRaycastHit (const NxSweepQueryHit& hit, float sweepDistance, RaycastHit& outHit);
+
+#if ENABLE_SCRIPTING
+ScriptingArrayPtr ConvertNativeRaycastHitsToManaged(const PhysicsManager::RaycastHits& hits);
+#endif
+
+#endif
diff --git a/Runtime/Dynamics/PhysicsModule.cpp b/Runtime/Dynamics/PhysicsModule.cpp
new file mode 100644
index 0000000..09bd13c
--- /dev/null
+++ b/Runtime/Dynamics/PhysicsModule.cpp
@@ -0,0 +1,141 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_PHYSICS
+#include "Runtime/Interfaces/IPhysics.h"
+#include "PhysicsManager.h"
+#include "RigidBody.h"
+#include "Runtime/Dynamics/PhysicsManager.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "External/PhysX/builds/SDKs/Cooking/include/NxCooking.h"
+#include "Runtime/Dynamics/SkinnedCloth.h"
+#include "Runtime/Dynamics/CapsuleCollider.h"
+#include "Runtime/Dynamics/Collider.h"
+#include "Runtime/Dynamics/MeshCollider.h"
+#include "Runtime/Math/Random/rand.h"
+#include "Runtime/Dynamics/NxMeshCreation.h"
+#include "Runtime/Dynamics/nxmemorystream.h"
+
+//only implementation.
+class PhysicsModule : public IPhysics
+{
+public:
+ virtual void SetRigidBodyState( Rigidbody& rigidbody, const RigidBodyState& state )
+ {
+ rigidbody.SetPosition(state.position);
+ rigidbody.SetRotation(state.rotation);
+ rigidbody.SetVelocity(state.velocity);
+ rigidbody.SetAngularVelocity(state.avelocity);
+ }
+
+ virtual void GetRigidBodyState( const Rigidbody& rigidbody, RigidBodyState* result)
+ {
+ result->position = rigidbody.GetPosition();
+ result->rotation = rigidbody.GetRotation();
+ result->velocity = rigidbody.GetVelocity();
+ result->avelocity = rigidbody.GetAngularVelocity();
+ }
+
+ virtual Vector3f GetRigidBodyVelocity( const Rigidbody& rigidbody)
+ {
+ return rigidbody.GetVelocity();
+ }
+
+#if ENABLE_PROFILER
+ virtual void GetProfilerStats(PhysicsStats& stats)
+ {
+ GetPhysicsManager().GetPerformanceStats(stats);
+ }
+#endif
+
+ virtual Vector3f GetGravity()
+ {
+ return GetPhysicsManager().GetGravity();
+ }
+
+ virtual void* CreateNxMeshFromNxStream(bool convex, const NxStream& stream)
+ {
+ return convex ? (void*)GetDynamicsSDK ().createConvexMesh(stream) : (void*)GetDynamicsSDK ().createTriangleMesh(stream);
+ }
+
+ virtual void ReleaseNxTriangleMesh(NxTriangleMesh& mesh)
+ {
+ GetDynamicsSDK ().releaseTriangleMesh (mesh);
+ }
+
+ virtual void ReleaseNxConvexMesh(NxConvexMesh& mesh)
+ {
+ GetDynamicsSDK ().releaseConvexMesh (mesh);
+ }
+
+#if ENABLE_CLOTH
+ virtual void SetUpSkinnedBuffersOnSkinnedCloth (SkinnedCloth& cloth, void *vertices, void *normals, void *tangents, size_t bufferStride)
+ {
+ cloth.SetUpSkinnedBuffers(vertices,normals,tangents,bufferStride);
+ }
+#endif
+
+ virtual void CapsuleColliderSetHeight(CapsuleCollider& collider, float height)
+ {
+ collider.SetHeight(height);
+ }
+
+ virtual ScriptingObjectPtr ConvertContactToMono (Collision* input)
+ {
+ return ::ConvertContactToMono(input);
+ }
+
+ virtual int GetColliderMaterialInstanceID(Collider& collider)
+ {
+ return collider.GetMaterial().GetInstanceID();
+ }
+
+ virtual int GetColliderSharedMeshInstanceID(MeshCollider& collider)
+ {
+ return collider.GetSharedMesh().GetInstanceID();
+ }
+
+ virtual bool CreateNxStreamFromUnityMesh (Mesh* mesh, bool convex, const Matrix4x4f& scalematrix, TransformType transformType, MemoryStream& stream )
+ {
+ return ::CreateNxStreamFromUnityMesh (mesh, convex, scalematrix, transformType, stream );
+ }
+
+ virtual void* CreateNxMeshFromUnityMesh (Mesh* mesh, bool convex, const Matrix4x4f& scalematrix, TransformType transformType )
+ {
+ return ::CreateNxMeshFromUnityMesh (mesh, convex, scalematrix, transformType );
+ }
+
+ virtual MemoryStream* CreateNxStreamFromUnityMesh(Mesh& meshData, bool convex)
+ {
+ return ::CreateNxStreamFromUnityMesh(meshData,convex);
+ }
+
+ virtual void DeleteMemoryStream(MemoryStream* stream)
+ {
+ delete stream;
+ }
+
+ virtual void ReleaseHeightField(NxHeightField& heightField)
+ {
+ GetDynamicsSDK().releaseHeightField(heightField);
+ }
+
+ virtual NxHeightField* CreateNxHeightField(NxHeightFieldDesc& desc)
+ {
+ return GetDynamicsSDK().createHeightField(desc);
+ }
+
+};
+
+void InitializePhysicsModule ()
+{
+ SetIPhysics(UNITY_NEW_AS_ROOT(PhysicsModule, kMemPhysics, "PhysicsInterface", ""));
+}
+
+void CleanupPhysicsModule ()
+{
+ PhysicsModule* module = reinterpret_cast<PhysicsModule*> (GetIPhysics ());
+ UNITY_DELETE(module, kMemPhysics);
+ SetIPhysics (NULL);
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/Dynamics/PhysicsModule.h b/Runtime/Dynamics/PhysicsModule.h
new file mode 100644
index 0000000..f700b51
--- /dev/null
+++ b/Runtime/Dynamics/PhysicsModule.h
@@ -0,0 +1,3 @@
+#pragma once
+void InitializePhysicsModule ();
+void CleanupPhysicsModule (); \ No newline at end of file
diff --git a/Runtime/Dynamics/PhysicsModule.jam b/Runtime/Dynamics/PhysicsModule.jam
new file mode 100644
index 0000000..15b2147
--- /dev/null
+++ b/Runtime/Dynamics/PhysicsModule.jam
@@ -0,0 +1,108 @@
+rule PhysicsModule_ReportCpp
+{
+ local files =
+ PhysicsModule.jam
+ BoxCollider.cpp
+ BoxCollider.h
+ CapsuleCollider.cpp
+ CapsuleCollider.h
+ CharacterController.cpp
+ CharacterController.h
+ CharacterJoint.cpp
+ CharacterJoint.h
+ Cloth.cpp
+ Cloth.h
+ Collider.cpp
+ Collider.h
+ ConfigurableJoint.cpp
+ ConfigurableJoint.h
+ ConstantForce.cpp
+ ConstantForce.h
+ DeformableMesh.cpp
+ DeformableMesh.h
+ FixedJoint.cpp
+ FixedJoint.h
+ HingeJoint.cpp
+ HingeJoint.h
+ Joint.cpp
+ Joint.h
+ JointDescriptions.h
+ Joints.h
+ MeshCollider.cpp
+ MeshCollider.h
+ PhysicMaterial.cpp
+ PhysicMaterial.h
+ PhysicsManager.cpp
+ PhysicsManager.h
+ PrimitiveCollider.h
+ RaycastCollider.cpp
+ RaycastCollider.h
+ RaycastHit.cpp
+ RaycastHit.h
+ Rigidbody.cpp
+ Rigidbody.h
+ SkinnedCloth.cpp
+ SkinnedCloth.h
+ SphereCollider.cpp
+ SphereCollider.h
+ SpringJoint.cpp
+ SpringJoint.h
+ WheelCollider.cpp
+ WheelCollider.h
+ PhysXRaycast.cpp
+ PhysXRaycast.h
+ TerrainCollider.cpp
+ TerrainCollider.h
+ ClothRenderer.cpp
+ ClothRenderer.h
+ PhysicsModuleRegistration.cpp
+ PhysicsModule.cpp
+ NxMeshCreation.cpp
+ NxMeshCreation.h
+ ExtractDataFromMesh.cpp
+ ExtractDataFromMesh.h
+ nxmemorystream.cpp
+ nxmemorystream.h
+ ;
+
+ return Runtime/Dynamics/$(files) ;
+}
+
+rule PhysicsModule_ReportTxt
+{
+ return Runtime/Dynamics/ScriptBindings/NewDynamics.txt ;
+}
+
+rule PhysicsModule_ReportIncludes
+{
+ return
+ Runtime/Dynamics
+ External/PhysX/builds/SDKs/Foundation/include
+ External/PhysX/builds/SDKs/Physics/include
+ External/PhysX/builds/SDKs/PhysXLoader/include
+ ;
+}
+
+rule PhysicsModule_ReportLibraries
+{
+ local libs = ;
+ if $(target) in MacEditor MacStandalonePlayer
+ {
+ libs +=
+ $(TOP)/External/PhysX/builds/SDKs/lib/osxstatic/novodex_cooking.a
+ $(TOP)/External/PhysX/builds/SDKs/lib/osxstatic/novodex_release.a
+ ;
+ }
+ return $(libs) ;
+}
+
+rule PhysicsModule_Init
+{
+ OverrideModule Physics : GetModule_Cpp : byOverridingWithMethod : PhysicsModule_ReportCpp ;
+ OverrideModule Physics : GetModule_Txt : byOverridingWithMethod : PhysicsModule_ReportTxt ;
+ OverrideModule Physics : GetModule_Inc : byOverridingWithMethod : PhysicsModule_ReportIncludes ;
+ OverrideModule Physics : GetModule_Lib : byChainingWithMethod : PhysicsModule_ReportLibraries ;
+}
+
+
+#RegisterModule Physics ; \ No newline at end of file
diff --git a/Runtime/Dynamics/PhysicsModuleRegistration.cpp b/Runtime/Dynamics/PhysicsModuleRegistration.cpp
new file mode 100644
index 0000000..8e5b0d3
--- /dev/null
+++ b/Runtime/Dynamics/PhysicsModuleRegistration.cpp
@@ -0,0 +1,67 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "Runtime/BaseClasses/ClassRegistration.h"
+#include "Runtime/Modules/ModuleRegistration.h"
+
+static void RegisterPhysicsClasses (ClassRegistrationContext& context)
+{
+ REGISTER_CLASS (Rigidbody)
+ REGISTER_CLASS (PhysicsManager)
+ REGISTER_CLASS (Collider)
+ REGISTER_CLASS (Joint)
+ REGISTER_CLASS (HingeJoint)
+ REGISTER_CLASS (MeshCollider)
+ REGISTER_CLASS (BoxCollider)
+ REGISTER_CLASS (ConstantForce)
+ REGISTER_CLASS (PhysicMaterial)
+ REGISTER_CLASS (SphereCollider)
+ REGISTER_CLASS (CapsuleCollider)
+ REGISTER_CLASS (FixedJoint)
+ REGISTER_CLASS (RaycastCollider)
+ REGISTER_CLASS (CharacterController)
+ REGISTER_CLASS (CharacterJoint)
+ REGISTER_CLASS (SpringJoint)
+ REGISTER_CLASS (WheelCollider)
+ REGISTER_CLASS (ConfigurableJoint)
+
+#if ENABLE_CLOTH
+ REGISTER_CLASS (Cloth)
+ REGISTER_CLASS (InteractiveCloth)
+ REGISTER_CLASS (ClothRenderer)
+ REGISTER_CLASS (SkinnedCloth)
+#endif
+
+#if ENABLE_TERRAIN
+ REGISTER_CLASS (TerrainCollider)
+#endif
+}
+
+#if ENABLE_MONO || UNITY_WINRT
+void ExportNewDynamics ();
+#if UNITY_EDITOR
+void ExportColliderUtil ();
+#endif
+
+static void RegisterPhysicsICallModule ()
+{
+ ///@TODO: Maybe this ifdef should be moved to the cspreprocess generated code instead???? (For all modules)
+
+#if !INTERNAL_CALL_STRIPPING
+ ExportNewDynamics ();
+#if UNITY_EDITOR
+ ExportColliderUtil ();
+#endif
+#endif
+}
+#endif
+
+extern "C" EXPORT_MODULE void RegisterModule_Physics ()
+{
+ ModuleRegistrationInfo info;
+ info.registerClassesCallback = &RegisterPhysicsClasses;
+#if ENABLE_MONO || UNITY_WINRT
+ info.registerIcallsCallback = &RegisterPhysicsICallModule;
+#endif
+ RegisterModuleInfo (info);
+}
+#endif \ No newline at end of file
diff --git a/Runtime/Dynamics/PhysicsTest.cpp b/Runtime/Dynamics/PhysicsTest.cpp
new file mode 100644
index 0000000..aa5575e
--- /dev/null
+++ b/Runtime/Dynamics/PhysicsTest.cpp
@@ -0,0 +1,93 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "Runtime/Dynamics/BoxCollider.h"
+#include "Runtime/Dynamics/RigidBody.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Graphics/Transform.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+
+
+void RunPhysicsTests ();
+
+void SetParentAttachesColliderToRigidbody ();
+void RemovingRididbodyComponentReattachesStaticCollider ();
+void RemovingRididbodyComponentReattachesColliderToNextRigidbody ();
+
+///@TODO: Tests for Joints losing connectiong with rigidbody etc. There seems to be no C++ code handling this properly???
+
+
+void RunPhysicsTests ()
+{
+ SetParentAttachesColliderToRigidbody ();
+ RemovingRididbodyComponentReattachesStaticCollider ();
+ RemovingRididbodyComponentReattachesColliderToNextRigidbody ();
+}
+
+
+// Changing parent of collider will reattach collider to rigidbody and deatch it.
+void SetParentAttachesColliderToRigidbody ()
+{
+ GameObject& root = CreateGameObject("Root", "Transform", "Rigidbody", NULL);
+ GameObject& colliderChild = CreateGameObject("Child", "Transform", "BoxCollider", NULL);
+ colliderChild.GetComponent(Transform).SetParent(root.QueryComponent(Transform));
+
+ Assert(colliderChild.GetComponent(BoxCollider).GetShape()->getActor().userData != root.QueryComponent(Rigidbody));
+ Assert(&colliderChild.GetComponent(BoxCollider).GetShape()->getActor() == root.GetComponent(Rigidbody).GetActor());
+
+ colliderChild.GetComponent(Transform).SetParent(NULL);
+
+ Assert(colliderChild.GetComponent(BoxCollider).GetShape()->getActor().userData == NULL);
+
+ colliderChild.GetComponent(Transform).SetParent(root.QueryComponent(Transform));
+
+ Assert(colliderChild.GetComponent(BoxCollider).GetShape()->getActor().userData != root.QueryComponent(Rigidbody));
+ Assert(&colliderChild.GetComponent(BoxCollider).GetShape()->getActor() == root.GetComponent(Rigidbody).GetActor());
+}
+
+/// @TODO: Test Modify hierarchy through prefab instantiate change. For example parenting change. Does it set up shapes correctly.
+/// Manually verify that we dont call Recreate too often, especially when destroying a whole hierarchy.
+
+void RemovingRididbodyComponentReattachesStaticCollider ()
+{
+ // Rigidbody
+ // - Collider
+ // 1) Remove Rigidbody component
+ // -> Rigidbody Cleanup is called once. No create is called.
+ // -> Collider becomes static collider
+
+ GameObject& root = CreateGameObject("Root", "Transform", "Rigidbody", NULL);
+ GameObject& colliderChild = CreateGameObject("Child", "Transform", "BoxCollider", NULL);
+ colliderChild.GetComponent(Transform).SetParent(root.QueryComponent(Transform));
+
+ Assert(colliderChild.GetComponent(BoxCollider).GetShape()->getActor().userData != root.QueryComponent(Rigidbody));
+ Assert(&colliderChild.GetComponent(BoxCollider).GetShape()->getActor() == root.GetComponent(Rigidbody).GetActor());
+ DestroyObjectHighLevel(root.QueryComponent(Rigidbody));
+ Assert(colliderChild.GetComponent(BoxCollider).GetShape()->getActor().userData == NULL);
+}
+
+void RemovingRididbodyComponentReattachesColliderToNextRigidbody ()
+{
+ // Rigidbody
+ // - Rigidbody
+ // - Collider
+ // 1) Remove Rigidbody component
+ // -> Rigidbody Cleanup is called once. No create is called.
+ // -> Collider
+
+ GameObject& root = CreateGameObject("Root", "Transform", "Rigidbody", NULL);
+ GameObject& rbChild = CreateGameObject("Root", "Transform", "Rigidbody", NULL);
+ GameObject& colliderChild = CreateGameObject("Child", "Transform", "BoxCollider", NULL);
+ rbChild.GetComponent(Transform).SetParent(root.QueryComponent(Transform));
+ colliderChild.GetComponent(Transform).SetParent(rbChild.QueryComponent(Transform));
+
+ Assert(colliderChild.GetComponent(BoxCollider).GetShape()->getActor().userData != NULL);
+ Assert(&colliderChild.GetComponent(BoxCollider).GetShape()->getActor() == rbChild.GetComponent(Rigidbody).GetActor());
+
+ DestroyObjectHighLevel(root.QueryComponent(Rigidbody));
+ Assert(colliderChild.GetComponent(BoxCollider).GetShape()->getActor().userData == root.GetComponent(Rigidbody).GetActor());
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/Dynamics/PrimitiveCollider.h b/Runtime/Dynamics/PrimitiveCollider.h
new file mode 100644
index 0000000..ec11c98
--- /dev/null
+++ b/Runtime/Dynamics/PrimitiveCollider.h
@@ -0,0 +1,7 @@
+#ifndef PRIMITIVECOLLIDER_H
+#define PRIMITIVECOLLIDER_H
+
+#include "SphereCollider.h"
+#include "BoxCollider.h"
+
+#endif
diff --git a/Runtime/Dynamics/RaycastCollider.cpp b/Runtime/Dynamics/RaycastCollider.cpp
new file mode 100644
index 0000000..79bfa90
--- /dev/null
+++ b/Runtime/Dynamics/RaycastCollider.cpp
@@ -0,0 +1,212 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "RaycastCollider.h"
+#include "Runtime/Graphics/Transform.h"
+#include "RigidBody.h"
+#include "PhysicsManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+
+#define GET_SHAPE() ((class NxCapsuleShape*)m_Shape)
+
+/*
+ - i am not sure about the getscaled extents calculation.
+*/
+
+const float RaycaseCollider_kMinSize = 0.00001F;
+
+// Novodex bugs with thin raycast triggers. It likes the fat hairy ones!
+// Remove this as soon as they have fixed this!!!
+const float kMinTriggerSize = 0.05F;
+
+
+using namespace std;
+
+RaycastCollider::RaycastCollider(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+RaycastCollider::~RaycastCollider ()
+{
+}
+
+void RaycastCollider::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ if (m_Shape)
+ {
+ // Apply changed values
+ SetLength (m_Length);
+ SetCenter (m_Center);
+ }
+
+ Super::AwakeFromLoad (awakeMode);
+}
+
+void RaycastCollider::SmartReset ()
+{
+ Super::Reset ();
+ AABB aabb;
+ if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb))
+ {
+ SetLength (aabb.GetExtent ().y);
+ SetCenter (aabb.GetCenter ());
+ }
+ else
+ {
+ SetLength (1.0F);
+ SetCenter (Vector3f::zero);
+ }
+}
+
+void RaycastCollider::Reset ()
+{
+ Super::Reset ();
+ m_Length = 1.0F;
+ m_Center = Vector3f::zero;
+}
+
+Vector3f RaycastCollider::GetGlobalCenter () const
+{
+ return GetComponent (Transform).TransformPoint (m_Center);
+}
+
+float RaycastCollider::GetGlobalLength () const
+{
+ Vector3f scale = GetComponent (Transform).GetWorldScaleLossy ();
+
+ float absoluteHeight = max (Abs (m_Length * scale.y), RaycaseCollider_kMinSize);
+ return absoluteHeight;
+}
+
+void RaycastCollider::Create (const Rigidbody* ignoreAttachRigidbody)
+{
+ if (m_Shape)
+ Cleanup ();
+
+ NxCapsuleShapeDesc shapeDesc;
+ shapeDesc.radius = GetIsTrigger() ? kMinTriggerSize : RaycaseCollider_kMinSize;
+ shapeDesc.height = GetGlobalLength ();
+ shapeDesc.flags |= NX_SWEPT_SHAPE;
+
+ FinalizeCreate (shapeDesc, true, ignoreAttachRigidbody);
+}
+
+void RaycastCollider::SetLength (float height)
+{
+ if (m_Length != height)
+ {
+ SetDirty ();
+ m_Length = height;
+ }
+
+ if (GET_SHAPE ())
+ GET_SHAPE ()->setHeight (GetGlobalLength ());
+}
+
+void RaycastCollider::SetCenter (const Vector3f& center)
+{
+ if (m_Center != center)
+ {
+ m_Center = center;
+ SetDirty ();
+ }
+
+ if (GET_SHAPE ())
+ TransformChanged (Transform::kRotationChanged | Transform::kPositionChanged);
+}
+
+void RaycastCollider::ScaleChanged ()
+{
+ PROFILE_MODIFY_STATIC_COLLIDER
+
+ NxCapsuleShape* shape = GET_SHAPE ();
+ shape->setHeight (GetGlobalLength ());
+}
+
+Matrix4x4f RaycastCollider::CalculateTransform () const
+{
+ Transform& transform = GetComponent (Transform);
+ Vector3f p = transform.TransformPoint (m_Center - Vector3f (0.0F, m_Length * .5F, 0.0F));
+
+ Quaternionf rotation = transform.GetRotation ();
+ rotation *= AxisAngleToQuaternion (Vector3f::xAxis, Deg2Rad (180));
+
+ Matrix4x4f matrix;
+ matrix.SetTR (p, rotation);
+
+ return matrix;
+}
+
+void RaycastCollider::FetchPoseFromTransform ()
+{
+ AssertIf (HasActorRigidbody ());
+
+ Transform& transform = GetComponent (Transform);
+ Vector3f p = transform.TransformPoint (m_Center - Vector3f (0.0F, m_Length * .5F, 0.0F));
+ AssertFiniteParameter(p)
+ m_Shape->getActor().setGlobalPosition ((const NxVec3&)p);
+
+ Quaternionf rotation = transform.GetRotation ();
+ rotation *= AxisAngleToQuaternion (Vector3f::xAxis, Deg2Rad (180));
+
+ AssertFiniteParameter(rotation)
+
+ m_Shape->getActor().setGlobalOrientationQuat ((const NxQuat&)rotation);
+}
+
+bool RaycastCollider::GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix)
+{
+ Matrix4x4f childMatrix = CalculateTransform ();
+ Matrix4x4f parentMatrix = anyParent.GetWorldToLocalMatrixNoScale ();
+ MultiplyMatrices4x4 (&parentMatrix, &childMatrix, &matrix);
+ ErrorFiniteParameterReturnFalse(matrix)
+ return true;
+}
+
+void RaycastCollider::TransformChanged (int changeMask)
+{
+ if (m_Shape)
+ {
+ if (!m_Shape->getActor ().userData)
+ {
+ PROFILER_AUTO(gStaticColliderMove, this)
+ FetchPoseFromTransform ();
+ }
+ else
+ {
+ Rigidbody* body = (Rigidbody*)m_Shape->getActor ().userData;
+ Matrix4x4f matrix;
+ if (GetRelativeToParentPositionAndRotation (GetComponent (Transform), body->GetComponent (Transform), matrix))
+ {
+ NxMat34 shapeMatrix;
+ shapeMatrix.setColumnMajor44 (matrix.GetPtr ());
+ m_Shape->setLocalPose (shapeMatrix);
+ }
+ }
+
+ if (changeMask & Transform::kScaleChanged)
+ ScaleChanged ();
+ }
+}
+
+void RaycastCollider::InitializeClass ()
+{
+ REGISTER_MESSAGE (RaycastCollider, kTransformChanged, TransformChanged, int);
+}
+
+template<class TransferFunction>
+void RaycastCollider::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.Align();
+ TRANSFER_SIMPLE (m_Center);
+ TRANSFER_SIMPLE (m_Length);
+}
+
+IMPLEMENT_CLASS_HAS_INIT (RaycastCollider)
+IMPLEMENT_OBJECT_SERIALIZE (RaycastCollider)
+
+#undef GET_SHAPE
+#endif //ENABLE_PHYSICS
diff --git a/Runtime/Dynamics/RaycastCollider.h b/Runtime/Dynamics/RaycastCollider.h
new file mode 100644
index 0000000..53e824c
--- /dev/null
+++ b/Runtime/Dynamics/RaycastCollider.h
@@ -0,0 +1,49 @@
+#ifndef RAYCASTCOLLIDER_H
+#define RAYCASTCOLLIDER_H
+
+#include "Collider.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Vector2.h"
+
+
+class RaycastCollider : public Collider
+{
+ public:
+ REGISTER_DERIVED_CLASS (RaycastCollider, Collider)
+ DECLARE_OBJECT_SERIALIZE (RaycastCollider)
+
+ RaycastCollider (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void Reset ();
+ virtual void SmartReset ();
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ float GetLength () const { return m_Length; }
+ void SetLength (float f);
+
+ Vector3f GetCenter () const { return m_Center; }
+ void SetCenter (const Vector3f& center);
+
+ float GetGlobalLength () const;
+ Vector3f GetGlobalCenter () const;
+
+ void TransformChanged (int changeMask);
+ static void InitializeClass ();
+ static void CleanupClass () {}
+ Matrix4x4f CalculateTransform () const;
+
+ protected:
+
+
+ ///@TODO ADD DIRECTION
+ virtual void FetchPoseFromTransform ();
+ virtual bool GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix);
+
+ virtual void Create (const Rigidbody* ignoreAttachRigidbody);
+ void ScaleChanged ();
+
+ Vector3f m_Center;
+ float m_Length;///< range { 0, infinity }
+};
+
+#endif
diff --git a/Runtime/Dynamics/RaycastHit.cpp b/Runtime/Dynamics/RaycastHit.cpp
new file mode 100644
index 0000000..b888d5e
--- /dev/null
+++ b/Runtime/Dynamics/RaycastHit.cpp
@@ -0,0 +1,48 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "RaycastHit.h"
+#include "MeshCollider.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Terrain/Heightmap.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Dynamics/TerrainCollider.h"
+
+Vector2f CalculateRaycastTexcoord (Collider* collider, const Vector2f& uv, const Vector3f& pos, UInt32 face, int texcoord)
+{
+ MeshCollider* meshCollider = dynamic_pptr_cast<MeshCollider*> (collider);
+ if (meshCollider != NULL)
+ {
+ Mesh* mesh = meshCollider->GetSharedMesh();
+ if (mesh == NULL)
+ return Vector2f::zero;
+ UInt32 indices[3];
+ if (!mesh->ExtractTriangle (face, indices))
+ return Vector2f::zero;
+ StrideIterator<Vector2f> uvs;
+ if (texcoord == 1 && mesh->IsAvailable (kShaderChannelTexCoord1))
+ uvs = mesh->GetUvBegin (1);
+ else if (mesh->IsAvailable (kShaderChannelTexCoord0))
+ uvs = mesh->GetUvBegin (0);
+ else
+ return Vector2f::zero;
+ Vector2f interpolated = uvs[indices[1]] * uv.x;
+ interpolated += uvs[indices[2]] * uv.y;
+ interpolated += uvs[indices[0]] * (1.0F - (uv.y + uv.x));
+ return interpolated;
+ }
+#if ENABLE_TERRAIN
+ TerrainCollider* terrainCollider = dynamic_pptr_cast<TerrainCollider*> (collider);
+ if (terrainCollider)
+ {
+ Vector2f uv;
+ Vector3f scale = terrainCollider->GetCachedInvSize();
+ Vector3f transformPos = terrainCollider->GetComponent(Transform).GetPosition();
+ uv.x = scale.x * (pos.x - transformPos.x);
+ uv.y = scale.z * (pos.z - transformPos.z);
+ return uv;
+ }
+ else
+#endif // ENABLE_TERRAIN
+ return Vector2f::zero;
+}
+#endif //ENABLE_PHYSICS
diff --git a/Runtime/Dynamics/RaycastHit.h b/Runtime/Dynamics/RaycastHit.h
new file mode 100644
index 0000000..2f0f9e8
--- /dev/null
+++ b/Runtime/Dynamics/RaycastHit.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include "Runtime/Math/Vector2.h"
+#include "Collider.h"
+
+Vector2f CalculateRaycastTexcoord (Collider* collider, const Vector2f& uv, const Vector3f& pos, UInt32 face, int texcoord);
diff --git a/Runtime/Dynamics/RigidBody.h b/Runtime/Dynamics/RigidBody.h
new file mode 100644
index 0000000..6c278b1
--- /dev/null
+++ b/Runtime/Dynamics/RigidBody.h
@@ -0,0 +1,266 @@
+#ifndef RIGIDBODY_H
+#define RIGIDBODY_H
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "PhysicsManager.h"
+
+class Transform;
+class Quaternionf;
+struct RootMotionData;
+
+namespace Unity { class Joint; }
+
+enum RigidbodyInterpolation { kNoInterpolation = 0, kInterpolate = 1, kExtrapolate = 2 };
+
+
+class Rigidbody : public Unity::Component {
+ public:
+ REGISTER_DERIVED_CLASS (Rigidbody, Component)
+ DECLARE_OBJECT_SERIALIZE (Rigidbody)
+
+ Rigidbody (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~Rigidbody(); declared-by-macro
+ virtual void Reset ();
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ virtual void Deactivate (DeactivateOperation operation);
+
+ static void InitializeClass ();
+ static void CleanupClass () {}
+
+ // Is the rigid body affected by gravity?
+ void SetUseGravity (bool gravity);
+ bool GetUseGravity () const;
+
+ // Mass of the rigid body
+ void SetMass (float mass);
+ float GetMass () const;
+
+ // Center of Mass of the rigid body in local space
+ void SetCenterOfMass (const Vector3f& centerOfMass);
+ Vector3f GetCenterOfMass () const;
+
+ // Center of Mass of the rigid body in world space
+ Vector3f GetWorldCenterOfMass () const;
+
+ // The rotation of the inertia tensor
+ void SetInertiaTensorRotation (const Quaternionf& inertia);
+ Quaternionf GetInertiaTensorRotation () const;
+
+ // The diagonal inertia tensor of mass relative to the center of mass and inertia rotation
+ void SetInertiaTensor (const Vector3f& inertia);
+ Vector3f GetInertiaTensor () const;
+
+ // Does the rigid body modify the rotation?
+ void SetFreezeRotation (bool freezeRotation);
+ bool GetFreezeRotation () const;
+
+ void SetConstraints (int flags);
+ int GetConstraints () const;
+
+ void SetIsKinematic (bool isKinematic);
+ bool GetIsKinematic () const;
+
+ void SetCollisionDetectionMode (int ccd);
+ int GetCollisionDetectionMode () const { return m_CollisionDetection; }
+
+ void SetDensity (float density);
+
+ // Get Position and rotation - This can be different from transform state when using interpolation
+ Vector3f GetPosition () const;
+ Quaternionf GetRotation () const;
+
+ // Set pos&rot this will cause transform.position/rotation to be updated delayed after the next fixed step
+ void SetPosition (const Vector3f& p);
+ void SetRotation (const Quaternionf& q);
+
+ /// Move to a position
+ /// This happens one frame delayed.
+ void MovePosition (const Vector3f& pos);
+ void MoveRotation (const Quaternionf& rot);
+
+ // Velocity
+ Vector3f GetVelocity () const;
+ void SetVelocity (const Vector3f& velocity);
+
+ // Angular velocity
+ Vector3f GetAngularVelocity () const;
+ void SetAngularVelocity (const Vector3f& velocity);
+
+ /// The linear drag coefficient. 0 means no drag. range { 0, infinity }
+ float GetDrag () const;
+ void SetDrag (float damping);
+
+ // The angular drag coefficient. 0 means no drag. range { 0, infinity }
+ void SetAngularDrag (float damping);
+ float GetAngularDrag () const;
+
+ /// Lets you set the maximum angular velocity permitted for this rigid body. Because for various computations, the rotation
+ /// of an object is linearized, quickly rotating actors introduce error into the simulation, which leads to bad things.
+ ///
+ /// However, because some rigid bodies, such as car wheels, should be able to rotate quickly, you can override the default setting
+ /// on a per-rigid body basis with the below call. Note that objects such as wheels which are approximated with spherical or
+ /// other smooth collision primitives can be simulated with stability at a much higher angular velocity than, say, a box that
+ /// has corners.
+ void SetMaxAngularVelocity (float maxAngularVelocity);
+ float GetMaxAngularVelocity () const;
+
+ // The velocity of a point given in world coordinates
+ // if it were attached to the actor and moving with it.
+ Vector3f GetPointVelocity (const Vector3f& worldPoint) const;
+
+ // The velocity of a point given in world coordinates
+ // if it were attached to the actor and moving with it.
+ Vector3f GetRelativePointVelocity (const Vector3f& localPoint) const;
+
+ enum ForceMode
+ {
+ FORCE, ///< parameter has unit of mass * distance/ time^2, i.e. a force
+ IMPULSE, ///< parameter has unit of mass * distance /time
+ VELOCITY_CHANGE, ///< parameter has unit of distance / time, i.e. the effect is mass independent: a velocity change.
+ SMOOTH_IMPULSE, ///< same as NX_IMPULSE but the effect is applied over all substeps. Use this for motion controllers that repeatedly apply an impulse.
+ SMOOTH_VELOCITY_CHANGE, ///< same as NX_VELOCITY_CHANGE but the effect is applied over all substeps. Use this for motion controllers that repeatedly apply an impulse.
+ ACCELERATION ///< parameter has unit of distance/ time^2, i.e. an acceleration. It gets treated just like a force except the mass is not divided out before integration.
+ };
+
+ enum { kCCDModeOff = 0, kCCDModeNormal = 1, kCCDModeDynamic = 2 };
+
+ enum {
+ kFreezeNone = 0,
+ kFreezePositionX = (1<<1),
+ kFreezePositionY = (1<<2),
+ kFreezePositionZ = (1<<3),
+ kFreezeRotationX = (1<<4),
+ kFreezeRotationY = (1<<5),
+ kFreezeRotationZ = (1<<6),
+ kFreezePosition = kFreezePositionX | kFreezePositionY | kFreezePositionZ,
+ kFreezeRotation = kFreezeRotationX | kFreezeRotationY | kFreezeRotationZ,
+ kFreezeAll = kFreezePosition | kFreezeRotation,
+ };
+
+ // Applies a force (or impulse) defined in the global coordinate frame,
+ // acting at a particular point in global coordinates on the rigid body.
+ // Note that if the force does not act along the center of mass of the actor, this
+ // will also add the corresponding torque.
+ // Forces should be applied inside FixedUpdate only, because forces are reset at the end of every fixed timestep.
+ void AddForceAtPosition (const Vector3f& force, const Vector3f& position, int mode = FORCE);
+
+ // Applies a force (or impulse) defined in the global coordinate frame,
+ // This will not induce torque.
+ // Forces should be applied inside FixedUpdate only, because forces are reset at the end of every fixed timestep.
+ void AddForce (const Vector3f& force, int mode = FORCE);
+
+ // Applies a force (or impulse) defined in the local coordinate frame,
+ // This will not induce torque.
+ // Forces should be applied inside FixedUpdate only, because forces are reset at the end of every fixed timestep.
+ void AddRelativeForce (const Vector3f& force, int mode = FORCE);
+
+ // Applies an (eventually impulsive) torque defined in the global coordinate frame to the actor.
+ // Torques should be applied inside FixedUpdate only, because forces are reset at the end of every fixed timestep.
+ void AddTorque (const Vector3f& torque, int mode = FORCE);
+
+ // Applies an (eventually impulsive) torque defined in the local coordinate frame to the actor.
+ // Torques should be applied inside FixedUpdate only, because forces are reset at the end of every fixed timestep.
+ void AddRelativeTorque (const Vector3f& torque, int mode = FORCE);
+
+
+ void AddExplosionForce (float force, const Vector3f& position, float radius, float upwardsModifier, int forceMode = FORCE);
+ void ClosestPointOnBounds (const Vector3f& position, Vector3f& outPosition, float& outSqrDistance);
+
+ bool GetDetectCollisions() const;
+ void SetDetectCollisions(bool enable);
+
+ RigidbodyInterpolation GetInterpolation () { return (RigidbodyInterpolation)m_Interpolate; }
+ void SetInterpolation (RigidbodyInterpolation interpolation);
+
+ bool GetUseConeFriction () const;
+ void SetUseConeFriction (bool cone);
+
+ bool IsSleeping ();
+ void Sleep ();
+ void WakeUp ();
+
+ void SetSolverIterationCount (int iterationCount);
+ int GetSolverIterationCount () const;
+
+ void SetSleepVelocity (float value);
+ float GetSleepVelocity () const;
+
+ void SetSleepAngularVelocity (float value);
+ float GetSleepAngularVelocity () const;
+
+ void TransformChanged (int mask);
+ void ApplyRootMotionBuiltin (RootMotionData* rootMotion);
+
+ bool SweepTest (const Vector3f &direction, float distance, RaycastHit& outHit);
+ const PhysicsManager::RaycastHits& SweepTestAll (const Vector3f &direction, float distance);
+
+ virtual void CheckConsistency ();
+
+ // Testing API
+ NxActor* GetActor() { return m_Actor; }
+
+ private:
+
+ void SortParentedRigidbodies ();
+ void FetchPoseFromTransform ();
+ virtual void SupportedMessagesDidChange (int supported);
+ void UpdateSortedBody ();
+
+ void Create (bool active);
+ void CleanupInternal (bool recreateColliders);
+ void UpdateInterpolationNode ();
+
+ void UpdateMassDistribution ();
+ ListNode<Rigidbody> m_SortedNode;
+ class NxActor* m_Actor;
+
+ float m_Mass;///< The mass of the body. range { 0.0000001, 1000000000 }
+ float m_Drag;///< The linear drag coefficient. 0 means no damping. range { 0, infinity }
+ float m_AngularDrag; ///< The angular drag coefficient. 0 means no damping. range { 0, infinity }
+
+ UInt8 m_ActiveScene;
+ UInt8 m_ImplicitTensor;
+
+ bool m_UseGravity;
+ bool m_IsKinematic;
+ int m_Constraints;
+ int m_CollisionDetection;///< enum { Discrete = 0, Continuous = 1, Continuous Dynamic = 2 }
+ int m_CachedCollisionDetection;
+
+ public:
+
+ /// This is used to prevent read back from kinematic rigidbodies.
+ /// * setGlobalPosition sets the position immediately. No triggers will be activated.
+ /// No velocity applied -> thus no friction for rigidbodies sitting on animated objects.
+ /// * moveGlobalPosition sets the position delayed during simulate. Simply calling moveGlobalPosition for all kinematic objects
+ /// doesn't work because then the physics update loop will update delayed, causing small epsilon precision errors to accumulate
+ /// from the local to world, then world to local transformation. If the transform which has the kinematic rigidbody attached
+ /// is not actually animated, the ragdoll will slowly drift apart.
+ /// On top of that, it is slow since we read back the position twice for animated ragdolls and characters.
+ /// ->> The solution is to store if we should read back the update.
+ /// If we are setting the position directly from a Transform update, there is no reason to update the transform
+ /// Also the world position wont change during simulation since it is kinematic rigidbody.
+
+
+ UInt8 m_Interpolate;///< enum { None = 0, Interpolate = 1, Extrapolate = 2 }
+ int m_DisableReadUpdateTransform;
+ int m_DisableInterpolation;
+ RigidbodyInterpolationInfo* m_InterpolationInfo;
+
+ friend class Collider;
+ friend class Unity::Joint;
+ friend class PhysicsManager;
+};
+
+/// Notifications:
+/// bool RigidbodyChanged (const Rigidbody& b);
+extern MessageIdentifier kRigidbodyChanged;
+
+
+
+#endif
diff --git a/Runtime/Dynamics/Rigidbody.cpp b/Runtime/Dynamics/Rigidbody.cpp
new file mode 100644
index 0000000..6ca8af6
--- /dev/null
+++ b/Runtime/Dynamics/Rigidbody.cpp
@@ -0,0 +1,1097 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "RigidBody.h"
+#include "Collider.h"
+#include "Joint.h"
+#include "PhysicsManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/BaseClasses/SupportedMessageOptimization.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "NxWrapperUtility.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/BaseClasses/MessageHandler.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+#include "Runtime/GameCode/RootMotionData.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+using namespace Unity;
+using namespace std;
+
+PROFILER_INFORMATION(gSweepTestProfile, "Rigidbody.SweepTest", kProfilerPhysics)
+PROFILER_INFORMATION(gSweepTestAllProfile, "Rigidbody.SweepTestAll", kProfilerPhysics)
+
+/*
+ - Rigid bodies can't change the inertia tensor
+ - Transfer function should be optimize to serialize into desc data if rigid body doesn't exit yet (Possibly always)
+
+ - Make it so you can have multiple rigidbodies inside of each other and they update in the right order!
+ What about sleeping??????
+
+*/
+
+
+inline Quaternionf QuatFromNx(const NxQuat& q)
+{
+ return Quaternionf(q.x, q.y, q.z, q.w);
+}
+
+
+
+Rigidbody::Rigidbody (MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode),
+ m_SortedNode(this)
+{
+ m_Actor = NULL;
+ m_ImplicitTensor = true;
+ m_InterpolationInfo = NULL;
+ m_DisableReadUpdateTransform = 0;
+ m_CollisionDetection = kCCDModeOff;
+ m_CachedCollisionDetection = kCCDModeOff;
+}
+
+Rigidbody::~Rigidbody ()
+{
+ CleanupInternal (false);
+}
+
+void Rigidbody::Reset ()
+{
+ Super::Reset ();
+
+ if (m_Actor)
+ {
+ SetMass(1.0F);
+ SetAngularDrag(0.05f);
+ SetDrag(0.0F);
+ SetConstraints(kFreezeNone);
+ SetIsKinematic(false);
+ SetUseGravity(true);
+ SetCollisionDetectionMode(kCCDModeOff);
+ }
+ else
+ {
+ m_Mass = 1.0F;
+ m_AngularDrag = 0.05f;
+ m_Drag = 0.0f;
+ m_Constraints = kFreezeNone;
+ m_IsKinematic = false;
+ m_UseGravity = true;
+ m_CollisionDetection = kCCDModeOff;
+ m_CachedCollisionDetection = kCCDModeOff;
+ }
+ m_Interpolate = kNoInterpolation;
+}
+
+void Rigidbody::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+ m_Mass = max(0.0000001F, m_Mass);
+}
+
+// Because Component -> Deactivate can be called but IsActive might still return true, we need to pass the active state!
+void Rigidbody::Create (bool isActive)
+{
+ if (m_Actor == NULL || (bool)m_ActiveScene != isActive)
+ {
+ NxBodyDesc bodyDesc;
+ bodyDesc.solverIterationCount = GetPhysicsManager ().GetSolverIterationCount ();
+ NxActorDesc actorDesc;
+
+ if (m_Actor)
+ {
+ m_Actor->saveBodyToDesc (bodyDesc);
+ CleanupInternal (true);
+ }
+ else
+ {
+ bodyDesc.massSpaceInertia = NxVec3 (1.0F, 1.0F, 1.0F);
+ bodyDesc.mass = m_Mass;
+ bodyDesc.linearDamping = m_Drag;
+ bodyDesc.angularDamping = m_AngularDrag;
+ bodyDesc.mass = m_Mass;
+ if (!m_UseGravity)
+ bodyDesc.flags |= NX_BF_DISABLE_GRAVITY;
+ if (m_IsKinematic)
+ bodyDesc.flags |= NX_BF_KINEMATIC;
+ bodyDesc.flags |= m_Constraints;
+ }
+ actorDesc.body = &bodyDesc;
+ actorDesc.userData = this;
+
+ if (isActive)
+ {
+ //;;printf_console ("Creating active physics actor\n");
+ m_Actor = GetDynamicsScene ().createActor (actorDesc);
+ SupportedMessagesDidChange (GetGameObject ().GetSupportedMessages ());
+ m_ActiveScene = true;
+ }
+ else
+ {
+ //;;printf_console ("Creating inactive physics actor\n");
+ m_Actor = GetInactiveDynamicsScene ().createActor (actorDesc);
+
+ m_ActiveScene = false;
+ }
+
+ UpdateInterpolationNode();
+ }
+
+ if (!isActive && m_Actor != NULL)
+ {
+ Assert(m_Actor->getNbShapes() == 0);
+ }
+}
+
+
+void Rigidbody::SupportedMessagesDidChange (int supported)
+{
+ if (m_Actor)
+ {
+ if (supported & kHasCollisionStay)
+ m_Actor->setGroup (kContactTouchGroup);
+ else if (supported & (kHasCollisionStay | kHasCollisionEnterExit))
+ m_Actor->setGroup (kContactEnterExitGroup);
+ else
+ m_Actor->setGroup (kContactNothingGroup);
+ }
+}
+
+void Rigidbody::CleanupInternal (bool recreateColliders)
+{
+ if (m_Actor)
+ {
+ int shapeCount = m_Actor->getNbShapes ();
+ NxShape*const * shapes = m_Actor->getShapes ();
+
+ Collider** colliders;
+ ALLOC_TEMP(colliders, Collider*, shapeCount)
+
+ for (int i=0;i<shapeCount;i++)
+ {
+ Collider* collider = (Collider*)shapes[i]->userData;
+ AssertIf (collider == NULL);
+ colliders[i] = collider;
+ collider->Cleanup ();
+ }
+
+ if (m_ActiveScene)
+ {
+ //;;printf_console ("Deleting active physics actor");
+ GetDynamicsScene ().releaseActor (*m_Actor);
+ }
+ else
+ {
+ //;;printf_console ("Deleting inactive physics actor");
+ GetInactiveDynamicsScene ().releaseActor (*m_Actor);
+ }
+ m_Actor = NULL;
+
+ if (recreateColliders)
+ {
+ for (int i=0;i<shapeCount;i++)
+ {
+ colliders[i]->RecreateCollider (this);
+ }
+ }
+
+ delete m_InterpolationInfo;
+ m_InterpolationInfo = NULL;
+ m_CachedCollisionDetection = m_CollisionDetection;
+ }
+ Assert(m_Actor == NULL);
+ m_SortedNode.RemoveFromList();
+}
+
+Vector3f Rigidbody::GetWorldCenterOfMass () const
+{
+ return Vec3FromNx (m_Actor->getCMassGlobalPosition());
+}
+
+void Rigidbody::Deactivate (DeactivateOperation operation)
+{
+ // When we are about to destroy a rigidbody, we don't destroy it immediately
+ // Instead we destroy it in the rigidbody destructor.
+ // This way scripts have no chance of accessing the colliders.
+ if (operation == kWillDestroyGameObjectDeactivate)
+ ;
+ else
+ {
+ Create (false);
+ }
+ m_SortedNode.RemoveFromList();
+
+ Super::Deactivate (operation);
+}
+
+void Rigidbody::UpdateSortedBody ()
+{
+ m_SortedNode.RemoveFromList();
+ if (m_ActiveScene)
+ GetPhysicsManager().AddBody(GetTransformDepth(GetComponent(Transform)), m_SortedNode);
+}
+
+void Rigidbody::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ AssertIf (GameObject::GetMessageHandler ().HasMessageCallback (ClassID (Rigidbody), kTransformChanged.messageID) == false);
+
+ Create (IsActive ());
+
+ // When modifying already loaded rigidbody
+ // Apply properties through immediate mode function
+ if (!(awakeMode & kDidLoadFromDisk))
+ {
+ SetIsKinematic(m_IsKinematic);
+ SetMass(m_Mass);
+ SetDrag(m_Drag);
+ SetAngularDrag(m_AngularDrag);
+ SetUseGravity(m_UseGravity);
+ SetCollisionDetectionMode(m_CollisionDetection);
+ SetConstraints(m_Constraints);
+ }
+
+ UpdateInterpolationNode();
+
+ if (IsActive())
+ FetchPoseFromTransform();
+
+ if (!GetIsKinematic())
+ m_DisableReadUpdateTransform = 0;
+
+ if (awakeMode & kActivateAwakeFromLoad)
+ {
+ MessageData data;
+ GetComponent(Transform).BroadcastMessageAny(kForceRecreateCollider, data);
+ }
+
+ UpdateSortedBody ();
+}
+
+void Rigidbody::ClosestPointOnBounds (const Vector3f& position, Vector3f& outPosition, float& outSqrDistance)
+{
+ // No collider - just the distance to the center of mass
+ int count = m_Actor->getNbShapes();
+ if (count == 0)
+ {
+ outPosition = GetWorldCenterOfMass();
+ outSqrDistance = SqrMagnitude(position - outPosition);
+ return;
+ }
+
+ outSqrDistance = std::numeric_limits<float>::infinity();
+
+ NxShape*const * shapes = m_Actor->getShapes();
+ for (int i=0;i<count;i++)
+ {
+ NxBounds3 bounds;
+ shapes[i]->getWorldBounds(bounds);
+ AABB aabb;
+ bounds.getCenter((NxVec3&)aabb.GetCenter());
+ bounds.getExtents((NxVec3&)aabb.GetExtent());
+
+ Vector3f closest;
+ float sqrDistance;
+
+ CalculateClosestPoint(position, aabb, closest, sqrDistance);
+ if (sqrDistance < outSqrDistance)
+ {
+ outPosition = closest;
+ outSqrDistance = sqrDistance;
+ }
+ }
+}
+
+void Rigidbody::AddExplosionForce (float force, const Vector3f& position, float radius, float upwardsModifier, int forceMode)
+{
+ Vector3f pointOnSurface;
+ float sqrDistance;
+
+ Vector3f offsetPosition = position - Vector3f(0.0F, upwardsModifier, 0.0F);
+ if (upwardsModifier == 0.0F)
+ {
+ ClosestPointOnBounds(position, pointOnSurface, sqrDistance);
+ }
+ else
+ {
+ /// Upwards modifier will not modify the distance
+ /// But it will modify the point on the surface
+ ClosestPointOnBounds(position, pointOnSurface, sqrDistance);
+
+ float tmpDistance;
+ ClosestPointOnBounds(offsetPosition, pointOnSurface, tmpDistance);
+ }
+
+ // Linear distance fall off
+ float distanceScale;
+ if (radius > Vector3f::epsilon)
+ distanceScale = 1.0F - clamp01(sqrtf(sqrDistance) / radius);
+ else
+ distanceScale = 1.0F;
+
+ // Calculate normalized direction towards surface point
+ Vector3f direction = pointOnSurface - offsetPosition;
+
+ float length = Magnitude(direction);
+ if (length > Vector3f::epsilon)
+ direction /= length;
+ else
+ direction = Vector3f (0.0F, 1.0F, 0.0F);
+
+ AddForceAtPosition(force * distanceScale * direction, pointOnSurface, forceMode);
+}
+
+void Rigidbody::InitializeClass ()
+{
+ REGISTER_MESSAGE (Rigidbody, kTransformChanged, TransformChanged, int);
+ REGISTER_MESSAGE_PTR (Rigidbody, kAnimatorMoveBuiltin, ApplyRootMotionBuiltin, RootMotionData);
+}
+
+template<class TransferFunction>
+void Rigidbody::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion (2);
+ TRANSFER_SIMPLE (m_Mass);
+ TRANSFER_SIMPLE (m_Drag);
+ TRANSFER_SIMPLE (m_AngularDrag);
+ TRANSFER_SIMPLE (m_UseGravity);
+ TRANSFER (m_IsKinematic);
+
+ transfer.Transfer (m_Interpolate, "m_Interpolate");
+ if (transfer.IsOldVersion(1))
+ {
+ bool freezeRotation;
+ transfer.Transfer (freezeRotation, "m_FreezeRotation");
+ if (freezeRotation)
+ m_Constraints = kFreezeRotation;
+ else
+ m_Constraints = kFreezeNone;
+ }
+ else
+ {
+ transfer.Align();
+
+ // Hide in editor and show using custom inspector instead.
+ transfer.Transfer (m_Constraints, "m_Constraints", kHideInEditorMask | kGenerateBitwiseDifferences);
+ }
+
+ TRANSFER (m_CollisionDetection);
+}
+
+void Rigidbody::SetUseGravity (bool value)
+{
+ AssertIf (m_Actor == NULL);
+ SetDirty ();
+ if (value)
+ {
+ m_Actor->clearBodyFlag (NX_BF_DISABLE_GRAVITY);
+ m_Actor->wakeUp();
+ }
+ else
+ m_Actor->raiseBodyFlag (NX_BF_DISABLE_GRAVITY);
+ m_UseGravity = value;
+}
+
+bool Rigidbody::GetUseGravity () const
+{
+ AssertIf (m_Actor == NULL);
+ return !m_Actor->readBodyFlag (NX_BF_DISABLE_GRAVITY);
+}
+
+void Rigidbody::SetFreezeRotation (bool value)
+{
+ if (value)
+ SetConstraints (GetConstraints() | kFreezeRotation);
+ else
+ SetConstraints (GetConstraints() & ~kFreezeRotation);
+}
+
+bool Rigidbody::GetFreezeRotation () const
+{
+ return (GetConstraints () & kFreezeRotation) == kFreezeRotation;
+}
+
+void Rigidbody::SetConstraints (int value)
+{
+ AssertIf (m_Actor == NULL);
+
+ // If we are removing constraints, wake up the rigidbody, so it can start moving.
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_3_a1) && m_Constraints & ~value)
+ WakeUp();
+
+ SetDirty ();
+ value &= NX_BF_FROZEN;
+ m_Actor->clearBodyFlag (NX_BF_FROZEN);
+ m_Actor->raiseBodyFlag ((NxBodyFlag)value);
+ m_Constraints = value;
+}
+
+int Rigidbody::GetConstraints () const
+{
+ AssertIf (m_Actor == NULL);
+
+ int value = 0;
+ for (int i = kFreezePositionX; i <= kFreezeRotationZ; i<<=1)
+ {
+ if (m_Actor->readBodyFlag ((NxBodyFlag)i))
+ value |= i;
+ }
+ return value;
+}
+
+void Rigidbody::SetIsKinematic (bool value)
+{
+ AssertIf (m_Actor == NULL);
+
+ SetDirty ();
+ if (value)
+ m_Actor->raiseBodyFlag (NX_BF_KINEMATIC);
+ else
+ m_Actor->clearBodyFlag (NX_BF_KINEMATIC);
+ m_IsKinematic = value;
+ UpdateInterpolationNode ();
+
+ m_DisableReadUpdateTransform = 0;
+}
+
+bool Rigidbody::GetIsKinematic () const
+{
+ AssertIf (m_Actor == NULL);
+ return m_Actor->readBodyFlag (NX_BF_KINEMATIC);
+}
+
+void Rigidbody::UpdateMassDistribution ()
+{
+ AssertIf (m_Actor == NULL);
+
+ if (m_ImplicitTensor)
+ {
+ NxShape*const * shapes = m_Actor->getShapes();
+ int count = m_Actor->getNbShapes();
+ for (int i=0;i<count;i++)
+ {
+ Collider* coll = (Collider*)(shapes[i]->userData);
+
+ // Triggers don't have mass, and RaycastColliders are too thing to give good mathematical useful results.
+ if (!shapes[i]->getFlag(NX_TRIGGER_ENABLE) && !(coll && coll->GetClassID() == 140))
+ {
+ m_Actor->updateMassFromShapes (0.0F, m_Mass);
+ return;
+ }
+ }
+
+ // no usable shapes - reset actor CoG and inertia
+ m_Actor->setMass (m_Mass);
+ m_Actor->setCMassOffsetLocalPosition ((const NxVec3&)Vector3f::zero);
+ m_Actor->setMassSpaceInertiaTensor ((const NxVec3&)Vector3f::one);
+ }
+}
+
+void Rigidbody::SetMass (float mass)
+{
+ AssertIf (m_Actor == NULL);
+ SetDirty ();
+ m_Mass = mass;
+
+ if (m_ImplicitTensor)
+ UpdateMassDistribution();
+ else
+ m_Actor->setMass (mass);
+}
+
+float Rigidbody::GetMass () const
+{
+ AssertIf (m_Actor == NULL);
+ return m_Actor->getMass ();
+}
+
+void Rigidbody::SetCenterOfMass (const Vector3f& centerOfMass)
+{
+ m_Actor->setCMassOffsetLocalPosition ((const NxVec3&)centerOfMass);
+ m_ImplicitTensor = false;
+}
+
+Vector3f Rigidbody::GetCenterOfMass () const
+{
+ return Vec3FromNx(m_Actor->getCMassLocalPosition ());
+}
+
+void Rigidbody::SetInertiaTensorRotation (const Quaternionf& inertia)
+{
+ m_ImplicitTensor = false;
+ NxMat33 matrix ((const NxQuat&)inertia);
+ m_Actor->setCMassOffsetLocalOrientation (matrix);
+}
+
+Quaternionf Rigidbody::GetInertiaTensorRotation () const
+{
+ NxMat33 matrix = m_Actor->getCMassLocalOrientation ();
+ NxQuat quat (matrix);
+ return (const Quaternionf&)quat;
+}
+
+void Rigidbody::SetInertiaTensor (const Vector3f& inertia)
+{
+ m_ImplicitTensor = false;
+ if (inertia.x > std::numeric_limits<float>::epsilon() &&
+ inertia.y > std::numeric_limits<float>::epsilon() &&
+ inertia.z > std::numeric_limits<float>::epsilon())
+ m_Actor->setMassSpaceInertiaTensor ((const NxVec3&)inertia);
+ else
+ ErrorStringObject ("Inertia tensor must be larger then zero in all coordinates.", this);
+}
+
+Vector3f Rigidbody::GetInertiaTensor () const
+{
+ return Vec3FromNx(m_Actor->getMassSpaceInertiaTensor ());
+}
+
+Vector3f Rigidbody::GetVelocity () const
+{
+ AssertIf (m_Actor == NULL);
+ return Vec3FromNx(m_Actor->getLinearVelocity ());
+}
+
+void Rigidbody::SetVelocity (const Vector3f& velocity)
+{
+ AssertIf (m_Actor == NULL);
+ ABORT_INVALID_VECTOR3 (velocity, velocity, rigidbody);
+ m_Actor->setLinearVelocity ((const NxVec3&)velocity);
+}
+
+Vector3f Rigidbody::GetAngularVelocity () const
+{
+ AssertIf (m_Actor == NULL);
+ return Vec3FromNx(m_Actor->getAngularVelocity ());
+}
+
+void Rigidbody::SetAngularVelocity (const Vector3f& velocity)
+{
+ AssertIf (m_Actor == NULL);
+ ABORT_INVALID_VECTOR3 (velocity, angularVelocity, rigidbody);
+ m_Actor->setAngularVelocity ((const NxVec3&)velocity);
+}
+
+void Rigidbody::SetDrag (float damping)
+{
+ AssertIf (m_Actor == NULL);
+ SetDirty ();
+ m_Drag = damping;
+ m_Actor->setLinearDamping (damping);
+}
+
+float Rigidbody::GetDrag () const
+{
+ AssertIf (m_Actor == NULL);
+ return m_Actor->getLinearDamping ();
+}
+
+void Rigidbody::SetAngularDrag (float damping)
+{
+ AssertIf (m_Actor == NULL);
+ SetDirty ();
+ m_AngularDrag = damping;
+ m_Actor->setAngularDamping (damping);
+}
+
+float Rigidbody::GetAngularDrag () const
+{
+ AssertIf (m_Actor == NULL);
+ return m_Actor->getAngularDamping ();
+}
+
+void Rigidbody::SetMaxAngularVelocity (float maxAngularVelocity)
+{
+ AssertIf (m_Actor == NULL);
+ return m_Actor->setMaxAngularVelocity (maxAngularVelocity);
+}
+
+float Rigidbody::GetMaxAngularVelocity () const
+{
+ AssertIf (m_Actor == NULL);
+ return m_Actor->getMaxAngularVelocity ();
+}
+
+void Rigidbody::SetSolverIterationCount (int iterationCount)
+{
+ return m_Actor->setSolverIterationCount(iterationCount);
+}
+
+int Rigidbody::GetSolverIterationCount () const
+{
+ return m_Actor->getSolverIterationCount();
+}
+
+void Rigidbody::SetSleepVelocity (float value)
+{
+ m_Actor->setSleepLinearVelocity(value);
+}
+
+float Rigidbody::GetSleepVelocity () const
+{
+ return m_Actor->getSleepLinearVelocity();
+}
+
+void Rigidbody::SetSleepAngularVelocity (float value)
+{
+ m_Actor->setSleepAngularVelocity(value);
+}
+
+float Rigidbody::GetSleepAngularVelocity () const
+{
+ return m_Actor->getSleepAngularVelocity();
+}
+
+void Rigidbody::SetInterpolation (RigidbodyInterpolation interpolation)
+{
+ m_Interpolate = interpolation;
+ UpdateInterpolationNode();
+}
+
+Vector3f Rigidbody::GetPointVelocity (const Vector3f& worldPoint) const
+{
+ AssertIf (m_Actor == NULL);
+ return Vec3FromNx(m_Actor->getPointVelocity ((const NxVec3&)worldPoint));
+}
+
+Vector3f Rigidbody::GetRelativePointVelocity (const Vector3f& localPoint) const
+{
+ AssertIf (m_Actor == NULL);
+ return Vec3FromNx(m_Actor->getLocalPointVelocity ((const NxVec3&)localPoint));
+}
+
+void Rigidbody::AddForceAtPosition (const Vector3f& force, const Vector3f& position, int mode)
+{
+ ABORT_INVALID_VECTOR3 (force, force, rigidbody)
+ ABORT_INVALID_VECTOR3 (position, position, rigidbody)
+ AssertIf (m_Actor == NULL);
+ if (!m_Actor->readBodyFlag(NX_BF_KINEMATIC))
+ m_Actor->addForceAtPos ((const NxVec3&)force, (const NxVec3&)position, (NxForceMode)mode);
+}
+
+void Rigidbody::AddForce (const Vector3f& force, int mode)
+{
+ ABORT_INVALID_VECTOR3 (force, force, rigidbody)
+ AssertIf (m_Actor == NULL);
+ if (!m_Actor->readBodyFlag(NX_BF_KINEMATIC))
+ m_Actor->addForce ((const NxVec3&)force, (NxForceMode)mode);
+}
+
+void Rigidbody::AddRelativeForce (const Vector3f& force, int mode)
+{
+ ABORT_INVALID_VECTOR3 (force, force, rigidbody)
+ AssertIf (m_Actor == NULL);
+ if (!m_Actor->readBodyFlag(NX_BF_KINEMATIC))
+ m_Actor->addLocalForce ((const NxVec3&)force, (NxForceMode)mode);
+}
+
+void Rigidbody::AddTorque (const Vector3f& torque, int mode)
+{
+ ABORT_INVALID_VECTOR3 (torque, torque, rigidbody)
+ AssertIf (m_Actor == NULL);
+ if (!m_Actor->readBodyFlag(NX_BF_KINEMATIC))
+ m_Actor->addTorque ((const NxVec3&)torque, (NxForceMode)mode);
+}
+
+void Rigidbody::AddRelativeTorque (const Vector3f& torque, int mode)
+{
+ ABORT_INVALID_VECTOR3 (torque, torque, rigidbody)
+ AssertIf (m_Actor == NULL);
+ if (!m_Actor->readBodyFlag(NX_BF_KINEMATIC))
+ m_Actor->addLocalTorque ((const NxVec3&)torque, (NxForceMode)mode);
+}
+
+void Rigidbody::FetchPoseFromTransform ()
+{
+ AssertIf (m_Actor == NULL);
+ Transform& transform = GetComponent (Transform);
+ Vector3f pos = transform.GetPosition ();
+ Quaternionf rot = transform.GetRotation ();
+ NxMat33 nxrot ((const NxQuat&)rot);
+ NxMat34 pose (nxrot, Vec3ToNx(pos));
+
+ AssertFiniteParameter(pos)
+ AssertFiniteParameter(rot)
+
+ if (GetIsKinematic())
+ {
+ m_Actor->setGlobalPose (pose);
+ // Novodex workaround ->Move global pose needs to always be called in order to make sure triggers get activated!
+ m_Actor->moveGlobalPose (pose);
+ m_DisableReadUpdateTransform = 1;
+ }
+ else
+ {
+ m_Actor->setGlobalPose (pose);
+ }
+}
+
+
+void Rigidbody::TransformChanged (int mask)
+{
+ if (m_Actor)
+ {
+ bool kine = GetIsKinematic();
+
+ // When reading transform positions back from PhysX, we don't want to write back to PhysX again.
+ // However, when a Rigidbody is kinematic or sleeping, and is moved because a parent rigidbody has changed,
+ // we still want to update the position.
+ if (GetPhysicsManager().IsRigidbodyTransformMessageEnabled() || kine || m_Actor->isSleeping())
+ {
+ Transform& transform = GetComponent (Transform);
+ const int posRotMask = Transform::kRotationChanged | Transform::kPositionChanged;
+
+ if ((mask & posRotMask) == posRotMask || (mask & Transform::kScaleChanged))
+ {
+ Vector3f pos = transform.GetPosition ();
+ Quaternionf rot = transform.GetRotation ();
+ NxMat33 nxrot ((const NxQuat&)rot);
+ NxMat34 pose (nxrot, Vec3ToNx(pos));
+
+ AssertFiniteParameter(pos)
+ AssertFiniteParameter(rot)
+
+
+ if (kine)
+ {
+ if ((mask & Transform::kAnimatePhysics) == 0)
+ m_Actor->setGlobalPose (pose);
+ // Novodex workaround ->Move global pose needs to always be called in order to make sure triggers get activated!
+ m_Actor->moveGlobalPose (pose);
+ m_DisableReadUpdateTransform = 1;
+ }
+ else
+ {
+ m_Actor->setGlobalPose (pose);
+ if (m_InterpolationInfo)
+ m_InterpolationInfo->disabled = 1;
+ }
+ }
+ else if (mask & Transform::kRotationChanged)
+ {
+ Quaternionf rot = transform.GetRotation ();
+ AssertFiniteParameter(rot)
+ if (kine)
+ {
+ if ((mask & Transform::kAnimatePhysics) == 0)
+ m_Actor->setGlobalOrientationQuat ((const NxQuat&)rot);
+ // Novodex workaround ->Move global pose needs to always be called in order to make sure triggers get activated!
+ m_Actor->moveGlobalOrientationQuat ((const NxQuat&)rot);
+ m_DisableReadUpdateTransform = 1;
+ }
+ else
+ {
+ m_Actor->setGlobalOrientationQuat ((const NxQuat&)rot);
+
+ if (m_InterpolationInfo)
+ m_InterpolationInfo->disabled = 1;
+ }
+ }
+ else if (mask & Transform::kPositionChanged)
+ {
+ Vector3f pos = transform.GetPosition ();
+ AssertFiniteParameter(pos)
+
+ if (kine)
+ {
+ if ((mask & Transform::kAnimatePhysics) == 0)
+ m_Actor->setGlobalPosition ((const NxVec3&)pos);
+ // Novodex workaround ->Move global pose needs to always be called in order to make sure triggers get activated!
+ m_Actor->moveGlobalPosition ((const NxVec3&)pos);
+ m_DisableReadUpdateTransform = 1;
+ }
+ else
+ {
+ m_Actor->setGlobalPosition ((const NxVec3&)pos);
+ if (m_InterpolationInfo)
+ m_InterpolationInfo->disabled = 1;
+ }
+
+ }
+ }
+ }
+ //printf_console ("Changed transform :%s\n", GetName ().c_str ());
+}
+
+void Rigidbody::ApplyRootMotionBuiltin (RootMotionData* rootMotion)
+{
+ if (m_Actor == NULL || rootMotion->didApply)
+ return;
+
+ if(GetIsKinematic())
+ {
+ SetPosition(GetPosition() + rootMotion->deltaPosition);
+ SetRotation(rootMotion->targetRotation);
+ }
+ else
+ {
+ Quaternionf rotation = GetRotation();
+ Quaternionf invRotation = Inverse(rotation);
+
+ // Get the physics velocity in local space
+ Vector3f physicsVelocityLocal = RotateVectorByQuat(invRotation, GetVelocity());
+
+ // Get the local space velocity and blend it with the physics velocity on the y-axis based on gravity weight
+ // We do this in local space in order to support moving on a curve earth with gravity changing direction
+ Vector3f animVelocityGlobal = rootMotion->deltaPosition * GetInvDeltaTime();
+ Vector3f localVelocity = RotateVectorByQuat (invRotation, animVelocityGlobal);
+ localVelocity.y = Lerp (localVelocity.y, physicsVelocityLocal.y, rootMotion->gravityWeight);
+
+ // If we use gravity, when we are in a jumping root motion, we have to cancel out the gravity
+ // applied by physX by default. When doing for example a jump the only thing affecting velocity should be the animation data.
+ // The animation already has gravity applied in the animation data so to speak...
+ if (GetUseGravity())
+ AddForce(GetPhysicsManager().GetGravity() * -Lerp(1.0F, 0.0F, rootMotion->gravityWeight));
+
+ // Apply velocity & rotation
+ Vector3f globalVelocity = RotateVectorByQuat(rotation, localVelocity);
+ SetVelocity(globalVelocity);
+ MoveRotation(rootMotion->targetRotation);
+ }
+
+ rootMotion->didApply = true;
+}
+
+
+Vector3f Rigidbody::GetPosition () const
+{
+ AssertIf (m_Actor == NULL);
+ return Vec3FromNx (m_Actor->getGlobalPosition ());
+}
+
+Quaternionf Rigidbody::GetRotation () const
+{
+ AssertIf (m_Actor == NULL);
+ return QuatFromNx(m_Actor->getGlobalOrientationQuat ());
+}
+
+void Rigidbody::SetPosition (const Vector3f& position)
+{
+ ABORT_INVALID_VECTOR3 (position, position, rigidbody);
+ if (GetIsKinematic())
+ {
+ m_Actor->setGlobalPosition ((const NxVec3&)position);
+ // Novodex workaround ->Move global pose needs to always be called in order to make sure triggers get activated!
+ m_Actor->moveGlobalPosition ((const NxVec3&)position);
+ m_DisableReadUpdateTransform = 0;
+ }
+ else
+ {
+ if (m_InterpolationInfo)
+ m_InterpolationInfo->disabled = 1;
+
+ m_Actor->setGlobalPosition ((const NxVec3&)position);
+ }
+}
+
+void Rigidbody::SetRotation (const Quaternionf& rotation)
+{
+ ABORT_INVALID_QUATERNION (rotation, rotation, rigidbody);
+
+ if (GetIsKinematic())
+ {
+ m_Actor->setGlobalOrientationQuat ((const NxQuat&)rotation);
+ // Novodex workaround ->Move global pose needs to always be called in order to make sure triggers get activated!
+ m_Actor->moveGlobalOrientationQuat ((const NxQuat&)rotation);
+ m_DisableReadUpdateTransform = 0;
+ }
+ else
+ {
+ if (m_InterpolationInfo)
+ m_InterpolationInfo->disabled = 1;
+
+ m_Actor->setGlobalOrientationQuat ((const NxQuat&)rotation);
+ }
+}
+
+void Rigidbody::MovePosition (const Vector3f& position)
+{
+ ABORT_INVALID_VECTOR3 (position, position, rigidbody);
+ if (GetIsKinematic())
+ {
+ m_Actor->moveGlobalPosition ((const NxVec3&)position);
+ m_DisableReadUpdateTransform = 0;
+ }
+ else
+ {
+ m_Actor->setGlobalPosition ((const NxVec3&)position);
+ }
+}
+
+void Rigidbody::MoveRotation (const Quaternionf& rotation)
+{
+ ABORT_INVALID_QUATERNION (rotation, rotation, rigidbody);
+
+ if (GetIsKinematic())
+ {
+ m_Actor->moveGlobalOrientation ((const NxQuat&)rotation);
+ m_DisableReadUpdateTransform = 0;
+ }
+ else
+ {
+ m_Actor->setGlobalOrientation ((const NxQuat&)rotation);
+ }
+}
+
+void Rigidbody::SetDensity (float density)
+{
+ if (m_Actor)
+ m_Actor->updateMassFromShapes (density, 0.0F);
+}
+
+bool Rigidbody::IsSleeping ()
+{
+ return m_Actor->isSleeping();
+}
+
+void Rigidbody::Sleep ()
+{
+ return m_Actor->putToSleep();
+}
+
+void Rigidbody::WakeUp ()
+{
+ return m_Actor->wakeUp();
+}
+
+
+bool Rigidbody::GetDetectCollisions() const
+{
+ if (m_Actor)
+ return !m_Actor->readActorFlag(NX_AF_DISABLE_COLLISION);
+ else
+ return true;
+}
+
+void Rigidbody::SetDetectCollisions(bool enable)
+{
+ if (m_Actor)
+ {
+ if (enable)
+ m_Actor->clearActorFlag (NX_AF_DISABLE_COLLISION);
+ else
+ m_Actor->raiseActorFlag (NX_AF_DISABLE_COLLISION);
+ }
+}
+
+bool Rigidbody::GetUseConeFriction() const
+{
+ if (m_Actor)
+ return !m_Actor->readActorFlag(NX_AF_FORCE_CONE_FRICTION);
+ else
+ return true;
+}
+
+void Rigidbody::SetUseConeFriction(bool enable)
+{
+ if (m_Actor)
+ {
+ if (enable)
+ m_Actor->clearActorFlag (NX_AF_FORCE_CONE_FRICTION);
+ else
+ m_Actor->raiseActorFlag (NX_AF_FORCE_CONE_FRICTION);
+ }
+}
+
+void Rigidbody::UpdateInterpolationNode ()
+{
+ if (m_Interpolate == kNoInterpolation || !m_ActiveScene)
+ {
+ delete m_InterpolationInfo;
+ m_InterpolationInfo = NULL;
+ }
+ else
+ {
+ if (m_InterpolationInfo == NULL)
+ {
+ m_InterpolationInfo = new RigidbodyInterpolationInfo();
+ RigidbodyInterpolationInfo& info = *m_InterpolationInfo;
+ info.body = this;
+ info.disabled = 1;
+ info.position = Vector3f::zero;
+ info.rotation = Quaternionf::identity();
+ GetPhysicsManager().GetInterpolatedBodies().push_back(*m_InterpolationInfo);
+ }
+ }
+}
+
+void Rigidbody::SetCollisionDetectionMode (int ccd)
+{
+ if (ccd != m_CachedCollisionDetection)
+ {
+ m_CollisionDetection = ccd;
+ m_CachedCollisionDetection = ccd;
+
+ if (m_Actor)
+ {
+ int shapeCount = m_Actor->getNbShapes ();
+ NxShape*const * shapes = m_Actor->getShapes ();
+
+ for (int i=0;i<shapeCount;i++)
+ {
+ Collider* collider = (Collider*)shapes[i]->userData;
+ collider->ReCreate();
+ }
+ }
+
+ SetDirty();
+ }
+}
+
+
+bool Rigidbody::SweepTest (const Vector3f &direction, float distance, RaycastHit& outHit)
+{
+ AssertIf (!IsNormalized (direction));
+ PROFILER_AUTO(gSweepTestProfile, NULL)
+
+ if (m_Actor)
+ {
+ if (distance == std::numeric_limits<float>::infinity())
+ // CapsuleCasts fail when using NX_MAX_F32 here.
+ // So pick a lower "high" number instead.
+ distance = 1000000.0f;
+
+ NxSweepQueryHit hit;
+ NxU32 nb = m_Actor->linearSweep ((const NxVec3&)direction * distance, NX_SF_DYNAMICS|NX_SF_STATICS, NULL, 1, &hit, NULL);
+ if (nb)
+ {
+ NxToRaycastHit(hit, distance, outHit);
+ return true;
+ }
+ }
+ return false;
+}
+
+#define kSweepMaxHits 128
+const PhysicsManager::RaycastHits& Rigidbody::SweepTestAll (const Vector3f &direction, float distance)
+{
+ AssertIf (!IsNormalized (direction));
+ PROFILER_AUTO(gSweepTestAllProfile, NULL)
+
+ if (distance == std::numeric_limits<float>::infinity())
+ // CapsuleCasts fail when using NX_MAX_F32 here.
+ // So pick a lower "high" number instead.
+ distance = 1000000.0f;
+
+ static vector<RaycastHit> outHits;
+
+ if (m_Actor)
+ {
+ NxSweepQueryHit hits[kSweepMaxHits];
+ NxU32 nb = m_Actor->linearSweep ((const NxVec3&)direction * distance, NX_SF_DYNAMICS|NX_SF_STATICS|NX_SF_ALL_HITS, NULL, kSweepMaxHits, hits, NULL);
+
+ outHits.resize(nb);
+ for (int i=0; i<nb; i++)
+ NxToRaycastHit(hits[i], distance, outHits[i]);
+ }
+ return outHits;
+}
+
+
+IMPLEMENT_CLASS_HAS_INIT (Rigidbody)
+IMPLEMENT_OBJECT_SERIALIZE (Rigidbody)
+#endif // ENABLE_PHYSICS
diff --git a/Runtime/Dynamics/ScriptBindings/NewDynamics.txt b/Runtime/Dynamics/ScriptBindings/NewDynamics.txt
new file mode 100644
index 0000000..9a93bab
--- /dev/null
+++ b/Runtime/Dynamics/ScriptBindings/NewDynamics.txt
@@ -0,0 +1,1797 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Dynamics/RigidBody.h"
+#include "Runtime/Geometry/Ray.h"
+#include "Runtime/Dynamics/PhysicsManager.h"
+#include "Runtime/Geometry/Sphere.h"
+#include "Runtime/Dynamics/Joints.h"
+#include "Runtime/Dynamics/ConstantForce.h"
+#include "Runtime/Terrain/Heightmap.h"
+
+#include "Runtime/Dynamics/CapsuleCollider.h"
+#include "Runtime/Dynamics/BoxCollider.h"
+#include "Runtime/Dynamics/SphereCollider.h"
+#include "Runtime/Dynamics/RaycastCollider.h"
+#include "Runtime/Dynamics/WheelCollider.h"
+#include "Runtime/Dynamics/MeshCollider.h"
+#include "Runtime/Dynamics/PhysicMaterial.h"
+#include "Runtime/Dynamics/CharacterController.h"
+#include "Runtime/Dynamics/CharacterJoint.h"
+#include "Runtime/Dynamics/ConfigurableJoint.h"
+#include "Runtime/Dynamics/SpringJoint.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Dynamics/Cloth.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Dynamics/SkinnedCloth.h"
+#include "Runtime/Dynamics/ClothRenderer.h"
+#include "Runtime/Dynamics/RaycastHit.h"
+#include "Runtime/Dynamics/TerrainCollider.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Terrain/TerrainData.h"
+#include "Runtime/Scripting/Scripting.h"
+
+using namespace Unity;
+
+CSRAW
+#if ENABLE_PHYSICS
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+#pragma warning disable 649
+
+namespace UnityEngine
+{
+
+// Option for how to apply a force using Rigidbody.AddForce.
+ENUM ForceMode
+
+ // Add a continuous force to the rigidbody, using its mass.
+ Force = 0,
+
+ // Add a continuous acceleration to the rigidbody, ignoring its mass.
+ Acceleration = 5,
+
+ // Add an instant force impulse to the rigidbody, using its mass.
+ Impulse = 1,
+
+ // Add an instant velocity change to the rigidbody, ignoring its mass.
+ VelocityChange = 2,
+END
+
+// Global physics properties and helper methods.
+CONDITIONAL ENABLE_PHYSICS
+NONSEALED_CLASS Physics
+
+ // The gravity applied to all rigid bodies in the scene.
+ THREAD_SAFE
+ CUSTOM_PROP static Vector3 gravity { return GetPhysicsManager ().GetGravity (); } { SCRIPTINGAPI_THREAD_CHECK(get_gravity) return GetPhysicsManager ().SetGravity (value); }
+
+
+ // The minimum contact penetration value in order to apply a penalty force (default 0.05). Must be positive.
+ CUSTOM_PROP static float minPenetrationForPenalty { return GetPhysicsManager ().GetMinPenetrationForPenalty (); } { return GetPhysicsManager ().SetMinPenetrationForPenalty (value); }
+
+ // Two colliding objects with a relative velocity below this will not bounce (default 2). Must be positive.
+ CUSTOM_PROP static float bounceThreshold { return GetPhysicsManager ().GetBounceThreshold (); } { return GetPhysicsManager ().SetBounceThreshold (value); }
+
+ OBSOLETE warning Please use bounceThreshold instead.
+ CSRAW static public float bounceTreshold { get { return bounceThreshold; } set { bounceThreshold = value; } }
+
+ // The default linear velocity, below which objects start going to sleep (default 0.15). Must be positive.
+ CUSTOM_PROP static float sleepVelocity { return GetPhysicsManager ().GetSleepVelocity (); } { return GetPhysicsManager ().SetSleepVelocity (value); }
+
+ // The default angular velocity, below which objects start sleeping (default 0.14). Must be positive.
+ CUSTOM_PROP static float sleepAngularVelocity { return GetPhysicsManager ().GetSleepAngularVelocity (); } { return GetPhysicsManager ().SetSleepAngularVelocity (value); }
+
+ // The default maximimum angular velocity permitted for any rigid bodies (default 7). Must be positive.
+ CUSTOM_PROP static float maxAngularVelocity { return GetPhysicsManager ().GetMaxAngularVelocity (); } { return GetPhysicsManager ().SetMaxAngularVelocity (value); }
+
+ // The default solver iteration count permitted for any rigid bodies (default 7). Must be positive.
+ CUSTOM_PROP static int solverIterationCount { return GetPhysicsManager ().GetSolverIterationCount (); } { return GetPhysicsManager ().SetSolverIterationCount (value); }
+
+ CUSTOM private static bool Internal_Raycast (Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float distance, int layermask)
+ {
+ hitInfo->collider = NULL;
+
+ float dirLength = Magnitude (direction);
+ if (dirLength > Vector3f::epsilon)
+ {
+ Vector3f normalizedDirection = direction / dirLength;
+ Ray ray (origin, normalizedDirection);
+
+ bool didHit = GetPhysicsManager ().Raycast (ray, distance, *hitInfo, layermask);
+
+ if (didHit)
+ {
+ hitInfo->collider = reinterpret_cast<Collider*>(ScriptingGetObjectReference (hitInfo->collider));
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ return false;
+ }
+
+ CUSTOM private static bool Internal_CapsuleCast (Vector3 point1, Vector3 point2, float radius, Vector3 direction, out RaycastHit hitInfo, float distance, int layermask)
+ {
+ hitInfo->collider = NULL;
+
+ float dirLength = Magnitude (direction);
+ if (dirLength > Vector3f::epsilon)
+ {
+ Vector3f normalizedDirection = direction / dirLength;
+
+ bool didHit = GetPhysicsManager ().CapsuleCast (point1, point2, radius, normalizedDirection, distance, *hitInfo, layermask);
+
+ if (didHit)
+ {
+ hitInfo->collider = reinterpret_cast<Collider*>(ScriptingGetObjectReference (hitInfo->collider));
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ return false;
+ }
+
+ CUSTOM private static bool Internal_RaycastTest (Vector3 origin, Vector3 direction, float distance, int layermask)
+ {
+ float dirLength = Magnitude (direction);
+ if (dirLength > Vector3f::epsilon)
+ {
+ Vector3f normalizedDirection = direction / dirLength;
+ Ray ray (origin, normalizedDirection);
+ return GetPhysicsManager ().RaycastTest (ray, distance, layermask);
+ }
+ else
+ return false;
+ }
+
+ // Casts a ray against all colliders in the scene.
+ CSRAW static public bool Raycast (Vector3 origin, Vector3 direction, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
+ {
+ return Internal_RaycastTest (origin, direction, distance, layerMask);
+ }
+
+ // Casts a ray against all colliders in the scene and returns detailed information on what was hit.
+ CSRAW static public bool Raycast (Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
+ {
+ return Internal_Raycast (origin, direction, out hitInfo, distance, layerMask);
+ }
+
+ // Same as above using /ray.origin/ and /ray.direction/ instead of /origin/ and /direction/.
+ CSRAW static public bool Raycast (Ray ray, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
+ {
+ return Raycast (ray.origin, ray.direction, distance, layerMask);
+ }
+
+ // Same as above using /ray.origin/ and /ray.direction/ instead of /origin/ and /direction/.
+ CSRAW static public bool Raycast (Ray ray, out RaycastHit hitInfo, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
+ {
+ return Raycast (ray.origin, ray.direction, out hitInfo, distance, layerMask);
+ }
+
+ /// *listonly*
+ CSRAW static public RaycastHit[] RaycastAll (Ray ray, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
+ {
+ return RaycastAll (ray.origin, ray.direction, distance, layerMask);
+ }
+
+ // Casts a ray through the scene and returns all hits. Note that order is not guaranteed.
+ CUSTOM static RaycastHit[] RaycastAll (Vector3 origin, Vector3 direction, float distance = Mathf.Infinity, int layermask = DefaultRaycastLayers)
+ {
+ float dirLength = Magnitude (direction);
+ if (dirLength > Vector3f::epsilon)
+ {
+ Vector3f normalizedDirection = direction / dirLength;
+ Ray ray (origin, normalizedDirection);
+
+ const PhysicsManager::RaycastHits& hits = GetPhysicsManager ().RaycastAll (ray, distance, layermask);
+
+ return ConvertNativeRaycastHitsToManaged(hits);
+ }
+ else
+ {
+ return CreateEmptyStructArray(GetMonoManager().GetCommonClasses().raycastHit);
+ }
+ }
+
+ // Returns true if there is any collider intersecting the line between /start/ and /end/.
+ CSRAW static public bool Linecast (Vector3 start, Vector3 end, int layerMask = DefaultRaycastLayers)
+ {
+ Vector3 dir = end - start;
+ return Raycast (start, dir, dir.magnitude, layerMask);
+ }
+
+ // Returns true if there is any collider intersecting the line between /start/ and /end/.
+ CSRAW static public bool Linecast (Vector3 start, Vector3 end, out RaycastHit hitInfo, int layerMask = DefaultRaycastLayers)
+ {
+ Vector3 dir = end - start;
+ return Raycast (start, dir, out hitInfo, dir.magnitude, layerMask);
+ }
+
+ // Returns an array with all colliders touching or inside the sphere.
+ CUSTOM static Collider[] OverlapSphere (Vector3 position, float radius, int layerMask = AllLayers)
+ {
+ const vector<Collider*>& colliders = GetPhysicsManager ().OverlapSphere (position, radius, layerMask);
+
+ return CreateScriptingArrayFromUnityObjects(colliders, ScriptingClassFor(Collider));
+ }
+ /// *listonly*
+ CSRAW static public bool CapsuleCast (Vector3 point1, Vector3 point2, float radius, Vector3 direction, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
+ {
+ RaycastHit hitInfo;
+ return Internal_CapsuleCast (point1, point2, radius, direction, out hitInfo, distance, layerMask);
+ }
+ // Casts a capsule against all colliders in the scene and returns detailed information on what was hit.
+ CSRAW static public bool CapsuleCast (Vector3 point1, Vector3 point2, float radius, Vector3 direction, out RaycastHit hitInfo, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
+ {
+ return Internal_CapsuleCast (point1, point2, radius, direction, out hitInfo, distance, layerMask);
+ }
+
+ // Casts a sphere against all colliders in the scene and returns detailed information on what was hit.
+
+ CSRAW static public bool SphereCast (Vector3 origin, float radius, Vector3 direction, out RaycastHit hitInfo, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
+ {
+ return Internal_CapsuleCast (origin, origin, radius, direction, out hitInfo, distance, layerMask);
+ }
+
+ /// *listonly*
+ CSRAW static public bool SphereCast (Ray ray, float radius, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
+ {
+ RaycastHit hitInfo;
+ return Internal_CapsuleCast (ray.origin, ray.origin, radius, ray.direction, out hitInfo, distance, layerMask);
+ }
+ // Casts a sphere against all colliders in the scene and returns detailed information on what was hit.
+
+ CSRAW static public bool SphereCast (Ray ray, float radius, out RaycastHit hitInfo, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
+ {
+ return Internal_CapsuleCast (ray.origin, ray.origin, radius, ray.direction, out hitInfo, distance, layerMask);
+ }
+
+
+ // Like [[Physics.CapsuleCast]], but this function will return all hits the capsule sweep intersects.
+
+ CUSTOM static RaycastHit[] CapsuleCastAll (Vector3 point1, Vector3 point2, float radius, Vector3 direction, float distance = Mathf.Infinity, int layermask = DefaultRaycastLayers)
+ {
+ float dirLength = Magnitude (direction);
+ if (dirLength > Vector3f::epsilon)
+ {
+ Vector3f normalizedDirection = direction / dirLength;
+
+ const PhysicsManager::RaycastHits& hits = GetPhysicsManager ().CapsuleCastAll (point1, point2, radius, normalizedDirection, distance, layermask);
+
+ return ConvertNativeRaycastHitsToManaged(hits);
+ }
+ else
+ {
+ return CreateEmptyStructArray(GetMonoManager().GetCommonClasses().raycastHit);
+ }
+ }
+
+ // Like [[Physics.SphereCast]], but this function will return all hits the sphere sweep intersects.
+
+
+ CSRAW static public RaycastHit[] SphereCastAll (Vector3 origin, float radius, Vector3 direction, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
+ {
+ return CapsuleCastAll (origin, origin, radius, direction, distance, layerMask);
+ }
+ // @param ray The starting point and direction of the ray into which the sphere sweep is cast.
+ /// *listonly*
+
+ CSRAW static public RaycastHit[] SphereCastAll (Ray ray, float radius, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
+ {
+ return CapsuleCastAll (ray.origin, ray.origin, radius, ray.direction, distance, layerMask);
+ }
+
+
+ // Returns true if there are any colliders overlapping the sphere defined by /position/ and /radius/ in world coordinates.
+ CUSTOM static bool CheckSphere (Vector3 position, float radius, int layerMask = DefaultRaycastLayers)
+ {
+ return GetPhysicsManager ().SphereTest (position, radius, layerMask);
+ }
+
+ // Returns true if there are any colliders overlapping the capsule defined by the axis going from /start/ and /end/ and having /radius/ in world coordinates
+ CUSTOM static bool CheckCapsule (Vector3 start, Vector3 end, float radius, int layermask = DefaultRaycastLayers)
+ {
+ return GetPhysicsManager().CapsuleTest(start, end, radius, layermask);
+ }
+
+ //*undocumented* DEPRECATED
+ CSRAW public const int kIgnoreRaycastLayer = 1 << 2;
+ //*undocumented* DEPRECATED
+ CSRAW public const int kDefaultRaycastLayers = ~kIgnoreRaycastLayer;
+ //*undocumented* DEPRECATED
+ CSRAW public const int kAllLayers = ~0;
+
+ CSRAW public const int IgnoreRaycastLayer = 1 << 2;
+ CSRAW public const int DefaultRaycastLayers = ~IgnoreRaycastLayer;
+ CSRAW public const int AllLayers = ~0;
+
+ // *undocumented* DEPRECATED
+ OBSOLETE warning penetrationPenaltyForce has no effect.
+ CUSTOM_PROP static float penetrationPenaltyForce { return 0; } { }
+
+
+ // Makes the collision detection system ignore all collisions between /collider1/ and /collider2/.
+ CUSTOM static void IgnoreCollision (Collider collider1, Collider collider2, bool ignore = true)
+ {
+ GetPhysicsManager().IgnoreCollision(*collider1, *collider2, ignore);
+ }
+
+ // Makes the collision detection system ignore all collisions between any collider in /layer1/ and any collider in /layer2/.
+ CUSTOM static void IgnoreLayerCollision (int layer1, int layer2, bool ignore = true)
+ {
+ GetPhysicsManager().IgnoreCollision(layer1, layer2, ignore);
+ }
+
+ // Are collisions between /layer1/ and /layer2/ being ignored?
+ CUSTOM static bool GetIgnoreLayerCollision (int layer1, int layer2)
+ {
+ return GetPhysicsManager().GetIgnoreCollision(layer1, layer2);
+ }
+
+END
+
+// Use these flags to constrain motion of Rigidbodies.
+ENUM RigidbodyConstraints
+ // No constraints
+ None = 0,
+
+ // Freeze motion along the X-axis.
+ FreezePositionX = 0x02,
+
+ // Freeze motion along the Y-axis.
+ FreezePositionY = 0x04,
+
+ // Freeze motion along the Z-axis.
+ FreezePositionZ = 0x08,
+
+ // Freeze rotation along the X-axis.
+ FreezeRotationX = 0x10,
+
+ // Freeze rotation along the Y-axis.
+ FreezeRotationY = 0x20,
+
+ // Freeze rotation along the Z-axis.
+ FreezeRotationZ = 0x40,
+
+ // Freeze motion along all axes.
+ FreezePosition = 0x0e,
+
+ // Freeze rotation along all axes.
+ FreezeRotation = 0x70,
+
+ // Freeze rotation and motion along all axes.
+ FreezeAll = 0x7e,
+
+END
+
+// Control of an object's position through physics simulation.
+CONDITIONAL ENABLE_PHYSICS
+CLASS Rigidbody : Component
+
+ // The velocity vector of the rigidbody.
+ AUTO_PROP Vector3 velocity GetVelocity SetVelocity
+
+ // The angular velocity vector of the rigidbody.
+ AUTO_PROP Vector3 angularVelocity GetAngularVelocity SetAngularVelocity
+
+ // The drag of the object.
+ AUTO_PROP float drag GetDrag SetDrag
+
+ // The angular drag of the object.
+ AUTO_PROP float angularDrag GetAngularDrag SetAngularDrag
+
+ // The mass of the rigidbody.
+ AUTO_PROP float mass GetMass SetMass
+
+ // Sets the mass based on the attached colliders assuming a constant density.
+ AUTO void SetDensity (float density);
+
+ // Controls whether gravity affects this rigidbody.
+ AUTO_PROP bool useGravity GetUseGravity SetUseGravity
+
+
+ // Controls whether physics affects the rigidbody.
+ AUTO_PROP bool isKinematic GetIsKinematic SetIsKinematic
+
+ // Controls whether physics will change the rotation of the object.
+ AUTO_PROP bool freezeRotation GetFreezeRotation SetFreezeRotation
+
+ // Controls which degrees of freedom are alowed for the simulation of this Rigidbody.
+ AUTO_PROP RigidbodyConstraints constraints GetConstraints SetConstraints
+
+ // The Rigidbody's collision detection mode.
+ AUTO_PROP CollisionDetectionMode collisionDetectionMode GetCollisionDetectionMode SetCollisionDetectionMode
+
+
+ // Adds a force to the rigidbody. As a result the rigidbody will start moving.
+ CUSTOM void AddForce (Vector3 force, ForceMode mode = ForceMode.Force) { self->AddForce (force, mode); }
+
+ // Adds a force to the rigidbody. As a result the rigidbody will start moving.
+ CSRAW public void AddForce (float x, float y, float z, ForceMode mode = ForceMode.Force) { AddForce (new Vector3 (x, y, z), mode); }
+
+ // Adds a force to the rigidbody relative to its coordinate system.
+ CUSTOM void AddRelativeForce (Vector3 force, ForceMode mode = ForceMode.Force) { self->AddRelativeForce (force, mode); }
+
+ // Adds a force to the rigidbody relative to its coordinate system.
+ CSRAW public void AddRelativeForce (float x, float y, float z, ForceMode mode = ForceMode.Force) { AddRelativeForce (new Vector3 (x, y, z), mode); }
+
+ // Adds a torque to the rigidbody.
+ CUSTOM void AddTorque (Vector3 torque, ForceMode mode = ForceMode.Force) { self->AddTorque (torque, mode); }
+
+
+ // Adds a torque to the rigidbody.
+ CSRAW public void AddTorque (float x, float y, float z, ForceMode mode = ForceMode.Force) { AddTorque (new Vector3 (x, y, z), mode); }
+
+ // Adds a torque to the rigidbody relative to the rigidbodie's own coordinate system.
+ CUSTOM void AddRelativeTorque (Vector3 torque, ForceMode mode = ForceMode.Force) { self->AddRelativeTorque (torque, mode); }
+
+ // Adds a torque to the rigidbody relative to the rigidbodie's own coordinate system.
+ CSRAW public void AddRelativeTorque (float x, float y, float z, ForceMode mode = ForceMode.Force) { AddRelativeTorque (new Vector3 (x, y, z), mode); }
+
+ // Applies /force/ at /position/. As a result this will apply a torque and force on the object.
+ CUSTOM void AddForceAtPosition (Vector3 force, Vector3 position, ForceMode mode = ForceMode.Force) { self->AddForceAtPosition (force, position, mode); }
+
+
+ // Applies a force to the rigidbody that simulates explosion effects. The explosion force will fall off linearly with distance to the rigidbody.
+ CUSTOM void AddExplosionForce(float explosionForce, Vector3 explosionPosition, float explosionRadius, float upwardsModifier = 0.0F, ForceMode mode = ForceMode.Force) { self->AddExplosionForce (explosionForce, explosionPosition, explosionRadius, upwardsModifier, mode); }
+
+ // The closest point to the bounding box of the attached colliders.
+ CUSTOM Vector3 ClosestPointOnBounds (Vector3 position)
+ {
+ float dist; Vector3f outpos;
+ self->ClosestPointOnBounds(position, outpos, dist);
+ return outpos;
+ }
+
+
+ // The velocity relative to the rigidbody at the point /relativePoint/.
+ AUTO Vector3 GetRelativePointVelocity (Vector3 relativePoint);
+
+ // The velocity of the rigidbody at the point /worldPoint/ in global space.
+ AUTO Vector3 GetPointVelocity (Vector3 worldPoint);
+
+
+ // The center of mass relative to the transform's origin.
+ AUTO_PROP Vector3 centerOfMass GetCenterOfMass SetCenterOfMass
+
+ // The center of mass of the rigidbody in world space (RO).
+ AUTO_PROP Vector3 worldCenterOfMass GetWorldCenterOfMass
+
+ // The rotation of the inertia tensor.
+ AUTO_PROP Quaternion inertiaTensorRotation GetInertiaTensorRotation SetInertiaTensorRotation
+
+ // The diagonal inertia tensor of mass relative to the center of mass.
+ AUTO_PROP Vector3 inertiaTensor GetInertiaTensor SetInertiaTensor
+
+ // Should collision detection be enabled? (By default always enabled)
+ AUTO_PROP bool detectCollisions GetDetectCollisions SetDetectCollisions
+
+ // Force cone friction to be used for this rigidbody.
+ AUTO_PROP bool useConeFriction GetUseConeFriction SetUseConeFriction
+
+ // The position of the rigidbody.
+ AUTO_PROP Vector3 position GetPosition SetPosition
+
+ // The rotation of the rigdibody.
+ AUTO_PROP Quaternion rotation GetRotation SetRotation
+
+ // Moves the rigidbody to /position/.
+ AUTO void MovePosition (Vector3 position);
+
+ // Rotates the rigidbody to /rotation/.
+ AUTO void MoveRotation (Quaternion rot);
+
+ // Interpolation allows you to smooth out the effect of running physics at a fixed frame rate.
+ AUTO_PROP RigidbodyInterpolation interpolation GetInterpolation SetInterpolation
+
+ // Forces a rigidbody to sleep at least one frame.
+ AUTO void Sleep ();
+
+ // Is the rigidbody sleeping?
+ AUTO bool IsSleeping ();
+
+ // Forces a rigidbody to wake up.
+ AUTO void WakeUp ();
+
+ // Allows you to override the solver iteration count per rigidbody.
+ AUTO_PROP int solverIterationCount GetSolverIterationCount SetSolverIterationCount
+
+ // The linear velocity, below which objects start going to sleep. (Default 0.14) range { 0, infinity }
+ AUTO_PROP float sleepVelocity GetSleepVelocity SetSleepVelocity
+
+ // The angular velocity, below which objects start going to sleep. (Default 0.14) range { 0, infinity }
+ AUTO_PROP float sleepAngularVelocity GetSleepAngularVelocity SetSleepAngularVelocity
+
+ // The maximimum angular velocity of the rigidbody. (Default 7) range { 0, infinity }
+ AUTO_PROP float maxAngularVelocity GetMaxAngularVelocity SetMaxAngularVelocity
+
+
+ // OnCollisionEnter is called when this collider/rigidbody has begun touching another rigidbody/collider.
+ CSNONE void OnCollisionEnter (Collision collisionInfo);
+
+ // OnCollisionEnter is called when this collider/rigidbody has stopped touching another rigidbody/collider.
+ CSNONE void OnCollisionExit (Collision collisionInfo);
+
+ // OnCollisionStay is called once per frame for every collider/rigidbody that is touching rigidbody/collider.
+ CSNONE void OnCollisionStay (Collision collisionInfo);
+
+ // Tests if a rigidbody would collide with anything, if it was moved through the scene.
+ CUSTOM public bool SweepTest (Vector3 direction, out RaycastHit hitInfo, float distance = Mathf.Infinity)
+ {
+ hitInfo->collider = NULL;
+
+ float dirLength = Magnitude (direction);
+ if (dirLength > Vector3f::epsilon)
+ {
+ Vector3f normalizedDirection = direction / dirLength;
+
+ bool didHit = self->SweepTest (normalizedDirection, distance, *hitInfo);
+
+ if (didHit)
+ {
+ hitInfo->collider = reinterpret_cast<Collider*>(ScriptingGetObjectReference (hitInfo->collider));
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ return false;
+ }
+ // Like [[Rigidbody.SweepTest]], but returns all hits.
+ CUSTOM RaycastHit[] SweepTestAll (Vector3 direction, float distance = Mathf.Infinity)
+ {
+ float dirLength = Magnitude (direction);
+ if (dirLength > Vector3f::epsilon)
+ {
+ Vector3f normalizedDirection = direction / dirLength;
+
+ const PhysicsManager::RaycastHits& hits = self->SweepTestAll (normalizedDirection, distance);
+
+ return ConvertNativeRaycastHitsToManaged(hits);
+ }
+ else
+ {
+ return CreateScriptingArray<RaycastHit>(NULL, 0, GetMonoManager().GetCommonClasses().raycastHit);
+ }
+ }
+
+ //*undocumented* DEPRECATED
+ OBSOLETE warning use Rigidbody.maxAngularVelocity instead.
+ CSRAW public void SetMaxAngularVelocity (float a) { maxAngularVelocity = a; }
+END
+
+// [[Rigidbody]] interpolation mode.
+ENUM RigidbodyInterpolation
+ // No Interpolation.
+ None = 0,
+
+ // Interpolation will always lag a little bit behind but can be smoother than extrapolation.
+ Interpolate = 1,
+
+ // Extrapolation will predict the position of the rigidbody based on the current velocity.
+ Extrapolate = 2
+END
+
+// The JointMotor is used to motorize a joint.
+CONDITIONAL ENABLE_PHYSICS
+STRUCT JointMotor
+ CSRAW private float m_TargetVelocity;
+ CSRAW private float m_Force;
+ CSRAW private bool m_FreeSpin;
+
+ // The motor will apply a force up to /force/ to achieve /targetVelocity/.
+ CSRAW public float targetVelocity { get { return m_TargetVelocity; } set { m_TargetVelocity = value; } }
+
+ // The motor will apply a force.
+ CSRAW public float force { get { return m_Force; } set { m_Force = value; } }
+
+ // If /freeSpin/ is enabled the motor will only accelerate but never slow down.
+ CSRAW public bool freeSpin { get { return m_FreeSpin; } set { m_FreeSpin = value; } }
+END
+
+// JointSpring is used add a spring force to [[HingeJoint]] and [[PhysicMaterial]].
+CONDITIONAL ENABLE_PHYSICS
+STRUCT JointSpring
+
+ // The spring forces used to reach the target position
+ CSRAW public float spring;
+
+ // The damper force uses to dampen the spring
+ CSRAW public float damper;
+
+ // The target position the joint attempts to reach.
+ CSRAW public float targetPosition;
+
+// We have to keep those as public variables because of a bug in the C# raycast sample.
+END
+
+// JointLimits is used by the [[HingeJoint]] to limit the joints angle.
+CONDITIONAL ENABLE_PHYSICS
+STRUCT JointLimits
+ CSRAW private float m_Min;
+ CSRAW private float m_MinBounce;
+ CSRAW private float m_MinHardness;
+ CSRAW private float m_Max;
+ CSRAW private float m_MaxBounce;
+ CSRAW private float m_MaxHardness;
+
+ // The lower limit of the joint. When the joint angle or position is below it,
+ CSRAW public float min { get { return m_Min; } set { m_Min = value; } }
+
+ // The bounciness of the joint when hitting the lower limit of the joint.
+ CSRAW public float minBounce { get { return m_MinBounce; } set { m_MinBounce = value; } }
+
+ // The upper limit of the joint. When the joint angle or position is above it,
+ CSRAW public float max { get { return m_Max; } set { m_Max = value; } }
+
+ // The bounciness of the joint when hitting the upper limit of the joint.
+ CSRAW public float maxBounce { get { return m_MaxBounce; } set { m_MaxBounce = value; } }
+END
+
+
+C++RAW
+
+struct MonoJointMotor
+{
+ float targetVelocity;
+ float force;
+ short freeSpin;// bool's need to be shorts in mono but in novodex bools are ints
+
+ MonoJointMotor (const JointMotor& motor)
+ {
+ targetVelocity = motor.targetVelocity;
+ force = motor.force;
+ freeSpin = motor.freeSpin;
+ }
+
+ operator JointMotor () const
+ {
+ JointMotor motor;
+ motor.targetVelocity = targetVelocity;
+ motor.force = force;
+ motor.freeSpin = freeSpin;
+ return motor;
+ }
+};
+
+
+
+// Joint is the base class for all joints.
+CONDITIONAL ENABLE_PHYSICS
+NONSEALED_CLASS Joint : Component
+
+ // A reference to another rigidbody this joint connects to.
+ AUTO_PTR_PROP Rigidbody connectedBody GetConnectedBody SetConnectedBody
+
+ // The Direction of the axis around which the body is constrained.
+ AUTO_PROP Vector3 axis GetAxis SetAxis
+
+ // The Position of the anchor around which the joints motion is constrained.
+ AUTO_PROP Vector3 anchor GetAnchor SetAnchor
+
+ // The Position of the connected anchor around which the joints motion is constrained.
+ AUTO_PROP Vector3 connectedAnchor GetConnectedAnchor SetConnectedAnchor
+
+ // Should the connected anchor position be used?
+ AUTO_PROP bool autoConfigureConnectedAnchor GetAutoConfigureConnectedAnchor SetAutoConfigureConnectedAnchor
+
+ // The force that needs to be applied for this joint to break.
+ AUTO_PROP float breakForce GetBreakForce SetBreakForce
+
+ // The torque that needs to be applied for this joint to break.
+ AUTO_PROP float breakTorque GetBreakTorque SetBreakTorque
+
+ // Called when a joint attached to the same game object broke.
+ CSNONE void OnJointBreak (float breakForce);
+END
+
+// The HingeJoint groups together 2 rigid bodies, constraining them to move like connected by a hinge.
+CONDITIONAL ENABLE_PHYSICS
+CLASS HingeJoint : Joint
+
+ // Setting the motor, limit, spring automatically enabled them.
+
+ // The motor will apply a force up to a maximum force to achieve the target velocity in degrees per second.
+ AUTO_PROP JointMotor motor GetMotor SetMotor
+
+ // The limits of the hinge joint.
+ AUTO_PROP JointLimits limits GetLimits SetLimits
+
+ // The spring attempts to reach a target angle by adding spring and damping forces.
+ AUTO_PROP JointSpring spring GetSpring SetSpring
+
+ // Enables the joint's motor.
+ AUTO_PROP bool useMotor GetUseMotor SetUseMotor
+
+ // Enables the joint's limits.
+ AUTO_PROP bool useLimits GetUseLimits SetUseLimits
+
+ // Enables the joint's spring.
+ AUTO_PROP bool useSpring GetUseSpring SetUseSpring
+
+ // The angular velocity of the joint in degrees per second.
+ AUTO_PROP float velocity GetVelocity
+
+ // The current angle in degrees of the joint relative to its rest position. (RO)
+ AUTO_PROP float angle GetAngle
+END
+
+
+
+// The spring joint ties together 2 rigid bodies, spring forces will be automatically applied to keep the object at the given distance.
+CONDITIONAL ENABLE_PHYSICS
+CLASS SpringJoint : Joint
+
+ // The spring force used to keep the two objects together
+ AUTO_PROP float spring GetSpring SetSpring
+ // The damper force used to dampen the spring force
+ AUTO_PROP float damper GetDamper SetDamper
+
+ // The minimum distance between the bodies relative to their initial distance
+ AUTO_PROP float minDistance GetMinDistance SetMinDistance
+
+ // The maximum distance between the bodies relative to their initial distance
+ AUTO_PROP float maxDistance GetMaxDistance SetMaxDistance
+END
+
+// The Fixed joint groups together 2 rigidbodies, making them stick together in their bound position.
+CONDITIONAL ENABLE_PHYSICS
+CLASS FixedJoint : Joint
+
+END
+
+// The limits defined by the [[CharacterJoint]]
+CONDITIONAL ENABLE_PHYSICS
+STRUCT SoftJointLimit
+ CSRAW float m_Limit;
+ CSRAW float m_Bounciness;
+ CSRAW float m_Spring;
+ CSRAW float m_Damper;
+
+ // The limit position/angle of the joint.
+ CSRAW public float limit { get { return m_Limit; } set { m_Limit = value; } }
+
+ // If greater than zero, the limit is soft. The spring will pull the joint back.
+ CSRAW public float spring { get { return m_Spring; } set { m_Spring = value; } }
+
+ // If spring is greater than zero, the limit is soft.
+ CSRAW public float damper { get { return m_Damper; } set { m_Damper = value; } }
+
+ // When the joint hits the limit, it can be made to bounce off it.
+ CSRAW public float bounciness { get { return m_Bounciness; } set { m_Bounciness = value; } }
+
+ OBSOLETE error Use SoftJointLimit.bounciness instead
+ CSRAW public float bouncyness { get { return m_Bounciness; } set { m_Bounciness = value; } }
+END
+
+
+// The [[ConfigurableJoint]] attempts to attain position / velocity targets based on this flag
+CSRAW [Flags]
+ENUM JointDriveMode
+ // Don't apply any forces to reach the target
+ None = 0,
+ // Try to reach the specified target position
+ Position = 1,
+ // Try to reach the specified target velocity
+ Velocity = 2,
+ // Try to reach the specified target position and velocity
+ PositionAndVelocity = 3
+END
+
+// Determines how to snap physics joints back to its constrained position when it drifts off too much
+ENUM JointProjectionMode
+ // Don't snap at all
+ None = 0,
+ // Snap both position and rotation
+ PositionAndRotation = 1,
+ // Snap Position only
+ PositionOnly = 2
+END
+
+// How the joint's movement will behave along its local X axis
+CONDITIONAL ENABLE_PHYSICS
+STRUCT JointDrive
+ CSRAW int m_Mode;
+ CSRAW float m_PositionSpring;
+ CSRAW float m_PositionDamper;
+ CSRAW float m_MaximumForce;
+
+ // Whether the drive should attempt to reach position, velocity, both or nothing
+ CSRAW public JointDriveMode mode { get { return (JointDriveMode)m_Mode; } set { m_Mode = (int)value; } }
+
+ // Strength of a rubber-band pull toward the defined direction. Only used if /mode/ includes Position.
+ CSRAW public float positionSpring { get { return m_PositionSpring; } set { m_PositionSpring = value; } }
+
+ // Resistance strength against the Position Spring. Only used if /mode/ includes Position.
+ CSRAW public float positionDamper { get { return m_PositionDamper; } set { m_PositionDamper = value; } }
+
+ // Amount of force applied to push the object toward the defined direction.
+ CSRAW public float maximumForce { get { return m_MaximumForce; } set { m_MaximumForce = value; } }
+END
+
+// Character Joints are mainly used for Ragdoll effects. They are an extended ball-socket joint which allows you to limit the joint on each axis.
+CONDITIONAL ENABLE_PHYSICS
+CLASS CharacterJoint : Joint
+
+ // The secondary axis around which the joint can rotate
+ AUTO_PROP Vector3 swingAxis GetSwingAxis SetSwingAxis
+
+ // The lower limit around the primary axis of the character joint.
+ AUTO_PROP SoftJointLimit lowTwistLimit GetLowTwistLimit SetLowTwistLimit
+
+ // The upper limit around the primary axis of the character joint.
+ AUTO_PROP SoftJointLimit highTwistLimit GetHighTwistLimit SetHighTwistLimit
+
+ // The limit around the primary axis of the character joint.
+ AUTO_PROP SoftJointLimit swing1Limit GetSwing1Limit SetSwing1Limit
+
+ // The limit around the primary axis of the character joint.
+ AUTO_PROP SoftJointLimit swing2Limit GetSwing2Limit SetSwing2Limit
+
+ //*undocumented*
+ AUTO_PROP Quaternion targetRotation GetTargetRotation SetTargetRotation
+ //*undocumented*
+ AUTO_PROP Vector3 targetAngularVelocity GetTargetAngularVelocity SetTargetAngularVelocity
+ //*undocumented*
+ AUTO_PROP JointDrive rotationDrive GetRotationDrive SetRotationDrive
+
+END
+
+// Constrains movement for a [[ConfigurableJoint]] along the 6 axes.
+ENUM ConfigurableJointMotion
+ // Motion along the axis will be locked
+ Locked = 0,
+ // Motion along the axis will be limited by the respective limit
+ Limited = 1,
+ // Motion along the axis will be completely free and completely unconstrained
+ Free = 2
+END
+
+// Control [[ConfigurableJoint]]'s rotation with either X & YZ or Slerp Drive
+ENUM RotationDriveMode
+ // Use XY & Z Drive
+ XYAndZ = 0,
+ // Use Slerp drive
+ Slerp = 1
+END
+
+// The configurable joint is an extremely flexible joint giving you complete control over rotation and linear motion.
+CONDITIONAL ENABLE_PHYSICS
+CLASS ConfigurableJoint : Joint
+
+ // The joint's secondary axis.
+ AUTO_PROP Vector3 secondaryAxis GetSecondaryAxis SetSecondaryAxis
+
+ // Allow movement along the X axis to be Free, completely Locked, or Limited according to Linear Limit
+ AUTO_PROP ConfigurableJointMotion xMotion GetXMotion SetXMotion
+ // Allow movement along the Y axis to be Free, completely Locked, or Limited according to Linear Limit
+ AUTO_PROP ConfigurableJointMotion yMotion GetYMotion SetYMotion
+ // Allow movement along the Z axis to be Free, completely Locked, or Limited according to Linear Limit
+ AUTO_PROP ConfigurableJointMotion zMotion GetZMotion SetZMotion
+
+ // Allow rotation around the X axis to be Free, completely Locked, or Limited according to Low and High Angular XLimit
+ AUTO_PROP ConfigurableJointMotion angularXMotion GetAngularXMotion SetAngularXMotion
+ // Allow rotation around the Y axis to be Free, completely Locked, or Limited according to Angular YLimit
+ AUTO_PROP ConfigurableJointMotion angularYMotion GetAngularYMotion SetAngularYMotion
+ // Allow rotation around the Z axis to be Free, completely Locked, or Limited according to Angular ZLimit
+ AUTO_PROP ConfigurableJointMotion angularZMotion GetAngularZMotion SetAngularZMotion
+
+ // Boundary defining movement restriction, based on distance from the joint's origin
+ AUTO_PROP SoftJointLimit linearLimit GetLinearLimit SetLinearLimit
+
+ // Boundary defining lower rotation restriction, based on delta from original rotation
+ AUTO_PROP SoftJointLimit lowAngularXLimit GetLowAngularXLimit SetLowAngularXLimit
+
+ // Boundary defining upper rotation restriction, based on delta from original rotation.
+ AUTO_PROP SoftJointLimit highAngularXLimit GetHighAngularXLimit SetHighAngularXLimit
+
+ // Boundary defining rotation restriction, based on delta from original rotation
+ AUTO_PROP SoftJointLimit angularYLimit GetAngularYLimit SetAngularYLimit
+
+ // Boundary defining rotation restriction, based on delta from original rotation
+ AUTO_PROP SoftJointLimit angularZLimit GetAngularZLimit SetAngularZLimit
+
+ // The desired position that the joint should move into
+ AUTO_PROP Vector3 targetPosition GetTargetPosition SetTargetPosition
+ // The desired velocity that the joint should move along
+ AUTO_PROP Vector3 targetVelocity GetTargetVelocity SetTargetVelocity
+
+ // Definition of how the joint's movement will behave along its local X axis
+ AUTO_PROP JointDrive xDrive GetXDrive SetXDrive
+
+ // Definition of how the joint's movement will behave along its local Y axis
+ AUTO_PROP JointDrive yDrive GetYDrive SetYDrive
+
+ // Definition of how the joint's movement will behave along its local Z axis
+ AUTO_PROP JointDrive zDrive GetZDrive SetZDrive
+
+ // This is a [[Quaternion]]. It defines the desired rotation that the joint should rotate into.
+ AUTO_PROP Quaternion targetRotation GetTargetRotation SetTargetRotation
+ // This is a [[Vector3]]. It defines the desired angular velocity that the joint should rotate into.
+ AUTO_PROP Vector3 targetAngularVelocity GetTargetAngularVelocity SetTargetAngularVelocity
+ // Control the object's rotation with either X & YZ or Slerp Drive by itself
+ AUTO_PROP RotationDriveMode rotationDriveMode GetRotationDriveMode SetRotationDriveMode
+
+ // Definition of how the joint's rotation will behave around its local X axis. Only used if Rotation Drive Mode is Swing & Twist
+ AUTO_PROP JointDrive angularXDrive GetAngularXDrive SetAngularXDrive
+
+ // Definition of how the joint's rotation will behave around its local Y and Z axes. Only used if Rotation Drive Mode is Swing & Twist
+ AUTO_PROP JointDrive angularYZDrive GetAngularYZDrive SetAngularYZDrive
+
+ // Definition of how the joint's rotation will behave around all local axes. Only used if Rotation Drive Mode is Slerp Only
+ AUTO_PROP JointDrive slerpDrive GetSlerpDrive SetSlerpDrive
+
+
+ // Properties to track to snap the object back to its constrained position when it drifts off too much
+ AUTO_PROP JointProjectionMode projectionMode GetProjectionMode SetProjectionMode
+
+ // Distance from the Connected Body that must be exceeded before the object snaps back to an acceptable position
+ AUTO_PROP float projectionDistance GetProjectionDistance SetProjectionDistance
+
+ // Difference in angle from the Connected Body that must be exceeded before the object snaps back to an acceptable position
+ AUTO_PROP float projectionAngle GetProjectionAngle SetProjectionAngle
+
+
+ // If enabled, all Target values will be calculated in world space instead of the object's local space
+ AUTO_PROP bool configuredInWorldSpace GetConfiguredInWorldSpace SetConfiguredInWorldSpace
+
+ // If enabled, the two connected rigidbodies will be swapped, as if the joint was attached to the other body.
+ AUTO_PROP bool swapBodies GetSwapBodies SetSwapBodies
+
+END
+
+
+// A force applied constantly.
+CONDITIONAL ENABLE_PHYSICS
+CLASS ConstantForce : Behaviour
+ // The force applied to the rigidbody every frame.
+ CUSTOM_PROP Vector3 force { return self->m_Force; } { self->m_Force = value; }
+
+ // The force - relative to the rigid bodies coordinate system - applied every frame.
+ CUSTOM_PROP Vector3 relativeForce { return self->m_RelativeForce; } { self->m_RelativeForce = value; }
+
+ // The torque applied to the rigidbody every frame.
+ CUSTOM_PROP Vector3 torque { return self->m_Torque; } { self->m_Torque = value; }
+
+ // The torque - relative to the rigid bodies coordinate system - applied every frame.
+ CUSTOM_PROP Vector3 relativeTorque { return self->m_RelativeTorque; } { self->m_RelativeTorque = value; }
+END
+
+// The collision detection mode constants used for [[Rigidbody.collisionDetectionMode]].
+ENUM CollisionDetectionMode
+ // Continuous collision detection is off for this Rigidbody.
+ Discrete = 0,
+ // Continuous collision detection is on for colliding with static mesh geometry.
+ Continuous = 1,
+ // Continuous collision detection is on for colliding with static and dynamic geometry.
+ ContinuousDynamic = 2
+END
+
+// A base class of all colliders.
+CONDITIONAL ENABLE_PHYSICS
+NONSEALED_CLASS Collider : Component
+ // Enabled Colliders will collide with other colliders, disabled Colliders won't.
+ AUTO_PROP bool enabled GetEnabled SetEnabled
+
+ // The rigidbody the collider is attached to.
+ AUTO_PTR_PROP Rigidbody attachedRigidbody GetRigidbody
+
+ // Is the collider a trigger?
+ AUTO_PROP bool isTrigger GetIsTrigger SetIsTrigger
+
+ // The material used by the collider.
+ CUSTOM_PROP PhysicMaterial material
+ {
+ PhysicMaterial* material = self->GetMaterial ();
+ PhysicMaterial* instance = &PhysicMaterial::GetInstantiatedMaterial (material, *self);
+ if (instance != material)
+ self->SetMaterial (instance);
+ return Scripting::ScriptingWrapperFor (instance);
+ }
+ {
+ self->SetMaterial (value);
+ }
+
+ // The closest point to the bounding box of the attached collider.
+ CUSTOM Vector3 ClosestPointOnBounds (Vector3 position)
+ {
+ float dist; Vector3f outpos;
+ self->ClosestPointOnBounds(position, outpos, dist);
+ return outpos;
+ }
+
+ // The shared physic material of this collider.
+ CUSTOM_PROP PhysicMaterial sharedMaterial { return Scripting::ScriptingWrapperFor (self->GetMaterial ()); } { self->SetMaterial (value); }
+
+ // The world space bounding volume of the collider.
+ AUTO_PROP Bounds bounds GetBounds
+
+ CUSTOM private static bool Internal_Raycast (Collider col, Ray ray, out RaycastHit hitInfo, float distance)
+ {
+ hitInfo->collider = NULL;
+
+ bool didHit = col->Raycast (ray, distance, *hitInfo);
+ if (didHit)
+ {
+ #if UNITY_WINRT
+ hitInfo->collider = reinterpret_cast<Collider*> (ScriptingGetObjectReference((Collider*)col));
+ #else
+ hitInfo->collider = reinterpret_cast<Collider*> (col.GetScriptingObject());
+ #endif
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // Casts a [[Ray]] that ignores all Colliders except this one.
+
+ CSRAW public bool Raycast (Ray ray, out RaycastHit hitInfo, float distance)
+ {
+ return Internal_Raycast (this, ray, out hitInfo, distance);
+ }
+
+ // OnTriggerEnter is called when the [[Collider]] /other/ enters the [[class-BoxCollider|trigger]].
+ CSNONE void OnTriggerEnter (Collider other);
+
+ // OnTriggerExit is called when the [[Collider]] /other/ has stopped touching the [[class-BoxCollider|trigger]].
+ CSNONE void OnTriggerExit (Collider other);
+
+ // OnTriggerStay is called ''almost'' all the frames for every [[Collider]] __other__ that is touching the [[class-BoxCollider|trigger]].
+ CSNONE void OnTriggerStay (Collider other);
+
+ // OnCollisionEnter is called when this collider/rigidbody has begun touching another rigidbody/collider.
+
+ CSNONE void OnCollisionEnter (Collision collisionInfo);
+
+ // OnCollisionExit is called when this collider/rigidbody has stopped touching another rigidbody/collider.
+ CSNONE void OnCollisionExit (Collision collisionInfo);
+
+ // OnCollisionStay is called once per frame for every collider/rigidbody that is touching rigidbody/collider.
+ CSNONE void OnCollisionStay (Collision collisionInfo);
+END
+
+// A box-shaped primitive collider.
+CONDITIONAL ENABLE_PHYSICS
+CLASS BoxCollider : Collider
+ // The center of the box, measured in the object's local space.
+ AUTO_PROP Vector3 center GetCenter SetCenter
+
+ // The size of the box, measured in the object's local space.
+ AUTO_PROP Vector3 size GetSize SetSize
+
+ //*undocumented* DEPRECATED
+ OBSOLETE warning use BoxCollider.size instead.
+ CSRAW public Vector3 extents { get { return size * 0.5F; } set { size = value * 2.0F; } }
+END
+
+
+// A sphere-shaped primitive collider.
+CONDITIONAL ENABLE_PHYSICS
+CLASS SphereCollider : Collider
+ // The center of the sphere, measured in the object's local space.
+ AUTO_PROP Vector3 center GetCenter SetCenter
+
+ // The radius of the sphere, measured in the object's local space.
+ AUTO_PROP float radius GetRadius SetRadius
+END
+
+// A mesh collider allows you to do [[wiki:class-MeshCollider|collision detection]] between meshes and primitives.
+CONDITIONAL ENABLE_PHYSICS
+CLASS MeshCollider : Collider
+ //*undocumented* deprecated with version 1.5 (should implement modifiable mesh interface)
+ OBSOLETE warning mesh has been replaced with sharedMesh and will be deprecated
+ CSRAW public Mesh mesh { get { return sharedMesh; } set { sharedMesh = value; } }
+
+ // The mesh object used for collision detection
+ AUTO_PTR_PROP Mesh sharedMesh GetSharedMesh SetSharedMesh
+
+ // Use a convex collider from the mesh.
+ AUTO_PROP bool convex GetConvex SetConvex
+
+ // Uses interpolated normals for sphere collisions instead of flat polygonal normals.
+ AUTO_PROP bool smoothSphereCollisions GetSmoothSphereCollisions SetSmoothSphereCollisions
+
+END
+
+// A capsule-shaped primitive collider.
+CONDITIONAL ENABLE_PHYSICS
+CLASS CapsuleCollider : Collider
+ // The center of the capsule, measured in the object's local space.
+ AUTO_PROP Vector3 center GetCenter SetCenter
+
+ // The radius of the sphere, measured in the object's local space.
+ AUTO_PROP float radius GetRadius SetRadius
+
+ // The height of the capsule meased in the object's local space.
+ AUTO_PROP float height GetHeight SetHeight
+
+ // The direction of the capsule.
+ AUTO_PROP int direction GetDirection SetDirection
+
+END
+
+OBSOLETE warning Use WheelCollider or BoxCollider instead, RaycastCollider is unreliable
+CONDITIONAL ENABLE_PHYSICS
+CLASS RaycastCollider : Collider
+ OBSOLETE warning Use WheelCollider or BoxCollider instead, RaycastCollider is unreliable
+ AUTO_PROP Vector3 center GetCenter SetCenter
+
+ OBSOLETE warning Use WheelCollider or BoxCollider instead, RaycastCollider is unreliable
+ AUTO_PROP float length GetLength SetLength
+END
+
+
+// WheelFrictionCurve is used by the [[WheelCollider]] to describe friction properties of the wheel tire.
+CONDITIONAL ENABLE_PHYSICS
+STRUCT WheelFrictionCurve
+ CSRAW private float m_ExtremumSlip;
+ CSRAW private float m_ExtremumValue;
+ CSRAW private float m_AsymptoteSlip;
+ CSRAW private float m_AsymptoteValue;
+ CSRAW private float m_Stiffness;
+
+ // Extremum point slip (default 1).
+ CSRAW public float extremumSlip { get { return m_ExtremumSlip; } set { m_ExtremumSlip = value; } }
+ // Force at the extremum slip (default 20000).
+ CSRAW public float extremumValue { get { return m_ExtremumValue; } set { m_ExtremumValue = value; } }
+ // Asymptote point slip (default 2).
+ CSRAW public float asymptoteSlip { get { return m_AsymptoteSlip; } set { m_AsymptoteSlip = value; } }
+ // Force at the asymptote slip (default 10000).
+ CSRAW public float asymptoteValue { get { return m_AsymptoteValue; } set { m_AsymptoteValue = value; } }
+ // Multiplier for the ::ref::extremumValue and ::ref::asymptoteValue values (default 1).
+ CSRAW public float stiffness { get { return m_Stiffness; } set { m_Stiffness = value; } }
+END
+
+
+// Contact information for the wheel, reported by [[WheelCollider]].
+CONDITIONAL ENABLE_PHYSICS
+STRUCT WheelHit
+ CSRAW private Vector3 m_Point;
+ CSRAW private Vector3 m_Normal;
+ CSRAW private Vector3 m_ForwardDir;
+ CSRAW private Vector3 m_SidewaysDir;
+ CSRAW private float m_Force;
+ CSRAW private float m_ForwardSlip;
+ CSRAW private float m_SidewaysSlip;
+ #if UNITY_WINRT
+ CSRAW private int m_ColliderHandle;
+ #else
+ CSRAW private Collider m_Collider;
+ #endif
+
+ // The other [[Collider]] the wheel is hitting.
+ CONDITIONAL !UNITY_WINRT
+ CSRAW public Collider collider { get { return m_Collider; } set { m_Collider = value; } }
+
+ CONDITIONAL UNITY_WINRT
+ CSRAW public Collider collider
+ {
+ get
+ {
+ return UnityEngineInternal.ScriptingUtils.GCHandleToObject<Collider>(m_ColliderHandle);
+ }
+ set
+ {
+ m_ColliderHandle = GetColliderHandle(value);
+ }
+ }
+
+ CONDITIONAL UNITY_WINRT
+ CUSTOM private static int GetColliderHandle(object collider)
+ {
+ ScriptingObjectOfType<Collider> col(collider);
+ return ScriptingGetObjectReference(col.GetPtr());
+ }
+
+ // The point of contact between the wheel and the ground.
+ CSRAW public Vector3 point { get { return m_Point; } set { m_Point = value; } }
+ // The normal at the point of contact.
+ CSRAW public Vector3 normal { get { return m_Normal; } set { m_Normal = value; } }
+ // The direction the wheel is pointing in.
+ CSRAW public Vector3 forwardDir { get { return m_ForwardDir; } set { m_ForwardDir = value; } }
+ // The sideways direction of the wheel.
+ CSRAW public Vector3 sidewaysDir { get { return m_SidewaysDir; } set { m_SidewaysDir = value; } }
+
+ // The magnitude of the force being applied for the contact.
+ CSRAW public float force { get { return m_Force; } set { m_Force = value; } }
+
+ // Tire slip in the rolling direction. Acceleration slip is negative, braking slip is positive.
+ CSRAW public float forwardSlip { get { return m_ForwardSlip; } set { m_Force = m_ForwardSlip; } }
+
+ // Tire slip in the sideways direction.
+ CSRAW public float sidewaysSlip { get { return m_SidewaysSlip; } set { m_SidewaysSlip = value; } }
+END
+
+
+
+// A special collider for vehicle wheels.
+CONDITIONAL ENABLE_PHYSICS
+CLASS WheelCollider : Collider
+ // The center of the wheel, measured in the object's local space.
+ AUTO_PROP Vector3 center GetCenter SetCenter
+
+ // The radius of the wheel, measured in local space.
+ AUTO_PROP float radius GetRadius SetRadius
+
+ // Maximum extension distance of wheel suspension, measured in local space.
+ AUTO_PROP float suspensionDistance GetSuspensionDistance SetSuspensionDistance
+
+ // The parameters of wheel's suspension. The suspension attempts to reach a target position
+ AUTO_PROP JointSpring suspensionSpring GetSuspensionSpring SetSuspensionSpring
+
+ // The mass of the wheel. Must be larger than zero.
+ AUTO_PROP float mass GetMass SetMass
+
+ // Properties of tire friction in the direction the wheel is pointing in.
+ AUTO_PROP WheelFrictionCurve forwardFriction GetForwardFriction SetForwardFriction
+
+ // Properties of tire friction in the sideways direction.
+ AUTO_PROP WheelFrictionCurve sidewaysFriction GetSidewaysFriction SetSidewaysFriction
+
+ // Motor torque on the wheel axle. Positive or negative depending on direction.
+ AUTO_PROP float motorTorque GetMotorTorque SetMotorTorque
+
+ // Brake torque. Must be positive.
+ AUTO_PROP float brakeTorque GetBrakeTorque SetBrakeTorque
+
+ // Steering angle in degrees, always around the local y-axis.
+ AUTO_PROP float steerAngle GetSteerAngle SetSteerAngle
+
+
+ // Indicates whether the wheel currently collides with something (RO).
+ AUTO_PROP bool isGrounded IsGrounded
+
+ C++RAW
+
+ struct MonoWheelHit
+ {
+ Vector3f point;
+ Vector3f normal;
+ Vector3f forwardDir;
+ Vector3f sidewaysDir;
+ float force;
+ float forwardSlip;
+ float sidewaysSlip;
+#if UNITY_WINRT
+ int colliderHandle;
+#else
+ ScriptingObjectPtr collider;
+#endif
+ };
+
+
+ // Gets ground collision data for the wheel.
+ CUSTOM public bool GetGroundHit (out WheelHit hit)
+ {
+ WheelHit col;
+ bool didHit = self->GetGroundHit( col );
+ if( didHit )
+ {
+ hit->point = col.point;
+ hit->normal = col.normal;
+ hit->forwardDir = col.forwardDir;
+ hit->sidewaysDir = col.sidewaysDir;
+ hit->force = col.force;
+ hit->forwardSlip = col.forwardSlip;
+ hit->sidewaysSlip = col.sidewaysSlip;
+#if UNITY_WINRT
+ hit->colliderHandle = ScriptingGetObjectReference (col.collider);
+#else
+ hit->collider = Scripting::ScriptingWrapperFor( col.collider );
+#endif
+ return true;
+ }
+ return false;
+ }
+
+ // Current wheel axle rotation speed, in rotations per minute (RO).
+ AUTO_PROP float rpm GetRpm
+END
+
+
+// Structure used to get information back from a raycast.
+CONDITIONAL ENABLE_PHYSICS
+STRUCT RaycastHit
+ CSRAW private Vector3 m_Point;
+ CSRAW private Vector3 m_Normal;
+ CSRAW private int m_FaceID;
+ CSRAW private float m_Distance;
+ CSRAW private Vector2 m_UV;
+ #if UNITY_WINRT
+ CSRAW private int m_ColliderHandle;
+ #else
+ CSRAW private Collider m_Collider;
+ #endif
+
+ // The impact point in world space where the ray hit the collider.
+ CSRAW public Vector3 point { get { return m_Point; } set { m_Point = value; } }
+
+ // The normal of the surface the ray hit.
+ CSRAW public Vector3 normal { get { return m_Normal; } set { m_Normal = value; } }
+
+ // The barycentric coordinate of the triangle we hit.
+ CSRAW public Vector3 barycentricCoordinate { get { return new Vector3 (1.0F - (m_UV.y + m_UV.x), m_UV.x, m_UV.y); } set { m_UV = value; } }
+
+ // The distance from the ray's origin to the impact point.
+ CSRAW public float distance { get { return m_Distance; } set { m_Distance = value; } }
+
+ // The index of the triangle that was hit.
+ CSRAW public int triangleIndex { get { return m_FaceID; } }
+
+ // Workaround for gcc/msvc gcc where passing small mono structures by value does not work
+ CUSTOM private static void CalculateRaycastTexCoord (out Vector2 output, Collider col, Vector2 uv, Vector3 point, int face, int index)
+ {
+ *output = CalculateRaycastTexcoord(col, uv, point, face, index);
+ }
+
+ // The uv texture coordinate at the impact point.
+ CSRAW public Vector2 textureCoord { get { Vector2 coord; CalculateRaycastTexCoord(out coord, collider, m_UV, m_Point, m_FaceID, 0); return coord; } }
+
+ // The secondary uv texture coordinate at the impact point.
+ CSRAW public Vector2 textureCoord2 { get { Vector2 coord; CalculateRaycastTexCoord(out coord, collider, m_UV, m_Point, m_FaceID, 1); return coord; } }
+
+ OBSOLETE warning Use textureCoord2 instead
+ CSRAW public Vector2 textureCoord1 { get { Vector2 coord; CalculateRaycastTexCoord(out coord, collider, m_UV, m_Point, m_FaceID, 1); return coord; } }
+
+ // The uv lightmap coordinate at the impact point.
+ CSRAW public Vector2 lightmapCoord { get {
+ Vector2 coord;
+ CalculateRaycastTexCoord(out coord, collider, m_UV, m_Point, m_FaceID, 1);
+ if( collider.renderer != null )
+ {
+ Vector4 st = collider.renderer.lightmapTilingOffset;
+ coord.x = coord.x * st.x + st.z;
+ coord.y = coord.y * st.y + st.w;
+ }
+ return coord;
+ } }
+
+ // The [[Collider]] that was hit.
+ CONDITIONAL !UNITY_WINRT
+ CSRAW public Collider collider { get { return m_Collider; } }
+
+ CONDITIONAL UNITY_WINRT
+ CSRAW public Collider collider { get { return UnityEngineInternal.ScriptingUtils.GCHandleToObject<Collider>(m_ColliderHandle); } }
+
+ // The [[Rigidbody]] of the collider that was hit. If the collider is not attached to a rigidbody then it is /null/.
+ CSRAW public Rigidbody rigidbody { get { return collider != null ? collider.attachedRigidbody : null; } }
+
+ // The [[Transform]] of the rigidbody or collider that was hit.
+ CSRAW public Transform transform { get {
+ Rigidbody body = rigidbody;
+ if (body != null)
+ return body.transform;
+ else if (collider != null)
+ return collider.transform;
+ else
+ return null;
+ } }
+END
+
+// Describes how physic materials of colliding objects are combined.
+ENUM PhysicMaterialCombine
+ // Averages the friction/bounce of the two colliding materials.
+ Average = 0,
+ // Uses the smaller friction/bounce of the two colliding materials.
+ Minimum = 2,
+ // Multiplies the friction/bounce of the two colliding materials.
+ Multiply = 1,
+ // Uses the larger friction/bounce of the two colliding materials.
+ Maximum = 3
+END
+
+// Physics material describes how to handle colliding objects (friction, bounciness).
+CONDITIONAL ENABLE_PHYSICS
+CLASS PhysicMaterial : Object
+
+ CUSTOM private static void Internal_CreateDynamicsMaterial ([Writable]PhysicMaterial mat, string name)
+ {
+ PhysicMaterial* material = NEW_OBJECT (PhysicMaterial);
+ SmartResetObject(*material);
+ material->SetNameCpp (name);
+ Scripting::ConnectScriptingWrapperToObject (mat.GetScriptingObject(), material);
+ }
+
+ // Creates a new material.
+ CSRAW public PhysicMaterial () { Internal_CreateDynamicsMaterial (this,null); }
+
+ // Creates a new material named /name/.
+ CSRAW public PhysicMaterial (string name){ Internal_CreateDynamicsMaterial (this,name); }
+
+ // The friction used when already moving. This value has to be between 0 and 1.
+ AUTO_PROP float dynamicFriction GetDynamicFriction SetDynamicFriction
+
+ // The friction used when an object is lying on a surface. Usually a value from 0 to 1.
+ AUTO_PROP float staticFriction GetStaticFriction SetStaticFriction
+
+ // How bouncy is the surface? A value of 0 will not bounce. A value of 1 will bounce without any loss of energy.
+ AUTO_PROP float bounciness GetBounciness SetBounciness
+
+ OBSOLETE error Use PhysicMaterial.bounciness instead
+ CSRAW public float bouncyness { get { return bounciness; } set { bounciness = value;} }
+
+ // The direction of anisotropy. Anisotropic friction is enabled if the vector is not zero.
+ AUTO_PROP Vector3 frictionDirection2 GetFrictionDirection2 SetFrictionDirection2
+
+ // If anisotropic friction is enabled, dynamicFriction2 will be applied along frictionDirection2.
+ AUTO_PROP float dynamicFriction2 GetDynamicFriction2 SetDynamicFriction2
+
+ // If anisotropic friction is enabled, staticFriction2 will be applied along frictionDirection2.
+ AUTO_PROP float staticFriction2 GetStaticFriction2 SetStaticFriction2
+
+ // Determines how the friction is combined.
+ AUTO_PROP PhysicMaterialCombine frictionCombine GetFrictionCombine SetFrictionCombine
+
+ // Determines how the bounciness is combined.
+ AUTO_PROP PhysicMaterialCombine bounceCombine GetBounceCombine SetBounceCombine
+
+ // *undocumented* DEPRECATED
+ OBSOLETE warning use PhysicMaterial.frictionDirection2 instead.
+ CSRAW public Vector3 frictionDirection { get { return frictionDirection2; } set { frictionDirection2 = value; }}
+END
+
+// Describes a contact point where the collision occurs.
+CONDITIONAL ENABLE_PHYSICS
+STRUCT ContactPoint
+ CSRAW internal Vector3 m_Point;
+ CSRAW internal Vector3 m_Normal;
+ CSRAW internal Collider m_ThisCollider;
+ CSRAW internal Collider m_OtherCollider;
+
+ // The point of contact.
+ CSRAW public Vector3 point { get { return m_Point; } }
+
+ // Normal of the contact point.
+ CSRAW public Vector3 normal { get { return m_Normal; } }
+
+ // The first collider in contact.
+ CSRAW public Collider thisCollider { get { return m_ThisCollider; } }
+
+ // The other collider in contact.
+ CSRAW public Collider otherCollider { get { return m_OtherCollider; } }
+END
+
+// Describes collision.
+CSRAW [StructLayout (LayoutKind.Sequential)]
+CONDITIONAL ENABLE_PHYSICS
+NONSEALED_CLASS Collision
+ CSRAW internal Vector3 m_RelativeVelocity;
+ CSRAW internal Rigidbody m_Rigidbody;
+ CSRAW internal Collider m_Collider;
+
+ CSRAW internal ContactPoint[] m_Contacts;
+
+ // The relative linear velocity of the two colliding objects (RO).
+ CSRAW public Vector3 relativeVelocity { get { return m_RelativeVelocity; } }
+
+ // The [[Rigidbody]] we hit (RO). This is /null/ if the object we hit is a collider with no rigidbody attached.
+ CSRAW public Rigidbody rigidbody { get { return m_Rigidbody; } }
+
+ // The [[Collider]] we hit (RO).
+ CSRAW
+ CSRAW public Collider collider { get { return m_Collider; } }
+
+ // The [[Transform]] of the object we hit (RO).
+ CSRAW public Transform transform { get { return rigidbody != null ? rigidbody.transform : collider.transform; } }
+
+ // The [[GameObject]] whose collider we are colliding with. (RO).
+ CSRAW public GameObject gameObject { get { return m_Rigidbody != null ? m_Rigidbody.gameObject : m_Collider.gameObject; } }
+
+ // The contact points generated by the physics engine.
+
+ CSRAW public ContactPoint[] contacts { get { return m_Contacts; } }
+
+ //*undocumented*
+ CSRAW public virtual IEnumerator GetEnumerator ()
+ {
+ return contacts.GetEnumerator ();
+ }
+
+ //*undocumented* DEPRECATED
+ OBSOLETE warning use Collision.relativeVelocity instead.
+ CSRAW public Vector3 impactForceSum { get { return relativeVelocity; } }
+ //*undocumented* DEPRECATED
+ OBSOLETE warning will always return zero.
+ CSRAW public Vector3 frictionForceSum { get { return Vector3.zero; } }
+
+ OBSOLETE warning Please use Collision.rigidbody, Collision.transform or Collision.collider instead
+ CSRAW public Component other { get { return m_Rigidbody != null ? (Component)m_Rigidbody : (Component)m_Collider; } }
+END
+
+
+// CollisionFlags is a bitmask returned by CharacterController.Move.
+ENUM CollisionFlags
+ // CollisionFlags is a bitmask returned by CharacterController.Move.
+ None = 0,
+
+ // CollisionFlags is a bitmask returned by CharacterController.Move.
+ Sides = 1,
+
+ // CollisionFlags is a bitmask returned by CharacterController.Move.
+ Above = 2,
+
+ // CollisionFlags is a bitmask returned by CharacterController.Move.
+ Below = 4,
+
+ //*undocumented
+ CollidedSides = 1,
+ //*undocumented
+ CollidedAbove = 2,
+ //*undocumented
+ CollidedBelow = 4
+END
+
+// ControllerColliderHit is used by CharacterController.OnControllerColliderHit to give detailed information about the collision and how to deal with it.
+CSRAW [StructLayout (LayoutKind.Sequential)]
+CONDITIONAL ENABLE_PHYSICS
+CLASS ControllerColliderHit
+ CSRAW internal CharacterController m_Controller;
+ CSRAW internal Collider m_Collider;
+ CSRAW internal Vector3 m_Point;
+ CSRAW internal Vector3 m_Normal;
+ CSRAW internal Vector3 m_MoveDirection;
+ CSRAW internal float m_MoveLength;
+ CSRAW internal int m_Push;
+
+ // The controller that hit the collider
+ CSRAW public CharacterController controller { get { return m_Controller; } }
+
+ // The collider that was hit by the controller
+ CSRAW public Collider collider { get { return m_Collider; } }
+
+ // The rigidbody that was hit by the controller.
+ CSRAW public Rigidbody rigidbody { get { return m_Collider.attachedRigidbody; } }
+
+ // The game object that was hit by the controller.
+ CSRAW public GameObject gameObject { get { return m_Collider.gameObject; } }
+
+ // The transform that was hit by the controller.
+ CSRAW public Transform transform { get { return m_Collider.transform; } }
+
+ // The impact point in world space.
+ CSRAW public Vector3 point { get { return m_Point; } }
+
+ // The normal of the surface we collided with in world space.
+ CSRAW public Vector3 normal { get { return m_Normal; } }
+
+ // Approximately the direction from the center of the capsule to the point we touch.
+ CSRAW public Vector3 moveDirection { get { return m_MoveDirection; } }
+
+ // How far the character has travelled until it hit the collider.
+ CSRAW public float moveLength { get { return m_MoveLength; } }
+
+ //*undocumented NOT IMPLEMENTED
+ CSRAW private bool push { get { return m_Push != 0; } set { m_Push = value ? 1 : 0; } }
+
+
+END
+
+
+// A CharacterController allows you to easily do movement constrained by collisions without having to deal with a rigidbody.
+CONDITIONAL ENABLE_PHYSICS
+CLASS CharacterController : Collider
+
+ // Moves the character with /speed/.
+ AUTO bool SimpleMove (Vector3 speed);
+
+ // A more complex move function taking absolute movement deltas.
+ AUTO CollisionFlags Move (Vector3 motion);
+
+
+ // Was the CharacterController touching the ground during the last move?
+ AUTO_PROP bool isGrounded IsGrounded
+
+ // The current relative velocity of the Character (see notes).
+ AUTO_PROP Vector3 velocity GetVelocity
+
+
+ // What part of the capsule collided with the environment during the last CharacterController.Move call.
+ AUTO_PROP CollisionFlags collisionFlags GetCollisionFlags
+
+ // The radius of the character's capsule
+ AUTO_PROP float radius GetRadius SetRadius
+
+ // The height of the character's capsule
+ AUTO_PROP float height GetHeight SetHeight
+
+ // The center of the character's capsule relative to the transform's position.
+ AUTO_PROP Vector3 center GetCenter SetCenter
+
+ // The character controllers slope limit in degrees
+ AUTO_PROP float slopeLimit GetSlopeLimit SetSlopeLimit
+
+ // The character controllers step offset in meters
+ AUTO_PROP float stepOffset GetStepOffset SetStepOffset
+
+
+ // OnControllerColliderHit is called when the controller hits a collider while performing a Move.
+ CSNONE void OnControllerColliderHit (ControllerColliderHit hit);
+
+
+ // Determines whether other rigidbodies or character controllers collide with this character controller (by default this is always enabled).
+ AUTO_PROP bool detectCollisions GetDetectCollisions SetDetectCollisions
+
+END
+
+CONDITIONAL ENABLE_CLOTH
+// Base class used to simulate cloth physics - shared by both [[InteractiveCloth]] and [[SkinnedCloth]]
+NONSEALED_CLASS Cloth : Component
+
+ // Bending stiffness of the cloth.
+ AUTO_PROP float bendingStiffness GetBendingStiffness SetBendingStiffness
+
+ // Stretching stiffness of the cloth.
+ AUTO_PROP float stretchingStiffness GetStretchingStiffness SetStretchingStiffness
+
+ // Damp cloth motion.
+ AUTO_PROP float damping GetDamping SetDamping
+
+ // The thickness of the cloth surface.
+ AUTO_PROP float thickness GetThickness SetThickness
+
+ // A constant, external acceleration applied to the cloth.
+ AUTO_PROP Vector3 externalAcceleration GetExternalAcceleration SetExternalAcceleration
+
+ // A random, external acceleration applied to the cloth.
+ AUTO_PROP Vector3 randomAcceleration GetRandomAcceleration SetRandomAcceleration
+
+ // Should gravity affect the cloth simulation?
+ AUTO_PROP bool useGravity GetUseGravity SetUseGravity
+
+ // Will the cloth collide with itself?
+ AUTO_PROP bool selfCollision GetSelfCollision SetSelfCollision
+
+ // Is this cloth enabled?
+ AUTO_PROP bool enabled GetEnabled SetEnabled
+
+ // The current vertex positions of the cloth object.
+ CUSTOM_PROP Vector3[] vertices
+ {
+ Vector3f* start = NULL;
+ if (self->GetVertices().size() > 0){
+ start = &self->GetVertices()[0];
+ }
+ return CreateScriptingArray(start, self->GetVertices().size(), GetMonoManager().GetCommonClasses().vector3);
+ }
+
+ // The current normals of the cloth object.
+ CUSTOM_PROP Vector3[] normals
+ {
+ Vector3f* start = NULL;
+ if (self->GetNormals().size() > 0){
+ start = &self->GetNormals()[0];
+ }
+ return CreateScriptingArray(start, self->GetNormals().size(), GetMonoManager().GetCommonClasses().vector3);
+ }
+END
+
+CONDITIONAL ENABLE_CLOTH
+// The InteractiveCloth component is used to simulate objects with cloth physics.
+CLASS InteractiveCloth : Cloth
+
+ // The mesh used as base for the cloth object.
+ AUTO_PTR_PROP Mesh mesh GetMesh SetMesh
+
+ // The friction of the cloth.
+ AUTO_PROP float friction GetFriction SetFriction
+
+ // The density of the cloth.
+ AUTO_PROP float density GetDensity SetDensity
+
+ // The pressure inside the cloth.
+ AUTO_PROP float pressure GetPressure SetPressure
+
+ // How much force will be applied to colliding rigidbodies?
+ AUTO_PROP float collisionResponse GetCollisionResponse SetCollisionResponse
+
+ // How far cloth vertices need to be stretched, before the cloth will tear.
+ AUTO_PROP float tearFactor GetTearFactor SetTearFactor
+
+ // How far attached rigid bodies need to be stretched, before they will tear off.
+ AUTO_PROP float attachmentTearFactor GetAttachmentTearFactor SetAttachmentTearFactor
+
+ // How much force will be applied to attached rigidbodies?
+ AUTO_PROP float attachmentResponse GetAttachmentResponse SetAttachmentResponse
+
+ // Did the cloth tear? (RO)
+ AUTO_PROP bool isTeared GetIsTeared
+
+ // Adds force /force/ to all vertices of the cloth mesh which are with /radius/ distance of /position/.
+ CUSTOM void AddForceAtPosition (Vector3 force, Vector3 position, float radius, ForceMode mode = ForceMode.Force) { self->AddForceAtPosition (force, position, radius, mode); }
+
+ // Attaches a /collider/ to the cloth object.
+ CUSTOM void AttachToCollider (Collider collider, bool tearable = false, bool twoWayInteraction = false) { self->AttachToCollider (collider, tearable, twoWayInteraction); }
+
+ // Detaches a /collider/ from the cloth object.
+ CUSTOM void DetachFromCollider (Collider collider) { self->DetachFromCollider(collider); }
+END
+
+CONDITIONAL ENABLE_CLOTH
+// The ClothSkinningCoefficient struct is used to set up how a [[SkinnedCloth]] component is allowed to move with respect to the [[SkinnedMeshRenderer]] it is attached to.
+STRUCT ClothSkinningCoefficient
+ //Distance a vertex is allowed to travel from the skinned mesh vertex position.
+ CSRAW public float maxDistance;
+ //Distorts the sphere defined by the maxDistance based on skinned mesh normals.
+ CSRAW public float maxDistanceBias;
+ //Definition of a sphere a vertex is not allowed to enter. This allows collision against the animated cloth.
+ CSRAW public float collisionSphereRadius;
+ //Definition of a sphere a vertex is not allowed to enter. This allows collision against the animated cloth.
+ CSRAW public float collisionSphereDistance;
+END
+
+CONDITIONAL ENABLE_CLOTH
+// The SkinnedCloth component works together with the [[SkinnedMeshRenderer]] to simulate clothing on a character.
+CLASS SkinnedCloth : Cloth
+ // The cloth skinning coefficients used to set up how the cloth interacts with the skinned mesh.
+ CUSTOM_PROP ClothSkinningCoefficient[] coefficients
+ {
+#if !UNITY_WINRT
+ return CreateScriptingArray(&self->GetCoefficients()[0], self->GetCoefficients().size(), GetMonoManager().GetBuiltinMonoClass("ClothSkinningCoefficient"));
+#else
+ return SCRIPTING_NULL;
+#endif
+ }
+ {
+#if !UNITY_WINRT
+ int count = mono_array_length_safe_wrapper(value);
+ if (count == self->GetCoefficients().size())
+ self->SetCoefficients(&GetMonoArrayElement<SkinnedCloth::ClothConstrainCoefficients> (value, 0));
+ else
+ ErrorString ("Number of coefficients must match number of vertices!");
+#endif
+ }
+
+ // How much world-space movement of the character will affect cloth vertices.
+ AUTO_PROP float worldVelocityScale GetWorldVelocityScale SetWorldVelocityScale
+
+ // How much world-space acceleration of the character will affect cloth vertices.
+ AUTO_PROP float worldAccelerationScale GetWorldAccelerationScale SetWorldAccelerationScale
+
+ // Fade the cloth simulation in or out, and enabled or disable the SkinnedCloth.
+ CUSTOM void SetEnabledFading (bool enabled, float interpolationTime = 0.5f) { self->SetEnabledFading (enabled, interpolationTime); }
+END
+
+CONDITIONAL ENABLE_CLOTH
+// The ClothRenderer component is used together with the [[InteractiveCloth]] component, to visualize a cloth object in the scene.
+CLASS ClothRenderer : Renderer
+ // Pause the cloth simulation, when the ClothRenderer is not currently visible.
+ AUTO_PROP bool pauseWhenNotVisible GetPauseWhenNotVisible SetPauseWhenNotVisible
+END
+
+// A heightmap based collider.
+CONDITIONAL ENABLE_TERRAIN && ENABLE_PHYSICS
+CLASS TerrainCollider : Collider
+
+ // The terrain that stores the heightmap
+ AUTO_PTR_PROP TerrainData terrainData GetTerrainData SetTerrainData
+
+END
+
+CSRAW
+}
+#endif
diff --git a/Runtime/Dynamics/SkinnedCloth.cpp b/Runtime/Dynamics/SkinnedCloth.cpp
new file mode 100644
index 0000000..65570c5
--- /dev/null
+++ b/Runtime/Dynamics/SkinnedCloth.cpp
@@ -0,0 +1,347 @@
+#include "UnityPrefix.h"
+#include "SkinnedCloth.h"
+
+#if ENABLE_CLOTH
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Filters/Mesh/MeshSkinning.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "Runtime/Input/TimeManager.h"
+#include "PhysicsManager.h"
+#include "Runtime/Filters/Deformation/SkinnedMeshFilter.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Math/Random/Random.h"
+
+extern Rand gClothRand;
+
+namespace Unity
+{
+
+SkinnedCloth::SkinnedCloth (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode),
+ m_UpdateNode(this)
+{
+ m_Fade = 0.0f;
+ m_TargetFade = 1.0f;
+ m_NeedsToReadVertices = true;
+ m_InterpolationTime = 0.1f;
+ m_WorldVelocityScale = 0.5f;
+ m_WorldAccelerationScale = 1.0f;
+}
+
+SkinnedCloth::~SkinnedCloth ()
+{
+}
+
+void SkinnedCloth::Reset ()
+{
+ Super::Reset();
+
+ m_WorldVelocityScale = 0.5f;
+ m_WorldAccelerationScale = 1.0f;
+}
+
+#if ENABLE_CLOTH
+
+void SkinnedCloth::Create()
+{
+ Cleanup ();
+
+ SkinnedMeshRenderer *smr = QueryComponent(SkinnedMeshRenderer);
+ Assert (smr != NULL);
+ if (smr->GetMesh() == NULL)
+ return;
+
+ m_LastFrameWorldPosition = smr->GetActualRootBone().GetPosition();
+ m_LastFrameVelocity = Vector3f::zero;
+
+ smr->SetCloth (this);
+ m_Mesh = smr->GetMesh();
+
+#if UNITY_PS3
+ //-- we need to unify the streams for skinned cloth
+ int streamCount = 0;
+ VertexData& oldVertexData = m_Mesh->GetVertexData();
+ if (oldVertexData.GetActiveStreamCount() > 2)
+ {
+ VertexStreamsLayout kHotColdSplit = {{ kShaderChannelsHot, kShaderChannelsCold, 0, 0 }};
+ VertexData newVertexData(oldVertexData, m_Mesh->GetAvailableChannels(), kHotColdSplit);
+ swap (newVertexData, oldVertexData);
+ }
+#endif
+
+ SetupMeshData (false, true, 1);
+
+ NxClothDesc clothDesc;
+ SetupClothDesc (clothDesc, false);
+ clothDesc.flags |= NX_CLF_DISABLE_COLLISION;
+
+ m_ClothScene = GetPhysicsManager ().GetClothingScene();
+ m_Cloth = m_ClothScene->createCloth(clothDesc);
+
+ if (m_Cloth == NULL)
+ GetDynamicsSDK().releaseClothMesh(*clothDesc.clothMesh);
+
+ SetupCoefficients ();
+
+ m_VertexBuffer = NULL;
+ m_NormalBuffer = NULL;
+ m_TangentBuffer = NULL;
+ m_VertexBufferStride = 0;
+}
+
+void SkinnedCloth::Cleanup ()
+{
+ Super::Cleanup ();
+
+ if (GetGameObjectPtr() != NULL)
+ {
+ SkinnedMeshRenderer *smr = QueryComponent(SkinnedMeshRenderer);
+ if (smr != NULL)
+ smr->SetCloth (NULL);
+ }
+}
+
+void SkinnedCloth::SetEnabled (bool enabled) {
+
+ if (enabled != GetEnabled())
+ {
+ Super::SetEnabled (enabled);
+ if (enabled)
+ {
+ m_Fade = 0.0f;
+ m_TargetFade = 1.0f;
+ m_InterpolationTime = 0.1f;
+ m_NeedsToReadVertices = true;
+ SkinnedMeshRenderer *smr = QueryComponent(SkinnedMeshRenderer);
+ smr->SetCloth (this);
+ m_LastFrameWorldPosition = smr->GetActualRootBone().GetPosition();
+ m_LastFrameVelocity = Vector3f::zero;
+ }
+ else
+ GetComponent(SkinnedMeshRenderer).SetCloth (NULL);
+ }
+}
+
+void SkinnedCloth::SetEnabledFading (bool enabled, float interpolationTime)
+{
+ if (enabled && !GetEnabled())
+ SetEnabled(true);
+ m_TargetFade = enabled ? 1.0f : 0.0f;
+ m_InterpolationTime = interpolationTime;
+}
+
+void SkinnedCloth::LateUpdate ()
+{
+ if (m_Cloth)
+ {
+ // calculate forces from world space movement
+ Vector3f externalAcceleration = m_ExternalAcceleration+RandomPointInsideCube (gClothRand, m_RandomAcceleration);
+ SkinnedMeshRenderer *smr = QueryComponent(SkinnedMeshRenderer);
+ Vector3f worldPosition = smr->GetActualRootBone().GetPosition();
+ Vector3f velocity = (m_LastFrameWorldPosition - worldPosition) * GetInvDeltaTime();
+ // Use squared velocity, as air friction is quadratic.
+ externalAcceleration += velocity * Magnitude(velocity) * m_WorldVelocityScale;
+ Vector3f acceleration = (m_LastFrameVelocity - velocity) * GetInvDeltaTime();
+ externalAcceleration -= acceleration * m_WorldAccelerationScale;
+
+ m_LastFrameWorldPosition = worldPosition;
+ m_LastFrameVelocity = velocity;
+ m_Cloth->setExternalAcceleration((const NxVec3&)externalAcceleration);
+ }
+
+ if (m_Fade != m_TargetFade)
+ {
+ if (m_InterpolationTime == 0.0f)
+ m_Fade = m_TargetFade;
+ else if (m_Fade > m_TargetFade)
+ {
+ m_Fade -= GetDeltaTime() / m_InterpolationTime;
+ if (m_Fade < m_TargetFade)
+ m_Fade = m_TargetFade;
+ }
+ else if (m_Fade < m_TargetFade)
+ {
+ m_Fade += GetDeltaTime() / m_InterpolationTime;
+ if (m_Fade > m_TargetFade)
+ m_Fade = m_TargetFade;
+ }
+ SetupCoefficients ();
+ if (m_Fade == 0.0f && m_TargetFade == 0.0f)
+ SetEnabled (false);
+ }
+}
+
+void SkinnedCloth::AddToManager ()
+{
+ GetLateBehaviourManager().AddBehaviour (m_UpdateNode, -1);
+}
+
+void SkinnedCloth::RemoveFromManager ()
+{
+ GetLateBehaviourManager().RemoveBehaviour (m_UpdateNode);
+}
+
+void SkinnedCloth::SetupCoefficients ()
+{
+ m_Coefficients.resize (m_NumVertices);
+ if (m_Cloth)
+ {
+ if (m_Fade == 1.0)
+ m_Cloth->setConstrainCoefficients ((NxClothConstrainCoefficients*)&m_Coefficients[0]);
+ else
+ {
+ std::vector<ClothConstrainCoefficients> coefficients = m_Coefficients;
+ for (std::vector<ClothConstrainCoefficients>::iterator i = coefficients.begin(); i != coefficients.end(); i++)
+ i->maxDistance *= m_Fade;
+ m_Cloth->setConstrainCoefficients ((NxClothConstrainCoefficients*)&coefficients[0]);
+ }
+ }
+}
+
+PROFILER_INFORMATION(gSetUpProfile, "SkinnedCloth.SetUpSkinnedBuffers", kProfilerPhysics)
+
+void SkinnedCloth::SetUpSkinnedBuffers (void *vertices, void *normals, void *tangents, size_t bufferStride)
+{
+ PROFILER_AUTO(gSetUpProfile, NULL)
+
+ // If the mesh has changed, re-created skinning buffers to match the changed mesh
+ SkinnedMeshRenderer *smr = QueryComponent(SkinnedMeshRenderer);
+ if (smr->GetMesh() != m_Mesh || m_Mesh->GetVertexCount() != m_VertexTranslationTable.size())
+ Create();
+
+ QuaternionToMatrix(Inverse(smr->GetActualRootBone().GetRotation()), m_WorldToLocalRotationMatrix);
+
+ int numTotalVertices = m_VertexTranslationTable.size();
+ if (normals)
+ {
+ for (int i=0; i<numTotalVertices; i++)
+ {
+ int index = m_VertexTranslationTable[i];
+ m_Vertices[index] = *(Vector3f*)((char*)vertices+i*bufferStride);
+ m_Normals[index] = *(Vector3f*)((char*)normals+i*bufferStride);
+ }
+ }
+ else
+ {
+ for (int i=0; i<numTotalVertices; i++)
+ {
+ int index = m_VertexTranslationTable[i];
+ m_Vertices[index] = *(Vector3f*)((char*)vertices+i*bufferStride);
+ }
+ }
+
+ // If the component has just been enabled, read vertices for the first frame,
+ // so we don't suddenly jerk into place.
+ if (m_NeedsToReadVertices)
+ {
+ m_NeedsToReadVertices = false;
+ m_Cloth->setPositions (&m_Vertices[0], sizeof(Vector3f));
+ }
+
+ m_Cloth->setConstrainPositions (&m_Vertices[0], sizeof(Vector3f));
+ if (normals)
+ m_Cloth->setConstrainNormals (&m_Normals[0], sizeof(Vector3f));
+
+ m_VertexBuffer = vertices;
+ m_NormalBuffer = normals;
+ m_TangentBuffer = tangents;
+ m_VertexBufferStride = bufferStride;
+}
+
+void SkinnedCloth::ReadBackSkinnedBuffers ()
+{
+ if (m_VertexBuffer != NULL)
+ {
+ TransformPoints3x3 (m_WorldToLocalRotationMatrix, &m_Vertices[0], &m_Vertices[0], m_NumVertices);
+ TransformPoints3x3 (m_WorldToLocalRotationMatrix, &m_Normals[0], &m_Normals[0], m_NumVertices);
+
+ int numTotalVertices = m_VertexTranslationTable.size();
+ if (m_NormalBuffer)
+ {
+ for (int i=0; i<numTotalVertices; i++)
+ {
+ int index = m_VertexTranslationTable[i];
+ *(Vector3f*)((char*)m_VertexBuffer+i*m_VertexBufferStride) = m_Vertices[index];
+ *(Vector3f*)((char*)m_NormalBuffer+i*m_VertexBufferStride) = m_Normals[index];
+ }
+ }
+ else
+ {
+ for (int i=0; i<numTotalVertices; i++)
+ {
+ int index = m_VertexTranslationTable[i];
+ *(Vector3f*)((char*)m_VertexBuffer+i*m_VertexBufferStride) = m_Vertices[index];
+ }
+ }
+
+ if (m_TangentBuffer != NULL)
+ {
+ // PhysX cloth will not output tangents. But we should at least
+ // transform them so things don't look completely wrong.
+ for (int i=0; i<numTotalVertices; i++)
+ {
+ Vector3f& v = *(Vector3f*)((char*)m_TangentBuffer+i*m_VertexBufferStride);
+ v = m_WorldToLocalRotationMatrix.MultiplyPoint3 (v);
+ }
+ }
+ }
+}
+
+void SkinnedCloth::SetCoefficients(ClothConstrainCoefficients *coefficients)
+{
+ memcpy (&m_Coefficients[0], coefficients, sizeof(SkinnedCloth::ClothConstrainCoefficients) * m_Coefficients.size());
+ SetupCoefficients();
+ SetDirty();
+}
+
+#else //ENABLE_CLOTH
+
+void SkinnedCloth::Cleanup () {}
+void SkinnedCloth::Create() {}
+void SkinnedCloth::LateUpdate() {}
+void SkinnedCloth::SetEnabled (bool) {}
+void SkinnedCloth::SetEnabledFading (bool enabled, float interpolationTime) {}
+void SkinnedCloth::AddToManager () {}
+void SkinnedCloth::RemoveFromManager () {}
+void SkinnedCloth::SetCoefficients(ClothConstrainCoefficients *coefficients) {}
+#endif //ENABLE_CLOTH
+
+void SkinnedCloth::SetWorldVelocityScale (float value)
+{
+ m_WorldVelocityScale = value;
+ SetDirty();
+}
+
+void SkinnedCloth::SetWorldAccelerationScale (float value)
+{
+ m_WorldAccelerationScale = value;
+ SetDirty();
+}
+
+template<class TransferFunction>
+void SkinnedCloth::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER (m_WorldVelocityScale);
+ TRANSFER (m_WorldAccelerationScale);
+ transfer.Transfer (m_Coefficients, "m_Coefficients", kHideInEditorMask);
+}
+
+template<class TransferFunction>
+void SkinnedCloth::ClothConstrainCoefficients::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (maxDistance);
+ TRANSFER (maxDistanceBias);
+ TRANSFER (collisionSphereRadius);
+ TRANSFER (collisionSphereDistance);
+}
+
+}
+
+IMPLEMENT_CLASS (SkinnedCloth)
+IMPLEMENT_OBJECT_SERIALIZE (SkinnedCloth)
+
+#endif // ENABLE_CLOTH
diff --git a/Runtime/Dynamics/SkinnedCloth.h b/Runtime/Dynamics/SkinnedCloth.h
new file mode 100644
index 0000000..5f5a72f
--- /dev/null
+++ b/Runtime/Dynamics/SkinnedCloth.h
@@ -0,0 +1,82 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_CLOTH
+
+#include "DeformableMesh.h"
+#include "Runtime/Math/Matrix3x3.h"
+
+namespace Unity
+{
+
+
+class SkinnedCloth : public Cloth
+{
+public:
+ REGISTER_DERIVED_CLASS (SkinnedCloth, Cloth)
+ DECLARE_OBJECT_SERIALIZE (SkinnedCloth)
+
+ SkinnedCloth (MemLabelId label, ObjectCreationMode mode);
+
+ void SetUpSkinnedBuffers (void *vertices, void *normals, void *tangents, size_t bufferStride);
+ void ReadBackSkinnedBuffers ();
+
+ virtual void Reset ();
+ virtual void SetEnabled (bool enabled);
+ virtual void LateUpdate ();
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+
+ struct ClothConstrainCoefficients {
+ float maxDistance;
+ float maxDistanceBias;
+ float collisionSphereRadius;
+ float collisionSphereDistance;
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (ClothConstrainCoefficients)
+
+ ClothConstrainCoefficients () : maxDistance(0.05f), maxDistanceBias(0), collisionSphereRadius(0.5f), collisionSphereDistance(0) {}
+ };
+
+ std::vector<ClothConstrainCoefficients> &GetCoefficients() {return m_Coefficients;}
+ void SetCoefficients(ClothConstrainCoefficients *coefficients);
+
+ float GetWorldVelocityScale () const { return m_WorldVelocityScale; }
+ void SetWorldVelocityScale (float value);
+
+ float GetWorldAccelerationScale () const { return m_WorldAccelerationScale; }
+ void SetWorldAccelerationScale (float value);
+
+ void SetEnabledFading (bool enabled, float interpolationTime);
+
+protected:
+
+ virtual void Create ();
+ virtual void Cleanup ();
+
+ void SetupCoefficients();
+
+ float m_Fade;
+ float m_TargetFade;
+ float m_InterpolationTime;
+
+ bool m_NeedsToReadVertices;
+ void *m_VertexBuffer;
+ void *m_NormalBuffer;
+ void *m_TangentBuffer;
+ Matrix3x3f m_WorldToLocalRotationMatrix;
+ size_t m_VertexBufferStride;
+
+ Vector3f m_LastFrameWorldPosition;
+ Vector3f m_LastFrameVelocity;
+
+ float m_WorldVelocityScale; ///<How much world-space movement of the character will affect cloth vertices.
+ float m_WorldAccelerationScale; ///<How much world-space acceleration of the character will affect cloth vertices.
+
+ BehaviourListNode m_UpdateNode;
+
+ std::vector<ClothConstrainCoefficients> m_Coefficients;
+};
+
+}
+
+#endif // ENABLE_CLOTH
diff --git a/Runtime/Dynamics/SphereCollider.cpp b/Runtime/Dynamics/SphereCollider.cpp
new file mode 100644
index 0000000..9bd05cb
--- /dev/null
+++ b/Runtime/Dynamics/SphereCollider.cpp
@@ -0,0 +1,272 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "SphereCollider.h"
+#include "Runtime/Graphics/Transform.h"
+#include "RigidBody.h"
+#include "PhysicsManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+#define GET_SHAPE() ((class NxSphereShape*)m_Shape)
+
+using namespace std;
+
+SphereCollider::SphereCollider (MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode)
+{
+ #if UNITY_EDITOR
+ fixupSphereColliderBackwardsCompatibility = false;
+ #endif
+}
+
+SphereCollider::~SphereCollider ()
+{
+}
+
+void SphereCollider::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ if (m_Shape)
+ {
+ // Apply changed values
+ SetRadius (m_Radius);
+ SetCenter (m_Center);
+ }
+
+ Super::AwakeFromLoad (awakeMode);
+
+ #if UNITY_EDITOR
+ if (fixupSphereColliderBackwardsCompatibility)
+ {
+ float oldRadius = m_Radius;
+ m_Radius = 1.0F;
+ if (GetScaledRadius () > 0.001)
+ {
+ SetRadius (oldRadius / GetScaledRadius ());
+ }
+ fixupSphereColliderBackwardsCompatibility = false;
+ }
+ #endif
+}
+
+void SphereCollider::SmartReset ()
+{
+ Super::Reset ();
+ AABB aabb;
+ if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb))
+ {
+ Vector3f dist = aabb.GetExtent ();
+ float longestAxis = max (dist.x, dist.y);
+ longestAxis = max (longestAxis, dist.z);
+ SetRadius (longestAxis);
+ SetCenter (aabb.GetCenter ());
+ }
+ else
+ {
+ SetRadius (0.5F);
+ SetCenter (Vector3f::zero);
+ }
+}
+
+void SphereCollider::Reset ()
+{
+ Super::Reset ();
+ m_Radius = 0.5F;
+ m_Center = Vector3f::zero;
+}
+
+float SphereCollider::GetScaledRadius () const
+{
+ Vector3f scale = GetComponent (Transform).GetWorldScaleLossy ();
+ float absoluteRadius = max (max (Abs (scale.x), Abs (scale.y)), Abs (scale.z)) * m_Radius;
+ absoluteRadius = Abs (absoluteRadius);
+ absoluteRadius = max (absoluteRadius, 0.00001F);
+ return absoluteRadius;
+}
+
+void SphereCollider::SetRadius (float radius)
+{
+ if (m_Radius != radius)
+ {
+ SetDirty ();
+ m_Radius = radius;
+ }
+
+ PROFILE_MODIFY_STATIC_COLLIDER
+
+ if (GET_SHAPE ())
+ {
+ GET_SHAPE ()->setRadius (GetScaledRadius ());
+ RigidbodyMassDistributionChanged ();
+ RefreshPhysicsInEditMode();
+ UpdateCCDSkeleton ();
+ }
+}
+
+
+void SphereCollider::SetCenter (const Vector3f& center)
+{
+ if (m_Center != center)
+ {
+ m_Center = center;
+ SetDirty ();
+ }
+
+ if (GET_SHAPE ())
+ TransformChanged (Transform::kRotationChanged | Transform::kPositionChanged | kForceUpdateMass);
+}
+
+Vector3f SphereCollider::GetGlobalCenter () const
+{
+ return GetComponent (Transform).TransformPoint (m_Center);
+}
+
+void SphereCollider::Create (const Rigidbody* ignoreRigidbody)
+{
+ if (m_Shape)
+ Cleanup ();
+
+ NxSphereShapeDesc shapeDesc;
+ shapeDesc.radius = GetScaledRadius ();
+
+ FinalizeCreate (shapeDesc, true, ignoreRigidbody);
+}
+
+void SphereCollider::FetchPoseFromTransform ()
+{
+ FetchPoseFromTransformUtility (m_Center);
+}
+
+bool SphereCollider::GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix)
+{
+ return GetRelativeToParentPositionAndRotationUtility (transform, anyParent, m_Center, matrix);
+}
+
+void SphereCollider::ScaleChanged ()
+{
+ GET_SHAPE ()->setRadius (GetScaledRadius ());
+ UpdateCCDSkeleton ();
+}
+
+void SphereCollider::TransformChanged (int changeMask)
+{
+ Super::TransformChanged (changeMask);
+ if (m_Shape)
+ {
+ if (!m_Shape->getActor ().userData)
+ {
+ PROFILER_AUTO(gStaticColliderMove, this)
+ FetchPoseFromTransformUtility (m_Center);
+ }
+ else
+ {
+ Rigidbody* body = (Rigidbody*)m_Shape->getActor ().userData;
+ Matrix4x4f matrix;
+ if (GetRelativeToParentPositionAndRotationUtility (GetComponent (Transform), body->GetComponent (Transform), m_Center, matrix))
+ {
+ NxMat34 shapeMatrix;
+ shapeMatrix.setColumnMajor44 (matrix.GetPtr ());
+ m_Shape->setLocalPose (shapeMatrix);
+ }
+
+ if (body->GetGameObjectPtr() != GetGameObjectPtr() || changeMask & (Transform::kScaleChanged | kForceUpdateMass))
+ RigidbodyMassDistributionChanged ();
+ }
+
+ if (changeMask & Transform::kScaleChanged)
+ ScaleChanged ();
+
+ RefreshPhysicsInEditMode();
+ }
+}
+
+AABB SphereCollider::GetBounds ()
+{
+ if (m_Shape)
+ {
+ // AABB reported by PhysX is inaccurate, as PhysX will just transform the local AABB.
+ // For Spheres it's very easy to do better.
+ float globalRadius = GetScaledRadius();
+ return AABB(GetGlobalCenter(), Vector3f(globalRadius, globalRadius, globalRadius));
+ }
+ else
+ return Super::GetBounds ();
+}
+
+NxCCDSkeleton* SphereCollider::CreateCCDSkeleton(float scale)
+{
+ // This is a very simple "approximation" of a sphere, of only 6 vertices.
+ // Since CCD only kicks in when normal collisions fail, any is probably mostly
+ // interesting for small objects (as those tend to move faster), I believe that this
+ // is a reasonable choice for common expected use. NVidia even recommends using
+ // single vertex meshes for very small objects.
+ float radius = scale;
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ radius *= GetScaledRadius();
+ else
+ // Prior to 4.0 we incorrectly ignored object scale here. Keep this behaviour for backwards compatibility.
+ radius *= m_Radius;
+
+ NxU32 triangles[3 * 8] = {
+ 0,1,2,
+ 0,2,3,
+ 0,3,4,
+ 0,4,1,
+ 5,2,1,
+ 5,3,2,
+ 5,4,3,
+ 5,1,4,
+ };
+
+ NxVec3 points[6];
+
+ // Static mesh
+ points[0].set( 0, -radius, 0);
+ points[1].set( -radius, 0, 0);
+ points[2].set( 0, 0, -radius);
+ points[3].set( radius, 0, 0);
+ points[4].set( 0, 0, radius);
+ points[5].set( 0, radius, 0);
+
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ {
+ // This is wrong, as the m_Center transformation is already applied at the shape matrix.
+ // Keep for backward compatibility.
+ NxVec3 center(m_Center.x, m_Center.y, m_Center.z);
+ for (int i=0;i<6;i++)
+ points[i] += center;
+ }
+
+ NxSimpleTriangleMesh stm;
+ stm.numVertices = 6;
+ stm.numTriangles = 8;
+ stm.pointStrideBytes = sizeof(NxVec3);
+ stm.triangleStrideBytes = sizeof(NxU32)*3;
+
+ stm.points = points;
+ stm.triangles = triangles;
+ return GetDynamicsSDK().createCCDSkeleton(stm);
+}
+
+template<class TransferFunction>
+void SphereCollider::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion (2);
+
+ transfer.Align();
+
+ #if UNITY_EDITOR
+ fixupSphereColliderBackwardsCompatibility = transfer.IsOldVersion (1);
+ #endif
+
+ TRANSFER_SIMPLE (m_Radius);
+ TRANSFER (m_Center);
+}
+
+IMPLEMENT_CLASS (SphereCollider)
+IMPLEMENT_OBJECT_SERIALIZE (SphereCollider)
+
+#undef GET_SHAPE
+#endif //ENABLE_PHYSICS \ No newline at end of file
diff --git a/Runtime/Dynamics/SphereCollider.h b/Runtime/Dynamics/SphereCollider.h
new file mode 100644
index 0000000..1a3f779
--- /dev/null
+++ b/Runtime/Dynamics/SphereCollider.h
@@ -0,0 +1,56 @@
+#ifndef SPHERECOLLIDER_H
+#define SPHERECOLLIDER_H
+
+#include "Collider.h"
+#include "Runtime/Math/Vector3.h"
+
+
+class SphereCollider : public Collider
+{
+ public:
+ REGISTER_DERIVED_CLASS (SphereCollider, Collider)
+ DECLARE_OBJECT_SERIALIZE (SphereCollider)
+
+ SphereCollider (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void Reset ();
+ virtual void SmartReset ();
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ void SetRadius (float r);
+ float GetRadius () const { return m_Radius; }
+
+ float GetScaledRadius () const;
+
+ void SetCenter (const Vector3f& center);
+ Vector3f GetCenter () { return m_Center; }
+
+ Vector3f GetGlobalCenter () const;
+
+ void TransformChanged (int changeMask);
+
+ virtual AABB GetBounds ();
+
+ protected:
+
+ virtual void Create (const Rigidbody* ignoreAttachRigidbody);
+ virtual void FetchPoseFromTransform ();
+ virtual bool GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix);
+
+ virtual NxCCDSkeleton* CreateCCDSkeleton(float scale);
+
+ void ScaleChanged ();
+
+ /// The radius of the sphere. range { 0.00001, infinity }
+ float m_Radius;
+ Vector3f m_Center;
+
+ #if UNITY_EDITOR
+ /// In unity version 1.0 sphere radius did not change with scale.
+ /// This was fixed with version 1.1
+ /// In the transfer function we check if we should up to account for the now introduced scale.
+ bool fixupSphereColliderBackwardsCompatibility;
+ #endif
+};
+
+#endif
diff --git a/Runtime/Dynamics/SpringJoint.cpp b/Runtime/Dynamics/SpringJoint.cpp
new file mode 100644
index 0000000..440d8f8
--- /dev/null
+++ b/Runtime/Dynamics/SpringJoint.cpp
@@ -0,0 +1,161 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "SpringJoint.h"
+#include "PhysicsManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+
+using namespace std;
+
+namespace Unity
+{
+
+#define GET_JOINT() static_cast<NxDistanceJoint*> (m_Joint)
+
+/*
+- We awake the hingejoint only once. (AwakeFromLoad)
+ At this point we setup the axes. They are never changed afterwards
+ -> The perfect solution remembers the old position/rotation of the rigid bodies.
+ Then when changing axis/anchor is changed it generates axes that are rleative to the old position/rotation state!
+*/
+
+SpringJoint::SpringJoint (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+SpringJoint::~SpringJoint ()
+{
+}
+
+void SpringJoint::Reset()
+{
+ Super::Reset();
+ m_MinDistance = 0.0F;
+ m_MaxDistance = 0.5F;
+ m_Spring = 2;
+ m_Damper = 0.2f;
+}
+
+void SpringJoint::Create ()
+{
+ AssertIf (!IsActive ());
+
+ NxDistanceJointDesc desc;
+
+ if (m_Joint && m_Joint->getState () == NX_JS_SIMULATING)
+ GET_JOINT()->saveToDesc (desc);
+
+ desc.spring.spring = m_Spring;
+ desc.spring.damper = m_Damper;
+
+ if (desc.minDistance < m_MaxDistance)
+ {
+ desc.minDistance = m_MinDistance;
+ desc.maxDistance = m_MaxDistance;
+ }
+ else
+ {
+ desc.minDistance = m_MaxDistance;
+ desc.maxDistance = m_MinDistance;
+ }
+
+ desc.flags |= NX_DJF_SPRING_ENABLED | NX_DJF_MAX_DISTANCE_ENABLED | NX_DJF_MIN_DISTANCE_ENABLED;
+
+ FINALIZE_CREATE (desc, NxDistanceJointDesc);
+}
+void SpringJoint::SetSpring (float spring)
+{
+ m_Spring = spring;
+ SetDirty();
+
+ if (GET_JOINT() != NULL)
+ {
+ NxDistanceJointDesc desc;
+ GET_JOINT()->saveToDesc (desc);
+ desc.spring.spring = m_Spring;
+ GET_JOINT()->loadFromDesc (desc);
+ }
+}
+
+void SpringJoint::SetDamper(float damper)
+{
+ m_Damper = damper;
+ SetDirty();
+
+ if (GET_JOINT() != NULL)
+ {
+ NxDistanceJointDesc desc;
+ GET_JOINT()->saveToDesc (desc);
+ desc.spring.damper = m_Damper;
+ GET_JOINT()->loadFromDesc (desc);
+ }
+}
+
+void SpringJoint::SetMinDistance(float distance)
+{
+ m_MinDistance = distance;
+ SetDirty();
+
+ if (GET_JOINT() != NULL)
+ {
+ NxDistanceJointDesc desc;
+ GET_JOINT()->saveToDesc (desc);
+ if (desc.minDistance < m_MaxDistance)
+ {
+ desc.minDistance = m_MinDistance;
+ desc.maxDistance = m_MaxDistance;
+ }
+ else
+ {
+ desc.minDistance = m_MaxDistance;
+ desc.maxDistance = m_MinDistance;
+ }
+ GET_JOINT()->loadFromDesc (desc);
+ }
+}
+
+void SpringJoint::SetMaxDistance(float distance)
+{
+ m_MaxDistance = distance;
+ SetDirty();
+
+ if (GET_JOINT() != NULL)
+ {
+ NxDistanceJointDesc desc;
+ GET_JOINT()->saveToDesc (desc);
+ if (desc.minDistance < m_MaxDistance)
+ {
+ desc.minDistance = m_MinDistance;
+ desc.maxDistance = m_MaxDistance;
+ }
+ else
+ {
+ desc.minDistance = m_MaxDistance;
+ desc.maxDistance = m_MinDistance;
+ }
+ GET_JOINT()->loadFromDesc (desc);
+ }
+}
+
+IMPLEMENT_AXIS_ANCHOR(SpringJoint,NxDistanceJointDesc)
+
+template<class TransferFunction>
+void SpringJoint::Transfer (TransferFunction& transfer)
+{
+ JointTransferPreNoAxis(transfer);
+ TRANSFER_SIMPLE (m_Spring);
+ TRANSFER_SIMPLE (m_Damper);
+ TRANSFER_SIMPLE (m_MinDistance);
+ TRANSFER_SIMPLE (m_MaxDistance);
+ JointTransferPost (transfer);
+}
+
+#undef GET_JOINT
+
+}
+
+IMPLEMENT_CLASS (SpringJoint)
+IMPLEMENT_OBJECT_SERIALIZE (SpringJoint)
+
+#endif //ENABLE_PHYSICS \ No newline at end of file
diff --git a/Runtime/Dynamics/SpringJoint.h b/Runtime/Dynamics/SpringJoint.h
new file mode 100644
index 0000000..99288c9
--- /dev/null
+++ b/Runtime/Dynamics/SpringJoint.h
@@ -0,0 +1,51 @@
+#ifndef SPRINGJOINT_H
+#define SPRINGJOINT_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Math/Vector3.h"
+#include "JointDescriptions.h"
+#include "Joint.h"
+class Rigidbody;
+class NxJointDesc;
+
+namespace Unity
+{
+
+
+class SpringJoint : public Joint
+{
+ public:
+
+ REGISTER_DERIVED_CLASS (SpringJoint, Joint)
+ DECLARE_OBJECT_SERIALIZE (SpringJoint)
+
+ SpringJoint (MemLabelId label, ObjectCreationMode mode);
+ void Reset();
+
+ float GetSpring () const { return m_Spring; }
+ void SetSpring (float spring);
+
+ float GetDamper () { return m_Damper; }
+ void SetDamper(float damper);
+
+ float GetMinDistance() { return m_MinDistance; }
+ void SetMinDistance(float distance);
+
+ float GetMaxDistance() { return m_MaxDistance; }
+ void SetMaxDistance(float distance);
+
+ private:
+
+ virtual void ApplySetupAxesToDesc (int option);
+ virtual void Create ();
+
+ float m_MinDistance; ///< range { 0, infinity }
+ float m_MaxDistance; ///< range { 0, infinity }
+ float m_Spring; ///< range { 0, infinity }
+ float m_Damper; ///< range { 0, infinity }
+};
+
+}
+
+#endif // SPRINGJOINT_H
+
diff --git a/Runtime/Dynamics/TerrainCollider.cpp b/Runtime/Dynamics/TerrainCollider.cpp
new file mode 100644
index 0000000..67a82a0
--- /dev/null
+++ b/Runtime/Dynamics/TerrainCollider.cpp
@@ -0,0 +1,238 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_TERRAIN && ENABLE_PHYSICS
+#include "TerrainCollider.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Dynamics/PhysicsManager.h"
+
+///@TODO: THis should really only be ITerrain
+#include "Runtime/Terrain/TerrainData.h"
+#include "Runtime/Terrain/Heightmap.h"
+#include "Runtime/Interfaces/ITerrainManager.h"
+
+#include "Runtime/Dynamics/CapsuleCollider.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/BaseClasses/SupportedMessageOptimization.h"
+#include "Runtime/Dynamics/NxWrapperUtility.h"
+
+inline UInt32 GetLowMaterialIndex (UInt32 index)
+{
+ return index & 0x7F;
+}
+
+inline UInt32 GetHighMaterialIndex (UInt32 index)
+{
+ return index >> 7;
+}
+
+
+IMPLEMENT_CLASS_HAS_INIT(TerrainCollider)
+IMPLEMENT_OBJECT_SERIALIZE(TerrainCollider)
+
+TerrainCollider::TerrainCollider (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_Node (this)
+{
+ m_CreateTreeColliders = true;
+ m_CachedInvSize = Vector3f(1.0F, 1.0F, 1.0F);
+}
+
+TerrainCollider::~TerrainCollider ()
+{
+}
+
+void TerrainCollider::SetTerrainData (PPtr<TerrainData> map)
+{
+ if (m_TerrainData != map)
+ {
+ m_TerrainData = map;
+ Create(NULL);
+ SetDirty();
+ }
+}
+
+TerrainData* TerrainCollider::GetTerrainData ()
+{
+ return m_TerrainData;
+}
+
+void TerrainCollider::Cleanup ()
+{
+ Super::Cleanup();
+
+ for (int i=0;i<m_TreeColliders.size();i++)
+ GetDynamicsScene ().releaseActor (m_TreeColliders[i]->getActor());
+ m_TreeColliders.clear();
+
+ m_Node.RemoveFromList();
+}
+
+void TerrainCollider::Create (const Rigidbody* ignoreAttachRigidbody)
+{
+ Cleanup ();
+
+ if (!GetTerrainData())
+ return;
+
+ ITerrainManager* terrainManager = GetITerrainManager();
+
+ //////@TODO: Make this more directly just return the physx representation...
+
+ Heightmap* map = &GetTerrainData()->GetHeightmap();
+ if (map == NULL || terrainManager->Heightmap_GetNxHeightField(*map) == NULL)
+ return;
+
+ NxHeightFieldShapeDesc desc;
+ desc.heightField = terrainManager->Heightmap_GetNxHeightField(*map);
+
+ if (desc.heightField == NULL)
+ return;
+
+ m_CachedInvSize = Inverse(terrainManager->Heightmap_GetSize(*map));
+ Vector3f mapScale = map->GetScale ();
+ desc.heightScale = mapScale.y / (float)(Heightmap::kMaxHeight);
+ desc.columnScale = mapScale.z;
+ desc.rowScale = mapScale.x;
+ desc.holeMaterial = 1;
+
+ // Smooth sphere collisions on terrains are much better!
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_2_a1))
+ desc.meshFlags = NX_MESH_SMOOTH_SPHERE_COLLISIONS;
+
+ desc.materialIndexHighBits = GetHighMaterialIndex(terrainManager->Heightmap_GetMaterialIndex(*map));
+ FinalizeCreate(desc, true, ignoreAttachRigidbody);
+
+ if (m_Shape)
+ {
+ // Insert into front of terrain colliders. Create() may be called from inside
+ // of RecreateColliders, and inserting to back will result in eternal loop.
+ Heightmap::TerrainColliderList& colliders = map->GetTerrainColliders();
+ colliders.insert(colliders.begin(), m_Node);
+ }
+
+ // Dont create trees in edit mode,its too slow to recreate all those trees when adding new trees
+ if (m_CreateTreeColliders && IsWorldPlaying())
+ CreateTrees();
+}
+
+void TerrainCollider::CreateTrees ()
+{
+ const float kMinSize = 0.00001F;
+ int prototypeCount = m_TerrainData->GetTreeDatabase().GetTreePrototypes().size();
+ const TreePrototype* prototypes = prototypeCount > 0 ? &m_TerrainData->GetTreeDatabase().GetTreePrototypes()[0] : NULL;
+
+ int instanceCount = m_TerrainData->GetTreeDatabase().GetInstances().size();
+ const TreeInstance* instances = instanceCount > 0 ? &m_TerrainData->GetTreeDatabase().GetInstances()[0] : NULL;
+
+
+ int supportedMessages = GetGameObject ().GetSupportedMessages ();
+
+ NxActorDesc actorDesc;
+ NxCapsuleShapeDesc shapeDesc;
+ actorDesc.userData = NULL;
+ actorDesc.shapes.push_back (&shapeDesc);
+ shapeDesc.materialIndex = GetMaterialIndex ();
+
+ if (supportedMessages & kHasCollisionStay)
+ actorDesc.group = kContactTouchGroup;
+ else if (supportedMessages & (kHasCollisionStay | kHasCollisionEnterExit))
+ actorDesc.group = kContactEnterExitGroup;
+ else
+ actorDesc.group = kContactNothingGroup;
+ shapeDesc.userData = this;
+ shapeDesc.group = GetGameObject ().GetLayer ();
+
+ Vector3f terrainPositionOffset = GetComponent(Transform).GetPosition();
+
+ Vector3f scale = GetITerrainManager()->Heightmap_GetSize(m_TerrainData->GetHeightmap());
+ NxMat33 id33;
+ id33.id();
+ for (int i=0;i<instanceCount;i++)
+ {
+ const TreeInstance& instance = instances[i];
+
+ if (instance.index >= prototypeCount)
+ {
+ ErrorString("Prototype for tree missing.");
+ continue;
+ }
+
+ const TreePrototype& proto = prototypes[instance.index];
+ Vector3f pos = Scale(scale, instance.position) + terrainPositionOffset;;
+ GameObject* prefab = proto.prefab;
+ if (prefab == NULL)
+ continue;
+ CapsuleCollider* capsule = prefab->QueryComponent(CapsuleCollider);
+ if (capsule == NULL)
+ continue;
+
+ float absoluteHeight = max (Abs (capsule->GetHeight() * instance.heightScale), kMinSize);
+ float absoluteRadius = Abs (instance.widthScale) * capsule->GetRadius();
+
+ float height = absoluteHeight - absoluteRadius * 2.0F;
+
+ height = max (height, kMinSize);
+ absoluteRadius = max (absoluteRadius, kMinSize);
+
+ shapeDesc.height = height;
+ shapeDesc.radius = absoluteRadius;
+ shapeDesc.group = prefab->GetLayer();
+
+ pos += Scale(capsule->GetCenter(), Vector3f(instance.widthScale, instance.heightScale, instance.widthScale));
+
+ actorDesc.globalPose = NxMat34(id33, Vec3ToNx(pos ));
+
+ NxActor* actor = GetDynamicsScene ().createActor (actorDesc);
+ if (actor == NULL)
+ {
+ ErrorString ("Could not create tree colliders. Maybe there are more Trees then PhysX can handle?");
+ for (int i=0;i<m_TreeColliders.size();i++)
+ GetDynamicsScene ().releaseActor (m_TreeColliders[i]->getActor());
+ m_TreeColliders.clear();
+ return;
+ }
+ NxShape* shape = actor->getShapes ()[0];
+
+ m_TreeColliders.push_back(shape);
+ }
+}
+
+template<class TransferFunc>
+void TerrainCollider::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.Align();
+ TRANSFER(m_TerrainData);
+ TRANSFER(m_CreateTreeColliders);
+}
+
+void TerrainCollider::FetchPoseFromTransform ()
+{
+ NxQuat quat; quat.id();
+ Vector3f pos = GetComponent(Transform).GetPosition();
+ NxMat34 shapeMatrix (quat, (const NxVec3&)pos);
+
+ m_Shape->setGlobalPose(shapeMatrix);
+}
+
+void TerrainCollider::InitializeClass ()
+{
+ REGISTER_MESSAGE (TerrainCollider, kTerrainChanged, TerrainChanged, int);
+}
+
+void TerrainCollider::TransformChanged (int changeMask)
+{
+ Super::TransformChanged(changeMask);
+
+ if (m_Shape)
+ FetchPoseFromTransform ();
+}
+
+void TerrainCollider::TerrainChanged (int changeMask)
+{
+ if (changeMask == TerrainData::kWillBeDestroyed)
+ Cleanup();
+}
+#endif
diff --git a/Runtime/Dynamics/TerrainCollider.h b/Runtime/Dynamics/TerrainCollider.h
new file mode 100644
index 0000000..1d01f41
--- /dev/null
+++ b/Runtime/Dynamics/TerrainCollider.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#if ENABLE_TERRAIN && ENABLE_PHYSICS
+
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Dynamics/Collider.h"
+
+class TerrainData;
+
+class TerrainCollider : public Collider
+{
+ PPtr<TerrainData> m_TerrainData;
+ bool m_CreateTreeColliders;
+ ListNode<TerrainCollider> m_Node;
+ std::vector<NxShape*> m_TreeColliders;
+ Vector3f m_CachedInvSize;
+
+ public:
+
+ TerrainCollider (MemLabelId label, ObjectCreationMode mode);
+
+ REGISTER_DERIVED_CLASS(TerrainCollider, Collider)
+ DECLARE_OBJECT_SERIALIZE(TerrainCollider)
+
+ void ScaleChanged (){}
+ void FetchPoseFromTransform ();
+
+ Vector3f GetCachedInvSize () { return m_CachedInvSize; }
+
+ void SetTerrainData (PPtr<TerrainData> map);
+ TerrainData* GetTerrainData ();
+
+ void CreateTrees ();
+ virtual void TransformChanged (int changeMask);
+ void TerrainChanged (int changeMask);
+ virtual void Create (const Rigidbody* ignoreAttachRigidbody);
+ virtual void Cleanup ();
+ static void InitializeClass ();
+ static void CleanupClass () {}
+
+ bool HasShape() const { return m_Shape != NULL; }
+ virtual bool SupportsMaterial () const { return false; }
+};
+
+#endif \ No newline at end of file
diff --git a/Runtime/Dynamics/WheelCollider.cpp b/Runtime/Dynamics/WheelCollider.cpp
new file mode 100644
index 0000000..a3cec0f
--- /dev/null
+++ b/Runtime/Dynamics/WheelCollider.cpp
@@ -0,0 +1,464 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "WheelCollider.h"
+#include "Runtime/Graphics/Transform.h"
+#include "RigidBody.h"
+#include "PhysicsManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+
+
+#define GET_SHAPE() static_cast<NxWheelShape*>(m_Shape)
+
+
+const float kMinSize = 0.00001F;
+const float kMinMass = 0.00001F;
+
+// we can't just reinterpret_cast one to another because NxTireFunctionDesc
+// has a vtable (for apparently no good reason)
+static inline void FrictionCurveToNovodexTireFunc( const WheelFrictionCurve& src, NxTireFunctionDesc& dst )
+{
+ dst.extremumSlip = src.extremumSlip;
+ dst.extremumValue = src.extremumValue;
+ dst.asymptoteSlip = src.asymptoteSlip;
+ dst.asymptoteValue = src.asymptoteValue;
+ dst.stiffnessFactor = src.stiffnessFactor;
+}
+static inline void NovodexTireFuncToFrictionCurve( const NxTireFunctionDesc& src, WheelFrictionCurve& dst )
+{
+ dst.extremumSlip = src.extremumSlip;
+ dst.extremumValue = src.extremumValue;
+ dst.asymptoteSlip = src.asymptoteSlip;
+ dst.asymptoteValue = src.asymptoteValue;
+ dst.stiffnessFactor = src.stiffnessFactor;
+}
+
+
+WheelCollider::WheelCollider(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ // set default parameters for tire curves
+ NxTireFunctionDesc func;
+ NovodexTireFuncToFrictionCurve( func, m_SidewaysFriction );
+ NovodexTireFuncToFrictionCurve( func, m_ForwardFriction );
+}
+
+WheelCollider::~WheelCollider ()
+{
+}
+
+void WheelCollider::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ if (m_Shape)
+ {
+ // Apply changed values
+ SetCenter (m_Center);
+ SetRadius (m_Radius);
+ SetSuspensionDistance (m_SuspensionDistance);
+ SetSuspensionSpring (m_SuspensionSpring);
+ SetForwardFriction(m_ForwardFriction);
+ SetSidewaysFriction(m_SidewaysFriction);
+ SetMass(m_Mass);
+ }
+
+ Super::AwakeFromLoad (awakeMode);
+}
+
+void WheelCollider::SmartReset()
+{
+ Super::SmartReset();
+ AABB aabb;
+ if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb))
+ {
+ SetCenter( aabb.GetCenter() );
+ SetRadius( aabb.GetExtent ().y );
+ SetSuspensionDistance( aabb.GetExtent ().y * 0.1f );
+ }
+ else
+ {
+ SetCenter( Vector3f::zero );
+ SetRadius( 1.0F );
+ SetSuspensionDistance( 0.1f );
+ }
+
+ // setup default friction curves
+ WheelFrictionCurve curve;
+ curve.extremumSlip = 1.0f;
+ curve.extremumValue = 20000.0f;
+ curve.asymptoteSlip = 2.0f;
+ curve.asymptoteValue = 10000.0f;
+ curve.stiffnessFactor = 1.0f;
+
+ SetForwardFriction( curve );
+ SetSidewaysFriction( curve );
+}
+
+void WheelCollider::Reset()
+{
+ Super::Reset();
+
+ m_Center = Vector3f::zero;
+ m_Radius = 1.0F;
+ m_SuspensionDistance = 0.1F;
+
+ m_SuspensionSpring.spring = 1.0f;
+ m_SuspensionSpring.damper = 0.0f;
+ m_SuspensionSpring.targetPosition = 0.0f;
+ m_Mass = 1.0F;
+
+ // setup default friction curves
+ WheelFrictionCurve curve;
+ curve.extremumSlip = 1.0f;
+ curve.extremumValue = 20000.0f;
+ curve.asymptoteSlip = 2.0f;
+ curve.asymptoteValue = 10000.0f;
+ curve.stiffnessFactor = 1.0f;
+
+ m_ForwardFriction = curve;
+ m_SidewaysFriction = curve;
+}
+
+Vector3f WheelCollider::GetGlobalCenter() const
+{
+ return GetComponent(Transform).TransformPoint( m_Center );
+}
+
+float WheelCollider::GetGlobalRadius() const
+{
+ Vector3f scale = GetComponent(Transform).GetWorldScaleLossy();
+ return std::max( Abs(m_Radius*scale.y), kMinSize );
+}
+
+float WheelCollider::GetGlobalSuspensionDistance() const
+{
+ Vector3f scale = GetComponent(Transform).GetWorldScaleLossy();
+ return std::max( Abs(m_SuspensionDistance*scale.y), kMinSize );
+}
+
+void WheelCollider::Create(const Rigidbody* ignoreAttachRigidbody)
+{
+ if( m_Shape )
+ Cleanup();
+
+ NxWheelShapeDesc shapeDesc;
+ shapeDesc.radius = GetGlobalRadius();
+ shapeDesc.suspensionTravel = GetGlobalSuspensionDistance();
+ shapeDesc.suspension = (NxSpringDesc&)m_SuspensionSpring;
+ shapeDesc.inverseWheelMass = 1.0F / std::max( m_Mass, kMinMass );
+ shapeDesc.wheelFlags = NX_WF_INPUT_LNG_SLIPVELOCITY | NX_WF_INPUT_LAT_SLIPVELOCITY;
+ shapeDesc.materialIndex = 2; // use default wheel material setup in CreateScene
+ FrictionCurveToNovodexTireFunc( m_ForwardFriction, shapeDesc.longitudalTireForceFunction );
+ FrictionCurveToNovodexTireFunc( m_SidewaysFriction, shapeDesc.lateralTireForceFunction );
+
+ m_IsTrigger = false; // TBD: make something nicer!
+
+ Rigidbody* body = FindNewAttachedRigidbody (ignoreAttachRigidbody);
+ // WheelCollider requires a Rigidbody, or PhysX will give an error.
+ if (body != NULL)
+ FinalizeCreate( shapeDesc, false, ignoreAttachRigidbody );
+ // Don't show error in Editor, since during setup people may create WheelColliders on empty GameObjects,
+ // and later move to a Rigidbody. We don't want to spam the console with errors in that case.
+ else
+#if UNITY_EDITOR
+ if (IsWorldPlaying())
+#endif
+ ErrorStringObject("WheelCollider requires an attached Rigidbody to function.", this);
+}
+
+void WheelCollider::Cleanup()
+{
+ Super::Cleanup();
+}
+
+void WheelCollider::SetCenter( const Vector3f& center )
+{
+ if (m_Center != center)
+ {
+ m_Center = center;
+ SetDirty();
+ }
+
+ if( GET_SHAPE () )
+ {
+ TransformChanged( Transform::kRotationChanged | Transform::kPositionChanged );
+ m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping
+ RefreshPhysicsInEditMode();
+ }
+}
+
+void WheelCollider::SetRadius( float f )
+{
+ if (m_Radius != f)
+ {
+ SetDirty();
+ m_Radius = f;
+ }
+
+ PROFILE_MODIFY_STATIC_COLLIDER
+
+ if( GET_SHAPE () )
+ {
+ GET_SHAPE()->setRadius( GetGlobalRadius() );
+ m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping
+ RefreshPhysicsInEditMode();
+ }
+}
+
+void WheelCollider::SetSuspensionDistance( float f )
+{
+ if (m_SuspensionDistance != f)
+ {
+ SetDirty();
+ m_SuspensionDistance = f;
+ }
+
+ if( GET_SHAPE () )
+ {
+ GET_SHAPE()->setSuspensionTravel( GetGlobalSuspensionDistance() );
+ m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping
+ }
+}
+
+void WheelCollider::SetSuspensionSpring( const JointSpring& spring )
+{
+ if (m_SuspensionSpring != spring)
+ {
+ SetDirty();
+ m_SuspensionSpring = spring;
+ }
+
+ if( GET_SHAPE () )
+ {
+ GET_SHAPE()->setSuspension( (NxSpringDesc&)spring );
+ m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping
+ }
+}
+
+void WheelCollider::SetSidewaysFriction( const WheelFrictionCurve& curve )
+{
+ if (m_SidewaysFriction != curve)
+ {
+ SetDirty();
+ m_SidewaysFriction = curve;
+ }
+
+ if( GET_SHAPE () )
+ {
+ NxTireFunctionDesc func;
+ FrictionCurveToNovodexTireFunc( curve, func );
+ GET_SHAPE()->setLateralTireForceFunction( func );
+ m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping
+ }
+}
+
+void WheelCollider::SetForwardFriction( const WheelFrictionCurve& curve )
+{
+ if (m_ForwardFriction != curve)
+ {
+ SetDirty();
+ m_ForwardFriction = curve;
+ }
+
+ if( GET_SHAPE () )
+ {
+ NxTireFunctionDesc func;
+ FrictionCurveToNovodexTireFunc( curve, func );
+ GET_SHAPE()->setLongitudalTireForceFunction( func );
+ m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping
+ }
+}
+
+void WheelCollider::SetMass( float f )
+{
+ if (m_Mass != f)
+ {
+ SetDirty();
+ m_Mass = std::max( f, kMinMass );
+ }
+
+ if( GET_SHAPE() )
+ {
+ GET_SHAPE()->setInverseWheelMass( 1.0F / m_Mass );
+ m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping
+ }
+}
+
+void WheelCollider::SetMotorTorque( float f )
+{
+ if( m_Shape )
+ {
+ GET_SHAPE()->setMotorTorque( f );
+ m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping
+ }
+}
+
+float WheelCollider::GetMotorTorque() const
+{
+ if( m_Shape )
+ return GET_SHAPE()->getMotorTorque();
+ else
+ return 0.0F;
+}
+
+void WheelCollider::SetBrakeTorque( float f )
+{
+ if( m_Shape )
+ {
+ GET_SHAPE()->setBrakeTorque( f );
+ m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping
+ }
+}
+
+float WheelCollider::GetBrakeTorque() const
+{
+ if( m_Shape )
+ return GET_SHAPE()->getBrakeTorque();
+ else
+ return 0.0F;
+}
+
+void WheelCollider::SetSteerAngle( float f )
+{
+ if( m_Shape )
+ {
+ GET_SHAPE()->setSteerAngle( Deg2Rad(f) );
+ m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping
+ }
+}
+
+float WheelCollider::GetSteerAngle() const
+{
+ if( m_Shape )
+ return Rad2Deg( GET_SHAPE()->getSteerAngle() );
+ else
+ return 0.0F;
+}
+
+bool WheelCollider::IsGrounded() const
+{
+ if( m_Shape )
+ {
+ NxWheelContactData contactData;
+ NxShape* otherShape = GET_SHAPE()->getContact( contactData );
+ return ( otherShape != NULL );
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool WheelCollider::GetGroundHit( WheelHit& hit ) const
+{
+ if( m_Shape )
+ {
+ NxWheelContactData contactData;
+ NxShape* otherShape = GET_SHAPE()->getContact( contactData );
+ if( otherShape != NULL )
+ {
+ hit.point = reinterpret_cast<Vector3f&>( contactData.contactPoint );
+ hit.normal = reinterpret_cast<Vector3f&>( contactData.contactNormal );
+ hit.forwardDir = reinterpret_cast<Vector3f&>( contactData.longitudalDirection );
+ hit.sidewaysDir = reinterpret_cast<Vector3f&>( contactData.lateralDirection );
+ hit.force = contactData.contactForce;
+ hit.forwardSlip = contactData.longitudalSlip;
+ hit.sidewaysSlip = contactData.lateralSlip;
+ hit.collider = reinterpret_cast<Collider*>( otherShape->userData );
+ return true;
+ }
+ return false;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+float WheelCollider::GetRpm() const
+{
+ if( m_Shape )
+ return GET_SHAPE()->getAxleSpeed() / (kPI*2.0) * 60.0;
+ else
+ return 0.0F;
+}
+
+
+void WheelCollider::ScaleChanged()
+{
+ NxWheelShape* shape = GET_SHAPE();
+ shape->setRadius( GetGlobalRadius() );
+ shape->setSuspensionTravel( GetGlobalSuspensionDistance() );
+ m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping
+ RefreshPhysicsInEditMode();
+}
+
+void WheelCollider::FetchPoseFromTransform()
+{
+ FetchPoseFromTransformUtility( m_Center );
+}
+
+bool WheelCollider::GetRelativeToParentPositionAndRotation( Transform& transform, Transform& anyParent, Matrix4x4f& matrix )
+{
+ return GetRelativeToParentPositionAndRotationUtility( transform, anyParent, m_Center, matrix );
+}
+
+void WheelCollider::TransformChanged( int changeMask )
+{
+ if( m_Shape )
+ {
+ if( m_Shape->getActor().userData == NULL )
+ {
+ PROFILER_AUTO(gStaticColliderMove, this)
+ FetchPoseFromTransform();
+ }
+ else
+ {
+ Rigidbody* body = (Rigidbody*)m_Shape->getActor().userData;
+ Matrix4x4f matrix;
+ if (GetRelativeToParentPositionAndRotation( GetComponent(Transform), body->GetComponent(Transform), matrix ))
+ {
+ NxMat34 shapeMatrix;
+ shapeMatrix.setColumnMajor44( matrix.GetPtr () );
+ m_Shape->setLocalPose( shapeMatrix );
+ }
+ }
+
+ if( changeMask & Transform::kScaleChanged )
+ ScaleChanged();
+
+ m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping
+ RefreshPhysicsInEditMode();
+ }
+}
+
+void WheelCollider::InitializeClass ()
+{
+ REGISTER_MESSAGE( WheelCollider, kTransformChanged, TransformChanged, int );
+}
+
+template<class TransferFunction>
+void WheelCollider::Transfer (TransferFunction& transfer)
+{
+ // NOTE: bypass Collider transfer, so that material and trigger flag are not
+ // serialized.
+
+ Super::Super::Transfer( transfer );
+ TRANSFER( m_Center );
+ TRANSFER_SIMPLE( m_Radius );
+ TRANSFER_SIMPLE( m_SuspensionDistance );
+ TRANSFER_SIMPLE( m_SuspensionSpring );
+ TRANSFER_SIMPLE( m_Mass );
+
+ TRANSFER( m_ForwardFriction );
+ TRANSFER( m_SidewaysFriction );
+
+ // Since we bypass Collider transfer, we need to transfer m_Enabled ourselves
+ transfer.Transfer (m_Enabled, "m_Enabled", kHideInEditorMask | kEditorDisplaysCheckBoxMask);
+ transfer.Align();
+}
+
+IMPLEMENT_CLASS_HAS_INIT( WheelCollider )
+IMPLEMENT_OBJECT_SERIALIZE( WheelCollider )
+#endif //ENABLE_PHYSICS \ No newline at end of file
diff --git a/Runtime/Dynamics/WheelCollider.h b/Runtime/Dynamics/WheelCollider.h
new file mode 100644
index 0000000..53ddcaf
--- /dev/null
+++ b/Runtime/Dynamics/WheelCollider.h
@@ -0,0 +1,142 @@
+#ifndef WHEELCOLLIDER_H
+#define WHEELCOLLIDER_H
+
+#include "Collider.h"
+#include "Runtime/Math/Vector3.h"
+#include "JointDescriptions.h"
+
+class NxMaterial;
+
+// ----------------------------------------------------------------------
+
+struct WheelFrictionCurve
+{
+ float extremumSlip; ///<Extremum Slip. range { 0.001, infinity }
+ float extremumValue; ///<Extremum Value. range { 0.001, infinity }
+ float asymptoteSlip; ///<Asymptote Slip. range { 0.001, infinity }
+ float asymptoteValue; ///<Asymptote Value. range { 0.001, infinity }
+ float stiffnessFactor; ///<Stiffness Factor. range { 0, infinity }
+
+ bool operator != (const WheelFrictionCurve& c)const
+ {
+ return extremumSlip != c.extremumSlip
+ || extremumValue != c.extremumValue
+ || asymptoteSlip != c.asymptoteSlip
+ || asymptoteValue != c.asymptoteValue
+ || stiffnessFactor != c.stiffnessFactor
+ ;
+ }
+
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (WheelFrictionCurve)
+};
+
+
+template<class TransferFunction>
+void WheelFrictionCurve::Transfer (TransferFunction& transfer)
+{
+ TRANSFER_SIMPLE( extremumSlip );
+ TRANSFER_SIMPLE( extremumValue );
+ TRANSFER_SIMPLE( asymptoteSlip );
+ TRANSFER_SIMPLE( asymptoteValue );
+ TRANSFER_SIMPLE( stiffnessFactor );
+}
+
+// ----------------------------------------------------------------------
+
+struct WheelHit
+{
+ Vector3f point;
+ Vector3f normal;
+ Vector3f forwardDir;
+ Vector3f sidewaysDir;
+ float force;
+ float forwardSlip;
+ float sidewaysSlip;
+ Collider* collider;
+};
+
+
+// ----------------------------------------------------------------------
+
+
+class WheelCollider : public Collider
+{
+public:
+ REGISTER_DERIVED_CLASS (WheelCollider, Collider)
+ DECLARE_OBJECT_SERIALIZE (WheelCollider)
+
+ WheelCollider(MemLabelId label, ObjectCreationMode mode);
+
+ virtual void Reset();
+ virtual void SmartReset();
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ Vector3f GetCenter() const { return m_Center; }
+ void SetCenter( const Vector3f& center );
+
+ float GetRadius() const { return m_Radius; }
+ void SetRadius( float f );
+
+ float GetSuspensionDistance() const { return m_SuspensionDistance; }
+ void SetSuspensionDistance( float f );
+
+ void SetSuspensionSpring( const JointSpring& spring );
+ const JointSpring& GetSuspensionSpring() const { return m_SuspensionSpring; }
+
+ void SetForwardFriction( const WheelFrictionCurve& curve );
+ const WheelFrictionCurve& GetForwardFriction() const { return m_ForwardFriction; }
+
+ void SetSidewaysFriction( const WheelFrictionCurve& curve );
+ const WheelFrictionCurve& GetSidewaysFriction() const { return m_SidewaysFriction; }
+
+ float GetMass() const { return m_Mass; }
+ void SetMass( float f );
+
+ float GetMotorTorque() const;
+ void SetMotorTorque( float f );
+
+ float GetBrakeTorque() const;
+ void SetBrakeTorque( float f );
+
+ float GetSteerAngle() const;
+ void SetSteerAngle( float f );
+
+ bool IsGrounded() const;
+ bool GetGroundHit( WheelHit& hit ) const;
+
+ float GetRpm() const;
+
+ void TransformChanged( int changeMask );
+
+ static void InitializeClass();
+ static void CleanupClass() { }
+
+ Vector3f GetGlobalCenter() const;
+ float GetGlobalRadius() const;
+ float GetGlobalSuspensionDistance() const;
+ Matrix4x4f CalculateTransform() const;
+
+ virtual bool SupportsMaterial () const { return false; }
+
+protected:
+ virtual void FetchPoseFromTransform();
+ virtual bool GetRelativeToParentPositionAndRotation( Transform& transform, Transform& anyParent, Matrix4x4f& matrix );
+
+ virtual void Create(const Rigidbody* ignoreRigidbody);
+ virtual void ScaleChanged();
+ virtual void Cleanup();
+
+private:
+ Vector3f m_Center;
+ JointSpring m_SuspensionSpring;
+ WheelFrictionCurve m_ForwardFriction;
+ WheelFrictionCurve m_SidewaysFriction;
+ float m_Radius; ///< range { 0, infinity }
+ float m_SuspensionDistance; ///< range { 0, infinity }
+ float m_Mass; ///< range { 0.0001, infinity }
+
+ // the wheel material is shared by all instances
+ static NxMaterial* m_WheelMaterial;
+};
+
+#endif
diff --git a/Runtime/Dynamics/nxmemorystream.cpp b/Runtime/Dynamics/nxmemorystream.cpp
new file mode 100644
index 0000000..eab492f
--- /dev/null
+++ b/Runtime/Dynamics/nxmemorystream.cpp
@@ -0,0 +1,134 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "nxmemorystream.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+
+
+#define BLOCKSIZE 4096
+
+MemoryStream::MemoryStream(NxU8* data,NxU32 len)
+{
+ if ( data )
+ {
+ mBuffer = data;
+ mMyAlloc = false;
+ }
+ else
+ {
+ mBuffer = (NxU8 *) UNITY_MALLOC(kMemFile, sizeof(NxU8)*len);
+ mMyAlloc = true;
+ }
+
+ mReadLoc = 0;
+ mWriteLoc = 0;
+ mLen = len;
+}
+
+MemoryStream::~MemoryStream(void)
+{
+ if ( mMyAlloc )
+ {
+ UNITY_FREE(kMemFile, mBuffer);
+ }
+}
+
+NxU8 MemoryStream::readByte(void) const
+{
+ NxU8 ret = 0;
+ readBuffer(&ret, sizeof(NxU8) );
+ return ret;
+}
+
+NxU16 MemoryStream::readWord(void) const
+{
+ NxU16 ret = 0;
+ readBuffer(&ret,sizeof(NxU16));
+ return ret;
+}
+
+NxU32 MemoryStream::readDword(void) const
+{
+ NxU32 ret = 0;
+ readBuffer(&ret,sizeof(NxU32));
+ return ret;
+}
+
+float MemoryStream::readFloat(void) const
+{
+ float ret = 0;
+ readBuffer(&ret,sizeof(float));
+ return ret;
+}
+
+double MemoryStream::readDouble(void) const
+{
+ double ret = 0;
+ readBuffer(&ret,sizeof(double));
+ return ret;
+}
+
+void MemoryStream::readBuffer(void* buffer, NxU32 size) const
+{
+ if ( (mReadLoc+size) <= mLen )
+ {
+ memcpy( buffer, &mBuffer[mReadLoc], size );
+ mReadLoc+=size;
+ }
+}
+
+NxStream& MemoryStream::storeByte(NxU8 b)
+{
+ storeBuffer(&b, sizeof(NxU8) );
+ return *this;
+}
+
+NxStream& MemoryStream::storeWord(NxU16 w)
+{
+ storeBuffer(&w,sizeof(NxU16));
+ return *this;
+}
+
+NxStream& MemoryStream::storeDword(NxU32 d)
+{
+ storeBuffer(&d,sizeof(NxU32));
+ return *this;
+}
+
+NxStream& MemoryStream::storeFloat(NxReal f)
+{
+ storeBuffer(&f,sizeof(NxReal));
+ return *this;
+}
+
+NxStream& MemoryStream::storeDouble(NxF64 f)
+{
+ storeBuffer(&f,sizeof(NxF64));
+ return *this;
+}
+
+NxStream& MemoryStream::storeBuffer(const void* buffer, NxU32 size)
+{
+
+ if ( (mWriteLoc+size) >= mLen )
+ {
+ unsigned int blocksize = BLOCKSIZE;
+
+ if ( size > BLOCKSIZE ) blocksize = size*2;
+
+ NxU8 *buf = (NxU8 *)UNITY_MALLOC(kMemFile, mLen+blocksize);
+ memcpy(buf,mBuffer,mWriteLoc);
+ UNITY_FREE(kMemFile, mBuffer);
+ mBuffer = buf;
+ mLen+=blocksize;
+
+ }
+ memcpy(&mBuffer[mWriteLoc], buffer, size );
+ mWriteLoc+=size;
+
+ return *this;
+}
+
+#endif //ENABLE_PHYSICS \ No newline at end of file
diff --git a/Runtime/Dynamics/nxmemorystream.h b/Runtime/Dynamics/nxmemorystream.h
new file mode 100644
index 0000000..80f8563
--- /dev/null
+++ b/Runtime/Dynamics/nxmemorystream.h
@@ -0,0 +1,34 @@
+#ifndef MEMORY_STREAM_H
+#define MEMORY_STREAM_H
+
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "External/PhysX/builds/SDKs/Foundation/include/NxStream.h"
+
+class MemoryStream : public NxStream
+ {
+ public:
+ MemoryStream(NxU8* data,NxU32 len);
+ virtual ~MemoryStream();
+
+ virtual NxU8 readByte() const;
+ virtual NxU16 readWord() const;
+ virtual NxU32 readDword() const;
+ virtual float readFloat() const;
+ virtual double readDouble() const;
+ virtual void readBuffer(void* buffer, NxU32 size) const;
+
+ virtual NxStream& storeByte(NxU8 b);
+ virtual NxStream& storeWord(NxU16 w);
+ virtual NxStream& storeDword(NxU32 d);
+ virtual NxStream& storeFloat(NxReal f);
+ virtual NxStream& storeDouble(NxF64 f);
+ virtual NxStream& storeBuffer(const void* buffer, NxU32 size);
+
+ bool mMyAlloc;
+ mutable NxU32 mReadLoc;
+ NxU32 mWriteLoc;
+ NxU32 mLen;
+ NxU8* mBuffer; // buffer
+ };
+
+#endif
diff --git a/Runtime/Export/AndroidInput.txt b/Runtime/Export/AndroidInput.txt
new file mode 100644
index 0000000..7667f15
--- /dev/null
+++ b/Runtime/Export/AndroidInput.txt
@@ -0,0 +1,85 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Input/GetInput.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+
+#if UNITY_ANDROID
+ #include <PlatformDependent/AndroidPlayer/AndroidInput.h>
+ #include <PlatformDependent/AndroidPlayer/TouchInput.h>
+ #include <android/input.h>
+#endif
+
+CSRAW
+
+namespace UnityEngine
+{
+
+// AndroidInput provides support for off-screen touch input, such as a touchpad.
+CONDITIONAL UNITY_ANDROID || UNITY_EDITOR
+CLASS AndroidInput
+
+ // Hide constructor
+ CSRAW private AndroidInput() {}
+
+ // Returns object representing status of a specific touch on a secondary touchpad (Does not allocate temporary variables).
+ CUSTOM static Touch GetSecondaryTouch (int index)
+ {
+ Touch touch;
+#if UNITY_ANDROID
+ if (index >= 0 && index < GetTouchCount ((int)AINPUT_SOURCE_TOUCHPAD))
+ {
+ if (!GetTouch ((int)AINPUT_SOURCE_TOUCHPAD, index, touch))
+ Scripting::RaiseMonoException ("Internal error.");
+ }
+ else
+ Scripting::RaiseMonoException ("Index out of bounds.");
+#endif
+ return touch;
+ }
+
+ // Number of secondary touches. Guaranteed not to change throughout the frame. (RO).
+ CUSTOM_PROP static int touchCountSecondary
+ {
+#if UNITY_ANDROID
+ return GetTouchCount((int)AINPUT_SOURCE_TOUCHPAD);
+#else
+ return 0;
+#endif
+ }
+
+ // Property indicating whether the system provides secondary touch input.
+ CUSTOM_PROP static bool secondaryTouchEnabled
+ {
+#if UNITY_ANDROID
+ return IsInputDeviceEnabled((int)AINPUT_SOURCE_TOUCHPAD);
+#else
+ return false;
+#endif
+ }
+
+ // Property indicating the width of the secondary touchpad.
+ CUSTOM_PROP static int secondaryTouchWidth
+ {
+#if UNITY_ANDROID
+ return GetTouchpadWidth();
+#else
+ return 0;
+#endif
+ }
+
+ // Property indicating the height of the secondary touchpad.
+ CUSTOM_PROP static int secondaryTouchHeight
+ {
+#if UNITY_ANDROID
+ return GetTouchpadHeight();
+#else
+ return 0;
+#endif
+ }
+
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/AndroidJNI.txt b/Runtime/Export/AndroidJNI.txt
new file mode 100644
index 0000000..fc7bd82
--- /dev/null
+++ b/Runtime/Export/AndroidJNI.txt
@@ -0,0 +1,918 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoScopedThreadAttach.h"
+
+#if UNITY_ANDROID
+
+#if UNITY_DEVELOPER_BUILD
+ static volatile bool DEBUGJNI = false;
+#else
+ #define DEBUGJNI 0
+#endif
+
+#define DEBUGJNI_VERBOSE 0
+
+#include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+
+ static MonoDomain* s_MonoDomain = 0;
+ jobject UnityJavaProxy_invoke(JNIEnv*, jobject thiz, jint delegate, jstring method, jobjectArray args)
+ {
+ ScopedThreadAttach mono(s_MonoDomain);
+ MonoObject* proxy = mono_gchandle_get_target(delegate);
+ void* params[] = {proxy, &method, &args};
+ MonoObject* mresult = CallStaticMonoMethod("_AndroidJNIHelper", "InvokeJavaProxyMethod", params);
+ if (!mresult)
+ return 0;
+ return ExtractMonoObjectData<jobject>(mresult);
+ }
+ void UnityJavaProxy_finalize(JNIEnv*, jobject thiz, jint jdelegate)
+ {
+ ScopedThreadAttach mono(s_MonoDomain);
+ mono_gchandle_free(jdelegate);
+ }
+
+#if DEBUGJNI
+ #define SCOPED_JNI(x) DalvikAttachThreadScoped jni_env(x)
+#else
+ #define SCOPED_JNI(x) DalvikAttachThreadScoped jni_env("AndroidJNI")
+#endif
+
+#define JNI_GET_ID( GetIDFunc ) \
+ do { \
+ SCOPED_JNI(__FUNCTION__); \
+ if (!jni_env) return 0; \
+ string methodName = name; \
+ string signature = sig; \
+ if (DEBUGJNI) \
+ printf_console("> %s(%08x, %s, %s)", __FUNCTION__, clazz, methodName.c_str(), signature.c_str()); \
+ if (clazz == 0) \
+ return 0; \
+ return jni_env->GetIDFunc((jclass)clazz, methodName.c_str(), signature.c_str()); \
+ } while(0)
+
+#define ConvertToJNIArgs() \
+ int numargs = mono_array_length(args); \
+ jvalue* jni_args = (jvalue*)alloca(sizeof(jvalue) * numargs); \
+ for (int i = 0; i < numargs; ++i) \
+ { \
+ jni_args[i] = GetMonoArrayElement<jvalue>(args, i); \
+ if (DEBUGJNI) \
+ printf_console(">\t\t\t, %08x", jni_args[i].i); \
+ } \
+ if (DEBUGJNI && numargs) \
+ printf_console(">\t\t\t)");
+
+#define JNI_CALL_METHOD( MethodFunc, ClassOrObj ) \
+ do { \
+ SCOPED_JNI(__FUNCTION__); \
+ if (!jni_env) return 0; \
+ if (DEBUGJNI) \
+ printf_console("> %s(%08x, %08x%s", __FUNCTION__, ClassOrObj, \
+ (jmethodID)methodID, mono_array_length(args) ? " " : ")"); \
+ if (ClassOrObj == 0 || (jmethodID)methodID == 0) \
+ return 0; \
+ ConvertToJNIArgs(); \
+ return jni_env->MethodFunc(ClassOrObj, (jmethodID)methodID, jni_args); \
+ } while(0)
+
+#define JNI_CALL_VOID_METHOD( MethodFunc, ClassOrObj ) \
+ do { \
+ SCOPED_JNI(__FUNCTION__); \
+ if (!jni_env) return; \
+ if (DEBUGJNI) \
+ printf_console("> %s(%08x, %08x%s", __FUNCTION__, ClassOrObj, \
+ (jmethodID)methodID, mono_array_length(args) ? " " : ")"); \
+ if (ClassOrObj == 0 || (jmethodID)methodID == 0) \
+ return; \
+ ConvertToJNIArgs(); \
+ jni_env->MethodFunc(ClassOrObj, (jmethodID)methodID, jni_args); \
+ } while(0)
+
+#define JNI_GET_FIELD( FieldFunc, ClassOrObj ) \
+ do { \
+ SCOPED_JNI(__FUNCTION__); \
+ if (!jni_env) return 0; \
+ if (DEBUGJNI) \
+ printf_console("> %s(%08x)", __FUNCTION__, ClassOrObj); \
+ if (ClassOrObj == 0 || (jmethodID)fieldID == 0) \
+ return 0; \
+ return jni_env->FieldFunc(ClassOrObj, (jfieldID)fieldID); \
+ } while(0)
+
+#define JNI_SET_FIELD( FieldFunc, ClassOrObj, Value ) \
+ do { \
+ SCOPED_JNI(__FUNCTION__); \
+ if (!jni_env) return; \
+ if (DEBUGJNI) \
+ printf_console("> %s(%08x)", __FUNCTION__, ClassOrObj); \
+ if (ClassOrObj == 0 || (jmethodID)fieldID == 0) \
+ return; \
+ jni_env->FieldFunc(ClassOrObj, (jfieldID)fieldID, Value); \
+ } while(0)
+
+#define JNI_PASS_VOID_VOID( Func ) \
+ do { \
+ SCOPED_JNI(__FUNCTION__); \
+ if (!jni_env) return; \
+ if (DEBUGJNI) \
+ printf_console("> %s()", __FUNCTION__); \
+ jni_env->Func(); \
+ } while(0)
+
+#define JNI_PASS_RETV_VOID( Func ) \
+ do { \
+ SCOPED_JNI(__FUNCTION__); \
+ if (!jni_env) return 0; \
+ if (DEBUGJNI) \
+ printf_console("> %s()", __FUNCTION__); \
+ return jni_env->Func(); \
+ } while(0)
+
+#define JNI_PASS_VOID_ARGS( Func, ...) \
+ do { \
+ SCOPED_JNI(__FUNCTION__); \
+ if (!jni_env) return; \
+ if (DEBUGJNI) \
+ printf_console("> %s()", __FUNCTION__); \
+ jni_env->Func(__VA_ARGS__); \
+ } while(0)
+
+#define JNI_PASS_RETV_ARGS( Func, ...) \
+ do { \
+ SCOPED_JNI(__FUNCTION__); \
+ if (!jni_env) return 0; \
+ if (DEBUGJNI) \
+ printf_console("> %s()", __FUNCTION__); \
+ return jni_env->Func(__VA_ARGS__); \
+ } while(0)
+
+#define JNI_GET_JSTR_FIELD( FieldFunc, ClassOrObj ) \
+ do { \
+ SCOPED_JNI(__FUNCTION__); \
+ if (!jni_env) return SCRIPTING_NULL; \
+ if (DEBUGJNI) \
+ printf_console("> %s(%08x)", __FUNCTION__, ClassOrObj); \
+ if (ClassOrObj == 0 || (jmethodID)fieldID == 0) \
+ return SCRIPTING_NULL; \
+ jstring str = (jstring)jni_env->FieldFunc(ClassOrObj, (jfieldID)fieldID); \
+ if (str == 0 || jni_env->ExceptionCheck()) return SCRIPTING_NULL; \
+ const char* cstring = jni_env->GetStringUTFChars(str, 0); \
+ if (cstring == 0 || jni_env->ExceptionCheck()) \
+ { \
+ jni_env->ReleaseStringUTFChars(str, cstring); \
+ jni_env->DeleteLocalRef(str); \
+ return SCRIPTING_NULL; \
+ } \
+ MonoString* mstring = scripting_string_new(cstring); \
+ jni_env->ReleaseStringUTFChars(str, cstring); \
+ jni_env->DeleteLocalRef(str); \
+ return mstring; \
+ } while(0)
+
+#define JNI_CALL_JSTR_METHOD( MethodFunc, ClassOrObj ) \
+ do { \
+ SCOPED_JNI(__FUNCTION__); \
+ if (!jni_env) return SCRIPTING_NULL; \
+ if (DEBUGJNI) \
+ printf_console("> %s(%08x, %08x%s", __FUNCTION__, ClassOrObj, \
+ (jmethodID)methodID, mono_array_length(args) ? " " : ")"); \
+ if (ClassOrObj == 0 || (jmethodID)methodID == 0) \
+ return SCRIPTING_NULL; \
+ ConvertToJNIArgs(); \
+ jstring str = (jstring)jni_env->MethodFunc(ClassOrObj, (jmethodID)methodID, jni_args); \
+ if (str == 0 || jni_env->ExceptionCheck()) return SCRIPTING_NULL; \
+ const char* cstring = jni_env->GetStringUTFChars(str, 0); \
+ if (cstring == 0 || jni_env->ExceptionCheck()) \
+ { \
+ jni_env->ReleaseStringUTFChars(str, cstring); \
+ jni_env->DeleteLocalRef(str); \
+ return SCRIPTING_NULL; \
+ } \
+ MonoString* mstring = scripting_string_new(cstring); \
+ jni_env->ReleaseStringUTFChars(str, cstring); \
+ jni_env->DeleteLocalRef(str); \
+ return mstring; \
+ } while(0)
+
+#define JNI_NEW_ARRAY( NewArrayType, Type, ArrayType ) \
+ do { \
+ SCOPED_JNI(__FUNCTION__); \
+ if (!jni_env) return SCRIPTING_NULL; \
+ int size = mono_array_length(array); \
+ ArrayType jni_array = jni_env->New ## NewArrayType ## Array(size); \
+ if (jni_array == 0 || jni_env->ExceptionCheck()) return SCRIPTING_NULL; \
+ for (int i = 0; i < size; ++i) \
+ { \
+ Type val = GetMonoArrayElement<Type>(array, i); \
+ jni_env->Set ## NewArrayType ## ArrayRegion(jni_array, i, 1, &val); \
+ if (jni_env->ExceptionCheck()) return SCRIPTING_NULL; \
+ } \
+ return jni_array; \
+ } while(0)
+
+#define JNI_GET_ARRAY( GetArrayType, Type, ArrayType, ArrayClass ) \
+ do { \
+ SCOPED_JNI(__FUNCTION__); \
+ if (!jni_env) return SCRIPTING_NULL; \
+ jsize length = jni_env->GetArrayLength((jarray)array); \
+ if (jni_env->ExceptionCheck()) return SCRIPTING_NULL; \
+ MonoArray* csarray = mono_array_new(mono_domain_get(), ArrayClass, length); \
+ Type* mem = jni_env->Get ## GetArrayType ## ArrayElements((ArrayType)array, 0); \
+ if (jni_env->ExceptionCheck()) return SCRIPTING_NULL; \
+ for (int i = 0; i < length; ++i) \
+ Scripting::SetScriptingArrayElement<Type>(csarray, i, mem[i]); \
+ jni_env->Release ## GetArrayType ## ArrayElements((ArrayType)array, mem, JNI_ABORT); \
+ return csarray; \
+ } while(0)
+
+#define JNI_GET_ARRAY_ELEMENT( GetArrayType, Type, ArrayType ) \
+ do { \
+ SCOPED_JNI(__FUNCTION__); \
+ if (!jni_env) return 0; \
+ Type val; \
+ jni_env->Get ## GetArrayType ## ArrayRegion((ArrayType)array, index, 1, &val); \
+ return val; \
+ } while(0)
+
+#define JNI_SET_ARRAY_ELEMENT( SetArrayType, Type, ArrayType ) \
+ do { \
+ SCOPED_JNI(__FUNCTION__); \
+ if (!jni_env) return; \
+ jni_env->Get ## SetArrayType ## ArrayRegion((ArrayType)array, index, 1, &val); \
+ } while(0)
+
+struct jStringWrapper
+{
+ std::string cString;
+ jstring javaString;
+ jStringWrapper(ICallString& cs)
+ : javaString(0)
+ {
+ cString = cs;
+ if (DEBUGJNI_VERBOSE)
+ printf_console("> [%s](\"%s\")", __FUNCTION__, cString.c_str());
+ }
+
+ ~jStringWrapper()
+ {
+ if (DEBUGJNI_VERBOSE)
+ printf_console("> [%s](\"%s\")", __FUNCTION__, cString.c_str());
+ if (!javaString)
+ return;
+ SCOPED_JNI(__FUNCTION__);
+ if (!jni_env) return;
+ jni_env->DeleteLocalRef(javaString);
+ }
+
+ operator jstring()
+ {
+ if (DEBUGJNI_VERBOSE)
+ printf_console("> [%s](\"%s\")", __FUNCTION__, cString.c_str());
+ SCOPED_JNI(__FUNCTION__);
+ if (!jni_env) return 0;
+ javaString = jni_env->NewStringUTF(cString.c_str());
+ return javaString;
+ }
+ operator const char*()
+ {
+ if (DEBUGJNI_VERBOSE)
+ printf_console("> [%s](\"%s\")", __FUNCTION__, cString.c_str());
+ return cString.c_str();
+ }
+};
+
+#else // UNITY_ANDROID
+
+typedef void* jobject;
+typedef void* jclass;
+
+#define JNI_GET_ID( GetIDFunc ) do { return 0; } while(0)
+#define JNI_CALL_METHOD( MethodFunc, ClassOrObj ) do { (void)ClassOrObj; return 0; } while(0)
+#define JNI_CALL_VOID_METHOD( MethodFunc, ClassOrObj ) do { (void)ClassOrObj; } while(0)
+#define JNI_GET_FIELD( FieldFunc, ClassOrObj ) do { (void)ClassOrObj; return 0; } while(0)
+#define JNI_GET_JSTR_FIELD( FieldFunc, ClassOrObj ) do { (void)ClassOrObj; return SCRIPTING_NULL; } while(0)
+#define JNI_SET_FIELD( FieldFunc, ClassOrObj, Value ) do { (void)ClassOrObj; (void)Value; } while(0)
+#define JNI_PASS_VOID_VOID( Func ) do { ; } while(0)
+#define JNI_PASS_RETV_VOID( Func) do { return 0; } while(0)
+#define JNI_PASS_VOID_ARGS( Func, ...) do { ; } while(0)
+#define JNI_PASS_RETV_ARGS( Func, ...) do { return 0; } while(0)
+#define JNI_CALL_JSTR_METHOD( MethodFunc, ClassOrObj ) do { (void)ClassOrObj; return SCRIPTING_NULL; } while(0)
+#define JNI_NEW_ARRAY( NewArrayFunc, Type, ArrayType ) do { return SCRIPTING_NULL; } while(0)
+#define JNI_GET_ARRAY( GetArrayType, Type, ArrayType, ArrayClass ) do { return SCRIPTING_NULL; } while(0)
+#define JNI_GET_ARRAY_ELEMENT( GetArrayType, Type, ArrayType ) do { return 0; } while(0)
+#define JNI_SET_ARRAY_ELEMENT( SetArrayType, Type, ArrayType ) do { ; } while(0)
+
+#endif
+
+CSRAW
+#if UNITY_EDITOR || UNITY_ANDROID
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace UnityEngine
+{
+ [StructLayout(LayoutKind.Explicit)]
+ public struct jvalue
+ {
+ [FieldOffset(0)] public bool z;
+ [FieldOffset(0)] public byte b;
+ [FieldOffset(0)] public char c;
+ [FieldOffset(0)] public short s;
+ [FieldOffset(0)] public int i;
+ [FieldOffset(0)] public long j;
+ [FieldOffset(0)] public float f;
+ [FieldOffset(0)] public double d;
+ [FieldOffset(0)] public System.IntPtr l;
+ }
+
+// Helper interface for JNI interaction; signature creation and method lookups
+CONDITIONAL !UNITY_WINRT
+CLASS AndroidJNIHelper
+
+ CSRAW private AndroidJNIHelper() {}
+
+ // Set /debug/ to true to log calls through the AndroidJNIHelper
+ CUSTOM_PROP static bool debug
+ {
+ #if UNITY_ANDROID && UNITY_DEVELOPER_BUILD
+ return DEBUGJNI;
+ #else
+ return false;
+ #endif
+ }
+ {
+ #if UNITY_ANDROID && UNITY_DEVELOPER_BUILD
+ DEBUGJNI = value;
+ SCOPED_JNI(__FUNCTION__);
+ if (!jni_env) return;
+ jclass cls_up = jni_env->FindClass("com/unity3d/player/ReflectionHelper");
+ if (!cls_up || jni_env->ExceptionCheck())
+ return;
+ jfieldID fid_get = jni_env->GetStaticFieldID(cls_up, "LOG", "Z");
+ if (!fid_get || jni_env->ExceptionCheck())
+ {
+ jni_env->DeleteLocalRef(cls_up);
+ return;
+ }
+ jni_env->SetStaticBooleanField(cls_up, fid_get, value);
+ jni_env->DeleteLocalRef(cls_up);
+ #endif
+ }
+
+ // Scans a particular Java class for a constructor method matching a signature.
+ CSRAW public static IntPtr GetConstructorID(IntPtr javaClass, string signature = "")
+ {
+ return _AndroidJNIHelper.GetConstructorID(javaClass, signature);
+ }
+
+ // Scans a particular Java class for a method matching a name and a signature.
+ CSRAW public static IntPtr GetMethodID(IntPtr javaClass, string methodName, string signature = "", bool isStatic = false)
+ {
+ return _AndroidJNIHelper.GetMethodID(javaClass, methodName, signature, isStatic);
+ }
+
+ CSRAW public static IntPtr GetFieldID(IntPtr javaClass, string fieldName, string signature = "", bool isStatic = false)
+ {
+ return _AndroidJNIHelper.GetFieldID(javaClass, fieldName, signature, isStatic);
+ }
+
+ // Creates a UnityJavaRunnable object (implements java.lang.Runnable).
+ CSRAW public static IntPtr CreateJavaRunnable(AndroidJavaRunnable jrunnable)
+ {
+ return _AndroidJNIHelper.CreateJavaRunnable(jrunnable);
+ }
+
+ // Creates a UnityJavaProxy object (implements jinterface).
+ THREAD_SAFE CUSTOM static IntPtr CreateJavaProxy(AndroidJavaProxy proxy)
+ {
+ #if UNITY_ANDROID
+ s_MonoDomain = mono_domain_get();
+
+ int gcHandle = mono_gchandle_new(proxy, 0);
+ void* params[] = { &gcHandle, proxy };
+ MonoException* exception;
+ MonoObject* result = CallStaticMonoMethod("_AndroidJNIHelper", "CreateJavaProxy", params, &exception);
+ if (exception)
+ {
+ mono_gchandle_free(gcHandle);
+ mono_raise_exception(exception);
+ return 0;
+ }
+ return ExtractMonoObjectData<jobject>(result);
+ #else
+ return 0;
+ #endif
+ }
+
+ // Creates a Java array from a managed array
+ CSRAW public static IntPtr ConvertToJNIArray(System.Array array)
+ {
+ return _AndroidJNIHelper.ConvertToJNIArray(array);
+ }
+
+ // Creates the parameter array to be used as argument list when invoking Java code through CallMethod() in AndroidJNI.
+ CSRAW public static jvalue[] CreateJNIArgArray(object[] args)
+ {
+ return _AndroidJNIHelper.CreateJNIArgArray(args);
+ }
+
+ // Deletes any local jni references previously allocated by CreateJNIArgArray()
+ //
+ // @param jniArgs the array returned by CreateJNIArgArray()
+ // @param args the array of arguments used as a parameter to CreateJNIArgArray()
+ //
+ CSRAW public static void DeleteJNIArgArray(object[] args, jvalue[] jniArgs)
+ {
+ _AndroidJNIHelper.DeleteJNIArgArray(args, jniArgs);
+ }
+
+ // Get a JNI method ID for a constructor based on calling arguments.
+ // Scans a particular Java class for a constructor method matching a signature based on passed arguments.
+ // The signature comparison is done to allow for sub-/base-classes of the class types.
+ //
+ // @param javaClass Raw JNI Java class object (obtained by calling AndroidJNI.FindClass).
+ // @param args Array with parameters to be passed to the constructor when invoked.
+ //
+ CSRAW public static System.IntPtr GetConstructorID(System.IntPtr jclass, object[] args)
+ {
+ return _AndroidJNIHelper.GetConstructorID(jclass, args);
+ }
+ // Get a JNI method ID based on calling arguments.
+ CSRAW public static System.IntPtr GetMethodID(System.IntPtr jclass, string methodName, object[] args, bool isStatic)
+ {
+ return _AndroidJNIHelper.GetMethodID(jclass, methodName, args, isStatic);
+ }
+
+ // Creates the JNI signature string for particular object type
+ CSRAW public static string GetSignature(object obj)
+ {
+ return _AndroidJNIHelper.GetSignature(obj);
+ }
+
+ // Creates the JNI signature string for an object parameter list.
+ CSRAW public static string GetSignature(object[] args)
+ {
+ return _AndroidJNIHelper.GetSignature(args);
+ }
+
+ //===================================================================
+
+ // Creates a managed array from a Java array
+ CSRAW public static ArrayType ConvertFromJNIArray<ArrayType>(IntPtr array)
+ {
+ return _AndroidJNIHelper.ConvertFromJNIArray<ArrayType>(array);
+ }
+
+ // Get a JNI method ID based on calling arguments.
+ CSRAW public static System.IntPtr GetMethodID<ReturnType>(System.IntPtr jclass, string methodName, object[] args, bool isStatic)
+ {
+ return _AndroidJNIHelper.GetMethodID<ReturnType>(jclass, methodName, args, isStatic);
+ }
+
+ // Get a JNI field ID based on type detection. Generic parameter represents the field type.
+ CSRAW public static System.IntPtr GetFieldID<FieldType>(System.IntPtr jclass, string fieldName, bool isStatic)
+ {
+ return _AndroidJNIHelper.GetFieldID<FieldType>(jclass, fieldName, isStatic);
+ }
+
+ // Creates the JNI signature string for an object parameter list.
+ CSRAW public static string GetSignature<ReturnType>(object[] args)
+ {
+ return _AndroidJNIHelper.GetSignature<ReturnType>(args);
+ }
+END
+
+// 'Raw' JNI interface to Android Dalvik (Java) VM from Mono (CS/JS)
+CONDITIONAL !UNITY_WINRT
+CLASS AndroidJNI
+
+ CSRAW private AndroidJNI() {}
+
+ // Attaches the current thread to a Java (Dalvik) VM.
+ THREAD_SAFE CUSTOM static int AttachCurrentThread()
+ {
+ #if UNITY_ANDROID
+ JavaVM* vm = GetJavaVm();
+ Assert(vm);
+ JNIEnv* env;
+ return vm->AttachCurrentThread(&env, 0);
+ #else
+ return 0;
+ #endif
+ }
+ // Detaches the current thread from a Java (Dalvik) VM.
+ THREAD_SAFE CUSTOM static int DetachCurrentThread()
+ {
+ #if UNITY_ANDROID
+ JavaVM* vm = GetJavaVm();
+ Assert(vm);
+ return vm->DetachCurrentThread();
+ #else
+ return 0;
+ #endif
+ }
+
+ // Returns the version of the native method interface.
+ THREAD_SAFE CUSTOM static int GetVersion() { JNI_PASS_RETV_VOID( GetVersion ); }
+
+ // This function loads a locally-defined class.
+ THREAD_SAFE CUSTOM static IntPtr FindClass(string name) { JNI_PASS_RETV_ARGS( FindClass, jStringWrapper(name) ); }
+
+ // Converts a <tt>java.lang.reflect.Method</tt> or <tt>java.lang.reflect.Constructor</tt> object to a method ID.
+ THREAD_SAFE CUSTOM static IntPtr FromReflectedMethod(IntPtr refMethod) { JNI_PASS_RETV_ARGS( FromReflectedMethod, (jobject)refMethod ); }
+ // Converts a <tt>java.lang.reflect.Field</tt> to a field ID.
+ THREAD_SAFE CUSTOM static IntPtr FromReflectedField(IntPtr refField) { JNI_PASS_RETV_ARGS( FromReflectedField, (jobject)refField ); }
+ // Converts a method ID derived from clazz to a <tt>java.lang.reflect.Method</tt> or <tt>java.lang.reflect.Constructor</tt> object.
+ THREAD_SAFE CUSTOM static IntPtr ToReflectedMethod(IntPtr clazz, IntPtr methodID, bool isStatic) { JNI_PASS_RETV_ARGS( ToReflectedMethod, (jclass)clazz, (jmethodID)methodID, isStatic ); }
+ // Converts a field ID derived from cls to a <tt>java.lang.reflect.Field</tt> object.
+ THREAD_SAFE CUSTOM static IntPtr ToReflectedField(IntPtr clazz, IntPtr fieldID, bool isStatic) { JNI_PASS_RETV_ARGS( ToReflectedField, (jclass)clazz, (jfieldID)fieldID, isStatic ); }
+
+ // If <tt>clazz</tt> represents any class other than the class <tt>Object</tt>, then this function returns the object that represents the superclass of the class specified by <tt>clazz</tt>.
+ THREAD_SAFE CUSTOM static IntPtr GetSuperclass(IntPtr clazz) { JNI_PASS_RETV_ARGS( GetSuperclass, (jclass)clazz ); }
+ // Determines whether an object of <tt>clazz1</tt> can be safely cast to <tt>clazz2</tt>.
+ THREAD_SAFE CUSTOM static bool IsAssignableFrom(IntPtr clazz1, IntPtr clazz2) { JNI_PASS_RETV_ARGS( IsAssignableFrom, (jclass)clazz1, (jclass)clazz2 ); }
+
+ // Causes a <tt>java.lang.Throwable</tt> object to be thrown.
+ THREAD_SAFE CUSTOM static int Throw(IntPtr obj) { JNI_PASS_RETV_ARGS( Throw, (jthrowable)obj ); }
+ // Constructs an exception object from the specified class with the <tt>message</tt> specified by message and causes that exception to be thrown.
+ THREAD_SAFE CUSTOM static int ThrowNew(IntPtr clazz, string message) { JNI_PASS_RETV_ARGS( ThrowNew, (jclass)clazz, jStringWrapper(message) ); }
+ // Determines if an exception is being thrown
+ THREAD_SAFE CUSTOM static IntPtr ExceptionOccurred() { JNI_PASS_RETV_VOID( ExceptionOccurred ); }
+ // Prints an exception and a backtrace of the stack to the <tt>logcat</tt>
+ THREAD_SAFE CUSTOM static void ExceptionDescribe() { JNI_PASS_VOID_VOID( ExceptionDescribe ); }
+ // Clears any exception that is currently being thrown.
+ THREAD_SAFE CUSTOM static void ExceptionClear() { JNI_PASS_VOID_VOID( ExceptionClear ); }
+ // Raises a fatal error and does not expect the VM to recover. This function does not return.
+ THREAD_SAFE CUSTOM static void FatalError(string message) { JNI_PASS_VOID_ARGS( FatalError, jStringWrapper(message) ); }
+
+ // Creates a new local reference frame, in which at least a given number of local references can be created.
+ THREAD_SAFE CUSTOM static int PushLocalFrame(int capacity) { JNI_PASS_RETV_ARGS( PushLocalFrame, capacity ); }
+ // Pops off the current local reference frame, frees all the local references, and returns a local reference in the previous local reference frame for the given <tt>result</tt> object.
+ THREAD_SAFE CUSTOM static IntPtr PopLocalFrame(IntPtr result) { JNI_PASS_RETV_ARGS( PopLocalFrame, (jobject)result ); }
+
+ // Creates a new global reference to the object referred to by the <tt>obj</tt> argument.
+ THREAD_SAFE CUSTOM static IntPtr NewGlobalRef(IntPtr obj) { JNI_PASS_RETV_ARGS( NewGlobalRef, (jobject)obj ); }
+ // Deletes the global reference pointed to by <tt>obj</tt>.
+ THREAD_SAFE CUSTOM static void DeleteGlobalRef(IntPtr obj) { JNI_PASS_VOID_ARGS( DeleteGlobalRef, (jobject)obj ); }
+ // Creates a new local reference that refers to the same object as <tt>obj</tt>.
+ THREAD_SAFE CUSTOM static IntPtr NewLocalRef(IntPtr obj) { JNI_PASS_RETV_ARGS( NewLocalRef, (jobject)obj ); }
+ // Deletes the local reference pointed to by <tt>obj</tt>.
+ THREAD_SAFE CUSTOM static void DeleteLocalRef(IntPtr obj) { JNI_PASS_VOID_ARGS( DeleteLocalRef, (jobject)obj ); }
+ // Tests whether two references refer to the same Java object.
+ THREAD_SAFE CUSTOM static bool IsSameObject(IntPtr obj1, IntPtr obj2) { JNI_PASS_RETV_ARGS( IsSameObject, (jobject)obj1, (jobject)obj2 ); }
+
+ // Ensures that at least a given number of local references can be created in the current thread.
+ THREAD_SAFE CUSTOM static int EnsureLocalCapacity(int capacity) { JNI_PASS_RETV_ARGS( EnsureLocalCapacity, capacity ); }
+
+
+ //-------------------------------------------
+
+ // Allocates a new Java object without invoking any of the constructors for the object.
+ THREAD_SAFE CUSTOM static IntPtr AllocObject(IntPtr clazz) { JNI_PASS_RETV_ARGS( AllocObject, (jclass)clazz ); }
+ // Constructs a new Java object. The method ID indicates which constructor method to invoke. This ID must be obtained by calling GetMethodID() with <init> as the method name and void (V) as the return type.
+ THREAD_SAFE CUSTOM static IntPtr NewObject(IntPtr clazz, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( NewObjectA, (jclass)clazz ); }
+
+ // Returns the class of an object.
+ THREAD_SAFE CUSTOM static IntPtr GetObjectClass(IntPtr obj) { JNI_PASS_RETV_ARGS( GetObjectClass, (jobject)obj ); }
+ // Tests whether an object is an instance of a class.
+ THREAD_SAFE CUSTOM static bool IsInstanceOf(IntPtr obj, IntPtr clazz){ JNI_PASS_RETV_ARGS( IsInstanceOf, (jobject)obj, (jclass)clazz ); }
+
+ // Returns the method ID for an instance (nonstatic) method of a class or interface.
+ THREAD_SAFE CUSTOM static IntPtr GetMethodID(IntPtr clazz, string name, string sig) { JNI_GET_ID(GetMethodID); }
+ // Returns the field ID for an instance (nonstatic) field of a class.
+ THREAD_SAFE CUSTOM static IntPtr GetFieldID(IntPtr clazz, string name, string sig) { JNI_GET_ID(GetFieldID); }
+ // Returns the method ID for a static method of a class.
+ THREAD_SAFE CUSTOM static IntPtr GetStaticMethodID(IntPtr clazz, string name, string sig) { JNI_GET_ID(GetStaticMethodID); }
+ // Returns the field ID for a static field of a class.
+ THREAD_SAFE CUSTOM static IntPtr GetStaticFieldID(IntPtr clazz, string name, string sig) { JNI_GET_ID(GetStaticFieldID); }
+
+ // Constructs a new <tt>java.lang.String</tt> object from an array of characters in modified UTF-8 encoding.
+ THREAD_SAFE CUSTOM static IntPtr NewStringUTF(string bytes) { JNI_PASS_RETV_ARGS( NewStringUTF, jStringWrapper(bytes) ); }
+ // Returns the length in bytes of the modified UTF-8 representation of a string.
+ THREAD_SAFE CUSTOM static int GetStringUTFLength(IntPtr str) { JNI_PASS_RETV_ARGS( GetStringUTFLength, (jstring) str); }
+ // Returns a managed string object representing the string in modified UTF-8 encoding.
+ THREAD_SAFE CUSTOM static string GetStringUTFChars(IntPtr str)
+ {
+ #if UNITY_ANDROID
+ SCOPED_JNI(__FUNCTION__);
+ if (!jni_env) return 0;
+ if (DEBUGJNI)
+ printf_console("> %s()", __FUNCTION__);
+ const char* cstring = jni_env->GetStringUTFChars((jstring)str, 0);
+ if (cstring == 0 || jni_env->ExceptionCheck())
+ {
+ jni_env->ReleaseStringUTFChars((jstring)str, cstring);
+ return 0;
+ }
+ MonoString* mstring = scripting_string_new(cstring);
+ jni_env->ReleaseStringUTFChars((jstring)str, cstring);
+ return mstring;
+ #else
+ return SCRIPTING_NULL;
+ #endif
+ }
+
+ //---------------------------------------------
+
+ // Calls an instance (nonstatic) Java method defined by <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static string CallStringMethod(IntPtr obj, IntPtr methodID, jvalue[] args) { JNI_CALL_JSTR_METHOD(CallObjectMethodA, (jobject)obj); }
+ // Calls an instance (nonstatic) Java method defined by <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static IntPtr CallObjectMethod(IntPtr obj, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( CallObjectMethodA, (jobject)obj ); }
+ // Calls an instance (nonstatic) Java method defined by <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static Int32 CallIntMethod(IntPtr obj, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( CallIntMethodA, (jobject)obj ); }
+ // Calls an instance (nonstatic) Java method defined by <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static bool CallBooleanMethod(IntPtr obj, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( CallBooleanMethodA, (jobject)obj ); }
+ // Calls an instance (nonstatic) Java method defined by <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static Int16 CallShortMethod(IntPtr obj, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( CallShortMethodA, (jobject)obj ); }
+ // Calls an instance (nonstatic) Java method defined by <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static Byte CallByteMethod(IntPtr obj, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( CallByteMethodA, (jobject)obj ); }
+ // Calls an instance (nonstatic) Java method defined by <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static Char CallCharMethod(IntPtr obj, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( CallCharMethodA, (jobject)obj ); }
+ // Calls an instance (nonstatic) Java method defined by <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static float CallFloatMethod(IntPtr obj, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( CallFloatMethodA, (jobject)obj ); }
+ // Calls an instance (nonstatic) Java method defined by <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static double CallDoubleMethod(IntPtr obj, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( CallDoubleMethodA, (jobject)obj ); }
+ // Calls an instance (nonstatic) Java method defined by <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static Int64 CallLongMethod(IntPtr obj, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( CallLongMethodA, (jobject)obj ); }
+ // Calls an instance (nonstatic) Java method defined by <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static void CallVoidMethod(IntPtr obj, IntPtr methodID, jvalue[] args) { JNI_CALL_VOID_METHOD( CallVoidMethodA, (jobject)obj ); }
+
+ //---------------------------------------------
+
+ // This function returns the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static string GetStringField(IntPtr obj, IntPtr fieldID) { JNI_GET_JSTR_FIELD( GetObjectField, (jobject)obj ); }
+ // This function returns the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static IntPtr GetObjectField(IntPtr obj, IntPtr fieldID) { JNI_GET_FIELD( GetObjectField, (jobject)obj ); }
+ // This function returns the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static bool GetBooleanField(IntPtr obj, IntPtr fieldID) { JNI_GET_FIELD( GetBooleanField, (jobject)obj ); }
+ // This function returns the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static Byte GetByteField(IntPtr obj, IntPtr fieldID) { JNI_GET_FIELD( GetByteField, (jobject)obj ); }
+ // This function returns the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static Char GetCharField(IntPtr obj, IntPtr fieldID) { JNI_GET_FIELD( GetCharField, (jobject)obj ); }
+ // This function returns the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static Int16 GetShortField(IntPtr obj, IntPtr fieldID) { JNI_GET_FIELD( GetShortField, (jobject)obj ); }
+ // This function returns the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static Int32 GetIntField(IntPtr obj, IntPtr fieldID) { JNI_GET_FIELD( GetIntField, (jobject)obj ); }
+ // This function returns the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static Int64 GetLongField(IntPtr obj, IntPtr fieldID) { JNI_GET_FIELD( GetLongField, (jobject)obj ); }
+ // This function returns the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static float GetFloatField(IntPtr obj, IntPtr fieldID) { JNI_GET_FIELD( GetFloatField, (jobject)obj ); }
+ // This function returns the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static double GetDoubleField(IntPtr obj, IntPtr fieldID) { JNI_GET_FIELD( GetDoubleField, (jobject)obj ); }
+
+ //---------------------------------------------
+
+ // This function sets the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static void SetStringField(IntPtr obj, IntPtr fieldID, string val) { JNI_PASS_VOID_ARGS( SetObjectField, (jobject)obj, (jfieldID)fieldID, jStringWrapper(val) ); }
+ // This function sets the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static void SetObjectField(IntPtr obj, IntPtr fieldID, IntPtr val) { JNI_SET_FIELD( SetObjectField, (jobject)obj, (jobject)val ); }
+ // This function sets the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static void SetBooleanField(IntPtr obj, IntPtr fieldID, bool val) { JNI_SET_FIELD( SetBooleanField, (jobject)obj, val ); }
+ // This function sets the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static void SetByteField(IntPtr obj, IntPtr fieldID, Byte val) { JNI_SET_FIELD( SetByteField, (jobject)obj, val ); }
+ // This function sets the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static void SetCharField(IntPtr obj, IntPtr fieldID, Char val) { JNI_SET_FIELD( SetCharField, (jobject)obj, val ); }
+ // This function sets the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static void SetShortField(IntPtr obj, IntPtr fieldID, Int16 val) { JNI_SET_FIELD( SetShortField, (jobject)obj, val ); }
+ // This function sets the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static void SetIntField(IntPtr obj, IntPtr fieldID, Int32 val) { JNI_SET_FIELD( SetIntField, (jobject)obj, val ); }
+ // This function sets the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static void SetLongField(IntPtr obj, IntPtr fieldID, Int64 val) { JNI_SET_FIELD( SetLongField, (jobject)obj, val ); }
+ // This function sets the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static void SetFloatField(IntPtr obj, IntPtr fieldID, float val) { JNI_SET_FIELD( SetFloatField, (jobject)obj, val ); }
+ // This function sets the value of an instance (nonstatic) field of an object.
+ THREAD_SAFE CUSTOM static void SetDoubleField(IntPtr obj, IntPtr fieldID, double val) { JNI_SET_FIELD( SetDoubleField, (jobject)obj, val ); }
+
+
+ //---------------------------------------------
+
+ // Invokes a static method on a Java object, according to the specified <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static string CallStaticStringMethod(IntPtr clazz, IntPtr methodID, jvalue[] args) { JNI_CALL_JSTR_METHOD(CallStaticObjectMethodA, (jclass)clazz); }
+ // Invokes a static method on a Java object, according to the specified <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static IntPtr CallStaticObjectMethod(IntPtr clazz, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( CallStaticObjectMethodA, (jclass)clazz ); }
+ // Invokes a static method on a Java object, according to the specified <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static Int32 CallStaticIntMethod(IntPtr clazz, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( CallStaticIntMethodA, (jclass)clazz ); }
+ // Invokes a static method on a Java object, according to the specified <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static bool CallStaticBooleanMethod(IntPtr clazz, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( CallStaticBooleanMethodA, (jclass)clazz ); }
+ // Invokes a static method on a Java object, according to the specified <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static Int16 CallStaticShortMethod(IntPtr clazz, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( CallStaticShortMethodA, (jclass)clazz ); }
+ // Invokes a static method on a Java object, according to the specified <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static Byte CallStaticByteMethod(IntPtr clazz, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( CallStaticByteMethodA, (jclass)clazz ); }
+ // Invokes a static method on a Java object, according to the specified <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static Char CallStaticCharMethod(IntPtr clazz, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( CallStaticCharMethodA, (jclass)clazz ); }
+ // Invokes a static method on a Java object, according to the specified <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static float CallStaticFloatMethod(IntPtr clazz, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( CallStaticFloatMethodA, (jclass)clazz ); }
+ // Invokes a static method on a Java object, according to the specified <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static double CallStaticDoubleMethod(IntPtr clazz, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( CallStaticDoubleMethodA, (jclass)clazz ); }
+ // Invokes a static method on a Java object, according to the specified <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static Int64 CallStaticLongMethod(IntPtr clazz, IntPtr methodID, jvalue[] args) { JNI_CALL_METHOD( CallStaticLongMethodA, (jclass)clazz ); }
+ // Invokes a static method on a Java object, according to the specified <tt>methodID</tt>, optionally passing an array of arguments (<tt>args</tt>) to the method.
+ THREAD_SAFE CUSTOM static void CallStaticVoidMethod(IntPtr clazz, IntPtr methodID, jvalue[] args) { JNI_CALL_VOID_METHOD( CallStaticVoidMethodA, (jclass)clazz ); }
+
+ //---------------------------------------------
+
+ // This function returns the value of a static field of an object.
+ THREAD_SAFE CUSTOM static string GetStaticStringField(IntPtr clazz, IntPtr fieldID) { JNI_GET_JSTR_FIELD( GetStaticObjectField, (jclass)clazz ); }
+ // This function returns the value of a static field of an object.
+ THREAD_SAFE CUSTOM static IntPtr GetStaticObjectField(IntPtr clazz, IntPtr fieldID) { JNI_GET_FIELD( GetStaticObjectField, (jclass)clazz ); }
+ // This function returns the value of a static field of an object.
+ THREAD_SAFE CUSTOM static bool GetStaticBooleanField(IntPtr clazz, IntPtr fieldID) { JNI_GET_FIELD( GetStaticBooleanField, (jclass)clazz ); }
+ // This function returns the value of a static field of an object.
+ THREAD_SAFE CUSTOM static Byte GetStaticByteField(IntPtr clazz, IntPtr fieldID) { JNI_GET_FIELD( GetStaticByteField, (jclass)clazz ); }
+ // This function returns the value of a static field of an object.
+ THREAD_SAFE CUSTOM static Char GetStaticCharField(IntPtr clazz, IntPtr fieldID) { JNI_GET_FIELD( GetStaticCharField, (jclass)clazz ); }
+ // This function returns the value of a static field of an object.
+ THREAD_SAFE CUSTOM static Int16 GetStaticShortField(IntPtr clazz, IntPtr fieldID) { JNI_GET_FIELD( GetStaticShortField, (jclass)clazz ); }
+ // This function returns the value of a static field of an object.
+ THREAD_SAFE CUSTOM static Int32 GetStaticIntField(IntPtr clazz, IntPtr fieldID) { JNI_GET_FIELD( GetStaticIntField, (jclass)clazz ); }
+ // This function returns the value of a static field of an object.
+ THREAD_SAFE CUSTOM static Int64 GetStaticLongField(IntPtr clazz, IntPtr fieldID) { JNI_GET_FIELD( GetStaticLongField, (jclass)clazz ); }
+ // This function returns the value of a static field of an object.
+ THREAD_SAFE CUSTOM static float GetStaticFloatField(IntPtr clazz, IntPtr fieldID) { JNI_GET_FIELD( GetStaticFloatField, (jclass)clazz ); }
+ // This function returns the value of a static field of an object.
+ THREAD_SAFE CUSTOM static double GetStaticDoubleField(IntPtr clazz, IntPtr fieldID) { JNI_GET_FIELD( GetStaticDoubleField, (jclass)clazz ); }
+
+ //---------------------------------------------
+
+ // This function ets the value of a static field of an object.
+ THREAD_SAFE CUSTOM static void SetStaticStringField(IntPtr clazz, IntPtr fieldID, string val) { JNI_PASS_VOID_ARGS( SetStaticObjectField, (jclass)clazz, (jfieldID)fieldID, jStringWrapper(val) ); }
+ // This function ets the value of a static field of an object.
+ THREAD_SAFE CUSTOM static void SetStaticObjectField(IntPtr clazz, IntPtr fieldID, IntPtr val) { JNI_SET_FIELD( SetStaticObjectField, (jclass)clazz, (jclass)val ); }
+ // This function ets the value of a static field of an object.
+ THREAD_SAFE CUSTOM static void SetStaticBooleanField(IntPtr clazz, IntPtr fieldID, bool val) { JNI_SET_FIELD( SetStaticBooleanField, (jclass)clazz, val ); }
+ // This function ets the value of a static field of an object.
+ THREAD_SAFE CUSTOM static void SetStaticByteField(IntPtr clazz, IntPtr fieldID, Byte val) { JNI_SET_FIELD( SetStaticByteField, (jclass)clazz, val ); }
+ // This function ets the value of a static field of an object.
+ THREAD_SAFE CUSTOM static void SetStaticCharField(IntPtr clazz, IntPtr fieldID, Char val) { JNI_SET_FIELD( SetStaticCharField, (jclass)clazz, val ); }
+ // This function ets the value of a static field of an object.
+ THREAD_SAFE CUSTOM static void SetStaticShortField(IntPtr clazz, IntPtr fieldID, Int16 val) { JNI_SET_FIELD( SetStaticShortField, (jclass)clazz, val ); }
+ // This function ets the value of a static field of an object.
+ THREAD_SAFE CUSTOM static void SetStaticIntField(IntPtr clazz, IntPtr fieldID, Int32 val) { JNI_SET_FIELD( SetStaticIntField, (jclass)clazz, val ); }
+ // This function ets the value of a static field of an object.
+ THREAD_SAFE CUSTOM static void SetStaticLongField(IntPtr clazz, IntPtr fieldID, Int64 val) { JNI_SET_FIELD( SetStaticLongField, (jclass)clazz, val ); }
+ // This function ets the value of a static field of an object.
+ THREAD_SAFE CUSTOM static void SetStaticFloatField(IntPtr clazz, IntPtr fieldID, float val) { JNI_SET_FIELD( SetStaticFloatField, (jclass)clazz, val ); }
+ // This function ets the value of a static field of an object.
+ THREAD_SAFE CUSTOM static void SetStaticDoubleField(IntPtr clazz, IntPtr fieldID, double val) { JNI_SET_FIELD( SetStaticDoubleField, (jclass)clazz, val ); }
+
+
+//---------------------------------------
+
+ // Convert a managed array of System.Boolean to a Java array of <tt>boolean</tt>.
+ THREAD_SAFE CUSTOM static IntPtr ToBooleanArray(Boolean[] array) { JNI_NEW_ARRAY( Boolean, jboolean, jbooleanArray );}
+ // Convert a managed array of System.Byte to a Java array of <tt>byte</tt>.
+ THREAD_SAFE CUSTOM static IntPtr ToByteArray(Byte[] array) { JNI_NEW_ARRAY( Byte, jbyte, jbyteArray ); }
+ // Convert a managed array of System.Char to a Java array of <tt>char</tt>.
+ THREAD_SAFE CUSTOM static IntPtr ToCharArray(Char[] array) { JNI_NEW_ARRAY( Char, jchar, jcharArray ); }
+ // Convert a managed array of System.Int16 to a Java array of <tt>short</tt>.
+ THREAD_SAFE CUSTOM static IntPtr ToShortArray(Int16[] array) { JNI_NEW_ARRAY( Short, jshort, jshortArray ); }
+ // Convert a managed array of System.Int32 to a Java array of <tt>int</tt>.
+ THREAD_SAFE CUSTOM static IntPtr ToIntArray(Int32[] array) { JNI_NEW_ARRAY( Int, jint, jintArray ); }
+ // Convert a managed array of System.Int64 to a Java array of <tt>long</tt>.
+ THREAD_SAFE CUSTOM static IntPtr ToLongArray(Int64[] array) { JNI_NEW_ARRAY( Long, jlong, jlongArray ); }
+ // Convert a managed array of System.Single to a Java array of <tt>float</tt>.
+ THREAD_SAFE CUSTOM static IntPtr ToFloatArray(float[] array) { JNI_NEW_ARRAY( Float, jfloat, jfloatArray ); }
+ // Convert a managed array of System.Double to a Java array of <tt>double</tt>.
+ THREAD_SAFE CUSTOM static IntPtr ToDoubleArray(double[] array) { JNI_NEW_ARRAY( Double, jdouble, jdoubleArray ); }
+ // Convert a managed array of System.IntPtr, representing Java objects, to a Java array of <tt>java.lang.Object</tt>.
+ THREAD_SAFE CUSTOM static IntPtr ToObjectArray(IntPtr[] array)
+ {
+ #if UNITY_ANDROID
+ SCOPED_JNI(__FUNCTION__);
+ if (!jni_env) return 0;
+ int size = mono_array_length(array);
+ jclass obj_class = jni_env->FindClass("java/lang/Object");
+ if (obj_class == 0 || jni_env->ExceptionCheck()) return SCRIPTING_NULL;
+ jobjectArray jni_array = jni_env->NewObjectArray(size, obj_class, 0);
+ if (jni_array == 0 || jni_env->ExceptionCheck())
+ {
+ jni_env->DeleteLocalRef(obj_class);
+ return SCRIPTING_NULL;
+ }
+ for (int i = 0; i < size; ++i)
+ {
+ void* val = GetMonoArrayElement<void*>(array, i);
+ jni_env->SetObjectArrayElement(jni_array, i, (jobject)val);
+ jni_env->DeleteLocalRef((jobject) val);
+ if (jni_env->ExceptionCheck())
+ {
+ jni_env->DeleteLocalRef(jni_array);
+ jni_env->DeleteLocalRef(obj_class);
+ return SCRIPTING_NULL;
+ }
+ }
+ jni_env->DeleteLocalRef(obj_class);
+ return jni_array;
+ #else
+ return 0;
+ #endif
+ }
+
+ // Convert a Java array of <tt>boolean</tt> to a managed array of System.Boolean.
+ THREAD_SAFE CUSTOM static Boolean[] FromBooleanArray(IntPtr array) { JNI_GET_ARRAY( Boolean, jboolean, jbooleanArray, mono_get_boolean_class() ); }
+ // Convert a Java array of <tt>byte</tt> to a managed array of System.Byte.
+ THREAD_SAFE CUSTOM static Byte[] FromByteArray(IntPtr array) { JNI_GET_ARRAY( Byte, jbyte, jbyteArray, mono_get_byte_class() ); }
+ // Convert a Java array of <tt>char</tt> to a managed array of System.Char.
+ THREAD_SAFE CUSTOM static Char[] FromCharArray(IntPtr array) { JNI_GET_ARRAY( Char, jchar, jcharArray, mono_get_char_class() ); }
+ // Convert a Java array of <tt>short</tt> to a managed array of System.Int16.
+ THREAD_SAFE CUSTOM static Int16[] FromShortArray(IntPtr array) { JNI_GET_ARRAY( Short, jshort, jshortArray, mono_get_int16_class() ); }
+ // Convert a Java array of <tt>int</tt> to a managed array of System.Int32.
+ THREAD_SAFE CUSTOM static Int32[] FromIntArray(IntPtr array) { JNI_GET_ARRAY( Int, jint, jintArray, mono_get_int32_class() ); }
+ // Convert a Java array of <tt>long</tt> to a managed array of System.Int64.
+ THREAD_SAFE CUSTOM static Int64[] FromLongArray(IntPtr array) { JNI_GET_ARRAY( Long, jlong, jlongArray, mono_get_int64_class() ); }
+ // Convert a Java array of <tt>float</tt> to a managed array of System.Single.
+ THREAD_SAFE CUSTOM static float[] FromFloatArray(IntPtr array) { JNI_GET_ARRAY( Float, jfloat, jfloatArray, mono_get_single_class() ); }
+ // Convert a Java array of <tt>double</tt> to a managed array of System.Double.
+ THREAD_SAFE CUSTOM static double[] FromDoubleArray(IntPtr array) { JNI_GET_ARRAY( Double, jdouble, jdoubleArray, mono_get_double_class() ); }
+ // Convert a Java array of <tt>java.lang.Object</tt> to a managed array of System.IntPtr, representing Java objects.
+ THREAD_SAFE CUSTOM static IntPtr[] FromObjectArray(IntPtr array)
+ {
+ #if UNITY_ANDROID
+ SCOPED_JNI(__FUNCTION__);
+ if (!jni_env) return 0;
+ jsize length = jni_env->GetArrayLength((jarray)array);
+ if (jni_env->ExceptionCheck()) return SCRIPTING_NULL;
+ MonoClass* clazz = mono_class_from_name (mono_get_corlib (), "System", "IntPtr");
+ MonoArray* csarray = mono_array_new(mono_domain_get(), clazz, length);
+ for (int i = 0; i < length; ++i)
+ {
+ jobject obj = jni_env->GetObjectArrayElement((jobjectArray)array, i);
+ if (jni_env->ExceptionCheck()) return SCRIPTING_NULL;
+ Scripting::SetScriptingArrayElement<jobject>(csarray, i, obj);
+ }
+ return csarray;
+ #else
+ return SCRIPTING_NULL;
+ #endif
+ }
+
+ // Returns the number of elements in the array.
+ THREAD_SAFE CUSTOM static int GetArrayLength(IntPtr array) { JNI_PASS_RETV_ARGS( GetArrayLength, (jarray) array); }
+
+ // Construct a new primitive array object.
+ THREAD_SAFE CUSTOM static IntPtr NewBooleanArray(int size) { JNI_PASS_RETV_ARGS( NewBooleanArray, (jsize)size);}
+ // Construct a new primitive array object.
+ THREAD_SAFE CUSTOM static IntPtr NewByteArray(int size) { JNI_PASS_RETV_ARGS( NewByteArray, (jsize)size); }
+ // Construct a new primitive array object.
+ THREAD_SAFE CUSTOM static IntPtr NewCharArray(int size) { JNI_PASS_RETV_ARGS( NewCharArray, (jsize)size); }
+ // Construct a new primitive array object.
+ THREAD_SAFE CUSTOM static IntPtr NewShortArray(int size) { JNI_PASS_RETV_ARGS( NewShortArray, (jsize)size); }
+ // Construct a new primitive array object.
+ THREAD_SAFE CUSTOM static IntPtr NewIntArray(int size) { JNI_PASS_RETV_ARGS( NewIntArray, (jsize)size); }
+ // Construct a new primitive array object.
+ THREAD_SAFE CUSTOM static IntPtr NewLongArray(int size) { JNI_PASS_RETV_ARGS( NewLongArray, (jsize)size); }
+ // Construct a new primitive array object.
+ THREAD_SAFE CUSTOM static IntPtr NewFloatArray(int size) { JNI_PASS_RETV_ARGS( NewFloatArray, (jsize)size); }
+ // Construct a new primitive array object.
+ THREAD_SAFE CUSTOM static IntPtr NewDoubleArray(int size) { JNI_PASS_RETV_ARGS( NewDoubleArray, (jsize)size); }
+ // Constructs a new array holding objects in class <tt>clazz</tt>. All elements are initially set to <tt>obj</tt>.
+ THREAD_SAFE CUSTOM static IntPtr NewObjectArray(int size, IntPtr clazz, IntPtr obj) { JNI_PASS_RETV_ARGS( NewObjectArray, (jsize)size, (jclass)clazz, (jobject) obj); }
+
+ // Returns the value of one element of a primitive array.
+ THREAD_SAFE CUSTOM static bool GetBooleanArrayElement(IntPtr array, int index) { JNI_GET_ARRAY_ELEMENT( Boolean, jboolean, jbooleanArray ); }
+ // Returns the value of one element of a primitive array.
+ THREAD_SAFE CUSTOM static Byte GetByteArrayElement(IntPtr array, int index) { JNI_GET_ARRAY_ELEMENT( Byte, jbyte, jbyteArray ); }
+ // Returns the value of one element of a primitive array.
+ THREAD_SAFE CUSTOM static Char GetCharArrayElement(IntPtr array, int index) { JNI_GET_ARRAY_ELEMENT( Char, jchar, jcharArray ); }
+ // Returns the value of one element of a primitive array.
+ THREAD_SAFE CUSTOM static Int16 GetShortArrayElement(IntPtr array, int index) { JNI_GET_ARRAY_ELEMENT( Short, jshort, jshortArray ); }
+ // Returns the value of one element of a primitive array.
+ THREAD_SAFE CUSTOM static Int32 GetIntArrayElement(IntPtr array, int index) { JNI_GET_ARRAY_ELEMENT( Int, jint, jintArray ); }
+ // Returns the value of one element of a primitive array.
+ THREAD_SAFE CUSTOM static Int64 GetLongArrayElement(IntPtr array, int index) { JNI_GET_ARRAY_ELEMENT( Long, jlong, jlongArray ); }
+ // Returns the value of one element of a primitive array.
+ THREAD_SAFE CUSTOM static float GetFloatArrayElement(IntPtr array, int index) { JNI_GET_ARRAY_ELEMENT( Float, jfloat, jfloatArray ); }
+ // Returns the value of one element of a primitive array.
+ THREAD_SAFE CUSTOM static double GetDoubleArrayElement(IntPtr array, int index) { JNI_GET_ARRAY_ELEMENT( Double, jdouble, jdoubleArray ); }
+ // Returns an element of an <tt>Object</tt> array.
+ THREAD_SAFE CUSTOM static IntPtr GetObjectArrayElement(IntPtr array, int index) { JNI_PASS_RETV_ARGS( GetObjectArrayElement, (jobjectArray)array, (jsize)index); }
+
+ // Sets the value of one element in a primitive array.
+ THREAD_SAFE CUSTOM static void SetBooleanArrayElement(IntPtr array, int index, byte val) { JNI_SET_ARRAY_ELEMENT( Boolean, jboolean, jbooleanArray ); }
+ // Sets the value of one element in a primitive array.
+ THREAD_SAFE CUSTOM static void SetByteArrayElement(IntPtr array, int index, sbyte val) { JNI_SET_ARRAY_ELEMENT( Byte, jbyte, jbyteArray ); }
+ // Sets the value of one element in a primitive array.
+ THREAD_SAFE CUSTOM static void SetCharArrayElement(IntPtr array, int index, Char val) { JNI_SET_ARRAY_ELEMENT( Char, jchar, jcharArray ); }
+ // Sets the value of one element in a primitive array.
+ THREAD_SAFE CUSTOM static void SetShortArrayElement(IntPtr array, int index, Int16 val) { JNI_SET_ARRAY_ELEMENT( Short, jshort, jshortArray ); }
+ // Sets the value of one element in a primitive array.
+ THREAD_SAFE CUSTOM static void SetIntArrayElement(IntPtr array, int index, Int32 val) { JNI_SET_ARRAY_ELEMENT( Int, jint, jintArray ); }
+ // Sets the value of one element in a primitive array.
+ THREAD_SAFE CUSTOM static void SetLongArrayElement(IntPtr array, int index, Int64 val) { JNI_SET_ARRAY_ELEMENT( Long, jlong, jlongArray ); }
+ // Sets the value of one element in a primitive array.
+ THREAD_SAFE CUSTOM static void SetFloatArrayElement(IntPtr array, int index, float val) { JNI_SET_ARRAY_ELEMENT( Float, jfloat, jfloatArray ); }
+ // Sets the value of one element in a primitive array.
+ THREAD_SAFE CUSTOM static void SetDoubleArrayElement(IntPtr array, int index, double val) { JNI_SET_ARRAY_ELEMENT( Double, jdouble, jdoubleArray ); }
+ // Sets an element of an <tt>Object</tt> array.
+ THREAD_SAFE CUSTOM static void SetObjectArrayElement(IntPtr array, int index, IntPtr obj) { JNI_PASS_VOID_ARGS( SetObjectArrayElement, (jobjectArray)array, (jsize)index, (jobject)obj); }
+END
+
+
+CSRAW
+}
+#endif
diff --git a/Runtime/Export/AndroidJNISafe.cs b/Runtime/Export/AndroidJNISafe.cs
new file mode 100644
index 0000000..8883059
--- /dev/null
+++ b/Runtime/Export/AndroidJNISafe.cs
@@ -0,0 +1,514 @@
+#if UNITY_EDITOR || UNITY_ANDROID
+
+using System;
+
+namespace UnityEngine
+{
+internal class AndroidJNISafe
+{
+ public static void CheckException()
+ {
+ IntPtr jthrowable = AndroidJNI.ExceptionOccurred();
+ if (jthrowable != IntPtr.Zero)
+ {
+ AndroidJNI.ExceptionClear();
+ IntPtr jthrowableClass = AndroidJNI.FindClass("java/lang/Throwable");
+ try
+ {
+ IntPtr toStringMethodId = AndroidJNI.GetMethodID(jthrowableClass, "toString", "()Ljava/lang/String;");
+ throw new AndroidJavaException(AndroidJNI.CallStringMethod(jthrowable, toStringMethodId, new jvalue[]{}));
+ }
+ finally
+ {
+ AndroidJNISafe.DeleteLocalRef(jthrowable);
+ AndroidJNISafe.DeleteLocalRef(jthrowableClass);
+ }
+ }
+ }
+
+ public static void DeleteGlobalRef(IntPtr globalref)
+ {
+ if (globalref != IntPtr.Zero) AndroidJNI.DeleteGlobalRef(globalref);
+ }
+
+ public static void DeleteLocalRef(IntPtr localref)
+ {
+ if (localref != IntPtr.Zero) AndroidJNI.DeleteLocalRef(localref);
+ }
+
+ public static IntPtr NewStringUTF(string bytes)
+ {
+ try { return AndroidJNI.NewStringUTF(bytes); } finally { CheckException(); }
+ }
+
+ public static string GetStringUTFChars(IntPtr str)
+ {
+ try { return AndroidJNI.GetStringUTFChars(str); } finally { CheckException(); }
+ }
+
+ public static IntPtr GetObjectClass(IntPtr ptr)
+ {
+ try { return AndroidJNI.GetObjectClass(ptr); } finally { CheckException(); }
+ }
+
+ public static IntPtr GetStaticMethodID(IntPtr clazz, string name, string sig)
+ {
+ try { return AndroidJNI.GetStaticMethodID(clazz, name, sig); } finally { CheckException(); }
+ }
+
+ public static IntPtr GetMethodID(IntPtr obj, string name, string sig)
+ {
+ try { return AndroidJNI.GetMethodID(obj, name, sig); } finally { CheckException(); }
+ }
+
+ public static IntPtr GetFieldID(IntPtr clazz, string name, string sig)
+ {
+ try { return AndroidJNI.GetFieldID(clazz, name, sig); } finally { CheckException(); }
+ }
+
+ public static IntPtr GetStaticFieldID(IntPtr clazz, string name, string sig)
+ {
+ try { return AndroidJNI.GetStaticFieldID(clazz, name, sig); } finally { CheckException(); }
+ }
+
+ public static IntPtr FromReflectedMethod(IntPtr refMethod)
+ {
+ try { return AndroidJNI.FromReflectedMethod(refMethod); } finally { CheckException(); }
+ }
+
+ public static IntPtr FromReflectedField(IntPtr refField)
+ {
+ try { return AndroidJNI.FromReflectedField(refField); } finally { CheckException(); }
+ }
+
+ public static IntPtr FindClass(string name)
+ {
+ try { return AndroidJNI.FindClass(name); } finally { CheckException(); }
+ }
+
+ public static IntPtr NewObject(IntPtr clazz, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.NewObject(clazz, methodID, args); } finally { CheckException(); }
+ }
+
+
+ public static void SetStaticObjectField(IntPtr clazz, IntPtr fieldID, IntPtr val)
+ {
+ try { AndroidJNI.SetStaticObjectField(clazz, fieldID, val); } finally { CheckException(); }
+ }
+
+ public static void SetStaticStringField(IntPtr clazz, IntPtr fieldID, string val)
+ {
+ try { AndroidJNI.SetStaticStringField(clazz, fieldID, val); } finally { CheckException(); }
+ }
+
+ public static void SetStaticCharField(IntPtr clazz, IntPtr fieldID, Char val)
+ {
+ try { AndroidJNI.SetStaticCharField(clazz, fieldID, val); } finally { CheckException(); }
+ }
+
+ public static void SetStaticDoubleField(IntPtr clazz, IntPtr fieldID, double val)
+ {
+ try { AndroidJNI.SetStaticDoubleField(clazz, fieldID, val); } finally { CheckException(); }
+ }
+
+ public static void SetStaticFloatField(IntPtr clazz, IntPtr fieldID, float val)
+ {
+ try { AndroidJNI.SetStaticFloatField(clazz, fieldID, val); } finally { CheckException(); }
+ }
+
+ public static void SetStaticLongField(IntPtr clazz, IntPtr fieldID, Int64 val)
+ {
+ try { AndroidJNI.SetStaticLongField(clazz, fieldID, val); } finally { CheckException(); }
+ }
+
+ public static void SetStaticShortField(IntPtr clazz, IntPtr fieldID, Int16 val)
+ {
+ try { AndroidJNI.SetStaticShortField(clazz, fieldID, val); } finally { CheckException(); }
+ }
+
+ public static void SetStaticByteField(IntPtr clazz, IntPtr fieldID, Byte val)
+ {
+ try { AndroidJNI.SetStaticByteField(clazz, fieldID, val); } finally { CheckException(); }
+ }
+
+ public static void SetStaticBooleanField(IntPtr clazz, IntPtr fieldID, bool val)
+ {
+ try { AndroidJNI.SetStaticBooleanField(clazz, fieldID, val); } finally { CheckException(); }
+ }
+
+ public static void SetStaticIntField(IntPtr clazz, IntPtr fieldID, Int32 val)
+ {
+ try { AndroidJNI.SetStaticIntField(clazz, fieldID, val); } finally { CheckException(); }
+ }
+
+
+ public static IntPtr GetStaticObjectField(IntPtr clazz, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetStaticObjectField(clazz, fieldID); } finally { CheckException(); }
+ }
+
+ public static string GetStaticStringField(IntPtr clazz, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetStaticStringField(clazz, fieldID); } finally { CheckException(); }
+ }
+
+ public static Char GetStaticCharField(IntPtr clazz, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetStaticCharField(clazz, fieldID); } finally { CheckException(); }
+ }
+
+ public static double GetStaticDoubleField(IntPtr clazz, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetStaticDoubleField(clazz, fieldID); } finally { CheckException(); }
+ }
+
+ public static float GetStaticFloatField(IntPtr clazz, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetStaticFloatField(clazz, fieldID); } finally { CheckException(); }
+ }
+
+ public static Int64 GetStaticLongField(IntPtr clazz, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetStaticLongField(clazz, fieldID); } finally { CheckException(); }
+ }
+
+ public static Int16 GetStaticShortField(IntPtr clazz, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetStaticShortField(clazz, fieldID); } finally { CheckException(); }
+ }
+
+ public static Byte GetStaticByteField(IntPtr clazz, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetStaticByteField(clazz, fieldID); } finally { CheckException(); }
+ }
+
+ public static bool GetStaticBooleanField(IntPtr clazz, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetStaticBooleanField(clazz, fieldID); } finally { CheckException(); }
+ }
+
+ public static Int32 GetStaticIntField(IntPtr clazz, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetStaticIntField(clazz, fieldID); } finally { CheckException(); }
+ }
+
+
+ public static void CallStaticVoidMethod(IntPtr clazz, IntPtr methodID, jvalue[] args)
+ {
+ try { AndroidJNI.CallStaticVoidMethod(clazz, methodID, args); } finally { CheckException(); }
+ }
+
+ public static IntPtr CallStaticObjectMethod(IntPtr clazz, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallStaticObjectMethod(clazz, methodID, args); } finally { CheckException(); }
+ }
+
+ public static string CallStaticStringMethod(IntPtr clazz, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallStaticStringMethod(clazz, methodID, args); } finally { CheckException(); }
+ }
+
+ public static Char CallStaticCharMethod(IntPtr clazz, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallStaticCharMethod(clazz, methodID, args); } finally { CheckException(); }
+ }
+
+ public static double CallStaticDoubleMethod(IntPtr clazz, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallStaticDoubleMethod(clazz, methodID, args); } finally { CheckException(); }
+ }
+
+ public static float CallStaticFloatMethod(IntPtr clazz, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallStaticFloatMethod(clazz, methodID, args); } finally { CheckException(); }
+ }
+
+ public static Int64 CallStaticLongMethod(IntPtr clazz, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallStaticLongMethod(clazz, methodID, args); } finally { CheckException(); }
+ }
+
+ public static Int16 CallStaticShortMethod(IntPtr clazz, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallStaticShortMethod(clazz, methodID, args); } finally { CheckException(); }
+ }
+
+ public static Byte CallStaticByteMethod(IntPtr clazz, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallStaticByteMethod(clazz, methodID, args); } finally { CheckException(); }
+ }
+
+ public static bool CallStaticBooleanMethod(IntPtr clazz, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallStaticBooleanMethod(clazz, methodID, args); } finally { CheckException(); }
+ }
+
+ public static Int32 CallStaticIntMethod(IntPtr clazz, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallStaticIntMethod(clazz, methodID, args); } finally { CheckException(); }
+ }
+
+
+ public static void SetObjectField(IntPtr obj, IntPtr fieldID, IntPtr val)
+ {
+ try { AndroidJNI.SetObjectField(obj, fieldID, val); } finally { CheckException(); }
+ }
+
+ public static void SetStringField(IntPtr obj, IntPtr fieldID, string val)
+ {
+ try { AndroidJNI.SetStringField(obj, fieldID, val); } finally { CheckException(); }
+ }
+
+ public static void SetCharField(IntPtr obj, IntPtr fieldID, Char val)
+ {
+ try { AndroidJNI.SetCharField(obj, fieldID, val); } finally { CheckException(); }
+ }
+
+ public static void SetDoubleField(IntPtr obj, IntPtr fieldID, double val)
+ {
+ try { AndroidJNI.SetDoubleField(obj, fieldID, val); } finally { CheckException(); }
+ }
+
+ public static void SetFloatField(IntPtr obj, IntPtr fieldID, float val)
+ {
+ try { AndroidJNI.SetFloatField(obj, fieldID, val); } finally { CheckException(); }
+ }
+
+ public static void SetLongField(IntPtr obj, IntPtr fieldID, Int64 val)
+ {
+ try { AndroidJNI.SetLongField(obj, fieldID, val); } finally { CheckException(); }
+ }
+
+ public static void SetShortField(IntPtr obj, IntPtr fieldID, Int16 val)
+ {
+ try { AndroidJNI.SetShortField(obj, fieldID, val); } finally { CheckException(); }
+ }
+
+ public static void SetByteField(IntPtr obj, IntPtr fieldID, Byte val)
+ {
+ try { AndroidJNI.SetByteField(obj, fieldID, val); } finally { CheckException(); }
+ }
+
+ public static void SetBooleanField(IntPtr obj, IntPtr fieldID, bool val)
+ {
+ try { AndroidJNI.SetBooleanField(obj, fieldID, val); } finally { CheckException(); }
+ }
+
+ public static void SetIntField(IntPtr obj, IntPtr fieldID, Int32 val)
+ {
+ try { AndroidJNI.SetIntField(obj, fieldID, val); } finally { CheckException(); }
+ }
+
+
+ public static IntPtr GetObjectField(IntPtr obj, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetObjectField(obj, fieldID); } finally { CheckException(); }
+ }
+
+ public static string GetStringField(IntPtr obj, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetStringField(obj, fieldID); } finally { CheckException(); }
+ }
+
+ public static Char GetCharField(IntPtr obj, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetCharField(obj, fieldID); } finally { CheckException(); }
+ }
+
+ public static double GetDoubleField(IntPtr obj, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetDoubleField(obj, fieldID); } finally { CheckException(); }
+ }
+
+ public static float GetFloatField(IntPtr obj, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetFloatField(obj, fieldID); } finally { CheckException(); }
+ }
+
+ public static Int64 GetLongField(IntPtr obj, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetLongField(obj, fieldID); } finally { CheckException(); }
+ }
+
+ public static Int16 GetShortField(IntPtr obj, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetShortField(obj, fieldID); } finally { CheckException(); }
+ }
+
+ public static Byte GetByteField(IntPtr obj, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetByteField(obj, fieldID); } finally { CheckException(); }
+ }
+
+ public static bool GetBooleanField(IntPtr obj, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetBooleanField(obj, fieldID); } finally { CheckException(); }
+ }
+
+ public static Int32 GetIntField(IntPtr obj, IntPtr fieldID)
+ {
+ try { return AndroidJNI.GetIntField(obj, fieldID); } finally { CheckException(); }
+ }
+
+
+ public static void CallVoidMethod(IntPtr obj, IntPtr methodID, jvalue[] args)
+ {
+ try { AndroidJNI.CallVoidMethod(obj, methodID, args); } finally { CheckException(); }
+ }
+
+ public static IntPtr CallObjectMethod(IntPtr obj, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallObjectMethod(obj, methodID, args); } finally { CheckException(); }
+ }
+
+ public static string CallStringMethod(IntPtr obj, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallStringMethod(obj, methodID, args); } finally { CheckException(); }
+ }
+
+ public static Char CallCharMethod(IntPtr obj, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallCharMethod(obj, methodID, args); } finally { CheckException(); }
+ }
+
+ public static double CallDoubleMethod(IntPtr obj, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallDoubleMethod(obj, methodID, args); } finally { CheckException(); }
+ }
+
+ public static float CallFloatMethod(IntPtr obj, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallFloatMethod(obj, methodID, args); } finally { CheckException(); }
+ }
+
+ public static Int64 CallLongMethod(IntPtr obj, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallLongMethod(obj, methodID, args); } finally { CheckException(); }
+ }
+
+ public static Int16 CallShortMethod(IntPtr obj, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallShortMethod(obj, methodID, args); } finally { CheckException(); }
+ }
+
+ public static Byte CallByteMethod(IntPtr obj, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallByteMethod(obj, methodID, args); } finally { CheckException(); }
+ }
+
+ public static bool CallBooleanMethod(IntPtr obj, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallBooleanMethod(obj, methodID, args); } finally { CheckException(); }
+ }
+
+ public static Int32 CallIntMethod(IntPtr obj, IntPtr methodID, jvalue[] args)
+ {
+ try { return AndroidJNI.CallIntMethod(obj, methodID, args); } finally { CheckException(); }
+ }
+
+
+ public static IntPtr[] FromObjectArray(IntPtr array)
+ {
+ try { return AndroidJNI.FromObjectArray(array); } finally { CheckException(); }
+ }
+
+ public static Char[] FromCharArray(IntPtr array)
+ {
+ try { return AndroidJNI.FromCharArray(array); } finally { CheckException(); }
+ }
+
+ public static double[] FromDoubleArray(IntPtr array)
+ {
+ try { return AndroidJNI.FromDoubleArray(array); } finally { CheckException(); }
+ }
+
+ public static float[] FromFloatArray(IntPtr array)
+ {
+ try { return AndroidJNI.FromFloatArray(array); } finally { CheckException(); }
+ }
+
+ public static Int64[] FromLongArray(IntPtr array)
+ {
+ try { return AndroidJNI.FromLongArray(array); } finally { CheckException(); }
+ }
+
+ public static Int16[] FromShortArray(IntPtr array)
+ {
+ try { return AndroidJNI.FromShortArray(array); } finally { CheckException(); }
+ }
+
+ public static Byte[] FromByteArray(IntPtr array)
+ {
+ try { return AndroidJNI.FromByteArray(array); } finally { CheckException(); }
+ }
+
+ public static bool[] FromBooleanArray(IntPtr array)
+ {
+ try { return AndroidJNI.FromBooleanArray(array); } finally { CheckException(); }
+ }
+
+ public static Int32[] FromIntArray(IntPtr array)
+ {
+ try { return AndroidJNI.FromIntArray(array); } finally { CheckException(); }
+ }
+
+
+ public static IntPtr ToObjectArray(IntPtr[] array)
+ {
+ try { return AndroidJNI.ToObjectArray(array); } finally { CheckException(); }
+ }
+
+ public static IntPtr ToCharArray(Char[] array)
+ {
+ try { return AndroidJNI.ToCharArray(array); } finally { CheckException(); }
+ }
+
+ public static IntPtr ToDoubleArray(double[] array)
+ {
+ try { return AndroidJNI.ToDoubleArray(array); } finally { CheckException(); }
+ }
+
+ public static IntPtr ToFloatArray(float[] array)
+ {
+ try { return AndroidJNI.ToFloatArray(array); } finally { CheckException(); }
+ }
+
+ public static IntPtr ToLongArray(Int64[] array)
+ {
+ try { return AndroidJNI.ToLongArray(array); } finally { CheckException(); }
+ }
+
+ public static IntPtr ToShortArray(Int16[] array)
+ {
+ try { return AndroidJNI.ToShortArray(array); } finally { CheckException(); }
+ }
+
+ public static IntPtr ToByteArray(Byte[] array)
+ {
+ try { return AndroidJNI.ToByteArray(array); } finally { CheckException(); }
+ }
+
+ public static IntPtr ToBooleanArray(bool[] array)
+ {
+ try { return AndroidJNI.ToBooleanArray(array); } finally { CheckException(); }
+ }
+
+ public static IntPtr ToIntArray(Int32[] array)
+ {
+ try { return AndroidJNI.ToIntArray(array); } finally { CheckException(); }
+ }
+
+ public static IntPtr GetObjectArrayElement(IntPtr array, int index)
+ {
+ try { return AndroidJNI.GetObjectArrayElement(array, index); } finally { CheckException(); }
+ }
+
+ public static int GetArrayLength(IntPtr array)
+ {
+ try { return AndroidJNI.GetArrayLength(array); } finally { CheckException(); }
+ }
+}
+}
+
+#endif
diff --git a/Runtime/Export/AndroidJava.txt b/Runtime/Export/AndroidJava.txt
new file mode 100644
index 0000000..ab2c9f5
--- /dev/null
+++ b/Runtime/Export/AndroidJava.txt
@@ -0,0 +1,114 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+
+CSRAW
+#if UNITY_EDITOR || UNITY_ANDROID
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace UnityEngine
+{
+
+// AndroidJavaObject is the Unity representation of a generic instance of java.lang.Object.
+NONSEALED_CLASS AndroidJavaObject : IDisposable
+
+ // Construct an AndroidJavaObject based on the name of the class.
+ CSRAW public AndroidJavaObject(string className, params object[] args) : this()
+ {
+ _AndroidJavaObject(className, args);
+ }
+
+ // IDisposable callback
+ CSRAW public void Dispose()
+ {
+ _Dispose();
+ }
+
+ //===================================================================
+
+ // Calls a Java method on an object (non-static).
+ CSRAW public void Call(string methodName, params object[] args)
+ {
+ _Call(methodName, args);
+ }
+
+ //===================================================================
+
+ // Call a static Java method on a class.
+ CSRAW public void CallStatic(string methodName, params object[] args)
+ {
+ _CallStatic(methodName, args);
+ }
+
+ //===================================================================
+
+ // Get the value of a field in an object (non-static).
+ CSRAW public FieldType Get<FieldType>(string fieldName)
+ {
+ return _Get<FieldType>(fieldName);
+ }
+
+
+ // Set the value of a field in an object (non-static).
+ CSRAW public void Set<FieldType>(string fieldName, FieldType val)
+ {
+ _Set<FieldType>(fieldName, val);
+ }
+
+
+ //===================================================================
+
+ // Get the value of a static field in an object type.
+ CSRAW public FieldType GetStatic<FieldType>(string fieldName)
+ {
+ return _GetStatic<FieldType>(fieldName);
+ }
+
+
+ // Set the value of a static field in an object type.
+ CSRAW public void SetStatic<FieldType>(string fieldName, FieldType val)
+ {
+ _SetStatic<FieldType>(fieldName, val);
+ }
+
+
+ //===================================================================
+
+ // Retrieve the <i>raw</i> jobject pointer to the Java object.
+ CSRAW public IntPtr GetRawObject() { return _GetRawObject(); }
+ // Retrieve the <i>raw</i> jclass pointer to the Java class;
+ CSRAW public IntPtr GetRawClass() { return _GetRawClass(); }
+
+ //===================================================================
+
+ // Call a Java method on an object.
+ CSRAW public ReturnType Call<ReturnType>(string methodName, params object[] args)
+ {
+ return _Call<ReturnType>(methodName, args);
+ }
+
+ // Call a static Java method on a class.
+ CSRAW public ReturnType CallStatic<ReturnType>(string methodName, params object[] args)
+ {
+ return _CallStatic<ReturnType>(methodName, args);
+ }
+END
+
+// AndroidJavaClass is the Unity representation of a generic instance of java.lang.Class
+NONSEALED_CLASS AndroidJavaClass : AndroidJavaObject
+
+ // Construct an AndroidJavaClass from the class name
+ CSRAW public AndroidJavaClass(string className) : base()
+ {
+ _AndroidJavaClass(className);
+ }
+END
+
+CSRAW
+}
+#endif
diff --git a/Runtime/Export/AndroidJavaImpl.cs b/Runtime/Export/AndroidJavaImpl.cs
new file mode 100644
index 0000000..84429cd
--- /dev/null
+++ b/Runtime/Export/AndroidJavaImpl.cs
@@ -0,0 +1,1147 @@
+#if UNITY_EDITOR || UNITY_ANDROID
+
+using UnityEngine;
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Collections.Generic;
+
+
+namespace UnityEngine
+{
+ public delegate void AndroidJavaRunnable();
+
+ public sealed class AndroidJavaException : Exception
+ {
+ internal AndroidJavaException(string message) : base(message) { }
+ }
+
+
+ internal class AndroidJavaRunnableProxy : AndroidJavaProxy
+ {
+ private AndroidJavaRunnable mRunnable;
+ public AndroidJavaRunnableProxy(AndroidJavaRunnable runnable) : base("java/lang/Runnable") { mRunnable = runnable; }
+ public void run() { mRunnable(); }
+ }
+
+ public class AndroidJavaProxy
+ {
+
+ public readonly AndroidJavaClass javaInterface;
+ public AndroidJavaProxy(string javaInterface) : this(new AndroidJavaClass(javaInterface)) {}
+ public AndroidJavaProxy(AndroidJavaClass javaInterface)
+ {
+ this.javaInterface = javaInterface;
+ }
+
+ public virtual AndroidJavaObject Invoke(string methodName, object[] args)
+ {
+ Exception error = null;
+ BindingFlags binderFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
+ Type[] argTypes = new Type[args.Length];
+ for (int i = 0; i < args.Length; ++i)
+ argTypes[i] = args[i] == null ? typeof(AndroidJavaObject) : args[i].GetType();
+ try
+ {
+ MethodInfo method = GetType().GetMethod(methodName, binderFlags, null, argTypes, null);
+ if (method != null)
+ return _AndroidJNIHelper.Box(method.Invoke(this, args));
+ }
+ catch (TargetInvocationException invocationError)
+ {
+ error = invocationError.InnerException;
+ }
+ catch (Exception invocationError)
+ {
+ error = invocationError;
+ }
+
+ // Log error
+ string[] argTypeNames = new string[args.Length];
+ for (int i = 0; i < argTypes.Length; ++i)
+ argTypeNames[i] = argTypes[i].ToString();
+
+ if (error != null)
+ throw new TargetInvocationException(GetType() + "." + methodName + "(" + string.Join(",", argTypeNames) + ")", error);
+
+ throw new Exception("No such proxy method: " + GetType() + "." + methodName + "(" + string.Join(",", argTypeNames) + ")");
+ }
+
+ public virtual AndroidJavaObject Invoke(string methodName, AndroidJavaObject[] javaArgs)
+ {
+ object[] args = new object[javaArgs.Length];
+ for (int i = 0; i < javaArgs.Length; ++i)
+ args[i] = _AndroidJNIHelper.Unbox(javaArgs[i]);
+ return Invoke(methodName, args);
+ }
+ }
+
+ public partial class AndroidJavaObject
+ {
+ //===================================================================
+
+ private static bool enableDebugPrints = false;
+
+ protected void DebugPrint(string msg)
+ {
+ if (!enableDebugPrints)
+ return;
+ Debug.Log(msg);
+ }
+ protected void DebugPrint(string call, string methodName, string signature, object[] args)
+ {
+ if (!enableDebugPrints)
+ return;
+ System.Text.StringBuilder sb = new System.Text.StringBuilder();
+ foreach (object obj in args)
+ {
+ sb.Append(", ");
+ sb.Append(obj == null ? "<null>" : obj.GetType().ToString());
+ }
+ Debug.Log(call + "(\"" + methodName + "\"" + sb.ToString() + ") = " + signature);
+ }
+
+ //===================================================================
+
+ private void _AndroidJavaObject(string className, params object[] args)
+ {
+ DebugPrint("Creating AndroidJavaObject from " + className);
+ if (args==null) args = new object[] { null };
+ using (var clazz = FindClass(className))
+ {
+ m_jclass = AndroidJNI.NewGlobalRef(clazz.GetRawObject());
+ jvalue[] jniArgs = AndroidJNIHelper.CreateJNIArgArray(args);
+ try
+ {
+ IntPtr constructorID = AndroidJNIHelper.GetConstructorID(m_jclass, args);
+ IntPtr jobject = AndroidJNISafe.NewObject(m_jclass, constructorID, jniArgs);
+ m_jobject = AndroidJNI.NewGlobalRef(jobject);
+ AndroidJNISafe.DeleteLocalRef(jobject);
+ }
+ finally
+ {
+ AndroidJNIHelper.DeleteJNIArgArray(args, jniArgs);
+ }
+ }
+ }
+ internal AndroidJavaObject(IntPtr jobject) : this() // should be protected and friends with AndroidJNIHelper..
+ {
+ if (jobject == IntPtr.Zero)
+ {
+ throw new Exception("JNI: Init'd AndroidJavaObject with null ptr!");
+ }
+
+ IntPtr jclass = AndroidJNISafe.GetObjectClass(jobject);
+ m_jobject = AndroidJNI.NewGlobalRef(jobject);
+ m_jclass = AndroidJNI.NewGlobalRef(jclass);
+ AndroidJNISafe.DeleteLocalRef(jclass);
+ }
+
+ internal AndroidJavaObject()
+ {
+ }
+
+ ~AndroidJavaObject()
+ {
+ Dispose(true);
+ }
+
+ private bool m_disposed = false;
+ protected virtual void Dispose(bool disposing)
+ {
+ if(m_disposed)
+ return;
+
+ m_disposed = true;
+
+ AndroidJNISafe.DeleteGlobalRef(m_jobject);
+ AndroidJNISafe.DeleteGlobalRef(m_jclass);
+ }
+ protected void _Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ //===================================================================
+
+ protected void _Call(string methodName, params object[] args)
+ {
+ if (args==null) args = new object[] { null };
+ IntPtr methodID = AndroidJNIHelper.GetMethodID(m_jclass, methodName, args, false);
+ jvalue[] jniArgs = AndroidJNIHelper.CreateJNIArgArray(args);
+ try
+ {
+ AndroidJNISafe.CallVoidMethod(m_jobject, methodID, jniArgs);
+ }
+ finally
+ {
+ AndroidJNIHelper.DeleteJNIArgArray(args, jniArgs);
+ }
+ }
+ protected ReturnType _Call<ReturnType>(string methodName, params object[] args)
+ {
+ if (args==null) args = new object[] { null };
+ IntPtr methodID = AndroidJNIHelper.GetMethodID<ReturnType>(m_jclass, methodName, args, false);
+ jvalue[] jniArgs = AndroidJNIHelper.CreateJNIArgArray(args);
+ try
+ {
+ if (typeof(ReturnType).IsPrimitive)
+ {
+ if (typeof(ReturnType) == typeof(Int32))
+ return (ReturnType)(object)AndroidJNISafe.CallIntMethod(m_jobject, methodID, jniArgs);
+ else if (typeof(ReturnType) == typeof(Boolean))
+ return (ReturnType)(object)AndroidJNISafe.CallBooleanMethod(m_jobject, methodID, jniArgs);
+ else if (typeof(ReturnType) == typeof(Byte))
+ return (ReturnType)(object)AndroidJNISafe.CallByteMethod(m_jobject, methodID, jniArgs);
+ else if (typeof(ReturnType) == typeof(Int16))
+ return (ReturnType)(object)AndroidJNISafe.CallShortMethod(m_jobject, methodID, jniArgs);
+ else if (typeof(ReturnType) == typeof(Int64))
+ return (ReturnType)(object)AndroidJNISafe.CallLongMethod(m_jobject, methodID, jniArgs);
+ else if (typeof(ReturnType) == typeof(Single))
+ return (ReturnType)(object)AndroidJNISafe.CallFloatMethod(m_jobject, methodID, jniArgs);
+ else if (typeof(ReturnType) == typeof(Double))
+ return (ReturnType)(object)AndroidJNISafe.CallDoubleMethod(m_jobject, methodID, jniArgs);
+ else if (typeof(ReturnType) == typeof(Char))
+ return (ReturnType)(object)AndroidJNISafe.CallCharMethod(m_jobject, methodID, jniArgs);
+ }
+ else if (typeof(ReturnType) == typeof(String))
+ return (ReturnType)(object)AndroidJNISafe.CallStringMethod(m_jobject, methodID, jniArgs);
+ else if (typeof(ReturnType) == typeof(AndroidJavaClass))
+ {
+ IntPtr jclass = AndroidJNISafe.CallObjectMethod(m_jobject, methodID, jniArgs);
+ return (ReturnType)(object)AndroidJavaClassDeleteLocalRef(jclass);
+ }
+ else if (typeof(ReturnType) == typeof(AndroidJavaObject))
+ {
+ IntPtr jobject = AndroidJNISafe.CallObjectMethod(m_jobject, methodID, jniArgs);
+ return (ReturnType)(object)AndroidJavaObjectDeleteLocalRef(jobject);
+ }
+ else if (typeof(System.Array).IsAssignableFrom(typeof(ReturnType)))
+ {
+ IntPtr jobject = AndroidJNISafe.CallObjectMethod(m_jobject, methodID, jniArgs);
+ return (ReturnType)(object)AndroidJNIHelper.ConvertFromJNIArray<ReturnType>(jobject);
+ }
+ else
+ {
+ throw new Exception("JNI: Unknown return type '" + typeof(ReturnType) + "'");
+ }
+ return default(ReturnType);
+ }
+ finally
+ {
+ AndroidJNIHelper.DeleteJNIArgArray(args, jniArgs);
+ }
+ }
+
+ //===================================================================
+
+ protected FieldType _Get<FieldType>(string fieldName)
+ {
+ IntPtr fieldID = AndroidJNIHelper.GetFieldID<FieldType>(m_jclass, fieldName, false);
+ if (typeof(FieldType).IsPrimitive)
+ {
+ if (typeof(FieldType) == typeof(Int32))
+ return (FieldType)(object)AndroidJNISafe.GetIntField(m_jobject, fieldID);
+ else if (typeof(FieldType) == typeof(Boolean))
+ return (FieldType)(object)AndroidJNISafe.GetBooleanField(m_jobject, fieldID);
+ else if (typeof(FieldType) == typeof(Byte))
+ return (FieldType)(object)AndroidJNISafe.GetByteField(m_jobject, fieldID);
+ else if (typeof(FieldType) == typeof(Int16))
+ return (FieldType)(object)AndroidJNISafe.GetShortField(m_jobject, fieldID);
+ else if (typeof(FieldType) == typeof(Int64))
+ return (FieldType)(object)AndroidJNISafe.GetLongField(m_jobject, fieldID);
+ else if (typeof(FieldType) == typeof(Single))
+ return (FieldType)(object)AndroidJNISafe.GetFloatField(m_jobject, fieldID);
+ else if (typeof(FieldType) == typeof(Double))
+ return (FieldType)(object)AndroidJNISafe.GetDoubleField(m_jobject, fieldID);
+ else if (typeof(FieldType) == typeof(Char))
+ return (FieldType)(object)AndroidJNISafe.GetCharField(m_jobject, fieldID);
+ }
+ else if (typeof(FieldType) == typeof(String))
+ return (FieldType)(object)AndroidJNISafe.GetStringField(m_jobject, fieldID);
+ else if (typeof(FieldType) == typeof(AndroidJavaClass))
+ {
+ IntPtr jclass = AndroidJNISafe.GetObjectField(m_jobject, fieldID);
+ return (FieldType)(object)AndroidJavaClassDeleteLocalRef(jclass);
+ }
+ else if (typeof(FieldType) == typeof(AndroidJavaObject))
+ {
+ IntPtr jobject = AndroidJNISafe.GetObjectField(m_jobject, fieldID);
+ return (FieldType)(object)AndroidJavaObjectDeleteLocalRef(jobject);
+ }
+ else if (typeof(System.Array).IsAssignableFrom(typeof(FieldType)))
+ {
+ IntPtr jobject = AndroidJNISafe.GetObjectField(m_jobject, fieldID);
+ return (FieldType)(object)AndroidJNIHelper.ConvertFromJNIArray<FieldType>(jobject);
+ }
+ else
+ {
+ throw new Exception("JNI: Unknown field type '" + typeof(FieldType) + "'");
+ }
+ return default(FieldType);
+ }
+ protected void _Set<FieldType>(string fieldName, FieldType val)
+ {
+ IntPtr fieldID = AndroidJNIHelper.GetFieldID<FieldType>(m_jclass, fieldName, false);
+ if (typeof(FieldType).IsPrimitive)
+ {
+ if (typeof(FieldType) == typeof(Int32))
+ AndroidJNISafe.SetIntField(m_jobject, fieldID, (Int32)(object)val);
+ else if (typeof(FieldType) == typeof(Boolean))
+ AndroidJNISafe.SetBooleanField(m_jobject, fieldID, (Boolean)(object)val);
+ else if (typeof(FieldType) == typeof(Byte))
+ AndroidJNISafe.SetByteField(m_jobject, fieldID, (Byte)(object)val);
+ else if (typeof(FieldType) == typeof(Int16))
+ AndroidJNISafe.SetShortField(m_jobject, fieldID, (Int16)(object)val);
+ else if (typeof(FieldType) == typeof(Int64))
+ AndroidJNISafe.SetLongField(m_jobject, fieldID, (Int64)(object)val);
+ else if (typeof(FieldType) == typeof(Single))
+ AndroidJNISafe.SetFloatField(m_jobject, fieldID, (Single)(object)val);
+ else if (typeof(FieldType) == typeof(Double))
+ AndroidJNISafe.SetDoubleField(m_jobject, fieldID, (Double)(object)val);
+ else if (typeof(FieldType) == typeof(Char))
+ AndroidJNISafe.SetCharField(m_jobject, fieldID, (Char)(object)val);
+ }
+ else if (typeof(FieldType) == typeof(String))
+ AndroidJNISafe.SetStringField(m_jobject, fieldID, (String)(object)val);
+ else if (typeof(FieldType) == typeof(AndroidJavaClass))
+ {
+ AndroidJNISafe.SetObjectField(m_jobject, fieldID, ((AndroidJavaClass)(object)val).m_jclass);
+ }
+ else if (typeof(FieldType) == typeof(AndroidJavaObject))
+ {
+ AndroidJNISafe.SetObjectField(m_jobject, fieldID, ((AndroidJavaObject)(object)val).m_jobject);
+ }
+ else if (typeof(System.Array).IsAssignableFrom(typeof(FieldType)))
+ {
+ IntPtr jobject = AndroidJNIHelper.ConvertToJNIArray((Array)(object)val);
+ AndroidJNISafe.SetObjectField(m_jclass, fieldID, jobject);
+ }
+ else
+ {
+ throw new Exception("JNI: Unknown field type '" + typeof(FieldType) + "'");
+ }
+ }
+
+ //===================================================================
+
+ protected void _CallStatic(string methodName, params object[] args)
+ {
+ if (args==null) args = new object[] { null };
+ IntPtr methodID = AndroidJNIHelper.GetMethodID(m_jclass, methodName, args, true);
+ jvalue[] jniArgs = AndroidJNIHelper.CreateJNIArgArray(args);
+ try
+ {
+ AndroidJNISafe.CallStaticVoidMethod(m_jclass, methodID, jniArgs);
+ }
+ finally
+ {
+ AndroidJNIHelper.DeleteJNIArgArray(args, jniArgs);
+ }
+ }
+ protected ReturnType _CallStatic<ReturnType>(string methodName, params object[] args)
+ {
+ if (args==null) args = new object[] { null };
+ IntPtr methodID = AndroidJNIHelper.GetMethodID<ReturnType>(m_jclass, methodName, args, true);
+ jvalue[] jniArgs = AndroidJNIHelper.CreateJNIArgArray(args);
+ try
+ {
+ if (typeof(ReturnType).IsPrimitive)
+ {
+ if (typeof(ReturnType) == typeof(Int32))
+ return (ReturnType)(object)AndroidJNISafe.CallStaticIntMethod(m_jclass, methodID, jniArgs);
+ else if (typeof(ReturnType) == typeof(Boolean))
+ return (ReturnType)(object)AndroidJNISafe.CallStaticBooleanMethod(m_jclass, methodID, jniArgs);
+ else if (typeof(ReturnType) == typeof(Byte))
+ return (ReturnType)(object)AndroidJNISafe.CallStaticByteMethod(m_jclass, methodID, jniArgs);
+ else if (typeof(ReturnType) == typeof(Int16))
+ return (ReturnType)(object)AndroidJNISafe.CallStaticShortMethod(m_jclass, methodID, jniArgs);
+ else if (typeof(ReturnType) == typeof(Int64))
+ return (ReturnType)(object)AndroidJNISafe.CallStaticLongMethod(m_jclass, methodID, jniArgs);
+ else if (typeof(ReturnType) == typeof(Single))
+ return (ReturnType)(object)AndroidJNISafe.CallStaticFloatMethod(m_jclass, methodID, jniArgs);
+ else if (typeof(ReturnType) == typeof(Double))
+ return (ReturnType)(object)AndroidJNISafe.CallStaticDoubleMethod(m_jclass, methodID, jniArgs);
+ else if (typeof(ReturnType) == typeof(Char))
+ return (ReturnType)(object)AndroidJNISafe.CallStaticCharMethod(m_jclass, methodID, jniArgs);
+ }
+ else if (typeof(ReturnType) == typeof(String))
+ return (ReturnType)(object)AndroidJNISafe.CallStaticStringMethod(m_jclass, methodID, jniArgs);
+ else if (typeof(ReturnType) == typeof(AndroidJavaClass))
+ {
+ IntPtr jclass = AndroidJNISafe.CallStaticObjectMethod(m_jclass, methodID, jniArgs);
+ return (ReturnType)(object)AndroidJavaClassDeleteLocalRef(jclass);
+ }
+ else if (typeof(ReturnType) == typeof(AndroidJavaObject))
+ {
+ IntPtr jobject = AndroidJNISafe.CallStaticObjectMethod(m_jclass, methodID, jniArgs);
+ return (ReturnType)(object)AndroidJavaObjectDeleteLocalRef(jobject);
+ }
+ else if (typeof(System.Array).IsAssignableFrom(typeof(ReturnType)))
+ {
+ IntPtr jobject = AndroidJNISafe.CallStaticObjectMethod(m_jclass, methodID, jniArgs);
+ return (ReturnType)(object)AndroidJNIHelper.ConvertFromJNIArray<ReturnType>(jobject);
+ }
+ else
+ {
+ throw new Exception("JNI: Unknown return type '" + typeof(ReturnType) + "'");
+ }
+
+ return default(ReturnType);
+ }
+ finally
+ {
+ AndroidJNIHelper.DeleteJNIArgArray(args, jniArgs);
+ }
+ }
+
+ //===================================================================
+
+ protected FieldType _GetStatic<FieldType>(string fieldName)
+ {
+ IntPtr fieldID = AndroidJNIHelper.GetFieldID<FieldType>(m_jclass, fieldName, true);
+ if (typeof(FieldType).IsPrimitive)
+ {
+ if (typeof(FieldType) == typeof(Int32))
+ return (FieldType)(object)AndroidJNISafe.GetStaticIntField(m_jclass, fieldID);
+ else if (typeof(FieldType) == typeof(Boolean))
+ return (FieldType)(object)AndroidJNISafe.GetStaticBooleanField(m_jclass, fieldID);
+ else if (typeof(FieldType) == typeof(Byte))
+ return (FieldType)(object)AndroidJNISafe.GetStaticByteField(m_jclass, fieldID);
+ else if (typeof(FieldType) == typeof(Int16))
+ return (FieldType)(object)AndroidJNISafe.GetStaticShortField(m_jclass, fieldID);
+ else if (typeof(FieldType) == typeof(Int64))
+ return (FieldType)(object)AndroidJNISafe.GetStaticLongField(m_jclass, fieldID);
+ else if (typeof(FieldType) == typeof(Single))
+ return (FieldType)(object)AndroidJNISafe.GetStaticFloatField(m_jclass, fieldID);
+ else if (typeof(FieldType) == typeof(Double))
+ return (FieldType)(object)AndroidJNISafe.GetStaticDoubleField(m_jclass, fieldID);
+ else if (typeof(FieldType) == typeof(Char))
+ return (FieldType)(object)AndroidJNISafe.GetStaticCharField(m_jclass, fieldID);
+ }
+ else if (typeof(FieldType) == typeof(String))
+ return (FieldType)(object)AndroidJNISafe.GetStaticStringField(m_jclass, fieldID);
+ else if (typeof(FieldType) == typeof(AndroidJavaClass))
+ {
+ IntPtr jclass = AndroidJNISafe.GetStaticObjectField(m_jclass, fieldID);
+ return (FieldType)(object)AndroidJavaClassDeleteLocalRef(jclass);
+ }
+ else if (typeof(FieldType) == typeof(AndroidJavaObject))
+ {
+ IntPtr jobject = AndroidJNISafe.GetStaticObjectField(m_jclass, fieldID);
+ return (FieldType)(object)AndroidJavaObjectDeleteLocalRef(jobject);
+ }
+ else if (typeof(System.Array).IsAssignableFrom(typeof(FieldType)))
+ {
+ IntPtr jobject = AndroidJNISafe.GetStaticObjectField(m_jclass, fieldID);
+ return (FieldType)(object)AndroidJNIHelper.ConvertFromJNIArray<FieldType>(jobject);
+ }
+ else
+ {
+ throw new Exception("JNI: Unknown field type '" + typeof(FieldType) + "'");
+ }
+ return default(FieldType);
+ }
+ protected void _SetStatic<FieldType>(string fieldName, FieldType val)
+ {
+ IntPtr fieldID = AndroidJNIHelper.GetFieldID<FieldType>(m_jclass, fieldName, true);
+ if (typeof(FieldType).IsPrimitive)
+ {
+ if (typeof(FieldType) == typeof(Int32))
+ AndroidJNISafe.SetStaticIntField(m_jclass, fieldID, (Int32)(object)val);
+ else if (typeof(FieldType) == typeof(Boolean))
+ AndroidJNISafe.SetStaticBooleanField(m_jclass, fieldID, (Boolean)(object)val);
+ else if (typeof(FieldType) == typeof(Byte))
+ AndroidJNISafe.SetStaticByteField(m_jclass, fieldID, (Byte)(object)val);
+ else if (typeof(FieldType) == typeof(Int16))
+ AndroidJNISafe.SetStaticShortField(m_jclass, fieldID, (Int16)(object)val);
+ else if (typeof(FieldType) == typeof(Int64))
+ AndroidJNISafe.SetStaticLongField(m_jclass, fieldID, (Int64)(object)val);
+ else if (typeof(FieldType) == typeof(Single))
+ AndroidJNISafe.SetStaticFloatField(m_jclass, fieldID, (Single)(object)val);
+ else if (typeof(FieldType) == typeof(Double))
+ AndroidJNISafe.SetStaticDoubleField(m_jclass, fieldID, (Double)(object)val);
+ else if (typeof(FieldType) == typeof(Char))
+ AndroidJNISafe.SetStaticCharField(m_jclass, fieldID, (Char)(object)val);
+ }
+ else if (typeof(FieldType) == typeof(String))
+ AndroidJNISafe.SetStaticStringField(m_jclass, fieldID, (String)(object)val);
+ else if (typeof(FieldType) == typeof(AndroidJavaClass))
+ {
+ AndroidJNISafe.SetStaticObjectField(m_jclass, fieldID, ((AndroidJavaClass)(object)val).m_jclass);
+ }
+ else if (typeof(FieldType) == typeof(AndroidJavaObject))
+ {
+ AndroidJNISafe.SetStaticObjectField(m_jclass, fieldID, ((AndroidJavaObject)(object)val).m_jobject);
+ }
+ else if (typeof(System.Array).IsAssignableFrom(typeof(FieldType)))
+ {
+ IntPtr jobject = AndroidJNIHelper.ConvertToJNIArray((Array)(object)val);
+ AndroidJNISafe.SetStaticObjectField(m_jclass, fieldID, jobject);
+ }
+ else
+ {
+ throw new Exception("JNI: Unknown field type '" + typeof(FieldType) + "'");
+ }
+ }
+
+ internal static AndroidJavaObject AndroidJavaObjectDeleteLocalRef(IntPtr jobject)
+ {
+ try { return new AndroidJavaObject(jobject); } finally { AndroidJNISafe.DeleteLocalRef(jobject); }
+ }
+
+ internal static AndroidJavaClass AndroidJavaClassDeleteLocalRef(IntPtr jclass)
+ {
+ try { return new AndroidJavaClass(jclass); } finally { AndroidJNISafe.DeleteLocalRef(jclass); }
+ }
+
+ //===================================================================
+ protected IntPtr _GetRawObject() { return m_jobject; }
+ protected IntPtr _GetRawClass() { return m_jclass; }
+
+ protected IntPtr m_jobject;
+ protected IntPtr m_jclass; // use this for static lookups; reset in subclases
+
+ protected static AndroidJavaObject FindClass(string name)
+ {
+ return JavaLangClass.CallStatic<AndroidJavaObject>("forName", name.Replace('/', '.'));
+ }
+
+ private static AndroidJavaClass s_JavaLangClass;
+ protected static AndroidJavaClass JavaLangClass
+ {
+ get
+ {
+ if (s_JavaLangClass == null)
+ s_JavaLangClass = new AndroidJavaClass(AndroidJNISafe.FindClass("java/lang/Class"));
+ return s_JavaLangClass;
+ }
+ }
+ }
+
+ public partial class AndroidJavaClass
+ {
+ private void _AndroidJavaClass(string className)
+ {
+ DebugPrint("Creating AndroidJavaClass from " + className);
+ using (var clazz = FindClass(className))
+ {
+ m_jclass = AndroidJNI.NewGlobalRef(clazz.GetRawObject());
+ m_jobject = IntPtr.Zero;
+ }
+ }
+
+ internal AndroidJavaClass(IntPtr jclass) // should be protected and friends with AndroidJNIHelper..
+ {
+ if (jclass == IntPtr.Zero)
+ {
+ throw new Exception("JNI: Init'd AndroidJavaClass with null ptr!");
+ }
+
+ m_jclass = AndroidJNI.NewGlobalRef(jclass);
+ m_jobject = IntPtr.Zero;
+ }
+ }
+
+ internal class AndroidReflection
+ {
+ private static IntPtr GetStaticMethodID(string clazz, string methodName, string signature)
+ {
+ IntPtr jclass = AndroidJNISafe.FindClass(clazz);
+ try
+ {
+ return AndroidJNISafe.GetStaticMethodID(jclass, methodName, signature);
+ }
+ finally
+ {
+ AndroidJNISafe.DeleteLocalRef(jclass);
+ }
+ }
+
+ private const string RELECTION_HELPER_CLASS_NAME = "com/unity3d/player/ReflectionHelper";
+ private static IntPtr s_ReflectionHelperClass = AndroidJNI.NewGlobalRef(AndroidJNISafe.FindClass(RELECTION_HELPER_CLASS_NAME));
+ private static IntPtr s_ReflectionHelperGetConstructorID = GetStaticMethodID(RELECTION_HELPER_CLASS_NAME, "getConstructorID", "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Constructor;");
+ private static IntPtr s_ReflectionHelperGetMethodID = GetStaticMethodID(RELECTION_HELPER_CLASS_NAME, "getMethodID", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/reflect/Method;");
+ private static IntPtr s_ReflectionHelperGetFieldID = GetStaticMethodID(RELECTION_HELPER_CLASS_NAME, "getFieldID", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/reflect/Field;");
+ private static IntPtr s_ReflectionHelperNewProxyInstance = GetStaticMethodID(RELECTION_HELPER_CLASS_NAME, "newProxyInstance", "(ILjava/lang/Class;)Ljava/lang/Object;");
+
+ public static IntPtr GetConstructorMember(IntPtr jclass, string signature)
+ {
+ jvalue[] jniArgs = new jvalue[2];
+ try
+ {
+ jniArgs[0].l = jclass;
+ jniArgs[1].l = AndroidJNISafe.NewStringUTF(signature);
+ return AndroidJNISafe.CallStaticObjectMethod(s_ReflectionHelperClass, s_ReflectionHelperGetConstructorID, jniArgs);
+ }
+ finally
+ {
+ AndroidJNISafe.DeleteLocalRef(jniArgs[1].l);
+ }
+ }
+
+ public static IntPtr GetMethodMember(IntPtr jclass, string methodName, string signature, bool isStatic)
+ {
+ jvalue[] jniArgs = new jvalue[4];
+ try
+ {
+ jniArgs[0].l = jclass;
+ jniArgs[1].l = AndroidJNISafe.NewStringUTF(methodName);
+ jniArgs[2].l = AndroidJNISafe.NewStringUTF(signature);
+ jniArgs[3].z = isStatic;
+ return AndroidJNISafe.CallStaticObjectMethod(s_ReflectionHelperClass, s_ReflectionHelperGetMethodID, jniArgs);
+ }
+ finally
+ {
+ AndroidJNISafe.DeleteLocalRef(jniArgs[1].l);
+ AndroidJNISafe.DeleteLocalRef(jniArgs[2].l);
+ }
+ }
+
+ public static IntPtr GetFieldMember(IntPtr jclass, string fieldName, string signature, bool isStatic)
+ {
+ jvalue[] jniArgs = new jvalue[4];
+ try
+ {
+ jniArgs[0].l = jclass;
+ jniArgs[1].l = AndroidJNISafe.NewStringUTF(fieldName);
+ jniArgs[2].l = AndroidJNISafe.NewStringUTF(signature);
+ jniArgs[3].z = isStatic;
+ return AndroidJNISafe.CallStaticObjectMethod(s_ReflectionHelperClass, s_ReflectionHelperGetFieldID, jniArgs);
+ }
+ finally
+ {
+ AndroidJNISafe.DeleteLocalRef(jniArgs[1].l);
+ AndroidJNISafe.DeleteLocalRef(jniArgs[2].l);
+ }
+ }
+
+ public static IntPtr NewProxyInstance(int delegateHandle, IntPtr interfaze)
+ {
+ jvalue[] jniArgs = new jvalue[2];
+ jniArgs[0].i = delegateHandle;
+ jniArgs[1].l = interfaze;
+ return AndroidJNISafe.CallStaticObjectMethod(s_ReflectionHelperClass, s_ReflectionHelperNewProxyInstance, jniArgs);
+ }
+ }
+
+ sealed class _AndroidJNIHelper
+ {
+ public static IntPtr CreateJavaProxy(int delegateHandle, AndroidJavaProxy proxy)
+ {
+ return AndroidReflection.NewProxyInstance(delegateHandle, proxy.javaInterface.GetRawClass());
+ }
+
+ public static IntPtr CreateJavaRunnable(AndroidJavaRunnable jrunnable)
+ {
+ return AndroidJNIHelper.CreateJavaProxy(new AndroidJavaRunnableProxy(jrunnable));
+ }
+
+ public static IntPtr InvokeJavaProxyMethod(AndroidJavaProxy proxy, IntPtr jmethodName, IntPtr jargs)
+ {
+ int arrayLen = AndroidJNISafe.GetArrayLength(jargs);
+ AndroidJavaObject[] args = new AndroidJavaObject[arrayLen];
+ for (int i = 0; i < arrayLen; ++i)
+ {
+ IntPtr objectRef = AndroidJNISafe.GetObjectArrayElement(jargs, i);
+ args[i] = objectRef != IntPtr.Zero ? new AndroidJavaObject(objectRef) : null;
+ }
+ using (AndroidJavaObject result = proxy.Invoke(AndroidJNI.GetStringUTFChars(jmethodName), args))
+ {
+ if (result == null)
+ return IntPtr.Zero;
+
+ return AndroidJNI.NewLocalRef(result.GetRawObject());
+ }
+ }
+
+ public static jvalue[] CreateJNIArgArray(object[] args)
+ {
+ jvalue[] ret = new jvalue[args.GetLength(0)];
+ int i = 0;
+ foreach( object obj in args)
+ {
+ if (obj == null)
+ ret[i].l = System.IntPtr.Zero;
+ else if (obj.GetType().IsPrimitive)
+ {
+ if (obj is System.Int32)
+ ret[i].i = (System.Int32)obj;
+ else if (obj is System.Boolean)
+ ret[i].z = (System.Boolean)obj;
+ else if (obj is System.Byte)
+ ret[i].b = (System.Byte)obj;
+ else if (obj is System.Int16)
+ ret[i].s = (System.Int16)obj;
+ else if (obj is System.Int64)
+ ret[i].j = (System.Int64)obj;
+ else if (obj is System.Single)
+ ret[i].f = (System.Single)obj;
+ else if (obj is System.Double)
+ ret[i].d = (System.Double)obj;
+ else if (obj is System.Char)
+ ret[i].c = (System.Char)obj;
+ }
+ else if (obj is System.String)
+ {
+ ret[i].l = AndroidJNISafe.NewStringUTF((System.String)obj);
+ }
+ else if (obj is AndroidJavaClass)
+ {
+ ret[i].l = ((AndroidJavaClass)obj).GetRawClass();
+ }
+ else if (obj is AndroidJavaObject)
+ {
+ ret[i].l = ((AndroidJavaObject)obj).GetRawObject();
+ }
+ else if (obj is System.Array)
+ {
+ ret[i].l = ConvertToJNIArray((System.Array)obj);
+ }
+ else if (obj is AndroidJavaProxy)
+ {
+ ret[i].l = AndroidJNIHelper.CreateJavaProxy((AndroidJavaProxy)obj);
+ }
+ else if (obj is AndroidJavaRunnable)
+ {
+ ret[i].l = AndroidJNIHelper.CreateJavaRunnable((AndroidJavaRunnable)obj);
+ }
+ else
+ {
+ throw new Exception("JNI; Unknown argument type '" + obj.GetType() + "'");
+ }
+ ++i;
+ }
+ return ret;
+ }
+
+ public static object UnboxArray(AndroidJavaObject obj)
+ {
+ if (obj == null)
+ return null;
+
+ AndroidJavaClass arrayUtil = new AndroidJavaClass("java/lang/reflect/Array");
+ AndroidJavaObject objClass = obj.Call<AndroidJavaObject>("getClass");
+ AndroidJavaObject compClass = objClass.Call<AndroidJavaObject>("getComponentType");
+ string className = compClass.Call<string>("getName");
+
+ int arrayLength = arrayUtil.Call<int>("getLength", obj);
+ Array array;
+ if (compClass.Call<bool>("IsPrimitive")) // need to setup primitive array
+ {
+ if ("I" == className)
+ array = new int[arrayLength];
+ else if ("Z" == className)
+ array = new bool[arrayLength];
+ else if ("B" == className)
+ array = new byte[arrayLength];
+ else if ("S" == className)
+ array = new short[arrayLength];
+ else if ("L" == className)
+ array = new long[arrayLength];
+ else if ("F" == className)
+ array = new float[arrayLength];
+ else if ("D" == className)
+ array = new double[arrayLength];
+ else if ("C" == className)
+ array = new char[arrayLength];
+ else
+ throw new Exception("JNI; Unknown argument type '" + className + "'");
+ }
+ else if ("java.lang.String" == className)
+ array = new string[arrayLength];
+ else if ("java.lang.Class" == className)
+ array = new AndroidJavaClass[arrayLength];
+ else
+ array = new AndroidJavaObject[arrayLength];
+
+ for (int i = 0; i < arrayLength; ++i)
+ array.SetValue(Unbox(arrayUtil.CallStatic<AndroidJavaObject>("get", obj, i)), i);
+
+ return array;
+ }
+
+ public static object Unbox(AndroidJavaObject obj)
+ {
+ if (obj == null)
+ return null;
+
+ AndroidJavaObject clazz = obj.Call<AndroidJavaObject>("getClass");
+ string className = clazz.Call<string>("getName");
+ if ("java.lang.Integer" == className)
+ return obj.Call<System.Int32>("intValue");
+ else if ("java.lang.Boolean" == className)
+ return obj.Call<System.Boolean>("booleanValue");
+ else if ("java.lang.Byte" == className)
+ return obj.Call<System.Byte>("byteValue");
+ else if ("java.lang.Short" == className)
+ return obj.Call<System.Int16>("shortValue");
+ else if ("java.lang.Long" == className)
+ return obj.Call<System.Int32>("longValue");
+ else if ("java.lang.Float" == className)
+ return obj.Call<System.Single>("floatValue");
+ else if ("java.lang.Double" == className)
+ return obj.Call<System.Double>("doubleValue");
+ else if ("java.lang.Character" == className)
+ return obj.Call<System.Char>("charValue");
+ else if ("java.lang.String" == className)
+ return obj.Call<System.String>("toString"); // um, can obvoiusly be performed in a better fasion
+ else if ("java.lang.Class" == className)
+ return new AndroidJavaClass(obj.GetRawObject());
+ else if (clazz.Call<bool>("isArray"))
+ return UnboxArray(obj);
+ else
+ return obj;
+ }
+
+ public static AndroidJavaObject Box(object obj)
+ {
+ if (obj == null)
+ return null;
+ else if (obj.GetType().IsPrimitive)
+ {
+ if (obj is System.Int32)
+ return new AndroidJavaObject("java.lang.Integer", (System.Int32)obj);
+ else if (obj is System.Boolean)
+ return new AndroidJavaObject("java.lang.Boolean", (System.Boolean)obj);
+ else if (obj is System.Byte)
+ return new AndroidJavaObject("java.lang.Byte", (System.Byte)obj);
+ else if (obj is System.Int16)
+ return new AndroidJavaObject("java.lang.Short", (System.Int16)obj);
+ else if (obj is System.Int64)
+ return new AndroidJavaObject("java.lang.Long", (System.Int64)obj);
+ else if (obj is System.Single)
+ return new AndroidJavaObject("java.lang.Float", (System.Single)obj);
+ else if (obj is System.Double)
+ return new AndroidJavaObject("java.lang.Double", (System.Double)obj);
+ else if (obj is System.Char)
+ return new AndroidJavaObject("java.lang.Character", (System.Char)obj);
+ else
+ throw new Exception("JNI; Unknown argument type '" + obj.GetType() + "'");
+ }
+ else if (obj is System.String)
+ {
+ return new AndroidJavaObject("java.lang.String", (System.String)obj);
+ }
+ else if (obj is AndroidJavaClass)
+ {
+ return new AndroidJavaObject(((AndroidJavaClass)obj).GetRawClass());
+ }
+ else if (obj is AndroidJavaObject)
+ {
+ return (AndroidJavaObject)obj;
+ }
+ else if (obj is System.Array)
+ {
+ return AndroidJavaObject.AndroidJavaObjectDeleteLocalRef(ConvertToJNIArray((System.Array)obj));
+ }
+ else if (obj is AndroidJavaProxy)
+ {
+ return AndroidJavaObject.AndroidJavaObjectDeleteLocalRef(AndroidJNIHelper.CreateJavaProxy((AndroidJavaProxy)obj));
+ }
+ else if (obj is AndroidJavaRunnable)
+ {
+ return AndroidJavaObject.AndroidJavaObjectDeleteLocalRef(AndroidJNIHelper.CreateJavaRunnable((AndroidJavaRunnable)obj));
+ }
+ else
+ {
+ throw new Exception("JNI; Unknown argument type '" + obj.GetType() + "'");
+ }
+ }
+
+ public static void DeleteJNIArgArray(object[] args, jvalue[] jniArgs)
+ {
+ int i = 0;
+ foreach (object obj in args)
+ {
+ if (obj is System.String || obj is AndroidJavaRunnable || obj is AndroidJavaProxy || obj is System.Array)
+ AndroidJNISafe.DeleteLocalRef(jniArgs[i].l);
+
+ ++i;
+ }
+ }
+
+ public static IntPtr ConvertToJNIArray(System.Array array)
+ {
+ Type type = array.GetType().GetElementType();
+ if (type.IsPrimitive)
+ {
+ if (type == typeof(Int32))
+ return AndroidJNISafe.ToIntArray((Int32[])array);
+ else if (type == typeof(Boolean))
+ return AndroidJNISafe.ToBooleanArray((Boolean[])array);
+ else if (type == typeof(Byte))
+ return AndroidJNISafe.ToByteArray((Byte[])array);
+ else if (type == typeof(Int16))
+ return AndroidJNISafe.ToShortArray((Int16[])array);
+ else if (type == typeof(Int64))
+ return AndroidJNISafe.ToLongArray((Int64[])array);
+ else if (type == typeof(Single))
+ return AndroidJNISafe.ToFloatArray((Single[])array);
+ else if (type == typeof(Double))
+ return AndroidJNISafe.ToDoubleArray((Double[])array);
+ else if (type == typeof(Char))
+ return AndroidJNISafe.ToCharArray((Char[])array);
+ }
+ else if (type == typeof(String))
+ {
+ String[] strArray = (string[])array;
+ int arrayLen = array.GetLength(0);
+ IntPtr[] jniStrs = new IntPtr[arrayLen];
+ for (int i = 0; i < arrayLen; ++i)
+ {
+ jniStrs[i] = AndroidJNISafe.NewStringUTF(strArray[i]);
+ }
+ return AndroidJNISafe.ToObjectArray(jniStrs);
+ }
+ else if (type == typeof(AndroidJavaObject))
+ {
+ AndroidJavaObject[] objArray = (AndroidJavaObject[])array;
+ int arrayLen = array.GetLength(0);
+ IntPtr[] jniObjs = new IntPtr[arrayLen];
+ for (int i = 0; i < arrayLen; ++i)
+ {
+ jniObjs[i] = objArray[i] == null ? IntPtr.Zero : objArray[i].GetRawObject();
+ }
+ return AndroidJNISafe.ToObjectArray(jniObjs);
+ }
+ else
+ {
+ throw new Exception("JNI; Unknown array type '" + type + "'");
+ }
+ return IntPtr.Zero;
+ }
+ public static ArrayType ConvertFromJNIArray<ArrayType>(IntPtr array)
+ {
+ Type type = typeof(ArrayType).GetElementType();
+ if (type.IsPrimitive)
+ {
+ if (type == typeof(Int32))
+ return (ArrayType)(object)AndroidJNISafe.FromIntArray(array);
+ else if (type == typeof(Boolean))
+ return (ArrayType)(object)AndroidJNISafe.FromBooleanArray(array);
+ else if (type == typeof(Byte))
+ return (ArrayType)(object)AndroidJNISafe.FromByteArray(array);
+ else if (type == typeof(Int16))
+ return (ArrayType)(object)AndroidJNISafe.FromShortArray(array);
+ else if (type == typeof(Int64))
+ return (ArrayType)(object)AndroidJNISafe.FromLongArray(array);
+ else if (type == typeof(Single))
+ return (ArrayType)(object)AndroidJNISafe.FromFloatArray(array);
+ else if (type == typeof(Double))
+ return (ArrayType)(object)AndroidJNISafe.FromDoubleArray(array);
+ else if (type == typeof(Char))
+ return (ArrayType)(object)AndroidJNISafe.FromCharArray(array);
+ }
+ else if (type == typeof(String))
+ {
+ IntPtr[] jniStrs = AndroidJNISafe.FromObjectArray(array);
+ int arrayLen = jniStrs.GetLength(0);
+ string[] strArray = new string[arrayLen];
+ for (int i = 0; i < arrayLen; ++i)
+ {
+ strArray[i] = AndroidJNISafe.GetStringUTFChars(jniStrs[i]);
+ }
+ return (ArrayType)(object)strArray;
+ }
+ else if (type == typeof(AndroidJavaObject))
+ {
+ IntPtr[] jniObjs = AndroidJNISafe.FromObjectArray(array);
+ int arrayLen = jniObjs.GetLength(0);
+ AndroidJavaObject[] objArray = new AndroidJavaObject[arrayLen];
+ for (int i = 0; i < arrayLen; ++i)
+ {
+ objArray[i] = new AndroidJavaObject(jniObjs[i]);
+ }
+ return (ArrayType)(object)objArray;
+ }
+ else
+ {
+ throw new Exception("JNI: Unknown generic array type '" + type + "'");
+ }
+ return default(ArrayType);
+ }
+
+ public static System.IntPtr GetConstructorID(System.IntPtr jclass, object[] args)
+ {
+ return AndroidJNIHelper.GetConstructorID(jclass, GetSignature(args));
+ }
+ public static System.IntPtr GetMethodID(System.IntPtr jclass, string methodName, object[] args, bool isStatic)
+ {
+ return AndroidJNIHelper.GetMethodID(jclass, methodName, GetSignature(args), isStatic);
+ }
+ public static System.IntPtr GetMethodID<ReturnType>(System.IntPtr jclass, string methodName, object[] args, bool isStatic)
+ {
+ return AndroidJNIHelper.GetMethodID(jclass, methodName, GetSignature<ReturnType>(args), isStatic);
+ }
+ public static System.IntPtr GetFieldID<ReturnType>(System.IntPtr jclass, string fieldName, bool isStatic)
+ {
+ return AndroidJNIHelper.GetFieldID(jclass, fieldName, GetSignature(typeof(ReturnType)), isStatic);
+ }
+
+ public static IntPtr GetConstructorID(IntPtr jclass, string signature)
+ {
+ IntPtr constructor = IntPtr.Zero;
+ try
+ {
+ constructor = AndroidReflection.GetConstructorMember(jclass, signature);
+ return AndroidJNISafe.FromReflectedMethod(constructor);
+ }
+ catch (Exception e)
+ {
+ IntPtr memberID = AndroidJNISafe.GetMethodID(jclass, "<init>", signature);
+ if (memberID != IntPtr.Zero)
+ return memberID;
+ throw e;
+ }
+ finally
+ {
+ AndroidJNISafe.DeleteLocalRef(constructor);
+ }
+ }
+
+ public static IntPtr GetMethodID(IntPtr jclass, string methodName, string signature, bool isStatic)
+ {
+ IntPtr method = IntPtr.Zero;
+ try
+ {
+ method = AndroidReflection.GetMethodMember(jclass, methodName, signature, isStatic);
+ return AndroidJNISafe.FromReflectedMethod(method);
+ }
+ catch (Exception e)
+ {
+ IntPtr memberID = isStatic
+ ? AndroidJNISafe.GetStaticMethodID(jclass, methodName, signature)
+ : AndroidJNISafe.GetMethodID(jclass, methodName, signature);
+ if (memberID != IntPtr.Zero)
+ return memberID;
+ throw e;
+ }
+ finally
+ {
+ AndroidJNISafe.DeleteLocalRef(method);
+ }
+ }
+
+ public static IntPtr GetFieldID(IntPtr jclass, string fieldName, string signature, bool isStatic)
+ {
+ IntPtr field = IntPtr.Zero;
+ try
+ {
+ field = AndroidReflection.GetFieldMember(jclass, fieldName, signature, isStatic);
+ return AndroidJNISafe.FromReflectedField(field);
+ }
+ catch (Exception e)
+ {
+ IntPtr memberID = isStatic
+ ? AndroidJNISafe.GetStaticFieldID(jclass, fieldName, signature)
+ : AndroidJNISafe.GetFieldID(jclass, fieldName, signature);
+ if (memberID != IntPtr.Zero)
+ return memberID;
+ throw e;
+ }
+ finally
+ {
+ AndroidJNISafe.DeleteLocalRef(field);
+ }
+ }
+
+ public static string GetSignature(object obj)
+ {
+ if (obj == null)
+ return "Ljava/lang/Object;";
+ System.Type type = (obj is System.Type) ? (System.Type)obj : obj.GetType();
+ if (type.IsPrimitive)
+ {
+ if (type.Equals(typeof(System.Int32)))
+ return "I";
+ else if (type.Equals(typeof(System.Boolean)))
+ return "Z";
+ else if (type.Equals(typeof(System.Byte)))
+ return "B";
+ else if (type.Equals(typeof(System.Int16)))
+ return "S";
+ else if (type.Equals(typeof(System.Int64)))
+ return "J";
+ else if (type.Equals(typeof(System.Single)))
+ return "F";
+ else if (type.Equals(typeof(System.Double)))
+ return "D";
+ else if (type.Equals(typeof(System.Char)))
+ return "C";
+ }
+ else if (type.Equals(typeof(System.String)))
+ {
+ return "Ljava/lang/String;";
+ }
+ else if (obj is AndroidJavaProxy)
+ {
+ AndroidJavaObject javaClass = new AndroidJavaObject(((AndroidJavaProxy) obj).javaInterface.GetRawClass());
+ return "L" + javaClass.Call<System.String>("getName") + ";";
+ }
+ else if (type.Equals(typeof(AndroidJavaRunnable)))
+ {
+ return "Ljava/lang/Runnable;";
+ }
+ else if (type.Equals(typeof(AndroidJavaClass)))
+ {
+ return "Ljava/lang/Class;";
+ }
+ else if (type.Equals(typeof(AndroidJavaObject)))
+ {
+ if (obj == type)
+ {
+ return "Ljava/lang/Object;";
+ }
+ AndroidJavaObject javaObject = (AndroidJavaObject)obj;
+ using (AndroidJavaObject javaClass = javaObject.Call<AndroidJavaObject>("getClass"))
+ {
+ return "L" + javaClass.Call<System.String>("getName") + ";";
+ }
+ }
+ else if (typeof(System.Array).IsAssignableFrom(type))
+ {
+ if (type.GetArrayRank() != 1)
+ {
+ throw new Exception("JNI: System.Array in n dimensions is not allowed");
+ }
+ System.Text.StringBuilder sb = new System.Text.StringBuilder();
+ sb.Append('[');
+ sb.Append(GetSignature(type.GetElementType()));
+ return sb.ToString();
+ }
+ else
+ {
+ throw new Exception("JNI: Unknown signature for type '" + type + "' (obj = " + obj + ") " + (type == obj? "equal":"instance"));
+ }
+ return "";
+ }
+ public static string GetSignature(object[] args)
+ {
+ System.Text.StringBuilder sb = new System.Text.StringBuilder();
+ sb.Append('(');
+ foreach (object obj in args)
+ {
+ sb.Append(GetSignature(obj));
+ }
+ sb.Append(")V");
+ return sb.ToString();
+ }
+
+ public static string GetSignature<ReturnType>(object[] args)
+ {
+ System.Text.StringBuilder sb = new System.Text.StringBuilder();
+ sb.Append('(');
+ foreach (object obj in args)
+ {
+ sb.Append(GetSignature(obj));
+ }
+ sb.Append(')');
+ sb.Append(GetSignature(typeof(ReturnType)));
+ return sb.ToString();
+ }
+ }
+}
+
+#endif // UNITY_EDITOR || UNITY_ANDROID
diff --git a/Runtime/Export/AssemblyInfo.cs b/Runtime/Export/AssemblyInfo.cs
new file mode 100644
index 0000000..4a16238
--- /dev/null
+++ b/Runtime/Export/AssemblyInfo.cs
@@ -0,0 +1,15 @@
+using System.Runtime.CompilerServices;
+
+//TODO: Think about if this is secure or not, and if we should be using UnityEditor's public key instead.
+//I think we're cool, because we ifdef it out in the version of UnityEngine.dll that we ship with the webplayer.
+#if UNITY_EDITOR
+[assembly: InternalsVisibleTo("UnityEditor")]
+[assembly: InternalsVisibleTo("Unity.PureCSharpTests")]
+[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] // for Moq
+[assembly: InternalsVisibleTo("UnityEditor.Graphs")]
+[assembly: InternalsVisibleTo("UnityEditor.Xbox360.Extensions")]
+#endif
+
+#if UNITY_WINRT
+[assembly: InternalsVisibleTo("WinRTBridge")]
+#endif \ No newline at end of file
diff --git a/Runtime/Export/AssetBundleBindings.txt b/Runtime/Export/AssetBundleBindings.txt
new file mode 100644
index 0000000..08f097e
--- /dev/null
+++ b/Runtime/Export/AssetBundleBindings.txt
@@ -0,0 +1,175 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Misc/AssetBundle.h"
+#include "Runtime/Misc/AssetBundleUtility.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Misc/SaveAndLoadHelper.h"
+#include "Runtime/Misc/AsyncOperation.h"
+#include "Runtime/Scripting/Scripting.h"
+
+
+using namespace std;
+
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngineInternal;
+
+namespace UnityEngine
+{
+
+
+// Asynchronous create request for an [[AssetBundle]].
+CONDITIONAL ENABLE_WWW
+CLASS AssetBundleCreateRequest : AsyncOperation
+
+ C++RAW
+ #define GET_REQUEST(variableName) \
+ AssetBundleCreateRequest* variableName; \
+ MarshallManagedStructIntoNative (self, &variableName);
+
+ // Asset object being loaded (RO).
+ CUSTOM_PROP AssetBundle assetBundle
+ {
+ GET_REQUEST (ptr);
+ return Scripting::ScriptingWrapperFor(ptr->GetAssetBundle());
+ }
+
+ CUSTOM internal void DisableCompatibilityChecks()
+ {
+ GET_REQUEST (ptr);
+ ptr->SetEnableCompatibilityChecks (false);
+ }
+
+ C++RAW
+ #undef GET_REQUEST
+
+END
+
+// Asynchronous load request from an [[AssetBundle]].
+CSRAW [StructLayout (LayoutKind.Sequential)]
+CLASS AssetBundleRequest : AsyncOperation
+ C++RAW
+ #define GET ExtractMonoObjectData<PreloadManagerOperation*> (self)
+
+ CSRAW internal AssetBundle m_AssetBundle;
+ CSRAW internal string m_Path;
+ CSRAW internal Type m_Type;
+
+ // Asset object being loaded (RO).
+ CSRAW public Object asset { get { return m_AssetBundle.Load(m_Path, m_Type); } }
+
+ C++RAW
+ #undef GET
+
+END
+
+// AssetBundles let you stream additional assets via the WWW class and instantiate them at runtime. AssetBundles are created via BuildPipeline.BuildAssetBundle.
+
+CLASS AssetBundle : Object
+
+ // Asynchronously create an AssetBundle from a memory region.
+ CONDITIONAL ENABLE_WWW
+ CUSTOM static AssetBundleCreateRequest CreateFromMemory(byte[] binary)
+ {
+ if (!binary)
+ return SCRIPTING_NULL;
+
+ int dataSize = GetScriptingArraySize(binary);
+ UInt8* dataPtr = Scripting::GetScriptingArrayStart<UInt8>(binary);
+
+ AsyncOperation* req = new AssetBundleCreateRequest(dataPtr, dataSize);
+
+
+ return CreateScriptingObjectFromNativeStruct(MONO_COMMON.assetBundleCreateRequest,req);
+ }
+
+ // Loads an asset bundle from a disk.
+ CUSTOM static AssetBundle CreateFromFile(string path)
+ {
+ return Scripting::ScriptingWrapperFor(ExtractAssetBundle(path));
+ }
+
+ // Main asset that was supplied when building the asset bundle (RO).
+ CUSTOM_PROP Object mainAsset { return Scripting::ScriptingWrapperFor(LoadMainObjectFromAssetBundle(*self)); }
+
+ // Check if an AssetBundle contains a specific object.
+ CUSTOM bool Contains (string name)
+ {
+ string lowerName = ToLower(name.AsUTF8());
+ AssetBundle::range found = self->GetPathRange(lowerName);
+ return found.first != found.second;
+ }
+
+ // Loads object with /name/ from the bundle.
+ CSRAW public Object Load (string name)
+ {
+ return Load(name, typeof(Object));
+ }
+
+ // Loads object with /name/ of a given /type/ from the bundle.
+ CSRAW
+ [TypeInferenceRule(TypeInferenceRules.TypeReferencedBySecondArgument)]
+ CONSTRUCTOR_SAFE
+ CUSTOM Object Load (string name, Type type)
+ {
+ Scripting::RaiseIfNull (type);
+ Object* o = LoadNamedObjectFromAssetBundle(*self, name, type);
+ if (o==0) return SCRIPTING_NULL;
+ return Scripting::ScriptingWrapperFor(o);
+ }
+
+ // Asynchronously loads object with /name/ of a given /type/ from the bundle.
+ CUSTOM AssetBundleRequest LoadAsync (string name, Type type)
+ {
+ AsyncOperation* result = PreloadLevelOperation::LoadAssetBundle(*self, name);
+ #if ENABLE_MONO || UNITY_WINRT
+ ScriptingObjectPtr mono = scripting_object_new(MONO_COMMON.assetBundleRequest);
+ AssetBundleRequestMono data;
+ data.m_Result = result;
+ data.m_Path = name.GetNativeString();
+ data.m_Type = type;
+ data.m_AssetBundle = Scripting::ScriptingWrapperFor((AssetBundle*)self);
+ MarshallNativeStructIntoManaged(data, mono);
+
+ return mono;
+ #else
+ return SCRIPTING_NULL;
+ #endif
+
+ }
+
+ // Loads all objects contained in the asset bundle that inherit from /type/.
+ CUSTOM Object[] LoadAll (Type type)
+ {
+ Scripting::RaiseIfNull (type);
+
+ vector<Object* > objects;
+ LoadAllFromAssetBundle(*self, type, objects);
+ return CreateScriptingArrayFromUnityObjects(objects, ClassID(Object));
+ }
+
+ // Loads all objects contained in the asset bundle.
+ CSRAW public Object[] LoadAll ()
+ {
+ return LoadAll(typeof(Object));
+ }
+
+ // Unloads all assets in the bundle.
+ CUSTOM void Unload (bool unloadAllLoadedObjects)
+ {
+ AssetBundle& file = *self;
+ UnloadAssetBundle(file, unloadAllLoadedObjects);
+ }
+END
+
+CSRAW }
diff --git a/Runtime/Export/AttributeHelperEngine.cs b/Runtime/Export/AttributeHelperEngine.cs
new file mode 100644
index 0000000..d8188e0
--- /dev/null
+++ b/Runtime/Export/AttributeHelperEngine.cs
@@ -0,0 +1,84 @@
+using UnityEngine;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+
+namespace AOT
+{
+ // Mono AOT compiler detects this attribute by name and generates required wrappers for
+ // native->managed callbacks. Works only for static methods.
+ [System.AttributeUsage(System.AttributeTargets.Method)]
+ public class MonoPInvokeCallbackAttribute : Attribute
+ {
+ public MonoPInvokeCallbackAttribute (Type type) {}
+ }
+}
+
+namespace UnityEngine
+{
+
+internal class WrapperlessIcall : System.Attribute {
+ public WrapperlessIcall () {
+ }
+}
+
+class AttributeHelperEngine
+{
+ static Type[] GetRequiredComponents (Type klass)
+ {
+ // Generate an array for all required components
+ // .NET doesnt give us multiple copies of the same attribute on derived classes
+ // Thus we do it manually
+ List<Type> required = null;
+ while (klass != null && klass != typeof (MonoBehaviour))
+ {
+ object[] attrs = klass.GetCustomAttributes(typeof(RequireComponent), false);
+
+
+ for (int i=0;i<attrs.Length;i++)
+ {
+ RequireComponent attri = (RequireComponent)attrs[i];
+ if (required == null && attrs.Length == 1 && klass.BaseType == typeof (MonoBehaviour))
+ {
+ Type[] types = { attri.m_Type0, attri.m_Type1, attri.m_Type2 };
+ return types;
+ }
+ else
+ {
+ if (required == null)
+ required = new List<Type>();
+
+ if (attri.m_Type0 != null)
+ required.Add(attri.m_Type0);
+ if (attri.m_Type1 != null)
+ required.Add(attri.m_Type1);
+ if (attri.m_Type2 != null)
+ required.Add(attri.m_Type2);
+ }
+ }
+
+ klass = klass.BaseType;
+ }
+ if (required == null)
+ return null;
+ else
+ return required.ToArray();
+ }
+
+ static bool CheckIsEditorScript (Type klass)
+ {
+ while (klass != null && klass != typeof (MonoBehaviour))
+ {
+ object[] attrs = klass.GetCustomAttributes(typeof(ExecuteInEditMode), false);
+ if (attrs.Length != 0)
+ return true;
+ klass = klass.BaseType;
+ }
+ return false;
+ }
+}
+} \ No newline at end of file
diff --git a/Runtime/Export/BaseClass.txt b/Runtime/Export/BaseClass.txt
new file mode 100644
index 0000000..f083ca0
--- /dev/null
+++ b/Runtime/Export/BaseClass.txt
@@ -0,0 +1,1174 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Input/InputManager.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Profiler/ProfilerImpl.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/BaseClasses/Tags.h"
+#include "Runtime/Misc/DebugUtility.h"
+#include "Runtime/Misc/PlayerSettings.h"
+#include "Runtime/GameCode/CloneObject.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/Misc/PreloadManager.h"
+#include "Runtime/Allocator/MemoryManager.h"
+#include "Runtime/Audio/AudioClip.h"
+#if ENABLE_AUDIO
+#include "Runtime/Audio/AudioSource.h"
+#include "Runtime/Audio/AudioListener.h"
+#include "Runtime/Audio/AudioManager.h"
+#include "Runtime/Audio/AudioReverbZone.h"
+#include "Runtime/Audio/AudioReverbFilter.h"
+#include "Runtime/Audio/AudioHighPassFilter.h"
+#include "Runtime/Audio/AudioLowPassFilter.h"
+#include "Runtime/Audio/AudioChorusFilter.h"
+#include "Runtime/Audio/AudioDistortionFilter.h"
+#include "Runtime/Audio/AudioEchoFilter.h"
+#endif
+#include "Runtime/Animation/Animation.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Utilities/PlayerPrefs.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Dynamics/RigidBody.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Camera/Light.h"
+#include "Runtime/Filters/Misc/TextMesh.h"
+#include "Runtime/Dynamics/ConstantForce.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Misc/SaveAndLoadHelper.h"
+#include "Runtime/Network/NetworkView.h"
+#include "Runtime/Network/NetworkManager.h"
+#include "Runtime/Camera/RenderLayers/GUIText.h"
+#include "Runtime/Camera/RenderLayers/GUITexture.h"
+#include "Runtime/Dynamics/Collider.h"
+#include "Runtime/Dynamics/HingeJoint.h"
+#include "Runtime/Filters/Particles/ParticleEmitter.h"
+#include "Runtime/Misc/Player.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Misc/CaptureScreenshot.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Misc/Plugins.h"
+#include "Runtime/Misc/ResourceManagerUtility.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Utilities/File.h"
+#include <ctime>
+#include "Runtime/Input/GetInput.h"
+#include "Runtime/NavMesh/NavMeshAgent.h"
+#include "Runtime/NavMesh/NavMesh.h"
+#include "Runtime/NavMesh/OffMeshLink.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Animation/AnimationManager.h"
+#include "Runtime/Animation/AnimationClip.h"
+#include "Runtime/BaseClasses/RefCounted.h"
+#include "Runtime/Misc/GOCreation.h"
+#include "Runtime/Utilities/URLUtility.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/Misc/GraphicsDevicesDB.h"
+#include "Configuration/UnityConfigureVersion.h"
+#include "Runtime/Profiler/CollectProfilerStats.h"
+#include "Runtime/File/ApplicationSpecificPersistentDataPath.h"
+#include "Runtime/Mono/Coroutine.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/GetComponent.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+
+#if SUPPORT_REPRODUCE_LOG
+#include "Runtime/Misc/ReproductionLog.h"
+#endif
+
+#if WEBPLUG
+ #include "PlatformDependent/CommonWebPlugin/UnityWebStream.h"
+ #include "PlatformDependent/CommonWebPlugin/WebScripting.h"
+#endif
+
+#if UNITY_EDITOR
+ #include "Editor/Src/EditorSettings.h"
+ #include "Editor/Src/EditorUserBuildSettings.h"
+ #include "Editor/Mono/MonoEditorUtility.h"
+#endif
+
+#if UNITY_WII
+ #include "PlatformDependent/Wii/WiiUtility.h"
+#endif
+
+using namespace Unity;
+
+/*
+ Mono defines a bool as either 1 or 2 bytes.
+ On windows a bool on the C++ side needs to be 2 bytes.
+ We use the typemap to map bool's to short's.
+ When using the C++ keyword and you want to export a bool value
+ to mono you have to use a short on the C++ side.
+*/
+
+
+void PauseEditor ();
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngineInternal;
+
+namespace UnityEngine
+{
+
+[StructLayout (LayoutKind.Sequential)]
+internal struct ReferenceData
+{
+ public int instanceID;
+ public IntPtr cachedPtr;
+}
+
+// Bit mask that controls object destruction and visibility in inspectors
+CSRAW [Flags]
+ENUM HideFlags
+ // A normal, visible object. This is the default.
+ None = 0,
+
+ // The object will not appear in the hierarchy and will not show up in the project view if it is stored in an asset.
+ HideInHierarchy = 1,
+
+ // It is not possible to view it in the inspector
+ HideInInspector = 2,
+
+ // The object will not be saved to the scene. __It will not be destroyed when a new scene is loaded__.
+ DontSave = 4,
+
+ // The object is not be editable in the inspector
+ NotEditable = 8,
+
+ // A combination of not shown in the hierarchy and not saved to to scenes.
+ HideAndDontSave = 13
+END
+
+// Options for how to send a message.
+ENUM SendMessageOptions
+ // A receiver is required for SendMessage.
+ RequireReceiver = 0,
+
+ // No receiver is required for SendMessage.
+ DontRequireReceiver = 1
+END
+
+// The various primitives that can be created using the GameObject.CreatePrimitive function.
+CSRAW
+ENUM PrimitiveType
+ // A sphere primitive
+ Sphere = 0,
+
+ // A capsule primitive
+ Capsule = 1,
+
+ // A cylinder primitive
+ Cylinder = 2,
+
+ // A cube primitive
+ Cube = 3,
+
+ // A plane primitive
+ Plane = 4,
+
+ // A quad primitive
+ Quad = 5
+END
+
+// The coordinate space in which to operate.
+ENUM Space
+ // Applies transformation relative to the world coordinate system
+ World = 0,
+ // Applies transformation relative to the local coordinate system
+ Self = 1
+END
+
+
+
+// LayerMask allow you to display the LayerMask popup menu in the inspector
+STRUCT LayerMask
+ CSRAW
+ private int m_Mask;
+
+ //*undocumented* TODO: make this actually work
+ CSRAW public static implicit operator int (LayerMask mask) { return mask.m_Mask; }
+
+ // implicitly converts an integer to a LayerMask
+ public static implicit operator LayerMask (int intVal) { LayerMask mask; mask.m_Mask = intVal; return mask; }
+
+ // Converts a layer mask value to an integer value.
+ CSRAW public int value { get { return m_Mask; } set { m_Mask = value; } }
+
+ // Given a layer number, returns the name of the layer as defined in either a Builtin or a User Layer in the [[wiki:class-TagManager|Tag Manager]]
+ CUSTOM static string LayerToName (int layer) { return scripting_string_new(LayerToString(layer)); }
+
+ // Given a layer name, returns the layer index as defined by either a Builtin or a User Layer in the [[wiki:class-TagManager|Tag Manager]]
+ CUSTOM static int NameToLayer (string layerName) { return StringToLayer(layerName); }
+END
+
+
+// The platform application is running. Returned by Application.platform.
+ENUM RuntimePlatform
+ // In the Unity editor on Mac OS X.
+ OSXEditor = 0,
+
+ // In the player on Mac OS X.
+ OSXPlayer = 1,
+
+ // In the player on Windows.
+ WindowsPlayer = 2,
+
+ // In the web player on Mac OS X.
+ OSXWebPlayer = 3,
+
+ // In the Dashboard widget on Mac OS X.
+ OSXDashboardPlayer = 4,
+
+ // In the web player on Windows.
+ WindowsWebPlayer = 5,
+
+ // In the player on Nintendo Wii.
+ WiiPlayer = 6,
+
+ // In the Unity editor on Windows.
+ WindowsEditor = 7,
+
+ // In the player on the iPhone.
+ IPhonePlayer = 8,
+
+ // In the player on the XBOX360
+ XBOX360 = 10,
+
+ // In the player on the Play Station 3
+ PS3 = 9,
+
+ // In the player on Android devices.
+ Android = 11,
+
+ //Google Native Client
+ NaCl = 12,
+
+ //*undocumented*
+ LinuxPlayer = 13,
+
+ //Flash Player
+ FlashPlayer = 15,
+
+ //*undocumented*
+ MetroPlayerX86 = 18,
+
+ //*undocumented*
+ MetroPlayerX64 = 19,
+
+ //*undocumented*
+ MetroPlayerARM = 20,
+
+ //*undocumented*
+ WP8Player = 21,
+
+ //*undocumented*
+ BB10Player = 22,
+
+ //*undocumented*
+ TizenPlayer = 23,
+
+END
+
+// The language the user's operating system is running in. Returned by Application.systemLanguage.
+ENUM SystemLanguage
+ //Afrikaans
+ Afrikaans = 0,
+ //Arabic
+ Arabic = 1,
+ //Basque
+ Basque = 2,
+ //Belarusian
+ Belarusian = 3,
+ //Bulgarian
+ Bulgarian = 4,
+ //Catalan
+ Catalan = 5,
+ //Chinese
+ Chinese = 6,
+ //Czech
+ Czech = 7,
+ //Danish
+ Danish = 8,
+ //Dutch
+ Dutch = 9,
+ //English
+ English = 10,
+ //Estonian
+ Estonian = 11,
+ //Faroese
+ Faroese = 12,
+ //Finnish
+ Finnish = 13,
+ //French
+ French = 14,
+ //German
+ German = 15,
+ //Greek
+ Greek = 16,
+ //Hebrew
+ Hebrew = 17,
+ //*undocumented*
+ Hugarian = 18,
+ //Icelandic
+ Icelandic = 19,
+ //Indonesian
+ Indonesian = 20,
+ //Italian
+ Italian = 21,
+ //Japanese
+ Japanese = 22,
+ //Korean
+ Korean = 23,
+ //Latvian
+ Latvian = 24,
+ //Lithuanian
+ Lithuanian = 25,
+ //Norwegian
+ Norwegian = 26,
+ //Polish
+ Polish = 27,
+ //Portuguese
+ Portuguese = 28,
+ //Romanian
+ Romanian = 29,
+ //Russian
+ Russian = 30,
+ //Serbo-Croatian
+ SerboCroatian = 31,
+ //Slovak
+ Slovak = 32,
+ //Slovenian
+ Slovenian = 33,
+ //Spanish
+ Spanish = 34,
+ //Swedish
+ Swedish = 35,
+ //Thai
+ Thai = 36,
+ //Turkish
+ Turkish = 37,
+ //Ukrainian
+ Ukrainian = 38,
+ //Vietnamese
+ Vietnamese = 39,
+ //Unknown
+ Unknown = 40,
+ //Hungarian
+ Hungarian = 18
+END
+
+// The type of the log message in the delegate registered with Application.RegisterLogCallback.
+ENUM LogType
+ // LogType used for Errors.
+ Error = 0,
+ // LogType used for Asserts. (These indicate an error inside Unity itself.)
+ Assert = 1,
+ // LogType used for Warnings.
+ Warning = 2,
+ // LogType used for regular log messages.
+ Log = 3,
+ // LogType used for Exceptions.
+ Exception = 4
+END
+
+// Enumeration for [[SystemInfo.deviceType]], denotes a coarse grouping of kinds of devices.
+ENUM DeviceType
+ // Device type is unknown. You should never see this in practice.
+ Unknown = 0,
+
+ // A handheld device like mobile phone or a tablet.
+ Handheld = 1,
+
+ // A stationary gaming console.
+ Console = 2,
+
+ // Desktop or laptop computer.
+ Desktop = 3,
+END
+
+// Access system information.
+CLASS SystemInfo
+
+ // Operating system name with version (RO).
+ CUSTOM_PROP static string operatingSystem { return scripting_string_new( systeminfo::GetOperatingSystem() ); }
+
+ // Processor name (RO).
+ CUSTOM_PROP static string processorType { return scripting_string_new( systeminfo::GetProcessorType() ); }
+
+ // Number of processors present (RO).
+ CUSTOM_PROP static int processorCount { return systeminfo::GetProcessorCount(); }
+
+ // Amount of system memory present (RO).
+ CUSTOM_PROP static int systemMemorySize { return systeminfo::GetPhysicalMemoryMB(); }
+
+
+ // Amount of video memory present (RO).
+ CUSTOM_PROP static int graphicsMemorySize { return (int)gGraphicsCaps.videoMemoryMB; }
+
+
+ // The name of the graphics device (RO).
+ CUSTOM_PROP static string graphicsDeviceName { return scripting_string_new(gGraphicsCaps.rendererString.c_str()); }
+
+
+ // The vendor of the graphics device (RO).
+ CUSTOM_PROP static string graphicsDeviceVendor { return scripting_string_new(gGraphicsCaps.vendorString.c_str()); }
+
+
+ // The identifier code of the graphics device (RO).
+ CUSTOM_PROP static int graphicsDeviceID { return gGraphicsCaps.rendererID; }
+
+
+ // The identifier code of the graphics device vendor (RO).
+ CUSTOM_PROP static int graphicsDeviceVendorID { return gGraphicsCaps.vendorID; }
+
+
+ // The graphics API version supported by the graphics device (RO).
+ CUSTOM_PROP static string graphicsDeviceVersion { return scripting_string_new(gGraphicsCaps.fixedVersionString.c_str()); }
+
+
+
+ // Graphics device shader capability level (RO).
+ CUSTOM_PROP static int graphicsShaderLevel { return gGraphicsCaps.shaderCaps; }
+
+
+ // Approximate pixel fill-rate of the graphics device (RO).
+ CUSTOM_PROP static int graphicsPixelFillrate {
+ return GetGraphicsPixelFillrate (gGraphicsCaps.vendorID, gGraphicsCaps.rendererID);
+ }
+
+
+ // Are built-in shadows supported? (RO)
+ CUSTOM_PROP static bool supportsShadows {
+ return RenderTexture::IsEnabled() && GetBuildSettings().hasShadows && gGraphicsCaps.supportsRenderTextureFormat[kRTFormatDepth];
+ }
+ // Are render textures supported? (RO)
+ CUSTOM_PROP static bool supportsRenderTextures {
+ return RenderTexture::IsEnabled();
+ }
+ CUSTOM_PROP static bool supportsRenderToCubemap {
+ return RenderTexture::IsEnabled() && (gGraphicsCaps.hasRenderToCubemap);
+ }
+ // Are image effects supported? (RO)
+ CUSTOM_PROP static bool supportsImageEffects {
+ return RenderTexture::IsEnabled() && (gGraphicsCaps.npotRT >= kNPOTRestricted);
+ }
+
+ // Are 3D (volume) textures supported? (RO)
+ CUSTOM_PROP static bool supports3DTextures {
+ return gGraphicsCaps.has3DTexture;
+ }
+
+ // Are compute shaders supported? (RO)
+ CUSTOM_PROP static bool supportsComputeShaders {
+ return gGraphicsCaps.hasComputeShader;
+ }
+
+ // Is GPU draw call instancing supported? (RO)
+ CUSTOM_PROP static bool supportsInstancing {
+ return gGraphicsCaps.hasInstancing;
+ }
+
+ // How many simultaneous render targets (MRTs) are supported? (RO)
+ CUSTOM_PROP static int supportedRenderTargetCount {
+ return RenderTexture::IsEnabled() ? gGraphicsCaps.maxMRTs : 0;
+ }
+
+ // Is the stencil buffer supported? (RO)
+ CUSTOM_PROP static int supportsStencil {
+ return gGraphicsCaps.hasStencil && GetBuildSettings ().hasAdvancedVersion;
+ }
+
+ //*undocumented*
+ CUSTOM_PROP static bool supportsVertexPrograms { return true; }
+
+ // Is render texture format supported?
+ CUSTOM static bool SupportsRenderTextureFormat (RenderTextureFormat format) {
+ return RenderTexture::IsEnabled() && gGraphicsCaps.supportsRenderTextureFormat[format];
+ }
+
+ /// What [[NPOT|NPOTSupport]] support does GPU provide? (RO)
+ ///
+ /// SA: [[NPOTSupport]] enum.
+ CUSTOM_PROP static NPOTSupport npotSupport { return gGraphicsCaps.npot; }
+
+ //A unique device identifier. It is guaranteed to be unique for every device (RO).
+ CONDITIONAL !UNITY_FLASH && !UNITY_WEBGL
+ CUSTOM_PROP static string deviceUniqueIdentifier {
+ return scripting_string_new (systeminfo::GetDeviceUniqueIdentifier ());
+ }
+
+ // The user defined name of the device (RO).
+ CONDITIONAL !UNITY_FLASH && !UNITY_WEBGL
+ CUSTOM_PROP static string deviceName {
+ return scripting_string_new (systeminfo::GetDeviceName ());
+ }
+
+ // The model of the device (RO).
+ CONDITIONAL !UNITY_FLASH && !UNITY_WEBGL
+ CUSTOM_PROP static string deviceModel {
+ return scripting_string_new (systeminfo::GetDeviceModel ());
+ }
+
+ // Returns a boolean value that indicates whether an accelerometer is
+ CUSTOM_PROP static bool supportsAccelerometer {
+ return systeminfo::SupportsAccelerometer ();
+ }
+
+ // Returns a boolean value that indicates whether a gyroscope is available
+ CUSTOM_PROP static bool supportsGyroscope {
+ return IsGyroAvailable ();
+ }
+
+ // Returns a boolean value that indicates whether the device is capable to
+ CUSTOM_PROP static bool supportsLocationService {
+ return systeminfo::SupportsLocationService ();
+ }
+
+ // Returns a boolean value that indicates whether the device is capable to
+ CUSTOM_PROP static bool supportsVibration {
+ return systeminfo::SupportsVibration ();
+ }
+
+ // Returns the kind of device the application is running on. See [[DeviceType]] enumeration for possible values.
+ CUSTOM_PROP static DeviceType deviceType
+ {
+ return systeminfo::DeviceType ();
+ }
+
+ CUSTOM_PROP static int maxTextureSize
+ {
+ return gGraphicsCaps.maxTextureSize;
+ }
+
+END
+
+// Suspends the coroutine execution for the given amount of seconds.
+CSRAW [StructLayout(LayoutKind.Sequential)]
+CLASS WaitForSeconds : YieldInstruction
+ //*undocumented*
+ CSRAW internal float m_Seconds;
+
+ // Creates a yield instruction to wait for a given number of seconds
+ CSRAW public WaitForSeconds (float seconds) { m_Seconds = seconds; }
+END
+
+// Waits until next fixed frame rate update function. SA: MonoBehaviour::pref::FixedUpdate.
+CLASS WaitForFixedUpdate : YieldInstruction
+END
+
+// Waits until the end of the frame after all cameras and GUI is rendered, just before displaying the frame on screen.
+CLASS WaitForEndOfFrame : YieldInstruction
+END
+
+
+// MonoBehaviour.StartCoroutine returns a Coroutine. Instances of this class are only used to reference these coroutines and do not hold any exposed properties or functions.
+CSRAW [StructLayout (LayoutKind.Sequential)]
+
+CLASS Coroutine : YieldInstruction
+ CSRAW internal IntPtr m_Ptr;
+ private Coroutine () { }
+
+ THREAD_SAFE
+ CUSTOM private void ReleaseCoroutine ()
+ {
+ Assert (self.GetPtr() != NULL);
+ Coroutine::CleanupCoroutineGC (self);
+ }
+
+ CSRAW
+ ~Coroutine ()
+ {
+ ReleaseCoroutine ();
+ }
+END
+
+CSRAW
+
+// The RequireComponent attribute lets automatically add required component as a dependency.
+
+
+CSRAW [AttributeUsage(AttributeTargets.Class, AllowMultiple=true)]
+CLASS RequireComponent : Attribute
+ //*undocumented*
+ CSRAW public Type m_Type0;
+ //*undocumented*
+ CSRAW public Type m_Type1;
+ //*undocumented*
+ CSRAW public Type m_Type2;
+
+ // Require a single component
+ CSRAW public RequireComponent (Type requiredComponent) { m_Type0 = requiredComponent; }
+ // Require a two components
+ CSRAW public RequireComponent (Type requiredComponent, Type requiredComponent2) { m_Type0 = requiredComponent; m_Type1 = requiredComponent2; }
+ // Require three components
+ CSRAW public RequireComponent (Type requiredComponent, Type requiredComponent2, Type requiredComponent3) { m_Type0 = requiredComponent; m_Type1 = requiredComponent2; m_Type2 = requiredComponent3; }
+END
+
+
+// The AddComponentMenu attribute allows you to place a script anywhere in the "Component" menu, instead of just the "Component->Scripts" menu.
+
+CLASS AddComponentMenu : Attribute
+ CSRAW private string m_AddComponentMenu;
+
+ // The script will be placed in the component menu according to /menuName/. /menuName/ is the path to the component
+ CSRAW public AddComponentMenu (string menuName) { m_AddComponentMenu = menuName; }
+
+ //* undocumented
+ CSRAW public string componentMenu { get {return m_AddComponentMenu; } }
+END
+
+
+// The ContextMenu attribute allows you to add commands to the context menu
+CLASS ContextMenu : Attribute
+
+ // Adds the function to the context menu of the component.
+ CSRAW public ContextMenu (string name) { m_ItemName = name; }
+
+
+ CSRAW private string m_ItemName;
+
+ //* undocumented
+ CSRAW public string menuItem { get { return m_ItemName; } }
+END
+
+
+// Makes a script execute in edit mode.
+CLASS ExecuteInEditMode : Attribute
+END
+
+
+// Makes a variable not show up in the inspector but be serialized.
+CLASS HideInInspector : Attribute
+END
+
+// A class you can derive from if you want to create objects that don't need to be attached to game objects.
+CSRAW
+[StructLayout (LayoutKind.Sequential)]
+NONSEALED_CLASS ScriptableObject : Object
+
+ //*undocumented* Users are not supposed to instantiate unextended ScriptableObjects, are they?
+ CSRAW public ScriptableObject ()
+ {
+ Internal_CreateScriptableObject(this);
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void Internal_CreateScriptableObject([Writable]ScriptableObject self)
+ {
+ Scripting::CreateEngineScriptableObject(self.GetScriptingObject());
+ }
+
+ CONDITIONAL ENABLE_MONO
+ OBSOLETE warning Use EditorUtility.SetDirty instead
+ AUTO void SetDirty ();
+
+ // Creates an instance of a scriptable object with /className/.
+ CUSTOM static ScriptableObject CreateInstance (string className) { return Scripting::CreateScriptableObject (className.AsUTF8()); }
+
+ // Creates an instance of a scriptable object with /type/.
+ CSRAW public static ScriptableObject CreateInstance (Type type) { return CreateInstanceFromType(type); }
+
+ CUSTOM private static ScriptableObject CreateInstanceFromType (Type type) { return Scripting::CreateScriptableObjectWithType (type); }
+
+ CSRAW
+ #if ENABLE_GENERICS
+ // Creates an instance of a scriptable object with /T/.
+ public static T CreateInstance<T> () where T : ScriptableObject
+ {
+ return (T)CreateInstance(typeof(T));
+ }
+ #endif
+
+ // This function is called when the object is loaded
+ CSNONE void OnEnable ();
+
+
+ // This function is called when the scriptable object goes out of scope
+ CSNONE void OnDisable();
+
+ // This function is called when the scriptable object will be destroyed.
+ CSNONE void OnDestroy();
+
+
+END
+
+// The Resources class allows you to find and access Objects including assets.
+CLASS Resources
+
+ CONDITIONAL (ENABLE_GENERICS && !UNITY_FLASH)
+ CSRAW internal static T[] ConvertObjects<T>(Object[] rawObjects) where T : Object
+ {
+ if (rawObjects == null) return null;
+ T[] typedObjects = new T[rawObjects.Length];
+ for (int i = 0; i < typedObjects.Length; i++)
+ typedObjects[i] = (T)rawObjects[i];
+ return typedObjects;
+ }
+
+ // Returns a list of all objects of Type /type/.
+ CSRAW
+ [TypeInferenceRule(TypeInferenceRules.ArrayOfTypeReferencedByFirstArgument)]
+ CUSTOM static Object[] FindObjectsOfTypeAll (Type type) { DISALLOW_IN_CONSTRUCTOR return Scripting::FindObjectsOfType (type, Scripting::kFindAnything); }
+
+ CONDITIONAL (ENABLE_GENERICS && !UNITY_FLASH)
+ CSRAW public static T[] FindObjectsOfTypeAll<T> () where T : Object
+ {
+ return ConvertObjects<T>(FindObjectsOfTypeAll (typeof (T)));
+ }
+
+ // Loads an asset stored at /path/ in a Resources folder.
+
+ CSRAW public static Object Load (string path)
+ {
+ return Load(path, typeof(Object));
+ }
+
+ CONDITIONAL ENABLE_GENERICS
+ CSRAW public static T Load<T> (string path) where T : Object
+ {
+ return (T) Load (path, typeof (T));
+ }
+
+ // Loads an asset stored at /path/ in a Resources folder.
+ CSRAW
+ [TypeInferenceRule(TypeInferenceRules.TypeReferencedBySecondArgument)]
+ CUSTOM static Object Load (string path, Type systemTypeInstance)
+ {
+ Scripting::RaiseIfNull (systemTypeInstance);
+
+ ScriptingClassPtr klass = GetScriptingTypeRegistry().GetType(systemTypeInstance);
+ string lowerPath = ToLower(path.AsUTF8());
+ ResourceManager::range found = GetResourceManager().GetPathRange(lowerPath);
+
+ Object* obj = NULL;
+ ScriptingObjectPtr o = SCRIPTING_NULL;
+ for (ResourceManager::iterator i=found.first;i != found.second;i++)
+ {
+ obj = i->second;
+ GetResourceManager ().PreloadDependencies (obj->GetInstanceID ());
+
+ o = Scripting::ScriptingWrapperFor(obj);
+ if (o == SCRIPTING_NULL)
+ continue;
+
+ ScriptingClassPtr k = scripting_object_get_class(o, GetScriptingTypeRegistry());
+ if (o && scripting_class_is_subclass_of(k,klass))
+ {
+ break;
+ }
+
+ GameObject* go = dynamic_pptr_cast<GameObject*> (obj);
+ if (go != NULL)
+ {
+ o = ScriptingGetComponentOfType(*go, systemTypeInstance, false);
+ if (o != SCRIPTING_NULL)
+ {
+ break;
+ }
+ }
+ }
+
+ // Android keeps its Resources folder split up (to minimize seeks inside the .apk)
+ // To not "leak" SerializedFile objects we need to unload the added stream here..
+ if (obj && UNITY_ANDROID)
+ {
+ PersistentManager& pm = GetPersistentManager();
+ pm.UnloadNonDirtyStreams();
+ }
+
+ return o;
+ }
+
+ // Loads all assets in a folder or file at /path/ in a Resources folder.
+ CUSTOM static Object[] LoadAll (string path, Type systemTypeInstance)
+ {
+ Scripting::RaiseIfNull (systemTypeInstance);
+
+ ScriptingClassPtr klass = GetScriptingTypeRegistry().GetType(systemTypeInstance);
+ ResourceManager::range found = GetResourceManager().GetAll();
+ string cpath = ToLower(path.AsUTF8());
+
+ vector<PPtr<Object> > objects;
+
+ for (ResourceManager::iterator i=found.first;i != found.second;i++)
+ {
+ // Path doesn't match (But allow empty path for all objects)
+ if (!StartsWithPath(i->first, cpath))
+ continue;
+
+ Object* obj = i->second;
+ GetResourceManager ().PreloadDependencies (obj->GetInstanceID ());
+
+ ScriptingObjectPtr o = Scripting::ScriptingWrapperFor(obj);
+ if (o == SCRIPTING_NULL)
+ continue;
+
+ ScriptingClassPtr k = scripting_object_get_class(o, GetScriptingTypeRegistry());
+ if (o && scripting_class_is_subclass_of(k, klass))
+ {
+ objects.push_back(i->second);
+ }
+ else
+ {
+ GameObject* go = dynamic_pptr_cast<GameObject*> (obj);
+ if (go != NULL)
+ {
+ o = ScriptingGetComponentOfType(*go, systemTypeInstance, false);
+ if (o != SCRIPTING_NULL)
+ {
+ objects.push_back(ScriptingObjectToObject<Object>(o));
+ }
+ }
+ }
+ }
+
+ return CreateScriptingArrayFromUnityObjects (objects, ClassID(Object));
+ }
+
+ // Loads all assets in a folder or file at /path/ in a Resources folder.
+
+ CSRAW public static Object[] LoadAll (string path)
+ {
+ return LoadAll(path, typeof(Object));
+ }
+
+ CONDITIONAL (ENABLE_GENERICS && !UNITY_FLASH)
+ CSRAW public static T[] LoadAll<T> (string path) where T : Object
+ {
+ return ConvertObjects<T>(LoadAll (path, typeof (T)));
+ }
+
+ // *undocumented
+ CSRAW
+ [TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
+ CUSTOM static Object GetBuiltinResource (Type type, string path)
+ {
+ Scripting::RaiseIfNull(type);
+ return GetScriptingBuiltinResource(type, path.AsUTF8());
+ }
+
+ CONDITIONAL ENABLE_GENERICS
+ CSRAW public static T GetBuiltinResource<T> (string path) where T : Object
+ {
+ return (T) GetBuiltinResource (typeof (T), path);
+ }
+
+ // Returns a resource at an asset path (Editor Only).
+ CSRAW
+ [TypeInferenceRule(TypeInferenceRules.TypeReferencedBySecondArgument)]
+ CUSTOM static Object LoadAssetAtPath (string assetPath, Type type)
+ {
+ #if UNITY_EDITOR
+ return LoadAssetAtPath(assetPath, type);
+ #else
+ return SCRIPTING_NULL;
+ #endif
+ }
+
+ CONDITIONAL ENABLE_GENERICS
+ CSRAW public static T LoadAssetAtPath<T> (string assetPath) where T : Object
+ {
+ return (T) LoadAssetAtPath (assetPath, typeof (T));
+ }
+
+ // Unloads /assetToUnload/ from memory.
+ CUSTOM static void UnloadAsset (Object assetToUnload) { Scripting::UnloadAssetFromScripting (assetToUnload); }
+
+ // Unloads assets that are not used.
+ CUSTOM static AsyncOperation UnloadUnusedAssets ()
+ {
+ AsyncOperation* result = UnloadUnusedAssetsOperation::UnloadUnusedAssets ();
+ ScriptingObjectPtr o = scripting_object_new(MONO_COMMON.asyncOperation);
+ ScriptingObjectWithIntPtrField<AsyncOperation>(o).SetPtr(result);
+ return o;
+ }
+
+END
+
+
+OBSOLETE warning Use SerializeField on the private variables that you want to be serialized instead
+CLASS SerializePrivateVariables : Attribute
+END
+
+// Priority of a thread.
+ENUM ThreadPriority
+ // Lowest thread priority
+ Low = 0,
+ // Below normal thread priority
+ BelowNormal = 1,
+ // Normal thread priority
+ Normal = 2,
+ // Highest thread priority
+ High = 4
+END
+
+
+// Force Unity to serialize a private field.
+
+
+
+CLASS SerializeField : Attribute
+END
+
+// Controls the [[wiki:Profiler]] from script.
+CLASS Profiler
+
+ // *undocumented*
+ CUSTOM_PROP static bool supported
+ {
+ #if ENABLE_PROFILER
+ return GetBuildSettings().hasPROVersion;
+ #else
+ return false;
+ #endif
+ }
+
+
+ // Sets profiler output file in built players.
+ CUSTOM_PROP static string logFile
+ {
+ #if ENABLE_PROFILER
+ return scripting_string_new(UnityProfiler::Get().GetLogPath());
+ #else
+ return SCRIPTING_NULL;
+ #endif
+ }
+ {
+ #if ENABLE_PROFILER
+ if (!GetBuildSettings().hasPROVersion)
+ {
+ ErrorString("Profiler is only supported in Unity Pro.");
+ return;
+ }
+ UnityProfiler::Get().SetLogPath(value);
+ #else
+ ErrorString("Profiler is not supported in this build");
+ #endif
+ }
+
+ // Sets profiler output file in built players.
+ CUSTOM_PROP static bool enableBinaryLog
+ {
+ #if ENABLE_PROFILER
+ return UnityProfiler::Get().BinaryLogEnabled();
+ #else
+ return false;
+ #endif
+ }
+ {
+ #if ENABLE_PROFILER
+ if (!GetBuildSettings().hasPROVersion)
+ {
+ ErrorString("Profiler is only supported in Unity Pro.");
+ return;
+ }
+ UnityProfiler::Get().EnableBinaryLog(value);
+ #else
+ ErrorString("Profiler is not supported in this build");
+ #endif
+ }
+
+ // Enables the Profiler.
+ CUSTOM_PROP static bool enabled
+ {
+ #if ENABLE_PROFILER
+ return UnityProfiler::Get().GetEnabled();
+ #else
+ return false;
+ #endif
+ }
+ {
+ #if ENABLE_PROFILER
+ if (!GetBuildSettings().hasPROVersion)
+ {
+ ErrorString("Profiler is only supported in Unity Pro.");
+ return;
+ }
+ return UnityProfiler::Get().SetEnabled(value);
+ #else
+ ErrorString("Profiler is not supported in this build");
+ #endif
+ }
+
+
+ // Displays the recorded profiledata in the profiler.
+ CSRAW [System.Diagnostics.ConditionalAttribute("ENABLE_PROFILER")]
+ CUSTOM static void AddFramesFromFile (string file)
+ {
+ #if ENABLE_PROFILER
+ if(file.Length() == 0)
+ {
+ ErrorString ("AddFramesFromFile: Invalid empty path");
+ return;
+ }
+ UnityProfiler::Get().AddFramesFromFile(file);
+ #endif
+ }
+
+
+ /// *listonly*
+ CSRAW [System.Diagnostics.ConditionalAttribute("ENABLE_PROFILER")]
+ CSRAW static public void BeginSample(string name)
+ {
+ BeginSampleOnly(name);
+ }
+
+ // Begin profiling a piece of code with a custom label.
+ CSRAW [System.Diagnostics.ConditionalAttribute("ENABLE_PROFILER")]
+ CUSTOM static void BeginSample(string name, Object targetObject)
+ {
+ #if ENABLE_PROFILER
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof && prof->GetIsActive())
+ prof->BeginSampleDynamic(name, targetObject);
+ #endif
+ }
+
+ CUSTOM private static void BeginSampleOnly(string name)
+ {
+ #if ENABLE_PROFILER
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof && prof->GetIsActive())
+ prof->BeginSampleDynamic(name, NULL);
+ #endif
+ }
+
+ // End profiling a piece of code with a custom label.
+ CSRAW [System.Diagnostics.ConditionalAttribute("ENABLE_PROFILER")]
+ CUSTOM static void EndSample ()
+ {
+ #if ENABLE_PROFILER
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof)
+ prof->EndSampleDynamic();
+ #endif
+ }
+
+ // Heap size used by the program
+ CUSTOM_PROP static uint usedHeapSize
+ {
+ #if ENABLE_PROFILER
+ return GetUsedHeapSize();
+ #else
+ return 0;
+ #endif
+ }
+
+ // Returns the runtime memory usage of the resource.
+
+ CUSTOM static int GetRuntimeMemorySize(Object o)
+ {
+ #if ENABLE_PROFILER
+ return o->GetRuntimeMemorySize();
+ #else
+ return 0;
+ #endif
+ }
+
+ // Returns the size of the mono heap
+ CUSTOM static uint GetMonoHeapSize ()
+ {
+ #if ENABLE_PROFILER && ENABLE_MONO
+ return mono_gc_get_heap_size ();
+ #else
+ return 0;
+ #endif
+ }
+
+ // Returns the used size from mono
+ CUSTOM static uint GetMonoUsedSize ()
+ {
+ #if ENABLE_PROFILER && ENABLE_MONO
+ return mono_gc_get_used_size ();
+ #else
+ return 0;
+ #endif
+ }
+ ///*undocumented*
+ CUSTOM static uint GetTotalAllocatedMemory()
+ {
+ #if ENABLE_MEMORY_MANAGER
+ return GetMemoryManager().GetTotalAllocatedMemory();
+ #else
+ return 0;
+ #endif
+ }
+ ///*undocumented*
+ CUSTOM static uint GetTotalUnusedReservedMemory()
+ {
+ #if ENABLE_MEMORY_MANAGER
+ return GetMemoryManager().GetTotalUnusedReservedMemory();
+ #else
+ return 0;
+ #endif
+ }
+ ///*undocumented*
+ CUSTOM static uint GetTotalReservedMemory()
+ {
+ #if ENABLE_MEMORY_MANAGER
+ return GetMemoryManager().GetTotalReservedMemory();
+ #else
+ return 0;
+ #endif
+ }
+
+ //CUSTOM static void SharkBeginRemoteProfiling ()
+ //{
+ // #if ENABLE_SHARK_PROFILE
+ // SharkBeginRemoteProfiling();
+ // #endif
+ //}
+ //
+ //CUSTOM static void SharkEndRemoteProfiling ()
+ //{
+ // #if ENABLE_SHARK_PROFILE
+ // SharkEndRemoteProfiling();
+ // #endif
+ //}
+END
+
+CSRAW }
+
+CSRAW
+namespace UnityEngineInternal
+{
+
+using UnityEngine;
+
+//*undocumented*
+// class for reproduction framework
+CONDITIONAL ENABLE_MONO
+CLASS Reproduction
+
+ CUSTOM static void CaptureScreenshot ()
+ {
+#if SUPPORT_REPRODUCE_LOG
+ CaptureScreenshotReproduction(true);
+#else
+ Scripting::RaiseMonoException("This method only works with internal development builds.");
+#endif
+ }
+
+END
+
+CSRAW }
diff --git a/Runtime/Export/CanConvertToFlash.cs b/Runtime/Export/CanConvertToFlash.cs
new file mode 100644
index 0000000..7e1da8c
--- /dev/null
+++ b/Runtime/Export/CanConvertToFlash.cs
@@ -0,0 +1,17 @@
+using System.Diagnostics;
+
+namespace UnityEngine
+{
+[Conditional("UNITY_FLASH")]
+internal class CanConvertToFlashAttribute : System.Attribute
+{
+ public CanConvertToFlashAttribute() {}
+ public CanConvertToFlashAttribute(params string[] members) {}
+}
+// ToDo: Move this to generic place
+[Conditional("UNITY_FLASH"), Conditional("UNITY_WINRT")]
+internal class MarshallOnlyFirstField : System.Attribute
+{
+}
+
+}
diff --git a/Runtime/Export/ClassLibraryInitializer.cs b/Runtime/Export/ClassLibraryInitializer.cs
new file mode 100644
index 0000000..d561399
--- /dev/null
+++ b/Runtime/Export/ClassLibraryInitializer.cs
@@ -0,0 +1,17 @@
+namespace UnityEngine
+{
+ internal static class ClassLibraryInitializer
+ {
+ static void Init()
+ {
+#if !UNITY_FLASH && !UNITY_WEBGL && !UNITY_WINRT
+ UnityLogWriter.Init();
+#endif
+
+#if UNITY_STANDALONE || (WEBPLUG && !UNITY_NACL)
+ if (Application.platform.ToString().Contains("WebPlayer"))
+ System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.DefaultSurrogateSelector = new UnityEngine.Serialization.UnitySurrogateSelector();
+#endif
+ }
+ }
+} \ No newline at end of file
diff --git a/Runtime/Export/Coroutines.cs b/Runtime/Export/Coroutines.cs
new file mode 100644
index 0000000..3333b40
--- /dev/null
+++ b/Runtime/Export/Coroutines.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections;
+using System.Reflection;
+
+namespace UnityEngine
+{
+#if ENABLE_MONO || UNITY_WP8
+internal class SetupCoroutine
+{
+ static public object InvokeMember (object behaviour, string name, object variable)
+ {
+ object[] args = null;
+ if (variable != null)
+ {
+ args = new System.Object[1];
+ args[0] = variable;
+ }
+
+ return behaviour.GetType ().InvokeMember (name, BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, behaviour, args, null, null, null);
+ }
+
+ static public object InvokeStatic (Type klass, string name, object variable)
+ {
+ object[] args = null;
+ if (variable != null)
+ {
+ args = new System.Object[1];
+ args[0] = variable;
+ }
+
+ return klass.InvokeMember (name, BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, null, null, args, null, null, null);
+ }
+
+}
+#endif
+}
diff --git a/Runtime/Export/CppAttributes.cs b/Runtime/Export/CppAttributes.cs
new file mode 100644
index 0000000..57c1e13
--- /dev/null
+++ b/Runtime/Export/CppAttributes.cs
@@ -0,0 +1,52 @@
+using System;
+
+namespace UnityEngine
+{
+ //TodoBC: make all these internal next time we do a breaking release
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]
+ internal class CppIncludeAttribute : Attribute
+ {
+ public CppIncludeAttribute(string header) {}
+ }
+
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]
+ internal class CppDefineAttribute : Attribute
+ {
+ public CppDefineAttribute(string symbol, string value) {}
+ }
+
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple = false)]
+ internal class CppBodyAttribute : Attribute
+ {
+ public CppBodyAttribute(string body) {}
+ }
+
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
+ internal class CppInvokeAttribute : Attribute
+ {
+ }
+
+ [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
+ internal class CppPropertyBodyAttribute : Attribute
+ {
+ public CppPropertyBodyAttribute(string getterBody, string setterBody) {}
+ public CppPropertyBodyAttribute(string getterBody) {}
+ }
+
+ [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
+ internal class CppPropertyAttribute : Attribute
+ {
+ public CppPropertyAttribute(string getter, string setter) { }
+ public CppPropertyAttribute(string getter) { }
+ }
+
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, AllowMultiple = false)]
+ public class ThreadSafeAttribute : Attribute {}
+
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, AllowMultiple = false)]
+ public class ConstructorSafeAttribute : Attribute {}
+
+ [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
+ internal class WritableAttribute: Attribute { }
+}
+
diff --git a/Runtime/Export/CrashReporter.txt b/Runtime/Export/CrashReporter.txt
new file mode 100644
index 0000000..2f689c9
--- /dev/null
+++ b/Runtime/Export/CrashReporter.txt
@@ -0,0 +1,150 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+CSRAW
+using System;
+using System.Collections.Generic;
+
+namespace UnityEngine
+{
+
+CLASS CrashReport
+ CSRAW static List<CrashReport> internalReports;
+ CSRAW static object reportsLock = new object();
+
+ CSRAW static int Compare(CrashReport c1, CrashReport c2)
+ {
+ long t1 = c1.time.Ticks;
+ long t2 = c2.time.Ticks;
+ if (t1 > t2)
+ return 1;
+ if (t1 < t2)
+ return -1;
+ return 0;
+ }
+
+ CSRAW static void PopulateReports()
+ {
+ lock (reportsLock)
+ {
+ if (internalReports != null)
+ return;
+
+ string[] ids = GetReports();
+ internalReports = new List<CrashReport>(ids.Length);
+ foreach (var id in ids)
+ {
+ double secondsSinceUnixEpoch;
+ string text;
+ GetReportData(id, out secondsSinceUnixEpoch, out text);
+ DateTime time = new DateTime(1970, 1, 1).AddSeconds(secondsSinceUnixEpoch);
+ internalReports.Add(new CrashReport(id, time, text));
+ }
+ internalReports.Sort(Compare);
+ }
+ }
+
+ CSRAW public static CrashReport[] reports
+ {
+ get
+ {
+ PopulateReports();
+ lock (reportsLock)
+ {
+ return internalReports.ToArray();
+ }
+ }
+ }
+
+ CSRAW public static CrashReport lastReport
+ {
+ get
+ {
+ PopulateReports();
+ lock (reportsLock)
+ {
+ if (internalReports.Count > 0)
+ {
+ return internalReports[internalReports.Count - 1];
+ }
+ }
+
+ return null;
+ }
+ }
+
+ CSRAW public static void RemoveAll()
+ {
+ foreach (var report in reports)
+ report.Remove();
+ }
+
+ CSRAW readonly string id;
+ CSRAW public readonly DateTime time;
+ CSRAW public readonly string text;
+
+ CSRAW CrashReport(string id, DateTime time, string text)
+ {
+ this.id = id;
+ this.time = time;
+ this.text = text;
+ }
+
+ CSRAW public void Remove()
+ {
+ if (RemoveReport(id))
+ {
+ lock (reportsLock)
+ {
+ internalReports.Remove(this);
+ }
+ }
+ }
+
+ THREAD_SAFE CUSTOM private static string[] GetReports()
+ {
+ if (!GetBuildSettings().hasAdvancedVersion)
+ {
+ ErrorString("Crash reports are only supported in Unity Pro.");
+ std::vector<std::string> reports;
+ return Scripting::StringVectorToMono(reports);
+ }
+
+#if UNITY_IPHONE
+ extern ScriptingArrayPtr GetCrashReports();
+ return GetCrashReports();
+#else
+ std::vector<std::string> reports;
+ return Scripting::StringVectorToMono(reports);
+#endif
+ }
+
+ THREAD_SAFE CUSTOM private static void GetReportData(string id, out double secondsSinceUnixEpoch, out string text)
+ {
+#if UNITY_IPHONE
+ extern void GetCrashReportData(ICallString id, double* secondsSinceUnixEpoch, ICallString* text);
+ GetCrashReportData(id, secondsSinceUnixEpoch, text);
+#else
+ *secondsSinceUnixEpoch = 0.0;
+#if !UNITY_WINRT && !UNITY_FLASH
+ text->str = scripting_string_new("");
+#endif
+#endif
+ }
+
+ THREAD_SAFE CUSTOM private static bool RemoveReport(string id)
+ {
+#if UNITY_IPHONE
+ extern bool RemoveCrashReport(ICallString id);
+ return RemoveCrashReport(id);
+#else
+ return false;
+#endif
+ }
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/CursorBindings.txt b/Runtime/Export/CursorBindings.txt
new file mode 100644
index 0000000..104bad4
--- /dev/null
+++ b/Runtime/Export/CursorBindings.txt
@@ -0,0 +1,44 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/BaseClasses/Cursor.h"
+
+using namespace std;
+
+CSRAW
+using System;
+using UnityEngine;
+using Object=UnityEngine.Object;
+
+namespace UnityEngine
+{
+// How should the custom cursor be rendered
+ENUM CursorMode
+ // Use hardware cursors on supported platforms.
+ Auto = 0,
+
+ // Force the use of software cursors.
+ ForceSoftware = 1,
+END
+
+// Cursor API for setting the cursor that is used for rendering.
+CLASS Cursor
+
+ // Change the mouse cursor to the set texture OnMouseEnter.
+
+ CSRAW static void SetCursor (Texture2D texture, CursorMode cursorMode)
+ {
+ SetCursor (texture, Vector2.zero, cursorMode);
+ }
+
+ //
+ CUSTOM static void SetCursor (Texture2D texture, Vector2 hotspot, CursorMode cursorMode)
+ {
+ Cursors::SetCursor (texture, hotspot, cursorMode);
+ }
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/EventBindings.txt b/Runtime/Export/EventBindings.txt
new file mode 100644
index 0000000..fe49b68
--- /dev/null
+++ b/Runtime/Export/EventBindings.txt
@@ -0,0 +1,1066 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+
+/*
+ Mono defines a bool as either 1 or 2 bytes.
+ On windows a bool on the C++ side needs to be 2 bytes.
+ We use the typemap to map bool's to short's.
+ When using the C++ keyword and you want to export a bool value
+ to mono you have to use a short on the C++ side.
+*/
+
+
+void PauseEditor ();
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+C++RAW
+ static void CleanupInputEvent(void* inputEvent){ delete (InputEvent*)inputEvent; };
+
+// A UnityGUI event.
+CSRAW [StructLayout (LayoutKind.Sequential)]
+CLASS Event
+
+CSRAW
+ // *undocumented
+ public Event () {
+ Init ();
+ }
+ THREAD_SAFE
+ CUSTOM private void Init () {
+ InputEvent* newEvent = new InputEvent ();
+ self.SetPtr(newEvent,CleanupInputEvent);
+ newEvent->Init();
+ }
+
+ // Copy an event
+ CSRAW public Event (Event other) {
+ if (other == null)
+ throw new ArgumentException ("Event to copy from is null.");
+ InitCopy (other);
+ }
+
+ // *undocumented
+ CSRAW private Event (IntPtr ptr) {
+ InitPtr (ptr);
+ }
+
+ // *undocumented
+ CSRAW ~Event () {
+ Cleanup ();
+ }
+ THREAD_SAFE
+ CUSTOM private void Cleanup () {
+ CleanupInputEvent(self.GetPtr());
+ }
+
+ THREAD_SAFE
+ CUSTOM private void InitCopy (Event other)
+ {
+ self.SetPtr(new InputEvent (*other), CleanupInputEvent);
+ }
+
+ THREAD_SAFE
+ CUSTOM private void InitPtr (IntPtr ptr)
+ {
+ self.SetPtr((InputEvent*)ptr, CleanupInputEvent);
+ }
+
+
+CSRAW
+ [System.NonSerialized] [NotRenamed]
+ internal IntPtr m_Ptr;
+
+ CUSTOM_PROP EventType rawType
+ { return self->type; }
+
+ // The type of event.
+ CUSTOM_PROP EventType type
+ { return IMGUI::GetEventType (GetGUIState(), *self); }
+ { self->type = value; }
+
+ CUSTOM EventType GetTypeForControl (int controlID)
+ { return IMGUI::GetEventTypeForControl (GetGUIState(), *self, controlID); }
+
+
+ /// Information about the current mouse or touch event, available during On*Event series of callbacks.
+ CONDITIONAL ENABLE_NEW_EVENT_SYSTEM
+ CUSTOM_PROP Touch touch { return self->touch; }
+
+ // The mouse position.
+ CSRAW public Vector2 mousePosition { get {
+ Vector2 tmp; Internal_GetMousePosition(out tmp); return tmp; }
+ set { Internal_SetMousePosition (value); }
+ }
+ CUSTOM private void Internal_SetMousePosition (Vector2 value)
+ {
+#if ENABLE_NEW_EVENT_SYSTEM
+ self->touch.pos = value;
+#else
+ self->mousePosition = value;
+#endif
+ }
+ CUSTOM private void Internal_GetMousePosition (out Vector2 value)
+ {
+#if ENABLE_NEW_EVENT_SYSTEM
+ *value = self->touch.pos;
+#else
+ *value = self->mousePosition;
+#endif
+ }
+
+
+ // The relative movement of the mouse compared to last event.
+ CSRAW public Vector2 delta { get {
+ Vector2 tmp; Internal_GetMouseDelta(out tmp); return tmp; }
+ set { Internal_SetMouseDelta (value); }
+ }
+ CUSTOM private void Internal_SetMouseDelta (Vector2 value)
+ {
+#if ENABLE_NEW_EVENT_SYSTEM
+ self->touch.deltaPos = value;
+#else
+ self->delta = value;
+#endif
+ }
+ CUSTOM private void Internal_GetMouseDelta (out Vector2 value)
+ {
+#if ENABLE_NEW_EVENT_SYSTEM
+ *value = self->touch.deltaPos;
+#else
+ *value = self->delta;
+#endif
+ }
+
+ OBSOLETE error Use HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
+ CSRAW public Ray mouseRay { get { return new Ray (Vector3.up, Vector3.up); } set { }}
+
+ // Which mouse button was pressed.
+ CUSTOM_PROP int button
+ { return self->button; }
+ { self->button = value; }
+
+ // Which modifier keys are held down.
+ CUSTOM_PROP EventModifiers modifiers
+ { return self->modifiers; }
+ { self->modifiers = value; }
+
+ // *undocumented*
+ CUSTOM_PROP float pressure
+ { return self->pressure; }
+ { self->pressure = value; }
+
+ // How many consecutive mouse clicks have we received.
+ // Detects consecutive clicks and prints them.
+ //
+ CUSTOM_PROP int clickCount
+ { return self->clickCount; }
+ { self->clickCount = value; }
+
+ // The character typed.
+ // Detects characters pressed and prints them.
+ //
+ CUSTOM_PROP char character
+ { return self->character; }
+ { self->character = value; }
+
+ // The name of an ExecuteCommand or ValidateCommand Event
+ CUSTOM_PROP string commandName
+ {
+ char* commandString = self->commandString;
+ return scripting_string_new(commandString == NULL ? "" : commandString);
+ }
+ {
+#if ENABLE_MONO
+ char* oldPtr = reinterpret_cast<char*>(self->commandString);
+ delete[] oldPtr;
+ char *str = mono_string_to_utf8 (value.str);
+ self->commandString = new char[strlen (str) + 1];
+ strcpy (self->commandString, str);
+#endif
+ }
+
+ // The raw key code for keyboard events.
+ CUSTOM_PROP KeyCode keyCode
+ { return self->keycode; }
+ { self->keycode = value; }
+
+ // Is Shift held down? (RO)
+ CSRAW public bool shift { get {
+ return (modifiers & EventModifiers.Shift) != 0; }
+ set { if (!value) modifiers &= ~EventModifiers.Shift; else modifiers |= EventModifiers.Shift; }
+ }
+
+ // Is Control key held down? (RO)
+ CSRAW public bool control { get {
+ return (modifiers & EventModifiers.Control) != 0; }
+ set { if (!value) modifiers &= ~EventModifiers.Control; else modifiers |= EventModifiers.Control; }
+ }
+
+ // Is Alt/Option key held down? (RO)
+ CSRAW public bool alt { get {
+ return (modifiers & EventModifiers.Alt) != 0; }
+ set { if (!value) modifiers &= ~EventModifiers.Alt; else modifiers |= EventModifiers.Alt; } }
+
+
+ // Is Command/Windows key held down? (RO)
+ CSRAW public bool command { get {
+ return (modifiers & EventModifiers.Command) != 0; }
+ set { if (!value) modifiers &= ~EventModifiers.Command; else modifiers |= EventModifiers.Command; }
+ }
+
+ // Is Caps Lock on? (RO)
+ CSRAW public bool capsLock { get {
+ return (modifiers & EventModifiers.CapsLock) != 0; }
+ set { if (!value) modifiers &= ~EventModifiers.CapsLock; else modifiers |= EventModifiers.CapsLock; }
+ }
+
+ // Is the current keypress on the numeric keyboard? (RO)
+ CSRAW public bool numeric { get {
+ return (modifiers & EventModifiers.Numeric) != 0; }
+ set { if (!value) modifiers &= ~EventModifiers.Shift; else modifiers |= EventModifiers.Shift; }
+ }
+
+ // Is the current keypress a function key? (RO)
+ CSRAW public bool functionKey { get { return (modifiers & EventModifiers.FunctionKey) != 0; } }
+
+ // The current event that's being processed right now.
+ // TODO: set this to null outside the event loop.
+ //
+ CSRAW public static Event current { get {
+ // return null if Event.current is queried outside OnGUI
+ // Only in editor because of backwards compat.
+ #if UNITY_EDITOR
+ if (GUIUtility.Internal_GetGUIDepth () > 0)
+ return s_Current;
+ else
+ return null;
+ #else
+ return s_Current;
+ #endif
+ }
+ set
+ {
+ if (value != null)
+ s_Current = value;
+ else
+ s_Current = s_MasterEvent;
+ Internal_SetNativeEvent (s_Current.m_Ptr);
+ }
+ }
+ CSRAW static Event s_Current;
+ CSRAW static Event s_MasterEvent;
+
+ CUSTOM static private void Internal_SetNativeEvent (IntPtr ptr)
+ {
+ GetGUIState().Internal_SetManagedEvent (ptr);
+ }
+
+ CSRAW static private void Internal_MakeMasterEventCurrent ()
+ {
+ if (s_MasterEvent == null)
+ s_MasterEvent = new Event ();
+ s_Current = s_MasterEvent;
+ Internal_SetNativeEvent (s_MasterEvent.m_Ptr);
+ }
+
+ // Use this event.
+ CUSTOM void Use () { self->Use(); }
+
+ // Is this event a keyboard event? (RO)
+ CSRAW public bool isKey { get {
+ EventType t = type; return t == EventType.KeyDown || t == EventType.KeyUp; }
+ }
+
+ // Is this event a mouse event? (RO)
+ CSRAW public bool isMouse { get {
+ EventType t = type; return t == EventType.MouseMove || t == EventType.MouseDown || t == EventType.MouseUp || t == EventType.MouseDrag; }
+ }
+
+ // Create a keyboard event.
+ CSRAW public static Event KeyboardEvent (string key) {
+ Event evt = new Event ();
+ evt.type = EventType.KeyDown;
+ // Can't use string.IsNullOrEmpty because it's not supported in NET 1.1
+ if (key == null || key == String.Empty)
+ return evt;
+ int startIdx = 0;
+ bool found = false;
+ do {
+ found = true;
+ if (startIdx >= key.Length)
+ { found = false; break; }
+ switch (key[startIdx]) {
+ case '&': // Alt
+ evt.modifiers |= EventModifiers.Alt; startIdx++;
+ break;
+ case '^': // Ctrl
+ evt.modifiers |= EventModifiers.Control; startIdx++;
+ break;
+ case '%':
+ evt.modifiers |= EventModifiers.Command; startIdx++;
+ break;
+ case '#':
+ evt.modifiers |= EventModifiers.Shift; startIdx++;
+ break;
+ default:
+ found = false;
+ break;
+ }
+ } while (found);
+ string subStr = key.Substring (startIdx, key.Length - startIdx).ToLower();
+ switch (subStr) {
+ case "[0]": evt.character = '0'; evt.keyCode = KeyCode.Keypad0; break;
+ case "[1]": evt.character = '1'; evt.keyCode = KeyCode.Keypad1; break;
+ case "[2]": evt.character = '2'; evt.keyCode = KeyCode.Keypad2; break;
+ case "[3]": evt.character = '3'; evt.keyCode = KeyCode.Keypad3; break;
+ case "[4]": evt.character = '4'; evt.keyCode = KeyCode.Keypad4; break;
+ case "[5]": evt.character = '5'; evt.keyCode = KeyCode.Keypad5; break;
+ case "[6]": evt.character = '6'; evt.keyCode = KeyCode.Keypad6; break;
+ case "[7]": evt.character = '7'; evt.keyCode = KeyCode.Keypad7; break;
+ case "[8]": evt.character = '8'; evt.keyCode = KeyCode.Keypad8; break;
+ case "[9]": evt.character = '9'; evt.keyCode = KeyCode.Keypad9; break;
+ case "[.]": evt.character = '.'; evt.keyCode = KeyCode.KeypadPeriod; break;
+ case "[/]": evt.character = '/'; evt.keyCode = KeyCode.KeypadDivide; break;
+ case "[-]": evt.character = '-'; evt.keyCode = KeyCode.KeypadMinus; break;
+ case "[+]": evt.character = '+'; evt.keyCode = KeyCode.KeypadPlus; break;
+ case "[=]": evt.character = '='; evt.keyCode = KeyCode.KeypadEquals; break;
+ case "[equals]": evt.character = '='; evt.keyCode = KeyCode.KeypadEquals; break;
+ case "[enter]": evt.character = '\n'; evt.keyCode = KeyCode.KeypadEnter; break;
+ case "up": /*evt.character = (char)63232; */evt.keyCode = KeyCode.UpArrow; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "down": /*evt.character = (char)63233; */evt.keyCode = KeyCode.DownArrow; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "left": /*evt.character = (char)63234; */evt.keyCode = KeyCode.LeftArrow; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "right": /*evt.character = (char)63235; */evt.keyCode = KeyCode.RightArrow; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "insert": evt.keyCode = KeyCode.Insert; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "home": /*evt.character = (char)63273; */evt.keyCode = KeyCode.Home; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "end": /*evt.character = (char)63275; */evt.keyCode = KeyCode.End; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "pgup": /*evt.character = (char)63276; */evt.keyCode = KeyCode.PageDown; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "page up": /*evt.character = (char)63276; */evt.keyCode = KeyCode.PageUp; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "pgdown": /*evt.character = (char)63277; */evt.keyCode = KeyCode.PageUp; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "page down": /*evt.character = (char)63277; */evt.keyCode = KeyCode.PageDown; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "backspace": /*evt.character = (char)127; */evt.keyCode = KeyCode.Backspace; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "delete": /*evt.character = (char)63272; */evt.keyCode = KeyCode.Delete; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "tab": /*evt.character = (char)9; */evt.keyCode = KeyCode.Tab; break;
+ case "f1": /*evt.character = (char)63236; */evt.keyCode = KeyCode.F1; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "f2": /*evt.character = (char)63237; */evt.keyCode = KeyCode.F2; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "f3": /*evt.character = (char)63238; */evt.keyCode = KeyCode.F3; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "f4": /*evt.character = (char)63239; */evt.keyCode = KeyCode.F4; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "f5": /*evt.character = (char)63240; */evt.keyCode = KeyCode.F5; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "f6": /*evt.character = (char)63241; */evt.keyCode = KeyCode.F6; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "f7": /*evt.character = (char)63242; */evt.keyCode = KeyCode.F7; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "f8": /*evt.character = (char)63243; */evt.keyCode = KeyCode.F8; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "f9": /*evt.character = (char)63244; */evt.keyCode = KeyCode.F9; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "f10": /*evt.character = (char)63245; */evt.keyCode = KeyCode.F10; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "f11": /*evt.character = (char)63246; */evt.keyCode = KeyCode.F11; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "f12": /*evt.character = (char)63247; */evt.keyCode = KeyCode.F12; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "f13": /*evt.character = (char)63248; */evt.keyCode = KeyCode.F13; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "f14": /*evt.character = (char)63249; */evt.keyCode = KeyCode.F14; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "f15": /*evt.character = (char)63250; */evt.keyCode = KeyCode.F15; evt.modifiers |= EventModifiers.FunctionKey; break;
+ case "[esc]": evt.keyCode = KeyCode.Escape; break;
+ case "return": evt.character = '\n'; evt.keyCode = KeyCode.Return; evt.modifiers &= ~EventModifiers.FunctionKey; break;
+ case "space": evt.keyCode = KeyCode.Space; evt.character = ' '; evt.modifiers &= ~EventModifiers.FunctionKey; break;
+ default:
+ if (subStr.Length != 1)
+ {
+ // soren: try this first
+ try {
+ evt.keyCode = (KeyCode)Enum.Parse(typeof(KeyCode), subStr, true);
+ }
+ catch (ArgumentException)
+ {
+ Debug.LogError (UnityString.Format ("Unable to find key name that matches '{0}'", subStr));
+ }
+ }
+ else
+ {
+ evt.character = subStr.ToLower()[0];
+ evt.keyCode = (KeyCode)evt.character;
+ if ((int)evt.modifiers != 0)
+ evt.character = (char)0;
+ }
+ break;
+ }
+ return evt;
+ }
+
+ // Calculate the hash code
+ CSRAW public override int GetHashCode( ) {
+ int hc = 1;
+ if (isKey)
+ hc = (ushort)keyCode;
+ if (isMouse)
+ hc = mousePosition.GetHashCode ();
+ hc = hc*37 | (int)modifiers;
+// Debug.Log (hc + " GetHashCode of " + ToString());
+ return hc;
+ }
+
+
+ CSRAW public override bool Equals (object obj) {
+ if (obj == null)
+ return false;
+ if (Object.ReferenceEquals (this, obj))
+ return true;
+ if (obj.GetType () != GetType())
+ return false;
+
+
+ Event rhs = (Event)obj;
+ if (type != rhs.type || modifiers != rhs.modifiers)
+ return false;
+ if (isKey)
+ return keyCode == rhs.keyCode && modifiers == rhs.modifiers;
+ if (isMouse)
+ return mousePosition == rhs.mousePosition;
+ return false;
+ }
+
+
+
+ CSRAW public override string ToString( )
+ {
+ if (isKey) {
+ if ((int)character == 0)
+ return UnityString.Format ("Event:{0} Character:\\0 Modifiers:{1} KeyCode:{2}", type, modifiers, keyCode);
+ else {
+ return UnityString.Format ("Event:" +type + " Character:" + (int)(character) + " Modifiers:" + modifiers + " KeyCode:" + keyCode);
+ }
+ }
+ if (isMouse)
+ return UnityString.Format ("Event: {0} Position: {1} Modifiers: {2}", type, mousePosition, modifiers);
+
+ if (type == EventType.ExecuteCommand || type == EventType.ValidateCommand)
+ return UnityString.Format ("Event: {0} \"{1}\"", type, commandName);
+
+ return "" + type;
+ }
+C++RAW
+
+
+END
+
+
+// Key codes returned by Event.keyCode. These map directly to a physical key on the keyboard.
+ENUM KeyCode
+ // Not assigned (never returned as the result of a keystroke)
+ None = 0,
+ // The backspace key
+ Backspace = 8,
+ // The forward delete key
+ Delete = 127,
+ // The tab key
+ Tab = 9,
+ // The Clear key
+ Clear = 12,
+ // Return key
+ Return = 13,
+ // Pause on PC machines
+ Pause = 19,
+ // Escape key
+ Escape = 27,
+ // Space key
+ Space = 32,
+
+ // Numeric keypad 0
+ Keypad0 = 256,
+ // Numeric keypad 1
+ Keypad1 = 257,
+ // Numeric keypad 2
+ Keypad2 = 258,
+ // Numeric keypad 3
+ Keypad3 = 259,
+ // Numeric keypad 4
+ Keypad4 = 260,
+ // Numeric keypad 5
+ Keypad5 = 261,
+ // Numeric keypad 6
+ Keypad6 = 262,
+ // Numeric keypad 7
+ Keypad7 = 263,
+ // Numeric keypad 8
+ Keypad8 = 264,
+ // Numeric keypad 9
+ Keypad9 = 265,
+ // Numeric keypad '.'
+ KeypadPeriod = 266,
+ // Numeric keypad '/'
+ KeypadDivide = 267,
+ // Numeric keypad '*'
+ KeypadMultiply = 268,
+ // Numeric keypad '-'
+ KeypadMinus = 269,
+ // Numeric keypad '+'
+ KeypadPlus = 270,
+ // Numeric keypad enter
+ KeypadEnter = 271,
+ // Numeric keypad '='
+ KeypadEquals = 272,
+
+ // Up arrow key
+ UpArrow = 273,
+ // Down arrow key
+ DownArrow = 274,
+ // Right arrow key
+ RightArrow = 275,
+ // Left arrow key
+ LeftArrow = 276,
+ // Insert key key
+ Insert = 277,
+ // Home key
+ Home = 278,
+ // End key
+ End = 279,
+ // Page up
+ PageUp = 280,
+ // Page down
+ PageDown = 281,
+
+ // F1 function key
+ F1 = 282,
+ // F2 function key
+ F2 = 283,
+ // F3 function key
+ F3 = 284,
+ // F4 function key
+ F4 = 285,
+ // F5 function key
+ F5 = 286,
+ // F6 function key
+ F6 = 287,
+ // F7 function key
+ F7 = 288,
+ // F8 function key
+ F8 = 289,
+ // F9 function key
+ F9 = 290,
+ // F10 function key
+ F10 = 291,
+ // F11 function key
+ F11 = 292,
+ // F12 function key
+ F12 = 293,
+ // F13 function key
+ F13 = 294,
+ // F14 function key
+ F14 = 295,
+ // F15 function key
+ F15 = 296,
+
+ // The '0' key on the top of the alphanumeric keyboard.
+ Alpha0 = 48,
+ // The '1' key on the top of the alphanumeric keyboard.
+ Alpha1 = 49,
+ // The '2' key on the top of the alphanumeric keyboard.
+ Alpha2 = 50,
+ // The '3' key on the top of the alphanumeric keyboard.
+ Alpha3 = 51,
+ // The '4' key on the top of the alphanumeric keyboard.
+ Alpha4 = 52,
+ // The '5' key on the top of the alphanumeric keyboard.
+ Alpha5 = 53,
+ // The '6' key on the top of the alphanumeric keyboard.
+ Alpha6 = 54,
+ // The '7' key on the top of the alphanumeric keyboard.
+ Alpha7 = 55,
+ // The '8' key on the top of the alphanumeric keyboard.
+ Alpha8 = 56,
+ // The '9' key on the top of the alphanumeric keyboard.
+ Alpha9 = 57,
+
+ // Exclamation mark key '!'
+ Exclaim = 33,
+ // Double quote key '"'
+ DoubleQuote = 34,
+ // Hash key '#'
+ Hash = 35,
+ // Dollar sign key '$'
+ Dollar = 36,
+ // Ampersand key '&'
+ Ampersand = 38,
+ // Quote key '
+ Quote = 39,
+ // Left Parenthesis key '('
+ LeftParen = 40,
+ // Right Parenthesis key ')'
+ RightParen = 41,
+ // Asterisk key '*'
+ Asterisk = 42,
+ // Plus key '+'
+ Plus = 43,
+ // Comma ',' key
+ Comma = 44,
+
+ // Minus '-' key
+ Minus = 45,
+ // Period '.' key
+ Period = 46,
+ // Slash '/' key
+ Slash = 47,
+
+ // Colon ':' key
+ Colon = 58,
+ // Semicolon ';' key
+ Semicolon = 59,
+ // Less than '<' key
+ Less = 60,
+ // Equals '=' key
+ Equals = 61,
+ // Greater than '>' key
+ Greater = 62,
+ // Question mark '?' key
+ Question = 63,
+ // At key '@'
+ At = 64,
+
+ // Left square bracket key '['
+ LeftBracket = 91,
+ // Backslash key '\'
+ Backslash = 92,
+ // Right square bracket key ']'
+ RightBracket = 93,
+ // Caret key '^'
+ Caret = 94,
+ // Underscore '_' key
+ Underscore = 95,
+ // Back quote key '`'
+ BackQuote = 96,
+
+ // 'a' key
+ A = 97,
+ // 'b' key
+ B = 98,
+ // 'c' key
+ C = 99,
+ // 'd' key
+ D = 100,
+ // 'e' key
+ E = 101,
+ // 'f' key
+ F = 102,
+ // 'g' key
+ G = 103,
+ // 'h' key
+ H = 104,
+ // 'i' key
+ I = 105,
+ // 'j' key
+ J = 106,
+ // 'k' key
+ K = 107,
+ // 'l' key
+ L = 108,
+ // 'm' key
+ M = 109,
+ // 'n' key
+ N = 110,
+ // 'o' key
+ O = 111,
+ // 'p' key
+ P = 112,
+ // 'q' key
+ Q = 113,
+ // 'r' key
+ R = 114,
+ // 's' key
+ S = 115,
+ // 't' key
+ T = 116,
+ // 'u' key
+ U = 117,
+ // 'v' key
+ V = 118,
+ // 'w' key
+ W = 119,
+ // 'x' key
+ X = 120,
+ // 'y' key
+ Y = 121,
+ // 'z' key
+ Z = 122,
+
+ // Numlock key
+ Numlock = 300,
+ // Capslock key
+ CapsLock = 301,
+ // Scroll lock key
+ ScrollLock = 302,
+ // Right shift key
+ RightShift = 303,
+ // Left shift key
+ LeftShift = 304,
+ // Right Control key
+ RightControl = 305,
+ // Left Control key
+ LeftControl = 306,
+ // Right Alt key
+ RightAlt = 307,
+ // Left Alt key
+ LeftAlt = 308,
+
+ // Left Command key
+ LeftCommand = 310,
+ // Left Command key
+ LeftApple = 310,
+ // Left Windows key
+ LeftWindows = 311,
+ // Right Command key
+ RightCommand = 309,
+ // Right Command key
+ RightApple = 309,
+ // Right Windows key
+ RightWindows = 312,
+ // Alt Gr key
+ AltGr = 313,
+
+ // Help key
+ Help = 315,
+ // Print key
+ Print = 316,
+ // Sys Req key
+ SysReq = 317,
+ // Break key
+ Break = 318,
+ // Menu key
+ Menu = 319,
+
+ // First (primary) mouse button
+ Mouse0 = 323,
+ // Second (secondary) mouse button
+ Mouse1 = 324,
+ // Third mouse button
+ Mouse2 = 325,
+ // Fourth mouse button
+ Mouse3 = 326,
+ // Fifth mouse button
+ Mouse4 = 327,
+ // Sixth mouse button
+ Mouse5 = 328,
+ // Seventh mouse button
+ Mouse6 = 329,
+
+ // Button 0 on any joystick
+ JoystickButton0 = 330,
+ // Button 1 on any joystick
+ JoystickButton1 = 331,
+ // Button 2 on any joystick
+ JoystickButton2 = 332,
+ // Button 3 on any joystick
+ JoystickButton3 = 333,
+ // Button 4 on any joystick
+ JoystickButton4 = 334,
+ // Button 5 on any joystick
+ JoystickButton5 = 335,
+ // Button 6 on any joystick
+ JoystickButton6 = 336,
+ // Button 7 on any joystick
+ JoystickButton7 = 337,
+ // Button 8 on any joystick
+ JoystickButton8 = 338,
+ // Button 9 on any joystick
+ JoystickButton9 = 339,
+ // Button 10 on any joystick
+ JoystickButton10 = 340,
+ // Button 11 on any joystick
+ JoystickButton11 = 341,
+ // Button 12 on any joystick
+ JoystickButton12 = 342,
+ // Button 13 on any joystick
+ JoystickButton13 = 343,
+ // Button 14 on any joystick
+ JoystickButton14 = 344,
+ // Button 15 on any joystick
+ JoystickButton15 = 345,
+ // Button 16 on any joystick
+ JoystickButton16 = 346,
+ // Button 17 on any joystick
+ JoystickButton17 = 347,
+ // Button 18 on any joystick
+ JoystickButton18 = 348,
+ // Button 19 on any joystick
+ JoystickButton19 = 349,
+
+ // Button 0 on first joystick
+ Joystick1Button0 = 350,
+ // Button 1 on first joystick
+ Joystick1Button1 = 351,
+ // Button 2 on first joystick
+ Joystick1Button2 = 352,
+ // Button 3 on first joystick
+ Joystick1Button3 = 353,
+ // Button 4 on first joystick
+ Joystick1Button4 = 354,
+ // Button 5 on first joystick
+ Joystick1Button5 = 355,
+ // Button 6 on first joystick
+ Joystick1Button6 = 356,
+ // Button 7 on first joystick
+ Joystick1Button7 = 357,
+ // Button 8 on first joystick
+ Joystick1Button8 = 358,
+ // Button 9 on first joystick
+ Joystick1Button9 = 359,
+ // Button 10 on first joystick
+ Joystick1Button10 = 360,
+ // Button 11 on first joystick
+ Joystick1Button11 = 361,
+ // Button 12 on first joystick
+ Joystick1Button12 = 362,
+ // Button 13 on first joystick
+ Joystick1Button13 = 363,
+ // Button 14 on first joystick
+ Joystick1Button14 = 364,
+ // Button 15 on first joystick
+ Joystick1Button15 = 365,
+ // Button 16 on first joystick
+ Joystick1Button16 = 366,
+ // Button 17 on first joystick
+ Joystick1Button17 = 367,
+ // Button 18 on first joystick
+ Joystick1Button18 = 368,
+ // Button 19 on first joystick
+ Joystick1Button19 = 369,
+
+ // Button 0 on second joystick
+ Joystick2Button0 = 370,
+ // Button 1 on second joystick
+ Joystick2Button1 = 371,
+ // Button 2 on second joystick
+ Joystick2Button2 = 372,
+ // Button 3 on second joystick
+ Joystick2Button3 = 373,
+ // Button 4 on second joystick
+ Joystick2Button4 = 374,
+ // Button 5 on second joystick
+ Joystick2Button5 = 375,
+ // Button 6 on second joystick
+ Joystick2Button6 = 376,
+ // Button 7 on second joystick
+ Joystick2Button7 = 377,
+ // Button 8 on second joystick
+ Joystick2Button8 = 378,
+ // Button 9 on second joystick
+ Joystick2Button9 = 379,
+ // Button 10 on second joystick
+ Joystick2Button10 = 380,
+ // Button 11 on second joystick
+ Joystick2Button11 = 381,
+ // Button 12 on second joystick
+ Joystick2Button12 = 382,
+ // Button 13 on second joystick
+ Joystick2Button13 = 383,
+ // Button 14 on second joystick
+ Joystick2Button14 = 384,
+ // Button 15 on second joystick
+ Joystick2Button15 = 385,
+ // Button 16 on second joystick
+ Joystick2Button16 = 386,
+ // Button 17 on second joystick
+ Joystick2Button17 = 387,
+ // Button 18 on second joystick
+ Joystick2Button18 = 388,
+ // Button 19 on second joystick
+ Joystick2Button19 = 389,
+
+ // Button 0 on third joystick
+ Joystick3Button0 = 390,
+ // Button 1 on third joystick
+ Joystick3Button1 = 391,
+ // Button 2 on third joystick
+ Joystick3Button2 = 392,
+ // Button 3 on third joystick
+ Joystick3Button3 = 393,
+ // Button 4 on third joystick
+ Joystick3Button4 = 394,
+ // Button 5 on third joystick
+ Joystick3Button5 = 395,
+ // Button 6 on third joystick
+ Joystick3Button6 = 396,
+ // Button 7 on third joystick
+ Joystick3Button7 = 397,
+ // Button 8 on third joystick
+ Joystick3Button8 = 398,
+ // Button 9 on third joystick
+ Joystick3Button9 = 399,
+ // Button 10 on third joystick
+ Joystick3Button10 = 400,
+ // Button 11 on third joystick
+ Joystick3Button11 = 401,
+ // Button 12 on third joystick
+ Joystick3Button12 = 402,
+ // Button 13 on third joystick
+ Joystick3Button13 = 403,
+ // Button 14 on third joystick
+ Joystick3Button14 = 404,
+ // Button 15 on third joystick
+ Joystick3Button15 = 405,
+ // Button 16 on third joystick
+ Joystick3Button16 = 406,
+ // Button 17 on third joystick
+ Joystick3Button17 = 407,
+ // Button 18 on third joystick
+ Joystick3Button18 = 408,
+ // Button 19 on third joystick
+ Joystick3Button19 = 409,
+
+ // Button 0 on forth joystick
+ Joystick4Button0 = 410,
+ // Button 1 on forth joystick
+ Joystick4Button1 = 411,
+ // Button 2 on forth joystick
+ Joystick4Button2 = 412,
+ // Button 3 on forth joystick
+ Joystick4Button3 = 413,
+ // Button 4 on forth joystick
+ Joystick4Button4 = 414,
+ // Button 5 on forth joystick
+ Joystick4Button5 = 415,
+ // Button 6 on forth joystick
+ Joystick4Button6 = 416,
+ // Button 7 on forth joystick
+ Joystick4Button7 = 417,
+ // Button 8 on forth joystick
+ Joystick4Button8 = 418,
+ // Button 9 on forth joystick
+ Joystick4Button9 = 419,
+ // Button 10 on forth joystick
+ Joystick4Button10 = 420,
+ // Button 11 on forth joystick
+ Joystick4Button11 = 421,
+ // Button 12 on forth joystick
+ Joystick4Button12 = 422,
+ // Button 13 on forth joystick
+ Joystick4Button13 = 423,
+ // Button 14 on forth joystick
+ Joystick4Button14 = 424,
+ // Button 15 on forth joystick
+ Joystick4Button15 = 425,
+ // Button 16 on forth joystick
+ Joystick4Button16 = 426,
+ // Button 17 on forth joystick
+ Joystick4Button17 = 427,
+ // Button 18 on forth joystick
+ Joystick4Button18 = 428,
+ // Button 19 on forth joystick
+ Joystick4Button19 = 429,
+
+ // We could expose all 10 joysticks here, but I think that a user would rarely want to explicitly
+ // specify a fifth or higher joystick (and they still can using the string version of Input.KeyDown).
+ // Four joysticks, however, are a common setup (especially on consoles), and we've had a bug report
+ // for only exposing three, so I added a forth.
+END
+
+
+
+// Types of UnityGUI input and processing events.
+ENUM EventType
+ // Mouse button was pressed.
+ MouseDown = 0,
+ // Mouse button was released.
+ MouseUp = 1,
+ // Mouse was moved (editor views only).
+ MouseMove = 2,
+ // Mouse was dragged.
+ MouseDrag = 3,
+ // A keyboard key was pressed.
+ KeyDown = 4,
+ // A keyboard key was released.
+ KeyUp = 5,
+ // The scroll wheel was moved.
+ ScrollWheel = 6,
+ // A repaint event. One is sent every frame.
+ Repaint = 7,
+ // A layout event.
+ Layout = 8,
+
+ // Editor only: drag & drop operation updated.
+ DragUpdated = 9,
+ // Editor only: drag & drop operation performed.
+ DragPerform = 10,
+ // Editor only: drag & drop operation exited.
+ DragExited = 15,
+
+ // [[Event]] should be ignored.
+ Ignore = 11,
+
+ // Already processed event.
+ Used = 12,
+
+ // Validates a special command (e.g. copy & paste)
+ ValidateCommand = 13,
+
+ // Execute a special command (eg. copy & paste)
+ ExecuteCommand = 14,
+
+ // User has right-clicked (or control-clicked on the mac).
+ ContextClick = 16,
+
+ OBSOLETE planned Please use EventType.MouseDown (with a capital M)
+ mouseDown = 0,
+ OBSOLETE planned Please use EventType.MouseUp (with a capital M)
+ mouseUp = 1,
+ OBSOLETE planned Please use EventType.MouseMove (with a capital M)
+ mouseMove = 2,
+ OBSOLETE planned Please use EventType.MouseDrag (with a capital M)
+ mouseDrag = 3,
+ OBSOLETE planned Please use EventType.KeyDown (with a capital K)
+ keyDown = 4,
+ OBSOLETE planned Please use EventType.KeyUp (with a capital K)
+ keyUp = 5,
+ OBSOLETE planned Please use EventType.ScrollWheel (with a capital S)
+ scrollWheel = 6,
+ OBSOLETE planned Please use EventType.Repaint (with a capital R)
+ repaint = 7,
+ OBSOLETE planned Please use EventType.Layout (with a capital L)
+ layout = 8,
+
+ OBSOLETE planned Please use EventType.DragUpdated (with a capital D)
+ dragUpdated = 9,
+ OBSOLETE planned Please use EventType.DragPerform (with a capital D)
+ dragPerform = 10,
+
+ OBSOLETE planned Please use EventType.Ignore (with a capital I)
+ ignore = 11,
+
+ OBSOLETE planned Please use EventType.Used (with a capital U)
+ used = 12
+END
+
+// Types of modifier key that can be active during a keystroke event.
+CSRAW [Flags]
+ENUM EventModifiers
+ // Shift key
+ Shift = 1,
+
+ // Control key
+ Control = 2,
+
+ // Alt key
+ Alt = 4,
+
+ // Command key (Mac)
+ Command = 8,
+
+ // Num lock key
+ Numeric = 16,
+
+ // Caps lock key
+ CapsLock = 32,
+
+ // Function key
+ FunctionKey = 64
+END
+
+
+CSRAW }
diff --git a/Runtime/Export/FlashHelper.cs b/Runtime/Export/FlashHelper.cs
new file mode 100644
index 0000000..5edd077
--- /dev/null
+++ b/Runtime/Export/FlashHelper.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Reflection;
+
+namespace UnityEngine
+{
+#if UNITY_FLASH
+ [NotRenamed]
+ internal static class ScriptingMethodHelper
+ {
+ [NotRenamed]
+ internal static MethodInfo GetMethodInfo(Type type, string methodName)
+ {
+ return type.GetMethod(methodName);
+ }
+
+ [NotRenamed]
+ internal static int NumberOfArgumentsOf(MethodInfo methodInfo)
+ {
+ return methodInfo.GetParameters().Length;
+ }
+
+ [NotRenamed]
+ internal static Type NthArgumentType(MethodInfo methodInfo, int index)
+ {
+ var args = methodInfo.GetParameters();
+ if (args.Length <= index)
+ return null;
+ return args[index].ParameterType;
+ }
+ }
+#endif
+}
diff --git a/Runtime/Export/GUI.txt b/Runtime/Export/GUI.txt
new file mode 100644
index 0000000..bda95db
--- /dev/null
+++ b/Runtime/Export/GUI.txt
@@ -0,0 +1,1723 @@
+C++RAW
+
+#include "UnityPrefix.h"
+
+#include "External/shaderlab/Library/FastPropertyName.h"
+#include "External/shaderlab/Library/properties.h"
+
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/IDList.h"
+#include "Runtime/IMGUI/GUIContent.h"
+#include "Runtime/IMGUI/GUILabel.h"
+#include "Runtime/IMGUI/GUIToggle.h"
+#include "Runtime/IMGUI/GUIButton.h"
+#include "Runtime/IMGUI/GUIWindows.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+#include "Runtime/IMGUI/GUIManager.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+#include "Runtime/Shaders/Material.h"
+
+#if UNITY_EDITOR
+# include "Editor/Platform/Interface/EditorWindows.h"
+#endif
+
+using namespace Unity;
+using namespace std;
+
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace UnityEngine
+{
+
+// Scaling mode to draw textures with
+ENUM ScaleMode
+ // Stretches the texture to fill the complete rectangle passed in to GUI.DrawTexture
+ StretchToFill = 0,
+ // Scales the texture, maintaining aspect ratio, so it completely covers the /position/ rectangle passed to GUI.DrawTexture. If the texture is being draw to a rectangle with a different aspect ratio than the original, the image is cropped.
+ ScaleAndCrop = 1,
+ // Scales the texture, maintaining aspect ratio, so it completely fits withing the /position/ rectangle passed to GUI.DrawTexture.
+ ScaleToFit = 2
+END
+
+// The GUI class is the interface for Unity's GUI with manual positioning.
+NONSEALED_CLASS GUI
+ CSRAW
+ static float scrollStepSize = 10f;
+ internal static DateTime nextScrollStepTime { get; set; }
+ static int scrollControlID;
+ internal static int scrollTroughSide { get; set; }
+
+ #if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN
+ static int hotTextField = -1;
+ #endif
+
+ // *undocumented*
+ CSRAW static GUI()
+ {
+ nextScrollStepTime = DateTime.Now; // whatever but null
+ }
+
+ CSRAW static UnityEngine.GUISkin s_Skin;
+
+ // The global skin to use.
+ CSRAW public static GUISkin skin {
+ set {
+ GUIUtility.CheckOnGUI();
+ if (!value)
+ value = GUIUtility.GetDefaultSkin ();
+ s_Skin = value;
+ value.MakeCurrent ();
+ }
+ get { GUIUtility.CheckOnGUI(); return s_Skin; }
+ }
+
+// COLORS
+ // Global tinting color for the GUI.
+ CUSTOM_PROP static Color color
+ { return IMGUI::GetColor (GetGUIState()); }
+ { IMGUI::SetColor (GetGUIState(), value); }
+
+ // Global tinting color for all background elements rendered by the GUI.
+ CUSTOM_PROP static Color backgroundColor
+ { return IMGUI::GetBackgroundColor (GetGUIState()); }
+ { IMGUI::SetBackgroundColor (GetGUIState(), value); }
+
+ // Tinting color for all text rendered by the GUI.
+ CUSTOM_PROP static Color contentColor
+ { return IMGUI::GetContentColor (GetGUIState()); }
+ { IMGUI::SetContentColor (GetGUIState(), value); }
+
+ // Returns true if any controls changed the value of the input data.
+ CUSTOM_PROP static bool changed
+ { return IMGUI::GetChanged (GetGUIState()); }
+ { IMGUI::SetChanged (GetGUIState(), value); }
+
+// ENABLED
+ // Is the GUI enabled?
+ CUSTOM_PROP static bool enabled
+ { return IMGUI::GetEnabled (GetGUIState()); }
+ { IMGUI::SetEnabled (GetGUIState(), value); }
+
+// MATRIX
+ // The GUI transform matrix.
+ CSRAW public static Matrix4x4 matrix {
+ get { return GUIClip.GetMatrix (); }
+ set { GUIClip.SetMatrix (value); }
+ }
+
+
+ // The tooltip of the control the mouse is currently over, or which has keyboard focus. (RO).
+ CSRAW public static string tooltip {
+ get {
+ string str = Internal_GetTooltip ();
+ if (str != null)
+ return str;
+ return "";
+ }
+ set { Internal_SetTooltip (value); }
+ }
+ CUSTOM private static string Internal_GetTooltip ()
+ {
+ GUIState &cState = GetGUIState();
+ UTF16String *mouseTooltip = cState.m_OnGUIState.m_MouseTooltip;
+ UTF16String *keyTooltip = cState.m_OnGUIState.m_KeyTooltip;
+ UTF16String *tooltip = NULL;
+ if (mouseTooltip)
+ {
+ tooltip = mouseTooltip;
+ }
+ else if (keyTooltip)
+ {
+ tooltip = keyTooltip;
+ }
+ if (tooltip)
+ return tooltip->GetScriptingString ();
+ return SCRIPTING_NULL;
+ }
+
+ CUSTOM private static void Internal_SetTooltip (string value)
+ {
+#if ENABLE_MONO
+ UTF16String str (value.AsUTF8().c_str());
+ GUIState &cState = GetGUIState();
+ cState.m_OnGUIState.SetMouseTooltip (str);
+ cState.m_OnGUIState.SetKeyTooltip (str);
+#endif
+ }
+
+ // *undocumented*
+ CSRAW static protected string mouseTooltip { get { return Internal_GetMouseTooltip (); } }
+ CUSTOM private static string Internal_GetMouseTooltip ()
+ {
+ GUIState &cState = GetGUIState();
+ UTF16String *mouseTooltip = cState.m_OnGUIState.m_MouseTooltip;
+ return mouseTooltip ? mouseTooltip->GetScriptingString () : SCRIPTING_NULL;
+ }
+
+ // *undocumented*
+ CSRAW protected static Rect tooltipRect { get { return s_ToolTipRect; } set { s_ToolTipRect = value; } }
+ CSRAW static internal Rect s_ToolTipRect;
+
+
+
+ // The sorting depth of the currently executing GUI behaviour.
+ CUSTOM_PROP static int depth
+ { return IMGUI::GetDepth (GetGUIState()); }
+ { IMGUI::SetDepth (GetGUIState(), value); }
+
+
+ /// *listonly*
+ CSRAW public static void Label (Rect position, string text) { Label (position, GUIContent.Temp (text), s_Skin.label); }
+ /// *listonly*
+ CSRAW public static void Label (Rect position, Texture image) { Label (position, GUIContent.Temp (image), s_Skin.label); }
+ /// *listonly*
+ CSRAW public static void Label (Rect position, GUIContent content) { Label (position, content, s_Skin.label); }
+ /// *listonly*
+ CSRAW public static void Label (Rect position, string text, GUIStyle style) { Label (position, GUIContent.Temp (text), style); }
+ /// *listonly*
+ CSRAW public static void Label (Rect position, Texture image, GUIStyle style) { Label (position, GUIContent.Temp (image), style); }
+
+ // Make a text or texture label on screen.
+ public static void Label (Rect position, GUIContent content, GUIStyle style) {
+ DoLabel (position, content, style.m_Ptr);
+ }
+
+ CUSTOM private static void DoLabel (Rect position, GUIContent content, IntPtr style)
+ {
+ IMGUI::GUILabel (
+ GetGUIState(),
+ position,
+ MonoGUIContentToTempNative (content),
+ *reinterpret_cast<GUIStyle*>(style)
+ );
+ }
+
+
+ CUSTOM private static void InitializeGUIClipTexture () { InitializeGUIClipTexture(); }
+
+ // Draw a texture within a rectangle.
+ CSRAW public static void DrawTexture (Rect position, Texture image, ScaleMode scaleMode = ScaleMode.StretchToFill, bool alphaBlend = true, float imageAspect = 0) {
+ if (Event.current.type == EventType.Repaint) {
+ if (image == null) {
+ Debug.LogWarning("null texture passed to GUI.DrawTexture");
+ return;
+ }
+
+ if (imageAspect == 0)
+ imageAspect = (float)image.width / image.height;
+
+ Material mat = alphaBlend ? blendMaterial : blitMaterial;
+ float destAspect = position.width / position.height;
+
+ InternalDrawTextureArguments arguments = new InternalDrawTextureArguments ();
+ #if UNITY_WINRT
+ arguments.textureInstanceId = image.GetInstanceID();
+ #else
+ arguments.texture = image;
+ #endif
+ arguments.leftBorder = 0;
+ arguments.rightBorder = 0;
+ arguments.topBorder = 0;
+ arguments.bottomBorder = 0;
+ arguments.color = GUI.color;
+ #if UNITY_WINRT
+ arguments.matInstanceId = mat.GetInstanceID();
+ #else
+ arguments.mat = mat;
+ #endif
+
+ switch (scaleMode) {
+ case ScaleMode.StretchToFill:
+ arguments.screenRect = position;
+ arguments.sourceRect = new Rect (0,0,1,1);
+ Graphics.DrawTexture (ref arguments);
+ break;
+ case ScaleMode.ScaleAndCrop:
+ if (destAspect > imageAspect) {
+ float stretch = imageAspect / destAspect;
+ arguments.screenRect = position;
+ arguments.sourceRect = new Rect (0, (1 - stretch) * .5f, 1, stretch);
+ Graphics.DrawTexture (ref arguments);
+ } else {
+ float stretch = destAspect / imageAspect;
+ arguments.screenRect = position;
+ arguments.sourceRect = new Rect (.5f - stretch * .5f, 0, stretch, 1);
+ Graphics.DrawTexture (ref arguments);
+ } break;
+ case ScaleMode.ScaleToFit:
+ if (destAspect > imageAspect) {
+ float stretch = imageAspect / destAspect;
+ arguments.screenRect = new Rect (position.xMin + position.width * (1.0f - stretch) * .5f, position.yMin, stretch * position.width, position.height);
+ arguments.sourceRect = new Rect (0, 0, 1, 1);
+ Graphics.DrawTexture (ref arguments);
+ } else {
+ float stretch = destAspect / imageAspect;
+ arguments.screenRect = new Rect (position.xMin, position.yMin + position.height * (1.0f - stretch) * .5f, position.width, stretch * position.height);
+ arguments.sourceRect = new Rect (0, 0, 1, 1);
+ Graphics.DrawTexture (ref arguments);
+ } break;
+ }
+ }
+ }
+
+ // Calculate screenrect and sourcerect for different scalemodes
+ CSRAW internal static bool CalculateScaledTextureRects(Rect position, ScaleMode scaleMode, float imageAspect, ref Rect outScreenRect, ref Rect outSourceRect)
+ {
+ float destAspect = position.width / position.height;
+ bool ret = false;
+
+ switch (scaleMode)
+ {
+ case ScaleMode.StretchToFill:
+ outScreenRect = position;
+ outSourceRect = new Rect (0,0,1,1);
+ ret = true;
+ break;
+ case ScaleMode.ScaleAndCrop:
+ if (destAspect > imageAspect) {
+ float stretch = imageAspect / destAspect;
+ outScreenRect = position;
+ outSourceRect = new Rect (0, (1 - stretch) * .5f, 1, stretch);
+ ret = true;
+ } else {
+ float stretch = destAspect / imageAspect;
+ outScreenRect = position;
+ outSourceRect = new Rect (.5f - stretch * .5f, 0, stretch, 1);
+ ret = true;
+ } break;
+ case ScaleMode.ScaleToFit:
+ if (destAspect > imageAspect) {
+ float stretch = imageAspect / destAspect;
+ outScreenRect = new Rect (position.xMin + position.width * (1.0f - stretch) * .5f, position.yMin, stretch * position.width, position.height);
+ outSourceRect = new Rect (0, 0, 1, 1);
+ ret = true;
+ } else {
+ float stretch = destAspect / imageAspect;
+ outScreenRect = new Rect (position.xMin, position.yMin + position.height * (1.0f - stretch) * .5f, position.width, stretch * position.height);
+ outSourceRect = new Rect (0, 0, 1, 1);
+ ret = true;
+ } break;
+ }
+
+ return ret;
+ }
+
+ // Draw a texture within a rectangle with the given texture coordinates. Use this function for clipping or tiling the image within the given rectangle.
+ CSRAW public static void DrawTextureWithTexCoords (Rect position, Texture image, Rect texCoords, bool alphaBlend = true)
+ {
+ if (Event.current.type == EventType.Repaint) {
+
+ Material mat = alphaBlend ? blendMaterial : blitMaterial;
+
+ InternalDrawTextureArguments arguments = new InternalDrawTextureArguments ();
+ #if UNITY_WINRT
+ arguments.textureInstanceId = image.GetInstanceID();
+ #else
+ arguments.texture = image;
+ #endif
+ arguments.leftBorder = 0;
+ arguments.rightBorder = 0;
+ arguments.topBorder = 0;
+ arguments.bottomBorder = 0;
+ arguments.color = GUI.color;
+ #if UNITY_WINRT
+ arguments.matInstanceId = mat.GetInstanceID();
+ #else
+ arguments.mat = mat;
+ #endif
+ arguments.screenRect = position;
+ arguments.sourceRect = texCoords;
+ Graphics.DrawTexture (ref arguments);
+ }
+ }
+
+ CUSTOM_PROP static private Material blendMaterial
+ {
+ return Scripting::ScriptingWrapperFor(GetGUIBlendMaterial());
+ }
+
+ CUSTOM_PROP static private Material blitMaterial
+ {
+ return Scripting::ScriptingWrapperFor(GetGUIBlitMaterial());
+ }
+
+ /// *listonly*
+ CSRAW public static void Box (Rect position, string text) { Box (position, GUIContent.Temp (text), s_Skin.box); }
+ /// *listonly*
+ CSRAW public static void Box (Rect position, Texture image) { Box (position, GUIContent.Temp (image), s_Skin.box); }
+ /// *listonly*
+ CSRAW public static void Box (Rect position, GUIContent content) { Box (position, content, s_Skin.box); }
+ /// *listonly*
+ CSRAW public static void Box (Rect position, string text, GUIStyle style) { Box (position, GUIContent.Temp (text), style); }
+ /// *listonly*
+ CSRAW public static void Box (Rect position, Texture image, GUIStyle style) { Box (position, GUIContent.Temp (image), style); }
+
+ // Make a graphical box.
+ public static void Box (Rect position, GUIContent content, GUIStyle style) {
+ GUIUtility.CheckOnGUI ();
+ int id = GUIUtility.GetControlID (boxHash, FocusType.Passive);
+ if (Event.current.type == EventType.Repaint) {
+ style.Draw (position, content, id);
+ }
+ }
+ static int boxHash = "Box".GetHashCode ();
+
+ /// *listonly*
+ CSRAW public static bool Button (Rect position, string text) { return DoButton (position, GUIContent.Temp (text), s_Skin.button.m_Ptr); }
+ /// *listonly*
+ CSRAW public static bool Button (Rect position, Texture image) { return DoButton (position, GUIContent.Temp (image), s_Skin.button.m_Ptr); }
+ /// *listonly*
+ CSRAW public static bool Button (Rect position, GUIContent content) { return DoButton (position, content, s_Skin.button.m_Ptr); }
+ /// *listonly*
+ CSRAW public static bool Button (Rect position, string text, GUIStyle style) { return DoButton (position, GUIContent.Temp (text), style.m_Ptr); }
+ /// *listonly*
+ CSRAW public static bool Button (Rect position, Texture image, GUIStyle style) { return DoButton (position, GUIContent.Temp (image), style.m_Ptr); }
+
+ // Make a single press button. The user clicks them and something happens immediately.
+ CSRAW public static bool Button (Rect position, GUIContent content, GUIStyle style)
+ { return DoButton (position, content, style.m_Ptr); }
+
+ CUSTOM private static bool DoButton (Rect position, GUIContent content, IntPtr style)
+ {
+ return IMGUI::GUIButton (
+ GetGUIState(),
+ position,
+ MonoGUIContentToTempNative (content),
+ *reinterpret_cast<GUIStyle*>(style)
+ );
+ }
+
+ /// *listonly*
+ CSRAW public static bool RepeatButton (Rect position, string text) { return DoRepeatButton (position, GUIContent.Temp (text), s_Skin.button, FocusType.Native); }
+ /// *listonly*
+ CSRAW public static bool RepeatButton (Rect position, Texture image) { return DoRepeatButton (position, GUIContent.Temp (image), s_Skin.button, FocusType.Native); }
+ /// *listonly*
+ CSRAW public static bool RepeatButton (Rect position, GUIContent content) { return DoRepeatButton (position, content, s_Skin.button, FocusType.Native); }
+ /// *listonly*
+ CSRAW public static bool RepeatButton (Rect position, string text, GUIStyle style) { return DoRepeatButton (position, GUIContent.Temp (text), style, FocusType.Native); }
+ /// *listonly*
+ CSRAW public static bool RepeatButton (Rect position, Texture image, GUIStyle style) { return DoRepeatButton (position, GUIContent.Temp (image), style, FocusType.Native); }
+
+ // Make a button that is active as long as the user holds it down.
+ CSRAW public static bool RepeatButton (Rect position, GUIContent content, GUIStyle style) { return DoRepeatButton (position, content, style, FocusType.Native); }
+
+ CSRAW static bool DoRepeatButton (Rect position, GUIContent content, GUIStyle style, FocusType focusType) {
+ GUIUtility.CheckOnGUI ();
+ int id = GUIUtility.GetControlID (repeatButtonHash, focusType, position);
+ switch (Event.current.GetTypeForControl (id)) {
+ case EventType.MouseDown:
+ // If the mouse is inside the button, we say that we're the hot control
+ if (position.Contains (Event.current.mousePosition)) {
+ GUIUtility.hotControl = id;
+ Event.current.Use ();
+ }
+ return false;
+ case EventType.MouseUp:
+ if (GUIUtility.hotControl == id) {
+ GUIUtility.hotControl = 0;
+
+ // If we got the mousedown, the mouseup is ours as well
+ // (no matter if the click was in the button or not)
+ Event.current.Use ();
+
+ // But we only return true if the button was actually clicked
+ return position.Contains (Event.current.mousePosition);
+ }
+ return false;
+ case EventType.Repaint:
+ style.Draw (position, content, id);
+ // Handles.Repaint ();
+ return id == GUIUtility.hotControl && position.Contains (Event.current.mousePosition);
+ }
+ return false;
+ }
+ static int repeatButtonHash = "repeatButton".GetHashCode ();
+
+// ====================================================== TEXTFIELDS ===============================
+ /// *listonly*
+ CSRAW public static string TextField (Rect position, string text) {
+ GUIContent t = GUIContent.Temp (text);
+ DoTextField (position, GUIUtility.GetControlID (FocusType.Keyboard, position), t, false, -1, GUI.skin.textField);
+ return t.text;
+ }
+ /// *listonly*
+ CSRAW public static string TextField (Rect position, string text, int maxLength) {
+ GUIContent t = GUIContent.Temp (text);
+ DoTextField (position, GUIUtility.GetControlID (FocusType.Keyboard, position), t, false, maxLength, GUI.skin.textField);
+ return t.text;
+ }
+ /// *listonly*
+ CSRAW public static string TextField (Rect position, string text, GUIStyle style) {
+ GUIContent t = GUIContent.Temp (text);
+ DoTextField (position, GUIUtility.GetControlID (FocusType.Keyboard, position), t, false, -1, style);
+ return t.text;
+ }
+ // Make a single-line text field where the user can edit a string.
+ CSRAW public static string TextField (Rect position, string text, int maxLength, GUIStyle style) {
+ GUIContent t = GUIContent.Temp (text);
+ DoTextField (position, GUIUtility.GetControlID (FocusType.Keyboard, position), t, true, maxLength, style);
+ return t.text;
+ }
+
+
+ // PASSWORDFIELD HERE ===============================================================
+ /// *listonly*
+ CSRAW public static string PasswordField (Rect position, string password, char maskChar) {
+ return PasswordField (position, password, maskChar, -1, GUI.skin.textField);
+ }
+
+ /// *listonly*
+ CSRAW public static string PasswordField (Rect position, string password, char maskChar, int maxLength) {
+ return PasswordField (position, password, maskChar, maxLength, GUI.skin.textField);
+ }
+ /// *listonly*
+ CSRAW public static string PasswordField (Rect position, string password, char maskChar, GUIStyle style) {
+ return PasswordField (position, password, maskChar, -1, style);
+ }
+
+ // Make a text field where the user can enter a password.
+ CSRAW public static string PasswordField (Rect position, string password, char maskChar, int maxLength, GUIStyle style) {
+ string strPassword = PasswordFieldGetStrToShow(password, maskChar);
+
+ GUIContent t = GUIContent.Temp (strPassword);
+ bool oldGUIChanged = GUI.changed;
+ GUI.changed = false;
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN
+ DoTextField (position, GUIUtility.GetControlID (FocusType.Keyboard), t, false, maxLength, style, password, maskChar);
+#else
+ DoTextField (position, GUIUtility.GetControlID (FocusType.Keyboard, position), t, false, maxLength, style);
+#endif
+
+ strPassword = GUI.changed ? t.text : password;
+
+ GUI.changed |= oldGUIChanged;
+
+ return strPassword;
+ }
+
+ // *undocumented*
+ CSRAW internal static string PasswordFieldGetStrToShow(string password, char maskChar)
+ {
+#if !UNITY_WEBGL
+ return (Event.current.type == EventType.repaint || Event.current.type == EventType.mouseDown) ?
+ "".PadRight(password.Length, maskChar) : password;
+#else
+ return password;
+#endif
+ }
+
+ // TEXTAREA HERE ====================================================================
+ /// *listonly*
+ CSRAW public static string TextArea (Rect position, string text) {
+ GUIContent t = GUIContent.Temp (text);
+ DoTextField (position, GUIUtility.GetControlID (FocusType.Keyboard, position), t, true, -1, GUI.skin.textArea);
+ return t.text;
+ }
+ /// *listonly*
+ CSRAW public static string TextArea (Rect position, string text, int maxLength) {
+ GUIContent t = GUIContent.Temp (text);
+ DoTextField (position, GUIUtility.GetControlID (FocusType.Keyboard, position), t, true, maxLength, GUI.skin.textArea);
+ return t.text;
+ }
+ /// *listonly*
+ CSRAW public static string TextArea (Rect position, string text, GUIStyle style) {
+ GUIContent t = GUIContent.Temp (text);
+ DoTextField (position, GUIUtility.GetControlID (FocusType.Keyboard, position), t, true, -1, style);
+ return t.text;
+ }
+ // Make a Multi-line text area where the user can edit a string.
+ CSRAW public static string TextArea (Rect position, string text, int maxLength, GUIStyle style) {
+ GUIContent t = GUIContent.Temp (text);
+ DoTextField (position, GUIUtility.GetControlID (FocusType.Keyboard, position), t, false, maxLength, style);
+ return t.text;
+ }
+ // LATER...
+ static string TextArea (Rect position, GUIContent content, int maxLength, GUIStyle style) {
+ GUIContent t = GUIContent.Temp (content.text, content.image);
+ DoTextField (position, GUIUtility.GetControlID (FocusType.Keyboard, position), t, false, maxLength, style);
+ return t.text;
+ }
+
+ CSRAW
+ internal static void DoTextField (Rect position, int id, GUIContent content, bool multiline, int maxLength, GUIStyle style
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN
+ , string secureText = null, char maskChar = '\0'
+#endif
+ ) {
+
+ //Pre-cull input string to maxLength.
+ if (maxLength >= 0 && content.text.Length > maxLength)
+ content.text = content.text.Substring (0, maxLength);
+
+ GUIUtility.CheckOnGUI ();
+ TextEditor editor = (TextEditor)GUIUtility.GetStateObject (typeof (TextEditor), id);
+ editor.content.text = content.text;
+ editor.SaveBackup ();
+ editor.position = position;
+ editor.style = style;
+ editor.multiline = multiline;
+ editor.controlID = id;
+ editor.ClampPos ();
+ Event evt = Event.current;
+
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN
+ switch (evt.type) {
+ case EventType.MouseDown:
+ if (position.Contains (evt.mousePosition)) {
+ GUIUtility.hotControl = id;
+
+ // Disable keyboard for previously active text field, if any
+ if (hotTextField != -1 && hotTextField != id) {
+ TextEditor currentEditor = (TextEditor)GUIUtility.GetStateObject (typeof (TextEditor), hotTextField);
+ currentEditor.keyboardOnScreen = null;
+ }
+
+ hotTextField = id;
+
+ // in player setting keyboard control calls OnFocus every time, don't want that. In editor it does not do that for some reason
+ if (GUIUtility.keyboardControl != id)
+ GUIUtility.keyboardControl = id;
+
+ editor.keyboardOnScreen = TouchScreenKeyboard.Open(
+ (secureText != null) ? secureText : content.text,
+ TouchScreenKeyboardType.Default,
+ true, // autocorrection
+ multiline,
+ (secureText != null));
+
+ evt.Use ();
+ }
+ break;
+ case EventType.Repaint:
+ if (editor.keyboardOnScreen != null) {
+ content.text = editor.keyboardOnScreen.text;
+ if (maxLength >= 0 && content.text.Length > maxLength)
+ content.text = content.text.Substring (0, maxLength);
+
+ if (editor.keyboardOnScreen.done) {
+ editor.keyboardOnScreen = null;
+ changed = true;
+ }
+ }
+
+ // if we use system keyboard we will have normal text returned (hiding symbols is done inside os)
+ // so before drawing make sure we hide them ourselves
+ string clearText = content.text;
+
+ if(secureText != null)
+ content.text = PasswordFieldGetStrToShow(clearText, maskChar);
+
+ style.Draw (position, content, id, false);
+ content.text = clearText;
+
+ break;
+ }
+#else // #if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN
+ bool change = false;
+ switch (evt.type) {
+ case EventType.MouseDown:
+ if (position.Contains (evt.mousePosition)) {
+ GUIUtility.hotControl = id;
+ GUIUtility.keyboardControl = id;
+ editor.m_HasFocus = true;
+ editor.MoveCursorToPosition (Event.current.mousePosition);
+ if (Event.current.clickCount == 2 && GUI.skin.settings.doubleClickSelectsWord) {
+ editor.SelectCurrentWord ();
+ editor.DblClickSnap(TextEditor.DblClickSnapping.WORDS);
+ editor.MouseDragSelectsWholeWords (true);
+ } if (Event.current.clickCount == 3 && GUI.skin.settings.tripleClickSelectsLine) {
+ editor.SelectCurrentParagraph ();
+ editor.MouseDragSelectsWholeWords (true);
+ editor.DblClickSnap(TextEditor.DblClickSnapping.PARAGRAPHS);
+ }
+ evt.Use ();
+ }
+ break;
+ case EventType.MouseDrag:
+ if (GUIUtility.hotControl == id)
+ {
+ if (evt.shift)
+ editor.MoveCursorToPosition (Event.current.mousePosition);
+ else
+ editor.SelectToPosition (Event.current.mousePosition);
+ evt.Use ();
+ }
+ break;
+ case EventType.MouseUp:
+ if (GUIUtility.hotControl == id) {
+ editor.MouseDragSelectsWholeWords (false);
+ GUIUtility.hotControl = 0;
+ evt.Use ();
+ }
+ break;
+ case EventType.KeyDown:
+ if (GUIUtility.keyboardControl != id)
+ return;
+
+ if (editor.HandleKeyEvent (evt)) {
+ evt.Use ();
+ change = true;
+ content.text = editor.content.text;
+ break;
+ }
+
+ // Ignore tab & shift-tab in textfields
+ if (evt.keyCode == KeyCode.Tab || evt.character == '\t')
+ return;
+
+ char c = evt.character;
+
+ if (c == '\n' && !multiline && !evt.alt)
+ return;
+
+
+ // Simplest test: only allow the character if the display font supports it.
+ Font font = style.font;
+ if (!font)
+ font = GUI.skin.font;
+
+ if (font.HasCharacter (c) || c == '\n') {
+ editor.Insert (c);
+ change = true;
+ break;
+ }
+
+ // On windows, keypresses also send events with keycode but no character. Eat them up here.
+ if (c == 0) {
+
+ // if we have a composition string, make sure we clear the previous selection.
+ if (Input.compositionString.Length > 0)
+ {
+ editor.ReplaceSelection ("");
+ change = true;
+ }
+
+ evt.Use ();
+ }
+// else {
+// REALLY USEFUL:
+// Debug.Log ("unhandled " +evt);
+// evt.Use ();
+// }
+ break;
+ case EventType.Repaint:
+ // If we have keyboard focus, draw the cursor
+ // TODO: check if this OpenGL view has keyboard focus
+ if (GUIUtility.keyboardControl != id) {
+ style.Draw (position, content, id, false);
+ } else {
+ editor.DrawCursor (content.text);
+ }
+ break;
+ }
+
+ if (GUIUtility.keyboardControl == id)
+ GUIUtility.textFieldInput = true;
+
+ if (change) {
+ changed = true;
+ content.text = editor.content.text;
+ if (maxLength >= 0 && content.text.Length > maxLength)
+ content.text = content.text.Substring (0, maxLength);
+ evt.Use ();
+ }
+ #endif // #if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN
+ }
+
+
+// INPUT CONTROLS
+
+ // Set the name of the next control.
+ CUSTOM static void SetNextControlName (string name)
+ {
+ GetGUIState().SetNameOfNextKeyboardControl (name);
+ }
+
+ // Get the name of named control that has focus.
+ CUSTOM static string GetNameOfFocusedControl ()
+ {
+ return scripting_string_new(GetGUIState().GetNameOfFocusedControl ().c_str());
+ }
+
+
+
+ // Move keyboard focus to a named control.
+ CUSTOM static void FocusControl (string name)
+ {
+ GetGUIState().FocusKeyboardControl (name);
+ }
+
+
+// ====================================================== TOGGLES ===============================
+ /// *listonly*
+ CSRAW public static bool Toggle (Rect position, bool value, string text) { return Toggle (position, value, GUIContent.Temp (text), s_Skin.toggle); }
+ /// *listonly*
+ CSRAW public static bool Toggle (Rect position, bool value, Texture image) { return Toggle (position, value, GUIContent.Temp (image), s_Skin.toggle); }
+ /// *listonly*
+ CSRAW public static bool Toggle (Rect position, bool value, GUIContent content) { return Toggle (position, value, content, s_Skin.toggle); }
+ /// *listonly*
+ CSRAW public static bool Toggle (Rect position, bool value, string text, GUIStyle style) { return Toggle (position, value, GUIContent.Temp (text), style); }
+ /// *listonly*
+ CSRAW public static bool Toggle (Rect position, bool value, Texture image, GUIStyle style) { return Toggle (position, value, GUIContent.Temp (image), style); }
+ // Make an on/off toggle button.
+ CSRAW public static bool Toggle (Rect position, bool value, GUIContent content, GUIStyle style)
+ { return DoToggle (position, GUIUtility.GetControlID (toggleHash, FocusType.Native, position), value, content, style.m_Ptr); }
+
+ CSRAW static int toggleHash = "Toggle".GetHashCode ();
+ CUSTOM internal static bool DoToggle (Rect position, int id, bool value, GUIContent content, IntPtr style)
+ {
+ return IMGUI::GUIToggle (
+ GetGUIState(),
+ position,
+ value,
+ MonoGUIContentToTempNative (content),
+ *reinterpret_cast<GUIStyle*>(style),
+ id
+ );
+ }
+
+ /// *listonly*
+ CSRAW public static int Toolbar (Rect position, int selected, string[] texts) { return Toolbar (position, selected, GUIContent.Temp (texts), s_Skin.button); }
+ /// *listonly*
+ CSRAW public static int Toolbar (Rect position, int selected, Texture[] images) { return Toolbar (position, selected, GUIContent.Temp (images), s_Skin.button); }
+ /// *listonly*
+ public static int Toolbar (Rect position, int selected, GUIContent[] content) { return Toolbar (position, selected, content, s_Skin.button); }
+ /// *listonly*
+ CSRAW public static int Toolbar (Rect position, int selected, string[] texts, GUIStyle style) { return Toolbar (position, selected, GUIContent.Temp (texts), style); }
+ /// *listonly*
+ CSRAW public static int Toolbar (Rect position, int selected, Texture[] images, GUIStyle style) { return Toolbar (position, selected, GUIContent.Temp (images), style); }
+ // Make a toolbar
+ CSRAW public static int Toolbar (Rect position, int selected, GUIContent[] contents, GUIStyle style) {
+ // Get the styles here
+ GUIStyle firstStyle, midStyle, lastStyle;
+ FindStyles (ref style, out firstStyle, out midStyle, out lastStyle, "left", "mid", "right");
+
+ return DoButtonGrid (position, selected, contents, contents.Length, style, firstStyle, midStyle, lastStyle);
+ }
+
+
+ /// *listonly*
+ CSRAW public static int SelectionGrid (Rect position, int selected, string[] texts, int xCount) { return SelectionGrid (position, selected, GUIContent.Temp (texts), xCount, null); }
+ /// *listonly*
+ CSRAW public static int SelectionGrid (Rect position, int selected, Texture[] images, int xCount) { return SelectionGrid (position, selected, GUIContent.Temp (images), xCount, null); }
+ /// *listonly*
+ CSRAW public static int SelectionGrid (Rect position, int selected, GUIContent[] content, int xCount) { return SelectionGrid (position, selected, content, xCount, null); }
+ /// *listonly*
+ CSRAW public static int SelectionGrid (Rect position, int selected, string[] texts, int xCount, GUIStyle style) { return SelectionGrid (position, selected, GUIContent.Temp (texts), xCount, style); }
+ /// *listonly*
+ CSRAW public static int SelectionGrid (Rect position, int selected, Texture[] images, int xCount, GUIStyle style) { return SelectionGrid (position, selected, GUIContent.Temp (images), xCount, style); }
+ // Make a grid of buttons.
+ CSRAW public static int SelectionGrid (Rect position, int selected, GUIContent[] contents, int xCount, GUIStyle style) {
+ if (style == null) style = s_Skin.button;
+ return DoButtonGrid (position, selected, contents, xCount, style, style, style, style);
+ }
+
+ // Internal toolbar & buttongrid code
+ // =============================================
+ CSRAW
+ // Find many GUIStyles from style.name permutations (Helper function for toolbars).
+ internal static void FindStyles (ref GUIStyle style, out GUIStyle firstStyle, out GUIStyle midStyle, out GUIStyle lastStyle, string first, string mid, string last) {
+ if (style == null)
+ style = GUI.skin.button;
+ string baseName = style.name;
+ midStyle = GUI.skin.FindStyle (baseName + mid);
+ if (midStyle == null)
+ midStyle = style;
+ firstStyle = GUI.skin.FindStyle (baseName + first);
+ if (firstStyle == null)
+ firstStyle = midStyle;
+ lastStyle = GUI.skin.FindStyle (baseName + last);
+ if (lastStyle == null)
+ lastStyle = midStyle;
+ }
+
+ static internal int CalcTotalHorizSpacing (int xCount, GUIStyle style, GUIStyle firstStyle, GUIStyle midStyle, GUIStyle lastStyle) {
+ if (xCount < 2)
+ return 0;
+ if (xCount == 2)
+ return Mathf.Max (firstStyle.margin.right, lastStyle.margin.left);
+
+ int internalSpace = Mathf.Max (midStyle.margin.left, midStyle.margin.right);
+ return Mathf.Max (firstStyle.margin.right, midStyle.margin.left) + Mathf.Max (midStyle.margin.right, lastStyle.margin.left) + internalSpace * (xCount - 3);
+ }
+
+ // Make a button grid
+ static int DoButtonGrid (Rect position, int selected, GUIContent[] contents, int xCount, GUIStyle style, GUIStyle firstStyle, GUIStyle midStyle, GUIStyle lastStyle) {
+ GUIUtility.CheckOnGUI ();
+ int count = contents.Length;
+ if (count == 0)
+ return selected;
+ if (xCount <= 0)
+ {
+ Debug.LogWarning("You are trying to create a SelectionGrid with zero or less elements to be displayed in the horizontal direction. Set xCount to a positive value.");
+ return selected;
+ }
+ int id = GUIUtility.GetControlID (buttonGridHash, FocusType.Native, position);
+
+ // Figure out how large each element should be
+ int rows = count / xCount;
+ if (count % xCount != 0)
+ rows++;
+ float totalHorizSpacing = CalcTotalHorizSpacing (xCount, style, firstStyle, midStyle, lastStyle);
+ float totalVerticalSpacing = Mathf.Max (style.margin.top, style.margin.bottom) * (rows - 1);
+ float elemWidth = (position.width - totalHorizSpacing) / xCount;
+ float elemHeight = (position.height - totalVerticalSpacing) / (float)rows;
+
+ if (style.fixedWidth != 0)
+ elemWidth = style.fixedWidth;
+ if (style.fixedHeight != 0)
+ elemHeight = style.fixedHeight;
+
+ Rect[] buttonRects;
+ switch (Event.current.GetTypeForControl (id)) {
+ case EventType.MouseDown:
+ if (position.Contains (Event.current.mousePosition)) {
+ //Check if the mouse is over a button (nobody says the grid is filled out)
+ buttonRects = CalcMouseRects (position, count, xCount, elemWidth, elemHeight, style, firstStyle, midStyle, lastStyle, false);
+ if (GetButtonGridMouseSelection (buttonRects, Event.current.mousePosition, true) != -1) {
+ GUIUtility.hotControl = id;
+ Event.current.Use ();
+ }
+ }
+ break;
+ case EventType.MouseDrag:
+ if (GUIUtility.hotControl == id)
+ Event.current.Use ();
+ break;
+ case EventType.MouseUp:
+ if (GUIUtility.hotControl == id) {
+ GUIUtility.hotControl = 0;
+ Event.current.Use ();
+
+ buttonRects = CalcMouseRects (position, count, xCount, elemWidth, elemHeight, style, firstStyle, midStyle, lastStyle, false);
+ int mouseSel = GetButtonGridMouseSelection (buttonRects, Event.current.mousePosition, true);
+
+ GUI.changed = true;
+ return mouseSel;
+ }
+ break;
+ case EventType.Repaint:
+ GUIStyle selStyle = null;
+
+ GUIClip.Push (position, Vector2.zero, Vector2.zero, false);
+ position = new Rect (0,0,position.width, position.height);
+
+ buttonRects = CalcMouseRects (position, count, xCount, elemWidth, elemHeight, style, firstStyle, midStyle, lastStyle, false);
+ int mouseOverSel = GetButtonGridMouseSelection (buttonRects, Event.current.mousePosition, id == GUIUtility.hotControl);
+
+ bool mouseInside = position.Contains (Event.current.mousePosition);
+ GUIUtility.mouseUsed |= mouseInside;
+
+ for (int i =0; i < count;i++) {
+ // Figure out the style
+ GUIStyle s = null;
+ if (i != 0)
+ s = midStyle;
+ else
+ s = firstStyle;
+ if (i == count - 1)
+ s = lastStyle;
+ if (count == 1)
+ s = style;
+
+ if (i != selected) // We draw the selected one last, so it overflows nicer
+ {
+ s.Draw (buttonRects[i], contents[i], i == mouseOverSel && (enabled || id == GUIUtility.hotControl) && (id == GUIUtility.hotControl || GUIUtility.hotControl == 0), id == GUIUtility.hotControl && GUI.enabled, false, false);
+ } else
+ {
+ selStyle = s;
+ }
+ }
+
+ // Draw it at the end
+ if (selected < count && selected > -1)
+ {
+ selStyle.Draw (buttonRects[selected], contents[selected], selected == mouseOverSel && (enabled || id == GUIUtility.hotControl) && (id == GUIUtility.hotControl || GUIUtility.hotControl == 0), id == GUIUtility.hotControl, true, false);
+// selStyle.Draw (buttonRects[selected], contents[selected], selected == mouseOverSel, id == GUIUtility.hotControl || (selected == mouseOverSel && GUIUtility.hotControl = 0), true, false);
+ }
+
+ if (mouseOverSel >= 0)
+ {
+ tooltip = contents[mouseOverSel].tooltip;
+ }
+
+ GUIClip.Pop ();
+ break;
+ }
+ return selected;
+ }
+ static int buttonGridHash = "ButtonGrid".GetHashCode ();
+
+ // Helper function: Get all mouse rects
+ static Rect[] CalcMouseRects (Rect position, int count, int xCount, float elemWidth, float elemHeight, GUIStyle style, GUIStyle firstStyle, GUIStyle midStyle, GUIStyle lastStyle, bool addBorders) {
+ int y = 0;
+ int x = 0;
+ float xPos = position.xMin, yPos = position.yMin;
+ GUIStyle currentButtonStyle = style;
+ Rect[] retval = new Rect[count];
+ if (count > 1)
+ currentButtonStyle = firstStyle;
+ for (int i = 0; i < count; i++) {
+ if (!addBorders)
+ retval[i] = new Rect (xPos, yPos, elemWidth, elemHeight);
+ else
+ retval[i] = currentButtonStyle.margin.Add (new Rect (xPos, yPos, elemWidth, elemHeight));
+
+ // Correct way to get the rounded width:
+ retval[i].width = Mathf.Round (retval[i].xMax) - Mathf.Round (retval[i].x);
+ // Round the position *after* the position has been rounded:
+ retval[i].x = Mathf.Round (retval[i].x);
+
+ // Don't round xPos here. If rounded, the right edge of this rect may
+ // not line up correctly with the left edge of the next,
+ // plus it can cause cumulative rounding errors.
+ // (See case 366967)
+
+ GUIStyle nextStyle = midStyle;
+ if (i == count - 2)
+ nextStyle = lastStyle;
+ xPos += elemWidth + Mathf.Max (currentButtonStyle.margin.right, nextStyle.margin.left);
+
+ x++;
+ if (x >= xCount) {
+ y++;
+ x = 0;
+ yPos += elemHeight + Mathf.Max (style.margin.top, style.margin.bottom);
+ xPos = position.xMin;
+ }
+ }
+ return retval;
+ }
+
+
+ // Helper function: Get the index of the element under the mouse position
+ static int GetButtonGridMouseSelection (Rect[] buttonRects, Vector2 mousePos, bool findNearest) {
+ // This could be implemented faster, but for now this is not supposed to be used for a gazillion elements :)
+
+ for (int i = 0; i < buttonRects.Length; i++) {
+ if (buttonRects[i].Contains (mousePos))
+ return i;
+ }
+ if (!findNearest)
+ return -1;
+ // We haven't found any we're over, so we need to find the closest button.
+ float minDist = 10000000;
+ int minIndex = -1;
+ for (int i = 0; i < buttonRects.Length; i++) {
+ Rect r = buttonRects[i];
+ Vector2 v = new Vector2 (Mathf.Clamp (mousePos.x, r.xMin, r.xMax), Mathf.Clamp (mousePos.y, r.yMin, r.yMax));
+ float dSqr = (mousePos - v).sqrMagnitude;
+ if (dSqr < minDist) {
+ minIndex = i;
+ minDist = dSqr;
+ }
+ }
+
+ return minIndex;
+ }
+
+ /// *listonly*
+ CSRAW public static float HorizontalSlider(Rect position, float value, float leftValue, float rightValue)
+ { return Slider (position, value, 0, leftValue, rightValue, skin.horizontalSlider, skin.horizontalSliderThumb, true, GUIUtility.GetControlID (sliderHash, FocusType.Native, position)); }
+ // A horizontal slider the user can drag to change a value between a min and a max.
+ CSRAW public static float HorizontalSlider (Rect position, float value, float leftValue, float rightValue, GUIStyle slider, GUIStyle thumb)
+ { return Slider (position, value, 0, leftValue, rightValue, slider, thumb, true, GUIUtility.GetControlID (sliderHash, FocusType.Native, position)); }
+
+ /// *listonly*
+ CSRAW public static float VerticalSlider (Rect position, float value, float topValue, float bottomValue)
+ { return Slider (position, value, 0, topValue, bottomValue, skin.verticalSlider, skin.verticalSliderThumb, false, GUIUtility.GetControlID (sliderHash, FocusType.Native, position)); }
+ // A vertical slider the user can drag to change a value between a min and a max.
+ CSRAW public static float VerticalSlider (Rect position, float value, float topValue, float bottomValue, GUIStyle slider, GUIStyle thumb)
+ { return Slider (position, value, 0, topValue, bottomValue, slider, thumb, false, GUIUtility.GetControlID (sliderHash, FocusType.Native, position)); }
+
+
+ // Main slider function.
+ // Handles scrollbars & sliders in both horizontal & vertical directions.
+ //*undocumented*
+ CSRAW public static float Slider (Rect position, float value, float size, float start, float end, GUIStyle slider, GUIStyle thumb, bool horiz, int id) {
+ GUIUtility.CheckOnGUI ();
+ return new SliderHandler(position, value, size, start, end, slider, thumb, horiz, id).Handle();
+ }
+ static int sliderHash = "Slider".GetHashCode ();
+
+ // should scrollbars do paging
+ CUSTOM_PROP static internal bool usePageScrollbars
+ {
+ #if UNITY_OSX
+ //respect system preference.
+ Boolean exists;
+ Boolean scroll = CFPreferencesGetAppBooleanValue(CFSTR("AppleScrollerPagingBehavior"),CFSTR("Apple Global Domain"),&exists);
+ return !scroll;
+ #else
+ return true;
+ #endif
+ }
+
+
+ /// *listonly*
+ CSRAW public static float HorizontalScrollbar (Rect position, float value, float size, float leftValue, float rightValue)
+ { return Scroller (position, value, size, leftValue, rightValue, skin.horizontalScrollbar, skin.horizontalScrollbarThumb, skin.horizontalScrollbarLeftButton, skin.horizontalScrollbarRightButton, true); }
+ // Make a horizontal scrollbar. Scrollbars are what you use to scroll through a document. Most likely, you want to use scrollViews instead.
+ CSRAW public static float HorizontalScrollbar (Rect position, float value, float size, float leftValue, float rightValue, GUIStyle style)
+ { return Scroller (position, value, size, leftValue, rightValue, style, skin.GetStyle (style.name + "thumb"), skin.GetStyle (style.name + "leftbutton"), skin.GetStyle (style.name + "rightbutton"), true); }
+
+ // *undocumented*
+ CUSTOM static internal void InternalRepaintEditorWindow()
+ {
+ #if UNITY_EDITOR
+ GUIView *view = GUIView::GetCurrent ();
+ if (view) {
+ view->RequestRepaint ();
+ } else {
+ ErrorString ("InternalRepaint called outside an editor OnGUI");
+ }
+ #endif
+ }
+
+ // *undocumented*
+ CSRAW internal static bool ScrollerRepeatButton(int scrollerID, Rect rect, GUIStyle style)
+ {
+ bool changed = false;
+
+ if (DoRepeatButton (rect, GUIContent.none, style, FocusType.Passive))
+ {
+ bool firstClick = scrollControlID != scrollerID;
+ scrollControlID = scrollerID;
+
+ if (firstClick)
+ {
+ changed = true;
+ nextScrollStepTime = DateTime.Now.AddMilliseconds(ScrollWaitDefinitions.firstWait);
+ }
+ else
+ {
+ if (DateTime.Now >= nextScrollStepTime)
+ {
+ changed = true;
+ nextScrollStepTime = DateTime.Now.AddMilliseconds(ScrollWaitDefinitions.regularWait);
+ }
+ }
+
+ if (Event.current.type == EventType.Repaint)
+ InternalRepaintEditorWindow();
+ }
+
+ return changed;
+ }
+
+ /// *listonly*
+ CSRAW public static float VerticalScrollbar (Rect position, float value, float size, float topValue, float bottomValue)
+ { return Scroller (position, value, size, topValue, bottomValue, skin.verticalScrollbar, skin.verticalScrollbarThumb, skin.verticalScrollbarUpButton, skin.verticalScrollbarDownButton, false); }
+ // Make a vertical scrollbar. Scrollbars are what you use to scroll through a document. Most likely, you want to use scrollViews instead.
+ CSRAW public static float VerticalScrollbar (Rect position, float value, float size, float topValue, float bottomValue, GUIStyle style)
+ { return Scroller (position, value, size, topValue, bottomValue, style, skin.GetStyle (style.name + "thumb"), skin.GetStyle (style.name + "upbutton"), skin.GetStyle (style.name + "downbutton"), false); }
+
+ static float Scroller (Rect position, float value, float size, float leftValue, float rightValue, GUIStyle slider, GUIStyle thumb, GUIStyle leftButton, GUIStyle rightButton, bool horiz) {
+ GUIUtility.CheckOnGUI ();
+
+ int id = GUIUtility.GetControlID (sliderHash, FocusType.Passive, position);
+
+ Rect sliderRect, minRect, maxRect;
+
+ if (horiz) {
+ sliderRect = new Rect (
+ position.x + leftButton.fixedWidth, position.y,
+ position.width - leftButton.fixedWidth - rightButton.fixedWidth, position.height
+ );
+ minRect = new Rect (position.x, position.y, leftButton.fixedWidth, position.height);
+ maxRect = new Rect (position.xMax - rightButton.fixedWidth, position.y, rightButton.fixedWidth, position.height);
+ } else {
+ sliderRect = new Rect (
+ position.x, position.y + leftButton.fixedHeight,
+ position.width, position.height - leftButton.fixedHeight - rightButton.fixedHeight
+ );
+ minRect = new Rect (position.x, position.y, position.width, leftButton.fixedHeight);
+ maxRect = new Rect (position.x, position.yMax - rightButton.fixedHeight, position.width, rightButton.fixedHeight);
+ }
+
+ value = Slider (sliderRect, value, size, leftValue, rightValue, slider, thumb, horiz, id);
+
+ bool wasMouseUpEvent = false;
+ if (Event.current.type == EventType.MouseUp)
+ wasMouseUpEvent = true;
+
+ if (ScrollerRepeatButton(id, minRect, leftButton))
+ value -= scrollStepSize * (leftValue < rightValue ? 1f : -1f);
+
+ if (ScrollerRepeatButton(id, maxRect, rightButton))
+ value += scrollStepSize * (leftValue < rightValue ? 1f : -1f);
+
+ if (wasMouseUpEvent && Event.current.type == EventType.Used) // repeat buttons ate mouse up event - release scrolling
+ scrollControlID = 0;
+
+ if (leftValue < rightValue)
+ value = Mathf.Clamp (value, leftValue, rightValue - size);
+ else
+ value = Mathf.Clamp (value, rightValue, leftValue - size);
+ return value;
+ }
+
+
+ /// *listonly*
+ CSRAW public static void BeginGroup (Rect position) { BeginGroup (position, GUIContent.none, GUIStyle.none); }
+ /// *listonly*
+ CSRAW public static void BeginGroup (Rect position, string text) { BeginGroup (position, GUIContent.Temp (text), GUIStyle.none); }
+ /// *listonly*
+ CSRAW public static void BeginGroup (Rect position, Texture image) { BeginGroup (position, GUIContent.Temp (image), GUIStyle.none); }
+ /// *listonly*
+ CSRAW public static void BeginGroup (Rect position, GUIContent content) { BeginGroup (position, content, GUIStyle.none); }
+ /// *listonly*
+ CSRAW public static void BeginGroup (Rect position, GUIStyle style) { BeginGroup (position, GUIContent.none, style); }
+ /// *listonly*
+ CSRAW public static void BeginGroup (Rect position, string text, GUIStyle style) { BeginGroup (position, GUIContent.Temp (text), style); }
+ /// *listonly*
+ CSRAW public static void BeginGroup (Rect position, Texture image, GUIStyle style) { BeginGroup (position, GUIContent.Temp (image), style); }
+
+ // Begin a group. Must be matched with a call to ::ref::EndGroup.
+ CSRAW public static void BeginGroup (Rect position, GUIContent content, GUIStyle style) {
+ GUIUtility.CheckOnGUI ();
+
+ int id = GUIUtility.GetControlID (beginGroupHash, FocusType.Passive);
+
+ if (content != GUIContent.none || style != GUIStyle.none) {
+ switch (Event.current.type) {
+ case EventType.Repaint:
+ style.Draw (position, content, id);
+ break;
+ default:
+ if (position.Contains (Event.current.mousePosition))
+ GUIUtility.mouseUsed = true;
+ break;
+ }
+ }
+
+ GUIClip.Push (position, Vector2.zero, Vector2.zero, false);
+ }
+ static int beginGroupHash = "BeginGroup".GetHashCode ();
+
+ // End a group.
+ CSRAW public static void EndGroup () {
+ GUIClip.Pop ();
+ }
+
+ /// *listonly*
+ CSRAW public static Vector2 BeginScrollView (Rect position, Vector2 scrollPosition, Rect viewRect)
+ { return BeginScrollView (position, scrollPosition, viewRect, false, false, skin.horizontalScrollbar, skin.verticalScrollbar, GUI.skin.scrollView); }
+ /// *listonly*
+ CSRAW public static Vector2 BeginScrollView (Rect position, Vector2 scrollPosition, Rect viewRect, bool alwaysShowHorizontal, bool alwaysShowVertical)
+ { return BeginScrollView (position, scrollPosition, viewRect, alwaysShowHorizontal, alwaysShowVertical, skin.horizontalScrollbar, skin.verticalScrollbar, GUI.skin.scrollView); }
+ /// *listonly*
+ CSRAW public static Vector2 BeginScrollView (Rect position, Vector2 scrollPosition, Rect viewRect, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar)
+ { return BeginScrollView (position, scrollPosition, viewRect, false, false, horizontalScrollbar, verticalScrollbar, GUI.skin.scrollView); }
+ // Begin a scrolling view inside your GUI.
+ CSRAW public static Vector2 BeginScrollView (Rect position, Vector2 scrollPosition, Rect viewRect, bool alwaysShowHorizontal, bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar)
+ { return BeginScrollView (position, scrollPosition, viewRect, alwaysShowHorizontal, alwaysShowVertical, horizontalScrollbar, verticalScrollbar, null); }
+
+ // *undocumented
+ CSRAW protected static Vector2 DoBeginScrollView (Rect position, Vector2 scrollPosition, Rect viewRect, bool alwaysShowHorizontal, bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background) {
+ return BeginScrollView (position, scrollPosition, viewRect, alwaysShowHorizontal, alwaysShowVertical, horizontalScrollbar, verticalScrollbar, background);
+ }
+ CSRAW internal static Vector2 BeginScrollView (Rect position, Vector2 scrollPosition, Rect viewRect, bool alwaysShowHorizontal, bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background) {
+ GUIUtility.CheckOnGUI ();
+
+ #if UNITY_EDITOR
+ if (Event.current.type == EventType.DragUpdated && position.Contains(Event.current.mousePosition))
+ {
+ if (Mathf.Abs(Event.current.mousePosition.y - position.y) < 8)
+ {
+ scrollPosition.y -= 16;
+ InternalRepaintEditorWindow ();
+ }
+ else if (Mathf.Abs(Event.current.mousePosition.y - position.yMax) < 8)
+ {
+ scrollPosition.y += 16;
+ InternalRepaintEditorWindow ();
+ }
+ }
+ #endif
+
+ int id = GUIUtility.GetControlID (scrollviewHash, FocusType.Passive);
+ ScrollViewState state = (ScrollViewState)GUIUtility.GetStateObject (typeof (ScrollViewState), id);
+
+ if (state.apply) {
+ scrollPosition = state.scrollPosition;
+ state.apply = false;
+ }
+ state.position = position;
+ state.scrollPosition = scrollPosition;
+ state.visibleRect = state.viewRect = viewRect;
+ state.visibleRect.width = position.width;
+ state.visibleRect.height = position.height;
+ s_ScrollViewStates.Push (state);
+
+ Rect clipRect = new Rect (position);
+ switch (Event.current.type) {
+ case EventType.Layout:
+ GUIUtility.GetControlID (sliderHash, FocusType.Passive);
+ GUIUtility.GetControlID (repeatButtonHash, FocusType.Passive);
+ GUIUtility.GetControlID (repeatButtonHash, FocusType.Passive);
+ GUIUtility.GetControlID (sliderHash, FocusType.Passive);
+ GUIUtility.GetControlID (repeatButtonHash, FocusType.Passive);
+ GUIUtility.GetControlID (repeatButtonHash, FocusType.Passive);
+ break;
+ case EventType.Used:
+ break;
+ default:
+ bool needsVertical = alwaysShowVertical, needsHorizontal = alwaysShowHorizontal;
+
+ // Check if we need a horizontal scrollbar
+ if (needsHorizontal || viewRect.width > clipRect.width) {
+ state.visibleRect.height = position.height - horizontalScrollbar.fixedHeight + horizontalScrollbar.margin.top;
+ clipRect.height -= horizontalScrollbar.fixedHeight + horizontalScrollbar.margin.top;
+ needsHorizontal = true;
+ }
+ if (needsVertical || viewRect.height > clipRect.height) {
+ state.visibleRect.width = position.width - verticalScrollbar.fixedWidth + verticalScrollbar.margin.left;
+ clipRect.width -= verticalScrollbar.fixedWidth + verticalScrollbar.margin.left;
+ needsVertical = true;
+ if (!needsHorizontal && viewRect.width > clipRect.width) {
+ state.visibleRect.height = position.height - horizontalScrollbar.fixedHeight + horizontalScrollbar.margin.top;
+ clipRect.height -= horizontalScrollbar.fixedHeight + horizontalScrollbar.margin.top;
+ needsHorizontal = true;
+ }
+ }
+
+ if (Event.current.type == EventType.Repaint && background != GUIStyle.none) {
+ background.Draw (position, position.Contains (Event.current.mousePosition), false, needsHorizontal && needsVertical, false);
+ }
+ if (needsHorizontal && horizontalScrollbar != GUIStyle.none) {
+ scrollPosition.x = HorizontalScrollbar (new Rect (position.x, position.yMax - horizontalScrollbar.fixedHeight, clipRect.width, horizontalScrollbar.fixedHeight),
+ scrollPosition.x, clipRect.width, 0, viewRect.width,
+ horizontalScrollbar);
+ } else {
+ GUIUtility.GetControlID (sliderHash, FocusType.Passive);
+ GUIUtility.GetControlID (repeatButtonHash, FocusType.Passive);
+ GUIUtility.GetControlID (repeatButtonHash, FocusType.Passive);
+ if (horizontalScrollbar != GUIStyle.none)
+ scrollPosition.x = 0;
+ else
+ scrollPosition.x = Mathf.Clamp (scrollPosition.x, 0, Mathf.Max (viewRect.width - position.width, 0));
+ }
+
+ if (needsVertical && verticalScrollbar != GUIStyle.none) {
+ scrollPosition.y = VerticalScrollbar (new Rect (clipRect.xMax + verticalScrollbar.margin.left, clipRect.y, verticalScrollbar.fixedWidth, clipRect.height),
+ scrollPosition.y, clipRect.height, 0, viewRect.height,
+ verticalScrollbar);
+ } else {
+ GUIUtility.GetControlID (sliderHash, FocusType.Passive);
+ GUIUtility.GetControlID (repeatButtonHash, FocusType.Passive);
+ GUIUtility.GetControlID (repeatButtonHash, FocusType.Passive);
+ if (verticalScrollbar != GUIStyle.none)
+ scrollPosition.y = 0;
+ else
+ scrollPosition.y = Mathf.Clamp (scrollPosition.y, 0, Mathf.Max (viewRect.height - position.height, 0));
+ }
+ break;
+ }
+ GUIClip.Push (clipRect, new Vector2 (Mathf.Round (-scrollPosition.x - viewRect.x), Mathf.Round (-scrollPosition.y - viewRect.y)),Vector2.zero, false);
+ return scrollPosition;
+ }
+ static int scrollviewHash = "scrollView".GetHashCode ();
+ static UnityEngineInternal.GenericStack s_ScrollViewStates = new UnityEngineInternal.GenericStack ();
+ CLASS internal ScrollViewState
+ CSRAW
+ //*undocumented*
+ public Rect position, visibleRect, viewRect;
+ //*undocumented*
+ public Vector2 scrollPosition;
+ //*undocumented*
+ public bool apply = false;
+ //*undocumented*
+ public bool hasScrollTo = false;
+
+ //*undocumented*
+ /*public void ScrollTo (Rect position) {
+ Vector2 pos = GUIClip.Unclip (new Vector2 (position.xMin, position.yMin));
+ if (!hasScrollTo) {
+ hasScrollTo = true;
+ scrollTo.xMin = pos.x;
+ scrollTo.yMin = pos.y;
+ pos = GUIClip.Unclip (new Vector2 (position.xMax, position.yMax));
+ scrollTo.xMax = pos.x;
+ scrollTo.yMax = pos.y;
+ hasScrollTo = true;
+ } else {
+ scrollTo.x = Mathf.Min (pos.x, scrollTo.x);
+ scrollTo.y = Mathf.Min (pos.y, scrollTo.y);
+ pos = GUIClip.Unclip (new Vector2 (position.xMax, position.yMax));
+ scrollTo.xMax = Mathf.Max (pos.x, scrollTo.xMax);
+ scrollTo.yMax = Mathf.Max (pos.y, scrollTo.yMax);
+ }
+ }*/
+
+ /*internal void ScrollTo (Rect position) {
+ Vector2 pos = new Vector2 (position.xMin, position.yMin);
+ if (!hasScrollTo) {
+ // Is hasScrollTo ever true outside of this method?
+ // The ScrollTo method doesn't seems to have any recursive logic so not sure what it's used for.
+ hasScrollTo = true;
+
+ // scrollTo is being set to the same as position but in a really cumbersome way?
+ scrollTo.xMin = pos.x;
+ scrollTo.yMin = pos.y;
+ pos = new Vector2 (position.xMax, position.yMax);
+ scrollTo.xMax = pos.x;
+ scrollTo.yMax = pos.y;
+ hasScrollTo = true;
+
+ Rect r = visibleRect;
+ r.x += scrollPosition.x;
+ r.y += scrollPosition.y;
+
+ Vector2 bottomRight = new Vector2 (scrollTo.xMax, scrollTo.yMax);
+ Vector2 topLeft = new Vector2 (scrollTo.xMin, scrollTo.yMin);
+
+ if (bottomRight.x > r.xMax)
+ scrollPosition.x += bottomRight.x - r.xMax;
+ if (topLeft.x < r.xMin)
+ scrollPosition.x -= r.xMin - topLeft.x;
+
+ if (bottomRight.y > r.yMax)
+ scrollPosition.y += bottomRight.y - r.yMax;
+ if (topLeft.y < r.yMin)
+ scrollPosition.y -= r.yMin - topLeft.y;
+
+ apply = true;
+ hasScrollTo = false;
+ } else {
+ scrollTo.x = Mathf.Min (pos.x, scrollTo.x);
+ scrollTo.y = Mathf.Min (pos.y, scrollTo.y);
+ pos = new Vector2 (position.xMax, position.yMax);
+ scrollTo.xMax = Mathf.Max (pos.x, scrollTo.xMax);
+ scrollTo.yMax = Mathf.Max (pos.y, scrollTo.yMax);
+ }
+ }*/
+
+ internal void ScrollTo (Rect position)
+ {
+ ScrollTowards (position, Mathf.Infinity);
+ }
+
+ internal bool ScrollTowards (Rect position, float maxDelta)
+ {
+ Vector2 scrollVector = ScrollNeeded (position);
+
+ // If we don't need scrolling, return false
+ if (scrollVector.sqrMagnitude < 0.0001f)
+ return false;
+
+ // If we need scrolling but don't actually allow any, just return true to
+ // indicate scrolling is needed to be able to see position
+ if (maxDelta == 0)
+ return true;
+
+ // Clamp scrolling to max allowed delta
+ if (scrollVector.magnitude > maxDelta)
+ scrollVector = scrollVector.normalized * maxDelta;
+
+ // Apply scrolling
+ scrollPosition += scrollVector;
+ apply = true;
+
+ return true;
+ }
+
+ internal Vector2 ScrollNeeded (Rect position)
+ {
+ Rect r = visibleRect;
+ r.x += scrollPosition.x;
+ r.y += scrollPosition.y;
+
+ // If the rect we want to see is larger than the visible rect, then trim it,
+ // otherwise we can get oscillation or other unwanted behavior
+ float excess = position.height - visibleRect.height;
+ if (excess > 0)
+ {
+ position.width -= excess;
+ position.x += excess * 0.5f;
+ }
+ excess = position.height - visibleRect.height;
+ if (excess > 0)
+ {
+ position.height -= excess;
+ position.y += excess * 0.5f;
+ }
+
+ Vector2 scrollVector = Vector2.zero;
+
+ // Calculate needed x scrolling
+ if (position.xMax > r.xMax)
+ scrollVector.x += position.xMax - r.xMax;
+ else if (position.xMin < r.xMin)
+ scrollVector.x -= r.xMin - position.xMin;
+
+ // Calculate needed y scrolling
+ if (position.yMax > r.yMax)
+ scrollVector.y += position.yMax - r.yMax;
+ else if (position.yMin < r.yMin)
+ scrollVector.y -= r.yMin - position.yMin;
+
+ // Clamp scrolling to bounds so we don't request to scroll past the edge
+ scrollVector.x = Mathf.Clamp (scrollVector.x, viewRect.xMin-scrollPosition.x, viewRect.xMax-visibleRect.width-scrollPosition.x);
+ scrollVector.y = Mathf.Clamp (scrollVector.y, viewRect.yMin-scrollPosition.y, viewRect.yMax-visibleRect.height-scrollPosition.y);
+
+ return scrollVector;
+ }
+ END
+
+ // Ends a scrollview started with a call to BeginScrollView.
+ public static void EndScrollView () {
+ EndScrollView (true);
+ }
+
+ public static void EndScrollView (bool handleScrollWheel) {
+ ScrollViewState state = (ScrollViewState)s_ScrollViewStates.Peek ();
+
+ GUIUtility.CheckOnGUI ();
+ GUIClip.Pop ();
+
+ s_ScrollViewStates.Pop ();
+
+ // This is the mac way of handling things: if the mouse is over a scrollview, the scrollview gets the event.
+ if (handleScrollWheel && Event.current.type == EventType.scrollWheel && state.position.Contains (Event.current.mousePosition)) {
+ state.scrollPosition.x = Mathf.Clamp (state.scrollPosition.x + (Event.current.delta.x * 20f), 0f, state.viewRect.width - state.visibleRect.width);
+ state.scrollPosition.y = Mathf.Clamp (state.scrollPosition.y + (Event.current.delta.y * 20f), 0f, state.viewRect.height - state.visibleRect.height);
+ state.apply = true;
+ Event.current.Use ();
+ }
+ }
+
+ internal static ScrollViewState GetTopScrollView()
+ {
+ if (s_ScrollViewStates.Count != 0)
+ return (ScrollViewState)s_ScrollViewStates.Peek ();
+ return null;
+ }
+
+ // Scrolls all enclosing scrollviews so they try to make /position/ visible.
+ public static void ScrollTo (Rect position)
+ {
+ ScrollViewState topmost = GetTopScrollView();
+ if (topmost != null)
+ topmost.ScrollTo(position);
+ }
+
+ // Scrolls all enclosing scrollviews towards making /position/ visible.
+ public static bool ScrollTowards (Rect position, float maxDelta)
+ {
+ ScrollViewState topmost = GetTopScrollView();
+ if (topmost == null)
+ return false;
+ return topmost.ScrollTowards(position, maxDelta);
+ }
+
+CSRAW
+// ====================================================== WINDOWS ===============================
+
+
+ /// *listonly*
+ public delegate void WindowFunction(int id);
+ /// *listonly*
+ CSRAW public static Rect Window (int id, Rect clientRect, WindowFunction func, string text)
+ { return DoWindow (id, clientRect, func, GUIContent.Temp (text), GUI.skin.window, GUI.skin, true); }
+ /// *listonly*
+ CSRAW public static Rect Window (int id, Rect clientRect, WindowFunction func, Texture image)
+ { return DoWindow (id, clientRect, func, GUIContent.Temp (image), GUI.skin.window, GUI.skin, true); }
+ /// *listonly*
+ CSRAW public static Rect Window (int id, Rect clientRect, WindowFunction func, GUIContent content)
+ { return DoWindow (id, clientRect, func, content, GUI.skin.window, GUI.skin, true); }
+ /// *listonly*
+ CSRAW public static Rect Window (int id, Rect clientRect, WindowFunction func, string text, GUIStyle style)
+ { return DoWindow (id, clientRect, func, GUIContent.Temp (text), style, GUI.skin, true); }
+ /// *listonly*
+ CSRAW public static Rect Window (int id, Rect clientRect, WindowFunction func, Texture image, GUIStyle style)
+ { return DoWindow (id, clientRect, func, GUIContent.Temp (image), style, GUI.skin, true); }
+
+ // Make a popup window.
+
+BEGIN DOC
+
+ Windows float above normal GUI controls, feature click-to-focus and can optionally be dragged around by the end user.
+ Unlike other controls, you need to pass them a separate function for the GUI controls to put inside the window.
+
+ <b>Note:</b> If you are using [[GUILayout]] to place your components inside the window, you should use GUILayout.Window.
+ Here is a small example to get you started:
+
+ You can use the same function to create multiple windows. Just make sure that ''each window has its own ID''. Example:
+
+ To stop showing a window, simply stop calling GUI.Window from inside your main OnGUI function:
+
+ To make a window that gets its size from automatic GUI layouting, use GUILayout.Window.
+
+ __Call Ordering__
+
+ Windows need to be drawn back-to-front; windows on top of other windows need to be drawn later than the ones below them. This means that you can not count on your DoWindow functions to
+ be called in any particular order. In order for this to work seamlessly, the following values are stored when you create your window (using the __Window__ function), and retrieved when your DoWindow gets called:
+ GUI.skin, GUI.enabled, GUI.color, GUI.backgroundColor, GUI.contentColor, GUI.matrix
+
+
+ This means it's easy to do colored windows like this:
+ Hint: you can use the alpha component of GUI.color to fade windows in and out.
+
+
+ SA: ::ref::DragWindow, ::ref::BringWindowToFront, ::ref::BringWindowToBack
+
+ @param id A unique ID to use for each window. This is the ID you'll use to interface to.
+ @param clientRect Rectangle on the screen to use for the group.
+ @param func The function that creates the GUI /inside/ the window. This function must take one parameter - the /id/ of the window it's currently making GUI for.
+ @param text Text to display as a title for the window.
+ @param image [[Texture]] to display an image in the titlebar.
+ @param content Text, image and tooltip for this window.
+ @param style An optional style to use for the window. If left out, the /window/ style from the current [[GUISkin]] is used.
+ @returns the rectangle the window is at.
+END DOC
+
+ CSRAW public static Rect Window (int id, Rect clientRect, WindowFunction func, GUIContent title, GUIStyle style) { return DoWindow (id, clientRect, func, title, style, GUI.skin, true); }
+
+ // TODO: DOCME
+ CSRAW public static Rect ModalWindow (int id, Rect clientRect, WindowFunction func, string text)
+ { return DoModalWindow(id, clientRect, func, GUIContent.Temp(text), GUI.skin.window, GUI.skin); }
+
+ CSRAW public static Rect ModalWindow (int id, Rect clientRect, WindowFunction func, Texture image)
+ { return DoModalWindow(id, clientRect, func, GUIContent.Temp(image), GUI.skin.window, GUI.skin); }
+
+ CSRAW public static Rect ModalWindow (int id, Rect clientRect, WindowFunction func, GUIContent content)
+ { return DoModalWindow(id, clientRect, func, content, GUI.skin.window, GUI.skin); }
+
+ CSRAW public static Rect ModalWindow (int id, Rect clientRect, WindowFunction func, string text, GUIStyle style)
+ { return DoModalWindow(id, clientRect, func, GUIContent.Temp(text), style, GUI.skin); }
+
+ CSRAW public static Rect ModalWindow (int id, Rect clientRect, WindowFunction func, Texture image, GUIStyle style)
+ { return DoModalWindow(id, clientRect, func, GUIContent.Temp(image), style, GUI.skin); }
+
+ CSRAW public static Rect ModalWindow (int id, Rect clientRect, WindowFunction func, GUIContent content, GUIStyle style)
+ { return DoModalWindow(id, clientRect, func, content, style, GUI.skin); }
+
+ CUSTOM private static Rect DoModalWindow (int id, Rect clientRect, WindowFunction func, GUIContent content, GUIStyle style, GUISkin skin)
+ {
+ return IMGUI::DoWindow (GetGUIState (), id, clientRect, func, MonoGUIContentToTempNative (content), style.GetScriptingObject(), skin, true, true);
+ }
+
+ CSRAW internal static void CallWindowDelegate (WindowFunction func, int id, GUISkin _skin, int forceRect, float width, float height, GUIStyle style)
+ {
+ GUILayoutUtility.SelectIDList (id, true);
+ GUISkin temp = skin;
+ if (Event.current.type == EventType.Layout)
+ {
+ if (forceRect != 0) {
+ GUILayoutOption[] options = { GUILayout.Width (width), GUILayout.Height(height) };
+
+ // Tell the GUILayout system we're starting a window, our style and our size. Then layouting is just the same as anything else
+ GUILayoutUtility.BeginWindow (id, style, options);
+ } else {
+ // If we don't want to force the rect (which is when we come from GUILayout.window), don't pass in the fixedsize options
+ GUILayoutUtility.BeginWindow (id, style, null);
+ }
+ }
+ skin = _skin;
+ func (id);
+
+ if (Event.current.type == EventType.Layout)
+ {
+ // Now layout the window.
+ GUILayoutUtility.Layout ();
+ }
+ skin = temp;
+ }
+
+ CUSTOM private static Rect DoWindow (int id, Rect clientRect, WindowFunction func, GUIContent title, GUIStyle style, GUISkin skin, bool forceRectOnLayout) {
+ return IMGUI::DoWindow (GetGUIState (), id, clientRect, func, MonoGUIContentToTempNative (title), style.GetScriptingObject(), skin, forceRectOnLayout);
+ }
+
+ // Make a window draggable.
+ CUSTOM static void DragWindow (Rect position)
+ { IMGUI::DragWindow (GetGUIState(), position); }
+
+
+ // If you want to have the entire window background to act as a drag area, use the version of DragWindow that takes no parameters and put it at the end of the window function.
+ CSRAW public static void DragWindow () { DragWindow (new Rect (0,0, 10000,10000)); }
+
+ // Bring a specific window to front of the floating windows.
+ CUSTOM static void BringWindowToFront (int windowID)
+ {
+ IMGUI::BringWindowToFront (GetGUIState(), windowID);
+ }
+
+ // Bring a specific window to back of the floating windows.
+ CUSTOM static void BringWindowToBack (int windowID)
+ {
+ IMGUI::BringWindowToBack (GetGUIState(), windowID);
+ }
+
+ // Make a window become the active window.
+ CUSTOM static void FocusWindow (int windowID)
+ {
+ IMGUI::FocusWindow (GetGUIState(), windowID);
+ }
+
+ // Remove focus from all windows.
+ CUSTOM static void UnfocusWindow () {
+ IMGUI::FocusWindow (GetGUIState(), -1);
+ }
+
+ CSRAW
+ // Call at the beginning of a frame.
+ // e event to process
+ // windowInfo - the list of windows we're currently using.
+ // *undocumented*
+ internal static void BeginWindows (int skinMode, int editorWindowInstanceID) {
+ // Let's just remember where we came from
+ GUILayoutGroup oldTopLevel = GUILayoutUtility.current.topLevel;
+ UnityEngineInternal.GenericStack oldLayoutGroups = GUILayoutUtility.current.layoutGroups;
+ GUILayoutGroup oldWindows = GUILayoutUtility.current.windows;
+ Matrix4x4 mat = GUI.matrix;
+
+ // Call into C++ land
+ Internal_BeginWindows ();
+
+ GUI.matrix = mat;
+ GUILayoutUtility.current.topLevel = oldTopLevel;
+ GUILayoutUtility.current.layoutGroups = oldLayoutGroups;
+ GUILayoutUtility.current.windows = oldWindows;
+ }
+
+ CUSTOM private static void Internal_BeginWindows ()
+ {
+ IMGUI::BeginWindows (GetGUIState(), false);
+ }
+
+ // Call at the end of frame (at layer 0) to do all windows
+ CSRAW internal static void EndWindows () {
+ // Let's just remember where we came from
+ GUILayoutGroup oldTopLevel = GUILayoutUtility.current.topLevel;
+ UnityEngineInternal.GenericStack oldLayoutGroups = GUILayoutUtility.current.layoutGroups;
+ GUILayoutGroup oldWindows = GUILayoutUtility.current.windows;
+
+ // Call Into C++ land
+ Internal_EndWindows ();
+
+ GUILayoutUtility.current.topLevel = oldTopLevel;
+ GUILayoutUtility.current.layoutGroups = oldLayoutGroups;
+ GUILayoutUtility.current.windows = oldWindows;
+ }
+
+ CUSTOM private static void Internal_EndWindows ()
+ {
+ IMGUI::EndWindows (GetGUIState());
+ }
+END
+
+
+CSRAW
+
+} // namespace
diff --git a/Runtime/Export/GUIContentBindings.txt b/Runtime/Export/GUIContentBindings.txt
new file mode 100644
index 0000000..c53201b
--- /dev/null
+++ b/Runtime/Export/GUIContentBindings.txt
@@ -0,0 +1,132 @@
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+
+
+// The contents of a GUI element.
+CSRAW [StructLayout (LayoutKind.Sequential)]
+CSRAW [System.Serializable]
+CLASS GUIContent
+
+ // MUST MATCH MEMORY LAYOUT IN GUICONTENT.CPP
+ CSRAW
+ [SerializeField]
+ string m_Text = "";
+ [SerializeField]
+ Texture m_Image;
+ [SerializeField]
+ string m_Tooltip = "";
+
+
+
+ // The text contained.
+ CSRAW public string text { get { return m_Text; } set { m_Text = value; } }
+
+ // The icon image contained.
+ CSRAW public Texture image { get { return m_Image; } set { m_Image = value; } }
+
+ // The tooltip of this element.
+ CSRAW public string tooltip { get { return m_Tooltip; } set { m_Tooltip = value; } }
+
+
+ // Constructor for GUIContent in all shapes and sizes
+ CSRAW public GUIContent () {}
+ // Build a GUIContent object containing only text.
+ CSRAW public GUIContent (string text) {
+ m_Text = text;
+ }
+
+ // Build a GUIContent object containing only an image.
+ CSRAW public GUIContent (Texture image) {
+ m_Image = image;
+ }
+
+ // Build a GUIContent object containing both /text/ and an image.
+ CSRAW public GUIContent (string text, Texture image) {
+ m_Text = text;
+ m_Image = image;
+ }
+
+ // Build a GUIContent containing some /text/. When the user hovers the mouse over it, the global GUI::ref::tooltip is set to the /tooltip/.
+ CSRAW public GUIContent (string text, string tooltip) {
+ m_Text = text;
+ m_Tooltip = tooltip;
+ }
+
+ // Build a GUIContent containing an image. When the user hovers the mouse over it, the global GUI::ref::tooltip is set to the /tooltip/.
+ CSRAW public GUIContent (Texture image, string tooltip) {
+ m_Image = image;
+ m_Tooltip = tooltip;
+ }
+
+ // Build a GUIContent that contains both /text/, an /image/ and has a /tooltip/ defined. When the user hovers the mouse over it, the global GUI::ref::tooltip is set to the /tooltip/.
+ CSRAW public GUIContent (string text, Texture image, string tooltip) {
+ m_Text = text;
+ m_Image = image;
+ m_Tooltip = tooltip;
+ }
+
+ // Build a GUIContent as a copy of another GUIContent.
+ CSRAW public GUIContent (GUIContent src) {
+ m_Text = src.m_Text;
+ m_Image = src.m_Image;
+ m_Tooltip = src.m_Tooltip;
+ }
+
+ // Shorthand for empty content.
+ CSRAW public static GUIContent none = new GUIContent ("");
+
+ // *undocumented*
+ CSRAW
+ internal int hash {get {
+ int h = 0;
+ if (m_Text != null && m_Text != "")
+ h = m_Text.GetHashCode () * 37;
+ return h;
+ }}
+ static GUIContent s_Text = new GUIContent(), s_Image = new GUIContent(), s_TextImage = new GUIContent();
+ internal static GUIContent Temp (string t) {
+ s_Text.m_Text = t;
+ return s_Text;
+ }
+ internal static GUIContent Temp (Texture i) {
+ s_Image.m_Image = i;
+ return s_Image;
+ }
+ internal static GUIContent Temp (string t, Texture i) {
+ s_TextImage.m_Text = t;
+ s_TextImage.m_Image = i;
+ return s_TextImage;
+ }
+ internal static void ClearStaticCache()
+ {
+ s_Text.m_Text = null;
+ s_Image.m_Image = null;
+ s_TextImage.m_Text = null;
+ s_TextImage.m_Image = null;
+ }
+
+ internal static GUIContent[] Temp (string[] texts) {
+ GUIContent[] retval = new GUIContent[texts.Length];
+ for (int i = 0; i < texts.Length; i++) {
+ retval[i] = new GUIContent (texts[i]);
+ }
+ return retval;
+ }
+ internal static GUIContent[] Temp (Texture[] images) {
+ GUIContent[] retval = new GUIContent[images.Length];
+ for (int i = 0; i < images.Length; i++) {
+ retval[i] = new GUIContent (images[i]);
+ }
+ return retval;
+ }
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/GUILayout.txt b/Runtime/Export/GUILayout.txt
new file mode 100644
index 0000000..c795017
--- /dev/null
+++ b/Runtime/Export/GUILayout.txt
@@ -0,0 +1,517 @@
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+
+// The GUILayout class is the interface for Unity gui with automatic layout.
+CLASS GUILayout
+CSRAW
+ /// *listonly*
+ CSRAW static public void Label (Texture image, params GUILayoutOption[] options) { DoLabel (GUIContent.Temp (image), GUI.skin.label, options); }
+ /// *listonly*
+ CSRAW static public void Label (string text, params GUILayoutOption[] options) { DoLabel (GUIContent.Temp (text), GUI.skin.label, options); }
+ /// *listonly*
+ CSRAW static public void Label (GUIContent content, params GUILayoutOption[] options) { DoLabel (content, GUI.skin.label, options); }
+ /// *listonly*
+ CSRAW static public void Label (Texture image, GUIStyle style, params GUILayoutOption[] options) { DoLabel (GUIContent.Temp (image), style, options); }
+ /// *listonly*
+ CSRAW static public void Label (string text, GUIStyle style, params GUILayoutOption[] options) { DoLabel (GUIContent.Temp (text), style, options); }
+ // Make an auto-layout label.
+ CSRAW static public void Label (GUIContent content, GUIStyle style, params GUILayoutOption[] options) { DoLabel (content, style, options); }
+ CSRAW static void DoLabel (GUIContent content, GUIStyle style, GUILayoutOption[] options)
+ { GUI.Label (GUILayoutUtility.GetRect (content, style, options), content, style); }
+
+
+ /// *listonly*
+ CSRAW static public void Box (Texture image, params GUILayoutOption[] options) { DoBox (GUIContent.Temp (image), GUI.skin.box, options); }
+ /// *listonly*
+ CSRAW static public void Box (string text, params GUILayoutOption[] options) { DoBox (GUIContent.Temp (text), GUI.skin.box, options); }
+ /// *listonly*
+ CSRAW static public void Box (GUIContent content, params GUILayoutOption[] options) { DoBox (content, GUI.skin.box, options); }
+ /// *listonly*
+ CSRAW static public void Box (Texture image, GUIStyle style, params GUILayoutOption[] options) { DoBox (GUIContent.Temp (image), style, options); }
+ /// *listonly*
+ CSRAW static public void Box (string text, GUIStyle style, params GUILayoutOption[] options) { DoBox (GUIContent.Temp (text), style, options); }
+ // Make an auto-layout box.
+ CSRAW static public void Box (GUIContent content, GUIStyle style, params GUILayoutOption[] options) { DoBox (content, style, options); }
+ CSRAW static void DoBox (GUIContent content, GUIStyle style, GUILayoutOption[] options)
+ { GUI.Box (GUILayoutUtility.GetRect (content, style, options), content, style); }
+
+ /// *listonly*
+ CSRAW static public bool Button (Texture image, params GUILayoutOption[] options) { return DoButton (GUIContent.Temp (image), GUI.skin.button, options); }
+ /// *listonly*
+ CSRAW static public bool Button (string text, params GUILayoutOption[] options) { return DoButton (GUIContent.Temp (text), GUI.skin.button, options); }
+ /// *listonly*
+ CSRAW static public bool Button (GUIContent content, params GUILayoutOption[] options) { return DoButton (content, GUI.skin.button, options); }
+ /// *listonly*
+ CSRAW static public bool Button (Texture image, GUIStyle style, params GUILayoutOption[] options) { return DoButton (GUIContent.Temp (image), style, options); }
+ /// *listonly*
+ CSRAW static public bool Button (string text, GUIStyle style, params GUILayoutOption[] options) { return DoButton (GUIContent.Temp (text), style, options); }
+ // Make a single press button. The user clicks them and something happens immediately.
+ CSRAW static public bool Button (GUIContent content, GUIStyle style, params GUILayoutOption[] options) { return DoButton (content, style, options); }
+ CSRAW static bool DoButton (GUIContent content, GUIStyle style, GUILayoutOption[] options)
+ { return GUI.Button (GUILayoutUtility.GetRect (content, style, options), content, style); }
+
+
+
+ /// *listonly*
+ CSRAW static public bool RepeatButton (Texture image, params GUILayoutOption[] options) { return DoRepeatButton (GUIContent.Temp (image), GUI.skin.button, options); }
+ /// *listonly*
+ CSRAW static public bool RepeatButton (string text, params GUILayoutOption[] options) { return DoRepeatButton (GUIContent.Temp (text), GUI.skin.button, options); }
+ /// *listonly*
+ CSRAW static public bool RepeatButton (GUIContent content, params GUILayoutOption[] options) { return DoRepeatButton (content, GUI.skin.button, options); }
+ /// *listonly*
+ CSRAW static public bool RepeatButton (Texture image, GUIStyle style, params GUILayoutOption[] options) { return DoRepeatButton (GUIContent.Temp (image), style, options); }
+ /// *listonly*
+ CSRAW static public bool RepeatButton (string text, GUIStyle style, params GUILayoutOption[] options) { return DoRepeatButton (GUIContent.Temp (text), style, options); }
+ // Make a repeating button. The button returns true as long as the user holds down the mouse
+ CSRAW static public bool RepeatButton (GUIContent content, GUIStyle style, params GUILayoutOption[] options) { return DoRepeatButton (content, style, options); }
+ CSRAW static bool DoRepeatButton (GUIContent content, GUIStyle style, GUILayoutOption[] options)
+ { return GUI.RepeatButton (GUILayoutUtility.GetRect (content, style, options), content, style); }
+
+ /// *listonly*
+ CSRAW public static string TextField (string text, params GUILayoutOption[] options) { return DoTextField (text, -1, false, GUI.skin.textField, options); }
+ /// *listonly*
+ CSRAW public static string TextField (string text, int maxLength, params GUILayoutOption[] options) { return DoTextField (text, maxLength, false, GUI.skin.textField, options); }
+ /// *listonly*
+ CSRAW public static string TextField (string text, GUIStyle style, params GUILayoutOption[] options) { return DoTextField (text, -1, false, style, options); }
+ // Make a single-line text field where the user can edit a string.
+ CSRAW public static string TextField (string text, int maxLength, GUIStyle style, params GUILayoutOption[] options) { return DoTextField (text, maxLength, true, style, options); }
+
+ /// *listonly*
+ CSRAW public static string PasswordField (string password, char maskChar, params GUILayoutOption[] options) {
+ return PasswordField(password, maskChar, -1, GUI.skin.textField, options);
+ }
+ /// *listonly*
+ CSRAW public static string PasswordField (string password, char maskChar, int maxLength, params GUILayoutOption[] options) {
+ return PasswordField(password, maskChar, maxLength, GUI.skin.textField, options);
+ }
+ /// *listonly*
+ CSRAW public static string PasswordField (string password, char maskChar, GUIStyle style, params GUILayoutOption[] options) {
+ return PasswordField(password, maskChar, -1, style, options);
+ }
+ // Make a text field where the user can enter a password.
+ CSRAW public static string PasswordField (string password, char maskChar, int maxLength, GUIStyle style, params GUILayoutOption[] options) {
+ GUIContent t = GUIContent.Temp (GUI.PasswordFieldGetStrToShow(password, maskChar));
+ return GUI.PasswordField(GUILayoutUtility.GetRect (t, GUI.skin.textField, options), password, maskChar, maxLength, style);
+ }
+
+ /// *listonly*
+ CSRAW public static string TextArea (string text, params GUILayoutOption[] options) { return DoTextField (text, -1, true, GUI.skin.textArea, options); }
+ /// *listonly*
+ CSRAW public static string TextArea (string text, int maxLength, params GUILayoutOption[] options) { return DoTextField (text, maxLength, true, GUI.skin.textArea, options); }
+ /// *listonly*
+ CSRAW public static string TextArea (string text, GUIStyle style, params GUILayoutOption[] options) { return DoTextField (text, -1, true, style, options); }
+ // Make a multi-line text field where the user can edit a string.
+ CSRAW public static string TextArea (string text, int maxLength, GUIStyle style, params GUILayoutOption[] options) { return DoTextField (text, maxLength, true, style, options); }
+
+ CSRAW static string DoTextField (string text, int maxLength, bool multiline, GUIStyle style, GUILayoutOption[] options) {
+ int id = GUIUtility.GetControlID (FocusType.Keyboard);
+ GUIContent content = GUIContent.Temp (text);
+ Rect r;
+ if (GUIUtility.keyboardControl != id)
+ content = GUIContent.Temp (text);
+ else
+ content = GUIContent.Temp (text + Input.compositionString);
+
+ r = GUILayoutUtility.GetRect (content, style, options);
+ if (GUIUtility.keyboardControl == id)
+ content = GUIContent.Temp (text);
+ GUI.DoTextField (r, id, content, multiline, maxLength, style);
+ return content.text;
+ }
+
+ /// *listonly*
+ CSRAW static public bool Toggle (bool value, Texture image, params GUILayoutOption[] options) { return DoToggle (value, GUIContent.Temp (image), GUI.skin.toggle, options); }
+ /// *listonly*
+ CSRAW static public bool Toggle (bool value, string text, params GUILayoutOption[] options) { return DoToggle (value, GUIContent.Temp (text), GUI.skin.toggle, options); }
+ /// *listonly*
+ CSRAW static public bool Toggle (bool value, GUIContent content, params GUILayoutOption[] options) { return DoToggle (value, content, GUI.skin.toggle, options); }
+ /// *listonly*
+ CSRAW static public bool Toggle (bool value, Texture image, GUIStyle style, params GUILayoutOption[] options) { return DoToggle (value, GUIContent.Temp (image), style, options); }
+ /// *listonly*
+ CSRAW static public bool Toggle (bool value, string text, GUIStyle style, params GUILayoutOption[] options) { return DoToggle (value, GUIContent.Temp (text), style, options); }
+ // Make an on/off toggle button.
+ CSRAW static public bool Toggle (bool value, GUIContent content, GUIStyle style, params GUILayoutOption[] options) { return DoToggle (value, content, style, options); }
+
+ //*undocumented*
+ CSRAW static bool DoToggle (bool value, GUIContent content, GUIStyle style, GUILayoutOption[] options)
+ { return GUI.Toggle (GUILayoutUtility.GetRect (content, style, options), value, content, style); }
+
+
+
+ /// *listonly*
+ CSRAW public static int Toolbar (int selected, string[] texts, params GUILayoutOption[] options) { return Toolbar (selected, GUIContent.Temp (texts), GUI.skin.button, options); }
+ /// *listonly*
+ CSRAW public static int Toolbar (int selected, Texture[] images, params GUILayoutOption[] options) { return Toolbar (selected, GUIContent.Temp (images), GUI.skin.button, options); }
+ /// *listonly*
+ CSRAW public static int Toolbar (int selected, GUIContent[] content, params GUILayoutOption[] options) { return Toolbar (selected, content, GUI.skin.button, options); }
+ /// *listonly*
+ CSRAW public static int Toolbar (int selected, string[] texts, GUIStyle style, params GUILayoutOption[] options) { return Toolbar (selected, GUIContent.Temp (texts), style, options); }
+ /// *listonly*
+ CSRAW public static int Toolbar (int selected, Texture[] images, GUIStyle style, params GUILayoutOption[] options) { return Toolbar (selected, GUIContent.Temp (images), style, options); }
+ // Make a toolbar
+ CSRAW public static int Toolbar (int selected, GUIContent[] contents, GUIStyle style, params GUILayoutOption[] options) {
+ GUIStyle firstStyle, midStyle, lastStyle;
+ GUI.FindStyles (ref style, out firstStyle, out midStyle, out lastStyle, "left", "mid", "right");
+
+ Vector2 size = new Vector2();
+ int count = contents.Length;
+ GUIStyle currentStyle = count > 1 ? firstStyle : style;
+ GUIStyle nextStyle = count > 1 ? midStyle : style;
+ GUIStyle endStyle = count > 1 ? lastStyle : style;
+ int margins = currentStyle.margin.left;
+
+ for (int i = 0; i < contents.Length; i++)
+ {
+ if (i == count - 2)
+ {
+ currentStyle = nextStyle;
+ nextStyle = endStyle;
+ }
+ if (i == count - 1)
+ currentStyle = endStyle;
+
+ Vector2 thisSize = currentStyle.CalcSize (contents[i]);
+
+ if (thisSize.x > size.x)
+ size.x = thisSize.x;
+ if (thisSize.y > size.y)
+ size.y = thisSize.y;
+
+ if (i == count - 1)
+ margins += currentStyle.margin.right;
+ else
+ margins += Mathf.Max (currentStyle.margin.right, nextStyle.margin.left);
+ }
+
+ size.x = size.x * contents.Length + margins;
+
+ return GUI.Toolbar (GUILayoutUtility.GetRect (size.x, size.y, style, options), selected, contents, style);
+ }
+
+ /// *listonly*
+ CSRAW public static int SelectionGrid (int selected, string[] texts, int xCount, params GUILayoutOption[] options) { return SelectionGrid (selected, GUIContent.Temp (texts), xCount, GUI.skin.button, options); }
+ /// *listonly*
+ CSRAW public static int SelectionGrid (int selected, Texture[] images, int xCount, params GUILayoutOption[] options) { return SelectionGrid (selected, GUIContent.Temp (images), xCount, GUI.skin.button, options); }
+ /// *listonly*
+ CSRAW public static int SelectionGrid (int selected, GUIContent[] content, int xCount, params GUILayoutOption[] options) { return SelectionGrid (selected, content, xCount, GUI.skin.button, options); }
+ /// *listonly*
+ CSRAW public static int SelectionGrid (int selected, string[] texts, int xCount, GUIStyle style, params GUILayoutOption[] options) { return SelectionGrid (selected, GUIContent.Temp (texts), xCount, style, options); }
+ /// *listonly*
+ CSRAW public static int SelectionGrid (int selected, Texture[] images, int xCount, GUIStyle style, params GUILayoutOption[] options) { return SelectionGrid (selected, GUIContent.Temp (images), xCount, style, options); }
+ // Make a Selection Grid
+ CSRAW public static int SelectionGrid (int selected, GUIContent[] contents, int xCount, GUIStyle style, params GUILayoutOption[] options) {
+ return GUI.SelectionGrid (GUIGridSizer.GetRect (contents, xCount, style, options), selected, contents, xCount, style);
+ }
+
+ /// *listonly*
+ static public float HorizontalSlider (float value, float leftValue, float rightValue, params GUILayoutOption[] options)
+ { return DoHorizontalSlider(value, leftValue, rightValue, GUI.skin.horizontalSlider, GUI.skin.horizontalSliderThumb, options); }
+ // A horizontal slider the user can drag to change a value between a min and a max.
+ static public float HorizontalSlider (float value, float leftValue, float rightValue, GUIStyle slider, GUIStyle thumb, params GUILayoutOption[] options)
+ { return DoHorizontalSlider(value, leftValue, rightValue, slider, thumb, options); }
+ static float DoHorizontalSlider (float value, float leftValue, float rightValue, GUIStyle slider, GUIStyle thumb, GUILayoutOption[] options)
+ { return GUI.HorizontalSlider(GUILayoutUtility.GetRect (GUIContent.Temp ("mmmm"), slider, options), value, leftValue, rightValue, slider, thumb); }
+
+ /// *listonly*
+ static public float VerticalSlider (float value, float leftValue, float rightValue, params GUILayoutOption[] options)
+ { return DoVerticalSlider(value, leftValue, rightValue, GUI.skin.verticalSlider, GUI.skin.verticalSliderThumb, options); }
+ // A vertical slider the user can drag to change a value between a min and a max.
+ static public float VerticalSlider (float value, float leftValue, float rightValue, GUIStyle slider, GUIStyle thumb, params GUILayoutOption[] options)
+ { return DoVerticalSlider(value, leftValue, rightValue, slider, thumb, options); }
+ static float DoVerticalSlider (float value, float leftValue, float rightValue, GUIStyle slider, GUIStyle thumb, params GUILayoutOption[] options)
+ { return GUI.VerticalSlider(GUILayoutUtility.GetRect (GUIContent.Temp ("\n\n\n\n\n"), slider, options), value, leftValue, rightValue, slider, thumb); }
+
+
+ /// *listonly*
+ CSRAW public static float HorizontalScrollbar (float value, float size, float leftValue, float rightValue, params GUILayoutOption[] options)
+ { return HorizontalScrollbar (value, size, leftValue, rightValue, GUI.skin.horizontalScrollbar, options); }
+ // Make a horiztonal scrollbar.
+ CSRAW public static float HorizontalScrollbar (float value, float size, float leftValue, float rightValue, GUIStyle style, params GUILayoutOption[] options)
+ { return GUI.HorizontalScrollbar (GUILayoutUtility.GetRect (GUIContent.Temp ("mmmm"), style, options), value, size, leftValue, rightValue, style); }
+
+
+
+ /// *listonly*
+ CSRAW public static float VerticalScrollbar (float value, float size, float topValue, float bottomValue, params GUILayoutOption[] options)
+ { return VerticalScrollbar (value, size, topValue, bottomValue, GUI.skin.verticalScrollbar, options); }
+ // Make a vertical scrollbar.
+ CSRAW public static float VerticalScrollbar (float value, float size, float topValue, float bottomValue, GUIStyle style, params GUILayoutOption[] options)
+ { return GUI.VerticalScrollbar (GUILayoutUtility.GetRect (GUIContent.Temp ("\n\n\n\n"), style, options), value, size, topValue, bottomValue, style); }
+
+ // Insert a space in the current layout group.
+ CSRAW static public void Space (float pixels) {
+ GUIUtility.CheckOnGUI();
+ if (GUILayoutUtility.current.topLevel.isVertical)
+ GUILayoutUtility.GetRect (0, pixels, GUILayoutUtility.spaceStyle, GUILayout.Height (pixels));
+ else
+ GUILayoutUtility.GetRect (pixels, 0, GUILayoutUtility.spaceStyle, GUILayout.Width (pixels));
+ }
+
+ // Insert a flexible space element.
+ CSRAW static public void FlexibleSpace () {
+ GUIUtility.CheckOnGUI();
+ GUILayoutOption op;
+ if (GUILayoutUtility.current.topLevel.isVertical)
+ op = ExpandHeight (true);
+ else
+ op = ExpandWidth (true);
+
+ op.value = 10000;
+ GUILayoutUtility.GetRect (0,0, GUILayoutUtility.spaceStyle, op);
+ }
+
+
+
+
+ /// *listonly*
+ CSRAW public static void BeginHorizontal (params GUILayoutOption[] options)
+ { BeginHorizontal (GUIContent.none, GUIStyle.none, options); }
+ /// *listonly*
+ CSRAW public static void BeginHorizontal (GUIStyle style, params GUILayoutOption[] options)
+ { BeginHorizontal (GUIContent.none, style, options); }
+ /// *listonly*
+ CSRAW public static void BeginHorizontal (string text, GUIStyle style, params GUILayoutOption[] options)
+ { BeginHorizontal (GUIContent.Temp(text), style, options); }
+ /// *listonly*
+ CSRAW public static void BeginHorizontal (Texture image, GUIStyle style, params GUILayoutOption[] options)
+ { BeginHorizontal (GUIContent.Temp(image), style, options); }
+ // Begin a Horizontal control group.
+ CSRAW public static void BeginHorizontal (GUIContent content, GUIStyle style, params GUILayoutOption[] options) {
+ GUILayoutGroup g = GUILayoutUtility.BeginLayoutGroup (style, options, typeof (GUILayoutGroup));
+ g.isVertical = false;
+ if (style != GUIStyle.none || content != GUIContent.none)
+ GUI.Box (g.rect, content, style);
+ }
+
+ // Close a group started with BeginHorizontal
+ CSRAW public static void EndHorizontal () {
+ GUILayoutUtility.EndGroup ("GUILayout.EndHorizontal");
+ GUILayoutUtility.EndLayoutGroup ();
+ }
+
+ /// *listonly*
+ CSRAW public static void BeginVertical (params GUILayoutOption[] options)
+ { BeginVertical (GUIContent.none, GUIStyle.none, options); }
+ /// *listonly*
+ CSRAW public static void BeginVertical (GUIStyle style, params GUILayoutOption[] options)
+ { BeginVertical (GUIContent.none, style, options); }
+ /// *listonly*
+ CSRAW public static void BeginVertical (string text, GUIStyle style, params GUILayoutOption[] options)
+ { BeginVertical (GUIContent.Temp(text), style, options); }
+ /// *listonly*
+ CSRAW public static void BeginVertical (Texture image, GUIStyle style, params GUILayoutOption[] options)
+ { BeginVertical (GUIContent.Temp(image), style, options); }
+ // Begin a vertical control group.
+ CSRAW public static void BeginVertical (GUIContent content, GUIStyle style, params GUILayoutOption[] options) {
+ GUILayoutGroup g = GUILayoutUtility.BeginLayoutGroup (style, options, typeof (GUILayoutGroup));
+ g.isVertical = true;
+ if (style != GUIStyle.none)
+ GUI.Box (g.rect, content, style);
+ }
+
+ // Close a group started with BeginVertical
+ CSRAW public static void EndVertical () {
+ GUILayoutUtility.EndGroup ("GUILayout.EndVertical");
+ GUILayoutUtility.EndLayoutGroup ();
+ }
+
+
+ /// *listonly*
+ CSRAW static public void BeginArea (Rect screenRect) { BeginArea (screenRect, GUIContent.none, GUIStyle.none); }
+ /// *listonly*
+ CSRAW static public void BeginArea (Rect screenRect, string text) { BeginArea (screenRect, GUIContent.Temp (text), GUIStyle.none); }
+ /// *listonly*
+ CSRAW static public void BeginArea (Rect screenRect, Texture image) { BeginArea (screenRect, GUIContent.Temp (image), GUIStyle.none); }
+ /// *listonly*
+ CSRAW static public void BeginArea (Rect screenRect, GUIContent content) { BeginArea (screenRect, GUIContent.none, GUIStyle.none); }
+ /// *listonly*
+ CSRAW static public void BeginArea (Rect screenRect, GUIStyle style) { BeginArea (screenRect, GUIContent.none, style); }
+ /// *listonly*
+ CSRAW static public void BeginArea (Rect screenRect, string text, GUIStyle style) { BeginArea (screenRect, GUIContent.Temp (text), style); }
+ /// *listonly*
+ CSRAW static public void BeginArea (Rect screenRect, Texture image, GUIStyle style) { BeginArea (screenRect, GUIContent.Temp (image), style); }
+
+ // Begin a GUILayout block of GUI controls in a fixed screen area.
+ CSRAW static public void BeginArea (Rect screenRect, GUIContent content, GUIStyle style)
+ {
+ GUIUtility.CheckOnGUI ();
+ GUILayoutGroup g = GUILayoutUtility.BeginLayoutArea (style, typeof (GUILayoutGroup));
+ if (Event.current.type == EventType.Layout)
+ {
+ g.resetCoords = true;
+ g.minWidth = g.maxWidth = screenRect.width;
+ g.minHeight = g.maxHeight = screenRect.height;
+ g.rect = Rect.MinMaxRect(screenRect.xMin, screenRect.yMin, g.rect.xMax, g.rect.yMax);
+ }
+
+ GUI.BeginGroup (g.rect, content, style);
+ }
+
+ // Close a GUILayout block started with BeginArea
+ CSRAW static public void EndArea () {
+ GUIUtility.CheckOnGUI ();
+ if (Event.current.type == EventType.Used)
+ return;
+ GUILayoutUtility.current.layoutGroups.Pop ();
+ GUILayoutUtility.current.topLevel = (GUILayoutGroup)GUILayoutUtility.current.layoutGroups.Peek ();
+ GUI.EndGroup ();
+ }
+
+
+// ====================================================== SCROLLVIEWS ===============================
+ /// *listonly*
+ CSRAW public static Vector2 BeginScrollView (Vector2 scrollPosition, params GUILayoutOption[] options) { return BeginScrollView (scrollPosition, false, false, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, GUI.skin.scrollView, options); }
+ /// *listonly*
+ CSRAW public static Vector2 BeginScrollView (Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical, params GUILayoutOption[] options) { return BeginScrollView (scrollPosition, alwaysShowHorizontal, alwaysShowVertical, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, GUI.skin.scrollView, options); }
+ /// *listonly*
+ CSRAW public static Vector2 BeginScrollView (Vector2 scrollPosition, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, params GUILayoutOption[] options) { return BeginScrollView (scrollPosition, false, false, horizontalScrollbar, verticalScrollbar, GUI.skin.scrollView, options); }
+
+ // We need to keep both of the following versions of BeginScrollView() since it was added with a different signature in 3.5
+ // Adding an optional argument with params unfortunately *does* change the function signature
+ /// *listonly*
+ CSRAW public static Vector2 BeginScrollView (Vector2 scrollPosition, GUIStyle style)
+ {
+ GUILayoutOption[] option = null;
+ return BeginScrollView (scrollPosition, style, option);
+ }
+ /// *listonly*
+ CSRAW public static Vector2 BeginScrollView (Vector2 scrollPosition, GUIStyle style, params GUILayoutOption[] options)
+ {
+ string name = style.name;
+
+ GUIStyle vertical = GUI.skin.FindStyle (name + "VerticalScrollbar");
+ if (vertical == null)
+ vertical = GUI.skin.verticalScrollbar;
+ GUIStyle horizontal = GUI.skin.FindStyle (name + "HorizontalScrollbar");
+ if (horizontal == null)
+ horizontal = GUI.skin.horizontalScrollbar;
+ return BeginScrollView (scrollPosition, false, false, horizontal, vertical, style, options);
+ }
+
+ /// *listonly*
+ CSRAW public static Vector2 BeginScrollView (Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, params GUILayoutOption[] options)
+ { return BeginScrollView (scrollPosition, alwaysShowHorizontal, alwaysShowVertical, horizontalScrollbar, verticalScrollbar, GUI.skin.scrollView, options); }
+
+ // Begin an automatically laid out scrollview.
+ CSRAW public static Vector2 BeginScrollView (Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options) {
+ GUIUtility.CheckOnGUI();
+
+ GUIScrollGroup g = (GUIScrollGroup)GUILayoutUtility.BeginLayoutGroup (background, null, typeof (GUIScrollGroup));
+ switch (Event.current.type) {
+ case EventType.Layout:
+ g.resetCoords = true;
+ g.isVertical = true;
+ g.stretchWidth = 1;
+ g.stretchHeight = 1;
+ g.verticalScrollbar = verticalScrollbar;
+ g.horizontalScrollbar = horizontalScrollbar;
+ g.needsVerticalScrollbar = alwaysShowVertical;
+ g.needsHorizontalScrollbar = alwaysShowHorizontal;
+ g.ApplyOptions (options);
+ break;
+ default:
+ break;
+ }
+ return GUI.BeginScrollView (g.rect, scrollPosition, new Rect (0,0,g.clientWidth,g.clientHeight), alwaysShowHorizontal, alwaysShowVertical, horizontalScrollbar, verticalScrollbar, background);
+ }
+ // End a scroll view begun with a call to BeginScrollView.
+ public static void EndScrollView () {
+ EndScrollView (true);
+ }
+
+
+ internal static void EndScrollView (bool handleScrollWheel) {
+ GUILayoutUtility.EndGroup ("GUILayout.EndScrollView");
+ GUILayoutUtility.EndLayoutGroup ();
+ GUI.EndScrollView (handleScrollWheel);
+ }
+
+ /// *listonly*
+ CSRAW public static Rect Window (int id, Rect screenRect, GUI.WindowFunction func, string text, params GUILayoutOption[ ] options) { return DoWindow (id, screenRect, func, GUIContent.Temp (text), GUI.skin.window, options); }
+ /// *listonly*
+ CSRAW public static Rect Window (int id, Rect screenRect, GUI.WindowFunction func, Texture image, params GUILayoutOption[ ] options) { return DoWindow (id, screenRect, func, GUIContent.Temp (image), GUI.skin.window, options); }
+ /// *listonly*
+ CSRAW public static Rect Window (int id, Rect screenRect, GUI.WindowFunction func, GUIContent content, params GUILayoutOption[ ] options) { return DoWindow (id, screenRect, func, content, GUI.skin.window, options); }
+ /// *listonly*
+ CSRAW public static Rect Window (int id, Rect screenRect, GUI.WindowFunction func, string text, GUIStyle style, params GUILayoutOption[ ] options) { return DoWindow (id, screenRect, func, GUIContent.Temp (text), style, options); }
+ /// *listonly*
+ CSRAW public static Rect Window (int id, Rect screenRect, GUI.WindowFunction func, Texture image, GUIStyle style, params GUILayoutOption[ ] options) { return DoWindow (id, screenRect, func, GUIContent.Temp (image), style, options); }
+
+ // Make a popup window that layouts its contents automatically.
+ CSRAW public static Rect Window (int id, Rect screenRect, GUI.WindowFunction func, GUIContent content, GUIStyle style, params GUILayoutOption[ ] options) { return DoWindow (id, screenRect, func, content, style, options); }
+ // Make an auto-sized draggable window...
+ CSRAW static Rect DoWindow (int id, Rect screenRect, GUI.WindowFunction func, GUIContent content, GUIStyle style, GUILayoutOption[ ] options) {
+ GUIUtility.CheckOnGUI ();
+ LayoutedWindow lw = new LayoutedWindow (func, screenRect, content, options, style);
+ return GUI.Window (id, screenRect, lw.DoWindow, content, style);
+ }
+
+ CLASS private LayoutedWindow
+ CSRAW
+ GUI.WindowFunction func;
+ Rect screenRect;
+ GUILayoutOption[ ] options;
+ GUIStyle style;
+ //*undocumented*
+ internal LayoutedWindow (GUI.WindowFunction f, Rect _screenRect, GUIContent _content, GUILayoutOption[ ] _options, GUIStyle _style) {
+ func = f;
+ screenRect = _screenRect;
+ options = _options;
+ style = _style;
+ }
+
+ //*undocumented*
+ public void DoWindow (int windowID) {
+ GUILayoutGroup g = GUILayoutUtility.current.topLevel;
+
+ switch (Event.current.type) {
+ case EventType.Layout:
+ // TODO: Add layoutoptions
+ // TODO: Take titlebar size into consideration
+ g.resetCoords = true;
+ g.rect = screenRect;
+ if (options != null)
+ g.ApplyOptions (options);
+ g.isWindow = true;
+ g.windowID = windowID;
+ g.style = style;
+ break;
+ default:
+ g.ResetCursor ();
+ break;
+ }
+ func (windowID);
+ }
+ END
+
+ // Option passed to a control to give it an absolute width.
+ CSRAW static public GUILayoutOption Width (float width) { return new GUILayoutOption (GUILayoutOption.Type.fixedWidth, width); }
+ // Option passed to a control to specify a minimum width.\\
+ CSRAW static public GUILayoutOption MinWidth (float minWidth) { return new GUILayoutOption (GUILayoutOption.Type.minWidth, minWidth); }
+ // Option passed to a control to specify a maximum width.
+ CSRAW static public GUILayoutOption MaxWidth (float maxWidth) { return new GUILayoutOption (GUILayoutOption.Type.maxWidth, maxWidth); }
+ // Option passed to a control to give it an absolute height.
+ CSRAW static public GUILayoutOption Height (float height) { return new GUILayoutOption (GUILayoutOption.Type.fixedHeight, height); }
+
+ // Option passed to a control to specify a minimum height.
+ CSRAW static public GUILayoutOption MinHeight (float minHeight) { return new GUILayoutOption (GUILayoutOption.Type.minHeight, minHeight); }
+
+ // Option passed to a control to specify a maximum height.
+ CSRAW static public GUILayoutOption MaxHeight (float maxHeight) { return new GUILayoutOption (GUILayoutOption.Type.maxHeight, maxHeight); }
+
+ // Option passed to a control to allow or disallow horizontal expansion.
+ CSRAW static public GUILayoutOption ExpandWidth (bool expand) { return new GUILayoutOption (GUILayoutOption.Type.stretchWidth, expand ? 1 : 0); }
+ // Option passed to a control to allow or disallow vertical expansion.
+ CSRAW static public GUILayoutOption ExpandHeight (bool expand) { return new GUILayoutOption (GUILayoutOption.Type.stretchHeight, expand ? 1 : 0); }
+END
+
+CSRAW
+
+}
diff --git a/Runtime/Export/GUILayoutUtility.txt b/Runtime/Export/GUILayoutUtility.txt
new file mode 100644
index 0000000..df94ffa
--- /dev/null
+++ b/Runtime/Export/GUILayoutUtility.txt
@@ -0,0 +1,1420 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/IMGUI/GUIWindows.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+using namespace Unity;
+using namespace std;
+
+CSRAW
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace UnityEngine
+{
+
+// Utility functions for implementing and extending the GUILayout class.
+NONSEALED_CLASS GUILayoutUtility
+CSRAW
+
+ CLASS internal LayoutCache
+ CSRAW
+ //*undocumented*
+ internal GUILayoutGroup topLevel = new GUILayoutGroup ();
+ //*undocumented*
+ internal UnityEngineInternal.GenericStack layoutGroups = new UnityEngineInternal.GenericStack ();
+ //*undocumented*
+ internal GUILayoutGroup windows = new GUILayoutGroup ();
+
+ //*undocumented*
+ internal LayoutCache () {
+ layoutGroups.Push (topLevel);
+ }
+
+ internal LayoutCache (LayoutCache other)
+ {
+ topLevel = other.topLevel;
+ layoutGroups = other.layoutGroups;
+ windows = other.windows;
+ }
+ END
+
+ // TODO: Clean these up after a while
+ static Dictionary<int, LayoutCache> storedLayouts = new Dictionary<int, LayoutCache> ();
+ static Dictionary<int, LayoutCache> storedWindows = new Dictionary<int, LayoutCache> ();
+
+ static internal LayoutCache current = new LayoutCache ();
+
+ static Rect kDummyRect = new Rect (0, 0, 1, 1);
+
+ CSRAW static internal LayoutCache SelectIDList (int instanceID, bool isWindow) {
+ Dictionary<int, LayoutCache> store = isWindow ? storedWindows : storedLayouts;
+ LayoutCache cache;
+ if (store.TryGetValue(instanceID, out cache) == false) {
+// Debug.Log ("Creating ID " +instanceID + " " + Event.current.type);
+ cache = new LayoutCache();
+ store[instanceID] = cache;
+ } else {
+// Debug.Log ("reusing ID " +instanceID + " " + Event.current.type);
+ }
+ current.topLevel = cache.topLevel;
+ current.layoutGroups = cache.layoutGroups;
+ current.windows = cache.windows;
+ return cache;
+ }
+
+ // Set up the internal GUILayouting
+ // Called by the main GUI class automatically (from GUI.Begin)
+ CSRAW static internal void Begin (int instanceID) {
+ LayoutCache cache = SelectIDList (instanceID, false);
+ // Make a vertical group to encompass the whole thing
+ if (Event.current.type == EventType.Layout) {
+ current.topLevel = cache.topLevel = new GUILayoutGroup ();
+ current.layoutGroups.Clear ();
+ current.layoutGroups.Push (current.topLevel);
+ current.windows = cache.windows = new GUILayoutGroup ();
+ } else {
+ current.topLevel = cache.topLevel;
+ current.layoutGroups = cache.layoutGroups;
+ current.windows = cache.windows;
+ }
+ }
+
+ CSRAW static internal void BeginWindow (int windowID, GUIStyle style, GUILayoutOption[] options) {
+ LayoutCache cache = SelectIDList (windowID, true);
+ // Make a vertical group to encompass the whole thing
+ if (Event.current.type == EventType.Layout) {
+ current.topLevel = cache.topLevel = new GUILayoutGroup ();
+ current.topLevel.style = style;
+ current.topLevel.windowID = windowID;
+ if (options != null)
+ current.topLevel.ApplyOptions (options);
+ current.layoutGroups.Clear ();
+ current.layoutGroups.Push (current.topLevel);
+ current.windows = cache.windows = new GUILayoutGroup ();
+ } else {
+ current.topLevel = cache.topLevel;
+ current.layoutGroups = cache. layoutGroups;
+ current.windows = cache.windows;
+ }
+ }
+
+ // TODO: actually make these check...
+ // *undocumented*
+ CSRAW static public void BeginGroup (string GroupName) {}
+ // *undocumented*
+ CSRAW static public void EndGroup (string groupName) {}
+
+
+ static internal void Layout () {
+ if (current.topLevel.windowID == -1) {
+ // Normal GUILayout.whatever -outside beginArea calls.
+ // Here we go over all entries and calculate their sizes
+ current.topLevel.CalcWidth ();
+ current.topLevel.SetHorizontal (0, Mathf.Min (Screen.width, current.topLevel.maxWidth));
+ current.topLevel.CalcHeight ();
+ current.topLevel.SetVertical (0, Mathf.Min (Screen.height, current.topLevel.maxHeight));
+
+// UNCOMMENT ME TO DEBUG THE ROOT LAYOUT RESULTS
+// Debug.Log ("ROOT: " + current.topLevel);
+ // Layout all beginarea parts...
+ LayoutFreeGroup (current.windows);
+ } else {
+ LayoutSingleGroup (current.topLevel);
+ LayoutFreeGroup (current.windows);
+// Debug.Log ("Windows: " + current.windows);
+ }
+ }
+
+ // Global fayout function. Called from EditorWindows (differs from game view in that they use the full window size and try to stretch GUI
+ // *undocumented*
+ static internal void LayoutFromEditorWindow () {
+ current.topLevel.CalcWidth ();
+ current.topLevel.SetHorizontal (0, Screen.width);
+ current.topLevel.CalcHeight ();
+ current.topLevel.SetVertical (0, Screen.height);
+
+// UNCOMMENT ME TO DEBUG THE EditorWindow ROOT LAYOUT RESULTS
+// Debug.Log (current.topLevel);
+ // Layout all beginarea parts...
+ LayoutFreeGroup (current.windows);
+ }
+
+
+ // Global layout function. Calculates all sizes of all windows etc & assigns.
+ // After this call everything has a properly calculated size
+ // Called by Unity automatically.
+ // Is public so we can access it from editor inspectors, but not supported by public stuff
+ // *undocumented*
+ static internal float LayoutFromInspector (float width) {
+ if (current.topLevel != null && current.topLevel.windowID == -1) {
+ // Here we go over all entries and calculate their sizes
+ current.topLevel.CalcWidth ();
+ current.topLevel.SetHorizontal (0, width);
+ current.topLevel.CalcHeight ();
+ current.topLevel.SetVertical (0, Mathf.Min (Screen.height, current.topLevel.maxHeight));
+// UNCOMMENT ME TO DEBUG THE INSPECTOR
+// Debug.Log (current.topLevel);
+ float height = ((GUILayoutGroup)current.topLevel).minHeight;
+ // Layout all beginarea parts...
+ // TODO: NOT SURE HOW THIS WORKS IN AN INSPECTOR
+ LayoutFreeGroup (current.windows);
+ return height;
+ }
+ else {
+ if (current.topLevel != null)
+ LayoutSingleGroup (current.topLevel);
+ return 0;
+ }
+ }
+
+ static internal void LayoutFreeGroup (GUILayoutGroup toplevel) {
+ foreach (GUILayoutGroup i in toplevel.entries) {
+ LayoutSingleGroup (i);
+ }
+ toplevel.ResetCursor ();
+ }
+
+ static void LayoutSingleGroup (GUILayoutGroup i) {
+ if (!i.isWindow) {
+ // CalcWidth knocks out minWidth with the calculated sizes from its children. Normally, this is fine, but since we're in a fixed-size area,
+ // we want to maintain that (godammit)
+ float origMinWidth = i.minWidth;
+ float origMaxWidth = i.maxWidth;
+
+ // Figure out the group's min & maxWidth.
+ i.CalcWidth ();
+
+ // Make it as wide as possible, but the Rect supplied takes precedence...
+ i.SetHorizontal (i.rect.x, Mathf.Clamp (i.maxWidth, origMinWidth, origMaxWidth));
+
+ // Do the same preservation for CalcHeight...
+ float origMinHeight = i.minHeight;
+ float origMaxHeight = i.maxHeight;
+
+ i.CalcHeight ();
+ // Make it as high as possible, but the Rect supplied takes precedence...
+ i.SetVertical (i.rect.y, Mathf.Clamp (i.maxHeight, origMinHeight, origMaxHeight));
+
+// UNCOMMENT ME TO SEE BEGINAREA/ENDAREA BLOCKS
+// Debug.Log (i);
+ } else {
+ // Figure out the group's min & maxWidth.
+ i.CalcWidth ();
+
+ Rect winRect = Internal_GetWindowRect (i.windowID);
+
+ // Make it as wide as possible, but the Rect supplied takes precedence...
+ i.SetHorizontal (winRect.x, Mathf.Clamp (winRect.width, i.minWidth, i.maxWidth));
+
+ i.CalcHeight ();
+
+ // Make it as high as possible, but the Rect supplied takes precedence...
+ i.SetVertical (winRect.y, Mathf.Clamp (winRect.height, i.minHeight, i.maxHeight));
+
+ // If GUILayout did any resizing, make sure the window reflects this.
+
+ Internal_MoveWindow (i.windowID, i.rect);
+ }
+ }
+
+ CUSTOM static private Rect Internal_GetWindowRect (int windowID)
+ {
+ return IMGUI::GetWindowRect (GetGUIState (), windowID);
+ }
+
+ CUSTOM static private void Internal_MoveWindow (int windowID, Rect r)
+ {
+ IMGUI::MoveWindowFromLayout (GetGUIState (), windowID, r);
+ }
+
+ CUSTOM static internal Rect GetWindowsBounds ()
+ {
+ return IMGUI::GetWindowsBounds (GetGUIState ());
+ }
+
+ CSRAW
+ [System.Security.SecuritySafeCritical]
+ static GUILayoutGroup CreateGUILayoutGroupInstanceOfType(System.Type LayoutType)
+ {
+ if (!typeof(GUILayoutGroup).IsAssignableFrom(LayoutType))
+ throw new ArgumentException("LayoutType needs to be of type GUILayoutGroup");
+ return (GUILayoutGroup)System.Activator.CreateInstance (LayoutType);
+ }
+
+ // Generic helper - use this when creating a layoutgroup. It will make sure everything is wired up correctly.
+ internal static GUILayoutGroup BeginLayoutGroup (GUIStyle style, GUILayoutOption[] options, System.Type LayoutType) {
+ GUILayoutGroup g;
+ switch (Event.current.type) {
+ case EventType.Used:
+ case EventType.Layout:
+ g = CreateGUILayoutGroupInstanceOfType (LayoutType);
+ g.style = style;
+ if (options != null)
+ g.ApplyOptions (options);
+ current.topLevel.Add (g);
+ break;
+ default:
+ g = current.topLevel.GetNext() as GUILayoutGroup;
+ if (g == null)
+ throw new ArgumentException("GUILayout: Mismatched LayoutGroup." + Event.current.type);
+ g.ResetCursor ();
+ break;
+ }
+ current.layoutGroups.Push (g);
+ current.topLevel = g;
+ return g;
+ }
+
+ // The matching end for BeginLayoutGroup
+ internal static void EndLayoutGroup () {
+ switch (Event.current.type) {
+ default:
+ current.layoutGroups.Pop ();
+ current.topLevel = (GUILayoutGroup)current.layoutGroups.Peek ();
+ return;
+ }
+ }
+
+ // Generic helper - use this when creating a layoutgroup. It will make sure everything is wired up correctly.
+ internal static GUILayoutGroup BeginLayoutArea (GUIStyle style, System.Type LayoutType) {
+ GUILayoutGroup g;
+ switch (Event.current.type) {
+ case EventType.Used:
+ case EventType.Layout:
+ g = CreateGUILayoutGroupInstanceOfType (LayoutType);
+ g.style = style;
+ current.windows.Add (g);
+ break;
+ default:
+ g = current.windows.GetNext() as GUILayoutGroup;
+ if (g == null)
+ throw new ArgumentException("GUILayout: Mismatched LayoutGroup." + Event.current.type);
+ g.ResetCursor ();
+ break;
+ }
+ current.layoutGroups.Push (g);
+ current.topLevel = g;
+ return g;
+ }
+
+ // Trampoline for Editor stuff
+ //*undocumented*
+ internal static GUILayoutGroup DoBeginLayoutArea (GUIStyle style, System.Type LayoutType) {
+ return BeginLayoutArea (style, LayoutType);
+ }
+ internal static GUILayoutGroup topLevel {
+ get { return current.topLevel; }
+ }
+
+
+ /// *listonly*
+ CSRAW public static Rect GetRect (GUIContent content, GUIStyle style) { return DoGetRect (content, style, null); }
+ // Reserve layout space for a rectangle for displaying some contents with a specific style.
+ CSRAW public static Rect GetRect (GUIContent content, GUIStyle style, params GUILayoutOption[] options) { return DoGetRect (content, style, options); }
+ CSRAW static Rect DoGetRect (GUIContent content, GUIStyle style, GUILayoutOption[] options) {
+ GUIUtility.CheckOnGUI();
+
+ switch (Event.current.type) {
+ case EventType.Layout: {
+ if (style.isHeightDependantOnWidth) {
+ current.topLevel.Add (new GUIWordWrapSizer (style, content, options));
+ } else {
+ Vector2 size = style.CalcSize (content);
+ current.topLevel.Add (new GUILayoutEntry (size.x, size.x, size.y, size.y, style, options));
+ }
+ return kDummyRect;
+ }
+ case EventType.Used:
+ return kDummyRect;
+ default:
+ return current.topLevel.GetNext ().rect;
+ }
+ }
+
+ /// *listonly*
+ CSRAW public static Rect GetRect (float width, float height) { return DoGetRect (width, width, height, height, GUIStyle.none, null);}
+ /// *listonly*
+ CSRAW public static Rect GetRect (float width, float height, GUIStyle style) {return DoGetRect (width, width, height, height, style, null);}
+ /// *listonly*
+ CSRAW public static Rect GetRect (float width, float height, params GUILayoutOption[] options) {return DoGetRect (width, width, height, height, GUIStyle.none, options);}
+ // Reserve layout space for a rectangle with a fixed content area.
+ CSRAW public static Rect GetRect (float width, float height, GUIStyle style, params GUILayoutOption[] options)
+ {return DoGetRect (width, width, height, height, style, options);}
+
+
+
+ /// *listonly*
+ CSRAW public static Rect GetRect (float minWidth, float maxWidth, float minHeight, float maxHeight)
+ { return DoGetRect (minWidth, maxWidth, minHeight, maxHeight, GUIStyle.none, null); }
+ /// *listonly*
+ CSRAW public static Rect GetRect (float minWidth, float maxWidth, float minHeight, float maxHeight, GUIStyle style)
+ { return DoGetRect (minWidth, maxWidth, minHeight, maxHeight, style, null); }
+ /// *listonly*
+ CSRAW public static Rect GetRect (float minWidth, float maxWidth, float minHeight, float maxHeight, params GUILayoutOption[] options)
+ { return DoGetRect (minWidth, maxWidth, minHeight, maxHeight, GUIStyle.none, options); }
+ // Reserve layout space for a flexible rect.
+ CSRAW public static Rect GetRect (float minWidth, float maxWidth, float minHeight, float maxHeight, GUIStyle style, params GUILayoutOption[] options)
+ { return DoGetRect (minWidth, maxWidth, minHeight, maxHeight, style, options); }
+ CSRAW static Rect DoGetRect (float minWidth, float maxWidth, float minHeight, float maxHeight, GUIStyle style, GUILayoutOption[] options) {
+ switch (Event.current.type) {
+ case EventType.Layout:
+ current.topLevel.Add (new GUILayoutEntry (minWidth, maxWidth, minHeight, maxHeight, style, options));
+ return kDummyRect;
+ case EventType.Used:
+ return kDummyRect;
+ default:
+ return current.topLevel.GetNext ().rect;
+ }
+ }
+
+ // Get the rectangle last used by GUILayout for a control.
+ CSRAW public static Rect GetLastRect () {
+ switch (Event.current.type) {
+ case EventType.Layout:
+ return kDummyRect;
+ case EventType.Used:
+ return kDummyRect;
+ default:
+ return current.topLevel.GetLast ();
+ }
+ }
+
+ /// *listonly*
+ CSRAW public static Rect GetAspectRect (float aspect) { return DoGetAspectRect (aspect, GUIStyle.none, null); }
+ /// *listonly*
+ CSRAW public static Rect GetAspectRect (float aspect, GUIStyle style) { return DoGetAspectRect (aspect, style, null); }
+ /// *listonly*
+ CSRAW public static Rect GetAspectRect (float aspect, params GUILayoutOption[] options) { return DoGetAspectRect (aspect, GUIStyle.none, options); }
+ // Reserve layout space for a rectangle with a specific aspect ratio.
+ CSRAW public static Rect GetAspectRect (float aspect, GUIStyle style, params GUILayoutOption[] options) { return DoGetAspectRect (aspect, GUIStyle.none, options); }
+ static Rect DoGetAspectRect (float aspect, GUIStyle style, GUILayoutOption[] options) {
+ switch (Event.current.type) {
+ case EventType.Layout:
+ current.topLevel.Add (new GUIAspectSizer (aspect, options));
+ return kDummyRect;
+ case EventType.Used:
+ return kDummyRect;
+ default:
+ return current.topLevel.GetNext ().rect;
+ }
+ }
+
+ // Style used by space elements so we can do special handling of spaces.
+ CSRAW internal static GUIStyle spaceStyle
+ {
+ get
+ {
+ if (s_SpaceStyle == null) s_SpaceStyle = new GUIStyle();
+ s_SpaceStyle.stretchWidth = false;
+ return s_SpaceStyle;
+ }
+ }
+ static GUIStyle s_SpaceStyle;
+END
+
+
+// Basic layout element
+NONSEALED_CLASS internal GUILayoutEntry
+ CSRAW
+ // The min and max sizes. Used during calculations...
+ CSRAW public float minWidth, maxWidth, minHeight, maxHeight;
+
+ // The rectangle that this element ends up having
+ CSRAW public Rect rect = new Rect (0,0,0,0);
+
+ // Can this element stretch?
+ CSRAW public int stretchWidth, stretchHeight;
+
+ // The style to use.
+ CSRAW GUIStyle m_Style = GUIStyle.none;
+ CSRAW public GUIStyle style { get { return m_Style; } set { m_Style = value; ApplyStyleSettings (value); } }
+
+ CSRAW internal static Rect kDummyRect = new Rect (0,0,1,1);
+
+ // The margins of this element.
+ public virtual RectOffset margin { get {
+ return style.margin;
+ }}
+
+ public GUILayoutEntry (float _minWidth, float _maxWidth, float _minHeight, float _maxHeight, GUIStyle _style) {
+ minWidth = _minWidth;
+ maxWidth = _maxWidth;
+ minHeight = _minHeight;
+ maxHeight = _maxHeight;
+ if (_style == null)
+ _style = GUIStyle.none;
+ style = _style;
+ }
+
+ public GUILayoutEntry (float _minWidth, float _maxWidth, float _minHeight, float _maxHeight, GUIStyle _style, GUILayoutOption[] options) {
+ minWidth = _minWidth;
+ maxWidth = _maxWidth;
+ minHeight = _minHeight;
+ maxHeight = _maxHeight;
+ style = _style;
+ ApplyOptions (options);
+ }
+
+ public virtual void CalcWidth () {}
+ public virtual void CalcHeight () {}
+ public virtual void SetHorizontal (float x, float width) { rect.x = x; rect.width = width; }
+ public virtual void SetVertical (float y, float height) { rect.y = y; rect.height = height; }
+
+ protected virtual void ApplyStyleSettings (GUIStyle style) {
+ stretchWidth = (style.fixedWidth == 0 && style.stretchWidth) ? 1 : 0;
+ stretchHeight = (style.fixedHeight == 0 && style.stretchHeight) ? 1 : 0;
+ m_Style = style;
+ }
+
+ public virtual void ApplyOptions (GUILayoutOption[] options) {
+ if (options == null)
+ return;
+ foreach (GUILayoutOption i in options) {
+ switch (i.type) {
+ case GUILayoutOption.Type.fixedWidth: minWidth = maxWidth = (float)i.value; stretchWidth = 0; break;
+ case GUILayoutOption.Type.fixedHeight: minHeight = maxHeight = (float)i.value; stretchHeight = 0; break;
+ case GUILayoutOption.Type.minWidth: minWidth = (float)i.value; if (maxWidth < minWidth) maxWidth = minWidth; break;
+ case GUILayoutOption.Type.maxWidth: maxWidth = (float)i.value; if (minWidth > maxWidth) minWidth = maxWidth; stretchWidth = 0; break;
+ case GUILayoutOption.Type.minHeight: minHeight = (float)i.value; if (maxHeight < minHeight) maxHeight = minHeight; break;
+ case GUILayoutOption.Type.maxHeight: maxHeight = (float)i.value; if (minHeight > maxHeight) minHeight = maxHeight; stretchHeight = 0; break;
+ case GUILayoutOption.Type.stretchWidth: stretchWidth = (int)i.value; break;
+ case GUILayoutOption.Type.stretchHeight: stretchHeight = (int)i.value; break;
+ }
+ }
+ if (maxWidth != 0 && maxWidth < minWidth)
+ maxWidth = minWidth;
+ if (maxHeight != 0 && maxHeight < minHeight)
+ maxHeight = minHeight;
+
+ }
+
+ protected static int indent = 0;
+ public override string ToString () {
+ string space = "";
+ for (int i = 0; i < indent; i++)
+ space += " ";
+ return space + UnityString.Format ("{1}-{0} (x:{2}-{3}, y:{4}-{5})", style != null ? style.name : "NULL", GetType(), rect.x, rect.xMax, rect.y, rect.yMax) +
+ " - W: " + minWidth + "-" + maxWidth + (stretchWidth != 0 ? "+" : "") + ", H: " + minHeight + "-" + maxHeight + (stretchHeight != 0 ? "+" : "");
+ }
+END
+
+// *undocumented*
+NONSEALED_CLASS internal GUILayoutGroup : GUILayoutEntry
+ CSRAW
+ public List<GUILayoutEntry> entries = new List<GUILayoutEntry>();
+ public bool isVertical = true; // Is this group vertical
+ public bool resetCoords = false; // Reset coordinate for GetRect. Used for groups that are part of a window
+ public float spacing = 0; // Spacing between the elements contained within
+ public bool sameSize = true; // Are all subelements the same size
+ public bool isWindow = false; // Is this a window at all?
+ public int windowID = -1; // Optional window ID for toplevel windows. Used by Layout to tell GUI.Window of size changes...
+ int cursor = 0;
+ protected int stretchableCountX = 100, stretchableCountY = 100;
+ protected bool userSpecifiedWidth = false, userSpecifiedHeight = false;
+ // Should all elements be the same size?
+ // TODO: implement
+// CSRAW bool equalSize = false;
+
+ // The summed sizes of the children. This is used to determine whether or not the children should be stretched
+ CSRAW protected float childMinWidth = 100, childMaxWidth = 100, childMinHeight = 100, childMaxHeight = 100;
+
+ // How are subelements justified along the minor direction?
+ // TODO: implement
+// CSRAW enum Align { start, middle, end, justify }
+// CSRAW Align align;
+
+ RectOffset m_Margin = new RectOffset();
+ public override RectOffset margin { get {
+ return m_Margin;
+ }}
+
+
+ public GUILayoutGroup () : base (0,0,0,0,GUIStyle.none) {}
+
+#if !UNITY_FLASH && !UNITY_WEBGL
+ public GUILayoutGroup (GUIStyle _style, GUILayoutOption[] options) : base (0,0,0,0, _style) {
+ if (options != null)
+ ApplyOptions (options);
+ m_Margin.left = _style.margin.left;
+ m_Margin.right = _style.margin.right;
+ m_Margin.top = _style.margin.top;
+ m_Margin.bottom = _style.margin.bottom;
+ }
+#endif
+
+ public override void ApplyOptions (GUILayoutOption[] options) {
+ if (options == null)
+ return;
+ base.ApplyOptions (options);
+ foreach (GUILayoutOption i in options) {
+ switch (i.type) {
+ case GUILayoutOption.Type.fixedWidth:
+ case GUILayoutOption.Type.minWidth:
+ case GUILayoutOption.Type.maxWidth:
+ userSpecifiedHeight = true;
+ break;
+ case GUILayoutOption.Type.fixedHeight:
+ case GUILayoutOption.Type.minHeight:
+ case GUILayoutOption.Type.maxHeight:
+ userSpecifiedWidth = true;
+ break;
+// TODO:
+// case GUILayoutOption.Type.alignStart: align = Align.start; break;
+// case GUILayoutOption.Type.alignMiddle: align = Align.middle; break;
+// case GUILayoutOption.Type.alignEnd: align = Align.end; break;
+// case GUILayoutOption.Type.alignJustify: align = Align.justify; break;
+// case GUILayoutOption.Type.equalSize: equalSize = true; break;
+ case GUILayoutOption.Type.spacing: spacing = (int)i.value; break;
+ }
+ }
+ }
+
+ protected override void ApplyStyleSettings (GUIStyle style) {
+ base.ApplyStyleSettings (style);
+ RectOffset mar = style.margin;
+ m_Margin.left = mar.left;
+ m_Margin.right = mar.right;
+ m_Margin.top = mar.top;
+ m_Margin.bottom = mar.bottom;
+ }
+
+ public void ResetCursor () { cursor = 0; }
+
+ public Rect PeekNext () {
+ if(cursor < entries.Count) {
+ GUILayoutEntry e = (GUILayoutEntry)entries[cursor];
+ return e.rect;
+ } else {
+ throw new ArgumentException("Getting control " + cursor + "'s position in a group with only " + entries.Count + " controls when doing " + Event.current.rawType + "\nAborting");
+ }
+ }
+
+ public GUILayoutEntry GetNext () {
+ if(cursor < entries.Count) {
+ GUILayoutEntry e = (GUILayoutEntry)entries[cursor];
+ cursor++;
+ return e;
+ } else {
+ throw new ArgumentException("Getting control " + cursor + "'s position in a group with only " + entries.Count + " controls when doing " + Event.current.rawType + "\nAborting");
+ }
+ }
+
+ //* undocumented
+ public Rect GetLast () {
+ if (cursor == 0) {
+ Debug.LogError ("You cannot call GetLast immediately after beginning a group.");
+ return kDummyRect;
+ }
+ if(cursor <= entries.Count) {
+ GUILayoutEntry e = (GUILayoutEntry)entries[cursor - 1];
+ return e.rect;
+ } else {
+ Debug.LogError ("Getting control " + cursor + "'s position in a group with only " + entries.Count + " controls when doing " + Event.current.type);
+ return kDummyRect;
+ }
+ }
+
+
+ public void Add (GUILayoutEntry e) {
+ entries.Add (e);
+ }
+
+ CSRAW public override void CalcWidth () {
+ if (entries.Count == 0) {
+ maxWidth = minWidth = style.padding.horizontal;
+ return;
+ }
+
+ childMinWidth = 0; childMaxWidth = 0;
+ int _leftMarginMin = 0, _rightMarginMin = 0;
+ stretchableCountX = 0;
+ bool first = true;
+ if (isVertical) {
+ foreach (GUILayoutEntry i in entries) {
+ i.CalcWidth ();
+ RectOffset margins = i.margin;
+ if (i.style != GUILayoutUtility.spaceStyle) {
+ if (!first) {
+ _leftMarginMin = Mathf.Min (margins.left, _leftMarginMin);
+ _rightMarginMin = Mathf.Min (margins.right, _rightMarginMin);
+ } else {
+ _leftMarginMin = margins.left;
+ _rightMarginMin = margins.right;
+ first = false;
+ }
+ childMinWidth = Mathf.Max (i.minWidth + margins.horizontal, childMinWidth);
+ childMaxWidth = Mathf.Max (i.maxWidth + margins.horizontal, childMaxWidth);
+ }
+ stretchableCountX += i.stretchWidth;
+ }
+ // Before, we added the margins to the width, now we want to suptract them again.
+ childMinWidth -= _leftMarginMin + _rightMarginMin;
+ childMaxWidth -= _leftMarginMin + _rightMarginMin;
+
+ } else {
+ int lastMargin = 0;
+ foreach (GUILayoutEntry i in entries) {
+ i.CalcWidth ();
+ RectOffset m = i.margin;
+ int margin;
+
+ // Specialcase spaceStyle - instead of handling margins normally, we just want to insert the size...
+ // This ensure that Space(1) adds ONE space, and doesn't prevent margin collapses
+ if (i.style != GUILayoutUtility.spaceStyle) {
+ if (!first)
+ margin = lastMargin > m.left ? lastMargin : m.left;
+ else {
+ // the first element's margins are handles _leftMarginMin and should not be added to the children's sizes
+ margin = 0;
+ first = false;
+ }
+ childMinWidth += i.minWidth + spacing + margin;
+ childMaxWidth += i.maxWidth + spacing + margin;
+ lastMargin = m.right;
+ stretchableCountX += i.stretchWidth;
+ } else {
+ childMinWidth += i.minWidth;
+ childMaxWidth += i.maxWidth;
+ stretchableCountX += i.stretchWidth;
+ }
+ }
+ childMinWidth -= spacing;
+ childMaxWidth -= spacing;
+ if (entries.Count != 0) {
+ _leftMarginMin = ((GUILayoutEntry)entries[0]).margin.left;
+ _rightMarginMin = lastMargin;
+ } else {
+ _leftMarginMin = _rightMarginMin = 0;
+ }
+ }
+ // Catch the cases where we have ONLY space elements in a group
+
+ // calculated padding values.
+ float leftPadding = 0, rightPadding = 0;
+
+ // If we have a style, the margins are handled i.r.t. padding.
+ if (style != GUIStyle.none || userSpecifiedWidth) {
+ // Add the padding of this group to the total min & max widths
+ leftPadding = Mathf.Max (style.padding.left, _leftMarginMin);
+ rightPadding = Mathf.Max (style.padding.right, _rightMarginMin);
+ }
+ else {
+ // If we don't have a GUIStyle, we pop the min of margins outward from children on to us.
+ m_Margin.left = _leftMarginMin;
+ m_Margin.right = _rightMarginMin;
+ leftPadding = rightPadding = 0;
+ }
+
+ // If we have a specified minwidth, take that into account...
+ minWidth = Mathf.Max (minWidth, childMinWidth + leftPadding + rightPadding);
+
+ if (maxWidth == 0) { // if we don't have a max width, take the one that was calculated
+ stretchWidth += stretchableCountX + (style.stretchWidth ? 1 : 0);
+ maxWidth = childMaxWidth + leftPadding + rightPadding;
+ } else {
+ // Since we have a maximum width, this element can't stretch width.
+ stretchWidth = 0;
+ }
+ // Finally, if our minimum width is greater than our maximum width, minWidth wins
+ maxWidth = Mathf.Max (maxWidth, minWidth);
+
+ // If the style sets us to be a fixed width that wins completely
+ if (style.fixedWidth != 0) {
+ maxWidth = minWidth = style.fixedWidth;
+ stretchWidth = 0;
+ }
+ }
+
+ public override void SetHorizontal (float x, float width) {
+ base.SetHorizontal (x, width);
+
+ if (resetCoords)
+ x = 0;
+
+ RectOffset padding = style.padding;
+
+ if (isVertical) {
+ // If we have a GUIStyle here, spacing from our edges to children are max (our padding, their margins)
+ if (style != GUIStyle.none) {
+ foreach (GUILayoutEntry i in entries) {
+ // NOTE: we can't use .horizontal here (As that could make things like right button margin getting eaten by large left padding - so we need to split up in left and right
+ float leftMar = Mathf.Max (i.margin.left, padding.left);
+ float thisX = x + leftMar;
+ float thisWidth = width - Mathf.Max (i.margin.right, padding.right) - leftMar;
+ if (i.stretchWidth != 0)
+ i.SetHorizontal (thisX, thisWidth);
+ else
+ i.SetHorizontal (thisX, Mathf.Clamp (thisWidth, i.minWidth, i.maxWidth));
+ }
+ } else {
+ // If not, PART of the subelements' margins have already been propagated upwards to this group, so we need to subtract that from what we apply
+ float thisX = x - margin.left;
+ float thisWidth = width + margin.horizontal;
+ foreach (GUILayoutEntry i in entries) {
+ if (i.stretchWidth != 0) {
+ i.SetHorizontal (thisX + i.margin.left, thisWidth - i.margin.horizontal);
+ } else
+ i.SetHorizontal (thisX + i.margin.left, Mathf.Clamp (thisWidth - i.margin.horizontal, i.minWidth, i.maxWidth));
+ }
+ }
+ } else { // we're horizontally laid out:
+ // apply margins/padding here
+ // If we have a style, adjust the sizing to take care of padding (if we don't the horizontal margins have been propagated fully up the hierarchy)...
+ if (style != GUIStyle.none) {
+ float leftMar = padding.left, rightMar = padding.right;
+ if (entries.Count != 0) {
+ leftMar = Mathf.Max (leftMar, ((GUILayoutEntry)entries[0]).margin.left);
+ rightMar = Mathf.Max (rightMar, ((GUILayoutEntry)entries[entries.Count - 1]).margin.right);
+ }
+ x += leftMar;
+ width -= rightMar + leftMar;
+ }
+
+ // Find out how much leftover width we should distribute.
+ float widthToDistribute = width - spacing * (entries.Count - 1);
+ // Where to place us in height between min and max
+ float minMaxScale = 0;
+ // How much height to add to stretchable elements
+ if (childMinWidth != childMaxWidth)
+ minMaxScale = Mathf.Clamp ((widthToDistribute - childMinWidth) / (childMaxWidth - childMinWidth), 0, 1);
+
+ // Handle stretching
+ float perItemStretch = 0;
+ if (widthToDistribute > childMaxWidth) { // If we have too much space, we need to distribute it.
+ if (stretchableCountX > 0) {
+ perItemStretch = (widthToDistribute - childMaxWidth) / (float)stretchableCountX;
+ }
+ }
+
+ // Set the positions
+ int lastMargin = 0;
+ bool firstMargin = true;
+// Debug.Log ("" + x + ", " + width + " perItemStretch:" + perItemStretch);
+// Debug.Log ("MinMaxScale"+ minMaxScale);
+ foreach (GUILayoutEntry i in entries) {
+ float thisWidth = Mathf.Lerp (i.minWidth, i.maxWidth, minMaxScale);
+// Debug.Log (i.minWidth);
+ thisWidth += perItemStretch * i.stretchWidth;
+
+ if (i.style != GUILayoutUtility.spaceStyle) { // Skip margins on spaces.
+ int leftMargin = i.margin.left;
+ if (firstMargin) {
+ leftMargin = 0;
+ firstMargin = false;
+ }
+ int margin = lastMargin > leftMargin ? lastMargin : leftMargin;
+ x += margin;
+ lastMargin = i.margin.right;
+ }
+
+ i.SetHorizontal (Mathf.Round (x), Mathf.Round(thisWidth));
+ x += thisWidth + spacing;
+ }
+ }
+ }
+
+ public override void CalcHeight () {
+ if (entries.Count == 0) {
+ maxHeight = minHeight = style.padding.vertical;
+ return;
+ }
+
+ childMinHeight = childMaxHeight = 0;
+ int _topMarginMin = 0, _bottomMarginMin = 0;
+ stretchableCountY = 0;
+ if (isVertical) {
+ int lastMargin = 0;
+ bool first = true;
+ foreach (GUILayoutEntry i in entries) {
+ i.CalcHeight ();
+ RectOffset m = i.margin;
+ int margin;
+
+ // Specialcase spaces - it's a space, so instead of handling margins normally, we just want to insert the size...
+ // This ensure that Space(1) adds ONE space, and doesn't prevent margin collapses
+ if (i.style != GUILayoutUtility.spaceStyle) {
+ if (!first)
+ margin = Mathf.Max(lastMargin, m.top);
+ else {
+ margin = 0;
+ first = false;
+ }
+
+ childMinHeight += i.minHeight + spacing + margin;
+ childMaxHeight += i.maxHeight + spacing + margin;
+ lastMargin = m.bottom;
+ stretchableCountY += i.stretchHeight;
+ } else {
+ childMinHeight += i.minHeight;
+ childMaxHeight += i.maxHeight;
+ stretchableCountY += i.stretchHeight;
+ }
+ }
+
+ childMinHeight -= spacing;
+ childMaxHeight -= spacing;
+ if (entries.Count != 0) {
+ _topMarginMin = ((GUILayoutEntry)entries[0]).margin.top;
+ _bottomMarginMin = lastMargin;
+ } else {
+ _bottomMarginMin = _topMarginMin = 0;
+ }
+ } else {
+ bool first = true;
+ foreach (GUILayoutEntry i in entries) {
+ i.CalcHeight ();
+ RectOffset margins = i.margin;
+ if (i.style != GUILayoutUtility.spaceStyle) {
+ if (!first) {
+ _topMarginMin = Mathf.Min (margins.top, _topMarginMin);
+ _bottomMarginMin = Mathf.Min (margins.bottom, _bottomMarginMin);
+ } else {
+ _topMarginMin = margins.top;
+ _bottomMarginMin = margins.bottom;
+ first = false;
+ }
+ childMinHeight = Mathf.Max (i.minHeight, childMinHeight);
+ childMaxHeight = Mathf.Max (i.maxHeight, childMaxHeight);
+ }
+ stretchableCountY += i.stretchHeight;
+ }
+ }
+ float firstPadding = 0, lastPadding = 0;
+
+ // If we have a style, the margins are handled i.r.t. padding.
+ if (style != GUIStyle.none || userSpecifiedHeight) {
+ // Add the padding of this group to the total min & max widths
+ firstPadding = Mathf.Max (style.padding.top, _topMarginMin);
+ lastPadding = Mathf.Max (style.padding.bottom, _bottomMarginMin);
+ }
+ else {
+ // If we don't have a GUIStyle, we bubble the margins outward from children on to us.
+ m_Margin.top = _topMarginMin;
+ m_Margin.bottom = _bottomMarginMin;
+ firstPadding = lastPadding = 0;
+ }
+ //Debug.Log ("Margins: " + _topMarginMin + ", " + _bottomMarginMin + " childHeights:" + childMinHeight + ", " + childMaxHeight);
+ // If we have a specified minheight, take that into account...
+ minHeight = Mathf.Max (minHeight, childMinHeight + firstPadding + lastPadding);
+
+ if (maxHeight == 0) { // if we don't have a max height, take the one that was calculated
+ stretchHeight += stretchableCountY + (style.stretchHeight ? 1 : 0);
+ maxHeight = childMaxHeight + firstPadding + lastPadding;
+ } else {
+ // Since we have a maximum height, this element can't stretch height.
+ stretchHeight = 0;
+ }
+ // Finally, if out minimum height is greater than our maximum height, minHeight wins
+ maxHeight = Mathf.Max (maxHeight, minHeight);
+
+ // If the style sets us to be a fixed height
+ if (style.fixedHeight != 0) {
+ maxHeight = minHeight = style.fixedHeight;
+ stretchHeight = 0;
+ }
+ }
+
+ public override void SetVertical (float y, float height) {
+ base.SetVertical (y, height);
+
+ if (entries.Count == 0)
+ return;
+
+ RectOffset padding = style.padding;
+
+ if (resetCoords)
+ y = 0;
+
+ if (isVertical) {
+ // If we have a skin, adjust the sizing to take care of padding (if we don't have a skin the vertical margins have been propagated fully up the hierarchy)...
+ if (style != GUIStyle.none) {
+ float topMar = padding.top, bottomMar = padding.bottom;
+ if (entries.Count != 0) {
+ topMar = Mathf.Max (topMar, ((GUILayoutEntry)entries[0]).margin.top);
+ bottomMar = Mathf.Max (bottomMar, ((GUILayoutEntry)entries[entries.Count - 1]).margin.bottom);
+ }
+ y += topMar;
+ height -= bottomMar + topMar;
+ }
+
+ // Find out how much leftover height we should distribute.
+ float heightToDistribute = height - spacing * (entries.Count - 1);
+ // Where to place us in height between min and max
+ float minMaxScale = 0;
+ // How much height to add to stretchable elements
+ if (childMinHeight != childMaxHeight)
+ minMaxScale = Mathf.Clamp ((heightToDistribute - childMinHeight) / (childMaxHeight - childMinHeight), 0, 1);
+
+ // Handle stretching
+ float perItemStretch = 0;
+ if (heightToDistribute > childMaxHeight) { // If we have too much space - stretch any stretchable children
+ if (stretchableCountY > 0)
+ perItemStretch = (heightToDistribute - childMaxHeight) / (float)stretchableCountY;
+ }
+
+ // Set the positions
+ int lastMargin = 0;
+ bool firstMargin = true;
+ foreach (GUILayoutEntry i in entries) {
+ float thisHeight = Mathf.Lerp (i.minHeight, i.maxHeight, minMaxScale);
+ thisHeight += perItemStretch * i.stretchHeight;
+
+ if (i.style != GUILayoutUtility.spaceStyle) { // Skip margins on spaces.
+ int topMargin = i.margin.top;
+ if (firstMargin) {
+ topMargin = 0;
+ firstMargin = false;
+ }
+ int margin = lastMargin > topMargin ? lastMargin : topMargin;
+ y += margin;
+ lastMargin = i.margin.bottom;
+ }
+ i.SetVertical (Mathf.Round (y), Mathf.Round(thisHeight));
+ y += thisHeight + spacing;
+ }
+ } else {
+ // If we have a GUIStyle here, we need to respect the subelements' margins
+ if (style != GUIStyle.none) {
+ foreach (GUILayoutEntry i in entries) {
+ float topMar = Mathf.Max (i.margin.top, padding.top);
+ float thisY = y + topMar;
+ float thisHeight = height - Mathf.Max (i.margin.bottom, padding.bottom) - topMar;
+
+ if (i.stretchHeight != 0)
+ i.SetVertical (thisY, thisHeight);
+ else {
+ i.SetVertical (thisY, Mathf.Clamp (thisHeight, i.minHeight, i.maxHeight));
+ }
+ }
+ } else {
+ // If not, the subelements' margins have already been propagated upwards to this group, so we can safely ignore them
+ float thisY = y - margin.top;
+ float thisHeight = height + margin.vertical;
+ foreach (GUILayoutEntry i in entries) {
+ if (i.stretchHeight != 0)
+ i.SetVertical (thisY +i.margin.top, thisHeight - i.margin.vertical);
+ else {
+ i.SetVertical (thisY + i.margin.top, Mathf.Clamp (thisHeight - i.margin.vertical, i.minHeight, i.maxHeight));
+ }
+ }
+
+ }
+ }
+ }
+
+ public override string ToString () {
+ string str = "", space = "";
+ for (int i = 0; i < indent; i++)
+ space += " ";
+ str += /* space + */ base.ToString () + " Margins: " + childMinHeight + " {\n";
+ indent += 4;
+ foreach (GUILayoutEntry i in entries) {
+ str += i.ToString() + "\n";
+ }
+ str += space + "}";
+ indent -= 4;
+ return str;
+ }
+END
+
+// Layout controller for content inside scroll views
+CLASS internal GUIScrollGroup : GUILayoutGroup
+ CSRAW
+ public float calcMinWidth, calcMaxWidth, calcMinHeight, calcMaxHeight;
+ public float clientWidth, clientHeight;
+ public bool allowHorizontalScroll = true;
+ public bool allowVerticalScroll = true;
+ public bool needsHorizontalScrollbar, needsVerticalScrollbar;
+ public GUIStyle horizontalScrollbar, verticalScrollbar;
+
+ public override void CalcWidth () {
+ // Save the size values & reset so we calc the sizes of children without any contraints
+ float _minWidth = minWidth;
+ float _maxWidth = maxWidth;
+ if (allowHorizontalScroll) {
+ minWidth = 0;
+ maxWidth = 0;
+ }
+
+ base.CalcWidth();
+ calcMinWidth = minWidth;
+ calcMaxWidth = maxWidth;
+
+ // restore the stored constraints for our parent's sizing
+ if (allowHorizontalScroll) {
+
+ // Set an explicit small minWidth so it will correctly scroll when place inside horizontal groups
+ if (minWidth > 32)
+ minWidth = 32;
+
+ if (_minWidth != 0)
+ minWidth = _minWidth;
+ if (_maxWidth != 0) {
+ maxWidth = _maxWidth;
+ stretchWidth = 0;
+ }
+ }
+ }
+
+ public override void SetHorizontal (float x, float width) {
+ float _cWidth = needsVerticalScrollbar ? width - verticalScrollbar.fixedWidth - verticalScrollbar.margin.left : width;
+ //if (allowVerticalScroll == false)
+ // Debug.Log ("width " + width);
+ // If we get a vertical scrollbar, the width changes, so we need to do a recalculation with the new width.
+ if (allowHorizontalScroll && _cWidth < calcMinWidth) {
+ // We're too small horizontally, so we need a horizontal scrollbar.
+ needsHorizontalScrollbar = true;
+
+ // set the min and max width we calculated for the children so SetHorizontal works correctly
+ minWidth = calcMinWidth;
+ maxWidth = calcMaxWidth;
+ base.SetHorizontal (x, calcMinWidth);
+
+ // SetHorizontal also sets our width, but we know better
+ rect.width = width;
+
+ clientWidth = calcMinWidth;
+ } else {
+ // Got enough space.
+ needsHorizontalScrollbar = false;
+
+ // set the min and max width we calculated for the children so SetHorizontal works correctly
+ if (allowHorizontalScroll) {
+ minWidth = calcMinWidth;
+ maxWidth = calcMaxWidth;
+ }
+ base.SetHorizontal (x, _cWidth);
+ rect.width = width;
+
+ // Store the client width
+ clientWidth = _cWidth;
+ }
+ }
+
+ public override void CalcHeight () {
+ // Save the values & reset so we calc the sizes of children without any contraints
+ float _minHeight = minHeight;
+ float _maxHeight = maxHeight;
+ if (allowVerticalScroll)
+ {
+ minHeight = 0;
+ maxHeight = 0;
+ }
+
+ base.CalcHeight();
+
+ calcMinHeight = minHeight;
+ calcMaxHeight = maxHeight;
+
+ // if we KNOW we need a horizontal scrollbar, claim space for it now
+ // otherwise we get a vertical scrollbar and leftover space beneath the scrollview.
+ if (needsHorizontalScrollbar) {
+ float scrollerSize = horizontalScrollbar.fixedHeight + horizontalScrollbar.margin.top;
+ minHeight += scrollerSize;
+ maxHeight += scrollerSize;
+ }
+
+ // restore the stored constraints from user SetHeight calls.
+ if (allowVerticalScroll)
+ {
+ if (minHeight > 32)
+ minHeight = 32;
+
+ if (_minHeight != 0)
+ minHeight = _minHeight;
+ if (_maxHeight != 0)
+ {
+ maxHeight = _maxHeight;
+ stretchHeight = 0;
+ }
+ }
+ }
+
+ public override void SetVertical (float y, float height) {
+ // if we have a horizontal scrollbar, we have less space than we thought
+ float availableHeight = height;
+ if (needsHorizontalScrollbar)
+ availableHeight -= horizontalScrollbar.fixedHeight + horizontalScrollbar.margin.top;
+
+ // Now we know how much height we have, and hence how much vertical space to distribute.
+ // If we get a vertical scrollbar, the width changes, so we need to do a recalculation with the new width.
+ if (allowVerticalScroll && availableHeight < calcMinHeight)
+ {
+ // We're too small vertically, so we need a vertical scrollbar.
+ // This means that we have less horizontal space, which can change the vertical size.
+ if (!needsHorizontalScrollbar && !needsVerticalScrollbar) {
+ // Subtract scrollbar width from the size...
+ clientWidth = rect.width - verticalScrollbar.fixedWidth - verticalScrollbar.margin.left;
+
+ // ...But make sure we never get too small.
+ if (clientWidth < calcMinWidth)
+ clientWidth = calcMinWidth;
+
+ // Set the new (smaller) size.
+ float outsideWidth = rect.width; // store a backup of our own width
+ SetHorizontal (rect.x, clientWidth);
+
+ // This can have caused a reflow, so we need to recalclate from here on down
+ // (we already know we need a vertical scrollbar, so this size change cannot bubble upwards.
+ CalcHeight();
+
+ rect.width = outsideWidth;
+ }
+
+
+ // set the min and max height we calculated for the children so SetVertical works correctly
+ float origMinHeight = minHeight, origMaxHeight = maxHeight;
+ minHeight = calcMinHeight;
+ maxHeight = calcMaxHeight;
+ base.SetVertical (y, calcMinHeight);
+ minHeight = origMinHeight;
+ maxHeight = origMaxHeight;
+
+ rect.height = height;
+ clientHeight = calcMinHeight;
+ } else {
+
+ // set the min and max height we calculated for the children so SetVertical works correctly
+ if (allowVerticalScroll)
+ {
+ minHeight = calcMinHeight;
+ maxHeight = calcMaxHeight;
+ }
+ base.SetVertical (y, availableHeight);
+ rect.height = height;
+ clientHeight = availableHeight;
+ }
+ }
+END
+
+// Layouter that makes elements which sizes will always conform to a specific aspect ratio.
+CLASS internal GUIAspectSizer : GUILayoutEntry
+ CSRAW float aspect;
+
+ public GUIAspectSizer (float aspect, GUILayoutOption[] options) : base (0,0,0,0,GUIStyle.none) {
+ this.aspect = aspect;
+ ApplyOptions (options);
+ }
+
+ public override void CalcHeight () {
+ minHeight = maxHeight = rect.width / aspect;
+ }
+END
+
+// Will layout a button grid so it can fit within the given rect.
+// *undocumented*
+CLASS internal GUIGridSizer : GUILayoutEntry
+ CSRAW
+ // Helper: Create the layout group and scale it to fit
+ public static Rect GetRect (GUIContent[] contents, int xCount, GUIStyle style, GUILayoutOption[] options) {
+ Rect r = new Rect (0,0,0,0);
+ switch (Event.current.type) {
+ case EventType.Layout: {
+ GUILayoutUtility.current.topLevel.Add (new GUIGridSizer (contents, xCount, style, options));
+ break;
+ }
+ case EventType.Used:
+ return kDummyRect;
+ default:
+ r = GUILayoutUtility.current.topLevel.GetNext ().rect;
+ break;
+ }
+ return r;
+ }
+ int count;
+ int xCount;
+ float minButtonWidth = -1, maxButtonWidth = -1, minButtonHeight = -1, maxButtonHeight = -1;
+
+ // Cache of the content for wordwrapping.
+ //GUIContent[] cachedContent;
+
+ private GUIGridSizer (GUIContent[] contents, int _xCount, GUIStyle buttonStyle, GUILayoutOption[] options) : base (0,0,0,0,GUIStyle.none) {
+ count = contents.Length;
+ xCount = _xCount;
+
+ // Most settings comes from the button style (can we stretch, etc). Hence, I apply the style here
+ ApplyStyleSettings (buttonStyle);
+
+ // We can have custom options coming from userland. We apply this last so it overrides
+ ApplyOptions (options);
+
+ if (_xCount == 0 || contents.Length == 0)
+ return;
+
+ // if we don't have wordwrap, there is no funky width<->height relationship. Hence, we can calculate everything here:
+ // TODO: Actually make it work with wordwrapping stuff (that is pretty hard)
+// if (!buttonStyle.wordWrap) {
+
+ // internal horizontal spacing
+ float totalHorizSpacing = Mathf.Max (buttonStyle.margin.left,buttonStyle.margin.right) * (xCount - 1);
+// Debug.Log (String.Format ("margins: {0}, {1} totalHoriz: {2}", buttonStyle.margin.left, buttonStyle.margin.right, totalHorizSpacing));
+ // internal horizontal margins
+ float totalVerticalSpacing = Mathf.Max (buttonStyle.margin.top, buttonStyle.margin.bottom) * (rows - 1);
+
+
+ // Handle fixedSize buttons
+ if (buttonStyle.fixedWidth != 0)
+ minButtonWidth = maxButtonWidth = buttonStyle.fixedWidth;
+// Debug.Log ("buttonStyle.fixedHeight " + buttonStyle.fixedHeight);
+ if (buttonStyle.fixedHeight != 0)
+ minButtonHeight = maxButtonHeight = buttonStyle.fixedHeight;
+
+ // Apply GUILayout.Width/Height/whatever properties.
+ if (minButtonWidth == -1) {
+ if (minWidth != 0)
+ minButtonWidth = (minWidth - totalHorizSpacing) / xCount;
+ if (maxWidth != 0)
+ maxButtonWidth = (maxWidth - totalHorizSpacing) / xCount;
+ }
+
+ if (minButtonHeight == -1) {
+ if (minHeight != 0)
+ minButtonHeight = (minHeight - totalVerticalSpacing) / rows;
+ if (maxHeight != 0)
+ maxButtonHeight = (maxHeight - totalVerticalSpacing) / rows;
+ }
+// Debug.Log (String.Format ("minButtonWidth {0}, maxButtonWidth {1}, minButtonHeight {2}, maxButtonHeight{3}", minButtonWidth, maxButtonWidth, minButtonHeight, maxButtonHeight));
+
+ // if anything is left unknown, we need to iterate over all elements and figure out the sizes.
+ if (minButtonHeight == -1 || maxButtonHeight == -1 || minButtonWidth == -1 || maxButtonWidth == -1) {
+ // figure out the max size. Since the buttons are in a grid, the max size determines stuff.
+ float calcHeight = 0, calcWidth = 0;
+ foreach (GUIContent i in contents) {
+ Vector2 size = buttonStyle.CalcSize (i);
+ calcWidth = Mathf.Max (calcWidth, size.x);
+ calcHeight = Mathf.Max (calcHeight, size.y);
+ }
+
+ // If the user didn't supply minWidth, we need to calculate that
+ if (minButtonWidth == -1) {
+ // if the user has supplied a maxButtonWidth, the buttons can never get larger.
+ if (maxButtonWidth != -1)
+ minButtonWidth = Mathf.Min (calcWidth, maxButtonWidth);
+ else
+ minButtonWidth = calcWidth;
+ }
+
+ // If the user didn't supply maxWidth, we need to calculate that
+ if (maxButtonWidth == -1) {
+ // if the user has supplied a minButtonWidth, the buttons can never get smaler.
+ if (minButtonWidth != -1)
+ maxButtonWidth = Mathf.Max (calcWidth, minButtonWidth);
+ else
+ maxButtonWidth = calcWidth;
+ }
+
+ // If the user didn't supply minWidth, we need to calculate that
+ if (minButtonHeight == -1) {
+ // if the user has supplied a maxButtonWidth, the buttons can never get larger.
+ if (maxButtonHeight != -1)
+ minButtonHeight = Mathf.Min (calcHeight, maxButtonHeight);
+ else
+ minButtonHeight = calcHeight;
+ }
+
+ // If the user didn't supply maxWidth, we need to calculate that
+ if (maxButtonHeight == -1) {
+ // if the user has supplied a minButtonWidth, the buttons can never get smaler.
+ if (minButtonHeight != -1)
+ maxHeight = Mathf.Max (maxHeight, minButtonHeight);
+ maxButtonHeight = maxHeight;
+ }
+
+ }
+ // We now know the button sizes. Calculate min & max values from that
+ minWidth = minButtonWidth * xCount + totalHorizSpacing;
+ maxWidth = maxButtonWidth * xCount + totalHorizSpacing;
+ minHeight = minButtonHeight * rows + totalVerticalSpacing;
+ maxHeight = maxButtonHeight * rows + totalVerticalSpacing;
+// Debug.Log (String.Format ("minWidth {0}, maxWidth {1}, minHeight {2}, maxHeight{3}", minWidth, maxWidth, minHeight, maxHeight));
+
+/*
+ } else {
+ // if we're wordwrapping, we need to copy the contents and then do the whole calculations a bit down the road.
+ cachedContent = new GUIContent [contents.Length];
+ for (int i = 0; i < contents.Length; i++) {
+ cachedContent[i] = new GUIContent (contents[i]);
+ }
+ }
+*/
+ }
+
+ int rows {
+ get {
+ int rows = count / xCount;
+ if (count % xCount != 0)
+ rows++;
+ return rows;
+ }
+ }
+END
+
+// Class that can handle word-wrap sizing. this is specialcased as setting width can make the text wordwrap, which would then increase height...
+CLASS internal GUIWordWrapSizer : GUILayoutEntry
+ CSRAW
+ GUIContent content;
+ // We need to differentiate between min & maxHeight we calculate for ourselves and one that is forced by the user
+ // (When inside a scrollview, we can be told to layout twice, so we need to know the difference)
+ float forcedMinHeight, forcedMaxHeight;
+ public GUIWordWrapSizer (GUIStyle _style, GUIContent _content, GUILayoutOption[] options) : base (0,0,0,0, _style) {
+ content = new GUIContent (_content);
+ base.ApplyOptions (options);
+ forcedMinHeight = minHeight;
+ forcedMaxHeight = maxHeight;
+ }
+
+ public override void CalcWidth () {
+ if (minWidth == 0 || maxWidth == 0) {
+ float _minWidth, _maxWidth;
+ style.CalcMinMaxWidth (content, out _minWidth, out _maxWidth);
+ if (minWidth == 0)
+ minWidth = _minWidth;
+ if (maxWidth == 0)
+ maxWidth = _maxWidth;
+ }
+ }
+
+ public override void CalcHeight () {
+ // When inside a scrollview, this can get called twice (as vertical scrollbar reduces width, which causes a reflow).
+ // Hence, we need to use the separately cached values for min & maxHeight coming from the user...
+ if (forcedMinHeight == 0 || forcedMaxHeight == 0) {
+ float height = style.CalcHeight (content, rect.width);
+ if (forcedMinHeight == 0)
+ minHeight = height;
+ else
+ minHeight = forcedMinHeight;
+ if (forcedMaxHeight == 0)
+ maxHeight = height;
+ else
+ maxHeight = forcedMaxHeight;
+ }
+ }
+END
+
+// Class internally used to pass layout options into [[GUILayout]] functions. You don't use these directly, but construct them with the layouting functions in the [[GUILayout]] class.
+CLASS GUILayoutOption
+ CSRAW
+ internal enum Type {
+ fixedWidth, fixedHeight, minWidth, maxWidth, minHeight, maxHeight, stretchWidth, stretchHeight,
+ // These are just for the spacing variables
+ alignStart, alignMiddle, alignEnd, alignJustify, equalSize, spacing
+ }
+ // *undocumented*
+ internal Type type;
+ // *undocumented*
+ internal object value;
+ // *undocumented*
+ internal GUILayoutOption (Type type, object value) {
+ this.type = type;
+ this.value = value;
+ }
+END
+
+
+CSRAW
+
+}
diff --git a/Runtime/Export/GUISkinBindings.txt b/Runtime/Export/GUISkinBindings.txt
new file mode 100644
index 0000000..bc6fcca
--- /dev/null
+++ b/Runtime/Export/GUISkinBindings.txt
@@ -0,0 +1,341 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Misc/InputEvent.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace UnityEngine
+{
+
+
+
+// Which platform to emulate.
+ENUM internal PlatformSelection
+ // The behaviour matches the platform the end user is running on.
+ Native = 0,
+ // The behaviour matches a Mac OS X machine.
+ Mac = 1,
+ // The behaviour matches a Windows machine.
+ Windows = 2,
+END
+
+// General settings for how the GUI behaves
+CSRAW [System.Serializable]
+CLASS GUISettings
+ // Should double-clicking select words in text fields.
+ CSRAW public bool doubleClickSelectsWord { get { return m_DoubleClickSelectsWord; } set { m_DoubleClickSelectsWord = value; } }
+ CSRAW [SerializeField]
+ CSRAW bool m_DoubleClickSelectsWord = true;
+
+ // Should triple-clicking select whole text in text fields.
+ CSRAW public bool tripleClickSelectsLine { get { return m_TripleClickSelectsLine; } set { m_TripleClickSelectsLine = value; } }
+ CSRAW [SerializeField]
+ CSRAW bool m_TripleClickSelectsLine = true;
+
+ // The color of the cursor in text fields.
+ CSRAW public Color cursorColor { get { return m_CursorColor; } set { m_CursorColor = value; } }
+ CSRAW [SerializeField]
+ CSRAW Color m_CursorColor = Color.white;
+
+ CUSTOM private static float Internal_GetCursorFlashSpeed () {
+ #if UNITY_OSX && !UNITY_64
+ return 60.0f / GetCaretTime();
+ #elif UNITY_WIN && !UNITY_WINRT
+ return 1000.0f / GetCaretBlinkTime();
+ #elif UNITY_WII || UNITY_XENON || UNITY_PS3 || UNITY_IPHONE || UNITY_ANDROID || UNITY_PEPPER || UNITY_LINUX || UNITY_FLASH || UNITY_WEBGL || UNITY_WINRT || UNITY_OSX || UNITY_BB10 || UNITY_TIZEN
+ return 2.0f;
+ #else
+ #error "Unknown platform"
+ #endif
+ }
+
+ // The speed of text field cursor flashes.
+ CSRAW public float cursorFlashSpeed { get {
+ if (m_CursorFlashSpeed >= 0)
+ return m_CursorFlashSpeed;
+ else {
+ return Internal_GetCursorFlashSpeed ();
+ }
+ }
+ set { m_CursorFlashSpeed = value; } }
+ CSRAW [SerializeField]
+ CSRAW float m_CursorFlashSpeed = -1;
+
+ // The color of the selection rect in text fields.
+ CSRAW public Color selectionColor { get { return m_SelectionColor; } set { m_SelectionColor = value; } }
+ CSRAW [SerializeField]
+ CSRAW Color m_SelectionColor = new Color (.5f, .5f, 1f);
+
+ // Which platform to match for keyboard focus rules.
+ // If keyboardFocus is Mac, only text entry labels will be able to revcieve the keyboard focus through tabbing. (GUI.TextField, GUI.TextArea, GUILayout.TextField, GUILayout.TextArea)
+ // If keyboardFocus is Windows, most controls can be tabbed through. If the end user presses the space bar, the focused control will recieve a mouse click.
+ // If keyboardFocus is Native, the focus rules will follow whatever platform the end user is running on.
+// CSRAW public PlatformSelection keyboardFocus { get { return m_KeyboardFocus; } set { m_KeyboardFocus = value; } }
+// CSRAW PlatformSelection m_KeyboardFocus = PlatformSelection.Native;
+END
+
+
+
+// Defines how GUI looks and behaves.
+CSRAW [System.Serializable]
+CSRAW [ExecuteInEditMode]
+CLASS GUISkin : ScriptableObject
+ CSRAW [SerializeField]
+ CSRAW Font m_Font;
+
+ // *undocumented*
+ CSRAW public GUISkin()
+ {
+ m_CustomStyles = new GUIStyle[1];
+ }
+
+ CSRAW internal void OnEnable()
+ {
+ Apply ();
+
+ foreach (var style in styles.Values)
+ style.CreateObjectReferences ();
+ }
+
+ // The default font to use for all styles.
+ CSRAW public Font font { get { return m_Font; } set { m_Font = value; if (current == this) GUIStyle.SetDefaultFont(m_Font); Apply();} }
+
+ CSRAW [SerializeField] //yes the attribute applies to all fields on the line below.
+ CSRAW GUIStyle m_box, m_button, m_toggle, m_label, m_textField, m_textArea, m_window;
+
+ // Style used by default for GUI::ref::Box controls.
+ CSRAW public GUIStyle box { get { return m_box; } set { m_box = value; Apply (); } }
+
+ // Style used by default for GUI::ref::Label controls.
+ CSRAW public GUIStyle label { get { return m_label; } set { m_label = value; Apply (); } }
+
+ // Style used by default for GUI::ref::TextField controls.
+ CSRAW public GUIStyle textField { get { return m_textField; } set { m_textField = value; Apply (); } }
+
+ // Style used by default for GUI::ref::TextArea controls.
+ CSRAW public GUIStyle textArea { get { return m_textArea; } set { m_textArea = value; Apply (); } }
+
+ // Style used by default for GUI::ref::Button controls.
+ CSRAW public GUIStyle button { get { return m_button; } set { m_button = value; Apply (); } }
+
+ // Style used by default for GUI::ref::Toggle controls.
+ CSRAW public GUIStyle toggle { get { return m_toggle; } set { m_toggle = value; Apply (); } }
+
+ // Style used by default for Window controls (SA GUI::ref::Window).
+ CSRAW public GUIStyle window { get { return m_window; } set { m_window = value; Apply (); } }
+
+ CSRAW
+ [SerializeField]
+ GUIStyle m_horizontalSlider;
+ [SerializeField]
+ GUIStyle m_horizontalSliderThumb;
+ [SerializeField]
+ GUIStyle m_verticalSlider;
+ [SerializeField]
+ GUIStyle m_verticalSliderThumb;
+
+ // Style used by default for the background part of GUI::ref::HorizontalSlider controls.
+ CSRAW public GUIStyle horizontalSlider { get { return m_horizontalSlider; } set { m_horizontalSlider = value; Apply (); } }
+
+ // Style used by default for the thumb that is dragged in GUI::ref::HorizontalSlider controls.
+ CSRAW public GUIStyle horizontalSliderThumb { get { return m_horizontalSliderThumb; } set { m_horizontalSliderThumb = value; Apply (); } }
+
+ // Style used by default for the background part of GUI::ref::VerticalSlider controls.
+ CSRAW public GUIStyle verticalSlider { get { return m_verticalSlider; } set { m_verticalSlider = value; Apply (); } }
+
+ // Style used by default for the thumb that is dragged in GUI::ref::VerticalSlider controls.
+ CSRAW public GUIStyle verticalSliderThumb { get { return m_verticalSliderThumb; } set { m_verticalSliderThumb = value; Apply (); } }
+
+ CSRAW
+ [SerializeField]
+ GUIStyle m_horizontalScrollbar;
+ [SerializeField]
+ GUIStyle m_horizontalScrollbarThumb;
+ [SerializeField]
+ GUIStyle m_horizontalScrollbarLeftButton;
+ [SerializeField]
+ GUIStyle m_horizontalScrollbarRightButton;
+
+ // Style used by default for the background part of GUI::ref::HorizontalScrollbar controls.
+ CSRAW public GUIStyle horizontalScrollbar { get { return m_horizontalScrollbar; } set { m_horizontalScrollbar = value; Apply (); } }
+ // Style used by default for the thumb that is dragged in GUI::ref::HorizontalScrollbar controls.
+ CSRAW public GUIStyle horizontalScrollbarThumb { get { return m_horizontalScrollbarThumb; } set { m_horizontalScrollbarThumb = value; Apply (); } }
+ // Style used by default for the left button on GUI::ref::HorizontalScrollbar controls.
+ CSRAW public GUIStyle horizontalScrollbarLeftButton { get { return m_horizontalScrollbarLeftButton; } set { m_horizontalScrollbarLeftButton = value; Apply (); } }
+ // Style used by default for the right button on GUI::ref::HorizontalScrollbar controls.
+ CSRAW public GUIStyle horizontalScrollbarRightButton { get { return m_horizontalScrollbarRightButton; } set { m_horizontalScrollbarRightButton = value; Apply (); } }
+
+ CSRAW
+ [SerializeField]
+ GUIStyle m_verticalScrollbar;
+ [SerializeField]
+ GUIStyle m_verticalScrollbarThumb;
+ [SerializeField]
+ GUIStyle m_verticalScrollbarUpButton;
+ [SerializeField]
+ GUIStyle m_verticalScrollbarDownButton;
+
+ // Style used by default for the background part of GUI::ref::VerticalScrollbar controls.
+ CSRAW public GUIStyle verticalScrollbar { get { return m_verticalScrollbar; } set { m_verticalScrollbar = value; Apply (); } }
+ // Style used by default for the thumb that is dragged in GUI::ref::VerticalScrollbar controls.
+ CSRAW public GUIStyle verticalScrollbarThumb { get { return m_verticalScrollbarThumb; } set { m_verticalScrollbarThumb = value; Apply (); } }
+ // Style used by default for the up button on GUI::ref::VerticalScrollbar controls.
+ CSRAW public GUIStyle verticalScrollbarUpButton { get { return m_verticalScrollbarUpButton; } set { m_verticalScrollbarUpButton = value; Apply (); } }
+ // Style used by default for the down button on GUI::ref::VerticalScrollbar controls.
+ CSRAW public GUIStyle verticalScrollbarDownButton { get { return m_verticalScrollbarDownButton; } set { m_verticalScrollbarDownButton = value; Apply (); } }
+
+ // Background style for scroll views.
+ CSRAW [SerializeField]
+ CSRAW GUIStyle m_ScrollView;
+
+ // Style used by default for the background of ScrollView controls (see GUI::ref::BeginScrollView).
+ CSRAW public GUIStyle scrollView { get { return m_ScrollView; } set { m_ScrollView = value; Apply (); } }
+
+ CSRAW [SerializeField]
+ CSRAW internal GUIStyle[] m_CustomStyles;
+
+ // Array of GUI styles for specific needs.
+ CSRAW public GUIStyle[] customStyles { get { return m_CustomStyles; } set { m_CustomStyles = value; Apply(); } }
+
+
+ CSRAW [SerializeField]
+ CSRAW private GUISettings m_Settings = new GUISettings ();
+
+ // Generic settings for how controls should behave with this skin.
+ CSRAW public GUISettings settings { get { return m_Settings; } }
+
+ CSRAW internal static GUIStyle ms_Error;
+ CSRAW internal static GUIStyle error { get { if(ms_Error == null) ms_Error = new GUIStyle(); return ms_Error; } }
+
+ CSRAW private Dictionary<string, GUIStyle> styles = null;
+
+ CSRAW internal void Apply ()
+ {
+ if (m_CustomStyles == null)
+ Debug.Log("custom styles is null");
+
+ BuildStyleCache ();
+ }
+
+
+ CSRAW private void BuildStyleCache () {
+
+ if (m_box == null) m_box = new GUIStyle();
+ if (m_button == null) m_button = new GUIStyle();
+ if (m_toggle == null) m_toggle = new GUIStyle();
+ if (m_label == null) m_label = new GUIStyle();
+ if (m_window == null) m_window = new GUIStyle();
+ if (m_textField == null) m_textField = new GUIStyle();
+ if (m_textArea == null) m_textArea = new GUIStyle();
+ if (m_horizontalSlider == null) m_horizontalSlider = new GUIStyle();
+ if (m_horizontalSliderThumb == null) m_horizontalSliderThumb = new GUIStyle();
+ if (m_verticalSlider == null) m_verticalSlider = new GUIStyle();
+ if (m_verticalSliderThumb == null) m_verticalSliderThumb = new GUIStyle();
+ if (m_horizontalScrollbar == null) m_horizontalScrollbar = new GUIStyle();
+ if (m_horizontalScrollbarThumb == null) m_horizontalScrollbarThumb = new GUIStyle();
+ if (m_horizontalScrollbarLeftButton == null) m_horizontalScrollbarLeftButton = new GUIStyle();
+ if (m_horizontalScrollbarRightButton == null) m_horizontalScrollbarRightButton = new GUIStyle();
+ if (m_verticalScrollbar == null) m_verticalScrollbar = new GUIStyle();
+ if (m_verticalScrollbarThumb == null) m_verticalScrollbarThumb = new GUIStyle();
+ if (m_verticalScrollbarUpButton == null) m_verticalScrollbarUpButton = new GUIStyle();
+ if (m_verticalScrollbarDownButton == null) m_verticalScrollbarDownButton = new GUIStyle();
+ if (m_ScrollView == null) m_ScrollView = new GUIStyle();
+
+ styles = new Dictionary<string, GUIStyle>(
+#if !UNITY_FLASH
+ System.StringComparer.OrdinalIgnoreCase
+#endif
+ );
+
+ styles["box"] = m_box; m_box.name = "box";
+ styles["button"] = m_button; m_button.name = "button";
+ styles["toggle"] = m_toggle; m_toggle.name = "toggle";
+ styles["label"] = m_label; m_label.name = "label";
+ styles["window"] = m_window; m_window.name = "window";
+ styles["textfield"] = m_textField; m_textField.name = "textfield";
+ styles["textarea"] = m_textArea; m_textArea.name = "textarea";
+
+ styles["horizontalslider"] = m_horizontalSlider; m_horizontalSlider.name = "horizontalslider";
+ styles["horizontalsliderthumb"] = m_horizontalSliderThumb; m_horizontalSliderThumb.name = "horizontalsliderthumb";
+ styles["verticalslider"] = m_verticalSlider; m_verticalSlider.name = "verticalslider";
+ styles["verticalsliderthumb"] = m_verticalSliderThumb; m_verticalSliderThumb.name = "verticalsliderthumb";
+
+ styles["horizontalscrollbar"] = m_horizontalScrollbar; m_horizontalScrollbar.name = "horizontalscrollbar";
+ styles["horizontalscrollbarthumb"] = m_horizontalScrollbarThumb; m_horizontalScrollbarThumb.name = "horizontalscrollbarthumb";
+ styles["horizontalscrollbarleftbutton"] = m_horizontalScrollbarLeftButton; m_horizontalScrollbarLeftButton.name = "horizontalscrollbarleftbutton";
+ styles["horizontalscrollbarrightbutton"] = m_horizontalScrollbarRightButton; m_horizontalScrollbarRightButton.name = "horizontalscrollbarrightbutton";
+ styles["verticalscrollbar"] = m_verticalScrollbar; m_verticalScrollbar.name = "verticalscrollbar";
+ styles["verticalscrollbarthumb"] = m_verticalScrollbarThumb; m_verticalScrollbarThumb.name = "verticalscrollbarthumb";
+ styles["verticalscrollbarupbutton"] = m_verticalScrollbarUpButton; m_verticalScrollbarUpButton.name = "verticalscrollbarupbutton";
+ styles["verticalscrollbardownbutton"] = m_verticalScrollbarDownButton; m_verticalScrollbarDownButton.name = "verticalscrollbardownbutton";
+ styles["scrollview"] = m_ScrollView; m_ScrollView.name = "scrollview";
+
+ if (m_CustomStyles != null)
+ {
+ for (int i=0;i<m_CustomStyles.Length;i++)
+ {
+ if (m_CustomStyles[i] == null)
+ continue;
+ styles[m_CustomStyles[i].name] = m_CustomStyles[i];
+ }
+ }
+ error.stretchHeight = true;
+ error.normal.textColor = Color.red;
+ }
+
+ // Get a named [[GUIStyle]].
+ CSRAW public GUIStyle GetStyle (string styleName) {
+ GUIStyle s = FindStyle (styleName);
+ if (s != null)
+ return s;
+ Debug.LogWarning ("Unable to find style '" + styleName + "' in skin '" + name + "' " + Event.current.type);
+ return error;
+ }
+
+ // Try to search for a [[GUIStyle]]. This functions returns NULL and does not give an error.
+ CSRAW public GUIStyle FindStyle (string styleName) {
+ if (this == null) {
+ Debug.LogError ("GUISkin is NULL");
+ return null;
+ }
+ if (styles == null)
+ BuildStyleCache ();
+
+ GUIStyle style;
+ if (styles.TryGetValue(styleName, out style))
+ return style;
+
+ return null;
+ }
+
+ CSRAW
+ internal delegate void SkinChangedDelegate();
+ internal static SkinChangedDelegate m_SkinChanged;
+
+ // Make this the current skin used by the GUI
+ static internal GUISkin current;
+ CSRAW internal void MakeCurrent () {
+ current = this;
+ GUIStyle.SetDefaultFont (font);
+ if (m_SkinChanged != null)
+ m_SkinChanged();
+ }
+
+ //*undocumented* Documented separately
+ CSRAW public IEnumerator GetEnumerator ()
+ {
+ if (styles == null)
+ BuildStyleCache ();
+ return (IEnumerator)styles.Values.GetEnumerator ();
+ }
+END
+
+
+}
diff --git a/Runtime/Export/GUIStateObjects.cs b/Runtime/Export/GUIStateObjects.cs
new file mode 100644
index 0000000..e37bc93
--- /dev/null
+++ b/Runtime/Export/GUIStateObjects.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace UnityEngine
+{
+
+internal class GUIStateObjects
+{
+ static Dictionary<int, object> s_StateCache = new Dictionary<int, object>();
+
+ [System.Security.SecuritySafeCritical]
+ static internal object GetStateObject (System.Type t, int controlID)
+ {
+ object o;
+ if (s_StateCache.TryGetValue(controlID, out o) == false || o.GetType() != t)
+ {
+ o = System.Activator.CreateInstance(t);
+ s_StateCache[controlID] = o;
+ }
+ return o;
+ }
+
+ static internal object QueryStateObject (System.Type t, int controlID)
+ {
+ object o = s_StateCache[controlID];
+ if (t.IsInstanceOfType (o))
+ return o;
+ return null;
+ }
+
+}
+
+} // namespace
diff --git a/Runtime/Export/GUIStyleBindings.txt b/Runtime/Export/GUIStyleBindings.txt
new file mode 100644
index 0000000..76ac550
--- /dev/null
+++ b/Runtime/Export/GUIStyleBindings.txt
@@ -0,0 +1,922 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "Runtime/IMGUI/GUIContent.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/GUIManager.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Filters/Misc/Font.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+
+#if UNITY_EDITOR
+//#include "Editor/Src/EditorHelper.h"
+//#include "Editor/Mono/MonoEditorUtility.h"
+//#include "Editor/Src/OptimizedGUIBlock.h"
+//#include "Editor/Src/AssetPipeline/AssetDatabase.h"
+
+//#include "Editor/Src/EditorResources.h"
+#endif
+
+using namespace Unity;
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using UnityEngineInternal;
+
+namespace UnityEngine
+{
+
+
+
+// Specialized values for the given states used by [[GUIStyle]] objects.
+CSRAW #if !UNITY_WINRT
+CSRAW [StructLayout (LayoutKind.Sequential)]
+CSRAW #endif
+CSRAW [System.Serializable]
+CLASS GUIStyleState
+ // Pointer to the GUIStyleState INSIDE a GUIStyle.
+ CSRAW [System.NonSerialized] [NotRenamed]
+ CSRAW internal IntPtr m_Ptr;
+
+ // Pointer to the source GUIStyle so it doesn't get garbage collected.
+ // If NULL, it means we own m_Ptr and need to delete it when this gets displosed
+ CSRAW GUIStyle m_SourceStyle;
+
+ // Pointer to the texture that is referenced from the GUIStyleState.
+ // Necessary for the Asset Garbage Collector to find the texture reference
+CSRAW #pragma warning disable 414
+ [System.NonSerialized]
+ CSRAW private Texture2D m_Background;
+CSRAW #pragma warning restore 414
+
+ public GUIStyleState ()
+ {
+ Init ();
+ }
+
+ internal GUIStyleState (GUIStyle sourceStyle, IntPtr source)
+ {
+ m_SourceStyle = sourceStyle;
+ m_Ptr = source;
+ RefreshAssetReference();
+ }
+
+ internal void RefreshAssetReference ()
+ {
+ m_Background = GetBackgroundInternal();
+ }
+
+ ~GUIStyleState ()
+ {
+ if (m_SourceStyle == null)
+ Cleanup ();
+ }
+
+ THREAD_SAFE
+ CUSTOM private void Init () {
+ self.SetPtr(new GUIStyleState());
+
+ }
+
+ THREAD_SAFE
+ CUSTOM private void Cleanup () {
+ delete self.GetPtr();
+ }
+
+ // The background image used by GUI elements in this given state.
+ CUSTOM private void SetBackgroundInternal (Texture2D value) {
+ self->background = (Texture2D*) (value);
+ }
+
+ CUSTOM private Texture2D GetBackgroundInternal () {
+ return Scripting::ScriptingWrapperFor (self->background);
+ }
+
+ CSRAW public Texture2D background {
+ get { return GetBackgroundInternal(); }
+ set { SetBackgroundInternal (value); m_Background = value; }
+ }
+
+ // The text color used by GUI elements in this state.
+ CUSTOM_PROP Color textColor { return self->textColor; } { self->textColor = value; }
+END
+
+
+// Offsets for rectangles, borders, etc.
+CSRAW #if !UNITY_WINRT
+CSRAW [StructLayout (LayoutKind.Sequential)]
+CSRAW #endif
+CSRAW [System.Serializable]
+CLASS RectOffset
+
+ // Pointer to the RectOffset INSIDE a GUIStyle.
+ CSRAW [System.NonSerialized] [NotRenamed]
+ CSRAW internal IntPtr m_Ptr;
+
+ // Pointer to the source GUIStyle so it doesn't get garbage collected.
+ // If NULL, it means we own m_Ptr and need to delete it when this gets displosed
+ CSRAW GUIStyle m_SourceStyle;
+
+ /// *listonly*
+ CSRAW public RectOffset () {
+ Init ();
+ }
+
+ internal RectOffset (GUIStyle sourceStyle, IntPtr source)
+ {
+ m_SourceStyle = sourceStyle;
+ m_Ptr = source;
+ }
+
+ ~RectOffset ()
+ {
+ if (m_SourceStyle == null)
+ Cleanup ();
+ }
+
+ THREAD_SAFE
+ CUSTOM private void Init () {
+ self.SetPtr(new RectOffset());
+ }
+
+ THREAD_SAFE
+ CUSTOM private void Cleanup () {
+ delete self.GetPtr();
+ }
+
+
+ // Creates a new rectangle with offsets.
+ CSRAW public RectOffset (int left, int right, int top, int bottom) {
+ Init ();
+ this.left = left;
+ this.right = right;
+ this.top = top;
+ this.bottom = bottom;
+ }
+
+ // Left edge size.
+ CUSTOM_PROP int left { return self->left; } { self->left = value; }
+ // Right edge size.
+ CUSTOM_PROP int right { return self->right; } { self->right = value; }
+ // Top edge size.
+ CUSTOM_PROP int top { return self->top; } { self->top = value; }
+ // Bottom edge size.
+ CUSTOM_PROP int bottom { return self->bottom; } { self->bottom = value; }
+ // shortcut for left + right (RO)
+ CUSTOM_PROP int horizontal { return self->left + self->right; }
+ // shortcut for top + bottom (RO)
+ CUSTOM_PROP public int vertical { return self->top + self->bottom; }
+ // Add the border offsets to a /rect/.
+ CUSTOM public Rect Add (Rect rect)
+ { return Rectf (rect.x - self->left, rect.y - self->top, rect.width + self->left + self->right, rect.height + self->top + self->bottom); }
+ // Remove the border offsets from a /rect/.
+ CUSTOM public Rect Remove (Rect rect)
+ { return Rectf (rect.x + self->left, rect.y + self->top, rect.width - self->left - self->right, rect.height - self->top - self->bottom); }
+
+ CSRAW public override string ToString () {
+ return UnityString.Format ("RectOffset (l:{0} r:{1} t:{2} b:{3})", left, right, top, bottom);
+ }
+
+END
+
+// Font Style applied to GUI Texts, Text Meshes or GUIStyles.
+ENUM FontStyle
+ // No special style is applied.
+ Normal = 0,
+
+ // Bold style applied to your texts.
+ Bold = 1,
+
+ // Italic style applied to your texts.
+ Italic = 2,
+
+ // Bold and Italic styles applied to your texts.
+ BoldAndItalic = 3,
+END
+
+C++RAW
+
+// Simple struct that contains all the arguments needed by Internal_Draw.
+STRUCT internal Internal_DrawArguments
+ CSRAW public IntPtr target;
+ CSRAW public Rect position;
+ CSRAW public int isHover;
+ CSRAW public int isActive;
+ CSRAW public int on;
+ CSRAW public int hasKeyboardFocus;
+END
+
+C++RAW
+
+struct MonoInternal_DrawArguments {
+ void* target;
+ const Rectf position;
+ int isHover;
+ int isActive;
+ int on;
+ int hasKeyboardFocus;
+};
+
+// Simple struct that contains all the arguments needed by Internal_DrawWithTextSelection.
+STRUCT internal Internal_DrawWithTextSelectionArguments
+ CSRAW public IntPtr target;
+ CSRAW public Rect position;
+ CSRAW public int firstPos;
+ CSRAW public int lastPos;
+ CSRAW public Color cursorColor;
+ CSRAW public Color selectionColor;
+ CSRAW public int isHover;
+ CSRAW public int isActive;
+ CSRAW public int on;
+ CSRAW public int hasKeyboardFocus;
+ CSRAW public int drawSelectionAsComposition;
+
+END
+
+C++RAW
+
+struct MonoInternal_DrawWithTextSelectionArguments {
+ void* target;
+ Rectf position;
+ int firstPos;
+ int lastPos;
+ const ColorRGBAf cursorColor;
+ const ColorRGBAf selectionColor;
+ int isHover;
+ int isActive;
+ int on;
+ int hasKeyboardFocus;
+ int drawSelectionAsComposition;
+};
+
+// How image and text is placed inside [[GUIStyle]].
+ENUM ImagePosition
+ // Image is to the left of the text.
+ ImageLeft = 0,
+ // Image is above the text.
+ ImageAbove = 1,
+ // Only the image is displayed.
+ ImageOnly = 2,
+ // Only the text is displayed.
+ TextOnly = 3
+END
+
+
+BEGIN DOC
+Style settings for a GUI element.
+This class contains all information for how a gui element should be rendered. It contains information for font, icon placement, background images, and spacing.
+It does /not/ contain information for what it contains - just defines how any text rendered with this style should be displayed.
+It does /not/ define what interactions can occur with the element, but defines the display settings commonly used in by the interactions.
+
+The settings of a [[GUIStyle]]. It is modelled after a CSS Style. It contains settings for the following items:
+<dl>
+<dt>Background images</dt>
+<dd> These are rendered behind the contents. Different images can be assigned for normal display, when the user hovers the mouse over the element,
+ when the user presses it down - as well as alternatives for when the element has been turned on - like toggle butons do. Below, these are referred to as style /states/.
+ SA: ::ref::normal, ::ref::hover, ::ref::active, ::ref::onNormal, ::ref::onHover, ::ref::onActive - these contain the background image & text color properties for each state.</dd>
+<dt>Text rendering</dt>
+<dd> The style can define a font for text rendering, as well as alignment, wordwrap and clipping settings. It also defines colors for the text in the various states of the style element
+ SA: ::ref::font, ::ref::alignment, ::ref::wordWrap, ::ref::normal, ::ref::hover, ::ref::active, ::ref::onHover, ::ref::onActive</dd>
+<dt>Icon Placement</dt>
+<dd> GUIStyles can be rendered with either text, and icon or both. The GUIStyle defines where these two are rendered in relation to each other (or can force it to only display one of them).
+ SA: ::ref::imagePosition</dd>
+<dt>Sizing and Spacing Options</dd>
+<dd> GUIStyles contain padding, margins and borders. These corresponds loosely to the similar named CSS properties. A GUIStyle can optionally define a fixed width and height.
+ SA: ::ref::margin, ::ref::padding, ::ref::border, ::ref::fixedWidth, ::ref::fixedHeight</dd>
+</dl>
+
+
+END DOC
+
+C++RAW
+ static void CleanupGUIStyle(void* guiStyle){ delete ((GUIStyle*)guiStyle); }
+CSRAW #if !UNITY_WINRT
+CSRAW [StructLayout (LayoutKind.Sequential)]
+CSRAW #endif
+CSRAW [System.Serializable]
+CLASS GUIStyle
+
+CSRAW
+ // Constructor for empty GUIStyle.
+ public GUIStyle () {
+ Init ();
+ }
+
+ // Constructs GUIStyle identical to given other GUIStyle.
+ public GUIStyle (GUIStyle other) {
+ InitCopy (other);
+ }
+
+ ~GUIStyle () {
+ Cleanup ();
+ }
+
+ internal void CreateObjectReferences () {
+ m_FontInternal = GetFontInternal();
+ normal.RefreshAssetReference ();
+ hover.RefreshAssetReference ();
+ active.RefreshAssetReference ();
+ focused.RefreshAssetReference ();
+ onNormal.RefreshAssetReference ();
+ onHover.RefreshAssetReference ();
+ onActive.RefreshAssetReference ();
+ onFocused.RefreshAssetReference ();
+ }
+
+ [System.NonSerialized] [NotRenamed]
+ internal IntPtr m_Ptr;
+
+ [System.NonSerialized]
+ GUIStyleState m_Normal, m_Hover, m_Active, m_Focused, m_OnNormal, m_OnHover, m_OnActive, m_OnFocused;
+
+ [System.NonSerialized]
+ RectOffset m_Border, m_Padding, m_Margin, m_Overflow;
+
+CSRAW #pragma warning disable 414
+ [System.NonSerialized]
+ private Font m_FontInternal;
+CSRAW #pragma warning restore 414
+
+ THREAD_SAFE
+ CUSTOM private void Init () {
+ self.SetPtr(new GUIStyle(), CleanupGUIStyle);
+ }
+
+ THREAD_SAFE
+ CUSTOM private void InitCopy (GUIStyle other) {
+ self.SetPtr(new GUIStyle (*other), CleanupGUIStyle);
+ }
+
+ THREAD_SAFE
+ CUSTOM private void Cleanup () {
+ CleanupGUIStyle(self.GetPtr());
+ }
+
+ // The name of this GUIStyle. Used for getting them based on name....
+ CUSTOM_PROP string name
+ {
+ return scripting_string_new(self->m_Name);
+ }
+ { self->m_Name = value.AsUTF8(); }
+
+ // Rendering settings for when the component is displayed normally.
+ CSRAW public GUIStyleState normal { get {
+ if (m_Normal == null)
+ m_Normal = new GUIStyleState (this, GetStyleStatePtr (0));
+ return m_Normal;
+ }
+ set { AssignStyleState (0, value.m_Ptr); }
+ }
+
+ // Rendering settings for when the mouse is hovering over the control
+ CSRAW public GUIStyleState hover { get {
+ if (m_Hover == null)
+ m_Hover = new GUIStyleState (this, GetStyleStatePtr (1));
+ return m_Hover;
+ }
+ set { AssignStyleState (1, value.m_Ptr); }
+ }
+
+ // Rendering settings for when the control is pressed down.
+ CSRAW public GUIStyleState active { get {
+ if (m_Active == null)
+ m_Active = new GUIStyleState (this, GetStyleStatePtr (2));
+ return m_Active;
+ }
+ set { AssignStyleState (2, value.m_Ptr); }
+ }
+
+ // Rendering settings for when the control is turned on.
+ CSRAW public GUIStyleState onNormal { get {
+ if (m_OnNormal == null)
+ m_OnNormal = new GUIStyleState (this, GetStyleStatePtr (4));
+ return m_OnNormal;
+ }
+ set { AssignStyleState (4, value.m_Ptr); }
+ }
+
+ // Rendering settings for when the control is turned on and the mouse is hovering it.
+ CSRAW public GUIStyleState onHover { get {
+ if (m_OnHover == null)
+ m_OnHover = new GUIStyleState (this, GetStyleStatePtr (5));
+ return m_OnHover;
+ }
+ set { AssignStyleState (5, value.m_Ptr); }
+ }
+
+ // Rendering settings for when the element is turned on and pressed down.
+ CSRAW public GUIStyleState onActive { get {
+ if (m_OnActive == null)
+ m_OnActive = new GUIStyleState (this, GetStyleStatePtr (6));
+ return m_OnActive;
+ }
+ set { AssignStyleState (6, value.m_Ptr); }
+ }
+
+ // Rendering settings for when the element has keyboard focus.
+ CSRAW public GUIStyleState focused { get {
+ if (m_Focused == null)
+ m_Focused = new GUIStyleState (this, GetStyleStatePtr (3));
+ return m_Focused;
+ }
+ set { AssignStyleState (3, value.m_Ptr); }
+ }
+
+ // Rendering settings for when the element has keyboard and is turned on.
+ CSRAW public GUIStyleState onFocused { get {
+ if (m_OnFocused == null)
+ m_OnFocused = new GUIStyleState (this, GetStyleStatePtr (7));
+ return m_OnFocused;
+ }
+ set { AssignStyleState (7, value.m_Ptr); }
+ }
+
+ CUSTOM private IntPtr GetStyleStatePtr (int idx)
+ {
+ GUIStyleState* gss = &(self->m_Normal);
+ return gss+idx;
+ }
+
+ CUSTOM private void AssignStyleState (int idx, IntPtr srcStyleState)
+ {
+ GUIStyleState* gss = &(self->m_Normal);
+ gss += idx;
+ *gss = *((GUIStyleState*)srcStyleState);
+ }
+
+
+ // RECT OFFSETS
+ // ================================================================================================================================================
+
+
+ // The borders of all background images.
+ CSRAW public RectOffset border { get {
+ if (m_Border == null)
+ m_Border = new RectOffset (this, GetRectOffsetPtr (0));
+ return m_Border;
+ }
+ set { AssignRectOffset (0, value.m_Ptr); }
+ }
+
+ // The margins between elements rendered in this style and any other GUI elements
+ CSRAW public RectOffset margin { get {
+ if (m_Margin == null)
+ m_Margin = new RectOffset (this, GetRectOffsetPtr (1));
+ return m_Margin;
+ }
+ set { AssignRectOffset (1, value.m_Ptr); }
+ }
+
+ // Space from the edge of [[GUIStyle]] to the start of the contents.
+ CSRAW public RectOffset padding { get {
+ if (m_Padding == null)
+ m_Padding = new RectOffset (this, GetRectOffsetPtr (2));
+ return m_Padding;
+ }
+ set { AssignRectOffset (2, value.m_Ptr); }
+ }
+
+ // Extra space to be added to the background image.
+ CSRAW public RectOffset overflow { get {
+ if (m_Overflow == null)
+ m_Overflow = new RectOffset (this, GetRectOffsetPtr (3));
+ return m_Overflow;
+ }
+ set { AssignRectOffset (3, value.m_Ptr); }
+ }
+
+ CUSTOM private IntPtr GetRectOffsetPtr (int idx)
+ {
+ RectOffset* ro = &(self->m_Border);
+ return ro+idx;
+ }
+
+ CUSTOM private void AssignRectOffset (int idx, IntPtr srcRectOffset)
+ {
+ RectOffset* ro = &(self->m_Border);
+ ro += idx;
+ *ro = *((RectOffset*)srcRectOffset);
+ }
+
+
+ // How image and text of the [[GUIContent]] is combined.
+ CUSTOM_PROP ImagePosition imagePosition
+ { return self->m_ImagePosition; }
+ { self->m_ImagePosition = value; }
+
+ // Text alignment.
+ CUSTOM_PROP TextAnchor alignment
+ { return self->m_Alignment; }
+ { self->m_Alignment = value; }
+
+ // Word wrap the text?
+ CUSTOM_PROP bool wordWrap { return self->m_WordWrap; } { self->m_WordWrap = value; }
+
+ // What to do when the contents to be rendered is too large to fit within the area given.
+ CUSTOM_PROP TextClipping clipping { return self->m_Clipping; } { self->m_Clipping = value; }
+
+ // Pixel offset to apply to the content of this GUIstyle
+ CUSTOM_PROP Vector2 contentOffset { return self->m_ContentOffset; } { self->m_ContentOffset = value; }
+
+ // *undocumented* Clip offset to apply to the content of this GUIstyle
+ OBSOLETE warning Don't use clipOffset - put things inside begingroup instead. This functionality will be removed in a later version.
+ CSRAW public Vector2 clipOffset { get { return Internal_clipOffset; } set {Internal_clipOffset = value; } }
+
+ CUSTOM_PROP internal Vector2 Internal_clipOffset { return self->m_ClipOffset; } { self->m_ClipOffset = value; }
+
+ // If non-0, any GUI elements rendered with this style will have the width specified here.
+ CUSTOM_PROP float fixedWidth { return self->m_FixedWidth; } { self->m_FixedWidth = value; }
+
+ // If non-0, any GUI elements rendered with this style will have the height specified here.
+ CUSTOM_PROP float fixedHeight { return self->m_FixedHeight; } { self->m_FixedHeight = value; }
+
+ // Can GUI elements of this style be stretched horizontally for better layouting?
+ CUSTOM_PROP bool stretchWidth { return self->m_StretchWidth; } { self->m_StretchWidth = value; }
+
+ // Can GUI elements of this style be stretched vertically for better layouting?
+ CUSTOM_PROP bool stretchHeight { return self->m_StretchHeight; } { self->m_StretchHeight = value; }
+
+ CUSTOM private static float Internal_GetLineHeight (IntPtr target) {
+ GUIStyle *cache = reinterpret_cast<GUIStyle*> (target);
+ return cache->GetLineHeight ();
+ }
+
+ CUSTOM private void SetFontInternal (Font value) {
+ self->m_Font = (Font*) (value);
+ }
+
+ CUSTOM private Font GetFontInternal () {
+ return Scripting::ScriptingWrapperFor (self->m_Font);
+ }
+
+ // The font to use for rendering. If null, the default font for the current [[GUISkin]] is used instead.
+ CSRAW public Font font {
+ get { return GetFontInternal(); }
+ set { SetFontInternal(value); m_FontInternal = value; }
+ }
+
+ // The font size to use (for dynamic fonts)
+ CUSTOM_PROP int fontSize { return self->m_FontSize; } { self->m_FontSize = value; }
+
+ // The font style to use (for dynamic fonts)
+ CUSTOM_PROP FontStyle fontStyle { return self->m_FontStyle; } { self->m_FontStyle = value; }
+
+ // Enable HTML-style tags for Text Formatting Markup.
+ CUSTOM_PROP bool richText { return self->m_RichText; } { self->m_RichText = value; }
+
+ // The height of one line of text with this style, measured in pixels. (RO)
+ CSRAW public float lineHeight { get { return Mathf.Round (Internal_GetLineHeight (m_Ptr)); } }
+
+
+ // Draw this GUIStyle on to the screen.
+ CSRAW private static void Internal_Draw (IntPtr target, Rect position, GUIContent content, bool isHover, bool isActive, bool on, bool hasKeyboardFocus) {
+ Internal_DrawArguments arguments = new Internal_DrawArguments ();
+ arguments.target = target;
+ arguments.position = position;
+ arguments.isHover = isHover ? 1 : 0;
+ arguments.isActive = isActive ? 1 : 0;
+ arguments.on = on ? 1 : 0;
+ arguments.hasKeyboardFocus = hasKeyboardFocus ? 1 : 0;
+ Internal_Draw (content, ref arguments);
+ }
+
+ // Draw this GUIStyle on to the screen, internal version
+ CUSTOM private static void Internal_Draw (GUIContent content, ref Internal_DrawArguments arguments) {
+ reinterpret_cast<GUIStyle*> (arguments.target)->Draw (GetGUIState(), arguments.position, MonoGUIContentToTempNative(content), arguments.isHover, arguments.isActive, arguments.on, arguments.hasKeyboardFocus);
+ }
+
+ // Draw plain GUIStyle without text nor image.
+ CSRAW public void Draw (Rect position, bool isHover, bool isActive, bool on, bool hasKeyboardFocus) {
+ #if UNITY_EDITOR
+ if (Event.current.type != EventType.Repaint)
+ {
+ Debug.LogError("Style.Draw may not be called if it is not a repaint event");
+ return;
+ }
+ #endif
+
+ Internal_Draw (m_Ptr, position, GUIContent.none, isHover, isActive, on, hasKeyboardFocus);
+ }
+ // Draw the GUIStyle with a text string inside.
+ CSRAW public void Draw (Rect position, string text, bool isHover, bool isActive, bool on, bool hasKeyboardFocus) {
+ #if UNITY_EDITOR
+ if (Event.current.type != EventType.Repaint)
+ {
+ Debug.LogError("Style.Draw may not be called if it is not a repaint event");
+ return;
+ }
+ #endif
+
+ Internal_Draw (m_Ptr, position, GUIContent.Temp(text), isHover, isActive, on, hasKeyboardFocus);
+ }
+ // Draw the GUIStyle with an image inside. If the image is too large to fit within the content area of the style it is scaled down.
+ CSRAW public void Draw (Rect position, Texture image, bool isHover, bool isActive, bool on, bool hasKeyboardFocus) {
+ #if UNITY_EDITOR
+ if (Event.current.type != EventType.Repaint)
+ {
+ Debug.LogError("Style.Draw may not be called if it is not a repaint event");
+ return;
+ }
+ #endif
+
+ Internal_Draw (m_Ptr, position, GUIContent.Temp(image), isHover, isActive, on, hasKeyboardFocus);
+ }
+ // Draw the GUIStyle with text and an image inside. If the image is too large to fit within the content area of the style it is scaled down.
+ CSRAW public void Draw (Rect position, GUIContent content, bool isHover, bool isActive, bool on, bool hasKeyboardFocus) {
+ #if UNITY_EDITOR
+ if (Event.current.type != EventType.Repaint)
+ {
+ Debug.LogError("Style.Draw may not be called if it is not a repaint event");
+ return;
+ }
+ #endif
+
+ Internal_Draw (m_Ptr, position, content, isHover, isActive, on, hasKeyboardFocus);
+
+ #if UNITY_GUI_SUPPORT_TOOLTIP
+ if (content.tooltip != null && content.tooltip != "") {
+ if (isHover || isActive)
+ {
+ GUI.s_EditorTooltip = GUI.s_MouseTooltip = content.tooltip;
+ Vector2 v = GUIUtility.GUIToScreenPoint(new Vector2(position.x, position.y));
+ GUI.s_ToolTipRect = new Rect (v.x, v.y, position.width, position.height);
+ }
+ if (hasKeyboardFocus)
+ GUI.s_KeyTooltip = content.tooltip;
+
+ }
+ #endif
+ }
+
+ CSRAW public void Draw (Rect position, GUIContent content, int controlID, bool on = false)
+ {
+ #if UNITY_EDITOR
+ if (Event.current.type != EventType.Repaint)
+ {
+ Debug.LogError("Style.Draw may not be called if it is not a repaint event.");
+ return;
+ }
+ #endif
+
+ if (content != null)
+ Internal_Draw2 (m_Ptr, position, content, controlID, on);
+ else
+ Debug.LogError("Style.Draw may not be called with GUIContent that is null.");
+ }
+
+ CUSTOM private static void Internal_Draw2 (IntPtr style, Rect position, GUIContent content, int controlID, bool on)
+ {
+ GUIStyle *_style = reinterpret_cast<GUIStyle*>(style);
+ _style->Draw (GetGUIState(), position, MonoGUIContentToTempNative (content), controlID, on);
+ }
+
+#if UNITY_EDITOR
+ // PrefixLabel has to be drawn with an alternative draw mathod.
+ // The normal draw methods use MonoGUIContentToTempNative which means they all share the same temp GUIContent on the native side.
+ // A native IMGUI control such as GUIButton is already using this temp GUIContent when it calls GetControlID, which,
+ // because of the delayed feature in PrefixLabel, can end up calling a style draw function again to draw the PrefixLabel.
+ // This draw call cannot use the same temp GUIContent that is already needed for the GUIButton control itself,
+ // so it has to use this alternative code path that uses a different GUIContent to store the content in.
+ // We can all agree this workaround is not nice at all. But nobody seemed to be able to come up with something better.
+ CSRAW internal void DrawPrefixLabel (Rect position, GUIContent content, int controlID)
+ {
+ if (content != null)
+ Internal_DrawPrefixLabel (m_Ptr, position, content, controlID, false);
+ else
+ Debug.LogError("Style.DrawPrefixLabel may not be called with GUIContent that is null.");
+ }
+
+ C++RAW static GUIContent s_TempPrefixLabelGUIContent;
+
+ CUSTOM private static void Internal_DrawPrefixLabel (IntPtr style, Rect position, GUIContent content, int controlID, bool on)
+ {
+ GUIStyle *_style = reinterpret_cast<GUIStyle*>(style);
+ MonoGUIContentToNative (content, s_TempPrefixLabelGUIContent);
+ _style->Draw (GetGUIState(), position, s_TempPrefixLabelGUIContent, controlID, on);
+ }
+#endif
+
+
+ // Does the ID-based Draw function show keyboard focus? Disabled by windows when they don't have keyboard focus
+ CSRAW internal static bool showKeyboardFocus = true;
+
+ CUSTOM private static float Internal_GetCursorFlashOffset () {
+ return GUIManager::GetCursorFlashTime ();
+ }
+
+ CUSTOM private static void Internal_DrawCursor (IntPtr target, Rect position, GUIContent content, int pos, Color cursorColor)
+ { reinterpret_cast<GUIStyle*> (target)->DrawCursor (GetGUIState(), position, MonoGUIContentToTempNative (content), pos, cursorColor); }
+
+ // Draw this GUIStyle with selected content.
+ CSRAW public void DrawCursor (Rect position, GUIContent content, int controlID, int Character) {
+ Event e = Event.current;
+ if (e.type == EventType.Repaint) {
+
+ // Figure out the cursor color...
+ Color cursorColor = new Color (0,0,0,0);
+ float cursorFlashSpeed = GUI.skin.settings.cursorFlashSpeed;
+ float cursorFlashRel = ((Time.realtimeSinceStartup - GUIStyle.Internal_GetCursorFlashOffset()) % cursorFlashSpeed) / cursorFlashSpeed;
+ if (cursorFlashSpeed == 0 || cursorFlashRel < .5f) {
+ cursorColor = GUI.skin.settings.cursorColor;
+ }
+
+ Internal_DrawCursor (
+ m_Ptr, position, content,
+ Character, cursorColor);
+ }
+ }
+
+
+ CUSTOM private static void Internal_DrawWithTextSelection (GUIContent content, ref Internal_DrawWithTextSelectionArguments arguments)
+ {
+ reinterpret_cast<GUIStyle*> (arguments.target)->DrawWithTextSelection (
+ GetGUIState (),
+ arguments.position,
+ MonoGUIContentToTempNative(content),
+ arguments.isHover,
+ arguments.isActive,
+ arguments.on,
+ arguments.hasKeyboardFocus,
+ arguments.drawSelectionAsComposition,
+ arguments.firstPos,
+ arguments.lastPos,
+ arguments.cursorColor,
+ arguments.selectionColor
+ );
+ }
+
+ CSRAW internal void DrawWithTextSelection (Rect position, GUIContent content, int controlID, int firstSelectedCharacter, int lastSelectedCharacter, bool drawSelectionAsComposition) {
+
+ #if UNITY_EDITOR
+ if (Event.current.type != EventType.Repaint)
+ {
+ Debug.LogError("Style.Draw may not be called if it is not a repaint event");
+ return;
+ }
+ #endif
+
+ Event e = Event.current;
+
+ // Figure out the cursor color...
+ Color cursorColor = new Color (0,0,0,0);
+ float cursorFlashSpeed = GUI.skin.settings.cursorFlashSpeed;
+ float cursorFlashRel = ((Time.realtimeSinceStartup - GUIStyle.Internal_GetCursorFlashOffset()) % cursorFlashSpeed) / cursorFlashSpeed;
+ if (cursorFlashSpeed == 0 || cursorFlashRel < .5f)
+ cursorColor = GUI.skin.settings.cursorColor;
+
+
+ Internal_DrawWithTextSelectionArguments arguments = new Internal_DrawWithTextSelectionArguments ();
+ arguments.target = m_Ptr;
+ arguments.position = position;
+ arguments.firstPos = firstSelectedCharacter;
+ arguments.lastPos = lastSelectedCharacter;
+ arguments.cursorColor = cursorColor;
+ arguments.selectionColor = GUI.skin.settings.selectionColor;
+ arguments.isHover = position.Contains (e.mousePosition) ? 1 : 0;
+ arguments.isActive = controlID == GUIUtility.hotControl ? 1 : 0;
+ arguments.on = 0;
+ arguments.hasKeyboardFocus = controlID == GUIUtility.keyboardControl && showKeyboardFocus ? 1 : 0;
+ arguments.drawSelectionAsComposition = drawSelectionAsComposition ? 1 : 0;
+
+ Internal_DrawWithTextSelection (content, ref arguments);
+ }
+
+ // Draw this GUIStyle with selected content.
+ CSRAW public void DrawWithTextSelection (Rect position, GUIContent content, int controlID, int firstSelectedCharacter, int lastSelectedCharacter) {
+ DrawWithTextSelection (position, content, controlID, firstSelectedCharacter, lastSelectedCharacter, false);
+
+ }
+
+ // Set the default font used if null is used.
+ CUSTOM static internal void SetDefaultFont (Font font)
+ { GUIStyle::SetDefaultFont (font); }
+
+ // Get a named GUI style from the current skin.
+ CSRAW public static implicit operator GUIStyle(string str)
+ {
+ if (GUISkin.current == null)
+ {
+ Debug.LogError ("Unable to use a named GUIStyle without a current skin. Most likely you need to move your GUIStyle initialization code to OnGUI");
+ return GUISkin.error;
+ }
+ return GUISkin.current.GetStyle (str);
+ }
+
+ // Shortcut for an empty GUIStyle.
+ CSRAW public static GUIStyle none { get { if(s_None == null) s_None = new GUIStyle(); return s_None; } }
+ static GUIStyle s_None;
+
+ // Get the pixel position of a given string index.
+ CSRAW public Vector2 GetCursorPixelPosition (Rect position, GUIContent content, int cursorStringIndex)
+ { Vector2 temp; Internal_GetCursorPixelPosition(m_Ptr, position, content, cursorStringIndex, out temp); return temp; }
+
+ // *undoc*
+ CUSTOM internal static void Internal_GetCursorPixelPosition (IntPtr target, Rect position, GUIContent content, int cursorStringIndex, out Vector2 ret)
+ {
+ *ret = reinterpret_cast<GUIStyle*> (target)->GetCursorPixelPosition (position, MonoGUIContentToTempNative (content), cursorStringIndex);
+ }
+
+ // Get the cursor position (indexing into contents.text) when the user clicked at cursorPixelPosition
+ CSRAW public int GetCursorStringIndex (Rect position, GUIContent content, Vector2 cursorPixelPosition)
+ { return Internal_GetCursorStringIndex (m_Ptr, position, content, cursorPixelPosition); }
+
+ // *undoc*
+ CUSTOM internal static int Internal_GetCursorStringIndex (IntPtr target, Rect position, GUIContent content, Vector2 cursorPixelPosition)
+ {
+ return reinterpret_cast<GUIStyle*> (target)->GetCursorStringIndex (position, MonoGUIContentToTempNative (content), cursorPixelPosition);
+ }
+
+
+ // Returns number of characters that can fit within width, returns -1 if fails due to missing font
+ CSRAW internal int GetNumCharactersThatFitWithinWidth (string text, float width)
+ {
+ return Internal_GetNumCharactersThatFitWithinWidth (m_Ptr, text, width);
+ }
+
+ // *undoc*
+ CUSTOM internal static int Internal_GetNumCharactersThatFitWithinWidth (IntPtr target, string text, float width)
+ {
+#if UNITY_FLASH
+ return 5;
+#else
+ return reinterpret_cast<GUIStyle*> (target)->GetNumCharactersThatFitWithinWidth (UTF16String (text.AsUTF8().c_str()), width);
+#endif
+ }
+
+ // Calculate the size of a some content if it is rendered with this style.
+ CSRAW public Vector2 CalcSize (GUIContent content)
+ {
+ Vector2 temp; Internal_CalcSize(m_Ptr, content, out temp);
+ return temp;
+ }
+
+ // *undoc*
+ CUSTOM internal static void Internal_CalcSize (IntPtr target, GUIContent content, out Vector2 ret)
+ {
+ *ret = reinterpret_cast<GUIStyle*> (target)->CalcSize (MonoGUIContentToTempNative (content));
+ }
+
+ // Calculate the size of an element formatted with this style, and a given space to content.
+ CSRAW public Vector2 CalcScreenSize (Vector2 contentSize) {
+ return new Vector2 (
+ (fixedWidth != 0.0f ? fixedWidth : Mathf.Ceil (contentSize.x + padding.left + padding.right)),
+ (fixedHeight != 0.0f ? fixedHeight : Mathf.Ceil (contentSize.y + padding.top + padding.bottom))
+ );
+ }
+
+ // How tall this element will be when rendered with /content/ and a specific /width/.
+ CSRAW public float CalcHeight (GUIContent content, float width) {
+ return Internal_CalcHeight (m_Ptr, content, width);
+ }
+
+ CUSTOM private static float Internal_CalcHeight (IntPtr target, GUIContent content, float width) {
+ return reinterpret_cast<GUIStyle*> (target)->CalcHeight (MonoGUIContentToTempNative (content), width);
+ }
+
+ // *undocumented*
+ CSRAW public bool isHeightDependantOnWidth { get {
+ return fixedHeight == 0 && (wordWrap == true && imagePosition != ImagePosition.ImageOnly);
+ } }
+
+ // Calculate the minimum and maximum widths for this style rendered with /content/.
+ CSRAW public void CalcMinMaxWidth (GUIContent content, out float minWidth, out float maxWidth)
+ {
+ Internal_CalcMinMaxWidth(m_Ptr, content, out minWidth, out maxWidth);
+ }
+ // *undoc*
+ CUSTOM private static void Internal_CalcMinMaxWidth (IntPtr target, GUIContent content, out float minWidth, out float maxWidth)
+ {
+ reinterpret_cast<GUIStyle*> (target)->CalcMinMaxWidth (MonoGUIContentToTempNative (content), minWidth, maxWidth);
+ }
+
+ // *undocumented
+ CSRAW public override string ToString () {
+ return UnityString.Format ("GUIStyle '{0}'", name);
+ }
+
+ C++RAW
+ #undef GET
+END
+
+
+// Different methods for how the GUI system handles text being too large to fit the rectangle allocated.
+ENUM TextClipping
+ // Text flows freely outside the element.
+ Overflow = 0,
+ // Text gets clipped to be inside the element.
+ Clip = 1,
+
+ // Text gets truncated with dots to show it is too long
+ // Truncate = 2
+END
+
+
+CSRAW
+}
diff --git a/Runtime/Export/GUIUtility.txt b/Runtime/Export/GUIUtility.txt
new file mode 100644
index 0000000..99fcbc9
--- /dev/null
+++ b/Runtime/Export/GUIUtility.txt
@@ -0,0 +1,476 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Misc/AssetBundle.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "Runtime/Filters/Misc/Font.h"
+#include "Runtime/Misc/InputEvent.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/IMGUI/GUIManager.h"
+#include "Runtime/Utilities/CopyPaste.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/GUIClip.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+#include "Runtime/IMGUI/GUIWindows.h"
+#include "Runtime/Input/InputManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#if UNITY_EDITOR
+
+#include "Editor/Src/EditorHelper.h"
+#include "Editor/Mono/MonoEditorUtility.h"
+#include "Editor/Src/OptimizedGUIBlock.h"
+#include "Editor/Src/AssetPipeline/AssetDatabase.h"
+#include "Editor/Src/Undo/UndoManager.h"
+
+extern float s_GUIStyleIconSizeX;
+extern float s_GUIStyleIconSizeY;
+#endif
+
+using namespace Unity;
+using namespace std;
+
+CSRAW
+
+// Use this define to debug who grabs and releases hotcontrol
+//#define DEBUG_HOTCONTROL
+
+// Use this define to debug controlID consistency together with 's_LogControlID' (default false) to enable logging in
+// a codepath thats needs tested for consistency. E.g:
+// if (Event.current.rawType == EventType.MouseUp)
+// GUIUtility.s_LogControlID = true;
+// And remember to set s_LogControlID to false at end of secion of interest.
+//#define DEBUG_CONTROLID
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+// Throw this to immediately exit from GUI code.
+// *undocumented*
+CLASS ExitGUIException : System.Exception
+END
+
+// Used by GUIUtility.GetcontrolID to inform the UnityGUI system if a given control can get keyboard focus.
+ENUM FocusType
+ // This control can get keyboard focus on Windows, but not on Mac. Used for buttons, checkboxes and other "pressable" things.
+ Native = 0,
+ // This is a proper keyboard control. It can have input focus on all platforms. Used for TextField and TextArea controls
+ Keyboard = 1,
+ // This control can never recieve keyboard focus.
+ Passive = 2
+END
+
+CSRAW
+
+// Utility class for making new GUI controls.
+NONSEALED_CLASS GUIUtility
+
+ /// *listonly*
+ CSRAW public static int GetControlID (FocusType focus)
+ {
+ return GetControlID (0, focus);
+ }
+ /// *listonly*
+ CUSTOM public static int GetControlID (int hint, FocusType focus)
+ {
+ return GetGUIState().GetControlID (hint, focus);
+ }
+ /// *listonly*
+ CSRAW public static int GetControlID (GUIContent contents,FocusType focus)
+ {
+ return GetControlID (contents.hash, focus);
+ }
+ /// *listonly*
+ CSRAW public static int GetControlID (FocusType focus, Rect position)
+ {
+ return Internal_GetNextControlID2 (0, focus, position);
+ }
+ /// *listonly*
+ CSRAW public static int GetControlID (int hint, FocusType focus, Rect position)
+ {
+ return Internal_GetNextControlID2 (hint, focus, position);
+ }
+ // Get a unique ID for a control.
+ CSRAW static public int GetControlID (GUIContent contents, FocusType focus, Rect position)
+ {
+ return Internal_GetNextControlID2 (contents.hash, focus, position);
+ }
+
+ CUSTOM private static int Internal_GetNextControlID2 (int hint, FocusType focusType, Rect rect)
+ {
+ return GetGUIState().GetControlID (hint, focusType, rect);
+ }
+
+ // Get a state object from a controlID.
+ CSRAW public static object GetStateObject (Type t, int controlID) { return GUIStateObjects.GetStateObject (t, controlID); }
+
+ // Get an existing state object from a controlID.
+ CSRAW public static object QueryStateObject (Type t, int controlID) { return GUIStateObjects.QueryStateObject (t, controlID); }
+
+ CUSTOM static internal int GetPermanentControlID ()
+ {
+ return GetGUIState().m_EternalGUIState->GetNextUniqueID ();
+ }
+
+CSRAW
+ #if DEBUG_HOTCONTROL
+ static public string s_WhoGrabbedHotControl, s_WhoReleasedHotControl;
+ #endif
+
+ #if DEBUG_CONTROLID
+ static public bool s_LogControlID = false;
+ #endif
+
+
+ // The controlID of the current hot control.
+ CSRAW public static int hotControl { get { return Internal_GetHotControl(); }
+ set {
+ #if DEBUG_HOTCONTROL
+ if (value != 0)
+ {
+ s_WhoGrabbedHotControl = StackTraceUtility.ExtractStackTrace();
+ Debug.Log("GRABBED " + s_WhoGrabbedHotControl);
+ }
+ else
+ {
+ s_WhoReleasedHotControl = StackTraceUtility.ExtractStackTrace();
+ Debug.Log("RELEASE: " + s_WhoReleasedHotControl);
+ }
+ #endif
+
+ Internal_SetHotControl (value);
+ } }
+
+ CUSTOM private static int Internal_GetHotControl ()
+ {
+ return IMGUI::GetHotControl (GetGUIState());
+ }
+ CUSTOM private static void Internal_SetHotControl (int value)
+ {
+ IMGUI::SetHotControl (GetGUIState(), value);
+ }
+
+ CUSTOM internal static void UpdateUndoName ()
+ {
+ #if UNITY_EDITOR
+ GetUndoManager().UpdateUndoName ();
+ #endif
+ }
+
+ // The controlID of the control that has keyboard focus.
+ CUSTOM_PROP static int keyboardControl
+ { return IMGUI::GetKeyboardControl (GetGUIState()); }
+ { IMGUI::SetKeyboardControl (GetGUIState(), value); }
+
+// Keep for debugging keyboardControl (prints the callstack when keyboardControl changes)
+// CSRAW public static int keyboardControl
+// {
+// get { return GetKeyboardControl (); }
+// set { Debug.Log ("Set keyboardControl " + value); SetKeyboardControl (value);}
+// }
+// CUSTOM static int GetKeyboardControl () {return IMGUI::GetKeyboardControl (GetGUIState()); }
+// CUSTOM static void SetKeyboardControl (int id) {IMGUI::SetKeyboardControl (GetGUIState(), id); }
+
+ //*undocumented*
+ CSRAW public static void ExitGUI ()
+ {
+ // We have to always throw the ExitGUIException otherwise the exiting out of recursive on GUI will not work.
+ throw new ExitGUIException ();
+ }
+
+ CUSTOM internal static void SetDidGUIWindowsEatLastEvent (bool value) {
+ GUIManager::SetDidGUIWindowsEatLastEvent (value);
+ }
+
+ // Get access to the system-wide pasteboard.
+ CUSTOM_PROP static internal string systemCopyBuffer { return scripting_string_new(GetCopyBuffer ()); } { SetCopyBuffer (value); }
+
+ CSRAW internal static GUISkin GetDefaultSkin() {
+ return Internal_GetDefaultSkin (s_SkinMode);
+ }
+
+ CUSTOM private static GUISkin Internal_GetDefaultSkin (int skinMode) {
+ return GetDefaultSkin (skinMode)->GetInstance ();
+ }
+
+ CUSTOM private static Object Internal_GetBuiltinSkin (int skin) {
+ return GetBuiltinSkin (skin)->GetInstance ();
+ }
+
+ // internal so we can get to it from EditorGUIUtility.GetBuiltinSkin
+ CSRAW internal static GUISkin GetBuiltinSkin (int skin) {
+ return Internal_GetBuiltinSkin (skin) as GUISkin;
+ }
+
+ [NotRenamed] static internal int s_SkinMode;
+ [NotRenamed] static internal int s_OriginalID;
+ CSRAW internal static void BeginGUI (int skinMode, int instanceID, int useGUILayout ) {
+ s_SkinMode = skinMode;
+ s_OriginalID = instanceID;
+ GUI.skin = null;
+
+ // Switch to the correct ID list & clear keyboard loop if we're about to layout (we rebuild it during layout, so we want it cleared beforehand)
+ if (useGUILayout != 0)
+ {
+ GUILayoutUtility.SelectIDList (instanceID, false);
+ GUILayoutUtility.Begin (instanceID);
+ }
+
+ GUI.changed = false;
+ }
+
+ CUSTOM private static void Internal_ExitGUI ()
+ {
+ #if UNITY_EDITOR
+ OptimizedGUIBlock::Abandon ();
+ s_GUIStyleIconSizeX = 0.0f;
+ s_GUIStyleIconSizeY = 0.0f;
+ #endif
+ }
+
+ // End the 2D GUI.
+ CSRAW internal static void EndGUI (int layoutType)
+ {
+ try
+ {
+ if (Event.current.type == EventType.Layout)
+ {
+ switch (layoutType)
+ {
+ case 0: // kNoLayout
+ break;
+ case 1: // kGameLayout
+ GUILayoutUtility.Layout ();
+ break;
+ case 2: // kEditorLayout
+ GUILayoutUtility.LayoutFromEditorWindow ();
+ break;
+ }
+ }
+
+ GUILayoutUtility.SelectIDList (s_OriginalID, false);
+ GUIContent.ClearStaticCache();
+ }
+ finally {
+ Internal_ExitGUI();
+ }
+ }
+
+ // End the 2D GUI.
+ CSRAW internal static bool EndGUIFromException (System.Exception exception) {
+ // Check if the exception is a ExitGUIException
+ if (exception == null)
+ return false;
+ if (exception as ExitGUIException == null
+#if !UNITY_FLASH && !UNITY_WEBGL
+ && exception.InnerException as ExitGUIException == null
+#endif
+ )
+ return false;
+
+ Internal_ExitGUI ();
+
+ return true;
+ }
+
+ // Only allow calling GUI functions from inside OnGUI
+ CSRAW static internal void CheckOnGUI() {
+ if (Internal_GetGUIDepth () <= 0)
+ {
+ throw new ArgumentException("You can only call GUI functions from inside OnGUI.");
+ }
+ }
+
+ CUSTOM static internal int Internal_GetGUIDepth ()
+ {
+ return GetGUIState().m_OnGUIDepth;
+ }
+
+ CUSTOM_PROP static internal bool mouseUsed
+ { return GetGUIState().m_CanvasGUIState.m_IsMouseUsed != 0; }
+ { GetGUIState().m_CanvasGUIState.m_IsMouseUsed = value ? 1 : 0; }
+
+ CSRAW
+
+ static internal Vector2 s_EditorScreenPointOffset = Vector2.zero;
+ static internal bool s_HasKeyboardFocus = false;
+
+ // Convert a point from GUI position to screen space.
+ CSRAW public static Vector2 GUIToScreenPoint (Vector2 guiPoint)
+ {
+ return GUIClip.Unclip (guiPoint) + s_EditorScreenPointOffset;
+ }
+
+ // Convert a rect from GUI position to screen space.
+ CSRAW internal static Rect GUIToScreenRect (Rect guiRect)
+ {
+ Vector2 screenPoint = GUIToScreenPoint (new Vector2 (guiRect.x, guiRect.y));
+ guiRect.x = screenPoint.x;
+ guiRect.y = screenPoint.y;
+ return guiRect;
+ }
+
+ // Convert a point from screen space to GUI position.
+ CSRAW public static Vector2 ScreenToGUIPoint (Vector2 screenPoint)
+ {
+ return GUIClip.Clip (screenPoint) - s_EditorScreenPointOffset;
+ }
+
+ // Convert a rect from screen space to GUI position.
+ CSRAW public static Rect ScreenToGUIRect (Rect screenRect)
+ {
+ Vector2 guiPoint = ScreenToGUIPoint (new Vector2 (screenRect.x, screenRect.y));
+ screenRect.x = guiPoint.x;
+ screenRect.y = guiPoint.y;
+ return screenRect;
+ }
+
+ // Helper function to rotate the GUI around a point.
+ public static void RotateAroundPivot (float angle, Vector2 pivotPoint) {
+ Matrix4x4 mat = GUI.matrix;
+ GUI.matrix = Matrix4x4.identity;
+ Vector2 point = GUIClip.Unclip(pivotPoint);
+ Matrix4x4 newMat = Matrix4x4.TRS (point, Quaternion.Euler (0,0,angle), Vector3.one) * Matrix4x4.TRS (-point, Quaternion.identity, Vector3.one);
+ GUI.matrix = newMat *mat;
+ }
+
+ // Helper function to scale the GUI around a point.
+ public static void ScaleAroundPivot (Vector2 scale, Vector2 pivotPoint) {
+ Matrix4x4 mat = GUI.matrix;
+ Vector2 point = GUIClip.Unclip(pivotPoint);
+ Matrix4x4 newMat = Matrix4x4.TRS (point, Quaternion.identity, new Vector3 (scale.x, scale.y, 1)) * Matrix4x4.TRS (-point, Quaternion.identity, Vector3.one);
+ GUI.matrix = newMat * mat;
+ }
+
+ // Check to see if there's a modal IMGUI window that's currently open
+ CUSTOM_PROP public static bool hasModalWindow
+ {
+ GUIState& state = GetGUIState();
+ return state.m_MultiFrameGUIState.m_Windows != NULL && state.m_MultiFrameGUIState.m_Windows->m_ModalWindow != NULL;
+ }
+
+ //*undocumented*
+ CUSTOM_PROP static internal bool textFieldInput
+ { return GetInputManager().GetTextFieldInput (); }
+ { GetInputManager().SetTextFieldInput (value); }
+END
+
+
+CLASS internal GUIClip
+ // Push a clip rect to the stack with pixel offsets.
+ CUSTOM internal static void Push (Rect screenRect, Vector2 scrollOffset, Vector2 renderOffset, bool resetOffset)
+ {
+ GetGUIState().m_CanvasGUIState.m_GUIClipState.Push (IMGUI::GetCurrentEvent(GetGUIState()), screenRect, scrollOffset, renderOffset, resetOffset);
+ }
+
+ // Removes the topmost clipping rectangle, undoing the effect of the latest GUIClip.Push
+ CUSTOM internal static void Pop ()
+ {
+ GetGUIState().m_CanvasGUIState.m_GUIClipState.Pop (IMGUI::GetCurrentEvent(GetGUIState()));
+ }
+
+ // Get the topmost rectangle
+ CUSTOM internal static Rect GetTopRect ()
+ {
+ return GetGUIState().m_CanvasGUIState.m_GUIClipState.GetTopRect ();
+ }
+
+
+ CUSTOM_PROP static bool enabled
+ { return GetGUIState().m_CanvasGUIState.m_GUIClipState.GetEnabled(); }
+
+
+ // Unclips /pos/ to physical device coordinates.
+ CSRAW static public Vector2 Unclip (Vector2 pos)
+ {
+ Unclip_Vector2(ref pos);
+ return pos;
+ }
+
+ CUSTOM private static void Unclip_Vector2 (ref Vector2 pos)
+ {
+ pos = GetGUIState().m_CanvasGUIState.m_GUIClipState.Unclip (pos);
+ }
+
+ // The topmost physical rect in unclipped coordinates
+ // Used in editor to clip cursor rects inside scrollviews
+ CUSTOM_PROP static Rect topmostRect
+ { return GetGUIState().m_CanvasGUIState.m_GUIClipState.GetTopMostPhysicalRect (); }
+
+
+ // Unclips /rect/ to physical device coordinates.
+ CSRAW public static Rect Unclip (Rect rect)
+ {
+ Unclip_Rect(ref rect);
+ return rect;
+ }
+ CUSTOM private static void Unclip_Rect (ref Rect rect)
+ {
+ rect = GetGUIState().m_CanvasGUIState.m_GUIClipState.Unclip(rect);
+ }
+
+ // Clips /absolutePos/ to drawing coordinates
+ CSRAW static public Vector2 Clip (Vector2 absolutePos)
+ {
+ Clip_Vector2 (ref absolutePos);
+ return absolutePos;
+ }
+
+ CUSTOM private static void Clip_Vector2 (ref Vector2 absolutePos)
+ {
+ absolutePos = GetGUIState().m_CanvasGUIState.m_GUIClipState.Clip(absolutePos);
+ }
+
+ // Convert /absoluteRect/ to drawing coordinates
+ CSRAW static public Rect Clip (Rect absoluteRect)
+ {
+ Internal_Clip_Rect (ref absoluteRect);
+ return absoluteRect;
+ }
+
+ CUSTOM static private void Internal_Clip_Rect (ref Rect absoluteRect)
+ {
+ absoluteRect = GetGUIState().m_CanvasGUIState.m_GUIClipState.Clip (absoluteRect);
+ }
+
+ // Reapply the clipping info.
+ CUSTOM internal static void Reapply ()
+ {
+ GetGUIState().m_CanvasGUIState.m_GUIClipState.Reapply (IMGUI::GetCurrentEvent(GetGUIState()));
+ }
+
+ // Set the GUIMatrix. This is here as this class handles all coordinate transforms anyways.
+ CUSTOM internal static Matrix4x4 GetMatrix()
+ {
+ return GetGUIState().m_CanvasGUIState.m_GUIClipState.GetMatrix();
+ }
+
+ CUSTOM internal static void SetMatrix (Matrix4x4 m)
+ {
+ GetGUIState().m_CanvasGUIState.m_GUIClipState.SetMatrix(IMGUI::GetCurrentEvent(GetGUIState()), m);
+ }
+
+ // The visible rectangle.
+ CUSTOM_PROP static Rect visibleRect
+ { return GetGUIState().m_CanvasGUIState.m_GUIClipState.GetVisibleRect (); }
+
+ CSRAW public static Vector2 GetAbsoluteMousePosition ()
+ {
+ Vector2 vec;
+ Internal_GetAbsoluteMousePosition(out vec);
+ return vec;
+ }
+ CUSTOM private static void Internal_GetAbsoluteMousePosition (out Vector2 output)
+ {
+ *output = GetGUIState().m_CanvasGUIState.m_GUIClipState.GetAbsoluteMousePosition();
+ }
+END
+
+CSRAW }
diff --git a/Runtime/Export/GameCenterServices.cs b/Runtime/Export/GameCenterServices.cs
new file mode 100644
index 0000000..2a140c3
--- /dev/null
+++ b/Runtime/Export/GameCenterServices.cs
@@ -0,0 +1,451 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace UnityEngine.SocialPlatforms.GameCenter
+{
+#if ENABLE_GAMECENTER
+ using UnityEngine.SocialPlatforms.Impl;
+
+ [StructLayout (LayoutKind.Sequential)]
+ internal struct GcUserProfileData
+ {
+ public string userName;
+ public string userID;
+ public int isFriend;
+ public Texture2D image;
+
+ public UserProfile ToUserProfile()
+ {
+ return new UserProfile(userName, userID, (isFriend==1?true:false), UserState.Offline, image);
+ }
+
+ public void AddToArray(ref UserProfile[] array, int number)
+ {
+ if (array.Length > number && number >= 0)
+ array[number] = ToUserProfile();
+ else
+ Debug.Log("Index number out of bounds when setting user data");
+ }
+ }
+
+ [StructLayout (LayoutKind.Sequential)]
+ internal struct GcAchievementDescriptionData
+ {
+ public string m_Identifier;
+ public string m_Title;
+ public Texture2D m_Image;
+ public string m_AchievedDescription;
+ public string m_UnachievedDescription;
+ public int m_Hidden;
+ public int m_Points;
+
+ public AchievementDescription ToAchievementDescription()
+ {
+ return new AchievementDescription(
+ m_Identifier,
+ m_Title,
+ m_Image,
+ m_AchievedDescription,
+ m_UnachievedDescription,
+ m_Hidden == 0 ? false : true,
+ m_Points);
+ }
+ }
+
+ [StructLayout (LayoutKind.Sequential)]
+ internal struct GcAchievementData
+ {
+ public string m_Identifier;
+ public double m_PercentCompleted;
+ public int m_Completed;
+ public int m_Hidden;
+ public int m_LastReportedDate;
+
+ public Achievement ToAchievement()
+ {
+ return new Achievement(
+ m_Identifier,
+ m_PercentCompleted,
+ m_Completed == 0 ? false : true,
+ m_Hidden == 0 ? false : true,
+ new DateTime(1970, 1, 1, 0, 0, 0, 0).AddSeconds(m_LastReportedDate));
+ }
+ }
+
+ [StructLayout (LayoutKind.Sequential)]
+ internal struct GcScoreData
+ {
+ public string m_Category;
+ public int m_ValueLow;
+ public int m_ValueHigh;
+ public int m_Date;
+ public string m_FormattedValue;
+ public string m_PlayerID;
+ public int m_Rank;
+
+ public Score ToScore()
+ {
+ return new Score(
+ m_Category,
+ (((Int64)m_ValueHigh) << 32) + m_ValueLow,
+ m_PlayerID,
+ new DateTime(1970, 1, 1, 0, 0, 0, 0).AddSeconds(m_Date),
+ m_FormattedValue,
+ m_Rank);
+ }
+ }
+
+ public partial class GameCenterPlatform : ISocialPlatform
+ {
+ static Action<bool> s_AuthenticateCallback;
+ static Action<bool> s_FriendsCallback;
+ static Action<IAchievementDescription[]> s_AchievementDescriptionLoaderCallback;
+ static Action<IAchievement[]> s_AchievementLoaderCallback;
+ static Action<bool> s_ProgressCallback;
+ static Action<bool> s_ScoreCallback;
+ static Action<IScore[]> s_ScoreLoaderCallback;
+ static Action<bool> s_LeaderboardCallback;
+ static Action<IUserProfile[]> s_UsersCallback;
+ static AchievementDescription[] s_adCache = new AchievementDescription[0];
+ static UserProfile[] s_friends = new UserProfile[0];
+ static UserProfile[] s_users = new UserProfile[0];
+ static Action<bool> s_ResetAchievements;
+ private static LocalUser m_LocalUser;
+ private static List<GcLeaderboard> m_GcBoards = new List<GcLeaderboard>();
+
+ static void ClearAchievementDescriptions(int size)
+ {
+ if (s_adCache == null || s_adCache.Length != size)
+ s_adCache = new AchievementDescription[size];
+ }
+
+ static void SetAchievementDescription(GcAchievementDescriptionData data, int number)
+ {
+ s_adCache[number] = data.ToAchievementDescription();
+ }
+
+ static void SetAchievementDescriptionImage(Texture2D texture, int number)
+ {
+ if (s_adCache.Length <= number || number < 0)
+ {
+ Debug.Log("Achievement description number out of bounds when setting image");
+ return;
+ }
+ s_adCache[number].SetImage(texture);
+ }
+
+ static void TriggerAchievementDescriptionCallback()
+ {
+ if (s_AchievementDescriptionLoaderCallback != null && s_adCache != null)
+ {
+ if (s_adCache.Length == 0)
+ Debug.Log("No achivevement descriptions returned");
+ s_AchievementDescriptionLoaderCallback(s_adCache);
+ }
+ }
+
+ static void AuthenticateCallbackWrapper(int result)
+ {
+ if (s_AuthenticateCallback != null)
+ {
+ PopulateLocalUser();
+ s_AuthenticateCallback((result == 1 ? true : false));
+ }
+ }
+
+ static void ClearFriends(int size)
+ {
+ SafeClearArray(ref s_friends, size);
+ }
+
+ static void SetFriends(GcUserProfileData data, int number)
+ {
+ data.AddToArray(ref s_friends, number);
+ }
+
+ static void SetFriendImage(Texture2D texture, int number)
+ {
+ SafeSetUserImage(ref s_friends, texture, number);
+ }
+
+ static void TriggerFriendsCallbackWrapper(int result)
+ {
+ if (s_friends != null)
+ m_LocalUser.SetFriends(s_friends);
+ if (s_FriendsCallback != null)
+ s_FriendsCallback((result == 1 ? true : false));
+ }
+
+ static void AchievementCallbackWrapper(GcAchievementData[] result)
+ {
+ if (s_AchievementLoaderCallback != null)
+ {
+ if (result.Length == 0)
+ Debug.Log("No achivevements returned");
+ Achievement[] migrated = new Achievement[result.Length];
+ for (int i = 0; i < result.Length; ++i)
+ migrated[i] = result[i].ToAchievement();
+ s_AchievementLoaderCallback(migrated);
+ }
+ }
+
+ static void ProgressCallbackWrapper(bool success)
+ {
+ if (s_ProgressCallback != null)
+ s_ProgressCallback(success);
+ }
+
+ static void ScoreCallbackWrapper(bool success)
+ {
+ if (s_ScoreCallback != null)
+ s_ScoreCallback(success);
+ }
+
+ static void ScoreLoaderCallbackWrapper(GcScoreData[] result)
+ {
+ if (s_ScoreLoaderCallback != null)
+ {
+ Score[] migrated = new Score[result.Length];
+ for (int i = 0; i < result.Length; ++i)
+ migrated[i] = result[i].ToScore();
+ s_ScoreLoaderCallback(migrated);
+ }
+ }
+
+ void ISocialPlatform.LoadFriends(ILocalUser user, Action<bool> callback)
+ {
+ s_FriendsCallback = callback;
+ Internal_LoadFriends();
+ }
+
+ void ISocialPlatform.Authenticate(ILocalUser user, Action<bool> callback)
+ {
+ s_AuthenticateCallback = callback;
+ Internal_Authenticate();
+ }
+
+ public ILocalUser localUser
+ {
+ get
+ {
+ if (m_LocalUser == null)
+ m_LocalUser = new LocalUser();
+
+ if (Internal_Authenticated() && m_LocalUser.id == "0")
+ PopulateLocalUser();
+ return m_LocalUser;
+ }
+ }
+
+ private static void PopulateLocalUser()
+ {
+ m_LocalUser.SetAuthenticated(Internal_Authenticated());
+ m_LocalUser.SetUserName(Internal_UserName());
+ m_LocalUser.SetUserID(Internal_UserID());
+ m_LocalUser.SetUnderage(Internal_Underage());
+ m_LocalUser.SetImage(Internal_UserImage());
+ }
+
+ public void LoadAchievementDescriptions(Action<IAchievementDescription[]> callback)
+ {
+ if (!VerifyAuthentication())
+ {
+ callback(new AchievementDescription[0]);
+ return;
+ }
+ s_AchievementDescriptionLoaderCallback = callback;
+ Internal_LoadAchievementDescriptions();
+ }
+
+ // TODO: This doesn't really work with the static callback wrapper, multiple progresses
+ // could be reported at the same time.
+ public void ReportProgress(string id, double progress, Action<bool> callback)
+ {
+ if (!VerifyAuthentication())
+ {
+ callback(false);
+ return;
+ }
+ s_ProgressCallback = callback;
+ Internal_ReportProgress(id, progress);
+ }
+
+ public void LoadAchievements(Action<IAchievement[]> callback)
+ {
+ if (!VerifyAuthentication())
+ {
+ callback(new Achievement[0]);
+ return;
+ }
+ s_AchievementLoaderCallback = callback;
+ Internal_LoadAchievements();
+ }
+
+ public void ReportScore(Int64 score, string board, Action<bool> callback)
+ {
+ if (!VerifyAuthentication())
+ {
+ callback(false);
+ return;
+ }
+ s_ScoreCallback = callback;
+ Internal_ReportScore(score, board);
+ }
+
+ public void LoadScores(string category, Action<IScore[]> callback)
+ {
+ if (!VerifyAuthentication())
+ {
+ callback(new Score[0]);
+ return;
+ }
+ s_ScoreLoaderCallback = callback;
+ Internal_LoadScores(category);
+ }
+
+ public void LoadScores(ILeaderboard board, Action<bool> callback)
+ {
+ if (!VerifyAuthentication())
+ {
+ callback(false);
+ return;
+ }
+ s_LeaderboardCallback = callback;
+ Leaderboard genericBoard = (Leaderboard)board;
+ GcLeaderboard gcBoard = new GcLeaderboard(genericBoard);
+ m_GcBoards.Add(gcBoard);
+ if (genericBoard.GetUserFilter().Length > 0)
+ gcBoard.Internal_LoadScoresWithUsers(board.id, (int)board.timeScope, genericBoard.GetUserFilter());
+ else
+ gcBoard.Internal_LoadScores(board.id, board.range.from, board.range.count, (int)board.userScope, (int)board.timeScope);
+ }
+
+ static void LeaderboardCallbackWrapper(bool success)
+ {
+ if (s_LeaderboardCallback != null)
+ s_LeaderboardCallback(success);
+ }
+
+ public bool GetLoading(ILeaderboard board)
+ {
+ if (!VerifyAuthentication()) return false;
+ foreach (GcLeaderboard gcBoard in m_GcBoards)
+ if (gcBoard.Contains((Leaderboard)board))
+ return gcBoard.Loading();
+ return false;
+ }
+
+ private bool VerifyAuthentication()
+ {
+ if (!localUser.authenticated)
+ {
+ Debug.Log ("Must authenticate first");
+ return false;
+ }
+ return true;
+ }
+
+ public void ShowAchievementsUI()
+ {
+ Internal_ShowAchievementsUI();
+ }
+
+ public void ShowLeaderboardUI()
+ {
+ Internal_ShowLeaderboardUI();
+ }
+
+ static void ClearUsers(int size)
+ {
+ SafeClearArray(ref s_users, size);
+ }
+
+ static void SetUser(GcUserProfileData data, int number)
+ {
+ data.AddToArray(ref s_users, number);
+ }
+
+ static void SetUserImage(Texture2D texture, int number)
+ {
+ SafeSetUserImage(ref s_users, texture, number);
+ }
+
+ static void TriggerUsersCallbackWrapper()
+ {
+ if (s_UsersCallback != null)
+ s_UsersCallback(s_users);
+ }
+
+ public void LoadUsers(string[] userIds, Action<IUserProfile[]> callback)
+ {
+ if (!VerifyAuthentication())
+ {
+ callback(new UserProfile[0]);
+ return;
+ }
+ s_UsersCallback = callback;
+ Internal_LoadUsers(userIds);
+ }
+
+ private static void SafeSetUserImage(ref UserProfile[] array, Texture2D texture, int number)
+ {
+ if (array.Length <= number || number < 0)
+ {
+ Debug.Log("Invalid texture when setting user image");
+ texture = new Texture2D(76, 76);
+ }
+ if (array.Length > number && number >= 0)
+ array[number].SetImage(texture);
+ else
+ Debug.Log("User number out of bounds when setting image");
+ }
+
+ private static void SafeClearArray(ref UserProfile[] array, int size)
+ {
+ if (array == null || array.Length != size)
+ array = new UserProfile[size];
+ }
+
+ public ILeaderboard CreateLeaderboard()
+ {
+ Leaderboard lb = new Leaderboard();
+ return (ILeaderboard)lb;
+ }
+
+ public IAchievement CreateAchievement()
+ {
+ Achievement achoo = new Achievement();
+ return (IAchievement)achoo;
+ }
+
+ static void TriggerResetAchievementCallback(bool result)
+ {
+ if (s_ResetAchievements != null)
+ s_ResetAchievements(result);
+ }
+
+
+ }
+#else
+ public class GameCenterPlatform : UnityEngine.SocialPlatforms.Local
+ {
+ static public void ResetAllAchievements(Action<bool> callback)
+ {
+ Debug.Log ("ResetAllAchievements - no effect in editor");
+ callback(true);
+ }
+
+ static public void ShowDefaultAchievementCompletionBanner(bool value)
+ {
+ Debug.Log ("ShowDefaultAchievementCompletionBanner - no effect in editor");
+ }
+
+ static public void ShowLeaderboardUI(string leaderboardID, TimeScope timeScope)
+ {
+ Debug.Log ("ShowLeaderboardUI - no effect in editor");
+ }
+ }
+#endif
+}
+
diff --git a/Runtime/Export/GameCenterServices.txt b/Runtime/Export/GameCenterServices.txt
new file mode 100644
index 0000000..b19ad5f
--- /dev/null
+++ b/Runtime/Export/GameCenterServices.txt
@@ -0,0 +1,367 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Utilities/Utility.h"
+#include <vector>
+
+#if ENABLE_GAMECENTER
+#include "External/GameKit/GameCenter.h"
+#endif
+
+
+CSRAW
+using System;
+using System.Runtime.InteropServices;
+
+#if ENABLE_GAMECENTER
+namespace UnityEngine.SocialPlatforms.GameCenter
+{
+ using UnityEngine.SocialPlatforms.Impl;
+
+ /// GameCenter implementation for network services.
+ /// An application bundle ID must be registered on iTunes Connect
+ /// before it can access GameCenter. This ID must be properly set in
+ /// the iOS player properties in Unity, or in the Info.plist file inside your
+ /// application bundle for OS X Standalone builds. When debugging you can use the GameCenter
+ /// sandbox (a text displaying this is shown when logging on). You
+ /// must log on in the application to get into sandbox mode, logging
+ /// on in the GameCenter application will always use the production version.
+ ///
+
+ // Remove this note if apple gets to fix it!
+ /// Note: Mac OS X (as of version 10.8.2) has a bug where it will not allow you to enter
+ /// Game Center sandbox mode unless you use the following workaround as suggested by Apple:
+ /// https://devforums.apple.com/message/763000
+ /// It does not seem to be possible to create Game Center sandbox accounts using this workaround,
+ /// though, so if you don't have an existing sandbox account, you need to create one using iOS.
+ ///
+
+ /// For testing Game Center on OS X, you need to first set up your Info.plist file to contain
+ /// the correct application bundle ID and version to match the app set up in iTunes Connect.
+ /// Then, you need to set up the correct entitlements for Game Center. Create an entitlements file
+ /// with the following contents:
+
+ BEGIN EX
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+ <plist version="1.0">
+ <dict>
+ <key>com.apple.security.app-sandbox</key>
+ <true/>
+ <key>com.apple.security.temporary-exception.mach-lookup.global-name</key>
+ <array>
+ <string>com.apple.gamed.osx</string>
+ <string>com.apple.gamed.xpc</string>
+ <string>com.apple.developer.game-center</string>
+ </array>
+ </dict>
+ </plist>
+ END EX
+
+ /// Finally, you need to sign you application using the entitlements file like this:
+
+ BEGIN EX
+ codesign -f -s "<Your code signing certificate>" --entitlements <path to your entitlements file> <path to your created unity standalone app>
+ END EX
+
+ /// When using the GameCenterPlatform class in C# you need to include the
+ /// UnityEngine.SocialPlatforms.GameCenter namespace.
+ ///
+ /// Some things to be aware of when using the generic API:
+ ///
+ /// __Authenticate()__ \\
+ /// If the user is not logged in, a standard GameKit UI is shown
+ /// where he can log on or create a new user. It is recommended
+ /// this is done as early as possible.
+ ///
+ /// __Achievement descriptions and Leaderboards__ \\
+ /// The achivements descriptions and leaderboard configurations can be configured in the
+ /// iTunes Connect portal. Achievements get unique identifiers and the
+ /// leaderboards use category names as identifiers.
+ ///
+ /// __GameCenter Sandbox__ \\
+ /// Development applications use the GameCenter Sandbox. This is a seperate GameCenter
+ /// than the live one, nothing is shared between them. It is recommended that you
+ /// create a seperate user for testing with the GameCenter Sandbox, you should not use
+ /// your real Apple ID for this. You can only log on to the sandbox through a development
+ /// application, make sure you are not logged into GameCenter using the GameCenter app before
+ /// testing begins. You should see ''*** Sandbox ***'' in the login dialog, if you don't see this
+ /// then you are logging on to the real one. Sometime it happens that the OS forgets that the
+ /// application is using the sandbox and you will be logged on to the real one. If the application
+ /// has not be submitted to Apple already then this will probably result in an error. To fix this
+ /// all that needs to be done is to delete the app and redeploy with Xcode. To make another apple
+ /// ID a friend of a sandbox user it needs to be a sandbox user as well.
+ ///
+ /// If you start getting errors when accessing GameCenter stating that the
+ /// application is not recognized you'll need to delete the application complately and re-deploy.
+ /// Make sure you are not logged on when starting the newly installed application again.
+ CONDITIONAL ENABLE_GAMECENTER
+ CLASS GameCenterPlatform : ISocialPlatform
+
+ CUSTOM internal static void Internal_Authenticate()
+ {
+ #if ENABLE_GAMECENTER
+ GameCenter::GcLocalUser::GetInstance()->Authenticate();
+ #endif
+ }
+
+ CUSTOM internal static bool Internal_Authenticated()
+ {
+ #if ENABLE_GAMECENTER
+ return GameCenter::GcLocalUser::GetInstance()->GetAuthenticated();
+ #else
+ return false;
+ #endif
+ }
+
+ CUSTOM internal static string Internal_UserName()
+ {
+ #if ENABLE_GAMECENTER
+ GameCenter::GcLocalUser *user = GameCenter::GcLocalUser::GetInstance();
+ std::string name = user->GetUserName();
+ return scripting_string_new(name);
+ #else
+ return scripting_string_new("");
+ #endif
+ }
+
+ CUSTOM internal static string Internal_UserID()
+ {
+ #if ENABLE_GAMECENTER
+ return scripting_string_new(GameCenter::GcLocalUser::GetInstance()->GetUserID());
+ #else
+ return scripting_string_new("");
+ #endif
+ }
+
+ CUSTOM internal static bool Internal_Underage()
+ {
+ #if ENABLE_GAMECENTER
+ return GameCenter::GcLocalUser::GetInstance()->GetIsUnderage();
+ #else
+ return false;
+ #endif
+ }
+
+ CUSTOM internal static Texture2D Internal_UserImage()
+ {
+ #if ENABLE_GAMECENTER
+ return GameCenter::GcLocalUser::GetInstance()->GetUserImage();
+ #else
+ return SCRIPTING_NULL;
+ #endif
+ }
+
+ CUSTOM internal static void Internal_LoadFriends()
+ {
+ #if ENABLE_GAMECENTER
+ GameCenter::GcLocalUser::GetInstance()->LoadFriends();
+ #endif
+ }
+
+ CUSTOM internal static void Internal_LoadAchievementDescriptions()
+ {
+ #if ENABLE_GAMECENTER
+ GameCenter::GcAchievementDescription::LoadAchievementDescriptions();
+ #endif
+ }
+
+ CUSTOM internal static void Internal_LoadAchievements()
+ {
+ #if ENABLE_GAMECENTER
+ GameCenter::GcAchievement::LoadAchievements();
+ #endif
+ }
+
+ CUSTOM internal static void Internal_ReportProgress(string id, double progress)
+ {
+ #if ENABLE_GAMECENTER
+ GameCenter::GcAchievement::ReportProgress(id, progress);
+ #endif
+ }
+
+ CUSTOM internal static void Internal_ReportScore(Int64 score, string category)
+ {
+ #if ENABLE_GAMECENTER
+ GameCenter::GcScore::ReportScore(score, category);
+ #endif
+ }
+
+ CUSTOM internal static void Internal_LoadScores(string category)
+ {
+ #if ENABLE_GAMECENTER
+ GameCenter::GcScore::LoadScores(category);
+ #endif
+ }
+
+ CUSTOM internal static void Internal_ShowAchievementsUI()
+ {
+ #if ENABLE_GAMECENTER
+ GameCenter::GcAchievementDescription::ShowAchievementsUI();
+ #endif
+ }
+
+ CUSTOM internal static void Internal_ShowLeaderboardUI()
+ {
+ #if ENABLE_GAMECENTER
+ GameCenter::GcLeaderboard::ShowLeaderboardUI();
+ #endif
+ }
+
+ CUSTOM internal static void Internal_LoadUsers(string[] userIds)
+ {
+ #if ENABLE_GAMECENTER
+ GameCenter::GcLocalUser::LoadUsers(userIds);
+ #endif
+ }
+
+ CUSTOM internal static void Internal_ResetAllAchievements()
+ {
+ #if ENABLE_GAMECENTER
+ GameCenter::GcAchievement::ResetAllAchievements();
+ #endif
+ }
+
+ CUSTOM internal static void Internal_ShowDefaultAchievementBanner(bool value)
+ {
+ #if ENABLE_GAMECENTER
+ GameCenter::GcAchievement::ShowDefaultAchievementBanner(value);
+ #endif
+ }
+
+ // Reset all the achievements for the local user.
+ CSRAW static public void ResetAllAchievements(Action<bool> callback)
+ {
+ s_ResetAchievements = callback;
+ Internal_ResetAllAchievements();
+ }
+
+ // Show the default iOS banner when achievements are completed.
+ CSRAW static public void ShowDefaultAchievementCompletionBanner(bool value)
+ {
+ Internal_ShowDefaultAchievementBanner(value);
+ }
+
+ // Show the leaderboard UI with a specific leaderboard shown initially with a specific time scope selected.
+ CSRAW static public void ShowLeaderboardUI(string leaderboardID, TimeScope timeScope)
+ {
+ Internal_ShowSpecificLeaderboardUI(leaderboardID, (int)timeScope);
+ }
+
+ CUSTOM internal static void Internal_ShowSpecificLeaderboardUI(string leaderboardID, int timeScope)
+ {
+ #if ENABLE_GAMECENTER
+ GameCenter::GcLeaderboard::ShowLeaderboardUI(leaderboardID, timeScope);
+ #endif
+ }
+ END
+
+ // This class cannot inherit from the generic Leaderboard class as it will break the marshaling of the pointer
+ // *undocumented*
+ CSRAW [StructLayout (LayoutKind.Sequential)]
+ CONDITIONAL ENABLE_GAMECENTER
+ CLASS internal GcLeaderboard
+ C++RAW
+ #if ENABLE_GAMECENTER
+ struct GcLeaderboardToMono
+ {
+ GameCenter::GcLeaderboard* leaderboard;
+ ScriptingObjectPtr genericLeaderboard;
+ };
+ #endif
+
+ CSRAW
+ private IntPtr m_InternalLeaderboard;
+ Leaderboard m_GenericLeaderboard;
+
+ internal GcLeaderboard(Leaderboard board)
+ {
+ m_GenericLeaderboard = board;
+ }
+
+ ~GcLeaderboard()
+ {
+ Dispose();
+ }
+
+ internal bool Contains(Leaderboard board)
+ {
+ return m_GenericLeaderboard == board;
+ }
+
+ internal void SetScores(GcScoreData[] scoreDatas)
+ {
+ if (m_GenericLeaderboard != null)
+ {
+ Score[] scores = new Score[scoreDatas.Length];
+ for(int i = 0; i < scoreDatas.Length; ++i)
+ scores[i] = scoreDatas[i].ToScore();
+ m_GenericLeaderboard.SetScores(scores);
+ }
+ }
+
+ internal void SetLocalScore(GcScoreData scoreData)
+ {
+ if (m_GenericLeaderboard != null)
+ m_GenericLeaderboard.SetLocalUserScore(scoreData.ToScore());
+ }
+
+ internal void SetMaxRange(uint maxRange)
+ {
+ if (m_GenericLeaderboard != null)
+ m_GenericLeaderboard.SetMaxRange(maxRange);
+ }
+
+ internal void SetTitle(string title)
+ {
+ if (m_GenericLeaderboard != null)
+ m_GenericLeaderboard.SetTitle(title);
+ }
+
+ CUSTOM internal void Internal_LoadScores(string category, int from, int count, int playerScope, int timeScope)
+ {
+ #if ENABLE_GAMECENTER
+ GcLeaderboardToMono& monoBoard = ExtractMonoObjectData<GcLeaderboardToMono>(self);
+ monoBoard.leaderboard = new GameCenter::GcLeaderboard();
+ monoBoard.leaderboard->Create();
+ monoBoard.leaderboard->RegisterManagedSelf(self);
+ monoBoard.leaderboard->LoadScores(category, from, count, playerScope, timeScope);
+ #endif
+ }
+
+ CUSTOM internal void Internal_LoadScoresWithUsers(string category, int timeScope, string[] userIDs)
+ {
+ #if ENABLE_GAMECENTER
+ GcLeaderboardToMono& monoBoard = ExtractMonoObjectData<GcLeaderboardToMono>(self);
+ monoBoard.leaderboard = new GameCenter::GcLeaderboard();
+ monoBoard.leaderboard->Create(userIDs);
+ monoBoard.leaderboard->RegisterManagedSelf(self);
+ monoBoard.leaderboard->LoadScores(category, 1, 10, 0, timeScope);
+ #endif
+ }
+
+ CUSTOM internal bool Loading()
+ {
+ #if ENABLE_GAMECENTER
+ GcLeaderboardToMono& monoBoard = ExtractMonoObjectData<GcLeaderboardToMono>(self);
+ return monoBoard.leaderboard->Loading();
+ #else
+ return false;
+ #endif
+ }
+
+ THREAD_SAFE
+ CUSTOM internal void Dispose()
+ {
+ #if ENABLE_GAMECENTER
+ GcLeaderboardToMono& monoBoard = ExtractMonoObjectData<GcLeaderboardToMono>(self);
+ delete monoBoard.leaderboard;
+ monoBoard.leaderboard = NULL;
+ #endif
+ }
+ END
+
+CSRAW
+}
+#endif
diff --git a/Runtime/Export/GameObjectExport.cpp b/Runtime/Export/GameObjectExport.cpp
new file mode 100644
index 0000000..1613f56
--- /dev/null
+++ b/Runtime/Export/GameObjectExport.cpp
@@ -0,0 +1,89 @@
+#include "UnityPrefix.h"
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/Scripting.h"
+
+#if ENABLE_SCRIPTING
+
+GameObject* MonoCreateGameObject (const char* name)
+{
+ string cname;
+ if (!name)
+ {
+ cname = "New Game Object";
+ }
+ else
+ {
+ cname = name;
+ }
+ return &CreateGameObject (cname, "Transform", NULL);
+}
+
+ScriptingObjectPtr MonoAddComponent (GameObject& go, const char* name)
+{
+ string error;
+ Unity::Component* component = AddComponent (go, name, &error);
+
+ if (component)
+ return Scripting::ScriptingWrapperFor (component);
+ else
+ {
+ LogStringObject (error, &go);
+ return SCRIPTING_NULL;
+ }
+}
+
+static bool IsNonMonoBehaviourUnityEngineType(ScriptingClassPtr klass)
+{
+ return !scripting_class_is_subclass_of(klass, MONO_COMMON.monoBehaviour);
+}
+
+ScriptingObjectPtr MonoAddComponentWithType (GameObject& go, ScriptingObjectPtr systemTypeInstance, bool dontShareMonoScript)
+{
+ string error;
+
+
+ Unity::Component* component = NULL;
+ ScriptingClassPtr klass = GetScriptingTypeRegistry().GetType(systemTypeInstance);
+
+ if (klass == SCRIPTING_NULL)
+ {
+#if MONO_QUALITY_ERRORS
+ ScriptWarning("AddComponent asking for invalid type", &go);
+#endif
+ return SCRIPTING_NULL;
+ }
+ int instanceID = go.GetInstanceID();
+ if (IsNonMonoBehaviourUnityEngineType(klass))
+ {
+ int classID = Object::StringToClassID (scripting_class_get_name (klass));
+ component = AddComponent (go, classID, NULL, &error);
+ }
+ else
+ {
+ MonoScript* script = NULL;
+ if (!dontShareMonoScript)
+ script = GetMonoScriptManager().FindRuntimeScript (klass);
+ if (!script)
+ script = CreateMonoScriptFromScriptingType(klass);
+ if (script)
+ component = AddComponent (go, ClassID (MonoBehaviour), script, &error);
+ }
+
+ if (component)
+ return Scripting::ScriptingWrapperFor(component);
+ else
+ {
+ // Check if the object is still valid ( could have been destroyed in Awake.)
+ LogStringObject (error, PPtr<Object> (instanceID));
+ return SCRIPTING_NULL;
+ }
+}
+#endif
diff --git a/Runtime/Export/GameObjectExport.h b/Runtime/Export/GameObjectExport.h
new file mode 100644
index 0000000..69e07f4
--- /dev/null
+++ b/Runtime/Export/GameObjectExport.h
@@ -0,0 +1,17 @@
+#ifndef _GAMEOBJECTEXPORT_H_
+#define _GAMEOBJECTEXPORT_H_
+
+#include "UnityPrefix.h"
+
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+
+namespace Unity
+{
+ class GameObject;
+}
+
+ScriptingObjectPtr MonoAddComponent(Unity::GameObject& go, const char* name);
+ScriptingObjectPtr MonoAddComponentWithType(Unity::GameObject& go, ScriptingObjectPtr reflectionTypeObject, bool dontShareMonoScript = false);
+Unity::GameObject* MonoCreateGameObject(const char* name);
+
+#endif
diff --git a/Runtime/Export/GizmoBindings.txt b/Runtime/Export/GizmoBindings.txt
new file mode 100644
index 0000000..ed3266c
--- /dev/null
+++ b/Runtime/Export/GizmoBindings.txt
@@ -0,0 +1,180 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Geometry/Ray.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Graphics/Texture.h"
+#include "Runtime/Filters/Misc/Font.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/IMGUI/TextUtil.h"
+#include "Runtime/Camera/RenderLayers/GUITexture.h"
+#include "Runtime/Misc/InputEvent.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/Gizmos/GizmoManager.h"
+#include "Editor/Src/Gizmos/GizmoUtil.h"
+#include "Editor/Src/Gizmos/GizmoRenderer.h"
+#endif
+
+using namespace Unity;
+
+/*
+ Mono defines a bool as either 1 or 2 bytes.
+ On windows a bool on the C++ side needs to be 2 bytes.
+ We use the typemap to map bool's to short's.
+ When using the C++ keyword and you want to export a bool value
+ to mono you have to use a short on the C++ side.
+*/
+
+
+void PauseEditor ();
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+
+// Gizmos are used to give visual debugging or setup aids in the scene view.
+CLASS Gizmos
+
+ C++RAW
+
+ static void CheckGizmoDrawing ();
+ static void CheckGizmoDrawing ()
+ {
+ #if !GAMERELEASE
+ if (!GizmoManager::Get ().IsDrawingGizmos ())
+ Scripting::RaiseMonoException ("Gizmo drawing functions can only be used in OnDrawGizmos and OnDrawGizmosSelected.");
+ #endif
+ }
+
+
+ /// *listonly*
+ CSRAW public static void DrawRay (Ray r)
+ {
+ Gizmos.DrawLine (r.origin, r.origin + r.direction);
+ }
+ // Draws a ray starting at /from/ to /from/ + /direction/.
+ CSRAW public static void DrawRay (Vector3 from, Vector3 direction)
+ {
+ Gizmos.DrawLine (from, from + direction);
+ }
+
+
+
+ // Draws a line starting at /from/ towards /to/.
+ CUSTOM static void DrawLine (Vector3 from, Vector3 to)
+ {
+ CheckGizmoDrawing ();
+ #if !GAMERELEASE
+ DrawLine (from, to);
+ #endif
+ }
+
+ // Draws a wireframe sphere with /center/ and /radius/.
+ CUSTOM static void DrawWireSphere (Vector3 center, float radius)
+ {
+ #if !GAMERELEASE
+ CheckGizmoDrawing ();
+ DrawWireSphere (center, radius);
+ #endif
+ }
+
+ // Draws a solid sphere with /center/ and /radius/.
+ CUSTOM static void DrawSphere (Vector3 center, float radius)
+ {
+ #if !GAMERELEASE
+ CheckGizmoDrawing ();
+ DrawSphere (center, radius);
+ #endif
+ }
+
+ // Draw a wireframe box with /center/ and /size/.
+ CUSTOM static void DrawWireCube (Vector3 center, Vector3 size)
+ {
+ #if !GAMERELEASE
+ CheckGizmoDrawing ();
+ DrawWireCube (center, size);
+ #endif
+ }
+
+ // Draw a solid box with /center/ and /size/.
+ CUSTOM static void DrawCube (Vector3 center, Vector3 size)
+ {
+ #if !GAMERELEASE
+ CheckGizmoDrawing ();
+ DrawCube (center, size);
+ #endif
+ }
+
+
+ // Draw an icon at a position in the scene view.
+ CUSTOM static void DrawIcon (Vector3 center, string name, bool allowScaling = true)
+ {
+ #if !GAMERELEASE
+ CheckGizmoDrawing ();
+ DrawIcon (center, name, allowScaling);
+ #endif
+ }
+
+
+ /// *listonly*
+ CSRAW public static void DrawGUITexture (Rect screenRect, Texture texture, Material mat = null) { DrawGUITexture (screenRect, texture,0,0,0,0, mat); }
+ // Draw a texture in screen coordinates. Useful for GUI backgrounds.
+ CUSTOM static void DrawGUITexture (Rect screenRect, Texture texture, int leftBorder, int rightBorder, int topBorder, int bottomBorder, Material mat = null) {
+ #if !GAMERELEASE
+ CheckGizmoDrawing ();
+ DrawGUITexture (screenRect, texture, leftBorder, rightBorder, topBorder, bottomBorder, ColorRGBA32(128,128,128,128), mat);
+ #endif
+ }
+
+
+ // Sets the color for the gizmos that will be drawn next.
+ CUSTOM_PROP static Color color
+ {
+ #if !GAMERELEASE
+ return gizmos::g_GizmoColor;
+ #endif
+ return ColorRGBAf (1,1,1,1);
+ }
+ {
+ #if !GAMERELEASE
+ gizmos::g_GizmoColor = value;
+ #endif
+ }
+
+ // Set the gizmo matrix used to draw all gizmos.
+ CUSTOM_PROP static Matrix4x4 matrix
+ {
+ #if !GAMERELEASE
+ return GetGizmoMatrix ();
+ #endif
+ return Matrix4x4f::identity;
+ }
+ {
+ #if !GAMERELEASE
+ SetGizmoMatrix (value);
+ #endif
+ }
+
+
+ CUSTOM static void DrawFrustum (Vector3 center, float fov, float maxRange, float minRange, float aspect) {
+ #if !GAMERELEASE
+ DrawFrustum (center, fov, maxRange, minRange, aspect);
+ #endif
+ }
+END
+
+
+
+CSRAW }
diff --git a/Runtime/Export/GradientBindings.txt b/Runtime/Export/GradientBindings.txt
new file mode 100644
index 0000000..1e4671b
--- /dev/null
+++ b/Runtime/Export/GradientBindings.txt
@@ -0,0 +1,231 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Math/Gradient.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+
+using namespace Unity;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using UnityEngine;
+
+namespace UnityEngine
+{
+
+// Color key used by Gradient
+STRUCT public GradientColorKey
+
+ // Gradient color key
+ CSRAW public GradientColorKey (Color col, float time)
+ {
+ color = col;
+ this.time = time;
+ }
+ // color of key
+ CSRAW public Color color;
+
+ // time of the key (0 - 1)
+ CSRAW public float time;
+END
+
+C++RAW
+
+struct MonoGradientColorKey
+{
+ ColorRGBAf color;
+ float time;
+};
+
+// Alpha key used by Gradient
+STRUCT public GradientAlphaKey
+
+ // Gradient alpha key
+ CSRAW public GradientAlphaKey (float alpha, float time)
+ {
+ this.alpha = alpha;
+ this.time = time;
+ }
+
+ // alpha alpha of key
+ CSRAW public float alpha;
+
+ // time of the key (0 - 1)
+ CSRAW public float time;
+END
+
+
+C++RAW
+
+struct MonoGradientAlphaKey
+{
+ float alpha;
+ float time;
+};
+
+C++RAW
+ static void CleanupGradientNEW(void* gradient){ delete ((GradientNEW*)gradient); } ;
+
+// Gradient used for animating colors
+CSRAW [StructLayout (LayoutKind.Sequential)]
+
+THREAD_SAFE
+CLASS public Gradient
+ CSRAW internal IntPtr m_Ptr;
+
+ THREAD_SAFE
+ CUSTOM private void Init () {
+ self.SetPtr(new GradientNEW(), CleanupGradientNEW);
+ }
+
+ THREAD_SAFE
+ CUSTOM private void Cleanup () { CleanupGradientNEW(self.GetPtr()); }
+
+ CSRAW public Gradient () {
+ Init ();
+ }
+
+ CSRAW ~Gradient() {
+ Cleanup ();
+ }
+
+
+ // Calculate color at a given time
+ CUSTOM Color Evaluate (float time)
+ {
+ time = clamp01(time);
+ return self->Evaluate (time);
+ }
+
+ // Returns num keys converted or -1 if invalid conversion
+ C++RAW
+ int ConvertColorKeyArray (ScriptingArrayPtr scriptColorKeys, GradientNEW::ColorKey* colorKeys)
+ {
+ if (scriptColorKeys == SCRIPTING_NULL)
+ {
+ ErrorString("SetKeys: Invalid input ColorKey array");
+ return -1;
+ }
+
+ int size = GetScriptingArraySize (scriptColorKeys);
+ if (size > kGradientMaxNumKeys)
+ {
+ ErrorString(Format("Max number of color keys is %d (given %d)", kGradientMaxNumKeys, size));
+ return -1;
+ }
+
+ for (int i=0; i < size ; i++)
+ {
+ MonoGradientColorKey &key = Scripting::GetScriptingArrayElement<MonoGradientColorKey> (scriptColorKeys, i);
+ colorKeys[i].m_Color = key.color;
+ colorKeys[i].m_Time = key.time;
+ }
+
+ return size;
+ }
+
+ // Returns num keys converted or -1 if invalid conversion
+ C++RAW
+ int ConvertAlphaKeyArray (ScriptingArrayPtr scriptAlphaKeys, GradientNEW::AlphaKey* alphaKeys)
+ {
+ if (scriptAlphaKeys == SCRIPTING_NULL)
+ {
+ ErrorString("SetKeys: Invalid input AlphaKey array");
+ return -1;
+ }
+
+ int size = GetScriptingArraySize (scriptAlphaKeys);
+ if (size > kGradientMaxNumKeys)
+ {
+ ErrorString(Format("Max number of alpha keys is %d (given %d)", kGradientMaxNumKeys, size));
+ return -1;
+ }
+
+ for (int i=0; i < size ; i++)
+ {
+ MonoGradientAlphaKey &key = Scripting::GetScriptingArrayElement<MonoGradientAlphaKey> (scriptAlphaKeys, i);
+ alphaKeys[i].m_Alpha = key.alpha;
+ alphaKeys[i].m_Time = key.time;
+ }
+
+ return size;
+ }
+
+ CUSTOM_PROP GradientColorKey[] colorKeys
+ {
+ int numColorKeys = self->GetNumColorKeys ();
+ GradientNEW::ColorKey colorKeys[kGradientMaxNumKeys];
+ ColorRGBA32& firstKey = self->GetKey(0);
+ UInt16& firstColorTime = self->GetColorTime(0);
+ for(int i = 0; i < kGradientMaxNumKeys; i++)
+ {
+ colorKeys[i].m_Color = (&firstKey)[i];
+ colorKeys[i].m_Color.a = 255; // See alphaKeys for alpha values
+ colorKeys[i].m_Time = WordToNormalized((&firstColorTime)[i]);
+ }
+ return CreateScriptingArray (colorKeys, numColorKeys, MONO_COMMON.gradientColorKey);
+ }
+ {
+ GradientNEW::ColorKey colorKeys[kGradientMaxNumKeys];
+ int numColorKeys = ConvertColorKeyArray (value, colorKeys);
+ if (numColorKeys == -1)
+ return;
+ self->SetColorKeys(&colorKeys[0], numColorKeys);
+ }
+
+ CUSTOM_PROP GradientAlphaKey[] alphaKeys
+ {
+ int numAlphaKeys = self->GetNumAlphaKeys ();
+ GradientNEW::AlphaKey alphaKeys[kGradientMaxNumKeys];
+ const ColorRGBA32& firstKey = self->GetKey(0);
+ const UInt16& firstAlphaTime = self->GetAlphaTime(0);
+ for(int i = 0; i < kGradientMaxNumKeys; i++)
+ {
+ alphaKeys[i].m_Alpha = ByteToNormalized((&firstKey)[i].a);
+ alphaKeys[i].m_Time = WordToNormalized((&firstAlphaTime)[i]);
+ }
+
+ return CreateScriptingArray (alphaKeys, numAlphaKeys, MONO_COMMON.gradientAlphaKey);
+ }
+ {
+ GradientNEW::AlphaKey alphaKeys[kGradientMaxNumKeys];
+ int numAlphaKeys = ConvertAlphaKeyArray (value, alphaKeys);
+ if (numAlphaKeys == -1)
+ return;
+ self->SetAlphaKeys(&alphaKeys[0], numAlphaKeys);
+ }
+
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM_PROP internal Color constantColor
+ {
+ return self->GetConstantColor ();
+ }
+ {
+ self->SetConstantColor (value);
+ }
+
+ // Setup Gradient with an array of color keys and alpha keys
+ CUSTOM void SetKeys (GradientColorKey[] colorKeys, GradientAlphaKey[] alphaKeys)
+ {
+ GradientNEW::ColorKey colors[kGradientMaxNumKeys];
+ int numColorKeys = ConvertColorKeyArray (colorKeys, colors);
+ if (numColorKeys == -1)
+ return;
+
+ GradientNEW::AlphaKey alphas[kGradientMaxNumKeys];
+ int numAlphaKeys = ConvertAlphaKeyArray (alphaKeys, alphas);
+ if (numAlphaKeys == -1)
+ return;
+
+ return self->SetKeys (&colors[0], numColorKeys, &alphas[0], numAlphaKeys);
+ }
+END
+
+CSRAW } // end of UnityEngine
+
diff --git a/Runtime/Export/GradientUtility.txt b/Runtime/Export/GradientUtility.txt
new file mode 100644
index 0000000..4aefd8a
--- /dev/null
+++ b/Runtime/Export/GradientUtility.txt
@@ -0,0 +1,30 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Animation/Animation.h"
+#include "Runtime/Animation/AnimationClip.h"
+#include "Runtime/Animation/AnimationManager.h"
+#include "Runtime/Animation/AnimationState.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Animation/AnimationCurveUtility.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Math/Gradient.h"
+
+using namespace Unity;
+
+
+CSRAW
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+// TODO: move Gradient here...
+
+CSRAW } // Namespace
diff --git a/Runtime/Export/Graphics.txt b/Runtime/Export/Graphics.txt
new file mode 100644
index 0000000..f8bdb76
--- /dev/null
+++ b/Runtime/Export/Graphics.txt
@@ -0,0 +1,3325 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoExportUtility.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Camera/ImageFilters.h"
+#include "Runtime/Camera/Light.h"
+#include "Runtime/Camera/Skybox.h"
+#include "Runtime/Graphics/LightmapSettings.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Camera/IntermediateRenderer.h"
+#include "Runtime/Graphics/GraphicsHelper.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Filters/Misc/TextMesh.h"
+#include "Runtime/Filters/Particles/EllipsoidParticleEmitter.h"
+#include "Runtime/Filters/Particles/MeshParticleEmitter.h"
+#include "Runtime/Shaders/Shader.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "Runtime/Camera/Renderqueue.h"
+#include "Runtime/Filters/Deformation/SkinnedMeshFilter.h"
+#include "Runtime/Camera/Flare.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/shaderlab/Library/properties.h"
+#include "Runtime/Geometry/Ray.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Graphics/CubemapTexture.h"
+#include "Runtime/Graphics/Texture3D.h"
+#include "Runtime/Graphics/SpriteFrame.h"
+#include "Runtime/Video/VideoTexture.h"
+#include "Runtime/Camera/Projector.h"
+#include "Runtime/Camera/RenderLayers/GUITexture.h"
+#include "Runtime/Camera/RenderLayers/GUIText.h"
+#include "Runtime/Camera/RenderLayers/GUILayer.h"
+#include "Runtime/Filters/Misc/LineRenderer.h"
+#include "Runtime/Graphics/DrawUtil.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Filters/Mesh/LodMeshFilter.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Graphics/MatrixStack.h"
+#include "Runtime/Filters/Misc/TrailRenderer.h"
+#include "Runtime/Camera/LODGroupManager.h"
+#include "Runtime/Graphics/RenderBufferManager.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/Filters/Particles/ParticleAnimator.h"
+#include "Runtime/Filters/Particles/ParticleRenderer.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include <list>
+#include <vector>
+#include "Runtime/Misc/QualitySettings.h"
+#include "Runtime/Filters/Misc/Font.h"
+#include "Runtime/Camera/RenderSettings.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/Shaders/ShaderKeywords.h"
+#include "Runtime/Shaders/ComputeShader.h"
+#include "Runtime/Geometry/TextureAtlas.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Misc/Player.h"
+#include "Runtime/Graphics/ImageConversion.h"
+#include "Runtime/Filters/Mesh/MeshCombiner.h"
+#include "Runtime/Filters/Mesh/MeshOptimizer.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Camera/Culler.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Threads/AtomicRefCounter.h"
+#include "Runtime/Camera/OcclusionArea.h"
+#include "Runtime/Camera/OcclusionPortal.h"
+#include "Runtime/Graphics/DrawSplashScreenAndWatermarks.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+#include "Runtime/Graphics/TriStripper.h"
+#include "Runtime/Misc/GraphicsScriptingUtility.h"
+
+#if ENABLE_TEXTUREID_MAP
+ #include "Runtime/GfxDevice/TextureIdMap.h"
+#endif
+
+
+#if !GAMERELEASE
+#include "Editor/Src/Gizmos/GizmoManager.h"
+#include "Editor/Src/Gizmos/GizmoUtil.h"
+#endif
+
+
+// matrix stacks to be able to support GL style Push/Pop
+static MatrixStack g_WorldMatrixStack;
+static MatrixStack g_ViewMatrixStack;
+static MatrixStack g_ProjectionMatrixStack;
+//
+
+C++RAW
+
+
+class MonoMaterialPropertyBlock : public MaterialPropertyBlock {
+public:
+ MonoMaterialPropertyBlock() : m_Counter() { }
+
+ void Retain()
+ {
+ m_Counter.Retain();
+ }
+
+ void Release()
+ {
+ if( m_Counter.Release() ) {
+ delete this;
+ }
+ }
+
+ static void CleanupMonoMaterialPropertyBlock(void* mmpb)
+ {
+ if (NULL != mmpb)
+ ((MonoMaterialPropertyBlock*)mmpb)->Release();
+ }
+
+private:
+ AtomicRefCounter m_Counter;
+};
+
+
+extern PPtr<Shader> s_ScriptingCurrentShader;
+extern const ChannelAssigns* s_ScriptingCurrentChannels;
+
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+// The type of a [[Light]].
+ENUM LightType
+ // The light is a spot light.
+ Spot = 0,
+
+ // The light is a directional light.
+ Directional = 1,
+
+ // The light is a point light.
+ Point = 2,
+
+ // The light is an area light. It affects only lightmaps and lightprobes.
+ Area = 3
+END
+
+// How the [[Light]] is rendered.
+ENUM LightRenderMode
+ // Automatically choose the render mode.
+ Auto = 0,
+ // Force the [[Light]] to be a pixel light.
+ ForcePixel = 1,
+ // Force the [[Light]] to be a vertex light.
+ ForceVertex = 2
+END
+
+// Shadow casting options for a [[Light]].
+ENUM LightShadows
+ // Do not cast shadows (default).
+ None = 0,
+ // Cast "hard" shadows (with no shadow filtering).
+ Hard = 1,
+ // Cast "soft" shadows (with 4x PCF filtering).
+ Soft = 2,
+END
+
+
+// OcclusionArea is an area in which occlusion culling is performed
+CLASS OcclusionArea : Component
+
+ // Center of the occlusion area relative to the transform
+ AUTO_PROP Vector3 center GetCenter SetCenter
+
+ // Size that the occlusion area will have
+ AUTO_PROP Vector3 size GetSize SetSize
+
+END
+
+//The portal for dynamically changing occlusion at runtime.
+CLASS OcclusionPortal : Component
+ //gets / sets the portal's open state
+ CUSTOM_PROP bool open { return self->GetIsOpen(); } { self->SetIsOpen(value); }
+END
+
+
+// Fog mode to use.
+ENUM FogMode
+ // Linear fog.
+ Linear = 1,
+
+ // Exponential fog.
+ Exponential = 2,
+
+ // Exponential squared fog (default).
+ ExponentialSquared = 3
+END
+
+
+// The Render Settings contain values for a range of visual elements in your scene, like fog and ambient light.
+CLASS RenderSettings : Object
+ // Is fog enabled?
+ CUSTOM_PROP static bool fog { return GetRenderSettings().GetUseFog(); } { return GetRenderSettings().SetUseFog(value); }
+
+ // Fog mode to use.
+ CUSTOM_PROP static FogMode fogMode { return GetRenderSettings().GetFogMode(); } { return GetRenderSettings().SetFogMode(value); }
+
+ // The color of the fog.
+ CUSTOM_PROP static Color fogColor { return GetRenderSettings().GetFogColor(); } { GetRenderSettings().SetFogColor(value); }
+
+ // The density of the exponential fog.
+ CUSTOM_PROP static float fogDensity { return GetRenderSettings().GetFogDensity(); } { GetRenderSettings().SetFogDensity(value); }
+
+
+ // The starting distance of linear fog.
+ CUSTOM_PROP static float fogStartDistance { return GetRenderSettings().GetLinearFogStart(); } { GetRenderSettings().SetLinearFogStart(value); }
+
+ // The ending distance of linear fog.
+ CUSTOM_PROP static float fogEndDistance { return GetRenderSettings().GetLinearFogEnd(); } { GetRenderSettings().SetLinearFogEnd(value); }
+
+
+ // Color of the scene's ambient light.
+ CUSTOM_PROP static Color ambientLight { return GetRenderSettings().GetAmbientLight(); } { GetRenderSettings().SetAmbientLight(value); }
+
+ // Size of the [[Light]] halos.
+ CUSTOM_PROP static float haloStrength { return GetRenderSettings().GetHaloStrength(); } { GetRenderSettings().SetHaloStrength(value); }
+
+ // The intensity of all flares in the scene.
+ CUSTOM_PROP static float flareStrength { return GetRenderSettings().GetFlareStrength(); } { GetRenderSettings().SetFlareStrength(value); }
+
+ // The fade speed of all flares in the scene.
+ CUSTOM_PROP static float flareFadeSpeed { return GetRenderSettings().GetFlareFadeSpeed(); } { GetRenderSettings().SetFlareFadeSpeed(value); }
+
+ // The global skybox to use.
+ CUSTOM_PROP static Material skybox { return Scripting::ScriptingWrapperFor(GetRenderSettings().GetSkyboxMaterial()); } { GetRenderSettings().SetSkyboxMaterial(value); }
+END
+
+OBSOLETE warning See QualitySettings.names, QualitySettings.SetQualityLevel, and QualitySettings.GetQualityLevel
+ENUM QualityLevel
+ // The "fastest" quality level.
+ Fastest = 0,
+ // The "fast" quality level.
+ Fast = 1,
+ // The "simple" quality level.
+ Simple = 2,
+ // The "good" quality level.
+ Good = 3,
+ // The "beautiful" quality level.
+ Beautiful = 4,
+ // The "fantastic" quality level.
+ Fantastic = 5,
+END
+
+// Shadow projection type for [[wiki:class-QualitySettings|Quality Settings]].
+ENUM ShadowProjection
+ // Close fit shadow maps with linear fadeout.
+ CloseFit = 0,
+
+ // Stable shadow maps with spherical fadeout.
+ StableFit = 1,
+END
+
+
+// Script interface for [[wiki:class-QualitySettings|Quality Settings]].
+CLASS QualitySettings : Object
+ // The indexed list of available Quality Settings
+ CONDITIONAL ENABLE_MONO || UNITY_WINRT
+ CUSTOM_PROP static string[] names
+ {
+ return Scripting::StringVectorToMono(GetQualitySettings().GetQualitySettingsNames ());
+ }
+
+ // Returns the current graphics quality level.
+ CUSTOM static int GetQualityLevel ()
+ {
+ return GetQualitySettings().GetCurrentIndex();
+ }
+
+ // Sets a new graphics quality level.
+ CUSTOM static void SetQualityLevel (int index, bool applyExpensiveChanges = true)
+ {
+ GetQualitySettings().SetCurrentIndex (index, applyExpensiveChanges);
+ }
+
+ OBSOLETE warning Use GetQualityLevel and SetQualityLevel
+ CUSTOM_PROP static QualityLevel currentLevel { return GetQualitySettings().GetCurrentIndex(); } { GetQualitySettings().SetCurrentIndex( value, true ); }
+
+ // Increase the current quality level.
+ CUSTOM static void IncreaseLevel(bool applyExpensiveChanges = false)
+ {
+ QualitySettings& q = GetQualitySettings();
+ q.SetCurrentIndex( q.GetCurrentIndex() + 1, applyExpensiveChanges ); // will clamp internally
+ }
+
+ // Decrease the current quality level.
+ CUSTOM static void DecreaseLevel(bool applyExpensiveChanges = false)
+ {
+ QualitySettings& q = GetQualitySettings();
+ q.SetCurrentIndex( q.GetCurrentIndex() - 1, applyExpensiveChanges ); // will clamp internally
+ }
+
+ // The maximum number of pixel lights that should affect any object.
+ CUSTOM_PROP static int pixelLightCount { return GetQualitySettings().GetCurrent().pixelLightCount; } { GetQualitySettings().SetPixelLightCount(value); }
+
+ // Directional light shadow projection.
+ CUSTOM_PROP static ShadowProjection shadowProjection { return (ShadowProjection)GetQualitySettings().GetCurrent().shadowProjection; } { GetQualitySettings().SetShadowProjection((ShadowProjection)value); }
+
+ // Number of cascades to use for directional light shadows.
+ CUSTOM_PROP static int shadowCascades { return GetQualitySettings().GetCurrent().shadowCascades; } { GetQualitySettings().SetShadowCascades(value); }
+
+ // Shadow drawing distance.
+ CUSTOM_PROP static float shadowDistance { return GetQualitySettings().GetCurrent().shadowDistance; } { GetQualitySettings().SetShadowDistance(value); }
+
+
+ // A texture size limit applied to all textures.
+ CUSTOM_PROP static int masterTextureLimit { return GetQualitySettings().GetCurrent().textureQuality; } { GetQualitySettings().SetMasterTextureLimit(value); }
+
+ // Global anisotropic filtering mode.
+ CUSTOM_PROP static AnisotropicFiltering anisotropicFiltering { return GetQualitySettings().GetCurrent().anisotropicTextures; } { GetQualitySettings().SetAnisotropicTextures(value); }
+
+ // Global multiplier for the LOD's switching distance.
+ CUSTOM_PROP static float lodBias { return GetQualitySettings().GetCurrent().lodBias; } { GetQualitySettings().SetLODBias (value); }
+
+ // A maximum LOD level. All LOD groups
+ CUSTOM_PROP static int maximumLODLevel { return GetQualitySettings().GetCurrent().maximumLODLevel; } { GetQualitySettings().SetMaximumLODLevel (value); }
+
+ // Budget for how many ray casts can be performed per frame for approximate collision testing.
+ CUSTOM_PROP static int particleRaycastBudget { return GetQualitySettings().GetCurrent().particleRaycastBudget; } { GetQualitySettings().SetParticleRaycastBudget (value); }
+
+ // Use a two-pass shader for the vegetation in the terrain engine.
+ CUSTOM_PROP static bool softVegetation { return GetQualitySettings().GetCurrent().softVegetation; } { GetQualitySettings().SetSoftVegetation (value); }
+
+
+ // Maximum number of frames queued up by graphics driver.
+ CUSTOM_PROP static int maxQueuedFrames { return GetGfxDevice().GetMaxBufferedFrames(); } { GetGfxDevice().SetMaxBufferedFrames (clamp(value, -1, 10)); }
+
+ // The VSync Count.
+ CUSTOM_PROP static int vSyncCount { return GetQualitySettings().GetCurrent().vSyncCount; } { GetQualitySettings().SetVSyncCount(value); }
+
+ // Set The AA Filtering option.
+ CUSTOM_PROP static int antiAliasing { return GetQualitySettings().GetCurrent().antiAliasing; } { GetQualitySettings().SetAntiAliasing(value); }
+
+ // Desired color space
+ CUSTOM_PROP static ColorSpace desiredColorSpace { return GetPlayerSettings().GetDesiredColorSpace (); }
+
+ // Active color space
+ CUSTOM_PROP static ColorSpace activeColorSpace { return GetPlayerSettings().GetValidatedColorSpace (); }
+
+ // Blend weights.
+ CUSTOM_PROP static BlendWeights blendWeights { return GetQualitySettings().GetCurrent().blendWeights; } { GetQualitySettings().SetBlendWeights(value); }
+END
+
+
+
+// Values for Camera.clearFlags, determining what to clear when rendering a [[Camera]].
+ENUM CameraClearFlags
+ // Clear with the skybox.
+ Skybox = 1,
+
+ OBSOLETE planned Use CameraClearFlags.SolidColor
+ Color = 2,
+
+ // Clear with a background color.
+ SolidColor = 2,
+
+ // Clear only the depth buffer.
+ Depth = 3,
+
+ // Don't clear anything.
+ Nothing = 4
+END
+
+
+// Depth texture generation mode for [[Camera]].
+CSRAW [Flags]
+ENUM DepthTextureMode
+ // Do not generate depth texture (Default).
+ None = 0,
+
+ // Generate a depth texture.
+ Depth = 1,
+
+ // Generate a depth + normals texture.
+ DepthNormals = 2,
+END
+
+//*undocumented*
+ENUM TexGenMode
+ // the texture gets its coordinates from UV
+ None = 0,
+ // The texture uses spherical reflection mappping
+ SphereMap = 1,
+ // The texture is applied in object space
+ Object = 2,
+ // Projected Eye space
+ EyeLinear = 3,
+ // Cubemap reflection calculation
+ CubeReflect = 4,
+ // Cubemap normal calculation
+ CubeNormal = 5
+END
+
+
+
+// Anisotropic filtering mode.
+ENUM AnisotropicFiltering
+ // Disable anisotropic filtering for all textures.
+ Disable = 0,
+ // Enable anisotropic filtering, as set for each texture.
+ Enable = 1,
+ // Enable anisotropic filtering for all textures.
+ ForceEnable = 2
+END
+
+// Blend weights.
+ENUM BlendWeights
+ // One bone affects each vertex.
+ OneBone = 1,
+ // Two bones affect each vertex.
+ TwoBones = 2,
+ // Four bones affect each vertex.
+ FourBones = 4
+END
+
+
+CONDITIONAL UNITY_EDITOR
+// Compression Quality. Corresponds to the settings in a [[wiki:class-Texture2D|texture inspector]].
+ENUM TextureCompressionQuality
+ // Fast compression
+ Fast = 0,
+ // Normal compression (default)
+ Normal = 50,
+ // Best compression
+ Best = 100
+END
+
+
+
+// A class to access the [[Mesh]] of the [[wiki:class-MeshFilter|mesh filter]].
+CLASS MeshFilter : Component
+
+ // Returns the instantiated [[Mesh]] assigned to the mesh filter.
+ AUTO_PTR_PROP Mesh mesh GetInstantiatedMesh SetInstantiatedMesh
+
+ // Returns the shared mesh of the mesh filter.
+ AUTO_PTR_PROP Mesh sharedMesh GetSharedMesh SetSharedMesh
+END
+
+
+// Struct used to describe meshes to be combined using Mesh.CombineMeshes.
+STRUCT CombineInstance
+
+ // [[Mesh]] to combine
+ CSRAW public Mesh mesh { get { return InternalGetMesh(m_MeshInstanceID); } set { m_MeshInstanceID = value != null ? value.GetInstanceID() : 0; } }
+
+ // Submesh index of the mesh
+ CSRAW public int subMeshIndex { get { return m_SubMeshIndex; } set { m_SubMeshIndex = value; } }
+
+ // Matrix to transform the mesh with before combining
+ CSRAW public Matrix4x4 transform { get { return m_Transform; } set { m_Transform = value; } }
+
+ CSRAW private int m_MeshInstanceID;
+ CUSTOM private Mesh InternalGetMesh(int instanceID)
+ {
+ if(instanceID == 0)
+ return SCRIPTING_NULL;
+ return Scripting::ScriptingWrapperFor(PPtr<Mesh>(instanceID));
+ }
+
+ CSRAW private int m_SubMeshIndex;
+ CSRAW private Matrix4x4 m_Transform;
+END
+
+C++RAW
+
+struct MonoCombineInstance
+{
+ int meshInstanceID;
+ int subMeshIndex;
+ Matrix4x4f transform;
+};
+
+// Topology of [[Mesh]] faces.
+ENUM MeshTopology
+
+ // Mesh is made from triangles.
+ Triangles = 0,
+
+ // Mesh is made from quads.
+ Quads = 2,
+
+ // Mesh is made from lines.
+ Lines = 3,
+
+ // Mesh is a line strip.
+ LineStrip = 4,
+
+ // Mesh is made from points.
+ Points = 5,
+END
+
+
+// A class that allows creating or modifying meshes from scripts.
+
+CLASS Mesh : Object
+
+ // Creates an empty mesh
+ CSRAW public Mesh ()
+ {
+ Internal_Create(this);
+ }
+
+ CUSTOM private static void Internal_Create ([Writable]Mesh mono)
+ {
+ Mesh* mesh = NEW_OBJECT_MAIN_THREAD (Mesh);
+ mesh->Reset();
+ Scripting::ConnectScriptingWrapperToObject (mono.GetScriptingObject(), mesh);
+ mesh->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+ }
+
+ // Clears all vertex data and all triangle indices.
+ CUSTOM void Clear (bool keepVertexLayout = true)
+ {
+ self->Clear (keepVertexLayout);
+ }
+
+ // Returns state of the Read/Write Enabled checkbox when model was imported.
+ AUTO_PROP bool isReadable GetIsReadable
+
+ // Works like isReadable, except it also returns true in editor outside the game loop.
+ CUSTOM_PROP internal bool canAccess { return self->CanAccessFromScript(); }
+
+ // Returns a copy of the vertex positions or assigns a new vertex positions array.
+ CUSTOM_PROP Vector3[] vertices
+ {
+ ScriptingClassPtr klass = GetScriptingManager().GetCommonClasses().vector3;
+ if (self->CanAccessFromScript())
+ {
+ if (self->IsAvailable(kShaderChannelVertex))
+ return CreateScriptingArrayStride<Vector3f>(self->GetChannelPointer(kShaderChannelVertex), self->GetVertexCount(), klass, self->GetStride(kShaderChannelVertex));
+ }
+ else
+ ErrorStringMsg("Not allowed to access vertices on mesh '%s'", self->GetName());
+
+ return CreateEmptyStructArray(klass);
+ }
+ {
+ if (self->CanAccessFromScript())
+ self->SetVertices (Scripting::GetScriptingArrayStart<Vector3f>(value), GetScriptingArraySize(value));
+ else
+ ErrorStringMsg("Not allowed to access vertices on mesh '%s'", self->GetName());
+ }
+
+ // The normals of the mesh.
+ CUSTOM_PROP Vector3[] normals
+ {
+ ScriptingClassPtr klass = GetScriptingManager().GetCommonClasses().vector3;
+ if (self->CanAccessFromScript())
+ {
+ if (self->IsAvailable(kShaderChannelNormal))
+ return CreateScriptingArrayStride<Vector3f>(self->GetChannelPointer(kShaderChannelNormal), self->GetVertexCount(), klass, self->GetStride(kShaderChannelNormal));
+ }
+ else
+ ErrorStringMsg("Not allowed to access normals on mesh '%s'", self->GetName());
+
+ return CreateEmptyStructArray(klass);
+ }
+ {
+ if (self->CanAccessFromScript())
+ self->SetNormals (Scripting::GetScriptingArrayStart<Vector3f>(value), GetScriptingArraySize(value));
+ else
+ ErrorStringMsg("Not allowed to access normals on mesh '%s'", self->GetName());
+ }
+
+ // The tangents of the mesh.
+ CUSTOM_PROP Vector4[] tangents
+ {
+ ScriptingClassPtr klass = GetScriptingManager().GetCommonClasses().vector4;
+ if (self->CanAccessFromScript())
+ {
+ if (self->IsAvailable(kShaderChannelTangent))
+ return CreateScriptingArrayStride<Vector4f>(self->GetChannelPointer(kShaderChannelTangent), self->GetVertexCount(), klass, self->GetStride(kShaderChannelTangent));
+ }
+ else
+ ErrorStringMsg("Not allowed to access tangents on mesh '%s'", self->GetName());
+
+ return CreateEmptyStructArray(klass);
+ }
+ {
+ if (self->CanAccessFromScript())
+ self->SetTangents (Scripting::GetScriptingArrayStart<Vector4f>(value), GetScriptingArraySize(value));
+ else
+ ErrorStringMsg("Not allowed to access tangents on mesh '%s'", self->GetName());
+ }
+
+ // The base texture coordinates of the mesh.
+ CUSTOM_PROP Vector2[] uv
+ {
+ ScriptingClassPtr klass = GetScriptingManager().GetCommonClasses().vector2;
+ if (self->CanAccessFromScript())
+ {
+ if (self->IsAvailable(kShaderChannelTexCoord0))
+ return CreateScriptingArrayStride<Vector2f>(self->GetChannelPointer(kShaderChannelTexCoord0), self->GetVertexCount(), klass, self->GetStride(kShaderChannelTexCoord0));
+ }
+ else
+ ErrorStringMsg("Not allowed to access uv on mesh '%s'", self->GetName());
+
+ return CreateEmptyStructArray(klass);
+ }
+ {
+ if (self->CanAccessFromScript())
+ self->SetUv (0, Scripting::GetScriptingArrayStart<Vector2f>(value), GetScriptingArraySize(value));
+ else
+ ErrorStringMsg("Not allowed to access uv on mesh '%s'", self->GetName());
+ }
+
+ // The second texture coordinate set of the mesh, if present.
+ CUSTOM_PROP Vector2[] uv2
+ {
+ ScriptingClassPtr klass = GetScriptingManager().GetCommonClasses().vector2;
+ if (self->CanAccessFromScript())
+ {
+ if (self->IsAvailable(kShaderChannelTexCoord1))
+ return CreateScriptingArrayStride<Vector2f>(self->GetChannelPointer(kShaderChannelTexCoord1), self->GetVertexCount(), klass, self->GetStride(kShaderChannelTexCoord1));
+ }
+ else
+ ErrorStringMsg("Not allowed to access uv2 on mesh '%s'", self->GetName());
+
+ return CreateEmptyStructArray(klass);
+ }
+ {
+ if (self->CanAccessFromScript())
+ self->SetUv (1, Scripting::GetScriptingArrayStart<Vector2f>(value), GetScriptingArraySize(value));
+ else
+ ErrorStringMsg("Not allowed to access uv2 on mesh '%s'", self->GetName());
+ }
+
+ OBSOLETE planned Use uv2 instead
+ CSRAW public Vector2[] uv1 { get { return uv2; } set { uv2 = value; } }
+
+ // The bounding volume of the mesh.
+ AUTO_PROP Bounds bounds GetBounds SetBounds
+
+
+ // Vertex colors of the mesh.
+ CUSTOM_PROP Color[] colors
+ {
+ ScriptingClassPtr klass = GetScriptingManager().GetCommonClasses().color;
+ if (self->CanAccessFromScript())
+ {
+ if (self->IsAvailable(kShaderChannelColor))
+ {
+ ScriptingArrayPtr array = CreateScriptingArray<ColorRGBAf>(klass, self->GetVertexCount());
+ self->ExtractColorArrayConverting (Scripting::GetScriptingArrayStart<ColorRGBAf>(array));
+ return array;
+ }
+ }
+ else
+ ErrorStringMsg("Not allowed to access colors on mesh '%s'", self->GetName());
+
+ return CreateEmptyStructArray(klass);
+ }
+ {
+ if (self->CanAccessFromScript())
+ self->SetColorsConverting (Scripting::GetScriptingArrayStart<ColorRGBAf>(value), GetScriptingArraySize(value));
+ else
+ ErrorStringMsg("Not allowed to access colors on mesh '%s'", self->GetName());
+ }
+
+ // Vertex colors of the mesh.
+ CUSTOM_PROP Color32[] colors32
+ {
+ ScriptingClassPtr klass = GetScriptingManager().GetCommonClasses().color32;
+ if (self->CanAccessFromScript())
+ {
+ if (self->IsAvailable(kShaderChannelColor))
+ {
+ ScriptingArrayPtr array = CreateScriptingArray<ColorRGBA32>(klass, self->GetVertexCount());
+ self->ExtractColorArray (Scripting::GetScriptingArrayStart<ColorRGBA32>(array));
+ return array;
+ }
+ }
+ else
+ ErrorStringMsg("Not allowed to access colors on mesh '%s'", self->GetName());
+
+ return CreateEmptyStructArray(klass);
+ }
+ {
+ if (self->CanAccessFromScript())
+ self->SetColors (Scripting::GetScriptingArrayStart<ColorRGBA32>(value), GetScriptingArraySize(value));
+ else
+ ErrorStringMsg("Not allowed to access colors on mesh '%s'", self->GetName());
+ }
+
+ // Recalculate the bounding volume of the mesh from the vertices.
+ CUSTOM void RecalculateBounds ()
+ {
+ if (self->CanAccessFromScript())
+ self->RecalculateBounds();
+ else
+ ErrorStringMsg("Not allowed to call RecalculateBounds() on mesh '%s'", self->GetName());
+ }
+
+ // Recalculates the normals of the mesh from the triangles and vertices.
+ CUSTOM void RecalculateNormals ()
+ {
+ if (self->CanAccessFromScript())
+ self->RecalculateNormals();
+ else
+ ErrorStringMsg("Not allowed to call RecalculateNormals() on mesh '%s'", self->GetName());
+ }
+
+
+ // Optimizes the mesh for display.
+ CUSTOM void Optimize ()
+ {
+ }
+
+ // An array containing all triangles in the mesh.
+ //
+ // If the mesh contains multiple sub meshes (materials) the triangle list will contain all triangles of all submeshes.
+ CUSTOM_PROP int[] triangles
+ {
+ ScriptingClassPtr klass = GetScriptingManager().GetCommonClasses().int_32;
+ if (self->CanAccessFromScript())
+ {
+ Mesh::TemporaryIndexContainer triangles;
+ self->GetTriangles(triangles);
+ return CreateScriptingArray(&triangles[0], triangles.size(), klass);
+ }
+ else
+ ErrorStringMsg("Not allowed to access triangles on mesh '%s'", self->GetName());
+
+ return CreateEmptyStructArray(klass);
+ }
+ {
+ if (self->CanAccessFromScript())
+ {
+ self->SetSubMeshCount(1);
+ self->SetIndices (Scripting::GetScriptingArrayStart<UInt32>(value), GetScriptingArraySize(value), 0, kPrimitiveTriangles);
+ }
+ else
+ ErrorStringMsg("Not allowed to access triangles on mesh '%s'", self->GetName());
+ }
+
+
+ // Returns the triangle list for the submesh.
+ CUSTOM int[] GetTriangles (int submesh)
+ {
+ ScriptingClassPtr klass = GetScriptingManager().GetCommonClasses().int_32;
+ if (self->CanAccessFromScript())
+ {
+ Mesh::TemporaryIndexContainer triangles;
+ self->GetTriangles(triangles, submesh);
+ return CreateScriptingArray(&triangles[0], triangles.size(), klass);
+ }
+ else
+ ErrorStringMsg("Not allowed to call GetTriangles() on mesh '%s'", self->GetName());
+
+ return CreateEmptyStructArray(klass);
+ }
+
+ // Sets the triangle list for the submesh.
+ CUSTOM void SetTriangles(int[] triangles, int submesh)
+ {
+ if (self->CanAccessFromScript())
+ {
+ self->SetIndices(Scripting::GetScriptingArrayStart<UInt32>(triangles), GetScriptingArraySize(triangles), submesh, kPrimitiveTriangles);
+ }
+ else
+ ErrorStringMsg("Not allowed to call SetTriangles() on mesh '%s'", self->GetName());
+ }
+
+
+ // Returns the index buffer for the submesh.
+ CUSTOM int[] GetIndices (int submesh)
+ {
+ ScriptingClassPtr klass = GetScriptingManager().GetCommonClasses().int_32;
+ if (self->CanAccessFromScript())
+ {
+ Mesh::TemporaryIndexContainer indices;
+ self->GetIndices (indices, submesh);
+ return CreateScriptingArray(&indices[0], indices.size(), klass);
+ }
+ else
+ ErrorStringMsg("Not allowed to call GetIndices() on mesh '%s'", self->GetName());
+
+ return CreateEmptyStructArray(klass);
+ }
+
+ // Sets the index buffer for the submesh.
+ CUSTOM void SetIndices (int[] indices, MeshTopology topology, int submesh)
+ {
+ if (self->CanAccessFromScript())
+ {
+ self->SetIndices(Scripting::GetScriptingArrayStart<UInt32>(indices), GetScriptingArraySize(indices), submesh, topology);
+ }
+ else
+ ErrorStringMsg("Not allowed to call SetIndices() on mesh '%s'", self->GetName());
+ }
+
+ // Gets the topology of a submesh.
+ CUSTOM MeshTopology GetTopology (int submesh)
+ {
+ if ((unsigned)submesh >= self->GetSubMeshCount())
+ {
+ ErrorString("Failed getting topology. Submesh index is out of bounds.");
+ return kPrimitiveTriangles;
+ }
+ return self->GetSubMeshFast(submesh).topology;
+ }
+
+ // Returns the number of vertices in the mesh (RO).
+ AUTO_PROP int vertexCount GetVertexCount
+
+ // The number of submeshes. Every material has a separate triangle list.
+ CUSTOM_PROP int subMeshCount
+ {
+ return self->GetSubMeshCount();
+ }
+ {
+ if(value < 0)
+ {
+ ErrorString ("subMeshCount can't be set to negative value");
+ return;
+ }
+ self->SetSubMeshCount(value);
+ }
+
+ //*undocumented* Internal api not really generally useful
+ OBSOLETE warning Use SetTriangles instead. Internally this function will convert the triangle strip to a list of triangles anyway.
+ CUSTOM void SetTriangleStrip (int[] triangles, int submesh)
+ {
+ UInt32* triStrip = Scripting::GetScriptingArrayStart<UInt32>(triangles);
+ UNITY_TEMP_VECTOR(UInt32) newTriangles;
+ Destripify(triStrip, GetScriptingArraySize(triangles), newTriangles);
+ self->SetIndices (&newTriangles[0], newTriangles.size(), submesh, kPrimitiveTriangles);
+ }
+
+ //*undocumented* Internal api not really generally useful
+ OBSOLETE warning Use GetTriangles instead. Internally this function converts a list of triangles to a strip, so it might be slow, it might be a mess.
+ CUSTOM int[] GetTriangleStrip (int submesh)
+ {
+ Mesh::TemporaryIndexContainer triangles;
+ self->GetTriangles(triangles, submesh);
+
+ UNITY_TEMP_VECTOR(UInt32) strip;
+ Stripify(&triangles[0], triangles.size(), strip);
+
+ return CreateScriptingArray(&strip[0], strip.size(), GetScriptingManager().GetCommonClasses().int_32);
+ }
+
+ // Combines several meshes into this mesh.
+ CUSTOM void CombineMeshes(CombineInstance[] combine, bool mergeSubMeshes = true, bool useMatrices = true)
+ {
+ CombineInstances combineVec;
+ combineVec.resize(GetScriptingArraySize(combine));
+ MonoCombineInstance* mono = Scripting::GetScriptingArrayStart<MonoCombineInstance>(combine);
+ for (int i=0;i<combineVec.size();i++)
+ {
+ combineVec[i].transform = mono[i].transform;
+ combineVec[i].subMeshIndex = mono[i].subMeshIndex;
+ combineVec[i].mesh = PPtr<Mesh>(mono[i].meshInstanceID);
+ }
+ CombineMeshes(combineVec, *self, mergeSubMeshes, useMatrices);
+ }
+
+
+ // The bone weights of each vertex
+ CUSTOM_PROP BoneWeight[] boneWeights
+ {
+ int size = self->GetVertexCount();
+ BoneInfluence* weights = self->GetBoneWeights();
+ return CreateScriptingArray(weights, size, MONO_COMMON.boneWeight);
+ }
+ {
+ self->SetBoneWeights(Scripting::GetScriptingArrayStart<BoneInfluence> (value), GetScriptingArraySize(value));
+ }
+
+ // The bind poses. The bind pose at each index refers to the bone with the same index.
+ CUSTOM_PROP Matrix4x4[] bindposes
+ {
+ return CreateScriptingArray(self->GetBindposes(), self->GetBindposeCount(), MONO_COMMON.matrix4x4);
+ }
+ {
+ self->SetBindposes(Scripting::GetScriptingArrayStart<Matrix4x4f> (value), GetScriptingArraySize(value));
+ }
+
+ // Optimize mesh for frequent updates.
+ CUSTOM void MarkDynamic ()
+ {
+ if (self->CanAccessFromScript())
+ self->MarkDynamic();
+ }
+
+ CUSTOM void UploadMeshData(bool markNoLogerReadable)
+ {
+ if (self->CanAccessFromScript())
+ self->UploadMeshData(markNoLogerReadable);
+ }
+
+ // Returns BlendShape count on this mesh.
+ CUSTOM_PROP int blendShapeCount
+ {
+ return self->GetBlendShapeChannelCount();
+ }
+
+ // Returns name of BlendShape by given index.
+ CUSTOM string GetBlendShapeName (int index)
+ {
+ return scripting_string_new(GetChannelName (self->GetBlendShapeData(), index));
+ }
+
+ // Calculates the index of the blendShape name
+ CUSTOM int GetBlendShapeIndex(string blendShapeName)
+ {
+ return GetChannelIndex (self->GetBlendShapeData(), blendShapeName.AsUTF8().c_str());
+ }
+
+END
+
+// Skinning bone weights of a vertex in the mesh.
+STRUCT BoneWeight
+ CSRAW private float m_Weight0;
+ CSRAW private float m_Weight1;
+ CSRAW private float m_Weight2;
+ CSRAW private float m_Weight3;
+
+ CSRAW private int m_BoneIndex0;
+ CSRAW private int m_BoneIndex1;
+ CSRAW private int m_BoneIndex2;
+ CSRAW private int m_BoneIndex3;
+
+ // Skinning weight for first bone.
+ CSRAW public float weight0 { get { return m_Weight0; } set { m_Weight0 = value; } }
+ // Skinning weight for second bone.
+ CSRAW public float weight1 { get { return m_Weight1; } set { m_Weight1 = value; } }
+ // Skinning weight for third bone.
+ CSRAW public float weight2 { get { return m_Weight2; } set { m_Weight2 = value; } }
+ // Skinning weight for fourth bone.
+ CSRAW public float weight3 { get { return m_Weight3; } set { m_Weight3 = value; } }
+
+ // Index of first bone.
+ CSRAW public int boneIndex0 { get { return m_BoneIndex0; } set { m_BoneIndex0 = value; } }
+ // Index of second bone.
+ CSRAW public int boneIndex1 { get { return m_BoneIndex1; } set { m_BoneIndex1 = value; } }
+ // Index of third bone.
+ CSRAW public int boneIndex2 { get { return m_BoneIndex2; } set { m_BoneIndex2 = value; } }
+ // Index of fourth bone.
+ CSRAW public int boneIndex3 { get { return m_BoneIndex3; } set { m_BoneIndex3 = value; } }
+
+ // used to allow BoneWeights to be used as keys in hash tables
+ public override int GetHashCode() {
+ return boneIndex0.GetHashCode() ^ (boneIndex1.GetHashCode()<<2) ^ (boneIndex2.GetHashCode()>>2) ^ (boneIndex3.GetHashCode()>>1)
+ ^ (weight0.GetHashCode() << 5) ^ (weight1.GetHashCode()<<4) ^ (weight2.GetHashCode()>>4) ^ (weight3.GetHashCode()>>3);
+ }
+
+ // also required for being able to use BoneWeights as keys in hash tables
+ public override bool Equals(object other) {
+ if(!(other is BoneWeight)) return false;
+
+ BoneWeight rhs=(BoneWeight)other;
+ return boneIndex0.Equals(rhs.boneIndex0)
+ && boneIndex1.Equals(rhs.boneIndex1)
+ && boneIndex2.Equals(rhs.boneIndex2)
+ && boneIndex3.Equals(rhs.boneIndex3)
+ && (new Vector4(weight0,weight1,weight2,weight3)).Equals(new Vector4(rhs.weight0,rhs.weight1,rhs.weight2,rhs.weight3));
+ }
+
+ //*undoc*
+ CSRAW public static bool operator == (BoneWeight lhs, BoneWeight rhs)
+ {
+ return lhs.boneIndex0 == rhs.boneIndex0
+ && lhs.boneIndex1 == rhs.boneIndex1
+ && lhs.boneIndex2 == rhs.boneIndex2
+ && lhs.boneIndex3 == rhs.boneIndex3
+ && (new Vector4( lhs.weight0, lhs.weight1, lhs.weight2, lhs.weight3 )
+ == new Vector4( rhs.weight0, rhs.weight1, rhs.weight2, rhs.weight3 ));
+ }
+
+ //*undoc*
+ CSRAW public static bool operator != (BoneWeight lhs, BoneWeight rhs)
+ {
+ return !(lhs == rhs);
+ }
+END
+
+/// The maximum number of bones affecting a single vertex
+ENUM SkinQuality
+ // Chooses the number of bones from the number current [[QualitySettings]] (Default)
+ Auto = 0,
+ // Use only 1 bone to deform a single vertex. (The most important bone will be used)
+ Bone1 = 1,
+ // Use 2 bones to deform a single vertex. (The most important bones will be used)
+ Bone2 = 2,
+ // Use 4 bones to deform a single vertex.
+ Bone4 = 4
+END
+
+// The Skinned Mesh filter
+NONSEALED_CLASS SkinnedMeshRenderer : Renderer
+
+ // The bones used to skin the mesh.
+ CUSTOM_PROP Transform[] bones
+ {
+ return CreateScriptingArrayFromUnityObjects(self->GetBones(), ClassID(Transform));
+ }
+ {
+ dynamic_array<PPtr<Transform> > transforms;
+ if (value != SCRIPTING_NULL)
+ {
+ int size = GetScriptingArraySize(value);
+ transforms.resize_uninitialized(size);
+
+ for (int i=0;i<size;i++)
+ {
+ int instanceID = Scripting::GetInstanceIDFromScriptingWrapper(Scripting::GetScriptingArrayElementNoRef<ScriptingObjectPtr>(value, i));
+ transforms[i] = PPtr<Transform> (instanceID);
+ }
+ }
+
+ self->SetBones(transforms);
+ }
+
+ AUTO_PTR_PROP Transform rootBone GetRootBone SetRootBone
+
+ // The maximum number of bones affecting a single vertex
+ AUTO_PROP SkinQuality quality GetQuality SetQuality
+
+ // The mesh used for skinning
+ AUTO_PTR_PROP Mesh sharedMesh GetMesh SetMesh
+
+ OBSOLETE warning Has no effect.
+ CSRAW public bool skinNormals { get { return true; } set {} }
+
+ // If enabled, the Skinned Mesh will be updated when offscreen. If disabled, this also disables updating animations.
+ AUTO_PROP bool updateWhenOffscreen GetUpdateWhenOffscreen SetUpdateWhenOffscreen
+
+ // AABB of this Skinned Mesh in its local space.
+ CUSTOM_PROP Bounds localBounds
+ {
+ AABB result;
+ self->GetSkinnedMeshLocalAABB(result);
+ return result;
+ }
+ {
+ self->SetLocalAABB(value);
+ }
+
+ // Creates a snapshot of SkinnedMeshRenderer and stores it in mesh.
+ CUSTOM void BakeMesh (Mesh mesh)
+ {
+ self->BakeMesh(*mesh);
+ }
+
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM_PROP internal Transform actualRootBone { return Scripting::ScriptingWrapperFor(&self->GetActualRootBone()); }
+
+ // Returns weight of BlendShape on this renderer.
+ CUSTOM float GetBlendShapeWeight(int index) { return self->GetBlendShapeWeight(index); }
+
+ // Sets weight of BlendShape on this renderer.
+ CUSTOM void SetBlendShapeWeight(int index, float value) { self->SetBlendShapeWeight(index, value); }
+
+END
+
+
+// A flare asset. Read more about flares in the [[wiki:class-Flare|components reference]].
+CLASS Flare : Object
+
+END
+
+
+// Script interface for a [[wiki:class-LensFlare|Lens flare component]].
+CLASS LensFlare : Behaviour
+ // The [[wiki:class-Flare|flare asset]] to use.
+ AUTO_PTR_PROP Flare flare GetFlare SetFlare
+
+ // The strength of the flare.
+ AUTO_PROP float brightness GetBrightness SetBrightness
+
+ // The fade speed of the flare.
+ AUTO_PROP float fadeSpeed GetFadeSpeed SetFadeSpeed
+
+ // The color of the flare.
+ AUTO_PROP Color color GetColor SetColor
+END
+
+
+// General functionality for all renderers.
+NONSEALED_CLASS Renderer : Component
+
+ CUSTOM_PROP internal Transform staticBatchRootTransform { return Scripting::ScriptingWrapperFor (self->GetStaticBatchRoot ()); } { self->SetStaticBatchRoot (value); }
+
+ CUSTOM_PROP internal int staticBatchIndex { return self->GetStaticBatchIndex(); }
+
+ CUSTOM internal void SetSubsetIndex (int index, int subSetIndexForMaterial)
+ {
+ self->SetMaterialCount (std::max(index+1, self->GetMaterialCount()));
+ self->SetSubsetIndex(index, subSetIndexForMaterial);
+ }
+
+ // Has this renderer been statically batched with any other renderers?
+ CUSTOM_PROP bool isPartOfStaticBatch { return self->GetStaticBatchIndex() != 0; }
+
+ // Matrix that transforms a point from world space into local space (RO).
+ AUTO_PROP Matrix4x4 worldToLocalMatrix GetWorldToLocalMatrix
+ // Matrix that transforms a point from local space into world space (RO).
+ AUTO_PROP Matrix4x4 localToWorldMatrix GetLocalToWorldMatrix
+
+
+ // Makes the rendered 3D object visible if enabled.
+ AUTO_PROP bool enabled GetEnabled SetEnabled
+
+
+ // Does this object cast shadows?
+ AUTO_PROP bool castShadows GetCastShadows SetCastShadows
+
+
+ // Does this object receive shadows?
+ AUTO_PROP bool receiveShadows GetReceiveShadows SetReceiveShadows
+
+
+ // The material of this object.
+
+ CUSTOM_PROP Material material
+ {
+ return Scripting::ScriptingWrapperFor (self->GetAndAssignInstantiatedMaterial (0, false));
+ }
+ {
+ self->SetMaterialCount (std::max(1, self->GetMaterialCount()));
+ self->SetMaterial (value, 0);
+ }
+
+
+ // The shared material of this object.
+ CUSTOM_PROP Material sharedMaterial
+ {
+ if (self->GetMaterialCount ())
+ return Scripting::ScriptingWrapperFor (self->GetMaterial (0));
+ else
+ return Scripting::ScriptingObjectNULL (ScriptingClassFor (Material));
+ }
+ {
+ self->SetMaterialCount (std::max(1, self->GetMaterialCount()));
+ self->SetMaterial (value, 0);
+ }
+
+
+ // All the shared materials of this object.
+ CUSTOM_PROP Material[] sharedMaterials
+ {
+ return CreateScriptingArrayFromUnityObjects(self->GetMaterialArray(), ClassID(Material));
+ }
+ {
+#if ENABLE_MONO
+ if (value == SCRIPTING_NULL)
+ Scripting::RaiseNullException("material array is null");
+#endif
+
+ int size = GetScriptingArraySize(value);
+ self->SetMaterialCount (size);
+ for (int i=0;i<size;i++)
+ self->SetMaterial (ScriptingObjectToObject<Material> (Scripting::GetScriptingArrayElementNoRef<ScriptingObjectPtr>(value, i)), i);
+ }
+
+ // All the materials of this object.
+ CUSTOM_PROP Material[] materials
+ {
+ int length = self->GetMaterialCount();
+ ScriptingArrayPtr array = CreateScriptingArray<ScriptingObjectPtr> (ScriptingClassFor(Material), length);
+
+ for (int i=0;i<length;i++)
+ {
+ Material* instantiated = self->GetAndAssignInstantiatedMaterial(i, false);
+ Scripting::SetScriptingArrayElement<ScriptingObjectPtr>(array,i,Scripting::ScriptingWrapperFor(instantiated));
+ }
+
+ return array;
+ }
+ {
+ if (value == SCRIPTING_NULL)
+ Scripting::RaiseNullException("material array is null");
+
+ int size = GetScriptingArraySize(value);
+ self->SetMaterialCount (size);
+ for (int i=0;i<size;i++)
+ {
+ ScriptingObjectPtr o = Scripting::GetScriptingArrayElementNoRef<ScriptingObjectPtr>(value,i);
+ self->SetMaterial (ScriptingObjectToObject<Material> (o), i);
+ }
+ }
+
+ // The bounding volume of the renderer (RO).
+ CUSTOM_PROP Bounds bounds { return CalculateWorldAABB (self->GetGameObject ()); }
+
+ // The index of the lightmap applied to this renderer.
+ CUSTOM_PROP int lightmapIndex { return self->GetLightmapIndexInt(); } { return self->SetLightmapIndexInt(value); }
+
+ // The tiling & offset used for lightmap.
+ CUSTOM_PROP Vector4 lightmapTilingOffset
+ {
+ Vector4f st = self->GetLightmapST();
+ return Vector4f(st.x, st.y, st.z, st.w);
+ }
+ {
+ Vector4f st( value.x, value.y, value.z, value.w );
+ self->SetLightmapST( st );
+ }
+
+ // ''OnBecameVisible'' is called when the object became visible by any camera.
+ CSNONE void OnBecameVisible();
+
+
+ // ''OnBecameInvisible'' is called when the object is no longer visible by any camera.
+ CSNONE void OnBecameInvisible();
+
+ // Is this renderer visible in any camera? (RO)
+ AUTO_PROP bool isVisible IsVisibleInScene
+
+ // If enabled and baked light probes are present in the scene, an interpolated light probe
+ AUTO_PROP bool useLightProbes GetUseLightProbes SetUseLightProbes
+
+ // If set, Renderer will use this Transform's position to find the interpolated light probe;
+ AUTO_PTR_PROP Transform lightProbeAnchor GetLightProbeAnchor SetLightProbeAnchor
+
+ // Lets you add per-renderer material parameters without duplicating a material.
+ CUSTOM void SetPropertyBlock (MaterialPropertyBlock properties)
+ {
+ if (properties.GetPtr())
+ self->SetPropertyBlock (*properties);
+ else
+ self->ClearPropertyBlock ();
+ }
+
+ CUSTOM void GetPropertyBlock (MaterialPropertyBlock dest)
+ {
+ if (!dest.GetPtr())
+ Scripting::RaiseNullException("dest property block is null");
+ self->GetPropertyBlock (*dest);
+ }
+
+
+ CUSTOM_PROP string sortingLayerName
+ {
+ DISALLOW_IN_CONSTRUCTOR
+ return scripting_string_new(self->GetSortingLayerName());
+ }
+ {
+ DISALLOW_IN_CONSTRUCTOR
+ self->SetSortingLayerName(value);
+ }
+ AUTO_PROP int sortingLayerID GetSortingLayerUserID SetSortingLayerUserID
+ AUTO_PROP int sortingOrder GetSortingOrder SetSortingOrder
+
+
+ OBSOLETE planned No longer available
+ CUSTOM void Render (int material)
+ {
+ Shader* shader = s_ScriptingCurrentShader;
+ if (!shader) {
+ ErrorString ("Render requires material.SetPass before!");
+ return;
+ }
+
+ GfxDevice& device = GetGfxDevice();
+
+ // D3D needs begin/end when rendering is called out-of-band
+ #if UNITY_EDITOR
+ bool outsideOfFrame = !device.IsInsideFrame();
+ if( outsideOfFrame )
+ device.BeginFrame();
+ #endif
+
+ float matWorld[16], matView[16];
+
+ CopyMatrix(device.GetViewMatrix(), matView);
+ CopyMatrix(device.GetWorldMatrix(), matWorld);
+
+ Matrix4x4f transformMatrix;
+ Matrix4x4f scaleOnly;
+ float scale;
+ TransformType matrixType = self->GetTransform().CalculateTransformMatrixDisableNonUniformScale (transformMatrix, scaleOnly, scale);
+ SetupObjectMatrix (transformMatrix, matrixType);
+
+ self->Render (material, *s_ScriptingCurrentChannels);
+ device.SetViewMatrix(matView);
+ device.SetWorldMatrix(matWorld);
+
+ #if UNITY_EDITOR
+ if( outsideOfFrame )
+ device.EndFrame();
+ #endif
+ }
+END
+
+
+
+// A script interface for a [[wiki:class-Projector|projector component]].
+CLASS Projector : Behaviour
+
+ // The near clipping plane distance.
+ AUTO_PROP float nearClipPlane GetNearClipPlane SetNearClipPlane
+
+ // The far clipping plane distance.
+ AUTO_PROP float farClipPlane GetFarClipPlane SetFarClipPlane
+
+ // The field of view of the projection in degrees.
+ AUTO_PROP float fieldOfView GetFieldOfView SetFieldOfView
+
+ // The aspect ratio of the projection.
+ AUTO_PROP float aspectRatio GetAspectRatio SetAspectRatio
+
+ OBSOLETE planned Use orthographic instead
+ CSRAW public bool isOrthoGraphic { get { return orthographic; } set { orthographic = value; } }
+
+ // Is the projection orthographic (''true'') or perspective (''false'')?
+ AUTO_PROP bool orthographic GetOrthographic SetOrthographic
+
+ // Projection's half-size when in orthographic mode.
+ AUTO_PROP float orthographicSize GetOrthographicSize SetOrthographicSize
+
+ OBSOLETE planned use orthographicSize
+ CSRAW public float orthoGraphicSize { get { return orthographicSize; } set { orthographicSize = value; } }
+
+ // Which object layers are ignored by the projector.
+ AUTO_PROP int ignoreLayers GetIgnoreLayers SetIgnoreLayers
+
+ // The material that will be projected onto every object.
+ AUTO_PTR_PROP Material material GetMaterial SetMaterial
+END
+
+// A script interface for the [[wiki:class-Skybox|skybox component]].
+CLASS Skybox : Behaviour
+ // The material used by the skybox.
+ CUSTOM_PROP Material material { return Scripting::ScriptingWrapperFor (self->GetMaterial ()); } { self->SetMaterial (value); }
+END
+
+
+// A script interface for the [[wiki:class-TextMesh|text mesh component]].
+CLASS TextMesh : Component
+
+ // The text that is displayed.
+ CUSTOM_PROP string text { return scripting_string_new (self->GetText ()); } { self->SetText (value); }
+
+ // The [[Font]] used.
+ AUTO_PTR_PROP Font font GetFont SetFont
+
+
+ // The font size to use (for dynamic fonts)
+ AUTO_PROP int fontSize GetFontSize SetFontSize
+
+ // The font style to use (for dynamic fonts)
+ AUTO_PROP FontStyle fontStyle GetFontStyle SetFontStyle
+
+ // How far should the text be offset from the transform.position.z when drawing
+ AUTO_PROP float offsetZ GetOffsetZ SetOffsetZ
+
+ // How lines of text are aligned (Left, Right, Center)
+ AUTO_PROP TextAlignment alignment GetAlignment SetAlignment
+
+ // Which point of the text shares the position of the Transform
+ AUTO_PROP TextAnchor anchor GetAnchor SetAnchor
+
+ // The size of each character (This scales the whole text)
+ AUTO_PROP float characterSize GetCharacterSize SetCharacterSize
+
+ // How much space will be in-between lines of text
+ AUTO_PROP float lineSpacing GetLineSpacing SetLineSpacing
+
+ // How much space will be inserted for a tab '\t' character. This is a multiplum of the 'spacebar' character offset
+ AUTO_PROP float tabSize GetTabSize SetTabSize
+
+ // Enable HTML-style tags for Text Formatting Markup.
+ AUTO_PROP bool richText GetRichText SetRichText
+
+ // Base color in which to render the text
+ AUTO_PROP Color color GetColor SetColor
+END
+
+//(Legacy Particle system)
+STRUCT Particle
+ CSRAW
+ private Vector3 m_Position;
+ private Vector3 m_Velocity;
+ private float m_Size;
+ private float m_Rotation;
+ private float m_AngularVelocity;
+ private float m_Energy;
+ private float m_StartEnergy;
+ private Color m_Color;
+
+ // The position of the particle.
+ CSRAW public Vector3 position { get { return m_Position; } set { m_Position = value; } }
+
+ // The velocity of the particle.
+ CSRAW public Vector3 velocity { get { return m_Velocity; } set { m_Velocity = value; } }
+
+ // The energy of the particle.
+ CSRAW public float energy { get { return m_Energy; } set { m_Energy = value; } }
+
+ // The starting energy of the particle.
+ CSRAW public float startEnergy { get { return m_StartEnergy; } set { m_StartEnergy = value; } }
+
+ // The size of the particle.
+ CSRAW public float size { get { return m_Size; } set { m_Size = value; } }
+
+ // The rotation of the particle.
+ CSRAW public float rotation { get { return m_Rotation; } set { m_Rotation = value; } }
+
+ // The angular velocity of the particle.
+ CSRAW public float angularVelocity { get { return m_AngularVelocity; } set { m_AngularVelocity = value; } }
+
+ // The color of the particle.
+ CSRAW public Color color { get { return m_Color; } set { m_Color = value; } }
+END
+
+// Simple struct that contains all the arguments needed by the internal DrawTexture.
+STRUCT internal InternalEmitParticleArguments
+ CSRAW public Vector3 pos;
+ CSRAW public Vector3 velocity;
+ CSRAW public float size;
+ CSRAW public float energy;
+ CSRAW public Color color;
+ CSRAW public float rotation;
+ CSRAW public float angularVelocity;
+END
+
+C++RAW
+
+struct MonoInternalEmitParticleArguments {
+ Vector3f pos;
+ Vector3f velocity;
+ float size;
+ float energy;
+ ColorRGBAf color;
+ float rotation;
+ float angularVelocity;
+};
+
+// (Legacy Particles) Script interface for particle emitters.
+CLASS ParticleEmitter : Component
+
+ // Should particles be automatically emitted each frame?
+ AUTO_PROP bool emit IsEmitting SetEmit
+
+ // The minimum size each particle can be at the time when it is spawned.
+ AUTO_PROP float minSize GetMinSize SetMinSize
+
+ // The maximum size each particle can be at the time when it is spawned.
+ AUTO_PROP float maxSize GetMaxSize SetMaxSize
+
+ // The minimum lifetime of each particle, measured in seconds.
+ AUTO_PROP float minEnergy GetMinEnergy SetMinEnergy
+
+ // The maximum lifetime of each particle, measured in seconds.
+ AUTO_PROP float maxEnergy GetMaxEnergy SetMaxEnergy
+
+ // The minimum number of particles that will be spawned every second.
+ AUTO_PROP float minEmission GetMinEmission SetMinEmission
+
+ // The maximum number of particles that will be spawned every second.
+ AUTO_PROP float maxEmission GetMaxEmission SetMaxEmission
+
+ // he amount of the emitter's speed that the particles inherit.
+ AUTO_PROP float emitterVelocityScale GetEmitterVelocityScale SetEmitterVelocityScale
+
+ // The starting speed of particles in world space, along X, Y, and Z.
+ AUTO_PROP Vector3 worldVelocity GetWorldVelocity SetWorldVelocity
+
+ // The starting speed of particles along X, Y, and Z, measured in the object's orientation.
+ AUTO_PROP Vector3 localVelocity GetLocalVelocity SetLocalVelocity
+
+ // A random speed along X, Y, and Z that is added to the velocity.
+ AUTO_PROP Vector3 rndVelocity GetRndVelocity SetRndVelocity
+
+ // If enabled, the particles don't move when the emitter moves. If false, when you move the emitter, the particles follow it around.
+ AUTO_PROP bool useWorldSpace GetUseWorldSpace SetUseWorldSpace
+
+ // If enabled, the particles will be spawned with random rotations.
+ AUTO_PROP bool rndRotation GetRndRotation SetRndRotation
+
+ // The angular velocity of new particles in degrees per second.
+ AUTO_PROP float angularVelocity GetAngularVelocity SetAngularVelocity
+
+ // A random angular velocity modifier for new particles.
+ AUTO_PROP float rndAngularVelocity GetRndAngularVelocity SetRndAngularVelocity
+
+ // Returns a copy of all particles and assigns an array of all particles to be the current particles.
+ CUSTOM_PROP Particle[] particles
+ {
+ int size = self->GetParticleCount();
+ ScriptingArrayPtr array = CreateScriptingArray<SimpleParticle> (GetScriptingManager().GetCommonClasses().particle, self->GetParticleCount());
+ self->ReadParticles(Scripting::GetScriptingArrayStart<SimpleParticle>(array), 0, size);
+ return array;
+ }
+ {
+ self->WriteParticles(Scripting::GetScriptingArrayStart<SimpleParticle>(value), GetScriptingArraySize(value));
+ }
+
+ // The current number of particles (RO).
+ AUTO_PROP int particleCount GetParticleCount
+
+ // Removes all particles from the particle emitter.
+ AUTO void ClearParticles();
+
+ // Emit a number of particles.
+ CSRAW public void Emit () { Emit2 ( (int)Random.Range (minEmission, maxEmission) ); }
+
+ // Emit /count/ particles immediately
+
+ CSRAW public void Emit (int count) { Emit2 (count);}
+
+ // Emit a single particle with given parameters.
+ CSRAW public void Emit (Vector3 pos, Vector3 velocity, float size, float energy, Color color)
+ {
+ InternalEmitParticleArguments args = new InternalEmitParticleArguments();
+ args.pos = pos;
+ args.velocity = velocity;
+ args.size = size;
+ args.energy = energy;
+ args.color = color;
+ args.rotation = 0;
+ args.angularVelocity = 0;
+ Emit3 (ref args);
+ }
+ //
+ CSRAW public void Emit (Vector3 pos, Vector3 velocity, float size, float energy, Color color, float rotation, float angularVelocity)
+ {
+ InternalEmitParticleArguments args = new InternalEmitParticleArguments();
+ args.pos = pos;
+ args.velocity = velocity;
+ args.size = size;
+ args.energy = energy;
+ args.color = color;
+ args.rotation = rotation;
+ args.angularVelocity = angularVelocity;
+ Emit3 (ref args);
+ }
+
+ CUSTOM private void Emit2 (int count)
+ {
+ self->EmitResetEmitterPos (count, 0.0F);
+ }
+
+ CUSTOM private void Emit3 (ref InternalEmitParticleArguments args)
+ {
+ self->Emit (args.pos, args.velocity, args.size, args.energy, args.color, args.rotation, args.angularVelocity);
+ }
+
+ // Advance particle simulation by given time.
+ CUSTOM void Simulate (float deltaTime)
+ {
+ self->UpdateParticleSystem(deltaTime);
+ }
+
+ // Turns the ParticleEmitter on or off.
+ AUTO_PROP bool enabled GetEnabled SetEnabled
+END
+
+
+// (Legacy Particles) Particle animators move your particles over time, you use them to apply wind, drag & color cycling to your particle emitters.
+CLASS ParticleAnimator : Component
+ // Do particles cycle their color over their lifetime?
+ AUTO_PROP bool doesAnimateColor GetDoesAnimateColor SetDoesAnimateColor
+
+ // World space axis the particles rotate around.
+ AUTO_PROP Vector3 worldRotationAxis GetWorldRotationAxis SetWorldRotationAxis
+
+ // Local space axis the particles rotate around.
+ AUTO_PROP Vector3 localRotationAxis GetLocalRotationAxis SetLocalRotationAxis
+
+ // How the particle sizes grow over their lifetime.
+ AUTO_PROP float sizeGrow GetSizeGrow SetSizeGrow
+
+ // A random force added to particles every frame.
+ AUTO_PROP Vector3 rndForce GetRndForce SetRndForce
+
+ // The force being applied to particles every frame.
+ AUTO_PROP Vector3 force GetForce SetForce
+
+ // How much particles are slowed down every frame.
+ AUTO_PROP float damping GetDamping SetDamping
+
+ // Does the [[GameObject]] of this particle animator auto destructs?
+ AUTO_PROP bool autodestruct GetAutodestruct SetAutodestruct
+
+ // Colors the particles will cycle through over their lifetime.
+
+ CUSTOM_PROP Color[] colorAnimation
+ {
+ ColorRGBAf col[ParticleAnimator::kColorKeys];
+ self->GetColorAnimation(col);
+ return CreateScriptingArray(col, ParticleAnimator::kColorKeys, GetScriptingManager().GetCommonClasses().color);
+ }
+ {
+ Scripting::RaiseIfNull(value);
+ if(GetScriptingArraySize(value) != ParticleAnimator::kColorKeys)
+ {
+ Scripting::RaiseMonoException(" Array needs to contain exactly 5 Colors for colorAnimation.");
+ return;
+ }
+ self->SetColorAnimation(Scripting::GetScriptingArrayStart<ColorRGBAf> (value));
+ }
+END
+
+
+// The trail renderer is used to make trails behind objects in the scene as they move about.
+CLASS TrailRenderer : Renderer
+
+ // How long does the trail take to fade out.
+ AUTO_PROP float time GetTime SetTime
+
+ // The width of the trail at the spawning point.
+ AUTO_PROP float startWidth GetStartWidth SetStartWidth
+
+ // The width of the trail at the end of the trail.
+ AUTO_PROP float endWidth GetEndWidth SetEndWidth
+
+ // Does the [[GameObject]] of this trail renderer auto destructs?
+ AUTO_PROP bool autodestruct GetAutodestruct SetAutodestruct
+
+END
+
+
+
+// The rendering mode for legacy particles.
+ENUM ParticleRenderMode
+ // Render the particles as billboards facing the player. (Default)
+ Billboard = 0,
+ // Stretch particles in the direction of motion.
+ Stretch = 3,
+ // Sort the particles back-to-front and render as billboards.
+ SortedBillboard = 2,
+ // Render the particles as billboards always facing up along the y-Axis.
+ HorizontalBillboard = 4,
+ // Render the particles as billboards always facing the player, but not pitching along the x-Axis.
+ VerticalBillboard = 5
+END
+
+// (Legacy Particles) Renders particles on to the screen.
+CLASS ParticleRenderer : Renderer
+ // How particles are drawn.
+ AUTO_PROP ParticleRenderMode particleRenderMode GetRenderMode SetRenderMode
+
+ // How much are the particles stretched in their direction of motion.
+ AUTO_PROP float lengthScale GetLengthScale SetLengthScale
+
+ // How much are the particles strectched depending on "how fast they move"
+ AUTO_PROP float velocityScale GetVelocityScale SetVelocityScale
+
+ // How much are the particles strected depending on the [[Camera]]'s speed.
+ AUTO_PROP float cameraVelocityScale GetCameraVelocityScale SetCameraVelocityScale
+
+ // Clamp the maximum particle size.
+ AUTO_PROP float maxParticleSize GetMaxParticleSize SetMaxParticleSize
+
+ // Set horizontal tiling count.
+ AUTO_PROP int uvAnimationXTile GetUVAnimationXTile SetUVAnimationXTile
+ // Set vertical tiling count.
+ AUTO_PROP int uvAnimationYTile GetUVAnimationYTile SetUVAnimationYTile
+
+ // Set uv animation cycles
+ AUTO_PROP float uvAnimationCycles GetUVAnimationCycles SetUVAnimationCycles
+
+ OBSOLETE warning animatedTextureCount has been replaced by uvAnimationXTile and uvAnimationYTile.
+ CSRAW public int animatedTextureCount { get { return uvAnimationXTile; } set { uvAnimationXTile = value; } }
+
+ //*undocumented fixed typo
+ CSRAW public float maxPartileSize { get { return maxParticleSize; } set { maxParticleSize = value; } }
+
+ //*undocumented UV Rect access
+ CUSTOM_PROP Rect[] uvTiles { return CreateScriptingArray(self->GetUVFrames(), self->GetNumUVFrames(), GetScriptingManager().GetCommonClasses().rect); } { Scripting::RaiseIfNull(value); self->SetUVFrames(Scripting::GetScriptingArrayStart<Rectf> (value), GetScriptingArraySize(value)); }
+
+CSRAW #if ENABLE_MONO
+ OBSOLETE error This function has been removed.
+ CSRAW public AnimationCurve widthCurve { get { return null; } set { } }
+
+ OBSOLETE error This function has been removed.
+ CSRAW public AnimationCurve heightCurve { get { return null; } set { } }
+
+ OBSOLETE error This function has been removed.
+ CSRAW public AnimationCurve rotationCurve { get { return null; } set { } }
+
+CSRAW #endif
+END
+
+
+// The line renderer is used to draw free-floating lines in 3D space.
+CLASS LineRenderer : Renderer
+ // Set the line width at the start and at the end.
+ AUTO void SetWidth(float start, float end);
+
+ // Set the line color at the start and at the end.
+ AUTO void SetColors(Color start, Color end);
+
+ // Set the number of line segments.
+ AUTO void SetVertexCount(int count);
+
+ // Set the position of the vertex in the line.
+ AUTO void SetPosition(int index, Vector3 position);
+
+ // If enabled, the lines are defined in world space.
+ AUTO_PROP bool useWorldSpace GetUseWorldSpace SetUseWorldSpace
+
+END
+
+
+
+// A block of material values to apply.
+CLASS MaterialPropertyBlock
+ // Just wraps a pointer to native property block object
+ CSRAW internal IntPtr m_Ptr;
+
+ //*undocumented*
+ CUSTOM internal void InitBlock()
+ {
+ MonoMaterialPropertyBlock* block = new MonoMaterialPropertyBlock();
+ self.SetPtr(block, MonoMaterialPropertyBlock::CleanupMonoMaterialPropertyBlock);
+ }
+ //*undocumented*
+ THREAD_SAFE
+ CUSTOM internal void DestroyBlock()
+ {
+ MonoMaterialPropertyBlock::CleanupMonoMaterialPropertyBlock((MonoMaterialPropertyBlock*)self.GetPtr());
+ }
+
+ //*undocumented*
+ CSRAW public MaterialPropertyBlock() { InitBlock(); }
+ CSRAW ~MaterialPropertyBlock() { DestroyBlock(); }
+
+ ///*listonly*
+ CSRAW public void AddFloat (string name, float value)
+ {
+ AddFloat( Shader.PropertyToID(name), value );
+ }
+ // Add a float material property.
+ CUSTOM void AddFloat (int nameID, float value)
+ {
+ ShaderLab::FastPropertyName name;
+ name.index = nameID;
+ self->AddProperty( name, &value, 1, 1, 1 );
+ }
+
+ ///*listonly*
+ CSRAW public void AddVector (string name, Vector4 value) { AddVector( Shader.PropertyToID(name), value ); }
+ // Add a vector material property.
+ CUSTOM void AddVector (int nameID, Vector4 value)
+ {
+ ShaderLab::FastPropertyName name;
+ name.index = nameID;
+#if ENABLE_MONO
+ self->AddProperty( name, value.GetPtr(), 1, 4, 1 );
+#endif
+ }
+
+ ///*listonly*
+ CSRAW public void AddColor (string name, Color value)
+ {
+ AddColor( Shader.PropertyToID(name), value );
+ }
+ // Add a color material property.
+ CUSTOM void AddColor (int nameID, Color value)
+ {
+ ShaderLab::FastPropertyName name;
+ name.index = nameID;
+#if ENABLE_MONO
+ self->AddPropertyColor( name, value );
+#endif
+ }
+
+ ///*listonly*
+ CSRAW public void AddMatrix (string name, Matrix4x4 value) { AddMatrix( Shader.PropertyToID(name), value ); }
+ // Add a matrix material property.
+ CUSTOM void AddMatrix (int nameID, Matrix4x4 value)
+ {
+ ShaderLab::FastPropertyName name;
+ name.index = nameID;
+#if ENABLE_MONO
+ self->AddProperty( name, value.GetPtr(), 4, 4, 1 );
+#endif
+ }
+
+ ///*listonly*
+ CSRAW public void AddTexture (string name, Texture value)
+ {
+ AddTexture (Shader.PropertyToID(name), value);
+ }
+ CUSTOM void AddTexture (int nameID, Texture value)
+ {
+ Texture* tex = value;
+ if (!tex)
+ {
+ ErrorString ("Invalid argument for AddTexture()");
+ return;
+ }
+ ShaderLab::FastPropertyName name;
+ name.index = nameID;
+ self->AddPropertyTexture (name, tex->GetDimension(), tex->GetTextureID());
+ }
+
+ CSRAW public float GetFloat (string name) { return GetFloat (Shader.PropertyToID(name)); }
+ CUSTOM float GetFloat (int nameID)
+ {
+ ShaderLab::FastPropertyName name;
+ name.index = nameID;
+ const float* v = self->FindFloat (name);
+ return v ? *v : 0.0f;
+ }
+
+ CSRAW public Vector4 GetVector (string name) { return GetVector (Shader.PropertyToID(name)); }
+ CUSTOM Vector4 GetVector (int nameID)
+ {
+ ShaderLab::FastPropertyName name;
+ name.index = nameID;
+ const Vector4f* v = self->FindVector (name);
+ return v ? *v : Vector4f(0,0,0,0);
+ }
+
+ CSRAW public Matrix4x4 GetMatrix (string name) { return GetMatrix (Shader.PropertyToID(name)); }
+ CUSTOM Matrix4x4 GetMatrix (int nameID)
+ {
+ ShaderLab::FastPropertyName name;
+ name.index = nameID;
+ const Matrix4x4f* v = self->FindMatrix (name);
+ return v ? *v : Matrix4x4f::identity;
+ }
+
+ CSRAW public Texture GetTexture (string name) { return GetTexture (Shader.PropertyToID(name)); }
+ CUSTOM Texture GetTexture (int nameID)
+ {
+ ShaderLab::FastPropertyName name;
+ name.index = nameID;
+ TextureID v = self->FindTexture (name);
+ if (v.m_ID == 0)
+ return SCRIPTING_NULL;
+ Texture* tex = Texture::FindTextureByID (v);
+ return Scripting::ScriptingWrapperFor (tex);
+ }
+
+ // Clear material property values.
+ CUSTOM void Clear() {
+#if ENABLE_MONO
+ self->Clear();
+#endif
+ }
+
+END
+
+
+// Color or depth buffer part of a [[RenderTexture]].
+STRUCT RenderBuffer
+ CSRAW internal int m_RenderTextureInstanceID;
+ CSRAW internal IntPtr m_BufferPtr;
+END
+
+// Simple struct that contains all the arguments needed by the internal DrawTexture.
+STRUCT internal InternalDrawTextureArguments
+ CSRAW public Rect screenRect;
+ #if UNITY_WINRT
+ CSRAW public int textureInstanceId;
+ #else
+ CSRAW public Texture texture;
+ #endif
+ CSRAW public Rect sourceRect;
+ CSRAW public int leftBorder;
+ CSRAW public int rightBorder;
+ CSRAW public int topBorder;
+ CSRAW public int bottomBorder;
+ CSRAW public Color32 color;
+ #if UNITY_WINRT
+ CSRAW public int matInstanceId;
+ #else
+ CSRAW public Material mat;
+ #endif
+END
+
+C++RAW
+
+struct MonoInternalDrawTextureArguments {
+ Rectf screenRect;
+ #if UNITY_WINRT
+ int textureInstanceId;
+ #else
+ ScriptingObjectOfType<Texture> texture;
+ #endif
+ Rectf sourceRect;
+ int leftBorder;
+ int rightBorder;
+ int topBorder;
+ int bottomBorder;
+ ColorRGBA32 color;
+ #if UNITY_WINRT
+ int matInstanceId;
+ #else
+ ScriptingObjectOfType<Material> mat;
+ #endif
+};
+
+// Simple struct that contains the arguments needed by Internal_DrawMeshTR.
+STRUCT internal Internal_DrawMeshTRArguments
+ CSRAW public int layer;
+ CSRAW public int submeshIndex;
+ CSRAW public Quaternion rotation;
+ CSRAW public Vector3 position;
+ CSRAW public int castShadows;
+ CSRAW public int receiveShadows;
+END
+
+C++RAW
+
+struct MonoInternal_DrawMeshTRArguments {
+ int layer;
+ int submeshIndex;
+ Quaternionf rotation;
+ Vector3f position;
+ int castShadows;
+ int receiveShadows;
+};
+
+// Simple struct that contains all the arguments needed by Internal_DrawMeshMatrix.
+STRUCT internal Internal_DrawMeshMatrixArguments
+ CSRAW public int layer;
+ CSRAW public int submeshIndex;
+ CSRAW public Matrix4x4 matrix;
+ CSRAW public int castShadows;
+ CSRAW public int receiveShadows;
+END
+
+C++RAW
+
+struct MonoInternal_DrawMeshMatrixArguments {
+ int layer;
+ int submeshIndex;
+ Matrix4x4f matrix;
+ int castShadows;
+ int receiveShadows;
+};
+
+// Raw interface to Unity's drawing functions.
+CLASS Graphics
+
+ /// *listonly*
+ CSRAW static public void DrawMesh (Mesh mesh, Vector3 position, Quaternion rotation, Material material, int layer, Camera camera = null, int submeshIndex = 0, MaterialPropertyBlock properties = null) {
+ Internal_DrawMeshTRArguments arguments = new Internal_DrawMeshTRArguments ();
+ arguments.position = position;
+ arguments.rotation = rotation;
+ arguments.layer = layer;
+ arguments.submeshIndex = submeshIndex;
+ arguments.castShadows = 1;
+ arguments.receiveShadows = 1;
+ Internal_DrawMeshTR(ref arguments, properties, material, mesh, camera);
+ }
+ // Draw a mesh.
+ CSRAW static public void DrawMesh (Mesh mesh, Matrix4x4 matrix, Material material, int layer, Camera camera = null, int submeshIndex = 0, MaterialPropertyBlock properties = null) {
+ Internal_DrawMeshMatrixArguments arguments = new Internal_DrawMeshMatrixArguments ();
+ arguments.matrix = matrix;
+ arguments.layer = layer;
+ arguments.submeshIndex = submeshIndex;
+ arguments.castShadows = 1;
+ arguments.receiveShadows = 1;
+
+ Internal_DrawMeshMatrix(ref arguments, properties, material, mesh, camera);
+ }
+ //*undocumented*
+ CSRAW static public void DrawMesh (Mesh mesh, Vector3 position, Quaternion rotation, Material material, int layer, Camera camera, int submeshIndex, MaterialPropertyBlock properties, bool castShadows, bool receiveShadows) {
+ Internal_DrawMeshTRArguments arguments = new Internal_DrawMeshTRArguments ();
+ arguments.position = position;
+ arguments.rotation = rotation;
+ arguments.layer = layer;
+ arguments.submeshIndex = submeshIndex;
+ arguments.castShadows = castShadows ? 1 : 0;
+ arguments.receiveShadows = receiveShadows ? 1 : 0;
+
+ Internal_DrawMeshTR(ref arguments, properties, material, mesh, camera);
+ }
+ //*undocumented*
+ CSRAW static public void DrawMesh (Mesh mesh, Matrix4x4 matrix, Material material, int layer, Camera camera, int submeshIndex, MaterialPropertyBlock properties, bool castShadows, bool receiveShadows) {
+ Internal_DrawMeshMatrixArguments arguments = new Internal_DrawMeshMatrixArguments ();
+ arguments.matrix = matrix;
+ arguments.layer = layer;
+ arguments.submeshIndex = submeshIndex;
+ arguments.castShadows = castShadows ? 1 : 0;
+ arguments.receiveShadows = receiveShadows ? 1 : 0;
+
+ Internal_DrawMeshMatrix(ref arguments, properties, material, mesh, camera);
+ }
+ CUSTOM private static void Internal_DrawMeshTR (ref Internal_DrawMeshTRArguments arguments, MaterialPropertyBlock properties, Material material, Mesh mesh, Camera camera) {
+ Camera* cameraPointer = camera;
+ Matrix4x4f matrix;
+ matrix.SetTR( arguments.position, arguments.rotation );
+
+ IntermediateRenderer* r = AddMeshIntermediateRenderer( matrix, mesh, material, arguments.layer, arguments.castShadows != 0, arguments.receiveShadows != 0, arguments.submeshIndex, cameraPointer );
+ MaterialPropertyBlock* propertiesPtr = properties.GetPtr();
+ if (propertiesPtr)
+ r->SetPropertyBlock (*propertiesPtr);
+ }
+ CUSTOM private static void Internal_DrawMeshMatrix (ref Internal_DrawMeshMatrixArguments arguments, MaterialPropertyBlock properties, Material material, Mesh mesh, Camera camera) {
+ Camera* cameraPointer = camera;
+ IntermediateRenderer* r = AddMeshIntermediateRenderer( arguments.matrix, mesh, material, arguments.layer, arguments.castShadows != 0, arguments.receiveShadows != 0, arguments.submeshIndex, cameraPointer);
+
+ MaterialPropertyBlock* propertiesPtr = properties.GetPtr();
+ if (propertiesPtr)
+ r->SetPropertyBlock (*propertiesPtr);
+ }
+
+ /// *listonly*
+ CSRAW static public void DrawMeshNow (Mesh mesh, Vector3 position, Quaternion rotation) { Internal_DrawMeshNow1(mesh, position, rotation, -1); }
+ /// *listonly*
+ CSRAW static public void DrawMeshNow (Mesh mesh, Vector3 position, Quaternion rotation, int materialIndex) { Internal_DrawMeshNow1(mesh, position, rotation, materialIndex); }
+ /// *listonly*
+ CSRAW static public void DrawMeshNow (Mesh mesh, Matrix4x4 matrix) { Internal_DrawMeshNow2(mesh, matrix, -1); }
+
+ // Draw a mesh immediately.
+ CSRAW static public void DrawMeshNow (Mesh mesh, Matrix4x4 matrix, int materialIndex) { Internal_DrawMeshNow2(mesh, matrix, materialIndex); }
+
+ CUSTOM private static void Internal_DrawMeshNow1 (Mesh mesh, Vector3 position, Quaternion rotation, int materialIndex) {
+ Shader* shader = s_ScriptingCurrentShader;
+ if (!shader) {
+ ErrorString ("DrawMesh requires material.SetPass before!");
+ return;
+ }
+ DrawUtil::DrawMesh (*s_ScriptingCurrentChannels, *mesh, position, rotation);
+ }
+ CUSTOM private static void Internal_DrawMeshNow2 (Mesh mesh, Matrix4x4 matrix, int materialIndex) {
+ Shader* shader = s_ScriptingCurrentShader;
+ if (!shader) {
+ ErrorString ("DrawMesh requires material.SetPass before!");
+ return;
+ }
+ DrawUtil::DrawMesh (*s_ScriptingCurrentChannels, *mesh, matrix, materialIndex);
+ }
+
+
+ // Draws a fully procedural geometry on the GPU.
+ CUSTOM static public void DrawProcedural (MeshTopology topology, int vertexCount, int instanceCount = 1) {
+ DrawUtil::DrawProcedural (topology, vertexCount, instanceCount);
+ }
+
+ // Draws a fully procedural geometry on the GPU.
+ CONDITIONAL !UNITY_FLASH
+ CUSTOM static public void DrawProceduralIndirect (MeshTopology topology, ComputeBuffer bufferWithArgs, int argsOffset = 0) {
+ DrawUtil::DrawProceduralIndirect (topology, bufferWithArgs.GetPtr(), argsOffset);
+ }
+
+
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM static internal void DrawSprite (Sprite sprite, Matrix4x4 matrix, Material material, int layer, Camera camera, Color color, MaterialPropertyBlock properties)
+ {
+ Camera* cameraPointer = camera;
+ IntermediateRenderer* r = AddSpriteIntermediateRenderer(matrix, sprite, material, layer, color, cameraPointer);
+
+ MaterialPropertyBlock* propertiesPtr = properties.GetPtr();
+ if (propertiesPtr)
+ r->SetPropertyBlock (*propertiesPtr);
+ }
+
+
+
+ FLUSHCONDITIONS
+
+ OBSOLETE warning Use Graphics.DrawMeshNow instead.
+ CSRAW static public void DrawMesh (Mesh mesh, Vector3 position, Quaternion rotation) { Internal_DrawMeshNow1(mesh, position, rotation, -1); }
+ OBSOLETE warning Use Graphics.DrawMeshNow instead.
+ CSRAW static public void DrawMesh (Mesh mesh, Vector3 position, Quaternion rotation, int materialIndex) { Internal_DrawMeshNow1(mesh, position, rotation, materialIndex); }
+ OBSOLETE warning Use Graphics.DrawMeshNow instead.
+ CSRAW static public void DrawMesh (Mesh mesh, Matrix4x4 matrix) { Internal_DrawMeshNow2(mesh, matrix, -1); }
+ OBSOLETE warning Use Graphics.DrawMeshNow instead.
+ CSRAW static public void DrawMesh (Mesh mesh, Matrix4x4 matrix, int materialIndex) { Internal_DrawMeshNow2(mesh, matrix, materialIndex); }
+
+
+ ///*listonly*
+ CSRAW public static void DrawTexture (Rect screenRect, Texture texture, Material mat = null) {
+ DrawTexture (screenRect, texture, 0,0,0,0, mat);
+ }
+ ///*listonly*
+ CSRAW public static void DrawTexture (Rect screenRect, Texture texture, int leftBorder, int rightBorder, int topBorder, int bottomBorder, Material mat = null) {
+ DrawTexture (screenRect, texture, new Rect (0,0,1,1), leftBorder, rightBorder, topBorder, bottomBorder, mat);
+ }
+ ///*listonly*
+ CSRAW public static void DrawTexture (Rect screenRect, Texture texture, Rect sourceRect, int leftBorder, int rightBorder, int topBorder, int bottomBorder, Material mat = null) {
+ InternalDrawTextureArguments arguments = new InternalDrawTextureArguments ();
+ arguments.screenRect = screenRect;
+ #if UNITY_WINRT
+ arguments.textureInstanceId = texture != null ? texture.GetInstanceID() : 0;
+ #else
+ arguments.texture = texture;
+ #endif
+ arguments.sourceRect = sourceRect;
+ arguments.leftBorder = leftBorder;
+ arguments.rightBorder = rightBorder;
+ arguments.topBorder = topBorder;
+ arguments.bottomBorder = bottomBorder;
+ Color32 c = new Color32();
+ c.r = c.g = c.b = c.a = 128;
+ arguments.color = c;
+ #if UNITY_WINRT
+ arguments.matInstanceId = mat != null ? mat.GetInstanceID() : 0;
+ #else
+ arguments.mat = mat;
+ #endif
+ DrawTexture (ref arguments);
+ }
+ // Draw a texture in screen coordinates.
+ CSRAW public static void DrawTexture (Rect screenRect, Texture texture, Rect sourceRect, int leftBorder, int rightBorder, int topBorder, int bottomBorder, Color color, Material mat = null) {
+ InternalDrawTextureArguments arguments = new InternalDrawTextureArguments ();
+ arguments.screenRect = screenRect;
+ #if UNITY_WINRT
+ arguments.textureInstanceId = texture != null ? texture.GetInstanceID() : 0;
+ #else
+ arguments.texture = texture;
+ #endif
+ arguments.sourceRect = sourceRect;
+ arguments.leftBorder = leftBorder;
+ arguments.rightBorder = rightBorder;
+ arguments.topBorder = topBorder;
+ arguments.bottomBorder = bottomBorder;
+ arguments.color = color;
+ #if UNITY_WINRT
+ arguments.matInstanceId = mat != null ? mat.GetInstanceID() : 0;
+ #else
+ arguments.mat = mat;
+ #endif
+ DrawTexture (ref arguments);
+ }
+
+ CUSTOM internal static void DrawTexture (ref InternalDrawTextureArguments arguments) {
+ #if UNITY_WINRT
+ DrawGUITexture (
+ arguments.screenRect,
+ PPtr<Texture>(arguments.textureInstanceId),
+ arguments.sourceRect,
+ arguments.leftBorder,
+ arguments.rightBorder,
+ arguments.topBorder,
+ arguments.bottomBorder,
+ ColorRGBA32(arguments.color),
+ PPtr<Material>(arguments.matInstanceId));
+ #else
+ DrawGUITexture (arguments.screenRect, arguments.texture, arguments.sourceRect, arguments.leftBorder, arguments.rightBorder, arguments.topBorder, arguments.bottomBorder, ColorRGBA32(arguments.color), arguments.mat);
+ #endif
+ }
+
+ /// *listonly*
+ CUSTOM static void Blit (Texture source, RenderTexture dest)
+ {
+ ImageFilters::Blit (source, dest);
+ }
+
+ // Copies source texture into destination render texture.
+ CSRAW public static void Blit (Texture source, RenderTexture dest, Material mat, int pass = -1)
+ {
+ Internal_BlitMaterial (source, dest, mat, pass, true);
+ }
+
+ CSRAW public static void Blit (Texture source, Material mat, int pass = -1)
+ {
+ Internal_BlitMaterial (source, null, mat, pass, false);
+ }
+
+ CUSTOM private static void Internal_BlitMaterial (Texture source, RenderTexture dest, Material mat, int pass, bool setRT)
+ {
+ Material* m = mat;
+ if (m == NULL)
+ {
+ ErrorString ("Graphics.Blit: material is null");
+ return;
+ }
+ ImageFilters::Blit (source, dest, m, pass, setRT);
+ }
+
+ // Copies source texture into destination, for multi-tap shader.
+ CSRAW public static void BlitMultiTap (Texture source, RenderTexture dest, Material mat, params Vector2[] offsets)
+ {
+ Internal_BlitMultiTap (source, dest, mat, offsets);
+ }
+
+ CUSTOM private static void Internal_BlitMultiTap (Texture source, RenderTexture dest, Material mat, Vector2[] offsets)
+ {
+ Material* m = mat;
+ if (m == NULL)
+ {
+ ErrorString ("Graphics.BlitMultiTap: material is null");
+ return;
+ }
+ int size = GetScriptingArraySize (offsets);
+ const Vector2f* arr = Scripting::GetScriptingArrayStart<Vector2f> (offsets);
+ ImageFilters::BlitMultiTap (source, dest, m, size, arr);
+ }
+
+
+ CSRAW public static void SetRenderTarget (RenderTexture rt) {
+ Internal_SetRT (rt, 0, -1);
+ }
+ CSRAW public static void SetRenderTarget (RenderTexture rt, int mipLevel) {
+ Internal_SetRT (rt, mipLevel, -1);
+ }
+ CSRAW public static void SetRenderTarget (RenderTexture rt, int mipLevel, CubemapFace face) {
+ Internal_SetRT (rt, mipLevel, (int)face);
+ }
+
+ ///*listonly*
+ CSRAW public static void SetRenderTarget (RenderBuffer colorBuffer, RenderBuffer depthBuffer) {
+ Internal_SetRTBuffer (out colorBuffer, out depthBuffer);
+ }
+
+ // Sets current render target.
+ CSRAW public static void SetRenderTarget (RenderBuffer[] colorBuffers, RenderBuffer depthBuffer) {
+ Internal_SetRTBuffers (colorBuffers, out depthBuffer);
+ }
+
+ CUSTOM private static void Internal_SetRT (RenderTexture rt, int mipLevel, int face) {
+ RenderTexture::SetActive (rt, mipLevel, (CubemapFace)face);
+ }
+
+ CUSTOM private static void Internal_SetRTBuffer (out RenderBuffer colorBuffer, out RenderBuffer depthBuffer) {
+ RenderTexture* rtC = PPtr<RenderTexture>(colorBuffer->m_RenderTextureInstanceID);
+ RenderTexture* rtD = PPtr<RenderTexture>(depthBuffer->m_RenderTextureInstanceID);
+ if (rtC && !rtD)
+ {
+ ErrorString ("SetRenderTarget can only mix color & depth buffers from RenderTextures. You're trying to set depth buffer from the screen.");
+ return;
+ }
+ if (!rtC && rtD)
+ {
+ ErrorString ("SetRenderTarget can only mix color & depth buffers from RenderTextures. You're trying to set color buffer from the screen.");
+ return;
+ }
+ if (rtC)
+ {
+ RenderSurfaceHandle rsHandle(colorBuffer->m_BufferPtr);
+ RenderTexture::SetActive (1, &rsHandle, RenderSurfaceHandle(depthBuffer->m_BufferPtr), rtC);
+ }
+ else
+ {
+ RenderTexture::SetActive (NULL);
+ }
+ }
+
+ CUSTOM private static void Internal_SetRTBuffers (RenderBuffer[] colorBuffers, out RenderBuffer depthBuffer) {
+ int size = GetScriptingArraySize (colorBuffers);
+ if (size < 1 || size > kMaxSupportedRenderTargets)
+ {
+ ErrorString ("Invalid color buffer count for SetRenderTarget");
+ return;
+ }
+
+ const ScriptingRenderBuffer* arr = Scripting::GetScriptingArrayStart<ScriptingRenderBuffer> (colorBuffers);
+ RenderTexture* rtC = PPtr<RenderTexture>(arr[0].m_RenderTextureInstanceID);
+ RenderTexture* rtD = PPtr<RenderTexture>(depthBuffer->m_RenderTextureInstanceID);
+ if (rtC && !rtD)
+ {
+ ErrorString ("SetRenderTarget can only mix color & depth buffers from RenderTextures. You're trying to set depth buffer from the screen.");
+ return;
+ }
+ if (!rtC && rtD)
+ {
+ ErrorString ("SetRenderTarget can only mix color & depth buffers from RenderTextures. You're trying to set color buffer from the screen.");
+ return;
+ }
+ if (rtC)
+ {
+ RenderSurfaceHandle colorHandles[kMaxSupportedRenderTargets];
+ for (int i = 0; i < size; ++i)
+ colorHandles[i].object = arr[i].m_BufferPtr;
+ RenderTexture::SetActive (size, colorHandles, RenderSurfaceHandle(depthBuffer->m_BufferPtr), rtC);
+ }
+ else
+ {
+ RenderTexture::SetActive (NULL);
+ }
+ }
+
+ // Currently active color buffer (RO).
+ CSRAW public static RenderBuffer activeColorBuffer { get { RenderBuffer res; GetActiveColorBuffer (out res); return res; } }
+
+ // Currently active depth buffer (RO).
+ CSRAW public static RenderBuffer activeDepthBuffer { get { RenderBuffer res; GetActiveDepthBuffer (out res); return res; } }
+
+ CUSTOM private static void GetActiveColorBuffer (out RenderBuffer res) {
+ GfxDevice& device = GetGfxDevice();
+ RenderTexture* rt = device.GetActiveRenderTexture();
+ res->m_RenderTextureInstanceID = rt ? rt->GetInstanceID() : 0;
+ res->m_BufferPtr = rt ? device.GetActiveRenderColorSurface(0).object : NULL;
+ }
+ CUSTOM private static void GetActiveDepthBuffer (out RenderBuffer res) {
+ GfxDevice& device = GetGfxDevice();
+ RenderTexture* rt = device.GetActiveRenderTexture();
+ res->m_RenderTextureInstanceID = rt ? rt->GetInstanceID() : 0;
+ res->m_BufferPtr = rt ? device.GetActiveRenderDepthSurface().object : NULL;
+ }
+
+ CSRAW public static void SetRandomWriteTarget (int index, RenderTexture uav) {
+ Internal_SetRandomWriteTargetRT (index, uav);
+ }
+
+ CONDITIONAL !UNITY_FLASH
+ CSRAW public static void SetRandomWriteTarget (int index, ComputeBuffer uav) {
+ Internal_SetRandomWriteTargetBuffer (index, uav);
+ }
+ CUSTOM public static void ClearRandomWriteTargets () {
+ GetGfxDevice().ClearRandomWriteTargets();
+ }
+
+ CUSTOM private static void Internal_SetRandomWriteTargetRT (int index, RenderTexture uav) {
+ RenderTexture* rt = uav;
+ TextureID tid = rt ? rt->GetTextureID() : TextureID();
+ GetGfxDevice().SetRandomWriteTargetTexture(index, tid);
+ }
+
+ CONDITIONAL !UNITY_FLASH
+ CUSTOM private static void Internal_SetRandomWriteTargetBuffer (int index, ComputeBuffer uav) {
+ GetGfxDevice().SetRandomWriteTargetBuffer(index, uav->GetBufferHandle());
+ }
+
+ FLUSHCONDITIONS
+
+ OBSOLETE warning Use SystemInfo.graphicsDeviceName instead.
+ CSRAW static public string deviceName { get { return SystemInfo.graphicsDeviceName; } }
+
+ OBSOLETE warning Use SystemInfo.graphicsDeviceVendor instead.
+ CSRAW static public string deviceVendor { get { return SystemInfo.graphicsDeviceVendor; } }
+
+ OBSOLETE warning Use SystemInfo.graphicsDeviceVersion instead.
+ CSRAW static public string deviceVersion { get { return SystemInfo.graphicsDeviceVersion; } }
+
+ OBSOLETE warning Use SystemInfo.supportsVertexPrograms instead.
+ CSRAW static public bool supportsVertexProgram { get { return SystemInfo.supportsVertexPrograms; } }
+
+ //*undocumented*
+ CUSTOM internal static void SetupVertexLights (Light[] lights)
+ {
+ const int size = GetScriptingArraySize(lights);
+ std::vector<Light*> lightsVec(size);
+ for (int i = 0; i < size; ++i)
+ lightsVec[i] = ScriptingObjectToObject<Light>(Scripting::GetScriptingArrayElementNoRef<ScriptingObjectPtr> (lights, i));
+
+ SetupVertexLights(lightsVec);
+ }
+
+END
+
+
+
+// Represents a display resolution.
+STRUCT Resolution
+ // Keep in sync with ScreenManager::Resolution
+ CSRAW private int m_Width;
+ CSRAW private int m_Height;
+ CSRAW private int m_RefreshRate;
+
+ // Resolution width in pixels.
+ CSRAW public int width { get { return m_Width; } set { m_Width = value; } }
+ // Resolution height in pixels.
+ CSRAW public int height { get { return m_Height; } set { m_Height = value; } }
+ // Resolution's vertical refresh rate in Hz.
+ CSRAW public int refreshRate { get { return m_RefreshRate; } set { m_RefreshRate = value; } }
+END
+
+
+
+// Data of a lightmap.
+CSRAW [StructLayout (LayoutKind.Sequential)]
+CLASS LightmapData
+ CSRAW internal Texture2D m_Lightmap;
+ CSRAW internal Texture2D m_IndirectLightmap;
+
+ // Lightmap storing the full incoming light.
+ CSRAW public Texture2D lightmapFar { get { return m_Lightmap; } set { m_Lightmap = value; } }
+
+ OBSOLETE warning Use lightmapFar instead
+ CSRAW public Texture2D lightmap { get { return m_Lightmap; } set { m_Lightmap = value; } }
+
+ // Lightmap storing only the indirect incoming light.
+ CSRAW public Texture2D lightmapNear { get { return m_IndirectLightmap; } set { m_IndirectLightmap = value; } }
+END
+
+// Single, dual, or directional lightmaps rendering mode.
+ENUM LightmapsMode
+ // Single, traditional lightmap rendering mode.
+ Single = 0,
+
+ // Dual lightmap rendering mode.
+ Dual = 1,
+
+ // Directional rendering mode.
+ Directional = 2,
+END
+
+// Valid color spaces
+ENUM ColorSpace
+ // Uninitialized colorspace
+ Uninitialized = -1,
+
+ // Lightmap has been baked for gamma rendering
+ Gamma = 0,
+
+ // Lightmap has been baked for linear rendering
+ Linear = 1
+END
+
+
+// Stores light probes for the scene.
+CONDITIONAL ENABLE_MONO || UNITY_WINRT
+CLASS LightProbes : Object
+ // Returns spherical harmonics coefficients of a light probe at the given position. The light probe is interpolated from the light probes baked
+ CUSTOM void GetInterpolatedLightProbe(Vector3 position, Renderer renderer, float[] coefficients)
+ {
+ if (GetScriptingArraySize(coefficients) != kLightProbeBasisCount * 3)
+ Scripting::RaiseArgumentException("Coefficients array must have 9*3 elements");
+
+ self->GetInterpolatedLightProbe(position, renderer, &Scripting::GetScriptingArrayElement<float> (coefficients, 0));
+ }
+
+ // Positions of the baked light probes.
+ CUSTOM_PROP Vector3[] positions
+ {
+ return CreateScriptingArray(self->GetPositions(), self->GetPositionsSize(), MONO_COMMON.vector3);
+ }
+
+ // Coefficients of the baked light probes. The coefficients represent a 3-band RGB spherical harmonics probe, with a total of 27 floats per light probe, laid out: rgbrgbrgb...
+
+ CUSTOM_PROP float[] coefficients
+ {
+ return CreateScriptingArray(reinterpret_cast<float*>(self->GetCoefficients()), self->GetPositionsSize()*kLightProbeCoefficientCount, MONO_COMMON.floatSingle);
+ }
+ {
+ if (GetScriptingArraySize(value) != self->GetPositionsSize() * kLightProbeBasisCount * 3)
+ Scripting::RaiseArgumentException("Coefficients array must have probeCount*9*3 elements");
+
+ self->SetCoefficients(&Scripting::GetScriptingArrayElement<float> (value, 0), GetScriptingArraySize(value));
+ }
+
+ // The number of light probes.
+ CUSTOM_PROP int count
+ {
+ return self->GetPositionsSize();
+ }
+
+ // The number of cells (tetrahedra + outer cells) the space is divided to.
+ CUSTOM_PROP int cellCount
+ {
+ return self->GetTetrahedraSize();
+ }
+END
+
+// Stores lightmaps of the scene.
+CONDITIONAL ENABLE_MONO || UNITY_WINRT
+CLASS LightmapSettings : Object
+
+ // Lightmap array.
+ CUSTOM_PROP static LightmapData[] lightmaps
+ { return VectorToScriptingClassArray<LightmapData, LightmapDataMono> (GetLightmapSettings().GetLightmaps(), GetScriptingTypeRegistry().GetType("UnityEngine", "LightmapData"), LightmapDataToMono); }
+ { GetLightmapSettings().SetLightmaps (ScriptingClassArrayToVector<LightmapData, LightmapDataMono> (value, LightmapDataToCpp)); }
+
+ // Single, Dual or Directional lightmaps rendering mode.
+ CUSTOM_PROP static LightmapsMode lightmapsMode
+ { return GetLightmapSettings().GetLightmapsMode (); }
+ { GetLightmapSettings().SetLightmapsMode (value); }
+
+ // Color space of the lightmap
+ CUSTOM_PROP static ColorSpace bakedColorSpace
+ { return GetLightmapSettings().GetBakedColorSpace (); }
+ { }
+
+ // Holds all data needed by the light probes.
+ CUSTOM_PROP static LightProbes lightProbes
+ {
+ return Scripting::ScriptingWrapperFor(GetLightmapSettings().GetLightProbes());
+ }
+ {
+ GetLightmapSettings().SetLightProbes(value);
+ }
+END
+
+// Utility class for common geometric functions.
+CLASS GeometryUtility
+
+ // Calculates frustum planes.
+ CSRAW static public Plane[] CalculateFrustumPlanes (Camera camera)
+ {
+ return CalculateFrustumPlanes (camera.projectionMatrix * camera.worldToCameraMatrix);
+ }
+
+ // Calculates frustum planes.
+ CSRAW static public Plane[] CalculateFrustumPlanes (Matrix4x4 worldToProjectionMatrix)
+ {
+ Plane[] planes = new Plane[6];
+ Internal_ExtractPlanes (planes, worldToProjectionMatrix);
+ return planes;
+ }
+
+ CUSTOM private static void Internal_ExtractPlanes (Plane[] planes, Matrix4x4 worldToProjectionMatrix)
+ {
+ ExtractProjectionPlanes(worldToProjectionMatrix, Scripting::GetScriptingArrayStart<Plane> (planes));
+ }
+
+
+ // Returns true if bounds are inside the plane array.
+ CUSTOM static bool TestPlanesAABB (Plane[] planes, Bounds bounds)
+ {
+ return TestPlanesAABB(Scripting::GetScriptingArrayStart<Plane> (planes), GetScriptingArraySize(planes), bounds);
+ }
+END
+
+
+// Describes screen orientation.
+ENUM ScreenOrientation
+ //*undocumented*
+ Unknown = 0,
+
+ // Portrait orientation.
+ Portrait = 1,
+
+ // Portrait orientation, upside down.
+ PortraitUpsideDown = 2,
+
+ // Landscape orientation, counter-clockwise from the portrait orientation.
+ LandscapeLeft = 3,
+
+ // Landscape orientation, clockwise from the portrait orientation.
+ LandscapeRight = 4,
+
+ // Auto Rotation, will use enabled orientations.
+ AutoRotation = 5,
+
+ //*undocumented*
+ Landscape = 3
+END
+
+
+// Access to display information.
+CLASS Screen
+
+ // All fullscreen resolutions supported by the monitor (RO).
+
+ CUSTOM_PROP static Resolution[] resolutions
+ {
+ ScriptingClassPtr klass = GetScriptingManager ().GetCommonClasses ().resolution;
+ ScreenManager::Resolutions resolutions = GetScreenManager ().GetResolutions ();
+ ScriptingArrayPtr array = CreateScriptingArray<ScreenManager::Resolution> (klass, resolutions.size ());
+ for (int i=0;i<resolutions.size ();i++)
+ Scripting::SetScriptingArrayElement (array, i, resolutions[i]);
+
+ return array;
+ }
+
+ OBSOLETE planned
+ CSRAW static public Resolution[] GetResolution { get { return resolutions; } }
+
+ // The current screen resolution (RO).
+ CUSTOM_PROP static Resolution currentResolution { return GetScreenManager().GetCurrentResolution (); }
+
+ // Switches the screen resolution.
+ CUSTOM static void SetResolution (int width, int height, bool fullscreen, int preferredRefreshRate = 0)
+ {
+ #if WEBPLUG
+ if (fullscreen)
+ {
+ if (!GetScreenManager ().GetAllowFullscreenSwitch())
+ {
+ ErrorString("Fullscreen mode can only be enabled in the web player after clicking on the content.");
+ return;
+ }
+ // when going from windowed to fullscreen, show escape warning
+ if( !GetScreenManager().IsFullScreen() )
+ ShowFullscreenEscapeWarning();
+ }
+ #endif
+
+ GetScreenManager ().RequestResolution (width, height, fullscreen, preferredRefreshRate);
+ }
+
+ // Should the cursor be visible?
+ CUSTOM_PROP static bool showCursor { return GetScreenManager ().GetShowCursor (); } { GetScreenManager ().SetShowCursor (value); }
+
+ // Should the cursor be locked?
+ CUSTOM_PROP static bool lockCursor { return GetScreenManager ().GetLockCursor (); } { GetScreenManager ().SetLockCursor (value); }
+
+
+ // The current width of the screen window in pixels (RO).
+ THREAD_SAFE
+ CUSTOM_PROP static int width { return GetScreenManager ().GetWidth (); }
+
+ // The current height of the screen window in pixels (RO).
+ THREAD_SAFE
+ CUSTOM_PROP static int height { return GetScreenManager ().GetHeight (); }
+
+ // The current DPI of the screen / device (RO).
+ CUSTOM_PROP static float dpi { return GetScreenManager ().GetDPI (); }
+
+ // Is the game running fullscreen?
+ CUSTOM_PROP static bool fullScreen
+ {
+ return GetScreenManager ().IsFullScreen ();
+ }
+ {
+ ScreenManager& screen = GetScreenManager();
+ bool goFullscreen = (bool)value;
+ if (goFullscreen == screen.IsFullScreen ())
+ {
+ return;
+ }
+
+ #if WEBPLUG
+ if (goFullscreen)
+ {
+ if (!GetScreenManager().GetAllowFullscreenSwitch())
+ {
+ ErrorString("Fullscreen mode can only be enabled in the web player after clicking on the content.");
+ return;
+ }
+ // when going from windowed to fullscreen, show escape warning
+ if( !screen.IsFullScreen() )
+ ShowFullscreenEscapeWarning();
+ }
+ #endif
+
+ screen.RequestSetFullscreen (goFullscreen);
+ }
+
+ // Allow auto-rotation to portrait?
+ CUSTOM_PROP static bool autorotateToPortrait {
+ return GetScreenManager().GetIsOrientationEnabled(kAutorotateToPortrait);
+ } {
+ GetScreenManager().SetIsOrientationEnabled(kAutorotateToPortrait, value);
+ }
+
+ // Allow auto-rotation to portrait, upside down?
+ CUSTOM_PROP static bool autorotateToPortraitUpsideDown {
+ return GetScreenManager().GetIsOrientationEnabled(kAutorotateToPortraitUpsideDown);
+ } {
+ GetScreenManager().SetIsOrientationEnabled(kAutorotateToPortraitUpsideDown, value);
+ }
+
+ // Allow auto-rotation to landscape left?
+ CUSTOM_PROP static bool autorotateToLandscapeLeft {
+ return GetScreenManager().GetIsOrientationEnabled(kAutorotateToLandscapeLeft);
+ } {
+ GetScreenManager().SetIsOrientationEnabled(kAutorotateToLandscapeLeft, value);
+ }
+
+ // Allow auto-rotation to landscape right?
+ CUSTOM_PROP static bool autorotateToLandscapeRight {
+ return GetScreenManager().GetIsOrientationEnabled(kAutorotateToLandscapeRight);
+ } {
+ GetScreenManager().SetIsOrientationEnabled(kAutorotateToLandscapeRight, value);
+ }
+
+
+ // Specifies logical orientation of the screen.
+ CUSTOM_PROP static ScreenOrientation orientation {
+ return GetScreenManager ().GetScreenOrientation ();
+ } {
+ GetScreenManager ().RequestOrientation (value);
+ }
+
+ // A power saving setting, allowing the screen to dim some time after the
+ CUSTOM_PROP static int sleepTimeout {
+ return GetScreenManager ().GetScreenTimeout ();
+ } {
+ GetScreenManager ().SetScreenTimeout (value);
+ }
+END
+
+// Constants for special values of [[Screen.sleepTimeout]]. Use them to
+CLASS SleepTimeout
+ // Prevent screen dimming.
+ CSRAW public const int NeverSleep = -1;
+
+ // Set the sleep timeout to whatever user has specified in the system
+ CSRAW public const int SystemSetting = -2;
+END
+
+
+
+// Low-level graphics library.
+CLASS GL
+ // Submit a vertex.
+ CUSTOM static void Vertex3 (float x, float y, float z) { GetGfxDevice().ImmediateVertex( x,y,z ); }
+ // Submit a vertex.
+ CUSTOM static void Vertex (Vector3 v) { GetGfxDevice().ImmediateVertex( v.x, v.y, v.z ); }
+
+ // Sets current vertex color.
+ CUSTOM static void Color (Color c) { GetGfxDevice().ImmediateColor(c.r, c.g, c.b, c.a); }
+
+ // Sets current texture coordinate (v.x,v.y,v.z) for all texture units.
+ CUSTOM static void TexCoord (Vector3 v) { GetGfxDevice().ImmediateTexCoordAll(v.x, v.y, v.z); }
+
+ // Sets current texture coordinate (x,y) for all texture units.
+ CUSTOM static void TexCoord2 (float x, float y) { GetGfxDevice().ImmediateTexCoordAll(x, y, 0.0f); }
+
+ // Sets current texture coordinate (x,y,z) for all texture units.
+ CUSTOM static void TexCoord3 (float x, float y, float z) { GetGfxDevice().ImmediateTexCoordAll(x, y, z); }
+
+ // Sets current texture coordinate (x,y) for the actual texture /unit/.
+ CUSTOM static void MultiTexCoord2 (int unit, float x, float y)
+ {
+ GetGfxDevice().ImmediateTexCoord( unit, x, y, 0.0f );
+ }
+
+ // Sets current texture coordinate (x,y,z) to the actual texture /unit/.
+ CUSTOM static void MultiTexCoord3 (int unit, float x, float y, float z)
+ {
+ GetGfxDevice().ImmediateTexCoord( unit, x, y, z );
+ }
+
+ // Sets current texture coordinate (v.x,v.y,v.z) to the actual texture /unit/.
+ CUSTOM static void MultiTexCoord (int unit, Vector3 v)
+ {
+ GetGfxDevice().ImmediateTexCoord( unit, v.x, v.y, v.z );
+ }
+
+
+ // Mode for ::ref::Begin: draw triangles.
+ CSRAW public const int TRIANGLES = 0x0004;
+
+ // Mode for ::ref::Begin: draw triangle strip.
+ CSRAW public const int TRIANGLE_STRIP = 0x0005;
+
+ // Mode for ::ref::Begin: draw quads.
+ CSRAW public const int QUADS = 0x0007;
+
+ // Mode for ::ref::Begin: draw lines.
+ CSRAW public const int LINES = 0x0001;
+
+
+ // Begin drawing 3D primitives.
+ CUSTOM static void Begin (int mode) {
+ GfxPrimitiveType pt;
+ if( mode == 0x0004 )
+ pt = kPrimitiveTriangles;
+ else if( mode == 0x0005 )
+ pt = kPrimitiveTriangleStripDeprecated;
+ else if( mode == 0x0007 )
+ pt = kPrimitiveQuads;
+ else if( mode == 0x0001 )
+ pt = kPrimitiveLines;
+ else {
+ Scripting::RaiseMonoException( "Invalid mode for GL.Begin" );
+ return;
+ }
+ GetGfxDevice().ImmediateBegin( pt );
+ }
+
+ // End drawing 3D primitives.
+ CUSTOM static void End () {
+ GetGfxDevice().ImmediateEnd();
+ GPU_TIMESTAMP();
+ }
+
+ // Helper function to set up an ortho perspective transform.
+ CUSTOM static void LoadOrtho ()
+ {
+ LoadFullScreenOrthoMatrix();
+ }
+
+ // Setup a matrix for pixel-correct rendering.
+ CUSTOM static void LoadPixelMatrix () {
+ // Now the hack: regular code (GUITexts, GUITExtures etc.) needs to add the
+ // vertical texel offset. However, looks like GUI code needs to subtract the
+ // vertical texel offset. No idea why; I guess it's because GUI code does
+ // the y-inversion by itself. So we pass true as the last parameter.
+ if (GetCurrentCameraPtr() != NULL)
+ {
+ LoadPixelMatrix( GetCurrentCamera().GetScreenViewportRect(), GetGfxDevice(), true, true );
+ }
+ }
+
+ // Do this "different name" trick because otherwise the wrapper functions for both
+ // LoadPixelMatrix overloads will have the same name, making a compile error.
+ CUSTOM private static void LoadPixelMatrixArgs(float left, float right, float bottom, float top)
+ {
+ // Now the hack: regular code (GUITexts, GUITExtures etc.) needs to add the
+ // vertical texel offset. However, looks like GUI code needs to subtract the
+ // vertical texel offset. No idea why; I guess it's because GUI code does
+ // the y-inversion by itself. So we pass true as the last parameter.
+ Rectf rect( left, bottom, right-left, top-bottom );
+ LoadPixelMatrix( rect, GetGfxDevice(), true, true );
+ }
+
+
+ // Setup a matrix for pixel-correct rendering.
+ CSRAW public static void LoadPixelMatrix (float left, float right, float bottom, float top)
+ {
+ LoadPixelMatrixArgs(left,right,bottom,top);
+ }
+
+ // Set the rendering viewport.
+ CUSTOM static void Viewport (Rect pixelRect) {
+ SetGLViewport(pixelRect);
+ }
+
+ // Load an arbitrary matrix to the current projection matrix.
+ CUSTOM static void LoadProjectionMatrix (Matrix4x4 mat) {
+ GetGfxDevice().SetProjectionMatrix (mat);
+ }
+
+ // Load the identity matrix to the current modelview matrix.
+ CUSTOM static void LoadIdentity () {
+ GetGfxDevice().SetViewMatrix (Matrix4x4f::identity.GetPtr()); // implicitly sets world to identity
+ }
+
+ // The current modelview matrix.
+ CUSTOM_PROP static Matrix4x4 modelview
+ {
+ Matrix4x4f temp;
+ GetGfxDevice().GetMatrix( temp.GetPtr() );
+ return temp;
+ }
+ {
+ GetGfxDevice().SetViewMatrix( value.GetPtr() );
+ }
+
+ // Multiplies the current modelview matrix with the one specified.
+ CUSTOM static void MultMatrix (Matrix4x4 mat) {
+ GetGfxDevice().SetWorldMatrix( mat.GetPtr() );
+ }
+
+ // Saves both projection and modelview matrices to the matrix stack.
+ CUSTOM static void PushMatrix()
+ {
+ GfxDevice& dev = GetGfxDevice();
+ g_ViewMatrixStack.Push(dev.GetViewMatrix());
+ g_WorldMatrixStack.Push(dev.GetWorldMatrix());
+ g_ProjectionMatrixStack.Push(dev.GetProjectionMatrix());
+ }
+
+ // Restores both projection and modelview matrices off the top of the matrix stack.
+ CUSTOM static void PopMatrix ()
+ {
+ GfxDevice& dev = GetGfxDevice();
+ g_WorldMatrixStack.Pop();
+ g_ViewMatrixStack.Pop();
+ g_ProjectionMatrixStack.Pop();
+ // Setting the view matrix clears the world matrix to be compatible with GL the ways
+ dev.SetViewMatrix(g_ViewMatrixStack.GetMatrix().GetPtr());
+ dev.SetWorldMatrix(g_WorldMatrixStack.GetMatrix().GetPtr());
+ dev.SetProjectionMatrix(g_ProjectionMatrixStack.GetMatrix());
+ }
+
+
+ // Compute GPU projection matrix from camera's projection matrix.
+ CUSTOM static Matrix4x4 GetGPUProjectionMatrix (Matrix4x4 proj, bool renderIntoTexture)
+ {
+ bool openGLStyle = GetGfxDevice().UsesOpenGLTextureCoords();
+ Matrix4x4f m = proj;
+ CalculateDeviceProjectionMatrix (m, openGLStyle, !openGLStyle && renderIntoTexture);
+ return m;
+ }
+
+
+ // Should rendering be done in wireframe?
+ CUSTOM_PROP static bool wireframe { return GetGfxDevice().GetWireframe(); } { GetGfxDevice().SetWireframe(value); }
+
+ CUSTOM_PROP static bool sRGBWrite { return GetGfxDevice().GetSRGBWrite(); } { GetGfxDevice().SetSRGBWrite(value); }
+
+ // Select whether to invert the backface culling (true) or not (false).
+ CUSTOM static void SetRevertBackfacing (bool revertBackFaces) { GetGfxDevice().SetUserBackfaceMode (revertBackFaces); }
+
+ // Clear the current render buffer.
+ CSRAW static public void Clear (bool clearDepth, bool clearColor, Color backgroundColor, float depth=1.0f) {
+ Internal_Clear (clearDepth, clearColor, backgroundColor, depth);
+ }
+
+ CUSTOM static private void Internal_Clear (bool clearDepth, bool clearColor, Color backgroundColor, float depth) {
+ UInt32 flags = 0;
+ if (clearColor) flags |= kGfxClearColor;
+ if (clearDepth) flags |= kGfxClearDepthStencil;
+ GraphicsHelper::Clear (flags, backgroundColor.GetPtr(), depth, 0);
+ GPU_TIMESTAMP();
+ }
+
+
+ // Clear the current render buffer with camera's skybox.
+ CUSTOM static void ClearWithSkybox (bool clearDepth, Camera camera) {
+ ClearWithSkybox(clearDepth, camera);
+ }
+
+ // Invalidate the internally cached renderstates.
+ CUSTOM static void InvalidateState () {
+ GL_CUSTOM_PushMatrix();
+ GfxDevice& dev = GetGfxDevice();
+ dev.InvalidateState();
+ GL_CUSTOM_PopMatrix();
+ }
+
+ // Send a user-defined event to a native code plugin.
+ CUSTOM static void IssuePluginEvent (int eventID)
+ {
+ GetGfxDevice().InsertCustomMarker (eventID);
+ }
+
+END
+
+
+// GUI STUFF
+// --------------------------------
+
+// Base class for images & text strings displayed in a GUI.
+NONSEALED_CLASS GUIElement : Behaviour
+
+ // Is a point on screen inside the element.
+ CUSTOM bool HitTest (Vector3 screenPosition, Camera camera = null) {
+ return self->HitTest (Vector2f (screenPosition.x, screenPosition.y), GetCameraOrWindowRect(camera));
+ }
+
+ // Returns bounding rectangle of [[GUIElement]] in screen coordinates.
+ CUSTOM Rect GetScreenRect (Camera camera = null) { return self->GetScreenRect (GetCameraOrWindowRect(camera)); }
+
+END
+
+// A texture image used in a 2D GUI.
+CLASS GUITexture : GUIElement
+ // The color of the GUI texture.
+ AUTO_PROP Color color GetColor SetColor
+
+ // The texture used for drawing.
+
+ AUTO_PTR_PROP Texture texture GetTexture SetTexture
+
+ // Pixel inset used for pixel adjustments for size and position.
+ CUSTOM_PROP Rect pixelInset { return self->GetPixelInset(); } { self->SetPixelInset(value); }
+
+
+ // The border defines the number of pixels from the edge that are not affected by scale.
+ CUSTOM_PROP RectOffset border
+ {
+ RectOffset* rectOffset = new RectOffset ();
+
+ rectOffset->left = self->m_LeftBorder;
+ rectOffset->right = self->m_RightBorder;
+ rectOffset->top = self->m_TopBorder;
+ rectOffset->bottom = self->m_BottomBorder;
+
+ ScriptingObjectWithIntPtrField<RectOffset> monoObject(scripting_object_new (MONO_COMMON.rectOffset));
+ monoObject.SetPtr(rectOffset);
+ return monoObject.object;
+ }
+ {
+ self->m_LeftBorder = value->left;
+ self->m_RightBorder = value->right;
+ self->m_TopBorder = value->top;
+ self->m_BottomBorder = value->bottom;
+ }
+END
+
+
+// How multiline text should be aligned.
+ENUM TextAlignment
+ // Text lines are aligned on the left side.
+ Left = 0,
+ // Text lines are centered.
+ Center = 1,
+ // Text lines are aligned on the right side.
+ Right = 2
+END
+
+
+// Where the anchor of the text is placed.
+ENUM TextAnchor
+
+ // Text is anchored in upper left corner.
+ UpperLeft = 0,
+
+ // Text is anchored in upper side, centered horizontally.
+ UpperCenter = 1,
+
+ // Text is anchored in upper right corner.
+ UpperRight = 2,
+
+ // Text is anchored in left side, centered vertically.
+ MiddleLeft = 3,
+
+ // Text is centered both horizontally and vertically.
+ MiddleCenter = 4,
+
+ // Text is anchored in right side, centered vertically.
+ MiddleRight = 5,
+
+ // Text is anchored in lower left corner.
+ LowerLeft = 6,
+
+ // Text is anchored in lower side, centered horizontally.
+ LowerCenter = 7,
+
+ // Text is anchored in lower right corner.
+ LowerRight = 8
+
+END
+
+
+// A text string displayed in a GUI.
+CLASS GUIText : GUIElement
+
+ // The text to display.
+ CUSTOM_PROP string text { return scripting_string_new (self->GetText ()); } { self->SetText (value); }
+
+ // The [[Material]] to use for rendering.
+ CUSTOM_PROP Material material
+ {
+ Material* material = self->GetMaterial ();
+ if (material == NULL)
+ material = GetBuiltinResource<Material> ("Font.mat");
+
+ Material* instantiated = &Material::GetInstantiatedMaterial (material, *self, false);
+ if (material != instantiated)
+ self->SetMaterial (instantiated);
+
+ return Scripting::ScriptingWrapperFor (instantiated);
+ }
+ {
+ self->SetMaterial (value);
+ }
+
+ // Workaround for gcc/msvc where passing small mono structures by value does not work
+ CUSTOM private void Internal_GetPixelOffset (out Vector2 output)
+ {
+ *output = self->GetPixelOffset();
+ }
+ CUSTOM private void Internal_SetPixelOffset (Vector2 p) { self->SetPixelOffset(p); }
+
+ // The pixel offset of the text.
+ CSRAW public Vector2 pixelOffset { get { Vector2 p; Internal_GetPixelOffset(out p); return p; } set { Internal_SetPixelOffset(value); } }
+
+ // The font used for the text.
+ AUTO_PTR_PROP Font font GetFont SetFont
+
+ // The alignment of the text.
+ AUTO_PROP TextAlignment alignment GetAlignment SetAlignment
+
+ // The anchor of the text.
+ AUTO_PROP TextAnchor anchor GetAnchor SetAnchor
+
+ // The line spacing multiplier.
+ AUTO_PROP float lineSpacing GetLineSpacing SetLineSpacing
+
+ // The tab width multiplier.
+ AUTO_PROP float tabSize GetTabSize SetTabSize
+
+ // The font size to use (for dynamic fonts)
+ AUTO_PROP int fontSize GetFontSize SetFontSize
+
+ // The font style to use (for dynamic fonts)
+ AUTO_PROP FontStyle fontStyle GetFontStyle SetFontStyle
+
+ // Enable HTML-style tags for Text Formatting Markup.
+ AUTO_PROP bool richText GetRichText SetRichText
+
+ // Color used to render the text
+ AUTO_PROP Color color GetColor SetColor
+END
+// Info how to render a character from the font texture. See /Font.characterInfo/
+STRUCT CharacterInfo
+ CSRAW
+ // Unicode value of the character
+ public int index;
+ // UV coordinates for the character in the texture
+ public Rect uv;
+ // Screen coordinates for the character in generated text meshes
+ public Rect vert;
+ // How for to advance between the beginning of this charcater and the next.
+ public float width;
+ // The size of the character or 0 if it is the default font size.
+ public int size;
+ // The style of the character.
+ public FontStyle style;
+ // Is the character flipped?
+ public bool flipped;
+END
+
+// Script interface for [[wiki:class-Font|font assets]].
+CLASS Font : Object
+
+ CUSTOM private static void Internal_CreateFont ([Writable]Font _font, string name)
+ {
+ Font* font = NEW_OBJECT (Font);
+ SmartResetObject(*font);
+ font->SetNameCpp (name);
+ Scripting::ConnectScriptingWrapperToObject (_font.GetScriptingObject(), font);
+ }
+
+ // Creates a new font.
+ CSRAW public Font () { Internal_CreateFont (this,null); }
+
+ // Creates a new font named /name/.
+ CSRAW public Font (string name){ Internal_CreateFont (this,name); }
+
+ // The material used for the font display.
+ AUTO_PTR_PROP Material material GetMaterial SetMaterial
+
+ // Does this font have a specific character?
+ CUSTOM bool HasCharacter (char c) { return self->HasCharacter((int)c);}
+
+ // *undocumented*
+ CONDITIONAL ENABLE_MONO || UNITY_WINRT
+ CUSTOM_PROP string[] fontNames
+ {
+ ScriptingArrayPtr arr = CreateScriptingArray<ScriptingStringPtr>(MONO_COMMON.string, self->GetFontNames().size());
+ int idx = 0;
+ for (UNITY_VECTOR(kMemFont,UnityStr)::const_iterator i = self->GetFontNames().begin(); i != self->GetFontNames().end(); ++i)
+ {
+ Scripting::SetScriptingArrayElement<ScriptingStringPtr>(arr, idx, scripting_string_new(*i));
+ idx++;
+ }
+ return arr;
+ }
+ {
+ UNITY_VECTOR(kMemFont,UnityStr) names;
+ for (int i = 0; i < GetScriptingArraySize(value); ++i)
+ names.push_back(scripting_cpp_string_for(Scripting::GetScriptingArrayElementNoRef<ScriptingStringPtr>(value, i)));
+ self->SetFontNames(names);
+ }
+
+ // Access an array of all characters contained in the font texture.
+ CUSTOM_PROP public CharacterInfo[] characterInfo
+ {
+ const Font::CharacterInfos &infos = self->GetCharacterInfos();
+ int size = infos.size();
+ ScriptingArrayPtr array = CreateScriptingArray<ScriptingCharacterInfo> (GetScriptingManager().GetCommonClasses().characterInfo, size);
+ #if UNITY_WINRT
+ for (int i=0; i<size; i++)
+ {
+ ScriptingCharacterInfo temp; temp.CopyFrom(infos[i]);
+ ScriptingObjectPtr data;
+ MarshallNativeStructIntoManaged(temp, data);
+ Scripting::SetScriptingArrayElement(array, i, data);
+ }
+ #else
+ ScriptingCharacterInfo *sci = Scripting::GetScriptingArrayStart<ScriptingCharacterInfo>(array);
+ for (int i=0; i<size; i++)
+ {
+ sci[i].CopyFrom(infos[i]);
+ }
+ #endif
+ return array;
+ }
+ {
+ Font::CharacterInfos infos;
+ int size = GetScriptingArraySize(value);
+ infos.resize(size);
+
+ #if UNITY_WINRT
+ for (int i=0; i<size; i++)
+ {
+ ScriptingCharacterInfo temp;
+ MarshallManagedStructIntoNative(Scripting::GetScriptingArrayElementNoRef<ScriptingObjectPtr>(value, i), &temp);
+ temp.CopyTo(infos[i]);
+ }
+ #else
+ ScriptingCharacterInfo *sci = Scripting::GetScriptingArrayStart<ScriptingCharacterInfo>(value);
+ for (int i=0; i<size; i++)
+ {
+ sci[i].CopyTo(infos[i]);
+ }
+ #endif
+ self->SetCharacterInfos (infos);
+ }
+
+ // Request characters to be added to the font texture (dynamic fonts only).
+
+ CUSTOM void RequestCharactersInTexture (string characters, int size = 0, FontStyle style = FontStyle.Normal)
+ {
+ UTF16String str(characters.AsUTF8().c_str());
+ self->CacheFontForText (str.text, str.length, size, style);
+ }
+
+ // Callback delegate for use with ::ref::textureRebuildCallback.
+ CSRAW public delegate void FontTextureRebuildCallback();
+ CSRAW private event FontTextureRebuildCallback m_FontTextureRebuildCallback = null;
+
+ CSRAW private void InvokeFontTextureRebuildCallback_Internal()
+ {
+ if (m_FontTextureRebuildCallback != null)
+ m_FontTextureRebuildCallback();
+ }
+
+ // A delegate which gets called when the font texture is rebuilt (dynamic fonts only).
+ CSRAW public FontTextureRebuildCallback textureRebuildCallback { get {
+ return m_FontTextureRebuildCallback; }
+ set { m_FontTextureRebuildCallback = value; }
+ }
+
+ // Get rendering info for a specific character
+ CUSTOM bool GetCharacterInfo(char ch, out CharacterInfo info, int size = 0, FontStyle style = FontStyle.Normal)
+ {
+ if (self->HasCharacterInTexture (ch, size, style))
+ {
+ info->index = ch;
+ info->size = size;
+ info->style = style;
+ self->GetCharacterRenderInfo( ch, size, style, info->vert, info->uv, info->flipped );
+ info->width = self->GetCharacterWidth (ch, size, style);
+ return true;
+ }
+ return false;
+ }
+
+END
+
+
+// [[Component]] added to a camera to make it render 2D GUI elements.
+CLASS GUILayer : Behaviour
+ // Get the GUI element at a specific screen position.
+ CUSTOM GUIElement HitTest (Vector3 screenPosition) { return Scripting::ScriptingWrapperFor (self->HitTest (Vector2f (screenPosition.x, screenPosition.y))); }
+END
+
+// Renders meshes inserted by the [[MeshFilter]] or [[TextMesh]].
+CLASS MeshRenderer : Renderer
+END
+
+// StaticBatchingUtility can prepare your objects to take advantage of Unity's static batching.
+CONDITIONAL !UNITY_FLASH
+CLASS StaticBatchingUtility
+
+ // Combine will prepare all children of the @@staticBatchRoot@@ for static batching.
+ CSRAW static public void Combine (GameObject staticBatchRoot)
+ {
+ InternalStaticBatchingUtility.Combine(staticBatchRoot);
+ }
+
+ // Combine will prepare all @@gos@@ for the static batching. @@staticBatchRoot@@ will be treated as their parent.
+ CSRAW static public void Combine (GameObject[] gos, GameObject staticBatchRoot)
+ {
+ InternalStaticBatchingUtility.Combine(gos, staticBatchRoot);
+ }
+
+
+ C++RAW
+
+ struct InternalMonoMeshInstance
+ {
+ int meshInstanceID;
+ Matrix4x4f transform;
+ Vector4f lightmapTilingOffset;
+ };
+
+ CUSTOM static internal Mesh InternalCombineVertices (MeshSubsetCombineUtility.MeshInstance[] meshes, string meshName)
+ {
+ const int numMeshes = GetScriptingArraySize( meshes );
+
+ CombineInstances mi;
+
+ mi.resize ( numMeshes );
+ InternalMonoMeshInstance* instances = Scripting::GetScriptingArrayStart<InternalMonoMeshInstance>( meshes );
+ for ( int i=0; i!=numMeshes; ++i )
+ {
+ mi[i].mesh = PPtr<Mesh>(instances[i].meshInstanceID);
+ mi[i].transform = instances[i].transform;
+ mi[i].lightmapTilingOffset = instances[i].lightmapTilingOffset;
+ }
+
+ Mesh* outMesh = CreateObjectFromCode<Mesh> (kDefaultAwakeFromLoad);
+
+ CombineMeshVerticesForStaticBatching ( mi, meshName, *outMesh );
+
+ return Scripting::ScriptingWrapperFor( outMesh );
+ }
+
+ C++RAW
+
+ struct InternalMonoSubMeshInstance
+ {
+ int meshInstanceID; // public Mesh mesh;
+ int vertexOffset; // public int vertexOffset;
+ int goInstanceID; // public GameObject gameObject;
+ int subMeshIndex; // public int subMeshIndex;
+ Matrix4x4f transform;
+ };
+
+ CUSTOM static internal void InternalCombineIndices(MeshSubsetCombineUtility.SubMeshInstance[] submeshes, [Writable]Mesh combinedMesh)
+ {
+ InternalMonoSubMeshInstance* instances = Scripting::GetScriptingArrayStart<InternalMonoSubMeshInstance>( submeshes );
+ int numSubMeshes = GetScriptingArraySize( submeshes );
+ CombineInstances smi;
+ smi.resize( numSubMeshes );
+
+ for( int i=0; i!=numSubMeshes; ++i )
+ {
+ smi[i].mesh = PPtr<Mesh>(instances[i].meshInstanceID);
+ smi[i].vertexOffset = instances[i].vertexOffset;
+ smi[i].subMeshIndex = instances[i].subMeshIndex;
+ smi[i].transform = instances[i].transform;
+ }
+
+ Mesh* mesh = combinedMesh;
+ CombineMeshIndicesForStaticBatching (smi, *mesh, false, true);
+ mesh->SetIsReadable (false);
+ mesh->SetKeepIndices (true);
+ // Call awake to create the actual VBO; needed since we did SetIsReadable (false)
+ mesh->AwakeFromLoad (kDefaultAwakeFromLoad);
+ }
+END
+
+// When using HDR rendering it can sometime be desirable to switch to LDR rendering during ImageEffect rendering.
+CLASS ImageEffectTransformsToLDR : Attribute
+
+END
+
+// Any Image Effect with this attribute will be rendered after opaque geometry but before transparent geometry.
+CLASS ImageEffectOpaque : Attribute
+
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/GraphicsEnums.cs b/Runtime/Export/GraphicsEnums.cs
new file mode 100644
index 0000000..6752dfc
--- /dev/null
+++ b/Runtime/Export/GraphicsEnums.cs
@@ -0,0 +1,185 @@
+using System;
+
+namespace UnityEngine
+{
+ public enum FilterMode
+ {
+ Point = 0,
+ Bilinear = 1,
+ Trilinear = 2,
+ }
+
+ public enum TextureWrapMode
+ {
+ Repeat = 0,
+ Clamp = 1,
+ }
+
+ public enum NPOTSupport
+ {
+ None = 0,
+ Restricted = 1,
+ Full = 2
+ }
+
+ public enum TextureFormat
+ {
+ Alpha8 = 1,
+ ARGB4444 = 2,
+ RGB24 = 3,
+ RGBA32 = 4,
+ ARGB32 = 5,
+ RGB565 = 7,
+ DXT1 = 10,
+ DXT5 = 12,
+ RGBA4444 = 13,
+
+ [System.Obsolete ("Use PVRTC_RGB2")]
+ PVRTC_2BPP_RGB = 30,
+ [System.Obsolete ("Use PVRTC_RGBA2")]
+ PVRTC_2BPP_RGBA = 31,
+ [System.Obsolete ("Use PVRTC_RGB4")]
+ PVRTC_4BPP_RGB = 32,
+ [System.Obsolete ("Use PVRTC_RGBA4")]
+ PVRTC_4BPP_RGBA = 33,
+
+ PVRTC_RGB2 = 30,
+ PVRTC_RGBA2 = 31,
+ PVRTC_RGB4 = 32,
+ PVRTC_RGBA4 = 33,
+ ETC_RGB4 = 34,
+ ATC_RGB4 = 35,
+ ATC_RGBA8 = 36,
+ BGRA32 = 37,
+ ATF_RGB_DXT1 = 38,
+ ATF_RGBA_JPG = 39,
+ ATF_RGB_JPG = 40
+ }
+
+ public enum CubemapFace
+ {
+ PositiveX = 0,
+ NegativeX = 1,
+ PositiveY = 2,
+ NegativeY = 3,
+ PositiveZ = 4,
+ NegativeZ = 5
+ }
+
+ public enum RenderTextureFormat
+ {
+ ARGB32 = 0,
+ Depth = 1,
+ ARGBHalf = 2,
+ RGB565 = 4,
+ ARGB4444 = 5,
+ ARGB1555 = 6,
+ Default = 7,
+ DefaultHDR = 9,
+ ARGBFloat = 11,
+ RGFloat = 12,
+ RGHalf = 13,
+ RFloat = 14,
+ RHalf = 15,
+ R8 = 16,
+ ARGBInt = 17,
+ RGInt = 18,
+ RInt = 19
+ }
+
+ public enum RenderTextureReadWrite
+ {
+ Default = 0,
+ Linear = 1,
+ sRGB = 2
+ }
+
+} // namespace UnityEngine
+
+
+namespace UnityEngine.Rendering
+{
+ public enum BlendMode
+ {
+ Zero = 0,
+ One = 1,
+ DstColor = 2,
+ SrcColor = 3,
+ OneMinusDstColor = 4,
+ SrcAlpha = 5,
+ OneMinusSrcColor = 6,
+ DstAlpha = 7,
+ OneMinusDstAlpha = 8,
+ SrcAlphaSaturate = 9,
+ OneMinusSrcAlpha = 10
+ }
+
+ public enum BlendOp
+ {
+ Add = 0,
+ Subtract = 1,
+ ReverseSubtract = 2,
+ Min = 3,
+ Max = 4,
+ LogicalClear = 5,
+ LogicalSet = 6,
+ LogicalCopy = 7,
+ LogicalCopyInverted = 8,
+ LogicalNoop = 9,
+ LogicalInvert = 10,
+ LogicalAnd = 11,
+ LogicalNand = 12,
+ LogicalOr = 13,
+ LogicalNor = 14,
+ LogicalXor = 15,
+ LogicalEquivalence = 16,
+ LogicalAndReverse = 17,
+ LogicalAndInverted = 18,
+ LogicalOrReverse = 19,
+ LogicalOrInverted = 20,
+ }
+
+ public enum CompareFunction
+ {
+ Disabled = 0,
+ Never = 1,
+ Less = 2,
+ Equal = 3,
+ LessEqual = 4,
+ Greater = 5,
+ NotEqual = 6,
+ GreaterEqual = 7,
+ Always = 8
+ }
+
+ public enum CullMode
+ {
+ Off = 0,
+ Front = 1,
+ Back = 2
+ }
+
+ [Flags]
+ public enum ColorWriteMask
+ {
+ Alpha = 1,
+ Blue = 2,
+ Green = 4,
+ Red = 8,
+ All = 15
+ }
+
+ public enum StencilOp
+ {
+ Keep = 0,
+ Zero = 1,
+ Replace = 2,
+ IncrementSaturate = 3,
+ DecrementSaturate = 4,
+ Invert = 5,
+ IncrementWrap = 6,
+ DecrementWrap = 7
+ }
+
+
+} // namespace UnityEngine.Rendering
diff --git a/Runtime/Export/Handheld.txt b/Runtime/Export/Handheld.txt
new file mode 100644
index 0000000..b1268a8
--- /dev/null
+++ b/Runtime/Export/Handheld.txt
@@ -0,0 +1,424 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Video/MoviePlayback.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Input/GetInput.h"
+#include "Runtime/Input/OnScreenKeyboard.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Misc/PlayerSettings.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Shaders/Material.h"
+#include "PlatformDependent/iPhonePlayer/iPhoneSettings.h"
+#include "PlatformDependent/iPhonePlayer/iPhoneMisc.h"
+#include "PlatformDependent/AndroidPlayer/AndroidSystemInfo.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+
+
+#if UNITY_ANDROID
+#include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+#elif UNITY_IPHONE
+ void StartActivityIndicator();
+ void StopActivityIndicator();
+ const char* GetVendorIdentifier();
+ const char* GetAdvertisingIdentifier();
+ bool IsAdvertisingTrackingEnabled();
+#endif
+
+using namespace Unity;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+// Simple struct that contains all the arguments needed by
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API || UNITY_BB10_API || UNITY_WP8 || UNITY_TIZEN_API
+STRUCT internal TouchScreenKeyboard_InternalConstructorHelperArguments
+ CSRAW public uint keyboardType;
+ CSRAW public uint autocorrection;
+ CSRAW public uint multiline;
+ CSRAW public uint secure;
+ CSRAW public uint alert;
+END
+
+C++RAW
+struct MonoTouchScreenKeyboard_InternalConstructorHelperArguments {
+ unsigned int keyboardType;
+ unsigned int autocorrection;
+ unsigned int multiline;
+ unsigned int secure;
+ unsigned int alert;
+};
+
+// Describes options for displaying movie playback controls.
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API || UNITY_BB10_API || UNITY_WP8 || UNITY_TIZEN_API
+ENUM FullScreenMovieControlMode
+ // Display the standard controls for controlling movie playback. This
+ Full = 0,
+ // Display minimal set of controls controlling movie playback. Set of
+ Minimal = 1,
+ // Do not display any controls, but cancel movie playback if input occurs.
+ CancelOnInput = 2,
+ // Do not display any controls. This mode prevents the user from
+ Hidden = 3,
+END
+
+// Describes scaling modes for displaying movies.
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API || UNITY_BB10_API || UNITY_WP8 || UNITY_TIZEN_API
+ENUM FullScreenMovieScalingMode
+ // Do not scale the movie.
+ None = 0,
+ // Scale the movie until one dimension fits on the screen exactly. In
+ AspectFit = 1,
+ // Scale the movie until the movie fills the entire screen. Content at
+ AspectFill = 2,
+ // Scale the movie until both dimensions fit the screen exactly. The
+ Fill = 3
+END
+
+CONDITIONAL UNITY_IPHONE_API
+/// iOS-specific ActivityIndicator styles.
+ENUM iOSActivityIndicatorStyle
+ /// Do not show ActivityIndicator
+ DontShow = -1,
+ /// The large white style of indicator (UIActivityIndicatorViewStyleWhiteLarge).
+ WhiteLarge = 0,
+ /// The standard white style of indicator (UIActivityIndicatorViewStyleWhite).
+ White = 1,
+ /// The standard gray style of indicator (UIActivityIndicatorViewStyleGray).
+ Gray = 2,
+END
+
+CONDITIONAL UNITY_ANDROID_API
+ENUM AndroidActivityIndicatorStyle
+ /// Do not show ActivityIndicator
+ DontShow = -1,
+ /// Large (android.R.attr.progressBarStyleLarge).
+ Large = 0,
+ /// Large Inversed (android.R.attr.progressBarStyleLargeInverse).
+ InversedLarge = 1,
+ /// Small (android.R.attr.progressBarStyleSmall).
+ Small = 2,
+ /// Small Inversed (android.R.attr.progressBarStyleSmallInverse).
+ InversedSmall = 3,
+END
+
+
+// Interface into functionality unique to handheld devices.
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API || UNITY_BB10_API || UNITY_WP8 || UNITY_TIZEN_API
+CLASS Handheld
+ // Plays a full-screen movie.
+ CUSTOM static bool PlayFullScreenMovie (string path, Color bgColor = Color.black, FullScreenMovieControlMode controlMode = FullScreenMovieControlMode.Full, FullScreenMovieScalingMode scalingMode = FullScreenMovieScalingMode.AspectFit)
+ {
+ return PlayFullScreenMovie (path, bgColor, controlMode, scalingMode);
+ }
+
+ // Triggers device vibration.
+ CUSTOM static void Vibrate ()
+ {
+ Vibrate ();
+ }
+
+ CUSTOM_PROP static bool use32BitDisplayBuffer
+ {
+ return GetPlayerSettings().GetUse32BitDisplayBuffer();
+ }
+ {
+ GetPlayerSettings().SetUse32BitDisplayBuffer(value);
+ }
+
+ CUSTOM internal static void SetActivityIndicatorStyleImpl(int style)
+ {
+ #if UNITY_IPHONE
+ GetPlayerSettings().SetIOSShowActivityIndicatorOnLoading(style);
+ #elif UNITY_ANDROID
+ GetPlayerSettings().SetAndroidShowActivityIndicatorOnLoading(style);
+ #endif
+ }
+
+ CONDITIONAL UNITY_IPHONE_API
+ /// Sets ActivityIndicator style. See iOSActivityIndicatorStyle enumeration for possible values.
+ /// Be warned that it will take effect on next call to StartActivityIndicator.
+ CSRAW public static void SetActivityIndicatorStyle(iOSActivityIndicatorStyle style)
+ {
+ SetActivityIndicatorStyleImpl((int)style);
+ }
+
+ CONDITIONAL UNITY_ANDROID_API
+ /// Sets ActivityIndicator style. See AndroidActivityIndicatorStyle enumeration for possible values.
+ /// Be warned that it will take effect on next call to StartActivityIndicator.
+ CSRAW public static void SetActivityIndicatorStyle(AndroidActivityIndicatorStyle style)
+ {
+ SetActivityIndicatorStyleImpl((int)style);
+ }
+
+ /// Gets current ActivityIndicator style.
+ CUSTOM static int GetActivityIndicatorStyle()
+ {
+ #if UNITY_IPHONE
+ return GetPlayerSettings().GetIOSShowActivityIndicatorOnLoading();
+ #elif UNITY_ANDROID
+ return GetPlayerSettings().GetAndroidShowActivityIndicatorOnLoading();
+ #else
+ return -1;
+ #endif
+ }
+
+ // Starts os activity indicator
+ CUSTOM static void StartActivityIndicator()
+ {
+ #if UNITY_IPHONE || UNITY_ANDROID
+ StartActivityIndicator();
+ #endif
+ }
+
+ // Stops os activity indicator
+ CUSTOM static void StopActivityIndicator()
+ {
+ #if UNITY_IPHONE || UNITY_ANDROID
+ StopActivityIndicator();
+ #endif
+ }
+
+ //*undocumented*
+ CUSTOM static void ClearShaderCache()
+ {
+ #if UNITY_ANDROID
+ extern void Internal_ClearShaderCache();
+ Internal_ClearShaderCache();
+ #endif
+ }
+END
+
+// Describes the type of keyboard.
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API || UNITY_BB10_API || UNITY_WP8 || UNITY_TIZEN_API
+ENUM TouchScreenKeyboardType
+ // Default keyboard for the current input method.
+ Default = 0,
+ // Keyboard displays standard ASCII characters.
+ ASCIICapable = 1,
+ // Keyboard with numbers and punctuation.
+ NumbersAndPunctuation = 2,
+ // Keyboard optimized for URL entry, features ".", "/", and ".com"
+ URL = 3,
+ // Numeric keypad designed for PIN entry, features the numbers 0 through 9
+ NumberPad = 4,
+ // Keypad designed for entering telephone numbers, features the numbers 0
+ PhonePad = 5,
+ // Keypad designed for entering a person's name or phone number.
+ NamePhonePad = 6,
+ // Keyboard optimized for specifying email addresses, features the "@",
+ EmailAddress = 7
+END
+
+// Interface into the native iPhone and Android on-screen keyboards - it is not available on other platforms.
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API || UNITY_BB10_API || UNITY_WP8 || UNITY_TIZEN_API
+CLASS TouchScreenKeyboard
+ // We are matching the KeyboardOnScreen class here so we can directly
+ // access it.
+ CSRAW
+ [System.NonSerialized] [NotRenamed]
+ internal IntPtr m_Ptr;
+
+ THREAD_SAFE
+ CUSTOM private void Destroy()
+ {
+ KeyboardOnScreen* keyboard = self.GetPtr();
+ if (keyboard)
+ {
+ delete keyboard;
+ self.SetPtr(NULL);
+ }
+ }
+
+ //*undocumented*
+ CSRAW ~TouchScreenKeyboard()
+ {
+ Destroy();
+ }
+
+ //*undocumented*
+ CSRAW public TouchScreenKeyboard(string text, TouchScreenKeyboardType keyboardType, bool autocorrection, bool multiline, bool secure, bool alert, string textPlaceholder)
+ {
+ TouchScreenKeyboard_InternalConstructorHelperArguments arguments = new TouchScreenKeyboard_InternalConstructorHelperArguments ();
+ arguments.keyboardType = Convert.ToUInt32(keyboardType);
+ arguments.autocorrection = Convert.ToUInt32(autocorrection);
+ arguments.multiline = Convert.ToUInt32(multiline);
+ arguments.secure = Convert.ToUInt32(secure);
+ arguments.alert = Convert.ToUInt32(alert);
+ TouchScreenKeyboard_InternalConstructorHelper (ref arguments, text, textPlaceholder);
+ }
+
+ CUSTOM private void TouchScreenKeyboard_InternalConstructorHelper (ref TouchScreenKeyboard_InternalConstructorHelperArguments arguments, string text, string textPlaceholder)
+ {
+ KeyboardOnScreen* keyboard = new KeyboardOnScreen(text, arguments.keyboardType, arguments.autocorrection, arguments.multiline, arguments.secure, arguments.alert, textPlaceholder);
+ self.SetPtr(keyboard);
+ }
+
+ // Opens the native keyboard provided by OS on the screen.
+ CSRAW public static TouchScreenKeyboard Open(string text, TouchScreenKeyboardType keyboardType = TouchScreenKeyboardType.Default, bool autocorrection = true, bool multiline = false, bool secure = false, bool alert = false, string textPlaceholder = "")
+ {
+ return new TouchScreenKeyboard(text, keyboardType, autocorrection, multiline, secure, alert, textPlaceholder);
+ }
+
+ // Returns the text displayed by the input field of the keyboard. This
+ CUSTOM_PROP public string text {
+ KeyboardOnScreen* keyboard = self.GetPtr();
+ if (keyboard) return scripting_string_new(keyboard->getText());
+ else return scripting_string_new("");
+ }
+ {
+ KeyboardOnScreen* keyboard = self.GetPtr();
+ if (keyboard) keyboard->setText(value);
+ }
+
+ // Specifies if text input field above the keyboard will be hidden when
+ CUSTOM_PROP public static bool hideInput {
+ return KeyboardOnScreen::isInputHidden();
+ } {
+ KeyboardOnScreen::setInputHidden(value);
+ }
+
+ // Specifies if the keyboard is visible or is sliding into the position on
+ CUSTOM_PROP public bool active {
+ KeyboardOnScreen* keyboard = self.GetPtr();
+ if (keyboard) return (short)keyboard->isActive();
+ else return false;
+ } {
+ KeyboardOnScreen* keyboard = self.GetPtr();
+ if (keyboard) keyboard->setActive(value);
+ }
+
+ // Specifies if input process was finished (RO)
+ CUSTOM_PROP public bool done {
+ KeyboardOnScreen* keyboard = self.GetPtr();
+ if (keyboard) return (short)keyboard->isDone();
+ else return false;
+ }
+
+ // Specifies if input process was canceled (RO)
+ CUSTOM_PROP public bool wasCanceled {
+ KeyboardOnScreen* keyboard = self.GetPtr();
+ if (keyboard) return (short)keyboard->wasCanceled();
+ else return false;
+ }
+
+ // Returns portion of the screen which is covered by the keyboard. Returns
+ CUSTOM_PROP static Rect area { return KeyboardOnScreen::GetRect(); }
+
+ // Returns true whenever any keyboard is completely visible on the screen.
+ CUSTOM_PROP static bool visible { return KeyboardOnScreen::IsVisible(); }
+END
+
+// iPhone device generation
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API
+ENUM iPhoneGeneration
+ //*undocumented*
+ Unknown = 0,
+ // First generation device
+ iPhone = 1,
+ // Second generation
+ iPhone3G = 2,
+ // Third generation
+ iPhone3GS = 3,
+ // iPod Touch, first generation
+ iPodTouch1Gen = 4,
+ // iPod Touch, second generation
+ iPodTouch2Gen = 5,
+ // iPod Touch, third generation
+ iPodTouch3Gen = 6,
+ // iPad, first generation
+ iPad1Gen = 7,
+ // Fourth generation
+ iPhone4 = 8,
+ // iPod Touch, fourth generation
+ iPodTouch4Gen = 9,
+ // iPad, second generation
+ iPad2Gen = 10,
+ // Fifth generation
+ iPhone4S = 11,
+ // iPad, third generation
+ iPad3Gen = 12,
+ // iPhone5
+ iPhone5 = 13,
+ // iPod Touch, fifth generation
+ iPodTouch5Gen = 14,
+ // iPadMini, first generation
+ iPadMini1Gen = 15,
+ // iPad, fourth generation
+ iPad4Gen = 16,
+ // iPhone5C
+ iPhone5C = 17,
+ // iPhone5S
+ iPhone5S = 18,
+
+ // Yet unknown iPhone
+ iPhoneUnknown = 10001,
+ // Yet unknown iPad
+ iPadUnknown = 10002,
+ // Yet unknown iPodTouch
+ iPodTouchUnknown = 10003,
+END
+
+// Interface into iPhone specific functionality.
+CONDITIONAL UNITY_IPHONE_API
+CLASS iPhone
+ // The generation of the device (RO)
+ CUSTOM_PROP static iPhoneGeneration generation {
+ return iphone::GetDeviceGeneration ();
+ }
+
+ // Set file flag to be excluded from iCloud/iTunes backup.
+ CUSTOM static void SetNoBackupFlag(string path)
+ {
+ #if UNITY_IPHONE
+ iphone::SetNoBackupFlag(path.AsUTF8().c_str());
+ #else
+ (void)path;
+ #endif
+ }
+
+ // Reset "no backup" file flag: file will be synced with iCloud/iTunes backup and can be deleted by OS in low storage situations.
+ CUSTOM static void ResetNoBackupFlag(string path)
+ {
+ #if UNITY_IPHONE
+ iphone::ResetNoBackupFlag(path.AsUTF8().c_str());
+ #else
+ (void)path;
+ #endif
+ }
+
+ CUSTOM_PROP static string vendorIdentifier
+ {
+ #if UNITY_IPHONE
+ return scripting_string_new(GetVendorIdentifier());
+ #else
+ return 0;
+ #endif
+ }
+ CUSTOM_PROP static string advertisingIdentifier
+ {
+ #if UNITY_IPHONE
+ return scripting_string_new(GetAdvertisingIdentifier());
+ #else
+ return 0;
+ #endif
+ }
+ CUSTOM_PROP static bool advertisingTrackingEnabled
+ {
+ #if UNITY_IPHONE
+ return IsAdvertisingTrackingEnabled();
+ #else
+ return false;
+ #endif
+ }
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/ImplementedInActionScript.cs b/Runtime/Export/ImplementedInActionScript.cs
new file mode 100644
index 0000000..afd0e41
--- /dev/null
+++ b/Runtime/Export/ImplementedInActionScript.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace UnityEngine
+{
+ [AttributeUsage(AttributeTargets.Method)]
+ public class ImplementedInActionScriptAttribute : System.Attribute
+ {
+// string actionScriptMethod;
+
+ public ImplementedInActionScriptAttribute()
+ {
+ }
+/*
+ public ImplementedInActionScriptAttribute(string actionScriptMethod)
+ {
+ this.actionScriptMethod = actionScriptMethod;
+ }
+*/
+ }
+}
diff --git a/Runtime/Export/Internal/DefaultValueAttribute.cs b/Runtime/Export/Internal/DefaultValueAttribute.cs
new file mode 100644
index 0000000..a664aa4
--- /dev/null
+++ b/Runtime/Export/Internal/DefaultValueAttribute.cs
@@ -0,0 +1,43 @@
+using System;
+
+namespace UnityEngine.Internal
+{
+ /// <summary>
+ /// Adds default value information for optional parameters
+ /// </summary>
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.GenericParameter)]
+ public class DefaultValueAttribute : Attribute
+ {
+ private object DefaultValue;
+
+ public DefaultValueAttribute (string value)
+ {
+ DefaultValue = value;
+ }
+
+ public object Value {
+ get { return DefaultValue; }
+ }
+
+ public override bool Equals (object obj)
+ {
+ DefaultValueAttribute dva = (obj as DefaultValueAttribute);
+ if (dva == null)
+ return false;
+
+ if (DefaultValue == null)
+ return (dva.Value == null);
+
+ return DefaultValue.Equals (dva.Value);
+ }
+
+ public override int GetHashCode()
+ {
+ if (DefaultValue == null)
+ return base.GetHashCode ();
+ return DefaultValue.GetHashCode();
+ }
+
+ }
+}
diff --git a/Runtime/Export/Internal/ExcludeFromDocs.cs b/Runtime/Export/Internal/ExcludeFromDocs.cs
new file mode 100644
index 0000000..42cfdbb
--- /dev/null
+++ b/Runtime/Export/Internal/ExcludeFromDocs.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace UnityEngine.Internal
+{
+ /// <summary>
+ /// Adds default value information for optional parameters
+ /// </summary>
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Method)]
+ public class ExcludeFromDocsAttribute : Attribute
+ {
+ public ExcludeFromDocsAttribute()
+ {
+ }
+ }
+}
diff --git a/Runtime/Export/JPEGMemsrc.c b/Runtime/Export/JPEGMemsrc.c
new file mode 100644
index 0000000..fcab486
--- /dev/null
+++ b/Runtime/Export/JPEGMemsrc.c
@@ -0,0 +1,174 @@
+/*
+ * ... this file ought really to have been a part of jpeglib to begin with.
+ *
+ * memsrc.c
+ *
+ * Copyright (C) 1994-1996, Thomas G. Lane.
+ * This file is part of the Independent JPEG Group's software.
+ * For conditions of distribution and use, see the accompanying README file.
+ *
+ * This file contains decompression data source routines for the case of
+ * reading JPEG data from a memory buffer that is preloaded with the entire
+ * JPEG file. This would not seem especially useful at first sight, but
+ * a number of people have asked for it.
+ * This is really just a stripped-down version of jdatasrc.c. Comparison
+ * of this code with jdatasrc.c may be helpful in seeing how to make
+ * custom source managers for other purposes.
+ */
+
+/* this is not a core library module, so it doesn't define JPEG_INTERNALS */
+#include "External/ProphecySDK/src/extlib/jpglib/jinclude.h"
+#include "External/ProphecySDK/src/extlib/jpglib/jpeglib.h"
+#include "External/ProphecySDK/src/extlib/jpglib/jerror.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Expanded data source object for memory input */
+
+typedef struct {
+ struct jpeg_source_mgr pub; /* public fields */
+
+ JOCTET eoi_buffer[2]; /* a place to put a dummy EOI */
+} my_source_mgr;
+
+typedef my_source_mgr * my_src_ptr;
+
+
+/*
+ * Initialize source --- called by jpeg_read_header
+ * before any data is actually read.
+ */
+
+METHODDEF(void)
+init_source (j_decompress_ptr cinfo)
+{
+ /* No work, since jpeg_memory_src set up the buffer pointer and count.
+ * Indeed, if we want to read multiple JPEG images from one buffer,
+ * this *must* not do anything to the pointer.
+ */
+}
+
+
+/*
+ * Fill the input buffer --- called whenever buffer is emptied.
+ *
+ * In this application, this routine should never be called; if it is called,
+ * the decompressor has overrun the end of the input buffer, implying we
+ * supplied an incomplete or corrupt JPEG datastream. A simple error exit
+ * might be the most appropriate response.
+ *
+ * But what we choose to do in this code is to supply dummy EOI markers
+ * in order to force the decompressor to finish processing and supply
+ * some sort of output image, no matter how corrupted.
+ */
+
+METHODDEF(int)
+fill_input_buffer (j_decompress_ptr cinfo)
+{
+ my_src_ptr src = (my_src_ptr) cinfo->src;
+
+ WARNMS(cinfo, JWRN_JPEG_EOF);
+
+ /* Create a fake EOI marker */
+ src->eoi_buffer[0] = (JOCTET) 0xFF;
+ src->eoi_buffer[1] = (JOCTET) JPEG_EOI;
+ src->pub.next_input_byte = src->eoi_buffer;
+ src->pub.bytes_in_buffer = 2;
+
+ return TRUE;
+}
+
+
+/*
+ * Skip data --- used to skip over a potentially large amount of
+ * uninteresting data (such as an APPn marker).
+ *
+ * If we overrun the end of the buffer, we let fill_input_buffer deal with
+ * it. An extremely large skip could cause some time-wasting here, but
+ * it really isn't supposed to happen ... and the decompressor will never
+ * skip more than 64K anyway.
+ */
+
+METHODDEF(void)
+skip_input_data (j_decompress_ptr cinfo, long num_bytes)
+{
+ my_src_ptr src = (my_src_ptr) cinfo->src;
+
+ if (num_bytes > 0) {
+ while (num_bytes > (long) src->pub.bytes_in_buffer) {
+ num_bytes -= (long) src->pub.bytes_in_buffer;
+ (void) fill_input_buffer(cinfo);
+ /* note we assume that fill_input_buffer will never return FALSE,
+ * so suspension need not be handled.
+ */
+ }
+ src->pub.next_input_byte += (size_t) num_bytes;
+ src->pub.bytes_in_buffer -= (size_t) num_bytes;
+ }
+}
+
+
+/*
+ * An additional method that can be provided by data source modules is the
+ * resync_to_restart method for error recovery in the presence of RST markers.
+ * For the moment, this source module just uses the default resync method
+ * provided by the JPEG library. That method assumes that no backtracking
+ * is possible.
+ */
+
+
+/*
+ * Terminate source --- called by jpeg_finish_decompress
+ * after all data has been read. Often a no-op.
+ *
+ * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
+ * application must deal with any cleanup that should happen even
+ * for error exit.
+ */
+
+METHODDEF(void)
+term_source (j_decompress_ptr cinfo)
+{
+ /* no work necessary here */
+}
+
+
+/*
+ * Prepare for input from a memory buffer.
+ */
+GLOBAL(void)
+jpeg_memory_src (j_decompress_ptr cinfo, JOCTET * buffer, size_t bufsize);
+
+GLOBAL(void)
+jpeg_memory_src (j_decompress_ptr cinfo, JOCTET * buffer, size_t bufsize)
+{
+ my_src_ptr src;
+
+ /* The source object is made permanent so that a series of JPEG images
+ * can be read from a single buffer by calling jpeg_memory_src
+ * only before the first one.
+ * This makes it unsafe to use this manager and a different source
+ * manager serially with the same JPEG object. Caveat programmer.
+ */
+ if (cinfo->src == NULL) { /* first time for this JPEG object? */
+ cinfo->src = (struct jpeg_source_mgr *)
+ (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+ SIZEOF(my_source_mgr));
+ }
+
+ src = (my_src_ptr) cinfo->src;
+ src->pub.init_source = init_source;
+ src->pub.fill_input_buffer = fill_input_buffer;
+ src->pub.skip_input_data = skip_input_data;
+ src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
+ src->pub.term_source = term_source;
+
+ src->pub.next_input_byte = buffer;
+ src->pub.bytes_in_buffer = bufsize;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Runtime/Export/JPEGMemsrc.h b/Runtime/Export/JPEGMemsrc.h
new file mode 100644
index 0000000..3c847ac
--- /dev/null
+++ b/Runtime/Export/JPEGMemsrc.h
@@ -0,0 +1,16 @@
+#ifndef JPEGMEMSRC_H
+#define JPEGMEMSRC_H
+
+#include "External/ProphecySDK/src/extlib/jpglib/jinclude.h"
+#include "External/ProphecySDK/src/extlib/jpglib/jpeglib.h"
+#include "External/ProphecySDK/src/extlib/jpglib/jerror.h"
+
+extern "C"
+{
+
+GLOBAL(void)
+jpeg_memory_src (j_decompress_ptr cinfo, JOCTET * buffer, size_t bufsize);
+
+}
+
+#endif
diff --git a/Runtime/Export/LODBindings.txt b/Runtime/Export/LODBindings.txt
new file mode 100644
index 0000000..d6053a3
--- /dev/null
+++ b/Runtime/Export/LODBindings.txt
@@ -0,0 +1,117 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Camera/LODGroup.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Utilities/LODUtility.h"
+#include "Runtime/Camera/LODGroupManager.h"
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+// Structure for building a LOD for passing to the SetLODs function
+CONDITIONAL !UNITY_FLASH
+STRUCT LOD
+ // Construct a LOD
+ CSRAW public LOD (float screenRelativeTransitionHeight, Renderer[] renderers)
+ {
+ this.screenRelativeTransitionHeight = screenRelativeTransitionHeight;
+ this.renderers = renderers;
+ }
+
+ // The screen relative height to use for the transition [0-1]
+ CSRAW public float screenRelativeTransitionHeight;
+ // List of renderers for this LOD level
+ CSRAW public Renderer[] renderers;
+END
+
+// LODGroup lets you group multiple Renderers into LOD levels.
+CLASS LODGroup : Component
+
+ // The local reference point against which the LOD distance is calculated.
+ AUTO_PROP Vector3 localReferencePoint GetLocalReferencePoint SetLocalReferencePoint
+
+ // The size of LOD object in local space
+ AUTO_PROP float size GetSize SetSize
+
+ // The number of LOD levels
+ CUSTOM_PROP int lodCount { return self->GetLODCount(); }
+
+ // Enable / Disable the LODGroup - Disabling will turn off all renderers.
+ CUSTOM_PROP bool enabled { return self->GetEnabled (); } { self->SetEnabled (value); }
+
+ // Recalculate the bounding region for the LODGroup (Relatively slow, do not call often)
+ CUSTOM void RecalculateBounds ()
+ {
+ CalculateLODGroupBoundingBox (*self);
+ }
+
+ // Set the LODs for the LOD group. This will remove any existing LODs configured on the LODGroup
+ CONDITIONAL !UNITY_FLASH
+ CUSTOM void SetLODS (LOD[] scriptingLODs)
+ {
+ //Number of LOD's
+ int size = GetScriptingArraySize (scriptingLODs);
+
+ if (size > kMaximumLODLevels)
+ {
+ WarningString(Format("SetLODs: Attempting to set more then the maximum number of LODS (%i) clamping", kMaximumLODLevels));
+ size = kMaximumLODLevels;
+ }
+
+ LODGroup::LODArray lods;
+ lods.resize(size);
+
+ for (int i = 0; i < size; i++)
+ {
+ #if UNITY_WINRT
+ // LOD is a structure with managed type inside that's why we need to perform slow Marshalling here.
+ MonoLOD monoLOD;
+ MarshallManagedStructIntoNative(Scripting::GetScriptingArrayElementNoRef<ScriptingObjectPtr>(scriptingLODs, i), &monoLOD);
+ #else
+ MonoLOD& monoLOD = Scripting::GetScriptingArrayElement<MonoLOD> (scriptingLODs, i);
+ #endif
+
+ lods[i].screenRelativeHeight = monoLOD.screenRelativeTransitionHeight;
+
+ // clamp to 0 - 1
+ lods[i].screenRelativeHeight = clamp01 (lods[i].screenRelativeHeight);
+
+ // if our values are broken...
+ if (i > 0 && (lods[i].screenRelativeHeight >= lods[i-1].screenRelativeHeight))
+ {
+ ErrorString("SetLODs: Attempting to set LOD where the screen relative size is greater then or equal to a higher detail LOD level.");
+ return;
+ }
+
+ int renderersSize = GetScriptingArraySize (monoLOD.renderers);
+ lods[i].renderers.resize_initialized(renderersSize);
+ for (int r = 0; r < renderersSize; r++)
+ {
+ ScriptingObjectPtr o = Scripting::GetScriptingArrayElementNoRef<ScriptingObjectPtr>(monoLOD.renderers,r);
+ lods[i].renderers[r].renderer = ScriptingObjectToObject<Renderer> (o);;
+ }
+ }
+
+ self->SetLODArray (lods);
+ CalculateLODGroupBoundingBox (*self);
+ }
+
+ // Force a LOD level on this LOD group
+ //
+ // @param index The LOD level to use. Passing index < 0 will return to standard LOD processing
+ CUSTOM void ForceLOD (int index)
+ {
+ ForceLODLevel (*self, index);
+ }
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/LightProbeBindings.txt b/Runtime/Export/LightProbeBindings.txt
new file mode 100644
index 0000000..172ff04
--- /dev/null
+++ b/Runtime/Export/LightProbeBindings.txt
@@ -0,0 +1,35 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Graphics/LightProbeGroup.h"
+
+CSRAW
+using System;
+namespace UnityEngine
+{
+
+// Light Probe Group
+CLASS LightProbeGroup : Component
+
+ // Editor only function to access and modify probe positions.
+ CONDITIONAL !UNITY_FLASH
+ CUSTOM_PROP Vector3[] probePositions
+ {
+ #if UNITY_EDITOR
+ return CreateScriptingArray (self->GetPositions(), self->GetPositionsSize (), MONO_COMMON.vector3);
+ #else
+ return CreateScriptingArray ((const Vector3f*)NULL, 0, MONO_COMMON.vector3);
+ #endif
+ }
+ {
+ #if UNITY_EDITOR
+ self->SetPositions (&GetMonoArrayElement<Vector3f> (value, 0), mono_array_length_safe(value));
+ #endif
+ }
+END
+
+CSRAW }
diff --git a/Runtime/Export/LocalService.cs b/Runtime/Export/LocalService.cs
new file mode 100644
index 0000000..bbb8ca4
--- /dev/null
+++ b/Runtime/Export/LocalService.cs
@@ -0,0 +1,655 @@
+using System;
+using System.Collections.Generic;
+
+namespace UnityEngine.SocialPlatforms.Impl
+{
+ public class LocalUser : UserProfile, ILocalUser
+ {
+ private IUserProfile[] m_Friends;
+ private bool m_Authenticated;
+ private bool m_Underage;
+
+ public LocalUser()
+ {
+ m_Friends = new UserProfile[0];
+ m_Authenticated = false;
+ m_Underage = false;
+ }
+
+ public void Authenticate (Action<bool> callback)
+ {
+ ActivePlatform.Instance.Authenticate((ILocalUser)this, callback);
+ }
+
+ public void LoadFriends (Action<bool> callback)
+ {
+ ActivePlatform.Instance.LoadFriends((ILocalUser)this, callback);
+ }
+
+ // Setters for implementors
+ public void SetFriends(IUserProfile[] friends)
+ {
+ m_Friends = friends;
+ }
+ public void SetAuthenticated(bool value)
+ {
+ m_Authenticated = value;
+ }
+ public void SetUnderage(bool value)
+ {
+ m_Underage = value;
+ }
+
+ public IUserProfile[] friends { get { return m_Friends; } }
+ public bool authenticated { get { return m_Authenticated; } }
+ public bool underage { get { return m_Underage; } }
+ }
+
+ public class UserProfile : IUserProfile
+ {
+ protected string m_UserName;
+ protected string m_ID;
+ protected bool m_IsFriend;
+ protected UserState m_State;
+ protected Texture2D m_Image;
+
+ public UserProfile()
+ {
+ m_UserName = "Uninitialized";
+ m_ID = "0";
+ m_IsFriend = false;
+ m_State = UserState.Offline;
+ m_Image = new Texture2D(32, 32);
+ }
+
+ public UserProfile(string name, string id, bool friend) : this (name, id, friend, UserState.Offline, new Texture2D(0, 0)) { }
+
+ public UserProfile(string name, string id, bool friend, UserState state, Texture2D image)
+ {
+ m_UserName = name;
+ m_ID = id;
+ m_IsFriend = friend;
+ m_State = state;
+ m_Image = image;
+ }
+
+ public override string ToString()
+ {
+ return id + " - " +
+ userName + " - " +
+ isFriend + " - " +
+ state;
+ }
+
+ public void SetUserName(string name)
+ {
+ m_UserName = name;
+ }
+ public void SetUserID(string id)
+ {
+ m_ID = id;
+ }
+ public void SetImage(Texture2D image)
+ {
+ m_Image = image;
+ }
+ public void SetIsFriend(bool value)
+ {
+ m_IsFriend = value;
+ }
+ public void SetState(UserState state)
+ {
+ m_State = state;
+ }
+
+ public string userName { get { return m_UserName; } }
+ public string id { get { return m_ID; } }
+ public bool isFriend { get { return m_IsFriend; } }
+ public UserState state { get { return m_State; } }
+ public Texture2D image { get { return m_Image; } }
+ }
+
+ public class Achievement : IAchievement
+ {
+ private bool m_Completed;
+ private bool m_Hidden;
+ private DateTime m_LastReportedDate;
+
+ public Achievement(string id,
+ double percentCompleted,
+ bool completed,
+ bool hidden,
+ DateTime lastReportedDate)
+ {
+ this.id = id;
+ this.percentCompleted = percentCompleted;
+ m_Completed = completed;
+ m_Hidden = hidden;
+ m_LastReportedDate = lastReportedDate;
+ }
+
+ public Achievement(string id, double percent)
+ {
+ this.id = id;
+ percentCompleted = percent;
+ m_Hidden = false;
+ m_Completed = false;
+ m_LastReportedDate = DateTime.MinValue;
+ }
+
+ public Achievement() : this("unknown", 0.0) { }
+
+ public override string ToString()
+ {
+ return id + " - " +
+ percentCompleted + " - " +
+ completed + " - " +
+ hidden + " - " +
+ lastReportedDate;
+ }
+
+ public void ReportProgress(Action<bool> callback)
+ {
+ ActivePlatform.Instance.ReportProgress(id, percentCompleted, callback);
+ }
+
+ // Setters for implementors
+ public void SetCompleted(bool value)
+ {
+ m_Completed = value;
+ }
+ public void SetHidden(bool value)
+ {
+ m_Hidden = value;
+ }
+ public void SetLastReportedDate(DateTime date)
+ {
+ m_LastReportedDate = date;
+ }
+
+ public string id { get; set; }
+ public double percentCompleted { get; set; }
+ public bool completed { get { return m_Completed; } }
+ public bool hidden { get { return m_Hidden; } }
+ public DateTime lastReportedDate { get { return m_LastReportedDate; } }
+
+ // TODO: How to have a static method for resetting all achivements?
+ //public abstract void ResetAllAchievements();
+ }
+
+ public class AchievementDescription : IAchievementDescription
+ {
+ private string m_Title;
+ private Texture2D m_Image;
+ private string m_AchievedDescription;
+ private string m_UnachievedDescription;
+ private bool m_Hidden;
+ private int m_Points;
+
+ public AchievementDescription(string id,
+ string title,
+ Texture2D image,
+ string achievedDescription,
+ string unachievedDescription,
+ bool hidden,
+ int points)
+ {
+ this.id = id;
+ m_Title = title;
+ m_Image = image;
+ m_AchievedDescription = achievedDescription;
+ m_UnachievedDescription = unachievedDescription;
+ m_Hidden = hidden;
+ m_Points = points;
+ }
+
+ public override string ToString()
+ {
+ return id + " - " +
+ title + " - " +
+ achievedDescription + " - " +
+ unachievedDescription + " - " +
+ points + " - " +
+ hidden;
+ }
+
+ public void SetImage(Texture2D image)
+ {
+ m_Image = image;
+ }
+
+ public string id { get; set; }
+ public string title { get { return m_Title; } }
+ public Texture2D image { get { return m_Image; } }
+ public string achievedDescription { get { return m_AchievedDescription; } }
+ public string unachievedDescription { get { return m_UnachievedDescription; } }
+ public bool hidden { get { return m_Hidden; } }
+ public int points { get { return m_Points; } }
+ }
+
+ public class Score : IScore
+ {
+ private DateTime m_Date;
+ private string m_FormattedValue;
+ private string m_UserID;
+ private int m_Rank;
+
+ public Score() : this("unkown", -1) { }
+
+ public Score(string leaderboardID, Int64 value)
+ : this(leaderboardID, value, "0", DateTime.Now, "", -1)
+ { }
+
+ public Score(string leaderboardID, Int64 value, string userID, DateTime date, string formattedValue, int rank)
+ {
+ this.leaderboardID = leaderboardID;
+ this.value = value;
+ m_UserID = userID;
+ m_Date = date;
+ m_FormattedValue = formattedValue;
+ m_Rank = rank;
+ }
+
+ public override string ToString()
+ {
+ return "Rank: '" + m_Rank + "' Value: '" + value + "' Category: '" + leaderboardID + "' PlayerID: '" +
+ m_UserID + "' Date: '" + m_Date;
+ }
+
+ public void ReportScore(Action<bool> callback)
+ {
+ ActivePlatform.Instance.ReportScore(value, leaderboardID, callback);
+ }
+
+ public void SetDate(DateTime date)
+ {
+ m_Date = date;
+ }
+ public void SetFormattedValue(string value)
+ {
+ m_FormattedValue = value;
+ }
+ public void SetUserID(string userID)
+ {
+ m_UserID = userID;
+ }
+ public void SetRank(int rank)
+ {
+ m_Rank = rank;
+ }
+
+ public string leaderboardID { get; set; }
+ // TODO: This is just an int64 here, but should be able to represent all supported formats, except for float type scores ...
+ public Int64 value { get; set; }
+ public DateTime date { get { return m_Date; } }
+ public string formattedValue { get { return m_FormattedValue; } }
+ public string userID { get { return m_UserID; } }
+ public int rank { get { return m_Rank; } }
+ }
+
+ public class Leaderboard : ILeaderboard
+ {
+ private bool m_Loading;
+ private IScore m_LocalUserScore;
+ private uint m_MaxRange;
+ private IScore[] m_Scores;
+ private string m_Title;
+ private string[] m_UserIDs;
+
+ public Leaderboard()
+ {
+ id = "Invalid";
+ range = new Range(1, 10);
+ userScope = UserScope.Global;
+ timeScope = TimeScope.AllTime;
+ m_Loading = false;
+ m_LocalUserScore = new Score("Invalid", 0);
+ m_MaxRange = 0;
+ m_Scores = new Score[0];
+ m_Title = "Invalid";
+ m_UserIDs = new string[0];
+ }
+
+ // TODO: Implement different behaviour when this is populated
+ /*public Leaderboard(string[] userIDs): this()
+ {
+ m_UserIDs = userIDs;
+ }*/
+
+ public void SetUserFilter(string[] userIDs)
+ {
+ m_UserIDs = userIDs;
+ }
+
+ public override string ToString()
+ {
+ return "ID: '" + id + "' Title: '" + m_Title + "' Loading: '" + m_Loading + "' Range: [" +
+ range.from + "," + range.count + "] MaxRange: '" + m_MaxRange + "' Scores: '" +
+ m_Scores.Length + "' UserScope: '" + userScope + "' TimeScope: '" + timeScope + "' UserFilter: '"
+ + m_UserIDs.Length;
+ }
+
+ public void LoadScores(Action<bool> callback)
+ {
+ ActivePlatform.Instance.LoadScores(this, callback);
+ }
+
+ public bool loading
+ {
+ get { return ActivePlatform.Instance.GetLoading(this); }
+ }
+
+ // Setters for implementors
+ public void SetLocalUserScore(IScore score)
+ {
+ m_LocalUserScore = score;
+ }
+ public void SetMaxRange(uint maxRange)
+ {
+ m_MaxRange = maxRange;
+ }
+ public void SetScores(IScore[] scores)
+ {
+ m_Scores = scores;
+ }
+ public void SetTitle(string title)
+ {
+ m_Title = title;
+ }
+ public string[] GetUserFilter()
+ {
+ return m_UserIDs;
+ }
+
+ public string id { get; set; }
+ public UserScope userScope { get; set; }
+ public Range range { get; set; }
+ public TimeScope timeScope { get; set; }
+ public IScore localUserScore { get { return m_LocalUserScore; } }
+ public uint maxRange { get { return m_MaxRange; } }
+ public IScore[] scores { get { return m_Scores; } }
+ public string title { get { return m_Title; } }
+ }
+}
+
+namespace UnityEngine.SocialPlatforms
+{
+ using UnityEngine.SocialPlatforms.Impl;
+
+ public class Local : ISocialPlatform
+ {
+ static LocalUser m_LocalUser = null;
+ List<UserProfile> m_Friends = new List<UserProfile>();
+ List<UserProfile> m_Users = new List<UserProfile>();
+ List<AchievementDescription> m_AchievementDescriptions = new List<AchievementDescription>();
+ List<Achievement> m_Achievements = new List<Achievement>();
+ List<Leaderboard> m_Leaderboards = new List<Leaderboard>();
+ Texture2D m_DefaultTexture;
+
+ public ILocalUser localUser
+ {
+ get
+ {
+ if (m_LocalUser == null)
+ m_LocalUser = new LocalUser();
+ return m_LocalUser;
+ }
+ }
+
+ void ISocialPlatform.Authenticate(ILocalUser user, System.Action<bool> callback)
+ {
+ LocalUser thisUser = (LocalUser)user;
+ m_DefaultTexture = CreateDummyTexture(32, 32);
+ PopulateStaticData();
+ thisUser.SetAuthenticated(true);
+ thisUser.SetUnderage(false);
+ thisUser.SetUserID("1000");
+ thisUser.SetUserName("Lerpz");
+ thisUser.SetImage(m_DefaultTexture);
+ if (callback != null)
+ callback(true);
+ }
+
+ void ISocialPlatform.LoadFriends(ILocalUser user, System.Action<bool> callback)
+ {
+ if (!VerifyUser()) return;
+ ((LocalUser)user).SetFriends(m_Friends.ToArray());
+ if (callback != null)
+ callback(true);
+ }
+
+ public void LoadUsers(string[] userIDs, Action<IUserProfile[]> callback)
+ {
+ List<UserProfile> matches = new List<UserProfile>();
+ if (!VerifyUser()) return;
+ foreach (string userId in userIDs)
+ {
+ foreach(UserProfile user in m_Users)
+ if (user.id == userId)
+ matches.Add(user);
+ foreach(UserProfile user in m_Friends)
+ if (user.id == userId)
+ matches.Add(user);
+ }
+ callback(matches.ToArray());
+ }
+
+ public void ReportProgress(string id, double progress, System.Action<bool> callback)
+ {
+ if (!VerifyUser()) return;
+ // Update achievement if it's already been reported
+ foreach (Achievement achoo in m_Achievements)
+ {
+ // TODO: Only allow increase in progress, figure out if xbox/gc report errors when lower progress is reported
+ if (achoo.id == id && achoo.percentCompleted <= progress)
+ {
+ if (progress >= 100.0)
+ achoo.SetCompleted(true);
+ achoo.SetHidden(false);
+ achoo.SetLastReportedDate(DateTime.Now);
+ achoo.percentCompleted = progress;
+ if (callback != null)
+ callback(true);
+ // No need to process this report any more
+ return;
+ }
+ }
+
+ // If it's not been reported already check if it's a valid achievement description ID and add it, else it's an error
+ foreach (AchievementDescription achoo in m_AchievementDescriptions)
+ {
+ // TODO: Only allow increase in progress, figure out if xbox/gc report errors when lower progress is reported
+ if (achoo.id == id)
+ {
+ bool completed = (progress >= 100.0 ? true : false);
+ Achievement newAchievement = new Achievement(id, progress, completed, false, DateTime.Now);
+ m_Achievements.Add(newAchievement);
+ if (callback != null)
+ callback(true);
+ return;
+ }
+ }
+
+ Debug.LogError("Achievement ID not found");
+ if (callback != null)
+ callback(false);
+ }
+
+ public void LoadAchievementDescriptions(System.Action<IAchievementDescription[]> callback)
+ {
+ if (!VerifyUser()) return;
+ if (callback != null)
+ callback(m_AchievementDescriptions.ToArray());
+ }
+
+ public void LoadAchievements(System.Action<IAchievement[]> callback)
+ {
+ if (!VerifyUser()) return;
+ if (callback != null)
+ callback(m_Achievements.ToArray());
+ }
+
+ public void ReportScore(Int64 score, string board, System.Action<bool> callback)
+ {
+ if (!VerifyUser()) return;
+ foreach (Leaderboard current in m_Leaderboards)
+ {
+ if (current.id == board)
+ {
+ // TODO: IIRC GameCenter only adds scores if they are higher than the users previous score, maybe mirror this here.
+ List<Score> scores = new List<Score>((Score[])current.scores);
+ scores.Add(new Score(board, score, localUser.id, DateTime.Now, score + " points", 0));
+ current.SetScores(scores.ToArray());
+ if (callback != null)
+ callback(true);
+ return;
+ }
+ }
+ Debug.LogError("Leaderboard not found");
+ if (callback != null)
+ callback(false);
+ }
+
+ public void LoadScores(string leaderboardID, Action<IScore[]> callback)
+ {
+ if (!VerifyUser()) return;
+ foreach (Leaderboard current in m_Leaderboards)
+ {
+ if (current.id == leaderboardID)
+ {
+ SortScores(current);
+ if (callback != null)
+ callback(current.scores);
+ return;
+ }
+ }
+ Debug.LogError("Leaderboard not found");
+ if (callback != null)
+ callback(new Score[0]);
+ }
+
+ void ISocialPlatform.LoadScores(ILeaderboard board, System.Action<bool> callback)
+ {
+ if (!VerifyUser()) return;
+ // Fill in fake server side data on leaderboard
+ Leaderboard thisBoard = (Leaderboard)board;
+ foreach (Leaderboard lb in m_Leaderboards)
+ {
+ // TODO: In the real world the board might have thousands of scores but only 100
+ // are returned, for now we can assume they are less than 100 here
+ // TODO: Apply the filters which the leaderboard has, right now it always returns everything found
+ if (lb.id == thisBoard.id)
+ {
+ thisBoard.SetTitle(lb.title);
+ thisBoard.SetScores(lb.scores);
+ thisBoard.SetMaxRange((uint)lb.scores.Length);
+ }
+ }
+ SortScores(thisBoard);
+ SetLocalPlayerScore(thisBoard);
+ if (callback != null)
+ callback(true);
+ }
+
+ bool ISocialPlatform.GetLoading(ILeaderboard board)
+ {
+ if (!VerifyUser()) return false;
+ return ((Leaderboard)board).loading;
+ }
+
+ // TODO: Sorting seems to be broken...
+ private void SortScores(Leaderboard board)
+ {
+ List<Score> scores = new List<Score>((Score[])board.scores);
+ scores.Sort(delegate(Score s1, Score s2)
+ {
+ return s2.value.CompareTo(s1.value);
+ });
+ for (int i = 0; i < scores.Count; i++)
+ scores[i].SetRank(i+1);
+ }
+
+ private void SetLocalPlayerScore(Leaderboard board)
+ {
+ foreach (Score score in board.scores)
+ {
+ if (score.userID == localUser.id)
+ {
+ board.SetLocalUserScore(score);
+ break;
+ }
+ }
+ }
+
+
+ public void ShowAchievementsUI()
+ {
+ Debug.Log("ShowAchievementsUI not implemented");
+ }
+
+ public void ShowLeaderboardUI()
+ {
+ Debug.Log("ShowLeaderboardUI not implemented");
+ }
+
+ public ILeaderboard CreateLeaderboard()
+ {
+ Leaderboard lb = new Leaderboard();
+ return (ILeaderboard)lb;
+ }
+
+ public IAchievement CreateAchievement()
+ {
+ Achievement achoo = new Achievement();
+ return (IAchievement)achoo;
+ }
+
+ private bool VerifyUser()
+ {
+ if (!localUser.authenticated)
+ {
+ Debug.LogError("Must authenticate first");
+ return false;
+ }
+ return true;
+ }
+
+ private void PopulateStaticData()
+ {
+ m_Friends.Add(new UserProfile("Fred", "1001", true, UserState.Online, m_DefaultTexture));
+ m_Friends.Add(new UserProfile("Julia", "1002", true, UserState.Online, m_DefaultTexture));
+ m_Friends.Add(new UserProfile("Jeff", "1003", true, UserState.Online, m_DefaultTexture));
+ m_Users.Add(new UserProfile("Sam", "1004", false, UserState.Offline, m_DefaultTexture));
+ m_Users.Add(new UserProfile("Max", "1005", false, UserState.Offline, m_DefaultTexture));
+ m_AchievementDescriptions.Add (new AchievementDescription("Achievement01", "First achievement", m_DefaultTexture, "Get first achievement", "Received first achievement", false, 10));
+ m_AchievementDescriptions.Add (new AchievementDescription("Achievement02", "Second achievement", m_DefaultTexture, "Get second achievement", "Received second achievement", false, 20));
+ m_AchievementDescriptions.Add (new AchievementDescription("Achievement03", "Third achievement", m_DefaultTexture, "Get third achievement", "Received third achievement", false, 15));
+ Leaderboard board = new Leaderboard();
+ board.SetTitle("High Scores");
+ board.id = "Leaderboard01";
+ List<Score> scores = new List<Score>();
+ scores.Add(new Score("Leaderboard01", 300, "1001", DateTime.Now.AddDays(-1), "300 points", 1));
+ scores.Add(new Score("Leaderboard01", 255, "1002", DateTime.Now.AddDays(-1), "255 points", 2));
+ scores.Add(new Score("Leaderboard01", 55, "1003", DateTime.Now.AddDays(-1), "55 points", 3));
+ scores.Add(new Score("Leaderboard01", 10, "1004", DateTime.Now.AddDays(-1), "10 points", 4));
+ board.SetScores(scores.ToArray());
+ m_Leaderboards.Add(board);
+ }
+
+ private Texture2D CreateDummyTexture(int width, int height)
+ {
+ Texture2D texture = new Texture2D(width, height);
+ for (int y = 0; y < height; ++y)
+ {
+ for (int x = 0; x < width; ++x)
+ {
+ Color color = (x&y) > 0 ? Color.white : Color.gray;
+ texture.SetPixel (x, y, color);
+ }
+ }
+ texture.Apply();
+ return texture;
+ }
+ }
+}
+
diff --git a/Runtime/Export/Math.txt b/Runtime/Export/Math.txt
new file mode 100644
index 0000000..6dbd21f
--- /dev/null
+++ b/Runtime/Export/Math.txt
@@ -0,0 +1,2391 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Geometry/Ray.h"
+#include "Runtime/Geometry/Ray2D.h"
+#include "Runtime/Geometry/Intersection.h"
+#include <vector>
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Terrain/PerlinNoise.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace UnityEngine
+{
+
+// Representation of 2D vectors and points.
+THREAD_SAFE
+STRUCT Vector2
+ // X component of the vector.
+ CSRAW public float x;
+ // Y component of the vector.
+ CSRAW public float y;
+
+ // Access the /x/ or /y/ component using [0] or [1] respectively.
+ CSRAW public float this [int index]
+ {
+ get
+ {
+ switch(index)
+ {
+ case 0: return x;
+ case 1: return y;
+ default:
+ throw new IndexOutOfRangeException("Invalid Vector2 index!");
+ }
+ }
+
+ set
+ {
+ switch(index)
+ {
+ case 0: x = value; break;
+ case 1: y = value; break;
+ default:
+ throw new IndexOutOfRangeException("Invalid Vector2 index!");
+ }
+ }
+ }
+
+ // Constructs a new vector with given x, y components.
+ public Vector2 (float x, float y) { this.x = x; this.y = y; }
+
+ // Set x and y components of an existing Vector2.
+ CSRAW public void Set (float new_x, float new_y) { x = new_x; y = new_y; }
+
+ // Linearly interpolates between two vectors.
+ CSRAW public static Vector2 Lerp (Vector2 from, Vector2 to, float t)
+ {
+ t = Mathf.Clamp01 (t);
+ return new Vector2(
+ from.x + (to.x - from.x)*t,
+ from.y + (to.y - from.y)*t
+ );
+ }
+
+ // Moves a point /current/ towards /target/.
+ CSRAW static public Vector2 MoveTowards (Vector2 current, Vector2 target, float maxDistanceDelta)
+ {
+ Vector2 toVector = target - current;
+ float dist = toVector.magnitude;
+ if (dist <= maxDistanceDelta || dist == 0)
+ return target;
+ return current + toVector / dist * maxDistanceDelta;
+ }
+
+ // Multiplies two vectors component-wise.
+ CSRAW public static Vector2 Scale (Vector2 a, Vector2 b) { return new Vector2 (a.x*b.x, a.y*b.y); }
+
+ // Multiplies every component of this vector by the same component of /scale/.
+ CSRAW public void Scale (Vector2 scale) { x *= scale.x; y *= scale.y; }
+
+ // Makes this vector have a ::ref::magnitude of 1.
+ CSRAW public void Normalize ()
+ {
+ float mag = this.magnitude;
+ if (mag > kEpsilon)
+ this = this / mag;
+ else
+ this = zero;
+ }
+
+ // Returns this vector with a ::ref::magnitude of 1 (RO).
+ CSRAW public Vector2 normalized { get {
+ Vector2 v = new Vector2(x, y);
+ v.Normalize();
+ return v;
+ } }
+
+ /// *listonly*
+ CSRAW override public string ToString() { return UnityString.Format("({0:F1}, {1:F1})", x, y); }
+ // Returns a nicely formatted string for this vector.
+ CSRAW public string ToString(string format) {
+ return UnityString.Format("({0}, {1})", x.ToString(format), y.ToString(format));
+ }
+
+ // used to allow Vector2s to be used as keys in hash tables
+ public override int GetHashCode() {
+ return x.GetHashCode() ^ (y.GetHashCode()<<2);
+ }
+
+ // also required for being able to use Vector2s as keys in hash tables
+ public override bool Equals(object other) {
+ if(!(other is Vector2)) return false;
+
+ Vector2 rhs=(Vector2)other;
+ return x.Equals(rhs.x) && y.Equals(rhs.y);
+ }
+
+ // Dot Product of two vectors.
+ public static float Dot (Vector2 lhs, Vector2 rhs) { return lhs.x*rhs.x + lhs.y*rhs.y; }
+
+ // Returns the length of this vector (RO).
+ CSRAW public float magnitude { get { return Mathf.Sqrt (x*x + y*y); } }
+ // Returns the squared length of this vector (RO).
+ CSRAW public float sqrMagnitude { get { return x*x + y*y; } }
+
+ // Returns the angle in degrees between /from/ and /to/.
+ CSRAW public static float Angle(Vector2 from, Vector2 to) { return Mathf.Acos(Mathf.Clamp (Vector2.Dot (from.normalized, to.normalized), -1F, 1F)) * Mathf.Rad2Deg; }
+
+ // Returns the distance between /a/ and /b/.
+ CSRAW public static float Distance (Vector2 a, Vector2 b) { return (a-b).magnitude; }
+
+ // Returns a copy of /vector/ with its magnitude clamped to /maxLength/.
+ CSRAW public static Vector2 ClampMagnitude (Vector2 vector, float maxLength)
+ {
+ if (vector.sqrMagnitude > maxLength * maxLength)
+ return vector.normalized * maxLength;
+ return vector;
+ }
+
+ OBSOLETE planned Use Vector2.sqrMagnitude
+ CSRAW public static float SqrMagnitude (Vector2 a) { return a.x*a.x + a.y*a.y; }
+ OBSOLETE planned Use .sqrMagnitude
+ CSRAW public float SqrMagnitude () { return x*x + y*y; }
+
+ // Returns a vector that is made from the smallest components of two vectors.
+ CSRAW public static Vector2 Min (Vector2 lhs, Vector2 rhs) { return new Vector2 (Mathf.Min(lhs.x,rhs.x), Mathf.Min(lhs.y,rhs.y)); }
+
+ // Returns a vector that is made from the largest components of two vectors.
+ CSRAW public static Vector2 Max (Vector2 lhs, Vector2 rhs) { return new Vector2 (Mathf.Max(lhs.x,rhs.x), Mathf.Max(lhs.y,rhs.y)); }
+
+ // Adds two vectors.
+ CSRAW public static Vector2 operator + (Vector2 a, Vector2 b) { return new Vector2 (a.x+b.x, a.y+b.y); }
+ // Subtracts one vector from another.
+ CSRAW public static Vector2 operator - (Vector2 a, Vector2 b) { return new Vector2 (a.x-b.x, a.y-b.y); }
+ // Negates a vector.
+ CSRAW public static Vector2 operator - (Vector2 a) { return new Vector2 (-a.x, -a.y); }
+ // Multiplies a vector by a number.
+ CSRAW public static Vector2 operator * (Vector2 a, float d) { return new Vector2 (a.x*d, a.y*d); }
+ // Multiplies a vector by a number.
+ CSRAW public static Vector2 operator * (float d, Vector2 a) { return new Vector2 (a.x*d, a.y*d); }
+ // Divides a vector by a number.
+ CSRAW public static Vector2 operator / (Vector2 a, float d) { return new Vector2 (a.x/d, a.y/d); }
+ // Returns true if the vectors are equal.
+ CSRAW public static bool operator == (Vector2 lhs, Vector2 rhs) {
+ return SqrMagnitude (lhs - rhs) < kEpsilon * kEpsilon;
+ }
+ // Returns true if vectors different.
+ CSRAW public static bool operator != (Vector2 lhs, Vector2 rhs)
+ {
+ return SqrMagnitude (lhs - rhs) >= kEpsilon * kEpsilon;
+ }
+
+ // Converts a [[Vector3]] to a Vector2.
+ CSRAW public static implicit operator Vector2(Vector3 v) {
+ return new Vector2(v.x, v.y);
+ }
+ // Converts a Vector2 to a [[Vector3]].
+ CSRAW public static implicit operator Vector3(Vector2 v) {
+ return new Vector3(v.x, v.y, 0);
+ }
+
+ // Shorthand for writing @@Vector2(0, 0)@@
+ CSRAW public static Vector2 zero { get { return new Vector2 (0.0F, 0.0F); } }
+ // Shorthand for writing @@Vector2(1, 1)@@
+ CSRAW public static Vector2 one { get { return new Vector2 (1.0F, 1.0F); } }
+ // Shorthand for writing @@Vector2(0, 1)@@
+ CSRAW public static Vector2 up { get { return new Vector2 (0.0F, 1.0F); } }
+ // Shorthand for writing @@Vector2(1, 0)@@
+ CSRAW public static Vector2 right { get { return new Vector2 (1.0F, 0.0F); } }
+
+ // *Undocumented*
+ CSRAW public const float kEpsilon = 0.00001F;
+END
+
+
+// Representation of 3D vectors and points.
+CSRAW
+THREAD_SAFE
+STRUCT Vector3
+
+ // *undocumented*
+ CSRAW public const float kEpsilon = 0.00001F;
+
+ // X component of the vector.
+ CSRAW public float x;
+ // Y component of the vector.
+ CSRAW public float y;
+ // Z component of the vector.
+ CSRAW public float z;
+
+
+ // Linearly interpolates between two vectors.
+ CSRAW public static Vector3 Lerp (Vector3 from, Vector3 to, float t)
+ {
+ t = Mathf.Clamp01 (t);
+ return new Vector3(
+ from.x + (to.x - from.x)*t,
+ from.y + (to.y - from.y)*t,
+ from.z + (to.z - from.z)*t
+ );
+ }
+
+
+ // Spherically interpolates between two vectors.
+ CUSTOM static Vector3 Slerp (Vector3 from, Vector3 to, float t) { return Slerp (from, to, clamp01 (t)); }
+
+
+ CUSTOM private static void Internal_OrthoNormalize2 (ref Vector3 a, ref Vector3 b) { OrthoNormalize (&a, &b); }
+ CUSTOM private static void Internal_OrthoNormalize3 (ref Vector3 a, ref Vector3 b, ref Vector3 c) { OrthoNormalize (&a, &b, &c); }
+
+ // Makes vectors normalized and orthogonal to each other.
+ CSRAW static public void OrthoNormalize (ref Vector3 normal, ref Vector3 tangent) { Internal_OrthoNormalize2 (ref normal, ref tangent); }
+ // Makes vectors normalized and orthogonal to each other.
+ CSRAW static public void OrthoNormalize (ref Vector3 normal, ref Vector3 tangent, ref Vector3 binormal) { Internal_OrthoNormalize3 (ref normal, ref tangent, ref binormal); }
+
+
+ // Moves a point /current/ in a straight line towards a /target/ point.
+ CSRAW static public Vector3 MoveTowards (Vector3 current, Vector3 target, float maxDistanceDelta)
+ {
+ Vector3 toVector = target - current;
+ float dist = toVector.magnitude;
+ if (dist <= maxDistanceDelta || dist == 0)
+ return target;
+ return current + toVector / dist * maxDistanceDelta;
+ }
+
+ // Rotates a vector /current/ towards /target/.
+ CUSTOM static Vector3 RotateTowards (Vector3 current, Vector3 target, float maxRadiansDelta, float maxMagnitudeDelta) { return RotateTowards (current, target, maxRadiansDelta, maxMagnitudeDelta); }
+
+ // Gradually changes a vector towards a desired goal over time.
+ CSRAW public static Vector3 SmoothDamp (Vector3 current, Vector3 target, ref Vector3 currentVelocity, float smoothTime, float maxSpeed = Mathf.Infinity, float deltaTime = Time.deltaTime)
+ {
+ // Based on Game Programming Gems 4 Chapter 1.10
+ smoothTime = Mathf.Max(0.0001F, smoothTime);
+ float omega = 2F / smoothTime;
+
+ float x = omega * deltaTime;
+ float exp = 1F / (1F + x + 0.48F*x*x + 0.235F*x*x*x);
+ Vector3 change = current - target;
+ Vector3 originalTo = target;
+
+ // Clamp maximum speed
+ float maxChange = maxSpeed * smoothTime;
+ change = Vector3.ClampMagnitude(change, maxChange);
+ target = current - change;
+
+ Vector3 temp = (currentVelocity + omega * change) * deltaTime;
+ currentVelocity = (currentVelocity - omega * temp) * exp;
+ Vector3 output = target + (change + temp) * exp;
+
+ // Prevent overshooting
+ if (Vector3.Dot(originalTo - current, output - originalTo) > 0)
+ {
+ output = originalTo;
+ currentVelocity = (output - originalTo) / deltaTime;
+ }
+
+ return output;
+ }
+
+ // Access the x, y, z components using [0], [1], [2] respectively.
+ CSRAW public float this [int index]
+ {
+ get
+ {
+ switch(index)
+ {
+ case 0: return x;
+ case 1: return y;
+ case 2: return z;
+ default:
+ throw new IndexOutOfRangeException("Invalid Vector3 index!");
+ }
+ }
+
+ set
+ {
+ switch(index)
+ {
+ case 0: x = value; break;
+ case 1: y = value; break;
+ case 2: z = value; break;
+ default:
+ throw new IndexOutOfRangeException("Invalid Vector3 index!");
+ }
+ }
+ }
+
+ // Creates a new vector with given x, y, z components.
+ public Vector3 (float x, float y, float z) { this.x = x; this.y = y; this.z = z; }
+ // Creates a new vector with given x, y components and sets /z/ to zero.
+ public Vector3 (float x, float y) { this.x = x; this.y = y; z = 0F; }
+
+ // Set x, y and z components of an existing Vector3.
+ CSRAW public void Set (float new_x, float new_y, float new_z) { x = new_x; y = new_y; z = new_z; }
+
+ // Multiplies two vectors component-wise.
+ CSRAW public static Vector3 Scale (Vector3 a, Vector3 b) { return new Vector3 (a.x*b.x, a.y*b.y, a.z*b.z); }
+
+ // Multiplies every component of this vector by the same component of /scale/.
+ CSRAW public void Scale (Vector3 scale) { x *= scale.x; y *= scale.y; z *= scale.z; }
+
+ // Cross Product of two vectors.
+ CSRAW public static Vector3 Cross (Vector3 lhs, Vector3 rhs)
+ {
+ return new Vector3 (
+ lhs.y * rhs.z - lhs.z * rhs.y,
+ lhs.z * rhs.x - lhs.x * rhs.z,
+ lhs.x * rhs.y - lhs.y * rhs.x);
+ }
+
+ // used to allow Vector3s to be used as keys in hash tables
+ public override int GetHashCode() {
+ return x.GetHashCode() ^ (y.GetHashCode()<<2) ^ (z.GetHashCode()>>2);
+ }
+
+ // also required for being able to use Vector3s as keys in hash tables
+ public override bool Equals(object other) {
+ if(!(other is Vector3)) return false;
+
+ Vector3 rhs=(Vector3)other;
+ return x.Equals(rhs.x) && y.Equals(rhs.y) && z.Equals(rhs.z);
+ }
+
+ // Reflects a vector off the plane defined by a normal.
+ CSRAW public static Vector3 Reflect (Vector3 inDirection, Vector3 inNormal)
+ {
+ return -2F * Dot (inNormal, inDirection) * inNormal + inDirection;
+ }
+
+ // *undoc* --- we have normalized property now
+ CSRAW public static Vector3 Normalize (Vector3 value)
+ {
+ float mag = Magnitude (value);
+ if (mag > kEpsilon)
+ return value / mag;
+ else
+ return zero;
+ }
+
+ // Makes this vector have a ::ref::magnitude of 1.
+ CSRAW public void Normalize ()
+ {
+ float mag = Magnitude (this);
+ if (mag > kEpsilon)
+ this = this / mag;
+ else
+ this = zero;
+ }
+
+ // Returns this vector with a ::ref::magnitude of 1 (RO).
+ CSRAW public Vector3 normalized { get { return Vector3.Normalize (this); } }
+
+ /// *listonly*
+ CSRAW override public string ToString() { return UnityString.Format("({0:F1}, {1:F1}, {2:F1})", x, y, z); }
+ // Returns a nicely formatted string for this vector.
+ CSRAW public string ToString(string format) {
+ return UnityString.Format("({0}, {1}, {2})", x.ToString(format), y.ToString(format), z.ToString(format));
+ }
+
+ // Dot Product of two vectors.
+ public static float Dot (Vector3 lhs, Vector3 rhs) { return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z; }
+
+ // Projects a vector onto another vector.
+ public static Vector3 Project (Vector3 vector, Vector3 onNormal) {
+ float sqrMag = Dot(onNormal,onNormal);
+ if (sqrMag < Mathf.Epsilon)
+ return zero;
+ else
+ return onNormal * Dot (vector, onNormal) / sqrMag;
+ }
+
+ //*undocumented* --------------------------- TODO is this generally useful? What is the intention? I know i understood it once upon a time but it evaded my mind.
+ CSRAW public static Vector3 Exclude (Vector3 excludeThis, Vector3 fromThat) {
+ return fromThat - Project (fromThat, excludeThis);
+ }
+
+ // Returns the angle in degrees between /from/ and /to/. This is always the smallest
+ CSRAW public static float Angle(Vector3 from, Vector3 to) { return Mathf.Acos(Mathf.Clamp (Vector3.Dot (from.normalized, to.normalized), -1F, 1F)) * Mathf.Rad2Deg; }
+
+ // Returns the distance between /a/ and /b/.
+ public static float Distance (Vector3 a, Vector3 b) { Vector3 vec = new Vector3 (a.x - b.x, a.y - b.y, a.z - b.z); return Mathf.Sqrt (vec.x * vec.x + vec.y * vec.y + vec.z * vec.z); }
+
+ // Returns a copy of /vector/ with its magnitude clamped to /maxLength/.
+ CSRAW public static Vector3 ClampMagnitude (Vector3 vector, float maxLength)
+ {
+ if (vector.sqrMagnitude > maxLength * maxLength)
+ return vector.normalized * maxLength;
+ return vector;
+ }
+
+ // *undoc* --- there's a property now
+ CSRAW public static float Magnitude (Vector3 a) { return Mathf.Sqrt (a.x*a.x + a.y*a.y + a.z*a.z); }
+
+ // Returns the length of this vector (RO).
+ CSRAW public float magnitude { get { return Mathf.Sqrt (x*x + y*y + z*z); } }
+
+ // *undoc* --- there's a property now
+ CSRAW public static float SqrMagnitude (Vector3 a) { return a.x*a.x + a.y*a.y + a.z*a.z; }
+
+ // Returns the squared length of this vector (RO).
+ CSRAW public float sqrMagnitude { get { return x * x + y * y + z * z; } }
+
+
+ // Returns a vector that is made from the smallest components of two vectors.
+ CSRAW public static Vector3 Min (Vector3 lhs, Vector3 rhs) { return new Vector3 (Mathf.Min(lhs.x,rhs.x), Mathf.Min(lhs.y,rhs.y), Mathf.Min(lhs.z,rhs.z)); }
+
+ // Returns a vector that is made from the largest components of two vectors.
+ CSRAW public static Vector3 Max (Vector3 lhs, Vector3 rhs) { return new Vector3 (Mathf.Max(lhs.x,rhs.x), Mathf.Max(lhs.y,rhs.y), Mathf.Max(lhs.z,rhs.z)); }
+
+ // Shorthand for writing @@Vector3(0, 0, 0)@@
+ CSRAW public static Vector3 zero { get { return new Vector3 (0F, 0F, 0F); } }
+ // Shorthand for writing @@Vector3(1, 1, 1)@@
+ CSRAW public static Vector3 one { get { return new Vector3 (1F, 1F, 1F); } }
+ // Shorthand for writing @@Vector3(0, 0, 1)@@
+ CSRAW public static Vector3 forward { get { return new Vector3 (0F, 0F, 1F); } }
+ OBSOLETE planned Use -Vector3.forward
+ CSRAW public static Vector3 back { get { return new Vector3 (0F, 0F, -1F); } }
+ // Shorthand for writing @@Vector3(0, 1, 0)@@
+ CSRAW public static Vector3 up { get { return new Vector3 (0F, 1F, 0F); } }
+ OBSOLETE planned Use -Vector3.up
+ CSRAW public static Vector3 down { get { return new Vector3 (0F, -1F, 0F); } }
+ OBSOLETE planned Use -Vector3.right
+ CSRAW public static Vector3 left { get { return new Vector3 (-1F, 0F, 0F); } }
+ // Shorthand for writing @@Vector3(1, 0, 0)@@
+ CSRAW public static Vector3 right { get { return new Vector3 (1F, 0F, 0F); } }
+
+
+ // Adds two vectors.
+ CSRAW public static Vector3 operator + (Vector3 a, Vector3 b) { return new Vector3 (a.x+b.x, a.y+b.y, a.z+b.z); }
+ // Subtracts one vector from another.
+ CSRAW public static Vector3 operator - (Vector3 a, Vector3 b) { return new Vector3 (a.x-b.x, a.y-b.y, a.z-b.z); }
+ // Negates a vector.
+ CSRAW public static Vector3 operator - (Vector3 a) { return new Vector3 (-a.x, -a.y, -a.z); }
+ // Multiplies a vector by a number.
+ CSRAW public static Vector3 operator * (Vector3 a, float d) { return new Vector3 (a.x*d, a.y*d, a.z*d); }
+ // Multiplies a vector by a number.
+ CSRAW public static Vector3 operator * (float d, Vector3 a) { return new Vector3 (a.x*d, a.y*d, a.z*d); }
+ // Divides a vector by a number.
+ CSRAW public static Vector3 operator / (Vector3 a, float d) { return new Vector3 (a.x/d, a.y/d, a.z/d); }
+
+ // Returns true if the vectors are equal.
+ CSRAW public static bool operator == (Vector3 lhs, Vector3 rhs)
+ {
+ return SqrMagnitude (lhs - rhs) < kEpsilon * kEpsilon;
+ }
+
+ // Returns true if vectors different.
+ CSRAW public static bool operator != (Vector3 lhs, Vector3 rhs)
+ {
+ return SqrMagnitude (lhs - rhs) >= kEpsilon * kEpsilon;
+ }
+
+ OBSOLETE warning Use Vector3.forward instead.
+ CSRAW public static Vector3 fwd { get { return new Vector3 (0F, 0F, 1F); } }
+
+ OBSOLETE warning Use Vector3.Angle instead. AngleBetween uses radians instead of degrees and was deprecated for this reason
+ CSRAW public static float AngleBetween(Vector3 from, Vector3 to) { return Mathf.Acos(Mathf.Clamp (Vector3.Dot (from.normalized, to.normalized), -1F, 1F)); }
+END
+
+
+// Representation of RGBA colors.
+
+THREAD_SAFE
+STRUCT Color
+ // Red component of the color.
+ CSRAW public float r;
+
+ // Green component of the color.
+ CSRAW public float g;
+
+ // Blue component of the color.
+ CSRAW public float b;
+
+ // Alpha component of the color.
+ CSRAW public float a;
+
+
+ // Constructs a new Color with given r,g,b,a components.
+ CSRAW public Color (float r, float g, float b, float a)
+ {
+ this.r = r; this.g = g; this.b = b; this.a = a;
+ }
+
+ // Constructs a new Color with given r,g,b components and sets /a/ to 1.
+ CSRAW public Color (float r, float g, float b)
+ {
+ this.r = r; this.g = g; this.b = b; this.a = 1.0F;
+ }
+
+ /// *listonly*
+ CSRAW override public string ToString() {
+ return UnityString.Format("RGBA({0:F3}, {1:F3}, {2:F3}, {3:F3})", r, g, b, a);
+ }
+ // Returns a nicely formatted string of this color.
+ CSRAW public string ToString(string format) {
+ return UnityString.Format("RGBA({0}, {1}, {2}, {3})", r.ToString(format), g.ToString(format), b.ToString(format), a.ToString(format));
+ }
+
+ // used to allow Colors to be used as keys in hash tables
+ public override int GetHashCode() {
+ return ((Vector4)this).GetHashCode();
+ }
+
+ // also required for being able to use Colors as keys in hash tables
+ public override bool Equals(object other) {
+ if (!(other is Color)) return false;
+ Color rhs = (Color)other;
+ return r.Equals(rhs.r) && g.Equals(rhs.g) && b.Equals(rhs.b) && a.Equals(rhs.a);
+ }
+
+ // Adds two colors together. Each component is added separately.
+ CSRAW public static Color operator + (Color a, Color b) { return new Color (a.r+b.r, a.g+b.g, a.b+b.b, a.a+b.a); }
+
+ // Subtracts color /b/ from color /a/. Each component is subtracted separately.
+ CSRAW public static Color operator - (Color a, Color b) { return new Color (a.r-b.r, a.g-b.g, a.b-b.b, a.a-b.a); }
+
+ // Multiplies two colors together. Each component is multiplied separately.
+ CSRAW public static Color operator * (Color a, Color b) { return new Color (a.r*b.r, a.g*b.g, a.b*b.b, a.a*b.a); }
+
+ // Multiplies color /a/ by the float /b/. Each color component is scaled separately.
+ CSRAW public static Color operator * (Color a, float b) { return new Color (a.r*b, a.g*b, a.b*b, a.a*b); }
+
+ // Multiplies color /a/ by the float /b/. Each color component is scaled separately.
+ CSRAW public static Color operator * (float b, Color a) { return new Color (a.r*b, a.g*b, a.b*b, a.a*b); }
+
+ // Divides color /a/ by the float /b/. Each color component is scaled separately.
+ CSRAW public static Color operator / (Color a, float b) { return new Color (a.r/b, a.g/b, a.b/b, a.a/b); }
+
+ //*undoc*
+ CSRAW public static bool operator == (Color lhs, Color rhs)
+ {
+ return ((Vector4)lhs == (Vector4)rhs);
+ }
+ //*undoc*
+ CSRAW public static bool operator != (Color lhs, Color rhs)
+ {
+ return ((Vector4)lhs != (Vector4)rhs);
+ }
+
+ // Interpolates between colors /a/ and /b/ by /t/.
+ CSRAW public static Color Lerp (Color a, Color b, float t)
+ {
+ t = Mathf.Clamp01 (t);
+ return new Color(
+ a.r + (b.r - a.r)*t,
+ a.g + (b.g - a.g)*t,
+ a.b + (b.b - a.b)*t,
+ a.a + (b.a - a.a)*t
+ );
+ }
+
+ // Returns new color that has RGB components multiplied, but leaving alpha untouched.
+ CSRAW internal Color RGBMultiplied (float multiplier) { return new Color (r * multiplier, g * multiplier, b * multiplier, a); }
+ // Returns new color that has RGB components multiplied, but leaving alpha untouched.
+ CSRAW internal Color AlphaMultiplied (float multiplier) { return new Color (r,g,b,a * multiplier); }
+ // Returns new color that has RGB components multiplied, but leaving alpha untouched.
+ CSRAW internal Color RGBMultiplied (Color multiplier) { return new Color (r * multiplier.r, g * multiplier.g, b * multiplier.b, a); }
+
+ // Solid red. RGBA is (1, 0, 0, 1).
+ CSRAW public static Color red { get { return new Color (1F, 0F, 0F, 1F); } }
+ // Solid green. RGBA is (0, 1, 0, 1).
+ CSRAW public static Color green { get { return new Color (0F, 1F, 0F, 1F); } }
+ // Solid blue. RGBA is (0, 0, 1, 1).
+ CSRAW public static Color blue { get { return new Color (0F, 0F, 1F, 1F); } }
+ // Solid white. RGBA is (1, 1, 1, 1).
+ CSRAW public static Color white { get { return new Color (1F, 1F, 1F, 1F); } }
+ // Solid black. RGBA is (0, 0, 0, 1).
+ CSRAW public static Color black { get { return new Color (0F, 0F, 0F, 1F); } }
+ // Yellow. RGBA is (1, 0.92, 0.016, 1), but the color is nice to look at!
+ CSRAW public static Color yellow { get { return new Color (1F, 235F / 255F, 4F / 255F, 1F); } }
+ // Cyan. RGBA is (0, 1, 1, 1).
+ CSRAW public static Color cyan { get { return new Color (0F, 1F, 1F, 1F); } }
+ // Magenta. RGBA is (1, 0, 1, 1).
+ CSRAW public static Color magenta { get { return new Color (1F, 0F, 1F, 1F); } }
+ // Gray. RGBA is (0.5, 0.5, 0.5, 1).
+ CSRAW public static Color gray { get { return new Color (.5F, .5F, .5F, 1F); } }
+ // English spelling for ::ref::gray. RGBA is the same (0.5, 0.5, 0.5, 1).
+ CSRAW public static Color grey { get { return new Color (.5F, .5F, .5F, 1F); } }
+ // Completely transparent. RGBA is (0, 0, 0, 0).
+ CSRAW public static Color clear { get { return new Color (0F, 0F, 0F, 0F); } }
+
+ // The grayscale value of the color (RO)
+ CSRAW public float grayscale { get { return 0.299F * r + 0.587F * g + 0.114F * b; } }
+
+ // A version of the color that has had the inverse gamma curve applied
+ CSRAW public Color linear
+ {
+ get {
+ return new Color (Mathf.GammaToLinearSpace(r), Mathf.GammaToLinearSpace(g), Mathf.GammaToLinearSpace(b), a);
+ }
+ }
+
+ // A version of the color that has had the gamma curve applied
+ CSRAW public Color gamma
+ {
+ get {
+ return new Color (Mathf.LinearToGammaSpace(r), Mathf.LinearToGammaSpace(g), Mathf.LinearToGammaSpace(b), a);
+ }
+ }
+
+ // Colors can be implicitly converted to and from [[Vector4]].
+ CSRAW public static implicit operator Vector4(Color c) {
+ return new Vector4(c.r, c.g, c.b, c.a);
+ }
+ // Colors can be implicitly converted to and from [[Vector4]].
+ CSRAW public static implicit operator Color(Vector4 v) {
+ return new Color(v.x, v.y, v.z, v.w);
+ }
+
+ // Access the r, g, b,a components using [0], [1], [2], [3] respectively.
+ CSRAW public float this [int index]
+ {
+ get
+ {
+ switch(index)
+ {
+ case 0: return r;
+ case 1: return g;
+ case 2: return b;
+ case 3: return a;
+ default:
+ throw new IndexOutOfRangeException("Invalid Vector3 index!");
+ }
+ }
+
+ set
+ {
+ switch(index)
+ {
+ case 0: r = value; break;
+ case 1: g = value; break;
+ case 2: b = value; break;
+ case 3: a = value; break;
+ default:
+ throw new IndexOutOfRangeException("Invalid Vector3 index!");
+ }
+ }
+ }
+
+
+END
+
+
+// Representation of RGBA colors in 32 bit format
+THREAD_SAFE
+STRUCT Color32
+ // Red component of the color.
+ CSRAW public byte r;
+
+ // Green component of the color.
+ CSRAW public byte g;
+
+ // Blue component of the color.
+ CSRAW public byte b;
+
+ // Alpha component of the color.
+ CSRAW public byte a;
+
+ // Constructs a new Color with given r, g, b, a components.
+ CSRAW public Color32(byte r, byte g, byte b, byte a) {
+ this.r = r; this.g = g; this.b = b; this.a = a;
+ }
+
+ // Color32 can be implicitly converted to and from [[Color]].
+ CSRAW public static implicit operator Color32(Color c) {
+ return new Color32((byte)(Mathf.Clamp01(c.r) * 255), (byte)(Mathf.Clamp01(c.g) * 255), (byte)(Mathf.Clamp01(c.b) * 255), (byte)(Mathf.Clamp01(c.a) * 255));
+ }
+
+ // Color32 can be implicitly converted to and from [[Color]].
+ CSRAW public static implicit operator Color (Color32 c) {
+ return new Color(c.r / 255f, c.g / 255f, c.b / 255f, c.a / 255f);
+ }
+
+ /// *listonly*
+ CSRAW override public string ToString() {
+ return UnityString.Format("RGBA({0}, {1}, {2}, {3})", r, g, b, a);
+ }
+ // Returns a nicely formatted string of this color.
+ CSRAW public string ToString(string format) {
+ return UnityString.Format("RGBA({0}, {1}, {2}, {3})", r.ToString(format), g.ToString(format), b.ToString(format), a.ToString(format));
+ }
+
+ // Interpolates between colors /a/ and /b/ by /t/.
+ CSRAW public static Color32 Lerp (Color32 a, Color32 b, float t)
+ {
+ t = Mathf.Clamp01 (t);
+ return new Color32(
+ (byte)(a.r + (b.r - a.r)*t),
+ (byte)(a.g + (b.g - a.g)*t),
+ (byte)(a.b + (b.b - a.b)*t),
+ (byte)(a.a + (b.a - a.a)*t)
+ );
+ }
+END
+
+
+// Quaternions are used to represent rotations.
+
+CSRAW
+THREAD_SAFE
+STRUCT Quaternion
+
+ // X component of the Quaternion. Don't modify this directly unless you know quaternions inside out.
+ CSRAW public float x;
+ // Y component of the Quaternion. Don't modify this directly unless you know quaternions inside out.
+ CSRAW public float y;
+ // Z component of the Quaternion. Don't modify this directly unless you know quaternions inside out.
+ CSRAW public float z;
+ // W component of the Quaternion. Don't modify this directly unless you know quaternions inside out.
+ CSRAW public float w;
+
+ // Access the x, y, z, w components using [0], [1], [2], [3] respectively.
+ CSRAW public float this [int index]
+ {
+ get
+ {
+ switch(index)
+ {
+ case 0: return x;
+ case 1: return y;
+ case 2: return z;
+ case 3: return w;
+ default:
+ throw new IndexOutOfRangeException("Invalid Quaternion index!");
+ }
+ }
+
+ set
+ {
+ switch(index)
+ {
+ case 0: x = value; break;
+ case 1: y = value; break;
+ case 2: z = value; break;
+ case 3: w = value; break;
+ default:
+ throw new IndexOutOfRangeException("Invalid Quaternion index!");
+ }
+ }
+ }
+
+
+ // Constructs new Quaternion with given x,y,z,w components.
+ CSRAW public Quaternion (float x, float y, float z, float w) { this.x = x; this.y = y; this.z = z; this.w = w; }
+
+ // Set x, y, z and w components of an existing Quaternion.
+ CSRAW public void Set (float new_x, float new_y, float new_z, float new_w) { x = new_x; y = new_y; z = new_z; w = new_w; }
+
+ // The identity rotation (RO). This quaternion corresponds to "no rotation": the object
+ CSRAW public static Quaternion identity { get { return new Quaternion (0F, 0F, 0F, 1F); } }
+
+ // Combines rotations /lhs/ and /rhs/.
+ CSRAW public static Quaternion operator * (Quaternion lhs, Quaternion rhs)
+ {
+ return new Quaternion (
+ lhs.w*rhs.x + lhs.x*rhs.w + lhs.y*rhs.z - lhs.z*rhs.y,
+ lhs.w*rhs.y + lhs.y*rhs.w + lhs.z*rhs.x - lhs.x*rhs.z,
+ lhs.w*rhs.z + lhs.z*rhs.w + lhs.x*rhs.y - lhs.y*rhs.x,
+ lhs.w*rhs.w - lhs.x*rhs.x - lhs.y*rhs.y - lhs.z*rhs.z);
+ }
+
+ // Rotates the point /point/ with /rotation/.
+ CSRAW public static Vector3 operator * (Quaternion rotation, Vector3 point)
+ {
+ float x = rotation.x * 2F;
+ float y = rotation.y * 2F;
+ float z = rotation.z * 2F;
+ float xx = rotation.x * x;
+ float yy = rotation.y * y;
+ float zz = rotation.z * z;
+ float xy = rotation.x * y;
+ float xz = rotation.x * z;
+ float yz = rotation.y * z;
+ float wx = rotation.w * x;
+ float wy = rotation.w * y;
+ float wz = rotation.w * z;
+
+ Vector3 res;
+ res.x = (1F - (yy + zz)) * point.x + (xy - wz) * point.y + (xz + wy) * point.z;
+ res.y = (xy + wz) * point.x + (1F - (xx + zz)) * point.y + (yz - wx) * point.z;
+ res.z = (xz - wy) * point.x + (yz + wx) * point.y + (1F - (xx + yy)) * point.z;
+ return res;
+ }
+
+ // *undocumented*
+ CSRAW public const float kEpsilon = 0.000001F;
+
+ // Are two quaternions equal to each other?
+ CSRAW public static bool operator == (Quaternion lhs, Quaternion rhs)
+ {
+ return Dot (lhs, rhs) > 1.0f-kEpsilon;
+ }
+
+ // Are two quaternions different from each other?
+ CSRAW public static bool operator != (Quaternion lhs, Quaternion rhs)
+ {
+ return Dot (lhs, rhs) <= 1.0f-kEpsilon;
+ }
+
+ // The dot product between two rotations.
+ CSRAW public static float Dot (Quaternion a, Quaternion b) { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; }
+
+ // Creates a rotation which rotates /angle/ degrees around /axis/.
+ CUSTOM static Quaternion AngleAxis (float angle, Vector3 axis) { return AxisAngleToQuaternionSafe (axis, Deg2Rad(angle)); }
+
+ // Converts a rotation to angle-axis representation.
+ CSRAW public void ToAngleAxis (out float angle, out Vector3 axis) { Internal_ToAxisAngleRad (this, out axis, out angle); angle *= Mathf.Rad2Deg; }
+
+ // Creates a rotation which rotates from /fromDirection/ to /toDirection/.
+ CUSTOM static Quaternion FromToRotation (Vector3 fromDirection, Vector3 toDirection) { return FromToQuaternionSafe (fromDirection, toDirection); }
+
+ // Creates a rotation which rotates from /fromDirection/ to /toDirection/.
+ CSRAW public void SetFromToRotation (Vector3 fromDirection, Vector3 toDirection) { this = FromToRotation (fromDirection, toDirection); }
+
+ // Creates a rotation with the specified /forward/ and /upwards/ directions.
+ CUSTOM static Quaternion LookRotation (Vector3 forward, Vector3 upwards = Vector3.up)
+ {
+ Quaternionf q = Quaternionf::identity ();
+ if (!LookRotationToQuaternion (forward, upwards, &q))
+ {
+ float mag = Magnitude (forward);
+ if (mag > Vector3f::epsilon)
+ {
+ Matrix3x3f m;
+ m.SetFromToRotation (Vector3f::zAxis, forward / mag);
+ MatrixToQuaternion (m, q);
+ }
+ else
+ {
+ LogString ("Look rotation viewing vector is zero");
+ }
+ }
+ return q;
+ }
+
+ // Creates a rotation with the specified /forward/ and /upwards/ directions.
+ CSRAW public void SetLookRotation (Vector3 view, Vector3 up = Vector3.up) { this = LookRotation (view, up); }
+
+ // Spherically interpolates between /from/ and /to/ by t.
+ CUSTOM static Quaternion Slerp (Quaternion from, Quaternion to, float t) { return Slerp (from, to, clamp01 (t)); }
+
+ // Interpolates between /from/ and /to/ by /t/ and normalizes the result afterwards.
+ CUSTOM static Quaternion Lerp (Quaternion from, Quaternion to, float t) { return Lerp (from, to, clamp01 (t)); }
+
+ // Rotates a rotation /from/ towards /to/.
+ CSRAW public static Quaternion RotateTowards (Quaternion from, Quaternion to, float maxDegreesDelta)
+ {
+ float angle = Quaternion.Angle(from, to);
+ if (angle == 0.0f)
+ return to;
+ float slerpValue = Mathf.Min(1.0f, maxDegreesDelta / angle);
+ return UnclampedSlerp(from, to, slerpValue);
+ }
+
+ CUSTOM private static Quaternion UnclampedSlerp (Quaternion from, Quaternion to, float t) { return Slerp (from, to, t); }
+
+ // Returns the Inverse of /rotation/.
+ CUSTOM static Quaternion Inverse (Quaternion rotation) { return Inverse (rotation); }
+
+
+ /// *listonly*
+ CSRAW override public string ToString() {
+ return UnityString.Format("({0:F1}, {1:F1}, {2:F1}, {3:F1})", x, y, z, w);
+ }
+ // Returns a nicely formatted string of the Quaternion
+ CSRAW public string ToString(string format) {
+ return UnityString.Format("({0}, {1}, {2}, {3})", x.ToString(format), y.ToString(format), z.ToString(format), w.ToString(format));
+ }
+
+ // Returns the angle in degrees between two rotations /a/ and /b/.
+ CSRAW static public float Angle (Quaternion a, Quaternion b)
+ {
+ float dot = Dot(a, b);
+ return Mathf.Acos(Mathf.Min(Mathf.Abs(dot), 1.0F)) * 2.0F * Mathf.Rad2Deg;
+ }
+
+ // Returns the euler angle representation of the rotation.
+ CSRAW public Vector3 eulerAngles { get { return Internal_ToEulerRad(this) * Mathf.Rad2Deg; } set { this=Internal_FromEulerRad(value * Mathf.Deg2Rad); } }
+
+ // Returns a rotation that rotates z degrees around the z axis, x degrees around the x axis, and y degrees around the y axis (in that order).
+ CSRAW static public Quaternion Euler (float x, float y, float z) { return Internal_FromEulerRad (new Vector3 (x, y, z) * Mathf.Deg2Rad); }
+
+ // Returns a rotation that rotates z degrees around the z axis, x degrees around the x axis, and y degrees around the y axis (in that order).
+ CSRAW static public Quaternion Euler (Vector3 euler) { return Internal_FromEulerRad (euler * Mathf.Deg2Rad); }
+
+
+ // Internal implementation. Note Rad suffix that indicates that this method works in radians.
+ CUSTOM static private Vector3 Internal_ToEulerRad (Quaternion rotation)
+ {
+ Quaternionf outRotation = NormalizeSafe (rotation);
+ return QuaternionToEuler (outRotation);
+ }
+
+ // Internal implementation. Note Rad suffix that indicates that this method works in radians.
+ CUSTOM static private Quaternion Internal_FromEulerRad (Vector3 euler) {
+ return EulerToQuaternion (euler);
+ }
+
+ // Internal implementation. Note Rad suffix that indicates that this method works in radians.
+ CUSTOM private static void Internal_ToAxisAngleRad (Quaternion q, out Vector3 axis, out float angle) {
+ QuaternionToAxisAngle (NormalizeSafe (q), axis, angle);
+ }
+
+
+ // Old obsolete radians-based Euler functions:
+ OBSOLETE warning Use Quaternion.Euler instead. This function was deprecated because it uses radians instead of degrees
+ CSRAW static public Quaternion EulerRotation (float x, float y, float z) { return Internal_FromEulerRad (new Vector3 (x, y, z)); }
+ OBSOLETE warning Use Quaternion.Euler instead. This function was deprecated because it uses radians instead of degrees
+ CSRAW public static Quaternion EulerRotation (Vector3 euler) { return Internal_FromEulerRad (euler); }
+ OBSOLETE warning Use Quaternion.Euler instead. This function was deprecated because it uses radians instead of degrees
+ CSRAW public void SetEulerRotation (float x, float y, float z) { this = Internal_FromEulerRad (new Vector3 (x, y, z)); }
+ OBSOLETE warning Use Quaternion.Euler instead. This function was deprecated because it uses radians instead of degrees
+ CSRAW public void SetEulerRotation (Vector3 euler) { this = Internal_FromEulerRad (euler); }
+ OBSOLETE warning Use Quaternion.eulerAngles instead. This function was deprecated because it uses radians instead of degrees
+ CSRAW public Vector3 ToEuler () { return Internal_ToEulerRad (this); }
+
+ OBSOLETE warning Use Quaternion.Euler instead. This function was deprecated because it uses radians instead of degrees
+ CSRAW static public Quaternion EulerAngles (float x, float y, float z) { return Internal_FromEulerRad (new Vector3 (x, y, z)); }
+ OBSOLETE warning Use Quaternion.Euler instead. This function was deprecated because it uses radians instead of degrees
+ CSRAW public static Quaternion EulerAngles (Vector3 euler) { return Internal_FromEulerRad (euler); }
+
+ OBSOLETE warning Use Quaternion.ToAngleAxis instead. This function was deprecated because it uses radians instead of degrees
+ CSRAW public void ToAxisAngle (out Vector3 axis, out float angle) { Internal_ToAxisAngleRad (this, out axis, out angle); }
+
+ OBSOLETE warning Use Quaternion.Euler instead. This function was deprecated because it uses radians instead of degrees
+ CSRAW public void SetEulerAngles (float x, float y, float z) { SetEulerRotation (new Vector3 (x, y, z)); }
+ OBSOLETE warning Use Quaternion.Euler instead. This function was deprecated because it uses radians instead of degrees
+ CSRAW public void SetEulerAngles (Vector3 euler) { this = EulerRotation (euler); }
+ OBSOLETE warning Use Quaternion.eulerAngles instead. This function was deprecated because it uses radians instead of degrees
+ CSRAW public static Vector3 ToEulerAngles (Quaternion rotation) { return Quaternion.Internal_ToEulerRad (rotation); }
+ OBSOLETE warning Use Quaternion.eulerAngles instead. This function was deprecated because it uses radians instead of degrees
+ CSRAW public Vector3 ToEulerAngles () { return Quaternion.Internal_ToEulerRad (this); }
+
+ OBSOLETE warning Use Quaternion.AngleAxis instead. This function was deprecated because it uses radians instead of degrees
+ CUSTOM static Quaternion AxisAngle (Vector3 axis, float angle) { return AxisAngleToQuaternionSafe (axis, angle); }
+
+ OBSOLETE warning Use Quaternion.AngleAxis instead. This function was deprecated because it uses radians instead of degrees
+ CSRAW public void SetAxisAngle (Vector3 axis, float angle) { this = AxisAngle (axis, angle); }
+
+ // used to allow Quaternions to be used as keys in hash tables
+ CSRAW public override int GetHashCode() {
+ return x.GetHashCode() ^ (y.GetHashCode()<<2) ^ (z.GetHashCode()>>2) ^ (w.GetHashCode()>>1);
+ }
+
+ // also required for being able to use Quaternions as keys in hash tables
+ public override bool Equals(object other) {
+ if(!(other is Quaternion)) return false;
+
+ Quaternion rhs=(Quaternion)other;
+ return x.Equals(rhs.x) && y.Equals(rhs.y) && z.Equals(rhs.z) && w.Equals(rhs.w);
+ }
+
+END
+
+
+// A 2D Rectangle defined by x, y position and width, height
+THREAD_SAFE
+STRUCT Rect
+ CSRAW private float m_XMin, m_YMin, m_Width, m_Height;
+
+ // Creates a new rectangle.
+ CSRAW public Rect (float left, float top, float width, float height) {
+ m_XMin = left;
+ m_YMin = top;
+ m_Width = width;
+ m_Height = height;
+ }
+
+ //*undocumented*
+ CSRAW public Rect (Rect source) {
+ m_XMin = source.m_XMin;
+ m_YMin = source.m_YMin;
+ m_Width = source.m_Width;
+ m_Height= source.m_Height;
+ }
+
+ // Creates a rectangle from min/max coordinate values.
+ static public Rect MinMaxRect (float left, float top, float right, float bottom)
+ {
+ return new Rect (left, top, right - left, bottom - top);
+ }
+
+ // Set components of an existing Rect.
+ CSRAW public void Set (float left, float top, float width, float height) {
+ m_XMin = left;
+ m_YMin = top;
+ m_Width = width;
+ m_Height = height;
+ }
+
+ // Left coordinate of the rectangle.
+ CSRAW public float x { get { return m_XMin; } set { m_XMin = value; } }
+
+ // Top coordinate of the rectangle.
+ CSRAW public float y { get { return m_YMin; } set { m_YMin = value; } }
+
+ // Center coordinate of the rectangle.
+ CSRAW public Vector2 center { get { return new Vector2 (x + m_Width / 2f, y + m_Height / 2f); } set { m_XMin = value.x - m_Width / 2f; m_YMin = value.y - m_Height / 2f; } }
+
+ // Width of the rectangle.
+ CSRAW public float width { get { return m_Width; } set { m_Width = value; } }
+
+ // Height of the rectangle.
+ CSRAW public float height { get { return m_Height; } set { m_Height = value; } }
+
+ OBSOLETE warning use xMin
+ CSRAW public float left { get { return m_XMin; } }
+ OBSOLETE warning use xMax
+ CSRAW public float right { get { return m_XMin + m_Width; } }
+ OBSOLETE warning use yMin
+ CSRAW public float top { get { return m_YMin; } }
+ OBSOLETE warning use yMax
+ CSRAW public float bottom { get { return m_YMin + m_Height; } }
+
+ // Left coordinate of the rectangle.
+ CSRAW public float xMin { get { return m_XMin; } set { float oldxmax = xMax; m_XMin = value; m_Width = oldxmax - m_XMin; } }
+ // Top coordinate of the rectangle.
+ CSRAW public float yMin { get { return m_YMin; } set { float oldymax = yMax; m_YMin = value; m_Height = oldymax - m_YMin; } }
+ // Right coordinate of the rectangle.
+ CSRAW public float xMax { get { return m_Width + m_XMin; } set { m_Width = value - m_XMin; } }
+ // Bottom coordinate of the rectangle.
+ CSRAW public float yMax { get { return m_Height + m_YMin; } set { m_Height = value - m_YMin; } }
+
+ /// *listonly*
+ CSRAW override public string ToString() { return UnityString.Format("(x:{0:F2}, y:{1:F2}, width:{2:F2}, height:{3:F2})", x, y, width, height); }
+ // Returns a nicely formatted string for this Rect.
+ CSRAW public string ToString(string format) {
+ return UnityString.Format("(x:{0}, y:{1}, width:{2}, height:{3})", x.ToString(format), y.ToString(format), width.ToString(format), height.ToString(format));
+ }
+
+ /// *listonly*
+ CSRAW public bool Contains (Vector2 point)
+ {
+ return (point.x >= xMin) && (point.x < xMax) && (point.y >= yMin) && (point.y < yMax);
+ }
+ // Returns true if the /x/ and /y/ components of /point/ is a point inside this rectangle.
+ public bool Contains (Vector3 point)
+ {
+ return (point.x >= xMin) && (point.x < xMax) && (point.y >= yMin) && (point.y < yMax);
+ }
+ public bool Contains(Vector3 point, bool allowInverse)
+ {
+ if (!allowInverse)
+ {
+ return Contains(point);
+ }
+ bool xAxis = false;
+ if (width < 0f && (point.x <= xMin) && (point.x > xMax) || width >= 0f && (point.x >= xMin) && (point.x < xMax))
+ xAxis = true;
+ if (xAxis && (height < 0f && (point.y <= yMin) && (point.y > yMax) || height >= 0f && (point.y >= yMin) && (point.y < yMax)))
+ return true;
+ return false;
+ }
+ // removed for 2.0
+ // Clamp a point to be within a rectangle.
+// CSRAW public Vector2 Clamp (Vector2 point) {
+// return new Vector2 (Mathf.Clamp (point.x, left, xMax-1), Mathf.Clamp (point.y, yMin, yMax-1));
+// }
+
+ // Swaps min and max if min was greater than max.
+ private static Rect OrderMinMax (Rect rect)
+ {
+ if (rect.xMin > rect.xMax)
+ {
+ float temp = rect.xMin;
+ rect.xMin = rect.xMax;
+ rect.xMax = temp;
+ }
+ if (rect.yMin > rect.yMax)
+ {
+ float temp = rect.yMin;
+ rect.yMin = rect.yMax;
+ rect.yMax = temp;
+ }
+ return rect;
+ }
+
+ CSRAW public bool Overlaps (Rect other)
+ {
+ return (other.xMax > xMin &&
+ other.xMin < xMax &&
+ other.yMax > yMin &&
+ other.yMin < yMax);
+ }
+
+ CSRAW public bool Overlaps (Rect other, bool allowInverse)
+ {
+ Rect self = this;
+ if (allowInverse)
+ {
+ self = OrderMinMax (self);
+ other = OrderMinMax (other);
+ }
+ return self.Overlaps (other);
+ }
+
+ // Returns true if the rectangles are different.
+ CSRAW public static bool operator != (Rect lhs, Rect rhs)
+ {
+ return lhs.x != rhs.x || lhs.y != rhs.y || lhs.width != rhs.width || lhs.height != rhs.height;
+ }
+
+ // Returns true if the rectangles are the same.
+ CSRAW public static bool operator == (Rect lhs, Rect rhs) {
+ return lhs.x == rhs.x && lhs.y == rhs.y && lhs.width == rhs.width && lhs.height == rhs.height;
+ }
+
+ public override int GetHashCode() {
+ return x.GetHashCode() ^ (width.GetHashCode()<<2) ^ (y.GetHashCode()>>2) ^ (height.GetHashCode()>>1);
+ }
+
+ public override bool Equals(object other) {
+ if(!(other is Rect)) return false;
+
+ Rect rhs=(Rect)other;
+ return x.Equals(rhs.x) && y.Equals(rhs.y) && width.Equals(rhs.width) && height.Equals(rhs.height);
+ }
+END
+
+
+// A standard 4x4 transformation matrix.
+THREAD_SAFE
+STRUCT Matrix4x4
+ CSRAW
+ ///*undocumented*
+ public float m00;
+ ///*undocumented*
+ public float m10;
+ ///*undocumented*
+ public float m20;
+ ///*undocumented*
+ public float m30;
+
+ ///*undocumented*
+ public float m01;
+ ///*undocumented*
+ public float m11;
+ ///*undocumented*
+ public float m21;
+ ///*undocumented*
+ public float m31;
+
+ ///*undocumented*
+ public float m02;
+ ///*undocumented*
+ public float m12;
+ ///*undocumented*
+ public float m22;
+ ///*undocumented*
+ public float m32;
+
+ ///*undocumented*
+ public float m03;
+ ///*undocumented*
+ public float m13;
+ ///*undocumented*
+ public float m23;
+ ///*undocumented*
+ public float m33;
+
+
+ CSRAW
+ // Access element at [row, column].
+ public float this [int row, int column]
+ {
+ get
+ {
+ return this [ row + column*4 ];
+ }
+
+ set
+ {
+ this [ row + column*4 ] = value;
+ }
+ }
+
+ // Access element at sequential index (0..15 inclusive).
+ CSRAW public float this [int index]
+ {
+ get
+ {
+ switch(index)
+ {
+ case 0: return m00;
+ case 1: return m10;
+ case 2: return m20;
+ case 3: return m30;
+ case 4: return m01;
+ case 5: return m11;
+ case 6: return m21;
+ case 7: return m31;
+ case 8: return m02;
+ case 9: return m12;
+ case 10:return m22;
+ case 11:return m32;
+ case 12:return m03;
+ case 13:return m13;
+ case 14:return m23;
+ case 15:return m33;
+ default:
+ throw new IndexOutOfRangeException("Invalid matrix index!");
+ }
+ }
+
+ set
+ {
+ switch(index)
+ {
+ case 0: m00 = value; break;
+ case 1: m10 = value; break;
+ case 2: m20 = value; break;
+ case 3: m30 = value; break;
+ case 4: m01 = value; break;
+ case 5: m11 = value; break;
+ case 6: m21 = value; break;
+ case 7: m31 = value; break;
+ case 8: m02 = value; break;
+ case 9: m12 = value; break;
+ case 10:m22 = value; break;
+ case 11:m32 = value; break;
+ case 12:m03 = value; break;
+ case 13:m13 = value; break;
+ case 14:m23 = value; break;
+ case 15:m33 = value; break;
+
+ default:
+ throw new IndexOutOfRangeException("Invalid matrix index!");
+ }
+ }
+ }
+
+ // used to allow Matrix4x4s to be used as keys in hash tables
+ public override int GetHashCode() {
+ return GetColumn( 0 ).GetHashCode() ^ (GetColumn( 1 ).GetHashCode()<<2) ^ (GetColumn( 2 ).GetHashCode()>>2) ^ (GetColumn( 3 ).GetHashCode()>>1);
+ }
+
+ // also required for being able to use Matrix4x4s as keys in hash tables
+ public override bool Equals(object other) {
+ if(!(other is Matrix4x4)) return false;
+
+ Matrix4x4 rhs=(Matrix4x4)other;
+ return GetColumn( 0 ).Equals(rhs.GetColumn( 0 ))
+ && GetColumn( 1 ).Equals(rhs.GetColumn( 1 ))
+ && GetColumn( 2 ).Equals(rhs.GetColumn( 2 ))
+ && GetColumn( 3 ).Equals(rhs.GetColumn( 3 ));
+ }
+
+ // Multiplies two matrices.
+ CSRAW static public Matrix4x4 operator * (Matrix4x4 lhs, Matrix4x4 rhs)
+ {
+ Matrix4x4 res = new Matrix4x4();
+ res.m00 = lhs.m00 * rhs.m00 + lhs.m01 * rhs.m10 + lhs.m02 * rhs.m20 + lhs.m03 * rhs.m30;
+ res.m01 = lhs.m00 * rhs.m01 + lhs.m01 * rhs.m11 + lhs.m02 * rhs.m21 + lhs.m03 * rhs.m31;
+ res.m02 = lhs.m00 * rhs.m02 + lhs.m01 * rhs.m12 + lhs.m02 * rhs.m22 + lhs.m03 * rhs.m32;
+ res.m03 = lhs.m00 * rhs.m03 + lhs.m01 * rhs.m13 + lhs.m02 * rhs.m23 + lhs.m03 * rhs.m33;
+
+ res.m10 = lhs.m10 * rhs.m00 + lhs.m11 * rhs.m10 + lhs.m12 * rhs.m20 + lhs.m13 * rhs.m30;
+ res.m11 = lhs.m10 * rhs.m01 + lhs.m11 * rhs.m11 + lhs.m12 * rhs.m21 + lhs.m13 * rhs.m31;
+ res.m12 = lhs.m10 * rhs.m02 + lhs.m11 * rhs.m12 + lhs.m12 * rhs.m22 + lhs.m13 * rhs.m32;
+ res.m13 = lhs.m10 * rhs.m03 + lhs.m11 * rhs.m13 + lhs.m12 * rhs.m23 + lhs.m13 * rhs.m33;
+
+ res.m20 = lhs.m20 * rhs.m00 + lhs.m21 * rhs.m10 + lhs.m22 * rhs.m20 + lhs.m23 * rhs.m30;
+ res.m21 = lhs.m20 * rhs.m01 + lhs.m21 * rhs.m11 + lhs.m22 * rhs.m21 + lhs.m23 * rhs.m31;
+ res.m22 = lhs.m20 * rhs.m02 + lhs.m21 * rhs.m12 + lhs.m22 * rhs.m22 + lhs.m23 * rhs.m32;
+ res.m23 = lhs.m20 * rhs.m03 + lhs.m21 * rhs.m13 + lhs.m22 * rhs.m23 + lhs.m23 * rhs.m33;
+
+ res.m30 = lhs.m30 * rhs.m00 + lhs.m31 * rhs.m10 + lhs.m32 * rhs.m20 + lhs.m33 * rhs.m30;
+ res.m31 = lhs.m30 * rhs.m01 + lhs.m31 * rhs.m11 + lhs.m32 * rhs.m21 + lhs.m33 * rhs.m31;
+ res.m32 = lhs.m30 * rhs.m02 + lhs.m31 * rhs.m12 + lhs.m32 * rhs.m22 + lhs.m33 * rhs.m32;
+ res.m33 = lhs.m30 * rhs.m03 + lhs.m31 * rhs.m13 + lhs.m32 * rhs.m23 + lhs.m33 * rhs.m33;
+
+ return res;
+ }
+
+ // Transforms a [[Vector4]] by a matrix.
+ CSRAW static public Vector4 operator * (Matrix4x4 lhs, Vector4 v)
+ {
+ Vector4 res;
+ res.x = lhs.m00 * v.x + lhs.m01 * v.y + lhs.m02 * v.z + lhs.m03 * v.w;
+ res.y = lhs.m10 * v.x + lhs.m11 * v.y + lhs.m12 * v.z + lhs.m13 * v.w;
+ res.z = lhs.m20 * v.x + lhs.m21 * v.y + lhs.m22 * v.z + lhs.m23 * v.w;
+ res.w = lhs.m30 * v.x + lhs.m31 * v.y + lhs.m32 * v.z + lhs.m33 * v.w;
+ return res;
+ }
+
+ //*undoc*
+ CSRAW public static bool operator == (Matrix4x4 lhs, Matrix4x4 rhs)
+ {
+ return lhs.GetColumn( 0 ) == rhs.GetColumn( 0 )
+ && lhs.GetColumn( 1 ) == rhs.GetColumn( 1 )
+ && lhs.GetColumn( 2 ) == rhs.GetColumn( 2 )
+ && lhs.GetColumn( 3 ) == rhs.GetColumn( 3 );
+ }
+ //*undoc*
+ CSRAW public static bool operator != (Matrix4x4 lhs, Matrix4x4 rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+ //*undocumented* --- have a property now
+ CUSTOM static Matrix4x4 Inverse (Matrix4x4 m) { Matrix4x4f output (m); output.Invert_Full(); return output; }
+ //*undocumented* --- have a property now
+ CUSTOM static Matrix4x4 Transpose (Matrix4x4 m) { Matrix4x4f output (m); output.Transpose(); return output; }
+
+ // Invert a matrix and return the success code.
+ CUSTOM internal static bool Invert (Matrix4x4 inMatrix, out Matrix4x4 dest) { return Matrix4x4f::Invert_Full(inMatrix, *dest); }
+
+ // The inverse of this matrix (RO).
+ CSRAW public Matrix4x4 inverse { get { return Matrix4x4.Inverse (this); } }
+
+ // Returns the transpose of this matrix (RO).
+ CSRAW public Matrix4x4 transpose { get { return Matrix4x4.Transpose (this); } }
+
+ // Is this the identity matrix?
+ CUSTOM_PROP bool isIdentity { return self.IsIdentity(); }
+
+ // Get a column of the matrix.
+ CSRAW public Vector4 GetColumn (int i) { return new Vector4 (this[0, i], this[1, i], this[2, i], this[3, i]); }
+ // Returns a row of the matrix.
+ CSRAW public Vector4 GetRow (int i) { return new Vector4 (this[i, 0], this[i, 1], this[i, 2], this[i, 3]); }
+ // Sets a column of the matrix.
+ CSRAW public void SetColumn (int i, Vector4 v) { this[0, i] = v.x; this[1, i] = v.y; this[2, i] = v.z; this[3, i] = v.w; }
+ // Sets a row of the matrix.
+ CSRAW public void SetRow (int i, Vector4 v){ this[i, 0] = v.x; this[i, 1] = v.y; this[i, 2] = v.z; this[i, 3] = v.w; }
+
+
+ // Transforms a position by this matrix (generic).
+ CSRAW public Vector3 MultiplyPoint (Vector3 v)
+ {
+ Vector3 res;
+ float w;
+ res.x = this.m00 * v.x + this.m01 * v.y + this.m02 * v.z + this.m03;
+ res.y = this.m10 * v.x + this.m11 * v.y + this.m12 * v.z + this.m13;
+ res.z = this.m20 * v.x + this.m21 * v.y + this.m22 * v.z + this.m23;
+ w = this.m30 * v.x + this.m31 * v.y + this.m32 * v.z + this.m33;
+
+ w = 1F / w;
+ res.x *= w;
+ res.y *= w;
+ res.z *= w;
+ return res;
+ }
+
+ // Transforms a position by this matrix (fast).
+ CSRAW public Vector3 MultiplyPoint3x4 (Vector3 v)
+ {
+ Vector3 res;
+ res.x = this.m00 * v.x + this.m01 * v.y + this.m02 * v.z + this.m03;
+ res.y = this.m10 * v.x + this.m11 * v.y + this.m12 * v.z + this.m13;
+ res.z = this.m20 * v.x + this.m21 * v.y + this.m22 * v.z + this.m23;
+ return res;
+ }
+
+ // Transforms a direction by this matrix.
+ CSRAW public Vector3 MultiplyVector (Vector3 v)
+ {
+ Vector3 res;
+ res.x = this.m00 * v.x + this.m01 * v.y + this.m02 * v.z;
+ res.y = this.m10 * v.x + this.m11 * v.y + this.m12 * v.z;
+ res.z = this.m20 * v.x + this.m21 * v.y + this.m22 * v.z;
+ return res;
+ }
+
+ // Creates a scaling matrix.
+ CSRAW static public Matrix4x4 Scale (Vector3 v)
+ {
+ Matrix4x4 m = new Matrix4x4();
+ m.m00 = v.x; m.m01 = 0F; m.m02 = 0F; m.m03 = 0F;
+ m.m10 = 0F; m.m11 = v.y; m.m12 = 0F; m.m13 = 0F;
+ m.m20 = 0F; m.m21 = 0F; m.m22 = v.z; m.m23 = 0F;
+ m.m30 = 0F; m.m31 = 0F; m.m32 = 0F; m.m33 = 1F;
+ return m;
+ }
+
+ // Returns a matrix with all elements set to zero (RO).
+ CSRAW public static Matrix4x4 zero {
+ get
+ {
+ Matrix4x4 m = new Matrix4x4();
+ m.m00 = 0F; m.m01 = 0F; m.m02 = 0F; m.m03 = 0F;
+ m.m10 = 0F; m.m11 = 0F; m.m12 = 0F; m.m13 = 0F;
+ m.m20 = 0F; m.m21 = 0F; m.m22 = 0F; m.m23 = 0F;
+ m.m30 = 0F; m.m31 = 0F; m.m32 = 0F; m.m33 = 0F;
+ return m;
+ }
+ }
+
+ // Returns the identity matrix (RO).
+ CSRAW public static Matrix4x4 identity {
+ get
+ {
+ Matrix4x4 m = new Matrix4x4();
+ m.m00 = 1F; m.m01 = 0F; m.m02 = 0F; m.m03 = 0F;
+ m.m10 = 0F; m.m11 = 1F; m.m12 = 0F; m.m13 = 0F;
+ m.m20 = 0F; m.m21 = 0F; m.m22 = 1F; m.m23 = 0F;
+ m.m30 = 0F; m.m31 = 0F; m.m32 = 0F; m.m33 = 1F;
+ return m;
+ }
+ }
+
+ // Sets this matrix to a translation, rotation and scaling matrix.
+ CSRAW public void SetTRS(Vector3 pos, Quaternion q, Vector3 s)
+ {
+ this = Matrix4x4.TRS(pos, q, s);
+ }
+
+ // Creates a translation, rotation and scaling matrix.
+ CUSTOM static Matrix4x4 TRS(Vector3 pos, Quaternion q, Vector3 s)
+ {
+ Matrix4x4f temp;
+ temp.SetTRS(pos,q,s);
+ return temp;
+ }
+
+ /// *listonly*
+ CSRAW override public string ToString() {
+ return UnityString.Format("{0:F5}\t{1:F5}\t{2:F5}\t{3:F5}\n{4:F5}\t{5:F5}\t{6:F5}\t{7:F5}\n{8:F5}\t{9:F5}\t{10:F5}\t{11:F5}\n{12:F5}\t{13:F5}\t{14:F5}\t{15:F5}\n", m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33);
+ }
+ // Returns a nicely formatted string for this matrix.
+ CSRAW public string ToString(string format) {
+ return UnityString.Format("{0}\t{1}\t{2}\t{3}\n{4}\t{5}\t{6}\t{7}\n{8}\t{9}\t{10}\t{11}\n{12}\t{13}\t{14}\t{15}\n",
+ m00.ToString(format), m01.ToString(format), m02.ToString(format), m03.ToString(format),
+ m10.ToString(format), m11.ToString(format), m12.ToString(format), m13.ToString(format),
+ m20.ToString(format), m21.ToString(format), m22.ToString(format), m23.ToString(format),
+ m30.ToString(format), m31.ToString(format), m32.ToString(format), m33.ToString(format));
+ }
+
+ // Creates an orthogonal projection matrix.
+ CUSTOM static public Matrix4x4 Ortho (float left, float right, float bottom, float top, float zNear, float zFar) {
+ Matrix4x4f m;
+ m.SetOrtho(left, right, bottom, top, zNear, zFar);
+ return m;
+ }
+
+ // Creates a perspective projection matrix.
+ CUSTOM static public Matrix4x4 Perspective (float fov, float aspect, float zNear, float zFar) {
+ Matrix4x4f m;
+ m.SetPerspective( fov, aspect, zNear, zFar );
+ return m;
+ }
+END
+
+
+// Represents an axis aligned bounding box.
+THREAD_SAFE
+STRUCT Bounds
+
+ CSRAW private Vector3 m_Center;
+ CSRAW private Vector3 m_Extents;
+
+ // Creates new Bounds with a given /center/ and total /size/. Bound ::ref::extents will be half the given size.
+ CSRAW public Bounds (Vector3 center, Vector3 size)
+ {
+ m_Center = center;
+ m_Extents = size * 0.5F;
+ }
+
+ // used to allow Bounds to be used as keys in hash tables
+ public override int GetHashCode() {
+ return center.GetHashCode() ^ (extents.GetHashCode()<<2);
+ }
+
+ // also required for being able to use Vector4s as keys in hash tables
+ public override bool Equals(object other) {
+ if(!(other is Bounds)) return false;
+
+ Bounds rhs=(Bounds)other;
+ return center.Equals(rhs.center) && extents.Equals(rhs.extents);
+ }
+
+ // The center of the bounding box.
+ CSRAW public Vector3 center { get { return m_Center; } set { m_Center = value; } }
+
+ // The total size of the box. This is always twice as large as the ::ref::extents.
+ CSRAW public Vector3 size { get { return m_Extents * 2.0F; } set { m_Extents = value * 0.5F; } }
+
+ // The extents of the box. This is always half of the ::ref::size.
+ CSRAW public Vector3 extents { get { return m_Extents; } set { m_Extents = value; } }
+
+ // The minimal point of the box. This is always equal to ''center-extents''.
+ CSRAW public Vector3 min { get { return center - extents; } set { SetMinMax (value, max); } }
+ // The maximal point of the box. This is always equal to ''center+extents''.
+ CSRAW public Vector3 max { get { return center + extents; } set { SetMinMax (min, value); } }
+
+ //*undoc*
+ CSRAW public static bool operator == (Bounds lhs, Bounds rhs)
+ {
+ return (lhs.center == rhs.center && lhs.extents == rhs.extents);
+ }
+ //*undoc*
+ CSRAW public static bool operator != (Bounds lhs, Bounds rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+ // Sets the bounds to the /min/ and /max/ value of the box.
+ CSRAW public void SetMinMax (Vector3 min, Vector3 max)
+ {
+ extents = (max - min) * 0.5F;
+ center = min + extents;
+ }
+
+ // Grows the Bounds to include the /point/.
+ CSRAW public void Encapsulate (Vector3 point)
+ {
+ SetMinMax(Vector3.Min (min, point), Vector3.Max (max, point));
+ }
+
+ // Grow the bounds to encapsulate the bounds.
+ CSRAW public void Encapsulate (Bounds bounds) {
+ Encapsulate (bounds.center - bounds.extents);
+ Encapsulate (bounds.center + bounds.extents);
+ }
+
+ // Expand the bounds by increasing its /size/ by /amount/ along each side.
+ CSRAW public void Expand (float amount) {
+ amount *= .5f;
+ extents += new Vector3 (amount, amount, amount);
+ }
+
+ // Expand the bounds by increasing its /size/ by /amount/ along each side.
+ CSRAW public void Expand (Vector3 amount) {
+ extents += amount * .5f;
+ }
+
+ // Does another bounding box intersect with this bounding box?
+ CSRAW public bool Intersects (Bounds bounds) {
+ return (min.x <= bounds.max.x) && (max.x >= bounds.min.x) &&
+ (min.y <= bounds.max.y) && (max.y >= bounds.min.y) &&
+ (min.z <= bounds.max.z) && (max.z >= bounds.min.z);
+ }
+
+ CUSTOM private static bool Internal_Contains (Bounds m, Vector3 point) { return m.IsInside (point); }
+ // Is /point/ contained in the bounding box?
+ CSRAW public bool Contains (Vector3 point) { return Internal_Contains (this, point); }
+
+ CUSTOM private static float Internal_SqrDistance (Bounds m, Vector3 point) { return CalculateSqrDistance(point, m); }
+
+ // The smallest squared distance between the point and this bounding box.
+ CSRAW public float SqrDistance (Vector3 point) { return Internal_SqrDistance(this, point); }
+
+ CUSTOM private static bool Internal_IntersectRay (ref Ray ray, ref Bounds bounds, out float distance) { return IntersectRayAABB(ray, bounds, distance); }
+
+ // Does /ray/ intersect this bounding box?
+ CSRAW public bool IntersectRay (Ray ray) { float dist; return Internal_IntersectRay (ref ray, ref this, out dist); }
+
+ // Does /ray/ intersect this bounding box?
+ CSRAW public bool IntersectRay (Ray ray, out float distance) { return Internal_IntersectRay (ref ray, ref this, out distance); }
+
+ /// *listonly*
+ CSRAW override public string ToString() {
+ return UnityString.Format("Center: {0}, Extents: {1}", m_Center, m_Extents);
+ }
+ // Returns a nicely formatted string for the bounds.
+ CSRAW public string ToString(string format) {
+ return UnityString.Format("Center: {0}, Extents: {1}", m_Center.ToString(format), m_Extents.ToString(format));
+ }
+
+
+END
+
+
+
+// Representation of four-dimensional vectors.
+THREAD_SAFE
+STRUCT Vector4
+
+ // *undocumented*
+ CSRAW public const float kEpsilon = 0.00001F;
+
+ // X component of the vector.
+ CSRAW public float x;
+ // Y component of the vector.
+ CSRAW public float y;
+ // Z component of the vector.
+ CSRAW public float z;
+ // W component of the vector.
+ CSRAW public float w;
+
+ // Access the x, y, z, w components using [0], [1], [2], [3] respectively.
+ CSRAW public float this [int index]
+ {
+ get
+ {
+ switch(index)
+ {
+ case 0: return x;
+ case 1: return y;
+ case 2: return z;
+ case 3: return w;
+ default:
+ throw new IndexOutOfRangeException("Invalid Vector4 index!");
+ }
+ }
+
+ set
+ {
+ switch(index)
+ {
+ case 0: x = value; break;
+ case 1: y = value; break;
+ case 2: z = value; break;
+ case 3: w = value; break;
+ default:
+ throw new IndexOutOfRangeException("Invalid Vector4 index!");
+ }
+ }
+ }
+
+ // Creates a new vector with given x, y, z, w components.
+ CSRAW public Vector4 (float x, float y, float z, float w) { this.x = x; this.y = y; this.z = z; this.w = w; }
+ // Creates a new vector with given x, y, z components and sets /w/ to zero.
+ CSRAW public Vector4 (float x, float y, float z) { this.x = x; this.y = y; this.z = z; this.w = 0F; }
+ // Creates a new vector with given x, y components and sets /z/ and /w/ to zero.
+ CSRAW public Vector4 (float x, float y) { this.x = x; this.y = y; this.z = 0F; this.w = 0F; }
+
+ // Set x, y, z and w components of an existing Vector4.
+ CSRAW public void Set (float new_x, float new_y, float new_z, float new_w) { x = new_x; y = new_y; z = new_z; w = new_w; }
+
+ // Linearly interpolates between two vectors.
+ CSRAW public static Vector4 Lerp (Vector4 from, Vector4 to, float t)
+ {
+ t = Mathf.Clamp01 (t);
+ return new Vector4(
+ from.x + (to.x - from.x)*t,
+ from.y + (to.y - from.y)*t,
+ from.z + (to.z - from.z)*t,
+ from.w + (to.w - from.w)*t
+ );
+ }
+
+ // Moves a point /current/ towards /target/.
+ CSRAW static public Vector4 MoveTowards (Vector4 current, Vector4 target, float maxDistanceDelta)
+ {
+ Vector4 toVector = target - current;
+ float dist = toVector.magnitude;
+ if (dist <= maxDistanceDelta || dist == 0)
+ return target;
+ return current + toVector / dist * maxDistanceDelta;
+ }
+
+ // Multiplies two vectors component-wise.
+ CSRAW public static Vector4 Scale (Vector4 a, Vector4 b) { return new Vector4 (a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w); }
+ // Multiplies every component of this vector by the same component of /scale/.
+ CSRAW public void Scale (Vector4 scale) { x *= scale.x; y *= scale.y; z *= scale.z; w *= scale.w; }
+
+
+ // used to allow Vector4s to be used as keys in hash tables
+ public override int GetHashCode() {
+ return x.GetHashCode() ^ (y.GetHashCode()<<2) ^ (z.GetHashCode()>>2) ^ (w.GetHashCode()>>1);
+ }
+
+ // also required for being able to use Vector4s as keys in hash tables
+ public override bool Equals(object other) {
+ if(!(other is Vector4)) return false;
+
+ Vector4 rhs=(Vector4)other;
+ return x.Equals(rhs.x) && y.Equals(rhs.y) && z.Equals(rhs.z) && w.Equals(rhs.w);
+ }
+
+ // *undoc* --- we have normalized property now
+ CSRAW public static Vector4 Normalize (Vector4 a)
+ {
+ float mag = Magnitude (a);
+ if (mag > kEpsilon)
+ return a / mag;
+ else
+ return zero;
+ }
+
+ // Makes this vector have a ::ref::magnitude of 1.
+ CSRAW public void Normalize ()
+ {
+ float mag = Magnitude (this);
+ if (mag > kEpsilon)
+ this = this / mag;
+ else
+ this = zero;
+ }
+
+ // Returns this vector with a ::ref::magnitude of 1 (RO).
+ CSRAW public Vector4 normalized { get { return Vector4.Normalize(this); } }
+
+
+ /// *listonly*
+ CSRAW override public string ToString() {
+ return UnityString.Format("({0:F1}, {1:F1}, {2:F1}, {3:F1})", x, y, z, w);
+ }
+ // Returns a nicely formatted string for this vector.
+ CSRAW public string ToString(string format) {
+ return UnityString.Format("({0}, {1}, {2}, {3})", x.ToString(format), y.ToString(format), z.ToString(format), w.ToString(format));
+ }
+
+
+ // Dot Product of two vectors.
+ CSRAW public static float Dot (Vector4 a, Vector4 b) { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; }
+
+ // Projects a vector onto another vector.
+ CSRAW public static Vector4 Project (Vector4 a, Vector4 b) { return b * Dot (a, b) / Dot (b, b); }
+
+ // Returns the distance between /a/ and /b/.
+ CSRAW public static float Distance (Vector4 a, Vector4 b) { return Magnitude (a-b); }
+
+ // *undoc* --- there's a property now
+ CSRAW public static float Magnitude (Vector4 a) { return Mathf.Sqrt (Dot (a, a)); }
+
+ // Returns the length of this vector (RO).
+ CSRAW public float magnitude { get { return Mathf.Sqrt (Dot (this, this)); } }
+
+ // *undoc* --- there's a property now
+ CSRAW public static float SqrMagnitude (Vector4 a) { return Vector4.Dot (a, a); }
+ // *undoc* --- there's a property now
+ CSRAW public float SqrMagnitude () { return Dot (this, this); }
+
+ // Returns the squared length of this vector (RO).
+ CSRAW public float sqrMagnitude { get { return Dot (this, this); } }
+
+
+ // Returns a vector that is made from the smallest components of two vectors.
+ CSRAW public static Vector4 Min (Vector4 lhs, Vector4 rhs) { return new Vector4 (Mathf.Min(lhs.x,rhs.x), Mathf.Min(lhs.y,rhs.y), Mathf.Min(lhs.z,rhs.z), Mathf.Min(lhs.w,rhs.w)); }
+
+ // Returns a vector that is made from the largest components of two vectors.
+ CSRAW public static Vector4 Max (Vector4 lhs, Vector4 rhs) { return new Vector4 (Mathf.Max(lhs.x,rhs.x), Mathf.Max(lhs.y,rhs.y), Mathf.Max(lhs.z,rhs.z), Mathf.Max(lhs.w,rhs.w)); }
+
+ // Shorthand for writing @@Vector4(0,0,0,0)@@
+ CSRAW public static Vector4 zero { get { return new Vector4 (0F, 0F, 0F, 0F); } }
+ // Shorthand for writing @@Vector4(1,1,1,1)@@
+ CSRAW public static Vector4 one { get { return new Vector4 (1F, 1F, 1F, 1F); } }
+
+
+ // Adds two vectors.
+ CSRAW public static Vector4 operator + (Vector4 a, Vector4 b) { return new Vector4 (a.x+b.x, a.y+b.y, a.z+b.z, a.w+b.w); }
+ // Subtracts one vector from another.
+ CSRAW public static Vector4 operator - (Vector4 a, Vector4 b) { return new Vector4 (a.x-b.x, a.y-b.y, a.z-b.z, a.w-b.w); }
+ // Negates a vector.
+ CSRAW public static Vector4 operator - (Vector4 a) { return new Vector4 (-a.x, -a.y, -a.z, -a.w); }
+ // Multiplies a vector by a number.
+ CSRAW public static Vector4 operator * (Vector4 a, float d) { return new Vector4 (a.x*d, a.y*d, a.z*d, a.w*d); }
+ // Multiplies a vector by a number.
+ CSRAW public static Vector4 operator * (float d, Vector4 a) { return new Vector4 (a.x*d, a.y*d, a.z*d, a.w*d); }
+ // Divides a vector by a number.
+ CSRAW public static Vector4 operator / (Vector4 a, float d) { return new Vector4 (a.x/d, a.y/d, a.z/d, a.w/d); }
+
+ // Returns true if the vectors are equal.
+ CSRAW public static bool operator == (Vector4 lhs, Vector4 rhs)
+ {
+ return SqrMagnitude (lhs - rhs) < kEpsilon * kEpsilon;
+ }
+ // Returns true if vectors different.
+ CSRAW public static bool operator != (Vector4 lhs, Vector4 rhs)
+ {
+ return SqrMagnitude (lhs - rhs) >= kEpsilon * kEpsilon;
+ }
+
+ // Converts a [[Vector3]] to a Vector4.
+ CSRAW public static implicit operator Vector4(Vector3 v) {
+ return new Vector4(v.x, v.y, v.z, 0.0F);
+ }
+
+ // Converts a Vector4 to a [[Vector3]].
+ CSRAW public static implicit operator Vector3(Vector4 v) {
+ return new Vector3(v.x, v.y, v.z);
+ }
+
+ // Converts a [[Vector2]] to a Vector4.
+ CSRAW public static implicit operator Vector4(Vector2 v) {
+ return new Vector4(v.x, v.y, 0.0F, 0.0F);
+ }
+
+ // Converts a Vector4 to a [[Vector2]].
+ CSRAW public static implicit operator Vector2(Vector4 v) {
+ return new Vector2(v.x, v.y);
+ }
+
+END
+
+
+// Representation of rays.
+THREAD_SAFE
+STRUCT Ray
+ CSRAW private Vector3 m_Origin;
+ CSRAW private Vector3 m_Direction;
+
+ // Creates a ray starting at /origin/ along /direction/.
+ CSRAW public Ray (Vector3 origin, Vector3 direction) { m_Origin = origin; m_Direction = direction.normalized; }
+
+ // The origin point of the ray.
+ CSRAW public Vector3 origin { get { return m_Origin; } set { m_Origin = value; } }
+ // The direction of the ray.
+ CSRAW public Vector3 direction { get { return m_Direction; } set { m_Direction = value.normalized; } }
+
+ // Returns a point at /distance/ units along the ray.
+ CSRAW public Vector3 GetPoint (float distance) { return m_Origin + m_Direction * distance; }
+
+ /// *listonly*
+ CSRAW override public string ToString() { return UnityString.Format("Origin: {0}, Dir: {1}", m_Origin, m_Direction); }
+ // Returns a nicely formatted string for this ray.
+ CSRAW public string ToString(string format) {
+ return UnityString.Format("Origin: {0}, Dir: {1}", m_Origin.ToString(format), m_Direction.ToString(format));
+ }
+
+END
+
+// Representation of 2D rays.
+THREAD_SAFE
+STRUCT Ray2D
+ CSRAW private Vector2 m_Origin;
+ CSRAW private Vector2 m_Direction;
+
+ // Creates a ray starting at /origin/ along /direction/.
+ CSRAW public Ray2D (Vector2 origin, Vector2 direction) { m_Origin = origin; m_Direction = direction.normalized; }
+
+ // The origin point of the ray.
+ CSRAW public Vector2 origin { get { return m_Origin; } set { m_Origin = value; } }
+
+ // The direction of the ray.
+ CSRAW public Vector2 direction { get { return m_Direction; } set { m_Direction = value.normalized; } }
+
+ // Returns a point at /distance/ units along the ray.
+ CSRAW public Vector2 GetPoint (float distance) { return m_Origin + m_Direction * distance; }
+
+ /// *listonly*
+ CSRAW override public string ToString() { return UnityString.Format("Origin: {0}, Dir: {1}", m_Origin, m_Direction); }
+
+ // Returns a nicely formatted string for this ray.
+ CSRAW public string ToString(string format) {
+ return UnityString.Format("Origin: {0}, Dir: {1}", m_Origin.ToString(format), m_Direction.ToString(format));
+ }
+
+END
+
+// Representation of planes.
+THREAD_SAFE
+STRUCT Plane
+ CSRAW Vector3 m_Normal;
+ CSRAW float m_Distance;
+
+ // Normal vector of the plane.
+ CSRAW public Vector3 normal { get { return m_Normal; } set { m_Normal = value; } }
+ // Distance from the origin to the plane.
+ CSRAW public float distance { get { return m_Distance; } set { m_Distance = value; } }
+
+ // Creates a plane.
+ public Plane (Vector3 inNormal, Vector3 inPoint) {
+ m_Normal = Vector3.Normalize (inNormal);
+ m_Distance = -Vector3.Dot (inNormal, inPoint);
+ }
+
+ // Creates a plane.
+ public Plane (Vector3 inNormal, float d) {
+ m_Normal = Vector3.Normalize (inNormal);
+ m_Distance = d;
+ }
+
+ // Creates a plane.
+ public Plane (Vector3 a, Vector3 b, Vector3 c) {
+ m_Normal = Vector3.Normalize (Vector3.Cross (b - a, c - a));
+ m_Distance = -Vector3.Dot (m_Normal, a);
+ }
+
+ // Sets a plane using a point that lies within it plus a normal to orient it (note that the normal must be a normalised vector).
+ public void SetNormalAndPosition (Vector3 inNormal, Vector3 inPoint) {
+ normal = Vector3.Normalize (inNormal);
+ distance = -Vector3.Dot (inNormal, inPoint);
+ }
+
+ // Sets a plane using three points that lie within it. The points go around clockwise as you look down on the top surface of the plane.
+ public void Set3Points (Vector3 a, Vector3 b, Vector3 c) {
+ normal = Vector3.Normalize (Vector3.Cross (b - a, c - a));
+ distance = -Vector3.Dot (normal, a);
+ }
+
+ // Returns a signed distance from plane to point.
+ public float GetDistanceToPoint (Vector3 inPt) { return Vector3.Dot (normal, inPt) + distance; }
+
+ // Is a point on the positive side of the plane?
+ public bool GetSide (Vector3 inPt) { return Vector3.Dot (normal, inPt) + distance > 0.0F; }
+
+ // Are two points on the same side of the plane?
+ public bool SameSide (Vector3 inPt0, Vector3 inPt1) {
+ float d0 = GetDistanceToPoint(inPt0);
+ float d1 = GetDistanceToPoint(inPt1);
+ if (d0 > 0.0f && d1 > 0.0f)
+ return true;
+ else if (d0 <= 0.0f && d1 <= 0.0f)
+ return true;
+ else
+ return false;
+ }
+
+ // Intersects a ray with the plane.
+ public bool Raycast (Ray ray, out float enter) {
+ float vdot = Vector3.Dot (ray.direction, normal);
+ float ndot = -Vector3.Dot (ray.origin, normal) - distance;
+
+ // is line parallel to the plane? if so, even if the line is
+ // at the plane it is not considered as intersection because
+ // it would be impossible to determine the point of intersection
+ if ( Mathf.Approximately (vdot, 0.0f) ) {
+ enter = 0.0F;
+ return false;
+ }
+
+ // the resulting intersection is behind the origin of the ray
+ // if the result is negative ( enter < 0 )
+ enter = ndot / vdot;
+
+ return enter > 0.0F;
+ }
+
+END
+
+
+// A collection of common math functions.
+THREAD_SAFE
+STRUCT Mathf
+ CSRAW
+
+ // Returns the sine of angle /f/ in radians.
+ CSRAW public static float Sin (float f) { return (float)Math.Sin (f); }
+
+ // Returns the cosine of angle /f/ in radians.
+ CSRAW public static float Cos (float f) { return (float)Math.Cos (f); }
+
+ // Returns the tangent of angle /f/ in radians.
+ CSRAW public static float Tan (float f) { return (float)Math.Tan (f); }
+
+ // Returns the arc-sine of /f/ - the angle in radians whose sine is /f/.
+ CSRAW public static float Asin (float f) { return (float)Math.Asin (f); }
+
+ // Returns the arc-cosine of /f/ - the angle in radians whose cosine is /f/.
+ CSRAW public static float Acos (float f) { return (float)Math.Acos (f); }
+
+ // Returns the arc-tangent of /f/ - the angle in radians whose tangent is /f/.
+ CSRAW public static float Atan (float f) { return (float)Math.Atan (f); }
+
+ // Returns the angle in radians whose ::ref::Tan is @@y/x@@.
+ CSRAW public static float Atan2 (float y, float x) { return (float)Math.Atan2 (y,x); }
+
+ // Returns square root of /f/.
+ CSRAW public static float Sqrt (float f) { return (float)Math.Sqrt (f); }
+
+ // Returns the absolute value of /f/.
+ CSRAW public static float Abs (float f) { return (float)Math.Abs (f); }
+
+ // Returns the absolute value of /value/.
+ CSRAW public static int Abs (int value) { return Math.Abs (value); }
+
+ /// *listonly*
+ CSRAW public static float Min (float a, float b) { return a < b ? a : b; }
+ // Returns the smallest of two or more values.
+ CSRAW public static float Min (params float[] values)
+ {
+ int len = values.Length; // cache the length
+ if (len == 0)
+ return 0;
+ float m = values[0];
+ for (int i=1; i<len; i++)
+ {
+ if (values[i] < m)
+ m = values[i];
+ }
+ return m;
+ }
+
+ /// *listonly*
+ CSRAW public static int Min (int a, int b) { return a < b ? a : b; }
+ // Returns the smallest of two or more values.
+ CSRAW public static int Min (params int[] values)
+ {
+ int len = values.Length; // cache the length
+ if (len == 0)
+ return 0;
+ int m = values[0];
+ for (int i=1; i<len; i++)
+ {
+ if (values[i] < m)
+ m = values[i];
+ }
+ return m;
+ }
+
+ /// *listonly*
+ CSRAW public static float Max (float a, float b) { return a > b ? a : b; }
+ // Returns largest of two or more values.
+ CSRAW public static float Max (params float[] values)
+ {
+ int len = values.Length; // cache the length
+ if (len == 0)
+ return 0;
+ float m = values[0];
+ for (int i=1; i<len; i++)
+ {
+ if (values[i] > m)
+ m = values[i];
+ }
+ return m;
+ }
+
+ /// *listonly*
+ CSRAW public static int Max (int a, int b) { return a > b ? a : b; }
+ // Returns the largest of two or more values.
+ CSRAW public static int Max (params int[] values)
+ {
+ int len = values.Length; // cache the length
+ if (len == 0)
+ return 0;
+ int m = values[0];
+ for (int i=1; i<len; i++)
+ {
+ if (values[i] > m)
+ m = values[i];
+ }
+ return m;
+ }
+
+ // Returns /f/ raised to power /p/.
+ CSRAW public static float Pow (float f, float p) { return (float)Math.Pow (f, p); }
+
+ // Returns e raised to the specified power.
+ CSRAW public static float Exp (float power) { return (float)Math.Exp (power); }
+
+ // Returns the logarithm of a specified number in a specified base.
+ CSRAW public static float Log (float f, float p) { return (float)Math.Log (f, p); }
+
+ // Returns the natural (base e) logarithm of a specified number.
+ CSRAW public static float Log (float f) { return (float)Math.Log (f); }
+
+ // Returns the base 10 logarithm of a specified number.
+ CSRAW public static float Log10 (float f) { return (float)Math.Log10 (f); }
+
+ // Returns the smallest integer greater to or equal to /f/.
+ CSRAW public static float Ceil (float f) { return (float)Math.Ceiling (f); }
+
+ // Returns the largest integer smaller to or equal to /f/.
+ CSRAW public static float Floor (float f) { return (float)Math.Floor (f); }
+
+ // Returns /f/ rounded to the nearest integer.
+ CSRAW public static float Round (float f) { return (float)Math.Round (f); }
+
+ // Returns the smallest integer greater to or equal to /f/.
+ CSRAW public static int CeilToInt (float f) { return (int)Math.Ceiling (f); }
+
+ // Returns the largest integer smaller to or equal to /f/.
+ CSRAW public static int FloorToInt (float f) { return (int)Math.Floor (f); }
+
+ // Returns /f/ rounded to the nearest integer.
+ CSRAW public static int RoundToInt (float f) { return (int)Math.Round (f); }
+
+ // Returns the sign of /f/.
+ CSRAW public static float Sign (float f) { return f >= 0F ? 1F : -1F; }
+
+ // The infamous ''3.14159265358979...'' value (RO).
+ CSRAW public const float PI = (float)Math.PI;
+
+
+ // A representation of positive infinity (RO).
+ CSRAW public const float Infinity = Single.PositiveInfinity;
+
+
+ // A representation of negative infinity (RO).
+ CSRAW public const float NegativeInfinity = Single.NegativeInfinity;
+
+
+ // Degrees-to-radians conversion constant (RO).
+ CSRAW public const float Deg2Rad = PI * 2F / 360F;
+
+ // Radians-to-degrees conversion constant (RO).
+ CSRAW public const float Rad2Deg = 1F / Deg2Rad;
+
+ // A tiny floating point value (RO).
+ #if UNITY_IPHONE || UNITY_BB10 || UNITY_TIZEN
+ CSRAW public const float Epsilon = 1.17549435E-38f; // VFP rounds de-normlized values to 0, unfortunatelly Single.Epsilon is de-normalized value!
+ #else
+ CSRAW public const float Epsilon = Single.Epsilon;
+ #endif
+
+
+ // Clamps a value between a minimum float and maximum float value.
+ CSRAW public static float Clamp (float value, float min, float max)
+ {
+ if (value < min)
+ value = min;
+ else if (value > max)
+ value = max;
+ return value;
+ }
+
+ // Clamps value between min and max and returns value.
+ // Set the position of the transform to be that of the time
+ // but never less than 1 or more than 3
+ //
+ CSRAW public static int Clamp (int value, int min, int max)
+ {
+ if (value < min)
+ value = min;
+ else if (value > max)
+ value = max;
+ return value;
+ }
+
+ // Clamps value between 0 and 1 and returns value
+ CSRAW public static float Clamp01 (float value)
+ {
+ if (value < 0F)
+ return 0F;
+ else if (value > 1F)
+ return 1F;
+ else
+ return value;
+ }
+
+ // Interpolates between /a/ and /b/ by /t/. /t/ is clamped between 0 and 1.
+ CSRAW public static float Lerp (float from, float to, float t)
+ {
+ return from + (to - from) * Clamp01 (t);
+ }
+
+ // Same as ::ref::Lerp but makes sure the values interpolate correctly when they wrap around 360 degrees.
+ CSRAW public static float LerpAngle (float a, float b, float t)
+ {
+ float delta = Repeat ((b - a), 360);
+ if (delta > 180)
+ delta -= 360;
+ return a + delta * Clamp01 (t);
+ }
+
+ // Moves a value /current/ towards /target/.
+ CSRAW static public float MoveTowards (float current, float target, float maxDelta)
+ {
+ if (Mathf.Abs(target - current) <= maxDelta)
+ return target;
+ return current + Mathf.Sign(target - current) * maxDelta;
+ }
+
+ // Same as ::ref::MoveTowards but makes sure the values interpolate correctly when they wrap around 360 degrees.
+ CSRAW static public float MoveTowardsAngle (float current, float target, float maxDelta)
+ {
+ target = current + DeltaAngle(current, target);
+ return MoveTowards(current, target, maxDelta);
+ }
+
+ // Interpolates between /min/ and /max/ with smoothing at the limits.
+ CSRAW public static float SmoothStep (float from, float to, float t)
+ {
+ t = Mathf.Clamp01(t);
+ t = -2.0F * t*t*t + 3.0F * t*t;
+ return to * t + from * (1F - t);
+ }
+
+ //*undocumented
+ CSRAW public static float Gamma (float value, float absmax, float gamma)
+ {
+ bool negative = false;
+ if (value < 0F)
+ negative = true;
+ float absval = Abs(value);
+ if (absval > absmax)
+ return negative ? -absval : absval;
+
+ float result = Pow(absval / absmax, gamma) * absmax;
+ return negative ? -result : result;
+ }
+
+ // Compares two floating point values if they are similar.
+ CSRAW public static bool Approximately (float a, float b)
+ {
+ // If a or b is zero, compare that the other is less or equal to epsilon.
+ // If neither a or b are 0, then find an epsilon that is good for
+ // comparing numbers at the maximum magnitude of a and b.
+ // Floating points have about 7 significant digits, so
+ // 1.000001f can be represented while 1.0000001f is rounded to zero,
+ // thus we could use an epsilon of 0.000001f for comparing values close to 1.
+ // We multiply this epsilon by the biggest magnitude of a and b.
+ return Abs(b - a) < Max( 0.000001f * Max(Abs(a), Abs(b)), Epsilon*8);
+ }
+
+ // Gradually changes a value towards a desired goal over time.
+ CSRAW public static float SmoothDamp (float current, float target, ref float currentVelocity, float smoothTime, float maxSpeed = Mathf.Infinity, float deltaTime = Time.deltaTime)
+ {
+ // Based on Game Programming Gems 4 Chapter 1.10
+ smoothTime = Mathf.Max(0.0001F, smoothTime);
+ float omega = 2F / smoothTime;
+
+ float x = omega * deltaTime;
+ float exp = 1F / (1F + x + 0.48F*x*x + 0.235F*x*x*x);
+ float change = current - target;
+ float originalTo = target;
+
+ // Clamp maximum speed
+ float maxChange = maxSpeed * smoothTime;
+ change = Mathf.Clamp(change, -maxChange, maxChange);
+ target = current - change;
+
+ float temp = (currentVelocity + omega * change) * deltaTime;
+ currentVelocity = (currentVelocity - omega * temp) * exp;
+ float output = target + (change + temp) * exp;
+
+ // Prevent overshooting
+ if (originalTo - current > 0.0F == output > originalTo)
+ {
+ output = originalTo;
+ currentVelocity = (output - originalTo) / deltaTime;
+ }
+
+ return output;
+ }
+
+
+ // Gradually changes an angle given in degrees towards a desired goal angle over time.
+ CSRAW public static float SmoothDampAngle (float current, float target, ref float currentVelocity, float smoothTime, float maxSpeed = Mathf.Infinity, float deltaTime = Time.deltaTime)
+ {
+ // Normalize angles
+ target = current + DeltaAngle(current, target);
+ return SmoothDamp(current, target, ref currentVelocity, smoothTime, maxSpeed, deltaTime);
+ }
+
+ // Loops the value t, so that it is never larger than length and never smaller than 0.
+ CSRAW public static float Repeat (float t, float length)
+ {
+ return t - Mathf.Floor (t / length) * length;
+ }
+
+ // PingPongs the value t, so that it is never larger than length and never smaller than 0.
+ CSRAW public static float PingPong (float t, float length)
+ {
+ t = Repeat (t, length * 2F);
+ return length - Mathf.Abs (t - length);
+ }
+
+ // Calculates the ::ref::Lerp parameter between of two values.
+ CSRAW public static float InverseLerp (float from, float to, float value)
+ {
+ if (from < to)
+ {
+ if (value < from)
+ return 0.0F;
+ else if (value > to)
+ return 1.0F;
+ else
+ {
+ value -= from;
+ value /= (to - from);
+ return value;
+ }
+ }
+ else if (from > to)
+ {
+ if (value < to)
+ return 1.0F;
+ else if (value > from)
+ return 0.0F;
+ else
+ {
+ return 1.0F - ((value - to) / (from - to));
+ }
+ }
+ else
+ {
+ return 0.0F;
+ }
+ }
+
+ // Returns the closest power of two value.
+ CUSTOM static int ClosestPowerOfTwo (int value)
+ {
+ return ClosestPowerOfTwo(value);
+ }
+
+ // Converts the given value from gamma to linear color space.
+ CUSTOM static float GammaToLinearSpace (float value)
+ {
+ return GammaToLinearSpace (value);
+ }
+
+ // Converts the given value from linear to gamma color space.
+ CUSTOM static float LinearToGammaSpace (float value)
+ {
+ return LinearToGammaSpace (value);
+ }
+
+ // Returns true if the value is power of two.
+ CUSTOM static bool IsPowerOfTwo (int value)
+ {
+ return IsPowerOfTwo(value);
+ }
+
+ // Returns the next power of two value
+ CUSTOM static int NextPowerOfTwo (int value)
+ {
+ return NextPowerOfTwo(value);
+ }
+
+ // Calculates the shortest difference between two given angles.
+ CSRAW public static float DeltaAngle (float current, float target)
+ {
+ float delta = Mathf.Repeat ((target - current), 360.0F);
+ if (delta > 180.0F)
+ delta -= 360.0F;
+ return delta;
+ }
+
+ // Generate 2D Perlin noise.
+ CUSTOM static float PerlinNoise (float x, float y)
+ {
+ return PerlinNoise::NoiseNormalized (x,y);
+ }
+
+
+ // Infinite Line Intersection (line1 is p1-p2 and line2 is p3-p4)
+ CSRAW internal static bool LineIntersection (Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, ref Vector2 result)
+ {
+ float bx = p2.x - p1.x;
+ float by = p2.y - p1.y;
+ float dx = p4.x - p3.x;
+ float dy = p4.y - p3.y;
+ float bDotDPerp = bx * dy - by * dx;
+ if (bDotDPerp == 0)
+ {
+ return false;
+ }
+ float cx = p3.x - p1.x;
+ float cy = p3.y - p1.y;
+ float t = (cx * dy - cy * dx) / bDotDPerp;
+
+ result = new Vector2 (p1.x + t * bx, p1.y + t * by);
+ return true;
+ }
+
+ // Line Segment Intersection (line1 is p1-p2 and line2 is p3-p4)
+ CSRAW internal static bool LineSegmentIntersection(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, ref Vector2 result)
+ {
+ float bx = p2.x - p1.x;
+ float by = p2.y - p1.y;
+ float dx = p4.x - p3.x;
+ float dy = p4.y - p3.y;
+ float bDotDPerp = bx * dy - by * dx;
+ if (bDotDPerp == 0)
+ {
+ return false;
+ }
+ float cx = p3.x - p1.x;
+ float cy = p3.y - p1.y;
+ float t = (cx * dy - cy * dx) / bDotDPerp;
+ if (t < 0 || t > 1)
+ {
+ return false;
+ }
+ float u = (cx * by - cy * bx) / bDotDPerp;
+ if (u < 0 || u > 1)
+ {
+ return false;
+ }
+ result = new Vector2(p1.x + t * bx, p1.y + t * by);
+ return true;
+ }
+END
+CSRAW
+}
diff --git a/Runtime/Export/MonoICallRegistration.cpp b/Runtime/Export/MonoICallRegistration.cpp
new file mode 100644
index 0000000..435cb28
--- /dev/null
+++ b/Runtime/Export/MonoICallRegistration.cpp
@@ -0,0 +1,343 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "MonoICallRegistration.h"
+#include "Runtime/Modules/ModuleRegistration.h"
+
+#if ENABLE_SCRIPTING && (ENABLE_MONO || UNITY_WINRT)
+
+// Generated by BuildPlayer for AOT platforms when we want
+// to take advantage of dead code stripping of C++ code
+void RegisterAllStrippedInternalCalls ();
+
+#if INTERNAL_CALL_STRIPPING
+
+void RegisterAllInternalCalls ()
+{
+ RegisterAllStrippedInternalCalls();
+ RegisterAllAvailableModuleICalls ();
+}
+
+#else
+
+void ExportCrashReporter();
+void ExportAssetBundleBindings();
+void ExportCrashReporter();
+void ExportBaseClass();
+void ExportGradientBindings();
+void ExportGraphics();
+void ExportTextureBindings();
+void ExportGUI();
+void ExportEventBindings ();
+void ExportGUIUtility();
+void ExportHandheld();
+void ExportGUILayoutUtility();
+void ExportGUISkinBindings();
+void ExportGUIStyleBindings();
+void ExportGizmoBindings();
+void ExportMath();
+void ExportNetworking();
+void ExportScriptAssets();
+void ExportUtility ();
+void ExportAnimationRecorderBindings();
+void ExportUtils();
+
+void ExportWiiDrive();
+void ExportiPhoneInput();
+void ExportiAD();
+void ExportAssetServer();
+void ExportEditorHandles();
+void ExportEditorHandlesUtility();
+void ExportEditorGUIUtility();
+void ExportSavedGUIState();
+void ExportEditorResourcesUtility();
+void ExportInternalUtility();
+void ExportShaderUtilBindings();
+void ExportMeshUtilityBindings();
+void ExportPrefabs();
+void ExportEditorApplication();
+void ExportBuildPipeline();
+void ExportSerializedObject();
+void ExportSerializedPropertyBindings();
+void ExportAssetDatabase();
+void ExportUtility_WIP();
+void ExportAnimationUtilityBindings();
+void ExportStateMachineBindings();
+void ExportAvatarAnimation();
+void ExportAnimatorControllerBindings();
+void ExportAvatarMaskBindings();
+void ExportBlendTreeBindings();
+void ExportMotionBindings();
+void ExportEditorWindow();
+void ExportWebView();
+void ExportAvatarUtility();
+void ExportAnimatorUtilityBindings();
+void ExportAvatar();
+void ExportAvatarBuilderBindings();
+void ExportAssetStoreUtils();
+void ExportAssetStoreToolUtilsBindings();
+void ExportProfilerAPI();
+void ExportPlayerSettings();
+void ExportXboxServices() {}
+void ExportXboxKinect() {}
+void ExportXboxAvatar() {}
+void ExportXboxKeyboard();
+void ExportXboxVideoMode();
+void ExportPS3() {}
+void ExportInternalEditorUtility();
+void ExportEditorMaterialUtility();
+void ExportInternalGraphUtility();
+void ExportFileUtil();
+void ExportEditorSettings ();
+void ExportEditorUserSettings ();
+void ExportAndroidInput();
+void ExportAndroidJNI();
+void ExportNavMeshBuilding();
+void ExportAnnotationUtility ();
+void ExportUploadingBuildsUtility ();
+void ExportShaderBindings ();
+void ExportSubstanceEditorUtility();
+void ExportSubstanceUtility();
+void ExportGradientBindings();
+void ExportParticleSystemEditorBindings ();
+void ExportParticleSystemBindings ();
+void ExportUnityEngineAsyncOperation ();
+void ExportUnityEngineApplication ();
+void ExportUnityEngineObject ();
+void ExportUnityEngineCamera ();
+void ExportUnityEngineComponent ();
+void ExportUnityEngineComputeShader ();
+void ExportUnityEngineDisplay ();
+void ExportUnityEngineTransform ();
+void ExportUnityEngineGameObject ();
+void ExportUnityEngineMetro();
+void ExportLODBindings ();
+void ExportUnityEngineTime ();
+void ExportUnityEngineBehaviour ();
+void ExportUnityEngineMonoBehaviour ();
+void ExportUnityEngineDebug ();
+void ExportUnityEngineLight ();
+void ExportUnityEngineRandom ();
+void ExportUnityEngineInput ();
+void ExportSpritesBindings ();
+void ExportLODUtilityBindings ();
+void ExportGameObjectUtilityBindings ();
+void ExportComponentUtilityBindings ();
+void ExportLightProbeBindings ();
+void ExportVCAssetBindings ();
+void ExportVCChangeSetBindings ();
+void ExportVCProviderBindings ();
+void ExportVCCustomCommandBindings ();
+void ExportVCTaskBindings ();
+void ExportVCMessageBindings ();
+void ExportVCPluginBindings ();
+void ExportAsyncHTTPClientBindings ();
+void ExportAssetStoreContextBindings ();
+void ExportUndoBindings ();
+void ExportCursorBindings ();
+void ExportAssetImporterBindings ();
+void ExportAssetPreviewBindings ();
+void ExportLicenseManagementWindow ();
+void ExportEditorBindings ();
+void ExportTerrainDataBindings ();
+void ExportWindZoneBindings ();
+void ExportSpritePackerBindings ();
+void ExportSpritesBindingsEditor ();
+void ExportPolygonEditorBindings();
+void ExportGameCenterServices();
+void ExportSerializedStateReader();
+void ExportSerializedStateWriter();
+void ExportPPtrRemapper();
+void ExportManagedLivenessAnalysis();
+void ExportSecurityPublic ();
+void ExportPlayerPrefsBindings ();
+void ExportHighlighterBindings ();
+
+void ExportAppTrial();
+void ExportWindowsFile();
+void ExportWindowsDirectory();
+void ExportWindowsCrypto();
+void ExportWSAApplication();
+void ExportWSATiles();
+
+typedef void InternallCallMethod ();
+static InternallCallMethod* sMonoBindingsRegistration[] =
+{
+ &ExportBaseClass,
+ &ExportCrashReporter,
+ &ExportAssetBundleBindings,
+ &ExportCrashReporter,
+ &ExportGradientBindings,
+ &ExportGraphics,
+ &ExportTextureBindings,
+ &ExportGUI,
+ &ExportGUIUtility,
+ &ExportGUILayoutUtility,
+ &ExportGUISkinBindings,
+ &ExportGUIStyleBindings,
+ &ExportEventBindings,
+ &ExportGizmoBindings,
+ &ExportMath,
+ &ExportParticleSystemBindings,
+ &ExportUnityEngineAsyncOperation,
+ &ExportUnityEngineObject,
+ &ExportUnityEngineCamera,
+ &ExportUnityEngineComponent,
+ &ExportUnityEngineComputeShader,
+ &ExportUnityEngineGameObject,
+ &ExportUnityEngineMonoBehaviour,
+ &ExportUnityEngineTransform,
+ &ExportLODBindings,
+ &ExportShaderBindings,
+ &ExportUnityEngineTime,
+ &ExportUnityEngineBehaviour,
+ &ExportUnityEngineDebug,
+ &ExportUnityEngineDisplay,
+ &ExportUnityEngineLight,
+ &ExportUnityEngineRandom,
+ &ExportUnityEngineInput,
+ &ExportUnityEngineApplication,
+ &ExportSpritesBindings,
+ &ExportLightProbeBindings,
+ &ExportCursorBindings,
+ &ExportPlayerPrefsBindings,
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+ &ExportSerializedStateReader,
+ &ExportSerializedStateWriter,
+ &ExportPPtrRemapper,
+ &ExportManagedLivenessAnalysis,
+#endif
+
+#if UNITY_XENON_API ///@TODO: Remove all #ifdefs. We are doing everything via defines in the generated cpp files.
+ &ExportXboxServices,
+ &ExportXboxKinect,
+ &ExportXboxAvatar,
+ &ExportXboxKeyboard,
+ &ExportXboxVideoMode,
+#endif
+#if UNITY_WINRT
+ &ExportUnityEngineMetro,
+#endif
+#if UNITY_PS3_API
+ &ExportPS3,
+#endif
+#if ENABLE_NETWORK
+ &ExportNetworking,
+#endif
+ &ExportScriptAssets,
+#if ENABLE_WWW
+ &ExportUtils,
+#endif
+
+ &ExportSubstanceUtility,
+
+#if UNITY_IPHONE_API || UNITY_ANDROID_API
+ &ExportiPhoneInput,
+#endif
+
+#if UNITY_IPHONE_API
+ &ExportiAD,
+#endif
+
+#if UNITY_IPHONE_API || UNITY_ANDROID_API || UNITY_BB10_API || UNITY_WP8 || UNITY_TIZEN_API
+ &ExportHandheld,
+#endif
+
+#if ENABLE_GAMECENTER
+ &ExportGameCenterServices,
+#endif
+
+#if UNITY_ANDROID_API
+ &ExportAndroidInput,
+ &ExportAndroidJNI,
+#endif
+
+#if UNITY_WEBPLAYER
+ &ExportSecurityPublic,
+#endif
+#if UNITY_WINRT_API
+ &ExportAppTrial,
+ &ExportWindowsFile,
+ &ExportWindowsDirectory,
+ &ExportWindowsCrypto,
+#endif
+#if UNITY_METRO_API
+ &ExportWSAApplication,
+ &ExportWSATiles,
+#endif
+#if UNITY_EDITOR
+ &ExportUtility,
+ &ExportEditorHandles,
+ &ExportEditorHandlesUtility,
+ &ExportEditorGUIUtility,
+ &ExportSavedGUIState,
+ &ExportInternalUtility,
+ &ExportShaderUtilBindings,
+ &ExportMeshUtilityBindings,
+ &ExportPrefabs,
+ &ExportEditorApplication,
+ &ExportEditorResourcesUtility,
+ &ExportBuildPipeline,
+ &ExportSerializedPropertyBindings,
+ &ExportAssetDatabase,
+ &ExportInternalEditorUtility,
+ &ExportEditorMaterialUtility,
+ &ExportInternalGraphUtility,
+ &ExportFileUtil,
+ &ExportNavMeshBuilding,
+ &ExportAnimationUtilityBindings,
+ &ExportStateMachineBindings,
+ &ExportAvatarAnimation,
+ &ExportAnimatorControllerBindings,
+ &ExportAvatarMaskBindings,
+ &ExportBlendTreeBindings,
+ &ExportAvatarUtility,
+ &ExportAnimatorUtilityBindings,
+ &ExportMotionBindings,
+ &ExportAssetServer,
+ &ExportEditorWindow,
+ &ExportWebView,
+ &ExportAssetStoreUtils,
+ &ExportAssetStoreToolUtilsBindings,
+ &ExportProfilerAPI,
+ &ExportEditorSettings,
+ &ExportEditorUserSettings,
+ &ExportPlayerSettings,
+ &ExportAnnotationUtility,
+ &ExportUploadingBuildsUtility,
+ &ExportSubstanceEditorUtility,
+ &ExportLODUtilityBindings,
+ &ExportGameObjectUtilityBindings,
+ &ExportComponentUtilityBindings,
+ &ExportAsyncHTTPClientBindings,
+ &ExportParticleSystemEditorBindings,
+ &ExportVCAssetBindings,
+ &ExportVCChangeSetBindings,
+ &ExportVCProviderBindings,
+ &ExportVCCustomCommandBindings,
+ &ExportVCTaskBindings,
+ &ExportVCMessageBindings,
+ &ExportVCPluginBindings,
+ &ExportAssetStoreContextBindings,
+ &ExportUndoBindings,
+ &ExportAssetImporterBindings,
+ &ExportAssetPreviewBindings,
+ &ExportLicenseManagementWindow,
+ &ExportEditorBindings,
+ &ExportSpritePackerBindings,
+ &ExportSpritesBindingsEditor,
+ &ExportPolygonEditorBindings,
+ &ExportHighlighterBindings
+#endif
+};
+
+void RegisterAllInternalCalls ()
+{
+ for( int i=0; i<sizeof(sMonoBindingsRegistration)/sizeof(sMonoBindingsRegistration[0]); ++i )
+ sMonoBindingsRegistration[i]();
+
+ RegisterAllAvailableModuleICalls ();
+}
+
+#endif
+
+#endif
diff --git a/Runtime/Export/MonoICallRegistration.h b/Runtime/Export/MonoICallRegistration.h
new file mode 100644
index 0000000..14d6730
--- /dev/null
+++ b/Runtime/Export/MonoICallRegistration.h
@@ -0,0 +1,2 @@
+
+void RegisterAllInternalCalls ();
diff --git a/Runtime/Export/MotionBindings.txt b/Runtime/Export/MotionBindings.txt
new file mode 100644
index 0000000..46aef06
--- /dev/null
+++ b/Runtime/Export/MotionBindings.txt
@@ -0,0 +1,43 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Animation/Motion.h"
+
+CSRAW
+using System;
+using Object=UnityEngine.Object;
+
+namespace UnityEngine
+{
+
+//*undocumented* leave undocumented until BlendTree are in public API
+NONSEALED_CLASS public Motion : Object
+
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM bool ValidateIfRetargetable(bool showWarning) {return self->ValidateIfRetargetable(showWarning);}
+
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM_PROP float averageDuration { return self->GetAverageDuration(); }
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM_PROP float averageAngularSpeed { return self->GetAverageAngularSpeed(); }
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM_PROP Vector3 averageSpeed { return self->GetAverageSpeed(); }
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM_PROP float apparentSpeed { return self->GetApparentSpeed(); }
+
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM_PROP bool isLooping {return self->IsLooping();}
+
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM_PROP bool isAnimatorMotion {return self->IsAnimatorMotion();}
+
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM_PROP bool isHumanMotion {return self->IsHumanMotion();}
+
+END
+
+
+CSRAW }
diff --git a/Runtime/Export/MouseEvents.cs b/Runtime/Export/MouseEvents.cs
new file mode 100644
index 0000000..bd87008
--- /dev/null
+++ b/Runtime/Export/MouseEvents.cs
@@ -0,0 +1,187 @@
+using System;
+using System.Collections;
+using System.Reflection;
+
+namespace UnityEngine
+{
+
+internal class SendMouseEvents
+{
+ struct HitInfo
+ {
+ public GameObject target;
+ public Camera camera;
+
+ public void SendMessage (string name)
+ {
+ target.SendMessage (name, null, SendMessageOptions.DontRequireReceiver);
+ }
+
+ public static implicit operator bool (HitInfo exists)
+ {
+ return exists.target != null && exists.camera != null;
+ }
+
+ public static bool Compare (HitInfo lhs, HitInfo rhs)
+ {
+ return lhs.target == rhs.target && lhs.camera == rhs.camera;
+ }
+ }
+
+ static HitInfo[] m_LastHit = { new HitInfo (), new HitInfo () };
+ static HitInfo[] m_MouseDownHit = { new HitInfo (), new HitInfo () };
+ static RaycastHit2D[] m_MouseRayHits2D = {new RaycastHit2D()};
+
+ [NotRenamed]
+ static void DoSendMouseEvents (int mouseUsed, int skipRTCameras)
+ {
+ HitInfo[] currentHit = { new HitInfo (), new HitInfo () };
+ Vector3 mousePosition = Input.mousePosition;
+ Camera[] cameras = Camera.allCameras;
+
+ // If UnityGUI has the mouse over, we simply don't do any mouse hit detection.
+ // That way, it will appear as if the mouse has missed everything.
+ if (mouseUsed == 0)
+ {
+ foreach (Camera camera in cameras)
+ {
+ // we do not want to check cameras that are rendering to textures, starting with 4.0
+ if (skipRTCameras != 0 && camera.targetTexture != null)
+ continue;
+
+ // Is the mouse inside the cameras viewport?
+ Rect rect = camera.pixelRect;
+ if (!rect.Contains (mousePosition))
+ continue;
+
+ // Did we hit any gui elements?
+ GUILayer layer = (GUILayer)camera.GetComponent (typeof (GUILayer));
+ if (layer)
+ {
+ GUIElement element = layer.HitTest (mousePosition);
+ if (element)
+ {
+ currentHit[0].target = element.gameObject;
+ currentHit[0].camera = camera;
+ }
+ }
+#if ENABLE_PHYSICS || ENABLE_2D_PHYSICS
+ // There is no need to continue if the camera shouldn't be sending out events
+ if ((int)camera.eventMask == 0)
+ continue;
+
+#if ENABLE_PHYSICS
+ // Did we hit any colliders?
+ RaycastHit hit;
+ if (camera.farClipPlane > 0.0f && Physics.Raycast (camera.ScreenPointToRay (mousePosition),
+ out hit, camera.farClipPlane, camera.cullingMask & camera.eventMask & Physics.DefaultRaycastLayers
+ ))
+ {
+ if (hit.rigidbody)
+ {
+ currentHit[1].target = hit.rigidbody.gameObject;
+ currentHit[1].camera = camera;
+ }
+ else
+ {
+ currentHit[1].target = hit.collider.gameObject;
+ currentHit[1].camera = camera;
+ }
+ }
+#endif
+#if ENABLE_2D_PHYSICS
+ // Did we hit any 2D colliders?
+ else if (camera.farClipPlane > 0.0f)
+ {
+ // Did we hit any 2D colliders?
+ if (Physics2D.GetRayIntersectionNonAlloc (camera.ScreenPointToRay (mousePosition),
+ m_MouseRayHits2D, camera.farClipPlane, camera.cullingMask & camera.eventMask & Physics2D.DefaultRaycastLayers) == 1)
+ {
+ currentHit[1].camera = camera;
+ if (m_MouseRayHits2D[0].rigidbody)
+ currentHit[1].target = m_MouseRayHits2D[0].rigidbody.gameObject;
+ else
+ currentHit[1].target = m_MouseRayHits2D[0].collider.gameObject;
+ }
+ }
+#endif
+ else
+ {
+ // We did not hit anything with a raycast from this camera. But our camera
+ // clears the screen and renders on top of whatever was below, thus making things
+ // rendered before invisible. So clear any previous hit we have found.
+ if (camera.clearFlags == CameraClearFlags.Skybox || camera.clearFlags == CameraClearFlags.Color)
+ {
+ currentHit[1].target = null;
+ currentHit[1].camera = null;
+ }
+ }
+#endif
+ }
+ }
+
+ // i=0: CameraGUILayer, i=1: 3D Objects
+ for (int i=0;i<2;i++)
+ SendEvents(i, currentHit[i]);
+ }
+
+ /// <summary>
+ /// Old-style mouse events used prior to the new event system of 4.2.
+ /// </summary>
+
+ static void SendEvents (int i, HitInfo hit)
+ {
+ // Handle MouseDown, MouseDrag, MouseUp
+ bool mouseDownThisFrame = Input.GetMouseButtonDown(0);
+ bool mousePressed = Input.GetMouseButton(0);
+
+ if (mouseDownThisFrame)
+ {
+ if (hit)
+ {
+ m_MouseDownHit[i] = hit;
+ m_MouseDownHit[i].SendMessage("OnMouseDown");
+ }
+ }
+ else if (!mousePressed)
+ {
+ if (m_MouseDownHit[i])
+ {
+ // For button like behavior only fire this event if same as on MouseDown
+ if (HitInfo.Compare(hit, m_MouseDownHit[i]))
+ m_MouseDownHit[i].SendMessage("OnMouseUpAsButton");
+
+ // For backwards compatibility we keep the event name OnMouseUp
+ m_MouseDownHit[i].SendMessage("OnMouseUp");
+ m_MouseDownHit[i] = new HitInfo();
+ }
+ }
+ else if (m_MouseDownHit[i])
+ {
+ m_MouseDownHit[i].SendMessage("OnMouseDrag");
+ }
+
+
+ // Handle MouseOver, MouseEnter, MouseExit
+ if (HitInfo.Compare(hit, m_LastHit[i]))
+ {
+ if (hit)
+ hit.SendMessage("OnMouseOver");
+ }
+ else
+ {
+ if (m_LastHit[i])
+ {
+ m_LastHit[i].SendMessage("OnMouseExit");
+ }
+
+ if (hit)
+ {
+ hit.SendMessage("OnMouseEnter");
+ hit.SendMessage("OnMouseOver");
+ }
+ }
+ m_LastHit[i] = hit;
+ }
+}
+}
diff --git a/Runtime/Export/NetworkServices.cs b/Runtime/Export/NetworkServices.cs
new file mode 100644
index 0000000..0e1f1cd
--- /dev/null
+++ b/Runtime/Export/NetworkServices.cs
@@ -0,0 +1,231 @@
+using System;
+
+namespace UnityEngine
+{
+ using UnityEngine.SocialPlatforms;
+
+ // A facade for the social API namespace, no state, only helper functions which delegate into others
+ public static class Social
+ {
+ public static ISocialPlatform Active
+ {
+ get { return ActivePlatform.Instance; }
+ set { ActivePlatform.Instance = value; }
+ }
+
+ public static ILocalUser localUser { get { return Active.localUser; } }
+
+ public static void LoadUsers(string[] userIDs, Action<IUserProfile[]> callback)
+ {
+ Active.LoadUsers(userIDs, callback);
+ }
+
+ public static void ReportProgress(string achievementID, double progress, Action<bool> callback)
+ {
+ Active.ReportProgress(achievementID, progress, callback);
+ }
+
+ public static void LoadAchievementDescriptions(Action<IAchievementDescription[]> callback)
+ {
+ Active.LoadAchievementDescriptions(callback);
+ }
+
+ public static void LoadAchievements(Action<IAchievement[]> callback)
+ {
+ Active.LoadAchievements(callback);
+ }
+
+ public static void ReportScore(Int64 score, string board, Action<bool> callback)
+ {
+ Active.ReportScore(score, board, callback);
+ }
+
+ public static void LoadScores(string leaderboardID, Action<IScore[]> callback)
+ {
+ Active.LoadScores(leaderboardID, callback);
+ }
+
+ public static ILeaderboard CreateLeaderboard()
+ {
+ return Active.CreateLeaderboard();
+ }
+
+ public static IAchievement CreateAchievement()
+ {
+ return Active.CreateAchievement();
+ }
+
+ public static void ShowAchievementsUI()
+ {
+ Active.ShowAchievementsUI();
+ }
+
+ public static void ShowLeaderboardUI()
+ {
+ Active.ShowLeaderboardUI();
+ }
+ }
+}
+
+namespace UnityEngine.SocialPlatforms
+{
+ // The state of the current active social implementation
+ internal static class ActivePlatform
+ {
+ private static ISocialPlatform _active;
+
+ internal static ISocialPlatform Instance
+ {
+ get
+ {
+ if (_active == null)
+ _active = SelectSocialPlatform();
+ return _active;
+ }
+ set
+ {
+ _active = value;
+ }
+ }
+
+ private static ISocialPlatform SelectSocialPlatform() {
+ // statically selecting community
+ #if ENABLE_GAMECENTER
+ return new UnityEngine.SocialPlatforms.GameCenter.GameCenterPlatform();
+ #elif UNITY_XENON_API && ENABLE_XENON_SOCIALAPI
+ return new XboxLive();
+ #else
+ return new UnityEngine.SocialPlatforms.Local();
+ #endif
+ }
+ }
+
+ public interface ISocialPlatform
+ {
+ ILocalUser localUser { get; }
+
+ void LoadUsers(string[] userIDs, Action<IUserProfile[]> callback);
+
+ void ReportProgress(string achievementID, double progress, Action<bool> callback);
+ void LoadAchievementDescriptions(Action<IAchievementDescription[]> callback);
+ void LoadAchievements(Action<IAchievement[]> callback);
+ IAchievement CreateAchievement();
+
+ void ReportScore(Int64 score, string board, Action<bool> callback);
+ void LoadScores(string leaderboardID, Action<IScore[]> callback);
+ ILeaderboard CreateLeaderboard();
+
+ void ShowAchievementsUI();
+ void ShowLeaderboardUI();
+
+ // ===> These should be explicitly implemented <===
+ void Authenticate(ILocalUser user, Action<bool> callback);
+ void LoadFriends(ILocalUser user, Action<bool> callback);
+ void LoadScores(ILeaderboard board, Action<bool> callback);
+ bool GetLoading(ILeaderboard board);
+ }
+
+ public interface ILocalUser : IUserProfile
+ {
+ void Authenticate (Action<bool> callback);
+ void LoadFriends (Action<bool> callback);
+
+ IUserProfile[] friends { get; }
+ bool authenticated { get; }
+ bool underage { get; }
+ }
+
+ public enum UserState
+ {
+ Online,
+ OnlineAndAway,
+ OnlineAndBusy,
+ Offline,
+ Playing
+ }
+
+ public interface IUserProfile
+ {
+ string userName { get; }
+ string id { get; }
+ bool isFriend { get; }
+ UserState state { get; }
+ Texture2D image { get; }
+ }
+
+ public interface IAchievement
+ {
+ void ReportProgress(Action<bool> callback);
+
+ string id { get; set; }
+ double percentCompleted { get; set; }
+ bool completed { get; }
+ bool hidden { get; }
+ DateTime lastReportedDate { get; }
+ }
+
+ public interface IAchievementDescription
+ {
+ string id { get; set; }
+ string title { get; }
+ Texture2D image { get; }
+ string achievedDescription { get; }
+ string unachievedDescription { get; }
+ bool hidden { get; }
+ int points { get; }
+ }
+
+ public interface IScore
+ {
+ void ReportScore(Action<bool> callback);
+
+ string leaderboardID { get; set; }
+ // TODO: This is just an int64 here, but should be able to represent all supported formats, except for float type scores ...
+ Int64 value { get; set; }
+ DateTime date { get; }
+ string formattedValue { get; }
+ string userID { get; }
+ int rank { get; }
+ }
+
+ public enum UserScope
+ {
+ Global = 0,
+ FriendsOnly
+ }
+
+ public enum TimeScope
+ {
+ Today = 0,
+ Week,
+ AllTime
+ }
+
+ public struct Range
+ {
+ public int from;
+ public int count;
+
+ public Range(int fromValue, int valueCount)
+ {
+ from = fromValue;
+ count = valueCount;
+ }
+ }
+
+ public interface ILeaderboard
+ {
+ void SetUserFilter(string[] userIDs);
+ void LoadScores(Action<bool> callback);
+ bool loading { get; }
+
+ string id { get; set; }
+ UserScope userScope { get; set; }
+ Range range { get; set; }
+ TimeScope timeScope { get; set; }
+ IScore localUserScore { get; }
+ uint maxRange { get; }
+ IScore[] scores { get; }
+ string title { get; }
+ }
+}
diff --git a/Runtime/Export/Networking.txt b/Runtime/Export/Networking.txt
new file mode 100644
index 0000000..8779122
--- /dev/null
+++ b/Runtime/Export/Networking.txt
@@ -0,0 +1,921 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Network/NetworkManager.h"
+#include "Runtime/Network/NetworkUtility.h"
+#include "Runtime/Network/BitStreamPacker.h"
+#include "Runtime/Network/MasterServerInterface.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Scripting/Scripting.h"
+
+CSRAW
+
+#if ENABLE_NETWORK
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using UnityEngineInternal;
+
+namespace UnityEngine
+{
+
+// Option for who will receive an [[RPC]], used by NetworkView.RPC.
+CONDITIONAL ENABLE_NETWORK
+ENUM RPCMode
+ // Sends to the server only
+ Server = 0,
+
+ // Sends to everyone except the sender
+ Others = 1,
+
+ // Sends to everyone except the sender and adds to the buffer
+ OthersBuffered = 5,
+
+ // Sends to everyone
+ All = 2,
+
+ // Sends to everyone and adds to the buffer
+ AllBuffered = 6
+END
+
+// The various test results the connection tester may return with.
+CONDITIONAL ENABLE_NETWORK
+ENUM ConnectionTesterStatus
+ // Some unknown error occurred.
+ Error = -2,
+ // Test result undetermined, still in progress.
+ Undetermined = -1,
+ OBSOLETE warning No longer returned, use newer connection tester enums instead.
+ PrivateIPNoNATPunchthrough = 0,
+ OBSOLETE warning No longer returned, use newer connection tester enums instead.
+ PrivateIPHasNATPunchThrough = 1,
+ // Public IP address detected and game listen port is accessible to the internet.
+ PublicIPIsConnectable = 2,
+ // Public IP address detected but the port is not connectable from the internet.
+ PublicIPPortBlocked = 3,
+ // Public IP address detected but server is not initialized and no port is listening.
+ PublicIPNoServerStarted = 4,
+ // Port-restricted NAT type, can do NAT punchthrough to everyone except symmetric.
+ LimitedNATPunchthroughPortRestricted = 5,
+ // Symmetric NAT type, cannot do NAT punchthrough to other symmetric types nor port restricted type.
+ LimitedNATPunchthroughSymmetric = 6,
+ // Full cone type, NAT punchthrough fully supported.
+ NATpunchthroughFullCone = 7,
+ // Address-restricted cone type, NAT punchthrough fully supported.
+ NATpunchthroughAddressRestrictedCone = 8
+END
+
+// Possible status messages returned by Network.Connect and in [[MonoBehaviour.OnFailedToConnect|OnFailedToConnect]]
+CONDITIONAL ENABLE_NETWORK
+ENUM NetworkConnectionError
+ // No error occurred.
+ NoError = 0,
+
+ // We presented an RSA public key which does not match what the system we connected to is using.
+ RSAPublicKeyMismatch = 21,
+
+ // The server is using a password and has refused our connection because we did not set the correct password.
+ InvalidPassword = 23,
+
+ // Connection attempt failed, possibly because of internal connectivity problems.
+ ConnectionFailed = 15,
+
+ // The server is at full capacity, failed to connect.
+ TooManyConnectedPlayers = 18,
+
+ // We are banned from the system we attempted to connect to (likely temporarily).
+ ConnectionBanned = 22,
+
+ // We are already connected to this particular server (can happen after fast disconnect/reconnect).
+ AlreadyConnectedToServer = 16,
+
+ // Cannot connect to two servers at once. Close the connection before connecting again.
+ AlreadyConnectedToAnotherServer = -1,
+
+ // Internal error while attempting to initialize network interface. Socket possibly already in use.
+ CreateSocketOrThreadFailure = -2,
+
+ // Incorrect parameters given to Connect function.
+ IncorrectParameters = -3,
+
+ // No host target given in Connect.
+ EmptyConnectTarget = -4,
+
+ // Client could not connect internally to same network NAT enabled server.
+ InternalDirectConnectFailed = -5,
+
+ // The NAT target we are trying to connect to is not connected to the facilitator server.
+ NATTargetNotConnected = 69,
+
+ // Connection lost while attempting to connect to NAT target.
+ NATTargetConnectionLost = 71,
+
+ // NAT punchthrough attempt has failed. The cause could be a too restrictive NAT implementation on either endpoints.
+ NATPunchthroughFailed = 73
+END
+
+// The reason a disconnect event occured, like in [[MonoBehaviour.OnDisconnectedFromServer|OnDisconnectedFromServer]].
+CONDITIONAL ENABLE_NETWORK
+ENUM NetworkDisconnection
+ // The connection to the system has been lost, no reliable packets could be delivered.
+ LostConnection = 20,
+
+ // The connection to the system has been closed.
+ Disconnected = 19
+END
+
+// Describes status messages from the master server as returned in [[MonoBehaviour.OnMasterServerEvent|OnMasterServerEvent]].
+CONDITIONAL ENABLE_NETWORK
+ENUM MasterServerEvent
+ // Registration failed because an empty game name was given.
+ RegistrationFailedGameName = 0,
+
+ // Registration failed because an empty game type was given.
+ RegistrationFailedGameType = 1,
+
+ // Registration failed because no server is running.
+ RegistrationFailedNoServer = 2,
+
+ // Registration to master server succeeded, received confirmation.
+ RegistrationSucceeded = 3,
+
+ // Received a host list from the master server.
+ HostListReceived = 4
+END
+
+// Different types of synchronization for the [[NetworkView]] component.
+CONDITIONAL ENABLE_NETWORK
+ENUM NetworkStateSynchronization
+
+ // No state data will be synchronized
+ Off = 0,
+
+ // All packets are sent reliable and ordered.
+ ReliableDeltaCompressed = 1,
+
+ // Brute force unreliable state sending
+ Unreliable = 2
+END
+
+// Describes the status of the network interface peer type as returned by Network.peerType.
+CONDITIONAL ENABLE_NETWORK
+ENUM NetworkPeerType
+ // No client connection running. Server not initialized.
+ Disconnected = 0,
+ // Running as server.
+ Server = 1,
+ // Running as client.
+ Client = 2,
+ // Attempting to connect to a server.
+ Connecting = 3
+END
+
+// Describes different levels of log information the network layer supports.
+CONDITIONAL ENABLE_NETWORK
+ENUM NetworkLogLevel
+ // Only report errors, otherwise silent.
+ Off = 0,
+ // Report informational messages like connectivity events.
+ Informational = 1,
+ // Full debug level logging down to each individual message being reported.
+ Full = 3
+END
+
+
+// The NetworkPlayer is a data structure with which you can locate another player over the network.
+CONDITIONAL ENABLE_NETWORK
+STRUCT NetworkPlayer
+ CSRAW internal int index;
+
+ CUSTOM private static string Internal_GetIPAddress(int index) { return scripting_string_new(GetNetworkManager().GetIPAddress(index)); }
+ CUSTOM private static int Internal_GetPort(int index) { return GetNetworkManager().GetPort(index); }
+ CUSTOM private static string Internal_GetExternalIP() { return scripting_string_new(GetNetworkManager().GetExternalIP()); }
+ CUSTOM private static int Internal_GetExternalPort() { return GetNetworkManager().GetExternalPort(); }
+ CUSTOM private static string Internal_GetLocalIP() { return scripting_string_new(GetLocalIP()); }
+ CUSTOM private static int Internal_GetLocalPort() { return GetNetworkManager().GetPort(); }
+ CUSTOM private static int Internal_GetPlayerIndex () { return GetNetworkManager().GetPlayerID(); }
+ CUSTOM private static string Internal_GetGUID(int index) { return scripting_string_new(GetNetworkManager().GetGUID(index)); }
+ CUSTOM private static string Internal_GetLocalGUID () { return scripting_string_new(GetNetworkManager().GetGUID()); }
+
+ // *undocumented*
+ CSRAW public NetworkPlayer (string ip, int port)
+ {
+ Debug.LogError("Not yet implemented");
+ index = 0;
+ }
+
+ // Returns true if two NetworkPlayers are the same player.
+ CSRAW public static bool operator == (NetworkPlayer lhs, NetworkPlayer rhs)
+ {
+ return lhs.index == rhs.index;
+ }
+
+ // Returns true if two NetworkPlayers are not the same player.
+ CSRAW public static bool operator != (NetworkPlayer lhs, NetworkPlayer rhs)
+ {
+ return lhs.index != rhs.index;
+ }
+
+ // *undocumented* Used to allow NetworkPlayer to be used as keys in hash tables. Also triggers warning for == and != operators if not present
+ CSRAW public override int GetHashCode() {
+ return index.GetHashCode();
+ }
+
+ // *undocumented* Required for being able to use NetworkPlayer as keys in hash tables
+ CSRAW public override bool Equals(object other) {
+ if(!(other is NetworkPlayer)) return false;
+
+ NetworkPlayer rhs=(NetworkPlayer)other;
+ return rhs.index == index;
+ }
+
+ // The IP address of this player.
+ CSRAW public string ipAddress { get { if (index == Internal_GetPlayerIndex()) return Internal_GetLocalIP(); else return Internal_GetIPAddress(index); } }
+
+ // The port of this player.
+ CSRAW public int port { get { if (index == Internal_GetPlayerIndex()) return Internal_GetLocalPort(); else return Internal_GetPort(index); } }
+
+ // The GUID for this player, used when connecting with NAT punchthrough.
+ CSRAW public string guid { get { if (index == Internal_GetPlayerIndex()) return Internal_GetLocalGUID(); else return Internal_GetGUID(index); } }
+
+ // Returns the index number for this network player.
+ CSRAW override public string ToString() { return index.ToString(); }
+
+ // Returns the external IP address of the network interface.
+ CSRAW public string externalIP { get { return Internal_GetExternalIP(); } }
+
+ // Returns the external port of the network interface.
+ CSRAW public int externalPort { get { return Internal_GetExternalPort(); } }
+
+ CSRAW static internal NetworkPlayer unassigned { get { NetworkPlayer val; val.index = -1; return val; } }
+
+END
+
+// The NetworkViewID is a unique identifier for a network view instance in a multiplayer game.
+CONDITIONAL ENABLE_NETWORK
+STRUCT NetworkViewID
+ CSRAW int a;
+ CSRAW int b;
+ CSRAW int c;
+
+ // Represents an invalid network view ID.
+ CUSTOM_PROP static NetworkViewID unassigned { return NetworkViewID::GetUnassignedViewID(); }
+
+ CUSTOM static internal bool Internal_IsMine(NetworkViewID value) { return GetNetworkManager().WasViewIdAllocatedByMe(value); }
+ CUSTOM static internal void Internal_GetOwner(NetworkViewID value, out NetworkPlayer player) { *player = GetNetworkManager().GetNetworkViewIDOwner(value); }
+ CUSTOM static internal string Internal_GetString(NetworkViewID value) { return scripting_string_new(value.ToString()); }
+ CUSTOM static internal bool Internal_Compare(NetworkViewID lhs, NetworkViewID rhs) { return rhs == lhs; }
+
+ // Returns true if two NetworkViewIDs are identical
+ CSRAW public static bool operator == (NetworkViewID lhs, NetworkViewID rhs)
+ {
+ return Internal_Compare(lhs, rhs);
+ }
+
+ // Returns true if two NetworkViewIDs are not identical
+ CSRAW public static bool operator != (NetworkViewID lhs, NetworkViewID rhs)
+ {
+ return !Internal_Compare(lhs, rhs);
+ }
+
+ // *undocumented* Used to allow NetworkViewIDs to be used as keys in hash tables. Also triggers warning for == and != operators if not present
+ CSRAW public override int GetHashCode() {
+ return a ^ b ^ c;
+ }
+
+ // *undocumented* Required for being able to use NetworkViewIDs as keys in hash tables
+ CSRAW public override bool Equals(object other) {
+ if(!(other is NetworkViewID)) return false;
+
+ NetworkViewID rhs=(NetworkViewID)other;
+ return Internal_Compare(this, rhs);
+ }
+
+ // True if instantiated by me
+ CSRAW public bool isMine { get
+ { return Internal_IsMine(this); } }
+
+ // The [[NetworkPlayer]] who owns the [[NetworkView]]. Could be the server.
+ CSRAW public NetworkPlayer owner { get { NetworkPlayer p; Internal_GetOwner(this, out p); return p;} }
+
+ // Returns a formatted string with details on this NetworkViewID.
+ CSRAW override public string ToString() { return Internal_GetString(this); }
+
+END
+
+
+// Ping any given IP address (given in dot notation).
+CONDITIONAL ENABLE_NETWORK
+CLASS Ping
+
+ // We are matching the Ping class here so we can directly access it.
+ CSRAW private IntPtr pingWrapper;
+
+ // this is needed only for batched android build due to bug in txt parser
+ // screwing up iphone_input.txt to have cpp line emited in wrong place
+ C++RAW #undef GET
+ C++RAW #define GET ExtractMonoObjectData<Ping*> (self)
+
+ // Perform a ping to the supplied target IP address.
+ CUSTOM Ping(string address)
+ {
+ GET = new Ping(address);
+ GetNetworkManager().PingWrapper(GET);
+ }
+
+ //*undocumented*
+ THREAD_SAFE
+ CUSTOM void DestroyPing()
+ {
+ if (GET)
+ {
+ GET->Release();
+ GET = 0;
+ }
+ }
+
+ CSRAW ~Ping()
+ {
+ DestroyPing();
+ }
+
+ // Has the ping function completed?
+ CUSTOM_PROP public bool isDone
+ {
+ if (GET) return (short)GET->GetIsDone();
+ else return false;
+ }
+
+ // This property contains the ping time result after isDone returns true.
+ CUSTOM_PROP public int time
+ {
+ return GET->GetTime();
+ }
+
+ // The IP target of the ping.
+ CUSTOM_PROP public string ip
+ {
+ return scripting_string_new(GET->GetIP());
+ }
+
+ C++RAW
+ #undef GET
+
+END
+
+
+// The network view is the binding material of multiplayer games.
+CONDITIONAL ENABLE_NETWORK
+CLASS NetworkView : Behaviour
+
+ CUSTOM private static void Internal_RPC (NetworkView view, string name, RPCMode mode, object[] args)
+ {
+ view->RPCCall(name, mode, args);
+ }
+
+ CUSTOM private static void Internal_RPC_Target (NetworkView view, string name, NetworkPlayer target, object[] args)
+ {
+ view->RPCCallSpecificTarget(name, target, args);
+ }
+
+ // Call a [[RPC]] function on all connected peers.
+ CSRAW public void RPC (string name, RPCMode mode, params object[] args) { Internal_RPC(this, name, mode, args); }
+
+ // Call a RPC function on a specific player
+ CSRAW public void RPC (string name, NetworkPlayer target, params object[] args) { Internal_RPC_Target(this, name, target, args); }
+
+ // The component the network view is observing.
+ AUTO_PTR_PROP Component observed GetObserved SetObserved
+
+ // The type of [[NetworkStateSynchronization]] set for this network view.
+ CUSTOM_PROP NetworkStateSynchronization stateSynchronization { return self->GetStateSynchronization(); } { self->SetStateSynchronization(value); }
+
+ CUSTOM private void Internal_GetViewID (out NetworkViewID viewID)
+ {
+ *viewID = self->GetViewID();
+ }
+ CUSTOM private void Internal_SetViewID (NetworkViewID viewID) { self->SetViewID(viewID); }
+
+ // The ViewID of this network view.
+ CSRAW public NetworkViewID viewID { get { NetworkViewID val; Internal_GetViewID(out val); return val; } set { Internal_SetViewID(value); } }
+
+ // The network group number of this network view.
+ AUTO_PROP int group GetGroup SetGroup
+
+ // Is the network view controlled by this object?
+ CSRAW public bool isMine { get { return viewID.isMine; } }
+
+ // The [[NetworkPlayer]] who owns this network view.
+ CSRAW public NetworkPlayer owner { get { return viewID.owner; } }
+
+ // Set the scope of the network view in relation to a specific network player.
+ CUSTOM bool SetScope(NetworkPlayer player, bool relevancy)
+ {
+ return self->SetPlayerScope(player, relevancy);
+ }
+
+ // Find a network view based on a [[NetworkViewID]].
+ CUSTOM static NetworkView Find (NetworkViewID viewID) { return Scripting::ScriptingWrapperFor(GetNetworkManager().ViewIDToNetworkView (viewID)); }
+
+END
+
+// The network class is at the heart of the network implementation and provides the core functions.
+CONDITIONAL ENABLE_NETWORK
+CLASS Network
+
+ // Initialize the server.
+ CUSTOM static NetworkConnectionError InitializeServer (int connections, int listenPort, bool useNat) { return GetNetworkManager().InitializeServer(connections, listenPort, useNat); }
+
+ CUSTOM private static NetworkConnectionError Internal_InitializeServerDeprecated (int connections, int listenPort) { return GetNetworkManager().InitializeServer(connections, listenPort, false); }
+
+ OBSOLETE warning Use the IntializeServer(connections, listenPort, useNat) function instead
+ CSRAW public static NetworkConnectionError InitializeServer (int connections, int listenPort) { return Internal_InitializeServerDeprecated(connections, listenPort); }
+
+ // Set the password for the server (for incoming connections).
+ CUSTOM_PROP static string incomingPassword { return scripting_string_new(GetNetworkManager().GetIncomingPassword());} { GetNetworkManager().SetIncomingPassword(value);}
+
+ // Set the log level for network messages (default is Off).
+ CUSTOM_PROP static NetworkLogLevel logLevel { return GetNetworkManager().GetDebugLevel(); } { GetNetworkManager().SetDebugLevel(value); }
+
+ // Initializes security layer.
+ CUSTOM static void InitializeSecurity() { GetNetworkManager().InitializeSecurity(); }
+
+ CUSTOM private static NetworkConnectionError Internal_ConnectToSingleIP (string IP, int remotePort, int localPort, string password = "")
+ {
+ return GetNetworkManager().Connect(IP, remotePort, localPort, password);
+ }
+
+ CUSTOM private static NetworkConnectionError Internal_ConnectToGuid (string guid, string password)
+ {
+ RakNetGUID remoteGuid;
+ remoteGuid.FromString(guid.AsUTF8().c_str());
+ return GetNetworkManager().Connect(remoteGuid, 0, password);
+ }
+
+ CUSTOM private static NetworkConnectionError Internal_ConnectToIPs (string[] IP, int remotePort, int localPort, string password = "")
+ {
+ std::vector<string> ipvector;
+ for (int i=0; i < mono_array_length_safe(IP); i++)
+ {
+ ipvector.push_back(scripting_cpp_string_for(GetMonoArrayElement<MonoString*> (IP, i)));
+ }
+ return GetNetworkManager().Connect(ipvector, remotePort, localPort, password);
+ }
+
+ // Connect to the specified host (ip or domain name) and server port.
+ CSRAW public static NetworkConnectionError Connect (string IP, int remotePort, string password = "") { return Internal_ConnectToSingleIP(IP, remotePort, 0, password); }
+
+ // This function is exactly like Network.Connect but can accept an array of IP addresses.
+ CSRAW public static NetworkConnectionError Connect (string[] IPs, int remotePort, string password = ""){
+ return Internal_ConnectToIPs(IPs, remotePort, 0, password); }
+
+ // Connect to a server GUID. NAT punchthrough can only be performed this way.
+ CSRAW public static NetworkConnectionError Connect (string GUID, string password = "") {
+ return Internal_ConnectToGuid(GUID, password);
+ }
+
+ // Connect to the host represented by a [[HostData]] structure returned by the Master Server.
+ CSRAW public static NetworkConnectionError Connect (HostData hostData, string password = "") {
+ if(hostData == null)
+ throw new NullReferenceException();
+ if (hostData.guid.Length > 0 && hostData.useNat)
+ return Connect(hostData.guid, password);
+ else
+ return Connect(hostData.ip, hostData.port, password);
+ }
+
+ // Close all open connections and shuts down the network interface.
+ CUSTOM static void Disconnect(int timeout = 200) { GetNetworkManager().Disconnect(timeout); }
+
+ // Close the connection to another system.
+ CUSTOM static void CloseConnection (NetworkPlayer target, bool sendDisconnectionNotification) { GetNetworkManager().CloseConnection(target, sendDisconnectionNotification); }
+
+ // All connected players.
+ CUSTOM_PROP static NetworkPlayer[] connections {
+ MonoArray* array = mono_array_new (mono_domain_get (), MONO_COMMON.networkPlayer, GetNetworkManager().GetConnectionCount());
+ GetNetworkManager().GetConnections(&GetMonoArrayElement<int> (array,0));
+ return array;
+ }
+
+ CUSTOM private static int Internal_GetPlayer () { return GetNetworkManager().GetPlayerID(); }
+
+ // Get the local [[NetworkPlayer]] instance
+ CSRAW public static NetworkPlayer player { get { NetworkPlayer np; np.index = Internal_GetPlayer(); return np; } }
+
+ CUSTOM private static void Internal_AllocateViewID(out NetworkViewID viewID)
+ { *viewID = GetNetworkManager().AllocateViewID(); }
+
+ // Query for the next available network view ID number and allocate it (reserve).
+ CSRAW public static NetworkViewID AllocateViewID() { NetworkViewID val; Internal_AllocateViewID (out val); return val; }
+
+ // Network instantiate a prefab.
+ CSRAW
+ [TypeInferenceRule(TypeInferenceRules.TypeOfFirstArgument)]
+ CUSTOM public static Object Instantiate (Object prefab, Vector3 position, Quaternion rotation, int group) { return Scripting::ScriptingWrapperFor(GetNetworkManager().Instantiate (*prefab, position, rotation, group)); }
+
+ // Destroy the object associated with this view ID across the network.
+ CUSTOM static void Destroy (NetworkViewID viewID) { GetNetworkManager().DestroyDelayed(viewID); }
+
+ // Destroy the object across the network.
+ CSRAW public static void Destroy (GameObject gameObject) {
+ if (gameObject != null)
+ {
+ NetworkView view = gameObject.networkView;
+ if (view != null)
+ Destroy(view.viewID);
+ else
+ Debug.LogError("Couldn't destroy game object because no network view is attached to it.", gameObject);
+ }
+ }
+
+ // Destroy all the objects based on view IDs belonging to this player.
+ CUSTOM static void DestroyPlayerObjects(NetworkPlayer playerID) { GetNetworkManager().DestroyPlayerObjects(playerID); }
+
+ CUSTOM private static void Internal_RemoveRPCs(NetworkPlayer playerID, NetworkViewID viewID, uint channelMask) { GetNetworkManager().RemoveRPCs(playerID, viewID, channelMask); }
+
+ // Remove all [[RPC]] functions which belong to this player ID.
+ CSRAW public static void RemoveRPCs(NetworkPlayer playerID) { Internal_RemoveRPCs(playerID, NetworkViewID.unassigned, 0xFFFFFFFF); }
+ // Remove all [[RPC]] functions which belong to this player ID and were sent based on the given group.
+ CSRAW public static void RemoveRPCs(NetworkPlayer playerID, int group) { Internal_RemoveRPCs(playerID, NetworkViewID.unassigned, (uint)(1 << group)); }
+ // Remove the [[RPC]] function calls accociated with this view ID number.
+ CSRAW public static void RemoveRPCs(NetworkViewID viewID) { Internal_RemoveRPCs(NetworkPlayer.unassigned, viewID, 0xFFFFFFFF); }
+ // Remove all [[RPC]] functions which belong to given group number.
+ CSRAW public static void RemoveRPCsInGroup (int group) { Internal_RemoveRPCs(NetworkPlayer.unassigned, NetworkViewID.unassigned, (uint)(1 << group)); }
+
+ // Returns true if your peer type is client.
+ CUSTOM_PROP static bool isClient { return GetNetworkManager().IsClient(); }
+
+ // Returns true if your peer type is server.
+ CUSTOM_PROP static bool isServer { return GetNetworkManager().IsServer(); }
+
+ // The status of the peer type, i.e. if it is disconnected, connecting, server or client.
+ CUSTOM_PROP static NetworkPeerType peerType { return GetNetworkManager().GetPeerType(); }
+
+ // Set the level prefix which will then be prefixed to all network ViewID numbers.
+ CUSTOM static void SetLevelPrefix (int prefix) { return GetNetworkManager().SetLevelPrefix(prefix); }
+
+
+ // The last ping time to the given /player/ in milliseconds.
+ CUSTOM static int GetLastPing (NetworkPlayer player) { return GetNetworkManager().GetLastPing(player); }
+
+ // The last average ping time to the given /player/ in milliseconds.
+ CUSTOM static int GetAveragePing (NetworkPlayer player) { return GetNetworkManager().GetAveragePing(player); }
+
+ // The default send rate of network updates for all Network Views.
+ CUSTOM_PROP static float sendRate { return GetNetworkManager().GetSendRate(); } { GetNetworkManager().SetSendRate(value); }
+
+ // Enable or disable the processing of network messages.
+ CUSTOM_PROP static bool isMessageQueueRunning { return GetNetworkManager().GetMessageQueueRunning(); } { GetNetworkManager().SetMessageQueueRunning(value); }
+
+ // Enable or disables the reception of messages in a specific group number from a specific player.
+ CUSTOM static void SetReceivingEnabled (NetworkPlayer player, int group, bool enabled) { GetNetworkManager().SetReceivingGroupEnabled(player, group, enabled); }
+
+ CUSTOM private static void Internal_SetSendingGlobal(int group, bool enabled) { GetNetworkManager().SetSendingGroupEnabled(group, enabled); }
+
+ CUSTOM private static void Internal_SetSendingSpecific (NetworkPlayer player, int group, bool enabled) { GetNetworkManager().SetSendingGroupEnabled(player, group, enabled); }
+
+ // Enables or disables transmission of messages and [[RPC]] calls on a specific network group number.
+ CSRAW public static void SetSendingEnabled (int group, bool enabled) { Internal_SetSendingGlobal(group, enabled); }
+
+ // Enable or disable transmission of messages and [[RPC]] calls based on target network player as well as the network group.
+ CSRAW public static void SetSendingEnabled (NetworkPlayer player, int group, bool enabled) { Internal_SetSendingSpecific(player, group, enabled); }
+
+ CUSTOM private static void Internal_GetTime(out double t) { *t = GetNetworkManager().GetTime();}
+
+ // Get the current network time (seconds).
+ CSRAW public static double time { get { double t; Internal_GetTime(out t); return t; } }
+
+ // Called on the server whenever a new player has successfully connected.
+ CSNONE void OnPlayerConnected (NetworkPlayer player);
+
+ // Called on the server whenever a Network.InitializeServer was invoked and has completed.
+ CSNONE void OnServerInitialized ();
+
+ // Called on the client when you have successfully connected to a server
+ CSNONE void OnConnectedToServer ();
+
+ // Called on the server whenever a player is disconnected from the server
+ CSNONE void OnPlayerDisconnected (NetworkPlayer player);
+
+ // Called on client during disconnection from server, but also on the server when the connection has disconnected.
+ CSNONE void OnDisconnectedFromServer (NetworkDisconnection mode);
+
+ // Called on the client when a connection attempt fails for some reason.
+ CSNONE void OnFailedToConnect (NetworkConnectionError error);
+
+ // Called on objects which have been network instantiated with Network.Instantiate
+ CSNONE void OnNetworkInstantiate (NetworkMessageInfo info);
+
+ // Used to customize synchronization of variables in a script watched by a network view.
+ CSNONE void OnSerializeNetworkView (BitStream stream, NetworkMessageInfo info);
+
+ // Get or set the minimum number of ViewID numbers in the ViewID pool given to clients by the server.
+ CUSTOM_PROP static int minimumAllocatableViewIDs { return GetNetworkManager().GetMinimumAllocatableViewIDs(); } { GetNetworkManager().SetMinimumAllocatableViewIDs(value); }
+
+ OBSOLETE warning No longer needed. This is now explicitly set in the InitializeServer function call. It is implicitly set when calling Connect depending on if an IP/port combination is used (useNat=false) or a GUID is used(useNat=true).
+ CUSTOM_PROP static bool useNat { return GetNetworkManager().GetUseNat(); } { GetNetworkManager().SetUseNat(value); }
+
+ // The IP address of the NAT punchthrough facilitator.
+ CUSTOM_PROP static string natFacilitatorIP { return scripting_string_new(GetNetworkManager().GetFacilitatorAddress().ToString());} { GetNetworkManager().GetFacilitatorAddress(false).SetBinaryAddress(value.AsUTF8().c_str());}
+ // The port of the NAT punchthrough facilitator.
+ CUSTOM_PROP static int natFacilitatorPort { return GetNetworkManager().GetFacilitatorAddress().port;} {
+ #ifdef _XBOX // mircea@XBOX: for some reason, the xbox compiler chokes on the "simple" assign
+ SystemAddress& sa = GetNetworkManager().GetFacilitatorAddress(false); sa.port = value;
+ #else
+ GetNetworkManager().GetFacilitatorAddress(false).port = value;
+ #endif
+}
+
+ // Test this machines network connection.
+ CUSTOM static ConnectionTesterStatus TestConnection(bool forceTest = false) { return GetNetworkManager().TestConnection(false, forceTest); }
+
+ // Test the connecction specifically for NAT punchthrough connectivity.
+ CUSTOM static ConnectionTesterStatus TestConnectionNAT(bool forceTest = false) { return GetNetworkManager().TestConnection(true, forceTest); }
+
+ // The IP address of the connection tester used in Network.TestConnection.
+ CUSTOM_PROP static string connectionTesterIP { return scripting_string_new(GetNetworkManager().GetConnTesterAddress().ToString(false));} { SystemAddress address = GetNetworkManager().GetConnTesterAddress(); address.SetBinaryAddress(value.AsUTF8().c_str()); GetNetworkManager().SetConnTesterAddress(address);}
+ // The port of the connection tester used in Network.TestConnection.
+ CUSTOM_PROP static int connectionTesterPort { return GetNetworkManager().GetConnTesterAddress().port;} { SystemAddress address = GetNetworkManager().GetConnTesterAddress(); address.port = value; GetNetworkManager().SetConnTesterAddress(address);}
+
+ // Check if this machine has a public IP address.
+ CUSTOM static bool HavePublicAddress() { return CheckForPublicAddress(); }
+
+ // Set the maximum amount of connections/players allowed.
+ CUSTOM_PROP static int maxConnections {return GetNetworkManager().GetMaxConnections(); } { GetNetworkManager().SetMaxConnections(value); }
+
+ // The IP address of the proxy server.
+ CUSTOM_PROP static string proxyIP { return scripting_string_new(GetNetworkManager().GetProxyIP());} { GetNetworkManager().SetProxyIP(value);}
+
+ // The port of the proxy server.
+ CUSTOM_PROP static int proxyPort { return GetNetworkManager().GetProxyPort();} { GetNetworkManager().SetProxyPort(value);}
+
+ // Indicate if proxy support is needed, in which case traffic is relayed through the proxy server.
+ CUSTOM_PROP static bool useProxy { return GetNetworkManager().GetUseProxy(); } { GetNetworkManager().SetUseProxy(value); }
+
+ // Set the proxy server password.
+ CUSTOM_PROP static string proxyPassword
+ {
+ return scripting_string_new(GetNetworkManager().GetProxyPassword());
+ }
+ {
+ GetNetworkManager().SetProxyPassword(value);
+ }
+END
+
+// The BitStream class represents seralized variables, packed into a stream.
+CONDITIONAL ENABLE_NETWORK
+CLASS BitStream
+ CSRAW internal IntPtr m_Ptr;
+
+
+ C++RAW
+ #define GET ExtractMonoObjectData<BitstreamPacker*> (self)
+
+ C++RAW
+ #define CHECK_PTR if (ExtractMonoObjectData<BitstreamPacker*> (self) == NULL) Scripting::RaiseNullException("");
+
+ CUSTOM private void Serializeb (ref int value) { CHECK_PTR bool temp = value; GET->Serialize(temp); value = temp; }
+ CUSTOM private void Serializec (ref char value) {
+ // pre-Unity 2.5 serialized .NET chars as single bytes. Let's keep it backwards compatible for now.
+ // on big endian the lower order bits of the 2 byte .NET Char value contains the actual bits we need to send
+ #if UNITY_BIG_ENDIAN
+ char* hack = reinterpret_cast<char*>(&value)+1;
+ #else
+ char* hack = reinterpret_cast<char*>(&value);
+ #endif
+ CHECK_PTR GET->Serialize(*hack);
+ }
+ CUSTOM private void Serializes (ref short value) { CHECK_PTR GET->Serialize(value); }
+ CUSTOM private void Serializei (ref int value) { CHECK_PTR GET->Serialize(reinterpret_cast<SInt32&>(value)); }
+ CUSTOM private void Serializef (ref float value, float maximumDelta) { CHECK_PTR GET->Serialize(value, maximumDelta); }
+ CUSTOM private void Serializeq (ref Quaternion value, float maximumDelta) { CHECK_PTR GET->Serialize(value, maximumDelta); }
+ CUSTOM private void Serializev (ref Vector3 value, float maximumDelta) { CHECK_PTR GET->Serialize(value, maximumDelta); }
+ CUSTOM private void Serializen (ref NetworkViewID viewID) { CHECK_PTR GET->Serialize(viewID); }
+
+ ///*listonly*
+ CSRAW public void Serialize(ref bool value) { int cross = value ? 1 : 0; Serializeb( ref cross ); value = cross == 0 ? false : true; }
+ ///*listonly*
+ CSRAW public void Serialize(ref char value) { Serializec( ref value ); }
+ ///*listonly*
+ CSRAW public void Serialize(ref short value) { Serializes( ref value); }
+ ///*listonly*
+ CSRAW public void Serialize(ref int value) { Serializei( ref value); }
+ ///*listonly*
+ CSRAW public void Serialize(ref float value, float maxDelta = 0.00001F) { Serializef( ref value, maxDelta); }
+ ///*listonly*
+ CSRAW public void Serialize(ref Quaternion value, float maxDelta = 0.00001F) { Serializeq( ref value, maxDelta); }
+ ///*listonly*
+ CSRAW public void Serialize(ref Vector3 value, float maxDelta = 0.00001F) { Serializev( ref value, maxDelta); }
+ ///*listonly*
+ CSRAW public void Serialize(ref NetworkPlayer value) { int index = value.index; Serializei( ref index ); value.index = index;}
+ // Serializes different types of variables.
+ CSRAW public void Serialize(ref NetworkViewID viewID) { Serializen(ref viewID); }
+
+ // Is the BitStream currently being read? (RO)
+ CUSTOM_PROP bool isReading { CHECK_PTR return GET->IsReading(); }
+ // Is the BitStream currently being written? (RO)
+ CUSTOM_PROP bool isWriting { CHECK_PTR return GET->IsWriting(); }
+
+ CUSTOM private void Serialize (ref string value)
+ {
+ CHECK_PTR
+ std::string cppValue;
+ if (GET->IsWriting())
+ cppValue = value;
+
+ GET->Serialize(cppValue);
+
+ if (GET->IsReading())
+ value.str = scripting_string_new(cppValue);
+ }
+
+ C++RAW
+ #undef GET
+
+ C++RAW
+ #undef CHECK
+
+END
+
+
+// Attribute for setting up RPC functions.
+CONDITIONAL ENABLE_NETWORK
+CSRAW [AttributeUsage(AttributeTargets.Method, AllowMultiple=true)]
+CLASS RPC : Attribute
+END
+
+// This is the data structure for holding individual host information.
+CONDITIONAL ENABLE_NETWORK
+CSRAW [StructLayout (LayoutKind.Sequential)]
+CLASS HostData
+ CSRAW private int m_Nat;
+ CSRAW private string m_GameType;
+ CSRAW private string m_GameName;
+ CSRAW private int m_ConnectedPlayers;
+ CSRAW private int m_PlayerLimit;
+ CSRAW private string[] m_IP;
+ CSRAW private int m_Port;
+ CSRAW private int m_PasswordProtected;
+ CSRAW private string m_Comment;
+ CSRAW private string m_GUID;
+
+ // Does this server require NAT punchthrough?
+
+ CSRAW public bool useNat { get { return m_Nat != 0; } set { m_Nat = value ? 1 : 0; } }
+ // The type of the game (like "MyUniqueGameType")
+ CSRAW public string gameType { get { return m_GameType; } set { m_GameType = value; } }
+
+ // The name of the game (like John Doe's Game)
+ CSRAW public string gameName { get { return m_GameName; } set { m_GameName = value; } }
+
+ // Currently connected players
+ CSRAW public int connectedPlayers { get { return m_ConnectedPlayers; } set { m_ConnectedPlayers = value; } }
+
+ // Maximum players limit
+ CSRAW public int playerLimit { get { return m_PlayerLimit; } set { m_PlayerLimit = value; } }
+ // Server IP address
+ CSRAW public string[] ip { get { return m_IP; } set { m_IP = value; } }
+
+ // Server port
+ CSRAW public int port { get { return m_Port; } set { m_Port = value; } }
+
+ // Does the server require a password?
+ CSRAW public bool passwordProtected { get { return m_PasswordProtected != 0; } set { m_PasswordProtected = value ? 1 : 0; } }
+
+ // A miscellaneous comment (can hold data)
+ CSRAW public string comment { get { return m_Comment; } set { m_Comment = value; } }
+
+ // The GUID of the host, needed when connecting with NAT punchthrough.
+ CSRAW public string guid { get { return m_GUID; } set { m_GUID = value; } }
+END
+
+C++RAW
+
+struct HostDataCpp
+{
+ int useNat;
+ MonoString* gameType;
+ MonoString* gameName;
+ int connectedPlayers;
+ int playerLimit;
+ MonoArray* IP;
+ int port;
+ int passwordProtected;
+ MonoString* comment;
+ MonoString* guid;
+};
+
+
+// The Master Server is used to make matchmaking between servers and clients easy.
+CONDITIONAL ENABLE_NETWORK
+CLASS MasterServer
+
+ // Called on clients or servers when there is a problem connecting to the master server.
+ CSNONE void OnFailedToConnectToMasterServer (NetworkConnectionError error);
+
+ // Called on clients or servers when reporting events from the MasterServer.
+ CSNONE void OnMasterServerEvent (MasterServerEvent msEvent);
+
+ // The IP address of the master server.
+ CUSTOM_PROP static string ipAddress { return scripting_string_new(GetMasterServerInterface().GetIPAddress());} { GetMasterServerInterface().SetIPAddress(value);}
+
+ // The connection port of the master server.
+ CUSTOM_PROP static int port { return GetMasterServerInterface().GetPort();} { GetMasterServerInterface().SetPort(value);}
+
+ // Request a host list from the master server.
+ CUSTOM static void RequestHostList(string gameTypeName) { GetMasterServerInterface().QueryHostList(gameTypeName); }
+
+ // Check for the latest host list received by using MasterServer.RequestHostList.
+ CUSTOM static HostData[] PollHostList()
+ {
+ const std::vector<HostData>& data = GetMasterServerInterface().PollHostList();
+ MonoClass* hostType = GetMonoManager ().GetCommonClasses ().hostData;
+
+ //if (data == NULL) return NULL;
+
+ //count = 0;
+
+ MonoArray* array = mono_array_new (mono_domain_get (), hostType, data.size());
+ for (int i = 0; i < data.size(); i++)
+ {
+ MonoObject* element = mono_object_new(mono_domain_get(), hostType);
+ GetMonoArrayElement<MonoObject*> (array, i) = element;
+ HostDataCpp& dst = ExtractMonoObjectData<HostDataCpp> (element);
+ HostData src = data[i];
+
+ dst.useNat = src.useNat;
+ dst.gameType = scripting_string_new(src.gameType);
+ dst.gameName = scripting_string_new(src.gameName);
+ dst.connectedPlayers = src.connectedPlayers;
+ dst.playerLimit = src.playerLimit;
+ MonoArray* ips = mono_array_new (mono_domain_get (), mono_get_string_class(), src.IP.size());
+ for (int i = 0; i < src.IP.size(); i++)
+ {
+ GetMonoArrayElement<MonoString*> (ips, i) = scripting_string_new(src.IP[i]);
+ }
+ dst.IP = ips;
+ dst.port = src.port;
+ dst.passwordProtected = src.passwordProtected;
+ dst.comment = scripting_string_new(src.comment);
+ dst.guid = scripting_string_new(src.guid);
+ }
+
+ return array;
+ }
+
+ // Register this server on the master server.
+ CUSTOM static void RegisterHost(string gameTypeName, string gameName, string comment = "") { GetMasterServerInterface().RegisterHost(gameTypeName, gameName, comment); }
+
+ // Unregister this server from the master server.
+ CUSTOM static void UnregisterHost() { GetMasterServerInterface().UnregisterHost(); }
+
+ // Clear the host list which was received by MasterServer.PollHostList.
+ CUSTOM static void ClearHostList() { GetMasterServerInterface().ClearHostList(); }
+
+ // Set the minimum update rate for master server host information update.
+ CUSTOM_PROP static int updateRate { return GetMasterServerInterface().GetUpdateRate();} { GetMasterServerInterface().SetUpdateRate(value);}
+
+ // Report this machine as a dedicated server.
+ CUSTOM_PROP static bool dedicatedServer { return GetMasterServerInterface().GetDedicatedServer(); } { GetMasterServerInterface().SetDedicatedServer(value); }
+END
+
+// This data structure contains information on a message just received from the network.
+CONDITIONAL ENABLE_NETWORK
+STRUCT NetworkMessageInfo
+ CSRAW
+ double m_TimeStamp;
+ NetworkPlayer m_Sender;
+ NetworkViewID m_ViewID;
+
+ // The time stamp when the Message was sent in seconds.
+ CSRAW public double timestamp { get { return m_TimeStamp; } }
+
+ // The player who sent this network message (owner)
+ CSRAW public NetworkPlayer sender { get { return m_Sender; } }
+
+ // The [[NetworkView]] who sent this message
+ CSRAW public NetworkView networkView
+ {
+ get
+ {
+ if (m_ViewID == NetworkViewID.unassigned)
+ {
+ Debug.LogError("No NetworkView is assigned to this NetworkMessageInfo object. Note that this is expected in OnNetworkInstantiate().");
+ return NullNetworkView();
+ }
+ return NetworkView.Find(m_ViewID);
+ }
+ }
+
+ CUSTOM internal NetworkView NullNetworkView() { return Scripting::ScriptingObjectNULL (ScriptingClassFor (NetworkView)); }
+
+END
+
+
+CSRAW }
+#endif
+
diff --git a/Runtime/Export/ParticleSystemBindings.txt b/Runtime/Export/ParticleSystemBindings.txt
new file mode 100644
index 0000000..f3d3a07
--- /dev/null
+++ b/Runtime/Export/ParticleSystemBindings.txt
@@ -0,0 +1,413 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+//#include "Runtime/Graphics/ParticleSystem/ParticleCollisionEvents.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystem.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemRenderer.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Scripting/Scripting.h"
+#if ENABLE_PHYSICS
+#include "Runtime/Dynamics/Collider.h"
+#endif
+
+#if UNITY_EDITOR
+#include "Editor/Src/ParticleSystem/ParticleSystemEditor.h"
+#endif
+
+using namespace Unity;
+using namespace std;
+
+CSRAW
+using System;
+using System.Collections.Generic;
+
+namespace UnityEngine
+{
+
+C++RAW
+
+// The rendering mode for particle systems (Shuriken).
+ENUM ParticleSystemRenderMode
+ // Render particles as billboards facing the player. (Default)
+ Billboard = 0,
+ // Stretch particles in the direction of motion.
+ Stretch = 1,
+ // Render particles as billboards always facing up along the y-Axis.
+ HorizontalBillboard = 2,
+ // Render particles as billboards always facing the player, but not pitching along the x-Axis.
+ VerticalBillboard = 3,
+
+ // Render particles as meshes.
+ Mesh = 4
+END
+
+// The simulation space for particle systems (Shuriken).
+ENUM ParticleSystemSimulationSpace
+ // Use local simulation space. (Default)
+ Local = 0,
+ // Use world simulation space.
+ World = 1
+END
+
+// Script interface for particle systems (Shuriken).
+
+CLASS ParticleSystem : Component
+
+// Script interface for a Particle
+STRUCT Particle
+ CSRAW
+ private Vector3 m_Position;
+ private Vector3 m_Velocity;
+ private Vector3 m_AnimatedVelocity;
+ private Vector3 m_AxisOfRotation;
+ private float m_Rotation;
+ private float m_AngularVelocity;
+ private float m_Size;
+ private Color32 m_Color;
+ private UInt32 m_RandomSeed;
+ private float m_Lifetime;
+ private float m_StartLifetime;
+ private float m_EmitAccumulator0;
+ private float m_EmitAccumulator1;
+
+ // The position of the particle.
+ CSRAW public Vector3 position { get { return m_Position; } set { m_Position = value; } }
+
+ // The velocity of the particle.
+ CSRAW public Vector3 velocity { get { return m_Velocity; } set { m_Velocity = value; } }
+
+ // The lifetime of the particle.
+ CSRAW public float lifetime { get { return m_Lifetime; } set { m_Lifetime = value; } }
+
+ // The starting lifetime of the particle.
+ CSRAW public float startLifetime { get { return m_StartLifetime; } set { m_StartLifetime = value; } }
+
+ // The size of the particle.
+ CSRAW public float size { get { return m_Size; } set { m_Size = value; } }
+
+ // The rotation axis of the particle.
+ CSRAW public Vector3 axisOfRotation { get { return m_AxisOfRotation; } set { m_AxisOfRotation = value; } }
+
+ // The rotation of the particle.
+ CSRAW public float rotation { get { return m_Rotation * Mathf.Rad2Deg; } set { m_Rotation = value * Mathf.Deg2Rad; } }
+
+ // The angular velocity of the particle.
+ CSRAW public float angularVelocity { get { return m_AngularVelocity * Mathf.Rad2Deg; } set { m_AngularVelocity = value * Mathf.Deg2Rad; } }
+
+ // The color of the particle.
+ CSRAW public Color32 color { get { return m_Color; } set { m_Color = value; } }
+
+ // The random value of the particle.
+ CONDITIONAL !UNITY_FLASH
+ OBSOLETE warning randomValue property is deprecated. Use randomSeed instead to control random behavior of particles.
+ CSRAW public float randomValue { get { return BitConverter.ToSingle(BitConverter.GetBytes(m_RandomSeed), 0); } set { m_RandomSeed = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); } }
+
+ // The random seed of the particle.
+ CSRAW public UInt32 randomSeed { get { return m_RandomSeed; } set { m_RandomSeed = value; } }
+END
+
+CONDITIONAL ENABLE_PHYSICS
+CUSTOM static internal Collider InstanceIDToCollider(int instanceID)
+ {
+ return instanceID != 0 ? Scripting::ScriptingWrapperFor (PPtr<Collider> (instanceID)) : SCRIPTING_NULL;
+ }
+
+// Script interface for a Particle collision event
+STRUCT CollisionEvent
+ CSRAW
+ private Vector3 m_Intersection;
+ private Vector3 m_Normal;
+ private Vector3 m_Velocity;
+ private int m_ColliderInstanceID;
+
+ CSRAW public Vector3 intersection { get { return m_Intersection; } }
+ CSRAW public Vector3 normal { get { return m_Normal; } }
+ CSRAW public Vector3 velocity { get { return m_Velocity; } }
+ CONDITIONAL ENABLE_PHYSICS
+ CSRAW public Collider collider { get { return InstanceIDToCollider(m_ColliderInstanceID); } }
+END
+
+ // Start delay in seconds.
+ SYNC_JOBS AUTO_PROP float startDelay GetStartDelay SetStartDelay
+
+ // Is the particle system playing right now ?
+ SYNC_JOBS AUTO_PROP bool isPlaying IsPlaying
+
+ // Is the particle system stopped right now ?
+ SYNC_JOBS AUTO_PROP bool isStopped IsStopped
+
+ // Is the particle system paused right now ?
+ SYNC_JOBS AUTO_PROP bool isPaused IsPaused
+
+ // Is the particle system looping?
+ SYNC_JOBS AUTO_PROP bool loop GetLoop SetLoop
+
+ // If set to true, the particle system will automatically start playing on startup.
+ SYNC_JOBS AUTO_PROP bool playOnAwake GetPlayOnAwake SetPlayOnAwake
+
+ // Playback position in seconds.
+ SYNC_JOBS AUTO_PROP float time GetSecPosition SetSecPosition
+
+ // The duration of the particle system in seconds (Read Only)
+ SYNC_JOBS AUTO_PROP float duration GetLengthInSec
+
+ // The playback speed of the particle system. 1 is normal playback speed.
+ SYNC_JOBS AUTO_PROP float playbackSpeed GetPlaybackSpeed SetPlaybackSpeed
+
+ // The current number of particles (Read Only).
+ SYNC_JOBS AUTO_PROP int particleCount GetParticleCount
+
+ // Safe array size for the collision event array used with GetCollisionEvents (Read Only).
+ AUTO_PROP int safeCollisionEventSize GetSafeCollisionEventSize
+
+ // When set to false, the particle system will not emit particles
+ SYNC_JOBS AUTO_PROP bool enableEmission GetEnableEmission SetEnableEmission
+
+ // The rate of emission
+ SYNC_JOBS AUTO_PROP float emissionRate GetEmissionRate SetEmissionRate
+
+ // The initial speed of particles when emitted. When using curves, this values acts as a scale on the curve.
+ SYNC_JOBS AUTO_PROP float startSpeed GetStartSpeed SetStartSpeed
+
+ // The initial size of particles when emitted. When using curves, this values acts as a scale on the curve.
+ SYNC_JOBS AUTO_PROP float startSize GetStartSize SetStartSize
+
+ // The initial color of particles when emitted.
+ SYNC_JOBS AUTO_PROP Color startColor GetStartColor SetStartColor
+
+ // The initial rotation of particles when emitted. When using curves, this values acts as a scale on the curve.
+ SYNC_JOBS AUTO_PROP float startRotation GetStartRotation SetStartRotation
+
+ // The total lifetime in seconds that particles will have when emitted. When using curves, this values acts as a scale on the curve. This value is set in the particle when it is create by the particle system.
+ SYNC_JOBS AUTO_PROP float startLifetime GetStartLifeTime SetStartLifeTime
+
+ // Scale being applied to the gravity defined by [[Physics.gravity]].
+ SYNC_JOBS AUTO_PROP float gravityModifier GetGravityModifier SetGravityModifier
+
+ // Maximum number of particles.
+ SYNC_JOBS AUTO_PROP int maxParticles GetMaxNumParticles SetMaxNumParticles
+
+ // Selects the space in which to simulate particles; can be local (default) or world.
+ SYNC_JOBS AUTO_PROP ParticleSystemSimulationSpace simulationSpace GetSimulationSpace SetSimulationSpace
+
+ // Random seed used for the particle system emission. If set to 0, it will be assigned a random value on awake.
+ SYNC_JOBS AUTO_PROP UInt32 randomSeed GetRandomSeed SetRandomSeed
+
+ // Set the particles of this particle system. /size/ is the number of particles that is set.
+ SYNC_JOBS CUSTOM void SetParticles (ParticleSystem.Particle[] particles, int size)
+ {
+ unsigned int actualSize = GetScriptingArraySize(particles);
+ if (size < 0 || actualSize < size)
+ size = actualSize;
+
+ self->SetParticlesExternal (Scripting::GetScriptingArrayStart<ParticleSystemParticle>(particles), size);
+ }
+
+ // Get the particles of this particle system. Returns the number of particles written to the input particle array.
+ SYNC_JOBS CUSTOM int GetParticles (ParticleSystem.Particle[] particles)
+ {
+ int size = std::min<unsigned int>(self->GetParticleCount(), GetScriptingArraySize(particles));
+ self->GetParticlesExternal (Scripting::GetScriptingArrayStart<ParticleSystemParticle>(particles), size);
+ return size;
+ }
+
+ // Get the particle collision events recorded for this particle system. Returns the number of particles written to the input collision event array.
+ CUSTOM int GetCollisionEvents (GameObject go, ParticleSystem.CollisionEvent[] collisionEvents)
+ {
+ return self->GetCollisionEventsExternal (go->GetInstanceID (), Scripting::GetScriptingArrayStart<MonoParticleCollisionEvent>(collisionEvents), GetScriptingArraySize(collisionEvents));
+ }
+
+ SYNC_JOBS CUSTOM private void Internal_Simulate (float t, bool restart) { self->Simulate (t, restart); }
+ SYNC_JOBS CUSTOM private void Internal_Play () { self->Play (); }
+ SYNC_JOBS CUSTOM private void Internal_Stop () { self->Stop (); }
+ SYNC_JOBS CUSTOM private void Internal_Pause () { self->Pause (); }
+ SYNC_JOBS CUSTOM private void Internal_Clear () { self->Clear (); }
+ SYNC_JOBS CUSTOM private bool Internal_IsAlive () { return self->IsAlive(); }
+
+ // Fastforwards the particle system by simulating particles over given period of time, then pauses it.
+ CSRAW public void Simulate (float t, bool withChildren = true, bool restart = true)
+ {
+ if (withChildren)
+ {
+ ParticleSystem[] emitters = GetParticleSystems (this);
+ foreach (var emitter in emitters)
+ emitter.Internal_Simulate (t, restart);
+ }
+ else
+ {
+ Internal_Simulate (t, restart);
+ }
+ }
+
+ // Plays the particle system.
+ CSRAW public void Play (bool withChildren = true)
+ {
+ if (withChildren)
+ {
+ ParticleSystem[] emitters = GetParticleSystems (this);
+ foreach (var emitter in emitters)
+ emitter.Internal_Play ();
+ }
+ else
+ {
+ Internal_Play ();
+ }
+ }
+
+ // Stops playing the particle system.
+ CSRAW public void Stop (bool withChildren = true)
+ {
+ if (withChildren)
+ {
+ ParticleSystem[] emitters = GetParticleSystems (this);
+ foreach (var emitter in emitters)
+ emitter.Internal_Stop ();
+ }
+ else
+ {
+ Internal_Stop ();
+ }
+ }
+
+ // Pauses playing the particle system.
+ CSRAW public void Pause (bool withChildren = true)
+ {
+ if (withChildren)
+ {
+ ParticleSystem[] emitters = GetParticleSystems (this);
+ foreach (var emitter in emitters)
+ emitter.Internal_Pause ();
+ }
+ else
+ {
+ Internal_Pause ();
+ }
+
+ }
+
+ // Remove all particles in the particle system
+ CSRAW public void Clear (bool withChildren = true)
+ {
+ if (withChildren)
+ {
+ ParticleSystem[] emitters = GetParticleSystems (this);
+ foreach (var emitter in emitters)
+ emitter.Internal_Clear ();
+ }
+ else
+ {
+ Internal_Clear ();
+ }
+ }
+
+ // Is the particle system done emitting particles and are all particles dead?
+ CSRAW public bool IsAlive (bool withChildren = true)
+ {
+ if (withChildren)
+ {
+ ParticleSystem[] emitters = GetParticleSystems (this);
+ foreach (var emitter in emitters)
+ if(emitter.Internal_IsAlive())
+ return true;
+ return false;
+ }
+
+ return this.Internal_IsAlive ();
+ }
+
+ // Emit /count/ particles immediately.
+ SYNC_JOBS AUTO void Emit (int count);
+
+ // Emit a single particle with given parameters.
+ CSRAW public void Emit(Vector3 position, Vector3 velocity, float size, float lifetime, Color32 color)
+ {
+ ParticleSystem.Particle particle = new ParticleSystem.Particle();
+ particle.position = position;
+ particle.velocity = velocity;
+ particle.lifetime = lifetime;
+ particle.startLifetime = lifetime;
+ particle.size = size;
+ particle.rotation = 0.0f;
+ particle.angularVelocity = 0.0f;
+ particle.color = color;
+ particle.randomSeed = 5;
+ Internal_Emit(ref particle);
+ }
+
+ // Emit a single particle.
+ CSRAW public void Emit(ParticleSystem.Particle particle)
+ {
+ Internal_Emit(ref particle);
+ }
+
+ SYNC_JOBS CUSTOM private void Internal_Emit (ref ParticleSystem.Particle particle)
+ {
+ self->EmitParticleExternal (&particle);
+ }
+
+
+ // Returns a list with 'root' and all its direct children.
+ CSRAW static internal ParticleSystem[] GetParticleSystems (ParticleSystem root)
+ {
+ if (!root)
+ return null;
+
+ List<ParticleSystem> particleSystems = new List<ParticleSystem>();
+ particleSystems.Add(root);
+ GetDirectParticleSystemChildrenRecursive(root.transform, particleSystems);
+ return particleSystems.ToArray();
+ }
+
+ // Adds only active Particle Systems
+ CSRAW static private void GetDirectParticleSystemChildrenRecursive(Transform transform, List<ParticleSystem> particleSystems)
+ {
+ foreach (Transform childTransform in transform)
+ {
+ ParticleSystem ps = childTransform.gameObject.GetComponent<ParticleSystem>();
+ if (ps != null)
+ {
+ // Note: we do not check for if the gameobject is active (we want inactive particle systems as well due prefabs)
+ particleSystems.Add (ps);
+ GetDirectParticleSystemChildrenRecursive(childTransform, particleSystems);
+ }
+ }
+ }
+
+ CONDITIONAL UNITY_EDITOR
+ //*undocumented
+ CUSTOM internal void SetupDefaultType (int type)
+ {
+ ParticleSystemEditor::SetupDefaultParticleSystemType(*self, (ParticleSystemSubType)type);
+ }
+END
+
+// Renders particles on to the screen (Shuriken).
+CLASS ParticleSystemRenderer : Renderer
+
+ // How particles are drawn.
+ AUTO_PROP ParticleSystemRenderMode renderMode GetRenderMode SetRenderMode
+
+ // How much are the particles stretched in their direction of motion.
+ AUTO_PROP float lengthScale GetLengthScale SetLengthScale
+
+ // How much are the particles strectched depending on "how fast they move"
+ AUTO_PROP float velocityScale GetVelocityScale SetVelocityScale
+
+ // How much are the particles strected depending on the [[Camera]]'s speed.
+ AUTO_PROP float cameraVelocityScale GetCameraVelocityScale SetCameraVelocityScale
+
+ // Clamp the maximum particle size.
+ AUTO_PROP float maxParticleSize GetMaxParticleSize SetMaxParticleSize
+
+ // Mesh used as particle instead of billboarded texture
+ AUTO_PTR_PROP Mesh mesh GetMesh SetMesh
+
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM_PROP internal bool editorEnabled { return self->GetEditorEnabled();} {self->SetEditorEnabled(value);}
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/PlayerPrefsBindings.txt b/Runtime/Export/PlayerPrefsBindings.txt
new file mode 100644
index 0000000..331886e
--- /dev/null
+++ b/Runtime/Export/PlayerPrefsBindings.txt
@@ -0,0 +1,115 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Profiler/ProfilerHistory.h"
+#include "Runtime/Allocator/MemoryManager.h"
+#include "Runtime/Animation/Animation.h"
+#include "Runtime/Utilities/PlayerPrefs.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/GetComponent.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+
+using namespace Unity;
+
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngineInternal;
+
+namespace UnityEngine
+{
+
+
+// This exception is thrown by the [[PlayerPrefs]] class in the Web player if the preference file would exceed the allotted storage space when setting a value.
+CLASS PlayerPrefsException : Exception
+
+ //*undocumented*
+ CSRAW public PlayerPrefsException(string error) : base(error) {}
+END
+
+// Stores and accesses player preferences between game sessions.
+CLASS PlayerPrefs
+ CUSTOM private static bool TrySetInt (string key, int value) { return (int)PlayerPrefs::SetInt (key, value); }
+ CUSTOM private static bool TrySetFloat (string key, float value) { return (int)PlayerPrefs::SetFloat (key, value); }
+ CUSTOM private static bool TrySetSetString (string key, string value) { return (int)PlayerPrefs::SetString (key, value); }
+
+ // Sets the value of the preference identified by /key/.
+ CSRAW public static void SetInt (string key, int value) { if( ! TrySetInt(key, value) ) throw new PlayerPrefsException("Could not store preference value"); }
+
+
+ // Returns the value corresponding to /key/ in the preference file if it exists.
+ CUSTOM static int GetInt (string key, int defaultValue = 0) { return PlayerPrefs::GetInt (key, defaultValue); }
+
+ // Sets the value of the preference identified by /key/.
+ CSRAW public static void SetFloat (string key, float value) { if( ! TrySetFloat(key, value) ) throw new PlayerPrefsException("Could not store preference value"); }
+
+ // Returns the value corresponding to /key/ in the preference file if it exists.
+ CUSTOM static float GetFloat (string key, float defaultValue = 0.0F) { return PlayerPrefs::GetFloat (key, defaultValue); }
+
+ // Sets the value of the preference identified by /key/.
+ CSRAW public static void SetString (string key, string value) { if( ! TrySetSetString(key, value) ) throw new PlayerPrefsException("Could not store preference value"); }
+
+
+ // Returns the value corresponding to /key/ in the preference file if it exists.
+ CUSTOM static string GetString (string key, string defaultValue = "") { return scripting_string_new (PlayerPrefs::GetString (key, defaultValue)); }
+
+ // Returns true if /key/ exists in the preferences.
+ CUSTOM static bool HasKey(string key) { return (int)PlayerPrefs::HasKey(key); }
+
+ // Removes /key/ and its corresponding value from the preferences.
+ CUSTOM static void DeleteKey(string key) { PlayerPrefs::DeleteKey(key); }
+
+ // Removes all keys and values from the preferences. Use with caution.
+ CUSTOM static void DeleteAll() { PlayerPrefs::DeleteAll(); }
+
+ // Writes all modified preferences to disk.
+ CUSTOM static void Save() { PlayerPrefs::Sync(); }
+
+
+ // Transfers PlayerPrefs content to and from an array of bytes. Can be useful to implement save/load game functionality.
+ CONDITIONAL UNITY_WII_API
+ CUSTOM_PROP static byte[] rawData
+ {
+ PlayerPrefs::RawData data;
+ #if (UNITY_WIN || UNITY_WII)
+ if (PlayerPrefs::GetRawData(data))
+ {
+ return CreateScriptingArray(&data[0], data.size(), GetMonoManager().GetCommonClasses().byte);
+ }
+ else
+ {
+ return CreateEmptyStructArray(GetMonoManager().GetCommonClasses().byte);
+ }
+ #else
+ return CreateEmptyStructArray(GetMonoManager().GetCommonClasses().byte);
+ #endif
+ }
+ {
+ size_t size = GetScriptingArraySize(value);
+ UInt8 const* begin = Scripting::GetScriptingArrayStart<UInt8>(value);
+ UInt8 const* end = begin + size;
+ PlayerPrefs::RawData data (begin, end);
+ #if (UNITY_WIN || UNITY_WII)
+ if (!PlayerPrefs::SetRawData(data))
+ printf_console ("Failed to load PlayerPrefs from rawData (size:%d)\n", size);
+ #endif
+ }
+
+END
+
+CSRAW }
+
diff --git a/Runtime/Export/PropertyAttribute.cs b/Runtime/Export/PropertyAttribute.cs
new file mode 100644
index 0000000..f008483
--- /dev/null
+++ b/Runtime/Export/PropertyAttribute.cs
@@ -0,0 +1,46 @@
+using System;
+
+namespace UnityEngine
+{
+
+// Base class to derive custom property attributes from. Use this to create custom attributes for script variables.
+[System.AttributeUsage (AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
+public abstract class PropertyAttribute : Attribute
+{
+}
+
+// Attribute used to make a float or int variable in a script be restricted to a specific range.
+[System.AttributeUsage (AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
+public sealed class RangeAttribute : PropertyAttribute
+{
+ public readonly float min;
+ public readonly float max;
+
+ // Attribute used to make a float or int variable in a script be restricted to a specific range.
+ public RangeAttribute (float min, float max)
+ {
+ this.min = min;
+ this.max = max;
+ }
+}
+
+
+// Attribute to make a string be edited with a multi-line textfield
+[System.AttributeUsage (AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
+public sealed class MultilineAttribute : PropertyAttribute
+{
+ public readonly int lines;
+
+ public MultilineAttribute ()
+ {
+ this.lines = 3;
+ }
+ // Attribute used to make a string value be shown in a multiline textarea.
+ public MultilineAttribute (int lines)
+ {
+ this.lines = lines;
+ }
+
+}
+
+}
diff --git a/Runtime/Export/ScriptAssets.txt b/Runtime/Export/ScriptAssets.txt
new file mode 100644
index 0000000..6002497
--- /dev/null
+++ b/Runtime/Export/ScriptAssets.txt
@@ -0,0 +1,33 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/TextAsset.h"
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+
+namespace UnityEngine
+{
+
+// Text file assets.
+NONSEALED_CLASS TextAsset : Object
+ // The text contents of the .txt file as a string. (RO)
+ CUSTOM_PROP string text { return scripting_string_new(self->GetScript().c_str()); }
+
+ // The raw bytes of the text asset. (RO)
+
+ CUSTOM_PROP byte[] bytes { return CreateScriptingArray( self->GetScript().c_str(), self->GetScript().size(), GetScriptingManager().GetCommonClasses().byte ); }
+
+ CSRAW public override string ToString() { return text; }
+END
+
+
+CSRAW
+}
diff --git a/Runtime/Export/ScriptRefImages/AlignPosition.png b/Runtime/Export/ScriptRefImages/AlignPosition.png
new file mode 100644
index 0000000..e352bb4
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/AlignPosition.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/Aligner.png b/Runtime/Export/ScriptRefImages/Aligner.png
new file mode 100644
index 0000000..fe7de69
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/Aligner.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/ArrowCap.png b/Runtime/Export/ScriptRefImages/ArrowCap.png
new file mode 100644
index 0000000..887b1ab
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/ArrowCap.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/BeginEndGUI.png b/Runtime/Export/ScriptRefImages/BeginEndGUI.png
new file mode 100644
index 0000000..b74a062
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/BeginEndGUI.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/BeginEndHorizontalExample.png b/Runtime/Export/ScriptRefImages/BeginEndHorizontalExample.png
new file mode 100644
index 0000000..f586c17
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/BeginEndHorizontalExample.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/BeginEndScrollView.png b/Runtime/Export/ScriptRefImages/BeginEndScrollView.png
new file mode 100644
index 0000000..1cf1ac5
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/BeginEndScrollView.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/BeginEndVerticalExample.png b/Runtime/Export/ScriptRefImages/BeginEndVerticalExample.png
new file mode 100644
index 0000000..91fcc44
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/BeginEndVerticalExample.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/ButtonHandle.png b/Runtime/Export/ScriptRefImages/ButtonHandle.png
new file mode 100644
index 0000000..d1a7f84
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/ButtonHandle.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/CameraViewer.png b/Runtime/Export/ScriptRefImages/CameraViewer.png
new file mode 100644
index 0000000..a2cef2c
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/CameraViewer.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/CircleCap.png b/Runtime/Export/ScriptRefImages/CircleCap.png
new file mode 100644
index 0000000..c33631e
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/CircleCap.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/ClearEditorPrefs.png b/Runtime/Export/ScriptRefImages/ClearEditorPrefs.png
new file mode 100644
index 0000000..7783168
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/ClearEditorPrefs.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/CloneObjects.png b/Runtime/Export/ScriptRefImages/CloneObjects.png
new file mode 100644
index 0000000..7b244e2
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/CloneObjects.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/ConeCap.png b/Runtime/Export/ScriptRefImages/ConeCap.png
new file mode 100644
index 0000000..15be1ab
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/ConeCap.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/CubeCap.png b/Runtime/Export/ScriptRefImages/CubeCap.png
new file mode 100644
index 0000000..ac1c82e
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/CubeCap.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/CustomEditor.png b/Runtime/Export/ScriptRefImages/CustomEditor.png
new file mode 100644
index 0000000..dff5638
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/CustomEditor.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/CustomPropertyDrawer_Class.png b/Runtime/Export/ScriptRefImages/CustomPropertyDrawer_Class.png
new file mode 100644
index 0000000..c158ada
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/CustomPropertyDrawer_Class.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/CylinderCap.png b/Runtime/Export/ScriptRefImages/CylinderCap.png
new file mode 100644
index 0000000..9feebe9
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/CylinderCap.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/DiscHandle.png b/Runtime/Export/ScriptRefImages/DiscHandle.png
new file mode 100644
index 0000000..ece5ce0
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/DiscHandle.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/DotCap.png b/Runtime/Export/ScriptRefImages/DotCap.png
new file mode 100644
index 0000000..2e7c968
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/DotCap.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/DrawAAPolyLine.png b/Runtime/Export/ScriptRefImages/DrawAAPolyLine.png
new file mode 100644
index 0000000..bfdd9da
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/DrawAAPolyLine.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/DrawBezier.png b/Runtime/Export/ScriptRefImages/DrawBezier.png
new file mode 100644
index 0000000..e3c1434
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/DrawBezier.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/DrawLine.png b/Runtime/Export/ScriptRefImages/DrawLine.png
new file mode 100644
index 0000000..7a0b378
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/DrawLine.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/DrawPolyLine.png b/Runtime/Export/ScriptRefImages/DrawPolyLine.png
new file mode 100644
index 0000000..bfdd9da
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/DrawPolyLine.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/DrawSolidArc.png b/Runtime/Export/ScriptRefImages/DrawSolidArc.png
new file mode 100644
index 0000000..df308aa
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/DrawSolidArc.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/DrawSolidDisc.png b/Runtime/Export/ScriptRefImages/DrawSolidDisc.png
new file mode 100644
index 0000000..07bd01d
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/DrawSolidDisc.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/DrawSolidRectangle.png b/Runtime/Export/ScriptRefImages/DrawSolidRectangle.png
new file mode 100644
index 0000000..281b215
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/DrawSolidRectangle.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/DrawWireArc.png b/Runtime/Export/ScriptRefImages/DrawWireArc.png
new file mode 100644
index 0000000..bfbdf95
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/DrawWireArc.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/DrawWireDisc.png b/Runtime/Export/ScriptRefImages/DrawWireDisc.png
new file mode 100644
index 0000000..0831fdd
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/DrawWireDisc.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIActionKey.png b/Runtime/Export/ScriptRefImages/EditorGUIActionKey.png
new file mode 100644
index 0000000..ff01d91
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIActionKey.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIBoundsField.png b/Runtime/Export/ScriptRefImages/EditorGUIBoundsField.png
new file mode 100644
index 0000000..a7a94b3
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIBoundsField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIColorField.png b/Runtime/Export/ScriptRefImages/EditorGUIColorField.png
new file mode 100644
index 0000000..a085068
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIColorField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUICurveField.png b/Runtime/Export/ScriptRefImages/EditorGUICurveField.png
new file mode 100644
index 0000000..7241e18
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUICurveField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIDrawPreviewTexture.png b/Runtime/Export/ScriptRefImages/EditorGUIDrawPreviewTexture.png
new file mode 100644
index 0000000..898f742
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIDrawPreviewTexture.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIDrawTextureAlpha.png b/Runtime/Export/ScriptRefImages/EditorGUIDrawTextureAlpha.png
new file mode 100644
index 0000000..b9fc13f
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIDrawTextureAlpha.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIDropShadowLabel.png b/Runtime/Export/ScriptRefImages/EditorGUIDropShadowLabel.png
new file mode 100644
index 0000000..d597b62
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIDropShadowLabel.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIEnumPopup.png b/Runtime/Export/ScriptRefImages/EditorGUIEnumPopup.png
new file mode 100644
index 0000000..36e79e0
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIEnumPopup.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIFloatField.png b/Runtime/Export/ScriptRefImages/EditorGUIFloatField.png
new file mode 100644
index 0000000..2583c6c
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIFloatField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIFoldout.png b/Runtime/Export/ScriptRefImages/EditorGUIFoldout.png
new file mode 100644
index 0000000..b9b6690
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIFoldout.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIIndent.png b/Runtime/Export/ScriptRefImages/EditorGUIIndent.png
new file mode 100644
index 0000000..fa467fc
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIIndent.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIInspectorTitlebar.png b/Runtime/Export/ScriptRefImages/EditorGUIInspectorTitlebar.png
new file mode 100644
index 0000000..2641790
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIInspectorTitlebar.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIIntField.png b/Runtime/Export/ScriptRefImages/EditorGUIIntField.png
new file mode 100644
index 0000000..29017e1
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIIntField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIIntPopup.png b/Runtime/Export/ScriptRefImages/EditorGUIIntPopup.png
new file mode 100644
index 0000000..964f1ec
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIIntPopup.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIIntSlider.png b/Runtime/Export/ScriptRefImages/EditorGUIIntSlider.png
new file mode 100644
index 0000000..2ecd884
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIIntSlider.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILabelField.png b/Runtime/Export/ScriptRefImages/EditorGUILabelField.png
new file mode 100644
index 0000000..87f09bd
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILabelField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILayerField.png b/Runtime/Export/ScriptRefImages/EditorGUILayerField.png
new file mode 100644
index 0000000..e31e8e7
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILayerField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILayoutEnumPopup.png b/Runtime/Export/ScriptRefImages/EditorGUILayoutEnumPopup.png
new file mode 100644
index 0000000..ad932e6
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILayoutEnumPopup.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILayoutFloatField.png b/Runtime/Export/ScriptRefImages/EditorGUILayoutFloatField.png
new file mode 100644
index 0000000..c5f93de
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILayoutFloatField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILayoutIntField.png b/Runtime/Export/ScriptRefImages/EditorGUILayoutIntField.png
new file mode 100644
index 0000000..6ce1c56
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILayoutIntField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILayoutIntPopup.png b/Runtime/Export/ScriptRefImages/EditorGUILayoutIntPopup.png
new file mode 100644
index 0000000..b4d04ea
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILayoutIntPopup.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILayoutIntSlider.png b/Runtime/Export/ScriptRefImages/EditorGUILayoutIntSlider.png
new file mode 100644
index 0000000..68c2c25
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILayoutIntSlider.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILayoutLabel.png b/Runtime/Export/ScriptRefImages/EditorGUILayoutLabel.png
new file mode 100644
index 0000000..a481b45
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILayoutLabel.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILayoutLayerField.png b/Runtime/Export/ScriptRefImages/EditorGUILayoutLayerField.png
new file mode 100644
index 0000000..2cef88e
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILayoutLayerField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILayoutMinMaxSlider.png b/Runtime/Export/ScriptRefImages/EditorGUILayoutMinMaxSlider.png
new file mode 100644
index 0000000..325b0f1
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILayoutMinMaxSlider.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILayoutPasswordField.png b/Runtime/Export/ScriptRefImages/EditorGUILayoutPasswordField.png
new file mode 100644
index 0000000..38060d0
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILayoutPasswordField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILayoutPopup.png b/Runtime/Export/ScriptRefImages/EditorGUILayoutPopup.png
new file mode 100644
index 0000000..46a1739
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILayoutPopup.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILayoutSlider.png b/Runtime/Export/ScriptRefImages/EditorGUILayoutSlider.png
new file mode 100644
index 0000000..8917254
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILayoutSlider.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILayoutTagField.png b/Runtime/Export/ScriptRefImages/EditorGUILayoutTagField.png
new file mode 100644
index 0000000..2f25d3f
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILayoutTagField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILayoutTextArea.png b/Runtime/Export/ScriptRefImages/EditorGUILayoutTextArea.png
new file mode 100644
index 0000000..0603b89
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILayoutTextArea.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILayoutTextField.png b/Runtime/Export/ScriptRefImages/EditorGUILayoutTextField.png
new file mode 100644
index 0000000..37eba3a
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILayoutTextField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILayoutToggle.png b/Runtime/Export/ScriptRefImages/EditorGUILayoutToggle.png
new file mode 100644
index 0000000..39758e0
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILayoutToggle.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILayoutVector2Field.png b/Runtime/Export/ScriptRefImages/EditorGUILayoutVector2Field.png
new file mode 100644
index 0000000..b310776
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILayoutVector2Field.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUILayoutVector3Field.png b/Runtime/Export/ScriptRefImages/EditorGUILayoutVector3Field.png
new file mode 100644
index 0000000..378f004
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUILayoutVector3Field.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIMinMaxSlider.png b/Runtime/Export/ScriptRefImages/EditorGUIMinMaxSlider.png
new file mode 100644
index 0000000..06f604c
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIMinMaxSlider.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIObjectField.png b/Runtime/Export/ScriptRefImages/EditorGUIObjectField.png
new file mode 100644
index 0000000..82f8f00
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIObjectField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIPasswordField.png b/Runtime/Export/ScriptRefImages/EditorGUIPasswordField.png
new file mode 100644
index 0000000..3d360e1
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIPasswordField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIPopup.png b/Runtime/Export/ScriptRefImages/EditorGUIPopup.png
new file mode 100644
index 0000000..d9eebc2
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIPopup.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIPrefixLabel.png b/Runtime/Export/ScriptRefImages/EditorGUIPrefixLabel.png
new file mode 100644
index 0000000..ff91c97
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIPrefixLabel.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIProgressBar.png b/Runtime/Export/ScriptRefImages/EditorGUIProgressBar.png
new file mode 100644
index 0000000..0c42458
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIProgressBar.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIRectField.png b/Runtime/Export/ScriptRefImages/EditorGUIRectField.png
new file mode 100644
index 0000000..1abc7dd
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIRectField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUISlider.png b/Runtime/Export/ScriptRefImages/EditorGUISlider.png
new file mode 100644
index 0000000..c0c9c04
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUISlider.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUITagField.png b/Runtime/Export/ScriptRefImages/EditorGUITagField.png
new file mode 100644
index 0000000..15c5de0
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUITagField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUITextArea.png b/Runtime/Export/ScriptRefImages/EditorGUITextArea.png
new file mode 100644
index 0000000..9c4459d
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUITextArea.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUITextField.png b/Runtime/Export/ScriptRefImages/EditorGUITextField.png
new file mode 100644
index 0000000..36b3f26
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUITextField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIToggle.png b/Runtime/Export/ScriptRefImages/EditorGUIToggle.png
new file mode 100644
index 0000000..f486dfc
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIToggle.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIUtilityFindTexture.png b/Runtime/Export/ScriptRefImages/EditorGUIUtilityFindTexture.png
new file mode 100644
index 0000000..db6b86b
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIUtilityFindTexture.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIUtilityLookLikeControls.png b/Runtime/Export/ScriptRefImages/EditorGUIUtilityLookLikeControls.png
new file mode 100644
index 0000000..7a5abe3
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIUtilityLookLikeControls.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIUtilityLookLikeInspector.png b/Runtime/Export/ScriptRefImages/EditorGUIUtilityLookLikeInspector.png
new file mode 100644
index 0000000..8622855
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIUtilityLookLikeInspector.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIUtilityObjectContent.png b/Runtime/Export/ScriptRefImages/EditorGUIUtilityObjectContent.png
new file mode 100644
index 0000000..cf48295
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIUtilityObjectContent.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIUtilitySystemCopyBuffer.png b/Runtime/Export/ScriptRefImages/EditorGUIUtilitySystemCopyBuffer.png
new file mode 100644
index 0000000..0c4b362
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIUtilitySystemCopyBuffer.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIUtilityWhiteTexture.png b/Runtime/Export/ScriptRefImages/EditorGUIUtilityWhiteTexture.png
new file mode 100644
index 0000000..bac47f5
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIUtilityWhiteTexture.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIVector2Field.png b/Runtime/Export/ScriptRefImages/EditorGUIVector2Field.png
new file mode 100644
index 0000000..588b6ef
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIVector2Field.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIVector3Field.png b/Runtime/Export/ScriptRefImages/EditorGUIVector3Field.png
new file mode 100644
index 0000000..959bde6
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIVector3Field.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorGUIVector4Field.png b/Runtime/Export/ScriptRefImages/EditorGUIVector4Field.png
new file mode 100644
index 0000000..463f740
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorGUIVector4Field.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorPrefsBool.png b/Runtime/Export/ScriptRefImages/EditorPrefsBool.png
new file mode 100644
index 0000000..a7289ef
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorPrefsBool.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorUtility CreateGameObjectWithHideFlags.png b/Runtime/Export/ScriptRefImages/EditorUtility CreateGameObjectWithHideFlags.png
new file mode 100644
index 0000000..365ef91
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorUtility CreateGameObjectWithHideFlags.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorUtilityCollectDependencies.png b/Runtime/Export/ScriptRefImages/EditorUtilityCollectDependencies.png
new file mode 100644
index 0000000..134f1b6
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorUtilityCollectDependencies.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorUtilityDisplayCancelableProgressBar.png b/Runtime/Export/ScriptRefImages/EditorUtilityDisplayCancelableProgressBar.png
new file mode 100644
index 0000000..6cef1ad
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorUtilityDisplayCancelableProgressBar.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorUtilityDisplayDialogComplex.png b/Runtime/Export/ScriptRefImages/EditorUtilityDisplayDialogComplex.png
new file mode 100644
index 0000000..18d0524
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorUtilityDisplayDialogComplex.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorUtilityDisplayProgressBar.png b/Runtime/Export/ScriptRefImages/EditorUtilityDisplayProgressBar.png
new file mode 100644
index 0000000..4b7d9a6
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorUtilityDisplayProgressBar.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorUtilityFocusProjectWindow.png b/Runtime/Export/ScriptRefImages/EditorUtilityFocusProjectWindow.png
new file mode 100644
index 0000000..ae80250
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorUtilityFocusProjectWindow.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorUtilityInstanceIDToObject.png b/Runtime/Export/ScriptRefImages/EditorUtilityInstanceIDToObject.png
new file mode 100644
index 0000000..1a9fea6
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorUtilityInstanceIDToObject.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorUtilityOpenFilePanel.png b/Runtime/Export/ScriptRefImages/EditorUtilityOpenFilePanel.png
new file mode 100644
index 0000000..a3f641c
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorUtilityOpenFilePanel.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorUtilityOpenFolderPanel.png b/Runtime/Export/ScriptRefImages/EditorUtilityOpenFolderPanel.png
new file mode 100644
index 0000000..6fe12de
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorUtilityOpenFolderPanel.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorUtilitySaveFilePanel.png b/Runtime/Export/ScriptRefImages/EditorUtilitySaveFilePanel.png
new file mode 100644
index 0000000..c785d80
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorUtilitySaveFilePanel.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorUtilitySaveFilePanelInProject.png b/Runtime/Export/ScriptRefImages/EditorUtilitySaveFilePanelInProject.png
new file mode 100644
index 0000000..131301f
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorUtilitySaveFilePanelInProject.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorUtilitySaveFolderPanel.png b/Runtime/Export/ScriptRefImages/EditorUtilitySaveFolderPanel.png
new file mode 100644
index 0000000..2deba38
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorUtilitySaveFolderPanel.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorUtilitySetSelectedWireframeHidden.png b/Runtime/Export/ScriptRefImages/EditorUtilitySetSelectedWireframeHidden.png
new file mode 100644
index 0000000..65866b6
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorUtilitySetSelectedWireframeHidden.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/EditorWindowPosition.png b/Runtime/Export/ScriptRefImages/EditorWindowPosition.png
new file mode 100644
index 0000000..d819e69
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/EditorWindowPosition.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/ErrorString.png b/Runtime/Export/ScriptRefImages/ErrorString.png
new file mode 100644
index 0000000..ddd5831
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/ErrorString.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/FinishCompiling.png b/Runtime/Export/ScriptRefImages/FinishCompiling.png
new file mode 100644
index 0000000..7e92eaf
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/FinishCompiling.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/FoldoutUsage.png b/Runtime/Export/ScriptRefImages/FoldoutUsage.png
new file mode 100644
index 0000000..07baa98
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/FoldoutUsage.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/FollowCurve.png b/Runtime/Export/ScriptRefImages/FollowCurve.png
new file mode 100644
index 0000000..b89cf3b
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/FollowCurve.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/ForceSync.png b/Runtime/Export/ScriptRefImages/ForceSync.png
new file mode 100644
index 0000000..0bc50f5
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/ForceSync.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/FreeMoveHandle.png b/Runtime/Export/ScriptRefImages/FreeMoveHandle.png
new file mode 100644
index 0000000..1dcf0cc
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/FreeMoveHandle.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/FreeRotateHandle.png b/Runtime/Export/ScriptRefImages/FreeRotateHandle.png
new file mode 100644
index 0000000..7ca4c73
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/FreeRotateHandle.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUIBackgroundColor.png b/Runtime/Export/ScriptRefImages/GUIBackgroundColor.png
new file mode 100644
index 0000000..35dda9a
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUIBackgroundColor.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUIColor.png b/Runtime/Export/ScriptRefImages/GUIColor.png
new file mode 100644
index 0000000..332d4c2
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUIColor.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUIContentColor.png b/Runtime/Export/ScriptRefImages/GUIContentColor.png
new file mode 100644
index 0000000..1dcdbf1
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUIContentColor.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUIDepth.png b/Runtime/Export/ScriptRefImages/GUIDepth.png
new file mode 100644
index 0000000..3c6f128
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUIDepth.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUIEnabled.png b/Runtime/Export/ScriptRefImages/GUIEnabled.png
new file mode 100644
index 0000000..3629528
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUIEnabled.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILabel.png b/Runtime/Export/ScriptRefImages/GUILabel.png
new file mode 100644
index 0000000..f8a3dfe
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILabel.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILabelTexture.png b/Runtime/Export/ScriptRefImages/GUILabelTexture.png
new file mode 100644
index 0000000..3e08dae
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILabelTexture.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutArea.png b/Runtime/Export/ScriptRefImages/GUILayoutArea.png
new file mode 100644
index 0000000..6974460
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutArea.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutBox.png b/Runtime/Export/ScriptRefImages/GUILayoutBox.png
new file mode 100644
index 0000000..ff5b9d1
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutBox.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutButton.png b/Runtime/Export/ScriptRefImages/GUILayoutButton.png
new file mode 100644
index 0000000..1f3b634
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutButton.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutFlexibleSpace.png b/Runtime/Export/ScriptRefImages/GUILayoutFlexibleSpace.png
new file mode 100644
index 0000000..0ab248d
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutFlexibleSpace.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutHeight.png b/Runtime/Export/ScriptRefImages/GUILayoutHeight.png
new file mode 100644
index 0000000..c345eed
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutHeight.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutHorizontal.png b/Runtime/Export/ScriptRefImages/GUILayoutHorizontal.png
new file mode 100644
index 0000000..9679169
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutHorizontal.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutHorizontalScrollBar.png b/Runtime/Export/ScriptRefImages/GUILayoutHorizontalScrollBar.png
new file mode 100644
index 0000000..e65b69f
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutHorizontalScrollBar.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutHorizontalSlider.png b/Runtime/Export/ScriptRefImages/GUILayoutHorizontalSlider.png
new file mode 100644
index 0000000..21c4e97
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutHorizontalSlider.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutLabel.png b/Runtime/Export/ScriptRefImages/GUILayoutLabel.png
new file mode 100644
index 0000000..33c7072
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutLabel.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutMaxHeight.png b/Runtime/Export/ScriptRefImages/GUILayoutMaxHeight.png
new file mode 100644
index 0000000..37f7d97
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutMaxHeight.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutMaxWidth.png b/Runtime/Export/ScriptRefImages/GUILayoutMaxWidth.png
new file mode 100644
index 0000000..45f05c9
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutMaxWidth.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutMinHeight.png b/Runtime/Export/ScriptRefImages/GUILayoutMinHeight.png
new file mode 100644
index 0000000..534f5d1
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutMinHeight.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutMinWidth.png b/Runtime/Export/ScriptRefImages/GUILayoutMinWidth.png
new file mode 100644
index 0000000..4703b2c
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutMinWidth.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutPasswordField.png b/Runtime/Export/ScriptRefImages/GUILayoutPasswordField.png
new file mode 100644
index 0000000..b6649c3
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutPasswordField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutScrollView.png b/Runtime/Export/ScriptRefImages/GUILayoutScrollView.png
new file mode 100644
index 0000000..4e96b52
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutScrollView.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutSelectionGrid.png b/Runtime/Export/ScriptRefImages/GUILayoutSelectionGrid.png
new file mode 100644
index 0000000..33d05c8
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutSelectionGrid.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutSpace.png b/Runtime/Export/ScriptRefImages/GUILayoutSpace.png
new file mode 100644
index 0000000..732dd86
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutSpace.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutTextArea.png b/Runtime/Export/ScriptRefImages/GUILayoutTextArea.png
new file mode 100644
index 0000000..9eb86ca
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutTextArea.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutTextField.png b/Runtime/Export/ScriptRefImages/GUILayoutTextField.png
new file mode 100644
index 0000000..cb53789
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutTextField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutToggle.png b/Runtime/Export/ScriptRefImages/GUILayoutToggle.png
new file mode 100644
index 0000000..e93960b
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutToggle.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutToolbar.png b/Runtime/Export/ScriptRefImages/GUILayoutToolbar.png
new file mode 100644
index 0000000..3036479
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutToolbar.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutVertical.png b/Runtime/Export/ScriptRefImages/GUILayoutVertical.png
new file mode 100644
index 0000000..7f4a854
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutVertical.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutVerticalScrollBar.png b/Runtime/Export/ScriptRefImages/GUILayoutVerticalScrollBar.png
new file mode 100644
index 0000000..9a9db32
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutVerticalScrollBar.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutVerticalSlider.png b/Runtime/Export/ScriptRefImages/GUILayoutVerticalSlider.png
new file mode 100644
index 0000000..fd3f5bf
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutVerticalSlider.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutWidth.png b/Runtime/Export/ScriptRefImages/GUILayoutWidth.png
new file mode 100644
index 0000000..62ebc6d
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutWidth.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUILayoutWindow.png b/Runtime/Export/ScriptRefImages/GUILayoutWindow.png
new file mode 100644
index 0000000..840bb6d
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUILayoutWindow.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUITooltip.png b/Runtime/Export/ScriptRefImages/GUITooltip.png
new file mode 100644
index 0000000..57c49a2
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUITooltip.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUIWindowDemo.png b/Runtime/Export/ScriptRefImages/GUIWindowDemo.png
new file mode 100644
index 0000000..525138b
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUIWindowDemo.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GUIWindowDemo2.png b/Runtime/Export/ScriptRefImages/GUIWindowDemo2.png
new file mode 100644
index 0000000..4ae6853
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GUIWindowDemo2.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GetWindowEx.png b/Runtime/Export/ScriptRefImages/GetWindowEx.png
new file mode 100644
index 0000000..c1e4272
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GetWindowEx.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/GetWindowRectEx.png b/Runtime/Export/ScriptRefImages/GetWindowRectEx.png
new file mode 100644
index 0000000..4d1ea3a
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/GetWindowRectEx.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/HandlesLabel.png b/Runtime/Export/ScriptRefImages/HandlesLabel.png
new file mode 100644
index 0000000..6043ac1
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/HandlesLabel.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/HelpString.png b/Runtime/Export/ScriptRefImages/HelpString.png
new file mode 100644
index 0000000..d019af4
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/HelpString.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/InspectorTitlebarUsage.png b/Runtime/Export/ScriptRefImages/InspectorTitlebarUsage.png
new file mode 100644
index 0000000..7d4c285
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/InspectorTitlebarUsage.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/InspectorTitlebarUsageSpace.png b/Runtime/Export/ScriptRefImages/InspectorTitlebarUsageSpace.png
new file mode 100644
index 0000000..9432184
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/InspectorTitlebarUsageSpace.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/LeftHandRuleDiagram.png b/Runtime/Export/ScriptRefImages/LeftHandRuleDiagram.png
new file mode 100644
index 0000000..3d83a39
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/LeftHandRuleDiagram.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/MaskField.png b/Runtime/Export/ScriptRefImages/MaskField.png
new file mode 100644
index 0000000..e9248f4
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/MaskField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/MassiveColorChange.png b/Runtime/Export/ScriptRefImages/MassiveColorChange.png
new file mode 100644
index 0000000..d5616e3
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/MassiveColorChange.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/MaterialPropertyDrawer_UI.png b/Runtime/Export/ScriptRefImages/MaterialPropertyDrawer_UI.png
new file mode 100644
index 0000000..0fd4ea9
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/MaterialPropertyDrawer_UI.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/ModifyQuaternionDirectly.png b/Runtime/Export/ScriptRefImages/ModifyQuaternionDirectly.png
new file mode 100644
index 0000000..2ed8489
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/ModifyQuaternionDirectly.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/MoveResizeSelectedWindow.png b/Runtime/Export/ScriptRefImages/MoveResizeSelectedWindow.png
new file mode 100644
index 0000000..d5ed0ee
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/MoveResizeSelectedWindow.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/OrthographicPreviewer.png b/Runtime/Export/ScriptRefImages/OrthographicPreviewer.png
new file mode 100644
index 0000000..a613faf
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/OrthographicPreviewer.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/PerformVariousRedo.png b/Runtime/Export/ScriptRefImages/PerformVariousRedo.png
new file mode 100644
index 0000000..c5a130e
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/PerformVariousRedo.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/PerformVariousUndo.png b/Runtime/Export/ScriptRefImages/PerformVariousUndo.png
new file mode 100644
index 0000000..0073f04
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/PerformVariousUndo.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/PerlinExample.png b/Runtime/Export/ScriptRefImages/PerlinExample.png
new file mode 100644
index 0000000..142978e
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/PerlinExample.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/PlaceSelectionOnSurface.png b/Runtime/Export/ScriptRefImages/PlaceSelectionOnSurface.png
new file mode 100644
index 0000000..605c30e
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/PlaceSelectionOnSurface.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/Plane3Points.png b/Runtime/Export/ScriptRefImages/Plane3Points.png
new file mode 100644
index 0000000..e2b9dbb
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/Plane3Points.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/PlaneNormalOffset.png b/Runtime/Export/ScriptRefImages/PlaneNormalOffset.png
new file mode 100644
index 0000000..85af155
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/PlaneNormalOffset.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/PlaneNormalOrigin.png b/Runtime/Export/ScriptRefImages/PlaneNormalOrigin.png
new file mode 100644
index 0000000..a29a657
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/PlaneNormalOrigin.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/PlayerSettingsCustomSettings.png b/Runtime/Export/ScriptRefImages/PlayerSettingsCustomSettings.png
new file mode 100644
index 0000000..69df5f5
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/PlayerSettingsCustomSettings.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/PositionHandle.png b/Runtime/Export/ScriptRefImages/PositionHandle.png
new file mode 100644
index 0000000..f24c2cd
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/PositionHandle.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/QuickHelper.png b/Runtime/Export/ScriptRefImages/QuickHelper.png
new file mode 100644
index 0000000..393bd9c
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/QuickHelper.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/QuickHelperObjectField.png b/Runtime/Export/ScriptRefImages/QuickHelperObjectField.png
new file mode 100644
index 0000000..9259aeb
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/QuickHelperObjectField.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/QuickNotes.png b/Runtime/Export/ScriptRefImages/QuickNotes.png
new file mode 100644
index 0000000..e6b5841
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/QuickNotes.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/RadiusHandle.png b/Runtime/Export/ScriptRefImages/RadiusHandle.png
new file mode 100644
index 0000000..ded8669
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/RadiusHandle.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/RandomizeInSelection.png b/Runtime/Export/ScriptRefImages/RandomizeInSelection.png
new file mode 100644
index 0000000..55b3e0d
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/RandomizeInSelection.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/RectXMinYMin.png b/Runtime/Export/ScriptRefImages/RectXMinYMin.png
new file mode 100644
index 0000000..c024477
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/RectXMinYMin.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/RectXY.png b/Runtime/Export/ScriptRefImages/RectXY.png
new file mode 100644
index 0000000..e6ba87b
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/RectXY.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/RectangleCap.png b/Runtime/Export/ScriptRefImages/RectangleCap.png
new file mode 100644
index 0000000..2c95a87
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/RectangleCap.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/RemoveSpecificEditorPrefs.png b/Runtime/Export/ScriptRefImages/RemoveSpecificEditorPrefs.png
new file mode 100644
index 0000000..a69be5f
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/RemoveSpecificEditorPrefs.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/RotationHandle.png b/Runtime/Export/ScriptRefImages/RotationHandle.png
new file mode 100644
index 0000000..e582d09
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/RotationHandle.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/ScaleHandle.png b/Runtime/Export/ScriptRefImages/ScaleHandle.png
new file mode 100644
index 0000000..5053026
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/ScaleHandle.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/ScaleSliderHandle.png b/Runtime/Export/ScriptRefImages/ScaleSliderHandle.png
new file mode 100644
index 0000000..dfbcbc5
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/ScaleSliderHandle.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/ScaleValueHandle.png b/Runtime/Export/ScriptRefImages/ScaleValueHandle.png
new file mode 100644
index 0000000..3b48719
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/ScaleValueHandle.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/ScriptAdder.png b/Runtime/Export/ScriptRefImages/ScriptAdder.png
new file mode 100644
index 0000000..7109eab
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/ScriptAdder.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/ScriptableWizardDisplayWizard.png b/Runtime/Export/ScriptRefImages/ScriptableWizardDisplayWizard.png
new file mode 100644
index 0000000..0e4e4b9
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/ScriptableWizardDisplayWizard.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/ScriptableWizardOnDrawGizmos.png b/Runtime/Export/ScriptRefImages/ScriptableWizardOnDrawGizmos.png
new file mode 100644
index 0000000..ae0113b
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/ScriptableWizardOnDrawGizmos.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/ScriptableWizardOnWizardCreate.png b/Runtime/Export/ScriptRefImages/ScriptableWizardOnWizardCreate.png
new file mode 100644
index 0000000..608baae
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/ScriptableWizardOnWizardCreate.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/ScriptableWizardOnWizardOtherButton.png b/Runtime/Export/ScriptRefImages/ScriptableWizardOnWizardOtherButton.png
new file mode 100644
index 0000000..7a53bc1
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/ScriptableWizardOnWizardOtherButton.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/SelectAllOfTag.png b/Runtime/Export/ScriptRefImages/SelectAllOfTag.png
new file mode 100644
index 0000000..e555343
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/SelectAllOfTag.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/SelectionChange.png b/Runtime/Export/ScriptRefImages/SelectionChange.png
new file mode 100644
index 0000000..93cbbd9
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/SelectionChange.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/ShowPopupEx.png b/Runtime/Export/ScriptRefImages/ShowPopupEx.png
new file mode 100644
index 0000000..0697fe6
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/ShowPopupEx.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/ShowRemoveNotification.png b/Runtime/Export/ScriptRefImages/ShowRemoveNotification.png
new file mode 100644
index 0000000..0f08f52
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/ShowRemoveNotification.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/SimpleAutoSave.png b/Runtime/Export/ScriptRefImages/SimpleAutoSave.png
new file mode 100644
index 0000000..7b35c28
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/SimpleAutoSave.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/SimplePrefixLabelUsage.png b/Runtime/Export/ScriptRefImages/SimplePrefixLabelUsage.png
new file mode 100644
index 0000000..fe741ef
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/SimplePrefixLabelUsage.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/SimpleRecorder.png b/Runtime/Export/ScriptRefImages/SimpleRecorder.png
new file mode 100644
index 0000000..45e9d0f
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/SimpleRecorder.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/SliderHandle.png b/Runtime/Export/ScriptRefImages/SliderHandle.png
new file mode 100644
index 0000000..9cb7e6e
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/SliderHandle.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/SphereCap.png b/Runtime/Export/ScriptRefImages/SphereCap.png
new file mode 100644
index 0000000..27ec6ac
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/SphereCap.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/TearMeshApart.png b/Runtime/Export/ScriptRefImages/TearMeshApart.png
new file mode 100644
index 0000000..fbf8afe
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/TearMeshApart.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/Vec3ProjectDiagram.png b/Runtime/Export/ScriptRefImages/Vec3ProjectDiagram.png
new file mode 100644
index 0000000..260980c
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/Vec3ProjectDiagram.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/Vec3ReflectDiagram.png b/Runtime/Export/ScriptRefImages/Vec3ReflectDiagram.png
new file mode 100644
index 0000000..4f40c73
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/Vec3ReflectDiagram.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/WantsMouseMoveEx.png b/Runtime/Export/ScriptRefImages/WantsMouseMoveEx.png
new file mode 100644
index 0000000..7b5ab71
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/WantsMouseMoveEx.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/WheelFrictionCurve.png b/Runtime/Export/ScriptRefImages/WheelFrictionCurve.png
new file mode 100644
index 0000000..4535fb6
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/WheelFrictionCurve.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/Window1.png b/Runtime/Export/ScriptRefImages/Window1.png
new file mode 100644
index 0000000..d21ef02
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/Window1.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/focusedWindowEx.png b/Runtime/Export/ScriptRefImages/focusedWindowEx.png
new file mode 100644
index 0000000..17859ed
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/focusedWindowEx.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/isValidScriptableWizard.png b/Runtime/Export/ScriptRefImages/isValidScriptableWizard.png
new file mode 100644
index 0000000..6c4a9c5
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/isValidScriptableWizard.png
Binary files differ
diff --git a/Runtime/Export/ScriptRefImages/mouseFocusedWindowEx.png b/Runtime/Export/ScriptRefImages/mouseFocusedWindowEx.png
new file mode 100644
index 0000000..eb04a7d
--- /dev/null
+++ b/Runtime/Export/ScriptRefImages/mouseFocusedWindowEx.png
Binary files differ
diff --git a/Runtime/Export/ScrollWaitDefinitions.cs b/Runtime/Export/ScrollWaitDefinitions.cs
new file mode 100644
index 0000000..babafd1
--- /dev/null
+++ b/Runtime/Export/ScrollWaitDefinitions.cs
@@ -0,0 +1,8 @@
+namespace UnityEngine
+{
+ internal static class ScrollWaitDefinitions
+ {
+ public const int firstWait = 250; // ms
+ public const int regularWait = 30; // ms
+ }
+}
diff --git a/Runtime/Export/Security.cs b/Runtime/Export/Security.cs
new file mode 100644
index 0000000..8d0dbad
--- /dev/null
+++ b/Runtime/Export/Security.cs
@@ -0,0 +1,122 @@
+#if !UNITY_WINRT
+using System;
+using System.IO;
+using System.Security;
+using System.Security.Cryptography;
+using System.Reflection;
+using System.Collections.Generic;
+using Mono.Security;
+using Mono.Security.Cryptography;
+
+namespace UnityEngine
+{
+ public sealed partial class Security
+ {
+ #if UNITY_EDITOR || UNITY_WEBPLAYER
+ static List<Assembly> _verifiedAssemblies = new List<Assembly>();
+ #endif
+
+ static MethodInfo GetUnityCrossDomainHelperMethod(string methodname)
+ {
+ //todo: enter strong name with public key here
+ Type type = Types.GetType ("UnityEngine.UnityCrossDomainHelper", "CrossDomainPolicyParser, Version=1.0.0.0, Culture=neutral");
+ if (type == null)
+ throw new SecurityException("Cant find type UnityCrossDomainHelper");
+ var result = type.GetMethod (methodname);
+ if (result == null)
+ throw new SecurityException("Cant find "+methodname);
+ return result;
+ }
+
+ internal static string TokenToHex (byte[] token)
+ {
+ if (null == token || 8 > token.Length)
+ return string.Empty;
+
+ return string.Format ("{0:x2}{1:x2}{2:x2}{3:x2}{4:x2}{5:x2}{6:x2}{7:x2}",
+ token[0],
+ token[1],
+ token[2],
+ token[3],
+ token[4],
+ token[5],
+ token[6],
+ token[7]
+ );
+ }
+
+ #if UNITY_EDITOR
+ internal static void ClearVerifiedAssemblies ()
+ {
+ _verifiedAssemblies.Clear ();
+ }
+ #endif
+
+ [SecuritySafeCritical]
+ public static Assembly LoadAndVerifyAssembly (byte[] assemblyData)
+ {
+ #if UNITY_EDITOR || UNITY_WEBPLAYER
+ var assembly = Assembly.Load (assemblyData);
+ byte[] publicKey = assembly.GetName ().GetPublicKey ();
+ if (null == publicKey || 0 == publicKey.Length)
+ return null;
+
+ var rsa = new RSACryptoServiceProvider ();
+ rsa.ImportCspBlob (publicKey);
+ var strongName = new StrongName (rsa);
+ using (var stream = new MemoryStream (assemblyData))
+ {
+ if (strongName.Verify (stream))
+ {
+ _verifiedAssemblies.Add(assembly);
+ return assembly;
+ } else
+ {
+ return null;
+ }
+ }
+ #else
+ return null;
+ #endif
+ }
+
+#if UNITY_EDITOR
+ static readonly string kSignatureExtension = ".signature";
+
+ internal static bool VerifySignature (string file, byte[] publicKey)
+ {
+ try {
+ string signature = file + kSignatureExtension;
+ if (!File.Exists (signature))
+ return false;
+
+ using (var provider = new RSACryptoServiceProvider ())
+ {
+ provider.ImportCspBlob (publicKey);
+ using (var sha1 = new SHA1CryptoServiceProvider ())
+ return provider.VerifyData (File.ReadAllBytes (file), sha1, File.ReadAllBytes (signature));
+ }
+ } catch (Exception e) {
+ Debug.LogException (e);
+ }
+ return false;
+ }
+#endif
+ }
+
+ public static class Types
+ {
+ public static Type GetType(string typeName, string assemblyName)
+ {
+ try
+ {
+ return Assembly.Load(assemblyName).GetType(typeName);
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/Runtime/Export/SecurityPublic.txt b/Runtime/Export/SecurityPublic.txt
new file mode 100644
index 0000000..a130ca4
--- /dev/null
+++ b/Runtime/Export/SecurityPublic.txt
@@ -0,0 +1,61 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+
+#if UNITY_WEBPLAYER
+#include "PlatformDependent/CommonWebPlayer/ChainOfTrust.h"
+#endif
+
+CSRAW
+using System;
+using System.Security;
+
+namespace UnityEngine
+{
+CSRAW #if !UNITY_WINRT
+ // Webplayer security related class.
+ CLASS Security
+
+ // Prefetch the webplayer socket security policy from a non-default port number.
+ CSRAW static public bool PrefetchSocketPolicy(string ip, int atPort, int timeout=3000)
+ {
+ #if UNITY_EDITOR || UNITY_WEBPLAYER
+ var method = GetUnityCrossDomainHelperMethod("PrefetchSocketPolicy");
+ var result = method.Invoke(null, new object[] { ip, atPort, timeout});
+ return (bool)result;
+ #else
+ return true;
+ #endif
+ }
+
+ CONDITIONAL UNITY_WEBPLAYER || UNITY_EDITOR
+ CSRAW
+ [System.Security.SecuritySafeCritical]
+ public static string GetChainOfTrustValue (string name)
+ {
+ var callingAssembly = System.Reflection.Assembly.GetCallingAssembly ();
+ if (!_verifiedAssemblies.Contains(callingAssembly))
+ throw new ArgumentException("Calling assembly needs to be verified by Security.LoadAndVerifyAssembly");
+
+ var token = callingAssembly.GetName ().GetPublicKeyToken ();
+
+ return GetChainOfTrustValueInternal (name, UnityEngine.Security.TokenToHex (token));
+ }
+
+ CONDITIONAL UNITY_WEBPLAYER || UNITY_EDITOR
+ CUSTOM private static string GetChainOfTrustValueInternal (string name, string publicKeyToken)
+ {
+ #if UNITY_WEBPLAYER
+ std::string token = publicKeyToken.AsUTF8 ();
+ std::string result = ChainOfTrust::GetValue(name,token);
+ return scripting_string_new(result);
+ #else
+ return scripting_string_new(string());
+ #endif
+ }
+ END
+CSRAW #endif
+}
diff --git a/Runtime/Export/Serialization/IManagedLivenessAnalysis.cs b/Runtime/Export/Serialization/IManagedLivenessAnalysis.cs
new file mode 100644
index 0000000..e9d8f00
--- /dev/null
+++ b/Runtime/Export/Serialization/IManagedLivenessAnalysis.cs
@@ -0,0 +1,11 @@
+namespace UnityEngine
+{
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+ public interface IManagedLivenessAnalysis
+ {
+ void Reset();
+ bool HasBeenProcessed(object value);
+ object GetNewInstanceToReplaceOldInstance(object value);
+ }
+#endif
+} \ No newline at end of file
diff --git a/Runtime/Export/Serialization/IPPtrRemapper.cs b/Runtime/Export/Serialization/IPPtrRemapper.cs
new file mode 100644
index 0000000..047615c
--- /dev/null
+++ b/Runtime/Export/Serialization/IPPtrRemapper.cs
@@ -0,0 +1,9 @@
+namespace UnityEngine
+{
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+ public interface IPPtrRemapper
+ {
+ object GetNewInstanceToReplaceOldInstance(object value);
+ }
+#endif
+} \ No newline at end of file
diff --git a/Runtime/Export/Serialization/ISerializedStateReader.cs b/Runtime/Export/Serialization/ISerializedStateReader.cs
new file mode 100644
index 0000000..cc96a7b
--- /dev/null
+++ b/Runtime/Export/Serialization/ISerializedStateReader.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+
+namespace UnityEngine
+{
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+ public interface ISerializedStateReader
+ {
+ void Align();
+ byte ReadByte();
+ int ReadInt32();
+ float ReadSingle();
+ double ReadDouble();
+ bool ReadBoolean();
+ string ReadString();
+ object ReadUnityEngineObject();
+ object ReadAnimationCurve();
+ object ReadGradient();
+ object ReadGUIStyle();
+ object ReadRectOffset();
+
+ byte[] ReadArrayOfByte();
+ List<byte> ReadListOfByte();
+ }
+#endif
+}
diff --git a/Runtime/Export/Serialization/ISerializedStateWriter.cs b/Runtime/Export/Serialization/ISerializedStateWriter.cs
new file mode 100644
index 0000000..2bcea66
--- /dev/null
+++ b/Runtime/Export/Serialization/ISerializedStateWriter.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+
+namespace UnityEngine
+{
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+ public interface ISerializedStateWriter
+ {
+ void Align();
+ void WriteByte(byte value);
+ void WriteInt32(int value);
+ void WriteSingle(float value);
+ void WriteDouble(double value);
+ void WriteBoolean(bool value);
+ void WriteString(string value);
+ void WriteUnityEngineObject(object value);
+ void WriteAnimationCurve(object value);
+ void WriteGradient(object value);
+ void WriteGUIStyle(object value);
+ void WriteRectOffset(object value);
+
+ void WriteArrayOfByte(byte[] value);
+ void WriteListOfByte(List<byte> value);
+ }
+#endif
+}
diff --git a/Runtime/Export/Serialization/IUnityAssetsReferenceHolder.cs b/Runtime/Export/Serialization/IUnityAssetsReferenceHolder.cs
new file mode 100644
index 0000000..64fff2d
--- /dev/null
+++ b/Runtime/Export/Serialization/IUnityAssetsReferenceHolder.cs
@@ -0,0 +1,9 @@
+namespace UnityEngine
+{
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+ public interface IUnityAssetsReferenceHolder
+ {
+ void Unity_LivenessCheck();
+ }
+#endif
+} \ No newline at end of file
diff --git a/Runtime/Export/Serialization/IUnitySerializable.cs b/Runtime/Export/Serialization/IUnitySerializable.cs
new file mode 100644
index 0000000..2abeb4f
--- /dev/null
+++ b/Runtime/Export/Serialization/IUnitySerializable.cs
@@ -0,0 +1,11 @@
+namespace UnityEngine
+{
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+ public interface IUnitySerializable
+ {
+ void Unity_Serialize();
+ void Unity_Deserialize();
+ void Unity_RemapPPtrs();
+ }
+#endif
+} \ No newline at end of file
diff --git a/Runtime/Export/Serialization/ManagedLivenessAnalysis.txt b/Runtime/Export/Serialization/ManagedLivenessAnalysis.txt
new file mode 100644
index 0000000..4f9800e
--- /dev/null
+++ b/Runtime/Export/Serialization/ManagedLivenessAnalysis.txt
@@ -0,0 +1,67 @@
+C++RAW
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+#include "Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.h"
+#endif
+
+CSRAW
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+using Object=UnityEngine.Object;
+using System.Runtime.CompilerServices;
+
+namespace UnityEngine.Serialization
+{
+
+CONDITIONAL ENABLE_SERIALIZATION_BY_CODEGENERATION
+CLASS public ManagedLivenessAnalysis : IManagedLivenessAnalysis
+
+ CSRAW public static IManagedLivenessAnalysis Instance = null;
+ CSRAW public readonly HashSet<int> _state = new HashSet<int>();
+
+ CSRAW public static void Init()
+ {
+ if(Instance == null)
+ Instance = new ManagedLivenessAnalysis();
+ }
+ CSRAW public static void ResetState()
+ {
+ Instance.Reset();
+ }
+ CSRAW public static void SetInstance(IManagedLivenessAnalysis analysis)
+ {
+ Instance = analysis;
+ }
+ CSRAW public void Reset()
+ {
+ ((ManagedLivenessAnalysis)Instance)._state.Clear();
+ }
+ CSRAW public bool HasBeenProcessed(object value)
+ {
+ return !_state.Add(RuntimeHelpers.GetHashCode(value));
+ }
+ CSRAW public object GetNewInstanceToReplaceOldInstance(object value)
+ {
+ return INTERNAL_GetNewInstanceToReplaceOldInstance(((UnityEngine.Object)value).GetInstanceID());
+ }
+
+ THREAD_SAFE
+ CUSTOM private static object INTERNAL_GetNewInstanceToReplaceOldInstance(int instance_id)
+ {
+ return NativeExt_MonoBehaviourSerialization_GetNewInstanceToReplaceOldInstance(instance_id);
+ }
+
+END
+
+CONDITIONAL !ENABLE_SERIALIZATION_BY_CODEGENERATION && UNITY_WINRT
+CLASS public ManagedLivenessAnalysis
+ CSRAW public static void SetInstance(IManagedLivenessAnalysis analysis)
+ {
+ }
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/Serialization/PPtrRemapper.txt b/Runtime/Export/Serialization/PPtrRemapper.txt
new file mode 100644
index 0000000..e1fd2a7
--- /dev/null
+++ b/Runtime/Export/Serialization/PPtrRemapper.txt
@@ -0,0 +1,53 @@
+C++RAW
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+#include "Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.h"
+#endif
+
+CSRAW
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+using Object=UnityEngine.Object;
+
+namespace UnityEngine.Serialization
+{
+
+CONDITIONAL ENABLE_SERIALIZATION_BY_CODEGENERATION
+CLASS public PPtrRemapper : IPPtrRemapper
+
+ CSRAW public static IPPtrRemapper Instance = null;
+
+ CSRAW public static void Init()
+ {
+ if(Instance == null)
+ Instance = new PPtrRemapper();
+ }
+ CSRAW public static void SetInstance(IPPtrRemapper remapper)
+ {
+ Instance = remapper;
+ }
+ CSRAW public object GetNewInstanceToReplaceOldInstance(object value)
+ {
+ return INTERNAL_GetNewInstanceToReplaceOldInstance(((UnityEngine.Object)value).GetInstanceID());
+ }
+
+ THREAD_SAFE
+ CUSTOM private static object INTERNAL_GetNewInstanceToReplaceOldInstance(int instance_id)
+ {
+ return NativeExt_MonoBehaviourSerialization_GetNewInstanceToReplaceOldInstance(instance_id);
+ }
+
+END
+
+CONDITIONAL !ENABLE_SERIALIZATION_BY_CODEGENERATION && UNITY_METRO
+CLASS public PPtrRemapper
+ CSRAW public static void SetInstance(IPPtrRemapper reader)
+ {
+ }
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/Serialization/SerializedStateReader.txt b/Runtime/Export/Serialization/SerializedStateReader.txt
new file mode 100644
index 0000000..35f6f0c
--- /dev/null
+++ b/Runtime/Export/Serialization/SerializedStateReader.txt
@@ -0,0 +1,214 @@
+C++RAW
+
+#include "UnityPrefix.h"
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+# include "Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.h"
+#endif
+
+CSRAW
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+using Object=UnityEngine.Object;
+
+namespace UnityEngine.Serialization
+{
+
+CONDITIONAL ENABLE_SERIALIZATION_BY_CODEGENERATION
+CLASS public SerializedStateReader : ISerializedStateReader
+
+ CSRAW public static ISerializedStateReader Instance = null;
+
+ CSRAW public static void Init()
+ {
+ if(Instance == null)
+ Instance = new SerializedStateReader();
+ }
+ CSRAW public static void SetInstance(ISerializedStateReader reader)
+ {
+ Instance = reader;
+ }
+ CSRAW public void Align()
+ {
+ INTERNAL_Align();
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void INTERNAL_Align()
+ {
+ NativeExt_MonoBehaviourSerialization_ReaderAlign();
+ }
+
+ CSRAW public byte ReadByte()
+ {
+ return INTERNAL_ReadByte();
+ }
+
+ THREAD_SAFE
+ CUSTOM private static byte INTERNAL_ReadByte()
+ {
+ return NativeExt_MonoBehaviourSerialization_ReadByte();
+ }
+
+ CSRAW public int ReadInt32()
+ {
+ return INTERNAL_ReadInt32();
+ }
+
+ THREAD_SAFE
+ CUSTOM private static int INTERNAL_ReadInt32()
+ {
+ return NativeExt_MonoBehaviourSerialization_ReadInt();
+ }
+
+ CSRAW public float ReadSingle()
+ {
+ return INTERNAL_ReadSingle();
+ }
+
+ THREAD_SAFE
+ CUSTOM private static float INTERNAL_ReadSingle()
+ {
+ return NativeExt_MonoBehaviourSerialization_ReadFloat();
+ }
+
+ CSRAW public double ReadDouble()
+ {
+ return INTERNAL_ReadDouble();
+ }
+
+ THREAD_SAFE
+ CUSTOM private static double INTERNAL_ReadDouble()
+ {
+ return NativeExt_MonoBehaviourSerialization_ReadDouble();
+ }
+
+ CSRAW public bool ReadBoolean()
+ {
+ return INTERNAL_ReadBoolean();
+ }
+
+ THREAD_SAFE
+ CUSTOM private static bool INTERNAL_ReadBoolean()
+ {
+ return NativeExt_MonoBehaviourSerialization_ReadBool() == 1;
+ }
+
+ CSRAW public string ReadString()
+ {
+ return INTERNAL_ReadString();
+ }
+
+ THREAD_SAFE
+ CUSTOM private static string INTERNAL_ReadString()
+ {
+ return NativeExt_MonoBehaviourSerialization_ReadString();
+ }
+
+ CSRAW public object ReadUnityEngineObject()
+ {
+ return INTERNAL_ReadUnityEngineObject();
+ }
+
+ THREAD_SAFE
+ CUSTOM private static object INTERNAL_ReadUnityEngineObject()
+ {
+ return NativeExt_MonoBehaviourSerialization_ReadUnityEngineObject();
+ }
+
+ CSRAW public object ReadAnimationCurve()
+ {
+ var animation_curve = new AnimationCurve();
+ INTERNAL_ReadAnimationCurve(animation_curve);
+ return animation_curve;
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void INTERNAL_ReadAnimationCurve(AnimationCurve animation_curve)
+ {
+ NativeExt_MonoBehaviourSerialization_ReadAnimationCurve(animation_curve);
+ }
+
+ CSRAW public object ReadGradient()
+ {
+ var gradient = new Gradient();
+ INTERNAL_ReadGradient(gradient);
+ return gradient;
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void INTERNAL_ReadGradient(Gradient gradient)
+ {
+ NativeExt_MonoBehaviourSerialization_ReadGradient(gradient);
+ }
+
+ CSRAW public object ReadGUIStyle()
+ {
+ var style = new GUIStyle();
+ INTERNAL_ReadGUIStyle(style);
+ return style;
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void INTERNAL_ReadGUIStyle(GUIStyle style)
+ {
+ NativeExt_MonoBehaviourSerialization_ReadGUIStyle(style);
+ }
+
+ CSRAW public object ReadRectOffset()
+ {
+ var rect_offset = new RectOffset();
+ INTERNAL_RectOffset(rect_offset);
+ return rect_offset;
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void INTERNAL_RectOffset(RectOffset rect_offset)
+ {
+ NativeExt_MonoBehaviourSerialization_ReadRectOffset(rect_offset);
+ }
+
+ CSRAW public byte[] ReadArrayOfByte()
+ {
+ return ReadArrayInternal<byte>(ReadByte);
+ }
+
+ CSRAW public List<byte> ReadListOfByte()
+ {
+ return ReadListInternal<byte>(ReadByte);
+ }
+
+ CSRAW private T[] ReadArrayInternal<T>(Func<T> reader)
+ {
+ var length = ReadInt32();
+ var result = new T[length];
+ for(var i = 0; i < length; ++i)
+ result[i] = reader();
+ return result;
+ }
+
+ CSRAW private List<T> ReadListInternal<T>(Func<T> reader)
+ {
+ var length = ReadInt32();
+ var result = new List<T>(length);
+ for(var i = 0; i < length; ++i)
+ result.Add(reader());
+ return result;
+ }
+
+END
+
+CONDITIONAL !ENABLE_SERIALIZATION_BY_CODEGENERATION && UNITY_METRO
+CLASS public SerializedStateReader
+ CSRAW public static void SetInstance(ISerializedStateReader reader)
+ {
+ }
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/Serialization/SerializedStateWriter.txt b/Runtime/Export/Serialization/SerializedStateWriter.txt
new file mode 100644
index 0000000..fb3a07c
--- /dev/null
+++ b/Runtime/Export/Serialization/SerializedStateWriter.txt
@@ -0,0 +1,220 @@
+C++RAW
+
+#include "UnityPrefix.h"
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+# include "Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.h"
+#endif
+
+CSRAW
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+using Object=UnityEngine.Object;
+
+namespace UnityEngine.Serialization
+{
+
+CONDITIONAL ENABLE_SERIALIZATION_BY_CODEGENERATION
+CLASS public SerializedStateWriter : ISerializedStateWriter
+
+ CSRAW public static ISerializedStateWriter Instance = null;
+
+ CSRAW public static void Init()
+ {
+ if(Instance == null)
+ Instance = new SerializedStateWriter();
+ }
+
+ CSRAW public static void SetInstance(ISerializedStateWriter writer)
+ {
+ Instance = writer;
+ }
+
+ CSRAW public void Align()
+ {
+ INTERNAL_Align();
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void INTERNAL_Align()
+ {
+ NativeExt_MonoBehaviourSerialization_WriterAlign();
+ }
+
+ CSRAW public void WriteByte(byte value)
+ {
+ INTERNAL_WriteByte(value);
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void INTERNAL_WriteByte(byte value)
+ {
+ NativeExt_MonoBehaviourSerialization_WriteByte(value);
+ }
+
+ CSRAW public void WriteInt32(int value)
+ {
+ INTERNAL_WriteInt32(value);
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void INTERNAL_WriteInt32(int value)
+ {
+ NativeExt_MonoBehaviourSerialization_WriteInt(value);
+ }
+
+ CSRAW public void WriteSingle(float value)
+ {
+ INTERNAL_WriteSingle(value);
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void INTERNAL_WriteSingle(float value)
+ {
+ NativeExt_MonoBehaviourSerialization_WriteFloat(value);
+ }
+
+ CSRAW public void WriteDouble(double value)
+ {
+ INTERNAL_WriteDouble(value);
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void INTERNAL_WriteDouble(double value)
+ {
+ NativeExt_MonoBehaviourSerialization_WriteDouble(value);
+ }
+
+ CSRAW public void WriteBoolean(bool value)
+ {
+ INTERNAL_WriteBoolean(value ? 1 : 0);
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void INTERNAL_WriteBoolean(int value)
+ {
+ NativeExt_MonoBehaviourSerialization_WriteBool(value);
+ }
+
+ CSRAW public void WriteString(string value)
+ {
+ INTERNAL_WriteString(value);
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void INTERNAL_WriteString(string value)
+ {
+ NativeExt_MonoBehaviourSerialization_WriteString(const_cast<char*>(value.AsUTF8().c_str()), value.Length());
+ }
+
+ CSRAW public void WriteUnityEngineObject(object value)
+ {
+ if(typeof(UnityEngine.Object).IsAssignableFrom(value.GetType()))
+ INTERNAL_WriteUnityEngineObject(((UnityEngine.Object)value).GetInstanceID());
+ else
+ throw new NotImplementedException("WriteUnityEngineObject on " + value.GetType().Name + " is not supported");
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void INTERNAL_WriteUnityEngineObject(int value)
+ {
+ NativeExt_MonoBehaviourSerialization_WriteUnityEngineObject(value);
+ }
+
+ CSRAW public void WriteIDeserializable(object value, Type type)
+ {
+ throw new NotImplementedException("WriteIDeserializable");
+ }
+
+ CSRAW public void WriteAnimationCurve(object value)
+ {
+ INTERNAL_WriteAnimationCurve((UnityEngine.AnimationCurve)value);
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void INTERNAL_WriteAnimationCurve(AnimationCurve value)
+ {
+ NativeExt_MonoBehaviourSerialization_WriteAnimationCurve(value);
+ }
+
+ CSRAW public void WriteGradient(object value)
+ {
+ INTERNAL_WriteGradient((UnityEngine.Gradient)value);
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void INTERNAL_WriteGradient(Gradient value)
+ {
+ NativeExt_MonoBehaviourSerialization_WriteGradient(value);
+ }
+
+ CSRAW public void WriteGUIStyle(object value)
+ {
+ INTERNAL_WriteGUIStyle((UnityEngine.GUIStyle)value);
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void INTERNAL_WriteGUIStyle(GUIStyle value)
+ {
+ NativeExt_MonoBehaviourSerialization_WriteGUIStyle(value);
+ }
+
+ CSRAW public void WriteRectOffset(object value)
+ {
+ INTERNAL_WriteRectOffset((UnityEngine.RectOffset)value);
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void INTERNAL_WriteRectOffset(RectOffset value)
+ {
+ NativeExt_MonoBehaviourSerialization_WriteRectOffset(value);
+ }
+
+ CSRAW public void WriteArrayOfByte(byte[] value)
+ {
+ WriteArrayInternal(value, WriteByte);
+ }
+
+ CSRAW public void WriteListOfByte(List<byte> value)
+ {
+ WriteListInternal(value, WriteByte);
+ }
+
+ CSRAW private void WriteArrayInternal<T>(T[] value, Action<T> writer)
+ {
+ if(value != null)
+ {
+ WriteInt32(value.Length);
+ foreach(var item in value)
+ writer(item);
+ } else
+ WriteInt32(0);
+ }
+
+ CSRAW private void WriteListInternal<T>(List<T> value, Action<T> writer)
+ {
+ if(value != null)
+ {
+ WriteInt32(value.Count);
+ foreach(var item in value)
+ writer(item);
+ } else
+ WriteInt32(0);
+ }
+
+END
+
+CONDITIONAL !ENABLE_SERIALIZATION_BY_CODEGENERATION && UNITY_METRO
+CLASS public SerializedStateWriter
+ CSRAW public static void SetInstance(ISerializedStateWriter reader)
+ {
+ }
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/Serialization/UnitySurrogateSelector.cs b/Runtime/Export/Serialization/UnitySurrogateSelector.cs
new file mode 100644
index 0000000..17f09e4
--- /dev/null
+++ b/Runtime/Export/Serialization/UnitySurrogateSelector.cs
@@ -0,0 +1,115 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace UnityEngine.Serialization
+{
+#if !UNITY_WINRT
+ /// <summary>
+ /// Serialization support for <see cref="List{T}" /> and <see cref="Dictionary{TKey,TValue}" /> that doesn't rely on reflection
+ /// of private members in order to be useable under the CoreCLR security model (WebPlayer).
+ /// </summary>
+ public class UnitySurrogateSelector : ISurrogateSelector
+ {
+ public ISerializationSurrogate GetSurrogate(Type type, StreamingContext context, out ISurrogateSelector selector)
+ {
+ if (type.IsGenericType)
+ {
+ var genericTypeDefinition = type.GetGenericTypeDefinition();
+ if (genericTypeDefinition == typeof(List<>))
+ {
+ selector = this;
+ return ListSerializationSurrogate.Default;
+ }
+ if (genericTypeDefinition == typeof(Dictionary<,>))
+ {
+ selector = this;
+ var dictSurrogateType = typeof(DictionarySerializationSurrogate<,>).MakeGenericType(type.GetGenericArguments());
+ return (ISerializationSurrogate) Activator.CreateInstance(dictSurrogateType);
+ }
+ }
+
+ selector = null;
+ return null;
+ }
+
+ public void ChainSelector(ISurrogateSelector selector)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ISurrogateSelector GetNextSelector()
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ /// <summary>
+ /// Serialization support for <see cref="List{T}" /> that doesn't rely on reflection of private members.
+ /// </summary>
+ class ListSerializationSurrogate : ISerializationSurrogate
+ {
+ public static readonly ISerializationSurrogate Default = new ListSerializationSurrogate();
+
+ public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
+ {
+ var list = (IList)obj;
+ info.AddValue("_size", list.Count);
+ info.AddValue("_items", ArrayFromGenericList(list));
+ info.AddValue("_version", 0); // required for compatibility with platform deserialization
+ }
+
+ public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
+ {
+ var list = (IList)Activator.CreateInstance(obj.GetType());
+ var size = info.GetInt32("_size");
+ if (size == 0)
+ return list;
+
+ var items = ((IEnumerable)info.GetValue("_items", typeof(IEnumerable))).GetEnumerator();
+ for (var i = 0; i < size; ++i)
+ {
+ if (!items.MoveNext())
+ throw new InvalidOperationException();
+ list.Add(items.Current);
+ }
+ return list;
+ }
+
+ private static Array ArrayFromGenericList(IList list)
+ {
+ var items = Array.CreateInstance(list.GetType().GetGenericArguments()[0], list.Count);
+ list.CopyTo(items, 0);
+ return items;
+ }
+ }
+
+ /// <summary>
+ /// Serialization support for <see cref="Dictionary{TKey,TValue}" /> that doesn't rely on non public members.
+ /// </summary>
+ class DictionarySerializationSurrogate<TKey, TValue> : ISerializationSurrogate
+ {
+ public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
+ {
+ var dictionary = ((Dictionary<TKey, TValue>)obj);
+ dictionary.GetObjectData(info, context);
+ }
+
+ public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
+ {
+ var comparer = (IEqualityComparer<TKey>)info.GetValue("Comparer", typeof(IEqualityComparer<TKey>));
+ var dictionary = new Dictionary<TKey, TValue>(comparer);
+ if (info.MemberCount > 3) // KeyValuePairs might not be present if the dictionary was empty
+ {
+ var keyValuePairs =
+ (KeyValuePair<TKey, TValue>[]) info.GetValue("KeyValuePairs", typeof(KeyValuePair<TKey, TValue>[]));
+ if (keyValuePairs != null)
+ foreach (var kvp in keyValuePairs)
+ dictionary.Add(kvp.Key, kvp.Value);
+ }
+ return dictionary;
+ }
+ }
+#endif
+}
diff --git a/Runtime/Export/ShaderBindings.txt b/Runtime/Export/ShaderBindings.txt
new file mode 100644
index 0000000..30a64bd
--- /dev/null
+++ b/Runtime/Export/ShaderBindings.txt
@@ -0,0 +1,415 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoExportUtility.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Shaders/Shader.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "Runtime/Camera/Renderqueue.h"
+#include "External/shaderlab/Library/properties.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/Shaders/ShaderKeywords.h"
+#include "Runtime/Shaders/ComputeShader.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Misc/GraphicsScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+
+
+
+C++RAW
+
+PPtr<Shader> s_ScriptingCurrentShader;
+const ChannelAssigns* s_ScriptingCurrentChannels;
+
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+CLASS Shader : Object
+
+ CUSTOM static Shader Find (string name)
+ {
+ return Scripting::ScriptingWrapperFor(GetScriptMapper().FindShader(name));
+ }
+ CUSTOM internal static Shader FindBuiltin (string name)
+ {
+ return Scripting::ScriptingWrapperFor(GetBuiltinResource<Shader> (name));
+ }
+
+ AUTO_PROP bool isSupported IsSupported
+
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM_PROP internal string customEditor { return scripting_string_new (self->GetCustomEditorName()); }
+
+ CUSTOM static void EnableKeyword (string keyword) {
+ g_ShaderKeywords.Enable( keywords::Create( keyword ) );
+ }
+ CUSTOM static void DisableKeyword (string keyword) {
+ g_ShaderKeywords.Disable( keywords::Create( keyword ) );
+ }
+
+ AUTO_PROP int maximumLOD GetMaximumShaderLOD SetMaximumShaderLOD
+ CUSTOM_PROP static int globalMaximumLOD { return Shader::GetGlobalMaximumShaderLOD(); } { Shader::SetGLobalMaximumShaderLOD (value); }
+
+ CUSTOM_PROP int renderQueue { return self->GetShaderLabShader()->GetRenderQueue(); }
+
+ CSRAW public static void SetGlobalColor (string propertyName, Color color) {
+ SetGlobalColor(Shader.PropertyToID(propertyName), color);
+ }
+ CUSTOM static void SetGlobalColor (int nameID, Color color) {
+ ShaderLab::PropertySheet *props = ShaderLab::g_GlobalProperties;
+ ShaderLab::FastPropertyName propName; propName.index = nameID;
+ props->SetVector (propName, color.GetPtr ());
+ }
+ CSRAW public static void SetGlobalVector (string propertyName, Vector4 vec) {
+ SetGlobalColor (propertyName, vec);
+ }
+ CSRAW public static void SetGlobalVector (int nameID, Vector4 vec) {
+ SetGlobalColor (nameID, vec);
+ }
+
+ CSRAW public static void SetGlobalFloat (string propertyName, float value) {
+ SetGlobalFloat(Shader.PropertyToID(propertyName), value);
+ }
+ CUSTOM static void SetGlobalFloat (int nameID, float value) {
+ ShaderLab::PropertySheet *props = ShaderLab::g_GlobalProperties;
+ ShaderLab::FastPropertyName propName; propName.index = nameID;
+ props->SetFloat (propName, value);
+ }
+
+ CSRAW public static void SetGlobalInt (string propertyName, int value) { SetGlobalFloat(propertyName, (float)value); }
+ CSRAW public static void SetGlobalInt (int nameID, int value) { SetGlobalFloat(nameID, (float)value); }
+
+ CSRAW public static void SetGlobalTexture (string propertyName, Texture tex) {
+ SetGlobalTexture (Shader.PropertyToID(propertyName), tex);
+ }
+ CUSTOM static void SetGlobalTexture (int nameID, Texture tex) {
+ Texture& texture = *tex;
+ ShaderLab::PropertySheet *props = ShaderLab::g_GlobalProperties;
+ ShaderLab::FastPropertyName propName; propName.index = nameID;
+ props->SetTexture (propName, &texture);
+ }
+
+ CSRAW public static void SetGlobalMatrix (string propertyName, Matrix4x4 mat) {
+ SetGlobalMatrix (Shader.PropertyToID(propertyName), mat);
+ }
+ CUSTOM static void SetGlobalMatrix (int nameID, Matrix4x4 mat) {
+ ShaderLab::PropertySheet *props = ShaderLab::g_GlobalProperties;
+ ShaderLab::FastPropertyName propName; propName.index = nameID;
+ props->SetValueProp (propName, 16, mat.GetPtr());
+ }
+
+ CUSTOM static void SetGlobalTexGenMode (string propertyName, TexGenMode mode) {
+ ShaderLab::PropertySheet *props = ShaderLab::g_GlobalProperties;
+ props->GetTexEnv(ScriptingStringToProperty (propertyName))->SetTexGen ((TexGenMode)mode);
+ }
+ CUSTOM static void SetGlobalTextureMatrixName (string propertyName, string matrixName) {
+ if(propertyName.Length() == 0)
+ {
+ ErrorString ("SetGlobalTextureMatrixName: Invalid empty propertyName");
+ return;
+ }
+ if(matrixName.Length() == 0)
+ {
+ ErrorString ("SetGlobalTextureMatrixName: Invalid empty matrixName");
+ return;
+ }
+ ShaderLab::PropertySheet *props = ShaderLab::g_GlobalProperties;
+ props->GetTexEnv(ScriptingStringToProperty (propertyName))->SetMatrixName (ScriptingStringToProperty (matrixName));
+ }
+
+ CONDITIONAL !UNITY_FLASH
+ CUSTOM static void SetGlobalBuffer (string propertyName, ComputeBuffer buffer) {
+ ShaderLab::PropertySheet *props = ShaderLab::g_GlobalProperties;
+ props->SetComputeBuffer (ScriptingStringToProperty (propertyName), buffer ? buffer->GetBufferHandle() : ComputeBufferID());
+ }
+
+ CUSTOM static int PropertyToID (string name) {
+ return ScriptingStringToProperty (name).index;
+ }
+
+ CUSTOM static void WarmupAllShaders () {
+ WarmupAllShaders ();
+ }
+
+END
+
+
+
+NONSEALED_CLASS Material : Object
+
+ CSRAW public Material (string contents) { Internal_CreateWithString (this, contents); }
+
+ CSRAW public Material (Shader shader) { Internal_CreateWithShader (this, shader); }
+
+ CSRAW public Material (Material source) : base () { Internal_CreateWithMaterial (this, source); }
+
+
+ AUTO_PTR_PROP Shader shader GetShader SetShader
+
+
+ CSRAW public Color color { get { return GetColor ("_Color"); } set { SetColor ("_Color", value); } }
+ CSRAW public Texture mainTexture { get { return GetTexture ("_MainTex"); } set { SetTexture ("_MainTex", value); } }
+ CSRAW public Vector2 mainTextureOffset { get { return GetTextureOffset("_MainTex"); } set { SetTextureOffset("_MainTex", value); } }
+ CSRAW public Vector2 mainTextureScale { get { return GetTextureScale("_MainTex"); } set { SetTextureScale("_MainTex", value); } }
+
+ CSRAW public void SetColor (string propertyName, Color color)
+ {
+ SetColor (Shader.PropertyToID(propertyName), color);
+ }
+ CUSTOM void SetColor (int nameID, Color color)
+ {
+ ShaderLab::FastPropertyName propName; propName.index = nameID;
+ self->SetColor (propName, color);
+ }
+ CSRAW public Color GetColor (string propertyName)
+ {
+ return GetColor (Shader.PropertyToID(propertyName));
+ }
+ CUSTOM Color GetColor (int nameID)
+ {
+ ShaderLab::FastPropertyName propName; propName.index = nameID;
+ return self->GetColor (propName);
+ }
+
+ CSRAW public void SetVector (string propertyName, Vector4 vector)
+ {
+ SetColor (propertyName, new Color (vector.x, vector.y, vector.z, vector.w));
+ }
+ CSRAW public void SetVector (int nameID, Vector4 vector)
+ {
+ SetColor (nameID, new Color (vector.x, vector.y, vector.z, vector.w));
+ }
+ CSRAW public Vector4 GetVector (string propertyName)
+ {
+ Color temp = GetColor (propertyName);
+ return new Vector4 (temp.r, temp.g, temp.b, temp.a);
+ }
+ CSRAW public Vector4 GetVector (int nameID)
+ {
+ Color temp = GetColor (nameID);
+ return new Vector4 (temp.r, temp.g, temp.b, temp.a);
+ }
+
+ CSRAW public void SetTexture (string propertyName, Texture texture)
+ {
+ SetTexture (Shader.PropertyToID(propertyName), texture);
+ }
+ CUSTOM void SetTexture (int nameID, Texture texture)
+ {
+ ShaderLab::FastPropertyName propName; propName.index = nameID;
+ self->SetTexture (propName, texture);
+ }
+ CSRAW public Texture GetTexture (string propertyName)
+ {
+ return GetTexture (Shader.PropertyToID(propertyName));
+ }
+ CUSTOM Texture GetTexture (int nameID)
+ {
+ ShaderLab::FastPropertyName propName; propName.index = nameID;
+ return Scripting::ScriptingWrapperFor (self->GetTexture (propName));
+ }
+
+
+ // Workaround for gcc/msvc where passing small mono structures by value does not work
+ CUSTOM private static void Internal_GetTextureOffset (Material mat, string name, out Vector2 output)
+ {
+ *output = mat->GetTextureOffset( ScriptingStringToProperty(name) );
+ }
+ CUSTOM private static void Internal_GetTextureScale (Material mat, string name, out Vector2 output)
+ {
+ *output = mat->GetTextureScale( ScriptingStringToProperty(name) );
+ }
+
+ CUSTOM void SetTextureOffset (string propertyName, Vector2 offset)
+ {
+ self->SetTextureOffset( ScriptingStringToProperty(propertyName), offset );
+ }
+ CSRAW public Vector2 GetTextureOffset (string propertyName)
+ {
+ Vector2 r;
+ Internal_GetTextureOffset(this, propertyName, out r);
+ return r;
+ }
+
+ CUSTOM void SetTextureScale (string propertyName, Vector2 scale)
+ {
+ self->SetTextureScale( ScriptingStringToProperty(propertyName), scale );
+ }
+ CSRAW public Vector2 GetTextureScale (string propertyName)
+ {
+ Vector2 r;
+ Internal_GetTextureScale(this, propertyName, out r);
+ return r;
+ }
+
+ CSRAW public void SetMatrix (string propertyName, Matrix4x4 matrix)
+ {
+ SetMatrix (Shader.PropertyToID(propertyName), matrix);
+ }
+ CUSTOM void SetMatrix (int nameID, Matrix4x4 matrix)
+ {
+ ShaderLab::FastPropertyName propName; propName.index = nameID;
+ self->SetMatrix (propName, matrix);
+ }
+ CSRAW public Matrix4x4 GetMatrix (string propertyName)
+ {
+ return GetMatrix (Shader.PropertyToID(propertyName));
+ }
+ CUSTOM Matrix4x4 GetMatrix (int nameID)
+ {
+ ShaderLab::FastPropertyName propName; propName.index = nameID;
+ return self->GetMatrix (propName);
+ }
+
+ CSRAW public void SetFloat (string propertyName, float value)
+ {
+ SetFloat (Shader.PropertyToID(propertyName), value);
+ }
+ CUSTOM void SetFloat (int nameID, float value)
+ {
+ ShaderLab::FastPropertyName propName; propName.index = nameID;
+ self->SetFloat (propName, value);
+ }
+ CSRAW public float GetFloat (string propertyName)
+ {
+ return GetFloat (Shader.PropertyToID(propertyName));
+ }
+ CUSTOM float GetFloat (int nameID)
+ {
+ ShaderLab::FastPropertyName propName; propName.index = nameID;
+ return self->GetFloat (propName);
+ }
+
+ CSRAW public void SetInt (string propertyName, int value) { SetFloat(propertyName, (float)value); }
+ CSRAW public void SetInt (int nameID, int value) { SetFloat(nameID, (float)value); }
+ CSRAW public int GetInt (string propertyName) { return (int)GetFloat(propertyName); }
+ CSRAW public int GetInt (int nameID) { return (int)GetFloat(nameID); }
+
+ CONDITIONAL !UNITY_FLASH
+ CUSTOM void SetBuffer (string propertyName, ComputeBuffer buffer) {
+ FastPropertyName fpName = ScriptingStringToProperty(propertyName);
+ self->SetComputeBuffer (fpName, buffer ? buffer->GetBufferHandle() : ComputeBufferID());
+ }
+
+ CSRAW public bool HasProperty (string propertyName)
+ {
+ return HasProperty (Shader.PropertyToID(propertyName));
+ }
+ CUSTOM bool HasProperty (int nameID)
+ {
+ ShaderLab::FastPropertyName propName; propName.index = nameID;
+ return self->HasProperty (propName);
+ }
+
+ CUSTOM string GetTag (string tag, bool searchFallbacks, string defaultValue = "") {
+ return scripting_string_new (self->GetTag (tag, !searchFallbacks, defaultValue));
+ }
+
+ CUSTOM void Lerp (Material start, Material end, float t)
+ {
+ const ShaderLab::PropertySheet &s1 = start->GetProperties();
+ const ShaderLab::PropertySheet &s2 = end->GetProperties();
+ self->GetWritableProperties().LerpProperties( s1, s2, clamp01(t) );
+ }
+
+ AUTO_PROP int passCount GetPassCount
+
+
+ CUSTOM bool SetPass (int pass) {
+ Material& mat = *self;
+ if (pass >= mat.GetPassCount())
+ {
+ ErrorStringMsg("Trying to access pass %d, but material '%s' subshader (0) has only %d valid passes.",
+ pass,
+ mat.GetName(),
+ mat.GetPassCount());
+ return false;
+ }
+
+ if (!CheckShouldRenderPass (pass, mat))
+ return false;
+ s_ScriptingCurrentShader = mat.GetShaderPPtr();
+ s_ScriptingCurrentChannels = mat.SetPass(pass);
+ return s_ScriptingCurrentChannels != NULL;
+ }
+
+ AUTO_PROP int renderQueue GetActualRenderQueue SetCustomRenderQueue
+
+ OBSOLETE warning Use the Material constructor instead.
+ CSRAW static public Material Create (string scriptContents)
+ {
+ return new Material (scriptContents);
+ }
+
+ CUSTOM private static void Internal_CreateWithString ([Writable]Material mono, string contents)
+ {
+ Material *mat = Material::CreateMaterial (contents.AsUTF8().c_str(), 0, true);
+ Scripting::ConnectScriptingWrapperToObject (mono.GetScriptingObject(), mat);
+ mat->ApplyMaterialPropertyDrawers();
+ }
+
+ CUSTOM private static void Internal_CreateWithShader ([Writable]Material mono, Shader shader)
+ {
+ Material *mat = Material::CreateMaterial (*shader, 0, true);
+ Scripting::ConnectScriptingWrapperToObject (mono.GetScriptingObject(), mat);
+ mat->ApplyMaterialPropertyDrawers();
+ }
+
+ CUSTOM private static void Internal_CreateWithMaterial ([Writable]Material mono, Material source)
+ {
+ Material *mat = Material::CreateMaterial (*source, 0, true);
+ Scripting::ConnectScriptingWrapperToObject (mono.GetScriptingObject(), mat);
+ mat->ApplyMaterialPropertyDrawers();
+ }
+
+ CUSTOM void CopyPropertiesFromMaterial (Material mat) {
+ if (mat != NULL)
+ self->CopyPropertiesFromMaterial(*mat);
+ else
+ ErrorString ("Trying to copy properties from null material.");
+ }
+
+ CUSTOM void EnableKeyword (string keyword) {
+ self->EnableKeyword (keyword);
+ }
+ CUSTOM void DisableKeyword (string keyword) {
+ self->DisableKeyword (keyword);
+ }
+
+ CONDITIONAL ENABLE_MONO || UNITY_WINRT
+ CUSTOM_PROP String[] shaderKeywords {
+ const size_t count = self->GetShaderKeywords ().size ();
+
+ ScriptingArrayPtr array = CreateScriptingArray<ScriptingStringPtr> (GetScriptingManager().GetCommonClasses().string, count);
+ for (int i=0;i<count;i++)
+ {
+ Scripting::SetScriptingArrayElement<ScriptingStringPtr>(array,i,scripting_string_new(self->GetShaderKeywords ()[i]));
+ }
+ return array;
+ }
+ {
+ Material::ShaderKeywordsT names;
+ for (int i=0;i<GetScriptingArraySize (value);i++)
+ names.push_back (scripting_cpp_string_for (Scripting::GetScriptingArrayElementNoRef<ScriptingStringPtr> (value, i)));
+ self->SetShaderKeywords (names);
+ }
+
+END
+
+
+
+CSRAW
+}
diff --git a/Runtime/Export/SliderHandler.cs b/Runtime/Export/SliderHandler.cs
new file mode 100644
index 0000000..057ef70
--- /dev/null
+++ b/Runtime/Export/SliderHandler.cs
@@ -0,0 +1,330 @@
+using System;
+
+namespace UnityEngine
+{
+ // State for when we're dragging a slider.
+ internal class SliderState
+ {
+ public float dragStartPos;
+ public float dragStartValue;
+ public bool isDragging;
+ }
+
+ // TODO: Make the thumb positioning / sizing be right
+ internal struct SliderHandler
+ {
+ readonly Rect position;
+ readonly float currentValue;
+ readonly float size;
+ readonly float start;
+ readonly float end;
+ readonly GUIStyle slider;
+ readonly GUIStyle thumb;
+ readonly bool horiz;
+ readonly int id;
+
+ public SliderHandler(Rect position, float currentValue, float size, float start, float end, GUIStyle slider, GUIStyle thumb, bool horiz, int id)
+ {
+ this.position = position;
+ this.currentValue = currentValue;
+ this.size = size;
+ this.start = start;
+ this.end = end;
+ this.slider = slider;
+ this.thumb = thumb;
+ this.horiz = horiz;
+ this.id = id;
+ }
+
+ public float Handle()
+ {
+ if (slider == null || thumb == null)
+ return currentValue;
+
+ switch (CurrentEventType())
+ {
+ case EventType.MouseDown:
+ return OnMouseDown();
+
+ case EventType.MouseDrag:
+ return OnMouseDrag();
+
+ case EventType.MouseUp:
+ return OnMouseUp();
+
+ case EventType.Repaint:
+ return OnRepaint();
+ }
+ return currentValue;
+ }
+
+ private float OnMouseDown()
+ {
+ // if the click is outside this control, just bail out...
+ if (!position.Contains(CurrentEvent().mousePosition) || IsEmptySlider())
+ return currentValue;
+
+ GUI.scrollTroughSide = 0;
+ GUIUtility.hotControl = id;
+ CurrentEvent().Use();
+
+ if (ThumbSelectionRect().Contains(CurrentEvent().mousePosition))
+ {
+ // We have a mousedown on the thumb
+ // Record where we're draging from, so the user can get back.
+ StartDraggingWithValue(ClampedCurrentValue());
+ return currentValue;
+ }
+
+ GUI.changed = true;
+
+ // We're outside the thumb, but inside the trough.
+ // If we have a scrollSize, we do pgup/pgdn style movements
+ // if not, we just snap to the current position and begin tracking
+ if (SupportsPageMovements())
+ {
+ SliderState().isDragging = false;
+ GUI.nextScrollStepTime = SystemClock.now.AddMilliseconds(ScrollWaitDefinitions.firstWait);
+ GUI.scrollTroughSide = CurrentScrollTroughSide();
+ return PageMovementValue();
+ }
+
+ float newValue = ValueForCurrentMousePosition();
+ StartDraggingWithValue(newValue);
+ return Clamp(newValue);
+ }
+
+ private float OnMouseDrag()
+ {
+ if (GUIUtility.hotControl != id)
+ return currentValue;
+
+ var sliderState = SliderState();
+ if (!sliderState.isDragging)
+ return currentValue;
+
+ GUI.changed = true;
+ CurrentEvent().Use();
+
+ // Recalculate the value from the mouse position. this has the side effect that values are relative to the
+ // click point - no matter where inside the trough the original value was. Also means user can get back original value
+ // if he drags back to start position.
+ float deltaPos = MousePosition() - sliderState.dragStartPos;
+ var newValue = sliderState.dragStartValue + deltaPos / ValuesPerPixel();
+ return Clamp(newValue);
+ }
+
+ private float OnMouseUp()
+ {
+ if (GUIUtility.hotControl == id)
+ {
+ CurrentEvent().Use();
+ GUIUtility.hotControl = 0;
+ }
+ return currentValue;
+ }
+
+ private float OnRepaint()
+ {
+ slider.Draw(position, GUIContent.none, id);
+ thumb.Draw(ThumbRect(), GUIContent.none, id);
+
+ if (GUIUtility.hotControl != id || !position.Contains(CurrentEvent().mousePosition) || IsEmptySlider())
+ return currentValue;
+
+ if (ThumbRect().Contains(CurrentEvent().mousePosition))
+ {
+ if (GUI.scrollTroughSide != 0) // if was scrolling with "trough" and the thumb reached mouse - sliding action over
+ {
+ GUIUtility.hotControl = 0;
+ }
+
+ return currentValue;
+ }
+
+ GUI.InternalRepaintEditorWindow();
+
+ if (SystemClock.now < GUI.nextScrollStepTime)
+ return currentValue;
+
+ if (CurrentScrollTroughSide() != GUI.scrollTroughSide)
+ return currentValue;
+
+ GUI.nextScrollStepTime = SystemClock.now.AddMilliseconds(ScrollWaitDefinitions.regularWait);
+
+ if (SupportsPageMovements())
+ {
+ SliderState().isDragging = false;
+ GUI.changed = true;
+ return PageMovementValue();
+ }
+ return ClampedCurrentValue();
+ }
+
+ private EventType CurrentEventType()
+ {
+ return CurrentEvent().GetTypeForControl(id);
+ }
+
+ private int CurrentScrollTroughSide()
+ {
+ float mousePos = horiz ? CurrentEvent().mousePosition.x : CurrentEvent().mousePosition.y;
+ float thumbPos = horiz ? ThumbRect().x : ThumbRect().y;
+
+ return mousePos > thumbPos ? 1 : -1;
+ }
+
+ private bool IsEmptySlider()
+ {
+ return start == end;
+ }
+
+ private bool SupportsPageMovements()
+ {
+ return size != 0 && GUI.usePageScrollbars;
+ }
+
+ private float PageMovementValue()
+ {
+ var newValue = currentValue;
+ var sign = start > end ? -1 : 1;
+ if (MousePosition() > PageUpMovementBound())
+ newValue += size * sign * .9f;
+ else
+ newValue -= size * sign * .9f;
+ return Clamp(newValue);
+ }
+
+ private float PageUpMovementBound()
+ {
+ if (horiz)
+ return ThumbRect().xMax - position.x;
+ return ThumbRect().yMax - position.y;
+ }
+
+ private Event CurrentEvent()
+ {
+ return Event.current;
+ }
+
+ private float ValueForCurrentMousePosition()
+ {
+ if (horiz)
+ return (MousePosition() - ThumbRect().width * .5f) / ValuesPerPixel() + start - size * .5f;
+ return (MousePosition() - ThumbRect().height * .5f) / ValuesPerPixel() + start - size * .5f;
+ }
+
+ private float Clamp(float value)
+ {
+ return Mathf.Clamp(value, MinValue(), MaxValue());
+ }
+
+ private Rect ThumbSelectionRect()
+ {
+ var selectionRect = ThumbRect();
+#if UNITY_IPHONE
+ int minSize = 12;
+ if (selectionRect.width < minSize)
+ {
+ selectionRect.x -= (minSize - selectionRect.width) / 2;
+ selectionRect.width = minSize;
+ }
+ if (selectionRect.height < minSize)
+ {
+ selectionRect.y -= (minSize - selectionRect.height) / 2;
+ selectionRect.height = minSize;
+ }
+
+#endif
+ return selectionRect;
+ }
+
+ private void StartDraggingWithValue(float dragStartValue)
+ {
+ var state = SliderState();
+ state.dragStartPos = MousePosition();
+ state.dragStartValue = dragStartValue;
+ state.isDragging = true;
+ }
+
+ private SliderState SliderState()
+ {
+ return (SliderState)GUIUtility.GetStateObject(typeof(SliderState), id);
+ }
+
+ private Rect ThumbRect()
+ {
+ return horiz ? HorizontalThumbRect() : VerticalThumbRect();
+ }
+
+ private Rect VerticalThumbRect()
+ {
+ var valuesPerPixel = ValuesPerPixel();
+ if (start < end)
+ return new Rect(
+ position.x + slider.padding.left,
+ (ClampedCurrentValue() - start) * valuesPerPixel + position.y + slider.padding.top,
+ position.width - slider.padding.horizontal,
+ size * valuesPerPixel + ThumbSize());
+
+ return new Rect(
+ position.x + slider.padding.left,
+ (ClampedCurrentValue() + size - start) * valuesPerPixel + position.y + slider.padding.top,
+ position.width - slider.padding.horizontal,
+ size * -valuesPerPixel + ThumbSize());
+ }
+
+ private Rect HorizontalThumbRect()
+ {
+ var valuesPerPixel = ValuesPerPixel();
+ if (start < end)
+ return new Rect(
+ (ClampedCurrentValue() - start) * valuesPerPixel + position.x + slider.padding.left,
+ position.y + slider.padding.top,
+ size * valuesPerPixel + ThumbSize(),
+ position.height - slider.padding.vertical);
+
+ return new Rect(
+ (ClampedCurrentValue() + size - start) * valuesPerPixel + position.x + slider.padding.left,
+ position.y,
+ size * -valuesPerPixel + ThumbSize(),
+ position.height);
+ }
+
+ private float ClampedCurrentValue()
+ {
+ return Clamp(currentValue);
+ }
+
+ private float MousePosition()
+ {
+ if (horiz)
+ return CurrentEvent().mousePosition.x - position.x;
+ return CurrentEvent().mousePosition.y - position.y;
+ }
+
+ private float ValuesPerPixel()
+ {
+ if (horiz)
+ return (position.width - slider.padding.horizontal - ThumbSize()) / (end - start);
+ return (position.height - slider.padding.vertical - ThumbSize()) / (end - start);
+ }
+
+ private float ThumbSize()
+ {
+ if (horiz)
+ return thumb.fixedWidth != 0 ? thumb.fixedWidth : thumb.padding.horizontal;
+ return thumb.fixedHeight != 0 ? thumb.fixedHeight : thumb.padding.vertical;
+ }
+
+ private float MaxValue()
+ {
+ return Mathf.Max(start, end) - size;
+ }
+
+ private float MinValue()
+ {
+ return Mathf.Min(start, end);
+ }
+ }
+}
diff --git a/Runtime/Export/SpritesBindings.txt b/Runtime/Export/SpritesBindings.txt
new file mode 100644
index 0000000..3f9dc97
--- /dev/null
+++ b/Runtime/Export/SpritesBindings.txt
@@ -0,0 +1,191 @@
+C++RAW
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/Scripting.h"
+#if ENABLE_SPRITES
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Filters/Mesh/SpriteRenderer.h"
+#include "Runtime/Graphics/SpriteFrame.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#endif //ENABLE_SPRITES
+
+CSRAW
+using UnityEngine;
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+#if ENABLE_SPRITES
+namespace UnityEngine
+{
+CSRAW
+ ENUM public SpriteAlignment
+ Center = 0,
+ TopLeft = 1,
+ TopCenter = 2,
+ TopRight = 3,
+ LeftCenter = 4,
+ RightCenter = 5,
+ BottomLeft = 6,
+ BottomCenter = 7,
+ BottomRight = 8,
+ Custom = 9,
+ END
+
+CONDITIONAL ENABLE_SPRITES
+ENUM SpritePackingMode
+ Tight = 0,
+ Rectangle
+END
+
+CONDITIONAL ENABLE_SPRITES
+ENUM SpritePackingRotation
+ None = 0,
+ // Reserved
+ Any = 15
+END
+
+CONDITIONAL ENABLE_SPRITES
+ENUM SpriteMeshType
+ FullRect = 0,
+ Tight = 1
+END
+
+CONDITIONAL ENABLE_SPRITES
+/// Describes one sprite frame.
+CLASS Sprite : Object
+
+ CUSTOM public static Sprite Create(Texture2D texture, Rect rect, Vector2 pivot, float pixelsToUnits = 100.0f, uint extrude = 0, SpriteMeshType meshType = SpriteMeshType.Tight)
+ {
+ if (texture.IsNull())
+ return NULL;
+
+ Sprite* sprite = CreateObjectFromCode<Sprite>();
+ sprite->Initialize(texture, rect, pivot, pixelsToUnits, extrude, meshType);
+ return Scripting::ScriptingWrapperFor(sprite);
+ }
+
+ CUSTOM_PROP Bounds bounds
+ {
+ return self->GetBounds();
+ }
+
+ // Sprite definition rectangle on source texture (in texels).
+ CUSTOM_PROP Rect rect
+ {
+ return self->GetRect();
+ }
+
+ CUSTOM_PROP Texture2D texture
+ {
+ return Scripting::ScriptingWrapperFor(self->GetRenderDataForPlayMode().texture);
+ }
+
+ // Sprite rectangle on texture (in texels).
+ CUSTOM_PROP Rect textureRect
+ {
+ const SpriteRenderData& rd = self->GetRenderDataForPlayMode(); // RenderData must match <texture> accessor's behavior.
+ if (rd.settings.packed && rd.settings.packingMode != kSPMRectangle)
+ Scripting::RaiseMonoException("Sprite is not rectangle-packed. TextureRect is invalid.");
+ return rd.textureRect;
+ }
+
+ // Sprite rectangle offset in sprite definition rectangle space (in texels).
+ CSRAW public Vector2 textureRectOffset
+ {
+ get
+ {
+ Vector2 v;
+ Internal_GetTextureRectOffset(this, out v);
+ return v;
+ }
+ }
+
+ CUSTOM_PROP bool packed
+ {
+ return self->GetIsPacked();
+ }
+
+ CUSTOM_PROP SpritePackingMode packingMode
+ {
+ const SpriteRenderData& rd = self->GetRenderData(true); // RenderData must always come from atlasing.
+ if (!rd.settings.packed)
+ Scripting::RaiseMonoException("Sprite is not packed.");
+ return (SpritePackingMode)rd.settings.packingMode;
+ }
+
+ CUSTOM_PROP SpritePackingRotation packingRotation
+ {
+ const SpriteRenderData& rd = self->GetRenderData(true); // RenderData must always come from atlasing.
+ if (!rd.settings.packed)
+ Scripting::RaiseMonoException("Sprite is not packed.");
+ return (SpritePackingRotation)rd.settings.packingRotation;
+ }
+
+ CUSTOM private static void Internal_GetTextureRectOffset(Sprite sprite, out Vector2 output)
+ {
+ const SpriteRenderData& rd = sprite->GetRenderDataForPlayMode(); // RenderData must match <texture> accessor's behavior.
+ if (rd.settings.packed && rd.settings.packingMode != kSPMRectangle)
+ Scripting::RaiseMonoException("Sprite is not rectangle-packed. TextureRectOffset is invalid.");
+ output->x = rd.textureRectOffset.x;
+ output->y = rd.textureRectOffset.y;
+ }
+
+ CONDITIONAL ENABLE_SPRITECOLLIDER
+ CUSTOM_PROP int colliderPathCount
+ {
+ return self->GetPoly().GetPathCount();
+ }
+
+ CONDITIONAL ENABLE_SPRITECOLLIDER
+ CUSTOM public Vector2[] GetColliderPath(int index)
+ {
+ if (index >= self->GetPoly().GetPathCount())
+ {
+ Scripting::RaiseOutOfRangeException("Path %d does not exist.", index);
+ return SCRIPTING_NULL;
+ }
+
+ const Polygon2D::TPath& path = self->GetPoly().GetPath(index);
+ return CreateScriptingArrayStride<Vector2f>(path.data(), path.size(), MONO_COMMON.vector2, sizeof(*path.data()));
+ }
+
+END
+
+CONDITIONAL ENABLE_SPRITES
+/// Renders a Sprite.
+CLASS SpriteRenderer : Renderer
+
+ CSRAW
+ public Sprite sprite
+ {
+ get
+ {
+ return GetSprite_INTERNAL();
+ }
+ set
+ {
+ SetSprite_INTERNAL(value);
+ }
+ }
+
+ CUSTOM private Sprite GetSprite_INTERNAL()
+ {
+ return Scripting::ScriptingWrapperFor(self->GetSprite());
+ }
+
+ CUSTOM private void SetSprite_INTERNAL(Sprite sprite)
+ {
+ self->SetSprite(sprite);
+ }
+
+ AUTO_PROP Color color GetColor SetColor
+
+END
+
+}
+
+#endif //ENABLE_SPRITES
diff --git a/Runtime/Export/StackTrace.cs b/Runtime/Export/StackTrace.cs
new file mode 100644
index 0000000..6e61bd5
--- /dev/null
+++ b/Runtime/Export/StackTrace.cs
@@ -0,0 +1,368 @@
+using System;
+using System.Diagnostics;
+using System.Text;
+using System.Reflection.Emit;
+using System.Reflection;
+using System.Runtime.Serialization;
+using System.Collections;
+
+#if UNITY_WINRT
+using SystemException = System.Exception;
+#endif
+
+namespace UnityEngine
+{
+#if !UNITY_WINRT
+public class StackTraceUtility
+{
+ static string projectFolder = "";
+
+ static internal void SetProjectFolder (string folder)
+ {
+ projectFolder = folder;
+ }
+
+ static public string ExtractStackTrace ()
+ {
+ StackTrace trace = new StackTrace (1, true);
+ string traceString = ExtractFormattedStackTrace (trace).ToString ();
+ return traceString;
+ }
+
+ static bool IsSystemStacktraceType (object name)
+ {
+ string casted = (string)name;
+ return casted.StartsWith ("UnityEditor.") || casted.StartsWith ("UnityEngine.") || casted.StartsWith ("System.") || casted.StartsWith ("UnityScript.Lang.") || casted.StartsWith ("Boo.Lang.") || casted.StartsWith ("UnityEngine.SetupCoroutine");
+ }
+
+ static public string ExtractStringFromException(System.Object exception)
+ {
+ string message = "", stackTrace = "";
+ ExtractStringFromExceptionInternal(exception, out message, out stackTrace);
+ return message + "\n" + stackTrace;
+ }
+
+ static internal void ExtractStringFromExceptionInternal(System.Object exceptiono, out string message, out string stackTrace)
+ {
+ if (exceptiono == null) throw new ArgumentException("ExtractStringFromExceptionInternal called with null exception");
+ var exception = exceptiono as System.Exception;
+ if (exception == null) throw new ArgumentException("ExtractStringFromExceptionInternal called with an exceptoin that was not of type System.Exception");
+
+ // StackTrace might not be available
+ StringBuilder sb = new StringBuilder(exception.StackTrace == null ? 512 : exception.StackTrace.Length*2);
+ message = "";
+ string traceString = "";
+ while (exception != null)
+ {
+ if (traceString.Length == 0)
+ traceString = exception.StackTrace;
+ else
+ traceString = exception.StackTrace + "\n" + traceString;
+
+ string thisMessage = exception.GetType ().Name;
+ string exceptionMessage = "";
+ if (exception.Message!=null) exceptionMessage = exception.Message;
+ if (exceptionMessage.Trim().Length != 0)
+ {
+ thisMessage += ": ";
+ thisMessage += exceptionMessage;
+ }
+ message = thisMessage;
+ if (exception.InnerException != null)
+ {
+ traceString = "Rethrow as " + thisMessage + "\n" + traceString;
+ }
+ exception = exception.InnerException;
+ }
+
+ sb.Append (traceString + "\n");
+
+ StackTrace trace = new StackTrace (1, true);
+ sb.Append (ExtractFormattedStackTrace (trace));
+
+ stackTrace = sb.ToString();
+ }
+
+ static internal string PostprocessStacktrace (string oldString, bool stripEngineInternalInformation)
+ {
+ if (oldString==null) return String.Empty;
+ string [] split = oldString.Split ('\n');
+ StringBuilder sb = new StringBuilder(oldString.Length);
+ for (int i=0; i<split.Length; i++)
+ split[i] = split[i].Trim();
+
+ for (int i=0; i<split.Length; i++)
+ {
+ string newLine = split[i];
+
+ // Ignore empty lines
+ if (newLine.Length == 0 || newLine[0] == '\n')
+ continue;
+
+ // Ignore unmanaged
+ if (newLine.StartsWith ("in (unmanaged)"))
+ continue;
+ // Make GameView GUI stack traces skip editor GUI part
+ if (stripEngineInternalInformation && newLine.StartsWith("UnityEditor.EditorGUIUtility:RenderGameViewCameras")) break;
+ // Ignore deep system stacktraces
+ if (stripEngineInternalInformation && i<split.Length-1 && IsSystemStacktraceType(newLine))
+ {
+ if (IsSystemStacktraceType(split[i+1]))
+ continue;
+ int lineInfo = newLine.IndexOf(" (at");
+ if (lineInfo != -1)
+ newLine = newLine.Substring (0, lineInfo);
+ }
+ // Ignore wrapper managed to native
+ if (newLine.IndexOf ("(wrapper managed-to-native)") != -1)
+ continue;
+ if (newLine.IndexOf ("(wrapper delegate-invoke)") != -1)
+ continue;
+ // Ignore unknown method
+ if (newLine.IndexOf ("at <0x00000> <unknown method>") != -1)
+ continue;
+ // Ignore C++ line information
+ if (stripEngineInternalInformation && newLine.StartsWith ("[") && newLine.EndsWith("]"))
+ continue;
+ // Ignore starting at
+ if (newLine.StartsWith ("at "))
+ {
+ newLine = newLine.Remove (0, 3);
+ }
+
+ // Remove square brace [0x00001]
+ int brace = newLine.IndexOf("[0x");
+ int braceClose = -1;
+ if (brace != -1)
+ braceClose = newLine.IndexOf("]", brace);
+ if (brace != -1 && braceClose > brace)
+ {
+ newLine = newLine.Remove (brace, braceClose - brace + 1);
+ }
+
+ newLine = newLine.Replace(" in <filename unknown>:0", "");
+
+ newLine = newLine.Replace(projectFolder, "");
+
+ // Unify path names to unix style
+ newLine = newLine.Replace('\\','/');
+
+ int inStart = newLine.LastIndexOf (" in ");
+ if (inStart != -1)
+ {
+ newLine = newLine.Remove(inStart, 5);
+ newLine = newLine.Insert(inStart, " (at ");
+ newLine = newLine.Insert(newLine.Length, ")");
+ }
+
+ sb.Append (newLine+"\n");
+ }
+
+ return sb.ToString ();
+ }
+
+ static internal string ExtractFormattedStackTrace (StackTrace stackTrace)
+ {
+ StringBuilder sb = new StringBuilder(255);
+ int iIndex;
+
+ // need to skip over "n" frames which represent the
+ // System.Diagnostics package frames
+ for (iIndex=0; iIndex < stackTrace.FrameCount; iIndex++)
+ {
+ StackFrame frame = stackTrace.GetFrame (iIndex);
+
+ MethodBase mb = frame.GetMethod ();
+ if (mb == null)
+ continue;
+
+ Type classType = mb.DeclaringType;
+ if (classType == null)
+ continue;
+
+ // Add namespace.classname:MethodName
+ String ns = classType.Namespace;
+ if (ns != null && ns.Length != 0)
+ {
+ sb.Append (ns);
+ sb.Append (".");
+ }
+
+ sb.Append (classType.Name);
+ sb.Append (":");
+ sb.Append (mb.Name);
+ sb.Append ("(");
+
+ // Add parameters
+ int j=0;
+ ParameterInfo[] pi = mb.GetParameters();
+ bool fFirstParam = true;
+ while (j < pi.Length)
+ {
+ if (fFirstParam == false)
+ sb.Append (", ");
+ else
+ fFirstParam = false;
+
+ sb.Append (pi[j].ParameterType.Name);
+ j++;
+ }
+ sb.Append (")");
+
+ // Add path name and line number - unless it is a Debug.Log call, then we are only interested
+ // in the calling frame.
+ string path = frame.GetFileName ();
+ if (path != null && !(classType.Name == "Debug" && classType.Namespace == "UnityEngine"))
+ {
+ sb.Append (" (at ");
+
+ if (path.StartsWith (projectFolder))
+ {
+ path = path.Substring (projectFolder.Length, path.Length - projectFolder.Length);
+ }
+ sb.Append (path);
+ sb.Append (":");
+ sb.Append (frame.GetFileLineNumber ().ToString ());
+ sb.Append (")");
+ }
+
+ sb.Append ("\n");
+ }
+
+ return sb.ToString();
+ }
+}
+#endif
+
+[Serializable]
+public class UnityException : SystemException
+{
+ const int Result = unchecked ((int)0x80004003);
+ string unityStackTrace;
+
+ // Constructors
+ public UnityException ()
+ : base ("A Unity Runtime error occurred!")
+ {
+ HResult = Result;
+ }
+
+ public UnityException (string message)
+ : base (message)
+ {
+ HResult = Result;
+ }
+
+ public UnityException (string message, Exception innerException)
+ : base (message, innerException)
+ {
+ HResult = Result;
+ }
+#if !UNITY_WINRT
+ protected UnityException (SerializationInfo info, StreamingContext context)
+ : base (info, context)
+ {
+ }
+#endif
+}
+
+[Serializable]
+public class MissingComponentException : SystemException
+{
+ const int Result = unchecked ((int)0x80004003);
+ string unityStackTrace;
+
+ // Constructors
+ public MissingComponentException ()
+ : base ("A Unity Runtime error occurred!")
+ {
+ HResult = Result;
+ }
+
+ public MissingComponentException (string message)
+ : base (message)
+ {
+ HResult = Result;
+ }
+
+ public MissingComponentException (string message, Exception innerException)
+ : base (message, innerException)
+ {
+ HResult = Result;
+ }
+#if !UNITY_WINRT
+ protected MissingComponentException (SerializationInfo info, StreamingContext context)
+ : base (info, context)
+ {
+ }
+#endif
+}
+
+
+[Serializable]
+public class UnassignedReferenceException : SystemException
+{
+ const int Result = unchecked ((int)0x80004003);
+ string unityStackTrace;
+
+ // Constructors
+ public UnassignedReferenceException ()
+ : base ("A Unity Runtime error occurred!")
+ {
+ HResult = Result;
+ }
+
+ public UnassignedReferenceException (string message)
+ : base (message)
+ {
+ HResult = Result;
+ }
+
+ public UnassignedReferenceException (string message, Exception innerException)
+ : base (message, innerException)
+ {
+ HResult = Result;
+ }
+#if !UNITY_WINRT
+ protected UnassignedReferenceException (SerializationInfo info, StreamingContext context)
+ : base (info, context)
+ {
+ }
+#endif
+}
+
+
+[Serializable]
+public class MissingReferenceException : SystemException
+{
+ const int Result = unchecked ((int)0x80004003);
+ string unityStackTrace;
+
+ // Constructors
+ public MissingReferenceException ()
+ : base ("A Unity Runtime error occurred!")
+ {
+ HResult = Result;
+ }
+
+ public MissingReferenceException (string message)
+ : base (message)
+ {
+ HResult = Result;
+ }
+
+ public MissingReferenceException (string message, Exception innerException)
+ : base (message, innerException)
+ {
+ HResult = Result;
+ }
+#if !UNITY_WINRT
+ protected MissingReferenceException (SerializationInfo info, StreamingContext context)
+ : base (info, context)
+ {
+ }
+#endif
+}
+
+
+}
diff --git a/Runtime/Export/StaticBatching/CombineForStaticBatching.cs b/Runtime/Export/StaticBatching/CombineForStaticBatching.cs
new file mode 100644
index 0000000..ffe5477
--- /dev/null
+++ b/Runtime/Export/StaticBatching/CombineForStaticBatching.cs
@@ -0,0 +1,230 @@
+//using UnityEngine;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+#if !UNITY_FLASH// no static batching in flash
+
+namespace UnityEngine
+{
+internal class InternalStaticBatchingUtility {
+ // assume 16bit indices
+ const int MaxVerticesInBatch = 64000; // a little bit less than 64K - just in case
+ const string CombinedMeshPrefix = "Combined Mesh";
+
+ static public void Combine (UnityEngine.GameObject staticBatchRoot)
+ {
+ Combine(staticBatchRoot, false);
+ }
+
+ static public void Combine (UnityEngine.GameObject staticBatchRoot, bool combineOnlyStatic)
+ {
+ GameObject[] gos = (GameObject[])UnityEngine.Object.FindObjectsOfType(typeof(GameObject));
+
+ List<GameObject> filteredGos = new List<GameObject>();
+ foreach (GameObject go in gos)
+ {
+ if (staticBatchRoot != null)
+ if (!go.transform.IsChildOf(staticBatchRoot.transform))
+ continue;
+
+ if (combineOnlyStatic && !go.isStaticBatchable )
+ continue;
+
+ filteredGos.Add(go);
+ }
+
+ gos = filteredGos.ToArray();
+
+ // Inform user about Advanced license only feature
+ // real license guard is baked into the native code.
+ bool hasProOrAdvancedLicense = Application.HasProLicense() || Application.HasAdvancedLicense();
+ if (!hasProOrAdvancedLicense)
+ {
+ // Display error only if invoked from the user script
+ if (staticBatchRoot != null && gos.Length > 0)
+ Debug.LogError("Your Unity license is not sufficient for Static Batching.");
+ }
+
+ Combine(gos, staticBatchRoot);
+ }
+
+ static public void Combine(GameObject[] gos, UnityEngine.GameObject staticBatchRoot)
+ {
+ Matrix4x4 staticBatchInverseMatrix = Matrix4x4.identity;
+ Transform staticBatchRootTransform = null;
+ if (staticBatchRoot)
+ {
+ staticBatchInverseMatrix = staticBatchRoot.transform.worldToLocalMatrix;
+ staticBatchRootTransform = staticBatchRoot.transform;
+ }
+
+ int batchIndex = 0;
+ int verticesInBatch = 0;
+ List<MeshSubsetCombineUtility.MeshInstance> meshes = new List<MeshSubsetCombineUtility.MeshInstance>();
+ List<MeshSubsetCombineUtility.SubMeshInstance> subsets = new List<MeshSubsetCombineUtility.SubMeshInstance>();
+ List<GameObject> subsetGOs = new List<GameObject> ();
+
+ Array.Sort(gos, new SortGO());
+
+ foreach (GameObject go in gos)
+ {
+ MeshFilter filter = go.GetComponent(typeof(MeshFilter)) as MeshFilter;
+ if (filter == null)
+ continue;
+
+ // reject if has no mesh
+ if (filter.sharedMesh == null)
+ continue;
+
+ // reject if mesh not readable
+ if (!filter.sharedMesh.canAccess)
+ continue;
+
+ // reject if has not renderer or renderer is disabled
+ if (filter.renderer == null || !filter.renderer.enabled)
+ continue;
+
+ // reject if already combined for static batching
+ if (filter.renderer.staticBatchIndex != 0)
+ continue;
+
+ // check if we have enough space inside the current batch
+ if (verticesInBatch + filter.sharedMesh.vertexCount > MaxVerticesInBatch)
+ {
+ MakeBatch (meshes, subsets, subsetGOs, staticBatchRootTransform, batchIndex++);
+ meshes.Clear();
+ subsets.Clear();
+ subsetGOs.Clear();
+ verticesInBatch = 0;
+ }
+
+ MeshSubsetCombineUtility.MeshInstance instance = new MeshSubsetCombineUtility.MeshInstance ();
+ Mesh instanceMesh = filter.sharedMesh;
+ instance.meshInstanceID = instanceMesh.GetInstanceID();
+ instance.transform = staticBatchInverseMatrix * filter.transform.localToWorldMatrix;
+ instance.lightmapTilingOffset = filter.renderer.lightmapTilingOffset;
+
+ //;;Debug.Log("New static mesh (" + go.name + ")verts: " + instance.mesh.vertexCount +
+ // ", tris: " + instance.mesh.triangles.Length +
+ // ", materials: " + filter.renderer.sharedMaterials.Length +
+ // ", subs: " + instance.mesh.subMeshCount
+ // );
+
+ meshes.Add(instance);
+
+ Material[] materials = filter.renderer.sharedMaterials;
+ if (materials.Length > instanceMesh.subMeshCount)
+ {
+ Debug.LogWarning("Mesh has more materials (" + materials.Length + ") than subsets (" + instanceMesh.subMeshCount + ")");
+ // extra materials don't have a meaning and it screws the rendering as Unity
+ // tries to render with those extra materials.
+ Material[] newMats = new Material[instanceMesh.subMeshCount];
+ for (int i = 0; i < instanceMesh.subMeshCount; ++i)
+ newMats[i] = filter.renderer.sharedMaterials[i];
+ filter.renderer.sharedMaterials = newMats;
+ materials = newMats;
+ }
+
+ for (int m = 0; m < System.Math.Min(materials.Length, instanceMesh.subMeshCount); ++m)
+ {
+ //;;Debug.Log(" new subset : " + m + ", tris " + instance.mesh.GetTriangles(m).Length);
+ MeshSubsetCombineUtility.SubMeshInstance subsetInstance = new MeshSubsetCombineUtility.SubMeshInstance();
+ subsetInstance.meshInstanceID = filter.sharedMesh.GetInstanceID();
+ subsetInstance.vertexOffset = verticesInBatch;
+ subsetInstance.subMeshIndex = m;
+ subsetInstance.gameObjectInstanceID = go.GetInstanceID();
+ subsetInstance.transform = instance.transform;
+ subsets.Add(subsetInstance);
+ subsetGOs.Add(go);
+ }
+ verticesInBatch += instanceMesh.vertexCount;
+ }
+
+ MakeBatch(meshes, subsets, subsetGOs, staticBatchRootTransform, batchIndex);
+ }
+
+ static private void MakeBatch(List<MeshSubsetCombineUtility.MeshInstance> meshes, List<MeshSubsetCombineUtility.SubMeshInstance> subsets, List<GameObject> subsetGOs, Transform staticBatchRootTransform, int batchIndex)
+ {
+ if (meshes.Count < 2)
+ return;
+
+ MeshSubsetCombineUtility.MeshInstance[] aMeshes = meshes.ToArray();
+ MeshSubsetCombineUtility.SubMeshInstance[] aSubsets = subsets.ToArray();
+
+ string combinedMeshName = CombinedMeshPrefix;
+ combinedMeshName += " (root: " + ((staticBatchRootTransform != null)? staticBatchRootTransform.name: "scene") + ")";
+ if (batchIndex > 0)
+ combinedMeshName += " " + (batchIndex + 1);
+
+ Mesh combinedMesh = StaticBatchingUtility.InternalCombineVertices (aMeshes, combinedMeshName);
+ StaticBatchingUtility.InternalCombineIndices(aSubsets, combinedMesh);
+
+ int subsetIndex = 0;
+ for (int item = 0; item < aSubsets.Length; item++)
+ {
+ MeshSubsetCombineUtility.SubMeshInstance i = aSubsets[item];
+ GameObject go = subsetGOs[item];
+ Mesh m = combinedMesh;
+
+ MeshFilter filter = (MeshFilter)go.GetComponent(typeof(MeshFilter));
+ filter.sharedMesh = m;
+
+ go.renderer.SetSubsetIndex(i.subMeshIndex, subsetIndex);
+ go.renderer.staticBatchRootTransform = staticBatchRootTransform;
+
+ // for some reason if GOs were created dynamically
+ // then we need to toggle renderer to avoid caching old geometry
+ go.renderer.enabled = false;
+ go.renderer.enabled = true;
+
+ subsetIndex++;
+ }
+ }
+
+ internal class SortGO : IComparer
+ {
+ int IComparer.Compare( object a, object b )
+ {
+ if (a == b)
+ return 0;
+
+ Renderer aRenderer = GetRenderer(a as GameObject);
+ Renderer bRenderer = GetRenderer(b as GameObject);
+
+ int compare = GetMaterialId(aRenderer).CompareTo(GetMaterialId(bRenderer));
+ if (compare == 0)
+ compare = GetLightmapIndex(aRenderer).CompareTo(GetLightmapIndex(bRenderer));
+ return compare;
+ }
+
+ static private int GetMaterialId(Renderer renderer)
+ {
+ if (renderer == null || renderer.sharedMaterial == null)
+ return 0;
+ return renderer.sharedMaterial.GetInstanceID();
+ }
+
+ static private int GetLightmapIndex(Renderer renderer)
+ {
+ if (renderer == null)
+ return -1;
+ return renderer.lightmapIndex;
+ }
+
+ static private Renderer GetRenderer(GameObject go)
+ {
+ if (go == null)
+ return null;
+ MeshFilter filter = go.GetComponent(typeof(MeshFilter)) as MeshFilter;
+ if (filter == null)
+ return null;
+
+ return filter.renderer;
+ }
+ }
+}
+
+} // namespace UnityEngine
+
+#endif
diff --git a/Runtime/Export/StaticBatching/MeshSubsetCombineUtility.cs b/Runtime/Export/StaticBatching/MeshSubsetCombineUtility.cs
new file mode 100644
index 0000000..0c76f14
--- /dev/null
+++ b/Runtime/Export/StaticBatching/MeshSubsetCombineUtility.cs
@@ -0,0 +1,30 @@
+using UnityEngine;
+using System.Collections;
+
+#if !UNITY_FLASH// no static batching in flash
+
+namespace UnityEngine
+{
+
+internal class MeshSubsetCombineUtility {
+
+ public struct MeshInstance
+ {
+ public int meshInstanceID;
+ public Matrix4x4 transform;
+ public Vector4 lightmapTilingOffset;
+ }
+
+ public struct SubMeshInstance
+ {
+ public int meshInstanceID;
+ public int vertexOffset;
+ public int gameObjectInstanceID;
+ public int subMeshIndex;
+ public Matrix4x4 transform;
+ }
+}
+
+} // namespace UnityEngine
+
+#endif
diff --git a/Runtime/Export/SubstanceUtility.txt b/Runtime/Export/SubstanceUtility.txt
new file mode 100644
index 0000000..d35d718
--- /dev/null
+++ b/Runtime/Export/SubstanceUtility.txt
@@ -0,0 +1,436 @@
+// SUBSTANCE HOOK
+
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Graphics/ProceduralTexture.h"
+#include "Runtime/Graphics/SubstanceArchive.h"
+#include "Runtime/Graphics/ProceduralMaterial.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Math/Color.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#if ENABLE_SUBSTANCE
+#include "Runtime/Graphics/SubstanceSystem.h"
+#endif
+#include "Runtime/Scripting/Scripting.h"
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+// The global Substance engine processor usage (as used for the ProceduralMaterial.substanceProcessorUsage property).
+CONDITIONAL ENABLE_SUBSTANCE
+ENUM ProceduralProcessorUsage
+ // Exact control of processor usage is not available.
+ Unsupported = 0,
+
+ // A single physical processor core is used for material generation.
+ One = 1,
+
+ // Half of all physical processor cores are used for material generation.
+ Half = 2,
+
+ // All physical processor cores are used for material generation.
+ All = 3
+END
+
+// Substance memory budget.
+CONDITIONAL ENABLE_SUBSTANCE
+ENUM ProceduralCacheSize
+ // A limit of 128MB for the cache or the working memory.
+ Tiny = 0,
+
+ // A limit of 256MB for the cache or the working memory.
+ Medium = 1,
+
+ // A limit of 512MB for the cache or the working memory.
+ Heavy = 2,
+
+ // No limit for the cache or the working memory.
+ NoLimit = 3,
+
+ // A limit of 1B (one byte) for the cache or the working memory.
+ None = 4
+END
+
+// ProceduralMaterial loading behavior
+CONDITIONAL ENABLE_SUBSTANCE
+ENUM ProceduralLoadingBehavior
+ // Do not generate the textures, allowing a custom loading
+ DoNothing = 0,
+
+ // Generate the textures when loading to favor application's size (default on supported platform)
+ Generate = 1,
+
+ // Bake the textures to speed-up loading, then it may be generated later on
+ BakeAndKeep = 2,
+
+ // Bake the textures and remove dependencies (default on unsupported platform)
+ BakeAndDiscard = 3,
+
+ // Generate the textures when loading and cache them to speed-up subsequent startups
+ Cache = 4
+END
+
+// The type of a Procedural property.
+CONDITIONAL ENABLE_SUBSTANCE
+ENUM ProceduralPropertyType
+ // Procedural boolean property. Use with ProceduralMaterial.GetProceduralBoolean.
+ Boolean = 0,
+
+ // Procedural float property. Use with ProceduralMaterial.GetProceduralFloat.
+ Float = 1,
+
+ // Procedural Vector2 property. Use with ProceduralMaterial.GetProceduralVector.
+ Vector2 = 2,
+
+ // Procedural Vector3 property. Use with ProceduralMaterial.GetProceduralVector.
+ Vector3 = 3,
+
+ // Procedural Vector4 property. Use with ProceduralMaterial.GetProceduralVector.
+ Vector4 = 4,
+
+ // Procedural Color property without alpha. Use with ProceduralMaterial.GetProceduralColor.
+ Color3 = 5,
+
+ // Procedural Color property with alpha. Use with ProceduralMaterial.GetProceduralColor.
+ Color4 = 6,
+
+ // Procedural Enum property. Use with ProceduralMaterial.GetProceduralEnum.
+ Enum = 7,
+
+ // Procedural Texture property. Use with ProceduralMaterial.GetProceduralTexture.
+ Texture = 8
+END
+
+// The type of generated image in a Procedural Material.
+CONDITIONAL ENABLE_SUBSTANCE
+ENUM ProceduralOutputType
+ // Undefined type.
+ Unknown = 0,
+ // Diffuse type.
+ Diffuse = 1,
+ // NormalMap (BumpMap) type.
+ Normal = 2,
+ // HeightMap type.
+ Height = 3,
+ // Emmisive type.
+ Emissive = 4,
+ // Specular (GlossMap) type.
+ Specular = 5,
+ // Opacity (Tranparence) type.
+ Opacity = 6
+END
+
+// Describes a Procedural property.
+CONDITIONAL ENABLE_SUBSTANCE
+CSRAW [StructLayout (LayoutKind.Sequential)]
+CLASS ProceduralPropertyDescription
+ // The name of the Procedural property. Used to get and set the values.
+ CSRAW public string name;
+
+ // The user friendly label of the Procedural property. Used to display Procedural property friendly names.
+ CSRAW public string label;
+
+ // The name of the GUI group. Used to display Procedural property in groups.
+ CSRAW public string group;
+
+ // The [[ProceduralPropertyType]] describes what type of property this is.
+ CSRAW public ProceduralPropertyType type;
+
+ // If true, the Float or Vector property is constrained to values within a specified range.
+ CSRAW public bool hasRange;
+
+ // If hasRange is true, minimum specifies the minimum allowed value for this Float or Vector property.
+ CSRAW public float minimum;
+
+ // If hasRange is true, maximum specifies the maximum allowed value for this Float or Vector property.
+ CSRAW public float maximum;
+
+ // Specifies the step size of this Float or Vector property. Zero is no step.
+ CSRAW public float step;
+
+ // The available options for a Procedural property of type Enum.
+ CSRAW public string[] enumOptions;
+END
+
+C++RAW
+
+#if ENABLE_SUBSTANCE
+struct MonoProceduralPropertyDescription
+{
+ ScriptingStringPtr name;
+ ScriptingStringPtr label;
+ ScriptingStringPtr group;
+ ProceduralPropertyType type;
+ bool hasRange;
+ float minimum;
+ float maximum;
+ float step;
+ ScriptingArrayPtr enumOptions;
+};
+
+void ProceduralPropertyDescriptionToMono (const SubstanceInput& src, MonoProceduralPropertyDescription& dest)
+{
+ dest.name = scripting_string_new(src.name);
+ dest.label = scripting_string_new(src.label);
+ dest.group = scripting_string_new(src.group);
+ dest.type = src.type;
+ dest.hasRange = src.IsFlagEnabled(SubstanceInput::Flag_Clamp);
+ dest.minimum = src.minimum;
+ dest.maximum = src.maximum;
+ dest.step = src.step;
+ dest.enumOptions = Scripting::StringVectorToMono (src.GetEnumOptions ());
+}
+#endif
+
+// ProceduralMaterial class.
+// TODO: Make example use material instead of sharedMaterial when instancing works.
+// TODO: Make example lerp between property min and max value when GetProceduralPropertyDescription is implemented.
+
+CONDITIONAL !UNITY_FLASH && !UNITY_WEBGL
+CLASS ProceduralMaterial : Material
+
+ // Default constructor. This should not be used.
+ // TODO: Constructor that takes a source ProceduralMaterial as parameter and clones it.
+ CSRAW public ProceduralMaterial () : base ("") {}
+
+ // Get an array of descriptions of all the properties this Procedural Material has.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM ProceduralPropertyDescription[] GetProceduralPropertyDescriptions ()
+ {
+ const std::vector<SubstanceInput>& inputs = self->GetSubstanceInputs ();
+ return VectorToScriptingClassArray<SubstanceInput, MonoProceduralPropertyDescription> (inputs, GetScriptingManager().GetCommonClasses().substancePropertyDescription, ProceduralPropertyDescriptionToMono);
+ }
+
+ // Checks if the Procedural Material has a property of a given name.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM bool HasProceduralProperty (string inputName) { return self->HasSubstanceProperty (inputName); }
+
+ // Get a named Procedural boolean property.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM bool GetProceduralBoolean (string inputName) { return self->GetSubstanceBoolean (inputName); }
+
+ // Set a named Procedural boolean property.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM void SetProceduralBoolean (string inputName, bool value) { self->SetSubstanceBoolean (inputName, value); }
+
+ // Get a named Procedural float property.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM float GetProceduralFloat (string inputName) { return self->GetSubstanceFloat (inputName); }
+
+ // Set a named Procedural float property.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM void SetProceduralFloat (string inputName, float value) { self->SetSubstanceFloat (inputName, value); }
+
+ // Get a named Procedural vector property.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM Vector4 GetProceduralVector (string inputName) { return self->GetSubstanceVector (inputName); }
+
+ // Set a named Procedural vector property.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM void SetProceduralVector (string inputName, Vector4 value) { self->SetSubstanceVector (inputName, value); }
+
+ // Get a named Procedural color property.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM Color GetProceduralColor (string inputName) { return self->GetSubstanceColor (inputName); }
+
+ // Set a named Procedural color property.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM void SetProceduralColor (string inputName, Color value) { self->SetSubstanceColor (inputName, value); }
+
+ // Get a named Procedural enum property.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM int GetProceduralEnum (string inputName) { return self->GetSubstanceEnum (inputName); }
+
+ // Set a named Procedural enum property.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM void SetProceduralEnum (string inputName, int value) { self->SetSubstanceEnum (inputName, value); }
+
+ // Get a named Procedural texture property.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM Texture2D GetProceduralTexture (string inputName) { return Scripting::ScriptingWrapperFor (self->GetSubstanceTexture (inputName)); }
+
+ // Set a named Procedural texture property.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM void SetProceduralTexture (string inputName, Texture2D value) { self->SetSubstanceTexture (inputName, value); }
+
+ // Checks if a named Procedural property is cached for efficient runtime tweaking.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM bool IsProceduralPropertyCached (string inputName) { return self->IsSubstancePropertyCached (inputName); }
+
+ // Specifies if a named Procedural property should be cached for efficient runtime tweaking.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM void CacheProceduralProperty (string inputName, bool value) { self->CacheSubstanceProperty (inputName, value); }
+
+ // Clear the Procedural cache.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM void ClearCache () { self->ClearCache (); }
+
+ // Set & get the Procedural cache budget.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM_PROP ProceduralCacheSize cacheSize
+ {
+ return self->GetProceduralMemoryBudget ();
+ }
+ {
+ self->SetProceduralMemoryBudget(value);
+ }
+
+ // Set & get the update rate in millisecond of the animated substance
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM_PROP int animationUpdateRate
+ {
+ return self->GetAnimationUpdateRate ();
+ }
+ {
+ self->SetAnimationUpdateRate(value);
+ }
+
+ // Triggers an asyncronous rebuild of all dirty textures.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM void RebuildTextures ()
+ {
+ self->RebuildTextures ();
+ }
+
+ // Triggers an immediate (synchronous) rebuild of all dirty textures.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM void RebuildTexturesImmediately ()
+ {
+ self->RebuildTexturesImmediately ();
+ }
+
+ // Checks if the Procedural Material is currently in the process of rebuilding the textures.
+ // Note that it doesn't show if it has not changed.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM_PROP bool isProcessing
+ {
+ return self->IsProcessing ();
+ }
+
+ // Remove the current pending rebuilds of the Procedural Material.
+ CUSTOM static void StopRebuilds ()
+ {
+ ProceduralMaterial::StopProcessing ();
+ }
+
+ // Should the Procedural Material be generated at load time?
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM_PROP bool isLoadTimeGenerated
+ {
+ return self->IsFlagEnabled (ProceduralMaterial::Flag_Animated)
+ || self->GetLoadingBehavior ()!=ProceduralLoadingBehavior_None;
+ }
+ {
+ self->SetLoadingBehavior( value ? ProceduralLoadingBehavior_Generate : ProceduralLoadingBehavior_None );
+ }
+
+ // Get Procedural Material loading behavior
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM_PROP ProceduralLoadingBehavior loadingBehavior
+ {
+ return self->GetLoadingBehavior ();
+ }
+
+ // Checks if the Procedural Materials are supported on the current platform.
+ CUSTOM_PROP static bool isSupported
+ {
+ return IsSubstanceSupported ();
+ }
+
+ // Used to specify the Substance engine CPU usage.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM_PROP static ProceduralProcessorUsage substanceProcessorUsage
+ {
+ return ProceduralMaterial::GetProceduralProcessorUsage ();
+ }
+ {
+ ProceduralMaterial::SetProceduralProcessorUsage (value);
+ }
+
+ // XML string of "input/value" pairs (setting the preset rebuilds the textures)
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM_PROP string preset
+ {
+ return scripting_string_new(self->GetPreset ());
+ }
+ {
+ self->SetPreset (value);
+ }
+
+ // Get generated textures.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM Texture[] GetGeneratedTextures ()
+ {
+ return CreateScriptingArrayFromUnityObjects (self->GetPingedTextures (), ClassID (Texture));
+ }
+
+ // Get generated texture by name.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM ProceduralTexture GetGeneratedTexture (string textureName)
+ {
+ return Scripting::ScriptingWrapperFor (self->GetGeneratedTexture (textureName));
+ }
+
+ // Readable substances keep the generated texture data accessible to GetPixels32 (format must be RAW (RGBA or ARGB)).
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM_PROP bool isReadable
+ {
+ return self->IsFlagEnabled (ProceduralMaterial::Flag_Readable);
+ }
+ {
+ self->EnableFlag (ProceduralMaterial::Flag_Readable, value);
+ }
+
+END
+
+CONDITIONAL !UNITY_FLASH && !UNITY_WEBGL
+CLASS ProceduralTexture : Texture
+
+ // The output type of this Texture.
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM ProceduralOutputType GetProceduralOutputType ()
+ {
+ return self->GetType ();
+ }
+
+ // Retrieve the procedural material parent to this texture
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM internal ProceduralMaterial GetProceduralMaterial ()
+ {
+ return Scripting::ScriptingWrapperFor(self->GetSubstanceMaterial ());
+ }
+
+ // Check if the texture has already been generated, at least one time
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM internal bool HasBeenGenerated ()
+ {
+ return self->HasBeenGenerated ();
+ }
+
+ // Get pixels from a generated texture.
+ // The ProceduralMaterial must have the flag isReadable set before a call to RebuildTexturesImmediately and be configured as 'RAW' format (RGBA/ARGB).
+ CONDITIONAL ENABLE_SUBSTANCE
+ CUSTOM Color32[] GetPixels32(int x, int y, int blockWidth, int blockHeight)
+ {
+ int w = blockWidth; if( w < 1 ) w = 1;
+ int h = blockHeight; if( h < 1 ) h = 1;
+
+ ScriptingArray* colors = CreateScriptingArray<ColorRGBA32>(GetMonoManager().GetCommonClasses().color32, w * h);
+ ColorRGBA32* firstElement = Scripting::GetScriptingArrayStart<ColorRGBA32>(colors);
+ self->GetPixels32(x, y, blockWidth, blockHeight, firstElement);
+ return colors;
+ }
+
+END
+
+CSRAW
+} // namespace UnityEngine
diff --git a/Runtime/Export/SyntaxDefs/template-csharp.plist b/Runtime/Export/SyntaxDefs/template-csharp.plist
new file mode 100644
index 0000000..c764621
--- /dev/null
+++ b/Runtime/Export/SyntaxDefs/template-csharp.plist
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>beginCommand</key>
+ <string></string>
+ <key>endCommand</key>
+ <string></string>
+ <key>beginInstruction</key>
+ <string></string>
+ <key>endInstruction</key>
+ <string></string>
+ <key>beginVariable</key>
+ <string></string>
+ <key>endVariable</key>
+ <string></string>
+ <key>firstString</key>
+ <string>"</string>
+ <key>secondString</key>
+ <string>'</string>
+ <key>firstSingleLineComment</key>
+ <string>//</string>
+ <key>secondSingleLineComment</key>
+ <string></string>
+ <key>beginFirstMultiLineComment</key>
+ <string>/*</string>
+ <key>endFirstMultiLineComment</key>
+ <string>*/</string>
+ <key>beginSecondMultiLineComment</key>
+ <string></string>
+ <key>endSecondMultiLineComment</key>
+ <string></string>
+ <key>functionDefinition</key>
+ <string>^\s*(static|public|private|protected|internal).*\(.*\).*\n?\s*{</string>
+ <key>removeFromFunction</key>
+ <string></string>
+ <key>keywordsCaseSensitive</key>
+ <true/>
+ <key>recolourKeywordIfAlreadyColoured</key>
+ <false/>
+ <key>keywords</key>
+ <array>
+ <string>abstract</string>
+ <string>as</string>
+ <string>base</string>
+ <string>break</string>
+ <string>case</string>
+ <string>catch</string>
+ <string>class</string>
+ <string>checked</string>
+ <string>continue</string>
+ <string>default</string>
+ <string>delegate</string>
+ <string>do</string>
+ <string>else</string>
+ <string>enum</string>
+ <string>event</string>
+ <string>explicit</string>
+ <string>extern</string>
+ <string>false</string>
+ <string>for</string>
+ <string>foreach</string>
+ <string>finally</string>
+ <string>fixed</string>
+ <string>goto</string>
+ <string>if</string>
+ <string>implicit</string>
+ <string>in</string>
+ <string>interface</string>
+ <string>internal</string>
+ <string>is</string>
+ <string>lock</string>
+ <string>namespace</string>
+ <string>new</string>
+ <string>null</string>
+ <string>operator</string>
+ <string>out</string>
+ <string>override</string>
+ <string>params</string>
+ <string>private</string>
+ <string>protected</string>
+ <string>public</string>
+ <string>readonly</string>
+ <string>ref</string>
+ <string>return</string>
+ <string>sealed</string>
+ <string>sizeof</string>
+ <string>stackalloc</string>
+ <string>static</string>
+ <string>struct</string>
+ <string>switch</string>
+ <string>this</string>
+ <string>throw</string>
+ <string>true</string>
+ <string>try</string>
+ <string>typeof</string>
+ <string>unchecked</string>
+ <string>unsafe</string>
+ <string>using</string>
+ <string>virtual</string>
+ <string>while</string>
+ <string>#if</string>
+ <string>#else</string>
+ <string>#elif</string>
+ <string>#endif</string>
+ <string>#define</string>
+ <string>#undef</string>
+ <string>#warning</string>
+ <string>#error</string>
+ <string>#line</string>
+ <string>bool</string>
+ <string>byte</string>
+ <string>char</string>
+ <string>const</string>
+ <string>decimal</string>
+ <string>double</string>
+ <string>float</string>
+ <string>int</string>
+ <string>long</string>
+ <string>object</string>
+ <string>uint</string>
+ <string>ushort</string>
+ <string>ulong</string>
+ <string>sbyte</string>
+ <string>short</string>
+ <string>string</string>
+ <string>void</string>
+ </array>
+ <key>autocompleteWords</key>
+ <array>
+ REPLACE_SCRIPT_SYNTAX
+ <string>IEnumerator</string>
+ <string>System</string>
+ <string>UnityEngine</string>
+ <string>UnityEditor</string>
+ <string>ArrayList</string>
+ <string>Length</string>
+ </array>
+</dict>
+</plist>
diff --git a/Runtime/Export/SyntaxDefs/template-javascript.plist b/Runtime/Export/SyntaxDefs/template-javascript.plist
new file mode 100644
index 0000000..344065a
--- /dev/null
+++ b/Runtime/Export/SyntaxDefs/template-javascript.plist
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>beginCommand</key>
+ <string></string>
+ <key>endCommand</key>
+ <string></string>
+ <key>beginInstruction</key>
+ <string></string>
+ <key>endInstruction</key>
+ <string></string>
+ <key>beginVariable</key>
+ <string></string>
+ <key>endVariable</key>
+ <string></string>
+ <key>firstString</key>
+ <string>"</string>
+ <key>secondString</key>
+ <string>'</string>
+ <key>firstSingleLineComment</key>
+ <string>//</string>
+ <key>secondSingleLineComment</key>
+ <string></string>
+ <key>beginFirstMultiLineComment</key>
+ <string>/*</string>
+ <key>endFirstMultiLineComment</key>
+ <string>*/</string>
+ <key>beginSecondMultiLineComment</key>
+ <string></string>
+ <key>endSecondMultiLineComment</key>
+ <string></string>
+ <key>functionDefinition</key>
+ <string>^\s*function\s+.*\n?\s*{</string>
+ <key>removeFromFunction</key>
+ <string>function </string>
+ <key>keywordsCaseSensitive</key>
+ <true/>
+ <key>recolourKeywordIfAlreadyColoured</key>
+ <false/>
+ <key>keywords</key>
+ <array>
+ <string>if</string>
+ <string>else</string>
+ <string>for</string>
+ <string>in</string>
+ <string>while</string>
+ <string>do</string>
+ <string>continue</string>
+ <string>break</string>
+ <string>with</string>
+ <string>try</string>
+ <string>catch</string>
+ <string>switch</string>
+ <string>case</string>
+ <string>new</string>
+ <string>var</string>
+ <string>function</string>
+ <string>return</string>
+ <string>delete</string>
+ <string>true</string>
+ <string>false</string>
+ <string>void</string>
+ <string>throw</string>
+ <string>typeof</string>
+ <string>const</string>
+ <string>default</string>
+ <string>this</string>
+ <string>as</string>
+ <string>int</string>
+ <string>float</string>
+ <string>boolean</string>
+ <string>string</string>
+ <string>yield</string>
+ <string>private</string>
+ <string>protected</string>
+ <string>public</string>
+ <string>internal</string>
+ <string>import</string>
+ <string>String</string>
+ <string>System</string>
+ <string>UnityEngine</string>
+ <string>UnityEditor</string>
+ <string>ArrayList</string>
+ <string>Length</string>
+ <string>class</string>
+ <string>struct</string>
+ <string>enum</string>
+ <string>extends</string>
+ <string>static</string>
+ </array>
+ <key>autocompleteWords</key>
+ <array>
+ REPLACE_SCRIPT_SYNTAX
+ </array>
+</dict>
+</plist>
diff --git a/Runtime/Export/SystemClock.cs b/Runtime/Export/SystemClock.cs
new file mode 100644
index 0000000..a4a26c9
--- /dev/null
+++ b/Runtime/Export/SystemClock.cs
@@ -0,0 +1,10 @@
+namespace UnityEngine
+{
+internal class SystemClock
+{
+ public static System.DateTime now
+ {
+ get { return System.DateTime.Now; }
+ }
+}
+}
diff --git a/Runtime/Export/TextEditor.cs b/Runtime/Export/TextEditor.cs
new file mode 100644
index 0000000..02672ac
--- /dev/null
+++ b/Runtime/Export/TextEditor.cs
@@ -0,0 +1,1203 @@
+using UnityEngine;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace UnityEngine {
+
+public class TextEditor {
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN
+ public TouchScreenKeyboard keyboardOnScreen = null;
+#endif
+
+ public int pos = 0;
+ public int selectPos = 0;
+ public int controlID = 0;
+ public GUIContent content = new GUIContent();
+ public GUIStyle style = GUIStyle.none;
+ public Rect position;
+ public bool multiline = false;
+ public bool hasHorizontalCursorPos = false;
+ public bool isPasswordField = false;
+ internal bool m_HasFocus;
+ public Vector2 scrollOffset = Vector2.zero; // The text field can have a scroll offset in order to display its contents
+
+ // are we up/downing?
+ public Vector2 graphicalCursorPos;
+ public Vector2 graphicalSelectCursorPos;
+
+ // Clear the cursor position for vertical movement...
+ void ClearCursorPos () {hasHorizontalCursorPos = false; m_iAltCursorPos = -1;}
+
+ // selection
+ bool m_MouseDragSelectsWholeWords = false;
+ int m_DblClickInitPos = 0;
+ DblClickSnapping m_DblClickSnap = DblClickSnapping.WORDS;
+ bool m_bJustSelected = false;
+
+ int m_iAltCursorPos = -1;
+
+ public enum DblClickSnapping : byte { WORDS, PARAGRAPHS };
+
+ public void OnFocus ()
+ {
+ if (multiline)
+ pos = selectPos = 0;
+ else
+ SelectAll ();
+ m_HasFocus = true;
+ }
+
+ public void OnLostFocus ()
+ {
+ m_HasFocus = false;
+ scrollOffset = Vector2.zero;
+ }
+
+ void GrabGraphicalCursorPos () {
+ if (!hasHorizontalCursorPos) {
+ graphicalCursorPos = style.GetCursorPixelPosition (position, content, pos);
+ graphicalSelectCursorPos = style.GetCursorPixelPosition (position, content, selectPos);
+ hasHorizontalCursorPos = false;
+ }
+ }
+
+ // Handle a key event.
+ // Looks up the platform-dependent key-action table & performs the event
+ // return true if the event was recognized.
+ public bool HandleKeyEvent (Event e) {
+ InitKeyActions ();
+ EventModifiers m = e.modifiers;
+ e.modifiers &= ~EventModifiers.CapsLock;
+ if (s_Keyactions.ContainsKey(e)) {
+ TextEditOp op = (TextEditOp)s_Keyactions[e];
+ PerformOperation (op);
+ e.modifiers = m;
+ return true;
+ }
+ e.modifiers = m;
+ return false;
+ }
+
+ // Deletes previous text on the line
+ public bool DeleteLineBack()
+ {
+ if (hasSelection)
+ {
+ DeleteSelection();
+ return true;
+ }
+ int p = pos;
+ int i = p;
+ while (i-- != 0)
+ if (content.text[i] == '\n') {
+ p = i + 1;
+ break;
+ }
+ if (i == -1)
+ p = 0;
+ if (pos != p)
+ {
+ content.text = content.text.Remove (p, pos - p);
+ selectPos = pos = p;
+ return true;
+ }
+ return false;
+ }
+
+ // Deletes the previous word
+ public bool DeleteWordBack()
+ {
+ if (hasSelection)
+ {
+ DeleteSelection();
+ return true;
+ }
+
+ int prevWordEnd = FindEndOfPreviousWord(pos);
+ if(pos != prevWordEnd)
+ {
+ content.text = content.text.Remove(prevWordEnd, pos - prevWordEnd);
+ selectPos = pos = prevWordEnd;
+ return true;
+ }
+ return false;
+ }
+
+ // Deletes the following word
+ public bool DeleteWordForward()
+ {
+ if (hasSelection)
+ {
+ DeleteSelection();
+ return true;
+ }
+
+ int nextWordStart = FindStartOfNextWord (pos);
+ if (pos < content.text.Length)
+ {
+ content.text = content.text.Remove(pos, nextWordStart - pos);
+ return true;
+ }
+ return false;
+ }
+
+ // perform a right-delete
+ public bool Delete () {
+ if (hasSelection) {
+ DeleteSelection ();
+ return true;
+ } else
+ if (pos < content.text.Length) {
+ content.text = content.text.Remove (pos, 1);
+ return true;
+ }
+ return false;
+ }
+ public bool CanPaste () {
+ return GUIUtility.systemCopyBuffer.Length != 0;
+ }
+ // Perform a left-delete
+ public bool Backspace () {
+ if (hasSelection) {
+ DeleteSelection ();
+ return true;
+ } else
+ if (pos > 0) {
+ content.text = content.text.Remove (pos - 1, 1);
+ selectPos = pos = pos - 1;
+ ClearCursorPos ();
+ return true;
+ }
+ return false;
+ }
+
+ /// Select all the text
+ public void SelectAll () {
+ pos = 0; selectPos = content.text.Length;
+ ClearCursorPos ();
+ }
+
+ /// Select none of the text
+ public void SelectNone () {
+ selectPos = pos;
+ ClearCursorPos ();
+ }
+
+ /// Does this text field has a selection
+ public bool hasSelection { get { return pos != selectPos; } }
+
+ /// Returns the selected text
+ public string SelectedText {
+ get {
+ int len = content.text.Length;
+ if (pos > len)
+ pos = len;
+ if (selectPos > len)
+ selectPos = len;
+ if (pos == selectPos)
+ return "";
+ if (pos < selectPos)
+ return content.text.Substring(pos, selectPos - pos);
+ else
+ return content.text.Substring(selectPos, pos - selectPos);
+ }
+ }
+
+ /// Delete the current selection. If there is no selection, this function does not do anything...
+ public bool DeleteSelection () {
+ int len = content.text.Length;
+ if (pos > len)
+ pos = len;
+ if (selectPos > len)
+ selectPos = len;
+ if (pos == selectPos)
+ return false;
+ if (pos < selectPos) {
+ content.text = content.text.Substring (0, pos) + content.text.Substring (selectPos, content.text.Length - selectPos);
+ selectPos = pos;
+ } else {
+ content.text = content.text.Substring (0, selectPos) + content.text.Substring (pos, content.text.Length - pos);
+ pos = selectPos;
+ }
+ ClearCursorPos ();
+
+ return true;
+ }
+
+ /// Replace the selection with /replace/. If there is no selection, /replace/ is inserted at the current cursor point.
+ public void ReplaceSelection (string replace) {
+ DeleteSelection ();
+ content.text = content.text.Insert (pos, replace);
+ selectPos = pos += replace.Length;
+ ClearCursorPos ();
+ }
+
+ /// Replacted the selection with /c/
+ public void Insert (char c) {
+ ReplaceSelection (c.ToString());
+ }
+
+ /// Move selection to alt cursor /position/
+ public void MoveSelectionToAltCursor()
+ {
+ if (m_iAltCursorPos == -1)
+ return;
+ int p = m_iAltCursorPos;
+ string tmp = SelectedText;
+ content.text = content.text.Insert (p, tmp);
+
+ if (p < pos)
+ {
+ pos += tmp.Length;
+ selectPos += tmp.Length;
+ }
+
+ DeleteSelection();
+
+ selectPos = pos = p;
+ ClearCursorPos ();
+
+ }
+
+ /// Move the cursor one character to the right and deselect.
+ public void MoveRight () {
+ ClearCursorPos ();
+ if (selectPos == pos) {
+ pos++;
+ ClampPos ();
+ selectPos = pos;
+ } else {
+ if (selectPos > pos)
+ pos = selectPos;
+ else
+ selectPos = pos;
+ }
+ }
+
+ /// Move the cursor one character to the left and deselect.
+ public void MoveLeft () {
+ if (selectPos == pos) {
+ pos--;
+ if (pos < 0)
+ pos = 0;
+ selectPos = pos;
+ } else {
+ if (selectPos > pos)
+ selectPos = pos;
+ else
+ pos = selectPos;
+ }
+ ClearCursorPos ();
+ return;
+ }
+
+ /// Move the cursor up and deselects.
+ public void MoveUp () {
+ if (selectPos < pos)
+ selectPos = pos;
+ else
+ pos = selectPos;
+ GrabGraphicalCursorPos ();
+ //graphicalCursorPos = style.GetCursorPixelPosition (position, content, pos);
+ graphicalCursorPos.y -= 1;
+ pos = selectPos = style.GetCursorStringIndex (position, content, graphicalCursorPos);
+ if (pos <= 0)
+ ClearCursorPos ();
+ }
+
+ /// Move the cursor down and deselects.
+ public void MoveDown () {
+ if (selectPos > pos)
+ selectPos = pos;
+ else
+ pos = selectPos;
+ GrabGraphicalCursorPos ();
+ //graphicalCursorPos = style.GetCursorPixelPosition (position, content, pos);
+ graphicalCursorPos.y += style.lineHeight + 5;
+ pos = selectPos = style.GetCursorStringIndex (position, content, graphicalCursorPos);
+ if (pos == content.text.Length)
+ ClearCursorPos ();
+
+ }
+
+
+ /// Moves the cursor to the start of the current line.
+ public void MoveLineStart () {
+ // we start from the left-most selected character
+ int p = selectPos < pos ? selectPos : pos;
+ // then we scan back to find the first newline
+ int i = p;
+ while (i-- != 0)
+ if (content.text[i] == '\n') {
+ selectPos = pos = i + 1;
+ return;
+ }
+ selectPos = pos = 0;
+ }
+
+ /// Moves the selection to the end of the current line
+ public void MoveLineEnd () {
+ // we start from the right-most selected character
+ int p = selectPos > pos ? selectPos : pos;
+ // then we scan forward to find the first newline
+ int i = p;
+ int strlen = content.text.Length;
+ while (i < strlen) {
+ if (content.text[i] == '\n') {
+ selectPos = pos = i;
+ return;
+ }
+ i++;
+ }
+ selectPos = pos = strlen;
+ }
+ /// Move to the start of the current graphical line. This takes word-wrapping into consideration.
+ public void MoveGraphicalLineStart () {
+ pos = selectPos = GetGraphicalLineStart (pos < selectPos ? pos : selectPos);
+ }
+ /// Move to the end of the current graphical line. This takes word-wrapping into consideration.
+ public void MoveGraphicalLineEnd () {
+ pos = selectPos = GetGraphicalLineEnd (pos > selectPos ? pos : selectPos);
+ }
+
+ /// Moves the cursor to the beginning of the text
+ public void MoveTextStart () {
+ selectPos = pos = 0;
+ }
+
+ /// Moves the cursor to the end of the text
+ public void MoveTextEnd () {
+ selectPos = pos = content.text.Length;
+ }
+
+ /// Move to the next paragraph
+ public void MoveParagraphForward () {
+ pos = pos > selectPos ? pos : selectPos;
+ if (pos < content.text.Length) {
+ selectPos = pos = content.text.IndexOf ('\n', pos + 1);
+ if (pos == -1)
+ selectPos = pos = content.text.Length;
+ }
+ }
+ /// Move to the previous paragraph
+ public void MoveParagraphBackward () {
+ pos = pos < selectPos ? pos : selectPos;
+ if (pos > 1) {
+ selectPos = pos = content.text.LastIndexOf ('\n', pos - 2) + 1;
+ } else
+ selectPos = pos = 0;
+ }
+
+ //
+
+ // Move the cursor to a graphical position. Used for moving the cursor on MouseDown events.
+ public void MoveCursorToPosition (Vector2 cursorPosition) {
+ selectPos = style.GetCursorStringIndex(position, content, cursorPosition + scrollOffset);
+
+ if (!Event.current.shift)
+ {
+ pos = selectPos;
+ }
+
+ ClampPos();
+ }
+ public void MoveAltCursorToPosition (Vector2 cursorPosition) {
+ m_iAltCursorPos = style.GetCursorStringIndex (position, content, cursorPosition + scrollOffset);
+ ClampPos();
+ }
+
+ public bool IsOverSelection(Vector2 cursorPosition) {
+ int p = style.GetCursorStringIndex (position, content, cursorPosition + scrollOffset);
+ return ((p < Mathf.Max(pos, selectPos)) && (p > Mathf.Min(pos, selectPos)));
+ }
+
+ // Do a drag selection. Used to expand the selection in MouseDrag events.
+ public void SelectToPosition (Vector2 cursorPosition) {
+ if (!m_MouseDragSelectsWholeWords)
+ pos = style.GetCursorStringIndex (position, content, cursorPosition + scrollOffset);
+ else // snap to words/paragraphs
+ {
+ int p = style.GetCursorStringIndex (position, content, cursorPosition + scrollOffset);
+
+
+
+ if (m_DblClickSnap == DblClickSnapping.WORDS)
+ {
+ if (p < m_DblClickInitPos)
+ {
+ pos = FindEndOfClassification(p,-1);
+ selectPos = FindEndOfClassification(m_DblClickInitPos,+1) ;
+ }
+ else
+ {
+ if (p >= content.text.Length)
+ p = content.text.Length -1;
+ pos = FindEndOfClassification(p,+1);
+ selectPos = FindEndOfClassification(m_DblClickInitPos - 1,-1);
+ }
+ } // paragraph
+ else
+ {
+ if (p < m_DblClickInitPos)
+ {
+ if (p > 0)
+ pos = content.text.LastIndexOf('\n', p - 2) + 1;
+ else
+ pos = 0;
+
+ selectPos = content.text.LastIndexOf('\n', m_DblClickInitPos);
+ }
+ else
+ {
+ if ( p < content.text.Length)
+ {
+ pos = content.text.IndexOf('\n', p + 1) + 1;
+
+ if (pos <= 0)
+ pos = content.text.Length;
+ }
+ else
+ pos = content.text.Length;
+
+ selectPos = content.text.LastIndexOf('\n', m_DblClickInitPos - 2) + 1;
+ }
+
+ }
+ }
+ }
+
+ /// Expand the selection to the left
+ public void SelectLeft () {
+ if (m_bJustSelected)
+ if (pos > selectPos)
+ { // swap
+ int tmp = pos;
+ pos = selectPos;
+ selectPos = tmp;
+ }
+ m_bJustSelected = false;
+
+ pos--;
+ if (pos < 0)
+ pos = 0;
+ }
+
+ public void SelectRight () {
+ if (m_bJustSelected)
+ if (pos < selectPos)
+ { // swap
+ int tmp = pos;
+ pos = selectPos;
+ selectPos = tmp;
+ }
+ m_bJustSelected = false;
+
+ pos++;
+ int stringlen = content.text.Length;
+ if (pos > stringlen)
+ pos = stringlen;
+ }
+
+ public void SelectUp () {
+ GrabGraphicalCursorPos ();
+ graphicalCursorPos.y -= 1;
+ pos = style.GetCursorStringIndex (position, content, graphicalCursorPos);
+ }
+
+ public void SelectDown () {
+ GrabGraphicalCursorPos ();
+ graphicalCursorPos.y += style.lineHeight + 5;
+ pos = style.GetCursorStringIndex (position, content, graphicalCursorPos);
+ }
+
+ /// Select to the end of the text
+ public void SelectTextEnd () {
+ // This is not quite like tha mac - there, when you select to end of text, the position of the cursor becomes somewhat i'll defined
+ // Hard to explain. In textedit, try: CMD-SHIFT-down, SHIFT-LEFT for case 1. then do CMD-SHIFT-down, SHIFT-RIGHT, SHIFT-LEFT for case 2.
+ // Anyways, it's fucked so we won't do that
+ pos = content.text.Length;
+ }
+
+ /// Select to the start of the text
+ public void SelectTextStart () {
+ // Same thing as SelectTextEnd...
+ pos = 0;
+ }
+
+ /// sets whether the text selection is done by dbl click or not
+ public void MouseDragSelectsWholeWords (bool on)
+ {
+ m_MouseDragSelectsWholeWords = on;
+ m_DblClickInitPos = pos;
+ }
+
+ public void DblClickSnap(DblClickSnapping snapping)
+ {
+ m_DblClickSnap = snapping;
+ }
+
+ int GetGraphicalLineStart (int p) {
+ Vector2 point = style.GetCursorPixelPosition (position, content, p);
+ point.x =0;
+ return style.GetCursorStringIndex (position, content, point);
+ }
+
+ int GetGraphicalLineEnd (int p) {
+ Vector2 point = style.GetCursorPixelPosition (position, content, p);
+ point.x += 5000;
+ return style.GetCursorStringIndex (position, content, point);
+ }
+
+ int FindNextSeperator (int startPos) {
+ int textLen = content.text.Length;
+ while (startPos < textLen && !isLetterLikeChar (content.text[startPos]))
+ startPos++;
+ while (startPos < textLen && isLetterLikeChar (content.text[startPos]))
+ startPos++;
+ return startPos;
+ }
+
+ static bool isLetterLikeChar (char c) {
+ return System.Char.IsLetterOrDigit (c) || c == '\'';
+ }
+
+ int FindPrevSeperator (int startPos) {
+ startPos --;
+ while (startPos > 0 && !isLetterLikeChar (content.text[startPos] ))
+ startPos--;
+
+ while (startPos >= 0 && isLetterLikeChar (content.text[startPos] ))
+ startPos--;
+ return startPos + 1;
+ }
+
+ /// Move to the end of the word.
+ /// If the cursor is over some space characters, these are skipped
+ /// Then, the cursor moves to the end of the following word.
+ /// This corresponds to Alt-RightArrow on a Mac
+ public void MoveWordRight () {
+ pos = pos > selectPos ? pos : selectPos;
+ pos = selectPos = FindNextSeperator (pos);
+ ClearCursorPos ();
+ }
+
+ public void MoveToStartOfNextWord () {
+ ClearCursorPos ();
+ if (pos != selectPos) {
+ MoveRight ();
+ return;
+ }
+ pos = selectPos = FindStartOfNextWord (pos);
+ }
+
+ public void MoveToEndOfPreviousWord () {
+ ClearCursorPos ();
+ if (pos != selectPos) {
+ MoveLeft ();
+ return;
+ }
+ pos = selectPos = FindEndOfPreviousWord (pos);
+ }
+
+
+
+ public void SelectToStartOfNextWord () {
+ ClearCursorPos ();
+ pos = FindStartOfNextWord (pos);
+ }
+
+ public void SelectToEndOfPreviousWord () {
+ ClearCursorPos ();
+ pos = FindEndOfPreviousWord (pos);
+ }
+
+ enum CharacterType {
+ LetterLike,
+ Symbol, Symbol2,
+ WhiteSpace
+ }
+
+ CharacterType ClassifyChar (char c) {
+ if (System.Char.IsWhiteSpace (c))
+ return CharacterType.WhiteSpace;
+ if (System.Char.IsLetterOrDigit (c) || c == '\'')
+ return CharacterType.LetterLike;
+ return CharacterType.Symbol;
+ }
+
+ /// Move to start of next word.
+ /// This corresponds to Ctrl-RightArrow on Windows
+ /// If the cursor is over a whitespace, it's moved forwards ''till the first non-whitespace character
+ /// If the cursor is over an alphanumeric character, it''s moved forward 'till it encounters space or a punctuation mark.
+ /// If the stopping character is a space, this is skipped as well.
+ /// If the cursor is over an punctuation mark, it's moved forward ''till it a letter or a space of a punctuation mark. If the stopping character is a space, this is skipped as well
+ public int FindStartOfNextWord (int p) {
+ int textLen = content.text.Length;
+ if (p == textLen)
+ return p;
+
+ // Find out which char type we're at...
+ char c = content.text[p];
+ CharacterType t = ClassifyChar (c);
+ if (t != CharacterType.WhiteSpace) {
+ p++;
+ while (p < textLen && ClassifyChar (content.text[p]) == t)
+ p++;
+ } else {
+ if ( c == '\t' || c == '\n')
+ return p + 1;
+ }
+
+ if (p == textLen)
+ return p;
+
+ // Skip spaces
+ c = content.text[p];
+ if (c == ' ') { // If we're at a space, skip over any number of spaces
+ while (p < textLen && System.Char.IsWhiteSpace(content.text[p]))
+ p++;
+ } else if (c == '\t' || c == '\n') { // If we're at a tab or a newline, just step one char ahead
+ return p;
+ }
+ return p;
+ }
+
+ int FindEndOfPreviousWord (int p) {
+ if (p == 0)
+ return p;
+ p--;
+
+ // Skip spaces
+ while (p > 0 && content.text[p] == ' ')
+ p--;
+
+ CharacterType t = ClassifyChar (content.text[p]);
+ if (t != CharacterType.WhiteSpace) {
+ while (p > 0 && ClassifyChar (content.text[p - 1]) == t)
+ p--;
+ }
+ return p;
+ }
+
+ public void MoveWordLeft () {
+ pos = pos < selectPos ? pos : selectPos;
+ pos = FindPrevSeperator (pos);
+ selectPos = pos;
+ }
+
+ public void SelectWordRight () {
+ ClearCursorPos ();
+ int cachedPos = selectPos;
+ if (pos < selectPos) {
+ selectPos = pos;
+ MoveWordRight ();
+ selectPos = cachedPos;
+ pos = pos < selectPos ? pos : selectPos;
+ return;
+ }
+ selectPos = pos;
+ MoveWordRight ();
+ selectPos = cachedPos;
+ }
+
+ public void SelectWordLeft () {
+ ClearCursorPos ();
+ int cachedPos = selectPos;
+ if (pos > selectPos) {
+ selectPos = pos;
+ MoveWordLeft ();
+ selectPos = cachedPos;
+ pos = pos > selectPos ? pos : selectPos;
+ return;
+ }
+ selectPos = pos;
+ MoveWordLeft ();
+ selectPos = cachedPos;
+ }
+
+ /// Expand the selection to the start of the line
+ /// Used on a mac for CMD-SHIFT-LEFT
+ public void ExpandSelectGraphicalLineStart () {
+ ClearCursorPos ();
+ if (pos < selectPos)
+ pos = GetGraphicalLineStart (pos);
+ else {
+ int temp = pos;
+ pos = GetGraphicalLineStart (selectPos);
+ selectPos = temp;
+ }
+ }
+
+ /// Expand the selection to the end of the line
+ /// Used on a mac for CMD-SHIFT-RIGHT
+ public void ExpandSelectGraphicalLineEnd () {
+ ClearCursorPos ();
+ if (pos > selectPos)
+ pos = GetGraphicalLineEnd (pos);
+ else {
+ int temp = pos;
+ pos = GetGraphicalLineEnd (selectPos);
+ selectPos = temp;
+ }
+ }
+
+ /// Move the selection point to the start of the line
+ /// Used on a Windows for SHIFT-Home
+ public void SelectGraphicalLineStart () {
+ ClearCursorPos ();
+ pos = GetGraphicalLineStart (pos);
+ }
+
+ /// Expand the selection to the end of the line
+ /// Used on a mac for SHIFT-End
+ public void SelectGraphicalLineEnd () {
+ ClearCursorPos ();
+ pos = GetGraphicalLineEnd (pos);
+
+ }
+
+ public void SelectParagraphForward () {
+ ClearCursorPos ();
+ bool wasBehind = pos < selectPos;
+ if (pos < content.text.Length) {
+ pos = content.text.IndexOf ('\n', pos + 1);
+ if (pos == -1)
+ pos = content.text.Length;
+ if (wasBehind && pos > selectPos)
+ pos = selectPos;
+ }
+ }
+
+ public void SelectParagraphBackward () {
+ ClearCursorPos ();
+ bool wasInFront = pos > selectPos;
+ if (pos > 1) {
+ pos = content.text.LastIndexOf ('\n', pos - 2) + 1;
+ if (wasInFront && pos < selectPos)
+ pos = selectPos;
+ } else
+ selectPos = pos = 0;
+ }
+
+ /// Select the word under the cursor
+ public void SelectCurrentWord () {
+ ClearCursorPos ();
+
+ int textLen = content.text.Length;
+ selectPos = pos;
+
+ // Handle that the text box can be empty
+ if (textLen == 0)
+ return;
+
+ if (pos >= textLen)
+ pos = textLen - 1;
+ if (selectPos >= textLen)
+ selectPos--;
+
+ if (pos < selectPos) {
+ pos = FindEndOfClassification (pos, -1);
+ selectPos = FindEndOfClassification (selectPos, +1);
+ } else {
+ pos = FindEndOfClassification (pos, +1);
+ selectPos = FindEndOfClassification (selectPos, -1);
+ }
+
+ m_bJustSelected = true;
+ }
+
+ int FindEndOfClassification (int p, int dir) {
+ int textLen = content.text.Length;
+
+ if ( p >= textLen || p < 0 )
+ return p;
+
+ CharacterType t = ClassifyChar(content.text[p]);
+ do {
+ p += dir;
+ if (p < 0)
+ return 0;
+ if (p >= textLen)
+ return textLen;
+ } while (ClassifyChar(content.text[p]) == t);
+ if (dir == 1)
+ return p;
+ return p + 1;
+ }
+
+ // Select the entire paragraph the cursor is on (separated by \n)
+ public void SelectCurrentParagraph () {
+ ClearCursorPos ();
+ int textLen = content.text.Length;
+
+ if (pos < textLen) {
+ pos = content.text.IndexOf ('\n', pos);
+ if (pos == -1)
+ pos = content.text.Length;
+ else
+ pos++;
+ }
+ if (selectPos != 0)
+ selectPos = content.text.LastIndexOf ('\n', selectPos - 1) + 1;
+ }
+
+ // TODO: get the height from the font
+
+ public void DrawCursor (string text) {
+ string realText = content.text;
+ int cursorPos = pos;
+ if (Input.compositionString.Length > 0)
+ {
+ content.text = text.Substring (0, pos) + Input.compositionString + text.Substring (selectPos);
+ cursorPos += Input.compositionString.Length;
+ }
+ else
+ content.text = text;
+
+ graphicalCursorPos = style.GetCursorPixelPosition (new Rect (0,0,position.width,position.height), content, cursorPos);
+
+ //Debug.Log("Cursor pos: " + graphicalCursorPos);
+
+ Rect r = style.padding.Remove (position);
+
+ Vector2 contentSize = new Vector2(style.CalcSize(content).x, style.CalcHeight(content, position.width));
+
+ // If there is plenty of room, simply show entire string
+ if (contentSize.x < position.width)
+ {
+ scrollOffset.x = 0;
+ }
+ else
+ {
+ //go right
+ if (graphicalCursorPos.x + 1 > scrollOffset.x + r.width)
+ // do we want html or apple behavior? this is html behavior
+ scrollOffset.x = graphicalCursorPos.x - r.width;
+ //go left
+ if (graphicalCursorPos.x < scrollOffset.x + style.padding.left)
+ scrollOffset.x = graphicalCursorPos.x - style.padding.left;
+ }
+ // ... and height/y as well
+ // If there is plenty of room, simply show entire string
+ // Debug.Log(contentSize.y + " < R : " + r);
+ if (contentSize.y < r.height)
+ {
+ scrollOffset.y = 0;
+ }
+ else
+ {
+ //go down
+ if (graphicalCursorPos.y + style.lineHeight > scrollOffset.y + r.height + style.padding.top)
+ scrollOffset.y = graphicalCursorPos.y - r.height - style.padding.top + style.lineHeight;
+ //go up
+ if (graphicalCursorPos.y < scrollOffset.y + style.padding.top )
+ scrollOffset.y = graphicalCursorPos.y - style.padding.top;
+ }
+
+ // This case takes many words to explain:
+ // 1. Text field has more text than it can fit vertically, and the cursor is at the very bottom (text field is scrolled down)
+ // 2. user e.g. deletes some lines of text at the bottom (backspace or select+delete)
+ // 3. now suddenly we have space at the bottom of text field, that is now not filled with any content
+ // 4. scroll text field up to fill in that space (this is what other text editors do)
+ if (scrollOffset.y > 0 && contentSize.y - scrollOffset.y < r.height)
+ scrollOffset.y = contentSize.y - r.height - style.padding.top - style.padding.bottom;
+
+ scrollOffset.y = scrollOffset.y<0?0:scrollOffset.y;
+
+ Vector2 originalContentOffset = style.contentOffset;
+ style.contentOffset -= scrollOffset;
+ style.Internal_clipOffset = scrollOffset;
+
+ // Debug.Log ("ScrollOffset : " + scrollOffset);
+
+ Input.compositionCursorPos = graphicalCursorPos + new Vector2(position.x, position.y + style.lineHeight) - scrollOffset;
+
+ if(Input.compositionString.Length > 0)
+ style.DrawWithTextSelection (position, content, controlID, pos, pos + Input.compositionString.Length, true);
+ else
+ style.DrawWithTextSelection (position, content, controlID, pos, selectPos);
+
+ if (m_iAltCursorPos != -1)
+ style.DrawCursor(position, content, controlID, m_iAltCursorPos);
+
+ // reset
+ style.contentOffset = originalContentOffset;
+ style.Internal_clipOffset = Vector2.zero;
+
+ content.text = realText;
+ }
+
+
+ bool PerformOperation (TextEditOp operation) {
+
+ switch (operation) {
+// NOTE the TODOs below:
+ case TextEditOp.MoveLeft: MoveLeft (); break;
+ case TextEditOp.MoveRight: MoveRight (); break;
+ case TextEditOp.MoveUp: MoveUp (); break;
+ case TextEditOp.MoveDown: MoveDown (); break;
+ case TextEditOp.MoveLineStart: MoveLineStart (); break;
+ case TextEditOp.MoveLineEnd: MoveLineEnd (); break;
+ case TextEditOp.MoveWordRight: MoveWordRight (); break;
+ case TextEditOp.MoveToStartOfNextWord: MoveToStartOfNextWord (); break;
+ case TextEditOp.MoveToEndOfPreviousWord: MoveToEndOfPreviousWord (); break;
+ case TextEditOp.MoveWordLeft: MoveWordLeft (); break;
+ case TextEditOp.MoveTextStart: MoveTextStart (); break;
+ case TextEditOp.MoveTextEnd: MoveTextEnd (); break;
+ case TextEditOp.MoveParagraphForward: MoveParagraphForward (); break;
+ case TextEditOp.MoveParagraphBackward: MoveParagraphBackward (); break;
+// case TextEditOp.MovePageUp: return MovePageUp (); break;
+// case TextEditOp.MovePageDown: return MovePageDown (); break;
+ case TextEditOp.MoveGraphicalLineStart: MoveGraphicalLineStart (); break;
+ case TextEditOp.MoveGraphicalLineEnd: MoveGraphicalLineEnd (); break;
+ case TextEditOp.SelectLeft: SelectLeft (); break;
+ case TextEditOp.SelectRight: SelectRight (); break;
+ case TextEditOp.SelectUp: SelectUp (); break;
+ case TextEditOp.SelectDown: SelectDown (); break;
+ case TextEditOp.SelectWordRight: SelectWordRight (); break;
+ case TextEditOp.SelectWordLeft: SelectWordLeft (); break;
+ case TextEditOp.SelectToEndOfPreviousWord: SelectToEndOfPreviousWord (); break;
+ case TextEditOp.SelectToStartOfNextWord: SelectToStartOfNextWord (); break;
+
+ case TextEditOp.SelectTextStart: SelectTextStart (); break;
+ case TextEditOp.SelectTextEnd: SelectTextEnd (); break;
+ case TextEditOp.ExpandSelectGraphicalLineStart: ExpandSelectGraphicalLineStart (); break;
+ case TextEditOp.ExpandSelectGraphicalLineEnd: ExpandSelectGraphicalLineEnd (); break;
+ case TextEditOp.SelectParagraphForward: SelectParagraphForward (); break;
+ case TextEditOp.SelectParagraphBackward: SelectParagraphBackward (); break;
+ case TextEditOp.SelectGraphicalLineStart: SelectGraphicalLineStart (); break;
+ case TextEditOp.SelectGraphicalLineEnd: SelectGraphicalLineEnd (); break;
+// case TextEditOp.SelectPageUp: return SelectPageUp (); break;
+// case TextEditOp.SelectPageDown: return SelectPageDown (); break;
+ case TextEditOp.Delete: return Delete ();
+ case TextEditOp.Backspace: return Backspace ();
+ case TextEditOp.Cut: return Cut ();
+ case TextEditOp.Copy: Copy (); break;
+ case TextEditOp.Paste: return Paste ();
+ case TextEditOp.SelectAll: SelectAll (); break;
+ case TextEditOp.SelectNone: SelectNone (); break;
+// case TextEditOp.ScrollStart: return ScrollStart (); break;
+// case TextEditOp.ScrollEnd: return ScrollEnd (); break;
+// case TextEditOp.ScrollPageUp: return ScrollPageUp (); break;
+// case TextEditOp.ScrollPageDown: return ScrollPageDown (); break;
+ case TextEditOp.DeleteWordBack: return DeleteWordBack(); // break; // The uncoditional return makes the "break;" issue a warning about unreachable code
+ case TextEditOp.DeleteLineBack: return DeleteLineBack();
+ case TextEditOp.DeleteWordForward: return DeleteWordForward(); // break; // The uncoditional return makes the "break;" issue a warning about unreachable code
+ default:
+ Debug.Log ("Unimplemented: " + operation);
+ break;
+ }
+ return false;
+ }
+
+ enum TextEditOp {
+ MoveLeft, MoveRight, MoveUp, MoveDown, MoveLineStart, MoveLineEnd, MoveTextStart, MoveTextEnd, MovePageUp, MovePageDown,
+ MoveGraphicalLineStart, MoveGraphicalLineEnd, MoveWordLeft, MoveWordRight,
+ MoveParagraphForward, MoveParagraphBackward, MoveToStartOfNextWord, MoveToEndOfPreviousWord,
+ SelectLeft, SelectRight, SelectUp, SelectDown, SelectTextStart, SelectTextEnd, SelectPageUp, SelectPageDown,
+ ExpandSelectGraphicalLineStart, ExpandSelectGraphicalLineEnd, SelectGraphicalLineStart, SelectGraphicalLineEnd,
+ SelectWordLeft, SelectWordRight, SelectToEndOfPreviousWord, SelectToStartOfNextWord,
+ SelectParagraphBackward, SelectParagraphForward,
+ Delete, Backspace, DeleteWordBack, DeleteWordForward, DeleteLineBack,
+ Cut, Copy, Paste, SelectAll, SelectNone,
+ ScrollStart, ScrollEnd, ScrollPageUp, ScrollPageDown
+ };
+
+ string oldText;
+ int oldPos, oldSelectPos;
+
+ public void SaveBackup () {
+ oldText = content.text;
+ oldPos = pos;
+ oldSelectPos = selectPos;
+ }
+
+ public void Undo () {
+ content.text = oldText;
+ pos = oldPos;
+ selectPos = oldSelectPos;
+ }
+
+ public bool Cut () {
+ //Debug.Log ("Cut");
+ if (isPasswordField)
+ return false;
+ Copy ();
+ return DeleteSelection ();
+ }
+
+ public void Copy () {
+ //Debug.Log ("Copy");
+ if (selectPos == pos)
+ return;
+
+ if (isPasswordField)
+ return;
+
+ string copyStr;
+ if (pos < selectPos)
+ copyStr = content.text.Substring (pos, selectPos - pos);
+ else
+ copyStr = content.text.Substring (selectPos, pos - selectPos);
+
+ GUIUtility.systemCopyBuffer = copyStr;
+ }
+
+ public bool Paste () {
+ //Debug.Log ("Paste");
+ string pasteval = GUIUtility.systemCopyBuffer;
+ if (pasteval != "") {
+ ReplaceSelection (pasteval);
+ return true;
+ }
+ return false;
+ }
+
+ static void MapKey (string key, TextEditOp action) {
+ s_Keyactions [Event.KeyboardEvent (key)] = action;
+ }
+ static Dictionary<Event, TextEditOp> s_Keyactions;
+ /// Set up a platform independant keyboard->Edit action map. This varies depending on whether we are on mac or windows.
+ void InitKeyActions () {
+ if (s_Keyactions != null)
+ return;
+ s_Keyactions = new Dictionary<Event, TextEditOp> ();
+
+ // key mappings shared by the platforms
+ MapKey ("left", TextEditOp.MoveLeft);
+ MapKey ("right", TextEditOp.MoveRight);
+ MapKey ("up", TextEditOp.MoveUp);
+ MapKey ("down", TextEditOp.MoveDown);
+
+ MapKey ("#left", TextEditOp.SelectLeft);
+ MapKey ("#right", TextEditOp.SelectRight);
+ MapKey ("#up", TextEditOp.SelectUp);
+ MapKey ("#down", TextEditOp.SelectDown);
+
+ MapKey ("delete", TextEditOp.Delete);
+ MapKey ("backspace", TextEditOp.Backspace);
+ MapKey ("#backspace", TextEditOp.Backspace);
+
+ // OSX is the special case for input shortcuts
+ if (Application.platform == RuntimePlatform.OSXPlayer ||
+ Application.platform == RuntimePlatform.OSXWebPlayer ||
+ Application.platform == RuntimePlatform.OSXDashboardPlayer ||
+ Application.platform == RuntimePlatform.OSXEditor) {
+ // Keyboard mappings for mac
+// TODO MapKey ("home", TextEditOp.ScrollStart);
+// TODO MapKey ("end", TextEditOp.ScrollEnd);
+// TODO MapKey ("page up", TextEditOp.ScrollPageUp);
+// TODO MapKey ("page down", TextEditOp.ScrollPageDown);
+
+ MapKey ("^left", TextEditOp.MoveGraphicalLineStart);
+ MapKey ("^right", TextEditOp.MoveGraphicalLineEnd);
+// TODO MapKey ("^up", TextEditOp.ScrollPageUp);
+// TODO MapKey ("^down", TextEditOp.ScrollPageDown);
+
+ MapKey ("&left", TextEditOp.MoveWordLeft);
+ MapKey ("&right", TextEditOp.MoveWordRight);
+ MapKey ("&up", TextEditOp.MoveParagraphBackward);
+ MapKey ("&down", TextEditOp.MoveParagraphForward);
+
+ MapKey ("%left", TextEditOp.MoveGraphicalLineStart);
+ MapKey ("%right", TextEditOp.MoveGraphicalLineEnd);
+ MapKey ("%up", TextEditOp.MoveTextStart);
+ MapKey ("%down", TextEditOp.MoveTextEnd);
+
+ MapKey ("#home", TextEditOp.SelectTextStart);
+ MapKey ("#end", TextEditOp.SelectTextEnd);
+// TODO MapKey ("#page up", TextEditOp.SelectPageUp);
+// TODO MapKey ("#page down", TextEditOp.SelectPageDown);
+
+ MapKey ("#^left", TextEditOp.ExpandSelectGraphicalLineStart);
+ MapKey ("#^right", TextEditOp.ExpandSelectGraphicalLineEnd);
+ MapKey ("#^up", TextEditOp.SelectParagraphBackward);
+ MapKey ("#^down", TextEditOp.SelectParagraphForward);
+
+ MapKey ("#&left", TextEditOp.SelectWordLeft);
+ MapKey ("#&right", TextEditOp.SelectWordRight);
+ MapKey ("#&up", TextEditOp.SelectParagraphBackward);
+ MapKey ("#&down", TextEditOp.SelectParagraphForward);
+
+ MapKey ("#%left", TextEditOp.ExpandSelectGraphicalLineStart);
+ MapKey ("#%right", TextEditOp.ExpandSelectGraphicalLineEnd);
+ MapKey ("#%up", TextEditOp.SelectTextStart);
+ MapKey ("#%down", TextEditOp.SelectTextEnd);
+
+ MapKey ("%a", TextEditOp.SelectAll);
+ MapKey ("%x", TextEditOp.Cut);
+ MapKey ("%c", TextEditOp.Copy);
+ MapKey ("%v", TextEditOp.Paste);
+
+ // emacs-like keybindings
+ MapKey ("^d", TextEditOp.Delete);
+ MapKey ("^h", TextEditOp.Backspace);
+ MapKey ("^b", TextEditOp.MoveLeft);
+ MapKey ("^f", TextEditOp.MoveRight);
+ MapKey ("^a", TextEditOp.MoveLineStart);
+ MapKey ("^e", TextEditOp.MoveLineEnd);
+ // Can't be bothered to do these
+ // MapKey ("^o", TextEditOp.InsertNewlineRight);
+ // MapKey ("^t", TextEditOp.TransposeCharacters);
+
+ MapKey("&delete", TextEditOp.DeleteWordForward);
+ MapKey("&backspace", TextEditOp.DeleteWordBack);
+ MapKey ("%backspace", TextEditOp.DeleteLineBack);
+ } else {
+ // Windows/Linux keymappings
+ MapKey("home", TextEditOp.MoveGraphicalLineStart);
+ MapKey ("end", TextEditOp.MoveGraphicalLineEnd);
+// TODO MapKey ("page up", TextEditOp.MovePageUp);
+// TODO MapKey ("page down", TextEditOp.MovePageDown);
+
+ MapKey ("%left", TextEditOp.MoveWordLeft);
+ MapKey ("%right", TextEditOp.MoveWordRight);
+ MapKey ("%up", TextEditOp.MoveParagraphBackward);
+ MapKey ("%down", TextEditOp.MoveParagraphForward);
+
+ MapKey ("^left", TextEditOp.MoveToEndOfPreviousWord);
+ MapKey ("^right", TextEditOp.MoveToStartOfNextWord);
+ MapKey ("^up", TextEditOp.MoveParagraphBackward);
+ MapKey ("^down", TextEditOp.MoveParagraphForward);
+
+ MapKey ("#^left", TextEditOp.SelectToEndOfPreviousWord);
+ MapKey ("#^right", TextEditOp.SelectToStartOfNextWord);
+ MapKey ("#^up", TextEditOp.SelectParagraphBackward);
+ MapKey ("#^down", TextEditOp.SelectParagraphForward);
+
+ MapKey ("#home", TextEditOp.SelectGraphicalLineStart);
+ MapKey ("#end", TextEditOp.SelectGraphicalLineEnd);
+// TODO MapKey ("#page up", TextEditOp.SelectPageUp);
+// TODO MapKey ("#page down", TextEditOp.SelectPageDown);
+
+ MapKey("^delete", TextEditOp.DeleteWordForward);
+ MapKey("^backspace", TextEditOp.DeleteWordBack);
+ MapKey ("%backspace", TextEditOp.DeleteLineBack);
+
+ MapKey ("^a", TextEditOp.SelectAll);
+ MapKey ("^x", TextEditOp.Cut);
+ MapKey ("^c", TextEditOp.Copy);
+ MapKey ("^v", TextEditOp.Paste);
+ MapKey("#delete", TextEditOp.Cut);
+ MapKey("^insert", TextEditOp.Copy);
+ MapKey("#insert", TextEditOp.Paste);
+ }
+ }
+
+ // clamp cursor & selection to the string length
+ public void ClampPos () {
+ if (m_HasFocus == true && controlID != GUIUtility.keyboardControl)
+ OnLostFocus ();
+ if (m_HasFocus == false && controlID == GUIUtility.keyboardControl)
+ OnFocus ();
+
+ if (pos < 0) pos = 0;
+ else if (pos > content.text.Length) pos = content.text.Length;
+ if (selectPos < 0) selectPos = 0;
+ else if (selectPos > content.text.Length) selectPos = content.text.Length;
+ if (m_iAltCursorPos > content.text.Length) m_iAltCursorPos = content.text.Length;
+
+ }
+}
+
+} // namespace
diff --git a/Runtime/Export/TextureBindings.txt b/Runtime/Export/TextureBindings.txt
new file mode 100644
index 0000000..cc79bb5
--- /dev/null
+++ b/Runtime/Export/TextureBindings.txt
@@ -0,0 +1,675 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#include "Runtime/Mono/MonoExportUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+#include "Runtime/Misc/GraphicsScriptingUtility.h"
+
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Graphics/CubemapTexture.h"
+#include "Runtime/Graphics/Texture3D.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Graphics/RenderBufferManager.h"
+#include "Runtime/Graphics/ImageConversion.h"
+#include "Runtime/Geometry/TextureAtlas.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+
+NONSEALED_CLASS Texture : Object
+ CUSTOM_PROP static int masterTextureLimit { return Texture::GetMasterTextureLimit (); } { Texture::SetMasterTextureLimit (value); }
+
+ CUSTOM_PROP static AnisotropicFiltering anisotropicFiltering { return Texture::GetAnisoLimit (); } { Texture::SetAnisoLimit (value); }
+
+ CUSTOM static void SetGlobalAnisotropicFilteringLimits( int forcedMin, int globalMax )
+ {
+ Texture::SetGlobalAnisoLimits(forcedMin, globalMax);
+ }
+
+ CUSTOM private static int Internal_GetWidth (Texture mono) { return mono->GetDataWidth(); }
+ CUSTOM private static int Internal_GetHeight (Texture mono) { return mono->GetDataHeight(); }
+
+ CSRAW virtual public int width { get { return Internal_GetWidth(this); } set { throw new Exception("not implemented"); } }
+ CSRAW virtual public int height { get { return Internal_GetHeight(this); } set { throw new Exception("not implemented"); } }
+
+ AUTO_PROP FilterMode filterMode GetFilterMode SetFilterMode
+ AUTO_PROP int anisoLevel GetAnisoLevel SetAnisoLevel
+ AUTO_PROP TextureWrapMode wrapMode GetWrapMode SetWrapMode
+ AUTO_PROP float mipMapBias GetMipMapBias SetMipMapBias
+
+ // Workaround for gcc/msvc where passing small mono structures by value does not work
+ CUSTOM private static void Internal_GetTexelSize (Texture tex, out Vector2 output)
+ {
+ Texture& texture = *tex;
+ output->x = texture.GetTexelSizeX();
+ output->y = texture.GetTexelSizeY();
+ }
+
+ CSRAW public Vector2 texelSize { get { Vector2 r; Internal_GetTexelSize(this, out r); return r; } }
+
+ CUSTOM IntPtr GetNativeTexturePtr () {
+ return self->GetNativeTexturePtr();
+ }
+
+ CUSTOM int GetNativeTextureID () {
+ return self->GetNativeTextureID();
+ }
+
+END
+
+
+CLASS Texture2D : Texture
+ C++RAW
+ #define CHECK_READABLE { if (!self->GetIsReadable()) Scripting::RaiseMonoException("Texture '%s' is not readable, the texture memory can not be accessed from scripts. You can make the texture readable in the Texture Import Settings.", self->GetName()); }
+
+ // Check if reading of texture is allowed by crossdomain security and throw if not
+ C++RAW
+ static void CheckReadAllowedAndThrow(Texture2D *tex)
+ {
+#if ENABLE_MONO && ENABLE_SECURITY
+ if ( !tex->GetReadAllowed() )
+ Scripting::RaiseSecurityException("No read access to the texture data: %s", tex->GetName());
+#endif
+ }
+
+ C++RAW
+ static void CheckScreenReadAllowedAndThrow()
+ {
+ if ( !Texture2D::GetScreenReadAllowed() )
+ Scripting::RaiseSecurityException("Reading from the screen is not allowed when you have used a downloaded texture without proper crossdomain.xml authorization");
+ }
+
+ AUTO_PROP int mipmapCount CountDataMipmaps
+
+ CSRAW public Texture2D (int width, int height)
+ {
+ Internal_Create(this, width, height, TextureFormat.ARGB32, true, false, IntPtr.Zero);
+ }
+
+ CSRAW public Texture2D (int width, int height, TextureFormat format, bool mipmap)
+ {
+ Internal_Create(this, width, height, format, mipmap, false, IntPtr.Zero);
+ }
+
+ CSRAW public Texture2D (int width, int height, TextureFormat format, bool mipmap, bool linear)
+ {
+ Internal_Create(this, width, height, format, mipmap, linear, IntPtr.Zero);
+ }
+
+ CUSTOM private static void Internal_Create ([Writable]Texture2D mono, int width, int height, TextureFormat format, bool mipmap, bool linear, IntPtr nativeTex)
+ {
+ if(!GetBuildSettings().hasAdvancedVersion && nativeTex != 0)
+ {
+ Scripting::RaiseMonoException("Creating texture from native texture is PRO only.");
+ return;
+ }
+
+ Texture2D* texture = NEW_OBJECT_MAIN_THREAD (Texture2D);
+ texture->Reset();
+
+ if (texture->InitTexture(width, height, format, mipmap ? Texture2D::kMipmapMask : Texture2D::kNoMipmap, 1, (intptr_t)nativeTex))
+ {
+ texture->SetStoredColorSpace (linear ? kTexColorSpaceLinear : kTexColorSpaceSRGB);
+ Scripting::ConnectScriptingWrapperToObject (mono.GetScriptingObject(), texture);
+ texture->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+ }
+ else
+ {
+ DestroySingleObject (texture);
+ Scripting::RaiseMonoException("Failed to create texture because of invalid parameters.");
+ }
+ }
+
+ CONDITIONAL ENABLE_TEXTUREID_MAP
+ CSRAW internal Texture2D (int width, int height, TextureFormat format, bool mipmap, bool linear, IntPtr nativeTex)
+ {
+ Internal_Create(this, width, height, format, mipmap, linear, nativeTex);
+ }
+
+ CONDITIONAL ENABLE_TEXTUREID_MAP
+ CSRAW static public Texture2D CreateExternalTexture(int width, int height, TextureFormat format, bool mipmap, bool linear, IntPtr nativeTex)
+ {
+ return new Texture2D(width, height, format, mipmap, linear, nativeTex);
+ }
+
+ CONDITIONAL ENABLE_TEXTUREID_MAP
+ CUSTOM public void UpdateExternalTexture(IntPtr nativeTex)
+ {
+ if(!GetBuildSettings().hasAdvancedVersion)
+ {
+ Scripting::RaiseMonoException("Updating native texture is PRO only.");
+ return;
+ }
+ GetGfxDevice().UpdateExternalTextureFromNative(self->GetTextureID(), (intptr_t)nativeTex);
+ }
+
+ AUTO_PROP TextureFormat format GetTextureFormat
+
+ CUSTOM void SetPixel (int x, int y, Color color)
+ {
+ CHECK_READABLE
+ self->SetPixel (0, x, y, color);
+ }
+ CUSTOM Color GetPixel (int x, int y) {
+ CHECK_READABLE
+ CheckReadAllowedAndThrow(self);
+ return self->GetPixel (0, x, y);
+ }
+ CUSTOM Color GetPixelBilinear (float u, float v) {
+ CHECK_READABLE
+ CheckReadAllowedAndThrow(self);
+ return self->GetPixelBilinear (0, u, v);
+ }
+ CSRAW public void SetPixels(Color[] colors, int miplevel = 0)
+ {
+ int w = width >> miplevel; if( w < 1 ) w = 1;
+ int h = height >> miplevel; if( h < 1 ) h = 1;
+ SetPixels( 0, 0, w, h, colors, miplevel );
+ }
+ CUSTOM void SetPixels(int x, int y, int blockWidth, int blockHeight, Color[] colors, int miplevel = 0)
+ {
+ CHECK_READABLE
+ self->SetPixels( x, y, blockWidth, blockHeight, GetScriptingArraySize(colors), Scripting::GetScriptingArrayStart<ColorRGBAf>(colors), miplevel );
+ }
+ CUSTOM void SetPixels32(Color32[] colors, int miplevel = 0)
+ {
+ CHECK_READABLE
+ self->SetPixels32( miplevel, Scripting::GetScriptingArrayStart<ColorRGBA32>(colors), GetScriptingArraySize(colors) );
+ }
+
+ CUSTOM bool LoadImage (byte[] data)
+ {
+ return LoadMemoryBufferIntoTexture(*self, Scripting::GetScriptingArrayStart<UInt8>(data), GetScriptingArraySize(data), IsCompressedDXTTextureFormat(self->GetTextureFormat())?kLoadImageDXTCompressDithered:kLoadImageUncompressed);
+ }
+
+ CUSTOM void LoadRawTextureData(byte[] data)
+ {
+ CHECK_READABLE
+ if(GetScriptingArraySize(data) < self->GetRawImageDataSize())
+ {
+ Scripting::RaiseMonoException("LoadRawTextureData: not enough data provided (will result in overread).");
+ return;
+ }
+ ::memcpy(self->GetRawImageData(), Scripting::GetScriptingArrayStart<UInt8>(data), self->GetRawImageDataSize());
+ }
+
+ CSRAW public Color[] GetPixels(int miplevel = 0)
+ {
+ int w = width >> miplevel; if( w < 1 ) w = 1;
+ int h = height >> miplevel; if( h < 1 ) h = 1;
+ return GetPixels( 0, 0, w, h, miplevel );
+ }
+ CUSTOM Color[] GetPixels(int x, int y, int blockWidth, int blockHeight, int miplevel = 0)
+ {
+ CHECK_READABLE
+ CheckReadAllowedAndThrow(self);
+
+ int res = blockWidth * blockHeight;
+ if (blockWidth != 0 && blockHeight != res / blockWidth) {
+ return SCRIPTING_NULL;
+ }
+
+
+ ScriptingArrayPtr colors = CreateScriptingArray<ColorRGBAf>(GetScriptingManager().GetCommonClasses().color, blockWidth * blockHeight);
+ ColorRGBAf* firstElement = Scripting::GetScriptingArrayStart<ColorRGBAf>(colors);
+ self->GetPixels( x, y, blockWidth, blockHeight, miplevel, firstElement);
+ return colors;
+ }
+
+ CUSTOM public Color32[] GetPixels32(int miplevel = 0)
+ {
+ CHECK_READABLE
+ CheckReadAllowedAndThrow(self);
+ int w = self->GetDataWidth() >> miplevel; if( w < 1 ) w = 1;
+ int h = self->GetDataHeight() >> miplevel; if( h < 1 ) h = 1;
+
+
+ ScriptingArrayPtr colors = CreateScriptingArray<ColorRGBA32>(GetScriptingManager().GetCommonClasses().color32, w * h);
+ ColorRGBA32* firstElement = Scripting::GetScriptingArrayStart<ColorRGBA32>(colors);
+ self->GetPixels32( miplevel, firstElement);
+ return colors;
+ }
+
+ CUSTOM void Apply (bool updateMipmaps=true, bool makeNoLongerReadable=false)
+ {
+ CHECK_READABLE
+ self->Apply(updateMipmaps, makeNoLongerReadable);
+ }
+
+ CUSTOM public bool Resize (int width, int height, TextureFormat format, bool hasMipMap)
+ {
+ return self->ResizeWithFormat (width, height, format, hasMipMap ? Texture2D::kMipmapMask : Texture2D::kNoMipmap);
+ }
+
+ CSRAW public bool Resize (int width, int height) { return Internal_ResizeWH(width, height); }
+ CUSTOM private bool Internal_ResizeWH (int width, int height) {
+ CHECK_READABLE
+ return self->Resize(width, height);
+ }
+
+ AUTO void Compress (bool highQuality);
+
+ CUSTOM Rect[] PackTextures( Texture2D[] textures, int padding, int maximumAtlasSize = 2048, bool makeNoLongerReadable = false )
+ {
+ int textureCount = GetScriptingArraySize(textures);
+ Texture2D** texturePtrs = new Texture2D*[textureCount];
+ for( int i = 0; i < textureCount; ++i )
+ {
+ Texture2D* tex = ScriptingObjectToObject<Texture2D>(Scripting::GetScriptingArrayElementNoRef<ScriptingObjectPtr>(textures,i));
+ if (tex && !tex->GetIsReadable())
+ {
+ ErrorString("Texture atlas needs textures to have Readable flag set!");
+ tex = NULL;
+ }
+ texturePtrs[i] = tex;
+ }
+
+ ScriptingArrayPtr rects = CreateScriptingArray<Rectf>(GetScriptingManager().GetCommonClasses().rect, textureCount);
+ Rectf* firstElement = Scripting::GetScriptingArrayStart<Rectf>(rects);
+
+ if( !PackTextureAtlasSimple( &*self, maximumAtlasSize, textureCount, texturePtrs, firstElement, padding, true, makeNoLongerReadable ) )
+ {
+ delete[] texturePtrs; // TODO: error
+ return SCRIPTING_NULL;
+ }
+
+ delete[] texturePtrs;
+ return rects;
+ }
+
+ CUSTOM void ReadPixels (Rect source, int destX, int destY, bool recalculateMipMaps = true) {
+ #if !UNITY_FLASH
+ CHECK_READABLE
+ CheckScreenReadAllowedAndThrow();
+ bool flipVertical = GetGfxDevice().GetInvertProjectionMatrix();
+ self->ReadPixels (0, (int)source.x, (int)source.y, (int)source.Width(), (int)source.Height(), destX, destY, flipVertical, recalculateMipMaps);
+ #elif UNITY_FLASH
+ printf_console("ReadPixels is unsupported on Flash");
+ #endif
+ }
+
+ CUSTOM byte[] EncodeToPNG ()
+ {
+ #if ENABLE_PNG_JPG
+ CHECK_READABLE
+ CheckReadAllowedAndThrow(self);
+
+ Texture2D* tex = self;
+ if( !tex )
+ {
+ ErrorString( "EncodeToPNG failed: texture is null" );
+ return SCRIPTING_NULL;
+ }
+ dynamic_array<UInt8> buffer;
+ if( !tex->EncodeToPNG( buffer ) )
+ {
+ return SCRIPTING_NULL;
+ }
+ return CreateScriptingArray<UInt8>( &buffer[0], buffer.size(), GetScriptingManager().GetCommonClasses().byte );
+ #else
+ return SCRIPTING_NULL;
+ #endif
+ }
+
+ CONDITIONAL UNITY_EDITOR
+ AUTO_PROP bool alphaIsTransparency GetAlphaIsTransparency SetAlphaIsTransparency
+
+END
+
+
+
+CLASS Cubemap : Texture
+ CUSTOM void SetPixel (CubemapFace face, int x, int y, Color color) {
+ CHECK_READABLE
+ self->SetPixel (face, x, y, color);
+ }
+ CUSTOM Color GetPixel (CubemapFace face, int x, int y) {
+ CHECK_READABLE
+ return self->GetPixel (face, x, y);
+ }
+ CUSTOM Color[] GetPixels(CubemapFace face, int miplevel = 0)
+ {
+ CHECK_READABLE
+ int size = std::max(self->GetDataWidth() >> miplevel, 1);
+
+ ScriptingArrayPtr colors = CreateScriptingArray<ColorRGBAf>(GetScriptingManager().GetCommonClasses().color, size*size);
+ ColorRGBAf* firstElement = Scripting::GetScriptingArrayStart<ColorRGBAf>(colors);
+ self->GetPixels( 0, 0, size, size, miplevel, firstElement, (int)face );
+ return colors;
+ }
+ CUSTOM void SetPixels(Color[] colors, CubemapFace face, int miplevel = 0)
+ {
+ CHECK_READABLE
+ int size = std::max(self->GetDataWidth() >> miplevel, 1);
+ self->SetPixels( 0, 0, size, size, GetScriptingArraySize(colors), Scripting::GetScriptingArrayStart<ColorRGBAf>(colors), miplevel, (int)face );
+ }
+
+ CUSTOM void Apply (bool updateMipmaps = true, bool makeNoLongerReadable=false)
+ {
+ #if !UNITY_EDITOR
+ CHECK_READABLE
+ #endif
+
+ if(makeNoLongerReadable)
+ {
+ self->SetIsReadable(false);
+ self->SetIsUnreloadable(true);
+ }
+
+ if (updateMipmaps)
+ self->UpdateImageData();
+ else
+ self->UpdateImageDataDontTouchMipmap();
+ }
+
+ AUTO_PROP TextureFormat format GetTextureFormat
+
+ CSRAW public Cubemap (int size, TextureFormat format, bool mipmap)
+ {
+ Internal_Create(this, size, format, mipmap);
+ }
+
+ CUSTOM private static void Internal_Create ([Writable]Cubemap mono, int size, TextureFormat format, bool mipmap)
+ {
+ Cubemap* texture = NEW_OBJECT_MAIN_THREAD (Cubemap);
+ texture->Reset();
+
+ if (texture->InitTexture(size, size, format, mipmap ? Texture2D::kMipmapMask : Texture2D::kNoMipmap, 6))
+ {
+ Scripting::ConnectScriptingWrapperToObject (mono.GetScriptingObject(), texture);
+ texture->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+ }
+ else
+ {
+ Scripting::RaiseMonoException("Failed to create texture because of invalid parameters.");
+ }
+ }
+
+ CUSTOM void SmoothEdges (int smoothRegionWidthInPixels = 1)
+ {
+ self->FixupEdges (smoothRegionWidthInPixels);
+ }
+
+END
+
+
+CLASS Texture3D : Texture
+
+ AUTO_PROP int depth GetDepth
+
+ CUSTOM Color[] GetPixels(int miplevel = 0)
+ {
+ int w = std::max(self->GetDataWidth() >> miplevel, 1);
+ int h = std::max(self->GetDataHeight() >> miplevel, 1);
+ int d = std::max(self->GetDepth() >> miplevel, 1);
+
+ ScriptingArrayPtr colors = CreateScriptingArray<ColorRGBAf>(GetScriptingManager().GetCommonClasses().color, w*h*d);
+ ColorRGBAf* firstElement = Scripting::GetScriptingArrayStart<ColorRGBAf>(colors);
+ self->GetPixels (firstElement, miplevel);
+ return colors;
+ }
+
+ CUSTOM void SetPixels(Color[] colors, int miplevel = 0)
+ {
+ int arrSize = GetScriptingArraySize(colors);
+ const ColorRGBAf* ptr = Scripting::GetScriptingArrayStart<ColorRGBAf>(colors);
+ self->SetPixels (arrSize, ptr, miplevel);
+ }
+
+ CUSTOM void Apply (bool updateMipmaps = true)
+ {
+ self->UpdateImageData (updateMipmaps);
+ }
+
+ AUTO_PROP TextureFormat format GetTextureFormat
+
+ CSRAW public Texture3D (int width, int height, int depth, TextureFormat format, bool mipmap)
+ {
+ Internal_Create(this, width, height, depth, format, mipmap);
+ }
+
+ CUSTOM private static void Internal_Create ([Writable]Texture3D mono, int width, int height, int depth, TextureFormat format, bool mipmap)
+ {
+ Texture3D* texture = NEW_OBJECT_MAIN_THREAD (Texture3D);
+ texture->Reset();
+
+ if (texture->InitTexture(width, height, depth, format, mipmap))
+ {
+ Scripting::ConnectScriptingWrapperToObject (mono.GetScriptingObject(), texture);
+ texture->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+ }
+ else
+ {
+ Scripting::RaiseMonoException("Failed to create texture because of invalid parameters.");
+ }
+ }
+END
+
+
+CLASS RenderTexture : Texture
+ CUSTOM private static void Internal_CreateRenderTexture ([Writable]RenderTexture rt)
+ {
+ RenderTexture* texture = NEW_OBJECT_MAIN_THREAD (RenderTexture);
+ texture->SetCreatedFromScript(true);
+ texture->Reset();
+ Scripting::ConnectScriptingWrapperToObject (rt.GetScriptingObject(), texture);
+ texture->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+ }
+
+ CSRAW public RenderTexture (int width, int height, int depth, RenderTextureFormat format, RenderTextureReadWrite readWrite)
+ {
+ Internal_CreateRenderTexture (this);
+ this.width = width;
+ this.height = height;
+ this.depth = depth;
+ this.format = format;
+
+ bool sRGB = readWrite == RenderTextureReadWrite.sRGB;
+ if (readWrite == RenderTextureReadWrite.Default)
+ {
+ sRGB = QualitySettings.activeColorSpace == ColorSpace.Linear;
+ }
+ Internal_SetSRGBReadWrite (this, sRGB);
+
+ this.isPowerOfTwo = Mathf.IsPowerOfTwo(width) && Mathf.IsPowerOfTwo(height);
+ }
+
+ CSRAW public RenderTexture (int width, int height, int depth, RenderTextureFormat format)
+ {
+ Internal_CreateRenderTexture (this);
+ this.width = width;
+ this.height = height;
+ this.depth = depth;
+ this.format = format;
+ Internal_SetSRGBReadWrite (this, QualitySettings.activeColorSpace == ColorSpace.Linear);
+ this.isPowerOfTwo = Mathf.IsPowerOfTwo(width) && Mathf.IsPowerOfTwo(height);
+ }
+
+ CSRAW public RenderTexture (int width, int height, int depth)
+ {
+ Internal_CreateRenderTexture (this);
+ this.width = width;
+ this.height = height;
+ this.depth = depth;
+ this.format = RenderTextureFormat.Default;
+ Internal_SetSRGBReadWrite (this, QualitySettings.activeColorSpace == ColorSpace.Linear);
+ this.isPowerOfTwo = Mathf.IsPowerOfTwo(width) && Mathf.IsPowerOfTwo(height);
+ }
+
+ CUSTOM static RenderTexture GetTemporary (int width, int height, int depthBuffer = 0, RenderTextureFormat format = RenderTextureFormat.Default, RenderTextureReadWrite readWrite = RenderTextureReadWrite.Default, int antiAliasing = 1) {
+ DepthBufferFormat depthFormat = DepthBufferFormatFromBits(depthBuffer);
+ UInt32 flags = RenderBufferManager::kRBCreatedFromScript;
+ return Scripting::ScriptingWrapperFor (GetRenderBufferManager().GetTempBuffer (width, height, depthFormat, static_cast<RenderTextureFormat>(format), flags, readWrite, antiAliasing));
+ }
+
+ CUSTOM static void ReleaseTemporary (RenderTexture temp) {
+ GetRenderBufferManager().ReleaseTempBuffer (temp);
+ }
+
+ CUSTOM private static int Internal_GetWidth (RenderTexture mono) { return mono->GetWidth(); }
+ CUSTOM private static void Internal_SetWidth (RenderTexture mono, int width) { mono->SetWidth(width); }
+ CUSTOM private static int Internal_GetHeight (RenderTexture mono) { return mono->GetHeight(); }
+ CUSTOM private static void Internal_SetHeight (RenderTexture mono, int width) { mono->SetHeight(width); }
+
+ CUSTOM private static void Internal_SetSRGBReadWrite (RenderTexture mono, bool sRGB) { mono->SetSRGBReadWrite(sRGB); }
+
+ CSRAW
+ override public int width { get { return Internal_GetWidth(this); } set { Internal_SetWidth(this,value); } }
+
+ CSRAW
+ override public int height { get { return Internal_GetHeight(this); } set { Internal_SetHeight(this,value); } }
+
+ CUSTOM_PROP int depth
+ {
+ DepthBufferFormat depthFormat = self->GetDepthFormat();
+ static int kDepthFormatBits[kDepthFormatCount] = { 0, 16, 24 };
+ return kDepthFormatBits[depthFormat];
+ }
+ {
+ DepthBufferFormat depthFormat = DepthBufferFormatFromBits(value);
+ self->SetDepthFormat( depthFormat );
+ }
+
+ CUSTOM_PROP bool isPowerOfTwo { return self->GetIsPowerOfTwo(); } { }
+
+ CUSTOM_PROP bool sRGB { return self->GetSRGBReadWrite(); }
+
+ CUSTOM_PROP RenderTextureFormat format { return self->GetColorFormat(); } { self->SetColorFormat( static_cast<RenderTextureFormat>(value) ); }
+
+ AUTO_PROP bool useMipMap GetMipMap SetMipMap
+
+ AUTO_PROP bool generateMips GetGenerateMips SetGenerateMips
+
+ CUSTOM_PROP bool isCubemap
+ {
+ return self->GetDimension()==kTexDimCUBE;
+ }
+ {
+ self->SetDimension (value ? kTexDimCUBE : kTexDim2D);
+ }
+
+ CUSTOM_PROP bool isVolume
+ {
+ return self->GetDimension()==kTexDim3D;
+ }
+ {
+ self->SetDimension (value ? kTexDim3D : kTexDim2D);
+ }
+
+ CUSTOM_PROP int volumeDepth { return self->GetVolumeDepth(); } { self->SetVolumeDepth(value); }
+
+ AUTO_PROP int antiAliasing GetAntiAliasing SetAntiAliasing
+
+ AUTO_PROP bool enableRandomWrite GetEnableRandomWrite SetEnableRandomWrite
+
+
+ AUTO bool Create ();
+ AUTO void Release ();
+ AUTO bool IsCreated ();
+
+ AUTO void DiscardContents();
+ CUSTOM void DiscardContents(bool discardColor, bool discardDepth)
+ {
+ self->DiscardContents(discardColor, discardDepth);
+ }
+
+ AUTO void MarkRestoreExpected();
+
+ CSRAW public RenderBuffer colorBuffer { get { RenderBuffer res; GetColorBuffer (out res); return res; } }
+
+ CSRAW public RenderBuffer depthBuffer { get { RenderBuffer res; GetDepthBuffer (out res); return res; } }
+
+ CUSTOM private void GetColorBuffer (out RenderBuffer res) {
+ RenderTexture* rt = self;
+ if (rt)
+ {
+ res->m_RenderTextureInstanceID = rt->GetInstanceID();
+ if (!rt->IsCreated())
+ rt->Create();
+ res->m_BufferPtr = rt->GetColorSurfaceHandle().object;
+ }
+ else
+ {
+ res->m_RenderTextureInstanceID = 0;
+ res->m_BufferPtr = NULL;
+ }
+ }
+
+ CUSTOM private void GetDepthBuffer (out RenderBuffer res) {
+ RenderTexture* rt = self;
+ if (rt)
+ {
+ res->m_RenderTextureInstanceID = rt->GetInstanceID();
+ if (!rt->IsCreated())
+ rt->Create();
+ res->m_BufferPtr = rt->GetDepthSurfaceHandle().object;
+ }
+ else
+ {
+ res->m_RenderTextureInstanceID = 0;
+ res->m_BufferPtr = NULL;
+ }
+ }
+
+
+ CUSTOM void SetGlobalShaderProperty (string propertyName) { self->SetGlobalProperty (ScriptingStringToProperty (propertyName)); }
+
+ CUSTOM_PROP static RenderTexture active { return Scripting::ScriptingWrapperFor (RenderTexture::GetActive ()); } { RenderTexture::SetActive (value); RenderTexture::FindAndSetSRGBWrite (value); }
+
+ OBSOLETE warning Use SystemInfo.supportsRenderTextures instead.
+ CUSTOM_PROP static bool enabled { return RenderTexture::IsEnabled (); } { RenderTexture::SetEnabled (value); }
+
+ CUSTOM private static void Internal_GetTexelOffset (RenderTexture tex, out Vector2 output)
+ {
+ if( GetGfxDevice().UsesHalfTexelOffset() )
+ {
+ RenderTexture& renderTex = *tex;
+ output->x = renderTex.GetTexelSizeX() * 0.5f;
+ output->y = renderTex.GetTexelSizeY() * 0.5f;
+ }
+ else
+ {
+ output->x = 0.0f;
+ output->y = 0.0f;
+ }
+ }
+
+ CSRAW public Vector2 GetTexelOffset ()
+ {
+ Vector2 r;
+ Internal_GetTexelOffset(this, out r);
+ return r;
+ }
+
+ CUSTOM public static bool SupportsStencil(RenderTexture rt)
+ {
+ return RenderTextureSupportsStencil(rt);
+ }
+
+ OBSOLETE error RenderTexture.SetBorderColor was removed
+ CSRAW public void SetBorderColor (Color color) { }
+
+END
+
+
+
+CSRAW
+}
diff --git a/Runtime/Export/TrackedReference.cs b/Runtime/Export/TrackedReference.cs
new file mode 100644
index 0000000..dcc3f80
--- /dev/null
+++ b/Runtime/Export/TrackedReference.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using UnityEngineInternal;
+namespace UnityEngine
+{
+
+[StructLayout (LayoutKind.Sequential)]
+//This class should be internal. Next time we can break backwardscompatibility we should do it.
+public class TrackedReference
+{
+ [NotRenamed]
+ internal IntPtr m_Ptr;
+
+ protected TrackedReference () { }
+
+ public static bool operator == (TrackedReference x, TrackedReference y)
+ {
+ object xo = x;
+ object yo = y;
+
+ if (yo == null && xo == null) return true;
+ if (yo == null) return x.m_Ptr == IntPtr.Zero;
+ if (xo == null) return y.m_Ptr == IntPtr.Zero;
+ return x.m_Ptr == y.m_Ptr;
+ }
+ public static bool operator != (TrackedReference x, TrackedReference y) { return !(x == y); }
+
+ public override bool Equals(object o) { return (o as TrackedReference) == this; }
+ public override int GetHashCode() { return (int)m_Ptr; }
+
+ public static implicit operator bool (TrackedReference exists)
+ {
+ return exists != null;
+ }
+}
+
+}
diff --git a/Runtime/Export/UnityEngineApplication.txt b/Runtime/Export/UnityEngineApplication.txt
new file mode 100644
index 0000000..e79f7e6
--- /dev/null
+++ b/Runtime/Export/UnityEngineApplication.txt
@@ -0,0 +1,657 @@
+C++RAW
+
+#include "UnityPrefix.h"
+
+#include <ctime>
+
+#include "Configuration/UnityConfigure.h"
+#include "Configuration/UnityConfigureVersion.h"
+
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Input/InputManager.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/BaseClasses/Tags.h"
+#include "Runtime/Misc/DebugUtility.h"
+#include "Runtime/Misc/PlayerSettings.h"
+#include "Runtime/GameCode/CloneObject.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/Misc/PreloadManager.h"
+#include "Runtime/Animation/Animation.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Utilities/PlayerPrefs.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Dynamics/RigidBody.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Camera/Light.h"
+#include "Runtime/Filters/Misc/TextMesh.h"
+#include "Runtime/Dynamics/ConstantForce.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Misc/SaveAndLoadHelper.h"
+#include "Runtime/Network/NetworkView.h"
+#include "Runtime/Camera/RenderLayers/GUIText.h"
+#include "Runtime/Camera/RenderLayers/GUITexture.h"
+#include "Runtime/Dynamics/Collider.h"
+#include "Runtime/Dynamics/HingeJoint.h"
+#include "Runtime/Filters/Particles/ParticleEmitter.h"
+#include "Runtime/Misc/AssetBundleUtility.h"
+#include "Runtime/Misc/Player.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Misc/CaptureScreenshot.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Misc/Plugins.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/Network/NetworkManager.h"
+#include "Runtime/Input/GetInput.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Animation/AnimationManager.h"
+#include "Runtime/Animation/AnimationClip.h"
+#include "Runtime/BaseClasses/RefCounted.h"
+#include "Runtime/Misc/GOCreation.h"
+#include "Runtime/Utilities/URLUtility.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/Misc/GraphicsDevicesDB.h"
+#include "Runtime/File/ApplicationSpecificPersistentDataPath.h"
+#include "Runtime/Mono/Coroutine.h"
+#include "Runtime/Utilities/UserAuthorizationManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+
+#if WEBPLUG
+# include "PlatformDependent/CommonWebPlugin/UnityWebStream.h"
+# include "PlatformDependent/CommonWebPlugin/WebScripting.h"
+#endif
+
+#if UNITY_EDITOR
+# include "Editor/Src/EditorSettings.h"
+# include "Editor/Src/EditorUserBuildSettings.h"
+# include "Editor/Mono/MonoEditorUtility.h"
+#endif
+
+#if UNITY_WII
+# include "PlatformDependent/Wii/WiiUtility.h"
+#endif
+
+#if UNITY_ANDROID
+# include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+#endif
+
+using namespace Unity;
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using UnityEngineInternal;
+
+namespace UnityEngine
+{
+
+// Describes network reachability options.
+ENUM NetworkReachability
+ // Network is not reachable
+ NotReachable = 0,
+ // Network is reachable via carrier data network
+ ReachableViaCarrierDataNetwork = 1,
+ // Network is reachable via WiFi or cable
+ ReachableViaLocalAreaNetwork = 2
+END
+
+
+// Access to application run-time data.
+
+CLASS Application
+ C++RAW
+
+
+ // Quits the player application. Quit is ignored in the editor or the web player.
+ CUSTOM static void Quit ()
+ {
+ GetInputManager ().QuitApplication ();
+ }
+
+ // Cancels quitting the application. This is useful for showing a splash screen at the end of a game.
+ CUSTOM static void CancelQuit ()
+ {
+ GetInputManager ().CancelQuitApplication ();
+ }
+
+ // The level index that was last loaded (RO).
+ CUSTOM_PROP static int loadedLevel { return PlayerGetLoadedLevel (); }
+
+ // The name of the level that was last loaded (RO).
+ CUSTOM_PROP static string loadedLevelName { return scripting_string_new(PlayerGetLoadedLevelName()); }
+
+
+ ///*listonly*
+ CSRAW static public void LoadLevel (int index) { LoadLevelAsync(null, index, false, true); }
+
+ // Loads the level by its name or index.
+ CSRAW static public void LoadLevel (string name) { LoadLevelAsync(name, -1, false, true); }
+
+
+ ///*listonly*
+ CSRAW static public AsyncOperation LoadLevelAsync (int index) { return LoadLevelAsync(null, index, false, false); }
+
+ // Loads the level asynchronously in the background.
+ CSRAW static public AsyncOperation LoadLevelAsync (string levelName)
+ {
+ return LoadLevelAsync(levelName, -1, false, false);
+ }
+
+
+ ///*listonly*
+ CSRAW static public AsyncOperation LoadLevelAdditiveAsync (int index) { return LoadLevelAsync(null, index, true, false); }
+
+ // Loads the level additively and asynchronously in the background.
+ CSRAW static public AsyncOperation LoadLevelAdditiveAsync (string levelName)
+ {
+ return LoadLevelAsync(levelName, -1, true, false);
+ }
+
+ CUSTOM static private AsyncOperation LoadLevelAsync (string monoLevelName, int index, bool additive, bool mustCompleteNextFrame)
+ {
+ string levelName = monoLevelName;
+ string levelPath;
+ string assetPath;
+
+ if (!GetLevelAndAssetPath (levelName, index, &levelPath, &assetPath, &index))
+ return SCRIPTING_NULL;
+
+ PreloadLevelOperation::LoadingMode mode = additive ? PreloadLevelOperation::kLoadAdditiveLevel : PreloadLevelOperation::kLoadLevel;
+ AsyncOperation* result = PreloadLevelOperation::LoadLevel(levelPath, assetPath, index, mode, mustCompleteNextFrame);
+ #if UNITY_FLASH
+
+ result->Release();
+
+ // ToDo: if we're returning AsyncOperation, it throws:
+ // ReferenceError: Error #1069: Property http://unity3d.com/cil2as::DefaultValue not found on class UnityEngine.AsyncOperation and there is no default value.
+ // at UnityEngine.Marshalling::Marshaller$/GetUninitializedObject()
+ // at com.unity::UnityNative$/Ext_Scripting_InstantiateObject()
+ // ...
+ return SCRIPTING_NULL;
+ #else
+ ScriptingObjectPtr o = scripting_object_new(MONO_COMMON.asyncOperation);
+ ScriptingObjectWithIntPtrField<AsyncOperation>(o).SetPtr(result);
+ return o;
+ #endif
+ }
+
+ ///*listonly*
+ CSRAW static public void LoadLevelAdditive (int index) { LoadLevelAsync (null, index, true, true); }
+ // Loads a level additively.
+ CSRAW static public void LoadLevelAdditive (string name) { LoadLevelAsync (name, -1, true, true); }
+
+ // Is some level being loaded? (RO)
+ CUSTOM_PROP static bool isLoadingLevel { return IsLoadingLevel(); }
+
+ // The total number of levels available (RO).
+ CUSTOM_PROP static int levelCount { return PlayerGetLevelCount (); }
+
+ CUSTOM private static float GetStreamProgressForLevelByName (string levelName) {
+ #if WEBPLUG
+ return GetUnityWebStream().GetProgressForLevel(GetBuildSettings().GetLevelIndexChecked (levelName));
+ #else
+ return 1.0F;
+ #endif
+ }
+
+ // How far has the download progressed? [0...1]
+ CUSTOM public static float GetStreamProgressForLevel (int levelIndex) {
+ #if WEBPLUG
+ return GetUnityWebStream().GetProgressForLevel(levelIndex);
+ #else
+ if (levelIndex >= 0 && levelIndex < GetBuildSettings().levels.size())
+ return 1.0F;
+ else
+ return 0.0F;
+ #endif
+ }
+
+ // How far has the download progressed? [0...1]
+ CSRAW public static float GetStreamProgressForLevel (string levelName) { return GetStreamProgressForLevelByName(levelName); }
+
+ // How many bytes have we downloaded from the main unity web stream (RO).
+ CUSTOM_PROP static int streamedBytes
+ {
+ #if WEBPLUG
+ return GetUnityWebStream().GetDownloadedBytes();
+ #else
+ return 0;
+ #endif
+ }
+
+ CUSTOM private static bool CanStreamedLevelBeLoadedByName (string levelName) {
+ string cppName = levelName;
+ #if WEBPLUG
+ return GetUnityWebStream().CanLevelBeLoaded(GetBuildSettings().GetLevelIndex (cppName)) || GetHasLateBoundLevelFromAssetBundle(cppName);
+ #else
+ return GetBuildSettings().GetLevelIndex (cppName) != -1 || GetHasLateBoundLevelFromAssetBundle(cppName);
+ #endif
+ }
+
+ // Can the streamed level be loaded?
+ CUSTOM public static bool CanStreamedLevelBeLoaded (int levelIndex) {
+ #if WEBPLUG
+ return GetUnityWebStream().CanLevelBeLoaded(levelIndex);
+ #else
+ return levelIndex >= 0 && levelIndex < GetBuildSettings().levels.size();
+ #endif
+ }
+
+ // Can the streamed level be loaded?
+ CSRAW public static bool CanStreamedLevelBeLoaded (string levelName) { return CanStreamedLevelBeLoadedByName(levelName); }
+
+ // Returns true when in any kind of player (RO).
+ CUSTOM_PROP static bool isPlaying { return IsWorldPlaying (); }
+
+ // Are we running inside the Unity editor? (RO)
+ CUSTOM_PROP static bool isEditor
+ {
+ #if UNITY_EDITOR
+ return true;
+ #else
+ return false;
+ #endif
+ }
+
+ // Are we running inside a web player? (RO)
+ CUSTOM_PROP static bool isWebPlayer
+ {
+ int platform = systeminfo::GetRuntimePlatform();
+ return platform == WindowsWebPlayer || platform == OSXWebPlayer;
+ }
+
+ // Returns the platform the game is running (RO).
+ THREAD_SAFE
+ CUSTOM_PROP static RuntimePlatform platform { return systeminfo::GetRuntimePlatform(); }
+
+
+ // Captures a screenshot at path /filename/ as a PNG file.
+ CUSTOM static void CaptureScreenshot (string filename, int superSize = 0)
+ {
+ #if CAPTURE_SCREENSHOT_AVAILABLE
+ QueueScreenshot (filename, superSize);
+ #endif
+ }
+
+ // Should the player be running when the application is in the background?
+ CUSTOM_PROP static bool runInBackground { return GetPlayerRunInBackground(); } { SetPlayerRunInBackground(value); }
+
+ OBSOLETE warning use Application.isEditor instead
+ CSRAW public static bool isPlayer { get { return !isEditor; } }
+
+ /// Is Unity activated with the Pro License?
+ CUSTOM static bool HasProLicense()
+ {
+ return GetBuildSettings().hasPROVersion;
+ }
+
+ CUSTOM static internal bool HasAdvancedLicense()
+ {
+ return GetBuildSettings().hasAdvancedVersion;
+ }
+
+ OBSOLETE warning Use Object.DontDestroyOnLoad instead
+ CUSTOM static void DontDestroyOnLoad (Object mono)
+ {
+ Object* o = mono;
+ if (o)
+ DontDestroyOnLoad (*o);
+ }
+
+ // Contains the path to the game data folder (RO).
+ CUSTOM_PROP static string dataPath { return scripting_string_new( GetAppDataPath() ); }
+
+ // Contains the path to the StreamingAssets folder (RO).
+ CUSTOM_PROP static string streamingAssetsPath { return scripting_string_new( GetStreamingAssetsPath() ); }
+
+ // Contains the path to a persistent data directory (RO).
+ CSRAW #if !UNITY_WP8 && !UNITY_METRO
+ CSRAW [System.Security.SecurityCritical]
+ CSRAW #endif
+ CUSTOM_PROP static string persistentDataPath { return scripting_string_new( GetPersistentDataPathApplicationSpecific() ); }
+
+ // Contains the path to a temporary data / cache directory (RO).
+ CUSTOM_PROP static string temporaryCachePath { return scripting_string_new( GetTemporaryCachePathApplicationSpecific() ); }
+
+ // The path to the web player data file relative to the html file (RO).
+ CUSTOM_PROP static string srcValue { return scripting_string_new(GetPlayerSettings().srcValue); }
+
+ // The absolute path to the web player data file (RO).
+ CUSTOM_PROP static string absoluteURL { return scripting_string_new(GetPlayerSettings().absoluteURL); }
+
+ OBSOLETE warning Please use absoluteURL instead
+ CSRAW public static string absoluteUrl { get { return absoluteURL; } }
+
+ // Converts an object to a JavaScript text representation.
+ CONDITIONAL ENABLE_MONO
+ CSRAW private static string ObjectToJSString( object o )
+ {
+ if( o == null ) {
+ return "null";
+ } else if( o is string ) {
+ string s = o.ToString().Replace( "\\", "\\\\" ); // escape \ into \\, JS vulnerability.
+ s = s.Replace( "\"", "\\\"" );
+ s = s.Replace( "\n", "\\n" );
+ s = s.Replace( "\r", "\\r" );
+ s = s.Replace( "\u0000", ""); // String-terminator. JS vulnerability.
+ s = s.Replace( "\u2028", ""); // Line-terminator via ecma-262-7.3 JS vulnerability.
+ s = s.Replace( "\u2029", ""); // Same as above
+ return '"' + s + '"';
+ } else if( o is Int32 || o is Int16 || o is UInt32 || o is UInt16 || o is Byte ) {
+ return o.ToString();
+ } else if( o is Single ) {
+ System.Globalization.NumberFormatInfo nf = System.Globalization.CultureInfo.InvariantCulture.NumberFormat;
+ return ((Single)o).ToString( nf );
+ } else if( o is Double ) {
+ System.Globalization.NumberFormatInfo nf = System.Globalization.CultureInfo.InvariantCulture.NumberFormat;
+ return ((Double)o).ToString( nf );
+ } else if( o is Char ) {
+ if( (Char)o == '"' )
+ return "\"\\\"\""; // escape the '"' character
+ else
+ return '"' + o.ToString() + '"';
+ } else if( o is System.Collections.IList ) {
+ // Any IList object is dumped as JS Array
+ System.Collections.IList list = (System.Collections.IList)o;
+
+ System.Text.StringBuilder sb = new System.Text.StringBuilder();
+ sb.Append( "new Array(" );
+ int size = list.Count;
+ for( int i = 0; i < size; ++i ) {
+ if( i != 0 )
+ sb.Append( ", " );
+ sb.Append( ObjectToJSString(list[i]) );
+ }
+ sb.Append( ")" );
+ return sb.ToString();
+ } else {
+ // Unrecognized objects are dumped as strings
+ return ObjectToJSString(o.ToString());
+ }
+ }
+
+ // Calls a function in the containing web page __(Web Player only)__.
+ CSRAW public static void ExternalCall (string functionName, params object[] args)
+ {
+ #if ENABLE_MONO
+ Internal_ExternalCall(BuildInvocationForArguments(functionName,args));
+ #elif UNITY_FLASH
+ Internal_ExternalCall_Flash(functionName,(object)args);
+ #endif
+ }
+
+ CONDITIONAL UNITY_FLASH
+ CUSTOM private static void Internal_ExternalCall_Flash(string script, object args)
+ {
+ ScriptingObjectPtr arg = args;
+ if(args){
+ Ext_ExternalCall(script.AsUTF8().c_str(), arg);
+ }
+ }
+
+ CONDITIONAL ENABLE_MONO
+ CSRAW private static string BuildInvocationForArguments(string functionName, params object[] args)
+ {
+ var sb = new System.Text.StringBuilder();
+ sb.Append( functionName );
+ sb.Append( '(' );
+ int size = args.Length;
+ for( int i = 0; i < size; ++i ) {
+ if( i != 0 )
+ sb.Append( ", " );
+ sb.Append( ObjectToJSString(args[i]) );
+ }
+ sb.Append( ')' );
+ sb.Append( ';' );
+ return sb.ToString();
+ }
+
+
+ // Evaluates script snippet in the containing web page __(Web Player only)__.
+ CONDITIONAL ENABLE_MONO
+ CSRAW public static void ExternalEval (string script)
+ {
+ if (script.Length > 0 && script[script.Length-1] != ';')
+ script += ';';
+ Internal_ExternalCall(script);
+ }
+
+ CONDITIONAL ENABLE_MONO
+ CUSTOM private static void Internal_ExternalCall(string script) {
+ #if WEBPLUG
+ WebScripting::Get().ExternalCall(script);
+ #else
+ LogString(Format ("External Call: %s", script.AsUTF8().c_str()));
+ #endif
+ }
+
+ // The version of the Unity runtime used to play the content.
+ CUSTOM_PROP static string unityVersion
+ {
+ return scripting_string_new(UNITY_VERSION);
+ }
+
+ CUSTOM internal static int GetBuildUnityVersion ()
+ {
+ return GetBuildSettings().GetIntVersion();
+ }
+
+ CUSTOM internal static int GetNumericUnityVersion (string version)
+ {
+ return GetNumericVersion (version);
+ }
+
+
+ // Indicates whether Unity's webplayer security model is enabled.
+ THREAD_SAFE
+ CUSTOM_PROP static bool webSecurityEnabled
+ {
+ #if WEBPLUG
+ return true;
+ #endif
+ #if UNITY_EDITOR
+ return (GetBuildTargetGroup( GetEditorUserBuildSettings().GetActiveBuildTarget ()) == kPlatformWebPlayer);
+ #endif
+ return false;
+ }
+
+ //*undocumented
+ THREAD_SAFE
+ CUSTOM_PROP static string webSecurityHostUrl
+ {
+ #if UNITY_EDITOR
+ return scripting_string_new(GetEditorSettings().GetWebSecurityEmulationHostUrl());
+ #elif WEBPLUG
+ return scripting_string_new(GetPlayerSettings().absoluteURL);
+ #else
+ return scripting_string_new("");
+ #endif
+ }
+
+ // Opens the /url/ in a browser.
+
+
+ CUSTOM static void OpenURL (string url)
+ {
+ #if WEBPLUG
+ #if UNITY_OSX && !UNITY_PEPPER
+ if (systeminfo::IsRunningInDashboardWidget ())
+ WebScripting::Get().ExternalCall("goToUrl(\"" + url.AsUTF8() + "\");");
+ else
+ #endif
+ WebScripting::Get().ExternalCall("location.href=\"" + url.AsUTF8() + "\";");
+ #elif UNITY_WII
+ wii::FatalError( "OpenURL not supported" );
+ #elif UNITY_PS3
+ printf("ERROR: OpenURL not supported\n"); exit(-1);
+ #elif UNITY_XENON
+ printf_console("ERROR: OpenURL not supported\n"); exit(-1);
+ #elif UNITY_FLASH
+ Ext_OpenURL(url.AsUTF8().c_str());
+ #else
+ OpenURL (url);
+ #endif
+ }
+
+ OBSOLETE warning For internal use only
+ CUSTOM public static void CommitSuicide (int mode)
+ {
+ if (mode == 0)
+ {
+ printf_console("Committing suicide -- Intentionally Dereferencing NULL pointer\n");
+ int* p = NULL;
+ *p = 5;
+ }
+ else if (mode == 1)
+ {
+ FatalErrorString("Intentionally caused fatal error");
+ }
+ else if (mode == 2)
+ {
+ abort();
+ }
+ }
+
+ // Instructs game to try to render at a specified frame rate.
+ CUSTOM_PROP static int targetFrameRate
+ {
+ return GetTargetFrameRateFromScripting();
+ }
+ {
+ SetTargetFrameRate(value);
+ }
+
+ // The language the user's operating system is running in.
+ CUSTOM_PROP static SystemLanguage systemLanguage
+ {
+ return (SystemLanguage)systeminfo::GetSystemLanguage();
+ }
+
+ // Register a delegate to be called on log messages.
+ CSRAW public static void RegisterLogCallback (Application.LogCallback handler)
+ {
+ s_LogCallback = handler; SetLogCallbackDefined(handler != null, false);
+ }
+
+ // Register a delegate to be called on log messages.
+ CSRAW public static void RegisterLogCallbackThreaded (Application.LogCallback handler) {
+ s_LogCallback = handler;
+ SetLogCallbackDefined(handler != null, true);
+ }
+
+ CSRAW private static volatile LogCallback s_LogCallback;
+
+ CSRAW private static void CallLogCallback(string logString, string stackTrace, LogType type)
+ {
+ if ( s_LogCallback != null )
+ s_LogCallback(logString, stackTrace, type);
+ }
+
+ // Use this delegate type with RegisterLogCallback to monitor what gets logged.
+ CSRAW public delegate void LogCallback (string condition, string stackTrace, LogType type);
+
+ // Log callback that calls into the mono log callback
+ C++RAW
+ static void LogCallbackImplementation(const std::string& condition, const std::string &stackTrace, int type)
+ {
+#if ENABLE_MONO || UNITY_WINRT
+ if ( !GetMonoManagerPtr() )
+ return;
+
+ ScriptingInvocation invocation(MONO_COMMON.callLogCallback);
+ invocation.AddString(condition.c_str());
+ invocation.AddString(stackTrace.c_str());
+ invocation.AddInt(type);
+ invocation.Invoke<ScriptingObjectPtr>();
+#endif
+ }
+ CUSTOM private static void SetLogCallbackDefined(bool defined, bool threaded)
+ {
+ RegisterLogCallback(defined ? LogCallbackImplementation : NULL, threaded);
+ }
+
+ // Priority of background loading thread.
+ CUSTOM_PROP static ThreadPriority backgroundLoadingPriority
+ {
+ return GetPreloadManager().GetThreadPriority();
+ }
+ {
+ GetPreloadManager().SetThreadPriority(value);
+ }
+
+ // Returns the type of Internet reachability currently possible on the device.
+ CUSTOM_PROP static NetworkReachability internetReachability
+ {
+ return GetInternetReachability ();
+ }
+
+
+ // Returns false if application is altered in any way after it was built.
+ CUSTOM_PROP static bool genuine
+ {
+ return IsApplicationGenuine ();
+ }
+
+ // Returns true if application integrity can be confirmed.
+ CUSTOM_PROP static bool genuineCheckAvailable
+ {
+ return IsApplicationGenuineAvailable ();
+ }
+
+ // Request authorization to use the webcam or microphone in the Web Player.
+ CUSTOM static AsyncOperation RequestUserAuthorization (UserAuthorization mode)
+ {
+ #if ENABLE_MONO
+ AsyncOperation* result = GetUserAuthorizationManager().RequestUserAuthorization (mode);
+ MonoObject* mono = mono_object_new(mono_domain_get(), MONO_COMMON.asyncOperation);
+ ExtractMonoObjectData<AsyncOperation*>(mono) = result;
+ return mono;
+ #else
+ return SCRIPTING_NULL;
+ #endif
+ }
+
+ // Check if the user has authorized use of the webcam or microphone in the Web Player.
+ CUSTOM static bool HasUserAuthorization (UserAuthorization mode)
+ {
+ return GetUserAuthorizationManager().HasUserAuthorization (mode);
+ }
+
+ CUSTOM static internal void ReplyToUserAuthorizationRequest (bool reply, bool remember = false)
+ {
+ return GetUserAuthorizationManager().ReplyToUserAuthorizationRequest (reply, remember);
+ }
+
+ CUSTOM static private int GetUserAuthorizationRequestMode_Internal ()
+ {
+ return GetUserAuthorizationManager().GetAuthorizationRequest();
+ }
+
+ CSRAW static internal UserAuthorization GetUserAuthorizationRequestMode ()
+ {
+ return (UserAuthorization)GetUserAuthorizationRequestMode_Internal();
+ }
+END
+
+// Constants to pass to [[Application.RequestUserAuthorization]].
+ENUM UserAuthorization
+ // Request permission to use any video input sources attached to the computer.
+ WebCam = 1,
+ // Request permission to use any audio input sources attached to the computer.
+ Microphone = 2
+END
+
+CSRAW }
diff --git a/Runtime/Export/UnityEngineAsyncOperation.txt b/Runtime/Export/UnityEngineAsyncOperation.txt
new file mode 100644
index 0000000..1cde86e
--- /dev/null
+++ b/Runtime/Export/UnityEngineAsyncOperation.txt
@@ -0,0 +1,81 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+
+#include "Configuration/UnityConfigure.h"
+
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Misc/AsyncOperation.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+
+using namespace Unity;
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using UnityEngineInternal;
+
+namespace UnityEngine
+{
+
+
+// Asynchronous operation coroutine.
+CSRAW [StructLayout (LayoutKind.Sequential)]
+NONSEALED_CLASS AsyncOperation : YieldInstruction
+
+ CSRAW [NotRenamed]internal IntPtr m_Ptr;
+
+ THREAD_SAFE
+ CUSTOM private void InternalDestroy ()
+ {
+ self->Release();
+ }
+
+ CSRAW ~AsyncOperation ()
+ {
+ InternalDestroy();
+ }
+
+ // Has the operation finished? (RO)
+ CUSTOM_PROP bool isDone
+ {
+ return self->IsDone();
+ }
+
+
+ // What's the operation's progress (RO)
+ CUSTOM_PROP float progress
+ {
+ return self->GetProgress();
+ }
+
+ // Priority lets you tweak in which order async operation calls will be performed.
+ CUSTOM_PROP int priority
+ {
+ return self->GetPriority();
+ }
+ {
+ if (value < 0)
+ {
+ value = 0;
+ ErrorString ("Priority can't be set to negative value");
+ }
+ return self->SetPriority(value);
+ }
+
+ // Allow scenes to be activated as soon as it is ready.
+ CUSTOM_PROP bool allowSceneActivation
+ {
+ return self->GetAllowSceneActivation();
+ }
+ {
+ return self->SetAllowSceneActivation(value);
+ }
+
+END
+
+CSRAW }
diff --git a/Runtime/Export/UnityEngineBehaviour.txt b/Runtime/Export/UnityEngineBehaviour.txt
new file mode 100644
index 0000000..d0e1304
--- /dev/null
+++ b/Runtime/Export/UnityEngineBehaviour.txt
@@ -0,0 +1,31 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+
+using namespace Unity;
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using UnityEngineInternal;
+
+namespace UnityEngine
+{
+
+// Behaviours are Components that can be enabled or disabled.
+CSRAW
+NONSEALED_CLASS Behaviour : Component
+ // Enabled Behaviours are Updated, disabled Behaviours are not.
+ AUTO_PROP bool enabled GetEnabled SetEnabled
+END
+
+
+CSRAW }
+
diff --git a/Runtime/Export/UnityEngineCamera.txt b/Runtime/Export/UnityEngineCamera.txt
new file mode 100644
index 0000000..92a7930
--- /dev/null
+++ b/Runtime/Export/UnityEngineCamera.txt
@@ -0,0 +1,420 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Graphics/CubemapTexture.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Scripting/Scripting.h"
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+// Rendering path of a [[Camera]].
+ENUM RenderingPath
+ // Use Player Settings.
+ UsePlayerSettings = -1,
+
+ // Vertex Lit.
+ VertexLit = 0,
+
+ // Forward Rendering.
+ Forward = 1,
+
+ // Deferred Lighting.
+ DeferredLighting = 2
+END
+
+
+// Transparent object sorting mode of a [[Camera]].
+ENUM TransparencySortMode
+ // Default sorting mode.
+ Default = 0,
+
+ // Perspective sorting mode.
+ Perspective = 1,
+
+ // Orthographic sorting mode.
+ Orthographic = 2
+END
+
+
+// A Camera is a device through which the player views the world.
+CLASS Camera : Behaviour
+
+ // *undocumented* deprecated
+ OBSOLETE warning use Camera.fieldOfView instead.
+ CSRAW public float fov { get { return fieldOfView; } set { fieldOfView = value; } }
+ // *undocumented* deprecated
+ OBSOLETE warning use Camera.nearClipPlane instead.
+ CSRAW public float near { get { return nearClipPlane; } set { nearClipPlane = value; } }
+ // *undocumented* deprecated
+ OBSOLETE warning use Camera.farClipPlane instead.
+ CSRAW public float far { get { return farClipPlane; } set { farClipPlane = value; } }
+
+ // The field of view of the camera in degrees.
+ AUTO_PROP float fieldOfView GetFov SetFov
+
+ // The near clipping plane distance.
+ AUTO_PROP float nearClipPlane GetNear SetNear
+
+ // The far clipping plane distance.
+ AUTO_PROP float farClipPlane GetFar SetFar
+
+
+ // Rendering path.
+ AUTO_PROP RenderingPath renderingPath GetRenderingPath SetRenderingPath
+
+ // Actually used rendering path (RO).
+ CUSTOM_PROP RenderingPath actualRenderingPath { return self->CalculateRenderingPath(); }
+
+ // High dynamic range rendering
+ AUTO_PROP bool hdr CalculateUsingHDR SetHDR
+
+ // Camera's half-size when in orthographic mode.
+ AUTO_PROP float orthographicSize GetOrthographicSize SetOrthographicSize
+
+ // Is the camera orthographic (''true'') or perspective (''false'')?
+ AUTO_PROP bool orthographic GetOrthographic SetOrthographic
+
+
+ // Transparent object sorting mode.
+ AUTO_PROP TransparencySortMode transparencySortMode GetSortMode SetSortMode
+
+
+ OBSOLETE planned Use orthographic instead
+ CSRAW public bool isOrthoGraphic { get { return orthographic; } set { orthographic = value; } }
+
+ // Camera's depth in the camera rendering order.
+ AUTO_PROP float depth GetDepth SetDepth
+
+ // The aspect ratio (width divided by height).
+ AUTO_PROP float aspect GetAspect SetAspect
+
+ // This is used to render parts of the scene selectively.
+ AUTO_PROP int cullingMask GetCullingMask SetCullingMask
+
+ // The event mask used by the camera.
+ AUTO_PROP int eventMask GetEventMask SetEventMask
+
+ // The color with which the screen will be cleared.
+ AUTO_PROP Color backgroundColor GetBackgroundColor SetBackgroundColor
+
+
+ // Where on the screen is the camera rendered in normalized coordinates.
+ AUTO_PROP Rect rect GetNormalizedViewportRect SetNormalizedViewportRect
+
+ // Where on the screen is the camera rendered in pixel coordinates.
+ AUTO_PROP Rect pixelRect GetScreenViewportRect SetScreenViewportRect
+
+ // Destination render texture __(Unity Pro only)__.
+ AUTO_PTR_PROP RenderTexture targetTexture GetTargetTexture SetTargetTexture
+
+ CUSTOM private void SetTargetBuffersImpl(out RenderBuffer color, out RenderBuffer depth)
+ {
+ self->SetTargetBuffersScript(1, color, depth);
+ }
+
+ CUSTOM private void SetTargetBuffersMRTImpl(RenderBuffer[] color, out RenderBuffer depth)
+ {
+ int count = GetScriptingArraySize(color);
+ if (count < 1 || count > kMaxSupportedRenderTargets)
+ {
+ ErrorString ("Invalid color buffer count for SetTargetBuffers");
+ return;
+ }
+
+ self->SetTargetBuffersScript(count, Scripting::GetScriptingArrayStart<ScriptingRenderBuffer>(color), depth);
+ }
+
+ CSRAW public void SetTargetBuffers(RenderBuffer colorBuffer, RenderBuffer depthBuffer)
+ {
+ SetTargetBuffersImpl(out colorBuffer, out depthBuffer);
+ }
+
+ CSRAW public void SetTargetBuffers(RenderBuffer[] colorBuffer, RenderBuffer depthBuffer)
+ {
+ SetTargetBuffersMRTImpl(colorBuffer, out depthBuffer);
+ }
+
+
+ // How wide is the camera in pixels (RO).
+ CUSTOM_PROP float pixelWidth { return self->GetScreenViewportRect ().Width(); }
+ // How tall is the camera in pixels (RO).
+ CUSTOM_PROP float pixelHeight { return self->GetScreenViewportRect ().Height(); }
+
+ // Matrix that transforms from camera space to world space (RO).
+ AUTO_PROP Matrix4x4 cameraToWorldMatrix GetCameraToWorldMatrix
+
+ // Matrix that transforms from world to camera space.
+ AUTO_PROP Matrix4x4 worldToCameraMatrix GetWorldToCameraMatrix SetWorldToCameraMatrix
+
+ // Make the rendering position reflect the camera's position in the scene.
+ AUTO void ResetWorldToCameraMatrix ();
+
+
+ // Set a custom projection matrix.
+ AUTO_PROP Matrix4x4 projectionMatrix GetProjectionMatrix SetProjectionMatrix
+
+ // Make the projection reflect normal camera's parameters.
+ AUTO void ResetProjectionMatrix ();
+
+ // Revert the aspect ratio to the screen's aspect ratio.
+ AUTO void ResetAspect ();
+
+ // Get the world-space speed of the camera (RO).
+ AUTO_PROP Vector3 velocity GetVelocity
+
+ // How the camera clears the background.
+ AUTO_PROP CameraClearFlags clearFlags GetClearFlags SetClearFlags
+
+ // Transforms /position/ from world space into screen space.
+ AUTO Vector3 WorldToScreenPoint (Vector3 position);
+
+ // Transforms /position/ from world space into viewport space.
+ AUTO Vector3 WorldToViewportPoint (Vector3 position);
+
+
+ // Transforms /position/ from viewport space into world space.
+ AUTO Vector3 ViewportToWorldPoint (Vector3 position);
+
+ // Transforms /position/ from screen space into world space.
+ AUTO Vector3 ScreenToWorldPoint (Vector3 position);
+
+
+ // Transforms /position/ from screen space into viewport space.
+ AUTO Vector3 ScreenToViewportPoint (Vector3 position);
+
+ // Transforms /position/ from viewport space into screen space.
+ AUTO Vector3 ViewportToScreenPoint (Vector3 position);
+
+
+ // Returns a ray going from camera through a viewport point.
+ CUSTOM Ray ViewportPointToRay (Vector3 position) { return self->ViewportPointToRay (Vector2f (position.x, position.y)); }
+
+ // Returns a ray going from camera through a screen point.
+ CUSTOM Ray ScreenPointToRay (Vector3 position) { return self->ScreenPointToRay (Vector2f (position.x, position.y)); }
+
+
+ // The first enabled camera tagged "MainCamera" (RO).
+ CUSTOM_PROP static Camera main
+ {
+ return Scripting::ScriptingWrapperFor(FindMainCamera());
+ }
+
+ // The camera we are currently rendering with, for low-level render control only (Read Only).
+ CUSTOM_PROP static Camera current { return Scripting::ScriptingWrapperFor (GetCurrentCameraPtr()); }
+
+ // Returns all enabled cameras in the scene.
+ CUSTOM_PROP static Camera[] allCameras
+ {
+ const RenderManager::CameraContainer& onscreen = GetRenderManager ().GetOnscreenCameras ();
+ const RenderManager::CameraContainer& offscreen = GetRenderManager ().GetOffscreenCameras ();
+
+ unsigned camCount = onscreen.size () + offscreen.size();
+
+ int curCameraI = 0;
+
+ ScriptingArrayPtr scriptingcams = CreateScriptingArray<ScriptingObjectPtr>(GetScriptingManager ().GetCommonClasses ().camera, camCount);
+
+ for ( RenderManager::CameraContainer::const_iterator camIter = onscreen.begin () ;
+ camIter != onscreen.end() ;
+ ++camIter, ++curCameraI
+ )
+ {
+ Scripting::SetScriptingArrayElement (scriptingcams, curCameraI, Scripting::ScriptingWrapperFor (*camIter));
+ }
+
+ for ( RenderManager::CameraContainer::const_iterator camIter = offscreen.begin () ;
+ camIter != offscreen.end() ;
+ ++camIter, ++curCameraI
+ )
+ {
+ Scripting::SetScriptingArrayElement (scriptingcams, curCameraI, Scripting::ScriptingWrapperFor (*camIter));
+ }
+
+ return scriptingcams;
+ }
+
+
+ // *undocumented* DEPRECATED
+ OBSOLETE warning use Camera.main instead.
+ CSRAW public static Camera mainCamera { get { return Camera.main; } }
+ //*undocumented* DEPRECATED
+ OBSOLETE warning use Screen.width instead.
+ CUSTOM float GetScreenWidth () { return GetScreenManager ().GetWidth (); }
+ //*undocumented* DEPRECATED
+ OBSOLETE warning use Screen.height instead.
+ CUSTOM float GetScreenHeight () { return GetScreenManager ().GetHeight (); }
+
+ //*undocumented* DEPRECATED
+ OBSOLETE warning Camera.DoClear is deprecated and may be removed in the future.
+ CUSTOM void DoClear () {
+ self->Clear ();
+ }
+
+
+ // OnPreCull is called before a camera culls the scene.
+ CSNONE void OnPreCull ();
+
+ // OnPreRender is called before a camera starts rendering the scene.
+ CSNONE void OnPreRender ();
+
+ // OnPostRender is called after a camera has finished rendering the scene.
+ CSNONE void OnPostRender ();
+
+ // OnRenderImage is called after all rendering is complete to render image
+ CSNONE void OnRenderImage (RenderTexture source, RenderTexture destination);
+
+
+ // OnRenderObject is called after camera has rendered the scene.
+ CSNONE void OnRenderObject ();
+
+
+ // OnWillRenderObject is called once for each camera if the object is visible.
+
+ CONVERTEXAMPLE
+ BEGIN EX
+ function OnWillRenderObject() {
+ // Tint the object red for identification if it is
+ // being shown on the overhead mini-map view.
+ if (Camera.current.name == "MiniMapcam") {
+ renderer.material.color = Color.red;
+ } else {
+ renderer.material.color = Color.white;
+ }
+ }
+ END EX
+ ///
+ CSNONE void OnWillRenderObject();
+
+ // Render the camera manually.
+ CUSTOM void Render () {
+ self->StandaloneRender( Camera::kRenderFlagSetRenderTarget, NULL, "" );
+ }
+
+ // Render the camera with shader replacement.
+ CUSTOM void RenderWithShader (Shader shader, string replacementTag) {
+ self->StandaloneRender( Camera::kRenderFlagSetRenderTarget, shader, replacementTag );
+ }
+
+ // Make the camera render with shader replacement.
+ CUSTOM void SetReplacementShader (Shader shader, string replacementTag) {
+ self->SetReplacementShader( shader, replacementTag );
+ }
+ // Remove shader replacement from camera.
+ AUTO void ResetReplacementShader ();
+
+ AUTO_PROP bool useOcclusionCulling GetUseOcclusionCulling SetUseOcclusionCulling
+
+ // These are only used by terrain engine impostor rendering and should be used with care!
+ //*undoc*
+ CUSTOM void RenderDontRestore() {
+ self->StandaloneRender( Camera::kRenderFlagDontRestoreRenderState | Camera::kRenderFlagSetRenderTarget, NULL, "" );
+ }
+ //*undoc*
+ CUSTOM static void SetupCurrent (Camera cur)
+ {
+ if (cur)
+ {
+ cur->StandaloneSetup();
+ }
+ else
+ {
+ GetRenderManager ().SetCurrentCamera (NULL);
+ RenderTexture::SetActive(NULL);
+ }
+ }
+
+
+ // Render into a static cubemap from this camera.
+ CSRAW public bool RenderToCubemap (Cubemap cubemap, int faceMask = 63) {
+ return Internal_RenderToCubemapTexture( cubemap, faceMask );
+ }
+
+ // Render into a cubemap from this camera.
+ CSRAW public bool RenderToCubemap (RenderTexture cubemap, int faceMask = 63) {
+ return Internal_RenderToCubemapRT ( cubemap, faceMask );
+ }
+
+
+ CUSTOM private bool Internal_RenderToCubemapRT( RenderTexture cubemap, int faceMask )
+ {
+ RenderTexture* rt = cubemap;
+ if( !rt )
+ {
+ ErrorString( "Cubemap must not be null" );
+ return false;
+ }
+ return self->StandaloneRenderToCubemap( rt, faceMask );
+ }
+ CUSTOM private bool Internal_RenderToCubemapTexture( Cubemap cubemap, int faceMask )
+ {
+ Cubemap* cube = cubemap;
+ if( !cube )
+ {
+ ErrorString( "Cubemap must not be null" );
+ return false;
+ }
+ return self->StandaloneRenderToCubemap( cube, faceMask );
+ }
+
+ // Per-layer culling distances.
+ CUSTOM_PROP float[] layerCullDistances
+ {
+ return CreateScriptingArray(self->GetLayerCullDistances(), 32, GetScriptingManager().GetCommonClasses().floatSingle);
+ }
+ {
+ Scripting::RaiseIfNull(value);
+ if(GetScriptingArraySize(value) != 32)
+ {
+ Scripting::RaiseMonoException(" Array needs to contain exactly 32 floats for layerCullDistances.");
+ return;
+ }
+ self->SetLayerCullDistances(Scripting::GetScriptingArrayStart<float> (value));
+ }
+
+ // How to perform per-layer culling for a Camera.
+ CUSTOM_PROP bool layerCullSpherical { return self->GetLayerCullSpherical(); } { self->SetLayerCullSpherical(value); }
+
+ // Makes this camera's settings match other camera.
+ CUSTOM void CopyFrom (Camera other) {
+ const Camera* otherCam = other;
+ if(!otherCam)
+ {
+ ErrorString( "Camera to copy from must not be null" );
+ return;
+ }
+
+ self->CopyFrom (*otherCam);
+ }
+
+ // How and if camera generates a depth texture.
+ CUSTOM_PROP DepthTextureMode depthTextureMode { return self->GetDepthTextureMode(); } { self->SetDepthTextureMode (value); }
+
+ // Should the camera clear the stencil buffer after the lighting stage of the deferred rendering path?
+ CUSTOM_PROP bool clearStencilAfterLightingPass { return self->GetClearStencilAfterLightingPass(); } { self->SetClearStencilAfterLightingPass (value); }
+
+ CUSTOM internal bool IsFiltered (GameObject go) {
+ #if UNITY_EDITOR
+ return true;
+//@TODO
+// return self->GetCuller().IsFiltered(*go);
+ #else
+ return true;
+ #endif
+ }
+END
+
+
+CSRAW }
diff --git a/Runtime/Export/UnityEngineComponent.txt b/Runtime/Export/UnityEngineComponent.txt
new file mode 100644
index 0000000..2fa9b9c
--- /dev/null
+++ b/Runtime/Export/UnityEngineComponent.txt
@@ -0,0 +1,329 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/BaseClasses/Tags.h"
+#if ENABLE_AUDIO
+#include "Runtime/Audio/AudioClip.h"
+#include "Runtime/Audio/AudioSource.h"
+#include "Runtime/Audio/AudioListener.h"
+#endif
+#include "Runtime/Animation/Animation.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Utilities/PlayerPrefs.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Camera/Light.h"
+#include "Runtime/Filters/Misc/TextMesh.h"
+#include "Runtime/Dynamics/ConstantForce.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Network/NetworkView.h"
+#include "Runtime/Camera/RenderLayers/GUIText.h"
+#include "Runtime/Camera/RenderLayers/GUITexture.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/GetComponent.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#if ENABLE_PHYSICS
+#include "Runtime/Dynamics/RigidBody.h"
+#include "Runtime/Dynamics/Collider.h"
+#include "Runtime/Dynamics/HingeJoint.h"
+#endif
+#if ENABLE_2D_PHYSICS
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Collider2D.h"
+#endif
+#include "Runtime/Filters/Particles/ParticleEmitter.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystem.h"
+#include "Runtime/Animation/AnimationClip.h"
+#include "Runtime/File/ApplicationSpecificPersistentDataPath.h"
+#if ENABLE_MONO
+#include "Runtime/Mono/Coroutine.h"
+#endif
+
+#if UNITY_WII
+ #include "PlatformDependent/Wii/WiiUtility.h"
+#endif
+
+#include "Runtime/Scripting/Scripting.h"
+
+using namespace Unity;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using UnityEngineInternal;
+
+namespace UnityEngine
+{
+
+// Base class for everything attached to GameObjects.
+NONSEALED_CLASS Component : Object
+
+ C++RAW
+ #define FAST_COMPONENT_QUERY_COMPONENT_HANDLE(x) DISALLOW_IN_CONSTRUCTOR GameObject* go = self->GetGameObjectPtr(); if (go) {Unity::Component* com = go->QueryComponent (x); Scripting::GetComponentObjectToScriptingObject (com, *go, ClassID (x)); return com->GetGCHandle(); } else { Scripting::RaiseMonoException ("The component is not attached to any game object!"); return 0; }
+
+ C++RAW
+ #define FAST_COMPONENT_QUERY_COMPONENT(x) DISALLOW_IN_CONSTRUCTOR GameObject* go = self->GetGameObjectPtr(); if (go) return Scripting::GetComponentObjectToScriptingObject (go->QueryComponent (x), *go, ClassID (x)); else { Scripting::RaiseMonoException ("The component is not attached to any game object!"); return SCRIPTING_NULL; }
+
+ // The [[Transform]] attached to this [[GameObject]] (null if there is none attached).
+ CSRAW
+ CSRAW public Transform transform
+ {
+ get
+ {
+ return InternalGetTransform();
+ }
+ }
+
+ CUSTOM internal Transform InternalGetTransform()
+ {
+ FAST_COMPONENT_QUERY_COMPONENT(Transform);
+ }
+
+ // The [[Rigidbody]] attached to this [[GameObject]] (null if there is none attached).
+ CONDITIONAL ENABLE_PHYSICS
+ CUSTOM_PROP Rigidbody rigidbody { FAST_COMPONENT_QUERY_COMPONENT(Rigidbody) }
+ CONDITIONAL ENABLE_2D_PHYSICS
+ CUSTOM_PROP Rigidbody2D rigidbody2D { FAST_COMPONENT_QUERY_COMPONENT(Rigidbody2D) }
+
+ // The [[Camera]] attached to this [[GameObject]] (null if there is none attached).
+ CUSTOM_PROP Camera camera { FAST_COMPONENT_QUERY_COMPONENT(Camera) }
+ // The [[Light]] attached to this [[GameObject]] (null if there is none attached).
+ CUSTOM_PROP Light light { FAST_COMPONENT_QUERY_COMPONENT(Light) }
+ // The [[Animation]] attached to this [[GameObject]] (null if there is none attached).
+ CUSTOM_PROP Animation animation { FAST_COMPONENT_QUERY_COMPONENT(Animation) }
+ // The [[ConstantForce]] attached to this [[GameObject]] (null if there is none attached).
+ CONDITIONAL ENABLE_PHYSICS
+ CUSTOM_PROP ConstantForce constantForce { FAST_COMPONENT_QUERY_COMPONENT(ConstantForce) }
+ // The [[Renderer]] attached to this [[GameObject]] (null if there is none attached).
+ CUSTOM_PROP Renderer renderer { FAST_COMPONENT_QUERY_COMPONENT(Renderer) }
+ // The [[AudioSource]] attached to this [[GameObject]] (null if there is none attached).
+ CONDITIONAL ENABLE_AUDIO
+ CUSTOM_PROP AudioSource audio { FAST_COMPONENT_QUERY_COMPONENT(AudioSource) }
+ // The [[GUIText]] attached to this [[GameObject]] (null if there is none attached).
+ CUSTOM_PROP GUIText guiText { FAST_COMPONENT_QUERY_COMPONENT(GUIText) }
+
+ CONDITIONAL ENABLE_NETWORK
+ // The [[NetworkView]] attached to this [[GameObject]] (RO). (null if there is none attached)
+ CUSTOM_PROP NetworkView networkView
+ {
+ #if ENABLE_NETWORK
+ FAST_COMPONENT_QUERY_COMPONENT(NetworkView)
+ #else
+ return SCRIPTING_NULL;
+ #endif
+ }
+
+ FLUSHCONDITIONS
+
+ CSRAW
+#if ENABLE_NETWORK
+ OBSOLETE warning Please use guiTexture instead
+#endif
+ CUSTOM_PROP GUIElement guiElement { FAST_COMPONENT_QUERY_COMPONENT(GUIElement) }
+
+ // The [[GUITexture]] attached to this [[GameObject]] (RO). (null if there is none attached)
+ CUSTOM_PROP GUITexture guiTexture { FAST_COMPONENT_QUERY_COMPONENT(GUITexture) }
+
+ // The [[Collider]] attached to this [[GameObject]] (null if there is none attached).
+ CONDITIONAL ENABLE_PHYSICS
+ CUSTOM_PROP Collider collider { FAST_COMPONENT_QUERY_COMPONENT(Collider) }
+ CONDITIONAL ENABLE_2D_PHYSICS
+ CUSTOM_PROP Collider2D collider2D { FAST_COMPONENT_QUERY_COMPONENT(Collider2D) }
+
+ // The [[HingeJoint]] attached to this [[GameObject]] (null if there is none attached).
+ CONDITIONAL ENABLE_PHYSICS
+ CUSTOM_PROP HingeJoint hingeJoint { FAST_COMPONENT_QUERY_COMPONENT(HingeJoint) }
+ // The [[ParticleEmitter]] attached to this [[GameObject]] (null if there is none attached).
+ CUSTOM_PROP ParticleEmitter particleEmitter { FAST_COMPONENT_QUERY_COMPONENT(ParticleEmitter) }
+ // The [[ParticleSystem]] attached to this [[GameObject]] (null if there is none attached).
+ CUSTOM_PROP ParticleSystem particleSystem { FAST_COMPONENT_QUERY_COMPONENT(ParticleSystem) }
+
+
+ // The game object this component is attached to. A component is always attached to a game object.
+
+ CSRAW public GameObject gameObject
+ {
+ get
+ {
+ return InternalGetGameObject();
+ }
+ }
+
+ CUSTOM internal GameObject InternalGetGameObject()
+ {
+ DISALLOW_IN_CONSTRUCTOR
+ return Scripting::ScriptingWrapperFor(self->GetGameObjectPtr());
+ }
+ // Returns the component of Type /type/ if the game object has one attached, null if it doesn't.
+
+ CSRAW
+ [TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
+ CUSTOM Component GetComponent (Type type)
+ {
+ DISALLOW_IN_CONSTRUCTOR
+
+ GameObject* go = self->GetGameObjectPtr();
+ if (go)
+ {
+ return ScriptingGetComponentOfType (*go, type);
+ }
+ else
+ {
+ Scripting::RaiseMonoException ("The component is not attached to any game object!");
+ return SCRIPTING_NULL;
+ }
+ }
+
+ C++RAW
+
+
+ #if UNITY_WINRT
+ GameObject& GetGameObjectThrow (void* com_)
+ {
+ ReadOnlyScriptingObjectOfType<Unity::Component> com(com_);
+ #else
+ GameObject& GetGameObjectThrow (ReadOnlyScriptingObjectOfType<Unity::Component> com)
+ {
+ #endif
+ GameObject* go = com->GetGameObjectPtr();
+ if (go)
+ return *go;
+ {
+ Scripting::RaiseMonoException ("The component is not attached to any game object!");
+ return *go;
+ }
+ }
+
+ CSRAW
+ #if ENABLE_GENERICS
+ // Generic version. See the [[wiki:Generic Functions|Generic Functions]] page for more details.
+ public T GetComponent<T>() where T : Component { return GetComponent(typeof(T)) as T; }
+ #endif
+
+ // Returns the component with name /type/ if the game object has one attached, null if it doesn't.
+
+ CSRAW public Component GetComponent (string type) { return gameObject.GetComponent(type); }
+
+
+
+
+ // Returns the component of Type /type/ in the [[GameObject]] or any of its children using depth first search.
+
+
+ CSRAW
+ [TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
+ CSRAW public Component GetComponentInChildren (Type t) { return gameObject.GetComponentInChildren (t); }
+
+ CSRAW
+ #if ENABLE_GENERICS
+ // Generic version. See the [[wiki:Generic Functions|Generic Functions]] page for more details.
+ public T GetComponentInChildren<T> () where T : Component { return (T) GetComponentInChildren(typeof(T)); }
+ #endif
+
+
+ // Returns all components of Type /type/ in the [[GameObject]] or any of its children.
+
+ CSRAW public Component[] GetComponentsInChildren (Type t, bool includeInactive = false) { return gameObject.GetComponentsInChildren (t, includeInactive); }
+
+ CSRAW
+ #if ENABLE_GENERICS
+ // Generic version. See the [[wiki:Generic Functions|Generic Functions]] page for more details.
+ public T[] GetComponentsInChildren<T> (bool includeInactive) where T : Component { return gameObject.GetComponentsInChildren<T>(includeInactive); }
+ #endif
+
+ CSRAW
+ #if ENABLE_GENERICS
+ // adding overload manually because lucas can't figure out how to get the cspreprocess overload generation code to deal with templates right now.
+ // Generic version. See the [[wiki:Generic Functions|Generic Functions]] page for more details.
+ public T[] GetComponentsInChildren<T> () where T : Component { return GetComponentsInChildren<T>(false); }
+ #endif
+
+
+
+ // Returns all components of Type /type/ in the [[GameObject]].
+
+ CUSTOM Component[] GetComponents (Type type) { DISALLOW_IN_CONSTRUCTOR return ScriptingGetComponentsOfType (GetGameObjectThrow(self), type, false, false, true); }
+
+ CUSTOM private Component[] GetComponentsWithCorrectReturnType(Type type) { DISALLOW_IN_CONSTRUCTOR return ScriptingGetComponentsOfType (GetGameObjectThrow(self), type, true, false, true); }
+
+ CSRAW
+ #if ENABLE_GENERICS
+ // Generic version. See the [[wiki:Generic Functions|Generic Functions]] page for more details.
+ public T[] GetComponents<T>() where T : Component
+ {
+ return (T[]) GetComponentsWithCorrectReturnType(typeof(T));
+ }
+ #endif
+
+
+ OBSOLETE warning the active property is deprecated on components. Please use gameObject.active instead. If you meant to enable / disable a single component use enabled instead.
+ CUSTOM_PROP bool active { DISALLOW_IN_CONSTRUCTOR return self->IsActive (); } { DISALLOW_IN_CONSTRUCTOR GameObject& go = GetGameObjectThrow(self); if (value) go.Activate (); else go.Deactivate (); }
+
+ // The tag of this game object.
+ CUSTOM_PROP string tag
+ {
+ DISALLOW_IN_CONSTRUCTOR
+ const string& tag = TagToString (GetGameObjectThrow(self).GetTag ());
+ if (!tag.empty ())
+ return scripting_string_new(tag);
+ else
+ {
+ Scripting::RaiseMonoException ("GameObject has undefined tag!");
+ return SCRIPTING_NULL;
+ }
+ }
+ {
+ DISALLOW_IN_CONSTRUCTOR
+ GetGameObjectThrow(self).SetTag (ExtractTagThrowing (value));
+ }
+
+ // Is this game object tagged /tag/?
+ CUSTOM public bool CompareTag (string tag) { DISALLOW_IN_CONSTRUCTOR return ExtractTagThrowing (tag) == GetGameObjectThrow(self).GetTag (); }
+
+
+ // Calls the method named /methodName/ on every [[MonoBehaviour]] in this game object and on every ancestor of the behaviour
+ CUSTOM void SendMessageUpwards (string methodName, object value = null, SendMessageOptions options = SendMessageOptions.RequireReceiver)
+ {
+ DISALLOW_IN_CONSTRUCTOR
+ Scripting::SendScriptingMessageUpwards(GetGameObjectThrow(self), methodName, value, options);
+ }
+
+ //*undocumented* Function is for convenience and avoid coming mistakes.
+ CSRAW public void SendMessageUpwards (string methodName, SendMessageOptions options)
+ {
+ SendMessageUpwards(methodName, null, options);
+ }
+
+ // Calls the method named /methodName/ on every [[MonoBehaviour]] in this game object.
+ CUSTOM void SendMessage (string methodName, object value = null, SendMessageOptions options = SendMessageOptions.RequireReceiver)
+ {
+ DISALLOW_IN_CONSTRUCTOR
+ Scripting::SendScriptingMessage(GetGameObjectThrow(self), methodName, value, options);
+ }
+
+ //*undocumented* Function is for convenience and avoid coming mistakes.
+ CSRAW public void SendMessage (string methodName, SendMessageOptions options)
+ {
+ SendMessage (methodName, null, options);
+ }
+
+ // Calls the method named /methodName/ on every [[MonoBehaviour]] in this game object or any of its children.
+ CUSTOM void BroadcastMessage (string methodName, object parameter = null, SendMessageOptions options = SendMessageOptions.RequireReceiver)
+ {
+ DISALLOW_IN_CONSTRUCTOR
+ Scripting::BroadcastScriptingMessage(GetGameObjectThrow(self), methodName, parameter, options);
+ }
+
+ //*undocumented* Function is for convenience and avoid coming mistakes.
+ CSRAW public void BroadcastMessage (string methodName, SendMessageOptions options)
+ {
+ BroadcastMessage (methodName, null, options);
+ }
+END
+
+CSRAW }
diff --git a/Runtime/Export/UnityEngineComputeShader.txt b/Runtime/Export/UnityEngineComputeShader.txt
new file mode 100644
index 0000000..8631069
--- /dev/null
+++ b/Runtime/Export/UnityEngineComputeShader.txt
@@ -0,0 +1,216 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Shaders/ComputeShader.h"
+#include "Runtime/Graphics/Texture.h"
+#include "Runtime/Math/Vector4.h"
+#include "Runtime/Threads/AtomicOps.h"
+
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Misc/GraphicsScriptingUtility.h"
+
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+// Compute Shader asset.
+CLASS ComputeShader : Object
+
+ // Find [[ComputeShader]] kernel index.
+ CUSTOM int FindKernel (string name) {
+ FastPropertyName fpName = ScriptingStringToProperty(name);
+ return self->FindKernel (fpName);
+ }
+
+ // Set a float parameter.
+ CUSTOM void SetFloat (string name, float val) {
+ FastPropertyName fpName = ScriptingStringToProperty(name);
+ self->SetValueParam (fpName, 4, &val);
+ }
+
+ // Set an integer parameter.
+ CUSTOM void SetInt (string name, int val) {
+ FastPropertyName fpName = ScriptingStringToProperty(name);
+ self->SetValueParam (fpName, 4, &val);
+ }
+
+ // Set a vector parameter.
+ CUSTOM void SetVector (string name, Vector4 val) {
+ FastPropertyName fpName = ScriptingStringToProperty(name);
+ self->SetValueParam (fpName, 16, &val.x);
+ }
+
+ // Set multiple consecutive float parameters at once.
+ CSRAW public void SetFloats (string name, params float[] values) {
+ Internal_SetFloats (name, values);
+ }
+ CUSTOM private void Internal_SetFloats (string name, float[] values) {
+ FastPropertyName fpName = ScriptingStringToProperty(name);
+ int size = GetScriptingArraySize (values);
+ const float* arr = Scripting::GetScriptingArrayStart<float> (values);
+ self->SetValueParam (fpName, size*4, arr);
+ }
+
+ // Set multiple consecutive integer parameters at once.
+ CSRAW public void SetInts (string name, params int[] values) {
+ Internal_SetInts (name, values);
+ }
+ CUSTOM private void Internal_SetInts (string name, int[] values) {
+ FastPropertyName fpName = ScriptingStringToProperty(name);
+ int size = GetScriptingArraySize (values);
+ const int* arr = Scripting::GetScriptingArrayStart<int> (values);
+ self->SetValueParam (fpName, size*4, arr);
+ }
+
+ // Set a texture parameter.
+ CUSTOM void SetTexture (int kernelIndex, string name, Texture texture) {
+ FastPropertyName fpName = ScriptingStringToProperty(name);
+ Texture& tex = *texture;
+ self->SetTextureParam (kernelIndex, fpName, tex.GetTextureID());
+ }
+
+ // Set a [[ComputeBuffer]] parameter.
+ //
+ CUSTOM void SetBuffer (int kernelIndex, string name, ComputeBuffer buffer) {
+ FastPropertyName fpName = ScriptingStringToProperty(name);
+ self->SetBufferParam (kernelIndex, fpName, buffer->GetBufferHandle());
+ }
+
+ // Execute a compute shader.
+ CUSTOM void Dispatch (int kernelIndex, int threadsX, int threadsY, int threadsZ)
+ {
+ self->DispatchComputeShader (kernelIndex, threadsX, threadsY, threadsZ);
+ }
+
+END
+
+// [[ComputeBuffer]] type.
+CSRAW [Flags]
+ENUM ComputeBufferType
+ // Default [[ComputeBuffer]] type.
+ Default = 0,
+ // Raw [[ComputeBuffer]] type.
+ Raw = 1,
+ // Append-consume [[ComputeBuffer]] type.
+ Append = 2,
+ // [[ComputeBuffer]] with a counter.
+ Counter = 4,
+ // [[ComputeBuffer]] used for Graphics.DrawProceduralIndirect.
+ DrawIndirect = 256,
+END
+
+
+// Data buffer to hold data for compute shaders.
+CLASS ComputeBuffer : IDisposable
+
+ CSRAW #pragma warning disable 414
+ CSRAW internal IntPtr m_Ptr;
+ CSRAW #pragma warning restore 414
+
+ // IDisposable implementation, with Release() for explicit cleanup.
+
+ ~ComputeBuffer()
+ {
+ Dispose(false);
+ }
+
+ //*undocumented*
+ CSRAW public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ private void Dispose (bool disposing)
+ {
+ // we don't have any managed references, so
+ // 'disposing' part of standard IDisposable pattern
+ // does not apply
+
+ // Release native resources
+ DestroyBuffer (this);
+ m_Ptr = IntPtr.Zero;
+ }
+
+ CUSTOM private static void InitBuffer (ComputeBuffer buf, int count, int stride, ComputeBufferType type) {
+ buf.SetPtr(new ComputeBuffer(count, stride, type));
+ }
+ CUSTOM private static void DestroyBuffer(ComputeBuffer buf) {
+ delete buf.GetPtr();
+ }
+
+ ///*listonly*
+ CSRAW public ComputeBuffer (int count, int stride) : this (count, stride, ComputeBufferType.Default)
+ {
+ }
+
+ // Create a Compute Buffer.
+ CSRAW public ComputeBuffer (int count, int stride, ComputeBufferType type)
+ {
+ m_Ptr = IntPtr.Zero;
+ InitBuffer (this, count, stride, type);
+ }
+
+ // Release a Compute Buffer.
+ CSRAW public void Release ()
+ {
+ Dispose();
+ }
+
+ // Number of elements in the buffer (RO).
+ CUSTOM_PROP public int count {
+ return self->GetCount();
+ }
+
+ // Size of one element in the buffer (RO).
+ CUSTOM_PROP public int stride {
+ return self->GetStride();
+ }
+
+ // Set buffer data.
+ CSRAW
+ [System.Security.SecuritySafeCritical] // due to Marshal.SizeOf
+ public void SetData (System.Array data) {
+ #if !UNITY_FLASH
+ InternalSetData (data, Marshal.SizeOf(data.GetType().GetElementType()));
+ #endif
+ }
+
+ CSRAW [System.Security.SecurityCritical] // to prevent accidentally making this public in the future
+ CUSTOM private void InternalSetData (System.Array data, int elemSize) {
+ self->SetData (Scripting::GetScriptingArrayStart<char>(data), GetScriptingArraySize(data) * elemSize);
+ }
+
+
+ // Read buffer data.
+ CSRAW
+ [System.Security.SecuritySafeCritical] // due to Marshal.SizeOf
+ public void GetData (System.Array data) {
+ #if !UNITY_FLASH
+ InternalGetData (data, Marshal.SizeOf(data.GetType().GetElementType()));
+ #endif
+ }
+
+ CSRAW [System.Security.SecurityCritical] // to prevent accidentally making this public in the future
+ CUSTOM private void InternalGetData (System.Array data, int elemSize) {
+ self->GetData (Scripting::GetScriptingArrayStart<char>(data), GetScriptingArraySize(data) * elemSize);
+ }
+
+ // Copy counter value of append/consume buffer into another buffer.
+ CUSTOM static void CopyCount (ComputeBuffer src, ComputeBuffer dst, int dstOffset) {
+ ComputeBuffer::CopyCount (src.GetPtr(), dst.GetPtr(), dstOffset);
+ }
+
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/UnityEngineDebug.txt b/Runtime/Export/UnityEngineDebug.txt
new file mode 100644
index 0000000..28c9832
--- /dev/null
+++ b/Runtime/Export/UnityEngineDebug.txt
@@ -0,0 +1,134 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Input/InputManager.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Misc/DeveloperConsole.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Misc/DebugUtility.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/DeveloperConsole.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+#if UNITY_EDITOR
+ #include "Editor/Src/EditorSettings.h"
+ #include "Editor/Src/EditorUserBuildSettings.h"
+ #include "Editor/Mono/MonoEditorUtility.h"
+ #include "Editor/Src/AssetPipeline/MonoCompilationPipeline.h"
+#endif
+
+using namespace Unity;
+
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+// Class containing methods to ease debugging while developing a game.
+CLASS Debug
+
+ // Draws a line from the /point/ start to /end/ with color for a duration of time and with or without depth testing. If duration is 0 then the line is rendered 1 frame.
+
+ CUSTOM public static void DrawLine (Vector3 start, Vector3 end, Color color = Color.white, float duration = 0.0f, bool depthTest = true) { DebugDrawLine (start, end, color, duration, depthTest); }
+
+
+ // Draws a line from /start/ to /start/ + /dir/ with color for a duration of time and with or without depth testing. If duration is 0 then the line is rendered 1 frame.
+
+ CSRAW public static void DrawRay (Vector3 start, Vector3 dir, Color color = Color.white, float duration = 0.0f, bool depthTest = true) { DrawLine (start, start + dir, color, duration, depthTest); }
+
+
+ // Pauses the editor.
+ CUSTOM static void Break () { PauseEditor (); }
+
+ // Breaks into the attached debugger, if present
+ CUSTOM static void DebugBreak ()
+ {
+ #if DEBUGMODE && (UNITY_WIN && !UNITY_WINRT)
+ if( IsDebuggerPresent() )
+ ::DebugBreak();
+ #endif
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void Internal_Log (int level, string msg, [Writable]Object obj)
+ {
+ DebugStringToFile (msg.AsUTF8().c_str(), 0, __FILE__, __LINE__, (level==0?kScriptingLog:level==1?kScriptingWarning:kScriptingError) | kMayIgnoreLineNumber, obj.GetInstanceID());
+ }
+
+ THREAD_SAFE
+ CUSTOM private static void Internal_LogException(Exception exception, [Writable]Object obj)
+ {
+ Scripting::LogException(exception, obj.GetInstanceID());
+ }
+
+ // Logs /message/ to the Unity Console.
+ CSRAW public static void Log (object message) { Internal_Log (0, message != null ? message.ToString () : "Null", null); }
+
+ // Logs /message/ to the Unity Console.
+ CSRAW public static void Log (object message, Object context)
+ {
+ Internal_Log (0, message != null ? message.ToString () : "Null", context);
+ }
+
+ // A variant of Debug.Log that logs an error message to the console.
+ CSRAW public static void LogError (object message) { Internal_Log (2, message != null ? message.ToString () : "Null", null); }
+
+ // A variant of Debug.Log that logs an error message to the console.
+ CSRAW public static void LogError (object message, Object context) { Internal_Log (2,message.ToString (), context); }
+
+ // Clears errors from the developer console.
+ CUSTOM public static void ClearDeveloperConsole ()
+ {
+ #if UNITY_HAS_DEVELOPER_CONSOLE
+ if (GetDeveloperConsolePtr() != NULL) GetDeveloperConsolePtr()->Clear();
+ #endif
+ }
+
+ // Opens or closes developer console.
+ CUSTOM_PROP static bool developerConsoleVisible
+ {
+ #if UNITY_HAS_DEVELOPER_CONSOLE
+ if (GetDeveloperConsolePtr() != NULL) return GetDeveloperConsolePtr()->IsVisible();
+ #endif
+ return false;
+ }
+ {
+ #if UNITY_HAS_DEVELOPER_CONSOLE
+ if (GetDeveloperConsolePtr() != NULL) return GetDeveloperConsolePtr()->SetOpen(value);
+ #endif
+ }
+
+ // A variant of Debug.Log that logs an error message from an exception to the console.
+ CSRAW public static void LogException(Exception exception) { Internal_LogException(exception, null); }
+
+ // A variant of Debug.Log that logs an error message to the console.
+ CSRAW public static void LogException(Exception exception, Object context) { Internal_LogException(exception, context); }
+
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM internal static void LogPlayerBuildError (string message,string file,int line, int column)
+ {
+ DebugStringToFilePostprocessedStacktrace (message.AsUTF8().c_str(), "", "", 1, file.AsUTF8().c_str(), line, kScriptingError | kDontExtractStacktrace, 0, GetBuildErrorIdentifier());
+ }
+
+ // A variant of Debug.Log that logs a warning message to the console.
+ CSRAW public static void LogWarning (object message) { Internal_Log (1,message.ToString (), null); }
+
+ // A variant of Debug.Log that logs a warning message to the console.
+ CSRAW public static void LogWarning (object message, Object context) { Internal_Log (1,message.ToString (), context); }
+
+ // In the Build Settings dialog there is a check box called "Development Build".
+ CUSTOM_PROP static bool isDebugBuild { return GetBuildSettings().isDebugBuild; }
+
+ CUSTOM internal static void OpenConsoleFile() { DeveloperConsole_OpenConsoleFile(); }
+END
+
+CSRAW }
diff --git a/Runtime/Export/UnityEngineDisplay.txt b/Runtime/Export/UnityEngineDisplay.txt
new file mode 100644
index 0000000..67c871c
--- /dev/null
+++ b/Runtime/Export/UnityEngineDisplay.txt
@@ -0,0 +1,127 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Graphics/DisplayManager.h"
+
+CSRAW
+using System;
+using System.Collections;
+
+namespace UnityEngine
+{
+CLASS Display
+
+ CSRAW
+ internal IntPtr nativeDisplay;
+ internal Display() {
+ #if UNITY_FLASH
+ this.nativeDisplay = new IntPtr();
+ #else
+ this.nativeDisplay = new IntPtr(0);
+ #endif
+ }
+ internal Display(IntPtr nativeDisplay) { this.nativeDisplay = nativeDisplay; }
+
+ CSRAW public int renderingWidth { get
+ {
+ int w=0, h=0;
+ GetRenderingExtImpl(nativeDisplay, out w, out h);
+ return w;
+ }
+ }
+ CSRAW public int renderingHeight { get
+ {
+ int w=0, h=0;
+ GetRenderingExtImpl(nativeDisplay, out w, out h);
+ return h;
+ }
+ }
+
+ CSRAW public int systemWidth { get
+ {
+ int w=0, h=0;
+ GetSystemExtImpl(nativeDisplay, out w, out h);
+ return w;
+ }
+ }
+ CSRAW public int systemHeight { get
+ {
+ int w=0, h=0;
+ GetSystemExtImpl(nativeDisplay, out w, out h);
+ return h;
+ }
+ }
+
+ CSRAW public RenderBuffer colorBuffer { get
+ {
+ RenderBuffer color, depth;
+ GetRenderingBuffersImpl(nativeDisplay, out color, out depth);
+ return color;
+ }
+ }
+
+ CSRAW public RenderBuffer depthBuffer { get
+ {
+ RenderBuffer color, depth;
+ GetRenderingBuffersImpl(nativeDisplay, out color, out depth);
+ return depth;
+ }
+ }
+
+ CSRAW public void SetRenderingResolution(int w, int h)
+ {
+ SetRenderingResolutionImpl(nativeDisplay, w, h);
+ }
+
+ public static Display[] displays = new Display[1] { new Display() };
+
+ private static Display _mainDisplay = displays[0];
+ public static Display main { get{return _mainDisplay;} }
+
+ CSRAW private static void RecreateDisplayList(IntPtr[] nativeDisplay)
+ {
+ Display.displays = new Display[nativeDisplay.Length];
+ for(int i = 0 ; i < nativeDisplay.Length ; ++i)
+ Display.displays[i] = new Display(nativeDisplay[i]);
+
+ _mainDisplay = displays[0];
+ }
+
+ CSRAW private static void FireDisplaysUpdated()
+ {
+ if(onDisplaysUpdated != null)
+ onDisplaysUpdated();
+ }
+
+ CSRAW public delegate void DisplaysUpdatedDelegate();
+ CSRAW public static event DisplaysUpdatedDelegate onDisplaysUpdated = null;
+
+
+
+ CUSTOM private static void GetSystemExtImpl(IntPtr nativeDisplay, out int w, out int h)
+ {
+ UnityDisplayManager_DisplaySystemResolution(nativeDisplay,w,h);
+ }
+
+ CUSTOM private static void GetRenderingExtImpl(IntPtr nativeDisplay, out int w, out int h)
+ {
+ UnityDisplayManager_DisplaySystemResolution(nativeDisplay,w,h);
+ }
+
+ CUSTOM private static void GetRenderingBuffersImpl(IntPtr nativeDisplay, out RenderBuffer color, out RenderBuffer depth)
+ {
+ UnityDisplayManager_DisplayRenderingBuffers(nativeDisplay, &color->m_BufferPtr, &depth->m_BufferPtr);
+ }
+
+ CUSTOM private static void SetRenderingResolutionImpl(IntPtr nativeDisplay, int w, int h)
+ {
+ UnityDisplayManager_SetRenderingResolution(nativeDisplay,w,h);
+ }
+
+END
+
+CSRAW }
+
diff --git a/Runtime/Export/UnityEngineFlash.txt b/Runtime/Export/UnityEngineFlash.txt
new file mode 100644
index 0000000..2df69d4
--- /dev/null
+++ b/Runtime/Export/UnityEngineFlash.txt
@@ -0,0 +1,80 @@
+CSRAW
+using System;
+using System.Diagnostics;
+
+namespace UnityEngine.Flash
+{
+// Inline ActionScript support.
+CSRAW [NotConverted]
+CLASS ActionScript
+
+ // Causes an import directive to be emitted in the ActionScript code generated for the current type.
+ CSRAW [Conditional("UNITY_FLASH")]
+ CSRAW public static void Import(string package)
+ {
+ }
+
+ // Emits a block of ActionScript code in the current method translating variable and field references to their ActionScript names.
+ CSRAW [Conditional("UNITY_FLASH")]
+ CSRAW public static void Statement(string formatString, params object[] arguments)
+ {
+ }
+
+ // Emits an ActionScript expression translating variable and field references to their ActionScript names.
+ CSRAW public static T Expression<T>(string formatString, params object[] arguments)
+ {
+ throw new InvalidOperationException();
+ }
+END
+
+CSRAW
+}
+
+namespace UnityEngine.Flash
+{
+// Runtime FlashPlayer support.
+CLASS FlashPlayer
+
+ // Get a string representing the version of the Flash Player which the current project has been compiled against.
+ CSRAW public static String TargetVersion {
+ get { return GetUnityAppConstants("TargetFlashPlayerVersion"); }
+ }
+
+ // Get a string representing the version of the SWF which the current project has been compiled against.
+ CSRAW public static String TargetSwfVersion {
+ get { return GetUnityAppConstants("TargetSwfVersion"); }
+ }
+
+ CSRAW internal static String GetUnityAppConstants(String name)
+ {
+ ActionScript.Import("com.unity.UnityNative");
+ var value = ActionScript.Expression<String>("UnityNative.getUnityAppConstants()[{0}]", name);
+ return value;
+ }
+END
+
+CSRAW
+}
+
+namespace UnityEngine
+{
+
+// Instructs the build pipeline not to convert a type or member to the target platform.
+CSRAW [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)]
+CLASS NotConvertedAttribute : Attribute
+END
+
+// Instructs the build pipeline not to try and validate a type or member for the flash platform.
+CSRAW [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field)]
+CSRAW [NotConverted]
+CLASS NotFlashValidatedAttribute : Attribute
+END
+
+// Prevent name mangling of constructors, methods, fields and properties.
+CSRAW [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field)]
+CSRAW [NotConverted]
+CLASS NotRenamedAttribute : Attribute
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/UnityEngineGameObject.txt b/Runtime/Export/UnityEngineGameObject.txt
new file mode 100644
index 0000000..1915db0
--- /dev/null
+++ b/Runtime/Export/UnityEngineGameObject.txt
@@ -0,0 +1,439 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/BaseClasses/Tags.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Export/GameObjectExport.h"
+#if ENABLE_AUDIO
+#include "Runtime/Audio/AudioSource.h"
+#endif
+
+#include "Runtime/Animation/Animation.h"
+#include "Runtime/Camera/Camera.h"
+
+#if ENABLE_PHYSICS
+#include "Runtime/Dynamics/RigidBody.h"
+#include "Runtime/Dynamics/ConstantForce.h"
+#include "Runtime/Dynamics/HingeJoint.h"
+#include "Runtime/Dynamics/Collider.h"
+#endif
+#if ENABLE_2D_PHYSICS
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Collider2D.h"
+#endif
+#include "Runtime/Camera/Light.h"
+#include "Runtime/Filters/Renderer.h"
+#if ENABLE_NETWORK
+#include "Runtime/Network/NetworkView.h"
+#endif
+#include "Runtime/Camera/RenderLayers/GUIText.h"
+#include "Runtime/Camera/RenderLayers/GUITexture.h"
+#include "Runtime/Filters/Particles/ParticleEmitter.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystem.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Misc/GOCreation.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/GetComponent.h"
+#include "Runtime/Scripting/Scripting.h"
+
+
+using namespace Unity;
+
+/*
+ Mono defines a bool as either 1 or 2 bytes.
+ On windows a bool on the C++ side needs to be 2 bytes.
+ We use the typemap to map bool's to short's.
+ When using the C++ keyword and you want to export a bool value
+ to mono you have to use a short on the C++ side.
+*/
+
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using UnityEngineInternal;
+
+namespace UnityEngine
+{
+
+
+
+// Base class for all entities in Unity scenes.
+CSRAW
+CLASS GameObject : Object
+
+ // Creates a game object with a primitive mesh renderer and appropriate collider.
+ CSRAW
+ CUSTOM static GameObject CreatePrimitive (PrimitiveType type)
+ {
+ return Scripting::ScriptingWrapperFor(CreatePrimitive(type));
+ }
+
+
+
+ // Returns the component of Type /type/ if the game object has one attached, null if it doesn't. You can access both builtin components or scripts with this function.
+
+ CSRAW
+ [TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
+ CUSTOM Component GetComponent (Type type) { return ScriptingGetComponentOfType (*self, type); }
+
+ CSRAW
+ #if ENABLE_GENERICS
+ // Generic version. See the [[wiki:Generic Functions|Generic Functions]] page for more details.
+ public T GetComponent<T>() where T : Component { return GetComponent(typeof(T)) as T; }
+ #endif
+
+
+ // Returns the component with name /type/ if the game object has one attached, null if it doesn't.
+
+ CSRAW
+ public Component GetComponent (string type) { return GetComponentByName(type); }
+ CUSTOM private Component GetComponentByName (string type) { return Scripting::GetScriptingWrapperOfComponentOfGameObject (*self, type); }
+
+
+
+
+ // Returns the component of Type /type/ in the GameObject or any of its children using depth first search.
+
+ CSRAW
+ [TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
+ CSRAW public Component GetComponentInChildren (Type type)
+ {
+ if( activeInHierarchy ) {
+
+ Component attachedCom = GetComponent (type);
+ if ( attachedCom != null )
+ return attachedCom;
+ }
+
+ Transform transform = this.transform;
+ if ( transform != null )
+ {
+ foreach (Transform child in transform)
+ {
+ Component childCom = child.gameObject.GetComponentInChildren (type);
+ if (childCom != null)
+ return childCom;
+ }
+ }
+
+ return null;
+ }
+
+ CSRAW
+ #if ENABLE_GENERICS
+ // Generic version. See the [[wiki:Generic Functions|Generic Functions]] page for more details.
+ public T GetComponentInChildren<T>() where T : Component { return GetComponentInChildren(typeof(T)) as T; }
+ #endif
+
+ // Editor only API that specifies if a game object is static.
+ AUTO_PROP bool isStatic GetIsStaticDeprecated SetIsStaticDeprecated
+
+ CUSTOM_PROP internal bool isStaticBatchable { return self->IsStaticBatchable (); }
+
+ // Returns all components of Type /type/ in the GameObject.
+
+ CSRAW [CanConvertToFlash]
+ CSRAW public Component[] GetComponents (Type type) { return (Component[])GetComponentsInternal (type, false, false, true); }
+
+ CSRAW
+ #if ENABLE_GENERICS
+ // Generic version. See the [[wiki:Generic Functions|Generic Functions]] page for more details.
+ public T[] GetComponents<T>() where T : Component
+ {
+ return (T[]) GetComponentsInternal(typeof(T), true, false, true);
+ }
+ #endif
+
+ // Returns all components of Type /type/ in the GameObject or any of its children.
+
+ CSRAW public Component[] GetComponentsInChildren (Type type, bool includeInactive = false)
+ {
+ return (Component[])GetComponentsInternal(type, false, true, includeInactive);
+ }
+
+ CSRAW
+ #if ENABLE_GENERICS
+ // Generic version. See the [[wiki:Generic Functions|Generic Functions]] page for more details.
+ public T[] GetComponentsInChildren<T>(bool includeInactive) where T : Component
+ {
+ return (T[])GetComponentsInternal(typeof(T), true, true, includeInactive);
+ }
+ #endif
+
+ CSRAW
+ #if ENABLE_GENERICS
+ // Manually supplying the overload because lucas cannot figure out how to patch cspreprocess to add template support to the overload generation code.
+ // Generic version. See the [[wiki:Generic Functions|Generic Functions]] page for more details.
+ public T[] GetComponentsInChildren<T>() where T : Component { return GetComponentsInChildren<T>(false); }
+ #endif
+
+
+
+ CUSTOM private Component[] GetComponentsInternal(Type type, bool isGenericTypeArray, bool recursive, bool includeInactive)
+ {
+ DISALLOW_IN_CONSTRUCTOR return ScriptingGetComponentsOfType (*self, type, isGenericTypeArray, recursive, includeInactive);
+ }
+
+
+ C++RAW
+ #define FASTGO_QUERY_COMPONENT(x) GameObject& go = *self; return Scripting::GetComponentObjectToScriptingObject (go.QueryComponent (x), go, ClassID (x));
+
+
+ // The [[Transform]] attached to this GameObject. (null if there is none attached)
+ CSRAW
+ CUSTOM_PROP Transform transform { FASTGO_QUERY_COMPONENT(Transform) }
+
+ CONDITIONAL ENABLE_PHYSICS
+ // The [[Rigidbody]] attached to this GameObject (RO). (null if there is none attached)
+ CUSTOM_PROP Rigidbody rigidbody { FASTGO_QUERY_COMPONENT(Rigidbody) }
+
+ CONDITIONAL ENABLE_2D_PHYSICS
+ CUSTOM_PROP Rigidbody2D rigidbody2D { FASTGO_QUERY_COMPONENT(Rigidbody2D) }
+
+ // The [[Camera]] attached to this GameObject (RO). (null if there is none attached)
+ CUSTOM_PROP Camera camera { FASTGO_QUERY_COMPONENT(Camera) }
+
+ // The [[Light]] attached to this GameObject (RO). (null if there is none attached)
+ CUSTOM_PROP Light light { FASTGO_QUERY_COMPONENT(Light) }
+
+ // The [[Animation]] attached to this GameObject (RO). (null if there is none attached)
+ CUSTOM_PROP Animation animation { FASTGO_QUERY_COMPONENT(Animation) }
+
+ // The [[ConstantForce]] attached to this GameObject (RO). (null if there is none attached)
+ CONDITIONAL ENABLE_PHYSICS
+ CUSTOM_PROP ConstantForce constantForce { FASTGO_QUERY_COMPONENT(ConstantForce) }
+
+ // The [[Renderer]] attached to this GameObject (RO). (null if there is none attached)
+ CUSTOM_PROP Renderer renderer { FASTGO_QUERY_COMPONENT(Renderer) }
+
+ // The [[AudioSource]] attached to this GameObject (RO). (null if there is none attached)
+ CONDITIONAL ENABLE_AUDIO
+ CUSTOM_PROP AudioSource audio { FASTGO_QUERY_COMPONENT(AudioSource) }
+
+ // The [[GUIText]] attached to this GameObject (RO). (null if there is none attached)
+ CUSTOM_PROP GUIText guiText { FASTGO_QUERY_COMPONENT(GUIText) }
+
+ CONDITIONAL ENABLE_NETWORK
+ // The [[NetworkView]] attached to this GameObject (RO). (null if there is none attached)
+ CUSTOM_PROP NetworkView networkView
+ {
+ #if ENABLE_NETWORK
+ FASTGO_QUERY_COMPONENT(NetworkView)
+ #else
+ return SCRIPTING_NULL;
+ #endif
+ }
+
+ FLUSHCONDITIONS
+
+ CSRAW
+#if ENABLE_NETWORK
+ OBSOLETE warning Please use guiTexture instead
+#endif
+ CUSTOM_PROP GUIElement guiElement { FASTGO_QUERY_COMPONENT(GUIElement) }
+
+ // The [[GUITexture]] attached to this GameObject (RO). (null if there is none attached)
+ CUSTOM_PROP GUITexture guiTexture { FASTGO_QUERY_COMPONENT(GUITexture) }
+
+ // The [[Collider]] attached to this GameObject (RO). (null if there is none attached)
+ CONDITIONAL ENABLE_PHYSICS
+ CUSTOM_PROP Collider collider { FASTGO_QUERY_COMPONENT(Collider) }
+
+ CONDITIONAL ENABLE_2D_PHYSICS
+ CUSTOM_PROP Collider2D collider2D { FASTGO_QUERY_COMPONENT(Collider2D) }
+
+ // The [[HingeJoint]] attached to this GameObject (RO). (null if there is none attached)
+ CONDITIONAL ENABLE_PHYSICS
+ CUSTOM_PROP HingeJoint hingeJoint { FASTGO_QUERY_COMPONENT(HingeJoint) }
+
+ // The [[ParticleEmitter]] attached to this GameObject (RO). (null if there is none attached)
+ CUSTOM_PROP ParticleEmitter particleEmitter { FASTGO_QUERY_COMPONENT(ParticleEmitter) }
+
+ // The [[ParticleSystem]] attached to this GameObject (RO). (null if there is none attached)
+ CUSTOM_PROP ParticleSystem particleSystem { FASTGO_QUERY_COMPONENT(ParticleSystem) }
+
+ // The layer the game object is in. A layer is in the range [0...31]
+ AUTO_PROP int layer GetLayer SetLayer
+
+ OBSOLETE warning GameObject.active is obsolete. Use GameObject.SetActive(), GameObject.activeSelf or GameObject.activeInHierarchy.
+ CSRAW
+ CUSTOM_PROP bool active { return self->IsActive (); } { self->SetSelfActive (value); }
+
+ // Activates/Deactivates the GameObject.
+ CUSTOM void SetActive (bool value) { self->SetSelfActive (value); }
+
+ // The local active state of this GameObject.
+ CUSTOM_PROP public bool activeSelf { return self->IsSelfActive (); }
+
+ // Is the GameObject active in the scene?
+ CUSTOM_PROP public bool activeInHierarchy { return self->IsActive (); }
+
+ OBSOLETE warning gameObject.SetActiveRecursively() is obsolete. Use GameObject.SetActive(), which is now inherited by children.
+ CUSTOM public void SetActiveRecursively (bool state)
+ {
+ self->SetActiveRecursivelyDeprecated (state);
+ }
+
+ // The tag of this game object.
+ CUSTOM_PROP string tag
+ {
+ const string& tag = TagToString (self->GetTag ());
+ if (!tag.empty ())
+ return scripting_string_new(tag);
+ else
+ {
+ Scripting::RaiseMonoException ("GameObject has undefined tag!");
+ return SCRIPTING_NULL;
+ }
+ }
+ {
+ self->SetTag (ExtractTagThrowing (value));
+ }
+
+ // Is this game object tagged with /tag/?
+ CUSTOM public bool CompareTag (string tag)
+ {
+ return ExtractTagThrowing (tag) == self->GetTag ();
+ }
+
+ //*undocumented*
+ CUSTOM static GameObject FindGameObjectWithTag (string tag)
+ {
+ DISALLOW_IN_CONSTRUCTOR
+ int tagInt = ExtractTagThrowing (tag);
+ return Scripting::ScriptingWrapperFor(FindGameObjectWithTag (tagInt));
+ }
+
+ // Returns one active GameObject tagged /tag/. Returns null if no GameObject was found.
+ CSRAW static public GameObject FindWithTag (string tag)
+ {
+ return FindGameObjectWithTag(tag);
+ }
+
+ // Returns a list of active GameObjects tagged /tag/. Returns null if no GameObject was found.
+ CUSTOM static GameObject[] FindGameObjectsWithTag (string tag)
+ {
+ DISALLOW_IN_CONSTRUCTOR
+ static vector<GameObject*> go; go.clear ();
+ int tagInt = ExtractTagThrowing (tag);
+ FindGameObjectsWithTag (tagInt, go);
+ ScriptingClassPtr goClass = Scripting::ClassIDToScriptingType (ClassID (GameObject));
+ return CreateScriptingArrayFromUnityObjects(go,goClass);
+ }
+
+ // Calls the method named /methodName/ on every [[MonoBehaviour]] in this game object and on every ancestor of the behaviour
+ CUSTOM void SendMessageUpwards (string methodName, object value = null, SendMessageOptions options = SendMessageOptions.RequireReceiver)
+ {
+ Scripting::SendScriptingMessageUpwards(*self, methodName, value, options);
+ }
+
+ //*undocumented* Function is for convenience and avoid coming mistakes.
+ CSRAW public void SendMessageUpwards (string methodName, SendMessageOptions options)
+ {
+ SendMessageUpwards (methodName, null, options);
+ }
+
+ // Calls the method named /methodName/ on every [[MonoBehaviour]] in this game object.
+ CUSTOM void SendMessage (string methodName, object value = null, SendMessageOptions options = SendMessageOptions.RequireReceiver)
+ {
+ Scripting::SendScriptingMessage(*self, methodName, value, options);
+ }
+
+ //*undocumented* Function is for convenience and avoid coming mistakes.
+ CSRAW public void SendMessage (string methodName, SendMessageOptions options)
+ {
+ SendMessage (methodName, null, options);
+ }
+
+ // Calls the method named /methodName/ on every [[MonoBehaviour]] in this game object or any of its children.
+ CUSTOM void BroadcastMessage (string methodName, object parameter = null, SendMessageOptions options = SendMessageOptions.RequireReceiver)
+ {
+ Scripting::BroadcastScriptingMessage(*self, methodName, parameter, options);
+ }
+
+ //*undocumented* Function is for convenience and avoid coming mistakes.
+ CSRAW public void BroadcastMessage (string methodName, SendMessageOptions options)
+ {
+ BroadcastMessage (methodName, null, options);
+ }
+
+ // Adds a component class named /className/ to the game object.
+
+ CUSTOM Component AddComponent (string className) { return MonoAddComponent (*self, className.AsUTF8().c_str()); }
+
+ // Adds a component class of type /componentType/ to the game object. C# Users can use a generic version
+ CSRAW
+ [TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
+ CSRAW public Component AddComponent (Type componentType)
+ {
+ return Internal_AddComponentWithType(componentType);
+ }
+
+ CSRAW
+ CUSTOM private Component Internal_AddComponentWithType (Type componentType)
+ {
+ return MonoAddComponentWithType(*self, componentType);
+ }
+
+ CSRAW
+ #if ENABLE_GENERICS
+ // Generic version. See the [[wiki:Generic Functions|Generic Functions]] page for more details.
+ public T AddComponent<T>() where T : Component
+ {
+ return AddComponent(typeof(T)) as T;
+ }
+ #endif
+
+ CUSTOM private static void Internal_CreateGameObject ([Writable]GameObject mono, string name)
+ {
+ DISALLOW_IN_CONSTRUCTOR
+ GameObject* go = MonoCreateGameObject (name.IsNull() ? NULL : name.AsUTF8().c_str() );
+ Scripting::ConnectScriptingWrapperToObject (mono.GetScriptingObject(), go);
+ }
+
+ // Creates a new game object, named __name__.
+ CSRAW public GameObject (string name) { Internal_CreateGameObject (this, name); }
+
+ // Creates a new game object.
+ CSRAW public GameObject () { Internal_CreateGameObject (this, null); }
+
+ // Creates a game object and attaches the specified components.
+ CSRAW public GameObject (string name, params Type[] components)
+ {
+ Internal_CreateGameObject (this, name);
+ foreach (Type t in components)
+ AddComponent (t);
+ }
+
+ OBSOLETE warning gameObject.PlayAnimation is not supported anymore. Use animation.Play
+ CUSTOM void PlayAnimation (AnimationClip animation) { ErrorString("gameObject.PlayAnimation(); is not supported anymore. Use animation.Stop();"); }
+
+ OBSOLETE warning gameObject.StopAnimation is not supported anymore. Use animation.Stop
+ CUSTOM void StopAnimation () { ErrorString("gameObject.StopAnimation(); is not supported anymore. Use animation.Stop();"); }
+
+ // Finds a game object by /name/ and returns it.
+ CSRAW
+ CUSTOM static GameObject Find (string name)
+ {
+ DISALLOW_IN_CONSTRUCTOR
+ Transform* transform = FindActiveTransformWithPath (name.AsUTF8().c_str ());
+ GameObject* go = NULL;
+ if (transform)
+ go = transform->GetGameObjectPtr ();
+ return Scripting::ScriptingWrapperFor (go);
+ }
+
+ //*undocumented - just for convenience
+ CSRAW public GameObject gameObject { get { return this; } }
+}
+END
+
+
diff --git a/Runtime/Export/UnityEngineInput.txt b/Runtime/Export/UnityEngineInput.txt
new file mode 100644
index 0000000..04e421b
--- /dev/null
+++ b/Runtime/Export/UnityEngineInput.txt
@@ -0,0 +1,700 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Input/InputManager.h"
+#include "Runtime/Input/GetInput.h"
+#include "Runtime/Input/LocationService.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Scripting.h"
+
+#if SUPPORT_REPRODUCE_LOG
+#include "Runtime/Misc/ReproductionLog.h"
+#endif
+
+using namespace Unity;
+
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using UnityEngineInternal;
+
+namespace UnityEngine
+{
+
+
+// Describes phase of a finger touch.
+ENUM TouchPhase
+ // A finger touched the screen.
+ Began = 0,
+ // A finger moved on the screen.
+ Moved = 1,
+ // A finger is touching the screen but hasn't moved.
+ Stationary = 2,
+ // A finger was lifted from the screen. This is the final phase of a touch.
+ Ended = 3,
+ // The system cancelled tracking for the touch, as when (for example) the user puts the device to her face or more than five touches happened simultaneously. This is the final phase of a touch.
+ Canceled = 4
+END
+
+// Controls IME input
+ENUM IMECompositionMode
+ // Enable IME input only when a text field is selected (default).
+ Auto = 0,
+ // Enable IME input.
+ On = 1,
+ // Disable IME input.
+ Off = 2
+END
+
+// Structure describing status of a finger touching the screen.
+STRUCT Touch
+ CSRAW
+ private int m_FingerId;
+ private Vector2 m_Position;
+ private Vector2 m_RawPosition;
+#if ENABLE_NEW_EVENT_SYSTEM
+ private Vector2 m_OldPosition;
+ private Vector2 m_PositionDelta;
+ private Vector2 m_ScrollDelta;
+ private Vector3 m_WorldPosition;
+ private float m_TimeDelta;
+ private int m_TapCount;
+ private TouchPhase m_Phase;
+ private IntPtr m_Hover;
+ private IntPtr m_Press;
+#else
+ private Vector2 m_PositionDelta;
+ private float m_TimeDelta;
+ private int m_TapCount;
+ private TouchPhase m_Phase;
+#endif
+
+ // The unique index for touch.
+ CSRAW public int fingerId { get { return m_FingerId; } }
+
+ // The position of the touch.
+ CSRAW public Vector2 position { get { return m_Position; } }
+
+ // Raw Position of the touch (taken from os without tweaking)
+ CSRAW public Vector2 rawPosition { get { return m_RawPosition; } }
+
+ // The position delta since last change.
+ CSRAW public Vector2 deltaPosition { get { return m_PositionDelta; } }
+
+ // The scroll delta position. X = horizontal, Y = vertical.
+ CONDITIONAL ENABLE_NEW_EVENT_SYSTEM
+ CSRAW public Vector2 deltaScroll { get { return m_ScrollDelta; } }
+
+ // The world position of the touch (raycast into the screen hitting a collider).
+ CONDITIONAL ENABLE_NEW_EVENT_SYSTEM
+ public Vector3 worldPosition { get { return m_WorldPosition; } }
+
+ // Amount of time passed since last change.
+ CSRAW public float deltaTime { get { return m_TimeDelta; } }
+
+ // Number of taps.
+ CSRAW public int tapCount { get { return m_TapCount; } }
+
+ // Describes the phase of the touch.
+ CSRAW public TouchPhase phase { get { return m_Phase; } }
+
+ CUSTOM static private GameObject Internal_GetPtr (IntPtr ptr) { return Scripting::ScriptingWrapperFor((GameObject*)ptr); }
+
+ // The object the mouse is hovering over (only works for mouse events)
+ CONDITIONAL ENABLE_NEW_EVENT_SYSTEM
+ CSRAW public GameObject hoveredObject { get { return Internal_GetPtr(m_Hover); } }
+
+ // The object this mouse or touch has pressed on
+ CONDITIONAL ENABLE_NEW_EVENT_SYSTEM
+ CSRAW public GameObject pressedObject { get { return Internal_GetPtr(m_Press); } }
+END
+
+// Describes physical orientation of the device as determined by the OS.
+ENUM DeviceOrientation
+ // The orientation of the device cannot be determined.
+ Unknown = 0,
+ // The device is in portrait mode, with the device held upright and the home button at the bottom.
+ Portrait = 1,
+ // The device is in portrait mode but upside down, with the device held upright and the home button at the top.
+ PortraitUpsideDown = 2,
+ // The device is in landscape mode, with the device held upright and the home button on the right side.
+ LandscapeLeft = 3,
+ // The device is in landscape mode, with the device held upright and the home button on the left side.
+ LandscapeRight = 4,
+ // The device is held parallel to the ground with the screen facing upwards.
+ FaceUp = 5,
+ // The device is held parallel to the ground with the screen facing downwards.
+ FaceDown = 6
+END
+
+// Structure describing acceleration status of the device.
+STRUCT AccelerationEvent
+ CSRAW private Vector3 m_Acceleration;
+ CSRAW private float m_TimeDelta;
+
+ // Value of acceleration.
+ CSRAW public Vector3 acceleration { get { return m_Acceleration; } }
+
+ // Amount of time passed since last accelerometer measurement.
+ CSRAW public float deltaTime { get { return m_TimeDelta; } }
+END
+
+// Interface into the Gyroscope.
+CLASS Gyroscope
+
+ CSRAW internal Gyroscope(int index) {
+ m_GyroIndex = index;
+ }
+
+ CSRAW private int m_GyroIndex;
+ CUSTOM private static Vector3 rotationRate_Internal(int idx) { return GetGyroRotationRate(idx); }
+ CUSTOM private static Vector3 rotationRateUnbiased_Internal(int idx) { return GetGyroRotationRateUnbiased(idx); }
+ CUSTOM private static Vector3 gravity_Internal(int idx) { return GetGravity(idx); }
+ CUSTOM private static Vector3 userAcceleration_Internal(int idx) { return GetUserAcceleration(idx); }
+ CUSTOM private static Quaternion attitude_Internal(int idx) { return GetAttitude(idx); }
+ CUSTOM private static bool getEnabled_Internal(int idx) { return IsGyroEnabled(idx); }
+ CUSTOM private static void setEnabled_Internal(int idx, bool enabled) { SetGyroEnabled(idx, enabled); }
+ CUSTOM private static float getUpdateInterval_Internal(int idx) { return GetGyroUpdateInterval(idx); }
+ CUSTOM private static void setUpdateInterval_Internal(int idx, float interval) { SetGyroUpdateInterval(idx, interval); }
+
+ // Returns rotation rate as measured by the device's gyroscope.
+ CSRAW public Vector3 rotationRate { get { return rotationRate_Internal(m_GyroIndex); } }
+
+ // Returns unbiased rotation rate as measured by the device's gyroscope.
+ CSRAW public Vector3 rotationRateUnbiased { get { return rotationRateUnbiased_Internal(m_GyroIndex); } }
+
+ // Returns the gravity acceleration vector expressed in the device's reference frame.
+ CSRAW public Vector3 gravity { get { return gravity_Internal(m_GyroIndex); } }
+
+ // Returns the acceleration that the user is giving to the device.
+ CSRAW public Vector3 userAcceleration { get { return userAcceleration_Internal(m_GyroIndex); } }
+
+ // Returns the attitude of the device.
+ CSRAW public Quaternion attitude { get { return attitude_Internal(m_GyroIndex); } }
+
+ // Sets or retrieves status of this gyroscope.
+ CSRAW public bool enabled { get { return getEnabled_Internal(m_GyroIndex); } set { setEnabled_Internal(m_GyroIndex, value); } }
+
+ // Sets or retrieves gyroscope interval in seconds.
+ CSRAW public float updateInterval { get { return getUpdateInterval_Internal(m_GyroIndex); } set { setUpdateInterval_Internal(m_GyroIndex, value); } }
+
+END
+
+// Structure describing device location.
+STRUCT LocationInfo
+ CSRAW private double m_Timestamp;
+ CSRAW private float m_Latitude;
+ CSRAW private float m_Longitude;
+ CSRAW private float m_Altitude;
+ CSRAW private float m_HorizontalAccuracy;
+ CSRAW private float m_VerticalAccuracy;
+
+ // Geographical device location latitude
+ CSRAW public float latitude { get { return m_Latitude; } }
+
+ // Geographical device location latitude
+ CSRAW public float longitude { get { return m_Longitude; } }
+
+ // Geographical device location altitude
+ CSRAW public float altitude { get { return m_Altitude; } }
+
+ // Horizontal accuracy of the location
+ CSRAW public float horizontalAccuracy { get { return m_HorizontalAccuracy; } }
+
+ // Vertical accuracy of the location
+ CSRAW public float verticalAccuracy { get { return m_VerticalAccuracy; } }
+
+ // Timestamp (in seconds since 1970) when location was last time updated
+ CSRAW public double timestamp { get { return m_Timestamp; } }
+END
+
+// Describes location service status.
+ENUM LocationServiceStatus
+ // Location service is stopped.
+ Stopped = 0,
+ // Location service is initializing, some time later it will switch to
+ Initializing = 1,
+ // Location service is running and locations could be queried.
+ Running = 2,
+ // Location service failed (user denied access to location service).
+ Failed = 3
+END
+
+// Interface into location functionality.
+CLASS LocationService
+ // Specifies whether location service is enabled in user settings.
+ CUSTOM_PROP bool isEnabledByUser
+ {
+ return LocationService::IsServiceEnabledByUser ();
+ }
+
+ // Returns location service status.
+ CUSTOM_PROP LocationServiceStatus status
+ {
+ return LocationService::GetLocationStatus ();
+ }
+
+ // Last measured device geographical location.
+ CUSTOM_PROP LocationInfo lastData
+ {
+ if (LocationService::GetLocationStatus () != kLocationServiceRunning)
+ printf_console ("Location service updates are not enabled. Check Handheld.locationServiceStatus before querying last location.\n");
+
+ return LocationService::GetLastLocation ();
+ }
+
+ // Starts location service updates. Last location coordinates could be
+ CUSTOM void Start (float desiredAccuracyInMeters = 10f, float updateDistanceInMeters = 10f)
+ {
+ LocationService::SetDesiredAccuracy (desiredAccuracyInMeters);
+ LocationService::SetDistanceFilter (updateDistanceInMeters);
+ LocationService::StartUpdatingLocation ();
+ }
+
+ // Stops location service updates. This could be useful for saving battery
+ CUSTOM void Stop ()
+ {
+ LocationService::StopUpdatingLocation ();
+ }
+END
+
+// Interface into compass functionality.
+CLASS Compass
+ // The heading in degrees relative to the magnetic North Pole.
+ CUSTOM_PROP public float magneticHeading
+ {
+ return LocationService::GetLastHeading().magneticHeading;
+ }
+
+ // The heading in degrees relative to the geographic North Pole.
+ CUSTOM_PROP public float trueHeading
+ {
+ return LocationService::GetLastHeading().trueHeading;
+ }
+
+ // The raw geomagnetic data measured in microteslas. (RO)
+ CUSTOM_PROP public Vector3 rawVector
+ {
+ return LocationService::GetLastHeading().raw;
+ }
+
+ // Timestamp (in seconds since 1970) when the heading was last time updated. (RO)
+ CUSTOM_PROP public double timestamp
+ {
+ return LocationService::GetLastHeading().timestamp;
+ }
+
+ // Used to enable or disable compass. Note, that if you want @@Input.compass.trueHeading@@ property to contain a valid value, you must also enable location updates by calling @@Input.location.Start()@@.
+ CUSTOM_PROP public bool enabled
+ {
+ return LocationService::IsHeadingUpdatesEnabled();
+ }
+ {
+ LocationService::SetHeadingUpdatesEnabled(value);
+ }
+END
+
+// Interface into the Input system.
+CLASS Input
+
+ CUSTOM private static int mainGyroIndex_Internal() { return GetGyro(); }
+
+ CONDITIONAL ENABLE_MONO || UNITY_WINRT
+ CSRAW private static Gyroscope m_MainGyro = null;
+
+ CUSTOM private static bool GetKeyInt (int key)
+ {
+ if (key > 0 && key < kKeyAndJoyButtonCount)
+ return GetInputManager ().GetKey (key);
+ else if (key == 0)
+ return false;
+ else
+ {
+ Scripting::RaiseMonoException ("Invalid KeyCode enum.");
+ return false;
+ }
+ }
+
+ CUSTOM private static bool GetKeyString (string name)
+ {
+ string cname = name;
+ int key = StringToKey (cname);
+ if (key != 0)
+ return GetInputManager ().GetKey (key);
+ else
+ {
+ Scripting::RaiseMonoException ("Input Key named: %s is unknown", cname.c_str());
+ return false;
+ }
+ }
+
+ CUSTOM private static bool GetKeyUpInt (int key)
+ {
+ if (key > 0 && key < kKeyAndJoyButtonCount)
+ return GetInputManager ().GetKeyUp (key);
+ else if (key == 0)
+ return false;
+ else
+ {
+ Scripting::RaiseMonoException ("Invalid KeyCode enum.");
+ return false;
+ }
+ }
+
+ CUSTOM private static bool GetKeyUpString (string name)
+ {
+ string cname = name;
+ int key = StringToKey (cname);
+ if (key != 0)
+ return GetInputManager ().GetKeyUp (key);
+ else
+ {
+ Scripting::RaiseMonoException ("Input Key named: %s is unknown", cname.c_str());
+ return false;
+ }
+ }
+
+ CUSTOM private static bool GetKeyDownInt (int key)
+ {
+ if (key > 0 && key < kKeyAndJoyButtonCount)
+ return GetInputManager ().GetKeyDown (key);
+ else if (key == 0)
+ return false;
+ else
+ {
+ Scripting::RaiseMonoException ("Invalid KeyCode enum.");
+ return false;
+ }
+ }
+
+ CUSTOM private static bool GetKeyDownString (string name)
+ {
+ string cname = name;
+ int key = StringToKey (cname);
+ if (key != 0)
+ return GetInputManager ().GetKeyDown (key);
+ else
+ {
+ Scripting::RaiseMonoException ("Input Key named: %s is unknown", cname.c_str());
+ return false;
+ }
+ }
+
+ C++RAW
+ static void CheckInputAxis (const string& name, bool button)
+ {
+ #if !GAMERELEASE
+ if (!GetInputManager ().HasAxisOrButton (name))
+ {
+ if (button)
+ Scripting::RaiseMonoException ("Input Button %s is not setup.\n To change the input settings use: Edit -> Project Settings -> Input", name.c_str());
+ else
+ Scripting::RaiseMonoException ("Input Axis %s is not setup.\n To change the input settings use: Edit -> Project Settings -> Input", name.c_str());
+ }
+
+ #endif
+ }
+
+
+ // Returns the value of the virtual axis identified by /axisName/.
+ CUSTOM static float GetAxis (string axisName)
+ {
+ string name = axisName;
+ CheckInputAxis (name, false);
+ return GetInputManager ().GetAxis (name);
+ }
+
+
+ // Returns the value of the virtual axis identified by /axisName/ with no smoothing filtering applied.
+ CUSTOM static float GetAxisRaw (string axisName)
+ {
+ string name = axisName;
+ CheckInputAxis (name, false);
+ return GetInputManager ().GetAxisRaw (name);
+ }
+
+ // Returns true while the virtual button identified by /buttonName/ is held down.
+ CUSTOM static bool GetButton (string buttonName)
+ {
+ string name = buttonName;
+ CheckInputAxis (name, true);
+ return GetInputManager ().GetButton (name);
+ }
+
+ // This property controls if input sensors should be compensated for screen orientation.
+ CUSTOM_PROP static bool compensateSensors { return IsCompensatingSensors(); } { SetCompensatingSensors(value); }
+
+ OBSOLETE warning isGyroAvailable property is deprecated. Please use SystemInfo.supportsGyroscope instead.
+ CUSTOM_PROP static bool isGyroAvailable { return IsGyroAvailable(); }
+
+ // Returns default gyroscope.
+ CONDITIONAL ENABLE_MONO || UNITY_WINRT
+ CSRAW public static Gyroscope gyro { get { if (m_MainGyro == null) m_MainGyro = new Gyroscope(mainGyroIndex_Internal()); return m_MainGyro; } }
+
+ // Returns true during the frame the user pressed down the virtual button identified by /buttonName/.
+ CUSTOM static bool GetButtonDown (string buttonName)
+ {
+ string name = buttonName;
+ CheckInputAxis (name, true);
+ return GetInputManager ().GetButtonDown (name);
+ }
+
+ // Returns true the first frame the user releases the virtual button identified by /buttonName/.
+ CUSTOM static bool GetButtonUp (string buttonName)
+ {
+ string name = buttonName;
+ CheckInputAxis (name, true);
+ return GetInputManager ().GetButtonUp (name);
+ }
+
+ // Returns true while the user holds down the key identified by /name/. Think auto fire.
+ CSRAW public static bool GetKey (string name)
+ {
+ return GetKeyString(name);
+ }
+
+ // Returns true while the user holds down the key identified by the /key/ [[KeyCode]] enum parameter.
+ CSRAW public static bool GetKey (KeyCode key)
+ {
+ return GetKeyInt((int)key);
+ }
+
+ // Returns true during the frame the user starts pressing down the key identified by /name/.
+ CSRAW public static bool GetKeyDown (string name)
+ {
+ return GetKeyDownString(name);
+ }
+
+ // Returns true during the frame the user starts pressing down the key identified by the /key/ [[KeyCode]] enum parameter.
+ CSRAW public static bool GetKeyDown (KeyCode key)
+ {
+ return GetKeyDownInt((int)key);
+ }
+
+ // Returns true during the frame the user releases the key identified by /name/.
+ CSRAW public static bool GetKeyUp (string name)
+ {
+ return GetKeyUpString(name);
+ }
+
+ // Returns true during the frame the user releases the key identified by the /key/ [[KeyCode]] enum parameter.
+ CSRAW public static bool GetKeyUp (KeyCode key)
+ {
+ return GetKeyUpInt((int)key);
+ }
+
+
+ // Returns an array of strings describing the connected joysticks.
+ CONDITIONAL ENABLE_MONO || UNITY_METRO
+ CUSTOM static string[] GetJoystickNames()
+ {
+ std::vector<std::string> names;
+
+ GetJoystickNames(names);
+
+ return Scripting::StringVectorToMono(names);
+ }
+
+ // Returns whether the given mouse button is held down.
+ CUSTOM static bool GetMouseButton (int button)
+ {
+ return GetInputManager ().GetMouseButton (button);
+ }
+
+ // Returns true during the frame the user pressed the given mouse button.
+ CUSTOM static bool GetMouseButtonDown (int button)
+ {
+ return GetInputManager ().GetMouseButtonDown (button);
+ }
+
+ // Returns true during the frame the user releases the given mouse button.
+ CUSTOM static bool GetMouseButtonUp (int button)
+ {
+ return GetInputManager ().GetMouseButtonUp (button);
+ }
+
+ // Resets all input. After ResetInputAxes all axes return to 0 and all buttons return to 0 for one frame.
+ CUSTOM static void ResetInputAxes () { ResetInput(); }
+
+ // The current mouse position in pixel coordinates. (RO)
+ CUSTOM_PROP static Vector3 mousePosition
+ {
+ Vector2f p = GetInputManager ().GetMousePosition();
+ return Vector3f (p.x, p.y, 0.0F);
+ }
+
+
+ CUSTOM_PROP static bool mousePresent
+ {
+ #if UNITY_METRO
+ return GetMousePresent();
+ #else
+ return true;
+ #endif
+ }
+
+ CUSTOM_PROP static bool simulateMouseWithTouches
+ {
+ return GetInputManager ().GetSimulateMouseWithTouches();
+ }
+ {
+ GetInputManager ().SetSimulateMouseWithTouches(value);
+ }
+ // Is any key or mouse button currently held down? (RO)
+ CUSTOM_PROP static bool anyKey { return GetInputManager ().GetAnyKey (); }
+
+ // Returns true the first frame the user hits any key or mouse button. (RO)
+ CUSTOM_PROP static bool anyKeyDown { return GetInputManager ().GetAnyKeyThisFrame (); }
+
+ // Returns the keyboard input entered this frame. (RO)
+ CUSTOM_PROP static string inputString { return scripting_string_new(GetInputManager ().GetInputString ()); }
+
+ // Last measured linear acceleration of a device in three-dimensional space. (RO)
+
+ CUSTOM_PROP static Vector3 acceleration { return GetAcceleration (); }
+
+ // Returns list of acceleration measurements which occurred during the last frame. (RO) (Allocates temporary variables)
+
+ CSRAW public static AccelerationEvent[] accelerationEvents { get {
+ int count = accelerationEventCount;
+ AccelerationEvent[] events = new AccelerationEvent[count];
+ for (int q = 0; q < count; ++q)
+ events[q] = GetAccelerationEvent (q);
+ return events;
+ }
+ }
+
+ // Returns specific acceleration measurement which occurred during last frame. (Does not allocate temporary variables)
+
+ CUSTOM static AccelerationEvent GetAccelerationEvent (int index)
+ {
+ Acceleration acc;
+ if (index >= 0 && index < GetAccelerationCount ())
+ GetAcceleration (index, acc);
+ else
+ Scripting::RaiseMonoException ("Index out of bounds.");
+ return acc;
+ }
+ // Number of acceleration measurements which occurred during last frame.
+
+ CUSTOM_PROP static int accelerationEventCount { return GetAccelerationCount (); }
+
+ // Returns list of objects representing status of all touches during last frame. (RO) (Allocates temporary variables)
+
+ CSRAW public static Touch[] touches { get {
+ int count = touchCount;
+ Touch[] touches = new Touch[count];
+ for (int q = 0; q < count; ++q)
+ touches[q] = GetTouch (q);
+ return touches;
+ }
+ }
+
+ // Returns object representing status of a specific touch. (Does not allocate temporary variables)
+ CUSTOM static Touch GetTouch (int index)
+ {
+ #if ENABLE_NEW_EVENT_SYSTEM
+ if (index >= 0 && index < GetTouchCount ())
+ {
+ Touch* t = GetTouch(index);
+
+ if (t != NULL)
+ {
+ return *t;
+ }
+ else
+ {
+ Scripting::RaiseMonoException ("GetTouch() failed!");
+ }
+ }
+ else
+ {
+ Scripting::RaiseMonoException ("Index specified to GetTouch() is out of bounds! Must be less than Touch.touchCount.");
+ }
+ Touch dummy;
+ return dummy;
+ #else
+ Touch touch;
+
+ if (index >= 0 && index < GetTouchCount ())
+ {
+ if (!GetTouch (index, touch))
+ Scripting::RaiseMonoException ("Internal error.");
+ }
+ else
+ Scripting::RaiseMonoException ("Index out of bounds.");
+ return touch;
+ #endif //ENABLE_NEW_EVENT_SYSTEM
+ }
+
+ // Number of touches. Guaranteed not to change throughout the frame. (RO)
+
+ CUSTOM_PROP static int touchCount { return GetTouchCount (); }
+
+ OBSOLETE warning eatKeyPressOnTextFieldFocus property is deprecated, and only provided to support legacy behavior.
+ // Property indicating whether keypresses are eaten by a textinput if it has focus (default true).
+ CUSTOM_PROP static bool eatKeyPressOnTextFieldFocus { return GetInputManager().GetEatKeyPressOnTextFieldFocus(); } { return GetInputManager().SetEatKeyPressOnTextFieldFocus(value); }
+
+ // Property indicating whether the system handles multiple touches.
+ CONDITIONAL !UNITY_FLASH && !UNITY_WEBGL
+ CUSTOM_PROP static bool multiTouchEnabled { return IsMultiTouchEnabled (); } { return SetMultiTouchEnabled (value); }
+
+ CSRAW private static LocationService locationServiceInstance;
+ // Property for accessing device location (handheld devices only). (RO)
+ CSRAW public static LocationService location { get {
+ if (locationServiceInstance == null)
+ locationServiceInstance = new LocationService ();
+
+ return locationServiceInstance;
+ }
+ }
+
+ CSRAW private static Compass compassInstance;
+ // Property for accessing compass (handheld devices only). (RO)
+ CSRAW public static Compass compass { get {
+ if (compassInstance == null)
+ compassInstance = new Compass();
+ return compassInstance;
+ }
+ }
+
+ // Device physical orientation as reported by OS. (RO)
+ CUSTOM_PROP static DeviceOrientation deviceOrientation { return GetOrientation(); }
+
+
+ OBSOLETE error Use ps3 move API instead
+ CSRAW public static Quaternion GetRotation (int deviceID) { return Quaternion.identity; }
+
+ OBSOLETE error Use ps3 move API instead
+ CSRAW public static Vector3 GetPosition (int deviceID) { return Vector3.zero; }
+
+ // Controls enabling and disabling of IME input composition.
+ CUSTOM_PROP static IMECompositionMode imeCompositionMode
+ { return GetInputManager().GetIMECompositionMode(); }
+ { GetInputManager().SetIMECompositionMode (value); }
+
+ // The current IME composition string being typed by the user.
+ CUSTOM_PROP static string compositionString { return scripting_string_new(GetInputManager ().GetCompositionString ()); }
+
+ // Indicates if the user has an IME keyboard input source selected.
+ CUSTOM_PROP static bool imeIsSelected { return (GetInputManager().GetIMEIsSelected()); }
+
+ // The current text input position used by IMEs to open windows.
+ CUSTOM_PROP static Vector2 compositionCursorPos
+ { return GetInputManager().GetTextFieldCursorPos (); }
+ { GetInputManager().GetTextFieldCursorPos () = value; }
+
+ // Information about the current mouse or touch event, available during OnInput* series of callbacks.
+ //CONDITIONAL ENABLE_NEW_EVENT_SYSTEM
+ //CUSTOM_PROP static Touch currentTouch { return GetInputManager().GetCurrentTouch(); }
+END
+
+CSRAW }
diff --git a/Runtime/Export/UnityEngineInternal/TypeInferenceRuleAttribute.cs b/Runtime/Export/UnityEngineInternal/TypeInferenceRuleAttribute.cs
new file mode 100644
index 0000000..f2c7fc2
--- /dev/null
+++ b/Runtime/Export/UnityEngineInternal/TypeInferenceRuleAttribute.cs
@@ -0,0 +1,52 @@
+using System;
+
+namespace UnityEngineInternal
+{
+ public enum TypeInferenceRules
+ {
+ /// <summary>
+ /// (typeof(T)) as T
+ /// </summary>
+ TypeReferencedByFirstArgument,
+
+ /// <summary>
+ /// (, typeof(T)) as T
+ /// </summary>
+ TypeReferencedBySecondArgument,
+
+ /// <summary>
+ /// (typeof(T)) as (T)
+ /// </summary>
+ ArrayOfTypeReferencedByFirstArgument,
+
+ /// <summary>
+ /// (T) as T
+ /// </summary>
+ TypeOfFirstArgument,
+ }
+
+ /// <summary>
+ /// Adds a special type inference rule to a method.
+ /// </summary>
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Method)]
+ public class TypeInferenceRuleAttribute : Attribute
+ {
+ private readonly string _rule;
+
+ public TypeInferenceRuleAttribute(TypeInferenceRules rule)
+ : this(rule.ToString())
+ {
+ }
+
+ public TypeInferenceRuleAttribute(string rule)
+ {
+ _rule = rule;
+ }
+
+ public override string ToString()
+ {
+ return _rule;
+ }
+ }
+} \ No newline at end of file
diff --git a/Runtime/Export/UnityEngineInternal/WrappedTypes.cs b/Runtime/Export/UnityEngineInternal/WrappedTypes.cs
new file mode 100644
index 0000000..54c2444
--- /dev/null
+++ b/Runtime/Export/UnityEngineInternal/WrappedTypes.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+namespace UnityEngineInternal
+{
+ // This is a solution to problem where we cannot use:
+ // * System.Collections.Stack on WP8 and Metro, because it was stripped from .NET
+ // * System.Collections.Generic.Stack cannot use on iOS because it creates a dependency to System.dll thus increasing overall executable size
+#if UNITY_WINRT
+ public class GenericStack : Stack<Object>
+ {
+
+ }
+#else
+ public class GenericStack : System.Collections.Stack
+ {
+
+ }
+#endif
+
+} \ No newline at end of file
diff --git a/Runtime/Export/UnityEngineLight.txt b/Runtime/Export/UnityEngineLight.txt
new file mode 100644
index 0000000..5880e58
--- /dev/null
+++ b/Runtime/Export/UnityEngineLight.txt
@@ -0,0 +1,133 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Camera/Light.h"
+#include "Runtime/Camera/Skybox.h"
+#include "Runtime/Graphics/LightmapSettings.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Camera/IntermediateRenderer.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Filters/Misc/TextMesh.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Camera/LightManager.h"
+#include "Runtime/Misc/QualitySettings.h"
+#include "Runtime/Camera/RenderSettings.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+
+#include <list>
+#include <vector>
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+// Script interface for [[wiki:class-Light|light components]].
+CLASS Light : Behaviour
+ // The type of the light.
+ AUTO_PROP LightType type GetType SetType
+
+ // The color of the light.
+ AUTO_PROP Color color GetColor SetColor
+
+ // The Intensity of a light is multiplied with the Light color.
+ AUTO_PROP float intensity GetIntensity SetIntensity
+
+ // How this light casts shadows?
+ AUTO_PROP LightShadows shadows GetShadows SetShadows
+
+
+ // Strength of light's shadows
+ AUTO_PROP float shadowStrength GetShadowStrength SetShadowStrength
+
+
+ // Shadow mapping bias
+ AUTO_PROP float shadowBias GetShadowBias SetShadowBias
+
+
+ // Softness of directional light's soft shadows
+ AUTO_PROP float shadowSoftness GetShadowSoftness SetShadowSoftness
+
+
+ // Fadeout speed of directional light's soft shadows
+ AUTO_PROP float shadowSoftnessFade GetShadowSoftnessFade SetShadowSoftnessFade
+
+
+
+ // The range of the light.
+ AUTO_PROP float range GetRange SetRange
+
+ // The angle of the light's spotlight cone in degrees.
+ AUTO_PROP float spotAngle GetSpotAngle SetSpotAngle
+
+ AUTO_PROP float cookieSize GetCookieSize SetCookieSize
+
+ // The cookie texture projected by the light.
+ AUTO_PTR_PROP Texture cookie GetCookie SetCookie
+
+ // The [[wiki:class-Flare|flare asset]] to use for this light.
+ AUTO_PTR_PROP Flare flare GetFlare SetFlare
+
+ // How to render the light.
+ AUTO_PROP LightRenderMode renderMode GetRenderMode SetRenderMode
+
+ // Has the light been already lightmapped.
+ AUTO_PROP bool alreadyLightmapped GetActuallyLightmapped SetActuallyLightmapped
+
+
+ // This is used to light certain objects in the scene selectively.
+ AUTO_PROP int cullingMask GetCullingMask SetCullingMask
+
+ // The size of the area light. Editor only.
+ CONDITIONAL UNITY_EDITOR
+ AUTO_PROP Vector2 areaSize GetAreaSize SetAreaSize
+
+ FLUSHCONDITIONS
+
+ CSRAW
+#if UNITY_EDITOR
+ OBSOLETE warning Use QualitySettings.pixelLightCount instead.
+#endif
+ CUSTOM_PROP static int pixelLightCount { return GetQualitySettings().GetCurrent().pixelLightCount; } { GetQualitySettings().SetPixelLightCount(value); }
+
+ //*undocumented For terrain engine only
+ CUSTOM public static Light[] GetLights (LightType type, int layer)
+ {
+ UNITY_TEMP_VECTOR(Light*) lightsvector;
+
+ layer = 1 << layer;
+ LightManager::Lights& lights = GetLightManager().GetAllLights();
+ for (LightManager::Lights::iterator i=lights.begin();i != lights.end();i++)
+ {
+ Light* light = &*i;
+ if (!light)
+ continue;
+ if (light->GetType() == type && (light->GetCullingMask() & layer) != 0)
+ lightsvector.push_back(light);
+ }
+
+ return CreateScriptingArrayFromUnityObjects(lightsvector,Scripting::ClassIDToScriptingType(ClassID(Light)));
+ }
+
+ OBSOLETE error light.shadowConstantBias was removed, use light.shadowBias
+ CSRAW public float shadowConstantBias { get { return 0.0f; } set {} }
+
+ OBSOLETE error light.shadowObjectSizeBias was removed, use light.shadowBias
+ CSRAW public float shadowObjectSizeBias { get { return 0.0f; } set {} }
+
+ OBSOLETE error light.attenuate was removed; all lights always attenuate now
+ CSRAW public bool attenuate { get { return true; } set {} }
+
+END
+
+
+CSRAW }
diff --git a/Runtime/Export/UnityEngineMonoBehaviour.txt b/Runtime/Export/UnityEngineMonoBehaviour.txt
new file mode 100644
index 0000000..69028db
--- /dev/null
+++ b/Runtime/Export/UnityEngineMonoBehaviour.txt
@@ -0,0 +1,350 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Scripting/DelayedCallUtility.h"
+
+using namespace Unity;
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using UnityEngineInternal;
+
+namespace UnityEngine
+{
+
+// MonoBehaviour is the base class every script derives from.
+CSRAW
+NONSEALED_CLASS MonoBehaviour : Behaviour
+
+ //*undocumented*
+ THREAD_SAFE
+ CONDITIONAL !UNITY_FLASH && !UNITY_WEBGL && !UNITY_WINRT
+ CUSTOM public MonoBehaviour ()
+ {
+ #if UNITY_EDITOR
+ if (self.GetInstanceID() == 0)
+ {
+ ScriptWarning ("You are trying to create a MonoBehaviour using the 'new' keyword. This is not allowed. MonoBehaviours can only be added using AddComponent(). Alternatively, your script can inherit from ScriptableObject or no base class at all", NULL);
+ }
+ #endif
+ }
+
+ // Update is called every frame, if the MonoBehaviour is enabled.
+ CSNONE void Update ();
+
+
+ // LateUpdate is called every frame, if the [[Behaviour]] is enabled.
+ CSNONE void LateUpdate ();
+
+
+ // This function is called every fixed framerate frame, if the MonoBehaviour is enabled.
+ CSNONE void FixedUpdate ();
+
+
+ // Awake is called when the script instance is being loaded.
+ CSNONE void Awake ();
+
+
+ // Start is called just before any of the Update methods is called the first time.
+ CSNONE void Start ();
+
+
+ // Reset to default values.
+ CSNONE void Reset ();
+
+
+ // OnMouseEnter is called when the mouse entered the [[GUIElement]] or [[Collider]].
+ CSNONE void OnMouseEnter ();
+
+
+ // OnMouseOver is called every frame while the mouse is over the [[GUIElement]] or [[Collider]].
+ CSNONE void OnMouseOver ();
+
+
+ // OnMouseExit is called when the mouse is not any longer over the [[GUIElement]] or [[Collider]].
+ CSNONE void OnMouseExit ();
+
+
+ // OnMouseDown is called when the user has pressed the mouse button while over the [[GUIElement]] or [[Collider]].
+ CSNONE void OnMouseDown ();
+
+ // OnMouseUp is called when the user has released the mouse button.
+ CSNONE void OnMouseUp ();
+
+
+ // OnMouseUpAsButton is only called when the mouse is released over the same [[GUIElement]] or [[Collider]] as it was pressed.
+ CSNONE void OnMouseUpAsButton ();
+
+
+ // OnMouseDrag is called when the user has clicked on a [[GUIElement]] or [[Collider]] and is still holding down the mouse.
+ CSNONE void OnMouseDrag ();
+
+ // OnTriggerEnter is called when the [[Collider]] /other/ enters the [[class-BoxCollider|trigger]].
+ CSNONE void OnTriggerEnter (Collider other);
+
+ // OnTriggerExit is called when the [[Collider]] /other/ has stopped touching the [[class-BoxCollider|trigger]].
+ CSNONE void OnTriggerExit (Collider other);
+
+ // OnTriggerStay is called once per frame for every [[Collider]] /other/ that is touching the [[class-BoxCollider|trigger]].
+ CSNONE void OnTriggerStay (Collider other);
+
+ // OnCollisionEnter is called when this collider/rigidbody has begun touching another rigidbody/collider.
+ CSNONE void OnCollisionEnter (Collision collisionInfo);
+
+ // OnCollisionExit is called when this collider/rigidbody has stopped touching another rigidbody/collider.
+ CSNONE void OnCollisionExit (Collision collisionInfo);
+
+ // OnCollisionStay is called once per frame for every collider/rigidbody that is touching rigidbody/collider.
+ CSNONE void OnCollisionStay (Collision collisionInfo);
+
+ // OnControllerColliderHit is called when the controller hits a collider while performing a Move.
+ CSNONE void OnControllerColliderHit (ControllerColliderHit hit);
+
+ // Called when a joint attached to the same game object broke.
+ CSNONE void OnJointBreak (float breakForce);
+
+#if ENABLE_2D_PHYSICS
+ // OnCollisionEnter2D is called when this [[Collider2D]] has begun touching another [[Collider2D]].
+ CSNONE void OnCollisionEnter2D (Collision2D info);
+
+ // OnCollisionStay2D is called once per frame for this [[Collider2D]] if it is continuing to touch another [[Collider2D]].
+ CSNONE void OnCollisionStay2D (Collision2D info);
+
+ // OnCollisionExit2D is called when this [[Collider2D]] has stopped touching another [[Collider2D]].
+ CSNONE void OnCollisionExit2D (Collision2D info);
+
+ // OnTriggerEnter2D is called when a [[Collider2D]] has begun touching another [[Collider2D]] configured as a trigger.
+ CSNONE void OnTriggerEnter2D (Collider2D other);
+
+ // OnTriggerStay2D is called once per frame for a [[Collider2D]] if it is continuing to touch another [[Collider2D]] configured as a trigger.
+ CSNONE void OnTriggerStay2D (Collider2D other);
+
+ // OnTriggerExit2D is called when a [[Collider2D]] has stopped touching another [[Collider2D]] configured as a trigger.
+ CSNONE void OnTriggerExit2D (Collider2D other);
+#endif
+
+ // OnParticleCollision is called when a particle hits a collider (legacy particle system only).
+ CSNONE void OnParticleCollision (GameObject other);
+
+ // OnBecameVisible is called when the renderer became visible by any camera.
+ CSNONE void OnBecameVisible();
+
+ // OnBecameInvisible is called when the renderer is no longer visible by any camera.
+ CSNONE void OnBecameInvisible();
+
+ // This function is called after a new level was loaded.
+ CSNONE void OnLevelWasLoaded (int level);
+
+ // This function is called when the object becomes enabled and active.
+ CSNONE void OnEnable ();
+
+
+ // This function is called when the behaviour becomes disabled () or inactive.
+ CSNONE void OnDisable();
+
+
+ // This function is called when the script is loaded or a value is changed in the inspector, use it to validate your data.
+ CSNONE void OnValidate ();
+
+
+ // This function is called when the MonoBehaviour will be destroyed.
+ CSNONE void OnDestroy();
+
+
+ // OnPreCull is called before a camera culls the scene.
+ CSNONE void OnPreCull ();
+
+ // OnPreRender is called before a camera starts rendering the scene.
+ CSNONE void OnPreRender ();
+
+ // OnPostRender is called after a camera finished rendering the scene.
+ CSNONE void OnPostRender ();
+
+
+ // OnRenderObject is called after camera has rendered the scene.
+ CSNONE void OnRenderObject ();
+
+
+ // OnWillRenderObject is called once for each camera if the object is visible.
+ CSNONE void OnWillRenderObject ();
+
+ // OnGUI is called for rendering and handling GUI events.
+ CSNONE void OnGUI ();
+
+ // OnRenderImage is called after all rendering is complete to render image
+ CSNONE void OnRenderImage (RenderTexture source, RenderTexture destination);
+
+
+ // Implement this OnDrawGizmosSelected if you want to draw gizmos only if the object is selected.
+ CSNONE void OnDrawGizmosSelected ();
+
+ // Implement this OnDrawGizmos if you want to draw gizmos that are also pickable and always drawn.
+ CSNONE void OnDrawGizmos ();
+
+ // Sent to all game objects when the player pauses.
+ CSNONE void OnApplicationPause(bool pause);
+
+ // Sent to all game objects when the player gets or loses focus.
+ CSNONE void OnApplicationFocus(bool focus);
+
+ // Sent to all game objects before the application is quit.
+ CSNONE void OnApplicationQuit ();
+
+ // Called on the server whenever a new player has successfully connected.
+ CSNONE void OnPlayerConnected (NetworkPlayer player);
+
+ // Called on the server whenever a Network.InitializeServer was invoked and has completed.
+ CSNONE void OnServerInitialized ();
+
+ // Called on the client when you have successfully connected to a server
+ CSNONE void OnConnectedToServer ();
+
+ // Called on the server whenever a player disconnected from the server.
+ CSNONE void OnPlayerDisconnected (NetworkPlayer player);
+
+ // Called on the client when the connection was lost or you disconnected from the server.
+ CSNONE void OnDisconnectedFromServer (NetworkDisconnection mode);
+
+ // Called on the client when a connection attempt fails for some reason.
+ CSNONE void OnFailedToConnect (NetworkConnectionError error);
+
+ // Called on clients or servers when there is a problem connecting to the MasterServer.
+ CSNONE void OnFailedToConnectToMasterServer (NetworkConnectionError error);
+
+ // Called on clients or servers when reporting events from the MasterServer.
+ CSNONE void OnMasterServerEvent (MasterServerEvent msEvent);
+
+ // Called on objects which have been network instantiated with Network.Instantiate
+ CSNONE void OnNetworkInstantiate (NetworkMessageInfo info);
+
+ // Used to customize synchronization of variables in a script watched by a network view.
+ CSNONE void OnSerializeNetworkView (BitStream stream, NetworkMessageInfo info);
+
+ // If OnAudioFilterRead is implemented, Unity will insert a custom filter into the audio DSP chain.
+ CSNONE void OnAudioFilterRead ();
+
+
+
+ // Callback for processing animation movements for modifying root motion.
+ CSNONE void OnAnimatorMove ();
+
+ // Callback for setting up animation IK (inverse kinematics)
+ CSNONE void OnAnimatorIK (int layerIndex);
+
+ CUSTOM private void Internal_CancelInvokeAll ()
+ {
+ CancelInvoke (*self);
+ }
+
+ CUSTOM private bool Internal_IsInvokingAll ()
+ {
+ return IsInvoking (*self);
+ }
+
+ // Invokes the method /methodName/ in time seconds.
+ CUSTOM void Invoke (string methodName, float time)
+ {
+ InvokeDelayed (*self, methodName, time, 0.0F);
+ }
+
+ // Invokes the method /methodName/ in /time/ seconds.
+ CUSTOM void InvokeRepeating (string methodName, float time, float repeatRate)
+ {
+ InvokeDelayed (*self, methodName, time, repeatRate);
+ }
+
+
+ // Cancels all Invoke calls on this MonoBehaviour.
+ CSRAW public void CancelInvoke ()
+ {
+ Internal_CancelInvokeAll ();
+ }
+
+ // Cancels all Invoke calls with name /methodName/ on this behaviour.
+ CUSTOM void CancelInvoke (string methodName) { CancelInvoke (*self, methodName); }
+
+
+ // Is any invoke on /methodName/ pending?
+ CUSTOM bool IsInvoking (string methodName) { return IsInvoking (*self, methodName); }
+
+ // Is any invoke pending on this MonoBehaviour?
+ CSRAW public bool IsInvoking ()
+ {
+ return Internal_IsInvokingAll ();
+ }
+
+ // Starts a coroutine.
+ CSRAW public Coroutine StartCoroutine (IEnumerator routine)
+ {
+ return StartCoroutine_Auto(routine);
+ }
+ //*undocumented*
+
+ CUSTOM Coroutine StartCoroutine_Auto (IEnumerator routine)
+ {
+ Scripting::RaiseIfNull(routine);
+ if (!self->IsInstanceIDCreated () || !self->IsDerivedFrom (ClassID (MonoBehaviour)))
+ Scripting::RaiseArgumentException ("Coroutines can only be started from a MonoBehaviour");
+ return self->StartCoroutineManaged2(routine);
+ }
+
+
+ // Starts a coroutine named /methodName/.
+ CUSTOM Coroutine StartCoroutine (string methodName, object value = null)
+ {
+ Scripting::RaiseIfNull((void*)(!methodName.IsNull()));
+ if (!self->IsInstanceIDCreated () || !self->IsDerivedFrom (ClassID (MonoBehaviour)))
+ Scripting::RaiseArgumentException ("Coroutines can only be started from a MonoBehaviour");
+ char* cString = ScriptingStringToAllocatedChars (methodName);
+ ScriptingObjectPtr coroutine = self->StartCoroutineManaged (cString, value);
+ ScriptingStringToAllocatedChars_Free (cString);
+ return coroutine;
+ }
+
+ // Stops all coroutines named /methodName/ running on this behaviour.
+ CUSTOM void StopCoroutine (string methodName) { self->StopCoroutine(methodName.AsUTF8().c_str()); }
+
+
+ // Stops all coroutines running on this behaviour.
+ CUSTOM void StopAllCoroutines () { self->StopAllCoroutines(); }
+
+ // Logs message to the Unity Console. This function is identical to [[Debug.Log]].
+ CSRAW public static void print (object message)
+ {
+ Debug.Log (message);
+ }
+
+ // Disabling this lets you skip the GUI layout phase.
+ CONDITIONAL !UNITY_WEBGL
+ AUTO_PROP bool useGUILayout GetUseGUILayout SetUseGUILayout
+
+ CSRAW #if UNITY_WINRT
+ CSRAW private static FastCallExceptionHandler _fastCallExceptionHandler;
+
+ CSRAW internal static void SetupFastCallExceptionHandler(FastCallExceptionHandler handler)
+ {
+ _fastCallExceptionHandler = handler;
+ }
+
+ CSRAW protected static void HandleFastCallException(Exception ex)
+ {
+ if (_fastCallExceptionHandler != null)
+ _fastCallExceptionHandler(ex);
+ else throw ex;
+ }
+ CSRAW #endif
+
+END
+
+
+
+CSRAW }
+
diff --git a/Runtime/Export/UnityEngineObject.txt b/Runtime/Export/UnityEngineObject.txt
new file mode 100644
index 0000000..2700188
--- /dev/null
+++ b/Runtime/Export/UnityEngineObject.txt
@@ -0,0 +1,226 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Misc/SaveAndLoadHelper.h"
+#include "Runtime/GameCode/CloneObject.h"
+#include "Runtime/Misc/Player.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Scripting.h"
+
+
+#if UNITY_WII
+ #include "PlatformDependent/Wii/WiiUtility.h"
+#endif
+
+using namespace Unity;
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using UnityEngineInternal;
+
+
+namespace UnityEngine
+{
+
+
+
+
+BEGIN DOC
+Base class for all objects Unity can reference.
+Any public variable you make that derives from Object gets shown in the inspector as a drop target,
+allowing you to set the value from the GUI.
+END DOC
+
+CSRAW
+[StructLayout (LayoutKind.Sequential)]
+NONSEALED_CLASS Object
+ CSRAW
+#if !UNITY_FLASH && !UNITY_WEBGL && !UNITY_WINRT
+ private ReferenceData m_UnityRuntimeReferenceData;
+
+ #if UNITY_EDITOR
+ private string m_UnityRuntimeErrorString;
+ #endif
+#else //UNITY_FLASH
+ //*undocumented*
+ [NotRenamed]
+ public int m_InstanceID;
+ //*undocumented*
+ [NotRenamed]
+ public int m_CachedPtr;
+#endif
+
+ public override bool Equals(object o) { return CompareBaseObjects (this, o as Object); }
+ public override int GetHashCode() { return GetInstanceID(); }
+
+ // Does the object exist?
+ public static implicit operator bool (Object exists)
+ {
+ return !CompareBaseObjects (exists, null);
+ }
+
+ CSRAW private static bool CompareBaseObjects (Object lhs, Object rhs)
+ {
+ #if UNITY_WINRT
+ return UnityEngineInternal.ScriptingUtils.CompareBaseObjects(lhs, rhs);
+ #else
+ return CompareBaseObjectsInternal (lhs, rhs);
+ #endif
+ }
+
+ CONSTRUCTOR_SAFE
+ CUSTOM private static bool CompareBaseObjectsInternal ([Writable]Object lhs, [Writable]Object rhs)
+ {
+ return Scripting::CompareBaseObjects (lhs.GetScriptingObject(), rhs.GetScriptingObject());
+ }
+
+ // Returns the instance id of the object.
+ CSRAW [NotRenamed]
+ CSRAW public int GetInstanceID ()
+ {
+#if !UNITY_FLASH && !UNITY_WEBGL && !UNITY_WINRT
+ return m_UnityRuntimeReferenceData.instanceID;
+#else
+ return m_InstanceID;
+#endif
+ }
+
+ CUSTOM private static Object Internal_CloneSingle (Object data)
+ {
+ DISALLOW_IN_CONSTRUCTOR
+ return Scripting::ScriptingWrapperFor (&CloneObject (*data));
+ }
+ CUSTOM private static Object Internal_InstantiateSingle (Object data, Vector3 pos, Quaternion rot)
+ {
+ DISALLOW_IN_CONSTRUCTOR
+ Object* obj = &InstantiateObject (*data, pos, rot);
+ return Scripting::ScriptingWrapperFor(obj);
+ }
+
+
+ // Clones the object /original/ and returns the clone.
+
+
+ CSRAW
+ [TypeInferenceRule(TypeInferenceRules.TypeOfFirstArgument)]
+ CSRAW public static Object Instantiate (Object original, Vector3 position, Quaternion rotation)
+ {
+ CheckNullArgument(original,"The prefab you want to instantiate is null.");
+ return Internal_InstantiateSingle (original, position, rotation);
+ }
+
+
+ // Clones the object /original/ and returns the clone.
+ CSRAW
+ [TypeInferenceRule(TypeInferenceRules.TypeOfFirstArgument)]
+ public static Object Instantiate (Object original)
+ {
+ CheckNullArgument(original,"The thing you want to instantiate is null.");
+ return Internal_CloneSingle (original);
+ }
+ #if ENABLE_GENERICS_BUT_CURRENTLY_DISABLED
+ // Generic version. See the [[wiki:Generic Functions|Generic Functions]] page for more details.
+ public static T Instantiate<T>(T original) where T : UnityEngine.Object
+ {
+ CheckNullArgument(original,"The thing you want to instantiate is null.");
+ return (T)Internal_CloneSingle (original);
+ }
+ #endif
+
+ static private void CheckNullArgument(object arg, string message)
+ {
+ if (arg==null)
+ throw new ArgumentException(message);
+ }
+
+ // Removes a gameobject, component or asset.
+ CSRAW
+ CUSTOM static void Destroy (Object obj, float t = 0.0F) { DISALLOW_IN_CONSTRUCTOR Scripting::DestroyObjectFromScripting (obj, t); }
+
+
+ // Destroys the object /obj/ immediately. It is strongly recommended to use Destroy instead.
+ CSRAW
+ CUSTOM static void DestroyImmediate (Object obj, bool allowDestroyingAssets = false) { Scripting::DestroyObjectFromScriptingImmediate (obj, allowDestroyingAssets); }
+
+ // Returns a list of all active loaded objects of Type /type/.
+ CSRAW [TypeInferenceRule(TypeInferenceRules.ArrayOfTypeReferencedByFirstArgument)]
+ CUSTOM static Object[] FindObjectsOfType (Type type) { DISALLOW_IN_CONSTRUCTOR return Scripting::FindObjectsOfType (type, Scripting::kFindActiveSceneObjects); }
+
+ CONDITIONAL (ENABLE_GENERICS && !UNITY_FLASH)
+ CSRAW public static T[] FindObjectsOfType<T> () where T: Object
+ {
+ return Resources.ConvertObjects<T> (FindObjectsOfType (typeof (T)));
+ }
+
+ // Returns the first active loaded object of Type /type/.
+ CSRAW
+ [TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
+ CSRAW public static Object FindObjectOfType (Type type)
+ {
+ Object[] objects = FindObjectsOfType (type);
+ if (objects.Length > 0)
+ return objects[0];
+ else
+ return null;
+ }
+
+ CONDITIONAL (ENABLE_GENERICS && !UNITY_FLASH)
+ CSRAW public static T FindObjectOfType<T> () where T: Object
+ {
+ return (T)FindObjectOfType (typeof (T));
+ }
+
+ // Compares if two objects refer to the same
+ CSRAW public static bool operator == (Object x, Object y) { return CompareBaseObjects (x, y); }
+
+ // Compares if two objects refer to a different object
+ CSRAW public static bool operator != (Object x, Object y) { return !CompareBaseObjects (x, y); }
+
+ // The name of the object.
+ CUSTOM_PROP string name { return scripting_string_new(self->GetName ()); } { self->SetName ( value.AsUTF8().c_str()); }
+
+ // Makes the object /target/ not be destroyed automatically when loading a new scene.
+ CUSTOM static void DontDestroyOnLoad (Object target)
+ {
+ Object* o = target;
+ if (o)
+ DontDestroyOnLoad (*o);
+ }
+
+ // Should the object be hidden, saved with the scene or modifiable by the user?
+ CONSTRUCTOR_SAFE
+ AUTO_PROP HideFlags hideFlags GetHideFlags SetHideFlags
+
+ //*undocumented* deprecated
+ // We cannot properly deprecate this in C# right now, since the optional parameter creates
+ // another method calling this, which creates compiler warnings when deprecated.
+ // OBSOLETE warning use Object.Destroy instead.
+ CUSTOM static void DestroyObject (Object obj, float t = 0.0F) { Scripting::DestroyObjectFromScripting (obj, t); }
+
+ //*undocumented* DEPRECATED
+ OBSOLETE warning use Object.FindObjectsOfType instead.
+ CUSTOM static Object[] FindSceneObjectsOfType (Type type) { DISALLOW_IN_CONSTRUCTOR return Scripting::FindObjectsOfType (type, Scripting::kFindActiveSceneObjects); }
+
+ //*undocumented* DEPRECATED
+ OBSOLETE warning use Resources.FindObjectsOfTypeAll instead.
+ CUSTOM static Object[] FindObjectsOfTypeIncludingAssets (Type type) { DISALLOW_IN_CONSTRUCTOR return Scripting::FindObjectsOfType (type, Scripting::kFindAssets); }
+
+ CONDITIONAL ENABLE_MONO
+ OBSOLETE warning Please use Resources.FindObjectsOfTypeAll instead
+ CSRAW public static Object[] FindObjectsOfTypeAll (Type type) { return Resources.FindObjectsOfTypeAll (type); }
+
+ // Returns the name of the game object.
+ CUSTOM public override string ToString() {
+ return scripting_string_new(GetSafeString(BaseObject, UnityObjectToString (self)));
+ }
+
+END
+CSRAW }
diff --git a/Runtime/Export/UnityEngineRandom.txt b/Runtime/Export/UnityEngineRandom.txt
new file mode 100644
index 0000000..a5ab72f
--- /dev/null
+++ b/Runtime/Export/UnityEngineRandom.txt
@@ -0,0 +1,70 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include <ctime>
+
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using UnityEngineInternal;
+
+namespace UnityEngine
+{
+
+// Class for generating random data.
+CLASS Random
+
+ C++RAW
+ Rand gScriptingRand (time(NULL));
+
+ // Sets the seed for the random number generator.
+ CUSTOM_PROP static int seed { return gScriptingRand.GetSeed(); } { gScriptingRand.SetSeed(value); }
+
+ // Returns a random float number between and /min/ [inclusive] and /max/ [inclusive] (RO).
+ CUSTOM static float Range (float min, float max) { return RangedRandom (gScriptingRand, min, max); }
+
+ // Returns a random integer number between /min/ [inclusive] and /max/ [exclusive] (RO).
+ CSRAW public static int Range (int min, int max) { return RandomRangeInt (min, max); }
+
+ CUSTOM private static int RandomRangeInt (int min, int max) { return RangedRandom (gScriptingRand, min, max); }
+
+ // Returns a random number between 0.0 [inclusive] and 1.0 [inclusive] (RO).
+ CUSTOM_PROP static float value { return Random01 (gScriptingRand); }
+
+ // Returns a random point inside a sphere with radius 1 (RO).
+ CUSTOM_PROP static Vector3 insideUnitSphere { return RandomPointInsideUnitSphere (gScriptingRand); }
+
+ // Workaround for gcc/msvc where passing small mono structures by value does not work
+ CUSTOM private static void GetRandomUnitCircle (out Vector2 output)
+ {
+ *output = RandomPointInsideUnitCircle (gScriptingRand);
+ }
+
+ // Returns a random point inside a circle with radius 1 (RO).
+ CSRAW public static Vector2 insideUnitCircle { get { Vector2 r; GetRandomUnitCircle(out r); return r; } }
+
+ // Returns a random point on the surface of a sphere with radius 1 (RO).
+ CUSTOM_PROP static Vector3 onUnitSphere { return RandomUnitVector (gScriptingRand); }
+
+ // Returns a random rotation (RO).
+ CUSTOM_PROP static Quaternion rotation { return RandomQuaternion (gScriptingRand); }
+
+ // Returns a random rotation with uniform distribution(RO).
+ CUSTOM_PROP static Quaternion rotationUniform { return RandomQuaternionUniformDistribution (gScriptingRand); }
+
+
+ OBSOLETE warning Use Random.Range instead
+ CSRAW public static float RandomRange (float min, float max) { return Range (min, max); }
+ OBSOLETE warning Use Random.Range instead
+ CSRAW public static int RandomRange (int min, int max) { return Range (min, max); }
+END
+
+
+CSRAW }
diff --git a/Runtime/Export/UnityEngineTime.txt b/Runtime/Export/UnityEngineTime.txt
new file mode 100644
index 0000000..9119d4b
--- /dev/null
+++ b/Runtime/Export/UnityEngineTime.txt
@@ -0,0 +1,65 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include <ctime>
+
+using namespace Unity;
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using UnityEngineInternal;
+
+namespace UnityEngine
+{
+
+// The interface to get time information from Unity.
+CLASS Time
+
+ // The time this frame has started (RO). This is the time in seconds since the start of the game.
+ CUSTOM_PROP static float time { return GetTimeManager ().GetCurTime (); }
+
+ // The time this frame has started (RO). This is the time in seconds since the last level has been loaded.
+ CUSTOM_PROP static float timeSinceLevelLoad { return GetTimeManager ().GetTimeSinceLevelLoad (); }
+
+ // The time in seconds it took to complete the last frame (RO).
+ CUSTOM_PROP static float deltaTime { return GetTimeManager ().GetDeltaTime (); }
+
+ // The time the latest MonoBehaviour::pref::FixedUpdate has started (RO). This is the time in seconds since the start of the game.
+ CUSTOM_PROP static float fixedTime { return GetTimeManager ().GetFixedTime (); }
+
+
+ // The interval in seconds at which physics and other fixed frame rate updates (like MonoBehaviour's MonoBehaviour::pref::FixedUpdate) are performed.
+ CUSTOM_PROP static float fixedDeltaTime { return GetTimeManager ().GetFixedDeltaTime (); } { GetTimeManager ().SetFixedDeltaTime (value); }
+
+ // The maximum time a frame can take. Physics and other fixed frame rate updates (like MonoBehaviour's MonoBehaviour::pref::FixedUpdate)
+ CUSTOM_PROP static float maximumDeltaTime { return GetTimeManager ().GetMaximumDeltaTime (); } { GetTimeManager ().SetMaximumDeltaTime (value); }
+
+ // A smoothed out Time.deltaTime (RO).
+ CUSTOM_PROP static float smoothDeltaTime { return GetTimeManager ().GetSmoothDeltaTime (); }
+
+ // The scale at which the time is passing. This can be used for slow motion effects.
+ CUSTOM_PROP static float timeScale { return GetTimeManager ().GetTimeScale (); } { GetTimeManager ().SetTimeScale (value); }
+
+ // The total number of frames that have passed (RO).
+ CUSTOM_PROP static int frameCount { return GetTimeManager ().GetFrameCount (); }
+
+ //*undocumented*
+ CUSTOM_PROP static int renderedFrameCount { return GetTimeManager ().GetRenderFrameCount (); }
+
+ // The real time in seconds since the game started (RO).
+ CUSTOM_PROP static float realtimeSinceStartup { return GetTimeManager().GetRealtime (); }
+
+ // If /captureFramerate/ is set to a value larger than 0, time will advance in
+ CUSTOM_PROP static int captureFramerate { return GetTimeManager().GetCaptureFramerate(); } { GetTimeManager().SetCaptureFramerate (value); }
+END
+
+CSRAW }
+
diff --git a/Runtime/Export/UnityEngineTransform.txt b/Runtime/Export/UnityEngineTransform.txt
new file mode 100644
index 0000000..f9eb906
--- /dev/null
+++ b/Runtime/Export/UnityEngineTransform.txt
@@ -0,0 +1,302 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Scripting/Scripting.h"
+
+using namespace Unity;
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using UnityEngineInternal;
+
+namespace UnityEngine
+{
+
+// Position, rotation and scale of an object.
+CSRAW
+CLASS Transform : Component, IEnumerable
+
+ CSRAW private Transform () { }
+
+ // The position of the transform in world space.
+ CSRAW
+ AUTO_PROP Vector3 position GetPosition SetPosition
+
+ // Position of the transform relative to the parent transform.
+ CSRAW
+ AUTO_PROP Vector3 localPosition GetLocalPosition SetLocalPosition
+
+ // The rotation as Euler angles in degrees.
+ CSRAW public Vector3 eulerAngles { get { return rotation.eulerAngles; } set { rotation = Quaternion.Euler(value); } }
+
+ // The rotation as Euler angles in degrees relative to the parent transform's rotation.
+ AUTO_PROP Vector3 localEulerAngles GetLocalEulerAngles SetLocalEulerAngles
+
+ // The red axis of the transform in world space.
+ CSRAW public Vector3 right { get { return rotation * Vector3.right; } set { rotation = Quaternion.FromToRotation(Vector3.right, value); } }
+
+ // The green axis of the transform in world space.
+ CSRAW public Vector3 up { get { return rotation * Vector3.up; } set { rotation = Quaternion.FromToRotation(Vector3.up, value); } }
+
+ // The blue axis of the transform in world space.
+ CSRAW public Vector3 forward { get { return rotation * Vector3.forward; } set { rotation = Quaternion.LookRotation(value); } }
+
+ // The rotation of the transform in world space stored as a [[Quaternion]].
+ AUTO_PROP Quaternion rotation GetRotation SetRotationSafe
+
+ // The rotation of the transform relative to the parent transform's rotation.
+ AUTO_PROP Quaternion localRotation GetLocalRotation SetLocalRotationSafe
+
+ // The scale of the transform relative to the parent.
+ AUTO_PROP Vector3 localScale GetLocalScale SetLocalScale
+
+ // The parent of the transform.
+ CSRAW
+ AUTO_PTR_PROP Transform parent GetParent SetParent
+
+ // Matrix that transforms a point from world space into local space (RO).
+ AUTO_PROP Matrix4x4 worldToLocalMatrix GetWorldToLocalMatrix
+ // Matrix that transforms a point from local space into world space (RO).
+ AUTO_PROP Matrix4x4 localToWorldMatrix GetLocalToWorldMatrix
+
+ // Moves the transform in the direction and distance of /translation/.
+ CSRAW public void Translate (Vector3 translation, Space relativeTo = Space.Self)
+ {
+ if (relativeTo == Space.World)
+ position += translation;
+ else
+ position += TransformDirection (translation);
+ }
+
+ // Moves the transform by /x/ along the x axis, /y/ along the y axis, and /z/ along the z axis.
+ CSRAW public void Translate (float x, float y, float z, Space relativeTo = Space.Self)
+ {
+ Translate (new Vector3 (x, y, z), relativeTo);
+ }
+
+ // Moves the transform in the direction and distance of /translation/.
+ CSRAW public void Translate (Vector3 translation, Transform relativeTo)
+ {
+ if (relativeTo)
+ position += relativeTo.TransformDirection (translation);
+ else
+ position += translation;
+ }
+
+ // Moves the transform by /x/ along the x axis, /y/ along the y axis, and /z/ along the z axis.
+ CSRAW public void Translate (float x, float y, float z, Transform relativeTo)
+ {
+ Translate (new Vector3 (x, y, z), relativeTo);
+ }
+
+ // Applies a rotation of /eulerAngles.z/ degrees around the z axis, /eulerAngles.x/ degrees around the x axis, and /eulerAngles.y/ degrees around the y axis (in that order).
+ CSRAW public void Rotate (Vector3 eulerAngles, Space relativeTo = Space.Self)
+ {
+ Quaternion eulerRot = Quaternion.Euler (eulerAngles.x, eulerAngles.y, eulerAngles.z);
+ if (relativeTo == Space.Self)
+ localRotation = localRotation * eulerRot;
+ else
+ {
+ rotation = rotation * (Quaternion.Inverse (rotation) * eulerRot * rotation);
+ }
+ }
+
+ // Applies a rotation of /zAngle/ degrees around the z axis, /xAngle/ degrees around the x axis, and /yAngle/ degrees around the y axis (in that order).
+ CSRAW public void Rotate (float xAngle, float yAngle, float zAngle, Space relativeTo = Space.Self)
+ {
+ Rotate (new Vector3 (xAngle, yAngle, zAngle), relativeTo);
+ }
+
+ CUSTOM internal void RotateAroundInternal (Vector3 axis, float angle) { self->RotateAroundSafe (axis, angle); }
+
+ // Rotates the transform around /axis/ by /angle/ degrees.
+ CSRAW public void Rotate (Vector3 axis, float angle, Space relativeTo = Space.Self)
+ {
+ if (relativeTo == Space.Self)
+ RotateAroundInternal (transform.TransformDirection (axis), angle * Mathf.Deg2Rad);
+ else
+ RotateAroundInternal (axis, angle * Mathf.Deg2Rad);
+ }
+
+ // Rotates the transform about /axis/ passing through /point/ in world coordinates by /angle/ degrees.
+ CSRAW public void RotateAround (Vector3 point, Vector3 axis, float angle)
+ {
+ Vector3 worldPos = position;
+ Quaternion q = Quaternion.AngleAxis (angle , axis);
+ Vector3 dif = worldPos - point;
+ dif = q * dif;
+ worldPos = point + dif;
+ position = worldPos;
+ RotateAroundInternal (axis, angle * Mathf.Deg2Rad);
+ }
+
+ // Rotates the transform so the forward vector points at /target/'s current position.
+ CSRAW public void LookAt (Transform target, Vector3 worldUp = Vector3.up) { if (target) LookAt (target.position, worldUp); }
+
+ // Rotates the transform so the forward vector points at /worldPosition/.
+ CUSTOM void LookAt (Vector3 worldPosition, Vector3 worldUp = Vector3.up)
+ {
+ Vector3f forward = worldPosition - self->GetPosition ();
+ Quaternionf q = Quaternionf::identity ();
+ if (LookRotationToQuaternion (forward, worldUp, &q))
+ self->SetRotationSafe (q);
+ else
+ {
+ float mag = Magnitude (forward);
+ if (mag > Vector3f::epsilon)
+ {
+ Matrix3x3f m;
+ m.SetFromToRotation (Vector3f::zAxis, forward / mag);
+ MatrixToQuaternion (m, q);
+ self->SetRotationSafe (q);
+ }
+ }
+ }
+
+ // Transforms /direction/ from local space to world space.
+ AUTO Vector3 TransformDirection (Vector3 direction);
+
+ // Transforms direction /x/, /y/, /z/ from local space to world space.
+ CSRAW public Vector3 TransformDirection (float x, float y, float z) { return TransformDirection (new Vector3 (x, y, z)); }
+
+
+ // Transforms a /direction/ from world space to local space. The opposite of Transform.TransformDirection.
+ AUTO Vector3 InverseTransformDirection (Vector3 direction);
+
+ // Transforms the direction /x/, /y/, /z/ from world space to local space. The opposite of Transform.TransformDirection.
+ CSRAW public Vector3 InverseTransformDirection (float x, float y, float z) { return InverseTransformDirection (new Vector3 (x, y, z)); }
+
+ // Transforms /position/ from local space to world space.
+ AUTO Vector3 TransformPoint (Vector3 position);
+
+ // Transforms the position /x/, /y/, /z/ from local space to world space.
+ CSRAW public Vector3 TransformPoint (float x, float y, float z) { return TransformPoint (new Vector3 (x, y, z)); }
+
+
+ // Transforms /position/ from world space to local space. The opposite of Transform.TransformPoint.
+ AUTO Vector3 InverseTransformPoint (Vector3 position);
+
+
+ // Transforms the position /x/, /y/, /z/ from world space to local space. The opposite of Transform.TransformPoint.
+ CSRAW public Vector3 InverseTransformPoint (float x, float y, float z) { return InverseTransformPoint (new Vector3 (x, y, z)); }
+
+
+ // Returns the topmost transform in the hierarchy.
+ CUSTOM_PROP Transform root { return Scripting::ScriptingWrapperFor(&self->GetRoot()); }
+
+ // The number of children the Transform has.
+ CUSTOM_PROP int childCount { return self->GetChildrenCount (); }
+
+ // Unparents all children.
+ CUSTOM void DetachChildren ()
+ {
+ Transform& transform = *self;
+ while (transform.begin () != transform.end ())
+ (**transform.begin ()).SetParent (NULL);
+ }
+
+ // Finds a child by /name/ and returns it.
+ CUSTOM Transform Find (string name)
+ {
+ return Scripting::ScriptingWrapperFor (FindRelativeTransformWithPath (*self, name.AsUTF8().c_str ()));
+ }
+
+ //*undocumented
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM internal void SendTransformChangedScale ()
+ {
+ self->SendTransformChanged ( Transform::kScaleChanged );
+ }
+
+
+ // The global scale of the object (RO).
+ AUTO_PROP Vector3 lossyScale GetWorldScaleLossy
+
+ // Is this transform a child of /parent/?
+ CUSTOM bool IsChildOf (Transform parent)
+ {
+ return IsChildOrSameTransform(*self, *parent);
+ }
+
+ // Has the transform changed since the last time the flag was set to 'false'?
+ CUSTOM_PROP bool hasChanged { return self->GetChangedFlag(); } { self->SetChangedFlag(value); }
+
+ //*undocumented*
+ CSRAW public Transform FindChild (string name) { return Find(name); }
+
+ //*undocumented* Documented separately
+ CSRAW public IEnumerator GetEnumerator ()
+ {
+ return new Transform.Enumerator (this);
+ }
+
+ CLASS private Enumerator : IEnumerator
+ CSRAW
+ Transform outer;
+ int currentIndex = -1;
+
+ internal Enumerator (Transform outer) { this.outer = outer; }
+ //*undocumented*
+ public object Current
+ {
+ get { return outer.GetChild (currentIndex); }
+ }
+
+ //*undocumented*
+ public bool MoveNext ()
+ {
+ int childCount = outer.childCount;
+ return ++currentIndex < childCount;
+ }
+
+ //*undocumented*
+ public void Reset () { currentIndex = -1; }
+ END
+
+ // *undocumented* DEPRECATED
+ CSRAW
+ OBSOLETE warning use Transform.Rotate instead.
+ CUSTOM void RotateAround (Vector3 axis, float angle) { self->RotateAroundSafe (axis, angle); }
+
+ // *undocumented* DEPRECATED
+ OBSOLETE warning use Transform.Rotate instead.
+ CUSTOM void RotateAroundLocal (Vector3 axis, float angle) { self->RotateAroundLocalSafe (axis, angle); }
+
+ // Get a transform child by index
+ CUSTOM Transform GetChild (int index)
+ {
+ Transform& transform = *self;
+ if (index >= 0 && index < transform.GetChildrenCount ())
+ return Scripting::ScriptingWrapperFor (&transform.GetChild (index));
+ else
+ {
+ Scripting::RaiseMonoException ("Transform child out of bounds");
+ return SCRIPTING_NULL;
+ }
+ }
+
+ //*undocumented* DEPRECATED
+ OBSOLETE warning use Transform.childCount instead.
+ CUSTOM int GetChildCount () { return self->GetChildrenCount (); }
+
+ CONDITIONAL UNITY_EDITOR
+ CUSTOM internal bool IsNonUniformScaleTransform ()
+ {
+ Transform& transform = *self;
+ Matrix4x4f temp;
+ TransformType scaleType = transform.CalculateTransformMatrix (temp);
+ return IsNonUniformScaleTransform (scaleType);
+ }
+
+END
+
+CSRAW }
diff --git a/Runtime/Export/UnityEngineYieldOperation.txt b/Runtime/Export/UnityEngineYieldOperation.txt
new file mode 100644
index 0000000..bbaca7e
--- /dev/null
+++ b/Runtime/Export/UnityEngineYieldOperation.txt
@@ -0,0 +1,14 @@
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace UnityEngine
+{
+
+// Base class for all /yield/ instructions.
+CSRAW [StructLayout (LayoutKind.Sequential)]
+NONSEALED_CLASS YieldInstruction
+END
+
+CSRAW }
diff --git a/Runtime/Export/UserAuthorizationDialog.cs b/Runtime/Export/UserAuthorizationDialog.cs
new file mode 100644
index 0000000..c2cb011
--- /dev/null
+++ b/Runtime/Export/UserAuthorizationDialog.cs
@@ -0,0 +1,166 @@
+using UnityEngine;
+namespace UnityEngine
+{
+
+[AddComponentMenu("")]
+class UserAuthorizationDialog : MonoBehaviour
+{
+#if !UNITY_FLASH && !UNITY_WEBGL && !UNITY_WINRT
+ private Rect windowRect;
+ private const int width = 385;
+ private const int height = 155;
+
+ private Texture warningIcon;
+#endif
+
+ private void Start ()
+ {
+#if !UNITY_FLASH && !UNITY_WEBGL && !UNITY_WINRT
+ warningIcon = Resources.GetBuiltinResource(typeof (Texture2D), "WarningSign.psd") as Texture2D;
+ if (Screen.width < width || Screen.height < height)
+ {
+ Debug.LogError ("Screen is to small to display authorization dialog. Authorization denied.");
+ Application.ReplyToUserAuthorizationRequest (false);
+ }
+
+ windowRect = new Rect (Screen.width / 2 - width/2, Screen.height / 2 - height/2, width, height);
+#endif
+ }
+
+ private void OnGUI () {
+#if !UNITY_FLASH && !UNITY_WEBGL && !UNITY_WINRT
+ GUISkin oldSkin = GUI.skin;
+
+ // Create a custom GUISkin from scratch so users cannot change
+ // the look of the security dialog by changing the default GUI skins.
+ GUISkin skin = ScriptableObject.CreateInstance ("GUISkin") as GUISkin;
+
+ skin.box.normal.background = (Texture2D)Resources.GetBuiltinResource(typeof(Texture2D), "GameSkin/box.png");
+ skin.box.normal.textColor = new Color (0.9f, 0.9f, 0.9f, 1.0f);
+ skin.box.padding.left = 6;
+ skin.box.padding.right = 6;
+ skin.box.padding.top = 4;
+ skin.box.padding.bottom = 4;
+ skin.box.border.left = 6;
+ skin.box.border.right = 6;
+ skin.box.border.top = 6;
+ skin.box.border.bottom = 6;
+ skin.box.margin.left = 4;
+ skin.box.margin.right = 4;
+ skin.box.margin.top = 4;
+ skin.box.margin.bottom = 4;
+
+ skin.button.normal.background = (Texture2D)Resources.GetBuiltinResource(typeof(Texture2D), "GameSkin/button.png");
+ skin.button.normal.textColor = new Color (0.9f, 0.9f, 0.9f, 1.0f);
+ skin.button.hover.background = (Texture2D)Resources.GetBuiltinResource(typeof(Texture2D), "GameSkin/button hover.png");
+ skin.button.hover.textColor = Color.white;
+ skin.button.active.background = (Texture2D)Resources.GetBuiltinResource(typeof(Texture2D), "GameSkin/button active.png");
+ skin.button.active.textColor = new Color (0.9f, 0.9f, 0.9f, 1.0f);
+ skin.button.border.left = 6;
+ skin.button.border.right = 6;
+ skin.button.border.top = 6;
+ skin.button.border.bottom = 6;
+ skin.button.padding.left = 8;
+ skin.button.padding.right = 8;
+ skin.button.padding.top = 4;
+ skin.button.padding.bottom = 4;
+ skin.button.margin.left = 4;
+ skin.button.margin.right = 4;
+ skin.button.margin.top = 4;
+ skin.button.margin.bottom = 4;
+
+ skin.label.normal.textColor = new Color (0.9f, 0.9f, 0.9f, 1.0f);
+ skin.label.padding.left = 6;
+ skin.label.padding.right = 6;
+ skin.label.padding.top = 4;
+ skin.label.padding.bottom = 4;
+ skin.label.margin.left = 4;
+ skin.label.margin.right = 4;
+ skin.label.margin.top = 4;
+ skin.label.margin.bottom = 4;
+ skin.label.alignment = TextAnchor.UpperLeft;
+
+ skin.window.normal.background = (Texture2D)Resources.GetBuiltinResource(typeof(Texture2D), "GameSkin/window.png");
+ skin.window.normal.textColor = Color.white;
+ skin.window.border.left = 8;
+ skin.window.border.right = 8;
+ skin.window.border.top = 18;
+ skin.window.border.bottom = 8;
+ skin.window.padding.left = 8;
+ skin.window.padding.right = 8;
+ skin.window.padding.top = 20;
+ skin.window.padding.bottom = 5;
+ skin.window.alignment = TextAnchor.UpperCenter;
+ skin.window.contentOffset = new Vector2 (0, -18);
+
+ GUI.skin = skin;
+ windowRect = GUI.Window (0, windowRect, DoUserAuthorizationDialog, "Unity Web Player Authorization Request");
+ GUI.skin = oldSkin;
+#endif
+ }
+
+ // Make the contents of the window
+ private void DoUserAuthorizationDialog (int windowID) {
+#if !UNITY_FLASH && !UNITY_WEBGL && !UNITY_WINRT
+ UserAuthorization auth = Application.GetUserAuthorizationRequestMode();
+
+ GUILayout.FlexibleSpace ();
+
+ GUI.backgroundColor = new Color (0.9f, 0.9f, 0.9f, 0.7f);
+ GUILayout.BeginHorizontal ("box");
+ GUILayout.FlexibleSpace ();
+
+ GUILayout.BeginVertical();
+ GUILayout.FlexibleSpace ();
+
+ GUILayout.Label(warningIcon);
+
+ GUILayout.FlexibleSpace ();
+ GUILayout.EndVertical();
+
+ GUILayout.FlexibleSpace ();
+
+ GUILayout.BeginVertical();
+ GUILayout.FlexibleSpace ();
+
+ if (auth == (UserAuthorization.WebCam | UserAuthorization.Microphone))
+ GUILayout.Label ("The content on this site would like to use your\ncomputer's web camera and microphone.\nThese images and sounds may be recorded.");
+ else
+ if (auth == UserAuthorization.WebCam)
+ GUILayout.Label ("The content on this site would like to use\nyour computer's web camera. The images\nfrom your web camera may be recorded.");
+ else
+ if (auth == UserAuthorization.Microphone)
+ GUILayout.Label ("The content on this site would like to use\nyour computer's microphone. The sounds\nfrom your microphone may be recorded.");
+ else
+ return;
+
+ GUILayout.FlexibleSpace ();
+ GUILayout.EndVertical();
+
+ GUILayout.FlexibleSpace ();
+ GUILayout.EndHorizontal ();
+
+ GUILayout.FlexibleSpace ();
+
+ GUI.backgroundColor = Color.white;
+ GUILayout.BeginHorizontal ();
+
+ if (GUILayout.Button ("Deny"))
+ Application.ReplyToUserAuthorizationRequest (false);
+
+ GUILayout.FlexibleSpace ();
+
+ if (GUILayout.Button ("Always Allow for this Site"))
+ Application.ReplyToUserAuthorizationRequest (true, true);
+
+ GUILayout.Space (5);
+
+ if (GUILayout.Button ("Allow"))
+ Application.ReplyToUserAuthorizationRequest (true);
+
+ GUILayout.EndHorizontal ();
+ GUILayout.FlexibleSpace ();
+#endif
+ }
+}
+}
diff --git a/Runtime/Export/Utils.txt b/Runtime/Export/Utils.txt
new file mode 100644
index 0000000..18fbfe1
--- /dev/null
+++ b/Runtime/Export/Utils.txt
@@ -0,0 +1,1235 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Graphics/ImageConversion.h"
+#include "Runtime/Misc/Player.h"
+#include "Runtime/Export/WWW.h"
+#include "Runtime/Misc/WWWCached.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+#include "Runtime/Interfaces/IAudio.h"
+
+#if WEBPLUG
+#include "PlatformDependent/CommonWebPlugin/UnityWebStream.h"
+#include "PlatformDependent/CommonWebPlugin/CompressedFileStreamMemory.h"
+#endif
+#include "Runtime/Misc/AssetBundle.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Misc/Player.h"
+#include "Runtime/Utilities/GUID.h"
+#include "Runtime/Misc/CachingManager.h"
+#include "Runtime/Misc/AssetBundleUtility.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/LicenseInfo.h"
+#include "Editor/Src/EditorUserBuildSettings.h"
+#endif
+
+#if UNITY_FLASH
+#include "PlatformDependent/FlashSupport/cpp/WWWFlash.h"
+#endif
+
+#if ENABLE_MOVIES
+#include "Runtime/Video/MovieTexture.h"
+#endif
+#if ENABLE_AUDIO
+#include "Runtime/Audio/AudioClip.h"
+#endif
+#include "Runtime/Misc/Player.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace UnityEngine
+{
+#if ENABLE_WWW
+// Simple access to web pages.
+
+CONDITIONAL ENABLE_WWW
+CLASS WWW : IDisposable
+ // We are matching the WWW class here so we can directly access it.
+ CSRAW internal IntPtr m_Ptr;
+
+ C++RAW
+
+ #if ENABLE_WWW
+ inline WWW* GetWWWChecked (ScriptingObjectWithIntPtrField<WWW>& self)
+ {
+ WWW* www = self.GetPtr();
+
+ if (!www)
+ Scripting::RaiseNullException("WWW class has already been disposed.");
+
+ return www;
+ }
+ #endif
+
+ C++RAW
+ #define GET GetWWWChecked (self)
+
+ //*undocumented*
+ CSRAW public void Dispose ()
+ {
+ DestroyWWW(true);
+ }
+
+ CSRAW ~WWW() {
+ DestroyWWW(false);
+ }
+
+ //*undocumented*
+ THREAD_SAFE
+ CUSTOM private void DestroyWWW(bool cancel) {
+ WWW* www = self.GetPtr();
+
+ // no-op if already 0
+ self.SetPtr(0);
+
+ if (www)
+ {
+ if (cancel)
+ www->Cancel();
+ www->Release();
+ }
+ }
+
+ //*undocumented*
+ CUSTOM void InitWWW( string url , byte[] postData, string[] iHeaders ) {
+ string cpp_string = url;
+ map<string,string> headers;
+ // Copy in headers from the MonoArray if available
+ int headersSize = GetScriptingArraySize(iHeaders);
+ for(int i=0; i < headersSize-1 ; i += 2) {
+ headers[scripting_cpp_string_for(Scripting::GetScriptingArrayElementNoRef<ScriptingStringPtr>(iHeaders,i))] = scripting_cpp_string_for(Scripting::GetScriptingArrayElementNoRef<ScriptingStringPtr>(iHeaders,i+1));
+ }
+ int rawPostDataLength = -1;
+ char* rawPostDataPtr = NULL;
+ if(postData != SCRIPTING_NULL) {
+ rawPostDataPtr = Scripting::GetScriptingArrayStart<char>(postData); // Will be copied by WWW::Create
+ rawPostDataLength = GetScriptingArraySize(postData);
+ }
+ WWW* www = WWW::Create (cpp_string.c_str(), rawPostDataPtr, rawPostDataLength, headers);
+ self.SetPtr(www);
+ }
+
+ // Creates a WWW request with the given URL.
+ CSRAW public WWW(string url) {
+ InitWWW(url, null, null);
+ }
+
+ // Creates a WWW request with the given URL.
+ CONDITIONAL !UNITY_WEBGL
+ CSRAW public WWW(string url, WWWForm form ) {
+ string[] flattenedHeaders = FlattenedHeadersFrom(form.headers);
+
+ #if UNITY_WEBPLAYER || UNITY_EDITOR
+ if(enforceWebSecurityRestrictions()) {
+ CheckSecurityOnHeaders(flattenedHeaders);
+ }
+ #endif
+
+ InitWWW(url, form.data, flattenedHeaders);
+ }
+
+ // Creates a WWW request with the given URL.
+ CSRAW public WWW(string url, byte[] postData) {
+ InitWWW(url, postData, null);
+ }
+
+ // Creates a WWW request with the given URL.
+ CSRAW #if !UNITY_METRO_API && !UNITY_WP8_API
+ CSRAW public WWW(string url, byte[] postData, Hashtable headers ) {
+ string[] flattenedHeaders = FlattenedHeadersFrom(headers);
+
+ #if UNITY_WEBPLAYER || UNITY_EDITOR
+ if(enforceWebSecurityRestrictions()) {
+ CheckSecurityOnHeaders(flattenedHeaders);
+ }
+ #endif
+
+ InitWWW(url, postData, flattenedHeaders);
+ }
+
+ CSRAW #else
+ CSRAW public WWW(string url, byte[] postData, Dictionary<string, string> headers ) {
+ string[] flattenedHeaders = FlattenedHeadersFrom(headers);
+
+ #if UNITY_WEBPLAYER || UNITY_EDITOR
+ if(enforceWebSecurityRestrictions()) {
+ CheckSecurityOnHeaders(flattenedHeaders);
+ }
+ #endif
+
+ InitWWW(url, postData, flattenedHeaders);
+ }
+ CSRAW #endif
+
+ CUSTOM internal bool enforceWebSecurityRestrictions() {
+ #if UNITY_WEBPLAYER
+ return true;
+ #elif UNITY_EDITOR
+ BuildTargetPlatform buildTarget = GetEditorUserBuildSettings().GetActiveBuildTarget();
+ return buildTarget == kBuildWebPlayerLZMA || buildTarget == kBuildWebPlayerLZMAStreamed;
+ #else
+ return false;
+ #endif
+ }
+
+ // Encodes string into an URL-friendly format.
+
+ CSRAW
+#if !UNITY_WEBGL
+ public static string EscapeURL(string s, Encoding e=System.Text.Encoding.UTF8)
+ {
+ if (s == null)
+ return null;
+
+ if (s == "")
+ return "";
+
+ if (e == null)
+ return null;
+
+ return WWWTranscoder.URLEncode(s,e);
+ }
+#endif
+
+ // Decodes string from an URL-friendly format.
+
+ CSRAW
+#if !UNITY_WEBGL
+ public static string UnEscapeURL(string s, Encoding e=System.Text.Encoding.UTF8)
+ {
+ if (null == s)
+ return null;
+
+ if (s.IndexOf ('%') == -1 && s.IndexOf ('+') == -1)
+ return s;
+
+ return WWWTranscoder.URLDecode(s,e);
+
+ }
+#endif
+
+
+ // The headers from the HTTP response.
+
+ CSRAW
+#if ENABLE_GENERICS
+ public System.Collections.Generic.Dictionary<string,string> responseHeaders
+ {
+ get {
+ #if UNITY_FLASH
+ UnityEngine.Flash.ActionScript.Import("System.UnityException");
+ #endif
+ if (!isDone) throw new UnityException("WWW is not finished downloading yet");
+ return ParseHTTPHeaderString(responseHeadersString);
+ }
+ }
+#endif
+ CUSTOM_PROP private string responseHeadersString
+ {
+ WWW& www = *GET;
+
+ ScriptingStringPtr res = scripting_string_new(www.GetResponseHeaders().c_str());
+ return res;
+ }
+
+
+ // Returns the contents of the fetched web page as a string (RO).
+ CONDITIONAL !UNITY_WEBGL
+ CSRAW public string text { get {
+#if !UNITY_FLASH
+ if (!isDone) throw new UnityException("WWW is not ready downloading yet");
+ return GetTextEncoder().GetString(this.bytes, 0, this.bytes.Length);
+#else
+ //Flash can't access responsheaders correctly, so we always asume utf8 and use native parsing, directly from the bytearray, and let flash try and deal with it.
+ return stringFromByteArray;
+#endif
+ }
+ }
+ CSRAW internal static System.Text.Encoding DefaultEncoding
+ {
+ get
+ {
+ #if UNITY_WINRT
+ return System.Text.Encoding.UTF8;
+ #else
+ return System.Text.Encoding.ASCII;
+ #endif
+ }
+ }
+
+ CONDITIONAL UNITY_FLASH
+ CUSTOM_PROP internal string stringFromByteArray {
+ WWWFlash *wwwflash = (WWWFlash*)GET;
+ ScriptingString* str = scripting_string_new("");//We don't really care about the return string, as we'll emit as3 to parse bytearray below, no need to marshall the bytearray into native first.
+ __asm __volatile__("var heapPos:int = heap.position;");
+ __asm __volatile__("heap.position = %0;"::"r"(wwwflash->m_Buffer));
+ __asm __volatile__("returnString = heap.readUTFBytes(%0); "::"r"(wwwflash->m_SizeLoaded));
+ __asm __volatile__("heap.position = heapPos;");
+ return str;
+ }
+
+
+ CONDITIONAL !UNITY_FLASH && !UNITY_WEBGL
+ CSRAW private Encoding GetTextEncoder()
+ {
+#if ENABLE_GENERICS
+ // Check for charset type
+ string contentType=null;
+ if (responseHeaders.TryGetValue("CONTENT-TYPE",out contentType))
+ {
+ int charsetKeyIndex = contentType.IndexOf("charset", StringComparison.OrdinalIgnoreCase);
+ if (charsetKeyIndex > -1)
+ {
+ int charsetValueIndex = contentType.IndexOf('=', charsetKeyIndex);
+ if (charsetValueIndex > -1)
+ {
+ string encoding = contentType.Substring(charsetValueIndex + 1).Trim().Trim(new []{'\'', '"'}).Trim();
+ int semicolonIndex = encoding.IndexOf(';');
+ if (semicolonIndex > -1)
+ encoding = encoding.Substring(0, semicolonIndex);
+ try
+ {
+ return System.Text.Encoding.GetEncoding(encoding);
+ } catch (Exception) {
+ Debug.Log("Unsupported encoding: '" + encoding + "'");
+ }
+ }
+ }
+ }
+#endif
+ // Use default (utf8)
+ return System.Text.Encoding.UTF8;
+ }
+
+ FLUSHCONDITIONS
+
+ CONDITIONAL !UNITY_FLASH && !UNITY_WEBGL
+ OBSOLETE warning Please use WWW.text instead
+ CSRAW public string data { get { return text; } }
+
+ // Returns the contents of the fetched web page as a byte array (RO).
+ CUSTOM_PROP byte[] bytes {
+ WWW& www = *GET;
+ if (www.GetType() == kWWWTypeCached)
+ {
+ ErrorString(kWWWCachedAccessError);
+ return SCRIPTING_NULL;
+ }
+ if (www.GetSecurityPolicy() != WWW::kSecurityPolicyAllowAccess)
+ Scripting::RaiseSecurityException("No valid crossdomain policy available to allow access");
+
+ if (!www.HasDownloadedOrMayBlock ())
+ return CreateEmptyStructArray(GetScriptingManager().GetCommonClasses().byte);
+
+ return CreateScriptingArray<UInt8>( www.GetData(), www.GetSize(), GetScriptingManager().GetCommonClasses().byte );
+ }
+
+ //*undocumented* OBSOLETE Can do the same with bytes.Length
+ CUSTOM_PROP int size {
+ WWW& www = *GET;
+ if (!www.HasDownloadedOrMayBlock ())
+ return 0;
+ return www.GetSize();
+ }
+
+ //*undocumented*
+ CONDITIONAL UNITY_WINRT
+ CUSTOM private IntPtr GetError()
+ {
+ WWW *www = GET;
+ if (www)
+ return (void*)self->GetError();
+ else
+ return (void*)kWWWErrCancelled;
+ }
+
+ // Returns an error message if there was an error during the download (RO).
+ CSRAW
+ #if UNITY_WINRT
+ public string error
+ {
+ get
+ {
+ return Marshal.PtrToStringAnsi(GetError());
+ }
+ }
+ #endif
+
+ // Returns an error message if there was an error during the download (RO).
+ CONDITIONAL !UNITY_WINRT
+ CUSTOM_PROP string error {
+ WWW *www = GET;
+ if (www)
+ {
+ const char* e = self->GetError();
+ if (e)
+ return scripting_string_new(e);
+ else
+ return SCRIPTING_NULL;
+ }
+ else
+ return scripting_string_new(kWWWErrCancelled);
+ }
+
+ //*undocumented*
+ CUSTOM private Texture2D GetTexture(bool markNonReadable)
+ {
+ WWW& www = *GET;
+ if (www.GetType() == kWWWTypeCached)
+ {
+ ErrorString(kWWWCachedAccessError);
+ return SCRIPTING_NULL;
+ }
+
+ // create the texture
+ Texture2D* tex = CreateObjectFromCode<Texture2D>();
+
+ if (www.HasDownloadedOrMayBlock ())
+ {
+ LoadMemoryBufferIntoTexture( *tex, self->GetData(), self->GetSize(), kLoadImageUncompressed, markNonReadable );
+ WWW::SecurityPolicy policy = self->GetSecurityPolicy();
+ if (policy != WWW::kSecurityPolicyAllowAccess)
+ tex->SetReadAllowed(false);
+ }
+ else
+ {
+ LoadMemoryBufferIntoTexture( *tex, NULL, 0, kLoadImageUncompressed, markNonReadable );
+ }
+
+ return Scripting::ScriptingWrapperFor(tex);
+ }
+
+ // Returns a [[Texture2D]] generated from the downloaded data (RO).
+ CSRAW public Texture2D texture { get { return GetTexture(false); } }
+
+ // Returns a non-readable [[Texture2D]] generated from the downloaded data (RO).
+ CSRAW public Texture2D textureNonReadable { get { return GetTexture(true); } }
+
+ // Returns a [[AudioClip]] generated from the downloaded data (RO).
+
+ CONDITIONAL !UNITY_WEBGL && ENABLE_AUDIO
+ CSRAW public AudioClip audioClip {
+ get { return GetAudioClip(true); }
+ }
+
+ FLUSHCONDITIONS
+
+ /// *listonly*
+ CONDITIONAL !UNITY_WEBGL && ENABLE_AUDIO
+ CSRAW public AudioClip GetAudioClip(bool threeD)
+ {
+ return GetAudioClip(threeD, false);
+ }
+
+ /// *listonly*
+ CONDITIONAL !UNITY_WEBGL && ENABLE_AUDIO
+ CSRAW public AudioClip GetAudioClip(bool threeD, bool stream)
+ {
+ return GetAudioClip(threeD, stream, AudioType.UNKNOWN);
+ }
+
+ // Returns an [[AudioClip]] generated from the downloaded data (RO).
+ CONDITIONAL !UNITY_WEBGL && ENABLE_AUDIO
+ CUSTOM public AudioClip GetAudioClip(bool threeD, bool stream, AudioType audioType)
+ {
+ IAudio *audio = GetIAudio();
+ if(audio == NULL)
+ return SCRIPTING_NULL;
+
+ WWW& www = *GET;
+ if (www.GetType() == kWWWTypeCached)
+ {
+ ErrorString(kWWWCachedAccessError);
+ return SCRIPTING_NULL;
+ }
+
+ AudioClip* clip = audio->CreateAudioClipFromWWW(*GET, threeD, stream, audioType);
+ return Scripting::ScriptingWrapperFor (clip);
+ }
+
+
+ // Returns a [[MovieTexture]] generated from the downloaded data (RO).
+ CONDITIONAL ENABLE_MOVIES
+ CUSTOM_PROP MovieTexture movie
+ {
+
+ WWW& www = *GET;
+ if (www.GetType() == kWWWTypeCached)
+ {
+ ErrorString(kWWWCachedAccessError);
+ return SCRIPTING_NULL;
+ }
+ #if UNITY_EDITOR
+ if (!LicenseInfo::Flag(lf_pro_version))
+ {
+ ErrorString("Movie playback is only possible with Unity Pro");
+ return SCRIPTING_NULL;
+ }
+ #endif
+
+ MovieTexture* tex = NULL;
+ IAudio *audio = GetIAudio();
+ if(audio)
+ tex = audio->CreateMovieTextureFromWWW(*GET);
+
+
+ //Not applying any security restrictions here, because currently our API does not allow grabbing samples from the movieclip
+ //It's impossible for a hacker to get his hands on the data, or xfer it somewhere. When we start supporting this, we should
+ //add a crossdomain securitycheck here.
+ return Scripting::ScriptingWrapperFor (tex);
+ }
+
+ // Replaces the contents of an existing [[Texture2D]] with an image from the downloaded data.
+ CUSTOM void LoadImageIntoTexture(Texture2D tex) {
+ WWW& www = *GET;
+ if (www.GetType() == kWWWTypeCached)
+ {
+ ErrorString(kWWWCachedAccessError);
+ return;
+ }
+
+ if (!www.HasDownloadedOrMayBlock ())
+ return;
+
+ LoadMemoryBufferIntoTexture( *tex, www.GetData(), www.GetSize(), IsCompressedDXTTextureFormat(tex->GetTextureFormat())?kLoadImageDXTCompressDithered:kLoadImageUncompressed );
+ if (www.GetSecurityPolicy() != WWW::kSecurityPolicyAllowAccess) tex->SetReadAllowed(false);
+ }
+
+ // Is the download already finished? (RO)
+ CUSTOM_PROP bool isDone {
+ return (short)GET->IsDone();
+ }
+
+ CONDITIONAL !UNITY_FLASH && !UNITY_WEBGL && !UNITY_WINRT
+ OBSOLETE error All blocking WWW functions have been deprecated, please use one of the asynchronous functions instead.
+ CUSTOM static string GetURL(string url) {
+ map<string,string> headers;
+ const char* c_string = url.AsUTF8().c_str();
+ WWW* fetcher = WWW::Create(c_string, NULL, -1, headers);
+
+ MonoString* result;
+
+ if (fetcher->GetSecurityPolicy() != WWW::kSecurityPolicyAllowAccess)
+ {
+ Scripting::RaiseSecurityException("No valid crossdomain policy available to allow access");
+ return scripting_string_new("");
+ }
+
+ if (!fetcher->HasDownloadedOrMayBlock ())
+ result = scripting_string_new("");
+ else
+ result = scripting_string_new((char*)fetcher->GetData(), fetcher->GetSize());
+
+ fetcher->Release();
+ return result;
+ }
+
+ FLUSHCONDITIONS
+
+ CSRAW
+#if !UNITY_FLASH && !UNITY_WEBGL && !UNITY_WINRT
+ OBSOLETE error All blocking WWW functions have been deprecated, please use one of the asynchronous functions instead.
+#endif
+ public static Texture2D GetTextureFromURL(string url) {
+ return new WWW(url).texture;
+ }
+
+ // How far has the download progressed (RO).
+ CUSTOM_PROP float progress
+ {
+ return GET->GetProgress();
+ }
+
+ // How far has the upload progressed (RO).
+ CUSTOM_PROP float uploadProgress
+ {
+ return GET->GetUploadProgress();
+ }
+
+ // How many bytes have been downloaded (RO).
+ CUSTOM_PROP int bytesDownloaded
+ {
+ return GET->GetSize();
+ }
+
+ // Loads the new web player data file.
+ CUSTOM void LoadUnityWeb ()
+ {
+ WWW& www = *GET;
+ if (!www.HasDownloadedOrMayBlock ())
+ return;
+
+ if (www.GetSecurityPolicy() != WWW::kSecurityPolicyAllowAccess)
+ {
+ Scripting::RaiseSecurityException("No valid crossdomain policy available to allow access");
+ return; //is this return required?
+ }
+
+ #if WEBPLUG
+ QueuePlayerLoadWebData (www.GetUnityWebStream());
+ www.GetUnityWebStream()->RetainDownload (&www);
+ #else
+ LogString (Format("Requested loading unity web file %s. This will only be loaded in the web player.", www.GetUrl ()));
+ #endif
+ }
+
+ // Load an Ogg Vorbis file into the audio clip.
+
+ CONDITIONAL !UNITY_FLASH && !UNITY_WEBGL && !UNITY_WINRT && ENABLE_AUDIO
+ OBSOLETE warning .oggVorbis accessor is deprecated, use .audioClip or GetAudioClip() instead.
+ CUSTOM_PROP AudioClip oggVorbis
+ {
+ IAudio *audio = GetIAudio();
+ if(audio == NULL)
+ return SCRIPTING_NULL;
+
+ if (audio->IsFormatSupportedByPlatform("ogg"))
+ {
+ ErrorString("Streaming of 'ogg' on this platform is not supported");
+ return SCRIPTING_NULL;
+ }
+
+ WWW& www = *GET;
+ if (www.GetType() == kWWWTypeCached)
+ {
+ ErrorString(kWWWCachedAccessError);
+ return SCRIPTING_NULL;
+ }
+
+ if (!www.HasDownloadedOrMayBlock ())
+ return SCRIPTING_NULL;
+
+ AudioClip *clip = audio->CreateAudioClipOGGFromWWW(*GET);
+ return Scripting::ScriptingWrapperFor(clip);
+ }
+
+
+ // The URL of this WWW request (RO).
+ CUSTOM_PROP string url { return scripting_string_new(GET->GetUrl()); }
+
+ // Streams an AssetBundle that can contain any kind of asset from the project folder.
+ CUSTOM_PROP AssetBundle assetBundle
+ {
+ return Scripting::ScriptingWrapperFor (ExtractAssetBundle (*GET));
+ }
+
+ // Priority of [[AssetBundle]] decompression thread.
+ // SA: [[ThreadPriority]] enum.
+ CONDITIONAL !UNITY_FLASH && !UNITY_WEBGL
+ CUSTOM_PROP ThreadPriority threadPriority { return GET->GetThreadPriority(); } { GET->SetThreadPriority(value); }
+
+ CUSTOM internal WWW (string url, int version, uint crc)
+ {
+ string cpp_string = url;
+ #if ENABLE_CACHING
+ WWW* www = new WWWCached(cpp_string.c_str(), version, crc);
+ #else
+ WWW* www = WWW::Create (cpp_string.c_str(), NULL, 0, WWW::WWWHeaders(), 0, crc);
+ #endif
+ self.SetPtr(www);
+ }
+
+ // Loads an AssetBundle with the specified version number from the cache. If the AssetBundle is not currently cached, it will automatically be downloaded and stored in the cache for future retrieval from local storage.
+
+
+ CSRAW public static WWW LoadFromCacheOrDownload( string url, int version, uint crc = 0) {
+ return new WWW(url, version, crc);
+ }
+ C++RAW
+ #undef GET
+END
+
+// Helper class to generate form data to post to web servers using the [[WWW]] class.
+
+
+
+CONDITIONAL !UNITY_WEBGL
+CLASS WWWForm
+
+CSRAW private List<byte[]> formData; // <byte[]>
+CSRAW private List<string> fieldNames; // <string>
+CSRAW private List<string> fileNames; // <string>
+CSRAW private List<string> types; // <string>
+CSRAW private byte[] boundary;
+CSRAW private bool containsFiles = false;
+
+// Creates an empty WWWForm object.
+CSRAW public WWWForm() {
+ formData = new List<byte[]>();
+ fieldNames = new List<string>();
+ fileNames = new List<string>();
+ types = new List<string>();
+
+ // Generate a random boundary
+ boundary=new byte[40];
+ for(int i=0; i<40; i++) {
+ int randomChar=Random.Range(48,110);
+ if(randomChar > 57) // skip unprintable chars between 57 and 64 (inclusive)
+ randomChar+=7;
+ if(randomChar > 90) // and 91 and 96 (inclusive)
+ randomChar+=6;
+ boundary[i]=(byte)randomChar;
+ }
+
+}
+
+// Add a simple field to the form.
+CSRAW public void AddField(string fieldName, string value, Encoding e=System.Text.Encoding.UTF8) {
+ fieldNames.Add(fieldName);
+ fileNames.Add(null);
+ formData.Add(e.GetBytes(value));
+ types.Add("text/plain; charset=\"" + e.WebName + "\"");
+}
+
+// Adds a simple field to the form.
+CSRAW public void AddField(string fieldName, int i) {
+ AddField(fieldName, i.ToString());
+}
+
+// Add binary data to the form.
+CSRAW public void AddBinaryData(string fieldName, byte[] contents, string fileName=null, string mimeType = null) {
+ containsFiles=true;
+
+ // We handle png files automatically as we suspect people will be uploading png files a lot due to the new
+ // screen shot feature. If we want to add support for detecting other file types, we will need to do it in a more extensible way.
+ bool isPng = contents.Length > 8 && contents[0] == 0x89 && contents[1] == 0x50 && contents[2] == 0x4e && contents[3] == 0x47
+ && contents[4] == 0x0d && contents[5] == 0x0a && contents[6] == 0x1a && contents[7] == 0x0a;
+ if(fileName == null) {
+ fileName = fieldName + (isPng?".png":".dat");
+ }
+ if(mimeType == null) {
+ if(isPng)
+ mimeType="image/png";
+ else
+ mimeType="application/octet-stream";
+ }
+
+ fieldNames.Add(fieldName);
+ fileNames.Add(fileName);
+ formData.Add(contents);
+ types.Add(mimeType);
+}
+
+// (RO) Returns the correct request headers for posting the form using the [[WWW]] class.
+CSRAW #if !UNITY_METRO_API && !UNITY_WP8_API
+CSRAW public Hashtable headers { get {
+ Hashtable retval = new Hashtable();
+ if(containsFiles)
+ retval["Content-Type"]="multipart/form-data; boundary=\"" + System.Text.Encoding.UTF8.GetString(boundary) + "\"";
+ else
+ retval["Content-Type"]="application/x-www-form-urlencoded";
+ return retval;
+ }
+}
+
+CSRAW #else
+CSRAW public Dictionary<string, string> headers { get {
+ Dictionary<string, string> retval = new Dictionary<string, string>();
+ if(containsFiles)
+ retval["Content-Type"]="multipart/form-data; boundary=\"" + System.Text.Encoding.UTF8.GetString(boundary, 0, boundary.Length) + "\"";
+ else
+ retval["Content-Type"]="application/x-www-form-urlencoded";
+ return retval;
+ }
+}
+
+CSRAW #endif
+
+// (RO) The raw data to pass as the POST request body when sending the form.
+CSRAW public byte[] data { get {
+
+ if(containsFiles) {
+ byte[] dDash = WWW.DefaultEncoding.GetBytes("--");
+ byte[] crlf = WWW.DefaultEncoding.GetBytes("\r\n");
+ byte[] contentTypeHeader = WWW.DefaultEncoding.GetBytes("Content-Type: ");
+ byte[] dispositionHeader = WWW.DefaultEncoding.GetBytes("Content-disposition: form-data; name=\"");
+ byte[] endQuote = WWW.DefaultEncoding.GetBytes("\"");
+ byte[] fileNameField = WWW.DefaultEncoding.GetBytes("; filename=\"");
+
+
+ using(MemoryStream memStream = new MemoryStream(1024))
+ {
+ for(int i=0; i < formData.Count; i++) {
+ memStream.Write(crlf, 0, (int) crlf.Length);
+ memStream.Write(dDash, 0, (int) dDash.Length);
+ memStream.Write(boundary, 0, (int) boundary.Length);
+ memStream.Write(crlf, 0, (int) crlf.Length);
+ memStream.Write(contentTypeHeader, 0, (int) contentTypeHeader.Length);
+
+ byte[] type=System.Text.Encoding.UTF8.GetBytes((string)types[i]);
+ memStream.Write(type, 0, (int) type.Length);
+ memStream.Write(crlf, 0, (int) crlf.Length);
+ memStream.Write(dispositionHeader, 0, (int) dispositionHeader.Length);
+
+ #if !UNITY_METRO_API && !UNITY_WP8_API
+ string headerName = System.Text.Encoding.UTF8.HeaderName;
+ #else
+ string headerName = "";
+ #endif
+ // Headers must be 7 bit clean, so encode as per rfc1522 using quoted-printable if needed.
+ string encodedFieldName=(string)fieldNames[i];
+ if(!WWWTranscoder.SevenBitClean(encodedFieldName, System.Text.Encoding.UTF8) || encodedFieldName.IndexOf("=?") > -1) {
+ encodedFieldName="=?"+headerName+"?Q?"+WWWTranscoder.QPEncode(encodedFieldName, System.Text.Encoding.UTF8) + "?=";
+ }
+ byte[] name=System.Text.Encoding.UTF8.GetBytes(encodedFieldName);
+ memStream.Write(name, 0, (int) name.Length);
+ memStream.Write(endQuote, 0, (int) endQuote.Length);
+
+ if(fileNames[i] != null) {
+ // Headers must be 7 bit clean, so encode as per rfc1522 using quoted-printable if needed.
+ string encodedFileName=(string)fileNames[i];
+ if(!WWWTranscoder.SevenBitClean(encodedFileName, System.Text.Encoding.UTF8) || encodedFileName.IndexOf("=?") > -1) {
+ encodedFileName="=?"+headerName+"?Q?"+WWWTranscoder.QPEncode(encodedFileName, System.Text.Encoding.UTF8) + "?=";
+ }
+ byte[] fileName=System.Text.Encoding.UTF8.GetBytes(encodedFileName);
+
+ memStream.Write(fileNameField, 0, (int) fileNameField.Length);
+ memStream.Write(fileName, 0, (int) fileName.Length);
+ memStream.Write(endQuote, 0, (int) endQuote.Length);
+
+ }
+ memStream.Write(crlf, 0, (int) crlf.Length);
+ memStream.Write(crlf, 0, (int) crlf.Length);
+
+ byte[] formBytes = (byte[])formData[i];
+ memStream.Write(formBytes, 0, (int) formBytes.Length);
+ }
+ memStream.Write(crlf, 0, (int) crlf.Length);
+ memStream.Write(dDash, 0, (int) dDash.Length);
+ memStream.Write(boundary, 0, (int) boundary.Length);
+ memStream.Write(dDash, 0, (int) dDash.Length);
+ memStream.Write(crlf, 0, (int) crlf.Length);
+
+ return memStream.ToArray();
+ }
+ }
+ else {
+ byte[] ampersand = WWW.DefaultEncoding.GetBytes("&");
+ byte[] equal = WWW.DefaultEncoding.GetBytes("=");
+
+ using(MemoryStream memStream = new MemoryStream(1024))
+ {
+ for(int i=0; i < formData.Count; i++) {
+ byte[] name=WWWTranscoder.URLEncode(System.Text.Encoding.UTF8.GetBytes((string)fieldNames[i]));
+ byte[] formBytes = (byte[])formData[i];
+ byte[] value=WWWTranscoder.URLEncode(formBytes);
+
+ if(i>0) memStream.Write(ampersand, 0, (int) ampersand.Length);
+ memStream.Write(name, 0, (int) name.Length);
+ memStream.Write(equal, 0, (int) equal.Length);
+ memStream.Write(value, 0, (int) value.Length);
+ }
+ return memStream.ToArray();
+ }
+ }
+ }
+}
+
+
+END
+//*undocumented*
+CONDITIONAL !UNITY_WEBGL
+CLASS internal WWWTranscoder
+
+ CSRAW private static byte [] ucHexChars = WWW.DefaultEncoding.GetBytes("0123456789ABCDEF");
+ CSRAW private static byte [] lcHexChars = WWW.DefaultEncoding.GetBytes("0123456789abcdef");
+ CSRAW private static byte urlEscapeChar=(byte)'%';
+ CSRAW private static byte urlSpace=(byte)'+';
+ CSRAW private static byte [] urlForbidden=WWW.DefaultEncoding.GetBytes("@&;:<>=?\"'/\\!#%+$,{}|^[]`");
+ CSRAW private static byte qpEscapeChar=(byte)'=';
+ CSRAW private static byte qpSpace=(byte)'_';
+ CSRAW private static byte [] qpForbidden=WWW.DefaultEncoding.GetBytes("&;=?\"'%+_");
+
+ CSRAW private static byte Hex2Byte (byte[] b, int offset) {
+ byte result=(byte)0;
+
+ for (int i = offset; i < offset+2; i++ ) {
+ result *= 16;
+ int d=b[i];
+
+ if (d >= 48 && d <= 57) // 0 - 9
+ d -= 48;
+ else if (d >= 65 && d <= 75) // A -F
+ d -= 55;
+ else if (d >= 97 && d <= 102) // a - f
+ d -= 87;
+ if (d>15) {
+ return 63; // ?
+ }
+
+ result += (byte)d;
+ }
+
+ return result;
+ }
+
+ CSRAW private static byte[] Byte2Hex (byte b, byte[] hexChars) {
+ byte[] dest= new byte[2];
+ dest[0]=hexChars[ b >> 4 ];
+ dest[1]=hexChars[ b &0xf ];
+ return dest;
+ }
+
+
+ CSRAW public static string URLEncode(string toEncode, Encoding e = Encoding.UTF8) {
+ byte[] data = Encode(e.GetBytes(toEncode), urlEscapeChar, urlSpace, urlForbidden, false);
+ return WWW.DefaultEncoding.GetString(data, 0, data.Length);
+ }
+
+ CSRAW public static byte[] URLEncode(byte[] toEncode) {
+ return Encode(toEncode, urlEscapeChar, urlSpace, urlForbidden, false);
+ }
+
+ CSRAW public static string QPEncode(string toEncode, Encoding e = Encoding.UTF8) {
+ byte[] data = Encode(e.GetBytes(toEncode), qpEscapeChar, qpSpace, qpForbidden, true);
+ return WWW.DefaultEncoding.GetString(data, 0, data.Length);
+ }
+
+ CSRAW public static byte[] QPEncode(byte[] toEncode) {
+ return Encode(toEncode, qpEscapeChar, qpSpace, qpForbidden, true);
+ }
+
+ CSRAW public static byte[] Encode(byte[] input, byte escapeChar, byte space, byte[] forbidden, bool uppercase) {
+ using(MemoryStream memStream = new MemoryStream(input.Length*2)) {
+ // encode
+ for(int i=0; i < input.Length; i++) {
+ if(input[i] == 32) {
+ memStream.WriteByte(space);
+ } else if(input[i] < 32 || input[i] > 126 || ByteArrayContains(forbidden, input[i])){
+ memStream.WriteByte(escapeChar);
+ memStream.Write(Byte2Hex(input[i],uppercase?ucHexChars:lcHexChars),0,2);
+ } else {
+ memStream.WriteByte(input[i]);
+ }
+ }
+
+ return memStream.ToArray();
+ }
+
+ }
+
+ CSRAW private static bool ByteArrayContains(byte[] array, byte b)
+ {
+#if !UNITY_FLASH
+ return (System.Array.IndexOf(array, b) != -1);
+#else
+ for(int i = 0; i<array.Length; i++){
+ if(array[i] == b)
+ return true;
+ }
+ return false;
+#endif
+ }
+
+ CSRAW public static string URLDecode(string toEncode, Encoding e = Encoding.UTF8) {
+ byte []data = Decode(WWW.DefaultEncoding.GetBytes(toEncode), urlEscapeChar, urlSpace);
+ return e.GetString(data, 0, data.Length);
+ }
+
+ CSRAW public static byte[] URLDecode(byte[] toEncode) {
+ return Decode(toEncode, urlEscapeChar, urlSpace);
+ }
+
+ CSRAW public static string QPDecode(string toEncode, Encoding e = Encoding.UTF8) {
+ byte[] data = Decode(WWW.DefaultEncoding.GetBytes(toEncode), qpEscapeChar, qpSpace);
+ return e.GetString(data, 0, data.Length);
+ }
+
+ CSRAW public static byte[] QPDecode(byte[] toEncode){
+ return Decode(toEncode, qpEscapeChar, qpSpace);
+ }
+
+ CSRAW public static byte[] Decode(byte[] input, byte escapeChar, byte space) {
+ using(MemoryStream memStream = new MemoryStream(input.Length)) {
+ // decode
+ for(int i=0; i < input.Length; i++) {
+ if(input[i] == space) {
+ memStream.WriteByte((byte)32);
+ } else if(input[i] == escapeChar && i+2 < input.Length) {
+ i++;
+ memStream.WriteByte(Hex2Byte(input,i++));
+ } else {
+ memStream.WriteByte(input[i]);
+ }
+ }
+
+ return memStream.ToArray();
+ }
+
+ }
+
+ CSRAW public static bool SevenBitClean(string s, Encoding e = Encoding.UTF8) {
+ return SevenBitClean(e.GetBytes(s));
+ }
+
+ CSRAW public static bool SevenBitClean(byte[] input) {
+ for(int i= 0; i<input.Length; i++) {
+ if (input[i] < 32 || input[i] > 126 )
+ return false;
+ }
+
+ return true;
+ }
+
+
+END
+
+CSRAW
+[System.Obsolete ("this API is not for public use.")]
+public struct CacheIndex {
+ public string name;
+ public int bytesUsed;
+ public int expires;
+}
+
+C++RAW
+
+struct MonoCacheIndex {
+ ScriptingStringPtr name;
+ int bytesUsed;
+ int expires;
+};
+
+// The Caching class lets you manage cached AssetBundles, downloaded using WWW::ref::LoadFromCacheOrDownload.
+CONDITIONAL ENABLE_CACHING
+CLASS Caching
+
+// (This is a WebPlayer-only function)
+CSRAW public static bool Authorize (string name, string domain, long size, string signature)
+{
+ return Authorize(name, domain, size, -1, signature);
+}
+///*listonly*
+CUSTOM static bool Authorize(string name, string domain, long size, int expiration, string signature)
+{
+ return GetCachingManager().Authorize(name, domain, size, expiration, signature);
+}
+
+OBSOLETE warning Size is now specified as a long
+CSRAW public static bool Authorize(string name, string domain, int size, int expiration, string signature)
+{
+ return Authorize(name, domain, (long)size, expiration, signature);
+}
+
+OBSOLETE warning Size is now specified as a long
+CSRAW public static bool Authorize (string name, string domain, int size, string signature)
+{
+ return Authorize( name, domain, (long)size, signature);
+}
+
+// Delete all AssetBundle content that has been cached by the current application.
+CUSTOM static bool CleanCache ()
+{
+ if (GetCachingManager().GetAuthorizationLevel() >= CachingManager::kAuthorizationUser)
+ return GetCachingManager().GetCurrentCache().CleanCache();
+ else
+ {
+ ErrorString("Unauthorized use of Caching API.");
+ return false;
+ }
+}
+
+OBSOLETE warning this API is not for public use.
+CUSTOM static bool CleanNamedCache (string name)
+{
+ if (GetCachingManager().GetAuthorizationLevel() >= CachingManager::kAuthorizationAdmin)
+ return GetCachingManager().CleanCache(name);
+ else
+ {
+ ErrorString("Unauthorized use of Caching API.");
+ return false;
+ }
+}
+
+OBSOLETE warning This function is obsolete and has no effect.
+CUSTOM static bool DeleteFromCache (string url)
+{
+ return false;
+}
+
+OBSOLETE warning This function is obsolete and will always return -1. Use IsVersionCached instead.
+CUSTOM static int GetVersionFromCache (string url)
+{
+ return -1;
+}
+
+// Checks if an AssetBundle is cached.
+CUSTOM static bool IsVersionCached (string url, int version)
+{
+ return GetCachingManager().IsCached(url, version);
+}
+
+// Bumps the timestamp of a cached file to be the current time.
+CUSTOM static bool MarkAsUsed (string url, int version)
+{
+ return GetCachingManager().MarkAsUsed(url, version);
+}
+
+OBSOLETE warning this API is not for public use.
+CUSTOM_PROP static CacheIndex[] index
+{
+#if !UNITY_WINRT && ENABLE_WWW
+ if (GetCachingManager().GetAuthorizationLevel() < CachingManager::kAuthorizationAdmin)
+ {
+ ErrorString("Unauthorized use of Caching API.");
+ return NULL;
+ }
+ vector<Cache*> &indices = GetGlobalCachingManager().GetCacheIndices();
+ MonoArray *monoIndices = mono_array_new (mono_domain_get (), GetMonoManager().GetCommonClasses().cacheIndex, indices.size() );
+
+ for(int i=0; i<indices.size(); i++)
+ {
+ GetMonoArrayElement<MonoCacheIndex>(monoIndices, i).name = scripting_string_new (indices[i]->m_Name);
+ GetMonoArrayElement<MonoCacheIndex>(monoIndices, i).bytesUsed = indices[i]->m_BytesUsed;
+ GetMonoArrayElement<MonoCacheIndex>(monoIndices, i).expires = indices[i]->m_Expires;
+ }
+ return monoIndices;
+#else
+ return SCRIPTING_NULL;
+#endif
+}
+
+// The number of currently unused bytes in the cache.
+CUSTOM_PROP static long spaceFree
+{
+ return GetCachingManager().GetCachingDiskSpaceFree();
+}
+
+// The total number of bytes that can potentially be allocated for caching.
+CUSTOM_PROP static long maximumAvailableDiskSpace
+{
+ return GetCachingManager().GetMaximumDiskSpaceAvailable();
+}
+{
+ GetCachingManager().SetMaximumDiskSpaceAvailable(value);
+}
+
+// Used disk space in bytes.
+CUSTOM_PROP static long spaceOccupied
+{
+ return GetCachingManager().GetCachingDiskSpaceUsed();
+}
+
+OBSOLETE warning Please use Caching.spaceFree instead
+CUSTOM_PROP static int spaceAvailable
+{
+ return GetCachingManager().GetCachingDiskSpaceFree();
+}
+
+OBSOLETE warning Please use Caching.spaceOccupied instead
+CUSTOM_PROP static int spaceUsed
+{
+ return GetCachingManager().GetCachingDiskSpaceUsed();
+}
+
+// The number of seconds that an AssetBundle may remain unused in the cache before it is automatically deleted.
+CUSTOM_PROP static int expirationDelay
+{
+ return GetCachingManager().GetExpirationDelay();
+}
+{
+ if (GetCachingManager().GetAuthorizationLevel() >= CachingManager::kAuthorizationUser)
+ GetCachingManager().GetCurrentCache().SetExpirationDelay(value);
+}
+
+// Is Caching enabled?
+CUSTOM_PROP static bool enabled
+{
+ return GetCachingManager().GetEnabled();
+}
+{
+ if (GetCachingManager().GetAuthorizationLevel() >= CachingManager::kAuthorizationAdmin)
+ GetCachingManager().SetEnabled(value);
+ else
+ ErrorString("Unable to assign a value to Caching.enabled. This property is read-only.");
+}
+
+// Is caching ready?
+CUSTOM_PROP static bool ready
+{
+ return GetCachingManager().GetIsReady();
+}
+
+CONDITIONAL UNITY_IPHONE_API
+CUSTOM static void SetNoBackupFlag (string url, int version)
+{
+#if UNITY_IPHONE
+ GetCachingManager().SetNoBackupFlag(url, version);
+#endif
+}
+
+CONDITIONAL UNITY_IPHONE_API
+CUSTOM static void ResetNoBackupFlag (string url, int version)
+{
+#if UNITY_IPHONE
+ GetCachingManager().ResetNoBackupFlag(url, version);
+#endif
+}
+
+END
+
+CSRAW
+
+#endif //ENABLE_WWW
+
+CONDITIONAL !UNITY_FLASH && !UNITY_WEBGL && !UNITY_WINRT
+CLASS internal UnityLogWriter : System.IO.TextWriter
+
+ THREAD_SAFE
+ CUSTOM static void WriteStringToUnityLog(string s)
+ {
+ //this userstring could contains things like "%20", which we should make sure printf does not interpet
+ //as if we wanted to print some argument.
+ if (s.IsNull())
+ return;
+
+ std::string utf8 = s.AsUTF8();
+ printf_console("%s",utf8.c_str());
+ }
+
+ CSRAW
+ public static void Init()
+ {
+ System.Console.SetOut(new UnityLogWriter());
+ }
+ public override System.Text.Encoding Encoding
+ {
+ get { return System.Text.Encoding.UTF8; }
+ }
+ public override void Write(char value)
+ {
+ WriteStringToUnityLog(value.ToString());
+ }
+ public override void Write(string s)
+ {
+ WriteStringToUnityLog(s);
+ }
+
+END
+
+CLASS internal UnityString
+ CSRAW
+ public static string Format(string fmt, params object[] args)
+ {
+ return String.Format(fmt, args);
+ }
+END
+
+} \ No newline at end of file
diff --git a/Runtime/Export/WP8.cs b/Runtime/Export/WP8.cs
new file mode 100644
index 0000000..773886c
--- /dev/null
+++ b/Runtime/Export/WP8.cs
@@ -0,0 +1,20 @@
+#if UNITY_WP8 && UNITY_WP8_NATIVE_COMPILER
+using System;
+using System.Runtime.InteropServices;
+
+namespace System
+{
+ [AttributeUsageAttribute(AttributeTargets.Class|AttributeTargets.Struct|AttributeTargets.Enum|AttributeTargets.Delegate, Inherited = false)]
+ [ComVisible(true)]
+ public class SerializableAttribute : Attribute
+ {
+
+ };
+
+ [AttributeUsageAttribute(AttributeTargets.Field, Inherited = false)]
+ [ComVisibleAttribute(true)]
+ public class NonSerializedAttribute : Attribute
+ {
+ };
+}
+#endif
diff --git a/Runtime/Export/WP8/WindowsPhone.txt b/Runtime/Export/WP8/WindowsPhone.txt
new file mode 100644
index 0000000..cf0ea9b
--- /dev/null
+++ b/Runtime/Export/WP8/WindowsPhone.txt
@@ -0,0 +1,32 @@
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using UnityEngineInternal;
+
+namespace UnityEngine.WindowsPhone
+{
+CONDITIONAL UNITY_WP8_API
+CLASS Media
+
+ CSRAW public enum MediaState
+ {
+ Stopped,
+ Playing,
+ Paused
+ }
+
+ /// BE CAREFUL WHEN CHANGING THIS. It's rewritten in DLL postprocessing, so any changes you make here will be overwritten
+ CSRAW public static MediaState mediaPlayerState
+ {
+ get
+ {
+ return (MediaState)0;
+ }
+ }
+
+CSRAW
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/WSA/WSAApplication.txt b/Runtime/Export/WSA/WSAApplication.txt
new file mode 100644
index 0000000..79a6c2f
--- /dev/null
+++ b/Runtime/Export/WSA/WSAApplication.txt
@@ -0,0 +1,130 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#if UNITY_METRO
+#include "PlatformDependent/MetroPlayer/AppCallbacks.h"
+#endif
+
+using namespace Unity;
+
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using UnityEngineInternal;
+
+namespace UnityEngine.WSA
+{
+CONDITIONAL UNITY_METRO_API
+public delegate void AppCallbackItem();
+
+CONDITIONAL UNITY_METRO_API
+public delegate void WindowSizeChanged(int width, int height);
+
+CONDITIONAL UNITY_METRO_API
+ENUM WindowActivationState
+ CodeActivated = 0,
+
+ Deactivated = 1,
+
+ PointerActivated = 2
+END
+
+CONDITIONAL UNITY_METRO_API
+public delegate void WindowActivated(WindowActivationState state);
+
+CONDITIONAL UNITY_METRO_API
+CLASS Application
+
+ public static event WindowSizeChanged windowSizeChanged;
+ public static event WindowActivated windowActivated;
+
+ CSRAW public static string arguments
+ {
+ get {
+ return GetAppArguments();
+ }
+ }
+
+ CUSTOM private static string GetAppArguments()
+ {
+ #if UNITY_METRO
+ std::string args = ConvertStringToUtf8(UnityPlayer::AppCallbacks::Instance->GetAppArguments());
+ return scripting_string_new(args);
+ #else
+ return scripting_string_new("");
+ #endif
+ }
+
+ CSRAW internal static void InvokeWindowSizeChangedEvent(int width, int height)
+ {
+ if (windowSizeChanged != null)
+ windowSizeChanged.Invoke(width, height);
+ }
+
+ CSRAW internal static void InvokeWindowActivatedEvent(WindowActivationState state)
+ {
+ if (windowActivated != null) windowActivated.Invoke(state);
+ }
+
+ CSRAW public static void InvokeOnAppThread(AppCallbackItem item, bool waitUntilDone)
+ {
+ #if UNITY_EDITOR
+ item();
+ #else
+ InternalInvokeOnAppThread(item, waitUntilDone);
+ #endif
+ }
+ CSRAW public static void InvokeOnUIThread(AppCallbackItem item, bool waitUntilDone)
+ {
+ #if UNITY_EDITOR
+ item();
+ #else
+ InternalInvokeOnUIThread(item, waitUntilDone);
+ #endif
+ }
+
+ CUSTOM internal static void InternalInvokeOnAppThread(AppCallbackItem item, bool waitUntilDone)
+ {
+ #if UNITY_METRO
+ UnityPlayer::AppCallbacks::Instance->InvokeOnAppThread(ref new UnityPlayer::AppCallbackItem(
+ GetWinRTTypeInformation()->CastScriptingObjectToAppCallbackItemStub(item)), waitUntilDone);
+ #endif
+ }
+ CUSTOM internal static void InternalInvokeOnUIThread(AppCallbackItem item, bool waitUntilDone)
+ {
+ #if UNITY_METRO
+ UnityPlayer::AppCallbacks::Instance->InvokeOnUIThread(ref new UnityPlayer::AppCallbackItem(
+ GetWinRTTypeInformation()->CastScriptingObjectToAppCallbackItemStub(item)), waitUntilDone);
+ #endif
+ }
+
+ CUSTOM public static bool RunningOnAppThread()
+ {
+ #if UNITY_METRO
+ return UnityPlayer::AppCallbacks::Instance->RunningOnAppThread();
+ #else
+ return true;
+ #endif
+ }
+
+ CUSTOM public static bool RunningOnUIThread()
+ {
+ #if UNITY_METRO
+ return UnityPlayer::AppCallbacks::Instance->RunningOnUIThread();
+ #else
+ return true;
+ #endif
+ }
+
+
+CSRAW
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/WSA/WSATiles.txt b/Runtime/Export/WSA/WSATiles.txt
new file mode 100644
index 0000000..71d7ca4
--- /dev/null
+++ b/Runtime/Export/WSA/WSATiles.txt
@@ -0,0 +1,848 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#if UNITY_METRO
+ #include "PlatformDependent/MetroPlayer/MetroTiles.h"
+ typedef Windows::UI::Notifications::TileTemplateType TileTemplate;
+ typedef Windows::UI::Notifications::ToastTemplateType ToastTemplate;
+#else
+ typedef int TileTemplate;
+ typedef int ToastTemplate;
+#endif
+
+CSRAW
+using UnityEngine;
+
+namespace UnityEngine.WSA
+{
+
+CONDITIONAL UNITY_METRO_API
+ENUM TileTemplate
+ TileSquare150x150Image = 0,
+ TileSquare150x150Block = 1,
+ TileSquare150x150Text01 = 2,
+ TileSquare150x150Text02 = 3,
+ TileSquare150x150Text03 = 4,
+ TileSquare150x150Text04 = 5,
+ TileSquare150x150PeekImageAndText01 = 6,
+ TileSquare150x150PeekImageAndText02 = 7,
+ TileSquare150x150PeekImageAndText03 = 8,
+ TileSquare150x150PeekImageAndText04 = 9,
+ TileWide310x150Image = 10,
+ TileWide310x150ImageCollection = 11,
+ TileWide310x150ImageAndText01 = 12,
+ TileWide310x150ImageAndText02 = 13,
+ TileWide310x150BlockAndText01 = 14,
+ TileWide310x150BlockAndText02 = 15,
+ TileWide310x150PeekImageCollection01 = 16,
+ TileWide310x150PeekImageCollection02 = 17,
+ TileWide310x150PeekImageCollection03 = 18,
+ TileWide310x150PeekImageCollection04 = 19,
+ TileWide310x150PeekImageCollection05 = 20,
+ TileWide310x150PeekImageCollection06 = 21,
+ TileWide310x150PeekImageAndText01 = 22,
+ TileWide310x150PeekImageAndText02 = 23,
+ TileWide310x150PeekImage01 = 24,
+ TileWide310x150PeekImage02 = 25,
+ TileWide310x150PeekImage03 = 26,
+ TileWide310x150PeekImage04 = 27,
+ TileWide310x150PeekImage05 = 28,
+ TileWide310x150PeekImage06 = 29,
+ TileWide310x150SmallImageAndText01 = 30,
+ TileWide310x150SmallImageAndText02 = 31,
+ TileWide310x150SmallImageAndText03 = 32,
+ TileWide310x150SmallImageAndText04 = 33,
+ TileWide310x150SmallImageAndText05 = 34,
+ TileWide310x150Text01 = 35,
+ TileWide310x150Text02 = 36,
+ TileWide310x150Text03 = 37,
+ TileWide310x150Text04 = 38,
+ TileWide310x150Text05 = 39,
+ TileWide310x150Text06 = 40,
+ TileWide310x150Text07 = 41,
+ TileWide310x150Text08 = 42,
+ TileWide310x150Text09 = 43,
+ TileWide310x150Text10 = 44,
+ TileWide310x150Text11 = 45,
+ TileSquare310x310BlockAndText01 = 46,
+ TileSquare310x310BlockAndText02 = 47,
+ TileSquare310x310Image = 48,
+ TileSquare310x310ImageAndText01 = 49,
+ TileSquare310x310ImageAndText02 = 50,
+ TileSquare310x310ImageAndTextOverlay01 = 51,
+ TileSquare310x310ImageAndTextOverlay02 = 52,
+ TileSquare310x310ImageAndTextOverlay03 = 53,
+ TileSquare310x310ImageCollectionAndText01 = 54,
+ TileSquare310x310ImageCollectionAndText02 = 55,
+ TileSquare310x310ImageCollection = 56,
+ TileSquare310x310SmallImagesAndTextList01 = 57,
+ TileSquare310x310SmallImagesAndTextList02 = 58,
+ TileSquare310x310SmallImagesAndTextList03 = 59,
+ TileSquare310x310SmallImagesAndTextList04 = 60,
+ TileSquare310x310Text01 = 61,
+ TileSquare310x310Text02 = 62,
+ TileSquare310x310Text03 = 63,
+ TileSquare310x310Text04 = 64,
+ TileSquare310x310Text05 = 65,
+ TileSquare310x310Text06 = 66,
+ TileSquare310x310Text07 = 67,
+ TileSquare310x310Text08 = 68,
+ TileSquare310x310TextList01 = 69,
+ TileSquare310x310TextList02 = 70,
+ TileSquare310x310TextList03 = 71,
+ TileSquare310x310SmallImageAndText01 = 72,
+ TileSquare310x310SmallImagesAndTextList05 = 73,
+ TileSquare310x310Text09 = 74,
+END
+
+
+CONDITIONAL UNITY_METRO_API
+ENUM ToastTemplate
+ ToastImageAndText01 = 0,
+ ToastImageAndText02 = 1,
+ ToastImageAndText03 = 2,
+ ToastImageAndText04 = 3,
+ ToastText01 = 4,
+ ToastText02 = 5,
+ ToastText03 = 6,
+ ToastText04 = 7,
+END
+
+
+CONDITIONAL UNITY_METRO_API
+ENUM TileForegroundText
+{
+ Default = -1,
+ Dark = 0,
+ Light = 1
+}
+
+#if UNITY_METRO_API
+public struct SecondaryTileData
+{
+ public string arguments;
+ private Color32 background;
+ public Color32 backgroundColor
+ {
+ get {
+ return background;
+ }
+ set {
+ background = value;
+ backgroundColorSet = true;
+ }
+ }
+ public bool backgroundColorSet;
+ public string displayName;
+ public TileForegroundText foregroundText;
+ public string lockScreenBadgeLogo;
+ public bool lockScreenDisplayBadgeAndTileText;
+ public string phoneticName;
+ public bool roamingEnabled;
+ public bool showNameOnSquare150x150Logo;
+ public bool showNameOnSquare310x310Logo;
+ public bool showNameOnWide310x150Logo;
+ public string square150x150Logo;
+ public string square30x30Logo;
+ public string square310x310Logo;
+ public string square70x70Logo;
+ public string tileId;
+ public string wide310x150Logo;
+
+ public SecondaryTileData(string id, string displayName)
+ {
+ arguments = "";
+ background = new UnityEngine.Color32(0, 0, 0, 0);
+ backgroundColorSet = false;
+ this.displayName = displayName;
+ foregroundText = TileForegroundText.Default;
+ lockScreenBadgeLogo = "";
+ lockScreenDisplayBadgeAndTileText = false;
+ phoneticName = "";
+ roamingEnabled = true; // this is default in Win 8.1
+ showNameOnSquare150x150Logo = true;
+ showNameOnSquare310x310Logo = false;
+ showNameOnWide310x150Logo = false;
+ square150x150Logo = "";
+ square30x30Logo = "";
+ square310x310Logo = "";
+ square70x70Logo = "";
+ tileId = id;
+ wide310x150Logo = "";
+ }
+}
+#endif
+
+CONDITIONAL UNITY_METRO_API
+CLASS Tile
+
+ CSRAW private string m_TileId;
+
+ CSRAW private static Tile s_MainTile;
+
+ CSRAW private Tile(string tileId)
+ {
+ m_TileId = tileId;
+ }
+
+ CSRAW public static Tile main
+ {
+ get {
+ if (s_MainTile == null)
+ s_MainTile = new Tile("");
+ return s_MainTile;
+ }
+ }
+
+ CUSTOM public static string GetTemplate(TileTemplate templ)
+ {
+ #if UNITY_METRO
+ return scripting_string_new(metro::Tiles::GetTemplate(templ).c_str());
+ #else
+ return scripting_string_new("");
+ #endif
+ }
+
+ CSRAW public void Update(string xml)
+ {
+ Update(m_TileId, xml);
+ }
+
+ CUSTOM private static void Update(string tileId, string xml)
+ {
+ #if UNITY_METRO
+ metro::Tile::Ptr tile = metro::Tiles::Instance(tileId);
+ if (tile)
+ tile->Update(xml);
+ #endif
+ }
+
+ CSRAW public void Update(string medium, string wide, string large, string text)
+ {
+ UpdateImageAndText(m_TileId, medium, wide, large, text);
+ }
+
+ CUSTOM private static void UpdateImageAndText(string tileId, string medium, string wide, string large, string text)
+ {
+ #if UNITY_METRO
+ metro::Tile::Ptr tile = metro::Tiles::Instance(tileId);
+ if (tile)
+ tile->Update(medium, wide, large, text);
+ #endif
+ }
+
+ CSRAW public void PeriodicUpdate(string uri, float interval)
+ {
+ PeriodicUpdate(m_TileId, uri, interval);
+ }
+
+ CUSTOM private static void PeriodicUpdate(string tileId, string uri, float interval)
+ {
+ #if UNITY_METRO
+ metro::Tile::Ptr tile = metro::Tiles::Instance(tileId);
+ if (tile)
+ tile->PeriodicUpdate(uri, interval);
+ #endif
+ }
+
+ CSRAW public void StopPeriodicUpdate()
+ {
+ StopPeriodicUpdate(m_TileId);
+ }
+
+ CUSTOM private static void StopPeriodicUpdate(string tileId)
+ {
+ #if UNITY_METRO
+ metro::Tile::Ptr tile = metro::Tiles::Instance(tileId);
+ if (tile)
+ tile->StopPeriodicUpdate();
+ #endif
+ }
+
+ CSRAW public void UpdateBadgeImage(string image)
+ {
+ UpdateBadgeImage(m_TileId, image);
+ }
+
+ CUSTOM private static void UpdateBadgeImage(string tileId, string image)
+ {
+ #if UNITY_METRO
+ metro::Tile::Ptr tile = metro::Tiles::Instance(tileId);
+ if (tile)
+ tile->UpdateBadgeImage(image);
+ #endif
+ }
+
+ CSRAW public void UpdateBadgeNumber(float number)
+ {
+ UpdateBadgeNumber(m_TileId, number);
+ }
+
+ CUSTOM private static void UpdateBadgeNumber(string tileId, float number)
+ {
+ #if UNITY_METRO
+ metro::Tile::Ptr tile = metro::Tiles::Instance(tileId);
+ if (tile)
+ tile->UpdateBadgeNumber(number);
+ #endif
+ }
+
+ CSRAW public void RemoveBadge()
+ {
+ RemoveBadge(m_TileId);
+ }
+
+ CUSTOM private static void RemoveBadge(string tileId)
+ {
+ #if UNITY_METRO
+ metro::Tile::Ptr tile = metro::Tiles::Instance(tileId);
+ if (tile)
+ tile->RemoveBadge();
+ #endif
+ }
+
+ CSRAW public void PeriodicBadgeUpdate(string uri, float interval)
+ {
+ PeriodicBadgeUpdate(m_TileId, uri, interval);
+ }
+
+ CUSTOM private static void PeriodicBadgeUpdate(string tileId, string uri, float interval)
+ {
+ #if UNITY_METRO
+ metro::Tile::Ptr tile = metro::Tiles::Instance(tileId);
+ if (tile)
+ tile->PeriodicBadgeUpdate(uri, interval);
+ #endif
+ }
+
+ CSRAW public void StopPeriodicBadgeUpdate()
+ {
+ StopPeriodicBadgeUpdate(m_TileId);
+ }
+
+ CUSTOM private static void StopPeriodicBadgeUpdate(string tileId)
+ {
+ #if UNITY_METRO
+ metro::Tile::Ptr tile = metro::Tiles::Instance(tileId);
+ if (tile)
+ tile->StopPeriodicBadgeUpdate();
+ #endif
+ }
+
+ CSRAW public string id
+ {
+ get {
+ return m_TileId;
+ }
+ }
+
+
+ CSRAW public bool hasUserConsent
+ {
+ get {
+ return HasUserConsent(m_TileId);
+ }
+ }
+
+ CUSTOM private static bool HasUserConsent(string tileId)
+ {
+ #if UNITY_METRO
+ metro::Tile::Ptr tile = metro::Tiles::Instance(tileId);
+ return tile ? tile->HasUserConsent() : true; // No tile means user removed/rejected
+ #else
+ return false;
+ #endif
+ }
+
+ CSRAW public bool exists
+ {
+ get {
+ return Exists(m_TileId);
+ }
+ }
+
+ CUSTOM public static bool Exists(string tileId)
+ {
+ #if UNITY_METRO
+ return metro::Tiles::Instance(tileId) != NULL;
+ #else
+ return false;
+ #endif
+ }
+
+ CSRAW private static string[] MakeSecondaryTileSargs(SecondaryTileData data)
+ {
+ string[] sargs = new string[10];
+ sargs[0] = data.arguments;
+ sargs[1] = data.displayName;
+ sargs[2] = data.lockScreenBadgeLogo;
+ sargs[3] = data.phoneticName;
+ sargs[4] = data.square150x150Logo;
+ sargs[5] = data.square30x30Logo;
+ sargs[6] = data.square310x310Logo;
+ sargs[7] = data.square70x70Logo;
+ sargs[8] = data.tileId;
+ sargs[9] = data.wide310x150Logo;
+ return sargs;
+ }
+
+ CSRAW private static bool[] MakeSecondaryTileBargs(SecondaryTileData data)
+ {
+ bool[] bargs = new bool[6];
+ bargs[0] = data.backgroundColorSet;
+ bargs[1] = data.lockScreenDisplayBadgeAndTileText;
+ bargs[2] = data.roamingEnabled;
+ bargs[3] = data.showNameOnSquare150x150Logo;
+ bargs[4] = data.showNameOnSquare310x310Logo;
+ bargs[5] = data.showNameOnWide310x150Logo;
+ return bargs;
+ }
+
+ CSRAW public static Tile CreateOrUpdateSecondary(SecondaryTileData data)
+ {
+ string[] sargs = MakeSecondaryTileSargs(data);
+ bool[] bargs = MakeSecondaryTileBargs(data);
+ Color32 backgroundColor = data.backgroundColor;
+
+ string tileId = CreateOrUpdateSecondaryTile(
+ sargs,
+ bargs,
+ ref backgroundColor,
+ (int) data.foregroundText
+ );
+ if (string.IsNullOrEmpty(tileId))
+ return null;
+ return new Tile(tileId);
+ }
+
+ // On Win 8.0 there is limitation on argument count, so we pass them as arrays
+ CUSTOM private static string CreateOrUpdateSecondaryTile(
+ string[] sargs,
+ bool[] bargs,
+ ref Color32 backgroundColor,
+ int foregroundText
+ )
+ {
+ #if UNITY_METRO
+ std::string arguments = GetStringFromArray(sargs, 0);
+ std::string displayName = GetStringFromArray(sargs, 1);
+ std::string lockScreenBadgeLogo = GetStringFromArray(sargs, 2);
+ std::string phoneticName = GetStringFromArray(sargs, 3);
+ std::string square150x150Logo = GetStringFromArray(sargs, 4);
+ std::string square30x30Logo = GetStringFromArray(sargs, 5);
+ std::string square310x310Logo = GetStringFromArray(sargs, 6);
+ std::string square70x70Logo = GetStringFromArray(sargs, 7);
+ std::string tileId = GetStringFromArray(sargs, 8);
+ std::string wide310x150Logo = GetStringFromArray(sargs, 9);
+
+ bool backgroundColorSet = GetBoolFromArray(bargs, 0);
+ bool lockScreenDisplayBadgeAndTileText = GetBoolFromArray(bargs, 1);
+ bool roamingEnabled = GetBoolFromArray(bargs, 2);
+ bool showNameOnSquare150x150Logo = GetBoolFromArray(bargs, 3);
+ bool showNameOnSquare310x310Logo = GetBoolFromArray(bargs, 4);
+ bool showNameOnWide310x150Logo = GetBoolFromArray(bargs, 5);
+
+
+ return scripting_string_new(metro::Tiles::CreateOrUpdateSecondary(
+ arguments,
+ backgroundColor,
+ backgroundColorSet,
+ displayName,
+ foregroundText,
+ lockScreenBadgeLogo,
+ lockScreenDisplayBadgeAndTileText,
+ phoneticName,
+ roamingEnabled,
+ showNameOnSquare150x150Logo,
+ showNameOnSquare310x310Logo,
+ showNameOnWide310x150Logo,
+ square150x150Logo,
+ square30x30Logo,
+ square310x310Logo,
+ square70x70Logo,
+ tileId,
+ wide310x150Logo
+ )->GetId());
+ #else
+ return scripting_string_new("");
+ #endif
+ }
+
+ CSRAW public static Tile CreateOrUpdateSecondary(SecondaryTileData data, Vector2 pos)
+ {
+ string[] sargs = MakeSecondaryTileSargs(data);
+ bool[] bargs = MakeSecondaryTileBargs(data);
+ Color32 backgroundColor = data.backgroundColor;
+
+ string tileId = CreateOrUpdateSecondaryTilePoint(
+ sargs,
+ bargs,
+ ref backgroundColor,
+ (int) data.foregroundText,
+ pos
+ );
+ if (string.IsNullOrEmpty(tileId))
+ return null;
+ return new Tile(tileId);
+ }
+
+ CUSTOM private static string CreateOrUpdateSecondaryTilePoint(
+ string[] sargs,
+ bool[] bargs,
+ ref Color32 backgroundColor,
+ int foregroundText,
+ Vector2 pos
+ )
+ {
+ #if UNITY_METRO
+ std::string arguments = GetStringFromArray(sargs, 0);
+ std::string displayName = GetStringFromArray(sargs, 1);
+ std::string lockScreenBadgeLogo = GetStringFromArray(sargs, 2);
+ std::string phoneticName = GetStringFromArray(sargs, 3);
+ std::string square150x150Logo = GetStringFromArray(sargs, 4);
+ std::string square30x30Logo = GetStringFromArray(sargs, 5);
+ std::string square310x310Logo = GetStringFromArray(sargs, 6);
+ std::string square70x70Logo = GetStringFromArray(sargs, 7);
+ std::string tileId = GetStringFromArray(sargs, 8);
+ std::string wide310x150Logo = GetStringFromArray(sargs, 9);
+
+ bool backgroundColorSet = GetBoolFromArray(bargs, 0);
+ bool lockScreenDisplayBadgeAndTileText = GetBoolFromArray(bargs, 1);
+ bool roamingEnabled = GetBoolFromArray(bargs, 2);
+ bool showNameOnSquare150x150Logo = GetBoolFromArray(bargs, 3);
+ bool showNameOnSquare310x310Logo = GetBoolFromArray(bargs, 4);
+ bool showNameOnWide310x150Logo = GetBoolFromArray(bargs, 5);
+
+ return scripting_string_new(metro::Tiles::CreateOrUpdateSecondary(
+ arguments,
+ backgroundColor,
+ backgroundColorSet,
+ displayName,
+ foregroundText,
+ lockScreenBadgeLogo,
+ lockScreenDisplayBadgeAndTileText,
+ phoneticName,
+ roamingEnabled,
+ showNameOnSquare150x150Logo,
+ showNameOnSquare310x310Logo,
+ showNameOnWide310x150Logo,
+ square150x150Logo,
+ square30x30Logo,
+ square310x310Logo,
+ square70x70Logo,
+ tileId,
+ wide310x150Logo,
+ pos
+ )->GetId());
+ #else
+ return scripting_string_new("");
+ #endif
+ }
+
+ CSRAW public static Tile CreateOrUpdateSecondary(SecondaryTileData data, Rect area)
+ {
+ string[] sargs = MakeSecondaryTileSargs(data);
+ bool[] bargs = MakeSecondaryTileBargs(data);
+ Color32 backgroundColor = data.backgroundColor;
+
+ string tileId = CreateOrUpdateSecondaryTileArea(
+ sargs,
+ bargs,
+ ref backgroundColor,
+ (int) data.foregroundText,
+ area
+ );
+ if (string.IsNullOrEmpty(tileId))
+ return null;
+ return new Tile(tileId);
+ }
+
+ CUSTOM private static string CreateOrUpdateSecondaryTileArea(
+ string[] sargs,
+ bool[] bargs,
+ ref Color32 backgroundColor,
+ int foregroundText,
+ Rect area
+ )
+ {
+ #if UNITY_METRO
+ std::string arguments = GetStringFromArray(sargs, 0);
+ std::string displayName = GetStringFromArray(sargs, 1);
+ std::string lockScreenBadgeLogo = GetStringFromArray(sargs, 2);
+ std::string phoneticName = GetStringFromArray(sargs, 3);
+ std::string square150x150Logo = GetStringFromArray(sargs, 4);
+ std::string square30x30Logo = GetStringFromArray(sargs, 5);
+ std::string square310x310Logo = GetStringFromArray(sargs, 6);
+ std::string square70x70Logo = GetStringFromArray(sargs, 7);
+ std::string tileId = GetStringFromArray(sargs, 8);
+ std::string wide310x150Logo = GetStringFromArray(sargs, 9);
+
+ bool backgroundColorSet = GetBoolFromArray(bargs, 0);
+ bool lockScreenDisplayBadgeAndTileText = GetBoolFromArray(bargs, 1);
+ bool roamingEnabled = GetBoolFromArray(bargs, 2);
+ bool showNameOnSquare150x150Logo = GetBoolFromArray(bargs, 3);
+ bool showNameOnSquare310x310Logo = GetBoolFromArray(bargs, 4);
+ bool showNameOnWide310x150Logo = GetBoolFromArray(bargs, 5);
+
+ return scripting_string_new(metro::Tiles::CreateOrUpdateSecondary(
+ arguments,
+ backgroundColor,
+ backgroundColorSet,
+ displayName,
+ foregroundText,
+ lockScreenBadgeLogo,
+ lockScreenDisplayBadgeAndTileText,
+ phoneticName,
+ roamingEnabled,
+ showNameOnSquare150x150Logo,
+ showNameOnSquare310x310Logo,
+ showNameOnWide310x150Logo,
+ square150x150Logo,
+ square30x30Logo,
+ square310x310Logo,
+ square70x70Logo,
+ tileId,
+ wide310x150Logo,
+ area
+ )->GetId());
+ #else
+ return scripting_string_new("");
+ #endif
+ }
+
+ CSRAW public static Tile GetSecondary(string tileId)
+ {
+ if (Exists(tileId))
+ return new Tile(tileId);
+ return null;
+ }
+
+ CSRAW public static Tile[] GetSecondaries()
+ {
+ string[] ids = GetAllSecondaryTiles();
+ Tile[] tiles = new Tile[ids.Length];
+ for (int i = 0; i < ids.Length; ++i)
+ tiles[i] = new Tile(ids[i]);
+ return tiles;
+ }
+
+ CUSTOM private static string[] GetAllSecondaryTiles()
+ {
+ #if UNITY_METRO
+ return Scripting::StringVectorToMono(metro::Tiles::GetSecondaries());
+ #else
+ return ScriptingArrayPtr();
+ #endif
+ }
+
+ CSRAW public void Delete()
+ {
+ DeleteSecondary(m_TileId);
+ }
+
+ CUSTOM public static void DeleteSecondary(string tileId)
+ {
+ #if UNITY_METRO
+ metro::Tiles::Delete(tileId);
+ #endif
+ }
+
+ CSRAW public void Delete(Vector2 pos)
+ {
+ DeleteSecondaryPos(m_TileId, pos);
+ }
+
+ CSRAW public static void DeleteSecondary(string tileId, Vector2 pos)
+ {
+ DeleteSecondaryPos(tileId, pos);
+ }
+
+ CUSTOM private static void DeleteSecondaryPos(string tileId, Vector2 pos)
+ {
+ #if UNITY_METRO
+ metro::Tiles::Delete(tileId, pos);
+ #endif
+ }
+
+ CSRAW public void Delete(Rect area)
+ {
+ DeleteSecondaryArea(m_TileId, area);
+ }
+
+ CSRAW public static void DeleteSecondary(string tileId, Rect area)
+ {
+ DeleteSecondary(tileId, area);
+ }
+
+ CUSTOM private static void DeleteSecondaryArea(string tileId, Rect area)
+ {
+ #if UNITY_METRO
+ metro::Tiles::Delete(tileId, area);
+ #endif
+ }
+
+END
+
+
+CONDITIONAL UNITY_METRO_API
+CLASS Toast
+ CSRAW private int m_ToastId;
+
+ CSRAW private Toast(int id)
+ {
+ m_ToastId = id;
+ }
+
+ CUSTOM public static string GetTemplate(ToastTemplate templ)
+ {
+ #if UNITY_METRO
+ return scripting_string_new(metro::Toasts::GetTemplate(templ));
+ #else
+ return scripting_string_new("");
+ #endif
+ }
+
+ CSRAW public static Toast Create(string xml)
+ {
+ int id = CreateToastXml(xml);
+ if (id < 0)
+ return null;
+ return new Toast(id);
+ }
+
+ CUSTOM private static int CreateToastXml(string xml)
+ {
+ #if UNITY_METRO
+ return metro::Toasts::Create(xml);
+ #else
+ return 0;
+ #endif
+ }
+
+ CSRAW public static Toast Create(string image, string text)
+ {
+ int id = CreateToastImageAndText(image, text);
+ if (id < 0)
+ return null;
+ return new Toast(id);
+ }
+
+ CUSTOM private static int CreateToastImageAndText(string image, string text)
+ {
+ #if UNITY_METRO
+ return metro::Toasts::Create(image, text);
+ #else
+ return 0;
+ #endif
+ }
+
+ CSRAW public string arguments
+ {
+ get {
+ return GetArguments(m_ToastId);
+ }
+ set {
+ SetArguments(m_ToastId, value);
+ }
+ }
+
+ CUSTOM private static string GetArguments(int id)
+ {
+ #if UNITY_METRO
+ metro::Toast::Ptr toast = metro::Toasts::Instance(id);
+ if (toast)
+ return scripting_string_new(toast->GetArguments());
+ return scripting_string_new("");
+ #else
+ return scripting_string_new("");
+ #endif
+ }
+
+ CUSTOM private static void SetArguments(int id, string args)
+ {
+ #if UNITY_METRO
+ metro::Toast::Ptr toast = metro::Toasts::Instance(id);
+ if (toast)
+ toast->SetArguments(args);
+ #endif
+ }
+
+ CSRAW public void Show()
+ {
+ Show(m_ToastId);
+ }
+
+ CUSTOM private static void Show(int id)
+ {
+ #if UNITY_METRO
+ metro::Toast::Ptr toast = metro::Toasts::Instance(id);
+ if (toast)
+ toast->Show();
+ #endif
+ }
+
+ CSRAW public void Hide()
+ {
+ Hide(m_ToastId);
+ }
+
+ CUSTOM private static void Hide(int id)
+ {
+ #if UNITY_METRO
+ metro::Toast::Ptr toast = metro::Toasts::Instance(id);
+ if (toast)
+ toast->Hide();
+ #endif
+ }
+
+ CSRAW public bool activated
+ {
+ get {
+ return GetActivated(m_ToastId);
+ }
+ }
+
+ CUSTOM private static bool GetActivated(int id)
+ {
+ #if UNITY_METRO
+ metro::Toast::Ptr toast = metro::Toasts::Instance(id);
+ return toast ? toast->GetActivated() : false;
+ #else
+ return false;
+ #endif
+ }
+
+ CSRAW public bool dismissed
+ {
+ get {
+ return GetDismissed(m_ToastId, false);
+ }
+ }
+
+ CSRAW public bool dismissedByUser
+ {
+ get {
+ return GetDismissed(m_ToastId, true);
+ }
+ }
+
+ CUSTOM private static bool GetDismissed(int id, bool byUser)
+ {
+ #if UNITY_METRO
+ metro::Toast::Ptr toast = metro::Toasts::Instance(id);
+ return toast ? toast->GetDismissed(byUser) : false;
+ #else
+ return false;
+ #endif
+ }
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/WWW.cpp b/Runtime/Export/WWW.cpp
new file mode 100644
index 0000000..facc72b
--- /dev/null
+++ b/Runtime/Export/WWW.cpp
@@ -0,0 +1,1575 @@
+#include "UnityPrefix.h"
+#include "WWW.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Threads/Thread.h"
+#include "Configuration/UnityConfigureVersion.h"
+#include "PlatformDependent/CommonWebPlugin/UnityWebStream.h"
+#include "Runtime/Misc/ReproductionLog.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingInvocation.h"
+#include "Runtime/Misc/PlayerSettings.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingMethodRegistry.h"
+#include "External/Curl/include/minimalcurl.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/EditorSettings.h"
+#include "Editor/Src/EditorUserBuildSettings.h"
+#endif
+
+#if UNITY_IPHONE
+#include <CFNetwork/CFNetwork.h>
+#include "PlatformDependent/iPhonePlayer/iPhoneWWW.h"
+#elif UNITY_ANDROID
+#include "PlatformDependent/AndroidPlayer/AndroidWWW.h"
+#elif UNITY_PS3
+#include "PlatformDependent/PS3Player/PS3WWW.h"
+#elif UNITY_FLASH
+#include "PlatformDependent/FlashSupport/cpp/WWWFlash.h"
+#elif UNITY_WINRT
+#include "PlatformDependent/MetroPlayer/WWWMetro.h"
+#elif UNITY_XENON
+#include "PlatformDependent/Xbox360/Source/XenonWWW.h"
+#elif UNITY_LINUX
+#include "PlatformDependent/Linux/WWW.h"
+#endif
+
+#if UNITY_WIN
+#include <malloc.h> // alloca
+#if !UNITY_WP8
+#include "Winhttp.h"
+#endif
+#include "PlatformDependent\Win\WinUnicode.h"
+#endif
+#ifdef __MWERKS__
+#include <alloca.h>
+#endif
+#if UNITY_OSX
+#include "PlatformDependent/OSX/HttpProxy.h"
+#endif
+
+#include <algorithm>
+
+#if ENABLE_WWW
+
+#if WWW_USE_CURL
+static void SetCurlOptProxy (CURL* curl, const std::string& proxyUtf8, std::string::size_type offset)
+{
+ std::string::size_type end = proxyUtf8.find(";", offset);
+ std::string proxyConverted = proxyUtf8.substr(offset, end-offset);
+ curl_easy_setopt(curl, CURLOPT_PROXY, proxyConverted.c_str());
+}
+
+void SetupCurlProxyServerBasedOnEnvironmentVariable(CURL* curl, const char* url)
+{
+ const char* proxy = getenv("UNITY_PROXYSERVER");
+ if (proxy)
+ {
+ printf_console("Setting up proxyserver from UNITY_PROXYSERVER environment variable. Setting to: %s\n",proxy);
+ curl_easy_setopt(curl, CURLOPT_PROXY, proxy);
+ }
+#if UNITY_OSX || UNITY_LINUX
+ else
+ {
+ string proxy;
+ string auth;
+ if (GetProxyForURL(url, proxy, auth))
+ {
+ curl_easy_setopt(curl, CURLOPT_PROXY, proxy.c_str());
+ if (!auth.empty())
+ curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, auth.c_str());
+ }
+ }
+#elif UNITY_WIN
+ else
+ {
+ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG config;
+ if (WinHttpGetIEProxyConfigForCurrentUser(&config) && config.lpszProxy != NULL)
+ {
+ std::string proxyUtf8;
+ ConvertWideToUTF8String( config.lpszProxy, proxyUtf8 );
+ std::string::size_type start = proxyUtf8.find("http=");
+ if (start != string::npos)
+ {
+ // handle http proxy with http= prefix
+ SetCurlOptProxy(curl, proxyUtf8, start+5);
+ }
+ else
+ {
+ // case #534876
+ // proxy might be set, but without the http= prefix.
+ // Sufficient to check that no "=" characters are found in the string
+ if (proxyUtf8.find("=") == std::string::npos )
+ SetCurlOptProxy(curl, proxyUtf8, 0);
+ }
+ }
+ }
+#else
+#pragma message("WARNING: This platform has no system proxy support")
+#endif
+}
+
+#endif // WWW_USE_CURL
+
+/*
+ WWW cleanup procedure>
+
+ WWW c# has pointer to the www class. When the WWW class is garbage collected. It tells the WWW class to kill itself. (RequestDestroy)
+ RequestDestroy will
+ * When the download is complete, the WWW c++ class is deleted immediately.
+ * When the download thread is still running, set a flag abortDownload in buffer which causes download to stop asap
+
+When the thread is finished, we check the abortDownload and based on that delete the www class
+(because RequestDestroy was called and no one is interested in it anymore)
+
+Or just keep it around, for any WWW c# access.
+
+*/
+
+const char* kWWWErrCustomHeadersWithGET="Error when creating request. GET request with custom headers is not supported.";
+const char* kWWWErrZeroPostData="Error when creating request. POST request with a zero-sized post buffer is not supported.";
+const char* kWWWErrNULLPostDataWithPositiveLength="Internal error when creating request. Post data is NULL but length is larger than 0";
+const char* kWWWErrCancelled="WWW request was cancelled";
+const char* kWWWErrPostDataWithNonHTTPSchema="Error when creating request. Non HTTP schemas with post data are not supported.";
+const char* kWWWErrHeadersWithNonHTTPSchema="Error when creating request. Non HTTP schemas with custom headers are not supported.";
+
+
+double CalculateEta (int downloadedBytes, int totalBytes, double startTime)
+{
+ double curTime = GetTimeSinceStartup();
+ // bytes left (total bytes can be smaller for streamed files, which are complete when the stream can begin)
+ int bytesLeft = std::max(totalBytes - downloadedBytes, 0);
+ double bytesPerSecond = downloadedBytes / std::max((curTime - startTime), 0.1);
+ double timeLeft = bytesLeft / bytesPerSecond;
+ return timeLeft;
+}
+
+const char* GetCachedWWWError(const WWW& www, std::string& err)
+{
+ if (!err.empty())
+ {
+ return err.c_str();
+ }
+
+ UnityWebStream* stream = www.GetUnityWebStream();
+ if( stream && stream->IsErrorFlagSet() )
+ {
+ // Constructs the error string without temporaries
+ err = stream->GetError ();
+ err += " URL: ";
+ err += www.GetUrl();
+ return err.c_str();
+ }
+
+ return 0;
+}
+
+
+WWW::~WWW ()
+{
+ if (m_UnityWebStream != NULL)
+ m_UnityWebStream->Release();
+
+ #if SUPPORT_REPRODUCE_LOG
+ CleanupWWW(this);
+ #endif
+}
+
+#if SUPPORT_THREADS
+void WWW::SetThreadPriority (ThreadPriority p)
+{
+ m_ThreadPriority = p;
+ if(m_UnityWebStream)
+ m_UnityWebStream->SetDecompressionPriority(m_ThreadPriority);
+}
+#endif
+
+WWW::SecurityPolicy WWW::GetSecurityPolicy() const
+{
+ return kSecurityPolicyAllowAccess;
+}
+
+std::string WWW::GetResponseHeaders() //returning a copy instead of a reference for thread safety
+{
+ // JVD: std::string does not have atomic copy c-tor, so returning by value does not help to improve thread-safety
+ return m_ResponseHeaders;
+}
+
+bool WWW::SetErrorFromResponseHeaders ()
+{
+ std::string headers = GetResponseHeaders();
+
+ if ( !BeginsWith(headers, "HTTP") )
+ return false; // Not a valid response header
+
+ // only interested in first line.
+ headers = headers.substr(0, headers.find ("\n"));
+
+ // get result code
+ size_t resultCodePos = headers.find (' ');
+ if (resultCodePos != string::npos)
+ {
+ headers = headers.substr(resultCodePos+1);
+ int status = 0;
+ if (sscanf (headers.c_str(), "%d", &status))
+ {
+ if (status >= 400)
+ {
+ SetError (headers);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+UnityWebStream* WWW::GetUnityWebStream() const
+{
+ return m_UnityWebStream;
+}
+
+bool WWW::IsCached () const
+{
+ if (m_UnityWebStream)
+ return m_UnityWebStream->IsCached();
+ else
+ return m_Cached;
+}
+
+bool WWW::IsDone() const
+{
+ /*
+ There is an issue with how IsReadyToPlay is updated that causes small assetbundles to fail downloading.
+ This causes AssetStore previews to fail in the Editor.
+ TODO: Figure out why IsReadyToPlay is never set to true.
+ */
+ return m_UnityWebStream ? m_UnityWebStream->IsFinished(): IsDownloadingDone();
+}
+
+void WWW::FeedUnityWebStream(bool isDone)
+{
+ // Check if this actually is a UnityWebStream file
+ if (!m_DidParseUnityWebStream)
+ {
+ UnityWebStreamHeader header;
+
+ // Safely parse stream header
+ int result;
+ {
+ WWW::AutoLock lock(*this);
+ const UInt8* partialData = GetPartialData();
+ result = ParseStreamHeader (header, partialData, partialData + GetPartialSize());
+ }
+
+ // Is Unity Web file
+ if (result == 0)
+ {
+ if(m_Cached)
+ m_UnityWebStream = UNITY_NEW_AS_ROOT(UnityWebStream(GetUrl(), m_CacheVersion, m_RequestedCRC), kMemFile, "WebStream", GetUrl());
+ else
+ m_UnityWebStream = UNITY_NEW_AS_ROOT(UnityWebStream(NULL, 0, m_RequestedCRC), kMemFile, "WebStream", GetUrl());
+#if SUPPORT_THREADS
+ m_UnityWebStream->SetDecompressionPriority(m_ThreadPriority);
+#endif
+ m_UnityWebStream->Retain();
+ m_DidParseUnityWebStream = true;
+ }
+ // Is not a Unity Web file
+ else if (result == 2)
+ {
+ m_DidParseUnityWebStream = true;
+ }
+ }
+
+ // Feed it the data that has arrived
+#if !UNITY_FLASH
+ if (m_UnityWebStream)
+ {
+ WWW::AutoLock lock(*this);
+
+ m_UnityWebStream->FeedDownloadData(GetPartialData() + m_StreamingPosition, GetPartialSize() - m_StreamingPosition, isDone);
+ m_StreamingPosition = GetPartialSize();
+ }
+#else
+ if(m_UnityWebStream && isDone){
+ //RH : TODO : Flash gets the whole stream in one go for now, but we can still chunk this if needs be (if we decide to use swf compression instead).
+ m_UnityWebStream->FeedDownloadData(GetData(), GetSize(), isDone);
+ }
+#endif
+}
+
+UInt32 WWW::GetEstimatedDownloadSize() const
+{
+ return m_UnityWebStream && m_UnityWebStream->DidParseHeader() ?
+ m_UnityWebStream->GetHeader().completeFileSize: 0u;
+}
+
+void WWW::Retain()
+{
+ m_RefCount.Retain();
+}
+
+void WWW::Release()
+{
+ if ( m_RefCount.Release() ) {
+ delete this;
+ }
+}
+
+#if WWW_USE_BROWSER
+#include "PlatformDependent/CommonWebPlugin/Download.h"
+Download* StartDownloadBinding (const char* url, void* userData, DownloadProgressFunction* callback, const void* headersData, size_t headersLength, const void* postData, size_t postLength );
+
+// Requires that m_Download exists
+int WWWBrowser::GetTotalBytesUntilLoadable() const
+{
+ Assert(m_Download);
+
+ if (GetUnityWebStream())
+ {
+ int res = GetUnityWebStream()->GetTotalBytesUntilLoadable();
+ if (res != -1)
+ return res;
+ }
+
+ return m_Download->GetTotalBytes();
+}
+
+// When all data was in browser's cache, the download might already be finished
+// (happens on IE6/IE7 at least). In this case our progress callback will not be
+// ever called. So check download here and call progress callback manually.
+// Case 15160, 15074.
+
+void WWWBrowser::ForceProgressDownload ()
+{
+ if( m_Download != NULL && (m_Download->GetStatus() == Download::kCompleted || m_Download->GetStatus() == Download::kFailed) )
+ {
+ WWWBrowser::ProgressDownload( m_Download );
+ }
+}
+
+extern int GetPluginVersion();
+
+int WWWBrowser::ProgressDownload(Download* download)
+{
+ if (download->GetUserData () == NULL)
+ return 0;
+
+ WWWBrowser& www = *reinterpret_cast<WWWBrowser*>(download->GetUserData ());
+
+ AssertIf(www.m_LockedPartialData != 0);
+ if (www.m_Download == NULL)
+ return 0;
+
+ AssertIf(www.m_Download != download);
+
+ // get the status before changing it later!
+ int downloadStatus = download->GetStatus();
+
+ // Detect if we are loading a unity web stream and feed it the data for live decompression
+ if (downloadStatus == Download::kLoading || downloadStatus == Download::kCompleted)
+ www.FeedUnityWebStream(downloadStatus == Download::kCompleted);
+
+ if( downloadStatus == Download::kLoading )
+ www.m_Eta = CalculateEta(download->GetDownloadedBytes(), www.GetTotalBytesUntilLoadable(), www.m_StartDownloadTime);
+
+ if( downloadStatus <= Download::kLoading )
+ return 0;
+
+ // Mark for destruction!
+ www.m_Download->SetStatus(Download::kDestroyAfterCallback);
+
+ if (GetPluginVersion() >= 5)
+ {
+ if(const char* headers = download->GetResponseHeaders())
+ {
+ www.m_ResponseHeaders.assign(headers);
+ www.SetErrorFromResponseHeaders ();
+ }
+ else
+ {
+ www.m_ResponseHeaders.clear();
+ }
+ }
+
+ // Error while downloading
+ if( downloadStatus != Download::kCompleted )
+ {
+ www.m_Error = "Failed downloading " + www.m_Url;
+ download->SetUserData (NULL);
+ www.m_Download = NULL;
+ return 0;
+ }
+
+ #if SUPPORT_REPRODUCE_LOG
+ CompleteWWWReproduce(&www, www.GetUrl(), download->GetDownloadedData(), download->GetDownloadedBytes());
+ #endif
+
+ if (www.GetUnityWebStream() == NULL)
+ {
+ // Move over the data. We can't just reference the data from Download, because it is allocated
+ // from a different DLL (on Windows each DLL has separate heaps).
+ std::size_t dsize = download->GetDownloadedBytes();
+ const UInt8* dfirst = download->GetDownloadedData();
+
+ www.m_Buffer.resize(dsize);
+ std::copy(dfirst, dfirst + dsize, www.m_Buffer.begin());
+ }
+ // Don't copy data for unitywebstream
+ else
+ {
+ www.m_Buffer.clear();
+ }
+ www.m_Eta = 0.0F;
+ download->SetUserData (NULL);
+ www.m_Download = NULL;
+
+ return 0;
+}
+
+WWWBrowser::WWWBrowser (const char* postDataPtr, int postDataLength, const WWWHeaders& i_Headers, bool cached, int cacheVersion , UInt32 crc)
+ : WWW(cached, cacheVersion, crc)
+
+ , m_Download(NULL)
+
+ , m_Buffer() // Empty
+{
+ m_Eta = std::numeric_limits<double>::infinity();
+ m_StartDownloadTime = GetTimeSinceStartup();
+ m_LockedPartialData = 0;
+
+ WWWHeaders headers = i_Headers; // Copy headers as we want to modify them before generating data
+
+ if( postDataPtr == NULL && postDataLength > 0 ) {
+ m_Error = kWWWErrNULLPostDataWithPositiveLength;
+ return;
+ }
+ if( postDataLength < 0 && !headers.empty() ) {
+ m_Error = kWWWErrCustomHeadersWithGET;
+ return;
+ }
+ if( postDataLength == 0 && postDataPtr != NULL) {
+ m_Error = kWWWErrZeroPostData;
+ return;
+ }
+
+ // Pepper adds it's own Content-Length header on post requests. Adding one here will cause an error.
+#if !UNITY_PEPPER
+ if( postDataLength > -1 && postDataPtr != NULL ) {
+ // Override the Content-Length header when we have post data
+ headers["Content-Length"] = IntToString( postDataLength );
+ }
+ else
+#endif
+ headers.erase("Content-Length");
+
+ // Put all headers into single headers string
+ m_HeadersString.clear();
+ for( WWWHeaders::iterator i = headers.begin(); i != headers.end(); ++i )
+ m_HeadersString += i->first + ": " + i->second +
+#if !UNITY_PEPPER
+ "\r\n"
+#else
+ // NaCl uses CORS validation to decide if it allows cross domain requests.
+ // Starting with Chrome 17, it will no longer allow any Unity requests with custom headers.
+ // Google explained that the validation would fail because we use \r which is an illegal
+ // character in the http headers string. We should probably also change it for non NaCl cases,
+ // but I don't want to risk anything at this rc stage.
+ "\n"
+#endif
+ ;
+ if( postDataLength >= 0 && postDataPtr != NULL && !m_HeadersString.empty() )
+ {
+ m_PostLength = postDataLength;
+
+ // Store a terminating byte after last byte in buffer just in case somebody thinks it's a string
+ m_PostData = new char[ m_PostLength + 1 ];
+ m_PostData[m_PostLength] = 0;
+ if( postDataLength > 0 )
+ memcpy( m_PostData, postDataPtr, postDataLength );
+ }
+ else
+ {
+ m_PostLength = -1;
+ m_PostData = NULL;
+ }
+
+ m_Cached = cached;
+ m_CacheVersion = cacheVersion;
+}
+
+void WWWBrowser::BlockUntilDone ()
+{
+ if (GetUnityWebStream())
+ GetUnityWebStream()->WaitForThreadDecompression();
+}
+
+WWWBrowser::~WWWBrowser ()
+{
+ #if UNITY_LINUX
+ #warning FIXME LINUX
+// if (m_Download != NULL) {
+// delete m_Download;
+// }
+ #else
+ if (m_Download != NULL)
+ {
+ m_Download->SetUserData (NULL);
+ m_Download->SetStatus(Download::kCancelAndDestroyAfterCallback);
+ m_Download = NULL;
+ }
+ #endif
+
+ delete[] m_PostData;
+}
+
+void WWWBrowser::Cancel ()
+{
+ m_Error = kWWWErrCancelled;
+ if (m_Download != NULL)
+ {
+ m_Download->SetUserData (NULL);
+ m_Download->SetStatus(Download::kCancelAndDestroyAfterCallback);
+ m_Download = NULL;
+ }
+}
+
+float WWWBrowser::GetProgress() const
+{
+ if (m_Download != NULL)
+ {
+ return GetDownloadProgress(m_Download->GetDownloadedBytes(), GetTotalBytesUntilLoadable());
+ }
+ else
+ return 1.0F;
+}
+
+float WWWBrowser::GetUploadProgress() const
+{
+ // @TODO: Implement this properly - currently we return 0.5 until download progress is > 0
+ if ( m_Download == NULL)
+ return 1.0F;
+ return ( m_Download->GetDownloadedBytes() )?1.0F:0.5F;
+}
+
+const char* WWWBrowser::GetError()
+{
+ return GetCachedWWWError(*this, m_Error);
+}
+
+void WWWBrowser::SetError (const std::string& error)
+{
+ m_Error = error;
+}
+
+const UInt8* WWWBrowser::GetPartialData() const
+{
+ AssertIf (m_LockedPartialData == 0);
+
+ if (!m_Buffer.empty())
+ return m_Buffer.data();
+ else if (m_Download && (m_Download->GetStatus() == Download::kLoading || m_Download->GetStatus() == Download::kCompleted))
+ {
+ return m_Download->GetDownloadedData ();
+ }
+ else
+ return NULL;
+}
+
+size_t WWWBrowser::GetPartialSize() const
+{
+ AssertIf (m_LockedPartialData == 0);
+
+ if (m_Buffer.empty() && m_Download != NULL)
+ return m_Download->GetDownloadedBytes ();
+
+ return m_Buffer.size();
+}
+
+void WWWBrowser::LockPartialData()
+{
+// ErrorString("Someone needs to think about and implement proper locking for this");
+ m_LockedPartialData++;
+}
+
+void WWWBrowser::UnlockPartialData()
+{
+ AssertIf(m_LockedPartialData == 0);
+ m_LockedPartialData--;
+// ErrorString("Someone needs to think about and implement proper locking for this");
+}
+
+double WWWBrowser::GetETA() const
+{
+ return m_Eta;
+}
+
+const UInt8* WWWBrowser::GetData()
+{
+ return m_Buffer.data();
+}
+
+size_t WWWBrowser::GetSize()
+{
+ return m_Buffer.size();
+}
+
+bool WWWBrowser::IsDownloadingDone() const
+{
+ return m_Download == NULL;
+}
+
+bool WWWBrowser::HasDownloadedOrMayBlock ()
+{
+ if (GetError () != NULL)
+ {
+ ErrorString(Format("You are trying to load data from a www stream which had the following error when downloading.\n%s", GetError()));
+ return false;
+ }
+
+ if (IsDone())
+ return true;
+ else
+ {
+ ErrorString("You are trying to load data from a www stream which has not completed the download yet.\nYou need to yield the download or wait until isDone returns true.");
+ return false;
+ }
+}
+
+const char* WWWBrowser::GetUrl() const
+{
+ return m_Url.c_str();
+}
+
+WWWBrowser* WWWBrowser::CreateBrowser (const char* url, const char* postDataPtr, int postDataLength, const WWWHeaders& headers, bool cached, int cacheVersion, UInt32 crc )
+{
+ WWWBrowser* www = new WWWBrowser (postDataPtr, postDataLength, headers, cached, cacheVersion, crc);
+ if (www->GetError() != NULL)
+ return www;
+
+ const char* actualUrl = url;
+ int actualPostLength = www->m_PostLength;
+
+ #if SUPPORT_REPRODUCE_LOG
+ string remappedurl;
+ CreateWWWReproduce(www, url, remappedurl, actualPostLength);
+ actualUrl = remappedurl.c_str();
+ #endif
+
+ Download* download = StartDownloadBinding(actualUrl, www, WWWBrowser::ProgressDownload, www->m_HeadersString.c_str(), www->m_HeadersString.size(), www->m_PostData, actualPostLength );
+ if (download)
+ {
+ www->m_Download = download;
+ www->m_Url = url;
+
+ #if SUPPORT_REPRODUCE_LOG
+ // Cant show the WWW as completed right away. Need to wait until next frame.
+ if (RunningReproduction())
+ {
+ return www;
+ }
+ #endif
+
+ // When all data was in browser's cache, the download might already be finished
+ // (happens on IE6/IE7 at least). In this case our progress callback will not be
+ // ever called. So check download here and call progress callback manually.
+ // Case 15160, 15074.
+ www->ForceProgressDownload();
+
+ return www;
+ }
+ else
+ {
+ printf_console("Failed browser download\n");
+ delete www;
+ return NULL;
+ }
+}
+
+#endif // WWW_USE_BROWSER
+
+// This function creates a WWW backend without cross domain checking; it's good to have it separated from WWW::Create, because this way it's simpler to debug
+// crossdomain checking routines, because they don't have to recursively call WWW::Create when download starts.
+static WWW* CreatePlatformWWWBackend (const char* url, const char* postDataPtr, int postDataLength, const WWW::WWWHeaders& headers, bool cached, int cacheVersion, UInt32 crc );
+
+WWW* WWW::Create (const char* url, const char* postDataPtr, int postDataLength, const WWWHeaders& headers, bool crossDomainChecked, bool cached, int cacheVersion, UInt32 crc )
+{
+#if ENABLE_WEBPLAYER_SECURITY
+
+#if SUPPORT_REPRODUCE_LOG
+ if (RunningReproduction()) crossDomainChecked = false;
+#endif
+
+#if UNITY_EDITOR
+ if (GetBuildTargetGroup( GetEditorUserBuildSettings().GetActiveBuildTarget ()) != kPlatformWebPlayer)
+ crossDomainChecked = false;
+#endif
+
+ if (crossDomainChecked)
+ {
+ return new WWWCrossDomainChecked(url, postDataPtr, postDataLength, headers, cached, cacheVersion, crc);
+ }
+
+#endif // ENABLE_WEBPLAYER_SECURITY
+ return CreatePlatformWWWBackend(url, postDataPtr, postDataLength, headers, cached, cacheVersion, crc);
+}
+
+static WWW* CreatePlatformWWWBackend (const char* url, const char* postDataPtr, int postDataLength, const WWW::WWWHeaders& headers, bool cached, int cacheVersion, UInt32 crc )
+{
+ #if WWW_USE_BROWSER
+ return WWWBrowser::CreateBrowser(url, postDataPtr, postDataLength, headers, cached, cacheVersion, crc);
+ #elif UNITY_IPHONE
+ return new iPhoneWWW(url, postDataPtr, postDataLength, headers, cached, cacheVersion, crc);
+ #elif UNITY_ANDROID
+ return new AndroidWWW(url, postDataPtr, postDataLength, headers, cached, cacheVersion, crc);
+ #elif UNITY_FLASH
+ return new WWWFlash(url, postDataPtr, postDataLength, headers, cached, cacheVersion, crc);
+ #elif UNITY_XENON
+ return new XenonWWW(url, postDataPtr, postDataLength, headers, cached, cacheVersion, crc);
+ #elif UNITY_PS3
+ return new PS3WWW(url, postDataPtr, postDataLength, headers, cached, cacheVersion, crc);
+ #elif UNITY_WINRT
+ return new WWWMetro(url, postDataPtr, postDataLength, headers, cached, cacheVersion, crc);
+ #elif WWW_USE_CURL
+ return new WWWCurl(url, postDataPtr, postDataLength, headers, cached, cacheVersion, crc);
+ #else
+ #error Unknown WWW backend
+ #endif
+}
+
+
+#if WWW_USE_CURL
+
+// Forward read callbacks from CURL to AppendBytes:
+size_t WWWCurl::WriteCallback(void * data, size_t size, size_t elements, WWWCurl * myData)
+{
+ if (myData->abortDownload)
+ return -1;
+
+ size_t res = myData->AppendBytes(data,size*elements);
+
+ myData->FeedUnityWebStream(false);
+
+ return res;
+}
+
+//If the server reports a Content-Length header, allocate the whole block of memory in advance.
+size_t WWWCurl::HeaderCallback(void * data, size_t size, size_t elements, WWWCurl * myData)
+{
+ if (myData->abortDownload)
+ return -1;
+
+ size_t len = size*elements;
+
+ char *str = (char*)alloca(len+1);
+
+ memcpy(str, data, size*elements);
+ str[size*elements] = '\0';
+
+ size_t totalSize = 0;
+ if(sscanf(str,"Content-Length:%d",&totalSize))
+ {
+ if(totalSize > myData->alloc_size) {
+ Mutex::AutoLock lock(myData->mutex);
+ myData->totalSize = totalSize;
+ myData->alloc_size = totalSize;
+ myData->data = (UInt8*)realloc((void*)myData->data, totalSize);
+ }
+ }
+ // Set response headers
+ {
+ Mutex::AutoLock lock(myData->mutex);
+ myData->m_ResponseHeaders.append(str);
+ myData->m_ResponseHeaders.append("\r\n");
+ }
+ myData->SetErrorFromResponseHeaders ();
+ return size*elements;
+}
+
+// Forward read callbacks from CURL to PostBytes:
+size_t WWWCurl::ReadCallback(void * data, size_t size, size_t elements, WWWCurl * myData)
+{
+ if (myData->abortDownload)
+ return -1;
+
+ size_t res = myData->PostBytes(data,size*elements);
+ return res;
+}
+
+int WWWCurl::ProgressCallback (WWWCurl *myData,
+ double dltotal,
+ double dlnow,
+ double ultotal,
+ double ulnow)
+{
+ if (myData->abortDownload)
+ return -1;
+
+ if( dltotal > 0 ) {
+ myData->totalSize = dltotal;
+ myData->progress = dlnow / dltotal;
+ myData->eta = CalculateEta (RoundfToInt(dlnow), RoundfToInt(dltotal), myData->startTime);
+ myData->uploadProgress = 1.0;
+ }
+ else if( ultotal > 0 ) {
+ myData->uploadProgress = ulnow / ultotal;
+ }
+ return 0;
+}
+
+UInt32 WWWCurl::GetEstimatedDownloadSize() const
+{
+ if (totalSize > 0)
+ return totalSize;
+ else
+ return WWW::GetEstimatedDownloadSize();
+}
+
+curl_slist* WWWCurl::GetHeaderSList () {
+ // Remove any previously generated headers
+ if(curlHeaders != NULL)
+ curl_slist_free_all(curlHeaders);
+
+ // Now begin with an empty slist
+ curlHeaders=NULL;
+
+ for(WWWHeaders::iterator i = requestHeaders.begin(); i != requestHeaders.end(); i++)
+ curlHeaders = curl_slist_append(curlHeaders, (i->first + ": " + i->second).c_str() );
+
+ return curlHeaders;
+}
+
+CURLcode WWWCurl::GetURL( const char* url) {
+ CURL* curl = curl_easy_init();
+ CURLcode res = 1;
+ if(curl) {
+
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); // We don't install CA certs with unity, so disable SSL cert validation
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); // Needs to be set when running cur in multible threads
+
+ // @TODO: Enable cookies using something like this:
+ // (or preferably something that does not require storing files on disk. expose cookies through Application.cookies[xx] or simmilar)
+ // curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookie_file);
+ // curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookie_file);
+
+ // If we have post data, the request should be a POST request
+ if(postData != NULL && postLength > -1) {
+ postPosition=0;
+ curl_easy_setopt(curl, CURLOPT_POST, 1L);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, postLength);
+ curl_easy_setopt(curl, CURLOPT_READDATA, this);
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, ReadCallback);
+ requestHeaders["Content-Length"]=Format("%d",postLength);
+ }
+
+ curl_slist* headers = GetHeaderSList ();
+ if(headers != NULL)
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, this);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, ProgressCallback);
+// curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 16000);
+
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer);
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, "UnityPlayer/" UNITY_VERSION " (http://unity3d.com)");
+// curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5);
+
+ curl_easy_setopt(curl, CURLOPT_HEADERDATA, this);
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, HeaderCallback);
+
+ SetupCurlProxyServerBasedOnEnvironmentVariable(curl, url);
+
+ // curl_easy_setopt(curl, CURLOPT_HEADER, 1L); // TODO
+
+ res = curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+ }
+
+ progress = 1.0F;
+ uploadProgress = 1.0F;
+ eta = 0.0F;
+
+ return res;
+}
+
+void WWWCurl::SetThreadPriority( ThreadPriority priority )
+{
+ WWW::SetThreadPriority(priority);
+ thread.SetPriority(priority);
+}
+
+size_t WWWCurl::AppendBytes(void * moreData, size_t bytes)
+{
+ if(size+bytes > alloc_size)
+ {
+ Mutex::AutoLock lock(mutex);
+ const UInt32 estimatedDownloadSize = GetEstimatedDownloadSize();
+ if (alloc_size+bytes <= estimatedDownloadSize)
+ alloc_size = estimatedDownloadSize;
+ else
+ alloc_size = (alloc_size * 1.5) + bytes;
+ data = (UInt8*)realloc((void*)data, alloc_size);
+ if (!data)
+ {
+ ErrorString("WWW: out of memory");
+ return 0; // this will stop the transfer - not all data is loaded
+ }
+ }
+ if (!moreData){
+ return 0;
+ }
+ memcpy(data+size,moreData,bytes);
+ size += bytes;
+ return bytes;
+}
+
+std::string WWWCurl::GetResponseHeaders()
+{
+ Mutex::AutoLock lock(mutex);
+ return m_ResponseHeaders;
+}
+
+size_t WWWCurl::PostBytes(void * moreData, size_t bytes) {
+ if(bytes > postLength-postPosition)
+ bytes = postLength-postPosition;
+
+ if(bytes <= 0) return 0; // end of file
+
+ memcpy(moreData,postData+postPosition,bytes);
+ postPosition += bytes;
+ return bytes;
+}
+
+// A C wrapper for the pthread entry point
+void* WWWCurl::WWW_ThreadEntryPoint(void* data)
+{
+ WWWCurl& www = *(WWWCurl*)data;
+
+ www.result = www.GetURL(www.url);
+
+ if (!www.abortDownload && www.GetError() == NULL)
+ {
+ www.FeedUnityWebStream(true);
+ }
+
+ return data;
+}
+WWWCurl::WWWCurl( const char* in_url, const char * in_postData, int in_postLength, const WWWHeaders& in_headers, bool cached, int cacheVersion, UInt32 crc )
+ : WWW(cached, cacheVersion, crc)
+{
+ DoInit(in_url, in_postData, in_postLength, in_headers);
+}
+
+void WWWCurl::StartThread()
+{
+ thread.Run(&WWW_ThreadEntryPoint, (void*) this);
+}
+
+void WWWCurl::BlockUntilDone()
+{
+ thread.WaitForExit();
+
+ if (GetUnityWebStream())
+ GetUnityWebStream()->WaitForThreadDecompression();
+}
+
+bool WWWCurl::IsDownloadingDone() const
+{
+ return !thread.IsRunning();
+}
+
+const UInt8* WWWCurl::GetData()
+{
+ BlockUntilDone();
+ return data;
+}
+
+const UInt8* WWWCurl::GetPartialData() const
+{
+ return data;
+}
+
+void WWWCurl::LockPartialData()
+{
+ mutex.Lock();
+}
+
+void WWWCurl::UnlockPartialData()
+{
+ mutex.Unlock();
+}
+
+const char* WWWCurl::GetError()
+{
+ if (strlen (errorBuffer))
+ return errorBuffer;
+
+ std::string fullError;
+ const char* err = GetCachedWWWError(*this, fullError);
+ SetError(fullError); // Nothing will be set if empty
+ return err;
+}
+
+void WWWCurl::SetError (const std::string& error)
+{
+ strncpy (errorBuffer, error.c_str(), CURL_ERROR_SIZE);
+}
+
+
+size_t WWWCurl::GetSize()
+{
+ BlockUntilDone();
+ return size;
+}
+
+size_t WWWCurl::GetPartialSize() const
+{
+ return size;
+}
+
+
+const char* WWWCurl::GetUrl() const
+{
+ return url;
+}
+
+float WWWCurl::GetProgress() const
+{
+ return progress;
+}
+
+float WWWCurl::GetUploadProgress() const
+{
+ return uploadProgress;
+}
+
+double WWWCurl::GetETA() const
+{
+ if(IsDone())
+ return 0.0;
+ else
+ return eta;
+}
+
+
+bool WWWCurl::HasDownloadedOrMayBlock ()
+{
+ if (GetError () != NULL)
+ {
+ ErrorString(Format("You are trying to load data from a www stream which had the following error when downloading.\n%s", GetError()));
+ return false;
+ }
+
+ if (IsDone())
+ return true;
+ else
+ {
+ // File based curl's may block
+ if( BeginsWithCaseInsensitive(url, "file://") )
+ return true;
+
+ ErrorString("You are trying to load data from a www stream which has not completed the download yet.\nYou need to yield the download or wait until isDone returns true.");
+ return false;
+ }
+}
+
+
+void WWWCurl::DoInit( const char* in_url, const char * in_postData, int in_postLength, const WWWHeaders& in_headers )
+{
+ alloc_size=0;
+ size=0;
+ postData=NULL;
+ postLength=-1;
+ postPosition=0;
+ data=NULL;
+ errorBuffer=(char*)calloc(CURL_ERROR_SIZE,sizeof(char));
+ AssertIf(!errorBuffer);
+ errorBuffer[0] = 0;
+ abortDownload = false;
+ progress = 0;
+ totalSize = 0;
+ uploadProgress = 0;
+ startTime = GetTimeSinceStartup();
+ curlHeaders=NULL;
+ eta = std::numeric_limits<double>::infinity();
+
+ url = (char*)malloc(strlen(in_url)+1);
+ AssertIf(!url);
+ strcpy(url, in_url);
+
+ if( in_postData == NULL && in_postLength > 0 ) {
+ result=-1;
+ if (errorBuffer) free (errorBuffer);
+ errorBuffer=(char*)malloc(strlen(kWWWErrNULLPostDataWithPositiveLength)+1);
+ strcpy(errorBuffer, kWWWErrNULLPostDataWithPositiveLength);
+ progress = 1.0F;
+ return;
+ }
+ if( in_postLength == 0 && in_postData != NULL) {
+ result=-1;
+ if (errorBuffer) free (errorBuffer);
+ errorBuffer=(char*)malloc(strlen(kWWWErrZeroPostData)+1);
+ if(errorBuffer)
+ strcpy(errorBuffer, kWWWErrZeroPostData);
+ progress = 1.0F;
+ return;
+ }
+
+
+ requestHeaders = in_headers;
+
+ if(in_postData != NULL && in_postLength > -1) {
+ postLength=in_postLength;
+ postData = new char[in_postLength];
+ memcpy((void *)postData, (const void*)in_postData, in_postLength);
+ }
+
+ result = 0;
+
+ StartThread();
+}
+
+WWWCurl::~WWWCurl()
+{
+ abortDownload = true;
+ thread.WaitForExit();
+
+ // Abort decompressionthreads that the curl thread might have spawned
+ if (GetUnityWebStream())
+ GetUnityWebStream()->AbortThreadDecompression ();
+
+ free(data);
+ free(errorBuffer);
+ if(postData != NULL) delete[](postData);
+ if(curlHeaders != NULL)
+ curl_slist_free_all(curlHeaders);
+
+ free(url);
+}
+
+void WWWCurl::Cancel ()
+{
+ strcpy(errorBuffer, kWWWErrCancelled);
+ if (thread.IsRunning ())
+ {
+ abortDownload = true;
+ }
+}
+
+
+#endif // WWW_USE_CURL
+
+WWWDelayCall::WWWDelayCall(WWW* www, DelayedCall* func, Object* o, void* userData, CleanupUserData* cleanup){
+ m_wait_for=www;
+ m_wait_for->Retain();
+ m_func=func;
+ m_o=o;
+ m_userData=userData;
+ m_cleanup=cleanup;
+}
+
+WWWDelayCall::~WWWDelayCall() {
+ m_wait_for->Release();
+ if (m_cleanup != NULL) {
+ m_cleanup(m_userData);
+ }
+}
+
+void WWWDelayCall::Cleanup(void* userData) {
+ AssertIf(userData == NULL);
+ WWWDelayCall* delayCall = (WWWDelayCall*) userData;
+ delete delayCall;
+}
+
+void WWWDelayCall::Callback(Object* o, void* userData) {
+ WWWDelayCall* delayCall = (WWWDelayCall*) userData;
+ AssertIf(delayCall->m_o != o);
+
+ if(delayCall->m_wait_for == NULL || delayCall->m_wait_for->IsDone()) {
+ delayCall->m_func(o, delayCall->m_userData);
+ GetDelayedCallManager ().CancelCallDelayed(o,WWWDelayCall::Callback, WWWDelayCall::MatchForCancel, userData);
+ }
+}
+
+bool WWWDelayCall::MatchForCancel(void* callBackUserData, void* cancelUserData) {
+ return callBackUserData == cancelUserData;
+}
+
+void WWW::CallWhenDone(DelayedCall* func, Object* o, void* userData, CleanupUserData* cleanup) {
+ WWWDelayCall* delayCall = new WWWDelayCall(this, func, o, userData, cleanup);
+ CallDelayed(WWWDelayCall::Callback, o, 0.0F, (void*)delayCall, -1.0F, WWWDelayCall::Cleanup, DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kWaitForNextFrame);
+}
+
+#if ENABLE_WEBPLAYER_SECURITY
+
+#include "Runtime/Utilities/ArrayUtility.h"
+
+void ProcessCrossDomainRequestsFromNonMainThread()
+{
+ MonoMethod* monoMethod = mono_unity_find_method("CrossDomainPolicyParser.dll","UnityEngine","UnityCrossDomainHelper","ProcessRequestsFromOtherThreads");
+ ScriptingMethodPtr method = GetScriptingManager().GetScriptingMethodRegistry().GetMethod(monoMethod);
+ if (!method)
+ {
+ Assert("Unable to find ProcessRequestsFromOtherThreads");
+ return;
+ }
+
+ ScriptingInvocation(method).Invoke();
+}
+
+
+class WWWCrossDomainCheckedImpl : public WWW
+{
+private:
+ std::string m_URL;
+ const char* m_Error;
+ ScriptingMethodPtr m_ScriptingMethod;
+
+ // Serves only as a cache, nothing more
+ mutable SecurityPolicy m_CachedSecPolicy;
+
+public:
+ WWWCrossDomainCheckedImpl (const char* url, bool cached, int cacheVersion, UInt32 crc);
+ ~WWWCrossDomainCheckedImpl () {}
+
+ virtual SecurityPolicy GetSecurityPolicy() const;
+ virtual const UInt8* GetData() { return 0; }
+ virtual const UInt8* GetPartialData() const { return 0; }
+ virtual size_t GetSize() { return 0u; }
+ virtual size_t GetPartialSize() const { return 0u; }
+ virtual UnityWebStream* GetUnityWebStream () const { return 0; }
+
+ virtual double GetETA() const { return std::numeric_limits<double>::infinity(); }
+
+ virtual void LockPartialData() {}
+ virtual void UnlockPartialData() {}
+
+ // Returns true when the download is complete or failed.
+ virtual void Cancel() { m_ScriptingMethod = 0; }
+ virtual bool IsDownloadingDone() const;
+ virtual float GetProgress() const { return 0.0f; }
+ virtual float GetUploadProgress() const { return 0.0f; }
+ virtual const char* GetError() { return m_Error; }
+ virtual const char* GetUrl() const { return m_URL.c_str(); }
+ virtual bool HasDownloadedOrMayBlock () { return true; }
+ virtual void BlockUntilDone () { while ( !IsDownloadingDone() ) { /*Wait*/ } }
+
+ virtual WWWType GetType () const { return kWWWTypeCrossDomainChecked; }
+
+ bool HasNoErrors() const { return m_Error == 0; }
+
+ const std::string& GetUrlString() const { return m_URL; }
+ void SetErrorStringPtr (const char* error) { m_Error = error; }
+};
+
+
+inline static ScriptingMethodPtr GetCrossDomainPolicyParserScriptingMethod()
+{
+ MonoMethod* monoMethod = mono_unity_find_method("CrossDomainPolicyParser.dll","UnityEngine","UnityCrossDomainHelper","GetSecurityPolicy");
+ return GetScriptingManager().GetScriptingMethodRegistry().GetMethod(monoMethod);
+}
+
+WWWCrossDomainCheckedImpl::WWWCrossDomainCheckedImpl (const char* url, bool cached, int cacheVersion, UInt32 crc)
+ : WWW(cached, cacheVersion, crc)
+ , m_URL(url)
+ , m_Error(0) // Null error string
+ , m_ScriptingMethod(GetCrossDomainPolicyParserScriptingMethod())
+ , m_CachedSecPolicy(kSecurityPolicyDontKnowYet)
+{
+ if (!m_ScriptingMethod)
+ {
+ m_CachedSecPolicy = kSecurityPolicyDenyAccess;
+ AssertString("Unable to find GetSecurityPolicy");
+ }
+}
+
+WWW::SecurityPolicy WWWCrossDomainCheckedImpl::GetSecurityPolicy() const
+{
+ if( kSecurityPolicyDontKnowYet != m_CachedSecPolicy )
+ return m_CachedSecPolicy;
+
+ if( !m_ScriptingMethod )
+ return kSecurityPolicyDenyAccess;
+
+ ScriptingInvocation invocation(m_ScriptingMethod);
+ invocation.AddString(m_URL.c_str());
+ MonoObject* result = invocation.Invoke();
+
+ // Cache the security policy so that we don't need to invoke Mono repeatedly once we know the status
+ m_CachedSecPolicy = result ? *reinterpret_cast<SecurityPolicy*>(mono_object_unbox (result)):
+ kSecurityPolicyDenyAccess;
+
+ return m_CachedSecPolicy;
+}
+
+bool WWWCrossDomainCheckedImpl::IsDownloadingDone() const
+{
+ return GetSecurityPolicy() != kSecurityPolicyDontKnowYet;
+}
+
+WWWCrossDomainChecked::WWWCrossDomainChecked (const char* url, const char* postData, int postDataLength, const WWWHeaders& headers, bool cached, int cacheVersion, UInt32 crc)
+ : WWW (cached, cacheVersion, crc),
+ m_PostData(0),
+ m_PostDataLength(postDataLength),
+ m_Headers(headers),
+ m_CrossChecker(new WWWCrossDomainCheckedImpl(url, cached, cacheVersion, crc))
+{
+ if( postDataLength > 0 && postData != 0 )
+ {
+ m_PostDataDataCopy.assign(postData, postData + postDataLength);
+ m_PostData = m_PostDataDataCopy.c_str();
+ }
+
+ // m_WWW is guaranteed to be initialized thoughout the lifetime of this object. At first it points to
+ // m_CrossChecker, which contains the stubs of all the required WWW member functions. This allows the
+ // trivial forwarders to be, indeed, trivial. For example, GetETA() or UnlockPartialData() do not require
+ // any additional checks whether the crossdomain checking has finished, or not.
+ m_WWW = m_CrossChecker;
+ m_WWW->Retain();
+}
+
+WWWCrossDomainChecked::~WWWCrossDomainChecked ()
+{
+ m_WWW->Release(); // It's a no-op for the crossdomain checker,
+ // but will destroy an initialized WWW backend
+ m_CrossChecker->Release();
+}
+
+bool WWWCrossDomainChecked::RequestLooksSafeEnoughToMakeWithoutPolicyAccess() const
+{
+ /// We do this in order to completely prevent downloads that have failed the cross domain check.
+ /// As a result, if you download a png with a .txt extension it will not even download it thus you can't use it as a texture.
+ /// Using ".png" with a failing cross domain check is actually legal.
+ /// As long as you don't use .bytes on it, but instead just use it as a texture. It results in no security risk.
+ ///
+ /// In theory this check could be removed but it might be confusing for admins looking at unity seeing that secret.pdf actually gets downloaded.
+ /// Even though the script code can't actually access the data.
+
+ //if it has post headers, we'll deny.
+ if (m_PostData != 0) return false;
+
+ static const char* allowed_extensions[] = {
+ "png", "jpg", "jpeg", "tga", "tiff", // if it looks like a texture, we'll allow it
+ "wav", "mp3", "ogg", "xm", "mod", "s3m", "it" // if it looks like an audiofile, we'll allow it
+ };
+
+ const char** iter_first = allowed_extensions;
+ const char** iter_last = allowed_extensions + ARRAY_SIZE(allowed_extensions);
+ return std::find( iter_first, iter_last, ToLower(GetPathNameExtension (m_CrossChecker->GetUrlString())) ) != iter_last;
+}
+
+bool WWWCrossDomainChecked::CanCreateDownloader() const
+{
+ // At this point we must have our security policy
+ Assert( m_CrossChecker->IsDownloadingDone() );
+
+ return m_WWW == m_CrossChecker && // Still no WWW backend
+ m_CrossChecker->HasNoErrors(); // There are no crossdomain checker errors
+}
+
+void WWWCrossDomainChecked::StartEmbeddedDownload()
+{
+ if( CanCreateDownloader() )
+ {
+ // Download hasn't begun yet, so we check what kind of security policy
+ // we have, or what type of content we want to download
+ if( GetSecurityPolicy() == kSecurityPolicyAllowAccess || RequestLooksSafeEnoughToMakeWithoutPolicyAccess() )
+ {
+ m_WWW->Release(); // Release previously hold counter on m_CrossChecker
+
+ m_WWW = CreatePlatformWWWBackend(m_CrossChecker->GetUrl(), m_PostData, m_PostDataLength, m_Headers,
+ m_Cached, m_CacheVersion, m_RequestedCRC);
+
+ #if SUPPORT_THREADS
+ m_WWW->SetThreadPriority(GetThreadPriority());
+ #endif
+ }
+ else
+ {
+ m_CrossChecker->SetErrorStringPtr("Rejected because no crossdomain.xml policy file was found");
+ }
+ }
+}
+
+void WWWCrossDomainChecked::BlockedStartEmbeddedDownload()
+{
+ // First we block on cross checker.
+ m_CrossChecker->BlockUntilDone();
+
+ // Now the cross domain checker has finished, we want to block
+ // on the donwloader until it's done.
+ StartEmbeddedDownload();
+}
+
+void WWWCrossDomainChecked::LockPartialData()
+{
+ // The crosschecker might have done its job, so we would like to
+ // start the real download and then lock the downloader's partial data.
+ if( m_CrossChecker->IsDownloadingDone() )
+ {
+ StartEmbeddedDownload();
+ }
+
+ // If embedded download has started, then this locks WWW backend, otherwise this
+ // tries to lock m_CrossChecker, which is no-op.
+ m_WWW->LockPartialData();
+}
+
+void WWWCrossDomainChecked::BlockUntilDone ()
+{
+ BlockedStartEmbeddedDownload();
+ m_WWW->BlockUntilDone();
+}
+
+const UInt8* WWWCrossDomainChecked::GetData()
+{
+ BlockedStartEmbeddedDownload();
+ return m_WWW->GetData();
+}
+std::size_t WWWCrossDomainChecked::GetSize()
+{
+ BlockedStartEmbeddedDownload();
+ return m_WWW->GetSize();
+}
+
+bool WWWCrossDomainChecked::HasDownloadedOrMayBlock ()
+{
+ if (GetError () != NULL)
+ {
+ ErrorString(Format("You are trying to load data from a www stream which had the following error when downloading.\n%s", GetError()));
+ return false;
+ }
+ return m_WWW->HasDownloadedOrMayBlock();
+}
+
+WWW::SecurityPolicy WWWCrossDomainChecked::GetSecurityPolicy() const
+{
+ return m_CrossChecker->GetSecurityPolicy();
+}
+
+const char* WWWCrossDomainChecked::GetUrl() const
+{
+ return m_CrossChecker->GetUrl();
+}
+
+// Trivial forwarders to the currently running WWW backend:
+// m_WWW can either be CrossDomainChecker or the backend that is created in CreatePlatformWWWBackend.
+bool WWWCrossDomainChecked::IsDownloadingDone() const
+{
+ if( !m_CrossChecker->IsDownloadingDone() )
+ {
+ // Can't be done, because crossdomain check is not finished
+ return false;
+ }
+
+ if( CanCreateDownloader() ) // There is still no WWW backend
+ {
+ // Dummy PartialDataLock/PartialDataUnlock sequence; this may create
+ // a WWW backend if content downloading is allowed.
+ WWW::AutoLock lock(*const_cast<WWWCrossDomainChecked*>(this));
+ }
+
+ return m_WWW->IsDone();
+}
+
+const char* WWWCrossDomainChecked::GetError()
+{
+ return m_WWW->GetError();
+}
+
+void WWWCrossDomainChecked::Cancel()
+{
+ return m_WWW->Cancel();
+}
+
+void WWWCrossDomainChecked::SetThreadPriority( ThreadPriority priority )
+{
+ WWW::SetThreadPriority(priority);
+ return m_WWW->SetThreadPriority(priority);
+}
+
+// PartialData functions will forward to the m_CrossDomainChecker if locking
+// was not acquired. Those functions are dummy and, naturally, do nothing.
+// If crossdomain checking has succeeded and permissions were sufficient to start
+// a download, then these partial data items will properly redirect to a WWW backend's
+// apropriate functions.
+
+const UInt8* WWWCrossDomainChecked::GetPartialData() const
+{
+ return m_WWW->GetPartialData();
+}
+
+std::size_t WWWCrossDomainChecked::GetPartialSize() const
+{
+ return m_WWW->GetPartialSize();
+}
+
+void WWWCrossDomainChecked::UnlockPartialData()
+{
+ return m_WWW->UnlockPartialData();
+}
+
+std::string WWWCrossDomainChecked::GetResponseHeaders()
+{
+ return m_WWW->GetResponseHeaders();
+}
+
+float WWWCrossDomainChecked::GetProgress () const
+{
+ // WWWCrossDomainChecked requires polling IsDone on the WWW to do it's job.
+ // Usually users would query isDone from script code, or yield the www, but
+ // they might only use .progress or .audioClip.isReadyToPlay, which would never
+ // call isDone, so we call it here.
+ IsDone();
+ return m_WWW->GetProgress();
+}
+
+double WWWCrossDomainChecked::GetETA () const
+{
+ return m_WWW->GetETA();
+}
+
+UnityWebStream* WWWCrossDomainChecked::GetUnityWebStream() const
+{
+ return m_WWW->GetUnityWebStream();
+}
+
+bool WWWCrossDomainChecked::IsCached () const
+{
+ return m_WWW->IsCached();
+}
+
+#endif // ENABLE_WEBPLAYER_SECURITY
+
+// Decodes % Escaped URL string
+std::string DecodeEscapedURL(const std::string& url)
+{
+ std::string urlCopy;
+ urlCopy.reserve(url.length());
+
+ int newLength = 0;
+ for (unsigned int i = 0u; i < url.length(); i++)
+ {
+ if (url[i] != '%')
+ {
+ urlCopy += url[i];
+ }
+ else if (i + 2 < url.length())
+ {
+ char hex[2] = { url[i + 1], url[i + 2] };
+ using namespace std;//Flash fix.
+ urlCopy += static_cast<char>(strtol(hex, NULL, 16));
+ i += 2;
+ }
+ }
+
+ return urlCopy;
+}
+#endif //ENABLE_WWW
diff --git a/Runtime/Export/WWW.cs b/Runtime/Export/WWW.cs
new file mode 100644
index 0000000..37652bc
--- /dev/null
+++ b/Runtime/Export/WWW.cs
@@ -0,0 +1,168 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+
+namespace UnityEngine
+{
+#if ENABLE_WWW
+ partial class WWW
+ {
+#if UNITY_WEBPLAYER || UNITY_EDITOR
+ static readonly char[] forbiddenCharacters = {
+ (char)0, (char)1, (char)2, (char)3, (char)4, (char)5,
+ (char)6, (char)7, (char)8, (char)9, (char)10,
+ (char)11, (char)12, (char)13, (char)14, (char)15,
+ (char)16, (char)17, (char)18, (char)19, (char)20,
+ (char)21, (char)22, (char)23, (char)24, (char)25,
+ (char)26, (char)27, (char)28, (char)29, (char)30,
+ (char)31, (char)127
+ };
+
+ // From http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader()-method
+ static readonly string[] forbiddenHeaderKeys =
+ {
+ "Accept-Charset",
+ "Accept-Encoding",
+ "Access-Control-Request-Headers",
+ "Access-Control-Request-Method",
+ "Connection",
+ "Content-Length",
+ "Cookie",
+ "Cookie2",
+ "Date",
+ "DNT",
+ "Expect",
+ "Host",
+ "Keep-Alive",
+ "Origin",
+ "Referer",
+ "TE",
+ "Trailer",
+ "Transfer-Encoding",
+ "Upgrade",
+ "User-Agent",
+ "Via",
+ "X-Unity-Version",
+ };
+
+ // Ensure headers do not contain invalid characters (ASCII 0-31, 127)
+ // This prevents XSS and header-spoofing vulnerabilities.
+ private static void CheckSecurityOnHeaders(string[] headers)
+ {
+ bool failNoisy = Application.GetBuildUnityVersion() >= Application.GetNumericUnityVersion("4.3.0b1");
+
+ // On non-webplayer platforms, we permit the platform itself to define the restrictions.
+ for(int i = 0; i < headers.Length; i+=2)
+ {
+ // headers[i] is the header name
+ // headers[i+1] is the header body
+
+ foreach(string forbiddenHeaderKey in forbiddenHeaderKeys)
+ {
+ if(string.Equals(headers[i], forbiddenHeaderKey, StringComparison.CurrentCultureIgnoreCase))
+ {
+ if(failNoisy)
+ {
+ throw new ArgumentException("Cannot overwrite header: " + headers[i]);
+ }
+ else
+ {
+ Debug.LogError("Illegal header overwrite, this will fail in 4.3 and above: " + headers[i]);
+ }
+ }
+ }
+
+ if(headers[i].StartsWith("Sec-") || headers[i].StartsWith("Proxy-"))
+ {
+ if(failNoisy)
+ {
+ throw new ArgumentException("Cannot overwrite header: " + headers[i]);
+ }
+ else
+ {
+ Debug.LogError("Illegal header overwrite, this will fail in 4.3 and above: " + headers[i]);
+ }
+ }
+
+ if(headers[i].IndexOfAny(forbiddenCharacters) > -1 ||
+ headers[i+1].IndexOfAny(forbiddenCharacters) > -1)
+ {
+ if(failNoisy)
+ {
+ throw new ArgumentException("Cannot include control characters " +
+ "in a HTTP header, either as key or value.");
+ }
+ else
+ {
+ Debug.LogError("Illegal control characters in header, this will fail in 4.3 and above");
+ }
+ }
+ }
+ }
+#endif
+
+#if !UNITY_METRO_API && !UNITY_WP8_API
+ private static string[] FlattenedHeadersFrom(Hashtable headers)
+ {
+ if (headers == null) return null;
+
+ var flattenedHeaders = new string[headers.Count * 2];
+ var i = 0;
+ foreach (DictionaryEntry entry in headers)
+ {
+ flattenedHeaders[i++] = entry.Key.ToString();
+ flattenedHeaders[i++] = entry.Value.ToString();
+ }
+
+ return flattenedHeaders;
+ }
+#else
+ private static string[] FlattenedHeadersFrom(Dictionary<string, string> headers)
+ {
+ if (headers == null) return null;
+
+ var flattenedHeaders = new string[headers.Count * 2];
+ var i = 0;
+ foreach (KeyValuePair<string, string> entry in headers)
+ {
+ flattenedHeaders[i++] = entry.Key.ToString();
+ flattenedHeaders[i++] = entry.Value.ToString();
+ }
+
+ return flattenedHeaders;
+ }
+#endif
+#if ENABLE_GENERICS && !UNITY_WEBGL
+ internal static Dictionary<string, string> ParseHTTPHeaderString(string input)
+ {
+ if (input == null) throw new ArgumentException("input was null to ParseHTTPHeaderString");
+ var result = new Dictionary<string, string>();
+ var reader = new StringReader(input);
+
+ int count = 0;
+ while (true)
+ {
+ var line = reader.ReadLine();
+ if (line == null) break;
+
+ // The first line in the response header is the HTTP status line, according to the
+ // HTTP 1.1 specification (http://tools.ietf.org/html/rfc2616#section-6). Lets save it.
+ if (count++ == 0 && line.StartsWith("HTTP"))
+ {
+ result["STATUS"] = line;
+ continue;
+ }
+
+ var index = line.IndexOf(": ");
+ if (index == -1) continue;
+ var key = line.Substring(0, index).ToUpper();
+ var value = line.Substring(index + 2);
+ result[key] = value;
+ }
+ return result;
+ }
+#endif
+ }
+#endif
+} \ No newline at end of file
diff --git a/Runtime/Export/WWW.h b/Runtime/Export/WWW.h
new file mode 100644
index 0000000..9bc48c5
--- /dev/null
+++ b/Runtime/Export/WWW.h
@@ -0,0 +1,420 @@
+#ifndef WWW_H
+#define WWW_H
+
+#if ENABLE_WWW
+
+#if (WEBPLUG || UNITY_IPHONE || UNITY_ANDROID || UNITY_FLASH || UNITY_XENON || UNITY_PS3 || UNITY_WINRT)
+#define WWW_USE_CURL 0
+#else
+#define WWW_USE_CURL 1
+#endif
+
+#define WWW_USE_BROWSER WEBPLUG && !WWW_USE_CURL
+
+#if WWW_USE_CURL
+#include "External/Curl/include/minimalcurl.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/AtomicRefCounter.h"
+#include "Runtime/GameCode/CallDelayed.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Utilities/NonCopyable.h"
+
+/// The time since startup
+double GetTimeSinceStartup ();
+
+class Thread;
+class Download;
+class UnityWebStream;
+class AsyncCachedUnityWebStream;
+class AssetBundle;
+class AudioClip;
+
+#include <map>
+
+enum WWWType {
+ kWWWTypeCurl,
+ kWWWTypeBrowser,
+ kWWWTypeCached,
+ kWWWTypeCrossDomainChecked,
+ kWWWFlash
+};
+
+class WWW : private NonCopyable
+{
+private:
+ UnityWebStream* m_UnityWebStream;
+ bool m_DidParseUnityWebStream;
+ int m_StreamingPosition;
+
+ #if SUPPORT_REPRODUCE_LOG
+ int m_ReproRemapCount;
+ #endif
+
+ AudioClip* m_AudioClip;
+
+protected:
+ bool m_Cached;
+ int m_CacheVersion;
+ UInt32 m_RequestedCRC;
+ friend class WWWCached;
+
+ ThreadPriority m_ThreadPriority;
+ string m_ResponseHeaders;
+ void FeedUnityWebStream (bool isCompleted);
+ bool SetErrorFromResponseHeaders (); // Returns true when error is set, otherwise returns false
+ UInt32 GetEstimatedDownloadSize() const;
+
+public:
+
+ class AutoLock
+ {
+ public:
+ AutoLock( WWW& www )
+ : m_WWW(&www)
+ {
+ www.LockPartialData();
+ }
+
+ ~AutoLock()
+ {
+ m_WWW->UnlockPartialData();
+ }
+
+ private:
+ AutoLock(const AutoLock&);
+ AutoLock& operator=(const AutoLock&);
+
+ private:
+ WWW* m_WWW;
+ };
+
+ enum SecurityPolicy
+ {
+ kSecurityPolicyDontKnowYet=0,
+ kSecurityPolicyAllowAccess=1,
+ kSecurityPolicyDenyAccess=2
+ };
+
+ WWW (bool cached, int cacheVersion, UInt32 crc)
+ // Private members (initialized by the order of their definition)
+ : m_UnityWebStream(NULL)
+ , m_DidParseUnityWebStream(false)
+ , m_StreamingPosition(0)
+ #if SUPPORT_REPRODUCE_LOG
+ , m_ReproRemapCount(0)
+ #endif
+ , m_AudioClip(NULL)
+
+ // Protected members
+ , m_Cached(cached)
+ , m_CacheVersion(cacheVersion)
+ , m_RequestedCRC(crc)
+ , m_ThreadPriority(kNormalPriority)
+ , m_ResponseHeaders() // Initially empty
+
+ // Private thread-safe reference counter
+ , m_RefCount()
+ {}
+
+ typedef std::map<std::string,std::string> WWWHeaders;
+
+ static WWW* Create (const char* url, const char * in_postData, int in_postLength, const WWWHeaders& in_headers, bool crossDomainChecked = true, bool cached = false, int cacheVersion = 0, UInt32 crc = 0);
+
+ virtual const UInt8* GetData() = 0;
+ virtual const UInt8* GetPartialData() const = 0;
+ virtual size_t GetSize() = 0;
+ virtual size_t GetPartialSize() const = 0;
+
+ virtual double GetETA() const = 0; //seconds remaining until we're done.
+
+ virtual void LockPartialData() = 0;
+ virtual void UnlockPartialData() = 0;
+
+ // Returns true when the download is complete or failed.
+ virtual void Cancel() = 0;
+ bool IsDone() const;
+ virtual float GetProgress() const = 0;
+ virtual float GetUploadProgress() const = 0;
+ virtual const char* GetError() = 0;
+ virtual void SetError (const std::string& error) {}
+ virtual const char* GetUrl() const = 0;
+ virtual std::string GetResponseHeaders();
+ virtual bool HasDownloadedOrMayBlock () = 0;
+ virtual void BlockUntilDone () = 0;
+ virtual SecurityPolicy GetSecurityPolicy() const;
+
+ virtual WWWType GetType () const = 0;
+
+ virtual UnityWebStream* GetUnityWebStream () const;
+
+ virtual bool IsCached () const;
+
+ void CallWhenDone(DelayedCall* func, Object* o, void* userData, CleanupUserData* cleanup);
+#if SUPPORT_THREADS
+ ThreadPriority GetThreadPriority() const { return m_ThreadPriority; }
+ virtual void SetThreadPriority( ThreadPriority priority );
+#endif
+
+ #if SUPPORT_REPRODUCE_LOG
+ int* GetReproRemapCount () { return &m_ReproRemapCount; }
+ #endif
+
+ AudioClip* GetAudioClip() const { return m_AudioClip; }
+ void SetAudioClip(AudioClip* clip) { m_AudioClip = clip; }
+
+protected:
+ virtual ~WWW ();
+
+#if ENABLE_WEBPLAYER_SECURITY
+ friend class WWWCrossDomainChecked;
+#endif
+ virtual bool IsDownloadingDone() const = 0;
+
+public:
+ /// These functions must be thread safe as they are called from the garbage collector thread
+ void Retain();
+ void Release();
+
+private:
+ AtomicRefCounter m_RefCount;
+};
+
+#if WWW_USE_BROWSER
+class WWWBrowser : public WWW
+{
+ Download* m_Download;
+ std::vector<UInt8> m_Buffer;
+ double m_Eta;
+ double m_StartDownloadTime;
+ std::string m_Url;
+ std::string m_Error;
+ std::string m_HeadersString;
+ char* m_PostData;
+ size_t m_PostLength;
+
+ int m_LockedPartialData;
+
+ int GetTotalBytesUntilLoadable() const;
+
+public:
+
+ WWWBrowser (const char* postDataPtr, int postDataLength, const WWWHeaders& headers, bool cached, int cacheVersion, UInt32 crc);
+
+ virtual const UInt8* GetData();
+ virtual size_t GetSize();
+
+ virtual bool IsDownloadingDone() const;
+
+ virtual float GetProgress() const;
+ virtual float GetUploadProgress() const;
+
+ virtual const char* GetError();
+ virtual void SetError (const std::string& error);
+
+ virtual const char* GetUrl() const;
+
+ virtual const UInt8* GetPartialData() const;
+ virtual size_t GetPartialSize() const;
+ virtual void LockPartialData();
+ virtual void UnlockPartialData();
+ virtual double GetETA() const; //seconds remaining until we're done.
+
+ virtual bool HasDownloadedOrMayBlock ();
+ virtual void BlockUntilDone ();
+ virtual void Cancel ();
+
+ void ForceProgressDownload ();
+
+ virtual WWWType GetType () const { return kWWWTypeBrowser; }
+ static int ProgressDownload(Download* download);
+
+public:
+ static WWWBrowser* CreateBrowser (const char* url, const char * in_postData, int in_postLength, const WWWHeaders& in_headers, bool cached, int cacheVersion, UInt32 crc);
+
+protected:
+ virtual ~WWWBrowser ();
+};
+#endif // WWW_USE_BROWSER
+
+
+#if WWW_USE_CURL
+
+class WWWCurl : public WWW
+{
+ private:
+ size_t alloc_size;
+ curl_slist* curlHeaders;
+ curl_slist* GetHeaderSList () ;
+
+ Mutex mutex;
+ size_t size;
+ UInt8* data;
+ char* errorBuffer;
+ bool abortDownload;
+ float progress;
+ float uploadProgress;
+ unsigned totalSize;
+ double eta;
+ double startTime;
+
+ char* postData;
+ int postLength; // -1 for GET requests
+ int postPosition;
+
+ WWWHeaders requestHeaders;
+
+ size_t AppendBytes(void * moreData, size_t bytes);
+ size_t PostBytes(void * moreData, size_t bytes);
+
+ CURLcode GetURL( const char* url );
+
+ Thread thread;
+ char* url;
+
+ int result;
+
+ void DoInit( const char* in_url, const char * in_postData, int in_postLength, const WWWHeaders& in_headers);
+ void Cleanup();
+
+ static size_t WriteCallback(void * data, size_t size, size_t elements, WWWCurl * myData);
+ static size_t ReadCallback(void * data, size_t size, size_t elements, WWWCurl * myData);
+ static size_t HeaderCallback(void * data, size_t size, size_t elements, WWWCurl * myData);
+ static int ProgressCallback (WWWCurl *myData, double dltotal, double dlnow, double ultotal, double ulnow);
+
+ public:
+
+ WWWCurl( const char* in_url, const char * in_postData, int in_postLength, const WWWHeaders& in_headers, bool cached, int cacheVersion, UInt32 crc );
+
+ virtual bool IsDownloadingDone() const;
+ virtual const UInt8* GetData();
+ virtual const char* GetError();
+ virtual void SetError (const std::string& error);
+ virtual const char* GetUrl() const;
+ virtual size_t GetSize();
+ virtual const UInt8* GetPartialData() const;
+ virtual size_t GetPartialSize() const;
+ virtual float GetProgress() const;
+ virtual float GetUploadProgress() const;
+ virtual double GetETA() const;
+
+ virtual void LockPartialData();
+ virtual void UnlockPartialData();
+ virtual bool HasDownloadedOrMayBlock ();
+
+ virtual void Cancel ();
+ virtual void SetThreadPriority( ThreadPriority priority );
+ void BlockUntilDone();
+
+ virtual WWWType GetType () const { return kWWWTypeCurl; }
+ virtual std::string GetResponseHeaders();
+
+ protected:
+ ~WWWCurl();
+
+ private:
+ void StartThread();
+ static void* WWW_ThreadEntryPoint(void* data);
+ UInt32 GetEstimatedDownloadSize() const;
+};
+#endif // WWW_USE_CURL
+
+class WWWDelayCall {
+ private:
+ WWW* m_wait_for;
+ DelayedCall* m_func;
+ Object* m_o;
+ void * m_userData;
+ CleanupUserData* m_cleanup;
+ public:
+ WWWDelayCall(WWW* www, DelayedCall* func, Object* o, void* userData, CleanupUserData* cleanup);
+ ~WWWDelayCall();
+ static void Callback(Object* o, void* userData);
+ static void Cleanup(void* userData);
+ static bool MatchForCancel(void* callBackUserData, void* cancelUserData);
+};
+
+#if ENABLE_WEBPLAYER_SECURITY
+
+void ProcessCrossDomainRequestsFromNonMainThread();
+
+class WWWCrossDomainCheckedImpl;
+class WWWCrossDomainChecked : public WWW
+{
+private:
+ const char* m_PostData;
+ int m_PostDataLength;
+ std::string m_PostDataDataCopy;
+ WWWHeaders m_Headers;
+
+ WWWCrossDomainCheckedImpl* m_CrossChecker;
+ WWW* m_WWW;
+
+ bool CanCreateDownloader() const;
+ bool RequestLooksSafeEnoughToMakeWithoutPolicyAccess() const;
+ void StartEmbeddedDownload();
+ void BlockedStartEmbeddedDownload();
+
+public:
+ WWWCrossDomainChecked (const char* url, const char* postData, int postDataLength, const WWWHeaders& headers, bool cached, int cacheVersion, UInt32 crc);
+
+ virtual SecurityPolicy GetSecurityPolicy() const;
+ virtual UnityWebStream* GetUnityWebStream() const;
+ virtual const UInt8* GetData();
+ virtual const UInt8* GetPartialData() const;
+ virtual size_t GetSize();
+ virtual size_t GetPartialSize() const;
+
+ virtual double GetETA() const;
+
+ virtual bool IsCached () const;
+
+ virtual void LockPartialData();
+ virtual void UnlockPartialData();
+
+ // Returns true when the download is complete or failed.
+ virtual void Cancel();
+ virtual bool IsDownloadingDone() const;
+ virtual float GetProgress() const;
+ virtual float GetUploadProgress() const { return 0.f; }
+ virtual const char* GetError();
+ virtual const char* GetUrl() const;
+ virtual std::string GetResponseHeaders();
+ virtual bool HasDownloadedOrMayBlock ();
+ virtual void BlockUntilDone ();
+
+ virtual void SetThreadPriority( ThreadPriority priority );
+
+ virtual WWWType GetType () const { return kWWWTypeCrossDomainChecked; }
+
+protected:
+ ~WWWCrossDomainChecked ();
+};
+#endif //ENABLE_WEBPLAYER_SECURITY
+
+// Constant error strings
+extern const char* kWWWErrCustomHeadersWithGET;
+extern const char* kWWWErrZeroPostData;
+extern const char* kWWWErrNULLPostDataWithPositiveLength;
+extern const char* kWWWErrCancelled;
+extern const char* kWWWErrPostDataWithNonHTTPSchema;
+extern const char* kWWWErrHeadersWithNonHTTPSchema;
+
+double CalculateEta (int downloadedBytes, int totalBytes, double startTime);
+
+const char* GetCachedWWWError(const WWW& www, std::string& err);
+
+#if !UNITY_FLASH
+
+std::string DecodeEscapedURL(const std::string& url);
+
+#endif
+
+#endif // ENABLE_WWW
+
+#endif // WWW_H
diff --git a/Runtime/Export/WinRT/AppTrial.txt b/Runtime/Export/WinRT/AppTrial.txt
new file mode 100644
index 0000000..37ec8f6
--- /dev/null
+++ b/Runtime/Export/WinRT/AppTrial.txt
@@ -0,0 +1,56 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#if UNITY_WINRT
+#include "PlatformDependent\WinRT\ApplicationTrial.h"
+#endif
+
+using namespace Unity;
+
+CSRAW
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using UnityEngineInternal;
+
+namespace UnityEngine.Windows
+{
+CONDITIONAL UNITY_WINRT_API
+CLASS LicenseInformation
+
+ /// Returns whether the user is using App trial version (rather than full version)
+ CUSTOM_PROP static bool isOnAppTrial
+ {
+ #if UNITY_WINRT
+ return ::IsOnAppTrial ();
+ #else
+ return false;
+ #endif
+ }
+
+ /// Windows Phone 8:
+ /// Redirects user to app page in the Store
+ /// Returns an empty string
+ /// Call isOnAppTrial to find out whether the user bought the app
+ ///
+ /// Windows Store apps:
+ /// Pops up a dialog for a user asking whether he wants to buy an app
+ /// If use buys an app, returns a valid purchase receipt string
+ CUSTOM public static string PurchaseApp ()
+ {
+ #if UNITY_WINRT
+ return ::PurchaseApp ();
+ #else
+ return SCRIPTING_NULL;
+ #endif
+ }
+
+CSRAW
+END
+
+CSRAW
+} \ No newline at end of file
diff --git a/Runtime/Export/Windows/WindowsCrypto.txt b/Runtime/Export/Windows/WindowsCrypto.txt
new file mode 100644
index 0000000..56daa50
--- /dev/null
+++ b/Runtime/Export/Windows/WindowsCrypto.txt
@@ -0,0 +1,46 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/Utilities/HashFunctions.h"
+
+
+using namespace Unity;
+
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using UnityEngineInternal;
+
+namespace UnityEngine.Windows
+{
+CONDITIONAL UNITY_WINRT_API
+CLASS Crypto
+ CUSTOM public static byte[] ComputeMD5Hash(byte[] buffer)
+ {
+ UInt8* first = Scripting::GetScriptingArrayStart<UInt8> (buffer);
+ int size = GetScriptingArraySize(buffer);
+ UInt8 outHash[16];
+ ComputeMD5Hash(first, size, outHash);
+ return CreateScriptingArray<UInt8>(outHash, sizeof(outHash), GetMonoManager().GetCommonClasses().byte);
+ }
+
+ CUSTOM public static byte[] ComputeSHA1Hash(byte[] buffer)
+ {
+ UInt8* first = Scripting::GetScriptingArrayStart<UInt8> (buffer);
+ int size = GetScriptingArraySize(buffer);
+ UInt8 outHash[20];
+ ComputeSHA1Hash(first, size, outHash);
+ return CreateScriptingArray<UInt8>(outHash, sizeof(outHash), GetMonoManager().GetCommonClasses().byte);
+ }
+CSRAW
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/Windows/WindowsDirectory.txt b/Runtime/Export/Windows/WindowsDirectory.txt
new file mode 100644
index 0000000..b034279
--- /dev/null
+++ b/Runtime/Export/Windows/WindowsDirectory.txt
@@ -0,0 +1,86 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Utilities/File.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/ProjectWizardUtility.h"
+#endif
+
+#if UNITY_METRO
+#include "PlatformDependent/MetroPlayer/MetroUtils.h"
+#endif
+
+using namespace Unity;
+
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using UnityEngineInternal;
+
+namespace UnityEngine.Windows
+{
+CONDITIONAL UNITY_WINRT_API
+CLASS Directory
+ CUSTOM_PROP public static string temporaryFolder
+ {
+ #if UNITY_METRO
+ return scripting_string_new(ConvertStringToUtf8(Windows::Storage::ApplicationData::Current->TemporaryFolder->Path->Data()));
+ #else
+ #if UNITY_WP8
+ string path = ConvertStringToUtf8(Platform::String::Concat(Windows::Storage::ApplicationData::Current->LocalFolder->Path, L"\\Temp")->Data());
+ #else
+ string path = GetProjectPath() + "/TempState";
+ #endif
+ if (IsDirectoryCreated(path) == false) CreateDirectory(path);
+ return scripting_string_new(path);
+ #endif
+ }
+ CUSTOM_PROP public static string localFolder
+ {
+ #if UNITY_WINRT
+ return scripting_string_new(ConvertStringToUtf8(Windows::Storage::ApplicationData::Current->LocalFolder->Path->Data()));
+ #else
+ string path = GetProjectPath() + "/LocalState";
+ if (IsDirectoryCreated(path) == false) CreateDirectory(path);
+ return scripting_string_new(path);
+ #endif
+ }
+ CUSTOM_PROP public static string roamingFolder
+ {
+ #if UNITY_METRO
+ return scripting_string_new(ConvertStringToUtf8(Windows::Storage::ApplicationData::Current->RoamingFolder->Path->Data()));
+ #else
+ #if UNITY_WP8
+ string path = ConvertStringToUtf8(Platform::String::Concat(Windows::Storage::ApplicationData::Current->LocalFolder->Path, L"\\Roaming")->Data());
+ #else
+ string path = GetProjectPath() + "/RoamingState";
+ #endif
+ if (IsDirectoryCreated(path) == false) CreateDirectory(path);
+ return scripting_string_new(path);
+ #endif
+ }
+ CUSTOM public static void CreateDirectory(string path)
+ {
+ CreateDirectory(path.AsUTF8());
+ }
+ CUSTOM public static bool Exists(string path)
+ {
+ return IsDirectoryCreated(path.AsUTF8());
+ }
+ CUSTOM public static void Delete(string path)
+ {
+ if (IsDirectoryCreated(path.AsUTF8()))
+ DeleteFileOrDirectory(path.AsUTF8());
+ }
+CSRAW
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/Windows/WindowsFile.txt b/Runtime/Export/Windows/WindowsFile.txt
new file mode 100644
index 0000000..9c63bd6
--- /dev/null
+++ b/Runtime/Export/Windows/WindowsFile.txt
@@ -0,0 +1,69 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Utilities/File.h"
+
+using namespace Unity;
+
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using UnityEngineInternal;
+
+namespace UnityEngine.Windows
+{
+CONDITIONAL UNITY_WINRT_API
+CLASS File
+ CUSTOM public static byte[] ReadAllBytes(string path)
+ {
+ File file;
+ if (file.Open(path.AsUTF8(), File::kReadPermission))
+ {
+ std::vector<UInt8> buffer;
+ buffer.resize(file.GetFileLength());
+ file.Read(&buffer[0], buffer.size());
+ file.Close();
+ return CreateScriptingArray<UInt8>(&buffer[0], buffer.size(), GetMonoManager().GetCommonClasses().byte);
+ }
+ else
+ {
+ ErrorStringMsg("File.ReadAllBytes - failed to open %s for reading", path.AsUTF8().c_str());
+ }
+ return SCRIPTING_NULL;
+ }
+
+ CUSTOM public static void WriteAllBytes(string path, byte[] bytes)
+ {
+ File file;
+ if (file.Open(path.AsUTF8(), File::kWritePermission))
+ {
+ int size = GetScriptingArraySize(bytes);
+ UInt8* rawBytes = Scripting::GetScriptingArrayStart<UInt8>(bytes);
+ file.Write(rawBytes, size);
+ file.Close();
+ }
+ else
+ {
+ ErrorStringMsg("File.WriteAllBytes - failed to open %s for writing", path.AsUTF8().c_str());
+ }
+ }
+ CUSTOM public static bool Exists(string path)
+ {
+ return IsFileCreated(path.AsUTF8());
+ }
+ CUSTOM public static void Delete(string path)
+ {
+ if (IsFileCreated(path.AsUTF8()))
+ DeleteFileOrDirectory(path.AsUTF8());
+ }
+CSRAW
+END
+
+CSRAW
+}
diff --git a/Runtime/Export/XboxKeyboard.txt b/Runtime/Export/XboxKeyboard.txt
new file mode 100644
index 0000000..c734c66
--- /dev/null
+++ b/Runtime/Export/XboxKeyboard.txt
@@ -0,0 +1,51 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Utilities/Utility.h"
+
+#if UNITY_XENON
+#include "PlatformDependent/Xbox360/Source/Services/Keyboard.h"
+#endif
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using UnityEngine;
+
+namespace UnityEngine
+{
+ // Xbox Guide's keyboard-style input interface
+ CONDITIONAL UNITY_XENON_API
+ CLASS X360Keyboard
+
+ CSRAW
+ internal X360Keyboard() {}
+
+ // Keyboard input result
+ public delegate void DelegateResult(bool textValid, string text);
+ public static DelegateResult OnResult;
+
+ CSRAW private static void TriggerOnResult(bool textValid, string text)
+ {
+ if (OnResult != null)
+ OnResult(textValid, text);
+ }
+
+ // Shows the onscreen keyboard.
+ CUSTOM public static bool Show(UInt32 userIndex, UInt32 characterLimit, string defaultText, string titleText, string descriptionText)
+ {
+ #if UNITY_XENON
+ return xenon::Keyboard::Show(userIndex, characterLimit, MonoStringToWideCpp(defaultText.str), MonoStringToWideCpp(titleText.str), MonoStringToWideCpp(descriptionText.str));
+ #else
+ return false;
+ #endif
+ }
+
+ END
+
+CSRAW
+} // namespace
diff --git a/Runtime/Export/XboxServices.cs b/Runtime/Export/XboxServices.cs
new file mode 100644
index 0000000..23cddf6
--- /dev/null
+++ b/Runtime/Export/XboxServices.cs
@@ -0,0 +1,794 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Threading;
+using UnityEngine;
+
+namespace UnityEngine
+{
+ #if UNITY_XENON_API && ENABLE_XENON_SOCIALAPI
+ // TODO: We have no concept of achievement types like xbox has, not exposed
+ // TODO: No concept of user privilege levels, not exposed
+ // NOTE: There doesn't seem to be any way to explicitly set your own state (online/offline/away/busy/playing)
+ public class XboxLive : ISocial
+ {
+ private static LocalUser s_LocalUser;
+ private Action<AchievementDescription[]> m_AchievementDescriptionCallback;
+ private Action<bool> m_AchievementReportingCallback;
+
+ private static GameObject s_SessionObject;
+ private static GameObject s_LeaderboardRoutine;
+
+ internal const uint kDefaultUserIndex = 0;
+
+ public static bool onlineMode { get; set; }
+
+ public XboxLive()
+ {
+ if (X360Core.IsUserSignedIn(0, true))
+ {
+ Debug.Log("User already signed in, in online mode");
+ onlineMode = true;
+ }
+
+ // DEBUG: Log when these callbacks are triggered but have not been set elsewhere
+ X360Achievements.OnAchievementsEnumerated = () => { Debug.Log("OnAchievementsEnumerated called"); };
+ X360Achievements.OnUserAchievementsUpdated = (id) => { Debug.Log("OnUserAchievementsUpdated called for user " + id); };
+ X360Achievements.OnAward =
+ (uid, aid, status) => { Debug.Log("OnAward called for user=" + uid + " achievement=" + aid + " status=" + status); };
+ }
+
+ public void ShowAchievementsUI()
+ {
+ X360Achievements.ShowUI(kDefaultUserIndex);
+ }
+
+ // TODO: These should include an action which is triggered (OnSystemUIVisibilityChange) when the UI state changes (UI is dismissed)
+ public void ShowAchievementsUI(uint userIndex)
+ {
+ X360Achievements.ShowUI(userIndex);
+ }
+
+ public void ShowLeaderboardUI()
+ {
+ Debug.Log("Not implemented");
+ }
+
+ public void ShowFriendsUI(uint userIndex)
+ {
+ X360Friends.ShowFriendsUI(userIndex);
+ }
+
+ // NOTE: localUser.underage is not implemented on Xbox Live
+ public LocalUser localUser
+ {
+ get
+ {
+ if (s_LocalUser == null)
+ s_LocalUser = new LocalUser();
+ GetLocalUser(kDefaultUserIndex, ref s_LocalUser);
+ return s_LocalUser;
+ }
+ }
+
+ // TODO: Cache user so he's not recreated every time
+ // TODO: Maybe set this up as a generic list, the list is populated as users are logged in and valid, no need to create on access, no need for seperate GetCount() type functions
+ public LocalUser this[uint index]
+ {
+ get
+ {
+ LocalUser user = new LocalUser();
+ GetLocalUser(index, ref user);
+ return user;
+ }
+ }
+
+ private void GetLocalUser(uint index, ref LocalUser user)
+ {
+ user.m_Authenticated = X360Core.IsUserSignedIn(index, onlineMode);
+ if (!user.m_Authenticated)
+ {
+ //Debug.Log("Must be logged in before getting local user details");
+ return;
+ }
+ user.m_UserName = X360Core.GetUserName(index);
+ user.m_UserId = X360Core.GetUserOnlinePlayerId(index).Raw.ToString();
+ user.m_Friends = GetFriendsList(index);
+ user.m_Image = X360Core.GetUserGamerPicture(index, true);
+ }
+
+ public void Authenticate(Action<bool> callback)
+ {
+ // Request online login of exactly 1 user
+ X360Core.RequestSignIn(1, 1, onlineMode);
+ X360Core.OnUserStateChange = delegate() { callback(true); };
+ }
+
+ // Request sign-in for min users up to max user count
+ public void Authenticate(uint minUsers, uint maxUsers, Action<bool> callback)
+ {
+ X360Core.RequestSignIn(minUsers, maxUsers, onlineMode);
+ // Xbox core doesn't report success/failure of sign-in attempts
+ // so lets just wrap it in another delegate which always reports true
+ // I assume this means all users have signed in
+ X360Core.OnUserStateChange = delegate() { callback(true); };
+ }
+
+ // This is kind of pointless here as the friends list is not loaded seperately
+ public void LoadFriends(Action<bool> callback)
+ {
+ /*if (X360Friends.IsInitialized(0))
+ {
+ Debug.Log("Not initialized yet... ");
+ X360Friends.OnFriendsUpdated = delegate(uint index) { callback(true); };
+ return;
+ }*/
+ callback(true);
+ Debug.Log("Friends list is always populated in the local user automatically");
+ }
+
+ private UserProfile[] GetFriendsList(uint index)
+ {
+ var friends = new List<UserProfile>();
+ for (uint i = 0; i < X360Friends.GetFriendCount(index); i++)
+ {
+ UserProfile friend = new UserProfile();
+ friend.m_UserName = X360Friends.GetFriendName(index, i);
+ friend.m_UserId = X360Friends.GetFriendPlayerId(index, i).Raw.ToString();
+ friend.m_IsFriend = true;
+ X360FriendState state = X360Friends.GetFriendState(index, i);
+ friend.m_State = ConvertState(state);
+ friend.m_Image = X360Core.GetPlayerGamerPicture(index, X360Friends.GetFriendPlayerId(index, i), false);
+ friends.Add(friend);
+ }
+ return friends.ToArray();
+ }
+
+ private UserState ConvertState(X360FriendState state)
+ {
+ if (state.IsOnline) return UserState.Online;
+ if (state.IsOnlineAndAway) return UserState.OnlineAndAway;
+ if (state.IsOnlineAndBusy) return UserState.OnlineAndBusy;
+ if (state.IsPlaying) return UserState.Playing;
+ return UserState.Offline;
+ }
+
+ // Achievements are set up with the Xbox 360 and LIVE Authoring Submission Tool (XLAST)
+ // The points (or gamescore) of each one depends on the title type (retail/arcade).
+ // They are actually loaded automatically at startup, so this doesn't actually load
+ // anything.
+ public void LoadAchievementDescriptions(Action<AchievementDescription[]> callback)
+ {
+ if (!X360Achievements.IsEnumerated())
+ {
+ m_AchievementDescriptionCallback = callback;
+ X360Achievements.OnAchievementsEnumerated = CallbackAchivementDescriptionLoader;
+ }
+ else if (X360Achievements.GetCount() == 0)
+ {
+ Debug.Log("No achievement descriptions found");
+ callback(new AchievementDescription[0]);
+ }
+ else
+ {
+ callback(PopulateAchievementDescriptions());
+ }
+ }
+
+ private void CallbackAchivementDescriptionLoader()
+ {
+ if (m_AchievementDescriptionCallback != null)
+ m_AchievementDescriptionCallback(PopulateAchievementDescriptions());
+ }
+
+ private AchievementDescription[] PopulateAchievementDescriptions()
+ {
+ var achievements = new List<AchievementDescription>();
+ for (uint i = 0; i < X360Achievements.GetCount(); ++i)
+ {
+ // TODO: Should the points maybe just be an uint like xbox uses? Not like you get negative points ever.
+ X360Achievement xboxAchoo = new X360Achievement(i);
+ AchievementDescription achievement = new AchievementDescription(
+ xboxAchoo.Id.ToString(),
+ xboxAchoo.Label,
+ xboxAchoo.Picture,
+ xboxAchoo.Description,
+ xboxAchoo.Unachieved,
+ xboxAchoo.ShowUnachieved,
+ (int)xboxAchoo.Cred);
+ achievements.Add(achievement);
+ }
+ return achievements.ToArray();
+ }
+
+ // Apparently xbox has no concept of unhiding achievements by reporting 0 progress
+ // TODO: This is printed in the log:
+ // '[XUI] Warning: XuiControlPlayOptionalVisual: no fallback for control: PopupControl. Trying to play: "Normal"->"EndNormal"'
+ public void ReportProgress(string id, double progress, Action<bool> callback)
+ {
+ uint numericId;
+ if (!XboxLiveUtil.TryExtractId(id, out numericId)) return;
+
+ ReportProgress(numericId, progress, callback);
+ }
+
+ public void ReportProgress(uint id, double progress, Action<bool> callback)
+ {
+ m_AchievementReportingCallback = callback;
+ X360Achievements.OnAward = CallbackAchivementReported;
+ X360Achievements.AwardUser(kDefaultUserIndex, id);
+ }
+
+ private void CallbackAchivementReported(uint userIndex, uint achievementId, X360AchievementStatus status)
+ {
+ // TODO: Check if the desired achievement ID actually got updated.
+ // TODO: We should have enums here instead of bools
+ if (m_AchievementReportingCallback != null)
+ {
+ if (status == X360AchievementStatus.AlreadyAwarded || status == X360AchievementStatus.Succeeded)
+ m_AchievementReportingCallback(true);
+ else
+ m_AchievementReportingCallback(false);
+ }
+ }
+
+ public void LoadAchievements(Action<Achievement[]> callback)
+ {
+ // TODO: This should really be communicated back with an enum, but since users can re-enumerate
+ // it's kind of useless. We should allow that or automatically handle it.
+ if (!X360Achievements.IsEnumerated())
+ {
+ Debug.Log("Achievements not yet enumerated");
+ callback(new Achievement[0]);
+ }
+ if (X360Achievements.GetCount() == 0)
+ {
+ Debug.Log("No achievements found or achieved");
+ callback(new Achievement[0]);
+ }
+ else
+ {
+ callback(PopulateAchievements(kDefaultUserIndex));
+ }
+ }
+
+ private Achievement[] PopulateAchievements(uint index)
+ {
+ var achievements = new List<Achievement>();
+ for (uint i = 0; i < X360Achievements.GetCount(); ++i)
+ {
+ X360Achievement xboxAchoo = new X360Achievement(i);
+ if (X360Achievements.IsUnlocked(index, xboxAchoo.Id, true))
+ {
+ Achievement achievement = new Achievement(
+ xboxAchoo.Id.ToString(),
+ 100.0,
+ true,
+ false,
+ X360Achievements.GetUnlockTime(index, xboxAchoo.Id));
+ achievements.Add(achievement);
+ }
+ }
+ return achievements.ToArray();
+ }
+
+ // Got this:
+ // WRN[XGI]: Mismatched types for property 0x10000001. XUSER_PROPERTY.value.type = 2 but property type is 1. Skipping write. Pass the correct type or 0.
+ public void ReportScore(long score, string board, Action<bool> callback)
+ {
+ uint boardId;
+ if (!XboxLiveUtil.TryExtractId(board, out boardId)) return;
+
+ // There must be a Score property assigned to this leaderboard and it must expect a 64 bit value
+ uint propertyId;
+ if (XboxLiveUtil.TryExtractId("PROPERTY_SCORE", out propertyId))
+ {
+ Debug.Log("Found Score property: " + propertyId);
+ var properties = new X360UserProperty[1];
+ properties[0].Id = propertyId;
+ properties[0].Value.Type = X360UserDataType.Int64;
+ properties[0].Value.ValueInt64 = score;
+ ReportScore(boardId, properties, callback);
+ }
+ else
+ Debug.LogError("Failed to report score to " + board);
+ }
+
+ // TODO: The API is set up so you have one leaderboard object, and muliple ones if you need to report/read
+ // to/from multiple leaderboards. Xbox has the X360StatsViewProperties value which allows you to report
+ // scores etc to multiple leaderboard views at a time. Here we always have one such property.
+ public void ReportScore(uint boardId, X360UserProperty[] props, Action<bool> callback)
+ {
+ if (X360Core.GetTotalOnlineUsers() == 0)
+ {
+ Debug.Log("ERROR: Leaderboards can only be used when the user is logged in online (online=" + X360Core.GetTotalOnlineUsers() + " signed-in=" + X360Core.GetTotalSignedInUsers() + ")");
+ callback(false);
+ return;
+ }
+
+ LeaderboardRoutine routine = GetRoutine();
+ XboxScore xboxScore = new XboxScore();
+ xboxScore.boardId = boardId;
+ xboxScore.properties = props;
+ routine.ReportScore(xboxScore, callback);
+ }
+
+ public void LoadScores(string category, Action<Score[]> callback)
+ {
+ uint categoryId;
+ if (!XboxLiveUtil.TryExtractId(category, out categoryId)) return;
+
+ // This will find all columns assigned to this leaderboard, if specific columns are desired
+ // you need to use the xbox specific LoadScores call which has the columns parameter
+ ushort[] columnIds;
+ if (XboxLiveUtil.TryExtractId("STATS_COLUMN_" + category.ToUpper(), out columnIds))
+ LoadScores(categoryId, columnIds, callback);
+ else
+ Debug.LogError("Failed to load scores from " + category);
+ }
+
+ // We convert X360StatsRow + X360StatsColumn objects to Score objects.
+ // NOTE: Each row contains user info + all the columns for him. Here we will only support the user info + 1 single column (value/score/points)
+ // NOTE: We only support getting long (64bit) values, not floats etc.
+ // NOTE: The date field in the Score class is not populated, not supported here unless we support it as a custom column in the leaderboard config.
+ // TODO: Fix formattedValue field, it should be possible to populate based on the localized string accociated with a column
+ // TODO: Unused row fields: Rating + Gamertag
+ // NOTE: Arbitrated leaderboards + the TrueSkill system complicates matters here. An
+ // arbitrated session must be started to be able to report scores on an arbitrated leaderboard.
+ // When using TrueSkill, every player must report all scores (also for other players) and the server
+ // verifies they are correct.
+ public void LoadScores(uint boardId, ushort[] columnIds, Action<Score[]> callback)
+ {
+ LeaderboardRoutine routine = GetRoutine();
+ routine.LoadScores(boardId, columnIds, callback);
+ }
+
+ private LeaderboardRoutine GetRoutine()
+ {
+ LeaderboardRoutine routine;
+ if (s_LeaderboardRoutine == null)
+ {
+ s_LeaderboardRoutine = new GameObject();
+ routine = s_LeaderboardRoutine.AddComponent<LeaderboardRoutine>();
+ }
+ else
+ routine = s_LeaderboardRoutine.GetComponent<LeaderboardRoutine>();
+ return routine;
+ }
+
+ internal static XboxLiveSession GetSession()
+ {
+ XboxLiveSession session;
+ if (s_SessionObject == null)
+ {
+ Debug.Log("Creating new session object");
+ s_SessionObject = new GameObject();
+ session = s_SessionObject.AddComponent<XboxLiveSession>();
+ }
+ else
+ {
+ Debug.Log("Reusing old session");
+ session = s_SessionObject.GetComponent<XboxLiveSession>();
+ }
+ return session;
+ }
+
+ public void LoadScores(Leaderboard board, Action<bool> callback)
+ {
+ board.m_Loading = true;
+ var routine = GetRoutine();
+ routine.LoadScores((XboxLeaderboard) board, callback);
+ }
+
+ public bool GetLoading(Leaderboard board)
+ {
+ return board.m_Loading;
+ }
+
+ public static void OpenSession(Action<bool> callback)
+ {
+ XboxLiveSession session = GetSession();
+
+ if (session.running)
+ {
+ callback(true);
+ return;
+ }
+
+ session.SetupSession(callback);
+ }
+
+ public static void CloseSession(Action<bool> callback)
+ {
+ if (s_SessionObject == null)
+ {
+ callback(true);
+ return;
+ }
+
+ var routine = s_SessionObject.GetComponent<XboxLiveSession>();
+ routine.Cleanup(callback);
+ }
+ }
+
+ class LeaderboardRoutine : MonoBehaviour
+ {
+ private Action<Score[]> m_ScoresCallback;
+ private Action<bool> m_LeaderboardCallback;
+ // If it's possible with the Xbox SDK to do parallel leaderboard queries, then expand this into a boardID=>board hashtable
+ private XboxLeaderboard m_CurrentBoard;
+
+ public void ReportScore(XboxScore score, Action<bool> callback)
+ {
+ StartCoroutine(DoReportScore(score, callback));
+ }
+
+ // TODO: Public/private slots needs to be exposed somehow, but this is only relevant for multiplayer games
+ // TODO: No callbacks are accociated with writes (as they happen later)?
+ // TODO: Deal with this error: WRN[XGI]: Invalid leaderboard id: 0x00000000
+ public IEnumerator DoReportScore(XboxScore score, Action<bool> callback)
+ {
+ /*yield return StartCoroutine(SetupSession());
+ Debug.Log("Session setup done");
+ if (m_Session == null)
+ {
+ callback(false);
+ yield break;
+ }*/
+ XboxLiveSession session = XboxLive.GetSession();
+ if (!session.running)
+ {
+ Debug.Log("Must open a session first");
+ callback(false);
+ yield break;
+ }
+
+ var viewProp = new X360StatsViewProperties[1];
+ viewProp[0] = new X360StatsViewProperties { ViewId = score.boardId, Properties = score.properties };
+ X360PlayerId onlineId = X360Core.GetUserOnlinePlayerId(XboxLive.kDefaultUserIndex);
+ if (!session.activeSession.WriteStats(onlineId, viewProp))
+ {
+ Debug.LogError("Failed to send leaderboard data to server");
+ //yield return StartCoroutine(TearDownSession());
+ callback(false);
+ }
+
+ // DEBUG: Maybe also skip this yield
+ yield return new WaitForSeconds(0.1F);
+ Debug.Log(DateTime.Now + ": Waiting for session to become available.");
+ yield return !session.activeSession.IsIdle();
+
+ //Debug.Log("Flush stats");
+ //m_Session.FlushStats();
+ //yield return new WaitForSeconds(0.1F);
+ //Debug.Log(DateTime.Now + ": Waiting for session to become available.");
+ //yield return !m_Session.IsIdle();
+
+ // TODO: Was it actually a success?
+ callback(true);
+ }
+
+ public void LoadScores(uint categoryId, ushort[] columnIds, Action<Score[]> callback)
+ {
+ m_ScoresCallback = callback;
+ XboxLeaderboard board = new XboxLeaderboard();
+ board.boardId = categoryId;
+ board.columnIds = columnIds;
+ board.playerScope = PlayerScope.FriendsOnly;
+ StartCoroutine(DoLoadScores(board));
+ }
+
+ public void LoadScores(XboxLeaderboard leaderboard, Action<bool> callback)
+ {
+ m_LeaderboardCallback = callback;
+ m_CurrentBoard = leaderboard;
+
+ string categoryString = "";
+ if (!XboxLiveUtil.TryExtractString(leaderboard.boardId, "STATS_VIEW_", out categoryString))
+ Debug.Log("Failed to set leaderboard category name");
+ else
+ m_CurrentBoard.category = categoryString;
+ StartCoroutine(DoLoadScores(leaderboard));
+ }
+
+ public IEnumerator DoLoadScores(XboxLeaderboard board)
+ {
+ XboxLiveSession session = XboxLive.GetSession();
+ if (!session.running)
+ {
+ Debug.Log("Must open a session first");
+ yield break;
+ }
+
+ /*yield return StartCoroutine(SetupSession());
+ if (m_Session == null)
+ {
+ Debug.Log("Session setup failed");
+ FinishCallbacks(false, new Score[0]);
+ yield break;
+ }*/
+
+ if (board.timeScope != TimeScope.AllTime)
+ Debug.Log("Time scope filtering is not supported, scores from any time are always used.");
+
+ // TODO: Figure out what should be supported, ReadPlayerStats only returns to score
+ // of the local player, this should go into Leaderboard.localPlayerScore
+ if (board.playerScope == PlayerScope.FriendsOnly)
+ Debug.Log("Friends only support not implemented yet, loading from all players");
+ X360Stats.ReadLeaderboardByIndex(
+ board.boardId,
+ board.columnIds,
+ (uint) board.range.from,
+ (uint) board.range.count,
+ ProcessLeaderboardResult);
+
+ // TODO: If the scores returned do not contain the local player score we need to fetch it
+ // seperately so the localPlayerScore property can be populated.
+ //X360Stats.ReadPlayerStats(board.boardId, board.columnIds, m_OnlineID, PopulateLocalPlayerScore);
+
+ Debug.Log(DateTime.Now + ": Waiting for session to become available.");
+ yield return !session.activeSession.IsIdle();
+ if (session.activeSession.LastFunctionFailed())
+ {
+ Debug.Log("Failed to read leaderboard info");
+ //m_Session = null;
+ yield break;
+ }
+ }
+
+ private void FinishCallbacks(bool result, Score[] scores)
+ {
+ if (m_CurrentBoard != null)
+ {
+ m_CurrentBoard.m_Scores = scores;
+ m_CurrentBoard.m_Loading = false;
+ if (m_LeaderboardCallback != null)
+ {
+ m_LeaderboardCallback(result);
+ m_LeaderboardCallback = null;
+ }
+ m_CurrentBoard = null;
+ }
+ else if (m_ScoresCallback != null)
+ {
+ m_ScoresCallback(scores);
+ m_ScoresCallback = null;
+ }
+ }
+
+ // TODO: This currently assumes one and only one value (column) per score element
+ private void ProcessLeaderboardResult(UInt32 viewId, UInt32 totalRows, X360StatsRow[] rows)
+ {
+ Debug.Log("Received " + rows.Length + " out of " + totalRows + " rows for id " + viewId);
+ m_CurrentBoard.m_MaxRange = totalRows;
+ var scores = new List<Score>();
+ foreach (X360StatsRow row in rows)
+ {
+ string playerId = row.Xuid.ToString();
+ long value = -1;
+ if (row.Columns.Length >= 1)
+ value = row.Columns[0].Value.ValueInt64;
+ DateTime date = DateTime.Now;
+ int rank = (int)row.Rank;
+
+ Score score = new Score(viewId.ToString(), value, playerId, date, value.ToString(), rank);
+ scores.Add(score);
+ }
+ FinishCallbacks(true, scores.ToArray());
+ }
+ }
+
+ public class XboxScore : Score
+ {
+ public X360UserProperty[] properties { get; set; }
+ public uint boardId { get; set; }
+ }
+
+ public class XboxLeaderboard : Leaderboard
+ {
+ public uint boardId { get; set; }
+ public ushort[] columnIds { get; set; }
+
+ public XboxLeaderboard()
+ {
+ boardId = 0;
+ columnIds = new ushort[0];
+ }
+
+ public override string ToString()
+ {
+ return base.ToString() + " BoardID: '" + boardId + "' ColumnIds: '" + columnIds.Length + "'";
+ }
+ }
+
+
+ internal class XboxLiveSession : MonoBehaviour
+ {
+ private X360Session m_Session;
+ private bool m_SessionRunning;
+ private X360PlayerId m_OnlineID;
+ private bool m_HazError;
+
+ internal bool running { get { return m_SessionRunning; } }
+ internal X360Session activeSession { get { return m_Session; } }
+
+ XboxLiveSession()
+ {
+ Debug.Log("XboxLiveSession GO created");
+ // Get the online ID, sanity checks is performed earlier to ensure that the user is actually online
+ m_OnlineID = X360Core.GetUserOnlinePlayerId(XboxLive.kDefaultUserIndex);
+ }
+
+ public void Cleanup(Action<bool> callback)
+ {
+ StartCoroutine(TearDownSession(callback));
+ }
+
+ internal void SetupSession(System.Action<bool> callback = null)
+ {
+ StartCoroutine(DoSetupSession(callback));
+ }
+
+ // TODO: These sessions calls should all be returning success boolean...
+ // TODO: Deal with this error: WRN[XGI]: User at index 0 is already a member of a presence session.
+ private IEnumerator DoSetupSession(System.Action<bool> callback)
+ {
+ if (m_SessionRunning)
+ {
+ Debug.Log("Skip session creation as we already have one running");
+ yield break;
+ }
+
+ m_HazError = false;
+
+ if (m_Session == null || m_Session.IsDead())
+ {
+ Debug.Log(DateTime.Now + ": Creating session.");
+ m_Session = X360Session.CreateSinglePlayerSessionWithStats(XboxLive.kDefaultUserIndex, 4, 0);
+ if (m_Session == null) yield break;
+ }
+ yield return StartCoroutine(ValidateOperation("Session creation failed", callback));
+ if (m_HazError) yield break;
+
+ Debug.Log(DateTime.Now + ": Session is ready. Joining.");
+ if (!m_Session.Join(m_OnlineID, false))
+ {
+ Debug.LogError("Failed to join session");
+ m_Session = null;
+ yield break;
+ }
+ yield return StartCoroutine(ValidateOperation("Session joining failed", callback));
+ if (m_HazError) yield break;
+
+ Debug.Log(DateTime.Now + ": Session is ready. Starting.");
+ m_Session.Start();
+ yield return StartCoroutine(ValidateOperation("Session failed to start", callback));
+ if (m_HazError) yield break;
+
+ Debug.Log("Session now running");
+ m_SessionRunning = true;
+ if (callback != null) callback(true);
+ }
+
+ // We do not delete the running session completely but only end it's operation. It will
+ // be reused if OpenSession is called again.
+ private IEnumerator TearDownSession(Action<bool> callback = null)
+ {
+ if (m_Session == null || m_Session.IsDead())
+ {
+ if (callback != null) callback(true);
+ yield break;
+ }
+ m_HazError = false;
+
+ Debug.Log("Leaving running session.");
+ m_Session.Leave(m_OnlineID, false);
+ yield return StartCoroutine(ValidateOperation("Failed to leave session", callback));
+ if (m_HazError) yield break;
+
+ Debug.Log("End running session");
+ m_Session.End();
+ yield return StartCoroutine(ValidateOperation("Failed to end session", callback));
+ if (m_HazError) yield break;
+
+ //Debug.Log("Deleting running session.");
+ //m_Session.Delete();
+ //yield return StartCoroutine(ValidateOperation("Failed to delete session", callback));
+ //if (m_HazError) yield break;
+
+ m_SessionRunning = false;
+ Debug.Log("Successfully tore down session.");
+ if (callback != null) callback(true);
+ }
+
+ private IEnumerator ValidateOperation(string error, Action<bool> callback)
+ {
+ //Debug.Log(DateTime.Now + ": Waiting for session to become available.");
+ yield return !m_Session.IsIdle();
+ if (m_Session.LastFunctionFailed())
+ {
+ Debug.Log(error);
+ m_Session = null;
+ if (callback != null) callback(false);
+ m_HazError = true;
+ yield break;
+ }
+ }
+ }
+
+
+ static internal class XboxLiveUtil
+ {
+ // TODO: Also look out for javascript/boo assemblies
+ internal static Assembly s_UserScriptingAssembly = Assembly.Load(new AssemblyName("Assembly-CSharp"));
+
+ static internal bool TryExtractId(string id, out ushort[] shorts)
+ {
+ List<ushort> foundShorts = new List<ushort>();
+ bool success = false;
+ foreach (FieldInfo info in GetSpaConfigFields())
+ {
+ //Debug.Log("Haz " + info);
+ if (info.Name.Contains(id.ToUpper()))
+ {
+ // The reflected enum contains uint values, so it must first be cast to that type
+ ushort value = (ushort)(uint)info.GetValue(info.GetType());
+ foundShorts.Add(value);
+ success = true;
+ //Debug.Log("Found desired ID: " + value);
+ }
+ }
+ shorts = foundShorts.ToArray();
+ if (!success) Debug.Log("Failed to find " + id + " in SPAConfig enum");
+ return success;
+ }
+
+ static internal bool TryExtractId(string id, out uint numericId)
+ {
+ if (uint.TryParse(id, out numericId)) return true;
+
+ foreach (FieldInfo info in GetSpaConfigFields())
+ {
+ //Debug.Log("Haz " + info);
+ if (info.Name.Contains(id.ToUpper()))
+ {
+ numericId = (uint)info.GetValue(info.GetType());
+ //Debug.Log("Found desired ID: " + numericId);
+ return true;
+ }
+ }
+
+ Debug.Log("Failed to find " + id + " in SPAConfig enum");
+ return false;
+ }
+
+ static internal bool TryExtractString(uint numericId, string pattern, out string outputString)
+ {
+ outputString = "";
+ foreach (FieldInfo info in GetSpaConfigFields())
+ {
+ if (info.Name.Contains(pattern) && (uint)info.GetValue(info.GetType()) == numericId)
+ {
+ outputString = info.Name;
+ return true;
+ }
+ }
+
+ Debug.Log("Failed to find " + pattern + " field in SPAConfig enum which matched " + numericId);
+ return false;
+ }
+
+ static internal FieldInfo[] GetSpaConfigFields()
+ {
+ if (s_UserScriptingAssembly == null) return new FieldInfo[0];
+ object spaObject = s_UserScriptingAssembly.CreateInstance("spaconfig", true);
+ if (spaObject == null) return new FieldInfo[0];
+ Type spaType = spaObject.GetType();
+ FieldInfo[] spaFields = spaType.GetFields();
+ return spaFields;
+ }
+ }
+
+#endif
+}
diff --git a/Runtime/Export/XboxVideoMode.txt b/Runtime/Export/XboxVideoMode.txt
new file mode 100644
index 0000000..1314041
--- /dev/null
+++ b/Runtime/Export/XboxVideoMode.txt
@@ -0,0 +1,85 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Utilities/Utility.h"
+
+#if UNITY_XENON
+#include "PlatformDependent/Xbox360/Source/Services/VideoMode.h"
+#endif
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using UnityEngine;
+
+namespace UnityEngine
+{
+ // Xbox Video mode
+ CONDITIONAL UNITY_XENON_API
+ CLASS X360VideoMode
+
+ CSRAW
+ internal X360VideoMode() {}
+
+ CUSTOM public static UInt32 GetDisplayHeight()
+ {
+ #if UNITY_XENON
+ return xenon::VideoMode::GetDisplayHeight();
+ #else
+ return 720;
+ #endif
+ }
+
+ CUSTOM public static UInt32 GetDisplayWidth()
+ {
+ #if UNITY_XENON
+ return xenon::VideoMode::GetDisplayWidth();
+ #else
+ return 1280;
+ #endif
+ }
+
+ CUSTOM public static bool IsWideScreen()
+ {
+ #if UNITY_XENON
+ return xenon::VideoMode::IsWideScreen();
+ #else
+ return true;
+ #endif
+ }
+
+ CUSTOM public static bool IsInterlaced()
+ {
+ #if UNITY_XENON
+ return xenon::VideoMode::IsInterlaced();
+ #else
+ return false;
+ #endif
+ }
+
+ CUSTOM public static bool IsHiDef()
+ {
+ #if UNITY_XENON
+ return xenon::VideoMode::IsHiDef();
+ #else
+ return true;
+ #endif
+ }
+
+ CUSTOM public static float GetRefreshRate()
+ {
+ #if UNITY_XENON
+ return xenon::VideoMode::GetRefreshRate();
+ #else
+ return 60.0f;
+ #endif
+ }
+
+ END
+
+CSRAW
+} // namespace
diff --git a/Runtime/Export/common_include b/Runtime/Export/common_include
new file mode 100644
index 0000000..ef5de8b
--- /dev/null
+++ b/Runtime/Export/common_include
@@ -0,0 +1,617 @@
+TYPEMAP
+
+Face=>Face
+MeshData=>ScriptingObjectPtr
+SelectMenuItemFunction=>ScriptingObjectPtr
+MaterialProperty=>ScriptingObjectPtr
+Vector3=>Vector3f
+Vector2=>Vector2f
+Bounds=>AABB
+Quaternion=>Quaternionf
+Matrix4x4=>Matrix4x4f
+Rect=>Rectf
+Color=>ColorRGBAf
+Color32=>ColorRGBA32
+Vector4=>Vector4f
+Particle=>SimpleParticle
+MaterialPropertyBlock=>ScriptingObjectWithIntPtrField<MaterialPropertyBlock>
+ComputeBuffer=>ScriptingObjectWithIntPtrField<ComputeBuffer>
+ComputeBufferType=>UInt32
+object=>ScriptingObjectPtr
+Type=>ScriptingObjectPtr
+Transform=>ReadOnlyScriptingObjectOfType<Transform>
+EventType=>int
+KeyCode=>int
+EventModifiers=>int
+bool=>ScriptingBool
+uint=>UInt32
+long=>SInt64
+ulong=>UInt64
+ushort=>UInt16
+byte=>UInt8
+Byte=>UInt8
+sbyte=>SInt8
+char=>UInt16
+Char=>UInt16
+Int16=>short
+Int32=>int
+Int64=>SInt64
+Exception=>ScriptingExceptionPtr
+RenderBuffer=>ScriptingRenderBuffer
+string=>ICallString
+IDictionary=>ScriptingObjectPtr
+TimeZoneInfo=>ScriptingObjectPtr
+RaycastHit=>RaycastHit
+NavMeshPath=>ScriptingObjectPtr
+WheelHit=>MonoWheelHit
+Coroutine=>ScriptingObjectWithIntPtrField<Coroutine>
+LineHandle=>LineRenderer::LineHandle
+Keyframe=>KeyframeTpl<float>
+WWW=>ScriptingObjectWithIntPtrField<WWW>
+WebView=>ScriptingObjectPtr
+WebScriptObject=>ScriptingObjectPtr
+Hashtable=>ScriptingObjectPtr
+IntPtr=>void*
+NetworkPlayer=>int
+ContainerWindow=>ScriptingObjectPtr
+GUIView=>ScriptingObjectPtr
+SerializedObject=>ScriptingObjectPtr
+SerializedProperty=>ScriptingObjectPtr
+ActiveEditorTracker=>ScriptingObjectPtr
+GameObjectAndHierarchyChangeTracker=>ScriptingObjectPtr
+EditorBuildSettingsScene=>ScriptingObjectPtr
+HierarchyProperty=>ScriptingObjectPtr
+ProfilerProperty=>ScriptingObjectPtr
+ProfilerFrameDataIterator=>ScriptingObjectPtr
+VisibilityNode[]=>ScriptingArrayPtr
+SceneNode[]=>ScriptingArrayPtr
+uint[]=>ScriptingArrayPtr
+AudioSpeakerMode=>FMOD_SPEAKERMODE
+AudioType=>FMOD_SOUND_TYPE
+AudioFormat=>int
+FindAllIterator=>ScriptingObjectPtr
+FFTWindow=>FMOD_DSP_FFT_WINDOW
+AudioReverbPreset=>int
+AudioRolloffMode=>RolloffMode
+CollisionDetectionMode=>int
+MeshTopology=>GfxPrimitiveType
+RequestProgressCallback=>ScriptingObjectPtr
+RequestDoneCallback=>ScriptingObjectPtr
+AsyncHTTPClient=>ScriptingObjectPtr
+DownloadDoneCallback=>ScriptingObjectPtr
+
+ProfilerMode=>int
+BuildAssetDependencyMode=>int
+ShaderPropertyType=>int
+ShaderPropertyTexDim=>int
+TextClipping=>int
+DragAndDropVisualMode=>int
+WrapMode=>int
+BuildTarget=>BuildTargetPlatform
+BuildTargetGroup=>BuildTargetPlatformGroup
+NetworkDiffType=>int
+MetaFlags=>int
+SerializedPropertyType=>int
+NetworkPeerType=>int
+RPCMode=>int
+NetworkRPCReliability=>int
+NetworkStateReliability=>int
+NetworkReliability=>int
+NetworkStateSynchronization=>int
+NetworkPriority=>int
+CubemapFace=>int
+ImagePosition=>int
+TextAlignment=>int
+TextAnchor=>int
+QueueMode=>int
+SendMessageOptions=>int
+CameraClearFlags=>int
+DownloadResolution=>int
+NameConflictResolution=>int
+Changeset=>MonoChangeset
+AssetsItem=>MonoAssetsItem
+DeletedAsset=>MonoDeletedAsset
+LightShadows=>int
+ForceMode=>int
+TexGenMode=>int
+CameraRenderMode=>int
+AnisotropicFiltering=>int
+BlendWeights=>int
+PhysicMaterialCombine=>int
+DynamicsMaterialCombine=>int
+AnimationPlayMode=>int
+AnimationCullingType=>Animation::CullingType
+AnimatorCullingMode=>Animator::CullingMode
+PlayMode=>int
+RuntimePlatform=>int
+CollisionFlags=>int
+PlaybackMode=>int
+LightRenderMode=>int
+LightAttenuationType=>int
+AnimationBlendMode=>int
+PrimitiveType=>int
+TextureWrapMode=>int
+NPOTSupport=>int
+NPOTSupport=>int
+EditorCurveValueType=>UnityEngine::Animation::EditorCurveValueType
+QualityLevel=>int
+CapStyle=>int
+HideFlags=>int
+AudioVelocityUpdateMode=>int
+FilterMode=>int
+ImportAssetOptions=>int
+SelectionMode=>int
+SkinQuality=>int
+JointProjectionMode=>int
+ConfigurableJointMotion=>int
+RotationDriveMode=>int
+NetworkConnectionError=>int
+NetworkLogLevel=>int
+ConnectionTesterStatus=>int
+ModelImporterAnimationCompression=>int
+NETVersion=>int
+ModelImporterMeshCompression=>int
+AssetStatus=>int
+StaticOcclusionCullingQuality=>int
+StaticOcclusionCulling.Quality=>int
+ModelImporterGenerateMaterials=>int
+ModelImporterClipAnimation=>ScriptingObjectPtr
+ModelImporterMaterialName=>ModelImporter::MaterialName
+ModelImporterMaterialSearch=>ModelImporter::MaterialSearch
+ModelImporterGenerateAnimations=>ModelImporter::LegacyGenerateAnimations
+ModelImporterTangentSpaceMode=>int
+TextureImporter=>ReadOnlyScriptingObjectOfType<TextureImporter>
+TextureImporterSettings=>ScriptingObjectPtr
+TextureImportInstructions=>ScriptingObjectPtr
+TextureImporterFormat=>int
+TextureImporterMipFilter=>int
+TextureImporterGenerateCubemap=>int
+TextureImporterNPOTScale=>int
+TextureImporterNormalFilter=>int
+TextureImporterType=>int
+LogEntry=>ScriptingObjectPtr
+AudioImporterFormat=>int
+AudioImporterChannels=>int
+FontTextureCase=>int
+System.Type=>ScriptingObjectPtr
+System.Array=>ScriptingArrayPtr
+DepthTextureMode=>int
+LightmapBakeQuality=>int
+LightmappingMode=>int
+LightProbeInterpMode=>int
+LightmapsMode=>int
+LightmapFormat=>TextureUsageMode
+DeviceOrientation=>unsigned
+RigidbodyConstraints=>int
+CharacterInfo=>ScriptingCharacterInfo
+AudioClip=>ReadOnlyScriptingObjectOfType<AudioClip>
+TreeData=>ReadOnlyScriptingObjectOfType<MonoBehaviour>
+Material=>ReadOnlyScriptingObjectOfType<Material>
+LodMesh=>ReadOnlyScriptingObjectOfType<Mesh>
+LodMeshFilter=>ReadOnlyScriptingObjectOfType<MeshFilter>
+LODGroup=>ReadOnlyScriptingObjectOfType<LODGroup>
+Mesh=>ReadOnlyScriptingObjectOfType<Mesh>
+SkinnedMeshRenderer=>ReadOnlyScriptingObjectOfType<SkinnedMeshRenderer>
+MeshFilter=>ReadOnlyScriptingObjectOfType<MeshFilter>
+MeshRenderer=>ReadOnlyScriptingObjectOfType<MeshRenderer>
+SkinnedMeshRenderer=>ReadOnlyScriptingObjectOfType<SkinnedMeshRenderer>
+Texture=>ReadOnlyScriptingObjectOfType<Texture>
+MovieTexture=>ReadOnlyScriptingObjectOfType<MovieTexture>
+FileTexture=>ReadOnlyScriptingObjectOfType<Texture2D>
+Texture2D=>ReadOnlyScriptingObjectOfType<Texture2D>
+Cubemap=>ReadOnlyScriptingObjectOfType<Cubemap>
+Texture3D=>ReadOnlyScriptingObjectOfType<Texture3D>
+HTMLTexture=>ReadOnlyScriptingObjectOfType<HTMLTexture>
+TextAsset=>ReadOnlyScriptingObjectOfType<TextAsset>
+MonoScript=>ReadOnlyScriptingObjectOfType<MonoScript>
+Shader=>ReadOnlyScriptingObjectOfType<Shader>
+PhysicMaterial=>ReadOnlyScriptingObjectOfType<PhysicMaterial>
+PhysicsMaterial2D=>ReadOnlyScriptingObjectOfType<PhysicsMaterial2D>
+Font=>ReadOnlyScriptingObjectOfType<Font>
+GUITexture=>ReadOnlyScriptingObjectOfType<GUITexture>
+Heightmap=>ReadOnlyScriptingObjectOfType<Heightmap>
+ModelImporterAnimation=>ScriptingObjectPtr
+ApplyToCacheArguments=>MonoApplyToCacheArguments
+InternalEmitParticleArguments=>MonoInternalEmitParticleArguments
+WebCamDevice=>MonoWebCamDevice
+WebCamFlags=>WebCamFlags
+Sprite=>ReadOnlyScriptingObjectOfType<Sprite>
+SpriteSheetMetaData=>ScriptingObjectPtr
+SpriteImportMode=>TextureImporter::SpriteMode
+AtlasSettings=>SpritePacker::SpriteAtlasSettings
+Packer.Execution=>SpritePacker::SpritePackerExecution
+PackerJob=>void*
+InternalDrawTextureArguments=>MonoInternalDrawTextureArguments
+Internal_DrawMeshTRArguments=>MonoInternal_DrawMeshTRArguments
+Internal_DrawArguments=>MonoInternal_DrawArguments
+Internal_DrawMeshMatrixArguments=>MonoInternal_DrawMeshMatrixArguments
+Internal_DrawWithTextSelectionArguments=>MonoInternal_DrawWithTextSelectionArguments
+RenderArguments=>MonoRenderArguments
+iPhoneKeyboard_InternalConstructorHelperArguments=>MonoiPhoneKeyboard_InternalConstructorHelperArguments
+TouchScreenKeyboard_InternalConstructorHelperArguments=>MonoTouchScreenKeyboard_InternalConstructorHelperArguments
+ParticleSystem.Particle=>ParticleSystemParticle
+ParticleSystem.CollisionEvent=>MonoParticleCollisionEvent
+TerrainConstructionParameters=>MonoTerrainConstructionParameters
+UnityEngine.Object=>ReadOnlyScriptingObjectOfType<Object>
+Object=>ReadOnlyScriptingObjectOfType<Object>
+GameObject=>ReadOnlyScriptingObjectOfType<GameObject>
+Component=>ReadOnlyScriptingObjectOfType<Unity::Component>
+
+[Writable]Material=>ScriptingObjectOfType<Material>
+[Writable]GameObject=>ScriptingObjectOfType<GameObject>
+[Writable]PhysicMaterial=>ScriptingObjectOfType<PhysicMaterial>
+[Writable]PhysicsMaterial2D=>ScriptingObjectOfType<PhysicsMaterial2D>
+[Writable]AnimationClip=>ScriptingObjectOfType<AnimationClip>
+[Writable]Object=>ScriptingObjectOfType<Object>
+[Writable]ScriptableObject=>ScriptingObjectOfType<MonoBehaviour>
+[Writable]RenderTexture=>ScriptingObjectOfType<RenderTexture>
+[Writable]Texture2D=>ScriptingObjectOfType<Texture2D>
+[Writable]Texture3D=>ScriptingObjectOfType<Texture3D>
+[Writable]Cubemap=>ScriptingObjectOfType<Cubemap>
+[Writable]Mesh=>ScriptingObjectOfType<Mesh>
+[Writable]TerrainData=>ScriptingObjectOfType<TerrainData>
+[Writable]WebCamTexture=>ScriptingObjectOfType<WebCamTexture>
+[Writable]Font=>ScriptingObjectOfType<Font>
+
+WebCamTexture=>ReadOnlyScriptingObjectOfType<WebCamTexture>
+TerrainData=>ReadOnlyScriptingObjectOfType<TerrainData>
+AssetBundle=>ReadOnlyScriptingObjectOfType<AssetBundle>
+Editor=>ReadOnlyScriptingObjectOfType<MonoBehaviour>
+EditorWindow=>ReadOnlyScriptingObjectOfType<MonoBehaviour>
+Rigidbody=>ReadOnlyScriptingObjectOfType<Rigidbody>
+PrimitiveCollider=>ReadOnlyScriptingObjectOfType<PrimitiveCollider>
+Collider=>ScriptingObjectOfType<Collider>
+BoxCollider=>ReadOnlyScriptingObjectOfType<BoxCollider>
+SphereCollider=>ReadOnlyScriptingObjectOfType<SphereCollider>
+CapsuleCollider=>ReadOnlyScriptingObjectOfType<CapsuleCollider>
+MeshCollider=>ReadOnlyScriptingObjectOfType<MeshCollider>
+RaycastCollider=>ReadOnlyScriptingObjectOfType<RaycastCollider>
+WheelCollider=>ReadOnlyScriptingObjectOfType<WheelCollider>
+Rigidbody2D=>ReadOnlyScriptingObjectOfType<Rigidbody2D>
+Collider2D=>ScriptingObjectOfType<Collider2D>
+CircleCollider2D=>ReadOnlyScriptingObjectOfType<CircleCollider2D>
+PolygonCollider2D=>ReadOnlyScriptingObjectOfType<PolygonCollider2D>
+SpriteCollider2D=>ReadOnlyScriptingObjectOfType<SpriteCollider2D>
+BoxCollider2D=>ReadOnlyScriptingObjectOfType<BoxCollider2D>
+EdgeCollider2D=>ReadOnlyScriptingObjectOfType<EdgeCollider2D>
+RaycastHit2D=>RaycastHit2D
+ShaderLayer=>ReadOnlyScriptingObjectOfType<ShaderLayer>
+ConstantForce=>ReadOnlyScriptingObjectOfType<ConstantForce>
+RenderTexture=>ReadOnlyScriptingObjectOfType<RenderTexture>
+Camera=>ReadOnlyScriptingObjectOfType<Camera>
+Light=>ReadOnlyScriptingObjectOfType<Light>
+LightProbes=>ReadOnlyScriptingObjectOfType<LightProbes>
+TextMesh=>ReadOnlyScriptingObjectOfType<TextMesh>
+GUIText=>ReadOnlyScriptingObjectOfType<GUIText>
+GUIElement=>ReadOnlyScriptingObjectOfType<GUIElement>
+Renderer=>ReadOnlyScriptingObjectOfType<Renderer>
+OffMeshLink=>ReadOnlyScriptingObjectOfType<OffMeshLink>
+NavMesh=>ReadOnlyScriptingObjectOfType<NavMesh>
+AudioSource=>ReadOnlyScriptingObjectOfType<AudioSource>
+AudioLowPassFilter=>ReadOnlyScriptingObjectOfType<AudioLowPassFilter>
+AssetImporter=>ReadOnlyScriptingObjectOfType<AssetImporter>
+HingeJoint=>ReadOnlyScriptingObjectOfType<HingeJoint>
+ParticleEmitter=>ReadOnlyScriptingObjectOfType<ParticleEmitter>
+EulerRotationMotor=>ReadOnlyScriptingObjectOfType<EulerRotationMotor>
+ScriptableObject=>ReadOnlyScriptingObjectOfType<MonoBehaviour>
+MonoBehaviour=>ReadOnlyScriptingObjectOfType<MonoBehaviour>
+CharacterController=>ReadOnlyScriptingObjectOfType<CharacterController>
+OpenGLView=>ReadOnlyScriptingObjectOfType<Object>
+NetworkView=>ReadOnlyScriptingObjectOfType<NetworkView>
+
+Rigidbody2D=>ReadOnlyScriptingObjectOfType<Rigidbody2D>
+
+ScriptableWizard=>ScriptingObjectPtr
+OptimizedGUIBlock=>ScriptingObjectPtr
+GUIStyle=>ScriptingObjectWithIntPtrField<GUIStyle>
+GUISkin=>ScriptingObjectPtr
+GUIStyleState=>ScriptingObjectWithIntPtrField<GUIStyleState>
+WindowFunction=>ScriptingObjectPtr
+RectOffset=>ScriptingObjectWithIntPtrField<RectOffset>
+BitStream=>ScriptingObjectPtr
+DetailRenderer=>ScriptingObjectPtr
+Terrain=>ReadOnlyScriptingObjectOfType<MonoBehaviour>
+GUIContent=>ScriptingObjectPtr
+NetworkViewID=>NetworkViewID
+Ping=>ScriptingObjectPtr
+TreeDatabase=>ScriptingObjectPtr
+TreeDatabasePrototype=>ScriptingObjectPtr
+TreeRenderer=>ScriptingObjectPtr
+ResolutionDialogSetting=>int
+MacFullscreenMode=>int
+FontStyle=>int
+FontRenderingMode=>int
+UserAuthorization=>int
+IMECompositionMode=>int
+ModelImporterAnimationType=>int
+PropertyModification=>ScriptingObjectPtr
+
+ParticleSystem=>ReadOnlyScriptingObjectOfType<ParticleSystem>
+ParticleSystemRenderer=>ReadOnlyScriptingObjectOfType<ParticleSystemRenderer>
+
+Gradient=>ScriptingObjectWithIntPtrField<GradientNEW>
+
+AnimationState=>ScriptingObjectWithIntPtrField<AnimationState>
+Animation=>ReadOnlyScriptingObjectOfType<Animation>
+AnimationClip=>ReadOnlyScriptingObjectOfType<AnimationClip>
+AudioImporter=>ReadOnlyScriptingObjectOfType<AudioImporter>
+MovieImporter=>ReadOnlyScriptingObjectOfType<MovieImporter>
+Resolution=>ScreenManager::Resolution
+AnimationCurve=>ScriptingObjectWithIntPtrField<AnimationCurve>
+LightmapData=>ScriptingObjectPtr
+EditorCurveBinding=>MonoEditorCurveBinding
+
+IEnumerator=>ScriptingObjectPtr
+AnimationClipCurveData=>ScriptingObjectPtr
+AnimationClipObjectReferenceCurveData=>ScriptingObjectPtr
+
+SupportedLanguage=>MonoSupportedLanguage
+
+JointMotor=>MonoJointMotor
+TerrainChangedFlags=>TerrainData::ChangedFlags
+
+WiiRemoteData=>WiiRemoteDataCpp
+WiiClassicData=>WiiClassicDataCpp
+WiiNunchukData=>WiiNunchukDataCpp
+WiiBalanceBoardData=>WiiBalanceBoardDataCpp
+WiiMotionPlusDirData=>WiiMotionPlusDirDataCpp
+WiiMotionPlusData=>WiiMotionPlusDataCpp
+WiiBalanceCommand=>int
+WiiBalanceError=>int
+WiiBalanceTGCError=>int
+WiiMotionPlusStatus=>int
+WiiMotionPlusZeroDriftMode=>int
+WiiPadInfo=>wii::WPadStatusInfo
+WiiButton=>int
+WiiSensorBarPosition=>int
+WiiPlayMode=>SInt32
+WiiDevType=>UInt32
+WiiErrorStatus=>UInt32
+WiiInput.Status=>SInt32
+
+WiiHomeMenu=>ScriptingObjectPtr
+
+WiiNand.NodeType=>UInt8
+WiiNand.Error=>int
+WiiNand.CheckResult=>int
+WiiNand.Access=>int
+WiiNand.SeekMode=>int
+
+NVFile=>ScriptingObjectPtr
+NVDirectory=>ScriptingObjectPtr
+NVSystem=>ScriptingObjectPtr
+NVBase=>ScriptingObjectPtr
+NVStatus=>wii::nvmem::NodeStatus
+NVBanner=>ScriptingObjectPtr
+AlignedBuffer=>ScriptingObjectPtr
+AlignedBuffer.Memory=>int
+
+AspectRatio=>int
+ProgressiveMode=>int
+EuRgb60=>int
+SoundMode=>int
+TvFormat=>int
+ScanMode=>int
+Language=>int
+WiiConsoleSettings.Language=>int
+
+WiiDrive.State=>SInt32
+WiiDVDFile=>ScriptingObjectPtr
+WiiFile=>ScriptingObjectPtr
+WiiOS.Setting=>SInt32
+WiiVI.TimeToDim=>SInt32
+WiiVI.TvFormat=>SInt32
+WiiVI.ScanMode=>SInt32
+
+WiiHomeMenu.Select=>SInt32
+WiiHomeMenu.NotSavedWarning=>SInt32
+WiiKeyboard.Result=>int
+WiiKeyboard.State=>int
+
+WiiRFL.ErrorCode=>int
+WiiRFL.Source=>SInt32
+WiiRFL.Sex=>SInt32
+WiiRFL.Age=>SInt32
+WiiRFL.Race=>SInt32
+WiiRFL.Expression=>SInt32
+WiiRFLAdditionalInfo=>WiiRFLAdditionalInfoCpp
+WiiRFLId=>WiiRFLIdCpp
+WiiRFLDB=>ScriptingObjectPtr
+WiiRFLDB.Type=>int
+WiiUtils.LoadingScreen.Placement=>int
+
+PrefabType=>int
+
+iPhoneTouch=>Touch
+Compass=>ScriptingObjectPtr
+CompassInfo=>HeadingInfo
+LocationInfo=>LocationInfo
+LocationServiceStatus=>LocationServiceStatus
+iPhoneAccelerationEvent=>Acceleration
+AccelerationEvent=>Acceleration
+iPhoneOrientation=>unsigned
+iPhoneMovieControlMode=>unsigned
+iPhoneMovieScalingMode=>unsigned
+FullScreenMovieControlMode=>unsigned
+FullScreenMovieScalingMode=>unsigned
+iPhoneKeyboardType=>unsigned
+iPhoneKeyboard=>ScriptingObjectPtr
+TouchScreenKeyboard=>ScriptingObjectWithIntPtrField<KeyboardOnScreen>
+iPhoneBuildSettings=>iphone::iPhoneBuildSettings
+WiiBuildSettings=>wii::WiiBuildSettings
+iPhoneScreenOrientation=>ScreenOrientation
+ScreenOrientation=>ScreenOrientation
+SleepTimeout=>SleepTimeout
+DeviceType=>DeviceType
+NetworkReachability=>NetworkReachability
+iPhoneNetworkReachability=>NetworkReachability
+iPhoneGeneration=>iphone::iPhoneGeneration
+GcLeaderboard=>ScriptingObjectPtr
+StrippingLevel=>int
+ScriptCallOptimizationLevel=>int
+iOSSdkVersion=>int
+iOSTargetOSVersion=>int
+iOSTargetDevice=>int
+iOSTargetPlatform=>int
+iOSTargetResolution=>int
+UIOrientation=>int
+iOSStatusBarStyle=>int
+iOSShowActivityIndicatorOnLoading=>int
+iOSActivityIndicatorStyle=>int
+AndroidTargetDevice=>int
+TargetGlesGraphics=>int
+AndroidSplashScreenScale=>int
+ApiCompatibilityLevel=>int
+AndroidSdkVersions=>int
+AndroidPreferredInstallLocation=>int
+AndroidShowActivityIndicatorOnLoading=>int
+AndroidActivityIndicatorStyle=>int
+WiiRegion=>int
+WiiHio2Usage=>int
+BuildOptions=>BuildPlayerOptions
+Attributes=>int
+MouseCursor=>int
+
+IDList=>ScriptingObjectPtr
+Event=>ScriptingObjectWithIntPtrField<InputEvent>
+AnimationEvent=>ScriptingObjectWithIntPtrField<AnimationEvent>
+UndoSnapshot=>ScriptingObjectPtr
+AsyncOperation=>ScriptingObjectWithIntPtrField<AsyncOperation>
+AssetBundleRequest=>ScriptingObjectPtr
+AssetBundleCreateRequest=>ScriptingObjectPtr
+IDList=>ScriptingObjectPtr
+GUIState=>ScriptingObjectPtr
+Speed=>int
+ShaderModel=>int
+ASRefreshState=>int
+SerializationMode=>int
+EditorBehaviorMode=>int
+SpritePackerMode=>int
+ExportPackageOptions=>int
+
+ADSizeIdentifier=>unsigned
+ADPosition=>unsigned
+ADErrorCode=>unsigned
+ADError=>ScriptingObjectPtr
+ADBannerView=>ScriptingObjectPtr
+ADInterstitialAd=>ScriptingObjectPtr
+LocalNotification=>ScriptingObjectPtr
+RemoteNotification=>ScriptingObjectPtr
+RemoteNotificationType=>int
+CalendarUnit=>unsigned
+CalendarIdentifier=>CalendarIdentifier
+
+AssetIndex.Item=>ScriptingObjectPtr
+WindZoneData=>ReadOnlyScriptingObjectOfType<WindZone>
+WindZoneMode=>WindZone::WindZoneMode
+
+X360PlayerId=>ScriptingObjectPtr
+X360SessionId=>ScriptingObjectPtr
+X360PlayerPrivilegeLevel=>UInt32
+X360PlayerPrivilegeType=>UInt32
+X360Achievement=>ScriptingObjectPtr
+X360AchievementType=>UInt32
+X360UserStorageStatus=>UInt32
+X360SaveGame=>ScriptingObjectPtr
+X360StatsRow=>xenon::StatsRow
+X360StatsColumn=>xenon::StatsColumn
+X360UserProperty=>XUSER_PROPERTY
+X360Avatar=>ScriptingObjectPtr
+X360AvatarBatchShaderType=>UInt32
+X360AvatarModel=>ScriptingObjectPtr
+X360AvatarShaderParam=>XAVATAR_SHADER_PARAM
+X360AvatarBodyType=>UInt32
+X360AvatarAnimation=>ScriptingObjectPtr
+X360AvatarAnimCursor=>ScriptingObjectPtr
+X360AvatarPose=>ScriptingObjectPtr
+X360AvatarLoadStatus=>UInt32
+X360AvatarAnimPlayMode=>UInt32
+X360AvatarStockAnim=>UInt32
+X360AvatarMapper=>ScriptingObjectPtr
+X360Session=>ScriptingObjectPtr
+X360GameRegion=>UInt32
+
+KinectSkeletonTrackingState=>UInt32
+KinectSkeletonPosition=>UInt32
+KinectSkeletonPositionTrackingState=>UInt32
+KinectAutoIdentityPlayerState=>UInt32
+KinectHandSpecificData=>ATG::HandSpecificData
+KinectHand=>UInt32
+KinectCameraType=>UInt32
+KinectCameraProperty=>UInt32
+KinectCameraPropertyF=>UInt32
+KinectCameraPropertyValue=>int
+KinectImageStreamType=>UInt32
+KinectImageStreamResolution=>UInt32
+KinectImageStream=>ScriptingObjectPtr
+KinectImageViewArea=>NUI_IMAGE_VIEW_AREA
+KinectImageDigitalZoom=>UInt32
+KinectTiltObject=>xenon::TiltObject
+KinectTiltObjects=>xenon::TiltObjects
+KinectWavePlayerState=>NUI_WAVE_PLAYER_STATE
+KinectSkeletonData=>xenon::SkeletonData
+KinectSyncMode=>UInt32
+KinectAudioType=>UInt32
+KinectHandlesTuningParamsGlobal=>NUI_HANDLES_TUNING_PARAMS_GLOBAL
+KinectSpeechLanguage=>UInt32
+KinectSpeechEvent=>UInt32
+KinectGesturePreprocess=>UInt32
+KinectGender=>UInt32
+KinectHeadOrientationData=>xenon::HeadOrientationData
+
+MetroApplicationShowName=>int
+MetroApplicationForegroundText=>int
+MetroCompilationOverrides=>int
+
+//SubstanceInput=>ReadOnlyScriptingObjectOfType<SubstanceInputMono>
+SubstanceArchive=>ReadOnlyScriptingObjectOfType<SubstanceArchive>
+ProceduralMaterial=>ReadOnlyScriptingObjectOfType<ProceduralMaterial>
+ProceduralTexture=>ReadOnlyScriptingObjectOfType<ProceduralTexture>
+SubstanceTextureGenerationType=>ProceduralMaterial::TextureGenerationType
+SubstanceDiffuseAlphaCombinationType=>ProceduralMaterial::DiffuseAlphaCombinationType
+
+BoneWeight=>BoneInfluence
+PS3ReturnCode=>int
+AndroidJavaRunnable=>MonoObject*
+AndroidJavaProxy=>MonoObject*
+Asset=>ScriptingObjectPtr
+Task=>ScriptingObjectPtr
+ChangeSet=>ScriptingObjectPtr
+Message=>ScriptingObjectPtr
+Plugin=>ScriptingObjectPtr
+ConfigField=>ScriptingObjectPtr
+CustomCommand=>ScriptingObjectPtr
+CommandContext=>int
+
+TransparencySortMode=>Camera::SortMode
+
+AppCallbackItem=>ScriptingObjectPtr
+
+AnimationClipSettings=>MonoObject*
+MuscleClipQualityInfo=>MonoObject*
+SkeletonBone=>MonoSkeletonBone
+SkeletonBoneLimit=>MonoHumanLimit
+HumanBone=>MonoHumanBone
+HumanDescription=>MonoHumanDescription
+State=>ReadOnlyScriptingObjectOfType<State>
+Transition=>ReadOnlyScriptingObjectOfType<Transition>
+StateMachine=>ReadOnlyScriptingObjectOfType<StateMachine>
+Graph=>ReadOnlyScriptingObjectOfType<Graph>
+MuscleClip=>ReadOnlyScriptingObjectOfType<MuscleClip>
+BlendTree=>ReadOnlyScriptingObjectOfType<BlendTree>
+AvatarMask=>ReadOnlyScriptingObjectOfType<AvatarMask>
+Animator=>ReadOnlyScriptingObjectOfType<Animator>
+RuntimeAnimatorController=>ReadOnlyScriptingObjectOfType<RuntimeAnimatorController>
+AnimatorController=>ReadOnlyScriptingObjectOfType<AnimatorController>
+HumanTemplate=>ReadOnlyScriptingObjectOfType<HumanTemplate>
+Avatar=>ReadOnlyScriptingObjectOfType<Avatar>
+[Writable]AnimatorOverrideController=>ScriptingObjectOfType<AnimatorOverrideController>
+GraphRegistry=>ReadOnlyScriptingObjectOfType<GraphRegistry>
+GraphModel=>MonoGraphModel
+GraphNode=>ReadOnlyScriptingObjectOfType<GraphNode>
+GraphSlot=>ReadOnlyScriptingObjectOfType<GraphSlot>
+AvatarTarget=>int
+AvatarIKGoal=>int
+Motion=>ReadOnlyScriptingObjectOfType<Motion>
+IconDrawStyle=>int
+
diff --git a/Runtime/Export/common_structs b/Runtime/Export/common_structs
new file mode 100644
index 0000000..e39b534
--- /dev/null
+++ b/Runtime/Export/common_structs
@@ -0,0 +1,29 @@
+Vector3
+Vector2
+Quaternion
+Matrix4x4
+Vector4
+Rect
+Color
+Bounds
+Keyframe
+Ray
+Ray2D
+Plane
+NetworkViewID
+JointMotor
+JointSpring
+JointLimits
+SoftJointLimit
+JointDrive
+WheelFrictionCurve
+WheelHit
+RaycastHit
+ContactPoint
+TreeInstance
+WiiNunchuk
+WiiClassic
+WiiPadInfo
+WiiRemote
+NVStatus
+MatchTargetWeightMask
diff --git a/Runtime/Export/docs.css b/Runtime/Export/docs.css
new file mode 100644
index 0000000..087a8e8
--- /dev/null
+++ b/Runtime/Export/docs.css
@@ -0,0 +1,461 @@
+body {
+ background-color: white;
+ margin: 10px;
+}
+body, p, ul, ol, td, li {
+ font-family: Helvetica, Arial, sans-serif;
+ font-size: 12px;
+}
+body, p { color: #352f28; line-height: 17px;}
+
+table {
+ border: 0px;
+ border-collapse: collapse;
+ font-size: 13px;
+}
+td, th {
+ vertical-align: top;
+ padding: 0px;
+}
+em {
+ color:#111;
+}
+h1 {
+ clear:both;
+ font-size:18px;
+ font-weight:bold;
+ color:black;
+ margin: .7em 0px 0px 0px;
+ line-height: 25px;
+}
+
+h2 {
+ font-size:14px;
+ font-weight:bold;
+ color:black;
+ margin: 1em 0px 0px 0px;
+}
+
+h3 {
+ font-size:12px;
+ color:#171411;
+ font-weight:bold;
+ margin: 1em 0px 0px 0px;
+}
+h4{ margin: 1em 0px 0px 0px;}
+
+p {
+ margin:0px 0px 1.5em 0px;
+}
+
+.doc-prop, .doc-keyword, .doc-menu {
+ font-weight:bold;
+}
+
+a {
+ color: #145d7b;
+ text-decoration:none;
+}
+
+a:hover {
+ text-decoration:underline;
+}
+
+ul, li { margin-left:0px; }
+a img {
+ border: 0px;
+}
+
+/* Property tables */
+.prop { width:20%; visibility:hidden; height:1px;}
+.function { width:80%; visibility:hidden;height:1px;}
+.tableheader { visibility:hidden; }
+
+/* Previous & Next buttons */
+/* Previous + Next style navigation */
+
+.nav {
+ clear: both;
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+
+.nav .nav-prev,
+.nav .nav-next {
+}
+
+.nav .nav-prev .nav-left {
+ float: left;
+ background: url(../images/navigation/prev_left.png) top;
+ height: 22px;
+ width: 20px;
+}
+.nav .nav-prev .nav-right {
+ float: left;
+ background: url(../images/navigation/prev_right.png) top;
+ height: 22px;
+ width: 11px;
+}
+.nav .nav-next .nav-left {
+ float: right;
+ background: url(../images/navigation/next_right.png) top;
+ height: 22px;
+ width: 20px;
+}
+.nav .nav-next .nav-right {
+ float: right;
+ background: url(../images/navigation/next_left.png) top;
+ height: 22px;
+ width: 11px;
+}
+.nav .nav-prev .nav-main,
+.nav .nav-next .nav-main {
+ height: 22px;
+ background: url(../images/navigation/nav_back.png) top repeat-x;
+ white-space: nowrap;
+
+ padding-left: 10px;
+ padding-right: 10px;
+ padding-top: 2px;
+
+ color: #333;
+}
+.nav .nav-prev .nav-main {
+ float: left;
+ text-align: left;
+}
+.nav .nav-next .nav-main {
+ float: right;
+ text-align: right;
+}
+.nav a {
+ text-decoration: none;
+}
+.nav a:hover {
+ text-decoration: underline;
+}
+
+
+/* Styles for the title bar */
+.titlebar {
+ width:100%;
+ background: url(../images/top/stretch.png) top;
+ font-weight:bold;
+ color:#a4abae;
+ vertical-align:middle;
+}
+.titleleft {
+ vertical-align: top;
+ width:80px;
+ height:64px;
+}
+.titleright {
+ background: url(../images/top/right.png);
+ width:9px;
+ height:64px;
+}
+
+.titlemid {
+ background: url(../images/top/stretch.png) top;
+ height:64px;
+ width:100%;
+}
+
+.titlemid .doctitle {
+ height:30px;
+ font-size: 15px;
+ padding-top:3px;
+ padding-left:9px;
+ vertical-align:middle;
+ width:100%;
+ color:#c7c7c7;
+ white-space: nowrap;
+}
+
+.titlemid .docpath {
+ white-space: nowrap;
+ color:#a4abae;
+ margin-top:2px;
+ margin-left:9px;
+ width:100%;
+}
+
+.titlemid .switchlink {
+ white-space: nowrap;
+ color:#a4abae;
+ vertical-align:middle;
+ text-align:right;
+}
+
+.titlemid a {
+ color: #a4abae;
+ text-decoration:none;
+}
+
+.titlemid a:hover {
+ text-decoration:underline;
+}
+
+.switchlink {
+ text-align:right;
+ padding-right:16px;
+ padding-left:10px;
+}
+
+.manual {
+ margin-left:74px;
+ margin-right:20px;
+}
+
+.vspace {
+ margin-top:10px;
+}
+
+/* Buttons the containing style (.Components) is the location of the current page
+ I use this to select between on and off images */
+.Components .manual { width:72px; height:36px; background: url(../images/top/manual.png) top right; margin:0px;}
+.Components .reference { width:94px; height:36px; background: url(../images/top/reference_on.png) top right; }
+.Components .scripting { width:82px; height:36px; background: url(../images/top/scripting.png) top right; }
+.Components .manual:hover { background: url(../images/top/manual_hover.png) top right; margin:0px;}
+.Components .reference:hover { background: url(../images/top/reference_onhover.png) top right; }
+.Components .scripting:hover { background: url(../images/top/scripting_hover.png) top right; }
+
+.Manual .manual { width:72px; height:36px; background: url(../images/top/manual_on.png) top right; margin:0px;}
+.Manual .reference { width:94px; height:36px; background: url(../images/top/reference.png) top right; }
+.Manual .scripting { width:82px; height:36px; background: url(../images/top/scripting.png) top right; }
+.Manual .manual:hover { background: url(../images/top/manual_onhover.png) top right; margin:0px;}
+.Manual .reference:hover { background: url(../images/top/reference_hover.png) top right; }
+.Manual .scripting:hover { background: url(../images/top/scripting_hover.png) top right; }
+
+.Scripting .manual { width:72px; height:36px; background: url(../images/top/manual.png) top right; margin:0px;}
+.Scripting .reference { width:94px; height:36px; background: url(../images/top/reference.png) top right; }
+.Scripting .scripting { width:82px; height:36px; background: url(../images/top/scripting_on.png) top right; }
+.Scripting .manual:hover { background: url(../images/top/manual_hover.png) top right; margin:0px;}
+.Scripting .reference:hover { background: url(../images/top/reference_hover.png) top right; }
+.Scripting .scripting:hover { background: url(../images/top/scripting_onhover.png) top right; }
+
+.docs-navigation {display:none;}
+.manual-text {display:none;}
+.components-text {display:none;}
+.scripting-text {display:none;}
+/* scriptref styles
+ ======================================================*/
+
+/* left navigation menu*/
+form.apisearch {
+ padding: 0px;
+ margin: 0px;
+}
+
+ul.left-menu {
+ position: absolute;
+ left: 5px;
+ top: 90px;
+
+ width: 159px;
+ padding: 0px 0px 0px 20px;
+ margin: 0px;
+ line-height: 15px;
+ list-style-type: none;
+}
+.left-menu-heading, .left-menu-softheading, .left-menu-subheading {
+ padding-top: 10px;
+ font-weight: bold;
+ color:black;
+ display: block;
+ overflow: hidden;
+ -o-text-overflow: ellipsis; /* Opera */
+ text-overflow: ellipsis; /* IE, Safari (WebKit) */
+}
+.left-menu-indent {
+ margin-left: 2em; /* this needs to be margin; otherwise .left-menu-current wont work */
+}
+.left-menu a {
+ text-decoration: none;
+ white-space: nowrap;
+}
+
+.left-menu a:hover {
+ text-decoration: underline;
+ color: #145d7b;
+}
+
+.left-menu-current {
+ position: relative;
+ background-color: #e8e8e8;
+ padding: 0px 5px 0px 5px;
+ left: -5px;
+ -moz-border-radius: 4px; -webkit-border-radius: 4px;
+}
+
+.left-menu-item, .left-menu-item a {
+ color: #352f28;
+ display: block;
+ width: 159px;
+ white-space: nowrap;
+ overflow: hidden;
+ -o-text-overflow: ellipsis; /* Opera */
+ text-overflow: ellipsis; /* IE, Safari (WebKit) */
+}
+.left-menu-inherited-item, .left-menu-inherited-item a {
+ color: #352f28;
+ display: block;
+ width: 159px;
+ white-space: nowrap;
+ overflow: hidden;
+ -o-text-overflow: ellipsis; /* Opera */
+ text-overflow: ellipsis; /* IE, Safari (WebKit) */
+}
+
+
+/* class page */
+.scriprefmain {
+ padding-top:0px;
+ padding-left:185px;
+ padding-right:5px;
+}
+.scriprefmain ul {
+ list-style-type: none;
+
+ }
+.heading {
+ clear:both;
+ font-size:18px;
+ font-weight:bold;
+ color:black;
+ margin: .9em 0px 0px 0px;
+ display:block;
+ line-height: 25px;
+}
+
+.text {
+ display:block;
+ margin-bottom: 1.5em;
+}
+
+.script-section-softheading {
+ font-size:12px;
+ font-weight:bold;
+ margin: 1em 0px 0px 0px;
+}
+
+.script-section-hardheading {
+ font-size:14px;
+ font-weight:bold;
+ color:black;
+ margin: 1em 0px 0px 0px;
+}
+
+.class-member-list, .class-member-list-inherited {
+ width: 100%;
+ border-bottom:1px solid #888580;
+ border-top:1px solid #888580;
+ margin-bottom:2em;
+}
+.class-member-list-name {
+ width: 20%;
+ text-align: left;
+}
+.class-member-description {
+ display: inline;
+}
+
+.scriprefmain ul.list {
+ list-style-type: disc;
+ margin-top: 2px;
+ margin-bottom: 2px;
+}
+
+.scriprefmain .editorclass {
+ color: #666;
+ background-color: #ccc;
+ background-color: #f8f8f8;
+ padding: 2px 5px 2px 5px;
+ -moz-border-radius: 4px; -webkit-border-radius: 4px;
+}
+
+
+/* function page */
+.manual-entry h3 { margin-top: 0px; text-indent: -60px; margin-left:60px;}
+.manual-entry h3 .hl-keyword,
+.manual-entry h3 .hl-datatype,
+.manual-entry h3 .classlink
+ {
+ font-weight:normal;
+}
+
+h3.soft { margin-top:1em;}
+table.parameters {
+ margin-top:-12px;
+ width:100%;
+}
+table.parameters thead { visibility:hidden; height:0px; display:block;}
+table.parameters tbody { border-bottom:1px solid #888580;
+ border-top:1px solid #888580;
+}
+td.param-name {font-weight:bold; margin-right:10px; padding-right: 10px; width:20%;}
+.code, .codelisting {
+ font-family: "Verdana", "Arial";
+ font-size: 8pt;
+ padding: 7px;
+ background-color: #eee;
+ color: #222;
+ white-space: pre;
+ margin-bottom: 1.5em;
+ margin-top:0em;
+ -moz-border-radius: 5px; -webkit-border-radius: 5px;
+}
+
+.comment {
+ color: #006600;
+}
+.hl-comment {
+ color: #006600;
+}
+
+
+/* Scripting overview stuff */
+.toc {
+ float: left;
+ margin-top: 1px;
+ margin-bottom: 1em;
+ vertical-align: top;
+ text-align: left;
+}
+.tocclear {
+ clear: both;
+}
+.toc p {
+ margin-bottom:0px;
+}
+.toc p.tocheader {
+ font-weight:bold;
+ background-color: white;
+ text-align: left;
+ font-size: 12px;
+}
+
+
+dd {
+ margin-left:0em;
+ margin-bottom:1em;
+}
+
+dt { font-weight:bold; }
+th { text-align:left; padding-right:20px;}
+
+.pre-next-link a {
+ width: 180px;
+ display: inline-block;
+ white-space: nowrap;
+ margin-bottom: -3px;
+}
+
+.main ul {
+ padding-left:20px;
+}
+
+.variable {
+ font-style:italic;
+}
+
diff --git a/Runtime/Export/iOS/CocoaIntegration.cs b/Runtime/Export/iOS/CocoaIntegration.cs
new file mode 100644
index 0000000..dea1431
--- /dev/null
+++ b/Runtime/Export/iOS/CocoaIntegration.cs
@@ -0,0 +1,89 @@
+#if UNITY_IPHONE_API
+
+using System;
+using System.Collections;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace UnityEngine
+{
+public sealed partial class iPhone
+{
+ [System.Runtime.InteropServices.DllImport("__Internal")]
+ internal static extern void UnityNSObject_RetainObject(IntPtr obj);
+
+ [System.Runtime.InteropServices.DllImport("__Internal")]
+ internal static extern void UnityNSObject_ReleaseObject(IntPtr obj);
+
+ public sealed partial class NSError
+ {
+ [System.Runtime.InteropServices.DllImport("__Internal")]
+ private static extern int UnityNSError_Code(IntPtr errorObj);
+
+ [System.Runtime.InteropServices.DllImport("__Internal")]
+ private static extern IntPtr UnityNSError_Description(IntPtr errorObj);
+
+ [System.Runtime.InteropServices.DllImport("__Internal")]
+ private static extern IntPtr UnityNSError_Reason(IntPtr errorObj);
+
+
+ private IntPtr _nativeError;
+ private NSError(IntPtr nativeError)
+ {
+ _nativeError = nativeError;
+ UnityNSObject_RetainObject(_nativeError);
+ }
+ ~NSError()
+ {
+ UnityNSObject_ReleaseObject(_nativeError);
+ }
+
+ public static NSError CreateNSError(IntPtr nativeError)
+ {
+ return nativeError == IntPtr.Zero ? null : new NSError(nativeError);
+ }
+
+ public int code
+ {
+ get { return UnityNSError_Code(_nativeError); }
+ }
+ public string description
+ {
+ get { return Marshal.PtrToStringAnsi(UnityNSError_Description(_nativeError)); }
+ }
+ public string reason
+ {
+ get { return Marshal.PtrToStringAnsi(UnityNSError_Reason(_nativeError)); }
+ }
+ }
+
+ public sealed partial class NSNotification
+ {
+ [System.Runtime.InteropServices.DllImport("__Internal")]
+ private static extern IntPtr UnityNSNotification_Name(IntPtr notificationObj);
+
+ private IntPtr _nativeNotification;
+ private NSNotification(IntPtr nativeNotification)
+ {
+ _nativeNotification = nativeNotification;
+ UnityNSObject_RetainObject(_nativeNotification);
+ }
+ ~NSNotification()
+ {
+ UnityNSObject_ReleaseObject(_nativeNotification);
+ }
+
+ public static NSNotification CreateNSNotification(IntPtr nativeNotification)
+ {
+ return nativeNotification == IntPtr.Zero ? null : new NSNotification(nativeNotification);
+ }
+
+ public string name
+ {
+ get { return Marshal.PtrToStringAnsi(UnityNSNotification_Name(_nativeNotification)); }
+ }
+ }
+}
+}
+
+#endif
diff --git a/Runtime/Export/iOS/iAD.cs b/Runtime/Export/iOS/iAD.cs
new file mode 100644
index 0000000..c8fc80f
--- /dev/null
+++ b/Runtime/Export/iOS/iAD.cs
@@ -0,0 +1,187 @@
+#if UNITY_IPHONE_API
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+public sealed partial class ADBannerView
+{
+ public enum
+ Layout
+ {
+ // banner
+ Top = 0,
+ Bottom = 1,
+
+ // rect
+ TopLeft = 0,
+ TopRight = 4,
+ TopCenter = 8,
+ BottomLeft = 1,
+ BottomRight = 5,
+ BottomCenter= 9,
+ CenterLeft = 2,
+ CenterRight = 6,
+ Center = 10,
+
+ Manual = -1
+ };
+
+ public enum
+ Type
+ {
+ Banner = 0,
+ MediumRect = 1
+ };
+
+ private Layout _layout;
+ private IntPtr _bannerView;
+
+ public static bool IsAvailable(Type type)
+ {
+ return Native_BannerTypeAvailable((int)type);
+ }
+
+ // some naughty magic to make sure we can properly strip class
+ // if ctor is ever called, static func will be forcibly generated
+ private static bool _AlwaysFalseDummy = false;
+ public ADBannerView(Type type, Layout layout)
+ {
+ if(_AlwaysFalseDummy)
+ {
+ FireBannerWasClicked();
+ FireBannerWasLoaded();
+ }
+
+ _bannerView = Native_CreateBanner((int)type, (int)layout);
+ }
+ ~ADBannerView()
+ {
+ Native_DestroyBanner(_bannerView);
+ }
+
+ public bool loaded
+ {
+ get { return Native_BannerAdLoaded(_bannerView); }
+ }
+ public bool visible
+ {
+ get { return Native_BannerAdVisible(_bannerView); }
+ set { Native_ShowBanner(_bannerView, value); }
+ }
+ public Layout layout
+ {
+ // TODO: should we query native side?
+ get { return _layout; }
+ set { _layout = value; Native_LayoutBanner(_bannerView, (int)_layout); }
+ }
+
+ public Vector2 position
+ {
+ get
+ {
+ Vector2 ret; Native_BannerPosition(_bannerView, out ret);
+ return OSToScreenCoords(ret);
+ }
+ set
+ {
+ Vector2 pos = new Vector2(value.x/Screen.width, value.y/Screen.height);
+ Native_MoveBanner(_bannerView, pos);
+ }
+ }
+
+ public Vector2 size
+ {
+ get
+ {
+ Vector2 ret; Native_BannerSize(_bannerView, out ret);
+ return OSToScreenCoords(ret);
+ }
+ }
+
+ public delegate void BannerWasClickedDelegate();
+ public static event BannerWasClickedDelegate onBannerWasClicked = null;
+
+ public delegate void BannerWasLoadedDelegate();
+ public static event BannerWasLoadedDelegate onBannerWasLoaded = null;
+
+ private Vector2 OSToScreenCoords(Vector2 v)
+ {
+ return new Vector2(v.x * Screen.width, v.y * Screen.height);
+ }
+
+ private static void FireBannerWasClicked()
+ {
+ if(onBannerWasClicked != null)
+ onBannerWasClicked();
+ }
+
+ private static void FireBannerWasLoaded()
+ {
+ if(onBannerWasLoaded != null)
+ onBannerWasLoaded();
+ }
+}
+
+public sealed partial class ADInterstitialAd
+{
+ private IntPtr interstitialView;
+
+ public static bool isAvailable
+ {
+ get { return Native_InterstitialAvailable(); }
+ }
+
+ // some naughty magic to make sure we can properly strip class
+ // if ctor is ever called, static func will be forcibly generated
+ private static bool _AlwaysFalseDummy = false;
+ private void CtorImpl(bool autoReload)
+ {
+ if(_AlwaysFalseDummy)
+ FireInterstitialWasLoaded();
+
+ interstitialView = Native_CreateInterstitial(autoReload);
+ }
+ public ADInterstitialAd(bool autoReload)
+ {
+ CtorImpl(autoReload);
+ }
+ public ADInterstitialAd()
+ {
+ CtorImpl(false);
+ }
+ ~ADInterstitialAd()
+ {
+ Native_DestroyInterstitial(interstitialView);
+ }
+
+
+ public void Show()
+ {
+ Native_ShowInterstitial(interstitialView);
+ }
+ public void ReloadAd()
+ {
+ Native_ReloadInterstitial(interstitialView);
+ }
+
+ public bool loaded
+ {
+ get { return Native_InterstitialAdLoaded(interstitialView); }
+ }
+
+ public delegate void InterstitialWasLoadedDelegate();
+ public static event InterstitialWasLoadedDelegate onInterstitialWasLoaded = null;
+
+ private static void FireInterstitialWasLoaded()
+ {
+ if(onInterstitialWasLoaded != null)
+ onInterstitialWasLoaded();
+ }
+}
+}
+
+#endif
diff --git a/Runtime/Export/iOS/iAD.txt b/Runtime/Export/iOS/iAD.txt
new file mode 100644
index 0000000..725e4e6
--- /dev/null
+++ b/Runtime/Export/iOS/iAD.txt
@@ -0,0 +1,91 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+using namespace Unity;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+CONDITIONAL UNITY_IPHONE_API
+CLASS ADBannerView
+
+ C++RAW extern void* UnityAD_CreateBanner(int, int);
+ C++RAW extern void UnityAD_DestroyBanner(void*);
+ C++RAW extern void UnityAD_ShowBanner(void*, bool);
+ C++RAW extern void UnityAD_MoveBanner(void*, float, float);
+ C++RAW extern void UnityAD_LayoutBanner(void*, int);
+ C++RAW extern bool UnityAD_BannerTypeAvailable(int);
+ C++RAW extern void UnityAD_BannerPosition(void*, float*, float*);
+ C++RAW extern void UnityAD_BannerSize(void*, float*, float*);
+ C++RAW extern bool UnityAD_BannerAdLoaded(void*);
+ C++RAW extern bool UnityAD_BannerAdVisible(void*);
+
+
+ CUSTOM private static IntPtr Native_CreateBanner(int type, int layout) { return UnityAD_CreateBanner(type, layout); }
+ CUSTOM private static void Native_ShowBanner(IntPtr view, bool show) { UnityAD_ShowBanner(view, show); }
+ CUSTOM private static void Native_MoveBanner(IntPtr view, Vector2 pos) { UnityAD_MoveBanner(view, pos.x, pos.y); }
+ CUSTOM private static void Native_LayoutBanner(IntPtr view, int layout) { UnityAD_LayoutBanner(view, layout); }
+ CUSTOM private static bool Native_BannerTypeAvailable(int type) { return UnityAD_BannerTypeAvailable(type); }
+ CUSTOM private static void Native_BannerPosition(IntPtr view, out Vector2 pos){ UnityAD_BannerPosition(view, &pos->x, &pos->y); }
+ CUSTOM private static void Native_BannerSize(IntPtr view, out Vector2 pos) { UnityAD_BannerSize(view, &pos->x, &pos->y); }
+ CUSTOM private static bool Native_BannerAdLoaded(IntPtr view) { return UnityAD_BannerAdLoaded(view); }
+ CUSTOM private static bool Native_BannerAdVisible(IntPtr view) { return UnityAD_BannerAdVisible(view); }
+
+
+ THREAD_SAFE CUSTOM private static void Native_DestroyBanner(IntPtr view) { UnityAD_DestroyBanner(view); }
+
+END
+
+CONDITIONAL UNITY_IPHONE_API
+CLASS ADInterstitialAd
+
+ C++RAW extern void* UnityAD_CreateInterstitial(bool);
+ C++RAW extern void UnityAD_DestroyInterstitial(void*);
+ C++RAW extern void UnityAD_ShowInterstitial(void*);
+ C++RAW extern void UnityAD_ReloadInterstitial(void*);
+ C++RAW extern bool UnityAD_InterstitialAvailable();
+ C++RAW extern bool UnityAD_InterstitialAdLoaded(void*);
+
+
+ CUSTOM private static IntPtr Native_CreateInterstitial(bool autoReload) { return UnityAD_CreateInterstitial(autoReload); }
+ CUSTOM private static void Native_ShowInterstitial(IntPtr view) { UnityAD_ShowInterstitial(view); }
+ CUSTOM private static void Native_ReloadInterstitial(IntPtr view) { UnityAD_ReloadInterstitial(view); }
+ CUSTOM private static bool Native_InterstitialAdLoaded(IntPtr view) { return UnityAD_InterstitialAdLoaded(view); }
+ CUSTOM private static bool Native_InterstitialAvailable() { return UnityAD_InterstitialAvailable(); }
+
+
+ THREAD_SAFE CUSTOM private static void Native_DestroyInterstitial(IntPtr view) { UnityAD_DestroyInterstitial(view); }
+END
+
+
+CSRAW
+}
+
+C++RAW
+#if !UNITY_IPHONE
+ void* UnityAD_CreateBanner(int, int) { return 0; }
+ void UnityAD_DestroyBanner(void*) {}
+ void UnityAD_ShowBanner(void*, bool) {}
+ void UnityAD_MoveBanner(void*, float, float) {}
+ void UnityAD_LayoutBanner(void*, int) {}
+ bool UnityAD_BannerTypeAvailable(int) { return false; }
+ void UnityAD_BannerPosition(void*, float*, float*) {}
+ void UnityAD_BannerSize(void*, float*, float*) {}
+ bool UnityAD_BannerAdLoaded(void*) { return false; }
+ bool UnityAD_BannerAdVisible(void*) { return false; }
+ void* UnityAD_CreateInterstitial(bool) { return 0; }
+ void UnityAD_DestroyInterstitial(void*) {}
+ void UnityAD_ShowInterstitial(void*) {}
+ void UnityAD_ReloadInterstitial(void*) {}
+ bool UnityAD_InterstitialAvailable() { return false; }
+ bool UnityAD_InterstitialAdLoaded(void*) { return false; }
+#endif
diff --git a/Runtime/Export/iPhoneInput.txt b/Runtime/Export/iPhoneInput.txt
new file mode 100644
index 0000000..ecda622
--- /dev/null
+++ b/Runtime/Export/iPhoneInput.txt
@@ -0,0 +1,1020 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Runtime/Video/MoviePlayback.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/Network/NetworkManager.h"
+#include "Runtime/Input/GetInput.h"
+#include "Runtime/Input/LocationService.h"
+#include "Runtime/Input/OnScreenKeyboard.h"
+#include "PlatformDependent/iPhonePlayer/iPhoneSettings.h"
+#include "PlatformDependent/iPhonePlayer/APN.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+using namespace Unity;
+
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace UnityEngine
+{
+
+
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API
+OBSOLETE warning iPhoneTouchPhase enumeration is deprecated. Please use TouchPhase instead.
+ENUM iPhoneTouchPhase
+ // A finger touched the screen.
+ Began = 0,
+ // A finger moved on the screen.
+ Moved = 1,
+ // A finger is touching the screen but hasn't moved.
+ Stationary = 2,
+ // A finger was lifted from the screen. This is the final phase of a touch.
+ Ended = 3,
+ // The system cancelled tracking for the touch, as when (for example) the user puts the device to her face or more than five touches happened simultaneously. This is the final phase of a touch.
+ Canceled = 4
+END
+
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API
+OBSOLETE warning iPhoneTouch struct is deprecated. Please use Touch instead.
+STRUCT iPhoneTouch
+ CSRAW private int m_FingerId;
+ CSRAW private Vector2 m_Position;
+ CSRAW private Vector2 m_PositionDelta;
+ CSRAW private float m_TimeDelta;
+ CSRAW private int m_TapCount;
+ CSRAW private iPhoneTouchPhase m_Phase;
+
+ // The unique index for touch.
+ CSRAW public int fingerId { get { return m_FingerId; } }
+
+ // The position of the touch.
+ CSRAW public Vector2 position { get { return m_Position; } }
+
+ // The position delta since last change.
+ CSRAW public Vector2 deltaPosition { get { return m_PositionDelta; } }
+
+ // Amount of time passed since last change.
+ CSRAW public float deltaTime { get { return m_TimeDelta; } }
+
+ // Number of taps.
+ CSRAW public int tapCount { get { return m_TapCount; } }
+
+ // Describes the phase of the touch.
+ CSRAW public iPhoneTouchPhase phase { get { return m_Phase; } }
+
+
+ OBSOLETE warning positionDelta property is deprecated. Please use iPhoneTouch.deltaPosition instead.
+ CSRAW public Vector2 positionDelta { get { return m_PositionDelta; } }
+
+ OBSOLETE warning timeDelta property is deprecated. Please use iPhoneTouch.deltaTime instead.
+ CSRAW public float timeDelta { get { return m_TimeDelta; } }
+END
+
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API
+OBSOLETE warning iPhoneAccelerationEvent struct is deprecated. Please use AccelerationEvent instead.
+STRUCT iPhoneAccelerationEvent
+ CSRAW private Vector3 m_Acceleration;
+ CSRAW private float m_TimeDelta;
+
+ // Value of acceleration.
+ CSRAW public Vector3 acceleration { get { return m_Acceleration; } }
+
+ // Amount of time passed since last accelerometer measurement.
+ CSRAW public float deltaTime { get { return m_TimeDelta; } }
+
+ OBSOLETE warning timeDelta property is deprecated. Please use iPhoneAccelerationEvent.deltaTime instead.
+ CSRAW public float timeDelta { get { return m_TimeDelta; } }
+
+END
+
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API
+OBSOLETE warning iPhoneOrientation enumeration is deprecated. Please use DeviceOrientation instead.
+ENUM iPhoneOrientation
+ // The orientation of the device cannot be determined.
+ Unknown = 0,
+ // The device is in portrait mode, with the device held upright and the home button at the bottom.
+ Portrait = 1,
+ // The device is in portrait mode but upside down, with the device held upright and the home button at the top.
+ PortraitUpsideDown = 2,
+ // The device is in landscape mode, with the device held upright and the home button on the right side.
+ LandscapeLeft = 3,
+ // The device is in landscape mode, with the device held upright and the home button on the left side.
+ LandscapeRight = 4,
+ // The device is held perpendicular to the ground with the screen facing upwards.
+ FaceUp = 5,
+ // The device is held perpendicular to the ground with the screen facing downwards.
+ FaceDown = 6
+END
+
+// The iPhoneInput class acts as the interface into the iPhone's unique Input systems.
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API
+CLASS iPhoneInput
+
+ OBSOLETE warning accelerationEvents property is deprecated. Please use Input.accelerationEvents instead.
+ CSRAW public static iPhoneAccelerationEvent[] accelerationEvents { get {
+ int count = accelerationEventCount;
+ iPhoneAccelerationEvent[] events = new iPhoneAccelerationEvent[count];
+ for (int q = 0; q < count; ++q)
+ events[q] = GetAccelerationEvent (q);
+ return events;
+ }
+ }
+
+ OBSOLETE warning touches property is deprecated. Please use Input.touches instead.
+ CSRAW public static iPhoneTouch[] touches { get {
+ int count = touchCount;
+ iPhoneTouch[] touches = new iPhoneTouch[count];
+ for (int q = 0; q < count; ++q)
+ touches[q] = GetTouch (q);
+ return touches;
+ }
+ }
+
+ OBSOLETE warning GetTouch method is deprecated. Please use Input.GetTouch instead.
+ CUSTOM static iPhoneTouch GetTouch (int index)
+ {
+#if ENABLE_NEW_EVENT_SYSTEM
+ if (index >= 0 && index < GetTouchCount ())
+ {
+ Touch* t = GetTouch(index);
+
+ if (t != NULL)
+ {
+ return *t;
+ }
+ else
+ {
+ Scripting::RaiseMonoException ("GetTouch() failed!");
+ }
+ }
+ else
+ {
+ Scripting::RaiseMonoException ("Index specified to GetTouch() is out of bounds! Must be less than Touch.touchCount.");
+ }
+ Touch dummy;
+ return dummy;
+#else
+ Touch touch;
+
+ if (index >= 0 && index < GetTouchCount ())
+ {
+ if (!GetTouch (index, touch))
+ Scripting::RaiseMonoException ("Internal error.");
+ }
+ else
+ Scripting::RaiseMonoException ("Index out of bounds.");
+ return touch;
+#endif
+ }
+
+ OBSOLETE warning touchCount property is deprecated. Please use Input.touchCount instead.
+ CUSTOM_PROP static int touchCount { return GetTouchCount (); }
+
+ OBSOLETE warning multiTouchEnabled property is deprecated. Please use Input.multiTouchEnabled instead.
+ CUSTOM_PROP static bool multiTouchEnabled { return IsMultiTouchEnabled (); } { return SetMultiTouchEnabled (value); }
+
+ OBSOLETE warning GetAccelerationEvent method is deprecated. Please use Input.GetAccelerationEvent instead.
+ CUSTOM static iPhoneAccelerationEvent GetAccelerationEvent (int index)
+ {
+ Acceleration acc;
+ if (index >= 0 && index < GetAccelerationCount ())
+ GetAcceleration (index, acc);
+ else
+ Scripting::RaiseMonoException ("Index out of bounds.");
+ return acc;
+ }
+
+ OBSOLETE warning accelerationEventCount property is deprecated. Please use Input.accelerationEventCount instead.
+ CUSTOM_PROP static int accelerationEventCount { return GetAccelerationCount (); }
+
+ OBSOLETE warning acceleration property is deprecated. Please use Input.acceleration instead.
+ CUSTOM_PROP static Vector3 acceleration { return GetAcceleration (); }
+
+ OBSOLETE warning orientation property is deprecated. Please use Input.deviceOrientation instead.
+ CUSTOM_PROP static iPhoneOrientation orientation { return GetOrientation(); }
+
+ OBSOLETE warning lastLocation property is deprecated. Please use Input.location.lastData instead.
+ CUSTOM_PROP static LocationInfo lastLocation {
+ if (LocationService::GetLocationStatus() != kLocationServiceRunning)
+ printf_console ("Location service updates are not enabled. Check Handheld.locationServiceStatus before querying last location.\n");
+
+ return LocationService::GetLastLocation();
+ }
+END
+
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API
+OBSOLETE warning iPhoneScreenOrientation enumeration is deprecated. Please use ScreenOrientation instead.
+ENUM iPhoneScreenOrientation
+ //*undocumented*
+ Unknown = 0,
+ // Portrait orientation.
+ Portrait = 1,
+ // Portrait orientation upside down.
+ PortraitUpsideDown = 2,
+ // Landscape orientation, home button on the right side.
+ LandscapeLeft = 3,
+ // Landscape orientation, home button on the left side.
+ LandscapeRight = 4,
+
+ // Default landscape orientation, home button on the right side (equals to \LandscapeLeft\).
+ Landscape = 3,
+END
+
+// Interface into iPhone specific settings.
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API
+CLASS iPhoneSettings
+ OBSOLETE warning screenOrientation property is deprecated. Please use Screen.orientation instead.
+ CUSTOM_PROP static iPhoneScreenOrientation screenOrientation { return GetScreenManager().GetScreenOrientation(); } { GetScreenManager().RequestOrientation(value); }
+ OBSOLETE warning verticalOrientation property is deprecated. Please use Screen.orientation instead.
+ CUSTOM_PROP static bool verticalOrientation { return GetScreenManager().GetScreenOrientation() == kPortrait; } { GetScreenManager().SetScreenOrientation(value ? kPortrait : kLandscapeLeft); }
+ OBSOLETE warning screenCanDarken property is deprecated. Please use Screen.sleepTimeout instead.
+ CUSTOM_PROP static bool screenCanDarken { return IsIdleTimerEnabled(); } { SetIdleTimerEnabled(value); }
+ OBSOLETE warning uniqueIdentifier property is deprecated. Please use SystemInfo.deviceUniqueIdentifier instead.
+ CUSTOM_PROP static string uniqueIdentifier { return scripting_string_new(systeminfo::GetDeviceUniqueIdentifier()); }
+ OBSOLETE warning name property is deprecated. Please use SystemInfo.deviceName instead.
+ CUSTOM_PROP static string name { return scripting_string_new(systeminfo::GetDeviceName()); }
+ OBSOLETE warning model property is deprecated. Please use SystemInfo.deviceModel instead.
+ CUSTOM_PROP static string model { return scripting_string_new(systeminfo::GetDeviceModel()); }
+ OBSOLETE warning systemName property is deprecated. Please use SystemInfo.operatingSystem instead.
+ CUSTOM_PROP static string systemName { return scripting_string_new(systeminfo::GetDeviceSystemName()); }
+ OBSOLETE warning systemVersion property is deprecated. Please use SystemInfo.operatingSystem instead.
+ CUSTOM_PROP static string systemVersion { return scripting_string_new(systeminfo::GetDeviceSystemVersion()); }
+ OBSOLETE warning internetReachability property is deprecated. Please use Application.internetReachability instead.
+ CUSTOM_PROP static iPhoneNetworkReachability internetReachability { return GetInternetReachability(); }
+ OBSOLETE warning generation property is deprecated. Please use iPhone.generation instead.
+ CUSTOM_PROP static iPhoneGeneration generation { return iphone::GetDeviceGeneration(); }
+ OBSOLETE warning locationServiceStatus property is deprecated. Please use Input.location.status instead.
+ CUSTOM_PROP static LocationServiceStatus locationServiceStatus { return LocationService::GetLocationStatus(); }
+ OBSOLETE warning locationServiceEnabledByUser property is deprecated. Please use Input.location.isEnabledByUser instead.
+ CUSTOM_PROP static bool locationServiceEnabledByUser { return LocationService::IsServiceEnabledByUser(); }
+
+ OBSOLETE warning StartLocationServiceUpdates method is deprecated. Please use Input.location.Start instead.
+ CUSTOM static void StartLocationServiceUpdates(float desiredAccuracyInMeters, float updateDistanceInMeters) {
+ LocationService::SetDesiredAccuracy(desiredAccuracyInMeters);
+ LocationService::SetDistanceFilter(updateDistanceInMeters);
+ LocationService::StartUpdatingLocation();
+ }
+ OBSOLETE warning StartLocationServiceUpdates method is deprecated. Please use Input.location.Start instead.
+ CSRAW public static void StartLocationServiceUpdates(float desiredAccuracyInMeters) {
+ StartLocationServiceUpdates(desiredAccuracyInMeters, 10.0f);
+ }
+ OBSOLETE warning StartLocationServiceUpdates method is deprecated. Please use Input.location.Start instead.
+ CSRAW public static void StartLocationServiceUpdates() {
+ StartLocationServiceUpdates(10.0f, 10.0f);
+ }
+
+ OBSOLETE warning StopLocationServiceUpdates method is deprecated. Please use Input.location.Stop instead.
+ CUSTOM static void StopLocationServiceUpdates() {
+ LocationService::StopUpdatingLocation();
+ }
+END
+
+// Describes the type of keyboard.
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API
+OBSOLETE warning iPhoneKeyboardType enumeration is deprecated. Please use TouchScreenKeyboardType instead.
+ENUM iPhoneKeyboardType
+ // Default keyboard for the current input method.
+ Default = 0,
+ // Keyboard displays standard ASCII characters.
+ ASCIICapable = 1,
+ // Keyboard with numbers and punctuation.
+ NumbersAndPunctuation = 2,
+ // Keyboard optimized for URL entry, features ".", "/", and ".com" prominently.
+ URL = 3,
+ // Numeric keypad designed for PIN entry, features the numbers 0 through 9 prominently.
+ NumberPad = 4,
+ // Keypad designed for entering telephone numbers, features the numbers 0 through 9 and the "*" and "#" characters prominently.
+ PhonePad = 5,
+ // Keypad designed for entering a person's name or phone number.
+ NamePhonePad = 6,
+ // Keyboard optimized for specifying email addresses, features the "@", "." and space characters prominently.
+ EmailAddress = 7
+END
+
+// Simple struct that contains all the arguments needed by iPhoneKeyboard_InternalConstructorHelper.
+
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API
+STRUCT internal iPhoneKeyboard_InternalConstructorHelperArguments
+ CSRAW public string text;
+ CSRAW public string textPlaceholder;
+ CSRAW public uint keyboardType;
+ CSRAW public uint autocorrection;
+ CSRAW public uint multiline;
+ CSRAW public uint secure;
+ CSRAW public uint alert;
+END
+
+C++RAW
+
+struct MonoiPhoneKeyboard_InternalConstructorHelperArguments {
+ MonoString* text;
+ MonoString* textPlaceholder;
+ unsigned int keyboardType;
+ unsigned int autocorrection;
+ unsigned int multiline;
+ unsigned int secure;
+ unsigned int alert;
+};
+
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API
+OBSOLETE warning iPhoneKeyboard class is deprecated. Please use TouchScreenKeyboard instead.
+CLASS iPhoneKeyboard
+ // We are matching the KeyboardOnScreen class here so we can directly access it.
+ CSRAW private IntPtr keyboardWrapper;
+
+ C++RAW
+ #define GET ExtractMonoObjectData<KeyboardOnScreen*> (self)
+
+ THREAD_SAFE
+ CUSTOM private void Destroy()
+ {
+ if (GET)
+ {
+ delete GET;
+ GET=0;
+ }
+ }
+
+ //*undocumented*
+ CSRAW ~iPhoneKeyboard()
+ {
+ Destroy();
+ }
+
+ //*undocumented*
+ public iPhoneKeyboard(string text, iPhoneKeyboardType keyboardType, bool autocorrection, bool multiline, bool secure, bool alert, string textPlaceholder)
+ {
+ iPhoneKeyboard_InternalConstructorHelperArguments arguments = new iPhoneKeyboard_InternalConstructorHelperArguments ();
+ arguments.text = text;
+ arguments.keyboardType = Convert.ToUInt32(keyboardType);
+ arguments.autocorrection = Convert.ToUInt32(autocorrection);
+ arguments.multiline = Convert.ToUInt32(multiline);
+ arguments.secure = Convert.ToUInt32(secure);
+ arguments.alert = Convert.ToUInt32(alert);
+ arguments.textPlaceholder = textPlaceholder;
+ iPhoneKeyboard_InternalConstructorHelper (arguments);
+ }
+
+ CUSTOM private void iPhoneKeyboard_InternalConstructorHelper (iPhoneKeyboard_InternalConstructorHelperArguments arguments)
+ {
+ GET = new KeyboardOnScreen(scripting_cpp_string_for(arguments.text),
+ arguments.keyboardType, arguments.autocorrection, arguments.multiline, arguments.secure, arguments.alert,
+ scripting_cpp_string_for(arguments.textPlaceholder));
+ }
+
+ //*undocumented*
+ CSRAW public static iPhoneKeyboard Open(string text, iPhoneKeyboardType keyboardType = iPhoneKeyboardType.Default, bool autocorrection = true, bool multiline = false, bool secure = false, bool alert = false, string textPlaceholder = "")
+ {
+ return new iPhoneKeyboard(text, keyboardType, autocorrection, multiline, secure, alert, textPlaceholder);
+ }
+
+ //*undocumented*
+ CUSTOM_PROP public string text {
+ if (GET) return scripting_string_new(GET->getText());
+ else return scripting_string_new("");
+ } {
+ if (GET) GET->setText(value);
+ }
+
+ //*undocumented*
+ CUSTOM_PROP public static bool hideInput {
+ return KeyboardOnScreen::isInputHidden();
+ } {
+ KeyboardOnScreen::setInputHidden(value);
+ }
+
+ //*undocumented*
+ CUSTOM_PROP public bool active {
+ if (GET) return (short)GET->isActive();
+ else return false;
+ } {
+ if (GET) GET->setActive(value);
+ }
+
+ //*undocumented*
+ CUSTOM_PROP public bool done {
+ if (GET) return (short)GET->isDone();
+ else return false;
+ }
+
+ C++RAW
+ #undef GET
+
+ //*undocumented*
+ CUSTOM_PROP static Rect area { return KeyboardOnScreen::GetRect(); }
+
+ //*undocumented*
+ CUSTOM_PROP static bool visible { return KeyboardOnScreen::IsVisible(); }
+END
+
+
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API
+OBSOLETE warning iPhoneMovieControlMode enumeration is deprecated. Please use FullScreenMovieControlMode instead.
+ENUM iPhoneMovieControlMode
+ // Display the standard controls for controlling movie playback. This includes play/pause controls, a volume slider, and a timeline control.
+ Full = 0,
+ // Display minimal set of controls controlling movie playback. Set of controls might differ between OS versions.
+ Minimal = 1,
+ // Do not display any controls, but cancel movie playback if the user touches the screen.
+ CancelOnTouch = 2,
+ // Do not display any controls. This mode prevents the user from controlling playback.
+ Hidden = 3,
+
+ OBSOLETE warning VolumeOnly is deprecated. Please use iPhoneMovieControlMode.Minimal instead.
+ // Display volume controls only.
+ VolumeOnly = 1
+END
+
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API
+OBSOLETE warning iPhoneMovieScalingMode enumeration is deprecated. Please use FullScreenMovieScalingMode instead.
+ENUM iPhoneMovieScalingMode
+ // Do not scale the movie.
+ None = 0,
+ // Scale the movie until one dimension fits on the screen exactly.
+ AspectFit = 1,
+ // Scale the movie until the movie fills the entire screen.
+ AspectFill = 2,
+ // Scale the movie until both dimensions fit the screen exactly. The aspect ratio of the movie is not preserved.
+ Fill = 3
+END
+
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API
+OBSOLETE warning iPhoneNetworkReachability enumeration is deprecated. Please use NetworkReachability instead.
+ENUM iPhoneNetworkReachability
+ // Network is not reachable
+ NotReachable = 0,
+ // Network is reachable via carrier data network
+ ReachableViaCarrierDataNetwork = 1,
+ // Network is reachable via WiFi network
+ ReachableViaWiFiNetwork = 2
+END
+
+// Interface into iPhone miscellaneous functionality.
+CONDITIONAL UNITY_IPHONE_API || UNITY_ANDROID_API
+CLASS iPhoneUtils
+
+ // we want to avoid obsolete warnings:
+ // if method is not marked as obsolete and uses obsolete types we get warning
+ // so prototypes for c-side calls will use ints and we manually create default-params overloads
+
+ OBSOLETE warning PlayMovie method is deprecated. Please use Handheld.PlayFullScreenMovie instead.
+ CUSTOM static void PlayMovie (string path, Color bgColor, int controlMode, int scalingMode)
+ {
+ PlayFullScreenMovie (path, bgColor, (unsigned)controlMode, (unsigned)scalingMode);
+ }
+ OBSOLETE warning PlayMovie method is deprecated. Please use Handheld.PlayFullScreenMovie instead.
+ CSRAW public static void PlayMovie (string path, Color bgColor, iPhoneMovieControlMode controlMode, iPhoneMovieScalingMode scalingMode)
+ {
+ PlayMovie (path, bgColor, (int)controlMode, (int)scalingMode);
+ }
+ OBSOLETE warning PlayMovie method is deprecated. Please use Handheld.PlayFullScreenMovie instead.
+ CSRAW public static void PlayMovie (string path, Color bgColor, iPhoneMovieControlMode controlMode)
+ {
+ PlayMovie (path, bgColor, (int)controlMode, (int)iPhoneMovieScalingMode.AspectFit);
+ }
+ OBSOLETE warning PlayMovie method is deprecated. Please use Handheld.PlayFullScreenMovie instead.
+ CSRAW public static void PlayMovie (string path, Color bgColor)
+ {
+ PlayMovie (path, bgColor, (int)iPhoneMovieControlMode.Full, (int)iPhoneMovieScalingMode.AspectFit);
+ }
+
+ OBSOLETE warning PlayMovieURL method is deprecated. Please use Handheld.PlayFullScreenMovie instead.
+ CUSTOM static void PlayMovieURL (string url, Color bgColor, int controlMode, int scalingMode)
+ {
+ PlayFullScreenMovie (url, bgColor, (unsigned)controlMode, (unsigned)scalingMode);
+ }
+ OBSOLETE warning PlayMovieURL method is deprecated. Please use Handheld.PlayFullScreenMovie instead.
+ CSRAW public static void PlayMovieURL (string url, Color bgColor, iPhoneMovieControlMode controlMode, iPhoneMovieScalingMode scalingMode)
+ {
+ PlayMovieURL (url, bgColor, (int)controlMode, (int)scalingMode);
+ }
+ OBSOLETE warning PlayMovieURL method is deprecated. Please use Handheld.PlayFullScreenMovie instead.
+ CSRAW public static void PlayMovieURL (string url, Color bgColor, iPhoneMovieControlMode controlMode)
+ {
+ PlayMovieURL (url, bgColor, (int)controlMode, (int)iPhoneMovieScalingMode.AspectFit);
+ }
+ OBSOLETE warning PlayMovieURL method is deprecated. Please use Handheld.PlayFullScreenMovie instead.
+ CSRAW public static void PlayMovieURL (string url, Color bgColor)
+ {
+ PlayMovieURL (url, bgColor, (int)iPhoneMovieControlMode.Full, (int)iPhoneMovieScalingMode.AspectFit);
+ }
+
+ OBSOLETE warning Vibrate method is deprecated. Please use Handheld.Vibrate instead.
+ CUSTOM static void Vibrate ()
+ {
+ Vibrate ();
+ }
+ OBSOLETE warning isApplicationGenuine property is deprecated. Please use Application.genuine instead.
+ CUSTOM_PROP static bool isApplicationGenuine
+ {
+ return IsApplicationGenuine ();
+ }
+ OBSOLETE warning isApplicationGenuineAvailable property is deprecated. Please use Application.genuineCheckAvailable instead.
+ CUSTOM_PROP static bool isApplicationGenuineAvailable
+ {
+ return IsApplicationGenuineAvailable();
+ }
+END
+
+// Specify calendar types.
+CONDITIONAL UNITY_IPHONE_API
+ENUM CalendarIdentifier
+ // Identifies the Gregorian calendar.
+ GregorianCalendar = 0,
+ // Identifies the Buddhist calendar.
+ BuddhistCalendar = 1,
+ // Identifies the Chinese calendar.
+ ChineseCalendar = 2,
+ // Identifies the Hebrew calendar.
+ HebrewCalendar = 3,
+ // Identifies the Islamic calendar.
+ IslamicCalendar = 4,
+ // Identifies the Islamic civil calendar.
+ IslamicCivilCalendar = 5,
+ // Identifies the Japanese calendar.
+ JapaneseCalendar = 6,
+ // Identifies the Republic of China (Taiwan) calendar.
+ RepublicOfChinaCalendar = 7,
+ // Identifies the Persian calendar.
+ PersianCalendar = 8,
+ // Identifies the Indian calendar.
+ IndianCalendar = 9,
+ // Identifies the ISO8601.
+ ISO8601Calendar = 10
+END
+
+// Specify calendrical units.
+CONDITIONAL UNITY_IPHONE_API
+ENUM CalendarUnit
+ // Specifies the era unit.
+ Era = 2,
+ // Specifies the year unit.
+ Year = 4,
+ // Specifies the month unit.
+ Month = 8,
+ // Specifies the day unit.
+ Day = 16,
+ // Specifies the hour unit.
+ Hour = 32,
+ // Specifies the minute unit.
+ Minute = 64,
+ // Specifies the second unit.
+ Second = 128,
+ // Specifies the week unit.
+ Week = 256,
+ // Specifies the weekday unit.
+ Weekday = 512,
+ // Specifies the ordinal weekday unit.
+ WeekdayOrdinal = 1024,
+ // Specifies the quarter of the calendar.
+ Quarter = 2048
+END
+
+// LocalNotification is a wrapper around the UILocalNotification class found in the Apple UIKit framework and is only available on iPhone/iPad/iPod Touch.
+CONDITIONAL UNITY_IPHONE_API
+CLASS LocalNotification
+ CSRAW private IntPtr notificationWrapper;
+
+ C++RAW
+ #define GET ExtractMonoObjectData<iPhoneLocalNotification*> (self)
+
+ //*undocumented*
+ CUSTOM private double GetFireDate()
+ {
+ return GET->GetFireDate();
+ }
+
+ //*undocumented*
+ CUSTOM private void SetFireDate(double dt)
+ {
+ GET->SetFireDate(dt);
+ }
+
+ CSRAW private static long m_NSReferenceDateTicks = new DateTime(
+ 2001, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks;
+
+ // The date and time when the system should deliver the notification.
+ CSRAW public DateTime fireDate { get {
+ return new DateTime((long)(GetFireDate() * 10000000) + m_NSReferenceDateTicks);
+ }
+
+ set {
+ SetFireDate((value.ToUniversalTime().Ticks - m_NSReferenceDateTicks) / 10000000.0);
+ }
+ }
+
+ // The time zone of the notification's fire date.
+ CUSTOM_PROP public string timeZone
+ {
+ const char *timeZone = GET->GetTimeZone();
+ return (timeZone ? scripting_string_new(timeZone) : 0);
+ }
+ {
+ GET->SetTimeZone(value.IsNull() ? 0 : value.AsUTF8().c_str());
+ }
+
+ // The calendar interval at which to reschedule the notification.
+ CUSTOM_PROP public CalendarUnit repeatInterval
+ {
+ return GET->GetRepeatInterval();
+ }
+ {
+ GET->SetRepeatInterval(value);
+ }
+
+ // The name of the file containing the sound to play when an alert is displayed.
+ CUSTOM_PROP public CalendarIdentifier repeatCalendar
+ {
+ return GET->GetRepeatCalendar();
+ }
+ {
+ GET->SetRepeatCalendar(value);
+ }
+
+ // The message displayed in the notification alert.
+ CUSTOM_PROP public string alertBody
+ {
+ const char *message = GET->GetAlertBody();
+ return (message ? scripting_string_new(message) : 0);
+ }
+ {
+ GET->SetAlertBody(value.IsNull() ? 0 : value.AsUTF8().c_str());
+ }
+
+ // The title of the action button or slider.
+ CUSTOM_PROP public string alertAction
+ {
+ const char *action = GET->GetAlertAction();
+ return (action ? scripting_string_new(action) : 0);
+ }
+ {
+ GET->SetAlertAction(value.IsNull() ? 0 : value.AsUTF8().c_str());
+ }
+
+ // A boolean value that controls whether the alert action is visible or not.
+ CUSTOM_PROP public bool hasAction
+ {
+ return GET->HasAction();
+ }
+ {
+ GET->HasAction(value);
+ }
+
+ // Identifies the image used as the launch image when the user taps the action button.
+ CUSTOM_PROP public string alertLaunchImage
+ {
+ const char *path = GET->GetAlertLaunchImage();
+ return (path ? scripting_string_new(path) : 0);
+ }
+ {
+ GET->SetAlertLaunchImage(value.IsNull () ? 0 : value.AsUTF8().c_str());
+ }
+
+ // The number to display as the application's icon badge.
+ CUSTOM_PROP public int applicationIconBadgeNumber
+ {
+ return GET->GetApplicationIconBadgeNumber();
+ }
+ {
+ GET->SetApplicationIconBadgeNumber(value);
+ }
+
+ // The name of the sound file to play when an alert is displayed.
+ CUSTOM_PROP public string soundName
+ {
+ const char *path = GET->GetSoundName();
+ return (path ? scripting_string_new(path) : 0);
+ }
+ {
+ GET->SetSoundName(value.IsNull() ? 0 : value.AsUTF8().c_str());
+ }
+
+ // The default system sound. (RO)
+ CUSTOM_PROP public static string defaultSoundName
+ {
+ const char *path = iPhoneLocalNotification::GetDefaultSoundName();
+ return (path ? scripting_string_new(path) : 0);
+ }
+
+ // A dictionary for passing custom information to the notified application.
+ CUSTOM_PROP public IDictionary userInfo
+ {
+ return GET->GetUserInfo();
+ }
+ {
+ GET->SetUserInfo(value);
+ }
+
+ THREAD_SAFE
+ CUSTOM private void Destroy()
+ {
+ if (GET)
+ {
+ delete GET;
+ GET = 0;
+ }
+ }
+
+ //*undocumented*
+ CSRAW ~LocalNotification()
+ {
+ Destroy();
+ }
+
+ //*undocumented*
+ CUSTOM private void InitWrapper()
+ {
+ GET = new iPhoneLocalNotification;
+ }
+
+ // Creates a new local notification.
+ CSRAW public LocalNotification()
+ {
+ InitWrapper();
+ }
+
+ C++RAW
+ #undef GET
+END
+
+// RemoteNotification is only available on iPhone/iPad/iPod Touch.
+CONDITIONAL UNITY_IPHONE_API
+CLASS RemoteNotification
+ CSRAW private IntPtr notificationWrapper;
+
+ C++RAW
+ #define GET ExtractMonoObjectData<iPhoneRemoteNotification*> (self)
+
+ // The message displayed in the notification alert. (RO)
+ CUSTOM_PROP public string alertBody
+ {
+ const char *message = GET->GetAlertBody();
+ return (message ? scripting_string_new(message) : 0);
+ }
+
+ // A boolean value that controls whether the alert action is visible or not. (RO)
+ CUSTOM_PROP public bool hasAction
+ {
+ return GET->HasAction();
+ }
+
+ // The number to display as the application's icon badge. (RO)
+ CUSTOM_PROP public int applicationIconBadgeNumber
+ {
+ return GET->GetApplicationIconBadgeNumber();
+ }
+
+ // The name of the sound file to play when an alert is displayed. (RO)
+ CUSTOM_PROP public string soundName
+ {
+ const char *path = GET->GetSoundName();
+ return (path ? scripting_string_new(path) : 0);
+ }
+
+ // A dictionary for passing custom information to the notified application. (RO)
+ CUSTOM_PROP public IDictionary userInfo
+ {
+ return GET->GetUserInfo();
+ }
+
+ THREAD_SAFE
+ CUSTOM private void Destroy()
+ {
+ if (GET)
+ {
+ delete GET;
+ GET = 0;
+ }
+ }
+
+ //*undocumented*
+ CSRAW ~RemoteNotification()
+ {
+ Destroy();
+ }
+
+ //*undocumented*
+ CSRAW private RemoteNotification()
+ { }
+
+ C++RAW
+ #undef GET
+END
+
+// Specify remote notification types.
+CONDITIONAL UNITY_IPHONE_API
+ENUM RemoteNotificationType
+ // The application accepts no notifications.
+ None = 0,
+ // The application accepts notifications that badge the application icon.
+ Badge = 1,
+ // The application accepts alert sounds as notifications.
+ Sound = 2,
+ // The application accepts alert messages as notifications.
+ Alert = 4
+END
+
+// NotificationServices is only available on iPhone/iPad/iPod Touch.
+CONDITIONAL UNITY_IPHONE_API
+CLASS NotificationServices
+ // The number of received local notifications. (RO)
+ CUSTOM_PROP static int localNotificationCount { return GetLocalNotificationCount(); }
+
+ // Returns an object representing a specific local notification. (RO)
+ CUSTOM static LocalNotification GetLocalNotification(int index)
+ {
+ if (index >= 0 && index < GetLocalNotificationCount())
+ {
+ MonoClass *classLocalNotification;
+ MonoObject *localNotification;
+
+ classLocalNotification = GetMonoManager().GetBuiltinMonoClass("LocalNotification");
+ localNotification = mono_object_new(mono_domain_get(), classLocalNotification);
+
+ ExtractMonoObjectData<iPhoneLocalNotification*>(localNotification) = CopyLocalNotification(index);
+
+ return localNotification;
+ }
+
+ Scripting::RaiseMonoException("Index out of bounds.");
+ return 0;
+ }
+
+ // The list of objects representing received local notifications. (RO)
+ CSRAW public static LocalNotification[] localNotifications { get {
+ int count = localNotificationCount;
+ LocalNotification[] notifications = new LocalNotification[count];
+ for (int i = 0; i < count; ++i)
+ notifications[i] = GetLocalNotification(i);
+ return notifications;
+ }
+ }
+
+ // Schedules a local notification.
+ CUSTOM public static void ScheduleLocalNotification(LocalNotification notification)
+ {
+ Scripting::RaiseIfNull(notification);
+ ExtractMonoObjectData<iPhoneLocalNotification*>(notification)->Schedule();
+ }
+
+ // Presents a local notification immediately.
+ CUSTOM public static void PresentLocalNotificationNow(LocalNotification notification)
+ {
+ Scripting::RaiseIfNull(notification);
+ ExtractMonoObjectData<iPhoneLocalNotification*>(notification)->PresentNow();
+ }
+
+ // Cancels the delivery of the specified scheduled local notification.
+ CUSTOM public static void CancelLocalNotification(LocalNotification notification)
+ {
+ Scripting::RaiseIfNull(notification);
+ ExtractMonoObjectData<iPhoneLocalNotification*>(notification)->Cancel();
+ }
+
+ // Cancels the delivery of all scheduled local notifications.
+ CUSTOM public static void CancelAllLocalNotifications()
+ {
+ iPhoneLocalNotification::CancelAll();
+ }
+
+ // All currently scheduled local notifications.
+ CUSTOM_PROP public static LocalNotification[] scheduledLocalNotifications
+ {
+ std::vector<iPhoneLocalNotification*> notifications = iPhoneLocalNotification::GetScheduled();
+ int count = notifications.size();
+ MonoClass *classLocalNotification = GetMonoManager().GetBuiltinMonoClass("LocalNotification");
+ MonoArray *monoNotifications = mono_array_new(mono_domain_get(), classLocalNotification, count);
+
+ for (int index = 0; index < count; ++index)
+ {
+ MonoObject *notif = mono_object_new(mono_domain_get(), classLocalNotification);
+ ExtractMonoObjectData<iPhoneLocalNotification*>(notif) = notifications[index];
+ Scripting::SetScriptingArrayElement(monoNotifications, index, notif);
+ }
+
+ return monoNotifications;
+ }
+
+ // The number of received remote notifications. (RO)
+ CUSTOM_PROP static int remoteNotificationCount { return GetRemoteNotificationCount(); }
+
+ // Returns an object representing a specific remote notification. (RO)
+ CUSTOM static RemoteNotification GetRemoteNotification(int index)
+ {
+ if (index >= 0 && index < GetRemoteNotificationCount())
+ {
+ MonoClass *classRemoteNotification;
+ MonoObject *remoteNotification;
+
+ classRemoteNotification = GetMonoManager().GetBuiltinMonoClass("RemoteNotification");
+ remoteNotification = mono_object_new(mono_domain_get(), classRemoteNotification);
+
+ ExtractMonoObjectData<iPhoneRemoteNotification*>(remoteNotification) = CopyRemoteNotification(index);
+
+ return remoteNotification;
+ }
+
+ Scripting::RaiseMonoException("Index out of bounds.");
+ return 0;
+ }
+
+ // The list of objects representing received remote notifications. (RO)
+ CSRAW public static RemoteNotification[] remoteNotifications { get {
+ int count = remoteNotificationCount;
+ RemoteNotification[] notifications = new RemoteNotification[count];
+ for (int i = 0; i < count; ++i)
+ notifications[i] = GetRemoteNotification(i);
+ return notifications;
+ }
+ }
+
+ // Discards of all received local notifications.
+ CUSTOM public static void ClearLocalNotifications()
+ {
+ ClearLocalNotifications();
+ }
+
+ // Discards of all received remote notifications.
+ CUSTOM public static void ClearRemoteNotifications()
+ {
+ ClearRemoteNotifications();
+ }
+
+ // Register to receive remote notifications of the specified types from a provider via Apple Push Service.
+ CUSTOM public static void RegisterForRemoteNotificationTypes(RemoteNotificationType notificationTypes)
+ {
+ iPhoneRemoteNotification::Register(notificationTypes);
+ }
+
+ // Unregister for remote notifications.
+ CUSTOM public static void UnregisterForRemoteNotifications()
+ {
+ iPhoneRemoteNotification::Unregister();
+ }
+
+ // The types of notifications the application accepts.
+ CUSTOM_PROP public static RemoteNotificationType enabledRemoteNotificationTypes
+ {
+ return iPhoneRemoteNotification::GetEnabledTypes();
+ }
+
+ // Device token received from Apple Push Service after calling @@NotificationServices.RegisterForRemoteNotificationTypes@@. (RO)
+ CUSTOM_PROP public static byte[] deviceToken
+ {
+ const char *deviceToken = iPhoneRemoteNotification::GetDeviceToken();
+
+ if (!deviceToken)
+ return 0;
+
+ int count = iPhoneRemoteNotification::GetDeviceTokenLength();
+ MonoArray *monoDeviceToken = mono_array_new(mono_domain_get(), mono_get_byte_class(), count);
+
+ for (int index = 0; index < count; ++index)
+ {
+ Scripting::SetScriptingArrayElement(monoDeviceToken, index, deviceToken[index]);
+ }
+
+ return monoDeviceToken;
+ }
+
+ // Returns an error that might occur on registration for remote notifications via @@NotificationServices.RegisterForRemoteNotificationTypes@@. (RO)
+ CUSTOM_PROP public static string registrationError
+ {
+ const char *error = iPhoneRemoteNotification::GetError();
+ return (error ? scripting_string_new(error) : 0);
+ }
+END
+
+CONDITIONAL UNITY_IPHONE_API
+CLASS internal UnhandledExceptionHandler
+ CSRAW private static void RegisterUECatcher()
+ {
+ AppDomain.CurrentDomain.UnhandledException += HandleUnhandledException;
+ }
+
+ C++RAW
+ extern void CrashedCheckBellowForHintsWhy();
+ THREAD_SAFE
+ CUSTOM private static void HandleUnhandledException(object sender, object args)
+ {
+ #if UNITY_IOS
+ // Let unhandled exceptions crash only when happening on main thread
+ if ( Thread::CurrentThreadIsMainThread() )
+ {
+ // This function is defined in iOS trampoline
+ CrashedCheckBellowForHintsWhy();
+ }
+ #endif
+ }
+END
+
+CSRAW
+}
+
diff --git a/Runtime/Export/style.css b/Runtime/Export/style.css
new file mode 100644
index 0000000..af8867a
--- /dev/null
+++ b/Runtime/Export/style.css
@@ -0,0 +1,407 @@
+html {
+ height: 100%;
+}
+body {
+ background-color: white;
+ margin: 0px;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 12px;
+ height: 100%;
+ overflow: auto;
+ color: #222;
+}
+img {
+ border: 0px;
+}
+.main {
+ padding-top:0px;
+ padding-left:185px;
+ padding-right:5px;
+}
+
+a {
+ color: #900000;
+ text-decoration:underline;
+}
+
+.classlink {
+ text-decoration:none;
+}
+.itemlink {
+ text-decoration:none;
+}
+
+
+td {
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 12px;
+}
+th {
+ border-bottom: 1px solid #c9c9c9;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 12px;
+ text-align: left;
+}
+
+table.parameters {
+ margin: 0px 20px 0px 20px;
+ padding: 7px;
+// border: 1px solid #ededed;
+ width: 87%;
+ border-collapse: collapse;
+}
+
+table.parameters th {
+ padding-left:2px;
+ padding-right:2px;
+ margin: 0px;
+ border-bottom: 1px solid #666666;
+}
+
+table.parameters td {
+ padding-left:2px;
+ padding-right:2px;
+ border: 0px;
+ margin: 0px;
+}
+
+table.parameters th {
+ color: #222;
+// background: #eee;
+}
+
+tr.even-row {
+// background: #edf3fe;
+}
+tr.even-row td{
+// border-left: 1px solid #dcdfe4;
+}
+tr.odd-row td {
+ padding-right: 20px;
+// border-left: 1px solid #ededed;
+}
+
+td.param-name {
+ font-weight: bold;
+ padding-left: 0px;
+}
+
+
+dt {
+ font-weight: bold;
+}
+
+h1{
+ font-size: 20px;
+ margin-bottom: 1px;
+ clear: both;
+}
+
+.script-section-hardheading {
+ display: block;
+ width: 100%;
+ font-size: 20px;
+ font-weight: bold;
+ clear: both;
+ color: #666666;
+ margin-bottom: 1px;
+ margin-top: 20px;
+}
+
+.script-section-softheading {
+ display: block;
+ width: 70%;
+ font-size: 12px;
+ font-weight: bold;
+ clear: both;
+ color: black;
+ margin-bottom: 1px;
+ margin-top: 10px;
+ border-bottom: 1px solid #c9c9c9;
+}
+
+h3 {
+ font-size: 12px;
+ font-weight: bold;
+ clear: both;
+ margin-top: 1px;
+ margin-bottom: 1px;
+ color: black;
+}
+.manual-entry h3 {
+ padding-left: 40px;
+ text-indent: -40px;
+}
+
+h3.soft {
+ color: #666666;
+ margin-top:10px;
+ margin-bottom:0px;
+ font-weight: normal;
+}
+p {
+ padding-top: 0px;
+ margin-top: 0px;
+ margin-bottom:10px;
+}
+
+
+p.details {
+ padding-left:20px;
+ padding-right:20px;
+}
+p.basic {
+}
+p.first {
+ margin-top: 10px;
+}
+.toplink {
+}
+
+.synopsis {
+ font-family: "monaco", "Courier", "Courier New";
+ font-size: 11px;
+ font-weight: normal;
+ padding-left:20px;
+ margin-bottom:10px;
+}
+.synopsis .doc-comment {
+ font: italic 12px;
+}
+.note {
+ font-weight: bold;
+ color: #666666;
+}
+.editorclass {
+ color: #666666;
+}
+.code {
+ font-family: "Verdana", "Arial";
+ font-size: 8pt;
+ margin: 10px 20px 10px 30px;
+ padding: 7px;
+ background-color: #eee;
+ color: #222;
+ border: 1px solid #c9c9c9;
+ white-space: pre;
+ width: 85%;
+}
+.comment {
+ color: #006600;
+}
+.hl-comment {
+ color: #666666;
+ color: #006600;
+}
+.hl-datatype {
+ color: #0000a0;
+}
+.hl-keyword {
+ color: #000066;
+}
+.hl-operator {
+ font-weight: bold;
+ color: #444;
+}
+.hl-string {
+ color: #0000a0;
+}
+
+.refimg {
+ display: block;
+ border: 1px solid #c9c9c9;
+ margin: 5px 20px 5px 30px;
+ padding: 1px;
+}
+
+.toc {
+ width:180;
+ font-size:12px;
+ border-top-width: 1px;
+ border-bottom-width: 1px;
+ float: left;
+ margin-top: 1px;
+ margin-bottom: 10px;
+ vertical-align: top;
+ text-align: left;
+ font-weight:bold;
+}
+.tocclear {
+ clear: both;
+}
+.toc p {
+ margin-bottom:0px;
+ margin-left: 10px;
+}
+.toc p.tocheader {
+ background-color: white;
+ border-bottom: 1px solid #c9c9c9;
+ margin-left: 10px;
+ text-align: left;
+ font-size: 12px;
+}
+.toc a {
+ font-weight: normal;
+ text-decoration:none;
+}
+
+.class-member-list {
+ width: 100%;
+}
+.class-member-list-name {
+ width: 200px;
+ text-align: left;
+}
+.class-member-list-name a {
+ text-decoration:none;
+}
+.class-member-description {
+ display: inline;
+}
+.class-member-list-inherited .class-member-list-name {
+ font-style: italic;
+}
+
+/* NAVIGATION BAR STUFF */
+
+
+td.doctitle {
+ height:50px;
+}
+td.doctitle p {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ padding: 0px 5px 0px 15px;
+ font-weight: bold;
+ color: #666666;
+}
+td.doctitle p a {
+ color: #666666;
+ text-decoration: none;
+}
+
+td.doctitle p a:hover {
+ color: #cc0000;
+ text-decoration: underline;
+}
+
+form.apisearch {
+ padding: 0px;
+ padding-top: 5px;
+ margin: 0px;
+}
+form.apisearch input {
+ width: 120px;
+ font-size: 12px;
+}
+
+.doctitle .heading {
+ margin-bottom: 1px;
+ color: #333333;
+ font-size: 24px;
+ font-weight:bold;
+ margin-top: 5px;
+ margin-left: 15px;
+}
+
+.doctitle .text {
+ padding: 0px 5px 0px 10px;
+ font-size: 12px;
+ font-weight: bold;
+ color: #333333;
+}
+
+.titlebar {
+ width: 100%;
+ border-width:0px;
+ margin-bottom:20px;
+ padding: 0px;
+ border-collapse: collapse;
+}
+
+.navbuttons {
+ text-align:right;
+ width:194px;
+ padding: 0px;
+ white-space: nowrap;
+}
+
+.variable {
+ font-style: italic;
+}
+.parameter {
+ font-style: italic;
+}
+
+
+ul.left-menu {
+ position: absolute;
+ left: 5px;
+ top: 50px;
+
+ width: 139px;
+ padding: 0px 0px 0px 20px;
+ margin: 0px;
+
+ list-style-type: none;
+}
+.left-menu a {
+ text-decoration: none;
+ white-space: nowrap;
+}
+.left-menu-item, .left-menu-item a {
+ color: #900000;
+}
+.left-menu-inherited-item, .left-menu-inherited-item a {
+ color: #900000;
+ font-style: oblique;
+}
+
+.left-menu-seperator {
+ height: 10px;
+ display: block;
+}
+.left-menu-heading {
+ padding-top: 0px;
+ font-weight: bold;
+ margin-left: -18px;
+ display: block;
+}
+.left-menu-subheading {
+ padding-top: 4px;
+ font-weight: bold;
+ border-bottom: 1px solid #c9c9c9;
+ display: block;
+}
+.left-menu-softheading {
+ padding-top: 4px;
+ border-bottom: 1px solid #c9c9c9;
+ display: block;
+ color: #666666;
+ font-style: italic;
+}
+.left-menu-seperator, .left-menu-heading, a.left-menu-heading {
+ color:black;
+}
+
+.left-menu-current {
+ color: #cc0000;
+ font-weight: bold;
+ white-space: nowrap;
+}
+
+.pre-next-link a {
+ width: 180px;
+ display: inline-block;
+ white-space: nowrap;
+ margin-bottom: -3px;
+ text-decoration: none;
+}
+div.pre-next-link {
+ margin-bottom: 10px;
+}
+
+a:hover {
+ color: #cc0000;
+ text-decoration: underline;
+} \ No newline at end of file
diff --git a/Runtime/File/ApplicationSpecificPersistentDataPath.cpp b/Runtime/File/ApplicationSpecificPersistentDataPath.cpp
new file mode 100644
index 0000000..5ec0255
--- /dev/null
+++ b/Runtime/File/ApplicationSpecificPersistentDataPath.cpp
@@ -0,0 +1,171 @@
+#include "UnityPrefix.h"
+#include "Runtime/Misc/Player.h"
+#include "Runtime/Misc/PlayerSettings.h"
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/Utilities/FileUtilities.h"
+
+#include <ctype.h>
+
+std::string GetAppDataPath()
+{
+#if UNITY_EDITOR
+ return AppendPathName (File::GetCurrentDirectory (), "Assets");
+#elif WEBPLUG || UNITY_PEPPER || UNITY_FLASH || UNITY_WEBGL
+ std::string url = GetPlayerSettings().absoluteURL;
+ size_t param = url.find('?');
+ return DeleteLastPathNameComponent(url.substr(0, param));
+#elif UNITY_WIN
+ return SelectDataFolder ();
+#elif UNITY_OSX
+ return AppendPathName (GetApplicationPath (), "Contents");
+#elif UNITY_WII
+ return AppendPathName (GetApplicationFolder (), "/Data");
+#elif UNITY_PS3
+ return GetApplicationFolder();
+#elif UNITY_XENON
+ return "game:\\Media";
+#elif UNITY_IPHONE
+ return AppendPathName (GetApplicationFolder (), "Data");
+#elif UNITY_ANDROID
+ return GetApplicationPath(); // full path to the .apk
+#elif UNITY_BB10
+ return AppendPathName (GetApplicationFolder (), "app/native/Data");
+#elif UNITY_TIZEN
+ return AppendPathName (GetApplicationFolder (), "data");
+#elif UNITY_LINUX
+ return SelectDataFolder ();
+#else
+#error "Unknown platform"
+#endif
+}
+
+
+
+#if SUPPORT_DIRECT_FILE_ACCESS
+
+// We fucked this one up badly. In 3.4, this function would incorrectly strip
+// illegal characters from the file name, by changing the wrong indices.
+// Now this a) does not solve the problem of illegal path names, and
+// b) looks stupid. But, by fixing it, we'd lose all data cached by previous
+// versions. So, what we do instead, is first check for the presence of
+// a broken file name, and use that if it exists, and use the correct one otherwise.
+// S.A. CachingManager.cpp.
+void ConvertToLegalPathNameCorrectly(std::string& path)
+{
+ for (size_t i = path.size(); i > 0; --i)
+ {
+ char c = path[i-1];
+ if (isalnum(c) || isspace(c))
+ continue;
+ path[i-1] = '_';
+ }
+}
+
+void ConvertToLegalPathNameBroken(std::string& path)
+{
+ size_t size = path.size();
+ for (size_t i = path.size(); i > 0; --i)
+ {
+ char c = path[i-1];
+ if (isalnum(c) || isspace(c))
+ continue;
+ if (i < size)
+ path[i] = '_';
+ }
+}
+
+std::string GetApplicationSpecificDataPathAppendix(bool broken)
+{
+ if (UNITY_EMULATE_PERSISTENT_DATAPATH)
+ {
+ std::string companyName = GetPlayerSettings().companyName;
+ std::string productName = GetPlayerSettings().productName;
+ if (broken)
+ {
+ ConvertToLegalPathNameBroken(companyName);
+ ConvertToLegalPathNameBroken(productName);
+ }
+ else
+ {
+#if UNITY_OSX && !UNITY_EDITOR
+ // In the OS X standalone, return the bundle ID to match App Store requirements.
+ return CFStringToString(CFBundleGetIdentifier(CFBundleGetMainBundle()));
+#endif
+ ConvertToLegalPathNameCorrectly(companyName);
+ ConvertToLegalPathNameCorrectly(productName);
+ }
+ return AppendPathName(companyName, productName);
+ }
+ return "";
+}
+
+std::string GetPersistentDataPathApplicationSpecific()
+{
+ std::string dataPath = systeminfo::GetPersistentDataPath();
+ #if !UNITY_WINRT
+ if (dataPath.empty())
+ return string();
+ std::string brokenPath = AppendPathName(dataPath, GetApplicationSpecificDataPathAppendix(true));
+ if (IsDirectoryCreated (brokenPath))
+ dataPath = brokenPath;
+ else
+ dataPath = AppendPathName(dataPath, GetApplicationSpecificDataPathAppendix(false));
+
+ if (!CreateDirectoryRecursive(dataPath))
+ return string();
+ #endif
+ return dataPath;
+}
+
+
+std::string GetTemporaryCachePathApplicationSpecific()
+{
+ std::string cachePath = systeminfo::GetTemporaryCachePath();
+ #if !UNITY_WINRT
+ if (cachePath.empty())
+ return string();
+ std::string brokenPath = AppendPathName(cachePath, GetApplicationSpecificDataPathAppendix(true));
+ if (IsDirectoryCreated (brokenPath))
+ cachePath = brokenPath;
+ else
+ cachePath = AppendPathName(cachePath, GetApplicationSpecificDataPathAppendix(false));
+ if (!CreateDirectoryRecursive(cachePath))
+ return string();
+ #endif
+ return cachePath;
+}
+
+std::string GetStreamingAssetsPath()
+{
+#if (UNITY_EDITOR || UNITY_WIN || UNITY_WII || UNITY_LINUX) && !WEBPLUG
+ return AppendPathName (GetAppDataPath(), "StreamingAssets");
+#elif UNITY_OSX && !WEBPLUG
+ return AppendPathName (SelectDataFolder(), "StreamingAssets");
+#elif UNITY_IPHONE || UNITY_XENON || UNITY_PS3 || UNITY_BB10 || UNITY_TIZEN
+ return AppendPathName (GetAppDataPath() , "Raw");
+#elif UNITY_ANDROID
+ return "jar:file://" + GetAppDataPath() + "!/assets";
+#else
+ ErrorString ("StreamingAssets is not available on this platform.");
+ return "";
+#endif
+}
+
+#else
+
+std::string GetPersistentDataPathApplicationSpecific()
+{
+ return "";
+}
+
+std::string GetTemporaryCachePathApplicationSpecific()
+{
+ return "";
+}
+
+std::string GetStreamingAssetsPath()
+{
+ return "";
+}
+#endif
diff --git a/Runtime/File/ApplicationSpecificPersistentDataPath.h b/Runtime/File/ApplicationSpecificPersistentDataPath.h
new file mode 100644
index 0000000..cd251e8
--- /dev/null
+++ b/Runtime/File/ApplicationSpecificPersistentDataPath.h
@@ -0,0 +1,9 @@
+#pragma once
+
+std::string GetPersistentDataPathApplicationSpecific();
+std::string GetTemporaryCachePathApplicationSpecific();
+std::string GetAppDataPath();
+std::string GetStreamingAssetsPath();
+
+void ConvertToLegalPathNameCorrectly(std::string& path);
+void ConvertToLegalPathNameBroken(std::string& path); \ No newline at end of file
diff --git a/Runtime/Filters/AABBUtility.cpp b/Runtime/Filters/AABBUtility.cpp
new file mode 100644
index 0000000..cf7eccf
--- /dev/null
+++ b/Runtime/Filters/AABBUtility.cpp
@@ -0,0 +1,136 @@
+#include "UnityPrefix.h"
+#include "AABBUtility.h"
+#include "Renderer.h"
+#include "Runtime/Filters/Mesh/LodMeshFilter.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Filters/Mesh/SpriteRenderer.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Filters/Deformation/SkinnedMeshFilter.h"
+#include "Runtime/BaseClasses/GameObject.h"
+
+inline bool HasAABB (Renderer& renderer)
+{
+ return renderer.IsActive () && renderer.GetVisible ();
+}
+
+bool CalculateWorldAABB (GameObject& go, AABB* aabb)
+{
+ Renderer* renderer = go.QueryComponent (Renderer);
+ if (renderer && HasAABB (*renderer))
+ {
+ renderer->GetWorldAABB ( *aabb );
+ return true;
+ }
+
+ MeshFilter *lmf = go.QueryComponent (MeshFilter);
+ if (lmf)
+ {
+ Mesh *lm = lmf->GetSharedMesh ();
+ if (lm)
+ {
+ *aabb = lm->GetBounds ();
+ Matrix4x4f matrix;
+ go.GetComponent (Transform).CalculateTransformMatrix (matrix);
+ TransformAABB (*aabb, matrix, *aabb);
+ return true;
+ }
+ }
+
+#if ENABLE_SPRITES
+ SpriteRenderer* sprite = go.QueryComponent (SpriteRenderer);
+ if (sprite)
+ {
+ Sprite* frame = sprite->GetSprite();
+ if (frame)
+ {
+ *aabb = frame->GetBounds();
+ Matrix4x4f matrix;
+ go.GetComponent (Transform).CalculateTransformMatrix (matrix);
+ TransformAABB (*aabb, matrix, *aabb);
+ return true;
+ }
+ }
+#endif
+
+ aabb->SetCenterAndExtent( Vector3f::zero, Vector3f::zero );
+ return false;
+}
+
+bool CalculateLocalAABB (GameObject& go, AABB* aabb)
+{
+ Renderer* renderer = go.QueryComponent (Renderer);
+ if (renderer && HasAABB (*renderer))
+ {
+ const TransformInfo& info = renderer->GetTransformInfo();
+
+ Matrix4x4f transformWorldToLocal = renderer->GetComponent(Transform).GetWorldToLocalMatrix();
+ Matrix4x4f rendererLocalToTransformLocal;
+ MultiplyMatrices4x4(&transformWorldToLocal, &info.worldMatrix, &rendererLocalToTransformLocal);
+
+ TransformAABB(info.localAABB, rendererLocalToTransformLocal, *aabb);
+
+ return true;
+ }
+
+ MeshFilter *lmf = go.QueryComponent (MeshFilter);
+ if (lmf)
+ {
+ Mesh *lm = lmf->GetSharedMesh ();
+ if (lm)
+ {
+ *aabb = lm->GetBounds ();
+ return true;
+ }
+ }
+
+ aabb->SetCenterAndExtent( Vector3f::zero, Vector3f::zero );
+ return false;
+}
+
+bool CalculateAABBCornerVertices (GameObject& go, Vector3f* vertices)
+{
+ Renderer* renderer = go.QueryComponent (Renderer);
+ if (renderer && HasAABB (*renderer))
+ {
+ Transform const& transform = renderer->GetTransform();
+
+ AABB aabb;
+ if (dynamic_pptr_cast<SkinnedMeshRenderer*> (renderer))
+ {
+ renderer->GetWorldAABB( aabb );
+ aabb.GetVertices (vertices);
+ }
+ else
+ {
+ renderer->GetLocalAABB( aabb );
+
+ aabb.GetVertices (vertices);
+ TransformPoints3x4 (transform.GetLocalToWorldMatrix (), vertices, vertices, 8);
+ }
+
+ return true;
+ }
+
+ Transform* transform = go.QueryComponent (Transform);
+ MeshFilter *lmf = go.QueryComponent (MeshFilter);
+ if (transform && lmf)
+ {
+ Mesh *lm = lmf->GetSharedMesh ();
+ if (lm)
+ {
+ lm->GetBounds ().GetVertices (vertices);
+ TransformPoints3x4 (transform->GetLocalToWorldMatrix (), vertices, vertices, 8);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+AABB CalculateWorldAABB (GameObject& go)
+{
+ AABB aabb;
+ CalculateWorldAABB (go, &aabb);
+ return aabb;
+}
diff --git a/Runtime/Filters/AABBUtility.h b/Runtime/Filters/AABBUtility.h
new file mode 100644
index 0000000..a6d01e6
--- /dev/null
+++ b/Runtime/Filters/AABBUtility.h
@@ -0,0 +1,18 @@
+#ifndef AABBUTILITY_H
+#define AABBUTILITY_H
+
+#include "Runtime/Geometry/AABB.h"
+
+class Transform;
+class Vector3f;
+namespace Unity { class GameObject; }
+
+bool EXPORT_COREMODULE CalculateLocalAABB (Unity::GameObject& go, AABB* aabb);
+
+AABB CalculateWorldAABB (Unity::GameObject& go);
+bool CalculateWorldAABB (Unity::GameObject& go, AABB* aabb);
+
+bool CalculateAABBSkinned (Transform& transform, AABB& aabb);
+bool CalculateAABBCornerVertices (Unity::GameObject& go, Vector3f* vertices);
+
+#endif
diff --git a/Runtime/Filters/Deformation/BlendShapeAnimationBinding.cpp b/Runtime/Filters/Deformation/BlendShapeAnimationBinding.cpp
new file mode 100644
index 0000000..484297f
--- /dev/null
+++ b/Runtime/Filters/Deformation/BlendShapeAnimationBinding.cpp
@@ -0,0 +1,141 @@
+#include "UnityPrefix.h"
+#include "Runtime/Animation/GenericAnimationBindingCache.h"
+#include "Runtime/Animation/BoundCurve.h"
+#include "SkinnedMeshFilter.h"
+#include "Runtime/Filters/Mesh/MeshBlendShape.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Interfaces/IAnimationBinding.h"
+#include "Runtime/mecanim/generic/crc32.h"
+
+const char* kBlendShapePrefix = "blendShape.";
+const char* kBlendShapeSerializedPropertyPath = "m_BlendShapeWeights.Array.data[";
+
+static int BlendShapeNameToIndex (SkinnedMeshRenderer& renderer, BindingHash hash)
+{
+ const Mesh* mesh = renderer.GetMesh();
+ if (mesh == NULL)
+ return -1;
+
+ const BlendShapeData& blendShapes = mesh->GetBlendShapeData();
+ int index = GetChannelIndex (blendShapes, hash);
+
+ return index;
+}
+
+static std::string BlendShapeIndexToName (Object& targetObject, int index)
+{
+ SkinnedMeshRenderer* renderer = dynamic_pptr_cast<SkinnedMeshRenderer*> (&targetObject);
+
+ if (renderer == NULL)
+ return std::string();
+ const Mesh* mesh = renderer->GetMesh();
+ if (mesh == NULL)
+ return std::string();
+
+ const BlendShapeData& blendShapeData = mesh->GetBlendShapeData();
+ if (index < mesh->GetBlendShapeChannelCount ())
+ return std::string(kBlendShapePrefix) + GetChannelName (blendShapeData, index);
+ else
+ return std::string();
+}
+
+static bool CalculateBlendShapeHash (const char* name, BindingHash& hash)
+{
+ // Special case support for blendshape weights. We have to call a function.
+ const char* shapeName = ParsePrefixedName (name, kBlendShapePrefix);
+ if (shapeName == NULL)
+ return false;
+
+ hash = mecanim::processCRC32(shapeName);
+ return true;
+}
+
+class BlendshapePropertyBinding : public IAnimationBinding
+{
+#if UNITY_EDITOR
+ virtual void GetAllAnimatableProperties (Object& targetObject, std::vector<EditorCurveBinding>& outProperties) const
+ {
+ SkinnedMeshRenderer& renderer = static_cast<SkinnedMeshRenderer&> (targetObject);
+ Mesh* mesh = renderer.GetMesh();
+ if (mesh == NULL)
+ return;
+
+ for (int i = 0; i < mesh->GetBlendShapeChannelCount (); ++i)
+ AddBinding (outProperties, ClassID(SkinnedMeshRenderer), BlendShapeIndexToName (targetObject, i));
+ }
+#endif
+
+ virtual float GetFloatValue (const UnityEngine::Animation::BoundCurve& bind) const
+ {
+ SkinnedMeshRenderer* renderer = reinterpret_cast<SkinnedMeshRenderer*>(bind.targetObject);
+ const int shapeIndex = reinterpret_cast<int> (bind.targetPtr);
+ return renderer->GetBlendShapeWeight(shapeIndex);
+ }
+
+ virtual void SetFloatValue (const UnityEngine::Animation::BoundCurve& bind, float value) const
+ {
+ SkinnedMeshRenderer* renderer = reinterpret_cast<SkinnedMeshRenderer*>(bind.targetObject);
+
+ const int shapeIndex = reinterpret_cast<int> (bind.targetPtr);
+ renderer->SetBlendShapeWeight(shapeIndex, value);
+ }
+
+ virtual void SetPPtrValue (const UnityEngine::Animation::BoundCurve& bound, SInt32 value) const { }
+
+ virtual SInt32 GetPPtrValue (const UnityEngine::Animation::BoundCurve& bound) const { return 0;}
+
+ virtual bool GenerateBinding (const UnityStr& attribute, bool pptrCurve, UnityEngine::Animation::GenericBinding& outputBinding) const
+ {
+ BindingHash hash;
+ if (pptrCurve || !CalculateBlendShapeHash (attribute.c_str(), hash))
+ return false;
+
+ outputBinding.attribute = hash;
+ return true;
+ }
+
+ virtual ClassIDType BindValue (Object& target, const UnityEngine::Animation::GenericBinding& inputBinding, UnityEngine::Animation::BoundCurve& bound) const
+ {
+ int index = BlendShapeNameToIndex (static_cast<SkinnedMeshRenderer&> (target), inputBinding.attribute);
+ if (index == -1)
+ return ClassID(Undefined);
+
+ bound.targetPtr = (void*)index;
+ return ClassID(float);
+ }
+
+ virtual std::string SerializedPropertyPathToCurveAttribute (Object& target, const char* propertyPath) const
+ {
+ Assert (target.GetClassID () == ClassID(SkinnedMeshRenderer));
+
+ if (BeginsWith (propertyPath, kBlendShapeSerializedPropertyPath))
+ {
+ int index = StringToInt (propertyPath + strlen(kBlendShapeSerializedPropertyPath));
+ return BlendShapeIndexToName (target, index);
+ }
+ else
+ return string();
+ }
+
+ virtual std::string CurveAttributeToSerializedPath (const UnityEngine::Animation::BoundCurve& bound) const
+ {
+ const int shapeIndex = reinterpret_cast<int> (bound.targetPtr);
+ Assert (shapeIndex != -1);
+ return kBlendShapeSerializedPropertyPath + IntToString(shapeIndex) + ']';
+ }
+
+};
+
+static BlendshapePropertyBinding* gBinding = NULL;
+
+void InitializeBlendShapeAnimationBindingInterface ()
+{
+ gBinding = UNITY_NEW (BlendshapePropertyBinding, kMemAnimation);
+ UnityEngine::Animation::GetGenericAnimationBindingCache ().RegisterIAnimationBinding (ClassID(SkinnedMeshRenderer), UnityEngine::Animation::kBlendShapeWeightBinding, gBinding);
+}
+
+void CleanupBlendShapeAnimationBindingInterface ()
+{
+ UNITY_DELETE (gBinding, kMemAnimation);
+}
+
diff --git a/Runtime/Filters/Deformation/BlendShapeAnimationBinding.h b/Runtime/Filters/Deformation/BlendShapeAnimationBinding.h
new file mode 100644
index 0000000..7857b28
--- /dev/null
+++ b/Runtime/Filters/Deformation/BlendShapeAnimationBinding.h
@@ -0,0 +1,4 @@
+#pragma once
+
+void InitializeBlendShapeAnimationBindingInterface ();
+void CleanupBlendShapeAnimationBindingInterface (); \ No newline at end of file
diff --git a/Runtime/Filters/Deformation/SkinnedMeshFilter.cpp b/Runtime/Filters/Deformation/SkinnedMeshFilter.cpp
new file mode 100644
index 0000000..a6ad95e
--- /dev/null
+++ b/Runtime/Filters/Deformation/SkinnedMeshFilter.cpp
@@ -0,0 +1,1535 @@
+#include "UnityPrefix.h"
+#include "SkinnedMeshFilter.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "Runtime/Filters/Mesh/MeshSkinning.h"
+#include "Runtime/Animation/Animation.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/QualitySettings.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Filters/Mesh/MeshRenderer.h"
+#include "Runtime/Filters/Mesh/MeshBlendShaping.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Filters/Mesh/MeshUtility.h"
+#include "Runtime/Graphics/DrawUtil.h"
+#include "Runtime/GameCode/CallDelayed.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/BaseClasses/SupportedMessageOptimization.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Threads/JobScheduler.h"
+#include "Runtime/Dynamics/SkinnedCloth.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/BaseClasses/CleanupManager.h"
+#include "Runtime/BaseClasses/EventIDs.h"
+#include "Runtime/Misc/PlayerSettings.h"
+#include "Runtime/Interfaces/IPhysics.h"
+#include "Runtime/Interfaces/IAnimation.h"
+#include "BlendShapeAnimationBinding.h"
+#if UNITY_PS3
+#include "Runtime/GfxDevice/ps3/GfxGCMVBO.h"
+#include "Runtime/Filters/Mesh/MeshPartitioner.h"
+#endif
+
+PROFILER_INFORMATION(gMeshSkinningUpdate, "MeshSkinning.Update", kProfilerRender)
+PROFILER_INFORMATION(gMeshSkinningUpdateImmediate, "MeshSkinning.UpdateImmediate", kProfilerRender)
+PROFILER_INFORMATION(gMeshSkinningPrepare, "MeshSkinning.Prepare", kProfilerRender)
+PROFILER_INFORMATION(gMeshSkinningRender, "MeshSkinning.Render", kProfilerRender)
+PROFILER_INFORMATION(gMeshSkinningWait, "MeshSkinning.WaitForSkinThreads", kProfilerRender)
+PROFILER_INFORMATION(gMeshSkinningSkinGPU, "MeshSkinning.SkinOnGPU", kProfilerRender)
+
+
+#if UNITY_EDITOR
+#define SET_CACHED_SURFACE_AREA_DIRTY() m_CachedSurfaceArea = -1.0f;
+#else
+#define SET_CACHED_SURFACE_AREA_DIRTY() // do nothing
+#endif
+
+typedef List< ListNode<SkinnedMeshRenderer> > SkinnedMeshList;
+static SkinnedMeshList gActiveSkinnedMeshes;
+
+/*
+JOE:
+ * TODO: Do we really need this. -> Test cloth
+
+MIRCEA:
+
+ * PS3 version for fallback skinning ( so mesh particle emitters & skinned cloth still works )
+
+*/
+
+SkinnedMeshRenderer::SkinnedMeshRenderer (MemLabelId label, ObjectCreationMode mode)
+: Super(kRendererSkinnedMesh, label, mode)
+, m_BlendShapeWeights(0, label)
+, m_CachedAnimator(NULL)
+, m_CachedBlendShapeCount (0)
+, m_SkinNode(this)
+, m_MeshNode(this)
+, m_AABB(Vector3f::zero, Vector3f::zero)
+, m_MemExportInfo(0)
+{
+ m_Visible = false;
+ m_UpdateBeforeRendering = false;
+ m_SourceMeshDirty = false;
+ m_DirtyAABB = true;
+ m_CachedMesh = NULL;
+ m_ChannelsInVBO = 0;
+ m_Cloth = NULL;
+ m_VBO = NULL;
+ SET_CACHED_SURFACE_AREA_DIRTY();
+}
+
+SkinnedMeshRenderer::~SkinnedMeshRenderer ()
+{
+ Assert(m_CachedAnimator == NULL);
+
+ if(m_MemExportInfo)
+ GetGfxDevice().DeleteGPUSkinningInfo(m_MemExportInfo);
+
+ if (m_VBO)
+ {
+ GetGfxDevice().DeleteVBO(m_VBO);
+ m_VBO = NULL;
+ }
+}
+
+void SkinnedMeshRenderer::Setup (Mesh* mesh, const dynamic_array<PPtr<Transform> >& state)
+{
+ m_Bones = state;
+ m_Mesh = mesh;
+ UpdateCachedMesh ();
+ SetDirty();
+}
+
+void SkinnedMeshRenderer::SetMesh (Mesh* mesh)
+{
+ m_Mesh = mesh;
+ UpdateCachedMesh ();
+ SetDirty();
+}
+
+Mesh* SkinnedMeshRenderer::GetMesh ()
+{
+ return m_Mesh;
+}
+
+void SkinnedMeshRenderer::SetBones (const dynamic_array<PPtr<Transform> >& bones)
+{
+ m_Bones = bones;
+ SetDirty();
+ if (!bones.empty())
+ ClearCachedAnimatorBinding(); // switch to non-optimized mode, no binding is needed anymore
+}
+
+void SkinnedMeshRenderer::Reset()
+{
+ Super::Reset();
+ m_Quality = 0;
+ m_UpdateWhenOffscreen = false;
+ m_AABB = AABB(Vector3f::zero, Vector3f::zero);
+}
+
+
+bool SkinnedMeshRenderer::DoesQualifyForMemExport() const
+{
+ bool qualifies = (m_Cloth == 0) && (m_MemExportInfo);
+ qualifies = qualifies && GetPlayerSettings().GetGPUSkinning();
+
+ return qualifies;
+}
+
+bool SkinnedMeshRenderer::CalculateBoneBasedBounds (const Matrix4x4f* poseMatrices, size_t size, MinMaxAABB& output)
+{
+ if (m_CachedMesh == NULL)
+ return false;
+
+ if (m_CachedMesh->GetMaxBoneIndex() >= size)
+ return false;
+
+ const Mesh::AABBContainer& bounds = m_CachedMesh->GetCachedBonesBounds ();
+ if (size > bounds.size())
+ return false;
+
+ MinMaxAABB minMaxAABB;
+ for(int i=0;i<size;i++)
+ {
+ AABB result;
+
+ ///@TODO: OPTIMIZATION: DO this precomputed.
+ if (!bounds[i].IsValid())
+ continue;
+
+ AABB aabb = bounds[i];
+
+ TransformAABB(aabb, poseMatrices[i], result);
+ // TransformAABBSlow (aabb, animatedPose[i], result);
+
+ minMaxAABB.Encapsulate(result.GetMin());
+ minMaxAABB.Encapsulate(result.GetMax());
+ }
+
+ output = minMaxAABB;
+ return true;
+}
+
+#if UNITY_EDITOR
+bool SkinnedMeshRenderer::CalculateVertexBasedBounds (const Matrix4x4f* poseMatrices, MinMaxAABB& output)
+{
+ if (m_CachedMesh == NULL)
+ return false;
+
+ int boneCount = m_CachedMesh->GetBindposeCount ();
+
+ Matrix4x4f* fullMatrices = NULL;
+ ALLOC_TEMP(fullMatrices, Matrix4x4f, boneCount);
+
+ const Matrix4x4f* bindPoses = m_CachedMesh->GetBindposes();
+ for (int i=0;i<boneCount;i++)
+ {
+ MultiplyMatrices4x4(&poseMatrices[i], &bindPoses[i], &fullMatrices[i]);
+ }
+
+ MinMaxAABB minMaxAABB;
+
+ // could actually use only the vertices which are affected by bones which are actually animated
+ StrideIterator<Vector3f> vertices = m_CachedMesh->GetVertexBegin();
+ const BoneInfluence* boneInfluences = m_CachedMesh->GetBoneWeights ();
+ int count = m_CachedMesh->GetVertexCount();
+ if (count == 0)
+ minMaxAABB.Encapsulate(Vector3f::zero);
+ for (int i = 0; i < count; ++i, ++vertices)
+ {
+ const BoneInfluence& boneInfluence = boneInfluences[i];
+ Vector3f v(Vector3f::zero);
+
+ float w = 0;
+
+ // This always calculates bounding box as if it was 4 bone skinning
+ // 2 bone or 1 bone skinning could produce slightly different results
+ for (int j = 0; j < 4; ++j)
+ {
+ const int boneIndex = boneInfluence.boneIndex[j];
+ Assert(boneIndex < boneCount);
+
+ v += fullMatrices[boneIndex].MultiplyPoint3(*vertices) * boneInfluence.weight[j];
+
+ w += boneInfluence.weight[j];
+ }
+
+ Assert(w > 0.99f && w < 1.01f);
+
+ minMaxAABB.Encapsulate(v);
+ }
+
+ output = minMaxAABB;
+
+ return true;
+}
+#endif
+
+bool SkinnedMeshRenderer::CalculateAnimatedPosesWithRoot (const Matrix4x4f& rootMatrix, Matrix4x4f* poses, size_t size)
+{
+ if (!CalculateAnimatedPoses(poses, size))
+ return false;
+
+ for (int i=0;i<size;i++)
+ {
+ Matrix4x4f temp;
+ MultiplyMatrices4x4(&rootMatrix, poses + i, &temp);
+ poses[i] = temp;
+ }
+ return true;
+}
+
+bool SkinnedMeshRenderer::CalculateSkinningMatrices (const Matrix4x4f& rootPose, Matrix4x4f* skinnedPoses, size_t size)
+{
+ Assert(m_CachedMesh != NULL);
+ DebugAssert (size > 0);
+
+ if (!CalculateAnimatedPoses(skinnedPoses, size))
+ return false;
+
+ MultiplyMatrixArrayWithBase4x4 (&rootPose, skinnedPoses, m_CachedMesh->GetBindposes(), skinnedPoses, size);
+
+ return true;
+}
+
+bool SkinnedMeshRenderer::CalculateAnimatedPoses (Matrix4x4f* poses, size_t size)
+{
+ if (IsOptimized())
+ {
+ const dynamic_array<UInt16>& skeletonIndices = GetSkeletonIndices();
+ if (!skeletonIndices.empty()) // implies: GetAnimator() && GetAnimationInterface()
+ return GetAnimationInterface()->CalculateWorldSpaceMatricesMainThread(*GetAnimator(), skeletonIndices.begin(), size, poses);
+ else
+ return false;
+ }
+ else
+ {
+ if (size > m_Bones.size())
+ return false;
+
+ bool hasAnyBones = false;
+ for (int i=0;i<size;i++)
+ {
+ Transform* transform = m_Bones[i];
+ if (transform)
+ {
+ poses[i] = transform->GetLocalToWorldMatrix ();
+ hasAnyBones = true;
+ }
+ else
+ poses[i].SetIdentity ();
+ }
+
+ return hasAnyBones;
+ }
+}
+
+const dynamic_array<UInt16>& SkinnedMeshRenderer::GetSkeletonIndices()
+{
+ Assert(IsOptimized());
+
+ if (m_SkeletonIndices.empty())
+ CreateCachedAnimatorBinding ();
+
+
+ return m_SkeletonIndices;
+}
+
+int SkinnedMeshRenderer::GetBindposeCount () const
+{
+ return m_CachedMesh ? m_CachedMesh->GetBindposeCount() : 0;
+}
+
+bool SkinnedMeshRenderer::PrepareVBO(bool hasSkin, bool hasBlendshape, bool doMemExport, int flags)
+{
+ if (!hasSkin && !hasBlendshape)
+ return false;
+
+ // Right now, it can be false for 2 cases:
+ // 1. GetSkinnedVerticesAndNormal
+ // In this case, SF_ReadbackBack will also be set.
+ // SkinMeshInfo.outVertices will be allocated in SkinnedMeshRenderer.m_SkinnedVertices.
+ // 2. Bake mesh
+ // In this case, SkinMeshInfo.outVertices will be the vertex data of the output Mesh.
+ bool doNeedVBO = !(flags & SF_NoUpdateVBO);
+ bool newVBO = false;
+
+ if (hasBlendshape)
+ {
+ // Handle cases:
+ // hasSkin==true, hasBlendshape==true or
+ // hasSkin==false, hasBlendshape==true
+ //
+ // Hardware skinning is not applicable
+ if (!m_VBO && doNeedVBO)
+ {
+ m_VBO = GetGfxDevice().CreateVBO();
+ m_VBO->SetVertexStreamMode(0, VBO::kStreamModeDynamic);
+ m_VBO->SetIndicesDynamic(false);
+ newVBO = true;
+ }
+ }
+ else
+ {
+ // Handle case: hasSkin==ture, hasBlendshape==false
+ bool canMapVBO = m_VBO && (m_VBO->GetVertexStreamMode(0) != VBO::kStreamModeNoAccess);
+
+ // Check if mem-export qualification changes. See SkinnedMeshRenderer::PrepareSkinXenon
+ // Re-create VBO if it changes.
+ if (m_VBO && (doMemExport == canMapVBO))
+ {
+ GetGfxDevice().DeleteVBO(m_VBO);
+ m_VBO = NULL;
+ if(m_MemExportInfo)
+ m_MemExportInfo->SetDestVBO(NULL);
+ }
+
+ if (!m_VBO && doNeedVBO)
+ {
+ m_VBO = GetGfxDevice().CreateVBO();
+ if (!doMemExport)
+ {
+ m_VBO->SetVertexStreamMode(0, VBO::kStreamModeDynamic);
+ m_VBO->SetIndicesDynamic(false);
+ }
+ else
+ m_VBO->UseAsStreamOutput();
+ newVBO = true;
+ }
+ }
+
+ if (doMemExport)
+ {
+ // Rebuild the VBO in case mem-export qualification is gained. This could use cleaning up.
+ // OR if source is dirty.
+ if (newVBO || (doNeedVBO && m_SourceMeshDirty))
+ m_CachedMesh->CopyToVBO(m_ChannelsInVBO, *m_VBO);
+ return true;
+ }
+
+ if (newVBO || (doNeedVBO && (m_SourceMeshDirty || m_VBO->IsVertexBufferLost())))
+ {
+ // fill the VBO
+ m_VBO->SetMappedFromRenderThread(!m_Cloth);
+ m_CachedMesh->CopyToVBO(m_ChannelsInVBO, *m_VBO);
+ m_SourceMeshDirty = false;
+ }
+
+ return true;
+}
+
+
+bool SkinnedMeshRenderer::PrepareSkinCommon(UInt32 requiredChannels, int flags, SkinMeshInfo& skin, CalculateSkinMatricesTask* calcSkinMatricesTask)
+{
+ m_UpdateBeforeRendering = false;
+ if (!m_CachedMesh || (m_CachedMesh->GetSubMeshCount () == 0))
+ return false;
+ DebugAssertIf(m_CachedMesh != m_Mesh);
+
+ int bindposeCount = GetBindposeCount();
+ int blendShapeCount = GetValidBlendShapeWeightCount ();
+
+ if (bindposeCount > 0 && m_CachedMesh->GetMaxBoneIndex() >= bindposeCount)
+ {
+ ErrorStringObject("Bone influences do not match bones.", this);
+ return false;
+ }
+
+ // We are not monitoring whether the bones have changed
+ // so we always have to make cached surface area dirty,
+ // as it could have changed.
+ SET_CACHED_SURFACE_AREA_DIRTY();
+
+ bool hasSkin = (bindposeCount > 0) && !m_CachedMesh->GetSkin().empty();
+
+ // For skinned meshes we only care about active blend shapes.
+ // For non-skinned meshes we need to take the blend shape code path
+ // even if all the shapes are zero weight. There is no optimized path
+ // to render the undeformed source mesh (case 557165).
+ // TODO: Write an optimized path for no skin, no active blend shapes.
+ bool hasBlendshape = false;
+ if (hasSkin)
+ hasBlendshape = blendShapeCount > 0;
+ else
+ hasBlendshape = m_CachedBlendShapeCount > 0;
+
+ bool doMemExport = DoesQualifyForMemExport() &&
+ (flags & SF_AllowMemExport) && hasSkin && !hasBlendshape;
+
+ m_CachedMesh->InitVertexBufferData(requiredChannels);
+ m_ChannelsInVBO = m_CachedMesh->GetAvailableChannels();
+
+ if (!PrepareVBO(hasSkin, hasBlendshape, doMemExport, flags))
+ return false;
+
+ // Fill SkinMeshInfo
+ skin.boneCount = bindposeCount;
+ skin.blendshapeCount = blendShapeCount;
+ skin.vertexCount = m_CachedMesh->GetVertexCount();
+ skin.memExport = doMemExport;
+ skin.Allocate();
+
+ if (hasSkin)
+ {
+ skin.bonesPerVertex = GetBonesPerVertexCount();
+ skin.compactSkin = m_CachedMesh->GetSkinInfluence(skin.bonesPerVertex);
+
+ Matrix4x4f rootPose;
+ if (!(flags & SF_ClothPlaying))
+ rootPose = GetActualRootBone().GetWorldToLocalMatrixNoScale ();
+ else
+ // clothed skins are simulated using world space rotation, so rotating the character will affect the cloth simulation.
+ // translation is applied using forces in the cloth, which is smoother.
+ rootPose.SetTranslate (-GetActualRootBone().GetPosition());
+
+ bool canCalcSkinMatricesInMT = false;
+ if (calcSkinMatricesTask && IsOptimized())
+ {
+ const dynamic_array<UInt16>& skeletonIndices = GetSkeletonIndices();
+ if (skeletonIndices.empty())
+ return false;
+ else // implies GetAnimationInterface() && GetAnimator()
+ {
+ const void* skeletonPose = GetAnimationInterface()->GetGlobalSpaceSkeletonPose(*GetAnimator());
+ if (skeletonPose)
+ {
+ canCalcSkinMatricesInMT = true;
+
+ calcSkinMatricesTask->skeletonPose = skeletonPose;
+ calcSkinMatricesTask->skeletonIndices = skeletonIndices.begin();
+ calcSkinMatricesTask->rootPose = rootPose;
+ calcSkinMatricesTask->bindPoseCount = bindposeCount;
+ calcSkinMatricesTask->bindPose = m_CachedMesh->GetBindposes();
+
+ calcSkinMatricesTask->outPose = skin.cachedPose;
+ }
+ }
+ }
+
+ if (!canCalcSkinMatricesInMT)
+ {
+ // slow code path
+ if (!CalculateSkinningMatrices(rootPose, skin.cachedPose, bindposeCount))
+ return false;
+ }
+ }
+ else
+ {
+ skin.cachedPose = NULL;
+ skin.compactSkin = NULL;
+ }
+
+ if (hasBlendshape)
+ {
+ Assert (skin.blendshapeCount <= m_CachedMesh->GetBlendShapeChannelCount());
+ memcpy (skin.blendshapeWeights, m_BlendShapeWeights.begin(), skin.blendshapeCount * sizeof(float));
+ skin.blendshapes = &m_CachedMesh->GetBlendShapeData();
+ }
+
+#if UNITY_PS3
+ if ((NULL == m_Cloth) && (!m_CachedMesh->m_PartitionInfos.empty()))
+ {
+ m_ChannelsInVBO = m_CachedMesh->GetAvailableChannels();
+ return true;
+ }
+#endif
+
+ const VertexData& vertexData = m_CachedMesh->GetVertexData();
+ const StreamInfo streamInfo = vertexData.GetStream(0);
+ skin.inVertices = vertexData.GetDataPtr() + streamInfo.offset;
+ skin.inStride = streamInfo.stride;
+ skin.outStride = streamInfo.stride;
+#if !UNITY_FLASH
+ if (streamInfo.channelMask & ~VERTEX_FORMAT3(Vertex, Normal, Tangent))
+ ErrorString(Format("Skinned mesh stream should contain only positions, normals and tangents. channelMask was %lx", streamInfo.channelMask));
+#endif
+
+ if (skin.memExport)
+ return true;
+
+ // Skin vertices into vbo
+ const ChannelInfo& normalInfo = vertexData.GetChannel(kShaderChannelNormal);
+ const ChannelInfo& tangentInfo = vertexData.GetChannel(kShaderChannelTangent);
+
+#if !UNITY_PS3 // Stream layout for skinned models is different on PS3
+ DebugAssert(!normalInfo.IsValid() || (normalInfo.stream == 0 && normalInfo.format == kChannelFormatFloat && normalInfo.dimension == 3));
+ DebugAssert(!tangentInfo.IsValid() || (tangentInfo.stream == 0 && tangentInfo.format == kChannelFormatFloat && tangentInfo.dimension == 4));
+#endif
+
+ skin.skinNormals = normalInfo.IsValid();
+ skin.normalOffset = normalInfo.offset;
+ skin.skinTangents = tangentInfo.IsValid();
+ skin.tangentOffset = tangentInfo.offset;
+
+ if (flags & SF_ReadbackBuffer)
+ {
+ // Allocate a temporary buffer to read back from
+ m_SkinnedVertices.resize_uninitialized(skin.outStride * skin.vertexCount);
+ skin.outVertices = &m_SkinnedVertices[0];
+ }
+ return true;
+}
+
+#if UNITY_PS3
+bool SkinnedMeshRenderer::PrepareSkinPS3( UInt32 requiredChannels, int flags, SkinMeshInfo& skin, CalculateSkinMatricesTask* calcSkinMatricesTask )
+{
+ if (!m_CachedMesh || m_CachedMesh->GetSkin().empty ())
+ return false;
+
+ skin.vertexData = NULL;
+
+ if (!PrepareSkinCommon( requiredChannels, flags, skin, calcSkinMatricesTask ))
+ return false;
+
+ skin.vertexData = &m_CachedMesh->GetVertexData();
+
+ if(m_CachedMesh->m_PartitionInfos.empty() || m_Cloth)
+ return true;
+
+ if(m_SourceMeshDirty)
+ {
+ VertexBufferData vertexBuffer;
+ IndexBufferData indexBuffer;
+
+ m_CachedMesh->GetVertexBufferData( vertexBuffer, m_ChannelsInVBO );
+ m_CachedMesh->GetIndexBufferData( indexBuffer );
+
+ vertexBuffer.inflPerVertex = skin.bonesPerVertex;
+ vertexBuffer.numInfluences = m_CachedMesh->GetSkin().size();
+ vertexBuffer.influences = skin.compactSkin;
+ vertexBuffer.numBones = skin.boneCount;
+ vertexBuffer.bones = skin.cachedPose;
+
+ m_VBO->UpdateVertexData( vertexBuffer );
+ m_VBO->UpdateIndexData( indexBuffer );
+
+ m_SourceMeshDirty = false;
+ }
+ else
+ ((GfxGCMVBO*)m_VBO)->UpdateBones(skin.boneCount, skin.cachedPose);
+
+ skin.Release();
+ return false;
+}
+#endif
+
+bool SkinnedMeshRenderer::PrepareSkinGPU( UInt32 requiredChannels, int flags, SkinMeshInfo& skin, CalculateSkinMatricesTask* calcSkinMatricesTask )
+{
+ if (!PrepareSkinCommon( requiredChannels, flags, skin, calcSkinMatricesTask ))
+ return false;
+
+ if (skin.memExport)
+ {
+ Assert(m_ChannelsInVBO != 0);
+ Assert(skin.inStride == skin.outStride);
+
+
+ // TODO: Are there any situations where this might change on the fly? In the editor?
+ if(!m_MemExportInfo->GetDestVBO() || m_SourceMeshDirty)
+ {
+ VertexBufferData vertexBuffer;
+ const StreamInfo& skinStream = vertexBuffer.streams[0];
+
+ m_CachedMesh->GetVertexBufferData(vertexBuffer, m_ChannelsInVBO);
+
+ // Source data
+ const size_t dataBufferSize = skinStream.stride * skin.vertexCount;
+ const void* dataBufferPtr = vertexBuffer.buffer + skinStream.offset;
+
+ // Skin
+ Assert(sizeof(BoneInfluence) == 32);
+ const int skinBufferSize = m_CachedMesh->GetVertexCount() * sizeof(BoneInfluence);
+ // const BoneInfluence* skinBufferPtr = m_CachedMesh->GetBoneWeights();
+ void *skinBufferPtr = m_CachedMesh->GetSkinInfluence(skin.bonesPerVertex);
+
+ m_MemExportInfo->SetVertexCount(skin.vertexCount);
+ m_MemExportInfo->SetChannelMap(skinStream.channelMask);
+ m_MemExportInfo->SetStride(skin.outStride);
+ m_MemExportInfo->SetDestVBO(m_VBO);
+ m_MemExportInfo->SetBonesPerVertex(skin.bonesPerVertex);
+
+ GetGfxDevice().UpdateSkinSourceData(m_MemExportInfo, dataBufferPtr, (const BoneInfluence *)skinBufferPtr, m_SourceMeshDirty);
+ }
+
+
+ // Bones (uses 4 weights, ignores quality settings)
+ Assert(skin.boneCount > 0);
+
+ skin.mei = m_MemExportInfo;
+ m_SourceMeshDirty = false;
+ }
+ else
+ {
+ Assert(m_SourceMeshDirty == false);
+ }
+
+ return true;
+}
+
+bool SkinnedMeshRenderer::PrepareSkin( UInt32 requiredChannels, int flags, SkinMeshInfo& skin, CalculateSkinMatricesTask* calcSkinMatricesTask )
+{
+#if UNITY_PS3
+ return PrepareSkinPS3(requiredChannels, flags, skin, calcSkinMatricesTask);
+#else
+ return PrepareSkinGPU(requiredChannels, flags, skin, calcSkinMatricesTask);
+#endif
+}
+
+#if UNITY_EDITOR
+void SkinnedMeshRenderer::UpdateClothDataForEditing(const SkinMeshInfo& skin)
+{
+ // update cloth vertices in edit mode for the cloth vertex editor
+ if (m_Cloth != NULL && !IsWorldPlaying())
+ {
+ dynamic_array<Vector3f> &vertices = m_Cloth->GetVertices();
+ dynamic_array<Vector3f> &normals = m_Cloth->GetNormals();
+ GetSkinnedVerticesAndNormals (&vertices, &normals);
+ }
+}
+#endif
+
+bool SkinnedMeshRenderer::SkinMesh( SkinMeshInfo& skin, bool lastMemExportThisFrame, UInt32 cpuFence, int flags )
+{
+ GfxDevice& device = GetGfxDevice();
+ if (skin.memExport)
+ {
+ GetGfxDevice().UpdateSkinBonePoses(m_MemExportInfo, skin.boneCount, skin.cachedPose);
+ skin.Release();
+
+ // Issue GPU skinning requests in sync. Cloth is done on CPU in parallel.
+ PROFILER_AUTO(gMeshSkinningSkinGPU, this)
+ device.SkinOnGPU(m_MemExportInfo, lastMemExportThisFrame);
+ device.GetFrameStats().AddDrawCall (skin.vertexCount, skin.vertexCount);
+ GPU_TIMESTAMP();
+ return true;
+ }
+ else
+ {
+#if ENABLE_MULTITHREADED_CODE
+ m_CachedMesh->SetCurrentCPUFence(cpuFence);
+#endif
+ return GetGfxDevice().SkinMesh(skin, (flags & SF_ReadbackBuffer) ? NULL : m_VBO);
+ }
+}
+
+bool SkinnedMeshRenderer::SkinMeshImmediate( UInt32 requiredChannels )
+{
+ GfxDevice& device = GetGfxDevice();
+ // Double check there are no fences inserted during skinning
+ UInt32 expectedFence = device.GetNextCPUFence();
+ device.BeginSkinning(1);
+ SkinMeshInfo skin;
+ int flags = SF_AllowMemExport;
+ bool success = PrepareSkin(requiredChannels, flags, skin);
+ if (success)
+ {
+ SkinMesh(skin, true, expectedFence, flags);
+#if UNITY_EDITOR
+ UpdateClothDataForEditing(skin);
+#endif
+ }
+ device.EndSkinning();
+ // Insert fence after all skinning is complete
+ UInt32 fence = device.InsertCPUFence();
+
+ return success;
+}
+
+#if UNITY_EDITOR
+float SkinnedMeshRenderer::GetCachedSurfaceArea ()
+{
+ if (m_CachedSurfaceArea >= 0.0f)
+ return m_CachedSurfaceArea;
+
+ Mesh* mesh = m_CachedMesh;
+ if (!mesh)
+ {
+ m_CachedSurfaceArea = 1.0f;
+ return m_CachedSurfaceArea;
+ }
+
+ Matrix4x4f objectToWorld = GetTransformInfo ().worldMatrix;
+
+ Mesh::TemporaryIndexContainer triangles;
+ mesh->GetTriangles (triangles);
+
+ dynamic_array<Vector3f> vertices;
+ if (GetSkinnedVerticesAndNormals (&vertices, NULL)) // this may fail and return empty array as is the case in repro for bug 505751.
+ {
+ m_CachedSurfaceArea = CalculateSurfaceArea (objectToWorld, triangles, vertices);
+ }
+ else
+ {
+ m_CachedSurfaceArea = 1.0f; // to avoid repeat invocation of failing GetCachedSurfaceArea
+ }
+ return m_CachedSurfaceArea;
+}
+
+bool SkinnedMeshRenderer::GetSkinnedVerticesAndNormals (dynamic_array<Vector3f>* vertices, dynamic_array<Vector3f>* normals)
+{
+ UInt32 requiredChannels = (1 << kShaderChannelVertex);
+ if (normals)
+ requiredChannels |= (1 << kShaderChannelNormal);
+
+ SkinMeshInfo skin;
+ if (!PrepareSkinCommon(requiredChannels, SF_ReadbackBuffer | SF_NoUpdateVBO, skin))
+ return false;
+
+ DeformSkinnedMesh(skin);
+ skin.Release();
+
+ bool hasVertices = m_ChannelsInVBO & (1 << kShaderChannelVertex);
+ bool hasNormals = skin.skinNormals;
+
+ if (vertices && hasVertices)
+ {
+ vertices->resize_uninitialized (skin.vertexCount);
+
+ for (int i = 0; i < skin.vertexCount; i++)
+ {
+ char* vertex = ((char*)skin.outVertices + i * skin.outStride);
+ (*vertices)[i] = *((Vector3f*)vertex);
+ }
+ }
+
+ if (normals && hasNormals)
+ {
+ normals->resize_uninitialized (skin.vertexCount);
+
+ for (int i = 0; i < skin.vertexCount; i++)
+ {
+ char* normal = ((char*)skin.outVertices + i * skin.outStride + skin.normalOffset);
+ (*normals)[i] = *((Vector3f*)normal);
+ }
+ }
+ return true;
+}
+#endif
+
+
+int SkinnedMeshRenderer::GetBonesPerVertexCount ()
+{
+ if (m_Quality == 0)
+ return GetQualitySettings().GetCurrent().blendWeights;
+ else
+ return m_Quality;
+}
+
+template<class TransferFunction> inline
+void SkinnedMeshRenderer::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ transfer.SetVersion(2);
+
+ TRANSFER_SIMPLE (m_Quality);
+ TRANSFER_SIMPLE (m_UpdateWhenOffscreen);
+ transfer.Align();
+
+ transfer.Transfer (m_Mesh, "m_Mesh");
+
+ transfer.Transfer (m_Bones, "m_Bones", kHideInEditorMask);
+ transfer.Align();
+
+ transfer.Transfer(m_BlendShapeWeights, "m_BlendShapeWeights");
+
+ transfer.Transfer(m_RootBone, "m_RootBone");
+
+ transfer.Transfer(m_AABB, "m_AABB");
+ transfer.Transfer(m_DirtyAABB, "m_DirtyAABB", kHideInEditorMask);
+ transfer.Align();
+}
+
+IMPLEMENT_CLASS_HAS_INIT (SkinnedMeshRenderer)
+IMPLEMENT_OBJECT_SERIALIZE (SkinnedMeshRenderer)
+
+void SkinnedMeshRenderer::InitializeClass ()
+{
+ REGISTER_MESSAGE_VOID (SkinnedMeshRenderer, kBecameVisible, BecameVisible);
+ REGISTER_MESSAGE_VOID (SkinnedMeshRenderer, kBecameInvisible, BecameInvisible);
+
+ REGISTER_MESSAGE_VOID(SkinnedMeshRenderer, kDidDeleteMesh, DidDeleteMesh);
+ REGISTER_MESSAGE_VOID(SkinnedMeshRenderer, kDidModifyMesh, DidModifyMesh);
+ RegisterAllowNameConversion (SkinnedMeshRenderer::GetClassStringStatic(), "m_LodMesh", "m_Mesh");
+ RegisterAllowNameConversion (SkinnedMeshRenderer::GetClassStringStatic(), "m_Animation", "m_DisableAnimationWhenOffscreen");
+
+
+ InitializeBlendShapeAnimationBindingInterface ();
+
+}
+
+void SkinnedMeshRenderer::CleanupClass ()
+{
+ CleanupBlendShapeAnimationBindingInterface ();
+}
+
+void SkinnedMeshRenderer::DidDeleteMesh ()
+{
+ m_CachedMesh = NULL;
+ m_CachedBlendShapeCount = 0;
+}
+
+void SkinnedMeshRenderer::DidModifyMesh ()
+{
+ m_SourceMeshDirty = true;
+}
+
+void SkinnedMeshRenderer::UpdateCachedMesh ()
+{
+ Mesh* mesh = m_Mesh;
+ if (mesh != m_CachedMesh)
+ {
+ m_SourceMeshDirty = true;
+
+ m_CachedMesh = mesh;
+ BoundsChanged();
+ m_TransformDirty = true;
+ SET_CACHED_SURFACE_AREA_DIRTY();
+
+ m_MeshNode.RemoveFromList();
+
+ if (m_CachedMesh)
+ m_CachedMesh->AddObjectUser( m_MeshNode );
+ }
+
+ if (m_CachedMesh != NULL)
+ m_CachedBlendShapeCount = GetBlendShapeChannelCount(m_CachedMesh->GetBlendShapeData());
+ else
+ m_CachedBlendShapeCount = 0;
+
+ ClearCachedAnimatorBinding();
+}
+
+
+
+void SkinnedMeshRenderer::UpdateRenderer()
+{
+ if (GetEnabled() && IsActive())
+ {
+ // Force update bounding volumes when we have a root bone or we have to
+ bool recalculateBoundingVolumeEveryFrame = ShouldRecalculateBoundingVolumeEveryFrame();
+ if (recalculateBoundingVolumeEveryFrame)
+ {
+ // Make we continously get this callback every frame so that we can make sure that skinned meshes are put in the gActiveSkinnedMeshes queue
+ // depending on their visibility
+ UpdateManagerState(true);
+
+ // The root transform can move every frame and there is no way for us to track it.
+ TransformChanged (Transform::kPositionChanged | Transform::kRotationChanged | Transform::kScaleChanged);
+ }
+ }
+
+ UpdateVisibleSkinnedMeshQueue(IsActive());
+
+ Super::UpdateRenderer();
+}
+
+void SkinnedMeshRenderer::UpdateVisibleSkinnedMeshQueue (bool active)
+{
+ bool needsUpdate = m_Visible && (GetEnabled() && active);
+ if (needsUpdate == m_SkinNode.IsInList())
+ return;
+
+ if (needsUpdate)
+ gActiveSkinnedMeshes.push_back(m_SkinNode);
+ else
+ m_SkinNode.RemoveFromList();
+}
+
+void SkinnedMeshRenderer::Deactivate (DeactivateOperation operation)
+{
+ Super::Deactivate(operation);
+ UpdateVisibleSkinnedMeshQueue(false);
+ ClearCachedAnimatorBinding();
+}
+
+void SkinnedMeshRenderer::SetUpdateWhenOffscreen (bool onlyIfVisible)
+{
+ m_UpdateWhenOffscreen = onlyIfVisible;
+
+ // We might have to start firing UpdateRenderer events every frame...
+ UpdateManagerState (IsActive());
+ BoundsChanged ();
+
+ SetDirty();
+}
+
+void SkinnedMeshRenderer::BecameVisible ()
+{
+ m_Visible = true;
+
+ // When using LOD we might have a skinned mesh, the animation component might sample during OnBecameVisible since it wasn't visible before.
+ // In that case the root bone might change, thus we force a transformDirty in on became visible as well as every single frame.
+ if (ShouldRecalculateBoundingVolumeEveryFrame())
+ m_TransformDirty = true;
+
+ UpdateVisibleSkinnedMeshQueue (IsActive());
+
+ m_UpdateBeforeRendering = true;
+}
+
+void SkinnedMeshRenderer::BecameInvisible ()
+{
+ m_Visible = false;
+ UpdateVisibleSkinnedMeshQueue (IsActive());
+}
+
+
+static GPUSkinningInfo* CreateGPUSkinningIfAvailable()
+{
+ if (!GetBuildSettings().hasAdvancedVersion)
+ return NULL;
+
+ return GetGfxDevice().CreateGPUSkinningInfo();
+}
+
+
+void SkinnedMeshRenderer::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ if (!m_MemExportInfo)
+ m_MemExportInfo = CreateGPUSkinningIfAvailable();
+
+ #if UNITY_EDITOR || SUPPORT_REPRODUCE_LOG
+ HandleOldSkinnedFilter ();
+ #endif
+
+ Super::AwakeFromLoad(awakeMode);
+
+ UpdateCachedMesh ();
+
+ // Make sure we are added to the visibile queue if we the renderer is active
+ UpdateVisibleSkinnedMeshQueue (IsActive());
+
+ // The root transform pptr might have changed and thus worldTransform needs to be updated
+ TransformChanged(Transform::kPositionChanged | Transform::kRotationChanged | Transform::kScaleChanged | Transform::kParentingChanged);
+
+ m_BlendShapeWeights.resize_initialized(m_CachedBlendShapeCount, 0.0F);
+}
+
+void SkinnedMeshRenderer::SetQuality (int quality)
+{
+ m_Quality = quality; SetDirty();
+}
+
+void SkinnedMeshRenderer::Render (int subsetIndex, const ChannelAssigns& channels)
+{
+ PROFILER_AUTO(gMeshSkinningRender, this)
+
+ if (m_CachedMesh)
+ {
+ if (m_CachedMesh->GetSkin().empty() && m_CachedBlendShapeCount == 0)
+ {
+ ErrorStringObject("SkinnedMeshRenderer requires a mesh with skinning or blendshape information.", this);
+ return;
+ }
+
+ UInt32 requiredChannels = channels.GetSourceMap();
+ // Skinned cloth prefers to have normals, even if shader does not need them
+ if (m_Cloth)
+ requiredChannels |= (1<<kShaderChannelNormal);
+
+ // Just in time update
+ if (m_UpdateBeforeRendering || m_SourceMeshDirty || !m_VBO || m_VBO->IsVertexBufferLost())
+ {
+ PROFILER_BEGIN(gMeshSkinningUpdateImmediate, this)
+ bool success = SkinMeshImmediate(requiredChannels);
+ PROFILER_END
+
+ // Mesh skinning can fail (Bone indices out of bounds, bone transforms missing etc)
+ if (!success)
+ return;
+ }
+
+ if (m_CustomProperties)
+ GetGfxDevice().SetMaterialProperties (*m_CustomProperties);
+ DrawUtil::DrawVBOMeshRaw (*m_VBO, *m_CachedMesh, channels, subsetIndex, m_ChannelsInVBO);
+ }
+
+ GPU_TIMESTAMP();
+}
+
+void SkinnedMeshRenderer::SetLocalAABB(const AABB& bounds)
+{
+ m_AABB = bounds;
+ m_DirtyAABB = false;
+ SetDirty();
+}
+
+void SkinnedMeshRenderer::ReadSkinningDataForCloth(const SkinMeshInfo& skin)
+{
+#if ENABLE_CLOTH
+ void *normalPointer = (char*)skin.outVertices + skin.normalOffset;
+ if (!skin.skinNormals)
+ normalPointer = NULL;
+ void *tangentPointer = (char*)skin.outVertices + skin.tangentOffset;
+ if (!skin.skinTangents)
+ tangentPointer = NULL;
+
+ GetIPhysics()->SetUpSkinnedBuffersOnSkinnedCloth(*m_Cloth, skin.outVertices, normalPointer, tangentPointer, skin.outStride);
+#endif
+}
+
+void SkinnedMeshRenderer::UpdateAllSkinnedMeshes(UpdateType updateType, dynamic_array<SkinnedMeshRenderer*>* outMeshes)
+{
+ PROFILER_AUTO(gMeshSkinningUpdate, NULL)
+
+ // TODO: we should submit skinning jobs, go do something else (e.g. update particles, movies, whatnot),
+ // and wait/reintegrate jobs after that is done.
+
+ int flags = SF_None;
+ if (updateType == kUpdateCloth)
+ {
+ flags |= SF_ReadbackBuffer;
+ if (IsWorldPlaying())
+ flags |= SF_ClothPlaying;
+ }
+ else
+ {
+ flags = SF_AllowMemExport;
+ }
+
+ size_t skinCount = 0;
+ size_t maxCount = gActiveSkinnedMeshes.size_slow();
+ dynamic_array<SkinnedMeshRenderer*> skinMeshes(maxCount, kMemTempAlloc);
+ dynamic_array<SkinMeshInfo> skinInfos(maxCount, kMemTempAlloc);
+ dynamic_array<CalculateSkinMatricesTask> calculateSkinMatricesTasks(maxCount, kMemTempAlloc);
+
+ PROFILER_BEGIN(gMeshSkinningPrepare, NULL);
+ // Find out which renderers to skin this frame
+ const SkinMeshInfo* lastMemExport = 0;
+ SkinnedMeshList::iterator next;
+ int skinMatrixTaskCount = 0;
+ for (SkinnedMeshList::iterator i=gActiveSkinnedMeshes.begin();i != gActiveSkinnedMeshes.end();i=next)
+ {
+ SkinnedMeshRenderer& skin = **i;
+ next = i;
+ next++;
+ UpdateType type = (skin.m_Cloth && IsWorldPlaying())? kUpdateCloth : kUpdateNonCloth;
+ if (type != updateType)
+ continue;
+ SkinMeshInfo& info = skinInfos[skinCount];
+ memset(&info, 0, sizeof(SkinMeshInfo));
+
+ calculateSkinMatricesTasks[skinMatrixTaskCount].skeletonPose = NULL; // mark invalid task
+ if (skin.PrepareSkin(skin.m_ChannelsInVBO, flags, info, &calculateSkinMatricesTasks[skinMatrixTaskCount]))
+ {
+ if (calculateSkinMatricesTasks[skinMatrixTaskCount].skeletonPose != NULL)
+ skinMatrixTaskCount++; // valid task
+ skinMeshes[skinCount] = &skin;
+ skinCount++;
+ if (info.memExport)
+ lastMemExport = &info;
+ }
+ }
+ PROFILER_END;
+
+ if (skinMatrixTaskCount)
+ {
+#if ENABLE_MULTITHREADED_CODE
+ JobScheduler& scheduler = GetJobScheduler();
+ JobScheduler::JobGroupID jobGroup;
+#define CALC_SKIN_MATRICES_LOOP(x,list,size) \
+ { \
+ size_t jobCount = size; \
+ jobGroup = scheduler.BeginGroup(jobCount); \
+ for (size_t i = 0; i < jobCount; ++i) \
+ { CalculateSkinMatricesTask& task = list[i]; scheduler.SubmitJob (jobGroup, x, &task, NULL); } \
+ scheduler.WaitForGroup (jobGroup); \
+ }
+#else
+#define CALC_SKIN_MATRICES_LOOP(x,list,size) \
+ for (size_t i=0;i<size;i++) \
+ { CalculateSkinMatricesTask& task = list[i]; x (&task); }
+#endif
+
+ // Do it in multiple threads
+ if (GetAnimationInterface())
+ {
+ /// @TODO: Simplify this. No need for having an interface that returns a callback... Doh
+ CalculateAnimatorSkinMatricesFunc calculateAnimatorSkinMatricesFunc =
+ GetAnimationInterface()->GetCalculateAnimatorSkinMatricesFunc();
+ if (calculateAnimatorSkinMatricesFunc)
+ CALC_SKIN_MATRICES_LOOP(calculateAnimatorSkinMatricesFunc, calculateSkinMatricesTasks, skinMatrixTaskCount)
+ }
+ }
+
+ if (skinCount == 0)
+ return;
+
+ skinMeshes.resize_uninitialized(skinCount);
+ skinInfos.resize_uninitialized(skinCount);
+
+ // Double check there are no fences inserted during skinning
+ GfxDevice& device = GetGfxDevice();
+ UInt32 expectedFence = device.GetNextCPUFence();
+
+ // Now we know exactly which renderers to skin and which one is last
+ device.BeginSkinning(skinCount);
+ for (int i = 0; i < skinCount; i++)
+ {
+ SkinMeshInfo& info = skinInfos[i];
+ SkinnedMeshRenderer& skin = *skinMeshes[i];
+ bool lastMemExportThisFrame = (&info == lastMemExport);
+ skin.SkinMesh(info, lastMemExportThisFrame, expectedFence, flags);
+ }
+
+ PROFILER_BEGIN(gMeshSkinningWait, NULL)
+ device.EndSkinning();
+ PROFILER_END;
+
+ // Insert fence after all skinning is complete
+ UInt32 fence = device.InsertCPUFence();
+ Assert(fence == expectedFence);
+
+ // Read back vertices for cloth
+ // It's fine to do this after EndSkinning() since we own the buffer (m_SkinnedVertices)
+ if (updateType == kUpdateCloth)
+ {
+ for (int i = 0; i < skinCount; i++)
+ {
+ SkinnedMeshRenderer& skin = *skinMeshes[i];
+ skin.ReadSkinningDataForCloth(skinInfos[i]);
+ }
+ }
+
+ if (outMeshes)
+ outMeshes->assign(skinMeshes.begin(), skinMeshes.end());
+}
+
+void SkinnedMeshRenderer::UploadSkinnedClothes(const dynamic_array<SkinnedMeshRenderer*>& skinnedMeshes)
+{
+ int skinCount = skinnedMeshes.size();
+ for (int i = 0; i < skinCount; i++)
+ {
+ SkinnedMeshRenderer& skin = *skinnedMeshes[i];
+
+ if (skin.m_SkinnedVertices.empty())
+ continue;
+
+ VertexStreamData mappedVSD;
+ if (skin.m_VBO->MapVertexStream(mappedVSD, 0))
+ {
+ memcpy(mappedVSD.buffer, skin.m_SkinnedVertices.data(), skin.m_SkinnedVertices.size());
+ skin.m_VBO->UnmapVertexStream(0);
+ }
+ }
+}
+
+Transform& SkinnedMeshRenderer::GetActualRootBone ()
+{
+ Transform* rootBone = m_RootBone;
+ if (rootBone != NULL)
+ // Not optimized mode && m_RootBone != NULL
+ return *rootBone;
+ else
+ // Optimized mode
+ // Not optimized mode && m_RootBone == NULL
+ return GetTransform();
+}
+
+
+bool SkinnedMeshRenderer::CalculateRootLocalSpaceBounds (MinMaxAABB& minMaxAAbb)
+{
+ Matrix4x4f* poses;
+ int poseCount = GetBindposeCount();
+ ALLOC_TEMP(poses, Matrix4x4f, poseCount);
+
+ Transform& rootBone = GetActualRootBone();
+
+ if (CalculateAnimatedPosesWithRoot(rootBone.GetWorldToLocalMatrix(), poses, poseCount) && CalculateBoneBasedBounds(poses, poseCount, minMaxAAbb))
+ return true;
+ else
+ return false;
+}
+
+void SkinnedMeshRenderer::UpdateTransformInfo ()
+{
+ Transform& rootBone = GetActualRootBone();
+
+ Vector3f pos;
+ Quaternionf rot;
+ TransformType transformType = rootBone.GetPositionAndRotationWithTransformType (pos, rot);
+
+ const bool hasSkin = !m_CachedMesh ? true : !m_CachedMesh->GetSkin().empty();
+ if (hasSkin || IsNoScaleTransform(transformType))
+ {
+ m_TransformInfo.transformType = transformType & kOddNegativeScaleTransform;
+ m_TransformInfo.worldMatrix.SetTR (pos, rot);
+ m_TransformInfo.invScale = 1.0f;
+ }
+ // This codepath only exists for blendshapes. Skinned meshes will always use only position & rotation (no scale)
+ // For blendshapes the skinning code does no bone or matrix deformation thus there is no good way to plug scale in there.
+ else
+ {
+ float uniformScale = 1.0f;
+ m_TransformInfo.worldMatrix = rootBone.GetLocalToWorldMatrix();
+ m_TransformInfo.transformType = transformType = ComputeTransformType(m_TransformInfo.worldMatrix, uniformScale);
+ m_TransformInfo.invScale = 1.0f / uniformScale;
+ }
+
+#if UNITY_SUPPORTS_VFP || (UNITY_SUPPORTS_NEON && !UNITY_DISABLE_NEON_SKINNING)
+ // NOTE: optimized VFP routines do not do any normalization
+ // instead we rely on GPU to do that
+ if (GetBonesPerVertexCount () != 1)
+ m_TransformInfo.transformType |= kNonUniformScaleTransform;
+#endif
+
+ // Compute world space bounding volume from each bone to get a very accurate bounding volume
+ if (m_UpdateWhenOffscreen && hasSkin)
+ {
+ Matrix4x4f* poses;
+ int poseCount = GetBindposeCount();
+ ALLOC_TEMP(poses, Matrix4x4f, poseCount);
+
+ MinMaxAABB accurateMinMax;
+ if (CalculateAnimatedPoses(poses, poseCount) && CalculateBoneBasedBounds(poses, poseCount, accurateMinMax))
+ {
+ m_TransformInfo.worldAABB = accurateMinMax;
+ InverseTransformAABB(m_TransformInfo.worldAABB, pos, rot, m_TransformInfo.localAABB);
+ return;
+ }
+ }
+
+ // Calculate m_AABB from the current pose and undirty the AABB
+ if (m_DirtyAABB)
+ {
+ MinMaxAABB accurateMinMax;
+ if (!hasSkin && m_CachedMesh)
+ {
+ accurateMinMax = m_CachedMesh->GetLocalAABB();
+ SetLocalAABB(accurateMinMax);
+ }
+ else if (CalculateRootLocalSpaceBounds(accurateMinMax))
+ {
+ SetLocalAABB(accurateMinMax);
+ }
+ else
+ {
+ ////@TOOD: figure out something logicalll....
+ m_AABB = AABB(Vector3f::zero, Vector3f::zero);
+ }
+ }
+
+ MinMaxAABB localAABB = m_AABB;
+
+ if (IsNoScaleTransform(transformType))
+ {
+ m_TransformInfo.localAABB = localAABB;
+ TransformAABB (localAABB, pos, rot, m_TransformInfo.worldAABB);
+ }
+ else
+ {
+ // Calculate world space bounding volume (Transform from root space to world space)
+ Matrix4x4f rootToWorldMatrix;
+ rootBone.CalculateTransformMatrix (rootToWorldMatrix);
+ TransformAABB (localAABB, rootToWorldMatrix, m_TransformInfo.worldAABB);
+
+
+ // Calculate local space bounding volume (Transform from root space to SkinnedMeshRenderer space)
+ // When we have scaled objects we have to
+ // The m_TransformInfo.localAABB is the local space bounding volume relative to the root position and rotation, excluding scale.
+ // Thus the following code simply exists to add scale to the stored m_AABB.
+ Matrix4x4f inverseWorldMatrix;
+ inverseWorldMatrix.SetTRInverse (pos, rot);
+
+ Matrix4x4f rootToLocal;
+ MultiplyMatrices4x4(&inverseWorldMatrix, &rootToWorldMatrix, &rootToLocal);
+
+ TransformAABB (localAABB, rootToLocal, m_TransformInfo.localAABB);
+ }
+}
+
+
+void SkinnedMeshRenderer::GetSkinnedMeshLocalAABB (AABB& bounds)
+{
+ // Make sure the localAABB & m_LocalAABB is up to date
+ const TransformInfo& info = GetTransformInfo();
+
+ // Extract from world space bounding volume
+ if (m_UpdateWhenOffscreen)
+ bounds = info.localAABB;
+ // Return precomputed local bounding volume
+ else
+ bounds = m_AABB;
+
+}
+
+void SkinnedMeshRenderer::BakeMesh (Mesh& mesh)
+{
+ if (m_CachedMesh == NULL)
+ return;
+
+ // Making the mesh relative to the root bone is very unintutive when using it as a SkinnedMesh.
+ // So don't do that when exporting the mesh.
+ PPtr<Transform> oldRootBone = m_RootBone;
+ m_RootBone = NULL;
+
+ SkinMeshInfo skin;
+#if UNITY_PS3
+ if (false == m_CachedMesh->m_Partitions.empty())
+ {
+ WarningString(Format("Optimized skinned meshes cannot be baked (%s).", m_CachedMesh->GetName()));
+ return;
+ }
+
+ skin.vertexData = &m_CachedMesh->GetVertexData();
+#endif
+
+ if (PrepareSkinCommon(m_CachedMesh->GetAvailableChannels(), SF_NoUpdateVBO, skin))
+ {
+ mesh.WaitOnRenderThreadUse();
+
+ mesh.SetBoneWeights(NULL, 0);
+
+ const VertexData& skinVertexData = m_CachedMesh->GetVertexData();
+ const VertexStreamsLayout& skinStreamsLayout = skinVertexData.GetStreamsLayout();
+ const VertexChannelsLayout& skinChannelsLayout = skinVertexData.GetChannelsLayout();
+ mesh.ResizeVertices (skin.vertexCount, m_ChannelsInVBO, skinStreamsLayout, skinChannelsLayout);
+
+ skin.outVertices = mesh.GetVertexDataPointer();
+
+ DeformSkinnedMesh(skin);
+ skin.Release();
+
+ // Skinning only updates vertex data in "hot" stream (zero). Have to copy
+ // all the other data like UVs.
+ const UInt32 channelsToCopy = m_ChannelsInVBO & (~skinStreamsLayout.channelMasks[0]);
+ CopyVertexDataChannels (skin.vertexCount, channelsToCopy, skinVertexData, mesh.GetVertexData());
+
+ mesh.GetIndexBuffer() = m_CachedMesh->GetIndexBuffer();
+ mesh.GetSubMeshes() = m_CachedMesh->GetSubMeshes();
+ mesh.SetVertexColorsSwizzled(m_CachedMesh->GetVertexColorsSwizzled());
+
+ mesh.SetChannelsDirty(true, true);
+
+ // Calculate bounding volume
+ Matrix4x4f rootPose = GetActualRootBone().GetWorldToLocalMatrixNoScale ();
+ MinMaxAABB accurateMinMax;
+ Matrix4x4f* poses;
+ int poseCount = GetBindposeCount();
+ ALLOC_TEMP(poses, Matrix4x4f, poseCount);
+ if (CalculateAnimatedPosesWithRoot(rootPose, poses, poseCount) && CalculateBoneBasedBounds(poses, poseCount, accurateMinMax))
+ mesh.SetLocalAABB(accurateMinMax);
+ }
+
+ m_RootBone = oldRootBone;
+}
+
+////@TODO: Write integration test for this!
+#if UNITY_EDITOR || SUPPORT_REPRODUCE_LOG
+
+void SkinnedMeshRenderer::HandleOldSkinnedFilter ()
+{
+ /// Backwards compatibility for old school skinned mesh filters
+ /// - Remove mesh renderer
+ /// - copy over materials from renderer
+ Renderer* meshRenderer = NULL;
+ if (GetGameObjectPtr())
+ meshRenderer = QueryComponent(MeshRenderer);
+
+ if (meshRenderer)
+ {
+ #if UNITY_EDITOR
+ GetCleanupManager ().MarkForDeletion (meshRenderer, "Obsolete");
+ #endif
+ SetMaterialArray(meshRenderer->GetMaterialArray(), meshRenderer->GetSubsetIndices());
+ }
+}
+
+#endif
+
+
+#if ENABLE_PROFILER
+int SkinnedMeshRenderer::GetVisibleSkinnedMeshRendererCount ()
+{
+ return gActiveSkinnedMeshes.size_slow();
+}
+#endif
+
+size_t SkinnedMeshRenderer::GetValidBlendShapeWeightCount () const
+{
+ size_t size = std::min<UInt32>(m_CachedBlendShapeCount, m_BlendShapeWeights.size());
+
+ for (int i=size-1;i >= 0;i--)
+ {
+ if (HasValidWeight(m_BlendShapeWeights[i]))
+ return i + 1;
+ }
+ return 0;
+}
+
+float SkinnedMeshRenderer::GetBlendShapeWeight(UInt32 index) const
+{
+ size_t size = std::min<UInt32>(m_CachedBlendShapeCount, m_BlendShapeWeights.size());
+ if (index >= size)
+ return 0.0F;
+ else
+ return m_BlendShapeWeights[index];
+}
+
+void SkinnedMeshRenderer::SetBlendShapeWeight(UInt32 index, float weight)
+{
+ if (index >= m_CachedBlendShapeCount)
+ {
+ ErrorStringMsg("Array index (%d) is out of bounds (size=%d)", (int)index, (int)m_BlendShapeWeights.size());
+ return;
+ }
+
+ // The m_BlendShapeWeights array is stored seperately from the mesh.
+ // Thus it can go out of sync.
+ // It is bad to fix dependent serialized data in Awake (eg. SkinnedMeshRenderer in scenes could change based on changes in an asset)
+ // Thus we resize to the correct size when the user sets a blendshape weight.
+ // We can never assume that m_BlendShapeWeights.size() already matches the amount of blendshapes in the used mesh when this function is called.
+ if (index >= m_BlendShapeWeights.size())
+ m_BlendShapeWeights.resize_initialized(m_CachedBlendShapeCount, 0.0F);
+
+ m_BlendShapeWeights[index] = weight;
+ SetDirty();
+}
+
+/*
+ * Make sure m_BlendShapeWeights is not accessed directly anywhere.. It is not the size of the number of blendshapes..
+ * Wrong: bool ShouldRecalculateBoundingVolumeEveryFrame () { return m_UpdateWhenOffscreen || m_RootBone.GetInstanceID() != 0 || !m_BlendShapeWeights.empty (); }
+ * Wrong: BlendShape bounding volume calculation needs serious review
+ * Extract & Store blendshape default values in skinned mesh renderer from fbx file
+ * Review code for multithreaded rendering integration when changing blendshape weights. Probably should also hook into mesh changed for recalculating blendshape count...
+ * Fix animation window editing being abysmal slow
+*/
+void SkinnedMeshRenderer::UnloadVBOFromGfxDevice()
+{
+ if (m_VBO)
+ {
+ GetGfxDevice().DeleteVBO (m_VBO);
+ m_VBO = NULL;
+ }
+ if (m_MemExportInfo)
+ {
+ GetGfxDevice().DeleteGPUSkinningInfo(m_MemExportInfo);
+ m_MemExportInfo = NULL;
+ }
+
+ m_SourceMeshDirty = true;
+}
+
+void SkinnedMeshRenderer::ReloadVBOToGfxDevice()
+{
+ if (!m_MemExportInfo)
+ m_MemExportInfo = CreateGPUSkinningIfAvailable();
+}
+
+Unity::Component* SkinnedMeshRenderer::GetAnimator()
+{
+ if (m_CachedAnimator == NULL)
+ CreateCachedAnimatorBinding ();
+
+ return m_CachedAnimator;
+}
+
+void SkinnedMeshRenderer::CreateCachedAnimatorBinding()
+{
+ ClearCachedAnimatorBinding();
+
+ if (!m_CachedMesh)
+ return;
+
+ const dynamic_array<BindingHash>& bonePathHashes = m_CachedMesh->GetBonePathHashes();
+ if (bonePathHashes.size() != GetBindposeCount())
+ {
+ ErrorStringObject("Bones do not match bindpose.", m_CachedMesh);
+ return;
+ }
+
+ m_CachedAnimator = FindAncestorComponentExactTypeImpl(GetGameObject(), ClassID(Animator));
+ if (m_CachedAnimator == NULL || GetAnimationInterface() == NULL)
+ return;
+
+ m_SkeletonIndices.resize_uninitialized(bonePathHashes.size());
+ if (!GetAnimationInterface()->PathHashesToIndices(*m_CachedAnimator, bonePathHashes.begin(), bonePathHashes.size(), m_SkeletonIndices.begin()))
+ m_SkeletonIndices.clear();
+
+ Assert(!m_CachedAnimator->HasEvent(AnimatorModifiedCallback, this));
+ m_CachedAnimator->AddEvent(AnimatorModifiedCallback, this);
+}
+
+void SkinnedMeshRenderer::ClearCachedAnimatorBinding()
+{
+ if (m_CachedAnimator == NULL)
+ return;
+
+ m_CachedAnimator->RemoveEvent(AnimatorModifiedCallback, this);
+ m_SkeletonIndices.clear();
+ m_CachedAnimator = NULL;
+}
+
+void SkinnedMeshRenderer::AnimatorModifiedCallback(void* userData, void* sender, int eventID)
+{
+ SkinnedMeshRenderer& skinnedMeshRenderer = *reinterpret_cast<SkinnedMeshRenderer*>(userData);
+
+ if (eventID == kAnimatorClearEvent)
+ skinnedMeshRenderer.ClearCachedAnimatorBinding();
+}
+
diff --git a/Runtime/Filters/Deformation/SkinnedMeshFilter.h b/Runtime/Filters/Deformation/SkinnedMeshFilter.h
new file mode 100644
index 0000000..d20d304
--- /dev/null
+++ b/Runtime/Filters/Deformation/SkinnedMeshFilter.h
@@ -0,0 +1,213 @@
+#ifndef SKINNEDMESHFILTER_H
+#define SKINNEDMESHFILTER_H
+
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Filters/Mesh/Mesh.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/GfxDevice/GPUSkinningInfo.h"
+#include "Runtime/Modules/ExportModules.h"
+#include "Runtime/BaseClasses/NamedObject.h"
+
+class Mesh;
+class Animation;
+class VBO;
+namespace Unity { class SkinnedCloth; }
+struct SkinMeshInfo;
+struct CalculateSkinMatricesTask;
+
+class EXPORT_COREMODULE SkinnedMeshRenderer : public Renderer
+{
+ PPtr<Mesh> m_Mesh;
+ Mesh* m_CachedMesh;
+
+ // Bones for non-optimized mode
+ dynamic_array<PPtr<Transform> > m_Bones;
+ PPtr<Transform> m_RootBone;
+ // Bones for optimized mode
+ dynamic_array<UInt16> m_SkeletonIndices;
+
+ Unity::Component* m_CachedAnimator;
+
+ UInt32 m_CachedBlendShapeCount;
+ dynamic_array<float> m_BlendShapeWeights;
+
+ AABB m_AABB;
+ int m_Quality; ///< enum { Auto = 0, 1 Bone = 1, 2 Bones = 2, 4 Bones = 4 } Number of bones to use per vertex during skinning.
+ bool m_UpdateWhenOffscreen;///< Unity will calculate an accurate bounding volume representation every frame.
+ bool m_DirtyAABB;
+ bool m_Visible;
+
+ VBO* m_VBO;
+ dynamic_array<UInt8> m_SkinnedVertices;
+ UInt32 m_ChannelsInVBO;
+ bool m_SourceMeshDirty;
+ bool m_UpdateBeforeRendering;
+ SkinnedCloth* m_Cloth;
+
+ GPUSkinningInfo* m_MemExportInfo;
+
+ ListNode<Object> m_MeshNode;
+
+#if UNITY_EDITOR
+ float m_CachedSurfaceArea;
+#endif
+
+#if UNITY_EDITOR || SUPPORT_REPRODUCE_LOG
+ PPtr<Animation> m_DeprecatedDisableAnimationWhenOffscreen;
+#endif
+
+ ListNode<SkinnedMeshRenderer> m_SkinNode;
+ void DirtyAndClearCache ();
+
+public:
+
+ int GetBonesPerVertexCount ();
+
+ REGISTER_DERIVED_CLASS (SkinnedMeshRenderer, Renderer)
+ DECLARE_OBJECT_SERIALIZE (SkinnedMeshRenderer)
+
+ SkinnedMeshRenderer (MemLabelId label, ObjectCreationMode mode);
+ // ~SkinnedMeshRenderer (); declared-by-macro
+
+ virtual void Reset ();
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+ virtual void Deactivate (DeactivateOperation operation);
+
+ void Setup (Mesh* lodmesh, const dynamic_array<PPtr<Transform> >& state);
+
+ enum SkinningFlags
+ {
+ SF_None = 0,
+ SF_NoUpdateVBO = 1 << 0,
+ SF_ReadbackBuffer = 1 << 1,
+ SF_ClothPlaying = 1 << 2,
+ SF_AllowMemExport = 1 << 3,
+ };
+
+ enum UpdateType
+ {
+ kUpdateCloth,
+ kUpdateNonCloth
+ };
+
+ bool PrepareSkinCommon( UInt32 requiredChannels, int flags, SkinMeshInfo& skin, CalculateSkinMatricesTask* calcSkinMatricesTask=NULL );
+ bool PrepareSkinPS3( UInt32 requiredChannels, int flags, SkinMeshInfo& skin, CalculateSkinMatricesTask* calcSkinMatricesTask=NULL );
+ bool PrepareSkinGPU( UInt32 requiredChannels, int flags, SkinMeshInfo& skin, CalculateSkinMatricesTask* calcSkinMatricesTask=NULL );
+ bool PrepareSkin( UInt32 requiredChannels, int flags, SkinMeshInfo& skin, CalculateSkinMatricesTask* calcSkinMatricesTask=NULL );
+
+ bool SkinMesh( SkinMeshInfo& skin, bool lastMemExportThisFrame, UInt32 cpuFence, int flags );
+ bool SkinMeshImmediate( UInt32 requiredChannels );
+
+ bool CalculateAnimatedPoses (Matrix4x4f* poses, size_t size);
+
+#if UNITY_EDITOR
+ float GetCachedSurfaceArea ();
+ bool GetSkinnedVerticesAndNormals (dynamic_array<Vector3f>* vertices, dynamic_array<Vector3f>* normals);
+ bool CalculateVertexBasedBounds (const Matrix4x4f* poseMatrices, MinMaxAABB& output);
+#endif
+
+ void SetUpdateWhenOffscreen (bool onlyIfVisible);
+ bool GetUpdateWhenOffscreen () { return m_UpdateWhenOffscreen; }
+
+ void SetDisableAnimationWhenOffscreen (Animation* animation);
+ Animation* GetDisableAnimationWhenOffscreen ();
+
+ const dynamic_array<PPtr<Transform> >& GetBones () const { return m_Bones; }
+ void SetBones (const dynamic_array<PPtr<Transform> >& bones);
+
+ bool IsOptimized () { return m_Bones.size()==0 && GetBindposeCount()>0; }
+ // Before invoking this function, please make sure the SkinnedMeshRenderer is in optimized mode.
+ const dynamic_array<UInt16>& GetSkeletonIndices();
+
+ int GetBindposeCount () const;
+ int GetBoneCount () const { return m_Bones.size(); }
+
+ void SetQuality (int quality);
+ int GetQuality () { return m_Quality; }
+
+ void SetMesh (Mesh* mesh);
+ Mesh* GetMesh ();
+
+ void SetCloth (SkinnedCloth *value) { m_Cloth = value; }
+ void SetRootBone (Transform* rootBone) { m_RootBone = rootBone; }
+ Transform* GetRootBone () { return m_RootBone; }
+
+ Transform& GetActualRootBone ();
+
+ static void UpdateAllSkinnedMeshes(UpdateType updateType, dynamic_array<SkinnedMeshRenderer*>* outMeshes = NULL);
+ static void UploadSkinnedClothes(const dynamic_array<SkinnedMeshRenderer*>& skinnedMeshes);
+ void ReadSkinningDataForCloth(const SkinMeshInfo& skin);
+#if UNITY_EDITOR
+ void UpdateClothDataForEditing(const SkinMeshInfo& skin);
+#endif
+
+ /// Handlers so we only skin when somebody sees us.
+ virtual void BecameVisible ();
+ virtual void BecameInvisible ();
+
+ #if UNITY_EDITOR || SUPPORT_REPRODUCE_LOG
+ void HandleOldSkinnedFilter ();
+ #endif
+
+ virtual void UpdateTransformInfo();
+
+ void SetLocalAABB(const AABB& bounds);
+ void GetSkinnedMeshLocalAABB(AABB& bounds);
+
+ void DidDeleteMesh ();
+ void DidModifyMesh ();
+ void UpdateCachedMesh ();
+
+ void BakeMesh (Mesh& mesh);
+
+ virtual void Render (int materialIndex, const ChannelAssigns& channels);
+
+ float GetBlendShapeWeight(UInt32 index) const;
+ void SetBlendShapeWeight(UInt32 index, float weight);
+
+#if ENABLE_PROFILER
+ static int GetVisibleSkinnedMeshRendererCount ();
+#endif
+
+ bool DoesQualifyForMemExport() const;
+
+ bool CalculateRootLocalSpaceBounds (MinMaxAABB& minMaxAAbb);
+
+ void UnloadVBOFromGfxDevice();
+ void ReloadVBOToGfxDevice();
+
+protected:
+
+ size_t GetValidBlendShapeWeightCount () const;
+
+ bool ShouldRecalculateBoundingVolumeEveryFrame () { return m_UpdateWhenOffscreen || m_RootBone.GetInstanceID() != 0; }
+
+
+ // Puts the renderer into the queue of all visible skinned meshes depending on if it is currently visible.
+ void UpdateVisibleSkinnedMeshQueue (bool active);
+
+ bool CalculateAnimatedPosesWithRoot (const Matrix4x4f& rootMatrix, Matrix4x4f* poses, size_t size);
+ // Main thread only.
+ bool CalculateSkinningMatrices (const Matrix4x4f& rootPose, Matrix4x4f* poses, size_t size);
+
+ bool PrepareVBO (bool hasSkin, bool hasBlendshape, bool doMemExport, int flags);
+
+ virtual void UpdateRenderer();
+
+ Unity::Component* GetAnimator ();
+ void CreateCachedAnimatorBinding ();
+ void ClearCachedAnimatorBinding ();
+ bool CalculateBoneBasedBounds (const Matrix4x4f* animatedPoses, size_t size, MinMaxAABB& output);
+
+ static void AnimatorModifiedCallback(void* userData, void* sender, int eventID);
+};
+
+#endif
diff --git a/Runtime/Filters/Mesh/CompressedMesh.cpp b/Runtime/Filters/Mesh/CompressedMesh.cpp
new file mode 100644
index 0000000..02cc74c
--- /dev/null
+++ b/Runtime/Filters/Mesh/CompressedMesh.cpp
@@ -0,0 +1,755 @@
+#include "UnityPrefix.h"
+#include "CompressedMesh.h"
+#include "LodMesh.h"
+#include "Runtime/Animation/AnimationCurveUtility.h"
+
+
+#define sqr(x) ((x)*(x))
+
+void PackedFloatVector::PackFloats(float *data, int itemCountInChunk, int chunkStride, int numChunks, int bitSize, bool adjustBitSize)
+{
+ float maxf = -std::numeric_limits<float>::infinity();
+ float minf = std::numeric_limits<float>::infinity();
+ float* end = Stride (data, numChunks * chunkStride);
+ for(float* it = data; it != end; it = Stride (it, chunkStride))
+ {
+ for (int i=0; i<itemCountInChunk; ++i)
+ {
+ if(maxf < it[i])
+ maxf = it[i];
+ if(minf > it[i])
+ minf = it[i];
+ }
+ }
+
+ m_Range = maxf-minf;
+
+ if(adjustBitSize)
+ bitSize += int(ceilf(Log2(m_Range)));
+ if(bitSize > 32)
+ bitSize = 32;
+
+ m_Start = minf;
+ m_NumItems = numChunks * itemCountInChunk;
+ m_BitSize = bitSize;
+ m_Data.resize((m_NumItems * bitSize + 7)/8, 0);
+
+
+ float scale = 1.0/m_Range;
+
+ int indexPos = 0;
+ int bitPos = 0;
+
+ for(float* it = data; it != end; it = Stride (it, chunkStride))
+ {
+ for(int i=0; i<itemCountInChunk; ++i)
+ {
+ float scaled = (it[i] - m_Start) * scale;
+ if(scaled < 0) scaled = 0;
+ if(scaled > 1) scaled = 1;
+
+ UInt32 x = UInt32(scaled * ((1 << (m_BitSize)) - 1));
+
+ int bits = 0;
+ while(bits < m_BitSize)
+ {
+ m_Data[indexPos] |= (x >> bits) << bitPos;
+ int num = std::min( m_BitSize-bits, 8-bitPos);
+ bitPos += num;
+ bits += num;
+ if(bitPos == 8)
+ {
+ indexPos++;
+ bitPos = 0;
+ }
+ }
+ }
+ }
+}
+
+void PackedFloatVector::UnpackFloats(float *data, int itemCountInChunk, int chunkStride, int start, int numChunks)
+{
+ int bitPos = m_BitSize*start;
+ int indexPos = bitPos/8;
+ bitPos %= 8;
+
+ float scale = 1.0/m_Range;
+ if (numChunks == -1)
+ numChunks = m_NumItems / itemCountInChunk;
+
+ for(float* end = Stride (data, chunkStride * numChunks); data != end; data = Stride (data, chunkStride))
+ {
+ for (int i=0; i<itemCountInChunk; ++i)
+ {
+ UInt32 x = 0;
+
+ int bits = 0;
+ while(bits < m_BitSize)
+ {
+ x |= (m_Data[indexPos] >> bitPos) << bits;
+ int num = std::min( m_BitSize-bits, 8-bitPos);
+ bitPos += num;
+ bits += num;
+ if(bitPos == 8)
+ {
+ indexPos++;
+ bitPos = 0;
+ }
+ }
+ x &= (1 << m_BitSize) - 1;
+ data[i] = (x / (scale * ((1 << (m_BitSize)) - 1))) + m_Start;
+ }
+ }
+}
+
+template <class IntSize> void PackedIntVector::PackInts(IntSize *data, int numItems)
+{
+ // make sure that the intsize is an unsigned type
+ Assert( (IntSize)0 < (IntSize)-1 );
+
+ UInt32 maxi = 0;
+ for(int i=0; i<numItems; i++)
+ if(maxi < data[i])
+ maxi = data[i];
+
+ m_NumItems = numItems;
+ //Prevent overflow
+ m_BitSize = UInt8(maxi == 0xFFFFFFFF ? 32 : ceilf(Log2(maxi+1)));
+ m_Data.resize((numItems * m_BitSize + 7)/8, 0);
+
+
+ int indexPos = 0;
+ int bitPos = 0;
+ for(int i=0; i<numItems; i++)
+ {
+ int bits = 0;
+ while(bits < m_BitSize)
+ {
+ m_Data[indexPos] |= (data[i] >> bits) << bitPos;
+ int num = std::min( m_BitSize-bits, 8-bitPos);
+ bitPos += num;
+ bits += num;
+ if(bitPos == 8)
+ {
+ indexPos++;
+ bitPos = 0;
+ }
+ }
+ }
+}
+
+template <class IntSize> void PackedIntVector::UnpackInts(IntSize *data)
+{
+ int indexPos = 0;
+ int bitPos = 0;
+ for(int i=0; i<m_NumItems; i++)
+ {
+ int bits = 0;
+ data[i] = 0;
+ while(bits < m_BitSize)
+ {
+ data[i] |= (m_Data[indexPos] >> bitPos) << bits;
+ int num = std::min( m_BitSize-bits, 8-bitPos);
+ bitPos += num;
+ bits += num;
+ if(bitPos == 8)
+ {
+ indexPos++;
+ bitPos = 0;
+ }
+ }
+ data[i] &= (1ULL << m_BitSize) - 1;
+ }
+}
+
+
+void PackedQuatVector::PackQuats(Quaternionf *data, int numItems)
+{
+ m_NumItems = numItems;
+ m_Data.resize(numItems * (32/8), 0);
+
+ int indexPos = 0;
+ int bitPos = 0;
+
+ for(int i=0; i<numItems; i++)
+ {
+ Quaternionf &q = data[i];
+ UInt8 flags = q.x<0? 4:0;
+
+ float max=fabs(q.x);
+ if(fabs(q.y) > max)
+ {
+ max = fabs(q.y);
+ flags = 1;
+ if(q.y<0)
+ flags |= 4;
+ }
+ if(fabs(q.z) > max)
+ {
+ max = fabs(q.z);
+ flags = 2;
+ if(q.z<0)
+ flags |= 4;
+ }
+ if(fabs(q.w) > max)
+ {
+ max = fabs(q.w);
+ flags = 3;
+ if(q.w<0)
+ flags |= 4;
+ }
+ int bits = 0;
+ while(bits < 3)
+ {
+ m_Data[indexPos] |= (flags >> bits) << bitPos;
+ int num = std::min( 3-bits, 8-bitPos);
+ bitPos += num;
+ bits += num;
+ if(bitPos == 8)
+ {
+ indexPos++;
+ bitPos = 0;
+ }
+ }
+ for(int j=0;j<4;j++)
+ {
+ if((flags&3) != j)
+ {
+ int bitSize = (((flags&3)+1)%4 == j)?9:10;
+ float scaled = (q[j] + 1) * 0.5;
+ if(scaled < 0) scaled = 0;
+ if(scaled > 1) scaled = 1;
+
+ UInt32 x = UInt32(scaled * ((1 << bitSize) - 1));
+
+ bits = 0;
+ while(bits < bitSize)
+ {
+ m_Data[indexPos] |= (x >> bits) << bitPos;
+ int num = std::min( bitSize-bits, 8-bitPos);
+ bitPos += num;
+ bits += num;
+ if(bitPos == 8)
+ {
+ indexPos++;
+ bitPos = 0;
+ }
+ }
+ }
+ }
+ }
+}
+
+void PackedQuatVector::UnpackQuats(Quaternionf *data)
+{
+ int indexPos = 0;
+ int bitPos = 0;
+
+ for(int i=0; i<m_NumItems; i++)
+ {
+ UInt32 flags = 0;
+
+ int bits = 0;
+ while(bits < 3)
+ {
+ flags |= (m_Data[indexPos] >> bitPos) << bits;
+ int num = std::min( 3-bits, 8-bitPos);
+ bitPos += num;
+ bits += num;
+ if(bitPos == 8)
+ {
+ indexPos++;
+ bitPos = 0;
+ }
+ }
+ flags &= 7;
+
+
+ Quaternionf &q = data[i];
+ float sum = 0;
+ for(int j=0;j<4;j++)
+ {
+ if((flags&3) != j)
+ {
+ int bitSize = (((flags&3)+1)%4 == j)?9:10;
+ UInt32 x = 0;
+
+ bits = 0;
+ while(bits < bitSize)
+ {
+ x |= (m_Data[indexPos] >> bitPos) << bits;
+ int num = std::min( bitSize-bits, 8-bitPos);
+ bitPos += num;
+ bits += num;
+ if(bitPos == 8)
+ {
+ indexPos++;
+ bitPos = 0;
+ }
+ }
+ x &= (1 << bitSize) - 1;
+ q[j] = (x / (0.5 * ((1 << (bitSize)) - 1))) - 1;
+ sum += sqr(q[j]);
+ }
+ }
+
+ int lastComponent = flags&3;
+ q[lastComponent] = FastSqrt(1 - sum);
+ if(flags & 4)
+ q[lastComponent] = -q[lastComponent];
+ }
+}
+
+void CompressedMesh::Compress(Mesh &src, int compression)
+{
+ int numVertices = src.GetVertexCount();
+
+ int vertexBits = 0;
+ switch(compression)
+ {
+ case kMeshCompressionHigh: vertexBits = 10; break;
+ case kMeshCompressionMed: vertexBits = 16; break;
+ case kMeshCompressionLow: vertexBits = 20; break;
+ }
+ m_Vertices.PackFloats((float*)src.GetChannelPointer(kShaderChannelVertex), 3, src.GetStride (kShaderChannelVertex), numVertices, vertexBits, false);
+
+ //Possible optimization: use Edgebreaker algorithm
+ //for 1.8 bits per triangle connectivity information
+ //http://www.gvu.gatech.edu/~jarek/edgebreaker/eb/
+
+ int numIndices = src.m_IndexBuffer.size();
+ numIndices/=2;
+
+ m_Triangles.PackInts<UInt16>((UInt16*)&src.m_IndexBuffer[0],numIndices);
+
+ if(src.IsAvailable(kShaderChannelTexCoord0))
+ {
+ int uvBits = 0;
+ switch(compression)
+ {
+ case kMeshCompressionHigh: uvBits = 8; break;
+ case kMeshCompressionMed: uvBits = 10; break;
+ case kMeshCompressionLow: uvBits = 16; break;
+ }
+ if(src.IsAvailable(kShaderChannelTexCoord1))
+ {
+ Vector2f *uv12 = new Vector2f[numVertices*2];
+ src.ExtractUvArray(0, uv12);
+ src.ExtractUvArray(1, uv12 + numVertices);
+ m_UV.PackFloats(&uv12->x, 2, sizeof(Vector2f), numVertices*2, uvBits, true);
+ delete[] uv12;
+ }
+ else
+ m_UV.PackFloats((float*)src.GetChannelPointer (kShaderChannelTexCoord0), 2, src.GetStride (kShaderChannelTexCoord0), numVertices, uvBits, true);
+ }
+ else if(src.IsAvailable(kShaderChannelTexCoord1))
+ ErrorString( "Mesh compression doesn't work on Meshes wich only have a UV1 channel but no UV0 channel. UVs will be dropped." );
+
+ if(src.IsAvailable (kShaderChannelNormal))
+ {
+ int normalBits = 0;
+ switch(compression)
+ {
+ case kMeshCompressionHigh: normalBits = 6; break;
+ case kMeshCompressionMed: normalBits = 8; break;
+ case kMeshCompressionLow: normalBits = 8; break;
+ }
+
+ float *normals = new float[numVertices*2];
+ UInt32 *signs = new UInt32[numVertices];
+ StrideIterator<Vector3f> n = src.GetNormalBegin ();
+ for(int i=0;i<numVertices; ++i, ++n)
+ {
+ normals[i*2+0] = n->x;
+ normals[i*2+1] = n->y;
+ signs[i] = n->z>0?1:0;
+ }
+ m_Normals.PackFloats(normals, 2, sizeof (float) * 2, numVertices, normalBits, false);
+ m_NormalSigns.PackInts(signs, numVertices);
+ delete[] normals;
+ delete[] signs;
+ }
+
+ if(src.IsAvailable (kShaderChannelTangent))
+ {
+ int normalBits = 0;
+ switch(compression)
+ {
+ case kMeshCompressionHigh: normalBits = 6; break;
+ case kMeshCompressionMed: normalBits = 8; break;
+ case kMeshCompressionLow: normalBits = 8; break;
+ }
+
+ float *tangents = new float[numVertices*2];
+ UInt32 *signs = new UInt32[numVertices*2];
+ StrideIterator<Vector4f> t = src.GetTangentBegin ();
+ for(int i=0;i<numVertices; ++i, ++t)
+ {
+ tangents[i*2+0] = t->x;
+ tangents[i*2+1] = t->y;
+ signs[i*2+0] = t->z>0?1:0;
+ signs[i*2+1] = t->w>0?1:0;
+ }
+ m_Tangents.PackFloats(tangents, 2, sizeof (float) * 2, numVertices, normalBits, false);
+ m_TangentSigns.PackInts(signs, numVertices*2);
+ delete[] tangents;
+ delete[] signs;
+ }
+
+ // TODO: do an actual compression
+ if(src.IsAvailable (kShaderChannelColor))
+ {
+ dynamic_array<UInt32> tempColors (numVertices, kMemTempAlloc);
+ std::transform (src.GetColorBegin (), src.GetColorEnd (), tempColors.begin (), OpColorRGBA32ToUInt32());
+ m_Colors.PackInts<UInt32> (tempColors.data (), tempColors.size ());
+ }
+
+ BoneInfluence* influence = src.GetBoneWeights();
+ if(influence)
+ {
+ UInt32 *weights = new UInt32[numVertices*3];
+ UInt32 *indices = new UInt32[numVertices*4];
+ int weightPos = 0;
+ int boneIndexPos = 0;
+ for(int i=0;i<numVertices;i++)
+ {
+ int j;
+ int sum = 0;
+
+ //As all four bone weights always add up to 1, we can always calculate the fourth one
+ // by subtracting the other three from 1. So we don't need to store it.
+
+ //Furthermore, once the weights we stored add up to 1, we don't need to store further
+ //weights or indices, as these will necessarily be zero. This is often the case, as many
+ //vertices have only the first weight set to one, and all others to zero.
+
+ //find last non-zero entry -- we don't need to store those after this.
+ int lastNonZero;
+ for(lastNonZero=3;lastNonZero>0&&influence[i].weight[lastNonZero]==0;lastNonZero--)
+ {}
+
+
+ for(j=0;j<3 && j<=lastNonZero && sum<31;j++)
+ {
+ weights[weightPos] = UInt32(influence[i].weight[j] * 31);
+ indices[boneIndexPos++] = influence[i].boneIndex[j];
+ sum += weights[weightPos++];
+ }
+ if(lastNonZero<3)
+ {
+ //we stored less then 3 weights, but they don't add up to one, due to quantization
+ //inprecision.
+ //Add the difference, so the math works out on decompression.
+ if(sum<31)
+ weights[weightPos-1] += 31-sum;
+ }
+
+ //we stored three weights, but they don't add up to one. we don't need to store the fourth weight
+ //(as it can be calculated from the other three), but we need the bone index.
+ else if(sum<31)
+ indices[boneIndexPos++] = influence[i].boneIndex[j];
+ }
+
+ m_Weights.PackInts(weights, weightPos);
+ m_BoneIndices.PackInts(indices, boneIndexPos);
+
+ delete[] weights;
+ delete[] indices;
+ }
+}
+
+void CompressedMesh::Decompress(Mesh &src)
+{
+ int numIndices = m_Triangles.Count();
+ src.m_IndexBuffer.resize(numIndices * 2);
+ m_Triangles.UnpackInts<UInt16>((UInt16*)&src.m_IndexBuffer[0]);
+
+ int numVertices = m_Vertices.Count()/3;
+ unsigned decompressedFormat = 0;
+ if (m_Vertices.Count ()) decompressedFormat |= VERTEX_FORMAT1(Vertex);
+ if (m_Normals.Count()) decompressedFormat |= VERTEX_FORMAT1(Normal);
+ if (m_UV.Count()) decompressedFormat |= VERTEX_FORMAT1(TexCoord0);
+ if (m_UV.Count() == numVertices * 4) decompressedFormat |= VERTEX_FORMAT1(TexCoord1);
+ if (m_Tangents.Count()) decompressedFormat |= VERTEX_FORMAT1(Tangent);
+ if (m_Colors.Count()) decompressedFormat |= VERTEX_FORMAT1(Color);
+
+ src.ResizeVertices(numVertices, decompressedFormat);
+ Assert (src.GetVertexCount () == numVertices);
+
+ m_Vertices.UnpackFloats((float*)src.GetChannelPointer (kShaderChannelVertex), 3, src.GetStride (kShaderChannelVertex));
+
+ if(m_UV.Count())
+ {
+ m_UV.UnpackFloats((float*)src.GetChannelPointer (kShaderChannelTexCoord0), 2, src.GetStride (kShaderChannelTexCoord0), 0, numVertices);
+
+ if(m_UV.Count()==numVertices * 4)
+ {
+ m_UV.UnpackFloats((float*)src.GetChannelPointer (kShaderChannelTexCoord1), 2, src.GetStride (kShaderChannelTexCoord1), numVertices*2, numVertices);
+ }
+ }
+
+ // TODO: This never gets written. Unity 3.4 and 3.5 never wrote this data.
+ // Most likely no version ever did. Remove code and bindpose serialization.
+ if(m_BindPoses.Count())
+ {
+ src.m_Bindpose.resize_initialized(m_BindPoses.Count()/16);
+ m_BindPoses.UnpackFloats(src.m_Bindpose[0].m_Data, 16, sizeof(float) * 16);
+ }
+
+ if(m_Normals.Count())
+ {
+ float *normalData = new float[m_Normals.Count()];
+ UInt32 *signs = new UInt32[m_NormalSigns.Count()];
+
+ m_Normals.UnpackFloats(normalData, 2, sizeof(float) * 2);
+ m_NormalSigns.UnpackInts(signs);
+
+ StrideIterator<Vector3f> n = src.GetNormalBegin ();
+ for(int i=0;i<m_Normals.Count()/2; ++i, ++n)
+ {
+ n->x = normalData[i*2+0];
+ n->y = normalData[i*2+1];
+ float zsqr = 1 - sqr(n->x) - sqr(n->y);
+ if(zsqr >= 0)
+ n->z = FastSqrt( zsqr );
+ else
+ {
+ n->z = 0;
+ *n = Normalize(*n);
+ }
+ if(signs[i]==0)
+ n->z = -n->z;
+ }
+
+ delete[] normalData;
+ delete[] signs;
+ }
+
+ if(m_Tangents.Count())
+ {
+ float *tangentData = new float[m_Tangents.Count()];
+ UInt32 *signs = new UInt32[m_TangentSigns.Count()];
+
+ m_Tangents.UnpackFloats(tangentData, 2, sizeof(float) * 2);
+ m_TangentSigns.UnpackInts(signs);
+
+ StrideIterator<Vector4f> t = src.GetTangentBegin ();
+ for(int i=0;i<m_Tangents.Count()/2; ++i, ++t)
+ {
+ t->x = tangentData[i*2+0];
+ t->y = tangentData[i*2+1];
+ float zsqr = 1-sqr(tangentData[i*2+0])-sqr(tangentData[i*2+1]);
+ if(zsqr >= 0.0f)
+ t->z = FastSqrt( zsqr );
+ else
+ {
+ t->z = 0;
+ *(Vector3f*)(&*t) = Normalize(*(Vector3f*)(&*t));
+ }
+ if(signs[i*2+0]==0)
+ t->z = -t->z;
+
+ t->w = signs[i*2+1]?1.0:-1.0;
+ }
+
+ delete[] tangentData;
+ delete[] signs;
+ }
+
+ // TODO: do an actual compression
+ if (m_Colors.Count())
+ {
+ dynamic_array<UInt32> tempColors (m_Colors.Count (), kMemTempAlloc);
+ m_Colors.UnpackInts<UInt32> (tempColors.data ());
+ Assert (tempColors.size () == src.GetVertexCount ());
+ strided_copy ((ColorRGBA32*)tempColors.begin (), (ColorRGBA32*)tempColors.end (), src.GetColorBegin ());
+ }
+
+ if(m_Weights.Count())
+ {
+ UInt32 *weights = new UInt32[m_Weights.Count()];
+ m_Weights.UnpackInts(weights);
+ UInt32 *boneIndices = new UInt32[m_BoneIndices.Count()];
+ m_BoneIndices.UnpackInts(boneIndices);
+ src.m_Skin.resize_uninitialized(numVertices);
+ int bonePos = 0;
+ int boneIndexPos = 0;
+ int j=0;
+ int sum = 0;
+
+ for(int i=0;i<m_Weights.Count();i++)
+ {
+ //read bone index and weight.
+ src.m_Skin[bonePos].weight[j] = weights[i]/31.0;
+ src.m_Skin[bonePos].boneIndex[j] = boneIndices[boneIndexPos++];
+ j++;
+ sum += weights[i];
+
+ //the weights add up to one. fill the rest for this vertex with zero, and continue with next one.
+ if(sum >= 31)
+ {
+ for(;j<4;j++)
+ {
+ src.m_Skin[bonePos].weight[j] = 0;
+ src.m_Skin[bonePos].boneIndex[j] = 0;
+ }
+ bonePos++;
+ j = 0;
+ sum = 0;
+ }
+ //we read three weights, but they don't add up to one. calculate the fourth one, and read
+ //missing bone index. continue with next vertex.
+ else if(j==3)
+ {
+ src.m_Skin[bonePos].weight[j] = (31-sum)/31.0;
+ src.m_Skin[bonePos].boneIndex[j] = boneIndices[boneIndexPos++];
+ bonePos++;
+ j = 0;
+ sum = 0;
+ }
+ }
+
+ delete[] weights;
+ delete[] boneIndices;
+ }
+}
+
+template <class T> void CompressedAnimationCurve::CompressTimeKeys(AnimationCurveTpl<T> &src)
+{
+ int numKeys = src.GetKeyCount();
+
+ float minTime=0;
+ for(int i=0;i<numKeys;i++)
+ {
+ float t = src.GetKey(i).time;
+ if(t < minTime)
+ {
+ //negative time key. offset all keys by this, so math doesn't break - but it's still wrong.
+ minTime = t;
+ }
+ }
+
+
+ UInt32 *times = new UInt32[numKeys];
+ UInt32 t=0;
+ for(int i=0;i<numKeys;i++)
+ {
+ times[i] = UInt32((src.GetKey(i).time - minTime) * 100);
+ times[i] -= t;
+ t += times[i];
+ }
+
+ m_Times.PackInts(times, numKeys);
+
+ delete[] times;
+}
+
+template <class T> void CompressedAnimationCurve::DecompressTimeKeys(AnimationCurveTpl<T> &src)
+{
+ int numKeys = m_Times.Count();
+ UInt32 *times = new UInt32[numKeys];
+ m_Times.UnpackInts(times);
+
+ UInt32 t=0;
+
+ src.ResizeUninitialized(numKeys);
+
+ for(int i=0;i<numKeys;i++)
+ {
+ t+=times[i];
+ src.GetKey(i).time = t*0.01;
+ }
+ delete[] times;
+}
+
+void CompressedAnimationCurve::CompressQuatCurve(AnimationClip::QuaternionCurve &src)
+{
+ CompressTimeKeys(src.curve);
+ int numKeys = src.curve.GetKeyCount();
+
+ Quaternionf *qkeys = new Quaternionf[numKeys];
+ for(int i=0;i<numKeys;i++)
+ qkeys[i] = src.curve.GetKey(i).value;
+ m_Values.PackQuats(qkeys, numKeys);
+
+ delete[] qkeys;
+
+ bool same = true;
+
+ for(int i=0;i<numKeys && same;i++)
+ {
+ Quaternionf &q1 = src.curve.GetKey(i).inSlope;
+ Quaternionf &q2 = src.curve.GetKey(i).inSlope;
+ if(q1.x!=q2.x)
+ same = false;
+ if(q1.y!=q2.y)
+ same = false;
+ if(q1.z!=q2.z)
+ same = false;
+ if(q1.w!=q2.w)
+ same = false;
+ }
+
+ float *keys = new float[numKeys*8];
+ for(int i=0;i<numKeys;i++)
+ {
+ Quaternionf q = src.curve.GetKey(i).inSlope;
+ keys[i*4+0] = q.x;
+ keys[i*4+1] = q.y;
+ keys[i*4+2] = q.z;
+ keys[i*4+3] = q.w;
+ q = src.curve.GetKey(i).outSlope;
+ keys[(i+numKeys)*4+0] = q.x;
+ keys[(i+numKeys)*4+1] = q.y;
+ keys[(i+numKeys)*4+2] = q.z;
+ keys[(i+numKeys)*4+3] = q.w;
+ }
+
+ //if in and out slopes are all the same, pack only the first of the two.
+ if(same)
+ m_Slopes.PackFloats(keys, 1, sizeof(float), numKeys * 4, 6, false);
+ else
+ m_Slopes.PackFloats(keys, 1, sizeof(float), numKeys * 8, 6, false);
+
+ delete[] keys;
+
+ m_PreInfinity = src.curve.GetPreInfinityInternal();
+ m_PostInfinity = src.curve.GetPostInfinityInternal();
+ m_Path = src.path;
+}
+
+void CompressedAnimationCurve::DecompressQuatCurve(AnimationClip::QuaternionCurve &src)
+{
+ DecompressTimeKeys(src.curve);
+ int numKeys = m_Values.Count();
+
+ Quaternionf *qkeys = new Quaternionf[numKeys];
+ m_Values.UnpackQuats(qkeys);
+ for(int i=0;i<numKeys;i++)
+ src.curve.GetKey(i).value = qkeys[i];
+ delete[] qkeys;
+
+ float *keys = new float[numKeys*8];
+ m_Slopes.UnpackFloats(keys, 1, sizeof(float));
+
+ //are there seperate in and out slopes?
+ int offs = 0;
+ if(m_Slopes.Count() == numKeys*8)
+ offs = numKeys;
+ for(int i=0;i<numKeys;i++)
+ {
+ src.curve.GetKey(i).inSlope.x = keys[i*4+0];
+ src.curve.GetKey(i).inSlope.y = keys[i*4+1];
+ src.curve.GetKey(i).inSlope.z = keys[i*4+2];
+ src.curve.GetKey(i).inSlope.w = keys[i*4+3];
+ src.curve.GetKey(i).outSlope.x = keys[(i+offs)*4+0];
+ src.curve.GetKey(i).outSlope.y = keys[(i+offs)*4+1];
+ src.curve.GetKey(i).outSlope.z = keys[(i+offs)*4+2];
+ src.curve.GetKey(i).outSlope.w = keys[(i+offs)*4+3];
+ }
+ delete[] keys;
+
+ src.curve.SetPreInfinityInternal( m_PreInfinity );
+ src.curve.SetPostInfinityInternal( m_PostInfinity );
+ src.path = m_Path;
+}
diff --git a/Runtime/Filters/Mesh/CompressedMesh.h b/Runtime/Filters/Mesh/CompressedMesh.h
new file mode 100644
index 0000000..cf2f01c
--- /dev/null
+++ b/Runtime/Filters/Mesh/CompressedMesh.h
@@ -0,0 +1,175 @@
+#ifndef COMPRESSEDMESH_H
+#define COMPRESSEDMESH_H
+
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Animation/AnimationClip.h"
+class Mesh;
+class AnimationClip;
+
+enum
+{
+ kMeshCompressionOff = 0,
+ kMeshCompressionLow = 1,
+ kMeshCompressionMed = 2,
+ kMeshCompressionHigh = 3,
+};
+
+typedef std::vector<UInt8> DataVector;
+
+class PackedFloatVector
+{
+public:
+ DECLARE_SERIALIZE (PackedBitVector)
+
+ PackedFloatVector() { m_NumItems = 0; m_Range = 0; m_Start = 0; m_BitSize = 0; }
+
+ void PackFloats(float *data, int chunkSize, int chunkStride, int chunkCount, int bitSize, bool adjustBitSize);
+ void UnpackFloats(float *data, int chunkSize, int chunkStride, int start = 0, int count = -1);
+ int Count() {return m_NumItems;}
+
+private:
+ UInt32 m_NumItems;
+ float m_Range;
+ float m_Start;
+ UInt8 m_BitSize;
+ std::vector<UInt8> m_Data;
+};
+
+class PackedIntVector
+{
+public:
+ DECLARE_SERIALIZE (PackedBitVector)
+
+ PackedIntVector() { m_NumItems = 0; m_BitSize = 0; }
+
+ template <class IntSize> void PackInts(IntSize *data, int numItems);
+ template <class IntSize> void UnpackInts(IntSize *data);
+ int Count() {return m_NumItems;}
+
+private:
+ UInt32 m_NumItems;
+ UInt8 m_BitSize;
+ std::vector<UInt8> m_Data;
+};
+
+class PackedQuatVector
+{
+public:
+ DECLARE_SERIALIZE (PackedBitVector)
+
+ PackedQuatVector() {m_NumItems = 0;}
+
+ void PackQuats(Quaternionf *data, int numItems);
+ void UnpackQuats(Quaternionf *data);
+ int Count() {return m_NumItems;}
+
+private:
+ UInt32 m_NumItems;
+ std::vector<UInt8> m_Data;
+};
+
+class CompressedMesh
+{
+public:
+ DECLARE_SERIALIZE (CompressedMesh)
+
+ void Compress(Mesh &src, int quality);
+ void Decompress(Mesh &src);
+
+private:
+ PackedFloatVector m_Vertices;
+ PackedFloatVector m_UV;
+
+ // TODO: This never gets written. Unity 3.4 and 3.5 never wrote this data.
+ // Most likely no version ever did. Remove code and bindpose serialization.
+ PackedFloatVector m_BindPoses;
+
+ PackedFloatVector m_Normals;
+ PackedIntVector m_NormalSigns;
+ PackedFloatVector m_Tangents;
+ PackedIntVector m_TangentSigns;
+ PackedIntVector m_Weights;
+ PackedIntVector m_BoneIndices;
+ PackedIntVector m_Triangles;
+ PackedIntVector m_Colors;
+};
+
+template<class TransferFunc>
+void PackedFloatVector::Transfer (TransferFunc& transfer) {
+ TRANSFER ( m_NumItems );
+ TRANSFER( m_Range );
+ TRANSFER( m_Start );
+ TRANSFER( m_Data );
+ TRANSFER( m_BitSize );
+ transfer.Align();
+}
+
+template<class TransferFunc>
+void PackedIntVector::Transfer (TransferFunc& transfer) {
+ TRANSFER( m_NumItems );
+ TRANSFER( m_Data );
+ TRANSFER( m_BitSize );
+ transfer.Align();
+}
+
+template<class TransferFunc>
+void PackedQuatVector::Transfer (TransferFunc& transfer) {
+ TRANSFER( m_NumItems );
+ TRANSFER( m_Data );
+ transfer.Align();
+}
+
+template<class TransferFunc>
+void CompressedMesh::Transfer (TransferFunc& transfer) {
+ TRANSFER( m_Vertices );
+ TRANSFER( m_UV );
+ TRANSFER( m_BindPoses );
+ TRANSFER( m_Normals );
+ TRANSFER( m_Tangents );
+ TRANSFER( m_Weights );
+ TRANSFER( m_NormalSigns );
+ TRANSFER( m_TangentSigns );
+ TRANSFER( m_BoneIndices );
+ TRANSFER( m_Triangles );
+ TRANSFER( m_Colors );
+}
+
+class CompressedAnimationCurve
+{
+public:
+ DECLARE_SERIALIZE (CompressedAnimationCurve)
+
+ CompressedAnimationCurve() { m_PreInfinity = 0; m_PostInfinity = 0; }
+
+ void CompressQuatCurve(AnimationClip::QuaternionCurve &src);
+ void DecompressQuatCurve(AnimationClip::QuaternionCurve &src);
+
+private:
+
+ template <class T> void CompressTimeKeys(AnimationCurveTpl<T> &src);
+ template <class T> void DecompressTimeKeys(AnimationCurveTpl<T> &src);
+
+ PackedIntVector m_Times;
+ PackedQuatVector m_Values;
+ PackedFloatVector m_Slopes;
+
+ int m_PreInfinity;
+ int m_PostInfinity;
+
+ UnityStr m_Path;
+};
+
+template<class TransferFunc>
+void CompressedAnimationCurve::Transfer (TransferFunc& transfer) {
+
+ TRANSFER( m_Path );
+
+ TRANSFER( m_Times );
+ TRANSFER( m_Values );
+ TRANSFER( m_Slopes );
+
+ TRANSFER( m_PreInfinity );
+ TRANSFER( m_PostInfinity );
+}
+
+#endif
diff --git a/Runtime/Filters/Mesh/LodMesh.cpp b/Runtime/Filters/Mesh/LodMesh.cpp
new file mode 100644
index 0000000..fc5dca8
--- /dev/null
+++ b/Runtime/Filters/Mesh/LodMesh.cpp
@@ -0,0 +1,2344 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "LodMesh.h"
+#include "Runtime/Utilities/vector_utility.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Math/FloatConversion.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Graphics/TriStripper.h"
+#include "MeshUtility.h"
+#include "Runtime/Geometry/TangentSpaceCalculation.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/Serialize/TransferUtility.h"
+#include "Runtime/Serialize/SwapEndianArray.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Camera/IntermediateRenderer.h"
+#include "Runtime/Filters/Mesh/MeshRenderer.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Utilities/UniqueIDGenerator.h"
+#if UNITY_XENON
+#include "PlatformDependent/Xbox360/Source/GfxDevice/GfxXenonVBO.h"
+#endif
+#include "Runtime/GfxDevice/GfxDeviceConfigure.h"
+
+#if UNITY_FLASH
+#include <limits.h>
+#define FLT_MAX __FLT_MAX__
+#define FLT_MIN __FLT_MIN__
+#endif
+
+#if UNITY_EDITOR
+# include "Editor/Src/BuildPipeline/PrepareMeshDataForBuildTarget.h"
+# include "Runtime/Camera/RenderLoops/RenderLoopPrivate.h"
+# include "Runtime/Misc/Player.h"
+#endif
+
+
+///* Checkbox in mesh importer that allows you have mesh access (Done)
+///* Default for new importers is to have mesh access enabled (done)
+///* Error Messages when acessing data although you shouldn't be allowed (--)
+///* MeshColliders / SkinnedMeshes / non-uniform scale. Forces meshes to be non-readable. (Done)
+
+
+///* MeshCollider with no-access allowed. Does it work / no errors
+///* MeshCollider with no-access allowed, mesh is assigned from script. Does it give an error in editor & player
+///* MeshCollider with no-access allowed, mesh is scaled at runtime does it give an error
+///* MeshCollider with no-access allowed, mesh is scaled in scene. Does it work without errors.
+///* Mesh data accessed from script, does it give an error.
+
+
+
+static char const* kMeshAPIErrorMessage =
+"Mesh.%s is out of bounds. The supplied array needs to be the same size as the Mesh.vertices array.";
+
+
+static UniqueIDGenerator s_MeshIDGenerator;
+
+
+// The Mesh class contains one of these for every Material that is bound to it.
+struct DeprecatedMeshData
+{
+ std::vector<Face> faces; // Indices for specific faces
+ std::vector <unsigned short> strips; // A list of triangle strips
+ int triangleCount;
+ DECLARE_SERIALIZE_NO_PPTR (MeshData)
+};
+
+template<class TransferFunc>
+void DeprecatedMeshData::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (faces);
+ TRANSFER (strips);
+ TRANSFER(triangleCount);
+}
+
+struct DeprecatedLOD
+{
+ vector<DeprecatedMeshData> m_MeshData;
+
+ DECLARE_SERIALIZE (LOD)
+};
+
+template<class TransferFunction>
+void DeprecatedLOD::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (m_MeshData);
+}
+
+static void LoadDeprecatedMeshData (Mesh& mesh, vector<DeprecatedLOD> &lods)
+{
+ mesh.GetIndexBuffer().clear();
+ mesh.GetSubMeshes().clear();
+
+ if (lods.empty())
+ return;
+
+ DeprecatedLOD& lod = lods.front();
+
+ mesh.SetSubMeshCount(lod.m_MeshData.size());
+ for (int i=0;i<lod.m_MeshData.size();i++)
+ {
+ DeprecatedMeshData& oldMeshData = lod.m_MeshData[i];
+ if (oldMeshData.faces.size())
+ mesh.SetIndicesComplex (&oldMeshData.faces[0].v1, oldMeshData.faces.size()*3, i, kPrimitiveTriangles, Mesh::k16BitIndices);
+ else
+ {
+ UNITY_TEMP_VECTOR(UInt16) triangles;
+ Destripify(&oldMeshData.strips[0], oldMeshData.strips.size(), triangles);
+ mesh.SetIndicesComplex (&triangles[0], triangles.size(), i, kPrimitiveTriangles, Mesh::k16BitIndices);
+ }
+ }
+}
+
+
+using namespace std;
+
+Mesh::Mesh (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_ChannelsInVBO(0)
+, m_VerticesDirty(true)
+, m_IndicesDirty(true)
+, m_IsDynamic(false)
+, m_HideFromRuntimeStats(false)
+, m_VertexColorsSwizzled(false)
+, m_MeshUsageFlags(0)
+, m_LocalAABB(Vector3f::zero, Vector3f::zero)
+, m_VBO(NULL)
+, m_InternalMeshID (0)
+, m_Skin (label)
+, m_CachedSkin2 (label)
+, m_CachedSkin1 (label)
+, m_CachedBonesAABB(label)
+, m_Bindpose(label)
+, m_BonePathHashes(label)
+, m_RootBonePathHash(0)
+{
+ m_MaxBoneIndex = -1;
+ SubMesh sub;
+ m_SubMeshes.push_back(sub);
+
+ m_MeshCompression = kMeshCompressionOff;
+ m_StreamCompression = kStreamCompressionDefault;
+ m_IsReadable = true;
+ m_KeepVertices = false;
+ m_KeepIndices = false;
+
+#if UNITY_EDITOR
+ m_MeshOptimized = false;
+#endif
+
+#if ENABLE_MULTITHREADED_CODE
+ m_CurrentCPUFence = 0;
+ m_WaitOnCPUFence = false;
+#endif
+
+ m_InternalMeshID = 0;
+}
+
+Mesh::~Mesh ()
+{
+ MainThreadCleanup ();
+}
+
+bool Mesh::MainThreadCleanup ()
+{
+ WaitOnRenderThreadUse();
+ NotifyObjectUsers( kDidDeleteMesh );
+ m_IntermediateUsers.Notify( kImNotifyAssetDeleted );
+
+ m_CollisionMesh.Cleanup();
+
+ if (m_VBO)
+ {
+ GetGfxDevice().DeleteVBO(m_VBO);
+ m_VBO = NULL;
+ }
+
+ if (m_InternalMeshID != 0)
+ {
+ s_MeshIDGenerator.RemoveID (m_InternalMeshID);
+ m_InternalMeshID = 0;
+ }
+
+ return true;
+}
+
+void Mesh::LoadDeprecatedTangentData (Mesh& mesh, DeprecatedTangentsArray &inTangents)
+{
+ int count = inTangents.size();
+ unsigned needChannels = m_VertexData.GetChannelMask () | VERTEX_FORMAT2(Normal, Tangent);
+ if (count != GetVertexCount () || m_VertexData.GetChannelMask () != needChannels)
+ ResizeVertices (count, needChannels);
+
+ Assert (GetVertexCount () == count);
+
+ StrideIterator<Vector3f> normals = GetNormalBegin ();
+ StrideIterator<Vector4f> tangents = GetTangentBegin ();
+
+ for(int i=0;i<count; ++i, ++normals, ++tangents)
+ {
+ *normals = inTangents[i].normal;
+ *tangents = Vector4f(inTangents[i].tangent.x,inTangents[i].tangent.y,inTangents[i].tangent.z,inTangents[i].handedness);
+ }
+}
+
+void Mesh::SwizzleVertexColorsIfNeeded ()
+{
+ // Early out if color are already in the right format
+ if (gGraphicsCaps.needsToSwizzleVertexColors == m_VertexColorsSwizzled)
+ return;
+
+ // Due to runtime GfxDevice switching we might need to unswizzle vertex colors (case 562695)
+ if (m_VertexColorsSwizzled)
+ {
+ std::transform(GetColorBegin(), GetColorEnd(), GetColorBegin(), UnswizzleColorForPlatform);
+ m_VertexColorsSwizzled = false;
+ }
+ else
+ {
+ std::transform(GetColorBegin(), GetColorEnd(), GetColorBegin(), SwizzleColorForPlatform);
+ m_VertexColorsSwizzled = true;
+ }
+}
+
+void Mesh::ExtractVertexArray (Vector3f* destination) const
+{
+ StrideIterator<Vector3f> v = GetVertexBegin ();
+ for (Vector3f* end = destination + GetVertexCount(); destination != end; ++v, ++destination)
+ *destination = *v;
+}
+
+void Mesh::ExtractNormalArray (Vector3f* destination) const
+{
+ StrideIterator<Vector3f> n = GetNormalBegin ();
+ for (Vector3f* end = destination + GetVertexCount(); destination != end; ++n, ++destination)
+ *destination = *n;
+}
+
+void Mesh::ExtractColorArray (ColorRGBA32* destination) const
+{
+ if (m_VertexColorsSwizzled)
+ std::transform(GetColorBegin(), GetColorEnd(), destination, UnswizzleColorForPlatform);
+ else
+ std::copy(GetColorBegin(), GetColorEnd(), destination);
+}
+
+void Mesh::ExtractColorArrayConverting (ColorRGBAf* destination) const
+{
+ if (m_VertexColorsSwizzled)
+ std::transform(GetColorBegin(), GetColorEnd(), destination, UnswizzleColorForPlatform);
+ else
+ std::copy(GetColorBegin(), GetColorEnd(), destination);
+}
+
+void Mesh::ExtractUvArray (int uvIndex, Vector2f* destination) const
+{
+ StrideIterator<Vector2f> uv = GetUvBegin (uvIndex);
+ for (Vector2f* end = destination + GetVertexCount(); destination != end; ++uv, ++destination)
+ *destination = *uv;
+}
+
+void Mesh::ExtractTangentArray (Vector4f* destination) const
+{
+ StrideIterator<Vector4f> t = GetTangentBegin ();
+ for (Vector4f* end = destination + GetVertexCount(); destination != end; ++t, ++destination)
+ *destination = *t;
+}
+
+
+UInt32 Mesh::ResizeVertices (size_t count, UInt32 shaderChannels, const VertexStreamsLayout& streams, const VertexChannelsLayout& channels)
+{
+ Assert (count <= std::numeric_limits<UInt16>::max());
+
+ UInt32 prevChannels = m_VertexData.GetChannelMask();
+
+ if (m_VertexData.GetVertexCount() != count ||
+ m_VertexData.GetChannelMask() != shaderChannels ||
+ !m_VertexData.ConformsToStreamsLayout(streams) ||
+ !m_VertexData.ConformsToChannelsLayout(channels))
+ {
+ WaitOnRenderThreadUse();
+
+ SET_ALLOC_OWNER(this);
+ m_VertexData.Resize(count, shaderChannels, streams, channels);
+
+ if (!m_Skin.empty ())
+ m_Skin.resize_initialized (count, BoneInfluence());
+ }
+
+ return m_VertexData.GetChannelMask() & ~prevChannels;
+}
+
+
+UInt32 Mesh::FormatVertices (UInt32 shaderChannels)
+{
+ return ResizeVertices(GetVertexCount(), shaderChannels);
+}
+
+void Mesh::InitChannelsToDefault (unsigned begin, unsigned count, unsigned shaderChannels)
+{
+ if (shaderChannels & VERTEX_FORMAT1(Vertex))
+ std::fill (GetVertexBegin () + begin, GetVertexBegin () + begin + count, Vector3f (0,0,0));
+ if (shaderChannels & VERTEX_FORMAT1(Normal))
+ std::fill (GetNormalBegin () + begin, GetNormalBegin () + begin + count, Vector3f (0,0,0));
+ if (shaderChannels & VERTEX_FORMAT1(Color))
+ std::fill (GetColorBegin () + begin, GetColorBegin () + begin + count, ColorRGBA32 (0xffffffff));
+ if (shaderChannels & VERTEX_FORMAT1(TexCoord0))
+ std::fill (GetUvBegin (0) + begin, GetUvBegin (0) + begin + count, Vector2f (0,0));
+ if (shaderChannels & VERTEX_FORMAT1(Tangent))
+ std::fill (GetTangentBegin () + begin, GetTangentBegin () + begin + count, Vector4f (0,0,0,0));
+
+ if (shaderChannels & VERTEX_FORMAT1(TexCoord1))
+ {
+ if( GetAvailableChannels () & VERTEX_FORMAT1(TexCoord0) )
+ std::copy (GetUvBegin (0) + begin, GetUvBegin (0) + begin + count, GetUvBegin (1) + begin);
+ else
+ std::fill (GetUvBegin (1) + begin, GetUvBegin (1) + begin + count, Vector2f (0,0));
+ }
+}
+
+namespace
+{
+ bool IsStripValid(const Mesh::TemporaryIndexContainer& triangles, const Mesh::TemporaryIndexContainer& newStrip)
+ {
+ int invalidTriangleCount = 0;
+ for (int j = 0; j < triangles.size(); j += 3)
+ {
+ int i0 = triangles[j + 0];
+ int i1 = triangles[j + 1];
+ int i2 = triangles[j + 2];
+
+ bool found = false;
+ for (int k = 0; k < newStrip.size() - 2; ++k)
+ {
+ int s0 = newStrip[k + 0];
+ int s1 = newStrip[k + 1];
+ int s2 = newStrip[k + 2];
+
+ if (k&1)
+ std::swap(s1, s2);
+
+ if ((s0 == i0 && s1 == i1 && s2 == i2) ||
+ (s0 == i1 && s1 == i2 && s2 == i0) ||
+ (s0 == i2 && s1 == i0 && s2 == i1))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ ++invalidTriangleCount;
+ }
+
+ AssertMsg(invalidTriangleCount == 0, "Mesh strip is missing %d triangles", invalidTriangleCount);
+ return invalidTriangleCount == 0;
+ }
+}
+
+void Mesh::RecalculateBoundsInternal ()
+{
+ MinMaxAABB minmax;
+ minmax.Init ();
+ for (StrideIterator<Vector3f> it = GetVertexBegin (), end = GetVertexEnd (); it != end; ++it)
+ minmax.Encapsulate (*it);
+
+ // Apply all blendshape targets to bounding volumes
+ if (!m_Shapes.vertices.empty())
+ {
+ StrideIterator<Vector3f> verts = GetVertexBegin ();
+
+ for (int i=0;i<m_Shapes.vertices.size();i++)
+ {
+ Vector3f pos = verts[m_Shapes.vertices[i].index] + m_Shapes.vertices[i].vertex;
+ minmax.Encapsulate (pos);
+ }
+ }
+
+ AABB aabb;
+ if (GetVertexCount ())
+ aabb = minmax;
+ else
+ aabb = AABB (Vector3f::zero, Vector3f::zero);
+
+ m_LocalAABB = aabb;
+
+ for (int submesh = 0; submesh < m_SubMeshes.size(); ++submesh)
+ RecalculateSubmeshBoundsInternal (submesh);
+}
+
+void Mesh::RecalculateSubmeshBoundsInternal (unsigned submesh)
+{
+ MinMaxAABB minmax;
+ minmax.Init ();
+
+ const UInt16* indices = GetSubMeshBuffer16(submesh);
+ StrideIterator<Vector3f> vertices = GetVertexBegin ();
+ for (unsigned int i = 0; i < GetSubMeshFast(submesh).indexCount; i++)
+ minmax.Encapsulate (vertices[indices[i]]);
+
+ AABB aabb;
+ if (GetSubMeshFast(submesh).indexCount > 0)
+ aabb = minmax;
+ else
+ aabb = AABB (Vector3f::zero, Vector3f::zero);
+
+ GetSubMeshFast(submesh).localAABB = aabb;
+}
+
+
+void Mesh::RecalculateBounds ()
+{
+ RecalculateBoundsInternal ();
+
+ SetDirty();
+ NotifyObjectUsers( kDidModifyBounds );
+ m_IntermediateUsers.Notify( kImNotifyBoundsChanged );
+}
+
+void Mesh::RecalculateSubmeshBounds (unsigned submesh)
+{
+ RecalculateSubmeshBoundsInternal (submesh);
+
+ SetDirty();
+ NotifyObjectUsers( kDidModifyBounds );
+ m_IntermediateUsers.Notify( kImNotifyBoundsChanged );
+}
+
+
+void Mesh::Clear (bool keepVertexLayout)
+{
+ WaitOnRenderThreadUse();
+
+ m_SubMeshes.clear();
+ SubMesh sub;
+ m_SubMeshes.push_back(sub);
+
+ ClearBlendShapes (m_Shapes);
+
+ m_IndexBuffer.clear();
+#if UNITY_EDITOR
+ m_MeshOptimized = false;
+#endif
+
+#if UNITY_PS3 || UNITY_EDITOR
+ m_PartitionInfos.clear();
+ m_Partitions.clear();
+#endif
+
+ unsigned prevFormat = m_VertexData.GetChannelMask();
+
+ if (m_VertexData.GetVertexCount() > 0)
+ {
+ // keepVertexLayout added in Unity 3.5.3; keep previous behaviour
+ // for older content for safety.
+ if (keepVertexLayout && IS_CONTENT_NEWER_OR_SAME (kUnityVersion3_5_3_a1))
+ {
+ ResizeVertices (0, prevFormat);
+ }
+ else
+ {
+ VertexData tempVD;
+ swap (tempVD, m_VertexData);
+ }
+ }
+
+ if (!m_Skin.empty())
+ {
+ m_Skin.clear();
+ }
+
+ m_VertexColorsSwizzled = false;
+ ClearSkinCache();
+
+ SetChannelsDirty( prevFormat, true );
+}
+
+IMPLEMENT_CLASS (Mesh)
+IMPLEMENT_OBJECT_SERIALIZE (Mesh)
+
+template <typename Index>
+static void GetVertexBufferRange(const Index* indices, int indexCount, UInt32& fromVertex, UInt32& toVertex)
+{
+ Index a = Index(INT_MAX);
+ Index b = 0;
+ const Index* indicesEnd = indices + indexCount;
+ for (const Index* index = indices; index < indicesEnd; ++index)
+ {
+ a = std::min(a, *index);
+ b = std::max(b, *index);
+ }
+ fromVertex = a;
+ toVertex = b;
+}
+
+void Mesh::ByteSwapIndices ()
+{
+ SwapEndianArray (&m_IndexBuffer[0], kVBOIndexSize, GetTotalndexCount());
+}
+
+template<class T>
+bool ShouldSerializeForBigEndian (T& transfer)
+{
+ bool bigEndian = UNITY_BIG_ENDIAN;
+ if (transfer.ConvertEndianess())
+ bigEndian = !bigEndian;
+ return bigEndian;
+}
+
+void Mesh::DestripifyIndices ()
+{
+ if (m_IndexBuffer.empty() || m_SubMeshes.empty())
+ return;
+
+ int submeshCount = m_SubMeshes.size();
+ bool anyStripped = false;
+ for (size_t i = 0; i < submeshCount; ++i)
+ {
+ if (m_SubMeshes[i].topology == kPrimitiveTriangleStripDeprecated)
+ {
+ anyStripped = true;
+ break;
+ }
+ }
+ if(!anyStripped)
+ return;
+
+ // destripify the stripped submeshes
+ typedef UNITY_TEMP_VECTOR(UInt16) TemporaryIndexContainer;
+
+ std::vector<TemporaryIndexContainer> submeshIndices;
+ submeshIndices.resize(submeshCount);
+ for(int i=0;i<submeshCount;i++)
+ {
+ SubMesh& sm = m_SubMeshes[i];
+ if (sm.topology == kPrimitiveTriangleStripDeprecated)
+ Destripify (GetSubMeshBuffer16(i), sm.indexCount, submeshIndices[i]);
+ else
+ {
+ submeshIndices[i].resize(sm.indexCount);
+ memcpy(&submeshIndices[i][0], GetSubMeshBuffer16(i), sm.indexCount << 1);
+ }
+ }
+
+ SetSubMeshCount(0);
+ SetSubMeshCount(submeshCount);
+
+ for(int i=0;i<submeshCount;i++)
+ SetIndices(&submeshIndices[i][0], submeshIndices[i].size(), i, kPrimitiveTriangles);
+}
+
+bool Mesh::CanAccessFromScript() const
+{
+#if UNITY_EDITOR
+ // Allow editor scripts access even if not allowed in runtime
+ if (!IsInsidePlayerLoop() && !IsInsideRenderLoop())
+ return true;
+#endif
+ return m_IsReadable;
+}
+
+
+template<class TransferFunction>
+void Mesh::Transfer (TransferFunction& transfer)
+{
+ #if SUPPORT_SERIALIZED_TYPETREES
+ // See TransferWorkaround35SerializeFuckup below for comments.
+ // Remove when we can break backwards-compatiblity.
+ if (transfer.GetFlags() & kWorkaround35MeshSerializationFuckup)
+ {
+ TransferWorkaround35SerializeFuckup (transfer);
+ return;
+ }
+ #endif
+
+ Super::Transfer (transfer);
+ transfer.SetVersion (8);
+
+ #if UNITY_EDITOR
+ const UInt32 supportedChannels = transfer.IsWritingGameReleaseData() ? transfer.GetBuildUsage().meshSupportedChannels : 0;
+ const UInt32 meshUsageFlags = transfer.IsWritingGameReleaseData() ? transfer.GetBuildUsage().meshUsageFlags : 0;
+ PrepareMeshDataForBuildTarget prepareMesh(*this, transfer.GetBuildingTarget().platform, supportedChannels, meshUsageFlags);
+ #endif
+
+ bool reswizzleColors = false;
+ if (m_VertexColorsSwizzled)
+ {
+ // Unswizzle colors before serializing
+ std::transform(GetColorBegin(), GetColorEnd(), GetColorBegin(), UnswizzleColorForPlatform);
+ m_VertexColorsSwizzled = false;
+ reswizzleColors = true;
+ }
+
+ transfer.Transfer (m_SubMeshes, "m_SubMeshes", kHideInEditorMask);
+ transfer.Transfer (m_Shapes, "m_Shapes", kHideInEditorMask);
+ transfer.Transfer (m_Bindpose, "m_BindPose", kHideInEditorMask);
+ transfer.Transfer (m_BonePathHashes, "m_BoneNameHashes", kHideInEditorMask);
+ transfer.Transfer (m_RootBonePathHash, "m_RootBoneNameHash", kHideInEditorMask);
+
+ transfer.Transfer (m_MeshCompression, "m_MeshCompression", kHideInEditorMask);
+ transfer.Transfer (m_StreamCompression, "m_StreamCompression", kHideInEditorMask);
+ transfer.Transfer (m_IsReadable, "m_IsReadable", kHideInEditorMask);
+ transfer.Transfer (m_KeepVertices, "m_KeepVertices", kHideInEditorMask);
+ transfer.Transfer (m_KeepIndices, "m_KeepIndices", kHideInEditorMask);
+ transfer.Align();
+
+ // Notice the two codepaths for serialization here.
+ // It is very important to keep both codepaths in sync, otherwise SafeBinaryRead serialization will crash.
+ // Look at kSerializeForPrefabSystem to disable compression when using Transfer to instantiate a Mesh.
+ // Changes to compression can break web content if we recompress at runtime. (case 546159)
+ bool doCompression = m_MeshCompression && !(transfer.GetFlags() & kSerializeForPrefabSystem);
+ if (!doCompression)
+ {
+ if (transfer.ConvertEndianess() && transfer.IsWriting ())
+ ByteSwapIndices();
+
+ transfer.Transfer (m_IndexBuffer, "m_IndexBuffer", kHideInEditorMask);
+
+ if (transfer.ConvertEndianess() && (transfer.IsWriting () || transfer.IsReading ()))
+ ByteSwapIndices();
+
+ transfer.Transfer (m_Skin, "m_Skin", kHideInEditorMask);
+
+ if (transfer.IsVersionSmallerOrEqual (5))
+ {
+ dynamic_array<Vector4f> tangents;
+ dynamic_array<Vector3f> vertices, normals;
+ dynamic_array<Vector2f> uvs, uvs1;
+ dynamic_array<ColorRGBA32> colors;
+
+
+ transfer.Transfer (vertices, "m_Vertices", kHideInEditorMask);
+ transfer.Transfer (uvs, "m_UV", kHideInEditorMask);
+ transfer.Transfer (uvs1, "m_UV1", kHideInEditorMask);
+ transfer.Transfer (tangents, "m_Tangents", kHideInEditorMask);
+ transfer.Transfer (normals, "m_Normals", kHideInEditorMask);
+ transfer.Transfer (colors, "m_Colors", kHideInEditorMask);
+
+ unsigned format = 0;
+ if (!vertices.empty ()) format |= VERTEX_FORMAT1(Vertex);
+ if (!tangents.empty ()) format |= VERTEX_FORMAT1(Tangent);
+ if (!normals.empty ()) format |= VERTEX_FORMAT1(Normal);
+ if (!uvs.empty ()) format |= VERTEX_FORMAT1(TexCoord0);
+ if (!uvs1.empty ()) format |= VERTEX_FORMAT1(TexCoord1);
+ if (!colors.empty ()) format |= VERTEX_FORMAT1(Color);
+
+ size_t vertexCount = vertices.size ();
+ if (GetVertexCount () != vertexCount || GetAvailableChannels () != format)
+ ResizeVertices (vertexCount, format);
+
+ strided_copy (vertices.begin (), vertices.begin () + std::min (vertices.size (), vertexCount), GetVertexBegin ());
+ strided_copy (normals.begin (), normals.begin () + std::min (normals.size (), vertexCount), GetNormalBegin ());
+ strided_copy (uvs.begin (), uvs.begin () + std::min (uvs.size (), vertexCount), GetUvBegin (0));
+ strided_copy (uvs1.begin (), uvs1.begin () + std::min (uvs1.size (), vertexCount), GetUvBegin (1));
+ strided_copy (tangents.begin (), tangents.begin () + std::min (tangents.size (), vertexCount), GetTangentBegin ());
+ strided_copy (colors.begin (), colors.begin () + std::min (colors.size (), vertexCount), GetColorBegin ());
+ }
+ else
+ {
+ // version 6 introduces interleaved buffer
+ if (transfer.ConvertEndianess() && transfer.IsWriting ())
+ m_VertexData.SwapEndianess ();
+
+ transfer.Transfer (m_VertexData, "m_VertexData", kHideInEditorMask);
+
+ if (transfer.ConvertEndianess() && (transfer.IsWriting () || transfer.IsReading ()))
+ m_VertexData.SwapEndianess ();
+ }
+ }
+ // Notice the two codepaths for serialization here.
+ // It is very important to keep both codepaths in sync, otherwise SafeBinaryRead serialization will crash.
+ else
+ {
+ BoneInfluenceContainer dummySkin;
+ VertexData dummyVertexData;
+ IndexContainer dummyIndexContainer;
+
+ transfer.Transfer (dummyIndexContainer, "m_IndexBuffer", kHideInEditorMask);
+ transfer.Transfer (dummySkin, "m_Skin", kHideInEditorMask);
+ transfer.Transfer (dummyVertexData, "m_VertexData", kHideInEditorMask);
+ }
+
+ {
+ // only keep the compressed mesh in memory while needed
+ CompressedMesh m_CompressedMesh;
+ transfer.Align();
+ // Check both IsWriting() and IsReading() since both are true when reading with SafeBinaryRead
+ if (doCompression && transfer.IsWriting())
+ m_CompressedMesh.Compress(*this, m_MeshCompression);
+
+ transfer.Transfer (m_CompressedMesh, "m_CompressedMesh", kHideInEditorMask);
+
+ if (doCompression && transfer.DidReadLastProperty ())
+ m_CompressedMesh.Decompress(*this);
+ }
+
+ #if !GFX_SUPPORTS_TRISTRIPS
+ if (transfer.IsReading())
+ DestripifyIndices ();
+ #endif
+
+ // Reswizzle colors after serializing
+ if (reswizzleColors)
+ {
+ std::transform(GetColorBegin(), GetColorEnd(), GetColorBegin(), SwizzleColorForPlatform);
+ m_VertexColorsSwizzled = true;
+ }
+
+ transfer.Transfer (m_LocalAABB, "m_LocalAABB", kHideInEditorMask);
+
+ #if UNITY_EDITOR
+ // When building player we precalcuate mesh usage based on who uses the different MeshColliders in different scenes.
+ if (transfer.IsWritingGameReleaseData())
+ {
+ int buildMeshUsageFlags = transfer.GetBuildUsage().meshUsageFlags;
+ transfer.Transfer (buildMeshUsageFlags, "m_MeshUsageFlags", kHideInEditorMask);
+ }
+ else
+ transfer.Transfer (m_MeshUsageFlags, "m_MeshUsageFlags", kHideInEditorMask);
+ #else
+ transfer.Transfer (m_MeshUsageFlags, "m_MeshUsageFlags", kHideInEditorMask);
+ #endif
+
+ m_CollisionMesh.Transfer(transfer, *this);
+
+ if (transfer.IsOldVersion(1))
+ {
+ vector<DeprecatedLOD> lod;
+ transfer.Transfer (lod, "m_LODData", kHideInEditorMask);
+ LoadDeprecatedMeshData(*this, lod);
+ }
+
+ if (transfer.IsVersionSmallerOrEqual(4))
+ {
+ for (int sm = 0; sm < m_SubMeshes.size(); ++sm)
+ {
+ UpdateSubMeshVertexRange (sm);
+ RecalculateSubmeshBoundsInternal (sm);
+ }
+ }
+
+ if (transfer.IsOldVersion(2) || transfer.IsOldVersion(1))
+ {
+ DeprecatedTangentsArray m_TangentSpace;
+ transfer.Transfer (m_TangentSpace, "m_TangentSpace", kHideInEditorMask);
+ if(transfer.IsReading())
+ LoadDeprecatedTangentData(*this,m_TangentSpace);
+ }
+
+ if (transfer.IsVersionSmallerOrEqual(7))
+ {
+ DestripifySubmeshOnTransferInternal();
+ }
+ TRANSFER_EDITOR_ONLY_HIDDEN(m_MeshOptimized);
+
+#if UNITY_EDITOR || UNITY_PS3
+ TransferPS3Data(transfer);
+#endif
+}
+
+#if SUPPORT_SERIALIZED_TYPETREES
+// Except for some dead-path removal and a change to the ResizeVertices call to account for an
+// API change, this is an exact copy of the Mesh::Transfer function as it shipped in 3.5.0 final.
+// This path exists solely to work around the issue with compressed mesh serialization in 3.5.0
+// which produced different serializations for compressed and uncompressed meshes while using the
+// same type tree for either case. This makes it impossible for SafeBinaryRead to sort things out.
+//
+// By having the exact same transfer path, we end up with identical type trees compared to version
+// 3.5.0 and thus automatically end up on the StreamedBinaryRead codepath. Also, as long as this
+// separate path here is preserved, we can read the faulty 3.5.0 streams without having to worry
+// about it in the normal transfer path.
+template<class TransferFunction>
+void Mesh::TransferWorkaround35SerializeFuckup (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion (6);
+
+ if (m_VertexColorsSwizzled)
+ {
+ // Unswizzle colors before serializing
+ std::transform(GetColorBegin(), GetColorEnd(), GetColorBegin(), UnswizzleColorForPlatform);
+ m_VertexColorsSwizzled = false;
+ }
+
+ transfer.Transfer (m_SubMeshes, "m_SubMeshes", kHideInEditorMask);
+
+ if (!transfer.IsVersionSmallerOrEqual(3))
+ transfer.Transfer (m_MeshCompression, "m_MeshCompression", kHideInEditorMask);
+ else
+ m_MeshCompression = kMeshCompressionOff;
+
+ transfer.Align();
+ if (m_MeshCompression == kMeshCompressionOff)
+ {
+ if (transfer.ConvertEndianess() && transfer.IsWriting ())
+ ByteSwapIndices();
+
+ transfer.Transfer (m_IndexBuffer, "m_IndexBuffer", kHideInEditorMask);
+
+ if (transfer.ConvertEndianess() && (transfer.IsWriting () || transfer.IsReading ()))
+ ByteSwapIndices();
+
+ transfer.Transfer (m_Skin, "m_Skin", kHideInEditorMask);
+ transfer.Transfer (m_Bindpose, "m_BindPose", kHideInEditorMask);
+
+ if (transfer.IsVersionSmallerOrEqual (5))
+ {
+ dynamic_array<Vector4f> tangents;
+ dynamic_array<Vector3f> vertices, normals;
+ dynamic_array<Vector2f> uvs, uvs1;
+ dynamic_array<ColorRGBA32> colors;
+
+
+ transfer.Transfer (vertices, "m_Vertices", kHideInEditorMask);
+ transfer.Transfer (uvs, "m_UV", kHideInEditorMask);
+ transfer.Transfer (uvs1, "m_UV1", kHideInEditorMask);
+ transfer.Transfer (tangents, "m_Tangents", kHideInEditorMask);
+ transfer.Transfer (normals, "m_Normals", kHideInEditorMask);
+ transfer.Transfer (colors, "m_Colors", kHideInEditorMask);
+
+ unsigned format = 0;
+ if (!vertices.empty ()) format |= VERTEX_FORMAT1(Vertex);
+ if (!tangents.empty ()) format |= VERTEX_FORMAT1(Tangent);
+ if (!normals.empty ()) format |= VERTEX_FORMAT1(Normal);
+ if (!uvs.empty ()) format |= VERTEX_FORMAT1(TexCoord0);
+ if (!uvs1.empty ()) format |= VERTEX_FORMAT1(TexCoord1);
+ if (!colors.empty ()) format |= VERTEX_FORMAT1(Color);
+
+ size_t vertexCount = vertices.size ();
+ if (GetVertexCount () != vertexCount || GetAvailableChannels () != format)
+ ResizeVertices (vertexCount, format);
+
+ strided_copy (vertices.begin (), vertices.begin () + std::min (vertices.size (), vertexCount), GetVertexBegin ());
+ strided_copy (normals.begin (), normals.begin () + std::min (normals.size (), vertexCount), GetNormalBegin ());
+ strided_copy (uvs.begin (), uvs.begin () + std::min (uvs.size (), vertexCount), GetUvBegin (0));
+ strided_copy (uvs1.begin (), uvs1.begin () + std::min (uvs1.size (), vertexCount), GetUvBegin (1));
+ strided_copy (tangents.begin (), tangents.begin () + std::min (tangents.size (), vertexCount), GetTangentBegin ());
+ strided_copy (colors.begin (), colors.begin () + std::min (colors.size (), vertexCount), GetColorBegin ());
+ }
+ else
+ {
+ // version 6 introduces interleaved buffer
+ if (transfer.ConvertEndianess() && transfer.IsWriting ())
+ m_VertexData.SwapEndianess ();
+
+ transfer.Transfer (m_VertexData, "m_VertexData", kHideInEditorMask);
+
+ if (transfer.ConvertEndianess() && (transfer.IsWriting () || transfer.IsReading ()))
+ m_VertexData.SwapEndianess ();
+ }
+ }
+ else
+ {
+ vector<Vector4f> emptyVector4;
+ vector<Vector3f> emptyVector3;
+ vector<Vector2f> emptyVector2;
+ vector<BoneInfluence> emptyBones;
+ vector<UInt8> emptyIndices;
+ vector<ColorRGBA32> emptyColors;
+
+ transfer.Transfer (emptyIndices, "m_IndexBuffer", kHideInEditorMask);
+ transfer.Transfer (emptyVector3, "m_Vertices", kHideInEditorMask);
+ transfer.Transfer (emptyBones, "m_Skin", kHideInEditorMask);
+ transfer.Transfer (m_Bindpose, "m_BindPose", kHideInEditorMask);
+ transfer.Transfer (emptyVector2, "m_UV", kHideInEditorMask);
+ transfer.Transfer (emptyVector2, "m_UV1", kHideInEditorMask);
+ transfer.Transfer (emptyVector4, "m_Tangents", kHideInEditorMask);
+ transfer.Transfer (emptyVector3, "m_Normals", kHideInEditorMask);
+ transfer.Transfer (emptyColors, "m_Colors", kHideInEditorMask);
+ }
+
+ CompressedMesh m_CompressedMesh;
+ transfer.Align();
+ if (transfer.IsWriting() && m_MeshCompression)
+ m_CompressedMesh.Compress(*this, m_MeshCompression);
+
+ printf_console( "Reading compressed mesh...\n" );
+ transfer.Transfer (m_CompressedMesh, "m_CompressedMesh", kHideInEditorMask);
+
+ if (transfer.DidReadLastProperty () && m_MeshCompression)
+ m_CompressedMesh.Decompress(*this);
+
+
+#if !GFX_SUPPORTS_TRISTRIPS
+ if (transfer.IsReading())
+ DestripifyIndices ();
+#endif
+
+ transfer.Transfer (m_LocalAABB, "m_LocalAABB", kHideInEditorMask);
+ transfer.Transfer (m_MeshUsageFlags, "m_MeshUsageFlags", kHideInEditorMask);
+
+ m_CollisionMesh.Transfer(transfer, *this);
+
+ if (transfer.IsOldVersion(1))
+ {
+ vector<DeprecatedLOD> lod;
+ transfer.Transfer (lod, "m_LODData", kHideInEditorMask);
+ LoadDeprecatedMeshData(*this, lod);
+ }
+
+ if (transfer.IsVersionSmallerOrEqual(4))
+ {
+ for (int sm = 0; sm < m_SubMeshes.size(); ++sm)
+ {
+ UpdateSubMeshVertexRange (sm);
+ RecalculateSubmeshBoundsInternal (sm);
+ }
+ }
+
+ if (transfer.IsOldVersion(2) || transfer.IsOldVersion(1))
+ {
+ DeprecatedTangentsArray m_TangentSpace;
+ transfer.Transfer (m_TangentSpace, "m_TangentSpace", kHideInEditorMask);
+ if(transfer.IsReading())
+ LoadDeprecatedTangentData(*this,m_TangentSpace);
+ }
+
+ if (transfer.IsReading())
+ DestripifySubmeshOnTransferInternal();
+}
+#endif
+
+#if UNITY_EDITOR || UNITY_PS3
+template<class TransferFunction>
+void Mesh::TransferPS3Data (TransferFunction& transfer)
+{
+ if (UNITY_PS3 || (kBuildPS3 == transfer.GetBuildingTarget().platform))
+ {
+ transfer.Transfer(m_Partitions, "m_Partitions", kHideInEditorMask);
+ transfer.Transfer(m_PartitionInfos, "m_PartitionInfos", kHideInEditorMask);
+ }
+}
+#endif
+
+
+void Mesh::UpdateSubMeshVertexRange (int index)
+{
+ SubMesh& submesh = m_SubMeshes[index];
+ if (submesh.indexCount > 0)
+ {
+ UInt32 lastVertex = 0;
+ GetVertexBufferRange(GetSubMeshBuffer16(index), submesh.indexCount, submesh.firstVertex, lastVertex);
+ Assert(lastVertex < GetVertexCount ());
+ Assert(submesh.firstVertex <= lastVertex);
+ submesh.vertexCount = lastVertex - submesh.firstVertex + 1;
+ }
+ else
+ {
+ submesh.firstVertex = 0;
+ submesh.vertexCount = 0;
+ }
+}
+
+static bool CheckOutOfBounds (unsigned max, const UInt16* p, unsigned count)
+{
+ for (int i=0;i<count;i++)
+ {
+ if (p[i] >= max)
+ return false;
+ }
+ return true;
+}
+
+static bool CheckOutOfBounds (unsigned max, const UInt32* p, unsigned count)
+{
+ for (int i=0;i<count;i++)
+ {
+ if (p[i] >= max)
+ return false;
+ }
+ return true;
+}
+
+bool Mesh::ValidateVertexCount (unsigned newVertexCount, const void* newTriangles, unsigned indexCount)
+{
+ if (newTriangles)
+ {
+ return CheckOutOfBounds (newVertexCount, reinterpret_cast<const UInt16*>(newTriangles), indexCount);
+ }
+ else
+ {
+ return CheckOutOfBounds(newVertexCount, reinterpret_cast<const UInt16*>(&m_IndexBuffer[0]), GetTotalndexCount());
+ }
+}
+
+int Mesh::GetTotalndexCount () const
+{
+ return m_IndexBuffer.size () / kVBOIndexSize;
+}
+
+void Mesh::SetVertices (Vector3f const* data, size_t count)
+{
+ if (m_StreamCompression)
+ return;
+
+ if (count > std::numeric_limits<UInt16>::max())
+ {
+ ErrorString("Mesh.vertices is too large. A mesh may not have more than 65000 vertices.");
+ return;
+ }
+
+ size_t prevCount = GetVertexCount ();
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion3_5_3_a1) && count < prevCount && !ValidateVertexCount(count, NULL, 0))
+ {
+ ErrorString("Mesh.vertices is too small. The supplied vertex array has less vertices than are referenced by the triangles array.");
+ return;
+ }
+
+ WaitOnRenderThreadUse();
+
+#if UNITY_PS3
+ if(m_Skin.empty() || (!(m_Skin.empty() || m_PartitionInfos.empty())))
+ {
+ // mircea@info: sadly for us GPU renders from pointers, so we need to create a new instance when something changes....(fixes nasty bug #434226)
+ SET_ALLOC_OWNER(this);
+ VertexData vertexData(m_VertexData, GetAvailableChannels(), GetStreamsLayout(), GetChannelsLayout());
+ swap(vertexData, m_VertexData);
+ }
+#endif
+
+ if (prevCount != count)
+ {
+ unsigned prevChannels = GetAvailableChannels ();
+ ResizeVertices (count, prevChannels | VERTEX_FORMAT1(Vertex));
+
+ // In case there were other channels present, initialize the newly created values of
+ // the expanded buffer to something meaningful.
+ if (prevCount != 0 && count > prevCount && (prevChannels & ~VERTEX_FORMAT1(Vertex)))
+ {
+ InitChannelsToDefault (prevCount, count - prevCount, prevChannels & ~VERTEX_FORMAT1(Vertex));
+ }
+ }
+
+ // Make sure we'll not be overrunning the buffer
+ if (GetVertexCount () < count)
+ count = GetVertexCount ();
+
+ strided_copy (data, data + count, GetVertexBegin ());
+ SetChannelsDirty (VERTEX_FORMAT1(Vertex), false);
+
+ // We do not recalc the bounds automatically when re-writing existing vertices
+ if (prevCount != count)
+ RecalculateBounds ();
+}
+
+void Mesh::SetNormals (Vector3f const* data, size_t count)
+{
+ if (m_StreamCompression)
+ return;
+ WaitOnRenderThreadUse();
+
+ if (count == 0 || !data)
+ {
+ FormatVertices (GetAvailableChannels () & ~VERTEX_FORMAT1(Normal));
+ SetChannelsDirty (VERTEX_FORMAT1(Normal), false);
+ return;
+ }
+
+ if (count != GetVertexCount ())
+ {
+ ErrorStringMsg(kMeshAPIErrorMessage, "normals");
+ return;
+ }
+
+ if (!IsAvailable (kShaderChannelNormal))
+ FormatVertices (GetAvailableChannels () | VERTEX_FORMAT1(Normal));
+
+ strided_copy (data, data + count, GetNormalBegin ());
+
+ SetChannelsDirty (VERTEX_FORMAT1(Normal), false);
+}
+
+void Mesh::SetTangents (Vector4f const* data, size_t count)
+{
+ if (m_StreamCompression)
+ return;
+ WaitOnRenderThreadUse();
+
+ if (count == 0 || !data)
+ {
+ FormatVertices (GetAvailableChannels () & ~VERTEX_FORMAT1(Tangent));
+ SetChannelsDirty (VERTEX_FORMAT1(Tangent), false);
+ return;
+ }
+
+ if (count != GetVertexCount ())
+ {
+ ErrorStringMsg(kMeshAPIErrorMessage, "tangents");
+ return;
+ }
+
+ if (!IsAvailable (kShaderChannelTangent))
+ FormatVertices (GetAvailableChannels () | VERTEX_FORMAT1(Tangent));
+
+ strided_copy (data, data + count, GetTangentBegin ());
+ SetChannelsDirty( VERTEX_FORMAT1(Tangent), false );
+}
+
+void Mesh::SetUv (int uvIndex, Vector2f const* data, size_t count)
+{
+ Assert (uvIndex <= 1);
+ if (m_StreamCompression)
+ return;
+ WaitOnRenderThreadUse();
+
+ ShaderChannel texCoordChannel = static_cast<ShaderChannel>(kShaderChannelTexCoord0 + uvIndex);
+ unsigned texCoordMask = 1 << texCoordChannel;
+ if (count == 0 || !data)
+ {
+ FormatVertices (GetAvailableChannels () & ~texCoordMask);
+ SetChannelsDirty (texCoordMask, false);
+ return;
+ }
+
+ if (count != GetVertexCount ())
+ {
+ const char* uvName = uvIndex == 1 ? "uv2" : "uv";
+ ErrorStringMsg(kMeshAPIErrorMessage, uvName);
+ return;
+ }
+
+ if (!IsAvailable (texCoordChannel))
+ FormatVertices (GetAvailableChannels () | texCoordMask);
+
+ strided_copy (data, data + count, GetUvBegin (uvIndex));
+ SetChannelsDirty (texCoordMask, false);
+}
+
+void Mesh::SetColors (ColorRGBA32 const* data, size_t count)
+{
+ if (m_StreamCompression)
+ return;
+ WaitOnRenderThreadUse();
+
+ if (count == 0 || !data)
+ {
+ FormatVertices (GetAvailableChannels () & ~VERTEX_FORMAT1(Color));
+ SetChannelsDirty( VERTEX_FORMAT1(Color), false );
+ return;
+ }
+
+ if (count != GetVertexCount ())
+ {
+ ErrorStringMsg(kMeshAPIErrorMessage, "colors");
+ return;
+ }
+
+ if (!IsAvailable (kShaderChannelColor))
+ {
+ FormatVertices (GetAvailableChannels () | VERTEX_FORMAT1(Color));
+ }
+ m_VertexColorsSwizzled = gGraphicsCaps.needsToSwizzleVertexColors;
+
+ if (m_VertexColorsSwizzled)
+ std::transform(data, data + count, GetColorBegin(), SwizzleColorForPlatform);
+ else
+ std::copy(data, data + count, GetColorBegin());
+
+ SetChannelsDirty( VERTEX_FORMAT1(Color), false );
+}
+
+void Mesh::SetColorsConverting (ColorRGBAf const* data, size_t count)
+{
+ if (m_StreamCompression)
+ return;
+ WaitOnRenderThreadUse();
+
+ if (count == 0 || !data)
+ {
+ FormatVertices (GetAvailableChannels () & ~VERTEX_FORMAT1(Color));
+ SetChannelsDirty( VERTEX_FORMAT1(Color), false );
+ return;
+ }
+
+ if (count != GetVertexCount ())
+ {
+ ErrorStringMsg(kMeshAPIErrorMessage, "colors");
+ return;
+ }
+
+ if (!IsAvailable (kShaderChannelColor))
+ {
+ FormatVertices (GetAvailableChannels () | VERTEX_FORMAT1(Color));
+ }
+ m_VertexColorsSwizzled = gGraphicsCaps.needsToSwizzleVertexColors;
+
+ if (m_VertexColorsSwizzled)
+ std::transform(data, data + count, GetColorBegin(), SwizzleColorForPlatform);
+ else
+ strided_copy_convert(data, data + count, GetColorBegin());
+
+ SetChannelsDirty( VERTEX_FORMAT1(Color), false );
+}
+
+
+void Mesh::GetTriangles (Mesh::TemporaryIndexContainer& triangles) const
+{
+ triangles.clear();
+ for (unsigned m=0;m<GetSubMeshCount();m++)
+ AppendTriangles(triangles, m);
+}
+
+void Mesh::GetTriangles (Mesh::TemporaryIndexContainer& triangles, unsigned submesh) const
+{
+ triangles.clear();
+ AppendTriangles(triangles, submesh);
+}
+
+void QuadsToTriangles(const UInt16* quads, const int indexCount, Mesh::TemporaryIndexContainer& triangles)
+{
+ DebugAssert (indexCount%4 == 0);
+ triangles.resize((indexCount/2)*3);
+ for (int q = 0, t = 0; q < indexCount; q += 4, t +=6)
+ {
+ triangles[t] = quads[q];
+ triangles[t + 1] = quads[q + 1];
+ triangles[t + 2] = quads[q + 2];
+
+ triangles[t + 3] = quads[q];
+ triangles[t + 4] = quads[q + 2];
+ triangles[t + 5] = quads[q + 3];
+ }
+}
+
+
+void Mesh::AppendTriangles (Mesh::TemporaryIndexContainer& triangles, unsigned submesh) const
+{
+ if (submesh >= GetSubMeshCount())
+ {
+ ErrorString("Failed getting triangles. Submesh index is out of bounds.");
+ return;
+ }
+
+ int topology = GetSubMeshFast(submesh).topology;
+ if (topology == kPrimitiveTriangleStripDeprecated)
+ Destripify(GetSubMeshBuffer16(submesh), GetSubMeshFast(submesh).indexCount, triangles);
+ else if (topology == kPrimitiveQuads)
+ QuadsToTriangles (GetSubMeshBuffer16 (submesh), GetSubMeshFast (submesh).indexCount, triangles);
+ else if (topology == kPrimitiveTriangles)
+ triangles.insert(triangles.end(), GetSubMeshBuffer16(submesh), GetSubMeshBuffer16(submesh) + GetSubMeshFast(submesh).indexCount);
+ else
+ ErrorString("Failed getting triangles. Submesh topology is lines or points.");
+}
+
+void Mesh::GetStrips (Mesh::TemporaryIndexContainer& triangles, unsigned submesh) const
+{
+ triangles.clear();
+ if (submesh >= GetSubMeshCount())
+ {
+ ErrorString("Failed getting triangles. Submesh index is out of bounds.");
+ return;
+ }
+
+ if (GetSubMeshFast(submesh).topology != kPrimitiveTriangleStripDeprecated)
+ return;
+
+ triangles.assign(GetSubMeshBuffer16(submesh), GetSubMeshBuffer16(submesh) + GetSubMeshFast(submesh).indexCount);
+}
+
+void Mesh::GetIndices (TemporaryIndexContainer& triangles, unsigned submesh) const
+{
+ triangles.clear();
+ if (submesh >= GetSubMeshCount())
+ {
+ ErrorString("Failed getting indices. Submesh index is out of bounds.");
+ return;
+ }
+ triangles.assign(GetSubMeshBuffer16(submesh), GetSubMeshBuffer16(submesh) + GetSubMeshFast(submesh).indexCount);
+}
+
+
+bool Mesh::SetIndices (const UInt32* indices, unsigned count, unsigned submesh, GfxPrimitiveType topology)
+{
+ int mask = kRebuildCollisionTriangles;
+ return SetIndicesComplex (indices, count, submesh, topology, mask);
+}
+
+bool Mesh::SetIndices (const UInt16* indices, unsigned count, unsigned submesh, GfxPrimitiveType topology)
+{
+ int mask = kRebuildCollisionTriangles | k16BitIndices;
+ return SetIndicesComplex (indices, count, submesh, topology, mask);
+}
+
+
+bool Mesh::SetIndicesComplex (const void* indices, unsigned count, unsigned submesh, GfxPrimitiveType topology, int mode)
+{
+ WaitOnRenderThreadUse();
+
+ if (indices == NULL && count != 0 && (mode & kDontAssignIndices) == 0)
+ {
+ ErrorString("failed setting triangles. triangles is NULL");
+ return false;
+ }
+
+ if (submesh >= GetSubMeshCount())
+ {
+ ErrorString("Failed setting triangles. Submesh index is out of bounds.");
+ return false;
+ }
+
+ if ((topology == kPrimitiveTriangles) && (count % 3 != 0))
+ {
+ ErrorString("Failed setting triangles. The number of supplied triangle indices must be a multiple of 3.");
+ return false;
+ }
+
+ if ((mode & kDontAssignIndices) == 0)
+ {
+ bool valid;
+ if (mode & k16BitIndices)
+ valid = CheckOutOfBounds (GetVertexCount(), reinterpret_cast<const UInt16*>(indices), count);
+ else
+ valid = CheckOutOfBounds (GetVertexCount(), reinterpret_cast<const UInt32*>(indices), count);
+
+ if (!valid)
+ {
+ ErrorString("Failed setting triangles. Some indices are referencing out of bounds vertices.");
+ return false;
+ }
+ }
+
+ SetIndexData(submesh, count, indices, topology, mode);
+
+ if (mode & Mesh::kDontSupportSubMeshVertexRanges)
+ {
+ Assert(m_SubMeshes.size () == 1);
+ m_SubMeshes[0].firstVertex = 0;
+ m_SubMeshes[0].vertexCount = GetVertexCount();
+ m_SubMeshes[0].localAABB = m_LocalAABB;
+ }
+ else
+ {
+ // Update vertex range
+ UpdateSubMeshVertexRange (submesh);
+ RecalculateSubmeshBounds(submesh);
+ }
+
+ if (mode & kRebuildCollisionTriangles)
+ RebuildCollisionTriangles();
+
+ SetChannelsDirty( 0, true );
+
+ return true;
+}
+
+void Mesh::DestripifySubmeshOnTransferInternal()
+{
+ if (m_IndexBuffer.empty() || m_SubMeshes.empty())
+ return;
+
+ int submeshCount = m_SubMeshes.size();
+ typedef UNITY_TEMP_VECTOR(UInt16) TemporaryIndexContainer;
+
+ std::vector<TemporaryIndexContainer> submeshIndices;
+ submeshIndices.resize(submeshCount);
+
+ // We have to do this in two batches, as SetIndexData seems to have a bug that causes
+ // triangle windings to get screwed up if we attempt to modify the submeshes in-place.
+
+ for (size_t i = 0; i < submeshCount; ++i)
+ {
+ SubMesh& sm = m_SubMeshes[i];
+ if (sm.topology == kPrimitiveTriangleStripDeprecated)
+ {
+ Destripify (GetSubMeshBuffer16(i), sm.indexCount, submeshIndices[i]);
+ }
+ else
+ {
+ submeshIndices[i].resize(sm.indexCount);
+ memcpy(&submeshIndices[i][0], GetSubMeshBuffer16(i), sm.indexCount << 1);
+ }
+ }
+
+ for(size_t i = 0; i < submeshCount; ++i)
+ {
+ SetIndexData(i, submeshIndices[i].size(), &submeshIndices[i][0], kPrimitiveTriangles, kRebuildCollisionTriangles | k16BitIndices);
+ }
+}
+
+void Mesh::SetIndexData(int submeshIndex, int indexCount, const void* indices, GfxPrimitiveType topology, int mode)
+{
+ int newByteSize = indexCount * kVBOIndexSize;
+ int oldSubmeshSize = GetSubMeshBufferByteSize (submeshIndex);
+ int insertedBytes = newByteSize - GetSubMeshBufferByteSize (submeshIndex);
+ int oldFirstByte = m_SubMeshes[submeshIndex].firstByte;
+ // Growing the buffer
+ if (insertedBytes > 0)
+ {
+ m_IndexBuffer.insert(m_IndexBuffer.begin() + oldFirstByte + oldSubmeshSize, insertedBytes, 0);
+ }
+ // Shrinking the buffer
+ else
+ {
+ m_IndexBuffer.erase(m_IndexBuffer.begin() + oldFirstByte, m_IndexBuffer.begin() + oldFirstByte - insertedBytes);
+ }
+
+#if UNITY_PS3
+
+ // mircea@info: sadly for us GPU renders from pointers, so we need to create a new instance when something changes....(fixes nasty bug #434226)
+ IndexContainer newIndexContainer;
+ newIndexContainer.resize(m_IndexBuffer.size());
+ m_IndexBuffer.swap(newIndexContainer);
+
+#endif
+
+ // Update the sub mesh
+ m_SubMeshes[submeshIndex].indexCount = indexCount;
+ m_SubMeshes[submeshIndex].topology = topology;
+
+ // Synchronize subsequent sub meshes
+ for (int i=submeshIndex+1;i<m_SubMeshes.size();i++)
+ {
+ m_SubMeshes[i].firstByte = m_SubMeshes[i-1].firstByte + m_SubMeshes[i-1].indexCount * kVBOIndexSize;
+ }
+
+ // Write indices into the allocated data
+ if ((mode & kDontAssignIndices) == 0)
+ {
+ if (mode & k16BitIndices)
+ {
+ const UInt16* src = reinterpret_cast<const UInt16*>(indices);
+ UInt16* dst = GetSubMeshBuffer16(submeshIndex);
+ for (int i=0;i<indexCount;i++)
+ dst[i] = src[i];
+ }
+ else
+ {
+ const UInt32* src = reinterpret_cast<const UInt32*>(indices);
+ UInt16* dst = GetSubMeshBuffer16(submeshIndex);
+ for (int i=0;i<indexCount;i++)
+ dst[i] = src[i];
+ }
+ }
+
+ return;
+}
+
+const UInt16* Mesh::GetSubMeshBuffer16 (int submesh) const
+{
+ return m_IndexBuffer.size() > 0 && m_SubMeshes[submesh].firstByte < m_IndexBuffer.size() ? reinterpret_cast<const UInt16*> (&m_IndexBuffer[m_SubMeshes[submesh].firstByte]) : NULL;
+}
+UInt16* Mesh::GetSubMeshBuffer16 (int submesh)
+{
+ return m_IndexBuffer.size() > 0 && m_SubMeshes[submesh].firstByte < m_IndexBuffer.size() ? reinterpret_cast<UInt16*> (&m_IndexBuffer[m_SubMeshes[submesh].firstByte]) : NULL;
+}
+
+void Mesh::SetBindposes (const Matrix4x4f* bindposes, int count)
+{
+ m_Bindpose.assign(bindposes, bindposes + count);
+ SetDirty();
+}
+
+void Mesh::SetBounds (const AABB& aabb)
+{
+ m_LocalAABB = aabb;
+ SetDirty();
+ NotifyObjectUsers( kDidModifyBounds );
+ m_IntermediateUsers.Notify( kImNotifyBoundsChanged );
+}
+
+void Mesh::SetBounds (unsigned submesh, const AABB& aabb)
+{
+ GetSubMeshFast(submesh).localAABB = aabb;
+ SetDirty();
+ NotifyObjectUsers( kDidModifyBounds );
+ m_IntermediateUsers.Notify( kImNotifyBoundsChanged );
+}
+
+void Mesh::NotifyObjectUsers(const MessageIdentifier& msg)
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD;
+
+ MessageData data;
+ data.SetData (this, ClassID (Mesh));
+
+ ObjectList::iterator next;
+ for( ObjectList::iterator i = m_ObjectUsers.begin(); i != m_ObjectUsers.end(); i=next )
+ {
+ next = i;
+ ++next;
+ Object& target = **i;
+ SendMessageDirect(target, msg, data);
+ }
+}
+
+void Mesh::WaitOnRenderThreadUse()
+{
+#if ENABLE_MULTITHREADED_CODE
+ if (m_WaitOnCPUFence)
+ {
+ GetGfxDevice().WaitOnCPUFence(m_CurrentCPUFence);
+ m_WaitOnCPUFence = false;
+ }
+#endif
+}
+
+void Mesh::RebuildCollisionTriangles()
+{
+ m_CollisionMesh.VertexDataHasChanged ();
+}
+
+PROFILER_INFORMATION(gRecalculateNormals, "Mesh.RecalculateNormals", kProfilerOther)
+
+void Mesh::RecalculateNormals()
+{
+ if (m_StreamCompression)
+ return;
+ WaitOnRenderThreadUse();
+
+ PROFILER_AUTO(gRecalculateNormals, this);
+
+ if (int vertexCount = GetVertexCount())
+ {
+ unsigned newChannels = m_VertexData.GetChannelMask () | VERTEX_FORMAT1(Normal);
+ if (newChannels != m_VertexData.GetChannelMask ())
+ FormatVertices (newChannels);
+
+ TemporaryIndexContainer triangles;
+ GetTriangles (triangles);
+
+ CalculateNormals( GetVertexBegin (), &triangles[0], vertexCount, triangles.size()/3, GetNormalBegin () );
+ }
+
+ SetChannelsDirty( VERTEX_FORMAT1(Normal), false );
+}
+
+
+void Mesh::SetSubMeshCount (unsigned int count)
+{
+ WaitOnRenderThreadUse();
+
+ if (count == 0)
+ {
+ m_IndexBuffer.clear();
+ m_SubMeshes.clear();
+ return;
+ }
+
+ // Remove elements
+ if (count < m_SubMeshes.size ())
+ {
+ m_IndexBuffer.resize(m_SubMeshes[count].firstByte);
+ m_SubMeshes.resize(count);
+ }
+ // Append elements
+ else if (count > m_SubMeshes.size ())
+ {
+ SubMesh data;
+ data.firstByte = m_IndexBuffer.size();
+ data.indexCount = 0;
+ data.topology = kPrimitiveTriangles;
+ data.firstVertex = 0;
+ data.vertexCount = 0;
+ data.localAABB = AABB (Vector3f::zero, Vector3f::zero);
+ m_SubMeshes.resize(count, data);
+ RecalculateBounds();
+ }
+}
+
+size_t Mesh::GetSubMeshCount () const
+{
+ return m_SubMeshes.size();
+}
+
+int Mesh::GetPrimitiveCount() const
+{
+ int submeshes = GetSubMeshCount();
+ int count = 0;
+ for( int m = 0; m < submeshes; ++m ) {
+ const SubMesh& sub = m_SubMeshes[m];
+ count += ::GetPrimitiveCount(sub.indexCount, sub.topology, false);
+ }
+ return count;
+}
+
+int Mesh::CalculateTriangleCount() const
+{
+ int submeshes = GetSubMeshCount();
+ int count = 0;
+ for( int m = 0; m < submeshes; ++m )
+ {
+ const SubMesh& sub = m_SubMeshes[m];
+ if (sub.topology == kPrimitiveTriangleStripDeprecated)
+ {
+ const UInt16* indices = GetSubMeshBuffer16(m);
+ int triCount = CountTrianglesInStrip (indices, sub.indexCount);
+ count += triCount;
+ }
+ else if (sub.topology == kPrimitiveTriangles)
+ {
+ count += sub.indexCount / 3;
+ }
+ }
+ return count;
+}
+
+Mesh& Mesh::GetInstantiatedMesh (Mesh* mesh, Object& owner)
+{
+ if (NULL != mesh && mesh->m_Owner == PPtr<Object> (&owner))
+ return *mesh;
+
+ if (!IsWorldPlaying())
+ ErrorStringObject("Instantiating mesh due to calling MeshFilter.mesh during edit mode. This will leak meshes. Please use MeshFilter.sharedMesh instead.", &owner);
+
+ if (mesh == NULL || !mesh->HasVertexData ())
+ {
+ if (!mesh)
+ mesh = NEW_OBJECT (Mesh);
+ mesh->Reset();
+
+ mesh->SetName(owner.GetName());
+ mesh->m_Owner = &owner;
+
+ mesh->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+ return *mesh;
+ }
+
+ Mesh* instance = NEW_OBJECT (Mesh);
+ CopySerialized(*mesh, *instance);
+ instance->SetNameCpp (Append (mesh->GetName (), " Instance"));
+ instance->m_Owner = &owner;
+ return *instance;
+}
+
+const VertexStreamsLayout& Mesh::GetStreamsLayout() const
+{
+ if (!m_Skin.empty() || GetBlendShapeChannelCount() != 0)
+ return VertexDataInfo::kVertexStreamsSkinnedHotColdSplit;
+ else
+ return VertexDataInfo::kVertexStreamsDefault;
+}
+
+const VertexChannelsLayout& Mesh::GetChannelsLayout() const
+{
+ UInt8 compressed = m_StreamCompression;
+#if !UNITY_EDITOR
+ // Editor only does build step for compression and never draws float16 vertices
+ if (!gGraphicsCaps.has16BitFloatVertex)
+ {
+ compressed = kStreamCompressionDefault;
+ }
+#endif
+ switch (compressed)
+ {
+ default: // fall through
+ case kStreamCompressionDefault:
+ return VertexDataInfo::kVertexChannelsDefault;
+ case kStreamCompressionCompressed:
+ return VertexDataInfo::kVertexChannelsCompressed;
+ case kStreamCompressionCompressedAggressive:
+ return VertexDataInfo::kVertexChannelsCompressedAggressive;
+ }
+}
+
+void Mesh::InitVertexBufferData( UInt32 wantedChannels )
+{
+#if GFX_CAN_UNLOAD_MESH_DATA
+ // If data was uploaded and freed we cannot update it.
+ if (!HasVertexData())
+ return;
+#endif
+ UInt32 presentChannels = GetAvailableChannels ();
+
+ // Modify the vertex buffer before fetching any channel pointers, as modifying the format reallocates the buffer and pointers
+ // are invalidated. Due to possible format changes, also fetch the stride sizes only after buffer reformatting.
+ unsigned initChannels = 0;
+
+ // Silently create an all-white color array if shader wants colors, but mesh does not have them.
+ // On D3D, some runtime/driver combinations will crash if a vertex shader wants colors but does not
+ // have them (e.g. Vista drivers for Intel 965). In other cases it will default to white for fixed function
+ // pipe, and to undefined value for vertex shaders, which is not good either.
+ if( (wantedChannels & VERTEX_FORMAT1(Color)) && !(presentChannels & VERTEX_FORMAT1(Color)) )
+ initChannels |= VERTEX_FORMAT1(Color);
+
+#if UNITY_PEPPER
+ // Pepper OpenGL implementation fails to draw anything if any channel is missing.
+ if( (wantedChannels & VERTEX_FORMAT1(Tangent)) && !(presentChannels & VERTEX_FORMAT1(Tangent)) )
+ initChannels |= VERTEX_FORMAT1(Tangent);
+#endif
+
+ if ((initChannels & presentChannels) != initChannels)
+ {
+ FormatVertices (presentChannels | initChannels);
+ InitChannelsToDefault (0, GetVertexCount (), initChannels);
+ }
+}
+
+void Mesh::GetVertexBufferData( VertexBufferData& buffer, UInt32 wantedChannels )
+{
+ InitVertexBufferData(wantedChannels);
+
+ for (int i = 0; i < kShaderChannelCount; i++)
+ buffer.channels[i] = m_VertexData.GetChannel(i);
+
+ for (int i = 0; i < kMaxVertexStreams; i++)
+ buffer.streams[i] = m_VertexData.GetStream(i);
+
+ int srcTexcoord = kShaderChannelNone;
+ for (int i = kShaderChannelTexCoord0; i <= kShaderChannelTexCoord1; i++)
+ {
+ if (buffer.channels[i].IsValid())
+ {
+ // We have a valid texcoord
+ srcTexcoord = i;
+ continue;
+ }
+ UInt32 channelMask = 1 << i;
+ if (srcTexcoord != kShaderChannelNone)
+ {
+ // Replicate last valid texture coord
+ const ChannelInfo& srcChannel = buffer.channels[srcTexcoord];
+ buffer.channels[i] = srcChannel;
+ buffer.streams[srcChannel.stream].channelMask |= channelMask;
+ }
+ }
+
+ // Data pointer can be NULL if we are only updating declaration of uploaded VBO
+ buffer.buffer = m_VertexData.GetDataPtr();
+ buffer.bufferSize = m_VertexData.GetDataSize();
+ buffer.vertexCount = GetVertexCount();
+
+#if UNITY_EDITOR
+ #define LogStringObjectEditor(x) LogStringObject(Format(x, GetName()),this)
+
+ if (Camera::ShouldShowChannelErrors(GetCurrentCameraPtr()))
+ {
+ const ChannelInfo* channels = buffer.channels;
+
+ if ((wantedChannels & VERTEX_FORMAT1(Tangent)) && !channels[kShaderChannelTangent].IsValid())
+ LogStringObjectEditor ("Shader wants tangents, but the mesh %s doesn't have them");
+
+ if ((wantedChannels & VERTEX_FORMAT1(Normal)) && !channels[kShaderChannelNormal].IsValid())
+ LogStringObjectEditor ("Shader wants normals, but the mesh %s doesn't have them");
+
+ if ((wantedChannels & VERTEX_FORMAT1(TexCoord0)) && !channels[kShaderChannelTexCoord0].IsValid())
+ LogStringObjectEditor ("Shader wants texture coordinates, but the mesh %s doesn't have them");
+
+ if ((wantedChannels & VERTEX_FORMAT1(TexCoord1)) && !channels[kShaderChannelTexCoord1].IsValid())
+ LogStringObjectEditor ("Shader wants secondary texture coordinates, but the mesh %s doesn't have any");
+
+ if ((wantedChannels & VERTEX_FORMAT1(Color)) && !channels[kShaderChannelColor].IsValid())
+ LogStringObjectEditor ("Shader wants vertex colors, and failed to create a vertex color array");
+ }
+ #undef LogStringObjectEditor
+#endif
+
+#if UNITY_PS3
+ if(m_PartitionInfos.empty())
+ {
+ int submeshCount = m_SubMeshes.size();
+ for (int submesh=0; submesh<submeshCount; submesh++)
+ {
+ SubMesh& sm = GetSubMeshFast(submesh);
+
+ MeshPartitionInfo partInfo;
+ partInfo.submeshStart = submesh;
+ partInfo.partitionCount = 1;
+ buffer.partInfo.push_back(partInfo);
+
+ MeshPartition part;
+ part.vertexCount = sm.vertexCount;
+ part.vertexOffset = 0;
+ part.indexCount = sm.indexCount;
+ part.indexByteOffset = sm.firstByte;
+ buffer.partitions.push_back(part);;
+ }
+ }
+ else
+ {
+ buffer.partInfo = m_PartitionInfos;
+ buffer.partitions = m_Partitions;
+ }
+
+#endif
+
+ buffer.vertexCount = GetVertexCount ();
+}
+
+void Mesh::GetIndexBufferData (IndexBufferData& buffer)
+{
+ DebugAssert (!m_IndexBuffer.empty());
+ buffer.indices = m_IndexBuffer.empty() ? NULL : (void*)&m_IndexBuffer[0];
+
+ ///@TODO: HACK for now to get index buffers working, without changing a lot of vbo code
+ // We should be passing the byte size not the number of indices
+ buffer.count = GetTotalndexCount();
+ buffer.hasTopologies = 0;
+ for (size_t i = 0, n = m_SubMeshes.size(); i < n; ++i)
+ {
+ buffer.hasTopologies |= (1<<m_SubMeshes[i].topology);
+ }
+}
+
+PROFILER_INFORMATION(gCreateVBOProfile, "Mesh.CreateVBO", kProfilerRender);
+PROFILER_INFORMATION(gAwakeFromLoadMesh, "Mesh.AwakeFromLoad", kProfilerLoading);
+PROFILER_INFORMATION(gUploadMeshDataMesh, "Mesh.UploadMeshData", kProfilerLoading);
+
+VBO* Mesh::GetSharedVBO( UInt32 wantedChannels )
+{
+ // Some badly written shaders have no Bind statements in the vertex shaders parts;
+ // and only happened to work before by accident. If requiredChannels turns out to be
+ // zero, let's pretend it did request at least position.
+ if (wantedChannels == 0)
+ wantedChannels = (1<<kShaderChannelVertex);
+
+ UInt32 newChannels = wantedChannels | m_ChannelsInVBO;
+ bool addedChannels = newChannels != m_ChannelsInVBO;
+
+#if GFX_CAN_UNLOAD_MESH_DATA
+ if (!m_IsReadable && !m_KeepVertices && m_VBO)
+ {
+ // Everything is already prepared, just return VBO
+ return m_VBO;
+ }
+#endif
+
+ if ((GFX_ALL_BUFFERS_CAN_BECOME_LOST || m_IsDynamic) && m_VBO && m_VBO->IsVertexBufferLost())
+ m_VerticesDirty = true;
+ if (GFX_ALL_BUFFERS_CAN_BECOME_LOST && m_VBO && m_VBO->IsIndexBufferLost())
+ m_IndicesDirty = true;
+
+ if (addedChannels || m_VerticesDirty || m_IndicesDirty)
+ CreateSharedVBO(wantedChannels);
+
+ return m_VBO;
+}
+
+void Mesh::CreateSharedVBO( UInt32 wantedChannels )
+{
+ if (m_IndexBuffer.empty())
+ {
+ if (m_VBO)
+ {
+ GetGfxDevice().DeleteVBO(m_VBO);
+ m_VBO = NULL;
+ }
+ return;
+ }
+
+ PROFILER_BEGIN(gCreateVBOProfile, this)
+ SET_ALLOC_OWNER(this);
+
+ if (!m_VBO)
+ {
+ m_VBO = GetGfxDevice().CreateVBO();
+ m_VBO->SetHideFromRuntimeStats(m_HideFromRuntimeStats);
+ }
+
+ UInt32 newChannels = wantedChannels | m_ChannelsInVBO;
+ if (m_VerticesDirty || newChannels != m_ChannelsInVBO)
+ {
+ if (m_IsDynamic)
+ m_VBO->SetVertexStreamMode(0, VBO::kStreamModeDynamic);
+
+ VertexBufferData vertexBuffer;
+ GetVertexBufferData (vertexBuffer, newChannels);
+ m_VBO->UpdateVertexData (vertexBuffer);
+ }
+
+ if (m_IndicesDirty)
+ {
+ // TODO: probably add separate script access to set vertex/index dynamic
+ if (m_IsDynamic)
+ m_VBO->SetIndicesDynamic(true);
+
+ IndexBufferData indexBuffer;
+ GetIndexBufferData (indexBuffer);
+ m_VBO->UpdateIndexData (indexBuffer);
+ }
+
+ m_VerticesDirty = false;
+ m_IndicesDirty = false;
+ m_ChannelsInVBO = newChannels;
+
+ PROFILER_END
+}
+
+bool Mesh::CopyToVBO ( UInt32 wantedChannels, VBO& vbo )
+{
+ if( m_IndexBuffer.empty() )
+ return false;
+
+ PROFILER_BEGIN(gCreateVBOProfile, this)
+
+ VertexBufferData vertexBuffer;
+ GetVertexBufferData( vertexBuffer, wantedChannels );
+ vbo.UpdateVertexData( vertexBuffer );
+
+ IndexBufferData indexBuffer;
+ GetIndexBufferData (indexBuffer);
+ vbo.UpdateIndexData (indexBuffer);
+#if UNITY_XENON
+ if( m_VBO )
+ vbo.CopyExtraUvChannels( m_VBO );
+#endif
+ PROFILER_END
+
+ return true;
+}
+
+
+void Mesh::UnloadVBOFromGfxDevice()
+{
+ if (m_VBO)
+ {
+ WaitOnRenderThreadUse();
+ GetGfxDevice().DeleteVBO (m_VBO);
+ }
+ m_VBO = NULL;
+ m_ChannelsInVBO = 0;
+ m_VerticesDirty = m_IndicesDirty = true;
+#if ENABLE_MULTITHREADED_CODE
+ m_CurrentCPUFence = 0;
+ m_WaitOnCPUFence = false;
+#endif
+}
+
+void Mesh::ReloadVBOToGfxDevice()
+{
+ const bool needReloadFromDisk = (!m_IsReadable && !HasVertexData());
+ if (needReloadFromDisk)
+ {
+ GetPersistentManager().ReloadFromDisk(this);
+ }
+ else
+ {
+ m_ChannelsInVBO = 0;
+ m_VerticesDirty = m_IndicesDirty = true;
+ }
+ SwizzleVertexColorsIfNeeded();
+}
+
+
+bool Mesh::ExtractTriangle (UInt32 face, UInt32* indices) const
+{
+ ///@TODO: OPTIMIZE this away
+ TemporaryIndexContainer triangles;
+ GetTriangles(triangles);
+ if (face * 3 > triangles.size ())
+ return false;
+
+ indices[0] = triangles[face * 3 + 0];
+ indices[1] = triangles[face * 3 + 1];
+ indices[2] = triangles[face * 3 + 2];
+ return true;
+}
+
+static void TransformNormals (const Matrix3x3f& invTranspose, StrideIterator<Vector3f> inNormals, StrideIterator<Vector3f> inNormalsEnd, StrideIterator<Vector3f> outNormals)
+{
+ for (; inNormals != inNormalsEnd; ++inNormals, ++outNormals)
+ *outNormals = NormalizeSafe (invTranspose.MultiplyVector3 (*inNormals));
+}
+
+static void TransformTangents (const Matrix3x3f& invTranspose, StrideIterator<Vector4f> inTangents, StrideIterator<Vector4f> inTangentsEnd, StrideIterator<Vector4f> outTangents)
+{
+ for ( ; inTangents != inTangentsEnd; ++inTangents, ++outTangents)
+ {
+ Vector3f tangent = Vector3f(inTangents->x,inTangents->y,inTangents->z);
+ Vector3f normalized = NormalizeSafe (invTranspose.MultiplyVector3 (tangent));
+ *outTangents = Vector4f(normalized.x, normalized.y ,normalized.z, inTangents->w);
+ }
+}
+
+void Mesh::CopyTransformed (const Mesh& mesh, const Matrix4x4f& transform)
+{
+ int vertexCount = mesh.GetVertexCount();
+ unsigned outVertexFormat = mesh.GetAvailableChannelsForRendering ();
+
+ ResizeVertices(mesh.GetVertexCount (), outVertexFormat);
+
+ if (outVertexFormat & VERTEX_FORMAT1(Vertex))
+ TransformPoints3x4 (transform,
+ (Vector3f*)mesh.GetChannelPointer (kShaderChannelVertex), mesh.GetStride (kShaderChannelVertex),
+ (Vector3f*)GetChannelPointer (kShaderChannelVertex), GetStride (kShaderChannelVertex),
+ vertexCount);
+
+ Matrix3x3f invTranspose3x3 = Matrix3x3f(transform); invTranspose3x3.InvertTranspose ();
+
+ if (outVertexFormat & VERTEX_FORMAT1(Normal))
+ TransformNormals (invTranspose3x3, mesh.GetNormalBegin (), mesh.GetNormalEnd (), GetNormalBegin ());
+ if (outVertexFormat & VERTEX_FORMAT1(Tangent))
+ TransformTangents (invTranspose3x3, mesh.GetTangentBegin (), mesh.GetTangentEnd (), GetTangentBegin ());
+
+ m_IndexBuffer = mesh.m_IndexBuffer;
+ m_SubMeshes = mesh.m_SubMeshes;
+ m_Skin = mesh.m_Skin;
+ if (outVertexFormat & VERTEX_FORMAT1(TexCoord0))
+ strided_copy (mesh.GetUvBegin (0), mesh.GetUvEnd (0), GetUvBegin (0));
+ if (outVertexFormat & VERTEX_FORMAT1(TexCoord1))
+ strided_copy (mesh.GetUvBegin (1), mesh.GetUvEnd (1), GetUvBegin (1));
+ if (outVertexFormat & VERTEX_FORMAT1(Color))
+ strided_copy (mesh.GetColorBegin (), mesh.GetColorEnd (), GetColorBegin ());
+ m_VertexColorsSwizzled = mesh.m_VertexColorsSwizzled;
+ m_LocalAABB = mesh.m_LocalAABB;
+
+ SetChannelsDirty( outVertexFormat, true );
+ ClearSkinCache();
+}
+
+
+void Mesh::SetChannelsDirty (unsigned vertexChannelsChanged, bool indices)
+{
+ SetDirty();
+
+ m_VerticesDirty |= vertexChannelsChanged != 0;
+ m_IndicesDirty |= indices;
+
+ // We should regenreate physics mesh only if verex data have changed
+ if ((vertexChannelsChanged & VERTEX_FORMAT1(Vertex)) || indices)
+ {
+ m_CollisionMesh.VertexDataHasChanged();
+ m_CachedBonesAABB.clear();
+ }
+ NotifyObjectUsers( kDidModifyMesh );
+}
+
+bool Mesh::SetBoneWeights (const BoneInfluence* v, int count)
+{
+ WaitOnRenderThreadUse();
+ ClearSkinCache();
+ if (count == 0)
+ {
+ m_Skin.clear();
+ UpdateVertexFormat();
+ return true;
+ }
+
+ if (count != GetVertexCount ())
+ {
+ ErrorString("Mesh.boneWeights is out of bounds. The supplied array needs to be the same size as the Mesh.vertices array.");
+ return false;
+ }
+ m_Skin.assign(v, v + count);
+ SetChannelsDirty (0, false);
+ UpdateVertexFormat();
+
+ return true;
+}
+
+static void ComputeBoneBindPoseAABB (const Matrix4x4f* bindPoses, size_t bindPoseCount, const StrideIterator<Vector3f> vertices, const BoneInfluence* influences, size_t vertexCount, const BlendShapeVertices& blendShapeVertices, MinMaxAABB* outputBounds)
+{
+ if (blendShapeVertices.empty())
+ {
+ for(int v=0;v<vertexCount;v++)
+ {
+ const Vector3f& vert = vertices[v];
+ for (int i = 0; i < 4; i++)
+ {
+ if(influences[v].weight[i] > 0.0f)
+ {
+ const UInt32 boneIndex = influences[v].boneIndex[i];
+
+ outputBounds[boneIndex].Encapsulate(bindPoses[boneIndex].MultiplyPoint3(vert));
+ }
+ }
+ }
+ }
+ else
+ {
+ Vector3f* minVertices;
+ ALLOC_TEMP(minVertices, Vector3f, vertexCount);
+ Vector3f* maxVertices;
+ ALLOC_TEMP(maxVertices, Vector3f, vertexCount);
+
+ strided_copy(vertices, vertices + vertexCount, minVertices);
+ strided_copy(vertices, vertices + vertexCount, maxVertices);
+
+ for (int i=0;i<blendShapeVertices.size();i++)
+ {
+ int index = blendShapeVertices[i].index;
+ Vector3f pos = blendShapeVertices[i].vertex + vertices[index];
+ maxVertices[index] = max (maxVertices[index], pos);
+ minVertices[index] = min (minVertices[index], pos);
+ }
+
+ for(int v=0;v<vertexCount;v++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ if(influences[v].weight[i] > 0.0f)
+ {
+ const UInt32 boneIndex = influences[v].boneIndex[i];
+ outputBounds[boneIndex].Encapsulate(bindPoses[boneIndex].MultiplyPoint3(minVertices[v]));
+ outputBounds[boneIndex].Encapsulate(bindPoses[boneIndex].MultiplyPoint3(maxVertices[v]));
+ }
+ }
+ }
+ }
+}
+
+const Mesh::AABBContainer& Mesh::GetCachedBonesBounds()
+{
+ // Use cached result if it has the correct size (including empty)
+ if (m_CachedBonesAABB.size() == m_Bindpose.size())
+ return m_CachedBonesAABB;
+
+ Assert(GetMaxBoneIndex() < m_Bindpose.size());
+
+ m_CachedBonesAABB.resize_initialized(m_Bindpose.size(), MinMaxAABB());
+
+ ComputeBoneBindPoseAABB (GetBindposes(), m_CachedBonesAABB.size(), GetVertexBegin(), m_Skin.begin(), GetVertexCount(), m_Shapes.vertices, &m_CachedBonesAABB[0]);
+
+ return m_CachedBonesAABB;
+}
+
+void Mesh::ClearSkinCache ()
+{
+ m_CachedBonesAABB.clear();
+ m_CachedSkin2.clear();
+ m_CachedSkin1.clear();
+ m_MaxBoneIndex = -1;
+}
+
+int Mesh::GetMaxBoneIndex ()
+{
+ if (m_MaxBoneIndex != -1)
+ return m_MaxBoneIndex;
+
+ m_MaxBoneIndex = 0;
+ for (int i=0;i<m_Skin.size();i++)
+ {
+ m_MaxBoneIndex = max(m_MaxBoneIndex, m_Skin[i].boneIndex[0]);
+ m_MaxBoneIndex = max(m_MaxBoneIndex, m_Skin[i].boneIndex[1]);
+ m_MaxBoneIndex = max(m_MaxBoneIndex, m_Skin[i].boneIndex[2]);
+ m_MaxBoneIndex = max(m_MaxBoneIndex, m_Skin[i].boneIndex[3]);
+ }
+
+ return m_MaxBoneIndex;
+}
+
+void* Mesh::GetSkinInfluence (int count)
+{
+ if (!m_Skin.empty())
+ {
+ BoneInfluence* bones4 = &m_Skin[0];
+ if (count == 1)
+ {
+ if (!m_CachedSkin1.empty())
+ return &m_CachedSkin1[0];
+
+ // Cache 1 bone skin weights
+ int size = m_Skin.size();
+ m_CachedSkin1.resize_uninitialized(size);
+
+ int* bones1 = &m_CachedSkin1[0];
+ for (int i=0;i<size;i++)
+ bones1[i] = bones4[i].boneIndex[0];
+ return bones1;
+
+ }
+ else if (count == 2)
+ {
+ if (!m_CachedSkin2.empty ())
+ return &m_CachedSkin2[0];
+
+ // Cache 2 bone skin weights
+ int size = m_Skin.size();
+ m_CachedSkin2.resize_uninitialized(size);
+
+ BoneInfluence2* bones2 = &m_CachedSkin2[0];
+ for (int i=0;i<size;i++)
+ {
+ bones2[i].boneIndex[0] = bones4[i].boneIndex[0];
+ bones2[i].boneIndex[1] = bones4[i].boneIndex[1];
+
+ float invSum = 1.0F / (bones4[i].weight[0] + bones4[i].weight[1]);
+ bones2[i].weight[0] = bones4[i].weight[0] * invSum;
+ bones2[i].weight[1] = bones4[i].weight[1] * invSum;
+ }
+ return bones2;
+ }
+ else if (count == 4)
+ {
+ return bones4;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+int Mesh::GetRuntimeMemorySize () const
+{
+ int size = Super::GetRuntimeMemorySize();
+
+ #if ENABLE_PROFILER
+ if (m_VBO)
+ size += m_VBO->GetRuntimeMemorySize();
+ #endif
+
+ return size;
+}
+
+
+void* Mesh::GetSharedNxMesh ()
+{
+ return m_CollisionMesh.GetSharedNxMesh (*this);
+}
+
+void* Mesh::GetSharedNxConvexMesh ()
+{
+ return m_CollisionMesh.GetSharedNxConvexMesh (*this);
+}
+
+void Mesh::UploadMeshData(bool markNoLongerReadable)
+{
+ if(markNoLongerReadable)
+ m_IsReadable = false;
+
+ ClearSkinCache();
+ UpdateVertexFormat();
+
+ // prepare VBO
+ UInt32 channelMask = GetAvailableChannelsForRendering();
+
+ // Create color channel in case it's needed by shader (and we can't patch it)
+#if GFX_CAN_UNLOAD_MESH_DATA
+ bool unloadData = !m_IsReadable && m_Skin.empty();
+ if (unloadData && !m_KeepVertices)
+ channelMask |= VERTEX_FORMAT1(Color);
+#endif
+
+ // Shared VBO is not required for skinned meshes (unless used as non-skinned)
+ if (m_Skin.empty())
+ CreateSharedVBO(channelMask);
+
+#if GFX_CAN_UNLOAD_MESH_DATA
+ if (unloadData)
+ {
+ if (!m_KeepVertices && m_VBO && !m_VBO->IsUsingSourceVertices())
+ {
+ Assert(m_Skin.empty());
+ m_VertexData.Deallocate();
+ m_VBO->UnloadSourceVertices();
+ }
+ if (!m_KeepIndices && m_VBO && !m_VBO->IsUsingSourceIndices())
+ {
+#if UNITY_METRO
+ m_IndexBuffer.clear();
+ m_IndexBuffer.shrink_to_fit();
+#else
+ // On Metro this throws "Expression: vector containers incompatible for swap" when compiling in VS 2013, works okay if compiling in VS 2012
+ // Case 568418
+ IndexContainer emptyIndices;
+ m_IndexBuffer.swap(emptyIndices);
+#endif
+ }
+ }
+#endif
+}
+
+void Mesh::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ PROFILER_AUTO(gAwakeFromLoadMesh, this)
+
+ Super::AwakeFromLoad(awakeMode);
+ m_CollisionMesh.AwakeFromLoad(awakeMode);
+
+ UploadMeshData(!m_IsReadable);
+
+ if (m_InternalMeshID == 0)
+ m_InternalMeshID = s_MeshIDGenerator.AllocateID ();
+}
+
+void Mesh::AwakeFromLoadThreaded()
+{
+ Super::AwakeFromLoadThreaded();
+ m_CollisionMesh.AwakeFromLoadThreaded(*this);
+}
+
+void Mesh::MarkDynamic()
+{
+ // Optimize for frequent updates
+ m_IsDynamic = true;
+}
+
+void Mesh::UpdateVertexFormat()
+{
+ // Make sure vertex streams are in the format we want for rendering
+ // This will also handle decompression of unsupported vertex formats
+ FormatVertices(GetAvailableChannels());
+ SwizzleVertexColorsIfNeeded();
+}
+
+bool Mesh::ShouldIgnoreInGarbageDependencyTracking ()
+{
+ return true;
+}
+
+UInt32 Mesh::GetAvailableChannels() const
+{
+ return m_VertexData.GetChannelMask ();
+}
+
+UInt32 Mesh::GetAvailableChannelsForRendering() const
+{
+ unsigned availChannels = m_VertexData.GetChannelMask ();
+ return availChannels;
+}
+
+bool Mesh::IsSuitableSizeForDynamicBatching () const
+{
+ // If any submesh has too many vertices, don't keep mesh data for batching
+ for (size_t i = 0; i < GetSubMeshCount(); i++)
+ {
+ if (m_SubMeshes[i].vertexCount > kDynamicBatchingVerticesThreshold)
+ return false;
+ }
+ return true;
+}
+
+void Mesh::CheckConsistency()
+{
+ Super::CheckConsistency();
+
+ for (int i = 0; i < m_SubMeshes.size(); ++i)
+ {
+ Assert(m_SubMeshes[i].topology != kPrimitiveTriangleStripDeprecated);
+ }
+}
+
+void Mesh::SwapBlendShapeData (BlendShapeData& shapes)
+{
+ WaitOnRenderThreadUse();
+
+// swap (m_Shapes, shapes);
+ m_Shapes = shapes;
+
+ NotifyObjectUsers( kDidModifyMesh );
+}
diff --git a/Runtime/Filters/Mesh/LodMesh.h b/Runtime/Filters/Mesh/LodMesh.h
new file mode 100644
index 0000000..41fcf74
--- /dev/null
+++ b/Runtime/Filters/Mesh/LodMesh.h
@@ -0,0 +1,509 @@
+#ifndef LODMESH_H
+#define LODMESH_H
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Vector4.h"
+#include "Mesh.h"
+#include "Runtime/Math/Color.h"
+#include <string>
+#include <vector>
+#include "Runtime/BaseClasses/MessageIdentifier.h"
+#include "Runtime/Shaders/VBO.h"
+#include "CompressedMesh.h"
+#include "VertexData.h"
+#include "Runtime/Dynamics/CollisionMeshData.h"
+#include "MeshBlendShape.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Camera/IntermediateUsers.h"
+
+class IntermediateRenderer;
+
+struct SubMesh
+{
+ UInt32 firstByte;
+ UInt32 indexCount;
+ GfxPrimitiveType topology;
+
+ UInt32 firstVertex;
+ UInt32 vertexCount;
+ AABB localAABB;
+
+ SubMesh ()
+ {
+ firstByte = 0;
+ indexCount = 0;
+ topology = kPrimitiveTriangles;
+ firstVertex = 0;
+ vertexCount = 0;
+ localAABB = AABB (Vector3f::zero, Vector3f::zero);
+ }
+
+ DECLARE_SERIALIZE_NO_PPTR (SubMesh)
+
+#if SUPPORT_SERIALIZED_TYPETREES
+ template<class TransferFunction>
+ void TransferWorkaround35SerializationFuckup (TransferFunction& transfer);
+#endif
+};
+
+/// typedef for tangent space lighting rotations
+typedef std::vector<DeprecatedTangent, STL_ALLOCATOR(kMemGeometry, DeprecatedTangent) > DeprecatedTangentsArray;
+
+template<class TransferFunc>
+void SubMesh::Transfer (TransferFunc& transfer)
+{
+ #if SUPPORT_SERIALIZED_TYPETREES
+ if (transfer.GetFlags() & kWorkaround35MeshSerializationFuckup)
+ {
+ TransferWorkaround35SerializationFuckup (transfer);
+ return;
+ }
+ #endif
+
+ transfer.SetVersion (2);
+ TRANSFER(firstByte);
+ TRANSFER(indexCount);
+ TRANSFER_ENUM(topology);
+ TRANSFER(firstVertex);
+ TRANSFER(vertexCount);
+ TRANSFER(localAABB);
+ if (transfer.IsOldVersion(1))
+ {
+ UInt32 triStrip;
+ transfer.Transfer (triStrip, "isTriStrip");
+ topology = triStrip ? kPrimitiveTriangleStripDeprecated : kPrimitiveTriangles;
+ }
+}
+
+#if SUPPORT_SERIALIZED_TYPETREES
+template<class TransferFunc>
+void SubMesh::TransferWorkaround35SerializationFuckup (TransferFunc& transfer)
+{
+ TRANSFER(firstByte);
+ TRANSFER(indexCount);
+
+ UInt32 triStrip;
+ transfer.Transfer (triStrip, "isTriStrip");
+ topology = triStrip ? kPrimitiveTriangleStripDeprecated : kPrimitiveTriangles;
+
+ UInt32 triangleCount;
+ transfer.Transfer (triangleCount, "triangleCount");
+
+ TRANSFER(firstVertex);
+ TRANSFER(vertexCount);
+ TRANSFER(localAABB);
+}
+#endif
+
+template<class TransferFunc>
+void MeshPartition::Transfer (TransferFunc& transfer)
+{
+ TRANSFER(vertexCount);
+ TRANSFER(vertexOffset);
+ TRANSFER(indexCount);
+ TRANSFER(indexByteOffset);
+}
+
+template<class TransferFunc>
+void MeshPartitionInfo::Transfer (TransferFunc& transfer)
+{
+ TRANSFER(submeshStart);
+ TRANSFER(partitionCount);
+}
+
+class EXPORT_COREMODULE Mesh : public NamedObject
+{
+public:
+ enum
+ {
+ #if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+ alignBoneContainer = 16,
+ #else
+ alignBoneContainer = kDefaultMemoryAlignment,
+ #endif
+ };
+
+ //mircea@INFO PS3 doesn't render from VBOs hence m_VertexData and m_IndexBuffer *have* to be allocated with kMemVertexData.
+ typedef UNITY_VECTOR(kMemVertexData, UInt8) IndexContainer;
+ typedef UNITY_VECTOR(kMemGeometry, SubMesh) SubMeshContainer;
+ typedef dynamic_array<Matrix4x4f> MatrixContainer;
+ typedef dynamic_array<int> SkinContainer;
+ typedef UNITY_VECTOR(kMemGeometry, UInt32) CollisionTriangleContainer;
+ typedef dynamic_array<MinMaxAABB> AABBContainer;
+
+ typedef dynamic_array<BoneInfluence, alignBoneContainer> BoneInfluenceContainer;
+ typedef dynamic_array<BoneInfluence2, alignBoneContainer> BoneInfluence2Container;
+
+ typedef UNITY_TEMP_VECTOR(UInt32) TemporaryIndexContainer;
+
+#if UNITY_PS3 || UNITY_EDITOR
+ typedef UNITY_VECTOR(kMemVertexData, MeshPartition) MeshPartitionContainer;
+ typedef UNITY_VECTOR(kMemVertexData, MeshPartitionInfo) MeshPartitionInfoContainer;
+#endif
+
+ REGISTER_DERIVED_CLASS (Mesh, NamedObject)
+ DECLARE_OBJECT_SERIALIZE (Mesh)
+
+ Mesh (MemLabelId label, ObjectCreationMode mode);
+ // ~Mesh (); declared-by-macro
+
+public:
+
+ virtual int GetRuntimeMemorySize () const;
+
+ VBO* GetSharedVBO( UInt32 wantedChannels );
+ bool CopyToVBO ( UInt32 wantedChannels, VBO& vbo );
+ void InitVertexBufferData ( UInt32 wantedChannels );
+ void GetVertexBufferData ( VertexBufferData& buffer, UInt32 wantedChannels );
+ void GetIndexBufferData (IndexBufferData& buffer);
+ void UnloadVBOFromGfxDevice();
+ void ReloadVBOToGfxDevice();
+
+
+ void AwakeFromLoad(AwakeFromLoadMode mode);
+ void AwakeFromLoadThreaded();
+ void UploadMeshData(bool markNoLongerReadable);
+
+ virtual bool MainThreadCleanup ();
+
+ void MarkDynamic();
+ void UpdateVertexFormat();
+
+ void SetBounds (const AABB& aabb );
+ const AABB& GetBounds () const { return m_LocalAABB; }
+
+ void SetBounds (unsigned submesh, const AABB& aabb );
+ const AABB& GetBounds (unsigned submesh) const
+ {
+ DebugAssertIf(submesh >= m_SubMeshes.size());
+ return m_SubMeshes[submesh].localAABB;
+ }
+
+ void Clear (bool keepVertexLayout);
+
+ /// Recalculate the bounding volume
+ void RecalculateBounds ();
+ void RecalculateSubmeshBounds (unsigned submesh);
+
+ // Recalculate normals
+ void RecalculateNormals();
+ void RecalculateNormalsWithHardAngle( float hardAngle );
+
+ // Validate that there are no out of bounds indices in the triangles
+ bool ValidateVertexCount (unsigned newVertexCount, const void* newTriangles, unsigned indexCount);
+
+ int GetVertexCount () const { return m_VertexData.GetVertexCount (); }
+
+ // Gets count in all submeshes.
+ int GetPrimitiveCount() const;
+ int CalculateTriangleCount() const; // ignores degenerates in strips
+
+ // NOTE: make sure to call SetChannelDirty and RecalculateBounds when changing the geometry!
+ StrideIterator<Vector3f> GetVertexBegin () const { return m_VertexData.MakeStrideIterator<Vector3f> (kShaderChannelVertex); }
+ StrideIterator<Vector3f> GetVertexEnd () const { return m_VertexData.MakeEndIterator<Vector3f> (kShaderChannelVertex); }
+
+ StrideIterator<Vector3f> GetNormalBegin () const { return m_VertexData.MakeStrideIterator<Vector3f> (kShaderChannelNormal); }
+ StrideIterator<Vector3f> GetNormalEnd () const { return m_VertexData.MakeEndIterator<Vector3f> (kShaderChannelNormal); }
+
+ StrideIterator<ColorRGBA32> GetColorBegin () const { return m_VertexData.MakeStrideIterator<ColorRGBA32> (kShaderChannelColor); }
+ StrideIterator<ColorRGBA32> GetColorEnd () const { return m_VertexData.MakeEndIterator<ColorRGBA32> (kShaderChannelColor); }
+
+ StrideIterator<Vector2f> GetUvBegin (int uvIndex = 0) const { return m_VertexData.MakeStrideIterator<Vector2f> ((ShaderChannel)(kShaderChannelTexCoord0 + uvIndex)); }
+ StrideIterator<Vector2f> GetUvEnd (int uvIndex = 0) const { return m_VertexData.MakeEndIterator<Vector2f> ((ShaderChannel)(kShaderChannelTexCoord0 + uvIndex)); }
+
+ StrideIterator<Vector4f> GetTangentBegin () const { return m_VertexData.MakeStrideIterator<Vector4f> (kShaderChannelTangent); }
+ StrideIterator<Vector4f> GetTangentEnd () const { return m_VertexData.MakeEndIterator<Vector4f> (kShaderChannelTangent); }
+
+ void ExtractVertexArray (Vector3f* destination) const;
+ void ExtractNormalArray (Vector3f* destination) const;
+ void ExtractColorArray (ColorRGBA32* destination) const;
+ void ExtractColorArrayConverting (ColorRGBAf* destination) const;
+ void ExtractUvArray (int uvIndex, Vector2f* destination) const;
+ void ExtractTangentArray (Vector4f* destination) const;
+
+ void SetVertices (Vector3f const* data, size_t count);
+ void SetNormals (Vector3f const* data, size_t count);
+ void SetTangents (Vector4f const* data, size_t count);
+ void SetUv (int uvIndex, Vector2f const* data, size_t count);
+ void SetColors (ColorRGBA32 const* data, size_t count);
+ void SetColorsConverting (ColorRGBAf const* data, size_t count);
+
+ bool GetVertexColorsSwizzled() const { return m_VertexColorsSwizzled; }
+ void SetVertexColorsSwizzled(bool flag) { m_VertexColorsSwizzled = flag; }
+ bool HasVertexData () const { return m_VertexData.GetDataPtr () != NULL; }
+ void* GetVertexDataPointer () const { return m_VertexData.GetDataPtr (); }
+ size_t GetVertexDataSize () const { return m_VertexData.GetDataSize (); }
+ size_t GetVertexSize () const { return m_VertexData.GetVertexSize(); }
+
+ const void* GetChannelPointer (ShaderChannel channel) const { return m_VertexData.GetDataPtr () + m_VertexData.GetChannelOffset (channel); }
+ void* GetChannelPointer (ShaderChannel channel) { return m_VertexData.GetDataPtr () + m_VertexData.GetChannelOffset (channel); }
+ void* GetChannelPointer (ShaderChannel channel, size_t offsetInElements) { return m_VertexData.GetDataPtr () + m_VertexData.GetChannelOffset (channel) + offsetInElements * m_VertexData.GetChannelStride(channel); }
+ size_t GetStride (ShaderChannel channel) const { return m_VertexData.GetChannelStride(channel); }
+
+ bool IsAvailable (ShaderChannel channel) const { return m_VertexData.HasChannel (channel); }
+ // returns a bitmask of a newly created channels
+ UInt32 ResizeVertices (size_t count, UInt32 shaderChannels, const VertexStreamsLayout& streams, const VertexChannelsLayout& channels);
+ UInt32 ResizeVertices (size_t count, UInt32 shaderChannels) { return ResizeVertices(count, shaderChannels, GetStreamsLayout(), GetChannelsLayout()); }
+
+ // returns a bitmask of a newly created channels
+ UInt32 FormatVertices (UInt32 shaderChannels);
+ // initializes the specified channels to default values
+ void InitChannelsToDefault (unsigned begin, unsigned count, unsigned shaderChannels);
+
+ bool SetBoneWeights (const BoneInfluence* v, int count);
+ const BoneInfluence* GetBoneWeights () const { return m_Skin.empty() ? NULL : &m_Skin[0]; }
+ BoneInfluence* GetBoneWeights () { return m_Skin.empty() ? NULL : &m_Skin[0]; }
+ void ClearSkinCache ();
+ int GetMaxBoneIndex ();
+
+ const Matrix4x4f* GetBindposes () const { return m_Bindpose.empty() ? NULL : &m_Bindpose[0]; }
+ int GetBindposeCount () const { return m_Bindpose.size(); }
+ void SetBindposes (const Matrix4x4f* bindposes, int count);
+
+ bool SetIndices (const UInt32* indices, unsigned count, unsigned submesh, GfxPrimitiveType topology);
+ bool SetIndices (const UInt16* indices, unsigned count, unsigned submesh, GfxPrimitiveType topology);
+
+ void GetTriangles (TemporaryIndexContainer& triangles, unsigned submesh) const;
+ void GetTriangles (TemporaryIndexContainer& triangles) const;
+ void AppendTriangles (TemporaryIndexContainer& triangles, unsigned submesh) const;
+ void GetStrips (TemporaryIndexContainer& triangles, unsigned submesh) const;
+ void GetIndices (TemporaryIndexContainer& triangles, unsigned submesh) const;
+
+ enum {
+ k16BitIndices = 1 << 0,
+ kRebuildCollisionTriangles = 1 << 2,
+ kDontAssignIndices = 1 << 3,
+ kDontSupportSubMeshVertexRanges = 1 << 4
+ };
+ bool SetIndicesComplex (const void* indices, unsigned count, unsigned submesh, GfxPrimitiveType topology, int mode);
+
+ bool ExtractTriangle (UInt32 face, UInt32* indices) const;
+
+ void SetSubMeshCount (unsigned int count);
+ size_t GetSubMeshCount () const;
+
+ void UpdateSubMeshVertexRange (int index);
+
+ void AddObjectUser( ListNode<Object>& node ) { m_ObjectUsers.push_back(node); }
+ void AddIntermediateUser( ListNode<IntermediateRenderer>& node ) { m_IntermediateUsers.AddUser(node); }
+
+ const BlendShapeData& GetBlendShapeData() const { return m_Shapes; }
+ size_t GetBlendShapeChannelCount() const { return m_Shapes.channels.size(); }
+ void SwapBlendShapeData (BlendShapeData& shapes);
+
+
+ BlendShapeData& GetWriteBlendShapeDataInternal() { return m_Shapes; }
+
+
+ void CheckConsistency();
+
+#if ENABLE_MULTITHREADED_CODE
+ void SetCurrentCPUFence( UInt32 fence ) { m_CurrentCPUFence = fence; m_WaitOnCPUFence = true; }
+#endif
+
+ void WaitOnRenderThreadUse();
+
+ static Mesh& GetInstantiatedMesh (Mesh* mesh, Object& owner);
+
+ void CopyTransformed (const Mesh& mesh, const Matrix4x4f& transform);
+
+ void SetChannelsDirty (unsigned vertexChannelsChanged, bool indices);
+
+ void* GetSharedNxMesh ();
+ void* GetSharedNxConvexMesh ();
+
+ void RebuildCollisionTriangles();
+
+ const SubMesh& GetSubMeshFast (unsigned int submesh) const
+ {
+ DebugAssertIf(submesh >= m_SubMeshes.size());
+ return m_SubMeshes[submesh];
+ }
+ SubMesh& GetSubMeshFast (unsigned int submesh)
+ {
+ DebugAssertIf(submesh >= m_SubMeshes.size());
+ return m_SubMeshes[submesh];
+ }
+
+ const UInt16* GetSubMeshBuffer16 (int submesh) const;
+ UInt16* GetSubMeshBuffer16 (int submesh);
+
+ int GetSubMeshBufferByteSize (int submesh) const { return kVBOIndexSize * m_SubMeshes[submesh].indexCount; }
+
+ // The number of indices contained in the index buffer (all submeshes)
+ int GetTotalndexCount () const;
+
+ void ByteSwapIndices ();
+
+ /// 4, 2, 1 bone influence (BoneInfluence, BoneInfluence2, int)
+ void* GetSkinInfluence (int count);
+
+ int GetMeshUsageFlags () const { return m_MeshUsageFlags; }
+
+ virtual bool ShouldIgnoreInGarbageDependencyTracking ();
+
+ UInt32 GetAvailableChannels() const;
+ // May return only a subset of channels that are present in the mesh
+ UInt32 GetAvailableChannelsForRendering() const;
+ UInt32 GetChannelsInVBO() const { return m_ChannelsInVBO; }
+
+ bool IsSuitableSizeForDynamicBatching () const;
+
+ // Calculate cached bone bounds per bone by calculating the bounding volume in bind pose space.
+ // This is used by the SkinnedMeshRenderer to compute an accurate world space bounding volume quickly.
+ const AABBContainer& GetCachedBonesBounds();
+
+ void DestripifyIndices ();
+ void SetHideFromRuntimeStats(bool flag) { m_HideFromRuntimeStats = flag; }
+
+ bool IsSharedPhysicsMeshDirty () { return m_CollisionMesh.IsSharedPhysicsMeshDirty(); }
+
+ bool CanAccessFromScript() const;
+
+ const VertexData& GetVertexData() const { return m_VertexData; }
+ VertexData& GetVertexData() { return m_VertexData; }
+
+ UInt8 GetMeshCompression() const { return m_MeshCompression; }
+ void SetMeshCompression(UInt8 mc) { m_MeshCompression = mc; }
+
+ enum
+ {
+ kStreamCompressionDefault = 0,
+ kStreamCompressionCompressed,
+ kStreamCompressionCompressedAggressive
+ };
+
+ UInt8 GetStreamCompression() const { return m_StreamCompression; }
+ void SetStreamCompression(UInt8 cs) { m_StreamCompression = cs; }
+ bool GetIsReadable() const { return m_IsReadable; }
+ void SetIsReadable(bool readable) { m_IsReadable = readable; }
+
+
+ bool GetKeepVertices() const { return m_KeepVertices; }
+ void SetKeepVertices(bool keep) { m_KeepVertices = keep; }
+
+ bool GetKeepIndices() const { return m_KeepIndices; }
+ void SetKeepIndices(bool keep) { m_KeepIndices = keep; }
+
+ const IndexContainer& GetIndexBuffer() const { return m_IndexBuffer; }
+ IndexContainer& GetIndexBuffer() { return m_IndexBuffer; }
+
+ const SubMeshContainer& GetSubMeshes() const { return m_SubMeshes; }
+ SubMeshContainer& GetSubMeshes() { return m_SubMeshes; }
+
+ const MatrixContainer& GetBindpose() const { return m_Bindpose; }
+ MatrixContainer& GetBindpose() { return m_Bindpose; }
+
+ const dynamic_array<BindingHash>& GetBonePathHashes() const { return m_BonePathHashes; }
+ dynamic_array<BindingHash>& GetBonePathHashes() { return m_BonePathHashes; }
+ BindingHash GetRootBonePathHash() const { return m_RootBonePathHash; }
+ void SetRootBonePathHash(BindingHash val) { m_RootBonePathHash = val; }
+
+ const BoneInfluenceContainer& GetSkin() const { return m_Skin; }
+ BoneInfluenceContainer& GetSkin() { return m_Skin; }
+
+ const AABB& GetLocalAABB() const { return m_LocalAABB; }
+ void SetLocalAABB(const AABB& aabb) { m_LocalAABB = aabb; }
+
+#if UNITY_PS3 || UNITY_EDITOR
+ MeshPartitionContainer m_Partitions;
+ MeshPartitionInfoContainer m_PartitionInfos;
+#endif
+
+
+#if UNITY_EDITOR
+ void SetMeshOptimized(bool meshOptimized) { m_MeshOptimized = meshOptimized; }
+ bool GetMeshOptimized() const { return m_MeshOptimized; }
+#endif
+
+ UInt32 GetInternalMeshID() const { Assert(m_InternalMeshID); return m_InternalMeshID; }
+
+private:
+ void CreateSharedVBO( UInt32 wantedChannels );
+ void NotifyObjectUsers( const MessageIdentifier& msg );
+ void RecalculateSubmeshBoundsInternal (unsigned submesh);
+ void RecalculateBoundsInternal ();
+ void LoadDeprecatedTangentData (Mesh& mesh, DeprecatedTangentsArray &tangents);
+ void SwizzleVertexColorsIfNeeded ();
+
+ const VertexStreamsLayout& GetStreamsLayout() const;
+ const VertexChannelsLayout& GetChannelsLayout() const;
+
+ void DestripifySubmeshOnTransferInternal();
+ void SetIndexData(int submeshIndex, int indexCount, const void* indices, GfxPrimitiveType topology, int mode);
+
+#if SUPPORT_SERIALIZED_TYPETREES
+ template<class TransferFunction>
+ void TransferWorkaround35SerializeFuckup (TransferFunction& transfer);
+#endif
+
+#if UNITY_EDITOR || UNITY_PS3
+ template<class TransferFunction>
+ void TransferPS3Data (TransferFunction& transfer);
+#endif
+#if UNITY_EDITOR
+ bool m_MeshOptimized;
+#endif
+
+ VertexData m_VertexData;
+
+ UInt8 m_MeshCompression;
+ UInt8 m_StreamCompression;
+ bool m_IsReadable;
+ bool m_KeepVertices;
+ bool m_KeepIndices;
+ UInt32 m_InternalMeshID;
+
+ int m_MeshUsageFlags;
+
+ IndexContainer m_IndexBuffer;
+ SubMeshContainer m_SubMeshes;
+ MatrixContainer m_Bindpose;
+ BlendShapeData m_Shapes;
+
+ dynamic_array<BindingHash> m_BonePathHashes;
+ BindingHash m_RootBonePathHash;
+
+ AABBContainer m_CachedBonesAABB;
+
+ BoneInfluenceContainer m_Skin;
+ BoneInfluence2Container m_CachedSkin2;
+ SkinContainer m_CachedSkin1;
+
+ int m_MaxBoneIndex;
+
+ AABB m_LocalAABB;
+
+ CollisionMeshData m_CollisionMesh;
+
+ typedef List< ListNode<Object> > ObjectList;
+ ObjectList m_ObjectUsers; // Object-derived users of this mesh
+
+ IntermediateUsers m_IntermediateUsers; // IntermediateRenderer users of this mesh
+
+ #if ENABLE_MULTITHREADED_CODE
+ UInt32 m_CurrentCPUFence;
+ bool m_WaitOnCPUFence;
+ #endif
+
+ PPtr<Object> m_Owner;
+ VBO* m_VBO;
+
+
+ UInt32 m_ChannelsInVBO;
+ bool m_VerticesDirty;
+ bool m_IndicesDirty;
+ bool m_IsDynamic;
+ bool m_HideFromRuntimeStats;
+ bool m_VertexColorsSwizzled;
+
+ friend class MeshFilter;
+ friend class ClothAnimator;
+ friend class CompressedMesh;
+ friend void PartitionSubmeshes (Mesh& m);
+ friend void OptimizeReorderVertexBuffer (Mesh& mesh);
+};
+
+#endif
diff --git a/Runtime/Filters/Mesh/LodMeshFilter.cpp b/Runtime/Filters/Mesh/LodMeshFilter.cpp
new file mode 100644
index 0000000..512f153
--- /dev/null
+++ b/Runtime/Filters/Mesh/LodMeshFilter.cpp
@@ -0,0 +1,96 @@
+#include "UnityPrefix.h"
+#include "LodMeshFilter.h"
+#include "LodMesh.h"
+#include "MeshRenderer.h"
+#include "Runtime/Filters/Particles/MeshParticleEmitter.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+
+MeshFilter::MeshFilter (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_Mesh = NULL;
+}
+
+MeshFilter::~MeshFilter ()
+{
+}
+
+void MeshFilter::OnDidAddMesh ()
+{
+ AssignMeshToRenderer ();
+}
+
+void MeshFilter::AssignMeshToRenderer ()
+{
+ if (GetGameObjectPtr())
+ {
+ MeshRenderer* renderer = QueryComponent(MeshRenderer);
+ if (renderer && renderer->GetSharedMesh() != m_Mesh)
+ renderer->SetSharedMesh(m_Mesh);
+
+ MeshParticleEmitter* emitter = QueryComponent(MeshParticleEmitter);
+ if (emitter && emitter->GetMesh() != m_Mesh)
+ emitter->SetMesh(m_Mesh);
+ }
+}
+
+void MeshFilter::SetSharedMesh (PPtr<Mesh> mesh)
+{
+ m_Mesh = mesh;
+
+ MeshRenderer* renderer = QueryComponent(MeshRenderer);
+ if (renderer)
+ renderer->SetSharedMesh(m_Mesh);
+
+ MeshParticleEmitter* emitter = QueryComponent(MeshParticleEmitter);
+ if (emitter)
+ emitter->SetMesh(m_Mesh);
+
+ SetDirty ();
+}
+
+PPtr<Mesh> MeshFilter::GetSharedMesh ()
+{
+ return m_Mesh;
+}
+
+Mesh* MeshFilter::GetInstantiatedMesh ()
+{
+ Mesh* instantiated = &Mesh::GetInstantiatedMesh (m_Mesh, *this);
+ if (PPtr<Mesh> (instantiated) != m_Mesh)
+ {
+ SetSharedMesh(instantiated);
+ }
+
+ return instantiated;
+}
+
+void MeshFilter::SetInstantiatedMesh (Mesh* mesh)
+{
+ SetSharedMesh(mesh);
+}
+
+IMPLEMENT_CLASS_HAS_INIT (MeshFilter)
+IMPLEMENT_OBJECT_SERIALIZE (MeshFilter)
+
+template<class TransferFunction> inline
+void MeshFilter::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.Transfer (m_Mesh, "m_Mesh", kSimpleEditorMask);
+}
+
+void MeshFilter::InitializeClass ()
+{
+ RegisterAllowNameConversion(GetClassStringStatic(), "m_LodMesh", "m_Mesh");
+ RegisterAllowTypeNameConversion ("PPtr<LodMesh>", "PPtr<Mesh>");
+
+ REGISTER_MESSAGE_VOID(MeshFilter, kDidAddComponent, OnDidAddMesh);
+}
+
+void MeshFilter::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ AssignMeshToRenderer ();
+}
diff --git a/Runtime/Filters/Mesh/LodMeshFilter.h b/Runtime/Filters/Mesh/LodMeshFilter.h
new file mode 100644
index 0000000..ff6273b
--- /dev/null
+++ b/Runtime/Filters/Mesh/LodMeshFilter.h
@@ -0,0 +1,38 @@
+#ifndef LODMESHFILTER_H
+#define LODMESHFILTER_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Modules/ExportModules.h"
+
+class Mesh;
+
+class EXPORT_COREMODULE MeshFilter : public Unity::Component
+{
+public:
+ REGISTER_DERIVED_CLASS (MeshFilter, Unity::Component)
+ DECLARE_OBJECT_SERIALIZE (MeshFilter)
+
+ MeshFilter (MemLabelId label, ObjectCreationMode mode);
+
+ void SetSharedMesh (PPtr<Mesh> mesh);
+ PPtr<Mesh> GetSharedMesh ();
+
+ Mesh* GetInstantiatedMesh ();
+ void SetInstantiatedMesh (Mesh* mesh);
+
+ static void InitializeClass ();
+ static void CleanupClass () {}
+
+ void OnDidAddMesh ();
+
+protected:
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+
+private:
+ void AssignMeshToRenderer ();
+
+ PPtr<Mesh> m_Mesh;
+};
+
+#endif
diff --git a/Runtime/Filters/Mesh/Mesh.h b/Runtime/Filters/Mesh/Mesh.h
new file mode 100644
index 0000000..e6b58dc
--- /dev/null
+++ b/Runtime/Filters/Mesh/Mesh.h
@@ -0,0 +1,76 @@
+#ifndef MESH_H
+#define MESH_H
+
+#include <vector>
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Misc/Allocator.h"
+
+class Quaternionf;
+
+/// A face in the mesh.
+struct Face {
+ UInt16 v1, v2, v3;
+ Face (UInt16 vert1, UInt16 vert2, UInt16 vert3)
+ {v1 = vert1; v2 = vert2; v3 = vert3;}
+ Face () {}
+
+ UInt16 &operator[] (int i) { return (&v1)[i]; }
+ UInt16 operator[] (int i) const { return (&v1)[i]; }
+
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (Face)
+};
+
+template<class TransferFunc>
+void Face::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (v1);
+ TRANSFER (v2);
+ TRANSFER (v3);
+}
+
+struct DeprecatedTangent
+{
+ Vector3f normal;
+ Vector3f tangent;
+ float handedness;
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (Tangent)
+};
+
+template<class TransferFunc>
+void DeprecatedTangent::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (normal);
+ TRANSFER (tangent);
+ TRANSFER (handedness);
+}
+
+struct BoneInfluence
+{
+ float weight[4];
+ int boneIndex[4];
+
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (BoneInfluence)
+};
+
+struct BoneInfluence2
+{
+ float weight[2];
+ int boneIndex[2];
+};
+
+template<class TransferFunc>
+void BoneInfluence::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (weight[0]);
+ TRANSFER (weight[1]);
+ TRANSFER (weight[2]);
+ TRANSFER (weight[3]);
+
+ TRANSFER (boneIndex[0]);
+ TRANSFER (boneIndex[1]);
+ TRANSFER (boneIndex[2]);
+ TRANSFER (boneIndex[3]);
+}
+
+#endif
diff --git a/Runtime/Filters/Mesh/MeshBlendShape.cpp b/Runtime/Filters/Mesh/MeshBlendShape.cpp
new file mode 100644
index 0000000..c7588e2
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshBlendShape.cpp
@@ -0,0 +1,234 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "MeshBlendShape.h"
+#include "Runtime/mecanim/generic/crc32.h"
+
+static const float kVertexDeltaEpsilon = 1e-5f;
+static const float kNormalDeltaEpsilon = 1e-5f;
+
+void SetBlendShapeVertices(const std::vector<Vector3f>& deltaVertices, const std::vector<Vector3f>& deltaNormals, const std::vector<Vector3f>& deltaTangents, BlendShapeVertices& sharedSparceVertices, BlendShape& frame)
+{
+ Assert(deltaNormals.empty() || deltaVertices.size() == deltaNormals.size());
+ Assert(deltaTangents.empty() || deltaVertices.size() == deltaTangents.size());
+
+ frame.firstVertex = sharedSparceVertices.size();
+
+ // Converting blend shape in to sparse blend shape
+ sharedSparceVertices.reserve(sharedSparceVertices.size() + deltaVertices.size());
+
+ frame.hasNormals = frame.hasTangents = false;
+
+ for (int j = 0; j < deltaVertices.size(); ++j)
+ {
+ const bool vertexHasNormal = (!deltaNormals.empty() && Magnitude(deltaNormals[j]) > kNormalDeltaEpsilon);
+ const bool vertexHasTangent = (!deltaTangents.empty() && Magnitude(deltaTangents[j]) > kNormalDeltaEpsilon);
+
+ frame.hasNormals = frame.hasNormals || vertexHasNormal;
+ frame.hasTangents = frame.hasTangents || vertexHasTangent;
+
+ if (Magnitude(deltaVertices[j]) > kVertexDeltaEpsilon || vertexHasNormal || vertexHasTangent)
+ {
+ BlendShapeVertex v;
+
+ v.vertex = deltaVertices[j];
+ if (!deltaNormals.empty())
+ v.normal = deltaNormals[j];
+ if (!deltaTangents.empty())
+ v.tangent = deltaTangents[j];
+
+ v.index = j;
+ sharedSparceVertices.push_back(v);
+ }
+ }
+
+ frame.vertexCount = sharedSparceVertices.size() - frame.firstVertex;
+}
+
+void BlendShape::UpdateFlags(const BlendShapeVertices& sharedSparceVertices)
+{
+ hasNormals = hasTangents = false;
+
+ for (int j = 0; j < vertexCount; ++j)
+ {
+ const BlendShapeVertex& v = sharedSparceVertices[firstVertex + j];
+ const bool vertexHasNormal = Magnitude(v.normal) > kNormalDeltaEpsilon;
+ const bool vertexHasTangent = Magnitude(v.tangent) > kNormalDeltaEpsilon;
+
+ hasNormals = hasNormals || vertexHasNormal;
+ hasTangents = hasTangents || vertexHasTangent;
+ }
+}
+
+void InitializeChannel (const UnityStr& inName, int frameIndex, int frameCount, BlendShapeChannel& channel)
+{
+ channel.name.assign(inName.c_str(), kMemGeometry);
+ channel.nameHash = mecanim::processCRC32(inName.c_str());
+ channel.frameIndex = frameIndex;
+ channel.frameCount = frameCount;
+}
+
+const char* GetChannelName (const BlendShapeData& data, int index)
+{
+ return data.channels[index].name.c_str();
+}
+
+int GetChannelIndex (const BlendShapeData& data, const char* name)
+{
+ for (int i=0;i<data.channels.size();i++)
+ {
+ if (name == data.channels[i].name)
+ return i;
+ }
+ return -1;
+}
+
+int GetChannelIndex (const BlendShapeData& data, BindingHash name)
+{
+ for (int i=0;i<data.channels.size();i++)
+ {
+ if (name == data.channels[i].nameHash)
+ return i;
+ }
+ return -1;
+}
+
+void ClearBlendShapes (BlendShapeData& data)
+{
+ data.vertices.clear();
+ data.shapes.clear();
+ data.channels.clear();
+ data.fullWeights.clear();
+}
+
+/*
+
+STRUCT BlendShapeChannel
+
+// BlendShape vertex class.
+STRUCT Vertex
+// Vertex delta.
+CSRAW public Vector3 vertex;
+
+// Normal delta.
+CSRAW public Vector3 normal;
+
+// Tangent delta.
+CSRAW public Vector3 tangent;
+
+// Index to [[Mesh]] vertex data.
+CSRAW public int index;
+END
+
+// A class representing a single BlendShape (also called morph-target).
+STRUCT BlendShape
+
+// The weight of the frame
+CSRAW public float weight;
+
+// Sparse vertex data.
+CSRAW public Vertex[] vertices;
+END
+
+// Name of the BlendShape.
+CSRAW public string name;
+
+// The frames making up a blendshape animation.
+// Each frame has a weight, based on the weight of the BlendShape in the SkinnedMeshRenderer, Unity will apply 1 or 2 frames.
+CSRAW public BlendShape[] shapes;
+END
+
+
+C++RAW
+/*
+ struct MonoMeshBlendShape
+ {
+ ScriptingStringPtr name;
+ ScriptingArrayPtr vertices;
+ };
+
+ void BlendShapeVertexToMono (const BlendShapeVertex &src, MonoBlendShapeVertex &dest) {
+ dest.vertex = src.vertex;
+ dest.normal = src.normal;
+ dest.tangent = src.tangent;
+ dest.index = src.index;
+ }
+ void BlendShapeVertexToCpp (const MonoBlendShapeVertex &src, BlendShapeVertex &dest) {
+ dest.vertex = src.vertex;
+ dest.normal = src.normal;
+ dest.tangent = src.tangent;
+ dest.index = src.index;
+ }
+
+ class MeshBlendShapeToMono
+ {
+ public:
+ MeshBlendShapeToMono(const BlendShapeVertices& sharedVertices_) : sharedVertices(sharedVertices_) {}
+
+ void operator() (const MeshBlendShape &src, MonoMeshBlendShape &dest)
+ {
+ dest.name = scripting_string_new(src.m_Name);
+ const BlendShapeVertices vertices(sharedVertices.begin() + src.firstVertex, sharedVertices.begin() + src.firstVertex + src.vertexCount);
+
+ ScriptingTypePtr classVertex = GetScriptingTypeRegistry().GetType("UnityEngine", "BlendShapeVertex");
+ dest.vertices = VectorToScriptingStructArray<BlendShapeVertex, MonoBlendShapeVertex>(vertices, classVertex, BlendShapeVertexToMono);
+ }
+
+ private:
+ const BlendShapeVertices& sharedVertices;
+ };
+
+ class MeshBlendShapeToCpp
+ {
+ public:
+ MeshBlendShapeToCpp(int meshVertexCount_, BlendShapeVertices& sharedVertices_) : meshVertexCount(meshVertexCount_), sharedVertices(sharedVertices_) {}
+
+ void operator() (MonoMeshBlendShape &src, MeshBlendShape &dest)
+ {
+ dest.weight = src.weight;
+
+ const BlendShapeVertex* vertices = Scripting::GetScriptingArrayStart<BlendShapeVertex> (src.vertices);
+ sharedVertices.insert(sharedVertices.end(), vertices, vertices + GetScriptingArraySize(src.vertices));
+
+ for (BlendShapeVertices::iterator it = vertices.begin(), end = vertices.end(); it != end; ++it)
+ {
+ BlendShapeVertex& v = *it;
+ if (v.index < 0 || v.index >= meshVertexCount)
+ {
+ ErrorStringMsg("Value (%d) of BlendShapeVertex.index #%d is out of bounds (Mesh vertex count: %d) on BlendShape '%s'. It will be reset to 0.", v.index, it - vertices.begin(), meshVertexCount, dest.m_Name.c_str());
+ v.index = 0;
+ }
+ }
+
+ dest.firstVertex = sharedVertices.size();
+ dest.vertexCount = vertices.size();
+
+ sharedVertices.insert(sharedVertices.end(), vertices.begin(), vertices.end());
+ dest.UpdateFlags(sharedVertices);
+ }
+
+ private:
+ int meshVertexCount;
+ BlendShapeVertices& sharedVertices;
+ };
+
+
+
+ ----------------
+
+ // BlendShapes for this mesh.
+ CUSTOM_PROP BlendShapeChannel[] blendShapes
+ {
+ // ScriptingTypePtr classBlendShape = GetScriptingTypeRegistry().GetType("UnityEngine", "MeshBlendShape");
+ // return VectorToScriptingStructArray<MeshBlendShape, MonoMeshBlendShape>(self->GetShapesVector(), classBlendShape, MeshBlendShapeToMono(self->GetShapeVertexVector()));
+ return SCRIPTING_NULL;
+ }
+ {
+ // Mesh::MeshBlendShapeContainer shapes;
+ // self->GetShapeVertexVector().clear();
+ // ScriptingStructArrayToVector<MeshBlendShape, MonoMeshBlendShape>(value, shapes, MeshBlendShapeToCpp(self->GetVertexCount(), self->GetShapeVertexVector()));
+ // self->SwapShapesVector(shapes);
+ }
+
+
+
+ */
diff --git a/Runtime/Filters/Mesh/MeshBlendShape.h b/Runtime/Filters/Mesh/MeshBlendShape.h
new file mode 100644
index 0000000..d4d0f41
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshBlendShape.h
@@ -0,0 +1,115 @@
+#ifndef MESHBLENDSHAPES_H
+#define MESHBLENDSHAPES_H
+
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Containers/ConstantString.h"
+#include "Runtime/Containers/ConstantStringSerialization.h"
+
+typedef UInt32 BindingHash;
+
+struct BlendShapeVertex
+{
+ // vertex, normal & tangent are stored as deltas
+ Vector3f vertex;
+ Vector3f normal;
+ Vector3f tangent;
+ UInt32 index;
+
+ BlendShapeVertex() : vertex(Vector3f::zero), normal(Vector3f::zero), tangent(Vector3f::zero), index(0) {}
+
+ DECLARE_SERIALIZE_NO_PPTR (BlendShapeVertex)
+};
+typedef dynamic_array<BlendShapeVertex> BlendShapeVertices;
+
+struct BlendShapeChannel
+{
+ ConstantString name;
+ BindingHash nameHash;
+
+ int frameIndex;
+ int frameCount;
+
+ DECLARE_SERIALIZE_NO_PPTR(MeshBlendShapeChannel)
+};
+
+struct BlendShape
+{
+ BlendShape() : firstVertex(0), vertexCount(0), hasNormals(false), hasTangents(false) {}
+
+ UInt32 firstVertex;
+ UInt32 vertexCount;
+
+ bool hasNormals;
+ bool hasTangents;
+
+
+ ///@TODO: MOve
+ // updates hasNormals and hasTangents based on data in vertices
+ void UpdateFlags(const BlendShapeVertices& sharedSparceVertices);
+
+ DECLARE_SERIALIZE_NO_PPTR (MeshBlendShape)
+};
+
+struct BlendShapeData
+{
+ BlendShapeVertices vertices;
+ dynamic_array<BlendShape> shapes;
+ std::vector<BlendShapeChannel> channels;
+ dynamic_array<float> fullWeights;
+
+ DECLARE_SERIALIZE_NO_PPTR(BlendShapeData)
+};
+
+
+// Convert between blendshape name and index
+const char* GetChannelName (const BlendShapeData& data, int index);
+inline size_t GetBlendShapeChannelCount (const BlendShapeData& data) { return data.channels.size(); }
+int GetChannelIndex (const BlendShapeData& data, const char* name);
+int GetChannelIndex (const BlendShapeData& data, BindingHash name);
+
+// data is passed as non-sparce arrays, i.e. deltaVertices.size() has to be the same as vertex count on the Mesh
+void SetBlendShapeVertices(const std::vector<Vector3f>& deltaVertices, const std::vector<Vector3f>& deltaNormals, const std::vector<Vector3f>& deltaTangents, BlendShapeVertices& sharedSparceVertices, BlendShape& frame);
+void InitializeChannel (const UnityStr& inName, int frameIndex, int frameCount, BlendShapeChannel& channel);
+void ClearBlendShapes (BlendShapeData& data);
+
+template<class TransferFunc>
+void BlendShape::Transfer (TransferFunc& transfer)
+{
+ TRANSFER(firstVertex);
+ TRANSFER(vertexCount);
+ TRANSFER(hasNormals);
+ TRANSFER(hasTangents);
+ transfer.Align();
+}
+
+template<class TransferFunc>
+void BlendShapeData::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (vertices);
+ TRANSFER (shapes);
+ TRANSFER (channels);
+ TRANSFER (fullWeights);
+}
+
+template<class TransferFunc>
+void BlendShapeVertex::Transfer (TransferFunc& transfer)
+{
+ TRANSFER(vertex);
+ TRANSFER(normal);
+ TRANSFER(tangent);
+ TRANSFER(index);
+}
+
+template<class TransferFunc>
+void BlendShapeChannel::Transfer (TransferFunc& transfer)
+{
+ TransferConstantString (name, "name", kNoTransferFlags, kMemGeometry, transfer);
+ TRANSFER (nameHash);
+ TRANSFER (frameIndex);
+ TRANSFER (frameCount);
+}
+
+#endif
diff --git a/Runtime/Filters/Mesh/MeshBlendShaping.cpp b/Runtime/Filters/Mesh/MeshBlendShaping.cpp
new file mode 100644
index 0000000..a86a24d
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshBlendShaping.cpp
@@ -0,0 +1,184 @@
+#include "UnityPrefix.h"
+#include "MeshBlendShaping.h"
+#include "MeshSkinning.h"
+#include "MeshBlendShape.h"
+
+template<bool skinNormal, bool skinTangent>
+void ApplyBlendShapeTmpl (const BlendShapeVertex* vertices, size_t vertexCount, size_t dstVertexCount, float weight, int normalOffset, int tangentOffset, int inStride, UInt8* dst)
+{
+ for (int i = 0; i < vertexCount; ++i)
+ {
+ const BlendShapeVertex& blendShapeVertex = vertices[i];
+
+ int offset = inStride * blendShapeVertex.index;
+
+ *reinterpret_cast<Vector3f*>(dst + offset) += blendShapeVertex.vertex * weight;
+ if (skinNormal)
+ {
+ DebugAssert (offset + normalOffset < inStride * dstVertexCount);
+ *reinterpret_cast<Vector3f*>(dst + offset + normalOffset) += blendShapeVertex.normal * weight;
+ }
+ if (skinTangent)
+ {
+ DebugAssert (offset + tangentOffset < inStride * dstVertexCount);
+ *reinterpret_cast<Vector3f*>(dst + offset + tangentOffset) += blendShapeVertex.tangent * weight;
+ }
+ }
+}
+
+
+void ApplyBlendShape (const BlendShape& target, const BlendShapeVertices& vertices, float weight, const SkinMeshInfo& info, UInt8* dst)
+{
+ if (!HasValidWeight(weight))
+ return;
+
+ weight = std::min(weight, 1.0F);
+
+ const BlendShapeVertex* v = vertices.begin() + target.firstVertex;
+
+ if (info.skinNormals && info.skinTangents && target.hasNormals && target.hasTangents)
+ ApplyBlendShapeTmpl<true, true> (v, target.vertexCount, info.vertexCount, weight, info.normalOffset, info.tangentOffset, info.inStride, dst);
+ else if (info.skinNormals && target.hasNormals)
+ ApplyBlendShapeTmpl<true, false> (v, target.vertexCount, info.vertexCount, weight, info.normalOffset, info.tangentOffset, info.inStride, dst);
+ else
+ ApplyBlendShapeTmpl<false, false> (v, target.vertexCount, info.vertexCount, weight, info.normalOffset, info.tangentOffset, info.inStride, dst);
+}
+
+static int FindFrame (const float* weights, size_t count, float targetWeight)
+{
+ // Find frame (left index)
+ int frame = 0;
+ while (frame < count-1 && targetWeight > weights[frame+1])
+ frame++;
+
+ return frame;
+}
+
+void ApplyBlendShapes (SkinMeshInfo& info, UInt8* dst)
+{
+ DebugAssert (info.blendshapeCount != 0);
+ Assert (info.inStride == info.outStride);
+ const int inStride = info.inStride;
+ const int count = info.vertexCount;
+
+ Assert (dst);
+ memcpy (dst, info.inVertices, inStride * count);
+
+ const BlendShapeData& blendShapeData = *info.blendshapes;
+
+ for (int c = 0; c < info.blendshapeCount; ++c)
+ {
+ const float targetWeight = info.blendshapeWeights[c];
+
+ if (!HasValidWeight (targetWeight))
+ continue;
+
+ const BlendShapeChannel& channel = blendShapeData.channels[c];
+ Assert(channel.frameCount != 0);
+
+ const BlendShape* blendShapeFrames = &blendShapeData.shapes[channel.frameIndex];
+ const float* weights = &blendShapeData.fullWeights[channel.frameIndex];
+
+ // The first blendshape does not need to do any blending. Just fade it in.
+ if (targetWeight < weights[0] || channel.frameCount == 1)
+ {
+ float lhsShapeWeight = weights[0];
+ ApplyBlendShape (blendShapeFrames[0], blendShapeData.vertices, targetWeight / lhsShapeWeight, info, dst);
+ }
+ // We are blending with two frames
+ else
+ {
+ // Find the frame we are blending with
+ int frame = FindFrame(weights, channel.frameCount, targetWeight);
+
+ float lhsShapeWeight = weights[frame + 0];
+ float rhsShapeWeight = weights[frame + 1];
+
+ float relativeWeight = (targetWeight - lhsShapeWeight) / (rhsShapeWeight - lhsShapeWeight);
+
+ ApplyBlendShape (blendShapeFrames[frame + 0], blendShapeData.vertices, 1.0F - relativeWeight, info, dst);
+ ApplyBlendShape (blendShapeFrames[frame + 1], blendShapeData.vertices, relativeWeight, info, dst);
+ }
+ }
+}
+
+///@TODO: How do we deal with resizing vertex count once mesh blendshapes have been created???
+
+/*
+ template<bool skinNormal, bool skinTangent>
+ static void ApplyBlendShapesTmpl (SkinMeshInfo& info, UInt8* dst)
+ {
+ DebugAssert (info.blendshapeCount != 0);
+ Assert (info.inStride == info.outStride);
+ const int inStride = info.inStride;
+ const int count = info.vertexCount;
+
+ Assert (dst);
+ memcpy (dst, info.inVertices, inStride * count);
+
+ const int normalOffset = info.normalOffset;
+ const int tangentOffset = info.tangentOffset;
+
+ #if BLEND_DIRECT_NORMALS
+ if (skinNormal)
+ { // figure out how what fraction of original normal should be used
+ float totalBlendshapeWeight = 0.0f;
+ for (int i = 0; i < info.blendshapeCount; ++i)
+ totalBlendshapeWeight += info.blendshapeWeights[i];
+ Assert (totalBlendshapeWeight >= 0.0f);
+ if (totalBlendshapeWeight > 0.0f)
+ {
+ for (int i = 0; i < count; ++i)
+ *reinterpret_cast<Vector3f*>(dst + i*inStride + normalOffset) *= max(0.0f, (1.0f - totalBlendshapeWeight));
+ }
+ }
+
+ bool atLeastOneSparseBlendshape = false;
+ #endif
+ for (int bs = 0; bs < info.blendshapeCount; ++bs)
+ {
+ const float w = info.blendshapeWeights[bs];
+
+ if (HasWeight(w))
+ {
+ const MeshBlendShape& blendShape = info.blendshapes[bs];
+
+ const BlendShapeVertex* vertices = info.blendshapesVertices + blendShape.firstVertex;
+ for (int i = 0; i < blendShape.vertexCount; ++i)
+ {
+ const BlendShapeVertex& blendShapeVertex = vertices[i];
+
+ int offset = inStride * blendShapeVertex.index;
+ Assert (offset < inStride * count);
+ *reinterpret_cast<Vector3f*>(dst + offset) += blendShapeVertex.vertex * w;
+ if (skinNormal)
+ {
+ Assert (offset + normalOffset < inStride * count);
+ *reinterpret_cast<Vector3f*>(dst + offset + normalOffset) += blendShapeVertex.normal * w;
+ }
+ if (skinTangent)
+ {
+ Assert (offset + tangentOffset < inStride * count);
+ *reinterpret_cast<Vector3f*>(dst + offset + tangentOffset) += blendShapeVertex.tangent * w;
+ }
+ }
+
+ #if BLEND_DIRECT_NORMALS
+ if (vertices.size () < count)
+ atLeastOneSparseBlendshape = true;
+ #endif
+ }
+ }
+
+ #if BLEND_DIRECT_NORMALS
+ if (atLeastOneSparseBlendshape && skinNormal) // we might need to take larger fraction from original normal
+ for (int i = 0; i < count; ++i)
+ {
+ Vector3f const& srcNormal = *reinterpret_cast<Vector3f*>((UInt8*)info.inVertices + i*inStride + normalOffset);
+ Vector3f* dstNormal = reinterpret_cast<Vector3f*>(dst + i*inStride + normalOffset);
+ const float missingFractionOfNormal = max (0.0f, 1.0f - Magnitude (*dstNormal));
+ *dstNormal += srcNormal * missingFractionOfNormal;
+ }
+ #endif
+ }
+*/ \ No newline at end of file
diff --git a/Runtime/Filters/Mesh/MeshBlendShaping.h b/Runtime/Filters/Mesh/MeshBlendShaping.h
new file mode 100644
index 0000000..7b39f26
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshBlendShaping.h
@@ -0,0 +1,12 @@
+#pragma once
+
+struct SkinMeshInfo;
+
+// Does "mesh skinning" logic for BlendShapes
+void ApplyBlendShapes (SkinMeshInfo& info, UInt8* dst);
+
+inline bool HasValidWeight(const float w)
+{
+ const float kWeightEpsilon = 1e-4f;
+ return w > kWeightEpsilon;
+}
diff --git a/Runtime/Filters/Mesh/MeshCombiner.cpp b/Runtime/Filters/Mesh/MeshCombiner.cpp
new file mode 100644
index 0000000..1bf93e5
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshCombiner.cpp
@@ -0,0 +1,502 @@
+#include "UnityPrefix.h"
+#include "MeshCombiner.h"
+#include "Runtime/Graphics/TriStripper.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Profiler/Profiler.h"
+#include <limits>
+
+
+#define sqr(x) ((x)*(x))
+
+PROFILER_INFORMATION(gCombineMeshesProfile, "CombineMeshes", kProfilerRender)
+PROFILER_INFORMATION(gCombineVerticesProfile, "CombineVertices", kProfilerRender)
+PROFILER_INFORMATION(gCombineIndicesProfile, "CombineIndices", kProfilerRender)
+
+static void CombineBoneSkinning (const CombineInstances &in, Mesh& outCombinedMesh);
+
+
+size_t ExtractMeshIndices(Mesh::TemporaryIndexContainer& srcIndices, const CombineInstance& in, bool useVertexOffsets, size_t& inoutTotalVertexOffset, UInt16* dstIndices)
+{
+ srcIndices.clear();
+
+ if (in.subMeshIndex < 0 || in.subMeshIndex >= in.mesh->GetSubMeshCount())
+ return 0;
+
+ const int subMeshIndex = in.subMeshIndex;
+ const int vertexOffset = useVertexOffsets ? in.vertexOffset : inoutTotalVertexOffset;
+ inoutTotalVertexOffset += in.mesh->GetVertexCount();
+
+ in.mesh->GetTriangles( srcIndices, subMeshIndex );
+
+ size_t numIndices = srcIndices.size();
+ if (Dot (Cross(in.transform.GetAxisX(), in.transform.GetAxisY()), in.transform.GetAxisZ()) >= 0)
+ {
+ for ( size_t k=0; k!=numIndices; ++k )
+ dstIndices[k] = srcIndices[k] + vertexOffset;
+ }
+ else
+ {
+ // if trilist, then
+ // reverse Cull order by reversing indices
+ for ( size_t k=0; k!=numIndices; ++k )
+ dstIndices[k] = srcIndices[numIndices-k-1] + vertexOffset;
+ }
+
+ return numIndices;
+}
+
+static bool IsMeshBatchable (const Mesh* mesh, int subMeshIndex)
+{
+ return mesh && mesh->HasVertexData() && subMeshIndex >= 0 && subMeshIndex < mesh->GetSubMeshCount();
+}
+
+
+void CombineMeshIndicesForStaticBatching(const CombineInstances& in, Mesh& inoutMesh, bool mergeSubMeshes, bool useVertexOffsets)
+{
+ PROFILER_AUTO(gCombineIndicesProfile, &inoutMesh);
+
+ size_t size = in.size();
+
+ UInt32 maxIndices = 0;
+ for ( size_t i=0; i!=size; ++i )
+ {
+ if (IsMeshBatchable(in[i].mesh, in[i].subMeshIndex))
+ {
+ const UInt32 numTris = in[i].mesh->GetSubMeshFast( in[i].subMeshIndex ).indexCount;
+ if (mergeSubMeshes)
+ maxIndices += numTris;
+ else
+ maxIndices = std::max( maxIndices, numTris );
+ }
+ }
+
+ UInt16* dstIndices = new UInt16[maxIndices+1];
+ Mesh::TemporaryIndexContainer srcIndices;
+ srcIndices.reserve( maxIndices+1 );
+
+ size_t totalVertexOffset = 0;
+ if (mergeSubMeshes)
+ {
+ inoutMesh.SetSubMeshCount( 1 );
+ size_t totalNumIndices = 0;
+ for ( size_t s=0; s!=size; ++s )
+ {
+ if (in[s].mesh)
+ {
+ size_t numIndices = ExtractMeshIndices (srcIndices, in[s], useVertexOffsets, totalVertexOffset, dstIndices+totalNumIndices);
+
+ totalNumIndices += numIndices;
+ Assert(totalNumIndices <= (maxIndices+1));
+ }
+ }
+ int mask = Mesh::k16BitIndices;
+ inoutMesh.SetIndicesComplex (dstIndices, totalNumIndices, 0, kPrimitiveTriangles, mask);
+ }
+ else
+ {
+ inoutMesh.SetSubMeshCount( in.size() );
+ for ( size_t s=0; s!=size; ++s )
+ {
+ if (in[s].mesh)
+ {
+ size_t numIndices = ExtractMeshIndices (srcIndices, in[s], useVertexOffsets, totalVertexOffset, dstIndices);
+ Assert(numIndices <= (maxIndices+1));
+
+ int mask = Mesh::k16BitIndices;
+ inoutMesh.SetIndicesComplex (dstIndices, numIndices, s, kPrimitiveTriangles, mask);
+ }
+ }
+ }
+
+ delete []dstIndices;
+}
+
+void CombineMeshVerticesForStaticBatching ( const CombineInstances& in, const string& combinedMeshName, Mesh& outCombinedMesh, bool useTransforms )
+{
+ PROFILER_AUTO(gCombineVerticesProfile, &outCombinedMesh);
+
+ int vertexCount = 0;
+ size_t size = in.size();
+ for( size_t i=0; i!=size; ++i )
+ {
+ if (IsMeshBatchable(in[i].mesh, in[i].subMeshIndex))
+ vertexCount += in[i].mesh->GetVertexCount();
+ }
+
+ bool hasNormals = false;
+ bool hasTangents = false;
+ bool hasUV0 = false;
+ bool hasUV1 = false;
+ bool hasColors = false;
+ bool hasSkin = false;
+ int bindposeCount = 0;
+
+ for( size_t i=0; i!=size; ++i )
+ {
+ if (IsMeshBatchable(in[i].mesh, in[i].subMeshIndex))
+ {
+ const Mesh* mesh = in[i].mesh;
+ const UInt32 channels = mesh->GetAvailableChannels();
+ hasNormals |= (channels & (1<<kShaderChannelNormal)) != 0;
+ hasTangents |= (channels & (1<<kShaderChannelTangent)) != 0;
+ hasUV0 |= (channels & (1<<kShaderChannelTexCoord0)) != 0;
+ hasUV1 |= (channels & (1<<kShaderChannelTexCoord1)) != 0 || (in[i].lightmapTilingOffset != Vector4f(1, 1, 0, 0));
+ hasColors |= (channels & (1<<kShaderChannelColor)) != 0;
+ hasSkin |= mesh->GetSkin().size() && mesh->GetBindpose().size();
+ bindposeCount += mesh->GetBindpose().size();
+ }
+ }
+
+ UInt32 channels = 1<<kShaderChannelVertex;
+ if ( hasNormals ) channels |= 1<<kShaderChannelNormal;
+ if ( hasTangents ) channels |= 1<<kShaderChannelTangent;
+ if ( hasUV0 ) channels |= 1<<kShaderChannelTexCoord0;
+ if ( hasUV1 ) channels |= 1<<kShaderChannelTexCoord1;
+ if ( hasColors ) channels |= 1<<kShaderChannelColor;
+
+ outCombinedMesh.Clear(true);
+ outCombinedMesh.ResizeVertices( vertexCount, channels );
+ outCombinedMesh.SetName( combinedMeshName.c_str() );
+ // Input meshes are already swizzled correctly, so we can copy colors directly
+ outCombinedMesh.SetVertexColorsSwizzled(gGraphicsCaps.needsToSwizzleVertexColors);
+
+ if ( hasSkin )
+ {
+ outCombinedMesh.GetSkin().resize_initialized(vertexCount);
+ outCombinedMesh.GetBindpose().resize_initialized(bindposeCount);
+ outCombinedMesh.GetBonePathHashes().resize_uninitialized(bindposeCount);
+ }
+
+ // avoid doing twice (in worst case)
+ Matrix4x4f* normalMatrices;
+ bool* isNonUniformScaleTransform;
+ ALLOC_TEMP (normalMatrices, Matrix4x4f, size);
+ ALLOC_TEMP (isNonUniformScaleTransform, bool, size);
+ if ( hasNormals || hasTangents )
+ {
+ for( size_t i=0; i!=size; ++i )
+ {
+ float uniformScale;
+ TransformType type = ComputeTransformType(in[i].transform, uniformScale);
+ Matrix4x4f m;
+ isNonUniformScaleTransform[i] = IsNonUniformScaleTransform(type);
+ if (isNonUniformScaleTransform[i])
+ {
+ Matrix4x4f::Invert_General3D( in[i].transform, normalMatrices[i] );
+ normalMatrices[i].Transpose();
+ }
+ else
+ {
+ normalMatrices[i] = Matrix3x3f(in[i].transform);
+ // Scale matrix to keep normals normalized
+ normalMatrices[i].Scale(Vector3f::one * (1.0f/uniformScale));
+ }
+ }
+ }
+
+ int offset = 0;
+ for( size_t i=0; i!=size; ++i )
+ {
+ if (IsMeshBatchable(in[i].mesh, in[i].subMeshIndex))
+ {
+ const Matrix4x4f& transform = in[i].transform;
+ const Mesh* mesh = in[i].mesh;
+ if (useTransforms)
+ TransformPoints3x4 (transform,
+ (Vector3f const*)mesh->GetChannelPointer (kShaderChannelVertex),
+ mesh->GetStride (kShaderChannelVertex),
+ (Vector3f*)outCombinedMesh.GetChannelPointer (kShaderChannelVertex, offset),
+ outCombinedMesh.GetStride (kShaderChannelVertex),
+ mesh->GetVertexCount());
+ else
+ strided_copy (mesh->GetVertexBegin (), mesh->GetVertexEnd (), outCombinedMesh.GetVertexBegin () + offset);
+ offset += mesh->GetVertexCount();
+ }
+ }
+
+ if ( hasNormals )
+ {
+ offset = 0;
+ for( size_t i=0; i!=size; ++i )
+ {
+ if (IsMeshBatchable(in[i].mesh, in[i].subMeshIndex))
+ {
+ const Mesh* mesh = in[i].mesh;
+ int vertexCount = mesh->GetVertexCount ();
+ if (!mesh->IsAvailable (kShaderChannelNormal))
+ std::fill(outCombinedMesh.GetNormalBegin () + offset, outCombinedMesh.GetNormalBegin () + offset + vertexCount, Vector3f(0.0f,1.0f,0.0f));
+ else
+ {
+ const Matrix4x4f& transform = normalMatrices[i];
+
+ StrideIterator<Vector3f> outNormal = outCombinedMesh.GetNormalBegin () + offset;
+ if (useTransforms)
+ {
+ if (isNonUniformScaleTransform[i])
+ {
+ for (StrideIterator<Vector3f> it = mesh->GetNormalBegin (), end = mesh->GetNormalEnd (); it != end; ++it, ++outNormal)
+ *outNormal = Normalize( transform.MultiplyVector3( *it) );
+ }
+ else
+ {
+ for (StrideIterator<Vector3f> it = mesh->GetNormalBegin (), end = mesh->GetNormalEnd (); it != end; ++it, ++outNormal)
+ *outNormal = transform.MultiplyVector3( *it);
+ }
+ }
+ else
+ strided_copy (mesh->GetNormalBegin (), mesh->GetNormalEnd (), outCombinedMesh.GetNormalBegin () + offset);
+ }
+ offset += vertexCount;
+ }
+ }
+ }
+
+ if ( hasTangents )
+ {
+ offset = 0;
+ for ( size_t i=0; i!=size; ++i )
+ {
+ if (IsMeshBatchable(in[i].mesh, in[i].subMeshIndex))
+ {
+ const Mesh* mesh = in[i].mesh;
+ int vertexCount = mesh->GetVertexCount ();
+ if (!mesh->IsAvailable (kShaderChannelTangent))
+ std::fill(outCombinedMesh.GetTangentBegin () + offset, outCombinedMesh.GetTangentBegin () + offset + vertexCount, Vector4f(1.0f,0.0f,0.0f,1.0f));
+ else
+ {
+ const Matrix4x4f& transform = normalMatrices[i];
+
+ StrideIterator<Vector4f> outTanget = outCombinedMesh.GetTangentBegin () + offset;
+ if (useTransforms)
+ {
+ if (isNonUniformScaleTransform[i])
+ {
+ for (StrideIterator<Vector4f> it = mesh->GetTangentBegin (), end = mesh->GetTangentEnd (); it != end; ++it, ++outTanget)
+ {
+ Vector3f t3 = Normalize(transform.MultiplyVector3(Vector3f(it->x, it->y, it->z)));
+ *outTanget = Vector4f(t3.x,t3.y,t3.z,it->w);
+ }
+ }
+ else
+ {
+ for (StrideIterator<Vector4f> it = mesh->GetTangentBegin (), end = mesh->GetTangentEnd (); it != end; ++it, ++outTanget)
+ {
+ Vector3f t3 = transform.MultiplyVector3(Vector3f(it->x, it->y, it->z));
+ *outTanget = Vector4f(t3.x,t3.y,t3.z,it->w);
+ }
+ }
+ }
+ else
+ strided_copy (mesh->GetTangentBegin (), mesh->GetTangentEnd (), outCombinedMesh.GetTangentBegin () + offset);
+ }
+ offset += vertexCount;
+ }
+ }
+ }
+
+ if ( hasUV0 )
+ {
+ offset = 0;
+ for ( size_t i=0; i!=size; ++i )
+ {
+ if (IsMeshBatchable(in[i].mesh, in[i].subMeshIndex))
+ {
+ const Mesh* mesh = in[i].mesh;
+ int vertexCount = mesh->GetVertexCount ();
+ if (!mesh->IsAvailable (kShaderChannelTexCoord0))
+ std::fill (outCombinedMesh.GetUvBegin (0) + offset, outCombinedMesh.GetUvBegin (0) + offset + vertexCount, Vector2f(0.0f,0.0f));
+ else
+ strided_copy (mesh->GetUvBegin (0), mesh->GetUvEnd (0), outCombinedMesh.GetUvBegin (0) + offset);
+ offset += vertexCount;
+ }
+ }
+ }
+
+ if ( hasUV1 )
+ {
+ offset = 0;
+ for ( size_t i=0; i!=size; ++i )
+ {
+ if (IsMeshBatchable(in[i].mesh, in[i].subMeshIndex))
+ {
+ const Mesh* mesh = in[i].mesh;
+ const int uvIndex = (mesh->GetAvailableChannels() & (1<<kShaderChannelTexCoord1))!=0? 1 : 0;
+ StrideIterator<Vector2f> it = in[i].mesh->GetUvBegin( uvIndex );
+ StrideIterator<Vector2f> end = in[i].mesh->GetUvEnd( uvIndex );
+
+ int vertexCount = mesh->GetVertexCount ();
+ if ( it == end)
+ std::fill (outCombinedMesh.GetUvBegin (1) + offset, outCombinedMesh.GetUvBegin (1) + offset + vertexCount, Vector2f(0.0f,0.0f));
+ else
+ {
+ // we have to apply lightmap UV scale and offset factors
+ // callee is responsible to reset lightmapTilingOffset on the Renderer afterwards
+ const Vector4f uvScaleOffset = in[i].lightmapTilingOffset;
+ if ( uvScaleOffset != Vector4f(1, 1, 0, 0) )
+ {
+ StrideIterator<Vector2f> outUV = outCombinedMesh.GetUvBegin (1) + offset;
+ for (; it != end; ++it, ++outUV)
+ {
+ outUV->x = it->x * uvScaleOffset.x + uvScaleOffset.z;
+ outUV->y = it->y * uvScaleOffset.y + uvScaleOffset.w;
+ }
+ }
+ else
+ strided_copy (it, end, outCombinedMesh.GetUvBegin (1) + offset);
+ }
+ offset += vertexCount;
+ }
+ }
+ }
+
+ if ( hasColors )
+ {
+ offset = 0;
+ for ( size_t i=0; i!=size; ++i )
+ {
+ if (IsMeshBatchable(in[i].mesh, in[i].subMeshIndex))
+ {
+ const Mesh* mesh = in[i].mesh;
+ int vertexCount = mesh->GetVertexCount ();
+ if (!mesh->IsAvailable (kShaderChannelColor))
+ std::fill (outCombinedMesh.GetColorBegin () + offset, outCombinedMesh.GetColorBegin () + offset + vertexCount, ColorRGBA32(255,255,255,255));
+ else
+ {
+ DebugAssert(mesh->GetVertexColorsSwizzled() == outCombinedMesh.GetVertexColorsSwizzled());
+ strided_copy (mesh->GetColorBegin (), mesh->GetColorEnd (), outCombinedMesh.GetColorBegin () + offset);
+ }
+ offset += vertexCount;
+ }
+ }
+ }
+
+ if ( hasSkin )
+ {
+ CombineBoneSkinning (in, outCombinedMesh);
+ }
+}
+
+static void CalculateRootBonePathHash (const CombineInstances &in, Mesh& outCombinedMesh)
+{
+ // We always pick the root bone path hash of the first combine instance.
+ // This is because anything else gives unpredictable behaviour and makes it impossible for the user
+ // to setup the skinned mesh renderer T/R/S correctly.
+ outCombinedMesh.SetRootBonePathHash(in[0].mesh->GetRootBonePathHash());
+
+ // If we made it so that the skinnedmeshrenderer always used the default pose from the Avatar
+ // Then it would be possible to pick the root bone from the mesh with the most bones instead.
+#if 0
+ size_t size = in.size();
+
+ BindingHash rootBonePathHash = 0;
+ int boneCount = 0;
+ for (size_t i=0; i<size; ++i)
+ {
+ }
+ }
+ if (rootBonePathHash)
+ outCombinedMesh.SetRootBonePathHash(rootBonePathHash);
+#endif
+}
+
+static void CombineBoneSkinning (const CombineInstances &in, Mesh& outCombinedMesh)
+{
+ size_t size = in.size();
+
+ int boneOffset = 0;
+ int offset = 0;
+ for ( size_t i=0; i!=size; ++i )
+ {
+ if (!IsMeshBatchable(in[i].mesh, in[i].subMeshIndex))
+ continue;
+
+ const Mesh* mesh = in[i].mesh;
+ Mesh::BoneInfluenceContainer& outSkin = outCombinedMesh.GetSkin();
+ const Mesh::BoneInfluenceContainer& inSkin = mesh->GetSkin();
+ int vertexCount = mesh->GetVertexCount ();
+ if (inSkin.empty())
+ {
+ for(int i=0; i<vertexCount;i++)
+ {
+ outSkin[offset+i].weight[0] = 0;
+ outSkin[offset+i].weight[1] = 0;
+ outSkin[offset+i].weight[2] = 0;
+ outSkin[offset+i].weight[3] = 0;
+ outSkin[offset+i].boneIndex[0] = 0;
+ outSkin[offset+i].boneIndex[1] = 0;
+ outSkin[offset+i].boneIndex[2] = 0;
+ outSkin[offset+i].boneIndex[3] = 0;
+ }
+ }
+ else
+ {
+ for(int i=0; i<vertexCount;i++)
+ {
+ outSkin[offset+i].weight[0] = inSkin[i].weight[0];
+ outSkin[offset+i].weight[1] = inSkin[i].weight[1];
+ outSkin[offset+i].weight[2] = inSkin[i].weight[2];
+ outSkin[offset+i].weight[3] = inSkin[i].weight[3];
+ outSkin[offset+i].boneIndex[0] = inSkin[i].boneIndex[0]+boneOffset;
+ outSkin[offset+i].boneIndex[1] = inSkin[i].boneIndex[1]+boneOffset;
+ outSkin[offset+i].boneIndex[2] = inSkin[i].boneIndex[2]+boneOffset;
+ outSkin[offset+i].boneIndex[3] = inSkin[i].boneIndex[3]+boneOffset;
+ }
+ }
+
+ offset += vertexCount;
+
+ int poseCount = mesh->GetBindpose().size();
+ int bindingHashCount = mesh->GetBonePathHashes().size();
+
+ memcpy(outCombinedMesh.GetBindpose().begin() + boneOffset, mesh->GetBindpose().begin(), poseCount*sizeof(Matrix4x4f));
+
+ // Old asset bundles might not have bindingHashCount in sync with bind poses.
+ if (poseCount == bindingHashCount)
+ memcpy(outCombinedMesh.GetBonePathHashes().begin () + boneOffset, mesh->GetBonePathHashes().begin(), poseCount*sizeof(BindingHash));
+ else
+ memset(outCombinedMesh.GetBonePathHashes().begin () + boneOffset, 0, poseCount*sizeof(BindingHash));
+
+ boneOffset += poseCount;
+ }
+
+ CalculateRootBonePathHash (in, outCombinedMesh);
+}
+
+
+void CombineMeshes (const CombineInstances &in, Mesh& out, bool mergeSubMeshes, bool useTransforms)
+{
+ if (!out.CanAccessFromScript())
+ {
+ ErrorStringMsg("Cannot combine into mesh that does not allow access: %s", out.GetName());
+ return;
+ }
+ for (size_t i = 0; i < in.size(); ++i)
+ {
+ Mesh* mesh = in[i].mesh;
+ if (!mesh)
+ {
+ WarningStringMsg("Combine mesh instance %" PRINTF_SIZET_FORMAT " is null.", i);
+ }
+ if (mesh && (in[i].subMeshIndex < 0 || in[i].subMeshIndex >= mesh->GetSubMeshCount()))
+ {
+ WarningStringMsg("Submesh index %d is invalid for mesh %s.", in[i].subMeshIndex, mesh->GetName());
+ }
+ if (mesh && !mesh->CanAccessFromScript())
+ {
+ ErrorStringMsg("Cannot combine mesh that does not allow access: %s", mesh->GetName());
+ return;
+ }
+ if (mesh == &out)
+ {
+ ErrorStringMsg("Cannot combine into a mesh that is also in the CombineInstances input: %s", mesh->GetName());
+ return;
+ }
+ }
+
+ CombineMeshVerticesForStaticBatching (in, out.GetName(), out, useTransforms);
+ CombineMeshIndicesForStaticBatching (in, out, mergeSubMeshes, false);
+
+ out.RecalculateBounds();
+ out.UpdateVertexFormat();
+}
+
diff --git a/Runtime/Filters/Mesh/MeshCombiner.h b/Runtime/Filters/Mesh/MeshCombiner.h
new file mode 100644
index 0000000..a6975a9
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshCombiner.h
@@ -0,0 +1,33 @@
+#ifndef MESHCOMBINER_H
+#define MESHCOMBINER_H
+
+#include "LodMesh.h"
+
+class Renderer;
+
+struct CombineInstance
+{
+ Mesh *mesh;
+ int subMeshIndex;
+ Matrix4x4f transform;
+
+ Vector4f lightmapTilingOffset;
+ int vertexOffset;
+
+ CombineInstance() :
+ mesh(NULL),
+ subMeshIndex(0),
+ lightmapTilingOffset(1, 1, 0, 0),
+ vertexOffset(0)
+ {}
+};
+
+typedef std::vector<CombineInstance> CombineInstances;
+
+void CombineMeshes (const CombineInstances &in, Mesh& out, bool mergeSubMeshes, bool useTransforms);
+// takes an array of meshes(their vertex data) and merges them into 1 combined mesh.
+void CombineMeshVerticesForStaticBatching ( const CombineInstances& in, const string& combinedMeshName, Mesh& outCombinedMesh, bool useTransforms = true );
+// takes an array of meshes(their indices) and merges them in 1 mesh (setups subsets)
+void CombineMeshIndicesForStaticBatching (const CombineInstances& in, Mesh& inoutMesh, bool mergeSubMeshes, bool useVertexOffsets);
+
+#endif
diff --git a/Runtime/Filters/Mesh/MeshOptimizer.cpp b/Runtime/Filters/Mesh/MeshOptimizer.cpp
new file mode 100644
index 0000000..068dc53
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshOptimizer.cpp
@@ -0,0 +1,359 @@
+#include "UnityPrefix.h"
+#include "MeshOptimizer.h"
+#include <vector>
+
+//@TODO:
+
+// Step 1
+
+//* bool ExtractCollisionData (Mesh& mesh, UNITY_TEMP_VECTOR(kMemGeometry, Vector3f)& vertices, UNITY_TEMP_VECTOR(kMemGeometry, UInt32)& triangles);
+// -> make it return welded vertices and triangle array
+//* Enable Deformablemesh code and make it work with welding code and check that cloth works visually...
+
+// Testing:
+//* Check mesh collision detection code to work visually correct.
+// * run functional test suite
+// * run lightmapper tests in the integration test suite. They have a complete test for the lightmap uv coordinates picking up lightmap values...
+
+
+// Step 2:
+//* Verify vertex cache performance on iPad1 / Wii / intel integrated graphics
+//* Switch to default gpu optimized mode and update all model importer templates
+
+
+
+template<typename T, const int CACHE_SIZE>
+class VertexCacheOptimizer
+{
+ UInt32* m_cacheEntries;
+ UInt32 m_cacheSize;
+
+ mutable UInt32 m_cacheMisses;
+ mutable UInt32 m_cacheHits;
+
+ UInt32 GetInCache(UInt32 lIndex, const char* vertexInCache) const
+ {
+ return vertexInCache[lIndex] ? 1 : 0;
+ }
+
+ void AddToCache(UInt32 lIndex, char* vertexInCache)
+ {
+ if(m_cacheEntries[0]!=-1)
+ vertexInCache[m_cacheEntries[0]]=0;
+
+ for(UInt32 i=0; i<m_cacheSize-1; i++)
+ m_cacheEntries[i]=m_cacheEntries[i+1];
+
+ m_cacheEntries[m_cacheSize-1]=lIndex;
+ vertexInCache[lIndex]=1;
+ }
+
+public:
+
+ VertexCacheOptimizer () : m_cacheSize(CACHE_SIZE)
+ {
+ m_cacheEntries=new UInt32 [m_cacheSize];
+
+ m_cacheHits = m_cacheMisses = 0;
+ for(UInt32 i=0; i<m_cacheSize; i++)
+ m_cacheEntries[i]=(UInt32)-1;
+ }
+
+ ~VertexCacheOptimizer() { delete m_cacheEntries; }
+
+ UInt32 GetCacheMisses() { return m_cacheMisses; }
+ UInt32 GetCacheHits() { return m_cacheHits; }
+
+ void OptimizeTriangles(T* pdstTris, UInt32 numVertices, const T* srcTris, UInt32 numTriangles)
+ {
+ UInt32 cachedVerts=0;
+ char* triangleUsed=new char [numTriangles];
+ char* vertexInCache=new char [numVertices];
+ memset(triangleUsed,0,numTriangles);
+ memset(vertexInCache,0,numVertices);
+
+ bool foundTriangle=true;
+ while (foundTriangle)
+ {
+ foundTriangle=false;
+ UInt32 bestCandidate=0;
+ UInt32 bestCacheValue=0;
+ for (UInt32 i = 0; i < numTriangles; i++)
+ {
+ if (triangleUsed[i])
+ continue;
+
+ foundTriangle=true;
+ UInt32 i1=srcTris[i*3+0];
+ UInt32 i2=srcTris[i*3+1];
+ UInt32 i3=srcTris[i*3+2];
+
+ UInt32 lCacheValue=GetInCache(i1,vertexInCache)+GetInCache(i2,vertexInCache)+GetInCache(i3,vertexInCache)+1;
+ if (lCacheValue > bestCacheValue)
+ {
+ bestCandidate=i;
+ bestCacheValue=lCacheValue;
+ if (bestCacheValue == 4)
+ break;
+ }
+ }
+ if(foundTriangle)
+ {
+ triangleUsed[bestCandidate]=1;
+ UInt32 i1=srcTris[bestCandidate*3+0];
+ UInt32 i2=srcTris[bestCandidate*3+1];
+ UInt32 i3=srcTris[bestCandidate*3+2];
+ *pdstTris++=(T)i1;
+ *pdstTris++=(T)i2;
+ *pdstTris++=(T)i3;
+ if (!GetInCache(i1,vertexInCache)) { AddToCache(i1,vertexInCache); cachedVerts++; m_cacheMisses++; } else m_cacheHits++;
+ if (!GetInCache(i2,vertexInCache)) { AddToCache(i2,vertexInCache); cachedVerts++; m_cacheMisses++; } else m_cacheHits++;
+ if (!GetInCache(i3,vertexInCache)) { AddToCache(i3,vertexInCache); cachedVerts++; m_cacheMisses++; } else m_cacheHits++;
+ }
+ }
+ delete[] triangleUsed;
+ delete[] vertexInCache;
+ }
+};
+
+inline bool CompareBlendShapeVertexIndex (const BlendShapeVertex& lhs, const BlendShapeVertex& rhs)
+{
+ return lhs.index < rhs.index;
+}
+
+void OptimizeReorderVertexBuffer (Mesh& mesh)
+{
+ const int submeshCount = mesh.GetSubMeshCount();
+ const int vertexCount = mesh.GetVertexCount();
+
+ // backup required data
+ VertexData backupVertexData(mesh.m_VertexData, mesh.GetAvailableChannels(), mesh.GetVertexData().GetStreamsLayout(), mesh.GetVertexData().GetChannelsLayout());
+
+ Mesh::BoneInfluenceContainer backupSkin;
+ if (!mesh.m_Skin.empty())
+ backupSkin.swap(mesh.m_Skin);
+
+ // reorder the vertices so they come in increasing order
+ dynamic_array<UInt32> oldToNew;
+ dynamic_array<UInt32> newToOld;
+ newToOld.resize_initialized(vertexCount, 0xFFFFFFFF);
+ oldToNew.resize_initialized(vertexCount, 0xFFFFFFFF);
+
+ Mesh::TemporaryIndexContainer dstIndices;
+ int newVertexCount = 0;
+ for (int submesh = 0; submesh < submeshCount; submesh++)
+ {
+ Mesh::TemporaryIndexContainer indices;
+ mesh.GetTriangles (indices, submesh);
+
+ const int indexCount = indices.size();
+ dstIndices.resize(indexCount);
+ for (int index=0; index < indexCount; index++)
+ {
+ int vertex = indices[index];
+ AssertBreak(vertex >= 0);
+ AssertBreak(vertex < vertexCount);
+
+ if (oldToNew[vertex] == 0xFFFFFFFF)
+ {
+ oldToNew[vertex]=newVertexCount;
+ newToOld[newVertexCount]=vertex;
+ newVertexCount++;
+ }
+ dstIndices[index] = oldToNew[vertex];
+ }
+
+ mesh.SetIndices (&dstIndices[0], dstIndices.size(), submesh, kPrimitiveTriangles);
+ }
+
+ mesh.ResizeVertices(newVertexCount, backupVertexData.GetChannelMask());
+
+ if (!backupSkin.empty())
+ mesh.m_Skin.resize_initialized(newVertexCount);
+
+ for (int vertex=0; vertex < newVertexCount; vertex++)
+ {
+ UInt32 remapNew = newToOld[vertex];
+ Assert(remapNew != 0xFFFFFFFF);
+
+ if (!backupSkin.empty())
+ mesh.m_Skin[vertex] = backupSkin[remapNew];
+
+ mesh.GetVertexBegin()[vertex] = backupVertexData.MakeStrideIterator<Vector3f> (kShaderChannelVertex)[remapNew];
+
+ if (backupVertexData.HasChannel(kShaderChannelNormal))
+ mesh.GetNormalBegin()[vertex] = backupVertexData.MakeStrideIterator<Vector3f> (kShaderChannelNormal)[remapNew];
+
+ if (backupVertexData.HasChannel(kShaderChannelColor))
+ mesh.GetColorBegin()[vertex] = backupVertexData.MakeStrideIterator<ColorRGBA32> (kShaderChannelColor)[remapNew];
+
+ if (backupVertexData.HasChannel(kShaderChannelTexCoord0))
+ mesh.GetUvBegin(0)[vertex] = backupVertexData.MakeStrideIterator<Vector2f> (kShaderChannelTexCoord0)[remapNew];
+
+ if (backupVertexData.HasChannel(kShaderChannelTexCoord1))
+ mesh.GetUvBegin(1)[vertex] = backupVertexData.MakeStrideIterator<Vector2f> (kShaderChannelTexCoord1)[remapNew];
+
+ if (backupVertexData.HasChannel(kShaderChannelTangent))
+ mesh.GetTangentBegin()[vertex] = backupVertexData.MakeStrideIterator<Vector4f> (kShaderChannelTangent)[remapNew];
+ }
+
+ // Remap vertex indices stored in blend shapes
+ BlendShapeData& blendShapeData = mesh.GetWriteBlendShapeDataInternal();
+ BlendShapeVertices& blendShapeVertices = blendShapeData.vertices;
+ for (BlendShapeVertices::iterator itv = blendShapeVertices.begin(), endv = blendShapeVertices.end(); itv != endv; ++itv)
+ {
+ BlendShapeVertex& bsv = *itv;
+ bsv.index = oldToNew[bsv.index];
+ }
+
+ // Sort each shape's vertices by index so the blending writes to memory as linearly as possible
+ for (int shapeIndex = 0; shapeIndex < blendShapeData.shapes.size(); shapeIndex++)
+ {
+ const BlendShape& shape = blendShapeData.shapes[shapeIndex];
+ BlendShapeVertex* vertices = &blendShapeVertices[shape.firstVertex];
+ std::sort(vertices, vertices + shape.vertexCount, CompareBlendShapeVertexIndex);
+ }
+
+ mesh.SetChannelsDirty(mesh.GetAvailableChannels(), true);
+}
+
+void OptimizeIndexBuffers (Mesh& mesh)
+{
+ const int submeshCount = mesh.GetSubMeshCount();
+ const int vertexCount = mesh.GetVertexCount();
+
+ // first optimize the indices for each submesh
+ for (int submesh = 0; submesh < submeshCount; submesh++)
+ {
+ Mesh::TemporaryIndexContainer unoptimizedIndices;
+ mesh.GetTriangles (unoptimizedIndices, submesh);
+
+ Mesh::TemporaryIndexContainer optimizedIndices;
+ optimizedIndices.resize(unoptimizedIndices.size());
+
+ VertexCacheOptimizer<UInt32, 16> vertexCacheOptimizer;
+ vertexCacheOptimizer.OptimizeTriangles(&optimizedIndices[0], vertexCount, &unoptimizedIndices[0], unoptimizedIndices.size() / 3);
+ // LogString(Format("[Optimize] mesh: %s: submesh: %d hits: %d misses: %d\n", mesh.GetName(), submesh, vertexCacheOptimizer.GetCacheHits(), vertexCacheOptimizer.GetCacheMisses()));
+
+ mesh.SetIndices (&optimizedIndices[0], optimizedIndices.size(), submesh, kPrimitiveTriangles);
+ }
+}
+
+
+template<typename T, const int CACHE_SIZE>
+class VertexCacheDeOptimizer
+{
+ UInt32* m_cacheEntries;
+ UInt32 m_cacheSize;
+
+ mutable UInt32 m_cacheMisses;
+ mutable UInt32 m_cacheHits;
+
+ UInt32 GetInCache(UInt32 lIndex, const char* vertexInCache) const
+ {
+ return vertexInCache[lIndex] ? 1 : 0;
+ }
+
+ void AddToCache(UInt32 lIndex, char* vertexInCache)
+ {
+ if(m_cacheEntries[0]!=-1)
+ vertexInCache[m_cacheEntries[0]]=0;
+
+ for(UInt32 i=0; i<m_cacheSize-1; i++)
+ m_cacheEntries[i]=m_cacheEntries[i+1];
+
+ m_cacheEntries[m_cacheSize-1]=lIndex;
+ vertexInCache[lIndex]=1;
+ }
+
+public:
+
+ VertexCacheDeOptimizer () : m_cacheSize(CACHE_SIZE)
+ {
+ m_cacheEntries=new UInt32 [m_cacheSize];
+
+ m_cacheHits = m_cacheMisses = 0;
+ for(UInt32 i=0; i<m_cacheSize; i++)
+ m_cacheEntries[i]=(UInt32)-1;
+ }
+
+ ~VertexCacheDeOptimizer() { delete m_cacheEntries; }
+
+ UInt32 GetCacheMisses() { return m_cacheMisses; }
+ UInt32 GetCacheHits() { return m_cacheHits; }
+
+ void DeOptimizeTriangles(T* pdstTris, UInt32 numVertices, const T* srcTris, UInt32 numTriangles)
+ {
+ UInt32 cachedVerts=0;
+ char* triangleUsed=new char [numTriangles];
+ char* vertexInCache=new char [numVertices];
+ memset(triangleUsed,0,numTriangles);
+ memset(vertexInCache,0,numVertices);
+
+ bool foundTriangle=true;
+ while (foundTriangle)
+ {
+ foundTriangle=false;
+ UInt32 bestCandidate=0;
+ UInt32 bestCacheValue=4;
+ for (UInt32 i = 0; i < numTriangles; i++)
+ {
+ if (triangleUsed[i])
+ continue;
+
+ foundTriangle=true;
+ UInt32 i1=srcTris[i*3+0];
+ UInt32 i2=srcTris[i*3+1];
+ UInt32 i3=srcTris[i*3+2];
+
+ UInt32 lCacheValue=GetInCache(i1,vertexInCache)+GetInCache(i2,vertexInCache)+GetInCache(i3,vertexInCache)+1;
+ if (lCacheValue <= bestCacheValue)
+ {
+ bestCandidate=i;
+ bestCacheValue=lCacheValue;
+ if (bestCacheValue == 1)
+ break;
+ }
+ }
+ if(foundTriangle)
+ {
+ triangleUsed[bestCandidate]=1;
+ UInt32 i1=srcTris[bestCandidate*3+0];
+ UInt32 i2=srcTris[bestCandidate*3+1];
+ UInt32 i3=srcTris[bestCandidate*3+2];
+ *pdstTris++=(T)i1;
+ *pdstTris++=(T)i2;
+ *pdstTris++=(T)i3;
+ if (!GetInCache(i1,vertexInCache)) { AddToCache(i1,vertexInCache); cachedVerts++; m_cacheMisses++; } else m_cacheHits++;
+ if (!GetInCache(i2,vertexInCache)) { AddToCache(i2,vertexInCache); cachedVerts++; m_cacheMisses++; } else m_cacheHits++;
+ if (!GetInCache(i3,vertexInCache)) { AddToCache(i3,vertexInCache); cachedVerts++; m_cacheMisses++; } else m_cacheHits++;
+ }
+ }
+ delete triangleUsed;
+ delete vertexInCache;
+ }
+};
+
+void DeOptimizeIndexBuffers (Mesh& mesh)
+{
+ const int submeshCount = mesh.GetSubMeshCount();
+ const int vertexCount = mesh.GetVertexCount();
+
+ // first optimize the indices for each submesh
+ for (int submesh = 0; submesh < submeshCount; submesh++)
+ {
+ Mesh::TemporaryIndexContainer unoptimizedIndices;
+ mesh.GetTriangles (unoptimizedIndices, submesh);
+
+ Mesh::TemporaryIndexContainer deOptimizedIndices;
+ deOptimizedIndices.resize(unoptimizedIndices.size());
+
+ VertexCacheDeOptimizer<UInt32, 16> vertexCacheDeOptimizer;
+ vertexCacheDeOptimizer.DeOptimizeTriangles(&deOptimizedIndices[0], vertexCount, &unoptimizedIndices[0], unoptimizedIndices.size() / 3);
+
+ //LogString(Format("[Deoptimize] mesh: %s: submesh: %d hits: %d misses: %d\n", mesh.GetName(), submesh, vertexCacheDeOptimizer.GetCacheHits(), vertexCacheDeOptimizer.GetCacheMisses()));
+
+ mesh.SetIndices (&deOptimizedIndices[0], deOptimizedIndices.size(), submesh, kPrimitiveTriangles);
+ }
+}
+
diff --git a/Runtime/Filters/Mesh/MeshOptimizer.h b/Runtime/Filters/Mesh/MeshOptimizer.h
new file mode 100644
index 0000000..8964edf
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshOptimizer.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#ifndef __importmeshoptimizer_h_included__
+#define __importmeshoptimizer_h_included__
+
+#include "Runtime/Filters/Mesh/LodMesh.h"
+
+void DeOptimizeIndexBuffers (Mesh& mesh);
+void OptimizeIndexBuffers (Mesh& mesh);
+void OptimizeReorderVertexBuffer (Mesh& mesh);
+
+
+#endif //__importmeshoptimizer_h_included__
diff --git a/Runtime/Filters/Mesh/MeshPartitioner.cpp b/Runtime/Filters/Mesh/MeshPartitioner.cpp
new file mode 100644
index 0000000..9ec9f87
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshPartitioner.cpp
@@ -0,0 +1,346 @@
+
+#include "UnityPrefix.h"
+#include "MeshPartitioner.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+
+#if UNITY_EDITOR
+
+static const UInt32 ComponentStride[] = { 12, 12, 4, 8, 8, 16, sizeof(BoneInfluence) };
+
+static int CalcDMABatchSize(int totalVerts, int stride, const int sizeRestriction, bool padded)
+{
+ const int alignmentRestriction = 16; // DMA transfers address must be a multiple of 16
+ int a = alignmentRestriction;
+
+ if(a>stride)
+ {
+ if(a % stride == 0)
+ return sizeRestriction;
+ while(a % stride) { a+=alignmentRestriction; }
+ }
+ else
+ {
+ if(stride % a == 0)
+ return sizeRestriction;
+ while(stride % a) { a+=alignmentRestriction; }
+ }
+
+ int batchMultiple = a / stride;
+ totalVerts = (totalVerts < sizeRestriction) ? totalVerts : sizeRestriction;
+ if(padded)
+ totalVerts += batchMultiple - 1;
+ totalVerts /= batchMultiple;
+ totalVerts *= batchMultiple;
+ return totalVerts;
+};
+
+static int CalcBestFitBatchSize(const UInt32 availableChannels, int vertexCount, int maxVerts, bool padded = false)
+{
+ int bestFit = INT_MAX;
+ for(int i=0;i<=kShaderChannelCount;i++)
+ {
+ if (availableChannels & (1<<i))
+ {
+ int maxVCount = CalcDMABatchSize(vertexCount, ComponentStride[i], maxVerts, padded);
+ bestFit = (bestFit > maxVCount) ? maxVCount : bestFit;
+ }
+ }
+ return bestFit;
+}
+
+template<typename T>
+struct TempPartition
+{
+ dynamic_array<Vector3f> m_Vertices;
+ dynamic_array<Vector2f> m_UV;
+ dynamic_array<Vector2f> m_UV1;
+ dynamic_array<ColorRGBA32> m_Colors;
+ dynamic_array<Vector3f> m_Normals;
+ dynamic_array<Vector4f> m_Tangents;
+ dynamic_array<BoneInfluence> m_Skin;
+ dynamic_array<T> indexBuffer;
+ dynamic_array<T> newToOld;
+ int vertexCount;
+ //
+ void InitRemapping(int numVertices)
+ {
+ newToOld.resize_uninitialized(numVertices);
+ memset(&newToOld[0],(T)-1,numVertices*sizeof(T));
+ }
+ void RemapVertices(Mesh& mesh, int actualVertexCount)
+ {
+ m_Vertices.resize_uninitialized(vertexCount);
+ const UInt32 channels = mesh.GetAvailableChannels();
+ if(channels&(1<<kShaderChannelNormal))
+ m_Normals.resize_uninitialized(vertexCount);
+ if(channels&(1<<kShaderChannelTexCoord0))
+ m_UV.resize_uninitialized(vertexCount);
+ if(channels&(1<<kShaderChannelTexCoord1))
+ m_UV1.resize_uninitialized(vertexCount);
+ if(channels&(1<<kShaderChannelTangent))
+ m_Tangents.resize_uninitialized(vertexCount);
+ if(channels&(1<<kShaderChannelColor))
+ m_Colors.resize_uninitialized(vertexCount);
+ if(!mesh.GetSkin().empty())
+ m_Skin.resize_uninitialized(vertexCount);
+
+ T remapNew = 0;
+ for(int vertex=0; vertex<vertexCount; vertex++)
+ {
+ if((T)-1 != newToOld[vertex])
+ remapNew = newToOld[vertex];
+ m_Vertices[vertex]=mesh.GetVertexBegin()[remapNew];
+ if(channels&(1<<kShaderChannelNormal))
+ m_Normals[vertex]=mesh.GetNormalBegin()[remapNew];
+ if(channels&(1<<kShaderChannelTexCoord0))
+ m_UV[vertex]=mesh.GetUvBegin(0)[remapNew];
+ if(channels&(1<<kShaderChannelTexCoord1))
+ m_UV1[vertex]=mesh.GetUvBegin(1)[remapNew];
+ if(channels&(1<<kShaderChannelTangent))
+ m_Tangents[vertex]=mesh.GetTangentBegin()[remapNew];
+ if(channels&(1<<kShaderChannelColor))
+ m_Colors[vertex]=mesh.GetColorBegin()[remapNew];
+ if(!mesh.GetSkin().empty())
+ m_Skin[vertex]=mesh.GetSkin()[remapNew];
+ }
+ }
+};
+
+template<typename T>
+struct SegmentedMesh
+{
+ std::vector<TempPartition<T> > m_Partitions;
+ void Clear() { m_Partitions.clear(); }
+};
+
+template<typename T>
+static void CreateFromSubMesh(std::vector< SegmentedMesh<T> >& segments, Mesh& mesh, int submesh)
+{
+ SubMesh& sm = mesh.GetSubMeshFast(submesh);
+
+ T vertexCount = 0;
+ const int numIndices = sm.indexCount;
+ const int numTriangles = numIndices / 3;
+
+ AssertBreak((numTriangles * 3) == numIndices);
+
+ UInt32 maxComponentStride = 0;
+ const UInt32 availableChannels = mesh.GetAvailableChannels() | (mesh.GetSkin().empty() ? 0 : (1<<kShaderChannelCount));
+ for(int i=0;i<=kShaderChannelCount;i++)
+ {
+ if(availableChannels & (1<<i))
+ {
+ if(maxComponentStride < ComponentStride[i])
+ maxComponentStride = ComponentStride[i];
+ }
+ }
+
+ const UInt32 maxDMATransferSize = 16 * 1024;
+ const UInt32 numVerts = (numIndices + 15) & (~15);
+ const UInt32 maxVerts = std::min(numVerts, maxDMATransferSize / maxComponentStride);
+ const UInt32 batchSize = CalcBestFitBatchSize(availableChannels, numVerts, maxVerts);
+
+ const int maxPartitions = (numIndices + batchSize-1) / batchSize;
+ const int numVertices = (sm.indexCount + 2*maxPartitions);
+
+ const T* srcIndices = reinterpret_cast<const T*> (&mesh.GetIndexBuffer()[sm.firstByte]);
+
+ int startTriangle = 0;
+ int startVertex = 0;
+ std::vector<T> oldToNew;
+ oldToNew.resize(mesh.GetVertexCount());
+ std::vector<TempPartition<T> > & partitions = segments[submesh].m_Partitions;
+ while(startTriangle != numTriangles)
+ {
+ TempPartition<T> p;
+ p.indexBuffer.clear();
+ p.vertexCount = 0;
+ p.InitRemapping(batchSize+3);
+ dynamic_array<T>& dstIndices = p.indexBuffer;
+ memset(&oldToNew[0],(T)-1,oldToNew.size()*sizeof(T));
+ for(int i=startTriangle; i<numTriangles; i++)
+ {
+ startTriangle = numTriangles;
+ T lastVertexCount = vertexCount; // undo stack
+ for(int j=0;j<3;j++)
+ {
+ int index = i*3+j;
+ int vertex = srcIndices[index];
+ AssertBreak(vertex >= 0);
+ AssertBreak(vertex < mesh.GetVertexCount());
+ AssertBreak(lastVertexCount-startVertex+j < p.newToOld.size());
+ AssertBreak(p.newToOld[lastVertexCount-startVertex+j] == (T)-1);
+ if(oldToNew[vertex]==(T)-1)
+ {
+ AssertBreak(vertexCount < numVertices);
+ oldToNew[vertex]=vertexCount-startVertex;
+ p.newToOld[vertexCount-startVertex]=vertex;
+ vertexCount++;
+ }
+ dstIndices.push_back(oldToNew[vertex]);
+ }
+ if((vertexCount-startVertex) > batchSize)
+ {
+ //undo the last one in the partition
+ for(int j=0;j<3;j++)
+ {
+ p.newToOld[lastVertexCount-startVertex+j] = -1;;
+ dstIndices.pop_back();
+ }
+ startTriangle = i;
+ vertexCount = lastVertexCount;
+ break;
+ }
+ }
+ const int actualVertexCount = vertexCount - startVertex;
+ p.vertexCount = maxVerts;//CalcBestFitBatchSize(availableChannels, actualVertexCount, maxVerts, true); // FIXME!!! This needs to find the next "best fit" that will still keep alignment restrictions..
+ p.RemapVertices(mesh, actualVertexCount);
+ partitions.push_back(p);
+ startVertex = vertexCount;
+ }
+ oldToNew.clear();
+}
+
+// mircea: todo: this would be awesome!!!
+// spuInOut:
+// m_Vertices
+// m_Normals
+// m_Tangents
+// spuIn:
+// m_Skin
+
+// rsxDirect
+// m_UV
+// m_UV1
+// m_Colors
+// m_IndexBuffer
+
+void PartitionSubmeshes(Mesh& m)
+{
+ typedef UInt16 T;
+
+ const int submeshCount = m.m_SubMeshes.size();
+
+ m.m_PartitionInfos.clear();
+ m.m_Partitions.clear();
+
+ // skinned meshes cannot be partitioned if the optimization flag is not set because partitioning changes the vertex/index buffers
+ if (!m.GetMeshOptimized() || m.GetSkin().empty())
+ return;
+
+ // destripify if needed
+ m.DestripifyIndices ();
+
+ // need to fixup the indices first so they are not relative to the partition start anymore.
+ Mesh::MeshPartitionInfoContainer& partInfos = m.m_PartitionInfos;
+ for(int pi=0; pi<partInfos.size(); pi++)
+ {
+ const MeshPartitionInfo& partInfo = m.m_PartitionInfos[pi];
+
+ for(int s=0; s<partInfo.partitionCount; s++)
+ {
+ const MeshPartition& p = m.m_Partitions[partInfo.submeshStart + s];
+ IndexBufferData indexBufferData;
+ m.GetIndexBufferData(indexBufferData);
+ UInt16* indices = (UInt16*)(&m.m_IndexBuffer[0] + p.indexByteOffset);
+ for(int i=0;i<p.indexCount;i++)
+ indices[i] += p.vertexOffset;
+ }
+ }
+
+ // make a segment for each submesh
+ std::vector< SegmentedMesh<T> > segments;
+ segments.resize(submeshCount);
+ for(int submesh=0;submesh<submeshCount;submesh++)
+ CreateFromSubMesh<T>(segments, m, submesh);
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // combine the segments to get the script accessible buffers
+
+ UInt32 availableChannels = m.GetAvailableChannels();
+
+ m.Clear(false);
+ m.SetMeshOptimized(true); //mircea@ m.Clear will set the optimized mesh to false. Being here means we are partitioning an optimized mesh so restore the flag.
+ m.SetSubMeshCount(submeshCount);
+
+ UInt32 vertexOffset = 0;
+ UInt32 indexOffset = 0;
+
+ for(int submesh=0;submesh<submeshCount;submesh++)
+ {
+ int indexCount = 0;
+ SegmentedMesh<T>& seg = segments[submesh];
+
+ MeshPartitionInfo partInfo;
+ partInfo.submeshStart = m.m_Partitions.size();
+ partInfo.partitionCount = seg.m_Partitions.size();
+ m.m_PartitionInfos.push_back(partInfo);
+
+ // create partitions & build the mesh buffers
+ for(int s=0;s<seg.m_Partitions.size();s++)
+ {
+ MeshPartition part;
+ TempPartition<T>& p = seg.m_Partitions[s];
+ part.vertexCount = p.vertexCount;
+ part.vertexOffset = vertexOffset;
+ part.indexCount = p.indexBuffer.size();
+ part.indexByteOffset = indexOffset;
+ AssertBreak(0 == (part.vertexOffset & 15));
+ m.m_Partitions.push_back(part);;
+ indexCount += part.indexCount;
+ indexOffset += p.indexBuffer.size() * sizeof(T);
+ vertexOffset += p.vertexCount;
+ }
+ }
+
+ // fill in the partitioned data back into the mesh.
+ m.ResizeVertices(vertexOffset, availableChannels);
+
+ for(int submesh=0;submesh<submeshCount;submesh++)
+ {
+ const SegmentedMesh<T>& seg = segments[submesh];
+ const MeshPartitionInfo& partInfo = m.m_PartitionInfos[submesh];
+ for(int s=0;s<seg.m_Partitions.size();s++)
+ {
+ const TempPartition<T>& p = seg.m_Partitions[s];
+ const MeshPartition& part = m.m_Partitions[partInfo.submeshStart + s];
+ strided_copy (p.m_Vertices.begin (), p.m_Vertices.end(), m.GetVertexBegin () + part.vertexOffset);
+ if(!p.m_Normals.empty())
+ strided_copy (p.m_Normals.begin (), p.m_Normals.end(), m.GetNormalBegin () + part.vertexOffset);
+ if(!p.m_UV.empty())
+ strided_copy (p.m_UV.begin (), p.m_UV.end (), m.GetUvBegin (0) + part.vertexOffset);
+ if(!p.m_UV1.empty())
+ strided_copy (p.m_UV1.begin (), p.m_UV1.end (), m.GetUvBegin (1) + part.vertexOffset);
+ if(!p.m_Tangents.empty())
+ strided_copy (p.m_Tangents.begin (), p.m_Tangents.end (), m.GetTangentBegin () + part.vertexOffset);
+ if(!p.m_Colors.empty())
+ strided_copy (p.m_Colors.begin (), p.m_Colors.end (), m.GetColorBegin() + part.vertexOffset);
+ if(!p.m_Skin.empty())
+ m.GetSkin().insert(m.GetSkin().end(), p.m_Skin.begin(), p.m_Skin.end());
+ }
+
+ std::vector<T> indices;
+ for(int s=0;s<partInfo.partitionCount;s++)
+ {
+ const MeshPartition& p = m.m_Partitions[partInfo.submeshStart+s];
+ const TempPartition<T>& tp = seg.m_Partitions[s];
+ for(int i=0;i<p.indexCount;i++)
+ {
+ int index = tp.indexBuffer[i];
+ AssertBreak( (index>=0) && (index < (p.vertexCount)));
+ #if DEBUG_PARTITIONING
+ index += p.vertexOffset;
+ #endif
+ indices.push_back(index);
+ }
+ }
+ m.SetIndices (&indices[0], indices.size(), submesh, kPrimitiveTriangles);
+ }
+}
+
+void PartitionMesh(Mesh* m)
+{
+ PartitionSubmeshes(*m);
+}
+
+#endif //UNITY_EDITOR
diff --git a/Runtime/Filters/Mesh/MeshPartitioner.h b/Runtime/Filters/Mesh/MeshPartitioner.h
new file mode 100644
index 0000000..95a0d98
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshPartitioner.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#define DEBUG_PARTITIONING 0
+class Mesh;
+void PartitionMesh(Mesh* m);
diff --git a/Runtime/Filters/Mesh/MeshRenderer.cpp b/Runtime/Filters/Mesh/MeshRenderer.cpp
new file mode 100644
index 0000000..08dfbae
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshRenderer.cpp
@@ -0,0 +1,664 @@
+#include "UnityPrefix.h"
+#include "MeshRenderer.h"
+#include "Runtime/Graphics/Transform.h"
+#include "LodMesh.h"
+#include "Runtime/Filters/Mesh/MeshUtility.h"
+#include "Runtime/Graphics/DrawUtil.h"
+#include "Runtime/GfxDevice/BatchRendering.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/shaderlab.h"
+
+#include "Runtime/Camera/Renderqueue.h"
+#include "Runtime/Camera/RenderLoops/BuiltinShaderParamUtility.h"
+#include "Runtime/GfxDevice/BatchRendering.h"
+
+#include "Runtime/Profiler/TimeHelper.h"
+#include "Runtime/GfxDevice/GfxDeviceStats.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+
+PROFILER_INFORMATION(gMeshRenderProfile, "MeshRenderer.Render", kProfilerRender)
+PROFILER_INFORMATION(gMeshRenderScaledProfile, "MeshRenderer.ComputeScaledMesh", kProfilerRender)
+PROFILER_INFORMATION(gMeshRenderStaticBatch, "MeshRenderer.RenderStaticBatch", kProfilerRender)
+PROFILER_INFORMATION(gMeshRenderDynamicBatch, "MeshRenderer.RenderDynamicBatch", kProfilerRender)
+
+
+#if UNITY_EDITOR
+#define SET_CACHED_SURFACE_AREA_DIRTY() m_CachedSurfaceArea = -1.0f;
+#else
+#define SET_CACHED_SURFACE_AREA_DIRTY() //do nothing
+#endif
+
+IMPLEMENT_CLASS_INIT_ONLY (MeshRenderer)
+
+MeshRenderer::MeshRenderer (MemLabelId label, ObjectCreationMode mode)
+: Super(kRendererMesh, label, mode)
+, m_MeshNode (this)
+{
+ m_ScaledMeshDirty = true;
+ m_MeshWasModified = false;
+
+ m_CachedMesh = NULL;
+ m_ScaledMesh = NULL;
+ SET_CACHED_SURFACE_AREA_DIRTY();
+}
+
+MeshRenderer::~MeshRenderer ()
+{
+ FreeScaledMesh ();
+}
+
+void MeshRenderer::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ UpdateCachedMesh ();
+}
+
+void MeshRenderer::Deactivate (DeactivateOperation operation)
+{
+ Super::Deactivate (operation);
+ FreeScaledMesh ();
+}
+
+void MeshRenderer::InitializeClass ()
+{
+ REGISTER_MESSAGE (MeshRenderer, kTransformChanged, TransformChanged, int);
+
+ REGISTER_MESSAGE_VOID(MeshRenderer, kDidModifyBounds, DidModifyMeshBounds);
+ REGISTER_MESSAGE_VOID(MeshRenderer, kDidDeleteMesh, DidDeleteMesh);
+ REGISTER_MESSAGE_VOID(MeshRenderer, kDidModifyMesh, DidModifyMesh);
+}
+
+void MeshRenderer::TransformChanged (int changeMask)
+{
+ if (changeMask & Transform::kScaleChanged)
+ {
+ SET_CACHED_SURFACE_AREA_DIRTY();
+ m_ScaledMeshDirty = true;
+ }
+ Super::TransformChanged (changeMask);
+}
+
+void MeshRenderer::UpdateLocalAABB()
+{
+ DebugAssertIf( m_CachedMesh != m_Mesh );
+ if( m_CachedMesh )
+ {
+ if (HasSubsetIndices())
+ {
+ if (GetMaterialCount() == 1)
+ m_TransformInfo.localAABB = m_CachedMesh->GetBounds(GetSubsetIndex(0));
+ else
+ {
+ MinMaxAABB minMaxAABB;
+ for (int m = 0; m < GetMaterialCount(); ++m)
+ minMaxAABB.Encapsulate(m_CachedMesh->GetBounds(GetSubsetIndex(m)));
+ m_TransformInfo.localAABB = minMaxAABB;
+ }
+ }
+ else
+ {
+ m_TransformInfo.localAABB = m_CachedMesh->GetBounds();
+ }
+ }
+ else
+ m_TransformInfo.localAABB.SetCenterAndExtent( Vector3f::zero, Vector3f::zero );
+}
+
+void MeshRenderer::SetSubsetIndex(int subsetIndex, int index)
+{
+ Renderer::SetSubsetIndex(subsetIndex, index);
+
+ // Reset scaled mesh if this renderer is now statically batched.
+ // Mesh scaling should never be used with static batching (case 551504).
+ FreeScaledMesh();
+}
+
+int MeshRenderer::GetStaticBatchIndex() const
+{
+ // Wrap non-virtual version in a virtual call
+ return GetMeshStaticBatchIndex();
+}
+
+int MeshRenderer::GetMeshStaticBatchIndex() const
+{
+ return IsPartOfStaticBatch() ? m_CachedMesh->GetInstanceID(): 0;
+}
+
+UInt32 MeshRenderer::GetMeshIDSmall() const
+{
+ return m_CachedMesh ? m_CachedMesh->GetInternalMeshID(): 0;
+}
+
+
+Mesh* MeshRenderer::GetCachedMesh ()
+{
+ DebugAssertIf(m_CachedMesh != m_Mesh);
+ return m_CachedMesh;
+}
+
+
+Mesh* MeshRenderer::GetMeshUsedForRendering ()
+{
+ Mesh* cachedMesh = GetCachedMesh ();
+
+ if (cachedMesh != NULL)
+ {
+ // NOTE: staticaly batched geometry already has scale applied
+ // therefore we skip mesh scaling
+ if (!m_ScaledMeshDirty || IsPartOfStaticBatch())
+ return m_ScaledMesh == NULL ? cachedMesh : m_ScaledMesh->mesh;
+
+ m_ScaledMeshDirty = false;
+
+ float unused2;
+ Matrix4x4f unused;
+ Matrix4x4f scalematrix;
+ TransformType type = GetTransform().CalculateTransformMatrixDisableNonUniformScale (unused, scalematrix, unused2);
+ // Check if no scale is needed or we can't access vertices anyway to transform them correctly
+ DebugAssert(!IsNonUniformScaleTransform(type) || cachedMesh->HasVertexData());
+ if (!IsNonUniformScaleTransform(type) || !cachedMesh->HasVertexData())
+ {
+ // Cleanup scaled mesh
+ FreeScaledMesh();
+ m_MeshWasModified = false;
+
+ return cachedMesh;
+ }
+ // Need scaled mesh
+ else
+ {
+ // Early out if the mesh scale hasn't actually changed
+ if (m_ScaledMesh != NULL && CompareApproximately(scalematrix, m_ScaledMesh->matrix) && !m_MeshWasModified)
+ return m_ScaledMesh->mesh;
+
+ // Scale has changed, maybe generated a new scaled mesh
+ PROFILER_AUTO(gMeshRenderScaledProfile, this)
+
+ // Allocate scaled mesh
+ if (m_ScaledMesh == NULL)
+ {
+ m_ScaledMesh = new ScaledMesh ();
+ m_ScaledMesh->mesh = NEW_OBJECT (Mesh);
+ m_ScaledMesh->mesh->Reset();
+ m_ScaledMesh->mesh->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+ m_ScaledMesh->mesh->SetHideFlags(kHideAndDontSave);
+ }
+
+ m_MeshWasModified = false;
+
+ // Rescale mesh
+ m_ScaledMesh->matrix = scalematrix;
+ m_ScaledMesh->mesh->CopyTransformed(*cachedMesh, scalematrix);
+ return m_ScaledMesh->mesh;
+ }
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+static SubMesh const& GetSubMesh(Mesh& mesh, int subsetIndex)
+{
+ const int subMeshCount = mesh.GetSubMeshCount()? mesh.GetSubMeshCount()-1 : 0;
+ const int subMeshIndex = std::min<unsigned int>(subsetIndex, subMeshCount);
+ return mesh.GetSubMeshFast(subMeshIndex);
+}
+
+
+void MeshRenderer::Render (int subsetIndex, const ChannelAssigns& channels)
+{
+ PROFILER_AUTO(gMeshRenderProfile, this);
+
+ Mesh* mesh = GetMeshUsedForRendering ();
+ if (!mesh)
+ return;
+ if (m_CustomProperties)
+ GetGfxDevice().SetMaterialProperties (*m_CustomProperties);
+ DrawUtil::DrawMeshRaw (channels, *mesh, subsetIndex);
+}
+
+
+#if UNITY_EDITOR
+
+void MeshRenderer::GetRenderStats (RenderStats& renderStats)
+{
+ ///@TODO: This does not work with static batching fixor it.
+ memset(&renderStats, 0, sizeof(renderStats));
+
+ Mesh* mesh = m_Mesh;
+ if (mesh)
+ {
+ for (int i=0;i<GetMaterialCount();i++)
+ {
+ const SubMesh& submesh = GetSubMesh (*mesh, GetSubsetIndex(i));
+
+ renderStats.triangleCount += GetPrimitiveCount(submesh.indexCount, submesh.topology, false);
+ renderStats.vertexCount += submesh.vertexCount;
+ renderStats.submeshCount++;
+ }
+ }
+}
+
+float MeshRenderer::GetCachedSurfaceArea ()
+{
+ if (m_CachedSurfaceArea >= 0.0f)
+ return m_CachedSurfaceArea;
+
+ Mesh* mesh = GetCachedMesh ();
+ if (!mesh)
+ {
+ m_CachedSurfaceArea = 1.0f;
+ return m_CachedSurfaceArea;
+ }
+
+ Matrix4x4f objectToWorld;
+ GetComponent (Transform).CalculateTransformMatrix (objectToWorld);
+
+ Mesh::TemporaryIndexContainer triangles;
+ mesh->GetTriangles (triangles);
+
+ dynamic_array<Vector3f> vertices (mesh->GetVertexCount(), kMemTempAlloc);
+ mesh->ExtractVertexArray (vertices.begin ());
+
+ m_CachedSurfaceArea = CalculateSurfaceArea (objectToWorld, triangles, vertices);
+
+ return m_CachedSurfaceArea;
+}
+#endif
+
+void MeshRenderer::DidModifyMeshBounds ()
+{
+ SET_CACHED_SURFACE_AREA_DIRTY();
+ m_TransformDirty = true;
+ BoundsChanged ();
+}
+
+void MeshRenderer::DidModifyMesh ()
+{
+ m_MeshWasModified = true;
+ m_ScaledMeshDirty = true;
+ m_TransformDirty = true;
+ BoundsChanged();
+}
+
+void MeshRenderer::DidDeleteMesh ()
+{
+ m_CachedMesh = NULL;
+}
+
+void MeshRenderer::SetSharedMesh (PPtr<Mesh> mesh)
+{
+ SET_CACHED_SURFACE_AREA_DIRTY();
+ m_Mesh = mesh;
+ UpdateCachedMesh ();
+}
+
+PPtr<Mesh> MeshRenderer::GetSharedMesh ()
+{
+ return m_Mesh;
+}
+
+void MeshRenderer::UpdateCachedMesh ()
+{
+ Mesh* mesh = m_Mesh;
+ if (mesh != m_CachedMesh)
+ {
+ // In order to make sure we are not using old subset indices referring to the previous mesh
+ // we clear them here, assuming that the correct subset indices will be set subsequently.
+ // We only do this if there was a previous mesh that the new mesh is replacing, since some
+ // code paths are transferring in the values and then call this function. In that case we do
+ // not want to mess with the indices.
+ if (m_CachedMesh) ClearSubsetIndices();
+ m_ScaledMeshDirty = true;
+ m_MeshWasModified = true;
+ m_CachedMesh = mesh;
+ m_TransformDirty = true;
+ BoundsChanged();
+ m_MeshNode.RemoveFromList();
+ if (m_CachedMesh)
+ m_CachedMesh->AddObjectUser( m_MeshNode );
+ }
+}
+
+void MeshRenderer::FreeScaledMesh ()
+{
+ if (m_ScaledMesh)
+ {
+ DestroySingleObject (m_ScaledMesh->mesh);
+ delete m_ScaledMesh;
+ m_ScaledMesh = NULL;
+ m_ScaledMeshDirty = false;
+ }
+}
+
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+
+PROFILER_INFORMATION(gDrawStaticBatchProfile, "Batch.DrawStatic", kProfilerRender)
+PROFILER_INFORMATION(gDrawDynamicBatchProfile, "Batch.DrawDynamic", kProfilerRender)
+
+static bool RenderStaticBatch (Mesh& mesh, VBO& vbo,
+ BatchInstanceData const* instances, size_t count, const ChannelAssigns& channels)
+{
+ if (count <= 1)
+ return false;
+ IndexBufferData indexBuffer;
+ mesh.GetIndexBufferData (indexBuffer);
+ if (!indexBuffer.indices)
+ return false;
+
+ PROFILER_AUTO(gMeshRenderStaticBatch, &mesh)
+
+ const SubMesh& firstSubmesh = GetSubMesh (mesh, instances[0].subsetIndex);
+ GfxPrimitiveType topology = firstSubmesh.topology;
+ const Matrix4x4f& xform = instances[0].xform;
+ int xformType = instances[0].xformType;
+
+ GfxDevice& device = GetGfxDevice();
+ device.BeginStaticBatching(channels, topology);
+
+ // Concat SubMeshes
+ for (BatchInstanceData const* it = instances; it < instances + count; ++it)
+ {
+ const SubMesh& submesh = GetSubMesh (mesh, it->subsetIndex);
+ device.StaticBatchMesh(submesh.firstVertex, submesh.vertexCount, indexBuffer, submesh.firstByte, submesh.indexCount);
+
+ Assert(topology == submesh.topology);
+ Assert(xformType == it->xformType);
+ }
+
+ device.EndStaticBatching(vbo, xform, TransformType(xformType), mesh.GetChannelsInVBO());
+ GPU_TIMESTAMP();
+
+#if ENABLE_MULTITHREADED_CODE
+ // Make sure renderer is done before mesh is changed or deleted
+ UInt32 cpuFence = device.InsertCPUFence();
+ mesh.SetCurrentCPUFence(cpuFence);
+#endif
+
+ return true;
+}
+
+static bool RenderDynamicBatch (BatchInstanceData const* instances, size_t count, size_t maxVertices, size_t maxIndices, const ChannelAssigns& shaderChannels, UInt32 availableChannels, GfxPrimitiveType topology)
+{
+ if (count <= 1)
+ return false;
+
+ if (gGraphicsCaps.buggyDynamicVBOWithTangents && (shaderChannels.GetSourceMap() & (1<<kShaderChannelTangent)))
+ return false;
+
+ PROFILER_AUTO(gMeshRenderDynamicBatch, NULL)
+
+ DebugAssert (topology != -1);
+
+ GfxDevice& device = GetGfxDevice();
+ UInt32 expectedFence = device.GetNextCPUFence();
+ device.BeginDynamicBatching(shaderChannels, availableChannels, maxVertices, maxIndices, topology);
+
+ // Transform on CPU
+ int xformType = -1;
+
+
+ for (BatchInstanceData const* it = instances; it < instances + count; ++it)
+ {
+ Assert(it->renderer);
+ Assert(it->renderer->GetRendererType() == kRendererMesh);
+ MeshRenderer* meshRenderer = (MeshRenderer*)it->renderer;
+ Mesh* mesh = meshRenderer->GetMeshUsedForRendering();
+ if (!mesh)
+ continue;
+
+ SubMesh const& submesh = GetSubMesh (*mesh, it->subsetIndex);
+
+ Assert(topology == ~0UL || topology == submesh.topology);
+ Assert(xformType == -1 || xformType == it->xformType);
+ xformType = it->xformType;
+
+ VertexBufferData vbData;
+ mesh->GetVertexBufferData(vbData, availableChannels);
+ IndexBufferData ibData;
+ mesh->GetIndexBufferData(ibData);
+
+ // Make sure renderer is done before mesh is changed or deleted
+#if ENABLE_MULTITHREADED_CODE
+ mesh->SetCurrentCPUFence(expectedFence);
+#endif
+
+ device.DynamicBatchMesh(it->xform, vbData, submesh.firstVertex, submesh.vertexCount, ibData, submesh.firstByte, submesh.indexCount);
+ }
+
+ // Draw
+ Assert(xformType != -1);
+ Assert(topology != ~0UL);
+
+ // We transformed all geometry into the world (Identity) space already.
+ // However, we did not normalize the normals.
+ // In fixed function, most GfxDevices (e.g. OpenGL & D3D) will try to figure out uniform
+ // scale directly from the matrix, and hence will not scale our normals.
+ // Therefore we upgrade normalization mode to "full normalize" to make them transform properly.
+ if (xformType & kUniformScaleTransform)
+ {
+ xformType &= ~kUniformScaleTransform;
+ xformType |= kNonUniformScaleTransform;
+ }
+
+ // Caveat: we do pass identity matrix when batching
+ // currently normals handling in vprog is:
+ // xform * (normalize(normal) * unity_Scale.w);
+ // as we pass identity matrix (no scale) we need NOT apply inv_scale
+ device.SetInverseScale(1.0f);
+ device.EndDynamicBatching(TransformType(xformType));
+
+ // Insert fence after batching is complete
+ UInt32 fence = device.InsertCPUFence();
+ Assert(fence == expectedFence);
+
+ GPU_TIMESTAMP();
+
+ return true;
+}
+
+void MeshRenderer::RenderMultiple (BatchInstanceData const* instances, size_t count, const ChannelAssigns& channels)
+{
+ Assert(count > 0);
+
+ GfxDevice& device = GetGfxDevice();
+ const float invScale = device.GetBuiltinParamValues().GetInstanceVectorParam(kShaderInstanceVecScale).w;
+
+ const MaterialPropertyBlock* customProps = instances[0].renderer->GetCustomProperties();
+ if (customProps)
+ device.SetMaterialProperties (*customProps);
+
+ const UInt32 wantedChannels = channels.GetSourceMap();
+ const bool enableDynamicBatching = GetBuildSettings().enableDynamicBatching;
+
+ BatchInstanceData const* instancesEnd = instances + count;
+ for (BatchInstanceData const* iBatchBegin = instances; iBatchBegin != instancesEnd; )
+ {
+ Assert(iBatchBegin->renderer->GetRendererType() == kRendererMesh);
+ MeshRenderer* meshRenderer = (MeshRenderer*)iBatchBegin->renderer;
+ Mesh* mesh = meshRenderer->GetMeshUsedForRendering ();
+ VBO* vbo = mesh ? mesh->GetSharedVBO (wantedChannels) : NULL;
+ if (!vbo)
+ {
+ // Skip mesh
+ ++iBatchBegin;
+ continue;
+ }
+
+ const UInt32 availableChannels = mesh->GetChannelsInVBO() & wantedChannels;
+ const int staticBatchIndex = meshRenderer->GetMeshStaticBatchIndex ();
+ const int xformType = iBatchBegin->xformType;
+
+ const SubMesh& firstSubMesh = GetSubMesh(*mesh, iBatchBegin->subsetIndex);
+ const GfxPrimitiveType topology = firstSubMesh.topology;
+ size_t batchVertexCount = firstSubMesh.vertexCount;
+ size_t batchIndexCount = firstSubMesh.indexCount;
+
+ // For first strip take 1 connecting (degenerate) triangles into account
+ if (topology == kPrimitiveTriangleStripDeprecated)
+ batchIndexCount += 1;
+
+ BatchInstanceData const* iBatchEnd = iBatchBegin + 1;
+
+ // static batching
+ if (staticBatchIndex != 0)
+ {
+ Assert(topology == kPrimitiveTriangles || topology == kPrimitiveTriangleStripDeprecated);
+ const int maxIndices = GetGfxDevice().GetMaxStaticBatchIndices();
+
+ for (; iBatchEnd != instancesEnd; ++iBatchEnd)
+ {
+ if (xformType != iBatchEnd->xformType)
+ break;
+
+ Assert(iBatchEnd->renderer->GetRendererType() == kRendererMesh);
+ MeshRenderer* meshRenderer = (MeshRenderer*)iBatchEnd->renderer;
+ if (staticBatchIndex != meshRenderer->GetMeshStaticBatchIndex())
+ break;
+
+ Mesh* nextMesh = meshRenderer->GetMeshUsedForRendering ();
+ if (!nextMesh)
+ break;
+
+ const SubMesh& submesh = GetSubMesh(*nextMesh, iBatchEnd->subsetIndex);
+ if (submesh.topology != topology)
+ break;
+
+ VBO* nextVbo = nextMesh->GetSharedVBO (wantedChannels);
+ if (nextVbo != vbo) // also a NULL check since vbo is non-NULL
+ break;
+
+ UInt32 nextAvailableChannels = nextMesh->GetChannelsInVBO() & wantedChannels;
+ if (availableChannels != nextAvailableChannels)
+ break;
+
+ UInt32 requiredIndexCount = batchIndexCount + submesh.indexCount;
+ if (topology == kPrimitiveTriangleStripDeprecated)
+ requiredIndexCount += 3; // take 3 connecting (degenerate) triangles into account
+
+ if (requiredIndexCount > maxIndices)
+ break;
+
+ batchIndexCount = requiredIndexCount;
+ }
+
+ if (mesh && vbo)
+ if (RenderStaticBatch (*mesh, *vbo, iBatchBegin, iBatchEnd - iBatchBegin, channels))
+ iBatchBegin = iBatchEnd;
+ }
+ else if (vbo && enableDynamicBatching)
+ // dynamic batching
+ {
+ const int firstVertexCount = batchVertexCount;
+ const int firstIndexCount = batchIndexCount;
+
+ // after moving to fully strided meshes we were hit by the issue that we might have different channels
+ // in src and dst data, so our optimized asm routines doesn't quite work.
+ // we will move to support vertex streams (this will solve lots of issues after skinning/batching asm rewrite ;-))
+ // but for now let just play safe
+
+ if (CanUseDynamicBatching(*mesh, wantedChannels, firstVertexCount) &&
+ firstIndexCount < kDynamicBatchingIndicesThreshold &&
+ topology != kPrimitiveLineStrip)
+ {
+ for (; iBatchEnd != instancesEnd; ++iBatchEnd)
+ {
+ if (xformType != iBatchEnd->xformType)
+ break;
+
+ Assert(iBatchEnd->renderer->GetRendererType() == kRendererMesh);
+ MeshRenderer* meshRenderer = (MeshRenderer*)iBatchEnd->renderer;
+ if (meshRenderer->IsPartOfStaticBatch())
+ break;
+
+ Mesh* nextMesh = meshRenderer->GetMeshUsedForRendering ();
+ if (!nextMesh)
+ break;
+
+ const SubMesh& submesh = GetSubMesh(*nextMesh, iBatchEnd->subsetIndex);
+ if (submesh.topology != topology)
+ break;
+
+ if (!CanUseDynamicBatching(*nextMesh, wantedChannels, submesh.vertexCount))
+ break;
+
+ UInt32 requiredVertexCount = batchVertexCount + submesh.vertexCount;
+ UInt32 requiredIndexCount = batchIndexCount + submesh.indexCount;
+ if (topology == kPrimitiveTriangleStripDeprecated)
+ requiredIndexCount += 3; // take 3 connecting (degenerate) triangles into account
+
+ if (requiredVertexCount > 0xffff)
+ break;
+
+ if (requiredIndexCount > kDynamicBatchingIndicesThreshold)
+ break;
+
+ VBO* nextVbo = nextMesh->GetSharedVBO (wantedChannels);
+ if (!nextVbo)
+ break;
+
+ const UInt32 nextAvailableChannels = nextMesh->GetChannelsInVBO() & wantedChannels;
+ if (availableChannels != nextAvailableChannels)
+ break;
+
+ batchVertexCount = requiredVertexCount;
+ batchIndexCount = requiredIndexCount;
+ }
+
+ // Skip batch if batchVertexCount == 0 or batchIndexCount == 0
+ if (batchVertexCount == 0 || batchIndexCount == 0 || RenderDynamicBatch (iBatchBegin, iBatchEnd - iBatchBegin, batchVertexCount, batchIndexCount, channels, availableChannels, topology))
+ iBatchBegin = iBatchEnd;
+ }
+ }
+
+ // old-school rendering for anything left
+ for (; iBatchBegin != iBatchEnd; ++iBatchBegin)
+ {
+ BatchInstanceData const* it = iBatchBegin;
+ Assert(iBatchBegin->renderer->GetRendererType() == kRendererMesh);
+ MeshRenderer* meshRenderer = (MeshRenderer*)iBatchBegin->renderer;
+ Mesh* mesh = meshRenderer->GetMeshUsedForRendering ();
+ if (!mesh)
+ continue;
+
+ VBO* vbo = mesh->GetSharedVBO (wantedChannels);
+ if (!vbo)
+ continue;
+
+ if (customProps)
+ device.SetMaterialProperties (*customProps);
+
+ // Batched rendering above will have set inverse scale to 1.0 (since everything is transformed
+ // to identity). For remaining meshes that aren't batched, we have to setup the original scale
+ // back.
+ device.SetInverseScale(invScale);
+ SetupObjectMatrix (it->xform, it->xformType);
+ DrawUtil::DrawVBOMeshRaw (*vbo, *mesh, channels, it->subsetIndex);
+ }
+
+ Assert(iBatchBegin == iBatchEnd); // everything was rendered successfully
+ }
+}
+
+bool MeshRenderer::CanUseDynamicBatching(const Mesh& mesh, UInt32 wantedChannels, int vertexCount)
+{
+ if (mesh.GetStreamCompression() != Mesh::kStreamCompressionDefault ||
+ mesh.GetIndexBuffer().empty() ||
+ vertexCount > kDynamicBatchingVerticesThreshold ||
+ vertexCount * BitsInMask(wantedChannels) > kDynamicBatchingVertsByChannelThreshold)
+ return false;
+ return true;
+}
+
+#endif // #if GFX_ENABLE_DRAW_CALL_BATCHING
+
diff --git a/Runtime/Filters/Mesh/MeshRenderer.h b/Runtime/Filters/Mesh/MeshRenderer.h
new file mode 100644
index 0000000..d42c22e
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshRenderer.h
@@ -0,0 +1,87 @@
+#ifndef MESHRENDERER_H
+#define MESHRENDERER_H
+
+#include "Runtime/Filters/Renderer.h"
+
+class Mesh;
+
+
+
+class MeshRenderer : public Renderer {
+ public:
+ MeshRenderer (MemLabelId label, ObjectCreationMode mode);
+ // ~MeshRenderer (); declared-by-macro
+ REGISTER_DERIVED_CLASS (MeshRenderer, Renderer)
+ static void InitializeClass ();
+
+ // Tag class as sealed, this makes QueryComponent faster.
+ static bool IsSealedClass () { return true; }
+
+ static void RenderMultiple (const BatchInstanceData* instances, size_t count, const ChannelAssigns& channels);
+ virtual void Render (int materialIndex, const ChannelAssigns& channels);
+
+ virtual void UpdateLocalAABB();
+
+ virtual void SetSubsetIndex(int subsetIndex, int index);
+
+ virtual int GetStaticBatchIndex() const;
+ virtual UInt32 GetMeshIDSmall() const;
+ int GetMeshStaticBatchIndex() const;
+
+ void TransformChanged (int changeMask);
+ void AwakeFromLoad(AwakeFromLoadMode mode);
+ virtual void Deactivate (DeactivateOperation operation);
+
+ void SetSharedMesh (PPtr<Mesh> mesh);
+ PPtr<Mesh> GetSharedMesh ();
+
+ Mesh& GetInstantiatedMesh ();
+ void SetInstantiatedMesh (Mesh* mesh);
+
+ Mesh* GetMeshUsedForRendering();
+
+ void DidModifyMeshBounds ();
+ void DidModifyMeshValidity ();
+ void DidModifyMesh ();
+ void DidDeleteMesh ();
+ #if UNITY_EDITOR
+ float GetCachedSurfaceArea ();
+ virtual void GetRenderStats (RenderStats& renderStats);
+ #endif
+
+ static bool CanUseDynamicBatching(const Mesh& mesh, UInt32 wantedChannels, int vertexCount);
+
+ private:
+
+ Mesh* GetCachedMesh ();
+
+ ListNode<Object> m_MeshNode;
+ void UpdateCachedMesh ();
+
+ void FreeScaledMesh ();
+
+ Mesh* m_CachedMesh;
+ PPtr<Mesh> m_Mesh;
+
+ struct ScaledMesh
+ {
+ Matrix4x4f matrix;
+ Mesh* mesh;
+ };
+
+ ScaledMesh* m_ScaledMesh;
+
+ // as we have padding anyway, we can add more flags here
+ UInt8 m_ScaledMeshDirty;
+ // setted on responce to event to properly handle vertices changing on non-uniform scale
+ UInt8 m_MeshWasModified;
+ // for future
+ UInt16 m_Padding16;
+
+ #if UNITY_EDITOR
+ float m_CachedSurfaceArea;
+ #endif
+
+};
+
+#endif
diff --git a/Runtime/Filters/Mesh/MeshSkinning.cpp b/Runtime/Filters/Mesh/MeshSkinning.cpp
new file mode 100644
index 0000000..7d01667
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshSkinning.cpp
@@ -0,0 +1,165 @@
+#include "UnityPrefix.h"
+#include "MeshSkinning.h"
+#if UNITY_OSX
+#include <alloca.h> // this is really deprecated and should be exchanged for stdlib.h
+#else
+#include <stdlib.h>
+#endif
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Utilities/OptimizationUtility.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Utilities/Prefetch.h"
+#include "Runtime/Profiler/TimeHelper.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Misc/CPUInfo.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+
+PROFILER_INFORMATION(gMeshSkinningProfile, "MeshSkinning.Skin", kProfilerRender)
+PROFILER_INFORMATION(gMeshSkinningSlowpath, "MeshSkinning.SlowPath", kProfilerRender)
+
+#include "MeshSkinningMobile.h"
+#include "MeshSkinningSSE2.h"
+#include "SkinGeneric.h"
+#include "MeshBlendShaping.h"
+
+
+//===========================================================================================================================================
+
+
+void SkinMesh(SkinMeshInfo& info)
+{
+ const TransformInstruction NormalizeTransformInstruction =
+#if (UNITY_SUPPORTS_NEON && !UNITY_DISABLE_NEON_SKINNING) || UNITY_SUPPORTS_VFP
+ // NOTE: optimized NEON/VFP routines do not do any normalization
+ // instead we rely on GPU to do that
+ kNoNormalize;
+#else
+ //@TODO: fix that "Fast" & "Fastest" crap. Right now "Fastest" is actually a win on PC (1ms saved in Dark Unity)
+ // so I'm leaving it there for now.
+ kNormalizeFastest;
+#endif
+
+ // Instantiates the right skinning template depending on the bone per vertex count
+ #define PERMUTE_BONES(skinNormal,skinTangent) { \
+ if (info.bonesPerVertex == 1) \
+ SkinGeneric<NormalizeTransformInstruction, 1, skinNormal, skinTangent> (info); \
+ else if (info.bonesPerVertex == 2) \
+ SkinGeneric<NormalizeTransformInstruction, 2, skinNormal, skinTangent> (info); \
+ else if (info.bonesPerVertex == 4) \
+ SkinGeneric<NormalizeTransformInstruction, 4, skinNormal, skinTangent> (info); \
+ }
+
+ if (info.skinNormals && info.skinTangents)
+ PERMUTE_BONES(true, true)
+ else if (info.skinNormals)
+ PERMUTE_BONES(true, false)
+ else
+ PERMUTE_BONES(false, false)
+}
+
+
+static void ApplyMeshSkinning (SkinMeshInfo& info)
+{
+ #if UNITY_WII
+ SkinMeshWii(info);
+ #else
+
+ PROFILER_AUTO(gMeshSkinningProfile, NULL);
+
+ if (SkinMeshOptimizedMobile(info))
+ return;
+
+ if (SkinMeshOptimizedSSE2(info))
+ return;
+
+ // fallback to slow generic implementation
+ {
+ PROFILER_AUTO(gMeshSkinningSlowpath, NULL);
+ SkinMesh(info);
+ }
+ #endif
+}
+
+void DeformSkinnedMesh (SkinMeshInfo& info)
+{
+ const bool hasBlendShapes = info.blendshapeCount != 0;
+ const bool hasSkin = info.boneCount != 0;
+
+ // No actual skinning can be done. Just copy vertex stream.
+ // TODO: This code can be removed if we render the undeformed mesh in SkinnedMeshRenderer
+ // when there is no skin and no active blend shapes. See case 557165.
+ if (!hasBlendShapes && !hasSkin)
+ {
+ memcpy (info.outVertices, info.inVertices, info.inStride * info.vertexCount);
+ return;
+ }
+
+ UInt8* tmpBlendShapes = NULL;
+
+ // blend shapes
+ if (hasBlendShapes)
+ {
+ // The final destination might be write-combined memory which is insanely slow to read
+ // or randomly access, so always allocate a temp buffer for blend shapes (case 554830).
+ // Skinning can write directly to a VB since it always writes sequentially to memory.
+ size_t bufferSize = info.inStride * info.vertexCount;
+ tmpBlendShapes = ALLOC_TEMP_MANUAL(UInt8, bufferSize);
+
+ ApplyBlendShapes (info, tmpBlendShapes);
+
+ if (hasSkin)
+ info.inVertices = tmpBlendShapes;
+ else
+ memcpy(info.outVertices, tmpBlendShapes, bufferSize);
+ }
+
+ // skinning
+ if (hasSkin)
+ ApplyMeshSkinning (info);
+
+ if (tmpBlendShapes)
+ FREE_TEMP_MANUAL(tmpBlendShapes);
+}
+
+
+void* DeformSkinnedMeshJob (void* rawData)
+{
+ SkinMeshInfo* data = reinterpret_cast<SkinMeshInfo*>(rawData);
+ DeformSkinnedMesh (*data);
+ return NULL;
+}
+
+
+SkinMeshInfo::SkinMeshInfo()
+{
+ memset(this, 0, sizeof(SkinMeshInfo));
+}
+
+void SkinMeshInfo::Allocate()
+{
+ size_t size = boneCount * sizeof(Matrix4x4f) + sizeof(float) * blendshapeCount;
+ if (size == 0)
+ return;
+
+ allocatedBuffer = (UInt8*)UNITY_MALLOC_ALIGNED(kMemSkinning, size, 64);
+
+ UInt8* head = allocatedBuffer;
+ if (boneCount != 0)
+ {
+ cachedPose = reinterpret_cast<Matrix4x4f*> (head);
+ head += sizeof(Matrix4x4f) * boneCount;
+ }
+
+ if (blendshapeCount != 0)
+{
+ blendshapeWeights = reinterpret_cast<float*> (head);
+ }
+}
+
+void SkinMeshInfo::Release() const
+{
+ if (allocatedBuffer)
+ UNITY_FREE(kMemSkinning, allocatedBuffer);
+}
diff --git a/Runtime/Filters/Mesh/MeshSkinning.h b/Runtime/Filters/Mesh/MeshSkinning.h
new file mode 100644
index 0000000..b56efa9
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshSkinning.h
@@ -0,0 +1,64 @@
+#ifndef MESHSKINNING_H
+#define MESHSKINNING_H
+
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Mesh.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include <vector>
+#include <list>
+
+class GPUSkinningInfo;
+
+typedef std::vector<BoneInfluence> CompactSkin;
+struct BlendShapeData;
+
+enum TransformInstruction { kNormalizeFastest = 0, kNormalizeFast = 1, kNoNormalize = 3 };
+class VertexData;
+
+struct SkinMeshInfo
+{
+ int bonesPerVertex;
+
+ void* compactSkin;
+ int boneCount;
+
+ const void* inVertices;
+ void* outVertices;
+ int inStride;
+ int outStride;
+
+ int normalOffset;
+ int tangentOffset;
+ bool skinNormals;
+ bool skinTangents;
+
+ int vertexCount;
+
+ // This is instance data and must be double buffered so the render thread can work in paralell.
+ UInt8* allocatedBuffer;
+ Matrix4x4f* cachedPose;
+ float* blendshapeWeights;
+
+ int blendshapeCount;
+ const BlendShapeData* blendshapes;
+
+ bool memExport; // Is set up for memexport (Xbox) or streamout (DX11)
+
+#if UNITY_PS3
+ const VertexData* vertexData;
+#endif
+
+ GPUSkinningInfo *mei;
+
+ SkinMeshInfo();
+
+ void Allocate();
+ void Release () const;
+};
+
+void DeformSkinnedMesh (SkinMeshInfo& info);
+void* DeformSkinnedMeshJob (void* rawData);
+
+#endif
diff --git a/Runtime/Filters/Mesh/MeshSkinningGenericSIMD.h b/Runtime/Filters/Mesh/MeshSkinningGenericSIMD.h
new file mode 100644
index 0000000..0b17b42
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshSkinningGenericSIMD.h
@@ -0,0 +1,212 @@
+#if 0
+
+/*
+ mircea@INFO: this doesn't do normalization.
+ */
+
+#include "Runtime/Math/Simd/Matrix4x4Simd.h"
+
+template<TransformInstruction transformInstruction, int bonesPerVertexCount,
+bool skinNormal, bool skinTangent, bool copy8BytesAt24Offset>
+void SkinGenericSimd (SkinMeshInfo& info)
+{
+ DebugAssertIf( copy8BytesAt24Offset && (!info.skinNormals || info.normalOffset != 12) );
+ const int* influence1 = reinterpret_cast<const int*> (info.compactSkin);
+ const BoneInfluence2* influence2 = reinterpret_cast<const BoneInfluence2*> (info.compactSkin);
+ const BoneInfluence* influence4 = reinterpret_cast<const BoneInfluence*> (info.compactSkin);
+
+ const Matrix4x4f* bones4x4 = info.cachedPose;
+
+ const int inStride = info.inStride;
+ int outStride = info.outStride;
+ int count = info.vertexCount;
+
+ const int normalOffset = (copy8BytesAt24Offset ? 12 : info.normalOffset) >> 2;
+ const int tangentOffset = info.tangentOffset >> 2;
+
+ const UInt8* inputVertex = (const UInt8*)info.inVertices;
+ UInt8* outputVertex = (UInt8*)info.outVertices;
+
+ Simd128 pose0, pose1, pose2, pose3;
+
+ for( int v = 0; v < count; v++ )
+ {
+ ALIGN_LOOP_OPTIMIZATION
+
+ // Blend the matrices first, then transform everything with this
+ // blended matrix. Gives a small speed boost on XCode/Intel (11.3 to 12.00 FPS
+ // in skin4 bench), and a good boost on MSVC/Windows (9.6 to 12.4 FPS).
+ if (bonesPerVertexCount == 1)
+ {
+ const float* maddr = bones4x4[*influence1].m_Data;
+
+ Prefetch(maddr);
+
+ pose0 = V4LoadUnaligned( maddr, 0x0 );
+ pose1 = V4LoadUnaligned( maddr, 0x4 );
+ pose2 = V4LoadUnaligned( maddr, 0x8 );
+ pose3 = V4LoadUnaligned( maddr, 0xC );
+ }
+ else if (bonesPerVertexCount == 2)
+ {
+ Prefetch(influence2);
+
+ Simd128 weights = {influence2->weight[0], influence2->weight[1], 0, 0};
+
+ const float* maddr0 = bones4x4[influence2->boneIndex[0]].m_Data;
+ const float* maddr1 = bones4x4[influence2->boneIndex[1]].m_Data;
+
+ Prefetch(maddr0);
+ Prefetch(maddr1);
+
+ Simd128 weight0 = V4Splat(weights, 0);
+ Simd128 weight1 = V4Splat(weights, 1);
+
+ Simd128 mat00 = V4LoadUnaligned( maddr0, 0x0 );
+ Simd128 mat01 = V4LoadUnaligned( maddr0, 0x4 );
+ Simd128 mat02 = V4LoadUnaligned( maddr0, 0x8 );
+ Simd128 mat03 = V4LoadUnaligned( maddr0, 0xC );
+
+ Simd128 mat10 = V4LoadUnaligned( maddr1, 0x0 );
+ Simd128 mat11 = V4LoadUnaligned( maddr1, 0x4 );
+ Simd128 mat12 = V4LoadUnaligned( maddr1, 0x8 );
+ Simd128 mat13 = V4LoadUnaligned( maddr1, 0xC );
+
+ pose0 = V4Mul(mat00, weight0);
+ pose1 = V4Mul(mat01, weight0);
+ pose2 = V4Mul(mat02, weight0);
+ pose3 = V4Mul(mat03, weight0);
+
+ pose0 = V4MulAdd(mat10, weight1, pose0);
+ pose1 = V4MulAdd(mat11, weight1, pose1);
+ pose2 = V4MulAdd(mat12, weight1, pose2);
+ pose3 = V4MulAdd(mat13, weight1, pose3);
+ }
+ else if (bonesPerVertexCount == 4)
+ {
+ Prefetch(influence4);
+
+ Simd128 weights = {influence4->weight[0], influence4->weight[1], influence4->weight[2], influence4->weight[3]};
+
+ const float* maddr0 = bones4x4[influence4->boneIndex[0]].m_Data;
+ const float* maddr1 = bones4x4[influence4->boneIndex[1]].m_Data;
+ const float* maddr2 = bones4x4[influence4->boneIndex[2]].m_Data;
+ const float* maddr3 = bones4x4[influence4->boneIndex[3]].m_Data;
+
+ Prefetch(maddr0);
+ Prefetch(maddr1);
+ Prefetch(maddr2);
+ Prefetch(maddr3);
+
+ Simd128 weight0 = V4Splat(weights, 0);
+ Simd128 weight1 = V4Splat(weights, 1);
+ Simd128 weight2 = V4Splat(weights, 2);
+ Simd128 weight3 = V4Splat(weights, 3);
+
+ Simd128 mat00 = V4LoadUnaligned( maddr0, 0x0 );
+ Simd128 mat01 = V4LoadUnaligned( maddr0, 0x4 );
+ Simd128 mat02 = V4LoadUnaligned( maddr0, 0x8 );
+ Simd128 mat03 = V4LoadUnaligned( maddr0, 0xC );
+
+ Simd128 mat10 = V4LoadUnaligned( maddr1, 0x0 );
+ Simd128 mat11 = V4LoadUnaligned( maddr1, 0x4 );
+ Simd128 mat12 = V4LoadUnaligned( maddr1, 0x8 );
+ Simd128 mat13 = V4LoadUnaligned( maddr1, 0xC );
+
+ Simd128 mat20 = V4LoadUnaligned( maddr2, 0x0 );
+ Simd128 mat21 = V4LoadUnaligned( maddr2, 0x4 );
+ Simd128 mat22 = V4LoadUnaligned( maddr2, 0x8 );
+ Simd128 mat23 = V4LoadUnaligned( maddr2, 0xC );
+
+ Simd128 mat30 = V4LoadUnaligned( maddr3, 0x0 );
+ Simd128 mat31 = V4LoadUnaligned( maddr3, 0x4 );
+ Simd128 mat32 = V4LoadUnaligned( maddr3, 0x8 );
+ Simd128 mat33 = V4LoadUnaligned( maddr3, 0xC );
+
+ pose0 = V4Mul(mat00, weight0);
+ pose1 = V4Mul(mat01, weight0);
+ pose2 = V4Mul(mat02, weight0);
+ pose3 = V4Mul(mat03, weight0);
+
+ pose0 = V4MulAdd(mat10, weight1, pose0);
+ pose1 = V4MulAdd(mat11, weight1, pose1);
+ pose2 = V4MulAdd(mat12, weight1, pose2);
+ pose3 = V4MulAdd(mat13, weight1, pose3);
+
+ pose0 = V4MulAdd(mat20, weight2, pose0);
+ pose1 = V4MulAdd(mat21, weight2, pose1);
+ pose2 = V4MulAdd(mat22, weight2, pose2);
+ pose3 = V4MulAdd(mat23, weight2, pose3);
+
+ pose0 = V4MulAdd(mat30, weight3, pose0);
+ pose1 = V4MulAdd(mat31, weight3, pose1);
+ pose2 = V4MulAdd(mat32, weight3, pose2);
+ pose3 = V4MulAdd(mat33, weight3, pose3);
+ }
+
+ Prefetch(inputVertex);
+
+ Simd128 vpos = V4LoadUnaligned((const float*)inputVertex, 0);
+ TransformPoint3NATIVE(pose0, pose1, pose2, pose3, vpos, vpos);
+
+ Simd128 vnor, vtan, ndot, tdot;
+
+ // remember... this is a template and skinNormal & skinTangent are consts
+ if(skinNormal || skinTangent)
+ {
+ Simd128 vlen;
+ if( skinNormal )
+ {
+ vnor = V4LoadUnaligned((const float*)inputVertex, normalOffset);
+ TransformVector3NATIVE(pose0, pose1, pose2, pose3, vnor, vnor);
+ ndot = V3Dot(vnor, vnor);
+ }
+ else
+ {
+ ndot = V4Zero();
+ }
+
+ if( skinTangent )
+ {
+ vtan = V4LoadUnaligned((const float*)inputVertex, tangentOffset);
+ TransformVector3NATIVE(pose0, pose1, pose2, pose3, vtan, vtan);
+ tdot = V3Dot(vtan, vtan);
+ }
+ else
+ {
+ tdot = V4Zero();
+ }
+
+ vlen = V4MergeH(ndot, tdot);
+ vlen = V4Rsqrt(vlen);
+
+ if(skinNormal) {
+ vnor = V4Mul(vnor, V4Splat(vlen, 0));
+ V3StoreUnaligned(vnor, (float*)outputVertex, normalOffset);
+ }
+
+ if(skinTangent) {
+ vtan = V4Mul(vtan, V4Splat(vlen, 1));
+ V3StoreUnaligned(vtan, (float*)outputVertex, tangentOffset);
+ }
+ }
+
+ V3StoreUnaligned(vpos, (float*)outputVertex, 0);
+
+ if( skinTangent )
+ {
+ *reinterpret_cast<float*>( outputVertex + (tangentOffset<<2) + sizeof(Vector3f) ) = *reinterpret_cast<const float*>( inputVertex + (tangentOffset<<2) + sizeof(Vector3f) );
+ }
+
+ outputVertex += outStride;
+ inputVertex += inStride;
+
+ if (bonesPerVertexCount == 1)
+ influence1++;
+ else if (bonesPerVertexCount == 2)
+ influence2++;
+ if (bonesPerVertexCount == 4)
+ influence4++;
+ }
+}
+#endif
diff --git a/Runtime/Filters/Mesh/MeshSkinningMobile.h b/Runtime/Filters/Mesh/MeshSkinningMobile.h
new file mode 100644
index 0000000..f6efc54
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshSkinningMobile.h
@@ -0,0 +1,160 @@
+#if UNITY_SUPPORTS_VFP
+
+#if UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+#define s_SkinVertices_VFP _s_SkinVertices_VFP
+#define s_SkinVertices_NoNormals_VFP _s_SkinVertices_NoNormals_VFP
+#define s_SkinVertices_Tangents_VFP _s_SkinVertices_Tangents_VFP
+
+#define s_SkinVertices2Bones_VFP _s_SkinVertices2Bones_VFP
+#define s_SkinVertices2Bones_NoNormals_VFP _s_SkinVertices2Bones_NoNormals_VFP
+#define s_SkinVertices2Bones_Tangents_VFP _s_SkinVertices2Bones_Tangents_VFP
+
+#define s_SkinVertices4Bones_VFP _s_SkinVertices4Bones_VFP
+#define s_SkinVertices4Bones_NoNormals_VFP _s_SkinVertices4Bones_NoNormals_VFP
+#define s_SkinVertices4Bones_Tangents_VFP _s_SkinVertices4Bones_Tangents_VFP
+#endif // UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+
+extern "C"
+{
+ void s_SkinVertices_VFP(const Matrix4x4f* bones4x4, const void* srcVertData, const void* srcVertDataEnd, const void* srcBoneInfluence1, void* dstVertData);
+ void s_SkinVertices_NoNormals_VFP(const Matrix4x4f* bones4x4, const void* srcVertData, const void* srcVertDataEnd, const void* srcBoneInfluence1, void* dstVertData);
+ void s_SkinVertices_Tangents_VFP(const Matrix4x4f* bones4x4, const void* srcVertData, const void* srcVertDataEnd, const void* srcBoneInfluence1, void* dstVertData);
+
+ void s_SkinVertices2Bones_VFP(const Matrix4x4f* bones4x4, const void* srcVertData, const void* srcVertDataEnd, const void* srcBoneInfluence2, void* dstVertData);
+ void s_SkinVertices2Bones_NoNormals_VFP(const Matrix4x4f* bones4x4, const void* srcVertData, const void* srcVertDataEnd, const void* srcBoneInfluence2, void* dstVertData);
+ void s_SkinVertices2Bones_Tangents_VFP(const Matrix4x4f* bones4x4, const void* srcVertData, const void* srcVertDataEnd, const void* srcBoneInfluence2, void* dstVertData);
+
+ void s_SkinVertices4Bones_VFP(const Matrix4x4f* bones4x4, const void* srcVertData, const void* srcVertDataEnd, const void* srcBoneInfluence4, void* dstVertData);
+ void s_SkinVertices4Bones_NoNormals_VFP(const Matrix4x4f* bones4x4, const void* srcVertData, const void* srcVertDataEnd, const void* srcBoneInfluence4, void* dstVertData);
+ void s_SkinVertices4Bones_Tangents_VFP(const Matrix4x4f* bones4x4, const void* srcVertData, const void* srcVertDataEnd, const void* srcBoneInfluence4, void* dstVertData);
+}
+#endif
+
+#if (UNITY_SUPPORTS_NEON && !UNITY_DISABLE_NEON_SKINNING)
+
+#if UNITY_ANDROID || UNITY_WINRT || UNITY_BB10 || UNITY_TIZEN
+#define s_SkinVertices_NEON _s_SkinVertices_NEON
+#define s_SkinVertices_NoNormals_NEON _s_SkinVertices_NoNormals_NEON
+#define s_SkinVertices_Tangents_NEON _s_SkinVertices_Tangents_NEON
+
+#define s_SkinVertices2Bones_NEON _s_SkinVertices2Bones_NEON
+#define s_SkinVertices2Bones_NoNormals_NEON _s_SkinVertices2Bones_NoNormals_NEON
+#define s_SkinVertices2Bones_Tangents_NEON _s_SkinVertices2Bones_Tangents_NEON
+
+#define s_SkinVertices4Bones_NEON _s_SkinVertices4Bones_NEON
+#define s_SkinVertices4Bones_NoNormals_NEON _s_SkinVertices4Bones_NoNormals_NEON
+#define s_SkinVertices4Bones_Tangents_NEON _s_SkinVertices4Bones_Tangents_NEON
+
+#endif // UNITY_ANDROID || UNITY_WINRT || UNITY_BB10 || UNITY_TIZEN
+
+extern "C"
+{
+ void s_SkinVertices_NEON(const Matrix4x4f* bones4x4, const void* srcVertData, const void* srcVertDataEnd, const int* srcBoneInfluence1, void* dstVertData);
+ void s_SkinVertices_NoNormals_NEON(const Matrix4x4f* bones4x4, const void* srcVertData, const void* srcVertDataEnd, const int* srcBoneInfluence1, void* dstVertData);
+ void s_SkinVertices_Tangents_NEON(const Matrix4x4f* bones4x4, const void* srcVertData, const void* srcVertDataEnd, const int* srcBoneInfluence1, void* dstVertData);
+
+ void s_SkinVertices2Bones_NEON(const Matrix4x4f* bones4x4, const void* srcVertData, const void* srcVertDataEnd, const BoneInfluence2* srcBoneInfluence2, void* dstVertData);
+ void s_SkinVertices2Bones_NoNormals_NEON(const Matrix4x4f* bones4x4, const void* srcVertData, const void* srcVertDataEnd, const BoneInfluence2* srcBoneInfluence2, void* dstVertData);
+ void s_SkinVertices2Bones_Tangents_NEON(const Matrix4x4f* bones4x4, const void* srcVertData, const void* srcVertDataEnd, const BoneInfluence2* srcBoneInfluence2, void* dstVertData);
+
+ void s_SkinVertices4Bones_NEON(const Matrix4x4f* bones4x4, const void* srcVertData, const void* srcVertDataEnd, const BoneInfluence* srcBoneInfluences, void* dstVertData);
+ void s_SkinVertices4Bones_NoNormals_NEON(const Matrix4x4f* bones4x4, const void* srcVertData, const void* srcVertDataEnd, const BoneInfluence* srcBoneInfluences, void* dstVertData);
+ void s_SkinVertices4Bones_Tangents_NEON(const Matrix4x4f* bones4x4, const void* srcVertData, const void* srcVertDataEnd, const BoneInfluence* srcBoneInfluences, void* dstVertData);
+}
+#endif
+
+#if UNITY_SUPPORTS_VFP || (UNITY_SUPPORTS_NEON && !UNITY_DISABLE_NEON_SKINNING)
+
+bool SkinMeshOptimizedMobile(SkinMeshInfo& info)
+{
+ static const size_t kPrefetchSizeBones = 4096;
+ static const size_t kPrefetchSizeVertex = 512;
+
+ const int bonesPerVertexCount = info.bonesPerVertex;
+ const bool skinNormal = info.skinNormals;
+ const bool skinTangent = info.skinTangents;
+
+ const int* influence1 = reinterpret_cast<const int*> (info.compactSkin);
+ const BoneInfluence2* influence2 = reinterpret_cast<const BoneInfluence2*> (info.compactSkin);
+ const BoneInfluence* influence4 = reinterpret_cast<const BoneInfluence*> (info.compactSkin);
+
+ const Matrix4x4f* bones4x4 = info.cachedPose;
+
+ const int inStride = info.inStride;
+ int count = info.vertexCount;
+
+ const UInt8* inputVertex = (const UInt8*)info.inVertices;
+ UInt8* outputVertex = (UInt8*)info.outVertices;
+
+ if (skinTangent && !skinNormal)
+ return false;
+
+ if( !UNITY_SUPPORTS_VFP && !CPUInfo::HasNEONSupport() )
+ {
+ ErrorString("non-NEON path not enabled!");
+ return false;
+ }
+
+#if !ENABLE_MULTITHREADED_SKINNING
+ PROFILER_AUTO_THREAD_SAFE(gMeshSkinningOptimized, NULL);
+#endif
+
+ Prefetch(bones4x4, std::min<size_t>(info.boneCount * sizeof(Matrix4x4f), kPrefetchSizeBones));
+ Prefetch(inputVertex + inStride, std::min<size_t>(inStride * (count-1), kPrefetchSizeVertex));
+
+#if UNITY_SUPPORTS_NEON && UNITY_SUPPORTS_VFP
+#define CALL_SKIN_FUNC( name, influence ) \
+do \
+{ \
+if (CPUInfo::HasNEONSupport()) \
+ name##_NEON(bones4x4, inputVertex, (UInt8*)inputVertex + (inStride * count), influence, outputVertex); \
+else \
+ name##_VFP(bones4x4, inputVertex, (UInt8*)inputVertex + (inStride * count), influence, outputVertex); \
+} \
+while(0)
+#endif
+#if UNITY_SUPPORTS_NEON && !UNITY_SUPPORTS_VFP
+#define CALL_SKIN_FUNC( name, influence ) name##_NEON(bones4x4, inputVertex, (UInt8*)inputVertex + (inStride * count), influence, outputVertex)
+#endif
+#if UNITY_SUPPORTS_VFP && !UNITY_SUPPORTS_NEON
+#define CALL_SKIN_FUNC( name, influence ) name##_VFP(bones4x4, inputVertex, (UInt8*)inputVertex + (inStride * count), influence, outputVertex)
+#endif
+
+ if (bonesPerVertexCount == 1 )
+ {
+ if (skinNormal && skinTangent)
+ CALL_SKIN_FUNC(s_SkinVertices_Tangents, influence1);
+ else if( skinNormal )
+ CALL_SKIN_FUNC(s_SkinVertices, influence1);
+ else
+ CALL_SKIN_FUNC(s_SkinVertices_NoNormals, influence1);
+ }
+ else if (bonesPerVertexCount == 2)
+ {
+ if (skinNormal && skinTangent)
+ CALL_SKIN_FUNC(s_SkinVertices2Bones_Tangents, influence2);
+ else if( skinNormal )
+ CALL_SKIN_FUNC(s_SkinVertices2Bones, influence2);
+ else
+ CALL_SKIN_FUNC(s_SkinVertices2Bones_NoNormals, influence2);
+ }
+ else if (bonesPerVertexCount == 4)
+ {
+ if (skinNormal && skinTangent)
+ CALL_SKIN_FUNC(s_SkinVertices4Bones_Tangents, influence4);
+ else if (skinNormal)
+ CALL_SKIN_FUNC(s_SkinVertices4Bones, influence4);
+ else
+ CALL_SKIN_FUNC(s_SkinVertices4Bones_NoNormals, influence4);
+ }
+
+ return true;
+}
+#else
+bool SkinMeshOptimizedMobile(SkinMeshInfo& info)
+{
+ return false;
+}
+#endif // UNITY_SUPPORTS_VFP || UNITY_SUPPORTS_NEON
+
+
diff --git a/Runtime/Filters/Mesh/MeshSkinningNEON.asm b/Runtime/Filters/Mesh/MeshSkinningNEON.asm
new file mode 100644
index 0000000..494b397
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshSkinningNEON.asm
@@ -0,0 +1,527 @@
+ AREA .text, CODE
+
+ EXPORT _s_SkinVertices_NEON
+ EXPORT _s_SkinVertices_NoNormals_NEON
+ EXPORT _s_SkinVertices_Tangents_NEON
+ EXPORT _s_SkinVertices2Bones_NEON
+ EXPORT _s_SkinVertices2Bones_NoNormals_NEON
+ EXPORT _s_SkinVertices2Bones_Tangents_NEON
+ EXPORT _s_SkinVertices4Bones_NEON
+ EXPORT _s_SkinVertices4Bones_NoNormals_NEON
+ EXPORT _s_SkinVertices4Bones_Tangents_NEON
+
+|_s_SkinVertices_NEON| PROC
+ mov ip, sp
+ vpush {d8-d10}
+ stmdb sp!, {r4, r5, r6, r7, r8}
+ ldr.w r4, [ip]
+ mov.w r8, #12
+ ldr.w r5, [r3], #4
+ add.w r7, r0, r5, lsl #6
+
+|_s_SkinVertices_NEON_loop|
+ vld1.32 {d24-d27}, [r7@128]!
+ vld1.32 {d28-d31}, [r7@128]
+ vld1.32 {d6-d8}, [r1@64]!
+ vmul.f32 q0, q12, d6[0]
+ vmul.f32 q1, q12, d7[1]
+ cmp r1, r2
+ pld [r1, #256] ; 0x100
+ vmla.f32 q0, q13, d6[1]
+ vmla.f32 q1, q13, d8[0]
+ it cc
+ ldrcc.w r5, [r3], #4
+ add.w r7, r0, r5, lsl #6
+ vmla.f32 q0, q14, d7[0]
+ vmla.f32 q1, q14, d8[1]
+ pld [r7]
+ vadd.f32 q0, q0, q15
+ vst1.32 {d0-d1}, [r4], r8
+ vst1.32 {d2-d3}, [r4], r8
+ bcc.w |_s_SkinVertices_NEON_loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8}
+ vpop {d8-d10}
+ bx lr
+ ENDP
+
+
+|_s_SkinVertices_NoNormals_NEON| PROC
+ mov ip, sp
+ vpush {d8-d10}
+ stmdb sp!, {r4, r5, r6, r7, r8}
+ ldr.w r4, [ip]
+ mov.w r8, #12
+ ldr.w r5, [r3], #4
+ add.w r7, r0, r5, lsl #6
+
+|_s_SkinVertices_NoNormals_NEON_loop|
+ vld1.32 {d24-d27}, [r7@128]!
+ vld1.32 {d28-d31}, [r7@128]
+ vld1.32 {d6-d7}, [r1], r8
+ vmul.f32 q0, q12, d6[0]
+ cmp r1, r2
+ pld [r1, #256] ; 0x100
+ vmla.f32 q0, q13, d6[1]
+ it cc
+ ldrcc.w r5, [r3], #4
+ add.w r7, r0, r5, lsl #6
+ vmla.f32 q0, q14, d7[0]
+ pld [r7]
+ vadd.f32 q0, q0, q15
+ vst1.32 {d0-d1}, [r4], r8
+ bcc.w |_s_SkinVertices_NoNormals_NEON_loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8}
+ vpop {d8-d10}
+ bx lr
+ ENDP
+
+
+|_s_SkinVertices_Tangents_NEON| PROC
+ mov ip, sp
+ vpush {d8-d10}
+ stmdb sp!, {r4, r5, r6, r7, r8}
+ ldr.w r4, [ip]
+ mov.w r8, #12
+ ldr.w r5, [r3], #4
+ add.w r7, r0, r5, lsl #6
+
+|_s_SkinVertices_Tangents_NEON_loop|
+ vld1.32 {d24-d27}, [r7@128]!
+ vld1.32 {d28-d31}, [r7@128]
+ vld1.32 {d6-d8}, [r1@64]!
+ vld1.32 {d9-d10}, [r1@64]!
+ vmul.f32 q0, q12, d6[0]
+ vmul.f32 q1, q12, d7[1]
+ vmul.f32 q2, q12, d9[0]
+ cmp r1, r2
+ pld [r1, #256] ; 0x100
+ vmla.f32 q0, q13, d6[1]
+ vmla.f32 q1, q13, d8[0]
+ vmla.f32 q2, q13, d9[1]
+ it cc
+ ldrcc.w r5, [r3], #4
+ add.w r7, r0, r5, lsl #6
+ vmla.f32 q0, q14, d7[0]
+ vmla.f32 q1, q14, d8[1]
+ vmla.f32 q2, q14, d10[0]
+ pld [r7]
+ vadd.f32 q0, q0, q15
+ vmov.f32 s11, s21
+ vst1.32 {d0-d1}, [r4], r8
+ vst1.32 {d2-d3}, [r4], r8
+ vst1.32 {d4-d5}, [r4]!
+ bcc.w |_s_SkinVertices_Tangents_NEON_loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8}
+ vpop {d8-d10}
+ bx lr
+ ENDP
+
+
+|_s_SkinVertices2Bones_NEON| PROC
+ mov ip, sp
+ vpush {d8-d11}
+ stmdb sp!, {r4, r5, r6, r7, r8, sl}
+ ldr.w r4, [ip]
+ vld1.32 {d11}, [r3]!
+ ldmia r3!, {r5, r6}
+ add.w r7, r0, r5, lsl #6
+ vld1.32 {d16-d19}, [r7@128]!
+ vmul.f32 q12, q8, d11[0]
+ vmul.f32 q13, q9, d11[0]
+ vld1.32 {d20-d23}, [r7@128]
+ add.w r7, r0, r6, lsl #6
+ vmul.f32 q14, q10, d11[0]
+ vmul.f32 q15, q11, d11[0]
+ vld1.32 {d16-d19}, [r7@128]!
+ vmla.f32 q12, q8, d11[1]
+ vmla.f32 q13, q9, d11[1]
+ ldr r5, [r3, #8]
+ mov.w r8, #12
+ sub.w sl, r2, #24
+ vld1.32 {d20-d23}, [r7@128]
+ vmla.f32 q14, q10, d11[1]
+ nop
+
+|_s_SkinVertices2Bones_NEON_loop|
+ cmp r1, sl
+ add.w r7, r0, r5, lsl #6
+ it cc
+ ldrcc r6, [r3, #12]
+ vld1.32 {d6-d8}, [r1@64]!
+ vmla.f32 q15, q11, d11[1]
+ vmul.f32 q0, q12, d6[0]
+ vld1.32 {d16-d19}, [r7@128]!
+ cmp r1, sl
+ vmul.f32 q1, q12, d7[1]
+ vld1.32 {d11}, [r3]
+ vmul.f32 q12, q8, d11[0]
+ pld [r1, #256] ; 0x100
+ vmla.f32 q0, q13, d6[1]
+ vld1.32 {d20-d23}, [r7@128]
+ add.w r7, r0, r6, lsl #6
+ vmla.f32 q1, q13, d8[0]
+ it cc
+ ldrcc r5, [r3, #24]
+ vmul.f32 q13, q9, d11[0]
+ vmla.f32 q0, q14, d7[0]
+ cmp r1, r2
+ vmla.f32 q1, q14, d8[1]
+ vld1.32 {d16-d19}, [r7@128]!
+ vmul.f32 q14, q10, d11[0]
+ vadd.f32 q0, q0, q15
+ vmul.f32 q15, q11, d11[0]
+ vld1.32 {d20-d23}, [r7@128]
+ vmla.f32 q12, q8, d11[1]
+ vst1.32 {d0-d1}, [r4], r8
+ vmla.f32 q13, q9, d11[1]
+ vst1.32 {d2-d3}, [r4], r8
+ add.w r3, r3, #16
+ vmla.f32 q14, q10, d11[1]
+ bcc.w |_s_SkinVertices2Bones_NEON_loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, sl}
+ vpop {d8-d11}
+ bx lr
+ ENDP
+
+
+|_s_SkinVertices2Bones_NoNormals_NEON| PROC
+ mov ip, sp
+ vpush {d8-d11}
+ stmdb sp!, {r4, r5, r6, r7, r8, sl}
+ ldr.w r4, [ip]
+ vld1.32 {d11}, [r3]!
+ ldmia r3!, {r5, r6}
+ add.w r7, r0, r5, lsl #6
+ vld1.32 {d16-d19}, [r7@128]!
+ vmul.f32 q12, q8, d11[0]
+ vmul.f32 q13, q9, d11[0]
+ vld1.32 {d20-d23}, [r7@128]
+ add.w r7, r0, r6, lsl #6
+ vmul.f32 q14, q10, d11[0]
+ vmul.f32 q15, q11, d11[0]
+ vld1.32 {d16-d19}, [r7@128]!
+ vmla.f32 q12, q8, d11[1]
+ vmla.f32 q13, q9, d11[1]
+ ldr r5, [r3, #8]
+ mov.w r8, #12
+ sub.w sl, r2, #12
+ vld1.32 {d20-d23}, [r7@128]
+ vmla.f32 q14, q10, d11[1]
+ nop
+ nop.w
+
+|_s_SkinVertices2Bones_NoNormals_NEON_loop|
+ cmp r1, sl
+ add.w r7, r0, r5, lsl #6
+ it cc
+ ldrcc r6, [r3, #12]
+ vld1.32 {d6-d7}, [r1], r8
+ vmla.f32 q15, q11, d11[1]
+ vmul.f32 q0, q12, d6[0]
+ vld1.32 {d16-d19}, [r7@128]!
+ cmp r1, sl
+ vld1.32 {d11}, [r3]
+ vmul.f32 q12, q8, d11[0]
+ pld [r1, #256] ; 0x100
+ vmla.f32 q0, q13, d6[1]
+ vld1.32 {d20-d23}, [r7@128]
+ add.w r7, r0, r6, lsl #6
+ it cc
+ ldrcc r5, [r3, #24]
+ vmul.f32 q13, q9, d11[0]
+ vmla.f32 q0, q14, d7[0]
+ cmp r1, r2
+ vld1.32 {d16-d19}, [r7@128]!
+ vmul.f32 q14, q10, d11[0]
+ vadd.f32 q0, q0, q15
+ vmul.f32 q15, q11, d11[0]
+ vld1.32 {d20-d23}, [r7@128]
+ vmla.f32 q12, q8, d11[1]
+ vst1.32 {d0-d1}, [r4], r8
+ vmla.f32 q13, q9, d11[1]
+ add.w r3, r3, #16
+ vmla.f32 q14, q10, d11[1]
+ bcc.w |_s_SkinVertices2Bones_NoNormals_NEON_loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, sl}
+ vpop {d8-d11}
+ bx lr
+ ENDP
+
+
+|_s_SkinVertices2Bones_Tangents_NEON| PROC
+ mov ip, sp
+ vpush {d8-d11}
+ stmdb sp!, {r4, r5, r6, r7, r8, sl}
+ ldr.w r4, [ip]
+ vld1.32 {d11}, [r3]!
+ ldmia r3!, {r5, r6}
+ add.w r7, r0, r5, lsl #6
+ vld1.32 {d16-d19}, [r7@128]!
+ vmul.f32 q12, q8, d11[0]
+ vmul.f32 q13, q9, d11[0]
+ vld1.32 {d20-d23}, [r7@128]
+ add.w r7, r0, r6, lsl #6
+ vmul.f32 q14, q10, d11[0]
+ vmul.f32 q15, q11, d11[0]
+ vld1.32 {d16-d19}, [r7@128]!
+ vmla.f32 q12, q8, d11[1]
+ vmla.f32 q13, q9, d11[1]
+ ldr r5, [r3, #8]
+ mov.w r8, #12
+ sub.w sl, r2, #40 ; 0x28
+ vld1.32 {d20-d23}, [r7@128]
+ vmla.f32 q14, q10, d11[1]
+ nop
+ nop.w
+
+|_s_SkinVertices2Bones_Tangents_NEON_loop|
+ cmp r1, sl
+ add.w r7, r0, r5, lsl #6
+ it cc
+ ldrcc r6, [r3, #12]
+ vld1.32 {d6-d8}, [r1@64]!
+ vmla.f32 q15, q11, d11[1]
+ vld1.32 {d9-d10}, [r1@64]!
+ vmul.f32 q0, q12, d6[0]
+ vld1.32 {d16-d19}, [r7@128]!
+ cmp r1, sl
+ vmul.f32 q1, q12, d7[1]
+ vmul.f32 q2, q12, d9[0]
+ vld1.32 {d11}, [r3]
+ vmul.f32 q12, q8, d11[0]
+ pld [r1, #256] ; 0x100
+ vmla.f32 q0, q13, d6[1]
+ vld1.32 {d20-d23}, [r7@128]
+ add.w r7, r0, r6, lsl #6
+ vmla.f32 q1, q13, d8[0]
+ vmla.f32 q2, q13, d9[1]
+ it cc
+ ldrcc r5, [r3, #24]
+ vmul.f32 q13, q9, d11[0]
+ vmla.f32 q0, q14, d7[0]
+ cmp r1, r2
+ vmla.f32 q1, q14, d8[1]
+ vmla.f32 q2, q14, d10[0]
+ vld1.32 {d16-d19}, [r7@128]!
+ vmul.f32 q14, q10, d11[0]
+ vadd.f32 q0, q0, q15
+ vmov.f32 s11, s21
+ vmul.f32 q15, q11, d11[0]
+ vld1.32 {d20-d23}, [r7@128]
+ vmla.f32 q12, q8, d11[1]
+ vst1.32 {d0-d1}, [r4], r8
+ vmla.f32 q13, q9, d11[1]
+ vst1.32 {d2-d3}, [r4], r8
+ add.w r3, r3, #16
+ vmla.f32 q14, q10, d11[1]
+ vst1.32 {d4-d5}, [r4]!
+ bcc.w |_s_SkinVertices2Bones_Tangents_NEON_loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, sl}
+ vpop {d8-d11}
+ bx lr
+ ENDP
+
+
+|_s_SkinVertices4Bones_NEON| PROC
+ mov ip, sp
+ vpush {d8-d12}
+ stmdb sp!, {r4, r5, r6, r7, r8}
+ ldr.w r4, [ip]
+ vld1.32 {d11-d12}, [r3]!
+ ldmia r3!, {r5, r6}
+ add.w r7, r0, r5, lsl #6
+ vld1.32 {d16-d19}, [r7@128]!
+ vld1.32 {d20-d23}, [r7@128]
+ mov.w r8, #12
+ nop.w
+ nop.w
+ nop.w
+
+|_s_SkinVertices4Bones_NEON_loop|
+ vmul.f32 q12, q8, d11[0]
+ vld1.32 {d6-d8}, [r1@64]!
+ vmul.f32 q13, q9, d11[0]
+ add.w r7, r0, r6, lsl #6
+ vmul.f32 q14, q10, d11[0]
+ vld1.32 {d16-d19}, [r7@128]!
+ vmul.f32 q15, q11, d11[0]
+ vld1.32 {d20-d23}, [r7@128]
+ vmla.f32 q12, q8, d11[1]
+ ldmia r3!, {r5, r6}
+ vmla.f32 q13, q9, d11[1]
+ add.w r7, r0, r5, lsl #6
+ cmp r1, r2
+ vmla.f32 q14, q10, d11[1]
+ vld1.32 {d16-d19}, [r7@128]!
+ vmla.f32 q15, q11, d11[1]
+ pld [r3, #256] ; 0x100
+ vld1.32 {d20-d23}, [r7@128]
+ vmla.f32 q12, q8, d12[0]
+ add.w r7, r0, r6, lsl #6
+ vmla.f32 q13, q9, d12[0]
+ vmla.f32 q14, q10, d12[0]
+ vld1.32 {d16-d19}, [r7@128]!
+ vmla.f32 q15, q11, d12[0]
+ vld1.32 {d20-d23}, [r7@128]
+ vmla.f32 q12, q8, d12[1]
+ vmla.f32 q13, q9, d12[1]
+ vmla.f32 q14, q10, d12[1]
+ vmla.f32 q15, q11, d12[1]
+ pld [r1, #256] ; 0x100
+ vmul.f32 q0, q12, d6[0]
+ vld1.32 {d11-d12}, [r3]!
+ vmul.f32 q1, q12, d7[1]
+ it cc
+ ldmiacc r3!, {r5, r6}
+ vmla.f32 q0, q13, d6[1]
+ add.w r7, r0, r5, lsl #6
+ vmla.f32 q1, q13, d8[0]
+ vldmia r7, {d16-d23}
+ vmla.f32 q0, q14, d7[0]
+ vmla.f32 q1, q14, d8[1]
+ vadd.f32 q0, q0, q15
+ vst1.32 {d0-d1}, [r4], r8
+ vst1.32 {d2-d3}, [r4], r8
+ bcc.w |_s_SkinVertices4Bones_NEON_loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8}
+ vpop {d8-d12}
+ bx lr
+ ENDP
+
+
+|_s_SkinVertices4Bones_NoNormals_NEON| PROC
+ mov ip, sp
+ vpush {d8-d12}
+ stmdb sp!, {r4, r5, r6, r7, r8}
+ ldr.w r4, [ip]
+ vld1.32 {d11-d12}, [r3]!
+ ldmia r3!, {r5, r6}
+ add.w r7, r0, r5, lsl #6
+ vld1.32 {d16-d19}, [r7@128]!
+ vld1.32 {d20-d23}, [r7@128]
+ mov.w r8, #12
+ nop
+ nop.w
+
+|_s_SkinVertices4Bones_NoNormals_NEON_loop|
+ vmul.f32 q12, q8, d11[0]
+ vld1.32 {d6-d7}, [r1], r8
+ vmul.f32 q13, q9, d11[0]
+ add.w r7, r0, r6, lsl #6
+ vmul.f32 q14, q10, d11[0]
+ vld1.32 {d16-d19}, [r7@128]!
+ vmul.f32 q15, q11, d11[0]
+ vld1.32 {d20-d23}, [r7@128]
+ vmla.f32 q12, q8, d11[1]
+ ldmia r3!, {r5, r6}
+ vmla.f32 q13, q9, d11[1]
+ add.w r7, r0, r5, lsl #6
+ cmp r1, r2
+ vmla.f32 q14, q10, d11[1]
+ vld1.32 {d16-d19}, [r7@128]!
+ vmla.f32 q15, q11, d11[1]
+ pld [r3, #256] ; 0x100
+ vld1.32 {d20-d23}, [r7@128]
+ vmla.f32 q12, q8, d12[0]
+ add.w r7, r0, r6, lsl #6
+ vmla.f32 q13, q9, d12[0]
+ vmla.f32 q14, q10, d12[0]
+ vld1.32 {d16-d19}, [r7@128]!
+ vmla.f32 q15, q11, d12[0]
+ vld1.32 {d20-d23}, [r7@128]
+ vmla.f32 q12, q8, d12[1]
+ vmla.f32 q13, q9, d12[1]
+ vmla.f32 q14, q10, d12[1]
+ vmla.f32 q15, q11, d12[1]
+ pld [r1, #256] ; 0x100
+ vmul.f32 q0, q12, d6[0]
+ vld1.32 {d11-d12}, [r3]!
+ it cc
+ ldmiacc r3!, {r5, r6}
+ vmla.f32 q0, q13, d6[1]
+ add.w r7, r0, r5, lsl #6
+ vldmia r7, {d16-d23}
+ vmla.f32 q0, q14, d7[0]
+ vadd.f32 q0, q0, q15
+ vst1.32 {d0-d1}, [r4], r8
+ bcc.w |_s_SkinVertices4Bones_NoNormals_NEON_loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8}
+ vpop {d8-d12}
+ bx lr
+ ENDP
+
+
+|_s_SkinVertices4Bones_Tangents_NEON| PROC
+ mov ip, sp
+ vpush {d8-d12}
+ stmdb sp!, {r4, r5, r6, r7, r8}
+ ldr.w r4, [ip]
+ vld1.32 {d11-d12}, [r3]!
+ ldmia r3!, {r5, r6}
+ add.w r7, r0, r5, lsl #6
+ vld1.32 {d16-d19}, [r7@128]!
+ vld1.32 {d20-d23}, [r7@128]
+ mov.w r8, #12
+ nop
+ nop.w
+
+|_s_SkinVertices4Bones_Tangents_NEON_loop|
+ vmul.f32 q12, q8, d11[0]
+ vld1.32 {d6-d8}, [r1@64]!
+ vmul.f32 q13, q9, d11[0]
+ vld1.32 {d9-d10}, [r1@64]!
+ add.w r7, r0, r6, lsl #6
+ vmul.f32 q14, q10, d11[0]
+ vld1.32 {d16-d19}, [r7@128]!
+ vmul.f32 q15, q11, d11[0]
+ vld1.32 {d20-d23}, [r7@128]
+ vmla.f32 q12, q8, d11[1]
+ ldmia r3!, {r5, r6}
+ vmla.f32 q13, q9, d11[1]
+ add.w r7, r0, r5, lsl #6
+ cmp r1, r2
+ vmla.f32 q14, q10, d11[1]
+ vld1.32 {d16-d19}, [r7@128]!
+ vmla.f32 q15, q11, d11[1]
+ pld [r3, #256] ; 0x100
+ vld1.32 {d20-d23}, [r7@128]
+ vmla.f32 q12, q8, d12[0]
+ add.w r7, r0, r6, lsl #6
+ vmla.f32 q13, q9, d12[0]
+ vmla.f32 q14, q10, d12[0]
+ vld1.32 {d16-d19}, [r7@128]!
+ vmla.f32 q15, q11, d12[0]
+ vld1.32 {d20-d23}, [r7@128]
+ vmla.f32 q12, q8, d12[1]
+ vmla.f32 q13, q9, d12[1]
+ vmla.f32 q14, q10, d12[1]
+ vmla.f32 q15, q11, d12[1]
+ pld [r1, #256] ; 0x100
+ vmul.f32 q0, q12, d6[0]
+ vld1.32 {d11-d12}, [r3]!
+ vmul.f32 q1, q12, d7[1]
+ vmul.f32 q2, q12, d9[0]
+ it cc
+ ldmiacc r3!, {r5, r6}
+ vmla.f32 q0, q13, d6[1]
+ add.w r7, r0, r5, lsl #6
+ vmla.f32 q1, q13, d8[0]
+ vmla.f32 q2, q13, d9[1]
+ vldmia r7, {d16-d23}
+ vmla.f32 q0, q14, d7[0]
+ vmla.f32 q1, q14, d8[1]
+ vmla.f32 q2, q14, d10[0]
+ vadd.f32 q0, q0, q15
+ vmov.f32 s11, s21
+ vst1.32 {d0-d1}, [r4], r8
+ vst1.32 {d2-d3}, [r4], r8
+ vst1.32 {d4-d5}, [r4]!
+ bcc.w |_s_SkinVertices4Bones_Tangents_NEON_loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8}
+ vpop {d8-d12}
+ bx lr
+ nop
+ ENDP
+
+
+ END
diff --git a/Runtime/Filters/Mesh/MeshSkinningNEON.s b/Runtime/Filters/Mesh/MeshSkinningNEON.s
new file mode 100644
index 0000000..e94542d
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshSkinningNEON.s
@@ -0,0 +1,183 @@
+#define UNITY_ASSEMBLER
+#include "Configuration/PrefixConfigure.h"
+
+#if (UNITY_SUPPORTS_NEON && !UNITY_DISABLE_NEON_SKINNING)
+
+.set device,0
+.set device,__arm__
+
+.if device
+
+//.code32
+
+.globl _s_SkinVertices_NEON
+.globl _s_SkinVertices_NoNormals_NEON
+.globl _s_SkinVertices_Tangents_NEON
+
+.globl _s_SkinVertices2Bones_NEON
+.globl _s_SkinVertices2Bones_NoNormals_NEON
+.globl _s_SkinVertices2Bones_Tangents_NEON
+
+.globl _s_SkinVertices4Bones_NEON
+.globl _s_SkinVertices4Bones_NoNormals_NEON
+.globl _s_SkinVertices4Bones_Tangents_NEON
+
+#if UNITY_ANDROID
+.hidden _s_SkinVertices_NEON
+.hidden _s_SkinVertices_NoNormals_NEON
+.hidden _s_SkinVertices_Tangents_NEON
+
+.hidden _s_SkinVertices2Bones_NEON
+.hidden _s_SkinVertices2Bones_NoNormals_NEON
+.hidden _s_SkinVertices2Bones_Tangents_NEON
+
+.hidden _s_SkinVertices4Bones_NEON
+.hidden _s_SkinVertices4Bones_NoNormals_NEON
+.hidden _s_SkinVertices4Bones_Tangents_NEON
+#endif
+
+
+//===========================================================================================================================================
+
+#define SKIN_POS 1
+#define SKIN_POS_NRM 2
+#define SKIN_POS_NRM_TAN 3
+
+
+#define SKIN_2BONES 0
+#define SKIN_4BONES 0
+
+_s_SkinVertices_NEON:
+
+#define SKIN_1BONE SKIN_POS_NRM
+#define VERTEX_SZ 24
+#define LOOP_NAME _s_SkinVertices_NEON_loop
+
+#include "MeshSkinningNeon_Loop.h"
+
+#undef LOOP_NAME
+#undef VERTEX_SZ
+#undef SKIN_1BONE
+
+_s_SkinVertices_NoNormals_NEON:
+
+#define SKIN_1BONE SKIN_POS
+#define VERTEX_SZ 12
+#define LOOP_NAME _s_SkinVertices_NoNormals_NEON_loop
+
+#include "MeshSkinningNeon_Loop.h"
+
+#undef LOOP_NAME
+#undef VERTEX_SZ
+#undef SKIN_1BONE
+
+_s_SkinVertices_Tangents_NEON:
+
+#define SKIN_1BONE SKIN_POS_NRM_TAN
+#define VERTEX_SZ 40
+#define LOOP_NAME _s_SkinVertices_Tangents_NEON_loop
+
+#include "MeshSkinningNeon_Loop.h"
+
+#undef LOOP_NAME
+#undef VERTEX_SZ
+#undef SKIN_1BONE
+
+#undef SKIN_4BONES
+#undef SKIN_2BONES
+
+//===========================================================================================================================================
+
+#define SKIN_1BONE 0
+#define SKIN_4BONES 0
+
+_s_SkinVertices2Bones_NEON:
+
+#define SKIN_2BONES SKIN_POS_NRM
+#define VERTEX_SZ 24
+#define LOOP_NAME _s_SkinVertices2Bones_NEON_loop
+
+#include "MeshSkinningNeon_Loop.h"
+
+#undef LOOP_NAME
+#undef VERTEX_SZ
+#undef SKIN_2BONES
+
+_s_SkinVertices2Bones_NoNormals_NEON:
+
+#define SKIN_2BONES SKIN_POS
+#define VERTEX_SZ 12
+#define LOOP_NAME _s_SkinVertices2Bones_NoNormals_NEON_loop
+
+#include "MeshSkinningNeon_Loop.h"
+
+#undef LOOP_NAME
+#undef VERTEX_SZ
+#undef SKIN_2BONES
+
+_s_SkinVertices2Bones_Tangents_NEON:
+
+#define SKIN_2BONES SKIN_POS_NRM_TAN
+#define VERTEX_SZ 40
+#define LOOP_NAME _s_SkinVertices2Bones_Tangents_NEON_loop
+
+#include "MeshSkinningNeon_Loop.h"
+
+#undef LOOP_NAME
+#undef VERTEX_SZ
+#undef SKIN_2BONES
+
+#undef SKIN_4BONES
+#undef SKIN_1BONE
+
+
+//===========================================================================================================================================
+
+#define SKIN_1BONE 0
+#define SKIN_2BONES 0
+
+_s_SkinVertices4Bones_NEON:
+
+#define SKIN_4BONES SKIN_POS_NRM
+#define VERTEX_SZ 24
+#define LOOP_NAME _s_SkinVertices4Bones_NEON_loop
+
+#include "MeshSkinningNeon_Loop.h"
+
+#undef LOOP_NAME
+#undef VERTEX_SZ
+#undef SKIN_4BONES
+
+_s_SkinVertices4Bones_NoNormals_NEON:
+
+#define SKIN_4BONES SKIN_POS
+#define VERTEX_SZ 12
+#define LOOP_NAME _s_SkinVertices4Bones_NoNormals_NEON_loop
+
+#include "MeshSkinningNeon_Loop.h"
+
+#undef LOOP_NAME
+#undef VERTEX_SZ
+#undef SKIN_4BONES
+
+_s_SkinVertices4Bones_Tangents_NEON:
+
+#define SKIN_4BONES SKIN_POS_NRM_TAN
+#define VERTEX_SZ 40
+#define LOOP_NAME _s_SkinVertices4Bones_Tangents_NEON_loop
+
+#include "MeshSkinningNeon_Loop.h"
+
+#undef LOOP_NAME
+#undef VERTEX_SZ
+#undef SKIN_4BONES
+
+
+#undef SKIN_2BONES
+#undef SKIN_1BONE
+
+//===========================================================================================================================================
+
+.endif
+
+#endif
diff --git a/Runtime/Filters/Mesh/MeshSkinningNeon_Loop.h b/Runtime/Filters/Mesh/MeshSkinningNeon_Loop.h
new file mode 100644
index 0000000..8e584da
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshSkinningNeon_Loop.h
@@ -0,0 +1,487 @@
+
+// defines
+// SKIN_1BONE
+// SKIN_2BONES
+// SKIN_4BONES
+// LOOP_NAME
+// VERTEX_SZ
+
+// skin types
+// SKIN_POS
+// SKIN_POS_NRM
+// SKIN_POS_NRM_TAN
+
+
+
+//r0: const void* bones4x4
+//r1: const void* srcVertData
+//r2: const void* srcVertDataEnd
+//r3: const BoneInfluence4* srcBoneInfluence4
+//[sp+0] -> r4: const void* dstVertData
+
+// r5, r6: index
+// r7: matrix address
+// r8: 12 (offset for vector3)
+
+// q0 <- output: pos
+// q1 <- output: nrm
+// q2 <- output: tan
+// q3 <- input: pos
+// q4 <- input: nrm
+// q5 <- input: tan
+// d11,d12 <- weights
+// q12-q15 (blended matrix)
+// q8-q11 (cur matrix)
+
+
+// input:
+// d6[0], d6[1], d7[0] <- pos
+// d7[1], d8[0], d8[1] <- nrm
+// d9[0], d9[1], d10[0], d10[1] <- tan
+// q3 <- pos.x, pos.y, pos.z, nrm.x
+// q4 <- nrm.y, nrm.z, tan.x, tan.y
+// q5 <- tan.z, tan.w, w0, w1
+
+
+//===========================================================================================================================================
+//
+// Common
+
+#define CALC_POS_1 vmul.f32 q0, q12, d6[0]
+#define CALC_POS_2 vmla.f32 q0, q13, d6[1]
+#define CALC_POS_3 vmla.f32 q0, q14, d7[0]
+#define CALC_POS_4 vadd.f32 q0, q15
+
+#define STORE_POS vst1.32 {d0, d1}, [r4], r8
+
+#if (SKIN_1BONE == SKIN_POS_NRM) || (SKIN_1BONE == SKIN_POS_NRM_TAN) \
+ || (SKIN_2BONES == SKIN_POS_NRM) || (SKIN_2BONES == SKIN_POS_NRM_TAN) \
+ || (SKIN_4BONES == SKIN_POS_NRM) || (SKIN_4BONES == SKIN_POS_NRM_TAN)
+
+ #define LOAD_POS_NRM vld1.32 {d6, d7, d8}, [r1, :64]!
+ #define STORE_NRM vst1.32 {d2, d3}, [r4], r8
+ #define CALC_NRM_1 vmul.f32 q1, q12, d7[1]
+ #define CALC_NRM_2 vmla.f32 q1, q13, d8[0]
+ #define CALC_NRM_3 vmla.f32 q1, q14, d8[1]
+#else
+ #define LOAD_POS_NRM vld1.32 {d6, d7}, [r1], r8
+ #define STORE_NRM
+ #define CALC_NRM_1
+ #define CALC_NRM_2
+ #define CALC_NRM_3
+#endif
+
+#if (SKIN_1BONE == SKIN_POS_NRM_TAN) || (SKIN_2BONES == SKIN_POS_NRM_TAN) || (SKIN_4BONES == SKIN_POS_NRM_TAN)
+ #define LOAD_TAN vld1.32 {d9, d10}, [r1, :64]!
+ #define STORE_TAN vst1.32 {d4, d5}, [r4]!
+ #define CALC_TAN_1 vmul.f32 q2, q12, d9[0]
+ #define CALC_TAN_2 vmla.f32 q2, q13, d9[1]
+ #define CALC_TAN_3 vmla.f32 q2, q14, d10[0]
+ #define CALC_TAN_4 vmov.f32 s11, s21
+#else
+ #define LOAD_TAN
+ #define STORE_TAN
+ #define CALC_TAN_1
+ #define CALC_TAN_2
+ #define CALC_TAN_3
+ #define CALC_TAN_4
+#endif
+
+// right after vertex-data will be copy-data stream, so be careful to not overwrite anything
+#if (SKIN_1BONE == SKIN_POS) || (SKIN_2BONES == SKIN_POS) || (SKIN_4BONES == SKIN_POS)
+#define STORE_POS_LAST1 vst1.32 {d0}, [r4]!
+#define STORE_POS_LAST2 vst1.32 {d1[0]}, [r4]!
+#else
+#define STORE_POS_LAST1 STORE_POS
+#define STORE_POS_LAST2
+#endif
+
+#if (SKIN_1BONE == SKIN_POS_NRM) || (SKIN_2BONES == SKIN_POS_NRM) || (SKIN_4BONES == SKIN_POS_NRM)
+#define STORE_NRM_LAST1 vst1.32 {d2}, [r4]!
+#define STORE_NRM_LAST2 vst1.32 {d3[0]}, [r4]!
+#else
+#define STORE_NRM_LAST1 STORE_NRM
+#define STORE_NRM_LAST2
+#endif
+
+#define __NAME_EPILOGUE(x) x ## EPILOGUE
+#define _NAME_EPILOGUE(x) __NAME_EPILOGUE(x)
+#define LOOP_EPILOGUE _NAME_EPILOGUE(LOOP_NAME)
+
+
+
+#if (SKIN_1BONE == SKIN_POS) || (SKIN_1BONE == SKIN_POS_NRM) || (SKIN_1BONE == SKIN_POS_NRM_TAN)
+ #define LOAD_M_12 vld1.32 {q12,q13}, [r7,:128]!
+ #define LOAD_M_34 vld1.32 {q14,q15}, [r7,:128]
+#else
+ #define LOAD_M_12 vld1.32 {q8,q9}, [r7,:128]!
+ #define LOAD_M_34 vld1.32 {q10,q11}, [r7,:128]
+#endif
+
+#define WEIGHT_MATRIX_1(op,r) op.f32 q12, q8, r
+#define WEIGHT_MATRIX_2(op,r) op.f32 q13, q9, r
+#define WEIGHT_MATRIX_3(op,r) op.f32 q14, q10, r
+#define WEIGHT_MATRIX_4(op,r) op.f32 q15, q11, r
+
+#define WEIGHT_M0_1 WEIGHT_MATRIX_1(vmul, d11[0])
+#define WEIGHT_M0_2 WEIGHT_MATRIX_2(vmul, d11[0])
+#define WEIGHT_M0_3 WEIGHT_MATRIX_3(vmul, d11[0])
+#define WEIGHT_M0_4 WEIGHT_MATRIX_4(vmul, d11[0])
+
+#define WEIGHT_M1_1 WEIGHT_MATRIX_1(vmla, d11[1])
+#define WEIGHT_M1_2 WEIGHT_MATRIX_2(vmla, d11[1])
+#define WEIGHT_M1_3 WEIGHT_MATRIX_3(vmla, d11[1])
+#define WEIGHT_M1_4 WEIGHT_MATRIX_4(vmla, d11[1])
+
+#define WEIGHT_M2_1 WEIGHT_MATRIX_1(vmla, d12[0])
+#define WEIGHT_M2_2 WEIGHT_MATRIX_2(vmla, d12[0])
+#define WEIGHT_M2_3 WEIGHT_MATRIX_3(vmla, d12[0])
+#define WEIGHT_M2_4 WEIGHT_MATRIX_4(vmla, d12[0])
+
+#define WEIGHT_M3_1 WEIGHT_MATRIX_1(vmla, d12[1])
+#define WEIGHT_M3_2 WEIGHT_MATRIX_2(vmla, d12[1])
+#define WEIGHT_M3_3 WEIGHT_MATRIX_3(vmla, d12[1])
+#define WEIGHT_M3_4 WEIGHT_MATRIX_4(vmla, d12[1])
+
+
+//===========================================================================================================================================
+//
+// 1 bone skinning
+
+#if (SKIN_1BONE == SKIN_POS) || (SKIN_1BONE == SKIN_POS_NRM) || (SKIN_1BONE == SKIN_POS_NRM_TAN)
+
+mov ip, sp
+
+vpush {d8-d10}
+stmfd sp!, {r4-r8}
+
+ldr r4, [ip, #0]
+mov r8, #12
+
+ ldr r5, [r3], #4
+ add r7, r0, r5, lsl #6
+
+LOOP_NAME:
+
+
+
+LOAD_M_12
+LOAD_M_34
+
+
+LOAD_POS_NRM
+LOAD_TAN
+
+CALC_POS_1
+CALC_NRM_1
+CALC_TAN_1
+
+ cmp r1, r2
+ pld [r1, #256]
+
+CALC_POS_2
+CALC_NRM_2
+CALC_TAN_2
+
+ ldrcc r5, [r3], #4
+ add r7, r0, r5, lsl #6
+
+CALC_POS_3
+CALC_NRM_3
+CALC_TAN_3
+
+ pld [r7]
+
+CALC_POS_4
+CALC_TAN_4
+
+beq LOOP_EPILOGUE
+
+STORE_POS
+STORE_NRM
+STORE_TAN
+
+bcc LOOP_NAME
+
+LOOP_EPILOGUE:
+STORE_POS_LAST1
+STORE_POS_LAST2
+STORE_NRM_LAST1
+STORE_NRM_LAST2
+STORE_TAN
+
+
+ldmfd sp!, {r4-r8}
+vpop {d8-d10}
+
+bx lr
+
+
+//===========================================================================================================================================
+//
+// 2 bones skinning
+
+#elif (SKIN_2BONES == SKIN_POS || SKIN_2BONES == SKIN_POS_NRM || SKIN_2BONES == SKIN_POS_NRM_TAN)
+
+mov ip, sp
+
+vpush {d8-d11}
+stmfd sp!, {r4,r5,r6,r7,r8,r10}
+
+ldr r4, [ip, #0]
+
+vld1.32 {d11}, [r3,:64]! // wgt ->
+ldmia r3!, {r5-r6} // idx ->
+
+add r7, r0, r5, lsl #6 // M0 ..
+LOAD_M_12 // M0
+WEIGHT_M0_1
+WEIGHT_M0_2
+
+LOAD_M_34 // M0
+add r7, r0, r6, lsl #6 // M1 ..
+WEIGHT_M0_3
+WEIGHT_M0_4
+
+LOAD_M_12 // M1
+WEIGHT_M1_1
+WEIGHT_M1_2
+
+ldr r5, [r3, #8] // idx0
+
+mov r8, #12
+sub r10, r2, #VERTEX_SZ
+
+LOAD_M_34 // M1
+
+WEIGHT_M1_3
+
+.align 4
+LOOP_NAME:
+
+ cmp r1, r10
+
+ add r7, r0, r5, lsl #6 // M0 ..
+ ldrcc r6, [r3, #12] // idx1
+LOAD_POS_NRM
+
+WEIGHT_M1_4
+
+LOAD_TAN
+
+CALC_POS_1
+LOAD_M_12 // M0
+ cmp r1, r10
+CALC_NRM_1
+CALC_TAN_1
+vld1.32 {d11}, [r3,:64] // wgt ->
+
+WEIGHT_M0_1
+ pld [r1,#256]
+
+CALC_POS_2
+LOAD_M_34 // M0
+ add r7, r0, r6, lsl #6 // M1 ..
+CALC_NRM_2
+CALC_TAN_2
+ ldrcc r5, [r3, #24] // idx0
+WEIGHT_M0_2
+CALC_POS_3
+
+ cmp r1, r2
+CALC_NRM_3
+CALC_TAN_3
+LOAD_M_12 // M1
+
+
+WEIGHT_M0_3
+
+CALC_POS_4
+CALC_TAN_4
+
+WEIGHT_M0_4
+LOAD_M_34 // M1
+
+beq LOOP_EPILOGUE
+
+WEIGHT_M1_1
+STORE_POS
+
+WEIGHT_M1_2
+STORE_NRM
+ add r3, r3, #16
+WEIGHT_M1_3
+STORE_TAN
+
+bcc LOOP_NAME
+
+LOOP_EPILOGUE:
+STORE_POS_LAST1
+STORE_POS_LAST2
+STORE_NRM_LAST1
+STORE_NRM_LAST2
+STORE_TAN
+
+
+ldmfd sp!, {r4,r5,r6,r7,r8,r10}
+vpop {d8-d11}
+bx lr
+
+
+//===========================================================================================================================================
+//
+// 4 bones skinning
+
+#elif (SKIN_4BONES == SKIN_POS || SKIN_4BONES == SKIN_POS_NRM || SKIN_4BONES == SKIN_POS_NRM_TAN)
+
+
+mov ip, sp
+
+vpush {d8-d12}
+stmfd sp!, {r4-r8}
+
+ldr r4, [ip, #0]
+
+vld1.32 {d11,d12}, [r3,:128]! // wgt ->
+ldmia r3!, {r5-r6} // idx' ->
+
+add r7, r0, r5, lsl #6 // M0 ..
+LOAD_M_12 // M0
+LOAD_M_34 // M0
+
+mov r8, #12
+
+.align 4
+LOOP_NAME:
+
+WEIGHT_M0_1
+LOAD_POS_NRM
+
+WEIGHT_M0_2
+LOAD_TAN
+ add r7, r0, r6, lsl #6 // M1 ..
+
+
+WEIGHT_M0_3
+LOAD_M_12 // M1
+
+WEIGHT_M0_4
+LOAD_M_34 // M1
+
+WEIGHT_M1_1
+ ldmia r3!, {r5-r6} // idx'' ->
+
+WEIGHT_M1_2
+ add r7, r0, r5, lsl #6 // M2 ..
+ cmp r1, r2
+
+WEIGHT_M1_3
+LOAD_M_12 // M2
+
+WEIGHT_M1_4
+ pld [r3, #256]
+LOAD_M_34 // M2
+
+WEIGHT_M2_1
+ add r7, r0, r6, lsl #6 // M3 ..
+WEIGHT_M2_2
+WEIGHT_M2_3
+LOAD_M_12 // M3
+WEIGHT_M2_4
+
+LOAD_M_34 // M3
+WEIGHT_M3_1
+WEIGHT_M3_2
+WEIGHT_M3_3
+WEIGHT_M3_4
+ pld [r1, #256]
+
+CALC_POS_1
+vld1.32 {d11,d12}, [r3,:128]! // wgt ->
+
+CALC_NRM_1
+CALC_TAN_1
+ ldmcc r3!, {r5-r6} // idx ->
+
+CALC_POS_2
+ add r7, r0, r5, lsl #6 // M0 ..
+CALC_NRM_2
+CALC_TAN_2
+vldmia r7, {q8-q11} // M0 ->
+
+CALC_POS_3
+CALC_NRM_3
+CALC_TAN_3
+
+CALC_POS_4
+CALC_TAN_4
+
+beq LOOP_EPILOGUE
+
+STORE_POS
+STORE_NRM
+STORE_TAN
+
+bcc LOOP_NAME
+
+LOOP_EPILOGUE:
+STORE_POS_LAST1
+STORE_POS_LAST2
+STORE_NRM_LAST1
+STORE_NRM_LAST2
+STORE_TAN
+
+
+ldmfd sp!, {r4-r8}
+vpop {d8-d12}
+bx lr
+
+
+//===========================================================================================================================================
+
+#endif
+
+#undef __NAME_EPILOGUE
+#undef _NAME_EPILOGUE
+#undef LOOP_EPILOGUE
+#undef CALC_POS_1
+#undef CALC_POS_2
+#undef CALC_POS_3
+#undef STORE_POS
+#undef STORE_POS_LAST1
+#undef STORE_POS_LAST2
+#undef LOAD_POS_NRM
+#undef STORE_NRM
+#undef STORE_NRM_LAST1
+#undef STORE_NRM_LAST2
+#undef CALC_NRM_1
+#undef CALC_NRM_2
+#undef CALC_NRM_3
+#undef LOAD_TAN
+#undef STORE_TAN
+#undef CALC_TAN_1
+#undef CALC_TAN_2
+#undef CALC_TAN_3
+#undef CALC_TAN_4
+#undef LOAD_M_12
+#undef LOAD_M_34
+#undef WEIGHT_MATRIX_1
+#undef WEIGHT_MATRIX_2
+#undef WEIGHT_MATRIX_3
+#undef WEIGHT_MATRIX_4
+#undef WEIGHT_M0_1
+#undef WEIGHT_M0_2
+#undef WEIGHT_M0_3
+#undef WEIGHT_M0_4
+#undef WEIGHT_M1_1
+#undef WEIGHT_M1_2
+#undef WEIGHT_M1_3
+#undef WEIGHT_M1_4
+#undef WEIGHT_M2_1
+#undef WEIGHT_M2_2
+#undef WEIGHT_M2_3
+#undef WEIGHT_M2_4
+#undef WEIGHT_M3_1
+#undef WEIGHT_M3_2
+#undef WEIGHT_M3_3
+#undef WEIGHT_M3_4
diff --git a/Runtime/Filters/Mesh/MeshSkinningSSE2.asm b/Runtime/Filters/Mesh/MeshSkinningSSE2.asm
new file mode 100644
index 0000000..395bf16
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshSkinningSSE2.asm
@@ -0,0 +1,323 @@
+;; SkinSSE2.s
+;;
+;; Created by Kaspar Daugaard on 1/12/11.
+;; Copyright 2011 Unity Technologies. All rights reserved.
+
+bits 32
+
+section .text align=32
+
+%define normalOffset 12
+%define tangentOffset 24
+
+%macro SkinSSE2_Generic 3
+ ; %1 numBones
+ ; %2 hasNormals
+ ; %3 hasTangents
+ ; [ebp + 8] inVertices
+ ; [ebp + 12] outVertices
+ ; [ebp + 16] numVertices
+ ; [ebp + 20] boneMatrices
+ ; [ebp + 24] weightsAndIndices
+ ; [ebp + 28] inputStride
+ ; [ebp + 32] outputStride
+
+ push ebp
+ mov ebp, esp
+ pushad
+
+ ; Local variables (32 byte aligned)
+ ; [esp + 0] MaskW
+ ; [esp + 16] MaskVec3
+ ; [esp + 32] savedEcx
+ sub esp, 16*3
+ and esp, ~31
+
+ ; Create bitmasks on stack
+ sub eax, eax
+ mov [esp + 0], eax ; MaskW
+ mov [esp + 4], eax
+ mov [esp + 8], eax
+ dec eax
+ mov [esp + 12], eax
+ mov [esp + 16], eax ; MaskVec3
+ mov [esp + 20], eax
+ mov [esp + 24], eax
+ inc eax
+ mov [esp + 28], eax
+
+ mov esi, [ebp + 8] ; inVertices
+ mov edi, [ebp + 12] ; outVertices
+ mov ecx, [ebp + 16] ; numVertices
+ mov edx, [ebp + 24] ; weightsAndIndices
+
+ ; Prefetch vertices
+ prefetchnta [edx]
+ prefetchnta [esi]
+ prefetchnta [esi + 32]
+
+ align 32
+
+%%SkinSSE2_loop:
+ prefetchnta [esi + 64]
+
+ mov ebx, [ebp + 20] ; boneMatrices
+ mov [esp + 32], ecx ; savedEcx
+
+ ; Load first bone index
+%if %1 == 1
+ ; Single bone, no weight
+ mov eax, [edx]
+ shl eax, 6
+%else
+ ; Indices come after weights
+ mov eax, [edx + %1*4]
+ shl eax, 6
+ prefetchnta [ebx + eax]
+ prefetchnta [ebx + eax + 32]
+
+ ; Load second bone index
+ mov ecx, [edx + %1*4 + 4]
+ shl ecx, 6
+ prefetchnta [ebx + ecx]
+ prefetchnta [ebx + ecx + 32]
+
+ ; Load all weights to xmm0
+ movups xmm0, [edx]
+%endif
+
+ ; Load first matrix to xmm4-xmm7
+ movaps xmm4, [ebx + eax]
+ movaps xmm5, [ebx + eax + 16]
+ movaps xmm6, [ebx + eax + 32]
+ movaps xmm7, [ebx + eax + 48]
+
+%if %1 >= 2
+ ; Multiply first matrix with first weight
+ movaps xmm1, xmm0
+ shufps xmm1, xmm1, 0x00
+ mulps xmm4, xmm1
+ mulps xmm5, xmm1
+ mulps xmm6, xmm1
+ mulps xmm7, xmm1
+%endif
+
+%if %1 >= 3
+ ; Load third bone index
+ mov eax, [edx + %1*4 + 8]
+ shl eax, 6
+ prefetchnta [ebx + eax]
+ prefetchnta [ebx + eax + 32]
+%endif
+
+%if %1 >= 2
+ ; Load first two rows of the second matrix to xmm2-xmm3
+ movaps xmm2, [ebx + ecx]
+ movaps xmm3, [ebx + ecx + 16]
+ ; Shuffle second weight to all elements of xmm1
+ movaps xmm1, xmm0
+ shufps xmm1, xmm1, 0x55
+ ; Multiply two first rows of second matrix with second weight
+ mulps xmm2, xmm1
+ mulps xmm3, xmm1
+ ; Add
+ addps xmm4, xmm2
+ addps xmm5, xmm3
+
+ ; Load last two rows of the second matrix to xmm2-xmm3
+ movaps xmm2, [ebx + ecx + 32]
+ movaps xmm3, [ebx + ecx + 48]
+ ; Multiply two last rows of the second matri with second weight
+ mulps xmm2, xmm1
+ mulps xmm3, xmm1
+ ; Add
+ addps xmm6, xmm2
+ addps xmm7, xmm3
+%endif
+
+%if %1 >= 4
+ ; Load fourth bone index
+ mov ecx, [edx + %1*4 + 12]
+ shl ecx, 6
+ prefetchnta [ebx + ecx]
+ prefetchnta [ebx + ecx + 32]
+%endif
+
+%if %1 >= 3
+ ; Load first two rows of the third matrix to xmm2-xmm3
+ movaps xmm2, [ebx + eax]
+ movaps xmm3, [ebx + eax + 16]
+ ; Shuffle third weight to all elements of xmm1
+ movaps xmm1, xmm0
+ shufps xmm1, xmm1, 0xaa
+ ; Multiply first two rows of third matrix with third weight
+ mulps xmm2, xmm1
+ mulps xmm3, xmm1
+ ; Add
+ addps xmm4, xmm2
+ addps xmm5, xmm3
+
+ ; Load last two rows of the third matrix to xmm2-xmm3
+ movaps xmm2, [ebx + eax + 32]
+ movaps xmm3, [ebx + eax + 48]
+ ; Multiply last two rows of third matrix with third weight
+ mulps xmm2, xmm1
+ mulps xmm3, xmm1
+ ; Add
+ addps xmm6, xmm2
+ addps xmm7, xmm3
+%endif
+
+%if %1 >= 4
+ ; Load first two rows of the fourth matrix into xmm2-xmm3
+ movaps xmm2, [ebx + ecx]
+ movaps xmm3, [ebx + ecx + 16]
+ ; Shuffle fourth weight to all elements of xmm1
+ movaps xmm1, xmm0
+ shufps xmm1, xmm1, 0xff
+ ; Multiply first two rows of the fourth matrix with fourth weight
+ mulps xmm2, xmm1
+ mulps xmm3, xmm1
+ ; Add
+ addps xmm4, xmm2
+ addps xmm5, xmm3
+
+ ; Load last two rows of the fourth matrix to xmm2-xmm3
+ movaps xmm2, [ebx + ecx + 32]
+ movaps xmm3, [ebx + ecx + 48]
+ ; Multiply last two rows of the fourth matrix with fourth weight
+ mulps xmm2, xmm1
+ mulps xmm3, xmm1
+ ; Add
+ addps xmm6, xmm2
+ addps xmm7, xmm3
+%endif
+
+ ; Matrix is in xmm4-xmm7
+ ; Transform position by 4x4 matrix in xmm4-xmm7
+ movups xmm0, [esi]
+ movaps xmm1, xmm0
+ movaps xmm2, xmm0
+ shufps xmm1, xmm1, 0x55
+ shufps xmm2, xmm2, 0xaa
+ shufps xmm0, xmm0, 0x00
+ mulps xmm1, xmm5
+ mulps xmm2, xmm6
+ mulps xmm0, xmm4
+ addps xmm1, xmm2
+ addps xmm0, xmm7
+ addps xmm0, xmm1
+ ; Store vertex position in outvert
+ movaps xmm7, [esp + 16] ; MaskVec3
+ maskmovdqu xmm0, xmm7
+
+%if %2 ; Has normal
+ ; Transform vector by 3x3 matrix in xmm4-xmm6
+ movups xmm0, [esi + normalOffset]
+ movaps xmm1, xmm0
+ movaps xmm2, xmm0
+ shufps xmm1, xmm1, 0x55
+ shufps xmm2, xmm2, 0xaa
+ shufps xmm0, xmm0, 0x00
+ mulps xmm1, xmm5
+ mulps xmm2, xmm6
+ mulps xmm0, xmm4
+ addps xmm1, xmm2
+ addps xmm0, xmm1
+%endif
+
+%if %3 ; Has tangent
+ ; Transform vector by 3x3 matrix in xmm4-xmm6
+ movups xmm1, [esi + tangentOffset]
+ movaps xmm2, xmm1
+ movaps xmm3, xmm1
+ shufps xmm2, xmm2, 0x55
+ shufps xmm3, xmm3, 0xaa
+ mulps xmm2, xmm5
+ mulps xmm3, xmm6
+ movaps xmm6, xmm1 ; Save original tangent's W in xmm6
+ shufps xmm1, xmm1, 0x00
+ andps xmm6, [esp + 0] ; MaskW
+ mulps xmm1, xmm4
+ addps xmm2, xmm3
+ addps xmm1, xmm2
+%endif
+
+%if %2 || %3 ; Has normal or tangent
+ ; Calculate lengths and normalize
+ movaps xmm2, xmm0
+ movaps xmm5, xmm1
+ mulps xmm2, xmm2
+ mulps xmm5, xmm5
+ movaps xmm3, xmm2
+ movaps xmm4, xmm2
+ shufps xmm3, xmm5, 0x55
+ shufps xmm4, xmm5, 0xaa
+ shufps xmm2, xmm5, 0x00
+ addps xmm3, xmm4
+ addps xmm2, xmm3
+ sqrtps xmm2, xmm2
+ rcpps xmm2, xmm2
+ movaps xmm3, xmm2
+ shufps xmm2, xmm2, 0x00
+ shufps xmm3, xmm3, 0xaa
+ mulps xmm0, xmm2
+ mulps xmm1, xmm3
+%endif
+
+%if %2 ; Write normal
+ add edi, normalOffset
+ maskmovdqu xmm0, xmm7 ; MaskVec3
+ sub edi, normalOffset
+%endif
+
+%if %3 ; Write tangent
+ andps xmm1, xmm7 ; MaskVec3
+ orps xmm1, xmm6 ; Restore original W
+ movups [edi + tangentOffset], xmm1
+%endif
+
+%if %1 == 1
+ ; Indices only
+ add edx, 4
+%else
+ ; Indices and weights
+ add edx, %1 * 8
+%endif
+
+ add esi, [ebp + 28] ; inputStride
+ add edi, [ebp + 32] ; outputStride
+ mov ecx, [esp + 32] ; savedEcx
+ dec ecx
+ jnz %%SkinSSE2_loop
+
+ ; Remove local variables from stack
+ lea esp, [ebp-32]
+
+ popad
+ pop ebp
+ ret
+ align 16
+%endmacro
+
+
+global SkinSSE2_1Bone_Pos
+global SkinSSE2_2Bones_Pos
+global SkinSSE2_4Bones_Pos
+global SkinSSE2_1Bone_PosNormal
+global SkinSSE2_2Bones_PosNormal
+global SkinSSE2_4Bones_PosNormal
+global SkinSSE2_1Bone_PosNormalTan
+global SkinSSE2_2Bones_PosNormalTan
+global SkinSSE2_4Bones_PosNormalTan
+
+
+SkinSSE2_1Bone_Pos: SkinSSE2_Generic 1, 0, 0
+SkinSSE2_2Bones_Pos: SkinSSE2_Generic 2, 0, 0
+SkinSSE2_4Bones_Pos: SkinSSE2_Generic 4, 0, 0
+SkinSSE2_1Bone_PosNormal: SkinSSE2_Generic 1, 1, 0
+SkinSSE2_2Bones_PosNormal: SkinSSE2_Generic 2, 1, 0
+SkinSSE2_4Bones_PosNormal: SkinSSE2_Generic 4, 1, 0
+SkinSSE2_1Bone_PosNormalTan: SkinSSE2_Generic 1, 1, 1
+SkinSSE2_2Bones_PosNormalTan: SkinSSE2_Generic 2, 1, 1
+SkinSSE2_4Bones_PosNormalTan: SkinSSE2_Generic 4, 1, 1
diff --git a/Runtime/Filters/Mesh/MeshSkinningSSE2.h b/Runtime/Filters/Mesh/MeshSkinningSSE2.h
new file mode 100644
index 0000000..c085309
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshSkinningSSE2.h
@@ -0,0 +1,129 @@
+#if UNITY_SUPPORTS_SSE && !UNITY_64
+
+#if UNITY_OSX || UNITY_LINUX
+#define __cdecl
+#endif
+
+#define SKIN_SSE2_PARAMS \
+ const void* inVertices, \
+ void* outVertices, \
+ int numVertices, \
+ const void* boneMatrices, \
+ const void* weightsAndIndices, \
+ int inputStride, \
+ int outputStride
+
+typedef void (__cdecl *SkinSSE2_Function)(SKIN_SSE2_PARAMS);
+
+extern "C"
+{
+ void __cdecl SkinSSE2_1Bone_Pos(SKIN_SSE2_PARAMS);
+ void __cdecl SkinSSE2_2Bones_Pos(SKIN_SSE2_PARAMS);
+ void __cdecl SkinSSE2_4Bones_Pos(SKIN_SSE2_PARAMS);
+ void __cdecl SkinSSE2_1Bone_PosNormal(SKIN_SSE2_PARAMS);
+ void __cdecl SkinSSE2_2Bones_PosNormal(SKIN_SSE2_PARAMS);
+ void __cdecl SkinSSE2_4Bones_PosNormal(SKIN_SSE2_PARAMS);
+ void __cdecl SkinSSE2_1Bone_PosNormalTan(SKIN_SSE2_PARAMS);
+ void __cdecl SkinSSE2_2Bones_PosNormalTan(SKIN_SSE2_PARAMS);
+ void __cdecl SkinSSE2_4Bones_PosNormalTan(SKIN_SSE2_PARAMS);
+}
+
+
+bool SkinMeshOptimizedSSE2(SkinMeshInfo& info)
+{
+ if (!CPUInfo::HasSSE2Support())
+ {
+ return false;
+ }
+
+ SkinSSE2_Function skinFunc = NULL;
+
+ if (!info.skinNormals && !info.skinTangents)
+ {
+ switch (info.bonesPerVertex)
+ {
+ DebugAssert(info.inStride == sizeof(Vector3f));
+ case 1:
+ skinFunc = &SkinSSE2_1Bone_Pos;
+ break;
+ case 2:
+ skinFunc = &SkinSSE2_2Bones_Pos;
+ break;
+ case 4:
+ skinFunc = &SkinSSE2_4Bones_Pos;
+ break;
+
+ }
+ }
+ else if (info.skinNormals && !info.skinTangents)
+ {
+ DebugAssert(info.inStride == sizeof(Vector3f) + sizeof(Vector3f));
+ switch (info.bonesPerVertex)
+ {
+ case 1:
+ skinFunc = &SkinSSE2_1Bone_PosNormal;
+ break;
+ case 2:
+ skinFunc = &SkinSSE2_2Bones_PosNormal;
+ break;
+ case 4:
+ skinFunc = &SkinSSE2_4Bones_PosNormal;
+ break;
+
+ }
+ }
+ else if (info.skinNormals && info.skinTangents)
+ {
+ DebugAssert(info.inStride == sizeof(Vector3f) + sizeof(Vector3f) + sizeof(Vector4f));
+ switch (info.bonesPerVertex)
+ {
+ case 1:
+ skinFunc = &SkinSSE2_1Bone_PosNormalTan;
+ break;
+ case 2:
+ skinFunc = &SkinSSE2_2Bones_PosNormalTan;
+ break;
+ case 4:
+ skinFunc = &SkinSSE2_4Bones_PosNormalTan;
+ break;
+
+ }
+ }
+
+ if (skinFunc == NULL)
+ return false;
+
+ // Skin all vertices apart from last one!
+ if (info.vertexCount > 1)
+ {
+ (*skinFunc)(info.inVertices, info.outVertices, info.vertexCount - 1,info.cachedPose, info.compactSkin, info.inStride, info.outStride);
+ }
+ // Copy last vertex to stack to avoid reading/writing past end of buffer
+ if (info.vertexCount > 0)
+ {
+ const int maxStride = 2 * sizeof(Vector3f) + sizeof(Vector4f) + 4;
+ Assert(info.inStride <= maxStride && info.outStride <= maxStride);
+ // Need 4 bytes padding to access Vec3 as Vec4
+ char vertexCopyIn[maxStride + 4];
+ char vertexCopyOut[maxStride + 4];
+ int skinStride = (info.bonesPerVertex == 4) ? sizeof(BoneInfluence) :
+ (info.bonesPerVertex == 2) ? sizeof(BoneInfluence2) :
+ (info.bonesPerVertex == 1) ? sizeof(int) : 0;
+ Assert(skinStride != 0);
+ int index = info.vertexCount - 1;
+ const char* compactSkin = static_cast<const char*>(info.compactSkin) + index * skinStride;
+ const char* inVertex = static_cast<const char*>(info.inVertices) + index * info.inStride;
+ char* outVertex = static_cast<char*>(info.outVertices) + index * info.outStride;
+ memcpy(vertexCopyIn, inVertex, info.inStride);
+ (*skinFunc)(vertexCopyIn, vertexCopyOut, 1, info.cachedPose, compactSkin, info.inStride, info.outStride);
+ memcpy(outVertex, vertexCopyOut, info.outStride);
+ }
+
+ return true;
+}
+#else
+inline bool SkinMeshOptimizedSSE2(SkinMeshInfo& info)
+{
+ return false;
+}
+#endif
diff --git a/Runtime/Filters/Mesh/MeshSkinningTests.cpp b/Runtime/Filters/Mesh/MeshSkinningTests.cpp
new file mode 100644
index 0000000..407729b
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshSkinningTests.cpp
@@ -0,0 +1,228 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_UNIT_TESTS && UNITY_SUPPORTS_SSE && !UNITY_64
+
+#include "Runtime/Filters/Mesh/MeshSkinning.h"
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Math/Random/rand.h"
+#include "Runtime/Math/Matrix4x4.h"
+
+bool SkinMeshOptimizedSSE2(SkinMeshInfo& info);
+void SkinMesh(SkinMeshInfo& info);
+
+Vector3f RandomVector3InUnitBox(Rand& rnd)
+{
+ return Vector3f(rnd.GetSignedFloat(),
+ rnd.GetSignedFloat(),
+ rnd.GetSignedFloat());
+}
+
+SUITE (MeshSkinningTests)
+{
+TEST(MeshSkinning_AllFeatures)
+{
+ int failedPositions = 0;
+ int failedNormals = 0;
+ int failedTangents = 0;
+ int failedTangentSigns = 0;
+ int failedVertexCopies = 0;
+
+ const int minVertices = 1;
+ const int maxVertices = 100;
+ const int positionSize = 3*sizeof(float);
+ const int normalSize = 3*sizeof(float);
+ const int tangentSize = 4*sizeof(float);
+ const int maxStride = positionSize + normalSize + tangentSize;
+ const int trailingBytes = 128;
+
+ UInt8 inVertices[maxVertices * maxStride];
+ UInt8 outVerticesRef[maxVertices * maxStride + trailingBytes];
+ UInt8 outVerticesSimd[maxVertices * maxStride + trailingBytes];
+
+ SkinMeshInfo info;
+ memset(&info, 0, sizeof(info));
+ info.inVertices = inVertices;
+ info.vertexCount = minVertices;
+ info.normalOffset = positionSize;
+ info.tangentOffset = positionSize + normalSize;
+
+ // Try a large offset so AABBs don't contain (0,0,0)
+ Vector3f posOffset(-2000, 0, 2000);
+
+ const int numBones = 64;
+ Matrix4x4f *cachedPose;
+ ALLOC_TEMP_ALIGNED(cachedPose, Matrix4x4f, numBones, 32);
+ info.cachedPose = cachedPose;
+ for (int i = 0; i < numBones; i++)
+ {
+ Matrix4x4f mat;
+ mat.SetScale(Vector3f(1.0 + 0.5f*sin(i*0.3f),
+ 1.0 + 0.5f*sin(i*0.5f),
+ 1.0 + 0.5f*sin(i*0.7f)));
+ mat.SetPosition(Vector3f(100.0f*sin(i*1.0f),
+ 100.0f*sin(i*2.5f),
+ 100.0f*sin(i*3.3f)) + posOffset);
+ cachedPose[i] = mat;
+ }
+ info.boneCount = numBones;
+
+ Rand rnd(123);
+
+ int boneIndices[maxVertices];
+ BoneInfluence2 boneInfl2[maxVertices];
+ BoneInfluence boneInfl4[maxVertices];
+ for (int i = 0; i < maxVertices; i++)
+ {
+ boneIndices[i] = i%numBones;
+
+ BoneInfluence2& b2 = boneInfl2[i];
+ b2.boneIndex[0] = (i)%numBones;
+ b2.boneIndex[1] = (i/2+10)%numBones;
+ b2.weight[0] = rnd.GetFloat();
+ b2.weight[1] = 1.0f - b2.weight[0];
+
+ BoneInfluence& b4 = boneInfl4[i];
+ b4.boneIndex[0] = (i)%numBones;
+ b4.boneIndex[1] = (i/2+10)%numBones;
+ b4.boneIndex[2] = (i/3+20)%numBones;
+ b4.boneIndex[3] = (i/4+30)%numBones;
+ float weightLeft = 1.0f;
+ for (int j=0; j<3; j++)
+ {
+ b4.weight[j] = weightLeft * rnd.GetFloat();
+ weightLeft -= b4.weight[j];
+ }
+ b4.weight[3] = weightLeft;
+ }
+
+ for (info.bonesPerVertex = 1; info.bonesPerVertex <= 4; info.bonesPerVertex++)
+ {
+ if (info.bonesPerVertex == 3) continue;
+
+ switch (info.bonesPerVertex)
+ {
+ case 1:
+ info.compactSkin = boneIndices;
+ break;
+ case 2:
+ info.compactSkin = boneInfl2;
+ break;
+ case 4:
+ info.compactSkin = boneInfl4;
+ break;
+ }
+
+ for (int skinNormals = 0; skinNormals <= 1; skinNormals++)
+ {
+ info.skinNormals = (skinNormals != 0);
+
+ for (int skinTangents = 0; skinTangents <= 1; skinTangents++)
+ {
+ if (!skinNormals && skinTangents) continue;
+ info.skinTangents = (skinTangents != 0);
+
+ // Randomize vertex count and stride
+ info.vertexCount += 7;
+ while (info.vertexCount > maxVertices) info.vertexCount -= (maxVertices - minVertices);
+ info.inStride = positionSize;
+ info.inStride += skinNormals ? normalSize : 0;
+ info.inStride += skinTangents ? tangentSize : 0;
+ info.outStride = info.inStride;
+
+ UInt8* inVert = inVertices;
+ for (int i = 0; i < info.vertexCount; i++)
+ {
+ Vector3f* nextVec = (Vector3f*)inVert;
+ Vector3f pos = RandomVector3InUnitBox(rnd);
+ pos *= 1000.0f;
+ *nextVec++ = pos;
+ if (info.skinNormals)
+ {
+ Vector3f normal = RandomVector3InUnitBox(rnd);
+ normal = NormalizeSafe(normal);
+ *nextVec++ = normal;
+ }
+
+ if (info.skinTangents)
+ {
+ Vector3f tangent = RandomVector3InUnitBox(rnd);
+ tangent = NormalizeSafe(tangent);
+ *nextVec++ = tangent;
+ float* tangentSign = (float*)nextVec;
+ *tangentSign = (rnd.GetSignedFloat() < 0.0f) ? -1.0f : 1.0f;
+ }
+ inVert += info.inStride;
+ }
+
+ int outSize = info.vertexCount * info.outStride;
+ memset(outVerticesRef, 0xcc, outSize + trailingBytes);
+ memset(outVerticesSimd, 0xdd, outSize + trailingBytes);
+
+ info.outVertices = outVerticesRef;
+ SkinMesh(info);
+
+ info.outVertices = outVerticesSimd;
+ bool successSimd = SkinMeshOptimizedSSE2(info);
+ CHECK(successSimd);
+
+ // Check if we wrote past end of buffer
+ for (int i = 0; i < trailingBytes; i++)
+ {
+ CHECK_EQUAL(0xcc, outVerticesRef[outSize + i]);
+ CHECK_EQUAL(0xdd, outVerticesSimd[outSize + i]);
+ }
+
+ inVert = inVertices;
+ UInt8* vertRef = outVerticesRef;
+ UInt8* vertSimd = outVerticesSimd;
+ for (int i = 0; i < info.vertexCount; i++)
+ {
+ Vector3f* posRef = (Vector3f*)vertRef;
+ Vector3f* posSimd = (Vector3f*)vertRef;
+ if (!CompareApproximately(*posRef, *posSimd))
+ {
+ failedPositions++;
+ }
+ if (info.skinNormals)
+ {
+ Vector3f* normalRef = (Vector3f*)(vertRef + info.normalOffset);
+ Vector3f* normalSimd = (Vector3f*)(vertRef + info.normalOffset);
+ if (!CompareApproximately(*normalRef, *normalSimd))
+ {
+ failedNormals++;
+ }
+ }
+ if (info.skinTangents)
+ {
+ Vector3f* tangentRef = (Vector3f*)(vertRef + info.tangentOffset);
+ Vector3f* tangentSimd = (Vector3f*)(vertRef + info.tangentOffset);
+ if (!CompareApproximately(*tangentRef, *tangentSimd))
+ {
+ failedTangents++;
+ }
+ float* tangentSignRef = (float*)(vertRef + info.tangentOffset + sizeof(Vector3f));
+ float* tangentSignSimd = (float*)(vertRef + info.tangentOffset + sizeof(Vector3f));
+ if (*tangentSignRef != *tangentSignSimd)
+ {
+ failedTangentSigns++;
+ }
+ }
+
+ inVert += info.inStride;
+ vertRef += info.outStride;
+ vertSimd += info.outStride;
+ }
+ }
+ }
+ }
+
+ CHECK_EQUAL(0, failedPositions);
+ CHECK_EQUAL(0, failedNormals);
+ CHECK_EQUAL(0, failedTangents);
+ CHECK_EQUAL(0, failedTangentSigns);
+ CHECK_EQUAL(0, failedVertexCopies);
+}
+}
+#endif
diff --git a/Runtime/Filters/Mesh/MeshSkinningVFP.s b/Runtime/Filters/Mesh/MeshSkinningVFP.s
new file mode 100644
index 0000000..8829981
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshSkinningVFP.s
@@ -0,0 +1,187 @@
+#define UNITY_ASSEMBLER
+#include "Configuration/PrefixConfigure.h"
+#include "Runtime/Utilities/VFPUtility.h"
+
+#if UNITY_SUPPORTS_VFP
+
+.syntax unified
+
+.set device,0
+.set device,__arm__
+
+.if device
+
+//.code32
+.globl _s_SkinVertices_VFP
+.globl _s_SkinVertices_NoNormals_VFP
+.globl _s_SkinVertices_Tangents_VFP
+
+.globl _s_SkinVertices2Bones_VFP
+.globl _s_SkinVertices2Bones_NoNormals_VFP
+.globl _s_SkinVertices2Bones_Tangents_VFP
+
+.globl _s_SkinVertices4Bones_VFP
+.globl _s_SkinVertices4Bones_Copy4Ints_VFP
+.globl _s_SkinVertices4Bones_NoNormals_VFP
+.globl _s_SkinVertices4Bones_NoNormals_Copy4Ints_VFP
+.globl _s_SkinVertices4Bones_Tangents_VFP
+.globl _s_SkinVertices4Bones_Tangents_Copy4Ints_VFP
+
+#if UNITY_ANDROID
+.hidden _s_SkinVertices_VFP
+.hidden _s_SkinVertices_NoNormals_VFP
+.hidden _s_SkinVertices_Tangents_VFP
+
+.hidden _s_SkinVertices2Bones_VFP
+.hidden _s_SkinVertices2Bones_NoNormals_VFP
+.hidden _s_SkinVertices2Bones_Tangents_VFP
+
+.hidden _s_SkinVertices4Bones_VFP
+.hidden _s_SkinVertices4Bones_NoNormals_VFP
+.hidden _s_SkinVertices4Bones_Tangents_VFP
+#endif
+
+
+//===========================================================================================================================================
+
+
+#define SKIN_POS 1
+#define SKIN_POS_NRM 2
+#define SKIN_POS_NRM_TAN 3
+
+
+#define SKIN_2BONES 0
+#define SKIN_4BONES 0
+
+_s_SkinVertices_VFP:
+
+#define SKIN_1BONE SKIN_POS_NRM
+#define VERTEX_SZ 24
+#define LOOP_NAME _s_SkinVertices_VFP_loop
+
+#include "MeshSkinningVFP_Loop.h"
+
+#undef LOOP_NAME
+#undef VERTEX_SZ
+#undef SKIN_1BONE
+
+_s_SkinVertices_NoNormals_VFP:
+
+#define SKIN_1BONE SKIN_POS
+#define VERTEX_SZ 12
+#define LOOP_NAME _s_SkinVertices_NoNormals_VFP_loop
+
+#include "MeshSkinningVFP_Loop.h"
+
+#undef LOOP_NAME
+#undef VERTEX_SZ
+#undef SKIN_1BONE
+
+_s_SkinVertices_Tangents_VFP:
+
+#define SKIN_1BONE SKIN_POS_NRM_TAN
+#define VERTEX_SZ 40
+#define LOOP_NAME _s_SkinVertices_Tangents_VFP_loop
+
+#include "MeshSkinningVFP_Loop.h"
+
+#undef LOOP_NAME
+#undef VERTEX_SZ
+#undef SKIN_1BONE
+
+#undef SKIN_4BONES
+#undef SKIN_2BONES
+
+
+//===========================================================================================================================================
+
+#define SKIN_1BONE 0
+#define SKIN_4BONES 0
+
+_s_SkinVertices2Bones_VFP:
+
+#define SKIN_2BONES SKIN_POS_NRM
+#define VERTEX_SZ 24
+#define LOOP_NAME _s_SkinVertices2Bones_VFP_Loop
+
+#include "MeshSkinningVFP_Loop.h"
+
+#undef LOOP_NAME
+#undef VERTEX_SZ
+#undef SKIN_2BONES
+
+_s_SkinVertices2Bones_NoNormals_VFP:
+
+#define SKIN_2BONES SKIN_POS
+#define VERTEX_SZ 12
+#define LOOP_NAME _s_SkinVertices2Bones_NoNormals_VFP_Loop
+
+#include "MeshSkinningVFP_Loop.h"
+
+#undef LOOP_NAME
+#undef VERTEX_SZ
+#undef SKIN_2BONES
+
+_s_SkinVertices2Bones_Tangents_VFP:
+
+#define SKIN_2BONES SKIN_POS_NRM_TAN
+#define VERTEX_SZ 40
+#define LOOP_NAME _s_SkinVertices2Bones_Tangents_VFP_loop
+
+#include "MeshSkinningVFP_Loop.h"
+
+#undef LOOP_NAME
+#undef VERTEX_SZ
+#undef SKIN_2BONES
+
+#undef SKIN_4BONES
+#undef SKIN_1BONE
+
+//===========================================================================================================================================
+
+#define SKIN_1BONE 0
+#define SKIN_2BONES 0
+
+_s_SkinVertices4Bones_VFP:
+
+#define SKIN_4BONES SKIN_POS_NRM
+#define VERTEX_SZ 24
+#define LOOP_NAME _s_SkinVertices4Bones_VFP_loop
+
+#include "MeshSkinningVFP_Loop.h"
+
+#undef LOOP_NAME
+#undef VERTEX_SZ
+#undef SKIN_4BONES
+
+_s_SkinVertices4Bones_NoNormals_VFP:
+
+#define SKIN_4BONES SKIN_POS
+#define VERTEX_SZ 12
+#define LOOP_NAME _s_SkinVertices4Bones_NoNormals_VFP_loop
+
+#include "MeshSkinningVFP_Loop.h"
+
+#undef LOOP_NAME
+#undef VERTEX_SZ
+#undef SKIN_4BONES
+
+_s_SkinVertices4Bones_Tangents_VFP:
+
+#define SKIN_4BONES SKIN_POS_NRM_TAN
+#define VERTEX_SZ 40
+#define LOOP_NAME _s_SkinVertices4Bones_Tangents_VFP_loop
+
+#include "MeshSkinningVFP_Loop.h"
+
+#undef LOOP_NAME
+#undef VERTEX_SZ
+#undef SKIN_4BONES
+
+#undef SKIN_2BONES
+#undef SKIN_1BONE
+
+//===========================================================================================================================================
+
+.endif
+#endif
diff --git a/Runtime/Filters/Mesh/MeshSkinningVFP_Loop.h b/Runtime/Filters/Mesh/MeshSkinningVFP_Loop.h
new file mode 100644
index 0000000..3b7400f
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshSkinningVFP_Loop.h
@@ -0,0 +1,335 @@
+
+// defines
+// SKIN_1BONE
+// SKIN_2BONES
+// SKIN_4BONES
+// LOOP_NAME
+// VERTEX_SZ
+
+// skin types
+// SKIN_POS
+// SKIN_POS_NRM
+// SKIN_POS_NRM_TAN
+
+//r0: const void* bones4x4
+//r1: const void* srcVertData
+//r2: const void* srcVertDataEnd
+//r3: const BoneInfluence4* srcBoneInfluence4
+//[sp+0] -> r4: const void* dstVertData
+
+// s0,s1,s2 <- output: pos
+// s3,s4,s5 <- output: nrm
+// s6,s7,s8,s9 <- output: tan
+// s10,s11,s12 <- input: pos
+// s13,s14,s15 <- input: nrm
+// s16,s17,s18,s19 <- input: tan
+// s20-s31 <- matrix [3x4] last row loaded directly to output pos
+
+//===========================================================================================================================================
+//
+// Common
+
+#define CALC_POS_2 FMACS3 (0,1,2, 20,21,22, 10,10,10)
+#define CALC_POS_3 FMACS3 (0,1,2, 24,25,26, 11,11,11)
+#define CALC_POS_4 FMACS3 (0,1,2, 28,29,30, 12,12,12)
+
+
+#if (SKIN_1BONE == SKIN_POS_NRM) || (SKIN_1BONE == SKIN_POS_NRM_TAN) \
+ || (SKIN_2BONES == SKIN_POS_NRM) || (SKIN_2BONES == SKIN_POS_NRM_TAN) \
+ || (SKIN_4BONES == SKIN_POS_NRM) || (SKIN_4BONES == SKIN_POS_NRM_TAN)
+
+ #define LOAD_POS_NRM vldmia.32 r1!, {s10-s15}
+ #define STORE_POS_NRM vstmia.32 r4!, {s0-s5}
+ #define CALC_NRM_1 FMULS3 (3,4,5, 20,21,22, 13,13,13)
+ #define CALC_NRM_2 FMACS3 (3,4,5, 24,25,26, 14,14,14)
+ #define CALC_NRM_3 FMACS3 (3,4,5, 28,29,30, 15,15,15)
+#else
+ #define LOAD_POS_NRM vldmia.32 r1!, {s10-s12}
+ #define STORE_POS_NRM vstmia.32 r4!, {s0-s2}
+ #define CALC_NRM_1
+ #define CALC_NRM_2
+ #define CALC_NRM_3
+#endif
+
+#if (SKIN_1BONE == SKIN_POS_NRM_TAN) || (SKIN_2BONES == SKIN_POS_NRM_TAN) || (SKIN_4BONES == SKIN_POS_NRM_TAN)
+ #define LOAD_TAN vldmia.32 r1!, {s16-s19}
+ #define STORE_TAN vstmia.32 r4!, {s6-s9}
+ #define CALC_TAN_1 FMULS3 (6,7,8, 20,21,22, 16,16,16)
+ #define CALC_TAN_2 FMACS3 (6,7,8, 24,25,26, 17,17,17)
+ #define CALC_TAN_3 FMACS3 (6,7,8, 28,29,30, 18,18,18)
+ #define CALC_TAN_4 fcpys s9, s19
+#else
+ #define LOAD_TAN
+ #define STORE_TAN
+ #define CALC_TAN_1
+ #define CALC_TAN_2
+ #define CALC_TAN_3
+ #define CALC_TAN_4
+#endif
+
+
+
+
+//===========================================================================================================================================
+//
+// 1 bone skinning
+
+#if (SKIN_1BONE == SKIN_POS) || (SKIN_1BONE == SKIN_POS_NRM) || (SKIN_1BONE == SKIN_POS_NRM_TAN)
+
+mov ip, sp
+vpush {d7-d15}
+stmfd sp!, {r4,r5,r6,r7,r8,r10,r11}
+
+ldr r4, [ip, #0]
+
+ldr r5, [r3], #4
+add r5, r0, r5, lsl #6
+add r6, r5, #48
+
+vldmia.32 r6, {s0-s2}
+vldmia.32 r5!, {s20-s23}
+vldmia.32 r5!, {s24-s27}
+
+.align 4
+LOOP_NAME:
+
+LOAD_POS_NRM
+
+CALC_POS_2
+CALC_NRM_1
+ ldr r6, [r3], #4 // next matrix index
+vldmia.32 r5, {s28-s30} // bone matrix
+ add r5, r0, r6, lsl #6 // next matrix addr
+
+
+CALC_POS_3
+CALC_NRM_2
+
+LOAD_TAN
+ add r6, r5, #48
+ cmp r1, r2
+
+CALC_TAN_1
+ vldmiacc.32 r5!, {s20-s23} // next bone matrix
+
+
+CALC_POS_4
+
+CALC_TAN_2
+CALC_NRM_3
+ vldmiacc.32 r5!, {s24-s27} // next bone matrix
+
+CALC_TAN_3
+CALC_TAN_4
+
+ pld [r1, #1024]
+
+
+STORE_POS_NRM
+STORE_TAN
+
+ vldmiacc.32 r6, {s0-s2}
+
+bcc LOOP_NAME
+
+ldmfd sp!, {r4,r5,r6,r7,r8,r10,r11}
+vpop {d7-d15}
+bx lr
+
+
+//===========================================================================================================================================
+
+#elif (SKIN_2BONES == SKIN_POS) || (SKIN_2BONES == SKIN_POS_NRM) || (SKIN_2BONES == SKIN_POS_NRM_TAN)
+
+mov ip, sp
+vpush {d7-d15}
+stmfd sp!, {r4,r5,r6,r7,r8,r10,r11}
+
+ldr r4, [ip, #0]
+
+
+.align 4
+LOOP_NAME:
+
+vldmia.32 r3!, {s3,s4} // w
+ ldmia r3!, {r5-r6} // idx
+
+ add r5, r0, r5, lsl #6 // M0
+ add r6, r0, r6, lsl #6 // M1
+
+
+vldmia.64 r5!, {d4,d5} // M0[0]
+
+vldmia.64 r6!, {d6,d7} // M1[0]
+FMULS3 (20,21,22, 8,9,10, 3,3,3) // M0[0] * w
+
+vldmia.64 r5!, {d4,d5} // M0[1]
+FMACS3 (20,21,22, 12,13,14, 4,4,4) // + M1[0] * w
+
+vldmia.64 r6!, {d6,d7} // M1[1]
+FMULS3 (24,25,26, 8,9,10, 3,3,3) // M0[1] * w
+
+vldmia.64 r5!, {d4,d5} // M0[2]
+FMACS3 (24,25,26, 12,13,14, 4,4,4) // + M1[1] * w
+
+vldmia.64 r6!, {d6,d7} // M1[2]
+FMULS3 (28,29,30, 8,9,10, 3,3,3) // M0[2] * w
+
+vldmia.64 r5!, {d4,d5} // M0[3]
+FMACS3 (28,29,30, 12,13,14, 4,4,4) // + M1[2] * w
+
+vldmia.64 r6!, {d6,d7} // M1[3]
+FMULS3 (0,1,2, 8,9,10, 3,3,3) // M0[3] * w
+
+FMACS3 (0,1,2, 12,13,14, 4,4,4) // + M1[3] * w
+
+
+LOAD_POS_NRM
+LOAD_TAN
+
+CALC_POS_2
+CALC_NRM_1
+CALC_TAN_1
+
+CALC_POS_3
+CALC_NRM_2
+CALC_TAN_2
+ pld [r1, #1024]
+ cmp r1, r2
+CALC_POS_4
+CALC_NRM_3
+CALC_TAN_3
+
+CALC_TAN_4
+
+
+STORE_POS_NRM
+STORE_TAN
+
+bcc LOOP_NAME
+
+ldmfd sp!, {r4,r5,r6,r7,r8,r10,r11}
+vpop {d7-d15}
+bx lr
+
+
+
+//===========================================================================================================================================
+
+#elif (SKIN_4BONES == SKIN_POS) || (SKIN_4BONES == SKIN_POS_NRM) || (SKIN_4BONES == SKIN_POS_NRM_TAN)
+
+mov ip, sp
+vpush {d7-d15}
+stmfd sp!, {r4,r5,r6,r7,r8}
+
+ldr r4, [ip, #0]
+
+
+.align 4
+LOOP_NAME:
+
+vldmia.32 r3!, {s3-s6} // w
+ ldmia r3!, {r5-r8} // idx
+
+ add r5, r0, r5, lsl #6 // M0
+ add r6, r0, r6, lsl #6 // M1
+ add r7, r0, r7, lsl #6 // M2
+ add r8, r0, r8, lsl #6 // M3
+
+
+vldmia.64 r5!, {d4,d5} // M0[0]
+
+vldmia.64 r6!, {d6,d7} // M1[0]
+FMULS3 (20,21,22, 8,9,10, 3,3,3) // M0[0] * w
+
+vldmia.64 r7!, {d4,d5} // M2[0]
+FMACS3 (20,21,22, 12,13,14, 4,4,4) // + M1[0] * w
+
+vldmia.64 r8!, {d6,d7} // M3[0]
+FMACS3 (20,21,22, 8,9,10, 5,5,5) // + M2[0] * w
+
+vldmia.64 r5!, {d4,d5} // M0[1]
+FMACS3 (20,21,22, 12,13,14, 6,6,6) // + M3[0] * w
+
+vldmia.64 r6!, {d6,d7} // M1[1]
+FMULS3 (24,25,26, 8,9,10, 3,3,3) // M0[1] * w
+
+vldmia.64 r7!, {d4,d5} // M2[1]
+FMACS3 (24,25,26, 12,13,14, 4,4,4) // + M1[1] * w
+
+vldmia.64 r8!, {d6,d7} // M3[1]
+FMACS3 (24,25,26, 8,9,10, 5,5,5) // + M2[1] * w
+
+vldmia.64 r5!, {d4,d5} // M0[2]
+FMACS3 (24,25,26, 12,13,14, 6,6,6) // + M3[1] * w
+
+vldmia.64 r6!, {d6,d7} // M1[2]
+FMULS3 (28,29,30, 8,9,10, 3,3,3) // M0[2] * w
+
+vldmia.64 r7!, {d4,d5} // M2[2]
+FMACS3 (28,29,30, 12,13,14, 4,4,4) // + M1[2] * w
+
+vldmia.64 r8!, {d6,d7} // M3[2]
+FMACS3 (28,29,30, 8,9,10, 5,5,5) // + M2[2] * w
+
+vldmia.64 r5!, {d4,d5} // M0[3]
+FMACS3 (28,29,30, 12,13,14, 6,6,6) // + M3[2] * w
+
+vldmia.64 r6!, {d6,d7} // M1[3]
+FMULS3 (0,1,2, 8,9,10, 3,3,3) // M0[3] * w
+
+vldmia.64 r7!, {d4,d5} // M2[3]
+FMACS3 (0,1,2, 12,13,14, 4,4,4) // + M1[3] * w
+
+vldmia.64 r8!, {d6,d7} // M3[3]
+FMACS3 (0,1,2, 8,9,10, 5,5,5) // + M2[3] * w
+
+FMACS3 (0,1,2, 12,13,14, 6,6,6) // + M3[3] * w
+
+
+LOAD_POS_NRM
+LOAD_TAN
+
+CALC_POS_2
+CALC_NRM_1
+CALC_TAN_1
+
+CALC_POS_3
+CALC_NRM_2
+CALC_TAN_2
+ pld [r1, #1024]
+ cmp r1, r2
+CALC_POS_4
+CALC_NRM_3
+CALC_TAN_3
+
+CALC_TAN_4
+
+
+STORE_POS_NRM
+STORE_TAN
+
+bcc LOOP_NAME
+
+ldmfd sp!, {r4,r5,r6,r7,r8}
+vpop {d7-d15}
+bx lr
+
+#endif
+
+//===========================================================================================================================================
+
+#undef CALC_POS_1
+#undef CALC_POS_2
+#undef CALC_POS_3
+#undef STORE_POS_NRM
+#undef LOAD_POS_NRM
+#undef CALC_NRM_1
+#undef CALC_NRM_2
+#undef CALC_NRM_3
+#undef LOAD_TAN
+#undef STORE_TAN
+#undef CALC_TAN_1
+#undef CALC_TAN_2
+#undef CALC_TAN_3
+#undef CALC_TAN_4
diff --git a/Runtime/Filters/Mesh/MeshUtility.cpp b/Runtime/Filters/Mesh/MeshUtility.cpp
new file mode 100644
index 0000000..75d8e7f
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshUtility.cpp
@@ -0,0 +1,58 @@
+#include "UnityPrefix.h"
+#include "MeshUtility.h"
+#include "Runtime/Geometry/Plane.h"
+#include "Mesh.h"
+
+using namespace std;
+
+void CalculateNormals (StrideIterator<Vector3f> verts, const UInt32* indices, int vertexCount, int triangleCount, StrideIterator<Vector3f> outNormals)
+{
+ std::fill_n (outNormals, vertexCount, Vector3f(0,0,0));
+
+ // Add normals from faces
+ int idx = 0;
+ for( int i = 0; i < triangleCount; ++i )
+ {
+ UInt32 index0 = indices[idx+0];
+ UInt32 index1 = indices[idx+1];
+ UInt32 index2 = indices[idx+2];
+ Vector3f faceNormal = CalcRawNormalFromTriangle( verts[index0], verts[index1], verts[index2] );
+ outNormals[index0] += faceNormal;
+ outNormals[index1] += faceNormal;
+ outNormals[index2] += faceNormal;
+ idx += 3;
+ }
+
+ // Normalize
+ for (StrideIterator<Vector3f> end = outNormals + vertexCount; outNormals != end; ++outNormals )
+ {
+ *outNormals = NormalizeFast (*outNormals);
+ }
+}
+
+
+float CalculateSurfaceArea (
+ const Matrix4x4f& objectToWorld,
+ const Mesh::TemporaryIndexContainer& triangles,
+ dynamic_array<Vector3f>& vertices)
+{
+ // transform the vertices to world space,
+ // do it in place since they are a copy
+ for (int i = 0; i < vertices.size (); i++)
+ vertices[i] = objectToWorld.MultiplyPoint3 (vertices[i]);
+
+ // calculate the area
+ float cachedSurfaceArea = 0;
+ for (int i = 0; i < triangles.size () / 3; i++)
+ {
+ DebugAssert (triangles[3 * i] < vertices.size ());
+ DebugAssert (triangles[3 * i + 1] < vertices.size ());
+ DebugAssert (triangles[3 * i + 2] < vertices.size ());
+ Vector3f a = vertices[triangles[3 * i]];
+ Vector3f b = vertices[triangles[3 * i + 1]];
+ Vector3f c = vertices[triangles[3 * i + 2]];
+ cachedSurfaceArea += Magnitude (Cross (b - a, c - a)) * 0.5f;
+ }
+
+ return cachedSurfaceArea;
+}
diff --git a/Runtime/Filters/Mesh/MeshUtility.h b/Runtime/Filters/Mesh/MeshUtility.h
new file mode 100644
index 0000000..748c874
--- /dev/null
+++ b/Runtime/Filters/Mesh/MeshUtility.h
@@ -0,0 +1,42 @@
+#ifndef MESHUTILITY_H
+#define MESHUTILITY_H
+
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Utilities/StrideIterator.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+struct Tangent;
+
+// Calculate normals for the mesh, given vertex array and triangle list (3 indices per triangle).
+void CalculateNormals( StrideIterator<Vector3f> verts, const UInt32* indices, int vertexCount, int triangleCount, StrideIterator<Vector3f> outNormals );
+
+float CalculateSurfaceArea (const Matrix4x4f& objectToWorld, const Mesh::TemporaryIndexContainer& triangles, dynamic_array<Vector3f>& vertices);
+
+// Use this to generate a normal from an tangent basis quickly
+inline Vector3f NormalFromQuatTangentBasis (const Quaternionf& lhs)
+{
+ float x = lhs.x * 2.0F;
+ float y = lhs.y * 2.0F;
+ float z = lhs.z * 2.0F;
+ float xx = lhs.x * x;
+ float yy = lhs.y * y;
+ float xz = lhs.x * z;
+ float yz = lhs.y * z;
+ float wx = lhs.w * x;
+ float wy = lhs.w * y;
+
+ Vector3f res;
+ res.x = xz - wy;
+ res.y = yz + wx;
+ res.z = 1.0f - xx - yy;
+ AssertIf (!CompareApproximately (res, RotateVectorByQuat(Inverse (lhs), Vector3f::zAxis)));
+ return res;
+}
+
+//bool HasDegenerateTriangles (const Vector3f* verts, const MeshData &meshData, float degenerateArea = 0.0001);
+
+
+#endif
diff --git a/Runtime/Filters/Mesh/SkinGeneric.h b/Runtime/Filters/Mesh/SkinGeneric.h
new file mode 100644
index 0000000..ef30d81
--- /dev/null
+++ b/Runtime/Filters/Mesh/SkinGeneric.h
@@ -0,0 +1,338 @@
+#ifndef SKINGENERIC_H
+#define SKINGENERIC_H
+
+#include "Runtime/Filters/Mesh/VertexData.h"
+
+#if UNITY_PS3
+template<TransformInstruction transformInstruction, int bonesPerVertexCount,
+bool skinNormal, bool skinTangent>
+void SkinGenericStreamed (SkinMeshInfo& info)
+{
+ const int* influence1 = reinterpret_cast<const int*> (info.compactSkin);
+ const BoneInfluence2* influence2 = reinterpret_cast<const BoneInfluence2*> (info.compactSkin);
+ const BoneInfluence* influence4 = reinterpret_cast<const BoneInfluence*> (info.compactSkin);
+
+ const Matrix4x4f* bones4x4 = info.cachedPose;
+
+ int count = info.vertexCount;
+
+ int vertexOffset = info.vertexData->GetStream(0).offset;
+ const int vertexStride = info.vertexData->GetStream(0).stride;
+
+ int normalOffset = info.vertexData->GetStream(1).offset;
+ const int normalStride = info.vertexData->GetStream(1).stride;
+
+ int tangentOffset = info.vertexData->GetStream(2).offset;
+ const int tangentStride = info.vertexData->GetStream(2).stride;
+
+ const int copyDataOffset = info.vertexData->GetStream(3).offset;
+ const int copyDataSize = info.vertexData->GetStream(3).stride * info.vertexCount;
+
+ const UInt8* inputVertex = (const UInt8*)info.inVertices;
+ UInt8* outputVertex = (UInt8*)info.outVertices;
+
+ Matrix4x4f poseBlended;
+ const Matrix4x4f* poseToUse;
+
+ for( int v = 0; v < count; v++ )
+ {
+ ALIGN_LOOP_OPTIMIZATION
+
+ Prefetch(inputVertex + 256);
+
+ // Blend the matrices first, then transform everything with this
+ // blended matrix. Gives a small speed boost on XCode/Intel (11.3 to 12.00 FPS
+ // in skin4 bench), and a good boost on MSVC/Windows (9.6 to 12.4 FPS).
+ if (bonesPerVertexCount == 1)
+ {
+ poseToUse = &bones4x4[*influence1];
+ }
+ else if (bonesPerVertexCount == 2)
+ {
+ float weight0 = influence2->weight[0];
+ float weight1 = influence2->weight[1];
+ const float* b4x40 = bones4x4[influence2->boneIndex[0]].m_Data;
+ const float* b4x41 = bones4x4[influence2->boneIndex[1]].m_Data;
+ // we need only 12 components of the matrix
+ poseBlended.m_Data[ 0] = b4x40[ 0] * weight0 + b4x41[ 0] * weight1;
+ poseBlended.m_Data[ 1] = b4x40[ 1] * weight0 + b4x41[ 1] * weight1;
+ poseBlended.m_Data[ 2] = b4x40[ 2] * weight0 + b4x41[ 2] * weight1;
+ poseBlended.m_Data[ 4] = b4x40[ 4] * weight0 + b4x41[ 4] * weight1;
+ poseBlended.m_Data[ 5] = b4x40[ 5] * weight0 + b4x41[ 5] * weight1;
+ poseBlended.m_Data[ 6] = b4x40[ 6] * weight0 + b4x41[ 6] * weight1;
+ poseBlended.m_Data[ 8] = b4x40[ 8] * weight0 + b4x41[ 8] * weight1;
+ poseBlended.m_Data[ 9] = b4x40[ 9] * weight0 + b4x41[ 9] * weight1;
+ poseBlended.m_Data[10] = b4x40[10] * weight0 + b4x41[10] * weight1;
+ poseBlended.m_Data[12] = b4x40[12] * weight0 + b4x41[12] * weight1;
+ poseBlended.m_Data[13] = b4x40[13] * weight0 + b4x41[13] * weight1;
+ poseBlended.m_Data[14] = b4x40[14] * weight0 + b4x41[14] * weight1;
+ poseToUse = &poseBlended;
+ }
+ else if (bonesPerVertexCount == 4)
+ {
+ float weight0 = influence4->weight[0];
+ float weight1 = influence4->weight[1];
+ float weight2 = influence4->weight[2];
+ float weight3 = influence4->weight[3];
+
+ const float* b4x40 = bones4x4[influence4->boneIndex[0]].m_Data;
+ const float* b4x41 = bones4x4[influence4->boneIndex[1]].m_Data;
+ const float* b4x42 = bones4x4[influence4->boneIndex[2]].m_Data;
+ const float* b4x43 = bones4x4[influence4->boneIndex[3]].m_Data;
+ // we need only 12 components of the matrix, so unroll
+ poseBlended.m_Data[ 0] = b4x40[ 0] * weight0 + b4x41[ 0] * weight1 + b4x42[ 0] * weight2 + b4x43[ 0] * weight3;
+ poseBlended.m_Data[ 1] = b4x40[ 1] * weight0 + b4x41[ 1] * weight1 + b4x42[ 1] * weight2 + b4x43[ 1] * weight3;
+ poseBlended.m_Data[ 2] = b4x40[ 2] * weight0 + b4x41[ 2] * weight1 + b4x42[ 2] * weight2 + b4x43[ 2] * weight3;
+ poseBlended.m_Data[ 4] = b4x40[ 4] * weight0 + b4x41[ 4] * weight1 + b4x42[ 4] * weight2 + b4x43[ 4] * weight3;
+ poseBlended.m_Data[ 5] = b4x40[ 5] * weight0 + b4x41[ 5] * weight1 + b4x42[ 5] * weight2 + b4x43[ 5] * weight3;
+ poseBlended.m_Data[ 6] = b4x40[ 6] * weight0 + b4x41[ 6] * weight1 + b4x42[ 6] * weight2 + b4x43[ 6] * weight3;
+ poseBlended.m_Data[ 8] = b4x40[ 8] * weight0 + b4x41[ 8] * weight1 + b4x42[ 8] * weight2 + b4x43[ 8] * weight3;
+ poseBlended.m_Data[ 9] = b4x40[ 9] * weight0 + b4x41[ 9] * weight1 + b4x42[ 9] * weight2 + b4x43[ 9] * weight3;
+ poseBlended.m_Data[10] = b4x40[10] * weight0 + b4x41[10] * weight1 + b4x42[10] * weight2 + b4x43[10] * weight3;
+ poseBlended.m_Data[12] = b4x40[12] * weight0 + b4x41[12] * weight1 + b4x42[12] * weight2 + b4x43[12] * weight3;
+ poseBlended.m_Data[13] = b4x40[13] * weight0 + b4x41[13] * weight1 + b4x42[13] * weight2 + b4x43[13] * weight3;
+ poseBlended.m_Data[14] = b4x40[14] * weight0 + b4x41[14] * weight1 + b4x42[14] * weight2 + b4x43[14] * weight3;
+ poseToUse = &poseBlended;
+ }
+
+ // skin components
+ Vector3f outVertex, outNormal, outTangent;
+ const Vector3f* vertex = reinterpret_cast<const Vector3f*>( inputVertex + vertexOffset);
+ const Vector3f* normal = reinterpret_cast<const Vector3f*>( inputVertex + normalOffset );
+ const Vector3f* tangent = reinterpret_cast<const Vector3f*>( inputVertex + tangentOffset );
+ poseToUse->MultiplyPoint3( *vertex, outVertex );
+ if( skinNormal )
+ {
+ poseToUse->MultiplyVector3( *normal, outNormal );
+ if (transformInstruction == kNormalizeFastest)
+ {
+ float sqr1 = SqrMagnitude( outNormal );
+ float invsqrt1 = FastestInvSqrt (sqr1);
+ outNormal *= invsqrt1;
+ }
+ else if (transformInstruction == kNormalizeFast)
+ {
+ float sqr1 = SqrMagnitude( outNormal );
+ float invsqrt1 = FastInvSqrt (sqr1);
+ outNormal *= invsqrt1;
+ }
+ }
+ if( skinTangent )
+ {
+ poseToUse->MultiplyVector3( *tangent, outTangent );
+ if (transformInstruction == kNormalizeFastest)
+ {
+ float sqr1 = SqrMagnitude( outTangent );
+ float invsqrt1 = FastestInvSqrt (sqr1);
+ outTangent *= invsqrt1;
+ }
+ else if (transformInstruction == kNormalizeFast)
+ {
+ float sqr1 = SqrMagnitude( outTangent );
+ float invsqrt1 = FastInvSqrt (sqr1);
+ outTangent *= invsqrt1;
+ }
+ }
+
+ // write data out
+ *reinterpret_cast<Vector3f*> (outputVertex + vertexOffset) = outVertex;
+ if( skinNormal )
+ {
+ *reinterpret_cast<Vector3f*>( outputVertex + normalOffset ) = outNormal;
+ }
+ if( skinTangent )
+ {
+ *reinterpret_cast<Vector3f*>( outputVertex + tangentOffset ) = outTangent;
+ *reinterpret_cast<float*>( outputVertex + tangentOffset + sizeof(Vector3f) ) = *reinterpret_cast<const float*>( inputVertex + tangentOffset + sizeof(Vector3f) );
+ }
+
+ vertexOffset += vertexStride;
+ normalOffset += normalStride;
+ tangentOffset += tangentStride;
+
+ if (bonesPerVertexCount == 1)
+ influence1++;
+ else if (bonesPerVertexCount == 2)
+ influence2++;
+ if (bonesPerVertexCount == 4)
+ influence4++;
+ }
+
+ // copy
+ const UInt8* copyDataSrc = inputVertex + copyDataOffset;
+ UInt8* copyDataDst = outputVertex + copyDataOffset;
+ memcpy(copyDataDst, copyDataSrc, copyDataSize);
+}
+#endif
+
+template<TransformInstruction transformInstruction, int bonesPerVertexCount,
+ bool skinNormal, bool skinTangent>
+void SkinGeneric (SkinMeshInfo& info);
+
+template<TransformInstruction transformInstruction, int bonesPerVertexCount,
+ bool skinNormal, bool skinTangent>
+void SkinGeneric (SkinMeshInfo& info)
+{
+#if UNITY_PS3
+ if(info.vertexData && (info.vertexData->GetActiveStreamCount() > 2))
+ return SkinGenericStreamed<transformInstruction, bonesPerVertexCount, skinNormal, skinTangent>(info);
+#endif
+ const int* influence1 = reinterpret_cast<const int*> (info.compactSkin);
+ const BoneInfluence2* influence2 = reinterpret_cast<const BoneInfluence2*> (info.compactSkin);
+ const BoneInfluence* influence4 = reinterpret_cast<const BoneInfluence*> (info.compactSkin);
+
+ const Matrix4x4f* bones4x4 = info.cachedPose;
+
+ const int inStride = info.inStride;
+ int outStride = info.outStride;
+ int count = info.vertexCount;
+
+ const int normalOffset = info.normalOffset;
+ const int tangentOffset = info.tangentOffset;
+
+ const UInt8* inputVertex = (const UInt8*)info.inVertices;
+ UInt8* outputVertex = (UInt8*)info.outVertices;
+
+ Matrix4x4f poseBlended;
+ const Matrix4x4f* poseToUse;
+
+
+#if !ENABLE_MULTITHREADED_SKINNING
+ PROFILER_AUTO(gMeshSkinningSlowpath, NULL);
+#endif
+
+ //;;printf_console("bonesPerVertexCount: %d, skinNormal: %d, normalOffset: %d, inStride: %d, copyDataSizeInts: %d, count: %d, boneCount: %d, outputVertex: %d\n",
+ // bonesPerVertexCount, (int)skinNormal, normalOffset, inStride, copyDataSizeInts, count, info.boneCount, (int)outputVertex);
+ //;;uint64_t delta = mach_absolute_time();
+
+ for( int v = 0; v < count; v++ )
+ {
+ ALIGN_LOOP_OPTIMIZATION
+
+ Prefetch(inputVertex + 256);
+
+ // Blend the matrices first, then transform everything with this
+ // blended matrix. Gives a small speed boost on XCode/Intel (11.3 to 12.00 FPS
+ // in skin4 bench), and a good boost on MSVC/Windows (9.6 to 12.4 FPS).
+ if (bonesPerVertexCount == 1)
+ {
+ poseToUse = &bones4x4[*influence1];
+ }
+ else if (bonesPerVertexCount == 2)
+ {
+ float weight0 = influence2->weight[0];
+ float weight1 = influence2->weight[1];
+ const float* b4x40 = bones4x4[influence2->boneIndex[0]].m_Data;
+ const float* b4x41 = bones4x4[influence2->boneIndex[1]].m_Data;
+ // we need only 12 components of the matrix
+ poseBlended.m_Data[ 0] = b4x40[ 0] * weight0 + b4x41[ 0] * weight1;
+ poseBlended.m_Data[ 1] = b4x40[ 1] * weight0 + b4x41[ 1] * weight1;
+ poseBlended.m_Data[ 2] = b4x40[ 2] * weight0 + b4x41[ 2] * weight1;
+ poseBlended.m_Data[ 4] = b4x40[ 4] * weight0 + b4x41[ 4] * weight1;
+ poseBlended.m_Data[ 5] = b4x40[ 5] * weight0 + b4x41[ 5] * weight1;
+ poseBlended.m_Data[ 6] = b4x40[ 6] * weight0 + b4x41[ 6] * weight1;
+ poseBlended.m_Data[ 8] = b4x40[ 8] * weight0 + b4x41[ 8] * weight1;
+ poseBlended.m_Data[ 9] = b4x40[ 9] * weight0 + b4x41[ 9] * weight1;
+ poseBlended.m_Data[10] = b4x40[10] * weight0 + b4x41[10] * weight1;
+ poseBlended.m_Data[12] = b4x40[12] * weight0 + b4x41[12] * weight1;
+ poseBlended.m_Data[13] = b4x40[13] * weight0 + b4x41[13] * weight1;
+ poseBlended.m_Data[14] = b4x40[14] * weight0 + b4x41[14] * weight1;
+ poseToUse = &poseBlended;
+ }
+ else if (bonesPerVertexCount == 4)
+ {
+ float weight0 = influence4->weight[0];
+ float weight1 = influence4->weight[1];
+ float weight2 = influence4->weight[2];
+ float weight3 = influence4->weight[3];
+
+ const float* b4x40 = bones4x4[influence4->boneIndex[0]].m_Data;
+ const float* b4x41 = bones4x4[influence4->boneIndex[1]].m_Data;
+ const float* b4x42 = bones4x4[influence4->boneIndex[2]].m_Data;
+ const float* b4x43 = bones4x4[influence4->boneIndex[3]].m_Data;
+ // we need only 12 components of the matrix, so unroll
+ poseBlended.m_Data[ 0] = b4x40[ 0] * weight0 + b4x41[ 0] * weight1 + b4x42[ 0] * weight2 + b4x43[ 0] * weight3;
+ poseBlended.m_Data[ 1] = b4x40[ 1] * weight0 + b4x41[ 1] * weight1 + b4x42[ 1] * weight2 + b4x43[ 1] * weight3;
+ poseBlended.m_Data[ 2] = b4x40[ 2] * weight0 + b4x41[ 2] * weight1 + b4x42[ 2] * weight2 + b4x43[ 2] * weight3;
+ poseBlended.m_Data[ 4] = b4x40[ 4] * weight0 + b4x41[ 4] * weight1 + b4x42[ 4] * weight2 + b4x43[ 4] * weight3;
+ poseBlended.m_Data[ 5] = b4x40[ 5] * weight0 + b4x41[ 5] * weight1 + b4x42[ 5] * weight2 + b4x43[ 5] * weight3;
+ poseBlended.m_Data[ 6] = b4x40[ 6] * weight0 + b4x41[ 6] * weight1 + b4x42[ 6] * weight2 + b4x43[ 6] * weight3;
+ poseBlended.m_Data[ 8] = b4x40[ 8] * weight0 + b4x41[ 8] * weight1 + b4x42[ 8] * weight2 + b4x43[ 8] * weight3;
+ poseBlended.m_Data[ 9] = b4x40[ 9] * weight0 + b4x41[ 9] * weight1 + b4x42[ 9] * weight2 + b4x43[ 9] * weight3;
+ poseBlended.m_Data[10] = b4x40[10] * weight0 + b4x41[10] * weight1 + b4x42[10] * weight2 + b4x43[10] * weight3;
+ poseBlended.m_Data[12] = b4x40[12] * weight0 + b4x41[12] * weight1 + b4x42[12] * weight2 + b4x43[12] * weight3;
+ poseBlended.m_Data[13] = b4x40[13] * weight0 + b4x41[13] * weight1 + b4x42[13] * weight2 + b4x43[13] * weight3;
+ poseBlended.m_Data[14] = b4x40[14] * weight0 + b4x41[14] * weight1 + b4x42[14] * weight2 + b4x43[14] * weight3;
+ poseToUse = &poseBlended;
+ }
+
+ // skin components
+ Vector3f outVertex, outNormal, outTangent;
+ const Vector3f* vertex = reinterpret_cast<const Vector3f*>( inputVertex );
+ const Vector3f* normal = reinterpret_cast<const Vector3f*>( inputVertex + normalOffset );
+ const Vector3f* tangent = reinterpret_cast<const Vector3f*>( inputVertex + tangentOffset );
+ poseToUse->MultiplyPoint3( *vertex, outVertex );
+ if( skinNormal )
+ {
+ poseToUse->MultiplyVector3( *normal, outNormal );
+ if (transformInstruction == kNormalizeFastest)
+ {
+ float sqr1 = SqrMagnitude( outNormal );
+ float invsqrt1 = FastestInvSqrt (sqr1);
+ outNormal *= invsqrt1;
+ }
+ else if (transformInstruction == kNormalizeFast)
+ {
+ float sqr1 = SqrMagnitude( outNormal );
+ float invsqrt1 = FastInvSqrt (sqr1);
+ outNormal *= invsqrt1;
+ }
+ }
+ if( skinTangent )
+ {
+ poseToUse->MultiplyVector3( *tangent, outTangent );
+ if (transformInstruction == kNormalizeFastest)
+ {
+ float sqr1 = SqrMagnitude( outTangent );
+ float invsqrt1 = FastestInvSqrt (sqr1);
+ outTangent *= invsqrt1;
+ }
+ else if (transformInstruction == kNormalizeFast)
+ {
+ float sqr1 = SqrMagnitude( outTangent );
+ float invsqrt1 = FastInvSqrt (sqr1);
+ outTangent *= invsqrt1;
+ }
+ }
+
+ // write data out
+ *reinterpret_cast<Vector3f*> (outputVertex) = outVertex;
+ if( skinNormal )
+ {
+ *reinterpret_cast<Vector3f*>( outputVertex + normalOffset ) = outNormal;
+ }
+
+ if( skinTangent )
+ {
+ *reinterpret_cast<Vector3f*>( outputVertex + tangentOffset ) = outTangent;
+ *reinterpret_cast<float*>( outputVertex + tangentOffset + sizeof(Vector3f) ) = *reinterpret_cast<const float*>( inputVertex + tangentOffset + sizeof(Vector3f) );
+ }
+
+ outputVertex += outStride;
+ inputVertex += inStride;
+
+ if (bonesPerVertexCount == 1)
+ influence1++;
+ else if (bonesPerVertexCount == 2)
+ influence2++;
+ if (bonesPerVertexCount == 4)
+ influence4++;
+ }
+
+ //;;static int frameCount = 0; frameCount++;
+ //delta = mach_absolute_time() - delta;
+ //;;static uint64_t deltaAccum = 0; deltaAccum += (int)(delta);
+ //;;printf_console("skin-c: %d %d\n", (int)(deltaAccum / frameCount), (int)delta);
+}
+
+#endif
diff --git a/Runtime/Filters/Mesh/SpriteRenderer.cpp b/Runtime/Filters/Mesh/SpriteRenderer.cpp
new file mode 100644
index 0000000..4ce85a1
--- /dev/null
+++ b/Runtime/Filters/Mesh/SpriteRenderer.cpp
@@ -0,0 +1,338 @@
+#include "UnityPrefix.h"
+#include "SpriteRenderer.h"
+
+#if ENABLE_SPRITES
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Graphics/SpriteFrame.h"
+#include "Runtime/Graphics/Texture.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/Filters/Mesh/TransformVertex.h"
+#include "Runtime/GfxDevice/BatchRendering.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/BaseClasses/Tags.h"
+#include "SpriteRendererAnimationBinding.h"
+
+
+PROFILER_INFORMATION(gSpriteRenderSingleProfile, "SpriteRenderer.RenderSingle", kProfilerRender)
+PROFILER_INFORMATION(gSpriteRenderBatchProfile, "SpriteRenderer.RenderBatch", kProfilerRender)
+PROFILER_INFORMATION(gSpriteRenderSubmitVBO, "Mesh.SubmitVBO", kProfilerRender)
+
+const float kSpriteScaleEpsilon = 0.0001f;
+#define kMaxNumSpriteTrianglesPerBatch (2*1024)
+
+static const char* const kDefaultSpriteShader = "Sprites/Default";
+static const char* const kDefaultSpriteMaterial = "Sprites-Default.mat";
+
+static SHADERPROP (MainTex);
+static SHADERPROP (MainTex_TexelSize);
+static Material* gSpriteDefaultMaterial = NULL;
+
+static void InitDefaultSpriteMaterial()
+{
+ Assert(gSpriteDefaultMaterial == NULL);
+ gSpriteDefaultMaterial = GetBuiltinResource<Material>(kDefaultSpriteMaterial);
+}
+
+IMPLEMENT_CLASS_HAS_INIT (SpriteRenderer)
+IMPLEMENT_OBJECT_SERIALIZE (SpriteRenderer)
+
+SpriteRenderer::SpriteRenderer (MemLabelId label, ObjectCreationMode mode)
+: Super(kRendererSprite, label, mode)
+, m_Color(1.0F, 1.0F, 1.0F, 1.0F)
+{
+ m_CastShadows = false;
+ m_ReceiveShadows = false;
+}
+
+SpriteRenderer::~SpriteRenderer ()
+{
+}
+
+inline ColorRGBA32 GetDeviceColor (const ColorRGBAf& color, GfxDevice& device)
+{
+ if (GetActiveColorSpace () == kLinearColorSpace)
+ return device.ConvertToDeviceVertexColor(GammaToActiveColorSpace(color));
+ else
+ return device.ConvertToDeviceVertexColor(color);
+}
+
+void SpriteRenderer::InitializeClass ()
+{
+ REGISTER_GLOBAL_CALLBACK(initializedEngineGraphics, InitDefaultSpriteMaterial());
+ InitializeSpriteRendererAnimationBindingInterface();
+}
+
+void SpriteRenderer::CleanupClass ()
+{
+ CleanupSpriteRendererAnimationBindingInterface ();
+ gSpriteDefaultMaterial = NULL;
+}
+
+template<class TransferFunction>
+void SpriteRenderer::Transfer(TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER (m_Sprite);
+ TRANSFER (m_Color);
+}
+
+void SpriteRenderer::UpdateLocalAABB ()
+{
+ if (m_Sprite.IsValid())
+ {
+ //TODO: calculate AABB from RenderData.
+ m_TransformInfo.localAABB = m_Sprite->GetBounds();
+ }
+ else
+ {
+ m_TransformInfo.localAABB.SetCenterAndExtent(Vector3f::zero, Vector3f::zero);
+ }
+}
+
+void SpriteRenderer::UpdateTransformInfo ()
+{
+ Transform const& transform = GetTransform();
+ if (m_TransformDirty)
+ {
+ // will return a cached matrix most of the time
+ TransformType type = transform.CalculateTransformMatrix (m_TransformInfo.worldMatrix);
+
+ // Always treat sprites has having a non-uniform scale. Will make them batch better
+ // (since we break batches on transform type changes). And does not have any negative effects
+ // since uniform vs. non-uniform scale only affects fixed function vertex normals, which
+ // aren't relevant here.
+ type &= ~kUniformScaleTransform;
+ type |= kNonUniformScaleTransform;
+ m_TransformInfo.transformType = type;
+
+ // Likewise, treat inverse scale as always being 1.
+ m_TransformInfo.invScale = 1.0f;
+ }
+
+ if (m_BoundsDirty)
+ UpdateLocalAABB();
+
+ TransformAABBSlow(m_TransformInfo.localAABB, m_TransformInfo.worldMatrix, m_TransformInfo.worldAABB);
+}
+
+void SpriteRenderer::SetSprite(PPtr<Sprite> sprite)
+{
+ if (m_Sprite != sprite)
+ {
+ m_Sprite = sprite;
+ BoundsChanged();
+ SetupMaterialProperties();
+
+ SetDirty();
+ }
+}
+
+void SpriteRenderer::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad(awakeMode);
+ BoundsChanged();
+ SetupMaterialProperties();
+}
+
+void SpriteRenderer::SmartReset ()
+{
+ SetMaterialCount(1);
+ SetMaterial(GetDefaultSpriteMaterial(), 0);
+}
+
+void SpriteRenderer::SetupMaterialProperties()
+{
+ if (m_Sprite.IsNull())
+ return;
+
+ // Patch sprite texture and apply material property block
+ MaterialPropertyBlock& block = GetPropertyBlockRememberToUpdateHash ();
+ SetupMaterialPropertyBlock(block, GetSpriteRenderDataInContext(m_Sprite)->texture);
+ ComputeCustomPropertiesHash ();
+}
+
+void SpriteRenderer::SetupMaterialPropertyBlock(MaterialPropertyBlock& block, const Texture2D* spriteTexture)
+{
+ const TextureID id = spriteTexture ? spriteTexture->GetTextureID() : TextureID(0);
+ const Vector4f texelSize = spriteTexture ? Vector4f(spriteTexture->GetTexelSizeX(), spriteTexture->GetTexelSizeY(), spriteTexture->GetGLWidth(), spriteTexture->GetGLHeight()) : Vector4f(0, 0, 0, 0);
+
+ block.ReplacePropertyTexture(kSLPropMainTex, kTexDim2D, id);
+ block.ReplacePropertyVector(kSLPropMainTex_TexelSize, texelSize);
+}
+
+const SpriteRenderData* SpriteRenderer::GetSpriteRenderDataInContext(const PPtr<Sprite>& frame)
+{
+ //@Note: this is here for a possible contextual atlas implementation.
+ return &frame->GetRenderDataForPlayMode();
+}
+
+void SpriteRenderer::Render (int materialIndex, const ChannelAssigns& channels)
+{
+ GfxDevice& device = GetGfxDevice();
+
+ Assert(materialIndex == 0);
+ if (m_Sprite.IsNull())
+ return;
+
+ const SpriteRenderData* rd = GetSpriteRenderDataInContext(m_Sprite);
+ Assert(rd->texture.IsValid());
+
+ PROFILER_AUTO_GFX(gSpriteRenderSingleProfile, this);
+
+ // Get VBO chunk for a rectangle or mesh
+ UInt32 numIndices, numVertices;
+ GetGeometrySize(numIndices, numVertices);
+ if (!numIndices)
+ return;
+
+ const UInt32 channelMask = (1<<kShaderChannelVertex) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelColor);
+
+ DynamicVBO& vbo = device.GetDynamicVBO();
+ UInt8* __restrict vbPtr;
+ UInt16* __restrict ibPtr;
+ if ( !vbo.GetChunk(channelMask, numVertices, numIndices, DynamicVBO::kDrawIndexedTriangles, (void**)&vbPtr, (void**)&ibPtr) )
+ return;
+
+ TransformSprite (vbPtr, ibPtr, NULL, rd, GetDeviceColor (m_Color, device), 0);
+ vbo.ReleaseChunk(numVertices, numIndices);
+
+ // Draw
+ if (m_CustomProperties)
+ device.SetMaterialProperties(*m_CustomProperties);
+
+ PROFILER_BEGIN(gSpriteRenderSubmitVBO, this)
+ vbo.DrawChunk(channels);
+ GPU_TIMESTAMP();
+ PROFILER_END
+}
+
+void SpriteRenderer::GetGeometrySize(UInt32& indexCount, UInt32& vertexCount)
+{
+ if (m_Sprite.IsValid())
+ {
+ const SpriteRenderData* rd = GetSpriteRenderDataInContext(m_Sprite);
+ if (rd->indices.size() > 0)
+ {
+ indexCount = rd->indices.size();
+ vertexCount = rd->vertices.size();
+ return;
+ }
+ }
+
+ indexCount = 0;
+ vertexCount = 0;
+}
+
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+void SpriteRenderer::RenderBatch (const BatchInstanceData* instances, size_t count, size_t numIndices, size_t numVertices, const ChannelAssigns& channels)
+{
+ DebugAssert(numIndices);
+ DebugAssert(numVertices);
+ PROFILER_AUTO_GFX(gSpriteRenderBatchProfile, 0);
+
+ GfxDevice& device = GetGfxDevice();
+ const MaterialPropertyBlock* customProps = count > 0 ? instances[0].renderer->GetCustomProperties() : NULL;
+ if (customProps)
+ device.SetMaterialProperties (*customProps);
+
+ UInt32 expectedFence = device.GetNextCPUFence();
+ const UInt32 channelMask = (1<<kShaderChannelVertex) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelColor);;
+ device.BeginDynamicBatching(channels, channelMask, numVertices, numIndices, kPrimitiveTriangles);
+
+ for (BatchInstanceData const* it = instances; it < instances + count; ++it)
+ {
+ UInt32 numIndices, numVertices;
+
+ Assert(it->renderer);
+ Assert(it->renderer->GetRendererType() == kRendererSprite);
+ SpriteRenderer* renderer = (SpriteRenderer*)it->renderer;
+ renderer->GetGeometrySize(numIndices, numVertices);
+ if (!numIndices)
+ continue;
+
+ const SpriteRenderData *rd = renderer->GetSpriteRenderDataInContext(renderer->m_Sprite);
+ Assert(rd->texture.IsValid());
+
+#if ENABLE_MULTITHREADED_CODE
+ renderer->m_Sprite->SetCurrentCPUFence(expectedFence);
+#endif
+ device.DynamicBatchSprite(&it->xform, rd, GetDeviceColor(renderer->m_Color, device));
+ }
+ device.SetInverseScale(1.0f);
+ device.EndDynamicBatching(TransformType(kNoScaleTransform));
+
+ // Insert fence after batching is complete
+ UInt32 fence = device.InsertCPUFence();
+ Assert(fence == expectedFence);
+ GPU_TIMESTAMP();
+}
+
+void SpriteRenderer::RenderMultiple (const BatchInstanceData* instances, size_t count, const ChannelAssigns& channels)
+{
+ size_t numIndicesBatch = 0;
+ size_t numVerticesBatch = 0;
+
+ BatchInstanceData const* instancesEnd = instances + count;
+ BatchInstanceData const* iBatchBegin = instances;
+ BatchInstanceData const* iBatchEnd = instances;
+ while (iBatchEnd != instancesEnd)
+ {
+ Assert(iBatchEnd->renderer->GetRendererType() == kRendererSprite);
+ SpriteRenderer* renderer = (SpriteRenderer*)iBatchEnd->renderer;
+
+ if (renderer->GetSprite().IsNull())
+ {
+ iBatchEnd++;
+ continue;
+ }
+
+ UInt32 numIndices, numVertices;
+ renderer->GetGeometrySize(numIndices, numVertices);
+
+ if ((numIndicesBatch + numIndices) <= kMaxNumSpriteTrianglesPerBatch)
+ {
+ numIndicesBatch += numIndices;
+ numVerticesBatch += numVertices;
+ iBatchEnd++;
+ }
+ else
+ {
+ if (numIndicesBatch)
+ {
+ RenderBatch(iBatchBegin, iBatchEnd - iBatchBegin, numIndicesBatch, numVerticesBatch, channels);
+ numIndicesBatch = 0;
+ numVerticesBatch = 0;
+ iBatchBegin = iBatchEnd;
+ }
+ else // Can't fit in one draw call
+ {
+ RenderBatch(iBatchEnd, 1, numIndices, numVertices, channels);
+ iBatchEnd++;
+ iBatchBegin = iBatchEnd;
+ }
+ }
+ }
+
+ if ((iBatchBegin != iBatchEnd) && numIndicesBatch)
+ {
+ RenderBatch(iBatchBegin, iBatchEnd - iBatchBegin, numIndicesBatch, numVerticesBatch, channels);
+ }
+}
+#endif
+
+Material* SpriteRenderer::GetDefaultSpriteMaterial ()
+{
+ Assert(gSpriteDefaultMaterial);
+ return gSpriteDefaultMaterial;
+}
+
+#endif // ENABLE_SPRITES
diff --git a/Runtime/Filters/Mesh/SpriteRenderer.h b/Runtime/Filters/Mesh/SpriteRenderer.h
new file mode 100644
index 0000000..0bf47b9
--- /dev/null
+++ b/Runtime/Filters/Mesh/SpriteRenderer.h
@@ -0,0 +1,60 @@
+#ifndef SPRITERENDERER_H
+#define SPRITERENDERER_H
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_SPRITES
+
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Graphics/SpriteFrame.h"
+
+class SpriteRenderer : public Renderer
+{
+public:
+ REGISTER_DERIVED_CLASS (SpriteRenderer, Renderer)
+ DECLARE_OBJECT_SERIALIZE (SpriteRenderer)
+
+ SpriteRenderer (MemLabelId label, ObjectCreationMode mode);
+ // ~SpriteRenderer (); declared-by-macro
+
+ static bool IsSealedClass () { return true; }
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+ virtual void SmartReset ();
+
+ virtual void UpdateTransformInfo();
+ virtual void UpdateLocalAABB ();
+ virtual void Render (int materialIndex, const ChannelAssigns& channels);
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+ static void RenderMultiple (const BatchInstanceData* instances, size_t count, const ChannelAssigns& channels);
+#endif
+ PPtr<Sprite> GetSprite() const { return m_Sprite; }
+ void SetSprite(PPtr<Sprite> sprite);
+
+ ColorRGBAf GetColor() const { return m_Color; }
+ void SetColor(const ColorRGBAf& color) { m_Color = color; }
+
+ static void SetupMaterialPropertyBlock(MaterialPropertyBlock& block, const Texture2D* spriteTexture);
+
+ static Material* GetDefaultSpriteMaterial();
+
+private:
+ PPtr<Sprite> m_Sprite;
+ ColorRGBAf m_Color;
+
+ void SetupMaterialProperties();
+ void GetGeometrySize(UInt32& indexCount, UInt32& vertexCount);
+
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+ static void RenderBatch (const BatchInstanceData* instances, size_t count, size_t numIndices, size_t numVertices, const ChannelAssigns& channels);
+#endif
+ // Context
+ const SpriteRenderData* GetSpriteRenderDataInContext(const PPtr<Sprite>& frame);
+};
+
+#endif //ENABLE_SPRITES
+
+#endif
diff --git a/Runtime/Filters/Mesh/SpriteRendererAnimationBinding.cpp b/Runtime/Filters/Mesh/SpriteRendererAnimationBinding.cpp
new file mode 100644
index 0000000..a36406f
--- /dev/null
+++ b/Runtime/Filters/Mesh/SpriteRendererAnimationBinding.cpp
@@ -0,0 +1,68 @@
+#include "UnityPrefix.h"
+#include "Runtime/Animation/GenericAnimationBindingCache.h"
+#include "Runtime/Animation/AnimationClipBindings.h"
+#include "SpriteRenderer.h"
+#include "Runtime/Interfaces/IAnimationBinding.h"
+
+#if ENABLE_SPRITES
+
+static const char* kSpriteFrame = "m_Sprite";
+
+class SpriteRendererAnimationBinding : public IAnimationBinding
+{
+public:
+
+#if UNITY_EDITOR
+ virtual void GetAllAnimatableProperties (Object& targetObject, std::vector<EditorCurveBinding>& outProperties) const
+ {
+ AddPPtrBinding (outProperties, ClassID(SpriteRenderer), kSpriteFrame);
+ }
+#endif
+
+ virtual float GetFloatValue (const UnityEngine::Animation::BoundCurve& bind) const { return 0.0F; }
+ virtual void SetFloatValue (const UnityEngine::Animation::BoundCurve& bind, float value) const { }
+
+ virtual void SetPPtrValue (const UnityEngine::Animation::BoundCurve& bound, SInt32 value) const
+ {
+ SpriteRenderer* renderer = reinterpret_cast<SpriteRenderer*>(bound.targetObject);
+ renderer->SetSprite(PPtr<Sprite> (value));
+ }
+
+ virtual SInt32 GetPPtrValue (const UnityEngine::Animation::BoundCurve& bound) const
+ {
+ SpriteRenderer* renderer = reinterpret_cast<SpriteRenderer*>(bound.targetObject);
+ return renderer->GetSprite().GetInstanceID();
+ }
+
+ virtual bool GenerateBinding (const UnityStr& attribute, bool pptrCurve, UnityEngine::Animation::GenericBinding& outputBinding) const
+ {
+ if (attribute == kSpriteFrame && pptrCurve)
+ {
+ outputBinding.attribute = 0;
+ return true;
+ }
+
+ return false;
+ }
+
+ virtual ClassIDType BindValue (Object& target, const UnityEngine::Animation::GenericBinding& inputBinding, UnityEngine::Animation::BoundCurve& bound) const
+ {
+ return ClassID(Sprite);
+ }
+};
+
+static SpriteRendererAnimationBinding* gSpriteRendererBinding = NULL;
+
+void InitializeSpriteRendererAnimationBindingInterface ()
+{
+ Assert(gSpriteRendererBinding == NULL);
+ gSpriteRendererBinding = UNITY_NEW (SpriteRendererAnimationBinding, kMemAnimation);
+ UnityEngine::Animation::GetGenericAnimationBindingCache ().RegisterIAnimationBinding (ClassID(SpriteRenderer), UnityEngine::Animation::kSpriteRendererPPtrBinding, gSpriteRendererBinding);
+}
+
+void CleanupSpriteRendererAnimationBindingInterface ()
+{
+ UNITY_DELETE (gSpriteRendererBinding, kMemAnimation);
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/Filters/Mesh/SpriteRendererAnimationBinding.h b/Runtime/Filters/Mesh/SpriteRendererAnimationBinding.h
new file mode 100644
index 0000000..63e2731
--- /dev/null
+++ b/Runtime/Filters/Mesh/SpriteRendererAnimationBinding.h
@@ -0,0 +1,2 @@
+void InitializeSpriteRendererAnimationBindingInterface ();
+void CleanupSpriteRendererAnimationBindingInterface (); \ No newline at end of file
diff --git a/Runtime/Filters/Mesh/TransformVertex.cpp b/Runtime/Filters/Mesh/TransformVertex.cpp
new file mode 100644
index 0000000..e9bebc1
--- /dev/null
+++ b/Runtime/Filters/Mesh/TransformVertex.cpp
@@ -0,0 +1,205 @@
+#include "UnityPrefix.h"
+#include "TransformVertex.h"
+
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Vector4.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Color.h"
+
+#include "Runtime/Misc/CPUInfo.h"
+
+void
+TransformVerticesStridedREF( StrideIterator<Vector3f> inPos, StrideIterator<Vector3f> inNormal,
+ StrideIterator<ColorRGBA32> inColor, StrideIterator<Vector2f> inTexCoord0, StrideIterator<Vector2f> inTexCoord1,
+ StrideIterator<Vector4f> inTangent,
+ UInt8* dstData, const Matrix4x4f& m, unsigned vertexCount, bool multiStream )
+{
+ // NOTE: kill this code once all shaders normalize normals & tangents!
+ //
+ // We batch uniformly scaled objects, so derive the "normal matrix" here by scaling world matrix axes.
+ // On reference code seems much cheaper than full normalization of normal/tangent vectors.
+ // Test with scene of 200k vertices on Core i7 2600K: no handling of scale 3.77ms, normalization 8.00ms,
+ // using scaled normal matrix 3.80ms.
+ //
+ // Note that ARM NEON/VFP transformation code does not handle this, but it's not needed on GLES platforms
+ // since shaders always normalize normal & tangent. Might be needed on WinRT+ARM though (or just disable
+ // dynamic batching with tangents there).
+ Matrix4x4f nm;
+ CopyMatrix(m.GetPtr(), nm.GetPtr());
+ const float axisLen = Magnitude (m.GetAxisX());
+ float scale = axisLen > 1.0e-6f ? 1.0f / axisLen : 1.0f;
+ nm.Get (0, 0) *= scale;
+ nm.Get (1, 0) *= scale;
+ nm.Get (2, 0) *= scale;
+ nm.Get (0, 1) *= scale;
+ nm.Get (1, 1) *= scale;
+ nm.Get (2, 1) *= scale;
+ nm.Get (0, 2) *= scale;
+ nm.Get (1, 2) *= scale;
+ nm.Get (2, 2) *= scale;
+
+ while (vertexCount --> 0)
+ {
+ Vector3f* outPos = reinterpret_cast<Vector3f*> (dstData);
+ m.MultiplyPoint3(*inPos, *outPos);
+ dstData += sizeof(Vector3f);
+ ++inPos;
+
+ if (inNormal.GetPointer())
+ {
+ Vector3f* outNormal = reinterpret_cast<Vector3f*> (dstData);
+ nm.MultiplyVector3(*inNormal, *outNormal);
+ dstData += sizeof(Vector3f);
+ ++inNormal;
+ }
+
+ if (inColor.GetPointer())
+ {
+ memcpy(dstData, inColor.GetPointer(), sizeof(ColorRGBA32));
+ dstData += sizeof(ColorRGBA32);
+ ++inColor;
+ }
+
+ if (inTexCoord0.GetPointer())
+ {
+ memcpy(dstData, inTexCoord0.GetPointer(), sizeof(Vector2f));
+ dstData += sizeof(Vector2f);
+ ++inTexCoord0;
+ }
+
+ if (inTexCoord1.GetPointer())
+ {
+ memcpy(dstData, inTexCoord1.GetPointer(), sizeof(Vector2f));
+ dstData += sizeof(Vector2f);
+ ++inTexCoord1;
+ }
+
+ if (inTangent.GetPointer())
+ {
+ Vector4f* outTangent = reinterpret_cast<Vector4f*> (dstData);
+ Vector3f* outTangentXYZ = reinterpret_cast<Vector3f*> (outTangent);
+ nm.MultiplyVector3(reinterpret_cast<const Vector3f&>(*inTangent), *outTangentXYZ);
+ outTangent->w = inTangent->w;
+ dstData += sizeof(Vector4f);
+ ++inTangent;
+ }
+ }
+}
+
+
+
+#if (UNITY_SUPPORTS_NEON && !UNITY_DISABLE_NEON_SKINNING) || UNITY_SUPPORTS_VFP
+
+typedef void (*TransformFunc)( const void*, const void*, const void*, const float*, void*, int );
+typedef void (*TransformFuncWithTangents)( const void*, const void*, const void*, const float*, void*, int, const void* );
+
+
+#if UNITY_SUPPORTS_NEON
+namespace TransformNEON
+{
+ #define TRANSFORM_FUNC(prefix, addData) s_TransformVertices_Strided_##prefix##_##addData##_NEON
+
+ TransformFunc TransformXYZ[] =
+ {
+ TRANSFORM_FUNC(XYZ,0), TRANSFORM_FUNC(XYZ,1), TRANSFORM_FUNC(XYZ,2), TRANSFORM_FUNC(XYZ,3), TRANSFORM_FUNC(XYZ,4), TRANSFORM_FUNC(XYZ,5)
+ };
+
+ TransformFunc TransformXYZN[] =
+ {
+ TRANSFORM_FUNC(XYZN,0), TRANSFORM_FUNC(XYZN,1), TRANSFORM_FUNC(XYZN,2), TRANSFORM_FUNC(XYZN,3), TRANSFORM_FUNC(XYZN,4), TRANSFORM_FUNC(XYZN,5)
+ };
+
+ TransformFuncWithTangents TransformXYZNT[] =
+ {
+ TRANSFORM_FUNC(XYZNT,0), TRANSFORM_FUNC(XYZNT,1), TRANSFORM_FUNC(XYZNT,2), TRANSFORM_FUNC(XYZNT,3), TRANSFORM_FUNC(XYZNT,4), TRANSFORM_FUNC(XYZNT,5)
+ };
+
+ #undef TRANSFORM_FUNC
+}
+#endif // UNITY_SUPPORTS_NEON
+
+
+#if UNITY_SUPPORTS_VFP
+namespace TransformVFP
+{
+ #define TRANSFORM_FUNC(prefix, addData) s_TransformVertices_Strided_##prefix##_##addData##_VFP
+
+ TransformFunc TransformXYZ[] =
+ {
+ TRANSFORM_FUNC(XYZ,0), TRANSFORM_FUNC(XYZ,1), TRANSFORM_FUNC(XYZ,2), TRANSFORM_FUNC(XYZ,3), TRANSFORM_FUNC(XYZ,4), TRANSFORM_FUNC(XYZ,5)
+ };
+
+ TransformFunc TransformXYZN[] =
+ {
+ TRANSFORM_FUNC(XYZN,0), TRANSFORM_FUNC(XYZN,1), TRANSFORM_FUNC(XYZN,2), TRANSFORM_FUNC(XYZN,3), TRANSFORM_FUNC(XYZN,4), TRANSFORM_FUNC(XYZN,5)
+ };
+
+ TransformFuncWithTangents TransformXYZNT[] =
+ {
+ TRANSFORM_FUNC(XYZNT,0), TRANSFORM_FUNC(XYZNT,1), TRANSFORM_FUNC(XYZNT,2), TRANSFORM_FUNC(XYZNT,3), TRANSFORM_FUNC(XYZNT,4), TRANSFORM_FUNC(XYZNT,5)
+ };
+
+ #undef TRANSFORM_FUNC
+}
+#endif // UNITY_SUPPORTS_VFP
+
+void
+TransformVerticesStridedARM( StrideIterator<Vector3f> inPos, StrideIterator<Vector3f> inNormal,
+ StrideIterator<ColorRGBA32> inColor, StrideIterator<Vector2f> inTexCoord0, StrideIterator<Vector2f> inTexCoord1,
+ StrideIterator<Vector4f> inTangent,
+ UInt8* dstData, const Matrix4x4f& m, unsigned vertexCount, bool multiStream )
+{
+ int addDataSize = 0;
+ if( inColor.GetPointer() ) addDataSize += 1;
+ if( inTexCoord0.GetPointer() ) addDataSize += 2;
+ if( inTexCoord1.GetPointer() ) addDataSize += 2;
+
+ const void* addDataSrc = 0;
+ if( inColor.GetPointer() ) addDataSrc = inColor.GetPointer();
+ else if( inTexCoord0.GetPointer() ) addDataSrc = inTexCoord0.GetPointer();
+ else if( inTexCoord1.GetPointer() ) addDataSrc = inTexCoord1.GetPointer();
+
+ // slow path determination
+ if( (inColor.GetPointer() && inTexCoord1.GetPointer() && !inTexCoord0.GetPointer())
+ || (inTangent.GetPointer() && !inNormal.GetPointer()) || multiStream )
+ {
+ TransformVerticesStridedREF(inPos, inNormal, inColor, inTexCoord0, inTexCoord1, inTangent, dstData, m, vertexCount, multiStream);
+ return;
+ }
+
+ int stride = inPos.GetStride();
+ const UInt8* inDataBegin = static_cast<const UInt8*>(inPos.GetPointer());
+ const UInt8* inDataEnd = inDataBegin + vertexCount * stride;
+
+#if UNITY_SUPPORTS_NEON
+ if (CPUInfo::HasNEONSupport())
+ {
+ using namespace TransformNEON;
+ if( inNormal.GetPointer() && inTangent.GetPointer() )
+ TransformXYZNT[addDataSize]( inDataBegin, inDataEnd, addDataSrc, m.m_Data, dstData, stride, inTangent.GetPointer() );
+ else if( inNormal.GetPointer() )
+ TransformXYZN[addDataSize]( inDataBegin, inDataEnd, addDataSrc, m.m_Data, dstData, stride );
+ else
+ TransformXYZ[addDataSize]( inDataBegin, inDataEnd, addDataSrc, m.m_Data, dstData, stride );
+ }
+ else
+#endif
+#if UNITY_SUPPORTS_VFP
+ {
+ using namespace TransformVFP;
+ if( inNormal.GetPointer() && inTangent.GetPointer() )
+ TransformXYZNT[addDataSize]( inDataBegin, inDataEnd, addDataSrc, m.m_Data, dstData, stride, inTangent.GetPointer() );
+ else if( inNormal.GetPointer() )
+ TransformXYZN[addDataSize]( inDataBegin, inDataEnd, addDataSrc, m.m_Data, dstData, stride );
+ else
+ TransformXYZ[addDataSize]( inDataBegin, inDataEnd, addDataSrc, m.m_Data, dstData, stride );
+ }
+#else
+ {
+ ErrorString("non-NEON path not enabled!");
+ }
+#endif
+}
+#endif
+
diff --git a/Runtime/Filters/Mesh/TransformVertex.h b/Runtime/Filters/Mesh/TransformVertex.h
new file mode 100644
index 0000000..fe7aa77
--- /dev/null
+++ b/Runtime/Filters/Mesh/TransformVertex.h
@@ -0,0 +1,175 @@
+#ifndef TRANSFORM_VERTEX_H_
+#define TRANSFORM_VERTEX_H_
+
+#include "Configuration/PrefixConfigure.h"
+#include "Runtime/Utilities/StrideIterator.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Vector4.h"
+#include "Runtime/Math/Color.h"
+
+class Matrix4x4f;
+
+
+//==============================================================================
+
+#define DECL_TRANSFORM_VERTICES_STRIDED(code, num, postfix) \
+ void s_TransformVertices_Strided_##code##_##num##_##postfix( const void* srcData, const void* srcDataEnd, const void* addData, \
+ const float* xform, void* outData, int stride \
+ );
+
+#define DECL_TRANSFORM_VERTICES_STRIDED_TANGENTS(num, postfix) \
+ void s_TransformVertices_Strided_XYZNT_##num##_##postfix( const void* srcData, const void* srcDataEnd, const void* addData, \
+ const float* xform, void* outData, int stride, const void* srcTangent \
+ );
+
+
+#if UNITY_SUPPORTS_NEON && !UNITY_DISABLE_NEON_SKINNING
+
+extern "C"
+{
+#if UNITY_ANDROID || UNITY_WINRT || UNITY_BB10 || UNITY_TIZEN
+ #define s_TransformVertices_Strided_XYZ_0_NEON _s_TransformVertices_Strided_XYZ_0_NEON
+ #define s_TransformVertices_Strided_XYZ_1_NEON _s_TransformVertices_Strided_XYZ_1_NEON
+ #define s_TransformVertices_Strided_XYZ_2_NEON _s_TransformVertices_Strided_XYZ_2_NEON
+ #define s_TransformVertices_Strided_XYZ_3_NEON _s_TransformVertices_Strided_XYZ_3_NEON
+ #define s_TransformVertices_Strided_XYZ_4_NEON _s_TransformVertices_Strided_XYZ_4_NEON
+ #define s_TransformVertices_Strided_XYZ_5_NEON _s_TransformVertices_Strided_XYZ_5_NEON
+
+ #define s_TransformVertices_Strided_XYZN_0_NEON _s_TransformVertices_Strided_XYZN_0_NEON
+ #define s_TransformVertices_Strided_XYZN_1_NEON _s_TransformVertices_Strided_XYZN_1_NEON
+ #define s_TransformVertices_Strided_XYZN_2_NEON _s_TransformVertices_Strided_XYZN_2_NEON
+ #define s_TransformVertices_Strided_XYZN_3_NEON _s_TransformVertices_Strided_XYZN_3_NEON
+ #define s_TransformVertices_Strided_XYZN_4_NEON _s_TransformVertices_Strided_XYZN_4_NEON
+ #define s_TransformVertices_Strided_XYZN_5_NEON _s_TransformVertices_Strided_XYZN_5_NEON
+
+ #define s_TransformVertices_Strided_XYZNT_0_NEON _s_TransformVertices_Strided_XYZNT_0_NEON
+ #define s_TransformVertices_Strided_XYZNT_1_NEON _s_TransformVertices_Strided_XYZNT_1_NEON
+ #define s_TransformVertices_Strided_XYZNT_2_NEON _s_TransformVertices_Strided_XYZNT_2_NEON
+ #define s_TransformVertices_Strided_XYZNT_3_NEON _s_TransformVertices_Strided_XYZNT_3_NEON
+ #define s_TransformVertices_Strided_XYZNT_4_NEON _s_TransformVertices_Strided_XYZNT_4_NEON
+ #define s_TransformVertices_Strided_XYZNT_5_NEON _s_TransformVertices_Strided_XYZNT_5_NEON
+#if ENABLE_SPRITES
+#define s_TransformVertices_Sprite_NEON _s_TransformVertices_Sprite_NEON
+#endif
+
+#endif // UNITY_ANDROID || UNITY_WINRT || UNITY_BB10 || UNITY_TIZEN
+
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZ,0,NEON);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZ,1,NEON);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZ,2,NEON);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZ,3,NEON);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZ,4,NEON);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZ,5,NEON);
+
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZN,0,NEON);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZN,1,NEON);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZN,2,NEON);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZN,3,NEON);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZN,4,NEON);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZN,5,NEON);
+
+ DECL_TRANSFORM_VERTICES_STRIDED_TANGENTS(0,NEON);
+ DECL_TRANSFORM_VERTICES_STRIDED_TANGENTS(1,NEON);
+ DECL_TRANSFORM_VERTICES_STRIDED_TANGENTS(2,NEON);
+ DECL_TRANSFORM_VERTICES_STRIDED_TANGENTS(3,NEON);
+ DECL_TRANSFORM_VERTICES_STRIDED_TANGENTS(4,NEON);
+ DECL_TRANSFORM_VERTICES_STRIDED_TANGENTS(5,NEON);
+#if ENABLE_SPRITES
+ void s_TransformVertices_Sprite_NEON(const void* srcData, const void* srcDataEnd, const void* addData, const float* xform, void* outData, int stride, unsigned int color);
+#endif
+}
+
+#endif
+
+
+#if UNITY_SUPPORTS_VFP
+
+extern "C"
+{
+#if UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+ #define s_TransformVertices_Strided_XYZ_0_VFP _s_TransformVertices_Strided_XYZ_0_VFP
+ #define s_TransformVertices_Strided_XYZ_1_VFP _s_TransformVertices_Strided_XYZ_1_VFP
+ #define s_TransformVertices_Strided_XYZ_2_VFP _s_TransformVertices_Strided_XYZ_2_VFP
+ #define s_TransformVertices_Strided_XYZ_3_VFP _s_TransformVertices_Strided_XYZ_3_VFP
+ #define s_TransformVertices_Strided_XYZ_4_VFP _s_TransformVertices_Strided_XYZ_4_VFP
+ #define s_TransformVertices_Strided_XYZ_5_VFP _s_TransformVertices_Strided_XYZ_5_VFP
+
+ #define s_TransformVertices_Strided_XYZN_0_VFP _s_TransformVertices_Strided_XYZN_0_VFP
+ #define s_TransformVertices_Strided_XYZN_1_VFP _s_TransformVertices_Strided_XYZN_1_VFP
+ #define s_TransformVertices_Strided_XYZN_2_VFP _s_TransformVertices_Strided_XYZN_2_VFP
+ #define s_TransformVertices_Strided_XYZN_3_VFP _s_TransformVertices_Strided_XYZN_3_VFP
+ #define s_TransformVertices_Strided_XYZN_4_VFP _s_TransformVertices_Strided_XYZN_4_VFP
+ #define s_TransformVertices_Strided_XYZN_5_VFP _s_TransformVertices_Strided_XYZN_5_VFP
+
+ #define s_TransformVertices_Strided_XYZNT_0_VFP _s_TransformVertices_Strided_XYZNT_0_VFP
+ #define s_TransformVertices_Strided_XYZNT_1_VFP _s_TransformVertices_Strided_XYZNT_1_VFP
+ #define s_TransformVertices_Strided_XYZNT_2_VFP _s_TransformVertices_Strided_XYZNT_2_VFP
+ #define s_TransformVertices_Strided_XYZNT_3_VFP _s_TransformVertices_Strided_XYZNT_3_VFP
+ #define s_TransformVertices_Strided_XYZNT_4_VFP _s_TransformVertices_Strided_XYZNT_4_VFP
+ #define s_TransformVertices_Strided_XYZNT_5_VFP _s_TransformVertices_Strided_XYZNT_5_VFP
+#if ENABLE_SPRITES
+ #define s_TransformVertices_Sprite_VFP _s_TransformVertices_Sprite_VFP
+#endif
+#endif // UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+
+
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZ,0,VFP);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZ,1,VFP);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZ,2,VFP);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZ,3,VFP);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZ,4,VFP);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZ,5,VFP);
+
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZN,0,VFP);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZN,1,VFP);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZN,2,VFP);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZN,3,VFP);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZN,4,VFP);
+ DECL_TRANSFORM_VERTICES_STRIDED(XYZN,5,VFP);
+
+ DECL_TRANSFORM_VERTICES_STRIDED_TANGENTS(0,VFP);
+ DECL_TRANSFORM_VERTICES_STRIDED_TANGENTS(1,VFP);
+ DECL_TRANSFORM_VERTICES_STRIDED_TANGENTS(2,VFP);
+ DECL_TRANSFORM_VERTICES_STRIDED_TANGENTS(3,VFP);
+ DECL_TRANSFORM_VERTICES_STRIDED_TANGENTS(4,VFP);
+ DECL_TRANSFORM_VERTICES_STRIDED_TANGENTS(5,VFP);
+#if ENABLE_SPRITES
+ void s_TransformVertices_Sprite_VFP (const void* srcData, const void* srcDataEnd, const void* addData, const float* xform, void* outData, int stride, unsigned int color);
+#endif
+}
+
+#endif
+
+
+#undef DECL_TRANSFORM_VERTICES_STRIDED_TANGENTS
+#undef DECL_TRANSFORM_VERTICES_STRIDED
+
+
+//==============================================================================
+
+void
+TransformVerticesStridedREF( StrideIterator<Vector3f> inPos, StrideIterator<Vector3f> inNormal,
+ StrideIterator<ColorRGBA32> inColor, StrideIterator<Vector2f> inTexCoord0, StrideIterator<Vector2f> inTexCoord1,
+ StrideIterator<Vector4f> inTangent,
+ UInt8* dstData, const Matrix4x4f& m, unsigned vertexCount, bool multiStream );
+
+#if (UNITY_SUPPORTS_NEON && !UNITY_DISABLE_NEON_SKINNING) || UNITY_SUPPORTS_VFP
+void
+TransformVerticesStridedARM( StrideIterator<Vector3f> inPos, StrideIterator<Vector3f> inNormal,
+ StrideIterator<ColorRGBA32> inColor, StrideIterator<Vector2f> inTexCoord0, StrideIterator<Vector2f> inTexCoord1,
+ StrideIterator<Vector4f> inTangent,
+ UInt8* dstData, const Matrix4x4f& m, unsigned vertexCount, bool multiStream );
+#endif
+
+
+#if (UNITY_SUPPORTS_NEON && !UNITY_DISABLE_NEON_SKINNING) || UNITY_SUPPORTS_VFP
+ #define TransformVerticesStrided TransformVerticesStridedARM
+#else
+ #define TransformVerticesStrided TransformVerticesStridedREF
+#endif
+
+
+//==============================================================================
+
+#endif // TRANSFORM_VERTEX_H_
diff --git a/Runtime/Filters/Mesh/TransformVertexNEON.asm b/Runtime/Filters/Mesh/TransformVertexNEON.asm
new file mode 100644
index 0000000..7db462b
--- /dev/null
+++ b/Runtime/Filters/Mesh/TransformVertexNEON.asm
@@ -0,0 +1,694 @@
+ AREA .text, CODE
+
+ EXPORT _s_TransformVertices_Strided_XYZ_0_NEON
+ EXPORT _s_TransformVertices_Strided_XYZ_1_NEON
+ EXPORT _s_TransformVertices_Strided_XYZ_2_NEON
+ EXPORT _s_TransformVertices_Strided_XYZ_3_NEON
+ EXPORT _s_TransformVertices_Strided_XYZ_4_NEON
+ EXPORT _s_TransformVertices_Strided_XYZ_5_NEON
+ EXPORT _s_TransformVertices_Strided_XYZN_0_NEON
+ EXPORT _s_TransformVertices_Strided_XYZN_1_NEON
+ EXPORT _s_TransformVertices_Strided_XYZN_2_NEON
+ EXPORT _s_TransformVertices_Strided_XYZN_3_NEON
+ EXPORT _s_TransformVertices_Strided_XYZN_4_NEON
+ EXPORT _s_TransformVertices_Strided_XYZN_5_NEON
+ EXPORT _s_TransformVertices_Strided_XYZNT_0_NEON
+ EXPORT _s_TransformVertices_Strided_XYZNT_1_NEON
+ EXPORT _s_TransformVertices_Strided_XYZNT_2_NEON
+ EXPORT _s_TransformVertices_Strided_XYZNT_3_NEON
+ EXPORT _s_TransformVertices_Strided_XYZNT_4_NEON
+ EXPORT _s_TransformVertices_Strided_XYZNT_5_NEON
+
+|_s_TransformVertices_Strided_XYZ_0_NEON| PROC
+ mov ip, sp
+ vpush {s0-s15}
+ stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vldmia r3!, {d24-d31}
+ mov.w r6, #12
+ ldr.w r3, [ip]
+ ldr.w r4, [ip, #4]
+ vorr q0, q15, q15
+ nop
+
+|TransformVertices_Strided_XYZ_0_Loop|
+ pld [r0, #512] ; 0x200
+ vld1.32 {d6-d7}, [r0], r4
+ vmla.f32 q0, q12, d6[0]
+ vmul.f32 q1, q13, d6[1]
+ vmul.f32 q2, q14, d7[0]
+ vadd.f32 q0, q0, q1
+ vadd.f32 q0, q0, q2
+ cmp r0, r1
+ vst1.32 {d0-d1}, [r3], r6
+ vorr q0, q15, q15
+ bcc.w |TransformVertices_Strided_XYZ_0_Loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vpop {s0-s15}
+ bx lr
+ ENDP
+
+
+|_s_TransformVertices_Strided_XYZ_1_NEON| PROC
+ mov ip, sp
+ vpush {s0-s15}
+ stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vldmia r3!, {d24-d31}
+ mov.w r6, #12
+ ldr.w r3, [ip]
+ ldr.w r4, [ip, #4]
+ vorr q0, q15, q15
+ nop
+ nop.w
+ nop.w
+ nop.w
+
+|TransformVertices_Strided_XYZ_1_Loop|
+ pld [r0, #512] ; 0x200
+ vld1.32 {d6-d7}, [r0], r4
+ vmla.f32 q0, q12, d6[0]
+ vmul.f32 q1, q13, d6[1]
+ vmul.f32 q2, q14, d7[0]
+ vadd.f32 q0, q0, q1
+ vld1.32 {d9}, [r2], r4
+ vadd.f32 q0, q0, q2
+ cmp r0, r1
+ vst1.32 {d0-d1}, [r3], r6
+ vorr q0, q15, q15
+ vst1.32 {d9[0]}, [r3]!
+ bcc.w |TransformVertices_Strided_XYZ_1_Loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vpop {s0-s15}
+ bx lr
+ ENDP
+
+
+|_s_TransformVertices_Strided_XYZ_2_NEON| PROC
+ mov ip, sp
+ vpush {s0-s15}
+ stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vldmia r3!, {d24-d31}
+ mov.w r6, #12
+ ldr.w r3, [ip]
+ ldr.w r4, [ip, #4]
+ vorr q0, q15, q15
+ nop
+ nop.w
+
+|TransformVertices_Strided_XYZ_2_Loop|
+ pld [r0, #512] ; 0x200
+ vld1.32 {d6-d7}, [r0], r4
+ vmla.f32 q0, q12, d6[0]
+ vmul.f32 q1, q13, d6[1]
+ vmul.f32 q2, q14, d7[0]
+ vadd.f32 q0, q0, q1
+ vld1.32 {d9}, [r2], r4
+ vadd.f32 q0, q0, q2
+ cmp r0, r1
+ vst1.32 {d0-d1}, [r3], r6
+ vorr q0, q15, q15
+ vst1.32 {d9}, [r3]!
+ bcc.w |TransformVertices_Strided_XYZ_2_Loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vpop {s0-s15}
+ bx lr
+ ENDP
+
+
+|_s_TransformVertices_Strided_XYZ_3_NEON| PROC
+ mov ip, sp
+ vpush {s0-s15}
+ stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vldmia r3!, {d24-d31}
+ mov.w r6, #12
+ ldr.w r3, [ip]
+ ldr.w r4, [ip, #4]
+ vorr q0, q15, q15
+ nop
+ nop.w
+
+|TransformVertices_Strided_XYZ_3_Loop|
+ pld [r0, #512] ; 0x200
+ vld1.32 {d6-d7}, [r0], r4
+ vmla.f32 q0, q12, d6[0]
+ vmul.f32 q1, q13, d6[1]
+ vmul.f32 q2, q14, d7[0]
+ vadd.f32 q0, q0, q1
+ vld1.32 {d9-d10}, [r2], r4
+ vadd.f32 q0, q0, q2
+ cmp r0, r1
+ vst1.32 {d0-d1}, [r3], r6
+ vorr q0, q15, q15
+ vst1.32 {d9}, [r3]!
+ vst1.32 {d10[0]}, [r3]!
+ bcc.w |TransformVertices_Strided_XYZ_3_Loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vpop {s0-s15}
+ bx lr
+ ENDP
+
+
+|_s_TransformVertices_Strided_XYZ_4_NEON| PROC
+ mov ip, sp
+ vpush {s0-s15}
+ stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vldmia r3!, {d24-d31}
+ mov.w r6, #12
+ ldr.w r3, [ip]
+ ldr.w r4, [ip, #4]
+ vorr q0, q15, q15
+ nop
+
+|TransformVertices_Strided_XYZ_4_Loop|
+ pld [r0, #512] ; 0x200
+ vld1.32 {d6-d7}, [r0], r4
+ vmla.f32 q0, q12, d6[0]
+ vmul.f32 q1, q13, d6[1]
+ vmul.f32 q2, q14, d7[0]
+ vadd.f32 q0, q0, q1
+ vld1.32 {d9-d10}, [r2], r4
+ vadd.f32 q0, q0, q2
+ cmp r0, r1
+ vst1.32 {d0-d1}, [r3], r6
+ vorr q0, q15, q15
+ vst1.32 {d9-d10}, [r3]!
+ bcc.w |TransformVertices_Strided_XYZ_4_Loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vpop {s0-s15}
+ bx lr
+ ENDP
+
+
+|_s_TransformVertices_Strided_XYZ_5_NEON| PROC
+ mov ip, sp
+ vpush {s0-s15}
+ stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vldmia r3!, {d24-d31}
+ mov.w r6, #12
+ ldr.w r3, [ip]
+ ldr.w r4, [ip, #4]
+ vorr q0, q15, q15
+ nop
+ nop.w
+
+|TransformVertices_Strided_XYZ_5_Loop|
+ pld [r0, #512] ; 0x200
+ vld1.32 {d6-d7}, [r0], r4
+ vmla.f32 q0, q12, d6[0]
+ vmul.f32 q1, q13, d6[1]
+ vmul.f32 q2, q14, d7[0]
+ vadd.f32 q0, q0, q1
+ vld1.32 {d9-d11}, [r2], r4
+ vadd.f32 q0, q0, q2
+ cmp r0, r1
+ vst1.32 {d0-d1}, [r3], r6
+ vorr q0, q15, q15
+ vst1.32 {d9-d10}, [r3]!
+ vst1.32 {d11[0]}, [r3]!
+ bcc.w |TransformVertices_Strided_XYZ_5_Loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vpop {s0-s15}
+ bx lr
+ ENDP
+
+
+|_s_TransformVertices_Strided_XYZN_0_NEON| PROC
+ mov ip, sp
+ vpush {s0-s15}
+ stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vldmia r3!, {d24-d31}
+ mov.w r6, #12
+ ldr.w r3, [ip]
+ ldr.w r4, [ip, #4]
+ vorr q0, q15, q15
+ nop
+
+|TransformVertices_Strided_XYZN_0_Loop|
+ pld [r0, #512] ; 0x200
+ vld1.32 {d4-d6}, [r0], r4
+ vmla.f32 q0, q12, d4[0]
+ vmul.f32 q1, q12, d5[1]
+ vmla.f32 q0, q13, d4[1]
+ vmla.f32 q1, q13, d6[0]
+ vmla.f32 q0, q14, d5[0]
+ vmla.f32 q1, q14, d6[1]
+ vst1.32 {d0-d1}, [r3], r6
+ cmp r0, r1
+ vorr q0, q15, q15
+ vst1.32 {d2-d3}, [r3], r6
+ bcc.w |TransformVertices_Strided_XYZN_0_Loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vpop {s0-s15}
+ bx lr
+ ENDP
+
+
+|_s_TransformVertices_Strided_XYZN_1_NEON| PROC
+ mov ip, sp
+ vpush {s0-s15}
+ stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vldmia r3!, {d24-d31}
+ mov.w r6, #12
+ ldr.w r3, [ip]
+ ldr.w r4, [ip, #4]
+ vorr q0, q15, q15
+ nop
+ nop.w
+
+|TransformVertices_Strided_XYZN_1_Loop|
+ pld [r0, #512] ; 0x200
+ vld1.32 {d4-d6}, [r0], r4
+ vmla.f32 q0, q12, d4[0]
+ vmul.f32 q1, q12, d5[1]
+ vld1.32 {d9}, [r2], r4
+ vmla.f32 q0, q13, d4[1]
+ vmla.f32 q1, q13, d6[0]
+ vmla.f32 q0, q14, d5[0]
+ vmla.f32 q1, q14, d6[1]
+ vst1.32 {d0-d1}, [r3], r6
+ cmp r0, r1
+ vorr q0, q15, q15
+ vst1.32 {d2-d3}, [r3], r6
+ vst1.32 {d9[0]}, [r3]!
+ bcc.w |TransformVertices_Strided_XYZN_1_Loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vpop {s0-s15}
+ bx lr
+ ENDP
+
+
+|_s_TransformVertices_Strided_XYZN_2_NEON| PROC
+ mov ip, sp
+ vpush {s0-s15}
+ stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vldmia r3!, {d24-d31}
+ mov.w r6, #12
+ ldr.w r3, [ip]
+ ldr.w r4, [ip, #4]
+ vorr q0, q15, q15
+ nop
+ nop.w
+ nop.w
+ nop.w
+
+|TransformVertices_Strided_XYZN_2_Loop|
+ pld [r0, #512] ; 0x200
+ vld1.32 {d4-d6}, [r0], r4
+ vmla.f32 q0, q12, d4[0]
+ vmul.f32 q1, q12, d5[1]
+ vld1.32 {d9}, [r2], r4
+ vmla.f32 q0, q13, d4[1]
+ vmla.f32 q1, q13, d6[0]
+ vmla.f32 q0, q14, d5[0]
+ vmla.f32 q1, q14, d6[1]
+ vst1.32 {d0-d1}, [r3], r6
+ cmp r0, r1
+ vorr q0, q15, q15
+ vst1.32 {d2-d3}, [r3], r6
+ vst1.32 {d9}, [r3]!
+ bcc.w |TransformVertices_Strided_XYZN_2_Loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vpop {s0-s15}
+ bx lr
+ ENDP
+
+
+|_s_TransformVertices_Strided_XYZN_3_NEON| PROC
+ mov ip, sp
+ vpush {s0-s15}
+ stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vldmia r3!, {d24-d31}
+ mov.w r6, #12
+ ldr.w r3, [ip]
+ ldr.w r4, [ip, #4]
+ vorr q0, q15, q15
+ nop
+ nop.w
+ nop.w
+ nop.w
+
+|TransformVertices_Strided_XYZN_3_Loop|
+ pld [r0, #512] ; 0x200
+ vld1.32 {d4-d6}, [r0], r4
+ vmla.f32 q0, q12, d4[0]
+ vmul.f32 q1, q12, d5[1]
+ vld1.32 {d9-d10}, [r2], r4
+ vmla.f32 q0, q13, d4[1]
+ vmla.f32 q1, q13, d6[0]
+ vmla.f32 q0, q14, d5[0]
+ vmla.f32 q1, q14, d6[1]
+ vst1.32 {d0-d1}, [r3], r6
+ cmp r0, r1
+ vorr q0, q15, q15
+ vst1.32 {d2-d3}, [r3], r6
+ vst1.32 {d9}, [r3]!
+ vst1.32 {d10[0]}, [r3]!
+ bcc.w |TransformVertices_Strided_XYZN_3_Loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vpop {s0-s15}
+ bx lr
+ ENDP
+
+
+|_s_TransformVertices_Strided_XYZN_4_NEON| PROC
+ mov ip, sp
+ vpush {s0-s15}
+ stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vldmia r3!, {d24-d31}
+ mov.w r6, #12
+ ldr.w r3, [ip]
+ ldr.w r4, [ip, #4]
+ vorr q0, q15, q15
+ nop
+ nop.w
+ nop.w
+
+|TransformVertices_Strided_XYZN_4_Loop|
+ pld [r0, #512] ; 0x200
+ vld1.32 {d4-d6}, [r0], r4
+ vmla.f32 q0, q12, d4[0]
+ vmul.f32 q1, q12, d5[1]
+ vld1.32 {d9-d10}, [r2], r4
+ vmla.f32 q0, q13, d4[1]
+ vmla.f32 q1, q13, d6[0]
+ vmla.f32 q0, q14, d5[0]
+ vmla.f32 q1, q14, d6[1]
+ vst1.32 {d0-d1}, [r3], r6
+ cmp r0, r1
+ vorr q0, q15, q15
+ vst1.32 {d2-d3}, [r3], r6
+ vst1.32 {d9-d10}, [r3]!
+ bcc.w |TransformVertices_Strided_XYZN_4_Loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vpop {s0-s15}
+ bx lr
+ ENDP
+
+
+|_s_TransformVertices_Strided_XYZN_5_NEON| PROC
+ mov ip, sp
+ vpush {s0-s15}
+ stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vldmia r3!, {d24-d31}
+ mov.w r6, #12
+ ldr.w r3, [ip]
+ ldr.w r4, [ip, #4]
+ vorr q0, q15, q15
+ nop
+ nop.w
+ nop.w
+ nop.w
+
+|TransformVertices_Strided_XYZN_5_Loop|
+ pld [r0, #512] ; 0x200
+ vld1.32 {d4-d6}, [r0], r4
+ vmla.f32 q0, q12, d4[0]
+ vmul.f32 q1, q12, d5[1]
+ vld1.32 {d9-d11}, [r2], r4
+ vmla.f32 q0, q13, d4[1]
+ vmla.f32 q1, q13, d6[0]
+ vmla.f32 q0, q14, d5[0]
+ vmla.f32 q1, q14, d6[1]
+ vst1.32 {d0-d1}, [r3], r6
+ cmp r0, r1
+ vorr q0, q15, q15
+ vst1.32 {d2-d3}, [r3], r6
+ vst1.32 {d9-d10}, [r3]!
+ vst1.32 {d11[0]}, [r3]!
+ bcc.w |TransformVertices_Strided_XYZN_5_Loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vpop {s0-s15}
+ bx lr
+ ENDP
+
+
+|_s_TransformVertices_Strided_XYZNT_0_NEON| PROC
+ mov ip, sp
+ vpush {s0-s15}
+ stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vldmia r3!, {d24-d31}
+ mov.w r6, #12
+ ldr.w r3, [ip]
+ ldr.w r4, [ip, #4]
+ vorr q0, q15, q15
+ ldr.w r8, [ip, #8]
+ mov.w r9, #12
+ mov.w sl, #4
+ nop
+ nop.w
+ nop.w
+ nop.w
+
+|TransformVertices_Strided_XYZNT_0_Loop|
+ pld [r0, #512] ; 0x200
+ vld1.32 {d4-d6}, [r0], r4
+ vld1.32 {d7-d8}, [r8], r4
+ vmla.f32 q0, q12, d4[0]
+ vmul.f32 q1, q12, d5[1]
+ vmul.f32 q11, q12, d7[0]
+ vmla.f32 q0, q13, d4[1]
+ vmla.f32 q1, q13, d6[0]
+ vmla.f32 q11, q13, d7[1]
+ vmla.f32 q0, q14, d5[0]
+ vmla.f32 q1, q14, d6[1]
+ vmla.f32 q11, q14, d8[0]
+ vst1.32 {d0-d1}, [r3], r6
+ cmp r0, r1
+ vorr q0, q15, q15
+ vst1.32 {d2-d3}, [r3], r6
+ vtrn.32 d8, d7
+ vst1.32 {d22-d23}, [r3], r9
+ vst1.32 {d7[0]}, [r3], sl
+ bcc.w |TransformVertices_Strided_XYZNT_0_Loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vpop {s0-s15}
+ bx lr
+ ENDP
+
+
+|_s_TransformVertices_Strided_XYZNT_1_NEON| PROC
+ mov ip, sp
+ vpush {s0-s15}
+ stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vldmia r3!, {d24-d31}
+ mov.w r6, #12
+ ldr.w r3, [ip]
+ ldr.w r4, [ip, #4]
+ vorr q0, q15, q15
+ ldr.w r8, [ip, #8]
+ mov.w r9, #12
+ mov.w sl, #4
+ nop
+ nop.w
+ nop.w
+ nop.w
+
+|TransformVertices_Strided_XYZNT_1_Loop|
+ pld [r0, #512] ; 0x200
+ vld1.32 {d4-d6}, [r0], r4
+ vld1.32 {d7-d8}, [r8], r4
+ vmla.f32 q0, q12, d4[0]
+ vmul.f32 q1, q12, d5[1]
+ vmul.f32 q11, q12, d7[0]
+ vld1.32 {d9}, [r2], r4
+ vmla.f32 q0, q13, d4[1]
+ vmla.f32 q1, q13, d6[0]
+ vmla.f32 q11, q13, d7[1]
+ vmla.f32 q0, q14, d5[0]
+ vmla.f32 q1, q14, d6[1]
+ vmla.f32 q11, q14, d8[0]
+ vst1.32 {d0-d1}, [r3], r6
+ cmp r0, r1
+ vorr q0, q15, q15
+ vst1.32 {d2-d3}, [r3], r6
+ vst1.32 {d9[0]}, [r3]!
+ vtrn.32 d8, d7
+ vst1.32 {d22-d23}, [r3], r9
+ vst1.32 {d7[0]}, [r3], sl
+ bcc.w |TransformVertices_Strided_XYZNT_1_Loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vpop {s0-s15}
+ bx lr
+ ENDP
+
+
+|_s_TransformVertices_Strided_XYZNT_2_NEON| PROC
+ mov ip, sp
+ vpush {s0-s15}
+ stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vldmia r3!, {d24-d31}
+ mov.w r6, #12
+ ldr.w r3, [ip]
+ ldr.w r4, [ip, #4]
+ vorr q0, q15, q15
+ ldr.w r8, [ip, #8]
+ mov.w r9, #12
+ mov.w sl, #4
+ nop
+ nop.w
+
+|TransformVertices_Strided_XYZNT_2_Loop|
+ pld [r0, #512] ; 0x200
+ vld1.32 {d4-d6}, [r0], r4
+ vld1.32 {d7-d8}, [r8], r4
+ vmla.f32 q0, q12, d4[0]
+ vmul.f32 q1, q12, d5[1]
+ vmul.f32 q11, q12, d7[0]
+ vld1.32 {d9}, [r2], r4
+ vmla.f32 q0, q13, d4[1]
+ vmla.f32 q1, q13, d6[0]
+ vmla.f32 q11, q13, d7[1]
+ vmla.f32 q0, q14, d5[0]
+ vmla.f32 q1, q14, d6[1]
+ vmla.f32 q11, q14, d8[0]
+ vst1.32 {d0-d1}, [r3], r6
+ cmp r0, r1
+ vorr q0, q15, q15
+ vst1.32 {d2-d3}, [r3], r6
+ vst1.32 {d9}, [r3]!
+ vtrn.32 d8, d7
+ vst1.32 {d22-d23}, [r3], r9
+ vst1.32 {d7[0]}, [r3], sl
+ bcc.w |TransformVertices_Strided_XYZNT_2_Loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vpop {s0-s15}
+ bx lr
+ ENDP
+
+
+|_s_TransformVertices_Strided_XYZNT_3_NEON| PROC
+ mov ip, sp
+ vpush {s0-s15}
+ stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vldmia r3!, {d24-d31}
+ mov.w r6, #12
+ ldr.w r3, [ip]
+ ldr.w r4, [ip, #4]
+ vorr q0, q15, q15
+ ldr.w r8, [ip, #8]
+ mov.w r9, #12
+ mov.w sl, #4
+ nop
+ nop.w
+
+|TransformVertices_Strided_XYZNT_3_Loop|
+ pld [r0, #512] ; 0x200
+ vld1.32 {d4-d6}, [r0], r4
+ vld1.32 {d7-d8}, [r8], r4
+ vmla.f32 q0, q12, d4[0]
+ vmul.f32 q1, q12, d5[1]
+ vmul.f32 q11, q12, d7[0]
+ vld1.32 {d9-d10}, [r2], r4
+ vmla.f32 q0, q13, d4[1]
+ vmla.f32 q1, q13, d6[0]
+ vmla.f32 q11, q13, d7[1]
+ vmla.f32 q0, q14, d5[0]
+ vmla.f32 q1, q14, d6[1]
+ vmla.f32 q11, q14, d8[0]
+ vst1.32 {d0-d1}, [r3], r6
+ cmp r0, r1
+ vorr q0, q15, q15
+ vst1.32 {d2-d3}, [r3], r6
+ vst1.32 {d9}, [r3]!
+ vst1.32 {d10[0]}, [r3]!
+ vtrn.32 d8, d7
+ vst1.32 {d22-d23}, [r3], r9
+ vst1.32 {d7[0]}, [r3], sl
+ bcc.w |TransformVertices_Strided_XYZNT_3_Loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vpop {s0-s15}
+ bx lr
+ ENDP
+
+
+|_s_TransformVertices_Strided_XYZNT_4_NEON| PROC
+ mov ip, sp
+ vpush {s0-s15}
+ stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vldmia r3!, {d24-d31}
+ mov.w r6, #12
+ ldr.w r3, [ip]
+ ldr.w r4, [ip, #4]
+ vorr q0, q15, q15
+ ldr.w r8, [ip, #8]
+ mov.w r9, #12
+ mov.w sl, #4
+ nop
+
+|TransformVertices_Strided_XYZNT_4_Loop|
+ pld [r0, #512] ; 0x200
+ vld1.32 {d4-d6}, [r0], r4
+ vld1.32 {d7-d8}, [r8], r4
+ vmla.f32 q0, q12, d4[0]
+ vmul.f32 q1, q12, d5[1]
+ vmul.f32 q11, q12, d7[0]
+ vld1.32 {d9-d10}, [r2], r4
+ vmla.f32 q0, q13, d4[1]
+ vmla.f32 q1, q13, d6[0]
+ vmla.f32 q11, q13, d7[1]
+ vmla.f32 q0, q14, d5[0]
+ vmla.f32 q1, q14, d6[1]
+ vmla.f32 q11, q14, d8[0]
+ vst1.32 {d0-d1}, [r3], r6
+ cmp r0, r1
+ vorr q0, q15, q15
+ vst1.32 {d2-d3}, [r3], r6
+ vst1.32 {d9-d10}, [r3]!
+ vtrn.32 d8, d7
+ vst1.32 {d22-d23}, [r3], r9
+ vst1.32 {d7[0]}, [r3], sl
+ bcc.w |TransformVertices_Strided_XYZNT_4_Loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vpop {s0-s15}
+ bx lr
+ ENDP
+
+
+|_s_TransformVertices_Strided_XYZNT_5_NEON| PROC
+ mov ip, sp
+ vpush {s0-s15}
+ stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vldmia r3!, {d24-d31}
+ mov.w r6, #12
+ ldr.w r3, [ip]
+ ldr.w r4, [ip, #4]
+ vorr q0, q15, q15
+ ldr.w r8, [ip, #8]
+ mov.w r9, #12
+ mov.w sl, #4
+ nop
+ nop.w
+
+|TransformVertices_Strided_XYZNT_5_Loop|
+ pld [r0, #512] ; 0x200
+ vld1.32 {d4-d6}, [r0], r4
+ vld1.32 {d7-d8}, [r8], r4
+ vmla.f32 q0, q12, d4[0]
+ vmul.f32 q1, q12, d5[1]
+ vmul.f32 q11, q12, d7[0]
+ vld1.32 {d9-d11}, [r2], r4
+ vmla.f32 q0, q13, d4[1]
+ vmla.f32 q1, q13, d6[0]
+ vmla.f32 q11, q13, d7[1]
+ vmla.f32 q0, q14, d5[0]
+ vmla.f32 q1, q14, d6[1]
+ vmla.f32 q11, q14, d8[0]
+ vst1.32 {d0-d1}, [r3], r6
+ cmp r0, r1
+ vorr q0, q15, q15
+ vst1.32 {d2-d3}, [r3], r6
+ vst1.32 {d9-d10}, [r3]!
+ vst1.32 {d11[0]}, [r3]!
+ vtrn.32 d8, d7
+ vst1.32 {d22-d23}, [r3], r9
+ vst1.32 {d7[0]}, [r3], sl
+ bcc.w |TransformVertices_Strided_XYZNT_5_Loop|
+ ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
+ vpop {s0-s15}
+ bx lr
+ nop.w
+ nop.w
+ nop.w
+ ENDP
+
+
+ END
diff --git a/Runtime/Filters/Mesh/TransformVertexNEON.s b/Runtime/Filters/Mesh/TransformVertexNEON.s
new file mode 100644
index 0000000..e21a554
--- /dev/null
+++ b/Runtime/Filters/Mesh/TransformVertexNEON.s
@@ -0,0 +1,224 @@
+#define UNITY_ASSEMBLER
+#include "Configuration/PrefixConfigure.h"
+
+#if UNITY_SUPPORTS_NEON
+
+.set device,0
+.set device,__arm__
+
+.if device
+
+//.code32
+
+
+.globl _s_TransformVertices_Strided_XYZ_0_NEON
+.globl _s_TransformVertices_Strided_XYZ_1_NEON
+.globl _s_TransformVertices_Strided_XYZ_2_NEON
+.globl _s_TransformVertices_Strided_XYZ_3_NEON
+.globl _s_TransformVertices_Strided_XYZ_4_NEON
+.globl _s_TransformVertices_Strided_XYZ_5_NEON
+
+.globl _s_TransformVertices_Strided_XYZN_0_NEON
+.globl _s_TransformVertices_Strided_XYZN_1_NEON
+.globl _s_TransformVertices_Strided_XYZN_2_NEON
+.globl _s_TransformVertices_Strided_XYZN_3_NEON
+.globl _s_TransformVertices_Strided_XYZN_4_NEON
+.globl _s_TransformVertices_Strided_XYZN_5_NEON
+
+.globl _s_TransformVertices_Strided_XYZNT_0_NEON
+.globl _s_TransformVertices_Strided_XYZNT_1_NEON
+.globl _s_TransformVertices_Strided_XYZNT_2_NEON
+.globl _s_TransformVertices_Strided_XYZNT_3_NEON
+.globl _s_TransformVertices_Strided_XYZNT_4_NEON
+.globl _s_TransformVertices_Strided_XYZNT_5_NEON
+
+.globl _s_TransformVertices_Sprite_NEON
+
+
+#define STRIDED_INPUT 1
+
+
+#define LOOP_XYZ 1
+#define LOOP_XYZN 0
+#define LOOP_XYZNT 0
+#define LOOP_SPRITE 0
+
+
+_s_TransformVertices_Strided_XYZ_0_NEON:
+#define COPY_DATA_SZ 0
+#define LOOP_NAME TransformVertices_Strided_XYZ_0_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZ_1_NEON:
+#define COPY_DATA_SZ 1
+#define LOOP_NAME TransformVertices_Strided_XYZ_1_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZ_2_NEON:
+#define COPY_DATA_SZ 2
+#define LOOP_NAME TransformVertices_Strided_XYZ_2_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZ_3_NEON:
+#define COPY_DATA_SZ 3
+#define LOOP_NAME TransformVertices_Strided_XYZ_3_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZ_4_NEON:
+#define COPY_DATA_SZ 4
+#define LOOP_NAME TransformVertices_Strided_XYZ_4_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZ_5_NEON:
+#define COPY_DATA_SZ 5
+#define LOOP_NAME TransformVertices_Strided_XYZ_5_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+
+#undef LOOP_XYZ
+#undef LOOP_XYZN
+#undef LOOP_XYZNT
+#undef LOOP_SPRITE
+
+
+#define LOOP_XYZ 0
+#define LOOP_XYZN 1
+#define LOOP_XYZNT 0
+#define LOOP_SPRITE 0
+
+
+_s_TransformVertices_Strided_XYZN_0_NEON:
+#define COPY_DATA_SZ 0
+#define LOOP_NAME TransformVertices_Strided_XYZN_0_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZN_1_NEON:
+#define COPY_DATA_SZ 1
+#define LOOP_NAME TransformVertices_Strided_XYZN_1_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZN_2_NEON:
+#define COPY_DATA_SZ 2
+#define LOOP_NAME TransformVertices_Strided_XYZN_2_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZN_3_NEON:
+#define COPY_DATA_SZ 3
+#define LOOP_NAME TransformVertices_Strided_XYZN_3_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZN_4_NEON:
+#define COPY_DATA_SZ 4
+#define LOOP_NAME TransformVertices_Strided_XYZN_4_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZN_5_NEON:
+#define COPY_DATA_SZ 5
+#define LOOP_NAME TransformVertices_Strided_XYZN_5_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+
+#undef LOOP_XYZ
+#undef LOOP_XYZN
+#undef LOOP_XYZNT
+#undef LOOP_SPRITE
+
+
+#define LOOP_XYZ 0
+#define LOOP_XYZN 0
+#define LOOP_XYZNT 1
+#define LOOP_SPRITE 0
+
+
+_s_TransformVertices_Strided_XYZNT_0_NEON:
+#define COPY_DATA_SZ 0
+#define LOOP_NAME TransformVertices_Strided_XYZNT_0_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZNT_1_NEON:
+#define COPY_DATA_SZ 1
+#define LOOP_NAME TransformVertices_Strided_XYZNT_1_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZNT_2_NEON:
+#define COPY_DATA_SZ 2
+#define LOOP_NAME TransformVertices_Strided_XYZNT_2_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZNT_3_NEON:
+#define COPY_DATA_SZ 3
+#define LOOP_NAME TransformVertices_Strided_XYZNT_3_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZNT_4_NEON:
+#define COPY_DATA_SZ 4
+#define LOOP_NAME TransformVertices_Strided_XYZNT_4_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZNT_5_NEON:
+#define COPY_DATA_SZ 5
+#define LOOP_NAME TransformVertices_Strided_XYZNT_5_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+
+#undef LOOP_XYZ
+#undef LOOP_XYZN
+#undef LOOP_XYZNT
+#undef LOOP_SPRITE
+
+#define LOOP_XYZ 0
+#define LOOP_XYZN 0
+#define LOOP_XYZNT 0
+#define LOOP_SPRITE 1
+
+_s_TransformVertices_Sprite_NEON:
+#define LOOP_NAME TransformVertices_Sprite_Loop
+#include "TransformVertexNEON_Loop.h"
+#undef LOOP_NAME
+
+#undef LOOP_XYZ
+#undef LOOP_XYZN
+#undef LOOP_XYZNT
+#undef LOOP_SPRITE
+
+#undef STRIDED_INPUT
+
+.endif
+
+#endif \ No newline at end of file
diff --git a/Runtime/Filters/Mesh/TransformVertexNEON_Loop.h b/Runtime/Filters/Mesh/TransformVertexNEON_Loop.h
new file mode 100644
index 0000000..d84a516
--- /dev/null
+++ b/Runtime/Filters/Mesh/TransformVertexNEON_Loop.h
@@ -0,0 +1,254 @@
+// TODO: SOA
+
+// defines
+// LOOP_XYZ
+// LOOP_XYZN
+// LOOP_XYZNT
+// LOOP_NAME
+// COPY_DATA_SZ
+// STRIDED_INPUT
+
+#if STRIDED_INPUT
+
+//r0: const void* srcData
+//r1: const void* srcDataEnd
+//r2: const void* addData
+//r3: const void* xform
+//[sp+0]: void* dstData
+//[sp+4]: const int stride
+
+mov ip, sp
+
+vpush {d0-d15}
+stmfd sp!, {r4-r11}
+
+vldmia r3!, {q12-q15}
+
+// r3:dstData
+// r4: stride
+// r6: proper offset for out ptr (pos, normal)
+
+mov r6, #12
+
+ldr r3, [ip, #0]
+ldr r4, [ip, #4]
+
+// overlap calculation
+
+vmov.32 q0, q15 // pos.w (1.0)
+
+
+#if LOOP_XYZ
+
+.align 4
+LOOP_NAME:
+
+pld [r0, #512] // prefetch
+
+vld1.32 {d6,d7}, [r0], r4 // load pos
+
+vmla.f32 q0, q12, d6[0] // pos.x
+vmul.f32 q1, q13, d6[1] // pos.y
+vmul.f32 q2, q14, d7[0] // pos.z
+
+vadd.f32 q0, q0, q1
+ // load additional data
+#if COPY_DATA_SZ == 1
+vld1.32 {d9}, [r2], r4
+#elif COPY_DATA_SZ == 2
+vld1.32 {d9}, [r2], r4
+#elif COPY_DATA_SZ == 3
+vld1.32 {d9,d10}, [r2], r4
+#elif COPY_DATA_SZ == 4
+vld1.32 {d9,d10}, [r2], r4
+#elif COPY_DATA_SZ == 5
+vld1.32 {d9,d10,d11}, [r2], r4
+#endif
+
+vadd.f32 q0, q0, q2
+cmp r0, r1 // check cycle
+
+vst1.32 {d0,d1}, [r3], r6
+
+vmov.32 q0, q15 // pos.w (1.0)
+ // save additional data
+#if COPY_DATA_SZ == 1
+vst1.32 {d9[0]}, [r3]!
+#elif COPY_DATA_SZ == 2
+vst1.32 {d9}, [r3]!
+#elif COPY_DATA_SZ == 3
+vst1.32 {d9}, [r3]!
+vst1.32 {d10[0]}, [r3]!
+#elif COPY_DATA_SZ == 4
+vst1.32 {d9,d10}, [r3]!
+#elif COPY_DATA_SZ == 5
+vst1.32 {d9,d10}, [r3]!
+vst1.32 {d11[0]}, [r3]!
+#endif
+
+bcc LOOP_NAME
+
+
+#elif LOOP_XYZN
+
+
+.align 4
+LOOP_NAME:
+
+pld [r0, #512] // prefetch
+
+vld1.32 {d4,d5,d6}, [r0], r4 // load pos + normal
+
+vmla.f32 q0, q12, d4[0] // pos.x
+vmul.f32 q1, q12, d5[1] // normal.x
+
+ // load additional data
+#if COPY_DATA_SZ == 1
+vld1.32 {d9}, [r2], r4
+#elif COPY_DATA_SZ == 2
+vld1.32 {d9}, [r2], r4
+#elif COPY_DATA_SZ == 3
+vld1.32 {d9,d10}, [r2], r4
+#elif COPY_DATA_SZ == 4
+vld1.32 {d9,d10}, [r2], r4
+#elif COPY_DATA_SZ == 5
+vld1.32 {d9,d10,d11}, [r2], r4
+#endif
+
+vmla.f32 q0, q13, d4[1] // pos.y
+vmla.f32 q1, q13, d6[0] // normal.y
+
+vmla.f32 q0, q14, d5[0] // pos.z
+vmla.f32 q1, q14, d6[1] // normal.z
+
+vst1.32 {d0,d1}, [r3], r6
+
+cmp r0, r1 // check cycle
+vmov.32 q0, q15 // pos.w (1.0)
+vst1.32 {d2,d3}, [r3], r6
+ // save additional data
+#if COPY_DATA_SZ == 1
+vst1.32 {d9[0]}, [r3]!
+#elif COPY_DATA_SZ == 2
+vst1.32 {d9}, [r3]!
+#elif COPY_DATA_SZ == 3
+vst1.32 {d9}, [r3]!
+vst1.32 {d10[0]}, [r3]!
+#elif COPY_DATA_SZ == 4
+vst1.32 {d9,d10}, [r3]!
+#elif COPY_DATA_SZ == 5
+vst1.32 {d9,d10}, [r3]!
+vst1.32 {d11[0]}, [r3]!
+#endif
+
+
+bcc LOOP_NAME
+
+
+#elif LOOP_XYZNT
+
+//[sp+8]: const void* tangent
+//r8: tangent
+
+ldr r8, [ip, #8]
+
+mov r9, #12
+mov r10, #4
+
+.align 4
+LOOP_NAME:
+
+pld [r0, #512] // prefetch
+
+vld1.32 {d4,d5,d6}, [r0], r4 // load pos + normal
+vld1.32 {d7,d8}, [r8], r4 // load tangent
+
+vmla.f32 q0, q12, d4[0] // pos.x
+vmul.f32 q1, q12, d5[1] // normal.x
+vmul.f32 q11, q12, d7[0] // tangent.x
+
+ // load additional data
+#if COPY_DATA_SZ == 1
+vld1.32 {d9}, [r2], r4
+#elif COPY_DATA_SZ == 2
+vld1.32 {d9}, [r2], r4
+#elif COPY_DATA_SZ == 3
+vld1.32 {d9,d10}, [r2], r4
+#elif COPY_DATA_SZ == 4
+vld1.32 {d9,d10}, [r2], r4
+#elif COPY_DATA_SZ == 5
+vld1.32 {d9,d10,d11}, [r2], r4
+#endif
+
+vmla.f32 q0, q13, d4[1] // pos.y
+vmla.f32 q1, q13, d6[0] // normal.y
+vmla.f32 q11, q13, d7[1] // tangent.y
+
+vmla.f32 q0, q14, d5[0] // pos.z
+vmla.f32 q1, q14, d6[1] // normal.z
+vmla.f32 q11, q14, d8[0] // tangent.z
+
+vst1.32 {d0,d1}, [r3], r6
+
+cmp r0, r1 // check cycle
+vmov.32 q0, q15 // pos.w (1.0)
+vst1.32 {d2,d3}, [r3], r6
+ // save additional data
+#if COPY_DATA_SZ == 1
+vst1.32 {d9[0]}, [r3]!
+#elif COPY_DATA_SZ == 2
+vst1.32 {d9}, [r3]!
+#elif COPY_DATA_SZ == 3
+vst1.32 {d9}, [r3]!
+vst1.32 {d10[0]}, [r3]!
+#elif COPY_DATA_SZ == 4
+vst1.32 {d9,d10}, [r3]!
+#elif COPY_DATA_SZ == 5
+vst1.32 {d9,d10}, [r3]!
+vst1.32 {d11[0]}, [r3]!
+#endif
+
+
+// TODO: less stupid way
+
+vtrn.32 d8, d7
+vst1.32 {d22,d23}, [r3], r9
+vst1.32 {d7[0]}, [r3], r10
+
+bcc LOOP_NAME
+#elif LOOP_SPRITE
+.align 4
+ldr r7, [ip, #8] // load color32
+vmov.32 d10[0], r7
+LOOP_NAME:
+
+pld [r0, #512] // prefetch
+
+vld1.32 {d6,d7}, [r0], r4 // load pos
+
+vmla.f32 q0, q12, d6[0] // pos.x
+vmul.f32 q1, q13, d6[1] // pos.y
+vmul.f32 q2, q14, d7[0] // pos.z
+vadd.f32 q0, q0, q1
+// load data
+vld1.32 {d9}, [r2], r4
+
+vadd.f32 q0, q0, q2
+cmp r0, r1 // check cycle
+
+vst1.32 {d0,d1}, [r3], r6
+
+vmov.32 q0, q15 // pos.w (1.0)
+// save data
+vst1.32 {d10[0]}, [r3]!
+vst1.32 {d9}, [r3]!
+
+
+bcc LOOP_NAME
+#endif
+
+ldmfd sp!, {r4-r11}
+vpop {d0-d15}
+bx lr
+
+#endif
diff --git a/Runtime/Filters/Mesh/TransformVertexVFP.s b/Runtime/Filters/Mesh/TransformVertexVFP.s
new file mode 100644
index 0000000..114afc6
--- /dev/null
+++ b/Runtime/Filters/Mesh/TransformVertexVFP.s
@@ -0,0 +1,250 @@
+#define UNITY_ASSEMBLER
+#include "Configuration/PrefixConfigure.h"
+#include "Runtime/Utilities/VFPUtility.h"
+
+#if UNITY_SUPPORTS_VFP
+
+.syntax unified
+
+.set device,0
+.set device,__arm__
+
+.if device
+
+//.code32
+
+
+.globl _s_TransformVertices_Strided_XYZ_0_VFP
+.globl _s_TransformVertices_Strided_XYZ_1_VFP
+.globl _s_TransformVertices_Strided_XYZ_2_VFP
+.globl _s_TransformVertices_Strided_XYZ_3_VFP
+.globl _s_TransformVertices_Strided_XYZ_4_VFP
+.globl _s_TransformVertices_Strided_XYZ_5_VFP
+
+.globl _s_TransformVertices_Strided_XYZN_0_VFP
+.globl _s_TransformVertices_Strided_XYZN_1_VFP
+.globl _s_TransformVertices_Strided_XYZN_2_VFP
+.globl _s_TransformVertices_Strided_XYZN_3_VFP
+.globl _s_TransformVertices_Strided_XYZN_4_VFP
+.globl _s_TransformVertices_Strided_XYZN_5_VFP
+
+.globl _s_TransformVertices_Strided_XYZNT_0_VFP
+.globl _s_TransformVertices_Strided_XYZNT_1_VFP
+.globl _s_TransformVertices_Strided_XYZNT_2_VFP
+.globl _s_TransformVertices_Strided_XYZNT_3_VFP
+.globl _s_TransformVertices_Strided_XYZNT_4_VFP
+.globl _s_TransformVertices_Strided_XYZNT_5_VFP
+
+.globl _s_TransformVertices_Sprite_VFP
+
+
+#if UNITY_ANDROID
+.hidden _s_TransformVertices_Strided_XYZ_0_VFP
+.hidden _s_TransformVertices_Strided_XYZ_1_VFP
+.hidden _s_TransformVertices_Strided_XYZ_2_VFP
+.hidden _s_TransformVertices_Strided_XYZ_3_VFP
+.hidden _s_TransformVertices_Strided_XYZ_4_VFP
+.hidden _s_TransformVertices_Strided_XYZ_5_VFP
+
+.hidden _s_TransformVertices_Strided_XYZN_0_VFP
+.hidden _s_TransformVertices_Strided_XYZN_1_VFP
+.hidden _s_TransformVertices_Strided_XYZN_2_VFP
+.hidden _s_TransformVertices_Strided_XYZN_3_VFP
+.hidden _s_TransformVertices_Strided_XYZN_4_VFP
+.hidden _s_TransformVertices_Strided_XYZN_5_VFP
+
+.hidden _s_TransformVertices_Strided_XYZNT_0_VFP
+.hidden _s_TransformVertices_Strided_XYZNT_1_VFP
+.hidden _s_TransformVertices_Strided_XYZNT_2_VFP
+.hidden _s_TransformVertices_Strided_XYZNT_3_VFP
+.hidden _s_TransformVertices_Strided_XYZNT_4_VFP
+.hidden _s_TransformVertices_Strided_XYZNT_5_VFP
+
+.hidden _s_TransformVertices_Sprite_VFP
+#endif
+
+#define STRIDED_INPUT 1
+
+
+#define LOOP_XYZ 1
+#define LOOP_XYZN 0
+#define LOOP_XYZNT 0
+#define LOOP_SPRITE 0
+
+_s_TransformVertices_Strided_XYZ_0_VFP:
+#define COPY_DATA_SZ 0
+#define LOOP_NAME TransformVertices_Strided_XYZ_0_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZ_1_VFP:
+#define COPY_DATA_SZ 1
+#define LOOP_NAME TransformVertices_Strided_XYZ_1_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZ_2_VFP:
+#define COPY_DATA_SZ 2
+#define LOOP_NAME TransformVertices_Strided_XYZ_2_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZ_3_VFP:
+#define COPY_DATA_SZ 3
+#define LOOP_NAME TransformVertices_Strided_XYZ_3_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZ_4_VFP:
+#define COPY_DATA_SZ 4
+#define LOOP_NAME TransformVertices_Strided_XYZ_4_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZ_5_VFP:
+#define COPY_DATA_SZ 5
+#define LOOP_NAME TransformVertices_Strided_XYZ_5_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+
+#undef LOOP_XYZ
+#undef LOOP_XYZN
+#undef LOOP_XYZNT
+#undef LOOP_SPRITE
+
+
+#define LOOP_XYZ 0
+#define LOOP_XYZN 1
+#define LOOP_XYZNT 0
+#define LOOP_SPRITE 0
+
+
+_s_TransformVertices_Strided_XYZN_0_VFP:
+#define COPY_DATA_SZ 0
+#define LOOP_NAME TransformVertices_Strided_XYZN_0_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZN_1_VFP:
+#define COPY_DATA_SZ 1
+#define LOOP_NAME TransformVertices_Strided_XYZN_1_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZN_2_VFP:
+#define COPY_DATA_SZ 2
+#define LOOP_NAME TransformVertices_Strided_XYZN_2_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZN_3_VFP:
+#define COPY_DATA_SZ 3
+#define LOOP_NAME TransformVertices_Strided_XYZN_3_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZN_4_VFP:
+#define COPY_DATA_SZ 4
+#define LOOP_NAME TransformVertices_Strided_XYZN_4_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZN_5_VFP:
+#define COPY_DATA_SZ 5
+#define LOOP_NAME TransformVertices_Strided_XYZN_5_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+
+#undef LOOP_XYZ
+#undef LOOP_XYZN
+#undef LOOP_XYZNT
+#undef LOOP_SPRITE
+
+
+#define LOOP_XYZ 0
+#define LOOP_XYZN 0
+#define LOOP_XYZNT 1
+#define LOOP_SPRITE 0
+
+
+_s_TransformVertices_Strided_XYZNT_0_VFP:
+#define COPY_DATA_SZ 0
+#define LOOP_NAME TransformVertices_Strided_XYZNT_0_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZNT_1_VFP:
+#define COPY_DATA_SZ 1
+#define LOOP_NAME TransformVertices_Strided_XYZNT_1_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZNT_2_VFP:
+#define COPY_DATA_SZ 2
+#define LOOP_NAME TransformVertices_Strided_XYZNT_2_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZNT_3_VFP:
+#define COPY_DATA_SZ 3
+#define LOOP_NAME TransformVertices_Strided_XYZNT_3_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZNT_4_VFP:
+#define COPY_DATA_SZ 4
+#define LOOP_NAME TransformVertices_Strided_XYZNT_4_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+_s_TransformVertices_Strided_XYZNT_5_VFP:
+#define COPY_DATA_SZ 5
+#define LOOP_NAME TransformVertices_Strided_XYZNT_5_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef COPY_DATA_SZ
+#undef LOOP_NAME
+
+#undef LOOP_XYZ
+#undef LOOP_XYZN
+#undef LOOP_XYZNT
+#undef LOOP_SPRITE
+
+#define LOOP_XYZ 0
+#define LOOP_XYZN 0
+#define LOOP_XYZNT 0
+#define LOOP_SPRITE 1
+
+_s_TransformVertices_Sprite_VFP:
+#define LOOP_NAME TransformVerties_Sprite_Loop
+#include "TransformVertexVFP_Loop.h"
+#undef LOOP_NAME
+
+#undef LOOP_XYZ
+#undef LOOP_XYZN
+#undef LOOP_XYZNT
+#undef LOOP_SPRITE
+
+#undef STRIDED_INPUT
+
+.endif
+
+#endif
diff --git a/Runtime/Filters/Mesh/TransformVertexVFP_Loop.h b/Runtime/Filters/Mesh/TransformVertexVFP_Loop.h
new file mode 100644
index 0000000..48193c8
--- /dev/null
+++ b/Runtime/Filters/Mesh/TransformVertexVFP_Loop.h
@@ -0,0 +1,252 @@
+// defines
+// LOOP_XYZ
+// LOOP_XYZN
+// LOOP_XYZNT
+// LOOP_SPRITE
+// LOOP_NAME
+// COPY_DATA_SZ
+// STRIDED_INPUT
+
+#if STRIDED_INPUT
+
+//r0: const void* srcData
+//r1: const void* srcDataEnd
+//r2: const void* addData
+//r3: const void* xform
+//[sp+0]: void* dstData
+//[sp+4]: const int stride
+//[sp+8]: const void* tangent
+
+mov ip, sp
+
+vpush {d0-d15}
+stmfd sp!, {r4-r11}
+
+// {s16-s31} xform
+
+vldmia.32 r3!, {s16-s31}
+
+// r3: dstData
+// r4: stride
+//r11: tangent
+ldr r3, [ip, #0]
+ldr r4, [ip, #4]
+
+#if LOOP_XYZNT
+ldr r11, [ip, #8]
+#endif
+
+#if LOOP_SPRITE
+//r6: color
+ldr r6, [ip, #8]
+#endif
+
+
+mov ip, r0
+// VFP_VECTOR_LENGTH(3)
+mov r0, ip
+
+
+#if LOOP_XYZ
+
+.align 4
+LOOP_NAME:
+
+mov r5, r0
+pld [r0, #512] // prefetch
+
+vldmia.32 r5!, {s0-s2} // load pos
+FCPYS4 (8,9,10,11, 28,29,30,31) // pos.w
+
+FMACS4 (8,9,10,11, 16,17,18,19, 0,0,0,0) // pos.x
+#if COPY_DATA_SZ == 1
+ldmia r2, {r6} // load additional data
+#elif COPY_DATA_SZ == 2
+ldmia r2, {r6-r7} // load additional data
+#elif COPY_DATA_SZ == 3
+ldmia r2, {r6-r8} // load additional data
+#elif COPY_DATA_SZ == 4
+ldmia r2, {r6-r9} // load additional data
+#elif COPY_DATA_SZ == 5
+ldmia r2, {r6-r10} // load additional data
+#endif
+
+FMACS4 (8,9,10,11, 20,21,22,23, 1,1,1,1) // pos.y
+add r0, r0, r4 // inc srcData
+
+FMACS4 (8,9,10,11, 24,25,26,27, 2,2,2,2) // pos.z
+add r2, r2, r4 // inc srcAddData
+
+vstmia.32 r3!, {s8-s10} // store pos
+cmp r0, r1 // check cycle
+
+#if COPY_DATA_SZ == 1
+stmia r3!, {r6} // save additional data
+#elif COPY_DATA_SZ == 2
+stmia r3!, {r6-r7} // save additional data
+#elif COPY_DATA_SZ == 3
+stmia r3!, {r6-r8} // save additional data
+#elif COPY_DATA_SZ == 4
+stmia r3!, {r6-r9} // save additional data
+#elif COPY_DATA_SZ == 5
+stmia r3!, {r6-r10} // save additional data
+#endif
+
+bcc LOOP_NAME
+
+
+#elif LOOP_XYZN
+
+.align 4
+LOOP_NAME:
+
+mov r5, r0
+pld [r0, #512] // prefetch
+
+vldmia.32 r5!, {s0-s2} // load pos
+FCPYS4 (8,9,10,11, 28,29,30,31) // pos.w
+
+vldmia.32 r5!, {s3-s5} // load normal
+FMACS4 (8,9,10,11, 16,17,18,19, 0,0,0,0) // pos.x
+
+FMULS4 (12,13,14,15, 16,17,18,19, 3,3,3,3) // normal.x
+FMACS4 (8,9,10,11, 20,21,22,23, 1,1,1,1) // pos.y
+
+#if COPY_DATA_SZ == 1
+ldmia r2, {r6} // load additional data
+#elif COPY_DATA_SZ == 2
+ldmia r2, {r6-r7} // load additional data
+#elif COPY_DATA_SZ == 3
+ldmia r2, {r6-r8} // load additional data
+#elif COPY_DATA_SZ == 4
+ldmia r2, {r6-r9} // load additional data
+#elif COPY_DATA_SZ == 5
+ldmia r2, {r6-r10} // load additional data
+#endif
+FMACS4 (8,9,10,11, 24,25,26,27, 2,2,2,2) // pos.z
+
+FMACS4 (12,13,14,15, 20,21,22,23, 4,4,4,4) // normal.y
+vstmia.32 r3!, {s8-s10} // store pos
+
+FMACS4 (12,13,14,15, 24,25,26,27, 5,5,5,5) // normal.z
+add r0, r0, r4 // inc srcData
+
+vstmia.32 r3!, {s12-s14} // store normal
+add r2, r2, r4 // inc srcAddData
+
+cmp r0, r1 // check cycle
+#if COPY_DATA_SZ == 1
+stmia r3!, {r6} // save additional data
+#elif COPY_DATA_SZ == 2
+stmia r3!, {r6-r7} // save additional data
+#elif COPY_DATA_SZ == 3
+stmia r3!, {r6-r8} // save additional data
+#elif COPY_DATA_SZ == 4
+stmia r3!, {r6-r9} // save additional data
+#elif COPY_DATA_SZ == 5
+stmia r3!, {r6-r10} // save additional data
+#endif
+
+bcc LOOP_NAME
+
+#elif LOOP_XYZNT
+
+.align 4
+LOOP_NAME:
+
+mov r5, r0
+pld [r0, #512] // prefetch
+
+vldmia.32 r5!, {s0-s2} // load pos
+FCPYS4 (8,9,10,11, 28,29,30,31) // pos.w
+
+vldmia.32 r5!, {s3-s5} // load normal
+FMACS4 (8,9,10,11, 16,17,18,19, 0,0,0,0) // pos.x
+
+FMULS4 (12,13,14,15, 16,17,18,19, 3,3,3,3) // normal.x
+FMACS4 (8,9,10,11, 20,21,22,23, 1,1,1,1) // pos.y
+
+#if COPY_DATA_SZ == 1
+ldmia r2, {r6} // load additional data
+#elif COPY_DATA_SZ == 2
+ldmia r2, {r6-r7} // load additional data
+#elif COPY_DATA_SZ == 3
+ldmia r2, {r6-r8} // load additional data
+#elif COPY_DATA_SZ == 4
+ldmia r2, {r6-r9} // load additional data
+#elif COPY_DATA_SZ == 5
+ldmia r2, {r6-r10} // load additional data
+#endif
+FMACS4 (8,9,10,11, 24,25,26,27, 2,2,2,2) // pos.z
+
+FMACS4 (12,13,14,15, 20,21,22,23, 4,4,4,4) // normal.y
+vstmia.32 r3!, {s8-s10} // store pos
+
+FMACS4 (12,13,14,15, 24,25,26,27, 5,5,5,5) // normal.z
+vldmia.32 r11, {s0-s3} // load tangent
+
+add r0, r0, r4 // inc srcData
+FMULS4 (8,9,10,11, 16,17,18,19, 0,0,0,0) // tangent.x
+
+vstmia.32 r3!, {s12-s14} // store normal
+FMACS4 (8,9,10,11, 20,21,22,23, 1,1,1,1) // tangent.y
+
+cmp r0, r1 // check cycle
+FMACS4 (8,9,10,11, 24,25,26,27, 2,2,2,2) // tangent.z
+
+#if COPY_DATA_SZ == 1
+stmia r3!, {r6} // save additional data
+#elif COPY_DATA_SZ == 2
+stmia r3!, {r6-r7} // save additional data
+#elif COPY_DATA_SZ == 3
+stmia r3!, {r6-r8} // save additional data
+#elif COPY_DATA_SZ == 4
+stmia r3!, {r6-r9} // save additional data
+#elif COPY_DATA_SZ == 5
+stmia r3!, {r6-r10} // save additional data
+#endif
+fcpys s11, s3 // copy tangent.w
+
+vstmia.32 r3!, {s8-s11} // store tangent
+add r2, r2, r4 // inc srcAddData
+
+add r11, r11, r4 // inc srcTangent
+bcc LOOP_NAME
+
+#elif LOOP_SPRITE
+
+.align 4
+LOOP_NAME:
+
+mov r5, r0
+pld [r0, #512] // prefetch
+
+vldmia.32 r5!, {s0-s2} // load pos
+FCPYS4 (8,9,10,11, 28,29,30,31) // pos.w
+
+FMACS4 (8,9,10,11, 16,17,18,19, 0,0,0,0) // pos.x
+
+
+ldmia r2, {r7-r8} // load uv
+
+FMACS4 (8,9,10,11, 20,21,22,23, 1,1,1,1) // pos.y
+add r0, r0, r4 // inc srcData
+
+FMACS4 (8,9,10,11, 24,25,26,27, 2,2,2,2) // pos.z
+add r2, r2, r4 // inc srcAddData
+
+vstmia.32 r3!, {s8-s10} // store pos
+cmp r0, r1 // check cycle
+
+stmia r3!, {r6-r8} // save color and uv
+
+bcc LOOP_NAME
+#endif
+
+// VFP_VECTOR_LENGTH_ZERO
+
+ldmfd sp!, {r4-r11}
+vpop {d0-d15}
+bx lr
+
+#endif // STRIDED_INPUT
diff --git a/Runtime/Filters/Mesh/VertexData.cpp b/Runtime/Filters/Mesh/VertexData.cpp
new file mode 100644
index 0000000..b922805
--- /dev/null
+++ b/Runtime/Filters/Mesh/VertexData.cpp
@@ -0,0 +1,559 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "VertexData.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferUtility.h"
+#include "Runtime/Serialize/SwapEndianArray.h"
+#include <algorithm>
+
+/*
+ On most platforms, for skinning/non-uniform-scaling of meshes you would want to split your data into
+ a hot data stream (position, normal and tangent) and a cold data stream (diffuse and uvs) in order to maximize CPU cache access patterns and
+ reduce bandwidth and computation ( you won't need to copy the cold data )
+*/
+
+VertexStreamsLayout VertexDataInfo::kVertexStreamsDefault = {{ kShaderChannelsAll, 0, 0, 0 }};
+#if UNITY_PS3
+ VertexStreamsLayout VertexDataInfo::kVertexStreamsSkinnedHotColdSplit = {{ VERTEX_FORMAT1(Vertex), VERTEX_FORMAT1(Normal), VERTEX_FORMAT1(Tangent), kShaderChannelsCold }};
+#else
+ VertexStreamsLayout VertexDataInfo::kVertexStreamsSkinnedHotColdSplit = {{ kShaderChannelsHot, kShaderChannelsCold, 0, 0 }};
+# if UNITY_EDITOR
+ VertexStreamsLayout VertexDataInfo::kVertexStreamsSkinnedHotColdSplitPS3 = {{ VERTEX_FORMAT1(Vertex), VERTEX_FORMAT1(Normal), VERTEX_FORMAT1(Tangent), kShaderChannelsCold }};
+# endif
+#endif
+
+#define MAKE_CHANNEL(fmt, dim) VertexChannelsLayout::Channel(kChannelFormat##fmt, dim)
+VertexChannelsLayout VertexDataInfo::kVertexChannelsDefault =
+{{ // Array wrapped by struct requires double braces
+ MAKE_CHANNEL(Float, 3), // position
+ MAKE_CHANNEL(Float, 3), // normal
+ MAKE_CHANNEL(Color, 1), // color
+ MAKE_CHANNEL(Float, 2), // texcoord0
+ MAKE_CHANNEL(Float, 2), // texcoord1
+ MAKE_CHANNEL(Float, 4) // tangent
+}};
+VertexChannelsLayout VertexDataInfo::kVertexChannelsCompressed =
+{{ // Array wrapped by struct requires double braces
+ MAKE_CHANNEL(Float, 3), // position
+ MAKE_CHANNEL(Float16, 4), // normal
+ MAKE_CHANNEL(Color, 1), // color
+ MAKE_CHANNEL(Float16, 2), // texcoord0
+ MAKE_CHANNEL(Float16, 2), // texcoord1
+ MAKE_CHANNEL(Float16, 4) // tangent
+}};
+VertexChannelsLayout VertexDataInfo::kVertexChannelsCompressedAggressive =
+{{ // Array wrapped by struct requires double braces
+ MAKE_CHANNEL(Float, 3), // position
+ MAKE_CHANNEL(Byte, 4), // normal
+ MAKE_CHANNEL(Color, 1), // color
+ MAKE_CHANNEL(Float16, 2), // texcoord0
+ MAKE_CHANNEL(Float16, 2), // texcoord1
+ MAKE_CHANNEL(Byte, 4) // tangent
+}};
+#undef MAKE_CHANNEL
+
+static const UInt8 kVertexChannelFormatSizes[kChannelFormatCount] = {
+ 4, // kChannelFormatFloat
+ 2, // kChannelFormatFloat16
+ 4, // kChannelFormatColor
+ 1 // kChannelFormatByte
+};
+
+size_t GetChannelFormatSize(UInt8 format)
+{
+ Assert (format < kChannelFormatCount);
+ return kVertexChannelFormatSizes[format];
+}
+
+static bool operator == (const VertexStreamsLayout& lhs, const VertexStreamsLayout& rhs)
+{
+ return CompareArrays(lhs.channelMasks, rhs.channelMasks, kMaxVertexStreams);
+}
+
+template<class TransferFunction>
+void VertexData::Transfer (TransferFunction& transfer)
+{
+ #if SUPPORT_SERIALIZED_TYPETREES
+ if (transfer.GetFlags() & kWorkaround35MeshSerializationFuckup)
+ {
+ TransferWorkaround35SerializationFuckup (transfer);
+ return;
+ }
+ #endif
+
+ transfer.Transfer (m_CurrentChannels, "m_CurrentChannels", kHideInEditorMask);
+ transfer.Transfer (m_VertexCount, "m_VertexCount", kHideInEditorMask);
+
+ dynamic_array<ChannelInfo> channels;
+ dynamic_array<StreamInfo> streams;
+ if (transfer.IsWriting ())
+ {
+ channels.resize_uninitialized (kShaderChannelCount);
+ streams.resize_uninitialized (kMaxVertexStreams);
+ std::copy (m_Channels, m_Channels + kShaderChannelCount, channels.begin ());
+ std::copy (m_Streams, m_Streams + kMaxVertexStreams, streams.begin ());
+ }
+ transfer.Transfer (channels, "m_Channels", kHideInEditorMask);
+ transfer.Transfer (streams, "m_Streams", kHideInEditorMask);
+
+ if (transfer.IsReading ())
+ {
+ // For compatibility do this even if channels/streams info didn't exist (case 558604)
+ // In the past there was only a channels mask, UpdateStreams() generates the info from that
+ if (channels.size () == kShaderChannelCount)
+ std::copy (channels.begin (), channels.begin () + kShaderChannelCount, m_Channels);
+ if (streams.size () == kMaxVertexStreams)
+ std::copy (streams.begin (), streams.begin () + kMaxVertexStreams, m_Streams);
+ else
+ std::fill (m_Streams, m_Streams + kMaxVertexStreams, StreamInfo());
+
+ UInt32 channelsInStreams = 0;
+ for (int i = 0; i < kMaxVertexStreams ; i++)
+ channelsInStreams |= m_Streams[i].channelMask;
+ if (channelsInStreams)
+ UpdateStreams(channelsInStreams, m_VertexCount, GetStreamsLayout (), GetChannelsLayout ());
+ else
+ UpdateStreams(m_CurrentChannels, m_VertexCount, kVertexStreamsDefault, kVertexChannelsDefault);
+ }
+
+ transfer.TransferTypeless (&m_DataSize, "m_DataSize", kHideInEditorMask);
+ if (transfer.DidReadLastProperty ())
+ {
+ if (m_Data)
+ UNITY_FREE (kMemVertexData, m_Data);
+ m_Data = (UInt8*)UNITY_MALLOC_ALIGNED (kMemVertexData, VertexData::GetAllocateDataSize (m_DataSize), kVertexDataAlign);
+ }
+
+ transfer.TransferTypelessData (m_DataSize, m_Data);
+}
+
+#if SUPPORT_SERIALIZED_TYPETREES
+template<class TransferFunction>
+void VertexData::TransferWorkaround35SerializationFuckup (TransferFunction& transfer)
+{
+ UInt32 currentChannels = m_CurrentChannels;
+ transfer.Transfer (currentChannels, "m_CurrentChannels", kHideInEditorMask);
+ transfer.Transfer (m_VertexCount, "m_VertexCount", kHideInEditorMask);
+
+ TRANSFER(m_Streams[0]);
+ TRANSFER(m_Streams[1]);
+ TRANSFER(m_Streams[2]);
+ TRANSFER(m_Streams[3]);
+
+ if (transfer.IsReading ())
+ {
+ if(m_VertexCount && (currentChannels == 0))
+ {
+ for(int i=0;i<kMaxVertexStreams;i++)
+ currentChannels |= m_Streams[i].channelMask;
+ }
+ UpdateStreams(currentChannels, m_VertexCount);
+ //GetComponentInfo(m_Components, currentChannels);
+ m_CurrentChannels = currentChannels;
+ }
+
+ transfer.TransferTypeless (&m_DataSize, "m_DataSize", kHideInEditorMask);
+
+ if (transfer.IsReading ())
+ {
+ if (m_Data)
+ UNITY_FREE (kMemVertexData, m_Data);
+ m_Data = (UInt8*)UNITY_MALLOC_ALIGNED (kMemVertexData, VertexData::GetAllocateDataSize (m_DataSize), kVertexDataAlign);
+ }
+
+ transfer.TransferTypelessData (m_DataSize, m_Data);
+}
+#endif
+
+INSTANTIATE_TEMPLATE_TRANSFER(VertexData)
+
+void VertexDataInfo::UpdateStreams(unsigned newChannelMask, size_t newVertexCount, const VertexStreamsLayout& streams, const VertexChannelsLayout& channels)
+{
+ m_VertexCount = newVertexCount;
+ m_CurrentChannels = 0;
+ m_VertexSize = 0;
+ size_t streamOffset = 0;
+ for (int s = 0; s < kMaxVertexStreams; s++)
+ {
+ StreamInfo& stream = m_Streams[s];
+ m_Streams[s].Reset();
+ stream.channelMask = streams.channelMasks[s] & newChannelMask;
+ if (stream.channelMask == 0)
+ continue;
+ m_CurrentChannels |= stream.channelMask;
+ for (int c = 0; c < kShaderChannelCount; c++)
+ {
+ if (stream.channelMask & (1 << c))
+ {
+ ChannelInfo& channel = m_Channels[c];
+ const VertexChannelsLayout::Channel& srcChannel = channels.channels[c];
+ channel.stream = s;
+ channel.offset = stream.stride;
+ channel.format = srcChannel.format;
+ channel.dimension = srcChannel.dimension;
+ stream.stride += channel.dimension * GetChannelFormatSize(channel.format);
+ }
+ }
+ streamOffset = AlignStreamSize(streamOffset);
+ stream.offset = streamOffset;
+ streamOffset += stream.stride * newVertexCount;
+ m_VertexSize += stream.stride;
+ }
+ for (int c = 0; c < kShaderChannelCount; c++)
+ {
+ // Reset channels that were removed
+ if (!(m_CurrentChannels & (1 << c)))
+ m_Channels[c].Reset();
+ }
+ m_DataSize = streamOffset;
+}
+
+size_t VertexDataInfo::GetActiveStreamCount() const
+{
+ size_t activeStreamCount = 0;
+ for (int i=0; i<kMaxVertexStreams; i++)
+ {
+ if(m_Streams[i].channelMask != 0)
+ activeStreamCount++;
+ }
+ return activeStreamCount;
+}
+
+size_t VertexDataInfo::GetStreamIndex(ShaderChannel channel) const
+{
+ UInt32 channelMask = 1 << channel;
+ for (int i=0; i<kMaxVertexStreams; i++)
+ {
+ if(m_Streams[i].channelMask & channelMask)
+ return i;
+ }
+ return -1;
+}
+
+VertexStreamsLayout VertexDataInfo::GetStreamsLayout() const
+{
+ VertexStreamsLayout result;
+ for (int i = 0; i < kMaxVertexStreams; i++)
+ result.channelMasks[i] = m_Streams[i].channelMask;
+ return result;
+}
+
+VertexChannelsLayout VertexDataInfo::GetChannelsLayout() const
+{
+ VertexChannelsLayout result;
+ for (int i = 0; i < kShaderChannelCount; i++)
+ {
+ result.channels[i] = VertexChannelsLayout::Channel(m_Channels[i].format, m_Channels[i].dimension);
+ }
+ return result;
+}
+
+bool VertexDataInfo::ConformsToStreamsLayout(const VertexStreamsLayout& streams) const
+{
+ for (int i = 0; i < kMaxVertexStreams; i++)
+ {
+ // Fail if we have a channel that's not in the layout
+ if (m_Streams[i].channelMask & ~streams.channelMasks[i])
+ return false;
+ }
+ return true;
+}
+
+bool VertexDataInfo::ConformsToChannelsLayout(const VertexChannelsLayout& channels) const
+{
+ for (int i = 0; i < kShaderChannelCount; i++)
+ {
+ if (m_Channels[i].IsValid())
+ {
+ const VertexChannelsLayout::Channel& channel = channels.channels[i];
+ if (m_Channels[i].format != channel.format ||
+ m_Channels[i].dimension != channel.dimension)
+ return false;
+ }
+ }
+ return true;
+}
+
+signed char f32_to_s8(float fval)
+{
+ return ((fval * 255.0f) - 1.0f) / 2.0f;
+}
+
+float s8_to_f32(signed char val)
+{
+ return (2*(val/255.0f)-1.0f);
+}
+
+static void ConvertCopyChannel(size_t vertexCount,
+ const UInt8* srcPtr, UInt8 srcStride, UInt8 srcType, UInt8 srcDim,
+ UInt8* dstPtr, UInt8 dstStride, UInt8 dstType, UInt8 dstDim)
+{
+ UInt8 minDim = std::min(srcDim, dstDim);
+ if (srcType == kChannelFormatFloat16 && dstType == kChannelFormatFloat)
+ {
+ // decompressing
+ for (size_t i = 0; i < vertexCount; i++)
+ {
+ UInt8 comp = 0;
+ for ( ; comp < minDim; comp++)
+ HalfToFloat(reinterpret_cast<const UInt16*>(srcPtr)[comp], reinterpret_cast<float*>(dstPtr)[comp]);
+ for ( ; comp < dstDim; comp++)
+ reinterpret_cast<float*>(dstPtr)[comp] = 0.0f;
+ srcPtr += srcStride;
+ dstPtr += dstStride;
+ }
+ }
+ else if (srcType == kChannelFormatByte && dstType == kChannelFormatFloat)
+ {
+ // decompressing
+ for (size_t i = 0; i < vertexCount; i++)
+ {
+ UInt8 comp = 0;
+ for ( ; comp < minDim; comp++)
+ reinterpret_cast<float*>(dstPtr)[comp] = s8_to_f32(reinterpret_cast<const SInt8*>(srcPtr)[comp]);
+ for ( ; comp < dstDim; comp++)
+ reinterpret_cast<float*>(dstPtr)[comp] = 0.0f;
+ srcPtr += srcStride;
+ dstPtr += dstStride;
+ }
+ }
+#if UNITY_EDITOR
+ else if (srcType == kChannelFormatFloat && dstType == kChannelFormatFloat16)
+ {
+ // compressing
+ for (size_t i = 0; i < vertexCount; i++)
+ {
+ UInt8 comp = 0;
+ for ( ; comp < minDim; comp++)
+ g_FloatToHalf.Convert(reinterpret_cast<const float*>(srcPtr)[comp], reinterpret_cast<UInt16*>(dstPtr)[comp]);
+ for ( ; comp < dstDim; comp++)
+ reinterpret_cast<UInt16*>(dstPtr)[comp] = 0;
+ srcPtr += srcStride;
+ dstPtr += dstStride;
+ }
+ }
+ else if (srcType == kChannelFormatFloat && dstType == kChannelFormatByte)
+ {
+ // compressing
+ for (size_t i = 0; i < vertexCount; i++)
+ {
+ UInt8 comp = 0;
+ for ( ; comp < minDim; comp++)
+ reinterpret_cast<SInt8*>(dstPtr)[comp] = f32_to_s8(reinterpret_cast<const float*>(srcPtr)[comp]);
+ for ( ; comp < dstDim; comp++)
+ reinterpret_cast<SInt8*>(dstPtr)[comp] = 0;
+ srcPtr += srcStride;
+ dstPtr += dstStride;
+ }
+ }
+#endif
+ else
+ ErrorString("Unsupported conversion of vertex formats");
+}
+
+static void CopyChannels (size_t vertexCount, unsigned copyChannels,
+ const StreamInfoArray srcStreams, const ChannelInfoArray srcChannels, const UInt8* srcData,
+ const StreamInfoArray dstStreams, const ChannelInfoArray dstChannels, UInt8* dstData)
+{
+ for (unsigned chan = copyChannels, i = 0; chan && (i < kShaderChannelCount); i++, chan >>= 1)
+ {
+ if (0 == (chan & 1))
+ continue;
+
+ const ChannelInfo& srcChannel = srcChannels[i];
+ const ChannelInfo& dstChannel = dstChannels[i];
+
+ const UInt8* srcPtr = srcData + srcChannel.CalcOffset(srcStreams);
+ UInt8* dstPtr = dstData + dstChannel.CalcOffset(dstStreams);
+ UInt8 srcStride = srcChannel.CalcStride(srcStreams);
+ UInt8 dstStride = dstChannel.CalcStride(dstStreams);
+
+ if(srcChannel.format == dstChannel.format)
+ {
+ size_t copySize = srcChannel.dimension * GetChannelFormatSize(srcChannel.format);
+ switch (copySize)
+ {
+ case 4:
+ {
+ for (size_t i=0; i<vertexCount; ++i)
+ {
+ *(reinterpret_cast<UInt32*> (dstPtr) + 0) = *(reinterpret_cast<const UInt32*> (srcPtr) + 0);
+ srcPtr += srcStride;
+ dstPtr += dstStride;
+ }
+ break;
+ }
+ case 8:
+ {
+ for (size_t i=0; i<vertexCount; ++i)
+ {
+ *(reinterpret_cast<UInt32*> (dstPtr) + 0) = *(reinterpret_cast<const UInt32*> (srcPtr) + 0);
+ *(reinterpret_cast<UInt32*> (dstPtr) + 1) = *(reinterpret_cast<const UInt32*> (srcPtr) + 1);
+ srcPtr += srcStride;
+ dstPtr += dstStride;
+ }
+ break;
+ }
+ case 12:
+ {
+ for (size_t i=0; i<vertexCount; ++i)
+ {
+ *(reinterpret_cast<UInt32*> (dstPtr) + 0) = *(reinterpret_cast<const UInt32*> (srcPtr) + 0);
+ *(reinterpret_cast<UInt32*> (dstPtr) + 1) = *(reinterpret_cast<const UInt32*> (srcPtr) + 1);
+ *(reinterpret_cast<UInt32*> (dstPtr) + 2) = *(reinterpret_cast<const UInt32*> (srcPtr) + 2);
+ srcPtr += srcStride;
+ dstPtr += dstStride;
+ }
+ break;
+ }
+ default:
+ {
+ for (size_t i=0; i<vertexCount; ++i)
+ {
+ memcpy (dstPtr, srcPtr, copySize);
+ srcPtr += srcStride;
+ dstPtr += dstStride;
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ ConvertCopyChannel(vertexCount, srcPtr, srcStride, srcChannel.format, srcChannel.dimension, dstPtr, dstStride, dstChannel.format, dstChannel.dimension);
+ }
+ }
+}
+
+VertexDataInfo::VertexDataInfo ()
+: m_Data(NULL)
+, m_DataSize(0)
+, m_VertexCount(0)
+, m_VertexSize(0)
+, m_CurrentChannels(0)
+{
+ // Channels and streams have default constructors
+}
+
+VertexData::VertexData (VertexData const& src, unsigned copyChannels, const VertexStreamsLayout& streams, const VertexChannelsLayout& channels)
+{
+ // We do not support inserting new channels that are not present in the source
+ Assert ((copyChannels & src.GetChannelMask()) == copyChannels);
+
+ UpdateStreams(copyChannels, src.m_VertexCount, streams, channels);
+ m_Data = (UInt8*) UNITY_MALLOC_ALIGNED (kMemVertexData, VertexData::GetAllocateDataSize (m_DataSize), kVertexDataAlign);
+
+ const VertexData& dest = *this;
+ if (m_DataSize == src.m_DataSize &&
+ copyChannels == src.GetChannelMask() &&
+ CompareMemory(dest.m_Channels, src.m_Channels) &&
+ CompareMemory(dest.m_Streams, src.m_Streams))
+ {
+ // Simple copy if the format didn't change
+ memcpy (m_Data, src.m_Data, m_DataSize);
+ }
+ else
+ CopyChannels (m_VertexCount, copyChannels, src.m_Streams, src.m_Channels, src.m_Data, m_Streams, m_Channels, m_Data);
+}
+
+VertexData::~VertexData ()
+{
+ Deallocate();
+}
+
+void VertexData::Deallocate ()
+{
+ if (m_Data)
+ UNITY_FREE(kMemVertexData, m_Data);
+ m_Data = NULL;
+}
+
+void VertexData::Resize (size_t vertexCount, unsigned channelMask, const VertexStreamsLayout& streams, const VertexChannelsLayout& channels)
+{
+ ChannelInfoArray srcChannels;
+ StreamInfoArray srcStreams;
+ memcpy(srcChannels, m_Channels, sizeof(srcChannels));
+ memcpy(srcStreams, m_Streams, sizeof(srcStreams));
+ UInt32 srcChannelMask = m_CurrentChannels;
+ UInt32 srcVertexCount = m_VertexCount;
+ UInt8* srcData = m_Data;
+
+ UpdateStreams(channelMask, vertexCount, streams, channels);
+
+ // In case the streams and channels don't change, simply reallocate the buffer and return
+ // Note that this will rarely be true with multiple streams since the stream offsets change
+ if (m_Data && CompareMemory(srcChannels, m_Channels) && CompareMemory(srcStreams, m_Streams))
+ {
+ m_Data = (UInt8*)UNITY_REALLOC_ALIGNED(kMemVertexData, m_Data, VertexData::GetAllocateDataSize(m_DataSize), kVertexDataAlign);
+ return;
+ }
+
+ m_Data = (UInt8*)UNITY_MALLOC_ALIGNED(kMemVertexData, VertexData::GetAllocateDataSize(m_DataSize), kVertexDataAlign);
+ // copy over the old data
+ if (srcData)
+ {
+ unsigned copyChannels = srcChannelMask & m_CurrentChannels;
+ size_t toCopyCount = std::min<size_t>(srcVertexCount, m_VertexCount);
+ CopyChannels(toCopyCount, copyChannels, srcStreams, srcChannels, srcData, m_Streams, m_Channels, m_Data);
+ UNITY_FREE(kMemVertexData, srcData);
+ }
+}
+
+
+void VertexData::SwapEndianess ()
+{
+ unsigned const kChannelSwapMask = VERTEX_FORMAT5(Vertex, Normal, TexCoord0, TexCoord1, Tangent);
+ for (int s = 0; s < kMaxVertexStreams; s++)
+ {
+ if (m_Streams[s].stride)
+ {
+ StreamInfo& stream = m_Streams[s];
+ size_t stride = stream.stride;
+ UInt8* dataStart = m_Data + stream.offset;
+ UInt8* dataEnd = dataStart + stream.stride * m_VertexCount;
+ UInt32 channelMask = stream.channelMask;
+ for (UInt8* p = dataStart, *end = dataEnd; p != end; p += stride)
+ {
+ // counting from LSb, 1 denotes that a value should be endian-swapped
+ int localOffset = 0;
+ for (unsigned i=0, chan = channelMask, swap = kChannelSwapMask; i<kShaderChannelCount; ++i, chan >>= 1, swap >>= 1)
+ {
+ if (chan & 1)
+ {
+ size_t componentCount = m_Channels[i].dimension;
+ size_t componentSize = GetChannelFormatSize(m_Channels[i].format);
+ if(swap & 1)
+ {
+ Assert (m_Channels [i].IsValid());
+ SwapEndianArray (p + localOffset, componentSize, componentCount);
+ }
+ localOffset += componentCount * componentSize;
+ }
+ }
+ }
+ }
+ }
+}
+
+void swap (VertexData& a, VertexData& b)
+{
+ std::swap_ranges (a.m_Channels, a.m_Channels + kShaderChannelCount, b.m_Channels);
+ std::swap_ranges (a.m_Streams, a.m_Streams + kMaxVertexStreams, b.m_Streams);
+ std::swap (a.m_CurrentChannels, b.m_CurrentChannels);
+ std::swap (a.m_VertexSize, b.m_VertexSize);
+ std::swap (a.m_VertexCount, b.m_VertexCount);
+ std::swap (a.m_DataSize, b.m_DataSize);
+ std::swap (a.m_Data, b.m_Data);
+}
+
+void CopyVertexDataChannels (size_t vertexCount, unsigned copyChannels, const VertexData& srcData, VertexData& dstData)
+{
+ Assert (vertexCount <= srcData.GetVertexCount() && vertexCount <= dstData.GetVertexCount());
+ Assert ((srcData.GetChannelMask() & copyChannels) == copyChannels);
+ Assert ((dstData.GetChannelMask() & copyChannels) == copyChannels);
+ CopyChannels (vertexCount, copyChannels,
+ srcData.GetStreams(), srcData.GetChannels(), srcData.GetDataPtr(),
+ dstData.GetStreams(), dstData.GetChannels(), dstData.GetDataPtr());
+}
+
diff --git a/Runtime/Filters/Mesh/VertexData.h b/Runtime/Filters/Mesh/VertexData.h
new file mode 100644
index 0000000..7cc6c98
--- /dev/null
+++ b/Runtime/Filters/Mesh/VertexData.h
@@ -0,0 +1,253 @@
+#ifndef VERTEX_DATA_H_
+#define VERTEX_DATA_H_
+
+#include "Runtime/Utilities/StrideIterator.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Serialize/TransferFunctionFwd.h"
+
+class VertexData;
+
+void swap (VertexData& a, VertexData& b);
+
+typedef struct StreamInfo
+{
+ enum { kDividerOpDivide=0, kDividerOpModulo };
+
+ UInt32 channelMask;
+ UInt32 offset;
+ UInt16 frequency;
+ UInt8 stride;
+ UInt8 dividerOp;
+
+ // We use default constructors instead of memset()
+ StreamInfo() : channelMask(0), offset(0), frequency(0), stride(0), dividerOp(kDividerOpDivide) {}
+ void Reset() { *this = StreamInfo(); }
+
+ bool operator == (const StreamInfo& rhs) const { return (channelMask == rhs.channelMask) && (offset == rhs.offset) && (frequency == rhs.frequency) && (stride == rhs.stride) && (dividerOp == rhs.dividerOp); }
+ bool operator != (const StreamInfo& rhs) const { return !(*this == rhs); }
+
+ DECLARE_SERIALIZE_NO_PPTR (StreamInfo);
+
+#if SUPPORT_SERIALIZED_TYPETREES
+ template<class TransferFunction>
+ void TransferWorkaround35SerializationFuckup (TransferFunction& transfer);
+#endif
+
+} StreamInfoArray [kMaxVertexStreams];
+
+struct VertexStreamsLayout
+{
+ UInt32 channelMasks[kMaxVertexStreams];
+};
+
+typedef struct ALIGN_TYPE(4) ChannelInfo
+{
+ UInt8 stream;
+ UInt8 offset;
+ UInt8 format;
+ UInt8 dimension;
+
+ enum { kInvalidDimension = 0 };
+
+ // We use default constructors instead of memset()
+ ChannelInfo() : stream(0), offset(0), format(0), dimension(kInvalidDimension) {}
+
+ UInt32 CalcOffset(const StreamInfoArray streams) const { return streams[stream].offset + offset; }
+ UInt32 CalcStride(const StreamInfoArray streams) const { return streams[stream].stride; }
+ bool IsValid() const { return (kInvalidDimension != dimension); }
+ void Reset() { *this = ChannelInfo(); }
+
+ bool operator == (const ChannelInfo& rhs) const { return (stream == rhs.stream) && (offset == rhs.offset) && (format == rhs.format) && (dimension == rhs.dimension); }
+ bool operator != (const ChannelInfo& rhs) const { return !(*this == rhs); }
+
+ DECLARE_SERIALIZE_NO_PPTR (ChannelInfo);
+
+} ChannelInfoArray [kShaderChannelCount];
+
+struct VertexChannelsLayout
+{
+ struct Channel
+ {
+ Channel(UInt8 fmt, UInt8 dim) : format(fmt), dimension(dim) {}
+ Channel() : format(0), dimension(0) {}
+ UInt8 format;
+ UInt8 dimension;
+ };
+ Channel channels[kShaderChannelCount];
+};
+
+
+template<class TransferFunc>
+void StreamInfo::Transfer (TransferFunc& transfer)
+{
+ #if SUPPORT_SERIALIZED_TYPETREES
+ if (transfer.GetFlags() & kWorkaround35MeshSerializationFuckup)
+ {
+ TransferWorkaround35SerializationFuckup (transfer);
+ return;
+ }
+ #endif
+
+ transfer.Transfer (channelMask, "channelMask", kHideInEditorMask);
+ transfer.Transfer (offset, "offset", kHideInEditorMask);
+ transfer.Transfer (stride, "stride", kHideInEditorMask);
+ transfer.Transfer (dividerOp, "dividerOp", kHideInEditorMask);
+ transfer.Transfer (frequency, "frequency", kHideInEditorMask);
+}
+
+#if SUPPORT_SERIALIZED_TYPETREES
+template<class TransferFunc>
+void StreamInfo::TransferWorkaround35SerializationFuckup (TransferFunc& transfer)
+{
+ transfer.Transfer (channelMask, "channelMask", kHideInEditorMask);
+ transfer.Transfer (offset, "offset", kHideInEditorMask);
+
+ UInt32 align;
+ UInt32 stride32bit;
+ transfer.Transfer (stride32bit, "stride", kHideInEditorMask);
+ transfer.Transfer (align, "align", kHideInEditorMask);
+
+ stride = (UInt8) stride32bit;
+}
+#endif
+
+template<class TransferFunc>
+void ChannelInfo::Transfer (TransferFunc& transfer)
+{
+ transfer.Transfer (stream, "stream", kHideInEditorMask);
+ transfer.Transfer (offset, "offset", kHideInEditorMask);
+ transfer.Transfer (format, "format", kHideInEditorMask);
+ transfer.Transfer (dimension, "dimension", kHideInEditorMask);
+}
+
+// Information about all vertex data, but does not own the memory
+class VertexDataInfo
+{
+public:
+ enum
+ {
+ kVertexDataAlign = 32,
+ kVertexStreamAlign = 16,
+ kVertexDataPadding = 16
+ };
+
+ static VertexStreamsLayout kVertexStreamsDefault;
+ static VertexStreamsLayout kVertexStreamsSkinnedHotColdSplit;
+ static VertexChannelsLayout kVertexChannelsDefault;
+ static VertexChannelsLayout kVertexChannelsCompressed;
+ static VertexChannelsLayout kVertexChannelsCompressedAggressive;
+#if UNITY_EDITOR
+ static VertexStreamsLayout kVertexStreamsSkinnedHotColdSplitPS3;
+#endif
+
+ static size_t AlignStreamSize (size_t size) { return (size + (kVertexStreamAlign-1)) & ~(kVertexStreamAlign-1); }
+
+ friend void ::swap (VertexData& a, VertexData& b);
+
+ VertexDataInfo ();
+
+ bool HasChannel (ShaderChannel shaderChannelIndex) const
+ {
+ Assert ((m_Channels[shaderChannelIndex].dimension != 0) == (((m_CurrentChannels & (1 << shaderChannelIndex)) != 0)));
+ return m_Channels[shaderChannelIndex].dimension != 0;
+ }
+
+ void UpdateStreams(unsigned newChannelMask, size_t newVertexCount, const VertexStreamsLayout& streams = kVertexStreamsDefault, const VertexChannelsLayout& channels = kVertexChannelsDefault);
+
+ size_t GetActiveStreamCount() const ;
+ size_t GetStreamIndex(ShaderChannel channel) const ;
+ const StreamInfo* GetStreams() const { return m_Streams; }
+ const StreamInfo& GetStream(int index) const { return m_Streams[index]; }
+
+ const ChannelInfo* GetChannels() const { return m_Channels; }
+ const ChannelInfo& GetChannel(int index) const { return m_Channels[index]; }
+
+ VertexStreamsLayout GetStreamsLayout() const;
+ VertexChannelsLayout GetChannelsLayout() const;
+
+ bool ConformsToStreamsLayout(const VertexStreamsLayout& streams) const;
+ bool ConformsToChannelsLayout(const VertexChannelsLayout& channels) const;
+
+ unsigned GetChannelMask () const { return m_CurrentChannels; }
+ size_t GetDataSize () const { return m_DataSize; }
+ size_t GetVertexSize () const { return m_VertexSize; }
+ size_t GetVertexCount () const { return m_VertexCount; }
+ size_t GetChannelOffset (unsigned channel) const { return m_Channels[channel].CalcOffset(m_Streams); }
+ size_t GetChannelStride (unsigned channel) const { return m_Channels[channel].CalcStride(m_Streams); }
+ UInt8* GetDataPtr () const { return m_Data; }
+
+ template<class T>
+ StrideIterator<T> MakeStrideIterator (ShaderChannel shaderChannelIndex) const
+ {
+ Assert (shaderChannelIndex < kShaderChannelCount);
+ void* p = m_Data + GetChannelOffset(shaderChannelIndex);
+ return HasChannel (shaderChannelIndex) ? StrideIterator<T> (p, GetChannelStride (shaderChannelIndex)) : StrideIterator<T> (NULL, GetChannelStride (shaderChannelIndex));
+ }
+
+ template<class T>
+ StrideIterator<T> MakeEndIterator (ShaderChannel shaderChannelIndex) const
+ {
+ T* end = GetEndPointer<T> (shaderChannelIndex);
+ return StrideIterator<T> (end, GetChannelStride (shaderChannelIndex));
+ }
+
+ template<class T>
+ T* GetEndPointer (ShaderChannel shaderChannelIndex) const
+ {
+ Assert (shaderChannelIndex < kShaderChannelCount);
+ void* p = HasChannel (shaderChannelIndex) ? (m_Data + GetChannelOffset(shaderChannelIndex) + m_VertexCount * GetChannelStride (shaderChannelIndex)) : NULL;
+ return reinterpret_cast<T*> (p);
+ }
+
+protected:
+ ChannelInfoArray m_Channels;
+ StreamInfoArray m_Streams;
+
+ size_t m_VertexSize; // must match m_CurrentChannels
+ UInt8* m_Data;
+
+ // The following are being serialized. Their size must match in both 32 and 64 bit platforms
+ UInt32 m_CurrentChannels; // kShaderChannel bitmask
+ UInt32 m_VertexCount;
+ unsigned m_DataSize;
+};
+
+
+// Owns the vertex memory
+class VertexData : public VertexDataInfo
+{
+public:
+
+ DECLARE_SERIALIZE (VertexData)
+
+ VertexData () : VertexDataInfo() { }
+ VertexData (VertexData const& src, unsigned copyChannels, const VertexStreamsLayout& streams = kVertexStreamsDefault, const VertexChannelsLayout& channels = kVertexChannelsDefault);
+ ~VertexData ();
+
+ static size_t GetAllocateDataSize (size_t accesibleBufferSize) { return accesibleBufferSize + kVertexDataPadding; }
+
+ void Deallocate ();
+ void Resize (size_t vertexCount, unsigned channelMask, const VertexStreamsLayout& streams = kVertexStreamsDefault, const VertexChannelsLayout& channels = kVertexChannelsDefault);
+ void SwapEndianess ();
+
+private:
+ VertexData (const VertexData& o);
+ void operator= (const VertexData& o);
+ VertexData (const VertexDataInfo& o);
+ void operator= (const VertexDataInfo& o);
+
+#if SUPPORT_SERIALIZED_TYPETREES
+ template<class TransferFunction>
+ void TransferWorkaround35SerializationFuckup (TransferFunction& transfer);
+#endif
+};
+
+
+void CopyVertexDataChannels (size_t vertexCount, unsigned copyChannels, const VertexData& srcData, VertexData& dstData);
+size_t GetChannelFormatSize(UInt8 format);
+
+
+
+#endif
diff --git a/Runtime/Filters/Misc/DynamicFontFreeType.cpp b/Runtime/Filters/Misc/DynamicFontFreeType.cpp
new file mode 100644
index 0000000..042e328
--- /dev/null
+++ b/Runtime/Filters/Misc/DynamicFontFreeType.cpp
@@ -0,0 +1,510 @@
+#include "UnityPrefix.h"
+#include "Font.h"
+
+#if UNITY_WIN
+#include "PlatformDependent/Win/Registry.h"
+#endif
+
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include FT_BITMAP_H
+
+
+DynamicFontData::DynamicFontData()
+{
+}
+
+DynamicFontData::~DynamicFontData ()
+{
+ for (FaceMap::iterator i=m_Faces.begin(); i!=m_Faces.end(); i++)
+ FT_Done_Face(i->second);
+}
+
+static FT_Library g_ftLib;
+static bool g_ftLibInit = false;
+static FT_Bitmap g_bitmap8bpp;
+static bool g_bitmap8bppInit = false;
+
+struct OSFont
+{
+ OSFont () :
+ index(0)
+ {}
+
+ OSFont (std::string _path, int _index) :
+ path(_path),
+ index(_index)
+ {}
+
+ bool operator != (const OSFont& other) const
+ {
+ return index != other.index || path != other.path;
+ }
+
+ std::string path;
+ int index;
+};
+typedef std::map<FontRef,OSFont> OSFontMap;
+static OSFontMap* gOSFontMap = NULL; // Maps family names to files
+
+
+namespace DynamicFontMap
+{
+ void StaticInitialize ()
+ {
+ gOSFontMap = UNITY_NEW (OSFontMap, kMemFont);
+ }
+ void StaticDestroy ()
+ {
+ UNITY_DELETE (gOSFontMap, kMemFont);
+ }
+}
+static float ConvertFixed26(long val)
+{
+ float f = (float)(val >> 6);
+ val = val & 0x3F;
+ f += (float)val/(2^6 - 1);
+ return f;
+}
+
+static float ConvertFixed16(long val)
+{
+ float f = (float)(val >> 16);
+ val = val & 0xFFFF;
+ f += (float)val/(2^16 - 1);
+ return f;
+}
+
+static long ConvertFloat26(float val)
+{
+ return (long)(val * (1 << 6));
+}
+
+static long ConvertFloat16(float val)
+{
+ return (long)(val * (1 << 16));
+}
+
+static unsigned int FreeTypeStyleToUnity (int style_flags)
+{
+ unsigned int style = 0;
+ if (style_flags & FT_STYLE_FLAG_ITALIC)
+ style |= kStyleFlagItalic;
+ if (style_flags & FT_STYLE_FLAG_BOLD)
+ style |= kStyleFlagBold;
+ return style;
+}
+
+static bool IsExpectedFontStyle(const char* style_name, unsigned int style)
+{
+ switch (style)
+ {
+ case kStyleDefault:
+ return (strcmp(style_name, "Regular") == 0);
+ case kStyleFlagItalic:
+ return (strcmp(style_name, "Italic") == 0);
+ case kStyleFlagBold:
+ return (strcmp(style_name, "Bold") == 0);
+ case kStyleFlagBold | kStyleFlagItalic:
+ return (strcmp(style_name, "Bold Italic") == 0);
+ }
+ return false;
+}
+
+bool GetFontMetadata(const std::string& path, std::string& family_name, std::string& style_name, unsigned& style_flags, unsigned& face_flags, int *faceIndex)
+{
+ std::string shortName = GetFileNameWithoutExtension(path);
+
+ // Check, maybe we already have font metadata in our preset table
+ if (GetFontMetadataPreset(shortName, family_name, style_name, style_flags, face_flags))
+ return true;
+
+ bool res = false;
+ FT_Face face;
+ if(FT_New_Face(g_ftLib, path.c_str(), *faceIndex, &face) == 0)
+ {
+ *faceIndex = face->num_faces;
+ if(face->family_name != NULL)
+ {
+ //Uncomment this line when font metadata needs to be rebuilt for newer OS/hw versions
+ //printf_console("\tgFontMetadata[\"%s\"] = (_FontInfo){\"%s\", \"%s\", 0x%x, 0x%x};\n", shortName.c_str(), face->family_name, face->style_name, face->style_flags, face->face_flags);
+
+ family_name = face->family_name;
+ style_name = face->style_name;
+ style_flags = face->style_flags;
+ face_flags = face->face_flags;
+ res = true;
+ }
+
+ FT_Done_Face(face);
+ }
+
+ return res;
+}
+
+void ReadFontFiles()
+{
+ DynamicFontMap::StaticInitialize();
+
+ std::vector<std::string> paths;
+ GetFontPaths(paths);
+
+ for(int folderIndex = 0; folderIndex < paths.size(); ++folderIndex)
+ {
+
+ std::string &path = paths[folderIndex];
+
+ std::string family_name, style_name;
+ unsigned style_flags, face_flags;
+ int numFaces = 1;
+ for (int fontIndex=0; fontIndex<numFaces; fontIndex++)
+ {
+ int faceIndex = fontIndex;
+ if (GetFontMetadata(path, family_name, style_name, style_flags, face_flags, &faceIndex))
+ {
+ numFaces = faceIndex;
+ if((face_flags & FT_FACE_FLAG_SCALABLE) == 0)
+ continue;
+
+ FontRef r(family_name.c_str(), FreeTypeStyleToUnity(style_flags));
+ OSFont font (path, fontIndex);
+ OSFontMap::iterator i = gOSFontMap->find(r);
+ if (i != gOSFontMap->end())
+ {
+ // There already is a font for this family and style
+ if (i->second != font)
+ {
+ // Try to pick the one with the expected style name.
+ // This way we don't accidentally catch fonts like "Arial Black" when we want "Arial",
+ // as both will show with family_name=="Arial" and style_flags==0. In such cases, pick
+ // the one with the style name matching what we'd expect from the style flags, if possible.
+ if (IsExpectedFontStyle(style_name.c_str(), r.style))
+ (*gOSFontMap)[r] = font;
+ }
+ }
+ else
+ (*gOSFontMap)[r] = font;
+ }
+ }
+ }
+}
+
+static OSFont SelectFont(FontRef &r)
+{
+ // Initialize font map. Do this on demand here
+ // as it takes a significant amount of time to go through
+ // all of the system's fonts.
+ if (!gOSFontMap)
+ {
+ ReadFontFiles();
+ Assert (gOSFontMap);
+ }
+
+ OSFontMap::iterator i = gOSFontMap->find(r);
+ if (i != gOSFontMap->end())
+ return i->second;
+ return OSFont();
+}
+
+FT_Face DynamicFontData::GetFaceForFontRef (FontRef &r, FontFallbacks &fallbacks)
+{
+ FaceMap::iterator i = m_Faces.find(r);
+ if (i != m_Faces.end())
+ return (i->second);
+ else
+ {
+ // First see if any of the fallback fonts in the project contain the needed font.
+ for (FontFallbacks::iterator j=fallbacks.begin(); j != fallbacks.end(); j++)
+ {
+ if (j->IsValid())
+ {
+ i = (**j).m_DynamicData.m_Faces.find(r);
+ if (i != (**j).m_DynamicData.m_Faces.end())
+ return (i->second);
+ }
+ }
+
+ // Then look at the system font files.
+ OSFont font = SelectFont(r);
+ if (!font.path.empty())
+ {
+ FT_New_Face(g_ftLib, font.path.c_str(), font.index, &m_Faces[r]);
+ return m_Faces[r];
+ }
+ }
+ return NULL;
+}
+
+FT_Face DynamicFontData::GetFaceForCharacterIfAvailableInFont (FontRef &r, FontFallbacks &fallbacks, unsigned int unicodeChar)
+{
+ // Do we have a font for the requested name and style which has the character?
+ FT_Face face = GetFaceForFontRef(r, fallbacks);
+ if (face != NULL)
+ {
+ if (FT_Get_Char_Index(face, unicodeChar))
+ return face;
+ }
+
+ // If not, try with default style (if we had something else requested).
+ if (r.style)
+ {
+ FontRef r2 = r;
+ r2.style = 0;
+ face = GetFaceForFontRef(r2, fallbacks);
+ if (face != NULL)
+ {
+ if (FT_Get_Char_Index(face, unicodeChar))
+ return face;
+ }
+ }
+ return NULL;
+}
+
+FT_Face DynamicFontData::GetFaceForCharacter (FontNames &fonts, FontFallbacks &fallbacks, unsigned int style, unsigned int unicodeChar)
+{
+ // Check if any of the fonts in the fallback names serialized with the font has the character
+ for (FontNames::iterator font = fonts.begin();font != fonts.end();font++)
+ {
+ // First check if we find the font by it's full name (as that
+ // is what's used for the embedded ttf data, if any)
+ unsigned int st = style;
+ std::string name = *font;
+ FontRef r (name, st);
+ FT_Face face = GetFaceForCharacterIfAvailableInFont (r, fallbacks, unicodeChar);
+ if (face != NULL)
+ return face;
+
+ // If that did not find anything, remove and parse potential style names, as OS fonts
+ // are identified by family name and style flags.
+ size_t pos = name.find(" Bold");
+ if (pos != std::string::npos)
+ {
+ name = name.substr(0,pos)+name.substr(pos+5);
+ st |= kStyleFlagBold;
+ }
+ pos = name.find(" Italic");
+ if (pos != std::string::npos)
+ {
+ name = name.substr(0,pos)+name.substr(pos+7);
+ st |= kStyleFlagItalic;
+ }
+ r = FontRef(name, st);
+ face = GetFaceForCharacterIfAvailableInFont (r, fallbacks, unicodeChar);
+ if (face != NULL)
+ return face;
+ }
+ // If not, fall back to the global fallbacks.
+ FontNames &globalFallbacks = GetFallbacks();
+ for (FontNames::iterator font = globalFallbacks.begin();font != globalFallbacks.end();font++)
+ {
+ FontRef r (*font, style);
+ FT_Face face = GetFaceForCharacterIfAvailableInFont (r, fallbacks, unicodeChar);
+ if (face != NULL)
+ return face;
+ }
+
+ return NULL;
+}
+
+bool Font::HasCharacterDynamic (unsigned int unicodeChar)
+{
+ return unicodeChar >= 32;
+}
+
+int GetLoadTarget (int fontsize, int mode)
+{
+ switch (mode)
+ {
+ case kFontRenderingModeSmooth:
+ return FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING;
+ case kFontRenderingModeHintedSmooth:
+ return FT_LOAD_TARGET_NORMAL;
+ case kFontRenderingModeHintedRaster:
+ return FT_LOAD_TARGET_MONO;
+ case kFontRenderingModeOSDefault:
+ #if UNITY_WINRT
+ return FT_LOAD_TARGET_NORMAL;
+ #elif UNITY_WIN
+ {
+ static bool smoothing = registry::getString( "Control Panel\\Desktop", "FontSmoothing", "2" ) == "2";
+ if (smoothing)
+ return FT_LOAD_TARGET_NORMAL;
+ else
+ return FT_LOAD_TARGET_MONO;
+ }
+ #elif UNITY_OSX
+ static int antiAliasingTreshold = -1;
+ if (antiAliasingTreshold == -1)
+ {
+ Boolean exists;
+ antiAliasingTreshold = CFPreferencesGetAppIntegerValue(CFSTR("AppleAntiAliasingThreshold"),CFSTR("Apple Global Domain"),&exists);
+ if (!exists)
+ antiAliasingTreshold = 4;
+ }
+ if (fontsize <= antiAliasingTreshold)
+ return FT_LOAD_TARGET_MONO;
+ else
+ return FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING;
+ #else
+ return FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING;
+ #endif
+ default:
+ ErrorString("Unknown font rendering mode.");
+ return FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING;
+ }
+}
+
+UInt8 *Font::GetCharacterBitmap(unsigned int &charWidth, unsigned int &charHeight, unsigned int &bufferWidth, Rectf &vert, float &advance, unsigned int unicodeChar, int size, unsigned int style)
+{
+ if(size == 0)
+ size = m_FontSize;
+
+ FT_Face face = m_DynamicData.GetFaceForCharacter(m_FontNames, m_FallbackFonts, style, unicodeChar);
+ if (face == NULL)
+ {
+ // If we don't find a fallback Font in the OS, try built-in default font.
+ // Needed for Platforms without access to OS fonts (like NaCl, which has no file system access).
+ Font *builtinFont = GetBuiltinResource<Font> (kDefaultFontName);
+ if (builtinFont)
+ face = builtinFont->m_DynamicData.GetFaceForCharacter(m_FontNames, builtinFont->m_FallbackFonts, style, unicodeChar);
+
+ if (face == NULL)
+ return NULL;
+ }
+
+ unsigned int faceStyle = FreeTypeStyleToUnity(face->style_flags);
+ // Perform transformations needed for bold/italic styles if the font does not natively support it.
+ FT_Matrix m;
+ if (!(faceStyle & kStyleFlagBold) && style & kStyleFlagBold)
+ m.xx = ConvertFloat16(1.25f);
+ else
+ m.xx = ConvertFloat16(1);
+ if (!(faceStyle & kStyleFlagItalic) && style & kStyleFlagItalic)
+ m.xy = ConvertFloat16(0.25f);
+ else
+ m.xy = ConvertFloat16(0);
+ m.yy = ConvertFloat16(1);
+ m.yx = ConvertFloat16(0);
+ FT_Set_Transform(face, &m, NULL);
+
+ FT_Set_Char_Size(face, 0, ConvertFloat26(size), 72, 72);
+
+ FT_UInt glyph = FT_Get_Char_Index(face, unicodeChar);
+ if(glyph != 0)
+ {
+ int loadTarget = FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING;
+ loadTarget = GetLoadTarget(size, m_FontRenderingMode);
+ if( FT_Load_Glyph(face, glyph, loadTarget ) == 0 )
+ {
+ bool glyphRendered = true;
+ if(face->glyph->format != FT_GLYPH_FORMAT_BITMAP )
+ glyphRendered = ( FT_Render_Glyph(face->glyph, FT_LOAD_TARGET_MODE(loadTarget) ) == 0 );
+
+ if(glyphRendered)
+ {
+ FT_Bitmap *srcBitmap = 0;
+ FT_Bitmap& bitmap =face->glyph->bitmap;
+
+ if(bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
+ {
+ if(!g_bitmap8bppInit)
+ {
+ FT_Bitmap_New(&g_bitmap8bpp);
+ g_bitmap8bppInit = true;
+ }
+ FT_Bitmap_Convert(g_ftLib, &bitmap, &g_bitmap8bpp, 4);
+ srcBitmap = &g_bitmap8bpp;
+ if (srcBitmap->num_grays != 256)
+ {
+ float factor = 1.0f/(srcBitmap->num_grays-1) * 255;
+ for (int i=0; i<srcBitmap->pitch*srcBitmap->rows; i++)
+ srcBitmap->buffer[i] *= factor;
+ }
+ }
+ else
+ {
+ srcBitmap = &bitmap;
+ }
+
+ charWidth = srcBitmap->width;
+ charHeight = srcBitmap->rows;
+ bufferWidth = srcBitmap->pitch;
+ vert = Rectf(face->glyph->bitmap_left,
+ face->glyph->bitmap_top - m_Ascent,
+ (float)charWidth,
+ -(float)charHeight);
+ advance = Roundf(face->glyph->metrics.horiAdvance / 64.0);
+
+ if (srcBitmap->width*srcBitmap->rows == 0)
+ return NULL;
+
+ return srcBitmap->buffer;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void Font::SetupDynamicFont ()
+{
+ Assert(g_ftLibInit);
+
+ if(!m_FontData.empty())
+ {
+ FT_Face face = NULL;
+ if(FT_New_Memory_Face(g_ftLib, (const FT_Byte*)&m_FontData[0], m_FontData.size(), 0, &face) != 0)
+ {
+ ErrorString("Failed to load font from memory");
+ }
+ else
+ {
+ // So we don't crash if we have a font where FreeType does not understand the name.
+ // Unity 4.x will refuse to import such fonts, but it can happen with content made with 3.x,
+ // which did not use FT to import the font.
+ if (face->family_name == NULL)
+ face->family_name = (FT_String*)"Unreadeable font name.";
+
+ // Make sure the name of the memory font is the first in the list of fonts to use.
+ if (strcmp(m_FontNames[0].c_str(), face->family_name) != 0)
+ m_FontNames.insert(m_FontNames.begin(), face->family_name);
+
+ FontRef r (face->family_name, FreeTypeStyleToUnity(face->style_flags));
+ m_DynamicData.m_Faces[r] = face;
+ if (r.style != 0)
+ {
+ r.style = 0;
+ if (FT_New_Memory_Face(g_ftLib, (const FT_Byte*)&m_FontData[0], m_FontData.size(), 0, &face) == 0)
+ m_DynamicData.m_Faces[r] = face;
+ }
+ }
+ }
+}
+
+void Font::InitializeClass()
+{
+ GetFontsManager::StaticInitialize();
+ if(FT_Init_FreeType( &g_ftLib ) != 0)
+ {
+ ErrorString("Could not initialize FreeType");
+ }
+ g_ftLibInit = true;
+}
+
+void Font::CleanupClass()
+{
+ if(g_bitmap8bppInit)
+ {
+ FT_Bitmap_Done(g_ftLib, &g_bitmap8bpp);
+ g_bitmap8bppInit = false;
+ }
+ if(g_ftLibInit)
+ {
+ FT_Done_FreeType(g_ftLib);
+ g_ftLibInit = false;
+ }
+ DynamicFontMap::StaticDestroy();
+ GetFontsManager::StaticDestroy();
+}
+
diff --git a/Runtime/Filters/Misc/DynamicFontFreeType.h b/Runtime/Filters/Misc/DynamicFontFreeType.h
new file mode 100644
index 0000000..4bfdb35
--- /dev/null
+++ b/Runtime/Filters/Misc/DynamicFontFreeType.h
@@ -0,0 +1,47 @@
+#ifndef DYNAMICFONTFREETYPE_H
+#define DYNAMICFONTFREETYPE_H
+
+#if DYNAMICFONTMODE == kDynamicFontModeFreeType
+
+#include "External/freetype2/include/ft2build.h"
+#include FT_FREETYPE_H
+//#include FT_GLYPH_H
+
+namespace DynamicFontMap
+{
+ void StaticInitialize();
+ void StaticDestroy();
+}
+
+struct FontRef
+{
+ std::string family;
+ unsigned int style;
+
+ FontRef(const std::string& _family, unsigned int _style) : family(_family), style(_style) {}
+
+ bool operator < (const FontRef& other) const {
+ if (family < other.family)
+ return true;
+ else if (family > other.family)
+ return false;
+ return style < other.style;
+ }
+};
+
+typedef std::map<FontRef, FT_Face> FaceMap;
+
+struct DynamicFontData {
+ DynamicFontData();
+ ~DynamicFontData();
+
+ FT_Face GetFaceForFontRef (FontRef &r, FontFallbacks &fallbacks);
+ FT_Face GetFaceForCharacter (FontNames &fonts, FontFallbacks &fallbacks, unsigned int style, unsigned int unicodeChar);
+ FT_Face GetFaceForCharacterIfAvailableInFont (FontRef &r, FontFallbacks &fallbacks, unsigned int unicodeChar);
+
+ FaceMap m_Faces;
+};
+
+#endif
+
+#endif
diff --git a/Runtime/Filters/Misc/Font.cpp b/Runtime/Filters/Misc/Font.cpp
new file mode 100644
index 0000000..57d84f3
--- /dev/null
+++ b/Runtime/Filters/Misc/Font.cpp
@@ -0,0 +1,845 @@
+#include "UnityPrefix.h"
+#include "Font.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Shaders/Material.h"
+#include "TextMesh.h"
+#if UNITY_EDITOR
+#include "Editor/Src/AssetPipeline/TrueTypeFontImporter.h"
+#endif
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Graphics/Texture.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/IMGUI/TextMeshGenerator2.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/Scripting.h"
+
+using namespace std;
+
+// on android we can recreate gles context and loose gpu-side texture copy
+// on editor we want to have it to write it later on build
+#define NEEDS_SYSTEM_MEM_COPY UNITY_ANDROID || UNITY_EDITOR
+
+PROFILER_INFORMATION(gFontTextureCacheProfile, "Font.CacheFontForText", kProfilerRender)
+
+unsigned int Font::s_FrameCount = 0;
+
+Font::Font(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ // dynamic font stuff
+ m_TexWidth = 256;
+ m_TexHeight = 256;
+ m_SubImageSize = 1;
+ m_CharacterSpacing = 1;
+ m_CharacterPadding = 0;
+ m_FontSize = 0;
+ m_Ascent = 0.0f;
+ m_DefaultStyle = kStyleDefault;
+ m_FontRenderingMode = kFontRenderingModeSmooth;
+
+ m_PixelScale = 0.1f;
+
+ m_AsciiCharacterRects.clear();
+ m_AsciiCharacterRects.resize (256);
+
+ m_TexturePositions.insert (TexturePosition (0, 0));
+ m_TexturePositionsSearchPosition = m_TexturePositions.begin();
+}
+
+Font::~Font ()
+{
+}
+
+void Font::Reset ()
+{
+ Super::Reset();
+
+ m_Kerning = 1.0F;
+ m_LineSpacing = 0.1F;
+ m_AsciiStartOffset = 0;
+ m_ConvertCase = 0;
+}
+
+unsigned int Font::GetGlyphNo (unsigned int charCode) const
+{
+ if (m_ConvertCase == kUpperCase)
+ return ToUpper ((char)charCode) - m_AsciiStartOffset;
+ else if (m_ConvertCase == kLowerCase)
+ return ToLower ((char)charCode) - m_AsciiStartOffset;
+ else
+ return charCode - m_AsciiStartOffset;
+}
+
+void Font::GetCharacterRenderInfo( unsigned int charCode, Rectf& verts, Rectf& uvs, bool &flipped ) const
+{
+ GetCharacterRenderInfo(charCode, 0, 0, verts, uvs, flipped);
+}
+
+void Font::GetCharacterRenderInfo( unsigned int charCode, int size, unsigned int style, Rectf& verts, Rectf& uvs, bool &flipped ) const
+{
+ unsigned int charNo = GetGlyphNo (charCode);
+ if (size == m_FontSize)
+ size = 0;
+
+ if (m_ConvertCase != kDynamicFont && (size != 0 || style != kStyleDefault))
+ {
+ ErrorString ("Font size and style overrides are only supported for dynamic fonts.");
+ size = 0;
+ style = kStyleDefault;
+ }
+
+ if (charNo < 256 && size == 0 && style == kStyleDefault)
+ {
+ verts = m_AsciiCharacterRects[charNo].vert;
+ uvs = m_AsciiCharacterRects[charNo].uv;
+ flipped = m_AsciiCharacterRects[charNo].flipped;
+ }
+ else
+ {
+ CharacterInfo proxy;
+ proxy.index = charNo;
+ proxy.size = size;
+ proxy.style = style;
+ vector_set<CharacterInfo>::const_iterator found = m_UnicodeCharacterRects.find(proxy);
+ if (found != m_UnicodeCharacterRects.end())
+ {
+ verts = found->vert;
+ uvs = found->uv;
+ flipped = found->flipped;
+ }
+ else
+ {
+ verts = Rectf( 0, 0, 0, 0 );
+ uvs = Rectf( 0, 0, 0, 0 );
+ flipped = false;
+ }
+ }
+}
+
+/// Does this font have a definition for a specific character?
+bool Font::HasCharacterInTexture (unsigned int unicodeChar, int size, unsigned int style)
+{
+ unsigned int charNo = GetGlyphNo (unicodeChar);
+ if (size == m_FontSize)
+ size = 0;
+
+ if (m_ConvertCase != kDynamicFont && (size != 0 || style != kStyleDefault))
+ {
+ ErrorString ("Font size and style overrides are only supported for dynamic fonts.");
+ size = 0;
+ style = kStyleDefault;
+ }
+
+ // This uses the character advancement - We're making the assumption that all characters have a width
+ if (charNo < 256 && size == 0 && style == kStyleDefault)
+ {
+ if (m_AsciiCharacterRects[charNo].width != 0.0f)
+ {
+ m_AsciiCharacterRects[charNo].lastUsedInFrame = s_FrameCount;
+ return true;
+ }
+ }
+
+ CharacterInfo proxy;
+ proxy.index = charNo;
+ proxy.size = size;
+ proxy.style = style;
+ vector_set<CharacterInfo>::iterator found = m_UnicodeCharacterRects.find(proxy);
+ if (found != m_UnicodeCharacterRects.end())
+ {
+ found->lastUsedInFrame = s_FrameCount;
+ return true;
+ }
+ return false;
+}
+
+bool Font::HasCharacter (unsigned int unicodeChar, int size, unsigned int style)
+{
+ if (m_ConvertCase == kDynamicFont)
+ return HasCharacterDynamic (unicodeChar);
+ else
+ return HasCharacterInTexture (unicodeChar, size, style);
+}
+
+float Font::GetCharacterWidth( unsigned int charCode, int size, unsigned int style ) const
+{
+ if (size == m_FontSize)
+ size = 0;
+
+ if (m_ConvertCase != kDynamicFont && (size != 0 || style != kStyleDefault))
+ {
+ ErrorString ("Font size and style overrides are only supported for dynamic fonts.");
+ size = 0;
+ style = kStyleDefault;
+ }
+
+ unsigned int charNo = GetGlyphNo (charCode);
+ if (charNo < 256 && size == 0 && style == kStyleDefault)
+ return m_AsciiCharacterRects[charNo].width * m_Kerning;
+ else
+ {
+ CharacterInfo proxy;
+ proxy.index = charNo;
+ proxy.size = size;
+ proxy.style = style;
+ vector_set<CharacterInfo>::const_iterator found = m_UnicodeCharacterRects.find(proxy);
+ if (found != m_UnicodeCharacterRects.end())
+ return found->width * m_Kerning;
+ else
+ return 0.0F;
+ }
+}
+
+float Font::GetTabWidth() const
+{
+ return GetCharacterWidth(' ');
+}
+
+template<class TransferFunction> inline
+void Font::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion (4);
+
+ TRANSFER(m_AsciiStartOffset);
+ TRANSFER(m_Kerning);
+ TRANSFER(m_LineSpacing);
+ TRANSFER(m_CharacterSpacing);
+ TRANSFER(m_CharacterPadding);
+ TRANSFER(m_ConvertCase);
+ TRANSFER(m_DefaultMaterial);
+
+ if (m_ConvertCase != kDynamicFont)
+ {
+ transfer.Transfer(m_CharacterRects, "m_CharacterRects");
+ }
+ else
+ {
+ // These are generated dynamically for dynamic fonts.
+ UNITY_TEMP_VECTOR(CharacterInfo) emptyCharacterInfo;
+ transfer.Transfer(emptyCharacterInfo, "m_CharacterRects");
+ }
+
+ transfer.Transfer(m_Texture, "m_Texture", kHideInEditorMask);
+
+ transfer.Transfer(m_KerningValues, "m_KerningValues", kHideInEditorMask);
+
+ // In version 1.5.0 line spacing is multiplicative instead of additive
+ if (transfer.IsOldVersion(1))
+ {
+ m_LineSpacing = 1.0F + m_LineSpacing;
+ }
+
+ transfer.Transfer(m_PixelScale, "m_PixelScale", kHideInEditorMask);
+
+ // Legacy Grid Font support
+ if (transfer.IsVersionSmallerOrEqual(3))
+ {
+ bool gridFont;
+ transfer.Transfer(gridFont, "m_GridFont");
+
+ if (gridFont)
+ {
+ int fontCountX;
+ int fontCountY;
+ transfer.Transfer(fontCountX, "m_FontCountX");
+ transfer.Transfer(fontCountY, "m_FontCountY");
+
+ m_PixelScale = -fontCountX;
+
+ PerCharacterKerning perCharacterKerning;
+
+ transfer.Transfer(perCharacterKerning, "m_PerCharacterKerning");
+
+ for (int charNo=0; charNo< fontCountX*fontCountY; charNo++ )
+ {
+ CharacterInfo info;
+ info.index = charNo;
+
+ info.vert = Rectf(0.0F, 0.0F, 1.0, -1.0);
+ short charCol = charNo % fontCountX;
+ short charRow = charNo / fontCountX;
+ float charUVSizeX = 1.0F / (float)fontCountX;
+ float charUVSizeY = 1.0F / (float)fontCountY;
+ Vector2f charUVOffset = Vector2f((float)charCol * charUVSizeX, (float)charRow * charUVSizeY);
+ info.uv = MinMaxRect ( charUVOffset.x, 1.0F - charUVOffset.y - charUVSizeY, charUVOffset.x + charUVSizeX, 1.0F - charUVOffset.y );
+ info.width = 1.0;
+ for (PerCharacterKerning::iterator i=perCharacterKerning.begin ();i != perCharacterKerning.end ();i++)
+ {
+ if (i->first - m_AsciiStartOffset == charNo)
+ info.width = i->second;
+ }
+ m_CharacterRects.push_back(info);
+ }
+ }
+ }
+
+ transfer.Align();
+ transfer.Transfer(m_FontData, "m_FontData", kHideInEditorMask);
+ transfer.Align();
+ float fontSize = m_FontSize;
+ transfer.Transfer(fontSize, "m_FontSize", kHideInEditorMask);
+ m_FontSize = (int)fontSize;
+ transfer.Transfer(m_Ascent, "m_Ascent", kHideInEditorMask);
+ transfer.Transfer(m_DefaultStyle, "m_DefaultStyle", kHideInEditorMask);
+ transfer.Transfer(m_FontNames, "m_FontNames", kHideInEditorMask);
+
+#if UNITY_EDITOR
+ if (transfer.IsWritingGameReleaseData ())
+ {
+ // Make sure we have references to all other fonts in the project
+ // which may be used as fallbacks in the build.
+ TrueTypeFontImporter::GetFallbackFontReferences (this);
+ }
+#endif
+
+ transfer.Transfer(m_FallbackFonts, "m_FallbackFonts", kHideInEditorMask);
+ transfer.Align();
+ TRANSFER(m_FontRenderingMode);
+}
+
+template<class TransferFunction> inline
+void Font::CharacterInfo::Transfer (TransferFunction& transfer)
+{
+ transfer.SetVersion(2);
+ TRANSFER(index);
+ TRANSFER(uv);
+ TRANSFER(vert);
+ TRANSFER(width);
+ TRANSFER(flipped);
+ transfer.Align();
+ if( !transfer.IsCurrentVersion() )
+ width = vert.Width();
+}
+
+void ApplyToMeshes ()
+{
+ vector<TextMesh*> meshes;
+ Object::FindObjectsOfType (&meshes);
+ for (int i=0;i<meshes.size ();i++)
+ {
+ meshes[i]->ApplyToMesh ();
+ }
+}
+
+void Font::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+#if UNITY_EDITOR
+ // Make sure we have references to all other fonts in the project
+ // which may be used as fallbacks in the build.
+ TrueTypeFontImporter::GetFallbackFontReferences (this);
+
+ if ((awakeMode & kDidLoadFromDisk) && (m_ConvertCase == kDynamicFont) && !m_Texture.IsValid())
+ ErrorStringObject(Format("Font texture for dynamic font %s is missing. Please reimport the Font. All dynamic fonts created with earlier Unity 3.0 betas need to be reimported.", GetName()), this);
+#endif
+
+ // m_PixelScale is set to -fontCountX for legacy gridfonts
+ if(m_PixelScale < 0.f)
+ {
+ // Load related material or texture to apply the scaling to the CharacterRects
+ Texture *tex = GetTexture ();
+ if (!tex) {
+ Material *mat = GetMaterial();
+ if (mat)
+ tex = mat->GetTexture (ShaderLab::Property("_MainTex"));
+ }
+ if (tex)
+ m_PixelScale = -m_PixelScale / tex->GetDataWidth ();
+ else
+ m_PixelScale = 1.0f;
+
+ for (int charNo=0; charNo< m_CharacterRects.size(); charNo++ )
+ {
+ CharacterInfo& info = m_CharacterRects[charNo];
+ info.vert = Rectf(0.0F, 0.0F, 1.0/m_PixelScale, -1.0/m_PixelScale);
+ info.width /= m_PixelScale;
+ }
+ m_LineSpacing /= m_PixelScale;
+ }
+
+ CacheRects();
+
+ if (m_ConvertCase == kDynamicFont)
+ {
+ if (m_FontNames.empty())
+ {
+ ErrorString ("Font does not contain font names!");
+ m_FontNames.push_back("Arial");
+ }
+ SetupDynamicFont ();
+ ResetCachedTexture();
+ }
+
+ if ((awakeMode & kDidLoadFromDisk) == 0)
+ ApplyToMeshes();
+}
+
+void Font::AddCharacterInfoEntry( const Rectf& uv, const Rectf& vert, float width, int character, bool flipped, int size, unsigned int style)
+{
+ character -= m_AsciiStartOffset;
+ AssertIf( character < 0 );
+
+ CharacterInfo inf;
+ inf.uv = uv;
+ inf.vert = vert;
+ inf.width = width;
+ inf.index = character;
+ if (size == m_FontSize)
+ inf.size = 0;
+ else
+ inf.size = size;
+ inf.style = style;
+ inf.lastUsedInFrame = s_FrameCount;
+ inf.flipped = flipped;
+ m_CharacterRects.push_back(inf);
+ AddRectToCache (inf);
+}
+
+void Font::AddRectToCache(CharacterInfo& info)
+{
+ // We cache ascii characters into a direct lookup buffer, for optimal performance on the most common characters
+ if (info.index < 256 && info.size == 0 && info.style == kStyleDefault)
+ m_AsciiCharacterRects[info.index] = info;
+ // And a set of characters for the rest.
+ else
+ m_UnicodeCharacterRects.insert(info);
+}
+
+void Font::CacheRects ()
+{
+ m_AsciiCharacterRects.clear();
+ m_AsciiCharacterRects.resize (256);
+ m_UnicodeCharacterRects.clear();
+
+ for (int i=0;i<m_CharacterRects.size();i++)
+ {
+ CharacterInfo& info = m_CharacterRects[i];
+ // Older version didn't have the index. So derive it from i
+ if (info.index == -1)
+ info.index = i;
+
+ AddRectToCache(info);
+ }
+}
+
+
+
+// Dynamic font stuff
+// ==================
+
+static bool Equals (FontNames& list1, FontNames& list2)
+{
+ if (list1.size() != list2.size())
+ return false;
+
+ for (unsigned i=0; i<list1.size (); ++i)
+ if (list1[i] != list2[i])
+ return false;
+
+ return true;
+}
+
+void Font::SetFontNames (FontNames &names)
+{
+ if (m_ConvertCase == kDynamicFont)
+ {
+ if (Equals (names, m_FontNames))
+ return;
+
+ m_FontNames = names;
+// TODO!
+// DestroyDynamicFont ();
+// SetupDynamicFont ();
+ ResetCachedTexture();
+ }
+ else
+ ErrorString ("Font.names can only be set for dynamic fonts.");
+}
+
+void Font::ResetPackingData ()
+{
+ m_TexturePositions.clear();
+ m_IntRects.clear();
+ m_TexturePositions.insert (TexturePosition (0, 0));
+ m_TexturePositionsSearchPosition = m_TexturePositions.begin();
+}
+
+bool Font::ResetCachedTexture ()
+{
+ if (m_ConvertCase != kDynamicFont)
+ return true; // nothing to do for static fonts
+
+ m_CharacterRects.clear();
+ CacheRects ();
+
+ int maxSize = gGraphicsCaps.maxTextureSize;
+
+ // Some windows setups apparently crash when allocating textures > 4096^2. So, don't do it.
+ maxSize = std::min(maxSize, 4096);
+
+ if (m_TexWidth > maxSize || m_TexHeight > maxSize)
+ {
+ ErrorString ("Failed to generate dynamic font texture, because all the needed characters do not fit onto a single texture. Try using less text or a smaller font size.");
+ m_TexWidth = maxSize;
+ m_TexHeight = maxSize;
+ return false;
+ }
+
+ Texture2D *tex;
+ if (!GetTexture().IsValid())
+ return false;
+ else
+ tex = dynamic_pptr_cast<Texture2D*>(GetTexture());
+
+ if (gGraphicsCaps.disableSubTextureUpload || (NEEDS_SYSTEM_MEM_COPY && !UNITY_EDITOR))
+ tex->SetIsReadable(true);
+ else
+ tex->SetIsUnreloadable (true);
+
+#if UNITY_EDITOR
+ tex->SetEditorDontWriteTextureData(true);
+#endif
+
+ if (tex->GetDataWidth() != m_TexWidth || tex->GetDataHeight() != m_TexHeight || !tex->GetIsUploaded())
+ {
+ if (!tex->InitTexture (m_TexWidth, m_TexHeight, kTexFormatAlpha8, Texture2D::kNoMipmap))
+ return false;
+ tex->UpdateImageData ();
+ }
+
+ {
+ UInt8* texData;
+ ALLOC_TEMP(texData, UInt8, m_TexWidth * m_TexHeight);
+ memset (texData, 0, m_TexWidth * m_TexHeight);
+ int dataSize = m_TexWidth * m_TexHeight;
+
+ if (!gGraphicsCaps.disableSubTextureUpload)
+ GetGfxDevice().UploadTextureSubData2D( tex->GetTextureID(), texData, dataSize, 0, 0, 0, m_TexWidth, m_TexHeight, kTexFormatAlpha8, tex->GetActiveTextureColorSpace() );
+ if (gGraphicsCaps.disableSubTextureUpload || NEEDS_SYSTEM_MEM_COPY)
+ {
+ ImageReference texImg;
+ if (tex->GetWriteImageReference ( &texImg, 0, 0 ))
+ {
+ ImageReference data (m_TexWidth, m_TexHeight, m_TexWidth, kTexFormatAlpha8, texData);
+ texImg.BlitImage( data );
+ }
+ if (gGraphicsCaps.disableSubTextureUpload)
+ tex->UpdateImageData();
+ }
+ }
+
+ ResetPackingData ();
+
+ m_SubImageIndex = 0;
+ m_SubImageSize = std::max(m_SubImageSize, (unsigned int)NextPowerOfTwo(8 * m_FontSize));
+ m_SubImageSize = std::min(m_SubImageSize, m_TexWidth);
+
+ return true;
+}
+
+bool Font::IsRectFree(const IntRect &r) const
+{
+ if (r.x < 0 || r.y < 0 || r.x + r.width > m_SubImageSize || r.y+r.height > m_SubImageSize)
+ return false;
+
+ for (UNITY_VECTOR(kMemFont,IntRect)::const_iterator i = m_IntRects.begin(); i != m_IntRects.end(); i++)
+ {
+ if (r.Intersects(*i))
+ return false;
+ }
+
+ return true;
+}
+
+bool Font::AddCharacterToTexture (unsigned int unicodeChar, int size, unsigned int style)
+{
+ Rectf vert;
+
+ unsigned int charWidth = 0;
+ unsigned int charHeight = 0;
+ unsigned int bufferWidth = 0;
+ float advance = 0;
+ // returns a pointer to static vector data
+ UInt8* bitmap = GetCharacterBitmap (charWidth, charHeight, bufferWidth, vert, advance, unicodeChar, size, style | m_DefaultStyle);
+ UNITY_TEMP_VECTOR(UInt8) flippedBitmap;
+ bool flipped = false;
+
+ if (bitmap == NULL && charHeight*charWidth != 0)
+ {
+ charWidth = 0;
+ charHeight = 0;
+ advance = 0;
+ }
+
+ if (charWidth > charHeight)
+ {
+ // flip glyphs with >1 aspect ratios for better packing results.
+ flipped = true;
+ flippedBitmap.resize (charWidth * charHeight);
+ for (int x = 0; x<charWidth; x++)
+ {
+ for (int y = 0; y<charHeight; y++)
+ flippedBitmap[charHeight-1-y + (charWidth-1-x)*charHeight] = bitmap[x + y*bufferWidth];
+ }
+ bitmap = &flippedBitmap[0];
+ bufferWidth = charHeight;
+ charHeight = charWidth;
+ charWidth = bufferWidth;
+ }
+ else if (bufferWidth > charWidth)
+ {
+ flippedBitmap.resize (charWidth * charHeight);
+ for (int x = 0; x<charWidth; x++)
+ {
+ for (int y = 0; y<charHeight; y++)
+ flippedBitmap[x + y*charWidth] = bitmap[x + y*bufferWidth];
+ }
+ bitmap = &flippedBitmap[0];
+ bufferWidth = charWidth;
+ }
+
+ vert.x -= m_CharacterPadding;
+ vert.y += m_CharacterPadding;
+ vert.width += 2*m_CharacterPadding;
+ vert.height -= 2*m_CharacterPadding;
+
+ while (true)
+ {
+ for (UNITY_SET(kMemFont,TexturePosition)::iterator i = m_TexturePositionsSearchPosition; i != m_TexturePositions.end(); i++)
+ {
+ IntRect r (i->x, i->y, charWidth+m_CharacterSpacing+2*m_CharacterPadding, charHeight+m_CharacterSpacing+2*m_CharacterPadding);
+ if (IsRectFree (r))
+ {
+ IntRect r2 = r;
+ r2.x--;
+ while (IsRectFree (r2) && r2.x > 0)
+ {
+ r = r2;
+ r2.x--;
+ }
+ r2 = r;
+ r2.y--;
+ while (IsRectFree (r2) && r2.y > 0)
+ {
+ r = r2;
+ r2.y--;
+ }
+
+ m_IntRects.push_back(r);
+ m_TexturePositionsSearchPosition = i;
+ m_TexturePositionsSearchPosition++;
+ m_TexturePositions.erase(i);
+ m_TexturePositions.insert( TexturePosition(r.x + r.width, r.y));
+ m_TexturePositions.insert( TexturePosition(r.x, r.y + r.height));
+
+ // Offset sub image position to get actual texture position
+ int subImagePos = m_SubImageIndex * m_SubImageSize;
+ r.x += subImagePos % m_TexWidth;
+ r.y += (subImagePos / m_TexWidth) * m_SubImageSize;
+
+ if (bitmap)
+ {
+ int dataSize = bufferWidth * charHeight;
+ Texture2D *tex = dynamic_pptr_cast<Texture2D*>(GetTexture());
+
+ if (!gGraphicsCaps.disableSubTextureUpload)
+ GetGfxDevice().UploadTextureSubData2D( tex->GetTextureID(), bitmap, dataSize, 0, r.x+m_CharacterPadding, r.y+m_CharacterPadding, bufferWidth, charHeight, kTexFormatAlpha8, tex->GetActiveTextureColorSpace() );
+ if (gGraphicsCaps.disableSubTextureUpload || NEEDS_SYSTEM_MEM_COPY)
+ {
+ ImageReference texImg;
+ if (tex->GetWriteImageReference ( &texImg, 0, 0 ))
+ {
+ ImageReference destRect = texImg.ClipImage( r.x, r.y, bufferWidth, charHeight );
+
+ ImageReference data (bufferWidth, charHeight, bufferWidth, kTexFormatAlpha8, bitmap);
+ destRect.BlitImage( data );
+ }
+ }
+ }
+
+ float width = m_TexWidth;
+ float height = m_TexHeight;
+
+ Rectf uv (r.x/width, (r.y+charHeight+2*m_CharacterPadding)/height, (charWidth+2*m_CharacterPadding)/width, -((charHeight+2*m_CharacterPadding)/height));
+ AddCharacterInfoEntry (uv, vert, advance, unicodeChar, flipped, size, style);
+ return true;
+ }
+ }
+ if (m_TexturePositionsSearchPosition != m_TexturePositions.begin())
+ m_TexturePositionsSearchPosition = m_TexturePositions.begin();
+ else
+ {
+ if (m_SubImageIndex+1 < (m_TexWidth/m_SubImageSize) * (m_TexHeight/m_SubImageSize))
+ {
+ // This sub image is full. move to the next one.
+ m_SubImageIndex++;
+ ResetPackingData ();
+ }
+ else
+ return false;
+ }
+ }
+ return false;
+}
+
+void Font::GrowTexture (int maxFontSize)
+{
+ // If we couldn't fit all characters by repainting the texture, enlarge it.
+ if (m_TexWidth < m_TexHeight)
+ m_TexWidth *= 2;
+ else
+ m_TexHeight *= 2;
+ // Make sure that we fit the largest characters in the string into a single sub image.
+ m_SubImageSize = std::max(m_SubImageSize, (unsigned int)NextPowerOfTwo(4 * maxFontSize));
+ m_SubImageSize = std::min(m_SubImageSize, m_TexWidth);
+}
+
+UInt16 *Font::CollectAllUsedCharacters (UInt16 *chars, int &length, int *&sizes, unsigned int *&styles)
+{
+ // We have to create a new texture. Make sure to add all characters which have been used in this frame.
+ int usedThisFrame = 0;
+ for (UNITY_VECTOR(kMemFont,CharacterInfo)::iterator ch = m_CharacterRects.begin(); ch != m_CharacterRects.end(); ch++)
+ {
+ if (ch->lastUsedInFrame == s_FrameCount)
+ usedThisFrame++;
+ }
+ UInt16 *newchars = new UInt16[usedThisFrame + length];
+ sizes = new int[usedThisFrame + length];
+ styles = new unsigned int[usedThisFrame + length];
+ // string being currently cached; without size/style override
+ for (int ch=0; ch<length; ch++)
+ {
+ newchars[ch] = chars[ch];
+ sizes[ch] = -1;
+ styles[ch] = -1;
+ }
+ // put in characters used in this frame, with their original size/style
+ for (UNITY_VECTOR(kMemFont,CharacterInfo)::iterator ch = m_CharacterRects.begin(); ch != m_CharacterRects.end(); ch++)
+ {
+ if (ch->lastUsedInFrame == s_FrameCount)
+ {
+ newchars[length] = ch->index;
+ sizes[length] = ch->size;
+ styles[length] = ch->style;
+ length++;
+ }
+ }
+ return newchars;
+}
+
+bool Font::CacheFontForText (UInt16 *chars, int length, int size, unsigned int style, std::vector<TextFormatChange> format)
+{
+ if (m_ConvertCase != kDynamicFont)
+ return true;
+
+ PROFILER_AUTO(gFontTextureCacheProfile, NULL)
+
+ if (!GetTexture().IsValid() && !ResetCachedTexture ())
+ return false;
+
+ bool didAdd = false;
+ int *sizes = NULL;
+ unsigned int *styles = NULL;
+ int maxFontSize = 0;
+ bool didNotFit = false;
+ do {
+ didNotFit = false;
+ FormatStack formatStack(0xffffffff, size, style);
+ int formatChange = 0;
+ for (int i=0; i<length; i++)
+ {
+ while (formatChange < format.size() && i >= format[formatChange].startPosition)
+ {
+ i += format[formatChange].skipCharacters;
+ formatStack.PushFormat(format[formatChange]);
+ formatChange++;
+ }
+ // Recheck range after skipping format changes
+ if (i >= length)
+ break;
+
+ int thisSize = formatStack.Current().size;
+ int thisStyle = formatStack.Current().style;
+ if (sizes && sizes[i] != -1)
+ {
+ // Normally, we just add characters with the computed size/style.
+ // But when we need to recreate the texture, then we need to make sure all used characters
+ // with all used sizes are in there, so we index size & style from an array.
+ thisSize = sizes[i];
+ thisStyle = styles[i];
+ }
+ if (thisSize == 0)
+ thisSize = m_FontSize;
+ if (thisSize > maxFontSize)
+ maxFontSize = thisSize;
+ UInt16 thisChar = chars[i];
+ if (HasCharacterDynamic (thisChar) && !HasCharacterInTexture (chars[i], thisSize, thisStyle))
+ {
+ if (!AddCharacterToTexture (thisChar, thisSize, thisStyle))
+ {
+ if (sizes != NULL)
+ GrowTexture(maxFontSize);
+ else
+ chars = CollectAllUsedCharacters (chars, length, sizes, styles);
+ didNotFit = true;
+ if (!ResetCachedTexture())
+ return false;
+ break;
+ }
+ didAdd = true;
+ }
+ }
+ } while(didNotFit);
+
+ if (didAdd && gGraphicsCaps.disableSubTextureUpload)
+ dynamic_pptr_cast<Texture2D*>(GetTexture())->UpdateImageData();
+
+ if (sizes != NULL)
+ {
+ delete[] chars;
+ delete[] sizes;
+ delete[] styles;
+
+ #if ENABLE_SCRIPTING
+ // Make sure we don't call InvokeFontTextureRebuildCallback_Internal repeatedly due to
+ // ApplyToMeshes adding characters.
+ static int recursionDepth = 0;
+ recursionDepth++;
+ TextMeshGenerator2::Flush();
+
+ ApplyToMeshes();
+ recursionDepth--;
+
+ if (recursionDepth == 0)
+ {
+ ScriptingObjectPtr instance = Scripting::ScriptingWrapperFor(this);
+ if (instance)
+ {
+ ScriptingInvocation invocation(GetScriptingManager().GetCommonClasses().font_InvokeFontTextureRebuildCallback_Internal);
+ invocation.object = instance;
+ invocation.Invoke();
+ }
+ }
+ #endif
+ }
+
+ return true;
+}
+
+float Font::GetLineSpacing (int size) const
+{
+ if (size == 0 || m_FontSize == 0)
+ return m_LineSpacing;
+ else
+ return m_LineSpacing * (float)size/m_FontSize;
+}
+
+IMPLEMENT_CLASS_HAS_INIT (Font)
+IMPLEMENT_OBJECT_SERIALIZE (Font)
diff --git a/Runtime/Filters/Misc/Font.h b/Runtime/Filters/Misc/Font.h
new file mode 100644
index 0000000..5004709
--- /dev/null
+++ b/Runtime/Filters/Misc/Font.h
@@ -0,0 +1,318 @@
+#ifndef FONT_H
+#define FONT_H
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/Utilities/vector_map.h"
+#include "Runtime/Utilities/vector_set.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Misc/UTF8.h"
+#include "Runtime/IMGUI/TextFormatting.h"
+
+class Font;
+
+typedef UNITY_VECTOR(kMemFont,UnityStr) FontNames;
+typedef UNITY_VECTOR (kMemFont, PPtr<Font> ) FontFallbacks;
+
+#include "DynamicFontFreeType.h"
+
+namespace Unity { class Material; }
+using namespace Unity;
+
+enum {
+ kFontRenderingModeSmooth,
+ kFontRenderingModeHintedSmooth,
+ kFontRenderingModeHintedRaster,
+ kFontRenderingModeOSDefault
+};
+
+class Texture;
+
+class Font : public NamedObject
+{
+ public:
+
+ struct KerningCompare : std::binary_function<std::pair<char, char>, std::pair<char, char>, std::size_t>
+ {
+ bool operator()(const std::pair<char, char> lhs, const std::pair<char, char> rhs) const
+ {
+ if (lhs.first != rhs.first)
+ return lhs.first < rhs.first;
+ else
+ return lhs.second < rhs.second;
+ }
+ };
+
+ // TrueType fonts: information for a character.
+ struct CharacterInfo {
+ unsigned int index;
+ Rectf uv; ///< UV coordinates for this glyph.
+ Rectf vert; ///< Rectangle for where to render the glyph.
+ float width;
+ int size;
+ unsigned int style;
+ unsigned int lastUsedInFrame;
+ bool flipped;
+ DECLARE_SERIALIZE_NO_PPTR (CharacterInfo)
+
+ CharacterInfo() :
+ vert(0.0F,0.0F,0.0F,0.0F),
+ uv(0.0F,0.0F,0.0F,0.0F),
+ index (-1),
+ width (0.0F),
+ size (0),
+ style (kStyleDefault),
+ lastUsedInFrame (0),
+ flipped (false)
+ { }
+
+ friend bool operator < (const CharacterInfo& lhs, const CharacterInfo& rhs)
+ {
+ if (lhs.index == rhs.index)
+ {
+ if (lhs.size < rhs.size)
+ return true;
+ else if (lhs.size > rhs.size)
+ return false;
+ return lhs.style < rhs.style;
+ }
+ return lhs.index < rhs.index;
+ }
+ };
+ typedef UNITY_VECTOR(kMemFont,CharacterInfo) CharacterInfos;
+
+
+ REGISTER_DERIVED_CLASS (Font, NamedObject)
+ DECLARE_OBJECT_SERIALIZE (Font)
+
+ Font (MemLabelId label, ObjectCreationMode mode);
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+ virtual void Reset();
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ /// Get the kerning of the font.
+ /// Kerning is letter-spacing
+ float GetKerning () const { return m_Kerning; }
+
+ /// Get the uv & vertex info for unicode character charCode into 'verts' & 'uvs'
+ void GetCharacterRenderInfo( unsigned int charCode, Rectf& verts, Rectf& uvs, bool &flipped ) const;
+ void GetCharacterRenderInfo( unsigned int charCode, int size, unsigned int style, Rectf& verts, Rectf& uvs, bool &flipped ) const;
+
+
+ /// Get the width of a character
+ float GetCharacterWidth (unsigned int charCode, int size = 0, unsigned int style = kStyleDefault) const;
+
+ /// Get the width of the tab character
+ float GetTabWidth () const;
+
+ /// Get the material of this font.
+ PPtr<Material> GetMaterial () const { return m_DefaultMaterial; }
+
+ /// Set the default material
+ void SetMaterial (PPtr<Material> material) {m_DefaultMaterial = material;}
+
+ /// Get the texture of this font...
+ PPtr<Texture> GetTexture () const { return m_Texture; }
+
+ /// Set the texture
+ void SetTexture(PPtr<Texture> texture) {m_Texture = texture;}
+
+ /// Get the line spacing of this font
+ float GetLineSpacing (int size = 0) const;
+
+ /// Get the line spacing of this font
+ void SetLineSpacing (float spacing) { m_LineSpacing = spacing; }
+
+ /// Adds character info (only for non grid fonts)
+ void AddCharacterInfoEntry( const Rectf& uv, const Rectf& vert, float width, int character, bool flipped, int size = 0, unsigned int style = kStyleDefault);
+
+ /// Set Ascii Start Offset
+ void SetAsciiStartOffset(short val) {m_AsciiStartOffset = val;}
+
+ // Old "Grid fonts" where scaled differently when using non-pixel correct rendering then
+ // normal fonts. So we need this to emulate the effect.
+ float GetDeprecatedPixelScale () const { return m_PixelScale; }
+
+ /// Set the Convert Case property
+ enum { kDynamicFont = -2, kUnicodeSet = -1, kDontConvertCase = 0, kUpperCase = 1, kLowerCase = 2, kCustomSet = 3 };
+ void SetConvertCase(int val) {m_ConvertCase = val;}
+ int GetConvertCase() { return m_ConvertCase; }
+
+ typedef vector_map<std::pair<UnicodeChar, UnicodeChar>, float, KerningCompare> KerningValues;
+ /// Get the kerning values for modifying in place
+ KerningValues &GetKerningValues() { return m_KerningValues; }
+
+ /// Set the font size, called by the truetype font importer
+ void SetFontSize(int val) { m_FontSize = val; }
+
+ /// Get the font size, used to find pixel height of the chars in the font
+ int GetFontSize() const { return m_FontSize; }
+
+ void SetAscent(float val) { m_Ascent = val; }
+ float GetAscent() const { return m_Ascent; }
+
+ void SetCharacterSpacing (int val) { m_CharacterSpacing = val; }
+ void SetCharacterPadding (int val) { m_CharacterPadding = val; }
+
+ /// Does this font have a definition for a specific character?
+ bool HasCharacter (unsigned int unicodeChar, int size = 0, unsigned int style = kStyleDefault);
+
+ bool HasCharacterInTexture (unsigned int unicodeChar, int size, unsigned int style);
+
+ /// Dynamic Font stuff:
+ bool CacheFontForText (UInt16 *chars, int length, int size = 0, unsigned int style = kStyleDefault, std::vector<TextFormatChange> format = std::vector<TextFormatChange>());
+
+ FontNames &GetFontNames () { return m_FontNames; }
+ FontFallbacks &GetFontFallbacks () { return m_FallbackFonts; }
+ void SetFontNames (FontNames &names);
+
+ UNITY_VECTOR(kMemFont,char) &GetFontData () { return m_FontData; }
+ void SetFontDefaultStyle (int style) { m_DefaultStyle = style; }
+ static void FrameComplete () { s_FrameCount++; }
+ bool ResetCachedTexture ();
+
+ const CharacterInfos &GetCharacterInfos() { return m_CharacterRects; }
+ void SetCharacterInfos (CharacterInfos &infos) { m_CharacterRects = infos; CacheRects(); }
+
+ void SetFontRenderingMode(int val) { m_FontRenderingMode = val; }
+ int GetFontRenderingMode() const { return m_FontRenderingMode; }
+#if UNITY_EDITOR
+ void SetMinimalFontTextureSize (int size) { m_TexWidth = size; m_TexHeight = size; }
+#endif
+
+protected:
+
+ void AddRectToCache(CharacterInfo& info);
+ void CacheRects();
+
+ /// Helper: Get the glyph code from a character, remapping cases, etc...
+ unsigned int GetGlyphNo( unsigned int charCode ) const;
+
+ // The kerning map
+ KerningValues m_KerningValues;
+
+ // These are only used in grid fonts
+ typedef std::pair<int, float> IntFloatPair;
+ typedef UNITY_VECTOR(kMemFont,IntFloatPair) PerCharacterKerning;
+
+ float m_Kerning;///< Kerning of space between characters (Smaller than 1.0 pulls them together, Larger pushes them out)
+ float m_LineSpacing; ///< Spacing between lines as multiplum of height of a character.
+ int m_CharacterSpacing;
+ int m_CharacterPadding;
+ int m_AsciiStartOffset; ///< What is the first ascii character in the texture.
+ int m_FontSize;
+
+ int m_ConvertCase; ///< enum { Don't change case, Convert to upper case characters, Convert to lower case characters }
+ PPtr<Material> m_DefaultMaterial;
+ PPtr<Texture> m_Texture;
+
+ // Legacy Grid font support
+ float m_PixelScale;
+
+ struct TexturePosition {
+ int x, y;
+
+ TexturePosition (int _x, int _y) : x(_x), y(_y) {}
+
+ friend bool operator < (const TexturePosition& lhs, const TexturePosition& rhs)
+ {
+ if (lhs.x + lhs.y != rhs.x + rhs.y)
+ return lhs.x + lhs.y < rhs.x + rhs.y;
+ else
+ return lhs.x < rhs.x;
+ }
+ };
+
+ struct IntRect {
+ int x, y, width, height;
+
+ IntRect (int _x, int _y, int _width, int _height) : x(_x), y(_y), width(_width), height(_height) {}
+
+ inline bool Intersects (const IntRect &r) const
+ {
+ return r.x+r.width > x && r.y+r.height > y && r.x < x + width && r.y < y + height;
+ }
+ };
+
+ UNITY_VECTOR(kMemFont,CharacterInfo) m_CharacterRects;
+ vector_set<CharacterInfo> m_UnicodeCharacterRects;
+ UNITY_VECTOR(kMemFont,CharacterInfo) m_AsciiCharacterRects;
+
+ // dynamic font stuff:
+ void ResetPackingData ();
+ void GrowTexture (int maxFontSize);
+ UInt16 *CollectAllUsedCharacters (UInt16 *chars, int &length, int *&sizes, unsigned int *&styles);
+ void SetupDynamicFont ();
+ bool HasCharacterDynamic (unsigned int unicodeChar);
+
+ bool AddCharacterToTexture (unsigned int unicodeChar, int size, unsigned int style);
+ bool IsRectFree (const IntRect &r) const;
+ UInt8 *GetCharacterBitmap(unsigned int &charWidth, unsigned int &charHeight, unsigned int &bufferWidth, Rectf &vert, float &advance, unsigned int unicodeChar, int size, unsigned int style);
+
+ UNITY_VECTOR(kMemFont,char) m_FontData;
+ FontNames m_FontNames;
+ FontFallbacks m_FallbackFonts;
+ UNITY_VECTOR(kMemFont,IntRect) m_IntRects;
+ UNITY_SET(kMemFont,TexturePosition) m_TexturePositions;
+ UNITY_SET(kMemFont,TexturePosition)::iterator m_TexturePositionsSearchPosition;
+
+ unsigned int m_TexWidth;
+ unsigned int m_TexHeight;
+ unsigned int m_TexMargin;
+ unsigned int m_SubImageSize;
+ unsigned int m_SubImageIndex;
+ static unsigned int s_FrameCount;
+ unsigned int m_DefaultStyle;
+ float m_Ascent;
+ int m_FontRenderingMode;
+
+ DynamicFontData m_DynamicData;
+
+ friend struct DynamicFontData;
+};
+
+namespace GetFontsManager
+{
+ void StaticInitialize();
+ void StaticDestroy();
+}
+
+void GetFontPaths (std::vector<std::string> &paths);
+FontNames &GetFallbacks ();
+bool GetFontMetadataPreset(const std::string& name, std::string& family_name, std::string& style_name, unsigned& style_flags, unsigned& face_flags);
+
+
+struct ScriptingCharacterInfo
+{
+ int index;
+ Rectf uv, vert;
+ float width;
+ int size, style;
+ bool flipped;
+
+ void CopyFrom(const Font::CharacterInfo& inData)
+ {
+ index = inData.index;
+ uv = inData.uv;
+ vert = inData.vert;
+ width = inData.width;
+ size = inData.size;
+ style = inData.style;
+ flipped = inData.flipped;
+ }
+ void CopyTo(Font::CharacterInfo& outData)
+ {
+ outData.index = index;
+ outData.uv = uv;
+ outData.vert = vert;
+ outData.width = width;
+ outData.size = size;
+ outData.style = style;
+ outData.flipped = flipped;
+ }
+};
+
+#endif
diff --git a/Runtime/Filters/Misc/GetFonts.cpp b/Runtime/Filters/Misc/GetFonts.cpp
new file mode 100644
index 0000000..37bdb11
--- /dev/null
+++ b/Runtime/Filters/Misc/GetFonts.cpp
@@ -0,0 +1,338 @@
+#include "UnityPrefix.h"
+#include "Font.h"
+#include "Runtime/Utilities/File.h"
+#if UNITY_LINUX
+#include <ftw.h>
+#endif
+
+#if DYNAMICFONTMODE == kDynamicFontModeFreeType || DYNAMICFONTMODE == kDynamicFontModeStb
+
+
+typedef std::vector<UnityStr> FontDirs;
+static FontDirs* gFontDirs = NULL;
+
+struct _FontInfo
+{
+ const char* family_name;
+ const char* style_name;
+ unsigned style_flags;
+ unsigned face_flags;
+};
+typedef std::map<std::string, _FontInfo> FontMetadataMap;
+static FontMetadataMap* gFontMetadata = NULL;
+
+static FontNames* gFontFallbacks = NULL;
+
+namespace GetFontsManager
+{
+ void StaticInitialize()
+ {
+ gFontDirs = UNITY_NEW(FontDirs, kMemFont);
+ gFontMetadata = UNITY_NEW(FontMetadataMap, kMemFont);
+ gFontFallbacks = UNITY_NEW(FontNames, kMemFont);
+ }
+
+ void StaticDestroy()
+ {
+ UNITY_DELETE(gFontDirs , kMemFont);
+ UNITY_DELETE(gFontMetadata, kMemFont);
+ UNITY_DELETE(gFontFallbacks, kMemFont);
+ }
+}
+
+
+FontNames &GetFallbacks ()
+{
+ if (gFontFallbacks->empty())
+ {
+ // Make Arial first fallback for consistency, as it widely available.
+ gFontFallbacks->push_back("Arial");
+ // Arial Unicode MS covers almost all unicode scripts, and is available on OS X (>=10.5).
+ gFontFallbacks->push_back("Arial Unicode MS");
+ // This should catch Unicode scripts on Windows, excluding Asian scripts
+ gFontFallbacks->push_back("Microsoft Sans Serif");
+ // This should catch Chinese on windows
+ gFontFallbacks->push_back("Microsoft YaHei");
+ // This should catch Korean on windows
+ gFontFallbacks->push_back("Gulim");
+ // This should catch Japanese on windows
+ gFontFallbacks->push_back("MS Gothic");
+#if UNITY_ANDROID
+ // Android system font
+ gFontFallbacks->push_back("Roboto");
+ gFontFallbacks->push_back("NanumGothic");
+ gFontFallbacks->push_back("Droid Sans");
+ gFontFallbacks->push_back("Droid Sans Japanese");
+ gFontFallbacks->push_back("Droid Sans Fallback");
+#elif UNITY_IPHONE
+ gFontFallbacks->push_back("Hiragino Kaku Gothic ProN");
+ gFontFallbacks->push_back("Heiti TC");
+ gFontFallbacks->push_back("AppleGothic");
+ gFontFallbacks->push_back(".LastResort");
+#elif UNITY_WP8
+ gFontFallbacks->push_back("Yu Gothic"); // Japanese
+ gFontFallbacks->push_back("Microsoft NeoGothic"); // Korean
+ gFontFallbacks->push_back("SimSun"); // Chinese simplified
+ gFontFallbacks->push_back("Microsoft Mhei"); // Chinese traditional
+ gFontFallbacks->push_back("Urdu Typesetting"); // Arabic
+#elif UNITY_TIZEN
+ gFontFallbacks->push_back("Tizen Sans");
+ gFontFallbacks->push_back("Tizen Sans Japanese");
+ gFontFallbacks->push_back("Tizen Sans Fallback");
+#endif
+#if UNITY_LINUX
+ gFontFallbacks->push_back("FreeSans");
+ gFontFallbacks->push_back("WenQuanYi Micro Hei");
+#endif
+ // Unicode debugging fallback: http://en.wikipedia.org/wiki/Fallback_font
+ gFontFallbacks->push_back("LastResort");
+ }
+ return *gFontFallbacks;
+}
+
+#if UNITY_LINUX
+int callback(const char *fpath, const struct stat *sb, int typeflag)
+{
+ if (typeflag == FTW_D)
+ {
+ gFontDirs->push_back (fpath);
+ }
+ return 0;
+}
+#endif
+
+void GetFontPaths (std::vector<std::string> &paths)
+{
+ paths.clear();
+
+ // paths should not be garbaged by the repetitive
+ // content accumulation in the dirs vector
+ gFontDirs->clear();
+
+// Xbox and Wii do not have GetFolderContentsAtPath().
+#if !UNITY_XENON && !UNITY_WII
+#if UNITY_OSX
+ gFontDirs->push_back ("/System/Library/Fonts");
+ gFontDirs->push_back ("/Library/Fonts");
+ string homeDir = getenv ("HOME");
+ gFontDirs->push_back (homeDir + "/Library/Fonts");
+
+#elif UNITY_WINRT
+ gFontDirs->push_back ("C:\\Windows\\Fonts");
+#elif UNITY_WIN && !UNITY_WINRT
+ // It must be noted that Windows installation does not necessarily have to reside on C: disk
+ std::string win_dir;
+ win_dir.resize( MAX_PATH );
+ UINT const dir_len = GetWindowsDirectoryA( &win_dir.front(), win_dir.size() );
+ if( 0u == dir_len ) // The function has failed, so a default is as good as any other choice
+ {
+ gFontDirs->push_back ("C:\\Windows\\Fonts");
+ }
+ else
+ {
+ std::string::size_type old_win_dir_size = win_dir.size();
+ win_dir.resize( dir_len );
+
+ if ( dir_len > old_win_dir_size )
+ {
+ // Absolutely unlikely, but possible; in such a case where the previous buffer was not enough
+ // to hold the path to the windows directory, we simply increase the size of the buffer
+ // and try again to fetch the directory name.
+ UINT const dir_len2 = GetWindowsDirectoryA( &win_dir.front(), win_dir.size() );
+ if( (dir_len2 + 1u) == win_dir.size() )
+ {
+ win_dir.pop_back(); // Remove the embedded null terminator
+ }
+ else
+ {
+ win_dir = "C:\\Windows"; // Seriously screwed up
+ }
+ }
+
+ gFontDirs->push_back( PlatformAppendPathName(win_dir, "Fonts") );
+ }
+
+#elif UNITY_LINUX
+ ftw ("/usr/share/fonts", callback, 16);
+#elif UNITY_ANDROID
+ gFontDirs->push_back ("/system/fonts");
+#elif UNITY_IPHONE
+ #if TARGET_IPHONE_SIMULATOR
+ gFontDirs->push_back ("/Library/Fonts");
+ #else
+ gFontDirs->push_back ("/System/Library/Fonts/Cache");
+ #endif
+#elif UNITY_TIZEN
+ gFontDirs->push_back ("/usr/share/fonts");
+ gFontDirs->push_back ("/usr/share/fallback_fonts");
+#endif
+
+ for(int i = 0; i < gFontDirs->size(); ++i)
+ {
+ std::set<std::string> dirPaths;
+ if ( GetFolderContentsAtPath( (*gFontDirs)[i], dirPaths ) )
+ {
+ for (std::set<std::string>::iterator j = dirPaths.begin(); j != dirPaths.end(); j++)
+ {
+ std::string extension = GetPathNameExtension(*j);
+ ToLowerInplace(extension);
+ if (!StrCmp(extension.c_str(), "ttf") || !StrCmp(extension.c_str(), "ttc") || !StrCmp(extension.c_str(), "otf") || !StrCmp(extension.c_str(), "dfont"))
+ paths.push_back(*j);
+ }
+ }
+ }
+#endif
+}
+
+
+
+static void InitFontMetadataPreset();
+bool GetFontMetadataPreset(const std::string& name, std::string& family_name, std::string& style_name, unsigned& style_flags, unsigned& face_flags)
+{
+ if (gFontMetadata->empty())
+ {
+ InitFontMetadataPreset();
+ }
+
+ FontMetadataMap::iterator it = gFontMetadata->find(name);
+ if (it != gFontMetadata->end())
+ {
+ family_name = it->second.family_name;
+ style_name = it->second.style_name;
+ style_flags = it->second.style_flags;
+ face_flags = it->second.face_flags;
+ return true;
+ }
+
+ return false;
+}
+
+static void InitFontMetadataPreset()
+{
+#if UNITY_IPHONE
+ // Reading font metada on iOS devices might take few seconds when missing OS cache, so keeping preset known font table there
+ // iOS 4.3
+ (*gFontMetadata)["AppleColorEmoji"] = (_FontInfo){"Apple Color Emoji", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["AppleGothic"] = (_FontInfo){"AppleGothic", "Regular", 0x0, 0x39};
+ (*gFontMetadata)["Arial"] = (_FontInfo){"Arial", "Regular", 0x0, 0x59};
+ (*gFontMetadata)["ArialBold"] = (_FontInfo){"Arial", "Bold", 0x2, 0x59};
+ (*gFontMetadata)["ArialBoldItalic"] = (_FontInfo){"Arial", "Bold Italic", 0x3, 0x59};
+ (*gFontMetadata)["ArialHB"] = (_FontInfo){"Arial Hebrew", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["ArialHBBold"] = (_FontInfo){"Arial Hebrew", "Bold", 0x2, 0x19};
+ (*gFontMetadata)["ArialItalic"] = (_FontInfo){"Arial", "Italic", 0x1, 0x59};
+ (*gFontMetadata)["ArialRoundedMTBold"] = (_FontInfo){"Arial Rounded MT Bold", "Regular", 0x0, 0x59};
+ (*gFontMetadata)["BanglaSangamMN"] = (_FontInfo){"Bangla Sangam MN", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["CourierNew"] = (_FontInfo){"Courier New", "Regular", 0x0, 0x1f};
+ (*gFontMetadata)["CourierNewBold"] = (_FontInfo){"Courier New", "Bold", 0x2, 0x1d};
+ (*gFontMetadata)["CourierNewBoldItalic"] = (_FontInfo){"Courier New", "Bold Italic", 0x3, 0x1d};
+ (*gFontMetadata)["CourierNewItalic"] = (_FontInfo){"Courier New", "Italic", 0x1, 0x1d};
+ (*gFontMetadata)["DB_LCD_Temp-Black"] = (_FontInfo){"DB LCD Temp", "Black", 0x0, 0x19};
+ (*gFontMetadata)["DevanagariSangamMN"] = (_FontInfo){"Devanagari Sangam MN", "Regular", 0x0, 0x59};
+ (*gFontMetadata)["Fallback"] = (_FontInfo){".PhoneFallback", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["GeezaPro"] = (_FontInfo){"Geeza Pro", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["GeezaProBold"] = (_FontInfo){"Geeza Pro", "Bold", 0x0, 0x19};
+ (*gFontMetadata)["Georgia"] = (_FontInfo){"Georgia", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["GeorgiaBold"] = (_FontInfo){"Georgia", "Bold", 0x2, 0x19};
+ (*gFontMetadata)["GeorgiaBoldItalic"] = (_FontInfo){"Georgia", "Bold Italic", 0x3, 0x19};
+ (*gFontMetadata)["GeorgiaItalic"] = (_FontInfo){"Georgia", "Italic", 0x1, 0x19};
+ (*gFontMetadata)["GujaratiSangamMN"] = (_FontInfo){"Gujarati Sangam MN", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["GurmukhiMN"] = (_FontInfo){"Gurmukhi MN", "Regular", 0x0, 0x59};
+ (*gFontMetadata)["HKGPW3UI"] = (_FontInfo){".HKGPW3UI", "Regular", 0x0, 0x59};
+ (*gFontMetadata)["HiraginoKakuGothicProNW3"] = (_FontInfo){"Hiragino Kaku Gothic ProN", "W3", 0x0, 0x39};
+ (*gFontMetadata)["HiraginoKakuGothicProNW6"] = (_FontInfo){"Hiragino Kaku Gothic ProN", "W6", 0x2, 0x39};
+ (*gFontMetadata)["Kailasa"] = (_FontInfo){"Kailasa", "Bold", 0x0, 0x59};
+ (*gFontMetadata)["KannadaSangamMN"] = (_FontInfo){"Kannada Sangam MN", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["LastResort"] = (_FontInfo){".LastResort", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["LockClock"] = (_FontInfo){".Lock Clock", "Light", 0x0, 0x59};
+ (*gFontMetadata)["MalayalamSangamMN"] = (_FontInfo){"Malayalam Sangam MN", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["OriyaSangamMN"] = (_FontInfo){"Oriya Sangam MN", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["PhoneKeyCaps"] = (_FontInfo){".PhoneKeyCaps", "Regular", 0x2, 0x59};
+ (*gFontMetadata)["PhoneKeyCapsTwo"] = (_FontInfo){".PhoneKeyCapsTwo", "Regular", 0x2, 0x59};
+ (*gFontMetadata)["PhonepadTwo"] = (_FontInfo){".PhonepadTwo", "Regular", 0x0, 0x59};
+ (*gFontMetadata)["STHeiti-Light"] = (_FontInfo){"Heiti TC", "Light", 0x0, 0x1b};
+ (*gFontMetadata)["STHeiti-Medium"] = (_FontInfo){"Heiti TC", "Medium", 0x2, 0x19};
+ (*gFontMetadata)["SinhalaSangamMN"] = (_FontInfo){"Sinhala Sangam MN", "Regular", 0x0, 0x59};
+ (*gFontMetadata)["TamilSangamMN"] = (_FontInfo){"Tamil Sangam MN", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["TeluguSangamMN"] = (_FontInfo){"Telugu Sangam MN", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["Thonburi"] = (_FontInfo){"Thonburi", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["ThonburiBold"] = (_FontInfo){"Thonburi", "Bold", 0x2, 0x19};
+ (*gFontMetadata)["TimesNewRoman"] = (_FontInfo){"Times New Roman", "Regular", 0x0, 0x59};
+ (*gFontMetadata)["TimesNewRomanBold"] = (_FontInfo){"Times New Roman", "Bold", 0x2, 0x59};
+ (*gFontMetadata)["TimesNewRomanBoldItalic"] = (_FontInfo){"Times New Roman", "Bold Italic", 0x3, 0x59};
+ (*gFontMetadata)["TimesNewRomanItalic"] = (_FontInfo){"Times New Roman", "Italic", 0x1, 0x59};
+ (*gFontMetadata)["TrebuchetMS"] = (_FontInfo){"Trebuchet MS", "Regular", 0x0, 0x59};
+ (*gFontMetadata)["TrebuchetMSBold"] = (_FontInfo){"Trebuchet MS", "Bold", 0x2, 0x59};
+ (*gFontMetadata)["TrebuchetMSBoldItalic"] = (_FontInfo){"Trebuchet MS", "Bold Italic", 0x3, 0x59};
+ (*gFontMetadata)["TrebuchetMSItalic"] = (_FontInfo){"Trebuchet MS", "Italic", 0x1, 0x59};
+ (*gFontMetadata)["Verdana"] = (_FontInfo){"Verdana", "Regular", 0x0, 0x59};
+ (*gFontMetadata)["VerdanaBold"] = (_FontInfo){"Verdana", "Bold", 0x2, 0x19};
+ (*gFontMetadata)["VerdanaBoldItalic"] = (_FontInfo){"Verdana", "Bold Italic", 0x3, 0x19};
+ (*gFontMetadata)["VerdanaItalic"] = (_FontInfo){"Verdana", "Italic", 0x1, 0x19};
+ (*gFontMetadata)["Zapfino"] = (_FontInfo){"Zapfino", "Regular", 0x1, 0x19};
+ (*gFontMetadata)["_H_AmericanTypewriter"] = (_FontInfo){"American Typewriter", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["_H_Baskerville"] = (_FontInfo){"Baskerville", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["_H_ChalkboardSE"] = (_FontInfo){"Chalkboard SE", "Light", 0x0, 0x59};
+ (*gFontMetadata)["_H_Cochin"] = (_FontInfo){"Cochin", "Regular", 0x0, 0x5b};
+ (*gFontMetadata)["_H_Courier"] = (_FontInfo){"Courier", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["_H_Futura"] = (_FontInfo){"Futura", "Medium", 0x0, 0x19};
+ (*gFontMetadata)["_H_Helvetica"] = (_FontInfo){"Helvetica", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["_H_HelveticaNeue"] = (_FontInfo){"Helvetica Neue", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["_H_HelveticaNeueExtras"] = (_FontInfo){"Helvetica Neue", "Light", 0x0, 0x19};
+ (*gFontMetadata)["_H_MarkerFeltThin"] = (_FontInfo){"Marker Felt", "Thin", 0x0, 0x59};
+ (*gFontMetadata)["_H_MarkerFeltWide"] = (_FontInfo){"Marker Felt", "Wide", 0x2, 0x59};
+ (*gFontMetadata)["_H_Noteworthy"] = (_FontInfo){"Noteworthy", "Light", 0x0, 0x59};
+ (*gFontMetadata)["_H_Palatino"] = (_FontInfo){"Palatino", "Regular", 0x0, 0x59};
+ (*gFontMetadata)["_H_SnellRoundhand"] = (_FontInfo){"Snell Roundhand", "Regular", 0x0, 0x59};
+ (*gFontMetadata)["_H__PO_Bodoni-Ornaments"] = (_FontInfo){"Bodoni Ornaments", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["_H__PO_Bodoni72-Book-SmallCaps"] = (_FontInfo){"Bodoni 72 Smallcaps", "Book", 0x0, 0x19};
+ (*gFontMetadata)["_H__PO_Bodoni72-OldStyle"] = (_FontInfo){"Bodoni 72 Oldstyle", "Book", 0x0, 0x19};
+ (*gFontMetadata)["_H__PO_Bodoni72"] = (_FontInfo){"Bodoni 72", "Book", 0x0, 0x19};
+ (*gFontMetadata)["_H__PO_BradleyHand-Bold"] = (_FontInfo){"Bradley Hand", "Bold", 0x0, 0x19};
+ (*gFontMetadata)["_H__PO_Didot"] = (_FontInfo){"Didot", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["_H__PO_GillSans"] = (_FontInfo){"Gill Sans", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["_H__PO_HoeflerText"] = (_FontInfo){"Hoefler Text", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["_H__PO_Optima"] = (_FontInfo){"Optima", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["_H__PO_PartyLET"] = (_FontInfo){"Party LET", "Plain", 0x0, 0x19};
+ (*gFontMetadata)["_H__PO_ZapfDingbats"] = (_FontInfo){"Zapf Dingbats", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["_PO_AcademyEngraved"] = (_FontInfo){"Academy Engraved LET", "Plain", 0x0, 0x19};
+ (*gFontMetadata)["_PO_Chalkduster"] = (_FontInfo){"Chalkduster", "Regular", 0x0, 0x59};
+ (*gFontMetadata)["_PO_Copperplate"] = (_FontInfo){"Copperplate", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["_PO_HiraginoMinchoProNW3"] = (_FontInfo){"Hiragino Mincho ProN", "W3", 0x0, 0x39};
+ (*gFontMetadata)["_PO_HiraginoMinchoProNW6"] = (_FontInfo){"Hiragino Mincho ProN", "W6", 0x2, 0x39};
+ (*gFontMetadata)["_PO_Papyrus"] = (_FontInfo){"Papyrus", "Regular", 0x0, 0x19};
+// iOS 5.0
+ (*gFontMetadata)["AcademyEngraved"] = (_FontInfo){"Academy Engraved LET", "Plain", 0x0, 0x19};
+ (*gFontMetadata)["Chalkduster"] = (_FontInfo){"Chalkduster", "Regular", 0x0, 0x59};
+ (*gFontMetadata)["Copperplate"] = (_FontInfo){"Copperplate", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["EuphemiaCAS"] = (_FontInfo){"Euphemia UCAS", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["HiraginoMinchoProNW3"] = (_FontInfo){"Hiragino Mincho ProN", "W3", 0x0, 0x39};
+ (*gFontMetadata)["HiraginoMinchoProNW6"] = (_FontInfo){"Hiragino Mincho ProN", "W6", 0x2, 0x39};
+ (*gFontMetadata)["Papyrus"] = (_FontInfo){"Papyrus", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["STFangsongCore"] = (_FontInfo){".STFangsongCore", "Regular", 0x0, 0x39};
+ (*gFontMetadata)["STKaitiCore"] = (_FontInfo){".STKaitiCore", "Regular", 0x0, 0x39};
+ (*gFontMetadata)["STSongCore"] = (_FontInfo){".STSongCore", "Regular", 0x0, 0x39};
+ (*gFontMetadata)["_H_Bodoni-Ornaments"] = (_FontInfo){"Bodoni Ornaments", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["_H_Bodoni72-Book-SmallCaps"] = (_FontInfo){"Bodoni 72 Smallcaps", "Book", 0x0, 0x19};
+ (*gFontMetadata)["_H_Bodoni72-OldStyle"] = (_FontInfo){"Bodoni 72 Oldstyle", "Book", 0x0, 0x19};
+ (*gFontMetadata)["_H_Bodoni72"] = (_FontInfo){"Bodoni 72", "Book", 0x0, 0x19};
+ (*gFontMetadata)["_H_BradleyHand-Bold"] = (_FontInfo){"Bradley Hand", "Bold", 0x0, 0x19};
+ (*gFontMetadata)["_H_Didot"] = (_FontInfo){"Didot", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["_H_GillSans"] = (_FontInfo){"Gill Sans", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["_H_HoeflerText"] = (_FontInfo){"Hoefler Text", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["_H_Marion"] = (_FontInfo){"Marion", "Regular", 0x0, 0x59};
+ (*gFontMetadata)["_H_Optima"] = (_FontInfo){"Optima", "Regular", 0x0, 0x19};
+ (*gFontMetadata)["_H_PartyLET"] = (_FontInfo){"Party LET", "Plain", 0x0, 0x19};
+ (*gFontMetadata)["_H_ZapfDingbats"] = (_FontInfo){"Zapf Dingbats", "Regular", 0x0, 0x19};
+// iOS 6.0
+ (*gFontMetadata)["AppleColorEmoji@2x"] = (_FontInfo){"Apple Color Emoji", "Regular", 0x0, 0x39};
+ (*gFontMetadata)["AppleSDGothicNeoBold"] = (_FontInfo){"Apple SD Gothic Neo", "Bold", 0x0, 0x39};
+ (*gFontMetadata)["AppleSDGothicNeoMedium"] = (_FontInfo){"Apple SD Gothic Neo", "Medium", 0x0, 0x39};
+ (*gFontMetadata)["Symbol"] = (_FontInfo){"Symbol", "Regular", 0x0, 0x1b};
+ (*gFontMetadata)["_H_Avenir"] = (_FontInfo){"Avenir", "Book", 0x0, 0x59};
+ (*gFontMetadata)["_H_AvenirNext"] = (_FontInfo){"Avenir Next", "Bold", 0x2, 0x59};
+ (*gFontMetadata)["_H_AvenirNextCondensed"] = (_FontInfo){"Avenir Next Condensed", "Bold", 0x2, 0x59};
+
+#endif
+}
+
+#endif
diff --git a/Runtime/Filters/Misc/LineBuilder.cpp b/Runtime/Filters/Misc/LineBuilder.cpp
new file mode 100644
index 0000000..5ef855e
--- /dev/null
+++ b/Runtime/Filters/Misc/LineBuilder.cpp
@@ -0,0 +1,95 @@
+#include "UnityPrefix.h"
+#include "LineBuilder.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+inline Vector2f Calculate2DLineExtrusionAverage (const Vector3f& p0, const Vector3f& delta, const Vector3f& delta2, float halfWidth)
+{
+ Vector2f dif;
+ dif.x = p0.y * delta.z - p0.z * delta.y;
+ dif.y = p0.z * delta.x - p0.x * delta.z;
+// dif = NormalizeFast(dif);
+
+ Vector2f dif2;
+ dif2.x = p0.y * delta2.z - p0.z * delta2.y;
+ dif2.y = p0.z * delta2.x - p0.x * delta2.z;
+// dif2 = NormalizeFast(dif2);
+
+ dif += dif2;
+ dif = NormalizeFast(dif);
+
+ dif.x *= halfWidth;
+ dif.y *= halfWidth;
+
+ return dif;
+/* Vector2f dif;
+ dif.x = p0.y * delta.z - p0.z * delta.y;
+ dif.y = p0.z * delta.x - p0.x * delta.z;
+*/
+}
+
+/// \todo have optional input lengths for speed
+/// \todo optimize the dif cross product (z is unused)
+void Build3DLine( LineParameters *param, const Vector3f *inVertices, int vertexCount )
+{
+ Assert(vertexCount > 1);
+ Assert(param->outVertices && param->outAABB);
+
+ LineVertex *outVertices = param->outVertices;
+ Matrix4x4f matrix = param->cameraTransform;
+
+ // As Gradient->GetFixed() needs an unnormalized position in 16.16 format
+ // (upper 16 color index, lower 16 - how far between), the max value is
+ // (nr of gradient colors - 1)*2^16 - 1
+ float fixedMult = (float)(((k3DLineGradientSize - 1) << 16) - 1);
+
+ GfxDevice& device = GetGfxDevice();
+
+ // Skip last vertex
+ Vector3f delta = matrix.MultiplyPoint3 (inVertices[0]) - matrix.MultiplyPoint3 (inVertices[1]);
+ for (int i=0;i<vertexCount;i++)
+ {
+ // Don't accumulate by adding deltaU, as the rounding error accumulates as well.
+ // Calculate u each time anew instead.
+ float u = i/(float)(vertexCount - 1);
+
+ // Calculate width and figure a cross section that faces the camera
+ Vector3f p0 = matrix.MultiplyPoint3 (inVertices[i]);
+
+ if (i+1 != vertexCount)
+ {
+ Vector3f p1 = matrix.MultiplyPoint3 (inVertices[i+1]);
+ delta = p0 - p1;
+ }
+
+ float width = Lerp(param->startWidth, param->endWidth, u);
+ Vector2f dif = Calculate2DLineExtrusion (p0, delta, width * 0.5F);
+
+ ColorRGBA32 color;
+ if(param->gradient)
+ color = param->gradient->GetFixed (UInt32(u*fixedMult));
+ else
+ // TODO: rewrite Gradient, so that we can elegantly use it here as well
+ color = Lerp((ColorRGBAf)param->color1, (ColorRGBAf)param->color2, u);
+
+ // Swizzle color of the renderer requires it
+ color = device.ConvertToDeviceVertexColor(color);
+
+ // One vertex
+ outVertices->vert.Set( p0.x - dif.x, p0.y - dif.y, p0.z );
+ outVertices->color = color;
+ outVertices->uv.Set( u, 1.0f );
+ ++outVertices;
+
+ // And another vertex
+ outVertices->vert.Set( p0.x + dif.x, p0.y + dif.y, p0.z );
+ outVertices->color = color;
+ outVertices->uv.Set( u, 0.0f );
+ ++outVertices;
+
+ param->outAABB->Encapsulate (inVertices[i]);
+ }
+ param->outAABB->Encapsulate (inVertices[vertexCount-1]);
+}
diff --git a/Runtime/Filters/Misc/LineBuilder.h b/Runtime/Filters/Misc/LineBuilder.h
new file mode 100644
index 0000000..ffc46a8
--- /dev/null
+++ b/Runtime/Filters/Misc/LineBuilder.h
@@ -0,0 +1,88 @@
+#ifndef LINEBUILDER_H
+#define LINEBUILDER_H
+
+class MinMaxAABB;
+
+#include "Runtime/Math/Gradient.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Color.h"
+
+enum { k3DLineGradientSize = 5 };
+
+struct LineVertex {
+ Vector3f vert;
+ ColorRGBA32 color;
+ Vector2f uv;
+};
+
+// Settings for the Build3DLine function
+// Instead of passing lots of parameters, make one of these and use that instead.
+struct LineParameters
+{
+ DECLARE_SERIALIZE (LineParameters)
+
+ LineVertex* outVertices; // Output vertices; 2 * input vertices size
+ class MinMaxAABB *outAABB; // AABB to be generated
+
+ // ptr to the gradient used for color generation
+ GradientDeprecated<k3DLineGradientSize> *gradient;
+ ColorRGBA32 color1;
+ ColorRGBA32 color2;
+ Matrix4x4f cameraTransform;
+
+ float startWidth; ///< The width (in worldspace) at the line start.
+ float endWidth; ///< The width (in worldspace) at the line end.
+
+ LineParameters () :
+ outVertices (NULL), outAABB (NULL),
+ gradient (NULL), startWidth (1), endWidth (1),
+ color1 (0), color2 (0) { cameraTransform = Matrix4x4f::identity; }
+};
+
+template<class TransferFunction>
+inline void LineParameters::Transfer (TransferFunction& transfer) {
+ TRANSFER_SIMPLE (startWidth);
+ TRANSFER_SIMPLE (endWidth);
+ transfer.Transfer (color1, "m_StartColor", kSimpleEditorMask);
+ transfer.Transfer (color2, "m_EndColor", kSimpleEditorMask);
+}
+
+/// build the mesh for a 3D line segement seen from the current camera
+/// @param param generation parameters.
+/// @param in ptr to the input vertices
+/// @param vertexCount the number of vertices in inVertices
+void Build3DLine (LineParameters *param, const Vector3f *in, int vertexCount);
+
+/// Calculates the 2D line extrusion, so that the line is halfWidth * 2 wide and always faces the viewer.
+/// The start point is p0, the endpoint is p0 + delta
+/// The points are expected to be in camera space.
+inline Vector2f Calculate2DLineExtrusion (const Vector3f& p0, const Vector3f& delta, float halfWidth)
+{
+ #if 1
+ Vector2f dif;
+ dif.x = p0.y * delta.z - p0.z * delta.y;
+ dif.y = p0.z * delta.x - p0.x * delta.z;
+
+ dif = NormalizeFast(dif);
+
+ dif.x *= halfWidth;
+ dif.y *= halfWidth;
+
+ return dif;
+
+ #else
+
+ Vector3f dif = Cross (p0, delta);
+ dif = NormalizeFast (dif);
+
+ dif.x *= halfWidth;
+ dif.y *= halfWidth;
+
+ return Vector2f (dif.x, dif.y);
+
+ #endif
+}
+
+#endif
diff --git a/Runtime/Filters/Misc/LineRenderer.cpp b/Runtime/Filters/Misc/LineRenderer.cpp
new file mode 100644
index 0000000..1bab153
--- /dev/null
+++ b/Runtime/Filters/Misc/LineRenderer.cpp
@@ -0,0 +1,195 @@
+#include "UnityPrefix.h"
+#include "LineRenderer.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Profiler/Profiler.h"
+
+IMPLEMENT_CLASS_INIT_ONLY (LineRenderer)
+IMPLEMENT_OBJECT_SERIALIZE (LineRenderer)
+
+LineRenderer::LineRenderer (MemLabelId label, ObjectCreationMode mode)
+: Super(kRendererLine, label, mode)
+{
+ SetVisible (false);
+}
+
+LineRenderer::~LineRenderer ()
+{
+}
+
+void LineRenderer::InitializeClass ()
+{
+ RegisterAllowNameConversion (LineRenderer::GetClassStringStatic(), "m_WorldSpace", "m_UseWorldSpace");
+}
+
+
+void LineRenderer::SetVertexCount(int count)
+{
+ if(count < 0)
+ {
+ count = 0;
+ ErrorString ("LineRenderer.SetVertexCount: Vertex count can't be set to negative value!");
+ }
+ UpdateManagerState( true );
+ m_Positions.resize(count);
+ SetVisible (m_Positions.size() >= 2);
+ SetDirty();
+ BoundsChanged();
+}
+
+void LineRenderer::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad(awakeMode);
+ SetVisible (m_Positions.size() >= 2);
+
+ if ((awakeMode & kDidLoadFromDisk) == 0)
+ BoundsChanged();
+}
+
+void LineRenderer::SetPosition (int index, const Vector3f& position)
+{
+ SetDirty();
+ UpdateManagerState( true );
+ if (index < m_Positions.size() && index >= 0)
+ m_Positions[index] = position;
+ else
+ ErrorString("LineRenderer.SetPosition index out of bounds!");
+ BoundsChanged();
+}
+
+PROFILER_INFORMATION(gSubmitVBOProfileLine, "Mesh.SubmitVBO", kProfilerRender)
+
+
+void LineRenderer::Render (int subsetIndex, const ChannelAssigns& channels)
+{
+ if( m_Positions.size() < 2 )
+ return;
+
+ Vector3f* lineInVerts = NULL;
+ ALLOC_TEMP(lineInVerts, Vector3f, m_Positions.size());
+
+ MinMaxAABB mmAABB = MinMaxAABB(Vector3f::zero, Vector3f::zero);
+
+ if (m_UseWorldSpace)
+ {
+ memcpy (lineInVerts, &m_Positions[0], m_Positions.size()*sizeof(Vector3f));
+ }
+ else
+ {
+ Transform& tc = GetComponent(Transform);
+ int idx = 0;
+ for (PositionVector::iterator j = m_Positions.begin(); j != m_Positions.end(); ++j, ++idx)
+ {
+ lineInVerts[idx] = tc.TransformPoint(*j);
+ }
+ }
+
+ // Get VBO chunk
+ int stripCount = m_Positions.size() * 2;
+ GfxDevice& device = GetGfxDevice();
+ DynamicVBO& vbo = device.GetDynamicVBO();
+ LineVertex* vbPtr;
+ if( !vbo.GetChunk( (1<<kShaderChannelVertex) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelColor),
+ stripCount, 0,
+ DynamicVBO::kDrawTriangleStrip,
+ (void**)&vbPtr, NULL ) )
+ {
+ return;
+ }
+
+ // Generate line into the chunk
+ m_Parameters.outVertices = vbPtr;
+ m_Parameters.outAABB = &mmAABB;
+ m_Parameters.cameraTransform = GetCurrentCamera().GetWorldToCameraMatrix();
+ Build3DLine (&m_Parameters, lineInVerts, m_Positions.size());
+
+ vbo.ReleaseChunk( stripCount, 0 );
+
+ // We can't set the view matrix since that breaks shadow maps (case 490315)
+ // Set the world matrix instead so it cancels out the usual view matrix
+ // i.e. it transforms from camera space back to world space
+ device.SetWorldMatrix(GetCurrentCamera().GetCameraToWorldMatrix().GetPtr());
+
+ if (m_CustomProperties)
+ device.SetMaterialProperties (*m_CustomProperties);
+
+ PROFILER_BEGIN(gSubmitVBOProfileLine, this)
+ vbo.DrawChunk (channels);
+ GPU_TIMESTAMP();
+ PROFILER_END
+}
+
+void LineRenderer::UpdateTransformInfo ()
+{
+ const Transform& transform = GetTransform();
+ if (m_TransformDirty)
+ {
+ m_TransformInfo.invScale = 1.0f;
+ // will return a cached matrix most of the time
+ m_TransformInfo.transformType = transform.CalculateTransformMatrix (m_TransformInfo.worldMatrix);;
+ }
+
+ if (m_BoundsDirty)
+ {
+ MinMaxAABB minmax;
+ minmax.Init();
+ for (PositionVector::const_iterator i = m_Positions.begin(), itEnd = m_Positions.end(); i != itEnd; ++i)
+ minmax.Encapsulate (*i);
+
+ if (m_UseWorldSpace)
+ {
+ m_TransformInfo.worldAABB = minmax;
+ TransformAABB (m_TransformInfo.worldAABB, transform.GetWorldToLocalMatrix(), m_TransformInfo.localAABB);
+ }
+ else
+ {
+ m_TransformInfo.localAABB = minmax;
+ TransformAABB (m_TransformInfo.localAABB, transform.GetLocalToWorldMatrix(), m_TransformInfo.worldAABB);
+ }
+ }
+}
+
+
+
+void LineRenderer::UpdateRenderer()
+{
+ Super::UpdateRenderer();
+ if (m_BoundsDirty)
+ {
+ BoundsChanged();
+ }
+}
+
+void LineRenderer::Reset()
+{
+ Super::Reset();
+ m_UseWorldSpace = true;
+ m_Positions.clear();
+ m_Positions.push_back (Vector3f (0,0,0));
+ m_Positions.push_back (Vector3f (0,0,1));
+ m_Parameters.color1 = ColorRGBA32(255, 255, 255, 255);
+ m_Parameters.color2 = ColorRGBA32(255, 255, 255, 255);
+ SetVisible (true);
+}
+
+void LineRenderer::SetUseWorldSpace (bool space)
+{
+ m_UseWorldSpace = space;
+ SetDirty();
+ UpdateManagerState( true );
+ BoundsChanged();
+}
+
+template<class TransferFunction> inline
+void LineRenderer::Transfer (TransferFunction& transfer) {
+ Super::Transfer (transfer);
+ TRANSFER_SIMPLE (m_Positions);
+ TRANSFER_SIMPLE (m_Parameters);
+ TRANSFER (m_UseWorldSpace);
+}
diff --git a/Runtime/Filters/Misc/LineRenderer.h b/Runtime/Filters/Misc/LineRenderer.h
new file mode 100644
index 0000000..3d05758
--- /dev/null
+++ b/Runtime/Filters/Misc/LineRenderer.h
@@ -0,0 +1,61 @@
+#ifndef LINERENDERER_H
+#define LINERENDERER_H
+
+#include <vector>
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Geometry/AABB.h"
+#include "LineBuilder.h"
+
+
+
+// Renders a freeform texture/colored line in 3D space.
+// (heavily based on TrailRenderer code, so most comments apply to both)
+class LineRenderer : public Renderer {
+public:
+ REGISTER_DERIVED_CLASS (LineRenderer, Renderer)
+ DECLARE_OBJECT_SERIALIZE (LineRenderer)
+
+ LineRenderer (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void Reset ();
+
+ virtual void Render (int materialIndex, const ChannelAssigns& channels);
+
+ // Can operate in either local or world space, so we need to fill whole transform info ourselves
+ virtual void UpdateTransformInfo();
+
+ void SetPosition (int index, const Vector3f& position);
+
+ void SetVertexCount(int count);
+
+ void SetColors(const ColorRGBAf& c0, const ColorRGBAf& c1) { m_Parameters.color1 = c0; m_Parameters.color2 = c1; SetDirty(); }
+
+ void SetWidth(float startWidth,float endWidth)
+ {
+ m_Parameters.startWidth = startWidth;
+ m_Parameters.endWidth = endWidth;
+ BoundsChanged();
+ SetDirty();
+ }
+
+ bool GetUseWorldSpace () { return m_UseWorldSpace; }
+ void SetUseWorldSpace (bool space);
+
+ void AwakeFromLoad(AwakeFromLoadMode mode);
+ static void InitializeClass ();
+
+protected:
+ // from Renderer
+ virtual void UpdateRenderer();
+
+private:
+// bool m_BoundsDirty;
+ bool m_UseWorldSpace; ///< Draw lines in worldspace (or localspace)
+ LineParameters m_Parameters;
+ typedef UNITY_VECTOR(kMemRenderer,Vector3f) PositionVector;
+ PositionVector m_Positions;
+};
+
+#endif
diff --git a/Runtime/Filters/Misc/MiniCoreText.h b/Runtime/Filters/Misc/MiniCoreText.h
new file mode 100644
index 0000000..1027645
--- /dev/null
+++ b/Runtime/Filters/Misc/MiniCoreText.h
@@ -0,0 +1,38 @@
+#if !defined(MINICORETEXT_H)
+#define MINICORETEXT_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef float CGFloat;
+typedef const struct __CTFont * CTFontRef;
+typedef const struct __CTFontDescriptor * CTFontDescriptorRef;
+typedef uint32_t CTFontOrientation;
+typedef const struct __CTLine * CTLineRef;
+
+enum {
+ kCTFontDefaultOrientation = 0,
+};
+
+extern const CFStringRef kCTForegroundColorAttributeName WEAK_IMPORT_ATTRIBUTE;
+extern const CFStringRef kCTFontAttributeName WEAK_IMPORT_ATTRIBUTE;
+
+
+CTFontRef CTFontCreateWithName(CFStringRef name, CGFloat size, const CGAffineTransform *matrix) WEAK_IMPORT_ATTRIBUTE;
+CTFontRef CTFontCreateWithGraphicsFont(CGFontRef graphicsFont, CGFloat size, const CGAffineTransform *matrix, CTFontDescriptorRef attributes) WEAK_IMPORT_ATTRIBUTE;
+CGFloat CTFontGetAscent(CTFontRef font) WEAK_IMPORT_ATTRIBUTE;
+CGFloat CTFontGetDescent(CTFontRef font) WEAK_IMPORT_ATTRIBUTE;
+Boolean CTFontGetGlyphsForCharacters(CTFontRef font, const UniChar characters[], CGGlyph glyphs[], CFIndex count) WEAK_IMPORT_ATTRIBUTE;
+CGRect CTFontGetBoundingRectsForGlyphs(CTFontRef font, CTFontOrientation orientation, const CGGlyph glyphs[], CGRect boundingRects[], CFIndex count ) WEAK_IMPORT_ATTRIBUTE;
+double CTFontGetAdvancesForGlyphs(CTFontRef font, CTFontOrientation orientation, const CGGlyph glyphs[], CGSize advances[], CFIndex count) WEAK_IMPORT_ATTRIBUTE;
+CTLineRef CTLineCreateWithAttributedString(CFAttributedStringRef string) WEAK_IMPORT_ATTRIBUTE;
+void CTLineDraw(CTLineRef line, CGContextRef context) WEAK_IMPORT_ATTRIBUTE;
+CTFontRef CTFontCreateForString(CTFontRef currentFont, CFStringRef string, CFRange range) WEAK_IMPORT_ATTRIBUTE;
+CGFontRef CGFontCreateWithDataProvider(CGDataProviderRef provider) WEAK_IMPORT_ATTRIBUTE;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/Runtime/Filters/Misc/TextMesh.cpp b/Runtime/Filters/Misc/TextMesh.cpp
new file mode 100644
index 0000000..80b6b76
--- /dev/null
+++ b/Runtime/Filters/Misc/TextMesh.cpp
@@ -0,0 +1,298 @@
+#include "UnityPrefix.h"
+#include "TextMesh.h"
+#include "Runtime/Filters/Mesh/MeshRenderer.h"
+#include "Runtime/IMGUI/TextMeshGenerator2.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Font.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+
+using namespace std;
+namespace TextMesh_Static
+{
+static Font* gDefaultFont = NULL;
+}
+
+
+TextMesh::TextMesh (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_Mesh = NULL;
+ m_FontSize = 0;
+ m_FontStyle = 0;
+ m_RichText = true;
+ m_Color = 0xffffffff;
+}
+
+TextMesh::~TextMesh ()
+{
+ DestroySingleObject(m_Mesh);
+}
+
+Mesh* TextMesh::GetMesh ()
+{
+ if (m_Mesh)
+ return m_Mesh;
+ else
+ {
+ m_Mesh = NEW_OBJECT (Mesh);
+ m_Mesh->Reset();
+ m_Mesh->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+
+ m_Mesh->SetHideFlags(kHideAndDontSave);
+ return m_Mesh;
+ }
+}
+
+void TextMesh::Reset () {
+ Super::Reset();
+
+ m_OffsetZ = 0.0f;
+ m_CharacterSize = 1.0f;
+ m_Anchor = kUpperLeft;
+ m_Alignment = kLeft;
+ m_LineSpacing = 1.0F;
+ m_TabSize = 4.0F;
+}
+
+Font * TextMesh::GetFont () const {
+ using namespace TextMesh_Static;
+ Font *f = m_Font;
+ if (!f) {
+ if (!gDefaultFont)
+ gDefaultFont = GetBuiltinResource<Font> (kDefaultFontName);
+ return gDefaultFont;
+ }
+ else {
+ return f;
+ }
+}
+
+void TextMesh::AwakeFromLoad(AwakeFromLoadMode awakeMode) {
+ Super::AwakeFromLoad (awakeMode);
+
+ if (IsActive())
+ {
+ SetupMeshRenderer ();
+ ApplyToMesh ();
+ }
+}
+
+void TextMesh::SetText (const string& text) {
+ if (m_Text != text)
+ {
+ m_Text = text;
+ ApplyToMesh();
+ }
+ SetDirty();
+}
+
+void TextMesh::SetFont (PPtr<Font> font)
+{
+ if (m_Font != font)
+ {
+ m_Font = font;
+ ApplyToMesh();
+ }
+ SetDirty();
+}
+
+void TextMesh::SetFontSize (int size)
+{
+ if (m_FontSize != size)
+ {
+ m_FontSize = size;
+ ApplyToMesh();
+ }
+ SetDirty();
+}
+
+void TextMesh::SetFontStyle (int style)
+{
+ if (m_FontStyle != style)
+ {
+ m_FontStyle = style;
+ ApplyToMesh();
+ }
+ SetDirty();
+}
+
+void TextMesh::SetAlignment(short alignment)
+{
+ if (m_Alignment != alignment)
+ {
+ m_Alignment = alignment;
+ ApplyToMesh();
+ }
+ SetDirty();
+}
+
+void TextMesh::SetOffsetZ(float offset)
+{
+ if (m_OffsetZ != offset)
+ {
+ m_OffsetZ = offset;
+ ApplyToMesh();
+ }
+ SetDirty();
+}
+
+void TextMesh::SetAnchor(short anchor)
+{
+ if (m_Anchor != anchor)
+ {
+ m_Anchor = anchor;
+ ApplyToMesh();
+ }
+ SetDirty();
+}
+
+void TextMesh::SetCharacterSize(float characterSize)
+{
+ if (m_CharacterSize != characterSize)
+ {
+ m_CharacterSize = characterSize;
+ ApplyToMesh();
+ }
+ SetDirty();
+}
+
+void TextMesh::SetLineSpacing(float lineSpacing)
+{
+ if (m_LineSpacing != lineSpacing)
+ {
+ m_LineSpacing = lineSpacing;
+ ApplyToMesh();
+ }
+ SetDirty();
+}
+
+void TextMesh::SetTabSize(float tabSize)
+{
+ if (m_TabSize != tabSize)
+ {
+ m_TabSize = tabSize;
+ ApplyToMesh();
+ }
+ SetDirty();
+}
+
+void TextMesh::SetRichText(bool richText)
+{
+ if (m_RichText != richText)
+ {
+ m_RichText = richText;
+ ApplyToMesh();
+ }
+ SetDirty();
+}
+
+void TextMesh::SetColor(const ColorRGBA32 color)
+{
+ if (m_Color != color)
+ {
+ m_Color = color;
+ ApplyToMesh();
+ }
+ SetDirty();
+}
+
+void TextMesh::SetupMeshRenderer () {
+ if (IsActive ())
+ {
+ MeshRenderer* renderer = QueryComponent(MeshRenderer);
+ if (renderer)
+ renderer->SetSharedMesh(GetMesh());
+ }
+}
+
+void TextMesh::DidAddComponent () {
+ if (IsActive ())
+ {
+ MeshRenderer* renderer = QueryComponent(MeshRenderer);
+ if (renderer)
+ renderer->SetSharedMesh(GetMesh());
+ }
+}
+
+void TextMesh::ApplyToMesh ()
+{
+ Mesh * mesh = GetMesh();
+ // Setup textmesh generator
+ TextMeshGenerator2 &tmgen = TextMeshGenerator2::Get (UTF16String(m_Text.c_str()), GetFont (), (TextAnchor)m_Anchor, (TextAlignment)m_Alignment, 0, m_TabSize, m_LineSpacing, m_RichText, false, m_Color, m_FontSize, m_FontStyle);
+
+ Vector2f size = tmgen.GetSize ();
+ Vector2f offset = tmgen.GetTextOffset (Rectf (0, 0, -size.x, size.y * 2));
+ switch (m_Alignment)
+ {
+ case kRight: offset.x += size.x; break;
+ case kCenter: offset.x += size.x * 0.5f; break;
+ }
+
+ Mesh* srcMesh = tmgen.GetMesh ();
+ Matrix4x4f m;
+ Vector3f scale(m_CharacterSize, -m_CharacterSize, m_CharacterSize);
+ scale *= GetFont()->GetDeprecatedPixelScale ();
+ m.SetTranslate (Vector3f(offset.x * scale.x, offset.y * -scale.y, m_OffsetZ));
+ m.Scale(scale);
+ mesh->CopyTransformed(*srcMesh, m);
+
+ // Mesh CopyTransformed does not update local AABB! Kind of scared to change it there,
+ // so instead manually transform the AABB here.
+ const AABB& bounds = mesh->GetLocalAABB();
+ AABB xformBounds;
+ TransformAABB (bounds, m, xformBounds);
+ mesh->SetLocalAABB (xformBounds);
+
+ MeshRenderer* meshRenderer = QueryComponent(MeshRenderer);
+ if (meshRenderer)
+ meshRenderer->SetSharedMesh(mesh);
+}
+
+IMPLEMENT_CLASS_HAS_INIT (TextMesh)
+IMPLEMENT_OBJECT_SERIALIZE (TextMesh)
+
+template<class TransferFunction> inline
+void TextMesh::Transfer (TransferFunction& transfer)
+{
+ transfer.SetVersion (3);
+ Super::Transfer (transfer);
+ TRANSFER(m_Text);
+ TRANSFER(m_OffsetZ);
+ TRANSFER(m_CharacterSize);
+ TRANSFER(m_LineSpacing);
+ TRANSFER(m_Anchor);
+ TRANSFER(m_Alignment);
+ TRANSFER(m_TabSize);
+
+ TRANSFER(m_FontSize);
+ TRANSFER(m_FontStyle);
+
+ TRANSFER(m_RichText);
+
+ transfer.Align();
+
+ TRANSFER (m_Font);
+ TRANSFER (m_Color);
+
+
+ #if UNITY_EDITOR
+ // Renamed m_Settings to m_Font in version 1.2.2
+ if (transfer.IsOldVersion(1))
+ {
+ transfer.Transfer(m_Font, "m_Settings");
+ }
+
+ // In version 1.5.0 line spacing is multiplicative instead of additive
+ if (transfer.IsOldVersion(1) || transfer.IsOldVersion(2))
+ {
+ Font* font = GetFont();
+ m_LineSpacing = (font->GetLineSpacing() + m_LineSpacing) / font->GetLineSpacing();
+ }
+ #endif
+}
+
+void TextMesh::InitializeClass ()
+{
+ REGISTER_MESSAGE_VOID (TextMesh, kDidAddComponent, DidAddComponent);
+}
diff --git a/Runtime/Filters/Misc/TextMesh.h b/Runtime/Filters/Misc/TextMesh.h
new file mode 100644
index 0000000..1a71c8b
--- /dev/null
+++ b/Runtime/Filters/Misc/TextMesh.h
@@ -0,0 +1,99 @@
+#ifndef TEXTMESH_H
+#define TEXTMESH_H
+
+#include "Runtime/Filters/Mesh/Mesh.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Geometry/AABB.h"
+#include <string>
+#include <vector>
+
+using std::vector;
+class Font;
+class Mesh;
+
+
+
+
+class TextMesh : public Unity::Component {
+ public:
+ REGISTER_DERIVED_CLASS (TextMesh, Component)
+ DECLARE_OBJECT_SERIALIZE (TextMesh)
+
+ TextMesh (MemLabelId label, ObjectCreationMode mode);
+ // ~TextMesh (); declared-by-macro
+
+ const UnityStr& GetText () const { return m_Text; }
+ void SetText (const std::string& text);
+
+ void SetFont (PPtr<Font> font);
+ Font * GetFont () const;
+
+ void SetFontSize (int size);
+ int GetFontSize() const { return m_FontSize; }
+
+ void SetFontStyle (int style);
+ int GetFontStyle() const { return m_FontStyle; }
+
+ void SetOffsetZ(float offset);
+ float GetOffsetZ(){ return m_OffsetZ; }
+
+ void SetAlignment(short alignment);
+ short GetAlignment(){ return m_Alignment; }
+
+ void SetAnchor(short anchor);
+ short GetAnchor(){ return m_Anchor; }
+
+ void SetCharacterSize(float characterSize);
+ float GetCharacterSize(){ return m_CharacterSize; }
+
+ void SetLineSpacing(float lineSpacing);
+ float GetLineSpacing(){ return m_LineSpacing; }
+
+ void SetTabSize(float tabSize);
+ float GetTabSize(){ return m_TabSize; }
+
+ void SetRichText(bool richText);
+ bool GetRichText() { return m_RichText; }
+
+ void SetColor(const ColorRGBA32 color);
+ ColorRGBA32 GetColor() const { return m_Color; }
+
+ void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ virtual void Reset ();
+ void DidAddComponent ();
+
+ static void InitializeClass();
+ static void CleanupClass() {}
+
+ void ApplyToMesh ();
+ private:
+
+ void SetupMeshRenderer();
+
+ UnityStr m_Text;
+
+ PPtr<Font> m_Font;
+
+ float m_OffsetZ; ///< How much to offset the generated mesh from the Z-position=0.
+ short m_Alignment; ///< enum { left, center, right }
+ short m_Anchor; ///< Where the text-mesh is anchored related to local origo. enum { upper left, upper center, upper right, middle left, middle center, middle right, lower left, lower center, lower right }
+ float m_CharacterSize; ///< Size of one character (as its height, since Aspect may change its width)
+ float m_LineSpacing; ///< Spacing between lines as multiplum of height of a character.
+ float m_TabSize; ///< Length of one tab
+
+ int m_FontSize; ///<The font size to use. Set to 0 to use default font size. Only applicable for dynamic fonts.
+ int m_FontStyle; ///<The font style to use. Only applicable for dynamic fonts. enum { Normal, Bold, Italic, Bold and Italic }
+
+ ColorRGBA32 m_Color;
+ bool m_RichText;
+
+ Mesh* GetMesh ();
+
+ Mesh* m_Mesh;
+};
+
+#endif
diff --git a/Runtime/Filters/Misc/TrailRenderer.cpp b/Runtime/Filters/Misc/TrailRenderer.cpp
new file mode 100644
index 0000000..c6208ee
--- /dev/null
+++ b/Runtime/Filters/Misc/TrailRenderer.cpp
@@ -0,0 +1,197 @@
+#include "UnityPrefix.h"
+#include "TrailRenderer.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/GameCode/DestroyDelayed.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Profiler/Profiler.h"
+
+const float kMinSqrDistance = 0.1f * 0.1f;
+
+IMPLEMENT_CLASS_INIT_ONLY (TrailRenderer)
+IMPLEMENT_OBJECT_SERIALIZE (TrailRenderer)
+
+void TrailRenderer::InitializeClass ()
+{
+ REGISTER_MESSAGE (TrailRenderer, kTransformChanged, TransformChanged, int);
+}
+
+TrailRenderer::TrailRenderer (MemLabelId label, ObjectCreationMode mode)
+: Super(kRendererTrail, label, mode)
+, m_TransformChanged(false)
+, m_WasRendered(false)
+, m_CurrentLength(0)
+, m_Time(0)
+, m_MinVertexDistance(0)
+, m_Autodestruct(false)
+{
+ m_AABB = MinMaxAABB (Vector3f::zero, Vector3f::zero);
+}
+
+TrailRenderer::~TrailRenderer ()
+{
+}
+
+void TrailRenderer::Reset () {
+ Super::Reset ();
+ m_Colors[0].Set (255,255,255,255);
+ m_Colors[1].Set (255,255,255,255);
+ m_Colors[2].Set (255,255,255,255);
+ m_Colors[3].Set (255,255,255,255);
+ m_Colors[4].Set (255,255,255,0);
+ m_Time = 5.0f;
+ m_TransformChanged = true;
+ m_MinVertexDistance = 0.1F;
+ m_Positions.clear();
+ m_TimeStamps.clear();
+}
+
+void TrailRenderer::UpdateRenderer()
+{
+ Super::UpdateRenderer();
+
+ float now = GetCurTime ();
+ // Remove last vertrices if neccessary
+ while (!m_TimeStamps.empty() && now > m_TimeStamps.back() + m_Time) {
+ m_Positions.pop_back();
+ m_TimeStamps.pop_back();
+ }
+
+ // Add a vertex to the object
+ if (m_TransformChanged) {
+ Vector3f position = GetComponent (Transform).GetPosition ();
+ if( m_Positions.empty () || SqrMagnitude (m_Positions.front () - position) > m_MinVertexDistance*m_MinVertexDistance )
+ {
+ m_Positions.push_front (position);
+ m_TimeStamps.push_front (now);
+ }
+
+ float halfWidth = GetHalfMaxLineWidth ();
+ AABB newPosAABB (m_Positions.front (), Vector3f(halfWidth, halfWidth, halfWidth));
+
+ // Expand the BBox with the new transform position.
+ m_AABB.Encapsulate (newPosAABB);
+ BoundsChanged ();
+ }
+
+ if (m_Positions.size() < 2) {
+ if (m_Autodestruct && m_WasRendered && IsWorldPlaying ())
+ DestroyObjectDelayed (GetGameObjectPtr());
+ }
+ else
+ m_WasRendered = true;
+
+ // Important: update manager state after calling any SetVisible() above. Fixes an issue
+ // where trails would stop be rendered when object is disabled or stops moving.
+ UpdateManagerState( true );
+
+ m_TransformChanged = false;
+}
+
+float TrailRenderer::GetHalfMaxLineWidth () const
+{
+ return std::max (m_LineParameters.endWidth, m_LineParameters.startWidth) * 0.5f;
+}
+
+PROFILER_INFORMATION(gTrailRenderProfile, "TrailRenderer.Render", kProfilerRender)
+PROFILER_INFORMATION(gSubmitVBOProfileTrail, "Mesh.SubmitVBO", kProfilerRender)
+
+void TrailRenderer::Render (int materialIndex, const ChannelAssigns& channels)
+{
+ PROFILER_AUTO(gTrailRenderProfile, this)
+
+ int size = m_Positions.size();
+ if( size < 2 )
+ return;
+
+ Vector3f* trailInVerts = NULL;
+ ALLOC_TEMP(trailInVerts, Vector3f, size);
+ int idx = 0;
+ for (std::list<Vector3f>::iterator j = m_Positions.begin(); j != m_Positions.end(); ++j, ++idx)
+ {
+ trailInVerts[idx] = *j;
+ }
+ trailInVerts[0] = GetComponent(Transform).GetPosition();
+
+ // Get VBO chunk
+ int stripCount = size * 2;
+ GfxDevice& device = GetGfxDevice();
+ DynamicVBO& vbo = device.GetDynamicVBO();
+ LineVertex* vbPtr;
+ if( !vbo.GetChunk( (1<<kShaderChannelVertex) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelColor),
+ stripCount, 0,
+ DynamicVBO::kDrawTriangleStrip,
+ (void**)&vbPtr, NULL ) )
+ {
+ return;
+ }
+
+ // Generate line into the chunk
+ MinMaxAABB aabb;
+ m_LineParameters.outVertices = vbPtr;
+ m_LineParameters.gradient = &m_Colors;
+ m_LineParameters.cameraTransform = GetCurrentCamera().GetWorldToCameraMatrix();
+ m_LineParameters.outAABB = &aabb;
+ Build3DLine( &m_LineParameters, trailInVerts, size );
+
+ vbo.ReleaseChunk( stripCount, 0 );
+
+ aabb.Expand (GetHalfMaxLineWidth ());
+
+ if (!CompareMemory (m_AABB, aabb))
+ {
+ m_AABB = aabb;
+ BoundsChanged ();
+ }
+
+ // We can't set the view matrix since that breaks shadow maps (case 490315)
+ // Set the world matrix instead so it cancels out the usual view matrix
+ // i.e. it transforms from camera space back to world space
+ device.SetWorldMatrix(GetCurrentCamera().GetCameraToWorldMatrix().GetPtr());
+
+ if (m_CustomProperties)
+ device.SetMaterialProperties (*m_CustomProperties);
+
+ PROFILER_BEGIN(gSubmitVBOProfileTrail, this)
+ vbo.DrawChunk (channels);
+ GPU_TIMESTAMP();
+ PROFILER_END
+}
+
+void TrailRenderer::TransformChanged (int changeMask)
+{
+ Renderer::TransformChanged (changeMask);
+ m_TransformChanged = true;
+}
+
+void TrailRenderer::UpdateTransformInfo()
+{
+ const Transform& t = GetComponent (Transform);
+
+ TransformType type = t.CalculateTransformMatrix (m_TransformInfo.worldMatrix);
+ m_TransformInfo.transformType = type;
+ m_TransformInfo.invScale = 1.0f;
+
+ m_TransformInfo.worldAABB = m_AABB;
+ InverseTransformAABB( m_TransformInfo.worldAABB, t.GetPosition(), t.GetRotation(), m_TransformInfo.localAABB );
+}
+
+template<class TransferFunction> inline
+void TrailRenderer::Transfer (TransferFunction& transfer) {
+ Super::Transfer (transfer);
+ transfer.Transfer (m_Time, "m_Time", kSimpleEditorMask);
+ transfer.Transfer (m_LineParameters.startWidth, "m_StartWidth", kSimpleEditorMask);
+ transfer.Transfer (m_LineParameters.endWidth, "m_EndWidth", kSimpleEditorMask);
+ TRANSFER_SIMPLE (m_Colors);
+
+ TRANSFER(m_MinVertexDistance);
+
+ transfer.Transfer (m_Autodestruct, "m_Autodestruct");
+ if (transfer.IsReading () && !m_Autodestruct)
+ m_WasRendered = false;
+}
diff --git a/Runtime/Filters/Misc/TrailRenderer.h b/Runtime/Filters/Misc/TrailRenderer.h
new file mode 100644
index 0000000..4535d64
--- /dev/null
+++ b/Runtime/Filters/Misc/TrailRenderer.h
@@ -0,0 +1,62 @@
+#ifndef TRAILRENDERER_H
+#define TRAILRENDERER_H
+
+#include <list>
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Math/Gradient.h"
+#include "Runtime/Math/Vector3.h"
+#include "LineBuilder.h"
+#include "Runtime/Geometry/AABB.h"
+
+
+
+// Renders a trail after an object.
+// This is good for smoke trails after missiles or a visual FX after strong lights.
+// @todo make it work so we track movement over a Cameras screen space.
+class TrailRenderer : public Renderer {
+public:
+ REGISTER_DERIVED_CLASS (TrailRenderer, Renderer)
+ DECLARE_OBJECT_SERIALIZE (TrailRenderer)
+
+ TrailRenderer (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void Reset ();
+
+ virtual void Render (int materialIndex, const ChannelAssigns& channels);
+
+ // Hook up to TransformChanged
+ static void InitializeClass ();
+
+ // TransformChanged message handler
+ void TransformChanged (int changeMask);
+
+ virtual void UpdateTransformInfo();
+
+ GET_SET_DIRTY(float, Time, m_Time)
+ GET_SET_DIRTY(float, MinVertexDistance, m_MinVertexDistance)
+ GET_SET_DIRTY(float, StartWidth, m_LineParameters.startWidth)
+ GET_SET_DIRTY(float, EndWidth, m_LineParameters.endWidth)
+ GET_SET_DIRTY(bool, Autodestruct, m_Autodestruct)
+
+protected:
+ // from Renderer
+ virtual void UpdateRenderer();
+
+private:
+ float GetHalfMaxLineWidth () const;
+
+ bool m_TransformChanged; // Has the transform changed since last render?
+ bool m_WasRendered; // Trail was rendered so enable autodestruct
+ std::list<Vector3f> m_Positions; // The positions for each of the centers
+ std::list<float> m_TimeStamps; // The timestamp for each position
+ int m_CurrentLength; // The current length in vertices
+ MinMaxAABB m_AABB; // The size in world coords
+
+ GradientDeprecated<k3DLineGradientSize> m_Colors;
+ LineParameters m_LineParameters;
+ float m_Time; ///< How long the tail should be (seconds). { 0, infinity}
+ float m_MinVertexDistance; ///< The minimum distance to spawn a new point on the trail range { 0, infinity}
+ bool m_Autodestruct; ///< Destroy GameObject when there is no trail?
+};
+
+#endif
diff --git a/Runtime/Filters/Particles/EllipsoidParticleEmitter.cpp b/Runtime/Filters/Particles/EllipsoidParticleEmitter.cpp
new file mode 100644
index 0000000..0401fa2
--- /dev/null
+++ b/Runtime/Filters/Particles/EllipsoidParticleEmitter.cpp
@@ -0,0 +1,110 @@
+#include "UnityPrefix.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+#include "EllipsoidParticleEmitter.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+using namespace std;
+
+Rand gEllipsoidEmitterRand (3);
+
+EllipsoidParticleEmitter::EllipsoidParticleEmitter (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_Ellipsoid = Vector3f (1, 1, 1);
+ m_MinEmitterRange = 0.0F;
+}
+
+EllipsoidParticleEmitter::~EllipsoidParticleEmitter ()
+{
+}
+
+static void ResetEllipsoidEmitterRand ()
+{
+ gEllipsoidEmitterRand.SetSeed (3);
+}
+
+void EllipsoidParticleEmitter::InitializeClass ()
+{
+ GlobalCallbacks::Get().resetRandomAfterLevelLoad.Register(ResetEllipsoidEmitterRand);
+}
+
+void EllipsoidParticleEmitter::CleanupClass ()
+{
+ GlobalCallbacks::Get().resetRandomAfterLevelLoad.Unregister(ResetEllipsoidEmitterRand);
+}
+
+
+void EllipsoidParticleEmitter::SetupParticles (
+ ParticleArray& particles,
+ const Vector3f& velocityOffset,
+ const Matrix3x3f& rotation,
+ int firstIndex)
+{
+ float deltaTime = GetDeltaTime ();
+ MinMaxAABB& aabb = m_PrivateInfo.aabb;
+ for (int i = firstIndex;i<particles.size ();i++)
+ {
+ SetupParticle (particles[i], velocityOffset, rotation, deltaTime);
+ aabb.Encapsulate (particles[i].position);
+ }
+}
+
+void EllipsoidParticleEmitter::SetupParticle (
+ Particle& p,
+ const Vector3f& velocityOffset,
+ const Matrix3x3f& rotation,
+ float deltaTime)
+{
+ InitParticleEnergy(gEllipsoidEmitterRand, p, deltaTime);
+
+ // Set particle starting position
+ p.position = m_PreviousEmitterPos;
+ p.position += velocityOffset * RangedRandom (gEllipsoidEmitterRand, 0.0F, deltaTime);
+ p.position += (m_EmitterPos - m_PreviousEmitterPos) * Random01 (gEllipsoidEmitterRand);
+ Vector3f insideEllipsoidPosition = RandomPointBetweenEllipsoid (gEllipsoidEmitterRand, m_Ellipsoid, m_MinEmitterRange);
+ p.position += rotation.MultiplyVector3 (insideEllipsoidPosition);
+
+ // Set velocity
+ p.velocity = velocityOffset;
+ p.velocity += rotation.MultiplyVector3 (RandomPointInsideEllipsoid (gEllipsoidEmitterRand, m_RndVelocity));
+
+ p.rotation = m_RndInitialRotations ? RangedRandom (gEllipsoidEmitterRand, 0.0f, 2*kPI):0.0F;
+ float angularVelocity = m_AngularVelocity;
+#if SUPPORT_REPRODUCE_LOG
+ if (m_RndAngularVelocity > Vector3f::epsilon)
+#endif
+ angularVelocity += RangedRandom (gEllipsoidEmitterRand, -m_RndAngularVelocity, m_RndAngularVelocity);
+ p.angularVelocity = Deg2Rad(angularVelocity);
+
+// Vector3f ellipsoidRelativeDirection = NormalizeSafe (insideEllipsoidPosition);
+// p.velocity += rotation.MultiplyVector3 (Cross (ellipsoidRelativeDirection, info.tangentVelocity));
+
+ Vector3f uTangent (insideEllipsoidPosition.z, 0.0F, -insideEllipsoidPosition.x);
+ Vector3f vTangent (insideEllipsoidPosition.x, 0.0F, -insideEllipsoidPosition.y);
+ uTangent = NormalizeSafe (uTangent);
+ vTangent = NormalizeSafe (vTangent);
+ Vector3f normal = NormalizeSafe (insideEllipsoidPosition);
+ p.velocity += rotation.MultiplyVector3 (uTangent * m_TangentVelocity.x);
+ p.velocity += rotation.MultiplyVector3 (vTangent * m_TangentVelocity.y);
+ p.velocity += rotation.MultiplyVector3 (normal * m_TangentVelocity.z);
+
+ p.color = ColorRGBA32 (255, 255, 255, 255);
+
+ // Set size
+ p.size = RangedRandom (gEllipsoidEmitterRand, m_MinSize, m_MaxSize);
+}
+
+IMPLEMENT_CLASS_HAS_INIT (EllipsoidParticleEmitter)
+IMPLEMENT_OBJECT_SERIALIZE (EllipsoidParticleEmitter)
+
+template<class TransferFunction> inline
+void EllipsoidParticleEmitter::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.Align();
+ TRANSFER_SIMPLE (m_Ellipsoid);
+ TRANSFER (m_MinEmitterRange);
+}
diff --git a/Runtime/Filters/Particles/EllipsoidParticleEmitter.h b/Runtime/Filters/Particles/EllipsoidParticleEmitter.h
new file mode 100644
index 0000000..8a93dda
--- /dev/null
+++ b/Runtime/Filters/Particles/EllipsoidParticleEmitter.h
@@ -0,0 +1,35 @@
+#ifndef ELLIPSOIDPARTICLEEMITTER_H
+#define ELLIPSOIDPARTICLEEMITTER_H
+
+#include "ParticleEmitter.h"
+#include "Runtime/Math/Vector3.h"
+#include "ParticleStruct.h"
+
+class Matrix4x4f;
+
+
+
+class EllipsoidParticleEmitter : public ParticleEmitter
+{
+public:
+ REGISTER_DERIVED_CLASS (EllipsoidParticleEmitter, ParticleEmitter)
+ DECLARE_OBJECT_SERIALIZE (EllipsoidParticleEmitter)
+ EllipsoidParticleEmitter (MemLabelId label, ObjectCreationMode mode);
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+private:
+
+ void SetupParticle (Particle& p, const Vector3f& velocityOffset, const Matrix3x3f& rotation, float deltaTime);
+ virtual void SetupParticles (ParticleArray& particles, const Vector3f& velocityOffset,
+ const Matrix3x3f& rotation, int firstIndex);
+
+public:
+ Vector3f m_Ellipsoid; ///< Size of emission area
+ float m_MinEmitterRange;///< [0...1] relative range to maxEmitterSize where particles will not be spawned
+ // 0 means that a full ellipsoid will be filled with particles
+ // 1 means only the outline will be filled with particles
+};
+
+#endif
diff --git a/Runtime/Filters/Particles/MeshParticleEmitter.cpp b/Runtime/Filters/Particles/MeshParticleEmitter.cpp
new file mode 100644
index 0000000..16ea50c
--- /dev/null
+++ b/Runtime/Filters/Particles/MeshParticleEmitter.cpp
@@ -0,0 +1,353 @@
+#include "UnityPrefix.h"
+#include "MeshParticleEmitter.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Filters/Mesh/MeshUtility.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+
+using namespace std;
+
+static Rand gMeshEmitterRand (4);
+
+MeshParticleEmitter::MeshParticleEmitter (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_VertexIndex = false;
+}
+
+MeshParticleEmitter::~MeshParticleEmitter ()
+{
+}
+
+void MeshParticleEmitter::Reset ()
+{
+ Super::Reset();
+ m_InterpolateTriangles = false;
+ m_Systematic = false;
+ m_MinNormalVelocity = 0.0F;
+ m_MaxNormalVelocity = 0.0F;
+}
+
+void MeshParticleEmitter::SetupParticles (
+ ParticleArray& particles,
+ const Vector3f& velocityOffset,
+ const Matrix3x3f& rotation,
+ int firstIndex)
+{
+ Mesh* mesh = m_Mesh;
+
+ MinMaxAABB& aabb = m_PrivateInfo.aabb;
+
+ Matrix4x4f scale;
+ GetComponent (Transform).CalculateTransformMatrixScaleDelta (scale);
+
+ Matrix3x3f rotationAndScale = Matrix3x3f(scale);
+ rotationAndScale = rotation * rotationAndScale;
+ rotationAndScale.InvertTranspose ();
+
+ float deltaTime = GetDeltaTime ();
+
+ // If there's an invalid mesh, then just emit from the center
+ if (mesh == NULL || mesh->GetSubMeshCount () == 0 || mesh->GetSubMeshFast (0).indexCount == 0 || !mesh->HasVertexData())
+ {
+ Vector3f v;
+ StrideIterator<Vector3f> vertices(&v, 0);
+ StrideIterator<Vector3f> normals;
+
+ for (int i = firstIndex;i<particles.size ();i++)
+ {
+ SetupParticle (0, particles[i], velocityOffset, scale, rotation, rotationAndScale, deltaTime, vertices, normals);
+ aabb.Encapsulate (particles[i].position);
+ }
+ return;
+ }
+
+ SubMesh& submesh = mesh->GetSubMeshFast (0);
+ int vertexCount = mesh->GetVertexCount();
+
+ StrideIterator<Vector3f> vertices = mesh->GetVertexBegin();
+ StrideIterator<Vector3f> normals = mesh->GetNormalBegin();
+ const UInt16* buffer = mesh->GetSubMeshBuffer16(0);
+
+ if (!m_Systematic)
+ {
+ if (m_InterpolateTriangles)
+ {
+ if (submesh.topology == kPrimitiveTriangleStripDeprecated)
+ {
+ for (int i = firstIndex;i<particles.size ();i++)
+ {
+ SetupParticleStrip (particles[i], velocityOffset, scale, rotation, rotationAndScale, deltaTime, vertices, normals, buffer, submesh.indexCount);
+ aabb.Encapsulate (particles[i].position);
+ }
+ }
+ else if (submesh.topology == kPrimitiveTriangles)
+ {
+ for (int i = firstIndex;i<particles.size ();i++)
+ {
+ SetupParticleTri (particles[i], velocityOffset, scale, rotation, rotationAndScale, deltaTime, vertices, normals, buffer, submesh.indexCount/3);
+ aabb.Encapsulate (particles[i].position);
+ }
+ }
+ }
+ else {
+ if (vertexCount != 0)
+ {
+ for (int i = firstIndex;i<particles.size ();i++) {
+ SetupParticle (RangedRandom (gMeshEmitterRand, 0, vertexCount), particles[i], velocityOffset, scale, rotation, rotationAndScale, deltaTime, vertices, normals);
+ aabb.Encapsulate (particles[i].position);
+ }
+ }
+ }
+ }
+ else {
+ if (vertexCount != 0)
+ {
+ // Just in case the mesh changed while the particle emitter was running
+ if (m_VertexIndex >= vertexCount)
+ m_VertexIndex = 0;
+
+ for (int i = firstIndex;i<particles.size ();i++) {
+ SetupParticle (m_VertexIndex, particles[i], velocityOffset, scale, rotation, rotationAndScale, deltaTime, vertices, normals);
+ aabb.Encapsulate (particles[i].position);
+
+ m_VertexIndex++;
+ if (m_VertexIndex >= vertexCount)
+ m_VertexIndex = 0;
+ }
+ }
+ }
+}
+
+void MeshParticleEmitter::SetupParticle (
+ int vertexIndex,
+ Particle& p,
+ const Vector3f& velocityOffset,
+ const Matrix4x4f& scale,
+ const Matrix3x3f& rotation,
+ const Matrix3x3f& normalTransform,
+ float deltaTime,
+ StrideIterator<Vector3f> vertices,
+ StrideIterator<Vector3f> normals)
+{
+ InitParticleEnergy(gMeshEmitterRand, p, deltaTime);
+
+ // position/normal of particle is vertex/vertex normal from mesh
+ Vector3f positionOnMesh = vertices[vertexIndex];
+ positionOnMesh = scale.MultiplyPoint3 (positionOnMesh);
+
+ Vector3f normal;
+ if (!normals.IsNull ())
+ {
+ normal = normalTransform.MultiplyVector3 (normals[vertexIndex]);
+ normal = NormalizeFast (normal);
+ }
+ else
+ normal = Vector3f(0,0,0);
+
+
+ // Set particle starting position
+ p.position = m_PreviousEmitterPos;
+ p.position += velocityOffset * RangedRandom (gMeshEmitterRand, 0.0F, deltaTime);
+ p.position += (m_EmitterPos - m_PreviousEmitterPos) * Random01 (gMeshEmitterRand);
+ p.position += rotation.MultiplyVector3 (positionOnMesh);
+
+ // Set velocity
+ p.velocity = velocityOffset + normal * RangedRandom (gMeshEmitterRand, m_MinNormalVelocity, m_MaxNormalVelocity);
+ p.velocity += rotation.MultiplyVector3 (RandomPointInsideEllipsoid (gMeshEmitterRand, m_RndVelocity));
+
+ p.rotation = m_RndInitialRotations ? RangedRandom (gMeshEmitterRand, 0.0f, 2*kPI):0.0F;
+ float angularVelocity = m_AngularVelocity;
+#if SUPPORT_REPRODUCE_LOG
+ if (m_RndAngularVelocity > Vector3f::epsilon)
+#endif
+ angularVelocity += RangedRandom (gMeshEmitterRand, -m_RndAngularVelocity, m_RndAngularVelocity);
+ p.angularVelocity = Deg2Rad(angularVelocity);
+
+ p.color = ColorRGBA32 (255, 255, 255, 255);
+
+ // Set size
+ p.size = RangedRandom (gMeshEmitterRand, m_MinSize, m_MaxSize);
+}
+
+void MeshParticleEmitter::SetupParticleTri (
+ Particle& p,
+ const Vector3f& velocityOffset,
+ const Matrix4x4f& scale,
+ const Matrix3x3f& rotation,
+ const Matrix3x3f& normalTransform,
+ float deltaTime,
+ StrideIterator<Vector3f> vertices,
+ StrideIterator<Vector3f> normals,
+ const UInt16* indices,
+ int triCount)
+{
+ InitParticleEnergy(gMeshEmitterRand, p, deltaTime);
+
+ int triIndex = RangedRandom (gMeshEmitterRand, 0, (int)triCount);
+ const UInt16* face = indices + triIndex * 3;
+ Vector3f barycenter = RandomBarycentricCoord (gMeshEmitterRand);
+
+ // Interpolate vertex with barycentric coordinate
+ Vector3f positionOnMesh = barycenter.x * vertices[face[0]] + barycenter.y * vertices[face[1]] + barycenter.z * vertices[face[2]];
+ positionOnMesh = scale.MultiplyPoint3 (positionOnMesh);
+
+ Vector3f normal;
+ if (!normals.IsNull ())
+ {
+ // Interpolate normal with barycentric coordinate
+ Vector3f const& normal1 = normals[face[0]];
+ Vector3f const& normal2 = normals[face[1]];
+ Vector3f const& normal3 = normals[face[2]];
+ normal = barycenter.x * normal1 + barycenter.y * normal2 + barycenter.z * normal3;
+ normal = normalTransform.MultiplyVector3 (normal);
+ normal = NormalizeFast (normal);
+ }
+ else
+ normal = Vector3f(0,0,0);
+
+ // Set particle starting position
+ p.position = m_PreviousEmitterPos;
+ p.position += velocityOffset * RangedRandom (gMeshEmitterRand, 0.0F, deltaTime);
+ p.position += (m_EmitterPos - m_PreviousEmitterPos) * Random01 (gMeshEmitterRand);
+ p.position += rotation.MultiplyVector3 (positionOnMesh);
+
+ // Set velocity
+ p.velocity = velocityOffset + normal * RangedRandom (gMeshEmitterRand, m_MinNormalVelocity, m_MaxNormalVelocity);
+ p.velocity += rotation.MultiplyVector3 (RandomPointInsideEllipsoid (gMeshEmitterRand, m_RndVelocity));
+
+ p.rotation = m_RndInitialRotations ? RangedRandom (gMeshEmitterRand, 0.0f, 2*kPI):0.0F;
+ float angularVelocity = m_AngularVelocity;
+#if SUPPORT_REPRODUCE_LOG
+ if (m_RndAngularVelocity > Vector3f::epsilon)
+#endif
+ angularVelocity += RangedRandom (gMeshEmitterRand, -m_RndAngularVelocity, m_RndAngularVelocity);
+ p.angularVelocity = Deg2Rad(angularVelocity);
+
+ p.color = ColorRGBA32 (255, 255, 255, 255);
+
+ // Set size
+ p.size = RangedRandom (gMeshEmitterRand, m_MinSize, m_MaxSize);
+}
+
+void MeshParticleEmitter::SetupParticleStrip (
+ Particle& p,
+ const Vector3f& velocityOffset,
+ const Matrix4x4f& scale,
+ const Matrix3x3f& rotation,
+ const Matrix3x3f& normalTransform,
+ float deltaTime,
+ StrideIterator<Vector3f> vertices,
+ StrideIterator<Vector3f> normals,
+ const UInt16* strip,
+ int stripSize)
+{
+ InitParticleEnergy(gMeshEmitterRand, p, deltaTime);
+
+ // Extract indices from tristrip
+ int stripIndex = RangedRandom (gMeshEmitterRand, 2, stripSize);
+ UInt16 a = strip[stripIndex-2];
+ UInt16 b = strip[stripIndex-1];
+ UInt16 c = strip[stripIndex];
+ // Ignore degenerate triangles
+ while (a == b || a == c || b == c)
+ {
+ stripIndex = RangedRandom (gMeshEmitterRand, 2, stripSize);
+ a = strip[stripIndex-2];
+ b = strip[stripIndex-1];
+ c = strip[stripIndex];
+ while (a == b || a == c || b == c)
+ {
+ stripIndex++;
+ if (stripIndex >= stripSize)
+ break;
+ a = strip[stripIndex-2];
+ b = strip[stripIndex-1];
+ c = strip[stripIndex];
+ }
+ }
+
+ Vector3f barycenter = RandomBarycentricCoord (gMeshEmitterRand);
+
+ // Interpolate vertex with barycentric coordinate
+ Vector3f positionOnMesh = barycenter.x * vertices[a] + barycenter.y * vertices[b] + barycenter.z * vertices[c];
+ positionOnMesh = scale.MultiplyPoint3 (positionOnMesh);
+
+
+ Vector3f normal;
+ if (!normals.IsNull ())
+ {
+ // Interpolate normal with barycentric coordinate
+ Vector3f normal1 = normals[a];
+ Vector3f normal2 = normals[b];
+ Vector3f normal3 = normals[c];
+ normal = barycenter.x * normal1 + barycenter.y * normal2 + barycenter.z * normal3;
+ normal = normalTransform.MultiplyVector3 (normal);
+ normal = NormalizeFast (normal);
+ }
+ else
+ {
+ normal = Vector3f(0,0,0);
+ }
+
+ // Set particle starting position
+ p.position = m_PreviousEmitterPos;
+ p.position += velocityOffset * RangedRandom (gMeshEmitterRand, 0.0F, deltaTime);
+ p.position += (m_EmitterPos - m_PreviousEmitterPos) * Random01 (gMeshEmitterRand);
+ p.position += rotation.MultiplyVector3 (positionOnMesh);
+
+ // Set velocity
+ p.velocity = velocityOffset + normal * RangedRandom (gMeshEmitterRand, m_MinNormalVelocity, m_MaxNormalVelocity);
+ p.velocity += rotation.MultiplyVector3 (RandomPointInsideEllipsoid (gMeshEmitterRand, m_RndVelocity));
+
+ p.rotation = m_RndInitialRotations ? RangedRandom (gMeshEmitterRand, 0.0f, 2*kPI):0.0F;
+ float angularVelocity = m_AngularVelocity;
+#if SUPPORT_REPRODUCE_LOG
+ if (m_RndAngularVelocity > Vector3f::epsilon)
+#endif
+ angularVelocity += RangedRandom (gMeshEmitterRand, -m_RndAngularVelocity, m_RndAngularVelocity);
+ p.angularVelocity = Deg2Rad(angularVelocity);
+
+ p.color = ColorRGBA32 (255, 255, 255, 255);
+
+ // Set size
+ p.size = RangedRandom (gMeshEmitterRand, m_MinSize, m_MaxSize);
+}
+
+static void ResetRandSeedForMeshParticleEmitter ()
+{
+ gMeshEmitterRand.SetSeed (4);
+}
+
+void MeshParticleEmitter::InitializeClass ()
+{
+ GlobalCallbacks::Get().resetRandomAfterLevelLoad.Register(ResetRandSeedForMeshParticleEmitter);
+}
+
+void MeshParticleEmitter::CleanupClass ()
+{
+ GlobalCallbacks::Get().resetRandomAfterLevelLoad.Unregister(ResetRandSeedForMeshParticleEmitter);
+}
+
+IMPLEMENT_CLASS_HAS_INIT (MeshParticleEmitter)
+IMPLEMENT_OBJECT_SERIALIZE (MeshParticleEmitter)
+
+template<class TransferFunction> inline
+void MeshParticleEmitter::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER (m_InterpolateTriangles);
+ TRANSFER (m_Systematic);
+ transfer.Align();
+ TRANSFER (m_MinNormalVelocity);
+ TRANSFER (m_MaxNormalVelocity);
+ TRANSFER (m_Mesh);
+}
+
+void MeshParticleEmitter::SetMesh (PPtr<Mesh> mesh)
+{
+ m_Mesh = mesh;
+ SetDirty();
+}
diff --git a/Runtime/Filters/Particles/MeshParticleEmitter.h b/Runtime/Filters/Particles/MeshParticleEmitter.h
new file mode 100644
index 0000000..6ac74c1
--- /dev/null
+++ b/Runtime/Filters/Particles/MeshParticleEmitter.h
@@ -0,0 +1,42 @@
+#ifndef MESHPARTICLEEMITTER_H
+#define MESHPARTICLEEMITTER_H
+
+#include "ParticleEmitter.h"
+#include "ParticleStruct.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+
+
+
+class MeshParticleEmitter : public ParticleEmitter
+{
+public:
+ REGISTER_DERIVED_CLASS (MeshParticleEmitter, ParticleEmitter)
+ DECLARE_OBJECT_SERIALIZE (MeshParticleEmitter)
+ MeshParticleEmitter (MemLabelId label, ObjectCreationMode mode);
+
+ void SetMesh (PPtr<Mesh> mesh);
+ PPtr<Mesh> GetMesh () { return m_Mesh; }
+
+ virtual void Reset ();
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+private:
+ void SetupParticle (int vertexIndex, Particle& p, const Vector3f& velocityOffset, const Matrix4x4f& scale, const Matrix3x3f& rotation, const Matrix3x3f& normalTransform, float deltaTime, StrideIterator<Vector3f> vertices, StrideIterator<Vector3f> normals);
+ void SetupParticleTri (Particle& p, const Vector3f& velocityOffset, const Matrix4x4f& scale, const Matrix3x3f& rotation, const Matrix3x3f& normalTransform, float deltaTime, StrideIterator<Vector3f> vertices, StrideIterator<Vector3f> normals, const UInt16* faces, int triCount);
+ void SetupParticleStrip (Particle& p, const Vector3f& velocityOffset, const Matrix4x4f& scale, const Matrix3x3f& rotation, const Matrix3x3f& normalTransform, float deltaTime, StrideIterator<Vector3f> vertices, StrideIterator<Vector3f> normals, const UInt16* strip, int stripSize);
+
+ virtual void SetupParticles (ParticleArray& particles, const Vector3f& velocityOffset,
+ const Matrix3x3f& rotation, int firstIndex);
+
+private:
+ bool m_InterpolateTriangles;
+ bool m_Systematic;
+ float m_MinNormalVelocity;
+ float m_MaxNormalVelocity;
+ int m_VertexIndex;
+ PPtr<Mesh> m_Mesh;
+};
+
+#endif
diff --git a/Runtime/Filters/Particles/ParticleAnimator.cpp b/Runtime/Filters/Particles/ParticleAnimator.cpp
new file mode 100644
index 0000000..95ceb3a
--- /dev/null
+++ b/Runtime/Filters/Particles/ParticleAnimator.cpp
@@ -0,0 +1,216 @@
+#include "UnityPrefix.h"
+#include "ParticleAnimator.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/Input/TimeManager.h"
+#include "ParticleStruct.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/GameCode/DestroyDelayed.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+
+static Rand gParticleAnimRand (2);
+
+void ParticleAnimator::GetColorAnimation(ColorRGBAf *col) const
+{
+ for(int i=0;i<kColorKeys;i++)
+ col[i] = m_ColorAnimation[i];
+}
+
+void ParticleAnimator::SetColorAnimation(ColorRGBAf *col)
+{
+ for(int i=0;i<kColorKeys;i++)
+ m_ColorAnimation[i] = col[i];
+}
+
+void ParticleAnimator::UpdateAnimator( ParticleArray& particles, PrivateParticleInfo& privateInfo, float deltaTime )
+{
+ // Quit updating if no new emissions and no old particles
+ if( particles.empty() )
+ {
+ m_EnergylossFraction = 0.0F;
+
+ if( WillAutoDestructIfNoParticles( privateInfo ) )
+ DestroyObjectDelayed (GetGameObjectPtr());
+ return;
+ }
+ if( m_Autodestruct == 1 )
+ m_Autodestruct = 2;
+
+ if( !m_StopSimulation )
+ {
+ privateInfo.aabb.Init ();
+ UpdateParticles( particles, privateInfo, deltaTime );
+
+ //Particle size is multipled to calculate the maximum size
+ //Handle cases where grow size is negative also
+ float endSize = privateInfo.maxEmitterParticleSize * pow (1.0F + m_SizeGrow, privateInfo.maxEnergy);
+ privateInfo.maxParticleSize = m_SizeGrow < 0.0f ? privateInfo.maxEmitterParticleSize : endSize;
+ }
+}
+
+bool ParticleAnimator::WillAutoDestructIfNoParticles( const PrivateParticleInfo& privateInfo ) const
+{
+ // Autodestruct destroys the GO if enabled AND either of those:
+ // * Particles have been emitted, but are all dead now.
+ // * Emit was once On, but now is Off.
+ if( m_Autodestruct != 0 && IsWorldPlaying() )
+ {
+ if( m_Autodestruct == 2 || (privateInfo.hadEverEmitOn && !privateInfo.isEmitOn) )
+ return true;
+ }
+ return false;
+}
+
+
+void ParticleAnimator::UpdateParticles (ParticleArray& particles, PrivateParticleInfo& privateInfo, float deltaTime) const
+{
+ Vector3f dtForce = m_Force * deltaTime;
+ Vector3f rotationAxis;
+ if (privateInfo.useWorldSpace)
+ rotationAxis = (m_WorldRotationAxis + GetComponent(Transform).TransformDirection(m_LocalRotationAxis)) * deltaTime;
+ else
+ rotationAxis = (GetComponent(Transform).InverseTransformDirection(m_WorldRotationAxis) + m_LocalRotationAxis) * deltaTime;
+
+ float damping = pow (m_Damping, deltaTime);
+ Vector3f rndForce = m_RndForce * deltaTime;
+
+
+ const float kColorScale = kColorKeys - 1.0f;
+
+ // The color keys are inverted
+ ColorRGBA32 colorAnimation[kColorKeys] = { m_ColorAnimation[4], m_ColorAnimation[3], m_ColorAnimation[2], m_ColorAnimation[1], m_ColorAnimation[0] };
+ float sizeScale = pow (1.0F + m_SizeGrow, deltaTime);
+ float maxColorAnimationT = (float)kColorKeys - 1.0F - Vector3f::epsilon;
+
+ bool doesAnimateColor = m_DoesAnimateColor;
+ int particleSize = particles.size ();
+ int i = 0;
+ while (i < particleSize)
+ {
+ Particle& p = particles[i];
+
+ // Update Alpha
+ p.energy -= deltaTime;
+
+ if (p.energy <= 0.0f) {
+ // Kill particle (replace particle i with last, and continue updating the moved particle)
+ KillParticle (particles, i);
+ --particleSize;
+ continue;
+ }
+
+ if( doesAnimateColor )
+ {
+ // [0..kColorKeys-1] for [0..maxEnergy]
+ float t = p.energy * kColorScale / p.startEnergy;
+ t = FloatMin (t, maxColorAnimationT);
+ int baseColor = FloorfToIntPos (t);
+ DebugAssertIf( baseColor < 0 || baseColor >= kColorKeys - 1 );
+ float frac = t - (float)baseColor;
+ int intFrac = RoundfToIntPos (frac * 255.0F);
+ p.color = Lerp (colorAnimation[baseColor], colorAnimation[baseColor + 1], intFrac);
+ }
+
+ // Update Velocity
+ p.velocity *= damping;
+
+ p.velocity += dtForce;
+
+ p.velocity += RandomPointInsideCube (gParticleAnimRand, rndForce);
+
+ p.velocity += Cross (rotationAxis, p.velocity);
+
+ // Update position
+ p.position += p.velocity * deltaTime;
+
+ // Update Size
+ p.size *= sizeScale;
+
+ // Update Bounding Box
+ privateInfo.aabb.Encapsulate (p.position);
+
+ p.rotation += p.angularVelocity * deltaTime;
+
+ Prefetch(&particles[i] + 8);
+ Prefetch(&particles[particleSize-1]);
+
+ i++;
+ }
+}
+
+ParticleAnimator::ParticleAnimator(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_ColorAnimation[0] = ColorRGBA32 (255, 255, 255, 10);
+ m_ColorAnimation[1] = ColorRGBA32 (255, 255, 255, 180);
+ m_ColorAnimation[2] = ColorRGBA32 (255, 255, 255, 255);
+ m_ColorAnimation[3] = ColorRGBA32 (255, 255, 255, 180);
+ m_ColorAnimation[4] = ColorRGBA32 (255, 255, 255, 10);
+
+ m_WorldRotationAxis = Vector3f::zero;
+ m_LocalRotationAxis = Vector3f::zero;
+ m_DoesAnimateColor = true;
+ m_RndForce = Vector3f::zero;
+ m_Force = Vector3f::zero;
+ m_SizeGrow = 0.0f;
+ m_Damping = 1.0f;
+ m_Autodestruct = 0;
+ m_StopSimulation = false;
+ m_EnergylossFraction = 0.0F;
+}
+
+ParticleAnimator::~ParticleAnimator ()
+{
+}
+
+IMPLEMENT_CLASS_HAS_INIT (ParticleAnimator)
+IMPLEMENT_OBJECT_SERIALIZE (ParticleAnimator)
+
+static void ResetRandSeed ()
+{
+ gParticleAnimRand.SetSeed (2);
+}
+
+void ParticleAnimator::InitializeClass ()
+{
+ GlobalCallbacks::Get().resetRandomAfterLevelLoad.Register(ResetRandSeed);
+}
+
+void ParticleAnimator::CleanupClass ()
+{
+ GlobalCallbacks::Get().resetRandomAfterLevelLoad.Unregister(ResetRandSeed);
+}
+
+template<class TransferFunction> inline
+void ParticleAnimator::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ transfer.Transfer (m_DoesAnimateColor, "Does Animate Color?");
+ transfer.Align();
+ transfer.Transfer( m_ColorAnimation[0], "colorAnimation[0]", kSimpleEditorMask );
+ transfer.Transfer( m_ColorAnimation[1], "colorAnimation[1]", kSimpleEditorMask );
+ transfer.Transfer( m_ColorAnimation[2], "colorAnimation[2]", kSimpleEditorMask );
+ transfer.Transfer( m_ColorAnimation[3], "colorAnimation[3]", kSimpleEditorMask );
+ transfer.Transfer( m_ColorAnimation[4], "colorAnimation[4]", kSimpleEditorMask );
+ DebugAssertIf (kColorKeys != 5);
+
+ transfer.Transfer( m_WorldRotationAxis, "worldRotationAxis" );
+ transfer.Transfer( m_LocalRotationAxis, "localRotationAxis" );
+ transfer.Transfer( m_SizeGrow, "sizeGrow", kSimpleEditorMask );
+ transfer.Transfer( m_RndForce, "rndForce", kSimpleEditorMask );
+ transfer.Transfer( m_Force, "force", kSimpleEditorMask );
+ transfer.Transfer( m_Damping, "damping", kSimpleEditorMask ); m_Damping = clamp<float> (m_Damping, 0.0f, 1.0f);
+ transfer.Transfer( m_StopSimulation, "stopSimulation", kHideInEditorMask );
+
+ bool autodestruct = m_Autodestruct;
+ transfer.Transfer (autodestruct, "autodestruct");
+ if (transfer.IsReading ())
+ {
+ if (autodestruct == 0)
+ m_Autodestruct = 0;
+ else if (m_Autodestruct == 0)
+ m_Autodestruct = 1;
+ }
+}
diff --git a/Runtime/Filters/Particles/ParticleAnimator.h b/Runtime/Filters/Particles/ParticleAnimator.h
new file mode 100644
index 0000000..dde7b9d
--- /dev/null
+++ b/Runtime/Filters/Particles/ParticleAnimator.h
@@ -0,0 +1,57 @@
+#ifndef PARTICLEANIMATOR_H
+#define PARTICLEANIMATOR_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Math/Color.h"
+#include "ParticleStruct.h"
+
+
+
+class ParticleAnimator : public Unity::Component
+{
+public:
+ REGISTER_DERIVED_CLASS (ParticleAnimator, Unity::Component)
+ DECLARE_OBJECT_SERIALIZE (ParticleAnimator)
+
+ ParticleAnimator(MemLabelId label, ObjectCreationMode mode);
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ enum { kColorKeys = 5 };
+
+ GET_SET_DIRTY (Vector3f, WorldRotationAxis, m_WorldRotationAxis)
+ GET_SET_DIRTY (Vector3f, LocalRotationAxis, m_LocalRotationAxis)
+ GET_SET_DIRTY (Vector3f, RndForce, m_RndForce)
+ GET_SET_DIRTY (Vector3f, Force, m_Force)
+ GET_SET_DIRTY (float, Damping, m_Damping)
+ GET_SET_DIRTY (float, SizeGrow, m_SizeGrow)
+ GET_SET_DIRTY (bool, Autodestruct, m_Autodestruct)
+ GET_SET_DIRTY (bool, DoesAnimateColor, m_DoesAnimateColor)
+ GET_SET_DIRTY (bool, stopSimulation, m_StopSimulation)
+
+ void GetColorAnimation(ColorRGBAf *col) const;
+ void SetColorAnimation(ColorRGBAf *col);
+
+ void UpdateAnimator( ParticleArray& particles, PrivateParticleInfo& privateInfo, float deltaTime );
+
+ bool WillAutoDestructIfNoParticles( const PrivateParticleInfo& privateInfo ) const;
+
+private:
+ void UpdateParticles (ParticleArray& particles, PrivateParticleInfo& privateInfo, float deltaTime) const;
+
+private:
+ Vector3f m_WorldRotationAxis; // axis around which the particle rotates, worldspace
+ Vector3f m_LocalRotationAxis; // axis around which the particle rotates, localspace
+ Vector3f m_RndForce;// rnd force
+ Vector3f m_Force; // gravity value
+ float m_Damping; // damping value
+ float m_SizeGrow; // Grows the size of the particle by sizeGrow per second. A value of 1.0 doubles the particle size every second.
+ ColorRGBA32 m_ColorAnimation[kColorKeys]; // Animates the color through the color keys
+ int m_Autodestruct;
+ bool m_DoesAnimateColor;
+ bool m_StopSimulation;
+ float m_EnergylossFraction;
+};
+
+#endif
diff --git a/Runtime/Filters/Particles/ParticleEmitter.cpp b/Runtime/Filters/Particles/ParticleEmitter.cpp
new file mode 100644
index 0000000..e6507e7
--- /dev/null
+++ b/Runtime/Filters/Particles/ParticleEmitter.cpp
@@ -0,0 +1,455 @@
+#include "UnityPrefix.h"
+#include "ParticleEmitter.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "ParticleAnimator.h"
+#include "WorldParticleCollider.h"
+#include "ParticleRenderer.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+
+
+using namespace std;
+
+static Rand gEmitterRand (5);
+
+enum { kMaxParticleCount = 65000 / 4 };
+const float kMaxEnergy = 1e20f;
+
+typedef List< ListNode<ParticleEmitter> > ParticleEmitterList;
+static ParticleEmitterList gActiveEmitters;
+
+
+ParticleEmitter::ParticleEmitter (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_EmittersListNode( this )
+{
+ m_EmissionFrac = 0.0F;
+ m_Emit = true;
+ m_Enabled = true;
+ m_OneShot = false;
+ m_PrivateInfo.hadEverEmitOn = false;
+ m_PrivateInfo.isEmitOn = false;
+
+ m_MinSize = 0.1F;
+ m_MaxSize = 0.1F;
+ m_MinEnergy = 3.0F;
+ m_MaxEnergy = 3.0F;
+ m_MinEmission = 50.0F;
+ m_MaxEmission = 50.0F;
+ m_UseWorldSpace = true;
+ m_EmitterVelocityScale = 0.05F;
+ m_WorldVelocity = Vector3f::zero;
+ m_LocalVelocity = Vector3f::zero;
+ m_TangentVelocity = Vector3f::zero;
+ m_RndVelocity = Vector3f::zero;
+ m_RndAngularVelocity = 0.0F;
+ m_AngularVelocity = 0.0F;
+ m_RndInitialRotations = false;
+ m_FirstFrame = true;
+ m_PrivateInfo.aabb.Init ();
+}
+
+ParticleEmitter::~ParticleEmitter ()
+{
+}
+
+void ParticleEmitter::UpdateManagerState( bool updateParticles )
+{
+ if( updateParticles == m_EmittersListNode.IsInList() )
+ return;
+ if( updateParticles )
+ gActiveEmitters.push_back(m_EmittersListNode);
+ else
+ m_EmittersListNode.RemoveFromList();
+}
+
+void ParticleEmitter::UpdateAllParticleSystems()
+{
+ const float deltaTimeEpsilon = 0.0001f;
+ float deltaTime = GetDeltaTime();
+ if(deltaTime < deltaTimeEpsilon)
+ return;
+
+ ParticleEmitterList::iterator next;
+ for( ParticleEmitterList::iterator i = gActiveEmitters.begin(); i != gActiveEmitters.end(); i = next )
+ {
+ next = i;
+ next++;
+ ParticleEmitter& emitter = **i;
+ emitter.UpdateParticleSystem(deltaTime);
+ }
+}
+
+
+
+void ParticleEmitter::ResetEmitterPos ()
+{
+ if (m_UseWorldSpace)
+ m_EmitterPos = GetComponent (Transform).GetPosition ();
+ else
+ m_EmitterPos = Vector3f::zero;
+ m_PreviousEmitterPos = m_EmitterPos;
+}
+
+
+void ParticleEmitter::Deactivate( DeactivateOperation operation )
+{
+ Super::Deactivate(operation);
+
+ m_PrivateInfo.hadEverEmitOn = false;
+ UpdateManagerState( false );
+}
+
+PROFILER_INFORMATION(gParticleEmitterProfile, "Particle.Update", kProfilerParticles)
+
+void ParticleEmitter::UpdateParticleSystem(float deltaTime)
+{
+ PROFILER_AUTO(gParticleEmitterProfile, this)
+
+ if( !IsActive() )
+ {
+ AssertStringObject( "UpdateParticle system should not happen on disabled GO", this );
+ return;
+ }
+
+ //@TODO: REMOVE FROM LIST PROPERLY TO SAVE ITERATING THROUGH ALL PARTICLES
+ if (m_Enabled)
+ {
+ m_PrivateInfo.maxParticleSize = m_MaxSize;
+ m_PrivateInfo.maxEmitterParticleSize = m_MaxSize;
+ m_PrivateInfo.maxEnergy = clamp( m_MaxEnergy, 0.0f, kMaxEnergy );
+ m_PrivateInfo.useWorldSpace = m_UseWorldSpace;
+ if( m_Emit )
+ m_PrivateInfo.hadEverEmitOn = true;
+ m_PrivateInfo.isEmitOn = m_Emit;
+
+ //
+ // Emit particles.
+
+ ParticleAnimator* animator = QueryComponent(ParticleAnimator);
+ if( IsEmitting() )
+ {
+ // We don't want to emit anymore if we have OneShot and auto destruct
+ // in particle animator is true. Otherwise we'd emit when we have no particles, and
+ // there will never be zero particles, hence no auto destruct ever!
+ bool willAutoDestruct = false;
+ if( animator && m_OneShot && m_Particles.empty() )
+ willAutoDestruct = animator->WillAutoDestructIfNoParticles( m_PrivateInfo );
+ if( !willAutoDestruct )
+ TimedEmit( deltaTime );
+ }
+
+ //
+ // Update particle animator
+
+ if( animator )
+ animator->UpdateAnimator( m_Particles, m_PrivateInfo, deltaTime );
+
+ //
+ // Update world particle collider
+
+ WorldParticleCollider* collider = QueryComponent(WorldParticleCollider);
+ if( collider )
+ collider->UpdateParticleCollider( m_Particles, m_PrivateInfo, deltaTime );
+
+ //
+ // Update renderer
+
+ ParticleRenderer* renderer = QueryComponent(ParticleRenderer);
+ if( renderer )
+ renderer->UpdateParticleRenderer();
+ }
+}
+
+void ParticleEmitter::SetEmit (bool emit)
+{
+ if (m_Emit == emit)
+ return;
+ m_Emit = emit;
+ UpdateManagerState( IsActive() );
+ if (emit) {
+ ResetEmitterPos ();
+ }
+}
+
+void ParticleEmitter::SetEnabled (bool enabled)
+{
+ if (m_Enabled != enabled)
+ {
+ m_Enabled = enabled;
+ SetDirty();
+ }
+}
+
+void ParticleEmitter::Emit (unsigned int newParticleCount, float invDeltaTime)
+{
+ if (newParticleCount <= 0)
+ return;
+
+ if (m_FirstFrame)
+ {
+ ResetEmitterPos();
+ m_FirstFrame = false;
+ }
+
+ unsigned int firstNewIndex = m_Particles.size ();
+ newParticleCount = min<unsigned int> (newParticleCount + firstNewIndex, kMaxParticleCount);
+
+ if (newParticleCount != firstNewIndex)
+ {
+ m_Particles.resize (newParticleCount);
+
+ Vector3f velocityOffset;
+ Matrix3x3f localRotation;
+
+ CalcOffsets (&velocityOffset, &localRotation, invDeltaTime);
+ // Setup particles
+ SetupParticles (m_Particles, velocityOffset, localRotation, firstNewIndex);
+ }
+}
+
+
+void ParticleEmitter::Emit (const Vector3f &pos, const Vector3f &dir, float size, float energy, const ColorRGBA32 &color, float rotation, float angularVelocity) {
+
+ if (m_Particles.size() >= kMaxParticleCount)
+ return;
+
+ Particle p;
+ p.position = pos;
+ p.velocity = dir;
+ p.size = size;
+ p.rotation = Deg2Rad(rotation);
+ p.angularVelocity = Deg2Rad(angularVelocity);
+// p.energy = SecondsToEnergy (energy + GetDeltaTime ()) + 1;
+ p.energy = energy;
+ p.startEnergy = energy;
+ p.color = color;
+ m_Particles.push_back (p);
+ m_PrivateInfo.aabb.Encapsulate( pos );
+ UpdateManagerState( IsActive() );
+}
+
+
+void ParticleEmitter::ClearParticles ()
+{
+ m_Particles.clear();
+ UpdateManagerState( IsActive() );
+}
+
+void ParticleEmitter::TimedEmit (float deltaTime)
+{
+ // Reserve enough memory to hold all particles that could ever be emitted with the current settings
+ int maxParticles = 0;
+ if (!m_OneShot)
+ {
+ maxParticles = CeilfToIntPos( min<float>(m_MaxEmission * m_MaxEnergy, kMaxParticleCount) );
+ }
+ else
+ {
+ maxParticles = RoundfToIntPos( min<float>(m_MaxEmission, kMaxParticleCount) );
+ }
+
+ m_Particles.reserve (maxParticles);
+
+ // Calculate how many new particles to emit
+ // never emit more particles than the capacity.
+ // the capacity can sometimes be too small if deltaTime is very large
+ // or minEnergy == maxEnergy or minEmissions == maxEmissions
+ int newParticleCount = 0;
+
+ float emission = min<float> ( RangedRandom (gEmitterRand, m_MinEmission, m_MaxEmission), maxParticles );
+ if (!m_OneShot)
+ {
+ float newParticleCountf = emission * deltaTime + m_EmissionFrac;
+ newParticleCount = FloorfToIntPos (newParticleCountf);
+ m_EmissionFrac = newParticleCountf - (float)newParticleCount;
+ }
+ else if(m_Particles.size () == 0)
+ {
+ newParticleCount = RoundfToIntPos(emission);
+ }
+
+ newParticleCount = min<int> (m_Particles.capacity () - m_Particles.size (), newParticleCount);
+
+ // Calculate velocity and rotation that is used to setup the particles
+ // We do this here so we don't get confused by calls to Emit from outside the filter loop.
+ if (m_UseWorldSpace)
+ {
+ m_PreviousEmitterPos = m_EmitterPos;
+ m_EmitterPos = GetComponent (Transform).GetPosition ();
+ }
+ else
+ {
+ m_PreviousEmitterPos = Vector3f::zero;
+ m_EmitterPos = Vector3f::zero;
+ }
+
+ if (newParticleCount > 0)
+ {
+ Emit (newParticleCount, CalcInvDeltaTime(deltaTime));
+ }
+}
+
+void ParticleEmitter::CalcOffsets (Vector3f *velocityOffset, Matrix3x3f *localRotation, float invDeltaTime) {
+ Transform& t = GetComponent (Transform);
+ if (m_UseWorldSpace)
+ {
+ m_EmitterPos = t.GetPosition ();
+
+ QuaternionToMatrix (t.GetRotation (), *localRotation);
+
+ *velocityOffset = localRotation->MultiplyVector3 (m_LocalVelocity);
+ *velocityOffset += m_WorldVelocity;
+ *velocityOffset += (m_EmitterPos - m_PreviousEmitterPos) * invDeltaTime * m_EmitterVelocityScale;
+ }
+ else {
+ localRotation->SetIdentity ();
+
+ *velocityOffset = m_LocalVelocity;
+ *velocityOffset += t.InverseTransformDirection (m_WorldVelocity);
+ }
+}
+
+template<class TransferFunction>
+void ParticleEmitter::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion(2);
+ transfer.Transfer (m_Enabled, "m_Enabled", kHideInEditorMask);
+ TRANSFER_SIMPLE (m_Emit);
+
+ transfer.Align();
+ transfer.Transfer (m_MinSize, "minSize", kSimpleEditorMask);
+ transfer.Transfer (m_MaxSize, "maxSize", kSimpleEditorMask);
+ // WARNING: energy/emission might be inf
+ // when using fastmath on ps3 or equivalent on other platform - these will be handled incorrectly
+ // in that case we need to patch them on build here
+ transfer.Transfer (m_MinEnergy, "minEnergy", kSimpleEditorMask);
+ transfer.Transfer (m_MaxEnergy, "maxEnergy", kSimpleEditorMask);
+ transfer.Transfer (m_MinEmission, "minEmission", kSimpleEditorMask);
+ transfer.Transfer (m_MaxEmission, "maxEmission", kSimpleEditorMask);
+ transfer.Transfer (m_WorldVelocity, "worldVelocity", kSimpleEditorMask);
+ transfer.Transfer (m_LocalVelocity, "localVelocity", kSimpleEditorMask);
+ transfer.Transfer (m_RndVelocity, "rndVelocity", kSimpleEditorMask);
+ transfer.Transfer (m_EmitterVelocityScale, "emitterVelocityScale");
+ // Emitter velocity scale was not frame rate independent!
+ if (transfer.IsOldVersion(1))
+ m_EmitterVelocityScale /= 40.0F;
+
+ transfer.Transfer (m_TangentVelocity, "tangentVelocity");
+ transfer.Transfer (m_AngularVelocity, "angularVelocity", kSimpleEditorMask);
+ transfer.Transfer (m_RndAngularVelocity, "rndAngularVelocity", kSimpleEditorMask);
+ transfer.Transfer (m_RndInitialRotations, "rndRotation", kSimpleEditorMask);
+ transfer.Transfer (m_UseWorldSpace, "Simulate in Worldspace?");
+ transfer.Transfer (m_OneShot, "m_OneShot");
+}
+
+void ParticleEmitter::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ if (IsActive())
+ ResetEmitterPos();
+ if (m_OneShot)
+ ClearParticles ();
+ UpdateManagerState( IsActive() );
+}
+
+void ParticleEmitter::ReadParticles( SimpleParticle* __restrict particle, int base, int count ) const
+{
+ if (base < 0 || base + count > m_Particles.size())
+ {
+ ErrorString("Reading out of bounds particles");
+ return;
+ }
+
+ count += base;
+ for (int i=0;i<count;++i)
+ {
+ SimpleParticle& output = particle[i];
+ const Particle& input = m_Particles[i + base];
+ output.position = input.position;
+ output.velocity = input.velocity;
+ output.size = input.size;
+ output.rotation = Rad2Deg (input.rotation);
+ output.angularVelocity = Rad2Deg (input.angularVelocity);
+ output.energy =input.energy;
+ output.startEnergy = input.startEnergy;
+ output.color = input.color;
+
+ Prefetch(&particle[i] + 8);
+ Prefetch(&particle[i + base] + 8);
+ }
+}
+
+void ParticleEmitter::WriteParticles( const SimpleParticle* __restrict particle, /*int base,*/ int count )
+{
+ if (count > kMaxParticleCount)
+ {
+ ErrorString(Format("You are assigning more than %d particles", kMaxParticleCount));
+ count = kMaxParticleCount;
+ }
+
+ MinMaxAABB& aabb = m_PrivateInfo.aabb;
+ aabb.Init();
+
+ m_Particles.resize(count);
+
+ int newSize = 0;
+ for (int i=0;i<count;++i)
+ {
+ const SimpleParticle& input = particle[i];
+ Particle& output = m_Particles[newSize];
+
+ output.position = input.position;
+ aabb.Encapsulate( input.position );
+ output.velocity = input.velocity;
+ output.size = input.size;
+ output.rotation = Deg2Rad(input.rotation);
+ output.angularVelocity = Deg2Rad(input.angularVelocity);
+ output.energy = input.energy;
+ output.startEnergy = FloatMax(input.startEnergy, input.energy);
+ output.color = input.color;
+
+ if (output.energy > 0.0f )
+ newSize++;
+
+ Prefetch(&particle[i] + 8);
+ }
+
+ m_Particles.resize(newSize);
+}
+
+
+void ParticleEmitter::InitParticleEnergy(Rand& r, Particle& p, float dt)
+{
+ // if any if energies is infinity - both energy and startEnergy will be infinity too.
+ // that would be fine, but we're not quite ready to handle Inf properly
+
+
+ // add deltatime - it will be removed in particle animator
+ p.startEnergy = clamp ( RangedRandom (r, m_MinEnergy, m_MaxEnergy), 0.0f, kMaxEnergy );
+ p.energy = p.startEnergy + dt + kTimeEpsilon;
+}
+
+static void ResetRandSeedForEmitter ()
+{
+ gEmitterRand.SetSeed (5);
+}
+
+void ParticleEmitter::InitializeClass ()
+{
+ GlobalCallbacks::Get().resetRandomAfterLevelLoad.Register(ResetRandSeedForEmitter);
+}
+
+void ParticleEmitter::CleanupClass ()
+{
+ GlobalCallbacks::Get().resetRandomAfterLevelLoad.Unregister(ResetRandSeedForEmitter);
+}
+
+IMPLEMENT_CLASS_HAS_INIT (ParticleEmitter)
+IMPLEMENT_OBJECT_SERIALIZE (ParticleEmitter)
+INSTANTIATE_TEMPLATE_TRANSFER (ParticleEmitter)
diff --git a/Runtime/Filters/Particles/ParticleEmitter.h b/Runtime/Filters/Particles/ParticleEmitter.h
new file mode 100644
index 0000000..3622fe2
--- /dev/null
+++ b/Runtime/Filters/Particles/ParticleEmitter.h
@@ -0,0 +1,151 @@
+#ifndef PARTICLEEMITTER_H
+#define PARTICLEEMITTER_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "ParticleStruct.h"
+#include "Runtime/Utilities/LinkedList.h"
+
+
+class Rand;
+
+#define GET_SET_DIRTY_MIN(TYPE,PROP_NAME,VAR_NAME,MIN_VAL) void Set##PROP_NAME (TYPE val) { VAR_NAME = std::max(val,MIN_VAL); SetDirty(); } TYPE Get##PROP_NAME () const { return (TYPE)VAR_NAME; }
+
+
+
+class ParticleEmitter : public Unity::Component
+{
+public:
+ REGISTER_DERIVED_ABSTRACT_CLASS (ParticleEmitter, Unity::Component)
+ DECLARE_OBJECT_SERIALIZE (ParticleEmitter)
+
+ ParticleEmitter (MemLabelId label, ObjectCreationMode mode);
+
+ void SetEmit (bool emit);
+ bool IsEmitting () const { return m_Emit; }
+
+ GET_SET_DIRTY_MIN (float, MinSize, m_MinSize, 0.0f);
+ GET_SET_DIRTY_MIN (float, MaxSize, m_MaxSize, 0.0f);
+ GET_SET_DIRTY_MIN (float, MinEnergy, m_MinEnergy, 0.0f);
+ GET_SET_DIRTY_MIN (float, MaxEnergy, m_MaxEnergy, 0.0f);
+ GET_SET_DIRTY_MIN (float, MinEmission, m_MinEmission, 0.0f);
+ GET_SET_DIRTY_MIN (float, MaxEmission, m_MaxEmission, 0.0f);
+
+ GET_SET_DIRTY (float, EmitterVelocityScale, m_EmitterVelocityScale);
+ GET_SET_DIRTY (Vector3f, WorldVelocity, m_WorldVelocity);
+ GET_SET_DIRTY (Vector3f, LocalVelocity, m_LocalVelocity);
+ GET_SET_DIRTY (Vector3f, RndVelocity, m_RndVelocity);
+ GET_SET_DIRTY (bool, RndRotation, m_RndInitialRotations);
+ GET_SET_DIRTY (float, AngularVelocity, m_AngularVelocity);
+
+ float GetRndAngularVelocity () const { return m_RndAngularVelocity; }
+ void SetRndAngularVelocity (float val) { if (val < 0) val = 0; m_RndAngularVelocity = val; SetDirty(); }
+
+ bool GetUseWorldSpace () const { return m_UseWorldSpace; }
+ void SetUseWorldSpace (bool val) { m_UseWorldSpace = val; } // TODO: set dirty?
+
+ // Emit particleCount particles
+ void Emit (unsigned int particleCount, float invDeltaTime);
+
+ // This is needed, so that subsequent calls to Emit() will each only emit at the current location, and not along the line
+ // between the current and last location.
+ void EmitResetEmitterPos (int particleCount, float invDeltaTime) { ResetEmitterPos(); Emit (particleCount, invDeltaTime); }
+
+ // Emit one particle
+ void Emit (const Vector3f &pos, const Vector3f &dir, float size, float energy, const ColorRGBA32 &color, float rotation, float angularVelocity);
+
+ void Deactivate (DeactivateOperation operation);
+ void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ // clear all particles.
+ void ClearParticles ();
+
+ void ReadParticles( SimpleParticle*__restrict particle, int baseIndex, int count ) const;
+ void WriteParticles( const SimpleParticle* __restrict particle, int count );
+
+ int GetParticleCount() const { return m_Particles.size(); }
+
+ static void UpdateAllParticleSystems();
+ void UpdateParticleSystem(float deltaTime);
+
+ const ParticleArray& GetParticles() const { return m_Particles; }
+ ParticleArray& GetParticles() { return m_Particles; }
+ const PrivateParticleInfo& GetPrivateInfo() const { return m_PrivateInfo; }
+
+ bool GetEnabled () const { return m_Enabled; }
+ void SetEnabled (bool enabled);
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+protected:
+ // Sets "does this emitter have to be updated next frame" flags.
+ void UpdateManagerState( bool updateParticles );
+
+private:
+ // Subclasses have to override this function to place particles
+ virtual void SetupParticles (ParticleArray& particles, const Vector3f& velocityOffset,
+ const Matrix3x3f& rotation, int firstIndex) = 0;
+
+ // Emit particles
+ void TimedEmit (float deltaTime);
+
+ void ResetEmitterPos ();
+ void CalcOffsets (Vector3f *velocityOffset, Matrix3x3f *localRotation, float invDeltaTime);
+
+protected:
+ Vector3f m_EmitterPos;
+ Vector3f m_PreviousEmitterPos;
+
+protected:
+ ParticleArray m_Particles;
+ PrivateParticleInfo m_PrivateInfo;
+ float m_EmissionFrac;
+
+ float m_MinSize; ///< minimum size range {0, infinity }
+ float m_MaxSize; ///< maximum size range {0, infinity }
+ float m_MinEnergy; ///< minimum energy range { 0, infinity }
+ float m_MaxEnergy; ///< maximum energy range { 0, infinity }
+
+ float m_MinEmission; ///< minimum emissions per second range { 0, infinity }
+ float m_MaxEmission; ///< maximum emissions per second range { 0, infinity }
+
+ float m_EmitterVelocityScale;///< Scales velocity of the emitter
+
+ Vector3f m_WorldVelocity;
+
+ Vector3f m_LocalVelocity;
+
+ Vector3f m_TangentVelocity;
+
+ Vector3f m_RndVelocity;
+
+ bool m_UseWorldSpace; ///< Are particles simulated in WorldSpace or in LocalSpace?
+
+ bool m_RndInitialRotations; ///< Should initial particle rotations be randomized?
+ float m_RndAngularVelocity;
+ float m_AngularVelocity;
+
+ bool m_Enabled;
+ bool m_Emit;
+ bool m_OneShot;
+ bool m_FirstFrame;
+
+protected:
+
+ void InitParticleEnergy(Rand& r, Particle& p, float dt);
+
+ #if DOXYGEN
+ ///////// @TODO: DO EASIER BACKWARDS COMPATIBILITY: THEN WE DONT NEED THIS CRAP
+ float minSize; ///< minimum size range {0, infinity }
+ float maxSize; ///< minimum size range {0, infinity }
+ float minEnergy; ///< minimum size range {0, infinity }
+ float maxEnergy; ///< minimum size range {0, infinity }
+ float minEmission; ///< minimum size range {0, infinity }
+ float maxEmission; ///< minimum size range {0, infinity }
+ #endif
+
+private:
+ ListNode<ParticleEmitter> m_EmittersListNode;
+};
+
+#endif
diff --git a/Runtime/Filters/Particles/ParticleRenderer.cpp b/Runtime/Filters/Particles/ParticleRenderer.cpp
new file mode 100644
index 0000000..3a638e9
--- /dev/null
+++ b/Runtime/Filters/Particles/ParticleRenderer.cpp
@@ -0,0 +1,630 @@
+#include "UnityPrefix.h"
+#include "ParticleRenderer.h"
+#include "ParticleEmitter.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Filters/Misc/LineBuilder.h"
+#include "Runtime/Camera/Camera.h"
+#include "ParticleStruct.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+
+IMPLEMENT_CLASS (ParticleRenderer)
+IMPLEMENT_OBJECT_SERIALIZE (ParticleRenderer)
+
+// The distance of particle Corner from the center
+// sin (45 deg), or sqrt(0.5)
+#define kMaxParticleSizeFactor 0.707106781186548f
+
+ParticleRenderer::ParticleRenderer (MemLabelId label, ObjectCreationMode mode)
+: Super(kRendererParticle, label, mode)
+{
+ SetVisible (false);
+ m_UVFrames = NULL;
+}
+
+ParticleRenderer::~ParticleRenderer ()
+{
+ if (m_UVFrames)
+ UNITY_FREE(kMemParticles, m_UVFrames);
+}
+
+struct ParticleVertex {
+ Vector3f vert;
+ Vector3f normal;
+ ColorRGBA32 color;
+ Vector2f uv;
+};
+
+void ParticleRenderer::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ ParticleRenderer::GenerateUVFrames ();
+}
+
+void ParticleRenderer::SetUVFrames (const Rectf *uvFrames, int numFrames)
+{
+ m_NumUVFrames = numFrames;
+
+ if (m_UVFrames)
+ UNITY_FREE(kMemParticles, m_UVFrames);
+
+ size_t size;
+ size = sizeof(Rectf) * m_NumUVFrames;
+
+ if (m_NumUVFrames != 0 && m_NumUVFrames != size / sizeof(Rectf))
+ {
+ m_UVFrames = NULL;
+ m_NumUVFrames = 0;
+ }
+ else
+ {
+ m_UVFrames = (Rectf*)UNITY_MALLOC(kMemParticles, size);
+ memcpy(m_UVFrames, uvFrames, numFrames*sizeof(Rectf));
+ }
+}
+
+void ParticleRenderer::GenerateUVFrames ()
+{
+ if(m_UVAnimation.xTile < 1)
+ m_UVAnimation.xTile = 1;
+ if(m_UVAnimation.yTile < 1)
+ m_UVAnimation.yTile = 1;
+
+ m_NumUVFrames = (m_UVAnimation.xTile * m_UVAnimation.yTile);
+ float animUScale = 1.0f / m_UVAnimation.xTile;
+ float animVScale = 1.0f / m_UVAnimation.yTile;
+
+ if (m_UVFrames)
+ UNITY_FREE(kMemParticles, m_UVFrames);
+
+ if(m_NumUVFrames == 1)
+ m_NumUVFrames = 0;
+ SET_ALLOC_OWNER(this);
+ m_UVFrames = (Rectf*)UNITY_MALLOC(kMemParticles, m_NumUVFrames * sizeof(Rectf));
+
+ for(int index=0;index<m_NumUVFrames;index++)
+ {
+ int vIdx = index / m_UVAnimation.xTile;
+ int uIdx = index - vIdx * m_UVAnimation.xTile; // slightly faster than index % m_UVAnimation.xTile
+ float uOffset = (float)uIdx * animUScale;
+ float vOffset = 1.0f - animVScale - (float)vIdx * animVScale;
+
+ m_UVFrames[index] = Rectf(uOffset, vOffset, animUScale, animVScale);
+ }
+}
+
+void ParticleRenderer::SetUVAnimationXTile (int v)
+{
+ v = std::max(v,1);
+ if (v == m_UVAnimation.xTile)
+ return;
+ m_UVAnimation.xTile = v;
+ SetDirty ();
+ GenerateUVFrames ();
+}
+void ParticleRenderer::SetUVAnimationYTile (int v)
+{
+ v = std::max(v,1);
+ if (v == m_UVAnimation.yTile)
+ return;
+ m_UVAnimation.yTile = v;
+ SetDirty ();
+ GenerateUVFrames ();
+}
+void ParticleRenderer::SetUVAnimationCycles (float v)
+{
+ v = std::max(v,0.0f);
+ if (v == m_UVAnimation.cycles)
+ return;
+ m_UVAnimation.cycles = v;
+ SetDirty ();
+}
+
+
+
+PROFILER_INFORMATION(gParticlesProfile, "ParticleRenderer.Render", kProfilerParticles)
+PROFILER_INFORMATION(gSubmitVBOProfileParticle, "Mesh.SubmitVBO", kProfilerRender)
+
+#pragma message ("Optimize particle systems to use templates for inner loops and build optimized inner loops without ifs")
+
+void ParticleRenderer::Render (int/* materialIndex*/, const ChannelAssigns& channels)
+{
+
+ ParticleEmitter* emitter = QueryComponent(ParticleEmitter);
+ if( !emitter )
+ return;
+
+ PROFILER_AUTO_GFX(gParticlesProfile, this)
+
+ GfxDevice& device = GetGfxDevice();
+ Matrix4x4f matrix;
+
+ ParticleArray& particles = emitter->GetParticles();
+ const PrivateParticleInfo& privateInfo = emitter->GetPrivateInfo();
+ if( particles.empty() )
+ return;
+
+ Camera& cam = GetCurrentCamera ();
+
+ Vector3f xSpan, ySpan;
+ if(m_StretchParticles == kBillboardFixedHorizontal)
+ {
+ xSpan = Vector3f(1.0f,0.0f,0.0f);
+ ySpan = Vector3f(0.0f,0.0f,1.0f);
+ }
+
+ if(m_StretchParticles == kBillboardFixedVertical)
+ {
+ ySpan = Vector3f(0.0f,1.0f,0.0f);
+ Vector3f zSpan = RotateVectorByQuat (cam.GetComponent (Transform).GetRotation(), Vector3f(0.0f,0.0f,1.0f));
+ xSpan = NormalizeSafe( Cross(ySpan, zSpan) );
+ }
+
+ Vector3f cameraVelocity;
+ if (privateInfo.useWorldSpace)
+ {
+ CopyMatrix (device.GetViewMatrix (), matrix.GetPtr());
+ cameraVelocity = cam.GetVelocity ();
+ }
+ else
+ {
+ Matrix4x4f mat, temp;
+ CopyMatrix (device.GetViewMatrix (), temp.GetPtr());
+ mat = GetComponent (Transform).GetLocalToWorldMatrixNoScale();
+ MultiplyMatrices4x4 (&temp, &mat, &matrix);
+ cameraVelocity = GetComponent (Transform).InverseTransformDirection (cam.GetVelocity ());
+ }
+
+ // Constrain the size to be a fraction of the viewport size.
+ // In perspective case, max size is (z*factorA). In ortho case, max size is just factorB. To have both
+ // without branches, we do (z*factorA+factorB) and set one of factors to zero.
+ float maxPlaneScale;
+ float maxOrthoSize;
+ if (!cam.GetOrthographic()) {
+ maxPlaneScale = -cam.CalculateFarPlaneWorldSpaceLength () * m_MaxParticleSize / cam.GetFar ();
+ maxOrthoSize = 0.0f;
+ } else {
+ maxPlaneScale = 0.0f;
+ maxOrthoSize = cam.CalculateFarPlaneWorldSpaceLength () * m_MaxParticleSize;
+ }
+
+ /// Sort the particles
+ if (m_StretchParticles == kSortedBillboard && !particles.empty ())
+ {
+ static vector<float> s_Dist;
+ if (s_Dist.size() < particles.size()) {
+ s_Dist.resize (0);
+ s_Dist.resize (particles.size());
+ }
+
+ // Calculate all distances
+ int i;
+ Vector3f distFactor = Vector3f (matrix.Get (2, 0), matrix.Get (2, 1), + matrix.Get (2, 2));
+ for (i = 0; i < particles.size(); i++)
+ s_Dist[i] = Dot (distFactor, particles[i].position);
+
+ // Bubblesort them
+ i = 0;
+ while (i < particles.size() - 1)
+ {
+ if (s_Dist[i] > s_Dist[i + 1])
+ {
+ std::swap (s_Dist[i], s_Dist[i + 1]);
+ std::swap (particles[i], particles[i + 1]);
+ if (i > 0)
+ i -= 2;
+ }
+ i++;
+ }
+ }
+
+ // Calculate parameters for UV animation
+ const int animFullTexCount = int(m_NumUVFrames * m_UVAnimation.cycles);
+
+ // Get VBO chunk
+ const int particleCount = particles.size();
+ DynamicVBO& vbo = device.GetDynamicVBO();
+ ParticleVertex* vbPtr;
+ if( !vbo.GetChunk( (1<<kShaderChannelVertex) | (1<<kShaderChannelNormal) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelColor),
+ particleCount * 4, 0,
+ DynamicVBO::kDrawQuads,
+ (void**)&vbPtr, NULL ) )
+ {
+ return;
+ }
+
+ int billboardRenderMode = m_StretchParticles;
+
+ if (billboardRenderMode == kSortedBillboard)
+ billboardRenderMode = kBillboard;
+ if (billboardRenderMode == kBillboardFixedVertical)
+ billboardRenderMode = kBillboardFixedHorizontal;
+
+ // Fill vertex buffer with particles
+ for( int i = 0; i < particleCount; ++i )
+ {
+ const Particle& p = particles[i];
+ Vector3f vert[4];
+ Vector2f uv[4];
+
+ if (p.rotation != 0)
+ {
+ if (billboardRenderMode == kBillboard)
+ billboardRenderMode = kBillboardRotated;
+ else if (billboardRenderMode == kBillboardFixedHorizontal)
+ billboardRenderMode = kBillboardFixedRotated;
+ }
+ // Positions
+ // @TODO: get rid of the branch here
+ switch (billboardRenderMode) {
+ case kBillboard:
+ {
+ // Project point and create quad
+ Vector3f center = matrix.MultiplyPoint3 (p.position);
+
+ // Constrain the size to be a fraction of the viewport size.
+ // v[0].z * / farPlaneZ * farPlaneWorldSpaceLength * maxLength[0...1]
+ // Also all valid z's are negative so we just negate the whole equation
+ float maxWorldSpaceLength = center.z * maxPlaneScale + maxOrthoSize;
+ float size = std::min (p.size, maxWorldSpaceLength);
+ float halfSize = size * 0.5f;
+
+ vert[0].Set( center.x - halfSize, center.y + halfSize, center.z );
+ vert[1].Set( center.x + halfSize, center.y + halfSize, center.z );
+ vert[2].Set( center.x + halfSize, center.y - halfSize, center.z );
+ vert[3].Set( center.x - halfSize, center.y - halfSize, center.z );
+ }
+ break;
+ case kBillboardFixedHorizontal:
+ {
+ // Project point and create quad
+ Vector3f center = matrix.MultiplyPoint3 (p.position);
+
+ // Constrain the size to be a fraction of the viewport size.
+ // v[0].z * / farPlaneZ * farPlaneWorldSpaceLength * maxLength[0...1]
+ // Also all valid z's are negative so we just negate the whole equation
+ float maxWorldSpaceLength = (center.z - p.size * kMaxParticleSizeFactor) * maxPlaneScale + maxOrthoSize;
+ float size = std::min (p.size, maxWorldSpaceLength);
+ float halfSize = size * 0.5f;
+
+ vert[0] = matrix.MultiplyPoint3 ( p.position - halfSize * xSpan + halfSize * ySpan );
+ vert[1] = matrix.MultiplyPoint3 ( p.position + halfSize * xSpan + halfSize * ySpan );
+ vert[2] = matrix.MultiplyPoint3 ( p.position + halfSize * xSpan - halfSize * ySpan );
+ vert[3] = matrix.MultiplyPoint3 ( p.position - halfSize * xSpan - halfSize * ySpan );
+ }
+ break;
+ case kBillboardRotated:
+ {
+ // Project point and create quad
+ // Particles can be rotated by animation curve and width & height can be animated individually using an animation curve
+ Vector3f center = matrix.MultiplyPoint3 (p.position);
+
+ // Constrain the size to be a fraction of the viewport size.
+ // v[0].z * / farPlaneZ * farPlaneWorldSpaceLength * maxLength[0...1]
+ // Also all valid z's are negative so we just negate the whole equation
+ float maxWorldSpaceLength = center.z * maxPlaneScale + maxOrthoSize;
+
+ float s = Sin (p.rotation);
+ float c = Cos (p.rotation);
+
+ float x00 = c;
+ float x01 = s;
+ float x10 = -s;
+ float x11 = c;
+
+ float size = std::min (p.size, maxWorldSpaceLength);
+ float halfSize = size * 0.5f;
+
+ #define MUL(w,h) center.x + x00 * w + x01 * h, center.y + x10 * w + x11 * h, center.z
+
+ vert[0].Set(MUL(-halfSize, halfSize));
+ vert[1].Set(MUL( halfSize, halfSize));
+ vert[2].Set(MUL( halfSize, -halfSize));
+ vert[3].Set(MUL(-halfSize, -halfSize));
+ #undef MUL
+ }
+ break;
+ case kBillboardFixedRotated:
+ {
+ // Project point and create quad
+ Vector3f center = matrix.MultiplyPoint3 (p.position);
+
+ // Constrain the size to be a fraction of the viewport size.
+ // v[0].z * / farPlaneZ * farPlaneWorldSpaceLength * maxLength[0...1]
+ // Also all valid z's are negative so we just negate the whole equation
+ float maxWorldSpaceLength = center.z * maxPlaneScale + maxOrthoSize;
+
+ float s = Sin (p.rotation);
+ float c = Cos (p.rotation);
+
+ float size = std::min (p.size, maxWorldSpaceLength);
+ float halfSize = size * 0.5f;
+
+ vert[0] = matrix.MultiplyPoint3 ( p.position + halfSize * xSpan * c + halfSize * ySpan * s );
+ vert[1] = matrix.MultiplyPoint3 ( p.position + halfSize * ySpan * c - halfSize * xSpan * s );
+ vert[2] = matrix.MultiplyPoint3 ( p.position - halfSize * xSpan * c - halfSize * ySpan * s );
+ vert[3] = matrix.MultiplyPoint3 ( p.position - halfSize * ySpan * c + halfSize * xSpan * s );
+ }
+ break;
+
+ case kStretch3D:
+ {
+ Vector3f velocity = p.velocity - cameraVelocity * m_CameraVelocityScale;
+ float sqrVelocity = SqrMagnitude (velocity);
+
+ float size = p.size;
+
+ float lineScale;
+ if (sqrVelocity > Vector3f::epsilon)
+ lineScale = m_VelocityScale + FastInvSqrt (sqrVelocity) * (m_LengthScale * size);
+ else
+ lineScale = 0.0F;
+
+ Vector3f lineDelta = velocity * lineScale;
+ Vector3f begProj = matrix.MultiplyPoint3 (p.position);
+ Vector3f endProj = matrix.MultiplyPoint3 (p.position - lineDelta);
+
+ // Constrain the size to be a fraction of the viewport size.
+ // v[0].z * / farPlaneZ * farPlaneWorldSpaceLength * maxLength[0...1]
+ // Also all valid z's are negative so we just negate the whole equation
+ float maxWorldSpaceLength = endProj.z * maxPlaneScale + maxOrthoSize;
+ size = std::min (size, maxWorldSpaceLength);
+ float halfSize = size * 0.5F;
+
+ Vector2f delta = Calculate2DLineExtrusion(begProj, begProj - endProj, halfSize);
+
+ vert[0].Set( begProj.x + delta.x, begProj.y + delta.y, begProj.z );
+ vert[1].Set( endProj.x + delta.x, endProj.y + delta.y, endProj.z );
+ vert[2].Set( endProj.x - delta.x, endProj.y - delta.y, endProj.z );
+ vert[3].Set( begProj.x - delta.x, begProj.y - delta.y, begProj.z );
+ }
+ break;
+ case kStretch2D:
+ {
+ Vector3f velocity = p.velocity - cameraVelocity * m_CameraVelocityScale;
+ float sqrVelocity = SqrMagnitude (velocity);
+
+ float size = p.size;
+
+ float lineScale;
+ if (sqrVelocity > Vector3f::epsilon)
+ lineScale = m_VelocityScale + FastInvSqrt (sqrVelocity) * (m_LengthScale * size);
+ else
+ lineScale = 0.0F;
+
+ Vector3f lineDelta = velocity * lineScale;
+ Vector3f begProj = matrix.MultiplyPoint3 (p.position);
+ Vector3f endProj = matrix.MultiplyPoint3 (p.position + lineDelta);
+
+ float dx = endProj.x - begProj.x;
+ float dy = endProj.y - begProj.y;
+
+ float sqrProjectedLength = dx*dx + dy*dy;
+
+ if (sqrProjectedLength < Vector3f::epsilon)
+ {
+ dx = 1.0F;
+ dy = 0.0F;
+ sqrProjectedLength = 1.0F;
+ }
+
+ // Constrain the size to be a fraction of the viewport size.
+ // v[0].z * / farPlaneZ * farPlaneWorldSpaceLength * maxLength[0...1]
+ // Also all valid z's are negative so we just negate the whole equation
+ float maxWorldSpaceLength = endProj.z * maxPlaneScale + maxOrthoSize;
+ size = std::min (size, maxWorldSpaceLength);
+ float halfSize = size * 0.5F;
+
+ float lengthInv = halfSize * FastInvSqrt (sqrProjectedLength);
+ // Orthogonal 2D-vector to sdx0
+ float sdx1 = -dy * lengthInv;
+ float sdy1 = dx * lengthInv;
+
+ // scale with velocity
+ vert[0].Set( begProj.x + sdx1, begProj.y + sdy1, begProj.z );
+ vert[1].Set( endProj.x + sdx1, endProj.y + sdy1, endProj.z );
+ vert[2].Set( endProj.x - sdx1, endProj.y - sdy1, endProj.z );
+ vert[3].Set( begProj.x - sdx1, begProj.y - sdy1, begProj.z );
+ }
+ break;
+ }
+
+
+ // UVs
+ if(m_NumUVFrames > 0)
+ {
+ const float alpha = 1.0f - clamp01 (p.energy / p.startEnergy);
+ unsigned int index = (unsigned int)(alpha * animFullTexCount);
+
+ Rectf *r = m_UVFrames+(index % m_NumUVFrames);
+ uv[0].Set( r->x, r->y + r->height );
+ uv[1].Set( r->x + r->width, r->y + r->height );
+ uv[2].Set( r->x + r->width, r->y );
+ uv[3].Set( r->x, r->y );
+ }
+ else
+ {
+ uv[0].Set( 0, 1 );
+ uv[1].Set( 1, 1 );
+ uv[2].Set( 1, 0 );
+ uv[3].Set( 0, 0 );
+ }
+
+
+ // Swizzle color of the renderer requires it
+ ColorRGBA32 color = p.color;
+ color = device.ConvertToDeviceVertexColor(color);
+
+ // Now, write out the vertex structures sequentially (important when writing into dynamic VBO)
+ // @TODO: this place seems to be heavy in the profile (even more than branches above), optimize somehow!
+ vbPtr[0].vert = vert[0];
+ vbPtr[0].normal.Set( 0.0f, 0.0f, 1.0f );
+ vbPtr[0].color = color;
+ vbPtr[0].uv = uv[0];
+
+ vbPtr[1].vert = vert[1];
+ vbPtr[1].normal.Set( 0.0f, 0.0f, 1.0f );
+ vbPtr[1].color = color;
+ vbPtr[1].uv = uv[1];
+
+ vbPtr[2].vert = vert[2];
+ vbPtr[2].normal.Set( 0.0f, 0.0f, 1.0f );
+ vbPtr[2].color = color;
+ vbPtr[2].uv = uv[2];
+
+ vbPtr[3].vert = vert[3];
+ vbPtr[3].normal.Set( 0.0f, 0.0f, 1.0f );
+ vbPtr[3].color = color;
+ vbPtr[3].uv = uv[3];
+
+ // Next four vertices
+ vbPtr += 4;
+ }
+
+ vbo.ReleaseChunk( particleCount * 4, 0 );
+
+ // Draw
+ float matView[16];
+ CopyMatrix(device.GetViewMatrix(), matView);
+ device.SetViewMatrix (Matrix4x4f::identity.GetPtr()); // implicitly sets world to identity
+
+ if (m_CustomProperties)
+ device.SetMaterialProperties (*m_CustomProperties);
+
+ PROFILER_BEGIN(gSubmitVBOProfileParticle, this)
+ vbo.DrawChunk (channels);
+ GPU_TIMESTAMP();
+ PROFILER_END
+
+ device.SetViewMatrix(matView);
+}
+
+void ParticleRenderer::AdjustBoundsForStretch( const ParticleEmitter& emitter, MinMaxAABB& aabb ) const
+{
+ Assert(m_StretchParticles == kStretch3D);
+
+ const ParticleArray& particles = emitter.GetParticles();
+ const size_t particleCount = particles.size();
+ const float velocityScale = m_VelocityScale;
+ const float lengthScale = m_LengthScale;
+ const Particle* p = &particles[0];
+
+ for( size_t i = 0; i < particleCount; ++i, ++p ) {
+ float sqrVelocity = SqrMagnitude (p->velocity);
+ if (sqrVelocity > Vector3f::epsilon) {
+ float scale = velocityScale + FastInvSqrt (sqrVelocity) * lengthScale * p->size;
+ aabb.Encapsulate( p->position - p->velocity * scale );
+ }
+ }
+}
+
+void ParticleRenderer::UpdateTransformInfo ()
+{
+ const Transform& transform = GetTransform();
+ if (m_TransformDirty)
+ {
+ m_TransformInfo.invScale = 1.0f;
+ // will return a cached matrix most of the time
+ m_TransformInfo.transformType = transform.CalculateTransformMatrix (m_TransformInfo.worldMatrix);;
+ }
+
+ if (m_BoundsDirty)
+ {
+ ParticleEmitter* emitter = QueryComponent(ParticleEmitter);
+ if (!emitter)
+ {
+ m_TransformInfo.localAABB.SetCenterAndExtent(Vector3f::zero, Vector3f::zero);
+ m_TransformInfo.worldAABB.SetCenterAndExtent(Vector3f::zero, Vector3f::zero);
+ return;
+ }
+
+ const PrivateParticleInfo& info = emitter->GetPrivateInfo();
+ MinMaxAABB aabb = info.aabb;
+ if (m_StretchParticles == kStretch3D)
+ AdjustBoundsForStretch (*emitter, aabb);
+ aabb.Expand (info.maxParticleSize * kMaxParticleSizeFactor);
+
+ if (info.useWorldSpace)
+ {
+ m_TransformInfo.worldAABB = aabb;
+ InverseTransformAABB (m_TransformInfo.worldAABB, transform.GetPosition(), transform.GetRotation(), m_TransformInfo.localAABB);
+ }
+ else
+ {
+ m_TransformInfo.localAABB = aabb;
+ TransformAABB (m_TransformInfo.localAABB, transform.GetPosition(), transform.GetRotation(), m_TransformInfo.worldAABB);
+ }
+ }
+}
+
+
+void ParticleRenderer::UpdateRenderer()
+{
+ ParticleEmitter* emitter = QueryComponent(ParticleEmitter);
+ if( emitter )
+ {
+ bool empty = emitter->GetParticles().empty();
+ SetVisible( !empty );
+ if( !empty )
+ BoundsChanged();
+ }
+ else
+ {
+ UpdateManagerState( false );
+ }
+
+ Super::UpdateRenderer();
+}
+
+void ParticleRenderer::UpdateParticleRenderer()
+{
+ UpdateManagerState( true );
+}
+
+
+template<class TransferFunction> inline
+void ParticleRenderer::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion (2);
+ TRANSFER (m_CameraVelocityScale);
+ TRANSFER_SIMPLE (m_StretchParticles);
+ TRANSFER_SIMPLE (m_LengthScale);
+ TRANSFER (m_VelocityScale);
+ TRANSFER (m_MaxParticleSize);
+
+ if (transfer.IsCurrentVersion()) {
+ transfer.Transfer (m_UVAnimation, "UV Animation");
+ } else {
+ transfer.Transfer (m_UVAnimation.xTile, "m_AnimatedTextureCount");
+ }
+}
+
+void ParticleRenderer::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+ m_MaxParticleSize = std::max (0.0F, m_MaxParticleSize);
+ m_UVAnimation.xTile = std::max (1, m_UVAnimation.xTile);
+ m_UVAnimation.yTile = std::max (1, m_UVAnimation.yTile);
+ m_UVAnimation.cycles = std::max (0.0F, m_UVAnimation.cycles);
+}
+
+void ParticleRenderer::Reset ()
+{
+ Super::Reset ();
+ m_StretchParticles = kBillboard;
+ m_LengthScale = 2.0F;
+ m_VelocityScale = 0.0F;
+ m_MaxParticleSize = 0.25F;
+ m_UVAnimation.xTile = 1;
+ m_UVAnimation.yTile = 1;
+ m_UVAnimation.cycles = 1;
+ m_CameraVelocityScale = 0.0;
+}
diff --git a/Runtime/Filters/Particles/ParticleRenderer.h b/Runtime/Filters/Particles/ParticleRenderer.h
new file mode 100644
index 0000000..d63661c
--- /dev/null
+++ b/Runtime/Filters/Particles/ParticleRenderer.h
@@ -0,0 +1,103 @@
+#ifndef PARTICLERENDERER_H
+#define PARTICLERENDERER_H
+
+#include <vector>
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/AnimationCurve.h"
+using std::vector;
+class ParticleEmitter;
+class MinMaxAABB;
+
+
+
+
+enum ParticleRenderMode {
+ kBillboard = 0,
+ kStretch2D = 1,
+ kStretch3D = 3,
+ kSortedBillboard = 2,
+ kBillboardFixedHorizontal = 4,
+ kBillboardFixedVertical = 5,
+
+ /// Internal modes
+ kBillboardRotated = 1000,
+ kBillboardFixedRotated = 1001
+};
+
+class ParticleRenderer : public Renderer {
+public:
+ REGISTER_DERIVED_CLASS (ParticleRenderer, Renderer)
+ DECLARE_OBJECT_SERIALIZE (ParticleRenderer)
+
+ ParticleRenderer (MemLabelId label, ObjectCreationMode mode);
+ // ~ParticleRenderer(); declared-by-macro
+
+ virtual void Render (int materialIndex, const ChannelAssigns& channels);
+
+ // Can operate in either local or world space, so we need to fill whole transform info ourselves
+ virtual void UpdateTransformInfo();
+
+ virtual void CheckConsistency ();
+ virtual void Reset ();
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ GET_SET_DIRTY (ParticleRenderMode, RenderMode, m_StretchParticles) ;
+ GET_SET_DIRTY (float, LengthScale, m_LengthScale) ;
+ GET_SET_DIRTY (float, VelocityScale, m_VelocityScale) ;
+ GET_SET_DIRTY (float, CameraVelocityScale, m_CameraVelocityScale) ;
+ GET_SET_DIRTY (float, MaxParticleSize, m_MaxParticleSize) ;
+ int GetUVAnimationXTile() const { return m_UVAnimation.xTile; }
+ int GetUVAnimationYTile() const { return m_UVAnimation.yTile; }
+ float GetUVAnimationCycles() const { return m_UVAnimation.cycles; }
+ void SetUVAnimationXTile (int v);
+ void SetUVAnimationYTile (int v);
+ void SetUVAnimationCycles (float v);
+
+ void UpdateParticleRenderer();
+
+ Rectf *GetUVFrames() {return m_UVFrames;};
+ int GetNumUVFrames() {return m_NumUVFrames;};
+
+ void SetUVFrames(const Rectf *uvFrames, int numFrames);
+
+private:
+ // from Renderer
+ virtual void UpdateRenderer();
+
+ void AdjustBoundsForStretch( const ParticleEmitter& emitter, MinMaxAABB& aabb ) const;
+
+protected:
+ struct UVAnimation {
+ int xTile; ///< Number of texture tiles in the X direction.
+ int yTile; ///< Number of texture tiles in the Y direction.
+ float cycles; ///< Number of cycles over a particle's life span.
+ DECLARE_SERIALIZE (UVAnimation)
+ };
+ void SetBufferSize (int particleCount);
+ void GenerateUVFrames ();
+
+ int m_StretchParticles; ///< enum { Billboard = 0, Stretched = 3, Sorted Billboard = 2, Horizontal Billboard = 4, Vertical Billboard = 5 } Should the particles be stretched along their velocity?
+ float m_LengthScale; ///< When Stretch Particles is enabled, defines the length of the particle compared to its width.
+ float m_VelocityScale; ///< When Stretch Particles is enabled, defines the length of the particle compared to its velocity.
+ float m_MaxParticleSize; ///< How large is a particle allowed to be on screen at most? 1 is entire viewport. 0.5 is half viewport.
+ UVAnimation m_UVAnimation; ///< Tiled UV settings.
+
+ float m_CameraVelocityScale; ///< How much the camera motion is factored in when determining particle stretching
+
+ int m_NumUVFrames; //uv tiles for uv animation
+ Rectf *m_UVFrames;
+};
+
+template<class TransferFunc>
+void ParticleRenderer::UVAnimation::Transfer (TransferFunc& transfer) {
+ transfer.Transfer (xTile, "x Tile", kSimpleEditorMask);
+ transfer.Transfer (yTile, "y Tile", kSimpleEditorMask);
+ TRANSFER_SIMPLE (cycles);
+}
+
+#endif
diff --git a/Runtime/Filters/Particles/ParticleStruct.h b/Runtime/Filters/Particles/ParticleStruct.h
new file mode 100644
index 0000000..cafd2dc
--- /dev/null
+++ b/Runtime/Filters/Particles/ParticleStruct.h
@@ -0,0 +1,59 @@
+#ifndef PARTICLESTRUCT_H
+#define PARTICLESTRUCT_H
+
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Geometry/AABB.h"
+#if UNITY_WII
+#include "Runtime/Misc/Allocator.h"
+#endif
+
+struct Particle
+{
+ Vector3f position; //12
+ Vector3f velocity; //12
+ float size; //4
+ float rotation; //4
+ float angularVelocity; //4
+ float energy; //4
+ float startEnergy; //4
+ ColorRGBA32 color; //4
+};
+
+struct SimpleParticle
+{
+ Vector3f position;
+ Vector3f velocity;
+ float size;
+ float rotation;
+ float angularVelocity;
+ float energy;
+ float startEnergy;
+ ColorRGBAf color;
+};
+
+
+// 24 bytes
+typedef UNITY_VECTOR(kMemParticles, Particle) ParticleArray;
+
+struct PrivateParticleInfo
+{
+ MinMaxAABB aabb;
+ float maxEmitterParticleSize;// Maximum size of any particle of emitted particles
+ float maxParticleSize;// max particle size of any particle after particle animation is done
+ float maxEnergy;
+ bool useWorldSpace;
+ bool hadEverEmitOn; // had "emit" flag ever set?
+ bool isEmitOn; // is "emit" flag currently set?
+};
+
+
+const float kTimeEpsilon = 0.001f;
+
+inline void KillParticle (ParticleArray& array, int i)
+{
+ array[i] = array.back ();
+ array.pop_back ();
+}
+
+#endif
diff --git a/Runtime/Filters/Particles/WorldParticleCollider.cpp b/Runtime/Filters/Particles/WorldParticleCollider.cpp
new file mode 100644
index 0000000..a209036
--- /dev/null
+++ b/Runtime/Filters/Particles/WorldParticleCollider.cpp
@@ -0,0 +1,197 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "WorldParticleCollider.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Geometry/Ray.h"
+#include "Runtime/Input/TimeManager.h"
+#include "ParticleStruct.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Interfaces/IRaycast.h"
+
+using namespace std;
+
+#pragma message ("Support collides with")
+///@TODO: SUPPORT COLLIDES WITH
+
+WorldParticleCollider::WorldParticleCollider (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+WorldParticleCollider::~WorldParticleCollider ()
+{
+}
+
+void WorldParticleCollider::Reset ()
+{
+ Super::Reset ();
+ m_BounceFactor = 0.5;
+ m_MinKillVelocity = 0.0F;
+ m_CollisionEnergyLoss = 0.0F;
+
+ m_CollidesWith.m_Bits = -1;
+ m_SendCollisionMessage = false;
+}
+
+IMPLEMENT_CLASS (WorldParticleCollider)
+IMPLEMENT_OBJECT_SERIALIZE (WorldParticleCollider)
+
+template<class T> inline
+void WorldParticleCollider::Transfer (T& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER (m_BounceFactor);
+ TRANSFER (m_CollisionEnergyLoss);
+ TRANSFER (m_CollidesWith);
+ TRANSFER (m_SendCollisionMessage);
+ transfer.Align();
+ TRANSFER (m_MinKillVelocity);
+}
+
+void WorldParticleCollider::UpdateParticleCollider( ParticleArray& particles, PrivateParticleInfo& privateInfo, float deltaTime )
+{
+ int particleCount = particles.size ();
+
+ float sqrMinKillVelocity = m_MinKillVelocity * m_MinKillVelocity;
+ // not sure if multiplication of infinity works on every platform
+ AssertIf (std::numeric_limits<float>::infinity () * std::numeric_limits<float>::infinity () != std::numeric_limits<float>::infinity ());
+
+ if (privateInfo.useWorldSpace)
+ {
+ for (int i=0;i<particleCount;i++)
+ {
+ Vector3f& position = particles[i].position;
+ Vector3f& velocity = particles[i].velocity;
+ Vector3f delta = velocity * deltaTime;
+
+ float psize = 0.5f * particles[i].size;
+ float poffset = 0.51f * particles[i].size;
+
+ Ray ray;
+ ray.SetOrigin (position - delta);
+ float deltaLength = Magnitude (delta);
+ if (deltaLength < Vector3f::epsilon)
+ continue;
+
+ ray.SetDirection (delta / deltaLength);
+
+ float checkLen = deltaLength + psize;
+ float t = checkLen;
+
+ #if ENABLE_PHYSICS
+ HitInfo hit;
+ IRaycast *raycast = GetRaycastInterface();
+ if(raycast)
+ {
+ if(raycast->Raycast(ray, t, m_CollidesWith.m_Bits, hit))
+ {
+ Particle& particle = particles[i];
+
+ // Reflect velocity and apply velocity * time left for particle to position
+ // Test and factor changes into other particle collider
+ velocity = ReflectVector (m_BounceFactor * velocity, hit.normal);
+ float fractionLeftForReflection = (checkLen - t) / deltaLength;
+
+ // Place particle slightly above the surface. Otherwise in next frame raycast can
+ // detect collision again and reflect particle back!
+ position = hit.intersection + hit.normal * poffset + velocity * (deltaTime * fractionLeftForReflection);
+
+ if (m_SendCollisionMessage)
+ {
+ AssertIf (hit.colliderInstanceID == 0);
+ PPtr<Component> collider_pptr(hit.colliderInstanceID);
+ Component* collider = collider_pptr;
+ SendMessage (kParticleCollisionEvent, &collider->GetGameObject (), ClassID (GameObject));
+ collider->SendMessage (kParticleCollisionEvent, &GetGameObject (), ClassID (GameObject));
+ }
+ // Update energy
+ particle.energy -= m_CollisionEnergyLoss;
+ float sqrVelocity = SqrMagnitude (velocity);
+ if (particle.energy <= 0.0f || sqrVelocity < sqrMinKillVelocity) {
+ // Kill particle (replace particle i with last, and continue updating the moved particle)
+ KillParticle (particles, i);
+ particleCount = particles.size ();
+ i--;
+ continue;
+ }
+ privateInfo.aabb.Encapsulate (position);
+ }
+ }
+
+ #endif // ENABLE_PHYSICS
+ }
+ }
+ else
+ {
+ Matrix4x4f localToWorld = GetComponent (Transform).GetLocalToWorldMatrixNoScale ();
+ for (int i=0;i<particleCount;i++)
+ {
+ Vector3f& position = particles[i].position;
+ Vector3f& velocity = particles[i].velocity;
+
+ float psize = 0.5f * particles[i].size;
+ float poffset = 0.51f * particles[i].size;
+
+ Vector3f worldSpaceDelta = localToWorld.MultiplyVector3 (velocity * deltaTime);
+ Vector3f worldPosition = localToWorld.MultiplyPoint3 (position);
+
+ Ray ray;
+ ray.SetOrigin (worldPosition - worldSpaceDelta);
+ float deltaLength = Magnitude (worldSpaceDelta);
+ if (deltaLength < Vector3f::epsilon)
+ continue;
+
+ ray.SetDirection (worldSpaceDelta / deltaLength);
+
+ float checkLen = deltaLength + psize;
+ float t = checkLen;
+
+ #if ENABLE_PHYSICS
+ HitInfo hit;
+ IRaycast *raycast = GetRaycastInterface();
+ if(raycast)
+ {
+ if(raycast->Raycast(ray, t, m_CollidesWith.m_Bits, hit))
+ {
+ Particle& particle = particles[i];
+
+ // Reflect velocity and apply velocity * time left for particle to position
+ Vector3f worldVelocity = localToWorld.MultiplyVector3 (velocity);
+ // Test and factor changes into other particle collider
+ worldVelocity = ReflectVector (m_BounceFactor * worldVelocity, hit.normal);
+ float fractionLeftForReflection = (checkLen - t) / deltaLength;
+
+ // Place particle slightly above the surface. Otherwise in next frame raycast can
+ // detect collision again and reflect particle back!
+ worldPosition = hit.intersection + hit.normal * poffset + worldVelocity * (deltaTime * fractionLeftForReflection);
+
+ position = localToWorld.InverseMultiplyPoint3Affine (worldPosition);
+ velocity = localToWorld.InverseMultiplyVector3Affine (worldVelocity);
+
+ if (m_SendCollisionMessage)
+ {
+ AssertIf (hit.colliderInstanceID == 0);
+ PPtr<Component> collider_pptr(hit.colliderInstanceID);
+ Component* collider = collider_pptr;
+ SendMessage (kParticleCollisionEvent, &collider->GetGameObject (), ClassID (GameObject));
+ collider->SendMessage (kParticleCollisionEvent, &GetGameObject (), ClassID (GameObject));
+ }
+
+ // Update energy
+ particle.energy -= m_CollisionEnergyLoss;
+ float sqrVelocity = SqrMagnitude (velocity);
+ if (particle.energy <= 0.0f || sqrVelocity < sqrMinKillVelocity) {
+ // Kill particle (replace particle i with last, and continue updating the moved particle)
+ KillParticle (particles, i);
+ particleCount = particles.size ();
+ i--;
+ continue;
+ }
+
+ privateInfo.aabb.Encapsulate (position);
+ }
+ }
+ #endif // ENABLE_PHYSICS
+ }
+ }
+}
diff --git a/Runtime/Filters/Particles/WorldParticleCollider.h b/Runtime/Filters/Particles/WorldParticleCollider.h
new file mode 100644
index 0000000..f6db8af
--- /dev/null
+++ b/Runtime/Filters/Particles/WorldParticleCollider.h
@@ -0,0 +1,33 @@
+#ifndef WORLDPARTICLECOLLIDER_H
+#define WORLDPARTICLECOLLIDER_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "ParticleStruct.h"
+
+
+
+class WorldParticleCollider : public Unity::Component
+{
+public:
+ REGISTER_DERIVED_CLASS (WorldParticleCollider, Unity::Component)
+ DECLARE_OBJECT_SERIALIZE (WorldParticleCollider)
+
+ WorldParticleCollider (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void Reset ();
+
+ void UpdateParticleCollider( ParticleArray& particles, PrivateParticleInfo& privateInfo, float deltaTime );
+
+private:
+ /// The velocity at which a particle is killed after the bounced velocity is calculated
+ float m_MinKillVelocity;
+ float m_BounceFactor;
+ /// Seconds of energy a particle loses when colliding
+ float m_CollisionEnergyLoss;
+ /// Collides the particles with every collider whose layerMask & m_CollidesWith != 0
+ BitField m_CollidesWith;
+ /// Should we send out a collision message for every particle that has collided?
+ bool m_SendCollisionMessage;
+};
+
+#endif
diff --git a/Runtime/Filters/Pipeline.cpp b/Runtime/Filters/Pipeline.cpp
new file mode 100644
index 0000000..cb554e6
--- /dev/null
+++ b/Runtime/Filters/Pipeline.cpp
@@ -0,0 +1,20 @@
+#include "UnityPrefix.h"
+#include "Pipeline.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+IMPLEMENT_CLASS (Pipeline)
+IMPLEMENT_OBJECT_SERIALIZE (Pipeline)
+
+Pipeline::Pipeline(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+Pipeline::~Pipeline ()
+{
+}
+
+template<class TransferFunction>
+void Pipeline::Transfer (TransferFunction& transfer) {
+ Super::Transfer (transfer);
+}
diff --git a/Runtime/Filters/Pipeline.h b/Runtime/Filters/Pipeline.h
new file mode 100644
index 0000000..a580c19
--- /dev/null
+++ b/Runtime/Filters/Pipeline.h
@@ -0,0 +1,16 @@
+#ifndef PIPELINE_H
+#define PIPELINE_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+
+
+class Pipeline : public Unity::Component {
+public:
+ REGISTER_DERIVED_CLASS (Pipeline, Component)
+ DECLARE_OBJECT_SERIALIZE (Pipeline)
+
+ Pipeline(MemLabelId label, ObjectCreationMode mode);
+
+};
+
+#endif
diff --git a/Runtime/Filters/Renderer.cpp b/Runtime/Filters/Renderer.cpp
new file mode 100644
index 0000000..33a0b5b
--- /dev/null
+++ b/Runtime/Filters/Renderer.cpp
@@ -0,0 +1,663 @@
+#include "UnityPrefix.h"
+#include "Renderer.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Shaders/MaterialProperties.h"
+#include "Runtime/Camera/UnityScene.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Camera/Culler.h"
+#include "Runtime/BaseClasses/SupportedMessageOptimization.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Camera/LODGroup.h"
+#include "Runtime/BaseClasses/EventIDs.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Modules/ExportModules.h"
+#include "RendererAnimationBinding.h"
+
+using namespace Unity;
+
+IMPLEMENT_CLASS_HAS_POSTINIT (Renderer)
+IMPLEMENT_OBJECT_SERIALIZE (Renderer)
+INSTANTIATE_TEMPLATE_TRANSFER_EXPORTED (Renderer)
+
+typedef List< ListNode<Renderer> > RendererList;
+static RendererList gRenderersToUpdate;
+
+
+static Transform *gIdentityTransform = NULL;
+
+Transform* GetIdentityTransform() { return gIdentityTransform; }
+
+Renderer::Renderer (RendererType type, MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, BaseRenderer(type)
+, m_LODGroup (NULL)
+, m_SceneHandle(kInvalidSceneHandle)
+, m_RenderersListNode(this)
+, m_Enabled(true)
+, m_Visible(true)
+//, m_Materials (Renderer::MaterialArray::allocator_type (*baseAllocator))
+#if UNITY_EDITOR
+, m_SelectedWireframeHidden(false)
+#endif
+, m_UseLightProbes(false)
+, m_LastLightProbeTetIndex(-1)
+, m_SortingLayer(0)
+, m_SortingOrder(0)
+#if UNITY_EDITOR
+, m_SortingLayerID(0)
+#endif
+{
+
+}
+
+void Renderer::SmartReset ()
+{
+ Super::SmartReset();
+ SetMaterialCount (1);
+}
+
+Renderer::~Renderer ()
+{
+ DebugAssert (!IsInScene());
+ delete m_CustomProperties;
+
+ if (m_LODGroup)
+ m_LODGroup->RemoveFromCachedRenderers(this);
+}
+
+void Renderer::CleanupClass()
+{
+ Assert(gIdentityTransform != NULL);
+ gIdentityTransform = NULL;
+
+ CleanupRendererAnimationBindingInterface ();
+}
+
+void Renderer::InitializeClass()
+{
+ REGISTER_MESSAGE (Renderer, kTransformChanged, TransformChanged, int);
+ REGISTER_MESSAGE_VOID (Renderer, kLayerChanged, LayerChanged);
+
+ InitializeRendererAnimationBindingInterface ();
+}
+
+void Renderer::PostInitializeClass()
+{
+ Assert(gIdentityTransform == NULL);
+
+ GameObject* go = CreateObjectFromCode<GameObject> ();
+ gIdentityTransform = CreateObjectFromCode<Transform> ();
+ GameObject::AddComponentInternal(*go, *gIdentityTransform);
+ go->SetHideFlags(kHideAndDontSave);
+ Assert(!go->IsActive());
+}
+
+bool Renderer::IsVisibleInScene () const
+{
+ Assert ((m_IsVisibleInScene && IsInScene()) == m_IsVisibleInScene);
+ return m_IsVisibleInScene;
+}
+
+void Renderer::RendererBecameVisible()
+{
+ BaseRenderer::RendererBecameVisible ();
+
+ InvokeEvent (kBecameVisibleEvent);
+
+ SendMessage (kBecameVisible);
+}
+
+void Renderer::RendererBecameInvisible()
+{
+ BaseRenderer::RendererBecameInvisible ();
+
+ SendMessage (kBecameInvisible);
+
+ InvokeEvent (kBecameInvisibleEvent);
+}
+
+int Renderer::GetLayer() const
+{
+ return GetGameObject().GetLayer();
+}
+
+
+void Renderer::SetVisible (bool visible)
+{
+ m_Visible = visible;
+
+ bool shouldBeInScene = ShouldBeInScene();
+ if (shouldBeInScene == IsInScene())
+ return;
+
+ if (!shouldBeInScene)
+ {
+ // Remove from scene immediately
+ RemoveFromScene ();
+ UpdateManagerState( false );
+
+ InvokeEvent(kBecameInvisibleEvent);
+ }
+ else
+ {
+ // Add to scene in renderers update
+ UpdateManagerState( true );
+ }
+}
+
+void Renderer::BoundsChanged ()
+{
+ m_BoundsDirty = true;
+ if (IsInScene())
+ GetScene().SetDirtyAABB(m_SceneHandle);
+}
+
+void Renderer::LayerMaskChanged ()
+{
+ if (IsInScene())
+ GetScene().SetRendererLayer(m_SceneHandle, GetLayer());
+}
+
+void Renderer::UpdateManagerState( bool needsUpdate )
+{
+ if( needsUpdate == m_RenderersListNode.IsInList() )
+ return;
+ if( needsUpdate )
+ gRenderersToUpdate.push_front(m_RenderersListNode);
+ else
+ m_RenderersListNode.RemoveFromList();
+}
+
+void Renderer::UpdateAllRenderersInternal()
+{
+ // Update the renderers from the update list:
+ // - before updating each, remove from the list
+ // - a renderer can add itself again, so only process the original list length
+ RendererList::iterator next, listEnd = gRenderersToUpdate.end();
+ for( RendererList::iterator i = gRenderersToUpdate.begin(); i != listEnd; i = next )
+ {
+ next = i;
+ next++;
+ Renderer& renderer = **i;
+ renderer.m_RenderersListNode.RemoveFromList();
+ renderer.UpdateRenderer();
+ }
+}
+
+void Renderer::NotifySceneHandleChange (SceneHandle handle)
+{
+ m_SceneHandle = handle;
+}
+
+void Renderer::UpdateLODGroup ()
+{
+ if (!IsInScene())
+ return;
+ Unity::Scene& scene = GetScene();
+ DebugAssert (scene.GetRendererNode(m_SceneHandle).renderer == this);
+
+ UInt32 lodGroup = 0;
+ UInt32 lodIndexMask = 0;
+ if (m_LODGroup != NULL)
+ {
+ m_LODGroup->GetLODGroupIndexAndMask(this, &lodGroup, &lodIndexMask);
+ }
+ scene.SetRendererLODGroup(m_SceneHandle, lodGroup);
+ scene.SetRendererLODIndexMask(m_SceneHandle, lodIndexMask);
+}
+
+void Renderer::UpdateSceneHandle ()
+{
+ if (!IsInScene())
+ return;
+ Unity::Scene& scene = GetScene();
+ DebugAssert (scene.GetRendererNode(m_SceneHandle).renderer == this);
+
+ AABB worldAABB;
+ GetWorldAABB (worldAABB);
+ scene.SetRendererAABB(m_SceneHandle, worldAABB);
+
+ bool needsCullCallback = (GetGameObject().GetSupportedMessages() & kHasOnWillRenderObject) != 0;
+ scene.SetRendererNeedsCullCallback(m_SceneHandle, needsCullCallback);
+ scene.SetRendererLayer(m_SceneHandle, GetLayer());
+
+ UpdateLODGroup();
+}
+
+void Renderer::UpdateTransformInfo ()
+{
+ Transform const& transform = GetTransform();
+ if(m_TransformDirty)
+ {
+ m_TransformInfo.invScale = 1.0f;
+ // will return a cached matrix most of the time
+ m_TransformInfo.transformType = transform.CalculateTransformMatrix (m_TransformInfo.worldMatrix);
+ }
+
+ if(m_BoundsDirty)
+ UpdateLocalAABB ();
+
+ TransformAABB( m_TransformInfo.localAABB, m_TransformInfo.worldMatrix, m_TransformInfo.worldAABB );
+
+ if (IsNoScaleTransform(m_TransformInfo.transformType))
+ return;
+
+ // run slow path for non uniform scale
+ Matrix4x4f scaleOnly;
+ float scale;
+ TransformType type = transform.CalculateTransformMatrixDisableNonUniformScale (m_TransformInfo.worldMatrix, scaleOnly, scale);
+ Assert (type == m_TransformInfo.transformType);
+
+ m_TransformInfo.invScale = 1.0F / scale;
+
+ if (IsNonUniformScaleTransform(type))
+ {
+ // must recalculate this since it is changed with the following transform
+ UpdateLocalAABB ();
+ TransformAABB (m_TransformInfo.localAABB, scaleOnly, m_TransformInfo.localAABB);
+ }
+}
+
+void Renderer::UpdateRenderer ()
+{
+ if (ShouldBeInScene ())
+ {
+ if (!IsInScene())
+ {
+ m_SceneHandle = GetScene().AddRenderer (this);
+ }
+ Assert (m_SceneHandle != kInvalidSceneHandle);
+ UpdateSceneHandle ();
+ }
+ else
+ {
+ // This should not be necessary but fixes a weird bug where a mesh is
+ // being leaked when disabled before loading a scene. Happens in the fps tutorial.
+ RemoveFromScene ();
+ }
+}
+
+void Renderer::SetPropertyBlock (const MaterialPropertyBlock& block)
+{
+ delete m_CustomProperties;
+ m_CustomProperties = new MaterialPropertyBlock (block);
+ ComputeCustomPropertiesHash();
+}
+
+void Renderer::GetPropertyBlock (MaterialPropertyBlock& outBlock)
+{
+ if (!m_CustomProperties)
+ {
+ outBlock.Clear();
+ return;
+ }
+ outBlock = *m_CustomProperties;
+}
+
+void Renderer::ClearPropertyBlock ()
+{
+ delete m_CustomProperties;
+ m_CustomProperties = NULL;
+ ComputeCustomPropertiesHash();
+}
+
+MaterialPropertyBlock& Renderer::GetPropertyBlockRememberToUpdateHash ()
+{
+ if (!m_CustomProperties)
+ m_CustomProperties = new MaterialPropertyBlock ();
+ return *m_CustomProperties;
+}
+
+
+void Renderer::SetEnabled (bool newEnabled)
+{
+ m_Enabled = newEnabled;
+ SetDirty();
+ SetVisible(m_Visible);
+}
+
+void Renderer::Deactivate (DeactivateOperation operation)
+{
+ RemoveFromScene ();
+ UpdateManagerState( false );
+ Super::Deactivate (operation);
+}
+
+/// Set how many materials this renderer uses
+/// @param size the number of materials.
+void Renderer::SetMaterialCount (int size)
+{
+ const size_t oldSize = m_Materials.size ();
+
+ if (size != (int)oldSize)
+ {
+ Assert(m_SubsetIndices.empty() || m_SubsetIndices.size() == m_Materials.size());
+
+ resize_trimmed (m_Materials, size);
+ HealSubsetIndices();
+
+ SetDirty ();
+ BoundsChanged();
+ }
+}
+
+void Renderer::HealSubsetIndices()
+{
+ // We are using batching and our subset indices have gone out of sync
+ if (!m_SubsetIndices.empty() && m_SubsetIndices.size() != m_Materials.size())
+ {
+ int oldSize = m_SubsetIndices.size();
+ resize_trimmed (m_SubsetIndices, m_Materials.size());
+ // All new subset indices get the
+ for (size_t q = oldSize; q < m_SubsetIndices.size(); ++q)
+ m_SubsetIndices[q] = (UInt32)q;
+ BoundsChanged();
+ }
+}
+
+void Renderer::RemoveFromScene ()
+{
+ if (!IsInScene())
+ return;
+
+ bool wasVisible = IsVisibleInScene ();
+
+ BaseRenderer* remRenderer = GetScene ().RemoveRenderer (m_SceneHandle);
+ Assert(remRenderer == this);
+
+ m_SceneHandle = kInvalidSceneHandle;
+
+ if (wasVisible)
+ RendererBecameInvisible ();
+}
+
+void Renderer::CheckConsistency()
+{
+ Super::CheckConsistency();
+
+ HealSubsetIndices();
+}
+
+void Renderer::ClearSubsetIndices()
+{
+ m_SubsetIndices.clear();
+ SetDirty ();
+ BoundsChanged();
+}
+
+void Renderer::SetSubsetIndex (int index, int subsetIndex)
+{
+ if (m_SubsetIndices.empty())
+ {
+ resize_trimmed (m_SubsetIndices, m_Materials.size());
+ for (size_t q = 0; q < m_Materials.size(); ++q)
+ m_SubsetIndices[q] = (UInt32)q;
+ }
+
+ Assert(index < m_SubsetIndices.size());
+ Assert (m_Materials.size() == m_SubsetIndices.size());
+ m_SubsetIndices[index] = subsetIndex;
+ SetDirty ();
+ BoundsChanged();
+}
+
+/// Set a given Material.
+/// @param material the material to assign
+/// @param index the index to assign the material to.
+void Renderer::SetMaterial (PPtr<Material> material, int index)
+{
+ Assert (index < m_Materials.size());
+ m_Materials[index] = material;
+
+ /*
+ #if !DEPLOY_OPTIMIZED
+ Material* materialPtr = material;
+ if (materialPtr && materialPtr->GetOwner ().GetInstanceID () != 0 && materialPtr->GetOwner() != PPtr<Object> (this))
+ {
+ ErrorString("Assigning an instantiated material is not a good idea. Since the material is owned by another game object, it will be destroyed when the game object is destroyed.\nYou probably want to explicitly instantiate the material.");
+ }
+ #endif
+ */
+
+ SetDirty ();
+}
+
+void Renderer::SetMaterialArray( const MaterialArray& m, const IndexArray& i )
+{
+ m_Materials = m;
+ m_SubsetIndices = i;
+}
+
+Material* Renderer::GetAndAssignInstantiatedMaterial(int i, bool allowFromEditMode)
+{
+ // Grab shared material
+ Material* material = NULL;
+ if (GetMaterialCount () > i)
+ material = GetMaterial (i);
+
+ // instantiate material if necessary
+ Material* instantiated = &Material::GetInstantiatedMaterial (material, *this, allowFromEditMode);
+
+ // Assign material
+ if (material != instantiated)
+ {
+ SetMaterialCount (std::max(GetMaterialCount (), i + 1));
+ SetMaterial (instantiated, i);
+ }
+
+ return instantiated;
+}
+
+Transform& Renderer::GetTransform()
+{
+ if (!IsPartOfStaticBatch())
+ return GetComponent(Transform);
+
+ if (!m_StaticBatchRoot.IsNull())
+ return *m_StaticBatchRoot;
+
+ return *gIdentityTransform;
+}
+
+Transform const& Renderer::GetTransform() const
+{
+ return const_cast<Renderer*>(this)->GetTransform();
+}
+
+Matrix4x4f Renderer::GetWorldToLocalMatrix () const
+{
+ return GetTransform().GetWorldToLocalMatrix();
+}
+
+Matrix4x4f Renderer::GetLocalToWorldMatrix () const
+{
+ return GetTransform().GetLocalToWorldMatrix();
+}
+
+
+// Receiver for the TransformChanged message.
+// Updates the scene info with the new bounds.
+void Renderer::TransformChanged (int changeMask)
+{
+ m_TransformDirty = true;
+ BoundsChanged ();
+}
+
+// Receiver for the LayerChanged message.
+// Updates the scene info with the new layerMask.
+void Renderer::LayerChanged ()
+{
+ LayerMaskChanged ();
+}
+
+void Renderer::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ // Materials might have been changed eg. from the property editor
+ // When loading from disk the materials are only being restored not changed
+ if ((awakeMode & kDidLoadFromDisk) == 0)
+ {
+ // m_Enabled might have changed - update visibility status
+ SetVisible(m_Visible);
+ }
+
+ UpdateManagerState( IsActive() );
+ SetupSortingOverride();
+}
+
+void Renderer::SupportedMessagesDidChange (int mask)
+{
+ Super::SupportedMessagesDidChange(mask);
+
+ if (IsInScene())
+ {
+ bool needsCullCallback = (GetGameObject().GetSupportedMessages() & kHasOnWillRenderObject) != 0;
+ GetScene().SetRendererNeedsCullCallback(m_SceneHandle, needsCullCallback);
+ }
+}
+
+void Renderer::SetLightmapIndexInt(int index)
+{
+ UInt8 oldIndex = m_LightmapIndex;
+ SetLightmapIndexIntNoDirty (index);
+ if (oldIndex != m_LightmapIndex)
+ SetDirty();
+}
+
+void Renderer::SetLightmapST( const Vector4f& st )
+{
+ if (st != m_LightmapST)
+ {
+ m_LightmapST = st;
+ SetDirty();
+ }
+}
+
+
+#if UNITY_EDITOR
+bool Renderer::CanSelectedWireframeBeRendered () const
+{
+ return m_Enabled && m_Visible && IsInScene() && !m_SelectedWireframeHidden;
+}
+#endif
+
+template<class TransferFunction>
+void Renderer::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.Transfer(m_Enabled, "m_Enabled", kHideInEditorMask);
+ transfer.Transfer(m_CastShadows, "m_CastShadows");
+ transfer.Transfer(m_ReceiveShadows, "m_ReceiveShadows");
+ transfer.Transfer(m_LightmapIndex, "m_LightmapIndex", kHideInEditorMask | kDontAnimate);
+ transfer.Transfer(m_LightmapST, "m_LightmapTilingOffset", kHideInEditorMask | kDontAnimate);
+ transfer.Transfer (m_Materials, "m_Materials");
+ transfer.Transfer (m_SubsetIndices, "m_SubsetIndices", kHideInEditorMask);
+ transfer.Transfer (m_StaticBatchRoot, "m_StaticBatchRoot", kHideInEditorMask);
+ TRANSFER (m_UseLightProbes);
+ transfer.Align();
+ TRANSFER (m_LightProbeAnchor);
+ #if UNITY_EDITOR
+ if (!transfer.IsSerializingForGameRelease())
+ transfer.Transfer (m_ScaleInLightmap, "m_ScaleInLightmap", kHideInEditorMask | kDontAnimate);
+ #endif
+ transfer.Align();
+
+ transfer.Transfer (m_SortingLayer, "m_SortingLayer", kHideInEditorMask);
+ transfer.Transfer (m_SortingOrder, "m_SortingOrder", kHideInEditorMask);
+ // In the editor, we also transfer global sorting layer ID, so we can derive final sorting value
+ // in case layers are reordered / etc.
+ TRANSFER_EDITOR_ONLY_HIDDEN(m_SortingLayerID);
+}
+
+Vector3f Renderer::GetLightProbeInterpolationPosition (const AABB& worldBounds)
+{
+ Transform* anchor = m_LightProbeAnchor;
+ if (anchor != NULL)
+ return anchor->GetPosition();
+
+ return worldBounds.GetCenter();
+}
+
+
+Vector3f Renderer::GetLightProbeInterpolationPosition ()
+{
+ Transform* anchor = m_LightProbeAnchor;
+ if (anchor != NULL)
+ return anchor->GetPosition();
+
+ AABB aabb;
+ GetWorldAABB(aabb);
+ if (aabb.IsValid())
+ return aabb.GetCenter();
+
+ return Vector3f(0,0,0);
+}
+
+
+
+int Renderer::GetSortingLayerUserID() const
+{
+ if (m_SortingLayer == 0)
+ return 0;
+ return ::GetSortingLayerUserIDFromValue(m_SortingLayer);
+}
+
+void Renderer::SetSortingLayerUserID(int id)
+{
+ int layerValue = GetSortingLayerValueFromUserID(id);
+ if (m_SortingLayer == layerValue)
+ return;
+
+# if UNITY_EDITOR
+ if (layerValue == 0)
+ m_SortingLayerID = 0;
+ else
+ m_SortingLayerID = ::GetSortingLayerUniqueIDFromValue(layerValue);
+# endif
+
+ m_SortingLayer = layerValue;
+ SetupSortingOverride();
+ SetDirty();
+}
+
+std::string Renderer::GetSortingLayerName() const
+{
+ if (m_SortingLayer == 0)
+ return std::string();
+ return ::GetSortingLayerNameFromValue(m_SortingLayer);
+}
+
+void Renderer::SetSortingLayerName(const std::string& name)
+{
+ int layerValue = ::GetSortingLayerValueFromName(name);
+ int layerUserID = ::GetSortingLayerUserIDFromValue(layerValue);
+ SetSortingLayerUserID (layerUserID);
+}
+
+
+void Renderer::SetSortingOrder(SInt16 newValue)
+{
+ if (m_SortingOrder == newValue)
+ return;
+
+ m_SortingOrder = newValue;
+ SetupSortingOverride();
+}
+
+void Renderer::SetupSortingOverride()
+{
+ // In editor, make sure our final layer sorting value is up to date
+ // (might have changed behind our backs by global layer reordering).
+# if UNITY_EDITOR
+ m_SortingLayer = GetSortingLayerValueFromUniqueID(m_SortingLayerID);
+# endif // if UNITY_EDITOR
+
+ GlobalLayeringData gld = GlobalLayeringDataCleared();
+ gld.layer = m_SortingLayer;
+ gld.order = m_SortingOrder;
+ SetGlobalLayeringData(gld);
+}
diff --git a/Runtime/Filters/Renderer.h b/Runtime/Filters/Renderer.h
new file mode 100644
index 0000000..69bd87a
--- /dev/null
+++ b/Runtime/Filters/Renderer.h
@@ -0,0 +1,234 @@
+#ifndef RENDERER_H
+#define RENDERER_H
+
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Camera/BaseRenderer.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include <vector>
+#include "Runtime/Modules/ExportModules.h"
+#include "Runtime/Camera/SceneNode.h"
+
+class Animation;
+class LODGroup;
+class Renderer;
+struct VisibleNode;
+
+struct RenderStats
+{
+ int triangleCount;
+ int vertexCount;
+ int submeshCount;
+};
+
+struct EventEntry;
+
+enum {
+ // @TBD: tweak to ideal values per platform / device / rendering API
+ kDynamicBatchingVerticesThreshold = 300, // verts (needed at build time since required channels isn't known)
+ kDynamicBatchingVertsByChannelThreshold = 300 * 3, // verts * channels
+ kDynamicBatchingIndicesThreshold = 32000 // >32k causes a slowdown on MBPs with AMD cards (Case 394520)
+};
+
+
+class EXPORT_COREMODULE Renderer : public Unity::Component, public BaseRenderer
+{
+public:
+
+ typedef UNITY_VECTOR(kMemRenderer, PPtr<Material>) MaterialArray;
+ typedef UNITY_VECTOR(kMemRenderer, UInt32) IndexArray;
+
+ REGISTER_DERIVED_ABSTRACT_CLASS (Renderer, Unity::Component)
+ DECLARE_OBJECT_SERIALIZE (Renderer)
+
+ static void InitializeClass ();
+ static void PostInitializeClass ();
+ static void CleanupClass ();
+
+ Renderer (RendererType type, MemLabelId label, ObjectCreationMode mode);
+ // ~Renderer (); declared-in-macro
+
+ virtual void SmartReset ();
+ virtual void CheckConsistency();
+
+ // BaseRenderer
+ virtual void RendererBecameVisible();
+ virtual void RendererBecameInvisible();
+ virtual int GetLayer() const;
+ virtual void UpdateTransformInfo();
+
+ // UpdateLocalAABB only needed for derived classes that don't implement UpdateTransformInfo
+ virtual void UpdateLocalAABB() {Assert(false);}
+
+ virtual int GetMaterialCount() const { return m_Materials.size (); }
+ virtual PPtr<Material> GetMaterial(int i) const { return m_Materials[i]; }
+ Material* GetAndAssignInstantiatedMaterial(int i, bool allowFromEditMode);
+
+ virtual int GetSubsetIndex(int i) const { return (m_SubsetIndices.empty())? i: m_SubsetIndices[i]; }
+ virtual void SetSubsetIndex(int subsetIndex, int index);
+
+ // Set the visibility of a renderer.
+ // If false, the renderer doesn't register itself in the scene, and never get any render calls.
+ void SetVisible (bool isVisible);
+ bool GetVisible () const { return m_Visible; }
+
+ bool GetEnabled () const { return m_Enabled; }
+ void SetEnabled( bool v );
+
+ void SetMaterialCount (int size);
+ void SetMaterial (PPtr<Material> material, int index);
+ void ClearSubsetIndices();
+
+ PPtr<Transform> GetStaticBatchRoot() const { return m_StaticBatchRoot; }
+ void SetStaticBatchRoot (PPtr<Transform> root) { m_StaticBatchRoot = root; }
+
+ // Due to batching GameObject and attached Renderer component transforms can differ
+ // User following methods to access Renderer transforms or matrix
+ Transform const& GetTransform () const;
+ Transform& GetTransform ();
+ Matrix4x4f GetWorldToLocalMatrix () const;
+ Matrix4x4f GetLocalToWorldMatrix () const;
+
+ // from Component
+ virtual void Deactivate (DeactivateOperation operation);
+
+ void TransformChanged (int changeMask);
+ void LayerChanged ();
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ void SetLightmapIndexInt(int index);
+
+ void SetLightmapST( const Vector4f& st );
+
+ void SetCastShadows( bool f ) { m_CastShadows = f; SetDirty(); }
+ void SetReceiveShadows( bool f ) { m_ReceiveShadows = f; SetDirty(); }
+
+ void SetMaterialArray( const MaterialArray& m, const IndexArray& i );
+ const MaterialArray& GetMaterialArray() { return m_Materials; }
+ const IndexArray& GetSubsetIndices() { return m_SubsetIndices; }
+
+ static void UpdateAllRenderersInternal();
+
+ #if UNITY_EDITOR
+ bool CanSelectedWireframeBeRendered () const;
+ void SetSelectedWireframeHidden (bool isHidden) { m_SelectedWireframeHidden = isHidden; };
+ virtual void GetRenderStats (RenderStats& renderStats) { memset(&renderStats, 0, sizeof(renderStats)); }
+ #endif
+
+ bool GetUseLightProbes() const { return m_UseLightProbes; }
+ void SetUseLightProbes(bool useLightProbes) { m_UseLightProbes = useLightProbes; SetDirty(); }
+ int GetLastLightProbeTetIndex() const { return m_LastLightProbeTetIndex; }
+ void SetLastLightProbeTetIndex(int index) { m_LastLightProbeTetIndex = index; }
+ PPtr<Transform> GetLightProbeAnchor() const { return m_LightProbeAnchor; }
+ void SetLightProbeAnchor (PPtr<Transform> anchor) { m_LightProbeAnchor = anchor; SetDirty(); }
+
+ // Tries to use the light probe anchor, or the RendererNode's AABB center or the WorldAABB's center;
+ // likes to get the RendererNode, as that's the fastest
+ Vector3f GetLightProbeInterpolationPosition ();
+ Vector3f GetLightProbeInterpolationPosition (const AABB& worldBounds);
+
+
+ void SetLODGroup(LODGroup* ptr) { m_LODGroup = ptr; }
+ LODGroup* GetLODGroup() { return m_LODGroup; }
+
+ // A callback from the scene to notify
+ // that the pointer to the cullnode associated with the renderer has changed
+ void NotifySceneHandleChange (SceneHandle cullNode);
+
+ SceneHandle GetSceneHandle() { return m_SceneHandle; }
+
+ bool IsInScene() const { return m_SceneHandle != kInvalidSceneHandle; }
+
+ bool IsVisibleInScene () const;
+
+ void UpdateLODGroup ();
+
+ const MaterialPropertyBlock* GetPropertyBlock () const { return m_CustomProperties; }
+ void GetPropertyBlock (MaterialPropertyBlock& outBlock);
+ void SetPropertyBlock (const MaterialPropertyBlock& block);
+
+ bool HasPropertyBlock() const { return m_CustomProperties != NULL; }
+ void ClearPropertyBlock ();
+ MaterialPropertyBlock& GetPropertyBlockRememberToUpdateHash ();
+
+ int GetSortingLayer() const { return m_SortingLayer; }
+ int GetSortingLayerUserID() const;
+ void SetSortingLayerUserID(int id);
+ std::string GetSortingLayerName() const;
+ void SetSortingLayerName(const std::string& name);
+ SInt16 GetSortingOrder() const { return m_SortingOrder; }
+ void SetSortingOrder(SInt16 newValue);
+ void SetupSortingOverride();
+
+protected:
+
+ void UpdateSceneHandle ();
+
+ // Update the scene info for the renderer.
+ // Registers/unregisters the renderer in the scene.
+ // Override in child classes to do custom stuff.
+ virtual void UpdateRenderer();
+
+ void HealSubsetIndices();
+
+ // Should this renderer be in the scene?
+ bool ShouldBeInScene() const { return m_Enabled && m_Visible && IsActive (); }
+
+ void SupportedMessagesDidChange (int mask);
+
+ void UpdateManagerState( bool needsUpdate );
+
+ // Inform a renderer that the aabb has changed.
+ void BoundsChanged ();
+
+ // Inform a renderer that the layerMask has changed.
+ void LayerMaskChanged ();
+
+ // Check if material indices are equal to subset indices
+ void ValidateSubsetIndexArray ();
+
+ bool HasSubsetIndices() const { return !m_SubsetIndices.empty(); }
+ bool IsPartOfStaticBatch() const { return HasSubsetIndices(); }
+
+ void RemoveFromScene();
+
+protected:
+ SceneHandle m_SceneHandle;
+ ListNode<Renderer> m_RenderersListNode;
+ MaterialArray m_Materials; ///< List of materials to use when rendering.
+
+private:
+ IndexArray m_SubsetIndices;
+ PPtr<Transform> m_StaticBatchRoot;
+
+ LODGroup* m_LODGroup;
+
+ bool m_Enabled;
+ bool m_Visible;
+
+ bool m_UseLightProbes;
+ PPtr<Transform> m_LightProbeAnchor; ///< Light probe lighting is interpolated at the center of the renderer's bounds or at the position of the anchor, if assigned.
+ int m_LastLightProbeTetIndex; // Last light probe tetrahedron this renderer was in, used as a guess for the next frame; don't serialize.
+
+ // Global sorting layer index. Zero is "Default" layer which is always there.
+ // Layers before the default one get negative numbers; after default one
+ // get positive ones.
+ //
+ // When the layers are reordered in the inspector, all alive renderers
+ // get this updated. Otherwise, at load time the proper sorting layer
+ // is fetched from m_SortingLayerID (each layer gets an unique one).
+ SInt16 m_SortingLayer;
+ SInt16 m_SortingOrder;
+
+# if UNITY_EDITOR
+ // Unique ID of our sorting layer, or zero for default one. Needed to resolve
+ // proper final m_SortingLayer value, when the layers were reordered.
+ UInt32 m_SortingLayerID;
+ bool m_SelectedWireframeHidden;
+# endif // if UNITY_EDITOR
+};
+
+Transform* GetIdentityTransform();
+
+#endif
diff --git a/Runtime/Filters/RendererAnimationBinding.cpp b/Runtime/Filters/RendererAnimationBinding.cpp
new file mode 100644
index 0000000..fef77e7
--- /dev/null
+++ b/Runtime/Filters/RendererAnimationBinding.cpp
@@ -0,0 +1,407 @@
+#include "UnityPrefix.h"
+#include "Runtime/Animation/GenericAnimationBindingCache.h"
+#include "Runtime/Animation/AnimationClipBindings.h"
+#include "Renderer.h"
+#include "External/shaderlab/Library/FastPropertyName.h"
+#include "External/shaderlab/Library/properties.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Shaders/MaterialProperties.h"
+#include "Runtime/Interfaces/IAnimationBinding.h"
+
+#define MATERIAL_ANIMATION 1
+
+class RendererAnimationBinding : public IAnimationBinding
+{
+#if UNITY_EDITOR
+ virtual void GetAllAnimatableProperties (Object& targetObject, std::vector<EditorCurveBinding>& outProperties) const
+ {
+ Renderer& renderer = static_cast<Renderer&> (targetObject);
+
+ for (int i=0;i<renderer.GetMaterialCount();i++)
+ AddPPtrBinding (outProperties, targetObject.GetClassID(), Format("m_Materials.Array.data[%d]", i));
+ }
+#endif
+
+ virtual float GetFloatValue (const UnityEngine::Animation::BoundCurve& bind) const
+ {
+ AssertString("unsupported"); return 0.0F;
+ }
+
+ virtual void SetFloatValue (const UnityEngine::Animation::BoundCurve& bound, float value) const
+ {
+ AssertString("unsupported");
+ }
+
+ virtual void SetPPtrValue (const UnityEngine::Animation::BoundCurve& bound, SInt32 value) const
+ {
+ Renderer& renderer = *static_cast<Renderer*> (bound.targetObject);
+ int index = reinterpret_cast<int> (bound.targetPtr);
+
+ if (index < renderer.GetMaterialCount ())
+ renderer.SetMaterial(PPtr<Material> (value), index);
+ }
+
+ virtual SInt32 GetPPtrValue (const UnityEngine::Animation::BoundCurve& bound) const
+ {
+ Renderer& renderer = *static_cast<Renderer*> (bound.targetObject);
+ int index = reinterpret_cast<int> (bound.targetPtr);
+
+ if (index < renderer.GetMaterialCount ())
+ return renderer.GetMaterial(index).GetInstanceID();
+ else
+ return 0;
+ }
+
+ virtual bool GenerateBinding (const UnityStr& attribute, bool pptrCurve, UnityEngine::Animation::GenericBinding& outputBinding) const
+ {
+ int index = ParseIndexAttributeIndex (attribute, "m_Materials.Array.data[");
+ if (index != -1 && pptrCurve)
+ {
+ outputBinding.attribute = index;
+ return true;
+ }
+
+ return false;
+ }
+
+ virtual ClassIDType BindValue (Object& target, const UnityEngine::Animation::GenericBinding& outputBinding, UnityEngine::Animation::BoundCurve& bound) const
+ {
+ bound.targetPtr = reinterpret_cast<void*> (outputBinding.attribute);
+ return ClassID(Material);
+ }
+};
+
+#if MATERIAL_ANIMATION
+
+const char* kMaterialPrefix = "material.";
+class RendererMaterialAnimationBinding : public IAnimationBinding
+{
+public:
+
+#if UNITY_EDITOR
+ virtual void GetAllAnimatableProperties (Object& targetObject, std::vector<EditorCurveBinding>& outProperties) const
+ {
+ Renderer& renderer = static_cast<Renderer&> (targetObject);
+ if (renderer.GetMaterialCount() == 0)
+ return;
+
+ int startIndex = outProperties.size();
+ for (int i=0;i<renderer.GetMaterialCount();i++)
+ {
+ Material* material = renderer.GetMaterial(i);
+ if (material == NULL)
+ continue;
+
+ ExtractAllMaterialAnimatableAttributes (*material, targetObject.GetClassID(), outProperties, startIndex);
+ }
+ }
+
+ static void ExtractAllMaterialAnimatableAttributes (Material& targetObject, int classID, std::vector<EditorCurveBinding>& outProperties, int startIndex)
+ {
+ Material& material = static_cast<Material&> (targetObject);
+
+ const ShaderLab::PropertySheet& properties = material.GetProperties();
+
+ const string materialPrefix = kMaterialPrefix;
+
+ // Get all float properties
+ const ShaderLab::PropertySheet::Floats& floats = properties.GetFloatsMap();
+ for (ShaderLab::PropertySheet::Floats::const_iterator i=floats.begin();i != floats.end();i++)
+ {
+ AddBindingCheckUnique (outProperties, startIndex, classID, materialPrefix + i->first.GetName());
+ }
+
+ // Get all vector properties
+ const ShaderLab::PropertySheet::Vectors& vectors = properties.GetVectorMap();
+ for (ShaderLab::PropertySheet::Vectors::const_iterator i=vectors.begin();i != vectors.end();i++)
+ {
+ string prefix = materialPrefix + i->first.GetName();
+ if (properties.GetColorTag(i->first))
+ {
+ AddBindingCheckUnique (outProperties, startIndex, classID, prefix + ".r");
+ AddBindingCheckUnique (outProperties, startIndex, classID, prefix + ".g");
+ AddBindingCheckUnique (outProperties, startIndex, classID, prefix + ".b");
+ AddBindingCheckUnique (outProperties, startIndex, classID, prefix + ".a");
+ }
+ else
+ {
+ AddBindingCheckUnique (outProperties, startIndex, classID, prefix + ".x");
+ AddBindingCheckUnique (outProperties, startIndex, classID, prefix + ".y");
+ AddBindingCheckUnique (outProperties, startIndex, classID, prefix + ".z");
+ AddBindingCheckUnique (outProperties, startIndex, classID, prefix + ".w");
+ }
+
+ }
+ }
+#endif
+
+ struct MaterialBinding
+ {
+ enum { kFloat4, kColor4, kFloat1 };
+
+ UInt32 propertyName : 28;
+ UInt32 colIndex : 2;
+ UInt32 type : 2;
+ };
+
+ static UInt32 BindingToUInt32 (MaterialBinding binding)
+ {
+ UInt32 data;
+ data = binding.propertyName;
+ data |= binding.colIndex << 28;
+ data |= binding.type << 30;
+ return data;
+ }
+
+ static MaterialBinding UInt32ToBinding (UInt32 data)
+ {
+ MaterialBinding binding;
+ binding.propertyName = data & 0x3FFFFFFF;
+ binding.colIndex = (data >> 28) & 0x3;
+ binding.type = (data >> 30) & 0x3;
+ return binding;
+ }
+
+ virtual float GetFloatValue (const UnityEngine::Animation::BoundCurve& bind) const
+ {
+ Renderer& renderer = *static_cast<Renderer*> (bind.targetObject);
+ MaterialBinding binding = *reinterpret_cast<const MaterialBinding*> (&bind.targetPtr);
+
+ ShaderLab::FastPropertyName name;
+ name.index = binding.propertyName;
+
+ const MaterialPropertyBlock* block = renderer.GetPropertyBlock();
+
+ // Extract from material property block
+ if (block)
+ {
+ if (binding.type == MaterialBinding::kFloat1)
+ {
+ const float* value = block->FindFloat (name);
+ if (value != NULL)
+ return *value;
+ }
+ else if (binding.type == MaterialBinding::kFloat4)
+ {
+ const Vector4f* value = block->FindVector (name);
+ if (value != NULL)
+ return value->GetPtr()[binding.colIndex];
+ }
+ else if (binding.type == MaterialBinding::kColor4)
+ {
+ ColorRGBAf color;
+ if (block->GetColor (name, color))
+ return color.GetPtr()[binding.colIndex];
+ }
+ }
+
+ // Extract from material
+ for (int i=0;i<renderer.GetMaterialCount ();i++)
+ {
+ Material* material = renderer.GetMaterial(0);
+ if (material == NULL)
+ continue;
+ if (!material->HasProperty (name))
+ continue;
+
+ if (binding.type == MaterialBinding::kFloat1)
+ return material->GetFloat(name);
+ else if (binding.type == MaterialBinding::kColor4)
+ return material->GetColor(name).GetPtr()[binding.colIndex];
+ else if (binding.type == MaterialBinding::kFloat4)
+ return material->GetColor(name).GetPtr()[binding.colIndex];
+ }
+
+ return 0.0F;
+ }
+
+ virtual void SetFloatValue (const UnityEngine::Animation::BoundCurve& bound, float value) const
+ {
+ Renderer& renderer = *static_cast<Renderer*> (bound.targetObject);
+ MaterialBinding binding = *reinterpret_cast<const MaterialBinding*> (&bound.targetPtr);
+
+ MaterialPropertyBlock& block = renderer.GetPropertyBlockRememberToUpdateHash();
+
+ ShaderLab::FastPropertyName name;
+ name.index = binding.propertyName;
+
+ if (binding.type == MaterialBinding::kFloat1)
+ {
+ block.ReplacePropertyFloat (name, value);
+ }
+ else if (binding.type == MaterialBinding::kFloat4)
+ {
+ block.ReplacePartialFloatProperty (name, value, 4, binding.colIndex);
+ }
+ else if (binding.type == MaterialBinding::kColor4)
+ {
+ block.ReplacePartialFloatColorProperty (name, value, 4, binding.colIndex);
+ }
+
+ renderer.ComputeCustomPropertiesHash();
+
+ // Force a repaint
+ #if UNITY_EDITOR
+ renderer.SetDirty();
+ #endif
+ }
+
+ virtual void SetPPtrValue (const UnityEngine::Animation::BoundCurve& bound, SInt32 value) const { }
+
+ virtual SInt32 GetPPtrValue (const UnityEngine::Animation::BoundCurve& bound) const { return 0; }
+
+ virtual bool GenerateBinding (const UnityStr& attributeStr, bool pptrCurve, UnityEngine::Animation::GenericBinding& outputBinding) const
+ {
+ if (pptrCurve)
+ return false;
+
+ if (!BeginsWith(attributeStr, kMaterialPrefix))
+ return false;
+
+ MaterialBinding binding;
+
+ const char* attribute = attributeStr.c_str() + strlen(kMaterialPrefix);
+ const char* a = attribute;
+
+ // Parse this:
+ // mainColor.r
+ // mainColor.x
+ // mainColor.y
+ // floatPropertyName
+
+ // Find shader propertyname
+ int dotIndex = -1;
+ const char* lastCharacter;
+ while (*a != 0)
+ {
+ if (*a == '.' && dotIndex == -1)
+ dotIndex = a - attribute;
+ a++;
+ }
+ lastCharacter = a - 1;
+
+ // No '.' found, thus it must be a float property
+ if (dotIndex == -1)
+ {
+ binding.propertyName = ShaderLab::GenerateFastPropertyName28BitHash(attribute);
+ binding.type = MaterialBinding::kFloat1;
+ binding.colIndex = 0;
+ }
+ // Calculate different property types
+ else
+ {
+ binding.propertyName = ShaderLab::GenerateFastPropertyName28BitHash(string(attribute, attribute + dotIndex).c_str());
+
+ // There must be exactly one property ('r', 'g' etc after the .)
+ if (dotIndex + 2 != strlen(attribute))
+ return false;
+
+ binding.type = MaterialBinding::kFloat4;
+ switch (*lastCharacter)
+ {
+ case 'r':
+ case 'g':
+ case 'b':
+ case 'a':
+ binding.type = MaterialBinding::kColor4;
+ }
+
+ switch (*lastCharacter)
+ {
+ // r color or x vector
+ case 'r':
+ case 'x':
+ binding.colIndex = 0;
+ break;
+
+ // g color or y vector
+ case 'g':
+ case 'y':
+ binding.colIndex = 1;
+ break;
+
+ // b or z vector
+ case 'b':
+ case 'z':
+ binding.colIndex = 2;
+ break;
+
+ // alpha or w vector
+ case 'a':
+ case 'w':
+ binding.colIndex = 3;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ outputBinding.attribute = BindingToUInt32 (binding);
+ return true;
+ }
+
+ virtual ClassIDType BindValue (Object& target, const UnityEngine::Animation::GenericBinding& outputBinding, UnityEngine::Animation::BoundCurve& bound) const
+ {
+ MaterialBinding materialBinding = UInt32ToBinding(outputBinding.attribute);
+
+ ShaderLab::FastPropertyName prop;
+ prop.InitBy28BitHash (materialBinding.propertyName);
+ materialBinding.propertyName = prop.index;
+
+ bound.targetPtr = *reinterpret_cast<void**> (&materialBinding);
+ return ClassID(float);
+ }
+};
+#endif
+
+
+static RendererAnimationBinding* gRendererBinding = NULL;
+#if MATERIAL_ANIMATION
+static RendererMaterialAnimationBinding* gMaterialBinding = NULL;
+#endif
+
+void InitializeRendererAnimationBindingInterface ()
+{
+ gRendererBinding = UNITY_NEW (RendererAnimationBinding, kMemAnimation);
+ UnityEngine::Animation::GetGenericAnimationBindingCache ().RegisterIAnimationBinding (ClassID(Renderer), UnityEngine::Animation::kRendererMaterialPPtrBinding, gRendererBinding);
+
+ #if MATERIAL_ANIMATION
+ gMaterialBinding = UNITY_NEW (RendererMaterialAnimationBinding, kMemAnimation);
+ UnityEngine::Animation::GetGenericAnimationBindingCache ().RegisterIAnimationBinding (ClassID(Renderer), UnityEngine::Animation::kRendererMaterialPropertyBinding, gMaterialBinding);
+ #endif
+}
+
+void CleanupRendererAnimationBindingInterface ()
+{
+ UNITY_DELETE (gRendererBinding, kMemAnimation);
+ #if MATERIAL_ANIMATION
+ UNITY_DELETE (gMaterialBinding, kMemAnimation);
+ #endif
+}
+
+
+#if ENABLE_UNIT_TESTS
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (MaterialBindingTests)
+{
+ TEST (MaterialBindingUInt32Conversion)
+ {
+ RendererMaterialAnimationBinding::MaterialBinding binding;
+ binding.propertyName = 12345678;
+ binding.colIndex = 3;
+ binding.type = 3;
+
+ RendererMaterialAnimationBinding::MaterialBinding converted = RendererMaterialAnimationBinding::UInt32ToBinding (RendererMaterialAnimationBinding::BindingToUInt32 (binding));
+ CHECK_EQUAL (converted.propertyName, binding.propertyName);
+ CHECK_EQUAL (converted.colIndex, binding.colIndex);
+ CHECK_EQUAL (converted.type, binding.type);
+ }
+
+ TEST (MaterialBindingCorrectlyEncodesAllBits)
+ {
+ CHECK_EQUAL ( RendererMaterialAnimationBinding::BindingToUInt32(RendererMaterialAnimationBinding::UInt32ToBinding (0xFFFFFFFF)), 0xFFFFFFFF);
+ CHECK_EQUAL ( RendererMaterialAnimationBinding::BindingToUInt32(RendererMaterialAnimationBinding::UInt32ToBinding (0)), 0);
+ }
+}
+#endif \ No newline at end of file
diff --git a/Runtime/Filters/RendererAnimationBinding.h b/Runtime/Filters/RendererAnimationBinding.h
new file mode 100644
index 0000000..4096ed8
--- /dev/null
+++ b/Runtime/Filters/RendererAnimationBinding.h
@@ -0,0 +1,2 @@
+void InitializeRendererAnimationBindingInterface ();
+void CleanupRendererAnimationBindingInterface ();
diff --git a/Runtime/GameCode/Behaviour.cpp b/Runtime/GameCode/Behaviour.cpp
new file mode 100644
index 0000000..49d5035
--- /dev/null
+++ b/Runtime/GameCode/Behaviour.cpp
@@ -0,0 +1,259 @@
+#include "UnityPrefix.h"
+#include "Behaviour.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Misc/ReproductionLog.h"
+#include "Runtime/Threads/Thread.h"
+
+Behaviour::~Behaviour ()
+{}
+
+void Behaviour::SetEnabled (bool enab)
+{
+ if ((bool)m_Enabled == enab)
+ return;
+ m_Enabled = enab;
+ UpdateEnabledState (IsActive ());
+ SetDirty ();
+}
+
+#if UNITY_EDITOR
+void Behaviour::SetEnabledNoDirty (bool enab)
+{
+ if ((bool)m_Enabled == enab)
+ return;
+ m_Enabled = enab;
+ UpdateEnabledState (IsActive ());
+}
+#endif
+
+void Behaviour::UpdateEnabledState (bool active)
+{
+ bool shouldBeAdded = active && m_Enabled;
+ if (shouldBeAdded == (bool)m_IsAdded)
+ return;
+
+ // Set IsAdded flag before adding/removing from manager. Otherwise if we get enabled update
+ // from inside of AddToManager/RemoveFromManager, we'll early out in the check above because
+ // flag is not set yet!
+ if (shouldBeAdded)
+ {
+ m_IsAdded = true;
+ AddToManager ();
+ }
+ else
+ {
+ m_IsAdded = false;
+ RemoveFromManager ();
+ }
+}
+
+void Behaviour::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ UpdateEnabledState (IsActive ());
+}
+
+void Behaviour::Deactivate (DeactivateOperation operation)
+{
+ UpdateEnabledState (false);
+ Super::Deactivate (operation);
+}
+
+IMPLEMENT_OBJECT_SERIALIZE (Behaviour)
+template<class TransferFunc>
+void Behaviour::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.Transfer (m_Enabled, "m_Enabled", kHideInEditorMask | kEditorDisplaysCheckBoxMask);
+ transfer.Align();
+}
+
+
+// BEHAVIOURMANAGER
+// --------------------------------------------------------------------------
+
+
+BaseBehaviourManager::~BaseBehaviourManager ()
+{
+ for (Lists::iterator i=m_Lists.begin();i != m_Lists.end();i++)
+ {
+ Lists::mapped_type& listPair = i->second;
+
+ Assert(listPair.first == NULL || listPair.first->empty());
+ delete listPair.first;
+
+ Assert(listPair.second == NULL || listPair.second->empty());
+ delete listPair.second;
+ }
+ m_Lists.clear();
+}
+
+void BaseBehaviourManager::AddBehaviour (BehaviourListNode& p, int queueIndex)
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ Lists::mapped_type& listPair = m_Lists[queueIndex];
+ if (listPair.first == NULL)
+ {
+ Assert(listPair.second == NULL);
+ listPair.first = new BehaviourList();
+ listPair.second = new BehaviourList();
+ }
+
+ listPair.second->push_back (p);
+}
+
+void BaseBehaviourManager::RemoveBehaviour (BehaviourListNode& p)
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+ p.RemoveFromList();
+}
+
+void BaseBehaviourManager::IntegrateLists()
+{
+ for (Lists::iterator i=m_Lists.begin();i!=m_Lists.end();i++)
+ {
+ Lists::mapped_type& listPair = (*i).second;
+
+ listPair.first->append(*listPair.second);
+ Assert(listPair.second->empty());
+ }
+}
+
+template<typename T>
+void BaseBehaviourManager::CommonUpdate ()
+{
+ IntegrateLists();
+
+ for (Lists::iterator i=m_Lists.begin();i!=m_Lists.end();i++)
+ {
+ Lists::mapped_type& listPair = (*i).second;
+
+ SafeIterator<BehaviourList> iterator (*listPair.first);
+ while (iterator.Next())
+ {
+ Behaviour& behaviour = **iterator;
+
+ #if SUPPORT_LOG_ORDER_TRACE
+ if (RunningReproduction())
+ {
+ if (SUPPORT_LOG_ORDER_TRACE == 2)
+ {
+ LogString(Format("UpdateBehaviour %s (%s) [%d]", behaviour.GetName(), behaviour.GetClassName().c_str(), behaviour.GetInstanceID()));
+ }
+ else
+ {
+ LogString(Format("UpdateBehaviour %s (%s)", behaviour.GetName(), behaviour.GetClassName().c_str()));
+ }
+ }
+ #endif
+
+ Assert(behaviour.IsAddedToManager ());
+
+ #if !UNITY_RELEASE
+ PPtr<Behaviour> behaviourPPtr (&behaviour);
+ #endif
+ T::UpdateBehaviour(behaviour);
+
+ // Behaviour might get destroyed in the mean time, so we have to check if the object still exists first
+ #if !UNITY_RELEASE
+ AssertIf (behaviourPPtr.IsValid() && (behaviour.GetEnabled () && behaviour.IsActive ()) != behaviour.IsAddedToManager ());
+ #endif
+ }
+ }
+}
+
+class BehaviourManager : public BaseBehaviourManager
+{
+ public:
+
+ virtual void Update()
+ {
+ BaseBehaviourManager::CommonUpdate<BehaviourManager>();
+ }
+
+ static inline void UpdateBehaviour(Behaviour& beh)
+ {
+ beh.Update();
+ }
+};
+
+class FixedBehaviourManager : public BaseBehaviourManager {
+ public:
+
+
+ virtual void Update()
+ {
+ BaseBehaviourManager::CommonUpdate<FixedBehaviourManager>();
+ }
+
+ static inline void UpdateBehaviour(Behaviour& beh)
+ {
+ beh.FixedUpdate();
+ }
+};
+
+class LateBehaviourManager : public BaseBehaviourManager
+{
+ public:
+
+ virtual void Update()
+ {
+ BaseBehaviourManager::CommonUpdate<LateBehaviourManager>();
+ }
+
+ static inline void UpdateBehaviour(Behaviour& beh)
+ {
+ beh.LateUpdate();
+ }
+};
+
+class UpdateManager : public BaseBehaviourManager {
+ public:
+
+
+ virtual void Update()
+ {
+ BaseBehaviourManager::CommonUpdate<BehaviourManager>();
+ }
+
+ static inline void UpdateBehaviour(Behaviour& beh)
+ {
+ beh.Update();
+ }
+};
+
+
+
+#define GET_BEHAVIOUR_MANAGER(x) \
+ x* s_instance##x; \
+ BaseBehaviourManager& Get##x () { return reinterpret_cast<BaseBehaviourManager&> (*s_instance##x); } \
+ void CreateInstance##x() { s_instance##x = new x; } \
+ void ReleaseInstance##x() { delete s_instance##x; }
+
+
+GET_BEHAVIOUR_MANAGER(BehaviourManager)
+GET_BEHAVIOUR_MANAGER(FixedBehaviourManager)
+GET_BEHAVIOUR_MANAGER(LateBehaviourManager)
+GET_BEHAVIOUR_MANAGER(UpdateManager)
+
+void Behaviour::InitializeClass ()
+{
+ CreateInstanceBehaviourManager();
+ CreateInstanceFixedBehaviourManager();
+ CreateInstanceLateBehaviourManager();
+ CreateInstanceUpdateManager();
+}
+
+void Behaviour::CleanupClass ()
+{
+ ReleaseInstanceBehaviourManager();
+ ReleaseInstanceFixedBehaviourManager();
+ ReleaseInstanceLateBehaviourManager();
+ ReleaseInstanceUpdateManager();
+
+}
+
+IMPLEMENT_CLASS_HAS_INIT (Behaviour)
+INSTANTIATE_TEMPLATE_TRANSFER_EXPORTED(Behaviour)
diff --git a/Runtime/GameCode/Behaviour.h b/Runtime/GameCode/Behaviour.h
new file mode 100644
index 0000000..08c3d6a
--- /dev/null
+++ b/Runtime/GameCode/Behaviour.h
@@ -0,0 +1,87 @@
+#ifndef BEHAVIOUR_H
+#define BEHAVIOUR_H
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Modules/ExportModules.h"
+
+class EXPORT_COREMODULE Behaviour : public Unity::Component
+{
+ public:
+
+ REGISTER_DERIVED_ABSTRACT_CLASS (Behaviour, Component)
+ DECLARE_OBJECT_SERIALIZE (Behaviour)
+ Behaviour (MemLabelId label, ObjectCreationMode mode) : Super(label, mode) { m_Enabled = true; m_IsAdded = false; }
+
+ void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ virtual void Update () {}
+ virtual void FixedUpdate () {}
+ virtual void LateUpdate () {}
+
+ void Deactivate (DeactivateOperation operation);
+ /// Enable or disable updates of this behaviour
+ virtual void SetEnabled (bool enab);
+ bool GetEnabled () const { return m_Enabled; }
+
+ bool IsAddedToManager () const { return m_IsAdded; }
+
+ #if UNITY_EDITOR
+ void SetEnabledNoDirty (bool enab);
+ virtual bool ShouldDisplayEnabled () { return true; }
+ #endif
+
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+// protected:
+
+
+ /// Override this to add the behaviour not to BehaviourManager but some other Manager
+ /// You should NOT call Super.
+ /// This is called when the behaviour has become enabled and its game object is disabled
+ /// You can rely on that AddToManager is only called once and will always be balanced out by RemoveFromManager before it is destroyed.
+ virtual void AddToManager () = 0;
+ virtual void RemoveFromManager () = 0;
+
+
+ private:
+ void UpdateEnabledState (bool active);
+
+ ///@todo DO THIS PROPERLY. MORE SPACE EFFICIENT
+ UInt8 m_Enabled;
+ UInt8 m_IsAdded;
+};
+
+typedef ListNode<Behaviour> BehaviourListNode;
+
+class EXPORT_COREMODULE BaseBehaviourManager
+{
+ public:
+ virtual ~BaseBehaviourManager ();
+
+ virtual void Update() = 0;
+
+ void AddBehaviour (BehaviourListNode& node, int queue);
+ void RemoveBehaviour (BehaviourListNode& node);
+
+ protected:
+
+ template<typename T> void CommonUpdate ();
+ void IntegrateLists();
+
+ typedef List<BehaviourListNode> BehaviourList;
+
+ // Need to use map instead of vector_map here, because it can change during iteration
+ // (Behaviours added in update calls).
+ typedef std::map<int, std::pair<BehaviourList*,BehaviourList*> > Lists;
+ Lists m_Lists;
+};
+
+EXPORT_COREMODULE BaseBehaviourManager& GetBehaviourManager ();
+EXPORT_COREMODULE BaseBehaviourManager& GetFixedBehaviourManager ();
+EXPORT_COREMODULE BaseBehaviourManager& GetLateBehaviourManager ();
+EXPORT_COREMODULE BaseBehaviourManager& GetUpdateManager ();
+
+#endif
diff --git a/Runtime/GameCode/CallDelayed.cpp b/Runtime/GameCode/CallDelayed.cpp
new file mode 100644
index 0000000..ca41f6f
--- /dev/null
+++ b/Runtime/GameCode/CallDelayed.cpp
@@ -0,0 +1,203 @@
+#include "UnityPrefix.h"
+#include "CallDelayed.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Profiler/Profiler.h"
+
+DelayedCallManager::DelayedCallManager (MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode)
+{
+ m_NextIterator = m_CallObjects.end();
+}
+
+DelayedCallManager::~DelayedCallManager () {
+ ClearAll ();
+}
+
+void CallDelayed (DelayedCall *func, PPtr<Object> o, float time, void* userData, float repeatRate, CleanupUserData* cleanup, int mode)
+{
+ DelayedCallManager::Callback callback;
+ if (time == 0.0F)
+ time = -1.0F;
+
+ callback.time = time + GetCurTime ();
+ callback.frame = -1;
+ if (mode & DelayedCallManager::kWaitForNextFrame)
+ callback.frame = GetTimeManager().GetFrameCount() + 1;
+ callback.repeatRate = repeatRate;
+ callback.repeat = repeatRate != 0.0F;
+ AssertIf (callback.repeat && repeatRate < 0.00001F && (mode & DelayedCallManager::kWaitForNextFrame) == 0);
+ callback.userData = userData;
+ callback.call = func;
+ callback.cleanup = cleanup;
+ callback.object = o;
+ callback.mode = mode;
+ callback.timeStamp = GetDelayedCallManager ().m_TimeStamp;
+ GetDelayedCallManager ().m_CallObjects.insert (callback);
+}
+
+void DelayedCallManager::CancelCallDelayed (PPtr<Object> o, DelayedCall* callback, ShouldCancelCall* shouldCancel, void* userData)
+{
+ Container::iterator next;
+ for (Container::iterator i=m_CallObjects.begin ();i != m_CallObjects.end ();i=next)
+ {
+ next = i; next++;
+ Callback &cb = const_cast<Callback&> (*i);
+ if (cb.object != o || callback != cb.call)
+ continue;
+
+ if (shouldCancel == NULL || shouldCancel (cb.userData, userData))
+ Remove (cb, i);
+ }
+}
+
+void DelayedCallManager::CancelCallDelayed2 (PPtr<Object> o, DelayedCall* callback, DelayedCall* otherCallback)
+{
+ Container::iterator next;
+ for (Container::iterator i=m_CallObjects.begin ();i != m_CallObjects.end ();i=next)
+ {
+ next = i; next++;
+ Callback &cb = const_cast<Callback&> (*i);
+ if (cb.object == o && (callback == cb.call || cb.call == otherCallback))
+ Remove (cb, i);
+ }
+}
+
+void DelayedCallManager::CancelAllCallDelayed( PPtr<Object> o )
+{
+ Container::iterator next;
+ for (Container::iterator i=m_CallObjects.begin ();i != m_CallObjects.end ();i=next)
+ {
+ next = i; next++;
+ Callback &cb = const_cast<Callback&> (*i);
+ if (cb.object != o)
+ continue;
+
+ Remove (cb, i);
+ }
+}
+
+bool DelayedCallManager::HasDelayedCall (PPtr<Object> o, DelayedCall* callback, ShouldCancelCall* shouldCancel, void* cancelUserData)
+{
+ for (Container::iterator i=m_CallObjects.begin ();i != m_CallObjects.end ();i++)
+ {
+ Callback &cb = const_cast<Callback&> (*i);
+ if (cb.object != o || callback != cb.call)
+ continue;
+
+ if (shouldCancel == NULL || shouldCancel (cb.userData, cancelUserData))
+ return true;
+ }
+ return false;
+}
+
+
+inline void DelayedCallManager::Remove (const Callback& cb, Container::iterator i)
+{
+ CleanupUserData* cleanup = cb.cleanup;
+ void* userData = cb.userData;
+
+ if (m_NextIterator != i)
+ m_CallObjects.erase (i);
+ else
+ {
+ m_NextIterator++;
+ m_CallObjects.erase (i);
+ }
+
+ if (cleanup && userData)
+ cleanup (userData);
+}
+
+inline void DelayedCallManager::RemoveNoCleanup (const Callback& cb, Container::iterator i)
+{
+ if (m_NextIterator != i)
+ m_CallObjects.erase (i);
+ else
+ {
+ m_NextIterator++;
+ m_CallObjects.erase (i);
+ }
+}
+
+//PROFILER_INFORMATION(gDelayedCallProfile, "Coroutines & Delayed Call", kProfilerOther)
+
+// Call all delayed functions when the time has come.
+void DelayedCallManager::Update (int modeMask)
+{
+// PROFILER_AUTO(gDelayedCallProfile, NULL)
+
+ // For robustness we are using a iterator that is stored in the manager and when Remove is called
+ // it makes sure that the iterator is set to the next element so that we never end up with a stale ptr
+ float time = GetCurTime();
+ int frame = GetTimeManager().GetFrameCount();
+ Container::iterator i = m_CallObjects.begin ();
+ m_TimeStamp++;
+
+ while (i != m_CallObjects.end () && i->time <= time)
+ {
+ m_NextIterator = i; m_NextIterator++;
+
+ Callback &cb = const_cast<Callback&> (*i);
+ //- Make sure the mask matches.
+ //- We never execute delayed calls that are added during the DelayedCallManager::Update function
+ if ((cb.mode & modeMask) && cb.timeStamp != m_TimeStamp && cb.frame <= frame)
+ {
+ // avoid loading stuff from persistent manager in the middle of async loading
+ Object *o = Object::IDToPointer (cb.object.GetInstanceID ());
+
+ if (o)
+ {
+ void* userData = cb.userData;
+ DelayedCall* callback = cb.call;
+ // Cleanup and Removal is a bit hard
+ // Problems are
+ // - CancelCall might be called from inside the callback so the best way is to remove the callback structure before.
+ // - of course we still need the user data to be deallocated calling the callback not before
+ if (!cb.repeat)
+ {
+ // Remove callback structure from set
+ CleanupUserData* cleanup = cb.cleanup;
+ RemoveNoCleanup (cb, i);
+ //call callback
+ callback (o, userData);
+ //afterwards cleanup userdata
+ if (cleanup && userData)
+ cleanup (userData);
+ }
+ else
+ {
+ // Advance time and reinsert (We dont call the cleanup function because we are repeating the call.
+ // It can only be canceleld by CancelDelayCall)
+ cb.time += cb.repeatRate;
+ if (cb.mode & DelayedCallManager::kWaitForNextFrame)
+ cb.frame = GetTimeManager().GetFrameCount() + 1;
+
+ m_CallObjects.insert (cb);
+ RemoveNoCleanup (cb, i);
+ // call callback
+ callback (o, userData);
+ }
+ }
+ else
+ Remove (cb, i);
+ }
+
+ i = m_NextIterator;
+ }
+}
+
+void DelayedCallManager::ClearAll ()
+{
+ for (Container::iterator i=m_CallObjects.begin ();i != m_CallObjects.end ();i++)
+ {
+ Callback &cb = const_cast<Callback&> (*i);
+ if (cb.cleanup && cb.userData)
+ cb.cleanup (cb.userData);
+ }
+ m_CallObjects.clear ();
+}
+
+IMPLEMENT_CLASS (DelayedCallManager)
+GET_MANAGER (DelayedCallManager)
diff --git a/Runtime/GameCode/CallDelayed.h b/Runtime/GameCode/CallDelayed.h
new file mode 100644
index 0000000..e293789
--- /dev/null
+++ b/Runtime/GameCode/CallDelayed.h
@@ -0,0 +1,102 @@
+#ifndef CALLDELAYED_H
+#define CALLDELAYED_H
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Utilities/MemoryPool.h"
+#include <set>
+
+
+
+/// Delayed call is called when the specified time has been reached.
+/// o is always non-NULL
+typedef void DelayedCall(Object* o, void* userData);
+/// CleanupUserData should be used to prevent leaking userData
+/// CleanupUserData is called whenever a registered callback is erased.
+/// Doing cleanup inside DelayedCall doesnt work since the object might get destroyed or the scene unloaded before a pending function is called
+/// CleanupUserData is called only when userData != NULL
+typedef void CleanupUserData (void* userData);
+
+/** Class to call functions delayed in time
+ */
+class DelayedCallManager : public GlobalGameManager
+{
+ public:
+ REGISTER_DERIVED_CLASS (DelayedCallManager, GlobalGameManager)
+
+ enum {
+ kRunFixedFrameRate = 1 << 0,
+ kRunDynamicFrameRate = 1 << 1,
+ kRunStartupFrame = 1 << 2,
+ kWaitForNextFrame = 1 << 3,
+ kAfterLoadingCompleted = 1 << 4,
+ kEndOfFrame = 1 << 5
+ };
+
+ DelayedCallManager (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~DelayedCallManager (); declared-by-macro
+
+ /// Time is the time from now we need to exceed to call the function
+ /// repeatRate determines at which intervals the call should be repeated. If repeat rate is 0.0F it will not repeat. If it is -1.0 it will repeat but you must have kWaitForNextFrame enabled
+ friend void CallDelayed (DelayedCall *func, PPtr<Object> o, float time, void* userData, float repeatRate, CleanupUserData* cleanup, int mode);
+ friend void CallDelayedAfterLoading (DelayedCall *func, PPtr<Object> o, void* userData);
+
+ /// Cancels all CallDelayed functions on object o if
+ /// - the callback is the same
+ /// - ShouldCancelCall returns true. (callBackUserData is the userdata stored with CallDelayed. cancelUserData is cancelUserData)
+ typedef bool ShouldCancelCall (void* callBackUserData, void* cancelUserdata);
+ void CancelCallDelayed (PPtr<Object> o, DelayedCall* callback, ShouldCancelCall* shouldCancel, void* cancelUserData);
+ void CancelCallDelayed2 (PPtr<Object> o, DelayedCall* callback, DelayedCall* otherCallback);
+ void CancelAllCallDelayed( PPtr<Object> o );
+
+ bool HasDelayedCall (PPtr<Object> o, DelayedCall* callback, ShouldCancelCall* shouldCancel, void* cancelUserData);
+
+ virtual void Update (int mask);
+
+ void ClearAll ();
+
+ int GetNumCallObjects() const { return m_CallObjects.size(); }
+
+ private:
+
+ /// Struct used to store which functions to execute on which objects.
+ struct Callback {
+ float time;
+ int frame;
+ float repeatRate;
+ bool repeat;
+ void* userData;
+ DelayedCall *call; ///< The function call to execute.
+ CleanupUserData *cleanup;
+ PPtr<Object> object; ///< The object to pass to m_Call.
+ int mode;
+ int timeStamp;
+
+ friend bool operator < (const Callback& lhs, const Callback& rhs) { return lhs.time < rhs.time; }
+ };
+
+#if ENABLE_CUSTOM_ALLOCATORS_FOR_STDMAP
+ typedef std::multiset<Callback, std::less<Callback>, memory_pool<Callback> > Container;
+#else
+ typedef std::multiset<Callback, std::less<Callback> > Container;
+#endif
+
+ void Remove (const Callback& cb, Container::iterator i);
+ void RemoveNoCleanup (const Callback& cb, Container::iterator i);
+ Container m_CallObjects;
+ Container::iterator m_NextIterator;
+ int m_TimeStamp;
+};
+
+/// Calls func in time seconds from now. When o is NULL when the call happens, only cleanup is called.
+/// The function call is repeated at repeatRate if repeatRate is not 0.0F
+/// cleanup is called whenever userData is non-NULL and a callback is removed (After calling of the delay function without repeat, object was destroyed or Shutdown of a scene)
+void CallDelayed (DelayedCall *func, PPtr<Object> o, float time = -1.0F, void* userData = NULL, float repeatRate = 0.0F, CleanupUserData* cleanup = NULL, int mode = DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kRunFixedFrameRate);
+inline void CallDelayedAfterLoading (DelayedCall *func, PPtr<Object> o, void* userData = NULL)
+{
+ CallDelayed(func, o, -1.0F, userData, 0.0F, NULL, DelayedCallManager::kAfterLoadingCompleted);
+}
+
+
+DelayedCallManager& GetDelayedCallManager ();
+
+#endif
diff --git a/Runtime/GameCode/CloneObject.cpp b/Runtime/GameCode/CloneObject.cpp
new file mode 100644
index 0000000..8996055
--- /dev/null
+++ b/Runtime/GameCode/CloneObject.cpp
@@ -0,0 +1,303 @@
+#include "UnityPrefix.h"
+#include "CloneObject.h"
+#include "Runtime/Serialize/TransferUtility.h"
+#include "Runtime/Serialize/TransferFunctions/RemapPPtrTransfer.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/StreamedBinaryWrite.h"
+#include "Runtime/Serialize/TransferFunctions/StreamedBinaryRead.h"
+#include "Runtime/Serialize/FileCache.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Serialize/AwakeFromLoadQueue.h"
+#include "Runtime/Filters/Misc/Font.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+#include <map>
+
+using namespace std;
+using namespace Unity;
+
+Object& ProduceClone (Object& object)
+{
+ Object* clone = Object::Produce (object.GetClassID ());
+
+#if ENABLE_SCRIPTING
+ MonoBehaviour* cloneBehaviour = dynamic_pptr_cast<MonoBehaviour*> (clone);
+ if (cloneBehaviour)
+ {
+ MonoBehaviour& cloneSrc = static_cast<MonoBehaviour&> (object);
+ cloneBehaviour->SetScript(cloneSrc.GetScript());
+ }
+#endif
+ return *clone;
+}
+
+void CollectAndProduceSingleObject (Object& singleObject, TempRemapTable* remappedPtrs)
+{
+ Object& clone = ProduceClone (singleObject);
+
+ remappedPtrs->insert(make_pair(singleObject.GetInstanceID(), clone.GetInstanceID()));
+}
+
+Transform* CollectAndProduceGameObjectHierarchy (GameObject& go, Transform* transform, TempRemapTable* remappedPtrs)
+{
+ GameObject* cloneGO = static_cast<GameObject*> (Object::Produce (ClassID(GameObject)));
+ remappedPtrs->insert(make_pair(go.GetInstanceID(), cloneGO->GetInstanceID()));
+
+ GameObject::Container& goContainer = go.GetComponentContainerInternal();
+ GameObject::Container& clonedContainer = cloneGO->GetComponentContainerInternal();
+
+ clonedContainer.resize(goContainer.size());
+ for (int i=0;i<goContainer.size();i++)
+ {
+ Unity::Component& component = *goContainer[i].second;
+ Unity::Component& clone = static_cast<Unity::Component&> (ProduceClone(component));
+
+ clonedContainer[i].first = goContainer[i].first;
+ clonedContainer[i].second = &clone;
+ clone.SetGameObjectInternal(cloneGO);
+
+ remappedPtrs->insert(make_pair(component.GetInstanceID(), clone.GetInstanceID()));
+ }
+
+ if (transform)
+ {
+ Transform& cloneTransform = cloneGO->GetComponent(Transform);
+
+ Transform::TransformComList& srcTransformArray = transform->GetChildrenInternal();
+ Transform::TransformComList& dstTransformArray = cloneTransform.GetChildrenInternal();
+
+ dstTransformArray.resize_uninitialized(srcTransformArray.size(), false);
+ for (int i=0;i<srcTransformArray.size();i++)
+ {
+ Transform& curT = *srcTransformArray[i];
+ GameObject& curGO = curT.GetGameObject();
+
+ Transform* curCloneTransform = CollectAndProduceGameObjectHierarchy(curGO, &curT, remappedPtrs);
+ curCloneTransform->GetParentPtrInternal() = &cloneTransform;
+ dstTransformArray[i] = curCloneTransform;
+ }
+ return &cloneTransform;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+inline GameObject* GetGameObjectPtr (Object& o)
+{
+ GameObject* go = dynamic_pptr_cast<GameObject*>(&o);
+ Unity::Component* component = dynamic_pptr_cast<Unity::Component*>(&o);
+ if (component != NULL && component->GetGameObjectPtr())
+ go = component->GetGameObjectPtr();
+
+ return go;
+}
+
+void CollectAndProduceClonedIsland (Object& o, TempRemapTable* remappedPtrs)
+{
+ AssertIf(!remappedPtrs->empty());
+
+ remappedPtrs->reserve(64);
+
+ GameObject* go = GetGameObjectPtr(o);
+ if (go)
+ {
+ ///@TODO: It would be useful to lock object creation around a long instantiate call.
+ // Butwe have to be careful that we dont load anything during the object creation in order to avoid
+ // a deadlock: case 389317
+
+ // LockObjectCreation();
+
+ CollectAndProduceGameObjectHierarchy(*go, go->QueryComponent(Transform), remappedPtrs);
+
+ // UnlockObjectCreation();
+ }
+ else
+ CollectAndProduceSingleObject(o, remappedPtrs);
+
+ remappedPtrs->sort();
+}
+
+void AwakeAndActivateClonedObjects (const TempRemapTable& ptrs)
+{
+ AwakeFromLoadQueue queue (kMemTempAlloc);
+ queue.Reserve(ptrs.size());
+
+ for (TempRemapTable::const_iterator i=ptrs.begin ();i!=ptrs.end ();++i)
+ {
+ Object& clone = *PPtr<Object> (i->second);
+ clone.SetHideFlags (0);
+ clone.SetDirty ();
+
+ #if !UNITY_RELEASE
+ // we will clone that object - no need to call Reset as we will construct it fully
+ clone.HackSetResetWasCalled();
+ #endif
+
+ queue.Add(*PPtr<Object> (i->second));
+ }
+
+ queue.AwakeFromLoad ((AwakeFromLoadMode)(kDefaultAwakeFromLoad | kInstantiateOrCreateFromCodeAwakeFromLoad));
+}
+
+class RemapFunctorTempRemapTable : public GenerateIDFunctor
+{
+public:
+ const TempRemapTable& remap;
+
+ RemapFunctorTempRemapTable (const TempRemapTable& inRemap) : remap (inRemap) { }
+
+ virtual SInt32 GenerateInstanceID (SInt32 oldInstanceID, TransferMetaFlags metaFlags = kNoTransferFlags)
+ {
+ AssertIf (metaFlags & kStrongPPtrMask);
+
+ TempRemapTable::const_iterator found = remap.find (oldInstanceID);
+ // No Remap found -> set zero or dont touch instanceID
+ if (found == remap.end ())
+ return oldInstanceID;
+ // Remap
+ else
+ return found->second;
+ }
+};
+
+static Object* CloneObjectImpl (Object* object, TempRemapTable& ptrs)
+{
+ // Since we will be creating a lot of objects here
+ // Just Lock the mutex all the time to avoid too many lock / unlock calls
+ CollectAndProduceClonedIsland (*object, &ptrs);
+
+ TempRemapTable::iterator it;
+
+#if UNITY_FLASH
+ //specialcase for flash, as that needs to be able to assume linear memorylayout.
+ dynamic_array<UInt8> buffer(kMemTempAlloc);
+ MemoryCacheWriter cacheWriter (buffer);
+#else
+ BlockMemoryCacheWriter cacheWriter (kMemTempAlloc);
+#endif
+
+ RemapFunctorTempRemapTable functor (ptrs);
+ RemapPPtrTransfer remapTransfer (kSerializeForPrefabSystem, true);
+ remapTransfer.SetGenerateIDFunctor (&functor);
+
+ for (it=ptrs.begin ();it != ptrs.end ();it++)
+ {
+ Object& original = *PPtr<Object> (it->first);
+
+ #if UNITY_EDITOR
+ original.WarnInstantiateDisallowed();
+ #endif
+
+ // Copy Data
+ Object& clone = *PPtr<Object> (it->second);
+
+ StreamedBinaryWrite<false> writeStream;
+ CachedWriter& writeCache = writeStream.Init (kSerializeForPrefabSystem, BuildTargetSelection::NoTarget());
+ writeCache.InitWrite (cacheWriter);
+ original.VirtualRedirectTransfer (writeStream);
+ writeCache.CompleteWriting();
+
+#if UNITY_FLASH
+ MemoryCacheReader cacheReader (buffer);
+#else
+ MemoryCacherReadBlocks cacheReader (cacheWriter.GetCacheBlocks (), cacheWriter.GetFileLength (), cacheWriter.GetCacheSize());
+#endif
+ StreamedBinaryRead<false> readStream;
+ CachedReader& readCache = readStream.Init (kSerializeForPrefabSystem);
+
+ readCache.InitRead (cacheReader, 0, writeCache.GetPosition());
+ clone.VirtualRedirectTransfer (readStream);
+ readCache.End();
+
+ if (!IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ {
+ GameObject* clonedGameObject = dynamic_pptr_cast<GameObject*> (&clone);
+ if (clonedGameObject)
+ clonedGameObject->SetActiveBitInternal(true);
+ }
+
+ #if UNITY_EDITOR
+ clone.CloneAdditionalEditorProperties(original);
+ #endif
+
+ // Remap references
+ clone.VirtualRedirectTransfer (remapTransfer);
+ }
+
+
+ TempRemapTable::iterator found = ptrs.find (object->GetInstanceID ());
+ AssertIf (found == ptrs.end ());
+ object = PPtr<Object> (found->second);
+
+ return object;
+}
+
+PROFILER_INFORMATION(gInstantiateProfile, "Instantiate", kProfilerOther)
+
+Object& CloneObject (Object& inObject)
+{
+ PROFILER_AUTO(gInstantiateProfile, &inObject)
+
+#if !GAMERELEASE
+ // For context info see case 499663
+ Font* font = dynamic_pptr_cast<Font*> (&inObject);
+ if (font && font->GetConvertCase() == Font::kDynamicFont)
+ ErrorString("Font Error: Cloning a dynamic font is not supported and may result in incorrect font rendering.");
+#endif
+
+ TempRemapTable ptrs;
+ Object* object = CloneObjectImpl(&inObject, ptrs);
+
+ if (object)
+ object->SetName(Append (object->GetName(), "(Clone)").c_str());
+
+ AwakeAndActivateClonedObjects(ptrs);
+
+ ANALYSIS_ASSUME(object);
+ return *object;
+}
+
+Object& InstantiateObject (Object& inObject, const Vector3f& worldPos, const Quaternionf& worldRot, TempRemapTable& ptrs)
+{
+ PROFILER_AUTO(gInstantiateProfile, &inObject)
+ Object* object = CloneObjectImpl (&inObject, ptrs);
+
+ // Get the transformComponent of the first object in the input objects
+ Transform *expTransform = NULL;
+ if (object)
+ {
+ Unity::Component* com = dynamic_pptr_cast<Unity::Component*> (object);
+ GameObject* go = dynamic_pptr_cast<GameObject*> (object);
+ if (com)
+ expTransform = com->QueryComponent (Transform);
+ else if (go)
+ expTransform = go->QueryComponent (Transform);
+
+ object->SetName(Append (object->GetName(), "(Clone)").c_str());
+ }
+
+ // Set position
+ if (expTransform)
+ {
+ expTransform->SetPosition (worldPos);
+ expTransform->SetRotationSafe (worldRot);
+ }
+
+ ANALYSIS_ASSUME(object);
+ return *object;
+}
+
+
+Object& InstantiateObject (Object& inObject, const Vector3f& worldPos, const Quaternionf& worldRot)
+{
+ TempRemapTable ptrs;
+ Object& obj = InstantiateObject (inObject, worldPos, worldRot, ptrs);
+
+ AwakeAndActivateClonedObjects(ptrs);
+
+ return obj;
+}
diff --git a/Runtime/GameCode/CloneObject.h b/Runtime/GameCode/CloneObject.h
new file mode 100644
index 0000000..f6ee068
--- /dev/null
+++ b/Runtime/GameCode/CloneObject.h
@@ -0,0 +1,29 @@
+#ifndef CLONEOBJECT_H
+#define CLONEOBJECT_H
+
+#include <vector>
+#include "Runtime/Utilities/vector_map.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+
+class Object;
+class Vector3f;
+class Quaternionf;
+
+typedef std::pair<SInt32, SInt32> IntPair;
+typedef vector_map<SInt32, SInt32, std::less<SInt32>, STL_ALLOCATOR(kMemTempAlloc, IntPair) > TempRemapTable;
+
+/// Clones a vector objects. Cloning will clone all islands of connected objects through pptrs.
+/// This will not properly clone editorextensionimpl data and should only be used in gamemode
+/// After all objects are loaded, all gameobjects will be Activated and if the original
+/// was in an animation the clone it will be added to it
+Object& CloneObject (Object& objects);
+
+/// The same as above, but set position and rotation of the first objects' transform component.
+/// It also performs a delayed activation of all instantiated objects.
+Object& InstantiateObject (Object& objects, const Vector3f& worldPos, const Quaternionf& worldRot);
+
+/// Use these in succession if you need to setup some data prior to activation/awake
+Object& InstantiateObject (Object& inObject, const Vector3f& worldPos, const Quaternionf& worldRot, TempRemapTable& ptrs);
+void AwakeAndActivateClonedObjects (const TempRemapTable& ptrs);
+
+#endif
diff --git a/Runtime/GameCode/DestroyDelayed.cpp b/Runtime/GameCode/DestroyDelayed.cpp
new file mode 100644
index 0000000..aeba252
--- /dev/null
+++ b/Runtime/GameCode/DestroyDelayed.cpp
@@ -0,0 +1,16 @@
+#include "UnityPrefix.h"
+#include "DestroyDelayed.h"
+#include "CallDelayed.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+
+void DelayedDestroyCallback (Object* o, void* userData);
+
+void DelayedDestroyCallback (Object* o, void* userData)
+{
+ DestroyObjectHighLevel (o);
+}
+
+void DestroyObjectDelayed (Object* o, float time)
+{
+ CallDelayed (DelayedDestroyCallback, o, time);
+}
diff --git a/Runtime/GameCode/DestroyDelayed.h b/Runtime/GameCode/DestroyDelayed.h
new file mode 100644
index 0000000..f017887
--- /dev/null
+++ b/Runtime/GameCode/DestroyDelayed.h
@@ -0,0 +1,10 @@
+#ifndef DESTROYDELAYED_H
+#define DESTROYDELAYED_H
+
+class Object;
+
+/// Destroys object in time seconds
+/// If time is not specified will be destroyed at the end of the frame
+void DestroyObjectDelayed (Object* o, float time = -100.0F);
+
+#endif
diff --git a/Runtime/GameCode/RootMotionData.h b/Runtime/GameCode/RootMotionData.h
new file mode 100644
index 0000000..789a34a
--- /dev/null
+++ b/Runtime/GameCode/RootMotionData.h
@@ -0,0 +1,9 @@
+#pragma once
+
+struct RootMotionData
+{
+ Vector3f deltaPosition;
+ Quaternionf targetRotation;
+ float gravityWeight;
+ bool didApply;
+}; \ No newline at end of file
diff --git a/Runtime/Geometry/AABB.cpp b/Runtime/Geometry/AABB.cpp
new file mode 100644
index 0000000..c8e01b4
--- /dev/null
+++ b/Runtime/Geometry/AABB.cpp
@@ -0,0 +1,241 @@
+#include "UnityPrefix.h"
+#include "AABB.h"
+#include "Runtime/Math/Quaternion.h"
+
+const AABB AABB::zero = AABB(Vector3f::zero, Vector3f::zero);
+
+void CalculateClosestPoint (const Vector3f& rkPoint, const AABB& rkBox, Vector3f& outPoint, float& outSqrDistance)
+{
+ // compute coordinates of point in box coordinate system
+ Vector3f kClosest = rkPoint - rkBox.GetCenter();
+
+ // project test point onto box
+ float fSqrDistance = 0.0f;
+ float fDelta;
+
+ for (int i=0;i<3;i++)
+ {
+ if ( kClosest[i] < -rkBox.GetExtent (i) )
+ {
+ fDelta = kClosest[i] + rkBox.GetExtent (i);
+ fSqrDistance += fDelta * fDelta;
+ kClosest[i] = -rkBox.GetExtent (i);
+ }
+ else if ( kClosest[i] > rkBox.GetExtent(i) )
+ {
+ fDelta = kClosest[i] - rkBox.GetExtent (i);
+ fSqrDistance += fDelta * fDelta;
+ kClosest[i] = rkBox.GetExtent (i);
+ }
+ }
+
+ // Inside
+ if (fSqrDistance == 0.0F)
+ {
+ outPoint = rkPoint;
+ outSqrDistance = 0.0F;
+ }
+ // Outside
+ else
+ {
+ outPoint = kClosest + rkBox.GetCenter();
+ outSqrDistance = fSqrDistance;
+ }
+}
+
+
+// Sphere-AABB distance, Arvo's algorithm
+float CalculateSqrDistance (const Vector3f& rkPoint, const AABB& rkBox)
+{
+ Vector3f closest = rkPoint - rkBox.GetCenter();
+ float sqrDistance = 0.0f;
+
+ for (int i = 0; i < 3; ++i)
+ {
+ float clos = closest[i];
+ float ext = rkBox.GetExtent(i);
+ if (clos < -ext)
+ {
+ float delta = clos + ext;
+ sqrDistance += delta * delta;
+ closest[i] = -ext;
+ }
+ else if (clos > ext)
+ {
+ float delta = clos - ext;
+ sqrDistance += delta * delta;
+ closest[i] = ext;
+ }
+ }
+
+ return sqrDistance;
+}
+
+void AABB::GetVertices (Vector3f* outVertices) const
+{
+ outVertices[0] = m_Center + Vector3f (-m_Extent.x, -m_Extent.y, -m_Extent.z);
+ outVertices[1] = m_Center + Vector3f (+m_Extent.x, -m_Extent.y, -m_Extent.z);
+ outVertices[2] = m_Center + Vector3f (-m_Extent.x, +m_Extent.y, -m_Extent.z);
+ outVertices[3] = m_Center + Vector3f (+m_Extent.x, +m_Extent.y, -m_Extent.z);
+
+ outVertices[4] = m_Center + Vector3f (-m_Extent.x, -m_Extent.y, +m_Extent.z);
+ outVertices[5] = m_Center + Vector3f (+m_Extent.x, -m_Extent.y, +m_Extent.z);
+ outVertices[6] = m_Center + Vector3f (-m_Extent.x, +m_Extent.y, +m_Extent.z);
+ outVertices[7] = m_Center + Vector3f (+m_Extent.x, +m_Extent.y, +m_Extent.z);
+}
+
+void MinMaxAABB::GetVertices( Vector3f outVertices[8] ) const
+{
+ // 7-----6
+ // / /|
+ // 3-----2 |
+ // | 4 | 5
+ // | |/
+ // 0-----1
+ outVertices[0].Set( m_Min.x, m_Min.y, m_Min.z );
+ outVertices[1].Set( m_Max.x, m_Min.y, m_Min.z );
+ outVertices[2].Set( m_Max.x, m_Max.y, m_Min.z );
+ outVertices[3].Set( m_Min.x, m_Max.y, m_Min.z );
+ outVertices[4].Set( m_Min.x, m_Min.y, m_Max.z );
+ outVertices[5].Set( m_Max.x, m_Min.y, m_Max.z );
+ outVertices[6].Set( m_Max.x, m_Max.y, m_Max.z );
+ outVertices[7].Set( m_Min.x, m_Max.y, m_Max.z );
+}
+
+
+
+bool AABB::IsInside (const Vector3f& inPoint) const
+{
+ if (inPoint[0] < m_Center[0] - m_Extent[0])
+ return false;
+ if (inPoint[0] > m_Center[0] + m_Extent[0])
+ return false;
+
+ if (inPoint[1] < m_Center[1] - m_Extent[1])
+ return false;
+ if (inPoint[1] > m_Center[1] + m_Extent[1])
+ return false;
+
+ if (inPoint[2] < m_Center[2] - m_Extent[2])
+ return false;
+ if (inPoint[2] > m_Center[2] + m_Extent[2])
+ return false;
+
+ return true;
+}
+
+void AABB::Encapsulate (const Vector3f& inPoint) {
+ MinMaxAABB temp = *this;
+ temp.Encapsulate (inPoint);
+ FromMinMaxAABB (temp);
+}
+
+bool MinMaxAABB::IsInside (const Vector3f& inPoint) const
+{
+ if (inPoint[0] < m_Min[0])
+ return false;
+ if (inPoint[0] > m_Max[0])
+ return false;
+
+ if (inPoint[1] < m_Min[1])
+ return false;
+ if (inPoint[1] > m_Max[1])
+ return false;
+
+ if (inPoint[2] < m_Min[2])
+ return false;
+ if (inPoint[2] > m_Max[2])
+ return false;
+
+ return true;
+}
+
+MinMaxAABB AddAABB (const MinMaxAABB& lhs, const MinMaxAABB& rhs)
+{
+ MinMaxAABB minMax;
+ if (lhs.IsValid())
+ minMax = lhs;
+
+ if (rhs.IsValid())
+ {
+ minMax.Encapsulate (rhs.GetMax ());
+ minMax.Encapsulate (rhs.GetMin ());
+ }
+
+ return minMax;
+}
+
+inline Vector3f RotateExtents (const Vector3f& extents, const Matrix3x3f& rotation)
+{
+ Vector3f newExtents;
+ for (int i=0;i<3;i++)
+ newExtents[i] = Abs (rotation.Get (i, 0) * extents.x) + Abs (rotation.Get (i, 1) * extents.y) + Abs (rotation.Get (i, 2) * extents.z);
+ return newExtents;
+}
+
+inline Vector3f RotateExtents (const Vector3f& extents, const Matrix4x4f& rotation)
+{
+ Vector3f newExtents;
+ for (int i=0;i<3;i++)
+ newExtents[i] = Abs (rotation.Get (i, 0) * extents.x) + Abs (rotation.Get (i, 1) * extents.y) + Abs (rotation.Get (i, 2) * extents.z);
+ return newExtents;
+}
+
+void TransformAABB (const AABB& aabb, const Vector3f& position, const Quaternionf& rotation, AABB& result)
+{
+ Matrix3x3f m;
+ QuaternionToMatrix (rotation, m);
+
+ Vector3f extents = RotateExtents (aabb.GetExtent (), m);
+ Vector3f center = m.MultiplyPoint3 (aabb.GetCenter ());
+ center += position;
+ result.SetCenterAndExtent( center, extents );
+}
+
+void TransformAABB (const AABB& aabb, const Matrix4x4f& transform, AABB& result)
+{
+ Vector3f extents = RotateExtents (aabb.GetExtent (), transform);
+ Vector3f center = transform.MultiplyPoint3 (aabb.GetCenter ());
+ result.SetCenterAndExtent( center, extents );
+}
+
+void TransformAABBSlow (const AABB& aabb, const Matrix4x4f& transform, AABB& result)
+{
+ MinMaxAABB transformed;
+ transformed.Init ();
+
+ Vector3f v[8];
+ aabb.GetVertices (v);
+ for (int i=0;i<8;i++)
+ transformed.Encapsulate (transform.MultiplyPoint3 (v[i]));
+
+ result = transformed;
+}
+
+
+void InverseTransformAABB (const AABB& aabb, const Vector3f& position, const Quaternionf& rotation, AABB& result)
+{
+ Matrix3x3f m;
+ QuaternionToMatrix (Inverse (rotation), m);
+
+ Vector3f extents = RotateExtents (aabb.GetExtent (), m);
+ Vector3f center = aabb.GetCenter () - position;
+ center = m.MultiplyPoint3 (center);
+
+ result.SetCenterAndExtent( center, extents );
+}
+
+bool IsContainedInAABB (const AABB& inside, const AABB& bigBounds)
+{
+ bool outside = false;
+ outside |= inside.m_Center[0] - inside.m_Extent[0] < bigBounds.m_Center[0] - bigBounds.m_Extent[0];
+ outside |= inside.m_Center[0] + inside.m_Extent[0] > bigBounds.m_Center[0] + bigBounds.m_Extent[0];
+
+ outside |= inside.m_Center[1] - inside.m_Extent[1] < bigBounds.m_Center[1] - bigBounds.m_Extent[1];
+ outside |= inside.m_Center[1] + inside.m_Extent[1] > bigBounds.m_Center[1] + bigBounds.m_Extent[1];
+
+ outside |= inside.m_Center[2] - inside.m_Extent[2] < bigBounds.m_Center[2] - bigBounds.m_Extent[2];
+ outside |= inside.m_Center[2] + inside.m_Extent[2] > bigBounds.m_Center[2] + bigBounds.m_Extent[2];
+
+ return !outside;
+}
diff --git a/Runtime/Geometry/AABB.h b/Runtime/Geometry/AABB.h
new file mode 100644
index 0000000..8b80cf1
--- /dev/null
+++ b/Runtime/Geometry/AABB.h
@@ -0,0 +1,202 @@
+#ifndef AABB_H
+#define AABB_H
+
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Matrix4x4.h"
+
+class MinMaxAABB;
+class Quaternionf;
+class Matrix3x3f;
+
+
+class AABB
+{
+public:
+ static const AABB zero;
+
+public:
+ Vector3f m_Center;
+ Vector3f m_Extent;
+
+
+ AABB () {}
+ AABB (const Vector3f& c, const Vector3f& e) { m_Center = c; m_Extent = e; }
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (AABB)
+
+ AABB (const MinMaxAABB& aabb) { FromMinMaxAABB (aabb); }
+ AABB& operator = (const MinMaxAABB& aabb) { FromMinMaxAABB (aabb); return *this; }
+
+ bool operator == (const AABB& b) const { return m_Center == b.m_Center && m_Extent == b.m_Extent; }
+
+ void SetCenterAndExtent( const Vector3f& c, const Vector3f& e ) { m_Center = c; m_Extent = e; }
+
+ Vector3f& GetCenter () { return m_Center; }
+ Vector3f& GetExtent () { return m_Extent; }
+ float& GetExtent (int i) { return m_Extent[i]; }
+ const Vector3f& GetCenter ()const { return m_Center; }
+ const Vector3f& GetExtent ()const { return m_Extent; }
+ float GetExtent (int i)const { return m_Extent[i]; }
+
+ Vector3f GetMin () const { return m_Center - m_Extent; }
+ Vector3f GetMax () const { return m_Center + m_Extent; }
+
+ void Expand (float inValue);
+
+ bool IsValid () const;
+
+ bool IsInside (const Vector3f& inPoint) const;
+
+ void GetVertices (Vector3f* outVertices) const;
+ void EXPORT_COREMODULE Encapsulate (const Vector3f& inPoint);
+
+ private:
+
+ void FromMinMaxAABB (const MinMaxAABB& aabb);
+};
+
+bool IsContainedInAABB (const AABB& inside, const AABB& bigBounds);
+
+
+// Find minimum AABB which includes both AABBs
+MinMaxAABB AddAABB (const MinMaxAABB& lhs, const MinMaxAABB& rhs);
+
+// Transforms AABB.
+// Can be thought of as Converting OBB to an AABB:
+// rotate the center and extents of the OBB And find the smallest enclosing AABB around it.
+void TransformAABB (const AABB& aabb, const Vector3f& position, const Quaternionf& rotation, AABB& result);
+
+/// This is not mathematically correct for non-uniform scaled objects. But it seems to work well enough.
+/// If you use it with non-uniform scale make sure to verify it extensively.
+EXPORT_COREMODULE void TransformAABB (const AABB& aabb, const Matrix4x4f& transform, AABB& result);
+
+/// This version is much slower but works correctly with non-uniform scale
+void TransformAABBSlow (const AABB& aabb, const Matrix4x4f& transform, AABB& result);
+
+void InverseTransformAABB (const AABB& aabb, const Vector3f& position, const Quaternionf& rotation, AABB& result);
+
+
+/// The closest distance to the surface or inside the aabb.
+float CalculateSqrDistance (const Vector3f& rkPoint, const AABB& rkBox);
+
+
+/// Returns the sqr distance and the closest point inside or on the surface of the aabb.
+/// If inside the aabb, distance will be zero and rkPoint will be returned.
+EXPORT_COREMODULE void CalculateClosestPoint (const Vector3f& rkPoint, const AABB& rkBox, Vector3f& outPoint, float& outSqrDistance);
+
+class MinMaxAABB
+{
+public:
+
+ Vector3f m_Min;
+ Vector3f m_Max;
+
+ MinMaxAABB () { Init (); }
+ MinMaxAABB (Vector3f min, Vector3f max) : m_Min(min), m_Max(max) { };
+ MinMaxAABB (const AABB& aabb) { FromAABB (aabb); }
+ MinMaxAABB& operator = (const AABB& aabb) { FromAABB (aabb); return *this; }
+ //DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (MinMaxAABB)
+
+ void Init ();
+
+ const Vector3f& GetMin () const { return m_Min; }
+ const Vector3f& GetMax () const { return m_Max; }
+ Vector3f GetCenter () const { return 0.5F * (m_Max + m_Min); }
+ Vector3f GetExtent () const { return 0.5F * (m_Max - m_Min); }
+ Vector3f GetSize () const { return (m_Max - m_Min); }
+
+ void Encapsulate (const Vector3f& inPoint);
+ void Encapsulate (const AABB& aabb);
+ void Encapsulate (const MinMaxAABB& other);
+
+ void Expand (float inValue);
+ void Expand (const Vector3f& inOffset);
+
+ // TODO : rename - it has different meaning than AABB::IsValid
+ bool IsValid () const;
+
+ bool IsInside (const Vector3f& inPoint) const;
+
+ void GetVertices( Vector3f outVertices[8] ) const;
+
+private:
+
+ void FromAABB (const AABB& inAABB);
+};
+
+inline void AABB::Expand (float inValue)
+{
+ m_Extent += Vector3f (inValue, inValue, inValue);
+}
+
+inline void AABB::FromMinMaxAABB (const MinMaxAABB& inAABB)
+{
+ m_Center = (inAABB.GetMax () + inAABB.GetMin ()) * 0.5F;
+ m_Extent = (inAABB.GetMax () - inAABB.GetMin ()) * 0.5F;
+}
+
+inline bool AABB::IsValid () const
+{
+ return IsFinite(m_Center) && IsFinite(m_Extent);
+}
+
+inline void MinMaxAABB::Encapsulate (const Vector3f& inPoint)
+{
+ m_Min = min (m_Min, inPoint);
+ m_Max = max (m_Max, inPoint);
+}
+
+inline void MinMaxAABB::Encapsulate (const AABB& aabb)
+{
+ Encapsulate (aabb.GetCenter()+aabb.GetExtent());
+ Encapsulate (aabb.GetCenter()-aabb.GetExtent());
+}
+
+inline void MinMaxAABB::Encapsulate (const MinMaxAABB& other)
+{
+ m_Min = min (m_Min, other.m_Min);
+ m_Max = max (m_Max, other.m_Max);
+}
+
+inline void MinMaxAABB::Expand (float inValue)
+{
+ Vector3f offset = Vector3f (inValue, inValue, inValue);
+ m_Min -= offset;
+ m_Max += offset;
+}
+
+inline void MinMaxAABB::Expand (const Vector3f& inOffset)
+{
+ m_Min -= inOffset;
+ m_Max += inOffset;
+}
+
+inline bool MinMaxAABB::IsValid () const
+{
+ return !(m_Min == Vector3f::infinityVec || m_Max == -Vector3f::infinityVec);
+}
+
+inline void MinMaxAABB::Init ()
+{
+ m_Min = Vector3f::infinityVec;
+ m_Max = -Vector3f::infinityVec;
+}
+
+inline void MinMaxAABB::FromAABB (const AABB& inAABB)
+{
+ m_Min = inAABB.GetCenter () - inAABB.GetExtent ();
+ m_Max = inAABB.GetCenter () + inAABB.GetExtent ();
+}
+template<class TransferFunction> inline
+void AABB::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (m_Center);
+ TRANSFER (m_Extent);
+}
+/*template<class TransferFunction> inline
+void MinMaxAABB::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (m_Min);
+ TRANSFER (m_Max);
+}*/
+
+#endif
diff --git a/Runtime/Geometry/BoundingUtils.cpp b/Runtime/Geometry/BoundingUtils.cpp
new file mode 100644
index 0000000..7c9b361
--- /dev/null
+++ b/Runtime/Geometry/BoundingUtils.cpp
@@ -0,0 +1,443 @@
+#include "UnityPrefix.h"
+#include "BoundingUtils.h"
+#include "Plane.h"
+#include "AABB.h"
+#include "Intersection.h"
+
+// --------------------------------------------------------------------------
+
+void GetFrustumPoints( const Matrix4x4f& clipToWorld, Vector3f* frustum )
+{
+ clipToWorld.PerspectiveMultiplyPoint3( Vector3f(-1,-1,-1), frustum[0] );
+ clipToWorld.PerspectiveMultiplyPoint3( Vector3f( 1,-1,-1), frustum[1] );
+ clipToWorld.PerspectiveMultiplyPoint3( Vector3f( 1, 1,-1), frustum[2] );
+ clipToWorld.PerspectiveMultiplyPoint3( Vector3f(-1, 1,-1), frustum[3] );
+ clipToWorld.PerspectiveMultiplyPoint3( Vector3f(-1,-1, 1), frustum[4] );
+ clipToWorld.PerspectiveMultiplyPoint3( Vector3f( 1,-1, 1), frustum[5] );
+ clipToWorld.PerspectiveMultiplyPoint3( Vector3f( 1, 1, 1), frustum[6] );
+ clipToWorld.PerspectiveMultiplyPoint3( Vector3f(-1, 1, 1), frustum[7] );
+}
+
+void GetFrustumPortion( const Vector3f* frustum, float nearSplit, float farSplit, Vector3f* outPortion )
+{
+ outPortion[0] = Lerp( frustum[0], frustum[0+4], nearSplit );
+ outPortion[1] = Lerp( frustum[1], frustum[1+4], nearSplit );
+ outPortion[2] = Lerp( frustum[2], frustum[2+4], nearSplit );
+ outPortion[3] = Lerp( frustum[3], frustum[3+4], nearSplit );
+ outPortion[4] = Lerp( frustum[0], frustum[0+4], farSplit );
+ outPortion[5] = Lerp( frustum[1], frustum[1+4], farSplit );
+ outPortion[6] = Lerp( frustum[2], frustum[2+4], farSplit );
+ outPortion[7] = Lerp( frustum[3], frustum[3+4], farSplit );
+}
+
+static bool ClipTest( const float p, const float q, float& u1, float& u2 )
+{
+ // Return value is 'true' if line segment intersects the current test
+ // plane. Otherwise 'false' is returned in which case the line segment
+ // is entirely clipped.
+ const float EPS = 1.0e-10f;
+ if( p < -EPS ) {
+ float r = q/p;
+ if( r > u2 )
+ return false;
+ else {
+ if( r > u1 )
+ u1 = r;
+ return true;
+ }
+ }
+ else if( p > EPS )
+ {
+ float r = q/p;
+ if( r < u1 )
+ return false;
+ else {
+ if( r < u2 )
+ u2 = r;
+ return true;
+ }
+ }
+ else
+ {
+ return q >= 0.0f;
+ }
+}
+
+static bool IntersectLineAABB( Vector3f& v, const Vector3f& p, const Vector3f& dir, const MinMaxAABB& b )
+{
+ float t1 = 0.0f;
+ float t2 = 1.0e30f;
+ bool intersect = false;
+ if (ClipTest(-dir.z,p.z-b.GetMin().z,t1,t2) && ClipTest(dir.z,b.GetMax().z-p.z,t1,t2) &&
+ ClipTest(-dir.y,p.y-b.GetMin().y,t1,t2) && ClipTest(dir.y,b.GetMax().y-p.y,t1,t2) &&
+ ClipTest(-dir.x,p.x-b.GetMin().x,t1,t2) && ClipTest(dir.x,b.GetMax().x-p.x,t1,t2))
+ {
+ if( 0 <= t1 ) {
+ v = p + dir * t1;
+ intersect = true;
+ }
+ if( 0 <= t2 ) {
+ v = p + dir * t2;
+ intersect = true;
+ }
+ }
+ return intersect;
+}
+
+inline bool ClipPolysByPlane( UInt8 numPoints, const Vector3f* __restrict input, const Plane& A, UInt8* __restrict pNumOutPoints, Vector3f* __restrict output, UInt8* __restrict pNumIntermPoints, Vector3f* __restrict interm )
+{
+ int i;
+ if( numPoints < 3 )
+ {
+ *pNumOutPoints = 0;
+ return false;
+ }
+
+ UInt8 numOutPoints = 0;
+ UInt8& numIntermPoints = *pNumIntermPoints;
+
+ Vector3f temp;
+
+ bool * outside = (bool*)alloca(numPoints*sizeof(bool));
+ for( i = 0; i < numPoints; ++i )
+ outside[i] = A.GetDistanceToPoint( input[i] ) < 0.0f;
+
+ for( i = 0; i < numPoints; ++i )
+ {
+ int idNext = (i+1) % numPoints;
+
+ // both outside -> save none
+ if(outside[i] && outside[idNext])
+ continue;
+
+ // outside-inside -> save intersection and i+1
+ if(outside[i])
+ {
+ if( IntersectSegmentPlane( input[i], input[idNext], A, &temp ) )
+ {
+ output[numOutPoints++] = temp;
+ interm[numIntermPoints++] = temp;
+ }
+
+ output[numOutPoints++] = input[idNext];
+ continue;
+ }
+
+ // inside-outside -> save intersection
+ if(outside[idNext])
+ {
+ if( IntersectSegmentPlane( input[i], input[idNext], A, &temp ) )
+ {
+ output[numOutPoints++] = temp;
+ interm[numIntermPoints++] = temp;
+ }
+
+ continue;
+ }
+
+ output[numOutPoints++] = input[idNext];
+ }
+ *pNumOutPoints = numOutPoints;
+ return numOutPoints ? true : false;
+}
+
+void CalcHullBounds(const Vector3f* __restrict hullPoints, const UInt8* __restrict hullCounts, UInt8 hullFaces, const Plane& nearPlane, const Matrix4x4f& cameraWorldToClip, MinMaxAABB& aabb)
+{
+ Vector3f outputData[128];
+ Vector3f intermData[128];
+
+ UInt8 outputDataCounts[64];
+ UInt8 intermDataCounts[64];
+
+ const Vector3f* __restrict inputPoints = hullPoints;
+ const UInt8* __restrict inputCounts = hullCounts;
+
+ Vector3f* __restrict outputPoints = outputData;
+ UInt8* __restrict outputCounts = outputDataCounts;
+ intermDataCounts[0] = 0;
+ for( UInt8 i = 0; i != hullFaces; ++i )
+ {
+ const UInt8 inputCount = *inputCounts++;
+ ClipPolysByPlane( inputCount, inputPoints, nearPlane, outputCounts, outputPoints, intermDataCounts, intermData);
+ inputPoints += inputCount;
+ outputPoints += *outputCounts;
+ outputCounts++;
+ }
+
+ // Project hull's points onto the screen and compute bounding rectangle of them.
+ Vector3f projectedPoint;
+ outputPoints = outputData;
+ outputCounts = outputDataCounts;
+
+ for( int f = 0; f < hullFaces; ++f )
+ {
+ const UInt8 numPoints = *outputCounts++;
+ for( int i = 0; i < numPoints; ++i )
+ {
+ cameraWorldToClip.PerspectiveMultiplyPoint3( outputPoints[i], projectedPoint );
+ aabb.Encapsulate( projectedPoint );
+ }
+ outputPoints += numPoints;
+ }
+
+ if( aabb.m_Min.x < -1.0f ) aabb.m_Min.x = -1.0f;
+ if( aabb.m_Min.y < -1.0f ) aabb.m_Min.y = -1.0f;
+ if( aabb.m_Max.x > 1.0f ) aabb.m_Max.x = 1.0f;
+ if( aabb.m_Max.y > 1.0f ) aabb.m_Max.y = 1.0f;
+
+}
+
+
+// Returns our "focused region of interest": frustum, clipped by scene bounds,
+// extruded towards light and clipped by scene bounds again.
+//
+// Frustum is
+// { -1, -1, -1 }
+// { 1, -1, -1 }
+// { 1, 1, -1 }
+// { -1, 1, -1 }
+// { -1, -1, 1 }
+// { 1, -1, 1 }
+// { 1, 1, 1 }
+// { -1, 1, 1 }
+
+
+
+void CalculateFocusedLightHull( const Vector3f* frustum, const Vector3f& lightDir, const MinMaxAABB& sceneAABB, std::vector<Vector3f>& points )
+{
+ int i;
+ Vector3f tempPoints[3][256];
+ UInt8 tempCounts[3][128];
+
+ Plane planes[6];
+ planes[0].SetABCD( 0, 1, 0, -sceneAABB.GetMin().y );
+ planes[1].SetABCD( 0, -1, 0, sceneAABB.GetMax().y );
+ planes[2].SetABCD( 1, 0, 0, -sceneAABB.GetMin().x );
+ planes[3].SetABCD( -1, 0, 0, sceneAABB.GetMax().x );
+ planes[4].SetABCD( 0, 0, 1, -sceneAABB.GetMin().z );
+ planes[5].SetABCD( 0, 0, -1, sceneAABB.GetMax().z );
+
+ Vector3f* __restrict pData[2] = { (Vector3f*)&tempPoints[0][0], (Vector3f*)&tempPoints[1][0] };
+ UInt8* __restrict pDataCounts[2] = { (UInt8*)&tempCounts[0][0], (UInt8*)&tempCounts[1][0] };
+
+ Vector3f* v = *pData;
+ UInt8* c = *pDataCounts;
+
+ UInt32 numFaces = 6;
+
+ c[0] = c[1] = c[2] = c[3] = c[4] = c[5] = 4;
+ v[ 0] = frustum[0]; v[ 1] = frustum[1]; v[ 2] = frustum[2]; v[ 3] = frustum[3];
+ v[ 4] = frustum[7]; v[ 5] = frustum[6]; v[ 6] = frustum[5]; v[ 7] = frustum[4];
+ v[ 8] = frustum[0]; v[ 9] = frustum[3]; v[10] = frustum[7]; v[11] = frustum[4];
+ v[12] = frustum[1]; v[13] = frustum[5]; v[14] = frustum[6]; v[15] = frustum[2];
+ v[16] = frustum[4]; v[17] = frustum[5]; v[18] = frustum[1]; v[19] = frustum[0];
+ v[20] = frustum[6]; v[21] = frustum[7]; v[22] = frustum[3]; v[23] = frustum[2];
+
+
+ UInt32 numTotalPoints;
+ UInt32 vIn = 0;
+
+
+ Vector3f* intermPoints = &tempPoints[2][0];
+ UInt8* intermCounts = &tempCounts[2][0];
+
+ for( int p = 0; p < 6; ++p )
+ {
+ const Vector3f* __restrict inputPoints=pData[vIn];
+ Vector3f* __restrict outputPoints = pData[1-vIn];
+
+ UInt8* __restrict inputCounts=pDataCounts[vIn];
+ UInt8* __restrict outputCounts = pDataCounts[1-vIn];
+
+ numTotalPoints = 0;
+ *intermCounts = 0;
+
+ UInt32 faceCount = numFaces;
+ for( i = 0; i < faceCount; ++i )
+ {
+ const UInt8 numInputPoints = *inputCounts;
+ if(ClipPolysByPlane(numInputPoints, inputPoints, planes[p], outputCounts, outputPoints, intermCounts, intermPoints))
+ {
+ const UInt8 outputCount = *outputCounts++;
+ numTotalPoints+=outputCount;
+ outputPoints += outputCount;
+ }
+ else
+ {
+ if(0 == (--numFaces))
+ break;
+ }
+
+ inputCounts++;
+ inputPoints += numInputPoints;
+ }
+ vIn = 1-vIn; // anyone for ping-pong ?
+
+ // add an extra face built from all the intersection points so it catches all the edges.
+ const UInt8 numIntermPoints = *intermCounts;
+ if(numIntermPoints && (p<5))
+ {
+ numFaces++;
+ *outputCounts = numIntermPoints;
+ memcpy(outputPoints, intermPoints, numIntermPoints * sizeof(Vector3f));
+ }
+ }
+
+ if(numFaces)
+ { // output the clipped points
+
+ Vector3f pt;
+ Vector3f ld = -lightDir;
+ points.reserve(numTotalPoints << 1); // worst case scenario
+ Vector3f* __restrict inputPoints=pData[vIn];
+ UInt8* __restrict inputCounts=pDataCounts[vIn];
+
+ for( i = 0; i < numFaces; ++i )
+ {
+ const UInt8 numPoints = *inputCounts++;
+ for(UInt8 k=0;k!=numPoints;k++)
+ {
+ const Vector3f& v = inputPoints[k];
+ points.push_back(v);
+ if( IntersectLineAABB( pt, v, ld, sceneAABB ) )
+ points.push_back( pt );
+ }
+ inputPoints += numPoints;
+ }
+ }
+}
+
+void CalculateBoundingSphereFromFrustumPoints(const Vector3f points[8], Vector3f& outCenter, float& outRadius)
+{
+ Vector3f spherePoints[4];
+ spherePoints[0] = points[0];
+ spherePoints[1] = points[3];
+ spherePoints[2] = points[5];
+ spherePoints[3] = points[7];
+
+ // Is bounding sphere at the far or near plane?
+ for( int plane = 1; plane >= 0; --plane )
+ {
+ Vector3f pointA = spherePoints[plane*2];
+ Vector3f pointB = spherePoints[plane*2 + 1];
+ Vector3f center = (pointA + pointB) * 0.5f;
+ float radius2 = SqrMagnitude(pointA - center);
+ Vector3f pointC = spherePoints[(1-plane)*2];
+ Vector3f pointD = spherePoints[(1-plane)*2 + 1];
+
+ // Check if all points are inside sphere
+ if( SqrMagnitude(pointC - center) <= radius2 &&
+ SqrMagnitude(pointD - center) <= radius2 )
+ {
+ outCenter = center;
+ outRadius = sqrt(radius2);
+ return;
+ }
+ }
+
+ // Sphere touches all four frustum points
+ CalculateSphereFrom4Points(spherePoints, outCenter, outRadius);
+}
+
+void CalculateSphereFrom4Points(const Vector3f points[4], Vector3f& outCenter, float& outRadius)
+{
+ Matrix4x4f mat;
+
+ for( int i = 0; i < 4; ++i )
+ {
+ mat.Get(i, 0) = points[i].x;
+ mat.Get(i, 1) = points[i].y;
+ mat.Get(i, 2) = points[i].z;
+ mat.Get(i, 3) = 1;
+ }
+ float m11 = mat.GetDeterminant();
+
+ for( int i = 0; i < 4; ++i )
+ {
+ mat.Get(i, 0) = points[i].x*points[i].x + points[i].y*points[i].y + points[i].z*points[i].z;
+ mat.Get(i, 1) = points[i].y;
+ mat.Get(i, 2) = points[i].z;
+ mat.Get(i, 3) = 1;
+ }
+ float m12 = mat.GetDeterminant();
+
+ for( int i = 0; i < 4; ++i )
+ {
+ mat.Get(i, 0) = points[i].x;
+ mat.Get(i, 1) = points[i].x*points[i].x + points[i].y*points[i].y + points[i].z*points[i].z;
+ mat.Get(i, 2) = points[i].z;
+ mat.Get(i, 3) = 1;
+ }
+ float m13 = mat.GetDeterminant();
+
+ for( int i = 0; i < 4; ++i )
+ {
+ mat.Get(i, 0) = points[i].x;
+ mat.Get(i, 1) = points[i].y;
+ mat.Get(i, 2) = points[i].x*points[i].x + points[i].y*points[i].y + points[i].z*points[i].z;
+ mat.Get(i, 3) = 1;
+ }
+ float m14 = mat.GetDeterminant();
+
+ for( int i = 0; i < 4; ++i )
+ {
+ mat.Get(i, 0) = points[i].x*points[i].x + points[i].y*points[i].y + points[i].z*points[i].z;
+ mat.Get(i, 1) = points[i].x;
+ mat.Get(i, 2) = points[i].y;
+ mat.Get(i, 3) = points[i].z;
+ }
+ float m15 = mat.GetDeterminant();
+
+ Vector3f c;
+ c.x = 0.5 * m12 / m11;
+ c.y = 0.5 * m13 / m11;
+ c.z = 0.5 * m14 / m11;
+ outRadius = sqrt(c.x*c.x + c.y*c.y + c.z*c.z - m15/m11);
+ outCenter = c;
+}
+
+void CalculateSpotLightBounds (const float range, const float cotanHalfSpotAngle, const Matrix4x4f& lightMatrix, SpotLightBounds& outBounds)
+{
+ float sideLength = range / cotanHalfSpotAngle;
+ outBounds.points[0] = lightMatrix.GetPosition();
+ outBounds.points[1] = lightMatrix.MultiplyPoint3( Vector3f(-sideLength,-sideLength, range) );
+ outBounds.points[2] = lightMatrix.MultiplyPoint3( Vector3f( sideLength,-sideLength, range) );
+ outBounds.points[3] = lightMatrix.MultiplyPoint3( Vector3f( sideLength, sideLength, range) );
+ outBounds.points[4] = lightMatrix.MultiplyPoint3( Vector3f(-sideLength, sideLength, range) );
+}
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Math/Random/rand.h"
+
+SUITE (BoundingUtilsTest)
+{
+TEST(BoundingUtilsTest_CalculateSphereFrom4Points)
+{
+ Rand rnd(123);
+ for( int i = 0; i < 10; ++i )
+ {
+ Vector3f points[4];
+ for( int j = 0; j < 4; ++j )
+ {
+ points[j].x = rnd.GetSignedFloat()*100.f;
+ points[j].y = rnd.GetSignedFloat()*100.f;
+ points[j].z = rnd.GetSignedFloat()*100.f;
+ }
+ Vector3f center;
+ float radius;
+ CalculateSphereFrom4Points(points, center, radius);
+ for( int j = 0; j < 4; ++j )
+ {
+ float dist = Magnitude(points[j] - center);
+ float ratio = dist / radius;
+ // Radius may be large compared to input range, avoid comparing absolute distances
+ bool correct = (ratio > 0.999f) && (ratio < 1.001f);
+ CHECK(correct);
+ }
+ }
+}
+}
+
+#endif
diff --git a/Runtime/Geometry/BoundingUtils.h b/Runtime/Geometry/BoundingUtils.h
new file mode 100644
index 0000000..5338706
--- /dev/null
+++ b/Runtime/Geometry/BoundingUtils.h
@@ -0,0 +1,30 @@
+#pragma once
+
+
+#include "Runtime/Math/Vector3.h"
+#include <vector>
+
+class MinMaxAABB;
+class Plane;
+class Matrix4x4f;
+
+struct SpotLightBounds
+{
+ enum { kPointCount = 5 };
+ Vector3f points[kPointCount];
+};
+
+// 7-----6 far
+// / /|
+// 3-----2 |
+// | 4 | 5
+// | |/
+// 0-----1 near
+
+void GetFrustumPoints( const Matrix4x4f& clipToWorld, Vector3f* frustum );
+void GetFrustumPortion( const Vector3f* frustum, float nearSplit, float farSplit, Vector3f* outPortion );
+void CalcHullBounds(const Vector3f* __restrict hullPoints, const UInt8* __restrict hullCounts, UInt8 hullFaces, const Plane& nearPlane, const Matrix4x4f& cameraWorldToClip, MinMaxAABB& aabb);
+void CalculateFocusedLightHull( const Vector3f* frustum, const Vector3f& lightDir, const MinMaxAABB& sceneAABB, std::vector<Vector3f>& points );
+void CalculateBoundingSphereFromFrustumPoints(const Vector3f points[8], Vector3f& outCenter, float& outRadius);
+void CalculateSphereFrom4Points(const Vector3f points[4], Vector3f& outCenter, float& outRadius);
+void CalculateSpotLightBounds (const float range, const float cotanHalfSpotAngle, const Matrix4x4f& lightMatrix, SpotLightBounds& outBounds);
diff --git a/Runtime/Geometry/BoundingVolumeConversion.h b/Runtime/Geometry/BoundingVolumeConversion.h
new file mode 100644
index 0000000..059c163
--- /dev/null
+++ b/Runtime/Geometry/BoundingVolumeConversion.h
@@ -0,0 +1,32 @@
+#ifndef BOUNDINGVOLUMECONVERSION_H
+#define BOUNDINGVOLUMECONVERSION_H
+
+#include "AABB.h"
+#include "Sphere.h"
+
+inline void AABBToSphere (const AABB& a, Sphere& s)
+{
+ s.GetCenter () = a.GetCenter ();
+ s.GetRadius () = Magnitude (a.GetExtent ());
+}
+
+inline void MinMaxAABBToSphere (const MinMaxAABB& a, Sphere& s)
+{
+ AABB aabb = a;
+ AABBToSphere (aabb, s);
+}
+
+inline void SphereToAABB (const Sphere& s, AABB& a)
+{
+ a.GetCenter () = s.GetCenter ();
+ a.GetExtent () = Vector3f (s.GetRadius (), s.GetRadius (), s.GetRadius ());
+}
+
+inline void SphereToMinMaxAABB (const Sphere& s, MinMaxAABB& minmax)
+{
+ AABB aabb;
+ SphereToAABB (s, aabb);
+ minmax = aabb;
+}
+
+#endif
diff --git a/Runtime/Geometry/ComputionalGeometry.cpp b/Runtime/Geometry/ComputionalGeometry.cpp
new file mode 100644
index 0000000..5b979a5
--- /dev/null
+++ b/Runtime/Geometry/ComputionalGeometry.cpp
@@ -0,0 +1,304 @@
+#include "UnityPrefix.h"
+#include "ComputionalGeometry.h"
+#include "Runtime/Math/FloatConversion.h"
+#include "Runtime/Math/Vector2.h"
+#include "Plane.h"
+#include <cmath>
+
+float SignedTriangleArea2D (const Vector2f& a, const Vector2f& b, const Vector2f& c)
+{
+ float i01 = (b.x - a.x) * (b.y + a.y);
+ float i12 = (c.x - b.x) * (c.y + b.y);
+ float i20 = (a.x - c.x) * (a.y + c.y);
+
+ return (i01 + i12 + i20) * 0.5F;
+}
+
+float SignedTriangleArea2D (const Vector3f* v)
+{
+ float i01 = (v[1].x - v[0].x) * (v[1].y + v[0].y);
+ float i12 = (v[2].x - v[1].x) * (v[2].y + v[1].y);
+ float i20 = (v[0].x - v[2].x) * (v[0].y + v[2].y);
+
+ return (i01 + i12 + i20) * 0.5F;
+}
+
+float TriangleArea3D (const Vector3f& a, const Vector3f& b, const Vector3f& c)
+{
+ return Magnitude (CalcRawNormalFromTriangle (a, b, c)) * 0.5F;
+}
+
+
+float CalculateProjectedBoxArea2D (const Vector3f* v)
+{
+ float i01 = (v[1].x - v[0].x) * (v[1].y + v[0].y);
+ float i13 = (v[3].x - v[1].x) * (v[3].y + v[1].y);
+ float i20 = (v[0].x - v[2].x) * (v[0].y + v[2].y);
+ float i37 = (v[7].x - v[3].x) * (v[7].y + v[3].y);
+ float i62 = (v[2].x - v[6].x) * (v[2].y + v[6].y);
+ float i23 = (v[3].x - v[2].x) * (v[3].y + v[2].y);
+ float i45 = (v[5].x - v[4].x) * (v[5].y + v[4].y);
+ float i57 = (v[7].x - v[5].x) * (v[7].y + v[5].y);
+ float i76 = (v[6].x - v[7].x) * (v[6].y + v[7].y);
+ float i64 = (v[4].x - v[6].x) * (v[4].y + v[6].y);
+ float i15 = (v[5].x - v[1].x) * (v[5].y + v[1].y);
+ float i40 = (v[0].x - v[4].x) * (v[0].y + v[4].y);
+
+ float area
+ = Abs (
+ i01
+ + i13
+ - i23
+ + i20);
+
+ area += Abs (
+ i23
+ + i37
+ + i76
+ + i62);
+
+ area += Abs (
+ i45
+ + i57
+ + i76
+ + i64);
+
+ area += Abs (
+ i01
+ + i15
+ - i45
+ + i40);
+
+ area += Abs (
+ i15
+ + i57
+ - i37
+ - i13);
+
+ area += Abs (
+ - i40
+ - i64
+ + i62
+ + i20);
+
+ return area * 0.5F;
+}
+
+float CalculateBoxAreaRadialFrustum (
+ Vector3f* v,
+ float inFovy,
+ float inNear,
+ float inFar,
+ float inScreenHeight)
+{
+ UInt32 i;
+ double cotan, w, distance;
+ Vector3f projectedTriangle[8];
+
+ cotan = Deg2Rad (inFovy / 2.0F);
+ cotan = cos (cotan) / sin (cotan);
+
+ for (i=0;i<8;i++)
+ {
+ projectedTriangle[i] = v[i];
+ distance = Magnitude (projectedTriangle[i]);
+
+ w = (inFar + inNear) / (inFar - inNear) * distance
+ + 2.0F * inNear * inFar / (inFar - inNear);
+ w = 1.0F / w * cotan * inScreenHeight / 2.0F;
+
+ projectedTriangle[i].x *= w;
+ projectedTriangle[i].y *= w;
+ }
+
+ return CalculateProjectedBoxArea2D (projectedTriangle);
+}
+
+//float a = (inFar + inNear) / (inFar - inNear);
+//float b = 2.0F * inNear * inFar / (inFar - inNear);
+//float c = cotan * inScreenHeight / 2.0F;
+
+float CalculateBoxAreaRadialFrustum2 (Vector3f* v, float a, float b, float c)
+{
+ UInt32 i;
+ float w, distance;
+ Vector3f projectedTriangle[8];
+
+ for (i=0;i<8;i++)
+ {
+ projectedTriangle[i] = v[i];
+ distance = Magnitude (projectedTriangle[i]);
+
+ w = a * distance + b;
+ w = 1.0F / w * c;
+
+ projectedTriangle[i].x *= w;
+ projectedTriangle[i].y *= w;
+ }
+
+ return CalculateProjectedBoxArea2D (projectedTriangle);
+
+}
+
+float CalculateTriangleAreaRadialFrustum (
+ Vector3f* v,
+ float inFovy,
+ float inNear,
+ float inFar,
+ float inScreenHeight)
+{
+ UInt32 i;
+ double cotan, w, distance;
+ Vector3f projectedTriangle[3];
+
+ cotan = Deg2Rad (inFovy / 2.0F);
+ cotan = cos (cotan) / sin (cotan);
+
+ for (i=0;i<3;i++)
+ {
+ projectedTriangle[i] = v[i];
+ distance = Magnitude (projectedTriangle[i]);
+
+ w = (inFar + inNear) / (inFar - inNear) * distance
+ + 2.0F * inNear * inFar / (inFar - inNear);
+ w = 1.0F / w;
+
+ projectedTriangle[i].x *= w * cotan * inScreenHeight / 2.0F;
+ projectedTriangle[i].y *= w * cotan * inScreenHeight / 2.0F;
+ }
+
+ return Abs (SignedTriangleArea2D (projectedTriangle));
+}
+//float a = (inFar + inNear) / (inFar - inNear);
+//float b = 2.0F * inNear * inFar / (inFar - inNear);
+//float c = cotan * inScreenHeight / 2.0F;
+
+float CalculateTriangleAreaRadialFrustum2 (Vector3f* v, float a, float b, float c)
+{
+ UInt32 i;
+ float w, distance;
+ Vector3f projectedTriangle[3];
+
+ for (i=0;i<3;i++)
+ {
+ projectedTriangle[i] = v[i];
+ distance = Magnitude (projectedTriangle[i]);
+
+ w = a * distance + b;
+ w = 1.0F / w * c;
+
+ projectedTriangle[i].x *= w;
+ projectedTriangle[i].y *= w;
+ }
+
+ return Abs (SignedTriangleArea2D (projectedTriangle));
+}
+
+float CalculateTriangleAreaRotationless (Vector3f* v, float inFovy, float inScreenWidth, float inScreenHeight, Vector3f& viewPoint)
+{
+ Vector3f normal = Normalize (Cross (v[0] - v[1], v[1] - v[2]));
+ float objectSpaceArea = Magnitude (Cross (v[1] - v[0], v[2] - v[0])) * 0.5F;
+ Vector3f centroid = (v[0] + v[1] + v[2]) / 3.0F;
+ Vector3f difference = viewPoint - centroid;
+ float distance = Magnitude (difference);
+ // We want 1.0 when it faces the tri directly -> 0 degrees
+ // We want 0.0 when the tri is invisible -> 90 degrees
+ // It doesn't matter if the tri faces away from the viewer or not.
+ float angle = Abs (Dot (normal, (difference / distance)));
+
+ float screenSpaceArea =
+ objectSpaceArea
+ * sin (Deg2Rad (inFovy * 0.5F))
+ * inScreenWidth
+ * inScreenHeight
+ * sin (Deg2Rad (inFovy * 0.5F))
+ * angle
+ / distance;
+
+ return screenSpaceArea;
+}
+
+int ClipPolygonAgainstPlane(int vertexCount, const Vector3f *vertex, const Plane& plane, char *location, Vector3f *result)
+{
+ const float kBoundaryEpsilon = 1.0e-3F;
+
+ enum
+ {
+ kPolygonInterior = 1, //## The point lies in the interior of the polygon.
+ kPolygonBoundary = 0, //## The point lies on or very near the boundary of the polygon.
+ kPolygonExterior = -1 //## The point lies outside the polygon.
+ };
+
+ int positive = 0;
+ int negative = 0;
+
+ for (int a = 0; a < vertexCount; a++)
+ {
+ float d = plane.GetDistanceToPoint(vertex[a]);
+ if (d > kBoundaryEpsilon)
+ {
+ location[a] = kPolygonInterior;
+ positive++;
+ }
+ else
+ {
+ if (d < -kBoundaryEpsilon)
+ {
+ location[a] = kPolygonExterior;
+ negative++;
+ }
+ else
+ {
+ location[a] = kPolygonBoundary;
+ }
+ }
+ }
+
+ if (negative == 0)
+ {
+ for (int a = 0; a < vertexCount; a++)
+ result[a] = vertex[a];
+ return vertexCount;
+ }
+ else if (positive == 0)
+ {
+ return 0;
+ }
+
+ int count = 0;
+ int previous = vertexCount - 1;
+ for (int index = 0; index < vertexCount; index++)
+ {
+ int loc = location[index];
+ if (loc == kPolygonExterior)
+ {
+ if (location[previous] == kPolygonInterior)
+ {
+ const Vector3f& v1 = vertex[previous];
+ const Vector3f& v2 = vertex[index];
+ Vector3f dv = v2 - v1;
+
+ float t = plane.GetDistanceToPoint(v2) / plane.GetDistanceToPoint(dv);
+ result[count++] = v2 - dv * t;
+ }
+ }
+ else
+ {
+ const Vector3f& v1 = vertex[index];
+ if ((loc == kPolygonInterior) && (location[previous] == kPolygonExterior))
+ {
+ const Vector3f& v2 = vertex[previous];
+ Vector3f dv = v2 - v1;
+
+ float t = plane.GetDistanceToPoint(v2) / plane.GetDistanceToPoint(dv);
+ result[count++] = v2 - dv * t;
+ }
+
+ result[count++] = v1;
+ }
+
+ previous = index;
+ }
+
+ return count;
+}
diff --git a/Runtime/Geometry/ComputionalGeometry.h b/Runtime/Geometry/ComputionalGeometry.h
new file mode 100644
index 0000000..42f5e47
--- /dev/null
+++ b/Runtime/Geometry/ComputionalGeometry.h
@@ -0,0 +1,19 @@
+#ifndef COMPUTIONALGEOMETRY_H
+#define COMPUTIONALGEOMETRY_H
+
+class Vector2f;
+class Vector3f;
+class Plane;
+
+float SignedTriangleArea2D (const Vector2f& a, const Vector2f& b, const Vector2f& c);
+float SignedTriangleArea2D (const Vector3f* v);
+float TriangleArea3D (const Vector3f& a, const Vector3f& b, const Vector3f& c);
+float CalculateProjectedBoxArea2D (const Vector3f* v);
+float CalculateTriangleAreaRotationless (Vector3f* v, float inFovy, float inScreenWidth, float inScreenHeight, Vector3f& viewPoint);
+float CalculateTriangleAreaRadialFrustum (Vector3f* v, float inFovy, float inNear, float inFar, float inScreenHeight);
+float CalculateBoxAreaRadialFrustum (Vector3f* v, float inFovy, float inNear, float inFar, float inScreenHeight);
+float CalculateBoxAreaRadialFrustum2 (Vector3f* v, float a, float b, float c);
+float CalculateTriangleAreaRadialFrustum2 (Vector3f* v, float a, float b, float c);
+int ClipPolygonAgainstPlane(int vertexCount, const Vector3f *vertex, const Plane& plane, char *location, Vector3f *result);
+
+#endif
diff --git a/Runtime/Geometry/Intersection.cpp b/Runtime/Geometry/Intersection.cpp
new file mode 100644
index 0000000..d1ae9b9
--- /dev/null
+++ b/Runtime/Geometry/Intersection.cpp
@@ -0,0 +1,940 @@
+#include "UnityPrefix.h"
+#include "Intersection.h"
+#include "Ray.h"
+#include "Plane.h"
+#include "Sphere.h"
+#include "AABB.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "TriTriIntersect.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Camera/CullingParameters.h"
+
+
+using namespace std;
+
+bool IntersectRayTriangle (const Ray& ray, const Vector3f& a, const Vector3f& b, const Vector3f& c)
+{
+ float t;
+ return IntersectRayTriangle (ray, a, b, c, &t);
+}
+
+bool IntersectRayTriangle (const Ray& ray, const Vector3f& a, const Vector3f& b, const Vector3f& c, float* outT)
+{
+ const float kMinDet = 1e-6f;
+
+ float t, u, v;
+ Vector3f edge1, edge2, tvec, pvec, qvec;
+ float det, inv_det;
+
+ /* find vectors for two edges sharing vert0 */
+ edge1 = b - a;
+ edge2 = c - a;
+
+ /* begin calculating determinant - also used to calculate U parameter */
+ pvec = Cross (ray.GetDirection (), edge2);
+
+ /* if determinant is near zero, ray lies in plane of triangle */
+ det = Dot (edge1, pvec);
+
+ if (Abs (det) < kMinDet)
+ return false;
+
+ inv_det = 1.0F / det;
+
+ /* calculate distance from vert0 to ray origin */
+ tvec = ray.GetOrigin () - a;
+
+ /* calculate U parameter and test bounds */
+ u = Dot (tvec, pvec) * inv_det;
+ if (u < 0.0F || u > 1.0F)
+ return false;
+
+ /* prepare to test V parameter */
+ qvec = Cross (tvec, edge1);
+
+ /* calculate V parameter and test bounds */
+ v = Dot (ray.GetDirection (), qvec) * inv_det;
+ if (v < 0.0F || u + v > 1.0F)
+ return false;
+
+ t = Dot (edge2, qvec) * inv_det;
+ if (t < 0.0F)
+ return false;
+ *outT = t;
+
+ return true;
+}
+
+bool IntersectRaySphere (const Ray& ray, const Sphere& inSphere)
+{
+ Vector3f dif = inSphere.GetCenter () - ray.GetOrigin ();
+ float d = Dot (dif, ray.GetDirection ());
+ float lSqr = Dot (dif, dif);
+ float rSqr = Sqr (inSphere.GetRadius ());
+
+ if (d < 0.0F && lSqr > rSqr)
+ return false;
+
+ float mSqr = lSqr - Sqr (d);
+
+ if (mSqr > rSqr)
+ return false;
+ else
+ return true;
+}
+/*
+bool IntersectRaySphere (const Ray& ray, const Sphere& inSphere, float* t)
+{
+ AssertIf (t == NULL);
+
+ Vector3f dif = inSphere.GetCenter () - ray.GetOrigin ();
+ float d = Dot (dif, ray.GetDirection ());
+ float lSqr = Dot (dif, dif);
+ float rSqr = Sqr (inSphere.GetRadius ());
+
+ if (d < 0.0F && lSqr > rSqr)
+ return false;
+
+ float mSqr = lSqr - Sqr (d);
+
+ if (mSqr > rSqr)
+ return false;
+
+ float q = sqrt (rSqr - mSqr);
+
+ // ray.origin is inside the ray so a negative intersection will be returned
+ *t = d - q;
+
+ return true;
+}
+*/
+bool IntersectRaySphere (const Ray& ray, const Sphere& inSphere, float* t0, float* t1)
+{
+ AssertIf (t0 == NULL);
+ AssertIf (t1 == NULL);
+
+ Vector3f dif = inSphere.GetCenter () - ray.GetOrigin ();
+ float d = Dot (dif, ray.GetDirection ());
+ float lSqr = Dot (dif, dif);
+ float rSqr = Sqr (inSphere.GetRadius ());
+
+ if (d < 0.0F && lSqr > rSqr)
+ return false;
+
+ float mSqr = lSqr - Sqr (d);
+
+ if (mSqr > rSqr)
+ return false;
+
+ float q = sqrt (rSqr - mSqr);
+
+ *t0 = d - q;
+ *t1 = d + q;
+
+ return true;
+}
+
+bool IntersectRayAABB (const Ray& ray, const AABB& inAABB)
+{
+ float t0, t1;
+ return IntersectRayAABB (ray, inAABB, &t0, &t1);
+}
+/*
+bool IntersectRayAABB (const Ray& ray, const AABB& inAABB, float* outT)
+{
+ float tmin = -Vector3f::infinity;
+ float tmax = Vector3f::infinity;
+
+ float t0, t1, f;
+
+ Vector3f p = inAABB.GetCenter () - ray.GetOrigin ();
+ Vector3f extent = inAABB.GetExtent ();
+ long i;
+ for (i=0;i<3;i++)
+ {
+ // ray and plane are paralell so no valid intersection can be found
+ //if (Abs (ray.GetDirection ()[i]) > Vector3f::epsilon)
+ {
+ f = 1.0F / ray.GetDirection ()[i];
+ t0 = (p[i] + extent[i]) * f;
+ t1 = (p[i] - extent[i]) * f;
+ // Ray leaves on Right, Top, Back Side
+ if (t0 < t1)
+ {
+ if (t0 > tmin)
+ tmin = t0;
+
+ if (t1 < tmax)
+ tmax = t1;
+
+ if (tmin > tmax)
+ return false;
+
+ if (tmax < 0.0F)
+ return false;
+ }
+ // Ray leaves on Left, Bottom, Front Side
+ else
+ {
+ if (t1 > tmin)
+ tmin = t1;
+
+ if (t0 < tmax)
+ tmax = t0;
+
+ if (tmin > tmax)
+ return false;
+
+ if (tmax < 0.0F)
+ return false;
+ }
+ }
+ }
+
+ if (tmin > 0.0F)
+ *outT = tmin;
+ // ray starts inside the aabb
+ else
+ *outT = 0.0F;
+
+ AssertIf (*outT < 0.0F);
+ return true;
+}
+*/
+
+bool IntersectRayAABB (const Ray& ray, const AABB& inAABB, float* outT0)
+{
+ float t1;
+ return IntersectRayAABB (ray, inAABB, outT0, &t1);
+}
+
+bool IntersectRayAABB (const Ray& ray, const AABB& inAABB, float* outT0, float* outT1)
+{
+ float tmin = -Vector3f::infinity;
+ float tmax = Vector3f::infinity;
+
+ float t0, t1, f;
+
+ Vector3f p = inAABB.GetCenter () - ray.GetOrigin ();
+ Vector3f extent = inAABB.GetExtent ();
+ long i;
+ for (i=0;i<3;i++)
+ {
+ // ray and plane are paralell so no valid intersection can be found
+ {
+ f = 1.0F / ray.GetDirection ()[i];
+ t0 = (p[i] + extent[i]) * f;
+ t1 = (p[i] - extent[i]) * f;
+ // Ray leaves on Right, Top, Back Side
+ if (t0 < t1)
+ {
+ if (t0 > tmin)
+ tmin = t0;
+
+ if (t1 < tmax)
+ tmax = t1;
+
+ if (tmin > tmax)
+ return false;
+
+ if (tmax < 0.0F)
+ return false;
+ }
+ // Ray leaves on Left, Bottom, Front Side
+ else
+ {
+ if (t1 > tmin)
+ tmin = t1;
+
+ if (t0 < tmax)
+ tmax = t0;
+
+ if (tmin > tmax)
+ return false;
+
+ if (tmax < 0.0F)
+ return false;
+ }
+ }
+ }
+
+ *outT0 = tmin;
+ *outT1 = tmax;
+
+ return true;
+}
+
+
+bool IntersectSphereSphere (const Sphere& s0, const Sphere& s1)
+{
+ float sqrDist = SqrMagnitude (s0.GetCenter () - s1.GetCenter ());
+ if (Sqr (s0.GetRadius () + s1.GetRadius ()) > sqrDist)
+ return true;
+ else
+ return false;
+}
+
+bool IntersectSphereSphereInclusive (const Sphere& s0, const Sphere& s1)
+{
+ float sqrDist = SqrMagnitude (s0.GetCenter () - s1.GetCenter ());
+ if (Sqr (s0.GetRadius () + s1.GetRadius ()) >= sqrDist)
+ return true;
+ else
+ return false;
+}
+
+bool IntersectAABBAABB (const AABB& b0, const AABB& b1)
+{
+ const Vector3f dif = (b1.GetCenter () - b0.GetCenter ());
+
+ return Abs (dif.x) < b0.GetExtent (0) + b1.GetExtent (0)
+ && Abs (dif.y) < b0.GetExtent (1) + b1.GetExtent (1)
+ && Abs (dif.z) < b0.GetExtent (2) + b1.GetExtent (2);
+}
+
+bool IntersectAABBAABBInclusive (const AABB& b0, const AABB& b1)
+{
+ const Vector3f dif = (b1.GetCenter () - b0.GetCenter ());
+
+ return Abs (dif.x) <= b0.GetExtent (0) + b1.GetExtent (0)
+ && Abs (dif.y) <= b0.GetExtent (1) + b1.GetExtent (1)
+ && Abs (dif.z) <= b0.GetExtent (2) + b1.GetExtent (2);
+}
+
+bool IntersectAABBSphere (const AABB& aabb, const Sphere& s)
+{
+ return CalculateSqrDistance (s.GetCenter (), aabb) < Sqr (s.GetRadius ());
+}
+
+bool IntersectAABBSphereInclusive (const AABB& aabb, const Sphere& s)
+{
+ return CalculateSqrDistance (s.GetCenter (), aabb) <= Sqr (s.GetRadius ());
+}
+
+// possible optimization: Precalculate the Abs () of the plane. (3 fabs less per plane)
+bool IntersectAABBFrustum (
+ const AABB& a,
+ const Plane* p,
+ UInt32 inClipMask)
+{
+ const Vector3f& m = a.GetCenter ();// center of AABB
+ const Vector3f& extent = a.GetExtent ();// half-diagonal
+ UInt32 mk = 1;
+
+ // loop while there are active planes..
+ while (mk <= inClipMask)
+ {
+ // if clip plane is active...
+ if (inClipMask & mk)
+ {
+ const Vector3f& normal = p->GetNormal ();
+ float dist = p->GetDistanceToPoint (m);
+ float radius = Dot (extent, Abs (normal));
+
+ if (dist + radius < 0) return false; // behind clip plane
+ // if (dist - radius < 0) *outClipMask |= mk; // straddles clipplane
+ // else in front of clip plane-> leave outClipMask bit off
+ // float m = (p->a () * b->v[p->nx].x) + (p->b * b->v[p->ny].y) + (p->c * b->v[p->nz].z);
+ // if (m > -p->d ()) return OUTSIDE;
+ // float r = Dot (m, normal) + p->d ();
+ // float n = (extent.x * Abs(normal.x)) + (extent.y * Abs(normal.y)) + (extent.z * Abs(normal.z));
+ // if (r + n < 0) return false;
+ }
+ mk += mk;
+ p++; // next plane
+ }
+ return true; // AABB intersects frustum
+}
+// Optimize like this: http://www.cg.tuwien.ac.at/studentwork/CESCG/CESCG-2002/DSykoraJJelinek/
+
+bool IntersectAABBFrustumFull (const AABB& a, const Plane p[6])
+{
+ return IntersectAABBPlaneBounds (a, p, 6);
+}
+
+bool IntersectAABBPlaneBounds (const AABB& a, const Plane* p, const int planeCount)
+{
+ const Vector3f& m = a.GetCenter ();// center of AABB
+ const Vector3f& extent = a.GetExtent ();// half-diagonal
+
+ for (int i = 0; i < planeCount; ++i, ++p)
+ {
+ const Vector3f& normal = p->GetNormal ();
+ float dist = p->GetDistanceToPoint (m);
+ float radius = Dot (extent, Abs (normal));
+ if (dist + radius < 0) return false; // behind clip plane
+ }
+ return true; // AABB intersects space bounded by planes
+}
+
+// Returns the shortest distance to planes if point is outside (positive float),
+// and 0.0 if point is inside frustum planes
+float PointDistanceToFrustum (const Vector4f& point, const Plane* p, const int planeCount)
+{
+ float maxDistanceNegative = -std::numeric_limits<float>::infinity();
+ for (int i = 0; i < planeCount; ++i, ++p)
+ {
+ float dist = p->GetDistanceToPoint (point);
+ if ((dist<0.0f) && (dist > maxDistanceNegative))
+ maxDistanceNegative = dist;
+ }
+ if (maxDistanceNegative != -std::numeric_limits<float>::infinity())
+ return -maxDistanceNegative;
+ else
+ return 0.0f;
+}
+
+bool IntersectTriTri (const Vector3f& a0, const Vector3f& b0, const Vector3f& c0,
+ const Vector3f& a1, const Vector3f& b1, const Vector3f& c1,
+ Vector3f* intersectionLine0, Vector3f* intersectionLine1, bool* coplanar)
+{
+ int coplanarInt;
+ bool ret;
+ ret = tri_tri_intersect_with_isectline (const_cast<Vector3f&> (a0).GetPtr (), const_cast<Vector3f&> (b0).GetPtr (), const_cast<Vector3f&> (c0).GetPtr (),
+ const_cast<Vector3f&> (a1).GetPtr (), const_cast<Vector3f&> (b1).GetPtr (), const_cast<Vector3f&> (c1).GetPtr (),
+ &coplanarInt, intersectionLine0->GetPtr (), intersectionLine1->GetPtr ());
+ *coplanar = coplanarInt;
+ return ret;
+}
+
+bool IntersectRayPlane (const Ray& ray, const Plane& plane, float* enter)
+{
+ AssertIf (enter == NULL);
+ float vdot = Dot (ray.GetDirection (), plane.GetNormal ());
+ float ndot = -Dot (ray.GetOrigin (), plane.GetNormal ()) - plane.d ();
+
+ // is line parallel to the plane? if so, even if the line is
+ // at the plane it is not considered as intersection because
+ // it would be impossible to determine the point of intersection
+ if ( CompareApproximately (vdot, 0.0F) )
+ return false;
+
+ // the resulting intersection is behind the origin of the ray
+ // if the result is negative ( enter < 0 )
+ *enter = ndot / vdot;
+
+ return *enter > 0.0F;
+}
+
+bool IntersectSegmentPlane( const Vector3f& p1, const Vector3f& p2, const Plane& plane, Vector3f* result )
+{
+ AssertIf( result == NULL );
+ Vector3f vec = p2 - p1;
+ float vdot = Dot( vec, plane.GetNormal() );
+
+ // segment parallel to the plane
+ if( CompareApproximately(vdot, 0.0f) )
+ return false;
+
+ float ndot = -Dot( p1, plane.GetNormal() ) - plane.d();
+ float u = ndot / vdot;
+ // intersection is out of segment
+ if( u < 0.0f || u > 1.0f )
+ return false;
+
+ *result = p1 + vec * u;
+ return true;
+}
+
+/*
+bool IntersectSphereTriangle (const Sphere& s, const Vector3f& vert0, const Vector3f& vert1, const Vector3f& vert2)
+{
+ const Vector3f& center = s.GetCenter ();
+ float radius = s.GetRadius ();
+ float radius2 = radius * radius;
+ // Early exit if one of the vertices is inside the sphere
+ Vector3f kDiff = vert2 - center;
+ float fC = SqrMagnitude (kDiff);
+ if(fC <= radius2) return true;
+
+ kDiff = vert1 - center;
+ fC = SqrMagnitude (kDiff);
+ if(fC <= radius2) return true;
+
+ kDiff = vert0 - center;
+ fC = SqrMagnitude (kDiff);
+ if(fC <= radius2) return true;
+
+ // Else do the full distance test
+ Vector3f TriEdge0 = vert1 - vert0;
+ Vector3f TriEdge1 = vert2 - vert0;
+
+ float fA00 = SqrMagnitude (TriEdge0);
+ float fA01 = Dot (TriEdge0, TriEdge1);
+ float fA11 = SqrMagnitude (TriEdge1);
+ float fB0 = Dot (kDiff, TriEdge0);
+ float fB1 = Dot (kDiff, TriEdge0);
+
+ float fDet = Abs(fA00*fA11 - fA01*fA01);
+ float u = fA01*fB1-fA11*fB0;
+ float v = fA01*fB0-fA00*fB1;
+ float SqrDist;
+
+ if(u + v <= fDet)
+ {
+ if(u < 0.0f)
+ {
+ if(v < 0.0f) // region 4
+ {
+ if(fB0 < 0.0f)
+ {
+ if(-fB0>=fA00) { SqrDist = fA00+2.0f*fB0+fC; }
+ else { u = -fB0/fA00; SqrDist = fB0*u+fC; }
+ }
+ else
+ {
+ if(fB1>=0.0f) { SqrDist = fC; }
+ else if(-fB1>=fA11) { SqrDist = fA11+2.0f*fB1+fC; }
+ else { v = -fB1/fA11; SqrDist = fB1*v+fC; }
+ }
+ }
+ else // region 3
+ {
+ if(fB1>=0.0f) { SqrDist = fC; }
+ else if(-fB1>=fA11) { SqrDist = fA11+2.0f*fB1+fC; }
+ else { v = -fB1/fA11; SqrDist = fB1*v+fC; }
+ }
+ }
+ else if(v < 0.0f) // region 5
+ {
+ if(fB0>=0.0f) { SqrDist = fC; }
+ else if(-fB0>=fA00) { SqrDist = fA00+2.0f*fB0+fC; }
+ else { u = -fB0/fA00; SqrDist = fB0*u+fC; }
+ }
+ else // region 0
+ {
+ // minimum at interior point
+ if(fDet==0.0f)
+ {
+ SqrDist = std::numeric_limits<float>::max ();
+ }
+ else
+ {
+ float fInvDet = 1.0f/fDet;
+ u *= fInvDet;
+ v *= fInvDet;
+ SqrDist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC;
+ }
+ }
+ }
+ else
+ {
+ float fTmp0, fTmp1, fNumer, fDenom;
+
+ if(u < 0.0f) // region 2
+ {
+ fTmp0 = fA01 + fB0;
+ fTmp1 = fA11 + fB1;
+ if(fTmp1 > fTmp0)
+ {
+ fNumer = fTmp1 - fTmp0;
+ fDenom = fA00-2.0f*fA01+fA11;
+ if(fNumer >= fDenom)
+ {
+// u = 1.0f;
+// v = 0.0f;
+ SqrDist = fA00+2.0f*fB0+fC;
+ }
+ else
+ {
+ u = fNumer/fDenom;
+ v = 1.0f - u;
+ SqrDist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC;
+ }
+ }
+ else
+ {
+// u = 0.0f;
+ if(fTmp1 <= 0.0f) { SqrDist = fA11+2.0f*fB1+fC; }
+ else if(fB1 >= 0.0f) { SqrDist = fC; }
+ else { v = -fB1/fA11; SqrDist = fB1*v+fC; }
+ }
+ }
+ else if(v < 0.0f) // region 6
+ {
+ fTmp0 = fA01 + fB1;
+ fTmp1 = fA00 + fB0;
+ if(fTmp1 > fTmp0)
+ {
+ fNumer = fTmp1 - fTmp0;
+ fDenom = fA00-2.0f*fA01+fA11;
+ if(fNumer >= fDenom)
+ {
+ SqrDist = fA11+2.0f*fB1+fC;
+ }
+ else
+ {
+ v = fNumer/fDenom;
+ u = 1.0f - v;
+ SqrDist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC;
+ }
+ }
+ else
+ {
+ if(fTmp1 <= 0.0f) { SqrDist = fA00+2.0f*fB0+fC; }
+ else if(fB0 >= 0.0f) { SqrDist = fC; }
+ else { u = -fB0/fA00; SqrDist = fB0*u+fC; }
+ }
+ }
+ else // region 1
+ {
+ fNumer = fA11 + fB1 - fA01 - fB0;
+ if(fNumer <= 0.0f)
+ {
+ SqrDist = fA11+2.0f*fB1+fC;
+ }
+ else
+ {
+ fDenom = fA00-2.0f*fA01+fA11;
+ if(fNumer >= fDenom)
+ {
+ SqrDist = fA00+2.0f*fB0+fC;
+ }
+ else
+ {
+ u = fNumer/fDenom;
+ v = 1.0f - u;
+ SqrDist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC;
+ }
+ }
+ }
+ }
+
+ return Abs (SqrDist) < radius2;
+}*/
+
+bool IntersectSphereTriangle (const Sphere& s, const Vector3f& vert0, const Vector3f& vert1, const Vector3f& vert2)
+{
+ const Vector3f& center = s.GetCenter ();
+ float radius = s.GetRadius ();
+ float radius2 = radius * radius;
+ Vector3f Diff;
+
+ // Early exit if one of the vertices is inside the sphere
+ float sqrDiff;
+ Diff = vert1 - center;
+ sqrDiff = SqrMagnitude (Diff);
+ if(sqrDiff <= radius2) return true;
+
+ Diff = vert2 - center;
+ sqrDiff = SqrMagnitude (Diff);
+ if(sqrDiff <= radius2) return true;
+
+ Diff = vert0 - center;
+ sqrDiff = SqrMagnitude (Diff);
+ if(sqrDiff <= radius2) return true;
+
+ // Else do the full distance test
+ Vector3f Edge0 = vert1 - vert0;
+ Vector3f Edge1 = vert2 - vert0;
+
+ float A00 = Dot (Edge0, Edge0);
+ float A01 = Dot (Edge0, Edge1);
+ float A11 = Dot (Edge1, Edge1);
+
+ float B0 = Dot (Diff, Edge0);
+ float B1 = Dot (Diff, Edge1);
+
+ float C = Dot (Diff, Diff);
+
+ float Det = Abs (A00 * A11 - A01 * A01);
+ float u = A01 * B1 - A11 * B0;
+ float v = A01 * B0 - A00 * B1;
+
+ float DistSq;
+ if (u + v <= Det)
+ {
+ if(u < 0.0F)
+ {
+ if(v < 0.0F)
+ {
+ // region 4
+ if(B0 < 0.0F)
+ {
+ if (-B0 >= A00)
+ DistSq = A00 + 2.0F * B0 + C;
+ else
+ {
+ u = -B0 / A00;
+ DistSq = B0 * u + C;
+ }
+ }
+ else{
+ if(B1 >= 0.0F)
+ DistSq = C;
+ else if(-B1 >= A11)
+ DistSq = A11 + 2.0F * B1 + C;
+ else
+ {
+ v = -B1 / A11;
+ DistSq = B1 * v + C;
+ }
+ }
+ }
+ else
+ { // region 3
+ if(B1 >= 0.0F)
+ DistSq = C;
+ else if(-B1 >= A11)
+ DistSq = A11 + 2.0F * B1 + C;
+ else
+ {
+ v = -B1 / A11;
+ DistSq = B1 * v + C;
+ }
+ }
+ }
+ else if(v < 0.0F)
+ { // region 5
+ if (B0 >= 0.0F)
+ DistSq = C;
+ else if (-B0 >= A00)
+ DistSq = A00 + 2.0F * B0 + C;
+ else
+ {
+ u = -B0 / A00;
+ DistSq = B0 * u + C;
+ }
+ }
+ else
+ { // region 0
+ // minimum at interior point
+ if (Det == 0.0F)
+ DistSq = std::numeric_limits<float>::max ();
+ else
+ {
+ float InvDet = 1.0F / Det;
+ u *= InvDet;
+ v *= InvDet;
+ DistSq = u * (A00 * u + A01 * v + 2.0F * B0) + v * (A01 * u + A11 * v + 2.0F * B1) + C;
+ }
+ }
+ }
+ else{
+ double Tmp0, Tmp1, Numer, Denom;
+
+ if(u < 0.0F)
+ {
+ // region 2
+ Tmp0 = A01 + B0;
+ Tmp1 = A11 + B1;
+ if (Tmp1 > Tmp0){
+ Numer = Tmp1 - Tmp0;
+ Denom = A00 - 2.0F * A01 + A11;
+ if (Numer >= Denom)
+ DistSq = A00 + 2.0F * B0 + C;
+ else
+ {
+ u = Numer / Denom;
+ v = 1.0 - u;
+ DistSq = u * (A00 * u + A01 * v + 2.0F * B0) + v * (A01 * u + A11 * v + 2.0F * B1) + C;
+ }
+ }
+ else
+ {
+ if(Tmp1 <= 0.0F)
+ DistSq = A11 + 2.0F * B1 + C;
+ else if(B1 >= 0.0)
+ DistSq = C;
+ else
+ {
+ v = -B1 / A11;
+ DistSq = B1 * v + C;
+ }
+ }
+ }
+ else if(v < 0.0)
+ { // region 6
+ Tmp0 = A01 + B1;
+ Tmp1 = A00 + B0;
+ if (Tmp1 > Tmp0)
+ {
+ Numer = Tmp1 - Tmp0;
+ Denom = A00 - 2.0F * A01 + A11;
+ if (Numer >= Denom)
+ DistSq = A11 + 2.0 * B1 + C;
+ else
+ {
+ v = Numer / Denom;
+ u = 1.0F - v;
+ DistSq = u * (A00 * u + A01 * v + 2.0F * B0) + v * (A01 * u + A11 * v + 2.0F * B1) + C;
+ }
+ }
+ else
+ {
+ if (Tmp1 <= 0.0F)
+ DistSq = A00 + 2.0F * B0 + C;
+ else if(B0 >= 0.0F)
+ DistSq = C;
+ else
+ {
+ u = -B0 / A00;
+ DistSq = B0 * u + C;
+ }
+ }
+ }
+ else
+ {
+ // region 1
+ Numer = A11 + B1 - A01 - B0;
+ if (Numer <= 0.0F)
+ DistSq = A11 + 2.0F * B1 + C;
+ else
+ {
+ Denom = A00 - 2.0F * A01 + A11;
+ if (Numer >= Denom)
+ DistSq = A00 + 2.0F * B0 + C;
+ else
+ {
+ u = Numer / Denom;
+ v = 1.0F - u;
+ DistSq = u * (A00 * u + A01 * v + 2.0F * B0) + v * (A01 * u + A11 * v + 2.0F * B1) + C;
+ }
+ }
+ }
+ }
+
+ return Abs (DistSq) <= radius2;
+}
+
+bool TestPlanesAABB(const Plane* planes, const int planeCount, const AABB& bounds)
+{
+ UInt32 planeMask = 0;
+ if (planeCount == 6)
+ planeMask = 63;
+ else
+ {
+ for (int i = 0; i < planeCount; ++i)
+ planeMask |= 1 << i;
+ }
+
+ return IntersectAABBFrustum (bounds, planes, planeMask);
+}
+
+// --------------------------------------------------------------------------
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/Profiler/TimeHelper.h"
+
+void GenerateUnitFrustumPlanes (Plane* p)
+{
+ p[0].SetABCD(-1.0, 0.0, 0.0,-1.0);
+ p[1].SetABCD( 1.0, 0.0, 0.0, 1.0);
+ p[2].SetABCD( 0.0,-1.0, 0.0,-1.0);
+ p[3].SetABCD( 0.0, 1.0, 0.0, 1.0);
+ p[4].SetABCD( 0.0, 0.0,-1.0,-1.0);
+ p[5].SetABCD( 0.0, 0.0, 1.0, 1.0);
+}
+
+float PointDistanceToFrustumRef (const Vector3f& point, const Plane* p, const int planeCount)
+{
+ DebugAssert(planeCount <= 6);
+
+ float maxDistanceNegative = -std::numeric_limits<float>::infinity();
+ float distances[6];
+
+ // Point distances to frustum planes
+ for (int i=0; i<planeCount; i++)
+ distances[i] = p[i].GetDistanceToPoint (point);
+
+ // Replace positive distances with negative infinity. This simplifies the shortest negative distance search to maximum operators
+ for (int i=0; i<planeCount; i++)
+ distances[i] = (distances[i] > 0.0f) ? -std::numeric_limits<float>::infinity() : distances[i];
+
+ // Find the shortest negative distance from the distance values
+ for (int i=0; i<planeCount; i++)
+ maxDistanceNegative = (distances[i] > maxDistanceNegative) ? distances[i] : maxDistanceNegative;
+
+ // If maxNegativeDistance is negative infinity, all distance were positive and the point is inside the frustum. In that case, return 0.0, otherwise return the shortest distance (abs value)
+ return (maxDistanceNegative == -std::numeric_limits<float>::infinity()) ? 0.0f : -maxDistanceNegative;
+}
+
+void TestComparePerformancePointDistanceToFrustum ();
+
+SUITE (IntersectionTests)
+{
+ TEST(PointDistanceToFrustum)
+ {
+ Plane unitFrustumPlanes[6];
+ GenerateUnitFrustumPlanes(unitFrustumPlanes);
+
+ Rand r (1); // fixed seed produces fixed random series
+
+ for (int i=0; i<1000; i++)
+ {
+ // Random coordinates (2x unit frustum volume)
+ float x = (r.GetFloat() - 0.5f) * 3.0f;
+ float y = (r.GetFloat() - 0.5f) * 3.0f;
+ float z = (r.GetFloat() - 0.5f) * 3.0f;
+ Vector3f point3f(x,y,z);
+ Vector4f point4f(x,y,z,0.0f);
+
+ float distanceRef = PointDistanceToFrustumRef(point3f, unitFrustumPlanes, 6);
+ float distance = PointDistanceToFrustum(point4f, unitFrustumPlanes, 6);
+
+ if (distanceRef > 0.0F)
+ {
+ CHECK_EQUAL(distance, distanceRef);
+ }
+ else
+ {
+ CHECK(distance <= 0.0F);
+ }
+ }
+ }
+} // SUITE
+
+static void TestPerformancePointDistanceToFrustum ()
+{
+ Plane unitFrustumPlanes[6];
+ GenerateUnitFrustumPlanes(unitFrustumPlanes);
+
+ Rand r (1); // fixed seed produces fixed random series
+ float x = (r.GetFloat() - 0.5f) * 2.0f;
+ float y = (r.GetFloat() - 0.5f) * 2.0f;
+ float z = (r.GetFloat() - 0.5f) * 2.0f;
+ Vector4f pointFloat4(x,y,z,0.0f);
+ float distanceSum = 0.0f;
+
+ ABSOLUTE_TIME time = START_TIME;
+
+ for (int i=0; i<1000000; i++)
+ {
+ distanceSum += PointDistanceToFrustum(pointFloat4, unitFrustumPlanes, 6);
+ }
+
+ printf_console("\n\nTime Impl: %f ms %f\n\n", GetElapsedTimeInSeconds (time) * 1000.0F, distanceSum);
+}
+
+static void TestPerformancePointDistanceToFrustumRef ()
+{
+ Plane unitFrustumPlanes[6];
+ GenerateUnitFrustumPlanes(unitFrustumPlanes);
+
+ Rand r (1); // fixed seed produces fixed random series
+ float x = (r.GetFloat() - 0.5f) * 2.0f;
+ float y = (r.GetFloat() - 0.5f) * 2.0f;
+ float z = (r.GetFloat() - 0.5f) * 2.0f;
+ Vector3f point3f(x,y,z);
+ float distanceSum = 0.0f;
+
+ ABSOLUTE_TIME time = START_TIME;
+
+ for (int i=0; i<1000000; i++)
+ {
+ distanceSum += PointDistanceToFrustumRef(point3f, unitFrustumPlanes, 6);
+ }
+
+ printf_console("\n\nTime REF: %f (%f)ms\n\n", GetElapsedTimeInSeconds (time) * 1000.0F, distanceSum);
+}
+
+void TestComparePerformancePointDistanceToFrustum ()
+{
+ TestPerformancePointDistanceToFrustum ();
+ TestPerformancePointDistanceToFrustumRef ();
+}
+
+#endif // ENABLE_UNIT_TESTS \ No newline at end of file
diff --git a/Runtime/Geometry/Intersection.h b/Runtime/Geometry/Intersection.h
new file mode 100644
index 0000000..4c96099
--- /dev/null
+++ b/Runtime/Geometry/Intersection.h
@@ -0,0 +1,95 @@
+#ifndef INTERSECTION_H
+#define INTERSECTION_H
+
+#include "Runtime/Math/Simd/math.h"
+
+class Ray;
+class OptimizedRay;
+class Sphere;
+class AABB;
+class Plane;
+
+class Vector3f;
+class Vector4f;
+
+// Intersects a Ray with a triangle.
+bool IntersectRayTriangle (const Ray& ray, const Vector3f& a, const Vector3f& b, const Vector3f& c);
+// t is to be non-Null and returns the first intersection point of the ray (ray.o + t * ray.dir)
+bool IntersectRayTriangle (const Ray& ray, const Vector3f& a, const Vector3f& b, const Vector3f& c, float* t);
+
+// Intersects a ray with a volume.
+// Returns true if the ray stats inside the volume or in front of the volume
+bool IntersectRaySphere (const Ray& ray, const Sphere& inSphere);
+bool IntersectRayAABB (const Ray& ray, const AABB& inAABB);
+
+// Intersects a ray with a volume.
+// Returns true if the ray stats inside the volume or in front of the volume
+// t0 is the first, t1 the second intersection. Both have to be non-NULL.
+// (t1 is always positive, t0 is negative if the ray starts inside the volume)
+bool IntersectRayAABB (const Ray& ray, const AABB& inAABB, float* t0, float* t1);
+bool IntersectRayAABB (const Ray& ray, const AABB& inAABB, float* t0);
+bool IntersectRaySphere (const Ray& ray, const Sphere& inSphere, float* t0, float* t1);
+
+// Do these volumes intersect each other?
+bool IntersectSphereSphere (const Sphere& s0, const Sphere& s1);
+bool IntersectAABBAABB (const AABB& s0, const AABB& s1);
+bool IntersectAABBSphere (const AABB& s0, const Sphere& s1);
+
+// Do these volumes intersect or touch each other?
+bool IntersectSphereSphereInclusive (const Sphere& s0, const Sphere& s1);
+bool EXPORT_COREMODULE IntersectAABBAABBInclusive (const AABB& s0, const AABB& s1);
+bool IntersectAABBSphereInclusive (const AABB& s0, const Sphere& s1);
+
+// Tests if the aabb is inside any of the planes enabled by inClipMask
+// The bitmask tells which planes have to be tested. (For 6 planes the bitmask is 63)
+bool IntersectAABBFrustum (const AABB& a, const Plane* p, UInt32 inClipMask);
+bool IntersectAABBFrustumFull (const AABB& a, const Plane p[6]);
+bool IntersectAABBPlaneBounds (const AABB& a, const Plane* p, const int planeCount);
+
+float PointDistanceToFrustum (const Vector4f& point, const Plane* p, const int planeCount);
+
+bool IntersectTriTri (const Vector3f& a0, const Vector3f& b0, const Vector3f& c0,
+ const Vector3f& a1, const Vector3f& b1, const Vector3f& c1,
+ Vector3f* intersectionLine0, Vector3f* intersectionLine1, bool* coplanar);
+
+// Intersects a ray with a plane (The ray can hit the plane from front and behind)
+// On return enter is the rays parameter where the intersection occurred.
+bool IntersectRayPlane (const Ray& ray, const Plane& plane, float* enter);
+
+
+// Intersects a line segment with a plane (can hit the plane from front and behind)
+// Fill result point if intersected.
+bool IntersectSegmentPlane( const Vector3f& p1, const Vector3f& p2, const Plane& plane, Vector3f* result );
+
+
+// Returns true if the triangle touches or is inside the triangle (a, b, c)
+bool IntersectSphereTriangle (const Sphere& s, const Vector3f& a, const Vector3f& b, const Vector3f& c);
+
+/// Returns true if the bounding box is inside the planes or intersects any of the planes.
+bool TestPlanesAABB(const Plane* planes, const int planeCount, const AABB& bounds);
+
+/// Projects point on a line.
+template <typename T>
+T ProjectPointLine(const T& point, const T& lineStart, const T& lineEnd)
+{
+ T relativePoint = point - lineStart;
+ T lineDirection = lineEnd - lineStart;
+ float length = Magnitude(lineDirection);
+ T normalizedLineDirection = lineDirection;
+ if (length > T::epsilon)
+ normalizedLineDirection /= length;
+
+ float dot = Dot(normalizedLineDirection, relativePoint);
+ dot = clamp(dot, 0.0F, length);
+
+ return lineStart + normalizedLineDirection * dot;
+}
+
+/// Returns the distance to a line from a point.
+template <typename T>
+float DistancePointLine(const T& point, const T& lineStart, const T& lineEnd)
+{
+ return Magnitude(ProjectPointLine<T>(point, lineStart, lineEnd) - point);
+}
+
+#endif
diff --git a/Runtime/Geometry/IntersectionTests.cpp b/Runtime/Geometry/IntersectionTests.cpp
new file mode 100644
index 0000000..1e384fc
--- /dev/null
+++ b/Runtime/Geometry/IntersectionTests.cpp
@@ -0,0 +1,288 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Geometry/Sphere.h"
+#include "Runtime/Geometry/Ray.h"
+
+
+SUITE (IntersectionTests)
+{
+
+
+// AABB Ray (ray inside aabb)
+TEST (RayInsideAABB)
+{
+ AABB aabb;
+ Ray ray;
+ float t0, t1;
+ bool result;
+
+ aabb.GetCenter () = Vector3f (5.0F, 10.0F, 20.0F);
+ aabb.GetExtent () = Vector3f (5.0F, 10.0F, 20.0F);
+
+ ray.SetOrigin (aabb.GetCenter ());
+ ray.SetDirection (Vector3f::zAxis);
+
+ CHECK (IntersectRayAABB (ray, aabb));
+
+ result = IntersectRayAABB (ray, aabb, &t0, &t1);
+ CHECK (result);
+ CHECK_CLOSE (t0, -20.0F, 0.000001F);
+ CHECK_CLOSE (t1, 20.0F, 0.000001F);
+
+ ray.SetDirection (-Vector3f::zAxis);
+ result = IntersectRayAABB (ray, aabb, &t0, &t1);
+ CHECK (result);
+ CHECK_CLOSE (t0, -20.0F, 0.000001F);
+ CHECK_CLOSE (t1, 20.0F, 0.000001F);
+}
+
+
+// AABB Ray (ray doesn't hit)
+TEST (RayOutsideAABB)
+{
+ AABB aabb;
+ Ray ray;
+ float t0, t1;
+
+ aabb.GetCenter () = Vector3f (5.0F, 10.0F, 20.0F);
+ aabb.GetExtent () = Vector3f (5.0F, 10.0F, 20.0F);
+
+ ray.SetOrigin (aabb.GetCenter () + Vector3f (5.0F, 10.0F, 20.01F));
+ ray.SetDirection (Vector3f::zAxis);
+
+ CHECK (!IntersectRayAABB (ray, aabb));
+ CHECK (!IntersectRayAABB (ray, aabb, &t0, &t1));
+}
+
+
+// AABB Ray (ray hits frontal)
+TEST (RayHitsAABBFrontal)
+{
+ AABB aabb;
+ Ray ray;
+ float t0, t1;
+ bool result;
+
+ aabb.GetCenter () = Vector3f (5.0F, 10.0F, 20.0F);
+ aabb.GetExtent () = Vector3f (5.0F, 10.0F, 20.0F);
+
+ ray.SetOrigin (Vector3f (5.0F, 10.0F, 60.0F));
+ ray.SetDirection (-Vector3f::zAxis);
+
+ CHECK (IntersectRayAABB (ray, aabb));
+
+ result = IntersectRayAABB (ray, aabb, &t0, &t1);
+ CHECK (result);
+ CHECK_CLOSE (t0, 20.0F, 0.000001F);
+ CHECK_CLOSE (t1, 60.0F, 0.000001F);
+}
+
+
+// AABB Ray (ray hits backward)
+TEST (RayHitsAABBBackward)
+{
+ AABB aabb;
+ Ray ray;
+ float t0, t1;
+
+ aabb.GetCenter () = Vector3f (5.0F, 10.0F, 20.0F);
+ aabb.GetExtent () = Vector3f (5.0F, 10.0F, 20.0F);
+
+ ray.SetOrigin (Vector3f (5.0F, 10.0F, 60.0F));
+ ray.SetDirection (Vector3f (0.0F, 0.0F, 1.0F));
+
+ CHECK (!IntersectRayAABB (ray, aabb));
+ CHECK (!IntersectRayAABB (ray, aabb, &t0, &t1));
+}
+
+
+// Sphere Ray (ray inside) (sphere.origin in front of ray.origin)
+TEST (SphereToRay1)
+{
+ Sphere sphere;
+ Ray ray;
+ float t0, t1;
+ bool result;
+
+ sphere.GetCenter () = Vector3f (5.0F, 10.0F, 20.0F);
+ sphere.GetRadius () = 10.0F;
+
+ ray.SetOrigin (Vector3f (5.0F, 10.0F, 25.0F));
+ ray.SetDirection (Vector3f (0.0F, 0.0F, 1.0F));
+
+ CHECK (IntersectRaySphere (ray, sphere));
+
+ result = IntersectRaySphere (ray, sphere, &t0, &t1);
+ CHECK (result);
+ CHECK_CLOSE (t0, -15.0F, 0.000001F);
+ CHECK_CLOSE (t1, 5.0F, 0.000001F);
+}
+
+
+// Sphere Ray (ray inside) (sphere.origin in front of ray.origin)
+TEST (SphereRay2)
+{
+ Sphere sphere;
+ Ray ray;
+ float t0, t1;
+ bool result;
+
+ sphere.GetCenter () = Vector3f (5.0F, 10.0F, 20.0F);
+ sphere.GetRadius () = 10.0F;
+
+ ray.SetOrigin (Vector3f (5.0F, 10.0F, 25.0F));
+ ray.SetDirection (Vector3f (0.0F, 0.0F, -1.0F));
+
+ CHECK (IntersectRaySphere (ray, sphere));
+
+ result = IntersectRaySphere (ray, sphere, &t0, &t1);
+ CHECK (result);
+ CHECK_CLOSE (t0, -5.0F, 0.000001F);
+ CHECK_CLOSE (t1, 15.0F, 0.000001F);
+}
+
+
+// Sphere Ray (hits sphere frontal)
+TEST (RayHitsSphereFrontal)
+{
+ Sphere sphere;
+ Ray ray;
+ float t0, t1;
+ bool result;
+
+ sphere.GetCenter () = Vector3f (5.0F, 10.0F, 20.0F);
+ sphere.GetRadius () = 10.0F;
+
+ ray.SetOrigin (Vector3f (5.0F, 10.0F, 0.0F));
+ ray.SetDirection (Vector3f (0.0F, 0.0F, 1.0F));
+
+ CHECK (IntersectRaySphere (ray, sphere));
+
+ result = IntersectRaySphere (ray, sphere, &t0, &t1);
+ CHECK (result);
+ CHECK_CLOSE (t0, 10.0F, 0.000001F);
+ CHECK_CLOSE (t1, 30.0F, 0.000001F);
+}
+
+
+// Sphere Ray (hits sphere backwards)
+TEST (RayHitsSphereBackwards)
+{
+ Sphere sphere;
+ Ray ray;
+ float t0, t1;
+ bool result;
+
+ sphere.GetCenter () = Vector3f (5.0F, 10.0F, 20.0F);
+ sphere.GetRadius () = 10.0F;
+
+ ray.SetOrigin (Vector3f (5.0F, 10.0F, 40.0F));
+ ray.SetDirection (Vector3f (0.0F, 0.0F, 1.0F));
+
+ CHECK (!IntersectRaySphere (ray, sphere));
+
+ result = IntersectRaySphere (ray, sphere, &t0, &t1);
+ CHECK (!result);
+ ErrorIf (result != false);
+}
+
+
+// Sphere Ray (misses sphere)
+TEST (RayMissesSphere)
+{
+ Sphere sphere;
+ Ray ray;
+ float t0, t1;
+
+ sphere.GetCenter () = Vector3f (5.0F, 10.0F, 20.0F);
+ sphere.GetRadius () = 10.0F;
+
+ ray.SetOrigin (Vector3f (5.0F, 10.0F, 30.01F));
+ ray.SetDirection (Vector3f (0.0F, 1.0F, 0.0F));
+
+ CHECK (!IntersectRaySphere (ray, sphere));
+ CHECK (!IntersectRaySphere (ray, sphere, &t0, &t1));
+}
+
+
+// Triangle Triangle (Not intersecting)
+TEST (TriangleTriangleNotIntersecting)
+{
+ Vector3f
+ a1 (0, 0, 0),
+ a2 (1, 1, 0),
+ a3 (2, 0, 0),
+ b1 (0, 0, 1),
+ b2 (1, 1, 1),
+ b3 (2, 0, 1);
+ Vector3f r1, r2;
+ bool coplanar;
+
+ CHECK (!IntersectTriTri (a1, a2, a3, b1, b2, b3, &r1, &r2, &coplanar));
+}
+
+
+// Triangle Triangle (intersecting)
+TEST (TriangleTriangleIntersecting)
+{
+ Vector3f
+ a1 (0, 2, 5),
+ a2 (2, 2, 0),
+ a3 (0, 2, 0),
+ b1 (0, 0, 0),
+ b2 (0, 5, 0),
+ b3 (0, 5, 3);
+ Vector3f r1, r2;
+ bool coplanar;
+
+ CHECK (IntersectTriTri (a1, a2, a3, b1, b2, b3, &r1, &r2, &coplanar));
+ CHECK (CompareApproximately (r1, Vector3f (0, 2, 0)));
+ CHECK (CompareApproximately (r2, Vector3f (0, 2, 1.2f)));
+ CHECK_EQUAL (false, coplanar);
+}
+
+
+// Triangle Triangle (coplanaer)
+TEST (TriangleTriangleCoplanar)
+{
+ Vector3f
+ a1 (0, 8, 0),
+ a2 (0, 4, 0),
+ a3 (5, 4, 0),
+ b1 (0, 5, 0),
+ b2 (5, 0, 0),
+ b3 (0, 0, 0);
+ Vector3f r1, r2;
+ bool coplanar;
+
+ CHECK (IntersectTriTri (a1, a2, a3, b1, b2, b3, &r1, &r2, &coplanar));
+ CHECK (coplanar);
+}
+
+
+TEST (Misc)
+{
+ CHECK (IntersectSphereTriangle (
+ Sphere (Vector3f (0.3F, 0.3F, 0.1F), .2F),
+ Vector3f (0.0F, 0.0F, 0.0F),
+ Vector3f (0.0F, 1.0F, 0.0F),
+ Vector3f (1.0F, 0.0F, 0.0F)));
+ CHECK (IntersectSphereTriangle (
+ Sphere (Vector3f (0.3F, 0.3F, 0.0F), .2F),
+ Vector3f (0.0F, 0.0F, 0.0F),
+ Vector3f (0.0F, 1.0F, 0.0F),
+ Vector3f (1.0F, 0.0F, 0.0F)));
+ CHECK (!CompareApproximately (0.01f, 0.0));
+}
+
+
+}
+
+
+#endif
diff --git a/Runtime/Geometry/Plane.h b/Runtime/Geometry/Plane.h
new file mode 100644
index 0000000..ca58ff2
--- /dev/null
+++ b/Runtime/Geometry/Plane.h
@@ -0,0 +1,169 @@
+#ifndef PLANE_H
+#define PLANE_H
+
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Vector4.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+
+// Calculates the unnormalized normal from 3 points
+Vector3f CalcRawNormalFromTriangle (const Vector3f& a, const Vector3f& b, const Vector3f& c);
+
+enum{
+ kPlaneFrustumLeft,
+ kPlaneFrustumRight,
+ kPlaneFrustumBottom,
+ kPlaneFrustumTop,
+ kPlaneFrustumNear,
+ kPlaneFrustumFar,
+ kPlaneFrustumNum,
+};
+
+class Plane
+{
+public:
+
+ Vector3f normal;
+ float distance;
+
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (Plane)
+
+ const float& a ()const { return normal.x; }
+ const float& b ()const { return normal.y; }
+ const float& c ()const { return normal.z; }
+
+ const float& d () const { return distance; }
+ float& d () { return distance; }
+
+ const Vector3f& GetNormal ()const{ return normal; }
+
+ Plane () { }
+ Plane (float a, float b, float c, float d) { normal.x = a; normal.y = b; normal.z = c; distance = d; }
+
+ Plane& operator *= (float scale);
+ bool operator == (const Plane& p)const { return normal == p.normal && distance == p.distance; }
+ bool operator != (const Plane& p)const { return normal != p.normal || distance != p.distance; }
+
+ void SetInvalid () { normal = Vector3f::zero; distance = 0.0F; }
+
+ // Just sets the coefficients. Does NOT normalize!
+ void SetABCD (const float a, const float b, const float c, const float d);
+
+ void Set3Points (const Vector3f& a, const Vector3f& b, const Vector3f& c);
+ void Set3PointsUnnormalized (const Vector3f& a, const Vector3f& b, const Vector3f& c);
+
+ void SetNormalAndPosition (const Vector3f& inNormal, const Vector3f& inPoint);
+
+ float GetDistanceToPoint (const Vector3f& inPt) const;
+ float GetDistanceToPoint (const Vector4f& inPt) const;
+ bool GetSide (const Vector3f& inPt) const;
+ bool SameSide (const Vector3f& inPt0, const Vector3f& inPt1);
+
+ void NormalizeRobust ();
+ void NormalizeUnsafe ();
+};
+
+template<class TransferFunction>
+inline void Plane::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (normal);
+ TRANSFER (distance);
+}
+
+inline float Plane::GetDistanceToPoint (const Vector3f& inPt)const
+{
+ DebugAssert (IsNormalized (normal));
+ return Dot (normal, inPt) + distance;
+}
+
+// inPt w component is ignored from distance computations
+inline float Plane::GetDistanceToPoint (const Vector4f& inPt)const
+{
+ DebugAssert (IsNormalized (normal));
+ //Dot3 (normal, inPt) + distance;
+ return normal.x * inPt.x + normal.y * inPt.y + normal.z * inPt.z + distance;
+}
+
+// Returns true if we are on the front side (same as: GetDistanceToPoint () > 0.0)
+inline bool Plane::GetSide (const Vector3f& inPt) const
+{
+ return Dot (normal, inPt) + distance > 0.0F;
+}
+
+
+// Calculates the normal from 3 points unnormalized
+inline Vector3f CalcRawNormalFromTriangle (const Vector3f& a, const Vector3f& b, const Vector3f& c)
+{
+ return Cross (b - a, c - a);
+}
+
+inline Vector3f RobustNormalFromTriangle (const Vector3f& v0, const Vector3f& v1, const Vector3f& v2)
+{
+ Vector3f normal = CalcRawNormalFromTriangle (v0, v1, v2);
+ return NormalizeRobust (normal);
+}
+
+inline void Plane::SetABCD (float a, float b, float c, float d)
+{
+ normal.Set(a, b, c);
+ distance = d;
+}
+
+inline void Plane::Set3Points (const Vector3f& a, const Vector3f& b, const Vector3f& c)
+{
+ normal = CalcRawNormalFromTriangle (a, b, c);
+ normal = ::Normalize (normal);
+ distance = -Dot (normal, a);
+ AssertIf (!IsNormalized (normal));
+}
+
+inline void Plane::Set3PointsUnnormalized (const Vector3f& a, const Vector3f& b, const Vector3f& c)
+{
+ normal = CalcRawNormalFromTriangle (a, b, c);
+ distance = -Dot (normal, a);
+}
+
+inline void Plane::SetNormalAndPosition (const Vector3f& inNormal, const Vector3f& inPoint)
+{
+ normal = inNormal;
+ AssertIf (!IsNormalized (normal, 0.001f));
+ distance = -Dot (inNormal, inPoint);
+}
+
+inline bool Plane::SameSide (const Vector3f& inPt0, const Vector3f& inPt1)
+{
+ float d0 = GetDistanceToPoint(inPt0);
+ float d1 = GetDistanceToPoint(inPt1);
+ if (d0 > 0.0f && d1 > 0.0f)
+ return true;
+ else if (d0 <= 0.0f && d1 <= 0.0f)
+ return true;
+ else
+ return false;
+}
+
+inline Plane& Plane::operator *= (float scale)
+{
+ normal *= scale;
+ distance *= scale;
+ return *this;
+}
+
+inline void Plane::NormalizeUnsafe ()
+{
+ float invMag = 1.0f/Magnitude(normal);
+ normal *= invMag;
+ distance *= invMag;
+}
+
+// It uses NormalizeRobust(), so it handles zero and extremely small vectors,
+// but can be slow. Another option would be to use plain normalize, but
+// always remember to check for division by zero with zero vectors.
+inline void Plane::NormalizeRobust ()
+{
+ float invMag;
+ normal = ::NormalizeRobust(normal, invMag);
+ distance *= invMag;
+}
+
+
+#endif
diff --git a/Runtime/Geometry/Ray.cpp b/Runtime/Geometry/Ray.cpp
new file mode 100644
index 0000000..0dac305
--- /dev/null
+++ b/Runtime/Geometry/Ray.cpp
@@ -0,0 +1,17 @@
+#include "UnityPrefix.h"
+#include "Ray.h"
+
+float Ray::SqrDistToPoint(const Vector3f& P) const {
+//ÊÊÊ Vector v = L.P1 - L.P0;
+ Vector3f v = m_Direction;
+// Vector3f w = P - L.P0;
+ Vector3f w = P - m_Origin;
+
+ float c1 = Dot(w,v);
+ float c2 = Dot(v,v);
+ float b = c1 / c2;
+
+//ÊÊÊ Point Pb = L.P0 + b * v;
+ Vector3f Pb = GetPoint (b);
+ return SqrMagnitude(P - Pb);
+}
diff --git a/Runtime/Geometry/Ray.h b/Runtime/Geometry/Ray.h
new file mode 100644
index 0000000..515126f
--- /dev/null
+++ b/Runtime/Geometry/Ray.h
@@ -0,0 +1,32 @@
+#ifndef RAY_H
+#define RAY_H
+
+#include "Runtime/Math/FloatConversion.h"
+#include "Runtime/Math/Vector3.h"
+
+class Ray
+{
+#if UNITY_FLASH //flash needs to be able to set these fields
+public:
+#endif
+ Vector3f m_Origin;
+ Vector3f m_Direction; // Direction is always normalized
+
+public:
+ Ray () {}
+ Ray (const Vector3f& orig, const Vector3f& dir) { m_Origin = orig; SetDirection (dir); }
+
+ const Vector3f& GetDirection ()const { return m_Direction; }
+ // Direction has to be normalized
+ void SetDirection (const Vector3f& dir) { AssertIf (!IsNormalized (dir)); m_Direction = dir; }
+ void SetApproxDirection (const Vector3f& dir) { m_Direction = NormalizeFast (dir); }
+ void SetOrigin (const Vector3f& origin) { m_Origin = origin; }
+
+ const Vector3f& GetOrigin ()const { return m_Origin; }
+ Vector3f GetPoint (float t) const { return m_Origin + t * m_Direction; }
+
+ float SqrDistToPoint (const Vector3f &v) const;
+};
+
+
+#endif
diff --git a/Runtime/Geometry/Ray2D.h b/Runtime/Geometry/Ray2D.h
new file mode 100644
index 0000000..7adff8e
--- /dev/null
+++ b/Runtime/Geometry/Ray2D.h
@@ -0,0 +1,32 @@
+#ifndef RAY2D_H
+#define RAY2D_H
+
+#include "Runtime/Math/Vector2.h"
+
+
+// --------------------------------------------------------------------------
+
+
+class Ray2D
+{
+#if UNITY_FLASH //flash needs to be able to set these fields
+public:
+#endif
+ Vector2f m_Origin;
+ Vector2f m_Direction; // Direction is always normalized
+
+public:
+ Ray2D () {}
+ Ray2D (const Vector2f& origin, const Vector2f& direction) { m_Origin = origin; SetDirection (direction); }
+
+ const Vector2f& GetOrigin ()const { return m_Origin; }
+ void SetOrigin (const Vector2f& origin) { m_Origin = origin; }
+
+ const Vector2f& GetDirection () const { return m_Direction; }
+ void SetDirection (const Vector2f& direction) { AssertIf (!IsNormalized (direction)); m_Direction = direction; }
+ void SetApproxDirection (const Vector2f& direction) { m_Direction = NormalizeFast (direction); }
+
+ Vector2f GetPoint (const float scale) const { return m_Origin + (scale * m_Direction); }
+};
+
+#endif
diff --git a/Runtime/Geometry/Sphere.cpp b/Runtime/Geometry/Sphere.cpp
new file mode 100644
index 0000000..19ddb3c
--- /dev/null
+++ b/Runtime/Geometry/Sphere.cpp
@@ -0,0 +1,57 @@
+#include "UnityPrefix.h"
+#include "Sphere.h"
+
+Sphere MergeSpheres (const Sphere& inSphere0, const Sphere& inSphere1);
+float MergeSpheresRadius (const Sphere& inSphere0, const Sphere& inSphere1);
+
+void Sphere::Set (const Vector3f* inVertices, UInt32 inHowmany)
+{
+ m_Radius = 0.0F;
+ m_Center = Vector3f::zero;
+ UInt32 i;
+ for (i=0;i<inHowmany;i++)
+ m_Radius = std::max (m_Radius, SqrMagnitude (inVertices[i]));
+ m_Radius = sqrt (m_Radius);
+}
+
+
+Sphere MergeSpheres (const Sphere& inSphere0, const Sphere& inSphere1)
+{
+ Vector3f kCDiff = inSphere1.GetCenter() - inSphere0.GetCenter();
+ float fLSqr = SqrMagnitude (kCDiff);
+ float fRDiff = inSphere1.GetRadius() - inSphere0.GetRadius();
+
+ if (fRDiff*fRDiff >= fLSqr)
+ return fRDiff >= 0.0 ? inSphere1 : inSphere0;
+
+ float fLength = sqrt (fLSqr);
+ const float fTolerance = 1.0e-06f;
+ Sphere kSphere;
+
+ if (fLength > fTolerance)
+ {
+ float fCoeff = (fLength + fRDiff) / (2.0 * fLength);
+ kSphere.GetCenter () = inSphere0.GetCenter () + fCoeff * kCDiff;
+ }
+ else
+ {
+ kSphere.GetCenter () = inSphere0.GetCenter ();
+ }
+
+ kSphere.GetRadius () = 0.5F * (fLength + inSphere0.GetRadius () + inSphere1.GetRadius ());
+
+ return kSphere;
+}
+
+float MergeSpheresRadius (const Sphere& inSphere0, const Sphere& inSphere1)
+{
+ Vector3f kCDiff = inSphere1.GetCenter() - inSphere0.GetCenter();
+ float fLSqr = SqrMagnitude (kCDiff);
+ float fRDiff = inSphere1.GetRadius () - inSphere0.GetRadius();
+
+ if (fRDiff*fRDiff >= fLSqr)
+ return fRDiff >= 0.0 ? inSphere1.GetRadius () : inSphere0.GetRadius ();
+
+ float fLength = sqrt (fLSqr);
+ return 0.5F * (fLength + inSphere0.GetRadius () + inSphere1.GetRadius ());
+}
diff --git a/Runtime/Geometry/Sphere.h b/Runtime/Geometry/Sphere.h
new file mode 100644
index 0000000..e19e2f5
--- /dev/null
+++ b/Runtime/Geometry/Sphere.h
@@ -0,0 +1,79 @@
+#ifndef SPHERE_H
+#define SPHERE_H
+
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Math/Vector3.h"
+#include <algorithm>
+#include "Runtime/Modules/ExportModules.h"
+
+class Sphere
+{
+ Vector3f m_Center;
+ float m_Radius;
+
+ public:
+
+ DECLARE_SERIALIZE (Sphere)
+
+ Sphere () {}
+ Sphere (const Vector3f& p0, float r) {Set (p0, r);}
+
+ void Set (const Vector3f& p0) {m_Center = p0; m_Radius = 0;}
+ void Set (const Vector3f& p0, float r) {m_Center = p0; m_Radius = r;}
+
+ void Set (const Vector3f& p0, const Vector3f& p1);
+
+ void Set (const Vector3f* inVertices, UInt32 inHowmany);
+
+ Vector3f& GetCenter () {return m_Center;}
+ const Vector3f& GetCenter ()const {return m_Center;}
+
+ float& GetRadius () {return m_Radius;}
+ const float& GetRadius ()const {return m_Radius;}
+
+ bool IsInside (const Sphere& inSphere)const;
+};
+
+float EXPORT_COREMODULE CalculateSqrDistance (const Vector3f& p, const Sphere& s);
+bool Intersect (const Sphere& inSphere0, const Sphere& inSphere1);
+
+
+inline void Sphere::Set (const Vector3f& p0, const Vector3f& p1)
+{
+ Vector3f dhalf = (p1 - p0) * 0.5;
+
+ m_Center = dhalf + p0;
+ m_Radius = Magnitude (dhalf);
+}
+
+inline bool Sphere::IsInside (const Sphere& inSphere)const
+{
+ float sqrDist = SqrMagnitude (GetCenter () - inSphere.GetCenter ());
+ if (Sqr (GetRadius ()) > sqrDist + Sqr (inSphere.GetRadius ()))
+ return true;
+ else
+ return false;
+}
+
+inline bool Intersect (const Sphere& inSphere0, const Sphere& inSphere1)
+{
+ float sqrDist = SqrMagnitude (inSphere0.GetCenter () - inSphere1.GetCenter ());
+ if (Sqr (inSphere0.GetRadius () + inSphere1.GetRadius ()) > sqrDist)
+ return true;
+ else
+ return false;
+}
+
+inline float CalculateSqrDistance (const Vector3f& p, const Sphere& s)
+{
+ return std::max (0.0F, SqrMagnitude (p - s.GetCenter ()) - Sqr (s.GetRadius ()));
+}
+
+template<class TransferFunction> inline
+void Sphere::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (m_Center);
+ TRANSFER (m_Radius);
+}
+
+#endif
diff --git a/Runtime/Geometry/SpriteMeshGenerator.cpp b/Runtime/Geometry/SpriteMeshGenerator.cpp
new file mode 100644
index 0000000..ad49bf5
--- /dev/null
+++ b/Runtime/Geometry/SpriteMeshGenerator.cpp
@@ -0,0 +1,973 @@
+#include "UnityPrefix.h"
+#include "SpriteMeshGenerator.h"
+
+#if ENABLE_SPRITES
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Graphics/SpriteUtility.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/FloatConversion.h"
+#include "Runtime/Math/Polynomials.h"
+
+#include "External/libtess2/libtess2/tesselator.h"
+#include <queue>
+
+PROFILER_INFORMATION (gProfileDecompose, "SpriteMeshGenerator.Decompose", kProfilerRender);
+PROFILER_INFORMATION (gProfileTraceShape, "SpriteMeshGenerator.TraceShape", kProfilerRender);
+PROFILER_INFORMATION (gProfileSimplify, "SpriteMeshGenerator.Simplify", kProfilerRender);
+
+static const float kHoleAreaLimit = 0.25f;
+static const float kMaxTriangles = 1000.0f;
+static const float kMaxOverdraw = 4.0f;
+static const float kResolution = 960*640*kMaxOverdraw;
+
+struct edge {
+ float m_a;
+ float m_b;
+ float m_c;
+ bool m_apos;
+ bool m_bpos;
+ edge(){};
+ edge(Vector2f p0, Vector2f p1) {
+ m_a = (p0.y - p1.y);
+ m_b = (p1.x - p0.x);
+ m_c = -p0.x*m_a - p0.y*m_b;
+
+ bool aez = (m_a == 0);
+ bool bez = (m_b == 0);
+ bool agz = (m_a < 0);
+ bool bgz = (m_b < 0);
+ m_apos = aez ? bgz : agz;
+ m_bpos = bez ? agz : bgz;
+ }
+
+ float grad(Vector2f p) { return (m_a*p.x + m_b*p.y + m_c); }
+ int test(Vector2f p) {
+ float g = grad(p);
+ return ((g > 0) || ((g == 0) && m_apos)) ? 1 : -1;
+ }
+};
+
+static inline int mod(int a, int n)
+{
+ return a>=n ? a%n : a>=0 ? a : n-1-(-1-a)%n;
+}
+
+
+inline float det(const Vector2f& a, const Vector2f& b, const Vector2f& c)
+{
+ float bax = b.x - a.x;
+ float acx = a.x - c.x;
+ float aby = a.y - b.y;
+ float cay = c.y - a.y;
+ return (bax * cay) - ( acx * aby );
+}
+
+inline Vector2f ortho(const Vector2f& v)
+{
+ return Vector2f(v.y, -v.x);
+}
+
+inline float distance_point_line(Vector2f pq, Vector2f p0, Vector2f p1)
+{
+ Vector2f v = p1 - p0;
+ Vector2f w = pq - p0;
+
+ float a = Dot(w, v);
+ if (a <= 0)
+ return Magnitude(p0-pq);
+
+ float b = Dot(v, v);
+ if (b <= a)
+ return Magnitude(p1-pq);
+
+ float c = a/b;
+ Vector2f p = p0 + v*c;
+ return Magnitude(p-pq);
+}
+
+void SpriteMeshGenerator::Decompose(std::vector<Vector2f>* vertices, std::vector<int>* indices)
+{
+ if (m_paths.size() == 0)
+ return;
+
+ vertices->clear();
+ indices->clear();
+
+ const int kVertexSize = 2;
+ const int kPolygonVertices = 3;
+
+ PROFILER_BEGIN(gProfileDecompose, NULL);
+ TESStesselator* tess = tessNewTess(NULL);
+ for (std::vector<path>::const_iterator it = m_paths.begin(); it != m_paths.end(); ++it)
+ {
+ const path& p = *it;
+ const std::vector<vertex>& vertices = p.m_path;
+ if (vertices.size() == 0)
+ continue;
+
+ tessAddContour(tess, kVertexSize, &vertices[0].p, sizeof(vertex), vertices.size());
+ }
+ int tessError = tessTesselate(tess, TESS_WINDING_NONZERO, TESS_POLYGONS, kPolygonVertices, kVertexSize, NULL);
+ AssertBreak(tessError == 1);
+
+ const int elemCount = tessGetElementCount(tess);
+ const TESSindex* elements = tessGetElements(tess);
+ const TESSreal* real = tessGetVertices(tess);
+
+ for (int e = 0; e < elemCount; ++e)
+ {
+ const int* idx = &elements[e * kPolygonVertices];
+
+ // Extract vertices
+ for (int i = 0; i < kPolygonVertices; ++i)
+ {
+ Assert(idx[i] != TESS_UNDEF);
+
+ float x = real[idx[i]*kVertexSize];
+ float y = real[idx[i]*kVertexSize + 1];
+
+#define SNAP_VERTEX_POSITION 1
+#if SNAP_VERTEX_POSITION
+ x = floor(x + 0.5f);
+ y = floor(y + 0.5f);
+#endif
+#undef SNAP_VERTEX_POSITION
+
+ Vector2f newVertex(x, y);
+
+ // Reuse vertex
+ bool reused = false;
+ for (int v = 0; v < vertices->size(); ++v)
+ {
+ const Vector2f& vertex = (*vertices)[v];
+ if ((std::abs(vertex.x - x) <= Vector2f::epsilon) && (std::abs(vertex.y - y) <= Vector2f::epsilon))
+ {
+ indices->push_back(v);
+ reused = true;
+ break;
+ }
+ }
+
+ // New vertex
+ if (!reused)
+ {
+ indices->push_back(vertices->size());
+ vertices->push_back(newVertex);
+ }
+ }
+
+ // Push polygon
+ }
+#ifdef __MESHGEN_STATS
+ {
+ int n=(int)m_paths.size();
+
+ double mesh_area = 0.0;
+ double rect_area = 0.0;
+ for(int i=0; i<n; i++) {
+ path *p = &m_paths[i];
+ int m = p->m_path.size();
+ for(int j=0; j<m; j++) {
+ Vector2f p0 = p->m_path[j].p;
+ Vector2f p1 = p->m_path[mod(j+1, m)].p;
+ mesh_area += (p0.x*p1.y)-(p0.y*p1.x);
+ }
+ }
+ mesh_area *= 0.5;
+ rect_area = m_mask_org.w*m_mask_org.h;
+
+ LogString(Format("Sprite mesh triangle count : %d", ((indices!=NULL) ? (int)(indices->size()/3) : 0)));
+ LogString(Format("Sprite mesh area (image space) : %.0f", mesh_area));
+ LogString(Format("Sprite rect area (image space) : %.0f", rect_area));
+ LogString(Format("Sprite diff area (image space) : %.0f", rect_area-mesh_area));
+
+ }
+#endif
+
+ tessDeleteTess(tess);
+ PROFILER_END
+}
+
+float SpriteMeshGenerator::evaluateLOD(const float areaHint, float area)
+{
+ // do rough estimation of simplification lod
+ int triangleCount=0;
+ int n = (int)m_paths.size();
+
+ // evaluate optimal triangle count
+ for(int i=0; i<n; i++) {
+ path *p = &m_paths[i];
+ if (p->isHole())
+ triangleCount += 2;
+ else
+ triangleCount += (int)p->m_path.size() - 2;
+ }
+ float maxTriangleCount = area * areaHint;
+ float lod = 1.0f-(maxTriangleCount / (float)triangleCount);
+ return clamp(lod, 0.0f, 1.0f);
+
+}
+
+void SpriteMeshGenerator::MakeShape(ColorRGBA32* image, int imageWidth, int imageHeight, float hullTolerance, unsigned char alphaTolerance, bool holeDetection, unsigned int extrude, float bias, int mode)
+{
+ PROFILER_BEGIN(gProfileTraceShape, NULL);
+ m_mask_org = mask(image, imageWidth, imageHeight, alphaTolerance, extrude);
+ m_mask_cur = mask(image, imageWidth, imageHeight, alphaTolerance, extrude);
+
+ std::vector<vertex> outline;
+
+ int sign;
+ float area;
+ float areaRect = imageWidth*imageHeight;
+ float areaTotal = 0;
+ while(contour(outline, sign, area)) {
+ if (!holeDetection && sign=='-')
+ continue;
+ if (area<(areaRect*kHoleAreaLimit) && sign=='-' && hullTolerance < 0.0f)
+ continue;
+ areaTotal += (sign=='+') ? area : -area;
+ m_paths.push_back(path(outline, imageWidth, imageHeight, sign, area, bias));
+ }
+ PROFILER_END
+
+ PROFILER_BEGIN(gProfileSimplify, NULL);
+ if (hullTolerance < 0.0f) {
+ hullTolerance = evaluateLOD(kMaxTriangles/kResolution, areaTotal);
+ }
+ // Simplify
+ for (std::vector<path>::iterator it = m_paths.begin(); it != m_paths.end(); ++it)
+ {
+ path& p = *it;
+ p.simplify(hullTolerance, mode);
+ }
+ // Snap
+ for (std::vector<path>::iterator it = m_paths.begin(); it != m_paths.end(); ++it)
+ {
+ path& p = *it;
+ for (std::vector<vertex>::iterator vit = p.m_path.begin(); vit != p.m_path.end(); ++vit)
+ {
+ vertex& vert = *vit;
+ vert.p.x = Roundf(vert.p.x);
+ vert.p.y = Roundf(vert.p.y);
+ }
+ }
+ PROFILER_END
+}
+
+inline bool predMinX(const SpriteMeshGenerator::path& a, const SpriteMeshGenerator::path& b) { return a.GetMin().x < b.GetMin().x; }
+inline bool predMinY(const SpriteMeshGenerator::path& a, const SpriteMeshGenerator::path& b) { return a.GetMin().y < b.GetMin().y; }
+inline bool predMaxX(const SpriteMeshGenerator::path& a, const SpriteMeshGenerator::path& b) { return a.GetMax().x < b.GetMax().x; }
+inline bool predMaxY(const SpriteMeshGenerator::path& a, const SpriteMeshGenerator::path& b) { return a.GetMax().y < b.GetMax().y; }
+
+bool SpriteMeshGenerator::FindBounds(Rectf& bounds)
+{
+ if (m_paths.size() == 0)
+ return false;
+
+ const SpriteMeshGenerator::path& minX = *std::min_element(m_paths.begin(), m_paths.end(), predMinX);
+ const SpriteMeshGenerator::path& minY = *std::min_element(m_paths.begin(), m_paths.end(), predMinY);
+ const SpriteMeshGenerator::path& maxX = *std::max_element(m_paths.begin(), m_paths.end(), predMaxX);
+ const SpriteMeshGenerator::path& maxY = *std::max_element(m_paths.begin(), m_paths.end(), predMaxY);
+
+ bounds.x = minX.GetMin().x;
+ bounds.SetRight(maxX.GetMax().x);
+
+ bounds.y = minY.GetMin().y;
+ bounds.SetBottom(maxY.GetMax().y);
+
+ return true;
+}
+
+void SpriteMeshGenerator::path::bbox()
+{
+ float minx=(std::numeric_limits<float>::max)();
+ float miny=(std::numeric_limits<float>::max)();
+ float maxx=(std::numeric_limits<float>::min)();
+ float maxy=(std::numeric_limits<float>::min)();
+
+ int n=(int)m_path.size();
+ for(int i=0; i<n; i++) {
+ Vector2f p=m_path[i].p;
+ if (p.x < minx) minx = p.x;
+ if (p.y < miny) miny = p.y;
+ if (p.x > maxx) maxx = p.x;
+ if (p.y > maxy) maxy = p.y;
+ }
+
+ // clamp to bounds
+ minx = (minx < 0.0) ? 0.0 : (minx > m_bx) ? m_bx : minx;
+ miny = (miny < 0.0) ? 0.0 : (miny > m_by) ? m_by : miny;
+ maxx = (maxx < 0.0) ? 0.0 : (maxx > m_bx) ? m_bx : maxx;
+ maxy = (maxy < 0.0) ? 0.0 : (maxy > m_by) ? m_by : maxy;
+
+ m_min = Vector2f(minx, miny);
+ m_max = Vector2f(maxx, maxy);
+}
+
+bool SpriteMeshGenerator::path::dec(int i)
+{
+ int n = (int)m_path.size();
+ if (n<3)
+ return false;
+
+ Vector2f a = m_path[mod(i-1, n)].p;
+ Vector2f b = m_path[mod(i+0, n)].p;
+ Vector2f c = m_path[mod(i+1, n)].p;
+ Vector2f ab = a-b;
+ Vector2f bc = b-c;
+ Vector2f na = NormalizeSafe(Vector2f(-ab.y, ab.x));
+ Vector2f nb = NormalizeSafe(Vector2f(-bc.y, bc.x));
+ Vector2f no = NormalizeSafe(nb+na);
+ m_path[mod(i, n)].n = no;
+ return true;
+}
+
+bool SpriteMeshGenerator::path::inf(int i)
+{
+ int n = (int)m_path.size();
+ if (n<3)
+ return false;
+ Vector2f a = m_path[mod(i-1, n)].p;
+ Vector2f b = m_path[mod(i+0, n)].p;
+ Vector2f c = m_path[mod(i+1, n)].p;
+ m_path[i].s = edge(a,c).test(b);
+ return true;
+}
+
+static int dir(Vector2f p0, Vector2f p1)
+{
+ int di[3*3] = {
+ 0, 1, 2,
+ 7, -1, 3,
+ 6, 5, 4
+ };
+ Vector2f dt = p0 - p1;
+ int dx = (dt.x > 0.0f) ? 1 : (dt.x < 0.0f) ? -1 : 0;
+ int dy = (dt.y > 0.0f) ? 1 : (dt.y < 0.0f) ? -1 : 0;
+ int d = 4 + 3*dx - dy;
+ return (d>=0 || d<=8) ? di[d] : -1;
+}
+
+static bool min_positive(float a, float b, float& res)
+{
+ if(a > 0 && b > 0)
+ res = a < b ? a : b;
+ else
+ res = a > b ? a : b;
+ return ((res > 0) || CompareFloatRobust(res, 0.0));
+}
+
+#define LE 0x1
+#define RE 0x2
+#define BE 0x4
+#define TE 0x8
+
+bool SpriteMeshGenerator::path::clip_test(Vector2f p, int side)
+{
+ switch( side ) {
+ case LE: return p.x >= m_min.x;
+ case RE: return p.x <= m_max.x;
+ case TE: return p.y >= m_min.y;
+ case BE: return p.y <= m_max.y;
+ }
+ return false;
+}
+
+Vector2f SpriteMeshGenerator::path::clip_isec(Vector2f p, Vector2f q, int e)
+{
+ double a = (q.y - p.y) / (q.x - p.x);
+ double b = p.y - p.x * a;
+ double x, y;
+ switch(e) {
+ case LE:
+ case RE:
+ x = (e == LE) ? m_min.x : m_max.x;
+ y = x * a + b;
+ break;
+ case TE:
+ case BE:
+ y = (e == TE) ? m_min.y : m_max.y;
+ x = (IsFinite(a)) ? (y - b) / a : p.x;
+ break;
+ }
+ return Vector2f(x,y);
+}
+
+void SpriteMeshGenerator::path::clip_edge(int e)
+{
+ int n=(int)m_path.size();
+ std::vector<vertex> cpath;
+
+ for (int i=0 ; i<n; i++) {
+ Vector2f s = m_path[mod(i+0, n)].p;
+ Vector2f p = m_path[mod(i+1, n)].p;
+ Vector2f c;
+ if (clip_test(p, e)) {
+ if (!clip_test(s, e) ) {
+ c = clip_isec(p, s, e);
+ cpath.push_back(vertex(c));
+ }
+ cpath.push_back(vertex(p));
+ }
+ else
+ if (clip_test(s, e)) {
+ c = clip_isec(s, p, e);
+ cpath.push_back(vertex(c));
+ }
+ }
+ m_path.clear();
+ m_path=cpath;
+}
+
+void SpriteMeshGenerator::path::clip()
+{
+ clip_edge(LE);
+ clip_edge(RE);
+ clip_edge(TE);
+ clip_edge(BE);
+}
+
+static bool lseg_intersect(Vector2f p1, Vector2f p2, Vector2f p3, Vector2f p4)
+{
+ Vector2f e43 = p4-p3;
+ Vector2f e21 = p2-p1;
+ Vector2f e13 = p1-p3;
+
+ double dn = e43.y*e21.x - e43.x*e21.y;
+ double na = e43.x*e13.y - e43.y*e13.x;
+ double nb = e21.x*e13.y - e21.y*e13.x;
+
+ // coincident?
+ if ((fabs(na) < Vector2f::epsilon) &&
+ (fabs(nb) < Vector2f::epsilon) &&
+ (fabs(dn) < Vector2f::epsilon)) {
+ return false;
+ }
+
+ // parallel ?
+ if (fabs(dn) < Vector2f::epsilon)
+ return false;
+
+ // collinear ?
+ double mua = na / dn;
+ double mub = nb / dn;
+ if ((mua < 0) || (mua > 1) || (mub < 0) || (mub > 1))
+ return false;
+
+ return true;
+}
+
+int SpriteMeshGenerator::path::self_intersect(Vector2f p0, Vector2f p1)
+{
+ int n=(int)m_path.size();
+ for(int i=0; i<n; i++) {
+ Vector2f p2 = m_path[mod(i+0, n)].p;
+ Vector2f p3 = m_path[mod(i+1, n)].p;
+ if ((p2==p0) ||
+ (p3==p1) ||
+ (p1==p2) ||
+ (p0==p3))
+ continue;
+ if (lseg_intersect(p0, p1, p2, p3))
+ return 1;
+ }
+ return 0;
+}
+
+bool SpriteMeshGenerator::path::cvx_cost(int i)
+{
+ int n = (int)m_path.size();
+ if (n<5)
+ return false;
+
+ vertex *v = &m_path[i];
+ Vector2f sn = m_path[mod(i-1,n)].n;
+ Vector2f tn = m_path[mod(i+1,n)].n;
+ // detect high convexity -> better triangulation for cyclic geometric shape
+ float q = Dot(sn,tn);
+ if ((q < 0.000) ||
+ CompareFloatRobust(q, 0.0) ||
+ CompareFloatRobust(q, 1.0) ) {
+ v->cost=s_cost(-1, 0.0);
+ return true;
+ }
+
+ Vector2f s0 = m_path[mod(i-1,n)].p;
+ Vector2f t0 = m_path[mod(i+1,n)].p;
+ Vector2f p0 = v->p;
+ Vector2f a0 = ortho(sn);
+ Vector2f c0 = ortho(p0-s0);
+ Vector2f c1 = s0-t0;
+ Vector2f b0 = tn-sn;
+ float c = Dot(c0, c1);
+ float a = Dot(tn, a0);
+ float b = Dot(b0, c0) + Dot(c1, a0);
+
+ float x0=-1,x1=-1,w,d;
+ if (QuadraticPolynomialRootsGeneric(a, -b, c, x0, x1) && min_positive(x0, x1, w)) {
+ Vector2f r0 = m_path[mod(i-2, n)].p;
+ Vector2f u0 = m_path[mod(i+2, n)].p;
+ Vector2f s1 = s0 + sn*w;
+ Vector2f t1 = t0 + tn*w;
+
+ float d0 = det(r0, s1, s0);
+ float d1 = det(s1, p0, s0);
+ float d2 = det(p0, t1, t0);
+ float d3 = det(t1, u0, t0);
+ float d = d0+d1+d2+d3;
+ v->cost = s_cost(d, w);
+ return true;
+ }
+ else {
+ // this should not ever happen.. but handle it anyway
+ d = -1.0;
+ w = 0.0;
+ v->cost = s_cost(d, w);
+ return false;
+ }
+}
+
+bool SpriteMeshGenerator::path::cve_cost(int i)
+{
+ int n=(int)m_path.size();
+ if (n<3)
+ return 0;
+ vertex *v = &m_path[i];
+ Vector2f s = m_path[mod(i-1,n)].p;
+ Vector2f t = m_path[mod(i+1,n)].p;
+ Vector2f u = v->p;
+ float d = det(s,t,u);
+ if ((d>0) || CompareFloatRobust(d, 0.0)) {
+ v->cost = s_cost(d, 0.0);
+ return true;
+ }
+ else {
+ v->cost = s_cost(-1.0, 0.0);
+ return false;
+ }
+}
+
+int SpriteMeshGenerator::path::min_cost()
+{
+ int n = (int)m_path.size();
+ int min_i = -1;
+ float min_c = (std::numeric_limits<float>::max)();
+ for (int i=0; i<n; i++) {
+ vertex v = m_path[i];
+ if (v.cost.c < 0)
+ continue;
+ float c = v.cost.c + v.c;
+ if (c < min_c) {
+ min_c = c;
+ min_i = i;
+ }
+ }
+ return min_i;
+}
+
+bool SpriteMeshGenerator::path::select()
+{
+ int n = (int)m_path.size();
+ if (n<5)
+ return false;
+
+ int i = 0;
+ int m = 0;
+ bool found = false;
+ do {
+ i = min_cost();
+ if (i<0)
+ return false;
+ struct vertex *v = &m_path[i];
+ struct vertex *s = &m_path[mod(i-1, n)];
+ struct vertex *t = &m_path[mod(i+1, n)];
+ struct s_cost cost = v->cost;
+ int isec = 0;
+ if (v->s > 0) {
+ Vector2f s0 = s->p;
+ Vector2f t0 = t->p;
+ // check self intersection
+ isec = self_intersect(s0, t0);
+
+ if (isec) v->cost.c = -1;
+ else {
+ s->c += cost.c;
+ t->c += cost.c;
+ m_invalid.push_back(mod(i+0, n));
+ m_invalid.push_back(mod(i+1, n));
+ found = true;
+ }
+ }
+ else {
+ struct vertex *r = &m_path[mod(i-2, n)];
+ struct vertex *u = &m_path[mod(i+2, n)];
+ Vector2f r0 = r->p;
+ Vector2f u0 = u->p;
+ Vector2f s0 = s->p + s->n*cost.w;
+ Vector2f t0 = t->p + t->n*cost.w;
+ // check self intersection
+ isec |= self_intersect(r0, s0);
+ isec |= self_intersect(s0, t0);
+ isec |= self_intersect(t0, u0);
+
+ if (isec) v->cost.c = -1;
+ else {
+ s->p = s0;
+ t->p = t0;
+ s->c += cost.c;
+ t->c += cost.c;
+ m_invalid.push_back(mod(i-2, n));
+ m_invalid.push_back(mod(i-1, n));
+ m_invalid.push_back(mod(i+1, n));
+ m_invalid.push_back(mod(i+2, n));
+ found = true;
+ }
+ }
+ }while((m++ < n) && !found);
+
+ if (found) {
+ m_path.erase(m_path.begin()+i);
+ // fix invalid indices after erase
+ m = (int)m_invalid.size();
+ for (int k=0; k<m; k++) {
+ if (m_invalid[k] > i)
+ m_invalid[k] = m_invalid[k]-1;
+ }
+ }
+ return found;
+}
+
+int SpriteMeshGenerator::path::find_max_distance(int i0)
+{
+ int n = m_path.size();
+
+ Vector2f a = m_path[i0].p;
+ float dm = -1;
+ int mi = -1;
+
+ for (int i=0; i<n; i++) {
+ Vector2f b = m_path[mod(i, n)].p;
+ float ba = Magnitude(b-a);
+ if (ba < dm)
+ continue;
+ dm = ba;
+ mi = i;
+ }
+ return mi;
+}
+
+int SpriteMeshGenerator::path_segment::max_distance(std::vector<vertex> path, int i0, int i1)
+{
+ int n = path.size();
+
+ Vector2f a = path[i0].p;
+ Vector2f b = path[i1].p;
+
+ float dm = -1;
+ int mi = -1;
+ m_cnt=0;
+ for (int i=i0; i != i1; i=mod(++i, n), m_cnt++) {
+ float dq = distance_point_line(path[i].p, a, b);
+ if (dq < dm)
+ continue;
+ dm = dq;
+ mi = i;
+ }
+ return mi;
+}
+
+void SpriteMeshGenerator::path::simplify(float q, int mode)
+{
+ m_path.clear();
+ m_path = m_path0;
+ int m;
+ int n = (int)m_path.size();
+ int lim = (float)n*(1.0f - clamp(q, 0.0f, 1.0f));
+
+ if (n < 5) goto bail_out;
+
+ if (mode==kPathEmbed) {
+ if (lim < 5) lim=5;
+
+ // mark all vertices invalid
+ for (int i=0; i<n; i++)
+ m_invalid.push_back(i);
+
+ do {
+ n = (int)m_path .size();
+ m = (int)m_invalid.size();
+ for (int i=0; i<m; i++) {
+ dec(m_invalid[i]);
+ inf(m_invalid[i]);
+ }
+ for (int i=0; i<m; i++) {
+ int k = m_invalid[i];
+ if (m_path[k].s > 0)
+ cve_cost(k);
+ else
+ cvx_cost(k);
+ }
+ m_invalid.clear();
+ if (select() == false)
+ break;
+ }while(n > lim);
+ }
+ else {
+ if (lim < 4) lim=4;
+
+ int i0 = find_max_distance( 0 );
+ int i1 = find_max_distance(i0 );
+
+ path_segment ls = path_segment(m_path, i0, i1);
+ path_segment rs = path_segment(m_path, i1, i0);
+
+ std::priority_queue<path_segment, std::vector<path_segment>, compare_path_segment> pq;
+
+ if (ls.m_mx>=0) pq.push(ls);
+ if (rs.m_mx>=0) pq.push(rs);
+
+ std::vector<bool> select(n);
+ std::fill(select.begin(), select.end(), false);
+
+ select[i0] = true;
+ select[i1] = true;
+
+ int count=2;
+ while (!pq.empty()) {
+ path_segment ts = pq.top();
+ pq.pop();
+
+ select[ts.m_mx]=true;
+ if (++count == lim)
+ break;
+ // split
+ ls = path_segment(m_path, ts.m_i0, ts.m_mx);
+ if (ls.m_mx >= 0) pq.push(ls);
+ rs = path_segment(m_path, ts.m_mx, ts.m_i1);
+ if (rs.m_mx >= 0) pq.push(rs);
+ }
+
+ m_path.clear();
+ for (int i=0; i<n; i++) {
+ if (select[i]==1)
+ m_path.push_back(m_path0[i]);
+ }
+ }
+bail_out:
+ if (m_sign == '+' && mode==1)
+ clip();
+}
+
+void SpriteMeshGenerator::path::fit(std::vector<int>& ci, int i0, int i1)
+{
+ int n = (int)m_path.size();
+
+ if ((mod(i0+1, n) == i1) || (i0==i1)) {
+ ci.push_back(i1);
+ return;
+ }
+
+ Vector2f a = m_path[i0].p;
+ Vector2f b = m_path[i1].p;
+ edge e = edge(a, b);
+ int im = -1;
+ float qm = -1;
+ int ic = i0;
+ do {
+ float qc = fabs(e.grad(m_path[ic].p));
+ if (qc > qm) {
+ im = ic;
+ qm = qc;
+ }
+ if (ic == i1)
+ break;
+ ic = mod(ic+1, n);
+ }while(1);
+
+ float lim = std::max(fabs(e.m_a)*0.5, fabs(e.m_b)*0.5);
+ if ( (qm <= lim) || (im < 0)) {
+ ci.push_back(i1);
+ return;
+ }
+ fit(ci, i0, im);
+ fit(ci, im, i1);
+}
+
+bool SpriteMeshGenerator::path::opt(float bias)
+{
+ int n = (int)m_path.size();
+ if (n<3)
+ return false;
+
+ std::vector<int> cp;
+ std::vector<int> ci;
+
+ int s = -1;
+ int dt[8] = {0};
+ cp.push_back(0);
+ for (int i=0; i<n; i++) {
+ Vector2f p0 = m_path[i].p;
+ Vector2f p1 = m_path[mod(i+1, n)].p;
+
+ int d = dir(p0, p1);
+ if (d < 0)
+ continue;
+ dt[d] = 1;
+ if (s < 0) {
+ s = d;
+ continue;
+ }
+ // cut path, if direction change is not possible for straight line
+ if (!(d == mod(s-1, 8) || d == mod(s+1, 8) || d == s) ||
+ ((dt[0] + dt[1] +
+ dt[2] + dt[3] +
+ dt[4] + dt[5] +
+ dt[6] + dt[7] ) > 2)) {
+ memset(dt, 0, 8*sizeof(int));
+ s = -1;
+ cp.push_back(i);
+ }
+ }
+ // fit sub paths to straight line
+ int m = (int)cp.size();
+ for (int i=0; i<m; i++)
+ fit(ci, cp[i], cp[mod(i+1, m)]);
+
+ //rm extra vertices
+ std::vector<vertex> tmp = m_path;
+ m_path.clear();
+ for(int i=0; i<ci.size(); i++)
+ m_path.push_back(tmp[ci[i]]);
+
+ // unit normals
+ n = (int)m_path.size();
+ for (int i=0; i<n; i++)
+ dec(i);
+
+ // bias
+ for (int i=0; i<n; i++)
+ m_path[i].p += m_path[i].n*bias;
+
+ return true;
+}
+
+bool SpriteMeshGenerator::invmask(std::vector<vertex>& outline)
+{
+ int n = (int)outline.size();
+ if (n <= 0)
+ return false;
+
+ int xa = (int)outline[ 0].p.x;
+ Vector2f pp = outline[n-1].p;
+
+ for (int i=0; i<n; i++) {
+ Vector2f p0 = outline[i].p;
+
+ while (((i+1)<n) && (p0.y == outline[i+1].p.y) ) {
+ int d = dir(p0, outline[i+1].p);
+ if ((d==1 && (pp.y < p0.y)) ||
+ (d==5 && (pp.y > p0.y)) )
+ p0 = outline[i+1].p;
+ i++;
+ }
+ int y = (int)p0.y;
+ int x0 = min(xa, (int)p0.x);
+ int x1 = max(xa, (int)p0.x);
+ for (int x=x0; x<x1; x++)
+ m_mask_cur.inv(x, y);
+
+ if (((i+1) < n) &&
+ (pp.y != p0.y) && (outline[i+1].p.y == pp.y) ) {
+ y = (int)p0.y;
+ x0 = min(xa, (int)p0.x);
+ x1 = max(xa, (int)p0.x);
+ for (int x=x0; x<x1; x++)
+ m_mask_cur.inv(x, y);
+ }
+ pp = p0;
+ }
+ for (int i=0; i<n; i++) {
+ Vector2f p = outline[i].p;
+ m_mask_cur.rst(p.x, p.y);
+ }
+ return true;
+}
+
+bool SpriteMeshGenerator::trace(Vector2f p0, Vector2f p1, Vector2f &p)
+{
+ static int dt[8][2] = {
+ { -1, 0 },
+ { -1, -1 },
+ { 0, -1 },
+ { 1, -1 },
+ { 1, 0 },
+ { 1, 1 },
+ { 0, 1 },
+ { -1, 1 }
+ };
+
+ int t0 = dir(p0, p1);
+ if (t0 < 0)
+ goto error;
+
+ for (int i = 0; i < 8; i++) {
+ int t = (t0 + i) % 8;
+ int x = (int)p1.x + dt[t][0];
+ int y = (int)p1.y + dt[t][1];
+ if (m_mask_cur.tst(x, y)) {
+ p = Vector2f(x, y);
+ return true;
+ }
+ }
+error:
+ p = Vector2f(-1, -1);
+ return false;
+}
+
+bool SpriteMeshGenerator::contour(std::vector<vertex>& outline, int &sign, float &area)
+{
+ do {
+ outline.clear();
+ int b = m_mask_cur.first();
+ if (b < 0)
+ return false;
+
+ int x = b % m_mask_cur.w;
+ int y = b / m_mask_cur.w;
+ Vector2f curr = Vector2f (x, y);
+ Vector2f stop;
+ Vector2f prev;
+ Vector2f next;
+ area = 0.0;
+ sign = m_mask_org.tst(x, y) ? '+' : '-';
+ stop = curr;
+ next = curr;
+ curr.x = curr.x-1;
+
+ do {
+ prev = curr;
+ curr = next;
+ outline.push_back(vertex(curr));
+ if (trace(prev, curr, next) == false)
+ break;
+
+ area += curr.x * next.y -
+ curr.y * next.x;
+ if (next == stop)
+ break;
+ } while(true);
+
+ invmask(outline);
+ if (fabs(area)<4) {
+ area=0;
+ continue;
+ }
+ if (((sign=='+') && (area < 0)) ||
+ ((sign=='-') && (area > 0)) )
+ std::reverse(outline.begin(), outline.end());
+ area = fabs(area);
+ break;
+ }while(1);
+ return true;
+}
+#endif //ENABLE_SPRITES
diff --git a/Runtime/Geometry/SpriteMeshGenerator.h b/Runtime/Geometry/SpriteMeshGenerator.h
new file mode 100644
index 0000000..927b629
--- /dev/null
+++ b/Runtime/Geometry/SpriteMeshGenerator.h
@@ -0,0 +1,269 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_SPRITES
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Utilities/dynamic_bitset.h"
+
+
+class SpriteMeshGenerator
+{
+public:
+ struct s_cost
+ {
+ s_cost(){};
+ s_cost(float _c, float _w)
+ {
+ c=_c;
+ w=_w;
+ };
+ float c;
+ float w;
+ };
+
+ struct vertex
+ {
+ vertex(){};
+ vertex(Vector2f pos)
+ {
+ p = pos;
+ };
+ Vector2f p;
+ Vector2f n; // normal
+ int s; // sign -> indicating concavity
+ float c;
+ struct s_cost cost;
+ };
+
+ class path_segment
+ {
+ public:
+ path_segment(std::vector<vertex> path, int i0, int i1)
+ {
+ m_i0 = i0;
+ m_i1 = i1;
+ m_mx = max_distance(path, i0, i1);
+ };
+
+ int m_i0;
+ int m_i1;
+ int m_mx;
+ int m_cnt;
+ private:
+ int max_distance(std::vector<vertex> path, int i0, int i1);
+ };
+
+ class compare_path_segment {
+ public:
+ bool operator()(path_segment& s0, path_segment& s1)
+ {
+ return (s0.m_cnt < s1.m_cnt);
+ }
+ };
+
+ class path
+ {
+ public:
+ path(){};
+ path(const std::vector<vertex>& p, int w, int h, int sign, float area, float bias)
+ {
+ m_bx = w;
+ m_by = h;
+ m_area = area;
+ m_sign = sign;
+ m_path = p;
+ opt (bias);
+ bbox();
+ m_path0 = m_path;
+ }
+
+ std::vector<vertex> m_path;
+
+ void bbox ();
+ void simplify (float q, int mode);
+
+ bool isHole() const { return m_sign == '-'; }
+
+ const Vector2f& GetMin() const { return m_min; }
+ const Vector2f& GetMax() const { return m_max; }
+
+ private:
+ int find_max_distance(int i0);
+
+ void fit (std::vector<int>& ci, int i0, int i1);
+ bool opt (float bias);
+
+ bool dec (int i);
+ bool inf (int i);
+ bool select();
+ bool cvx_cost(int i);
+ bool cve_cost(int i);
+ int min_cost();
+ int self_intersect(Vector2f p0, Vector2f p1);
+
+ void clip();
+ void clip_edge(int e);
+ bool clip_test(Vector2f p, int side);
+ Vector2f clip_isec(Vector2f p, Vector2f q, int e);
+
+ int m_bx;
+ int m_by;
+ int m_sign;
+ float m_area;
+ Vector2f m_min;
+ Vector2f m_max;
+ std::vector<vertex> m_path0;
+ std::vector<int> m_invalid;
+ };
+
+ struct mask
+ {
+ mask(){};
+ mask(ColorRGBA32 *img, int width, int height, unsigned char acut, unsigned int dn)
+ {
+ w = width;
+ h = height;
+ int n = w*h;
+ dynamic_bitset bv;
+ bv.resize(n);
+ for (int y=0; y<height; y++)
+ for (int x=0; x<width; x++) {
+ if (img[x+y*width].a > acut)
+ bv.set(x+y*w);
+ }
+
+ if (dn)
+ this->dilate(dn, bv);
+
+ w+=1;
+ h+=1;
+ m_bv.resize(w*h);
+ for (int y=0; y<height; y++)
+ for (int x=0; x<width; x++) {
+ if (bv.test(x+y*width)) {
+ m_bv.set((x+0)+(y+0)*w);
+ m_bv.set((x+1)+(y+1)*w);
+ m_bv.set((x+0)+(y+1)*w);
+ m_bv.set((x+1)+(y+0)*w);
+ }
+ }
+ }
+
+ bool tst(int x, int y)
+ {
+ if ((x<0) || (x>=w) ||
+ (y<0) || (y>=h) )
+ return 0;
+ int i=x+y*w;
+ return m_bv.test(i);
+ }
+
+ void set(int x, int y)
+ {
+ if ((x<0) || (x>=w) ||
+ (y<0) || (y>=h) )
+ return;
+ int i=x+y*w;
+ m_bv.set(i);
+ }
+
+ void rst(int x, int y)
+ {
+ if ((x<0) || (x>=w) ||
+ (y<0) || (y>=h) )
+ return;
+ int i=x+y*w;
+ m_bv[i]=0;
+ }
+
+ void inv(int x, int y)
+ {
+ if ((x<0) || (x>=w) ||
+ (y<0) || (y>=h) )
+ return;
+ int i=x+y*w;
+ m_bv[i].flip();
+ }
+
+ int first()
+ {
+ int n=(int)m_bv.size();
+ for (int i=0; i<n; i++)
+ if (m_bv.test(i))
+ return i;
+ return -1;
+ }
+
+ bool dilate(int n, dynamic_bitset &bv)
+ {
+ if ((w==0) || (h==0))
+ return false;
+ UInt32 *md = new UInt32[w*h];
+ if (!mdist(md, bv))
+ return false;
+
+ for (int i=0; i<w*h; i++) {
+ if (md[i] <= n)
+ bv.set(i);
+ }
+ delete md;
+ return true;
+ }
+ int w;
+ int h;
+ dynamic_bitset m_bv;
+ private:
+ bool mdist(UInt32 *md, dynamic_bitset& bv )
+ {
+ if (!md)
+ return false;
+
+ for (int y=0; y<h; y++)
+ for (int x=0; x<w; x++) {
+ int i = x+y*w;
+ if (bv.test(i))
+ md[i] = 0;
+ else {
+ md[i] = w+h;
+ if (y>0) md[i] = min(md[i], md[i-w]+1);
+ if (x>0) md[i] = min(md[i], md[i-1]+1);
+ }
+ }
+
+ for (int y=h-1; y>=0; y--)
+ for (int x=w-1; x>=0; x--) {
+ int i = x+y*w;
+ if ((y+1) < h) md[i] = min(md[i], md[i+w]+1);
+ if ((x+1) < w) md[i] = min(md[i], md[i+1]+1);
+ }
+ return true;
+ }
+ };
+
+public:
+ void Decompose(std::vector<Vector2f>* vertices, std::vector<int>* indices);
+ void MakeShape(ColorRGBA32* image, int imageWidth, int imageHeight, float hullTolerance, unsigned char alphaTolerance, bool holeDetection, unsigned int extrude, float bias, int mode);
+ bool FindBounds(Rectf& bounds);
+
+ const std::vector<path>& GetPaths() const { return m_paths; }
+
+
+private:
+ bool trace(Vector2f p0, Vector2f p1, Vector2f &p);
+ bool invmask(std::vector<vertex>& outline);
+ bool contour(std::vector<vertex>& outline, int &sign, float &area);
+
+ std::vector<path> m_paths;
+
+ float evaluateLOD(const float areaHint, float area);
+
+ struct mask m_mask_org;
+ struct mask m_mask_cur;
+};
+
+#endif //ENABLE_SPRITES
diff --git a/Runtime/Geometry/TangentSpaceCalculation.cpp b/Runtime/Geometry/TangentSpaceCalculation.cpp
new file mode 100644
index 0000000..f7b17f1
--- /dev/null
+++ b/Runtime/Geometry/TangentSpaceCalculation.cpp
@@ -0,0 +1,534 @@
+#include "UnityPrefix.h"
+#include "TangentSpaceCalculation.h"
+#include "Runtime/Math/Matrix3x3.h"
+#include "Runtime/Math/Vector2.h"
+#include "Plane.h"
+#include <vector>
+
+using std::vector;
+/*
+void CreateTangentSpaceTangents(const Vector3f* vertex, const Vector2f* texcoord, const Vector3f* normals, Matrix3x3f* avgOrthonormalBases, int vertexCount, const UInt16* indices, int triangleCount)
+{
+ Vector3f *tan1 = new Vector3f[vertexCount * 2];
+ Vector3f *tan2 = tan1 + vertexCount;
+ memset(tan1, 0, vertexCount * sizeof(Vector3f) * 2);
+
+ for (int a = 0; a < triangleCount; a++)
+ {
+ int i1 = indices[0];
+ int i2 = indices[1];
+ int i3 = indices[2];
+
+ const Vector3f& v1 = vertex[i1];
+ const Vector3f& v2 = vertex[i2];
+ const Vector3f& v3 = vertex[i3];
+
+ const Vector2f& w1 = texcoord[i1];
+ const Vector2f& w2 = texcoord[i2];
+ const Vector2f& w3 = texcoord[i3];
+
+ float x1 = v2.x - v1.x;
+ float x2 = v3.x - v1.x;
+ float y1 = v2.y - v1.y;
+ float y2 = v3.y - v1.y;
+ float z1 = v2.z - v1.z;
+ float z2 = v3.z - v1.z;
+
+ float s1 = w2.x - w1.x;
+ float s2 = w3.x - w1.x;
+ float t1 = w2.y - w1.y;
+ float t2 = w3.y - w1.y;
+
+ float r = 1.0F / (s1 * t2 - s2 * t1);
+ Vector3f sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
+ Vector3f tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
+
+ tan1[i1] += sdir;
+ tan1[i2] += sdir;
+ tan1[i3] += sdir;
+
+ tan2[i1] += tdir;
+ tan2[i2] += tdir;
+ tan2[i3] += tdir;
+
+ indices += 3;
+ }
+
+
+ for (long a = 0; a < vertexCount; a++)
+ {
+ Vector3f normal = normals[a];
+ Vector3f tangent = tan1[a];
+
+
+ // Gram-Schmidt orthogonalize
+ (tangent - normal * (normal * tangent)));
+
+ // Calculate handedness
+ //tangent[a].w = (normal % tangent * tan2[a] < 0.0F) ? -1.0F : 1.0F;
+
+ avgOrthonormalBases[a].SetOrthoNormalBasis (tangent, Cross (normal, tangent), normal);
+ }
+
+ delete[] tan1;
+}*/
+
+/*
+void CreateTangentSpaceTangents (const Vector3f* pPositions, const Vector2f* tex, const Vector3f* normals, Matrix3x3f* avgOrthonormalBases, int vertexCount, const UInt16* indices, int faceCount)
+{
+ vector<Vector3f> sVector, tVector;
+ vector<Vector3f> avgS, avgT;
+
+ avgS.resize (vertexCount);
+ avgT.resize (vertexCount);
+
+ sVector.reserve (faceCount * 3);
+ tVector.reserve (faceCount * 3);
+
+ // for each face, calculate its S,T & SxT vector, & store its edges
+ for (int f = 0; f < faceCount * 3; f += 3 )
+ {
+ Vector3f edge0;
+ Vector3f edge1;
+ Vector3f s;
+ Vector3f t;
+
+ // create an edge out of x, s and t
+ edge0.x = pPositions[ indices[ f + 1 ] ].x - pPositions[ indices[ f ] ].x;
+ edge0.y = tex[ indices[ f + 1 ] ].x - tex[ indices[ f ] ].x;
+ edge0.z = tex[ indices[ f + 1 ] ].y - tex[ indices[ f ] ].y;
+
+ // create an edge out of x, s and t
+ edge1.x = pPositions[ indices[ f + 2 ] ].x - pPositions[ indices[ f ] ].x;
+ edge1.y = tex[ indices[ f + 2 ] ].x - tex[ indices[ f ] ].x;
+ edge1.z = tex[ indices[ f + 2 ] ].y - tex[ indices[ f ] ].y;
+
+ Vector3f sxt = Cross (edge0, edge1);
+
+ float a = sxt.x;
+ float b = sxt.y;
+ float c = sxt.z;
+
+ float ds_dx = 0.0F;
+ if ( Abs( a ) > Vector3f::epsilon )
+ ds_dx = -b / a;
+
+ float dt_dx = 0.0F;
+ if ( Abs( a ) > Vector3f::epsilon )
+ dt_dx = -c / a;
+
+ // create an edge out of y, s and t
+ edge0.x = pPositions[ indices[ f + 1 ] ].y - pPositions[ indices[ f ] ].y;
+ // create an edge out of y, s and t
+ edge1.x = pPositions[ indices[ f + 2 ] ].y - pPositions[ indices[ f ] ].y;
+
+ sxt = Cross (edge0, edge1);
+
+ a = sxt.x;
+ b = sxt.y;
+ c = sxt.z;
+
+ float ds_dy = 0.0F;
+ if ( Abs( a ) > Vector3f::epsilon )
+ ds_dy = -b / a;
+
+ float dt_dy = 0.0F;
+ if ( Abs( a ) > Vector3f::epsilon )
+ dt_dy = -c / a;
+
+ // create an edge out of z, s and t
+ edge0.x = pPositions[ indices[ f + 1 ] ].z - pPositions[ indices[ f ] ].z;
+ // create an edge out of z, s and t
+ edge1.x = pPositions[ indices[ f + 2 ] ].z - pPositions[ indices[ f ] ].z;
+
+ sxt = Cross (edge0, edge1);
+
+ a = sxt.x;
+ b = sxt.y;
+ c = sxt.z;
+
+ float ds_dz = 0.0F;
+ if ( Abs( a ) > Vector3f::epsilon )
+ ds_dz = -b / a;
+
+ float dt_dz = 0.0F;
+ if ( Abs( a ) > Vector3f::epsilon )
+ dt_dz = -c / a;
+
+ // generate coordinate frame from the gradients
+ s = Vector3f( ds_dx, ds_dy, ds_dz );
+ t = Vector3f( dt_dx, dt_dy, dt_dz );
+
+ s = NormalizeSafe (s);
+ t = NormalizeSafe (t);
+
+ // save vectors for this face
+ sVector.push_back( s );
+ tVector.push_back( t );
+ }
+
+
+ for ( int p = 0; p < vertexCount; p ++ )
+ {
+ avgS[p] = Vector3f::zero;
+ avgT[p] = Vector3f::zero;
+ }
+
+ // go through faces and add up the bases for each vertex
+ for ( int face = 0; face < faceCount; ++face )
+ {
+ // sum bases, so we smooth the tangent space across edges
+ avgS[ indices[ face * 3 ] ] += sVector[ face ];
+ avgT[ indices[ face * 3 ] ] += tVector[ face ];
+
+ avgS[ indices[ face * 3 + 1 ] ] += sVector[ face ];
+ avgT[ indices[ face * 3 + 1 ] ] += tVector[ face ];
+
+ avgS[ indices[ face * 3 + 2 ] ] += sVector[ face ];
+ avgT[ indices[ face * 3 + 2 ] ] += tVector[ face ];
+ }
+
+ // now renormalize
+ for ( int p = 0; p < vertexCount; p ++ )
+ {
+ Vector3f normal = normals[p];
+
+
+ //OrthoNormalize (&normal, &avgS[p], &avgT[p]);
+
+ avgOrthonormalBases[p].SetOrthoNormalBasis (avgS[p], avgT[p], normal);
+
+ #if DEBUGMODE
+ float det = avgOrthonormalBases[p].GetDeterminant ();
+ AssertIf (!CompareApproximately (det, 1.0F,0.001) && !CompareApproximately (det, -1.0F,0.001));
+ #endif
+// AssertIf (!CompareApproximately (avgOrthonormalBases[p].MultiplyPoint3Transpose (Vector3f (0,0,1)), normal));
+ }
+}*/
+
+/*
+void CreateTangentSpaceTangents (const Vector3f* pPositions, const Vector2f* tex, const Vector3f* normals, Matrix3x3f* avgOrthonormalBases, int vertexCount, const UInt16* indices, int faceCount)
+{
+
+
+ Vector3f v0,v1,v2, tanu, tanv;
+
+ Vector3f p0,p1,p2;
+
+ Vector3f d1,d2;
+
+ float uv[3][2],det,u,v,l1,l2;
+
+ int i,j,k;
+ vector<Vector3f> sVector, tVector;
+ vector<Vector3f> avgS, avgT;
+
+ avgS.resize (vertexCount);
+ avgT.resize (vertexCount);
+
+ sVector.reserve (faceCount * 3);
+ tVector.reserve (faceCount * 3);
+
+ for( i=0;i<vertexCount;i++ )
+
+ {
+
+ sVector[i]=Vector3f(0.0, 0.0, 0.0);
+
+ tVector[i]=Vector3f(0.0, 0.0, 0.0);
+
+ }
+
+ k=0;
+ for( i=0;i<faceCount;i++,k+=3 )
+
+ {
+
+ v0=*((Vector3f *)&pPositions[indices[k]]);
+
+ v1=*((Vector3f *)&pPositions[indices[k+1]])-v0;
+
+ v2=*((Vector3f *)&pPositions[indices[k+2]])-v0;
+
+ uv[0][0]=-tex[indices[k]].x;
+
+ uv[0][1]=tex[indices[k]].y;
+
+ uv[1][0]=-tex[indices[k+1]].x-uv[0][0];
+
+ uv[1][1]=tex[indices[k+1]].y-uv[0][1];
+
+ uv[2][0]=-tex[indices[k+2]].x-uv[0][0];
+
+ uv[2][1]=tex[indices[k+2]].y-uv[0][1];
+
+ det=(uv[1][0]*uv[2][1])-(uv[2][0]*uv[1][1]);
+
+
+
+ if (fabsf(det)<0.000000001f){
+ continue;
+ }
+
+ u=0; v=0;
+
+ u-=uv[0][0]; v-=uv[0][1];
+
+ p0=v0+v1*((u*uv[2][1]-uv[2][0]*v)/det)+v2*((uv[1][0]*v-u*uv[1][1])/det);
+
+
+
+ u=1; v=0;
+
+ u-=uv[0][0]; v-=uv[0][1];
+
+ p1=v0+v1*((u*uv[2][1]-uv[2][0]*v)/det)+v2*((uv[1][0]*v-u*uv[1][1])/det);
+
+
+
+ u=0; v=1;
+
+ u-=uv[0][0]; v-=uv[0][1];
+
+ p2=v0+v1*((u*uv[2][1]-uv[2][0]*v)/det)+v2*((uv[1][0]*v-u*uv[1][1])/det);
+
+
+
+ d1=p2-p0;
+
+ d2=p1-p0;
+
+ l1=Magnitude(d1);
+
+ l2=Magnitude(d2);
+
+ d1*=1.0f/l1;
+
+ d2*=1.0f/l2;
+
+
+
+ j=indices[k];
+
+ sVector[j].x+=d1.x; sVector[j].y+=d1.y; sVector[j].z+=d1.z;
+
+ tVector[j].x+=d2.x; tVector[j].y+=d2.y; tVector[j].z+=d2.z;
+
+
+
+ j=indices[k+1];
+
+ sVector[j].x+=d1.x; sVector[j].y+=d1.y; sVector[j].z+=d1.z;
+
+ tVector[j].x+=d2.x; tVector[j].y+=d2.y; tVector[j].z+=d2.z;
+
+
+
+ j=indices[k+2];
+
+ sVector[j].x+=d1.x; sVector[j].y+=d1.y; sVector[j].z+=d1.z;
+
+ tVector[j].x+=d2.x; tVector[j].y+=d2.y; tVector[j].z+=d2.z;
+
+ }
+
+
+
+ for( i=0;i<vertexCount;i++ )
+
+ {
+
+ //v0.vec(vert[i].tanu[0],vert[i].tanu[1],vert[i].tanu[2]);
+ v0 = Vector3f(sVector[i].x,sVector[i].y,sVector[i].z);
+ //v0.normalize();
+ v0 = NormalizeRobust(v0);
+
+ //v1.vec(vert[i].tanv[0],vert[i].tanv[1],vert[i].tanv[2]);
+ v1 = Vector3f(tVector[i].x,tVector[i].y,tVector[i].z);
+ //v1 = NormalizeRobust(v1);
+
+ Vector3f n(normals[i].x,normals[i].y,normals[i].z);
+
+ if (SqrMagnitude(v1)<0.0001f)
+ {
+ v1 = Cross(n,v0);
+ }
+
+ v1 = NormalizeRobust(v1);
+
+
+ sVector[i].x=v0.x;
+
+ sVector[i].y=v0.y;
+
+ sVector[i].z=v0.z;
+
+ tVector[i].x=v1.x;
+
+ tVector[i].y=v1.y;
+
+ tVector[i].z=v1.z;
+
+ avgOrthonormalBases[i].SetOrthoNormalBasis (tVector[i], sVector[i], n);
+ }
+
+}*/
+
+void CreateTangentSpaceTangents (const Vector3f* pPositions, const Vector2f* tex, const Vector3f* normals, Matrix3x3f* avgOrthonormalBases, int vertexCount, const int* indices, int faceCount)
+{
+
+
+ Vector3f v0,v1,v2, tanu, tanv;
+
+ Vector3f p0,p1,p2;
+
+ Vector3f d1,d2;
+
+ float uv[3][2],det,u,v,l1,l2;
+
+ int i,j,k;
+ vector<Vector3f> sVector, tVector;
+ vector<Vector3f> avgS, avgT;
+
+ avgS.resize (vertexCount);
+ avgT.resize (vertexCount);
+
+ sVector.reserve (faceCount * 3);
+ tVector.reserve (faceCount * 3);
+
+ for( i=0;i<vertexCount;i++ )
+
+ {
+
+ sVector[i]=Vector3f(0.0, 0.0, 0.0);
+
+ tVector[i]=Vector3f(0.0, 0.0, 0.0);
+
+ }
+
+ k=0;
+ for( i=0;i<faceCount;i++,k+=3 )
+
+ {
+
+ v0=*((Vector3f *)&pPositions[indices[k]]);
+
+ v1=*((Vector3f *)&pPositions[indices[k+1]])-v0;
+
+ v2=*((Vector3f *)&pPositions[indices[k+2]])-v0;
+
+ uv[0][0]=tex[indices[k]].x;
+
+ uv[0][1]=tex[indices[k]].y;
+
+ uv[1][0]=tex[indices[k+1]].x-uv[0][0];
+
+ uv[1][1]=tex[indices[k+1]].y-uv[0][1];
+
+ uv[2][0]=tex[indices[k+2]].x-uv[0][0];
+
+ uv[2][1]=tex[indices[k+2]].y-uv[0][1];
+
+ det=(uv[1][0]*uv[2][1])-(uv[2][0]*uv[1][1]);
+
+
+
+ if (fabsf(det)<0.000001f){
+ continue;
+ }
+
+ u=0; v=0;
+
+ u-=uv[0][0]; v-=uv[0][1];
+
+ p0=v0+v1*((u*uv[2][1]-uv[2][0]*v)/det)+v2*((uv[1][0]*v-u*uv[1][1])/det);
+
+
+
+ u=1; v=0;
+
+ u-=uv[0][0]; v-=uv[0][1];
+
+ p1=v0+v1*((u*uv[2][1]-uv[2][0]*v)/det)+v2*((uv[1][0]*v-u*uv[1][1])/det);
+
+
+
+ u=0; v=1;
+
+ u-=uv[0][0]; v-=uv[0][1];
+
+ p2=v0+v1*((u*uv[2][1]-uv[2][0]*v)/det)+v2*((uv[1][0]*v-u*uv[1][1])/det);
+
+
+
+ d1=p2-p0;
+
+ d2=p1-p0;
+
+ l1=Magnitude(d1);
+
+ l2=Magnitude(d2);
+
+ d1*=1.0f/l1;
+
+ d2*=1.0f/l2;
+
+
+
+ j=indices[k];
+
+ sVector[j] += d1;
+
+ tVector[j].x+=d2.x; tVector[j].y+=d2.y; tVector[j].z+=d2.z;
+
+
+
+ j=indices[k+1];
+
+ sVector[j].x+=d1.x; sVector[j].y+=d1.y; sVector[j].z+=d1.z;
+
+ tVector[j].x+=d2.x; tVector[j].y+=d2.y; tVector[j].z+=d2.z;
+
+
+
+ j=indices[k+2];
+
+ sVector[j].x+=d1.x; sVector[j].y+=d1.y; sVector[j].z+=d1.z;
+
+ tVector[j].x+=d2.x; tVector[j].y+=d2.y; tVector[j].z+=d2.z;
+
+ }
+
+
+
+ for( i=0;i<vertexCount;i++ )
+
+ {
+
+ v0 = Vector3f(sVector[i].x,sVector[i].y,sVector[i].z);
+ v0 = NormalizeRobust(v0);
+
+ v1 = Vector3f(tVector[i].x,tVector[i].y,tVector[i].z);
+
+ Vector3f n(normals[i].x,normals[i].y,normals[i].z);
+
+ if (SqrMagnitude(v1)<0.0001f)
+ {
+ v1 = Cross(v0,n);
+ }
+ v1 = NormalizeRobust(v1);
+
+
+ sVector[i]=v0;
+
+ tVector[i].x=v1.x;
+
+ tVector[i].y=v1.y;
+
+ tVector[i].z=v1.z;
+
+ avgOrthonormalBases[i].SetOrthoNormalBasis (tVector[i], sVector[i], n);
+ }
+
+}
+
diff --git a/Runtime/Geometry/TangentSpaceCalculation.h b/Runtime/Geometry/TangentSpaceCalculation.h
new file mode 100644
index 0000000..2e925b0
--- /dev/null
+++ b/Runtime/Geometry/TangentSpaceCalculation.h
@@ -0,0 +1,13 @@
+#ifndef TANGENTSPACECALCULATION_H
+#define TANGENTSPACECALCULATION_H
+
+class Vector3f;
+class Vector2f;
+class Matrix3x3f;
+
+void CreateTangentSpaceTangents (const Vector3f* pPositions, const Vector2f* tex, const Vector3f* normals,
+ Matrix3x3f* avgOrthonormalBases, int vertexCount,
+ const int* indices, int faceCount);
+
+
+#endif
diff --git a/Runtime/Geometry/TextureAtlas.cpp b/Runtime/Geometry/TextureAtlas.cpp
new file mode 100644
index 0000000..6183bc3
--- /dev/null
+++ b/Runtime/Geometry/TextureAtlas.cpp
@@ -0,0 +1,588 @@
+#include "UnityPrefix.h"
+#include "TextureAtlas.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+
+using namespace std;
+
+// Just implemented from this article:
+// http://www.blackpawn.com/texts/lightmaps/default.html
+
+bool PackTextureAtlas( Texture2D* atlas, int atlasMaximumSize, int textureCount, Texture2D** textures, Rectf* outRects, int padding, int textureMode );
+
+struct Node
+{
+ Node() : taken(false) { child[0] = NULL; child[1] = NULL; }
+ ~Node() { delete child[0]; delete child[1]; }
+
+ void Reset()
+ {
+ delete child[0]; delete child[1];
+ child[0] = NULL; child[1] = NULL;
+ taken = false;
+ }
+
+ Node* Insert( float width, float height, float padding, bool use4PixelBoundaries );
+
+ Node* child[2];
+ Rectf rect;
+ bool taken;
+};
+
+
+Node* Node::Insert( float width, float height, float padding, bool use4PixelBoundaries )
+{
+ // if we're not leaf, try inserting into children
+ if( child[0] )
+ {
+ Node* newNode = child[0]->Insert( width, height, padding, use4PixelBoundaries );
+ if( newNode )
+ return newNode;
+ return child[1]->Insert( width, height, padding, use4PixelBoundaries );
+ }
+
+ // we are leaf
+
+ if( taken )
+ return NULL; // already taken
+
+ // will it fit?
+ // 0.5 float error margin and we don't care about sub-texel overlaps anyway
+ if( width > rect.Width() - padding + 0.5f || height > rect.Height() - padding + 0.5f )
+ return NULL; // won't fit
+
+ // if this a perfect or nearly perfect fit, take it
+ float dw = rect.Width() - width;
+ float dh = rect.Height() - height;
+ if( dw <= padding*2 && dh <= padding*2 )
+ {
+ taken = true;
+ return this;
+ }
+ if( use4PixelBoundaries && dw < 4 && dh < 4 )
+ {
+ taken = true;
+ return this;
+ }
+
+ // split the node
+ child[0] = new Node();
+ child[1] = new Node();
+
+ // decide which way to split
+ if( dw > dh )
+ {
+ // horizontal children
+ int split = int(width + padding);
+ if( use4PixelBoundaries )
+ split = (split + 3) & (~3);
+ child[0]->rect = MinMaxRect( rect.x, rect.y, rect.x+width+padding, rect.GetBottom() );
+ child[1]->rect = MinMaxRect( rect.x+split, rect.y, rect.GetRight(), rect.GetBottom() );
+ }
+ else
+ {
+ // vertical children
+ int split = int(height + padding);
+ if( use4PixelBoundaries )
+ split = (split + 3) & (~3);
+ child[0]->rect = MinMaxRect ( rect.x, rect.y, rect.GetRight(), rect.y+height+padding );
+ child[1]->rect = MinMaxRect ( rect.x, rect.y+split, rect.GetRight(), rect.GetBottom() );
+ }
+
+ // insert into first child
+ return child[0]->Insert( width, height, padding, use4PixelBoundaries );
+}
+
+typedef std::pair<int,int> IntPair;
+typedef std::vector<IntPair> TextureSizes;
+
+struct IndexSorter {
+ bool operator()( int a, int b ) const
+ {
+ return sizes[a].first * sizes[a].second > sizes[b].first * sizes[b].second;
+ }
+ IndexSorter( const TextureSizes& s ) : sizes(s) { }
+ const TextureSizes& sizes;
+};
+
+void PackAtlases (dynamic_array<Vector2f>& sizes, const int maxAtlasSize, const float padding, dynamic_array<Vector2f>& outOffsets, dynamic_array<int>& outIndices, int& atlasCount)
+{
+ const int count = sizes.size ();
+ const bool use4PixelBoundaries = false;
+
+ dynamic_array<Node> atlases;
+ outOffsets.resize_uninitialized (count);
+ outIndices.resize_uninitialized (count);
+
+ for (int i = 0; i < count; ++i)
+ {
+ Node* node = NULL;
+ int atlasIndex = -1;
+ while (!node)
+ {
+ atlasIndex++;
+ Vector2f& size = sizes[i];
+ if (atlasIndex == atlases.size ())
+ {
+ Node atlas;
+ atlas.rect.Set (0, 0, maxAtlasSize, maxAtlasSize);
+ atlases.push_back (atlas);
+ node = atlases[atlasIndex].Insert (size.x, size.y, padding, use4PixelBoundaries);
+ if (!node)
+ {
+ // We just tried inserting into an empty atlas. If that didn't succeed, we need to make the current rect smaller to fit maxAtlasSize
+ if (size.x > size.y)
+ {
+ size.y *= ((float)maxAtlasSize) / size.x;
+ size.x = maxAtlasSize;
+ }
+ else
+ {
+ size.x *= ((float)maxAtlasSize) / size.y;
+ size.y = maxAtlasSize;
+ }
+ node = atlases[atlasIndex].Insert (size.x, size.y, 0.0f, use4PixelBoundaries);
+ DebugAssert (node);
+ }
+ }
+ else
+ {
+ node = atlases[atlasIndex].Insert (size.x, size.y, padding, use4PixelBoundaries);
+ }
+ }
+ outOffsets[i].Set (node->rect.x, node->rect.y);
+ outIndices[i] = atlasIndex;
+ }
+
+ atlasCount = atlases.size ();
+
+ // deallocate all the trees
+ for (int i = 0; i < atlases.size (); ++i)
+ atlases[i].Reset ();
+}
+
+bool PackTextureAtlasSimple( Texture2D* atlas, int atlasMaximumSize, int textureCount, Texture2D** textures, Rectf* outRects, int padding, bool upload, bool markNoLongerReadable )
+{
+ atlasMaximumSize = min(gGraphicsCaps.maxTextureSize, atlasMaximumSize);
+
+ // Cleanup the texture set.
+ // * Remove duplicate textures
+ // * Remove null textures
+ vector<int> remap;
+ remap.resize(textureCount);
+ vector<Texture2D*> uniqueTextures;
+ for (int i=0;i<textureCount;i++)
+ {
+ // Completely ignore null textures
+ if (textures[i] == NULL)
+ {
+ *outRects = Rectf (0,0,0,0);
+ remap[i] = -1;
+ continue;
+ }
+
+ // Find duplicate texture and update remap
+ vector<Texture2D*> ::iterator found = find(uniqueTextures.begin(), uniqueTextures.end(), textures[i]);
+ if (found != uniqueTextures.end())
+ {
+ remap[i] = distance(uniqueTextures.begin(), found);
+ }
+ else
+ {
+ remap[i] = uniqueTextures.size();
+ uniqueTextures.push_back(textures[i]);
+ }
+ }
+
+ if (!uniqueTextures.empty())
+ {
+ vector<Rectf> uniqueRects;
+ uniqueRects.resize(uniqueTextures.size());
+
+ // Do the heavy lifting
+ if (!PackTextureAtlas(atlas, atlasMaximumSize, uniqueTextures.size(), &uniqueTextures[0], &uniqueRects[0], padding, upload ? 0 : Texture2D::kThreadedInitialize ))
+ return false;
+
+ // Copy out rects from unique
+ for (int i=0;i<textureCount;i++)
+ {
+ if (remap[i] != -1)
+ outRects[i] = uniqueRects[remap[i]];
+ }
+ }
+
+ if (upload)
+ {
+ if (!IsAnyCompressedTextureFormat(atlas->GetTextureFormat()))
+ atlas->RebuildMipMap ();
+
+ if( markNoLongerReadable )
+ {
+ atlas->SetIsReadable(false);
+ atlas->SetIsUnreloadable(false);
+ }
+
+ atlas->AwakeFromLoad(kDefaultAwakeFromLoad);
+ }
+ return true;
+}
+
+enum PackingFormat {
+ kPackingUncompressed,
+ kPackingDXT1,
+ kPackingDXT5,
+};
+
+
+bool PackTextureAtlas( Texture2D* atlas, int atlasMaximumSize, int textureCount, Texture2D** textures, Rectf* outRects, int padding, int textureOptions )
+{
+ DebugAssertIf( !atlas || !textures || !outRects || textureCount <= 0 );
+ const int kMinTextureSize = 4;
+ const int kMinAtlasSize = 8;
+ int i;
+ atlasMaximumSize = max (atlasMaximumSize, kMinAtlasSize);
+
+ PackingFormat packFormat = kPackingDXT1;
+ bool packWithMipmaps = false;
+ bool someInputHasNoMipmaps = false;
+
+ // Immediately decrease input texture sizes that are too large to fit; figure out
+ // result packing format and whether we'll have mipmaps.
+ TextureSizes textureSizes;
+ textureSizes.resize( textureCount );
+ for( i = 0; i < textureCount; ++i )
+ {
+ IntPair& size = textureSizes[i];
+ size.first = textures[i]->GetDataWidth();
+ size.second = textures[i]->GetDataHeight();
+ while( size.first > atlasMaximumSize && size.first > kMinTextureSize )
+ {
+ size.first /= 2;
+ packFormat = kPackingUncompressed; // we'll have to scale down, switch to uncompressed
+ }
+ while( size.second > atlasMaximumSize && size.second > kMinTextureSize )
+ {
+ size.second /= 2;
+ packFormat = kPackingUncompressed; // we'll have to scale down, switch to uncompressed
+ }
+
+ // Atlas format rules:
+ // Defaults to DXT1
+ // If there is a DXT5 texture, pack to DXT5 (expand DXT1 alpha to opaque)
+ // If there is an uncompressed or DXT3 texture, pack to 32 bit uncompressed.
+ TextureFormat texFormat = textures[i]->GetTextureFormat();
+ if (texFormat == kTexFormatDXT1 || texFormat == kTexFormatDXT5)
+ {
+ // Incoming texture is DXT1 or DXT5
+ // If currently we are packing to DXT1 and incoming is DXT5, switch to that.
+ if( packFormat == kPackingDXT1 && texFormat == kTexFormatDXT5 )
+ packFormat = kPackingDXT5;
+ }
+ else
+ {
+ // Incoming texture is anything else: pack to uncompressed
+ packFormat = kPackingUncompressed;
+ }
+
+ // If any texture has mipmaps, then atlas will have them
+ if( textures[i]->HasMipMap() )
+ packWithMipmaps = true;
+ else
+ someInputHasNoMipmaps = true;
+ }
+
+ // If some input textures have mipmaps and some don't, then
+ // pack to uncompressed atlas.
+ if( packWithMipmaps && someInputHasNoMipmaps )
+ packFormat = kPackingUncompressed;
+
+ // Sort incoming textures by size; largest area first
+ std::vector<int> sortedIndices;
+ sortedIndices.resize( textureCount );
+ for( i = 0; i < textureCount; ++i )
+ {
+ sortedIndices[i] = i;
+ }
+ std::sort( sortedIndices.begin(), sortedIndices.end(), IndexSorter(textureSizes) );
+
+ // Calculate an initial lower bound estimate for the atlas width & height
+ int totalPixels = 0;
+ for( i = 0; i < textureCount; ++i )
+ {
+ IntPair& size = textureSizes[i];
+ totalPixels += size.first * size.second;
+ }
+ int atlasWidth = min<int>(NextPowerOfTwo(UInt32(Sqrt (totalPixels))), atlasMaximumSize);
+ int atlasHeight = min<int>(NextPowerOfTwo(totalPixels / atlasWidth), atlasMaximumSize);
+ // Do the packing of rectangles
+ bool packOk = true;
+ const int kMaxPackIterations = 100;
+ int packIterations = 0;
+ std::vector<Node*> textureNodes;
+ textureNodes.resize( textureCount );
+
+ // Create a tree to track occupied areas in the atlas
+ Node tree;
+
+ do {
+ packOk = true;
+ tree.Reset();
+ tree.rect = MinMaxRect<float> ( 0, 0, atlasWidth, atlasHeight );
+
+ bool use4PixelBoundaries = (packFormat != kPackingUncompressed);
+
+ for( i = 0; i < textureCount; ++i )
+ {
+ int texIndex = sortedIndices[i];
+ DebugAssertIf( texIndex < 0 || texIndex >= textureCount );
+ int texWidth = textureSizes[texIndex].first;
+ int texHeight = textureSizes[texIndex].second;
+ Node* node = tree.Insert( texWidth, texHeight, padding, use4PixelBoundaries );
+ textureNodes[texIndex] = node;
+ if( !node )
+ {
+ // texture does not fit; break out, reduce sizes and repack again
+ packOk = false;
+ break;
+ }
+ }
+
+ // packing failed - decrease image sizes and try again
+ if( !packOk )
+ {
+ // First we just increase the atlas size and see if we can fit all textures in.
+ if (atlasWidth != atlasMaximumSize || atlasHeight != atlasMaximumSize)
+ {
+ // Never increase beyond max size
+ if (atlasWidth == atlasMaximumSize)
+ atlasHeight *= 2;
+ else if (atlasHeight == atlasMaximumSize)
+ atlasWidth *= 2;
+ // increase the smaller of width/height
+ else if (atlasWidth < atlasHeight)
+ atlasWidth *= 2;
+ else
+ atlasHeight *= 2;
+ }
+ else
+ {
+ // TODO: the decreasing logic can be arbitrarily more complex. E.g. decrease the largest
+ // images first only, etc.
+ for( i = 0; i < textureCount; ++i )
+ {
+ IntPair& size = textureSizes[i];
+ if( size.first > kMinTextureSize && size.second > kMinTextureSize ) {
+ size.first = size.first * 3 / 4;
+ size.second = size.second * 3 / 4;
+ }
+ }
+
+ // we'll scale images down, no DXT for ya
+ packFormat = kPackingUncompressed;
+
+ // Only update pack iterations, for decreasing texture size
+ ++packIterations;
+ }
+
+ AssertIf (atlasWidth > atlasMaximumSize);
+ AssertIf (atlasHeight > atlasMaximumSize);
+ }
+ } while( !packOk && packIterations < kMaxPackIterations );
+
+ if( !packOk )
+ return false;
+
+
+ // Fill out UV rectangles for the input textures
+ for( i = 0; i < textureCount; ++i )
+ {
+ int texIndex = sortedIndices[i];
+ DebugAssertIf( texIndex < 0 || texIndex >= textureCount );
+ int texWidth = textureSizes[texIndex].first;
+ int texHeight = textureSizes[texIndex].second;
+ const Node* node = textureNodes[texIndex];
+ AssertIf( !node );
+
+ // Set the rectangle
+ outRects[texIndex] = MinMaxRect (
+ node->rect.x/atlasWidth,
+ node->rect.y/atlasHeight,
+ (node->rect.x+texWidth)/atlasWidth,
+ (node->rect.y+texHeight)/atlasHeight );
+ }
+
+
+ // Initialize atlas texture
+ TextureFormat atlasFormat;
+ if( packFormat == kPackingDXT1 )
+ atlasFormat = kTexFormatDXT1;
+ else if( packFormat == kPackingDXT5 )
+ atlasFormat = kTexFormatDXT5;
+ else
+ atlasFormat = kTexFormatARGB32;
+
+ textureOptions |= packWithMipmaps ? (Texture2D::kMipmapMask) : (Texture2D::kNoMipmap);
+ atlas->InitTexture( atlasWidth, atlasHeight, atlasFormat, textureOptions );
+
+ // Packing into uncompressed texture atlas
+ if( packFormat == kPackingUncompressed )
+ {
+ UInt8* atlasData = atlas->GetRawImageData();
+ memset( atlasData, 0, atlas->GetRawImageData(1) - atlasData );
+
+ // Blit textures into final destinations
+ Image* decompressedImage = 0;
+ const int numAtlasMips = atlas->CountDataMipmaps();
+
+ for( i = 0; i < textureCount; ++i )
+ {
+ int texIndex = sortedIndices[i];
+ DebugAssertIf( texIndex < 0 || texIndex >= textureCount );
+
+ int texWidth = textureSizes[texIndex].first;
+ int texHeight = textureSizes[texIndex].second;
+ const Node* node = textureNodes[texIndex];
+ AssertIf( !node );
+
+ int destCoordX = node->rect.x;
+ int destCoordY = node->rect.y;
+ int destWidth = std::min(texWidth, std::max(1, (int)node->rect.width - padding));
+ int destHeight = std::min(texHeight, std::max(1, (int)node->rect.height - padding));
+
+ int atlasMipWidth = atlasWidth;
+ int atlasMipHeight = atlasHeight;
+ // copy all mips to atlas
+ for ( int mip=0, numMips=std::min( textures[texIndex]->CountDataMipmaps(), numAtlasMips ); mip!=numMips; ++mip )
+ {
+ ImageReference atlasImgRef;
+ atlas->GetWriteImageReference ( &atlasImgRef, 0, mip );
+ // get texture rect in atlas for current source texture
+ ImageReference destRect = atlasImgRef.ClipImage( destCoordX, destCoordY, destWidth, destHeight );
+
+ ImageReference srcMip;
+ if ( textures[texIndex]->GetWriteImageReference( &srcMip, 0, mip ) )
+ {
+ ImageReference::BlitMode blit_mode = ImageReference::BLIT_BILINEAR_SCALE;
+ if ( destWidth==srcMip.GetWidth() && destHeight==srcMip.GetHeight() )
+ blit_mode = ImageReference::BLIT_COPY;
+ destRect.BlitImage( srcMip, blit_mode );
+ }
+ else
+ {
+ if( !decompressedImage )
+ decompressedImage = new Image( texWidth, texHeight, kTexFormatRGBA32 );
+ else
+ decompressedImage->SetImage( texWidth, texHeight, kTexFormatRGBA32, false );
+
+ textures[texIndex]->ExtractImage( decompressedImage, 0 );
+ ImageReference::BlitMode blit_mode = ImageReference::BLIT_BILINEAR_SCALE;
+ if ( destWidth==decompressedImage->GetWidth() && destHeight==decompressedImage->GetHeight() )
+ blit_mode = ImageReference::BLIT_COPY;
+ destRect.BlitImage( *decompressedImage, blit_mode );
+ }
+
+ // Go to next mip level
+ destCoordX /= 2;
+ destCoordY /= 2;
+ destWidth /= 2;
+ destHeight /= 2;
+ atlasMipWidth = std::max( atlasMipWidth/2, 1 );
+ atlasMipHeight = std::max( atlasMipHeight/2, 1 );
+ texWidth = std::max( texWidth/2, 1 );
+ texHeight = std::max( texHeight/2, 1 );
+ }
+ }
+ delete decompressedImage;
+ }
+ // Packing into compressed texture atlas
+ else
+ {
+ UInt8* atlasData = atlas->GetRawImageData();
+ bool atlasDXT1 = (atlasFormat==kTexFormatDXT1);
+ int blockBytes = atlasDXT1 ? 8 : 16;
+ memset( atlasData, 0, atlas->GetRawImageData(1) - atlasData );
+
+ // Blit textures into final destinations
+ for( i = 0; i < textureCount; ++i )
+ {
+ int texIndex = sortedIndices[i];
+ DebugAssertIf( texIndex < 0 || texIndex >= textureCount );
+ int texWidth = textureSizes[texIndex].first;
+ int texHeight = textureSizes[texIndex].second;
+ Texture2D* src = textures[texIndex];
+ int mipCount = std::min( src->CountDataMipmaps(), atlas->CountDataMipmaps() );
+ AssertIf( texWidth != src->GetDataWidth() || texHeight != src->GetDataHeight() );
+ const Node* node = textureNodes[texIndex];
+ AssertIf( !node );
+ int destCoordX = int(node->rect.x), destCoordY = int(node->rect.y);
+ int destWidth = int(node->rect.width), destHeight = int(node->rect.height);
+ AssertIf( (destCoordX & 3) != 0 || (destCoordY & 3) != 0 );
+
+ UInt8* atlasMipData = atlasData;
+ int atlasMipWidth = atlasWidth;
+ int atlasMipHeight = atlasHeight;
+ const UInt8* srcPointer = src->GetRawImageData();
+
+ if(srcPointer)
+ {
+
+ // blit mip levels while we have them
+ for( int mip = 0; mip < mipCount; ++mip )
+ {
+ // Get pointer where should we blit texture into
+ int destBlockX = destCoordX / 4;
+ int destBlockY = destCoordY / 4;
+ UInt8* destPointer = atlasMipData + (destBlockY * atlasMipWidth/4 + destBlockX) * blockBytes;
+
+ TextureFormat srcFormat = src->GetTextureFormat();
+ AssertIf( !IsCompressedDXTTextureFormat(srcFormat) );
+ if( srcFormat == atlasFormat )
+ {
+ BlitCopyCompressedImage( srcFormat, srcPointer,
+ texWidth, texHeight,
+ destPointer, atlasMipWidth, atlasMipHeight, false );
+ }
+ else if( srcFormat == kTexFormatDXT1 && atlasFormat == kTexFormatDXT5 )
+ {
+ BlitCopyCompressedDXT1ToDXT5( srcPointer,
+ texWidth, texHeight,
+ destPointer, atlasMipWidth, atlasMipHeight );
+ }
+ else
+ {
+ AssertString( "Unsupported format in compressed texture atlas" );
+ }
+
+ // Go to next mip level
+ srcPointer += CalculateImageSize( texWidth, texHeight, srcFormat );
+ atlasMipData += CalculateImageSize( atlasMipWidth, atlasMipHeight, atlasFormat );
+ destCoordX /= 2;
+ destCoordY /= 2;
+ destWidth /= 2;
+ destHeight /= 2;
+ atlasMipWidth = std::max( atlasMipWidth / 2, 4 );
+ atlasMipHeight = std::max( atlasMipHeight / 2, 4 );
+ texWidth = std::max( texWidth / 2, 4 );
+ texHeight = std::max( texHeight / 2, 4 );
+
+ // Stop if we begin to straddle DXT block boundaries.
+ if( (destCoordX & 3) != 0 || (destCoordY & 3) != 0 )
+ break;
+
+ // Stop if we don't fit into our initial area anymore.
+ if( destWidth < 4 || destHeight < 4 )
+ break;
+ }
+ }
+ else
+ ErrorStringMsg ("Could not read texture data for texture '%s'. Make sure that Read/Write access is enabled in the texture importer advanced settings\n", src->GetName());
+ }
+ }
+
+ return true;
+}
diff --git a/Runtime/Geometry/TextureAtlas.h b/Runtime/Geometry/TextureAtlas.h
new file mode 100644
index 0000000..132f8ef
--- /dev/null
+++ b/Runtime/Geometry/TextureAtlas.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Modules/ExportModules.h"
+
+class Texture2D;
+
+bool EXPORT_COREMODULE PackTextureAtlasSimple( Texture2D* atlas, int atlasMaximumSize, int textureCount, Texture2D** textures, Rectf* outRects, int padding, bool upload, bool markNoLongerReadable );
+void PackAtlases (dynamic_array<Vector2f>& sizes, const int maxAtlasSize, const float padding, dynamic_array<Vector2f>& outOffsets, dynamic_array<int>& outIndices, int& atlasCount);
+
diff --git a/Runtime/Geometry/TriTriIntersect.cpp b/Runtime/Geometry/TriTriIntersect.cpp
new file mode 100644
index 0000000..03dfdc4
--- /dev/null
+++ b/Runtime/Geometry/TriTriIntersect.cpp
@@ -0,0 +1,719 @@
+#include "UnityPrefix.h"
+/* Triangle/triangle intersection test routine,
+ * by Tomas Moller, 1997.
+ * See article "A Fast Triangle-Triangle Intersection Test",
+ * Journal of Graphics Tools, 2(2), 1997
+ * updated: 2001-06-20 (added line of intersection)
+ *
+ * int tri_tri_intersect(float V0[3],float V1[3],float V2[3],
+ * float U0[3],float U1[3],float U2[3])
+ *
+ * parameters: vertices of triangle 1: V0,V1,V2
+ * vertices of triangle 2: U0,U1,U2
+ * result : returns 1 if the triangles intersect, otherwise 0
+ *
+ * Here is a version withouts divisions (a little faster)
+ * int NoDivTriTriIsect(float V0[3],float V1[3],float V2[3],
+ * float U0[3],float U1[3],float U2[3]);
+ *
+ * This version computes the line of intersection as well (if they are not coplanar):
+ * int tri_tri_intersect_with_isectline(float V0[3],float V1[3],float V2[3],
+ * float U0[3],float U1[3],float U2[3],int *coplanar,
+ * float isectpt1[3],float isectpt2[3]);
+ * coplanar returns whether the tris are coplanar
+ * isectpt1, isectpt2 are the endpoints of the line of intersection
+ */
+
+#include <math.h>
+static int coplanar_tri_tri(float N[3],float V0[3],float V1[3],float V2[3],
+ float U0[3],float U1[3],float U2[3]);
+static int tri_tri_intersect(float V0[3],float V1[3],float V2[3],
+ float U0[3],float U1[3],float U2[3]);
+static int NoDivTriTriIsect(float V0[3],float V1[3],float V2[3],
+ float U0[3],float U1[3],float U2[3]);
+int tri_tri_intersect_with_isectline(float V0[3],float V1[3],float V2[3],
+ float U0[3],float U1[3],float U2[3],int *coplanar,
+ float isectpt1[3],float isectpt2[3]);
+
+#define FABS(x) ((float)fabs(x)) /* implement as is fastest on your machine */
+
+/* if USE_EPSILON_TEST is true then we do a check:
+ if |dv|<EPSILON then dv=0.0;
+ else no check is done (which is less robust)
+*/
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#define USE_EPSILON_TEST TRUE
+#define EPSILON 0.000001
+
+
+/* some macros */
+#define CROSS(dest,v1,v2) \
+ dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \
+ dest[1]=v1[2]*v2[0]-v1[0]*v2[2]; \
+ dest[2]=v1[0]*v2[1]-v1[1]*v2[0];
+
+#define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2])
+
+#define SUB(dest,v1,v2) dest[0]=v1[0]-v2[0]; dest[1]=v1[1]-v2[1]; dest[2]=v1[2]-v2[2];
+
+#define ADD(dest,v1,v2) dest[0]=v1[0]+v2[0]; dest[1]=v1[1]+v2[1]; dest[2]=v1[2]+v2[2];
+
+#define MULT(dest,v,factor) dest[0]=factor*v[0]; dest[1]=factor*v[1]; dest[2]=factor*v[2];
+
+#define SET(dest,src) dest[0]=src[0]; dest[1]=src[1]; dest[2]=src[2];
+
+/* sort so that a<=b */
+#define SORT(a,b) \
+ if(a>b) \
+ { \
+ float c; \
+ c=a; \
+ a=b; \
+ b=c; \
+ }
+
+#define ISECT(VV0,VV1,VV2,D0,D1,D2,isect0,isect1) \
+ isect0=VV0+(VV1-VV0)*D0/(D0-D1); \
+ isect1=VV0+(VV2-VV0)*D0/(D0-D2);
+
+
+#define COMPUTE_INTERVALS(VV0,VV1,VV2,D0,D1,D2,D0D1,D0D2,isect0,isect1) \
+ if(D0D1>0.0f) \
+ { \
+ /* here we know that D0D2<=0.0 */ \
+ /* that is D0, D1 are on the same side, D2 on the other or on the plane */ \
+ ISECT(VV2,VV0,VV1,D2,D0,D1,isect0,isect1); \
+ } \
+ else if(D0D2>0.0f) \
+ { \
+ /* here we know that d0d1<=0.0 */ \
+ ISECT(VV1,VV0,VV2,D1,D0,D2,isect0,isect1); \
+ } \
+ else if(D1*D2>0.0f || D0!=0.0f) \
+ { \
+ /* here we know that d0d1<=0.0 or that D0!=0.0 */ \
+ ISECT(VV0,VV1,VV2,D0,D1,D2,isect0,isect1); \
+ } \
+ else if(D1!=0.0f) \
+ { \
+ ISECT(VV1,VV0,VV2,D1,D0,D2,isect0,isect1); \
+ } \
+ else if(D2!=0.0f) \
+ { \
+ ISECT(VV2,VV0,VV1,D2,D0,D1,isect0,isect1); \
+ } \
+ else \
+ { \
+ /* triangles are coplanar */ \
+ return coplanar_tri_tri(N1,V0,V1,V2,U0,U1,U2); \
+ }
+
+
+
+/* this edge to edge test is based on Franlin Antonio's gem:
+ "Faster Line Segment Intersection", in Graphics Gems III,
+ pp. 199-202 */
+#define EDGE_EDGE_TEST(V0,U0,U1) \
+ Bx=U0[i0]-U1[i0]; \
+ By=U0[i1]-U1[i1]; \
+ Cx=V0[i0]-U0[i0]; \
+ Cy=V0[i1]-U0[i1]; \
+ f=Ay*Bx-Ax*By; \
+ d=By*Cx-Bx*Cy; \
+ if((f>0 && d>=0 && d<=f) || (f<0 && d<=0 && d>=f)) \
+ { \
+ e=Ax*Cy-Ay*Cx; \
+ if(f>0) \
+ { \
+ if(e>=0 && e<=f) return 1; \
+ } \
+ else \
+ { \
+ if(e<=0 && e>=f) return 1; \
+ } \
+ }
+
+#define EDGE_AGAINST_TRI_EDGES(V0,V1,U0,U1,U2) \
+{ \
+ float Ax,Ay,Bx,By,Cx,Cy,e,d,f; \
+ Ax=V1[i0]-V0[i0]; \
+ Ay=V1[i1]-V0[i1]; \
+ /* test edge U0,U1 against V0,V1 */ \
+ EDGE_EDGE_TEST(V0,U0,U1); \
+ /* test edge U1,U2 against V0,V1 */ \
+ EDGE_EDGE_TEST(V0,U1,U2); \
+ /* test edge U2,U1 against V0,V1 */ \
+ EDGE_EDGE_TEST(V0,U2,U0); \
+}
+
+#define POINT_IN_TRI(V0,U0,U1,U2) \
+{ \
+ float a,b,c,d0,d1,d2; \
+ /* is T1 completly inside T2? */ \
+ /* check if V0 is inside tri(U0,U1,U2) */ \
+ a=U1[i1]-U0[i1]; \
+ b=-(U1[i0]-U0[i0]); \
+ c=-a*U0[i0]-b*U0[i1]; \
+ d0=a*V0[i0]+b*V0[i1]+c; \
+ \
+ a=U2[i1]-U1[i1]; \
+ b=-(U2[i0]-U1[i0]); \
+ c=-a*U1[i0]-b*U1[i1]; \
+ d1=a*V0[i0]+b*V0[i1]+c; \
+ \
+ a=U0[i1]-U2[i1]; \
+ b=-(U0[i0]-U2[i0]); \
+ c=-a*U2[i0]-b*U2[i1]; \
+ d2=a*V0[i0]+b*V0[i1]+c; \
+ if(d0*d1>0.0) \
+ { \
+ if(d0*d2>0.0) return 1; \
+ } \
+}
+
+static int coplanar_tri_tri(float N[3],float V0[3],float V1[3],float V2[3],
+ float U0[3],float U1[3],float U2[3])
+{
+ float A[3];
+ short i0,i1;
+ /* first project onto an axis-aligned plane, that maximizes the area */
+ /* of the triangles, compute indices: i0,i1. */
+ A[0]=fabs(N[0]);
+ A[1]=fabs(N[1]);
+ A[2]=fabs(N[2]);
+ if(A[0]>A[1])
+ {
+ if(A[0]>A[2])
+ {
+ i0=1; /* A[0] is greatest */
+ i1=2;
+ }
+ else
+ {
+ i0=0; /* A[2] is greatest */
+ i1=1;
+ }
+ }
+ else /* A[0]<=A[1] */
+ {
+ if(A[2]>A[1])
+ {
+ i0=0; /* A[2] is greatest */
+ i1=1;
+ }
+ else
+ {
+ i0=0; /* A[1] is greatest */
+ i1=2;
+ }
+ }
+
+ /* test all edges of triangle 1 against the edges of triangle 2 */
+ EDGE_AGAINST_TRI_EDGES(V0,V1,U0,U1,U2);
+ EDGE_AGAINST_TRI_EDGES(V1,V2,U0,U1,U2);
+ EDGE_AGAINST_TRI_EDGES(V2,V0,U0,U1,U2);
+
+ /* finally, test if tri1 is totally contained in tri2 or vice versa */
+ POINT_IN_TRI(V0,U0,U1,U2);
+ POINT_IN_TRI(U0,V0,V1,V2);
+
+ return 0;
+}
+
+
+static int tri_tri_intersect(float V0[3],float V1[3],float V2[3],
+ float U0[3],float U1[3],float U2[3])
+{
+ float E1[3],E2[3];
+ float N1[3],N2[3],d1,d2;
+ float du0,du1,du2,dv0,dv1,dv2;
+ float D[3];
+ float isect1[2], isect2[2];
+ float du0du1,du0du2,dv0dv1,dv0dv2;
+ short index;
+ float vp0,vp1,vp2;
+ float up0,up1,up2;
+ float b,c,max;
+
+ /* compute plane equation of triangle(V0,V1,V2) */
+ SUB(E1,V1,V0);
+ SUB(E2,V2,V0);
+ CROSS(N1,E1,E2);
+ d1=-DOT(N1,V0);
+ /* plane equation 1: N1.X+d1=0 */
+
+ /* put U0,U1,U2 into plane equation 1 to compute signed distances to the plane*/
+ du0=DOT(N1,U0)+d1;
+ du1=DOT(N1,U1)+d1;
+ du2=DOT(N1,U2)+d1;
+
+ /* coplanarity robustness check */
+#if USE_EPSILON_TEST==TRUE
+ if(fabs(du0)<EPSILON) du0=0.0;
+ if(fabs(du1)<EPSILON) du1=0.0;
+ if(fabs(du2)<EPSILON) du2=0.0;
+#endif
+ du0du1=du0*du1;
+ du0du2=du0*du2;
+
+ if(du0du1>0.0f && du0du2>0.0f) /* same sign on all of them + not equal 0 ? */
+ return 0; /* no intersection occurs */
+
+ /* compute plane of triangle (U0,U1,U2) */
+ SUB(E1,U1,U0);
+ SUB(E2,U2,U0);
+ CROSS(N2,E1,E2);
+ d2=-DOT(N2,U0);
+ /* plane equation 2: N2.X+d2=0 */
+
+ /* put V0,V1,V2 into plane equation 2 */
+ dv0=DOT(N2,V0)+d2;
+ dv1=DOT(N2,V1)+d2;
+ dv2=DOT(N2,V2)+d2;
+
+#if USE_EPSILON_TEST==TRUE
+ if(fabs(dv0)<EPSILON) dv0=0.0;
+ if(fabs(dv1)<EPSILON) dv1=0.0;
+ if(fabs(dv2)<EPSILON) dv2=0.0;
+#endif
+
+ dv0dv1=dv0*dv1;
+ dv0dv2=dv0*dv2;
+
+ if(dv0dv1>0.0f && dv0dv2>0.0f) /* same sign on all of them + not equal 0 ? */
+ return 0; /* no intersection occurs */
+
+ /* compute direction of intersection line */
+ CROSS(D,N1,N2);
+
+ /* compute and index to the largest component of D */
+ max=fabs(D[0]);
+ index=0;
+ b=fabs(D[1]);
+ c=fabs(D[2]);
+ if(b>max) max=b,index=1;
+ if(c>max) max=c,index=2;
+
+ /* this is the simplified projection onto L*/
+ vp0=V0[index];
+ vp1=V1[index];
+ vp2=V2[index];
+
+ up0=U0[index];
+ up1=U1[index];
+ up2=U2[index];
+
+ /* compute interval for triangle 1 */
+ COMPUTE_INTERVALS(vp0,vp1,vp2,dv0,dv1,dv2,dv0dv1,dv0dv2,isect1[0],isect1[1]);
+
+ /* compute interval for triangle 2 */
+ COMPUTE_INTERVALS(up0,up1,up2,du0,du1,du2,du0du1,du0du2,isect2[0],isect2[1]);
+
+ SORT(isect1[0],isect1[1]);
+ SORT(isect2[0],isect2[1]);
+
+ if(isect1[1]<isect2[0] || isect2[1]<isect1[0]) return 0;
+ return 1;
+}
+
+
+#define NEWCOMPUTE_INTERVALS(VV0,VV1,VV2,D0,D1,D2,D0D1,D0D2,A,B,C,X0,X1) \
+{ \
+ if(D0D1>0.0f) \
+ { \
+ /* here we know that D0D2<=0.0 */ \
+ /* that is D0, D1 are on the same side, D2 on the other or on the plane */ \
+ A=VV2; B=(VV0-VV2)*D2; C=(VV1-VV2)*D2; X0=D2-D0; X1=D2-D1; \
+ } \
+ else if(D0D2>0.0f)\
+ { \
+ /* here we know that d0d1<=0.0 */ \
+ A=VV1; B=(VV0-VV1)*D1; C=(VV2-VV1)*D1; X0=D1-D0; X1=D1-D2; \
+ } \
+ else if(D1*D2>0.0f || D0!=0.0f) \
+ { \
+ /* here we know that d0d1<=0.0 or that D0!=0.0 */ \
+ A=VV0; B=(VV1-VV0)*D0; C=(VV2-VV0)*D0; X0=D0-D1; X1=D0-D2; \
+ } \
+ else if(D1!=0.0f) \
+ { \
+ A=VV1; B=(VV0-VV1)*D1; C=(VV2-VV1)*D1; X0=D1-D0; X1=D1-D2; \
+ } \
+ else if(D2!=0.0f) \
+ { \
+ A=VV2; B=(VV0-VV2)*D2; C=(VV1-VV2)*D2; X0=D2-D0; X1=D2-D1; \
+ } \
+ else \
+ { \
+ /* triangles are coplanar */ \
+ return coplanar_tri_tri(N1,V0,V1,V2,U0,U1,U2); \
+ } \
+}
+
+
+
+static int NoDivTriTriIsect(float V0[3],float V1[3],float V2[3],
+ float U0[3],float U1[3],float U2[3])
+{
+ float E1[3],E2[3];
+ float N1[3],N2[3],d1,d2;
+ float du0,du1,du2,dv0,dv1,dv2;
+ float D[3];
+ float isect1[2], isect2[2];
+ float du0du1,du0du2,dv0dv1,dv0dv2;
+ short index;
+ float vp0,vp1,vp2;
+ float up0,up1,up2;
+ float bb,cc,max;
+ float a,b,c,x0,x1;
+ float d,e,f,y0,y1;
+ float xx,yy,xxyy,tmp;
+
+ /* compute plane equation of triangle(V0,V1,V2) */
+ SUB(E1,V1,V0);
+ SUB(E2,V2,V0);
+ CROSS(N1,E1,E2);
+ d1=-DOT(N1,V0);
+ /* plane equation 1: N1.X+d1=0 */
+
+ /* put U0,U1,U2 into plane equation 1 to compute signed distances to the plane*/
+ du0=DOT(N1,U0)+d1;
+ du1=DOT(N1,U1)+d1;
+ du2=DOT(N1,U2)+d1;
+
+ /* coplanarity robustness check */
+#if USE_EPSILON_TEST==TRUE
+ if(FABS(du0)<EPSILON) du0=0.0;
+ if(FABS(du1)<EPSILON) du1=0.0;
+ if(FABS(du2)<EPSILON) du2=0.0;
+#endif
+ du0du1=du0*du1;
+ du0du2=du0*du2;
+
+ if(du0du1>0.0f && du0du2>0.0f) /* same sign on all of them + not equal 0 ? */
+ return 0; /* no intersection occurs */
+
+ /* compute plane of triangle (U0,U1,U2) */
+ SUB(E1,U1,U0);
+ SUB(E2,U2,U0);
+ CROSS(N2,E1,E2);
+ d2=-DOT(N2,U0);
+ /* plane equation 2: N2.X+d2=0 */
+
+ /* put V0,V1,V2 into plane equation 2 */
+ dv0=DOT(N2,V0)+d2;
+ dv1=DOT(N2,V1)+d2;
+ dv2=DOT(N2,V2)+d2;
+
+#if USE_EPSILON_TEST==TRUE
+ if(FABS(dv0)<EPSILON) dv0=0.0;
+ if(FABS(dv1)<EPSILON) dv1=0.0;
+ if(FABS(dv2)<EPSILON) dv2=0.0;
+#endif
+
+ dv0dv1=dv0*dv1;
+ dv0dv2=dv0*dv2;
+
+ if(dv0dv1>0.0f && dv0dv2>0.0f) /* same sign on all of them + not equal 0 ? */
+ return 0; /* no intersection occurs */
+
+ /* compute direction of intersection line */
+ CROSS(D,N1,N2);
+
+ /* compute and index to the largest component of D */
+ max=(float)FABS(D[0]);
+ index=0;
+ bb=(float)FABS(D[1]);
+ cc=(float)FABS(D[2]);
+ if(bb>max) max=bb,index=1;
+ if(cc>max) max=cc,index=2;
+
+ /* this is the simplified projection onto L*/
+ vp0=V0[index];
+ vp1=V1[index];
+ vp2=V2[index];
+
+ up0=U0[index];
+ up1=U1[index];
+ up2=U2[index];
+
+ /* compute interval for triangle 1 */
+ NEWCOMPUTE_INTERVALS(vp0,vp1,vp2,dv0,dv1,dv2,dv0dv1,dv0dv2,a,b,c,x0,x1);
+
+ /* compute interval for triangle 2 */
+ NEWCOMPUTE_INTERVALS(up0,up1,up2,du0,du1,du2,du0du1,du0du2,d,e,f,y0,y1);
+
+ xx=x0*x1;
+ yy=y0*y1;
+ xxyy=xx*yy;
+
+ tmp=a*xxyy;
+ isect1[0]=tmp+b*x1*yy;
+ isect1[1]=tmp+c*x0*yy;
+
+ tmp=d*xxyy;
+ isect2[0]=tmp+e*xx*y1;
+ isect2[1]=tmp+f*xx*y0;
+
+ SORT(isect1[0],isect1[1]);
+ SORT(isect2[0],isect2[1]);
+
+ if(isect1[1]<isect2[0] || isect2[1]<isect1[0]) return 0;
+ return 1;
+}
+
+/* sort so that a<=b */
+#define SORT2(a,b,smallest) \
+ if(a>b) \
+ { \
+ float c; \
+ c=a; \
+ a=b; \
+ b=c; \
+ smallest=1; \
+ } \
+ else smallest=0;
+
+
+inline void isect2(float VTX0[3],float VTX1[3],float VTX2[3],float VV0,float VV1,float VV2,
+ float D0,float D1,float D2,float *isect0,float *isect1,float isectpoint0[3],float isectpoint1[3])
+{
+ float tmp=D0/(D0-D1);
+ float diff[3];
+ *isect0=VV0+(VV1-VV0)*tmp;
+ SUB(diff,VTX1,VTX0);
+ MULT(diff,diff,tmp);
+ ADD(isectpoint0,diff,VTX0);
+ tmp=D0/(D0-D2);
+ *isect1=VV0+(VV2-VV0)*tmp;
+ SUB(diff,VTX2,VTX0);
+ MULT(diff,diff,tmp);
+ ADD(isectpoint1,VTX0,diff);
+}
+
+
+#if 0
+#define ISECT2(VTX0,VTX1,VTX2,VV0,VV1,VV2,D0,D1,D2,isect0,isect1,isectpoint0,isectpoint1) \
+ tmp=D0/(D0-D1); \
+ isect0=VV0+(VV1-VV0)*tmp; \
+ SUB(diff,VTX1,VTX0); \
+ MULT(diff,diff,tmp); \
+ ADD(isectpoint0,diff,VTX0); \
+ tmp=D0/(D0-D2);
+/* isect1=VV0+(VV2-VV0)*tmp; \ */
+/* SUB(diff,VTX2,VTX0); \ */
+/* MULT(diff,diff,tmp); \ */
+/* ADD(isectpoint1,VTX0,diff); */
+#endif
+
+inline int compute_intervals_isectline(float VERT0[3],float VERT1[3],float VERT2[3],
+ float VV0,float VV1,float VV2,float D0,float D1,float D2,
+ float D0D1,float D0D2,float *isect0,float *isect1,
+ float isectpoint0[3],float isectpoint1[3])
+{
+ if(D0D1>0.0f)
+ {
+ /* here we know that D0D2<=0.0 */
+ /* that is D0, D1 are on the same side, D2 on the other or on the plane */
+ isect2(VERT2,VERT0,VERT1,VV2,VV0,VV1,D2,D0,D1,isect0,isect1,isectpoint0,isectpoint1);
+ }
+ else if(D0D2>0.0f)
+ {
+ /* here we know that d0d1<=0.0 */
+ isect2(VERT1,VERT0,VERT2,VV1,VV0,VV2,D1,D0,D2,isect0,isect1,isectpoint0,isectpoint1);
+ }
+ else if(D1*D2>0.0f || D0!=0.0f)
+ {
+ /* here we know that d0d1<=0.0 or that D0!=0.0 */
+ isect2(VERT0,VERT1,VERT2,VV0,VV1,VV2,D0,D1,D2,isect0,isect1,isectpoint0,isectpoint1);
+ }
+ else if(D1!=0.0f)
+ {
+ isect2(VERT1,VERT0,VERT2,VV1,VV0,VV2,D1,D0,D2,isect0,isect1,isectpoint0,isectpoint1);
+ }
+ else if(D2!=0.0f)
+ {
+ isect2(VERT2,VERT0,VERT1,VV2,VV0,VV1,D2,D0,D1,isect0,isect1,isectpoint0,isectpoint1);
+ }
+ else
+ {
+ /* triangles are coplanar */
+ return 1;
+ }
+ return 0;
+}
+
+#define COMPUTE_INTERVALS_ISECTLINE(VERT0,VERT1,VERT2,VV0,VV1,VV2,D0,D1,D2,D0D1,D0D2,isect0,isect1,isectpoint0,isectpoint1) \
+ if(D0D1>0.0f) \
+ { \
+ /* here we know that D0D2<=0.0 */ \
+ /* that is D0, D1 are on the same side, D2 on the other or on the plane */ \
+ isect2(VERT2,VERT0,VERT1,VV2,VV0,VV1,D2,D0,D1,&isect0,&isect1,isectpoint0,isectpoint1); \
+ }
+#if 0
+ else if(D0D2>0.0f) \
+ { \
+ /* here we know that d0d1<=0.0 */ \
+ isect2(VERT1,VERT0,VERT2,VV1,VV0,VV2,D1,D0,D2,&isect0,&isect1,isectpoint0,isectpoint1); \
+ } \
+ else if(D1*D2>0.0f || D0!=0.0f) \
+ { \
+ /* here we know that d0d1<=0.0 or that D0!=0.0 */ \
+ isect2(VERT0,VERT1,VERT2,VV0,VV1,VV2,D0,D1,D2,&isect0,&isect1,isectpoint0,isectpoint1); \
+ } \
+ else if(D1!=0.0f) \
+ { \
+ isect2(VERT1,VERT0,VERT2,VV1,VV0,VV2,D1,D0,D2,&isect0,&isect1,isectpoint0,isectpoint1); \
+ } \
+ else if(D2!=0.0f) \
+ { \
+ isect2(VERT2,VERT0,VERT1,VV2,VV0,VV1,D2,D0,D1,&isect0,&isect1,isectpoint0,isectpoint1); \
+ } \
+ else \
+ { \
+ /* triangles are coplanar */ \
+ coplanar=1; \
+ return coplanar_tri_tri(N1,V0,V1,V2,U0,U1,U2); \
+ }
+#endif
+
+int tri_tri_intersect_with_isectline(float V0[3],float V1[3],float V2[3],
+ float U0[3],float U1[3],float U2[3],int *coplanar,
+ float isectpt1[3],float isectpt2[3])
+{
+ float E1[3],E2[3];
+ float N1[3],N2[3],d1,d2;
+ float du0,du1,du2,dv0,dv1,dv2;
+ float D[3];
+ float isect1[2], isect2[2] = {.0f, .0f};
+ float isectpointA1[3],isectpointA2[3];
+ float isectpointB1[3] = {.0f, .0f, .0f},isectpointB2[3] = {.0f, .0f, .0f};
+ float du0du1,du0du2,dv0dv1,dv0dv2;
+ short index;
+ float vp0,vp1,vp2;
+ float up0,up1,up2;
+ float b,c,max;
+ int smallest1,smallest2;
+
+ /* compute plane equation of triangle(V0,V1,V2) */
+ SUB(E1,V1,V0);
+ SUB(E2,V2,V0);
+ CROSS(N1,E1,E2);
+ d1=-DOT(N1,V0);
+ /* plane equation 1: N1.X+d1=0 */
+
+ /* put U0,U1,U2 into plane equation 1 to compute signed distances to the plane*/
+ du0=DOT(N1,U0)+d1;
+ du1=DOT(N1,U1)+d1;
+ du2=DOT(N1,U2)+d1;
+
+ /* coplanarity robustness check */
+#if USE_EPSILON_TEST==TRUE
+ if(fabs(du0)<EPSILON) du0=0.0;
+ if(fabs(du1)<EPSILON) du1=0.0;
+ if(fabs(du2)<EPSILON) du2=0.0;
+#endif
+ du0du1=du0*du1;
+ du0du2=du0*du2;
+
+ if(du0du1>0.0f && du0du2>0.0f) /* same sign on all of them + not equal 0 ? */
+ return 0; /* no intersection occurs */
+
+ /* compute plane of triangle (U0,U1,U2) */
+ SUB(E1,U1,U0);
+ SUB(E2,U2,U0);
+ CROSS(N2,E1,E2);
+ d2=-DOT(N2,U0);
+ /* plane equation 2: N2.X+d2=0 */
+
+ /* put V0,V1,V2 into plane equation 2 */
+ dv0=DOT(N2,V0)+d2;
+ dv1=DOT(N2,V1)+d2;
+ dv2=DOT(N2,V2)+d2;
+
+#if USE_EPSILON_TEST==TRUE
+ if(fabs(dv0)<EPSILON) dv0=0.0;
+ if(fabs(dv1)<EPSILON) dv1=0.0;
+ if(fabs(dv2)<EPSILON) dv2=0.0;
+#endif
+
+ dv0dv1=dv0*dv1;
+ dv0dv2=dv0*dv2;
+
+ if(dv0dv1>0.0f && dv0dv2>0.0f) /* same sign on all of them + not equal 0 ? */
+ return 0; /* no intersection occurs */
+
+ /* compute direction of intersection line */
+ CROSS(D,N1,N2);
+
+ /* compute and index to the largest component of D */
+ max=fabs(D[0]);
+ index=0;
+ b=fabs(D[1]);
+ c=fabs(D[2]);
+ if(b>max) max=b,index=1;
+ if(c>max) max=c,index=2;
+
+ /* this is the simplified projection onto L*/
+ vp0=V0[index];
+ vp1=V1[index];
+ vp2=V2[index];
+
+ up0=U0[index];
+ up1=U1[index];
+ up2=U2[index];
+
+ /* compute interval for triangle 1 */
+ *coplanar=compute_intervals_isectline(V0,V1,V2,vp0,vp1,vp2,dv0,dv1,dv2,
+ dv0dv1,dv0dv2,&isect1[0],&isect1[1],isectpointA1,isectpointA2);
+ if(*coplanar) return coplanar_tri_tri(N1,V0,V1,V2,U0,U1,U2);
+
+
+ /* compute interval for triangle 2 */
+ compute_intervals_isectline(U0,U1,U2,up0,up1,up2,du0,du1,du2,
+ du0du1,du0du2,&isect2[0],&isect2[1],isectpointB1,isectpointB2);
+
+ SORT2(isect1[0],isect1[1],smallest1);
+ SORT2(isect2[0],isect2[1],smallest2);
+
+ if(isect1[1]<isect2[0] || isect2[1]<isect1[0]) return 0;
+
+ /* at this point, we know that the triangles intersect */
+
+ if(isect2[0]<isect1[0])
+ {
+ if(smallest1==0) { SET(isectpt1,isectpointA1); }
+ else { SET(isectpt1,isectpointA2); }
+
+ if(isect2[1]<isect1[1])
+ {
+ if(smallest2==0) { SET(isectpt2,isectpointB2); }
+ else { SET(isectpt2,isectpointB1); }
+ }
+ else
+ {
+ if(smallest1==0) { SET(isectpt2,isectpointA2); }
+ else { SET(isectpt2,isectpointA1); }
+ }
+ }
+ else
+ {
+ if(smallest2==0) { SET(isectpt1,isectpointB1); }
+ else { SET(isectpt1,isectpointB2); }
+
+ if(isect2[1]>isect1[1])
+ {
+ if(smallest1==0) { SET(isectpt2,isectpointA2); }
+ else { SET(isectpt2,isectpointA1); }
+ }
+ else
+ {
+ if(smallest2==0) { SET(isectpt2,isectpointB2); }
+ else { SET(isectpt2,isectpointB1); }
+ }
+ }
+ return 1;
+}
diff --git a/Runtime/Geometry/TriTriIntersect.h b/Runtime/Geometry/TriTriIntersect.h
new file mode 100644
index 0000000..ef3ecb4
--- /dev/null
+++ b/Runtime/Geometry/TriTriIntersect.h
@@ -0,0 +1,9 @@
+#ifndef TRITRIINTERSECT_H
+#define TRITRIINTERSECT_H
+
+int tri_tri_intersect_with_isectline(
+ float V0[3],float V1[3],float V2[3],
+ float U0[3],float U1[3],float U2[3],int *coplanar,
+ float isectpt1[3],float isectpt2[3]);
+
+#endif
diff --git a/Runtime/GfxDevice/BatchRendering.cpp b/Runtime/GfxDevice/BatchRendering.cpp
new file mode 100644
index 0000000..3707b2c
--- /dev/null
+++ b/Runtime/GfxDevice/BatchRendering.cpp
@@ -0,0 +1,279 @@
+#include "UnityPrefix.h"
+#include "BatchRendering.h"
+
+#include "GfxDevice.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "Runtime/Filters/Mesh/TransformVertex.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Filters/Mesh/MeshRenderer.h"
+#include "Runtime/Filters/Mesh/SpriteRenderer.h"
+#include "Runtime/Camera/Renderqueue.h"
+#include "Runtime/Utilities/Prefetch.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemRenderer.h"
+
+
+BatchRenderer::BatchRenderer()
+:m_BatchInstances(kMemTempAlloc)
+,m_ActiveChannels(0)
+,m_ActiveType(kRendererUnknown)
+{
+ m_BatchInstances.reserve(128);
+}
+
+void BatchRenderer::Add(BaseRenderer* renderer, int subsetIndex, ChannelAssigns const* channels, const Matrix4x4f& xform, int xformType)
+{
+ const RendererType rendererType = renderer->GetRendererType();
+ if (m_ActiveChannels != channels || m_ActiveType != rendererType)
+ Flush();
+
+ m_ActiveChannels = channels;
+ m_ActiveType = rendererType;
+
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+
+ // batching requires position
+ bool channelSetupAllowsBatching = (channels->GetTargetMap() & (1<<kVertexCompVertex));
+#if GFX_OPENGLESxx_ONLY
+ // we do not renormalize normals in gles11 so let's play safe here
+ channelSetupAllowsBatching &= !(GetGfxDevice().IsPositionRequiredForTexGen() || GetGfxDevice().IsNormalRequiredForTexGen());
+#endif
+
+
+ bool rendererTypeAllowsBatching = (rendererType == kRendererMesh) || (rendererType == kRendererSprite);
+ if(rendererType == kRendererParticleSystem)
+ {
+ ParticleSystemRenderer* psRenderer = (ParticleSystemRenderer*)renderer;
+ if(kSRMMesh != psRenderer->GetRenderMode()) // We currently don't do batching for mesh particle emitters
+ rendererTypeAllowsBatching = true;
+ }
+ if (channelSetupAllowsBatching && rendererTypeAllowsBatching)
+ {
+ BatchInstanceData& rb = m_BatchInstances.push_back();
+ rb.renderer = (Renderer*)renderer;
+ rb.subsetIndex = subsetIndex;
+ rb.xform = xform;
+ rb.xformType = xformType;
+ }
+ else
+#endif
+ {
+ Assert(channels);
+ SetupObjectMatrix (xform, xformType);
+ renderer->Render (subsetIndex, *channels);
+ }
+}
+
+void BatchRenderer::Flush()
+{
+ size_t instanceCount = m_BatchInstances.size();
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+ if (instanceCount > 0)
+ {
+ Assert(m_ActiveChannels);
+ if (instanceCount == 1)
+ {
+ const BatchInstanceData* data = m_BatchInstances.begin();
+ SetupObjectMatrix (data->xform, data->xformType);
+ data->renderer->Render (data->subsetIndex, *m_ActiveChannels);
+ }
+ else if(m_ActiveType == kRendererMesh)
+ MeshRenderer::RenderMultiple (m_BatchInstances.begin(), instanceCount, *m_ActiveChannels);
+ else if(m_ActiveType == kRendererParticleSystem)
+ ParticleSystemRenderer::RenderMultiple (m_BatchInstances.begin(), instanceCount, *m_ActiveChannels);
+#if ENABLE_SPRITES
+ else if (m_ActiveType == kRendererSprite)
+ SpriteRenderer::RenderMultiple (m_BatchInstances.begin(), instanceCount, *m_ActiveChannels);
+#endif
+ else
+ Assert (!"Renderer type does not support batching");
+ }
+#else
+ Assert(instanceCount == 0);
+#endif
+ m_BatchInstances.resize_uninitialized(0);
+ m_ActiveChannels = 0;
+ m_ActiveType = kRendererUnknown;
+}
+
+void AppendMeshIndices(UInt16* dstIndices, size_t& dstIndexCount, const UInt16* srcIndices, size_t srcIndexCount, bool isTriStrip)
+{
+ Prefetch(srcIndices, srcIndexCount * kVBOIndexSize);
+
+ if (isTriStrip && dstIndexCount > 0)
+ {
+ dstIndices[dstIndexCount] = dstIndices[dstIndexCount - 1];
+ dstIndexCount++;
+ dstIndices[dstIndexCount] = srcIndices[0];
+ dstIndexCount++;
+ }
+
+ memcpy(&dstIndices[dstIndexCount], srcIndices, srcIndexCount * kVBOIndexSize);
+ dstIndexCount += srcIndexCount;
+
+ if (isTriStrip && (srcIndexCount % 2 == 1))
+ {
+ Assert(dstIndexCount != 0);
+ dstIndices[dstIndexCount] = dstIndices[dstIndexCount - 1];
+ dstIndexCount++;
+ Assert(dstIndexCount % 2 == 0);
+ }
+}
+
+
+static inline void
+TransformIndicesInternalImplPositive( int* __restrict dst, const int* __restrict src, unsigned count, int offset )
+{
+ Assert( offset>=0 );
+ Assert( offset<(int)0xFFFF );
+ const int maskOffset = ((unsigned)offset << 16) | (unsigned)offset;
+
+ for( unsigned i = 0 ; i < count ; ++i )
+ *dst++ = *src++ + maskOffset;
+}
+
+static inline void
+TransformIndicesInternalImplNegative( int* __restrict dst, const int* __restrict src, unsigned count, int offset )
+{
+ Assert( offset>=0 );
+ Assert( offset<(int)0xFFFF );
+ const int maskOffset = ((unsigned)offset << 16) | (unsigned)offset;
+
+ for( unsigned i = 0 ; i < count ; ++i )
+ *dst++ = *src++ - maskOffset;
+}
+
+size_t TransformIndices(UInt16* dst, const void* srcIB, size_t firstByte, size_t indexCount, size_t firstVertex, size_t batchVertexOffset, bool isTriStrip)
+{
+ UInt16* srcIndices = (UInt16*)((UInt8*)srcIB + firstByte);
+ Prefetch(srcIndices, indexCount * kVBOIndexSize);
+
+ UInt16* const baseDataPtr = dst;
+ if (isTriStrip && batchVertexOffset > 0)
+ {
+ Assert(srcIndices[0] >= firstVertex);
+ dst[0] = dst[-1];
+ dst[1] = srcIndices[0] - firstVertex + batchVertexOffset;
+ dst += 2;
+ }
+
+ int offset = (int)batchVertexOffset - (int)firstVertex;
+ int copyCount = indexCount/2;
+ if( offset >= 0 ) TransformIndicesInternalImplPositive((int*)dst, (int*)srcIndices, copyCount, offset);
+ else TransformIndicesInternalImplNegative((int*)dst, (int*)srcIndices, copyCount, -offset);
+
+ // leftover, as we copy ints
+ if(2*copyCount != indexCount)
+ dst[indexCount-1] = srcIndices[indexCount-1] - firstVertex + batchVertexOffset;
+
+#ifdef CHECK_INDEX_TRANSFORM_RESULTS
+ const UInt16* dstCheck = dst;
+ for( unsigned i = 0 ; i < indexCount ; ++i )
+ Assert( dstCheck[i] == srcIndices[i] - submesh.firstVertex + batchVertexOffset );
+#endif
+
+ dst += indexCount;
+ if (isTriStrip && indexCount % 2 == 1)
+ {
+ dst[0] = dst[-1];
+ ++dst;
+ }
+ Assert ((dst - baseDataPtr) <= indexCount + 3);
+ return (dst - baseDataPtr);
+}
+
+size_t TransformVertices(UInt8* dst, Matrix4x4f const& m, const VertexBufferData& src, size_t firstVertex, size_t vertexCount, UInt32 channelsInVBO)
+{
+ UInt8* inChannels[kShaderChannelCount];
+ UInt32 inStrides[kShaderChannelCount];
+ bool multiStream = false;
+ for (int i = 0; i < kShaderChannelCount; i++)
+ {
+ if (channelsInVBO & (1 << i))
+ {
+ const ChannelInfo& info = src.channels[i];
+ DebugAssert(info.IsValid());
+ UInt32 stride = info.CalcStride(src.streams);
+ UInt32 offset = info.CalcOffset(src.streams) + stride * firstVertex;
+ inChannels[i] = &src.buffer[offset];
+ inStrides[i] = stride;
+ if (info.stream > 0)
+ multiStream = true;
+ }
+ else
+ {
+ inChannels[i] = NULL;
+ inStrides[i] = 0;
+ }
+ }
+
+ TransformVerticesStrided(
+ StrideIterator<Vector3f>(inChannels[kShaderChannelVertex], inStrides[kShaderChannelVertex]),
+ StrideIterator<Vector3f>(inChannels[kShaderChannelNormal], inStrides[kShaderChannelNormal]),
+ StrideIterator<ColorRGBA32>(inChannels[kShaderChannelColor], inStrides[kShaderChannelColor]),
+ StrideIterator<Vector2f>(inChannels[kShaderChannelTexCoord0], inStrides[kShaderChannelTexCoord0]),
+ StrideIterator<Vector2f>(inChannels[kShaderChannelTexCoord1], inStrides[kShaderChannelTexCoord1]),
+ StrideIterator<Vector4f>(inChannels[kShaderChannelTangent], inStrides[kShaderChannelTangent]),
+ dst, m, vertexCount, multiStream);
+
+ return vertexCount;
+}
+#if ENABLE_SPRITES
+void TransformSprite (UInt8* vb, UInt16* ib, const Matrix4x4f* m, const SpriteRenderData* rd, ColorRGBA32 color, size_t firstVertex)
+{
+ UInt32 vertexCount = rd->vertices.size();
+ UInt32 indexCount = rd->indices.size();
+ UInt8* srcVertices = vertexCount > 0 ? (UInt8 *)&rd->vertices[0] : NULL;
+ UInt16* srcIndices = indexCount > 0 ? (UInt16 *)&rd->indices[0] : NULL;
+
+#if (UNITY_SUPPORTS_NEON || UNITY_SUPPORTS_VFP) && !UNITY_DISABLE_NEON_SKINNING
+ int stride = sizeof(SpriteVertex);
+ UInt8* end = srcVertices + vertexCount * stride;
+ UInt8* uv = srcVertices + sizeof(Vector3f);
+ Matrix4x4f xform = (m) ? *m : Matrix4x4f::identity;
+# if UNITY_SUPPORTS_NEON
+ if (CPUInfo::HasNEONSupport())
+ s_TransformVertices_Sprite_NEON(srcVertices, end, uv, xform.m_Data, (UInt8 *)vb, stride, color.GetUInt32());
+ else
+# endif
+# if UNITY_SUPPORTS_VFP
+ s_TransformVertices_Sprite_VFP (srcVertices, end, uv, xform.m_Data, (UInt8 *)vb, stride, color.GetUInt32());
+# else
+ {
+ ErrorString("non-NEON path not enabled!");
+ }
+# endif
+#else
+ struct SpriteVBOLayout {
+ Vector3f pos;
+ ColorRGBA32 col;
+ Vector2f uv;
+ };
+ SpriteVertex *src = (SpriteVertex *)srcVertices;
+ SpriteVBOLayout *dst = (SpriteVBOLayout *)vb;
+ while (vertexCount-- > 0)
+ {
+ dst->pos = (m) ? m->MultiplyPoint3(src->pos) : src->pos;
+ dst->col = color;
+ dst->uv = src->uv;
+ dst++;
+ src++;
+ }
+#endif
+ Prefetch(srcIndices, indexCount * kVBOIndexSize);
+ if (firstVertex)
+ {
+ int maskOffset = ((unsigned)firstVertex << 16) | (unsigned)firstVertex;
+ int copyCount = indexCount>>1;
+
+ if(2*copyCount != indexCount)
+ ib[indexCount-1] = srcIndices[indexCount-1] + firstVertex;
+ int *dst = (int *)ib;
+ int *src = (int *)srcIndices;
+ while (copyCount-- > 0)
+ *(dst++) = *(src++) + maskOffset;
+ }
+ else
+ memcpy(ib, srcIndices, sizeof(UInt16) * indexCount);
+}
+#endif
diff --git a/Runtime/GfxDevice/BatchRendering.h b/Runtime/GfxDevice/BatchRendering.h
new file mode 100644
index 0000000..a12bdbb
--- /dev/null
+++ b/Runtime/GfxDevice/BatchRendering.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include "GfxDeviceConfigure.h"
+#include "Runtime/Camera/BaseRenderer.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Math/Color.h"
+
+struct BatchInstanceData;
+class Matrix4x4f;
+struct SubMesh;
+struct VertexBufferData;
+class BaseRenderer;
+class ChannelAssigns;
+#if ENABLE_SPRITES
+struct SpriteRenderData;
+#endif
+struct BatchRenderer
+{
+ dynamic_array<BatchInstanceData> m_BatchInstances;
+ ChannelAssigns const* m_ActiveChannels;
+ RendererType m_ActiveType;
+
+ BatchRenderer();
+
+ void Add(BaseRenderer* renderer, int subsetIndex, ChannelAssigns const* channels, const Matrix4x4f& xform, int xformType);
+ void Flush();
+};
+
+void AppendMeshIndices(UInt16* dstIndices, size_t& dstIndexCount, const UInt16* srcIndices, size_t srcIndexCount, bool isTriStrip);
+size_t TransformIndices(UInt16* dst, const void* srcIB, size_t firstByte, size_t indexCount, size_t firstVertex, size_t batchVertexOffset, bool isTriStrip);
+size_t TransformVertices(UInt8* dst, Matrix4x4f const& m, const VertexBufferData& src, size_t firstVertex, size_t vertexCount, UInt32 channelsInVBO);
+#if ENABLE_SPRITES
+void TransformSprite (UInt8* vb, UInt16* ib, const Matrix4x4f* m, const SpriteRenderData* rd, ColorRGBA32 color, size_t firstVertex);
+#endif
diff --git a/Runtime/GfxDevice/BuiltinShaderParams.cpp b/Runtime/GfxDevice/BuiltinShaderParams.cpp
new file mode 100644
index 0000000..a8edcb5
--- /dev/null
+++ b/Runtime/GfxDevice/BuiltinShaderParams.cpp
@@ -0,0 +1,100 @@
+#include "UnityPrefix.h"
+#include "BuiltinShaderParams.h"
+#include "BuiltinShaderParamsNames.h"
+
+BuiltinShaderParamValues::BuiltinShaderParamValues ()
+{
+ memset (vectorParamValues, 0, sizeof(vectorParamValues));
+ memset (matrixParamValues, 0, sizeof(matrixParamValues));
+ memset (instanceVectorValues, 0, sizeof(instanceVectorValues));
+
+ // Initialize default light directions to (1,0,0,0), to avoid the case
+ // when a shader with uninitialized value gets "tolight" vector of zero,
+ // which returns NaN when doing normalize() on it, on GeForce FX/6/7.
+ for (int i = 0; i < kMaxSupportedVertexLights; ++i)
+ vectorParamValues[kShaderVecLight0Position+i].x = 1.0f;
+}
+
+
+
+bool BuiltinShaderParamIndices::CheckMatrixParam(const char* name, int index, int rowCount, int colCount, int cbID)
+{
+ int paramIndex;
+ if (IsShaderInstanceMatrixParam(name, &paramIndex))
+ {
+ mat[paramIndex].gpuIndex = index;
+ mat[paramIndex].rows = rowCount;
+ mat[paramIndex].cols = colCount;
+ #if GFX_SUPPORTS_CONSTANT_BUFFERS
+ mat[paramIndex].cbID = cbID;
+ #endif
+ return true;
+ }
+ return false;
+}
+
+bool BuiltinShaderParamIndices::CheckVectorParam(const char* name, int index, int dim, int cbID)
+{
+ int paramIndex;
+ if (IsShaderInstanceVectorParam(name, &paramIndex))
+ {
+ vec[paramIndex].gpuIndex = index;
+ vec[paramIndex].dim = dim;
+ #if GFX_SUPPORTS_CONSTANT_BUFFERS
+ vec[paramIndex].cbID = cbID;
+ #endif
+ return true;
+ }
+ return false;
+}
+
+// --------------------------------------------------------------------------
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (BuiltinShaderParamsTests)
+{
+ // Having anything named _Reflection as built-in property is dangerous.
+ // We do have a built-in matrix like that, but there are often shaders using that name
+ // as texture. Ensure we never have anything _Reflection in built-ins.
+ TEST(MakeSureNoBuiltinNamedReflection)
+ {
+ int index;
+ CHECK (!IsVectorBuiltinParam ("_Reflection", &index));
+ CHECK (!IsMatrixBuiltinParam ("_Reflection", &index));
+ CHECK (!IsTexEnvBuiltinParam ("_Reflection", &index));
+ }
+
+ // Ensure initial vector & matrix values are zero (except light directions, which should default to 1,0,0,0).
+ // For some built-ins, we only setup their values when needed (e.g. when actual light is set up),
+ // but a shader might reference them and get NaNs or other totally invalid values.
+ TEST (BuiltinParamValuesAreInitialized)
+ {
+ BuiltinShaderParamValues vals;
+ for (int i = 0; i < kShaderVecCount; ++i)
+ {
+ const Vector4f& v = vals.GetVectorParam(BuiltinShaderVectorParam(i));
+ float expected = (i>=kShaderVecLight0Position && i<=kShaderVecLight7Position) ? 1.0f : 0.0f;
+ CHECK_EQUAL (expected,v.x); CHECK_EQUAL (0.0f,v.y); CHECK_EQUAL (0.0f,v.z); CHECK_EQUAL (0.0f,v.w);
+ }
+ for (int i = 0; i < kShaderMatCount; ++i)
+ {
+ const Matrix4x4f& v = vals.GetMatrixParam(BuiltinShaderMatrixParam(i));
+ for (int j = 0; j < 16; ++j)
+ {
+ CHECK_EQUAL (0.0f, v.GetPtr()[j]);
+ }
+ }
+ }
+
+ // basic checks for builtin arrays recognition
+ TEST(BuiltinArrays)
+ {
+ CHECK_EQUAL(IsBuiltinArrayName("unity_LightPosition"), true);
+ CHECK_EQUAL(IsBuiltinArrayName("unity_LightPosition0"), false);
+ }
+} // SUITE
+
+#endif // ENABLE_UNIT_TESTS
diff --git a/Runtime/GfxDevice/BuiltinShaderParams.h b/Runtime/GfxDevice/BuiltinShaderParams.h
new file mode 100644
index 0000000..a39f7f2
--- /dev/null
+++ b/Runtime/GfxDevice/BuiltinShaderParams.h
@@ -0,0 +1,73 @@
+#pragma once
+
+#include "GfxDeviceObjects.h"
+#include "BuiltinShaderParamsNames.h"
+#include "Runtime/Utilities/fixed_bitset.h"
+#include "Runtime/Utilities/NonCopyable.h"
+
+#include "Runtime/Math/Vector4.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "External/shaderlab/Library/texenv.h"
+
+
+struct BuiltinShaderParamIndices
+{
+ struct MatrixParamData {
+ MatrixParamData() : gpuIndex(-1), rows(0), cols(0)
+ #if GFX_SUPPORTS_CONSTANT_BUFFERS
+ , cbID(-1)
+ #endif
+ { }
+ int gpuIndex;
+ UInt16 rows;
+ UInt16 cols;
+ #if GFX_SUPPORTS_CONSTANT_BUFFERS
+ int cbID;
+ #endif
+ };
+ struct VectorParamData {
+ VectorParamData() : gpuIndex(-1), dim(0)
+ #if GFX_SUPPORTS_CONSTANT_BUFFERS
+ , cbID(-1)
+ #endif
+ { }
+ int gpuIndex;
+ SInt16 dim;
+ #if GFX_SUPPORTS_CONSTANT_BUFFERS
+ int cbID;
+ #endif
+ };
+
+ MatrixParamData mat[kShaderInstanceMatCount];
+ VectorParamData vec[kShaderInstanceVecCount];
+
+ bool CheckMatrixParam (const char* name, int index, int rowCount, int colCount, int cbID);
+ bool CheckVectorParam (const char* name, int index, int dim, int cbID);
+};
+
+
+class BuiltinShaderParamValues : public NonCopyable
+{
+public:
+ BuiltinShaderParamValues ();
+
+ FORCE_INLINE const Vector4f& GetVectorParam(BuiltinShaderVectorParam param) const { DebugAssert(param >= 0 && param < kShaderVecCount); return vectorParamValues[param]; }
+ FORCE_INLINE const Matrix4x4f& GetMatrixParam(BuiltinShaderMatrixParam param) const { DebugAssert(param >= 0 && param < kShaderMatCount); return matrixParamValues[param]; }
+ FORCE_INLINE const ShaderLab::TexEnv& GetTexEnvParam(BuiltinShaderTexEnvParam param) const { DebugAssert(param >= 0 && param < kShaderTexEnvCount); return texEnvParamValues[param]; }
+ FORCE_INLINE const Vector4f& GetInstanceVectorParam(ShaderBuiltinInstanceVectorParam param) const { DebugAssert(param >= 0 && param < kShaderInstanceVecCount); return instanceVectorValues[param]; }
+
+ FORCE_INLINE Vector4f& GetWritableVectorParam(BuiltinShaderVectorParam param) { DebugAssert(param >= 0 && param < kShaderVecCount); return vectorParamValues[param]; }
+ FORCE_INLINE Matrix4x4f& GetWritableMatrixParam(BuiltinShaderMatrixParam param) { DebugAssert(param >= 0 && param < kShaderMatCount); return matrixParamValues[param]; }
+ FORCE_INLINE ShaderLab::TexEnv& GetWritableTexEnvParam(BuiltinShaderTexEnvParam param) { DebugAssert(param >= 0 && param < kShaderTexEnvCount); return texEnvParamValues[param]; }
+
+ FORCE_INLINE void SetVectorParam(BuiltinShaderVectorParam param, const Vector4f& val) { GetWritableVectorParam(param) = val; }
+ FORCE_INLINE void SetMatrixParam(BuiltinShaderMatrixParam param, const Matrix4x4f& val) { GetWritableMatrixParam(param) = val; }
+ FORCE_INLINE void SetTexEnvParam(BuiltinShaderTexEnvParam param, const ShaderLab::TexEnv& val) { GetWritableTexEnvParam(param) = val; }
+ FORCE_INLINE void SetInstanceVectorParam(ShaderBuiltinInstanceVectorParam param, const Vector4f& val) { DebugAssert(param >= 0 && param < kShaderInstanceVecCount); instanceVectorValues[param] = val; }
+
+private:
+ Vector4f vectorParamValues[kShaderVecCount];
+ Matrix4x4f matrixParamValues[kShaderMatCount];
+ ShaderLab::TexEnv texEnvParamValues[kShaderTexEnvCount];
+ Vector4f instanceVectorValues[kShaderInstanceVecCount];
+};
diff --git a/Runtime/GfxDevice/BuiltinShaderParamsNames.cpp b/Runtime/GfxDevice/BuiltinShaderParamsNames.cpp
new file mode 100644
index 0000000..d0c9eb2
--- /dev/null
+++ b/Runtime/GfxDevice/BuiltinShaderParamsNames.cpp
@@ -0,0 +1,259 @@
+#include "UnityPrefix.h"
+#include "BuiltinShaderParamsNames.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+
+namespace
+{
+ struct ParamNameIndex
+ {
+ bool operator < (const ParamNameIndex& rhs) const { return strcmp(name, rhs.name) < 0; }
+ bool operator == (const ParamNameIndex& rhs) const { return strcmp(name, rhs.name) == 0; }
+ const char* name;
+ int index;
+ };
+
+ class ParamNameLookup
+ {
+ public:
+ ParamNameLookup() {}
+
+ void AddBuiltinNames(const char** names, int count)
+ {
+ m_Params.resize(count);
+ for (int i = 0; i < count; ++i)
+ {
+ m_Params[i].name = names[i];
+ m_Params[i].index = i;
+ }
+ }
+ void AddSynonyms(ParamNameIndex* params, int count)
+ {
+ m_Params.reserve(m_Params.size() + count);
+ for (int i = 0; i < count; ++i)
+ m_Params.push_back(params[i]);
+ }
+ void Sort()
+ {
+ std::sort(m_Params.begin(), m_Params.end());
+ }
+ bool Find(const char* name, int* outIndex) const
+ {
+ ParamNameIndex key = { name, 0 };
+ ParamNameArray::const_iterator found = std::find(m_Params.begin(), m_Params.end(), key);
+ if (found != m_Params.end())
+ {
+ if (outIndex)
+ *outIndex = found->index;
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ typedef std::vector<ParamNameIndex> ParamNameArray;
+ ParamNameArray m_Params;
+ };
+
+ struct ParamNameLookups
+ {
+ ParamNameLookup instanceMatrices;
+ ParamNameLookup instanceVectors;
+ ParamNameLookup vectors;
+ ParamNameLookup matrices;
+ ParamNameLookup texEnvs;
+ };
+
+ ParamNameLookups* s_NameLookups;
+}
+
+
+//==============================================================================
+
+
+inline static const char** GetInstanceMatrixParams()
+{
+ static const char* lookup[kShaderInstanceMatCount] = {
+ BUILTIN_SHADER_PARAMS_INSTANCE_MATRICES
+
+#if GFX_HIGH_LEVEL_FF
+ ,
+ BUILTIN_SHADER_PARAMS_INSTANCE_MATRICES_FF
+#endif
+ };
+ return lookup;
+}
+
+inline static const char** GetInstanceVectorParams()
+{
+ static const char* lookup[kShaderInstanceVecCount] = {
+ BUILTIN_SHADER_PARAMS_INSTANCE_VECTORS
+ };
+ return lookup;
+}
+
+inline static const char** GetBuiltinVectorParams()
+{
+ static const char* lookup[kShaderVecCount] = {
+ BUILTIN_SHADER_PARAMS_VECTORS
+#if GFX_HIGH_LEVEL_FF
+ ,
+ BUILTIN_SHADER_PARAMS_VECTORS_FF
+#endif
+ };
+ return lookup;
+}
+
+static ParamNameIndex s_VectorSynonymMapping[] = {
+ { "glstate_light0_diffuse", kShaderVecLight0Diffuse },
+ { "glstate_light1_diffuse", kShaderVecLight1Diffuse },
+ { "glstate_light2_diffuse", kShaderVecLight2Diffuse },
+ { "glstate_light3_diffuse", kShaderVecLight3Diffuse },
+ { "glstate_light0_position", kShaderVecLight0Position },
+ { "glstate_light1_position", kShaderVecLight1Position },
+ { "glstate_light2_position", kShaderVecLight2Position },
+ { "glstate_light3_position", kShaderVecLight3Position },
+ { "glstate_light0_attenuation", kShaderVecLight0Atten },
+ { "glstate_light1_attenuation", kShaderVecLight1Atten },
+ { "glstate_light2_attenuation", kShaderVecLight2Atten },
+ { "glstate_light3_attenuation", kShaderVecLight3Atten },
+ { "glstate_light0_spotDirection", kShaderVecLight0SpotDirection },
+ { "glstate_light1_spotDirection", kShaderVecLight1SpotDirection },
+ { "glstate_light2_spotDirection", kShaderVecLight2SpotDirection },
+ { "glstate_light3_spotDirection", kShaderVecLight3SpotDirection }
+};
+
+inline static const char** GetBuiltinMatrixParams()
+{
+ static const char* lookup[kShaderMatCount] = {
+ BUILTIN_SHADER_PARAMS_MATRICES
+ };
+ return lookup;
+}
+
+static ParamNameIndex s_MatrixSynonymMapping[] = {
+ { "unity_World2Shadow", kShaderMatWorldToShadow },
+ { "unity_World2Shadow0", kShaderMatWorldToShadow },
+ { "unity_World2Shadow1", kShaderMatWorldToShadow1 },
+ { "unity_World2Shadow2", kShaderMatWorldToShadow2 },
+ { "unity_World2Shadow3", kShaderMatWorldToShadow3 }
+};
+
+inline static const char** GetBuiltinTexEnvParams()
+{
+ static const char* lookup[kShaderTexEnvCount] = {
+ BUILTIN_SHADER_PARAMS_TEXENVS
+ };
+ return lookup;
+}
+
+
+//------------------------------------------------------------------------------
+
+const char* GetShaderInstanceMatrixParamName(int paramIndex)
+{
+ Assert(paramIndex >= 0 && paramIndex < kShaderInstanceMatCount);
+ return GetInstanceMatrixParams()[paramIndex];
+}
+const char* GetShaderInstanceVectorParamName(int paramIndex)
+{
+ Assert(paramIndex >= 0 && paramIndex < kShaderInstanceVecCount);
+ return GetInstanceVectorParams()[paramIndex];
+}
+const char* GetBuiltinMatrixParamName(int paramIndex)
+{
+ Assert(paramIndex >= 0 && paramIndex < kShaderMatCount);
+ return GetBuiltinMatrixParams()[paramIndex];
+}
+const char* GetBuiltinVectorParamName(int paramIndex)
+{
+ Assert(paramIndex >= 0 && paramIndex < kShaderVecCount);
+ return GetBuiltinVectorParams()[paramIndex];
+}
+const char* GetBuiltinTexEnvParamName(int paramIndex)
+{
+ Assert(paramIndex >= 0 && paramIndex < kShaderTexEnvCount);
+ return GetBuiltinTexEnvParams()[paramIndex];
+}
+
+//------------------------------------------------------------------------------
+
+void InitializeBuiltinShaderParamNames ()
+{
+ Assert (!s_NameLookups);
+ // needs to dynamically allocate this due to static initialization order (FastPropertyNames
+ // might be initialized before this is etc.)
+ s_NameLookups = new ParamNameLookups ();
+
+ s_NameLookups->instanceMatrices.AddBuiltinNames(GetInstanceMatrixParams(), kShaderInstanceMatCount);
+ s_NameLookups->instanceMatrices.Sort();
+
+ s_NameLookups->instanceVectors.AddBuiltinNames(GetInstanceVectorParams(), kShaderInstanceVecCount);
+ s_NameLookups->instanceVectors.Sort();
+
+ s_NameLookups->vectors.AddBuiltinNames(GetBuiltinVectorParams(), kShaderVecCount);
+ s_NameLookups->vectors.AddSynonyms(s_VectorSynonymMapping, ARRAY_SIZE(s_VectorSynonymMapping));
+ s_NameLookups->vectors.Sort();
+
+ s_NameLookups->matrices.AddBuiltinNames(GetBuiltinMatrixParams(), kShaderMatCount);
+ s_NameLookups->matrices.AddSynonyms(s_MatrixSynonymMapping, ARRAY_SIZE(s_MatrixSynonymMapping));
+ s_NameLookups->matrices.Sort();
+
+ s_NameLookups->texEnvs.AddBuiltinNames(GetBuiltinTexEnvParams(), kShaderTexEnvCount);
+ s_NameLookups->texEnvs.Sort();
+}
+
+void CleanupBuiltinShaderParamNames ()
+{
+ delete s_NameLookups;
+ s_NameLookups = NULL;
+}
+
+bool IsShaderInstanceMatrixParam(const char* name, int* paramIndex)
+{
+ return s_NameLookups->instanceMatrices.Find(name, paramIndex);
+}
+
+bool IsShaderInstanceVectorParam(const char* name, int* paramIndex)
+{
+ return s_NameLookups->instanceVectors.Find(name, paramIndex);
+}
+
+bool IsVectorBuiltinParam(const char* name, int* paramIndex)
+{
+ return s_NameLookups->vectors.Find(name, paramIndex);
+}
+
+bool IsMatrixBuiltinParam(const char* name, int* paramIndex)
+{
+ return s_NameLookups->matrices.Find(name, paramIndex);
+}
+
+bool IsTexEnvBuiltinParam(const char* name, int* paramIndex)
+{
+ return s_NameLookups->texEnvs.Find(name, paramIndex);
+}
+
+bool IsBuiltinArrayName(const char* name)
+{
+ static const char* _BuiltinArrayName[] =
+ {
+ "glstate_matrix_texture",
+ "unity_LightColor",
+ "unity_LightPosition",
+ "unity_LightAtten",
+ "unity_World2Shadow",
+ "unity_ShadowSplitSpheres",
+ "_ShadowOffsets",
+ "unity_CameraWorldClipPlanes"
+ };
+
+ static const unsigned _BuiltinArrayCount = sizeof(_BuiltinArrayName) / sizeof(const char*);
+
+ for( unsigned i = 0 ; i < _BuiltinArrayCount ; ++i )
+ {
+ if( ::strcmp(name, _BuiltinArrayName[i]) == 0 )
+ return true;
+ }
+
+ return false;
+}
diff --git a/Runtime/GfxDevice/BuiltinShaderParamsNames.h b/Runtime/GfxDevice/BuiltinShaderParamsNames.h
new file mode 100644
index 0000000..7e68b6e
--- /dev/null
+++ b/Runtime/GfxDevice/BuiltinShaderParamsNames.h
@@ -0,0 +1,444 @@
+#pragma once
+
+
+// Fixed function emulation is done in a high-level language where we need variable names etc.
+#define GFX_HIGH_LEVEL_FF (GFX_SUPPORTS_OPENGLES20 || GFX_SUPPORTS_OPENGLES30 || GFX_SUPPORTS_D3D11)
+
+
+// We need the set of "known" glstate parameters for both non-OpenGL rendering runtimes and
+// the CgBatch compiler (so that it can report errors for unsupported ones).
+// Hence they are in a separate file here.
+
+const char* GetShaderInstanceMatrixParamName(int paramIndex);
+const char* GetShaderInstanceVectorParamName(int paramIndex);
+
+const char* GetBuiltinVectorParamName(int paramIndex);
+const char* GetBuiltinMatrixParamName(int paramIndex);
+const char* GetBuiltinTexEnvParamName(int paramIndex);
+
+bool IsShaderInstanceMatrixParam(const char* name, int* paramIndex=0);
+bool IsShaderInstanceVectorParam(const char* name, int* paramIndex);
+
+bool IsVectorBuiltinParam(const char* name, int* paramIndex=0);
+bool IsMatrixBuiltinParam(const char* name, int* paramIndex=0);
+bool IsTexEnvBuiltinParam(const char* name, int* paramIndex=0);
+
+void InitializeBuiltinShaderParamNames ();
+void CleanupBuiltinShaderParamNames ();
+
+bool IsBuiltinArrayName(const char* name);
+
+
+// Matrices set by unity as "per-instance" data for each object.
+enum ShaderBuiltinInstanceMatrixParam {
+ kShaderInstanceMatMVP = 0,
+ kShaderInstanceMatMV,
+ kShaderInstanceMatM,
+ kShaderInstanceMatInvM,
+ kShaderInstanceMatTransMV,
+ kShaderInstanceMatInvTransMV,
+ kShaderInstanceMatTexture0,
+ kShaderInstanceMatTexture1,
+ kShaderInstanceMatTexture2,
+ kShaderInstanceMatTexture3,
+ kShaderInstanceMatTexture4,
+ kShaderInstanceMatTexture5,
+ kShaderInstanceMatTexture6,
+ kShaderInstanceMatTexture7,
+
+#if GFX_HIGH_LEVEL_FF
+ kShaderInstanceMatNormalMatrix,
+#endif
+
+ kShaderInstanceMatCount
+};
+
+enum ShaderBuiltinInstanceVectorParam {
+ kShaderInstanceVecScale,
+
+ kShaderInstanceVecCount
+};
+
+
+enum BuiltinShaderMatrixParam {
+ kShaderMatView,
+ kShaderMatProj,
+ kShaderMatViewProj,
+ kShaderMatWorldToCamera,
+ kShaderMatCameraToWorld,
+ kShaderMatWorldToShadow,
+ kShaderMatWorldToShadow1,
+ kShaderMatWorldToShadow2,
+ kShaderMatWorldToShadow3,
+ kShaderMatLightmapMatrix,
+ kShaderMatProjector,
+ kShaderMatProjectorDistance,
+ kShaderMatProjectorClip,
+ kShaderMatGUIClip,
+ kShaderMatLightMatrix,
+
+ kShaderMatCount
+};
+
+enum BuiltinShaderVectorParam {
+ kShaderVecLight0Diffuse = 0,
+ kShaderVecLight1Diffuse,
+ kShaderVecLight2Diffuse,
+ kShaderVecLight3Diffuse,
+ kShaderVecLight4Diffuse,
+ kShaderVecLight5Diffuse,
+ kShaderVecLight6Diffuse,
+ kShaderVecLight7Diffuse,
+ kShaderVecLight0Position,
+ kShaderVecLight1Position,
+ kShaderVecLight2Position,
+ kShaderVecLight3Position,
+ kShaderVecLight4Position,
+ kShaderVecLight5Position,
+ kShaderVecLight6Position,
+ kShaderVecLight7Position,
+ kShaderVecLight0SpotDirection,
+ kShaderVecLight1SpotDirection,
+ kShaderVecLight2SpotDirection,
+ kShaderVecLight3SpotDirection,
+ kShaderVecLight4SpotDirection,
+ kShaderVecLight5SpotDirection,
+ kShaderVecLight6SpotDirection,
+ kShaderVecLight7SpotDirection,
+ kShaderVecLight0Atten,
+ kShaderVecLight1Atten,
+ kShaderVecLight2Atten,
+ kShaderVecLight3Atten,
+ kShaderVecLight4Atten,
+ kShaderVecLight5Atten,
+ kShaderVecLight6Atten,
+ kShaderVecLight7Atten,
+ kShaderVecLightModelAmbient,
+ kShaderVecWorldSpaceLightPos0,
+ kShaderVecLightColor0,
+ kShaderVecWorldSpaceCameraPos,
+ kShaderVecProjectionParams,
+ kShaderVecScreenParams,
+ kShaderVecZBufferParams,
+ kShaderVecLightPositionRange,
+ kShaderVecUnityAmbient, //@TODO: kill it; replace all uses with LightModelAmbient
+ kShaderVecLightmapFade,
+ kShaderVecShadowOffset0,
+ kShaderVecShadowOffset1,
+ kShaderVecShadowOffset2,
+ kShaderVecShadowOffset3,
+ kShaderVecLightShadowData,
+ kShaderVecLightShadowBias,
+ kShaderVecLightSplitsNear,
+ kShaderVecLightSplitsFar,
+ kShaderVecShadowSplitSpheres0,
+ kShaderVecShadowSplitSpheres1,
+ kShaderVecShadowSplitSpheres2,
+ kShaderVecShadowSplitSpheres3,
+ kShaderVecShadowSplitSqRadii,
+ kShaderVecShadowFadeCenterAndType,
+ kShaderVecSHAr,
+ kShaderVecSHAg,
+ kShaderVecSHAb,
+ kShaderVecSHBr,
+ kShaderVecSHBg,
+ kShaderVecSHBb,
+ kShaderVecSHC,
+ kShaderVecTime,
+ kShaderVecSinTime,
+ kShaderVecCosTime,
+ kShaderVecPiTime,
+ kShaderVecDeltaTime,
+ kShaderVecVertexLightPosX0,
+ kShaderVecVertexLightPosY0,
+ kShaderVecVertexLightPosZ0,
+ kShaderVecVertexLightAtten0,
+ kShaderVecUnityLightmapST,
+ kShaderVecUnityFogStart,
+ kShaderVecUnityFogEnd,
+ kShaderVecUnityFogDensity,
+ kShaderVecUnityFogColor,
+ kShaderVecColorSpaceGrey,
+ kShaderVecNPOTScale,
+ kShaderVecCameraWorldClipPlanes0,
+ kShaderVecCameraWorldClipPlanes1,
+ kShaderVecCameraWorldClipPlanes2,
+ kShaderVecCameraWorldClipPlanes3,
+ kShaderVecCameraWorldClipPlanes4,
+ kShaderVecCameraWorldClipPlanes5,
+
+ // common textures texel size
+ kShaderVecWhiteTexelSize,
+ kShaderVecBlackTexelSize,
+ kShaderVecRedTexelSize,
+ kShaderVecGrayTexelSize,
+ kShaderVecGreyTexelSize,
+ kShaderVecGrayscaleRampTexelSize,
+ kShaderVecGreyscaleRampTexelSize,
+ kShaderVecBumpTexelSize,
+ kShaderVecLightmapTexelSize,
+ kShaderVecUnityLightmapTexelSize,
+ kShaderVecUnityLightmapIndTexelSize,
+ kShaderVecUnityLightmapThirdTexelSize,
+ kShaderVecLightTextureB0TexelSize,
+ kShaderVecGUIClipTexelSize,
+ kShaderVecDitherMaskLODSize,
+ kShaderVecRandomRotationTexelSize,
+
+
+#if GFX_HIGH_LEVEL_FF
+ kShaderVecFFColor,
+ kShaderVecFFTextureEnvColor0,
+ kShaderVecFFTextureEnvColor1,
+ kShaderVecFFTextureEnvColor2,
+ kShaderVecFFTextureEnvColor3,
+ kShaderVecFFTextureEnvColor4,
+ kShaderVecFFTextureEnvColor5,
+ kShaderVecFFTextureEnvColor6,
+ kShaderVecFFTextureEnvColor7,
+ kShaderVecFFMatEmission,
+ kShaderVecFFMatAmbient,
+ kShaderVecFFMatDiffuse,
+ kShaderVecFFMatSpecular,
+ kShaderVecFFMatShininess,
+ kShaderVecFFAlphaTestRef,
+ kShaderVecFFFogColor,
+ kShaderVecFFFogParams,
+#endif
+
+ kShaderVecCount
+};
+
+enum BuiltinShaderTexEnvParam {
+ kShaderTexEnvWhite = 0,
+ kShaderTexEnvBlack,
+ kShaderTexEnvRed,
+ kShaderTexEnvGray,
+ kShaderTexEnvGrey, // TODO: synonims
+ kShaderTexEnvGrayscaleRamp,
+ kShaderTexEnvGreyscaleRamp, // TODO: synonims
+ kShaderTexEnvBump,
+ kShaderTexEnvLightmap,
+ kShaderTexEnvUnityLightmap,
+ kShaderTexEnvUnityLightmapInd,
+ kShaderTexEnvUnityLightmapThird,
+ kShaderTexEnvDitherMaskLOD,
+ kShaderTexEnvRandomRotation,
+
+ kShaderTexEnvCount
+};
+
+
+
+#define BUILTIN_SHADER_PARAMS_INSTANCE_MATRICES \
+ "glstate_matrix_mvp", \
+ "glstate_matrix_modelview0", \
+ "_Object2World", \
+ "_World2Object", \
+ "glstate_matrix_transpose_modelview0", \
+ "glstate_matrix_invtrans_modelview0", \
+ "glstate_matrix_texture0", \
+ "glstate_matrix_texture1", \
+ "glstate_matrix_texture2", \
+ "glstate_matrix_texture3", \
+ "glstate_matrix_texture4", \
+ "glstate_matrix_texture5", \
+ "glstate_matrix_texture6", \
+ "glstate_matrix_texture7" \
+
+#define BUILTIN_SHADER_PARAMS_INSTANCE_VECTORS \
+ "unity_Scale" \
+
+
+#define BUILTIN_SHADER_PARAMS_MATRICES \
+ "unity_MatrixV", \
+ "glstate_matrix_projection", \
+ "unity_MatrixVP", \
+ "_WorldToCamera", \
+ "_CameraToWorld", \
+ "_World2Shadow", \
+ "_World2Shadow1", \
+ "_World2Shadow2", \
+ "_World2Shadow3", \
+ "unity_LightmapMatrix", \
+ "_Projector", \
+ "_ProjectorDistance", \
+ "_ProjectorClip", \
+ "_GUIClipTextureMatrix", \
+ "_LightMatrix0" \
+
+
+#define BUILTIN_SHADER_PARAMS_VECTORS \
+ "unity_LightColor0", \
+ "unity_LightColor1", \
+ "unity_LightColor2", \
+ "unity_LightColor3", \
+ "unity_LightColor4", \
+ "unity_LightColor5", \
+ "unity_LightColor6", \
+ "unity_LightColor7", \
+ "unity_LightPosition0", \
+ "unity_LightPosition1", \
+ "unity_LightPosition2", \
+ "unity_LightPosition3", \
+ "unity_LightPosition4", \
+ "unity_LightPosition5", \
+ "unity_LightPosition6", \
+ "unity_LightPosition7", \
+ "unity_SpotDirection0", \
+ "unity_SpotDirection1", \
+ "unity_SpotDirection2", \
+ "unity_SpotDirection3", \
+ "unity_SpotDirection4", \
+ "unity_SpotDirection5", \
+ "unity_SpotDirection6", \
+ "unity_SpotDirection7", \
+ "unity_LightAtten0", \
+ "unity_LightAtten1", \
+ "unity_LightAtten2", \
+ "unity_LightAtten3", \
+ "unity_LightAtten4", \
+ "unity_LightAtten5", \
+ "unity_LightAtten6", \
+ "unity_LightAtten7", \
+ "glstate_lightmodel_ambient", \
+ "_WorldSpaceLightPos0", \
+ "_LightColor0", \
+ "_WorldSpaceCameraPos", \
+ "_ProjectionParams", \
+ "_ScreenParams", \
+ "_ZBufferParams", \
+ "_LightPositionRange", \
+ "unity_Ambient", \
+ "unity_LightmapFade", \
+ "_ShadowOffsets0", \
+ "_ShadowOffsets1", \
+ "_ShadowOffsets2", \
+ "_ShadowOffsets3", \
+ "_LightShadowData", \
+ "unity_LightShadowBias", \
+ "_LightSplitsNear", \
+ "_LightSplitsFar", \
+ "unity_ShadowSplitSpheres0", \
+ "unity_ShadowSplitSpheres1", \
+ "unity_ShadowSplitSpheres2", \
+ "unity_ShadowSplitSpheres3", \
+ "unity_ShadowSplitSqRadii", \
+ "unity_ShadowFadeCenterAndType", \
+ "unity_SHAr", \
+ "unity_SHAg", \
+ "unity_SHAb", \
+ "unity_SHBr", \
+ "unity_SHBg", \
+ "unity_SHBb", \
+ "unity_SHC", \
+ "_Time", \
+ "_SinTime", \
+ "_CosTime", \
+ "_PiTime", \
+ "unity_DeltaTime", \
+ "unity_4LightPosX0", \
+ "unity_4LightPosY0", \
+ "unity_4LightPosZ0", \
+ "unity_4LightAtten0", \
+ "unity_LightmapST", \
+ "unity_FogStart", \
+ "unity_FogEnd", \
+ "unity_FogDensity", \
+ "unity_FogColor", \
+ "unity_ColorSpaceGrey", \
+ "unity_NPOTScale", \
+ "unity_CameraWorldClipPlanes0", \
+ "unity_CameraWorldClipPlanes1", \
+ "unity_CameraWorldClipPlanes2", \
+ "unity_CameraWorldClipPlanes3", \
+ "unity_CameraWorldClipPlanes4", \
+ "unity_CameraWorldClipPlanes5", \
+ \
+ "white_TexelSize", \
+ "black_TexelSize", \
+ "red_TexelSize", \
+ "gray_TexelSize", \
+ "grey_TexelSize", \
+ "grayscaleRamp_TexelSize", \
+ "greyscaleRamp_TexelSize", \
+ "bump_TexelSize", \
+ "lightmap_TexelSize", \
+ "unity_Lightmap_TexelSize", \
+ "unity_LightmapInd_TexelSize", \
+ "unity_LightmapThird_TexelSize", \
+ "_LightTextureB0_TexelSize", \
+ "_GUIClipTexture_TexelSize", \
+ "_DitherMaskLOD_TexelSize", \
+ "unity_RandomRotation16_TexelSize"
+
+#define BUILTIN_SHADER_PARAMS_VECTORS_SYNONYMS \
+ /* aliases for unity_LightColorN */ \
+ "glstate_light0_diffuse", \
+ "glstate_light1_diffuse", \
+ "glstate_light2_diffuse", \
+ "glstate_light3_diffuse", \
+ /* aliases for unity_LightPositionN */ \
+ "glstate_light0_position", \
+ "glstate_light1_position", \
+ "glstate_light2_position", \
+ "glstate_light3_position", \
+ /* aliases for unity_LightAttenN */ \
+ "glstate_light0_attenuation", \
+ "glstate_light1_attenuation", \
+ "glstate_light2_attenuation", \
+ "glstate_light3_attenuation", \
+ /* aliases for unity_SpotDirectionN */ \
+ "glstate_light0_spotDirection", \
+ "glstate_light1_spotDirection", \
+ "glstate_light2_spotDirection", \
+ "glstate_light3_spotDirection"
+
+
+
+
+#define BUILTIN_SHADER_PARAMS_TEXENVS \
+ "white", \
+ "black", \
+ "red", \
+ "gray", \
+ "grey", \
+ "grayscaleRamp", \
+ "greyscaleRamp", \
+ "bump", \
+ "lightmap", \
+ "unity_Lightmap", \
+ "unity_LightmapInd", \
+ "unity_LightmapThird", \
+ "_DitherMaskLOD", \
+ "unity_RandomRotation16"
+
+
+
+#if GFX_HIGH_LEVEL_FF
+
+#define BUILTIN_SHADER_PARAMS_INSTANCE_MATRICES_FF \
+ "_glesNormalMatrix" \
+
+
+#define BUILTIN_SHADER_PARAMS_VECTORS_FF \
+ "_glesFFColor", \
+ "_glesTextureEnvColor0", \
+ "_glesTextureEnvColor1", \
+ "_glesTextureEnvColor2", \
+ "_glesTextureEnvColor3", \
+ "_glesTextureEnvColor4", \
+ "_glesTextureEnvColor5", \
+ "_glesTextureEnvColor6", \
+ "_glesTextureEnvColor7", \
+ "_glesFrontMaterial.emission", \
+ "_glesFrontMaterial.ambient", \
+ "_glesFrontMaterial.diffuse", \
+ "_glesFrontMaterial.specular", \
+ "_glesFrontMaterial.shininess", \
+ "_glesAlphaTestReference", \
+ "_glesFogColor", \
+ "_glesFogParams" \
+
+#endif
+
diff --git a/Runtime/GfxDevice/ChannelAssigns.cpp b/Runtime/GfxDevice/ChannelAssigns.cpp
new file mode 100644
index 0000000..59ec758
--- /dev/null
+++ b/Runtime/GfxDevice/ChannelAssigns.cpp
@@ -0,0 +1,225 @@
+#include "UnityPrefix.h"
+#include "ChannelAssigns.h"
+//#include "ShaderLabErrors.h"
+
+// Channel names (strings for reading from ShaderLab)
+static const char * const kShaderChannelName[kShaderChannelCount] = {
+ "VERTEX",
+ "NORMAL",
+ "COLOR",
+ "TEXCOORD",
+ "TEXCOORD1",
+ "TANGENT",
+};
+
+
+ShaderChannel GetShaderChannelFromName( const std::string& name )
+{
+ std::string nameUpper = ToUpper(name);
+ for( int i = 0; i < kShaderChannelCount; ++i )
+ if( kShaderChannelName[i] == nameUpper )
+ return (ShaderChannel)i;
+
+ return kShaderChannelNone;
+}
+
+
+
+ChannelAssigns::ChannelAssigns()
+: m_TargetMap(0)
+, m_SourceMap(0)
+, m_DirectlyWired(true)
+{
+ for( int i = 0; i < kVertexCompCount; ++i )
+ m_Channels[i] = kShaderChannelNone;
+}
+
+void ChannelAssigns::MergeWith( const ChannelAssigns& additional )
+{
+ for( int i = 0; i < kVertexCompCount; ++i )
+ {
+ ShaderChannel source = additional.GetSourceForTarget(VertexComponent(i));
+ if( source != kShaderChannelNone )
+ Bind( source, (VertexComponent)i );
+ }
+}
+
+static bool IsChannelDirectlyWired (ShaderChannel source, VertexComponent target)
+{
+ switch (source)
+ {
+ case kShaderChannelVertex:
+ return (target == kVertexCompVertex);
+ case kShaderChannelNormal:
+ return (target == kVertexCompNormal);
+ case kShaderChannelColor:
+ return (target == kVertexCompColor);
+ case kShaderChannelTexCoord0:
+ return (target == kVertexCompTexCoord0);
+ case kShaderChannelTexCoord1:
+ return (target == kVertexCompTexCoord1);
+ case kShaderChannelTangent:
+ return (target == kVertexCompTexCoord2);
+ default:
+ break;
+ }
+ return false;
+}
+
+void ChannelAssigns::Bind (ShaderChannel source, VertexComponent target)
+{
+ AssertIf( source == kShaderChannelNone );
+ // TODO: skip kShaderChannelTexCoord ones here?
+ // TODO: filter duplicates somehow?
+ m_Channels[target] = source;
+ m_TargetMap |= (1<<target);
+ m_SourceMap |= (1<<source);
+
+ if (m_DirectlyWired)
+ m_DirectlyWired = IsChannelDirectlyWired (source, target);
+}
+
+void ChannelAssigns::Unbind( VertexComponent target )
+{
+ m_TargetMap &= ~(1<<target);
+ m_Channels[target] = kShaderChannelNone;
+
+ RecalculateIsDirectlyWired ();
+}
+
+void ChannelAssigns::RecalculateIsDirectlyWired ()
+{
+ m_DirectlyWired = true;
+ for (int i = 0; i < kVertexCompCount && m_DirectlyWired; ++i)
+ {
+ ShaderChannel source = GetSourceForTarget(VertexComponent(i));
+ if( source != kShaderChannelNone )
+ m_DirectlyWired &= IsChannelDirectlyWired (source, (VertexComponent)i);
+ }
+}
+
+bool ChannelAssigns::operator== (const ChannelAssigns& other) const
+{
+ return m_SourceMap == other.m_SourceMap &&
+ m_TargetMap == other.m_TargetMap &&
+ memcmp(&m_Channels[0], &other.m_Channels[0], sizeof(m_Channels)) == 0;
+}
+
+
+#if ENABLE_UNIT_TESTS
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "External/shaderlab/Library/SLParserData.h"
+SUITE (ChanelAssignsTest)
+{
+TEST(IsDirectlyWired)
+{
+ ChannelAssigns ch;
+ // usual binding
+ ch.Bind(kShaderChannelVertex, kVertexCompVertex);
+ CHECK(ch.IsDirectlyWired());
+ ch.Bind(kShaderChannelNormal, kVertexCompNormal);
+ CHECK(ch.IsDirectlyWired());
+ ch.Bind(kShaderChannelColor, kVertexCompColor);
+ CHECK(ch.IsDirectlyWired());
+ ch.Bind(kShaderChannelTexCoord0, kVertexCompTexCoord0);
+ CHECK(ch.IsDirectlyWired());
+ ch.Bind(kShaderChannelTexCoord1, kVertexCompTexCoord1);
+ CHECK(ch.IsDirectlyWired());
+ ch.Bind(kShaderChannelTangent, kVertexCompTexCoord2);
+ CHECK(ch.IsDirectlyWired());
+
+ // bind twice
+ ch.Bind(kShaderChannelVertex, kVertexCompVertex);
+ ch.Bind(kShaderChannelNormal, kVertexCompNormal);
+ ch.Bind(kShaderChannelColor, kVertexCompColor);
+ ch.Bind(kShaderChannelTexCoord0, kVertexCompTexCoord0);
+ ch.Bind(kShaderChannelTexCoord1, kVertexCompTexCoord1);
+ ch.Bind(kShaderChannelTangent, kVertexCompTexCoord2);
+ CHECK(ch.IsDirectlyWired());
+
+ // cross-bar
+ ch.Bind(kShaderChannelTexCoord0, kVertexCompTexCoord2);
+ CHECK(!ch.IsDirectlyWired());
+
+ // unbinding cross-bar
+ ch.Unbind(kVertexCompTexCoord2);
+ CHECK(ch.IsDirectlyWired());
+
+ // rebinding
+ ch.Bind(kShaderChannelTexCoord0, kVertexCompTexCoord0);
+ ch.Bind(kShaderChannelTexCoord1, kVertexCompTexCoord1);
+ ch.Bind(kShaderChannelTangent, kVertexCompTexCoord2);
+ CHECK(ch.IsDirectlyWired());
+
+ // cross-bar 2
+ ChannelAssigns ch2 = ch;
+ ch2.Bind(kShaderChannelVertex, kVertexCompNormal);
+ CHECK(!ch2.IsDirectlyWired());
+ ch2 = ch;
+ ch2.Bind(kShaderChannelNormal, kVertexCompColor);
+ CHECK(!ch2.IsDirectlyWired());
+ ch2 = ch;
+ ch2.Bind(kShaderChannelColor, kVertexCompTexCoord0);
+ CHECK(!ch2.IsDirectlyWired());
+ ch2 = ch;
+ ch2.Bind(kShaderChannelTexCoord0, kVertexCompTexCoord2);
+ CHECK(!ch2.IsDirectlyWired());
+ ch2 = ch;
+ ch2.Bind(kShaderChannelTexCoord1, kVertexCompVertex);
+ CHECK(!ch2.IsDirectlyWired());
+ ch2 = ch;
+ ch2.Bind(kShaderChannelTangent, kVertexCompAttrib0);
+ CHECK(!ch2.IsDirectlyWired());
+ ch2 = ch;
+ ch2.Bind(kShaderChannelTexCoord0, kVertexCompTexCoord1);
+ CHECK(!ch2.IsDirectlyWired());
+ ch2 = ch;
+ ch2.Bind(kShaderChannelTexCoord0, kVertexCompTexCoord2);
+ CHECK(!ch2.IsDirectlyWired());
+ ch2 = ch;
+ ch2.Bind(kShaderChannelTexCoord0, kVertexCompColor);
+ CHECK(!ch2.IsDirectlyWired());
+
+ // unbinding all
+ ch.Unbind(kVertexCompVertex);
+ ch.Unbind(kVertexCompNormal);
+ ch.Unbind(kVertexCompColor);
+ ch.Unbind(kVertexCompTexCoord0);
+ ch.Unbind(kVertexCompTexCoord1);
+ ch.Unbind(kVertexCompTexCoord2);
+ CHECK(ch.IsDirectlyWired());
+
+ // check empty/1channel
+ ch.Bind(kShaderChannelVertex, kVertexCompTexCoord0);
+ CHECK(!ch.IsDirectlyWired());
+
+ ch.Unbind(kVertexCompTexCoord0);
+ CHECK(ch.IsDirectlyWired());
+ CHECK(ch.IsEmpty());
+
+ ch.Bind(kShaderChannelVertex, kVertexCompVertex);
+ CHECK(ch.IsDirectlyWired());
+}
+TEST(IsDirectlyWiredXBar)
+{
+ ChannelAssigns ch;
+ // usual binding
+ ch.Bind(kShaderChannelVertex, kVertexCompVertex);
+ ch.Bind(kShaderChannelNormal, kVertexCompNormal);
+ CHECK(ch.IsDirectlyWired());
+ ch.Bind(kShaderChannelTexCoord0, kVertexCompTexCoord1);
+ ch.Bind(kShaderChannelTexCoord1, kVertexCompTexCoord0);
+ CHECK(!ch.IsDirectlyWired());
+}
+TEST(IsDirectlyWiredUpdatedWhenCreatingFromParserChannels)
+{
+ ShaderLab::ParserBindChannels pchn;
+ pchn.Bind(kShaderChannelTexCoord0, kVertexCompTexCoord1, false, NULL);
+ pchn.Bind(kShaderChannelTexCoord1, kVertexCompTexCoord0, false, NULL);
+ ChannelAssigns ch;
+ ch.FromParsedChannels (pchn);
+ CHECK(!ch.IsDirectlyWired());
+}
+}
+
+#endif
diff --git a/Runtime/GfxDevice/ChannelAssigns.h b/Runtime/GfxDevice/ChannelAssigns.h
new file mode 100644
index 0000000..fafb4d5
--- /dev/null
+++ b/Runtime/GfxDevice/ChannelAssigns.h
@@ -0,0 +1,52 @@
+#ifndef __CHANNELASSIGNS_H__
+#define __CHANNELASSIGNS_H__
+
+#include <string>
+#include "GfxDeviceTypes.h"
+
+namespace ShaderLab {
+struct ParserBindChannels;
+}
+
+// Tracks which vertex components should be sourced from which shader channels
+// TODO: It gets serialized a lot for multithreading, can we make it more compact?
+class ChannelAssigns {
+public:
+ ChannelAssigns();
+ void FromParsedChannels (const ShaderLab::ParserBindChannels& parsed); // ShaderParser.cpp
+
+ void Bind( ShaderChannel source, VertexComponent target );
+ void Unbind( VertexComponent target );
+ void MergeWith( const ChannelAssigns& additional );
+
+ UInt32 GetTargetMap() const { return m_TargetMap; }
+ UInt32 GetSourceMap() const { return m_SourceMap; }
+ bool IsEmpty() const { return m_TargetMap == 0; }
+
+ // if and only if all source channels directly map to target components
+ // src.Vertex -> dst.Vertex, src.Normal -> dst.Normal, etc
+ // there is NO cross-bar connections like: src.TexCoord0 -> dst.TexCoord1
+ bool IsDirectlyWired() const { return m_DirectlyWired; }
+
+ ShaderChannel GetSourceForTarget( VertexComponent target ) const { return ShaderChannel(m_Channels[target]); }
+
+ bool operator== (const ChannelAssigns& other) const;
+
+private:
+ void RecalculateIsDirectlyWired ();
+
+private:
+ UInt32 m_TargetMap; // bitfield of which vertex components are sourced
+ UInt32 m_SourceMap; // bitfield of which source channels are used
+ SInt8 m_Channels[kVertexCompCount]; // for each vertex component: from which channel it is sourced
+ bool m_DirectlyWired;
+
+ // Friends for serialization
+ friend struct GfxRet_ChannelAssigns;
+};
+
+
+ShaderChannel GetShaderChannelFromName( const std::string& name );
+
+
+#endif
diff --git a/Runtime/GfxDevice/GLDataBufferCommon.h b/Runtime/GfxDevice/GLDataBufferCommon.h
new file mode 100644
index 0000000..7e7a9b9
--- /dev/null
+++ b/Runtime/GfxDevice/GLDataBufferCommon.h
@@ -0,0 +1,24 @@
+#ifndef GL_DATABUFFER_COMMON_H
+#define GL_DATABUFFER_COMMON_H
+
+#if GFX_SUPPORTS_OPENGL || GFX_SUPPORTS_OPENGLES || GFX_SUPPORTS_OPENGLES20 || GFX_SUPPORTS_OPENGLES30
+
+#define DATA_BUFFER_ID_MASK 0xC0000000
+#define MAKE_DATA_BUFFER_ID(id) (id|DATA_BUFFER_ID_MASK)
+
+inline void glRegisterBufferData(UInt32 bufferID, GLsizeiptr size, void* related)
+{
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(MAKE_DATA_BUFFER_ID(bufferID) );
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(MAKE_DATA_BUFFER_ID(bufferID),size,related);
+}
+
+inline void glDeregisterBufferData(int count, GLuint* buffferIds)
+{
+ for (size_t q = 0; q < count; ++q)
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(MAKE_DATA_BUFFER_ID(buffferIds[q]) );
+}
+
+#endif
+
+#endif // GL_DATABUFFER_COMMON_H
+
diff --git a/Runtime/GfxDevice/GLESChannels.h b/Runtime/GfxDevice/GLESChannels.h
new file mode 100644
index 0000000..9a683e7
--- /dev/null
+++ b/Runtime/GfxDevice/GLESChannels.h
@@ -0,0 +1,84 @@
+#ifndef GLES_CHANNELS_H
+#define GLES_CHANNELS_H
+
+#if GFX_SUPPORTS_OPENGLES20
+ #include "Runtime/GfxDevice/opengles20/UnityGLES20Ext.h"
+#endif
+
+// dimensionality of the different channels (element count)
+static const int kDefaultChannelSizes[kShaderChannelCount] = {
+ 3, // pos
+ 3, // normal
+ 4, // color
+ 2, // uv
+ 2, // uv2
+ 4, // tangent
+};
+
+static const GLenum kDefaultChannelTypes[kShaderChannelCount] = {
+ GL_FLOAT, // pos
+ GL_FLOAT, // normal
+ GL_UNSIGNED_BYTE, // color
+ GL_FLOAT, // UV0
+ GL_FLOAT, // UV1
+ GL_FLOAT, // tangent
+};
+
+enum VertexChannel {
+ kVtxChnVertex = 1 << kVertexCompVertex,
+ kVtxChnColor = 1 << kVertexCompColor,
+ kVtxChnNormal = 1 << kVertexCompNormal,
+ kVtxChnTexCoord0 = 1 << kVertexCompTexCoord0,
+ kVtxChnTexCoord1 = 1 << kVertexCompTexCoord1,
+ kVtxChnTexCoord2 = 1 << kVertexCompTexCoord2,
+ kVtxChnTexCoord3 = 1 << kVertexCompTexCoord3,
+ kVtxChnTexCoord4 = 1 << kVertexCompTexCoord4,
+ kVtxChnTexCoord5 = 1 << kVertexCompTexCoord5,
+ kVtxChnTexCoord6 = 1 << kVertexCompTexCoord6,
+ kVtxChnTexCoord7 = 1 << kVertexCompTexCoord7
+};
+
+static const VertexChannel sTexCoordChannels[] = {
+ kVtxChnTexCoord0, kVtxChnTexCoord1, kVtxChnTexCoord2, kVtxChnTexCoord3,
+ kVtxChnTexCoord4, kVtxChnTexCoord5, kVtxChnTexCoord6, kVtxChnTexCoord7
+};
+
+static void SetupGLESChannelSizes(const ChannelInfoArray channels, int outSizes[kShaderChannelCount])
+{
+ for (int i = 0; i < kShaderChannelCount; i++)
+ {
+ if (channels[i].format == kChannelFormatColor)
+ outSizes[i] = 4;
+ else
+ outSizes[i] = channels[i].dimension;
+ }
+}
+
+static void SetupGLESChannelTypes(const ChannelInfoArray channels, GLenum outTypes[kShaderChannelCount])
+{
+ for (int i = 0; i < kShaderChannelCount; i++)
+ {
+ switch (channels[i].format)
+ {
+ case kChannelFormatFloat:
+ outTypes[i] = GL_FLOAT;
+ break;
+#if GFX_SUPPORTS_OPENGLES20
+ case kChannelFormatFloat16:
+ outTypes[i] = GL_HALF_FLOAT_OES;
+ break;
+#endif
+ case kChannelFormatColor:
+ outTypes[i] = GL_UNSIGNED_BYTE;
+ break;
+ case kChannelFormatByte:
+ outTypes[i] = GL_BYTE;
+ break;
+ default:
+ if (channels[i].IsValid())
+ ErrorString("Vertex channel has invalid type for GLES");
+ }
+ }
+}
+
+#endif
diff --git a/Runtime/GfxDevice/GLESCommon.h b/Runtime/GfxDevice/GLESCommon.h
new file mode 100644
index 0000000..9d1ffee
--- /dev/null
+++ b/Runtime/GfxDevice/GLESCommon.h
@@ -0,0 +1,105 @@
+#ifndef GLES_COMMON_H
+#define GLES_COMMON_H
+
+// internal header
+// common gles 1.x/2.x stuff
+// also some common android/ios stuff
+
+#include <ctype.h>
+
+inline int GLES_EstimateVRAM_MB(int total_mem_mb)
+{
+#if UNITY_IPHONE
+ // For iOS we estimate 1/4 of POT(total_mem)
+ const int physical_mem_mb = 1 << (32 - __builtin_clz(total_mem_mb - 1));
+ return physical_mem_mb >> 2;
+#elif UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+ // There is (afawk) no way to determine the size of the VRAM
+ // So we try to create ('fake') a value that is somewhere along the lines of possibly correct.
+ // We make two assumption here:
+ // 1) The VRAM is cut-out from the physical memory range and 2) VRAM size is never bigger than
+ // a 1/4 of the physical memory size, but still bigger than a 1/16 of the total memory size.
+ // Even if not correct it should give an indication of how much memory is available for GPU resources.
+ const int physical_mem_mb = 1 << (32 - __builtin_clz(total_mem_mb - 1));
+ return std::max( std::min(physical_mem_mb - total_mem_mb, (physical_mem_mb >> 2)), (total_mem_mb >> 4) );
+#else
+ return 256;
+#endif
+}
+
+namespace systeminfo { int GetPhysicalMemoryMB(); }
+struct GraphicsCaps;
+
+inline void GLES_InitCommonCaps(GraphicsCaps* caps)
+{
+ {
+ caps->vendorID = 0;
+ caps->rendererID = 0;
+
+ #define GRAB_STRING(target, name) \
+ do { \
+ const char* _tmp_str = (const char*)glGetString(name); \
+ GLESAssert(); \
+ caps->target = _tmp_str ? _tmp_str : "<unknown>"; \
+ } while(0) \
+
+ GRAB_STRING(rendererString, GL_RENDERER);
+ GRAB_STRING(vendorString, GL_VENDOR);
+ GRAB_STRING(driverVersionString, GL_VERSION);
+
+ #undef GRAB_STRING
+
+ // Distill
+ // driverVersionString = "OpenGL ES 2.0 build 1.8@905891"
+ // into
+ // driverLibraryString = "build 1.8@905891"
+ //
+ // See http://www.khronos.org/opengles/sdk/1.1/docs/man/glGetString.xml
+ //
+ const char OpenGL[] = "OpenGL";
+ const char ES[] = "ES";
+ const char* gl_version = caps->driverVersionString.c_str();
+ for (int i = 0 ; i < 3; ++i, ++gl_version)
+ {
+ if( (i == 0 && strncmp(gl_version, OpenGL, sizeof(OpenGL)-1))
+ || (i == 1 && strncmp(gl_version, ES, sizeof(ES)-1))
+ || (i == 2 && !isdigit(*gl_version))
+ )
+ {
+ gl_version = NULL;
+ break;
+ }
+ if ( !(gl_version = strstr(gl_version, " ")) )
+ break;
+ }
+
+ if (gl_version)
+ caps->driverLibraryString = gl_version;
+ else
+ caps->driverLibraryString = "n/a";
+
+ caps->fixedVersionString = caps->driverVersionString;
+
+ const char* ext = (const char*)glGetString(GL_EXTENSIONS);
+ GLESAssert();
+
+ ::printf_console ("Renderer: %s\n", caps->rendererString.c_str());
+ ::printf_console ("Vendor: %s\n", caps->vendorString.c_str());
+ ::printf_console ("Version: %s\n", caps->driverVersionString.c_str());
+
+ if(ext) DebugTextLineByLine(ext);
+ else ::printf_console("glGetString(GL_EXTENSIONS) - failure");
+ }
+
+ {
+ #if UNITY_ANDROID || UNITY_IPHONE || UNITY_BB10 || UNITY_TIZEN
+ caps->videoMemoryMB = GLES_EstimateVRAM_MB(systeminfo::GetPhysicalMemoryMB());
+ #else
+ caps->videoMemoryMB = 256; // awesome estimation
+ #endif
+ }
+}
+
+
+#endif // GLES_COMMON_H
+
diff --git a/Runtime/GfxDevice/GLRTCommon.h b/Runtime/GfxDevice/GLRTCommon.h
new file mode 100644
index 0000000..89d0659
--- /dev/null
+++ b/Runtime/GfxDevice/GLRTCommon.h
@@ -0,0 +1,370 @@
+#ifndef GL_RT_COMMON_H
+#define GL_RT_COMMON_H
+
+// internal header
+// common RT-related stuff
+// as we might need both gles and gles20 linked we a forced to use external defines here
+
+#ifndef GL_RT_COMMON_GLES
+#define GL_RT_COMMON_GLES 0
+#endif
+
+#ifndef GL_RT_COMMON_GLES2
+#define GL_RT_COMMON_GLES2 0
+#endif
+
+#ifndef GL_RT_COMMON_GL
+#define GL_RT_COMMON_GL 0
+#endif
+
+#if GL_RT_COMMON_GLES==0 && GL_RT_COMMON_GLES2==0 && GL_RT_COMMON_GL==0
+#error dont include this header without specifying api used
+#endif
+
+
+#if GL_RT_COMMON_GLES
+#define CHECK GL_CHK
+#define EXT_CALL(f) f##OES
+#define EXT_ENUM(x) x##_OES
+#define MANGLE_NAME(f) f##GLES
+#elif GL_RT_COMMON_GLES2
+#define CHECK GLES_CHK
+#define EXT_CALL(f) f
+#define EXT_ENUM(x) x
+#define MANGLE_NAME(f) f##GLES2
+#elif GL_RT_COMMON_GL
+#define CHECK GL_CHK
+#define EXT_CALL(f) f##EXT
+#define EXT_ENUM(x) x##_EXT
+#define MANGLE_NAME(f) f##GL
+#endif
+
+
+//==============================================================================
+// rt format handling
+//==============================================================================
+
+
+inline GLenum MANGLE_NAME(RTColorTextureFormat)(RenderTextureFormat fmt)
+{
+ switch( fmt )
+ {
+ case kRTFormatARGB32:
+ case kRTFormatARGB4444:
+ case kRTFormatARGB1555:
+ case kRTFormatARGBHalf:
+ case kRTFormatARGBFloat:
+#if GL_RT_COMMON_GLES || GL_RT_COMMON_GLES2
+ return GL_RGBA;
+#else
+ return GL_BGRA;
+#endif
+
+ case kRTFormatRGB565:
+ return GL_RGB;
+
+ case kRTFormatR8:
+ case kRTFormatRHalf:
+ case kRTFormatRFloat:
+#if GL_RT_COMMON_GLES || GL_RT_COMMON_GLES2
+ return 0x1903; // GL_RED_EXT
+#else
+ return GL_RED;
+#endif
+
+ case kRTFormatRGHalf:
+ case kRTFormatRGFloat:
+#if GL_RT_COMMON_GLES || GL_RT_COMMON_GLES2
+ return 0x8227; // GL_RG_EXT
+#else
+ return 0x8227; // GL_RG
+#endif
+
+ // int formats are not supported
+ case kRTFormatARGBInt:
+ case kRTFormatRInt:
+ case kRTFormatRGInt:
+ break;
+
+ default:
+ break;
+ }
+
+ Assert( false && "wrong color rt format" );
+ return 0;
+}
+
+
+inline GLenum MANGLE_NAME(RTColorInternalFormat)(RenderTextureFormat fmt)
+{
+ switch( fmt )
+ {
+#if GL_RT_COMMON_GLES || GL_RT_COMMON_GLES2
+ case kRTFormatARGB32: return GL_RGBA;
+ case kRTFormatARGB4444: return GL_RGBA;
+ case kRTFormatARGB1555: return GL_RGBA;
+ case kRTFormatRGB565: return GL_RGB;
+ case kRTFormatARGBHalf: return GL_RGBA;
+ case kRTFormatARGBFloat: return GL_RGBA;
+ case kRTFormatR8: return 0x1903; // GL_RED_EXT
+ case kRTFormatRHalf: return 0x1903; // GL_RED_EXT
+ case kRTFormatRFloat: return 0x1903; // GL_RED_EXT
+ case kRTFormatRGHalf: return 0x8227; // GL_RG_EXT
+ case kRTFormatRGFloat: return 0x8227; // GL_RG_EXT
+#else
+ case kRTFormatARGB32: return GL_RGBA;
+ case kRTFormatARGB4444: return GL_RGBA4;
+ case kRTFormatARGB1555: return GL_RGB5_A1;
+ case kRTFormatRGB565: return GL_RGB5;
+ case kRTFormatARGBHalf: return GL_RGBA16F_ARB;
+ case kRTFormatARGBFloat: return GL_RGBA32F_ARB;
+ case kRTFormatR8: return 0x8229; // GL_R8
+ case kRTFormatRHalf: return 0x822D; // GL_R16F
+ case kRTFormatRFloat: return 0x822E; // GL_R32F
+ case kRTFormatRGHalf: return 0x822F; // GL_RG16F
+ case kRTFormatRGFloat: return 0x8230; // GL_RG32F
+#endif
+
+ case kRTFormatARGBInt: break;
+ case kRTFormatRInt: break;
+ case kRTFormatRGInt: break;
+
+ default:
+ break;
+ }
+
+ Assert( false && "wrong color rt format" );
+ return 0;
+}
+
+inline GLenum MANGLE_NAME(RBColorInternalFormat)(RenderTextureFormat fmt)
+{
+#if GL_RT_COMMON_GLES || GL_RT_COMMON_GLES2
+ switch( fmt )
+ {
+ case kRTFormatARGB32: return GL_RGBA8_OES;
+ case kRTFormatRGB565: return EXT_ENUM(GL_RGB565);
+ default: break;
+ }
+
+ Assert(false && "wrong color rb format");
+ return 0;
+#else
+ return RTColorTextureFormatGL(fmt);
+#endif
+}
+
+
+inline GLenum MANGLE_NAME(RTColorTextureFormatSRGB)(RenderTextureFormat fmt)
+{
+#if GL_RT_COMMON_GLES || GL_RT_COMMON_GLES2
+ switch( fmt )
+ {
+ case kRTFormatARGB32: return 0x8C42; // GL_SRGB_ALPHA_EXT
+ default: break;
+ }
+ return MANGLE_NAME(RTColorTextureFormat)(fmt);
+#else
+ return RTColorTextureFormatGL(fmt);
+#endif
+}
+
+
+inline GLenum MANGLE_NAME(RTColorInternalFormatSRGB)(RenderTextureFormat fmt)
+{
+#if GL_RT_COMMON_GLES || GL_RT_COMMON_GLES2
+ switch( fmt )
+ {
+ case kRTFormatARGB32: return 0x8C42; // GL_SRGB_ALPHA_EXT
+ default: break;
+ }
+ return MANGLE_NAME(RTColorInternalFormat)(fmt);
+#else
+ switch( fmt )
+ {
+ case kRTFormatARGB32: return GL_SRGB8_ALPHA8_EXT;
+ default: break;
+ }
+ return RTColorInternalFormatGL(fmt);
+#endif
+
+ Assert( false && "wrong color rt format" );
+ return 0;
+}
+
+inline GLenum MANGLE_NAME(RTColorTextureType)(RenderTextureFormat fmt)
+{
+ switch( fmt )
+ {
+ case kRTFormatARGB32: return GL_UNSIGNED_BYTE;
+ case kRTFormatARGB4444: return GL_UNSIGNED_SHORT_4_4_4_4;
+ case kRTFormatARGB1555: return GL_UNSIGNED_SHORT_5_5_5_1;
+ case kRTFormatRGB565: return GL_UNSIGNED_SHORT_5_6_5;
+
+ case kRTFormatR8: return GL_UNSIGNED_BYTE;
+
+ case kRTFormatARGBHalf:
+ case kRTFormatRHalf:
+ case kRTFormatRGHalf:
+#if GL_RT_COMMON_GLES || GL_RT_COMMON_GLES2
+ return 0x8D61; // GL_HALF_FLOAT_OES;
+#else
+ return GL_HALF_FLOAT_ARB;
+#endif
+
+ case kRTFormatARGBFloat:
+ case kRTFormatRFloat:
+ case kRTFormatRGFloat:
+ return GL_FLOAT;
+
+ case kRTFormatARGBInt:
+ case kRTFormatRInt:
+ case kRTFormatRGInt:
+ break;
+
+ default:
+ break;
+ }
+
+ Assert( false && "wrong color rt format" );
+ return 0;
+}
+
+
+//==============================================================================
+// rt format support
+//==============================================================================
+
+
+#if GL_RT_COMMON_GLES
+#define DEPTH_ENUM GL_DEPTH_COMPONENT16_OES
+#elif GL_RT_COMMON_GLES2
+#define DEPTH_ENUM GL_DEPTH_COMPONENT16
+#elif GL_RT_COMMON_GL
+#define DEPTH_ENUM GL_DEPTH_COMPONENT
+#endif
+
+struct MANGLE_NAME(FBColorFormatChecker)
+{
+ GLint oldFB;
+ GLint oldRB;
+
+ GLuint fb;
+ GLuint depth;
+ GLuint color;
+
+ static const int FBExt = 8;
+
+ MANGLE_NAME(FBColorFormatChecker)()
+ {
+ #if UNITY_IPHONE
+ CHECK(glGetIntegerv(EXT_ENUM(GL_FRAMEBUFFER_BINDING), &oldFB));
+ CHECK(glGetIntegerv(EXT_ENUM(GL_RENDERBUFFER_BINDING), &oldRB));
+ #else
+ oldFB = oldRB = 0;
+ #endif
+
+ CHECK(EXT_CALL(glGenFramebuffers)(1, &fb));
+ CHECK(EXT_CALL(glBindFramebuffer)(EXT_ENUM(GL_FRAMEBUFFER), fb));
+
+ CHECK(EXT_CALL(glGenRenderbuffers)(1, &depth));
+ CHECK(EXT_CALL(glBindRenderbuffer)(EXT_ENUM(GL_RENDERBUFFER), depth));
+ CHECK(EXT_CALL(glRenderbufferStorage)(EXT_ENUM(GL_RENDERBUFFER), DEPTH_ENUM, FBExt, FBExt));
+
+ CHECK(glGenTextures(1, &color));
+ }
+
+ ~MANGLE_NAME(FBColorFormatChecker)()
+ {
+ // gives out warning under emu
+ #if !UNITY_GLES_EMU
+ CHECK(EXT_CALL(glFramebufferTexture2D)(EXT_ENUM(GL_FRAMEBUFFER), EXT_ENUM(GL_COLOR_ATTACHMENT0), GL_TEXTURE_2D, 0, 0));
+ CHECK(EXT_CALL(glFramebufferRenderbuffer)(EXT_ENUM(GL_FRAMEBUFFER), EXT_ENUM(GL_DEPTH_ATTACHMENT), EXT_ENUM(GL_RENDERBUFFER), 0));
+ #endif
+
+ CHECK(EXT_CALL(glBindFramebuffer)(EXT_ENUM(GL_FRAMEBUFFER), oldFB));
+ CHECK(EXT_CALL(glDeleteFramebuffers)(1, &fb));
+
+ CHECK(EXT_CALL(glBindRenderbuffer)(EXT_ENUM(GL_RENDERBUFFER), oldRB));
+ CHECK(EXT_CALL(glDeleteRenderbuffers)(1, &depth));
+
+ CHECK(glDeleteTextures(1, &color));
+ }
+
+ bool CheckFormatSupported(GLint internalFormat, GLenum format, GLenum type)
+ {
+ #if !GL_RT_COMMON_GLES2
+ CHECK(glEnable(GL_TEXTURE_2D));
+ #endif
+ CHECK(glBindTexture(GL_TEXTURE_2D, color));
+ CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
+ CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
+ CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+ CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+
+ #if GL_RT_COMMON_GL
+ CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
+ #endif
+
+ CHECK(glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, FBExt, FBExt, 0, format, type, 0));
+
+ CHECK(EXT_CALL(glFramebufferTexture2D)(EXT_ENUM(GL_FRAMEBUFFER), EXT_ENUM(GL_COLOR_ATTACHMENT0), GL_TEXTURE_2D, color, 0));
+ CHECK(EXT_CALL(glFramebufferRenderbuffer)(EXT_ENUM(GL_FRAMEBUFFER), EXT_ENUM(GL_DEPTH_ATTACHMENT), EXT_ENUM(GL_RENDERBUFFER), depth));
+
+ GLenum status = EXT_CALL(glCheckFramebufferStatus)(EXT_ENUM(GL_FRAMEBUFFER));
+
+ CHECK(glBindTexture(GL_TEXTURE_2D, 0));
+
+ return (status == EXT_ENUM(GL_FRAMEBUFFER_COMPLETE));
+ }
+};
+
+
+//==============================================================================
+// fb/rt format query
+//==============================================================================
+
+// for now
+#if GL_RT_COMMON_GLES || GL_RT_COMMON_GLES2
+
+inline RenderTextureFormat MANGLE_NAME(QueryFBColorFormat)()
+{
+ GLint rbits=0, gbits=0, bbits=0, abits=0;
+ CHECK(glGetIntegerv(GL_RED_BITS, &rbits));
+ CHECK(glGetIntegerv(GL_GREEN_BITS, &gbits));
+ CHECK(glGetIntegerv(GL_BLUE_BITS, &bbits));
+ CHECK(glGetIntegerv(GL_ALPHA_BITS, &abits));
+
+ if(rbits==8 && gbits==8 && bbits==8 && abits==8)
+ return kRTFormatARGB32;
+ else if(rbits==4 && gbits==4 && bbits==4 && abits==4)
+ return kRTFormatARGB4444;
+ else if(rbits==5 && gbits==5 && bbits==5 && abits==1)
+ return kRTFormatARGB1555;
+ else if(rbits==5 && gbits==6 && bbits==5 && abits==0)
+ return kRTFormatRGB565;
+
+#if UNITY_ANDROID
+ //we can end up with 32bits without alpha
+ if(rbits==8 && gbits==8 && bbits==8)
+ return kRTFormatARGB32;
+#endif
+
+ return kRTFormatARGB32;
+}
+
+inline DepthBufferFormat MANGLE_NAME(QueryFBDepthFormat)()
+{
+ GLint dbits=0;
+ CHECK(glGetIntegerv(GL_DEPTH_BITS, &dbits));
+
+ if(dbits == 0)
+ return kDepthFormatNone;
+
+ return dbits == 16 ? kDepthFormat16 : kDepthFormat24;
+}
+
+#endif // GL_RT_COMMON_GLES || GL_RT_COMMON_GLES2
+
+#endif // GL_RT_COMMON_H
+
diff --git a/Runtime/GfxDevice/GPUSkinningInfo.h b/Runtime/GfxDevice/GPUSkinningInfo.h
new file mode 100644
index 0000000..7f32a0c
--- /dev/null
+++ b/Runtime/GfxDevice/GPUSkinningInfo.h
@@ -0,0 +1,61 @@
+#ifndef __GPUSKINNINGINFO_H__
+#define __GPUSKINNINGINFO_H__
+
+class VBO;
+class ThreadedStreamBuffer;
+class Matrix4x4f;
+struct BoneInfluence;
+
+/**
+* Abstract class for GPU skinning, implemented in each GfxDevice
+*/
+class GPUSkinningInfo
+{
+protected:
+ //! Number of vertices in the skin
+ UInt32 m_VertexCount;
+ //! Channel map for the VBO
+ UInt32 m_ChannelMap;
+ //! Destination VBO stride
+ int m_Stride;
+
+ //! Destination VBO
+ VBO *m_DestVBO;
+
+ //! Bones per vertex, must be 1, 2 or 4
+ UInt32 m_BonesPerVertex;
+
+ // Protected constructor and destructor, can only be created and deleted from GfxDevice impl.
+ // For threading purposes, constructor should not perform any GL operations (called directly from main thread).
+ GPUSkinningInfo() : m_VertexCount(0), m_ChannelMap(0), m_Stride(0), m_DestVBO(NULL), m_BonesPerVertex(4) {}
+ virtual ~GPUSkinningInfo() {};
+
+public:
+ virtual UInt32 GetVertexCount() const { return m_VertexCount; }
+ virtual UInt32 GetChannelMap() const { return m_ChannelMap; }
+ virtual int GetStride() const { return m_Stride; }
+ virtual VBO * GetDestVBO() const { return m_DestVBO; }
+ virtual UInt32 GetBonesPerVertex() const { return m_BonesPerVertex; }
+
+ /** Update vertex count */
+ virtual void SetVertexCount(UInt32 count) { m_VertexCount = count; }
+
+ /** Update channel map */
+ virtual void SetChannelMap(UInt32 channelmap) { m_ChannelMap = channelmap; }
+
+ /** Update stride of the vertices in bytes (not including skin data). */
+ virtual void SetStride(int stride) { m_Stride = stride; }
+
+ /** Update destination VBO */
+ virtual void SetDestVBO(VBO *vbo) { m_DestVBO = vbo; }
+
+ /** Update bones-per-vertex */
+ virtual void SetBonesPerVertex(UInt32 bones) { m_BonesPerVertex = bones; }
+
+};
+
+
+
+
+
+#endif
diff --git a/Runtime/GfxDevice/GfxDevice.cpp b/Runtime/GfxDevice/GfxDevice.cpp
new file mode 100644
index 0000000..a1837df
--- /dev/null
+++ b/Runtime/GfxDevice/GfxDevice.cpp
@@ -0,0 +1,926 @@
+#include "UnityPrefix.h"
+#include "GfxDevice.h"
+#include "Runtime/Utilities/HashFunctions.h"
+#include "Runtime/Filters/Mesh/MeshSkinning.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Dynamics/PhysicsManager.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Graphics/RenderSurface.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/Threads/AtomicOps.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Misc/Plugins.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "ChannelAssigns.h"
+#include "BatchRendering.h"
+#include "GpuProgram.h"
+#include "Runtime/Geometry/BoundingUtils.h"
+
+#if ENABLE_TEXTUREID_MAP
+ #include "TextureIdMap.h"
+#endif
+
+#if ENABLE_SPRITES
+ #include "Runtime/Graphics/SpriteFrame.h"
+#endif
+
+class VBOList
+{
+public:
+ List<VBO> m_List;
+};
+
+static GfxDevice* gfxDevice = NULL;
+
+#if ENABLE_MULTITHREADED_CODE
+static GfxDevice* realGfxDevice = NULL;
+static Thread::ThreadID realGfxDeviceThreadId;
+static GfxThreadingMode gfxThreadingMode = kGfxThreadingModeDirect;
+#endif
+
+void ApplyTexEnvData (unsigned int texUnit, unsigned int samplerUnit, const TexEnvData& data)
+{
+ GfxDevice& device = GetRealGfxDevice();
+
+ device.SetTexture (kShaderFragment, texUnit, samplerUnit, data.textureID, static_cast<TextureDimension>(data.texDim), data.mipBias);
+ // Only setup texture matrix & transform for texture units that fit into supported
+ // coordinate count. Shaders can use more textures,
+ // but then they can't have T&L matrices nor fixed function texgen.
+ if (texUnit < kMaxSupportedTextureCoords)
+ {
+ device.SetTextureTransform (texUnit, static_cast<TextureDimension>(data.texDim), static_cast<TexGenMode>(data.texGen), data.identityMatrix, data.matrix.GetPtr());
+ }
+}
+
+void ClearStaticBatchIndices();
+
+bool IsGfxDevice()
+{
+ return gfxDevice != NULL;
+}
+
+GfxDevice& GetGfxDevice()
+{
+ Assert( gfxDevice );
+#if ENABLE_MULTITHREADED_CODE
+ DebugAssert(realGfxDevice == NULL || Thread::CurrentThreadIsMainThread());
+#endif
+ return *gfxDevice;
+}
+
+GfxDevice& GetUncheckedGfxDevice()
+{
+ return *gfxDevice;
+}
+
+void SetGfxDevice(GfxDevice* device)
+{
+ gfxDevice = device;
+}
+
+void DestroyGfxDevice()
+{
+ if (gfxDevice)
+ {
+ UNITY_DELETE(gfxDevice, kMemGfxDevice);
+ gfxDevice = NULL;
+ }
+}
+
+GfxDevice& GetRealGfxDevice()
+{
+#if ENABLE_MULTITHREADED_CODE
+ if (realGfxDevice)
+ {
+ DebugAssert(Thread::EqualsCurrentThreadIDForAssert(realGfxDeviceThreadId));
+ return *realGfxDevice;
+ }
+#endif
+ return *gfxDevice;
+}
+
+bool IsRealGfxDeviceThreadOwner()
+{
+#if ENABLE_MULTITHREADED_CODE
+ if (realGfxDevice)
+ return Thread::EqualsCurrentThreadIDForAssert(realGfxDeviceThreadId);
+#endif
+ return true;
+}
+
+#if ENABLE_MULTITHREADED_CODE
+void SetRealGfxDevice(GfxDevice* device)
+{
+ Assert( !realGfxDevice );
+ realGfxDevice = device;
+ SetRealGfxDeviceThreadOwnership();
+}
+
+void SetRealGfxDeviceThreadOwnership()
+{
+ realGfxDeviceThreadId = Thread::GetCurrentThreadID();
+}
+
+void DestroyRealGfxDevice()
+{
+ if (realGfxDevice)
+ {
+ UNITY_DELETE(realGfxDevice, kMemGfxThread);
+ realGfxDevice = NULL;
+ }
+}
+
+void SetGfxThreadingMode(GfxThreadingMode mode)
+{
+ gfxThreadingMode = mode;
+}
+
+GfxThreadingMode GetGfxThreadingMode()
+{
+ return gfxThreadingMode;
+}
+#endif
+
+#if GFX_DEVICE_VIRTUAL
+GfxDevice::GfxDevice()
+{
+ OnCreate();
+}
+
+GfxDevice::~GfxDevice()
+{
+ OnDelete();
+}
+#endif
+
+void GfxDevice::OnCreate()
+{
+ m_Stats.ResetFrame();
+ m_SavedStats.ResetFrame();
+ m_ActiveRenderTexture = NULL;
+ m_InsideFrame = false;
+ m_IsRecording = false;
+ m_IsThreadable = false;
+ m_FramebufferDepthFormat = kDepthFormatNone;
+ for (int i = 0; i < kShaderTypeCount; ++i)
+ m_BuiltinParamIndices[i] = &m_NullParamIndices;
+ m_VBOList = new VBOList;
+
+#if ENABLE_TEXTUREID_MAP
+ TextureIdMap::Initialize();
+#endif
+}
+
+void GfxDevice::OnDelete()
+{
+ delete m_VBOList;
+
+#if ENABLE_TEXTUREID_MAP
+ TextureIdMap::Uninitialize();
+#endif
+
+ ClearStaticBatchIndices();
+}
+
+void GfxDevice::OnCreateVBO(VBO* vbo)
+{
+ SET_ALLOC_OWNER(this);
+ m_VBOList->m_List.push_back(*vbo);
+}
+
+void GfxDevice::OnDeleteVBO(VBO* vbo)
+{
+ m_VBOList->m_List.erase(vbo);
+}
+
+int GfxDevice::GetTotalVBOCount() const
+{
+ int count = 0;
+ List<VBO>::iterator itr, end = m_VBOList->m_List.end();
+ for (itr = m_VBOList->m_List.begin(); itr != end; ++itr)
+ {
+ if (!itr->GetHideFromRuntimeStats())
+ ++count;
+ }
+ return count;
+}
+
+int GfxDevice::GetTotalVBOBytes() const
+{
+ int size = 0;
+ List<VBO>::iterator itr, end = m_VBOList->m_List.end();
+ for (itr = m_VBOList->m_List.begin(); itr != end; ++itr)
+ {
+ if (!itr->GetHideFromRuntimeStats())
+ size += itr->GetRuntimeMemorySize();
+ }
+ return size;
+}
+
+void GfxDevice::RecreateAllVBOs()
+{
+ List<VBO>::iterator itr, end = m_VBOList->m_List.end();
+ for (itr = m_VBOList->m_List.begin() ; itr != end; ++itr)
+ {
+ itr->Recreate();
+ }
+ GetDynamicVBO().Recreate();
+}
+
+#if GFX_SUPPORTS_D3D9
+void GfxDevice::ResetDynamicVBs()
+{
+ List<VBO>::iterator itr, end = m_VBOList->m_List.end();
+ for (itr = m_VBOList->m_List.begin() ; itr != end; ++itr)
+ {
+ itr->ResetDynamicVB();
+ }
+}
+#endif
+
+#if GFX_SUPPORTS_OPENGLES20
+void GfxDevice::MarkAllVBOsLost()
+{
+ for(List<VBO>::iterator itr = m_VBOList->m_List.begin(), end = m_VBOList->m_List.end() ; itr != end; ++itr)
+ itr->MarkBuffersLost();
+}
+#endif
+
+void GfxDevice::SetWorldMatrixAndType( const float matrix[16], TransformType type )
+{
+ SetWorldMatrix(matrix);
+ bool backface = (type & kOddNegativeScaleTransform) != 0;
+ int normalization = (type & kUniformScaleTransform) ? kNormalizationScale : 0;
+ normalization |= (type & kNonUniformScaleTransform) ? kNormalizationFull : 0;
+ DebugAssert(normalization != (kNormalizationScale|kNormalizationFull));
+ SetNormalizationBackface(NormalizationMode(normalization), backface);
+}
+
+void GfxDevice::SetInverseScale (float invScale)
+{
+ m_BuiltinParamValues.SetInstanceVectorParam(kShaderInstanceVecScale, Vector4f(0,0,0, invScale));
+}
+
+GpuProgram* GfxDevice::CreateGpuProgram( const std::string& source, CreateGpuProgramOutput& output )
+{
+ return ::CreateGpuProgram( source, output );
+}
+
+void GfxDevice::RecordSetBlendState(const DeviceBlendState* state, const ShaderLab::FloatVal& alphaRef, const ShaderLab::PropertySheet* props )
+{
+ ErrorString("GfxDevice does not support recording");
+}
+
+void GfxDevice::RecordSetMaterial( const ShaderLab::VectorVal& ambient, const ShaderLab::VectorVal& diffuse, const ShaderLab::VectorVal& specular, const ShaderLab::VectorVal& emissive, const ShaderLab::FloatVal& shininess, const ShaderLab::PropertySheet* props )
+{
+ ErrorString("GfxDevice does not support recording");
+}
+
+void GfxDevice::RecordSetColor( const ShaderLab::VectorVal& color, const ShaderLab::PropertySheet* props )
+{
+ ErrorString("GfxDevice does not support recording");
+}
+
+void GfxDevice::RecordEnableFog( FogMode fogMode, const ShaderLab::FloatVal& fogStart, const ShaderLab::FloatVal& fogEnd, const ShaderLab::FloatVal& fogDensity, const ShaderLab::VectorVal& fogColor, const ShaderLab::PropertySheet* props )
+{
+ ErrorString("GfxDevice does not support recording");
+}
+
+void GfxDevice::SetMaterialProperties(const MaterialPropertyBlock& block)
+{
+ m_MaterialProperties = block;
+}
+
+
+
+struct SkinMeshTask
+{
+ SkinMeshInfo info;
+ VBO* vbo;
+ void* vboMemory;
+};
+
+#if ENABLE_MULTITHREADED_SKINNING
+static JobScheduler::JobGroupID s_SkinJobGroup;
+#endif
+static dynamic_array<SkinMeshTask> s_ActiveSkins;
+static bool s_InsideSkinning = false;
+
+void EndSkinTask(SkinMeshTask& task)
+{
+ if (task.vbo)
+ task.vbo->UnmapVertexStream(0);
+ task.info.Release();
+}
+
+void GfxDevice::BeginSkinning( int maxSkinCount )
+{
+ Assert(!s_InsideSkinning);
+#if ENABLE_MULTITHREADED_SKINNING
+ s_SkinJobGroup = GetJobScheduler().BeginGroup(maxSkinCount);
+#endif
+ s_ActiveSkins.reserve(maxSkinCount);
+ s_InsideSkinning = true;
+}
+
+bool GfxDevice::SkinMesh( const SkinMeshInfo& skin, VBO* vbo )
+{
+ Assert(s_InsideSkinning);
+ Assert((vbo == NULL) != (skin.outVertices == NULL));
+ VertexStreamData mappedVSD;
+ if (vbo && !vbo->MapVertexStream(mappedVSD, 0))
+ {
+ // Bail out before we push to active skins
+ skin.Release();
+ return false;
+ }
+
+ // Array must be preallocated to at least the right size
+ Assert(s_ActiveSkins.size() < s_ActiveSkins.capacity());
+ int skinIndex = s_ActiveSkins.size();
+ s_ActiveSkins.resize_uninitialized(skinIndex + 1);
+ SkinMeshTask& task = s_ActiveSkins[skinIndex];
+ task.info = skin;
+ task.vbo = vbo;
+
+ // Caller passes in a buffer if it wants to read it back
+ // Otherwise skin directly to VBO memory
+ if (vbo)
+ task.info.outVertices = mappedVSD.buffer;
+
+#if ENABLE_MULTITHREADED_SKINNING
+ GetJobScheduler().SubmitJob(s_SkinJobGroup, DeformSkinnedMeshJob, &task.info, NULL);
+#else
+ DeformSkinnedMesh(task.info);
+ EndSkinTask(task);
+#endif
+ return true;
+}
+
+void GfxDevice::EndSkinning()
+{
+ Assert(s_InsideSkinning);
+#if ENABLE_MULTITHREADED_SKINNING
+ GetJobScheduler().WaitForGroup(s_SkinJobGroup);
+ for (int i = 0; i < s_ActiveSkins.size(); i++)
+ EndSkinTask(s_ActiveSkins[i]);
+#endif
+ s_ActiveSkins.resize_uninitialized(0);
+ s_InsideSkinning = false;
+}
+
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+struct StaticBatch
+{
+#if GFX_SUPPORTS_OPENGLES20 && GFX_OPENGLESxx_ONLY
+ enum { kMaxIndexCount = 16384 * 3 };
+#else // everything else
+ // Anything over 32k causes a slowdown on MBPs with AMD cards (Case 394520)
+ enum { kMaxIndexCount = 32000 };
+#endif
+ ~StaticBatch() { UNITY_FREE( kMemBatchedGeometry, indices); }
+ bool isActive;
+ ABSOLUTE_TIME startTime;
+ ChannelAssigns channels;
+ size_t indexCount;
+ size_t vertexCount;
+ size_t meshCount;
+ GfxPrimitiveType topology;
+ size_t vertexRangeBegin;
+ size_t vertexRangeEnd;
+ UInt16* indices;
+} s_StaticBatch;
+
+void ClearStaticBatchIndices()
+{
+ UNITY_FREE(kMemBatchedGeometry,s_StaticBatch.indices);
+ s_StaticBatch.indices = NULL;
+}
+
+int GfxDevice::GetMaxStaticBatchIndices()
+{
+ return StaticBatch::kMaxIndexCount;
+}
+
+void GfxDevice::BeginStaticBatching (const ChannelAssigns& channels, GfxPrimitiveType topology)
+{
+ Assert(!s_StaticBatch.isActive);
+ Assert(topology==kPrimitiveTriangles || topology==kPrimitiveTriangleStripDeprecated);
+
+ StaticBatch& batch = s_StaticBatch;
+ if (!batch.indices)
+ {
+ const size_t ibSize = StaticBatch::kMaxIndexCount * kVBOIndexSize;
+ batch.indices = reinterpret_cast<UInt16*>(UNITY_MALLOC_ALIGNED( kMemBatchedGeometry, ibSize, 32));
+ }
+ batch.startTime = START_TIME;
+ batch.channels = channels;
+ batch.indexCount = 0;
+ batch.vertexCount = 0;
+ batch.meshCount = 0;
+ batch.topology = topology;
+ batch.vertexRangeBegin = std::numeric_limits<size_t>::max();
+ batch.vertexRangeEnd = 0;
+ batch.isActive = true;
+}
+
+void GfxDevice::StaticBatchMesh( UInt32 firstVertex, UInt32 vertexCount, const IndexBufferData& indices, UInt32 firstIndexByte, UInt32 indexCount )
+{
+ Assert(s_StaticBatch.isActive);
+
+ StaticBatch& batch = s_StaticBatch;
+ batch.vertexCount += vertexCount;
+ batch.vertexRangeBegin = std::min<size_t>(batch.vertexRangeBegin, firstVertex);
+ batch.vertexRangeEnd = std::max<size_t>(batch.vertexRangeEnd, firstVertex + vertexCount);
+ const UInt16* srcIndices = reinterpret_cast<const UInt16*>(static_cast<const UInt8*>(indices.indices) + firstIndexByte);
+ AppendMeshIndices(batch.indices, batch.indexCount, srcIndices, indexCount, batch.topology==kPrimitiveTriangleStripDeprecated);
+ batch.meshCount++;
+}
+
+void GfxDevice::EndStaticBatching( VBO& vbo, const Matrix4x4f& matrix, TransformType transformType, int sourceChannels )
+{
+ Assert(s_StaticBatch.isActive);
+
+ SetWorldMatrixAndType(matrix.GetPtr(), transformType);
+ const StaticBatch& batch = s_StaticBatch;
+ vbo.DrawCustomIndexed(batch.channels, batch.indices, batch.indexCount, batch.topology,
+ batch.vertexRangeBegin, batch.vertexRangeEnd, batch.vertexCount);
+
+ ABSOLUTE_TIME elapsedTime = ELAPSED_TIME(batch.startTime);
+ int primCount = GetPrimitiveCount(batch.indexCount, batch.topology, false);
+ GetFrameStats().AddBatch(primCount, batch.vertexCount, batch.meshCount, elapsedTime);
+ s_StaticBatch.isActive = false;
+}
+
+struct DynamicBatch
+{
+ bool isActive;
+ ABSOLUTE_TIME startTime;
+ ChannelAssigns shaderChannels;
+ UInt32 availableChannels;
+ size_t maxVertices;
+ size_t maxIndices;
+ size_t vertexCount;
+ size_t indexCount;
+ size_t meshCount;
+ GfxPrimitiveType topology;
+ size_t destStride;
+ UInt8* outVertices;
+ UInt16* outIndices;
+} s_DynamicBatch;
+
+void GfxDevice::BeginDynamicBatching( const ChannelAssigns& shaderChannels, UInt32 availableChannels, size_t maxVertices, size_t maxIndices, GfxPrimitiveType topology)
+{
+ Assert(!s_DynamicBatch.isActive);
+ Assert(topology != kPrimitiveLineStrip);
+
+ DynamicBatch& batch = s_DynamicBatch;
+ batch.startTime = START_TIME;
+ batch.shaderChannels = shaderChannels;
+ batch.availableChannels = availableChannels;
+ batch.maxVertices = maxVertices;
+ batch.maxIndices = (topology == kPrimitiveQuads) ? maxIndices/4*6 : maxIndices;
+ batch.vertexCount = 0;
+ batch.indexCount = 0;
+ batch.meshCount = 0;
+ batch.topology = topology;
+
+ batch.destStride = 0;
+ for( int i = 0; i < kShaderChannelCount; ++i )
+ if( availableChannels & (1<<i) )
+ batch.destStride += VBO::GetDefaultChannelByteSize(i);
+
+ DynamicVBO::RenderMode renderMode;
+ switch (topology)
+ {
+ case kPrimitiveTriangleStripDeprecated:
+ renderMode = DynamicVBO::kDrawIndexedTriangleStrip;
+ break;
+ case kPrimitiveLines:
+ renderMode = DynamicVBO::kDrawIndexedLines;
+ break;
+ case kPrimitiveQuads:
+ renderMode = DynamicVBO::kDrawIndexedQuads;
+ break;
+ case kPrimitivePoints:
+ renderMode = DynamicVBO::kDrawIndexedPoints;
+ break;
+ default:
+ renderMode = DynamicVBO::kDrawIndexedTriangles;
+ break;
+ }
+
+ // Get VBO chunk
+ batch.isActive = GetDynamicVBO().GetChunk(
+ availableChannels, maxVertices, batch.maxIndices, renderMode,
+ (void**)&batch.outVertices, (void**)&batch.outIndices);
+}
+
+void GfxDevice::DynamicBatchMesh( const Matrix4x4f& matrix, const VertexBufferData& vertices, UInt32 firstVertex, UInt32 vertexCount, const IndexBufferData& indices, UInt32 firstIndexByte, UInt32 indexCount )
+{
+ Assert(s_DynamicBatch.isActive);
+ DynamicBatch& batch = s_DynamicBatch;
+ size_t outIndexCount;
+
+ // convert quad indices to triangle indices
+ if (batch.topology == kPrimitiveQuads)
+ {
+ int quadIndexCount = indexCount/4*6;
+ UInt16* quadIB = ALLOC_TEMP_MANUAL (UInt16, quadIndexCount);
+ UInt16* src = (UInt16*)((UInt8*)indices.indices + firstIndexByte);
+ Prefetch(src, indexCount * kVBOIndexSize);
+ for (int i = 0; i < indexCount/4; ++i)
+ {
+ quadIB[6*i+0] = src[0];
+ quadIB[6*i+1] = src[1];
+ quadIB[6*i+2] = src[2];
+ quadIB[6*i+3] = src[0];
+ quadIB[6*i+4] = src[2];
+ quadIB[6*i+5] = src[3];
+ src += 4;
+ }
+ outIndexCount = TransformIndices(batch.outIndices, quadIB, 0, quadIndexCount, firstVertex, batch.vertexCount, false);
+ FREE_TEMP_MANUAL(quadIB);
+ }
+ else
+ outIndexCount = TransformIndices(batch.outIndices, indices.indices, firstIndexByte, indexCount, firstVertex, batch.vertexCount, batch.topology==kPrimitiveTriangleStripDeprecated);
+
+ size_t outVertexCount = TransformVertices(batch.outVertices, matrix, vertices, firstVertex, vertexCount, batch.availableChannels);
+
+ batch.outIndices += outIndexCount;
+ batch.outVertices += outVertexCount * batch.destStride;
+ batch.indexCount += outIndexCount;
+ batch.vertexCount += outVertexCount;
+ batch.meshCount++;
+}
+
+void GfxDevice::EndDynamicBatching( TransformType transformType )
+{
+ Assert(s_DynamicBatch.isActive);
+
+ const DynamicBatch& batch = s_DynamicBatch;
+ Assert(batch.vertexCount <= batch.maxVertices);
+ Assert(batch.indexCount <= batch.maxIndices);
+
+ // Release VBO chunk
+ GetDynamicVBO().ReleaseChunk(batch.vertexCount, batch.indexCount);
+
+ SetWorldMatrixAndType(Matrix4x4f::identity.GetPtr(), transformType);
+
+ GetDynamicVBO().DrawChunk(batch.shaderChannels);
+ ABSOLUTE_TIME elapsedTime = ELAPSED_TIME(batch.startTime);
+ int primCount = GetPrimitiveCount(batch.indexCount, batch.topology, false);
+ GetFrameStats().AddBatch(primCount, batch.vertexCount, batch.meshCount, elapsedTime);
+ s_DynamicBatch.isActive = false;
+}
+#if ENABLE_SPRITES
+void GfxDevice::DynamicBatchSprite(const Matrix4x4f* matrix, const SpriteRenderData* rd, ColorRGBA32 color)
+{
+ Assert(s_DynamicBatch.isActive);
+ DynamicBatch& batch = s_DynamicBatch;
+
+ TransformSprite (batch.outVertices, batch.outIndices, matrix, rd, color, batch.vertexCount);
+ int outIndexCount = (int)rd->indices.size();
+ int outVertexCount = (int)rd->vertices.size();
+ batch.outIndices += outIndexCount;
+ batch.outVertices += outVertexCount * batch.destStride;
+ batch.indexCount += outIndexCount;
+ batch.vertexCount += outVertexCount;
+ batch.meshCount++;
+}
+#endif
+#else
+void ClearStaticBatchIndices(){}
+#endif //GFX_ENABLE_DRAW_CALL_BATCHING
+
+void GfxDevice::AddBatchingStats( int batchedTris, int batchedVerts, int batchedCalls )
+{
+ ABSOLUTE_TIME unusedTime;
+ ABSOLUTE_TIME_INIT(unusedTime);
+ GetFrameStats().AddBatch(batchedTris, batchedVerts, batchedCalls, unusedTime);
+}
+
+// on gl/gles we create textures at the very beginning with glGenTextures
+// so start generate ids from smth not 0
+volatile int GfxDevice::ms_TextureIDGenerator = 10;
+volatile int GfxDevice::ms_ComputeBufferIDGenerator = 0;
+
+#if !UNITY_WII // Wii also needs to register the ID, so separate impl
+TextureID GfxDevice::CreateTextureID ()
+{
+ return TextureID(AtomicIncrement(&ms_TextureIDGenerator));
+}
+#endif
+
+void GfxDevice::FreeTextureID( TextureID texture )
+{
+}
+
+ComputeBufferID GfxDevice::CreateComputeBufferID()
+{
+ return ComputeBufferID(AtomicIncrement (&ms_ComputeBufferIDGenerator));
+}
+
+void GfxDevice::FreeComputeBufferID(ComputeBufferID id)
+{
+ // Do nothing yet
+}
+
+
+void GfxDevice::ResetFrameStats()
+{
+ m_Stats.ResetFrame();
+}
+
+void GfxDevice::BeginFrameStats()
+{
+ m_Stats.BeginFrameStats();
+}
+
+void GfxDevice::EndFrameStats()
+{
+ m_Stats.EndClientFrameStats();
+}
+
+void GfxDevice::SaveDrawStats()
+{
+ m_SavedStats.CopyAllDrawStats(m_Stats);
+ m_SavedStats.CopyClientStats(m_Stats);
+}
+
+void GfxDevice::RestoreDrawStats()
+{
+ m_Stats.CopyAllDrawStats(m_SavedStats);
+ m_Stats.CopyClientStats(m_SavedStats);
+}
+
+void GfxDevice::SynchronizeStats()
+{
+}
+
+#if UNITY_EDITOR
+void GfxDevice::SetColorBytes (const UInt8 color[4])
+{
+ float colorFloat[4];
+ colorFloat[0] = ByteToNormalized(color[0]);
+ colorFloat[1] = ByteToNormalized(color[1]);
+ colorFloat[2] = ByteToNormalized(color[2]);
+ colorFloat[3] = ByteToNormalized(color[3]);
+ SetColor (colorFloat);
+}
+#endif
+
+
+void GfxThreadableDevice::SetShadersMainThread (ShaderLab::SubProgram* programs[kShaderTypeCount], const ShaderLab::PropertySheet* props)
+{
+ ErrorString("Don't call SetShadersMainThread on threadable device! Use GraphicsHelper instead");
+}
+
+
+
+void GfxDevice::CommonReloadResources(UInt32 flags)
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS_WORKER
+ if (flags & kReloadTextures)
+ Texture::ReloadAll();
+
+ if (flags & kReloadShaders)
+ Shader::ReloadAllShaders();
+
+ if (flags & kReleaseRenderTextures)
+ RenderTexture::ReleaseAll();
+#else
+ //todo.
+#endif
+}
+
+
+void CalculateDeviceProjectionMatrix (Matrix4x4f& m, bool usesOpenGLTextureCoords, bool invertY)
+{
+ if (usesOpenGLTextureCoords)
+ return; // nothing to do on OpenGL-like devices
+
+ // Otherwise, the matrix is OpenGL style, and we have to convert it to
+ // D3D-like projection matrix
+
+ if (invertY)
+ {
+ m.Get(1,0) = -m.Get(1,0);
+ m.Get(1,1) = -m.Get(1,1);
+ m.Get(1,2) = -m.Get(1,2);
+ m.Get(1,3) = -m.Get(1,3);
+ }
+
+
+ // Now scale&bias to get Z range from -1..1 to 0..1:
+ // matrix = scaleBias * matrix
+ // 1 0 0 0
+ // 0 1 0 0
+ // 0 0 0.5 0.5
+ // 0 0 0 1
+ m.Get(2,0) = m.Get(2,0) * 0.5f + m.Get(3,0) * 0.5f;
+ m.Get(2,1) = m.Get(2,1) * 0.5f + m.Get(3,1) * 0.5f;
+ m.Get(2,2) = m.Get(2,2) * 0.5f + m.Get(3,2) * 0.5f;
+ m.Get(2,3) = m.Get(2,3) * 0.5f + m.Get(3,3) * 0.5f;
+}
+
+
+
+void GfxDevice::SetupVertexLightParams(int light, const GfxVertexLight& data)
+{
+ DebugAssert(light >= 0 && light < kMaxSupportedVertexLights);
+
+ const Matrix4x4f& viewMat = m_BuiltinParamValues.GetMatrixParam(kShaderMatView);
+
+ Vector4f& position = m_BuiltinParamValues.GetWritableVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Position + light));
+ Vector4f& spotDirection = m_BuiltinParamValues.GetWritableVectorParam(BuiltinShaderVectorParam(kShaderVecLight0SpotDirection + light));
+ Vector4f& atten = m_BuiltinParamValues.GetWritableVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Atten + light));
+
+ // color
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Diffuse + light), data.color);
+
+ // position
+ if (data.type == kLightDirectional)
+ {
+ Vector3f p = viewMat.MultiplyVector3 ((const Vector3f&)data.position);
+ position.Set(-p.x, -p.y, -p.z, 0.0f);
+ }
+ else
+ {
+ Vector3f p = viewMat.MultiplyPoint3 ((const Vector3f&)data.position);
+ position.Set(p.x, p.y, p.z, 1.0f);
+ }
+
+ // attenuation set in a way where distance attenuation can be computed:
+ // float lengthSq = dot(toLight, toLight);
+ // float atten = 1.0 / (1.0 + lengthSq * unity_LightAtten[i].z);
+ // and spot cone attenuation:
+ // float rho = max (0, dot(normalize(toLight), unity_SpotDirection[i].xyz));
+ // float spotAtt = (rho - unity_LightAtten[i].x) * unity_LightAtten[i].y;
+ // spotAtt = saturate(spotAtt);
+ // and the above works for all light types, i.e. spot light code works out
+ // to correct math for point & directional lights as well.
+
+ const float rangeSq = data.range * data.range;
+
+ // spot direction & attenuation
+ if (data.spotAngle > 0.0f)
+ {
+ // spot light
+ Vector3f d = viewMat.MultiplyVector3((const Vector3f&)data.spotDirection);
+ spotDirection.Set(-d.x, -d.y, -d.z, 0.0f);
+
+ const float radAngle = Deg2Rad(data.spotAngle);
+ const float cosTheta = cosf(radAngle*0.25f);
+ const float cosPhi = cosf(radAngle*0.5f);
+ const float cosDiff = cosTheta - cosPhi;
+ atten.Set(cosPhi, (cosDiff != 0.0f) ? 1.0f / cosDiff : 1.0f, data.quadAtten, rangeSq);
+ }
+ else
+ {
+ // non-spot light
+ spotDirection.Set(0.0f, 0.0f, 1.0f, 0.0f);
+ atten.Set(-1.0f, 1.0f, data.quadAtten, rangeSq);
+ }
+}
+
+
+
+#if UNITY_EDITOR
+VertexComponent kSuitableVertexComponentForChannel[kShaderChannelCount] = {
+ kVertexCompVertex,
+ kVertexCompNormal,
+ kVertexCompColor,
+ kVertexCompTexCoord0,
+ kVertexCompTexCoord1,
+ kVertexCompTexCoord2,
+};
+#endif
+
+static const float kDodecahedron[20][3] = {
+ { 0.607f, 0.000f, 0.795f },
+ { 0.188f, 0.577f, 0.795f },
+ { -0.491f, 0.357f, 0.795f },
+ { -0.491f, -0.357f, 0.795f },
+ { 0.188f, -0.577f, 0.795f },
+ { 0.982f, 0.000f, 0.188f },
+ { 0.304f, 0.934f, 0.188f },
+ { -0.795f, 0.577f, 0.188f },
+ { -0.795f, -0.577f, 0.188f },
+ { 0.304f, -0.934f, 0.188f },
+ { 0.795f, 0.577f, -0.188f },
+ { -0.304f, 0.934f, -0.188f },
+ { -0.982f, 0.000f, -0.188f },
+ { -0.304f, -0.934f, -0.188f },
+ { 0.795f, -0.577f, -0.188f },
+ { 0.491f, 0.357f, -0.795f },
+ { -0.188f, 0.577f, -0.795f },
+ { -0.607f, 0.000f, -0.795f },
+ { -0.188f, -0.577f, -0.795f },
+ { 0.491f, -0.357f, -0.795f },
+};
+
+#define DODECAHEDRON_TRIANGLE(x,y,z,a,b,c,s) \
+ ImmediateVertex(x + kDodecahedron[a][0] * s, y + kDodecahedron[a][1] * s, z + kDodecahedron[a][2] * s); \
+ ImmediateVertex(x + kDodecahedron[b][0] * s, y + kDodecahedron[b][1] * s, z + kDodecahedron[b][2] * s); \
+ ImmediateVertex(x + kDodecahedron[c][0] * s, y + kDodecahedron[c][1] * s, z + kDodecahedron[c][2] * s);
+
+#define DODECAHEDRON_FACE(x,y,z,a,b,c,d,e,s) \
+ DODECAHEDRON_TRIANGLE(x,y,z,a,b,c,s) \
+ DODECAHEDRON_TRIANGLE(x,y,z,a,c,d,s) \
+ DODECAHEDRON_TRIANGLE(x,y,z,a,d,e,s)
+
+void GfxDevice::ImmediateShape( float x, float y, float z, float scale, ImmediateShapeType shape )
+{
+ switch (shape)
+ {
+ case kShapeCube:
+ ImmediateBegin(kPrimitiveQuads);
+ ImmediateNormal(0, 0, 0);
+ // -z
+ ImmediateVertex (x+scale, y-scale, z-scale);
+ ImmediateVertex (x-scale, y-scale, z-scale);
+ ImmediateVertex (x-scale, y+scale, z-scale);
+ ImmediateVertex (x+scale, y+scale, z-scale);
+ // +z
+ ImmediateVertex (x-scale, y-scale, z+scale);
+ ImmediateVertex (x+scale, y-scale, z+scale);
+ ImmediateVertex (x+scale, y+scale, z+scale);
+ ImmediateVertex (x-scale, y+scale, z+scale);
+ // -x
+ ImmediateVertex (x-scale, y+scale, z-scale);
+ ImmediateVertex (x-scale, y-scale, z-scale);
+ ImmediateVertex (x-scale, y-scale, z+scale);
+ ImmediateVertex (x-scale, y+scale, z+scale);
+ // +x
+ ImmediateVertex (x+scale, y-scale, z-scale);
+ ImmediateVertex (x+scale, y+scale, z-scale);
+ ImmediateVertex (x+scale, y+scale, z+scale);
+ ImmediateVertex (x+scale, y-scale, z+scale);
+ // -y
+ ImmediateVertex (x-scale, y-scale, z-scale);
+ ImmediateVertex (x+scale, y-scale, z-scale);
+ ImmediateVertex (x+scale, y-scale, z+scale);
+ ImmediateVertex (x-scale, y-scale, z+scale);
+ // +y
+ ImmediateVertex (x+scale, y+scale, z-scale);
+ ImmediateVertex (x-scale, y+scale, z-scale);
+ ImmediateVertex (x-scale, y+scale, z+scale);
+ ImmediateVertex (x+scale, y+scale, z+scale);
+
+ ImmediateEnd();
+ break;
+
+ case kShapeDodecahedron:
+ // template edge length
+ // a = 0.713644
+ // radius of sphere containing the dodecahedron
+ // r = a / 20 * sqrtf(250 + 110*sqrtf(5))
+ // scale our radius to fit the template
+ // TODO: is this correct? :)
+ scale = scale * 1.258408f;
+
+ ImmediateBegin(kPrimitiveTriangles);
+ ImmediateNormal(0, 0, 0);
+
+ DODECAHEDRON_FACE(x,y,z, 0,1,2,3,4, scale);
+ DODECAHEDRON_FACE(x,y,z, 0,5,10,6,1, scale);
+ DODECAHEDRON_FACE(x,y,z, 1,6,11,7,2, scale);
+ DODECAHEDRON_FACE(x,y,z, 2,7,12,8,3, scale);
+ DODECAHEDRON_FACE(x,y,z, 3,8,13,9,4, scale);
+ DODECAHEDRON_FACE(x,y,z, 4,9,14,5,0, scale);
+ DODECAHEDRON_FACE(x,y,z, 15,16,11,6,10, scale);
+ DODECAHEDRON_FACE(x,y,z, 16,17,12,7,11, scale);
+ DODECAHEDRON_FACE(x,y,z, 17,18,13,8,12, scale);
+ DODECAHEDRON_FACE(x,y,z, 18,19,14,9,13, scale);
+ DODECAHEDRON_FACE(x,y,z, 19,15,10,5,14, scale);
+ DODECAHEDRON_FACE(x,y,z, 15,19,18,17,16, scale);
+
+ ImmediateEnd();
+ break;
+
+ default:
+ FatalErrorString("Unknown ImmediateShape");
+ break;
+ };
+}
+
+#undef DODECAHEDRON_FACE
+#undef DODECAHEDRON_TRIANGLE
+
+UInt32 GfxDevice::GetNativeTextureID(TextureID id)
+{
+#if ENABLE_TEXTUREID_MAP
+ return TextureIdMap::QueryNativeTexture(id);
+#else
+ return id.m_ID;
+#endif
+}
+
+void GfxDevice::InsertCustomMarker (int marker)
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS_WORKER
+ PluginsRenderMarker (marker);
+#endif
+}
diff --git a/Runtime/GfxDevice/GfxDevice.h b/Runtime/GfxDevice/GfxDevice.h
new file mode 100644
index 0000000..07d3906
--- /dev/null
+++ b/Runtime/GfxDevice/GfxDevice.h
@@ -0,0 +1,813 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+#include "GfxDeviceConfigure.h"
+#include "GfxDeviceTypes.h"
+#include "GfxDeviceObjects.h"
+#include "GfxDeviceStats.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Shaders/MaterialProperties.h"
+#include "Runtime/Threads/Thread.h"
+#include "BuiltinShaderParams.h"
+#include "Runtime/Modules/ExportModules.h"
+
+#if UNITY_EDITOR && UNITY_WIN
+#include "GfxDeviceWindow.h"
+#endif
+
+#if ENABLE_TEXTUREID_MAP
+ #include "TextureIdMap.h"
+#endif
+
+
+// On some platforms we choose renderer at runtime; on others there's always a single
+// renderer. On those that have only one, GFX_DEVICE_VIRTUAL is defined to zero, and
+// the actual implementation is named GfxDevice, and uses no virtual functions.
+
+#if UNITY_PS3
+//# define CELL_GCM_DEBUG
+# include <cell/gcm.h>
+#endif
+
+#if UNITY_WIN || UNITY_OSX || UNITY_LINUX || UNITY_PS3 || UNITY_IPHONE || UNITY_ANDROID || UNITY_PEPPER || UNITY_XENON || UNITY_BB10 || UNITY_WEBGL || UNITY_TIZEN
+#define GFX_DEVICE_VIRTUAL 1
+#else
+#define GFX_DEVICE_VIRTUAL 0
+#endif
+
+#if (UNITY_OSX && WEBPLUG) || UNITY_LINUX && !UNITY_PEPPER
+#define GFX_USES_VIEWPORT_OFFSET 1
+#else
+#define GFX_USES_VIEWPORT_OFFSET 0
+#endif
+
+
+
+#if GFX_DEVICE_VIRTUAL
+
+#define GFX_API virtual
+#define GFX_PURE = 0
+#define GFX_GL_IMPL GfxDeviceGL
+
+#else
+
+#define GFX_API
+#define GFX_PURE
+#define GFX_GL_IMPL GfxDevice
+struct GfxDeviceImpl;
+
+#endif
+
+#define GFX_DEVICE_VERIFY_ENABLE (!UNITY_RELEASE)
+
+class VBO;
+class RawVBO;
+class VBOList;
+class DynamicVBO;
+class RenderTexture;
+class ImageReference;
+class Matrix4x4f;
+class GpuProgram;
+class GpuProgramParameters;
+class GfxTimerQuery;
+class GfxDisplayList;
+class MaterialPropertyBlock;
+class ShaderErrors;
+class ChannelAssigns;
+class CreateGpuProgramOutput;
+struct SkinMeshInfo;
+struct MemExportInfo;
+struct VertexBufferData;
+struct IndexBufferData;
+struct PropertyNamesSet;
+#if ENABLE_SPRITES
+struct SpriteRenderData;
+#endif
+struct BoneInfluence;
+namespace ShaderLab {
+ class IntShader;
+ struct ParserShader;
+ struct TextureBinding;
+ class PropertySheet;
+ class SubProgram;
+}
+namespace xenon {
+ class IVideoPlayer;
+}
+
+class GPUSkinningInfo;
+
+class GfxDevice {
+public:
+
+ enum PresentMode
+ {
+ kPresentBeforeUpdate,
+ kPresentAfterDraw
+ };
+
+ enum SurfaceFlags
+ {
+ kSurfaceDefault = 0,
+
+ // Bits 0 and 1 are used to control render target restores. There flags are mutually exclusive.
+ kSurfaceNeverRestore = (1<<0), // Xbox 360: SetRenderTarget will never restore contents to EDRAM.
+ kSurfaceAlwaysRestore = (1<<1), // Xbox 360: SetRenderTarget will always restore contents to EDRAM.
+ kSurfaceRestoreMask = kSurfaceNeverRestore | kSurfaceAlwaysRestore,
+ // Xbox 360: SetRenderTarget by default will only restore contents to EDRAM if render target was previously used that frame.
+
+ // next flag (1<<2)
+ };
+
+ enum ReloadResourcesFlags {
+ kReleaseRenderTextures = (1<<0),
+ kReloadShaders = (1<<1),
+ kReloadTextures = (1<<2),
+ };
+
+ enum RenderTargetFlags {
+ kFlagDontRestoreColor = (1<<0), // Xbox 360 specific: do not restore old contents to EDRAM
+ kFlagDontRestoreDepth = (1<<1), // Xbox 360 specific: do not restore old contents to EDRAM
+ kFlagDontRestore = kFlagDontRestoreColor | kFlagDontRestoreDepth,
+ kFlagForceResolve = (1<<3), // Xbox 360 specific: force a resolve to system RAM
+ };
+
+ enum ImmediateShapeType {
+ kShapeCube = 0, // Quads
+ kShapeDodecahedron // Triangles
+ };
+
+ enum GfxProfileControl {
+ kGfxProfBeginFrame = 0,
+ kGfxProfEndFrame,
+ kGfxProfDisableSampling,
+ kGfxProfSetActive,
+ };
+
+ GfxDevice();
+ GFX_API ~GfxDevice();
+
+ GFX_API void InvalidateState() GFX_PURE;
+ #if GFX_DEVICE_VERIFY_ENABLE
+ GFX_API void VerifyState() GFX_PURE;
+ #endif
+
+ GfxDeviceRenderer GetRenderer() const { return m_Renderer; }
+ // OpenGL: texture V coordinate is 0 at the bottom; 1 at the top
+ // otherwise: texture V coordinate is 0 at the top; 1 at the bottom
+ bool UsesOpenGLTextureCoords() const { return m_UsesOpenGLTextureCoords; }
+ // Should half-texel offset be applied for pixel-correct rendering (true on D3D9)?
+ bool UsesHalfTexelOffset() const { return m_UsesHalfTexelOffset; }
+
+ GFX_API void SetMaxBufferedFrames (int bufferSize) { m_MaxBufferedFrames = bufferSize; }
+ int GetMaxBufferedFrames () const { return m_MaxBufferedFrames; }
+
+ const GfxDeviceStats& GetFrameStats() const { return m_Stats; }
+ GfxDeviceStats& GetFrameStats() { return m_Stats; }
+
+ RenderTexture* GetActiveRenderTexture() const
+ {
+#if !UNITY_EDITOR // TODO: this needs fixing in the editor
+ ASSERT_RUNNING_ON_MAIN_THREAD;
+#endif
+ return m_ActiveRenderTexture;
+ }
+ void SetActiveRenderTexture(RenderTexture* rt)
+ {
+#if !UNITY_EDITOR // TODO: this needs fixing in the editor
+ ASSERT_RUNNING_ON_MAIN_THREAD;
+#endif
+ m_ActiveRenderTexture = rt;
+ }
+
+ const BuiltinShaderParamValues& GetBuiltinParamValues() const { return m_BuiltinParamValues; }
+ BuiltinShaderParamValues& GetBuiltinParamValues() { return m_BuiltinParamValues; }
+ const GfxFogParams& GetFogParams() const { return m_FogParams; }
+
+ static inline ColorRGBA32 ConvertToDeviceVertexColor(const ColorRGBA32& color)
+ {
+ #if GFX_OPENGLESxx_ONLY || UNITY_PS3 || UNITY_WII
+ // Optimization: we know that we never have to swizzle vertex color here
+ DebugAssert(gGraphicsCaps.needsToSwizzleVertexColors == false);
+ return color;
+ #elif UNITY_XENON
+ // Optimization: we know that we always have to swizzle vertex color on Xbox360 platform
+ DebugAssert(gGraphicsCaps.needsToSwizzleVertexColors == true);
+ return color.SwizzleToARGB();
+ #else
+ return gGraphicsCaps.needsToSwizzleVertexColors ? color.SwizzleToBGRA() : color;
+ #endif
+ };
+
+ int GetTotalVBOCount() const;
+ int GetTotalVBOBytes() const;
+
+ void RecreateAllVBOs();
+
+ #if GFX_SUPPORTS_D3D9
+ void ResetDynamicVBs();
+ #endif
+ #if GFX_SUPPORTS_OPENGLES20
+ void MarkAllVBOsLost();
+ #endif
+
+
+ GFX_API void Clear (UInt32 clearFlags, const float color[4], float depth, int stencil) GFX_PURE;
+ GFX_API void SetInvertProjectionMatrix( bool enable ) GFX_PURE;
+ GFX_API bool GetInvertProjectionMatrix() const GFX_PURE;
+ #if GFX_USES_VIEWPORT_OFFSET
+ GFX_API void SetViewportOffset( float x, float y ) GFX_PURE;
+ GFX_API void GetViewportOffset( float &x, float &y ) const GFX_PURE;
+ #endif
+
+ GFX_API DeviceBlendState* CreateBlendState(const GfxBlendState& state) GFX_PURE;
+ GFX_API DeviceDepthState* CreateDepthState(const GfxDepthState& state) GFX_PURE;
+ GFX_API DeviceStencilState* CreateStencilState(const GfxStencilState& state) GFX_PURE;
+ GFX_API DeviceRasterState* CreateRasterState(const GfxRasterState& state) GFX_PURE;
+
+ GFX_API void RecordSetBlendState(const DeviceBlendState* state, const ShaderLab::FloatVal& alphaRef, const ShaderLab::PropertySheet* props );
+ GFX_API void SetBlendState(const DeviceBlendState* state, float alphaRef) GFX_PURE;
+ GFX_API void SetRasterState(const DeviceRasterState* state) GFX_PURE;
+ GFX_API void SetDepthState(const DeviceDepthState* state) GFX_PURE;
+ GFX_API void SetStencilState(const DeviceStencilState* state, int stencilRef) GFX_PURE;
+ GFX_API void SetSRGBWrite (const bool) GFX_PURE;
+ GFX_API bool GetSRGBWrite () GFX_PURE;
+
+ GFX_API void SetUserBackfaceMode(bool enable) GFX_PURE;
+ GFX_API void SetWireframe(bool wire) GFX_PURE;
+ GFX_API bool GetWireframe() const GFX_PURE;
+
+ GFX_API void SetWorldMatrixAndType( const float matrix[16], TransformType type );
+ GFX_API void SetWorldMatrix( const float matrix[16] ) GFX_PURE;
+ GFX_API void SetViewMatrix( const float matrix[16] ) GFX_PURE;
+ GFX_API void SetProjectionMatrix (const Matrix4x4f& matrix) GFX_PURE;
+
+ GFX_API void GetMatrix( float outMatrix[16] ) const GFX_PURE;
+
+
+ GFX_API const float* GetWorldMatrix() const GFX_PURE;
+ GFX_API const float* GetViewMatrix() const GFX_PURE;
+ GFX_API const float* GetProjectionMatrix() const GFX_PURE; // get projection matrix as passed from Unity (OpenGL projection conventions)
+ GFX_API const float* GetDeviceProjectionMatrix() const GFX_PURE; // get projection matrix that will be actually used
+
+ GFX_API void SetInverseScale (float invScale);
+
+ GFX_API void SetNormalizationBackface( NormalizationMode mode, bool backface ) GFX_PURE;
+
+ GFX_API void SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial ) GFX_PURE;
+ GFX_API void RecordSetMaterial( const ShaderLab::VectorVal& ambient, const ShaderLab::VectorVal& diffuse, const ShaderLab::VectorVal& specular, const ShaderLab::VectorVal& emissive, const ShaderLab::FloatVal& shininess, const ShaderLab::PropertySheet* props );
+ GFX_API void SetMaterial( const float ambient[4], const float diffuse[4], const float specular[4], const float emissive[4], const float shininess ) GFX_PURE;
+ GFX_API void RecordSetColor( const ShaderLab::VectorVal& color, const ShaderLab::PropertySheet* props );
+ GFX_API void SetColor( const float color[4] ) GFX_PURE;
+ GFX_API void SetViewport( int x, int y, int width, int height ) GFX_PURE;
+ GFX_API void GetViewport( int* values ) const GFX_PURE;
+
+ GFX_API void SetScissorRect( int x, int y, int width, int height ) GFX_PURE;
+ GFX_API void DisableScissor() GFX_PURE;
+ GFX_API bool IsScissorEnabled() const GFX_PURE;
+ GFX_API void GetScissorRect( int values[4] ) const GFX_PURE;
+
+ GFX_API TextureCombinersHandle CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular ) GFX_PURE;
+ GFX_API void DeleteTextureCombiners( TextureCombinersHandle& textureCombiners ) GFX_PURE;
+ GFX_API void SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props ) GFX_PURE;
+
+ GFX_API void SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias) GFX_PURE;
+ GFX_API void SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace ) GFX_PURE;
+ GFX_API void SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16]) GFX_PURE;
+ GFX_API void SetTextureName( TextureID texture, char const* name ) GFX_PURE;
+
+ GFX_API void SetMaterialProperties(const MaterialPropertyBlock& block);
+
+ GFX_API GpuProgram* CreateGpuProgram( const std::string& source, CreateGpuProgramOutput& output );
+ GFX_API void SetShadersMainThread( ShaderLab::SubProgram* programs[kShaderTypeCount], const ShaderLab::PropertySheet* props ) GFX_PURE;
+
+ GFX_API bool IsShaderActive( ShaderType type ) const GFX_PURE;
+ GFX_API void DestroySubProgram( ShaderLab::SubProgram* subprogram ) GFX_PURE;
+ GFX_API void SetConstantBufferInfo (int /*id*/, int /*size*/) { }
+
+ GFX_API void DisableLights( int startLight ) GFX_PURE;
+ GFX_API void SetLight( int light, const GfxVertexLight& data) GFX_PURE;
+ GFX_API void SetAmbient( const float ambient[4] ) GFX_PURE;
+
+ GFX_API void RecordEnableFog( FogMode fogMode, const ShaderLab::FloatVal& fogStart, const ShaderLab::FloatVal& fogEnd, const ShaderLab::FloatVal& fogDensity, const ShaderLab::VectorVal& fogColor, const ShaderLab::PropertySheet* props );
+ GFX_API void EnableFog( const GfxFogParams& fog ) GFX_PURE;
+ GFX_API void DisableFog() GFX_PURE;
+
+ GFX_API VBO* CreateVBO() GFX_PURE;
+ GFX_API void DeleteVBO( VBO* vbo ) GFX_PURE;
+ GFX_API DynamicVBO& GetDynamicVBO() GFX_PURE;
+
+ GFX_API void BeginSkinning( int maxSkinCount );
+ GFX_API bool SkinMesh( const SkinMeshInfo& skin, VBO* vbo );
+ GFX_API void EndSkinning();
+
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+ static int GetMaxStaticBatchIndices();
+ GFX_API void BeginStaticBatching(const ChannelAssigns& channels, GfxPrimitiveType topology);
+ GFX_API void StaticBatchMesh( UInt32 firstVertex, UInt32 vertexCount, const IndexBufferData& indices, UInt32 firstIndexByte, UInt32 indexCount );
+ GFX_API void EndStaticBatching( VBO& vbo, const Matrix4x4f& matrix, TransformType transformType, int sourceChannels );
+
+ GFX_API void BeginDynamicBatching( const ChannelAssigns& shaderChannels, UInt32 availableChannels, size_t maxVertices, size_t maxIndices, GfxPrimitiveType topology);
+ GFX_API void DynamicBatchMesh( const Matrix4x4f& matrix, const VertexBufferData& vertices, UInt32 firstVertex, UInt32 vertexCount, const IndexBufferData& indices, UInt32 firstIndexByte, UInt32 indexCount );
+ GFX_API void EndDynamicBatching( TransformType transformType );
+#if ENABLE_SPRITES
+ GFX_API void DynamicBatchSprite(const Matrix4x4f* matrix, const SpriteRenderData* rd, ColorRGBA32 color);
+#endif
+#endif
+
+ GFX_API void AddBatchingStats( int batchedTris, int batchedVerts, int batchedCalls );
+
+ /**
+ * CreateGPUSkinningInfo - Create a GPU-assisted skinning object.
+ *
+ * GPU-assisted skinning is limited to platforms that support GPU
+ * writing the skinned output mesh into a VBO (StreamOut, MemExport, Transform Feedback).
+ *
+ * The returned object should be used against a single Skinned Mesh instance, and deleted once no longer needed.
+ *
+ * @return GFX_API GPUSkinningInfo* New GPU skinning object, or NULL if GPU skinning is not supported on this GfxDevice.
+ */
+ GFX_API GPUSkinningInfo* CreateGPUSkinningInfo() GFX_PURE;
+
+ /**
+ * DeleteGPUSkinningInfo - Release a GPUSkinningInfo object.
+ *
+ * @param info GPUSkinningInfo object to delete
+ */
+ GFX_API void DeleteGPUSkinningInfo(GPUSkinningInfo *info) GFX_PURE;
+
+ /**
+ * SkinOnGPU - Perform GPU-assisted skinning into the destination VBO.
+ *
+ * @param info GPUSkinningInfo object filled with valid data and set up using UpdateSkinData and UpdateSkinBones
+ * @param lastThisFrame True if this is the last GPU-skinned mesh in this frame, false otherwise.
+ */
+ GFX_API void SkinOnGPU( GPUSkinningInfo* info, bool lastThisFrame ) GFX_PURE;
+
+ /**
+ * UpdateSkinSourceData - Pass the Vertex and skin data to the GPU.
+ *
+ * Assumes channel map, vertex count and stride has been previously set.
+ * If dirty is true, the vertex or skin data has changed since the last call, and the implementation should refresh the content.
+ * Otherwise, the implementation should just check that it has set up its internal buffers correctly and return.
+ *
+ * @param info GPUSkinningInfo object, expected to be properly set up with calls to setVertexCount, setChannelMap and setStride
+ * @param vertData Vertex data, array size defined by previous calls to setVertexCount(), setChannelMap() and setStride
+ * @param skinData Bone influence data, array size defined by setVertexCount
+ * @param dirty Dirty flag, see above
+ */
+ GFX_API void UpdateSkinSourceData(GPUSkinningInfo *info, const void *vertData, const BoneInfluence *skinData, bool dirty) GFX_PURE;
+
+ /**
+ * UpdateSkinBonePoses - Update bone pose matrices for a GPU-assisted skin.
+ *
+ * Note that the array is not guaranteed to stay alive after the call, so unless
+ * the data can be uploaded immediately, a local copy is required.
+ *
+ * @param boneCount Number of bones
+ * @param poses Array of bone matrices
+ */
+ GFX_API void UpdateSkinBonePoses(GPUSkinningInfo *info, const int boneCount, const Matrix4x4f* poses) GFX_PURE;
+
+
+#if UNITY_XENON
+ GFX_API RawVBO* CreateRawVBO( UInt32 size, UInt32 flags ) GFX_PURE;
+ GFX_API void DeleteRawVBO( RawVBO* vbo ) GFX_PURE;
+ GFX_API void EnablePersistDisplayOnQuit( bool enabled ) GFX_PURE;
+
+ GFX_API void RegisterTexture2D( TextureID tid, IDirect3DBaseTexture9* texture ) GFX_PURE;
+ GFX_API void PatchTexture2D( TextureID tid, IDirect3DBaseTexture9* texture ) GFX_PURE;
+ GFX_API void DeleteTextureEntryOnly( TextureID textureID ) GFX_PURE;
+ GFX_API void UnbindAndDelayReleaseTexture( IDirect3DBaseTexture9* texture ) GFX_PURE;
+ GFX_API void SetTextureWrapModes( TextureID textureID, TextureWrapMode wrapU, TextureWrapMode wrapV, TextureWrapMode wrapW ) GFX_PURE;
+
+ GFX_API void OnLastFrameCallback() GFX_PURE;
+
+ GFX_API xenon::IVideoPlayer* CreateVideoPlayer( bool fullscreen ) GFX_PURE;
+ GFX_API void DeleteVideoPlayer( xenon::IVideoPlayer* player ) GFX_PURE;
+ GFX_API void SetNullPixelShader() GFX_PURE;
+ GFX_API void SetHiZEnable( const HiZstate hiz_enable ) GFX_PURE;
+ GFX_API void SetHiStencilState( const bool hiStencilEnable, const bool hiStencilWriteEnable, const int hiStencilRef, const CompareFunction cmpFunc ) GFX_PURE;
+ GFX_API void HiStencilFlush( const HiSflush flushtype ) GFX_PURE;
+#endif
+
+ GFX_API RenderSurfaceHandle CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags) GFX_PURE;
+ GFX_API RenderSurfaceHandle CreateRenderDepthSurface (TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags) GFX_PURE;
+ GFX_API void DestroyRenderSurface (RenderSurfaceHandle& rs) GFX_PURE;
+ GFX_API void SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel = 0, CubemapFace face = kCubeFaceUnknown) GFX_PURE;
+ GFX_API void SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face, UInt32 flags)
+#if !UNITY_XENON
+ { SetRenderTargets(count, colorHandles, depthHandle, mipLevel, face); }
+#else
+ GFX_PURE
+#endif
+ ;
+
+ GFX_API void ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle) GFX_PURE;
+
+ GFX_API void DiscardContents (RenderSurfaceHandle& rs) GFX_PURE;
+ // Do not produce a warning for next unresolve of current RT; it is expected and there's nothing we can do about it
+ GFX_API void IgnoreNextUnresolveOnCurrentRenderTarget() { }
+ GFX_API void IgnoreNextUnresolveOnRS(RenderSurfaceHandle rs) { }
+
+ GFX_API void ResolveDepthIntoTexture (RenderSurfaceHandle /*colorHandle*/, RenderSurfaceHandle /*depthHandle*/) { }
+
+ GFX_API RenderSurfaceHandle GetActiveRenderColorSurface (int index) GFX_PURE;
+ GFX_API RenderSurfaceHandle GetActiveRenderDepthSurface () GFX_PURE;
+
+ // TODO: we might need to extend it in the future, e.g. for multi-display
+ GFX_API RenderSurfaceHandle GetBackBufferColorSurface () { return m_BackBufferColor; }
+ GFX_API RenderSurfaceHandle GetBackBufferDepthSurface () { return m_BackBufferDepth; }
+
+ GFX_API void SetBackBufferColorSurface(RenderSurfaceBase* color) { m_BackBufferColor=RenderSurfaceHandle(color); }
+ GFX_API void SetBackBufferDepthSurface(RenderSurfaceBase* depth) { m_BackBufferDepth=RenderSurfaceHandle(depth); }
+
+ GFX_API bool IsRenderTargetConfigValid(UInt32 width, UInt32 height, RenderTextureFormat /*colorFormat*/, DepthBufferFormat /*depthFormat*/)
+#if !UNITY_XENON
+ { return width <= gGraphicsCaps.maxRenderTextureSize && height <= gGraphicsCaps.maxRenderTextureSize; }
+#else
+ GFX_PURE
+#endif
+ ;
+
+ GFX_API void SetSurfaceFlags(RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags = 0) GFX_PURE;
+
+ GFX_API TextureID CreateTextureID();
+ GFX_API void FreeTextureID( TextureID texture );
+
+#if ENABLE_TEXTUREID_MAP
+ GFX_API intptr_t CreateExternalTextureFromNative(intptr_t nativeTex) { return nativeTex; }
+ GFX_API void UpdateExternalTextureFromNative(TextureID tex, intptr_t nativeTex) { TextureIdMap::UpdateTexture(tex, nativeTex); }
+#endif
+
+ enum kUploadTextureFlags
+ {
+ kUploadTextureDefault = 0,
+ kUploadTextureDontUseSubImage = 1<<0, // texture might not be created yet, or is being resized
+ kUploadTextureOSDrawingCompatible = 1<<1, // create an OS-drawing compatible one (e.g. for GDI on Windows)
+ // NOTE: Richard S added these on another Xbox360 branch, uncomment/merge when that branch comes back
+ //kUploadTextureTiled = 1<<2,
+ //kUploadTextureMemoryReady = 1<<3,
+ };
+
+ GFX_API void UploadTexture2D( TextureID texture, TextureDimension dimension, UInt8* srcData, int srcSize, int width, int height, TextureFormat format, int mipCount, UInt32 uploadFlags, int skipMipLevels, TextureUsageMode usageMode, TextureColorSpace colorSpace ) GFX_PURE;
+ GFX_API void UploadTextureSubData2D( TextureID texture, UInt8* srcData, int srcSize, int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace ) GFX_PURE;
+ GFX_API void UploadTextureCube( TextureID texture, UInt8* srcData, int srcSize, int faceDataSize, int size, TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace ) GFX_PURE;
+ GFX_API void UploadTexture3D( TextureID texture, UInt8* srcData, int srcSize, int width, int height, int depth, TextureFormat format, int mipCount, UInt32 uploadFlags ) GFX_PURE;
+ GFX_API void DeleteTexture( TextureID texture ) GFX_PURE;
+
+ GFX_API PresentMode GetPresentMode() GFX_PURE;
+
+ GFX_API void BeginFrame() GFX_PURE;
+ GFX_API void EndFrame() GFX_PURE;
+ inline bool IsInsideFrame() const { return m_InsideFrame; }
+ inline void SetInsideFrame(bool v) { m_InsideFrame = v; }
+ GFX_API void PresentFrame() GFX_PURE;
+ // Check if device is in valid state. E.g. lost device on D3D9; in this case all rendering
+ // should be skipped.
+ GFX_API bool IsValidState() GFX_PURE;
+ GFX_API bool HandleInvalidState() { return true; }
+ GFX_API void ResetDynamicResources() {}
+ GFX_API bool IsReadyToBeginFrame() { return true; }
+
+ // Fully finish any queued-up rendering (including on the GPU)
+ GFX_API void FinishRendering() GFX_PURE;
+ // Insert CPU fence into command queue (if threaded)
+ GFX_API UInt32 InsertCPUFence() { return 0; }
+ // Get next CPU fence that will be inserted
+ GFX_API UInt32 GetNextCPUFence() { return 0; }
+ // Finish any threaded commands before CPU fence
+ GFX_API void WaitOnCPUFence(UInt32 /*fence*/) {}
+
+ // Are we recording graphics commands?
+ inline bool IsRecording() const { return m_IsRecording; }
+
+ // Does this device derive from GfxThreadableDevice?
+ inline bool IsThreadable() const { return m_IsThreadable; }
+
+ // Acquire thread ownership on the calling thread. Worker releases ownership.
+ GFX_API void AcquireThreadOwnership() {}
+ // Release thread ownership on the calling thread. Worker acquires ownership.
+ GFX_API void ReleaseThreadOwnership() {}
+
+ // Immediate mode rendering
+ GFX_API void ImmediateShape( float x, float y, float z, float scale, ImmediateShapeType shape );
+ GFX_API void ImmediateVertex( float x, float y, float z ) GFX_PURE;
+ GFX_API void ImmediateNormal( float x, float y, float z ) GFX_PURE;
+ GFX_API void ImmediateColor( float r, float g, float b, float a ) GFX_PURE;
+ GFX_API void ImmediateTexCoordAll( float x, float y, float z ) GFX_PURE;
+ GFX_API void ImmediateTexCoord( int unit, float x, float y, float z ) GFX_PURE;
+ GFX_API void ImmediateBegin( GfxPrimitiveType type ) GFX_PURE;
+ GFX_API void ImmediateEnd() GFX_PURE;
+
+ // Recording display lists
+#if GFX_SUPPORTS_DISPLAY_LISTS
+ GFX_API bool BeginRecording() { return false; }
+ GFX_API bool EndRecording( GfxDisplayList** outDisplayList ) { return false; }
+#endif
+
+ // Capturing screen shots / blits
+ GFX_API bool CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 ) GFX_PURE;
+ GFX_API bool ReadbackImage( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY ) GFX_PURE;
+ GFX_API void GrabIntoRenderTexture (RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height) GFX_PURE;
+
+ // Any housekeeping around draw calls
+ GFX_API void BeforeDrawCall( bool immediateMode ) GFX_PURE;
+ GFX_API void AfterDrawCall() {};
+
+ GFX_API bool IsPositionRequiredForTexGen (int texStageIndex) const GFX_PURE;
+ GFX_API bool IsNormalRequiredForTexGen (int texStageIndex) const GFX_PURE;
+ GFX_API bool IsPositionRequiredForTexGen() const GFX_PURE;
+ GFX_API bool IsNormalRequiredForTexGen() const GFX_PURE;
+
+ GFX_API void SetActiveContext (void* /*ctx*/) {};
+
+ GFX_API void ResetFrameStats();
+ GFX_API void BeginFrameStats();
+ GFX_API void EndFrameStats();
+ GFX_API void SaveDrawStats();
+ GFX_API void RestoreDrawStats();
+ GFX_API void SynchronizeStats();
+
+ #if ENABLE_PROFILER
+ GFX_API void BeginProfileEvent (const char* /*name*/) {}
+ GFX_API void EndProfileEvent () {}
+ GFX_API void ProfileControl (GfxProfileControl /*ctrl*/, unsigned /*param*/) {}
+
+ GFX_API GfxTimerQuery* CreateTimerQuery() GFX_PURE;
+ GFX_API void DeleteTimerQuery(GfxTimerQuery* query) GFX_PURE;
+ GFX_API void BeginTimerQueries() GFX_PURE;
+ GFX_API void EndTimerQueries() GFX_PURE;
+ GFX_API bool TimerQueriesIsActive() { return false; }
+ #endif
+
+ // Editor-only stuff
+ #if UNITY_EDITOR
+ GFX_API void SetColorBytes (const UInt8 color[4]);
+ GFX_API void SetAntiAliasFlag( bool aa ) GFX_PURE;
+ GFX_API void DrawUserPrimitives( GfxPrimitiveType type, int vertexCount, UInt32 vertexChannels, const void* data, int stride ) GFX_PURE;
+ GFX_API int GetCurrentTargetAA() const GFX_PURE;
+
+ #if UNITY_WIN
+ //ToDo: This is windows specific code, we should replace HWND window with something more abstract
+ GFX_API GfxDeviceWindow* CreateGfxWindow( HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias ) GFX_PURE;
+
+ #endif
+ #endif
+
+ #if UNITY_WIN
+ GFX_API int GetCurrentTargetWidth() const { return 0; }
+ GFX_API int GetCurrentTargetHeight() const { return 0; }
+ GFX_API void SetCurrentTargetSize(int /*width*/, int /*height*/) { }
+ GFX_API void SetCurrentWindowSize(int /*width*/, int /*height*/) { }
+ #endif
+
+ static void CommonReloadResources( UInt32 flags );
+
+ #if GFX_SUPPORTS_OPENGL
+ GFX_API void UnbindObjects () {}
+ #endif
+
+ #if GFX_OPENGLESxx_ONLY || GFX_SUPPORTS_MOLEHILL
+ GFX_API void ReloadResources() GFX_PURE;
+ #endif
+
+ #if !GFX_DEVICE_VIRTUAL
+ GfxDeviceImpl* GetImpl() { return impl; }
+ #endif
+
+#if GFX_DEVICE_VIRTUAL
+ GFX_API RenderTextureFormat GetDefaultRTFormat() const { return kRTFormatARGB32; }
+#else
+ GFX_API RenderTextureFormat GetDefaultRTFormat() const;
+#endif
+
+#if GFX_DEVICE_VIRTUAL
+ GFX_API RenderTextureFormat GetDefaultHDRRTFormat() const { return kRTFormatARGBHalf; }
+#else
+ GFX_API RenderTextureFormat GetDefaultHDRRTFormat() const;
+#endif
+
+ GFX_API void* GetNativeGfxDevice() { return NULL; }
+ GFX_API void* GetNativeTexturePointer(TextureID /*id*/) { return NULL; }
+ GFX_API UInt32 GetNativeTextureID(TextureID id);
+ GFX_API void InsertCustomMarker (int marker);
+
+ GFX_API ComputeBufferID CreateComputeBufferID();
+ GFX_API void FreeComputeBufferID(ComputeBufferID id);
+
+ GFX_API void SetComputeBufferData (ComputeBufferID /*bufferHandle*/, const void* /*data*/, size_t /*size*/) { }
+ GFX_API void GetComputeBufferData (ComputeBufferID /*bufferHandle*/, void* /*dest*/, size_t /*destSize*/) { }
+ GFX_API void CopyComputeBufferCount (ComputeBufferID /*srcBuffer*/, ComputeBufferID /*dstBuffer*/, UInt32 /*dstOffset*/) { }
+
+ GFX_API void SetRandomWriteTargetTexture (int /*index*/, TextureID /*tid*/) { }
+ GFX_API void SetRandomWriteTargetBuffer (int /*index*/, ComputeBufferID /*bufferHandle*/) { }
+ GFX_API void ClearRandomWriteTargets () { }
+
+ GFX_API ComputeProgramHandle CreateComputeProgram (const UInt8* /*code*/, size_t /*codeSize*/) { ComputeProgramHandle cp; return cp; }
+ GFX_API void DestroyComputeProgram (ComputeProgramHandle& /*cpHandle*/) { }
+ GFX_API void CreateComputeConstantBuffers (unsigned /*count*/, const UInt32* /*sizes*/, ConstantBufferHandle* /*outCBs*/) { }
+ GFX_API void DestroyComputeConstantBuffers (unsigned /*count*/, ConstantBufferHandle* /*cbs*/) { }
+ GFX_API void CreateComputeBuffer (ComputeBufferID /*id*/, size_t /*count*/, size_t /*stride*/, UInt32 /*flags*/) { }
+ GFX_API void DestroyComputeBuffer (ComputeBufferID /*handle*/) { }
+ GFX_API void UpdateComputeConstantBuffers (unsigned /*count*/, ConstantBufferHandle* /*cbs*/, UInt32 /*cbDirty*/, size_t /*dataSize*/, const UInt8* /*data*/, const UInt32* /*cbSizes*/, const UInt32* /*cbOffsets*/, const int* /*bindPoints*/) { }
+ GFX_API void UpdateComputeResources (
+ unsigned /*texCount*/, const TextureID* /*textures*/, const int* /*texBindPoints*/,
+ unsigned /*samplerCount*/, const unsigned* /*samplers*/,
+ unsigned /*inBufferCount*/, const ComputeBufferID* /*inBuffers*/, const int* /*inBufferBindPoints*/,
+ unsigned /*outBufferCount*/, const ComputeBufferID* /*outBuffers*/, const TextureID* /*outTextures*/, const UInt32* /*outBufferBindPoints*/) { }
+ GFX_API void DispatchComputeProgram (ComputeProgramHandle /*cpHandle*/, unsigned /*threadsX*/, unsigned /*threadsY*/, unsigned /*threadsZ*/) { }
+
+ GFX_API void DrawNullGeometry (GfxPrimitiveType /*topology*/, int /*vertexCount*/, int /*instanceCount*/) { };
+ GFX_API void DrawNullGeometryIndirect (GfxPrimitiveType /*topology*/, ComputeBufferID /*bufferHandle*/, UInt32 /*bufferOffset*/) { };
+ DepthBufferFormat GetFramebufferDepthFormat() const { return m_FramebufferDepthFormat; }
+ void SetFramebufferDepthFormat(DepthBufferFormat depthFormat) { m_FramebufferDepthFormat = depthFormat; }
+
+protected:
+ void SetupVertexLightParams(int light, const GfxVertexLight& data);
+
+private:
+
+ #if !GFX_DEVICE_VIRTUAL
+ GfxDeviceImpl* impl;
+ #endif
+
+protected:
+ void OnCreate();
+ void OnDelete();
+ void OnCreateVBO(VBO* vbo);
+ void OnDeleteVBO(VBO* vbo);
+
+ // Mutable state
+ BuiltinShaderParamValues m_BuiltinParamValues;
+ GfxFogParams m_FogParams;
+ GfxDeviceStats m_Stats;
+ GfxDeviceStats m_SavedStats;
+ bool m_InsideFrame;
+ bool m_IsRecording;
+ bool m_IsThreadable;
+ RenderTexture* m_ActiveRenderTexture;
+ const BuiltinShaderParamIndices* m_BuiltinParamIndices[kShaderTypeCount];
+ BuiltinShaderParamIndices m_NullParamIndices;
+ MaterialPropertyBlock m_MaterialProperties;
+
+ // Immutable data
+ GfxDeviceRenderer m_Renderer;
+ bool m_UsesOpenGLTextureCoords;
+ bool m_UsesHalfTexelOffset;
+ int m_MaxBufferedFrames;
+ DepthBufferFormat m_FramebufferDepthFormat;
+
+
+ RenderSurfaceHandle m_BackBufferColor;
+ RenderSurfaceHandle m_BackBufferDepth;
+
+
+private:
+
+ VBOList* m_VBOList;
+ static volatile int ms_TextureIDGenerator;
+ static volatile int ms_ComputeBufferIDGenerator;
+
+ typedef std::map<TextureID, size_t> TextureIDToSizeMap;
+ TextureIDToSizeMap m_TextureSizes;
+};
+
+class GfxThreadableDevice : public GfxDevice
+{
+public:
+ //! Called by the worker thread on thread startup
+ GFX_API void OnDeviceCreated (bool /*callingFromRenderThread*/) { }
+
+ //! IsCombineModeSupported() exists because we want CreateTextureCombiners() failure to happen
+ //! on the main thread, not the render thread where we can't do anything about it.
+ //! When it returns true then creating combiners *should* succeed
+ GFX_API bool IsCombineModeSupported( unsigned int combiner ) GFX_PURE;
+
+ GFX_API void SetTextureCombinersThreadable( TextureCombinersHandle textureCombiners, const TexEnvData* texEnvData, const Vector4f* texColors ) GFX_PURE;
+ GFX_API void SetShadersMainThread (ShaderLab::SubProgram* programs[kShaderTypeCount], const ShaderLab::PropertySheet* props);
+ GFX_API void SetShadersThreadable (GpuProgram* programs[kShaderTypeCount], const GpuProgramParameters* params[kShaderTypeCount], UInt8 const * const paramsBuffer[kShaderTypeCount]) { };
+
+ //! CreateShaderParameters() exists because the main thread needs to know which parameters a shader takes, and on
+ //! GL-like platforms it can vary with different fog modes because we recompile shaders when the mode was changed.
+ GFX_API void CreateShaderParameters( ShaderLab::SubProgram* /*program*/, FogMode /*fogMode*/ ) { }
+};
+
+bool IsGfxDevice();
+EXPORT_COREMODULE GfxDevice& GetGfxDevice();
+GfxDevice& GetUncheckedGfxDevice();
+void SetGfxDevice(GfxDevice* device);
+void DestroyGfxDevice();
+
+GfxDevice& GetRealGfxDevice();
+bool IsRealGfxDeviceThreadOwner();
+#if ENABLE_MULTITHREADED_CODE
+void SetRealGfxDevice(GfxDevice* device);
+void SetRealGfxDeviceThreadOwnership();
+void DestroyRealGfxDevice();
+void SetGfxThreadingMode(GfxThreadingMode mode);
+GfxThreadingMode GetGfxThreadingMode();
+#endif
+
+class AutoGfxDeviceAcquireThreadOwnership
+{
+public:
+ AutoGfxDeviceAcquireThreadOwnership();
+ ~AutoGfxDeviceAcquireThreadOwnership();
+
+private:
+ bool m_WasOwner;
+};
+
+class AutoGfxDeviceBeginEndFrame
+{
+public:
+ AutoGfxDeviceBeginEndFrame();
+ ~AutoGfxDeviceBeginEndFrame();
+
+ void End();
+ bool GetSuccess() const { return m_Success; }
+
+private:
+ bool m_Success;
+ bool m_NeedsEndFrame;
+};
+
+void CalculateDeviceProjectionMatrix (Matrix4x4f& m, bool usesOpenGLTextureCoords, bool invertY);
+
+inline AutoGfxDeviceAcquireThreadOwnership::AutoGfxDeviceAcquireThreadOwnership()
+{
+ m_WasOwner = IsRealGfxDeviceThreadOwner();
+ if (!m_WasOwner)
+ GetGfxDevice().AcquireThreadOwnership();
+}
+
+inline AutoGfxDeviceAcquireThreadOwnership::~AutoGfxDeviceAcquireThreadOwnership()
+{
+ if (!m_WasOwner)
+ GetGfxDevice().ReleaseThreadOwnership();
+}
+
+inline AutoGfxDeviceBeginEndFrame::AutoGfxDeviceBeginEndFrame() :
+m_Success(true), m_NeedsEndFrame(false)
+{
+ GfxDevice& device = GetGfxDevice();
+ if (!device.IsInsideFrame())
+ {
+ device.BeginFrame();
+ m_Success = device.IsValidState();
+ m_NeedsEndFrame = true;
+ }
+}
+
+inline AutoGfxDeviceBeginEndFrame::~AutoGfxDeviceBeginEndFrame()
+{
+ End();
+}
+
+inline void AutoGfxDeviceBeginEndFrame::End()
+{
+ if (m_NeedsEndFrame)
+ GetGfxDevice().EndFrame();
+ m_NeedsEndFrame = false;
+}
+
+
+inline DepthBufferFormat DepthBufferFormatFromBits(int bits)
+{
+ if( bits <= 0 )
+ return kDepthFormatNone;
+ else if( bits <= 16 )
+ return kDepthFormat16;
+ else
+ return kDepthFormat24;
+}
+
+#if UNITY_EDITOR
+extern VertexComponent kSuitableVertexComponentForChannel[];
+#endif
+
+
+class SetAndRestoreWireframeMode {
+public:
+ SetAndRestoreWireframeMode() {
+ GfxDevice& device = GetGfxDevice();
+ m_SavedWireframe = device.GetWireframe();
+ }
+ SetAndRestoreWireframeMode(bool wireframe) {
+ GfxDevice& device = GetGfxDevice();
+ m_SavedWireframe = device.GetWireframe();
+ device.SetWireframe(wireframe);
+ }
+ ~SetAndRestoreWireframeMode() {
+ GfxDevice& device = GetGfxDevice();
+ device.SetWireframe(m_SavedWireframe);
+ }
+private:
+ bool m_SavedWireframe;
+};
+
+void ApplyTexEnvData (unsigned int texUnit, unsigned int samplerUnit, const TexEnvData& data);
+
diff --git a/Runtime/GfxDevice/GfxDeviceConfigure.h b/Runtime/GfxDevice/GfxDeviceConfigure.h
new file mode 100644
index 0000000..58ba4a5
--- /dev/null
+++ b/Runtime/GfxDevice/GfxDeviceConfigure.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+# define GFX_SUPPORTS_DXT_COMPRESSION 0
+# define GFX_SUPPORTS_DISPLAY_LISTS 0
+#elif (UNITY_LINUX && GFX_SUPPORTS_OPENGLES20)
+#warning Linux: determine the real configs here
+# define GFX_SUPPORTS_DXT_COMPRESSION 1
+# define GFX_SUPPORTS_DISPLAY_LISTS 0
+#elif (UNITY_WII)
+# define GFX_SUPPORTS_DXT_COMPRESSION 0
+# define GFX_SUPPORTS_DISPLAY_LISTS 0
+#else
+# define GFX_SUPPORTS_DXT_COMPRESSION 1
+# define GFX_SUPPORTS_DISPLAY_LISTS 1
+#endif
+
+#define GFX_ENABLE_DRAW_CALL_BATCHING (!ENABLE_GFXDEVICE_REMOTE_PROCESS && (UNITY_OSX || UNITY_WIN || UNITY_IPHONE || UNITY_ANDROID || UNITY_PEPPER || UNITY_LINUX || UNITY_FLASH || UNITY_XENON || UNITY_BB10 || UNITY_WEBGL || UNITY_TIZEN))
+#define GFX_HAS_NO_FIXED_FUNCTION (UNITY_PS3 || UNITY_XENON || ((GFX_SUPPORTS_OPENGLES20 || GFX_SUPPORTS_OPENGLES30) && GFX_OPENGLESxx_ONLY)))
+
+#define GFX_SUPPORTS_RENDERLOOP_PREPASS (!UNITY_WII && !UNITY_FLASH && !UNITY_WEBGL && !UNITY_BB10 && !UNITY_TIZEN)
+
+#define GFX_ALL_BUFFERS_CAN_BECOME_LOST (UNITY_FLASH || GFX_SUPPORTS_OPENGLES20)
+#define GFX_CAN_UNLOAD_MESH_DATA (GAMERELEASE && !UNITY_PEPPER && !UNITY_PS3 && !UNITY_WP8 && !GFX_ALL_BUFFERS_CAN_BECOME_LOST)
+
+#define GFX_SUPPORTS_TRISTRIPS (!UNITY_FLASH)
+
+#define GFX_EMULATES_NPOT_RENDERTEXTURES (UNITY_FLASH)
+
+#define NV_STATE_FILTERING UNITY_ANDROID
+
+
+
+#define GFX_ENABLE_SHADOW_BATCHING (GFX_ENABLE_DRAW_CALL_BATCHING && 1)
+
+#define GFX_SUPPORTS_CONSTANT_BUFFERS (GFX_SUPPORTS_D3D11 || GFX_SUPPORTS_OPENGLES30)
+
+#define GFX_USE_SPHERE_FOR_POINT_LIGHT (!UNITY_PS3)
diff --git a/Runtime/GfxDevice/GfxDeviceObjects.h b/Runtime/GfxDevice/GfxDeviceObjects.h
new file mode 100644
index 0000000..756f6df
--- /dev/null
+++ b/Runtime/GfxDevice/GfxDeviceObjects.h
@@ -0,0 +1,145 @@
+#pragma once
+
+#include "Runtime/Camera/Lighting.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Vector4.h"
+#include "GfxDeviceTypes.h"
+
+// --------------------------------------------------------------------------
+
+// A type safe opaque pointer to something.
+// ID type is used just so that you can have handles to different types, all type safe.
+template<typename ID, typename ObjectPtrType=void*>
+struct ObjectHandle
+{
+ explicit ObjectHandle(ObjectPtrType obj = 0) : object(obj) {}
+ bool IsValid() const { return object != 0; }
+ void Reset() { object = 0; }
+ bool operator==( const ObjectHandle<ID,ObjectPtrType>& o ) const { return object == o.object; }
+ bool operator!=( const ObjectHandle<ID,ObjectPtrType>& o ) const { return object != o.object; }
+
+ ObjectPtrType object;
+};
+
+#define OBJECT_FROM_HANDLE(handle,type) reinterpret_cast<type*>((handle).object)
+
+
+// --------------------------------------------------------------------------
+
+struct RenderSurfaceBase;
+struct RenderSurface_Tag;
+typedef ObjectHandle<RenderSurface_Tag, RenderSurfaceBase*> RenderSurfaceHandle;
+
+struct GraphicsContext_Tag;
+typedef ObjectHandle<GraphicsContext_Tag> GraphicsContextHandle;
+
+struct TextureCombiners_Tag;
+typedef ObjectHandle<TextureCombiners_Tag> TextureCombinersHandle;
+
+struct ComputeProgram_Tag;
+typedef ObjectHandle<ComputeProgram_Tag> ComputeProgramHandle;
+
+struct ConstantBuffer_Tag;
+typedef ObjectHandle<ConstantBuffer_Tag> ConstantBufferHandle;
+
+// --------------------------------------------------------------------------
+
+
+struct SimpleVec4
+{
+ float val[4];
+
+ void set( const float *v )
+ {
+ val[0] = v[0];
+ val[1] = v[1];
+ val[2] = v[2];
+ val[3] = v[3];
+ }
+ void set( float v0, float v1, float v2, float v3 )
+ {
+ val[0] = v0;
+ val[1] = v1;
+ val[2] = v2;
+ val[3] = v3;
+ }
+
+ bool operator==(const SimpleVec4& o) const {
+ return val[0] == o.val[0] && val[1] == o.val[1] && val[2] == o.val[2] && val[3] == o.val[3];
+ }
+ bool operator!=(const SimpleVec4& o) const {
+ return val[0] != o.val[0] || val[1] != o.val[1] || val[2] != o.val[2] || val[3] != o.val[3];
+ }
+ bool operator==(const float* o) const {
+ return val[0] == o[0] && val[1] == o[1] && val[2] == o[2] && val[3] == o[3];
+ }
+ bool operator!=(const float* o) const {
+ return val[0] != o[0] || val[1] != o[1] || val[2] != o[2] || val[3] != o[3];
+ }
+
+ const float* GetPtr() const { return val; }
+};
+
+
+
+// --------------------------------------------------------------------------
+
+
+struct GfxVertexLight
+{
+ Vector4f position; // directional: direction (w=0); others: position (w=1)
+ Vector4f spotDirection; // w = 0
+ Vector4f color; // diffuse&specular color
+ float range; // range
+ float quadAtten; // quadratic attenuation (constant = 1, linear = 0)
+ float spotAngle; // in degrees of full cone; -1 if not spot light
+ LightType type;
+
+ GfxVertexLight()
+ : position(Vector3f::zero,1.0f)
+ , spotDirection (Vector3f::zAxis,1.0f)
+ , color (Vector3f::zero,1.0f)
+ , range (0.0f)
+ , quadAtten (0.0f)
+ , spotAngle (0.0f)
+ , type (kLightDirectional)
+ {}
+};
+
+
+struct GfxMaterialParams
+{
+ Vector4f ambient;
+ Vector4f diffuse;
+ Vector4f specular;
+ Vector4f emissive;
+ float shininess;
+
+ void Invalidate()
+ {
+ ambient.Set( -1, -1, -1, -1 );
+ diffuse.Set( -1, -1, -1, -1 );
+ specular.Set( -1, -1, -1, -1 );
+ emissive.Set( -1, -1, -1, -1 );
+ shininess = -1.0f;
+ }
+};
+
+
+struct GfxFogParams
+{
+ FogMode mode;
+ Vector4f color;
+ float start;
+ float end;
+ float density;
+
+ void Invalidate()
+ {
+ mode = kFogUnknown;
+ color.Set( -1, -1, -1, -1 );
+ start = -1.0f;
+ end = -1.0f;
+ density = -1.0f;
+ }
+};
diff --git a/Runtime/GfxDevice/GfxDeviceRecreate.cpp b/Runtime/GfxDevice/GfxDeviceRecreate.cpp
new file mode 100644
index 0000000..6447cc3
--- /dev/null
+++ b/Runtime/GfxDevice/GfxDeviceRecreate.cpp
@@ -0,0 +1,291 @@
+#include "UnityPrefix.h"
+#include "GfxDeviceRecreate.h"
+#include "Runtime/Camera/RenderSettings.h"
+#include "Runtime/Filters/Deformation/SkinnedMeshFilter.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Dynamics/ClothRenderer.h"
+#include "Runtime/Filters/Misc/Font.h"
+#include "Runtime/Filters/Misc/TextMesh.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/GfxDevice/GfxDeviceSetup.h"
+#include "Runtime/Graphics/GeneratedTextures.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Graphics/Texture.h"
+#include "Runtime/IMGUI/TextMeshGenerator2.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Shaders/ComputeShader.h"
+#if ENABLE_PROFILER
+#include "Runtime/Profiler/ProfilerImpl.h"
+#endif
+#if UNITY_EDITOR
+#include "Editor/Platform/Interface/EditorWindows.h"
+#include "Editor/Src/Application.h"
+#endif
+
+#if ENABLE_TERRAIN
+#include "Runtime/Interfaces/ITerrainManager.h"
+#endif
+
+#if ENABLE_WEBCAM
+#include "Runtime/Video/VideoTexture.h"
+#endif
+
+static void UnloadAllTextures()
+{
+ #if ENABLE_WEBCAM
+ BaseVideoTexture::SuspendVideoTextures ();
+ #endif
+
+ Texture::ReloadAll (true, false, true);
+}
+
+static void LoadAllTextures()
+{
+ Texture::ReloadAll (false, true);
+
+ #if ENABLE_WEBCAM
+ BaseVideoTexture::ResumeVideoTextures ();
+ #endif
+}
+
+static void UnloadAllShaders()
+{
+ std::vector<SInt32> allShaders;
+ Shader::DeleteAllShaders(allShaders);
+ Shader::UnloadDefaultShaderLabShader();
+
+ // Now, some shaders might have been unsupported, which means they were pointing to default
+ // ShaderLab shader. Go over all of them and make sure ShaderLab shaders are NULL everywhere.
+ for (std::vector<SInt32>::iterator i = allShaders.begin(); i != allShaders.end(); ++i)
+ {
+ Shader *s = PPtr<Shader> (*i);
+ if (s)
+ s->ResetInternalPointersToNull();
+ }
+}
+
+static void LoadAllShaders()
+{
+ // Default pink shader is handled specially in shader load/unload because it's shared
+ // between multiple shader objects. We have to recreate it here.
+ Shader::LoadDefaultShaderLabShader();
+
+ std::vector<SInt32> allShaders;
+ Object::FindAllDerivedObjects (ClassID (Shader), &allShaders);
+ Shader::RecreateAllShaders(allShaders);
+}
+
+static void UnloadAllComputeShaders()
+{
+ vector<SInt32> objs;
+ Object::FindAllDerivedObjects (ClassID (ComputeShader), &objs);
+ for (size_t i = 0, n = objs.size(); i != n; ++i)
+ {
+ ComputeShader* obj = PPtr<ComputeShader> (objs[i]);
+ obj->UnloadFromGfxDevice();
+ }
+}
+
+static void LoadAllComputeShaders()
+{
+ vector<SInt32> objs;
+ Object::FindAllDerivedObjects (ClassID (ComputeShader), &objs);
+ for (size_t i = 0, n = objs.size(); i != n; ++i)
+ {
+ ComputeShader* obj = PPtr<ComputeShader> (objs[i]);
+ obj->ReloadToGfxDevice();
+ }
+}
+
+static void UnloadAllMeshes()
+{
+ vector<SInt32> meshes;
+ Object::FindAllDerivedObjects (ClassID (Mesh), &meshes, true);
+ for (size_t i = 0, n = meshes.size(); i != n; ++i)
+ {
+ Mesh* mesh = PPtr<Mesh> (meshes[i]);
+ mesh->UnloadVBOFromGfxDevice();
+ }
+}
+
+static void LoadAllMeshes()
+{
+ vector<SInt32> meshes;
+ Object::FindAllDerivedObjects (ClassID (Mesh), &meshes, true);
+ for (size_t i = 0, n = meshes.size(); i != n; ++i)
+ {
+ Mesh* mesh = PPtr<Mesh> (meshes[i]);
+ mesh->ReloadVBOToGfxDevice();
+ }
+}
+
+// Note: nothing needs to be done to "unload" fonts
+static void LoadAllFonts()
+{
+ vector<SInt32> fonts;
+ Object::FindAllDerivedObjects (ClassID (Font), &fonts, true);
+ for (size_t i = 0, n = fonts.size(); i != n; ++i)
+ {
+ Font* font = PPtr<Font> (fonts[i]);
+ font->ResetCachedTexture();
+ }
+}
+
+static void UnloadAllSkins()
+{
+ vector<SInt32> skins;
+ Object::FindAllDerivedObjects (ClassID (SkinnedMeshRenderer), &skins, true);
+ for (size_t i = 0, n = skins.size(); i != n; ++i)
+ {
+ SkinnedMeshRenderer* skin = PPtr<SkinnedMeshRenderer> (skins[i]);
+ skin->UnloadVBOFromGfxDevice();
+ }
+}
+
+static void LoadAllSkins()
+{
+ vector<SInt32> skins;
+ Object::FindAllDerivedObjects (ClassID (SkinnedMeshRenderer), &skins, true);
+ for (size_t i = 0, n = skins.size(); i != n; ++i)
+ {
+ SkinnedMeshRenderer* skin = PPtr<SkinnedMeshRenderer> (skins[i]);
+ skin->ReloadVBOToGfxDevice();
+ }
+}
+
+
+static void UnloadAllCloths()
+{
+# if ENABLE_CLOTH
+ vector<SInt32> cloths;
+ Object::FindAllDerivedObjects (ClassID (ClothRenderer), &cloths, true);
+ for (size_t i = 0, n = cloths.size(); i != n; ++i)
+ {
+ ClothRenderer* cloth = PPtr<ClothRenderer> (cloths[i]);
+ cloth->UnloadVBOFromGfxDevice();
+ }
+# endif // #if ENABLE_CLOTH
+}
+
+static void LoadAllCloths()
+{
+# if ENABLE_CLOTH
+ vector<SInt32> cloths;
+ Object::FindAllDerivedObjects (ClassID (ClothRenderer), &cloths, true);
+ for (size_t i = 0, n = cloths.size(); i != n; ++i)
+ {
+ ClothRenderer* cloth = PPtr<ClothRenderer> (cloths[i]);
+ cloth->ReloadVBOToGfxDevice();
+ }
+# endif // #if ENABLE_CLOTH
+}
+
+#if ENABLE_TERRAIN
+static void UnloadAllTerrains()
+{
+ GetITerrainManager()->UnloadTerrainsFromGfxDevice();
+}
+
+static void LoadAllTerrains()
+{
+ GetITerrainManager()->ReloadTerrainsToGfxDevice();
+}
+#endif
+
+static void AwakeFromLoadTextMeshes()
+{
+ vector<SInt32> textMeshes;
+ Object::FindAllDerivedObjects (ClassID (TextMesh), &textMeshes, true);
+ for (size_t i = 0, n = textMeshes.size(); i != n; ++i)
+ {
+ TextMesh* textMesh = PPtr<TextMesh> (textMeshes[i]);
+ textMesh->AwakeFromLoad(kDefaultAwakeFromLoad);
+ }
+}
+
+void CleanupAllGfxDeviceResources()
+{
+ #if UNITY_EDITOR && UNITY_WIN // GfxWindow thing only exists on Windows right now
+ ReleaseGfxWindowOnAllGUIViews();
+ #endif
+
+ TextMeshGenerator2::Flush();
+ RenderTexture::ReleaseAll();
+ ComputeBuffer::UnloadAllFromGfxDevice();
+ UnloadAllComputeShaders();
+ UnloadAllCloths();
+ UnloadAllSkins();
+ UnloadAllShaders();
+ UnloadAllTextures();
+ UnloadAllMeshes();
+
+#if ENABLE_TERRAIN
+ UnloadAllTerrains();
+#endif
+
+#if ENABLE_PROFILER
+ UnityProfiler::CleanupGfx();
+#endif
+
+ GetGfxDevice().FinishRendering();
+
+ DestroyGfxDevice();
+}
+
+void RecreateAllGfxDeviceResources()
+{
+ // Reinitialize graphics caps
+ gGraphicsCaps = GraphicsCaps();
+ #if UNITY_EDITOR
+ gGraphicsCaps.ResetOriginalEmulationCaps();
+ #endif
+
+ InitializeGfxDevice();
+
+ LoadAllMeshes();
+ LoadAllTextures();
+ LoadAllShaders();
+ LoadAllFonts();
+ LoadAllSkins();
+ LoadAllCloths();
+ LoadAllComputeShaders();
+ ComputeBuffer::ReloadAllToGfxDevice();
+
+#if ENABLE_TERRAIN
+ LoadAllTerrains();
+#endif
+
+ // Neet to re-setup buit-in texture props on the new gfx device
+ builtintex::ReinitBuiltinTextures();
+
+ // Need to re-setup fog values etc.
+ GetRenderSettings().AwakeFromLoad(kDefaultAwakeFromLoad);
+
+ // Resetup text meshes, because dynamic font textures were reset
+ AwakeFromLoadTextMeshes();
+
+ #if UNITY_EDITOR && UNITY_WIN // GfxWindow thing only exists on Windows right now
+ GetApplication().UpdateMainWindowTitle();
+ CreateGfxWindowOnAllGUIViews();
+ GUIView::RepaintAll(true);
+ #endif
+
+ // Reload current scene in the editor, so that any scripts that depend on the gfx device
+ // capabilities are reinitialized.
+ #if UNITY_EDITOR
+ GetApplication().OpenScene(GetApplication().GetCurrentScene());
+ #endif
+}
+
+void RecreateGfxDevice()
+{
+ CleanupAllGfxDeviceResources();
+ RecreateAllGfxDeviceResources();
+}
+
+void RecreateSkinnedMeshResources()
+{
+ UnloadAllSkins();
+ LoadAllSkins();
+}
+
diff --git a/Runtime/GfxDevice/GfxDeviceRecreate.h b/Runtime/GfxDevice/GfxDeviceRecreate.h
new file mode 100644
index 0000000..9dce6ac
--- /dev/null
+++ b/Runtime/GfxDevice/GfxDeviceRecreate.h
@@ -0,0 +1,10 @@
+#pragma once
+
+// Cleanup and recreate as separate steps
+void CleanupAllGfxDeviceResources();
+void RecreateAllGfxDeviceResources();
+
+// Cleanup and recreate as a single step
+void RecreateGfxDevice();
+
+void RecreateSkinnedMeshResources();
diff --git a/Runtime/GfxDevice/GfxDeviceResources.h b/Runtime/GfxDevice/GfxDeviceResources.h
new file mode 100644
index 0000000..9cb7825
--- /dev/null
+++ b/Runtime/GfxDevice/GfxDeviceResources.h
@@ -0,0 +1,129 @@
+#pragma once
+
+#include "GfxDeviceTypes.h"
+
+template<typename T>
+struct memcmp_less
+{
+ bool operator () (const T& lhs, const T& rhs) const
+ {
+ return memcmp(&lhs, &rhs, sizeof(T)) < 0;
+ }
+};
+
+
+struct GfxBlendState
+{
+ BlendMode srcBlend;
+ BlendMode dstBlend;
+ BlendMode srcBlendAlpha;
+ BlendMode dstBlendAlpha;
+ BlendOp blendOp;
+ BlendOp blendOpAlpha;
+ UInt32 renderTargetWriteMask;
+ CompareFunction alphaTest;
+ bool alphaToMask;
+
+ GfxBlendState()
+ {
+ memset(this, 0, sizeof(*this));
+ srcBlend = kBlendOne;
+ dstBlend = kBlendZero;
+ srcBlendAlpha = kBlendOne;
+ dstBlendAlpha = kBlendZero;
+ blendOp = kBlendOpAdd;
+ blendOpAlpha = kBlendOpAdd;
+ renderTargetWriteMask = KColorWriteAll;
+ alphaTest = kFuncDisabled;
+ alphaToMask = false;
+ }
+};
+
+
+struct GfxRasterState
+{
+ CullMode cullMode;
+ int depthBias;
+ float slopeScaledDepthBias;
+
+ GfxRasterState()
+ {
+ memset(this, 0, sizeof(*this));
+ cullMode = kCullBack;
+ depthBias = 0;
+ slopeScaledDepthBias = 0.0f;
+ }
+};
+
+
+struct GfxDepthState
+{
+ bool depthWrite;
+ CompareFunction depthFunc;
+
+ GfxDepthState()
+ {
+ memset(this, 0, sizeof(*this));
+ depthWrite = true;
+ depthFunc = kFuncLess;
+ }
+};
+
+struct GfxStencilState
+{
+ bool stencilEnable;
+ UInt8 readMask;
+ UInt8 writeMask;
+ CompareFunction stencilFuncFront;
+ StencilOp stencilPassOpFront; // stencil and depth pass
+ StencilOp stencilFailOpFront; // stencil fail (depth irrelevant)
+ StencilOp stencilZFailOpFront; // stencil pass, depth fail
+ CompareFunction stencilFuncBack;
+ StencilOp stencilPassOpBack;
+ StencilOp stencilFailOpBack;
+ StencilOp stencilZFailOpBack;
+
+ GfxStencilState()
+ {
+ memset(this, 0, sizeof(*this));
+ stencilEnable = false;
+ readMask = 0xFF;
+ writeMask = 0xFF;
+ stencilFuncFront = kFuncAlways;
+ stencilFailOpFront = kStencilOpKeep;
+ stencilZFailOpFront = kStencilOpKeep;
+ stencilPassOpFront = kStencilOpKeep;
+ stencilFuncBack = kFuncAlways;
+ stencilFailOpBack = kStencilOpKeep;
+ stencilZFailOpBack = kStencilOpKeep;
+ stencilPassOpBack = kStencilOpKeep;
+ }
+};
+
+struct DeviceBlendState
+{
+ DeviceBlendState(const GfxBlendState& src) : sourceState(src) {}
+ DeviceBlendState() {}
+ GfxBlendState sourceState;
+};
+
+struct DeviceDepthState
+{
+ DeviceDepthState(const GfxDepthState& src) : sourceState(src) {}
+ DeviceDepthState() {}
+ GfxDepthState sourceState;
+};
+
+struct DeviceStencilState
+{
+ DeviceStencilState(const GfxStencilState& src) : sourceState(src) {}
+ DeviceStencilState() {}
+ GfxStencilState sourceState;
+};
+
+struct DeviceRasterState
+{
+ DeviceRasterState(const GfxRasterState& src) : sourceState(src) {}
+ DeviceRasterState() {}
+ GfxRasterState sourceState;
+};
diff --git a/Runtime/GfxDevice/GfxDeviceSetup.cpp b/Runtime/GfxDevice/GfxDeviceSetup.cpp
new file mode 100644
index 0000000..1ce7fa7
--- /dev/null
+++ b/Runtime/GfxDevice/GfxDeviceSetup.cpp
@@ -0,0 +1,396 @@
+#include "UnityPrefix.h"
+#include "GfxDeviceSetup.h"
+#include "GfxDevice.h"
+#include "threaded/GfxDeviceClient.h"
+#include "Configuration/UnityConfigure.h"
+#include "../Misc/SystemInfo.h"
+#include "Runtime/Utilities/Argv.h"
+#include "Runtime/Misc/PlayerSettings.h"
+#include "Runtime/Misc/BuildSettings.h"
+#if UNITY_WIN
+#include "Runtime/GfxDevice/opengl/GLContext.h"
+#if GFX_SUPPORTS_D3D9
+#include "Runtime/GfxDevice/d3d/D3D9Context.h"
+#endif
+#include "PlatformDependent/Win/WinUtils.h"
+#include "PlatformDependent/Win/WinUnicode.h"
+#endif
+#if UNITY_WII
+#include "PlatformDependent/wii/WiiDeviceContext.h"
+#endif
+#if GFX_SUPPORTS_GCM
+#include "PS3/GfxDevicePS3.h"
+#endif
+#if UNITY_EDITOR
+#include "Runtime/Utilities/PlayerPrefs.h"
+#endif
+
+#if ENABLE_FORCE_GFX_RENDERER
+GfxDeviceRenderer g_ForcedGfxRenderer = (GfxDeviceRenderer)-1;
+#if GFX_SUPPORTS_D3D9
+bool g_ForceD3D9RefDevice;
+#endif
+#endif
+
+GfxThreadingMode g_ForcedGfxThreadingMode = kGfxThreadingModeAutoDetect;
+
+
+#if GFX_SUPPORTS_OPENGLES20 || GFX_SUPPORTS_OPENGLES30
+int gDefaultFBO = -1;
+#endif
+
+
+bool IsThreadableGfxDevice (GfxDeviceRenderer renderer)
+{
+ switch (renderer) {
+ case kGfxRendererD3D9:
+ case kGfxRendererD3D11:
+#if UNITY_WIN || UNITY_LINUX || UNITY_ANDROID
+ // Multithreaded gles30 renderer currently only tested on windows simulator
+ case kGfxRendererOpenGLES30:
+#endif
+#if UNITY_ANDROID
+ case kGfxRendererOpenGLES20Mobile:
+#endif
+#if !UNITY_WIN
+ // Multithreaded OpenGL is broken in Windows editor/player
+ // Editor used to work when creating real GfxDevice on render thread
+ case kGfxRendererOpenGL:
+#endif
+ case kGfxRendererXenon:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+GfxDevice* CreateRealGfxDevice (GfxDeviceRenderer renderer, bool forceRef)
+{
+ #if GFX_SUPPORTS_D3D9
+ if (renderer == kGfxRendererD3D9)
+ {
+ GfxThreadableDevice* CreateD3D9GfxDevice(bool forceREF);
+ return CreateD3D9GfxDevice(forceRef);
+ }
+ #endif
+ #if GFX_SUPPORTS_OPENGL
+ if (renderer == kGfxRendererOpenGL)
+ {
+ GfxThreadableDevice* CreateGLGfxDevice();
+ return CreateGLGfxDevice();
+ }
+ #endif
+ #if GFX_SUPPORTS_XENON
+ if (renderer == kGfxRendererXenon)
+ {
+ GfxThreadableDevice* CreateXenonGfxDevice();
+ return CreateXenonGfxDevice();
+ }
+ #endif
+
+ #if GFX_SUPPORTS_D3D11
+ if (renderer == kGfxRendererD3D11)
+ {
+ GfxDevice* CreateD3D11GfxDevice();
+ return CreateD3D11GfxDevice();
+ }
+ #endif
+
+ #if GFX_SUPPORTS_NULL
+ if (renderer == kGfxRendererNull)
+ {
+ GfxDevice* CreateNullGfxDevice();
+ return CreateNullGfxDevice();
+ }
+ #endif
+
+ #if GFX_SUPPORTS_OPENGLES20
+ if (renderer == kGfxRendererOpenGLES20Mobile)
+ {
+ extern GfxDevice* CreateGLES20GfxDevice();
+ return CreateGLES20GfxDevice();
+ }
+ #endif
+
+ #if GFX_SUPPORTS_OPENGLES30
+ if (renderer = kGfxRendererOpenGLES30)
+ {
+ extern GfxDevice* CreateGLES30GfxDevice();
+ return CreateGLES30GfxDevice();
+ }
+ #endif
+
+ AssertString ("should not happen");
+ return NULL;
+}
+#endif
+
+void ParseGfxDeviceArgs ()
+{
+ // multithreading modes
+ if (HasARGV("force-gfx-direct"))
+ {
+ ::g_ForcedGfxThreadingMode = kGfxThreadingModeDirect;
+ }
+ else if (HasARGV("force-gfx-st"))
+ {
+ ::g_ForcedGfxThreadingMode = kGfxThreadingModeNonThreaded;
+ }
+ else if (HasARGV("force-gfx-mt"))
+ {
+ ::g_ForcedGfxThreadingMode = kGfxThreadingModeThreaded;
+ }
+
+ // forced gfx device types
+ #if ENABLE_FORCE_GFX_RENDERER
+
+ #if GFX_SUPPORTS_D3D9
+ if (HasARGV ("force-d3d9"))
+ ::g_ForcedGfxRenderer = kGfxRendererD3D9;
+ #endif
+ #if GFX_SUPPORTS_OPENGL
+ if (HasARGV ("force-opengl"))
+ ::g_ForcedGfxRenderer = kGfxRendererOpenGL;
+ #endif
+ #if GFX_SUPPORTS_D3D11
+ if (HasARGV ("force-d3d11"))
+ ::g_ForcedGfxRenderer = kGfxRendererD3D11;
+ #endif
+ #if GFX_SUPPORTS_OPENGLES20
+ if (HasARGV ("force-gles20"))
+ ::g_ForcedGfxRenderer = kGfxRendererOpenGLES20Mobile;
+ #endif
+ #if GFX_SUPPORTS_OPENGLES30
+ if (HasARGV ("force-gles30"))
+ ::g_ForcedGfxRenderer = kGfxRendererOpenGLES30;
+ #endif
+ #if GFX_SUPPORTS_D3D9
+ if (HasARGV("force-d3d9-ref"))
+ ::g_ForceD3D9RefDevice = true;
+ #endif
+
+ #endif // ENABLE_FORCE_GFX_RENDERER
+}
+
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS_WORKER
+bool InitializeGfxDeviceWorkerProcess(size_t size, void *buffer)
+{
+ Assert (!IsGfxDevice());
+ GfxDevice* device = NULL;
+ device = CreateClientGfxDevice (kGfxRendererOpenGL, kClientDeviceThreaded | kClientDeviceWorkerProcess, size, buffer);
+ SetGfxDevice(device);
+
+ // hack to make sure FastPropertyNames are initialized on the Worker process.
+ ShaderLab::FastPropertyName name;
+ name.SetName("");
+
+ return device != NULL;
+}
+#endif
+
+bool InitializeGfxDevice()
+{
+ Assert (!IsGfxDevice());
+ GfxDevice* device = NULL;
+ PlayerSettings* playerSettings = GetPlayerSettingsPtr();
+
+ #if ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+
+ device = CreateClientGfxDevice (kGfxRendererOpenGL, kClientDeviceThreaded | kClientDeviceClientProcess);
+
+ #else //ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+
+ #if ENABLE_MULTITHREADED_CODE && !UNITY_WP8
+
+ // Device threading mode and flags
+ #if !UNITY_WIN && !UNITY_OSX && !UNITY_XENON && !UNITY_LINUX && !UNITY_ANDROID
+ g_ForcedGfxThreadingMode = kGfxThreadingModeDirect; // direct device everywhere except where it isn't
+ #endif
+
+ bool threaded = systeminfo::GetProcessorCount() > 1; // default to MT rendering on multicore
+ #if WEBPLUG
+ threaded = false; // for now, don't use MT rendering in web player; needs way more testing
+ #endif
+ #if !WEBPLUG
+ if (!IsHumanControllingUs())
+ threaded = false; // in automated/batchmode, default to non threaded
+ #endif
+
+ #if UNITY_XENON || UNITY_ANDROID
+ if (playerSettings && playerSettings->GetMTRenderingRuntime() == false)
+ threaded = false; // disabled by the user
+ #endif
+
+ #if UNITY_WIN && UNITY_EDITOR
+ if (systeminfo::GetOperatingSystemNumeric() < 600 && g_ForcedGfxThreadingMode == kGfxThreadingModeAutoDetect)
+ {
+ printf_console("Disabled multithreaded rendering due to old version of Windows.\n"\
+ "Changing input language can cause lockups with versions before Vista.\n"\
+ "Use -force-gfx-mt option on the command line to override.\n");
+ threaded = false;
+ }
+ #endif
+
+ if (g_ForcedGfxThreadingMode == kGfxThreadingModeThreaded)
+ threaded = true;
+ else if (g_ForcedGfxThreadingMode == kGfxThreadingModeNonThreaded)
+ threaded = false;
+
+ UInt32 deviceFlags = 0;
+
+ if (threaded) deviceFlags |= kClientDeviceThreaded;
+ if (g_ForcedGfxThreadingMode == kGfxThreadingModeDirect) deviceFlags |= kClientDeviceUseRealDevice;
+ #if GFX_SUPPORTS_D3D9 && ENABLE_FORCE_GFX_RENDERER
+ if (g_ForceD3D9RefDevice) deviceFlags |= kClientDeviceForceRef;
+ #endif
+
+ #endif
+
+
+#if ENABLE_FORCE_GFX_RENDERER
+
+if (g_ForcedGfxRenderer >= 0)
+{
+ printf_console ("Forcing GfxDevice: %d\n", g_ForcedGfxRenderer);
+ if (!IsThreadableGfxDevice(g_ForcedGfxRenderer))
+ deviceFlags |= kClientDeviceUseRealDevice;
+ device = CreateClientGfxDevice (g_ForcedGfxRenderer, deviceFlags);
+}
+#endif
+
+ #if UNITY_WP8
+
+ device = CreateRealGfxDevice (kGfxRendererD3D11, false);
+
+ #elif UNITY_WIN
+ // -------- Windows -----------------------------------------------------
+
+ // Try D3D11 if project was built with support for that
+ #if GFX_SUPPORTS_D3D11
+ if (!device && (UNITY_WINRT || (playerSettings && playerSettings->GetUseDX11())))
+ {
+ if (!IsThreadableGfxDevice(kGfxRendererD3D11))
+ deviceFlags |= kClientDeviceUseRealDevice;
+ device = CreateClientGfxDevice (kGfxRendererD3D11, deviceFlags);
+ }
+ #endif
+
+ // Try D3D9
+ #if GFX_SUPPORTS_D3D9
+ if (!device)
+ {
+ device = CreateClientGfxDevice (kGfxRendererD3D9, deviceFlags);
+ if (!device & (deviceFlags & kClientDeviceForceRef))
+ {
+ winutils::AddErrorMessage( "Failed to initialize Direct3D 9 REF device.\r\nMake sure you have DX9 SDK installed." );
+ return false;
+ }
+ }
+ #endif
+
+ if (!device)
+ {
+ // Try OpenGL. Editor is dx only unless you force GL
+ #if GFX_SUPPORTS_OPENGL && !UNITY_EDITOR
+ printf_console( "D3D9 initialization failed, trying OpenGL\n" );
+ device = CreateClientGfxDevice (kGfxRendererOpenGL, deviceFlags);
+ #endif
+ if (!device)
+ winutils::AddErrorMessage( "Failed to initialize Direct3D 9.\r\nMake sure you have DirectX 9.0c installed, have drivers for your\r\ngraphics card and have not disabled 3D acceleration\r\nin display settings." );
+ }
+
+
+ #elif UNITY_OSX
+ // -------- Mac OS X -----------------------------------------------------
+
+ device = CreateClientGfxDevice (kGfxRendererOpenGL, deviceFlags);
+
+ #elif UNITY_WII
+
+ GfxDevice* CreateWiiGfxDevice();
+ device = CreateWiiGfxDevice();
+
+ #elif UNITY_XENON
+
+ GfxThreadableDevice* CreateXenonGfxDevice();
+ device = CreateClientGfxDevice(kGfxRendererXenon, deviceFlags);
+
+ #elif UNITY_PS3
+
+ GfxDevice* CreateGCMGfxDevice();
+ device = CreateGCMGfxDevice();
+ SetGfxDevice(device);
+ ((GfxDevicePS3*)device)->Init();
+
+ #elif UNITY_IPHONE
+
+ extern GfxDevice* CreateUniversalGLESGfxDevice();
+ device = CreateUniversalGLESGfxDevice();
+
+ #elif UNITY_ANDROID
+
+ if (!device)
+ {
+ GfxDeviceRenderer renderer =
+ playerSettings->GetTargetGlesGraphics() + 1 == 2 ? kGfxRendererOpenGLES20Mobile :
+ playerSettings->GetTargetGlesGraphics() + 1 == 3 ? kGfxRendererOpenGLES30 :
+ kGfxRendererNull;
+
+ if (!IsThreadableGfxDevice(renderer))
+ deviceFlags |= kClientDeviceUseRealDevice;
+ device = CreateClientGfxDevice(renderer, deviceFlags);
+ }
+
+ #elif UNITY_BB10
+
+ GfxDevice* CreateGLES20GfxDevice();
+ device = CreateGLES20GfxDevice();
+
+ #elif UNITY_TIZEN
+
+ GfxDevice* CreateGLES20GfxDevice();
+ device = CreateGLES20GfxDevice();
+
+ #elif UNITY_PEPPER
+
+ GfxDevice* CreateGLES20GfxDevice();
+ device = CreateGLES20GfxDevice();
+ #elif UNITY_WEBGL
+
+ GfxDevice* CreateGLES20GfxDevice();
+ device = CreateGLES20GfxDevice();
+
+ #elif UNITY_LINUX && GFX_SUPPORTS_OPENGL
+
+ if (NULL == device) {
+ GfxThreadableDevice* CreateGLGfxDevice();
+ device = CreateGLGfxDevice();
+ }
+
+ #elif UNITY_LINUX && GFX_SUPPORTS_OPENGLES20
+
+ GfxDevice* CreateGLES20GfxDevice();
+ device = CreateGLES20GfxDevice();
+
+ #elif UNITY_LINUX && !SUPPORT_X11
+
+ GfxDevice* CreateNullGfxDevice();
+ device = CreateNullGfxDevice();
+
+ #elif UNITY_FLASH
+ GfxDevice* CreateMolehillGfxDevice();
+ device = CreateMolehillGfxDevice();
+
+ #else
+ #error "Unknown platform"
+ #endif
+ #endif //ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+
+ SetGfxDevice(device);
+
+ gGraphicsCaps.SharedCapsPostInitialize ();
+
+ return device != NULL;
+}
diff --git a/Runtime/GfxDevice/GfxDeviceSetup.h b/Runtime/GfxDevice/GfxDeviceSetup.h
new file mode 100644
index 0000000..53efa2f
--- /dev/null
+++ b/Runtime/GfxDevice/GfxDeviceSetup.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "GfxDeviceTypes.h"
+class GfxDevice;
+
+bool InitializeGfxDevice();
+
+bool InitializeGfxDeviceWorkerProcess(size_t size, void *buffer);
+
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+GfxDevice* CreateRealGfxDevice (GfxDeviceRenderer renderer, bool forceRef);
+#endif
+bool IsThreadableGfxDevice (GfxDeviceRenderer renderer);
+void ParseGfxDeviceArgs ();
+
+#define ENABLE_FORCE_GFX_RENDERER ((UNITY_WIN && !UNITY_WP8) || UNITY_LINUX || UNITY_ANDROID)
+
+#if ENABLE_FORCE_GFX_RENDERER
+extern GfxDeviceRenderer g_ForcedGfxRenderer;
+#if GFX_SUPPORTS_D3D9
+extern bool g_ForceD3D9RefDevice;
+#endif
+#endif
+
+extern GfxThreadingMode g_ForcedGfxThreadingMode;
+
+
+#if GFX_SUPPORTS_OPENGLES20 || GFX_SUPPORTS_OPENGLES30
+extern int gDefaultFBO;
+#endif
diff --git a/Runtime/GfxDevice/GfxDeviceStats.cpp b/Runtime/GfxDevice/GfxDeviceStats.cpp
new file mode 100644
index 0000000..2b002a0
--- /dev/null
+++ b/Runtime/GfxDevice/GfxDeviceStats.cpp
@@ -0,0 +1,170 @@
+#include "UnityPrefix.h"
+#include "GfxDevice.h"
+#include "GfxDeviceStats.h"
+#include "Runtime/Graphics/Texture.h"
+#include "Runtime/Input/TimeManager.h"
+
+#if UNITY_IPHONE
+ #include <mach/mach_time.h>
+#elif UNITY_ANDROID
+ #include <sys/time.h>
+#endif
+#include "Runtime/Profiler/MemoryProfiler.h"
+
+GfxDeviceStats::GfxDeviceStats()
+{
+ #if ENABLE_PROFILER
+ m_StatsEnabled = false; // off by default!
+ m_ClientFrameTime = 0.0f;
+ m_RenderFrameTime = 0.0f;
+ m_EnableStartTime = 0.0;
+ #endif
+
+ // just initialize to some sane values
+ SetScreenParams( 64, 64, 4, 4, 4, 0 );
+ m_Memory.renderTextureBytes = 0;
+}
+
+
+void GfxDeviceStats::StateStats::Reset()
+{
+ renderTexture = 0;
+ vboUploads = 0;
+ vboUploadBytes = 0;
+ ibUploads = 0;
+ ibUploadBytes = 0;
+}
+
+void GfxDeviceStats::ClientStats::Reset()
+{
+ shadowCasters = 0;
+
+ ABSOLUTE_TIME_INIT(cullingDt);
+ ABSOLUTE_TIME_INIT(clearDt);
+}
+
+void GfxDeviceStats::DrawStats::Reset()
+{
+ calls = 0;
+ tris = 0;
+ trisSent = 0;
+ verts = 0;
+ batches = 0;
+ batchedCalls = 0;
+ batchedTris = 0;
+ batchedVerts = 0;
+
+ ABSOLUTE_TIME_INIT(dt);
+ ABSOLUTE_TIME_INIT(batchDt);
+
+ usedTextureCount = 0;
+ usedTextureBytes = 0;
+
+ #if ENABLE_PROFILER
+ usedTextures.clear();
+ #endif
+}
+
+void GfxDeviceStats::AccumulateUsedTextureUsage()
+{
+#if ENABLE_PROFILER
+ m_Draw.usedTextureCount += m_Draw.usedTextures.size();
+ #if ENABLE_MEM_PROFILER
+ for (GfxDeviceStats::DrawStats::TextureIDSet::const_iterator i=m_Draw.usedTextures.begin();i != m_Draw.usedTextures.end();i++)
+ m_Draw.usedTextureBytes += GetMemoryProfiler()->GetRelatedIDMemorySize(i->m_ID);
+ #endif
+ m_Draw.usedTextures.clear();
+#endif
+}
+
+void GfxDeviceStats::ResetFrame()
+{
+ m_Changes.Reset();
+ m_Draw.Reset();
+ m_Client.Reset();
+ #if ENABLE_PROFILER
+ m_ClientFrameTime = 0.0f;
+ m_RenderFrameTime = 0.0f;
+ #endif
+}
+
+void GfxDeviceStats::ResetClientStats()
+{
+ m_Client.Reset();
+ m_ClientFrameTime = 0.0f;
+}
+
+#if ENABLE_PROFILER
+void GfxDeviceStats::AddUsedTexture(TextureID tex)
+{
+ CHECK_STATS_ENABLED
+ if(m_Draw.usedTextures.find(tex) == m_Draw.usedTextures.end())
+ m_Draw.usedTextures.insert(tex);
+}
+#endif
+
+void GfxDeviceStats::SetScreenParams( int width, int height, int backbufferBPP, int frontbufferBPP, int depthBPP, int fsaa )
+{
+ m_Memory.screenWidth = width;
+ m_Memory.screenHeight = height;
+
+ // can pass -1 for BPP params to keep current ones
+ if (frontbufferBPP >= 0)
+ m_Memory.screenFrontBPP = frontbufferBPP;
+ if (backbufferBPP >= 0)
+ m_Memory.screenBackBPP = backbufferBPP;
+ if (depthBPP >= 0)
+ m_Memory.screenDepthBPP = depthBPP;
+ if (fsaa >= 0)
+ m_Memory.screenFSAA = fsaa;
+
+ m_Memory.screenBytes = width * height * (std::max(m_Memory.screenFSAA,1) * (m_Memory.screenBackBPP + m_Memory.screenDepthBPP) + m_Memory.screenFrontBPP);
+ //printf_console("set screen params: %ix%i %ixAA mem=%.1fMB\n", width, height, fsaa, m_Memory.screenBytes/1024.0f/1024.0f);
+}
+
+void GfxDeviceStats::BeginFrameStats()
+{
+ m_StatsEnabled = true;
+ m_EnableStartTime = GetTimeSinceStartup();
+}
+
+void GfxDeviceStats::EndClientFrameStats()
+{
+ m_StatsEnabled = false;
+ m_ClientFrameTime += GetTimeSinceStartup() - m_EnableStartTime;
+}
+
+void GfxDeviceStats::EndRenderFrameStats()
+{
+ m_StatsEnabled = false;
+ m_RenderFrameTime += GetTimeSinceStartup() - m_EnableStartTime;
+}
+
+void GfxDeviceStats::CopyAllDrawStats( const GfxDeviceStats& s )
+{
+ m_Draw = s.m_Draw;
+ m_Changes = s.m_Changes;
+ m_RenderFrameTime = s.m_RenderFrameTime;
+}
+
+void GfxDeviceStats::CopyClientStats( const GfxDeviceStats& s )
+{
+ m_Client = s.m_Client;
+ m_ClientFrameTime = s.m_ClientFrameTime;
+}
+
+#if UNITY_IPHONE || UNITY_ANDROID
+ABSOLUTE_TIME GfxDeviceStats::GetHighResolutionAbsTime ()
+{
+#if UNITY_IPHONE
+ return mach_absolute_time ();
+#elif UNITY_ANDROID
+ timespec ts;
+ clock_gettime (CLOCK_REALTIME, &ts);
+ long long nanosecs = ts.tv_sec;
+ nanosecs <<= 32;
+ nanosecs |= ts.tv_nsec;
+ return nanosecs;
+#endif
+}
+#endif
diff --git a/Runtime/GfxDevice/GfxDeviceStats.h b/Runtime/GfxDevice/GfxDeviceStats.h
new file mode 100644
index 0000000..688993c
--- /dev/null
+++ b/Runtime/GfxDevice/GfxDeviceStats.h
@@ -0,0 +1,154 @@
+#pragma once
+
+#include "GfxDeviceTypes.h"
+#include "Runtime/Profiler/TimeHelper.h"
+
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_WII
+#define CHECK_STATS_ENABLED
+#else
+#define CHECK_STATS_ENABLED if( !m_StatsEnabled ) return;
+#endif
+
+class GfxDeviceStats {
+public:
+ struct StateStats {
+ void Reset();
+
+ int renderTexture;
+ int vboUploads;
+ int vboUploadBytes;
+ int ibUploads;
+ int ibUploadBytes;
+ };
+ struct ClientStats {
+ // Main thread stats
+ void Reset();
+
+ int shadowCasters;
+
+ ABSOLUTE_TIME cullingDt;
+ ABSOLUTE_TIME clearDt;
+ };
+ struct DrawStats {
+ // Render thread stats
+ void Reset();
+
+ int calls;
+ int tris, trisSent;
+ int verts;
+ int batches;
+ int batchedCalls;
+ int batchedTris;
+ int batchedVerts;
+
+ ABSOLUTE_TIME dt;
+ ABSOLUTE_TIME batchDt;
+
+ int usedTextureCount;
+ int usedTextureBytes;
+
+ #if ENABLE_PROFILER
+ typedef std::set<TextureID, std::less<TextureID>, STL_ALLOCATOR(kMemProfiler, TextureID) > TextureIDSet;
+ TextureIDSet usedTextures;
+ #endif
+ };
+ struct MemoryStats {
+ int screenWidth, screenHeight;
+ int screenFrontBPP, screenBackBPP, screenDepthBPP;
+ int screenFSAA; // actual level of anti-aliasing used
+ int screenBytes; // memory for backbuffer + frontbuffer
+ SInt64 renderTextureBytes;
+ };
+
+ GfxDeviceStats();
+
+ void AddRenderTextureChange() { CHECK_STATS_ENABLED ++m_Changes.renderTexture; }
+ void AddUploadVBO(int vertexData) { CHECK_STATS_ENABLED ++m_Changes.vboUploads; m_Changes.vboUploadBytes += vertexData; }
+ void AddUploadIB(int indexData) { CHECK_STATS_ENABLED ++m_Changes.ibUploads; m_Changes.ibUploadBytes += indexData; }
+ #if ENABLE_PROFILER
+ void AddUsedTexture(TextureID tex);
+ #else
+ void AddUsedTexture(TextureID tex) {}
+ #endif
+
+ void AddDrawCall( int tris, int verts, ABSOLUTE_TIME dt, int trisSent = -1) { CHECK_STATS_ENABLED
+ ++m_Draw.calls; m_Draw.tris += tris; m_Draw.verts += verts; m_Draw.dt = COMBINED_TIME(m_Draw.dt, dt);
+ m_Draw.trisSent += ((trisSent > 0)? trisSent: tris);
+ }
+ void AddDrawCall( int tris, int verts, int trisSent = -1) { CHECK_STATS_ENABLED
+ ABSOLUTE_TIME dt;
+ ABSOLUTE_TIME_INIT(dt);
+ AddDrawCall(tris, verts, dt, trisSent);
+ }
+ void AddBatch(int trisSent, int batchedVerts, int batchedCalls, ABSOLUTE_TIME batchDt) { CHECK_STATS_ENABLED
+ m_Draw.batches++;
+ m_Draw.batchedCalls += batchedCalls; m_Draw.batchDt = COMBINED_TIME(m_Draw.batchDt, batchDt);
+ m_Draw.batchedTris += trisSent;
+ m_Draw.batchedVerts += batchedVerts;
+ }
+ void AddCulling(ABSOLUTE_TIME cullingDt) { CHECK_STATS_ENABLED
+ m_Client.cullingDt = COMBINED_TIME(m_Client.cullingDt, cullingDt);
+ }
+ void AddClear(ABSOLUTE_TIME clearDt) { CHECK_STATS_ENABLED
+ m_Client.clearDt = COMBINED_TIME(m_Client.clearDt, clearDt);
+ }
+ void AddShadowCaster() { CHECK_STATS_ENABLED
+ m_Client.shadowCasters++;
+ }
+ void AddShadowCasters(int count) { CHECK_STATS_ENABLED
+ m_Client.shadowCasters+=count;
+ }
+
+
+ virtual void SetScreenParams( int width, int height, int backbufferBPP, int frontbufferBPP, int depthBPP, int fsaa );
+
+ void ChangeRenderTextureBytes (int deltaBytes)
+ {
+ SInt64 deltaBytes64 = static_cast<SInt64>(deltaBytes);
+ m_Memory.renderTextureBytes += deltaBytes64;
+ Assert (m_Memory.renderTextureBytes >= 0);
+ }
+
+ const StateStats& GetStateChanges() const { return m_Changes; }
+ const DrawStats& GetDrawStats() const { return m_Draw; }
+ const ClientStats& GetClientStats() const { return m_Client; }
+ const MemoryStats& GetMemoryStats() const { return m_Memory; }
+
+ float GetClientFrameTime() const { return m_ClientFrameTime; }
+ float GetRenderFrameTime() const { return m_RenderFrameTime; }
+ void AddToClientFrameTime (float dt) { m_ClientFrameTime += dt; }
+
+ // Calculates texture memory usage, and clears the set (to avoid copying the set in multithreaded rendering)
+ virtual void AccumulateUsedTextureUsage();
+
+#if UNITY_IPHONE || UNITY_ANDROID
+ ABSOLUTE_TIME GetHighResolutionAbsTime ();
+#endif
+
+private:
+ friend class GfxDevice;
+ friend class GfxDeviceClient;
+ friend class GfxDeviceWorker;
+
+ // Changing stats from the application should always be done through the device
+ void ResetFrame();
+ void ResetClientStats();
+ void BeginFrameStats();
+ void EndClientFrameStats();
+ void EndRenderFrameStats();
+
+ // Saves/restores everything except the memory stats (those have to be properly tracked
+ // all the time).
+ void CopyAllDrawStats( const GfxDeviceStats& s );
+ void CopyClientStats( const GfxDeviceStats& s );
+
+private:
+ StateStats m_Changes;
+ DrawStats m_Draw;
+ ClientStats m_Client;
+ MemoryStats m_Memory;
+ double m_EnableStartTime;
+ float m_ClientFrameTime;
+ float m_RenderFrameTime;
+ bool m_StatsEnabled;
+};
diff --git a/Runtime/GfxDevice/GfxDeviceTypes.h b/Runtime/GfxDevice/GfxDeviceTypes.h
new file mode 100644
index 0000000..9468a7e
--- /dev/null
+++ b/Runtime/GfxDevice/GfxDeviceTypes.h
@@ -0,0 +1,616 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+#include "GfxDeviceConfigure.h"
+#include "Runtime/Math/Matrix4x4.h"
+
+// Never change the enum values!
+// They are used in low level native plugin interface.
+enum GfxDeviceRenderer
+{
+ kGfxRendererOpenGL = 0,
+ kGfxRendererD3D9 = 1,
+ kGfxRendererD3D11 = 2,
+ kGfxRendererGCM = 3,
+ kGfxRendererNull = 4,
+ kGfxRendererHollywood = 5,
+ kGfxRendererXenon = 6,
+ //kGfxRendererOpenGLES = 7, // removed
+ kGfxRendererOpenGLES20Mobile = 8,
+ kGfxRendererMolehill = 9,
+ kGfxRendererOpenGLES20Desktop = 10,
+ kGfxRendererOpenGLES30 = 11,
+ kGfxRendererCount = 12
+};
+
+enum GfxThreadingMode
+{
+ kGfxThreadingModeDirect,
+ kGfxThreadingModeThreaded,
+ kGfxThreadingModeNonThreaded,
+ kGfxThreadingModeAutoDetect
+};
+
+#if UNITY_WIN
+typedef HWND NativeWindow;
+#else
+typedef unsigned long NativeWindow;
+#endif
+
+enum
+{
+ #if GFX_OPENGLESxx_ONLY || GFX_SUPPORTS_MOLEHILL
+ kMaxSupportedTextureUnits = 8,
+ #else
+ kMaxSupportedTextureUnits = 16,
+ #endif
+ kMaxSupportedTextureUnitsGLES = 8,
+
+ kMaxSupportedVertexLights = 8,
+ kMaxSupportedTextureCoords = 8,
+
+ kMaxSupportedRenderTargets = 4,
+ kMaxSupportedConstantBuffers = 16,
+ kMaxSupportedComputeResources = 16,
+};
+
+
+enum TextureDimension
+{
+ kTexDimUnknown = -1, // unknown
+ kTexDimNone = 0, // no texture
+ kTexDimDeprecated1D, // not used anymore, value there for backwards compatibility in serialization
+ kTexDim2D,
+ kTexDim3D,
+ kTexDimCUBE,
+ kTexDimAny,
+
+ kTexDimCount, // keep this last!
+ kTexDimForce32Bit = 0x7fffffff
+};
+
+
+// this is kept as UInt32 because it's serialized in some places; to ensure that it's 32
+// bits everywhere.
+
+
+typedef UInt32 TextureFormat;
+enum
+{
+ kTexFormatAlpha8 = 1,
+ kTexFormatARGB4444 = 2,
+ kTexFormatRGB24 = 3,
+ kTexFormatRGBA32 = 4,
+ kTexFormatARGB32 = 5,
+ kTexFormatARGBFloat = 6, // only for internal use at runtime
+ kTexFormatRGB565 = 7,
+ kTexFormatBGR24 = 8,
+ // This one is for internal use; storage is 16 bits/pixel; samples
+ // as Alpha (OpenGL) or RGB (D3D9). Can be reduced to 8 bit alpha/luminance on lower hardware.
+ // Why it's not Luminance on GL: for some reason alpha seems to be faster.
+ kTexFormatAlphaLum16 = 9,
+ kTexFormatDXT1 = 10,
+ kTexFormatDXT3 = 11,
+ kTexFormatDXT5 = 12,
+ kTexFormatRGBA4444 = 13,
+
+ kTexFormatPCCount = 14,
+
+ kTexReserved1 = 14, // Use reservedX when adding a new 'PC' texture format
+ kTexReserved2 = 15,
+ kTexReserved3 = 16,
+ kTexReserved4 = 17,
+ kTexReserved5 = 18,
+ kTexReserved6 = 19,
+ // [20..27] used to be Wii-specific formats before Unity 4.0
+ kTexReserved11 = 28,
+ kTexReserved12 = 29,
+
+ // iPhone
+ kTexFormatPVRTC_RGB2 = 30,
+ kTexFormatPVRTC_RGBA2 = 31,
+
+ kTexFormatPVRTC_RGB4 = 32,
+ kTexFormatPVRTC_RGBA4 = 33,
+
+ kTexFormatETC_RGB4 = 34,
+
+ kTexFormatATC_RGB4 = 35,
+ kTexFormatATC_RGBA8 = 36,
+
+ // Pixels returned by iPhone camera
+ kTexFormatBGRA32 = 37,
+
+ kTexFormatFlashATF_RGB_DXT1 = 38,
+ kTexFormatFlashATF_RGBA_JPG = 39,
+ kTexFormatFlashATF_RGB_JPG = 40,
+
+ // EAC and ETC2 compressed formats, mandated by OpenGL ES 3.0
+ kTexFormatEAC_R = 41,
+ kTexFormatEAC_R_SIGNED = 42,
+ kTexFormatEAC_RG = 43,
+ kTexFormatEAC_RG_SIGNED = 44,
+ kTexFormatETC2_RGB = 45,
+ kTexFormatETC2_RGBA1 = 46,
+ kTexFormatETC2_RGBA8 = 47,
+
+ // ASTC. The RGB and RGBA formats are internally identical, we just need to carry the has-alpha information somehow
+ kTexFormatASTC_RGB_4x4 = 48,
+ kTexFormatASTC_RGB_5x5 = 49,
+ kTexFormatASTC_RGB_6x6 = 50,
+ kTexFormatASTC_RGB_8x8 = 51,
+ kTexFormatASTC_RGB_10x10 = 52,
+ kTexFormatASTC_RGB_12x12 = 53,
+
+ kTexFormatASTC_RGBA_4x4 = 54,
+ kTexFormatASTC_RGBA_5x5 = 55,
+ kTexFormatASTC_RGBA_6x6 = 56,
+ kTexFormatASTC_RGBA_8x8 = 57,
+ kTexFormatASTC_RGBA_10x10 = 58,
+ kTexFormatASTC_RGBA_12x12 = 59,
+
+ kTexFormatTotalCount = 60 // keep this last!
+};
+
+
+enum TextureUsageMode
+{
+ kTexUsageNone = 0,
+ kTexUsageLightmapDoubleLDR,
+ kTexUsageLightmapRGBM,
+ kTexUsageNormalmapDXT5nm,
+ kTexUsageNormalmapPlain,
+};
+
+
+enum TextureColorSpace
+{
+ kTexColorSpaceLinear = 0,
+ kTexColorSpaceSRGB,
+ kTexColorSpaceSRGBXenon
+};
+
+
+enum TextureFilterMode
+{
+ kTexFilterNearest = 0,
+ kTexFilterBilinear,
+ kTexFilterTrilinear,
+ kTexFilterCount // keep this last!
+};
+
+
+enum TextureWrapMode
+{
+ kTexWrapRepeat,
+ kTexWrapClamp,
+ kTexWrapCount // keep this last!
+};
+
+
+#if UNITY_EDITOR
+// enum values to match 0..100 scale used on some platforms
+enum TextureCompressionQuality
+{
+ kTexCompressionFast = 0,
+ kTexCompressionNormal = 50,
+ kTexCompressionBest = 100
+};
+#endif
+
+
+enum RenderTextureFormat
+{
+ kRTFormatARGB32 = 0, // ARGB, 8 bit/channel
+ kRTFormatDepth, // whatever is for "depth texture": Depth16 on GL, R32F on D3D9, ...
+ kRTFormatARGBHalf, // ARGB, 16 bit floating point/channel
+ kRTFormatShadowMap, // whatever is "native" (with built-in comparisons) shadow map format
+ kRTFormatRGB565,
+ kRTFormatARGB4444,
+ kRTFormatARGB1555,
+ kRTFormatDefault,
+ kRTFormatA2R10G10B10,
+ kRTFormatDefaultHDR,
+ kRTFormatARGB64,
+ kRTFormatARGBFloat,
+ kRTFormatRGFloat,
+ kRTFormatRGHalf,
+ kRTFormatRFloat,
+ kRTFormatRHalf,
+ kRTFormatR8,
+ kRTFormatARGBInt,
+ kRTFormatRGInt,
+ kRTFormatRInt,
+ kRTFormatBGRA32,
+ kRTFormatCount // keep this last!
+};
+
+
+enum RenderTextureReadWrite
+{
+ kRTReadWriteDefault = 0, // The 'correct' state for the given position in the render pipeline
+ kRTReadWriteLinear, // No sRGB read / write
+ kRTReadWriteSRGB, // sRGB read / write
+ kRTSRGBCount // keep this last!
+};
+
+
+enum DepthBufferFormat
+{
+ kDepthFormatNone = 0, // no depth buffer
+ kDepthFormat16, // 16 bit depth buffer
+ kDepthFormat24, // 24 bit depth buffer
+ kDepthFormatCount // keep this last!
+};
+
+
+enum SurfaceCreateFlags
+{
+ // unused (1<<0),
+ kSurfaceCreateMipmap = (1<<1),
+ kSurfaceCreateSRGB = (1<<2),
+ kSurfaceCreateShadowmap = (1<<3),
+ kSurfaceCreateRandomWrite = (1<<4),
+ kSurfaceCreateSampleOnly = (1<<5),
+ kSurfaceCreateNeverUsed = (1<<6),
+ kSurfaceCreateAutoGenMips = (1<<7),
+};
+
+
+enum StencilOp
+{
+ kStencilOpKeep = 0,
+ kStencilOpZero,
+ kStencilOpReplace,
+ kStencilOpIncrSat,
+ kStencilOpDecrSat,
+ kStencilOpInvert,
+ kStencilOpIncrWrap,
+ kStencilOpDecrWrap,
+ kStencilOpCount
+};
+
+
+enum BlendOp
+{
+ kBlendOpAdd = 0,
+ kBlendOpSub,
+ kBlendOpRevSub,
+ kBlendOpMin,
+ kBlendOpMax,
+ kBlendOpLogicalClear,
+ kBlendOpLogicalSet,
+ kBlendOpLogicalCopy,
+ kBlendOpLogicalCopyInverted,
+ kBlendOpLogicalNoop,
+ kBlendOpLogicalInvert,
+ kBlendOpLogicalAnd,
+ kBlendOpLogicalNand,
+ kBlendOpLogicalOr,
+ kBlendOpLogicalNor,
+ kBlendOpLogicalXor,
+ kBlendOpLogicalEquiv,
+ kBlendOpLogicalAndReverse,
+ kBlendOpLogicalAndInverted,
+ kBlendOpLogicalOrReverse,
+ kBlendOpLogicalOrInverted,
+ kBlendOpCount,
+};
+
+
+enum BlendMode
+{
+ kBlendZero = 0,
+ kBlendOne,
+ kBlendDstColor,
+ kBlendSrcColor,
+ kBlendOneMinusDstColor,
+ kBlendSrcAlpha,
+ kBlendOneMinusSrcColor,
+ kBlendDstAlpha,
+ kBlendOneMinusDstAlpha,
+ kBlendSrcAlphaSaturate,
+ kBlendOneMinusSrcAlpha,
+ kBlendCount
+};
+
+
+enum CompareFunction
+{
+ kFuncUnknown = -1,
+ kFuncDisabled = 0,
+ kFuncNever,
+ kFuncLess,
+ kFuncEqual,
+ kFuncLEqual,
+ kFuncGreater,
+ kFuncNotEqual,
+ kFuncGEqual,
+ kFuncAlways,
+ kFuncCount
+};
+
+
+enum CullMode
+{
+ kCullUnknown = -1,
+ kCullOff = 0,
+ kCullFront,
+ kCullBack,
+ kCullCount
+};
+
+
+enum ColorWriteMask
+{
+ kColorWriteA = 1,
+ kColorWriteB = 2,
+ kColorWriteG = 4,
+ kColorWriteR = 8,
+ KColorWriteAll = (kColorWriteR|kColorWriteG|kColorWriteB|kColorWriteA)
+};
+
+
+enum ColorMaterialMode
+{
+ kColorMatUnknown = -1,
+ kColorMatDisabled = 0,
+ kColorMatEmission,
+ kColorMatAmbientAndDiffuse,
+ kColorMatTypeCount
+};
+
+
+enum TexGenMode
+{
+ kTexGenUnknown = -1,
+ kTexGenDisabled = 0,
+ kTexGenSphereMap, // Spherical reflection map
+ kTexGenObject, // Object space
+ kTexGenEyeLinear, // Projected Eye space
+ kTexGenCubeReflect, // Cubemap reflection calculation
+ kTexGenCubeNormal, // Cubemap normal calculation
+ kTexGenCount
+};
+
+
+enum ShaderType // bit masks!
+{
+ kShaderNone = 0,
+ kShaderVertex = 1,
+ kShaderFragment = 2,
+ kShaderGeometry = 3,
+ kShaderHull = 4,
+ kShaderDomain = 5,
+ kShaderTypeCount // keep this last!
+};
+
+enum ShaderImplType
+{
+ kShaderImplUndefined = -1,
+
+ // Must match ShaderType!
+ kShaderImplVertex = 1, // Vertex shader
+ kShaderImplFragment = 2, // Fragment shader
+ kShaderImplGeometry = 3,
+ kShaderImplHull = 4,
+ kShaderImplDomain = 5,
+
+
+ kShaderImplBoth = 6, // Vertex+fragment (e.g. GLSL)
+};
+
+
+enum ShaderParamType
+{
+ kShaderParamFloat = 0,
+ kShaderParamInt,
+ kShaderParamBool,
+ kShaderParamTypeCount
+};
+
+
+// Ordering is like this so it matches valid D3D9 FVF layouts
+// Range must fit into an SInt8
+enum ShaderChannel
+{
+ kShaderChannelNone = -1,
+ kShaderChannelVertex = 0, // Vertex (vector3)
+ kShaderChannelNormal, // Normal (vector3)
+ kShaderChannelColor, // Vertex color
+ kShaderChannelTexCoord0, // UV set 0 (vector2)
+ kShaderChannelTexCoord1, // UV set 1 (vector2)
+ kShaderChannelTangent, // Tangent (vector4)
+ kShaderChannelCount, // Keep this last!
+};
+
+
+enum ShaderChannelMask
+{
+ kShaderChannelsAll = ( (1<<kShaderChannelVertex) | (1<<kShaderChannelNormal) | (1<<kShaderChannelColor) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelTexCoord1) | (1<<kShaderChannelTangent) ),
+ kShaderChannelsHot = ( (1<<kShaderChannelVertex) | (1<<kShaderChannelNormal) | (1<<kShaderChannelTangent) ),
+ kShaderChannelsCold= ( (1<<kShaderChannelColor) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelTexCoord1) ),
+};
+
+
+enum VertexComponent
+{
+ kVertexCompNone = 0,
+ kVertexCompVertex,
+ kVertexCompColor,
+ kVertexCompNormal,
+ kVertexCompTexCoord,
+ kVertexCompTexCoord0, kVertexCompTexCoord1, kVertexCompTexCoord2, kVertexCompTexCoord3,
+ kVertexCompTexCoord4, kVertexCompTexCoord5, kVertexCompTexCoord6, kVertexCompTexCoord7,
+ kVertexCompAttrib0, kVertexCompAttrib1, kVertexCompAttrib2, kVertexCompAttrib3,
+ kVertexCompAttrib4, kVertexCompAttrib5, kVertexCompAttrib6, kVertexCompAttrib7,
+ kVertexCompAttrib8, kVertexCompAttrib9, kVertexCompAttrib10, kVertexCompAttrib11,
+ kVertexCompAttrib12, kVertexCompAttrib13, kVertexCompAttrib14, kVertexCompAttrib15,
+ kVertexCompCount // keep this last!
+};
+
+
+enum VertexChannelFormat
+{
+ kChannelFormatFloat = 0,
+ kChannelFormatFloat16,
+ kChannelFormatColor,
+ kChannelFormatByte,
+ kChannelFormatCount
+};
+
+
+enum FogMode
+{
+ kFogUnknown = -1,
+ kFogDisabled = 0,
+ kFogLinear,
+ kFogExp,
+ kFogExp2,
+ kFogModeCount // keep this last!
+};
+
+
+enum NormalizationMode
+{
+ kNormalizationUnknown = -1,
+ kNormalizationDisabled = 0,
+ kNormalizationScale = 1, // = kUniformScaleTransform,
+ kNormalizationFull = 2 // = kNonUniformScaleTransform
+};
+
+
+enum CubemapFace
+{
+ kCubeFaceUnknown = -1,
+ kCubeFacePX = 0,
+ kCubeFaceNX,
+ kCubeFacePY,
+ kCubeFaceNY,
+ kCubeFacePZ,
+ kCubeFaceNZ,
+};
+
+
+enum GfxPrimitiveType
+{
+ kPrimitiveTriangles = 0,
+ kPrimitiveTriangleStripDeprecated,
+ kPrimitiveQuads,
+ kPrimitiveLines,
+ kPrimitiveLineStrip,
+ kPrimitivePoints,
+
+ kPrimitiveTypeCount, // keep this last!
+ kPrimitiveForce32BitInt = 0x7fffffff // force 32 bit enum size
+};
+
+
+enum GfxImmediateType
+{
+ kImmediateVertex,
+ kImmediateNormal,
+ kImmediateColor,
+ kImmediateTexCoordAll,
+ kImmediateTexCoord,
+};
+
+
+enum GfxClearFlags
+{
+ kGfxClearColor = (1<<0),
+ kGfxClearDepth = (1<<1),
+ kGfxClearStencil = (1<<2),
+ kGfxClearDepthStencil = kGfxClearDepth | kGfxClearStencil,
+ kGfxClearAll = kGfxClearColor | kGfxClearDepth | kGfxClearStencil,
+};
+
+
+struct TextureID
+{
+ explicit TextureID() : m_ID(0) { }
+ explicit TextureID(unsigned int i) : m_ID(i) { }
+ bool operator==(const TextureID& o) const { return m_ID==o.m_ID; }
+ bool operator!=(const TextureID& o) const { return m_ID!=o.m_ID; }
+ bool operator < (const TextureID& o) const { return m_ID<o.m_ID; }
+ unsigned int m_ID;
+};
+
+
+struct ComputeBufferID {
+ explicit ComputeBufferID() : m_ID(0) { }
+ explicit ComputeBufferID(unsigned int i) : m_ID(i) { }
+ bool IsValid() const { return m_ID != 0; }
+ bool operator==(const ComputeBufferID& o) const { return m_ID==o.m_ID; }
+ bool operator!=(const ComputeBufferID& o) const { return m_ID!=o.m_ID; }
+ bool operator < (const ComputeBufferID& o) const { return m_ID<o.m_ID; }
+ unsigned int m_ID;
+};
+
+
+#define VERTEX_FORMAT1(a) (1 << kShaderChannel##a)
+#define VERTEX_FORMAT2(a,b) ((1 << kShaderChannel##a) | (1 << kShaderChannel##b))
+#define VERTEX_FORMAT3(a,b,c) ((1 << kShaderChannel##a) | (1 << kShaderChannel##b) | (1 << kShaderChannel##c))
+#define VERTEX_FORMAT4(a,b,c,d) ((1 << kShaderChannel##a) | (1 << kShaderChannel##b) | (1 << kShaderChannel##c) | (1 << kShaderChannel##d))
+#define VERTEX_FORMAT5(a,b,c,d,e) ((1 << kShaderChannel##a) | (1 << kShaderChannel##b) | (1 << kShaderChannel##c) | (1 << kShaderChannel##d) | (1 << kShaderChannel##e))
+#define VERTEX_FORMAT6(a,b,c,d,e,f) ((1 << kShaderChannel##a) | (1 << kShaderChannel##b) | (1 << kShaderChannel##c) | (1 << kShaderChannel##d) | (1 << kShaderChannel##e) | (1 << kShaderChannel##f))
+
+#define kMaxVertexStreams 4
+
+
+enum ComputeBufferFlags
+{
+ kCBFlagNone = 0,
+ kCBFlagRaw = (1<<0),
+ kCBFlagAppend = (1<<1),
+ kCBFlagCounter = (1<<2),
+ kCBFlagTypeMask = kCBFlagRaw | kCBFlagAppend | kCBFlagCounter,
+
+ kCBFlagDrawIndirect = (1<<8),
+};
+
+enum BuiltinSamplerState
+{
+ kSamplerPointClamp = 0,
+ kSamplerLinearClamp,
+ kSamplerPointRepeat,
+ kSamplerLinearRepeat,
+
+ kBuiltinSamplerStateCount, // keep this last!
+ kBuiltinSamplerStateForce32Bit = 0x7fffffff
+};
+
+struct TexEnvProperties
+{
+ TextureID textureID; // 4
+ int texturePropertyID; // 4
+ float mipBias; // 4
+ UInt32 texDim : 4; // 4
+ UInt32 texGen : 4;
+ UInt32 identityMatrix : 1;
+ // ---- 16 bytes
+};
+
+struct TexEnvData : public TexEnvProperties // 16
+{
+ Matrix4x4f matrix; // 64
+ // ---- 80 bytes
+};
+
+#if !UNITY_SPU
+#include "GfxDeviceResources.h"
+#endif
+
+#if UNITY_XENON
+enum HiZstate
+{
+ kHiZDisable = 0,
+ kHiZEnable,
+ kHiZAuto
+};
+enum HiSflush
+{
+ kHiSflush_async = 0,
+ kHiSflush_sync
+};
+#endif
diff --git a/Runtime/GfxDevice/GfxDeviceWindow.cpp b/Runtime/GfxDevice/GfxDeviceWindow.cpp
new file mode 100644
index 0000000..c6e4933
--- /dev/null
+++ b/Runtime/GfxDevice/GfxDeviceWindow.cpp
@@ -0,0 +1,55 @@
+#include "UnityPrefix.h"
+#include "GfxDeviceWindow.h"
+
+
+GfxDeviceWindow::GfxDeviceWindow (NativeWindow window, int width, int height, DepthBufferFormat depthFormat, int antiAlias)
+ : m_Window (window)
+ , m_Width (0)
+ , m_Height (0)
+ , m_InvalidState (true)
+ , m_CanUseBlitOptimization (false)
+{
+ //Reshape (width, height, depthFormat, antiAlias);
+}
+
+GfxDeviceWindow::~GfxDeviceWindow ()
+{
+}
+
+bool GfxDeviceWindow::Reshape (int width, int height, DepthBufferFormat depthFormat, int antiAlias)
+{
+ m_InvalidState = false;
+
+ AssertIf (!m_Window);
+
+ m_Width = width;
+ m_Height = height;
+
+ if (m_Width <= 0 || m_Height <= 0)
+ {
+ m_InvalidState = true;
+ }
+
+ return !m_InvalidState;
+}
+
+
+bool GfxDeviceWindow::BeginRendering ()
+{
+ if (m_InvalidState)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool GfxDeviceWindow::EndRendering (bool presentContent)
+{
+ if (m_InvalidState)
+ {
+ return false;
+ }
+
+ return true;
+}
diff --git a/Runtime/GfxDevice/GfxDeviceWindow.h b/Runtime/GfxDevice/GfxDeviceWindow.h
new file mode 100644
index 0000000..0b8b022
--- /dev/null
+++ b/Runtime/GfxDevice/GfxDeviceWindow.h
@@ -0,0 +1,36 @@
+#ifndef GFXDEVICEWINDOW_H
+#define GFXDEVICEWINDOW_H
+
+#include "GfxDeviceTypes.h"
+
+class GfxDeviceWindow
+{
+protected:
+ NativeWindow m_Window;
+ int m_Width;
+ int m_Height;
+ bool m_InvalidState;
+ bool m_CanUseBlitOptimization;
+public:
+ GfxDeviceWindow (NativeWindow window, int width, int height, DepthBufferFormat depthFormat, int antiAlias);
+ virtual ~GfxDeviceWindow();
+
+ //Returns true if reshaping was successful
+ virtual bool Reshape( int width, int height, DepthBufferFormat depthFormat, int antiAlias );
+
+ //Returns true if succeeded to prepare for rendering
+ virtual bool BeginRendering();
+
+ virtual void SetAsActiveWindow () { };
+
+ //Returns true if succeeded to finish rendering
+ virtual bool EndRendering( bool presentContent );
+
+ inline bool CanUseBlitOptimization() const { return m_CanUseBlitOptimization; }
+
+ inline int GetWidth() const { return m_Width; }
+ inline int GetHeight() const { return m_Height; }
+ inline NativeWindow GetHandle () const { return m_Window; }
+};
+
+#endif
diff --git a/Runtime/GfxDevice/GfxDisplayList.h b/Runtime/GfxDevice/GfxDisplayList.h
new file mode 100644
index 0000000..d245abf
--- /dev/null
+++ b/Runtime/GfxDevice/GfxDisplayList.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "Runtime/Threads/ThreadSharedObject.h"
+
+class GfxDisplayList : public ThreadSharedObject
+{
+public:
+ virtual void Call() = 0;
+};
diff --git a/Runtime/GfxDevice/GfxPatchInfo.cpp b/Runtime/GfxDevice/GfxPatchInfo.cpp
new file mode 100644
index 0000000..8ba76f1
--- /dev/null
+++ b/Runtime/GfxDevice/GfxPatchInfo.cpp
@@ -0,0 +1,99 @@
+#include "UnityPrefix.h"
+#include "GfxPatchInfo.h"
+#include "External/shaderlab/Library/shaderlab.h"
+
+
+void GfxPatchInfo::Reset()
+{
+ for (int pt = 0; pt < GfxPatch::kTypeCount; pt++)
+ m_Patches[pt].resize_uninitialized(0);
+ m_TexEnvPatches.resize_uninitialized(0);
+}
+
+void GfxPatchInfo::AddPatchableFloat(const ShaderLab::FloatVal& val, float& dest, const void* bufferStart,
+ const ShaderLab::PropertySheet* props)
+{
+ if (val.var.IsValid())
+ {
+ using namespace ShaderLab::shaderprops;
+ PropertyLocation location;
+ bool missing;
+ const float& src = GetFloat(props, val.var, location);
+ dest = src;
+ if (IsPatchable(location, missing))
+ {
+ size_t patchOffset = reinterpret_cast<UInt8*>(&dest) - static_cast<const UInt8*>(bufferStart);
+ AddPatch(GfxPatch::kTypeFloat, GfxPatch(val.var, missing ? NULL : &src, patchOffset));
+ }
+ }
+ else
+ dest = val.val;
+}
+
+void GfxPatchInfo::AddPatchableVector(const ShaderLab::VectorVal& val, Vector4f& dest, const void* bufferStart,
+ const ShaderLab::PropertySheet* props)
+{
+ if (val.var.IsValid())
+ {
+ using namespace ShaderLab::shaderprops;
+ PropertyLocation location;
+ bool missing;
+ const Vector4f& src = GetVector(props, val.var, location);
+ dest = src;
+ if (IsPatchable(location, missing))
+ {
+ size_t patchOffset = reinterpret_cast<UInt8*>(&dest) - static_cast<const UInt8*>(bufferStart);
+ AddPatch(GfxPatch::kTypeVector, GfxPatch(val.var, missing ? NULL : &src, patchOffset));
+ }
+ }
+ else
+ {
+ AddPatchableFloat(val.x, dest.x, bufferStart, props);
+ AddPatchableFloat(val.y, dest.y, bufferStart, props);
+ AddPatchableFloat(val.z, dest.z, bufferStart, props);
+ AddPatchableFloat(val.w, dest.w, bufferStart, props);
+ }
+}
+
+bool GfxPatchInfo::AddPatchableTexEnv(const ShaderLab::FastPropertyName& name, const ShaderLab::FastPropertyName& matrixName,
+ TextureDimension dim, TexEnvData* dest, const void* bufferStart, const ShaderLab::PropertySheet* props)
+{
+ using ShaderLab::TexEnv;
+ using namespace ShaderLab::shaderprops;
+
+ // Get TexEnv and prepare data
+ PropertyLocation texEnvLocation;
+ TexEnv* texEnv = GetTexEnv(props, name, dim, texEnvLocation);
+ Assert(texEnv != NULL);
+ texEnv->PrepareData(name.index, matrixName, props, dest);
+
+ bool missing;
+ bool patchable = IsPatchable(texEnvLocation, missing);
+ if (!missing)
+ {
+ UInt32 patchFlags = 0;
+ if (patchable)
+ patchFlags |= GfxTexEnvPatch::kPatchProperties;
+
+ if (matrixName.IsValid() || texEnv->GetMatrixName().IsValid())
+ {
+ ShaderLab::FastPropertyName finalMatrixName = matrixName.IsValid() ? matrixName : texEnv->GetMatrixName();
+ PropertyLocation matLocation;
+ int size;
+ GetValueProp (props, finalMatrixName, 16, &size, matLocation);
+ bool matMissing;
+ if (IsPatchable(matLocation, matMissing))
+ {
+ patchFlags |= GfxTexEnvPatch::kPatchMatrix;
+ }
+ }
+ if (patchFlags != 0)
+ {
+ size_t offset = reinterpret_cast<const UInt8*>(dest) - static_cast<const UInt8*>(bufferStart);
+ GfxTexEnvPatch patch(name, matrixName, texEnv, dim, offset, patchFlags);
+ AddTexEnvPatch(patch);
+ }
+ return true;
+ }
+ return false;
+}
diff --git a/Runtime/GfxDevice/GfxPatchInfo.h b/Runtime/GfxDevice/GfxPatchInfo.h
new file mode 100644
index 0000000..355b4d5
--- /dev/null
+++ b/Runtime/GfxDevice/GfxPatchInfo.h
@@ -0,0 +1,78 @@
+#pragma once
+
+#include "GfxDeviceTypes.h"
+#include "External/shaderlab/Library/shadertypes.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+struct GfxPatch
+{
+ GfxPatch(const ShaderLab::FastPropertyName& name, const void* src, size_t ofs)
+ : nameIndex(name.index), source(src), patchOffset(ofs) {}
+
+ enum Type
+ {
+ kTypeFloat,
+ kTypeVector,
+ kTypeMatrix,
+ kTypeBuffer,
+ kTypeCount
+ };
+
+ int nameIndex;
+ const void* source;
+ size_t patchOffset;
+};
+
+struct GfxTexEnvPatch
+{
+ GfxTexEnvPatch(const ShaderLab::FastPropertyName& name, const ShaderLab::FastPropertyName& matName,
+ ShaderLab::TexEnv* tex, TextureDimension dim, size_t ofs, UInt32 flags)
+ : nameIndex(name.index), matrixName(matName), texEnv(tex), texDim(dim), patchOffset(ofs), patchFlags(flags) {}
+
+ enum PatchFlag
+ {
+ kPatchProperties = 1 << 0,
+ kPatchMatrix = 1 << 1
+ };
+
+ //ShaderLab::FastPropertyName textureName;
+ int nameIndex;
+ ShaderLab::FastPropertyName matrixName;
+ ShaderLab::TexEnv* texEnv;
+ TextureDimension texDim;
+ size_t patchOffset;
+ UInt32 patchFlags;
+};
+
+class GfxPatchInfo
+{
+public:
+ FORCE_INLINE size_t GetPatchCount(GfxPatch::Type type) const { return m_Patches[type].size(); }
+ FORCE_INLINE const GfxPatch& GetPatch(GfxPatch::Type type, int index) const { return m_Patches[type][index]; }
+ FORCE_INLINE GfxPatch& GetPatch(GfxPatch::Type type, int index) { return m_Patches[type][index]; }
+ FORCE_INLINE void AddPatch(GfxPatch::Type type, const GfxPatch& p) { m_Patches[type].push_back(p); }
+
+ FORCE_INLINE size_t GetTexEnvPatchCount() const { return m_TexEnvPatches.size(); }
+ FORCE_INLINE const GfxTexEnvPatch& GetTexEnvPatch(int index) const { return m_TexEnvPatches[index]; }
+ FORCE_INLINE GfxTexEnvPatch& GetTexEnvPatch(int index) { return m_TexEnvPatches[index]; }
+ FORCE_INLINE void AddTexEnvPatch(const GfxTexEnvPatch& p) { m_TexEnvPatches.push_back(p); }
+
+ void Reset();
+
+ void AddPatchableFloat(const ShaderLab::FloatVal& val, float& dest, const void* bufferStart,
+ const ShaderLab::PropertySheet* props);
+
+ void AddPatchableVector(const ShaderLab::VectorVal& val, Vector4f& dest, const void* bufferStart,
+ const ShaderLab::PropertySheet* props);
+
+ bool AddPatchableTexEnv(const ShaderLab::FastPropertyName& name, const ShaderLab::FastPropertyName& matrixName,
+ TextureDimension dim, TexEnvData* dest, const void* bufferStart, const ShaderLab::PropertySheet* props);
+
+private:
+ typedef dynamic_array<GfxPatch> PatchArray;
+ typedef dynamic_array<GfxTexEnvPatch> TexEnvPatchArray;
+ PatchArray m_Patches[GfxPatch::kTypeCount];
+ TexEnvPatchArray m_TexEnvPatches;
+};
+
diff --git a/Runtime/GfxDevice/GfxTimerQuery.h b/Runtime/GfxDevice/GfxTimerQuery.h
new file mode 100644
index 0000000..bba5174
--- /dev/null
+++ b/Runtime/GfxDevice/GfxTimerQuery.h
@@ -0,0 +1,23 @@
+#ifndef GFXTIMERQUERY_H
+#define GFXTIMERQUERY_H
+
+#include "Runtime/Profiler/TimeHelper.h"
+#include "Runtime/Utilities/LinkedList.h"
+
+class GfxTimerQuery : public ListElement
+{
+public:
+ virtual ~GfxTimerQuery() {}
+
+ enum
+ {
+ kWaitRenderThread = 1 << 0,
+ kWaitClientThread = 1 << 1,
+ kWaitAll = (kWaitClientThread | kWaitRenderThread)
+ };
+
+ virtual void Measure() = 0;
+ virtual ProfileTimeFormat GetElapsed(UInt32 flags) = 0;
+};
+
+#endif
diff --git a/Runtime/GfxDevice/GpuProgram.cpp b/Runtime/GfxDevice/GpuProgram.cpp
new file mode 100644
index 0000000..688fdc7
--- /dev/null
+++ b/Runtime/GfxDevice/GpuProgram.cpp
@@ -0,0 +1,695 @@
+#include "UnityPrefix.h"
+#include "GpuProgram.h"
+#include "GfxPatchInfo.h"
+#include "External/shaderlab/Library/ShaderLabErrors.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "GfxDevice.h"
+
+#if GFX_SUPPORTS_OPENGL
+#include "Runtime/GfxDevice/opengl/GpuProgramsGL.h"
+#include "Runtime/GfxDevice/opengl/ArbGpuProgamGL.h"
+#endif
+#if GFX_SUPPORTS_OPENGLES30
+#include "Runtime/GfxDevice/opengles30/GpuProgramsGLES30.h"
+#endif
+#if GFX_SUPPORTS_OPENGLES20
+#include "Runtime/GfxDevice/opengles20/GpuProgramsGLES20.h"
+#endif
+#if GFX_SUPPORTS_D3D9
+#include "Runtime/GfxDevice/d3d/GpuProgramsD3D.h"
+#endif
+#if GFX_SUPPORTS_D3D11
+#include "d3d11/GpuProgramsD3D11.h"
+#endif
+#if GFX_SUPPORTS_GCM
+#include "GpuProgramPS3.h"
+#endif
+#if GFX_SUPPORTS_XENON
+#include "GpuProgramsXenon.h"
+#endif
+#if GFX_SUPPORTS_MOLEHILL
+#include "Runtime/GfxDevice/molehill/GpuProgramsMH.h"
+#endif
+
+
+// --------------------------------------------------------------------------
+void GpuProgramParameters::AddValueParam (const ValueParameter& param)
+{
+ m_ValueParams.push_back (param);
+ m_ValuesSize += param.m_ArraySize * param.m_RowCount * param.m_ColCount * sizeof(float);
+ m_Status = kDirty;
+}
+
+void GpuProgramParameters::AddTextureParam (const TextureParameter& param)
+{
+ m_TextureParams.push_back (param);
+ m_ValuesSize += sizeof(TexEnvData);
+ m_Status = kDirty;
+}
+
+void GpuProgramParameters::AddVectorParam (int index, ShaderParamType type, int dimension, const char* nameStr, int cbIndex, PropertyNamesSet* outNames)
+{
+ int cbNameID = -1;
+ #if GFX_SUPPORTS_CONSTANT_BUFFERS
+ if (cbIndex >= 0)
+ cbNameID = m_ConstantBuffers[cbIndex].m_Name.index;
+ #endif
+ if (m_BuiltinParams.CheckVectorParam (nameStr, index, dimension, cbNameID))
+ return;
+
+ ValueParameterArray& params = GetValuesArray(cbIndex);
+ const FastPropertyName name = ShaderLab::Property(nameStr);
+ params.push_back (ValueParameter (name, type, index, 1, 1, dimension));
+ const UInt32 size = sizeof(Vector4f);
+ m_ValuesSize += size;
+ m_Status = kDirty;
+
+ if (outNames && !name.IsBuiltin() && outNames->names.insert(name.index).second)
+ outNames->valueSize += size;
+}
+
+void GpuProgramParameters::AddMatrixParam (int index, const char* nameStr, int rowCount, int colCount, int cbIndex, PropertyNamesSet* outNames)
+{
+ Assert(rowCount <= 4);
+
+ int cbNameID = -1;
+ #if GFX_SUPPORTS_CONSTANT_BUFFERS
+ if (cbIndex >= 0)
+ cbNameID = m_ConstantBuffers[cbIndex].m_Name.index;
+ #endif
+
+ if (m_BuiltinParams.CheckMatrixParam (nameStr, index, rowCount, colCount, cbNameID))
+ return;
+
+ ValueParameterArray& params = GetValuesArray(cbIndex);
+ const FastPropertyName name = ShaderLab::Property(nameStr);
+ params.push_back (ValueParameter (name, kShaderParamFloat, index, 1, rowCount, colCount));
+ const UInt32 size = sizeof(int) + sizeof(Matrix4x4f);
+ m_ValuesSize += size;
+ m_Status = kDirty;
+
+ if (outNames && !name.IsBuiltin() && outNames->names.insert(name.index).second)
+ outNames->valueSize += size;
+}
+
+void GpuProgramParameters::AddTextureParam (int index, int samplerIndex, const char* nameStr, TextureDimension dim, PropertyNamesSet* outNames)
+{
+ const FastPropertyName name = ShaderLab::Property(nameStr);
+ m_TextureParams.push_back (TextureParameter (name, index, samplerIndex, dim));
+ const UInt32 size = sizeof(TexEnvData);
+ m_ValuesSize += size;
+ m_Status = kDirty;
+
+ if (outNames && !name.IsBuiltin() && outNames->names.insert(name.index).second)
+ outNames->valueSize += size;
+}
+
+void GpuProgramParameters::AddBufferParam (int index, const char* nameStr, PropertyNamesSet* outNames)
+{
+ const FastPropertyName name = ShaderLab::Property(nameStr);
+ m_BufferParams.push_back (BufferParameter (name, index));
+ const UInt32 size = sizeof(ComputeBufferID);
+ m_ValuesSize += size;
+
+ if (outNames && !name.IsBuiltin() && outNames->names.insert(name.index).second)
+ outNames->valueSize += size;
+}
+
+const GpuProgramParameters::ValueParameterArray& GpuProgramParameters::GetValueParams() const
+{
+ if (IsDirty())
+ const_cast<GpuProgramParameters*>(this)->MakeReady();
+ return m_ValueParams;
+}
+
+const GpuProgramParameters::TextureParameter* GpuProgramParameters::FindTextureParam(const FastPropertyName& name, TextureDimension dim) const
+{
+ for (TextureParameterList::const_iterator it = m_TextureParams.begin(), itEnd = m_TextureParams.end(); it != itEnd; ++it)
+ {
+ const TextureParameter& tex = *it;
+ if (tex.m_Name == name && tex.m_Dim == dim)
+ return &tex;
+ }
+ return NULL;
+}
+
+
+const GpuProgramParameters::ValueParameter* GpuProgramParameters::FindParam (const FastPropertyName& name, int* outCBIndex) const
+{
+ DebugAssert(!IsDirty());
+ NameToValueIndex key = { name.index, 0, 0 };
+ // Binary search using lower_bound should be faster, but seems slower...
+ //NameToValueIndexMap::const_iterator pos = std::lower_bound(m_NamedParams.begin(), m_NamedParams.end(), key);
+ NameToValueIndexMap::const_iterator pos = std::find(m_NamedParams.begin(), m_NamedParams.end(), key);
+ const GpuProgramParameters::ValueParameter* param = NULL;
+ if (pos != m_NamedParams.end())
+ {
+ const ValueParameterArray& params = GetValuesArray(pos->cbIndex);
+ param = &params[pos->valueIndex];
+ DebugAssert(name.index == param->m_Name.index);
+
+ #if GFX_SUPPORTS_CONSTANT_BUFFERS
+ if (outCBIndex)
+ *outCBIndex = pos->cbIndex;
+ #endif
+ }
+ return param;
+}
+
+
+template <typename T>
+static inline UInt8* PushToBuffer (UInt8* buffer, const T& val)
+{
+ memcpy (buffer, &val, sizeof(val));
+ return buffer + sizeof(val);
+}
+
+static UInt8* PrepareValueParameters (
+ const GpuProgramParameters::ValueParameterArray& valueParams,
+ const ShaderLab::PropertySheet* props,
+ UInt8* buffer,
+ const UInt8* bufferStart,
+ GfxPatchInfo* outPatchInfo)
+{
+ using namespace ShaderLab;
+ using namespace ShaderLab::shaderprops;
+ PropertyLocation location;
+ bool missing;
+
+ GpuProgramParameters::ValueParameterArray::const_iterator valueParamsEnd = valueParams.end();
+ for (GpuProgramParameters::ValueParameterArray::const_iterator i = valueParams.begin(); i != valueParamsEnd; ++i)
+ {
+ FastPropertyName name = i->m_Name;
+ int rows = i->m_RowCount;
+ int cols = i->m_ColCount;
+ int arrSize = i->m_ArraySize;
+ if (rows == 1 && arrSize <= 1)
+ {
+ // single floats and vectors
+ if (cols == 1) {
+ const float& src = GetFloat(props, name, location);
+ if (outPatchInfo && IsPatchable(location, missing)) {
+ outPatchInfo->AddPatch(GfxPatch::kTypeFloat,
+ GfxPatch(name, missing ? NULL : &src, buffer - bufferStart));
+ }
+ Vector4f val(src,0,0,0);
+ buffer = PushToBuffer (buffer, val);
+ } else {
+ const Vector4f& src = GetVector(props, name, location);
+ if (outPatchInfo && IsPatchable(location, missing)) {
+ outPatchInfo->AddPatch(GfxPatch::kTypeVector,
+ GfxPatch(name, missing ? NULL : &src, buffer - bufferStart));
+ }
+ buffer = PushToBuffer (buffer, src);
+ }
+ }
+ else
+ {
+ // matrices, arrays etc.
+ int expectSize = arrSize * rows * cols;
+
+ //@TODO: for now, arrays are not really supported yet; and a bunch of
+ // other code around us expect matrices to always be 4x4, even if
+ // actual shader uses less. Right now this is only a problematic case
+ // for Xbox360, since on other platforms the compiler never "strips away"
+ // unreferenced matrix constant slots.
+ // When we actually add shader parameter array support, this place,
+ // and a ton of other places, would have to remove 4x4 assumptions.
+ if (expectSize < 16)
+ expectSize = 16;
+
+ int gotSize;
+ const float* src = GetValueProp (props, name, expectSize, &gotSize, location);
+ DebugAssert (gotSize <= expectSize);
+ memcpy (buffer, &gotSize, sizeof(gotSize)); buffer += sizeof(gotSize); // size
+ if (outPatchInfo && IsPatchable(location, missing)) {
+ //@TODO: once we have arrays here, it's not necessarily matrix anymore!
+ outPatchInfo->AddPatch(GfxPatch::kTypeMatrix,
+ GfxPatch(name, missing ? NULL : src, buffer - bufferStart));
+ }
+ memcpy (buffer, src, gotSize*sizeof(float)); buffer += gotSize*sizeof(float); // value
+ }
+ }
+ return buffer;
+}
+
+UInt8* GpuProgramParameters::PrepareValues (
+ const ShaderLab::PropertySheet* props,
+ UInt8* buffer,
+ const UInt8* bufferStart,
+ GfxPatchInfo* outPatchInfo,
+ bool* outMissingTextures) const
+{
+ using namespace ShaderLab;
+ using namespace ShaderLab::shaderprops;
+
+ // Value parameters
+
+ buffer = PrepareValueParameters (GetValueParams(), props, buffer, bufferStart, outPatchInfo);
+
+ #if GFX_SUPPORTS_CONSTANT_BUFFERS
+ for (size_t i = 0; i < m_ConstantBuffers.size(); ++i)
+ {
+ buffer = PrepareValueParameters (m_ConstantBuffers[i].m_ValueParams, props, buffer, bufferStart, outPatchInfo);
+ }
+ #endif
+
+ // textures
+ TextureParameterList::const_iterator textureParamsEnd = m_TextureParams.end();
+ for (TextureParameterList::const_iterator i = m_TextureParams.begin(); i != textureParamsEnd; ++i)
+ {
+ const GpuProgramParameters::TextureParameter& t = *i;
+ FastPropertyName name = t.m_Name;
+ TextureDimension dim = t.m_Dim;
+ TexEnvData* dest = reinterpret_cast<TexEnvData*>(buffer);
+ if (outPatchInfo)
+ {
+ DebugAssert(outMissingTextures);
+ if (!outPatchInfo->AddPatchableTexEnv(name, FastPropertyName(), t.m_Dim, dest, bufferStart, props))
+ *outMissingTextures = true;
+ }
+ else
+ {
+ TexEnv* src = GetTexEnv(props, name, dim);
+ src->PrepareData(name.index, FastPropertyName(), props, dest);
+ }
+ buffer += sizeof(TexEnvData);
+ }
+
+ // buffers
+ bool missing;
+ BufferParameterArray::const_iterator bufferParamsEnd = m_BufferParams.end();
+ for (BufferParameterArray::const_iterator i = m_BufferParams.begin(); i != bufferParamsEnd; ++i)
+ {
+ const BufferParameter& t = *i;
+
+ PropertyLocation location;
+ const ComputeBufferID& buf = shaderprops::GetComputeBuffer(props, t.m_Name, location);
+ if (outPatchInfo && IsPatchable(location, missing)) {
+ outPatchInfo->AddPatch(GfxPatch::kTypeBuffer,
+ GfxPatch(t.m_Name, missing ? NULL : &buf, buffer - bufferStart));
+ }
+
+ ComputeBufferID* destPtr = reinterpret_cast<ComputeBufferID*>(buffer);
+ *destPtr = buf; buffer += sizeof(ComputeBufferID);
+ }
+
+ return buffer;
+}
+
+void GpuProgramParameters::MakeValueParamsReady (ValueParameterArray& values, int cbIndex)
+{
+ std::sort (values.begin(), values.end());
+ int size = values.size();
+ for (int i = 0; i < size; i++)
+ {
+ const ValueParameter& param = values[i];
+ NameToValueIndex key = { param.m_Name.index, cbIndex, i };
+ m_NamedParams.push_back(key);
+ }
+}
+
+
+void GpuProgramParameters::MakeReady()
+{
+ if (IsDirty())
+ {
+ m_NamedParams.clear();
+ MakeValueParamsReady (m_ValueParams, -1);
+ #if GFX_SUPPORTS_CONSTANT_BUFFERS
+ for (size_t cb = 0; cb < m_ConstantBuffers.size(); ++cb)
+ MakeValueParamsReady (m_ConstantBuffers[cb].m_ValueParams, cb);
+ #endif
+ std::sort(m_NamedParams.begin(), m_NamedParams.end());
+ }
+ m_Status = kReady;
+}
+
+// --------------------------------------------------------------------------
+
+
+bool CheckGpuProgramUsable (const char* str)
+{
+ GfxDeviceRenderer rendererType = GetGfxDevice().GetRenderer();
+
+ // Pretend program is usable if we are building shaders in "nographics" mode
+ if( rendererType == kGfxRendererNull )
+ return true;
+
+ // determine the kind of program from the start of the string
+ if( !strncmp (str, "!!ARBvp1.0", 10) || !strncmp (str, "3.0-!!ARBvp1.0", 14) ) {
+ #if GFX_SUPPORTS_OPENGL
+ if( rendererType == kGfxRendererOpenGL )
+ return true;
+ #endif
+ return false;
+ } else if( !strncmp (str, "!!ARBfp1.0", 10) || !strncmp (str, "3.0-!!ARBfp1.0", 14) ) {
+ #if GFX_SUPPORTS_OPENGL
+ if( rendererType == kGfxRendererOpenGL )
+ return true;
+ #endif
+ return false;
+ } else if (!strncmp (str, "!!GLSL", 6)) {
+ #if GFX_SUPPORTS_OPENGL
+ if( rendererType == kGfxRendererOpenGL )
+ return true;
+ #endif
+ return false;
+ } else if (!strncmp (str, "!!GLES3", 7)) {
+ #if GFX_SUPPORTS_OPENGLES30
+ if( rendererType = kGfxRendererOpenGLES30 )
+ return true;
+ #endif
+ return false;
+ } else if (!strncmp (str, "!!GLES", 6)) {
+ #if GFX_SUPPORTS_OPENGLES20
+ if( rendererType == kGfxRendererOpenGLES20Mobile || rendererType == kGfxRendererOpenGLES20Desktop )
+ return true;
+ #endif
+ #if GFX_SUPPORTS_OPENGLES30
+ if( rendererType = kGfxRendererOpenGLES30 )
+ return true;
+ #endif
+ return false;
+ } else if (!strncmp (str, "!!ATIfs1.0", 10)) {
+ printf_console ("@TODO: found ATIfs1.0 shader; those are not supported anymore\n");
+ return false;
+ } else if (!strncmp (str, "vs_1_1", 6) || !strncmp (str, "vs_2_0", 6) || !strncmp (str, "vs_3_0", 6)) {
+ #if GFX_SUPPORTS_D3D9
+ if( rendererType == kGfxRendererD3D9 )
+ return true;
+ #endif
+ return false;
+ } else if (!strncmp (str, "ps_2_0", 6) || !strncmp (str, "ps_3_0", 6)) {
+ #if GFX_SUPPORTS_D3D9
+ if( rendererType == kGfxRendererD3D9 )
+ return true;
+ #endif
+ return false;
+ } else if (!strncmp (str, "vs_4_0_level_9", strlen("vs_4_0_level_9"))) {
+ #if GFX_SUPPORTS_D3D11
+ if( rendererType == kGfxRendererD3D11 && gGraphicsCaps.d3d11.featureLevel < kDX11Level10_0 )
+ return true;
+ #endif
+ return false;
+ } else if (!strncmp (str, "ps_4_0_level_9", strlen("ps_4_0_level_9"))) {
+ #if GFX_SUPPORTS_D3D11
+ if( rendererType == kGfxRendererD3D11 && gGraphicsCaps.d3d11.featureLevel < kDX11Level10_0 )
+ return true;
+ #endif
+ return false;
+ } else if (!strncmp (str, "vs_dx11", 7) || !strncmp (str, "vs_4_0", 6) || !strncmp (str, "vs_5_0", 6)) {
+ #if GFX_SUPPORTS_D3D11
+ if( rendererType == kGfxRendererD3D11 && gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0 )
+ return true;
+ #endif
+ return false;
+ } else if (!strncmp (str, "ps_dx11", 7) || !strncmp (str, "ps_4_0", 6) || !strncmp (str, "ps_5_0", 6)) {
+ #if GFX_SUPPORTS_D3D11
+ if( rendererType == kGfxRendererD3D11 && gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0 )
+ return true;
+ #endif
+ return false;
+ } else if (!strncmp (str, "gs_4_0", 6) || !strncmp (str, "gs_5_0", 6) || !strncmp (str, "hs_5_0", 6) || !strncmp (str, "ds_5_0", 6)) {
+ #if GFX_SUPPORTS_D3D11
+ if( rendererType == kGfxRendererD3D11 && gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0 )
+ return true;
+ #endif
+ return false;
+ } else if (!strncmp (str, "vs_360", 6)) {
+ #if GFX_SUPPORTS_XENON
+ if( rendererType == kGfxRendererXenon )
+ return true;
+ #endif
+ return false;
+ } else if (!strncmp (str, "ps_360", 6)) {
+ #if GFX_SUPPORTS_XENON
+ if( rendererType == kGfxRendererXenon )
+ return true;
+ #endif
+ return false;
+ } else if (!strncmp (str, "sce_vp_rsx", 10)) {
+ #if GFX_SUPPORTS_GCM
+ if( rendererType == kGfxRendererGCM )
+ return true;
+ #endif
+ return false;
+ } else if (!strncmp (str, "sce_fp_rsx", 10)) {
+ #if GFX_SUPPORTS_GCM
+ if( rendererType == kGfxRendererGCM )
+ return true;
+ #endif
+ return false;
+ } else if (!strncmp (str, "agal_vs", 7)) {
+ #if GFX_SUPPORTS_MOLEHILL
+ if (rendererType == kGfxRendererMolehill)
+ return true;
+ #endif
+ return false;
+ } else if (!strncmp (str, "agal_ps", 7)) {
+ #if GFX_SUPPORTS_MOLEHILL
+ if (rendererType == kGfxRendererMolehill)
+ return true;
+ #endif
+ return false;
+ }
+
+ // If we got here, it's something unrecognized. Return that it's usable,
+ // this will make current SubShader be not supported.
+ return true;
+}
+
+GpuProgram* CreateGpuProgram( const std::string& source, CreateGpuProgramOutput& output )
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+ GpuProgram* program = NULL;
+
+ // determine the kind of program from the start of the string
+ GfxDeviceRenderer rendererType = GetRealGfxDevice().GetRenderer();
+
+ if( !strncmp (source.c_str(), "!!ARBvp1.0", 10) || !strncmp (source.c_str(), "3.0-!!ARBvp1.0", 14) ) {
+ #if GFX_SUPPORTS_OPENGL
+ if( rendererType == kGfxRendererOpenGL )
+ program = new ArbGpuProgram( source, kShaderVertex, kShaderImplVertex );
+ #endif
+ } else if( !strncmp (source.c_str(), "!!ARBfp1.0", 10) || !strncmp (source.c_str(), "3.0-!!ARBfp1.0", 14) ) {
+ #if GFX_SUPPORTS_OPENGL
+ if( rendererType == kGfxRendererOpenGL )
+ program = new ArbGpuProgram( source, kShaderFragment, kShaderImplFragment );
+ #endif
+ } else if (!strncmp (source.c_str(), "!!GLSL", 6)) {
+ #if GFX_SUPPORTS_OPENGL
+ if( rendererType == kGfxRendererOpenGL )
+ program = new GlslGpuProgram( source.substr(6,source.size()-6), output );
+ #endif
+ } else if (!strncmp (source.c_str(), "!!GLES3", 7)) {
+ #if GFX_SUPPORTS_OPENGLES30
+ if( rendererType == kGfxRendererOpenGLES30 )
+ program = new GlslGpuProgramGLES30(source.substr(7,source.size()-7), output);
+ #endif
+ } else if (!strncmp (source.c_str(), "!!GLES", 6)) {
+ #if GFX_SUPPORTS_OPENGLES20
+ if( rendererType == kGfxRendererOpenGLES20Mobile || rendererType == kGfxRendererOpenGLES20Desktop )
+ program = new GlslGpuProgramGLES20( source.substr(6,source.size()-6), output );
+ #endif
+ #if GFX_SUPPORTS_OPENGLES30
+ if( rendererType == kGfxRendererOpenGLES30 )
+ program = new GlslGpuProgramGLES30( source.substr(6,source.size()-6), output);
+ #endif
+ } else if (!strncmp (source.c_str(), "!!ATIfs1.0", 10)) {
+ printf_console ("@TODO: found ATIfs1.0 shader; those are not supported anymore\n");
+ } else if (!strncmp (source.c_str(), "vs_1_1", 6) || !strncmp (source.c_str(), "vs_2_0", 6) || !strncmp (source.c_str(), "vs_3_0", 6)) {
+ #if GFX_SUPPORTS_D3D9
+ if( rendererType == kGfxRendererD3D9 )
+ program = new D3D9VertexShader( source );
+ #endif
+ } else if (!strncmp (source.c_str(), "ps_2_0", 6) || !strncmp (source.c_str(), "ps_3_0", 6)) {
+ #if GFX_SUPPORTS_D3D9
+ if( rendererType == kGfxRendererD3D9 )
+ program = new D3D9PixelShader( source );
+ #endif
+ } else if (!strncmp (source.c_str(), "vs_4_0_level_9", strlen("vs_4_0_level_9"))) {
+ #if GFX_SUPPORTS_D3D11
+ if( rendererType == kGfxRendererD3D11 && gGraphicsCaps.d3d11.featureLevel < kDX11Level10_0 )
+ program = new D3D11VertexShader( source );
+ #endif
+ } else if (!strncmp (source.c_str(), "ps_4_0_level_9", strlen("ps_4_0_level_9"))) {
+ #if GFX_SUPPORTS_D3D11
+ if( rendererType == kGfxRendererD3D11 && gGraphicsCaps.d3d11.featureLevel < kDX11Level10_0 )
+ program = new D3D11PixelShader( source );
+ #endif
+ } else if (!strncmp (source.c_str(), "vs_dx11", 7) || !strncmp (source.c_str(), "vs_4_0", 6) || !strncmp (source.c_str(), "vs_5_0", 6)) {
+ #if GFX_SUPPORTS_D3D11
+ if( rendererType == kGfxRendererD3D11 && gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0 )
+ program = new D3D11VertexShader( source );
+ #endif
+ } else if (!strncmp (source.c_str(), "ps_dx11", 7) || !strncmp (source.c_str(), "ps_4_0", 6) || !strncmp (source.c_str(), "ps_5_0", 6)) {
+ #if GFX_SUPPORTS_D3D11
+ if( rendererType == kGfxRendererD3D11 && gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0 )
+ program = new D3D11PixelShader( source );
+ #endif
+ } else if (!strncmp (source.c_str(), "gs_4_0", 6) || !strncmp (source.c_str(), "gs_5_0", 6)) {
+ #if GFX_SUPPORTS_D3D11
+ if (rendererType == kGfxRendererD3D11)
+ program = new D3D11GeometryShader(source);
+ #endif
+ } else if (!strncmp (source.c_str(), "hs_5_0", 6)) {
+ #if GFX_SUPPORTS_D3D11
+ if (rendererType == kGfxRendererD3D11)
+ program = new D3D11HullShader(source);
+ #endif
+ } else if (!strncmp (source.c_str(), "ds_5_0", 6)) {
+ #if GFX_SUPPORTS_D3D11
+ if (rendererType == kGfxRendererD3D11)
+ program = new D3D11DomainShader(source);
+ #endif
+ } else if (!strncmp (source.c_str(), "vs_360", 6)) {
+ #if GFX_SUPPORTS_XENON
+ if( rendererType == kGfxRendererXenon )
+ program = new XenonVertexShader (source);
+ #endif
+ } else if (!strncmp (source.c_str(), "ps_360", 6)) {
+ #if GFX_SUPPORTS_XENON
+ if( rendererType == kGfxRendererXenon )
+ program = new XenonPixelShader (source);
+ #endif
+ } else if (!strncmp (source.c_str(), "sce_vp_rsx", 10)) {
+ #if GFX_SUPPORTS_GCM
+ if( rendererType == kGfxRendererGCM )
+ program = new GcmGpuProgram( source );
+ #endif
+ } else if (!strncmp (source.c_str(), "sce_fp_rsx", 10)) {
+ #if GFX_SUPPORTS_GCM
+ if( rendererType == kGfxRendererGCM )
+ program = new GcmGpuProgram( source );
+ #endif
+ } else if (!strncmp (source.c_str(), "agal_vs", 7)) {
+ #if GFX_SUPPORTS_MOLEHILL
+ if (rendererType == kGfxRendererMolehill)
+ program = new GpuProgramMH (source, kShaderVertex);
+ #endif
+ } else if (!strncmp (source.c_str(), "agal_ps", 7)) {
+ #if GFX_SUPPORTS_MOLEHILL
+ if (rendererType == kGfxRendererMolehill)
+ program = new GpuProgramMH (source, kShaderFragment);
+ #endif
+ } else {
+ if( source == "!!error" )
+ {
+ // we've got a program that had errors when compiling. Silently fail, errors
+ // were dumped before
+ printf_console( "Shader had programs with errors, disabling subshader\n" );
+ }
+ else if( source.empty() )
+ {
+ output.CreateShaderErrors().AddShaderError ("Empty program string", -1, false);
+ }
+ else
+ {
+ const int kProgramStartChars = 10;
+ int len = source.size() < kProgramStartChars ? source.size() : kProgramStartChars;
+ output.CreateShaderErrors().AddShaderError (Format("Unrecognized program string: %s ...", source.substr(0,len).c_str()), -1, false);
+ }
+ }
+
+ // TODO: immediately check if not supported?
+
+ return program;
+#else
+ return NULL;
+#endif
+}
+
+
+// --------------------------------------------------------------------------
+
+GpuProgram::GpuProgram()
+{
+ m_ImplType = kShaderImplUndefined;
+ m_GpuProgramLevel = kGpuProgramNone;
+ m_NotSupported = false;
+ m_WasDestroyed = false;
+}
+
+GpuProgram::~GpuProgram ()
+{
+}
+
+bool GpuProgram::IsSupported () const
+{
+ if (m_NotSupported)
+ return false;
+
+ // OpenGL specific caps check
+ #if GFX_SUPPORTS_OPENGL
+ if( GetGfxDevice().GetRenderer() == kGfxRendererOpenGL )
+ {
+ switch (m_ImplType) {
+ case kShaderImplBoth:
+ if( !gGraphicsCaps.gl.hasGLSL ) return false;
+ break;
+ default:
+ break;
+ }
+ }
+ #endif
+
+ return true;
+}
+
+// --------------------------------------------------------------------------
+
+CreateGpuProgramOutput::CreateGpuProgramOutput()
+: m_PerFogModeParamsEnabled(false)
+, m_Params(NULL)
+, m_ShaderErrors(NULL)
+, m_ChannelAssigns(NULL)
+{
+}
+
+CreateGpuProgramOutput::~CreateGpuProgramOutput()
+{
+ delete m_Params;
+ delete m_ShaderErrors;
+ delete m_ChannelAssigns;
+}
+
+GpuProgramParameters& CreateGpuProgramOutput::CreateParams()
+{
+ Assert(!m_Params);
+ m_Params = new GpuProgramParameters;
+ return *m_Params;
+}
+
+ShaderErrors& CreateGpuProgramOutput::CreateShaderErrors()
+{
+ Assert(!m_ShaderErrors);
+ m_ShaderErrors = new ShaderErrors;
+ return *m_ShaderErrors;
+}
+
+ChannelAssigns& CreateGpuProgramOutput::CreateChannelAssigns()
+{
+ Assert(!m_ChannelAssigns);
+ m_ChannelAssigns = new ChannelAssigns;
+ return *m_ChannelAssigns;
+}
+
+
+#if GFX_SUPPORTS_OPENGL || GFX_SUPPORTS_OPENGLES20
+
+GpuProgramGL::GpuProgramGL()
+: GpuProgram()
+{
+ for (int i = 0; i < kFogModeCount; ++i)
+ m_Programs[i] = 0;
+}
+
+GpuProgramGL::~GpuProgramGL()
+{
+}
+
+#endif
diff --git a/Runtime/GfxDevice/GpuProgram.h b/Runtime/GfxDevice/GpuProgram.h
new file mode 100644
index 0000000..192d32b
--- /dev/null
+++ b/Runtime/GfxDevice/GpuProgram.h
@@ -0,0 +1,278 @@
+#pragma once
+
+#include <string>
+#include "External/shaderlab/Library/FastPropertyName.h"
+#include "GfxDeviceTypes.h"
+#include "External/shaderlab/Library/shadertypes.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/vector_set.h"
+#include "BuiltinShaderParams.h"
+#include "GfxDeviceConfigure.h"
+#include "Runtime/GfxDevice/threaded/ClientIDMapper.h"
+
+class ShaderErrors;
+class GpuProgram;
+class GpuProgramParameters;
+class CreateGpuProgramOutput;
+class GfxPatchInfo;
+class ChannelAssigns;
+using ShaderLab::FastPropertyName;
+namespace ShaderLab { class PropertySheet; class SubProgram; }
+
+
+enum GpuProgramLevel {
+ kGpuProgramNone = 0, // fixed function
+ kGpuProgramSM1,
+ kGpuProgramSM2,
+ kGpuProgramSM3,
+ kGpuProgramSM4,
+ kGpuProgramSM5,
+ kGpuProgramCount // keep this last
+};
+
+
+struct PropertyNamesSet {
+ PropertyNamesSet() : valueSize(0) { }
+ vector_set<int> names; // IDs of shader property names
+ UInt32 valueSize; // Buffer size needed to hold all property values of the above
+};
+
+
+// ------------------------------------------------------------------------
+
+bool CheckGpuProgramUsable (const char* str);
+
+// Create GPU programs with this function. This can return NULL if the program is not
+// recognized. When done with a program call Release() on it, never delete directly.
+GpuProgram* CreateGpuProgram( const std::string& source, CreateGpuProgramOutput& output );
+
+
+// ------------------------------------------------------------------------
+
+class GpuProgramParameters {
+public:
+ struct ValueParameter {
+ FastPropertyName m_Name; // The name of the value to look up.
+ int m_Index; // The index of the first parameter to upload to
+ int m_ArraySize; // The index of the first parameter to upload to
+ ShaderParamType m_Type;
+ UInt8 m_RowCount;
+ UInt8 m_ColCount;
+ ValueParameter (FastPropertyName n, ShaderParamType type, int idx, int arraySize, int rowCount, int colCount) : m_Name(n), m_Index(idx), m_ArraySize(arraySize), m_Type(type), m_RowCount(rowCount), m_ColCount(colCount) {}
+ ValueParameter () : m_Index(0), m_ArraySize(0), m_RowCount(0), m_ColCount(0) {}
+ bool operator == (const ValueParameter& other) const { return m_Name.index == other.m_Name.index; }
+ bool operator < (const ValueParameter& other) const { return m_Name.index < other.m_Name.index; }
+ };
+ typedef dynamic_array<ValueParameter> ValueParameterArray;
+
+ struct TextureParameter {
+ FastPropertyName m_Name;
+ int m_Index;
+ int m_SamplerIndex;
+ TextureDimension m_Dim;
+ TextureParameter (FastPropertyName n, int idx, int samplerIdx, TextureDimension dim) : m_Name(n), m_Index(idx), m_SamplerIndex(samplerIdx), m_Dim(dim) { }
+ TextureParameter () : m_Index(0), m_SamplerIndex(0), m_Dim(kTexDimNone) {}
+ };
+ typedef std::vector<TextureParameter> TextureParameterList;
+
+ struct BufferParameter {
+ FastPropertyName m_Name;
+ int m_Index;
+ BufferParameter (FastPropertyName n, int idx) : m_Name(n), m_Index(idx) { }
+ BufferParameter () : m_Index(0) {}
+ };
+ typedef dynamic_array<BufferParameter> BufferParameterArray;
+
+
+ #if GFX_SUPPORTS_CONSTANT_BUFFERS
+ struct ConstantBuffer {
+ FastPropertyName m_Name;
+ ValueParameterArray m_ValueParams;
+ int m_Size;
+ int m_BindIndex;
+ };
+ typedef std::vector<ConstantBuffer> ConstantBufferList;
+ #endif
+
+public:
+ GpuProgramParameters () : m_ValuesSize(0), m_Status(kBlank)
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+ ,m_InternalHandle(0)
+#endif
+ { }
+
+ void AddValueParam (const ValueParameter& param);
+ void AddTextureParam (const TextureParameter& param);
+ void AddVectorParam (int index, ShaderParamType type, int dimension, const char* nameStr, int cbIndex, PropertyNamesSet* outNames);
+ void AddMatrixParam (int index, const char* nameStr, int rowCount, int colCount, int cbIndex, PropertyNamesSet* outNames);
+ void AddTextureParam (int index, int samplerIndex, const char* nameStr, TextureDimension dim, PropertyNamesSet* outNames);
+ void AddBufferParam (int index, const char* nameStr, PropertyNamesSet* outNames);
+ const ValueParameterArray& GetValueParams() const;
+ const TextureParameterList& GetTextureParams() const { return m_TextureParams; }
+ TextureParameterList& GetTextureParams() { return m_TextureParams; }
+ const BufferParameterArray& GetBufferParams() const { return m_BufferParams; }
+
+ const ValueParameter* FindParam(const FastPropertyName& name, int* outCBIndex = NULL) const;
+ const TextureParameter* FindTextureParam(const FastPropertyName& name, TextureDimension dim) const;
+
+ #if GFX_SUPPORTS_CONSTANT_BUFFERS
+ const ConstantBufferList& GetConstantBuffers() const { return m_ConstantBuffers; }
+ ConstantBufferList& GetConstantBuffers() { return m_ConstantBuffers; }
+ void SetConstantBufferCount (size_t count)
+ {
+ m_ConstantBuffers.resize (count);
+ }
+ #endif
+
+ const BuiltinShaderParamIndices& GetBuiltinParams() const { return m_BuiltinParams; }
+
+ int GetValuesSize() const { return m_ValuesSize; }
+ UInt8* PrepareValues (
+ const ShaderLab::PropertySheet* props,
+ UInt8* buffer,
+ const UInt8* bufferStart = NULL,
+ GfxPatchInfo* outPatchInfo = NULL,
+ bool* outMissingTextures = NULL) const;
+
+ bool IsReady() const { return m_Status == kReady; }
+ bool IsDirty() const { return m_Status == kDirty; }
+
+ virtual void MakeReady();
+
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+ // Reference to the created GpuPogramParameters on the worker.
+ ClientIDWrapper(GpuProgramParameters) m_InternalHandle;
+#endif
+
+private:
+ enum State {
+ kBlank,
+ kDirty,
+ kReady
+ };
+
+ struct NameToValueIndex {
+ int nameIndex;
+ SInt16 cbIndex;
+ UInt16 valueIndex;
+ bool operator == (const NameToValueIndex& other) const { return nameIndex == other.nameIndex; }
+ bool operator < (const NameToValueIndex& other) const { return nameIndex < other.nameIndex; }
+ };
+
+ void MakeValueParamsReady (ValueParameterArray& values, int cbIndex);
+ ValueParameterArray& GetValuesArray (int cbIndex) {
+ ValueParameterArray* values = &m_ValueParams;
+ #if GFX_SUPPORTS_CONSTANT_BUFFERS
+ if (cbIndex >= 0)
+ {
+ DebugAssert (cbIndex < m_ConstantBuffers.size());
+ values = &m_ConstantBuffers[cbIndex].m_ValueParams;
+ }
+ #endif
+ return *values;
+ }
+ const ValueParameterArray& GetValuesArray (int cbIndex) const {
+ const ValueParameterArray* values = &m_ValueParams;
+ #if GFX_SUPPORTS_D3D11
+ if (cbIndex >= 0)
+ {
+ DebugAssert (cbIndex < m_ConstantBuffers.size());
+ values = &m_ConstantBuffers[cbIndex].m_ValueParams;
+ }
+ #endif
+ return *values;
+ }
+
+private:
+
+ typedef dynamic_array<NameToValueIndex> NameToValueIndexMap;
+ ValueParameterArray m_ValueParams;
+ NameToValueIndexMap m_NamedParams;
+ TextureParameterList m_TextureParams;
+ BufferParameterArray m_BufferParams;
+
+ #if GFX_SUPPORTS_CONSTANT_BUFFERS
+ ConstantBufferList m_ConstantBuffers;
+ #endif
+
+ BuiltinShaderParamIndices m_BuiltinParams;
+ int m_ValuesSize;
+ State m_Status;
+};
+
+
+// ------------------------------------------------------------------------
+
+
+// Base class for GPU programs.
+class GpuProgram {
+public:
+ virtual ~GpuProgram();
+
+ virtual void ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer) = 0;
+
+ ShaderImplType GetImplType() const { return m_ImplType; }
+ virtual bool IsSupported () const;
+ GpuProgramLevel GetLevel() const { return m_GpuProgramLevel; }
+
+ void SetNotSupported (bool v) { m_NotSupported = v; }
+
+protected:
+ GpuProgram();
+
+protected:
+ ShaderImplType m_ImplType; // Actual implementation type
+ bool m_NotSupported;
+ bool m_WasDestroyed;
+ GpuProgramLevel m_GpuProgramLevel;
+};
+
+
+class CreateGpuProgramOutput
+{
+public:
+ CreateGpuProgramOutput();
+ ~CreateGpuProgramOutput();
+
+ bool GetPerFogModeParamsEnabled() const { return m_PerFogModeParamsEnabled; }
+ void SetPerFogModeParamsEnabled(bool enable) { m_PerFogModeParamsEnabled = enable; }
+
+ const GpuProgramParameters* GetParams() const { return m_Params; }
+ GpuProgramParameters& CreateParams();
+
+ const ShaderErrors* GetShaderErrors() const { return m_ShaderErrors; }
+ ShaderErrors& CreateShaderErrors();
+
+ PropertyNamesSet* GetOutNames() const { return m_OutNames; }
+ const void SetOutNames(PropertyNamesSet* value) { m_OutNames = value; }
+
+ const ChannelAssigns* GetChannelAssigns() const { return m_ChannelAssigns; }
+ ChannelAssigns& CreateChannelAssigns();
+
+private:
+ bool m_PerFogModeParamsEnabled;
+ GpuProgramParameters* m_Params;
+ ShaderErrors* m_ShaderErrors;
+ ChannelAssigns* m_ChannelAssigns;
+ PropertyNamesSet* m_OutNames;
+};
+
+#if GFX_SUPPORTS_OPENGL || GFX_SUPPORTS_OPENGLES20 || GFX_SUPPORTS_OPENGLES30
+
+typedef unsigned int GLShaderID;
+
+class GpuProgramGL : public GpuProgram {
+public:
+ virtual ~GpuProgramGL();
+
+ GLShaderID GetGLProgramIfCreated(FogMode fog) const { return m_Programs[fog]; }
+
+protected:
+ GpuProgramGL();
+
+protected:
+ std::string m_SourceForFog; // original source, used for fog patching if needed
+ GLShaderID m_Programs[kFogModeCount];
+};
+
+#endif
diff --git a/Runtime/GfxDevice/GpuProgramParamsApply.h b/Runtime/GfxDevice/GpuProgramParamsApply.h
new file mode 100644
index 0000000..4ac5ad0
--- /dev/null
+++ b/Runtime/GfxDevice/GpuProgramParamsApply.h
@@ -0,0 +1,130 @@
+#pragma once
+
+
+template<typename SetValuesFunctor>
+void ApplyMaterialPropertyBlockValues(
+ MaterialPropertyBlock& propblock,
+ GpuProgram* activeGpuProgram[kShaderTypeCount],
+ const GpuProgramParameters* activeGpuProgramParams[kShaderTypeCount],
+ SetValuesFunctor& functor)
+{
+ const MaterialPropertyBlock::Property* curProp = propblock.GetPropertiesBegin();
+ const MaterialPropertyBlock::Property* propEnd = propblock.GetPropertiesEnd();
+ const float* propBuffer = propblock.GetBufferBegin();
+ while (curProp != propEnd)
+ {
+ FastPropertyName name;
+ name.index = curProp->nameIndex;
+ for (int shaderType = kShaderVertex; shaderType < kShaderTypeCount; ++shaderType)
+ {
+ GpuProgram* GpuProgram = activeGpuProgram[shaderType];
+ if (!GpuProgram)
+ continue;
+ const GpuProgramParameters* params = activeGpuProgramParams[shaderType];
+
+ if (curProp->texDim != kTexDimNone)
+ {
+ // texture parameter
+ const GpuProgramParameters::TextureParameter* param = params->FindTextureParam(name, (TextureDimension)curProp->texDim);
+ if (!param)
+ continue;
+ const float* val = &propBuffer[curProp->offset];
+ functor.SetTextureVal ((ShaderType)shaderType, param->m_Index, param->m_SamplerIndex, param->m_Dim, TextureID(*(unsigned int*)val));
+ }
+ else
+ {
+ // value parameter
+ int cbIndex;
+ const GpuProgramParameters::ValueParameter* param = params->FindParam(name, &cbIndex);
+ if (!param)
+ continue;
+ if (curProp->rows == 1)
+ {
+ const float* src = &propBuffer[curProp->offset];
+ Vector4f val;
+ if (curProp->cols == 4)
+ val = Vector4f(src);
+ else
+ {
+ DebugAssert(curProp->cols == 1);
+ val = Vector4f(*src, 0, 0, 0);
+ }
+ functor.SetVectorVal ((ShaderType)shaderType, param->m_Type, param->m_Index, val.GetPtr(), curProp->cols, *params, cbIndex);
+ }
+ else if (curProp->rows == 4)
+ {
+ DebugAssert(curProp->cols == 4);
+ const Matrix4x4f* src = (const Matrix4x4f*)&propBuffer[curProp->offset];
+ functor.SetMatrixVal ((ShaderType)shaderType, param->m_Index, src, param->m_RowCount, *params, cbIndex);
+ }
+ else
+ {
+ AssertString("Unknown property dimensions");
+ }
+ }
+ }
+ ++curProp;
+ }
+
+ propblock.Clear();
+}
+
+
+// GL ES is different from everyone else, in that shader variables are always for the whole
+// "program" (all shader stages at once).
+template<typename SetValuesFunctor>
+void ApplyMaterialPropertyBlockValuesES(
+ MaterialPropertyBlock& propblock,
+ const GpuProgram* activeProgram,
+ const GpuProgramParameters* activeProgramParams,
+ SetValuesFunctor& functor)
+{
+ if (activeProgram)
+ {
+ const MaterialPropertyBlock::Property* curProp = propblock.GetPropertiesBegin();
+ const MaterialPropertyBlock::Property* propEnd = propblock.GetPropertiesEnd();
+ const float* propBuffer = propblock.GetBufferBegin();
+ while (curProp != propEnd)
+ {
+ FastPropertyName name;
+ name.index = curProp->nameIndex;
+
+ if (curProp->texDim != kTexDimNone)
+ {
+ // texture parameter
+ const GpuProgramParameters::TextureParameter* param = activeProgramParams->FindTextureParam(name, (TextureDimension)curProp->texDim);
+ if (param)
+ {
+ const float* val = &propBuffer[curProp->offset];
+ functor.SetTextureVal (kShaderFragment, param->m_Index, param->m_SamplerIndex, param->m_Dim, TextureID(*(unsigned int*)val));
+ }
+ }
+ else
+ {
+ // value parameter
+ const GpuProgramParameters::ValueParameter* param = activeProgramParams->FindParam(name);
+ if (param && curProp->rows == param->m_RowCount)
+ {
+ if (curProp->rows == 1)
+ {
+ const float* src = &propBuffer[curProp->offset];
+ functor.SetVectorVal (param->m_Type, param->m_Index, src, param->m_ColCount);
+ }
+ else if (curProp->rows == 4)
+ {
+ DebugAssert(curProp->cols == 4);
+ const Matrix4x4f* src = (const Matrix4x4f*)&propBuffer[curProp->offset];
+ functor.SetMatrixVal (param->m_Index, src, param->m_RowCount);
+ }
+ else
+ {
+ AssertString("Unknown property dimensions");
+ }
+ }
+ }
+ ++curProp;
+ }
+ }
+
+ propblock.Clear();
+}
diff --git a/Runtime/GfxDevice/ShaderConstantCache.h b/Runtime/GfxDevice/ShaderConstantCache.h
new file mode 100644
index 0000000..35b54eb
--- /dev/null
+++ b/Runtime/GfxDevice/ShaderConstantCache.h
@@ -0,0 +1,142 @@
+#pragma once
+
+#include "GfxDeviceTypes.h"
+#include "Runtime/Math/Vector4.h"
+//#include "Runtime/Utilities/fixed_bitset.h"
+#include "D3D9Utils.h"
+
+
+template<int SIZE>
+class ShaderConstantCache2 {
+public:
+ ShaderConstantCache2() { memset(m_Values, 0, sizeof(m_Values)); Invalidate(); }
+
+ void SetValues( int index, const float* values, int count )
+ {
+ DebugAssert( index >= 0 && count > 0 && (index + count) <= SIZE );
+ float* dest = m_Values[index].GetPtr();
+ UInt8 andedFlags = m_Flags[index];
+ for (int i = 1; i < count; i++)
+ andedFlags &= m_Flags[index + i];
+ // Not worth filtering values bigger than one register
+ if (andedFlags == kValid && count == 1)
+ {
+ // We have a valid value which is not dirty
+ UInt32* destInt32 = reinterpret_cast<UInt32*>(dest);
+ int sizeInt32 = count * sizeof(Vector4f) / sizeof(UInt32);
+ if (CompareArrays(destInt32, reinterpret_cast<const UInt32*>(values), sizeInt32))
+ return;
+ }
+ memcpy(dest, values, count * sizeof(Vector4f));
+ // If all values are marked as dirty we are done
+ if (andedFlags & kDirty)
+ return;
+ // Update flags
+ for (int i = 0; i < count; i++)
+ m_Flags[index + i] = kValid | kDirty;
+ // Add dirty range or append to last range
+ if (!m_DirtyRanges.empty() && m_DirtyRanges.back().second == index)
+ m_DirtyRanges.back().second += count;
+ else
+ m_DirtyRanges.push_back(Range(index, index + count));
+ }
+
+ void Invalidate()
+ {
+ memset(m_Flags, 0, sizeof(m_Flags));
+ }
+
+ void CommitVertexConstants()
+ {
+ IDirect3DDevice9* dev = GetD3DDevice();
+ int numRanges = m_DirtyRanges.size();
+ for (int i = 0; i < numRanges; i++)
+ {
+ const Range& range = m_DirtyRanges[i];
+ int size = range.second - range.first;
+ D3D9_CALL(dev->SetVertexShaderConstantF( range.first, m_Values[range.first].GetPtr(), size ));
+ // Update flags
+ for (int i = 0; i < size; i++)
+ m_Flags[range.first + i] = kValid;
+ }
+ m_DirtyRanges.clear();
+ }
+
+ void CommitPixelConstants()
+ {
+ IDirect3DDevice9* dev = GetD3DDevice();
+ int numRanges = m_DirtyRanges.size();
+ for (int i = 0; i < numRanges; i++)
+ {
+ const Range& range = m_DirtyRanges[i];
+ int size = range.second - range.first;
+ D3D9_CALL(dev->SetPixelShaderConstantF( range.first, m_Values[range.first].GetPtr(), size ));
+ // Update flags
+ for (int i = 0; i < size; i++)
+ m_Flags[range.first + i] = kValid;
+ }
+ m_DirtyRanges.clear();
+ }
+
+private:
+ enum
+ {
+ kValid = 1 << 0,
+ kDirty = 1 << 1
+ };
+ UInt8 m_Flags[SIZE];
+ Vector4f m_Values[SIZE];
+ typedef std::pair<int,int> Range;
+ std::vector<Range> m_DirtyRanges;
+};
+
+
+template<int SIZE>
+class ShaderConstantCache {
+public:
+ ShaderConstantCache() { Invalidate(); }
+
+ bool CheckCache( int index, const float value[4] )
+ {
+ DebugAssertIf( index < 0 );
+ if( index >= SIZE )
+ return false;
+
+ if( m_Values[index] == value )
+ return true;
+
+ m_Values[index].Set( value );
+ return false;
+ }
+
+ bool CheckCache( int index, const float* values, int count )
+ {
+ DebugAssertIf( index < 0 );
+
+ // Checking whole range for validity seems to be overkill from profiling (just slows it down a bit)
+ // So just invalidate those cache entries.
+
+ if( index + count > SIZE )
+ count = SIZE-index;
+
+ for( int i = 0; i < count; ++i )
+ {
+ m_Values[index+i].x = -std::numeric_limits<float>::infinity();
+ }
+ return false;
+ }
+
+ void Invalidate()
+ {
+ for (int i = 0; i < SIZE; ++i)
+ m_Values[i].x = -std::numeric_limits<float>::infinity();
+ }
+
+private:
+ Vector4f m_Values[SIZE];
+};
+
+
+typedef ShaderConstantCache2<256> VertexShaderConstantCache;
+typedef ShaderConstantCache2<256> PixelShaderConstantCache;
+
diff --git a/Runtime/GfxDevice/TextureIdMap.cpp b/Runtime/GfxDevice/TextureIdMap.cpp
new file mode 100644
index 0000000..0f750f3
--- /dev/null
+++ b/Runtime/GfxDevice/TextureIdMap.cpp
@@ -0,0 +1,31 @@
+#include "UnityPrefix.h"
+#include "TextureIdMap.h"
+
+
+void TextureIdMap::Initialize()
+{
+ if(!s_Inited)
+ {
+ s_Textures.set_empty_key(TextureID(-1));
+ s_Textures.set_deleted_key(TextureID(-2));
+
+ s_Inited = true;
+ }
+}
+
+void TextureIdMap::Uninitialize()
+{
+ if (!s_Inited)
+ return;
+
+ //@TODO: make sure all textures are cleaned up properly and re-enabled the assert
+ //#if 0 // was !UNITY_EDITOR
+ //Assert(s_Textures.empty());
+ //#endif
+
+ // Note, do not set s_Inited to false; would make it impossible to reload GfxDevice
+ // since the hash map object stays the same but we can't set the empty key again.
+}
+
+TextureIdMap::TextureMap TextureIdMap::s_Textures;
+bool TextureIdMap::s_Inited = false;
diff --git a/Runtime/GfxDevice/TextureIdMap.h b/Runtime/GfxDevice/TextureIdMap.h
new file mode 100644
index 0000000..9b1061c
--- /dev/null
+++ b/Runtime/GfxDevice/TextureIdMap.h
@@ -0,0 +1,70 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+
+#include "Runtime/Utilities/dense_hash_map.h"
+#include "Runtime/Utilities/LogAssert.h"
+
+#include "Runtime/GfxDevice/GfxDeviceConfigure.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+class
+TextureIdMap
+{
+public:
+
+ static void Initialize();
+ static void Uninitialize();
+
+ static void UpdateTexture(TextureID texid, intptr_t nativeTex);
+ static void RemoveTexture(TextureID texid);
+
+ static intptr_t QueryNativeTexture(TextureID texid);
+
+
+private:
+
+ struct TextureIDHashFunctor
+ {
+ inline size_t operator()(TextureID x) const
+ {
+ UInt32 a = x.m_ID;
+ a = (a+0x7ed55d16) + (a<<12);
+ a = (a^0xc761c23c) ^ (a>>19);
+ a = (a+0x165667b1) + (a<<5);
+ a = (a+0xd3a2646c) ^ (a<<9);
+ a = (a+0xfd7046c5) + (a<<3);
+ a = (a^0xb55a4f09) ^ (a>>16);
+ return a;
+ }
+ };
+
+
+ typedef pair<const TextureID, intptr_t> TextureIdToTexturePair;
+ typedef dense_hash_map< TextureID, intptr_t, TextureIDHashFunctor, std::equal_to<TextureID>, STL_ALLOCATOR(kMemSTL, TextureIdToTexturePair) > TextureMap;
+
+ static TextureMap s_Textures;
+
+ // some devices call OnCreate twice - easier to workaround here
+ static bool s_Inited;
+};
+
+inline void TextureIdMap::UpdateTexture(TextureID texid, intptr_t nativeTex)
+{
+ TextureMap::iterator it = s_Textures.find(texid);
+ if(it != s_Textures.end())
+ it->second = nativeTex;
+ else
+ s_Textures.insert(std::make_pair(texid, nativeTex));
+}
+
+inline void TextureIdMap::RemoveTexture(TextureID texid)
+{
+ s_Textures.erase(texid);
+}
+
+inline intptr_t TextureIdMap::QueryNativeTexture(TextureID texid)
+{
+ TextureMap::iterator it = s_Textures.find(texid);
+ return it == s_Textures.end() ? 0 : it->second;
+}
diff --git a/Runtime/GfxDevice/TextureUploadUtils.h b/Runtime/GfxDevice/TextureUploadUtils.h
new file mode 100644
index 0000000..8543552
--- /dev/null
+++ b/Runtime/GfxDevice/TextureUploadUtils.h
@@ -0,0 +1,254 @@
+#pragma once
+
+#include "External/ProphecySDK/include/prcore/surface.hpp"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Graphics/S3Decompression.h"
+#include "Runtime/Graphics/FlashATFDecompression.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "GfxDevice.h"
+#include "VramLimits.h"
+
+extern bool IsNPOTTextureAllowed(bool hasMipMap);
+
+const int kDoubleLDRAlpha = 255 * 0.25f;
+
+
+inline bool SkipLevelsForMasterTextureLimit (
+ int masterTextureLimit,
+ TextureFormat dataFormat, TextureFormat uploadFormat,
+ int mipCount,
+ bool uploadIsCompressed,
+ UInt8** srcData, int* width, int* height,
+ int* baseLevel, int* maxLevel, int* texWidth, int* texHeight, size_t* textureSize)
+{
+ bool wasScaledDown = false;
+
+ // For compressed textures, stop applying masterTextureLimit if texture size drops below 4
+ if( uploadIsCompressed )
+ {
+ while( masterTextureLimit > 0 && ((*width >> masterTextureLimit) < 4 || (*height >> masterTextureLimit) < 4) )
+ {
+ --masterTextureLimit;
+ }
+ }
+
+ // skip several levels in data based on masterTextureLimit
+ *maxLevel = mipCount - 1;
+ *baseLevel = std::min( masterTextureLimit, *maxLevel );
+ int level;
+ for( level = 0; level < *baseLevel; ++level )
+ {
+ *srcData += CalculateImageSize (*width, *height, dataFormat);
+ AssertIf( *width == 1 && *height == 1 && level != *maxLevel );
+ *width = std::max( *width / 2, 1 );
+ *height = std::max( *height / 2, 1 );
+ }
+
+ // Now estimate VRAM usage for the texture
+ *textureSize = CalculateImageSize (*width, *height, uploadFormat);
+ if (mipCount > 1)
+ *textureSize += *textureSize / 3;
+ int textureSizeKB = *textureSize / 1024;
+
+ const int vramSizeKB = gGraphicsCaps.videoMemoryMB * 1024;
+ const GfxDeviceStats::MemoryStats& memoryStats = GetRealGfxDevice().GetFrameStats().GetMemoryStats();
+ const int currentVramUsageKB = (memoryStats.screenBytes + memoryStats.renderTextureBytes) / 1024;
+ const int allowedVramUsageKB = std::max( (vramSizeKB - currentVramUsageKB) * kVRAMMaxFreePortionForTexture, 1.0f );
+
+ // While texture is too large for hardware limits, or too large to sanely fit into VRAM, skip
+ // mip levels. If it's non-mipmapped one, we have to reduce it and scale it into size that fits.
+ // Don't do fitting into VRAM for Alpha8 textures (those are used for dynamic fonts, and
+ // everything can break down if we scale them down).
+ *texWidth = *width;
+ *texHeight = *height;
+ while (
+ *texWidth > gGraphicsCaps.maxTextureSize ||
+ *texHeight > gGraphicsCaps.maxTextureSize ||
+ (textureSizeKB > allowedVramUsageKB && dataFormat != kTexFormatAlpha8))
+ {
+ printf_console("Texture2D %ix%i won't fit, reducing size (needed mem=%i used mem=%i allowedmem=%i)\n", *texWidth, *texHeight, textureSizeKB, currentVramUsageKB, allowedVramUsageKB);
+ if (*baseLevel < *maxLevel)
+ {
+ *srcData += CalculateImageSize (*texWidth, *texHeight, dataFormat);
+ *width = std::max (*width / 2, 1);
+ *height = std::max (*height / 2, 1);
+ ++*baseLevel;
+ }
+ *texWidth = std::max( *texWidth / 2, 1 );
+ *texHeight = std::max( *texHeight / 2, 1 );
+ textureSizeKB /= 4;
+ *textureSize /= 4;
+ wasScaledDown = true;
+ if( *texWidth <= 4 && *texHeight <= 4 )
+ break;
+ }
+
+ return wasScaledDown;
+}
+
+
+inline void HandleFormatDecompression (
+ TextureFormat format,
+ TextureUsageMode* usageMode,
+ TextureColorSpace colorSpace,
+ bool* uploadIsCompressed,
+ bool* decompressOnTheFly
+ )
+{
+ // Figure out whether we'll upload compressed or decompress on the fly
+
+ ///@TODO: This looks wrong. What the fuck???
+ *uploadIsCompressed = IsCompressedDXTTextureFormat(format);
+
+ switch (*usageMode)
+ {
+ case kTexUsageLightmapRGBM:
+ // No special processing for RGBM if we support it
+ if (*usageMode == kTexUsageLightmapRGBM && gGraphicsCaps.SupportsRGBM())
+ *usageMode = kTexUsageNone;
+ break;
+ case kTexUsageLightmapDoubleLDR:
+ // Never any special processing of DoubleLDR in players.
+ //
+ // In the editor, we'll add a dummy 0.25f alpha channel to the doubleLDR lightmap.
+ // When we're in GLES20 emulation mode in the editor we're using pixel shaders compiled
+ // for the platform the editor is currently running on and that forces RGBM decoding.
+ // So we get (8.0 * 0.25) * color.rgb -- which is doubleLDR decoding.
+#if !UNITY_EDITOR
+ *usageMode = kTexUsageNone;
+#endif
+ break;
+ case kTexUsageNormalmapPlain:
+ // Never any special processing of plain normal maps in players.
+ //
+ // In the editor, we'll put .r into .a to make it work with shaders that expect DXT5nm
+ // encoding.
+#if !UNITY_EDITOR
+ *usageMode = kTexUsageNone;
+#endif
+ break;
+ default:
+ *usageMode = kTexUsageNone;
+ }
+
+ ////@TODO: BIG WTF??? We always decompress on the fly for pvrtc etc atc and wii formats????
+
+ // Decompress on the fly when the device cannot handle
+ // the compressed format or we have to do any special processing based on usage mode.
+ *decompressOnTheFly = (*uploadIsCompressed && (!gGraphicsCaps.hasS3TCCompression || *usageMode != kTexUsageNone)) ||
+ IsCompressedPVRTCTextureFormat(format) ||
+ IsCompressedETCTextureFormat(format) ||
+ IsCompressedATCTextureFormat(format) ||
+ IsCompressedETC2TextureFormat(format) ||
+ IsCompressedASTCTextureFormat(format) ||
+ IsCompressedFlashATFTextureFormat(format);
+
+# if !UNITY_XENON
+ //If we are not on Xenon and the texture color space is xenon decompress on the fly
+ if (*uploadIsCompressed && colorSpace == kTexColorSpaceSRGBXenon)
+ {
+ *decompressOnTheFly |= *uploadIsCompressed && colorSpace == kTexColorSpaceSRGBXenon;
+ }
+#endif
+}
+
+
+inline void InitImageBuffer (int width, int height, UInt8*& buffer, TextureFormat textureFormat)
+{
+ int imageSize = CalculateImageSize( width, height, textureFormat );
+ if( buffer == NULL )
+ buffer = new UInt8[imageSize];
+}
+
+
+inline void PerformUploadConversions (
+ int width, int height,
+ UInt8* dstBuffer, int dstPitch,
+ TextureUsageMode usageMode,
+ TextureColorSpace colorSpace,
+ const prcore::PixelFormat& pf
+ )
+{
+ if (usageMode == kTexUsageLightmapRGBM)
+ DecodeRGBM (width, height, dstBuffer, dstPitch, pf);
+#if UNITY_EDITOR
+ if (usageMode == kTexUsageLightmapDoubleLDR)
+ SetAlphaChannel (width, height, dstBuffer, dstPitch, pf, kDoubleLDRAlpha);
+ if (usageMode == kTexUsageNormalmapPlain)
+ SetAlphaToRedChannel (width, height, dstBuffer, dstPitch, pf);
+ if (colorSpace == kTexColorSpaceSRGBXenon)
+ XenonToNormalSRGBTexture(width, height, dstBuffer, dstPitch, pf);
+#endif
+}
+
+
+inline void ConvertCompressedTextureUpload (
+ int width, int height,
+ TextureFormat format,
+ const UInt8* srcData,
+ UInt8*& decompressBuffer, int& tempBufferPitch,
+ TextureUsageMode usageMode,
+ TextureColorSpace colorSpace,
+ int mipLevel)
+{
+ int dstWidth = std::max( width, 4 );
+ int dstHeight = std::max( height, 4 );
+
+ InitImageBuffer (dstWidth, dstHeight, decompressBuffer, kTexFormatRGBA32);
+ tempBufferPitch = GetRowBytesFromWidthAndFormat(width, kTexFormatRGBA32);
+
+ DecompressNativeTextureFormatWithMipLevel (format, width, height, mipLevel, (UInt32*)srcData, dstWidth, dstHeight, (UInt32*)decompressBuffer);
+
+ PerformUploadConversions (width, height, decompressBuffer, tempBufferPitch, usageMode, colorSpace, GetProphecyPixelFormat(kTexFormatRGBA32));
+}
+
+
+inline bool ConvertUncompressedTextureUpload (
+ prcore::Surface& srcSurface,
+ prcore::Surface& dstSurface,
+ prcore::Surface::BlitMode blitMode,
+ TextureFormat uploadFormat,
+ TextureUsageMode usageMode,
+ TextureColorSpace colorSpace,
+ int width, int height, UInt8* inplaceData, int pitch, const prcore::PixelFormat& pf,
+ UInt8*& tempBuffer, int& tempBufferPitch
+ )
+{
+ if (usageMode != kTexUsageNone || colorSpace == kTexColorSpaceSRGBXenon)
+ {
+ if (uploadFormat == kTexFormatRGBA32 || uploadFormat == kTexFormatARGB32)
+ {
+ // Copy to locked rect
+ dstSurface.BlitImage( srcSurface, blitMode );
+ // Process in place
+ PerformUploadConversions (width, height, inplaceData, pitch, usageMode, colorSpace, pf);
+ }
+ else
+ {
+ InitImageBuffer (width, height, tempBuffer, kTexFormatRGBA32);
+ tempBufferPitch = GetRowBytesFromWidthAndFormat(width, kTexFormatRGBA32);
+
+ // Copy to a temporary buffer so we can process the texture in place
+ prcore::Surface tempSurface( width, height, tempBufferPitch, pf, tempBuffer );
+ tempSurface.BlitImage( srcSurface, blitMode );
+
+ // Process in place in the temp surface
+ PerformUploadConversions (width, height, tempBuffer, tempBufferPitch, usageMode, colorSpace, pf);
+
+ // Copy to locked rect
+ dstSurface.BlitImage( tempSurface, blitMode );
+ }
+ return true;
+ }
+ return false;
+}
+
+
+inline void AdvanceToNextMipLevel (TextureFormat format, UInt8*& srcData, int& width, int& height, int& texWidth, int& texHeight)
+{
+ srcData += CalculateImageSize (width, height, format);
+ width = std::max( width / 2, 1 );
+ height = std::max( height / 2, 1 );
+ texWidth = std::max( texWidth / 2, 1 );
+ texHeight = std::max( texHeight / 2, 1 );
+}
diff --git a/Runtime/GfxDevice/TransformState.h b/Runtime/GfxDevice/TransformState.h
new file mode 100644
index 0000000..ef56382
--- /dev/null
+++ b/Runtime/GfxDevice/TransformState.h
@@ -0,0 +1,64 @@
+#pragma once
+
+#include "GfxDeviceTypes.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "BuiltinShaderParams.h"
+
+struct TransformState
+{
+ enum {
+ kWorldDirty = (1<<0),
+ kViewDirty = (1<<1),
+ kProjDirty = (1<<2),
+
+ kWorldViewDirty = (kWorldDirty | kViewDirty),
+ kViewProjDirty = (kViewDirty | kProjDirty),
+ kWorldViewProjDirty = (kWorldDirty | kViewDirty | kProjDirty),
+ };
+
+ Matrix4x4f worldMatrix;
+ Matrix4x4f projectionMatrixOriginal; // Originally set from Unity code
+
+ Matrix4x4f texMatrices[kMaxSupportedTextureCoords];
+
+ // cache
+ mutable Matrix4x4f worldViewMatrix;
+
+ mutable volatile UInt32 dirtyFlags;
+
+public:
+ void Invalidate(BuiltinShaderParamValues& builtins);
+ void UpdateWorldViewMatrix (const BuiltinShaderParamValues& builtins) const;
+ void SetViewMatrix (const float matrix[16], BuiltinShaderParamValues& builtins);
+};
+
+inline void TransformState::Invalidate(BuiltinShaderParamValues& builtins)
+{
+ worldViewMatrix.SetIdentity();
+ worldMatrix.SetIdentity();
+ builtins.GetWritableMatrixParam(kShaderMatView).SetIdentity();
+ builtins.GetWritableMatrixParam(kShaderMatProj).SetIdentity();
+ builtins.GetWritableMatrixParam(kShaderMatViewProj).SetIdentity();
+ projectionMatrixOriginal.SetIdentity();
+ dirtyFlags = kWorldViewProjDirty;
+}
+
+inline void TransformState::UpdateWorldViewMatrix (const BuiltinShaderParamValues& builtins) const
+{
+ if (dirtyFlags & kWorldViewDirty)
+ {
+ MultiplyMatrices4x4 (&builtins.GetMatrixParam(kShaderMatView), &worldMatrix, &worldViewMatrix);
+ dirtyFlags &= ~kWorldViewDirty;
+ }
+}
+
+inline void TransformState::SetViewMatrix (const float matrix[16], BuiltinShaderParamValues& builtins)
+{
+ dirtyFlags |= TransformState::kWorldViewDirty;
+ Matrix4x4f& viewMat = builtins.GetWritableMatrixParam(kShaderMatView);
+ const Matrix4x4f& projMat = builtins.GetMatrixParam(kShaderMatProj);
+ Matrix4x4f& viewProjMat = builtins.GetWritableMatrixParam(kShaderMatViewProj);
+ CopyMatrix (matrix, viewMat.GetPtr());
+ MultiplyMatrices4x4 (&projMat, &viewMat, &viewProjMat);
+ worldMatrix = Matrix4x4f::identity;
+}
diff --git a/Runtime/GfxDevice/VramLimits.cpp b/Runtime/GfxDevice/VramLimits.cpp
new file mode 100644
index 0000000..36bfe50
--- /dev/null
+++ b/Runtime/GfxDevice/VramLimits.cpp
@@ -0,0 +1,39 @@
+#include "UnityPrefix.h"
+#include "VramLimits.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+
+
+int ChooseSuitableFSAALevel( int width, int height, int backbufferBPP, int frontbufferBPP, int depthBPP, int fsaa )
+{
+ // no AA support?
+ if( !gGraphicsCaps.hasMultiSample )
+ return 0;
+
+ // figure out appropriate AA level based on VRAM and screen size
+ int vramKB = int(gGraphicsCaps.videoMemoryMB * 1024);
+
+ const int vramAllowedByPortion = int(vramKB * 0.5f); // allow max. 50% of total VRAM for screen
+ const int vramAllowedMax = 256 * 1024; // max 256MB for screen
+ // Make sure at least this amount of free of VRAM left after the screen.
+ // E.g. on a 32MB VRAM PPC MacMini, going 1280x960 2xAA still corrupts the screen (VRAM taken: 23.5MB).
+ // So we need to keep somewhat more free than 8MB... 16MB works.
+ const int vramAllowedWithKeepingSomeFree = int(vramKB - 16*1024);
+
+ int vramAllowedKB = std::min(vramAllowedByPortion, std::min(vramAllowedMax,vramAllowedWithKeepingSomeFree));
+ int vramNeededKB;
+ do {
+ vramNeededKB = width * height * (std::max(fsaa,1) * (backbufferBPP+depthBPP) + frontbufferBPP) / 1024;
+ if( vramNeededKB < vramAllowedKB )
+ break;
+
+ #if !UNITY_RELEASE
+ printf_console("Screen %ix%i at %ixAA won't fit, reducing AA (needed mem=%i allowedmem=%i)\n", width, height, fsaa, vramNeededKB, vramAllowedKB );
+ #endif
+ fsaa /= 2;
+ } while( fsaa > 1 );
+
+ if( fsaa == 1 )
+ fsaa = 0;
+
+ return fsaa;
+}
diff --git a/Runtime/GfxDevice/VramLimits.h b/Runtime/GfxDevice/VramLimits.h
new file mode 100644
index 0000000..238f260
--- /dev/null
+++ b/Runtime/GfxDevice/VramLimits.h
@@ -0,0 +1,11 @@
+#pragma once
+
+// Various limits for what we can do based on VRAM size
+
+#define kVRAMEnoughForLargeShadowmaps 480 // VRAM MB after which we allow even higher resolution shadow maps
+
+#define kVRAMMaxFreePortionForShadowMap 0.3f // allow single shadowmap to take 30% of possibly free VRAM
+#define kVRAMMaxFreePortionForTexture 0.4f // allow single texture to take 40% of possibly free VRAM
+
+
+int ChooseSuitableFSAALevel( int width, int height, int backbufferBPP, int frontbufferBPP, int depthBPP, int fsaa );
diff --git a/Runtime/GfxDevice/d3d/CombinerD3D.cpp b/Runtime/GfxDevice/d3d/CombinerD3D.cpp
new file mode 100644
index 0000000..2be2b47
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/CombinerD3D.cpp
@@ -0,0 +1,600 @@
+#include "UnityPrefix.h"
+#include "CombinerD3D.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/shaderlab/Library/pass.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "D3D9Context.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "External/DirectX/builds/dx9include/d3dx9.h"
+
+
+// --------------------------------------------------------------------------
+// Combiners to fixed function texture stages
+
+// NOTE: not all GL combiner modes are representable in TSS:
+// * per-stage constants
+// * DOUBLE/QUAD on arbitrary operations
+// * a*b+-c, a*b-c
+// So what we do is: if hardware supports ps_1_1, we generate pixel shaders on the fly, see below.
+
+static D3DTEXTUREOP kCombinerFuncTable[3][8] = {
+ { D3DTOP_SELECTARG1, D3DTOP_MODULATE, D3DTOP_ADD, D3DTOP_ADDSIGNED, D3DTOP_SUBTRACT, D3DTOP_LERP, D3DTOP_DOTPRODUCT3, D3DTOP_DOTPRODUCT3 },
+ { D3DTOP_ADD, D3DTOP_MODULATE2X, D3DTOP_DISABLE, D3DTOP_ADDSIGNED2X, D3DTOP_DISABLE, D3DTOP_DISABLE, D3DTOP_DISABLE, D3DTOP_DISABLE },
+ { D3DTOP_DISABLE, D3DTOP_MODULATE4X, D3DTOP_DISABLE, D3DTOP_DISABLE, D3DTOP_DISABLE, D3DTOP_DISABLE, D3DTOP_DISABLE, D3DTOP_DISABLE },
+};
+static DWORD kCombinerFuncCapsTable[3][8] = {
+ { D3DTEXOPCAPS_SELECTARG1, D3DTEXOPCAPS_MODULATE, D3DTEXOPCAPS_ADD, D3DTEXOPCAPS_ADDSIGNED, D3DTEXOPCAPS_SUBTRACT, D3DTEXOPCAPS_LERP, D3DTEXOPCAPS_DOTPRODUCT3, D3DTEXOPCAPS_DOTPRODUCT3 },
+ { D3DTEXOPCAPS_ADD, D3DTEXOPCAPS_MODULATE2X, 0, D3DTEXOPCAPS_ADDSIGNED2X, 0, 0, 0, 0 },
+ { 0, D3DTEXOPCAPS_MODULATE4X, 0, 0, 0, 0, 0, 0 },
+};
+
+void InitializeCombinerCapsD3D9()
+{
+ DWORD texOpCaps = gGraphicsCaps.d3d.d3dcaps.TextureOpCaps;
+ for( int r = 0; r < 3; ++r ) {
+ for( int c = 0; c < 8; ++c ) {
+ if( kCombinerFuncCapsTable[r][c] ) {
+ if( !(texOpCaps & kCombinerFuncCapsTable[r][c]) )
+ kCombinerFuncTable[r][c] = D3DTOP_DISABLE;
+ }
+ }
+ }
+}
+
+static int kCombinerSourceTable[4] = {
+ D3DTA_CURRENT, D3DTA_TEXTURE, D3DTA_TFACTOR, D3DTA_DIFFUSE // TODO: TFACTOR is global, not per-stage!
+};
+static const int kCombinerOperandModTableRGB[4] = {
+ 0, D3DTA_ALPHAREPLICATE, D3DTA_COMPLEMENT, D3DTA_ALPHAREPLICATE | D3DTA_COMPLEMENT
+};
+static const int kCombinerOperandModTableAlpha[4] = {
+ 0, 0, D3DTA_COMPLEMENT, D3DTA_COMPLEMENT
+};
+
+
+
+static bool CombinerToTextureStage( UInt32 comb, D3DTEXTUREOP& outOp, int outArgs[3], bool alpha )
+{
+ int s0 = (comb >> combiner::kSrcZeroShift) & 0xFF;
+
+ int cf = COMBINER_GET_FUNC(comb);
+ int s1 = (comb) & 0xFF;
+ int scale = HighestBit( (comb >> combiner::kScaleShift) );
+ AssertIf( scale < 0 || scale > 2 );
+
+ const int* kCombinerOperandModTable = alpha ? kCombinerOperandModTableAlpha : kCombinerOperandModTableRGB;
+ int source0 = kCombinerSourceTable[s0 & combiner::kSourceMask] | kCombinerOperandModTable[s0 >> combiner::kOperandShift];
+ int source1 = kCombinerSourceTable[s1 & combiner::kSourceMask] | kCombinerOperandModTable[s1 >> combiner::kOperandShift];
+ if( !(cf & combiner::kBlendFuncMask) )
+ {
+ outOp = kCombinerFuncTable[scale][cf];
+ if( outOp == D3DTOP_DISABLE )
+ return false;
+ // we emulate "source double" with "source + source"
+ if( cf == 0 )
+ source1 = source0;
+ outArgs[0] = source0;
+ outArgs[1] = source1;
+ outArgs[2] = D3DTA_CURRENT;
+ }
+ else
+ {
+ int blendF = COMBINER_GET_BLEND_FUNC_INDEX(cf);
+ int src2 = cf & combiner::kSourceMask;
+ int oper2 = ((cf & combiner::kOperandTwo) >> combiner::kOperandShift) | 1;
+ int source2 = kCombinerSourceTable[src2] | kCombinerOperandModTable[oper2];
+
+ DWORD texOpCaps = gGraphicsCaps.d3d.d3dcaps.TextureOpCaps;
+
+ switch( blendF )
+ {
+ case 0:
+ // src0 lerp(src2 alpha) src1
+ if( (scale != 0) || !(texOpCaps & D3DTEXOPCAPS_LERP) )
+ return false;
+ outOp = D3DTOP_LERP;
+ outArgs[0] = source0;
+ outArgs[1] = source1;
+ outArgs[2] = source2;
+ break;
+ case 1:
+ // src0 * src2 alpha + src1
+ if( texOpCaps & D3DTEXOPCAPS_MULTIPLYADD ) {
+ if( scale != 0 )
+ return false;
+ outOp = D3DTOP_MULTIPLYADD;
+ outArgs[0] = source0;
+ outArgs[1] = source2;
+ outArgs[2] = source1;
+ } else {
+ // TODO
+ return false;
+ }
+ break;
+ case 2:
+ // src0 * src2 alpha +- src1
+ // not supported!
+ return false;
+ case 3:
+ // src0 * src2 alpha - src1
+ // not supported!
+ return false;
+ default:
+ AssertString( "Unknown combiner blend function" );
+ return false;
+ }
+ AssertIf( outOp <= D3DTOP_DISABLE || outOp > D3DTOP_LERP );
+ }
+
+ return true;
+}
+
+static bool CombinerToTextureStage( const ShaderLab::TextureBinding& te, D3DTextureStage& stage, bool& outTFactorUsed )
+{
+ int combColor = te.m_CombColor;
+ if( !CombinerToTextureStage( combColor, stage.colorOp, stage.colorArgs, false ) )
+ return false;
+
+ // For DOT3 operation, we have to force using no function on alpha
+ // However, on some old cards this has no effect; they always replicate DOT3 to all channels
+ // (e.g. GeForce 2). Oh well.
+ int combColorFunc = COMBINER_GET_FUNC(combColor);
+ int combAlpha = te.m_CombAlpha;
+ if( combColorFunc == 6 ) { // DOT3
+ combAlpha &= ~(0xFF << combiner::kFuncShift);
+ }
+ if( !CombinerToTextureStage( combAlpha, stage.alphaOp, stage.alphaArgs, true ) )
+ return false;
+
+ if( te.IsTexColorUsed() )
+ {
+ outTFactorUsed = true;
+ }
+
+ return true;
+}
+
+
+// --------------------------------------------------------------------------
+// Combiners to pixel shader 1.1
+
+
+// Supports up to 4 texture stages.
+// Each stage outputs into r0 register.
+// Per-stage constants are stored in corresponding constant registers [c0..c3].
+// "Color" command (TFACTOR equivalent) should store in c4.
+// r1 is used in some cases to load & process some constants (e.g. where we'd want to do a c4_bias, we first load it into r1 and then do r1_bias)
+
+
+// Cache for generated pixel shaders.
+struct CombinersCacheEntry
+{
+ UInt32 combColor[kMaxD3DTextureStagesForPS];
+ UInt32 combAlpha[kMaxD3DTextureStagesForPS];
+ IDirect3DPixelShader9* pixelShader;
+ bool specular;
+
+ bool Equals( int count, const ShaderLab::TextureBinding* texEnvs, bool specular ) const
+ {
+ AssertIf( count > kMaxD3DTextureStagesForPS );
+ if( specular != this->specular )
+ return false;
+ for( int i = 0; i < count; ++i )
+ {
+ if( texEnvs[i].m_CombColor != combColor[i] )
+ return false;
+ if( texEnvs[i].m_CombAlpha != combAlpha[i] )
+ return false;
+ }
+ return true;
+ }
+};
+// Not a map on purpose - comparison is cheap and we want to store everything in a single block.
+static std::vector<CombinersCacheEntry> s_CombinersCache[kMaxD3DTextureStagesForPS][2]; // [2] = lighting off, lighting on
+
+void TextureCombinersD3D::CleanupCombinerCache()
+{
+ for( int i = 0; i < kMaxD3DTextureStagesForPS; ++i )
+ {
+ for( int j = 0; j < 2; ++j )
+ {
+ std::vector<CombinersCacheEntry>& cache = s_CombinersCache[i][j];
+ for( int k = 0; k < cache.size(); ++k )
+ {
+ IDirect3DPixelShader9* ps = cache[k].pixelShader;
+ if( ps ) {
+ ULONG refCount = ps->Release();
+ AssertIf( refCount != 0 );
+ }
+ }
+ cache.clear();
+ cache.swap(std::vector<CombinersCacheEntry>());
+ }
+ }
+}
+
+static const char* kPSDestRegRGBA[3] = { " r0", "_x2 r0", "_x4 r0" };
+static const char* kPSDestRegRGB [3] = { " r0.rgb", "_x2 r0.rgb", "_x4 r0.rgb" };
+static const char* kPSDestRegA [3] = { " r0.a", "_x2 r0.a", "_x4 r0.a" };
+
+static const char* kPSOperandPrefixTable[4] = {
+ "", "", "1-", "1-",
+};
+static const char* kPSOperandSuffixTableRGB[4] = {
+ "", ".a", "", ".a",
+};
+
+static std::string CombinerSrcPS( combiner::Source source, int stage, bool lighting )
+{
+ switch( source )
+ {
+ case combiner::kSrcPrevious:
+ if( stage == 0 )
+ return lighting ? "v0" : "c4";
+ else
+ return "r0";
+ case combiner::kSrcTexture:
+ return 't' + IntToString(stage);
+ case combiner::kSrcConstant:
+ return 'c' + IntToString(stage);
+ case combiner::kSrcPrimaryColor:
+ return lighting ? "v0" : "c4";
+ default:
+ AssertString( "Unknown source" );
+ return "";
+ }
+}
+
+static void FixupForConstantModifiers( std::string& source, std::string& outFixup, UInt32 operand, bool alpha, bool dot3 )
+{
+ if( source.size() >= 4 && source[0]=='1' && source[1]=='-' && source[2] == 'c' )
+ {
+ std::string sub = source.substr( 3, source.size()-3 );
+ outFixup += std::string( alpha ? "+mov_sat r1.a, c" : "mov_sat r1.rgb, c" ) + sub + '\n';
+ source = "1-r1";
+ if( !alpha )
+ source += kPSOperandSuffixTableRGB[operand];
+ }
+ if( dot3 && source.size() >= 2 && source[0]=='c' )
+ {
+ std::string sub = source.substr( 1, source.size()-1 );
+ outFixup += std::string( alpha ? "+mov_sat r1.a, c" : "mov_sat r1.rgb, c" ) + sub + '\n';
+ source = "r1";
+ if( !alpha )
+ source += kPSOperandSuffixTableRGB[operand];
+ }
+}
+
+static void CombinerToPixelShaderText( int stage, bool lighting, UInt32 comb, bool alpha, std::string& outInstruction, std::string& outPrevFixup, bool& outPrevSat, bool& outSkipAlpha )
+{
+ outSkipAlpha = false;
+
+ int s0 = (comb >> combiner::kSrcZeroShift) & 0xFF;
+
+ int cf = COMBINER_GET_FUNC(comb);
+ int s1 = (comb) & 0xFF;
+ int scale = HighestBit( (comb >> combiner::kScaleShift) );
+ AssertIf( scale < 0 || scale > 2 );
+
+ combiner::Source src0 = static_cast<combiner::Source>(s0 & combiner::kSourceMask);
+ combiner::Source src1 = static_cast<combiner::Source>(s1 & combiner::kSourceMask);
+ UInt32 oper0 = s0 >> combiner::kOperandShift;
+ UInt32 oper1 = s1 >> combiner::kOperandShift;
+ std::string source0 = kPSOperandPrefixTable[oper0] + CombinerSrcPS( src0, stage, lighting );
+ if( !alpha )
+ source0 += kPSOperandSuffixTableRGB[oper0];
+ std::string source1 = kPSOperandPrefixTable[oper1] + CombinerSrcPS( src1, stage, lighting );
+ std::string suffix1 = alpha ? "" : kPSOperandSuffixTableRGB[oper1];
+ const char** kPSDestReg = (cf == 7) ? kPSDestRegRGBA : (alpha ? kPSDestRegA : kPSDestRegRGB);
+ std::string destReg = kPSDestReg[scale];
+
+ std::string text;
+
+ // Some special rules:
+ // * For bias modifier or a lerp, a previous instruction should saturate the result
+ // * For negate modifier, a previous instruction can't saturate the result (hence can't just saturate everything)
+ // * 1-x or x_bias not allowed on constants. So if we detect such case, we try to load it into r1
+ // with additional instruction.
+ // * Emulating DOT3 requires _bx2 modifier, which is not allowed on constants. So we also detect
+ // that and load it into r1 with additional instruction.
+
+ bool dot3 = (cf == 6 || cf == 7);
+ FixupForConstantModifiers( source0, outPrevFixup, oper0, alpha, dot3 );
+ FixupForConstantModifiers( source1, outPrevFixup, oper1, alpha, dot3 );
+
+ bool addSatOnPrevious = false; // should we add "saturate" on previous instruction?
+ if( !(cf & combiner::kBlendFuncMask) )
+ {
+ switch( cf )
+ {
+ case 0: text = "mov" + destReg + ", " + source0; break;
+ case 1: text = "mul" + destReg + ", " + source0 + ", " + source1 + suffix1; break;
+ case 2: text = "add" + destReg + ", " + source0 + ", " + source1 + suffix1; break;
+ case 3:
+ if( source1[0] == 'c' )
+ {
+ std::string sub = source1.substr( 1, source1.size()-1 );
+ outPrevFixup += std::string( alpha ? "+mov_sat r1.a, c" : "mov_sat r1.rgb, c" ) + sub + '\n';
+ source1 = "r1";
+ }
+ text = "add" + destReg + ", " + source0 + ", " + source1 + "_bias" + suffix1;
+ if( (s1 & combiner::kSourceMask) == 0 ) // if source1 is "previous", need to saturate previous instruction
+ addSatOnPrevious = true;
+ break;
+ case 4: text = "sub" + destReg + ", " + source0 + ", " + source1 + suffix1; break;
+ case 5: AssertIf(false); break;
+ case 6:
+ // DOT3: for rgb do the dot, for alpha just use source0
+ if( alpha )
+ text += "mov" + destReg + ", " + source0;
+ else
+ text = "dp3" + destReg + ", " + source0 + "_bx2, " + source1 + "_bx2" + suffix1; break;
+ case 7:
+ // DOT3RGBA: do the dot into all four channels, and do not issue co-issued alpha
+ // instructions for this stage.
+ text = "dp3" + destReg + ", " + source0 + "_bx2, " + source1 + "_bx2" + suffix1;
+ outSkipAlpha = true;
+ break;
+ }
+ }
+ else
+ {
+ int blendF = COMBINER_GET_BLEND_FUNC_INDEX(cf);
+ combiner::Source src2 = static_cast<combiner::Source>(cf & combiner::kSourceMask);
+ int oper2 = ((cf & combiner::kOperandTwo) >> combiner::kOperandShift) | 1;
+ std::string source2 = kPSOperandPrefixTable[oper2] + CombinerSrcPS(src2, stage, lighting);
+ if( !alpha )
+ source2 += kPSOperandSuffixTableRGB[oper2];
+
+ FixupForConstantModifiers( source2, outPrevFixup, oper2, alpha, false );
+
+ switch( blendF )
+ {
+ case 0:
+ // src0 lerp(src2 alpha) src1
+ text = "lrp" + destReg + ", " + source2 + ", " + source0 + ", " + source1 + suffix1;
+ if( src2 == combiner::kSrcPrevious ) // if src2 is "previous", need to saturate previous instruction
+ addSatOnPrevious = true;
+ break;
+ case 1:
+ // src0 * src2 alpha + src1
+ text = "mad" + destReg + ", " + source0 + ", " + source2 + ", " + source1 + suffix1;
+ break;
+ case 2:
+ // src0 * src2 alpha +- src1
+ if( source1[0] == 'c' )
+ {
+ std::string sub = source1.substr( 1, source1.size()-1 );
+ outPrevFixup += std::string( alpha ? "+mov_sat r1.a, c" : "mov_sat r1.rgb, c" ) + sub + '\n';
+ source1 = "r1";
+ }
+ text = "mad" + destReg + ", " + source0 + ", " + source2 + ", " + source1 + "_bias" + suffix1;
+ if( (s1 & combiner::kSourceMask) == 0 ) // if source1 is "previous", need to saturate previous instruction
+ addSatOnPrevious = true;
+ break;
+ case 3:
+ // src0 * src2 alpha - src1
+ text = "mad" + destReg + ", " + source0 + ", " + source2 + ", -" + source1 + suffix1;
+ break;
+ default:
+ AssertString( "Unknown combiner blend function" );
+ break;
+ }
+ }
+
+ // if we're not the first instruction and we need to modify previous one - do it.
+ int typeIndex = alpha ? 1 : 0;
+ outPrevSat = false;
+ if( stage != 0 && addSatOnPrevious )
+ outPrevSat = true;
+
+ if( alpha )
+ outInstruction += '+';
+ outInstruction += text;
+ outInstruction += '\n';
+}
+
+
+// GpuProgramsD3D.cpp
+ID3DXBuffer* AssembleD3DShader( const std::string& source );
+
+
+static IDirect3DPixelShader9* CombinersToPixelShader( int count, const ShaderLab::TextureBinding* texEnvs, bool lighting, bool addSpecular )
+{
+ AssertIf( count < 1 );
+
+ // ps_1_1 supports only 4 textures
+ if (count > kMaxD3DTextureStagesForPS)
+ return NULL;
+
+ // look for such combiner setup in cache
+ int lightingIdx = lighting ? 1 : 0;
+ int cacheCount = s_CombinersCache[count-1][lightingIdx].size();
+ for( int i = 0; i < cacheCount; ++i )
+ {
+ CombinersCacheEntry& ce = s_CombinersCache[count-1][lightingIdx][i];
+ if( ce.Equals( count, texEnvs, addSpecular ) )
+ {
+ AssertIf( !ce.pixelShader );
+ return ce.pixelShader;
+ }
+ }
+
+ std::string text = "ps_1_1\n";
+ // sample textures
+ for( int i = 0; i < count; ++i )
+ {
+ text += "tex t" + IntToString(i) + '\n';
+ }
+
+ // do combiner operations
+ CombinersCacheEntry cacheEntry;
+ int previousInstructions[2] = { 0, 0 };
+ for( int i = 0; i < count; ++i )
+ {
+ UInt32 combColor = texEnvs[i].m_CombColor;
+ UInt32 combAlpha = texEnvs[i].m_CombAlpha;
+ cacheEntry.combColor[i] = combColor;
+ cacheEntry.combAlpha[i] = combAlpha;
+ cacheEntry.specular = addSpecular;
+ std::string instruction, fixup;
+ bool satPrevious, skipAlpha;
+ // color
+ CombinerToPixelShaderText( i, lighting, combColor, false, instruction, fixup, satPrevious, skipAlpha );
+ if( satPrevious ) {
+ while( text[previousInstructions[0]] != ' ' )
+ ++previousInstructions[0];
+ text.insert( previousInstructions[0], "_sat" );
+ previousInstructions[1] += 4; // move the other pointer forward by _sat length as well
+ }
+ int colorInstructionLength = instruction.size();
+ // alpha
+ if( !skipAlpha )
+ {
+ CombinerToPixelShaderText( i, lighting, combAlpha, true, instruction, fixup, satPrevious, skipAlpha );
+ if( satPrevious ) {
+ while( text[previousInstructions[1]] != ' ' )
+ ++previousInstructions[1];
+ text.insert( previousInstructions[1], "_sat" );
+ previousInstructions[0] += 4; // move the other pointer forward by _sat length as well
+ }
+ }
+
+ text += fixup;
+ previousInstructions[0] = text.size();
+ previousInstructions[1] = text.size() + (skipAlpha ? 0 : colorInstructionLength);
+ text += instruction;
+ }
+
+ // add specular at the end if needed
+ if( addSpecular )
+ {
+ text += "add r0.rgb, r0, v1";
+ }
+
+ // compile pixel shader
+ HRESULT hr;
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ // assemble shader
+ ID3DXBuffer *compiledShader = AssembleD3DShader( text );
+ IDirect3DPixelShader9* ps = NULL;
+ if( compiledShader )
+ {
+ // create shader
+ hr = dev->CreatePixelShader( (const DWORD*)compiledShader->GetBufferPointer(), &ps );
+ compiledShader->Release();
+ if( FAILED(hr) )
+ {
+ ErrorStringMsg ("D3D9 Combiners: failed to create pixel shader representation: %s", text.c_str());
+ }
+ }
+ AssertIf( !ps );
+
+ // insert into cache
+ cacheEntry.pixelShader = ps;
+ s_CombinersCache[count-1][lightingIdx].push_back( cacheEntry );
+
+ return ps;
+}
+
+
+// --------------------------------------------------------------------------
+
+
+
+TextureCombinersD3D* TextureCombinersD3D::Create( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular )
+{
+ static int uniqueIDCounter = 1;
+
+ static bool combinerCapsInitialized = false;
+ if( !combinerCapsInitialized )
+ {
+ InitializeCombinerCapsD3D9();
+ combinerCapsInitialized = true;
+ }
+
+ // For threaded rendering this check is done on the client side (and we get NULL here)
+ if (props)
+ {
+ // check texgen modes & texture dimension are supported
+ for( int i = 0; i < count; ++i ) {
+ TextureDimension texDim;
+ TexGenMode texGen;
+ GetTexEnvInfoFromName( texEnvs[i].m_TextureName, texDim, texGen, props );
+ if( !ShaderLab::IsTexEnvSupported( texEnvs[i].m_TextureName, texDim, texGen ) )
+ return NULL;
+ }
+ }
+
+ bool canConvertToStages = true;
+
+ // "primary" in the combiner might refer to diffuse or texture factor, depending on
+ // whether lighting is on or vertex colors are bound
+ kCombinerSourceTable[3] = hasVertexColorOrLighting ? D3DTA_DIFFUSE : D3DTA_TFACTOR;
+
+ TextureCombinersD3D* d3dte = new TextureCombinersD3D();
+ d3dte->uniqueID = ++uniqueIDCounter;
+ d3dte->envCount = count;
+ d3dte->texEnvs = texEnvs;
+ d3dte->pixelShader = NULL;
+ d3dte->textureFactorIndex = -1;
+
+ // special case: when no SetTextures are present, setup to do { combine primary } equivalent
+ if( count == 0 )
+ {
+ d3dte->stages[0].colorOp = D3DTOP_SELECTARG1;
+ d3dte->stages[0].colorArgs[0] = d3dte->stages[0].colorArgs[1] = d3dte->stages[0].colorArgs[2] = kCombinerSourceTable[3];
+ d3dte->stages[0].alphaOp = D3DTOP_SELECTARG1;
+ d3dte->stages[0].alphaArgs[0] = d3dte->stages[0].alphaArgs[1] = d3dte->stages[0].alphaArgs[2] = kCombinerSourceTable[3];
+ d3dte->stages[1].colorOp = D3DTOP_DISABLE;
+ d3dte->stageCount = 1;
+ return d3dte;
+ }
+
+ // try to convert to pixel shader and use that if everything is ok
+ d3dte->pixelShader = CombinersToPixelShader( count, texEnvs, hasVertexColorOrLighting, usesAddSpecular );
+
+ // if don't have pixel shader, convert to TSS setup
+ if( !d3dte->pixelShader )
+ {
+ if( count > gGraphicsCaps.d3d.d3dcaps.MaxSimultaneousTextures )
+ {
+ // In theory we could convert more; if most of combiner stages do not actually
+ // use the texture. In practice we just cap it at MaxSimultaneousTextures;
+ // it will match GL behaviour as well.
+ canConvertToStages = false;
+ }
+ else
+ {
+ for( int i = 0; i < count; ++i )
+ {
+ const ShaderLab::TextureBinding& te = texEnvs[i];
+ D3DTextureStage& stage = d3dte->stages[i];
+ bool textureFactorUsed = false;
+ if( !CombinerToTextureStage(te, stage, textureFactorUsed) )
+ {
+ canConvertToStages = false;
+ break;
+ }
+ if (textureFactorUsed)
+ {
+ d3dte->textureFactorIndex = i;
+ }
+ }
+ d3dte->stages[count].colorOp = D3DTOP_DISABLE;
+ }
+
+ // no can't do
+ if( !canConvertToStages )
+ {
+ delete d3dte;
+ return NULL;
+ }
+ }
+
+ d3dte->stageCount = count;
+ return d3dte;
+}
diff --git a/Runtime/GfxDevice/d3d/CombinerD3D.h b/Runtime/GfxDevice/d3d/CombinerD3D.h
new file mode 100644
index 0000000..6f7fb05
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/CombinerD3D.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include "D3D9Includes.h"
+#include "External/shaderlab/Library/shadertypes.h"
+
+namespace ShaderLab {
+ struct TextureBinding;
+ class TexEnv;
+}
+
+
+const int kMaxD3DTextureStages = 8;
+const int kMaxD3DTextureStagesForPS = 4;
+
+struct D3DTextureStage
+{
+ D3DTEXTUREOP colorOp;
+ int colorArgs[3];
+ D3DTEXTUREOP alphaOp;
+ int alphaArgs[3];
+};
+
+struct TextureCombinersD3D
+{
+ static TextureCombinersD3D* Create( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular );
+ static void CleanupCombinerCache();
+
+ D3DTextureStage stages[kMaxD3DTextureStages+1];
+ int envCount, stageCount; // these might be different!
+ IDirect3DPixelShader9* pixelShader;
+ const ShaderLab::TextureBinding* texEnvs;
+
+ int textureFactorIndex;
+ bool textureFactorUsed;
+
+ int uniqueID;
+};
diff --git a/Runtime/GfxDevice/d3d/D3D9Context.cpp b/Runtime/GfxDevice/d3d/D3D9Context.cpp
new file mode 100644
index 0000000..e192ad8
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/D3D9Context.cpp
@@ -0,0 +1,629 @@
+#include "UnityPrefix.h"
+#include "D3D9Context.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "D3D9Enumeration.h"
+#include "D3D9Utils.h"
+#include "GfxDeviceD3D9.h"
+#include "TimerQueryD3D9.h"
+#include "PlatformDependent/Win/WinUtils.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Threads/ThreadSharedObject.h"
+#include "Runtime/Misc/Plugins.h"
+#if UNITY_EDITOR
+#include "Runtime/GfxDevice/GfxDeviceSetup.h"
+#include "Runtime/Misc/QualitySettings.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "D3D9Window.h"
+#endif
+
+#if WEBPLUG
+#define ENABLE_NV_PERFHUD 0
+#else
+#define ENABLE_NV_PERFHUD 1
+#endif
+
+#define ENABLE_D3D_WINDOW_LOGGING 1
+
+static IDirect3D9* s_D3D = NULL;
+static IDirect3DDevice9* s_Device = NULL;
+
+static RenderColorSurfaceD3D9 s_BackBuffer;
+static RenderDepthSurfaceD3D9 s_DepthStencil;
+static HWND s_Window = NULL;
+static HINSTANCE s_D3DDll = NULL;
+static D3DPRESENT_PARAMETERS s_PresentParams;
+static D3D9FormatCaps* s_FormatCaps = NULL;
+static bool s_CurrentlyWindowed = true;
+static D3DDISPLAYMODE s_LastWindowedMode;
+bool g_D3DUsesMixedVP = false;
+bool g_D3DHasDepthStencil = true;
+D3DFORMAT g_D3DDepthStencilFormat = D3DFMT_D16;
+D3DDEVTYPE g_D3DDevType;
+DWORD g_D3DAdapter = D3DADAPTER_DEFAULT;
+
+#if WEBPLUG
+extern bool gInsideFullscreenToggle;
+#endif
+
+typedef IDirect3D9* (WINAPI* Direct3DCreate9Func)(UINT);
+
+GfxDeviceD3D9& GetD3D9GfxDevice();
+void SetD3D9DeviceLost( bool lost ); // GfxDeviceD3D9.cpp
+bool IsD3D9DeviceLost();
+void ResetDynamicResourcesD3D9();
+
+#if ENABLE_PROFILER
+D3DPERF_BeginEventFunc g_D3D9BeginEventFunc;
+D3DPERF_EndEventFunc g_D3D9EndEventFunc;
+#endif
+
+
+bool InitializeD3D(D3DDEVTYPE devtype)
+{
+ AssertIf( s_D3D || s_Device || s_Window || s_D3DDll || s_FormatCaps );
+ g_D3DDevType = devtype;
+
+ s_D3DDll = LoadLibrary( "d3d9.dll" );
+ if( !s_D3DDll )
+ {
+ printf_console( "d3d: no D3D9 installed\n" );
+ return false; // no d3d9 installed
+ }
+
+ Direct3DCreate9Func createFunc = (Direct3DCreate9Func)GetProcAddress( s_D3DDll, "Direct3DCreate9" );
+ if( !createFunc )
+ {
+ printf_console( "d3d: Direct3DCreate9 not found\n" );
+ FreeLibrary( s_D3DDll );
+ s_D3DDll = NULL;
+ return false; // for some reason Direct3DCreate9 not found
+ }
+
+ #if ENABLE_PROFILER
+ g_D3D9BeginEventFunc = (D3DPERF_BeginEventFunc)GetProcAddress(s_D3DDll, "D3DPERF_BeginEvent");
+ g_D3D9EndEventFunc = (D3DPERF_EndEventFunc)GetProcAddress(s_D3DDll, "D3DPERF_EndEvent");
+ #endif
+
+ // create D3D object
+ s_D3D = createFunc( D3D_SDK_VERSION );
+ if( !s_D3D )
+ {
+ printf_console( "d3d: no 9.0c available\n" );
+ FreeLibrary( s_D3DDll );
+ s_D3DDll = NULL;
+ return false; // D3D initialization failed
+ }
+
+ // validate the adapter ordinal
+ UINT adapterCount = s_D3D->GetAdapterCount();
+ if ( g_D3DAdapter >= adapterCount )
+ g_D3DAdapter = D3DADAPTER_DEFAULT;
+
+ // check whether we have a HAL device
+ D3DDISPLAYMODE mode;
+ HRESULT hr;
+ if (FAILED(hr = s_D3D->GetAdapterDisplayMode(g_D3DAdapter, &mode)))
+ {
+ printf_console ("d3d: failed to get adapter mode (adapter %d error 0x%08x)\n", g_D3DAdapter, hr);
+ s_D3D->Release();
+ s_D3D = NULL;
+ FreeLibrary( s_D3DDll );
+ s_D3DDll = NULL;
+ return false; // failed to get adapter mode
+ }
+ if( FAILED( s_D3D->CheckDeviceType( g_D3DAdapter, g_D3DDevType, mode.Format, mode.Format, TRUE ) ) )
+ {
+ printf_console( "d3d: no support for this device type (accelerated/ref)\n" );
+ s_D3D->Release();
+ s_D3D = NULL;
+ FreeLibrary( s_D3DDll );
+ s_D3DDll = NULL;
+ return false; // no HAL driver available
+ }
+
+ // enumerate all formats, multi sample types and whatnot
+ s_FormatCaps = new D3D9FormatCaps();
+ if( !s_FormatCaps->Enumerate( *s_D3D ) )
+ {
+ printf_console( "d3d: no video modes available\n" );
+ return false;
+ }
+
+ return true;
+}
+
+IDirect3D9* GetD3DObject()
+{
+ AssertIf( !s_D3D );
+ return s_D3D;
+}
+D3D9FormatCaps* GetD3DFormatCaps()
+{
+ AssertIf( !s_FormatCaps );
+ return s_FormatCaps;
+}
+
+void CleanupD3D()
+{
+ AssertIf( s_Device || s_Window );
+
+ delete s_FormatCaps;
+ s_FormatCaps = NULL;
+
+ if( s_D3D )
+ {
+ s_D3D->Release();
+ s_D3D = NULL;
+ }
+ if( s_D3DDll )
+ {
+ FreeLibrary( s_D3DDll );
+ s_D3DDll = NULL;
+ }
+}
+
+D3DFORMAT GetD3DFormatForChecks()
+{
+ AssertIf( !s_FormatCaps );
+ return s_FormatCaps->GetAdapterFormatForChecks();
+}
+
+static void SetFramebufferDepthFormat(GfxDevice* realDevice, D3DFORMAT format)
+{
+ // Not the most robust way to figure out the format, but should do.
+ int depthBPP = GetBPPFromD3DFormat(format);
+ DepthBufferFormat depthFormat = kDepthFormatNone;
+ if (depthBPP == 16)
+ depthFormat = kDepthFormat16;
+ else if (depthBPP == 32)
+ depthFormat = kDepthFormat24;
+ realDevice->SetFramebufferDepthFormat(depthFormat);
+
+ // Set it on the client device as well, if we're changing resolutions
+ // and the property hasn't been propagated by copying from the real to client device.
+ if (IsGfxDevice())
+ GetGfxDevice().SetFramebufferDepthFormat(depthFormat);
+}
+
+bool InitializeOrResetD3DDevice(
+ class GfxDevice* device,
+ HWND window, int width, int height,
+ int refreshRate, bool fullscreen, int vBlankCount, int fsaa,
+ int& outBackbufferBPP, int& outFrontbufferBPP, int& outDepthBPP, int& outFSAA )
+{
+ AssertIf( !s_D3D );
+
+ outBackbufferBPP = 4;
+ outFrontbufferBPP = 4;
+ outDepthBPP = 4;
+ outFSAA = 0;
+
+ width = std::max(width, 1);
+ height = std::max(height, 1);
+
+ D3DDISPLAYMODE mode;
+ if( s_CurrentlyWindowed )
+ {
+ HRESULT hr = s_D3D->GetAdapterDisplayMode( g_D3DAdapter, &mode );
+ if( FAILED( hr ) )
+ {
+ printf_console( "d3d initialize: failed to get adapter display mode [%s]\n", GetD3D9Error(hr) );
+ return false;
+ }
+ s_LastWindowedMode = mode;
+ }
+ else
+ {
+ // If we are fullscreen right now, use last checked Windowed mode format
+ // to choose compatible formats. Otherwise we won't be able to switch to 16 bit
+ // desktop mode after a 32 bit fullscreen one.
+ mode = s_LastWindowedMode;
+ }
+
+ D3DPRESENT_PARAMETERS& pparams = s_PresentParams;
+ ZeroMemory (&pparams, sizeof(D3DPRESENT_PARAMETERS));
+ pparams.BackBufferWidth = width;
+ pparams.BackBufferHeight = height;
+ pparams.BackBufferCount = 1;
+ pparams.hDeviceWindow = window;
+ pparams.FullScreen_RefreshRateInHz = fullscreen ? refreshRate : 0;
+
+ pparams.EnableAutoDepthStencil = FALSE;
+ g_D3DHasDepthStencil = true;
+
+ pparams.Windowed = fullscreen ? FALSE : TRUE;
+ pparams.SwapEffect = D3DSWAPEFFECT_DISCARD;
+
+ // fullscreen FSAA might be buggy
+ if( fullscreen && gGraphicsCaps.buggyFullscreenFSAA )
+ fsaa = 1;
+
+ s_FormatCaps->FindBestPresentationParams( width, height, mode.Format, !fullscreen, vBlankCount, fsaa, pparams );
+
+ outBackbufferBPP = GetBPPFromD3DFormat(pparams.BackBufferFormat)/8;
+ outFrontbufferBPP = GetBPPFromD3DFormat(mode.Format)/8;
+ outDepthBPP = GetBPPFromD3DFormat(pparams.AutoDepthStencilFormat)/8;
+ outFSAA = (pparams.MultiSampleType == D3DMULTISAMPLE_NONMASKABLE) ? pparams.MultiSampleQuality : pparams.MultiSampleType;
+ g_D3DDepthStencilFormat = pparams.AutoDepthStencilFormat;
+ device->SetCurrentTargetSize(pparams.BackBufferWidth, pparams.BackBufferHeight);
+ SetFramebufferDepthFormat(device, pparams.AutoDepthStencilFormat);
+
+ bool deviceInLostState = false;
+ if( !s_Device )
+ {
+ AssertIf( s_Window );
+
+ UINT adapterIndex = g_D3DAdapter;
+ D3DDEVTYPE devType = g_D3DDevType;
+
+ #if ENABLE_NV_PERFHUD
+ UINT adapterCount = s_D3D->GetAdapterCount();
+ D3DADAPTER_IDENTIFIER9 perfHudID;
+ memset( &perfHudID, 0, sizeof(perfHudID) );
+ s_D3D->GetAdapterIdentifier( adapterCount-1, 0, &perfHudID );
+ perfHudID.Description[MAX_DEVICE_IDENTIFIER_STRING-1] = 0;
+ if( strstr( perfHudID.Description, "PerfHUD" ) != NULL )
+ {
+ adapterIndex = adapterCount-1;
+ devType = D3DDEVTYPE_REF;
+ }
+ #endif
+
+ const int kShaderVersion11 = (1 << 8) + 1;
+ bool hasHardwareTL = gGraphicsCaps.d3d.d3dcaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT;
+ bool hasVS11 = LOWORD(gGraphicsCaps.d3d.d3dcaps.VertexShaderVersion) >= kShaderVersion11;
+ DWORD behaviourFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING;
+ if( !hasVS11 )
+ behaviourFlags = D3DCREATE_MIXED_VERTEXPROCESSING;
+ if( !hasHardwareTL )
+ behaviourFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
+ g_D3DUsesMixedVP = (behaviourFlags == D3DCREATE_MIXED_VERTEXPROCESSING);
+
+ if( GetGfxThreadingMode() == kGfxThreadingModeThreaded )
+ behaviourFlags |= D3DCREATE_MULTITHREADED;
+
+ // Preserve FPU mode. Benchmarking both in hardware and software vertex processing does not
+ // reveal any real differences. If FPU mode is not preserved, bad things will happen, like:
+ // * doubles will act like floats
+ // * on Firefox/Safari, some JavaScript libraries will stop working (spect.aculo.us, dojo) - case 17513
+ // * some random funky FPU exceptions will happen
+ HRESULT hr = s_D3D->CreateDevice( adapterIndex, devType, window, behaviourFlags | D3DCREATE_FPU_PRESERVE, &pparams, &s_Device );
+ if( FAILED( hr ) )
+ {
+ printf_console( "d3d: creation params: flags=%x swap=%i vsync=%x w=%i h=%i fmt=%i bbcount=%i dsformat=%i pflags=%x\n",
+ behaviourFlags, pparams.SwapEffect, pparams.PresentationInterval,
+ pparams.BackBufferWidth, pparams.BackBufferHeight, pparams.BackBufferFormat, pparams.BackBufferCount,
+ pparams.AutoDepthStencilFormat, pparams.Flags );
+ printf_console( "d3d: failed to create device [%s]\n", GetD3D9Error(hr) );
+ if (devType == D3DDEVTYPE_REF)
+ {
+ winutils::AddErrorMessage("Reference Rasterizer was requested but is not available.\nPlease make sure you have DirectX SDK installed.");
+ winutils::DisplayErrorMessagesAndQuit ("REFRAST not available");
+ }
+ return false;
+ }
+ s_CurrentlyWindowed = pparams.Windowed ? true : false;
+
+ gGraphicsCaps.hasTimerQuery =
+ (GetD3DDevice()->CreateQuery(D3DQUERYTYPE_TIMESTAMPFREQ, NULL) != D3DERR_NOTAVAILABLE) &&
+ (GetD3DDevice()->CreateQuery(D3DQUERYTYPE_TIMESTAMP,NULL) != D3DERR_NOTAVAILABLE);
+ }
+ else
+ {
+ AssertIf( !s_Window );
+
+ // If we're resetting device mid-frame (e.g. script calls Screen.SetResolution),
+ // we need to end scene, reset and begin scene again.
+ bool wasInsideFrame = GetD3D9GfxDevice().IsInsideFrame();
+ if( wasInsideFrame )
+ {
+ s_Device->EndScene();
+ GetD3D9GfxDevice().SetInsideFrame(false);
+ }
+
+ // cleanup
+ s_BackBuffer.Release();
+ s_DepthStencil.Release();
+
+ PluginsSetGraphicsDevice (s_Device, kGfxRendererD3D9, kGfxDeviceEventBeforeReset);
+
+ D3DPRESENT_PARAMETERS ppcopy = pparams; // copy them, as Reset changes some values
+ HRESULT hr = s_Device->Reset( &ppcopy );
+ if( FAILED(hr) )
+ {
+ if( hr == D3DERR_DEVICELOST )
+ {
+ deviceInLostState = true;
+ SetD3D9DeviceLost( true );
+ }
+ else
+ {
+ ErrorString( Format("D3D device reset failed [%s]", GetD3D9Error(hr)) );
+ return false;
+ }
+ }
+
+ PluginsSetGraphicsDevice (s_Device, kGfxRendererD3D9, kGfxDeviceEventAfterReset);
+
+ s_CurrentlyWindowed = ppcopy.Windowed ? true : false;
+ if( wasInsideFrame && !deviceInLostState )
+ {
+ s_Device->BeginScene();
+ GetD3D9GfxDevice().SetInsideFrame(true);
+ }
+
+#if ENABLE_PROFILER
+ if (gGraphicsCaps.hasTimerQuery)
+ GetD3D9GfxDevice().GetTimerQueries().RecreateAllQueries();
+#endif
+ }
+
+ s_Window = window;
+ if( !deviceInLostState )
+ {
+ s_Device->GetRenderTarget (0, &s_BackBuffer.m_Surface);
+ s_BackBuffer.width = pparams.BackBufferWidth;
+ s_BackBuffer.height = pparams.BackBufferHeight;
+ // create depth stencil
+ D3D9DepthStencilTexture depthStencil = CreateDepthStencilTextureD3D9 (s_Device, pparams.BackBufferWidth, pparams.BackBufferHeight, pparams.AutoDepthStencilFormat, pparams.MultiSampleType, pparams.MultiSampleQuality, TRUE);
+ if (depthStencil.m_Surface)
+ {
+ s_DepthStencil.m_Surface = depthStencil.m_Surface;
+ s_DepthStencil.m_Texture = depthStencil.m_Texture;
+ s_DepthStencil.width = pparams.BackBufferWidth;
+ s_DepthStencil.height = pparams.BackBufferHeight;
+ s_DepthStencil.depthFormat = kDepthFormat16; //@TODO?
+ }
+
+ s_BackBuffer.backBuffer = true;
+ s_DepthStencil.backBuffer = true;
+
+ #if !UNITY_EDITOR
+ RenderSurfaceHandle bbHandle(&s_BackBuffer), dsHandle(&s_DepthStencil);
+ device->SetRenderTargets(1, &bbHandle, dsHandle);
+ #endif
+ s_Device->SetRenderState (D3DRS_ZENABLE, TRUE);
+ }
+
+ return true;
+}
+
+void GetBackBuffersAfterDeviceReset()
+{
+ AssertIf (!s_Device);
+ AssertIf (!s_DepthStencil.m_Surface);
+ s_BackBuffer.Release();
+ s_Device->GetRenderTarget (0, &s_BackBuffer.m_Surface);
+ s_BackBuffer.backBuffer = true;
+}
+
+#if UNITY_EDITOR
+void EditorInitializeD3D(GfxDevice* device)
+{
+ int dummy;
+ if( !InitializeOrResetD3DDevice( device, s_HiddenWindowD3D, 32, 32, 0, false, 0, 0, dummy, dummy, dummy, dummy ) )
+ {
+ winutils::AddErrorMessage( "Failed to create master Direct3D window" );
+ DestroyGfxDevice();
+ winutils::DisplayErrorMessagesAndQuit( "Failed to initialize 3D graphics" );
+ }
+
+ // Disable D3D Debug runtime in editor release mode:
+ // VERTEXSTATS query is only available in Debug runtime.
+ #if UNITY_RELEASE
+ if (CheckD3D9DebugRuntime(GetD3DDevice()))
+ {
+ winutils::AddErrorMessage (
+ "You are using Direct3D Debug Runtime, this is not supported by\r\n"
+ "Unity. Switch to Retail runtime in DirectX Control Panel.");
+ DestroyGfxDevice();
+ winutils::DisplayErrorMessagesAndQuit ("D3D9 Debug Runtime is not supported");
+ }
+ #endif
+}
+#endif
+
+bool FullResetD3DDevice()
+{
+ #if ENABLE_D3D_WINDOW_LOGGING
+ printf_console("FullResetD3DDevice\n");
+ #endif
+ // destroy dynamic VBO / render textures and reset the device
+ ResetDynamicResourcesD3D9();
+ bool ok = ResetD3DDevice();
+ if( ok )
+ SetD3D9DeviceLost( false );
+ return ok;
+}
+
+bool HandleD3DDeviceLost()
+{
+ #if ENABLE_D3D_WINDOW_LOGGING
+ printf_console("HandleD3DDeviceLost\n");
+ #endif
+ HRESULT hr = s_Device->TestCooperativeLevel();
+ bool ok = false;
+ switch( hr )
+ {
+ // Is device actually lost?
+ case D3D_OK:
+ {
+ ok = true;
+ break;
+ }
+ // If device was lost, do not render until we get it back
+ case D3DERR_DEVICELOST:
+ {
+ #if ENABLE_D3D_WINDOW_LOGGING
+ printf_console(" HandleD3DDeviceLost: still lost\n");
+ #endif
+ break;
+ }
+ // If device needs to be reset, do that
+ case D3DERR_DEVICENOTRESET:
+ {
+ #if ENABLE_D3D_WINDOW_LOGGING
+ printf_console(" HandleD3DDeviceLost: needs reset, doing it\n");
+ #endif
+ ok = FullResetD3DDevice();
+ break;
+ }
+ }
+
+ if( !ok )
+ return false;
+
+ // device is not lost anymore, proceed
+ #if ENABLE_D3D_WINDOW_LOGGING
+ printf_console("D3Dwindow device not lost anymore\n");
+ #endif
+ GetBackBuffersAfterDeviceReset();
+ SetD3D9DeviceLost( false );
+
+ return true;
+}
+
+bool ResetD3DDevice()
+{
+ AssertIf( !s_D3D || !s_Device || !s_Window );
+
+ #if ENABLE_D3D_WINDOW_LOGGING
+ printf_console("ResetD3DDevice\n");
+ #endif
+
+ // cleanup
+ s_BackBuffer.Release();
+ s_DepthStencil.Release();
+
+ #if ENABLE_D3D_WINDOW_LOGGING
+ printf_console("dev->Reset\n");
+ #endif
+
+ D3DPRESENT_PARAMETERS ppcopy = s_PresentParams; // copy them, as Reset changes some values
+
+ #if WEBPLUG
+ // Reset sends WM_ACTIVATE message which makes Web Player exit fullscreen (unless gInsideFullscreenToggle is set).
+ bool insideFullscreenToggle = gInsideFullscreenToggle;
+ gInsideFullscreenToggle = true;
+ #endif
+
+ PluginsSetGraphicsDevice (s_Device, kGfxRendererD3D9, kGfxDeviceEventBeforeReset);
+
+ HRESULT hr = s_Device->Reset( &ppcopy );
+
+ #if WEBPLUG
+ gInsideFullscreenToggle = insideFullscreenToggle;
+ #endif
+
+ bool setToLost = false;
+ if( FAILED(hr) )
+ {
+ if( hr == D3DERR_DEVICELOST )
+ {
+ #if ENABLE_D3D_WINDOW_LOGGING
+ printf_console("set device to lost\n");
+ #endif
+ SetD3D9DeviceLost( true );
+ setToLost = true;
+ }
+ else
+ {
+ ErrorString( Format("D3D device reset failed [%s]", GetD3D9Error(hr)) );
+ return false;
+ }
+ }
+ else
+ {
+ PluginsSetGraphicsDevice (s_Device, kGfxRendererD3D9, kGfxDeviceEventAfterReset);
+
+ s_Device->GetRenderTarget (0, &s_BackBuffer.m_Surface);
+ s_BackBuffer.width = ppcopy.BackBufferWidth;
+ s_BackBuffer.height = ppcopy.BackBufferHeight;
+ // create depth stencil
+ D3D9DepthStencilTexture depthStencil = CreateDepthStencilTextureD3D9 (s_Device, ppcopy.BackBufferWidth, ppcopy.BackBufferHeight, ppcopy.AutoDepthStencilFormat, ppcopy.MultiSampleType, ppcopy.MultiSampleQuality, TRUE);
+ if (depthStencil.m_Surface)
+ {
+ s_DepthStencil.m_Surface = depthStencil.m_Surface;
+ s_DepthStencil.m_Texture = depthStencil.m_Texture;
+ s_DepthStencil.width = ppcopy.BackBufferWidth;
+ s_DepthStencil.height = ppcopy.BackBufferHeight;
+ s_DepthStencil.depthFormat = kDepthFormat16; //@TODO?
+ }
+
+ s_BackBuffer.backBuffer = true;
+ s_DepthStencil.backBuffer = true;
+
+ #if !UNITY_EDITOR
+ RenderSurfaceHandle bbHandle(&s_BackBuffer), dsHandle(&s_DepthStencil);
+ GetRealGfxDevice().SetRenderTargets(1, &bbHandle, dsHandle);
+ #endif
+ s_Device->SetRenderState (D3DRS_ZENABLE, TRUE);
+ }
+ s_CurrentlyWindowed = ppcopy.Windowed ? true : false;
+
+ return !setToLost;
+}
+
+void DestroyD3DDevice()
+{
+ // This can happen when quiting from screen selector - window is not set up yet
+ if( !s_Window || !s_Device )
+ return;
+
+ // cleanup
+ s_BackBuffer.Release();
+ s_DepthStencil.Release();
+ s_Device->Release();
+ s_Device = NULL;
+ s_Window = NULL;
+}
+
+IDirect3DDevice9* GetD3DDevice()
+{
+ AssertIf( !s_Device );
+ return s_Device;
+}
+
+IDirect3DDevice9* GetD3DDeviceNoAssert()
+{
+ return s_Device;
+}
+
+
+
+#if UNITY_EDITOR
+
+#include "PlatformDependent/Win/WinUtils.h"
+
+HWND s_HiddenWindowD3D = NULL;
+
+bool CreateHiddenWindowD3D()
+{
+ AssertIf( s_HiddenWindowD3D );
+
+ // Dummy master window is 64x64 in size. Seems that 32x32 is too small for Rage cards (produces internal driver errors in CreateDevice).
+ s_HiddenWindowD3D = CreateWindowW(
+ L"STATIC",
+ L"UnityHiddenWindow",
+ WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
+ 0, 0, 64, 64,
+ NULL, NULL,
+ winutils::GetInstanceHandle(), NULL );
+ if( !s_HiddenWindowD3D )
+ {
+ winutils::AddErrorMessage( "Failed to create hidden window: %s", WIN_LAST_ERROR_TEXT );
+ return false;
+ }
+
+ return true;
+}
+
+void DestroyHiddenWindowD3D()
+{
+ AssertIf( !s_HiddenWindowD3D );
+ DestroyWindow( s_HiddenWindowD3D );
+ s_HiddenWindowD3D = NULL;
+}
+
+#endif
diff --git a/Runtime/GfxDevice/d3d/D3D9Context.h b/Runtime/GfxDevice/d3d/D3D9Context.h
new file mode 100644
index 0000000..370a1c7
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/D3D9Context.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "D3D9Includes.h"
+#include "D3D9Enumeration.h"
+
+bool InitializeD3D(D3DDEVTYPE devtype);
+void CleanupD3D();
+bool InitializeOrResetD3DDevice(
+ class GfxDevice* device,
+ HWND window, int width, int height,
+ int refreshRate, bool fullscreen, int vBlankCount, int fsaa,
+ int& outBackbufferBPP, int& outFrontbufferBPP, int& outDepthBPP, int& outFSAA );
+void GetBackBuffersAfterDeviceReset();
+bool ResetD3DDevice();
+#if UNITY_EDITOR
+void EditorInitializeD3D(GfxDevice* device);
+#endif
+bool FullResetD3DDevice();
+bool HandleD3DDeviceLost();
+void DestroyD3DDevice();
+extern D3DDEVTYPE g_D3DDevType;
+extern DWORD g_D3DAdapter;
+extern bool g_D3DUsesMixedVP;
+extern bool g_D3DHasDepthStencil;
+extern D3DFORMAT g_D3DDepthStencilFormat;
+
+IDirect3DDevice9* GetD3DDevice();
+IDirect3DDevice9* GetD3DDeviceNoAssert();
+IDirect3D9* GetD3DObject();
+D3D9FormatCaps* GetD3DFormatCaps();
+D3DFORMAT GetD3DFormatForChecks();
+
+typedef int (WINAPI* D3DPERF_BeginEventFunc)(D3DCOLOR, LPCWSTR);
+typedef int (WINAPI* D3DPERF_EndEventFunc)();
+extern D3DPERF_BeginEventFunc g_D3D9BeginEventFunc;
+extern D3DPERF_EndEventFunc g_D3D9EndEventFunc;
+
+
+#if UNITY_EDITOR
+bool CreateHiddenWindowD3D();
+void DestroyHiddenWindowD3D();
+extern HWND s_HiddenWindowD3D;
+#endif
+
diff --git a/Runtime/GfxDevice/d3d/D3D9Enumeration.cpp b/Runtime/GfxDevice/d3d/D3D9Enumeration.cpp
new file mode 100644
index 0000000..b78433e
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/D3D9Enumeration.cpp
@@ -0,0 +1,344 @@
+#include "UnityPrefix.h"
+#include "D3D9Enumeration.h"
+#include "D3D9Utils.h"
+#include "Runtime/GfxDevice/VramLimits.h"
+
+// ---------------------------------------------------------------------------
+
+
+const int kMinDisplayWidth = 512;
+const int kMinDisplayHeight = 384;
+const int kMinColorBits = 4;
+const int kMinAlphaBits = 0;
+
+extern D3DDEVTYPE g_D3DDevType;
+extern DWORD g_D3DAdapter;
+
+// ---------------------------------------------------------------------------
+
+static int GetFormatColorBits( D3DFORMAT fmt ) {
+ switch( fmt ) {
+ case D3DFMT_A2B10G10R10:
+ case D3DFMT_A2R10G10B10: return 10;
+ case D3DFMT_R8G8B8:
+ case D3DFMT_A8R8G8B8:
+ case D3DFMT_X8R8G8B8: return 8;
+ case D3DFMT_R5G6B5:
+ case D3DFMT_X1R5G5B5:
+ case D3DFMT_A1R5G5B5: return 5;
+ case D3DFMT_A4R4G4B4:
+ case D3DFMT_X4R4G4B4: return 4;
+ case D3DFMT_R3G3B2:
+ case D3DFMT_A8R3G3B2: return 2;
+ default: return 0;
+ }
+}
+
+static int GetFormatAlphaBits( D3DFORMAT fmt ) {
+ switch( fmt ) {
+ case D3DFMT_R8G8B8:
+ case D3DFMT_X8R8G8B8:
+ case D3DFMT_R5G6B5:
+ case D3DFMT_X1R5G5B5:
+ case D3DFMT_R3G3B2:
+ case D3DFMT_X4R4G4B4: return 0;
+ case D3DFMT_A8R8G8B8:
+ case D3DFMT_A8R3G3B2: return 8;
+ case D3DFMT_A1R5G5B5: return 1;
+ case D3DFMT_A4R4G4B4: return 4;
+ case D3DFMT_A2B10G10R10:
+ case D3DFMT_A2R10G10B10: return 2;
+ default: return 0;
+ }
+}
+
+int GetFormatDepthBits( D3DFORMAT fmt ) {
+ switch( fmt ) {
+ case D3DFMT_D16: return 16;
+ case D3DFMT_D15S1: return 15;
+ case D3DFMT_D24X8:
+ case D3DFMT_D24S8:
+ case D3DFMT_D24X4S4: return 24;
+ case D3DFMT_D32: return 32;
+ default: return 0;
+ }
+}
+
+static D3DFORMAT ConvertToAlphaFormat( D3DFORMAT fmt )
+{
+ if( fmt == D3DFMT_X8R8G8B8 )
+ fmt = D3DFMT_A8R8G8B8;
+ else if( fmt == D3DFMT_X4R4G4B4 )
+ fmt = D3DFMT_A4R4G4B4;
+ else if( fmt == D3DFMT_X1R5G5B5 )
+ fmt = D3DFMT_A1R5G5B5;
+ return fmt;
+}
+
+// -----------------------------------------------------------------------------
+
+
+static UInt32 buildVertexProcessings( const D3DCAPS9& caps )
+{
+ UInt32 result = 0;
+
+ // TODO: check vertex shader version
+
+ DWORD devCaps = caps.DevCaps;
+ if( devCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) {
+ if( devCaps & D3DDEVCAPS_PUREDEVICE ) {
+ result |= (1<<kVPPureHardware);
+ }
+ result |= (1<<kVPHardware);
+ result |= (1<<kVPMixed);
+ }
+
+ result |= (1<<kVPSoftware);
+
+ return result;
+}
+
+
+static void buildDepthStencilFormats( IDirect3D9& d3d, D3DDeviceCombo& devCombo )
+{
+ const D3DFORMAT dsFormats[] = {
+ D3DFMT_D24S8, D3DFMT_D24X8, D3DFMT_D24X4S4, D3DFMT_D16, D3DFMT_D15S1, D3DFMT_D32,
+ };
+ const int dsFormatCount = sizeof(dsFormats) / sizeof(dsFormats[0]);
+
+ for( int idsf = 0; idsf < dsFormatCount; ++idsf ) {
+ D3DFORMAT format = dsFormats[idsf];
+ if( SUCCEEDED( d3d.CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, devCombo.adapterFormat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, format ) ) )
+ {
+ if( SUCCEEDED( d3d.CheckDepthStencilMatch( g_D3DAdapter, g_D3DDevType, devCombo.adapterFormat, devCombo.backBufferFormat, format ) ) )
+ {
+ devCombo.depthStencilFormats.push_back( format );
+ }
+ }
+ }
+}
+
+
+static void buildMultiSampleTypes( IDirect3D9& d3d, D3DDeviceCombo& devCombo )
+{
+ const size_t kMaxSamples = 16;
+ devCombo.multiSampleTypes.reserve( kMaxSamples );
+ devCombo.multiSampleTypes.push_back( D3DMULTISAMPLE_NONE );
+
+ for( int samples = 2; samples <= kMaxSamples; ++samples ) {
+ D3DMULTISAMPLE_TYPE msType = GetD3DMultiSampleType( samples );
+ DWORD msQuality;
+ if( SUCCEEDED( d3d.CheckDeviceMultiSampleType( g_D3DAdapter, g_D3DDevType, devCombo.backBufferFormat, devCombo.isWindowed, msType, NULL ) ) )
+ devCombo.multiSampleTypes.push_back( samples );
+ }
+}
+
+
+static void buildConflicts( IDirect3D9& d3d, D3DDeviceCombo& devCombo )
+{
+ for( size_t ids = 0; ids < devCombo.depthStencilFormats.size(); ++ids ) {
+ D3DFORMAT format = (D3DFORMAT)devCombo.depthStencilFormats[ids];
+ for( size_t ims = 0; ims < devCombo.multiSampleTypes.size(); ++ims ) {
+ D3DMULTISAMPLE_TYPE msType = (D3DMULTISAMPLE_TYPE)devCombo.multiSampleTypes[ims];
+ if( FAILED( d3d.CheckDeviceMultiSampleType(
+ g_D3DAdapter, g_D3DDevType,
+ format, devCombo.isWindowed, msType, NULL ) ) )
+ {
+ D3DDeviceCombo::MultiSampleConflict conflict;
+ conflict.format = format;
+ conflict.type = msType;
+ devCombo.conflicts.push_back( conflict );
+ }
+ }
+ }
+}
+
+
+static bool enumerateDeviceCombos( IDirect3D9& d3d, const D3DCAPS9& caps, const DwordVector& adapterFormats, D3DDeviceComboVector& outCombos )
+{
+ const D3DFORMAT bbufferFormats[] = {
+ D3DFMT_A8R8G8B8, D3DFMT_X8R8G8B8, D3DFMT_A2R10G10B10,
+ D3DFMT_R5G6B5, D3DFMT_A1R5G5B5, D3DFMT_X1R5G5B5
+ };
+ const int bbufferFormatCount = sizeof(bbufferFormats) / sizeof(bbufferFormats[0]);
+
+ bool isWindowedArray[] = { false, true };
+
+ // see which adapter formats are supported by this device
+ for( size_t iaf = 0; iaf < adapterFormats.size(); ++iaf )
+ {
+ D3DFORMAT format = (D3DFORMAT)adapterFormats[iaf];
+ for( int ibbf = 0; ibbf < bbufferFormatCount; ibbf++ )
+ {
+ D3DFORMAT bbufferFormat = bbufferFormats[ibbf];
+ if( GetFormatAlphaBits(bbufferFormat) < kMinAlphaBits )
+ continue;
+ for( int iiw = 0; iiw < 2; ++iiw ) {
+ bool isWindowed = isWindowedArray[iiw];
+ if( FAILED( d3d.CheckDeviceType( g_D3DAdapter, g_D3DDevType, format, bbufferFormat, isWindowed ) ) )
+ continue;
+
+ // Here, we have an adapter format / backbuffer format/ windowed
+ // combo that is supported by the system. We still need to find one or
+ // more suitable depth/stencil buffer format, multisample type,
+ // vertex processing type, and vsync.
+ D3DDeviceCombo devCombo;
+
+ devCombo.adapterFormat = format;
+ devCombo.backBufferFormat = bbufferFormat;
+ devCombo.isWindowed = isWindowed;
+ devCombo.presentationIntervals = caps.PresentationIntervals;
+
+ buildDepthStencilFormats( d3d, devCombo );
+ if( devCombo.depthStencilFormats.empty() )
+ continue;
+
+ buildMultiSampleTypes( d3d, devCombo );
+ if( devCombo.multiSampleTypes.empty() )
+ continue;
+
+ buildConflicts( d3d, devCombo );
+
+ outCombos.push_back( devCombo );
+ }
+ }
+ }
+
+ return !outCombos.empty();
+}
+
+
+bool D3D9FormatCaps::Enumerate( IDirect3D9& d3d )
+{
+ AssertIf( !m_Combos.empty() );
+ HRESULT hr;
+
+ const D3DFORMAT allowedFormats[] = {
+ D3DFMT_X8R8G8B8, D3DFMT_X1R5G5B5, D3DFMT_R5G6B5, D3DFMT_A2R10G10B10
+ };
+ const int allowedFormatCount = sizeof(allowedFormats) / sizeof(allowedFormats[0]);
+
+ m_AdapterFormatForChecks = D3DFMT_UNKNOWN;
+
+ // build a list of all display adapter formats
+ DwordVector adapterFormatList; // D3DFORMAT
+
+ for( size_t ifmt = 0; ifmt < allowedFormatCount; ++ifmt )
+ {
+ D3DFORMAT format = allowedFormats[ifmt];
+ int modeCount = d3d.GetAdapterModeCount( g_D3DAdapter, format );
+ for( int mode = 0; mode < modeCount; ++mode ) {
+ D3DDISPLAYMODE dm;
+ d3d.EnumAdapterModes( g_D3DAdapter, format, mode, &dm );
+ if( dm.Width < (UINT)kMinDisplayWidth || dm.Height < (UINT)kMinDisplayHeight || GetFormatColorBits(dm.Format) < kMinColorBits )
+ continue;
+ // adapterInfo->displayModes.push_back( dm );
+ if( std::find(adapterFormatList.begin(),adapterFormatList.end(),dm.Format) == adapterFormatList.end() ) {
+ adapterFormatList.push_back( dm.Format );
+ if( m_AdapterFormatForChecks == D3DFMT_UNKNOWN )
+ m_AdapterFormatForChecks = format;
+ }
+ }
+ }
+
+ if( m_AdapterFormatForChecks == D3DFMT_UNKNOWN ) // for some reason no format was selected for checks, use default
+ m_AdapterFormatForChecks = allowedFormats[0];
+
+ // get info for device on this adapter
+ D3DCAPS9 caps;
+ if( FAILED( d3d.GetDeviceCaps( g_D3DAdapter, g_D3DDevType, &caps ) ) )
+ return false;
+
+ // find suitable vertex processing modes (if any)
+ m_VertexProcessings = buildVertexProcessings( caps );
+ AssertIf( !m_VertexProcessings );
+
+ // get info for each device combo on this device
+ if( !enumerateDeviceCombos( d3d, caps, adapterFormatList, m_Combos ) )
+ return false;
+
+ return true;
+}
+
+
+void D3D9FormatCaps::FindBestPresentationParams( int width, int height, D3DFORMAT desktopMode, bool windowed, int vBlankCount, int multiSample, D3DPRESENT_PARAMETERS& outParams ) const
+{
+ const D3DDeviceCombo* bestCombo = NULL;
+ int bestScore = -1;
+
+ for( size_t idc = 0; idc < m_Combos.size(); ++idc )
+ {
+ const D3DDeviceCombo& devCombo = m_Combos[idc];
+ if( windowed && !devCombo.isWindowed )
+ continue;
+ if( !windowed && devCombo.isWindowed )
+ continue;
+ if( windowed )
+ {
+ if( devCombo.adapterFormat != desktopMode )
+ continue;
+ }
+
+ int score = 0;
+
+ bool matchesBB = (devCombo.backBufferFormat == ConvertToAlphaFormat(devCombo.adapterFormat));
+ bool matchesDesktop = (devCombo.adapterFormat == desktopMode);
+
+ if( matchesBB )
+ score += 1;
+ if( matchesDesktop )
+ score += 1;
+ if( GetFormatAlphaBits(devCombo.backBufferFormat) > 0 )
+ score += 1;
+
+ if( score > bestScore )
+ {
+ bestScore = score;
+ bestCombo = &devCombo;
+ }
+ }
+
+ if( !bestCombo )
+ {
+ // This can happen if we're debugging force-16BPP modes on a 32BPP desktop, and so on
+ outParams.BackBufferFormat = desktopMode;
+ outParams.AutoDepthStencilFormat = D3DFMT_D16;
+ outParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
+ outParams.MultiSampleType = D3DMULTISAMPLE_NONE;
+ outParams.MultiSampleQuality = 0;
+ return;
+ }
+
+ outParams.BackBufferFormat = bestCombo->backBufferFormat;
+ outParams.AutoDepthStencilFormat = (D3DFORMAT)bestCombo->depthStencilFormats[0];
+
+ // No support for intervals above 1 in windowed mode (case 497116)
+ if (windowed && vBlankCount > 1)
+ vBlankCount = 1;
+
+ // best possible vsync parameter (if device doesn't support 2 fall back to 1)
+ DWORD intervals = bestCombo->presentationIntervals;
+ outParams.PresentationInterval = ( vBlankCount >= 2 ) && ( intervals & D3DPRESENT_INTERVAL_TWO ) ? D3DPRESENT_INTERVAL_TWO :
+ ( vBlankCount >= 1 ) && ( intervals & D3DPRESENT_INTERVAL_ONE ) ? D3DPRESENT_INTERVAL_ONE :
+ ( vBlankCount == 0 ) && ( intervals & D3DPRESENT_INTERVAL_IMMEDIATE ) ? D3DPRESENT_INTERVAL_IMMEDIATE :
+ D3DPRESENT_INTERVAL_DEFAULT;
+
+ // Here we already know backbuffer, depth buffer formats and so on, so we can also clamp used FSAA to sane VRAM limits.
+ int backbufferBPP = GetBPPFromD3DFormat(outParams.BackBufferFormat)/8;
+ int frontbufferBPP = GetBPPFromD3DFormat(desktopMode)/8;
+ int depthBPP = GetBPPFromD3DFormat(outParams.AutoDepthStencilFormat)/8;
+ multiSample = ChooseSuitableFSAALevel( width, height, backbufferBPP, frontbufferBPP, depthBPP, multiSample );
+
+ // Find out best matched multi sample type.
+ int msIdx = 0;
+ if( multiSample > 1 )
+ {
+ while( msIdx < bestCombo->multiSampleTypes.size() && bestCombo->multiSampleTypes[msIdx] <= multiSample )
+ ++msIdx;
+ --msIdx;
+ AssertIf( msIdx < 0 );
+ }
+ outParams.MultiSampleType = GetD3DMultiSampleType(bestCombo->multiSampleTypes[msIdx]);
+ outParams.MultiSampleQuality = 0;
+}
+
diff --git a/Runtime/GfxDevice/d3d/D3D9Enumeration.h b/Runtime/GfxDevice/d3d/D3D9Enumeration.h
new file mode 100644
index 0000000..240bb89
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/D3D9Enumeration.h
@@ -0,0 +1,64 @@
+#pragma once
+
+#include "D3D9Includes.h"
+
+
+struct D3DDeviceCombo;
+
+typedef std::vector<DWORD> DwordVector;
+typedef std::vector<D3DDeviceCombo> D3DDeviceComboVector;
+
+
+enum D3DVertexProcessing {
+ kVPPureHardware,
+ kVPHardware,
+ kVPMixed,
+ kVPSoftware,
+};
+
+
+//---------------------------------------------------------------------------
+
+// A combo of adapter format, back buffer format, and windowed/fulscreen that
+// is compatible with a D3D device.
+struct D3DDeviceCombo {
+public:
+ // A depth/stencil buffer format that is incompatible with a multisample type.
+ struct MultiSampleConflict {
+ D3DFORMAT format;
+ D3DMULTISAMPLE_TYPE type;
+ };
+ typedef std::vector<MultiSampleConflict> MultiSampleConflictVector;
+public:
+ D3DFORMAT adapterFormat;
+ D3DFORMAT backBufferFormat;
+ bool isWindowed;
+ DWORD presentationIntervals;
+
+ DwordVector depthStencilFormats;
+ DwordVector multiSampleTypes;
+ MultiSampleConflictVector conflicts;
+};
+
+
+//---------------------------------------------------------------------------
+
+class D3D9FormatCaps {
+public:
+ D3D9FormatCaps() : m_VertexProcessings(0) { }
+
+ bool Enumerate( IDirect3D9& d3d );
+
+ // Fills in BackBufferFormat, AutoDepthStencilFormat, PresentationInterval,
+ // MultiSampleType, MultiSampleQuality.
+ void FindBestPresentationParams( int width, int height, D3DFORMAT desktopMode, bool windowed, int vBlankCount, int multiSample, D3DPRESENT_PARAMETERS& outParams ) const;
+
+ // Gets adapter format for doing CheckDeviceFormat checks.
+ // Usually D3DFMT_X8R8G8B8, except for really old cards that can't do 32 bpp.
+ D3DFORMAT GetAdapterFormatForChecks() const { return m_AdapterFormatForChecks; }
+
+public:
+ D3DDeviceComboVector m_Combos;
+ UInt32 m_VertexProcessings; // bitmask
+ D3DFORMAT m_AdapterFormatForChecks;
+};
diff --git a/Runtime/GfxDevice/d3d/D3D9Includes.h b/Runtime/GfxDevice/d3d/D3D9Includes.h
new file mode 100644
index 0000000..84596ff
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/D3D9Includes.h
@@ -0,0 +1,7 @@
+#ifndef D3DINCLUDES_H
+#define D3DINCLUDES_H
+
+//#define D3D_DEBUG_INFO
+#include "External/DirectX/builds/dx9include/d3d9.h"
+
+#endif
diff --git a/Runtime/GfxDevice/d3d/D3D9Utils.cpp b/Runtime/GfxDevice/d3d/D3D9Utils.cpp
new file mode 100644
index 0000000..3e25633
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/D3D9Utils.cpp
@@ -0,0 +1,169 @@
+#include "UnityPrefix.h"
+#include "D3D9Utils.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+
+
+#ifdef DUMMY_D3D9_CALLS
+HRESULT CallDummyD3D9Function()
+{
+ return S_OK;
+}
+#endif
+
+struct D3D9Error {
+ HRESULT hr;
+ const char* message;
+};
+
+static D3D9Error s_D3DErrors[] = {
+ { D3DOK_NOAUTOGEN, "no mipmap autogen" },
+ { D3DERR_WRONGTEXTUREFORMAT, "wrong texture format" },
+ { D3DERR_UNSUPPORTEDCOLOROPERATION, "unsupported color op" },
+ { D3DERR_UNSUPPORTEDCOLORARG, "unsupported color arg" },
+ { D3DERR_UNSUPPORTEDALPHAOPERATION, "unsupported alpha op" },
+ { D3DERR_UNSUPPORTEDALPHAARG, "unsupported alpha arg" },
+ { D3DERR_TOOMANYOPERATIONS, "too many texture operations" },
+ { D3DERR_CONFLICTINGTEXTUREFILTER, "conflicting texture filters" },
+ { D3DERR_UNSUPPORTEDFACTORVALUE, "unsupported factor value" },
+ { D3DERR_CONFLICTINGRENDERSTATE, "conflicting render states" },
+ { D3DERR_UNSUPPORTEDTEXTUREFILTER, "unsupported texture filter" },
+ { D3DERR_CONFLICTINGTEXTUREPALETTE, "conflicting texture palettes" },
+ { D3DERR_DRIVERINTERNALERROR, "internal driver error" },
+ { D3DERR_NOTFOUND, "requested item not found" },
+ { D3DERR_MOREDATA, "more data than fits into buffer" },
+ { D3DERR_DEVICELOST, "device lost" },
+ { D3DERR_DEVICENOTRESET, "device not reset" },
+ { D3DERR_NOTAVAILABLE, "queried technique not available" },
+ { D3DERR_OUTOFVIDEOMEMORY, "out of VRAM" },
+ { D3DERR_INVALIDDEVICE, "invalid device" },
+ { D3DERR_INVALIDCALL, "invalid call" },
+ { D3DERR_DRIVERINVALIDCALL, "driver invalid call" },
+ { D3DERR_WASSTILLDRAWING, "was still drawing" },
+ { S_OK, "S_OK" },
+ { E_FAIL, "E_FAIL" },
+ { E_INVALIDARG, "E_INVALIDARG" },
+ { E_OUTOFMEMORY, "out of memory" },
+};
+
+const char* GetD3D9Error( HRESULT hr )
+{
+ for( int i = 0; i < ARRAY_SIZE(s_D3DErrors); ++i )
+ {
+ if( hr == s_D3DErrors[i].hr )
+ return s_D3DErrors[i].message;
+ }
+
+ static char buffer[1000];
+ sprintf( buffer, "unknown error, code 0x%X", hr );
+ return buffer;
+}
+
+int GetBPPFromD3DFormat( D3DFORMAT format )
+{
+ switch( format ) {
+ case D3DFMT_UNKNOWN:
+ case kD3D9FormatNULL:
+ return 0;
+ case D3DFMT_X8R8G8B8:
+ case D3DFMT_A8R8G8B8:
+ case D3DFMT_A2R10G10B10:
+ case D3DFMT_A2B10G10R10:
+ case D3DFMT_R8G8B8:
+ case D3DFMT_A8B8G8R8:
+ case D3DFMT_R32F:
+ case D3DFMT_D24X8:
+ case D3DFMT_D24S8:
+ case D3DFMT_D24X4S4:
+ case kD3D9FormatINTZ:
+ case kD3D9FormatRAWZ:
+ return 32;
+ case D3DFMT_X1R5G5B5:
+ case D3DFMT_A1R5G5B5:
+ case D3DFMT_A4R4G4B4:
+ case D3DFMT_X4R4G4B4:
+ case D3DFMT_R5G6B5:
+ case D3DFMT_R16F:
+ case D3DFMT_D16:
+ case D3DFMT_D15S1:
+ case D3DFMT_D16_LOCKABLE:
+ case D3DFMT_L16:
+ case D3DFMT_A8L8:
+ case kD3D9FormatDF16:
+ return 16;
+ case D3DFMT_A16B16G16R16F:
+ return 64;
+ case D3DFMT_A32B32G32R32F:
+ return 128;
+ case D3DFMT_DXT1:
+ return 4;
+ case D3DFMT_A8:
+ case D3DFMT_L8:
+ case D3DFMT_DXT3:
+ case D3DFMT_DXT5:
+ return 8;
+ default:
+ ErrorString( Format("Unknown D3D format %x", format) );
+ return 32;
+ }
+}
+
+int GetStencilBitsFromD3DFormat (D3DFORMAT fmt)
+{
+ switch( fmt ) {
+ case D3DFMT_D15S1: return 1;
+ case D3DFMT_D24S8: return 8;
+ case D3DFMT_D24X4S4: return 4;
+ default: return 0;
+ }
+}
+
+D3DMULTISAMPLE_TYPE GetD3DMultiSampleType (int samples)
+{
+ // Optimizer should take care of this, since value of D3DMULTISAMPLE_N_SAMPLES is N
+ switch( samples ) {
+ case 0:
+ case 1: return D3DMULTISAMPLE_NONE;
+ case 2: return D3DMULTISAMPLE_2_SAMPLES;
+ case 3: return D3DMULTISAMPLE_3_SAMPLES;
+ case 4: return D3DMULTISAMPLE_4_SAMPLES;
+ case 5: return D3DMULTISAMPLE_5_SAMPLES;
+ case 6: return D3DMULTISAMPLE_6_SAMPLES;
+ case 7: return D3DMULTISAMPLE_7_SAMPLES;
+ case 8: return D3DMULTISAMPLE_8_SAMPLES;
+ case 9: return D3DMULTISAMPLE_9_SAMPLES;
+ case 10: return D3DMULTISAMPLE_10_SAMPLES;
+ case 11: return D3DMULTISAMPLE_11_SAMPLES;
+ case 12: return D3DMULTISAMPLE_12_SAMPLES;
+ case 13: return D3DMULTISAMPLE_13_SAMPLES;
+ case 14: return D3DMULTISAMPLE_14_SAMPLES;
+ case 15: return D3DMULTISAMPLE_15_SAMPLES;
+ case 16: return D3DMULTISAMPLE_16_SAMPLES;
+ default:
+ ErrorString("Unknown sample count");
+ return D3DMULTISAMPLE_NONE;
+ }
+}
+
+bool CheckD3D9DebugRuntime (IDirect3DDevice9* dev)
+{
+ IDirect3DQuery9* query = NULL;
+ HRESULT hr = dev->CreateQuery (D3DQUERYTYPE_VERTEXSTATS, &query);
+ if( SUCCEEDED(hr) )
+ {
+ query->Release ();
+ return true;
+ }
+ return false;
+}
+
+
+D3D9DepthStencilTexture CreateDepthStencilTextureD3D9 (IDirect3DDevice9* dev, int width, int height, D3DFORMAT format, D3DMULTISAMPLE_TYPE msType, DWORD msQuality, BOOL discardable)
+{
+ D3D9DepthStencilTexture tex;
+
+ HRESULT hr = dev->CreateDepthStencilSurface (width, height, format, msType, msQuality, discardable, &tex.m_Surface, NULL);
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(tex.m_Surface, width * height * GetBPPFromD3DFormat(format), NULL);
+
+ return tex;
+}
diff --git a/Runtime/GfxDevice/d3d/D3D9Utils.h b/Runtime/GfxDevice/d3d/D3D9Utils.h
new file mode 100644
index 0000000..529e58b
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/D3D9Utils.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include "D3D9Includes.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+//#define DUMMY_D3D9_CALLS
+
+#ifndef DUMMY_D3D9_CALLS
+#define D3D9_CALL(x) x
+#define D3D9_CALL_HR(x) x
+#else
+HRESULT CallDummyD3D9Function();
+#define D3D9_CALL(x) CallDummyD3D9Function()
+#define D3D9_CALL_HR(x) CallDummyD3D9Function()
+#endif
+
+
+const char* GetD3D9Error( HRESULT hr );
+int GetBPPFromD3DFormat( D3DFORMAT format );
+int GetStencilBitsFromD3DFormat (D3DFORMAT fmt);
+D3DMULTISAMPLE_TYPE GetD3DMultiSampleType (int samples);
+
+bool CheckD3D9DebugRuntime (IDirect3DDevice9* dev);
+
+struct D3D9DepthStencilTexture {
+ D3D9DepthStencilTexture() : m_Texture(NULL), m_Surface(NULL) {}
+
+ IDirect3DTexture9* m_Texture;
+ IDirect3DSurface9* m_Surface;
+
+ void Release() {
+ if (m_Texture) {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_Texture);
+ m_Texture->Release();
+ m_Texture = NULL;
+ }
+ if (m_Surface) {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_Surface);
+ m_Surface->Release();
+ m_Surface = NULL;
+ }
+ }
+};
+
+const D3DFORMAT kD3D9FormatDF16 = (D3DFORMAT)MAKEFOURCC('D','F','1','6');
+const D3DFORMAT kD3D9FormatINTZ = (D3DFORMAT)MAKEFOURCC('I','N','T','Z');
+const D3DFORMAT kD3D9FormatRAWZ = (D3DFORMAT)MAKEFOURCC('R','A','W','Z');
+const D3DFORMAT kD3D9FormatNULL = (D3DFORMAT)MAKEFOURCC('N','U','L','L');
+const D3DFORMAT kD3D9FormatRESZ = (D3DFORMAT)MAKEFOURCC('R','E','S','Z');
+
+
+D3D9DepthStencilTexture CreateDepthStencilTextureD3D9 (
+ IDirect3DDevice9* dev, int width, int height, D3DFORMAT format,
+ D3DMULTISAMPLE_TYPE msType, DWORD msQuality, BOOL discardable );
+
+static inline DWORD GetD3D9SamplerIndex (ShaderType type, int unit)
+{
+ switch (type) {
+ case kShaderVertex:
+ DebugAssert (unit >= 0 && unit < 4); // DX9 has limit of 4 vertex samplers
+ return unit + D3DVERTEXTEXTURESAMPLER0;
+ case kShaderFragment:
+ DebugAssert (unit >= 0 && unit < kMaxSupportedTextureUnits);
+ return unit;
+ default:
+ Assert ("Unsupported shader type for sampler");
+ return 0;
+ }
+}
diff --git a/Runtime/GfxDevice/d3d/D3D9VBO.cpp b/Runtime/GfxDevice/d3d/D3D9VBO.cpp
new file mode 100644
index 0000000..19cc409
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/D3D9VBO.cpp
@@ -0,0 +1,815 @@
+#include "UnityPrefix.h"
+#include "D3D9VBO.h"
+#include "D3D9Context.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "D3D9Utils.h"
+#include "GfxDeviceD3D9.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+
+
+// defined in GfxDeviceD3D9.cpp
+IDirect3DVertexDeclaration9* GetD3DVertexDeclaration( UInt32 shaderChannelsMap );
+void UpdateChannelBindingsD3D( const ChannelAssigns& channels );
+
+
+// Define this to 1 to make VBO operations randomly fail.
+// Use this to test error checking code.
+#define DEBUG_RANDOMLY_FAIL_D3D_VBO 0
+
+
+#if !DEBUGMODE && DEBUG_RANDOMLY_FAIL_D3D_VBO
+#error Never enable random VBO failures on release code!
+#endif
+
+#if DEBUG_RANDOMLY_FAIL_D3D_VBO
+#define RANDOM_FAIL_FOR_DEBUG - ((rand()%8==0) ? 100000000 : 0)
+#else
+#define RANDOM_FAIL_FOR_DEBUG
+#endif
+
+
+static const D3DPRIMITIVETYPE kTopologyD3D9[kPrimitiveTypeCount] =
+{
+ D3DPT_TRIANGLELIST,
+ D3DPT_TRIANGLESTRIP,
+ D3DPT_TRIANGLELIST, //@TODO: make work
+ D3DPT_LINELIST,
+ D3DPT_LINESTRIP,
+ D3DPT_POINTLIST,
+};
+
+
+// -----------------------------------------------------------------------------
+
+IDirect3DIndexBuffer9* D3D9VBO::ms_CustomIB = NULL;
+int D3D9VBO::ms_CustomIBSize = 0;
+UInt32 D3D9VBO::ms_CustomIBUsedBytes = 0;
+
+D3D9VBO::D3D9VBO()
+: m_IB(NULL)
+, m_IBSize(0)
+{
+ memset(m_VertexDecls, 0, sizeof(m_VertexDecls));
+ memset(m_VBStreams, 0, sizeof(m_VBStreams));
+}
+
+D3D9VBO::~D3D9VBO ()
+{
+ for( int s = 0; s < kMaxVertexStreams; s++ )
+ {
+ if( m_VBStreams[s] ) {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_VBStreams[s]);
+ ULONG refCount = m_VBStreams[s]->Release();
+ AssertIf( refCount != 0 );
+ m_VBStreams[s] = NULL;
+ }
+ }
+ if( m_IB ) {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_IB);
+ ULONG refCount = m_IB->Release();
+ AssertIf( refCount != 0 );
+ m_IB = NULL;
+ }
+
+}
+
+
+void D3D9VBO::ResetDynamicVB()
+{
+ // Gets called on all VBs and ignores non-dynamic ones
+ for( int s = 0; s < kMaxVertexStreams; s++ )
+ {
+ if( m_StreamModes[s] == kStreamModeDynamic )
+ {
+ // Vertex buffer can be null when switching fullscreen in web player.
+ // There we lose device a couple of times, and ResetDynamicVB is called several
+ // times in succession.
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_VBStreams[s]);
+ SAFE_RELEASE( m_VBStreams[s] );
+ }
+ }
+}
+
+void D3D9VBO::CleanupSharedIndexBuffer()
+{
+ if( ms_CustomIB )
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(ms_CustomIB);
+ ULONG refCount = ms_CustomIB->Release();
+ AssertIf( refCount != 0 );
+ ms_CustomIBSize = 0;
+ ms_CustomIBUsedBytes = 0;
+ ms_CustomIB = NULL;
+ }
+}
+
+void D3D9VBO::BindVertexStreams( IDirect3DDevice9* dev, const ChannelAssigns& channels )
+{
+ int freeStream = -1;
+ for( int s = 0; s < kMaxVertexStreams; s++ )
+ {
+ if( m_VBStreams[s] )
+ D3D9_CALL( dev->SetStreamSource( s, m_VBStreams[s], 0, m_Streams[s].stride ) );
+ else
+ freeStream = s;
+ }
+ int declIndex = kVertexDeclDefault;
+ if ((channels.GetSourceMap() & VERTEX_FORMAT1(Color)) && !m_ChannelInfo[kShaderChannelColor].IsValid())
+ {
+ if (freeStream != -1)
+ {
+ declIndex = kVertexDeclAllWhiteStream;
+ if (!m_VertexDecls[declIndex])
+ {
+ ChannelInfoArray channelInfo;
+ memcpy(&channelInfo, m_ChannelInfo, sizeof(channelInfo));
+ ChannelInfo& colorInfo = channelInfo[kShaderChannelColor];
+ colorInfo.stream = freeStream;
+ colorInfo.offset = 0;
+ colorInfo.format = kChannelFormatColor;
+ colorInfo.dimension = 1;
+ m_VertexDecls[declIndex] = GetD3D9GfxDevice().GetVertexDecls().GetVertexDecl( channelInfo );
+ }
+ IDirect3DVertexBuffer9* whiteVB = GetD3D9GfxDevice().GetAllWhiteVertexStream();
+ D3D9_CALL( dev->SetStreamSource( freeStream, whiteVB, 0, sizeof(D3DCOLOR) ) );
+ }
+ else
+ ErrorString("Need a free stream to add default vertex colors!");
+ }
+ D3D9_CALL( dev->SetVertexDeclaration( m_VertexDecls[declIndex] ) );
+ UpdateChannelBindingsD3D( channels );
+}
+
+void D3D9VBO::UpdateVertexStream( const VertexBufferData& sourceData, unsigned stream )
+{
+ DebugAssert( !m_IsStreamMapped[stream] );
+ const StreamInfo& srcStream = sourceData.streams[stream];
+ int oldSize = CalculateVertexStreamSize(m_Streams[stream], m_VertexCount);
+ int newSize = CalculateVertexStreamSize(srcStream, sourceData.vertexCount);
+ m_Streams[stream] = srcStream;
+ if (newSize == 0)
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_VBStreams[stream]);
+ SAFE_RELEASE( m_VBStreams[stream] );
+ return;
+ }
+
+ const bool isDynamic = (m_StreamModes[stream] == kStreamModeDynamic);
+ DWORD usage = isDynamic ? (D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY) : (D3DUSAGE_WRITEONLY);
+ D3DPOOL pool = isDynamic ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
+
+ if( m_VBStreams[stream] == NULL || newSize != oldSize )
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_VBStreams[stream]);
+ SAFE_RELEASE( m_VBStreams[stream] );
+ IDirect3DDevice9* dev = GetD3DDevice();
+ HRESULT hr = dev->CreateVertexBuffer( newSize RANDOM_FAIL_FOR_DEBUG, usage, 0, pool, &m_VBStreams[stream], NULL );
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(m_VBStreams[stream],newSize,this);
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d: failed to create vertex buffer of size %d [%s]\n", newSize, GetD3D9Error(hr) );
+ return;
+ }
+ }
+
+ // Don't update contents if there is no source data.
+ // This is used to update the vertex declaration only, leaving buffer intact.
+ // Also to create an empty buffer that is written to later.
+ if (!sourceData.buffer)
+ return;
+
+ UInt8* buffer;
+ HRESULT hr = m_VBStreams[stream]->Lock( 0 RANDOM_FAIL_FOR_DEBUG, 0, (void**)&buffer, isDynamic ? D3DLOCK_DISCARD : 0 );
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d: failed to lock vertex buffer %p [%s]\n", m_VBStreams[stream], GetD3D9Error(hr) );
+ return;
+ }
+ CopyVertexStream( sourceData, buffer, stream );
+
+ m_VBStreams[stream]->Unlock();
+}
+
+
+void D3D9VBO::UpdateIndexBufferData (const IndexBufferData& sourceData)
+{
+ if( !sourceData.indices )
+ {
+ m_IBSize = 0;
+ return;
+ }
+
+ AssertIf( !m_IB );
+ UInt8* buffer;
+ HRESULT hr = m_IB->Lock( 0 RANDOM_FAIL_FOR_DEBUG, 0, (void**)&buffer, 0 );
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d: failed to lock index buffer %p [%s]\n", m_IB, GetD3D9Error(hr) );
+ return;
+ }
+
+ memcpy (buffer, sourceData.indices, sourceData.count * kVBOIndexSize);
+
+ m_IB->Unlock();
+}
+
+bool D3D9VBO::MapVertexStream( VertexStreamData& outData, unsigned stream )
+{
+ if( m_VBStreams[stream] == NULL )
+ {
+ printf_console( "d3d: attempt to map null vertex buffer\n" );
+ return false;
+ }
+ DebugAssertIf( IsVertexBufferLost() );
+ AssertIf( m_IsStreamMapped[stream] );
+
+ const bool isDynamic = (m_StreamModes[stream] == kStreamModeDynamic);
+
+ UInt8* buffer;
+ int vbSize = CalculateVertexStreamSize(m_Streams[stream], m_VertexCount);
+ HRESULT hr = m_VBStreams[stream]->Lock( 0 RANDOM_FAIL_FOR_DEBUG, 0, (void**)&buffer, isDynamic ? D3DLOCK_DISCARD : 0 );
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d: failed to map vertex buffer %p of size %i [%s]\n", m_VBStreams[stream], vbSize, GetD3D9Error(hr) );
+ return false;
+ }
+ m_IsStreamMapped[stream] = true;
+
+ outData.buffer = buffer;
+ outData.channelMask = m_Streams[stream].channelMask;
+ outData.stride = m_Streams[stream].stride;
+ outData.vertexCount = m_VertexCount;
+
+ GetRealGfxDevice().GetFrameStats().AddUploadVBO( vbSize );
+
+ return true;
+}
+
+void D3D9VBO::UnmapVertexStream( unsigned stream )
+{
+ DebugAssert( m_VBStreams[stream] );
+ AssertIf( !m_IsStreamMapped[stream] );
+ m_IsStreamMapped[stream] = false;
+ m_VBStreams[stream]->Unlock();
+}
+
+bool D3D9VBO::IsVertexBufferLost() const
+{
+ for( int s = 0; s < kMaxVertexStreams; s++ )
+ if( m_Streams[s].channelMask && !m_VBStreams[s] )
+ return true;
+
+ return false;
+}
+
+int D3D9VBO::GetRuntimeMemorySize() const
+{
+#if ENABLE_MEM_PROFILER
+ return GetMemoryProfiler()->GetRelatedMemorySize(this)
+ + GetMemoryProfiler()->GetRelatedIDMemorySize((UInt32)this);
+#else
+ return 0;
+#endif
+/* int vertexSize = 0;
+ for( int s = 0; s < kMaxVertexStreams; s++ )
+ vertexSize += m_Streams[s].stride;
+
+ return vertexSize * m_VertexCount + m_IBSize;*/
+}
+
+
+void D3D9VBO::DrawVBO (const ChannelAssigns& channels, UInt32 firstIndexByte, UInt32 indexCount, GfxPrimitiveType topology, UInt32 firstVertex, UInt32 vertexCount)
+{
+ // just return if no indices
+ if( m_IBSize == 0 )
+ return;
+
+ HRESULT hr;
+
+ if( m_VBStreams[0] == NULL || m_IB == NULL )
+ {
+ printf_console( "d3d: VB or IB is null\n" );
+ return;
+ }
+
+ GfxDevice& device = GetRealGfxDevice();
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ BindVertexStreams( dev, channels );
+ device.BeforeDrawCall( false );
+
+ if (topology == kPrimitiveQuads)
+ {
+ UInt32 ibBytesLocked;
+ UInt16* ibPtr = MapDynamicIndexBuffer (indexCount/4*6, ibBytesLocked);
+ if (!ibPtr)
+ return;
+ const UInt16* ibSrc = NULL;
+ hr = m_IB->Lock (firstIndexByte, indexCount*kVBOIndexSize, (void**)&ibSrc, D3DLOCK_READONLY);
+ if (FAILED(hr))
+ {
+ UnmapDynamicIndexBuffer();
+ return;
+ }
+ FillIndexBufferForQuads (ibPtr, ibBytesLocked, ibSrc, indexCount/4);
+ m_IB->Unlock ();
+ UnmapDynamicIndexBuffer ();
+ firstIndexByte = ms_CustomIBUsedBytes;
+ ms_CustomIBUsedBytes += ibBytesLocked;
+ D3D9_CALL(dev->SetIndices(ms_CustomIB));
+ }
+ else
+ {
+ D3D9_CALL(dev->SetIndices( m_IB ));
+ }
+
+ // draw
+ D3DPRIMITIVETYPE primType = kTopologyD3D9[topology];
+ int primCount = GetPrimitiveCount (indexCount, topology, false);
+ hr = D3D9_CALL_HR(dev->DrawIndexedPrimitive (primType, 0, firstVertex, vertexCount, firstIndexByte/2, primCount));
+ Assert(SUCCEEDED(hr));
+
+ device.GetFrameStats().AddDrawCall (primCount, vertexCount);
+}
+
+UInt16* D3D9VBO::MapDynamicIndexBuffer (int indexCount, UInt32& outBytesUsed)
+{
+ HRESULT hr;
+ const UInt32 kMaxIndices = 64000; // Smaller threshold than absolutely necessary
+ Assert (indexCount <= kMaxIndices);
+ indexCount = std::min<UInt32>(indexCount, kMaxIndices);
+ int ibCapacity = indexCount * kVBOIndexSize;
+ int newIBSize = std::max (ibCapacity, 32*1024); // 32k IB at least
+
+ if (newIBSize > ms_CustomIBSize)
+ {
+ if (ms_CustomIB)
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(ms_CustomIB);
+ ms_CustomIB->Release();
+ }
+ ms_CustomIBSize = newIBSize;
+ ms_CustomIBUsedBytes = 0;
+
+ IDirect3DDevice9* dev = GetD3DDevice();
+ HRESULT hr = dev->CreateIndexBuffer (ms_CustomIBSize RANDOM_FAIL_FOR_DEBUG, D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, D3DFMT_INDEX16, D3DPOOL_DEFAULT , &ms_CustomIB, NULL);
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(ms_CustomIB,ms_CustomIBSize,0);
+
+ if( FAILED(hr) )
+ {
+ printf_console ("d3d: failed to create custom index buffer of size %d [%s]\n", newIBSize, GetD3D9Error(hr));
+ return NULL;
+ }
+ }
+
+ UInt16* buffer;
+ if (ms_CustomIBUsedBytes + ibCapacity > ms_CustomIBSize)
+ {
+ hr = ms_CustomIB->Lock (0 RANDOM_FAIL_FOR_DEBUG, ibCapacity, (void**)&buffer, D3DLOCK_DISCARD);
+ if (FAILED(hr))
+ {
+ printf_console ("d3d: failed to lock shared index buffer with discard [%s]\n", GetD3D9Error(hr));
+ return NULL;
+ }
+ ms_CustomIBUsedBytes = 0;
+ }
+ else
+ {
+ hr = ms_CustomIB->Lock (ms_CustomIBUsedBytes RANDOM_FAIL_FOR_DEBUG, ibCapacity, (void**)&buffer, D3DLOCK_NOOVERWRITE);
+ if (FAILED(hr))
+ {
+ printf_console ("d3d: failed to lock shared index buffer, offset %i size %i [%s]\n", ms_CustomIBUsedBytes, ibCapacity, GetD3D9Error(hr));
+ return NULL;
+ }
+ }
+ outBytesUsed = ibCapacity;
+
+ return buffer;
+}
+
+void D3D9VBO::UnmapDynamicIndexBuffer ()
+{
+ ms_CustomIB->Unlock();
+}
+
+
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+ void D3D9VBO::DrawCustomIndexed( const ChannelAssigns& channels, void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount )
+ {
+ Assert(!m_IsStreamMapped[0]);
+
+ if (m_VBStreams[0] == NULL)
+ {
+ printf_console( "d3d: VB is null\n" );
+ return;
+ }
+ UInt32 ibBytesUsed;
+ UInt16* ibPtr = MapDynamicIndexBuffer (indexCount, ibBytesUsed);
+ if (!ibPtr)
+ return;
+ memcpy (ibPtr, indices, ibBytesUsed);
+ UnmapDynamicIndexBuffer ();
+
+ GfxDevice& device = GetRealGfxDevice();
+ IDirect3DDevice9* dev = GetD3DDevice();
+ HRESULT hr;
+
+ BindVertexStreams( dev, channels );
+ device.BeforeDrawCall( false );
+
+ D3D9_CALL(dev->SetIndices( ms_CustomIB ));
+
+ D3DPRIMITIVETYPE primType = kTopologyD3D9[topology];
+ int primCount = GetPrimitiveCount (indexCount, topology, false);
+ hr = D3D9_CALL_HR(dev->DrawIndexedPrimitive(primType, 0, vertexRangeBegin, vertexRangeEnd-vertexRangeBegin, ms_CustomIBUsedBytes / kVBOIndexSize, primCount));
+ Assert(SUCCEEDED(hr));
+ ms_CustomIBUsedBytes += ibBytesUsed;
+
+ device.GetFrameStats().AddDrawCall (primCount, drawVertexCount);
+ }
+#endif
+
+
+void D3D9VBO::UpdateVertexData( const VertexBufferData& buffer )
+{
+ // Old vertex count and streams are still used here
+ for (unsigned stream = 0; stream < kMaxVertexStreams; stream++)
+ UpdateVertexStream( buffer, stream );
+
+ memcpy( m_ChannelInfo, buffer.channels, sizeof(m_ChannelInfo) );
+ memset( m_VertexDecls, 0, sizeof(m_VertexDecls) );
+ m_VertexDecls[kVertexDeclDefault] = GetD3D9GfxDevice().GetVertexDecls().GetVertexDecl( m_ChannelInfo );
+ m_VertexCount = buffer.vertexCount;
+}
+
+void D3D9VBO::UpdateIndexData (const IndexBufferData& buffer)
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ int newSize = CalculateIndexBufferSize(buffer);
+
+ if( !m_IB )
+ {
+ // initially, create a static buffer
+ HRESULT hr = dev->CreateIndexBuffer( newSize RANDOM_FAIL_FOR_DEBUG, (buffer.hasTopologies & (1<<kPrimitiveQuads)) ? 0 : D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &m_IB, NULL );
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(m_IB,newSize,this);
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d: failed to create index buffer of size %d [%s]\n", newSize, GetD3D9Error(hr) );
+ return;
+ }
+ }
+ else
+ {
+ if( newSize != m_IBSize )
+ {
+ IDirect3DIndexBuffer9* oldIB = m_IB;
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_IB);
+ m_IB->Release();
+ HRESULT hr = dev->CreateIndexBuffer( newSize RANDOM_FAIL_FOR_DEBUG, (buffer.hasTopologies & (1<<kPrimitiveQuads)) ? 0 : D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &m_IB, NULL );
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(m_IB,newSize,this);
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d: failed to resize index buffer %p to size %d [%s]\n", oldIB, newSize, GetD3D9Error(hr) );
+ return;
+ }
+ }
+ }
+ m_IBSize = newSize;
+ UpdateIndexBufferData(buffer);
+}
+
+// -----------------------------------------------------------------------------
+
+
+DynamicD3D9VBO::DynamicD3D9VBO( UInt32 vbSize, UInt32 ibSize )
+: DynamicVBO()
+, m_VBSize(vbSize)
+, m_VBUsedBytes(0)
+, m_IBSize(ibSize)
+, m_IBUsedBytes(0)
+, m_VB(NULL)
+, m_IB(NULL)
+, m_VertexDecl(NULL)
+, m_LastChunkStartVertex(0)
+, m_LastChunkStartIndex(0)
+, m_QuadsIB(NULL)
+, m_QuadsIBFailed(false)
+{
+}
+
+DynamicD3D9VBO::~DynamicD3D9VBO ()
+{
+ if( m_VB ) {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_VB);
+ ULONG refCount = m_VB->Release();
+ AssertIf( refCount != 0 );
+ }
+ if( m_IB ) {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_IB);
+ ULONG refCount = m_IB->Release();
+ AssertIf( refCount != 0 );
+ }
+ if( m_QuadsIB ) {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_QuadsIB);
+ ULONG refCount = m_QuadsIB->Release();
+ AssertIf( refCount != 0 );
+ }
+}
+
+void DynamicD3D9VBO::InitializeQuadsIB()
+{
+ AssertIf( m_QuadsIB );
+
+ IDirect3DDevice9* dev = GetD3DDevice();
+ HRESULT hr = dev->CreateIndexBuffer( VBO::kMaxQuads * 6 * kVBOIndexSize RANDOM_FAIL_FOR_DEBUG, D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &m_QuadsIB, NULL );
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(m_QuadsIB,VBO::kMaxQuads * 6 * kVBOIndexSize,this);
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d: failed to create quads index buffer [%s]\n", GetD3D9Error(hr) );
+ m_QuadsIBFailed = true;
+ return;
+ }
+ UInt16* ib = NULL;
+ hr = m_QuadsIB->Lock( 0 RANDOM_FAIL_FOR_DEBUG, 0, (void**)&ib, 0 );
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d: failed to lock quads index buffer [%s]\n", GetD3D9Error(hr) );
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_QuadsIB);
+ m_QuadsIB->Release();
+ m_QuadsIB = NULL;
+ m_QuadsIBFailed = true;
+ return;
+ }
+
+ UInt32 baseIndex = 0;
+ for( int i = 0; i < VBO::kMaxQuads; ++i )
+ {
+ ib[0] = baseIndex + 1;
+ ib[1] = baseIndex + 2;
+ ib[2] = baseIndex;
+ ib[3] = baseIndex + 2;
+ ib[4] = baseIndex + 3;
+ ib[5] = baseIndex;
+ baseIndex += 4;
+ ib += 6;
+ }
+
+ m_QuadsIB->Unlock();
+}
+
+void DynamicD3D9VBO::DrawChunk (const ChannelAssigns& channels)
+{
+ // just return if nothing to render
+ if( !m_LastChunkShaderChannelMask )
+ return;
+
+ HRESULT hr;
+
+ AssertIf( !m_LastChunkShaderChannelMask || !m_LastChunkStride );
+ AssertIf( m_LendedChunk );
+
+ GfxDevice& device = GetRealGfxDevice();
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ // setup VBO
+ DebugAssertIf( !m_VB );
+ D3D9_CALL(dev->SetStreamSource( 0, m_VB, 0, m_LastChunkStride ));
+ D3D9_CALL(dev->SetVertexDeclaration( m_VertexDecl ));
+ UpdateChannelBindingsD3D( channels );
+ device.BeforeDrawCall( false );
+
+ // draw
+ GfxDeviceStats& stats = device.GetFrameStats();
+ int primCount = 0;
+ if( m_LastRenderMode == kDrawTriangleStrip )
+ {
+ hr = D3D9_CALL_HR(dev->DrawPrimitive( D3DPT_TRIANGLESTRIP, m_LastChunkStartVertex, m_LastChunkVertices-2 ));
+ primCount = m_LastChunkVertices-2;
+ }
+ else if (m_LastRenderMode == kDrawIndexedTriangleStrip)
+ {
+ DebugAssertIf( !m_IB );
+ D3D9_CALL(dev->SetIndices( m_IB ));
+ hr = D3D9_CALL_HR(dev->DrawIndexedPrimitive( D3DPT_TRIANGLESTRIP, m_LastChunkStartVertex, 0, m_LastChunkVertices, m_LastChunkStartIndex, m_LastChunkIndices-2 ));
+ primCount = m_LastChunkIndices-2;
+ }
+ else if( m_LastRenderMode == kDrawQuads )
+ {
+ // initialize quads index buffer if needed
+ if( !m_QuadsIB )
+ InitializeQuadsIB();
+ // if quads index buffer has valid data, draw with it
+ if( !m_QuadsIBFailed )
+ {
+ D3D9_CALL(dev->SetIndices( m_QuadsIB ));
+ hr = D3D9_CALL_HR(dev->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, m_LastChunkStartVertex, 0, m_LastChunkVertices, 0, m_LastChunkVertices/2 ));
+ primCount = m_LastChunkVertices/2;
+ }
+ }
+ else if (m_LastRenderMode == kDrawIndexedLines)
+ {
+ DebugAssertIf( !m_IB );
+ D3D9_CALL(dev->SetIndices( m_IB ));
+ hr = D3D9_CALL_HR(dev->DrawIndexedPrimitive( D3DPT_LINELIST, m_LastChunkStartVertex, 0, m_LastChunkVertices, m_LastChunkStartIndex, m_LastChunkIndices/2 ));
+ primCount = m_LastChunkIndices/2;
+ }
+ else if (m_LastRenderMode == kDrawIndexedPoints)
+ {
+ DebugAssertIf( !m_IB );
+ D3D9_CALL(dev->SetIndices( m_IB ));
+ hr = D3D9_CALL_HR(dev->DrawIndexedPrimitive( D3DPT_POINTLIST, m_LastChunkStartVertex, 0, m_LastChunkVertices, m_LastChunkStartIndex, m_LastChunkIndices ));
+ primCount = m_LastChunkIndices;
+ }
+ else
+ {
+ DebugAssertIf( !m_IB );
+ D3D9_CALL(dev->SetIndices( m_IB ));
+ hr = D3D9_CALL_HR(dev->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, m_LastChunkStartVertex, 0, m_LastChunkVertices, m_LastChunkStartIndex, m_LastChunkIndices/3 ));
+ primCount = m_LastChunkIndices/3;
+ }
+ stats.AddDrawCall (primCount, m_LastChunkVertices);
+ AssertIf(FAILED(hr));
+}
+
+bool DynamicD3D9VBO::GetChunk( UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode renderMode, void** outVB, void** outIB )
+{
+ Assert( !m_LendedChunk );
+ Assert( maxVertices < 65536 && maxIndices < 65536*3 );
+ Assert(!((renderMode == kDrawQuads) && (VBO::kMaxQuads*4 < maxVertices)));
+ DebugAssertMsg(outVB != NULL && maxVertices > 0, "DynamicD3D9VBO::GetChunk - outVB: 0x%08x maxVertices: %d", outVB, maxVertices);
+ DebugAssertMsg(
+ (renderMode == kDrawIndexedQuads && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedPoints && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedLines && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedTriangles && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedTriangleStrip && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawTriangleStrip && (outIB == NULL && maxIndices == 0)) ||
+ (renderMode == kDrawQuads && (outIB == NULL && maxIndices == 0)),
+ "DynamicD3D9VBO::GetChunk - renderMode: %d outIB: 0x%08x maxIndices: %d", renderMode, outIB, maxIndices);
+ HRESULT hr;
+ bool success = true;
+
+ m_LendedChunk = true;
+ m_LastRenderMode = renderMode;
+
+ if( maxVertices == 0 )
+ maxVertices = 8;
+
+ m_LastChunkStride = 0;
+ for( int i = 0; i < kShaderChannelCount; ++i ) {
+ if( shaderChannelMask & (1<<i) )
+ m_LastChunkStride += VBO::GetDefaultChannelByteSize(i);
+ }
+ if (shaderChannelMask != m_LastChunkShaderChannelMask)
+ {
+ m_VertexDecl = GetD3DVertexDeclaration( shaderChannelMask );
+ m_LastChunkShaderChannelMask = shaderChannelMask;
+ }
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ // -------- vertex buffer
+
+ DebugAssertIf( !outVB );
+ UInt32 vbCapacity = maxVertices * m_LastChunkStride;
+ // check if requested chunk is larger than current buffer
+ if( vbCapacity > m_VBSize ) {
+ m_VBSize = vbCapacity * 2; // allocate more up front
+ if( m_VB ){
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_VB);
+ m_VB->Release();
+ }
+ m_VB = NULL;
+ }
+ // allocate buffer if don't have it yet
+ if( !m_VB ) {
+ hr = dev->CreateVertexBuffer( m_VBSize RANDOM_FAIL_FOR_DEBUG, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &m_VB, NULL );
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(m_VB,m_VBSize,this);
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d: failed to create dynamic vertex buffer of size %d [%s]\n", m_VBSize, GetD3D9Error(hr) );
+ success = false;
+ *outVB = NULL;
+ }
+ }
+
+ // lock, making sure the offset we lock is multiple of vertex stride
+ if( m_VB )
+ {
+ m_VBUsedBytes = ((m_VBUsedBytes + (m_LastChunkStride-1)) / m_LastChunkStride) * m_LastChunkStride;
+ if( m_VBUsedBytes + vbCapacity > m_VBSize ) {
+ hr = m_VB->Lock( 0 RANDOM_FAIL_FOR_DEBUG, 0, outVB, D3DLOCK_DISCARD );
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d: failed to lock dynamic vertex buffer with discard [%s]\n", GetD3D9Error(hr) );
+ *outVB = NULL;
+ success = false;
+ }
+ m_VBUsedBytes = 0;
+ } else {
+ hr = m_VB->Lock( m_VBUsedBytes RANDOM_FAIL_FOR_DEBUG, vbCapacity, outVB, D3DLOCK_NOOVERWRITE );
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d: failed to lock vertex index buffer, offset %i size %i [%s]\n", m_VBUsedBytes, vbCapacity, GetD3D9Error(hr) );
+ *outVB = NULL;
+ success = false;
+ }
+ }
+ m_LastChunkStartVertex = m_VBUsedBytes / m_LastChunkStride;
+ DebugAssertIf( m_LastChunkStartVertex * m_LastChunkStride != m_VBUsedBytes );
+ }
+
+ // -------- index buffer
+
+ const bool indexed = (renderMode != kDrawQuads) && (renderMode != kDrawTriangleStrip);
+ if( success && maxIndices && indexed )
+ {
+ UInt32 ibCapacity = maxIndices * kVBOIndexSize;
+ // check if requested chunk is larger than current buffer
+ if( ibCapacity > m_IBSize ) {
+ m_IBSize = ibCapacity * 2; // allocate more up front
+ if( m_IB ){
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_IB);
+ m_IB->Release();
+ }
+ m_IB = NULL;
+ }
+ // allocate buffer if don't have it yet
+ if( !m_IB ) {
+ hr = dev->CreateIndexBuffer( m_IBSize RANDOM_FAIL_FOR_DEBUG, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &m_IB, NULL );
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(m_IB,m_IBSize,this);
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d: failed to create dynamic index buffer of size %d [%s]\n", m_IBSize, GetD3D9Error(hr) );
+ if( m_VB )
+ m_VB->Unlock();
+ }
+ }
+ // lock it if we have IB created successfully
+ if( m_IB )
+ {
+ if( m_IBUsedBytes + ibCapacity > m_IBSize ) {
+ hr = m_IB->Lock( 0 RANDOM_FAIL_FOR_DEBUG, 0, outIB, D3DLOCK_DISCARD );
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d: failed to lock dynamic index buffer with discard [%s]\n", GetD3D9Error(hr) );
+ *outIB = NULL;
+ success = false;
+ if( m_VB )
+ m_VB->Unlock();
+ }
+ m_IBUsedBytes = 0;
+ } else {
+ hr = m_IB->Lock( m_IBUsedBytes RANDOM_FAIL_FOR_DEBUG, ibCapacity, outIB, D3DLOCK_NOOVERWRITE );
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d: failed to lock dynamic index buffer, offset %i size %i [%s]\n", m_IBUsedBytes, ibCapacity, GetD3D9Error(hr) );
+ *outIB = NULL;
+ success = false;
+ if( m_VB )
+ m_VB->Unlock();
+ }
+ }
+ m_LastChunkStartIndex = m_IBUsedBytes / 2;
+ }
+ else
+ {
+ *outIB = NULL;
+ success = false;
+ }
+ }
+
+ if( !success )
+ m_LendedChunk = false;
+
+ return success;
+}
+
+void DynamicD3D9VBO::ReleaseChunk( UInt32 actualVertices, UInt32 actualIndices )
+{
+ Assert( m_LendedChunk );
+ Assert( m_LastRenderMode == kDrawIndexedTriangleStrip || m_LastRenderMode == kDrawIndexedQuads || m_LastRenderMode == kDrawIndexedPoints || m_LastRenderMode == kDrawIndexedLines || actualIndices % 3 == 0 );
+ m_LendedChunk = false;
+
+ const bool indexed = (m_LastRenderMode != kDrawQuads) && (m_LastRenderMode != kDrawTriangleStrip);
+
+ m_LastChunkVertices = actualVertices;
+ m_LastChunkIndices = actualIndices;
+
+ // unlock buffers
+ m_VB->Unlock();
+ if( indexed )
+ m_IB->Unlock();
+
+ if( !actualVertices || (indexed && !actualIndices) ) {
+ m_LastChunkShaderChannelMask = 0;
+ return;
+ }
+
+ UInt32 actualVBSize = actualVertices * m_LastChunkStride;
+ m_VBUsedBytes += actualVBSize;
+ UInt32 actualIBSize = actualIndices * kVBOIndexSize;
+ m_IBUsedBytes += actualIBSize;
+}
+
+
diff --git a/Runtime/GfxDevice/d3d/D3D9VBO.h b/Runtime/GfxDevice/d3d/D3D9VBO.h
new file mode 100644
index 0000000..71c0da9
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/D3D9VBO.h
@@ -0,0 +1,86 @@
+#pragma once
+
+#include "D3D9Includes.h"
+#include "Runtime/Shaders/VBO.h"
+
+
+// Implements Direct3D9 VBO
+class D3D9VBO : public VBO {
+public:
+ D3D9VBO();
+ virtual ~D3D9VBO();
+
+ virtual void UpdateVertexData( const VertexBufferData& buffer );
+ virtual void UpdateIndexData (const IndexBufferData& buffer);
+ virtual void DrawVBO (const ChannelAssigns& channels, UInt32 firstIndexByte, UInt32 indexCount, GfxPrimitiveType topology, UInt32 firstVertex, UInt32 vertexCount);
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ virtual void DrawCustomIndexed( const ChannelAssigns& channels, void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount );
+ #endif
+ virtual bool MapVertexStream( VertexStreamData& outData, unsigned stream );
+ virtual void UnmapVertexStream( unsigned stream );
+ virtual bool IsVertexBufferLost() const;
+
+ virtual void ResetDynamicVB();
+
+ virtual int GetRuntimeMemorySize() const;
+
+ static void CleanupSharedIndexBuffer();
+
+private:
+ void BindVertexStreams( IDirect3DDevice9* dev, const ChannelAssigns& channels );
+ void UpdateVertexStream( const VertexBufferData& sourceData, unsigned stream );
+ void UpdateIndexBufferData (const IndexBufferData& sourceData);
+ static UInt16* MapDynamicIndexBuffer (int indexCount, UInt32& outBytesUsed);
+ static void UnmapDynamicIndexBuffer ();
+
+private:
+ int m_VertexCount;
+
+ enum
+ {
+ kVertexDeclDefault,
+ kVertexDeclAllWhiteStream,
+ kVertexDeclCount
+ };
+
+ IDirect3DVertexBuffer9* m_VBStreams[kMaxVertexStreams];
+ IDirect3DIndexBuffer9* m_IB;
+ IDirect3DVertexDeclaration9* m_VertexDecls[kVertexDeclCount];
+ ChannelInfoArray m_ChannelInfo;
+ int m_IBSize;
+
+ static IDirect3DIndexBuffer9* ms_CustomIB;
+ static int ms_CustomIBSize;
+ static UInt32 ms_CustomIBUsedBytes;
+};
+
+class DynamicD3D9VBO : public DynamicVBO {
+public:
+ DynamicD3D9VBO( UInt32 vbSize, UInt32 ibSize );
+ virtual ~DynamicD3D9VBO();
+
+ virtual bool GetChunk( UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode mode, void** outVB, void** outIB );
+ virtual void ReleaseChunk( UInt32 actualVertices, UInt32 actualIndices );
+ virtual void DrawChunk (const ChannelAssigns& channels);
+
+private:
+ void InitializeQuadsIB();
+
+private:
+ UInt32 m_VBSize;
+ UInt32 m_VBUsedBytes;
+ UInt32 m_IBSize;
+ UInt32 m_IBUsedBytes;
+
+ IDirect3DVertexBuffer9* m_VB;
+ IDirect3DIndexBuffer9* m_IB;
+ IDirect3DVertexDeclaration9* m_VertexDecl; // vertex declaration for the last chunk
+
+ UInt32 m_LastChunkStartVertex;
+ UInt32 m_LastChunkStartIndex;
+
+ IDirect3DIndexBuffer9* m_QuadsIB; // static IB for drawing quads
+ bool m_QuadsIBFailed;
+};
+
diff --git a/Runtime/GfxDevice/d3d/D3D9Window.cpp b/Runtime/GfxDevice/d3d/D3D9Window.cpp
new file mode 100644
index 0000000..b568b34
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/D3D9Window.cpp
@@ -0,0 +1,272 @@
+#include "UnityPrefix.h"
+#include "D3D9Window.h"
+#include "GfxDeviceD3D9.h"
+#include "RenderTextureD3D.h"
+#include "Runtime/Misc/QualitySettings.h"
+#include "Runtime/Threads/ThreadSharedObject.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+
+#if UNITY_EDITOR
+
+bool IsD3D9DeviceLost();
+void SetD3D9DeviceLost( bool lost );
+
+static bool s_OldHasDepthFlag = false;
+static D3D9Window* s_CurrentD3DWindow = NULL;
+static int s_CurrentD3DFSAALevel = 0;
+
+int GetCurrentD3DFSAALevel() { return s_CurrentD3DFSAALevel; }
+
+void SetNoRenderTextureActiveEditor(); // RenderTexture.cpp
+
+
+D3D9Window::D3D9Window(IDirect3DDevice9* device, HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias )
+: GfxDeviceWindow(window, width, height, depthFormat, antiAlias)
+, m_SwapChain(NULL)
+, m_FSAALevel(0)
+{
+ m_Device = device;
+ Reshape( width, height, depthFormat, antiAlias );
+}
+
+D3D9Window::~D3D9Window()
+{
+ if( s_CurrentD3DWindow == this )
+ {
+ s_CurrentD3DWindow = NULL;
+ s_CurrentD3DFSAALevel = 0;
+ }
+
+ DestroyRenderSurfaceD3D9(&m_DepthStencil);
+ DestroyRenderSurfaceD3D9(&m_BackBuffer);
+ SAFE_RELEASE(m_SwapChain);
+}
+
+bool D3D9Window::Reshape( int width, int height, DepthBufferFormat depthFormat, int antiAlias )
+{
+ if(GfxDeviceWindow::Reshape(width, height, depthFormat, antiAlias)==false)return false;
+
+
+ #if ENABLE_D3D_WINDOW_LOGGING
+ printf_console("D3Dwindow %x Reshape %ix%i d=%i aa=%i\n", this, width, height, depthFormat, antiAlias);
+ #endif
+ // release old
+ m_DepthStencil.Release();
+ m_BackBuffer.Release();
+ SAFE_RELEASE(m_SwapChain);
+
+ HRESULT hr;
+
+
+ // Choose presentation params
+ if( antiAlias == -1 )
+ antiAlias = GetQualitySettings().GetCurrent().antiAliasing;
+
+ D3DDISPLAYMODE mode;
+ hr = GetD3DObject()->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &mode );
+ D3DPRESENT_PARAMETERS params;
+
+ ZeroMemory( &params, sizeof(params) );
+ params.BackBufferWidth = m_Width;
+ params.BackBufferHeight = m_Height;
+ params.BackBufferCount = 1;
+ params.hDeviceWindow = m_Window;
+ params.FullScreen_RefreshRateInHz = 0;
+ params.Windowed = TRUE;
+ params.SwapEffect = D3DSWAPEFFECT_COPY;
+ params.BackBufferFormat = D3DFMT_A8R8G8B8;
+ params.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
+ params.EnableAutoDepthStencil = FALSE;
+ GetD3DFormatCaps()->FindBestPresentationParams( width, height, mode.Format, true, 0, antiAlias, params );
+ if( params.MultiSampleType != D3DMULTISAMPLE_NONE ) {
+ params.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ m_CanUseBlitOptimization = false;
+ } else {
+ m_CanUseBlitOptimization = true;
+ }
+ m_FSAALevel = (params.MultiSampleType == D3DMULTISAMPLE_NONMASKABLE) ? params.MultiSampleQuality : params.MultiSampleType;
+
+ hr = m_Device->CreateAdditionalSwapChain( &params, &m_SwapChain );
+ if( FAILED(hr) ) {
+ printf_console( "d3d: swap chain: swap=%i vsync=%x w=%i h=%i fmt=%i bbcount=%i dsformat=%i pflags=%x\n",
+ params.SwapEffect, params.PresentationInterval,
+ params.BackBufferWidth, params.BackBufferHeight, params.BackBufferFormat, params.BackBufferCount,
+ params.AutoDepthStencilFormat, params.Flags );
+ printf_console( "d3d: failed to create swap chain [%s]\n", GetD3D9Error(hr) );
+ m_InvalidState = true;
+ return !m_InvalidState;
+ }
+
+ IDirect3DSurface9* backBuffer = NULL;
+ hr = m_SwapChain->GetBackBuffer( 0, D3DBACKBUFFER_TYPE_MONO, &backBuffer );
+ if( FAILED(hr) ) {
+ AssertString( "Failed to get back buffer for D3DWindow" );
+ m_SwapChain->Release();
+ m_SwapChain = NULL;
+ m_InvalidState = true;
+ return !m_InvalidState;
+ }
+
+ m_BackBuffer.backBuffer = true;
+ m_DepthStencil.backBuffer = true;
+
+ m_BackBuffer.m_Surface = backBuffer;
+ m_BackBuffer.width = params.BackBufferWidth;
+ m_BackBuffer.height = params.BackBufferHeight;
+ m_BackBuffer.format = kRTFormatARGB32;
+
+ // Depth format
+ bool needsDepth = false;
+ m_DepthStencilFormat = D3DFMT_UNKNOWN;
+ switch( depthFormat ) {
+ case kDepthFormatNone:
+ needsDepth = false;
+ m_DepthStencilFormat = D3DFMT_UNKNOWN;
+ break;
+ case kDepthFormat16:
+ needsDepth = true;
+ m_DepthStencilFormat = D3DFMT_D16;
+ break;
+ case kDepthFormat24:
+ needsDepth = true;
+ m_DepthStencilFormat = D3DFMT_D24S8;
+ break;
+ default:
+ ErrorString("Unknown depth format");
+ }
+
+ if( needsDepth )
+ {
+ D3D9DepthStencilTexture depthStencil = CreateDepthStencilTextureD3D9 (m_Device, m_Width, m_Height, m_DepthStencilFormat, params.MultiSampleType, params.MultiSampleQuality, FALSE);
+ m_Device->SetRenderState (D3DRS_ZENABLE, TRUE);
+ if (!depthStencil.m_Surface)
+ {
+ AssertString( "Failed to create depth/stencil for D3DWindow" );
+ m_SwapChain->Release();
+ m_SwapChain = NULL;
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_BackBuffer.m_Surface);
+ m_BackBuffer.m_Surface->Release();
+ m_BackBuffer.m_Surface = NULL;
+ m_InvalidState = true;
+ return !m_InvalidState;
+ }
+ m_DepthStencil.m_Surface = depthStencil.m_Surface;
+ m_DepthStencil.m_Texture = depthStencil.m_Texture;
+ m_DepthStencil.width = m_Width;
+ m_DepthStencil.height = m_Height;
+ m_DepthStencil.depthFormat = depthFormat;
+ }
+
+ return !m_InvalidState;
+}
+
+void D3D9Window::SetAsActiveWindow ()
+{
+ GetRealGfxDevice().SetRenderTargets(1, &GetBackBuffer(), GetDepthStencil());
+ GetRealGfxDevice().SetActiveRenderTexture(NULL);
+ GetRealGfxDevice().SetCurrentWindowSize(m_Width, m_Height);
+ GetRealGfxDevice().SetInvertProjectionMatrix(false);
+
+ s_OldHasDepthFlag = g_D3DHasDepthStencil;
+ g_D3DHasDepthStencil = (m_DepthStencil.m_Surface != NULL);
+
+ s_CurrentD3DWindow = this;
+ s_CurrentD3DFSAALevel = m_FSAALevel;
+
+ // not entirely correct but better not touch anything if we don't have depth
+ if(m_DepthStencil.m_Surface != NULL)
+ g_D3DDepthStencilFormat = m_DepthStencilFormat;
+}
+
+bool D3D9Window::BeginRendering()
+{
+ if (GfxDeviceWindow::BeginRendering())
+ {
+ HRESULT hr;
+
+ // Handle lost devices
+ if (!GetRealGfxDevice().IsValidState())
+ {
+ return false;
+ }
+
+ // begin scene
+ if (IsD3D9DeviceLost())
+ {
+ ErrorString ("GUI Window tries to begin rendering while D3D9 device is lost!");
+ }
+ GfxDeviceD3D9& device = static_cast<GfxDeviceD3D9&>( GetRealGfxDevice() );
+ if (device.IsInsideFrame())
+ {
+ ErrorString ("GUI Window tries to begin rendering while something else has not finished rendering! Either you have a recursive OnGUI rendering, or previous OnGUI did not clean up properly.");
+ }
+
+ m_Device->BeginScene();
+ SetAsActiveWindow ();
+
+ device.SetInsideFrame(true);
+ return true;
+ }
+ else
+ {
+ #if ENABLE_D3D_WINDOW_LOGGING
+ printf_console("D3Dwindow %ix%i BeginRendering: invalid state\n", m_Width, m_Height);
+ #endif
+ return false;
+ }
+}
+
+bool D3D9Window::EndRendering( bool presentContent )
+{
+ if(GfxDeviceWindow::EndRendering(presentContent))
+ {
+
+ g_D3DHasDepthStencil = s_OldHasDepthFlag;
+ s_CurrentD3DWindow = NULL;
+ s_CurrentD3DWindow = 0;
+
+ if( IsD3D9DeviceLost() )
+ return false;
+
+ HRESULT hr;
+ GfxDeviceD3D9& device = static_cast<GfxDeviceD3D9&>( GetRealGfxDevice() );
+ Assert( device.IsInsideFrame() );
+ hr = m_Device->EndScene();
+ device.SetInsideFrame(false);
+ if( m_SwapChain && presentContent )
+ {
+ hr = m_SwapChain->Present( NULL, NULL, NULL, NULL, 0 );
+ device.PushEventQuery();
+ // When D3DERR_DRIVERINTERNALERROR is returned from Present(),
+ // the application can do one of the following, try recovering just as
+ // from the lost device.
+ if( hr == D3DERR_DEVICELOST || hr == D3DERR_DRIVERINTERNALERROR )
+ {
+ SetD3D9DeviceLost( true );
+ return false;
+ }
+ }
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+RenderSurfaceHandle D3D9Window::GetBackBuffer()
+{
+ RenderSurfaceHandle handle;
+ handle.object = &m_BackBuffer;
+ return handle;
+}
+
+RenderSurfaceHandle D3D9Window::GetDepthStencil()
+{
+ RenderSurfaceHandle handle;
+ handle.object = &m_DepthStencil;
+ return handle;
+}
+
+#endif
diff --git a/Runtime/GfxDevice/d3d/D3D9Window.h b/Runtime/GfxDevice/d3d/D3D9Window.h
new file mode 100644
index 0000000..038b59f
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/D3D9Window.h
@@ -0,0 +1,39 @@
+#ifndef D3D9WINDOW_H
+#define D3D9WINDOW_H
+
+#include "D3D9Includes.h"
+#include "Runtime/GfxDevice/GfxDeviceWindow.h"
+#include "Runtime/GfxDevice/GfxDeviceObjects.h"
+#include "D3D9Utils.h"
+#include "TexturesD3D9.h"
+
+class D3D9Window : public GfxDeviceWindow
+{
+private:
+ IDirect3DDevice9* m_Device;
+ IDirect3DSwapChain9* m_SwapChain;
+ RenderColorSurfaceD3D9 m_BackBuffer;
+ RenderDepthSurfaceD3D9 m_DepthStencil;
+ D3DFORMAT m_DepthStencilFormat;
+ int m_FSAALevel;
+public:
+ D3D9Window( IDirect3DDevice9* device, HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias );
+ ~D3D9Window();
+
+ bool Reshape( int width, int height, DepthBufferFormat depthFormat, int antiAlias );
+
+ bool BeginRendering();
+ bool EndRendering( bool presentContent );
+ void SetAsActiveWindow ();
+
+ D3DFORMAT GetDepthStencilFormat() const { return m_DepthStencilFormat; }
+
+ RenderSurfaceHandle GetBackBuffer();
+ RenderSurfaceHandle GetDepthStencil();
+};
+
+#if UNITY_EDITOR
+int GetCurrentD3DFSAALevel();
+#endif
+
+#endif
diff --git a/Runtime/GfxDevice/d3d/GfxDeviceD3D9.cpp b/Runtime/GfxDevice/d3d/GfxDeviceD3D9.cpp
new file mode 100644
index 0000000..77fe956
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/GfxDeviceD3D9.cpp
@@ -0,0 +1,3009 @@
+#include "UnityPrefix.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "GfxDeviceD3D9.h"
+#include "D3D9Context.h"
+#include "Runtime/Math/FloatConversion.h"
+#include "D3D9VBO.h"
+#include "CombinerD3D.h"
+#include "External/shaderlab/Library/program.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "External/shaderlab/Library/pass.h"
+#include "Runtime/GfxDevice/BuiltinShaderParams.h"
+#include "Runtime/GfxDevice/GpuProgramParamsApply.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "PlatformDependent/Win/SmartComPointer.h"
+#include "PlatformDependent/Win/WinUnicode.h"
+#include "Runtime/Allocator/LinearAllocator.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Misc/Plugins.h"
+#include "D3D9Utils.h"
+#include "D3D9Window.h"
+#include "RenderTextureD3D.h"
+#include "GpuProgramsD3D.h"
+#include "TimerQueryD3D9.h"
+#include "GfxDeviceD3D9.h"
+
+
+// --------------------------------------------------------------------------
+
+bool IsActiveRenderTargetWithColorD3D9();
+
+typedef std::list<IDirect3DQuery9*> D3D9QueryList;
+static D3D9QueryList s_EventQueries;
+
+static void PushEventQuery (int maxBuffer);
+static void CleanupEventQueries ();
+
+
+
+static const D3DBLEND kBlendModeD3D9[] = {
+ D3DBLEND_ZERO, D3DBLEND_ONE, D3DBLEND_DESTCOLOR, D3DBLEND_SRCCOLOR, D3DBLEND_INVDESTCOLOR, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCCOLOR,
+ D3DBLEND_DESTALPHA, D3DBLEND_INVDESTALPHA, D3DBLEND_SRCALPHASAT, D3DBLEND_INVSRCALPHA,
+};
+
+static const D3DBLENDOP kBlendOpD3D9[] = {
+ D3DBLENDOP_ADD, D3DBLENDOP_SUBTRACT, D3DBLENDOP_REVSUBTRACT, D3DBLENDOP_MIN, D3DBLENDOP_MAX,
+};
+
+static const D3DCMPFUNC kCmpFuncD3D9[] = {
+ D3DCMP_ALWAYS, D3DCMP_NEVER, D3DCMP_LESS, D3DCMP_EQUAL, D3DCMP_LESSEQUAL, D3DCMP_GREATER, D3DCMP_NOTEQUAL, D3DCMP_GREATEREQUAL, D3DCMP_ALWAYS
+};
+
+static const D3DSTENCILOP kStencilOpD3D9[] = {
+ D3DSTENCILOP_KEEP, D3DSTENCILOP_ZERO, D3DSTENCILOP_REPLACE, D3DSTENCILOP_INCRSAT,
+ D3DSTENCILOP_DECRSAT, D3DSTENCILOP_INVERT, D3DSTENCILOP_INCR, D3DSTENCILOP_DECR
+};
+
+static D3DCULL kCullModeD3D9[] = {
+ D3DCULL_NONE, D3DCULL_CW, D3DCULL_CCW
+};
+
+// --------------------------------------------------------------------------
+
+
+static inline D3DCOLOR ColorToD3D( const float color[4] )
+{
+ return D3DCOLOR_RGBA( NormalizedToByte(color[0]), NormalizedToByte(color[1]), NormalizedToByte(color[2]), NormalizedToByte(color[3]) );
+}
+
+
+
+// --------------------------------------------------------------------------
+
+enum {
+ kNeedsSoftwareVPVertexShader = (1<<0),
+ kNeedsSoftwareVPTexGen = (1<<1),
+};
+
+class GfxDeviceD3D9;
+
+static void ApplyBackfaceMode( DeviceStateD3D& state );
+static void ApplyStencilFuncAndOp( DeviceStateD3D& state );
+
+
+
+
+
+void DeviceStateD3D::Invalidate( GfxDeviceD3D9& device )
+{
+ int i;
+
+ depthFunc = kFuncUnknown;
+ depthWrite = -1;
+
+ blending = -1; // unknown
+ srcBlend = destBlend = srcBlendAlpha = destBlendAlpha = -1; // won't match any D3D mode
+ blendOp = blendOpAlpha = -1; // won't match any D3D mode
+ alphaFunc = kFuncUnknown;
+ alphaValue = -1.0f;
+
+ culling = kCullUnknown;
+ d3dculling = D3DCULL_FORCE_DWORD;
+ scissor = -1;
+
+ offsetFactor = offsetUnits = -1000.0f;
+ for( i = 0; i < kShaderTypeCount; ++i )
+ {
+ activeGpuProgramParams[i] = NULL;
+ activeGpuProgram[i] = NULL;
+ activeShader[i] = NULL;
+ }
+ fixedFunctionPS = 0;
+
+ colorWriteMask = -1; // TBD ?
+ m_StencilRef = -1;
+
+ for (i = 0; i < ARRAY_SIZE(texturesPS); ++i)
+ texturesPS[i].Invalidate();
+ for (i = 0; i < ARRAY_SIZE(texturesVS); ++i)
+ texturesVS[i].Invalidate();
+
+ m_SoftwareVP = false;
+ m_NeedsSofwareVPFlags = 0;
+
+ IDirect3DDevice9* dev = GetD3DDeviceNoAssert();
+ if( dev && !m_DeviceLost )
+ {
+ D3D9_CALL(dev->SetVertexShader( NULL ));
+ D3D9_CALL(dev->SetPixelShader( NULL ));
+
+ ApplyBackfaceMode( *this );
+
+ if( g_D3DUsesMixedVP )
+ D3D9_CALL(dev->SetSoftwareVertexProcessing( FALSE ));
+
+ // misc. state
+ D3D9_CALL(dev->SetRenderState( D3DRS_LOCALVIEWER, TRUE ));
+
+ #if UNITY_EDITOR
+ D3D9_CALL(dev->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID ));
+ #endif
+ }
+}
+
+
+void UpdateChannelBindingsD3D( const ChannelAssigns& channels )
+{
+ // Texture coordinate index bindings
+ GfxDeviceD3D9& device = (GfxDeviceD3D9&)GetRealGfxDevice();
+ if( device.IsShaderActive(kShaderVertex) )
+ return;
+ DeviceStateD3D& state = device.GetState();
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ const int maxTexCoords = gGraphicsCaps.maxTexCoords; // fetch here once
+
+ VertexPipeConfig& config = device.GetVertexPipeConfig();
+ UInt32 textureSources = config.textureSources;
+ for( int i = 0; i < maxTexCoords; ++i )
+ {
+ UInt32 source = (textureSources >> (i*3)) & 0x7;
+ if( source > kTexSourceUV1 )
+ continue;
+ ShaderChannel texCoordChannel = channels.GetSourceForTarget( (VertexComponent)(kVertexCompTexCoord0 + i) );
+ if( texCoordChannel == kShaderChannelTexCoord0 )
+ textureSources = textureSources & ~(7<<i*3) | (kTexSourceUV0<<i*3);
+ else if( texCoordChannel == kShaderChannelTexCoord1 )
+ textureSources = textureSources & ~(7<<i*3) | (kTexSourceUV1<<i*3);
+ else if( texCoordChannel != kShaderChannelNone ) {
+ AssertString( "Bad texcoord index" );
+ }
+ }
+ config.textureSources = textureSources;
+
+ config.hasVertexColor = (channels.GetTargetMap() & (1<<kVertexCompColor)) ? 1 : 0;
+}
+
+
+struct SetValuesFunctorD3D9
+{
+ SetValuesFunctorD3D9(GfxDevice& device, VertexShaderConstantCache& vs, PixelShaderConstantCache& ps) : m_Device(device), vscache(vs), pscache(ps) { }
+ GfxDevice& m_Device;
+ VertexShaderConstantCache& vscache;
+ PixelShaderConstantCache& pscache;
+ void SetVectorVal (ShaderType shaderType, ShaderParamType type, int index, const float* ptr, int cols, const GpuProgramParameters& params, int cbIndex)
+ {
+ if (shaderType == kShaderVertex)
+ vscache.SetValues(index, ptr, 1);
+ else
+ pscache.SetValues(index, ptr, 1);
+ }
+ void SetMatrixVal (ShaderType shaderType, int index, const Matrix4x4f* ptr, int rows, const GpuProgramParameters& params, int cbIndex)
+ {
+ DebugAssert(rows == 4);
+ Matrix4x4f mat;
+ TransposeMatrix4x4 (ptr, &mat);
+ if (shaderType == kShaderVertex)
+ vscache.SetValues(index, mat.GetPtr(), 4);
+ else
+ pscache.SetValues(index, mat.GetPtr(), 4);
+ }
+ void SetTextureVal (ShaderType shaderType, int index, int samplerIndex, TextureDimension dim, TextureID texID)
+ {
+ m_Device.SetTexture (shaderType, index, samplerIndex, texID, dim, std::numeric_limits<float>::infinity());
+ }
+};
+
+
+// Compute/Update any deferred state before each draw call
+void GfxDeviceD3D9::BeforeDrawCall( bool immediateMode )
+{
+ VertexShaderConstantCache& vscache = GetVertexShaderConstantCache();
+ PixelShaderConstantCache& pscache = GetPixelShaderConstantCache();
+ DeviceStateD3D& state = m_State;
+ IDirect3DDevice9* dev = GetD3DDevice();
+ bool usesVertexShader = (state.activeShader[kShaderVertex] != NULL);
+
+ //@TODO: remove TESTING CODE
+ static bool oldTnL = false;
+ if( oldTnL != (!immediateMode) )
+ {
+ m_VertexPrevious.config.Reset ();
+ m_VertexPrevious.ambient.set(-1,-1,-1,-1);
+ oldTnL = !immediateMode;
+ }
+
+ m_TransformState.UpdateWorldViewMatrix (m_BuiltinParamValues);
+
+ // Deferred setup of fixed function stuff
+ if (!immediateMode)
+ SetupVertexShaderD3D9( dev, m_TransformState, m_BuiltinParamValues, m_VertexConfig, m_VertexData, m_VertexPrevious, vscache, usesVertexShader, immediateMode );
+ else
+ SetupFixedFunctionD3D9( dev, m_TransformState, m_BuiltinParamValues, m_VertexConfig, m_VertexData, m_VertexPrevious, usesVertexShader, immediateMode );
+
+
+ // update GL equivalents of built-in shader state
+
+ const BuiltinShaderParamIndices& paramsVS = *m_BuiltinParamIndices[kShaderVertex];
+ const BuiltinShaderParamIndices& paramsPS = *m_BuiltinParamIndices[kShaderFragment];
+ int gpuIndexVS, gpuIndexPS;
+
+#define SET_BUILTIN_MATRIX_BEGIN(idx) \
+ gpuIndexVS = paramsVS.mat[idx].gpuIndex; gpuIndexPS = paramsPS.mat[idx].gpuIndex; if (gpuIndexVS >= 0 || gpuIndexPS >= 0)
+
+#define SET_BUILTIN_MATRIX_END(name) \
+ if (gpuIndexVS >= 0) vscache.SetValues(gpuIndexVS, name.GetPtr(), 4); \
+ if (gpuIndexPS >= 0) pscache.SetValues(gpuIndexPS, name.GetPtr(), 4)
+
+ // MVP matrix
+ SET_BUILTIN_MATRIX_BEGIN(kShaderInstanceMatMVP)
+ {
+ Matrix4x4f matMul;
+ MultiplyMatrices4x4 (&m_BuiltinParamValues.GetMatrixParam(kShaderMatProj), &m_TransformState.worldViewMatrix, &matMul);
+ Matrix4x4f mat;
+ TransposeMatrix4x4 (&matMul, &mat);
+ SET_BUILTIN_MATRIX_END(mat);
+ }
+ // MV matrix
+ SET_BUILTIN_MATRIX_BEGIN(kShaderInstanceMatMV)
+ {
+ Matrix4x4f mat;
+ TransposeMatrix4x4 (&m_TransformState.worldViewMatrix, &mat);
+ SET_BUILTIN_MATRIX_END(mat);
+ }
+ // Transpose MV matrix
+ SET_BUILTIN_MATRIX_BEGIN(kShaderInstanceMatTransMV)
+ {
+ const Matrix4x4f& mat = m_TransformState.worldViewMatrix;
+ SET_BUILTIN_MATRIX_END(mat);
+ }
+ // Inverse transpose of MV matrix
+ SET_BUILTIN_MATRIX_BEGIN(kShaderInstanceMatInvTransMV)
+ {
+ Matrix4x4f mat;
+ Matrix4x4f::Invert_Full (m_TransformState.worldViewMatrix, mat);
+ if (m_VertexData.normalization == kNormalizationScale)
+ {
+ // Inverse transpose of modelview should be scaled by uniform
+ // normal scale (this will match state.matrix.invtrans.modelview
+ // and gl_NormalMatrix in OpenGL)
+ float scale = Magnitude (m_TransformState.worldMatrix.GetAxisX());
+ mat.Get (0, 0) *= scale;
+ mat.Get (1, 0) *= scale;
+ mat.Get (2, 0) *= scale;
+ mat.Get (0, 1) *= scale;
+ mat.Get (1, 1) *= scale;
+ mat.Get (2, 1) *= scale;
+ mat.Get (0, 2) *= scale;
+ mat.Get (1, 2) *= scale;
+ mat.Get (2, 2) *= scale;
+ }
+ SET_BUILTIN_MATRIX_END(mat);
+ }
+ // M matrix
+ SET_BUILTIN_MATRIX_BEGIN(kShaderInstanceMatM)
+ {
+ Matrix4x4f mat;
+ TransposeMatrix4x4 (&m_TransformState.worldMatrix, &mat);
+ SET_BUILTIN_MATRIX_END(mat);
+ }
+ // Inverse M matrix
+ SET_BUILTIN_MATRIX_BEGIN(kShaderInstanceMatInvM)
+ {
+ Matrix4x4f mat = m_TransformState.worldMatrix;
+ if (m_VertexData.normalization == kNormalizationScale)
+ {
+ // Kill scale in the world matrix before inverse
+ float invScale = m_BuiltinParamValues.GetInstanceVectorParam(kShaderInstanceVecScale).w;
+ mat.Get (0, 0) *= invScale;
+ mat.Get (1, 0) *= invScale;
+ mat.Get (2, 0) *= invScale;
+ mat.Get (0, 1) *= invScale;
+ mat.Get (1, 1) *= invScale;
+ mat.Get (2, 1) *= invScale;
+ mat.Get (0, 2) *= invScale;
+ mat.Get (1, 2) *= invScale;
+ mat.Get (2, 2) *= invScale;
+ }
+ Matrix4x4f inverseMat;
+ Matrix4x4f::Invert_General3D (mat, inverseMat);
+ TransposeMatrix4x4 (&inverseMat, &mat);
+ SET_BUILTIN_MATRIX_END(mat);
+ }
+
+ // Set instance vector parameters
+ for (int i = 0; i < kShaderInstanceVecCount; ++i)
+ {
+ gpuIndexVS = paramsVS.vec[i].gpuIndex;
+ if (gpuIndexVS >= 0)
+ vscache.SetValues(gpuIndexVS, m_BuiltinParamValues.GetInstanceVectorParam((ShaderBuiltinInstanceVectorParam)i).GetPtr(), 1);
+ gpuIndexPS = paramsPS.vec[i].gpuIndex;
+ if (gpuIndexPS >= 0)
+ pscache.SetValues(gpuIndexPS, m_BuiltinParamValues.GetInstanceVectorParam((ShaderBuiltinInstanceVectorParam)i).GetPtr(), 1);
+ }
+
+ // Texture matrices for vertex shader
+ for( int i = 0; i < 8; ++i )
+ {
+ if( paramsVS.mat[kShaderInstanceMatTexture0 + i].gpuIndex >= 0 )
+ {
+ Matrix4x4f mat;
+ TransposeMatrix4x4 (&m_TransformState.texMatrices[i], &mat);
+ const int index = paramsVS.mat[kShaderInstanceMatTexture0 + i].gpuIndex;
+ vscache.SetValues( index, mat.GetPtr(), 4 );
+ }
+ }
+
+ // Software VP flags
+ if( g_D3DUsesMixedVP )
+ {
+ if( state.m_NeedsSofwareVPFlags )
+ {
+ if( state.m_SoftwareVP == false )
+ {
+ D3D9_CALL(dev->SetSoftwareVertexProcessing( TRUE ));
+ state.m_SoftwareVP = true;
+ }
+ }
+ else
+ {
+ if( state.m_SoftwareVP == true )
+ {
+ D3D9_CALL(dev->SetSoftwareVertexProcessing( FALSE ));
+ state.m_SoftwareVP = false;
+ }
+ }
+ }
+
+ SetValuesFunctorD3D9 setValuesFunc(*this, vscache, pscache);
+ ApplyMaterialPropertyBlockValues(m_MaterialProperties, m_State.activeGpuProgram ,m_State.activeGpuProgramParams, setValuesFunc);
+
+ vscache.CommitVertexConstants();
+ pscache.CommitPixelConstants();
+}
+
+
+DeviceBlendState* GfxDeviceD3D9::CreateBlendState(const GfxBlendState& state)
+{
+ std::pair<CachedBlendStates::iterator, bool> result = m_CachedBlendStates.insert(std::make_pair(state, DeviceBlendStateD3D9()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceBlendStateD3D9& d3dstate = result.first->second;
+ memcpy(&d3dstate.sourceState, &state, sizeof(GfxBlendState));
+ DWORD d3dmask = 0;
+ const UInt8 mask = state.renderTargetWriteMask;
+ if( mask & kColorWriteR ) d3dmask |= D3DCOLORWRITEENABLE_RED;
+ if( mask & kColorWriteG ) d3dmask |= D3DCOLORWRITEENABLE_GREEN;
+ if( mask & kColorWriteB ) d3dmask |= D3DCOLORWRITEENABLE_BLUE;
+ if( mask & kColorWriteA ) d3dmask |= D3DCOLORWRITEENABLE_ALPHA;
+ d3dstate.renderTargetWriteMask = d3dmask;
+
+ DebugAssertIf(kFuncUnknown==state.alphaTest);
+ d3dstate.alphaFunc = kCmpFuncD3D9[state.alphaTest];
+ return &result.first->second;
+}
+
+
+DeviceDepthState* GfxDeviceD3D9::CreateDepthState(const GfxDepthState& state)
+{
+ std::pair<CachedDepthStates::iterator, bool> result = m_CachedDepthStates.insert(std::make_pair(state, DeviceDepthStateD3D9()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceDepthStateD3D9& d3dstate = result.first->second;
+ memcpy(&d3dstate.sourceState, &state, sizeof(GfxDepthState));
+ d3dstate.depthFunc = kCmpFuncD3D9[state.depthFunc];
+ return &result.first->second;
+}
+
+DeviceStencilState* GfxDeviceD3D9::CreateStencilState(const GfxStencilState& state)
+{
+ std::pair<CachedStencilStates::iterator, bool> result = m_CachedStencilStates.insert(std::make_pair(state, DeviceStencilStateD3D9()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceStencilStateD3D9& st = result.first->second;
+ memcpy(&st.sourceState, &state, sizeof(state));
+ st.stencilFuncFront = kCmpFuncD3D9[state.stencilFuncFront];
+ st.stencilFailOpFront = kStencilOpD3D9[state.stencilFailOpFront];
+ st.depthFailOpFront = kStencilOpD3D9[state.stencilZFailOpFront];
+ st.depthPassOpFront = kStencilOpD3D9[state.stencilPassOpFront];
+ st.stencilFuncBack = kCmpFuncD3D9[state.stencilFuncBack];
+ st.stencilFailOpBack = kStencilOpD3D9[state.stencilFailOpBack];
+ st.depthFailOpBack = kStencilOpD3D9[state.stencilZFailOpBack];
+ st.depthPassOpBack = kStencilOpD3D9[state.stencilPassOpBack];
+ return &result.first->second;
+}
+
+
+
+DeviceRasterState* GfxDeviceD3D9::CreateRasterState(const GfxRasterState& state)
+{
+ std::pair<CachedRasterStates::iterator, bool> result = m_CachedRasterStates.insert(std::make_pair(state, DeviceRasterState()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceRasterState& d3dstate = result.first->second;
+ memcpy(&d3dstate.sourceState, &state, sizeof(DeviceRasterState));
+
+ return &result.first->second;
+}
+
+
+void GfxDeviceD3D9::SetBlendState(const DeviceBlendState* state, float alphaRef)
+{
+ DeviceBlendStateD3D9* devstate = (DeviceBlendStateD3D9*)state;
+
+ if (m_CurrBlendState == devstate && alphaRef == m_State.alphaValue)
+ return;
+
+ m_CurrBlendState = devstate;
+ if (!m_CurrBlendState)
+ return;
+
+ UInt32 colMask = devstate->renderTargetWriteMask;
+ if (!IsActiveRenderTargetWithColorD3D9())
+ colMask = 0;
+
+ if(colMask != m_State.colorWriteMask)
+ {
+ IDirect3DDevice9* dev = GetD3DDeviceNoAssert();
+ D3D9_CALL(dev->SetRenderState(D3DRS_COLORWRITEENABLE, colMask));
+ m_State.colorWriteMask = colMask;
+ }
+
+ const GfxBlendState& desc = state->sourceState;
+ const CompareFunction mode = state->sourceState.alphaTest;
+ const D3DBLEND d3dsrc = kBlendModeD3D9[desc.srcBlend];
+ const D3DBLEND d3ddst = kBlendModeD3D9[desc.dstBlend];
+ const D3DBLEND d3dsrca = kBlendModeD3D9[desc.srcBlendAlpha];
+ const D3DBLEND d3ddsta = kBlendModeD3D9[desc.dstBlendAlpha];
+ const D3DBLENDOP d3dop = kBlendOpD3D9[desc.blendOp];
+ const D3DBLENDOP d3dopa = kBlendOpD3D9[desc.blendOpAlpha];
+
+ const bool blendDisabled = (d3dsrc == D3DBLEND_ONE && d3ddst == D3DBLEND_ZERO && d3dsrca == D3DBLEND_ONE && d3ddsta == D3DBLEND_ZERO);
+
+ IDirect3DDevice9* dev = GetD3DDevice();
+ if(blendDisabled)
+ {
+ if( m_State.blending != 0 )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE ));
+ m_State.blending = 0;
+ }
+ }
+ else
+ {
+ if( d3dsrc != m_State.srcBlend || d3ddst != m_State.destBlend )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_SRCBLEND, d3dsrc ));
+ D3D9_CALL(dev->SetRenderState( D3DRS_DESTBLEND, d3ddst ));
+ m_State.srcBlend = d3dsrc;
+ m_State.destBlend = d3ddst;
+ }
+
+ if (d3dop != m_State.blendOp)
+ {
+ bool supports = true;
+ if( (d3dop == D3DBLENDOP_SUBTRACT || d3dop == D3DBLENDOP_REVSUBTRACT) && !gGraphicsCaps.hasBlendSub )
+ supports = false;
+ if( (d3dop == D3DBLENDOP_MIN || d3dop == D3DBLENDOP_MAX) && !gGraphicsCaps.hasBlendMinMax )
+ supports = false;
+
+ if(supports)
+ {
+ D3D9_CALL(dev->SetRenderState(D3DRS_BLENDOP, d3dop));
+ m_State.blendOp = d3dop;
+ }
+ }
+ if (gGraphicsCaps.hasSeparateAlphaBlend)
+ {
+ if( d3dsrca != m_State.srcBlendAlpha || d3ddsta != m_State.destBlendAlpha || d3dopa != m_State.blendOpAlpha )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_SEPARATEALPHABLENDENABLE, d3dsrc != d3dsrca || d3ddst != d3ddsta || d3dopa != d3dop));
+ D3D9_CALL(dev->SetRenderState( D3DRS_SRCBLENDALPHA, d3dsrca ));
+ D3D9_CALL(dev->SetRenderState( D3DRS_DESTBLENDALPHA, d3ddsta ));
+ m_State.srcBlendAlpha = d3dsrca;
+ m_State.destBlendAlpha = d3ddsta;
+
+ bool supports = true;
+ if( (d3dopa == D3DBLENDOP_SUBTRACT || d3dopa == D3DBLENDOP_REVSUBTRACT) && !gGraphicsCaps.hasBlendSub )
+ supports = false;
+ if( (d3dopa == D3DBLENDOP_MIN || d3dopa == D3DBLENDOP_MAX) && !gGraphicsCaps.hasBlendMinMax )
+ supports = false;
+
+ if (supports)
+ {
+ D3D9_CALL(dev->SetRenderState(D3DRS_BLENDOPALPHA, d3dopa));
+ m_State.blendOpAlpha = d3dopa;
+ }
+ }
+ }
+ if( m_State.blending != 1 )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ));
+ m_State.blending = 1;
+ }
+ }
+
+ DebugAssertIf(mode==kFuncUnknown);
+#if UNITY_EDITOR // gles2.0 doesn't have FF alpha testing(only discard/clip on shader side), so disable on editor while emulating
+ bool skipAlphaTestFF = (gGraphicsCaps.IsEmulatingGLES20() && IsShaderActive(kShaderFragment));
+ // possible that vertex shader will be used with FF "frag shader" (like Transparent/vertexlit.shader),
+ // which will change alphatesting. So later on when real frag shaders come, we need to force disable alpha
+ // testing or enjoy nasty artefacts (like active alpha testing messing up the whole scene).
+ if ( skipAlphaTestFF && m_State.alphaFunc!=kFuncDisabled )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_ALPHATESTENABLE, FALSE ));
+ m_State.alphaFunc = kFuncDisabled;
+ }
+
+ if ( !skipAlphaTestFF )
+ {
+#endif
+ if( mode != m_State.alphaFunc || alphaRef != m_State.alphaValue )
+ {
+ if( mode != kFuncDisabled )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE ));
+ D3D9_CALL(dev->SetRenderState( D3DRS_ALPHAFUNC, kCmpFuncD3D9[mode] ));
+ D3D9_CALL(dev->SetRenderState( D3DRS_ALPHAREF, alphaRef * 255.0f ));
+ }
+ else
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_ALPHATESTENABLE, FALSE ));
+ }
+
+ m_State.alphaFunc = mode;
+ m_State.alphaValue = alphaRef;
+ }
+#if UNITY_EDITOR
+ }
+#endif
+ // TODO: ATI/NVIDIA hacks
+}
+
+
+void GfxDeviceD3D9::SetRasterState(const DeviceRasterState* state)
+{
+ DeviceRasterState* devstate = (DeviceRasterState*)state;
+ if(!devstate)
+ {
+ m_CurrRasterState = NULL;
+ return;
+ }
+
+ m_CurrRasterState = devstate;
+
+ IDirect3DDevice9* dev = GetD3DDeviceNoAssert();
+ CullMode cull = devstate->sourceState.cullMode;
+ D3DCULL d3dcull = kCullModeD3D9[cull];
+ if( d3dcull != m_State.d3dculling )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_CULLMODE, d3dcull ));
+ m_State.culling = cull;
+ m_State.d3dculling = d3dcull;
+ }
+
+ float zFactor = devstate->sourceState.slopeScaledDepthBias;
+ float zUnits = devstate->sourceState.depthBias;
+ if( zFactor != m_State.offsetFactor || zUnits != m_State.offsetUnits )
+ {
+ m_State.offsetFactor = zFactor;
+ m_State.offsetUnits = zUnits;
+
+ // In D3D9 the values are in floating point, with 1 meaning "full depth range".
+ // In theory the offset should depend on depth buffer bit count, and on 24 bit depth buffer a value close to 4.8e-7 should be used
+ // (see Lengyel's GDC2007 "projection matrix tricks").
+ // However, it looks like even on 16 bit depth buffer, a value as-if-24-bit should be used (tested on Radeon HD 3850, GeForce 8600, Intel 945).
+ const double kOneBit = 4.8e-7;
+
+ // It looks like generally we need twice the one bit (PolygonOff2 unit test, on Radeon 3850 and GeForce 8600).
+ // To be somewhat more safer, we make it trhee times the one bit. Still looks quite okay.
+ const float kBiasMultiplier = 3.0 * kOneBit;
+
+ if( gGraphicsCaps.d3d.d3dcaps.RasterCaps & D3DPRASTERCAPS_DEPTHBIAS )
+ {
+ zUnits *= kBiasMultiplier;
+ D3D9_CALL(dev->SetRenderState( D3DRS_DEPTHBIAS, *(DWORD*)&zUnits ));
+ }
+ if( gGraphicsCaps.d3d.d3dcaps.RasterCaps & D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_SLOPESCALEDEPTHBIAS, *(DWORD*)&zFactor ));
+ }
+ }
+}
+
+
+void GfxDeviceD3D9::SetDepthState(const DeviceDepthState* state)
+{
+ IDirect3DDevice9* dev = GetD3DDeviceNoAssert();
+ DeviceDepthStateD3D9* devstate = (DeviceDepthStateD3D9*)state;
+ if (m_CurrDepthState == devstate)
+ return;
+
+ m_CurrDepthState = devstate;
+
+ if (!m_CurrDepthState)
+ return;
+
+ if( devstate->sourceState.depthFunc != m_State.depthFunc )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_ZFUNC, devstate->depthFunc ));
+ m_State.depthFunc = devstate->sourceState.depthFunc;
+ }
+
+ int d3dDepthWriteMode = devstate->sourceState.depthWrite ? TRUE : FALSE;
+ if( d3dDepthWriteMode != m_State.depthWrite )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_ZWRITEENABLE, d3dDepthWriteMode ));
+ m_State.depthWrite = d3dDepthWriteMode;
+ }
+}
+
+void GfxDeviceD3D9::SetStencilState(const DeviceStencilState* state, int stencilRef)
+{
+ if (m_CurrStencilState == state && m_State.m_StencilRef == stencilRef)
+ return;
+ const DeviceStencilStateD3D9* st = static_cast<const DeviceStencilStateD3D9*>(state);
+ m_CurrStencilState = st;
+ if (!m_CurrStencilState)
+ return;
+
+ IDirect3DDevice9* dev = GetD3DDevice();
+ D3D9_CALL (dev->SetRenderState (D3DRS_STENCILENABLE, st->sourceState.stencilEnable));
+ D3D9_CALL (dev->SetRenderState (D3DRS_TWOSIDEDSTENCILMODE, TRUE));
+ D3D9_CALL (dev->SetRenderState (D3DRS_STENCILMASK, st->sourceState.readMask));
+ D3D9_CALL (dev->SetRenderState (D3DRS_STENCILWRITEMASK, st->sourceState.writeMask));
+ D3D9_CALL (dev->SetRenderState (D3DRS_STENCILREF, stencilRef));
+
+ m_State.stencilFunc[0] = st->stencilFuncFront;
+ m_State.stencilFailOp[0] = st->stencilFailOpFront;
+ m_State.depthFailOp[0] = st->depthFailOpFront;
+ m_State.depthPassOp[0] = st->depthPassOpFront;
+ m_State.stencilFunc[1] = st->stencilFuncBack;
+ m_State.stencilFailOp[1] = st->stencilFailOpBack;
+ m_State.depthFailOp[1] = st->depthFailOpBack;
+ m_State.depthPassOp[1] = st->depthPassOpBack;
+ ApplyStencilFuncAndOp(m_State);
+
+ m_State.m_StencilRef = stencilRef;
+}
+
+static void ApplyStencilFuncAndOp (DeviceStateD3D& state)
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ // Normally [0] is front and [1] back stencil state, but when rendering
+ // upside-down, the winding order flips, so flip the state as well.
+ const int cw = state.invertProjMatrix ? 1 : 0;
+ const int ccw = (cw + 1)%2;
+ D3D9_CALL (dev->SetRenderState (D3DRS_STENCILFUNC, state.stencilFunc[cw]));
+ D3D9_CALL (dev->SetRenderState (D3DRS_STENCILFAIL, state.stencilFailOp[cw]));
+ D3D9_CALL (dev->SetRenderState (D3DRS_STENCILZFAIL, state.depthFailOp[cw]));
+ D3D9_CALL (dev->SetRenderState (D3DRS_STENCILPASS, state.depthPassOp[cw]));
+ D3D9_CALL (dev->SetRenderState (D3DRS_CCW_STENCILFUNC, state.stencilFunc[ccw]));
+ D3D9_CALL (dev->SetRenderState (D3DRS_CCW_STENCILFAIL, state.stencilFailOp[ccw]));
+ D3D9_CALL (dev->SetRenderState (D3DRS_CCW_STENCILZFAIL, state.depthFailOp[ccw]));
+ D3D9_CALL (dev->SetRenderState (D3DRS_CCW_STENCILPASS, state.depthPassOp[ccw]));
+}
+
+void GfxDeviceD3D9::SetSRGBWrite (bool enable)
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ D3D9_CALL (dev->SetRenderState (D3DRS_SRGBWRITEENABLE, enable));
+}
+
+bool GfxDeviceD3D9::GetSRGBWrite ()
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ DWORD v;
+ D3D9_CALL (dev->GetRenderState (D3DRS_SRGBWRITEENABLE, &v));
+ return (v==TRUE);
+}
+
+GfxThreadableDevice* CreateD3D9GfxDevice(bool forceREF)
+{
+ if( !InitializeD3D(forceREF ? D3DDEVTYPE_REF : D3DDEVTYPE_HAL) )
+ return NULL;
+
+ #if UNITY_EDITOR
+ if (!CreateHiddenWindowD3D())
+ return NULL;
+ #endif
+
+ gGraphicsCaps.InitD3D9();
+
+ GfxDeviceD3D9* device = UNITY_NEW_AS_ROOT(GfxDeviceD3D9(), kMemGfxDevice, "D3D9GfxDevice", "");
+
+#if UNITY_EDITOR
+ EditorInitializeD3D(device);
+#else
+ ScreenManagerWin& screenMgr = GetScreenManager();
+ HWND window = screenMgr.GetWindow();
+ int width = screenMgr.GetWidth();
+ int height = screenMgr.GetHeight();
+ int dummy;
+ if (!InitializeOrResetD3DDevice(device, window, width, height, 0, false, 0, 0, dummy, dummy, dummy, dummy))
+ {
+ UNITY_DELETE(device, kMemGfxDevice);
+ device = NULL;
+ }
+#endif
+
+ return device;
+}
+
+GfxDeviceD3D9& GetD3D9GfxDevice()
+{
+ GfxDevice& device = GetRealGfxDevice();
+ Assert( device.GetRenderer() == kGfxRendererD3D9 );
+ return static_cast<GfxDeviceD3D9&>(device);
+}
+
+bool IsD3D9DeviceLost()
+{
+ GfxDeviceD3D9& device = static_cast<GfxDeviceD3D9&>( GetRealGfxDevice() );
+ AssertIf( device.GetRenderer() != kGfxRendererD3D9 );
+ return device.GetState().m_DeviceLost;
+}
+
+void SetD3D9DeviceLost( bool lost )
+{
+ GfxDeviceD3D9& device = static_cast<GfxDeviceD3D9&>( GetRealGfxDevice() );
+ AssertIf( device.GetRenderer() != kGfxRendererD3D9 );
+ device.GetState().m_DeviceLost = lost;
+}
+
+
+GfxDeviceD3D9::GfxDeviceD3D9()
+{
+ m_State.m_DeviceLost = false;
+ m_DynamicVBO = NULL;
+
+ m_State.appBackfaceMode = false;
+ m_State.userBackfaceMode = false;
+ m_State.invertProjMatrix = false;
+ m_State.wireframe = false;
+
+ InvalidateState();
+ ResetFrameStats();
+
+ m_Renderer = kGfxRendererD3D9;
+ m_UsesOpenGLTextureCoords = false;
+ m_UsesHalfTexelOffset = true;
+ m_IsThreadable = true;
+
+ m_MaxBufferedFrames = 1; // -1 means no limiting, default is 1
+
+ m_State.viewport[0] = m_State.viewport[1] = m_State.viewport[2] = m_State.viewport[3] = 0;
+ m_State.scissorRect[0] = m_State.scissorRect[1] = m_State.scissorRect[2] = m_State.scissorRect[3] = 0;
+
+ m_CurrBlendState = 0;
+ m_CurrDepthState = 0;
+ m_CurrStencilState = 0;
+ m_CurrRasterState = 0;
+ m_CurrTargetWidth = 0;
+ m_CurrTargetHeight = 0;
+ m_CurrWindowWidth = 0;
+ m_CurrWindowHeight = 0;
+
+ m_AllWhiteVertexStream = NULL;
+
+ extern RenderSurfaceBase* DummyColorBackBuferD3D9();
+ SetBackBufferColorSurface(DummyColorBackBuferD3D9());
+
+ extern RenderSurfaceBase* DummyDepthBackBuferD3D9();
+ SetBackBufferDepthSurface(DummyDepthBackBuferD3D9());
+}
+
+GfxDeviceD3D9::~GfxDeviceD3D9()
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS_WORKER
+ PluginsSetGraphicsDevice (GetD3DDevice(), kGfxRendererD3D9, kGfxDeviceEventShutdown);
+#endif
+
+ D3D9VBO::CleanupSharedIndexBuffer();
+
+ CleanupEventQueries ();
+#if ENABLE_PROFILER
+ m_TimerQueriesD3D9.ReleaseAllQueries();
+#endif
+
+ if( m_DynamicVBO )
+ delete m_DynamicVBO;
+
+ SAFE_RELEASE(m_AllWhiteVertexStream);
+ SAFE_RELEASE(m_Imm.m_ImmVertexDecl);
+ m_VertexDecls.Clear();
+ TextureCombinersD3D::CleanupCombinerCache();
+ CleanupVertexShadersD3D9 ();
+ DestroyD3DDevice();
+
+ #if UNITY_EDITOR
+ DestroyHiddenWindowD3D();
+ #endif
+
+ CleanupD3D();
+}
+
+void GfxDeviceD3D9::InvalidateState()
+{
+ IDirect3DDevice9* dev = GetD3DDeviceNoAssert();
+ if( m_State.m_DeviceLost )
+ dev = NULL;
+
+ ResetVertexPipeStateD3D9 (dev, m_TransformState, m_BuiltinParamValues, m_VertexConfig, m_VertexData, m_VertexPrevious);
+ m_FogParams.Invalidate();
+ m_State.Invalidate(*this);
+ m_Imm.Invalidate();
+ m_VSConstantCache.Invalidate();
+ m_PSConstantCache.Invalidate();
+
+ m_CurrBlendState = NULL;
+ m_CurrDepthState = NULL;
+ m_CurrStencilState = NULL;
+ m_CurrRasterState = NULL;
+}
+
+
+void GfxDeviceD3D9::Clear(UInt32 clearFlags, const float color[4], float depth, int stencil)
+{
+ if( !g_D3DHasDepthStencil )
+ clearFlags &= ~kGfxClearDepthStencil;
+ if (!IsActiveRenderTargetWithColorD3D9())
+ clearFlags &= ~kGfxClearColor;
+
+ DWORD flags = 0;
+ if (clearFlags & kGfxClearColor) flags |= D3DCLEAR_TARGET;
+ if (clearFlags & kGfxClearDepth) flags |= D3DCLEAR_ZBUFFER;
+ if (clearFlags & kGfxClearStencil && GetStencilBitsFromD3DFormat (g_D3DDepthStencilFormat) > 0) {
+ flags |= D3DCLEAR_STENCIL;
+ }
+ GetD3DDevice()->Clear (0, NULL, flags, ColorToD3D(color), depth, stencil);
+}
+
+
+static void ApplyBackfaceMode( DeviceStateD3D& state )
+{
+ if( (state.appBackfaceMode == state.userBackfaceMode) == state.invertProjMatrix )
+ {
+ kCullModeD3D9[kCullFront] = D3DCULL_CCW;
+ kCullModeD3D9[kCullBack] = D3DCULL_CW;
+ }
+ else
+ {
+ kCullModeD3D9[kCullFront] = D3DCULL_CW;
+ kCullModeD3D9[kCullBack] = D3DCULL_CCW;
+ }
+
+ if( state.culling != kCullUnknown )
+ {
+ IDirect3DDevice9* dev = GetD3DDevice();
+ D3DCULL d3dcull = kCullModeD3D9[state.culling];
+ if( d3dcull != state.d3dculling )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_CULLMODE, d3dcull ));
+ state.d3dculling = d3dcull;
+ }
+ }
+}
+
+void GfxDeviceD3D9::SetUserBackfaceMode( bool enable )
+{
+ if( m_State.userBackfaceMode == enable )
+ return;
+ m_State.userBackfaceMode = enable;
+ ApplyBackfaceMode( m_State );
+}
+
+
+void GfxDeviceD3D9::SetWireframe( bool wire )
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ D3D9_CALL(dev->SetRenderState( D3DRS_FILLMODE, wire ? D3DFILL_WIREFRAME : D3DFILL_SOLID ));
+ m_State.wireframe = wire;
+}
+
+bool GfxDeviceD3D9::GetWireframe() const
+{
+ return m_State.wireframe;
+}
+
+
+
+// Even with programmable shaders, some things need fixed function D3DTS_PROJECTION to be set up;
+// most notably fixed function fog (shader model 2.0).
+static void SetFFProjectionMatrixD3D9 (const Matrix4x4f& m)
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ Matrix4x4f projFlip;
+ projFlip.m_Data[ 0] = m.m_Data[ 0];
+ projFlip.m_Data[ 1] = m.m_Data[ 1];
+ projFlip.m_Data[ 2] = m.m_Data[ 2];
+ projFlip.m_Data[ 3] = m.m_Data[ 3];
+ projFlip.m_Data[ 4] = m.m_Data[ 4];
+ projFlip.m_Data[ 5] = m.m_Data[ 5];
+ projFlip.m_Data[ 6] = m.m_Data[ 6];
+ projFlip.m_Data[ 7] = m.m_Data[ 7];
+ projFlip.m_Data[ 8] = -m.m_Data[ 8];
+ projFlip.m_Data[ 9] = -m.m_Data[ 9];
+ projFlip.m_Data[10] = -m.m_Data[10];
+ projFlip.m_Data[11] = -m.m_Data[11];
+ projFlip.m_Data[12] = m.m_Data[12];
+ projFlip.m_Data[13] = m.m_Data[13];
+ projFlip.m_Data[14] = m.m_Data[14];
+ projFlip.m_Data[15] = m.m_Data[15];
+ D3D9_CALL(dev->SetTransform (D3DTS_PROJECTION, (const D3DMATRIX*)projFlip.GetPtr()));
+}
+
+
+void GfxDeviceD3D9::SetInvertProjectionMatrix( bool enable )
+{
+ if( m_State.invertProjMatrix == enable )
+ return;
+
+ m_State.invertProjMatrix = enable;
+ ApplyBackfaceMode( m_State );
+ ApplyStencilFuncAndOp( m_State );
+
+ // When setting up "invert" flag, invert the matrix as well.
+ Matrix4x4f& m = m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatProj);
+ m.Get(1,1) = -m.Get(1,1);
+ m.Get(1,3) = -m.Get(1,3);
+ m_TransformState.dirtyFlags |= TransformState::kProjDirty;
+ SetFFProjectionMatrixD3D9 (m);
+}
+
+bool GfxDeviceD3D9::GetInvertProjectionMatrix() const
+{
+ return m_State.invertProjMatrix;
+}
+
+void GfxDeviceD3D9::SetWorldMatrix( const float matrix[16] )
+{
+ CopyMatrix (matrix, m_TransformState.worldMatrix.GetPtr());
+ m_TransformState.dirtyFlags |= TransformState::kWorldDirty;
+}
+
+void GfxDeviceD3D9::SetViewMatrix( const float matrix[16] )
+{
+ m_TransformState.SetViewMatrix (matrix, m_BuiltinParamValues);
+}
+
+void GfxDeviceD3D9::SetProjectionMatrix(const Matrix4x4f& matrix)
+{
+ Matrix4x4f& m = m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatProj);
+ CopyMatrix (matrix.GetPtr(), m.GetPtr());
+ CopyMatrix (matrix.GetPtr(), m_TransformState.projectionMatrixOriginal.GetPtr());
+
+ CalculateDeviceProjectionMatrix (m, m_UsesOpenGLTextureCoords, m_State.invertProjMatrix);
+ SetFFProjectionMatrixD3D9 (m);
+
+ m_TransformState.dirtyFlags |= TransformState::kProjDirty;
+}
+
+
+void GfxDeviceD3D9::GetMatrix(float outMatrix[16]) const
+{
+ m_TransformState.UpdateWorldViewMatrix (m_BuiltinParamValues);
+ CopyMatrix (m_TransformState.worldViewMatrix.GetPtr(), outMatrix);
+}
+
+const float* GfxDeviceD3D9::GetWorldMatrix() const
+{
+ return m_TransformState.worldMatrix.GetPtr();
+}
+
+const float* GfxDeviceD3D9::GetViewMatrix() const
+{
+ return m_BuiltinParamValues.GetMatrixParam(kShaderMatView).GetPtr();
+}
+
+const float* GfxDeviceD3D9::GetProjectionMatrix() const
+{
+ return m_TransformState.projectionMatrixOriginal.GetPtr();
+}
+
+const float* GfxDeviceD3D9::GetDeviceProjectionMatrix() const
+{
+ return m_BuiltinParamValues.GetMatrixParam(kShaderMatProj).GetPtr();
+}
+
+void GfxDeviceD3D9::SetNormalizationBackface( NormalizationMode mode, bool backface )
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ if( mode != m_VertexData.normalization )
+ {
+ m_VertexData.normalization = mode;
+ m_VertexConfig.hasNormalization = (mode == kNormalizationFull);
+ }
+ if( m_State.appBackfaceMode != backface )
+ {
+ m_State.appBackfaceMode = backface;
+ ApplyBackfaceMode( m_State );
+ }
+}
+
+void GfxDeviceD3D9::SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial )
+{
+ m_VertexConfig.hasLighting = on ? 1 : 0;
+ m_VertexConfig.hasSpecular = separateSpecular ? 1 : 0;
+ DebugAssertIf(colorMaterial==kColorMatUnknown);
+ m_VertexConfig.colorMaterial = colorMaterial;
+}
+
+void GfxDeviceD3D9::SetMaterial( const float ambient[4], const float diffuse[4], const float specular[4], const float emissive[4], const float shininess )
+{
+ D3DMATERIAL9& mat = m_VertexData.material;
+ mat.Ambient = *(D3DCOLORVALUE*)ambient;
+ mat.Diffuse = *(D3DCOLORVALUE*)diffuse;
+ mat.Specular = *(D3DCOLORVALUE*)specular;
+ mat.Emissive = *(D3DCOLORVALUE*)emissive;
+ mat.Power = std::max<float>( std::min<float>(shininess,1.0f), 0.0f) * 128.0f;
+}
+
+
+void GfxDeviceD3D9::SetColor( const float color[4] )
+{
+ // If we have pixel shader set up, do nothing; fixed function
+ // constant color can't be possibly used there
+ if (m_State.activeShader[kShaderFragment] != 0) // inlined IsShaderActive(kShaderFragment)
+ return;
+
+ // There's no really good place to make a glColor equivalent, put it into
+ // TFACTOR... Additionally put that into c4 register for ps_1_1 combiner emulation
+ IDirect3DDevice9* dev = GetD3DDevice();
+ D3D9_CALL(dev->SetRenderState( D3DRS_TEXTUREFACTOR, ColorToD3D(color) ));
+ m_PSConstantCache.SetValues( kMaxD3DTextureStagesForPS, color, 1 );
+}
+
+
+void GfxDeviceD3D9::SetViewport( int x, int y, int width, int height )
+{
+ m_State.viewport[0] = x;
+ m_State.viewport[1] = y;
+ m_State.viewport[2] = width;
+ m_State.viewport[3] = height;
+
+ IDirect3DDevice9* dev = GetD3DDeviceNoAssert();
+ if( !dev ) // happens on startup, when deleting all render textures
+ return;
+ D3DVIEWPORT9 view;
+ view.X = x;
+ view.Y = y;
+ view.Width = width;
+ view.Height = height;
+ view.MinZ = 0.0f;
+ view.MaxZ = 1.0f;
+ dev->SetViewport( &view );
+}
+
+void GfxDeviceD3D9::GetViewport( int* port ) const
+{
+ port[0] = m_State.viewport[0];
+ port[1] = m_State.viewport[1];
+ port[2] = m_State.viewport[2];
+ port[3] = m_State.viewport[3];
+}
+
+
+void GfxDeviceD3D9::SetScissorRect( int x, int y, int width, int height )
+{
+ if (m_State.scissor != 1)
+ {
+ if (gGraphicsCaps.d3d.d3dcaps.RasterCaps & D3DPRASTERCAPS_SCISSORTEST )
+ {
+ GetD3DDevice()->SetRenderState( D3DRS_SCISSORTESTENABLE, TRUE );
+ }
+ m_State.scissor = 1;
+ }
+
+
+ m_State.scissorRect[0] = x;
+ m_State.scissorRect[1] = y;
+ m_State.scissorRect[2] = width;
+ m_State.scissorRect[3] = height;
+
+ RECT rc;
+ rc.left = x;
+ rc.top = y;
+ rc.right = x + width;
+ rc.bottom = y + height;
+ GetD3DDevice()->SetScissorRect( &rc );
+
+}
+void GfxDeviceD3D9::DisableScissor()
+{
+ if (m_State.scissor != 0)
+ {
+ if( gGraphicsCaps.d3d.d3dcaps.RasterCaps & D3DPRASTERCAPS_SCISSORTEST )
+ {
+ GetD3DDevice()->SetRenderState( D3DRS_SCISSORTESTENABLE, FALSE);
+ }
+ m_State.scissor = 0;
+ }
+}
+bool GfxDeviceD3D9::IsScissorEnabled() const
+{
+ return m_State.scissor == 1;
+}
+
+void GfxDeviceD3D9::GetScissorRect( int scissor[4] ) const
+{
+ scissor[0] = m_State.scissorRect[0];
+ scissor[1] = m_State.scissorRect[1];
+ scissor[2] = m_State.scissorRect[2];
+ scissor[3] = m_State.scissorRect[3];
+}
+
+bool GfxDeviceD3D9::IsCombineModeSupported( unsigned int combiner )
+{
+ return true;
+}
+
+TextureCombinersHandle GfxDeviceD3D9::CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular )
+{
+ TextureCombinersD3D* implD3D = TextureCombinersD3D::Create( count, texEnvs, props, hasVertexColorOrLighting, usesAddSpecular );
+ return TextureCombinersHandle( implD3D );
+}
+
+void GfxDeviceD3D9::DeleteTextureCombiners( TextureCombinersHandle& textureCombiners )
+{
+ TextureCombinersD3D* implD3D = OBJECT_FROM_HANDLE(textureCombiners, TextureCombinersD3D);
+ delete implD3D;
+ textureCombiners.Reset();
+}
+
+void GfxDeviceD3D9::SetTextureCombinersThreadable( TextureCombinersHandle textureCombiners, const TexEnvData* texEnvData, const Vector4f* texColors )
+{
+ TextureCombinersD3D* implD3D = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersD3D);
+ AssertIf( !implD3D );
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ AssertIf (IsShaderActive( kShaderFragment ));
+
+ const int maxTexUnits = gGraphicsCaps.maxTexUnits; // fetch here once
+
+ // set textures
+ int i = 0;
+ for( ; i < maxTexUnits && i < implD3D->envCount; ++i )
+ {
+ ApplyTexEnvData (i, i, texEnvData[i]);
+ }
+
+ // clear unused textures
+ for (; i < maxTexUnits; ++i)
+ {
+ if (i < kMaxSupportedTextureCoords)
+ m_VertexConfig.ClearTextureUnit(i);
+
+ TextureUnitStateD3D& currTex = m_State.texturesPS[i];
+ if (currTex.texID.m_ID != 0)
+ {
+ D3D9_CALL(dev->SetTexture( GetD3D9SamplerIndex(kShaderFragment,i), NULL ));
+ currTex.texID.m_ID = 0;
+ }
+ }
+
+ // setup texture stages
+ if( implD3D->pixelShader )
+ {
+ for( i = 0; i < implD3D->stageCount; ++i )
+ {
+ const ShaderLab::TextureBinding& binding = implD3D->texEnvs[i];
+ const Vector4f& texcolorVal = texColors[i];
+ m_PSConstantCache.SetValues( i, texcolorVal.GetPtr(), 1 );
+ }
+ if( m_State.fixedFunctionPS != implD3D->uniqueID )
+ {
+ D3D9_CALL(dev->SetPixelShader( implD3D->pixelShader ));
+ m_State.fixedFunctionPS = implD3D->uniqueID;
+ }
+ }
+ else
+ {
+ if( implD3D->textureFactorIndex != -1 )
+ {
+ const Vector4f& color = texColors[implD3D->textureFactorIndex];
+ D3D9_CALL(dev->SetRenderState( D3DRS_TEXTUREFACTOR, ColorToD3D( color.GetPtr() ) ));
+ }
+ for( i = 0; i < implD3D->stageCount; ++i )
+ {
+ // TODO: cache!
+ const D3DTextureStage& stage = implD3D->stages[i];
+ AssertIf( stage.colorOp == D3DTOP_DISABLE || stage.alphaOp == D3DTOP_DISABLE );
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_COLOROP, stage.colorOp ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_COLORARG1, stage.colorArgs[0] ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_COLORARG2, stage.colorArgs[1] ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_COLORARG0, stage.colorArgs[2] ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_ALPHAOP, stage.alphaOp ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_ALPHAARG1, stage.alphaArgs[0] ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_ALPHAARG2, stage.alphaArgs[1] ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_ALPHAARG0, stage.alphaArgs[2] ));
+ }
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_COLOROP, D3DTOP_DISABLE ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_ALPHAOP, D3DTOP_DISABLE ));
+ D3D9_CALL(dev->SetPixelShader( NULL ));
+ m_State.fixedFunctionPS = 0;
+ }
+}
+
+
+void GfxDeviceD3D9::SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props )
+{
+ TextureCombinersD3D* implD3D = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersD3D);
+ AssertIf( !implD3D );
+
+ int count = std::min(implD3D->envCount, gGraphicsCaps.maxTexUnits);
+
+ // Fill in arrays
+ TexEnvData* texEnvData;
+ ALLOC_TEMP (texEnvData, TexEnvData, count);
+ for( int i = 0; i < count; ++i )
+ {
+ ShaderLab::TexEnv *te = ShaderLab::GetTexEnvForBinding( implD3D->texEnvs[i], props );
+ Assert( te != NULL );
+ te->PrepareData (implD3D->texEnvs[i].m_TextureName.index, implD3D->texEnvs[i].m_MatrixName, props, &texEnvData[i]);
+ }
+
+ Vector4f* texColors;
+ ALLOC_TEMP (texColors, Vector4f, implD3D->envCount);
+ for( int i = 0; i < implD3D->envCount; ++i )
+ {
+ const ShaderLab::TextureBinding& binding = implD3D->texEnvs[i];
+ texColors[i] = binding.GetTexColor().Get (props);
+ }
+ GfxDeviceD3D9::SetTextureCombinersThreadable(textureCombiners, texEnvData, texColors);
+}
+
+
+void GfxDeviceD3D9::SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias)
+{
+ DebugAssertIf( dim < kTexDim2D || dim > kTexDimCUBE );
+ DebugAssertIf (unit < 0 || unit >= kMaxSupportedTextureUnits);
+
+ if (unit < kMaxSupportedTextureCoords)
+ m_VertexConfig.SetTextureUnit(unit);
+
+ TextureUnitStateD3D* currTex = NULL;
+ if (shaderType == kShaderFragment)
+ currTex = &m_State.texturesPS[unit];
+ else if (shaderType == kShaderVertex)
+ currTex = &m_State.texturesVS[unit];
+ else
+ {
+ AssertString ("Unsupported shader type for SetTexture");
+ return;
+ }
+
+ if (texture != currTex->texID)
+ {
+ if (m_Textures.SetTexture (shaderType, unit, texture))
+ currTex->texID = texture;
+ }
+ m_Stats.AddUsedTexture(texture);
+ if (gGraphicsCaps.hasMipLevelBias && bias != currTex->bias && shaderType == kShaderFragment)
+ {
+ D3D9_CALL(GetD3DDevice()->SetSamplerState( unit, D3DSAMP_MIPMAPLODBIAS, *(DWORD*)&bias ));
+ currTex->bias = bias;
+ }
+}
+
+
+
+void GfxDeviceD3D9::SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16] )
+{
+ Assert (unit >= 0 && unit < kMaxSupportedTextureCoords);
+
+ m_State.m_NeedsSofwareVPFlags &= ~kNeedsSoftwareVPTexGen;
+
+ // -------- texture matrix
+
+ float* mat = m_TransformState.texMatrices[unit].GetPtr();
+ CopyMatrix( matrix, mat );
+
+ // In OpenGL all texture reads are projective, and matrices are always 4x4, and z/w defaults to 0/1.
+ // In D3D everything is different. So here we try to figure out how many components need to be transformed,
+ // munge the matrix and enable projective texturing if needed.
+
+ TextureMatrixMode matrixMode;
+ int projectedTexture = 0;
+ if( identity )
+ {
+ // matrix guaranteed to be identity: disable transformation
+ matrixMode = kTexMatrixNone;
+ }
+ else if( dim == kTexDimCUBE || dim == kTexDim3D )
+ {
+ // for cube/volume texture: count3
+ matrixMode = kTexMatrix3;
+ }
+ else
+ {
+ // detect projected matrix
+ projectedTexture = (mat[3] != 0.0f || mat[7] != 0.0f || mat[11] != 0.0f || mat[15] != 1.0f) ? 1 : 0;
+ // Cards that do support projected textures or cubemaps seem to want
+ // Count3 flags for object/eyelinear transforms. Cards that don't support
+ // projection nor cubemaps will have to use Count2 - fixes GUI text rendering!
+ bool is3DTexGen = (texGen != kTexGenDisabled && texGen != kTexGenSphereMap);
+
+ if( projectedTexture )
+ {
+ matrixMode = kTexMatrix4;
+ }
+ else if( is3DTexGen )
+ {
+ matrixMode = kTexMatrix3;
+ }
+ else
+ {
+ // regular texture: count2, and move matrix' 4th row into 3rd one
+ matrixMode = kTexMatrix2;
+ mat[ 8] = mat[12];
+ mat[ 9] = mat[13];
+ mat[10] = mat[14];
+ mat[11] = mat[15];
+ }
+ }
+
+ m_VertexConfig.textureMatrixModes = m_VertexConfig.textureMatrixModes & ~(3<<(unit*2)) | (matrixMode<<(unit*2));
+ m_VertexData.projectedTextures = m_VertexData.projectedTextures & ~(1<<unit) | (projectedTexture<<unit);
+
+ // -------- texture coordinate generation
+
+ TextureSourceMode texSource = texGen == kTexGenDisabled ? kTexSourceUV0 : static_cast<TextureSourceMode>(texGen + 1);
+ m_VertexConfig.textureSources = m_VertexConfig.textureSources & ~(7<<(unit*3)) | (texSource<<(unit*3));
+
+ if( texGen == kTexGenSphereMap && !IsShaderActive(kShaderVertex) )
+ {
+ if( g_D3DUsesMixedVP && !(gGraphicsCaps.d3d.d3dcaps.VertexProcessingCaps & D3DVTXPCAPS_TEXGEN_SPHEREMAP) )
+ m_State.m_NeedsSofwareVPFlags |= kNeedsSoftwareVPTexGen;
+ }
+}
+
+void GfxDeviceD3D9::SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace )
+{
+ m_Textures.SetTextureParams( texture, texDim, filter, wrap, anisoLevel, hasMipMap, colorSpace );
+
+ // we'll need to set texture sampler states, so invalidate current texture cache
+ // invalidate texture unit states that used this texture
+ for (int i = 0; i < ARRAY_SIZE(m_State.texturesPS); ++i)
+ {
+ TextureUnitStateD3D& currTex = m_State.texturesPS[i];
+ if( currTex.texID == texture )
+ currTex.Invalidate();
+ }
+ for (int i = 0; i < ARRAY_SIZE(m_State.texturesVS); ++i)
+ {
+ TextureUnitStateD3D& currTex = m_State.texturesVS[i];
+ if (currTex.texID == texture)
+ currTex.Invalidate();
+ }
+}
+
+
+void GfxDeviceD3D9::SetShadersThreadable( GpuProgram* programs[kShaderTypeCount], const GpuProgramParameters* params[kShaderTypeCount], UInt8 const * const paramsBuffer[kShaderTypeCount])
+{
+ GpuProgram* vertexProgram = programs[kShaderVertex];
+ GpuProgram* fragmentProgram = programs[kShaderFragment];
+
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ // vertex shader
+ if( vertexProgram && vertexProgram->GetImplType() == kShaderImplVertex )
+ {
+ // set the shader
+ bool resetToNoFog = false;
+ IDirect3DVertexShader9* shader = static_cast<D3D9VertexShader&>(*vertexProgram).GetShader(m_FogParams.mode, resetToNoFog);
+ // Note: get pixel shader to match actually used fog mode from VS. If VS was too complex
+ // to patch for fog, for example, then we want PS to not have fog as well.
+ if (resetToNoFog)
+ m_FogParams.mode = kFogDisabled;
+ DebugAssert (shader);
+
+ if( m_State.activeShader[kShaderVertex] != shader )
+ {
+ D3D9_CALL(dev->SetVertexShader( shader ));
+ if (m_State.activeShader[kShaderVertex] == NULL)
+ {
+ for( int i = 0; i < kMaxSupportedTextureCoords; ++i )
+ {
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_TEXCOORDINDEX, i ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTSS_TCI_PASSTHRU ));
+ }
+ }
+
+ m_VertexPrevious.vertexShader = NULL;
+ m_VertexPrevious.ambient.set(-1,-1,-1,-1);
+
+ m_State.activeShader[kShaderVertex] = shader;
+ }
+
+ if( g_D3DUsesMixedVP )
+ m_State.m_NeedsSofwareVPFlags |= kNeedsSoftwareVPVertexShader;
+
+ m_BuiltinParamIndices[kShaderVertex] = &params[kShaderVertex]->GetBuiltinParams();
+ }
+ else
+ {
+ // clear the shader
+ DebugAssertIf( vertexProgram != 0 );
+ if( m_State.activeShader[kShaderVertex] != 0 )
+ {
+ D3D9_CALL(dev->SetVertexShader( NULL ));
+ m_State.activeShader[kShaderVertex] = 0;
+ }
+
+ if( g_D3DUsesMixedVP )
+ m_State.m_NeedsSofwareVPFlags &= ~kNeedsSoftwareVPVertexShader;
+
+ m_BuiltinParamIndices[kShaderVertex] = &m_NullParamIndices;
+ }
+
+ // pixel shader
+ if( fragmentProgram && fragmentProgram->GetImplType() == kShaderImplFragment )
+ {
+ // set the shader
+ IDirect3DPixelShader9* shader = static_cast<D3D9PixelShader&>(*fragmentProgram).GetShader(m_FogParams.mode, *params[kShaderFragment]);
+ DebugAssert (shader);
+
+ if( m_State.activeShader[kShaderFragment] != shader )
+ {
+ D3D9_CALL(dev->SetPixelShader( shader ));
+ m_State.activeShader[kShaderFragment] = shader;
+ m_State.fixedFunctionPS = 0;
+ }
+
+ m_BuiltinParamIndices[kShaderFragment] = &params[kShaderFragment]->GetBuiltinParams();
+ }
+ else
+ {
+ // clear the shader
+ DebugAssertIf( fragmentProgram != 0 );
+ if( m_State.activeShader[kShaderFragment] != 0 )
+ {
+ D3D9_CALL(dev->SetPixelShader( NULL ));
+ m_State.activeShader[kShaderFragment] = 0;
+ m_State.fixedFunctionPS = 0;
+ }
+
+ m_BuiltinParamIndices[kShaderFragment] = &m_NullParamIndices;
+ }
+
+ for (int pt = 0; pt < kShaderTypeCount; ++pt)
+ {
+ if (programs[pt])
+ {
+ m_State.activeGpuProgramParams[pt] = params[pt];
+ m_State.activeGpuProgram[pt] = programs[pt];
+ programs[pt]->ApplyGpuProgram (*params[pt], paramsBuffer[pt]);
+ }
+ else
+ {
+ m_State.activeGpuProgramParams[pt] = NULL;
+ m_State.activeGpuProgram[pt] = NULL;
+ }
+ }
+}
+
+
+bool GfxDeviceD3D9::IsShaderActive( ShaderType type ) const
+{
+ return m_State.activeShader[type] != 0;
+}
+
+void GfxDeviceD3D9::DestroySubProgram( ShaderLab::SubProgram* subprogram )
+{
+ GpuProgram* program = &subprogram->GetGpuProgram();
+ if (program->GetImplType() == kShaderImplVertex)
+ {
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ IUnknown* shader = static_cast<D3D9VertexShader*>(program)->GetShaderAtFogIndex(static_cast<FogMode>(i));
+ if (m_State.activeShader[kShaderVertex] == shader)
+ m_State.activeShader[kShaderVertex] = NULL;
+ }
+ }
+ else if (program->GetImplType() == kShaderImplFragment)
+ {
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ IUnknown* shader = static_cast<D3D9PixelShader*>(program)->GetShaderAtFogIndex(static_cast<FogMode>(i));
+ if (m_State.activeShader[kShaderFragment] == shader)
+ m_State.activeShader[kShaderFragment] = NULL;
+ }
+ }
+ delete subprogram;
+}
+
+void GfxDeviceD3D9::DisableLights( int startLight )
+{
+ m_VertexData.vertexLightCount = startLight;
+
+ const Vector4f black(0.0F, 0.0F, 0.0F, 0.0F);
+ for (int i = startLight; i < gGraphicsCaps.maxLights; ++i)
+ {
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Diffuse + i), black);
+ }
+}
+
+void GfxDeviceD3D9::SetLight( int light, const GfxVertexLight& data)
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ DebugAssert(light >= 0 && light < kMaxSupportedVertexLights);
+
+ DebugAssertIf( (data.position.w == 0.0f) != (data.type == kLightDirectional) ); // directional lights should have 0 in position.w
+ DebugAssertIf( (data.spotAngle != -1.0f) != (data.type == kLightSpot) ); // non-spot lights should have -1 in spot angle
+
+ GfxVertexLight& dest = m_VertexData.lights[light];
+ dest = data;
+
+ const Matrix4x4f& viewMat = m_BuiltinParamValues.GetMatrixParam(kShaderMatView);
+
+ if (data.type == kLightDirectional)
+ {
+ dest.position.Set(0.0f,0.0f,0.0f,0.0f);
+ Vector3f v = viewMat.MultiplyVector3((const Vector3f&)data.position);
+ dest.spotDirection.Set( v.x, v.y, v.z, 0.0f );
+ }
+ else
+ {
+ Vector3f v = viewMat.MultiplyPoint3((const Vector3f&)data.position);
+ dest.position.Set( v.x, v.y, v.z, 1.0f );
+ Vector3f d = viewMat.MultiplyVector3((const Vector3f&)data.spotDirection);
+ dest.spotDirection.Set( d.x, d.y, d.z, 0.0f );
+ }
+
+ SetupVertexLightParams (light, data);
+}
+
+void GfxDeviceD3D9::SetAmbient( const float ambient[4] )
+{
+ if( m_VertexData.ambient != ambient )
+ {
+ m_VertexData.ambient.set( ambient );
+ m_VertexData.ambientClamped.set( clamp01(ambient[0]), clamp01(ambient[1]), clamp01(ambient[2]), clamp01(ambient[3]) );
+ m_BuiltinParamValues.SetVectorParam(kShaderVecLightModelAmbient, Vector4f(ambient));
+ }
+}
+
+
+static D3DFOGMODE s_D3DFogModes[kFogModeCount] = { D3DFOG_NONE, D3DFOG_LINEAR, D3DFOG_EXP, D3DFOG_EXP2 };
+
+void GfxDeviceD3D9::EnableFog(const GfxFogParams& fog)
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ DebugAssertIf( fog.mode <= kFogDisabled );
+ if( m_FogParams.mode != fog.mode )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_FOGTABLEMODE, s_D3DFogModes[fog.mode] )); // TODO: or maybe vertex fog?
+ D3D9_CALL(dev->SetRenderState( D3DRS_FOGENABLE, TRUE ));
+ m_FogParams.mode = fog.mode;
+ }
+ if( m_FogParams.start != fog.start )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_FOGSTART, *(DWORD*)&fog.start ));
+ m_FogParams.start = fog.start;
+ }
+ if( m_FogParams.end != fog.end )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_FOGEND, *(DWORD*)&fog.end ));
+ m_FogParams.end = fog.end;
+ }
+ if( m_FogParams.density != fog.density )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_FOGDENSITY, *(DWORD*)&fog.density ));
+ m_FogParams.density = fog.density;
+ }
+ if( m_FogParams.color != fog.color )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_FOGCOLOR, ColorToD3D(fog.color.GetPtr()) ));
+ m_FogParams.color = fog.color;
+ }
+}
+
+void GfxDeviceD3D9::DisableFog()
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ if( m_FogParams.mode != kFogDisabled )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_FOGENABLE, FALSE ));
+ m_FogParams.mode = kFogDisabled;
+ }
+}
+
+VBO* GfxDeviceD3D9::CreateVBO()
+{
+ VBO* vbo = new D3D9VBO();
+ OnCreateVBO(vbo);
+ return vbo;
+}
+
+void GfxDeviceD3D9::DeleteVBO( VBO* vbo )
+{
+ OnDeleteVBO(vbo);
+ delete vbo;
+}
+
+DynamicVBO& GfxDeviceD3D9::GetDynamicVBO()
+{
+ if( !m_DynamicVBO ) {
+ m_DynamicVBO = new DynamicD3D9VBO( 1024 * 1024, 65536 ); // initial 1 MiB VB, 64 KiB IB
+ }
+ return *m_DynamicVBO;
+}
+
+IDirect3DVertexBuffer9* GfxDeviceD3D9::GetAllWhiteVertexStream()
+{
+ if( !m_AllWhiteVertexStream )
+ {
+ int maxVerts = 0x10000;
+ int size = maxVerts * sizeof(D3DCOLOR);
+ HRESULT hr = GetD3DDevice()->CreateVertexBuffer( size, D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &m_AllWhiteVertexStream, NULL );
+ if( !SUCCEEDED(hr) )
+ return NULL;
+ void* buffer;
+ hr = m_AllWhiteVertexStream->Lock( 0 , 0, &buffer, 0 );
+ if( !SUCCEEDED(hr) )
+ {
+ SAFE_RELEASE( m_AllWhiteVertexStream );
+ return NULL;
+ }
+ D3DCOLOR* dest = (D3DCOLOR*)buffer;
+ for( int i = 0; i < maxVerts; i++ )
+ dest[i] = D3DCOLOR_ARGB(255, 255, 255, 255);
+ m_AllWhiteVertexStream->Unlock();
+ }
+ return m_AllWhiteVertexStream;
+}
+
+void GfxDeviceD3D9::ResetDynamicResources()
+{
+ delete m_DynamicVBO;
+ m_DynamicVBO = NULL;
+
+ CleanupEventQueries ();
+ ResetDynamicVBs ();
+
+ #if ENABLE_PROFILER
+ m_TimerQueriesD3D9.ReleaseAllQueries();
+ #endif
+
+ D3D9VBO::CleanupSharedIndexBuffer();
+}
+
+
+void ResetDynamicResourcesD3D9()
+{
+ AutoGfxDeviceAcquireThreadOwnership autoOwner;
+ GetD3D9GfxDevice().ResetDynamicResources();
+}
+
+IDirect3DVertexDeclaration9* GetD3DVertexDeclaration( UInt32 shaderChannelsMap )
+{
+ ChannelInfoArray channels;
+ int offset = 0;
+ for (int i = 0; i < kShaderChannelCount; i++)
+ {
+ ChannelInfo& info = channels[i];
+ if (shaderChannelsMap & (1 << i))
+ {
+ info.stream = 0;
+ info.offset = offset;
+ info.format = VBO::GetDefaultChannelFormat( i );
+ info.dimension = VBO::GetDefaultChannelDimension( i );
+ offset += VBO::GetDefaultChannelByteSize( i );
+}
+ else
+ info.Reset();
+ }
+ return GetD3D9GfxDevice().GetVertexDecls().GetVertexDecl( channels );
+}
+
+VertexShaderConstantCache& GetD3D9VertexShaderConstantCache()
+{
+ return GetD3D9GfxDevice().GetVertexShaderConstantCache();
+}
+
+PixelShaderConstantCache& GetD3D9PixelShaderConstantCache()
+{
+ return GetD3D9GfxDevice().GetPixelShaderConstantCache();
+}
+
+
+// ---------- render textures
+
+RenderSurfaceHandle GfxDeviceD3D9::CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags)
+{
+ return CreateRenderColorSurfaceD3D9 (textureID, width, height, samples, dim, createFlags, format, m_Textures);
+}
+RenderSurfaceHandle GfxDeviceD3D9::CreateRenderDepthSurface(TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags)
+{
+ return CreateRenderDepthSurfaceD3D9 (textureID, width, height, samples, depthFormat, createFlags, m_Textures);
+}
+void GfxDeviceD3D9::DestroyRenderSurface(RenderSurfaceHandle& rs)
+{
+ DestroyRenderSurfaceD3D9( rs, m_Textures );
+}
+void GfxDeviceD3D9::SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face)
+{
+ bool isBackBuffer;
+ m_CurrTargetWidth = m_CurrWindowWidth;
+ m_CurrTargetHeight = m_CurrWindowHeight;
+ if (SetRenderTargetD3D9 (count, colorHandles, depthHandle, mipLevel, face, m_CurrTargetWidth, m_CurrTargetHeight, isBackBuffer))
+ {
+ // changing render target might mean different color clear flags; so reset current state
+ m_CurrBlendState = NULL;
+ }
+}
+void GfxDeviceD3D9::ResolveDepthIntoTexture (RenderSurfaceHandle colorHandle, RenderSurfaceHandle depthHandle)
+{
+ Assert (gGraphicsCaps.d3d.hasDepthResolveRESZ);
+
+ RenderSurfaceD3D9* depthSurf = reinterpret_cast<RenderSurfaceD3D9*>(depthHandle.object);
+
+ IDirect3DDevice9* dev = GetD3DDevice();
+ // Important: change point size render state to something else than RESZ
+ // before the dummy draw call; otherwise RESZ state set will be filtered out
+ // by non-PURE D3D device.
+ dev->SetRenderState (D3DRS_POINTSIZE, 0);
+
+ // Bind destination as texture
+ SetTexture (kShaderFragment, 0, 0, depthSurf->textureID, kTexDim2D, 0.0f);
+
+ // Dummy draw call
+ float dummy[3] = {0,0,0};
+ dev->DrawPrimitiveUP (D3DPT_POINTLIST, 1, dummy, 12);
+
+ // RESZ to trigger depth buffer copy
+ dev->SetRenderState (D3DRS_POINTSIZE, 0x7fa05000);
+}
+
+
+void GfxDeviceD3D9::ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle)
+{
+ Assert (srcHandle.IsValid());
+ Assert (dstHandle.IsValid());
+ RenderColorSurfaceD3D9* src = reinterpret_cast<RenderColorSurfaceD3D9*>(srcHandle.object);
+ RenderColorSurfaceD3D9* dst = reinterpret_cast<RenderColorSurfaceD3D9*>(dstHandle.object);
+ if (!src->colorSurface || !dst->colorSurface)
+ {
+ WarningString("RenderTexture: Resolving non-color surfaces.");
+ return;
+ }
+ if (!src->m_Surface || !dst->m_Surface)
+ {
+ WarningString("RenderTexture: Resolving NULL surfaces.");
+ return;
+ }
+ if (src->dim != dst->dim)
+ {
+ WarningString("RenderTexture: Resolving surfaces of different types.");
+ return;
+ }
+ if (src->format != dst->format)
+ {
+ WarningString("RenderTexture: Resolving surfaces of different formats.");
+ return;
+ }
+ if (src->width != dst->width || src->height != dst->height)
+ {
+ WarningString("RenderTexture: Resolving surfaces of different sizes.");
+ return;
+ }
+
+ IDirect3DDevice9* dev = GetD3DDevice();
+ dev->StretchRect (src->m_Surface, NULL, dst->m_Surface, NULL, D3DTEXF_NONE);
+}
+
+RenderSurfaceHandle GfxDeviceD3D9::GetActiveRenderColorSurface (int index)
+{
+ return GetActiveRenderColorSurfaceD3D9(index);
+}
+RenderSurfaceHandle GfxDeviceD3D9::GetActiveRenderDepthSurface()
+{
+ return GetActiveRenderDepthSurfaceD3D9();
+}
+void GfxDeviceD3D9::SetSurfaceFlags (RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags)
+{
+}
+
+
+// ---------- uploading textures
+
+void GfxDeviceD3D9::UploadTexture2D( TextureID texture, TextureDimension dimension, UInt8* srcData, int srcSize, int width, int height, TextureFormat format, int mipCount, UInt32 uploadFlags, int skipMipLevels, TextureUsageMode usageMode, TextureColorSpace colorSpace )
+{
+ m_Textures.UploadTexture2D( texture, dimension, srcData, width, height, format, mipCount, uploadFlags, skipMipLevels, usageMode, colorSpace );
+}
+void GfxDeviceD3D9::UploadTextureSubData2D( TextureID texture, UInt8* srcData, int srcSize, int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace )
+{
+ m_Textures.UploadTextureSubData2D( texture, srcData, mipLevel, x, y, width, height, format, colorSpace );
+}
+void GfxDeviceD3D9::UploadTextureCube( TextureID texture, UInt8* srcData, int srcSize, int faceDataSize, int size, TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace )
+{
+ m_Textures.UploadTextureCube( texture, srcData, faceDataSize, size, format, mipCount, uploadFlags, colorSpace );
+}
+void GfxDeviceD3D9::UploadTexture3D( TextureID texture, UInt8* srcData, int srcSize, int width, int height, int depth, TextureFormat format, int mipCount, UInt32 uploadFlags )
+{
+ m_Textures.UploadTexture3D( texture, srcData, width, height, depth, format, mipCount, uploadFlags );
+}
+
+void GfxDeviceD3D9::DeleteTexture( TextureID texture )
+{
+ m_Textures.DeleteTexture( texture );
+
+ // invalidate texture unit states that used this texture
+ for (int i = 0; i < ARRAY_SIZE(m_State.texturesPS); ++i)
+ {
+ TextureUnitStateD3D& currTex = m_State.texturesPS[i];
+ if( currTex.texID == texture )
+ currTex.Invalidate();
+ }
+ for (int i = 0; i < ARRAY_SIZE(m_State.texturesVS); ++i)
+ {
+ TextureUnitStateD3D& currTex = m_State.texturesVS[i];
+ if (currTex.texID == texture)
+ currTex.Invalidate();
+ }
+}
+
+void UnbindTextureD3D9( TextureID texture )
+{
+ GfxDeviceD3D9& device = static_cast<GfxDeviceD3D9&>( GetRealGfxDevice() );
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ // invalidate texture unit states that used this texture
+ for (int i = 0; i < ARRAY_SIZE(device.GetState().texturesPS); ++i)
+ {
+ TextureUnitStateD3D& currTex = device.GetState().texturesPS[i];
+ if( currTex.texID == texture )
+ {
+ D3D9_CALL(dev->SetTexture(GetD3D9SamplerIndex(kShaderFragment,i), NULL));
+ currTex.Invalidate();
+ }
+ }
+ for (int i = 0; i < ARRAY_SIZE(device.GetState().texturesVS); ++i)
+ {
+ TextureUnitStateD3D& currTex = device.GetState().texturesVS[i];
+ if (currTex.texID == texture)
+ {
+ D3D9_CALL(dev->SetTexture(GetD3D9SamplerIndex(kShaderVertex,i), NULL));
+ currTex.Invalidate();
+ }
+ }
+}
+
+
+// ---------- context
+
+GfxDevice::PresentMode GfxDeviceD3D9::GetPresentMode()
+{
+ return kPresentBeforeUpdate;
+}
+
+void GfxDeviceD3D9::BeginFrame()
+{
+ if( m_State.m_DeviceLost )
+ return;
+
+ // begin scene
+ Assert( !m_InsideFrame );
+ GetD3DDevice()->BeginScene();
+ m_InsideFrame = true;
+
+}
+
+void GfxDeviceD3D9::EndFrame()
+{
+ // Check if we're inside scene in case BeginFrame() failed
+ if( !m_InsideFrame )
+ return;
+
+ GetD3DDevice()->EndScene();
+ m_InsideFrame = false;
+}
+
+bool GfxDeviceD3D9::IsValidState()
+{
+ return !m_State.m_DeviceLost;
+}
+
+bool GfxDeviceD3D9::HandleInvalidState()
+{
+#if ENABLE_MULTITHREADED_CODE
+ // Reset render textures owned by the main thread
+ if (Thread::CurrentThreadIsMainThread())
+ CommonReloadResources(kReleaseRenderTextures);
+#endif
+
+ ResetDynamicResourcesD3D9();
+
+ bool success = HandleD3DDeviceLost();
+
+#if ENABLE_PROFILER
+ if (success)
+ m_TimerQueriesD3D9.RecreateAllQueries();
+#endif
+
+ InvalidateState();
+ return success;
+}
+
+static void CleanupEventQueries ()
+{
+ D3D9QueryList::iterator itEnd = s_EventQueries.end();
+ for (D3D9QueryList::iterator it = s_EventQueries.begin(); it != itEnd; ++it)
+ {
+ IDirect3DQuery9* query = *it;
+ if (query != NULL)
+ {
+ query->Release();
+ }
+ }
+ s_EventQueries.clear();
+}
+
+static void PopEventQuery ()
+{
+ AssertIf (s_EventQueries.empty());
+
+ IDirect3DQuery9* query = s_EventQueries.front();
+ AssertIf (query == NULL);
+
+ while (S_FALSE == query->GetData (NULL, 0, D3DGETDATA_FLUSH))
+ {
+ Sleep (1);
+ }
+ query->Release();
+
+ s_EventQueries.pop_front();
+}
+
+void GfxDeviceD3D9::PushEventQuery ()
+{
+ if (m_MaxBufferedFrames < 0)
+ return;
+
+ IDirect3DQuery9* query = NULL;
+ HRESULT hr = GetD3DDevice()->CreateQuery (D3DQUERYTYPE_EVENT, &query);
+ if (query != NULL)
+ {
+ if (SUCCEEDED(query->Issue(D3DISSUE_END)))
+ s_EventQueries.push_back (query);
+ else
+ query->Release();
+ }
+
+ // don't exceed maximum lag... instead we'll deterministically block here until the GPU has done enough work
+ while (!s_EventQueries.empty() && s_EventQueries.size() > m_MaxBufferedFrames)
+ {
+ PopEventQuery();
+ }
+}
+
+void GfxDeviceD3D9::PresentFrame()
+{
+ if( m_State.m_DeviceLost )
+ return;
+
+ HRESULT hr = GetD3DDevice()->Present( NULL, NULL, NULL, NULL );
+ PushEventQuery();
+ // When D3DERR_DRIVERINTERNALERROR is returned from Present(),
+ // the application can do one of the following, try recovering just as
+ // from the lost device.
+ if( hr == D3DERR_DEVICELOST || hr == D3DERR_DRIVERINTERNALERROR )
+ {
+ m_State.m_DeviceLost = true;
+ }
+}
+
+void GfxDeviceD3D9::FinishRendering()
+{
+ // not needed on D3D
+}
+
+
+
+// ---------- immediate mode rendering
+
+// we break very large immediate mode submissions into multiple batches internally
+const int kMaxImmediateVerticesPerDraw = 8192;
+
+
+ImmediateModeD3D::ImmediateModeD3D()
+: m_ImmVertexDecl(NULL)
+{
+ m_QuadsIB = new UInt16[kMaxImmediateVerticesPerDraw*6];
+ UInt32 baseIndex = 0;
+ UInt16* ibPtr = m_QuadsIB;
+ for( int i = 0; i < kMaxImmediateVerticesPerDraw; ++i )
+ {
+ ibPtr[0] = baseIndex + 1;
+ ibPtr[1] = baseIndex + 2;
+ ibPtr[2] = baseIndex;
+ ibPtr[3] = baseIndex + 2;
+ ibPtr[4] = baseIndex + 3;
+ ibPtr[5] = baseIndex;
+ baseIndex += 4;
+ ibPtr += 6;
+ }
+}
+
+ImmediateModeD3D::~ImmediateModeD3D()
+{
+ delete[] m_QuadsIB;
+}
+
+
+void ImmediateModeD3D::Invalidate()
+{
+ m_Vertices.clear();
+ memset( &m_Current, 0, sizeof(m_Current) );
+}
+
+void GfxDeviceD3D9::ImmediateVertex( float x, float y, float z )
+{
+ // If the current batch is becoming too large, internally end it and begin it again.
+ size_t currentSize = m_Imm.m_Vertices.size();
+ if( currentSize >= kMaxImmediateVerticesPerDraw - 4 )
+ {
+ GfxPrimitiveType mode = m_Imm.m_Mode;
+ // For triangles, break batch when multiple of 3's is reached.
+ if( mode == kPrimitiveTriangles && currentSize % 3 == 0 )
+ {
+ ImmediateEnd();
+ ImmediateBegin( mode );
+ }
+ // For other primitives, break on multiple of 4's.
+ // NOTE: This won't quite work for triangle strips, but we'll just pretend
+ // that will never happen.
+ else if( mode != kPrimitiveTriangles && currentSize % 4 == 0 )
+ {
+ ImmediateEnd();
+ ImmediateBegin( mode );
+ }
+ }
+ D3DVECTOR& vert = m_Imm.m_Current.vertex;
+ vert.x = x;
+ vert.y = y;
+ vert.z = z;
+ m_Imm.m_Vertices.push_back( m_Imm.m_Current );
+}
+
+void GfxDeviceD3D9::ImmediateNormal( float x, float y, float z )
+{
+ m_Imm.m_Current.normal.x = x;
+ m_Imm.m_Current.normal.y = y;
+ m_Imm.m_Current.normal.z = z;
+}
+
+void GfxDeviceD3D9::ImmediateColor( float r, float g, float b, float a )
+{
+ float color[4] = { r, g, b, a };
+ m_Imm.m_Current.color = ColorToD3D( color );
+}
+
+void GfxDeviceD3D9::ImmediateTexCoordAll( float x, float y, float z )
+{
+ for( int i = 0; i < 8; ++i )
+ {
+ D3DVECTOR& uv = m_Imm.m_Current.texCoords[i];
+ uv.x = x;
+ uv.y = y;
+ uv.z = z;
+ }
+}
+
+void GfxDeviceD3D9::ImmediateTexCoord( int unit, float x, float y, float z )
+{
+ if( unit < 0 || unit >= 8 )
+ {
+ ErrorString( "Invalid unit for texcoord" );
+ return;
+ }
+ D3DVECTOR& uv = m_Imm.m_Current.texCoords[unit];
+ uv.x = x;
+ uv.y = y;
+ uv.z = z;
+}
+
+void GfxDeviceD3D9::ImmediateBegin( GfxPrimitiveType type )
+{
+ m_Imm.m_Mode = type;
+ m_Imm.m_Vertices.clear();
+}
+
+void GfxDeviceD3D9::ImmediateEnd()
+{
+ if( m_Imm.m_Vertices.empty() )
+ return;
+
+ // lazily create vertex declaration
+ IDirect3DDevice9* dev = GetD3DDevice();
+ HRESULT hr = S_OK;
+ if( !m_Imm.m_ImmVertexDecl )
+ {
+ static const D3DVERTEXELEMENT9 elements[] = {
+ // stream, offset, data type, processing, semantics, index
+ { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, // position
+ { 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 }, // normal
+ { 0, 24, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 }, // color
+ { 0, 28, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, // UVs
+ { 0, 40, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },
+ { 0, 52, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 2 },
+ { 0, 64, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 3 },
+ { 0, 76, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 4 },
+ { 0, 88, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 5 },
+ { 0, 100, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 6 },
+ { 0, 112, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 7 },
+ D3DDECL_END()
+ };
+ hr = dev->CreateVertexDeclaration( elements, &m_Imm.m_ImmVertexDecl );
+ if( FAILED(hr) ) {
+ // TODO: error
+ }
+ }
+
+ // draw
+ D3D9_CALL(dev->SetVertexDeclaration( m_Imm.m_ImmVertexDecl ));
+
+ BeforeDrawCall( true );
+
+ int vertexCount = m_Imm.m_Vertices.size();
+ const ImmediateVertexD3D* vb = &m_Imm.m_Vertices[0];
+ switch( m_Imm.m_Mode )
+ {
+ case kPrimitiveTriangles:
+ hr = D3D9_CALL_HR(dev->DrawPrimitiveUP( D3DPT_TRIANGLELIST, vertexCount / 3, vb, sizeof(ImmediateVertexD3D) ));
+ m_Stats.AddDrawCall( vertexCount / 3, vertexCount );
+ break;
+ case kPrimitiveTriangleStripDeprecated:
+ hr = D3D9_CALL_HR(dev->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, vertexCount - 2, vb, sizeof(ImmediateVertexD3D) ));
+ m_Stats.AddDrawCall( vertexCount - 2, vertexCount );
+ break;
+ case kPrimitiveQuads:
+ hr = D3D9_CALL_HR(dev->DrawIndexedPrimitiveUP( D3DPT_TRIANGLELIST, 0, vertexCount, vertexCount / 4 * 2, m_Imm.m_QuadsIB, D3DFMT_INDEX16, vb, sizeof(ImmediateVertexD3D) ));
+ m_Stats.AddDrawCall( vertexCount / 4 * 2, vertexCount );
+ break;
+ case kPrimitiveLines:
+ hr = D3D9_CALL_HR(dev->DrawPrimitiveUP( D3DPT_LINELIST, vertexCount / 2, vb, sizeof(ImmediateVertexD3D) ));
+ m_Stats.AddDrawCall( vertexCount / 2, vertexCount );
+ break;
+ default:
+ AssertString("ImmediateEnd: unknown draw mode");
+ }
+ AssertIf( FAILED(hr) );
+ // TODO: stats
+
+ // clear vertices
+ m_Imm.m_Vertices.clear();
+}
+
+
+
+bool GfxDeviceD3D9::CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 )
+{
+ HRESULT hr;
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ SurfacePointer renderTarget;
+ hr = dev->GetRenderTarget( 0, &renderTarget );
+ if( !renderTarget || FAILED(hr) )
+ return false;
+
+ D3DSURFACE_DESC rtDesc;
+ renderTarget->GetDesc( &rtDesc );
+
+ SurfacePointer resolvedSurface;
+ if( rtDesc.MultiSampleType != D3DMULTISAMPLE_NONE )
+ {
+ hr = dev->CreateRenderTarget( rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DMULTISAMPLE_NONE, 0, FALSE, &resolvedSurface, NULL );
+ if( FAILED(hr) )
+ return false;
+ hr = dev->StretchRect( renderTarget, NULL, resolvedSurface, NULL, D3DTEXF_NONE );
+ if( FAILED(hr) )
+ return false;
+ renderTarget = resolvedSurface;
+ }
+
+ SurfacePointer offscreenSurface;
+ hr = dev->CreateOffscreenPlainSurface( rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DPOOL_SYSTEMMEM, &offscreenSurface, NULL );
+ if( FAILED(hr) )
+ return false;
+
+ hr = dev->GetRenderTargetData( renderTarget, offscreenSurface );
+ bool ok = SUCCEEDED(hr);
+ if( ok )
+ {
+ rgba32 += (height-1) * width * sizeof(UInt32);
+ if( rtDesc.Format == D3DFMT_A8R8G8B8 || rtDesc.Format == D3DFMT_X8R8G8B8 )
+ {
+ // Backbuffer is 32 bit
+ D3DLOCKED_RECT lr;
+ RECT rect;
+ rect.left = left;
+ rect.right = left + width;
+ rect.top = rtDesc.Height - bottom - height;
+ rect.bottom = rtDesc.Height - bottom;
+ hr = offscreenSurface->LockRect( &lr, &rect, D3DLOCK_READONLY );
+ if( SUCCEEDED(hr) )
+ {
+ const UInt8* src = (const UInt8*)lr.pBits;
+ for( int y = 0; y < height; ++y )
+ {
+ const UInt32* srcPtr = (const UInt32*)src;
+ UInt32* dstPtr = (UInt32*)rgba32;
+ for( int x = 0; x < width; ++x )
+ {
+ UInt32 argbCol = *srcPtr;
+ UInt32 abgrCol = (argbCol&0xFF00FF00) | ((argbCol&0x00FF0000)>>16) | ((argbCol&0x000000FF)<<16);
+ *dstPtr = abgrCol;
+ ++srcPtr;
+ ++dstPtr;
+ }
+ rgba32 -= width * sizeof(UInt32);
+ src += lr.Pitch;
+ }
+ }
+ else
+ {
+ ok = false;
+ }
+ offscreenSurface->UnlockRect();
+ }
+ else if( rtDesc.Format == D3DFMT_R5G6B5 )
+ {
+ // Backbuffer is 16 bit 565
+ D3DLOCKED_RECT lr;
+ RECT rect;
+ rect.left = left;
+ rect.right = left + width;
+ rect.top = rtDesc.Height - bottom - height;
+ rect.bottom = rtDesc.Height - bottom;
+ hr = offscreenSurface->LockRect( &lr, &rect, D3DLOCK_READONLY );
+ if( SUCCEEDED(hr) )
+ {
+ const UInt8* src = (const UInt8*)lr.pBits;
+ for( int y = 0; y < height; ++y )
+ {
+ const UInt16* srcPtr = (const UInt16*)src;
+ UInt32* dstPtr = (UInt32*)rgba32;
+ for( int x = 0; x < width; ++x )
+ {
+ UInt16 rgbCol = *srcPtr;
+ UInt32 abgrCol = 0xFF000000 | ((rgbCol&0xF800)>>8) | ((rgbCol&0x07E0)<<5) | ((rgbCol&0x001F)<<19);
+ *dstPtr = abgrCol;
+ ++srcPtr;
+ ++dstPtr;
+ }
+ rgba32 -= width * sizeof(UInt32);
+ src += lr.Pitch;
+ }
+ }
+ else
+ {
+ ok = false;
+ }
+ offscreenSurface->UnlockRect();
+ }
+ else if( rtDesc.Format == D3DFMT_X1R5G5B5 || rtDesc.Format == D3DFMT_A1R5G5B5 )
+ {
+ // Backbuffer is 15 bit 555
+ D3DLOCKED_RECT lr;
+ RECT rect;
+ rect.left = left;
+ rect.right = left + width;
+ rect.top = rtDesc.Height - bottom - height;
+ rect.bottom = rtDesc.Height - bottom;
+ hr = offscreenSurface->LockRect( &lr, &rect, D3DLOCK_READONLY );
+ if( SUCCEEDED(hr) )
+ {
+ const UInt8* src = (const UInt8*)lr.pBits;
+ for( int y = 0; y < height; ++y )
+ {
+ const UInt16* srcPtr = (const UInt16*)src;
+ UInt32* dstPtr = (UInt32*)rgba32;
+ for( int x = 0; x < width; ++x )
+ {
+ UInt16 rgbCol = *srcPtr;
+ UInt32 abgrCol = ((rgbCol&0x8000)<<16) | ((rgbCol&0x7C00)>>7) | ((rgbCol&0x03E0)<<6) | ((rgbCol&0x001F)<<19);
+ *dstPtr = abgrCol;
+ ++srcPtr;
+ ++dstPtr;
+ }
+ rgba32 -= width * sizeof(UInt32);
+ src += lr.Pitch;
+ }
+ }
+ else
+ {
+ ok = false;
+ }
+ offscreenSurface->UnlockRect();
+ }
+ else
+ {
+ // TODO: handle more conversions!
+ ok = false;
+ }
+ }
+
+ return ok;
+}
+
+
+
+bool GfxDeviceD3D9::ReadbackImage( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY )
+{
+ // TODO: make it work in all different situations
+
+ AssertIf( image.GetFormat() != kTexFormatARGB32 && image.GetFormat() != kTexFormatRGB24 );
+
+ HRESULT hr;
+ IDirect3DDevice9* dev = GetD3DDevice();
+ SurfacePointer renderTarget;
+ hr = dev->GetRenderTarget( 0, &renderTarget );
+ if( !renderTarget || FAILED(hr) )
+ return false;
+
+ D3DSURFACE_DESC rtDesc;
+ renderTarget->GetDesc( &rtDesc );
+
+ SurfacePointer resolvedSurface;
+ if( rtDesc.MultiSampleType != D3DMULTISAMPLE_NONE )
+ {
+ hr = dev->CreateRenderTarget( rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DMULTISAMPLE_NONE, 0, FALSE, &resolvedSurface, NULL );
+ if( FAILED(hr) )
+ return false;
+ hr = dev->StretchRect( renderTarget, NULL, resolvedSurface, NULL, D3DTEXF_NONE );
+ if( FAILED(hr) )
+ return false;
+ renderTarget = resolvedSurface;
+ }
+
+ SurfacePointer offscreenSurface;
+ hr = dev->CreateOffscreenPlainSurface( rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DPOOL_SYSTEMMEM, &offscreenSurface, NULL );
+ if( FAILED(hr) )
+ return false;
+ if (width <= 0 || left < 0 || left + width > rtDesc.Width)
+ {
+ ErrorString("Trying to read pixel out of bounds");
+ return false;
+ }
+ if (height <= 0 || bottom < 0 || bottom + height > rtDesc.Height)
+ {
+ ErrorString("Trying to read pixel out of bounds");
+ return false;
+ }
+
+ hr = dev->GetRenderTargetData( renderTarget, offscreenSurface );
+ bool ok = SUCCEEDED(hr);
+ if( ok )
+ {
+ if( rtDesc.Format == D3DFMT_A8R8G8B8 || rtDesc.Format == D3DFMT_X8R8G8B8 )
+ {
+ // Render target is 32 bit
+ D3DLOCKED_RECT lr;
+ RECT rect;
+ rect.left = left;
+ rect.right = left + width;
+ rect.top = rtDesc.Height - bottom - height;
+ rect.bottom = rtDesc.Height - bottom;
+ hr = offscreenSurface->LockRect( &lr, &rect, D3DLOCK_READONLY );
+ if( SUCCEEDED(hr) )
+ {
+ const UInt8* src = (const UInt8*)lr.pBits;
+ if( image.GetFormat() == kTexFormatARGB32 )
+ {
+ for( int y = height-1; y >= 0; --y )
+ {
+ const UInt32* srcPtr = (const UInt32*)src;
+ UInt32* dstPtr = (UInt32*)(image.GetRowPtr(destY+y) + destX * 4);
+ for( int x = 0; x < width; ++x )
+ {
+ UInt32 argbCol = *srcPtr;
+ UInt32 bgraCol = ((argbCol&0xFF000000)>>24) | ((argbCol&0x00FF0000)>>8) | ((argbCol&0x0000FF00)<<8) | ((argbCol&0x000000FF)<<24);
+ *dstPtr = bgraCol;
+ ++srcPtr;
+ ++dstPtr;
+ }
+ src += lr.Pitch;
+ }
+ }
+ else if( image.GetFormat() == kTexFormatRGB24 )
+ {
+ for( int y = height-1; y >= 0; --y )
+ {
+ const UInt32* srcPtr = (const UInt32*)src;
+ UInt8* dstPtr = image.GetRowPtr(destY+y) + destX * 3;
+ for( int x = 0; x < width; ++x )
+ {
+ UInt32 argbCol = *srcPtr;
+ dstPtr[0] = (argbCol & 0x00FF0000) >> 16;
+ dstPtr[1] = (argbCol & 0x0000FF00) >> 8;
+ dstPtr[2] = (argbCol & 0x000000FF);
+ ++srcPtr;
+ dstPtr += 3;
+ }
+ src += lr.Pitch;
+ }
+ }
+ else
+ {
+ AssertString( "Invalid image format" );
+ }
+ }
+ else
+ {
+ ok = false;
+ }
+ offscreenSurface->UnlockRect();
+ }
+ else if( rtDesc.Format == D3DFMT_R5G6B5 )
+ {
+ // Render target is 16 bit 565
+ D3DLOCKED_RECT lr;
+ RECT rect;
+ rect.left = left;
+ rect.right = left + width;
+ rect.top = rtDesc.Height - bottom - height;
+ rect.bottom = rtDesc.Height - bottom;
+ hr = offscreenSurface->LockRect( &lr, &rect, D3DLOCK_READONLY );
+ if( SUCCEEDED(hr) )
+ {
+ const UInt8* src = (const UInt8*)lr.pBits;
+ if( image.GetFormat() == kTexFormatARGB32 )
+ {
+ for( int y = height-1; y >= 0; --y )
+ {
+ const UInt16* srcPtr = (const UInt16*)src;
+ UInt32* dstPtr = (UInt32*)(image.GetRowPtr(destY+y) + destX * 4);
+ for( int x = 0; x < width; ++x )
+ {
+ UInt16 argbCol = *srcPtr;
+ UInt32 bgraCol = 0x000000FF | (argbCol&0xF800) | ((argbCol&0x07E0)<<13) | ((argbCol&0x001F)<<27);
+ *dstPtr = bgraCol;
+ ++srcPtr;
+ ++dstPtr;
+ }
+ src += lr.Pitch;
+ }
+ }
+ else if( image.GetFormat() == kTexFormatRGB24 )
+ {
+ for( int y = height-1; y >= 0; --y )
+ {
+ const UInt16* srcPtr = (const UInt16*)src;
+ UInt8* dstPtr = image.GetRowPtr(destY+y) + destX * 3;
+ for( int x = 0; x < width; ++x )
+ {
+ UInt16 argbCol = *srcPtr;
+ dstPtr[0] = (argbCol & 0xF800) >> 8;
+ dstPtr[1] = (argbCol & 0x07E0) >> 3;
+ dstPtr[2] = (argbCol & 0x001F) << 3;
+ ++srcPtr;
+ dstPtr += 3;
+ }
+ src += lr.Pitch;
+ }
+ }
+ else
+ {
+ AssertString( "Invalid image format" );
+ }
+ }
+ else
+ {
+ ok = false;
+ }
+ offscreenSurface->UnlockRect();
+ }
+ else if( rtDesc.Format == D3DFMT_A1R5G5B5 || rtDesc.Format == D3DFMT_X1R5G5B5 )
+ {
+ // Render target is 15 bit 555
+ D3DLOCKED_RECT lr;
+ RECT rect;
+ rect.left = left;
+ rect.right = left + width;
+ rect.top = rtDesc.Height - bottom - height;
+ rect.bottom = rtDesc.Height - bottom;
+ hr = offscreenSurface->LockRect( &lr, &rect, D3DLOCK_READONLY );
+ if( SUCCEEDED(hr) )
+ {
+ const UInt8* src = (const UInt8*)lr.pBits;
+ if( image.GetFormat() == kTexFormatARGB32 )
+ {
+ for( int y = height-1; y >= 0; --y )
+ {
+ const UInt16* srcPtr = (const UInt16*)src;
+ UInt32* dstPtr = (UInt32*)(image.GetRowPtr(destY+y) + destX * 4);
+ for( int x = 0; x < width; ++x )
+ {
+ UInt16 argbCol = *srcPtr;
+ UInt32 bgraCol = ((argbCol&0x8000)>>8) | ((argbCol&0x7C00)<<1) | ((argbCol&0x03E0)<<14) | ((argbCol&0x001F)<<27);
+ *dstPtr = bgraCol;
+ ++srcPtr;
+ ++dstPtr;
+ }
+ src += lr.Pitch;
+ }
+ }
+ else if( image.GetFormat() == kTexFormatRGB24 )
+ {
+ for( int y = height-1; y >= 0; --y )
+ {
+ const UInt16* srcPtr = (const UInt16*)src;
+ UInt8* dstPtr = image.GetRowPtr(destY+y) + destX * 3;
+ for( int x = 0; x < width; ++x )
+ {
+ UInt16 argbCol = *srcPtr;
+ dstPtr[0] = (argbCol & 0x7C00) >> 7;
+ dstPtr[1] = (argbCol & 0x03E0) >> 2;
+ dstPtr[2] = (argbCol & 0x001F) << 3;
+ ++srcPtr;
+ dstPtr += 3;
+ }
+ src += lr.Pitch;
+ }
+ }
+ else
+ {
+ AssertString( "Invalid image format" );
+ }
+ }
+ else
+ {
+ ok = false;
+ }
+ offscreenSurface->UnlockRect();
+ }
+ else
+ {
+ // TODO: handle more conversions!
+ ok = false;
+ }
+ }
+
+ return ok;
+}
+
+void GfxDeviceD3D9::GrabIntoRenderTexture(RenderSurfaceHandle rtHandle, RenderSurfaceHandle rd, int x, int y, int width, int height )
+{
+ if( !rtHandle.IsValid() )
+ return;
+
+ RenderColorSurfaceD3D9* renderTexture = reinterpret_cast<RenderColorSurfaceD3D9*>( rtHandle.object );
+
+ HRESULT hr;
+ IDirect3DDevice9* dev = GetD3DDevice();
+ SurfacePointer currentRenderTarget;
+ hr = dev->GetRenderTarget( 0, &currentRenderTarget );
+ if( !currentRenderTarget || FAILED(hr) )
+ return;
+
+ D3DSURFACE_DESC rtDesc;
+ currentRenderTarget->GetDesc( &rtDesc );
+
+ IDirect3DTexture9* texturePointer = static_cast<IDirect3DTexture9*>(m_Textures.GetTexture (renderTexture->textureID));
+ if( !texturePointer )
+ return;
+
+ SurfacePointer textureSurface;
+ hr = texturePointer->GetSurfaceLevel( 0, &textureSurface );
+ if( !textureSurface || FAILED(hr) )
+ return;
+
+ RECT rc;
+ rc.left = x;
+ rc.top = rtDesc.Height - (y + height);
+ rc.right = x + width;
+ rc.bottom = rtDesc.Height - (y);
+ hr = dev->StretchRect( currentRenderTarget, &rc, textureSurface, NULL, D3DTEXF_NONE );
+}
+
+
+void* GfxDeviceD3D9::GetNativeGfxDevice()
+{
+ return GetD3DDevice();
+}
+
+void* GfxDeviceD3D9::GetNativeTexturePointer(TextureID id)
+{
+ return m_Textures.GetTexture (id);
+}
+
+intptr_t GfxDeviceD3D9::CreateExternalTextureFromNative(intptr_t nativeTex)
+{
+ return m_Textures.RegisterNativeTexture((IDirect3DBaseTexture9*)nativeTex);
+}
+
+void GfxDeviceD3D9::UpdateExternalTextureFromNative(TextureID tex, intptr_t nativeTex)
+{
+ m_Textures.UpdateNativeTexture(tex, (IDirect3DBaseTexture9*)nativeTex);
+}
+
+
+#if ENABLE_PROFILER
+
+void GfxDeviceD3D9::BeginProfileEvent (const char* name)
+{
+ if (g_D3D9BeginEventFunc)
+ {
+ wchar_t wideName[100];
+ UTF8ToWide (name, wideName, 100);
+ g_D3D9BeginEventFunc (0, wideName);
+ }
+}
+
+void GfxDeviceD3D9::EndProfileEvent ()
+{
+ if (g_D3D9EndEventFunc)
+ {
+ g_D3D9EndEventFunc ();
+ }
+}
+
+GfxTimerQuery* GfxDeviceD3D9::CreateTimerQuery()
+{
+ Assert(gGraphicsCaps.hasTimerQuery);
+ return m_TimerQueriesD3D9.CreateTimerQuery();
+}
+
+void GfxDeviceD3D9::DeleteTimerQuery(GfxTimerQuery* query)
+{
+ delete query;
+}
+
+void GfxDeviceD3D9::BeginTimerQueries()
+{
+ if(!gGraphicsCaps.hasTimerQuery)
+ return;
+
+ m_TimerQueriesD3D9.BeginTimerQueries();
+}
+
+void GfxDeviceD3D9::EndTimerQueries()
+{
+ if(!gGraphicsCaps.hasTimerQuery)
+ return;
+
+ m_TimerQueriesD3D9.EndTimerQueries();
+}
+
+/*
+SInt32 GfxDeviceD3D9::GetTimerQueryIdentifier()
+{
+ if(!gGraphicsCaps.hasTimerQuery)
+ return -1;
+ // Allocate more queries
+ if(m_QueryCount[m_CurrentQueryBuffer] >= m_GPUQueries[m_CurrentQueryBuffer].size())
+ {
+ int count = std::max (m_QueryCount[m_CurrentQueryBuffer], 100);
+ IDirect3DQuery9* d3dQuery;
+ for( int i = 0; i < count; i++)
+ {
+ GetD3DDevice()->CreateQuery(D3DQUERYTYPE_TIMESTAMP, &d3dQuery);
+ // initialze more Query objects
+ m_GPUQueries[m_CurrentQueryBuffer].push_back(d3dQuery);
+ }
+ }
+ int index = m_QueryCount[m_CurrentQueryBuffer]++;
+ IDirect3DQuery9* currentQuery = m_GPUQueries[m_CurrentQueryBuffer][index];
+ currentQuery ->Issue(D3DISSUE_END);
+ return index;
+}
+
+ProfileTimeFormat GfxDeviceD3D9::GetTimerQueryData(SInt32 identifier, bool wait)
+{
+ if(!gGraphicsCaps.hasTimerQuery)
+ return 0;
+
+ if(m_GPUQueries[m_CurrentQueryBuffer].size()<=identifier)
+ return 0;
+
+ UINT64 time;
+ while (S_OK != m_GPUQueries[m_CurrentQueryBuffer][identifier]->GetData(&time, sizeof(time), D3DGETDATA_FLUSH)) {}
+ return (double)time * m_TimeMultiplier;
+}
+
+void GfxDeviceD3D9::CleanupTimerQueries ()
+{
+ if(!gGraphicsCaps.hasTimerQuery)
+ return;
+
+ for(int buffer = 0; buffer < 2; buffer++)
+ {
+ for(int i = 0; i < m_GPUQueries[buffer].size(); i++)
+ m_GPUQueries[buffer][i]->Release();
+ m_GPUQueries[buffer].clear();
+ if(m_FrequencyQuery[buffer])
+ m_FrequencyQuery[buffer]->Release();
+ m_FrequencyQuery[buffer] = NULL;
+ m_QueryCount[buffer] = 0;
+ }
+}
+*/
+
+#endif // ENABLE_PROFILER
+
+
+// -------- editor only functions
+
+#if UNITY_EDITOR
+void GfxDeviceD3D9::SetAntiAliasFlag( bool aa )
+{
+ #pragma message("! implement SetAntiAliasFlag")
+}
+
+
+void GfxDeviceD3D9::DrawUserPrimitives( GfxPrimitiveType type, int vertexCount, UInt32 vertexChannels, const void* data, int stride )
+{
+ if( vertexCount == 0 )
+ return;
+
+ AssertIf(vertexCount > 60000); // TODO: handle this by multi-batching
+
+ AssertIf( !data || vertexCount < 0 || vertexChannels == 0 );
+
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ IDirect3DVertexDeclaration9* vertexDecl = GetD3DVertexDeclaration( vertexChannels );
+
+ ChannelAssigns channels;
+ for( int i = 0; i < kShaderChannelCount; ++i )
+ {
+ if( !( vertexChannels & (1<<i) ) )
+ continue;
+ VertexComponent destComponent = kSuitableVertexComponentForChannel[i];
+ channels.Bind( (ShaderChannel)i, destComponent );
+ }
+ D3D9_CALL(dev->SetVertexDeclaration( vertexDecl ));
+ UpdateChannelBindingsD3D( channels );
+ BeforeDrawCall(false);
+
+ HRESULT hr;
+ switch( type ) {
+ case kPrimitiveTriangles:
+ hr = D3D9_CALL_HR(dev->DrawPrimitiveUP( D3DPT_TRIANGLELIST, vertexCount/3, data, stride ));
+ m_Stats.AddDrawCall( vertexCount / 3, vertexCount );
+ break;
+ case kPrimitiveQuads:
+ while (vertexCount > 0)
+ {
+ int vcount = std::min(vertexCount,kMaxImmediateVerticesPerDraw);
+ hr = D3D9_CALL_HR(dev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, vcount, vcount / 4 * 2, m_Imm.m_QuadsIB, D3DFMT_INDEX16, data, stride));
+ m_Stats.AddDrawCall(vcount / 4 * 2, vcount);
+ data = (const UInt8*)data + vcount * stride;
+ vertexCount -= vcount;
+ }
+ break;
+ case kPrimitiveLines:
+ hr = D3D9_CALL_HR(dev->DrawPrimitiveUP( D3DPT_LINELIST, vertexCount/2, data, stride ));
+ m_Stats.AddDrawCall( vertexCount / 2, vertexCount );
+ break;
+ case kPrimitiveLineStrip:
+ hr = D3D9_CALL_HR(dev->DrawPrimitiveUP( D3DPT_LINESTRIP, vertexCount-1, data, stride ));
+ m_Stats.AddDrawCall( vertexCount-1, vertexCount );
+ break;
+ default:
+ ErrorString("Primitive type not supported");
+ return;
+ }
+ Assert(SUCCEEDED(hr));
+}
+
+int GfxDeviceD3D9::GetCurrentTargetAA() const
+{
+ return GetCurrentD3DFSAALevel();
+}
+
+GfxDeviceWindow* GfxDeviceD3D9::CreateGfxWindow( HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias )
+{
+ return new D3D9Window( GetD3DDevice(), window, width, height, depthFormat, antiAlias);
+}
+
+#endif
+
+int GfxDeviceD3D9::GetCurrentTargetWidth() const
+{
+ return m_CurrTargetWidth;
+}
+
+int GfxDeviceD3D9::GetCurrentTargetHeight() const
+{
+ return m_CurrTargetHeight;
+}
+
+void GfxDeviceD3D9::SetCurrentTargetSize(int width, int height)
+{
+ m_CurrTargetWidth = width;
+ m_CurrTargetHeight = height;
+}
+
+void GfxDeviceD3D9::SetCurrentWindowSize(int width, int height)
+{
+ m_CurrWindowWidth = m_CurrTargetWidth = width;
+ m_CurrWindowHeight = m_CurrTargetHeight = height;
+}
+
+
+#if UNITY_EDITOR
+
+static IDirect3DTexture9* FindD3D9TextureByID (TextureID tid)
+{
+ GfxDevice& device = GetRealGfxDevice();
+ if (device.GetRenderer() != kGfxRendererD3D9)
+ return NULL;
+ GfxDeviceD3D9& dev = static_cast<GfxDeviceD3D9&>(device);
+ IDirect3DBaseTexture9* basetex = dev.GetTextures().GetTexture (tid);
+ if (!basetex)
+ return NULL;
+ if (basetex->GetType() != D3DRTYPE_TEXTURE)
+ return NULL;
+ return static_cast<IDirect3DTexture9*>(basetex);
+}
+
+// In the editor, for drawing directly into HDC of D3D texture.
+// Functions not defined in any header; declare prototypes manually:
+// HDC AcquireHDCForTextureD3D9 (TextureID tid, int& outWidth, int& outHeight);
+// void ReleaseHDCForTextureD3D9 (TextureID tid, HDC dc);
+// AcquireHDCForTextureD3D9 _can_ return NULL if it can't get to DC (not D3D9, no
+// texture, wrong texture format, ...).
+
+HDC AcquireHDCForTextureD3D9 (TextureID tid, int& outWidth, int& outHeight)
+{
+ IDirect3DTexture9* tex = FindD3D9TextureByID (tid);
+ if (!tex)
+ return NULL;
+ SurfacePointer surface;
+ if (FAILED(tex->GetSurfaceLevel(0,&surface)))
+ return NULL;
+ D3DSURFACE_DESC desc;
+ if (FAILED(surface->GetDesc (&desc)))
+ return NULL;
+ outWidth = desc.Width;
+ outHeight = desc.Height;
+ HDC dc = NULL;
+ if (FAILED(surface->GetDC(&dc)))
+ return NULL;
+ return dc;
+}
+
+void ReleaseHDCForTextureD3D9 (TextureID tid, HDC dc)
+{
+ IDirect3DTexture9* tex = FindD3D9TextureByID (tid);
+ if (!tex)
+ return;
+ SurfacePointer surface;
+ if (FAILED(tex->GetSurfaceLevel(0,&surface)))
+ return;
+ surface->ReleaseDC (dc);
+}
+
+#endif
+
+
+// ----------------------------------------------------------------------
+// verification of state
+
+#if GFX_DEVICE_VERIFY_ENABLE
+
+#include "Runtime/Utilities/Utility.h"
+
+void VerifyStateF(D3DRENDERSTATETYPE rs, float val, const char *str);
+#define VERIFYF(s,t) VerifyState (s, t, #s " (" #t ")")
+void VerifyStateI(D3DRENDERSTATETYPE rs, int val, const char *str);
+#define VERIFYI(s,t) VerifyStateI (s, t, #s " (" #t ")")
+void VerifyEnabled(D3DRENDERSTATETYPE rs, bool val, const char *str);
+#define VERIFYENAB(s,t) VerifyEnabled ( s, t, #s " (" #t ")")
+
+static void VERIFY_PRINT( const char* format, ... )
+{
+ ErrorString( VFormat( format, va_list(&format + 1) ) );
+}
+
+const float kVerifyDelta = 0.0001f;
+
+void VerifyStateF(D3DRENDERSTATETYPE rs, float val, const char *str)
+{
+ float temp = 0;
+ GetD3DDevice()->GetRenderState(rs,(DWORD*)&temp);
+ if( !CompareApproximately(temp,val,kVerifyDelta) ) {
+ VERIFY_PRINT ("%s differs from cache (%f != %f)\n", str, val, temp);
+ }
+}
+
+void VerifyStateI(D3DRENDERSTATETYPE rs, int val, const char *str)
+{
+ int temp;
+ GetD3DDevice()->GetRenderState(rs,(DWORD*)&temp);
+ if (temp != val) {
+ VERIFY_PRINT ("%s differs from cache (%i != %i)\n", str, val, temp);
+ }
+}
+
+void VerifyEnabled(D3DRENDERSTATETYPE rs, bool val, const char *str)
+{
+ DWORD v;
+ GetD3DDevice()->GetRenderState(rs,&v);
+ bool temp = v==TRUE ? true : false;
+ if (temp != val) {
+ VERIFY_PRINT ("%s differs from cache (%d != %d)\n", str, val, temp);
+ }
+}
+
+void GfxDeviceD3D9::VerifyState()
+{
+ // check if current state blocks match internal state
+ if (m_CurrBlendState != NULL) {
+ if (m_State.blending == 0) {
+ Assert (D3DBLEND_ONE == kBlendModeD3D9[m_CurrBlendState->sourceState.srcBlend]);
+ Assert (D3DBLEND_ZERO == kBlendModeD3D9[m_CurrBlendState->sourceState.dstBlend]);
+ } else {
+ Assert (m_State.srcBlend == kBlendModeD3D9[m_CurrBlendState->sourceState.srcBlend]);
+ Assert (m_State.destBlend == kBlendModeD3D9[m_CurrBlendState->sourceState.dstBlend]);
+ }
+ #if !UNITY_EDITOR // Editor does some funkiness when emulating alpha test, see SetBlendState
+ Assert (kCmpFuncD3D9[m_State.alphaFunc] == m_CurrBlendState->alphaFunc);
+ #endif
+ }
+
+ m_State.Verify();
+}
+
+
+
+void DeviceStateD3D::Verify()
+{
+ #ifdef DUMMY_D3D9_CALLS
+ return;
+ #endif
+ if( !GetD3DDevice() ) {
+ ErrorString("Verify: no D3D device");
+ return;
+ }
+
+ if( depthFunc != kFuncUnknown ) {
+ VERIFYI( D3DRS_ZFUNC, kCmpFuncD3D9[depthFunc] );
+ }
+ if( depthWrite != -1 ) {
+ VERIFYI( D3DRS_ZWRITEENABLE, (depthWrite ? TRUE : FALSE) );
+ }
+ if( blending != -1 ) {
+ VERIFYENAB( D3DRS_ALPHABLENDENABLE, blending != 0 );
+ if( blending ) {
+ VERIFYI( D3DRS_SRCBLEND, srcBlend );
+ VERIFYI( D3DRS_DESTBLEND, destBlend );
+ }
+ }
+
+ if( alphaFunc != kFuncUnknown ) {
+ VERIFYENAB( D3DRS_ALPHATESTENABLE, alphaFunc != kFuncDisabled );
+ if( alphaFunc != kFuncDisabled ) {
+ VERIFYI( D3DRS_ALPHAFUNC, kCmpFuncD3D9[alphaFunc] );
+ if( alphaValue != -1 )
+ VERIFYI( D3DRS_ALPHAREF, alphaValue*255.0f );
+ }
+ }
+}
+
+#endif // GFX_DEVICE_VERIFY_ENABLE
+
diff --git a/Runtime/GfxDevice/d3d/GfxDeviceD3D9.h b/Runtime/GfxDevice/d3d/GfxDeviceD3D9.h
new file mode 100644
index 0000000..f648a35
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/GfxDeviceD3D9.h
@@ -0,0 +1,361 @@
+#pragma once
+
+#include "D3D9Includes.h"
+#include "VertexDeclarations.h"
+#include "TexturesD3D9.h"
+#include "Runtime/GfxDevice/ShaderConstantCache.h"
+#include "Runtime/Shaders/MaterialProperties.h"
+#include "VertexPipeD3D9.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "D3D9Context.h"
+#include "Runtime/Math/FloatConversion.h"
+#include "D3D9VBO.h"
+#include "CombinerD3D.h"
+#include "External/shaderlab/Library/program.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "Runtime/GfxDevice/BuiltinShaderParams.h"
+#include "Runtime/Graphics/Image.h"
+#include "PlatformDependent/Win/SmartComPointer.h"
+#include "Runtime/Utilities/Utility.h"
+#include "D3D9Utils.h"
+#include "D3D9Window.h"
+#include "GpuProgramsD3D.h"
+#include "TimerQueryD3D9.h"
+
+typedef SmartComPointer<IDirect3DSurface9> SurfacePointer;
+
+struct TextureUnitStateD3D
+{
+ TextureID texID;
+ float bias;
+
+ void Invalidate()
+ {
+ texID.m_ID = -1;
+ bias = 1.0e6f;
+ }
+};
+
+class GfxDeviceD3D9;
+
+struct DeviceStateD3D
+{
+ int viewport[4];
+ int scissorRect[4];
+
+ CompareFunction depthFunc;
+ int depthWrite; // 0/1 or -1
+
+ int blending;
+ int srcBlend, destBlend, srcBlendAlpha, destBlendAlpha; // D3D modes
+ int blendOp, blendOpAlpha; // D3D modes
+ CompareFunction alphaFunc;
+ float alphaValue;
+
+ CullMode culling;
+ D3DCULL d3dculling;
+ bool appBackfaceMode, userBackfaceMode, invertProjMatrix;
+ bool wireframe;
+ int scissor;
+
+ // [0] is front, [1] is back, unless invertProjMatrix is true
+ D3DCMPFUNC stencilFunc[2];
+ D3DSTENCILOP stencilFailOp[2], depthFailOp[2], depthPassOp[2];
+
+ float offsetFactor, offsetUnits;
+
+ GpuProgram* activeGpuProgram[kShaderTypeCount];
+ const GpuProgramParameters* activeGpuProgramParams[kShaderTypeCount];
+ IUnknown* activeShader[kShaderTypeCount];
+
+ int colorWriteMask; // ColorWriteMask combinations
+
+ int m_StencilRef;
+
+ TextureUnitStateD3D texturesPS[kMaxSupportedTextureUnits];
+ TextureUnitStateD3D texturesVS[4];
+
+ int fixedFunctionPS;
+
+ bool m_DeviceLost;
+
+ bool m_SoftwareVP;
+ UInt32 m_NeedsSofwareVPFlags;
+
+ void Invalidate( GfxDeviceD3D9& device );
+ void Verify();
+};
+
+// TODO: optimize this. Right now we just send off whole 8 float3 UVs with each
+// immediate mode vertex. We could at least detect the number of them used from
+// ImmediateTexCoord calls.
+struct ImmediateVertexD3D {
+ D3DVECTOR vertex;
+ D3DVECTOR normal;
+ D3DCOLOR color;
+ D3DVECTOR texCoords[8];
+};
+
+struct ImmediateModeD3D {
+ std::vector<ImmediateVertexD3D> m_Vertices;
+ ImmediateVertexD3D m_Current;
+ GfxPrimitiveType m_Mode;
+ IDirect3DVertexDeclaration9* m_ImmVertexDecl;
+ UInt16* m_QuadsIB;
+
+ ImmediateModeD3D();
+ ~ImmediateModeD3D();
+ void Invalidate();
+};
+
+class GfxDeviceD3D9 : public GfxThreadableDevice
+{
+public:
+ struct DeviceBlendStateD3D9 : public DeviceBlendState
+ {
+ UInt8 renderTargetWriteMask;
+ D3DCMPFUNC alphaFunc;
+ };
+
+ struct DeviceDepthStateD3D9 : public DeviceDepthState
+ {
+ D3DCMPFUNC depthFunc;
+ };
+
+ struct DeviceStencilStateD3D9 : public DeviceStencilState
+ {
+ D3DCMPFUNC stencilFuncFront;
+ D3DSTENCILOP stencilFailOpFront;
+ D3DSTENCILOP depthFailOpFront;
+ D3DSTENCILOP depthPassOpFront;
+ D3DCMPFUNC stencilFuncBack;
+ D3DSTENCILOP stencilFailOpBack;
+ D3DSTENCILOP depthFailOpBack;
+ D3DSTENCILOP depthPassOpBack;
+ };
+
+
+ typedef std::map< GfxBlendState, DeviceBlendStateD3D9, memcmp_less<GfxBlendState> > CachedBlendStates;
+ typedef std::map< GfxDepthState, DeviceDepthStateD3D9, memcmp_less<GfxDepthState> > CachedDepthStates;
+ typedef std::map< GfxStencilState, DeviceStencilStateD3D9, memcmp_less<GfxStencilState> > CachedStencilStates;
+ typedef std::map< GfxRasterState, DeviceRasterState, memcmp_less<GfxRasterState> > CachedRasterStates;
+
+
+public:
+ GfxDeviceD3D9();
+ GFX_API ~GfxDeviceD3D9();
+
+ GFX_API void InvalidateState();
+ #if GFX_DEVICE_VERIFY_ENABLE
+ GFX_API void VerifyState();
+ #endif
+
+ GFX_API void Clear(UInt32 clearFlags, const float color[4], float depth, int stencil);
+ GFX_API void SetUserBackfaceMode( bool enable );
+ GFX_API void SetWireframe(bool wire);
+ GFX_API bool GetWireframe() const;
+ GFX_API void SetInvertProjectionMatrix( bool enable );
+ GFX_API bool GetInvertProjectionMatrix() const;
+
+ GFX_API GPUSkinningInfo *CreateGPUSkinningInfo() { return NULL; }
+ GFX_API void DeleteGPUSkinningInfo(GPUSkinningInfo *info) { AssertBreak(false); }
+ GFX_API void SkinOnGPU( GPUSkinningInfo * info, bool lastThisFrame ) { AssertBreak(false); }
+ GFX_API void UpdateSkinSourceData(GPUSkinningInfo *info, const void *vertData, const BoneInfluence *skinData, bool dirty) { AssertBreak(false); }
+ GFX_API void UpdateSkinBonePoses(GPUSkinningInfo *info, const int boneCount, const Matrix4x4f* poses) { AssertBreak(false); }
+
+ GFX_API DeviceBlendState* CreateBlendState(const GfxBlendState& state);
+ GFX_API DeviceDepthState* CreateDepthState(const GfxDepthState& state);
+ GFX_API DeviceStencilState* CreateStencilState(const GfxStencilState& state);
+ GFX_API DeviceRasterState* CreateRasterState(const GfxRasterState& state);
+
+ GFX_API void SetBlendState(const DeviceBlendState* state, float alphaRef);
+ GFX_API void SetRasterState(const DeviceRasterState* state);
+ GFX_API void SetDepthState(const DeviceDepthState* state);
+ GFX_API void SetStencilState(const DeviceStencilState* state, int stencilRef);
+ GFX_API void SetSRGBWrite (const bool);
+ GFX_API bool GetSRGBWrite ();
+
+ GFX_API void SetWorldMatrix( const float matrix[16] );
+ GFX_API void SetViewMatrix( const float matrix[16] );
+ GFX_API void SetProjectionMatrix(const Matrix4x4f& matrix);
+ GFX_API void GetMatrix( float outMatrix[16] ) const;
+
+ GFX_API const float* GetWorldMatrix() const ;
+ GFX_API const float* GetViewMatrix() const ;
+ GFX_API const float* GetProjectionMatrix() const ;
+ GFX_API const float* GetDeviceProjectionMatrix() const;
+
+ GFX_API void SetNormalizationBackface( NormalizationMode mode, bool backface );
+ GFX_API void SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial );
+ GFX_API void SetMaterial( const float ambient[4], const float diffuse[4], const float specular[4], const float emissive[4], const float shininess );
+ GFX_API void SetColor( const float color[4] );
+ GFX_API void SetViewport( int x, int y, int width, int height );
+ GFX_API void GetViewport( int* port ) const;
+
+ GFX_API void SetScissorRect( int x, int y, int width, int height );
+ GFX_API void DisableScissor();
+ GFX_API bool IsScissorEnabled() const;
+ GFX_API void GetScissorRect( int values[4] ) const;
+
+ GFX_API bool IsCombineModeSupported( unsigned int combiner );
+ GFX_API TextureCombinersHandle CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular );
+ GFX_API void DeleteTextureCombiners( TextureCombinersHandle& textureCombiners );
+ GFX_API void SetTextureCombinersThreadable( TextureCombinersHandle textureCombiners, const TexEnvData* texEnvData, const Vector4f* texColors );
+ GFX_API void SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props );
+
+ GFX_API void SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias);
+ GFX_API void SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace );
+ GFX_API void SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16]);
+ GFX_API void SetTextureName ( TextureID texture, const char* name ) { }
+
+ GFX_API void SetShadersThreadable (GpuProgram* programs[kShaderTypeCount], const GpuProgramParameters* params[kShaderTypeCount], UInt8 const * const paramsBuffer[kShaderTypeCount]);
+ GFX_API bool IsShaderActive( ShaderType type ) const;
+ GFX_API void DestroySubProgram( ShaderLab::SubProgram* subprogram );
+
+ GFX_API void DisableLights( int startLight );
+ GFX_API void SetLight( int light, const GfxVertexLight& data);
+ GFX_API void SetAmbient( const float ambient[4] );
+
+ GFX_API void EnableFog(const GfxFogParams& fog);
+ GFX_API void DisableFog();
+
+ GFX_API VBO* CreateVBO();
+ GFX_API void DeleteVBO( VBO* vbo );
+ GFX_API DynamicVBO& GetDynamicVBO();
+
+ GFX_API RenderSurfaceHandle CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags);
+ GFX_API RenderSurfaceHandle CreateRenderDepthSurface(TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags);
+ GFX_API void DestroyRenderSurface(RenderSurfaceHandle& rs);
+ GFX_API void SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face = kCubeFaceUnknown);
+ GFX_API void ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle);
+ GFX_API void ResolveDepthIntoTexture (RenderSurfaceHandle colorHandle, RenderSurfaceHandle depthHandle);
+ GFX_API RenderSurfaceHandle GetActiveRenderColorSurface(int index);
+ GFX_API RenderSurfaceHandle GetActiveRenderDepthSurface();
+ GFX_API void SetSurfaceFlags(RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags);
+
+ GFX_API void UploadTexture2D( TextureID texture, TextureDimension dimension, UInt8* srcData, int srcSize, int width, int height, TextureFormat format, int mipCount, UInt32 uploadFlags, int skipMipLevels, TextureUsageMode usageMode, TextureColorSpace colorSpace );
+ GFX_API void UploadTextureSubData2D( TextureID texture, UInt8* srcData, int srcSize, int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace );
+ GFX_API void UploadTextureCube( TextureID texture, UInt8* srcData, int srcSize, int faceDataSize, int size, TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace );
+ GFX_API void UploadTexture3D( TextureID texture, UInt8* srcData, int srcSize, int width, int height, int depth, TextureFormat format, int mipCount, UInt32 uploadFlags );
+ GFX_API void DeleteTexture( TextureID texture );
+
+ GFX_API PresentMode GetPresentMode();
+
+ GFX_API void BeginFrame();
+ GFX_API void EndFrame();
+ GFX_API void PresentFrame();
+ GFX_API bool IsValidState();
+ GFX_API bool HandleInvalidState();
+ GFX_API void FinishRendering();
+
+ // Immediate mode rendering
+ GFX_API void ImmediateVertex( float x, float y, float z );
+ GFX_API void ImmediateNormal( float x, float y, float z );
+ GFX_API void ImmediateColor( float r, float g, float b, float a );
+ GFX_API void ImmediateTexCoordAll( float x, float y, float z );
+ GFX_API void ImmediateTexCoord( int unit, float x, float y, float z );
+ GFX_API void ImmediateBegin( GfxPrimitiveType type );
+ GFX_API void ImmediateEnd();
+
+ GFX_API bool CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 );
+ GFX_API bool ReadbackImage( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY );
+ GFX_API void GrabIntoRenderTexture(RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height);
+
+ GFX_API void BeforeDrawCall( bool immediateMode );
+
+ GFX_API bool IsPositionRequiredForTexGen(int texStageIndex) const { return false; }
+ GFX_API bool IsNormalRequiredForTexGen(int texStageIndex) const { return false; }
+ GFX_API bool IsPositionRequiredForTexGen() const { return false; }
+ GFX_API bool IsNormalRequiredForTexGen() const { return false; }
+
+ GFX_API void DiscardContents (RenderSurfaceHandle& rs) {}
+
+#if ENABLE_PROFILER
+ GFX_API void BeginProfileEvent (const char* name);
+ GFX_API void EndProfileEvent ();
+
+ TimerQueriesD3D9& GetTimerQueries() {return m_TimerQueriesD3D9;}
+ GFX_API GfxTimerQuery* CreateTimerQuery();
+ GFX_API void DeleteTimerQuery(GfxTimerQuery* query);
+ GFX_API void BeginTimerQueries();
+ GFX_API void EndTimerQueries();
+ #endif
+
+ #if UNITY_EDITOR
+ GFX_API void SetAntiAliasFlag( bool aa );
+ GFX_API void DrawUserPrimitives( GfxPrimitiveType type, int vertexCount, UInt32 vertexChannels, const void* data, int stride );
+ GFX_API int GetCurrentTargetAA() const;
+ GFX_API GfxDeviceWindow* CreateGfxWindow( HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias );
+ #endif
+
+ GFX_API int GetCurrentTargetWidth() const;
+ GFX_API int GetCurrentTargetHeight() const;
+ GFX_API void SetCurrentTargetSize(int width, int height);
+ GFX_API void SetCurrentWindowSize(int width, int height);
+
+ GFX_API void* GetNativeGfxDevice();
+ GFX_API void* GetNativeTexturePointer(TextureID id);
+ GFX_API intptr_t CreateExternalTextureFromNative(intptr_t nativeTex);
+ GFX_API void UpdateExternalTextureFromNative(TextureID tex, intptr_t nativeTex);
+
+ GFX_API void ResetDynamicResources();
+
+ IDirect3DVertexBuffer9* GetAllWhiteVertexStream();
+
+ VertexDeclarations& GetVertexDecls() { return m_VertexDecls; }
+
+ const DeviceStateD3D& GetState() const { return m_State; }
+ DeviceStateD3D& GetState() { return m_State; }
+ VertexShaderConstantCache& GetVertexShaderConstantCache() { return m_VSConstantCache; }
+ PixelShaderConstantCache& GetPixelShaderConstantCache() { return m_PSConstantCache; }
+
+ const VertexPipeConfig& GetVertexPipeConfig() const { return m_VertexConfig; }
+ VertexPipeConfig& GetVertexPipeConfig() { return m_VertexConfig; }
+ const VertexPipeDataD3D9& GetVertexPipeData() const { return m_VertexData; }
+ VertexPipeDataD3D9& GetVertexPipeData() { return m_VertexData; }
+ TexturesD3D9& GetTextures() { return m_Textures; }
+
+ void PushEventQuery();
+
+private:
+
+ DeviceStateD3D m_State;
+ ImmediateModeD3D m_Imm;
+ VertexPipeConfig m_VertexConfig;
+ TransformState m_TransformState;
+ VertexPipeDataD3D9 m_VertexData;
+ VertexPipePrevious m_VertexPrevious;
+
+ DeviceBlendStateD3D9* m_CurrBlendState;
+ DeviceDepthStateD3D9* m_CurrDepthState;
+ const DeviceStencilStateD3D9* m_CurrStencilState;
+ DeviceRasterState* m_CurrRasterState;
+ int m_CurrTargetWidth;
+ int m_CurrTargetHeight;
+ int m_CurrWindowWidth;
+ int m_CurrWindowHeight;
+
+ IDirect3DVertexBuffer9* m_AllWhiteVertexStream;
+
+ VertexDeclarations m_VertexDecls;
+ TexturesD3D9 m_Textures;
+ DynamicVBO* m_DynamicVBO;
+
+ CachedBlendStates m_CachedBlendStates;
+ CachedDepthStates m_CachedDepthStates;
+ CachedStencilStates m_CachedStencilStates;
+ CachedRasterStates m_CachedRasterStates;
+
+ VertexShaderConstantCache m_VSConstantCache;
+ PixelShaderConstantCache m_PSConstantCache;
+
+#if ENABLE_PROFILER
+ TimerQueriesD3D9 m_TimerQueriesD3D9;
+#endif
+};
+
+GfxDeviceD3D9& GetD3D9GfxDevice();
diff --git a/Runtime/GfxDevice/d3d/GpuProgramsD3D.cpp b/Runtime/GfxDevice/d3d/GpuProgramsD3D.cpp
new file mode 100644
index 0000000..9a67a54
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/GpuProgramsD3D.cpp
@@ -0,0 +1,474 @@
+#include "UnityPrefix.h"
+#include "GpuProgramsD3D.h"
+#include "External/shaderlab/Library/ShaderLabErrors.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Math/Vector4.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/DirectX/builds/dx9include/d3dx9.h"
+#include "D3D9Context.h"
+#include "Runtime/GfxDevice/ShaderConstantCache.h"
+#include "D3D9Utils.h"
+#include "ShaderPatchingD3D9.h"
+
+#define ENABLE_GPU_PROGRAM_STATS 0
+
+
+#if ENABLE_GPU_PROGRAM_STATS
+typedef std::map<ShaderLab::FastPropertyName, int> PropertyCount;
+PropertyCount s_StatCounts[kShaderTypeCount];
+void PrintDebugGpuProgramStats ()
+{
+ typedef std::pair<std::string, int> NameIntPair;
+ struct Sorter {
+ bool operator() (const NameIntPair& a, const NameIntPair& b) const {
+ return a.second > b.second;
+ }
+ };
+ for (int i = kShaderVertex; i < kShaderTypeCount; ++i)
+ {
+ std::vector<NameIntPair> sorted;
+ sorted.reserve (s_StatCounts[i].size());
+ int totalCount = 0;
+ for (PropertyCount::const_iterator it = s_StatCounts[i].begin(); it != s_StatCounts[i].end(); ++it)
+ {
+ sorted.push_back (std::make_pair(it->first.GetName(), it->second));
+ totalCount += it->second;
+ }
+ std::sort (sorted.begin(), sorted.end(), Sorter());
+ printf_console ("%i Shader Stats: %i props, %i requests\n", i, sorted.size(), totalCount);
+ for (size_t j = 0; j < sorted.size(); ++j)
+ {
+ printf_console (" %-25s %6i %5.1f%%\n", sorted[j].first.c_str(), sorted[j].second, sorted[j].second*100.0/totalCount);
+ }
+ s_StatCounts[i].clear();
+ }
+}
+#define ADD_TO_VS_STATS(name) ++s_StatCounts[kShaderVertex][name]
+#define ADD_TO_PS_STATS(name) ++s_StatCounts[kShaderFragment][name]
+#else
+#define ADD_TO_VS_STATS(name)
+#define ADD_TO_PS_STATS(name)
+#endif
+
+
+VertexShaderConstantCache& GetD3D9VertexShaderConstantCache(); // GfxDeviceD3D9.cpp
+PixelShaderConstantCache& GetD3D9PixelShaderConstantCache(); // GfxDeviceD3D9.cpp
+
+
+// non static; used by CombinerD3D.cpp and VertexPipeD3D9.cpp
+ID3DXBuffer* AssembleD3DShader (const std::string& source)
+{
+ ID3DXBuffer *compiledShader, *compileErrors;
+
+ // Skip validation of shaders at assembly time when in release mode. Saves
+ // some time when loading them.
+ DWORD flags = D3DXSHADER_SKIPVALIDATION;
+ #if DEBUGMODE
+ flags = 0;
+ #endif
+
+ HRESULT hr = D3DXAssembleShader( source.c_str(), source.size(), NULL, NULL, flags, &compiledShader, &compileErrors );
+ if( FAILED(hr) )
+ {
+ if (compileErrors && compileErrors->GetBufferSize() > 0)
+ {
+ std::string error = Format ("Shader error in '%s': D3D shader assembly failed with: %s\nShader Assembly: %s", g_LastParsedShaderName.c_str(), (const char*)compileErrors->GetBufferPointer(), source.c_str());
+ compileErrors->Release();
+ ErrorString (error);
+ }
+ if( compiledShader )
+ compiledShader->Release();
+ return NULL;
+ }
+
+ return compiledShader;
+}
+
+// --------------------------------------------------------------------------
+
+template <typename CACHE>
+static const UInt8* ApplyValueParametersD3D9 (CACHE& constantCache, const UInt8* buffer, const GpuProgramParameters::ValueParameterArray& valueParams)
+{
+ GpuProgramParameters::ValueParameterArray::const_iterator valueParamsEnd = valueParams.end();
+ for (GpuProgramParameters::ValueParameterArray::const_iterator i = valueParams.begin(); i != valueParamsEnd; ++i)
+ {
+ if (i->m_RowCount == 1 && i->m_ArraySize == 1)
+ {
+ // Apply vector parameters
+ const Vector4f* val = reinterpret_cast<const Vector4f*>(buffer);
+ constantCache.SetValues(i->m_Index, val->GetPtr(), 1);
+ buffer += sizeof(Vector4f);
+ }
+ else
+ {
+ // matrix/array
+ int size = *reinterpret_cast<const int*>(buffer); buffer += sizeof(int);
+ Assert (i->m_RowCount == 4 && size == 16);
+ const Matrix4x4f* val = reinterpret_cast<const Matrix4x4f*>(buffer);
+ Matrix4x4f transposed;
+ TransposeMatrix4x4 (val, &transposed);
+ const float *ptr = transposed.GetPtr();
+ constantCache.SetValues (i->m_Index, ptr, 4);
+ buffer += size * sizeof(float);
+ }
+ }
+ return buffer;
+}
+
+
+
+// --------------------------------------------------------------------------
+
+D3D9VertexShader::D3D9VertexShader( const std::string& source )
+: m_FogFailed(0)
+{
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ m_Shaders[i] = NULL;
+ }
+ m_ImplType = kShaderImplVertex;
+ if( !Create(source) )
+ m_NotSupported = true;
+}
+
+D3D9VertexShader::~D3D9VertexShader ()
+{
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ if( m_Shaders[i] )
+ {
+ ULONG refCount = m_Shaders[i]->Release();
+ AssertIf( refCount != 0 );
+ }
+ }
+}
+
+
+bool D3D9VertexShader::Create( const std::string& source )
+{
+ // fast skip 3.0 shaders on unsupporting hardware
+ bool isShaderModel3 = !strncmp(source.c_str(), "vs_3_0", 6);
+ if( gGraphicsCaps.shaderCaps < kShaderLevel3 && isShaderModel3 )
+ return false;
+
+ if (isShaderModel3)
+ m_GpuProgramLevel = kGpuProgramSM3;
+ else
+ {
+ bool isShaderModel1 = !strncmp(source.c_str(), "vs_1_1", 6);
+ m_GpuProgramLevel = isShaderModel1 ? kGpuProgramSM1 : kGpuProgramSM2;
+ }
+
+ HRESULT hr;
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ // assemble shader
+ ID3DXBuffer *compiledShader = AssembleD3DShader( source );
+ if( !compiledShader )
+ {
+ return false;
+ }
+
+ // create shader
+ hr = dev->CreateVertexShader( (const DWORD*)compiledShader->GetBufferPointer(), &m_Shaders[0] );
+ compiledShader->Release();
+ if( FAILED(hr) )
+ {
+ printf_console( "D3D shader create error for shader %s\n", source.c_str() );
+ return false;
+ }
+
+ if (isShaderModel3)
+ {
+ m_SourceForFog = source;
+ }
+
+ return true;
+}
+
+void D3D9VertexShader::ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer)
+{
+ GfxDevice& device = GetRealGfxDevice();
+ IDirect3DDevice9* dev = GetD3DDevice();
+ VertexShaderConstantCache& constantCache = GetD3D9VertexShaderConstantCache();
+
+ const GpuProgramParameters::ValueParameterArray& valueParams = params.GetValueParams();
+ buffer = ApplyValueParametersD3D9<VertexShaderConstantCache>(constantCache, buffer, valueParams);
+
+ // Apply textures
+ if (gGraphicsCaps.hasVertexTextures)
+ {
+ const GpuProgramParameters::TextureParameterList& textureParams = params.GetTextureParams();
+ const GpuProgramParameters::TextureParameterList::const_iterator textureParamsEnd = textureParams.end();
+ for( GpuProgramParameters::TextureParameterList::const_iterator i = textureParams.begin(); i != textureParamsEnd; ++i )
+ {
+ const GpuProgramParameters::TextureParameter& t = *i;
+ const TexEnvData* texdata = reinterpret_cast<const TexEnvData*>(buffer);
+ device.SetTexture (kShaderVertex, t.m_Index, 0, texdata->textureID, static_cast<TextureDimension>(texdata->texDim), 0);
+ buffer += sizeof(*texdata);
+ }
+ }
+}
+
+IDirect3DVertexShader9* D3D9VertexShader::GetShader (FogMode fog, bool& outResetToNoFog)
+{
+ int index = 0;
+ outResetToNoFog = false;
+ if (fog > kFogDisabled && !m_SourceForFog.empty())
+ {
+ Assert (fog >= 0 && fog < kFogModeCount);
+
+ if (m_Shaders[fog])
+ {
+ // already have patched fog shader
+ index = fog;
+ }
+ else if (!(m_FogFailed & (1<<fog)))
+ {
+ // patch fog shader on demand
+ std::string src = m_SourceForFog;
+
+ if (PatchVertexShaderFogD3D9 (src))
+ {
+ // assemble & create the shader
+ ID3DXBuffer *compiledShader = AssembleD3DShader (src);
+ if (compiledShader)
+ {
+ HRESULT hr = GetD3DDevice()->CreateVertexShader ((const DWORD*)compiledShader->GetBufferPointer(), &m_Shaders[fog]);
+ compiledShader->Release();
+ if (SUCCEEDED(hr))
+ {
+ index = fog;
+ }
+ else
+ {
+ printf_console ("D3D vertex shader create error for patched fog mode %d shader %s\n", (int)fog, src.c_str());
+ }
+ }
+ }
+ }
+ if (index == 0)
+ {
+ outResetToNoFog = true;
+ m_FogFailed |= (1<<fog);
+ }
+ }
+ return m_Shaders[index];
+}
+
+// --------------------------------------------------------------------------
+
+D3D9PixelShader::D3D9PixelShader( const std::string& source )
+: m_FogFailed(0)
+{
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ m_Shaders[i] = NULL;
+ m_FogRegisters[i] = NULL;
+ }
+ m_ImplType = kShaderImplFragment;
+ if( !Create(source) )
+ m_NotSupported = true;
+}
+
+D3D9PixelShader::~D3D9PixelShader ()
+{
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ if( m_Shaders[i] )
+ {
+ ULONG refCount = m_Shaders[i]->Release();
+ AssertIf( refCount != 0 );
+ }
+ }
+}
+
+bool D3D9PixelShader::Create( const std::string& source )
+{
+ // fast skip 3.0 shaders on unsupporting hardware
+ bool isShaderModel3 = !strncmp(source.c_str(), "ps_3_0", 6);
+ if( gGraphicsCaps.shaderCaps < kShaderLevel3 && isShaderModel3 )
+ return false;
+
+ m_GpuProgramLevel = isShaderModel3 ? kGpuProgramSM3 : kGpuProgramSM2;
+
+ HRESULT hr;
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ // assemble shader
+ ID3DXBuffer *compiledShader = AssembleD3DShader( source );
+ if( !compiledShader )
+ {
+ return false;
+ }
+
+ // create shader
+ hr = dev->CreatePixelShader( (const DWORD*)compiledShader->GetBufferPointer(), &m_Shaders[0] );
+ compiledShader->Release();
+ if( FAILED(hr) )
+ {
+ printf_console( "D3D shader create error for shader %s\n", source.c_str() );
+ return false;
+ }
+
+ if (isShaderModel3)
+ {
+ m_SourceForFog = source;
+ }
+
+ return true;
+}
+
+void D3D9PixelShader::ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer)
+{
+ GfxDevice& device = GetRealGfxDevice();
+ IDirect3DDevice9* dev = GetD3DDevice();
+ PixelShaderConstantCache& constantCache = GetD3D9PixelShaderConstantCache();
+
+ const GpuProgramParameters::ValueParameterArray& valueParams = params.GetValueParams();
+ buffer = ApplyValueParametersD3D9<PixelShaderConstantCache>(constantCache, buffer, valueParams);
+
+ // Apply textures
+ const GpuProgramParameters::TextureParameterList& textureParams = params.GetTextureParams();
+ GpuProgramParameters::TextureParameterList::const_iterator textureParamsEnd = textureParams.end();
+ for( GpuProgramParameters::TextureParameterList::const_iterator i = textureParams.begin(); i != textureParamsEnd; ++i )
+ {
+ const GpuProgramParameters::TextureParameter& t = *i;
+ const TexEnvData* texdata = reinterpret_cast<const TexEnvData*>(buffer);
+ ApplyTexEnvData (t.m_Index, t.m_SamplerIndex, *texdata);
+ buffer += sizeof(*texdata);
+ }
+
+ // Apply fog parameters if needed
+ if (!m_SourceForFog.empty())
+ {
+ const GfxFogParams& fog = device.GetFogParams();
+ if (fog.mode > kFogDisabled && !(m_FogFailed & (1<<fog.mode)))
+ {
+ int reg = m_FogRegisters[fog.mode];
+ constantCache.SetValues (reg, fog.color.GetPtr(), 1);
+ float params[4];
+ params[0] = fog.density * 1.2011224087f ; // density / sqrt(ln(2))
+ params[1] = fog.density * 1.4426950408f; // density / ln(2)
+ if (fog.mode == kFogLinear)
+ {
+ float diff = fog.end - fog.start;
+ float invDiff = Abs(diff) > 0.0001f ? 1.0f/diff : 0.0f;
+ params[2] = -invDiff;
+ params[3] = fog.end * invDiff;
+ }
+ else
+ {
+ params[2] = 0.0f;
+ params[3] = 0.0f;
+ }
+ constantCache.SetValues (reg+1, params, 1);
+ }
+ }
+}
+
+static int FindUnusedConstantRegister (const std::string& src, const GpuProgramParameters& params)
+{
+ int maxRegisterUsed = -1;
+
+ const GpuProgramParameters::ValueParameterArray& valueParams = params.GetValueParams();
+ for (GpuProgramParameters::ValueParameterArray::const_iterator it = valueParams.begin(), itEnd = valueParams.end(); it != itEnd; ++it)
+ {
+ int idx = it->m_Index + it->m_RowCount - 1;
+ if (idx > maxRegisterUsed)
+ maxRegisterUsed = idx;
+ }
+
+ // Built-ins
+ const BuiltinShaderParamIndices& builtins = params.GetBuiltinParams();
+ for (int i = 0; i < kShaderInstanceMatCount; ++i)
+ {
+ int index = builtins.mat[i].gpuIndex;
+ if (index >= 0 && index + 3 > maxRegisterUsed)
+ maxRegisterUsed = index + 3;
+ }
+
+ // Explicit constants in the shader ("def c*")
+ size_t pos = 0;
+ const size_t n = src.size();
+ while ((pos = src.find("def c", pos)) != std::string::npos)
+ {
+ pos += 5; // skip "def c"
+ int reg = -1;
+ sscanf(src.c_str() + pos, "%d", &reg);
+ if (reg > maxRegisterUsed)
+ maxRegisterUsed = reg;
+ }
+
+ return maxRegisterUsed + 1;
+}
+
+IDirect3DPixelShader9* D3D9PixelShader::GetShader(FogMode fog, const GpuProgramParameters& params)
+{
+ int index = 0;
+ if (fog > kFogDisabled && !m_SourceForFog.empty())
+ {
+ Assert (fog >= 0 && fog < kFogModeCount);
+
+ if (m_Shaders[fog])
+ {
+ // already have patched fog shader
+ index = fog;
+ }
+ else if (!(m_FogFailed & (1<<fog)))
+ {
+ // patch fog shader on demand
+ std::string src = m_SourceForFog;
+
+ // find constant register that we'll use to store fog params
+ int reg = FindUnusedConstantRegister (src, params);
+ m_FogRegisters[fog] = reg;
+
+ if (PatchPixelShaderFogD3D9 (src, fog, reg, reg+1))
+ {
+ // assemble & create the shader
+ ID3DXBuffer *compiledShader = AssembleD3DShader (src);
+ if (compiledShader)
+ {
+ HRESULT hr = GetD3DDevice()->CreatePixelShader ((const DWORD*)compiledShader->GetBufferPointer(), &m_Shaders[fog]);
+ compiledShader->Release();
+ if (SUCCEEDED(hr))
+ {
+ index = fog;
+ }
+ else
+ {
+ printf_console ("D3D pixel shader create error for patched fog mode %d shader %s\n", (int)fog, src.c_str());
+ }
+ }
+ }
+
+ if (index == 0)
+ m_FogFailed |= (1<<fog);
+ }
+ }
+ return m_Shaders[index];
+}
+
+
+
+// --------------------------------------------------------------------------
+
+#if ENABLE_UNIT_TESTS
+#include "External/UnitTest++/src/UnitTest++.h"
+SUITE (GpuProgramsD3DTests)
+{
+
+TEST(FindUnusedConstantRegisterCanHandleUnsortedParams)
+{
+ GpuProgramParameters pp;
+ pp.AddVectorParam(1,kShaderParamFloat,4,"A",-1,NULL);
+ pp.AddVectorParam(0,kShaderParamFloat,4,"B",-1,NULL);
+ pp.MakeReady(); // this does sort, but sorts by name; NOT the GPU index!
+ CHECK_EQUAL(2,FindUnusedConstantRegister("", pp));
+}
+
+} // SUITE
+#endif // ENABLE_UNIT_TESTS
diff --git a/Runtime/GfxDevice/d3d/GpuProgramsD3D.h b/Runtime/GfxDevice/d3d/GpuProgramsD3D.h
new file mode 100644
index 0000000..6b21fa9
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/GpuProgramsD3D.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "D3D9Includes.h"
+#include "Runtime/GfxDevice/GpuProgram.h"
+
+
+class D3D9VertexShader : public GpuProgram {
+public:
+ D3D9VertexShader( const std::string& source );
+ virtual ~D3D9VertexShader();
+
+ virtual void ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer);
+ IDirect3DVertexShader9* GetShader(FogMode fog, bool& outResetToNoFog);
+ IDirect3DVertexShader9* GetShaderAtFogIndex(FogMode fog) { return m_Shaders[fog]; }
+
+private:
+ bool Create( const std::string& source );
+
+ std::string m_SourceForFog; // original source, used for fog patching if needed
+ IDirect3DVertexShader9* m_Shaders[kFogModeCount];
+ unsigned m_FogFailed; // bit per fog mode
+};
+
+class D3D9PixelShader : public GpuProgram {
+public:
+ D3D9PixelShader( const std::string& source );
+ virtual ~D3D9PixelShader();
+
+ virtual void ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer);
+ IDirect3DPixelShader9* GetShader(FogMode fog, const GpuProgramParameters& params);
+ IDirect3DPixelShader9* GetShaderAtFogIndex(FogMode fog) { return m_Shaders[fog]; }
+
+private:
+ bool Create( const std::string& source );
+
+ std::string m_SourceForFog; // original source, used for fog patching if needed
+ IDirect3DPixelShader9* m_Shaders[kFogModeCount];
+ int m_FogRegisters[kFogModeCount];
+ unsigned m_FogFailed; // bit per fog mode
+};
diff --git a/Runtime/GfxDevice/d3d/GraphicsCapsD3D9.cpp b/Runtime/GfxDevice/d3d/GraphicsCapsD3D9.cpp
new file mode 100644
index 0000000..f1d95c8
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/GraphicsCapsD3D9.cpp
@@ -0,0 +1,384 @@
+#include "UnityPrefix.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "D3D9Context.h"
+#include "Runtime/Utilities/Utility.h"
+#include "PlatformDependent/Win/WinDriverUtils.h"
+#include "D3D9Utils.h"
+#include <Shlwapi.h>
+
+#define CAPS_DEBUG_DISABLE_RT 0
+
+
+extern D3DFORMAT kD3D9RenderTextureFormats[kRTFormatCount];
+
+
+extern D3DDEVTYPE g_D3DDevType;
+extern DWORD g_D3DAdapter;
+
+static bool IsTextureFormatSupported( D3DFORMAT format )
+{
+ if( format == D3DFMT_UNKNOWN )
+ return false;
+ HRESULT hr = GetD3DObject()->CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), 0, D3DRTYPE_TEXTURE, format );
+ return SUCCEEDED( hr );
+}
+static bool IsSRGBTextureReadSupported( D3DFORMAT format )
+{
+ if( format == D3DFMT_UNKNOWN )
+ return false;
+ HRESULT hr = GetD3DObject()->CheckDeviceFormat (g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_QUERY_SRGBREAD, D3DRTYPE_TEXTURE, format);
+ return SUCCEEDED( hr );
+}
+static bool IsSRGBTextureWriteSupported( D3DFORMAT format )
+{
+ if( format == D3DFMT_UNKNOWN )
+ return false;
+ HRESULT hr = GetD3DObject()->CheckDeviceFormat (g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_QUERY_SRGBWRITE, D3DRTYPE_TEXTURE, format);
+ return SUCCEEDED( hr );
+}
+static bool IsRenderTextureFormatSupported( D3DFORMAT format )
+{
+ if( format == D3DFMT_UNKNOWN )
+ return false;
+ HRESULT hr = GetD3DObject()->CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, format );
+ return SUCCEEDED( hr );
+}
+
+D3DFORMAT GetD3D9TextureFormat( TextureFormat inFormat ); // TexturesD3D9.cpp
+
+
+enum {
+ kVendorDummyRef = 0x0000,
+ kVendor3DLabs = 0x3d3d,
+ kVendorMatrox = 0x102b,
+ kVendorS3 = 0x5333,
+ kVendorSIS = 0x1039,
+ kVendorXGI = 0x18ca,
+ kVendorIntel = 0x8086,
+ kVendorATI = 0x1002,
+ kVendorNVIDIA = 0x10de,
+ kVendorTrident = 0x1023,
+ kVendorImgTech = 0x104a,
+ kVendorVIAS3G = 0x1106,
+ kVendor3dfx = 0x121a,
+ kVendorParallels= 0x1ab8,
+ kVendorMicrosoft= 0x1414,
+ kVendorVMWare = 0x15ad,
+};
+struct KnownVendors {
+ DWORD vendorId;
+ const char* name;
+};
+static KnownVendors s_KnownVendors[] = {
+ { kVendorDummyRef, "REFERENCE" },
+ { kVendor3DLabs, "3dLabs" },
+ { kVendorMatrox, "Matrox" },
+ { kVendorS3, "S3" },
+ { kVendorSIS, "SIS" },
+ { kVendorXGI, "XGI" },
+ { kVendorIntel, "Intel" },
+ { kVendorATI, "ATI" },
+ { kVendorNVIDIA, "NVIDIA" },
+ { kVendorTrident, "Trident" },
+ { kVendorImgTech, "Imagination Technologies" },
+ { kVendorVIAS3G, "VIA/S3" },
+ { kVendor3dfx, "3dfx" },
+ { kVendorParallels, "Parallels" },
+ { kVendorMicrosoft, "Microsoft" },
+ { kVendorVMWare, "VMWare" },
+};
+static int kKnownVendorsSize = sizeof(s_KnownVendors)/sizeof(s_KnownVendors[0]);
+
+
+void GraphicsCaps::InitD3D9()
+{
+ IDirect3D9* d3dobject = GetD3DObject();
+ d3dobject->GetDeviceCaps( g_D3DAdapter, g_D3DDevType, &d3d.d3dcaps );
+
+ // get renderer, vendor & driver information
+ D3DADAPTER_IDENTIFIER9 adapterInfo;
+ d3dobject->GetAdapterIdentifier( g_D3DAdapter, 0, &adapterInfo );
+ adapterInfo.Driver[MAX_DEVICE_IDENTIFIER_STRING-1] = 0;
+ adapterInfo.Description[MAX_DEVICE_IDENTIFIER_STRING-1] = 0;
+ adapterInfo.DeviceName[31] = 0;
+ rendererString = adapterInfo.Description;
+
+ if (g_D3DDevType == D3DDEVTYPE_REF)
+ {
+ adapterInfo.VendorId = kVendorDummyRef;
+ rendererString = "REF on " + rendererString;
+ }
+
+ int i;
+ for( i = 0; i < kKnownVendorsSize; ++i )
+ {
+ if( s_KnownVendors[i].vendorId == adapterInfo.VendorId )
+ {
+ vendorString = s_KnownVendors[i].name;
+ break;
+ }
+ }
+ if( i == kKnownVendorsSize )
+ {
+ vendorString = Format( "Unknown (ID=%x)", adapterInfo.VendorId );
+ }
+ windriverutils::VersionInfo driverVersion( HIWORD(adapterInfo.DriverVersion.HighPart), LOWORD(adapterInfo.DriverVersion.HighPart),
+ HIWORD(adapterInfo.DriverVersion.LowPart), LOWORD(adapterInfo.DriverVersion.LowPart) );
+ driverVersionString = Format( "%s %i.%i.%i.%i", adapterInfo.Driver,
+ HIWORD(adapterInfo.DriverVersion.HighPart), LOWORD(adapterInfo.DriverVersion.HighPart),
+ HIWORD(adapterInfo.DriverVersion.LowPart), LOWORD(adapterInfo.DriverVersion.LowPart) );
+ driverLibraryString = driverVersionString;
+ fixedVersionString = "Direct3D 9.0c [" + driverVersionString + ']';
+
+ rendererID = adapterInfo.DeviceId;
+ vendorID = adapterInfo.VendorId;
+
+ // We can't use GetAvailableTextureMem here because the device is not created yet!
+ // And besides that, it would return much more than VRAM on Vista (virtualization and so on).
+ // Use WMI instead.
+ int vramMB;
+ const char* vramMethod = "";
+ if (g_D3DDevType != D3DDEVTYPE_REF)
+ vramMB = windriverutils::GetVideoMemorySizeMB (d3dobject->GetAdapterMonitor(g_D3DAdapter), &vramMethod);
+ else
+ vramMB = 128;
+ videoMemoryMB = vramMB;
+
+ // On windows, we always output D3D info. There is so much variety that it always helps!
+ printf_console( "Direct3D:\n" );
+ printf_console( " Version: %s\n", fixedVersionString.c_str() );
+ printf_console( " Renderer: %s\n", rendererString.c_str() );
+ printf_console( " Vendor: %s\n", vendorString.c_str() );
+ printf_console( " VRAM: %i MB (via %s)\n", (int)videoMemoryMB, vramMethod );
+
+ maxVSyncInterval = 0;
+ if( d3d.d3dcaps.PresentationIntervals & D3DPRESENT_INTERVAL_ONE )
+ {
+ maxVSyncInterval = 1;
+ if( d3d.d3dcaps.PresentationIntervals & D3DPRESENT_INTERVAL_TWO )
+ maxVSyncInterval = 2;
+ }
+
+ DWORD declTypesFloat16 = D3DDTCAPS_FLOAT16_2 | D3DDTCAPS_FLOAT16_4;
+ has16BitFloatVertex = (d3d.d3dcaps.DeclTypes & declTypesFloat16) == declTypesFloat16;
+ needsToSwizzleVertexColors = true;
+
+ bool usesSoftwareVP = !(d3d.d3dcaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT);
+ if( usesSoftwareVP )
+ maxLights = 8; // software T&L always has 8 lights
+ else
+ maxLights = clamp<unsigned int>( d3d.d3dcaps.MaxActiveLights, 0, 8 );
+
+ // Texture sizes
+ maxTextureSize = std::min( d3d.d3dcaps.MaxTextureWidth, d3d.d3dcaps.MaxTextureHeight );
+ maxRenderTextureSize = maxTextureSize;
+ maxCubeMapSize = maxTextureSize;
+
+ has3DTexture = d3d.d3dcaps.TextureCaps & D3DPTEXTURECAPS_VOLUMEMAP;
+ maxTexUnits = d3d.d3dcaps.MaxSimultaneousTextures;
+ maxTexImageUnits = 16;
+ maxTexCoords = d3d.d3dcaps.MaxSimultaneousTextures;
+ if (maxTexCoords > 8)
+ maxTexCoords = 8;
+
+ // In theory, vertex texturing is texture format dependent. However, in practice the caps lie,
+ // especially on NVIDIA hardware.
+ //
+ // ATI cards: all DX10+ GPUs report all texture formats as vertex texture capable (good!)
+ // Intel cards: all SM3.0+ GPUs report all texture formats as vertex texture capable (good!)
+ // NV cards: all DX10+ GPUs report only floating point formats as capable, but all others actually work as well.
+ // GeForce 6&7 only report R32F and A32R32G32B32F, and only those work.
+ //
+ // So we check for R16F support; this will return true on all GPUs that can handle ALL
+ // texture formats.
+ hasVertexTextures = ((LOWORD(d3d.d3dcaps.VertexShaderVersion) >= (3<<8)+0)) &&
+ SUCCEEDED(d3dobject->CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_QUERY_VERTEXTEXTURE, D3DRTYPE_TEXTURE, D3DFMT_R16F));
+
+ hasAnisoFilter = d3d.d3dcaps.RasterCaps & D3DPRASTERCAPS_ANISOTROPY;
+ maxAnisoLevel = hasAnisoFilter ? d3d.d3dcaps.MaxAnisotropy : 1;
+ hasMipLevelBias = d3d.d3dcaps.RasterCaps & D3DPRASTERCAPS_MIPMAPLODBIAS;
+
+ for( i = 0; i < kTexFormatPCCount; ++i )
+ {
+ d3d.hasBaseTextureFormat[i] = IsTextureFormatSupported( GetD3D9TextureFormat( static_cast<TextureFormat>(i) ) );
+ supportsTextureFormat[i] = d3d.hasBaseTextureFormat[i];
+ }
+
+ hasS3TCCompression = IsTextureFormatSupported(D3DFMT_DXT1) && IsTextureFormatSupported(D3DFMT_DXT3) && IsTextureFormatSupported(D3DFMT_DXT5);
+ d3d.hasTextureFormatA8 = IsTextureFormatSupported(D3DFMT_A8);
+ d3d.hasTextureFormatL8 = IsTextureFormatSupported(D3DFMT_L8);
+ d3d.hasTextureFormatA8L8 = IsTextureFormatSupported(D3DFMT_A8L8);
+ d3d.hasTextureFormatL16 = IsTextureFormatSupported(D3DFMT_L16);
+
+ if (!(d3d.d3dcaps.TextureCaps & D3DPTEXTURECAPS_POW2))
+ npot = kNPOTFull;
+ else if (d3d.d3dcaps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL)
+ npot = kNPOTRestricted;
+ else
+ npot = kNPOTNone;
+
+ npotRT = npot;
+
+ hasSRGBReadWrite =
+ IsSRGBTextureReadSupported(GetD3D9TextureFormat(static_cast<TextureFormat>(kTexFormatRGB24)))
+ && IsSRGBTextureReadSupported(GetD3D9TextureFormat(static_cast<TextureFormat>(kTexFormatRGBA32)))
+ && IsSRGBTextureReadSupported(GetD3D9TextureFormat(static_cast<TextureFormat>(kTexFormatARGB32)))
+ && IsSRGBTextureReadSupported(GetD3D9TextureFormat(static_cast<TextureFormat>(kTexFormatBGR24)))
+ && IsSRGBTextureReadSupported(GetD3D9TextureFormat(static_cast<TextureFormat>(kTexFormatDXT1)))
+ && IsSRGBTextureReadSupported(GetD3D9TextureFormat(static_cast<TextureFormat>(kTexFormatDXT3)))
+ && IsSRGBTextureReadSupported(GetD3D9TextureFormat(static_cast<TextureFormat>(kTexFormatDXT5)));
+
+ // we only do sRGB writes to an 8 bit buffer ...
+ hasSRGBReadWrite = hasSRGBReadWrite && IsSRGBTextureWriteSupported(D3DFMT_A8R8G8B8);
+
+ hasInstancing = false; //@TODO: instancing!
+
+ hasBlendSquare = (d3d.d3dcaps.SrcBlendCaps & D3DPBLENDCAPS_SRCCOLOR) && (d3d.d3dcaps.DestBlendCaps & D3DPBLENDCAPS_DESTCOLOR);
+ hasSeparateAlphaBlend = d3d.d3dcaps.PrimitiveMiscCaps & D3DPMISCCAPS_SEPARATEALPHABLEND;
+ hasBlendSub = hasBlendMinMax = d3d.d3dcaps.PrimitiveMiscCaps & D3DPMISCCAPS_BLENDOP;
+
+ hasAutoMipMapGeneration = d3d.d3dcaps.Caps2 & D3DCAPS2_CANAUTOGENMIPMAP;
+
+ for (int i = 0; i < kRTFormatCount; ++i)
+ {
+ if (i == kRTFormatDefault || i == kRTFormatDefaultHDR || i == kRTFormatShadowMap)
+ continue;
+ supportsRenderTextureFormat[i] = IsRenderTextureFormatSupported(kD3D9RenderTextureFormats[i]);
+ }
+ hasRenderToTexture = supportsRenderTextureFormat[kRTFormatARGB32];
+ supportsRenderTextureFormat[kRTFormatDefault] = hasRenderToTexture;
+
+ hasRenderToCubemap = hasRenderToTexture;
+ hasStencil = true;
+ hasRenderTargetStencil = true;
+ hasTwoSidedStencil = d3d.d3dcaps.StencilCaps & D3DSTENCILCAPS_TWOSIDED;
+ maxMRTs = clamp<int> (d3d.d3dcaps.NumSimultaneousRTs, 1, kMaxSupportedRenderTargets);
+ if (!(d3d.d3dcaps.PrimitiveMiscCaps & D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING))
+ maxMRTs = 1;
+
+ d3d.hasATIDepthFormat16 = SUCCEEDED( d3dobject->CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, kD3D9FormatDF16 ) );
+ supportsRenderTextureFormat[kRTFormatDepth] |= d3d.hasATIDepthFormat16;
+ d3d.hasNVDepthFormatINTZ = SUCCEEDED( d3dobject->CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, kD3D9FormatINTZ ) );
+ supportsRenderTextureFormat[kRTFormatDepth] |= d3d.hasNVDepthFormatINTZ;
+ d3d.hasNVDepthFormatRAWZ = SUCCEEDED( d3dobject->CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, kD3D9FormatRAWZ ) );
+ d3d.hasNULLFormat = SUCCEEDED( d3dobject->CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE, kD3D9FormatNULL ) );
+ d3d.hasDepthResolveRESZ = SUCCEEDED( d3dobject->CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE, kD3D9FormatRESZ ) );
+
+ hasNativeDepthTexture = d3d.hasATIDepthFormat16 || d3d.hasNVDepthFormatINTZ;
+ hasStencilInDepthTexture = d3d.hasNVDepthFormatINTZ;
+ hasNativeShadowMap = SUCCEEDED( d3dobject->CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, D3DFMT_D16 ) );
+ supportsRenderTextureFormat[kRTFormatShadowMap] = hasRenderToTexture && hasNativeShadowMap;
+
+ #if CAPS_DEBUG_DISABLE_RT
+ hasRenderToTexture = hasRenderToCubemap = false;
+ for (int i = 0; i < kRTFormatCount; ++i)
+ supportsRenderTextureFormat[i] = false;
+ maxMRTs = 1;
+ #endif
+
+ // This is somewhat dummy; actual resolving of FSAA levels and types supported happens later when choosing presentation parameters.
+ hasMultiSample = true;
+
+ // Driver bugs/workarounds following
+ DetectDriverBugsD3D9( adapterInfo.VendorId, driverVersion );
+
+ // safeguards
+ maxRenderTextureSize = std::min( maxRenderTextureSize, maxTextureSize );
+ maxCubeMapSize = std::min( maxCubeMapSize, maxTextureSize );
+
+ // in the very end, figure out shader capabilities level (after all workarounds are applied)
+ if( LOWORD(d3d.d3dcaps.PixelShaderVersion) < (3<<8)+0 )
+ {
+ // no ps3.0: 2.x shaders
+ shaderCaps = kShaderLevel2;
+ }
+ else
+ {
+ // has everything we care about!
+ shaderCaps = kShaderLevel3;
+ }
+
+ // Print overall caps & D3D9 hacks used
+ printf_console( " Caps: Shader=%i DepthRT=%i NativeDepth=%i NativeShadow=%i DF16=%i INTZ=%i RAWZ=%i NULL=%i RESZ=%i SlowINTZ=%i\n",
+ shaderCaps,
+ supportsRenderTextureFormat[kRTFormatDepth], hasNativeDepthTexture, hasNativeShadowMap,
+ d3d.hasATIDepthFormat16,
+ d3d.hasNVDepthFormatINTZ, d3d.hasNVDepthFormatRAWZ,
+ d3d.hasNULLFormat, d3d.hasDepthResolveRESZ,
+ d3d.slowINTZSampling
+ );
+}
+
+
+enum WindowsVersion {
+ kWindows2000 = 50, // 5.0
+ kWindowsXP = 51, // 5.1
+ kWindows2003 = 52, // 5.2
+ kWindowsVista = 60, // 6.0
+};
+
+static int GetWindowsVersion()
+{
+ OSVERSIONINFO osinfo;
+ osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if( !GetVersionEx(&osinfo) )
+ return 0;
+
+ if( osinfo.dwPlatformId == VER_PLATFORM_WIN32_NT )
+ return osinfo.dwMajorVersion * 10 + osinfo.dwMinorVersion % 10;
+ else
+ return 0;
+}
+
+
+void GraphicsCaps::DetectDriverBugsD3D9( UInt32 vendorCode, const windriverutils::VersionInfo& driverVersion )
+{
+ d3d.slowINTZSampling = false;
+
+
+ if( vendorCode == kVendorNVIDIA )
+ {
+ // GeForceFX and earlier have sort-of-buggy render to cubemap. E.g. skybox draws correctly,
+ // but objects do not appear. Huh.
+ const int kShaderVersion30 = (3 << 8) + 0;
+ bool isFXOrEarlier = LOWORD(gGraphicsCaps.d3d.d3dcaps.PixelShaderVersion) < kShaderVersion30;
+ if( isFXOrEarlier )
+ {
+ printf_console( "D3D: disabling render to cubemap on pre-GeForce6\n" );
+ buggyCameraRenderToCubemap = true;
+ }
+
+ // Also, native shadow maps seem to have problems on GeForce FX; perhaps it needs to use tex2Dproj instead of tex2D,
+ // or something (FX 5200). Since FX cards are really dying, and the only left ones are FX 5200/5500,
+ // let's just turn shadows off. You don't want them on those cards anyway!
+ if (isFXOrEarlier)
+ {
+ printf_console ("D3D: disabling shadows on pre-GeForce6\n");
+ hasNativeShadowMap = false;
+ hasNativeDepthTexture = false;
+ supportsRenderTextureFormat[kRTFormatDepth] = false;
+ }
+
+ // GeForceFX on 6.14.10.9147 drivers has buggy fullscreen FSAA.
+ // It displays everything stretched, as if AA samples map to pixels directly.
+ if( isFXOrEarlier && driverVersion <= windriverutils::VersionInfo(6,14,10,9147) )
+ {
+ printf_console( "D3D: disabling fullscreen AA (buggy pre-GeForce6 driver)\n" );
+ buggyFullscreenFSAA = true;
+ }
+ }
+ if( vendorCode == kVendorATI )
+ {
+ // On D3D9 Radeon HD cards have big performance hit when using INTZ texture for both sampling & depth testing
+ // (Radeon HD 3xxx-5xxx, Catalyst 9.10 to 10.5). Talking with AMD, we found that using RESZ to copy it into a separate
+ // texture is a decent workaround that results in ok performance.
+ if (d3d.hasDepthResolveRESZ)
+ d3d.slowINTZSampling = true;
+ }
+
+ // Sanitize VRAM amount
+ if( videoMemoryMB < 32 ) {
+ printf_console("D3D: VRAM amount suspiciously low (less than 32MB)\n");
+ videoMemoryMB = 32;
+ }
+}
diff --git a/Runtime/GfxDevice/d3d/RenderTextureD3D.cpp b/Runtime/GfxDevice/d3d/RenderTextureD3D.cpp
new file mode 100644
index 0000000..0d444b3
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/RenderTextureD3D.cpp
@@ -0,0 +1,583 @@
+#include "UnityPrefix.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Graphics/Image.h"
+#include "D3D9Context.h"
+#include "TexturesD3D9.h"
+#include "RenderTextureD3D.h"
+#include "D3D9Utils.h"
+
+
+// defined in GfxDeviceD3D9.cpp
+void UnbindTextureD3D9( TextureID texture );
+
+
+// define to 1 to print lots of activity info
+#define DEBUG_RENDER_TEXTURES 0
+
+
+D3DFORMAT kD3D9RenderTextureFormats[kRTFormatCount] = {
+ D3DFMT_A8R8G8B8,
+ D3DFMT_R32F, // Depth
+ D3DFMT_A16B16G16R16F,
+ D3DFMT_D16, // Shadowmap
+ D3DFMT_R5G6B5,
+ D3DFMT_A4R4G4B4,
+ D3DFMT_A1R5G5B5,
+ (D3DFORMAT)-1, // Default
+ D3DFMT_A2R10G10B10,
+ (D3DFORMAT)-1, // DefaultHDR
+ D3DFMT_A16B16G16R16,
+ D3DFMT_A32B32G32R32F,
+ D3DFMT_G32R32F,
+ D3DFMT_G16R16F,
+ D3DFMT_R32F,
+ D3DFMT_R16F,
+ D3DFMT_L8, // R8
+ (D3DFORMAT)-1, // ARGBInt
+ (D3DFORMAT)-1, // RGInt
+ (D3DFORMAT)-1, // RInt
+ (D3DFORMAT)-1, // BGRA32
+};
+
+
+static D3DMULTISAMPLE_TYPE FindSupportedD3DMultiSampleType (D3DFORMAT d3dformat, int maxSamples)
+{
+ BOOL windowed = !GetScreenManager().IsFullScreen();
+ for (int samples = maxSamples; samples >= 1; samples--)
+ {
+ D3DMULTISAMPLE_TYPE msaa = GetD3DMultiSampleType( samples );
+ HRESULT hr = GetD3DObject()->CheckDeviceMultiSampleType( g_D3DAdapter, g_D3DDevType, d3dformat, windowed, msaa, NULL );
+ if (SUCCEEDED(hr))
+ return msaa;
+ }
+ return D3DMULTISAMPLE_NONE;
+}
+
+static bool InitD3DRenderColorSurface (RenderColorSurfaceD3D9& rs, TexturesD3D9& textures)
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ HRESULT hr;
+ DWORD usage;
+
+ if (rs.textureID.m_ID)
+ {
+ // Regular render texture
+ usage = D3DUSAGE_RENDERTARGET;
+ int mipCount = 1;
+ if (rs.flags & kSurfaceCreateMipmap && !IsDepthRTFormat(rs.format))
+ {
+ Assert(gGraphicsCaps.hasAutoMipMapGeneration);
+ if (rs.flags & kSurfaceCreateAutoGenMips)
+ usage |= D3DUSAGE_AUTOGENMIPMAP;
+ else
+ mipCount = CalculateMipMapCount3D (rs.width, rs.height, 1);
+ }
+ if (rs.dim == kTexDim2D)
+ {
+ IDirect3DTexture9* rt;
+ D3DFORMAT d3dformat = D3DFMT_UNKNOWN;
+ d3dformat = kD3D9RenderTextureFormats[rs.format];
+ hr = dev->CreateTexture (rs.width, rs.height, mipCount, usage, d3dformat, D3DPOOL_DEFAULT, &rt, NULL);
+ if( FAILED(hr) )
+ {
+ ErrorString( Format( "RenderTexture creation error: CreateTexture failed [%s]", GetD3D9Error(hr) ) );
+ return false;
+ }
+ rs.m_Texture = rt;
+ rt->GetSurfaceLevel( 0, &rs.m_Surface );
+ }
+ else if (rs.dim == kTexDimCUBE)
+ {
+ Assert(rs.width == rs.height);
+ IDirect3DCubeTexture9* rt;
+ hr = dev->CreateCubeTexture (rs.width, mipCount, usage, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &rt, NULL);
+ if( FAILED(hr) )
+ {
+ ErrorString( Format( "RenderTexture creation error: CreateCubeTexture failed [%s]", GetD3D9Error(hr) ) );
+ return false;
+ }
+ rs.m_Texture = rt;
+ }
+ else
+ {
+ ErrorString("RenderTexture creation error: D3D9 only supports 2D or CUBE textures");
+ return false;
+ }
+ }
+ else
+ {
+ D3DFORMAT d3dformat = D3DFMT_UNKNOWN;
+ D3DMULTISAMPLE_TYPE msaa = D3DMULTISAMPLE_NONE;
+ if (!(rs.flags & kSurfaceCreateNeverUsed))
+ {
+ // Create surface without texture to resolve from
+ // Find supported MSAA type based on device and format
+ d3dformat = kD3D9RenderTextureFormats[rs.format];
+ msaa = FindSupportedD3DMultiSampleType( d3dformat, rs.samples );
+ }
+ else
+ {
+ // Dummy render target surface (only needed to make D3D runtime happy)
+ d3dformat = gGraphicsCaps.d3d.hasNULLFormat ? kD3D9FormatNULL : D3DFMT_A8R8G8B8;
+ }
+ IDirect3DSurface9* ds = NULL;
+ hr = dev->CreateRenderTarget( rs.width, rs.height, d3dformat, msaa, 0, FALSE, &ds, NULL );
+ if (FAILED(hr))
+ {
+ ErrorString( Format( "RenderTexture creation error: CreateRenderTarget failed [%s]", GetD3D9Error(hr) ) );
+ return false;
+ }
+ rs.m_Surface = ds;
+ }
+
+ // add to textures map
+ if (rs.textureID.m_ID)
+ textures.AddTexture( rs.textureID, rs.m_Texture );
+
+ return true;
+}
+
+static bool InitD3DRenderDepthSurface (RenderDepthSurfaceD3D9& rs, TexturesD3D9& textures)
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ HRESULT hr;
+
+ if (!rs.textureID.m_ID)
+ {
+ // Create depth buffer surface
+ if( rs.depthFormat == kDepthFormatNone )
+ {
+ rs.m_Surface = NULL;
+ }
+ else
+ {
+ // Create surface without texture to resolve from
+ // Find supported MSAA type based on device and format
+ D3DFORMAT d3dformat = (rs.depthFormat == kDepthFormat16 ? D3DFMT_D16 : D3DFMT_D24S8);
+ D3DMULTISAMPLE_TYPE msaa = FindSupportedD3DMultiSampleType( d3dformat, rs.samples );
+ hr = dev->CreateDepthStencilSurface( rs.width, rs.height, d3dformat, msaa, 0, TRUE, &rs.m_Surface, NULL );
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(rs.m_Surface, rs.width * rs.height * GetBPPFromD3DFormat(d3dformat), &rs);
+ if( FAILED(hr) )
+ {
+ ErrorString( Format( "RenderTexture creation error: CreateDepthStencilSurface failed [%s]", GetD3D9Error(hr) ) );
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // Create depth buffer as texture
+ D3DFORMAT d3dformat = D3DFMT_UNKNOWN;
+ if (rs.flags & kSurfaceCreateShadowmap)
+ {
+ Assert (rs.depthFormat == kDepthFormat16);
+ Assert (gGraphicsCaps.hasNativeShadowMap);
+ d3dformat = D3DFMT_D16;
+ }
+ else
+ {
+ Assert (gGraphicsCaps.hasNativeDepthTexture);
+ if (gGraphicsCaps.d3d.hasNVDepthFormatINTZ)
+ d3dformat = kD3D9FormatINTZ;
+ else if (gGraphicsCaps.d3d.hasATIDepthFormat16)
+ d3dformat = kD3D9FormatDF16;
+ else
+ {
+ AssertString ("No available native depth format");
+ }
+ }
+ IDirect3DTexture9* texture = NULL;
+ hr = dev->CreateTexture (rs.width, rs.height, 1, D3DUSAGE_DEPTHSTENCIL, d3dformat, D3DPOOL_DEFAULT, &texture, NULL);
+ if( FAILED(hr) )
+ {
+ ErrorString( Format( "RenderTexture creation error: CreateTexture failed [%s]", GetD3D9Error(hr) ) );
+ return false;
+ }
+ rs.m_Texture = texture;
+ texture->GetSurfaceLevel (0, &rs.m_Surface);
+ }
+
+ if (rs.textureID.m_ID)
+ textures.AddTexture( rs.textureID, rs.m_Texture );
+
+ return true;
+}
+
+
+static RenderColorSurfaceD3D9* s_ActiveColorTargets[kMaxSupportedRenderTargets];
+static int s_ActiveColorTargetCount;
+static RenderDepthSurfaceD3D9* s_ActiveDepthTarget = NULL;
+static int s_ActiveMip = 0;
+static CubemapFace s_ActiveFace = kCubeFaceUnknown;
+
+static RenderColorSurfaceD3D9* s_ActiveColorBackBuffer = NULL;
+static RenderDepthSurfaceD3D9* s_ActiveDepthBackBuffer = NULL;
+
+// on dx editor we can switch swapchain underneath
+// so lets do smth like gl's default FBO
+// it will be used only from "user" code and we will select proper swap chain here
+static RenderColorSurfaceD3D9* s_DummyColorBackBuffer = NULL;
+static RenderDepthSurfaceD3D9* s_DummyDepthBackBuffer = NULL;
+
+RenderSurfaceBase* DummyColorBackBuferD3D9()
+{
+ if(s_DummyColorBackBuffer == 0)
+ {
+ static RenderColorSurfaceD3D9 __bb;
+ RenderSurfaceBase_InitColor(__bb);
+ __bb.backBuffer = true;
+
+ s_DummyColorBackBuffer = &__bb;
+ }
+ return s_DummyColorBackBuffer;
+}
+
+RenderSurfaceBase* DummyDepthBackBuferD3D9()
+{
+ if(s_DummyDepthBackBuffer == 0)
+ {
+ static RenderDepthSurfaceD3D9 __bb;
+ RenderSurfaceBase_InitDepth(__bb);
+ __bb.backBuffer = true;
+
+ s_DummyDepthBackBuffer = &__bb;
+ }
+ return s_DummyDepthBackBuffer;
+}
+
+bool SetRenderTargetD3D9 (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face, int& outRenderTargetWidth, int& outRenderTargetHeight, bool& outIsBackBuffer)
+{
+ RenderColorSurfaceD3D9* rcolorZero = reinterpret_cast<RenderColorSurfaceD3D9*>(colorHandles[0].object);
+ RenderDepthSurfaceD3D9* rdepth = reinterpret_cast<RenderDepthSurfaceD3D9*>( depthHandle.object );
+
+ #if DEBUG_RENDER_TEXTURES
+ printf_console( "RT: SetRenderTargetD3D9 color=%i depth=%i (%x) mip=%i face=%i\n",
+ rcolorZero ? rcolorZero->textureID.m_ID : 0,
+ rdepth ? rdepth->textureID.m_ID : 0, rdepth ? rdepth->m_Surface : 0,
+ mipLevel, face );
+ #endif
+
+ outIsBackBuffer = false;
+
+ if (count == s_ActiveColorTargetCount && s_ActiveDepthTarget == rdepth && s_ActiveMip == mipLevel && s_ActiveFace == face)
+ {
+ bool colorsSame = true;
+ for (int i = 0; i < count; ++i)
+ {
+ if (s_ActiveColorTargets[i] != reinterpret_cast<RenderColorSurfaceD3D9*>(colorHandles[i].object))
+ colorsSame = false;
+ }
+ if (colorsSame)
+ return false;
+ }
+
+ IDirect3DDevice9* dev = GetD3DDeviceNoAssert();
+ // Happens at startup, when deleting all RenderTextures
+ if( !dev )
+ {
+ Assert (!rcolorZero && !rdepth);
+ return false;
+ }
+
+ HRESULT hr = S_FALSE;
+
+ Assert(colorHandles[0].IsValid() && depthHandle.IsValid());
+ Assert(rcolorZero->backBuffer == rdepth->backBuffer);
+
+ outIsBackBuffer = rcolorZero->backBuffer;
+ if (!outIsBackBuffer)
+ GetRealGfxDevice().GetFrameStats().AddRenderTextureChange(); // stats
+
+ if(rcolorZero->backBuffer && rcolorZero == s_DummyColorBackBuffer)
+ colorHandles[0].object = rcolorZero = s_ActiveColorBackBuffer;
+ if(rdepth->backBuffer && rdepth == s_DummyDepthBackBuffer)
+ depthHandle.object = rdepth = s_ActiveDepthBackBuffer;
+
+
+ // color surfaces
+ for (int i = 0; i < count; ++i)
+ {
+ RenderColorSurfaceD3D9* rcolor = reinterpret_cast<RenderColorSurfaceD3D9*>(colorHandles[i].object);
+ if(rcolor)
+ {
+ // color surface
+ Assert (rcolor->colorSurface);
+ // Make sure this texture is not used when setting it as render target
+ if (rcolor->textureID.m_ID)
+ UnbindTextureD3D9( rcolor->textureID );
+
+ // Set color surface
+ IDirect3DSurface9* surface = NULL;
+ bool needsRelease = false;
+ if( !rcolor->m_Texture )
+ {
+ Assert (rcolor->m_Surface);
+ surface = rcolor->m_Surface;
+ #if DEBUG_RENDER_TEXTURES
+ printf_console( " RT: color buffer plain\n" );
+ #endif
+ }
+ else if (rcolor->dim == kTexDimCUBE)
+ {
+ Assert (rcolor->m_Texture);
+ IDirect3DCubeTexture9* rt = static_cast<IDirect3DCubeTexture9*>( rcolor->m_Texture );
+ hr = rt->GetCubeMapSurface((D3DCUBEMAP_FACES)(D3DCUBEMAP_FACE_POSITIVE_X + clamp<int>(face,0,5)), mipLevel, &surface);
+ needsRelease = true;
+ }
+ else
+ {
+ #if DEBUG_RENDER_TEXTURES
+ printf_console( " RT: color buffer texture %i\n", rcolor->textureID.m_ID );
+ #endif
+ Assert (rcolor->m_Texture);
+ IDirect3DTexture9* rt = static_cast<IDirect3DTexture9*>( rcolor->m_Texture );
+ hr = rt->GetSurfaceLevel (mipLevel, &surface);
+ needsRelease = true;
+ }
+
+ if( surface )
+ {
+ hr = dev->SetRenderTarget (i, surface);
+ if( FAILED(hr) ) {
+ ErrorString( Format("RenderTexture error: failed to set render target [%s]", GetD3D9Error(hr)) );
+ }
+ if (needsRelease)
+ surface->Release();
+ }
+ else
+ {
+ ErrorString( Format("RenderTexture error: failed to retrieve color surface [%s]", GetD3D9Error(hr)) );
+ }
+ outRenderTargetWidth = rcolor->width;
+ outRenderTargetHeight = rcolor->height;
+ }
+ else
+ {
+ hr = dev->SetRenderTarget (i, NULL);
+ }
+ }
+ for (int i = count; i < s_ActiveColorTargetCount; ++i)
+ {
+ hr = dev->SetRenderTarget (i, NULL);
+ }
+
+
+ // depth surface
+ Assert (!rdepth || !rdepth->colorSurface);
+
+ if (rdepth && rdepth->m_Surface)
+ {
+ // Make sure this texture is not used when setting it as render target
+ if (rdepth->textureID.m_ID)
+ UnbindTextureD3D9( rdepth->textureID );
+
+ // Set depth surface
+ if( rdepth->m_Surface )
+ {
+ #if DEBUG_RENDER_TEXTURES
+ if (rdepth->textureID.m_ID)
+ printf_console( " RT: depth buffer texture %i\n", rdepth->textureID.m_ID );
+ else
+ printf_console( " RT: depth buffer plain %x\n", rdepth->m_Surface );
+ #endif
+ hr = dev->SetDepthStencilSurface( rdepth->m_Surface );
+ if( FAILED(hr) ) {
+ ErrorString( Format("RenderTexture error: failed to set depth stencil [%s]", GetD3D9Error(hr)) );
+ }
+ g_D3DHasDepthStencil = true;
+ D3DSURFACE_DESC desc;
+ desc.Format = D3DFMT_D16;
+ rdepth->m_Surface->GetDesc( &desc );
+ g_D3DDepthStencilFormat = desc.Format;
+ }
+ }
+ else
+ {
+ #if DEBUG_RENDER_TEXTURES
+ printf_console( " RT: depth buffer none\n" );
+ #endif
+ dev->SetDepthStencilSurface( NULL );
+ g_D3DHasDepthStencil = false;
+ g_D3DDepthStencilFormat = D3DFMT_UNKNOWN;
+ }
+
+ for (int i = 0; i < count; ++i)
+ s_ActiveColorTargets[i] = reinterpret_cast<RenderColorSurfaceD3D9*>(colorHandles[i].object);
+ s_ActiveColorTargetCount = count;
+ s_ActiveDepthTarget = rdepth;
+ s_ActiveFace = face;
+ s_ActiveMip = mipLevel;
+
+ if (outIsBackBuffer)
+ {
+ s_ActiveColorBackBuffer = (RenderColorSurfaceD3D9*)colorHandles[0].object;
+ s_ActiveDepthBackBuffer = (RenderDepthSurfaceD3D9*)depthHandle.object;
+
+ // we are rendering to "default FBO", so current target is dummy
+ // as a side effect, if we change swap chain, it will be set correctly, and active remain valid
+ s_ActiveColorTargets[0] = s_DummyColorBackBuffer;
+ s_ActiveDepthTarget = s_DummyDepthBackBuffer;
+ }
+ return true;
+}
+
+RenderSurfaceHandle GetActiveRenderColorSurfaceD3D9(int index)
+{
+ return RenderSurfaceHandle(s_ActiveColorTargets[index]);
+}
+RenderSurfaceHandle GetActiveRenderDepthSurfaceD3D9()
+{
+ return RenderSurfaceHandle(s_ActiveDepthTarget);
+}
+
+bool IsActiveRenderTargetWithColorD3D9()
+{
+ return !s_ActiveColorTargets[0] || s_ActiveColorTargets[0]->backBuffer || !(s_ActiveColorTargets[0]->flags & kSurfaceCreateNeverUsed);
+}
+
+
+RenderSurfaceHandle CreateRenderColorSurfaceD3D9( TextureID textureID, int width, int height, int samples, TextureDimension dim, UInt32 createFlags, RenderTextureFormat format, TexturesD3D9& textures )
+{
+ RenderSurfaceHandle rsHandle;
+
+ if( !gGraphicsCaps.hasRenderToTexture )
+ return rsHandle;
+ if( !gGraphicsCaps.supportsRenderTextureFormat[format] )
+ return rsHandle;
+
+ RenderColorSurfaceD3D9* rs = new RenderColorSurfaceD3D9;
+ rs->width = width;
+ rs->height = height;
+ rs->samples = samples;
+ rs->format = format;
+ rs->textureID = textureID;
+ rs->dim = dim;
+ rs->flags = createFlags;
+
+ // Create it
+ if (!InitD3DRenderColorSurface(*rs, textures))
+ {
+ delete rs;
+ return rsHandle;
+ }
+
+ rsHandle.object = rs;
+ return rsHandle;
+}
+
+RenderSurfaceHandle CreateRenderDepthSurfaceD3D9( TextureID textureID, int width, int height, int samples, DepthBufferFormat depthFormat, UInt32 createFlags, TexturesD3D9& textures )
+{
+ RenderSurfaceHandle rsHandle;
+
+ if( !gGraphicsCaps.hasRenderToTexture )
+ return rsHandle;
+
+ RenderDepthSurfaceD3D9* rs = new RenderDepthSurfaceD3D9;
+ rs->width = width;
+ rs->height = height;
+ rs->samples = samples;
+ rs->depthFormat = depthFormat;
+ rs->textureID = textureID;
+ rs->flags = createFlags;
+
+ // Create it
+ if (!InitD3DRenderDepthSurface( *rs, textures))
+ {
+ delete rs;
+ return rsHandle;
+ }
+
+ rsHandle.object = rs;
+ return rsHandle;
+}
+
+
+void DestroyRenderSurfaceD3D9 (RenderSurfaceD3D9* rs)
+{
+ Assert(rs);
+
+ if(rs == s_ActiveColorBackBuffer || rs == s_ActiveDepthBackBuffer)
+ {
+ #if DEBUG_RENDER_TEXTURES
+ printf_console( " RT: Destroying main %s buffer.\n", s == s_ActiveColorBackBuffer ? "color" : "depth" );
+ #endif
+ s_ActiveColorBackBuffer = NULL;
+ s_ActiveDepthBackBuffer = NULL;
+ }
+
+ RenderSurfaceHandle defaultColor(s_DummyColorBackBuffer);
+ RenderSurfaceHandle defaultDepth(s_DummyDepthBackBuffer);
+
+ if (s_ActiveDepthTarget == rs)
+ {
+ ErrorString( "RenderTexture warning: Destroying active render texture. Switching to main context." );
+ int targetWidth, targetHeight;
+ bool isBackBuffer;
+ SetRenderTargetD3D9 (1, &defaultColor, defaultDepth, 0, kCubeFaceUnknown, targetWidth, targetHeight, isBackBuffer);
+ }
+ for (int i = 0; i < s_ActiveColorTargetCount; ++i)
+ {
+ if (s_ActiveColorTargets[i] == rs)
+ {
+ ErrorString( "RenderTexture warning: Destroying active render texture. Switching to main context." );
+ int targetWidth, targetHeight;
+ bool isBackBuffer;
+ SetRenderTargetD3D9 (1, &defaultColor, defaultDepth, 0, kCubeFaceUnknown, targetWidth, targetHeight, isBackBuffer);
+ }
+ }
+
+ if (rs->m_Surface)
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(rs->m_Surface);
+ ULONG refCount = rs->m_Surface->Release();
+ Assert(refCount == (rs->m_Texture ? 1 : 0));
+ rs->m_Surface = NULL;
+ }
+ if( rs->m_Texture )
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(rs->m_Texture);
+ ULONG refCount = rs->m_Texture->Release();
+ Assert(refCount == 0);
+ rs->m_Texture = NULL;
+ }
+}
+
+void DestroyRenderSurfaceD3D9 (RenderSurfaceHandle& rsHandle, TexturesD3D9& textures)
+{
+ if( !rsHandle.IsValid() )
+ return;
+
+ RenderSurfaceD3D9* rs = reinterpret_cast<RenderSurfaceD3D9*>( rsHandle.object );
+ DestroyRenderSurfaceD3D9( rs );
+
+ if (rs->m_Texture || rs->textureID.m_ID)
+ textures.RemoveTexture (rs->textureID);
+
+ delete rs;
+ rsHandle.object = NULL;
+}
+
+
+
+// --------------------------------------------------------------------------
+
+
+#if ENABLE_UNIT_TESTS
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE ( RenderTextureD3DTests )
+{
+TEST(RenderTextureD3DTests_FormatTableCorrect)
+{
+ // checks that you did not forget to update format table when adding a new format :)
+ for (int i = 0; i < kRTFormatCount; ++i)
+ {
+ CHECK(kD3D9RenderTextureFormats[i] != 0);
+ }
+}
+}
+#endif
diff --git a/Runtime/GfxDevice/d3d/RenderTextureD3D.h b/Runtime/GfxDevice/d3d/RenderTextureD3D.h
new file mode 100644
index 0000000..255e89d
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/RenderTextureD3D.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "D3D9Includes.h"
+
+
+RenderSurfaceHandle CreateRenderColorSurfaceD3D9 (TextureID textureID, int width, int height, int samples, TextureDimension dim, UInt32 createFlags, RenderTextureFormat format, TexturesD3D9& textures);
+RenderSurfaceHandle CreateRenderDepthSurfaceD3D9 (TextureID textureID, int width, int height, int samples, DepthBufferFormat depthFormat, UInt32 createFlags, TexturesD3D9& textures);
+void DestroyRenderSurfaceD3D9 (RenderSurfaceD3D9* rs);
+void DestroyRenderSurfaceD3D9 (RenderSurfaceHandle& rsHandle, TexturesD3D9& textures);
+bool SetRenderTargetD3D9 (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face, int& outRenderTargetWidth, int& outRenderTargetHeight, bool& outIsBackBuffer);
+RenderSurfaceHandle GetActiveRenderColorSurfaceD3D9(int index);
+RenderSurfaceHandle GetActiveRenderDepthSurfaceD3D9();
+
+RenderSurfaceHandle GetBackBufferColorSurfaceD3D9();
+RenderSurfaceHandle GetBackBufferDepthSurfaceD3D9();
+void SetBackBufferColorSurfaceD3D9(RenderSurfaceBase* color);
+void SetBackBufferDepthSurfaceD3D9(RenderSurfaceBase* depth);
diff --git a/Runtime/GfxDevice/d3d/ShaderGenerator.cpp b/Runtime/GfxDevice/d3d/ShaderGenerator.cpp
new file mode 100644
index 0000000..b62e5c7
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/ShaderGenerator.cpp
@@ -0,0 +1,948 @@
+#include "UnityPrefix.h"
+#include <stdlib.h>
+#include <string>
+#include <vector>
+#include <algorithm>
+#include <assert.h>
+#include "ShaderGenerator.h"
+#include "Runtime/Utilities/Word.h"
+
+enum ShaderInputRegister {
+ kInputPosition,
+ kInputNormal,
+ kInputUV0,
+ kInputUV1,
+ kInputColor,
+ kInputCount
+};
+
+const char* kShaderInputNames[kInputCount] = {
+ "$IPOS",
+ "$INOR",
+ "$IUV0",
+ "$IUV1",
+ "$ICOL",
+};
+
+const char* kShaderInputDecls[kInputCount] = {
+ "dcl_position",
+ "dcl_normal",
+ "dcl_texcoord0",
+ "dcl_texcoord1",
+ "dcl_color",
+};
+
+
+
+enum ShaderFragmentOptions {
+ kOptionHasTexMatrix = (1<<0),
+};
+
+const int kConstantLocations[kConstCount] = {
+ 0, // kConstMatrixMVP
+ 4, // kConstMatrixMV
+ 8, // kConstMatrixMV_IT
+ 12, // kConstMatrixTexture
+ 44, // kConstAmbient
+ 57, // kConstColorMatAmbient
+ 45, // kConstLightMisc
+ 46, // kConstMatDiffuse
+ 47, // kConstMatSpecular
+ 48, // kConstLightIndexes
+};
+
+enum CommonDependencies {
+ kDep_CamSpacePos,
+ kDep_CamSpaceN,
+ kDep_ViewVector,
+ kDep_ReflVector,
+ kDep_Normal,
+ kDepCount
+};
+
+
+// --------------------------------------------------------------------------
+
+// transform position
+const ShaderFragment kVS_Pos = {
+ (1<<kInputPosition), // input
+ (1<<kConstMatrixMVP), // constants
+ 0, // deps
+ 0, // options
+ 0, // temps
+ NULL, // ins
+ NULL, // outs
+ "dp4 oPos.x, $IPOS, c0\n"
+ "dp4 oPos.y, $IPOS, c1\n"
+ "dp4 oPos.z, $IPOS, c2\n"
+ "dp4 oPos.w, $IPOS, c3\n",
+};
+
+// --------------------------------------------------------------------------
+// temps
+
+// NORM = vertex normal
+const ShaderFragment kVS_Load_Normal = {
+ (1<<kInputNormal), // input
+ 0, // constants
+ 0, // deps
+ 0, // options
+ 0, // temps
+ NULL, // ins
+ "NORM", // outs
+ "mov $O_NORM, $INOR\n"
+};
+
+// NORM = normalized vertex normal
+const ShaderFragment kVS_Normalize_Normal = {
+ 0, // input
+ 0, // constants
+ (1<<kDep_Normal), // deps
+ 0, // options
+ 1, // temps
+ "NORM", // ins
+ "NORM", // outs
+ "nrm $TMP0.xyz, $O_NORM\n"
+ "mov $O_NORM.xyz, $TMP0\n"
+};
+
+
+// OPOS = input position of the vertex
+const ShaderFragment kVS_Temp_ObjSpacePos = {
+ (1<<kInputPosition), // input
+ 0, // constants
+ 0, // deps
+ 0, // options
+ 0, // temps
+ NULL, // ins
+ "OPOS", // outs
+ "mov $O_OPOS, $IPOS\n"
+};
+
+// CPOS = camera space position of the vertex
+const ShaderFragment kVS_Temp_CamSpacePos = {
+ (1<<kInputPosition), // input
+ (1<<kConstMatrixMV), // constants
+ 0, // deps
+ 0, // options
+ 0, // temps
+ NULL, // ins
+ "CPOS", // outs
+ "mul $O_CPOS, $IPOS.y, c5\n"
+ "mad $O_CPOS, c4, $IPOS.x, $O_CPOS\n"
+ "mad $O_CPOS, c6, $IPOS.z, $O_CPOS\n"
+ "mad $O_CPOS, c7, $IPOS.w, $O_CPOS\n",
+};
+
+// CNOR = camera space normal of the vertex
+const ShaderFragment kVS_Temp_CamSpaceN = {
+ 0, // input
+ (1<<kConstMatrixMV_IT), // constants
+ (1<<kDep_Normal), // deps
+ 0, // options
+ 0, // temps
+ "NORM", // ins
+ "CNOR", // outs
+ "mul $O_CNOR, $O_NORM.y, c9\n"
+ "mad $O_CNOR, c8, $O_NORM.x, $O_CNOR\n"
+ "mad $O_CNOR, c10, $O_NORM.z, $O_CNOR\n",
+};
+
+// VIEW = normalized vertex-to-eye vector
+const ShaderFragment kVS_Temp_ViewVector = {
+ 0, // input
+ 0, // constants
+ (1<<kDep_CamSpacePos), // deps
+ 0, // options
+ 0, // temps
+ "CPOS", // ins
+ "VIEW", // outs
+ "dp3 $O_VIEW.w, $O_CPOS, $O_CPOS\n"
+ "rsq $O_VIEW.w, $O_VIEW.w\n"
+ "mul $O_VIEW, -$O_CPOS, $O_VIEW.w\n",
+};
+
+// REFL = camera space reflection vector: 2*dot(V,N)*N-V
+const ShaderFragment kVS_Temp_CamSpaceRefl = {
+ 0, // input
+ 0, // constants
+ (1<<kDep_CamSpaceN) | (1<<kDep_ViewVector), // deps
+ 0, // options
+ 0, // temps
+ "CNOR VIEW", // ins
+ "REFL", // outs
+ "mov $O_REFL.xyz, $O_VIEW\n"
+ "dp3 $O_REFL.w, $O_REFL, $O_CNOR\n"
+ "add $O_REFL.w, $O_REFL.w, $O_REFL.w\n"
+ "mad $O_REFL.xyz, $O_REFL.w, $O_CNOR, -$O_REFL\n"
+};
+
+// cheap version
+// SPHR = sphere map: N*0.5+0.5
+//const ShaderFragment kVS_Temp_SphereMap = {
+// 0, // input
+// (1<<kConstLightMisc), // constants
+// (1<<kDep_CamSpaceN), // deps
+// 0, // options
+// 0, // temps
+// "CNOR", // ins
+// "SPHR", // outs
+// "mad $O_SPHR.xyz, $O_CNOR, c45.w, c45.w"
+//};
+
+// SPHR = sphere map. R = reflection vector
+// m = 2*sqrt(Rx*Rx + Ry*Ry + (Rz+1)*(Rz+1))
+// SPHR = Rx/m + 0.5, Ry/m + 0.5
+const ShaderFragment kVS_Temp_SphereMap = {
+ 0, // input
+ (1<<kConstLightMisc), // constants
+ (1<<kDep_ReflVector), // deps
+ 0, // options
+ 1, // temps
+ "REFL", // ins
+ "SPHR", // outs
+ "mul $TMP0.xy, $O_REFL, $O_REFL\n" // Rx*Rx, Ry*Ry
+ "add $O_SPHR.w, $TMP0.y, $TMP0.x\n" // Rx*Rx + Ry*Ry
+ "add $O_SPHR.z, $O_REFL.z, c45.z\n" // Rz+1
+ "mad $O_SPHR.z, $O_SPHR.z, $O_SPHR.z, $O_SPHR.w\n" // (Rz+1)*(Rz+1) + Rx*Rx + Ry*Ry
+ "mul $O_SPHR.z, $O_SPHR.z, c45.y\n" // * 4
+ "rsq $O_SPHR.z, $O_SPHR.z\n" // m
+ "mad $O_SPHR.xy, $O_REFL, $O_SPHR.z, c45.w\n" // R/m+0.5
+};
+
+// --------------------------------------------------------------------------
+// Texture coordinates
+
+const ShaderFragment kVS_Load_UV0 = {
+ (1<<kInputUV0), // input
+ 0, // constants
+ 0, // deps
+ 0, // options
+ 0, // temps
+ NULL, // ins
+ "UV0", // outs
+ "mov $O_UV0, $IUV0\n"
+};
+
+const ShaderFragment kVS_Load_UV1 = {
+ (1<<kInputUV1), // input
+ 0, // constants
+ 0, // deps
+ 0, // options
+ 0, // temps
+ NULL, // ins
+ "UV1", // outs
+ "mov $O_UV1, $IUV1\n"
+};
+
+const ShaderFragment kVS_Out_TexCoord = {
+ 0, // input
+ 0, // constants
+ 0, // deps
+ 0, // options
+ 0, // temps
+ "$0", // ins
+ NULL, // outs
+ "mov oT$PARAM, $I_0\n"
+};
+
+
+const ShaderFragment kVS_Out_Matrix2 = {
+ 0, // input
+ (1<<kConstMatrixTexture), // constants
+ 0, // deps
+ kOptionHasTexMatrix, // options
+ 1, // temps
+ "$0", // ins
+ NULL, // outs
+ "mul $TMP0, $I_0.y, $TMPARAM1\n"
+ "mad $TMP0, $TMPARAM0, $I_0.x, $TMP0\n"
+ "add oT$PARAM, $TMPARAM3, $TMP0\n"
+};
+
+const ShaderFragment kVS_Out_Matrix3 = {
+ 0, // input
+ (1<<kConstMatrixTexture), // constants
+ 0, // deps
+ kOptionHasTexMatrix, // options
+ 1, // temps
+ "$0", // ins
+ NULL, // outs
+ "mul $TMP0, $I_0.y, $TMPARAM1\n"
+ "mad $TMP0, $TMPARAM0, $I_0.x, $TMP0\n"
+ "mad $TMP0, $TMPARAM2, $I_0.z, $TMP0\n"
+ "add oT$PARAM, $TMPARAM3, $TMP0\n"
+};
+
+// --------------------------------------------------------------------------
+// Lighting
+
+const ShaderFragment kVS_Out_Diffuse_VertexColor= {
+ (1<<kInputColor), // input
+ 0, // constants
+ 0, // deps
+ 0, // options
+ 0, // temps
+ NULL, // ins
+ NULL, // outs
+ "mov oD0, $ICOL\n"
+};
+
+const ShaderFragment kVS_Light_Diffuse_Pre = {
+ 0, // input
+ (1<<kConstLightMisc), // constants
+ 0, // deps
+ 0, // options
+ 0, // temps
+ NULL, // ins
+ "DIFF", // outs
+
+ "mov $O_DIFF, c45.xxxz\n" // diffuse = 0
+};
+
+const ShaderFragment kVS_Light_Diffuse_Dir = {
+ 0, // input
+ (1<<kConstLightMisc) | (1<<kConstLightIndexes), // constants
+ (1<<kDep_CamSpaceN), // deps
+ 0, // options
+ 1, // temps
+ "CNOR", // ins
+ "DIFF", // outs
+
+ "mov $O_CNOR.w, c48.y\n" // CNOR.w is reused as light data index
+ "rep i1\n"
+ " mova a0.x, $O_CNOR.w\n"
+ " dp3 $TMP0.x, $O_CNOR, c61[a0.x]\n" // NdotL
+ " slt $TMP0.w, c45.x, $TMP0.x\n" // clamp = NdotL > 0
+ " mul $TMP0.xyz, $TMP0.x, c62[a0.x]\n" // doff = NdotL * lightColor
+ " mad $O_DIFF.xyz, $TMP0.w, $TMP0, $O_DIFF\n" // diffuse += diff * clamp
+ " add $O_CNOR.w, $O_CNOR.w, c45.y\n" // index += 4
+ "endrep\n"
+};
+
+const ShaderFragment kVS_Light_Diffuse_Point = {
+ 0, // input
+ (1<<kConstLightMisc) | (1<<kConstLightIndexes), // constants
+ (1<<kDep_CamSpaceN) | (1<<kDep_CamSpacePos), // deps
+ 0, // options
+ 3, // temps
+ "CNOR CPOS", // ins
+ "DIFF", // outs
+
+ "mov $O_CNOR.w, c48.z\n" // CNOR.w is reused as light data index
+ "rep i2\n"
+ " mova a0.x, $O_CNOR.w\n"
+ " add $TMP1.xyz, -$O_CPOS, c60[a0.x]\n" // toLight in view space
+ " dp3 $TMP0.w, $TMP1, $TMP1\n" // lightDirection = normalize(toLight)
+ " rsq $TMP1.w, $TMP0.w\n"
+ " mul $TMP1.xyz, $TMP1, $TMP1.w\n"
+ " dp3 $TMP1.x, $O_CNOR, $TMP1\n" // NdotL
+ " slt $TMP1.y, c63[a0.x].z, $TMP0.w\n" // range = range2 < toLight2
+ " mov $TMP1.z, c45.z\n" // 1
+ " mad $TMP0.w, c63[a0.x].w, $TMP0.w, $TMP1.z\n" // 1 + toLight2 * quadAttenuation
+ " rcp $TMP0.w, $TMP0.w\n" // attenuation
+ " mad $TMP0.w, $TMP1.y, -$TMP0.w, $TMP0.w\n" // attenuation = 0 if out of range
+ " sge $TMP1.y, $TMP1.x, c45.x\n" // clamp = NdotL > 0
+ " mul $TMP2, $TMP1.x, c62[a0.x]\n" // diff = NdotL * lightColor
+ " mul $TMP2, $TMP0.w, $TMP2\n" // diff *= attenuation
+ " mad $O_DIFF.xyz, $TMP1.y, $TMP2, $O_DIFF\n" // diffuse += diff * clamp
+ " add $O_CNOR.w, $O_CNOR.w, c45.y\n" // index += 4
+ "endrep\n"
+};
+
+
+
+const ShaderFragment kVS_Light_Diffuse_Spot = {
+ 0, // input
+ (1<<kConstLightMisc) | (1<<kConstLightIndexes), // constants
+ (1<<kDep_CamSpaceN) | (1<<kDep_CamSpacePos), // deps
+ 0, // options
+ 3, // temps
+ "CNOR CPOS", // ins
+ "DIFF", // outs
+
+ "mov $O_CNOR.w, c48.x\n" // CNOR.w is reused as light data index
+ "rep i0\n"
+ " mova a0.x, $O_CNOR.w\n"
+ " add $TMP1.xyz, -$O_CPOS, c60[a0.x]\n" // toLight in view space
+ " dp3 $TMP0.w, $TMP1, $TMP1\n" // lightDirection = normalize(toLight)
+ " rsq $TMP1.w, $TMP0.w\n"
+ " mul $TMP1.xyz, $TMP1, $TMP1.w\n"
+ " dp3 $TMP1.w, $O_CNOR, $TMP1\n" // NdotL
+ " dp3 $TMP1.x, $TMP1, c61[a0.x]\n" // rho = dot(L,lightAxisDirection)
+ " add $TMP1.x, $TMP1.x, -c63[a0.x].y\n" // rho-cos(phi/2)
+ " mul $TMP1.x, $TMP1.x, c63[a0.x].x\n" // spotAtten = (rho-cos(phi/2)) / (cos(theta/2)-cos(phi/2))
+ " mov $TMP1.z, c45.z\n" // 1
+ " mad $TMP1.y, c63[a0.x].w, $TMP0.w, $TMP1.z\n" // 1 + toLight2 * quadAttenuation
+ " rcp $TMP1.y, $TMP1.y\n" // attenuation
+ " slt $TMP0.w, c63[a0.x].z, $TMP0.w\n" // range = range2 < toLight2
+ " mad $TMP0.w, $TMP0.w, -$TMP1.y, $TMP1.y\n" // attenuation = 0 if out of range
+ " max $TMP1.x, $TMP1.x, c45.x\n" // spotAtten = saturate(spotAtten)
+ " min $TMP1.x, $TMP1.x, c45.z\n"
+ " mul $TMP0.w, $TMP0.w, $TMP1.x\n" // attenuation *= spotAtten
+ " sge $TMP1.x, $TMP1.w, c45.x\n" // clamp = NdotL > 0
+ " mul $TMP2, $TMP1.w, c62[a0.x]\n" // diff = NdotL * lightColor
+ " mul $TMP2, $TMP0.w, $TMP2\n" // diff *= attenuation
+ " mad $O_DIFF.xyz, $TMP1.x, $TMP2, $O_DIFF\n" // diffuse += diff * clamp
+ " add $O_CNOR.w, $O_CNOR.w, c45.y\n" // index += 4
+ "endrep\n"
+};
+
+
+const ShaderFragment kVS_Light_Specular_Pre = {
+ 0, // input
+ (1<<kConstLightMisc), // constants
+ 0, // deps
+ 0, // options
+ 0, // temps
+ NULL, // ins
+ "DIFF SPEC", // outs
+ "mov $O_DIFF, c45.xxxz\n" // diffuse = 0
+ "mov $O_SPEC, c45.x\n" // specular = 0
+};
+
+
+const ShaderFragment kVS_Light_Specular_Dir = {
+ 0, // input
+ (1<<kConstLightMisc) | (1<<kConstLightIndexes) | (1<<kConstMatSpecular), // constants
+ (1<<kDep_CamSpaceN) | (1<<kDep_ViewVector), // deps
+ 0, // options
+ 2, // temps
+ "CNOR VIEW", // ins
+ "DIFF SPEC", // outs
+
+ "mov $O_CNOR.w, c48.y\n" // CNOR.w is reused as light data index
+ "rep i1\n"
+ " mova a0.x, $O_CNOR.w\n"
+ " mov $TMP0.xyz, c61[a0.x]\n" // L = lightDirection
+ // diffuse
+ " dp3 $TMP1.x, $O_CNOR, $TMP0\n" // NdotL
+ " slt $TMP0.w, c45.x, $TMP1.x\n" // clamp = NdotL > 0
+ " mul $TMP1, $TMP1.x, c62[a0.x]\n" // diff = NdotL * lightColor
+ " mad $O_DIFF.xyz, $TMP0.w, $TMP1, $O_DIFF\n" // diffuse += diff * clamp
+ // spec
+ " add $TMP0.xyz, $TMP0, $O_VIEW\n" // L + V
+ " nrm $TMP1.xyz, $TMP0\n" // H = normalize(L + V)
+ " dp3 $TMP1.w, $TMP1, $O_CNOR\n" // H dot N
+ " max $TMP1.w, $TMP1.w, c45.x\n" // sp = max(H dot N, 0)
+ " pow $TMP1.w, $TMP1.w, c47.w\n" // sp = pow(sp, exponent)
+ " mul $TMP1.w, $TMP1.w, $TMP0.w\n" // sp *= clamp
+ " mad $O_SPEC.xyz, $TMP1.w, c62[a0.x], $O_SPEC\n" // spec += sp * lightColor
+
+ " add $O_CNOR.w, $O_CNOR.w, c45.y\n" // index += 4
+ "endrep\n"
+};
+
+
+const ShaderFragment kVS_Light_Specular_Point = {
+ 0, // input
+ (1<<kConstLightMisc) | (1<<kConstLightIndexes) | (1<<kConstMatSpecular), // constants
+ (1<<kDep_CamSpaceN) | (1<<kDep_CamSpacePos) | (1<<kDep_ViewVector), // deps
+ 0, // options
+ 3, // temps
+ "CNOR CPOS VIEW", // ins
+ "DIFF SPEC", // outs
+
+ "mov $O_CNOR.w, c48.z\n" // CNOR.w is reused as light data index
+ "rep i2\n"
+ " mova a0.x, $O_CNOR.w\n"
+ " add $TMP1.xyz, -$O_CPOS, c60[a0.x]\n" // toLight in view space
+ " dp3 $TMP0.w, $TMP1, $TMP1\n" // L = normalize(toLight)
+ " rsq $TMP1.w, $TMP0.w\n"
+ " mul $TMP1.xyz, $TMP1, $TMP1.w\n"
+ // diffuse
+ " dp3 $TMP0.x, $O_CNOR, $TMP1\n" // NdotL
+ " slt $TMP0.y, c63[a0.x].z, $TMP0.w\n" // range = range2 < toLight2
+ " mov $TMP0.z, c45.z\n" // 1
+ " mad $TMP0.w, c63[a0.x].w, $TMP0.w, $TMP0.z\n" // 1 + toLight2 * quadAttenuation
+ " rcp $TMP0.w, $TMP0.w\n" // attenuation
+ " mad $TMP0.w, $TMP0.y, -$TMP0.w, $TMP0.w\n" // attenuation = 0 if out of range
+ " sge $TMP0.y, $TMP0.x, c45.x\n" // clamp = NdotL > 0
+ " mul $TMP2, $TMP0.x, c62[a0.x]\n" // diff = NdotL * lightColor
+ " mul $TMP2, $TMP0.w, $TMP2\n" // diff *= attenuation
+ " mad $O_DIFF.xyz, $TMP0.y, $TMP2, $O_DIFF\n" // diffuse += diff * clamp
+ // spec
+ " add $TMP2.xyz, $TMP1, $O_VIEW\n" // L + V
+ " nrm $TMP1.xyz, $TMP2\n" // H = normalize(L + V)
+ " dp3 $TMP1.w, $TMP1, $O_CNOR\n" // H dot N
+ " max $TMP1.w, $TMP1.w, c45.x\n" // sp = max(H dot N, 0)
+ " pow $TMP1.w, $TMP1.w, c47.w\n" // sp = pow(sp, exponent)
+ " mul $TMP1.w, $TMP1.w, $TMP0.w\n" // sp *= attenuation
+ " mul $TMP1.w, $TMP1.w, $TMP0.y\n" // sp *= clamp
+ " mad $O_SPEC.xyz, $TMP1.w, c62[a0.x], $O_SPEC\n" // spec += sp * lightColor
+
+ " add $O_CNOR.w, $O_CNOR.w, c45.y\n" // index += 4
+ "endrep\n"
+};
+
+const ShaderFragment kVS_Light_Specular_Spot = {
+ 0, // input
+ (1<<kConstLightMisc) | (1<<kConstLightIndexes) | (1<<kConstMatSpecular), // constants
+ (1<<kDep_CamSpaceN) | (1<<kDep_CamSpacePos) | (1<<kDep_ViewVector), // deps
+ 0, // options
+ 3, // temps
+ "CNOR CPOS VIEW", // ins
+ "DIFF SPEC", // outs
+
+ "mov $O_CNOR.w, c48.x\n" // CNOR.w is reused as light data index
+ "rep i0\n"
+ " mova a0.x, $O_CNOR.w\n"
+ " add $TMP1.xyz, -$O_CPOS, c60[a0.x]\n" // toLight in view space
+ " dp3 $TMP0.w, $TMP1, $TMP1\n" // lightDirection = normalize(toLight)
+ " rsq $TMP1.w, $TMP0.w\n"
+ " mul $TMP1.xyz, $TMP1, $TMP1.w\n"
+ // diffuse
+ " dp3 $TMP1.w, $O_CNOR, $TMP1\n" // NdotL
+ " dp3 $TMP0.x, $TMP1, c61[a0.x]\n" // rho = dot(L,lightAxisDirection)
+ " add $TMP0.x, $TMP0.x, -c63[a0.x].y\n" // rho-cos(phi/2)
+ " mul $TMP0.x, $TMP0.x, c63[a0.x].x\n" // spotAtten = (rho-cos(phi/2)) / (cos(theta/2)-cos(phi/2))
+ " mov $TMP0.z, c45.z\n" // 1
+ " mad $TMP0.y, c63[a0.x].w, $TMP0.w, $TMP0.z\n" // 1 + toLight2 * quadAttenuation
+ " rcp $TMP0.y, $TMP0.y\n" // attenuation
+ " slt $TMP0.w, c63[a0.x].z, $TMP0.w\n" // range = range2 < toLight2
+ " mad $TMP0.w, $TMP0.w, -$TMP0.y, $TMP0.y\n" // attenuation = 0 if out of range
+ " max $TMP0.x, $TMP0.x, c45.x\n" // spotAtten = saturate(spotAtten)
+ " min $TMP0.x, $TMP0.x, c45.z\n"
+ " mul $TMP0.w, $TMP0.w, $TMP0.x\n" // attenuation *= spotAtten
+ " sge $TMP0.x, $TMP1.w, c45.x\n" // clamp = NdotL > 0
+ " mul $TMP2, $TMP1.w, c62[a0.x]\n" // diff = NdotL * lightColor
+ " mul $TMP2, $TMP0.w, $TMP2\n" // diff *= attenuation
+ " mad $O_DIFF.xyz, $TMP0.x, $TMP2, $O_DIFF\n" // diffuse += diff * clamp
+ // spec
+ " add $TMP2.xyz, $TMP1, $O_VIEW\n" // L + V
+ " nrm $TMP1.xyz, $TMP2\n" // H = normalize(L + V)
+ " dp3 $TMP1.w, $TMP1, $O_CNOR\n" // H dot N
+ " max $TMP1.w, $TMP1.w, c45.x\n" // sp = max(H dot N, 0)
+ " pow $TMP2.x, $TMP1.w, c47.w\n" // sp = pow(sp, exponent)
+ " mul $TMP2.x, $TMP2.x, $TMP0.w\n" // sp *= attenuation
+ " mul $TMP2.x, $TMP2.x, $TMP0.x\n" // sp *= clamp
+ " mad $O_SPEC.xyz, $TMP2.x, c62[a0.x], $O_SPEC\n" // spec += sp * lightColor
+
+ " add $O_CNOR.w, $O_CNOR.w, c45.y\n" // index += 4
+ "endrep\n"
+};
+
+
+const ShaderFragment kVS_Out_Diffuse_Lighting = {
+ 0, // input
+ (1<<kConstAmbient) | (1<<kConstMatDiffuse) | (1<<kConstLightMisc), // constants
+ 0, // deps
+ 0, // options
+ 0, // temps
+ "DIFF", // ins
+ NULL, // outs
+ "mul $O_DIFF, $O_DIFF, c46\n" // diffuse *= materialDiffuse
+ "add $O_DIFF.xyz, $O_DIFF, c44\n" // diffuse += ambient
+ "min oD0, $O_DIFF, c45.z\n" // diffuse = max(diffuse,1)
+};
+
+const ShaderFragment kVS_Out_Specular_Lighting = {
+ 0, // input
+ (1<<kConstMatSpecular) | (1<<kConstLightMisc), // constants
+ 0, // deps
+ 0, // options
+ 0, // temps
+ "SPEC", // ins
+ NULL, // outs
+ "mul $O_SPEC, $O_SPEC, c47\n" // specular *= materialSpecular
+ "min oD1, $O_SPEC, c45.z\n" // specular = max(specular,1)
+};
+
+const ShaderFragment kVS_Out_Diffuse_Lighting_ColorDiffuseAmbient = {
+ (1<<kInputColor), // input
+ (1<<kConstColorMatAmbient) | (1<<kConstAmbient) | (1<<kConstLightMisc), // constants
+ 0, // deps
+ 0, // options
+ 0, // temps
+ "DIFF", // ins
+ NULL, // outs
+ "mul $O_DIFF, $O_DIFF, $ICOL\n" // diffuse *= vertexColor
+ "mad $O_DIFF.xyz, $ICOL, c57, $O_DIFF\n" // diffuse += ambient * vertexColor
+ "add $O_DIFF.xyz, $O_DIFF, c44\n" // diffuse += emissive
+ "min oD0, $O_DIFF, c45.z\n" // diffuse = max(diffuse,1)
+};
+
+const ShaderFragment kVS_Out_Diffuse_Lighting_ColorEmission = {
+ (1<<kInputColor), // input
+ (1<<kConstAmbient) | (1<<kConstMatDiffuse) | (1<<kConstLightMisc), // constants
+ 0, // deps
+ 0, // options
+ 0, // temps
+ "DIFF", // ins
+ NULL, // outs
+ "mul $O_DIFF, $O_DIFF, c46\n" // diffuse *= materialDiffuse
+ "add $O_DIFF.xyz, c44, $O_DIFF\n" // diffuse += ambient
+ "add $O_DIFF, $O_DIFF, $ICOL\n" // diffuse += vertex color
+ "min oD0, $O_DIFF, c45.z\n" // diffuse = max(diffuse,1)
+};
+
+
+const ShaderFragment kVS_Out_Diffuse_White = {
+ 0, // input
+ (1<<kConstLightMisc), // constants
+ 0, // deps
+ 0, // options
+ 0, // temps
+ NULL, // ins
+ NULL, // outs
+ "mov oD0, c45.z\n"
+};
+
+
+// --------------------------------------------------------------------------
+
+
+static const ShaderFragment* kCommonDependencies[kDepCount] = {
+ &kVS_Temp_CamSpacePos,
+ &kVS_Temp_CamSpaceN,
+ &kVS_Temp_ViewVector,
+ &kVS_Temp_CamSpaceRefl,
+ &kVS_Load_Normal,
+};
+
+static bool IsAlNum( char c ) {
+ return c=='$' || c>='A' && c<='Z' || c>='0' && c<='9';
+}
+
+static const char* SkipTokens( const char* p, int count ) {
+ while( count-- ) {
+ while( IsAlNum(*p++) ) ;
+ if( *p == 0 )
+ return p;
+ ++p;
+ }
+ return p;
+}
+
+static std::string ExtractToken( const char** text ) {
+ const char* ptr = *text;
+ char c = *ptr;
+ while( IsAlNum(c) ) {
+ ++ptr;
+ c = *ptr;
+ }
+
+ if( ptr == *text )
+ return std::string();
+
+ // result
+ std::string res(*text, ptr);
+
+ // skip space after result
+ ++ptr;
+ *text = ptr;
+
+ return res;
+}
+
+void ShaderGenerator::AddFragment( const ShaderFragment* fragment, const char* inputNames, int param )
+{
+ // is already added?
+ FragmentData data(fragment, inputNames, param);
+ for( int i = 0; i < m_FragmentCount; ++i ) {
+ if( m_Fragments[i] == data )
+ return;
+ }
+
+ // add it's dependencies first
+ if( fragment->dependencies ) {
+ for( int i = 0; i < kDepCount; ++i ) {
+ // has this dependency?
+ if( !(fragment->dependencies & (1<<i)) )
+ continue;
+ AddFragment( kCommonDependencies[i] );
+ }
+ }
+
+ // add itself
+ m_Fragments[m_FragmentCount] = data;
+ m_FragmentCount++;
+ assert( m_FragmentCount < kMaxShaderFragments );
+}
+
+// Register plus it's living range - first and last shader fragment indices
+// on where it can be used.
+struct SavedRegister {
+ std::string name;
+ int firstUse;
+ int lastUse;
+ int regIndex;
+};
+typedef std::vector<SavedRegister> SavedRegisters;
+
+static inline int FindSavedRegister( const SavedRegisters& regs, const std::string& name )
+{
+ int n = regs.size();
+ for( int i = 0; i < n; ++i )
+ if( regs[i].name == name )
+ return i;
+ return -1;
+}
+
+void ShaderGenerator::GenerateShader( std::string& output, unsigned int& usedConstants )
+{
+ unsigned int usedConstantsMask = 0;
+
+ output.clear();
+ output.reserve(1024);
+ //debug.clear();
+
+ // shader input mappings
+ int inputMapping[kInputCount];
+ for( int i = 0; i < kInputCount; ++i )
+ inputMapping[i] = -1;
+ int usedInputStack[kInputCount];
+ int usedInputs = 0;
+
+ // saved registers across fragments
+ SavedRegisters savedRegisters;
+
+ // go over fragments and figure out inputs, saved registers and used constants
+ int maxTemps = 0;
+ for( int fi = 0; fi < m_FragmentCount; ++fi ) {
+ const ShaderFragment& frag = *m_Fragments[fi].fragment;
+
+ // fragment vertex inputs
+ for( int i = 0; i < kInputCount; ++i ) {
+ // does fragment use this input?
+ if( frag.inputs & (1<<i) ) {
+ // add to inputs list of in there yet
+ if( inputMapping[i] == -1 ) {
+ usedInputStack[usedInputs] = i;
+ inputMapping[i] = usedInputs;
+ ++usedInputs;
+ }
+ }
+ }
+
+ // remember output registers
+ if( frag.outs ) {
+ const char* outputs = frag.outs;
+ std::string token;
+ while( !(token = ExtractToken(&outputs)).empty() ) {
+ token = "$O_" + token;
+ //TODO: check that text has that token.
+ //TODO: check that text has no $O_ tokens that are not in the output
+ // add to list if not there yet
+ int savedIndex = FindSavedRegister( savedRegisters, token );
+ if( savedIndex == -1 )
+ {
+ SavedRegister r;
+ r.name = token;
+ r.firstUse = fi;
+ r.lastUse = fi;
+ r.regIndex = -1;
+ savedRegisters.push_back( r );
+ }
+ else
+ {
+ savedRegisters[savedIndex].lastUse = fi;
+ assert(savedRegisters[savedIndex].firstUse <= savedRegisters[savedIndex].lastUse);
+ }
+ }
+ }
+
+ // from fragment input registers, determine last use of saved registers
+ if( frag.ins ) {
+ const char* inputs = frag.ins;
+ std::string token;
+ while( !(token = ExtractToken(&inputs)).empty() ) {
+ // a parametrized token?
+ if( token[0] == '$' ) {
+ assert(token.size()==2);
+ assert(token[1]>='0' && token[1]<='9');
+ int index = token[1]-'0';
+ const char* inputNames = m_Fragments[fi].inputNames;
+ inputNames = SkipTokens( inputNames, index );
+ token = ExtractToken(&inputNames);
+ }
+ token = "$O_" + token;
+
+ //TODO: check that text has that token.
+ //TODO: check that text has no $O_ tokens that are not in the input
+ int savedIndex = FindSavedRegister( savedRegisters, token );
+ assert(savedIndex != -1);
+ assert(savedRegisters[savedIndex].lastUse <= fi);
+ savedRegisters[savedIndex].lastUse = fi;
+ }
+ }
+
+ maxTemps = std::max(maxTemps, frag.temps);
+
+ // used constants
+ usedConstantsMask |= frag.constants;
+ }
+
+ assert( savedRegisters.size() <= kMaxSavedRegisters );
+
+ // assign register indices to saved registers
+ int mapFragmentRegister[kMaxShaderFragments][kMaxTempRegisters]; // [fragment][index] = used or not?
+ memset(mapFragmentRegister, 0, sizeof(mapFragmentRegister));
+ for( size_t i = 0; i < savedRegisters.size(); ++i ) {
+ // find unused register over whole lifetime, and assign it
+ SavedRegister& sr = savedRegisters[i];
+ assert(sr.regIndex == -1);
+ for( int regIndex = 0; regIndex < kMaxTempRegisters; ++regIndex ) {
+ bool unused = true;
+ for( int fi = sr.firstUse; fi <= sr.lastUse; ++fi ) {
+ if( mapFragmentRegister[fi][regIndex] != 0 ) {
+ unused = false;
+ break;
+ }
+ }
+ if( unused ) {
+ for( int fi = sr.firstUse; fi <= sr.lastUse; ++fi )
+ mapFragmentRegister[fi][regIndex] = 1;
+ sr.regIndex = regIndex;
+ break;
+ }
+ }
+ assert(sr.regIndex != -1);
+ }
+
+ // generate prolog with declarations
+ output += "vs_2_0\n";
+ for( int i = 0; i < usedInputs; ++i ) {
+ output += kShaderInputDecls[usedInputStack[i]];
+ output += " v";
+ assert(i<=9);
+ output += ('0' + i);
+ output += '\n';
+ }
+
+ // go over fragments, transform register names and output
+ for( int fi = 0; fi < m_FragmentCount; ++fi ) {
+ const ShaderFragment& frag = *m_Fragments[fi].fragment;
+ int param = m_Fragments[fi].param;
+
+ output += '\n';
+ std::string text = frag.text;
+
+ std::string regname("r0");
+ std::string regname2("r00");
+
+ // input registers
+ regname[0] = 'v';
+ for( int i = 0; i < usedInputs; ++i ) {
+ int inputIndex = usedInputStack[i];
+ assert(i<=9);
+ regname[1] = '0' + i;
+ replace_string(text, kShaderInputNames[inputIndex], regname);
+ }
+
+ // fragment inputs
+ if( frag.ins ) {
+ const char* inputs = frag.ins;
+ std::string token;
+ while( !(token = ExtractToken(&inputs)).empty() ) {
+ std::string searchName;
+ std::string savedName;
+ // a parametrized token?
+ if( token[0] == '$' ) {
+ assert(token.size()==2);
+ assert(token[1]>='0' && token[1]<='9');
+ int index = token[1]-'0';
+ const char* inputNames = m_Fragments[fi].inputNames;
+ inputNames = SkipTokens( inputNames, index );
+ token = ExtractToken(&inputNames);
+ searchName = std::string("$I_") + char('0'+index);
+ } else {
+ searchName = "$O_" + token;
+ }
+ savedName = "$O_" + token;
+
+ // Assign register index to this saved reg
+ SavedRegisters::iterator it, itEnd = savedRegisters.end();
+ for( it = savedRegisters.begin(); it != itEnd; ++it ) {
+ const SavedRegister& sr = *it;
+ if( sr.name == savedName )
+ {
+ // replace with register value
+ regname[0] = 'r';
+ assert(sr.regIndex<=9);
+ regname[1] = '0' + sr.regIndex;
+ replace_string(text, searchName, regname);
+ break;
+ }
+ }
+ assert( it != itEnd );
+ }
+ }
+
+ // saved registers
+ if( frag.outs ) {
+ regname[0] = 'r';
+ SavedRegisters::iterator it, itEnd = savedRegisters.end();
+ for( it = savedRegisters.begin(); it != itEnd; ++it ) {
+ const SavedRegister& sr = *it;
+ assert(sr.regIndex<=9);
+ regname[1] = '0' + sr.regIndex;
+ replace_string(text, sr.name, regname);
+ }
+ }
+
+ // fragment-private temporary registers
+ regname[0] = 'r';
+ regname2[0] = 'r';
+ std::string tmpname("$TMP0");
+ int regIndex = 0;
+ for( int i = 0; i < frag.temps; ++i ) {
+ assert(i<=9);
+ tmpname[4] = '0' + i;
+ // find unused register at this fragment
+ while( regIndex < kMaxTempRegisters && mapFragmentRegister[fi][regIndex] != 0 )
+ ++regIndex;
+ assert(regIndex < kMaxTempRegisters);
+ if( regIndex > 9 ) {
+ regname2[1] = '1';
+ regname2[2] = '0' + (regIndex-10);
+ replace_string(text, tmpname, regname2);
+ } else {
+ regname[1] = '0' + regIndex;
+ replace_string(text, tmpname, regname);
+ }
+ ++regIndex;
+ }
+
+ // parameter
+ if( param >= 0 ) {
+ std::string paramString("0");
+ assert(param<=9);
+ paramString[0] = '0'+param;
+ replace_string(text, "$PARAM", paramString);
+ }
+
+ // texture matrix parameters
+ if( frag.options & kOptionHasTexMatrix ) {
+ std::string tmpstring("$TMPARAM0");
+ std::string paramString("c00");
+ for( int i = 0; i < 4; ++i ) {
+ assert(i<=9);
+ tmpstring[8] = '0' + i;
+ int constant = kConstantLocations[kConstMatrixTexture] + param*4 + i;
+ paramString[1] = '0' + constant/10;
+ paramString[2] = '0' + constant%10;
+ replace_string(text, tmpstring, paramString);
+ }
+ }
+
+ output += text;
+ }
+
+
+ usedConstants = usedConstantsMask;
+
+ // checks
+
+ // should be no '$' left
+ assert( output.find('$') == std::string::npos );
+
+ // debug info
+ //char buffer[1000];
+ //_snprintf_s( buffer, 1000, "Fragments: %i SavedRegs: %i\n", m_FragmentCount, maxTemps );
+ //debug += buffer;
+ //for( size_t i = 0; i < savedRegisters.size(); ++i ) {
+ // _snprintf_s( buffer, 1000, " saved %s [%i..%i] r%i\n", savedRegisters[i].name.c_str(), savedRegisters[i].firstUse, savedRegisters[i].lastUse, savedRegisters[i].regIndex );
+ // debug += buffer;
+ //}
+}
diff --git a/Runtime/GfxDevice/d3d/ShaderGenerator.h b/Runtime/GfxDevice/d3d/ShaderGenerator.h
new file mode 100644
index 0000000..ccc52b7
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/ShaderGenerator.h
@@ -0,0 +1,100 @@
+#pragma once
+#include <string>
+
+enum ShaderConstant {
+ kConstMatrixMVP, // model*view*proj
+ kConstMatrixMV, // model*view
+ kConstMatrixMV_IT, // model*view inverse transpose
+ kConstMatrixTexture,// texture matrix
+ kConstAmbient, // materialEmissive + sceneAmbient * materialAmbient
+ kConstColorMatAmbient, // various combos of kConstAmbient, based on color material mode
+ kConstLightMisc, // 0, 4, 1, 0.5
+ kConstMatDiffuse, // material diffuse
+ kConstMatSpecular, // material specular
+ kConstLightIndexes, // light start indexes * 4
+ kConstCount
+};
+
+extern const int kConstantLocations[kConstCount];
+
+
+struct ShaderFragment
+{
+ unsigned int inputs;
+ unsigned int constants;
+ unsigned int dependencies;
+ unsigned int options;
+ int temps;
+ const char* ins;
+ const char* outs;
+ const char* text;
+};
+
+
+class ShaderGenerator
+{
+public:
+ enum {
+ kMaxShaderFragments = 32,
+ kMaxTempRegisters = 12,
+ kMaxSavedRegisters = 16,
+ };
+
+private:
+ struct FragmentData {
+ FragmentData() : fragment(NULL), inputNames(NULL), param(0) { }
+ FragmentData( const ShaderFragment* f, const char* inames, int p ) : fragment(f), inputNames(inames), param(p) { }
+ bool operator==( const FragmentData& rhs ) const {
+ return
+ fragment==rhs.fragment &&
+ param==rhs.param &&
+ ((inputNames==NULL && rhs.inputNames==NULL) || (inputNames && rhs.inputNames && !strcmp(inputNames, rhs.inputNames)));
+ }
+
+ const ShaderFragment* fragment;
+ const char* inputNames;
+ int param;
+ };
+
+public:
+
+ ShaderGenerator() : m_FragmentCount(0)
+ {
+ }
+
+ void AddFragment( const ShaderFragment* fragment, const char* inputNames = NULL, int param = -1 );
+ void GenerateShader( std::string& output, unsigned int& usedConstants );
+
+private:
+ int m_FragmentCount;
+ FragmentData m_Fragments[kMaxShaderFragments];
+};
+
+
+extern const ShaderFragment kVS_Pos;
+extern const ShaderFragment kVS_Light_Diffuse_Pre;
+extern const ShaderFragment kVS_Light_Diffuse_Dir;
+extern const ShaderFragment kVS_Light_Diffuse_Point;
+extern const ShaderFragment kVS_Light_Diffuse_Spot;
+extern const ShaderFragment kVS_Light_Specular_Pre;
+extern const ShaderFragment kVS_Light_Specular_Dir;
+extern const ShaderFragment kVS_Light_Specular_Point;
+extern const ShaderFragment kVS_Light_Specular_Spot;
+extern const ShaderFragment kVS_Out_Diffuse_Lighting;
+extern const ShaderFragment kVS_Out_Specular_Lighting;
+extern const ShaderFragment kVS_Out_Diffuse_Lighting_ColorDiffuseAmbient;
+extern const ShaderFragment kVS_Out_Diffuse_Lighting_ColorEmission;
+extern const ShaderFragment kVS_Out_Diffuse_VertexColor;
+extern const ShaderFragment kVS_Out_Diffuse_White;
+extern const ShaderFragment kVS_Load_UV0;
+extern const ShaderFragment kVS_Load_UV1;
+extern const ShaderFragment kVS_Load_Normal;
+extern const ShaderFragment kVS_Normalize_Normal;
+extern const ShaderFragment kVS_Out_TexCoord;
+extern const ShaderFragment kVS_Out_Matrix2;
+extern const ShaderFragment kVS_Out_Matrix3;
+extern const ShaderFragment kVS_Temp_CamSpacePos;
+extern const ShaderFragment kVS_Temp_CamSpaceN;
+extern const ShaderFragment kVS_Temp_CamSpaceRefl;
+extern const ShaderFragment kVS_Temp_ObjSpacePos;
+extern const ShaderFragment kVS_Temp_SphereMap;
diff --git a/Runtime/GfxDevice/d3d/ShaderPatchingD3D9.cpp b/Runtime/GfxDevice/d3d/ShaderPatchingD3D9.cpp
new file mode 100644
index 0000000..87f8e17
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/ShaderPatchingD3D9.cpp
@@ -0,0 +1,376 @@
+#include "UnityPrefix.h"
+#include "ShaderPatchingD3D9.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Utilities/Word.h"
+
+#define DEBUG_FOG_PATCHING 0
+
+
+static inline bool IsNewline( char c ) { return c == '\n' || c == '\r'; }
+
+static int FindMaxUsedDclIndex (const std::string& src, char registerName)
+{
+ size_t n = src.size();
+ size_t pos = 0;
+ int maxDcl = -1;
+ while ((pos = src.find("dcl_", pos)) != std::string::npos)
+ {
+ // skip "dcl_"
+ pos += 4;
+
+ // skip until end of dcl_*
+ while (pos < n && !isspace(src[pos]))
+ ++pos;
+ // skip space
+ while (pos < n && isspace(src[pos]))
+ ++pos;
+ // is this an needed register type?
+ if (pos < n && src[pos] == registerName) {
+ int number = -1;
+ sscanf (src.c_str() + pos + 1, "%d", &number);
+ if (number > maxDcl)
+ maxDcl = number;
+ }
+ }
+ return maxDcl;
+}
+
+
+static bool InsertFogDcl (std::string& src, const std::string& registerName)
+{
+ // insert dcl_fog after vs_3_0/ps_3_0 line
+ size_t pos = 6;
+ while (pos < src.size() && !IsNewline(src[pos])) // skip until newline
+ ++pos;
+ while (pos < src.size() && IsNewline(src[pos])) // skip newlines
+ ++pos;
+ if (pos >= src.size())
+ return false;
+ src.insert (pos, Format("dcl_fog %s\n", registerName.c_str()));
+ return true;
+}
+
+
+bool PatchPixelShaderFogD3D9 (std::string& src, FogMode fog, int fogColorReg, int fogParamsReg)
+{
+ const bool isPS3 = !strncmp(src.c_str(), "ps_3_0", 6);
+ if (!isPS3)
+ return true; // nothing to do
+
+ #if DEBUG_FOG_PATCHING
+ printf_console ("D3D9 fog patching: original pixel shader:\n%s\n", src.c_str());
+ #endif
+
+ // SM3.0 has 10 input registers (v0..v9).
+
+ const int maxDclReg = FindMaxUsedDclIndex (src, 'v');
+ if (maxDclReg >= 9)
+ {
+ // out of registers
+ return false;
+ }
+ const int fogReg = 9;
+ if (!InsertFogDcl (src, Format("v%d.x", fogReg)))
+ {
+ DebugAssert (!"failed to insert fog dcl");
+ return false;
+ }
+
+ // Remap writes to oC0 with r30
+ const int colorReg = 30;
+ const int tempReg = 31;
+ replace_string (src, "oC0", "r30");
+
+ // make sure source ends with a newline
+ if (!IsNewline(src[src.size()-1]))
+ src += '\n';
+
+ // inject fog handling code
+ if (fog == kFogExp2)
+ {
+ // fog = exp(-(density*z)^2)
+ src += Format("mul r%d.x, c%d.x, v%d.x\n", tempReg, fogParamsReg, fogReg); // tmp = (density/sqrt(ln(2))) * fog
+ src += Format("mul r%d.x, r%d.x, r%d.x\n", tempReg, tempReg, tempReg); // tmp = tmp * tmp
+ src += Format("exp_sat r%d.x, -r%d.x\n", tempReg, tempReg); // tmp = saturate (exp2 (-tmp))
+ src += Format("lrp r%d.rgb, r%d.x, r%d, c%d\n", colorReg, tempReg, colorReg, fogColorReg); // color.rgb = lerp (color, fogColor, tmp)
+ }
+ else if (fog == kFogExp)
+ {
+ // fog = exp(-density*z)
+ src += Format("mul r%d.x, c%d.y, v%d.x\n", tempReg, fogParamsReg, fogReg); // tmp = (density/ln(2)) * fog
+ src += Format("exp_sat r%d.x, -r%d.x\n", tempReg, tempReg); // tmp = saturate (exp2 (-tmp))
+ src += Format("lrp r%d.rgb, r%d.x, r%d, c%d\n", colorReg, tempReg, colorReg, fogColorReg); // color.rgb = lerp (color, fogColor, tmp)
+ }
+ else if (fog == kFogLinear)
+ {
+ // fog = (end-z)/(end-start)
+ src += Format("mad_sat r%d.x, c%d.z, v%d.x, c%d.w\n", tempReg, fogParamsReg, fogReg, fogParamsReg); // tmp = (-1/(end-start)) * fog + (end/(end-start))
+ src += Format("lrp r%d.rgb, r%d.x, r%d, c%d\n", colorReg, tempReg, colorReg, fogColorReg); // color.rgb = lerp (color, fogColor, tmp)
+ }
+
+
+ // append final move into oC0
+ src += Format("mov oC0, r%d\n", colorReg);
+
+ #if DEBUG_FOG_PATCHING
+ printf_console ("D3D9 fog patching: after patching, fog mode %d:\n%s\n", fog, src.c_str());
+ #endif
+
+ return true;
+}
+
+
+bool PatchVertexShaderFogD3D9 (std::string& src)
+{
+ const bool isVS3 = !strncmp(src.c_str(), "vs_3_0", 6);
+ if (!isVS3)
+ return true; // nothing to do
+
+ #if DEBUG_FOG_PATCHING
+ printf_console ("D3D9 fog patching: original vertex shader:\n%s\n", src.c_str());
+ #endif
+
+ // SM3.0 has 12 output registers (o0..o11), but the pixel shader only has 10 input ones.
+ // Play it safe and let's assume we only have 10 here.
+
+ const int maxDclReg = FindMaxUsedDclIndex (src, 'o');
+ if (maxDclReg >= 9)
+ {
+ // out of registers
+ return false;
+ }
+ const int fogReg = 9;
+ std::string fogRegName = Format("o%d", fogReg);
+ if (!InsertFogDcl (src, fogRegName))
+ {
+ DebugAssert (!"failed to insert fog dcl");
+ return false;
+ }
+
+ // find write to o0, and do the same for oFog
+ size_t posWrite = src.find ("o0.z,");
+ bool writesFullPos = false;
+ if (posWrite == std::string::npos)
+ {
+ posWrite = src.find ("o0,");
+ if (posWrite == std::string::npos)
+ {
+ DebugAssert (!"couldn't find write to o0");
+ return false;
+ }
+ writesFullPos = true;
+ }
+
+ // get whole line
+ size_t n = src.size();
+ size_t posWriteStart = posWrite, posWriteEnd = posWrite;
+ while (posWriteStart > 0 && !IsNewline(src[posWriteStart])) --posWriteStart;
+ ++posWriteStart;
+ while (posWriteEnd < n && !IsNewline(src[posWriteEnd])) ++posWriteEnd;
+
+ std::string instr = src.substr (posWriteStart, posWriteEnd-posWriteStart);
+ if (writesFullPos)
+ {
+ replace_string (instr, "o0", fogRegName, 0);
+ instr += ".z";
+ }
+ else
+ {
+ replace_string (instr, "o0.z", fogRegName, 0);
+ }
+ instr += '\n';
+
+ // insert fog code just after write to position
+ src.insert (posWriteEnd+1, instr);
+
+ #if DEBUG_FOG_PATCHING
+ printf_console ("D3D9 fog patching: after patching:\n%s\n", src.c_str());
+ #endif
+
+ return true;
+}
+
+
+// --------------------------------------------------------------------------
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (ShaderPatchingD3D9Tests)
+{
+
+TEST(FindMaxDclIndexNotPresent)
+{
+ CHECK_EQUAL (-1, FindMaxUsedDclIndex("", 'v'));
+ CHECK_EQUAL (-1, FindMaxUsedDclIndex("foobar", 'v'));
+ CHECK_EQUAL (-1, FindMaxUsedDclIndex("dcl_", 'v'));
+ CHECK_EQUAL (-1, FindMaxUsedDclIndex("dcl_foo", 'v'));
+ CHECK_EQUAL (-1, FindMaxUsedDclIndex("dcl_foo ", 'v'));
+ CHECK_EQUAL (-1, FindMaxUsedDclIndex("dcl_foo v", 'v'));
+}
+TEST(FindMaxDclIndexOne)
+{
+ CHECK_EQUAL (0, FindMaxUsedDclIndex("dcl_foobar v0", 'v'));
+ CHECK_EQUAL (1, FindMaxUsedDclIndex("dcl_foobar v1", 'v'));
+ CHECK_EQUAL (2, FindMaxUsedDclIndex("dcl_foobar v2.x", 'v'));
+ CHECK_EQUAL (3, FindMaxUsedDclIndex("dcl_foobar v3.rgb", 'v'));
+ CHECK_EQUAL (6, FindMaxUsedDclIndex("dcl_foobar v6", 'v'));
+ CHECK_EQUAL (10, FindMaxUsedDclIndex("dcl_foobar v10", 'v'));
+ CHECK_EQUAL (0, FindMaxUsedDclIndex("ps_3_0\ndcl_foobar v0\nmov oC0, v0", 'v'));
+}
+TEST(FindMaxDclIndexMultiple)
+{
+ CHECK_EQUAL (2, FindMaxUsedDclIndex("dcl_foobar v0\ndcl_foobar v2", 'v'));
+ CHECK_EQUAL (3, FindMaxUsedDclIndex("dcl_foobar v3\ndcl_foobar v1", 'v'));
+}
+
+TEST(PatchVSZWrite)
+{
+ std::string s;
+ s = "vs_3_0\n"
+ "dcl_position o0\n"
+ "dp4 o0.z, c0, c1\n"
+ ;
+ CHECK (PatchVertexShaderFogD3D9(s));
+ CHECK_EQUAL(
+ "vs_3_0\n"
+ "dcl_fog o9\n"
+ "dcl_position o0\n"
+ "dp4 o0.z, c0, c1\n"
+ "dp4 o9, c0, c1\n"
+ , s);
+}
+TEST(PatchVSFullWrite)
+{
+ std::string s;
+ s = "vs_3_0\n"
+ "dcl_position o0\n"
+ "mov o0, c0\n"
+ ;
+ CHECK (PatchVertexShaderFogD3D9(s));
+ CHECK_EQUAL(
+ "vs_3_0\n"
+ "dcl_fog o9\n"
+ "dcl_position o0\n"
+ "mov o0, c0\n"
+ "mov o9, c0.z\n"
+ , s);
+}
+TEST(PatchVSWriteNotAtEnd)
+{
+ std::string s;
+ s = "vs_3_0\n"
+ "dcl_position o0\n"
+ "mov o0, r0\n"
+ "mov r0, r1\n"
+ ;
+ CHECK (PatchVertexShaderFogD3D9(s));
+ CHECK_EQUAL(
+ "vs_3_0\n"
+ "dcl_fog o9\n"
+ "dcl_position o0\n"
+ "mov o0, r0\n"
+ "mov o9, r0.z\n"
+ "mov r0, r1\n"
+ , s);
+}
+TEST(PatchPSDisjointColorAlphaWrite)
+{
+ std::string s =
+ "ps_3_0\n"
+ "; 31 ALU, 2 TEX\n"
+ "dcl_2d s0\n"
+ "dcl_2d s1\n"
+ "def c5, 0.0, 128.0, 2.0, 0\n"
+ "dcl_texcoord0 v0.xy\n"
+ "dcl_texcoord1 v1.xyz\n"
+ "dcl_texcoord2 v2.xyz\n"
+ "dcl_texcoord3 v3.xyz\n"
+ "dcl_texcoord4 v4\n"
+ "texldp r3.x, v4, s1\n"
+ "dp3_pp r0.x, v3, v3\n"
+ "rsq_pp r0.x, r0.x\n"
+ "mad_pp r0.xyz, r0.x, v3, c0\n"
+ "dp3_pp r0.w, r0, r0\n"
+ "rsq_pp r0.w, r0.w\n"
+ "mul_pp r0.xyz, r0.w, r0\n"
+ "mov_pp r0.w, c4.x\n"
+ "dp3_pp r0.x, v1, r0\n"
+ "dp3_pp r2.x, v1, c0\n"
+ "mul_pp r1.y, c5, r0.w\n"
+ "max_pp r1.x, r0, c5\n"
+ "pow r0, r1.x, r1.y\n"
+ "mov r1.x, r0\n"
+ "texld r0, v0, s0\n"
+ "mul r1.w, r0, r1.x\n"
+ "mul_pp r1.xyz, r0, c3\n"
+ "mul_pp r0.xyz, r1, c1\n"
+ "max_pp r2.x, r2, c5\n"
+ "mul_pp r2.xyz, r0, r2.x\n"
+ "mov_pp r0.xyz, c1\n"
+ "mul_pp r0.xyz, c2, r0\n"
+ "mad r0.xyz, r0, r1.w, r2\n"
+ "mul_pp r2.w, r3.x, c5.z\n"
+ "mul r0.xyz, r0, r2.w\n"
+ "mad_pp oC0.xyz, r1, v2, r0\n" // color RGB
+ "mov_pp r2.x, c1.w\n"
+ "mul_pp r0.x, c2.w, r2\n"
+ "mul_pp r0.y, r0.w, c3.w\n"
+ "mul r0.x, r1.w, r0\n"
+ "mad oC0.w, r3.x, r0.x, r0.y\n"; // color A
+ std::string exps =
+ "ps_3_0\n"
+ "dcl_fog v9.x\n"
+ "; 31 ALU, 2 TEX\n"
+ "dcl_2d s0\n"
+ "dcl_2d s1\n"
+ "def c5, 0.0, 128.0, 2.0, 0\n"
+ "dcl_texcoord0 v0.xy\n"
+ "dcl_texcoord1 v1.xyz\n"
+ "dcl_texcoord2 v2.xyz\n"
+ "dcl_texcoord3 v3.xyz\n"
+ "dcl_texcoord4 v4\n"
+ "texldp r3.x, v4, s1\n"
+ "dp3_pp r0.x, v3, v3\n"
+ "rsq_pp r0.x, r0.x\n"
+ "mad_pp r0.xyz, r0.x, v3, c0\n"
+ "dp3_pp r0.w, r0, r0\n"
+ "rsq_pp r0.w, r0.w\n"
+ "mul_pp r0.xyz, r0.w, r0\n"
+ "mov_pp r0.w, c4.x\n"
+ "dp3_pp r0.x, v1, r0\n"
+ "dp3_pp r2.x, v1, c0\n"
+ "mul_pp r1.y, c5, r0.w\n"
+ "max_pp r1.x, r0, c5\n"
+ "pow r0, r1.x, r1.y\n"
+ "mov r1.x, r0\n"
+ "texld r0, v0, s0\n"
+ "mul r1.w, r0, r1.x\n"
+ "mul_pp r1.xyz, r0, c3\n"
+ "mul_pp r0.xyz, r1, c1\n"
+ "max_pp r2.x, r2, c5\n"
+ "mul_pp r2.xyz, r0, r2.x\n"
+ "mov_pp r0.xyz, c1\n"
+ "mul_pp r0.xyz, c2, r0\n"
+ "mad r0.xyz, r0, r1.w, r2\n"
+ "mul_pp r2.w, r3.x, c5.z\n"
+ "mul r0.xyz, r0, r2.w\n"
+ "mad_pp r30.xyz, r1, v2, r0\n"
+ "mov_pp r2.x, c1.w\n"
+ "mul_pp r0.x, c2.w, r2\n"
+ "mul_pp r0.y, r0.w, c3.w\n"
+ "mul r0.x, r1.w, r0\n"
+ "mad r30.w, r3.x, r0.x, r0.y\n"
+ "mul r31.x, c7.x, v9.x\n"
+ "mul r31.x, r31.x, r31.x\n"
+ "exp_sat r31.x, -r31.x\n"
+ "lrp r30.rgb, r31.x, r30, c6\n"
+ "mov oC0, r30\n";
+ CHECK (PatchPixelShaderFogD3D9(s, kFogExp2, 6, 7));
+ CHECK_EQUAL(exps, s);
+}
+
+} // SUITE
+
+#endif // ENABLE_UNIT_TESTS
diff --git a/Runtime/GfxDevice/d3d/ShaderPatchingD3D9.h b/Runtime/GfxDevice/d3d/ShaderPatchingD3D9.h
new file mode 100644
index 0000000..e36a619
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/ShaderPatchingD3D9.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include <string>
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+bool PatchVertexShaderFogD3D9 (std::string& src);
+bool PatchPixelShaderFogD3D9 (std::string& src, FogMode fog, int fogColorReg, int fogParamsReg);
diff --git a/Runtime/GfxDevice/d3d/TexturesD3D9.cpp b/Runtime/GfxDevice/d3d/TexturesD3D9.cpp
new file mode 100644
index 0000000..d2baef9
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/TexturesD3D9.cpp
@@ -0,0 +1,696 @@
+#include "UnityPrefix.h"
+#include "TexturesD3D9.h"
+#include "Runtime/Graphics/TextureFormat.h"
+#include "Runtime/Graphics/Image.h"
+#include "D3D9Context.h"
+#include "Runtime/Allocator/FixedSizeAllocator.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Graphics/S3Decompression.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "D3D9Utils.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/GfxDevice/VramLimits.h"
+#include "Runtime/GfxDevice/TextureUploadUtils.h"
+#include "Runtime/GfxDevice/TextureIdMap.h"
+#include "External/ProphecySDK/include/prcore/Surface.hpp"
+#include "Runtime/Profiler/MemoryProfiler.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+
+struct D3DTexture
+{
+ explicit D3DTexture( IDirect3DBaseTexture9* tex )
+ : texture(tex), wrapMode(D3DTADDRESS_CLAMP), minFilter(D3DTEXF_POINT), magFilter(D3DTEXF_POINT), mipFilter(D3DTEXF_NONE), aniso(1), sRGB(0) { }
+
+ IDirect3DBaseTexture9* texture;
+ D3DTEXTUREADDRESS wrapMode;
+ D3DTEXTUREFILTERTYPE minFilter;
+ D3DTEXTUREFILTERTYPE magFilter;
+ D3DTEXTUREFILTERTYPE mipFilter;
+ int aniso;
+ bool sRGB;
+};
+
+typedef FixedSizeAllocator<sizeof(D3DTexture)> TextureAllocator;
+static TextureAllocator* _TextureAlloc = NULL;
+
+namespace TextureD3D9Alloc
+{
+ void StaticInitialize()
+ {
+ _TextureAlloc = UNITY_NEW_AS_ROOT(TextureAllocator(kMemGfxDevice),kMemGfxDevice, "TextureStructs", "");
+ }
+
+ void StaticDestroy()
+ {
+ UNITY_DELETE(_TextureAlloc, kMemGfxDevice);
+ }
+}
+
+static RegisterRuntimeInitializeAndCleanup s_TextureAllocManagerCallbacks(TextureD3D9Alloc::StaticInitialize, TextureD3D9Alloc::StaticDestroy);
+
+static inline intptr_t AllocD3DTexture(IDirect3DBaseTexture9* tex)
+{
+ return (intptr_t)(new (_TextureAlloc->alloc()) D3DTexture(tex));
+}
+
+static inline D3DTexture* QueryD3DTexture(TextureID textureID)
+{
+ return (D3DTexture*)TextureIdMap::QueryNativeTexture(textureID);
+}
+
+
+static D3DCOLOR ColorToD3D( const float color[4] )
+{
+ return D3DCOLOR_RGBA( NormalizedToByte(color[0]), NormalizedToByte(color[1]), NormalizedToByte(color[2]), NormalizedToByte(color[3]) );
+}
+
+
+struct FormatDesc {
+ TextureFormat unityformat;
+ D3DFORMAT d3dformat;
+ int bpp;
+ prcore::PixelFormat prformat;
+};
+
+const static FormatDesc kTextureFormatTable[kTexFormatPCCount+2] = // +1 for A8L8 case
+{
+ { kTexFormatPCCount, D3DFMT_UNKNOWN, 0, prcore::PixelFormat() },
+ { kTexFormatAlpha8, D3DFMT_A8, 1, prcore::PixelFormat(8,0,0xff) }, // Alpha8
+ { kTexFormatARGB4444, D3DFMT_A4R4G4B4, 2, prcore::PixelFormat(16,0x00000f00,0x000000f0,0x0000000f,0x0000f000) }, // ARGB4444
+ { kTexFormatRGB24, D3DFMT_X8R8G8B8, 4, prcore::PixelFormat(32,0x00ff0000,0x0000ff00,0x000000ff,0xff000000) }, // RGB24
+ { kTexFormatRGBA32, D3DFMT_A8R8G8B8, 4, prcore::PixelFormat(32,0x00ff0000,0x0000ff00,0x000000ff,0xff000000) }, // RGBA32
+ { kTexFormatARGB32, D3DFMT_A8R8G8B8, 4, prcore::PixelFormat(32,0x00ff0000,0x0000ff00,0x000000ff,0xff000000) }, // ARGB32
+ { kTexFormatARGBFloat, D3DFMT_UNKNOWN, 0, prcore::PixelFormat() }, // ARGBFloat
+ { kTexFormatRGB565, D3DFMT_R5G6B5, 2, prcore::PixelFormat(16,0x0000f800,0x000007e0,0x0000001f,0x00000000) }, // RGB565
+ { kTexFormatBGR24, D3DFMT_X8R8G8B8, 4, prcore::PixelFormat(32,0x00ff0000,0x0000ff00,0x000000ff,0xff000000) }, // BGR24
+ { kTexFormatAlphaLum16, D3DFMT_L16, 0, prcore::PixelFormat() }, // AlphaLum16
+ { kTexFormatDXT1, D3DFMT_DXT1, 0, prcore::PixelFormat() }, // DXT1
+ { kTexFormatDXT3, D3DFMT_DXT3, 0, prcore::PixelFormat() }, // DXT3
+ { kTexFormatDXT5, D3DFMT_DXT5, 0, prcore::PixelFormat() }, // DXT5
+ { kTexFormatRGBA4444, D3DFMT_A4R4G4B4, 2, prcore::PixelFormat(16,0x00000f00,0x000000f0,0x0000000f,0x0000f000) }, // RGBA4444
+
+ // following are not Unity formats, but might be used as fallbacks for some unsupported formats
+ { kTexFormatAlphaLum16, D3DFMT_A8L8, 2, prcore::PixelFormat(16,0x00ff,0xff00) }, // A8L8, used on cards that don't support A8; alpha -> alpha
+};
+
+const static FormatDesc kTextureFormatETC =
+{
+ kTexFormatETC_RGB4, D3DFMT_X8R8G8B8, 4, prcore::PixelFormat(32,0x00ff0000,0x0000ff00,0x000000ff,0xff000000)
+};
+
+const static FormatDesc kTextureFormatATC[2] =
+{
+ { kTexFormatATC_RGB4, D3DFMT_X8R8G8B8, 4, prcore::PixelFormat(32,0x00ff0000,0x0000ff00,0x000000ff,0xff000000) }, // RGB24
+ { kTexFormatATC_RGBA8, D3DFMT_A8R8G8B8, 4, prcore::PixelFormat(32,0x00ff0000,0x0000ff00,0x000000ff,0xff000000) }, // RGBA32
+};
+
+
+D3DFORMAT GetD3D9TextureFormat( TextureFormat inFormat )
+{
+ return kTextureFormatTable[inFormat].d3dformat;
+}
+
+static const FormatDesc& GetUploadFormat( TextureFormat inFormat, bool forceFallbackFormat = false )
+{
+ if (forceFallbackFormat)
+ {
+ return kTextureFormatTable[kTexFormatARGB32];
+ }
+ else if( inFormat == kTexFormatAlpha8 && !gGraphicsCaps.d3d.hasTextureFormatA8 )
+ {
+ // A8 not supported: A8L8 or fallback one depending on support
+ if( gGraphicsCaps.d3d.hasTextureFormatA8L8 )
+ return kTextureFormatTable[ kTexFormatPCCount ]; // return A8L8 option, see table above
+ else
+ return kTextureFormatTable[kTexFormatARGB32];
+ }
+ else if( IsCompressedDXTTextureFormat(inFormat) && !gGraphicsCaps.hasS3TCCompression )
+ {
+ // Compressed format not supported: decompress into fallback format
+ return kTextureFormatTable[kTexFormatARGB32];
+ }
+ else if ( IsCompressedETCTextureFormat(inFormat) )
+ {
+ return kTextureFormatETC;
+ }
+ else if ( IsCompressedATCTextureFormat(inFormat) )
+ {
+ return kTextureFormatATC[ HasAlphaTextureFormat(inFormat)? 1 : 0 ];
+ }
+ else if (!gGraphicsCaps.d3d.hasBaseTextureFormat[inFormat])
+ {
+ // This format not supported in general: convert to fallback format
+ return kTextureFormatTable[kTexFormatARGB32];
+ }
+
+ // All ok, return incoming format
+ return kTextureFormatTable[inFormat];
+}
+
+intptr_t TexturesD3D9::RegisterNativeTexture(IDirect3DBaseTexture9* texture) const
+{
+ return AllocD3DTexture(texture);
+}
+
+void TexturesD3D9::UpdateNativeTexture(TextureID textureID, IDirect3DBaseTexture9* texture)
+{
+ D3DTexture* target = QueryD3DTexture(textureID);
+ if(target)
+ target->texture = texture;
+ else
+ AddTexture(textureID, texture);
+}
+
+void TexturesD3D9::AddTexture( TextureID textureID, IDirect3DBaseTexture9* texture )
+{
+ TextureIdMap::UpdateTexture(textureID, AllocD3DTexture(texture));
+}
+
+void TexturesD3D9::RemoveTexture( TextureID textureID )
+{
+ D3DTexture* target = QueryD3DTexture(textureID);
+ if(target)
+ {
+ target->~D3DTexture();
+ _TextureAlloc->free(target);
+ }
+ TextureIdMap::RemoveTexture(textureID);
+}
+
+IDirect3DBaseTexture9* TexturesD3D9::GetTexture( TextureID textureID ) const
+{
+ D3DTexture* target = QueryD3DTexture(textureID);
+ return target ? target->texture : 0;
+}
+
+
+
+static void BlitAlphaLum16 (int width, int height, D3DFORMAT d3dFormat, const UInt8* srcData, UInt8* destData, int pitch)
+{
+ // Handle AlphaLum16 case. ProphecySDK does not support 16 bit/channel formats,
+ // so we blit manually.
+ UInt32 rowBytes = GetRowBytesFromWidthAndFormat(width,kTexFormatAlphaLum16);
+ const UInt8* srcRowData = srcData;
+ UInt8* destRowData = destData;
+ if( d3dFormat == D3DFMT_L16 )
+ {
+ for( int r = 0; r < height; ++r )
+ {
+ memcpy( destRowData, srcRowData, rowBytes );
+ srcRowData += rowBytes;
+ destRowData += pitch;
+ }
+ }
+ else if( d3dFormat == D3DFMT_L8 )
+ {
+ for( int r = 0; r < height; ++r )
+ {
+ for( int c = 0; c < width; ++c )
+ destRowData[c] = srcRowData[c*2+1];
+ srcRowData += rowBytes;
+ destRowData += pitch;
+ }
+ }
+ else
+ {
+ AssertIf( d3dFormat != D3DFMT_A8R8G8B8 );
+ for( int r = 0; r < height; ++r )
+ {
+ for( int c = 0; c < width; ++c )
+ {
+ DWORD val = srcRowData[c*2+1];
+ ((D3DCOLOR*)destRowData)[c] = 0xFF000000 | (val<<16) | (val<<8) | (val);
+ }
+ srcRowData += rowBytes;
+ destRowData += pitch;
+ }
+ }
+}
+
+void InitRGBA32Buffer(int width, int height, UInt8*& buffer, int& srcPitch, prcore::PixelFormat& pf)
+{
+ int imageSize = CalculateImageSize( width, height, kTexFormatRGBA32 );
+ if( buffer == NULL )
+ buffer = new UInt8[imageSize];
+ srcPitch = GetRowBytesFromWidthAndFormat(width, kTexFormatRGBA32);
+ pf = GetProphecyPixelFormat(kTexFormatRGBA32);
+}
+
+void TexturesD3D9::UploadTexture2D(
+ TextureID tid, TextureDimension dimension, UInt8* srcData, int width, int height,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, int masterTextureLimit, TextureUsageMode usageMode, TextureColorSpace colorSpace )
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ AssertIf( srcData == NULL );
+ AssertIf( (!IsPowerOfTwo(width) || !IsPowerOfTwo(height)) && !IsNPOTTextureAllowed(mipCount > 1) );
+
+ if( dimension != kTexDim2D )
+ {
+ ErrorString( "Incorrect texture dimension!" );
+ return;
+ }
+
+ // Nothing to do here. Early out instead of failing, empty textures are serialized by dynamic fonts.
+ if( width == 0 || height == 0 )
+ return;
+
+ bool uploadIsCompressed, decompressOnTheFly;
+ HandleFormatDecompression (format, &usageMode, colorSpace, &uploadIsCompressed, &decompressOnTheFly);
+
+ if( decompressOnTheFly )
+ uploadIsCompressed = false;
+
+ const FormatDesc& uploadFormat = GetUploadFormat (decompressOnTheFly ? kTexFormatRGBA32 : format, usageMode != kTexUsageNone);
+ D3DFORMAT d3dFormat = uploadFormat.d3dformat;
+
+ if( format == kTexFormatAlphaLum16 && !gGraphicsCaps.d3d.hasTextureFormatL16 )
+ {
+ // AlphaLum16 requires some trickery if hardware does not support L16:
+ // first we try to do L8 instead, then fallback to A8R8G8B8.
+ if( gGraphicsCaps.d3d.hasTextureFormatL8 )
+ d3dFormat = D3DFMT_L8;
+ else
+ d3dFormat = D3DFMT_A8R8G8B8;
+ }
+
+ int baseLevel, maxLevel, texWidth, texHeight;
+ size_t textureSize;
+ prcore::Surface::BlitMode blitMode = prcore::Surface::BLIT_COPY;
+ if (SkipLevelsForMasterTextureLimit (masterTextureLimit, format, uploadFormat.unityformat, mipCount, uploadIsCompressed, &srcData, &width, &height, &baseLevel, &maxLevel, &texWidth, &texHeight, &textureSize))
+ blitMode = prcore::Surface::BLIT_SCALE;
+
+ // if we don't support mip maps - don't use them
+ if( !(gGraphicsCaps.d3d.d3dcaps.TextureCaps & D3DPTEXTURECAPS_MIPMAP) )
+ {
+ mipCount = 1;
+ baseLevel = 0;
+ }
+
+ // create texture if it does not exist already
+ IDirect3DTexture9* texture = NULL;
+
+ D3DTexture* target = QueryD3DTexture(tid);
+ if(!target)
+ {
+ HRESULT hr = dev->CreateTexture( texWidth, texHeight, mipCount - baseLevel, 0, d3dFormat, D3DPOOL_MANAGED, &texture, NULL );
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(texture, CalculateImageSize(texWidth, texHeight,format)*(mipCount>1?1.33:1),tid.m_ID);
+ if( FAILED(hr) )
+ printf_console( "d3d: failed to create 2D texture id=%i w=%i h=%i mips=%i d3dfmt=%i [%s]\n", tid, texWidth, texHeight, mipCount-baseLevel, d3dFormat, GetD3D9Error(hr) );
+ TextureIdMap::UpdateTexture(tid, AllocD3DTexture(texture));
+ }
+ else
+ {
+ texture = (IDirect3DTexture9*)target->texture;
+ }
+
+ if( !texture )
+ {
+ AssertString( "failed to create 2D texture" );
+ return;
+ }
+
+ UInt8* decompressBuffer = NULL;
+ UInt8* tempBuffer = NULL;
+ int bufferPitch;
+
+ // Upload the mip levels
+ for( int level = baseLevel; level <= maxLevel; ++level )
+ {
+ D3DLOCKED_RECT lr;
+ HRESULT hr = texture->LockRect( level-baseLevel, &lr, NULL, 0 );
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d: failed to lock level %i of texture %i [%s]\n", level-baseLevel, tid, GetD3D9Error(hr) );
+ if( decompressBuffer )
+ delete[] decompressBuffer;
+ return;
+ }
+
+ if( decompressOnTheFly )
+ {
+ ConvertCompressedTextureUpload (width, height, format, srcData, decompressBuffer, bufferPitch, usageMode, colorSpace, level);
+
+ prcore::Surface srcSurface( width, height, bufferPitch, GetProphecyPixelFormat(kTexFormatRGBA32), decompressBuffer );
+ prcore::Surface dstSurface( texWidth, texHeight, lr.Pitch, uploadFormat.prformat, lr.pBits );
+ dstSurface.BlitImage( srcSurface, blitMode );
+ }
+ else if( format == kTexFormatAlphaLum16 )
+ {
+ BlitAlphaLum16( width, height, d3dFormat, srcData, (UInt8*)lr.pBits, lr.Pitch );
+ }
+ else if( !uploadIsCompressed )
+ {
+ prcore::Surface srcSurface( width, height, GetRowBytesFromWidthAndFormat( width,format ), GetProphecyPixelFormat(format), srcData );
+ prcore::Surface dstSurface( texWidth, texHeight, lr.Pitch, uploadFormat.prformat, lr.pBits );
+
+ if (!ConvertUncompressedTextureUpload(srcSurface, dstSurface, blitMode, uploadFormat.unityformat, usageMode, colorSpace, width, height, (UInt8*)lr.pBits, lr.Pitch, uploadFormat.prformat, tempBuffer, bufferPitch))
+ {
+ dstSurface.BlitImage( srcSurface, blitMode );
+ }
+ }
+ else
+ {
+ if( width == texWidth && height == texHeight )
+ {
+ BlitCopyCompressedImage( format, srcData, width, height, (UInt8*)lr.pBits, width, height, false );
+ }
+ else
+ {
+ // TODO: fill with garbage?
+ }
+ }
+
+ texture->UnlockRect( level-baseLevel );
+
+ // Go to next level
+ AssertIf( width == 1 && height == 1 && level != maxLevel );
+ AdvanceToNextMipLevel (format, srcData, width, height, texWidth, texHeight);
+ }
+
+ delete[] decompressBuffer;
+}
+
+void TexturesD3D9::UploadTextureSubData2D(
+ TextureID tid, UInt8* srcData, int mipLevel,
+ int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace )
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ if( !dev )
+ return;
+
+ // if we don't support mip maps and want to change higher level - don't
+ if( !(gGraphicsCaps.d3d.d3dcaps.TextureCaps & D3DPTEXTURECAPS_MIPMAP) && mipLevel != 0 )
+ return;
+
+ AssertIf( srcData == NULL );
+ AssertIf( IsCompressedDXTTextureFormat( format ) );
+
+ // find the texture
+ D3DTexture* target = QueryD3DTexture(tid);
+ if(target == 0)
+ {
+ AssertString( "Texture not found" );
+ return;
+ }
+
+ const FormatDesc& uploadFormat = GetUploadFormat( format );
+ IDirect3DTexture9* texture = (IDirect3DTexture9*)target->texture;
+ AssertIf( !texture );
+
+ RECT rect;
+ rect.left = x;
+ rect.top = y;
+ rect.right = x + width;
+ rect.bottom = y + height;
+ D3DLOCKED_RECT lr;
+ HRESULT hr = texture->LockRect( mipLevel, &lr, &rect, 0 );
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d: failed to lock sub level %i of texture %i [%s]\n", mipLevel, tid, GetD3D9Error(hr) );
+ return;
+ }
+
+ // TODO: handle other format conversions
+
+ prcore::Surface srcSurface( width, height, GetRowBytesFromWidthAndFormat(width,format), GetProphecyPixelFormat(format), srcData );
+ prcore::Surface dstSurface( width, height, lr.Pitch, uploadFormat.prformat, lr.pBits );
+ dstSurface.BlitImage( srcSurface, prcore::Surface::BLIT_COPY );
+
+ texture->UnlockRect( mipLevel );
+}
+
+
+void TexturesD3D9::UploadTextureCube(
+ TextureID tid, UInt8* srcData, int faceDataSize, int size,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace )
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ if (!dev)
+ return;
+
+ // if we don't support cube mip maps - don't use them
+ if( !(gGraphicsCaps.d3d.d3dcaps.TextureCaps & D3DPTEXTURECAPS_MIPCUBEMAP) )
+ mipCount = 1;
+
+ const FormatDesc& uploadFormat = GetUploadFormat(format);
+ IDirect3DCubeTexture9* texture = NULL;
+
+ D3DTexture* target = QueryD3DTexture(tid);
+ if(!target)
+ {
+ HRESULT hr = dev->CreateCubeTexture( size, mipCount, 0, uploadFormat.d3dformat, D3DPOOL_MANAGED, &texture, NULL );
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(texture, 6*CalculateImageSize(size, size, format)*(mipCount>1?1.33:1),tid.m_ID);
+ if( FAILED(hr) )
+ printf_console( "d3d: failed to create cubemap id=%i size=%i mips=%i d3dfmt=%i [%s]\n", tid, size, mipCount, uploadFormat.d3dformat, GetD3D9Error(hr) );
+ TextureIdMap::UpdateTexture(tid, AllocD3DTexture(texture));
+ }
+ else
+ {
+ texture = (IDirect3DCubeTexture9*)target->texture;
+ }
+ if( !texture )
+ {
+ AssertString( "failed to create cubemap" );
+ return;
+ }
+
+ // Upload data
+ bool uploadIsCompressed = IsCompressedDXTTextureFormat(format); // TODO: handle when we don't have DXT
+
+ static const D3DCUBEMAP_FACES faces[6] =
+ {
+ D3DCUBEMAP_FACE_POSITIVE_X,
+ D3DCUBEMAP_FACE_NEGATIVE_X,
+ D3DCUBEMAP_FACE_POSITIVE_Y,
+ D3DCUBEMAP_FACE_NEGATIVE_Y,
+ D3DCUBEMAP_FACE_POSITIVE_Z,
+ D3DCUBEMAP_FACE_NEGATIVE_Z,
+ };
+
+ int maxLevel = mipCount - 1;
+ for (int face=0;face<6;face++)
+ {
+ int mipSize = size;
+ UInt8* data = srcData + face * faceDataSize;
+
+ // Upload the mip levels
+ for( int level = 0; level <= maxLevel; ++level )
+ {
+ D3DLOCKED_RECT lr;
+ HRESULT hr = texture->LockRect( faces[face], level, &lr, NULL, 0 );
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d: failed to lock level %i of face %i of cubemap %i [%s]\n", level, face, tid, GetD3D9Error(hr) );
+ return;
+ }
+
+ // TODO: handle DXT decompression on the fly
+ // TODO: handle other format conversions
+
+ if( !uploadIsCompressed )
+ {
+ prcore::Surface srcSurface( mipSize, mipSize, GetRowBytesFromWidthAndFormat(mipSize,format), GetProphecyPixelFormat(format), data );
+ prcore::Surface dstSurface( mipSize, mipSize, lr.Pitch, uploadFormat.prformat, lr.pBits );
+ dstSurface.BlitImage( srcSurface, prcore::Surface::BLIT_COPY );
+ }
+ else
+ {
+ BlitCopyCompressedImage( format, data, mipSize, mipSize, (UInt8*)lr.pBits, mipSize /* TODO */, mipSize, false );
+ }
+
+ texture->UnlockRect( faces[face], level );
+
+ // Go to next level
+ data += CalculateImageSize( mipSize, mipSize, format );
+ AssertIf( mipSize == 1 && level != maxLevel );
+
+ mipSize = std::max( mipSize / 2, 1 );
+ }
+ }
+}
+
+void TexturesD3D9::UploadTexture3D(
+ TextureID tid, UInt8* srcData, int width, int height, int depth,
+ TextureFormat format, int mipCount, UInt32 uploadFlags )
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+ if (!dev || !gGraphicsCaps.has3DTexture)
+ return;
+
+ // if we don't support volume mip maps - don't use them
+ if( !(gGraphicsCaps.d3d.d3dcaps.TextureCaps & D3DPTEXTURECAPS_VOLUMEMAP) )
+ mipCount = 1;
+
+
+ const FormatDesc& uploadFormat = GetUploadFormat( format );
+ D3DFORMAT d3dFormat = uploadFormat.d3dformat;
+ if( format == kTexFormatAlphaLum16 )
+ {
+ // AlphaLum16 requires some trickery if hardware does not support L16:
+ // first we try to do L8 instead, then fallback to A8R8G8B8.
+ if( !gGraphicsCaps.d3d.hasTextureFormatL16 && gGraphicsCaps.d3d.hasTextureFormatL8 )
+ d3dFormat = D3DFMT_L8;
+ else
+ d3dFormat = D3DFMT_A8R8G8B8;
+ }
+
+ IDirect3DVolumeTexture9* texture = NULL;
+
+ D3DTexture* target = QueryD3DTexture(tid);
+ if(!target)
+ {
+ HRESULT hr = dev->CreateVolumeTexture( width, height, depth, mipCount, 0, d3dFormat, D3DPOOL_MANAGED, &texture, NULL );
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(texture, depth*CalculateImageSize(width, height, format)*(mipCount>1?1.33:1),tid.m_ID);
+ if( FAILED(hr) )
+ printf_console( "d3d: failed to create 3D texture id=%i w=%i h=%i d=%i mips=%i d3dfmt=%i [%s]\n", tid, width, height, depth, mipCount, d3dFormat, GetD3D9Error(hr) );
+ TextureIdMap::UpdateTexture(tid, AllocD3DTexture(texture));
+ }
+ else
+ {
+ texture = (IDirect3DVolumeTexture9*)target->texture;
+ }
+ if( !texture )
+ {
+ AssertString( "failed to create 3D texture" );
+ return;
+ }
+
+ int maxLevel = mipCount - 1;
+ for( int level=0; level <= maxLevel; ++level )
+ {
+ D3DLOCKED_BOX lr;
+ HRESULT hr = texture->LockBox( level, &lr, NULL, 0 );
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d: failed to lock level %i of 3D texture %i [%s]\n", level, tid, GetD3D9Error(hr) );
+ return;
+ }
+
+ UInt8* destData = (UInt8*)lr.pBits;
+ const int sliceSize = CalculateImageSize(width, height, format);
+ for( int slice = 0; slice < depth; ++slice )
+ {
+ if( format == kTexFormatAlphaLum16 )
+ {
+ BlitAlphaLum16 (width, height, d3dFormat, srcData, destData, lr.RowPitch);
+ }
+ else
+ {
+ // Regular ProphecySDK blit
+ prcore::Surface srcSurface( width, height, GetRowBytesFromWidthAndFormat(width,format), GetProphecyPixelFormat(format), srcData );
+ prcore::Surface dstSurface( width, height, lr.RowPitch, uploadFormat.prformat, destData );
+ dstSurface.BlitImage( srcSurface, prcore::Surface::BLIT_COPY );
+ }
+ srcData += sliceSize;
+ destData += lr.SlicePitch;
+ }
+
+ texture->UnlockBox( level );
+
+ AssertIf( width == 1 && height == 1 && level != maxLevel );
+
+ width = std::max( width / 2, 1 );
+ height = std::max( height / 2, 1 );
+ depth = std::max( depth / 2, 1 );
+ }
+}
+
+
+
+bool TexturesD3D9::SetTexture (ShaderType shaderType, int unit, TextureID textureID)
+{
+ IDirect3DDevice9* dev = GetD3DDevice();
+
+ D3DTexture* target = QueryD3DTexture(textureID);
+ if(target)
+ {
+ const D3DTexture& texture = *target;
+ DWORD d3dUnit = GetD3D9SamplerIndex (shaderType, unit);
+ D3D9_CALL(dev->SetTexture( d3dUnit, texture.texture ));
+ // TODO: caching of those!
+ D3D9_CALL(dev->SetSamplerState( d3dUnit, D3DSAMP_ADDRESSU, texture.wrapMode ));
+ D3D9_CALL(dev->SetSamplerState( d3dUnit, D3DSAMP_ADDRESSV, texture.wrapMode ));
+ D3D9_CALL(dev->SetSamplerState( d3dUnit, D3DSAMP_ADDRESSW, texture.wrapMode ));
+ D3D9_CALL(dev->SetSamplerState( d3dUnit, D3DSAMP_MINFILTER, texture.minFilter ));
+ D3D9_CALL(dev->SetSamplerState( d3dUnit, D3DSAMP_MAGFILTER, texture.magFilter ));
+ D3D9_CALL(dev->SetSamplerState( d3dUnit, D3DSAMP_MIPFILTER, texture.mipFilter ));
+ D3D9_CALL(dev->SetSamplerState( d3dUnit, D3DSAMP_MAXANISOTROPY, texture.aniso ));
+ D3D9_CALL(dev->SetSamplerState( d3dUnit, D3DSAMP_SRGBTEXTURE, texture.sRGB ));
+ return true;
+ }
+ else
+ {
+ // Ok, just don't complain here. Mostly with render textures, once in a while it
+ // happens that RT is not created yet, and someone tries to render with it.
+ // Just silently ignore that case.
+ //ErrorString( Format("SetTexture with unknown texture %i", textureID) );
+ return false;
+ }
+}
+
+static D3DTEXTUREADDRESS s_D3DWrapModes[kTexWrapCount] = {
+ D3DTADDRESS_WRAP,
+ D3DTADDRESS_CLAMP,
+};
+static D3DTEXTUREFILTERTYPE s_D3DMinMagFilters[kTexFilterCount] = {
+ D3DTEXF_POINT,
+ D3DTEXF_LINEAR,
+ D3DTEXF_LINEAR,
+};
+static D3DTEXTUREFILTERTYPE s_D3DMipFilters[kTexFilterCount] = {
+ D3DTEXF_POINT,
+ D3DTEXF_POINT,
+ D3DTEXF_LINEAR,
+};
+
+
+void TexturesD3D9::SetTextureParams( TextureID textureID, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace )
+{
+ D3DTexture* target = QueryD3DTexture(textureID);
+ if(!target)
+ return;
+
+ D3DTexture& texture = *target;
+ AssertIf( !texture.texture );
+
+ if( gGraphicsCaps.hasAnisoFilter && texDim != kTexDim3D )
+ texture.aniso = std::min( anisoLevel, gGraphicsCaps.maxAnisoLevel );
+ else
+ texture.aniso = 1;
+ texture.wrapMode = s_D3DWrapModes[wrap];
+
+ if( !hasMipMap && filter == kTexFilterTrilinear )
+ filter = kTexFilterBilinear;
+
+ texture.minFilter = texture.magFilter = s_D3DMinMagFilters[filter];
+ if( texture.aniso > 1 )
+ {
+ texture.minFilter = D3DTEXF_ANISOTROPIC;
+ // some cards (notably GeForces) can do min anisotropic filter, but not mag anisotropic filter
+ if( gGraphicsCaps.d3d.d3dcaps.TextureFilterCaps & D3DPTFILTERCAPS_MAGFANISOTROPIC )
+ texture.magFilter = D3DTEXF_ANISOTROPIC;
+ }
+ texture.mipFilter = s_D3DMipFilters[filter];
+
+ //sRGB
+ texture.sRGB = colorSpace == kTexColorSpaceSRGB || colorSpace == kTexColorSpaceSRGBXenon;
+ // actual setting of sampler states will happen in SetTexture
+}
+
+
+void TexturesD3D9::DeleteTexture( TextureID textureID )
+{
+ D3DTexture* target = QueryD3DTexture(textureID);
+ if(!target)
+ return;
+
+ // texture can be null if texture creation failed. At least don't make it crash here
+ if( target->texture )
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(target->texture);
+ ULONG refCount = target->texture->Release();
+ AssertIf( refCount != 0 );
+ }
+ TextureIdMap::RemoveTexture(textureID);
+}
diff --git a/Runtime/GfxDevice/d3d/TexturesD3D9.h b/Runtime/GfxDevice/d3d/TexturesD3D9.h
new file mode 100644
index 0000000..113434c
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/TexturesD3D9.h
@@ -0,0 +1,90 @@
+#pragma once
+
+#include "D3D9Includes.h"
+#include "Runtime/Graphics/TextureFormat.h"
+#include "Runtime/Graphics/RenderSurface.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/Threads/AtomicOps.h"
+#include <map>
+
+class ImageReference;
+
+class TexturesD3D9
+{
+public:
+ TexturesD3D9() {}
+ ~TexturesD3D9() {}
+ bool SetTexture (ShaderType shaderType, int unit, TextureID textureID);
+ void SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace );
+
+ void DeleteTexture( TextureID textureID );
+
+ void UploadTexture2D(
+ TextureID tid, TextureDimension dimension, UInt8* srcData, int width, int height,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, int masterTextureLimit, TextureUsageMode usageMode, TextureColorSpace colorSpace );
+
+ void UploadTextureSubData2D(
+ TextureID tid, UInt8* srcData, int mipLevel,
+ int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace );
+
+ void UploadTextureCube(
+ TextureID tid, UInt8* srcData, int faceDataSize, int size,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace );
+
+ void UploadTexture3D(
+ TextureID tid, UInt8* srcData, int width, int height, int depth,
+ TextureFormat format, int mipCount, UInt32 uploadFlags );
+
+ void AddTexture( TextureID textureID, IDirect3DBaseTexture9* texture );
+ void RemoveTexture( TextureID textureID );
+ IDirect3DBaseTexture9* GetTexture( TextureID textureID ) const;
+
+ intptr_t RegisterNativeTexture(IDirect3DBaseTexture9* texture) const;
+ void UpdateNativeTexture(TextureID textureID, IDirect3DBaseTexture9* texture);
+};
+
+struct RenderSurfaceD3D9 : RenderSurfaceBase
+{
+ RenderSurfaceD3D9()
+ : m_Texture(NULL)
+ , m_Surface(NULL)
+ {
+ RenderSurfaceBase_Init(*this);
+ }
+ void Release() {
+ if (m_Texture) {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_Texture);
+ m_Texture->Release();
+ m_Texture = NULL;
+ }
+ if (m_Surface) {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_Surface);
+ m_Surface->Release();
+ m_Surface = NULL;
+ }
+ }
+ IDirect3DBaseTexture9* m_Texture;
+ IDirect3DSurface9* m_Surface;
+};
+
+struct RenderColorSurfaceD3D9 : public RenderSurfaceD3D9
+{
+ RenderColorSurfaceD3D9()
+ : format(kRTFormatARGB32)
+ , dim(kTexDim2D)
+ {
+ RenderSurfaceBase_InitColor(*this);
+ }
+ RenderTextureFormat format;
+ TextureDimension dim;
+};
+
+struct RenderDepthSurfaceD3D9 : public RenderSurfaceD3D9
+{
+ RenderDepthSurfaceD3D9()
+ : depthFormat(kDepthFormatNone)
+ {
+ RenderSurfaceBase_InitDepth(*this);
+ }
+ DepthBufferFormat depthFormat;
+};
diff --git a/Runtime/GfxDevice/d3d/TimerQueryD3D9.cpp b/Runtime/GfxDevice/d3d/TimerQueryD3D9.cpp
new file mode 100644
index 0000000..70f9ed7
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/TimerQueryD3D9.cpp
@@ -0,0 +1,196 @@
+#include "UnityPrefix.h"
+#if ENABLE_PROFILER
+#include "GfxDeviceD3D9.h"
+#include "TimerQueryD3D9.h"
+
+
+TimerQueryD3D9::TimerQueryD3D9()
+ : m_Query(NULL), m_Time(0), m_Active(false)
+{
+ GetD3DDevice()->CreateQuery(D3DQUERYTYPE_TIMESTAMP, &m_Query);
+ m_TimeMultiplier = 0.0f;
+}
+
+TimerQueryD3D9::~TimerQueryD3D9()
+{
+ SAFE_RELEASE(m_Query);
+}
+
+void TimerQueryD3D9::Measure()
+{
+ // Flush previous result
+ GetElapsed(kWaitRenderThread);
+
+ TimerQueriesD3D9& queries = GetD3D9GfxDevice().GetTimerQueries();
+ if (m_Query && queries.HasFrequencyQuery())
+ {
+ queries.AddActiveTimerQuery(this);
+ m_Query->Issue(D3DISSUE_END);
+ m_Active = true;
+ m_Time = kInvalidProfileTime;
+ }
+ else
+ m_Time = 0;
+ m_TimeMultiplier = 0.0f;
+}
+
+ProfileTimeFormat TimerQueryD3D9::GetElapsed(UInt32 flags)
+{
+ while (m_Active)
+ {
+ bool wait = (flags & kWaitRenderThread) != 0;
+ if (!GetD3D9GfxDevice().GetTimerQueries().PollNextTimerQuery(wait))
+ break;
+ }
+ return m_Time;
+}
+
+bool TimerQueryD3D9::PollResult(UInt64& prevTime, bool wait)
+{
+ for (;;)
+ {
+ UINT64 time;
+ DWORD flags = wait ? D3DGETDATA_FLUSH : 0;
+ HRESULT hr = m_Query->GetData(&time, sizeof(time), flags);
+ if (hr == S_OK)
+ {
+ UInt64 elapsed = prevTime ? (time - prevTime) : 0;
+ m_Time = ProfileTimeFormat(elapsed * m_TimeMultiplier);
+ prevTime = time;
+ return true;
+ }
+ // Stop polling on unknown result (e.g D3DERR_DEVICELOST)
+ if (hr != S_FALSE)
+ {
+ m_Time = 0;
+ prevTime = 0;
+ return true;
+ }
+ if (!wait)
+ break;
+ }
+ return false;
+}
+
+TimerQueriesD3D9::TimerQueriesD3D9()
+{
+ m_LastQueryTime = 0;
+ m_FrequencyQuery = NULL;
+ memset(m_StartTimeQueries, 0, sizeof(m_StartTimeQueries));
+ m_StartTimeQueryIndex = 0;
+}
+
+void TimerQueriesD3D9::ReleaseAllQueries()
+{
+ SAFE_RELEASE(m_FrequencyQuery);
+ for (int i = 0; i < kStartTimeQueryCount; i++)
+ {
+ delete m_StartTimeQueries[i];
+ m_StartTimeQueries[i] = NULL;
+ }
+ m_InactiveTimerQueries.append(m_ActiveTimerQueries);
+ m_InactiveTimerQueries.append(m_PolledTimerQueries);
+ TimerQueryList& queries = m_InactiveTimerQueries;
+ for (TimerQueryList::iterator it = queries.begin(); it != queries.end(); ++it)
+ {
+ TimerQueryD3D9& query = *it;
+ query.m_Active = false;
+ query.m_Time = 0;
+ SAFE_RELEASE(query.m_Query);
+ }
+}
+
+void TimerQueriesD3D9::RecreateAllQueries()
+{
+ Assert(m_ActiveTimerQueries.empty());
+ Assert(m_PolledTimerQueries.empty());
+ TimerQueryList& queries = m_InactiveTimerQueries;
+ for (TimerQueryList::iterator it = queries.begin(); it != queries.end(); ++it)
+ {
+ TimerQueryD3D9& query = *it;
+ GetD3DDevice()->CreateQuery(D3DQUERYTYPE_TIMESTAMP, &query.m_Query);
+ }
+}
+
+void TimerQueriesD3D9::BeginTimerQueries()
+{
+ // Poll queries from previous frames
+ PollTimerQueries();
+
+ if (m_FrequencyQuery == NULL)
+ {
+ GetD3DDevice()->CreateQuery(D3DQUERYTYPE_TIMESTAMPFREQ, &m_FrequencyQuery);
+ }
+ if (m_FrequencyQuery)
+ m_FrequencyQuery->Issue(D3DISSUE_END);
+
+ int& index = m_StartTimeQueryIndex;
+ if (m_StartTimeQueries[index] == NULL)
+ {
+ m_StartTimeQueries[index] = new TimerQueryD3D9;
+ }
+ m_StartTimeQueries[index]->Measure();
+ index = (index + 1) % kStartTimeQueryCount;
+}
+
+void TimerQueriesD3D9::EndTimerQueries()
+{
+ if(m_FrequencyQuery == NULL)
+ return;
+
+ HRESULT hr;
+ UINT64 freq;
+ do
+ {
+ hr = m_FrequencyQuery->GetData(&freq, sizeof(freq), D3DGETDATA_FLUSH);
+ } while (hr == S_FALSE);
+ if (hr == S_OK)
+ {
+ float timeMult = float(1000000000.0 / (double)freq);
+ TimerQueryList::iterator query, queryEnd = m_ActiveTimerQueries.end();
+ for (query = m_ActiveTimerQueries.begin(); query != queryEnd; ++query)
+ query->SetTimeMultiplier(timeMult);
+ }
+ // Move queries from active to polled list
+ m_PolledTimerQueries.append(m_ActiveTimerQueries);
+}
+
+TimerQueryD3D9* TimerQueriesD3D9::CreateTimerQuery()
+{
+ TimerQueryD3D9* query = new TimerQueryD3D9;
+ m_InactiveTimerQueries.push_back(*query);
+ return query;
+}
+
+void TimerQueriesD3D9::AddActiveTimerQuery(TimerQueryD3D9* query)
+{
+ query->RemoveFromList();
+ m_ActiveTimerQueries.push_back(*query);
+}
+
+void TimerQueriesD3D9::PollTimerQueries()
+{
+ for (;;)
+ {
+ if (!PollNextTimerQuery(false))
+ break;
+ }
+}
+
+bool TimerQueriesD3D9::PollNextTimerQuery(bool wait)
+{
+ if (m_PolledTimerQueries.empty())
+ return false;
+
+ TimerQueryD3D9& query = m_PolledTimerQueries.front();
+ if (query.PollResult(m_LastQueryTime, wait))
+ {
+ query.m_Active = false;
+ query.RemoveFromList();
+ m_InactiveTimerQueries.push_back(query);
+ return true;
+ }
+ return false;
+}
+
+#endif
diff --git a/Runtime/GfxDevice/d3d/TimerQueryD3D9.h b/Runtime/GfxDevice/d3d/TimerQueryD3D9.h
new file mode 100644
index 0000000..ecc4a94
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/TimerQueryD3D9.h
@@ -0,0 +1,67 @@
+#ifndef TIMERQUERYD3D9_H
+#define TIMERQUERYD3D9_H
+
+#if ENABLE_PROFILER
+
+#include "Runtime/GfxDevice/GfxTimerQuery.h"
+
+class TimerQueriesD3D9;
+
+class TimerQueryD3D9 : public GfxTimerQuery
+{
+public:
+ ~TimerQueryD3D9();
+
+ virtual void Measure();
+ virtual ProfileTimeFormat GetElapsed(UInt32 flags);
+
+ bool PollResult(UInt64& prevTime, bool wait);
+ void SetTimeMultiplier(float tm) { m_TimeMultiplier = tm; }
+
+private:
+ friend TimerQueriesD3D9;
+ TimerQueryD3D9();
+
+ IDirect3DQuery9* m_Query;
+ ProfileTimeFormat m_Time;
+ float m_TimeMultiplier;
+ bool m_Active;
+};
+
+class TimerQueriesD3D9
+{
+public:
+ TimerQueriesD3D9();
+
+ void ReleaseAllQueries();
+ void RecreateAllQueries();
+
+ void BeginTimerQueries();
+ void EndTimerQueries();
+
+ TimerQueryD3D9* CreateTimerQuery();
+
+ void AddActiveTimerQuery(TimerQueryD3D9* query);
+ void PollTimerQueries();
+ bool PollNextTimerQuery(bool wait);
+
+ bool HasFrequencyQuery() const { return m_FrequencyQuery != NULL; }
+
+private:
+ enum
+ {
+ kStartTimeQueryCount = 3
+ };
+
+ UInt64 m_LastQueryTime;
+ IDirect3DQuery9* m_FrequencyQuery;
+ TimerQueryD3D9* m_StartTimeQueries[kStartTimeQueryCount];
+ int m_StartTimeQueryIndex;
+ typedef List<TimerQueryD3D9> TimerQueryList;
+ TimerQueryList m_InactiveTimerQueries;
+ TimerQueryList m_ActiveTimerQueries;
+ TimerQueryList m_PolledTimerQueries;
+};
+
+#endif
+#endif
diff --git a/Runtime/GfxDevice/d3d/VertexDeclarations.cpp b/Runtime/GfxDevice/d3d/VertexDeclarations.cpp
new file mode 100644
index 0000000..180a105
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/VertexDeclarations.cpp
@@ -0,0 +1,124 @@
+#include "UnityPrefix.h"
+#include "VertexDeclarations.h"
+#include "D3D9Context.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+bool VertexDeclarations::KeyType::operator < (const KeyType& rhs) const
+{
+ return memcmp(channels, rhs.channels, sizeof(channels)) < 0;
+}
+
+VertexDeclarations::VertexDeclarations()
+{
+}
+
+VertexDeclarations::~VertexDeclarations()
+{
+ Clear();
+}
+
+struct D3DVertexSemantics
+{
+ UInt8 usage;
+ UInt8 index;
+};
+
+static D3DVertexSemantics kChannelVertexSemantics[kShaderChannelCount] =
+{
+ { D3DDECLUSAGE_POSITION, 0 }, // position
+ { D3DDECLUSAGE_NORMAL, 0 }, // normal
+ { D3DDECLUSAGE_COLOR, 0 }, // color
+ { D3DDECLUSAGE_TEXCOORD, 0 }, // uv
+ { D3DDECLUSAGE_TEXCOORD, 1 }, // uv2
+ { D3DDECLUSAGE_TANGENT, 0 }, // tangent
+};
+
+static FORCE_INLINE D3DDECLTYPE GetD3DVertexDeclType(const ChannelInfo& info)
+{
+ switch (info.format)
+ {
+ case kChannelFormatFloat:
+ {
+ switch (info.dimension)
+ {
+ case 1: return D3DDECLTYPE_FLOAT1;
+ case 2: return D3DDECLTYPE_FLOAT2;
+ case 3: return D3DDECLTYPE_FLOAT3;
+ case 4: return D3DDECLTYPE_FLOAT4;
+ }
+ break;
+ }
+ case kChannelFormatFloat16:
+ {
+ switch (info.dimension)
+ {
+ case 2: return D3DDECLTYPE_FLOAT16_2;
+ case 4: return D3DDECLTYPE_FLOAT16_4;
+ }
+ break;
+ }
+ case kChannelFormatColor:
+ {
+ return D3DDECLTYPE_D3DCOLOR;
+ }
+ }
+ Assert("No matching D3D vertex decl type!");
+ return D3DDECLTYPE_UNUSED;
+}
+
+IDirect3DVertexDeclaration9* VertexDeclarations::GetVertexDecl( const ChannelInfoArray channels )
+{
+ KeyType key;
+ memcpy(key.channels, channels, sizeof(key.channels));
+
+ // already have vertex declaration for these formats?
+ VertexDeclMap::iterator it = m_VertexDeclMap.find( key );
+ if( it != m_VertexDeclMap.end() )
+ return it->second;
+
+ // don't have this declaration yet - create one
+ // KD: not sure if elements need to be ordered by stream, playing it safe
+ D3DVERTEXELEMENT9 elements[kShaderChannelCount+1];
+ int elIndex = 0;
+ for( int stream = 0; stream < kMaxVertexStreams; stream++ )
+ {
+ for( int chan = 0; chan < kShaderChannelCount; chan++ )
+ {
+ if( channels[chan].stream == stream && channels[chan].IsValid() )
+ {
+ DebugAssert(elIndex < kShaderChannelCount);
+ D3DVERTEXELEMENT9& elem = elements[elIndex];
+ elem.Stream = stream;
+ elem.Offset = channels[chan].offset;
+ elem.Type = GetD3DVertexDeclType(channels[chan]);
+ elem.Method = D3DDECLMETHOD_DEFAULT;
+ elem.Usage = kChannelVertexSemantics[chan].usage;
+ elem.UsageIndex = kChannelVertexSemantics[chan].index;
+ ++elIndex;
+ }
+ }
+ }
+ D3DVERTEXELEMENT9 declEnd = D3DDECL_END();
+ elements[elIndex] = declEnd;
+
+ IDirect3DVertexDeclaration9* decl = NULL;
+ HRESULT hr = GetD3DDevice()->CreateVertexDeclaration( elements, &decl );
+ if( FAILED(hr) ) {
+ // TODO: error!
+ }
+ m_VertexDeclMap[key] = decl;
+ return decl;
+}
+
+void VertexDeclarations::Clear()
+{
+ VertexDeclMap::iterator it;
+ for( it = m_VertexDeclMap.begin(); it != m_VertexDeclMap.end(); ++it )
+ {
+ if( it->second ) {
+ ULONG refCount = it->second->Release();
+ AssertIf( refCount != 0 );
+ }
+ }
+ m_VertexDeclMap.clear();
+}
diff --git a/Runtime/GfxDevice/d3d/VertexDeclarations.h b/Runtime/GfxDevice/d3d/VertexDeclarations.h
new file mode 100644
index 0000000..f737f5a
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/VertexDeclarations.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "D3D9Includes.h"
+#include "Runtime\Filters\Mesh\VertexData.h"
+#include <map>
+
+
+class VertexDeclarations
+{
+public:
+ VertexDeclarations();
+ ~VertexDeclarations();
+
+ IDirect3DVertexDeclaration9* GetVertexDecl( const ChannelInfoArray channels );
+ void Clear();
+
+private:
+ struct KeyType
+ {
+ bool operator < (const KeyType& rhs) const;
+ ChannelInfoArray channels;
+ };
+
+ typedef UNITY_MAP(kMemVertexData, KeyType, IDirect3DVertexDeclaration9*) VertexDeclMap;
+ VertexDeclMap m_VertexDeclMap;
+};
diff --git a/Runtime/GfxDevice/d3d/VertexPipeD3D9.cpp b/Runtime/GfxDevice/d3d/VertexPipeD3D9.cpp
new file mode 100644
index 0000000..8a91f74
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/VertexPipeD3D9.cpp
@@ -0,0 +1,705 @@
+#include "UnityPrefix.h"
+#include "VertexPipeD3D9.h"
+#include "ShaderGenerator.h"
+#include "D3D9Utils.h"
+#include "Runtime/GfxDevice/BuiltinShaderParams.h"
+#include "External/DirectX/builds/dx9include/d3dx9.h"
+#include <map>
+
+
+
+#define PRINT_VERTEX_PIPE_STATS 0
+
+#define PRINT_AMD_SHADER_ANALYZER_OUTPUT 0
+
+
+
+
+// GpuProgramsD3D.cpp
+ID3DXBuffer* AssembleD3DShader( const std::string& source );
+
+
+#if PRINT_AMD_SHADER_ANALYZER_OUTPUT
+void PrintAMDShaderAnalyzer( const std::string& source )
+{
+ const char* kPath = "C:\\Program Files\\AMD\\GPU ShaderAnalyzer 1.45\\GPUShaderAnalyzer.exe";
+ const char* kInputPath = "ShaderInput.txt";
+ const char* kOutputPath = "ShaderOutput.txt";
+ DeleteFileA(kInputPath);
+ DeleteFileA(kOutputPath);
+ FILE* fout = fopen(kInputPath, "wt");
+ fwrite(source.c_str(), source.size(), 1, fout);
+ fclose(fout);
+
+ std::string commandLine = std::string(kPath) + " " + kInputPath + " -Analyze " + kOutputPath + " -Module Latest -ASIC HD3870";
+
+ STARTUPINFOA si;
+ ZeroMemory( &si, sizeof(si) );
+ si.cb = sizeof(si);
+
+ PROCESS_INFORMATION pi;
+ ZeroMemory( &pi, sizeof(pi) );
+
+ if( CreateProcessA(
+ NULL, // name of executable module
+ (char*)commandLine.c_str(), // command line string
+ NULL, // process attributes
+ NULL, // thread attributes
+ FALSE, // handle inheritance option
+ 0, // creation flags
+ NULL, // new environment block
+ NULL, // current directory name
+ &si, // startup information
+ &pi ) ) // process information
+ {
+ WaitForSingleObject( pi.hProcess, INFINITE );
+ CloseHandle( pi.hProcess );
+ CloseHandle( pi.hThread );
+
+ FILE* fin = fopen(kOutputPath, "rt");
+ if( fin ) {
+ fseek(fin, 0, SEEK_END);
+ int length = ftell(fin);
+ fseek(fin, 0, SEEK_SET);
+ char* buffer = new char[length+1];
+ memset(buffer, 0,length+1);
+ fread(buffer, length, 1, fin);
+ fclose(fin);
+ }
+ }
+ //DeleteFileA(kInputPath);
+ //DeleteFileA(kOutputPath);
+}
+#endif
+
+
+
+static inline D3DCOLOR ColorToD3D( const float color[4] )
+{
+ return D3DCOLOR_RGBA( NormalizedToByte(color[0]), NormalizedToByte(color[1]), NormalizedToByte(color[2]), NormalizedToByte(color[3]) );
+}
+
+
+static void ResetDeviceVertexPipeStateD3D9 (IDirect3DDevice9* dev, const TransformState& state, const BuiltinShaderParamValues& builtins, const VertexPipeConfig& config, const VertexPipeDataD3D9& data)
+{
+ DebugAssertIf (!dev);
+
+ data.haveToResetDeviceState = false;
+
+ dev->SetTransform( D3DTS_WORLD, (const D3DMATRIX*)state.worldViewMatrix.GetPtr() );
+ Matrix4x4f dummyViewMatrix;
+ dummyViewMatrix.SetIdentity(); dummyViewMatrix.Get(2,2) = -1.0f;
+ dev->SetTransform( D3DTS_VIEW, (const D3DMATRIX*)dummyViewMatrix.GetPtr() );
+ dev->SetTransform( D3DTS_PROJECTION, (const D3DMATRIX*)builtins.GetMatrixParam(kShaderMatProj).GetPtr() );
+
+ dev->SetRenderState( D3DRS_COLORVERTEX, FALSE );
+
+ for( int i = 0; i < kMaxSupportedVertexLights; ++i )
+ dev->LightEnable( i, FALSE );
+
+ dev->SetRenderState( D3DRS_AMBIENT, 0 );
+ dev->SetRenderState( D3DRS_LIGHTING, FALSE );
+ dev->SetRenderState( D3DRS_SPECULARENABLE, FALSE );
+
+ for( int i = 0; i < kMaxSupportedTextureCoords; ++i ) {
+ dev->SetTextureStageState( i, D3DTSS_TEXCOORDINDEX, i );
+ dev->SetTextureStageState( i, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTSS_TCI_PASSTHRU );
+ }
+}
+
+void ResetVertexPipeStateD3D9 (IDirect3DDevice9* dev, TransformState& state, BuiltinShaderParamValues& builtins, VertexPipeConfig& config, VertexPipeDataD3D9& data, VertexPipePrevious& previous)
+{
+ config.Reset();
+ data.Reset();
+ state.Invalidate(builtins);
+ previous.Reset();
+
+ data.haveToResetDeviceState = true;
+ if (dev)
+ ResetDeviceVertexPipeStateD3D9 (dev, state, builtins, config, data);
+}
+
+
+void SetupFixedFunctionD3D9 (
+ IDirect3DDevice9* dev,
+ TransformState& state,
+ BuiltinShaderParamValues& builtins,
+ const VertexPipeConfig& config,
+ const VertexPipeDataD3D9& data,
+ VertexPipePrevious& previous,
+ bool vsActive, bool immediateMode)
+{
+ if (dev && data.haveToResetDeviceState)
+ ResetDeviceVertexPipeStateD3D9 (dev, state, builtins, config, data);
+
+ // matrices
+ if (!vsActive)
+ {
+ D3D9_CALL(dev->SetTransform( D3DTS_WORLD, (const D3DMATRIX*)state.worldViewMatrix.GetPtr() ));
+ }
+
+ // set color material first, then material, then color
+ if( config.colorMaterial != previous.config.colorMaterial )
+ {
+ if( config.colorMaterial != kColorMatDisabled )
+ {
+ D3DMATERIALCOLORSOURCE srcAmbient, srcDiffuse, srcEmission;
+ switch( config.colorMaterial )
+ {
+ case kColorMatEmission:
+ srcAmbient = D3DMCS_MATERIAL;
+ srcDiffuse = D3DMCS_MATERIAL;
+ srcEmission = D3DMCS_COLOR1;
+ break;
+ case kColorMatAmbientAndDiffuse:
+ srcAmbient = D3DMCS_COLOR1;
+ srcDiffuse = D3DMCS_COLOR1;
+ srcEmission = D3DMCS_MATERIAL;
+ break;
+ default:
+ return;
+ }
+ D3D9_CALL(dev->SetRenderState( D3DRS_AMBIENTMATERIALSOURCE, srcAmbient ));
+ D3D9_CALL(dev->SetRenderState( D3DRS_DIFFUSEMATERIALSOURCE, srcDiffuse ));
+ D3D9_CALL(dev->SetRenderState( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL ));
+ D3D9_CALL(dev->SetRenderState( D3DRS_EMISSIVEMATERIALSOURCE, srcEmission ));
+ D3D9_CALL(dev->SetRenderState( D3DRS_COLORVERTEX, TRUE ));
+ }
+ else
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_COLORVERTEX, FALSE ));
+ }
+ }
+
+ // material
+ if( !vsActive && config.hasLighting )
+ D3D9_CALL(dev->SetMaterial( &data.material ));
+
+ // lights
+ D3DLIGHT9 d3dlight;
+ d3dlight.Ambient.r = d3dlight.Ambient.g = d3dlight.Ambient.b = d3dlight.Ambient.a = 0.0f;
+ d3dlight.Falloff = 1.0f;
+ d3dlight.Attenuation0 = 1.0f;
+ d3dlight.Attenuation1 = 0.0f;
+
+ const UInt32 lightsEnabled = (1<<data.vertexLightCount)-1;
+ const UInt32 lightsPrevious = (1<<previous.vertexLightCount)-1;
+ const UInt32 lightsDifferent = lightsPrevious ^ lightsEnabled;
+ UInt32 lightMask = 1;
+ for (int i = 0; i < kMaxSupportedVertexLights; ++i, lightMask <<= 1)
+ {
+ const UInt32 lightDiff = lightsDifferent & lightMask;
+ if( lightsEnabled & lightMask )
+ {
+ const GfxVertexLight& l = data.lights[i];
+ static D3DLIGHTTYPE kD3DTypes[kLightTypeCount] = { D3DLIGHT_SPOT, D3DLIGHT_DIRECTIONAL, D3DLIGHT_POINT };
+ d3dlight.Type = kD3DTypes[l.type];
+ d3dlight.Diffuse = *(const D3DCOLORVALUE*)&l.color;
+ d3dlight.Specular = *(const D3DCOLORVALUE*)&l.color;
+ d3dlight.Position = *(const D3DVECTOR*)&l.position;
+ d3dlight.Direction = *(const D3DVECTOR*)&l.spotDirection;
+ d3dlight.Range = l.range;
+ d3dlight.Attenuation2 = l.quadAtten;
+ d3dlight.Theta = Deg2Rad(l.spotAngle) * 0.5f;
+ d3dlight.Phi = Deg2Rad(l.spotAngle);
+ D3D9_CALL(dev->SetLight (i,&d3dlight));
+ if (lightDiff)
+ D3D9_CALL(dev->LightEnable (i,TRUE));
+ }
+ else
+ {
+ if (lightDiff)
+ D3D9_CALL(dev->LightEnable (i, FALSE));
+ }
+ }
+ previous.vertexLightCount = data.vertexLightCount;
+
+
+ // ambient, lighting & specular
+ if( data.ambient != previous.ambient )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_AMBIENT, ColorToD3D(data.ambient.GetPtr()) ));
+ previous.ambient = data.ambient;
+ }
+ if( config.hasLighting != previous.config.hasLighting )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_LIGHTING, config.hasLighting ? TRUE : FALSE ));
+ }
+ if( config.hasSpecular != previous.config.hasSpecular )
+ {
+ D3D9_CALL(dev->SetRenderState( D3DRS_SPECULARENABLE, config.hasSpecular ? TRUE : FALSE ));
+ }
+ if (config.hasNormalization != previous.config.hasNormalization)
+ {
+ D3D9_CALL(dev->SetRenderState (D3DRS_NORMALIZENORMALS, config.hasNormalization ? TRUE : FALSE));
+ }
+
+
+ UInt32 textureMatrixModes = config.textureMatrixModes;
+ UInt32 projectedTextures = data.projectedTextures;
+ UInt32 textureSources = config.textureSources;
+ for( int i = 0; i < config.texCoordCount; ++i )
+ {
+ // texgen
+ UInt32 texSource = (textureSources >> (i*3)) & 0x7;
+ if( !vsActive )
+ {
+ static DWORD kTexSourceFlags[kTexSourceTypeCount] = { 0, 1, D3DTSS_TCI_SPHEREMAP, D3DTSS_TCI_CAMERASPACEPOSITION, D3DTSS_TCI_CAMERASPACEPOSITION, D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR, D3DTSS_TCI_CAMERASPACENORMAL };
+ DWORD d3dsource = kTexSourceFlags[texSource];
+ if( immediateMode && texSource <= kTexSourceUV1 )
+ d3dsource = i;
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_TEXCOORDINDEX, d3dsource ));
+ }
+ else
+ {
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_TEXCOORDINDEX, i ));
+ }
+
+ // matrix
+ unsigned matmode = (textureMatrixModes >> (i*2)) & 3;
+ static DWORD kTexFlags[kTexMatrixTypeCount] = { D3DTTFF_DISABLE, D3DTTFF_COUNT2, D3DTTFF_COUNT3, D3DTTFF_COUNT4 };
+ DWORD textureTransformFlags = kTexFlags[matmode];
+ if (projectedTextures & (1<<i))
+ textureTransformFlags |= D3DTTFF_PROJECTED;
+ if (vsActive)
+ textureTransformFlags = D3DTTFF_DISABLE;
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_TEXTURETRANSFORMFLAGS, textureTransformFlags ));
+
+ if( !vsActive )
+ {
+ if( texSource == kTexSourceObject )
+ {
+ // D3D has no "object space" texture generation.
+ // So instead we use camera space, and multiply the matrix so it matches:
+ // newMatrix = matrix * inverse(modelview) * mirrorZ
+ // Mirror along Z is required to match OpenGL's generation (eye space Z is negative).
+ Matrix4x4f mv = state.worldViewMatrix;
+ mv.Invert_Full();
+ // Negate Z axis (mv = mv * Scale(1,1,-1))
+ mv.Get(0,2) = -mv.Get(0,2);
+ mv.Get(1,2) = -mv.Get(1,2);
+ mv.Get(2,2) = -mv.Get(2,2);
+ mv.Get(3,2) = -mv.Get(3,2);
+ Matrix4x4f texmat;
+ MultiplyMatrices4x4 (&state.texMatrices[i], &mv, &texmat);
+ D3D9_CALL(dev->SetTransform( (D3DTRANSFORMSTATETYPE)(D3DTS_TEXTURE0 + i), (const D3DMATRIX*)texmat.GetPtr() ));
+ }
+ else
+ {
+ D3D9_CALL(dev->SetTransform( (D3DTRANSFORMSTATETYPE)(D3DTS_TEXTURE0 + i), (const D3DMATRIX*)state.texMatrices[i].GetPtr() ));
+ }
+ }
+ }
+ if( config.texCoordCount != previous.config.texCoordCount )
+ {
+ for( int i = config.texCoordCount; i < kMaxSupportedTextureCoords; ++i )
+ {
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_TEXCOORDINDEX, i ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE ));
+ }
+ }
+
+ if( !vsActive )
+ D3D9_CALL(dev->SetVertexShader(NULL));
+ previous.vertexShader = NULL;
+ previous.config = config;
+}
+
+
+
+
+
+// ----------------------------------------------------------------------
+
+
+
+
+struct VSLightData {
+ Vector4f pos;
+ Vector4f dir;
+ Vector4f color;
+ Vector4f params;
+};
+
+struct ShaderData {
+ IDirect3DVertexShader9* shader;
+ unsigned int usedConstants;
+ std::string text;
+ //std::string debug;
+};
+
+
+struct VertexPipeKeyCompare {
+ union {
+ VertexPipeConfig key;
+ UInt64 asint;
+ } u;
+ VertexPipeKeyCompare() { u.asint = 0; }
+ bool operator <( const VertexPipeKeyCompare& r ) const { return u.asint < r.u.asint; }
+};
+
+typedef std::map<VertexPipeKeyCompare, ShaderData> ShaderCache;
+static ShaderCache g_Shaders;
+
+
+static IDirect3DVertexShader9* GetShaderForConfig( const VertexPipeConfig& config, IDirect3DDevice9* dev, unsigned int& usedConstants )
+{
+ VertexPipeKeyCompare key;
+ key.u.key = config;
+ ShaderCache::iterator it = g_Shaders.find(key);
+ if( it != g_Shaders.end() ) {
+ const ShaderData& sdata = it->second;
+ usedConstants = sdata.usedConstants;
+ return sdata.shader;
+ }
+
+ ShaderGenerator gen;
+ gen.AddFragment( &kVS_Pos );
+
+ // lighting
+ if( config.hasLighting )
+ {
+ // normalize normals?
+ if (config.hasNormalization)
+ gen.AddFragment (&kVS_Normalize_Normal);
+
+ UInt32 hasLightType = config.hasLightType;
+ if( config.hasSpecular )
+ {
+ gen.AddFragment( &kVS_Light_Specular_Pre );
+ if( hasLightType & (1<<kLightDirectional) )
+ gen.AddFragment( &kVS_Light_Specular_Dir );
+ if( hasLightType & (1<<kLightPoint) )
+ gen.AddFragment( &kVS_Light_Specular_Point );
+ if( hasLightType & (1<<kLightSpot) )
+ gen.AddFragment( &kVS_Light_Specular_Spot );
+ }
+ else
+ {
+ gen.AddFragment( &kVS_Light_Diffuse_Pre );
+ if( hasLightType & (1<<kLightDirectional) )
+ gen.AddFragment( &kVS_Light_Diffuse_Dir );
+ if( hasLightType & (1<<kLightPoint) )
+ gen.AddFragment( &kVS_Light_Diffuse_Point );
+ if( hasLightType & (1<<kLightSpot) )
+ gen.AddFragment( &kVS_Light_Diffuse_Spot );
+ }
+
+ const ShaderFragment* frag = NULL;
+ if( config.hasVertexColor ) {
+ switch( config.colorMaterial ) {
+ case kColorMatAmbientAndDiffuse: frag = &kVS_Out_Diffuse_Lighting_ColorDiffuseAmbient; break;
+ case kColorMatEmission: frag = &kVS_Out_Diffuse_Lighting_ColorEmission; break;
+ default: frag = &kVS_Out_Diffuse_Lighting; break;
+ }
+ } else {
+ frag = &kVS_Out_Diffuse_Lighting;
+ }
+ gen.AddFragment( frag );
+
+ if( config.hasSpecular ) {
+ gen.AddFragment( &kVS_Out_Specular_Lighting );
+ }
+
+ }
+ else
+ {
+ if( config.hasVertexColor )
+ gen.AddFragment( &kVS_Out_Diffuse_VertexColor );
+ else
+ gen.AddFragment( &kVS_Out_Diffuse_White );
+ }
+ // texgen
+ static const ShaderFragment* kFragSources[kTexSourceTypeCount] = {
+ &kVS_Load_UV0,
+ &kVS_Load_UV1,
+ &kVS_Temp_SphereMap,
+ &kVS_Temp_ObjSpacePos,
+ &kVS_Temp_CamSpacePos,
+ &kVS_Temp_CamSpaceRefl,
+ &kVS_Temp_CamSpaceN,
+ };
+ static const char* kFragSourceNames[kTexSourceTypeCount] = {
+ "UV0",
+ "UV1",
+ "SPHR",
+ "OPOS",
+ "CPOS",
+ "REFL",
+ "CNOR",
+ };
+ static const ShaderFragment* kFragMatrices[kTexMatrixTypeCount] = {
+ &kVS_Out_TexCoord,
+ &kVS_Out_Matrix2,
+ &kVS_Out_Matrix3,
+ &kVS_Out_Matrix3
+ };
+ for( int i = 0; i < config.texCoordCount; ++i )
+ {
+ unsigned src = (config.textureSources >> (i*3)) & 7;
+ // normalize normals?
+ if (config.hasNormalization)
+ {
+ if (src == kTexSourceSphereMap || src == kTexSourceCubeReflect || src == kTexSourceCubeNormal)
+ gen.AddFragment (&kVS_Normalize_Normal);
+ }
+ gen.AddFragment( kFragSources[src] );
+ }
+ for( int i = 0; i < config.texCoordCount; ++i )
+ {
+ unsigned src = (config.textureSources >> (i*3)) & 7;
+ unsigned matmode = (config.textureMatrixModes >> (i*2)) & 3;
+ gen.AddFragment (kFragMatrices[matmode], kFragSourceNames[src], i);
+ }
+ ShaderData data;
+ data.shader = NULL;
+ gen.GenerateShader( data.text, data.usedConstants );
+
+ ID3DXBuffer* compiledShader = AssembleD3DShader( data.text );
+ if( compiledShader ) {
+ dev->CreateVertexShader( (const DWORD*)compiledShader->GetBufferPointer(), &data.shader );
+ compiledShader->Release();
+ }
+
+ AssertIf(!data.shader);
+ g_Shaders.insert( std::make_pair(key, data) );
+
+ #if PRINT_AMD_SHADER_ANALYZER_OUTPUT
+ PrintAMDShaderAnalyzer( data.text );
+ #endif
+
+ usedConstants = data.usedConstants;
+ return data.shader;
+}
+
+void SetupVertexShaderD3D9 (
+ IDirect3DDevice9* dev,
+ TransformState& state,
+ const BuiltinShaderParamValues& builtins,
+ VertexPipeConfig& config,
+ const VertexPipeDataD3D9& data,
+ VertexPipePrevious& previous,
+ VertexShaderConstantCache& cache,
+ bool vsActive, bool immediateMode)
+{
+ if( vsActive )
+ return;
+
+ D3D9_CALL(dev->SetTransform( D3DTS_WORLD, (const D3DMATRIX*)state.worldViewMatrix.GetPtr() ));
+
+ // figure out which light types do we have
+ if( !config.hasLighting ) {
+ config.hasLightType = 0;
+ } else {
+ UInt32 hasLightType = 0;
+ for (int i = 0; i < data.vertexLightCount; ++i)
+ {
+ hasLightType |= (1<<data.lights[i].type);
+ }
+ config.hasLightType = hasLightType;
+ }
+
+ // create vertex shader
+ unsigned int usedConstants;
+ IDirect3DVertexShader9* shader = GetShaderForConfig(config, dev, usedConstants);
+ AssertIf(!shader);
+
+ // set shader
+ if( shader != previous.vertexShader )
+ {
+ D3D9_CALL(dev->SetVertexShader( shader ));
+ previous.vertexShader = shader;
+ }
+
+ // matrices
+ Matrix4x4f mvp;
+ MultiplyMatrices4x4 (&builtins.GetMatrixParam(kShaderMatProj), &state.worldViewMatrix, &mvp );
+ mvp.Transpose();
+ cache.SetValues( kConstantLocations[kConstMatrixMVP], mvp.GetPtr(), 4 );
+
+ const Matrix4x4f& mv = state.worldViewMatrix;
+ cache.SetValues( kConstantLocations[kConstMatrixMV], mv.GetPtr(), 4 );
+
+ if( usedConstants & (1<<kConstMatrixMV_IT) )
+ {
+ Matrix4x4f matrixTemp;
+ Matrix4x4f::Invert_General3D( mv, matrixTemp );
+ matrixTemp.Transpose();
+ if (data.normalization == kNormalizationScale)
+ {
+ // Inverse transpose of modelview is only used to transform the normals
+ // in our generated shader. We can just stuff mesh scale in there.
+ float scale = Magnitude (state.worldMatrix.GetAxisX());
+ matrixTemp.Get (0, 0) *= scale;
+ matrixTemp.Get (1, 0) *= scale;
+ matrixTemp.Get (2, 0) *= scale;
+ matrixTemp.Get (0, 1) *= scale;
+ matrixTemp.Get (1, 1) *= scale;
+ matrixTemp.Get (2, 1) *= scale;
+ matrixTemp.Get (0, 2) *= scale;
+ matrixTemp.Get (1, 2) *= scale;
+ matrixTemp.Get (2, 2) *= scale;
+ }
+ cache.SetValues( kConstantLocations[kConstMatrixMV_IT], matrixTemp.GetPtr(), 4 );
+ }
+
+ // misc
+ float misc[4] = { 0, 4, 1, 0.5f };
+ cache.SetValues( kConstantLocations[kConstLightMisc], misc, 1 );
+
+ // if lighting is used:
+ if( config.hasLighting )
+ {
+ // ambient
+ if( config.colorMaterial != kColorMatAmbientAndDiffuse )
+ {
+ SimpleVec4 amb;
+ amb.val[0] = data.ambientClamped.val[0] * data.material.Ambient.r;
+ amb.val[1] = data.ambientClamped.val[1] * data.material.Ambient.g;
+ amb.val[2] = data.ambientClamped.val[2] * data.material.Ambient.b;
+ amb.val[3] = data.ambientClamped.val[3] * data.material.Ambient.a;
+ if( config.colorMaterial != kColorMatEmission ) {
+ amb.val[0] += data.material.Emissive.r;
+ amb.val[1] += data.material.Emissive.g;
+ amb.val[2] += data.material.Emissive.b;
+ amb.val[3] += data.material.Emissive.a;
+ }
+ cache.SetValues( kConstantLocations[kConstAmbient], amb.GetPtr(), 1 );
+ }
+ else
+ {
+ cache.SetValues( kConstantLocations[kConstColorMatAmbient], data.ambientClamped.GetPtr(), 1 );
+ cache.SetValues( kConstantLocations[kConstAmbient], &data.material.Emissive.r, 1 );
+ }
+ previous.ambient = data.ambient;
+
+ // material
+ cache.SetValues( kConstantLocations[kConstMatDiffuse], &data.material.Diffuse.r, 1 );
+ D3D9_CALL(dev->SetVertexShaderConstantF( kConstantLocations[kConstMatDiffuse], &data.material.Diffuse.r, 1 ));
+ if( usedConstants & (1<<kConstMatSpecular) )
+ {
+ D3DCOLORVALUE specAndPower = data.material.Specular;
+ specAndPower.a = data.material.Power;
+ cache.SetValues( kConstantLocations[kConstMatSpecular], &specAndPower.r, 1 );
+ }
+
+ // pack the lights
+ int lightCounts[kLightTypeCount];
+ float lightStart[kLightTypeCount];
+ int lightsTotal = 0;
+ float lightsTotalF = 0;
+ memset(lightCounts, 0, sizeof(lightCounts));
+ memset(lightStart, 0, sizeof(lightStart));
+ VSLightData lights[kMaxSupportedVertexLights];
+ for( int t = 0; t < kLightTypeCount; ++t )
+ {
+ lightStart[t] = lightsTotalF;
+ for( int i = 0; i < data.vertexLightCount; ++i )
+ {
+ const GfxVertexLight& src = data.lights[i];
+ if( src.type != t )
+ continue;
+
+ VSLightData& dst = lights[lightsTotal];
+ // position
+ dst.pos.Set( src.position.x, src.position.y, src.position.z, 1.0f );
+ // direction
+ dst.dir.Set( -src.spotDirection.x, -src.spotDirection.y, -src.spotDirection.z, 0.0f );
+ // color
+ dst.color.Set( src.color.x, src.color.y, src.color.z, 1.0f );
+ // params: 1/(cos(theta/2)-cos(phi/2), cos(phi/2), range^2, d^2 attenuation
+ float sqrRange = src.range * src.range;
+ if( src.type == kLightSpot )
+ {
+ float cosTheta = cosf(Deg2Rad(src.spotAngle)*0.25f);
+ float cosPhi = cosf(Deg2Rad(src.spotAngle)*0.5f);
+ float cosDiff = cosTheta - cosPhi;
+ dst.params.Set(
+ cosDiff != 0.0f ? 1.0f / cosDiff : 0.0f,
+ cosPhi,
+ src.range * src.range,
+ src.quadAtten
+ );
+ }
+ else
+ {
+ dst.params.Set(
+ 0.0f,
+ 0.0f,
+ src.range * src.range,
+ src.quadAtten
+ );
+ }
+
+ ++lightCounts[t];
+ ++lightsTotal;
+ ++lightsTotalF;
+ }
+ }
+
+ // light indices
+ int miscI[kLightTypeCount][4];
+ for( int t = 0; t < kLightTypeCount; ++t ) {
+ miscI[t][0] = lightCounts[t];
+ miscI[t][1] = 0;
+ miscI[t][2] = 0;
+ miscI[t][3] = 0;
+ }
+ D3D9_CALL(dev->SetVertexShaderConstantI( 0, miscI[0], kLightTypeCount ));
+
+ if (lightsTotal)
+ cache.SetValues( 60, (const float*)lights, 4*lightsTotal );
+ misc[0] = lightStart[0] * 4.0f;
+ misc[1] = lightStart[1] * 4.0f;
+ misc[2] = lightStart[2] * 4.0f;
+ misc[3] = 0.0f;
+ cache.SetValues(kConstantLocations[kConstLightIndexes], misc, 1);
+ }
+
+ // texture matrices & transform flags
+ UInt32 matrixModes = config.textureMatrixModes;
+ UInt32 projectedTextures = data.projectedTextures;
+ UInt32 textureSources = config.textureSources;
+ for( int i = 0; i < config.texCoordCount; ++i )
+ {
+ unsigned matmode = (matrixModes >> (i*2)) & 0x3;
+ if( matmode != kTexMatrixNone )
+ {
+ cache.SetValues(kConstantLocations[kConstMatrixTexture]+i*4, state.texMatrices[i].GetPtr(), 4);
+ }
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_TEXCOORDINDEX, i ));
+ // projected texture flag
+ DWORD textureTransformFlags = (projectedTextures & (1<<i)) ? D3DTTFF_PROJECTED : D3DTTFF_DISABLE;
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_TEXTURETRANSFORMFLAGS, textureTransformFlags ));
+ }
+
+ if( config.texCoordCount != previous.config.texCoordCount )
+ {
+ for( int i = config.texCoordCount; i < kMaxSupportedTextureCoords; ++i )
+ {
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_TEXCOORDINDEX, i ));
+ D3D9_CALL(dev->SetTextureStageState( i, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE ));
+ }
+ }
+
+ previous.config = config;
+}
+
+
+void CleanupVertexShadersD3D9 ()
+{
+ #if PRINT_VERTEX_PIPE_STATS
+ printf_console("Vertex pipe shader cache: %i shaders generated\n", g_Shaders.size());
+ #endif
+ ShaderCache::iterator it, itEnd = g_Shaders.end();
+ for( it = g_Shaders.begin(); it != itEnd; ++it )
+ {
+ IDirect3DVertexShader9* vs = it->second.shader;
+ if( vs ) {
+ ULONG refCount = vs->Release();
+ AssertIf( refCount != 0 );
+ }
+ }
+ g_Shaders.clear ();
+}
+
diff --git a/Runtime/GfxDevice/d3d/VertexPipeD3D9.h b/Runtime/GfxDevice/d3d/VertexPipeD3D9.h
new file mode 100644
index 0000000..af9d8d3
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/VertexPipeD3D9.h
@@ -0,0 +1,139 @@
+#pragma once
+
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/GfxDevice/GfxDeviceObjects.h"
+#include "Runtime/Math/Vector4.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/GfxDevice/ShaderConstantCache.h"
+#include "Runtime/GfxDevice/TransformState.h"
+#include "D3D9Includes.h"
+
+class BuiltinShaderParamValues;
+
+enum TextureSourceMode {
+ kTexSourceUV0,
+ kTexSourceUV1,
+ // match the order of TexGenMode!
+ kTexSourceSphereMap,
+ kTexSourceObject,
+ kTexSourceEyeLinear,
+ kTexSourceCubeReflect,
+ kTexSourceCubeNormal,
+ kTexSourceTypeCount
+};
+
+enum TextureMatrixMode {
+ kTexMatrixNone,
+ kTexMatrix2,
+ kTexMatrix3,
+ kTexMatrix4,
+ kTexMatrixTypeCount
+};
+
+struct VertexPipeConfig {
+ // 2 bytes
+ UInt64 textureMatrixModes : 16; // TextureMatrixMode: 2 bits for each unit
+ // 3 bytes
+ UInt64 textureSources : 24; // TextureSourceMode: 3 bits for each unit
+ // 1 byte
+ UInt64 colorMaterial : 3; // ColorMaterialMode
+ UInt64 texCoordCount : 4; // number of texture coordinates
+ UInt64 hasVertexColor : 1; // is vertex color coming from per-vertex data?
+ // 1 byte
+ UInt64 hasLighting : 1; // lighting on?
+ UInt64 hasSpecular : 1; // specular on?
+ UInt64 hasLightType : 3; // has light of given type? (bit per type)
+ UInt64 hasNormalization : 1; // needs to normalize normals?
+ // 10 bits left
+
+ void Reset() {
+ memset(this, 0, sizeof(*this));
+ }
+
+ void SetTextureUnit( UInt32 unit ) {
+ Assert (unit < 8);
+ UInt32 tc = texCoordCount;
+ if( unit >= tc ) {
+ tc = unit+1;
+ texCoordCount = tc;
+ }
+ }
+ void ClearTextureUnit( UInt32 unit ) {
+ Assert (unit < 8);
+ UInt32 tc = texCoordCount;
+ if( unit < tc ) {
+ tc = unit;
+ texCoordCount = tc;
+ }
+ }
+};
+
+
+struct VertexPipeDataD3D9
+{
+ GfxVertexLight lights[kMaxSupportedVertexLights];
+ D3DMATERIAL9 material;
+ SimpleVec4 ambient;
+ SimpleVec4 ambientClamped;
+ int vertexLightCount;
+ UInt32 projectedTextures; // 1 bit per unit
+
+
+ NormalizationMode normalization;
+
+ mutable bool haveToResetDeviceState;
+
+ void Reset() {
+ memset (&material, 0, sizeof(material));
+ ambient.set (0,0,0,0);
+ ambientClamped.set (0,0,0,0);
+ vertexLightCount = 0;
+ projectedTextures = 0;
+ normalization = kNormalizationUnknown;
+ haveToResetDeviceState = false;
+ }
+};
+
+
+struct VertexPipePrevious {
+ VertexPipeConfig config;
+ SimpleVec4 ambient;
+ int vertexLightCount;
+ IDirect3DVertexShader9* vertexShader;
+
+ void Reset() {
+ config.Reset ();
+ ambient.set(-1,-1,-1,-1);
+ vertexLightCount = 0;
+ vertexShader = NULL;
+ }
+};
+
+void ResetVertexPipeStateD3D9 (
+ IDirect3DDevice9* dev,
+ TransformState& state,
+ BuiltinShaderParamValues& builtins,
+ VertexPipeConfig& config,
+ VertexPipeDataD3D9& data,
+ VertexPipePrevious& previous);
+
+void SetupFixedFunctionD3D9 (
+ IDirect3DDevice9* dev,
+ TransformState& state,
+ BuiltinShaderParamValues& builtins,
+ const VertexPipeConfig& config,
+ const VertexPipeDataD3D9& data,
+ VertexPipePrevious& previous,
+ bool vsActive, bool immediateMode);
+
+void SetupVertexShaderD3D9 (
+ IDirect3DDevice9* dev,
+ TransformState& state,
+ const BuiltinShaderParamValues& builtins,
+ VertexPipeConfig& config,
+ const VertexPipeDataD3D9& data,
+ VertexPipePrevious& previous,
+ VertexShaderConstantCache& cache,
+ bool vsActive, bool immediateMode);
+
+void CleanupVertexShadersD3D9 ();
diff --git a/Runtime/GfxDevice/d3d11/ConstantBuffersD3D11.cpp b/Runtime/GfxDevice/d3d11/ConstantBuffersD3D11.cpp
new file mode 100644
index 0000000..fe61e06
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/ConstantBuffersD3D11.cpp
@@ -0,0 +1,368 @@
+#include "UnityPrefix.h"
+#include "ConstantBuffersD3D11.h"
+#include "D3D11Context.h"
+#include "D3D11Utils.h"
+
+
+// NEVER enable this for release! Turns off all CB caching and makes things
+// very slow, for debugging.
+#define DEBUG_DISABLE_CONSTANT_BUFFER_CACHES (0 && !UNITY_RELEASE)
+
+
+#if DEBUG_D3D11_CONSTANT_BUFFER_STATS
+#include "External/shaderlab/Library/FastPropertyName.h"
+extern std::string g_LastParsedShaderName;
+#endif
+
+
+ConstantBuffersD3D11::ConstantBuffersD3D11()
+{
+ InvalidateState();
+}
+
+void ConstantBuffersD3D11::InvalidateState()
+{
+ memset (m_ActiveBuffers, 0, sizeof(m_ActiveBuffers));
+}
+
+
+void ConstantBuffersD3D11::Clear()
+{
+ memset (m_ActiveBuffers, 0, sizeof(m_ActiveBuffers));
+ for (size_t i = 0; i < m_Buffers.size(); ++i)
+ {
+ ConstBuffer& cb = m_Buffers[i];
+ delete[] cb.data;
+ if (cb.buffer)
+ {
+ cb.buffer->Release();
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(cb.buffer);
+ }
+ #if DEBUG_D3D11_CONSTANT_BUFFER_STATS
+ delete[] cb.changeCounts;
+ delete[] cb.tryCounts;
+ #endif
+ }
+ m_Buffers.clear();
+ m_BufferKeys.clear();
+}
+
+
+void ConstantBuffersD3D11::SetCBInfo (int id, int size)
+{
+ size_t n = m_Buffers.size();
+ Assert (m_BufferKeys.size() == n);
+ UInt32 key = id | (size<<16);
+ for (size_t i = 0; i < n; ++i)
+ {
+ if (m_BufferKeys[i] == key)
+ return;
+ }
+
+ // not found, create one
+ ConstBuffer cb;
+ cb.data = new UInt8[size];
+ memset (cb.data, 0, size);
+ cb.dirty = true;
+ for (int i = 0; i < kShaderTypeCount; ++i)
+ cb.bindIndex[i] = -1;
+ cb.bindStages = 0;
+ #if DEBUG_D3D11_CONSTANT_BUFFER_STATS
+ cb.statsDirty = 0;
+ for (int i = 0; i < kShaderTypeCount; ++i)
+ cb.stats[i] = 0;
+ ShaderLab::FastPropertyName name;
+ name.index = id;
+ printf_console ("DX11 Constant Buffer Info: new %s size=%i shader=%s\n", name.GetName(), size, g_LastParsedShaderName.c_str());
+ cb.changeCounts = new int[size/4];
+ memset (cb.changeCounts, 0, size);
+ cb.tryCounts = new int[size/4];
+ memset (cb.tryCounts, 0, size);
+ #endif
+
+ ID3D11Device* dev = GetD3D11Device();
+ // Default usage and using UpdateSubresource is seemingly preferred path in drivers
+ // over dynamic buffer with Map.
+ D3D11_BUFFER_DESC desc;
+ desc.ByteWidth = size;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+ desc.CPUAccessFlags = 0;
+ desc.MiscFlags = 0;
+ desc.StructureByteStride = 0;
+
+ HRESULT hr = dev->CreateBuffer (&desc, NULL, &cb.buffer);
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(cb.buffer,size,this);
+ Assert (SUCCEEDED(hr));
+ SetDebugNameD3D11 (cb.buffer, Format("ConstantBuffer-%d-%d", id, size));
+
+ m_Buffers.push_back (cb);
+ m_BufferKeys.push_back (key);
+}
+
+void ConstantBuffersD3D11::SetBuiltinCBConstant (int id, int offset, const void* data, int size)
+{
+ int idx = GetCBIndexByID (id);
+ ConstBuffer& cb = m_Buffers[idx];
+ Assert (offset >= 0 && offset+size <= (m_BufferKeys[idx]>>16) && size > 0);
+
+ #if DEBUG_D3D11_CONSTANT_BUFFER_STATS
+ for (int i = offset/4; i < offset/4+size/4; ++i)
+ ++cb.tryCounts[i];
+ #endif
+
+ if (DEBUG_DISABLE_CONSTANT_BUFFER_CACHES || memcmp(cb.data+offset, data, size) != 0)
+ {
+ memcpy (cb.data+offset, data, size);
+ cb.dirty = true;
+
+ #if DEBUG_D3D11_CONSTANT_BUFFER_STATS
+ for (int i = offset/4; i < offset/4+size/4; ++i)
+ ++cb.changeCounts[i];
+ #endif
+ }
+}
+
+void ConstantBuffersD3D11::SetCBConstant (int idx, int offset, const void* data, int size)
+{
+ Assert (idx >= 0 && idx < m_Buffers.size());
+ ConstBuffer& cb = m_Buffers[idx];
+ Assert (offset >= 0 && offset+size <= (m_BufferKeys[idx]>>16) && size > 0);
+
+ #if DEBUG_D3D11_CONSTANT_BUFFER_STATS
+ for (int i = offset/4; i < offset/4+size/4; ++i)
+ ++cb.tryCounts[i];
+ #endif
+
+ if (size == 4)
+ {
+ UInt32* dstData = (UInt32*)(cb.data+offset);
+ UInt32 srcData = *(UInt32*)data;
+ if (DEBUG_DISABLE_CONSTANT_BUFFER_CACHES || *dstData != srcData)
+ {
+ *dstData = srcData;
+ cb.dirty = true;
+
+ #if DEBUG_D3D11_CONSTANT_BUFFER_STATS
+ for (int i = offset/4; i < offset/4+size/4; ++i)
+ ++cb.changeCounts[i];
+ #endif
+ }
+ }
+ else
+ {
+ if (DEBUG_DISABLE_CONSTANT_BUFFER_CACHES || memcmp(cb.data+offset, data, size) != 0)
+ {
+ memcpy (cb.data+offset, data, size);
+ cb.dirty = true;
+
+ #if DEBUG_D3D11_CONSTANT_BUFFER_STATS
+ for (int i = offset/4; i < offset/4+size/4; ++i)
+ ++cb.changeCounts[i];
+ #endif
+ }
+ }
+}
+
+int ConstantBuffersD3D11::FindAndBindCB (int id, ShaderType shaderType, int bind, int size)
+{
+ UInt32 key = id | (size<<16);
+ int idx = 0;
+ for (ConstBufferKeys::const_iterator it = m_BufferKeys.begin(), itEnd = m_BufferKeys.end(); it != itEnd; ++it, ++idx)
+ {
+ if (*it == key)
+ {
+ ConstBuffer& cb = m_Buffers[idx];
+ if (bind >= 0)
+ {
+ cb.bindIndex[shaderType] = bind;
+ cb.bindStages |= (1<<shaderType);
+ }
+ return idx;
+ }
+ }
+ Assert (false);
+ return -1;
+}
+
+void ConstantBuffersD3D11::ResetBinds(ShaderType shaderType)
+{
+ for (ConstBuffers::iterator it = m_Buffers.begin(), itEnd = m_Buffers.end(); it != itEnd; ++it)
+ {
+ it->bindIndex[shaderType] = -1;
+ it->bindStages &= ~(1<<shaderType);
+ }
+}
+
+
+void ConstantBuffersD3D11::UpdateBuffers ()
+{
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ HRESULT hr;
+ size_t n = m_Buffers.size();
+
+ #if !UNITY_RELEASE
+ // check if we have duplicate buffers bound to the same slot (should never happen!)
+ UInt32 bound[kShaderTypeCount] = {0};
+ for (size_t i = 0; i < n; ++i)
+ {
+ ConstBuffer& cb = m_Buffers[i];
+ for (int pt = kShaderVertex; pt < kShaderTypeCount; ++pt)
+ {
+ int bind = cb.bindIndex[pt];
+ if (bind >= 0 && bind < 32)
+ {
+ Assert (!(bound[pt] & (1<<bind)));
+ bound[pt] |= (1<<bind);
+ }
+ }
+ }
+ #endif
+
+
+ for (size_t i = 0; i < n; ++i)
+ {
+ ConstBuffer& cb = m_Buffers[i];
+ if (!DEBUG_DISABLE_CONSTANT_BUFFER_CACHES && cb.bindStages == 0)
+ continue;
+ if (DEBUG_DISABLE_CONSTANT_BUFFER_CACHES || cb.dirty)
+ {
+ ctx->UpdateSubresource (cb.buffer, 0, NULL, cb.data, (m_BufferKeys[i]>>16), 1);
+ #if DEBUG_D3D11_CONSTANT_BUFFER_STATS
+ ++cb.statsDirty;
+ #endif
+ }
+
+ int bindIndex;
+
+ // Bind to used stages
+ // WP8 seems to be buggy with constant buffers; we need to always rebind them. Hence UNITY_WP8 test below.
+ #define BIND_CB(cbShaderType,dxCall) \
+ bindIndex = cb.bindIndex[cbShaderType]; \
+ if (bindIndex >= 0 && (DEBUG_DISABLE_CONSTANT_BUFFER_CACHES || UNITY_WP8 || m_ActiveBuffers[cbShaderType][bindIndex] != cb.buffer)) { \
+ ctx->dxCall (bindIndex, 1, &cb.buffer); \
+ m_ActiveBuffers[cbShaderType][bindIndex] = cb.buffer; \
+ }
+
+ BIND_CB(kShaderVertex,VSSetConstantBuffers);
+ BIND_CB(kShaderFragment,PSSetConstantBuffers);
+ BIND_CB(kShaderGeometry,GSSetConstantBuffers);
+ BIND_CB(kShaderHull,HSSetConstantBuffers);
+ BIND_CB(kShaderDomain,DSSetConstantBuffers);
+ cb.dirty = false;
+ }
+}
+
+
+#if DEBUG_D3D11_CONSTANT_BUFFER_STATS
+
+static void WriteTGAFile (const char* filename, int width, int height, const UInt8* bgr)
+{
+ FILE* f = fopen(filename, "wb");
+ // header
+ putc(0,f);
+ putc(0,f);
+ putc(2,f); // uncompressed RGB
+ putc(0,f); putc(0,f);
+ putc(0,f); putc(0,f);
+ putc(0,f);
+ putc(0,f); putc(0,f);
+ putc(0,f); putc(0,f);
+ putc((width & 0x00FF),f);
+ putc((width & 0xFF00)>>8,f);
+ putc((height & 0x00FF),f);
+ putc((height & 0xFF00)>>8,f);
+ putc(24,f); // 24 bit
+ putc(0x20,f); // vertical flip
+ // data
+ fwrite (bgr, 3, width*height, f);
+ fclose (f);
+}
+
+static void DensityToBGR (int density, UInt8* bgr)
+{
+ if (density < 1)
+ {
+ bgr[0] = bgr[1] = bgr[2] = 0;
+ return;
+ }
+ bgr[0] = clamp(40+density/4, 0, 255);
+ bgr[1] = clamp(40+density/4, 0, 255);
+ bgr[2] = clamp(40+density/4, 0, 255);
+}
+
+static void PutBGRPixelBlock (UInt8* img, int imgWidth, int x, int y, const UInt8* bgr)
+{
+ for (int i = 0; i < 4; ++i)
+ {
+ UInt8* ptr = img + ((y+i)*imgWidth+x) * 3;
+ for (int j = 0; j < 4; ++j, ptr += 3)
+ {
+ ptr[0] = bgr[0];
+ ptr[1] = bgr[1];
+ ptr[2] = bgr[2];
+ }
+ }
+}
+
+
+void ConstantBuffersD3D11::NewFrame()
+{
+ if (GetAsyncKeyState(VK_F7))
+ {
+ printf_console ("DX11 Constant Buffer stats:\n");
+ float traffic = 0.0f;
+ int uploads = 0;
+ int maxSize = 0;
+ for (size_t i = 0; i < m_BufferKeys.size(); ++i)
+ maxSize = std::max(int(m_BufferKeys[i]>>16), maxSize);
+
+ int imgWidth = maxSize+1;
+ int imgHeight = m_Buffers.size()*3*4;
+ UInt8* imgData = new UInt8[imgWidth*imgHeight*3];
+ memset (imgData, 0, imgWidth*imgHeight*3);
+
+ for (size_t i = 0; i < m_Buffers.size(); ++i)
+ {
+ ConstBuffer& cb = m_Buffers[i];
+ int cbId = (m_BufferKeys[i]&0xFFFF);
+ int cbSize = (m_BufferKeys[i]>>16);
+ ShaderLab::FastPropertyName name;
+ name.index = cbId;
+ traffic += (cbSize*cb.statsDirty)/1024.0f;
+ uploads += cb.statsDirty;
+ printf_console (" %s size:%i (%.1fkB in %i upl) vs:%i ps:%i\n", name.GetName(), cbSize, (cbSize*cb.statsDirty)/1024.0f, cb.statsDirty, cb.statsVS, cb.statsPS);
+ if (cb.statsDirty > 0)
+ {
+ for (int j = 0; j < cbSize/4; ++j)
+ {
+ UInt8 bgr[3];
+ DensityToBGR (cb.tryCounts[j], bgr);
+ PutBGRPixelBlock (imgData, imgWidth, j*4, i*3*4, bgr);
+ DensityToBGR (cb.changeCounts[j], bgr);
+ PutBGRPixelBlock (imgData, imgWidth, j*4, i*3*4+4, bgr);
+ }
+ }
+ for (int j = 0; j < 8; ++j)
+ {
+ imgData[((i*3*4+j)*imgWidth + cbSize)*3 + 1] = 255;
+ }
+ }
+ WriteTGAFile ("cbStats.tga", imgWidth, imgHeight, imgData);
+ delete[] imgData;
+ printf_console (" =%i uploads, %.1fkB traffic\n\n", uploads, traffic);
+ }
+
+ // reset stats
+ for (size_t i = 0; i < m_Buffers.size(); ++i)
+ {
+ ConstBuffer& cb = m_Buffers[i];
+ int cbSize = (m_BufferKeys[i]>>16);
+ cb.statsDirty = cb.statsVS = cb.statsPS = 0;
+ memset (cb.changeCounts, 0, cbSize/4*4);
+ memset (cb.tryCounts, 0, cbSize/4*4);
+ }
+}
+#endif
diff --git a/Runtime/GfxDevice/d3d11/ConstantBuffersD3D11.h b/Runtime/GfxDevice/d3d11/ConstantBuffersD3D11.h
new file mode 100644
index 0000000..7c2a3eb
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/ConstantBuffersD3D11.h
@@ -0,0 +1,74 @@
+#pragma once
+
+#include "D3D11Includes.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+
+#define DEBUG_D3D11_CONSTANT_BUFFER_STATS 0
+
+#if DEBUG_D3D11_CONSTANT_BUFFER_STATS
+#include <map>
+#endif
+
+
+class ConstantBuffersD3D11
+{
+public:
+ ConstantBuffersD3D11();
+ ~ConstantBuffersD3D11() { Clear(); }
+
+ void Clear();
+ void InvalidateState();
+
+ struct ConstBuffer {
+ int bindIndex[kShaderTypeCount];
+ unsigned bindStages;
+ bool dirty;
+ UInt8* data;
+ ID3D11Buffer* buffer;
+ #if DEBUG_D3D11_CONSTANT_BUFFER_STATS
+ int statsDirty;
+ int stats[kShaderTypeCount]
+ int* tryCounts;
+ int* changeCounts;
+ #endif
+ };
+
+ void SetCBInfo (int id, int size);
+ int FindAndBindCB (int id, ShaderType shaderType, int bind, int size);
+ void ResetBinds (ShaderType shaderType);
+
+ void SetBuiltinCBConstant (int id, int offset, const void* data, int size);
+ void SetCBConstant (int index, int offset, const void* data, int size);
+
+ void UpdateBuffers();
+ void NewFrame();
+
+private:
+ inline int GetCBIndexByID (int id) const
+ {
+ UInt32 key = id;
+ int n = m_BufferKeys.size();
+ for (int i = 0; i < n; ++i)
+ {
+ if ((m_BufferKeys[i]&0xFFFF) == key)
+ return i;
+ }
+ Assert (false);
+ return -1;
+ }
+
+private:
+ typedef std::vector<UInt32> ConstBufferKeys;
+ typedef std::vector<ConstBuffer> ConstBuffers;
+ ConstBufferKeys m_BufferKeys;
+ ConstBuffers m_Buffers;
+
+ ID3D11Buffer* m_ActiveBuffers[kShaderTypeCount][16];
+};
+
+
+#if !DEBUG_D3D11_CONSTANT_BUFFER_STATS
+inline void ConstantBuffersD3D11::NewFrame() { }
+#endif
+
diff --git a/Runtime/GfxDevice/d3d11/D3D11ByteCode.cpp b/Runtime/GfxDevice/d3d11/D3D11ByteCode.cpp
new file mode 100644
index 0000000..8ec6c6a
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/D3D11ByteCode.cpp
@@ -0,0 +1,2237 @@
+#include "UnityPrefix.h"
+#include "D3D11ByteCode.h"
+#include "D3D11Includes.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "External/DirectX/builds/dx9include/d3d9.h"
+
+// Some things in this file are based on Mesa3d DX11 state tracker:
+
+/**************************************************************************
+ *
+ * Copyright 2010 Luca Barbieri
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+
+
+enum SM4SystemValue
+{
+ kSM4SV_UNDEFINED,
+ kSM4SV_POSITION,
+ kSM4SV_CLIP_DISTANCE,
+ kSM4SV_CULL_DISTANCE,
+ kSM4SV_RENDER_TARGET_ARRAY_INDEX,
+ kSM4SV_VIEWPORT_ARRAY_INDEX,
+ kSM4SV_VERTEX_ID,
+ kSM4SV_PRIMITIVE_ID,
+ kSM4SV_INSTANCE_ID,
+ kSM4SV_IS_FRONT_FACE,
+ kSM4SV_SAMPLE_INDEX,
+ kSM4SV_FINAL_QUAD_U_EQ_0_EDGE_TESSFACTOR,
+ kSM4SV_FINAL_QUAD_V_EQ_0_EDGE_TESSFACTOR,
+ kSM4SV_FINAL_QUAD_U_EQ_1_EDGE_TESSFACTOR,
+ kSM4SV_FINAL_QUAD_V_EQ_1_EDGE_TESSFACTOR,
+ kSM4SV_FINAL_QUAD_U_INSIDE_TESSFACTOR,
+ kSM4SV_FINAL_QUAD_V_INSIDE_TESSFACTOR,
+ kSM4SV_FINAL_TRI_U_EQ_0_EDGE_TESSFACTOR,
+ kSM4SV_FINAL_TRI_V_EQ_0_EDGE_TESSFACTOR,
+ kSM4SV_FINAL_TRI_W_EQ_0_EDGE_TESSFACTOR,
+ kSM4SV_FINAL_TRI_INSIDE_TESSFACTOR,
+ kSM4SV_FINAL_LINE_DETAIL_TESSFACTOR,
+ kSM4SV_FINAL_LINE_DENSITY_TESSFACTOR,
+ kSM4SV_COUNT
+};
+
+enum SM4InstrExtype
+{
+ SM4_TOKEN_INSTRUCTION_EXTENDED_TYPE_EMPTY,
+ SM4_TOKEN_INSTRUCTION_EXTENDED_TYPE_SAMPLE_CONTROLS,
+ SM4_TOKEN_INSTRUCTION_EXTENDED_TYPE_RESOURCE_DIM,
+ SM4_TOKEN_INSTRUCTION_EXTENDED_TYPE_RESOURCE_RETURN_TYPE,
+ SM4_TOKEN_INSTRUCTION_EXTENDED_TYPE_COUNT
+};
+
+enum SM4OperExtype
+{
+ SM4_TOKEN_OPERAND_EXTENDED_TYPE_EMPTY,
+ SM4_TOKEN_OPERAND_EXTENDED_TYPE_MODIFIER,
+ SM4_TOKEN_OPERAND_EXTENDED_TYPE_COUNT
+};
+
+enum SM4ReturnType
+{
+ kSM4RetType_UNORM = 1,
+ kSM4RetType_SNORM = 2,
+ kSM4RetType_SINT = 3,
+ kSM4RetType_UINT = 4,
+ kSM4RetType_FLOAT = 5,
+ kSM4RetType_MIXED = 6,
+};
+
+
+const char* kSM4OpcodeNames[kSM4Op_COUNT] = {
+ "add",
+ "and",
+ "break",
+ "breakc",
+ "call",
+ "callc",
+ "case",
+ "continue",
+ "continuec",
+ "cut",
+ "default",
+ "deriv_rtx",
+ "deriv_rty",
+ "discard",
+ "div",
+ "dp2",
+ "dp3",
+ "dp4",
+ "else",
+ "emit",
+ "emitthencut",
+ "endif",
+ "endloop",
+ "endswitch",
+ "eq",
+ "exp",
+ "frc",
+ "ftoi",
+ "ftou",
+ "ge",
+ "iadd",
+ "if",
+ "ieq",
+ "ige",
+ "ilt",
+ "imad",
+ "imax",
+ "imin",
+ "imul",
+ "ine",
+ "ineg",
+ "ishl",
+ "ishr",
+ "itof",
+ "label",
+ "ld",
+ "ldms",
+ "log",
+ "loop",
+ "lt",
+ "mad",
+ "min",
+ "max",
+ "customdata",
+ "mov",
+ "movc",
+ "mul",
+ "ne",
+ "nop",
+ "not",
+ "or",
+ "resinfo",
+ "ret",
+ "retc",
+ "round_ne",
+ "round_ni",
+ "round_pi",
+ "round_z",
+ "rsq",
+ "sample",
+ "sample_c",
+ "sample_c_lz",
+ "sample_l",
+ "sample_d",
+ "sample_b",
+ "sqrt",
+ "switch",
+ "sincos",
+ "udiv",
+ "ult",
+ "uge",
+ "umul",
+ "umad",
+ "umax",
+ "umin",
+ "ushr",
+ "utof",
+ "xor",
+ "dcl_resource",
+ "dcl_constant_buffer",
+ "dcl_sampler",
+ "dcl_index_range",
+ "dcl_gs_output_primitive_topology",
+ "dcl_gs_input_primitive",
+ "dcl_max_output_vertex_count",
+ "dcl_input",
+ "dcl_input_sgv",
+ "dcl_input_siv",
+ "dcl_input_ps",
+ "dcl_input_ps_sgv",
+ "dcl_input_ps_siv",
+ "dcl_output",
+ "dcl_output_sgv",
+ "dcl_output_siv",
+ "dcl_temps",
+ "dcl_indexable_temp",
+ "dcl_global_flags",
+ "d3d10_count",
+ "lod",
+ "gather4",
+ "sample_pos",
+ "sample_info",
+ "d3d10_1_count",
+ "hs_decls",
+ "hs_control_point_phase",
+ "hs_fork_phase",
+ "hs_join_phase",
+ "emit_stream",
+ "cut_stream",
+ "emitthencut_stream",
+ "interface_call",
+ "bufinfo",
+ "deriv_rtx_coarse",
+ "deriv_rtx_fine",
+ "deriv_rty_coarse",
+ "deriv_rty_fine",
+ "gather4_c",
+ "gather4_po",
+ "gather4_po_c",
+ "rcp",
+ "f32tof16",
+ "f16tof32",
+ "uaddc",
+ "usubb",
+ "countbits",
+ "firstbit_hi",
+ "firstbit_lo",
+ "firstbit_shi",
+ "ubfe",
+ "ibfe",
+ "bfi",
+ "bfrev",
+ "swapc",
+ "dcl_stream",
+ "dcl_function_body",
+ "dcl_function_table",
+ "dcl_interface",
+ "dcl_input_control_point_count",
+ "dcl_output_control_point_count",
+ "dcl_tess_domain",
+ "dcl_tess_partitioning",
+ "dcl_tess_output_primitive",
+ "dcl_hs_max_tessfactor",
+ "dcl_hs_fork_phase_instance_count",
+ "dcl_hs_join_phase_instance_count",
+ "dcl_thread_group",
+ "dcl_unordered_access_view_typed",
+ "dcl_unordered_access_view_raw",
+ "dcl_unordered_access_view_structured",
+ "dcl_thread_group_shared_memory_raw",
+ "dcl_thread_group_shared_memory_structured",
+ "dcl_resource_raw",
+ "dcl_resource_structured",
+ "ld_uav_typed",
+ "store_uav_typed",
+ "ld_raw",
+ "store_raw",
+ "ld_structured",
+ "store_structured",
+ "atomic_and",
+ "atomic_or",
+ "atomic_xor",
+ "atomic_cmp_store",
+ "atomic_iadd",
+ "atomic_imax",
+ "atomic_imin",
+ "atomic_umax",
+ "atomic_umin",
+ "imm_atomic_alloc",
+ "imm_atomic_consume",
+ "imm_atomic_iadd",
+ "imm_atomic_and",
+ "imm_atomic_or",
+ "imm_atomic_xor",
+ "imm_atomic_exch",
+ "imm_atomic_cmp_exch",
+ "imm_atomic_imax",
+ "imm_atomic_imin",
+ "imm_atomic_umax",
+ "imm_atomic_umin",
+ "sync",
+ "dadd",
+ "dmax",
+ "dmin",
+ "dmul",
+ "deq",
+ "dge",
+ "dlt",
+ "dne",
+ "dmov",
+ "dmovc",
+ "dtof",
+ "ftod",
+ "eval_snapped",
+ "eval_sample_index",
+ "eval_centroid",
+ "dcl_gs_instance_count",
+};
+
+struct SM4TokVersion
+{
+ unsigned minor : 4;
+ unsigned major : 4;
+ unsigned format : 8;
+ unsigned type : 16;
+};
+
+
+
+struct SM4TokResourceReturnType
+{
+ unsigned x : 4;
+ unsigned y : 4;
+ unsigned z : 4;
+ unsigned w : 4;
+};
+
+
+#define SM4_OPERAND_SEL_MASK(sel) ((sel) & 0xf)
+#define SM4_OPERAND_SEL_SWZ(sel, i) (((sel) >> ((i) * 2)) & 3)
+#define SM4_OPERAND_SEL_SCALAR(sel) ((sel) & 3)
+
+struct SM4TokOperandEx
+{
+ union {
+ UInt32 dword;
+ struct {
+ unsigned type : 6;
+ unsigned neg : 1;
+ unsigned abs : 1;
+ };
+ };
+};
+
+struct SM4TokResourceRetType
+{
+ union {
+ UInt32 dword;
+ struct {
+ unsigned x : 4;
+ unsigned y : 4;
+ unsigned z : 4;
+ unsigned w : 4;
+ unsigned reserved : 16;
+ };
+ };
+};
+
+
+union SM4Any
+{
+ float f32;
+ SInt64 i64;
+ SInt32 i32;
+};
+
+struct SM4Op;
+struct SM4Instr;
+struct SM4Decl;
+struct SM4Program;
+
+
+struct SM4Op
+{
+ UInt8 mode;
+ UInt8 comps;
+ UInt8 mask;
+ UInt8 num_indices;
+ UInt8 swizzle[4];
+ SM4RegFile file;
+ SM4Any imm_values[4];
+ bool neg;
+ bool abs;
+ struct
+ {
+ SInt64 disp;
+ std::auto_ptr<SM4Op> reg;
+ } indices[3];
+
+ bool is_index_simple(unsigned i) const
+ {
+ return !indices[i].reg.get() && indices[i].disp >= 0 && (SInt64)(SInt32)indices[i].disp == indices[i].disp;
+ }
+
+ bool has_simple_index() const
+ {
+ return num_indices == 1 && is_index_simple(0);
+ }
+
+ SM4Op()
+ {
+ memset(this, 0, sizeof(*this));
+ }
+
+private:
+ SM4Op(const SM4Op& op)
+ {}
+};
+
+/* for sample_d */
+#define SM4_MAX_OPS 6
+
+struct SM4Instr : public SM4TokInstruction
+{
+ SInt8 sample_offset[3];
+ UInt8 resource_target;
+ UInt8 resource_return_type[4];
+
+ unsigned num;
+ unsigned num_ops;
+ std::auto_ptr<SM4Op> ops[SM4_MAX_OPS];
+
+ SM4Instr()
+ {
+ memset(this, 0, sizeof(*this));
+ }
+
+private:
+ SM4Instr(const SM4Instr& op)
+ {}
+};
+
+struct SM4Decl : public SM4TokInstruction
+{
+ std::auto_ptr<SM4Op> op;
+ union
+ {
+ unsigned num;
+ float f32;
+ SM4SystemValue sv;
+ struct
+ {
+ unsigned id;
+ unsigned expected_function_table_length;
+ unsigned table_length;
+ unsigned array_length;
+ } intf;
+ unsigned thread_group_size[3];
+ SM4TokResourceReturnType rrt;
+ struct
+ {
+ unsigned num;
+ unsigned comps;
+ } indexable_temp;
+ struct
+ {
+ unsigned stride;
+ unsigned count;
+ } structured;
+ };
+
+ void* data;
+
+ SM4Decl()
+ {
+ memset(this, 0, sizeof(*this));
+ }
+
+ ~SM4Decl()
+ {
+ free(data);
+ }
+
+private:
+ SM4Decl(const SM4Decl& op)
+ {}
+};
+
+struct _D3D11_SIGNATURE_PARAMETER_DESC;
+
+struct SM4Program
+{
+ SM4TokVersion version;
+ dynamic_array<SM4Decl*> dcls;
+ dynamic_array<SM4Instr*> insns;
+
+ SM4Program()
+ {
+ memset(&version, 0, sizeof(version));
+ }
+
+ ~SM4Program()
+ {
+ for(dynamic_array<SM4Decl*>::iterator i = dcls.begin(), e = dcls.end(); i != e; ++i)
+ delete *i;
+ for(dynamic_array<SM4Instr*>::iterator i = insns.begin(), e = insns.end(); i != e; ++i)
+ delete *i;
+ }
+
+private:
+ SM4Program(const SM4Decl& op)
+ {}
+};
+
+SM4Program* sm4_parse(const void* tokens, int size);
+
+
+
+
+struct DXBCHeader
+{
+ UInt32 fourcc;
+ UInt32 hash[4];
+ UInt32 one;
+ UInt32 total_size;
+ UInt32 chunk_count;
+};
+
+
+static inline DXBCChunkHeader* dxbc_find_shader_bytecode(const void* data, int size)
+{
+ DXBCChunkHeader* chunk;
+ chunk = dxbc_find_chunk(data, size, kFOURCC_SHDR);
+ if(!chunk)
+ chunk = dxbc_find_chunk(data, size, kFOURCC_SHEX);
+ return chunk;
+}
+
+#define DXBC_FIND_INPUT_SIGNATURE 0
+#define DXBC_FIND_OUTPUT_SIGNATURE 1
+#define DXBC_FIND_PATCH_SIGNATURE 2
+
+static inline DXBCChunkSig* dxbc_find_signature(const void* data, int size, unsigned kind)
+{
+ unsigned fourcc;
+ switch(kind) {
+ case DXBC_FIND_INPUT_SIGNATURE: fourcc = kFOURCC_ISGN; break;
+ case DXBC_FIND_OUTPUT_SIGNATURE: fourcc = kFOURCC_OSGN; break;
+ case DXBC_FIND_PATCH_SIGNATURE: fourcc = kFOURCC_PCSG; break;
+ default:
+ return NULL;
+ }
+ return (DXBCChunkSig*)dxbc_find_chunk(data, size, fourcc);
+}
+
+std::pair<void*, size_t> dxbc_assemble(struct DXBCChunkHeader** chunks, unsigned num_chunks);
+
+
+DXBCContainer* dxbc_parse(const void* data, int size)
+{
+ std::auto_ptr<DXBCContainer> container(new DXBCContainer());
+ container->data = data;
+ DXBCHeader* header = (DXBCHeader*)data;
+ UInt32* chunk_offsets = (UInt32*)(header + 1);
+ if(header->fourcc != kFOURCC_DXBC)
+ return 0;
+ unsigned num_chunks = header->chunk_count;
+ for(unsigned i = 0; i < num_chunks; ++i)
+ {
+ unsigned offset = chunk_offsets[i];
+ DXBCChunkHeader* chunk = (DXBCChunkHeader*)((char*)data + offset);
+ unsigned fourcc = chunk->fourcc;
+ container->chunks.push_back(chunk);
+ }
+ return container.release();
+}
+
+DXBCChunkHeader* dxbc_find_chunk(const void* data, int size, unsigned fourcc)
+{
+ DXBCHeader* header = (DXBCHeader*)data;
+ UInt32* chunk_offsets = (UInt32*)(header + 1);
+ if(header->fourcc != kFOURCC_DXBC)
+ return 0;
+ unsigned num_chunks = header->chunk_count;
+ for(unsigned i = 0; i < num_chunks; ++i)
+ {
+ unsigned offset = chunk_offsets[i];
+ DXBCChunkHeader* chunk = (DXBCChunkHeader*)((char*)data + offset);
+ if(chunk->fourcc == fourcc)
+ return chunk;
+ }
+ return 0;
+}
+
+static void print_binary_chunk (const DXBCChunkHeader& chk, int perLine = 16)
+{
+ const char* kHex = "0123456789abcdef";
+ const UInt8* ptr = ((const UInt8*)&chk) + sizeof(DXBCChunkHeader);
+ std::string res;
+ for (unsigned i = 0; i < chk.size; ++i)
+ {
+ if (i != 0 && i%perLine == 0)
+ res += '\n';
+ if ((i & 3) == 0)
+ res += ' ';
+ UInt8 b = ptr[i];
+ res += kHex[b>>4];
+ res += kHex[b&0xF];
+ }
+ printf_console ("%s\n", res.c_str());
+}
+
+static void print_sm4_program (const SM4Program& prog)
+{
+}
+
+void dxbc_print(const DXBCContainer* dxbc)
+{
+ printf_console ("DXBC dump:\n");
+ if (!dxbc)
+ {
+ printf_console ("null\n");
+ return;
+ }
+
+ printf_console ("chunk count: %d\n", (int)dxbc->chunks.size());
+ for (size_t i = 0; i < dxbc->chunks.size(); ++i)
+ {
+ const DXBCChunkHeader& chk = *dxbc->chunks[i];
+ printf_console ("chunk #%i: %c%c%c%c size %u\n", (int)i, chk.fourcc&0xFF, (chk.fourcc>>8)&0xFF, (chk.fourcc>>16)&0xFF, (chk.fourcc>>24)&0xFF, chk.size);
+ if (chk.fourcc == kFOURCC_ISGN || chk.fourcc == kFOURCC_OSGN)
+ {
+ print_binary_chunk (chk);
+ const DXBCChunkSig* sig = (const DXBCChunkSig*)&chk;
+ D3D11_SIGNATURE_PARAMETER_DESC* params;
+ int count = dxbc_parse_signature (sig, &params);
+ for (int j = 0; j < count; ++j)
+ {
+ const D3D11_SIGNATURE_PARAMETER_DESC& p = params[j];
+ printf_console (" #%i: %s/%u reg %u sv %u type %u mask %x rwmask %x stream %u\n",
+ j, p.SemanticName, p.SemanticIndex, p.Register, p.SystemValueType, p.ComponentType, p.Mask, p.ReadWriteMask, p.Stream);
+ }
+ free (params);
+ }
+ else if (chk.fourcc == kFOURCC_SHDR || chk.fourcc == kFOURCC_SHEX)
+ {
+ printf_console ("shader code:\n");
+ print_binary_chunk (chk);
+ SM4Program* prog = sm4_parse((&chk)+1, chk.size);
+ if (prog)
+ {
+ print_sm4_program(*prog);
+ delete prog;
+ }
+ }
+ }
+}
+
+
+int dxbc_parse_signature(const DXBCChunkSig* sig, D3D11_SIGNATURE_PARAMETER_DESC** params)
+{
+ unsigned count = sig->count;
+ *params = (D3D11_SIGNATURE_PARAMETER_DESC*)malloc(sizeof(D3D11_SIGNATURE_PARAMETER_DESC) * count);
+
+ for (unsigned i = 0; i < count; ++i)
+ {
+ D3D11_SIGNATURE_PARAMETER_DESC& param = (*params)[i];
+ param.SemanticName = (char*)&sig->count + sig->elements[i].name_offset;
+ param.SemanticIndex = sig->elements[i].semantic_index;
+ param.SystemValueType = (D3D_NAME)sig->elements[i].system_value_type;
+ param.ComponentType = (D3D_REGISTER_COMPONENT_TYPE)sig->elements[i].component_type;
+ param.Register = sig->elements[i].register_num;
+ param.Mask = sig->elements[i].mask;
+ param.ReadWriteMask = sig->elements[i].read_write_mask;
+ param.Stream = sig->elements[i].stream;
+ }
+ return count;
+}
+
+// sm4_parse.cpp
+
+struct SM4Parser
+{
+ const unsigned* tokens;
+ const unsigned* tokens_end;
+ SM4Program& program;
+
+ SM4Parser(SM4Program& program, const void* p_tokens, unsigned size)
+ : program(program)
+ {
+ tokens = (const unsigned*)p_tokens;
+ tokens_end = (const unsigned*)((const char*)p_tokens + size);
+ }
+
+ UInt32 read32()
+ {
+ Assert(tokens < tokens_end);
+ return *tokens++;
+ }
+
+ template<typename T>
+ void read_token(T* tok)
+ {
+ *(unsigned*)tok = read32();
+ }
+
+ UInt64 read64()
+ {
+ unsigned a = read32();
+ unsigned b = read32();
+ return (UInt64)a | ((UInt64)b << 32);
+ }
+
+ void skip(unsigned toskip)
+ {
+ tokens += toskip;
+ }
+
+ bool read_op(SM4Op* pop)
+ {
+ SM4Op& op = *pop;
+ SM4TokOperand optok;
+ read_token(&optok);
+ if (optok.file >= kSM4File_COUNT)
+ {
+ AssertString ("DXBC: unknown register type");
+ return false;
+ }
+ op.swizzle[0] = 0;
+ op.swizzle[1] = 1;
+ op.swizzle[2] = 2;
+ op.swizzle[3] = 3;
+ op.mask = 0xf;
+ switch(optok.comps_enum)
+ {
+ case kSM4OperComp0:
+ op.comps = 0;
+ break;
+ case kSM4OperComp1:
+ op.comps = 1;
+ op.swizzle[1] = op.swizzle[2] = op.swizzle[3] = 0;
+ break;
+ case kSM4OperComp4:
+ op.comps = 4;
+ op.mode = optok.mode;
+ switch(optok.mode)
+ {
+ case SM4_OPERAND_MODE_MASK:
+ op.mask = SM4_OPERAND_SEL_MASK(optok.sel);
+ break;
+ case SM4_OPERAND_MODE_SWIZZLE:
+ op.swizzle[0] = SM4_OPERAND_SEL_SWZ(optok.sel, 0);
+ op.swizzle[1] = SM4_OPERAND_SEL_SWZ(optok.sel, 1);
+ op.swizzle[2] = SM4_OPERAND_SEL_SWZ(optok.sel, 2);
+ op.swizzle[3] = SM4_OPERAND_SEL_SWZ(optok.sel, 3);
+ break;
+ case SM4_OPERAND_MODE_SCALAR:
+ op.swizzle[0] = op.swizzle[1] = op.swizzle[2] = op.swizzle[3] = SM4_OPERAND_SEL_SCALAR(optok.sel);
+ break;
+ }
+ break;
+ case kSM4OperCompN:
+ AssertString("Unhandled operand component type");
+ return false;
+ break;
+ }
+ op.file = (SM4RegFile)optok.file;
+ op.num_indices = optok.num_indices;
+
+ if(optok.extended)
+ {
+ SM4TokOperandEx optokext;
+ read_token(&optokext);
+ if(optokext.type == 0)
+ {}
+ else if(optokext.type == 1)
+ {
+ op.neg = optokext.neg;
+ op.abs= optokext.abs;
+ }
+ else
+ {
+ AssertString("Unhandled extended operand token type");
+ return false;
+ }
+ }
+
+ for(unsigned i = 0; i < op.num_indices; ++i)
+ {
+ unsigned repr;
+ if(i == 0)
+ repr = optok.index0_repr;
+ else if(i == 1)
+ repr = optok.index1_repr;
+ else if(i == 2)
+ repr = optok.index2_repr;
+ else
+ {
+ AssertString("Unhandled operand index representation");
+ return false;
+ }
+ op.indices[i].disp = 0;
+ // TODO: is disp supposed to be signed here??
+ switch(repr)
+ {
+ case SM4_OPERAND_INDEX_REPR_IMM32:
+ op.indices[i].disp = (SInt32)read32();
+ break;
+ case SM4_OPERAND_INDEX_REPR_IMM64:
+ op.indices[i].disp = read64();
+ break;
+ case SM4_OPERAND_INDEX_REPR_REG:
+relative:
+ op.indices[i].reg.reset(new SM4Op());
+ if (!read_op(&*op.indices[i].reg))
+ return false;
+ break;
+ case SM4_OPERAND_INDEX_REPR_REG_IMM32:
+ op.indices[i].disp = (SInt32)read32();
+ goto relative;
+ case SM4_OPERAND_INDEX_REPR_REG_IMM64:
+ op.indices[i].disp = read64();
+ goto relative;
+ }
+ }
+
+ if(op.file == kSM4File_IMMEDIATE32)
+ {
+ for(unsigned i = 0; i < op.comps; ++i)
+ op.imm_values[i].i32 = read32();
+ }
+ else if(op.file == kSM4File_IMMEDIATE64)
+ {
+ for(unsigned i = 0; i < op.comps; ++i)
+ op.imm_values[i].i64 = read64();
+ }
+ return true;
+ }
+
+ bool parse()
+ {
+ read_token(&program.version);
+
+ unsigned lentok = read32();
+ tokens_end = tokens - 2 + lentok;
+
+ while(tokens != tokens_end)
+ {
+ SM4TokInstruction insntok;
+ read_token(&insntok);
+ const unsigned* insn_end = tokens - 1 + insntok.length;
+ SM4Opcode opcode = (SM4Opcode)insntok.opcode;
+ if (opcode >= kSM4Op_COUNT)
+ {
+ AssertString ("Unknown DXBC opcode");
+ return false;
+ }
+
+ if(opcode == kSM4Op_CUSTOMDATA)
+ {
+ // immediate constant buffer data
+ unsigned customlen = read32() - 2;
+
+ SM4Decl& dcl = *new SM4Decl;
+ program.dcls.push_back(&dcl);
+
+ dcl.opcode = kSM4Op_CUSTOMDATA;
+ dcl.num = customlen;
+ dcl.data = malloc(customlen * sizeof(tokens[0]));
+
+ memcpy(dcl.data, &tokens[0], customlen * sizeof(tokens[0]));
+
+ skip(customlen);
+ continue;
+ }
+
+ if(opcode == kSM4Op_HS_FORK_PHASE || opcode == kSM4Op_HS_JOIN_PHASE)
+ {
+ // need to interleave these with the declarations or we cannot
+ // assign fork/join phase instance counts to phases
+ SM4Decl& dcl = *new SM4Decl;
+ program.dcls.push_back(&dcl);
+ dcl.opcode = opcode;
+ }
+
+ if((opcode >= kSM4Op_DCL_RESOURCE && opcode <= kSM4Op_DCL_GLOBAL_FLAGS)
+ || (opcode >= kSM4Op_DCL_STREAM && opcode <= kSM4Op_DCL_RESOURCE_STRUCTURED))
+ {
+ SM4Decl& dcl = *new SM4Decl;
+ program.dcls.push_back(&dcl);
+ (SM4TokInstruction&)dcl = insntok;
+
+ SM4TokInstructionEx exttok;
+ memcpy(&exttok, &insntok, sizeof(exttok));
+ while(exttok.extended)
+ {
+ read_token(&exttok);
+ }
+
+#define READ_OP_ANY dcl.op.reset(new SM4Op()); if (!read_op(&*dcl.op)) return false;
+#define READ_OP(FILE) READ_OP_ANY
+ //Assert(dcl.op->file == kSM4File_##FILE);
+
+ switch(opcode)
+ {
+ case kSM4Op_DCL_GLOBAL_FLAGS:
+ break;
+ case kSM4Op_DCL_RESOURCE:
+ READ_OP(RESOURCE);
+ read_token(&dcl.rrt);
+ break;
+ case kSM4Op_DCL_SAMPLER:
+ READ_OP(SAMPLER);
+ break;
+ case kSM4Op_DCL_INPUT:
+ case kSM4Op_DCL_INPUT_PS:
+ READ_OP(INPUT);
+ break;
+ case kSM4Op_DCL_INPUT_SIV:
+ case kSM4Op_DCL_INPUT_SGV:
+ case kSM4Op_DCL_INPUT_PS_SIV:
+ case kSM4Op_DCL_INPUT_PS_SGV:
+ READ_OP(INPUT);
+ dcl.sv = (SM4SystemValue)(UInt16)read32();
+ break;
+ case kSM4Op_DCL_OUTPUT:
+ READ_OP(OUTPUT);
+ break;
+ case kSM4Op_DCL_OUTPUT_SIV:
+ case kSM4Op_DCL_OUTPUT_SGV:
+ READ_OP(OUTPUT);
+ dcl.sv = (SM4SystemValue)(UInt16)read32();
+ break;
+ case kSM4Op_DCL_INDEX_RANGE:
+ READ_OP_ANY;
+ Assert(dcl.op->file == kSM4File_INPUT || dcl.op->file == kSM4File_OUTPUT);
+ dcl.num = read32();
+ break;
+ case kSM4Op_DCL_TEMPS:
+ dcl.num = read32();
+ break;
+ case kSM4Op_DCL_INDEXABLE_TEMP:
+ READ_OP(INDEXABLE_TEMP);
+ dcl.indexable_temp.num = read32();
+ dcl.indexable_temp.comps = read32();
+ break;
+ case kSM4Op_DCL_CONSTANT_BUFFER:
+ READ_OP(CONSTANT_BUFFER);
+ break;
+ case kSM4Op_DCL_GS_INPUT_PRIMITIVE:
+ case kSM4Op_DCL_GS_OUTPUT_PRIMITIVE_TOPOLOGY:
+ break;
+ case kSM4Op_DCL_MAX_OUTPUT_VERTEX_COUNT:
+ dcl.num = read32();
+ break;
+ case kSM4Op_DCL_GS_INSTANCE_COUNT:
+ dcl.num = read32();
+ break;
+ case kSM4Op_DCL_INPUT_CONTROL_POINT_COUNT:
+ case kSM4Op_DCL_OUTPUT_CONTROL_POINT_COUNT:
+ case kSM4Op_DCL_TESS_DOMAIN:
+ case kSM4Op_DCL_TESS_PARTITIONING:
+ case kSM4Op_DCL_TESS_OUTPUT_PRIMITIVE:
+ break;
+ case kSM4Op_DCL_HS_MAX_TESSFACTOR:
+ dcl.f32 = read32();
+ break;
+ case kSM4Op_DCL_HS_FORK_PHASE_INSTANCE_COUNT:
+ dcl.num = read32();
+ break;
+ case kSM4Op_DCL_FUNCTION_BODY:
+ dcl.num = read32();
+ break;
+ case kSM4Op_DCL_FUNCTION_TABLE:
+ dcl.num = read32();
+ dcl.data = malloc(dcl.num * sizeof(UInt32));
+ for(unsigned i = 0; i < dcl.num; ++i)
+ ((UInt32*)dcl.data)[i] = read32();
+ break;
+ case kSM4Op_DCL_INTERFACE:
+ dcl.intf.id = read32();
+ dcl.intf.expected_function_table_length = read32();
+ {
+ UInt32 v = read32();
+ dcl.intf.table_length = v & 0xffff;
+ dcl.intf.array_length = v >> 16;
+ }
+ dcl.data = malloc(dcl.intf.table_length * sizeof(UInt32));
+ for(unsigned i = 0; i < dcl.intf.table_length; ++i)
+ ((UInt32*)dcl.data)[i] = read32();
+ break;
+ case kSM4Op_DCL_THREAD_GROUP:
+ dcl.thread_group_size[0] = read32();
+ dcl.thread_group_size[1] = read32();
+ dcl.thread_group_size[2] = read32();
+ break;
+ case kSM4Op_DCL_UNORDERED_ACCESS_VIEW_TYPED:
+ READ_OP(UNORDERED_ACCESS_VIEW);
+ read_token(&dcl.rrt);
+ break;
+ case kSM4Op_DCL_UNORDERED_ACCESS_VIEW_RAW:
+ READ_OP(UNORDERED_ACCESS_VIEW);
+ break;
+ case kSM4Op_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED:
+ READ_OP(UNORDERED_ACCESS_VIEW);
+ dcl.structured.stride = read32();
+ break;
+ case kSM4Op_DCL_THREAD_GROUP_SHARED_MEMORY_RAW:
+ READ_OP(THREAD_GROUP_SHARED_MEMORY);
+ dcl.num = read32();
+ break;
+ case kSM4Op_DCL_THREAD_GROUP_SHARED_MEMORY_STRUCTURED:
+ READ_OP(THREAD_GROUP_SHARED_MEMORY);
+ dcl.structured.stride = read32();
+ dcl.structured.count = read32();
+ break;
+ case kSM4Op_DCL_RESOURCE_RAW:
+ READ_OP(RESOURCE);
+ break;
+ case kSM4Op_DCL_RESOURCE_STRUCTURED:
+ READ_OP(RESOURCE);
+ dcl.structured.stride = read32();
+ break;
+ case kSM4Op_DCL_STREAM:
+ // TODO: dcl_stream is undocumented: what is it?
+ AssertString("DXBC: Unhandled dcl_stream since it's undocumented");
+ return false;
+ break;
+ default:
+ AssertString("DXBC: Unhandled declaration type");
+ return false;
+ }
+
+ if (tokens != insn_end)
+ {
+ AssertString("DXBC: token size mismatch");
+ return false;
+ }
+ }
+ else
+ {
+ SM4Instr& insn = *new SM4Instr;
+ program.insns.push_back(&insn);
+ (SM4TokInstruction&)insn = insntok;
+
+ SM4TokInstructionEx exttok;
+ memcpy(&exttok, &insntok, sizeof(exttok));
+ while(exttok.extended)
+ {
+ read_token(&exttok);
+ if(exttok.type == SM4_TOKEN_INSTRUCTION_EXTENDED_TYPE_SAMPLE_CONTROLS)
+ {
+ insn.sample_offset[0] = exttok.sample_controls.offset_u;
+ insn.sample_offset[1] = exttok.sample_controls.offset_v;
+ insn.sample_offset[2] = exttok.sample_controls.offset_w;
+ }
+ else if(exttok.type == SM4_TOKEN_INSTRUCTION_EXTENDED_TYPE_RESOURCE_DIM)
+ insn.resource_target = exttok.resource_target.target;
+ else if(exttok.type == SM4_TOKEN_INSTRUCTION_EXTENDED_TYPE_RESOURCE_RETURN_TYPE)
+ {
+ insn.resource_return_type[0] = exttok.resource_return_type.x;
+ insn.resource_return_type[1] = exttok.resource_return_type.y;
+ insn.resource_return_type[2] = exttok.resource_return_type.z;
+ insn.resource_return_type[3] = exttok.resource_return_type.w;
+ }
+ }
+
+ switch(opcode)
+ {
+ case kSM4Op_INTERFACE_CALL:
+ insn.num = read32();
+ break;
+ default:
+ break;
+ }
+
+ unsigned op_num = 0;
+ while(tokens != insn_end)
+ {
+ if (tokens >= insn_end)
+ {
+ AssertString ("DXBC: token size mismatch");
+ return false;
+ }
+ if (op_num >= SM4_MAX_OPS)
+ {
+ AssertString ("DXBC: too many operands");
+ return false;
+ }
+ insn.ops[op_num].reset(new SM4Op);
+ if (!read_op(&*insn.ops[op_num]))
+ return false;
+ ++op_num;
+ }
+ insn.num_ops = op_num;
+ }
+ }
+ return true;
+ }
+};
+
+SM4Program* sm4_parse(const void* tokens, int size)
+{
+ SM4Program* program = new SM4Program;
+ SM4Parser parser(*program, tokens, size);
+ if(parser.parse())
+ return program;
+ delete program;
+ return 0;
+}
+
+static void dxbc_create_internal(struct DXBCChunkHeader** chunks, unsigned num_chunks, void* buffer, unsigned total_size)
+{
+ DXBCHeader* header = (DXBCHeader*)buffer;
+
+ header->fourcc = kFOURCC_DXBC;
+ memset(header->hash, 0, sizeof(header->hash));
+
+ header->one = 1;
+ header->total_size = total_size;
+ header->chunk_count = num_chunks;
+
+ UInt32* chunk_offsets = (UInt32*)(header + 1);
+ UInt32 off = sizeof(struct DXBCHeader) + num_chunks * sizeof(UInt32);
+ for(unsigned i = 0; i < num_chunks; ++i)
+ {
+ chunk_offsets[i] = off;
+ unsigned chunk_full_size = sizeof(DXBCChunkHeader) + chunks[i]->size;
+ memcpy((char*)header + off, chunks[i], chunk_full_size);
+ off += chunk_full_size;
+ }
+
+ void D3DHash (const unsigned char* data, unsigned size, unsigned char res[16]);
+ D3DHash ((const UInt8*)&header->one, total_size-20, (UInt8*)header->hash);
+}
+
+
+static std::pair<void*, size_t> dxbc_create(struct DXBCChunkHeader** chunks, unsigned num_chunks)
+{
+ size_t data_size = 0;
+ for(unsigned i = 0; i < num_chunks; ++i)
+ data_size += sizeof(UInt32) + sizeof(DXBCChunkHeader) + chunks[i]->size;
+ const size_t total_size = sizeof(DXBCHeader) + data_size;
+
+ void* buffer = malloc(total_size);
+ if (!buffer)
+ return std::make_pair((void*)0, 0);
+
+ dxbc_create_internal (chunks, num_chunks, buffer, total_size);
+
+ return std::make_pair(buffer, total_size);
+}
+
+
+void dxbc_create(struct DXBCChunkHeader** chunks, unsigned num_chunks, dynamic_array<UInt8>& out)
+{
+ size_t data_size = 0;
+ for(unsigned i = 0; i < num_chunks; ++i)
+ data_size += sizeof(UInt32) + sizeof(DXBCChunkHeader) + chunks[i]->size;
+ const size_t total_size = sizeof(DXBCHeader) + data_size;
+
+ out.resize_uninitialized (total_size);
+
+ dxbc_create_internal (chunks, num_chunks, out.data(), total_size);
+}
+
+
+
+
+// -------------------------------------------------------------------
+
+enum DXBCBuilderChunks {
+ kBuilderChunkInput = 0,
+ kBuilderChunkOutput,
+ kBuilderChunkCode,
+ kBuilderChunkSM20,
+ kBuilderChunkCount
+};
+struct SigElement {
+ const char* name;
+ int index;
+ int reg;
+ int mask;
+};
+
+struct DXBCCodeBuilder
+{
+ DXBCCodeBuilder(dynamic_array<UInt32>& destArray)
+ : insns(destArray)
+ , curInsnIndex(-1)
+ , tempCount(0)
+ , opcode(kSM4Op_MOV)
+ , opcode2(kSM2Op_MOV)
+ , saturate(0)
+ { }
+ dynamic_array<UInt32>& insns; // reference to actual output array!
+ int curInsnIndex;
+ int tempCount;
+
+ SM4Opcode opcode;
+ SM2Opcode opcode2;
+
+ int saturate;
+ char dstRegType;
+ int dstRegIndex;
+ unsigned dstRegMask;
+ unsigned dstRegComps;
+ int tmpSatRegIndex;
+};
+
+struct DXBCBuilder
+{
+ DXBCBuilder() : codeBuilder(insns) { }
+ DXBCCodeBuilder codeBuilder;
+ dynamic_array<SigElement> inputs;
+ dynamic_array<SigElement> outputs;
+ dynamic_array<UInt32> insns;
+ dynamic_array<UInt32> dcls;
+ //sm20 stuff
+ dynamic_array<UInt32> inputs2;
+ dynamic_array<UInt32> defs2;
+ dynamic_array<UInt32> insns2;
+ std::map<UInt32,UInt32> outputMap;
+ std::map<UInt32,UInt32> inputMap;
+
+ int numTextures2;
+ int curInsnIndex2;
+
+ SM4TokVersion version;
+ DXBCChunkHeader* chunks[kBuilderChunkCount];
+};
+
+DXBCCodeBuilder* dxb_create_code(dynamic_array<UInt32>& destArray)
+{
+ DXBCCodeBuilder* b = new DXBCCodeBuilder(destArray);
+ return b;
+}
+
+void dxb_destroy_code(DXBCCodeBuilder* b)
+{
+ delete b;
+}
+
+const UInt32* dxb_get_code(DXBCCodeBuilder* b, size_t* outSize)
+{
+ *outSize = b->insns.size();
+ return b->insns.data();
+}
+
+
+DXBCBuilder* dxb_create(int major, int minor, SM4ShaderType type)
+{
+ DXBCBuilder* b = new DXBCBuilder();
+ memset (b->chunks, 0, sizeof(b->chunks));
+ b->version.major = major;
+ b->version.minor = minor;
+ b->version.type = type;
+ b->version.format = 0; //@TODO?
+ b->numTextures2 = 0;
+
+ return b;
+}
+void dxb_destroy(DXBCBuilder* b)
+{
+ if (b)
+ {
+ for (int i = 0; i < kBuilderChunkCount; ++i)
+ if (b->chunks[i])
+ free(b->chunks[i]);
+ }
+ delete b;
+}
+
+DXBCCodeBuilder* dxb_get_code_builder(DXBCBuilder* b)
+{
+ return &b->codeBuilder;
+}
+
+
+void dxb_dcl_input (DXBCBuilder* b, const char* name, int index, int reg, int mask)
+{
+ SigElement el;
+ el.name = name;
+ el.index = index;
+ el.reg = reg;
+ el.mask = mask;
+ b->inputs.push_back (el);
+
+ dxb_dcl_input2(b,name,index,reg,mask);
+}
+
+void dxb_dcl_output (DXBCBuilder* b, const char* name, int index, int reg, int mask)
+{
+ SigElement el;
+ el.name = name;
+ el.index = index;
+ el.reg = reg;
+ el.mask = mask;
+ b->outputs.push_back (el);
+
+ dxb_dcl_output2(b,name,index,reg,mask);
+}
+
+
+void dxb_dcl_tex (DXBCBuilder* b, int index, SM4Target dim)
+{
+ SM4TokInstruction dcl;
+ dcl.dword = 0;
+ dcl.opcode = kSM4Op_DCL_SAMPLER;
+ dcl.length = 3;
+ b->dcls.push_back (dcl.dword);
+ SM4TokOperand op;
+ op.dword = 0;
+ op.file = kSM4File_SAMPLER;
+ op.num_indices = 1;
+ b->dcls.push_back (op.dword);
+ b->dcls.push_back (index);
+
+ dcl.dword = 0;
+ dcl.opcode = kSM4Op_DCL_RESOURCE;
+ dcl.length = 4;
+ dcl.dcl_resource.target = dim;
+ b->dcls.push_back (dcl.dword);
+ op.dword = 0;
+ op.file = kSM4File_RESOURCE;
+ op.num_indices = 1;
+ SM4TokResourceRetType ret;
+ ret.dword = 0;
+ ret.x = ret.y = ret.z = ret.w = kSM4RetType_FLOAT;
+ b->dcls.push_back (op.dword);
+ b->dcls.push_back (index);
+ b->dcls.push_back (ret.dword);
+
+ dxb_dcl_tex2 (b, index, dim);
+}
+
+
+void dxb_dcl_cb (DXBCBuilder* b, int index, int size)
+{
+ SM4TokInstruction dcl;
+ dcl.dword = 0;
+ dcl.opcode = kSM4Op_DCL_CONSTANT_BUFFER;
+ dcl.length = 4;
+ dcl.dcl_constant_buffer.dynamic = 0;
+ b->dcls.push_back (dcl.dword);
+ SM4TokOperand op;
+ op.dword = 0;
+ op.comps_enum = kSM4OperComp4;
+ op.file = kSM4File_CONSTANT_BUFFER;
+ op.num_indices = 2;
+ b->dcls.push_back (op.dword);
+ b->dcls.push_back (index);
+ b->dcls.push_back (size);
+}
+
+
+static void dxbc_update_insn_length (DXBCCodeBuilder* b, int len)
+{
+ DebugAssert (b->curInsnIndex >= 0 && b->curInsnIndex < b->insns.size());
+ SM4TokInstruction* tok = (SM4TokInstruction*)&b->insns[b->curInsnIndex];
+ tok->length += len;
+}
+
+void dxb_op (DXBCCodeBuilder* b, SM4Opcode op, bool sat)
+{
+ b->opcode = op;
+ b->curInsnIndex = b->insns.size();
+ SM4TokInstruction tok;
+ tok.dword = 0;
+ tok.opcode = op;
+ tok.insn.sat = sat ? 1 : 0;
+ tok.length = 1;
+ if (op == kSM4Op_DISCARD) // discard instructions emitted by HLSL have nz flag set
+ tok.insn.test_nz = 1;
+ b->insns.push_back (tok.dword);
+}
+
+static inline SM4RegFile dxb_reg_type(char c)
+{
+ switch (c)
+ {
+ case 'r': return kSM4File_TEMP;
+ case 'v': return kSM4File_INPUT;
+ case 'o': return kSM4File_OUTPUT;
+ case 'c': return kSM4File_CONSTANT_BUFFER;
+ case 's': return kSM4File_SAMPLER;
+ case 't': return kSM4File_RESOURCE;
+ default: AssertString("unknown register type"); return kSM4File_TEMP;
+ }
+}
+
+
+void dxb_reg (DXBCCodeBuilder* b, char rchar, int reg, unsigned mask)
+{
+ b->dstRegMask = mask;
+ b->dstRegComps = BitsInMask(mask);
+
+ SM4RegFile rtype = dxb_reg_type(rchar);
+ dxbc_update_insn_length (b, 2);
+ SM4TokOperand op;
+ op.dword = 0;
+ op.comps_enum = rtype==kSM4File_SAMPLER ? kSM4OperComp0 : kSM4OperComp4;
+
+ // discard only has 1 parameter which is similar to a source register argument:
+ // if it's one channel only, then emit it as a "scalar channel index"
+ op.mode = (b->dstRegComps==1 && b->opcode==kSM4Op_DISCARD) ? SM4_OPERAND_MODE_SCALAR : SM4_OPERAND_MODE_MASK;
+ op.sel = (b->dstRegComps==1 && b->opcode==kSM4Op_DISCARD) ? HighestBit(mask) : mask;
+ if (rtype==kSM4File_SAMPLER)
+ op.sel = 0;
+ op.file = rtype;
+ op.num_indices = 1;
+ b->insns.push_back (op.dword);
+ b->insns.push_back (reg);
+ if (rtype == kSM4File_TEMP)
+ b->tempCount = std::max (b->tempCount, reg+1);
+}
+
+
+static unsigned adjust_swizzle (DXBCCodeBuilder* b, unsigned swizzle, bool sm2)
+{
+ // For DP3 with "no swizzle", SM2 expects to not have the swizzle, while SM4 expects xyzx
+ if (b->opcode == kSM4Op_DP3 && swizzle == kSM4SwzNone && !sm2)
+ swizzle = kSM4SwzXYZX;
+
+ // arguments for single-channel destinations in SM4 should be emitted as "1 channel index"
+ // instead of "swizzle that replicates all channels"
+ if (!sm2 && b->dstRegComps==1)
+ {
+ if (swizzle == kSM4SwzRepX)
+ swizzle = 0;
+ else if (swizzle == kSM4SwzRepY)
+ swizzle = 1;
+ else if (swizzle == kSM4SwzRepZ)
+ swizzle = 2;
+ else if (swizzle == kSM4SwzRepW)
+ swizzle = 3;
+ }
+ return swizzle;
+}
+
+
+void dxb_swz (DXBCCodeBuilder* b, char rchar, int reg, unsigned swizzle, bool neg)
+{
+ swizzle = adjust_swizzle (b, swizzle, false);
+
+ SM4RegFile rtype = dxb_reg_type(rchar);
+ int len = 2;
+ if (neg) ++len;
+ if (rtype==kSM4File_CONSTANT_BUFFER) ++len;
+ dxbc_update_insn_length (b, len);
+ SM4TokOperand op;
+ op.dword = 0;
+ op.comps_enum = rtype==kSM4File_SAMPLER ? kSM4OperComp0 : kSM4OperComp4;
+ op.mode = (b->dstRegComps==1 && swizzle<4) ? SM4_OPERAND_MODE_SCALAR : SM4_OPERAND_MODE_SWIZZLE;
+ op.sel = swizzle;
+ op.file = rtype;
+ op.num_indices = rtype==kSM4File_CONSTANT_BUFFER ? 2 : 1;
+ op.extended = neg ? 1 : 0;
+ b->insns.push_back (op.dword);
+ if (neg)
+ {
+ SM4TokOperandEx opex;
+ opex.dword = 0;
+ opex.type = 1;
+ opex.neg = 1;
+ b->insns.push_back (opex.dword);
+ }
+ if (rtype==kSM4File_CONSTANT_BUFFER)
+ {
+ b->insns.push_back (reg >> 16);
+ b->insns.push_back (reg & 0xFFFF);
+ }
+ else
+ {
+ b->insns.push_back (reg);
+ }
+ if (rtype == kSM4File_TEMP)
+ b->tempCount = std::max (b->tempCount, reg+1);
+}
+
+void dxb_float1 (DXBCCodeBuilder* b, float v)
+{
+ dxbc_update_insn_length (b, 2);
+ SM4TokOperand op;
+ op.dword = 0;
+ op.comps_enum = kSM4OperComp1;
+ op.file = kSM4File_IMMEDIATE32;
+ b->insns.push_back (op.dword);
+ union { float f; UInt32 i; } f2i;
+ f2i.f = v;
+ b->insns.push_back (f2i.i);
+}
+
+void dxb_int1 (DXBCCodeBuilder* b, int i)
+{
+ dxbc_update_insn_length (b, 2);
+ SM4TokOperand op;
+ op.dword = 0;
+ op.comps_enum = kSM4OperComp1;
+ op.file = kSM4File_IMMEDIATE32;
+ b->insns.push_back (op.dword);
+ b->insns.push_back (i);
+}
+
+
+void dxb_float4 (DXBCCodeBuilder* b, float v0, float v1, float v2, float v3)
+{
+ dxbc_update_insn_length (b, 5);
+ SM4TokOperand op;
+ op.dword = 0;
+ op.comps_enum = kSM4OperComp4;
+ op.file = kSM4File_IMMEDIATE32;
+ b->insns.push_back (op.dword);
+ union { float f; UInt32 i; } f2i;
+ f2i.f = v0; b->insns.push_back (f2i.i);
+ f2i.f = v1; b->insns.push_back (f2i.i);
+ f2i.f = v2; b->insns.push_back (f2i.i);
+ f2i.f = v3; b->insns.push_back (f2i.i);
+}
+
+
+
+static void dxbc_builder_build_sig (DXBCBuilder* b, bool input)
+{
+ int chunkIdx = input ? kBuilderChunkInput : kBuilderChunkOutput;
+ const dynamic_array<SigElement>& sigs = input ? b->inputs : b->outputs;
+ const int nsigs = sigs.size();
+
+ // size = 8 (header) + 8 (sig chunk header) + count * DXBCSignatureElement + names
+ unsigned size = 16 + nsigs * sizeof(DXBCSignatureElement);
+ unsigned namesOffset = size;
+ for (int i = 0; i < nsigs; ++i)
+ size += strlen(sigs[i].name) + 1;
+ size = (size + 3) & ~3; // align to next dword
+
+ UInt8* buf = (UInt8*)malloc(size);
+ DXBCChunkSig* chunk = (DXBCChunkSig*)buf;
+ memset (chunk, 0xAB, size);
+
+ chunk->fourcc = input ? kFOURCC_ISGN : kFOURCC_OSGN;
+ chunk->size = size-8;
+ chunk->count = nsigs;
+ chunk->unk8 = 8;
+ for (int i = 0; i < nsigs; ++i)
+ {
+ DXBCSignatureElement* s = &chunk->elements[i];
+ s->name_offset = namesOffset-8;
+ int len = strlen(sigs[i].name) + 1;
+ memcpy (buf + namesOffset, sigs[i].name, len);
+ namesOffset += len;
+ const bool outputPos = !strcmp(sigs[i].name,"SV_POSITION");
+ unsigned mask = sigs[i].mask;
+ s->semantic_index = sigs[i].index;
+ s->system_value_type = outputPos ? 1 : 0;
+ s->component_type = 3; // float
+ s->register_num = sigs[i].reg;
+ s->mask = mask; // mask of channels used
+ if (input)
+ s->read_write_mask = mask;
+ else
+ s->read_write_mask = ~mask & 0xF;
+ s->stream = 0;
+ s->unused = 0;
+
+
+ SM4TokInstruction dcl;
+ dcl.dword = 0;
+ dcl.opcode = input ?
+ (b->version.type == kSM4Shader_Pixel ? kSM4Op_DCL_INPUT_PS : kSM4Op_DCL_INPUT) :
+ (outputPos ? kSM4Op_DCL_OUTPUT_SIV : kSM4Op_DCL_OUTPUT);
+ dcl.length = outputPos ? 4 : 3;
+ if (dcl.opcode == kSM4Op_DCL_INPUT_PS)
+ dcl.dcl_input_ps.interpolation = kSM4Interp_LINEAR;
+ b->dcls.push_back (dcl.dword);
+ SM4TokOperand op;
+ op.dword = 0;
+ op.comps_enum = kSM4OperComp4;
+ op.mode = SM4_OPERAND_MODE_MASK;
+ op.sel = mask;
+ op.file = input ? kSM4File_INPUT : kSM4File_OUTPUT;
+ op.num_indices = 1;
+ op.index0_repr = SM4_OPERAND_INDEX_REPR_IMM32;
+ b->dcls.push_back (op.dword);
+ b->dcls.push_back (sigs[i].reg);
+ if (outputPos)
+ b->dcls.push_back (s->system_value_type);
+ }
+
+ b->chunks[chunkIdx] = chunk;
+}
+
+
+static void dxbc_builder_build_code (DXBCBuilder* b)
+{
+ const unsigned n = b->dcls.size() + b->insns.size();
+
+ // size = 8 (header) + 8 (code chunk header) + tokens
+ unsigned size = 16 + n*4;
+
+ UInt8* buf = (UInt8*)malloc(size);
+ DXBCChunkCode* chunk = (DXBCChunkCode*)buf;
+
+ chunk->fourcc = kFOURCC_SHDR;
+ chunk->size = size-8;
+ chunk->version = *(UInt32*)&b->version;
+ chunk->length = n+2;
+
+ UInt8* codePtr = (UInt8*)(chunk+1);
+ if (!b->dcls.empty())
+ {
+ size_t size = b->dcls.size()*sizeof(b->dcls[0]);
+ memcpy (codePtr, &b->dcls[0], size);
+ codePtr += size;
+ }
+ if (!b->insns.empty())
+ {
+ size_t size = b->insns.size()*sizeof(b->insns[0]);
+ memcpy (codePtr, &b->insns[0], size);
+ codePtr += size;
+ }
+ DebugAssert (buf + size == codePtr);
+
+ b->chunks[kBuilderChunkCode] = chunk;
+}
+
+void dxbc_builder_build_code2 (DXBCBuilder* b);
+
+void* dxb_build (DXBCBuilder* b, size_t& outSize)
+{
+ dxbc_builder_build_sig (b, true);
+ dxbc_builder_build_sig (b, false);
+ if (b->codeBuilder.tempCount > 0)
+ {
+ SM4TokInstruction tok;
+ tok.dword = 0;
+ tok.opcode = kSM4Op_DCL_TEMPS;
+ tok.length = 2;
+ b->dcls.push_back (tok.dword);
+ b->dcls.push_back (b->codeBuilder.tempCount);
+ }
+ dxbc_builder_build_code (b);
+ dxbc_builder_build_code2(b);
+
+ std::pair<void*,size_t> dxbc = dxbc_create (b->chunks, kBuilderChunkCount);
+ outSize = dxbc.second;
+
+ return dxbc.first;
+}
+
+
+
+//------------------------------------------------------------------------------------
+//SM 2.0 stuff
+
+
+//table format was reversed and is still not 100% known
+static dynamic_array<UInt32> dxbc_builder_build_mapping_table2 (DXBCBuilder* b)
+{
+ const unsigned shader_size = b->inputs2.size() + b->insns2.size() + b->defs2.size() + 1;
+ dynamic_array<UInt32> table;
+
+ const unsigned rmap_offset = 0x24;
+ const unsigned cmap_offset = rmap_offset + b->numTextures2*4;
+ const unsigned imap_offset = cmap_offset + 3*4;
+ const unsigned shdr_offset = imap_offset + (b->version.type == kSM4Shader_Vertex ? 4 : 0);
+
+ unsigned t = b->version.type == kSM4Shader_Pixel ? 4 : 0;
+ table.push_back(b->version.type == kSM4Shader_Pixel ? 0xffff0200 : 0xfffe0200);
+ table.push_back(shader_size*4);
+ table.push_back(shdr_offset);
+ table.push_back(1|(cmap_offset<<16));
+ table.push_back(shdr_offset<<16);
+ table.push_back(shdr_offset<<16);
+ table.push_back(b->numTextures2|(rmap_offset<<16));
+ table.push_back((b->version.type == kSM4Shader_Pixel ? 0 : 1)|(imap_offset<<16));
+
+ // Sampler mapping: 16-23 bits target sampler, 8-15 bits source sampler, 0-7 bits source resource
+ for (int n = 0;n < b->numTextures2;n++)
+ table.push_back(n|(n<<8)|(n<<16));
+
+ //constant to CB mapping
+ //CBn:CBoff; always zero
+ table.push_back(0);
+ //REGnum:REGn
+ table.push_back(b->version.type == kSM4Shader_Pixel ? 10 : 63); //k11VertexSize or k11PixelSize
+ //convert mode 8:8:8:8; always zero
+ table.push_back(0);
+
+ //input mapping; always one constant for vertex shader with pixel offset: oPos.xy += offset.xy * oPos.w;
+ if (b->version.type == kSM4Shader_Vertex)
+ table.push_back(64<<16); //k11VertexPosOffset9x
+
+ return table;
+}
+
+
+static unsigned swizzle_from_mask (unsigned mask)
+{
+ if (mask == 1)
+ return kSM4SwzRepX;
+ if (mask == 2)
+ return kSM4SwzRepY;
+ if (mask == 4)
+ return kSM4SwzRepZ;
+ if (mask == 8)
+ return kSM4SwzRepW;
+ //@TODO: more?
+ return kSM4SwzNone;
+}
+
+static void dxb_handle_saturate2 (DXBCBuilder* b)
+{
+ if (!b->codeBuilder.saturate || b->version.type == kSM4Shader_Pixel)
+ return;
+
+ // previous instruction had saturate on destination, so insert manual min/max instructions
+ const int tmpReg = b->codeBuilder.tmpSatRegIndex;
+ DebugAssert(tmpReg >= 0);
+ b->codeBuilder.saturate = 0;
+
+ const int constReg = dxb_imm_f4 (b, 0, 1, 0, 0);
+ const unsigned mask = b->codeBuilder.dstRegMask;
+ const unsigned swz = swizzle_from_mask(mask);
+
+ dxb_op2(b, kSM2Op_MAX, false);
+ dxb_reg2(b,'r',tmpReg,mask);
+ dxb_swz2(b,'r',tmpReg,swz);
+ dxb_swz2(b,'c',constReg,kSM4SwzRepX);
+ dxb_op2(b, kSM2Op_MIN, false);
+ dxb_reg2(b,b->codeBuilder.dstRegType,b->codeBuilder.dstRegIndex,mask);
+ dxb_swz2(b,'r',tmpReg,swz);
+ dxb_swz2(b,'c',constReg,kSM4SwzRepY);
+}
+
+static void dxbc_builder_build_code2 (DXBCBuilder* b)
+{
+ dxb_handle_saturate2(b); // handle possible saturate on the last instruction
+
+ dynamic_array<UInt32> table = dxbc_builder_build_mapping_table2(b);
+
+ const unsigned shader_size = b->inputs2.size() + b->insns2.size() + b->defs2.size() + 1;
+
+ // size = 8 (header) + 8 (code chunk header) + tokens
+ unsigned size = 12 + 4*table.size() + shader_size*4;
+
+ UInt8* buf = (UInt8*)malloc(size);
+ DXBCChunkSM20* chunk = (DXBCChunkSM20*)buf;
+
+ chunk->fourcc = kFOURCC_SM20;
+ chunk->size = size - 8;
+ chunk->length = chunk->size;
+
+ UInt8* codePtr = (UInt8*)(chunk+1);
+ if (!table.empty())
+ {
+ size_t size = table.size()*sizeof(table[0]);
+ memcpy (codePtr, &table[0], size);
+ codePtr += size;
+ }
+
+ *(UInt32*)codePtr = b->version.type == kSM4Shader_Pixel ? 0xffff0201 : 0xfffe0201;
+ codePtr += 4;
+
+ if (!b->defs2.empty())
+ {
+ size_t size = b->defs2.size()*sizeof(b->defs2[0]);
+ memcpy (codePtr, &b->defs2[0], size);
+ codePtr += size;
+ }
+ if (!b->inputs2.empty())
+ {
+ size_t size = b->inputs2.size()*sizeof(b->inputs2[0]);
+ memcpy (codePtr, &b->inputs2[0], size);
+ codePtr += size;
+ }
+ if (!b->insns2.empty())
+ {
+ size_t size = b->insns2.size()*sizeof(b->insns2[0]);
+ memcpy (codePtr, &b->insns2[0], size);
+ codePtr += size;
+ }
+
+ b->chunks[kBuilderChunkSM20] = chunk;
+}
+
+
+struct SM2TokSrc
+{
+ union {
+ UInt32 dword;
+ struct {
+ unsigned reg_num : 11;
+ unsigned reg_type34 : 2;
+ unsigned _res0 : 1;
+ unsigned _res1 : 2;
+ unsigned swizzle : 8;
+ unsigned src_mod : 4;
+ unsigned reg_type02 : 3;
+ unsigned _one : 1;
+ };
+ };
+};
+
+static inline UInt32 dxb_reg_split2(UInt32 n)
+{
+ return ((n<<D3DSP_REGTYPE_SHIFT)&D3DSP_REGTYPE_MASK)|((n<<(D3DSP_REGTYPE_SHIFT2))&D3DSP_REGTYPE_MASK2);
+}
+
+static inline UInt32 dxb_reg_type2(char c)
+{
+ switch (c)
+ {
+ case 'r': return D3DSPR_TEMP;
+ case 'v': return D3DSPR_INPUT;
+ case 'c': return D3DSPR_CONST;
+ //should be D3DSPR_TEXTURE, however, I'm replacing
+ //t swizzle with sampler source and removing s source reg
+ //to convert sm40 sample to sm20 texld on-fly
+ case 't': return D3DSPR_SAMPLER;
+ // case 's': return D3DSPR_SAMPLER;
+
+ case 'x': return D3DSPR_RASTOUT;
+ case 'y': return D3DSPR_TEXCRDOUT;
+
+ case 'o': return D3DSPR_RASTOUT;
+
+ default: AssertString("unknown register type"); return D3DSPR_TEMP;
+ }
+}
+
+static void dxbc_update_insn_length2 (DXBCBuilder* b, int len)
+{
+ DebugAssert (b->curInsnIndex2 >= 0 && b->curInsnIndex2 < b->insns2.size());
+ SM2TokInstruction* tok = (SM2TokInstruction*)&b->insns2[b->curInsnIndex2];
+ tok->length += len;
+}
+
+struct SNameToID
+{
+ const char* name;
+ UInt32 id;
+};
+
+
+//SM4.0 semantic to SM2.0 VS usage mapping
+static const SNameToID s_VSDeclNames[] =
+{
+ {"POSITION", D3DDECLUSAGE_POSITION},
+ {"NORMAL", D3DDECLUSAGE_NORMAL},
+ {"TEXCOORD", D3DDECLUSAGE_TEXCOORD},
+ {"COLOR", D3DDECLUSAGE_COLOR},
+ {"FOG", D3DDECLUSAGE_FOG},
+
+ {"SV_POSITION", 0},
+
+ {NULL, 0},
+};
+
+
+//SM4.0 semantic to SM2.0 PS register file mapping
+static const SNameToID s_PSDeclNames[] =
+{
+ {"TEXCOORD", D3DSPR_TEXTURE},
+ {"COLOR", D3DSPR_INPUT},
+
+ {"SV_Target", D3DSPR_COLOROUT},
+
+ {NULL, 0},
+};
+
+
+//SM4.0 semantic to SM2.0 register file mapping
+static const SNameToID s_OutNames[] =
+{
+ {"POSITION", D3DSPR_RASTOUT},
+ {"SV_POSITION", D3DSPR_RASTOUT},
+ {"FOG", D3DSPR_RASTOUT},
+ {"TEXCOORD", D3DSPR_TEXCRDOUT},
+ {"COLOR", D3DSPR_ATTROUT},
+
+ {"SV_Target", D3DSPR_COLOROUT},
+
+ {NULL, 0},
+};
+
+
+static UInt32 find_dcl_by_name(const char* name,const SNameToID* p=s_VSDeclNames)
+{
+ while (p->name)
+ {
+ if (0 == strcmp(p->name,name))
+ return p->id;
+ p++;
+ }
+
+ Assert(0 || "DCL name not found!");
+
+ return -1;
+}
+
+void dxb_dcl_tex2 (DXBCBuilder* b, int index, SM4Target dim)
+{
+ UInt32 tok2 = 0x80000000;
+
+ switch (dim)
+ {
+ case kSM4Target_TEXTURE2D:
+ tok2 |= D3DSTT_2D;
+ break;
+ case kSM4Target_TEXTURE3D:
+ tok2 |= D3DSTT_VOLUME;
+ break;
+ case kSM4Target_TEXTURECUBE:
+ tok2 |= D3DSTT_CUBE;
+ break;
+ default:
+ Assert(0 || "Wrong texture type!");
+ };
+
+ SM2TokInstruction tok;
+ tok.dword = 0;
+ tok.opcode = kSM2Op_DCL;
+ tok.length = 2;
+
+ b->inputs2.push_back (tok.dword);
+ b->inputs2.push_back (tok2);
+
+ SM2TokDst op;
+ op.dword = dxb_reg_split2(D3DSPR_SAMPLER);
+ op.reg_num = index;
+ op.write_mask = 0xf;
+ op._one = 1;
+ b->inputs2.push_back (op.dword);
+
+ b->numTextures2++;
+}
+
+//CAVEAT: DX11 feature level 9.x vertex shaders use texcoord semantics for _all_ attributes
+int dxb_find_input_by_name2(DXBCBuilder* b,const char* name, int index)
+{
+ int index2 = 0;
+ while (0 != strcmp(b->inputs[index2].name,name) || b->inputs[index2].index != index)
+ index2++;
+
+ return index2;
+}
+
+int dxb_find_output_by_name2(DXBCBuilder* b,const char* name, int index)
+{
+ int index2 = 0;
+ while (0 != strcmp(b->outputs[index2].name,name) || b->outputs[index2].index != index)
+ index2++;
+
+ return index2;
+}
+
+
+//CAVEAT1: DX11 feature level 9.x vertex shaders use texcoord semantics for _all_ attributes
+//CAVEAT2: [Qualcomm] DX11 feature level 9.x uses texcoord interpolators for _all_ varyings
+void dxb_dcl_input2 (DXBCBuilder* b, const char* name, int index, int reg, int mask)
+{
+ //TODO: this is not needed anymore.
+ UInt32 decl = find_dcl_by_name(name,b->version.type == kSM4Shader_Vertex ? s_VSDeclNames : s_PSDeclNames);
+ UInt32 reg_type = b->version.type == kSM4Shader_Vertex ? dxb_reg_type2('v') : (decl);
+
+ SM2TokInstruction tok;
+ tok.dword = 0;
+ tok.opcode = kSM2Op_DCL;
+ tok.length = 2;
+
+ b->inputs2.push_back (tok.dword);
+
+ UInt32 tok2 = 0x80000000;
+ //CAVEAT1
+ if (b->version.type == kSM4Shader_Vertex)
+ tok2 |= D3DDECLUSAGE_TEXCOORD|(dxb_find_input_by_name2(b,name,index)<<16);
+ b->inputs2.push_back (tok2);
+
+#if 1
+ //CAVEAT2
+ if (b->version.type == kSM4Shader_Pixel)
+ {
+ index = dxb_find_input_by_name2(b,name,index);
+ reg_type = D3DSPR_TEXTURE;
+ }
+#endif
+
+ SM2TokDst op;
+ op.dword = dxb_reg_split2(reg_type);
+ op.reg_num = b->version.type == kSM4Shader_Vertex ? reg : index;
+ op.write_mask = mask;
+ op._one = 1;
+ op._res0 = 0;
+ op._res1 = 0;
+ b->inputs2.push_back (op.dword);
+
+ if (b->version.type == kSM4Shader_Pixel)
+ {
+ op.write_mask = 0;
+ b->inputMap[reg] = op.dword;
+ }
+}
+
+void dxb_dcl_output2 (DXBCBuilder* b, const char* name, int index, int reg, int mask)
+{
+ UInt32 reg_type = find_dcl_by_name(name,s_OutNames);
+#if 1
+ //CAVEAT2. Excluding POSITION
+ if (b->version.type == kSM4Shader_Vertex && NULL == strstr(name,"POSITION"))
+ {
+ index = dxb_find_output_by_name2(b,name,index);
+ reg_type = D3DSPR_TEXCRDOUT;
+ }
+#else
+ //special case in raster reg. file
+ if (0 == strcmp(name,"FOG"))
+ index = 2;
+#endif
+
+ SM2TokDst op;
+ op.dword = dxb_reg_split2(reg_type);
+ op.reg_num = index;
+
+ b->outputMap[reg] = op.dword;
+}
+
+static UInt32 dxb_find_output2 (DXBCBuilder* b, int reg)
+{
+ Assert (b->outputMap.find(reg) != b->outputMap.end());
+ return b->outputMap[reg];
+}
+
+static UInt32 dxb_find_input2 (DXBCBuilder* b, int reg)
+{
+ Assert (b->inputMap.find(reg) != b->inputMap.end());
+ return b->inputMap[reg];
+}
+
+
+void dxb_op2 (DXBCBuilder* b, SM2Opcode op, bool sat, int scratchTmpRegForSat)
+{
+ dxb_handle_saturate2 (b);
+
+ b->codeBuilder.opcode2 = op;
+ b->curInsnIndex2 = b->insns2.size();
+
+ SM2TokInstruction tok;
+ tok.dword = 0;
+ tok.opcode = op;
+ tok.specific = op>>16;
+ tok.length = 0;
+
+ b->insns2.push_back (tok.dword);
+ b->codeBuilder.saturate = sat;
+ b->codeBuilder.tmpSatRegIndex = scratchTmpRegForSat;
+}
+
+void dxb_reg2 (DXBCBuilder* b, char rchar, int reg,unsigned mask)
+{
+ b->codeBuilder.dstRegMask = mask;
+ b->codeBuilder.dstRegComps = BitsInMask(mask);
+ if (b->codeBuilder.saturate)
+ {
+ b->codeBuilder.dstRegType = rchar;
+ b->codeBuilder.dstRegIndex = reg;
+ if (b->version.type != kSM4Shader_Pixel)
+ {
+ rchar = 'r';
+ reg = b->codeBuilder.tmpSatRegIndex;
+ }
+ }
+
+ //for sm20 sampler will be emitted by texture coords swizzle source op
+ if ('s' == rchar && b->version.type == kSM4Shader_Pixel)
+ return;
+
+ dxbc_update_insn_length2 (b, 1);
+ SM2TokDst op;
+
+ if ('o' == rchar)
+ op.dword = dxb_find_output2(b,reg);
+ else if ('v' == rchar && b->version.type == kSM4Shader_Pixel)
+ op.dword = dxb_find_input2(b,reg);
+ else
+ {
+ op.dword = dxb_reg_split2(dxb_reg_type2(rchar));
+ op.reg_num = reg;
+ }
+
+ op.res_mod = 0;
+ if (b->codeBuilder.saturate && b->version.type == kSM4Shader_Pixel)
+ {
+ op.res_mod = D3DSPDM_SATURATE>>D3DSP_DSTMOD_SHIFT;
+ b->codeBuilder.saturate = 0;
+ }
+
+ op.write_mask = mask;
+ op._one = 1;
+ op._res0 = 0;
+ op._res1 = 0;
+
+ b->insns2.push_back (op.dword);
+}
+
+void dxb_swz2 (DXBCBuilder* b, char rchar, int reg, unsigned swizzle, bool neg)
+{
+ swizzle = adjust_swizzle (&b->codeBuilder, swizzle, true);
+
+ UInt32 rtype = dxb_reg_type2(rchar);
+ dxbc_update_insn_length2 (b, 1);
+
+ SM2TokSrc op;
+ if ('o' == rchar)
+ op.dword = dxb_find_output2(b,reg);
+ else if ('v' == rchar && b->version.type == kSM4Shader_Pixel)
+ op.dword = dxb_find_input2(b,reg);
+ else
+ {
+ op.dword = dxb_reg_split2(dxb_reg_type2(rchar));
+ op.reg_num = reg;
+ }
+
+ //texld must not swizzle
+ if ('s' != rchar && 't' != rchar)// && op.reg_type02 != D3DSPR_TEXTURE)
+ {
+ op.swizzle = swizzle;
+ op.src_mod = neg ? 1 : 0;
+ }
+ else
+ op.swizzle = kSM4SwzNone;
+
+ op._one = 1;
+ op._res0 = 0;
+ op._res1 = 0;
+
+ b->insns2.push_back (op.dword);
+}
+
+//find a constant, define a constant if not found
+int dxb_imm_f4 (DXBCBuilder* b, float v0, float v1, float v2, float v3)
+{
+ union { float f; UInt32 i; } f2i;
+ //TODO: do not hardcode!
+ int reg = b->version.type == kSM4Shader_Pixel ? 10 : 63 + 2;
+ dynamic_array<UInt32>::const_iterator it = b->defs2.begin();
+ for (;it != b->defs2.end();it += 6, reg++)
+ {
+ f2i.f = v0;
+ if (f2i.i != *(it + 2))
+ continue;
+ f2i.f = v1;
+ if (f2i.i != *(it + 3))
+ continue;
+ f2i.f = v2;
+ if (f2i.i != *(it + 4))
+ continue;
+ f2i.f = v3;
+ if (f2i.i != *(it + 5))
+ continue;
+
+ break;
+ }
+
+ if (it == b->defs2.end())
+ {
+ SM2TokInstruction tok;
+ tok.dword = 0;
+ tok.opcode = kSM2Op_DEF;
+ tok.specific = 0;
+ tok.length = 5;
+ b->defs2.push_back (tok.dword);
+
+ SM2TokDst op;
+ op.dword = dxb_reg_split2(dxb_reg_type2('c'));
+ op.reg_num = reg;
+ op.write_mask = 0xF;
+ op._one = 1;
+ b->defs2.push_back (op.dword);
+
+ f2i.f = v0; b->defs2.push_back (f2i.i);
+ f2i.f = v1; b->defs2.push_back (f2i.i);
+ f2i.f = v2; b->defs2.push_back (f2i.i);
+ f2i.f = v3; b->defs2.push_back (f2i.i);
+ }
+
+ return reg;
+}
+
+SM2Opcode dxb_to_sm2 (SM4Opcode op)
+{
+ switch (op)
+ {
+ case kSM4Op_SQRT:
+ break;
+ case kSM4Op_DIV:
+ break;
+ case kSM4Op_RSQ:
+ return kSM2Op_RSQ;
+ case kSM4Op_LOG:
+ return kSM2Op_LOG;
+ case kSM4Op_EXP:
+ return kSM2Op_EXP;
+ case kSM4Op_RCP:
+ return kSM2Op_RCP;
+
+ case kSM4Op_MOV:
+ return kSM2Op_MOV;
+ case kSM4Op_MOVC:
+ break;
+
+ case kSM4Op_MAD:
+ return kSM2Op_MAD;
+ case kSM4Op_DP2:
+ break;
+ case kSM4Op_DP3:
+ return kSM2Op_DP3;
+ case kSM4Op_DP4:
+ return kSM2Op_DP4;
+ case kSM4Op_MUL:
+ return kSM2Op_MUL;
+ case kSM4Op_ADD:
+ return kSM2Op_ADD;
+ case kSM4Op_AND:
+ break;
+ case kSM4Op_MIN:
+ return kSM2Op_MIN;
+ case kSM4Op_MAX:
+ return kSM2Op_MAX;
+
+ case kSM4Op_SAMPLE:
+ return kSM2Op_TEX;
+ case kSM4Op_DISCARD:
+ return kSM2Op_TEXKILL;
+
+ case kSM4Op_LT:
+ return kSM2Op_SLT;
+ case kSM4Op_GE:
+ return kSM2Op_SGE;
+ case kSM4Op_NE:
+ break;
+ case kSM4Op_EQ:
+ break;
+
+ case kSM4Op_RET:
+ return kSM2Op_END;
+ }
+
+ AssertString("unknown SM4 opcode");
+ __debugbreak();
+
+ return kSM2Op_NOP;
+}
+
+
diff --git a/Runtime/GfxDevice/d3d11/D3D11ByteCode.h b/Runtime/GfxDevice/d3d11/D3D11ByteCode.h
new file mode 100644
index 0000000..020a8c1
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/D3D11ByteCode.h
@@ -0,0 +1,856 @@
+#pragma once
+
+#include "Runtime/Utilities/dynamic_array.h"
+
+struct _D3D11_SIGNATURE_PARAMETER_DESC;
+typedef struct _D3D11_SIGNATURE_PARAMETER_DESC D3D11_SIGNATURE_PARAMETER_DESC;
+
+
+
+#define MAKE_FOURCC(a, b, c, d) ((UInt32)(UInt8)(a) | ((UInt32)(UInt8)(b) << 8) | ((UInt32)(UInt8)(c) << 16) | ((UInt32)(UInt8)(d) << 24 ))
+#define kFOURCC_DXBC MAKE_FOURCC('D', 'X', 'B', 'C')
+
+// Reflection or debug information? stripped shaders don't have this chunk
+#define kFOURCC_RDEF MAKE_FOURCC('R', 'D', 'E', 'F')
+// Shader input signature
+#define kFOURCC_ISGN MAKE_FOURCC('I', 'S', 'G', 'N')
+// Shader output signature
+#define kFOURCC_OSGN MAKE_FOURCC('O', 'S', 'G', 'N')
+// Shader code
+#define kFOURCC_SHDR MAKE_FOURCC('S', 'H', 'D', 'R')
+#define kFOURCC_SHEX MAKE_FOURCC('S', 'H', 'E', 'X')
+// Statistics? stripped shaders don't have this chunk
+#define kFOURCC_STAT MAKE_FOURCC('S', 'T', 'A', 'T')
+// Patch information?
+#define kFOURCC_PCSG MAKE_FOURCC('P', 'C', 'S', 'G')
+//FL 9.x SM 2.0 shader magic signature
+#define kFOURCC_SM20 MAKE_FOURCC('A', 'o', 'n', '9')
+
+
+struct DXBCChunkHeader
+{
+ unsigned fourcc;
+ unsigned size;
+};
+
+
+struct DXBCContainer
+{
+ const void* data;
+ dynamic_array<DXBCChunkHeader*> chunks;
+};
+
+struct DXBCSignatureElement
+{
+ UInt32 name_offset;
+ UInt32 semantic_index;
+ UInt32 system_value_type;
+ UInt32 component_type;
+ UInt32 register_num;
+ UInt8 mask;
+ UInt8 read_write_mask;
+ UInt8 stream;
+ UInt8 unused;
+};
+
+struct DXBCChunkSig : public DXBCChunkHeader
+{
+ UInt32 count;
+ UInt32 unk8; // always has 8?
+ DXBCSignatureElement elements[1];
+};
+
+struct DXBCChunkCode : public DXBCChunkHeader
+{
+ UInt32 version;
+ UInt32 length; // length in dword tokens
+};
+
+struct DXBCChunkSM20 : public DXBCChunkHeader
+{
+ UInt32 length; // length in bytes
+};
+
+
+struct SM4TokInstruction
+{
+ // not an union directly because unions can't be inherited from
+ union
+ {
+ UInt32 dword;
+ // length and extended are always present, but they are only here to reduce duplication
+ struct
+ {
+ unsigned opcode : 11;
+ unsigned _11_23 : 13;
+ unsigned length : 7;
+ unsigned extended : 1;
+ };
+ struct
+ {
+ unsigned opcode : 11;
+ unsigned resinfo_return_type : 2;
+ unsigned sat : 1;
+ unsigned _14_17 : 4;
+ unsigned test_nz : 1; // bit 18
+ unsigned precise_mask : 4;
+ unsigned _23 : 1;
+ unsigned length : 7;
+ unsigned extended : 1;
+ } insn;
+ struct
+ {
+ unsigned opcode : 11;
+ unsigned threads_in_group : 1;
+ unsigned shared_memory : 1;
+ unsigned uav_group : 1;
+ unsigned uav_global : 1;
+ unsigned _15_17 : 3;
+ } sync;
+ struct
+ {
+ unsigned opcode : 11;
+ unsigned allow_refactoring : 1;
+ unsigned fp64 : 1;
+ unsigned early_depth_stencil : 1;
+ unsigned enable_raw_and_structured_in_non_cs : 1;
+ } dcl_global_flags;
+ struct
+ {
+ unsigned opcode : 11;
+ unsigned target : 5;
+ unsigned nr_samples : 7;
+ } dcl_resource;
+ struct
+ {
+ unsigned opcode : 11;
+ unsigned shadow : 1;
+ unsigned mono : 1;
+ } dcl_sampler;
+ struct
+ {
+ unsigned opcode : 11;
+ unsigned interpolation : 5;
+ } dcl_input_ps;
+ struct
+ {
+ unsigned opcode : 11;
+ unsigned dynamic : 1;
+ } dcl_constant_buffer;
+ struct
+ {
+ unsigned opcode : 11;
+ unsigned primitive : 6;
+ } dcl_gs_input_primitive;
+ struct
+ {
+ unsigned opcode : 11;
+ unsigned primitive_topology : 7;
+ } dcl_gs_output_primitive_topology;
+ struct
+ {
+ unsigned opcode : 11;
+ unsigned control_points : 6;
+ } dcl_input_control_point_count;
+ struct
+ {
+ unsigned opcode : 11;
+ unsigned control_points : 6;
+ } dcl_output_control_point_count;
+ struct
+ {
+ unsigned opcode : 11;
+ unsigned domain : 3; /* D3D_TESSELLATOR_DOMAIN */
+ } dcl_tess_domain;
+ struct
+ {
+ unsigned opcode : 11;
+ unsigned partitioning : 3; /* D3D_TESSELLATOR_PARTITIONING */
+ } dcl_tess_partitioning;
+ struct
+ {
+ unsigned opcode : 11;
+ unsigned primitive : 3; /* D3D_TESSELLATOR_OUTPUT_PRIMITIVE */
+ } dcl_tess_output_primitive;
+ };
+};
+
+union SM4TokInstructionEx
+{
+ UInt32 dword;
+ struct
+ {
+ unsigned type : 6;
+ unsigned _6_30 : 25;
+ unsigned extended :1;
+ };
+ struct
+ {
+ unsigned type : 6;
+ unsigned _6_8 : 3;
+ int offset_u : 4;
+ int offset_v : 4;
+ int offset_w : 4;
+ } sample_controls;
+ struct
+ {
+ unsigned type : 6;
+ unsigned target : 5;
+ } resource_target;
+ struct
+ {
+ unsigned type : 6;
+ unsigned x : 4;
+ unsigned y : 4;
+ unsigned z : 4;
+ unsigned w : 4;
+ } resource_return_type;
+};
+
+struct SM4TokOperand
+{
+ union {
+ UInt32 dword;
+ struct {
+ unsigned comps_enum : 2; /* sm4_operands_comps */
+ unsigned mode : 2; /* SM4OperMode */
+ unsigned sel : 8;
+ unsigned file : 8; /* SM4RegFile */
+ unsigned num_indices : 2;
+ unsigned index0_repr : 3; /* SM4OperIndexRepr */
+ unsigned index1_repr : 3; /* SM4OperIndexRepr */
+ unsigned index2_repr : 3; /* SM4OperIndexRepr */
+ unsigned extended : 1;
+ };
+ };
+};
+
+struct SM2TokInstruction
+{
+ union
+ {
+ UInt32 dword;
+ struct
+ {
+ unsigned opcode : 16;
+ unsigned specific : 8;
+ unsigned length : 4;
+ unsigned predicated : 1;
+ unsigned _res0 : 1;
+ unsigned _res1 : 1;
+ unsigned _res2 : 1;
+ };
+ struct
+ {
+ unsigned minor : 8;
+ unsigned major : 8;
+ unsigned _magic : 16;
+ } version;
+ };
+};
+
+struct SM2TokDst
+{
+ union {
+ UInt32 dword;
+ struct {
+ unsigned reg_num : 11;
+ unsigned reg_type34 : 2;
+ unsigned _res0 : 1;
+ unsigned _res1 : 2;
+ unsigned write_mask : 4;
+ unsigned res_mod : 4;
+ unsigned shift_scl : 4;
+ unsigned reg_type02 : 3;
+ unsigned _one : 1;
+ };
+ };
+};
+
+void dxbc_create(struct DXBCChunkHeader** chunks, unsigned num_chunks, dynamic_array<UInt8>& out);
+
+
+DXBCContainer* dxbc_parse(const void* data, int size);
+DXBCChunkHeader* dxbc_find_chunk(const void* data, int size, unsigned fourcc);
+void dxbc_print(const DXBCContainer* dxbc);
+int dxbc_parse_signature(const DXBCChunkSig* sig, D3D11_SIGNATURE_PARAMETER_DESC** params);
+
+
+enum SM2Opcode
+{
+ kSM2Op_NOP = 0,
+ kSM2Op_MOV ,
+ kSM2Op_ADD ,
+ kSM2Op_SUB ,
+ kSM2Op_MAD ,
+ kSM2Op_MUL ,
+ kSM2Op_RCP ,
+ kSM2Op_RSQ ,
+ kSM2Op_DP3 ,
+ kSM2Op_DP4 ,
+ kSM2Op_MIN ,
+ kSM2Op_MAX ,
+ kSM2Op_SLT ,
+ kSM2Op_SGE ,
+ kSM2Op_EXP ,
+ kSM2Op_LOG ,
+ kSM2Op_LIT ,
+ kSM2Op_DST ,
+ kSM2Op_LRP ,
+ kSM2Op_FRC ,
+ kSM2Op_M4x4 ,
+ kSM2Op_M4x3 ,
+ kSM2Op_M3x4 ,
+ kSM2Op_M3x3 ,
+ kSM2Op_M3x2 ,
+ kSM2Op_CALL ,
+ kSM2Op_CALLNZ ,
+ kSM2Op_LOOP ,
+ kSM2Op_RET ,
+ kSM2Op_ENDLOOP ,
+ kSM2Op_LABEL ,
+ kSM2Op_DCL ,
+ kSM2Op_POW ,
+ kSM2Op_CRS ,
+ kSM2Op_SGN ,
+ kSM2Op_ABS ,
+ kSM2Op_NRM ,
+ kSM2Op_SINCOS ,
+ kSM2Op_REP ,
+ kSM2Op_ENDREP ,
+ kSM2Op_IF ,
+ kSM2Op_IFC ,
+ kSM2Op_ELSE ,
+ kSM2Op_ENDIF ,
+ kSM2Op_BREAK ,
+ kSM2Op_BREAKC ,
+ kSM2Op_MOVA ,
+ kSM2Op_DEFB ,
+ kSM2Op_DEFI ,
+
+ kSM2Op_TEXCOORD = 64,
+ kSM2Op_TEXKILL ,
+ kSM2Op_TEX ,
+ kSM2Op_TEXBEM ,
+ kSM2Op_TEXBEML ,
+ kSM2Op_TEXREG2AR ,
+ kSM2Op_TEXREG2GB ,
+ kSM2Op_TEXM3x2PAD ,
+ kSM2Op_TEXM3x2TEX ,
+ kSM2Op_TEXM3x3PAD ,
+ kSM2Op_TEXM3x3TEX ,
+ kSM2Op_RESERVED0 ,
+ kSM2Op_TEXM3x3SPEC ,
+ kSM2Op_TEXM3x3VSPEC ,
+ kSM2Op_EXPP ,
+ kSM2Op_LOGP ,
+ kSM2Op_CND ,
+ kSM2Op_DEF ,
+ kSM2Op_TEXREG2RGB ,
+ kSM2Op_TEXDP3TEX ,
+ kSM2Op_TEXM3x2DEPTH ,
+ kSM2Op_TEXDP3 ,
+ kSM2Op_TEXM3x3 ,
+ kSM2Op_TEXDEPTH ,
+ kSM2Op_CMP ,
+ kSM2Op_BEM ,
+ kSM2Op_DP2ADD ,
+ kSM2Op_DSX ,
+ kSM2Op_DSY ,
+ kSM2Op_TEXLDD ,
+ kSM2Op_SETP ,
+ kSM2Op_TEXLDL ,
+ kSM2Op_BREAKP ,
+
+ kSM2Op_PHASE = 0xFFFD,
+ kSM2Op_COMMENT = 0xFFFE,
+ kSM2Op_END = 0xFFFF,
+};
+
+enum SM4ShaderType
+{
+ kSM4Shader_Pixel,
+ kSM4Shader_Vertex,
+};
+
+enum SM4Opcode
+{
+ kSM4Op_ADD,
+ kSM4Op_AND,
+ kSM4Op_BREAK,
+ kSM4Op_BREAKC,
+ kSM4Op_CALL,
+ kSM4Op_CALLC,
+ kSM4Op_CASE,
+ kSM4Op_CONTINUE,
+ kSM4Op_CONTINUEC,
+ kSM4Op_CUT,
+ kSM4Op_DEFAULT,
+ kSM4Op_DERIV_RTX,
+ kSM4Op_DERIV_RTY,
+ kSM4Op_DISCARD,
+ kSM4Op_DIV,
+ kSM4Op_DP2,
+ kSM4Op_DP3,
+ kSM4Op_DP4,
+ kSM4Op_ELSE,
+ kSM4Op_EMIT,
+ kSM4Op_EMITTHENCUT,
+ kSM4Op_ENDIF,
+ kSM4Op_ENDLOOP,
+ kSM4Op_ENDSWITCH,
+ kSM4Op_EQ,
+ kSM4Op_EXP,
+ kSM4Op_FRC,
+ kSM4Op_FTOI,
+ kSM4Op_FTOU,
+ kSM4Op_GE,
+ kSM4Op_IADD,
+ kSM4Op_IF,
+ kSM4Op_IEQ,
+ kSM4Op_IGE,
+ kSM4Op_ILT,
+ kSM4Op_IMAD,
+ kSM4Op_IMAX,
+ kSM4Op_IMIN,
+ kSM4Op_IMUL,
+ kSM4Op_INE,
+ kSM4Op_INEG,
+ kSM4Op_ISHL,
+ kSM4Op_ISHR,
+ kSM4Op_ITOF,
+ kSM4Op_LABEL,
+ kSM4Op_LD,
+ kSM4Op_LD_MS,
+ kSM4Op_LOG,
+ kSM4Op_LOOP,
+ kSM4Op_LT,
+ kSM4Op_MAD,
+ kSM4Op_MIN,
+ kSM4Op_MAX,
+ kSM4Op_CUSTOMDATA,
+ kSM4Op_MOV,
+ kSM4Op_MOVC,
+ kSM4Op_MUL,
+ kSM4Op_NE,
+ kSM4Op_NOP,
+ kSM4Op_NOT,
+ kSM4Op_OR,
+ kSM4Op_RESINFO,
+ kSM4Op_RET,
+ kSM4Op_RETC,
+ kSM4Op_ROUND_NE,
+ kSM4Op_ROUND_NI,
+ kSM4Op_ROUND_PI,
+ kSM4Op_ROUND_Z,
+ kSM4Op_RSQ,
+ kSM4Op_SAMPLE,
+ kSM4Op_SAMPLE_C,
+ kSM4Op_SAMPLE_C_LZ,
+ kSM4Op_SAMPLE_L,
+ kSM4Op_SAMPLE_D,
+ kSM4Op_SAMPLE_B,
+ kSM4Op_SQRT,
+ kSM4Op_SWITCH,
+ kSM4Op_SINCOS,
+ kSM4Op_UDIV,
+ kSM4Op_ULT,
+ kSM4Op_UGE,
+ kSM4Op_UMUL,
+ kSM4Op_UMAD,
+ kSM4Op_UMAX,
+ kSM4Op_UMIN,
+ kSM4Op_USHR,
+ kSM4Op_UTOF,
+ kSM4Op_XOR,
+ kSM4Op_DCL_RESOURCE,
+ kSM4Op_DCL_CONSTANT_BUFFER,
+ kSM4Op_DCL_SAMPLER,
+ kSM4Op_DCL_INDEX_RANGE,
+ kSM4Op_DCL_GS_OUTPUT_PRIMITIVE_TOPOLOGY,
+ kSM4Op_DCL_GS_INPUT_PRIMITIVE,
+ kSM4Op_DCL_MAX_OUTPUT_VERTEX_COUNT,
+ kSM4Op_DCL_INPUT,
+ kSM4Op_DCL_INPUT_SGV,
+ kSM4Op_DCL_INPUT_SIV,
+ kSM4Op_DCL_INPUT_PS,
+ kSM4Op_DCL_INPUT_PS_SGV,
+ kSM4Op_DCL_INPUT_PS_SIV,
+ kSM4Op_DCL_OUTPUT,
+ kSM4Op_DCL_OUTPUT_SGV,
+ kSM4Op_DCL_OUTPUT_SIV,
+ kSM4Op_DCL_TEMPS,
+ kSM4Op_DCL_INDEXABLE_TEMP,
+ kSM4Op_DCL_GLOBAL_FLAGS,
+ kSM4Op_D3D10_COUNT,
+ kSM4Op_LOD,
+ kSM4Op_GATHER4,
+ kSM4Op_SAMPLE_POS,
+ kSM4Op_SAMPLE_INFO,
+ kSM4Op_D3D10_1_COUNT,
+ kSM4Op_HS_DECLS,
+ kSM4Op_HS_CONTROL_POINT_PHASE,
+ kSM4Op_HS_FORK_PHASE,
+ kSM4Op_HS_JOIN_PHASE,
+ kSM4Op_EMIT_STREAM,
+ kSM4Op_CUT_STREAM,
+ kSM4Op_EMITTHENCUT_STREAM,
+ kSM4Op_INTERFACE_CALL,
+ kSM4Op_BUFINFO,
+ kSM4Op_DERIV_RTX_COARSE,
+ kSM4Op_DERIV_RTX_FINE,
+ kSM4Op_DERIV_RTY_COARSE,
+ kSM4Op_DERIV_RTY_FINE,
+ kSM4Op_GATHER4_C,
+ kSM4Op_GATHER4_PO,
+ kSM4Op_GATHER4_PO_C,
+ kSM4Op_RCP,
+ kSM4Op_F32TOF16,
+ kSM4Op_F16TOF32,
+ kSM4Op_UADDC,
+ kSM4Op_USUBB,
+ kSM4Op_COUNTBITS,
+ kSM4Op_FIRSTBIT_HI,
+ kSM4Op_FIRSTBIT_LO,
+ kSM4Op_FIRSTBIT_SHI,
+ kSM4Op_UBFE,
+ kSM4Op_IBFE,
+ kSM4Op_BFI,
+ kSM4Op_BFREV,
+ kSM4Op_SWAPC,
+ kSM4Op_DCL_STREAM,
+ kSM4Op_DCL_FUNCTION_BODY,
+ kSM4Op_DCL_FUNCTION_TABLE,
+ kSM4Op_DCL_INTERFACE,
+ kSM4Op_DCL_INPUT_CONTROL_POINT_COUNT,
+ kSM4Op_DCL_OUTPUT_CONTROL_POINT_COUNT,
+ kSM4Op_DCL_TESS_DOMAIN,
+ kSM4Op_DCL_TESS_PARTITIONING,
+ kSM4Op_DCL_TESS_OUTPUT_PRIMITIVE,
+ kSM4Op_DCL_HS_MAX_TESSFACTOR,
+ kSM4Op_DCL_HS_FORK_PHASE_INSTANCE_COUNT,
+ kSM4Op_DCL_HS_JOIN_PHASE_INSTANCE_COUNT,
+ kSM4Op_DCL_THREAD_GROUP,
+ kSM4Op_DCL_UNORDERED_ACCESS_VIEW_TYPED,
+ kSM4Op_DCL_UNORDERED_ACCESS_VIEW_RAW,
+ kSM4Op_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED,
+ kSM4Op_DCL_THREAD_GROUP_SHARED_MEMORY_RAW,
+ kSM4Op_DCL_THREAD_GROUP_SHARED_MEMORY_STRUCTURED,
+ kSM4Op_DCL_RESOURCE_RAW,
+ kSM4Op_DCL_RESOURCE_STRUCTURED,
+ kSM4Op_LD_UAV_TYPED,
+ kSM4Op_STORE_UAV_TYPED,
+ kSM4Op_LD_RAW,
+ kSM4Op_STORE_RAW,
+ kSM4Op_LD_STRUCTURED,
+ kSM4Op_STORE_STRUCTURED,
+ kSM4Op_ATOMIC_AND,
+ kSM4Op_ATOMIC_OR,
+ kSM4Op_ATOMIC_XOR,
+ kSM4Op_ATOMIC_CMP_STORE,
+ kSM4Op_ATOMIC_IADD,
+ kSM4Op_ATOMIC_IMAX,
+ kSM4Op_ATOMIC_IMIN,
+ kSM4Op_ATOMIC_UMAX,
+ kSM4Op_ATOMIC_UMIN,
+ kSM4Op_IMM_ATOMIC_ALLOC,
+ kSM4Op_IMM_ATOMIC_CONSUME,
+ kSM4Op_IMM_ATOMIC_IADD,
+ kSM4Op_IMM_ATOMIC_AND,
+ kSM4Op_IMM_ATOMIC_OR,
+ kSM4Op_IMM_ATOMIC_XOR,
+ kSM4Op_IMM_ATOMIC_EXCH,
+ kSM4Op_IMM_ATOMIC_CMP_EXCH,
+ kSM4Op_IMM_ATOMIC_IMAX,
+ kSM4Op_IMM_ATOMIC_IMIN,
+ kSM4Op_IMM_ATOMIC_UMAX,
+ kSM4Op_IMM_ATOMIC_UMIN,
+ kSM4Op_SYNC,
+ kSM4Op_DADD,
+ kSM4Op_DMAX,
+ kSM4Op_DMIN,
+ kSM4Op_DMUL,
+ kSM4Op_DEQ,
+ kSM4Op_DGE,
+ kSM4Op_DLT,
+ kSM4Op_DNE,
+ kSM4Op_DMOV,
+ kSM4Op_DMOVC,
+ kSM4Op_DTOF,
+ kSM4Op_FTOD,
+ kSM4Op_EVAL_SNAPPED,
+ kSM4Op_EVAL_SAMPLE_INDEX,
+ kSM4Op_EVAL_CENTROID,
+ kSM4Op_DCL_GS_INSTANCE_COUNT,
+ kSM4Op_COUNT
+};
+
+
+enum SM4Interpolation
+{
+ kSM4Interp_UNDEFINED,
+ kSM4Interp_CONSTANT,
+ kSM4Interp_LINEAR,
+ kSM4Interp_LINEAR_CENTROID,
+ kSM4Interp_LINEAR_NOPERSPECTIVE,
+ kSM4Interp_LINEAR_NOPERSPECTIVE_CENTROID,
+ kSM4Interp_LINEAR_SAMPLE,
+ kSM4Interp_LINEAR_NOPERSPECTIVE_SAMPLE,
+ kSM4Interp_COUNT
+};
+
+
+enum SM4OperCompnum
+{
+ kSM4OperComp0,
+ kSM4OperComp1,
+ kSM4OperComp4,
+ kSM4OperCompN,
+ kSM4OperComp,
+ kSM4OperCompCOUNT
+};
+
+enum SM4OperMode
+{
+ SM4_OPERAND_MODE_MASK,
+ SM4_OPERAND_MODE_SWIZZLE,
+ SM4_OPERAND_MODE_SCALAR,
+ SM4_OPERAND_MODE_,
+ SM4_OPERAND_MODE_COUNT
+};
+
+enum SM4OperIndexRepr
+{
+ SM4_OPERAND_INDEX_REPR_IMM32,
+ SM4_OPERAND_INDEX_REPR_IMM64,
+ SM4_OPERAND_INDEX_REPR_REG,
+ SM4_OPERAND_INDEX_REPR_REG_IMM32,
+ SM4_OPERAND_INDEX_REPR_REG_IMM64,
+ SM4_OPERAND_INDEX_REPR_COUNT
+};
+
+
+enum SM4RegFile
+{
+ kSM4File_TEMP,
+ kSM4File_INPUT,
+ kSM4File_OUTPUT,
+ kSM4File_INDEXABLE_TEMP,
+ kSM4File_IMMEDIATE32,
+ kSM4File_IMMEDIATE64,
+ kSM4File_SAMPLER,
+ kSM4File_RESOURCE,
+ kSM4File_CONSTANT_BUFFER,
+ kSM4File_IMMEDIATE_CONSTANT_BUFFER,
+ kSM4File_LABEL,
+ kSM4File_INPUT_PRIMITIVEID,
+ kSM4File_OUTPUT_DEPTH,
+ kSM4File_NULL,
+ kSM4File_RASTERIZER,
+ kSM4File_OUTPUT_COVERAGE_MASK,
+ kSM4File_STREAM,
+ kSM4File_FUNCTION_BODY,
+ kSM4File_FUNCTION_TABLE,
+ kSM4File_INTERFACE,
+ kSM4File_FUNCTION_INPUT,
+ kSM4File_FUNCTION_OUTPUT,
+ kSM4File_OUTPUT_CONTROL_POINT_ID,
+ kSM4File_INPUT_FORK_INSTANCE_ID,
+ kSM4File_INPUT_JOIN_INSTANCE_ID,
+ kSM4File_INPUT_CONTROL_POINT,
+ kSM4File_OUTPUT_CONTROL_POINT,
+ kSM4File_INPUT_PATCH_CONSTANT,
+ kSM4File_INPUT_DOMAIN_POINT,
+ kSM4File_THIS_POINTER,
+ kSM4File_UNORDERED_ACCESS_VIEW,
+ kSM4File_THREAD_GROUP_SHARED_MEMORY,
+ kSM4File_INPUT_THREAD_ID,
+ kSM4File_INPUT_THREAD_GROUP_ID,
+ kSM4File_INPUT_THREAD_ID_IN_GROUP,
+ kSM4File_INPUT_COVERAGE_MASK,
+ kSM4File_INPUT_THREAD_ID_IN_GROUP_FLATTENED,
+ kSM4File_INPUT_GS_INSTANCE_ID,
+ kSM4File_OUTPUT_DEPTH_GREATER_EQUAL,
+ kSM4File_OUTPUT_DEPTH_LESS_EQUAL,
+ kSM4File_CYCLE_COUNTER,
+ kSM4File_COUNT
+};
+
+
+extern const char* kSM4OpcodeNames[kSM4Op_COUNT];
+
+
+enum SM4Swizzle {
+ kSM4SwzNone = 0xE4, // 11.10.01.00
+ kSM4SwzRepX = 0x00, // 00.00.00.00
+ kSM4SwzRepY = 0x55, // 01.01.01.01
+ kSM4SwzRepZ = 0xAA, // 10.10.10.10
+ kSM4SwzRepW = 0xFF, // 11.11.11.11
+ kSM4SwzXYZZ = 0xA4, // 10.10.01.00
+ kSM4SwzXYYY = 0x54, // 01.01.01.00
+ kSM4SwzXYXX = 0x04, // 00.00.01.00
+ kSM4SwzXYXY = 0x44, // 01.00.01.00
+ kSM4SwzZWZW = 0xEE, // 11.10.11.10
+ kSM4SwzZWWW = 0xFE, // 11.11.11.10
+ kSM4SwzYYYZ = 0x95, // 10.01.01.01
+ kSM4SwzXYZX = 0x24, // 00.10.01.00
+ kSM4SwzXYZY = 0x64, // 01.10.01.00
+ kSM4SwzYZWY = 0x79, // 01.11.10.01
+};
+
+enum SM4Target
+{
+ kSM4Target_UNKNOWN,
+ kSM4Target_BUFFER,
+ kSM4Target_TEXTURE1D,
+ kSM4Target_TEXTURE2D,
+ kSM4Target_TEXTURE2DMS,
+ kSM4Target_TEXTURE3D,
+ kSM4Target_TEXTURECUBE,
+ kSM4Target_TEXTURE1DARRAY,
+ kSM4Target_TEXTURE2DARRAY,
+ kSM4Target_TEXTURE2DMSARRAY,
+ kSM4Target_TEXTURECUBEARRAY,
+ kSM4Target_RAW_BUFFER,
+ kSM4Target_STRUCTURED_BUFFER,
+ kSM4Target_COUNT
+};
+
+
+struct DXBCBuilder;
+DXBCBuilder* dxb_create(int major, int minor, SM4ShaderType type);
+void dxb_destroy(DXBCBuilder* b);
+
+
+struct DXBCCodeBuilder;
+DXBCCodeBuilder* dxb_get_code_builder(DXBCBuilder* b);
+DXBCCodeBuilder* dxb_create_code(dynamic_array<UInt32>& destArray);
+void dxb_destroy_code(DXBCCodeBuilder* b);
+
+// result must be free()'d
+void* dxb_build (DXBCBuilder* b, size_t& outSize);
+
+void dxb_dcl_input (DXBCBuilder* b, const char* name, int index, int reg, int mask = 0xF);
+void dxb_dcl_output (DXBCBuilder* b, const char* name, int index, int reg, int mask = 0xF);
+void dxb_dcl_tex (DXBCBuilder* b, int index, SM4Target dim);
+void dxb_dcl_cb (DXBCBuilder* b, int index, int size);
+
+void dxb_op (DXBCCodeBuilder* b, SM4Opcode op, bool sat);
+void dxb_reg (DXBCCodeBuilder* b, char rchar, int reg, unsigned mask = 0xF);
+void dxb_swz (DXBCCodeBuilder* b, char rchar, int reg, unsigned swiz = kSM4SwzNone, bool neg=false);
+void dxb_float1 (DXBCCodeBuilder* b, float v);
+void dxb_int1 (DXBCCodeBuilder* b, int i);
+void dxb_float4 (DXBCCodeBuilder* b, float v0, float v1, float v2, float v3);
+
+//------------------------------------------------------------------------------------
+//SM 2.0 stuff
+void dxb_dcl_input2 (DXBCBuilder* b, const char* name, int index, int reg, int mask);
+void dxb_dcl_output2 (DXBCBuilder* b, const char* name, int index, int reg, int mask);
+void dxb_dcl_tex2 (DXBCBuilder* b, int index, SM4Target dim);
+void dxb_op2 (DXBCBuilder* b, SM2Opcode op, bool sat, int scratchTmpRegForSat = -1);
+void dxb_reg2 (DXBCBuilder* b, char rchar, int reg, unsigned mask = 0xF);
+void dxb_swz2 (DXBCBuilder* b, char rchar, int reg, unsigned swiz = kSM4SwzNone, bool neg=false);
+int dxb_imm_f4 (DXBCBuilder* b, float v0, float v1, float v2, float v3);
+SM2Opcode dxb_to_sm2 (SM4Opcode op);
+
+
+struct DXBCBuilderStream
+{
+ //no shader builder and no auto SM20 code for raw code works
+ DXBCBuilderStream (DXBCCodeBuilder* b) : builder(NULL), bldcode(b), noSM2(true) { }
+ DXBCBuilderStream (DXBCBuilder* b) : builder(b), bldcode(dxb_get_code_builder(b)), noSM2(false) { }
+
+ DXBCBuilderStream& op(SM4Opcode op)
+ {
+ dxb_op(bldcode, op, false);
+ if (!noSM2 && builder)
+ dxb_op2(builder, dxb_to_sm2(op), false);
+ return *this;
+ }
+ DXBCBuilderStream& op_sat(SM4Opcode op, int scratchTmpRegForSM2)
+ {
+ dxb_op(bldcode, op, true);
+ if (!noSM2 && builder)
+ dxb_op2(builder, dxb_to_sm2(op), true, scratchTmpRegForSM2);
+ return *this;
+ }
+ DXBCBuilderStream& reg (char rchar, int reg, unsigned mask = 0xF)
+ {
+ dxb_reg(bldcode, rchar, reg, mask);
+ if (!noSM2 && builder)
+ dxb_reg2(builder, rchar, reg, mask);
+ return *this;
+ }
+ DXBCBuilderStream& swz (char rchar, int reg, unsigned swiz = kSM4SwzNone, bool neg=false)
+ {
+ dxb_swz(bldcode, rchar, reg, swiz, neg);
+ if (!noSM2 && builder)
+ dxb_swz2(builder, rchar, reg, swiz, neg);
+ return *this;
+ }
+ DXBCBuilderStream& float1 (float v)
+ {
+ dxb_float1(bldcode, v);
+
+ if (!noSM2 && builder)
+ {
+ const int reg = dxb_imm_f4 (builder, v, v, v, v);
+ dxb_swz2(builder, 'c', reg, kSM4SwzRepX);
+ }
+ return *this;
+ }
+ // float1 constant for SM2 only
+ DXBCBuilderStream& float1_2 (float v)
+ {
+ if (builder)
+ {
+ const int reg = dxb_imm_f4 (builder, v, v, v, v);
+ dxb_swz2(builder, 'c', reg, kSM4SwzRepX);
+ }
+ return *this;
+ }
+ DXBCBuilderStream& float4 (float v0, float v1, float v2, float v3)
+ {
+ dxb_float4(bldcode, v0, v1, v2, v3);
+
+ if (!noSM2 && builder)
+ {
+ const int reg = dxb_imm_f4 (builder, v0, v1, v2, v3);
+ dxb_swz2(builder, 'c', reg);
+ }
+ return *this;
+ }
+
+
+ //------------------------------------------------------------------------------------
+ //SM 2.0 stuff
+
+ DXBCBuilderStream& op2(SM2Opcode op)
+ {
+ dxb_op2(builder, op, false);
+ return *this;
+ }
+ DXBCBuilderStream& reg2 (char rchar, int reg, unsigned mask = 0xF)
+ {
+ dxb_reg2(builder, rchar, reg, mask);
+ return *this;
+ }
+ DXBCBuilderStream& swz2 (char rchar, int reg, unsigned swiz = kSM4SwzNone, bool neg=false)
+ {
+ dxb_swz2(builder, rchar, reg, swiz, neg);
+ return *this;
+ }
+ void noAutoSM2(bool _noSM2=true)
+ {
+ noSM2 = _noSM2;
+ }
+ void autoSM2()
+ {
+ noSM2 = false;
+ }
+
+ DXBCBuilder* builder;
+ DXBCCodeBuilder* bldcode;
+ bool noSM2;
+};
+
diff --git a/Runtime/GfxDevice/d3d11/D3D11Compiler.cpp b/Runtime/GfxDevice/d3d11/D3D11Compiler.cpp
new file mode 100644
index 0000000..5cda3d7
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/D3D11Compiler.cpp
@@ -0,0 +1,2 @@
+#include "UnityPrefix.h"
+#include "D3D11Compiler.h"
diff --git a/Runtime/GfxDevice/d3d11/D3D11Compiler.h b/Runtime/GfxDevice/d3d11/D3D11Compiler.h
new file mode 100644
index 0000000..93284b2
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/D3D11Compiler.h
@@ -0,0 +1,117 @@
+#pragma once
+
+#if UNITY_WIN
+
+#if UNITY_WP8
+
+#pragma message("WP8 TODO: implement") // ?!-
+
+#define kD3D11CompilerDLL "dummy.dll" // ?!-
+
+struct D3D11Compiler // ?!-
+{
+public:
+ void Initialize (const char* dllName) {}
+ void Shutdown () {}
+ bool IsValid() const { return false; }
+};
+
+#else
+
+#if UNITY_WINRT
+#include <d3dcompiler.h>
+#else
+#include "External/DirectX/builds/dx11include/d3d11.h"
+#endif
+
+#if UNITY_WINRT
+#define kD3D11CompilerDLL "D3DCompiler_45.dll"
+#else
+#define kD3D11CompilerDLL "D3DCompiler_43.dll"
+#endif
+
+struct D3D11Compiler
+{
+public:
+ #if UNITY_WINRT
+ typedef HRESULT (WINAPI* D3DCompileFunc)(
+ _In_reads_bytes_(SrcDataSize) LPCVOID pSrcData,
+ _In_ SIZE_T SrcDataSize,
+ _In_opt_ LPCSTR pSourceName,
+ _In_reads_opt_(_Inexpressible_(pDefines->Name != NULL)) CONST D3D_SHADER_MACRO* pDefines,
+ _In_opt_ ID3DInclude* pInclude,
+ _In_ LPCSTR pEntrypoint,
+ _In_ LPCSTR pTarget,
+ _In_ UINT Flags1,
+ _In_ UINT Flags2,
+ _Out_ ID3DBlob** ppCode,
+ _Out_opt_ ID3DBlob** ppErrorMsgs);
+
+ typedef HRESULT (WINAPI* D3DStripShaderFunc)(
+ _In_reads_bytes_(BytecodeLength) LPCVOID pShaderBytecode,
+ _In_ SIZE_T BytecodeLength,
+ _In_ UINT uStripFlags,
+ _Out_ ID3DBlob** ppStrippedBlob);
+
+ typedef HRESULT (WINAPI* D3DReflectFunc)(
+ _In_reads_bytes_(SrcDataSize) LPCVOID pSrcData,
+ _In_ SIZE_T SrcDataSize,
+ _In_ REFIID pInterface,
+ _Out_ void** ppReflector);
+ #else
+ typedef HRESULT (WINAPI *D3DCompileFunc)(
+ const void* pSrcData,
+ unsigned long SrcDataSize,
+ const char* pFileName,
+ const D3D10_SHADER_MACRO* pDefines,
+ ID3D10Include* pInclude,
+ const char* pEntrypoint,
+ const char* pTarget,
+ UINT Flags1,
+ UINT Flags2,
+ ID3D10Blob** ppCode,
+ ID3D10Blob** ppErrorMsgs);
+
+ typedef HRESULT (WINAPI *D3DStripShaderFunc)(
+ __in_bcount(BytecodeLength) const void* pShaderBytecode,
+ __in unsigned long BytecodeLength,
+ __in unsigned int uStripFlags,
+ __out ID3D10Blob** ppStrippedBlob);
+
+ typedef HRESULT (WINAPI *D3DReflectFunc)(
+ __in_bcount(SrcDataSize) const void* pSrcData,
+ __in unsigned long SrcDataSize,
+ __in REFIID pInterface,
+ __out void** ppReflector);
+ #endif
+
+ typedef HRESULT (WINAPI *D3DDisassembleFunc)(
+ __in const void* pSrcData,
+ __in SIZE_T SrcDataSize,
+ __in UINT Flags,
+ __in const char* szComments,
+ __out ID3D10Blob** ppDisassembly);
+
+ typedef HRESULT (WINAPI *D3DCreateBlobFunc)(SIZE_T size, ID3D10Blob** blob);
+
+public:
+ void Initialize (const char* dllName);
+ void Shutdown ();
+ bool IsValid() const { return compileFunc && reflectFunc; }
+
+public:
+ #if !UNITY_WINRT
+ HINSTANCE dll;
+ #endif
+ D3DCompileFunc compileFunc;
+ D3DStripShaderFunc stripShaderFunc;
+ D3DReflectFunc reflectFunc;
+ D3DDisassembleFunc disassembleFunc;
+ D3DCreateBlobFunc createBlobFunc;
+};
+
+extern GUID kIID_ID3D11ShaderReflection;
+
+#endif
+
+#endif // UNITY_WIN
diff --git a/Runtime/GfxDevice/d3d11/D3D11Context.cpp b/Runtime/GfxDevice/d3d11/D3D11Context.cpp
new file mode 100644
index 0000000..06abd9b
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/D3D11Context.cpp
@@ -0,0 +1,610 @@
+#include "UnityPrefix.h"
+
+#if !UNITY_WP8 && !UNITY_METRO
+
+#include "D3D11Context.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+#include "D3D11Includes.h"
+#include "D3D11Utils.h"
+#include "TexturesD3D11.h"
+#include "TimerQueryD3D11.h"
+#include "Runtime/Misc/QualitySettings.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Utilities/LogUtility.h"
+#include "PlatformDependent/Win/ComPtr.h"
+#include "Runtime/Utilities/Argv.h"
+
+
+SupportedFeatureLevels GetSupportedFeatureLevels()
+{
+ SupportedFeatureLevels features;
+
+ if (HasARGV("force-feature-level-9-1")) features.push_back(D3D_FEATURE_LEVEL_9_1);
+ if (HasARGV("force-feature-level-9-2")) features.push_back(D3D_FEATURE_LEVEL_9_2);
+ if (HasARGV("force-feature-level-9-3")) features.push_back(D3D_FEATURE_LEVEL_9_3);
+ if (HasARGV("force-feature-level-10-0")) features.push_back(D3D_FEATURE_LEVEL_10_0);
+ if (HasARGV("force-feature-level-10-1")) features.push_back(D3D_FEATURE_LEVEL_10_1);
+ if (HasARGV("force-feature-level-11-0")) features.push_back(D3D_FEATURE_LEVEL_11_0);
+
+ features.push_back(D3D_FEATURE_LEVEL_11_0);
+ features.push_back(D3D_FEATURE_LEVEL_10_1);
+ features.push_back(D3D_FEATURE_LEVEL_10_0);
+
+ return features;
+}
+
+#if ENABLE_PROFILER
+D3D11PERF_BeginEventFunc g_D3D11BeginEventFunc = NULL;
+D3D11PERF_EndEventFunc g_D3D11EndEventFunc = NULL;
+#endif
+
+using namespace win;
+
+#if !UNITY_RELEASE
+#define UNITY_DX11_CREATE_FLAGS D3D11_CREATE_DEVICE_DEBUG
+#else
+#define UNITY_DX11_CREATE_FLAGS 0
+#endif
+
+
+static D3D_FEATURE_LEVEL kSupportedFeatureLevels[] = {
+ D3D_FEATURE_LEVEL_11_0,
+ D3D_FEATURE_LEVEL_10_1,
+ D3D_FEATURE_LEVEL_10_0,
+};
+
+
+bool InitD3D11RenderDepthSurface (RenderDepthSurfaceD3D11& rs, TexturesD3D11* textures, bool sampleOnly);
+
+
+static ComPtr<ID3D11Device> s_Device = NULL;
+// DX11.1 runtime only. On older runtimes this will stay null. Internally accessed by GetGfxDevice11_1().
+static ComPtr<ID3D11Device1> s_Device11_1;
+
+static ID3D11DeviceContext* s_Context = NULL;
+static IDXGIFactory* s_DXGIFactory = NULL;
+IDXGIFactory* GetDXGIFactory() { return s_DXGIFactory; }
+static IDXGISwapChain* s_SwapChain = NULL;
+static IDXGIOutput* s_Output = NULL;
+static int s_SwapChainAA = -1;
+IDXGISwapChain* GetD3D11SwapChain() { return s_SwapChain; }
+
+static int s_SyncInterval = 0;
+int GetD3D11SyncInterval() { return s_SyncInterval; }
+
+static RenderColorSurfaceD3D11 s_BackBuffer;
+static RenderDepthSurfaceD3D11 s_DepthStencil;
+
+ID3D11RenderTargetView* g_D3D11CurrRT;
+ID3D11DepthStencilView* g_D3D11CurrDS;
+ID3D11Resource* g_D3D11CurrRTResource;
+ID3D11Resource* g_D3D11CurrDSResource;
+RenderColorSurfaceD3D11* g_D3D11CurrColorRT;
+RenderDepthSurfaceD3D11* g_D3D11CurrDepthRT;
+int g_D3D11Adapter = 0;
+int g_D3D11Output = 0;
+
+static HWND s_Window = NULL;
+static HINSTANCE s_D3DDll = NULL;
+static HINSTANCE s_D3D9Dll = NULL;
+static HINSTANCE s_DXGIDll = NULL;
+static bool s_CurrentlyWindowed = true;
+
+typedef HRESULT (WINAPI* D3D11CreateDeviceFunc)(
+ IDXGIAdapter *pAdapter,
+ D3D_DRIVER_TYPE DriverType,
+ HMODULE Software,
+ UINT Flags,
+ CONST D3D_FEATURE_LEVEL *pFeatureLevels,
+ UINT FeatureLevels,
+ UINT SDKVersion,
+ ID3D11Device **ppDevice,
+ D3D_FEATURE_LEVEL *pFeatureLevel,
+ ID3D11DeviceContext **ppImmediateContext
+);
+
+typedef HRESULT (WINAPI* CreateDXGIFactoryFunc)(
+ REFIID ridd,
+ void** ppFactory
+);
+
+// Either selects default adapter (NULL) or n-th one,
+// defined by adapterIndex. Returned adapter
+// must be released if not NULL!
+static IDXGIAdapter* SelectAdapter (int adapterIndex)
+{
+ s_DXGIDll = LoadLibrary( "dxgi.dll" );
+ if( !s_DXGIDll )
+ return NULL;
+
+ CreateDXGIFactoryFunc createDXGIFactory = (CreateDXGIFactoryFunc)GetProcAddress( s_DXGIDll, "CreateDXGIFactory" );
+ if( !createDXGIFactory )
+ return NULL;
+
+ IDXGIAdapter* adapter = NULL;
+ if ( SUCCEEDED(createDXGIFactory(__uuidof(IDXGIFactory), (void**)&s_DXGIFactory)) )
+ {
+ for ( int i = 0; SUCCEEDED(s_DXGIFactory->EnumAdapters(i, &adapter)); ++i )
+ {
+ if ( i == adapterIndex )
+ break;
+ else
+ adapter->Release();
+ }
+ }
+
+ return adapter;
+}
+
+// Selects default output (NULL) or the one defined by outputIndex.
+// Result must be released!
+static IDXGIOutput* SelectOutput (IDXGIAdapter* adapter, int outputIndex)
+{
+ if (outputIndex == 0)
+ return NULL;
+
+ Assert(adapter);
+
+ IDXGIOutput* output = NULL;
+ for ( int i = 0; SUCCEEDED(adapter->EnumOutputs(i, &output)); ++i )
+ {
+ if ( i == outputIndex )
+ break;
+ else
+ output->Release();
+ }
+
+ return output;
+}
+
+bool InitializeD3D11 ()
+{
+ AssertIf (s_Device || s_Context || s_Window || s_D3DDll || s_D3D9Dll || s_DXGIDll);
+
+ SupportedFeatureLevels features = GetSupportedFeatureLevels();
+ IDXGIAdapter* adapter = NULL;
+
+ s_D3DDll = LoadLibrary( "d3d11.dll" );
+ if (!s_D3DDll)
+ {
+ printf_console ("d3d11: no D3D11 installed\n");
+ goto _cleanup;
+ }
+
+ D3D11CreateDeviceFunc createFunc = (D3D11CreateDeviceFunc)GetProcAddress( s_D3DDll, "D3D11CreateDevice" );
+ if( !createFunc )
+ {
+ printf_console ("d3d11: D3D11CreateDevice not found\n");
+ goto _cleanup;
+ }
+
+
+ DWORD d3d11CreateFlags = 0;
+ if (!HasARGV("force-d3d11-no-singlethreaded"))
+ {
+ d3d11CreateFlags |= D3D11_CREATE_DEVICE_SINGLETHREADED;
+ }
+
+ adapter = SelectAdapter (g_D3D11Adapter);
+ if (adapter)
+ s_Output = SelectOutput (adapter, g_D3D11Output);
+
+ // create D3D device & immediate context,
+ // with debug layer in Debug config
+ HRESULT hr = E_FAIL;
+ D3D_FEATURE_LEVEL level;
+ D3D_DRIVER_TYPE driverType = adapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE;
+ #if !UNITY_RELEASE
+ hr = createFunc (
+ adapter, driverType, NULL,
+ d3d11CreateFlags | D3D11_CREATE_DEVICE_DEBUG,
+ &features[0],
+ features.size(),
+ D3D11_SDK_VERSION, &s_Device, &level, &s_Context
+ );
+ #endif
+ // create without debug layer if the above failed or was not called at all
+ if (FAILED(hr))
+ {
+ hr = createFunc (
+ adapter, driverType, NULL,
+ d3d11CreateFlags,
+ &features[0], features.size(),
+ D3D11_SDK_VERSION, &s_Device, &level, &s_Context
+ );
+ }
+ if (FAILED(hr))
+ {
+ printf_console( "d3d11: failed to create D3D11 device (0x%08x)\n", hr );
+ goto _cleanup;
+ }
+
+ SAFE_RELEASE(adapter);
+
+ // Query DX11.1 interface, may well fail for older runtimes and is silently ignored.
+ s_Device->QueryInterface(&s_Device11_1);
+
+ // Create DXGIFactory if it isn't already created by SelectAdapter
+ if (!s_DXGIFactory)
+ {
+ IDXGIDevice* dxgiDevice = NULL;
+ hr = s_Device->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice);
+ IDXGIAdapter* dxgiAdapter = NULL;
+ hr = dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&dxgiAdapter);
+ hr = dxgiAdapter->GetParent(__uuidof(IDXGIFactory), (void**)&s_DXGIFactory);
+ dxgiAdapter->Release();
+ dxgiDevice->Release();
+ }
+
+
+ #if ENABLE_PROFILER
+ // Even on D3D11, PIX event marker functions are in D3D9 DLL
+ s_D3D9Dll = LoadLibrary ("d3d9.dll");
+ if (s_D3D9Dll)
+ {
+ g_D3D11BeginEventFunc = (D3D11PERF_BeginEventFunc)GetProcAddress(s_D3D9Dll, "D3DPERF_BeginEvent");
+ g_D3D11EndEventFunc = (D3D11PERF_EndEventFunc)GetProcAddress(s_D3D9Dll, "D3DPERF_EndEvent");
+ }
+ #endif
+
+ return true;
+
+_cleanup:
+ SAFE_RELEASE(adapter);
+ SAFE_RELEASE(s_Output);
+ SAFE_RELEASE(s_DXGIFactory);
+
+ s_Device11_1.Free();
+ s_Device.Free();
+
+ SAFE_RELEASE(s_Context);
+
+ if (s_D3DDll) {
+ FreeLibrary (s_D3DDll);
+ s_D3DDll = NULL;
+ }
+ if (s_D3D9Dll) {
+ FreeLibrary (s_D3D9Dll);
+ s_D3D9Dll = NULL;
+ }
+ if (s_DXGIDll) {
+ FreeLibrary (s_DXGIDll);
+ s_DXGIDll = NULL;
+ }
+ return false;
+}
+
+
+void CleanupD3D11()
+{
+ AssertIf (((ID3D11Device *)s_Device) || s_Context || s_Window);
+
+ if (s_D3DDll)
+ {
+ FreeLibrary (s_D3DDll);
+ s_D3DDll = NULL;
+ }
+ if (s_D3D9Dll)
+ {
+ FreeLibrary (s_D3D9Dll);
+ s_D3D9Dll = NULL;
+ }
+ if (s_DXGIDll) {
+ FreeLibrary (s_DXGIDll);
+ s_DXGIDll = NULL;
+ }
+}
+
+static void ReleaseBackbufferResources()
+{
+ //NESTED_LOG("DX11 debug", "ReleaseBackbufferResources");
+
+ Assert(s_Device && s_Context);
+
+ #if ENABLE_PROFILER
+ g_TimerQueriesD3D11.ReleaseAllQueries();
+ #endif
+
+ SAFE_RELEASE(s_DepthStencil.m_Texture);
+ SAFE_RELEASE(s_DepthStencil.m_SRView);
+ SAFE_RELEASE(s_DepthStencil.m_DSView);
+ s_BackBuffer.Reset();
+
+ if (s_Context)
+ s_Context->OMSetRenderTargets(0, NULL, NULL);
+}
+
+
+static void CreateBackbufferResources(GfxDevice* device, int width, int height, int antiAlias, DXGI_FORMAT swapFormat, bool sRGB)
+{
+ //NESTED_LOG("DX11 debug", "CreateBackbufferResources %ix%i", width, height);
+
+ HRESULT hr;
+
+ // Set the Backbuffer primary format flags
+ s_BackBuffer.flags = sRGB ? (s_BackBuffer.flags | kSurfaceCreateSRGB) : (s_BackBuffer.flags & ~kSurfaceCreateSRGB);
+
+ // Backbuffer
+ s_BackBuffer.width = width;
+ s_BackBuffer.height = height;
+ s_BackBuffer.samples = antiAlias;
+ s_BackBuffer.backBuffer = true;
+ s_BackBuffer.format = kRTFormatARGB32;
+
+ hr = s_SwapChain->GetBuffer (0, __uuidof(*s_BackBuffer.m_Texture), (void**)&s_BackBuffer.m_Texture);
+ Assert(SUCCEEDED(hr));
+ SetDebugNameD3D11 (s_BackBuffer.m_Texture, Format("SwapChain-BackBuffer-Texture-%dx%d", width, height));
+
+ // Create the primary backbuffer view
+ ID3D11RenderTargetView* rtv = NULL;
+ D3D11_RENDER_TARGET_VIEW_DESC rtDesc;
+ rtDesc.Format = sRGB ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
+ rtDesc.ViewDimension = antiAlias > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS : D3D11_RTV_DIMENSION_TEXTURE2D;
+ rtDesc.Texture2D.MipSlice = 0;
+ hr = s_Device->CreateRenderTargetView (s_BackBuffer.m_Texture, &rtDesc, &rtv);
+ s_BackBuffer.SetRTV (0, 0, false, rtv);
+ Assert(SUCCEEDED(hr));
+ SetDebugNameD3D11 (rtv, Format("SwapChain-BackBuffer-RTV-%dx%d", width, height));
+
+ // Create the secondary backbuffer view
+ D3D11_RENDER_TARGET_VIEW_DESC rtDescSecondary;
+ rtDescSecondary.Format = !sRGB ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
+ rtDescSecondary.ViewDimension = antiAlias > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS : D3D11_RTV_DIMENSION_TEXTURE2D;
+ rtDescSecondary.Texture2D.MipSlice = 0;
+ hr = s_Device->CreateRenderTargetView (s_BackBuffer.m_Texture, &rtDescSecondary, &rtv);
+ s_BackBuffer.SetRTV (0, 0, true, rtv);
+ Assert(SUCCEEDED(hr));
+ SetDebugNameD3D11 (rtv, Format("SwapChain-BackBuffer-RTVSec-%dx%d", width, height));
+
+ if (gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0)
+ {
+ // Create shader resource view
+ D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
+ srvDesc.Format = swapFormat;
+ srvDesc.ViewDimension = antiAlias > 1 ? D3D11_SRV_DIMENSION_TEXTURE2DMS : D3D11_SRV_DIMENSION_TEXTURE2D;
+ srvDesc.Texture2D.MostDetailedMip = 0;
+ srvDesc.Texture2D.MipLevels = 1;
+
+ ID3D11ShaderResourceView* srView = NULL;
+ hr = s_Device->CreateShaderResourceView (s_BackBuffer.m_Texture, &srvDesc, &s_BackBuffer.m_SRView);
+ Assert (SUCCEEDED(hr));
+ SetDebugNameD3D11 (s_BackBuffer.m_SRView, Format("SwapChain-BackBuffer-SRV-%dx%d", width, height));
+ }
+
+ // Depth stencil
+ s_DepthStencil.width = width;
+ s_DepthStencil.height = height;
+ s_DepthStencil.samples = antiAlias;
+ s_DepthStencil.dim = kTexDim2D;
+ s_DepthStencil.backBuffer = true;
+ s_DepthStencil.depthFormat = device->GetFramebufferDepthFormat();
+ {
+ bool dsOk = InitD3D11RenderDepthSurface (s_DepthStencil, NULL, false);
+ Assert (dsOk);
+ }
+
+#if !UNITY_EDITOR
+ RenderSurfaceHandle bbHandle(&s_BackBuffer), dsHandle(&s_DepthStencil);
+ device->SetRenderTargets(1, &bbHandle, dsHandle);
+#endif
+
+ #if ENABLE_PROFILER
+ if (gGraphicsCaps.hasTimerQuery)
+ g_TimerQueriesD3D11.RecreateAllQueries();
+ #endif
+}
+
+
+bool InitializeOrResetD3D11SwapChain(
+ class GfxDevice* device,
+ HWND window, int width, int height,
+ int refreshRate, bool fullscreen, int vsynccount, int antiAlias,
+ int& outBackbufferBPP, int& outFrontbufferBPP, int& outDepthBPP, int& outFSAA )
+{
+ Assert(s_Device && s_Context);
+
+ ReleaseBackbufferResources();
+
+ outBackbufferBPP = 4;
+ outFrontbufferBPP = 4;
+ outDepthBPP = 4;
+ outFSAA = 0;
+
+ device->SetCurrentWindowSize (width, height);
+
+ // pick supported AA level
+ if (antiAlias == -1)
+ antiAlias = GetQualitySettings().GetCurrent().antiAliasing;
+ while (antiAlias > 1 && !(gGraphicsCaps.d3d11.msaa & (1<<antiAlias)))
+ --antiAlias;
+ antiAlias = std::max(antiAlias, 1);
+
+ const bool sRGB = GetActiveColorSpace() == kLinearColorSpace;
+
+ // Release old swap chain if we need to change AA
+ if (s_SwapChain && s_SwapChainAA != antiAlias)
+ {
+ // swap chain must go out of fullscreen before releasing it
+ s_SwapChain->SetFullscreenState (FALSE, NULL);
+ SAFE_RELEASE(s_SwapChain);
+ }
+
+ // Create Swap Chain
+ HRESULT hr;
+
+ DWORD swapFlags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+ DXGI_FORMAT swapFormat = sRGB ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
+ s_SwapChainAA = antiAlias;
+ s_SyncInterval = vsynccount;
+ if (!s_SwapChain)
+ {
+ //NESTED_LOG("DX11 debug", "InitializeOrResetD3D11SwapChain, init %ix%i, window %p", width, height, window);
+
+ DXGI_SWAP_CHAIN_DESC sd;
+ ZeroMemory (&sd, sizeof(sd));
+ sd.BufferCount = 1;
+ sd.BufferDesc.Width = width;
+ sd.BufferDesc.Height = height;
+ sd.BufferDesc.Format = swapFormat;
+ sd.BufferDesc.RefreshRate.Numerator = refreshRate;
+ sd.BufferDesc.RefreshRate.Denominator = 1;
+ sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ if (gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0)
+ sd.BufferUsage |= DXGI_USAGE_SHADER_INPUT;
+ sd.Flags = swapFlags;
+ sd.OutputWindow = window;
+ sd.SampleDesc.Count = antiAlias;
+ sd.SampleDesc.Quality = 0;
+ // Docs suggest always setting this to true and then doing SetFullscreenState
+ sd.Windowed = TRUE;
+ hr = s_DXGIFactory->CreateSwapChain (s_Device, &sd, &s_SwapChain);
+ Assert(SUCCEEDED(hr));
+
+ // We'll handle Alt-Enter and other things ourselves
+ DWORD dxgiFlags = DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_WINDOW_CHANGES;
+ s_DXGIFactory->MakeWindowAssociation (window, dxgiFlags);
+
+ if (fullscreen)
+ s_SwapChain->SetFullscreenState(TRUE, s_Output);
+
+ CreateBackbufferResources(device, width, height, antiAlias, swapFormat, sRGB);
+ }
+ else
+ {
+ DXGI_MODE_DESC mode;
+ mode.Width = width;
+ mode.Height = height;
+ mode.RefreshRate.Numerator = refreshRate;
+ mode.RefreshRate.Denominator = 1;
+ mode.Format = swapFormat;
+ mode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
+ mode.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
+
+ //NESTED_LOG("DX11 debug", "InitializeOrResetD3D11SwapChain, resize %ix%i, fs=%i, window %p", width, height, fullscreen, window);
+
+ // Note: the following will often call WM_SIZE on us, which will handle actual resizing (since size might be slightly different!),
+ // which will go into ResizeSwapD3D11SwapChain.
+
+ {
+ //NESTED_LOG("DX11 debug", "ResizeTarget 1st");
+ s_SwapChain->ResizeTarget (&mode);
+ }
+ {
+ //NESTED_LOG("DX11 debug", "SetFullscreenState");
+ s_SwapChain->SetFullscreenState (fullscreen, fullscreen ? s_Output : NULL);
+ }
+
+ // according to "DXGI: Best Practices" on MSDN, advisable
+ // to call resize target again with refresh rate zeroed out.
+ mode.RefreshRate.Numerator = 0;
+ mode.RefreshRate.Denominator = 0;
+ if (fullscreen)
+ {
+ //NESTED_LOG("DX11 debug", "ResizeTarget 2nd");
+ s_SwapChain->ResizeTarget (&mode);
+ }
+
+ // In some cases above calls do not post WM_SIZE, so ResizeSwapD3D11SwapChain is not called,
+ // which means we won't get back buffer.
+ // If we still don't have back buffer here, just set it up.
+ if (!s_BackBuffer.m_Texture)
+ {
+ CreateBackbufferResources (device, width, height, s_SwapChainAA, swapFormat, sRGB);
+ }
+ }
+
+ return true;
+}
+
+
+void ResizeSwapD3D11SwapChain (class GfxDevice* device, HWND window, int width, int height)
+{
+ if (!s_SwapChain)
+ return;
+
+ const bool hadBackBuffer = (s_DepthStencil.m_Texture != NULL);
+ if (hadBackBuffer)
+ ReleaseBackbufferResources();
+
+ device->SetCurrentWindowSize (width, height);
+
+ const bool sRGB = GetActiveColorSpace() == kLinearColorSpace;
+ DWORD swapFlags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+ DXGI_FORMAT swapFormat = sRGB ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
+
+ //NESTED_LOG("DX11 debug", "ResizeSwapD3D11SwapChain, resize %ix%i", width, height);
+ s_SwapChain->ResizeBuffers (1, width, height, sRGB ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM, swapFlags);
+
+ CreateBackbufferResources (device, width, height, s_SwapChainAA, swapFormat, sRGB);
+}
+
+
+
+void DestroyD3D11Device()
+{
+ // This can happen when quiting from screen selector - window is not set up yet
+ if( !((ID3D11Device *)s_Device)|| !s_Context || !s_DXGIFactory )
+ return;
+
+ // swap chain must go out of fullscreen before releasing it
+ if (s_SwapChain)
+ {
+ s_SwapChain->SetFullscreenState (FALSE, NULL);
+ }
+
+ // cleanup
+ SAFE_RELEASE(s_DepthStencil.m_Texture);
+ SAFE_RELEASE(s_DepthStencil.m_SRView);
+ SAFE_RELEASE(s_DepthStencil.m_DSView);
+ s_BackBuffer.Reset();
+ SAFE_RELEASE(s_SwapChain);
+
+ s_Context->ClearState();
+ s_Context->Flush();
+
+ s_Context->Release();
+ s_Context = NULL;
+
+ /*
+ // Helper code to report any live objects
+ ID3D11Debug *d3dDebug = NULL;
+ if (SUCCEEDED(s_Device->QueryInterface(__uuidof(ID3D11Debug), (void**)&d3dDebug)))
+ {
+ d3dDebug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL | D3D11_RLDO_SUMMARY);
+ d3dDebug->Release();
+ }
+ */
+
+ s_Device11_1.Free();
+ s_Device.Free();
+
+ SAFE_RELEASE(s_Output);
+ SAFE_RELEASE(s_DXGIFactory);
+
+ s_Window = NULL;
+}
+
+ID3D11Device* GetD3D11Device()
+{
+ AssertIf( !((ID3D11Device *)s_Device) );
+ return s_Device;
+}
+
+ID3D11Device1* GetD3D11_1Device()
+{
+ return s_Device11_1;
+}
+
+
+ID3D11DeviceContext* GetD3D11Context(bool expectNull)
+{
+ if (!expectNull)
+ Assert( s_Context );
+ return s_Context;
+}
+
+#endif
+
diff --git a/Runtime/GfxDevice/d3d11/D3D11Context.h b/Runtime/GfxDevice/d3d11/D3D11Context.h
new file mode 100644
index 0000000..16134ce
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/D3D11Context.h
@@ -0,0 +1,65 @@
+#pragma once
+
+#include "D3D11Includes.h"
+
+#if UNITY_METRO
+#include <windows.ui.xaml.media.dxinterop.h>
+#endif
+
+struct D3D11Compiler;
+
+bool InitializeD3D11();
+void CleanupD3D11();
+#if UNITY_WP8
+HRESULT UpdateD3D11Device(ID3D11Device1* device, ID3D11DeviceContext1* deviceContext, ID3D11RenderTargetView* renderTargetView, int& width, int& height);
+void ActivateD3D11BackBuffer(class GfxDevice* device);
+#elif UNITY_METRO
+IDXGISwapChain1* CreateSwapChainForXAML(ISwapChainBackgroundPanelNative* panel, int width, int height);
+#if UNITY_METRO_VS2013
+IDXGIDevice3* GetIDXGIDevice3();
+#endif
+IDXGISwapChain1* CreateSwapChainForD3D(IUnknown* coreWindow, int width, int height);
+bool InitializeOrResetD3D11SwapChain(
+ class GfxDevice* device,
+ IDXGISwapChain1* chain, int width, int height, int vsynccount,
+ int& outBackbufferBPP, int& outFrontbufferBPP, int& outDepthBPP, int& outFSAA);
+void ActivateD3D11BackBuffer(class GfxDevice* device);
+#else
+bool InitializeOrResetD3D11SwapChain(
+ class GfxDevice* device,
+ HWND window, int width, int height,
+ int refreshRate, bool fullscreen, int vsynccount, int fsaa,
+ int& outBackbufferBPP, int& outFrontbufferBPP, int& outDepthBPP, int& outFSAA);
+void ResizeSwapD3D11SwapChain (class GfxDevice* device, HWND window, int width, int height);
+
+#endif
+
+typedef std::vector<D3D_FEATURE_LEVEL> SupportedFeatureLevels;
+SupportedFeatureLevels GetSupportedFeatureLevels();
+
+void DestroyD3D11Device();
+
+ID3D11Device* GetD3D11Device();
+ID3D11Device1* GetD3D11_1Device();
+ID3D11DeviceContext* GetD3D11Context(bool expectNull = false);
+
+IDXGIFactory* GetDXGIFactory();
+IDXGISwapChain* GetD3D11SwapChain();
+int GetD3D11SyncInterval();
+
+#if ENABLE_DX11_FRAME_LATENCY_WAITABLE_OBJECT
+HANDLE GetFrameLatencyWaitableObject();
+void WaitOnSwapChain();
+#endif
+
+extern ID3D11RenderTargetView* g_D3D11CurrRT;
+extern ID3D11DepthStencilView* g_D3D11CurrDS;
+struct RenderColorSurfaceD3D11;
+struct RenderDepthSurfaceD3D11;
+extern RenderColorSurfaceD3D11* g_D3D11CurrColorRT;
+extern RenderDepthSurfaceD3D11* g_D3D11CurrDepthRT;
+
+typedef int (WINAPI* D3D11PERF_BeginEventFunc)(DWORD, LPCWSTR);
+typedef int (WINAPI* D3D11PERF_EndEventFunc)();
+extern D3D11PERF_BeginEventFunc g_D3D11BeginEventFunc;
+extern D3D11PERF_EndEventFunc g_D3D11EndEventFunc;
diff --git a/Runtime/GfxDevice/d3d11/D3D11Context_Metro.cpp b/Runtime/GfxDevice/d3d11/D3D11Context_Metro.cpp
new file mode 100644
index 0000000..4c285ff
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/D3D11Context_Metro.cpp
@@ -0,0 +1,484 @@
+#include "UnityPrefix.h"
+
+#if UNITY_METRO
+#include "D3D11Context.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+#include "D3D11Includes.h"
+#include "D3D11Utils.h"
+#include "TexturesD3D11.h"
+#include "TimerQueryD3D11.h"
+#include "Runtime/Misc/QualitySettings.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Utilities/LogUtility.h"
+#include "PlatformDependent/Win/ComPtr.h"
+#include "PlatformDependent/MetroPlayer/AppCallbacks.h"
+#include "Runtime/Utilities/Argv.h"
+
+SupportedFeatureLevels GetSupportedFeatureLevels()
+{
+ SupportedFeatureLevels features;
+
+ if (HasARGV("force-feature-level-9-1")) features.push_back(D3D_FEATURE_LEVEL_9_1);
+ if (HasARGV("force-feature-level-9-2")) features.push_back(D3D_FEATURE_LEVEL_9_2);
+ if (HasARGV("force-feature-level-9-3")) features.push_back(D3D_FEATURE_LEVEL_9_3);
+ if (HasARGV("force-feature-level-10-0")) features.push_back(D3D_FEATURE_LEVEL_10_0);
+ if (HasARGV("force-feature-level-10-1")) features.push_back(D3D_FEATURE_LEVEL_10_1);
+ if (HasARGV("force-feature-level-11-0")) features.push_back(D3D_FEATURE_LEVEL_11_0);
+
+ features.push_back(D3D_FEATURE_LEVEL_11_0);
+ features.push_back(D3D_FEATURE_LEVEL_10_1);
+ features.push_back(D3D_FEATURE_LEVEL_10_0);
+ features.push_back(D3D_FEATURE_LEVEL_9_3);
+ features.push_back(D3D_FEATURE_LEVEL_9_2);
+ features.push_back(D3D_FEATURE_LEVEL_9_1);
+
+ return features;
+}
+
+#if ENABLE_PROFILER
+D3D11PERF_BeginEventFunc g_D3D11BeginEventFunc = NULL;
+D3D11PERF_EndEventFunc g_D3D11EndEventFunc = NULL;
+#endif
+
+using namespace win;
+
+#if !UNITY_RELEASE && !defined(__arm__)
+#define UNITY_DX11_CREATE_FLAGS D3D11_CREATE_DEVICE_DEBUG
+#else
+#define UNITY_DX11_CREATE_FLAGS 0
+#endif
+
+
+static ComPtr<ID3D11Device1> s_Device;
+static ComPtr<ID3D11DeviceContext1> s_Context;
+static ComPtr<IDXGIFactory2> s_DXGIFactory;
+static IDXGISwapChain1* s_SwapChain = NULL;
+#if UNITY_METRO_VS2013
+static IDXGIDevice3* s_DXGIDevice3 = NULL;
+#endif
+IDXGISwapChain* GetD3D11SwapChain() { return s_SwapChain; }
+
+static int s_SyncInterval = 0;
+int GetD3D11SyncInterval() { return s_SyncInterval; }
+
+static RenderColorSurfaceD3D11 s_BackBuffer;
+static RenderDepthSurfaceD3D11 s_DepthStencil;
+
+ID3D11RenderTargetView* g_D3D11CurrRT;
+ID3D11DepthStencilView* g_D3D11CurrDS;
+ID3D11Resource* g_D3D11CurrRTResource;
+ID3D11Resource* g_D3D11CurrDSResource;
+RenderColorSurfaceD3D11* g_D3D11CurrColorRT;
+RenderDepthSurfaceD3D11* g_D3D11CurrDepthRT;
+
+static const int kSwapChainBackBufferCount = 2;
+#if ENABLE_DX11_FRAME_LATENCY_WAITABLE_OBJECT
+static bool s_EnableLowLatencyPresentationAPI = true;
+static HANDLE s_FrameLatencyWaitableObject = NULL;
+static UINT kSwapChainFlags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
+#else
+static UINT kSwapChainFlags = 0;
+#endif
+static const DXGI_FORMAT kSwapChainBackBufferFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
+
+#define DX_CHECK(HR, ...) if (FAILED(hr)) {ErrorStringMsg(__VA_ARGS__); goto error; }
+
+bool InitializeD3D11()
+{
+ HRESULT hr;
+
+ ComPtr<ID3D11Device> device;
+ ComPtr<ID3D11DeviceContext> context;
+ ComPtr<IDXGIDevice1> dxgiDevice;
+ ComPtr<IDXGIAdapter> dxgiAdapter;
+
+ #if UNITY_DX11_CREATE_FLAGS
+ ComPtr<ID3D11Debug> debug;
+ #endif
+
+ Assert(!s_Device);
+ Assert(!s_Context);
+ Assert(!s_DXGIFactory);
+
+ SupportedFeatureLevels features = GetSupportedFeatureLevels();
+
+ D3D_DRIVER_TYPE driverType = D3D_DRIVER_TYPE_HARDWARE;
+
+ if (HasARGV("force-driver-type-warp"))
+ {
+ driverType = D3D_DRIVER_TYPE_WARP;
+ }
+
+ DWORD d3d11CreateFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
+ if (!HasARGV("force-d3d11-no-singlethreaded"))
+ {
+ d3d11CreateFlags |= D3D11_CREATE_DEVICE_SINGLETHREADED;
+ }
+#if ENABLE_DX11_FRAME_LATENCY_WAITABLE_OBJECT
+ if (HasARGV("disable-low-latency-presentation-api"))
+ {
+ printf_console("Disabling Low Latency presentation API.\n");
+ s_EnableLowLatencyPresentationAPI = false;
+ kSwapChainFlags = 0;
+ }
+#endif
+
+ D3D_FEATURE_LEVEL level;
+ hr = D3D11CreateDevice(
+ nullptr,
+ driverType,
+ nullptr,
+ (d3d11CreateFlags | UNITY_DX11_CREATE_FLAGS),
+ &features[0],
+ features.size(),
+ D3D11_SDK_VERSION,
+ &device,
+ &level,
+ &context);
+
+ if (FAILED(hr) && driverType != D3D_DRIVER_TYPE_WARP)
+ {
+ WarningStringMsg("D3D11 failed to create with D3D_DRIVER_TYPE_HARDWARE, fallbacking D3D_DRIVER_TYPE_WARP...");
+ driverType = D3D_DRIVER_TYPE_WARP;
+ hr = D3D11CreateDevice(
+ nullptr,
+ driverType,
+ nullptr,
+ (d3d11CreateFlags | UNITY_DX11_CREATE_FLAGS),
+ &features[0],
+ features.size(),
+ D3D11_SDK_VERSION,
+ &device,
+ &level,
+ &context);
+ }
+
+ DX_CHECK(hr, "D3D11CreateDevice failed with error 0x%08x ", hr);
+
+ hr = device->QueryInterface(&s_Device);
+ DX_CHECK(hr, "device->QueryInterface(&s_Device) failed with error 0x%08x ", hr);
+
+ hr = context->QueryInterface(&s_Context);
+ DX_CHECK(hr, "context->QueryInterface(&s_Context) failed with error 0x%08x ", hr);
+
+ #if UNITY_DX11_CREATE_FLAGS
+ hr = s_Device->QueryInterface(&debug);
+ AssertMsg(SUCCEEDED(hr), "s_Device->QueryInterface(&debug) failed failed with error 0x%08x", hr);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = debug->SetFeatureMask(D3D11_DEBUG_FEATURE_FLUSH_PER_RENDER_OP);
+ AssertMsg(SUCCEEDED(hr), "debug->SetFeatureMask(D3D11_DEBUG_FEATURE_FLUSH_PER_RENDER_OP) failed with error 0x%08x", hr);
+ }
+
+ #endif
+ #if UNITY_METRO_VS2013
+ hr = s_Device->QueryInterface(__uuidof(IDXGIDevice3), (void**) &s_DXGIDevice3);
+ DX_CHECK(hr, "s_Device->QueryInterface(__uuidof(IDXGIDevice3), (void**) &s_DXGIDevice3) with error 0x%08x ", hr);
+ #endif
+
+ hr = s_Device->QueryInterface(&dxgiDevice);
+ DX_CHECK(hr, "s_Device->QueryInterface(&dxgiDevice) failed with error 0x%08x ", hr);
+
+#if ENABLE_DX11_FRAME_LATENCY_WAITABLE_OBJECT
+ // The maximum frame latency should be 1, if game is running at 60 FPS, but because
+ // most games will probably be running around 30, I guess it's better to set 2
+ hr = dxgiDevice->SetMaximumFrameLatency(s_EnableLowLatencyPresentationAPI ? 2 : 1);
+#else
+ hr = dxgiDevice->SetMaximumFrameLatency(1);
+#endif
+ DX_CHECK(hr, "dxgiDevice->SetMaximumFrameLatency(1)) failed with error 0x%08x ", hr);
+
+ hr = dxgiDevice->GetAdapter(&dxgiAdapter);
+ DX_CHECK(hr, "dxgiDevice->GetAdapter(&dxgiAdapter) failed with error 0x%08x ", hr);
+
+ hr = dxgiAdapter->GetParent(__uuidof(s_DXGIFactory), reinterpret_cast<void**>(&s_DXGIFactory)); // ???
+ DX_CHECK(hr, "dxgiAdapter->GetParent(...) failed with error 0x%08x ", hr);
+
+ return true;
+
+
+
+error:
+
+ s_DXGIFactory.Free();
+ s_Context.Free();
+ s_Device.Free();
+
+ return false;
+}
+
+void GetSwapChainDesc1(DXGI_SWAP_CHAIN_DESC1& sd)
+{
+ // Found on forums, why BGRA is used here instead of RGBA:
+ // Microsoft dude - "I believe there are some flip optimizatoin benefits to using BGR rather than RGB."
+ // Update: So I changed the format to DXGI_FORMAT_R8G8B8A8_UNORM, checked the FPS in one of the games, didn't see any FPS loss
+ // But because all D3D11 gfxdevice was origanlly tested with DXGI_FORMAT_R8G8B8A8_UNORM, I think it makes sense to have this format instead
+ // It also makes image effects work correctly, for ex., it fixes bug - https://fogbugz.unity3d.com/default.asp?492440
+ sd.Format = kSwapChainBackBufferFormat;
+ sd.Stereo = FALSE;
+ sd.SampleDesc.Count = 1;
+ sd.SampleDesc.Quality = 0;
+ sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ sd.BufferCount = kSwapChainBackBufferCount;
+ sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+ sd.Flags = kSwapChainFlags;
+ sd.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
+}
+IDXGISwapChain1* CreateSwapChainForXAML(ISwapChainBackgroundPanelNative* panel, int width, int height)
+{
+ IDXGISwapChain1* chain;
+ DXGI_SWAP_CHAIN_DESC1 sd;
+ GetSwapChainDesc1(sd);
+
+ sd.Width = width;
+ sd.Height = height;
+
+ HRESULT hr;
+ sd.Scaling = DXGI_SCALING_STRETCH;
+ hr = s_DXGIFactory->CreateSwapChainForComposition(s_Device, &sd, nullptr, &chain);
+ DX_CHECK(hr, "CreateSwapChainForComposition failed with error 0x%08x", hr);
+
+#if UNITY_METRO_VS2013
+ UnityPlayer::AppCallbacks::Instance->InvokeOnUIThread(ref new UnityPlayer::AppCallbackItem([panel, chain]()
+ {
+ HRESULT hr = panel->SetSwapChain(chain);
+ if (FAILED(hr)) FatalErrorMsg("SetSwapChain failed with error 0x%08x", hr);
+ }
+ ), false);
+#else
+ hr = panel->SetSwapChain(chain);
+ if (FAILED(hr)) FatalErrorMsg("SetSwapChain failed with error 0x%08x", hr);
+#endif
+
+ return chain;
+error:
+ return NULL;
+}
+IDXGISwapChain1* CreateSwapChainForD3D(IUnknown* coreWindow, int width, int height)
+{
+ IDXGISwapChain1* chain;
+ DXGI_SWAP_CHAIN_DESC1 sd;
+ GetSwapChainDesc1(sd);
+
+ sd.Width = width;
+ sd.Height = height;
+
+ HRESULT hr;
+
+ sd.Scaling = DXGI_SCALING_STRETCH;
+ hr = s_DXGIFactory->CreateSwapChainForCoreWindow(s_Device, coreWindow, &sd, nullptr, &chain);
+ DX_CHECK(hr, "CreateSwapChainForCoreWindow failed with error 0x%08x", hr);
+
+ return chain;
+error:
+ return NULL;
+}
+bool InitializeOrResetD3D11SwapChain(GfxDevice* device, IDXGISwapChain1* chain, int width, int height, int vsynccount, int& outBackbufferBPP, int& outFrontbufferBPP, int& outDepthBPP, int& outFSAA)
+{
+ HRESULT hr;
+ Microsoft::WRL::ComPtr<ID3D11Texture2D> backBuffer;
+ Microsoft::WRL::ComPtr<ID3D11Texture2D> depthStencil;
+
+ //
+
+ Assert(nullptr != device);
+ Assert(nullptr != s_Device);
+ Assert(nullptr != s_Context);
+ Assert(nullptr != s_DXGIFactory);
+
+ //
+
+ outBackbufferBPP = 4;
+ outFrontbufferBPP = 4;
+ outDepthBPP = 4;
+ outFSAA = 0;
+
+
+ SAFE_RELEASE(s_DepthStencil.m_Texture);
+ SAFE_RELEASE(s_DepthStencil.m_SRView);
+ SAFE_RELEASE(s_DepthStencil.m_DSView);
+ s_BackBuffer.Reset();
+
+ //
+
+ s_Context->OMSetRenderTargets(0, nullptr, nullptr);
+ s_Context->Flush();
+
+ s_SyncInterval = vsynccount;
+
+ if (nullptr == s_SwapChain)
+ {
+ s_SwapChain = chain;
+#if ENABLE_DX11_FRAME_LATENCY_WAITABLE_OBJECT
+ s_FrameLatencyWaitableObject = s_EnableLowLatencyPresentationAPI ?
+ ((IDXGISwapChain2*) s_SwapChain)->GetFrameLatencyWaitableObject() :
+ NULL;
+#endif
+ }
+ else
+ {
+ // Use efficient DX 11.2 swap chain scaling if available
+#if 0 && UNITY_METRO_VS2013
+
+ // Check if new resolution fits into already allocated swap chain
+ DXGI_SWAP_CHAIN_DESC swapChainDesc;
+ s_SwapChain->GetDesc(&swapChainDesc);
+ if (width <= swapChainDesc.BufferDesc.Width && height <= swapChainDesc.BufferDesc.Height)
+ {
+ hr = ((IDXGISwapChain2*)s_SwapChain)->SetSourceSize(width, height);
+ DX_CHECK(hr, "SetSourceSize failed with error 0x%08x", hr);
+ }
+ else
+#endif
+ {
+ // Note: If you set third parameter as DXGI_FORMAT_UNKNOWN, D3D11 debugger incorrectly captures frame when swap chain is resized, for ex., when you perform snapping
+ hr = s_SwapChain->ResizeBuffers(kSwapChainBackBufferCount, width, height, kSwapChainBackBufferFormat, kSwapChainFlags);
+ DX_CHECK(hr, "ResizeBuffers failed with error 0x%08x", hr);
+ }
+ }
+
+ hr = s_SwapChain->GetBuffer(0, __uuidof(backBuffer), reinterpret_cast<void**>(backBuffer.GetAddressOf()));
+ DX_CHECK(hr, "s_SwapChain->GetBuffer failed with error 0x%08x", hr);
+
+ D3D11_TEXTURE2D_DESC backBufferDesc;
+ backBuffer->GetDesc(&backBufferDesc);
+
+ width = backBufferDesc.Width;
+ height = backBufferDesc.Height;
+
+ s_BackBuffer.m_Texture = backBuffer.Detach();
+ s_BackBuffer.width = width;
+ s_BackBuffer.height = height;
+ s_BackBuffer.format = kRTFormatARGB32;
+
+ device->SetCurrentWindowSize(width, height);
+
+ ID3D11RenderTargetView* rtv = 0;
+ hr = s_Device->CreateRenderTargetView(s_BackBuffer.m_Texture, nullptr, &rtv);
+ DX_CHECK(hr, "CreateRenderTargetView failed with error 0x%08x", hr);
+ s_BackBuffer.SetRTV(0,0,false,rtv);
+
+ hr = s_Device->CreateTexture2D(&CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_D24_UNORM_S8_UINT, width, height, 1, 1, D3D11_BIND_DEPTH_STENCIL), nullptr, &depthStencil);
+ DX_CHECK(hr, "CreateTexture2D failed with error 0x%08x", hr);
+
+ device->SetFramebufferDepthFormat(kDepthFormat24);
+ if (IsGfxDevice())
+ GetGfxDevice().SetFramebufferDepthFormat(kDepthFormat24);
+
+ s_DepthStencil.m_Texture = depthStencil.Get();
+
+ hr = s_Device->CreateDepthStencilView(depthStencil.Get(), &CD3D11_DEPTH_STENCIL_VIEW_DESC(D3D11_DSV_DIMENSION_TEXTURE2D), &s_DepthStencil.m_DSView); // ???
+ DX_CHECK(hr, "CreateDepthStencilView failed with error 0x%08x", hr);
+
+ s_BackBuffer.backBuffer = true;
+ s_DepthStencil.backBuffer = true;
+
+ ActivateD3D11BackBuffer(device);
+
+ return true;
+
+error:
+
+ SAFE_RELEASE(s_DepthStencil.m_Texture);
+ SAFE_RELEASE(s_DepthStencil.m_SRView);
+ SAFE_RELEASE(s_DepthStencil.m_DSView);
+ s_BackBuffer.Reset();
+
+ SAFE_RELEASE(s_SwapChain);
+#if UNITY_METRO_VS2013
+ SAFE_RELEASE(s_DXGIDevice3);
+#endif
+ return false;
+}
+
+void ActivateD3D11BackBuffer(GfxDevice* device)
+{
+ Assert(nullptr != device);
+ device->SetRenderTargets(1, &RenderSurfaceHandle(&s_BackBuffer), RenderSurfaceHandle(&s_DepthStencil));
+}
+
+void DestroyD3D11Device()
+{
+ // This can happen when quiting from screen selector - window is not set up yet
+ if( !s_Device || !s_Context || !s_DXGIFactory)
+ return;
+
+ // swap chain must go out of fullscreen before releasing it
+ if (s_SwapChain)
+ {
+ s_SwapChain->SetFullscreenState (FALSE, NULL);
+ }
+
+ // cleanup
+ SAFE_RELEASE(s_DepthStencil.m_Texture);
+ SAFE_RELEASE(s_DepthStencil.m_SRView);
+ SAFE_RELEASE(s_DepthStencil.m_DSView);
+ s_BackBuffer.Reset();
+ SAFE_RELEASE(s_SwapChain);
+#if UNITY_METRO_VS2013
+ SAFE_RELEASE(s_DXGIDevice3);
+#endif
+ s_Context->ClearState();
+ s_Context->Flush();
+
+ s_Context->Release();
+ s_Context = NULL;
+ s_Device->Release();
+ s_Device = NULL;
+ s_DXGIFactory->Release();
+ s_DXGIFactory = NULL;
+}
+
+void CleanupD3D11()
+{
+ AssertIf (s_Device || s_Context);
+}
+
+ID3D11Device* GetD3D11Device()
+{
+ AssertIf( !s_Device );
+ return s_Device;
+}
+
+ID3D11Device1* GetD3D11_1Device()
+{
+ AssertIf( !s_Device );
+ return s_Device;
+}
+
+
+ID3D11DeviceContext* GetD3D11Context(bool expectNull)
+{
+ if (!expectNull)
+ Assert( s_Context );
+ return s_Context;
+}
+#if ENABLE_DX11_FRAME_LATENCY_WAITABLE_OBJECT
+HANDLE GetFrameLatencyWaitableObject()
+{
+ return s_FrameLatencyWaitableObject;
+}
+void WaitOnSwapChain()
+{
+ if (s_EnableLowLatencyPresentationAPI)
+ {
+ DWORD result = WaitForSingleObjectEx(
+ s_FrameLatencyWaitableObject,
+ 1000, // 1 second timeout (shouldn't ever occur)
+ true
+ );
+ }
+}
+#endif
+
+#if UNITY_METRO_VS2013
+IDXGIDevice3* GetIDXGIDevice3()
+{
+ return s_DXGIDevice3;
+}
+#endif
+#endif
diff --git a/Runtime/GfxDevice/d3d11/D3D11Context_WP8.cpp b/Runtime/GfxDevice/d3d11/D3D11Context_WP8.cpp
new file mode 100644
index 0000000..dbf8613
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/D3D11Context_WP8.cpp
@@ -0,0 +1,233 @@
+#include "UnityPrefix.h"
+
+#if UNITY_WP8
+
+#include "D3D11Context.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+#include "D3D11Includes.h"
+#include "D3D11Utils.h"
+#include "TexturesD3D11.h"
+#include "TimerQueryD3D11.h"
+#include "Runtime/Misc/Plugins.h"
+#include "Runtime/Misc/QualitySettings.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Utilities/LogUtility.h"
+#include "Runtime/Utilities/Argv.h"
+#include "Runtime/Graphics/ScreenManager.h"
+
+SupportedFeatureLevels GetSupportedFeatureLevels()
+{
+ SupportedFeatureLevels features;
+
+ features.push_back(D3D_FEATURE_LEVEL_9_3);
+
+ return features;
+}
+
+#if ENABLE_PROFILER
+D3D11PERF_BeginEventFunc g_D3D11BeginEventFunc = NULL;
+D3D11PERF_EndEventFunc g_D3D11EndEventFunc = NULL;
+#endif
+
+using namespace Microsoft::WRL;
+
+
+static ComPtr<ID3D11Device1> s_Device;
+static ComPtr<ID3D11DeviceContext1> s_Context;
+
+static RenderColorSurfaceD3D11 s_BackBuffer;
+static RenderDepthSurfaceD3D11 s_DepthStencil;
+
+ID3D11RenderTargetView* g_D3D11CurrRT;
+ID3D11DepthStencilView* g_D3D11CurrDS;
+ID3D11Resource* g_D3D11CurrRTResource;
+ID3D11Resource* g_D3D11CurrDSResource;
+RenderColorSurfaceD3D11* g_D3D11CurrColorRT;
+RenderDepthSurfaceD3D11* g_D3D11CurrDepthRT;
+
+
+HRESULT UpdateD3D11Device(ID3D11Device1* device, ID3D11DeviceContext1* deviceContext, ID3D11RenderTargetView* renderTargetView, int& width, int& height)
+{
+ HRESULT hr;
+
+ Assert(device);
+
+ // update device context
+
+ if (deviceContext)
+ s_Context = deviceContext;
+ else
+ device->GetImmediateContext1(s_Context.ReleaseAndGetAddressOf());
+
+ // update device
+
+ if (s_Device.Get() != device)
+ {
+ s_Context->OMSetRenderTargets(0, nullptr, nullptr);
+ s_BackBuffer.Reset();
+ s_DepthStencil.Reset();
+
+ //@TODO: have to recreate all graphics resources,
+ // Runtime/GfxDevice/GfxDeviceRecreate.h
+ //
+ // CleanupAllGfxDeviceResources should be called while the "old device" is still active;
+ // RecreateAllGfxDeviceResources should be called when the "new device" is ready.
+ //
+ // Right now that works fine (tm) for switching editor between dx9 & dx11 in player settings,
+ // but possibly needs some adjustment for WP8.
+
+ s_Device = device;
+ PluginsSetGraphicsDevice(device, kGfxRendererD3D11, kGfxDeviceEventInitialize);
+
+ void RecreateAllGfxDeviceResources();
+
+ if (deviceContext)
+ RecreateAllGfxDeviceResources();
+ }
+
+ // update render target
+
+ if (renderTargetView)
+ {
+ // get back buffer
+
+ ComPtr<ID3D11Texture2D> backBuffer;
+
+ {
+ ComPtr<ID3D11Resource> resource;
+ renderTargetView->GetResource(resource.GetAddressOf());
+
+ hr = resource.As(&backBuffer);
+ Assert(SUCCEEDED(hr));
+ }
+
+ // get description
+
+ D3D11_TEXTURE2D_DESC backBufferDesc;
+ backBuffer->GetDesc(&backBufferDesc);
+
+ // get actual resolution
+
+ width = backBufferDesc.Width;
+ height = backBufferDesc.Height;
+
+ bool const sizeChanged = ((s_BackBuffer.width != backBufferDesc.Width) || (s_BackBuffer.height != backBufferDesc.Height));
+
+ // update back buffer
+
+ s_Context->OMSetRenderTargets(0, nullptr, nullptr);
+ Assert(DXGI_FORMAT_B8G8R8A8_UNORM == backBufferDesc.Format);
+
+ s_BackBuffer.Reset();
+ s_BackBuffer.width = backBufferDesc.Width;
+ s_BackBuffer.height = backBufferDesc.Height;
+ s_BackBuffer.backBuffer = true;
+ s_BackBuffer.format = kRTFormatARGB32;
+ s_BackBuffer.backBuffer = true;
+ s_BackBuffer.m_Texture = backBuffer.Detach();
+
+ s_BackBuffer.SetRTV(0, 0, false, renderTargetView);
+ renderTargetView->AddRef();
+
+ // update depth/stencil buffer if sice changed
+
+ if (sizeChanged)
+ {
+ s_DepthStencil.Reset();
+
+ // create depth/stencil texture
+
+ ComPtr<ID3D11Texture2D> depthStencil;
+
+ hr = s_Device->CreateTexture2D(&CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_D24_UNORM_S8_UINT, backBufferDesc.Width, backBufferDesc.Height, 1, 1, D3D11_BIND_DEPTH_STENCIL), nullptr, &depthStencil);
+ Assert(SUCCEEDED(hr));
+
+ if (FAILED(hr))
+ return hr;
+
+ // create depth/stencil view
+
+ hr = s_Device->CreateDepthStencilView(depthStencil.Get(), &CD3D11_DEPTH_STENCIL_VIEW_DESC(D3D11_DSV_DIMENSION_TEXTURE2D), &s_DepthStencil.m_DSView); // ???
+ Assert(SUCCEEDED(hr));
+
+ if (FAILED(hr))
+ return hr;
+
+ // store depth/stencil buffer
+
+ s_DepthStencil.m_Texture = depthStencil.Detach();
+ s_DepthStencil.width = backBufferDesc.Width;
+ s_DepthStencil.height = backBufferDesc.Height;
+ s_DepthStencil.backBuffer = true; // ?!-
+ s_DepthStencil.depthFormat = kDepthFormat24;
+
+ GetGfxDevice().SetFramebufferDepthFormat(kDepthFormat24);
+ }
+
+ // set active render target
+
+ ActivateD3D11BackBuffer(&GetGfxDevice()); // ?!-
+ }
+
+ return S_OK;
+}
+
+bool InitializeD3D11()
+{
+ Assert(s_Device);
+ Assert(s_Context);
+ return true;
+}
+
+void CleanupD3D11()
+{
+ Assert(!s_Device);
+ Assert(!s_Context);
+}
+
+void ActivateD3D11BackBuffer(GfxDevice* device)
+{
+ Assert(nullptr != device);
+ device->SetRenderTargets(1, &RenderSurfaceHandle(&s_BackBuffer), RenderSurfaceHandle(&s_DepthStencil));
+}
+
+void DestroyD3D11Device()
+{
+ if (!s_Device || !s_Context)
+ return;
+
+ SAFE_RELEASE(s_DepthStencil.m_Texture);
+ SAFE_RELEASE(s_DepthStencil.m_SRView);
+ SAFE_RELEASE(s_DepthStencil.m_DSView);
+ s_BackBuffer.Reset();
+
+ s_Context->ClearState();
+ s_Context->Flush();
+
+ s_Context.Reset();
+ s_Device.Reset();
+}
+
+ID3D11Device* GetD3D11Device()
+{
+ AssertIf(!s_Device);
+ return s_Device.Get();
+}
+
+ID3D11Device1* GetD3D11_1Device()
+{
+ AssertIf( !s_Device );
+ return s_Device.Get();
+}
+
+ID3D11DeviceContext* GetD3D11Context(bool expectNull)
+{
+ if (!expectNull)
+ Assert(s_Context);
+ return s_Context.Get();
+}
+
+#endif
diff --git a/Runtime/GfxDevice/d3d11/D3D11Debug.cpp b/Runtime/GfxDevice/d3d11/D3D11Debug.cpp
new file mode 100644
index 0000000..f6ed253
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/D3D11Debug.cpp
@@ -0,0 +1,282 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "D3D11Includes.h"
+
+
+#if ENABLE_DX11_DEBUGGING
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "D3D11VBO.h"
+
+
+static bool s_DX11LoggingEnabled = true;
+static UInt32 s_DX11CommandCount = 0;
+static UInt32 s_Indentation = 0;
+static DbgFrameDebugType s_FrameDebugType = kFrameDebugOutputDX11Commands;
+//static DbgFrameDebugType s_FrameDebugType = kFrameDebugOutputNone;
+static bool s_BreakpointReached = FALSE;
+
+
+void DbgDX11DisableLogging()
+{
+ s_DX11LoggingEnabled = false;
+}
+void DbgDX11EnableLogging()
+{
+ s_DX11LoggingEnabled = true;
+}
+void DbgDX11LogOutput(const char* format,...)
+{
+ if (!s_DX11LoggingEnabled || s_FrameDebugType == kFrameDebugOutputNone) return;
+
+ va_list va;
+ va_start (va, format);
+ char buffer [1024 * 10];
+ vsnprintf (buffer, sizeof(buffer), format, va);
+ va_end (va);
+ printf_console("%*s[%d]%s\n", s_Indentation, "", ++s_DX11CommandCount, buffer);
+
+}
+
+void DbgDX11IncreaseIndentation()
+{
+ s_Indentation++;
+}
+void DbgDX11DecreaseIndentation()
+{
+ s_Indentation--;
+}
+void DbgDX11PostOperation()
+{
+ if (!s_DX11LoggingEnabled) return;
+}
+void DbgDX11MarkDrawing(UInt32 triCount, UInt32 vertexCount)
+{
+ if (s_FrameDebugType != kFrameDebugOutputDX11CommandsWithStepping) return;
+
+ printf_console("Submitted draw call, triangle count: %d, vertex count: %d\n", triCount, vertexCount);
+ printf_console("------------------------------------------------------------------------------------------------\n");
+ DbgDX11ShowCurrentGfxState();
+ printf_console("------------------------------------------------------------------------------------------------\n");
+
+ /*
+ RenderColorSurfaceWiiU* colorSurface = InternalGetActiveRenderColorSurfaceWiiU(0);
+ if (colorSurface != NULL)
+ {
+ DX11SwapBuffers(&colorSurface->colorBuffer);
+ }
+ else
+ {
+ DX11SwapBuffers(cafe::GetColorBuffer());
+ }
+// DX11_CHK (DX11SwapBuffers(&s_ColorBuffer));
+
+ cafe::SetContextState();
+
+ DX11_CHK (DX11Flush());
+ printf_console("A - Contine, B - Cancel, + - Capture screenshot\n");
+
+ bool done = false;
+
+ while (!done)
+ {
+
+ switch (cafe::DbgWaitForKey())
+ {
+ case KPAD_BUTTON_A:
+ done = true;
+ break;
+ case KPAD_BUTTON_B:
+ DbgDX11SetFrameDebug(kFrameDebugOutputNone);
+ break;
+ case KPAD_BUTTON_PLUS:
+ cafe::DbgCaptureScreenshot();
+ break;
+ default:
+ #if ENABLE_DX11_METRICS
+ DbgDX11SetBreakpoint();
+ DX11ClearGPMetric();
+ #endif
+ break;
+ }
+ }
+ */
+}
+void DbgDX11SetFrameDebug(DbgFrameDebugType type)
+{
+ s_DX11CommandCount = 0;
+
+ switch (s_FrameDebugType)
+ {
+ case kFrameDebugOutputDX11CommandsWithStepping:
+ break;
+ }
+
+ s_FrameDebugType = type;
+
+ switch (s_FrameDebugType)
+ {
+ case kFrameDebugOutputDX11CommandsWithStepping:
+ break;
+ }
+}
+
+void DbgDX11MarkFrameBegin()
+{
+ switch (s_FrameDebugType)
+ {
+ case kFrameDebugOutputDX11CommandsWithStepping:
+ break;
+ }
+}
+void DbgDX11MarkFrameEnd()
+{
+ DbgDX11SetFrameDebug(kFrameDebugOutputNone);
+}
+
+
+const char* DbgDX11GetShaderChannelName(ShaderChannel channel)
+{
+ const char* kShaderChannelStrings[kShaderChannelCount] = {
+ "kShaderChannelVertex",
+ "kShaderChannelNormal",
+ "kShaderChannelColor",
+ "kShaderChannelTexCoord0",
+ "kShaderChannelTexCoord1",
+ "kShaderChannelTangent"
+ };
+ return kShaderChannelStrings[channel];
+}
+const char* DbgDX11GetVertexComponentName(VertexComponent comp)
+{
+ const char* kVertexComponentStrings[kVertexCompCount] = {
+ "kVertexCompVertex",
+ "kVertexCompColor",
+ "kVertexCompNormal",
+ "kVertexCompTexCoord",
+ "kVertexCompTexCoord0", "kVertexCompTexCoord1", "kVertexCompTexCoord2", "kVertexCompTexCoord3",
+ "kVertexCompTexCoord4", "kVertexCompTexCoord5", "kVertexCompTexCoord6", "kVertexCompTexCoord7",
+ "kVertexCompAttrib0", "kVertexCompAttrib1", "kVertexCompAttrib2", "kVertexCompAttrib3",
+ "kVertexCompAttrib4", "kVertexCompAttrib5", "kVertexCompAttrib6", "kVertexCompAttrib7",
+ "kVertexCompAttrib8", "kVertexCompAttrib9", "kVertexCompAttrib10", "kVertexCompAttrib11",
+ "kVertexCompAttrib12", "kVertexCompAttrib13", "kVertexCompAttrib14", "kVertexCompAttrib15"
+ };
+ return kVertexComponentStrings[comp];
+}
+void DbgDX11ShowChannelBindings(const ChannelAssigns& channels, VertexBufferData& data)
+{
+ /*
+ DX11_LOG_OUTPUT("VBO layout, VertexStride: %d", data.vertexStride);
+ for (int i = 0; i < kShaderChannelCount; i++)
+ {
+ DX11_LOG_OUTPUT(" [%d] - 0x%08x, Stride: %d (%s)", i, data.channel[i], data.channelStrides[i], DbgDX11GetShaderChannelName((ShaderChannel)i));
+ }
+ for (int i = kVertexCompVertex; i < kVertexCompCount; i++)
+ {
+ ShaderChannel sh = channels.GetSourceForTarget((VertexComponent)i);
+ if (sh != kShaderChannelNone)
+ {
+ DX11_LOG_OUTPUT(" Vertex component %d targets %s", i, DbgDX11GetShaderChannelName((ShaderChannel)sh));
+ }
+ }
+ */
+}
+void DbgDX11ShowStridedVertexData(const VertexBufferData& data, void* rawData)
+{
+ /*
+ DX11_LOG_OUTPUT("VBO has %d vertices with stride %d", data.vertexCount, data.vertexStride);
+
+ UInt8* p = (UInt8*) rawData;
+
+ int offset[kShaderChannelCount];
+ int curOffset = 0;
+ for (int i = 0; i < kShaderChannelCount; i++)
+ {
+ if (data.channel[i] == NULL) offset[i] = -1;
+ else
+ {
+ offset[i] = curOffset;
+ curOffset += VBO::GetChannelByteSize(i);
+ }
+ }
+ Assert (VBO::GetChannelByteSize(kShaderChannelVertex) == sizeof(Vector3f));
+ Assert (VBO::GetChannelByteSize(kShaderChannelNormal) == sizeof(Vector3f));
+ Assert (VBO::GetChannelByteSize(kShaderChannelColor) == sizeof(ColorRGBA32));
+ Assert (VBO::GetChannelByteSize(kShaderChannelTexCoord0) == sizeof(Vector2f));
+ Assert (VBO::GetChannelByteSize(kShaderChannelTexCoord1) == sizeof(Vector2f));
+ Assert (VBO::GetChannelByteSize(kShaderChannelTangent) == sizeof(Vector4f));
+ for (int i = 0; i < data.vertexCount; i++)
+ {
+ if (offset[kShaderChannelVertex] != -1)
+ {
+ Vector3f v = *(Vector3f*)(p + offset[kShaderChannelVertex] + i * data.vertexStride);
+ DX11_LOG_OUTPUT("[%d - Vertex] - %.2f %.2f %.2f", i, v.x, v.y, v.z);
+ }
+
+ if (offset[kShaderChannelNormal] != -1)
+ {
+ Vector3f n = *(Vector3f*)(p + offset[kShaderChannelNormal] + i * data.vertexStride);
+ DX11_LOG_OUTPUT("[%d - Normal] - %.2f %.2f %.2f", i, n.x, n.y, n.z);
+ }
+ if (offset[kShaderChannelColor] != -1)
+ {
+ ColorRGBA32 c = *(ColorRGBA32*)(p + offset[kShaderChannelColor] + i * data.vertexStride);
+ DX11_LOG_OUTPUT("[%d - Color] - %d %d %d %d", i, c.r, c.g, c.b, c.a);
+ }
+ if (offset[kShaderChannelTexCoord0] != -1)
+ {
+ Vector2f uv = *(Vector2f*)(p + offset[kShaderChannelTexCoord0] + i * data.vertexStride);
+ DX11_LOG_OUTPUT("[%d - UV0] - %.2f %.2f", i, uv.x, uv.x, uv.y);
+ }
+ if (offset[kShaderChannelTexCoord1] != -1)
+ {
+ Vector2f uv = *(Vector2f*)(p + offset[kShaderChannelTexCoord1] + i * data.vertexStride);
+ DX11_LOG_OUTPUT("[%d - UV1] - %.2f %.2f", i, uv.x, uv.x, uv.y);
+ }
+ if (offset[kShaderChannelTangent] != -1)
+ {
+ Vector4f t = *(Vector4f*)(p + offset[kShaderChannelTangent] + i * data.vertexStride);
+ DX11_LOG_OUTPUT("[%d - Tangent] - %.2f %.2f %.2f %.2f", i, t.x, t.y, t.z, t.w);
+ }
+ }
+ */
+ FatalErrorMsg("Stop");
+}
+void DbgDX11ShowStridedIndexData(const IndexBufferData& data, void* rawData)
+{
+ /*
+ DX11_LOG_OUTPUT("VBO has %d indices with stride %d", data.count, data.stride);
+ UInt8* p = (UInt8*) rawData;
+ for (int i = 0; i < data.count; i++)
+ {
+ Assert (data.stride == sizeof(UInt16));
+ DX11_LOG_OUTPUT("[%d] - %d", i, *(UInt16*)(p + data.stride * i));
+ }
+ */
+ FatalErrorMsg("Stop");
+}
+
+
+
+void DbgDX11ShowCurrentGfxState()
+{
+ /*
+ const DeviceStateWiiU& state = GetWiiUDeviceState(GetGfxDevice());
+
+ int blendingEnabled = state.currBlendState->blendingEnabled;
+ DbgDX11ShowColorControlReg(state.colorControlReg, blendingEnabled);
+ if (blendingEnabled > 0)
+ {
+ DbgDX11ShowBlendControlReg(state.blendControlReg);
+ }
+
+ DbgDX11ShowTargetChannelMaskReg(state.targetChannelMaskReg);
+ DbgDX11ShowAlphaTestReg(state.alphaTestReg);
+ DbgDX11ShowPolygonControlReg(state.polygonControlReg);
+ //DbgDX11ShowPolygonOffsetReg(state.polygonOffsetReg);
+ DbgDX11ShowDepthStencilControlReg(state.depthStencilReg);
+ */
+}
+
+
+#endif
diff --git a/Runtime/GfxDevice/d3d11/D3D11Debug.h b/Runtime/GfxDevice/d3d11/D3D11Debug.h
new file mode 100644
index 0000000..1f57adf
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/D3D11Debug.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#define ENABLE_DX11_DEBUGGING (!UNITY_RELEASE && UNITY_METRO) && 0
+
+#if ENABLE_DX11_DEBUGGING
+
+ enum DbgFrameDebugType
+ {
+ kFrameDebugOutputNone,
+ kFrameDebugOutputDX11Commands,
+ kFrameDebugOutputDX11CommandsWithStepping
+ };
+
+ void DbgDX11DisableLogging();
+ void DbgDX11EnableLogging();
+ void DbgDX11LogOutput(const char* format,...);
+ void DbgDX11IncreaseIndentation();
+ void DbgDX11DecreaseIndentation();
+ void DbgDX11MarkDrawing(UInt32 triCount, UInt32 vertexCount);
+ void DbgDX11MarkFrameBegin();
+ void DbgDX11MarkFrameEnd();
+ void DbgDX11ShowCurrentGfxState();
+
+ class DbgDX11AutoIndentation
+ {
+ public:
+ DbgDX11AutoIndentation() {DbgDX11IncreaseIndentation();}
+ ~DbgDX11AutoIndentation() {DbgDX11DecreaseIndentation();}
+ };
+
+ inline const char* GetDX11BoolString (bool val) {return val == true ? "true" : "false";}
+
+ #define DX11_DISABLE_LOGGING() DbgDX11DisableLogging()
+ #define DX11_ENABLE_LOGGING() DbgDX11EnableLogging()
+ #define DX11_LOG_OUTPUT(...) DbgDX11LogOutput(__VA_ARGS__);
+ #define DX11_LOG_ENTER_FUNCTION(...) DbgDX11LogOutput(__VA_ARGS__); DbgDX11AutoIndentation sDbgDX11AutoIndentation;
+ #define DX11_MARK_DRAWING(TriCount, VertexCount) DbgDX11MarkDrawing(TriCount, VertexCount);
+ #define DX11_MARK_FRAME_BEGIN() DbgDX11MarkFrameBegin();
+ #define DX11_MARK_FRAME_END() DbgDX11MarkFrameEnd();
+ #define DX11_CHK(x) {DbgDX11LogOutput(#x); x; }
+
+#else
+ #define DX11_DISABLE_LOGGING() {}
+ #define DX11_ENABLE_LOGGING() {}
+ #define DX11_LOG_OUTPUT(...) {}
+ #define DX11_LOG_OUTPUT_MTX3x4(m) {}
+ #define DX11_LOG_OUTPUT_MTX4x4(m) {}
+ #define DX11_LOG_ENTER_FUNCTION(...) {}
+ #define DX11_MARK_FRAME_BEGIN() {}
+ #define DX11_MARK_FRAME_END() {}
+ #define DX11_CHK(x) x
+ #define DX11_MARK_DRAWING(TriCount, VertexCount) {}
+ #define DX11_SET_FRAME_DEBUG(Type) {}
+#endif
diff --git a/Runtime/GfxDevice/d3d11/D3D11Hash.cpp b/Runtime/GfxDevice/d3d11/D3D11Hash.cpp
new file mode 100644
index 0000000..f4f692f
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/D3D11Hash.cpp
@@ -0,0 +1,326 @@
+// Original MD5 implementation put into public domain
+// by Alexander Peslyak, see comment below.
+// Modifications for D3D hash by Aras Pranckevicius, public domain.
+
+// D3D shader hash seems to be almost MD5, except small differences in
+// the final step (MD5_Final in OpenSSL). Look for "CHANGE FROM MD5".
+
+
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
+ * MD5 Message-Digest Algorithm (RFC 1321).
+ *
+ * Homepage:
+ * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
+ *
+ * Author:
+ * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
+ *
+ * This software was written by Alexander Peslyak in 2001. No copyright is
+ * claimed, and the software is hereby placed in the public domain.
+ * In case this attempt to disclaim copyright and place the software in the
+ * public domain is deemed null and void, then the software is
+ * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * (This is a heavily cut-down "BSD license".)
+ *
+ * This differs from Colin Plumb's older public domain implementation in that
+ * no exactly 32-bit integer data type is required (any 32-bit or wider
+ * unsigned integer data type will do), there's no compile-time endianness
+ * configuration, and the function prototypes match OpenSSL's. No code from
+ * Colin Plumb's implementation has been reused; this comment merely compares
+ * the properties of the two independent implementations.
+ *
+ * The primary goals of this implementation are portability and ease of use.
+ * It is meant to be fast, but not as fast as possible. Some known
+ * optimizations are not included to reduce source code size and avoid
+ * compile-time configuration.
+ */
+
+/* Any 32-bit or wider unsigned integer data type will do */
+typedef unsigned int MD5_u32plus;
+
+typedef struct {
+ MD5_u32plus a, b, c, d;
+ MD5_u32plus hi;
+ MD5_u32plus lo; // CHANGE FROM MD5: need to have "lo" go directly before the buffer
+ unsigned char buffer[64];
+ MD5_u32plus block[16];
+} MD5_CTX;
+
+extern void MD5_Init(MD5_CTX *ctx);
+extern void MD5_Update(MD5_CTX *ctx, void *data, unsigned long size);
+extern void MD5_Final(unsigned char *result, MD5_CTX *ctx);
+
+
+#include <string.h>
+
+/*
+ * The basic MD5 functions.
+ *
+ * F and G are optimized compared to their RFC 1321 definitions for
+ * architectures that lack an AND-NOT instruction, just like in Colin Plumb's
+ * implementation.
+ */
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+
+/*
+ * The MD5 transformation for all four rounds.
+ */
+#define STEP(f, a, b, c, d, x, t, s) \
+ (a) += f((b), (c), (d)) + (x) + (t); \
+ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
+ (a) += (b);
+
+/*
+ * SET reads 4 input bytes in little-endian byte order and stores them
+ * in a properly aligned word in host byte order.
+ *
+ * The check for little-endian architectures that tolerate unaligned
+ * memory accesses is just an optimization. Nothing will break if it
+ * doesn't work.
+ */
+#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) || defined(_MSC_VER)
+#define SET(n) \
+ (*(MD5_u32plus *)&ptr[(n) * 4])
+#define GET(n) \
+ SET(n)
+#else
+#define SET(n) \
+ (ctx->block[(n)] = \
+ (MD5_u32plus)ptr[(n) * 4] | \
+ ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
+ ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
+ ((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
+#define GET(n) \
+ (ctx->block[(n)])
+#endif
+
+/*
+ * This processes one or more 64-byte data blocks, but does NOT update
+ * the bit counters. There are no alignment requirements.
+ */
+static void *body(MD5_CTX *ctx, void *data, unsigned long size)
+{
+ unsigned char *ptr;
+ MD5_u32plus a, b, c, d;
+ MD5_u32plus saved_a, saved_b, saved_c, saved_d;
+
+ ptr = (unsigned char*)data;
+
+ a = ctx->a;
+ b = ctx->b;
+ c = ctx->c;
+ d = ctx->d;
+
+ do {
+ saved_a = a;
+ saved_b = b;
+ saved_c = c;
+ saved_d = d;
+
+/* Round 1 */
+ STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
+ STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
+ STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
+ STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
+ STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
+ STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
+ STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
+ STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
+ STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
+ STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
+ STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
+ STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
+ STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
+ STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
+ STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
+ STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
+
+/* Round 2 */
+ STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
+ STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
+ STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
+ STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
+ STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
+ STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
+ STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
+ STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
+ STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
+ STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
+ STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
+ STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
+ STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
+ STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
+ STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
+ STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
+
+/* Round 3 */
+ STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
+ STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
+ STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
+ STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
+ STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
+ STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
+ STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
+ STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
+ STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
+ STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
+ STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
+ STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
+ STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
+ STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
+ STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
+ STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
+
+/* Round 4 */
+ STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
+ STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
+ STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
+ STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
+ STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
+ STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
+ STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
+ STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
+ STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
+ STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
+ STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
+ STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
+ STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
+ STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
+ STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
+ STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
+
+ a += saved_a;
+ b += saved_b;
+ c += saved_c;
+ d += saved_d;
+
+ ptr += 64;
+ } while (size -= 64);
+
+ ctx->a = a;
+ ctx->b = b;
+ ctx->c = c;
+ ctx->d = d;
+
+ return ptr;
+}
+
+void MD5_Init(MD5_CTX *ctx)
+{
+ ctx->a = 0x67452301;
+ ctx->b = 0xefcdab89;
+ ctx->c = 0x98badcfe;
+ ctx->d = 0x10325476;
+
+ ctx->lo = 0;
+ ctx->hi = 0;
+}
+
+void MD5_Update(MD5_CTX *ctx, void *data, unsigned long size)
+{
+ MD5_u32plus saved_lo;
+ unsigned long used, free;
+
+ saved_lo = ctx->lo;
+ if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
+ ctx->hi++;
+ ctx->hi += size >> 29;
+
+ used = saved_lo & 0x3f;
+
+ if (used) {
+ free = 64 - used;
+
+ if (size < free) {
+ memcpy(&ctx->buffer[used], data, size);
+ return;
+ }
+
+ memcpy(&ctx->buffer[used], data, free);
+ data = (unsigned char *)data + free;
+ size -= free;
+ body(ctx, ctx->buffer, 64);
+ }
+
+ if (size >= 64) {
+ data = body(ctx, data, size & ~(unsigned long)0x3f);
+ size &= 0x3f;
+ }
+
+ memcpy(ctx->buffer, data, size);
+}
+
+void MD5_Final(unsigned char *result, MD5_CTX *ctx)
+{
+ unsigned long used, free;
+
+ used = ctx->lo & 0x3f;
+
+ ctx->buffer[used++] = 0x80;
+
+ free = 64 - used;
+
+ if (free < 8) {
+ memset(&ctx->buffer[used], 0, free);
+ body(ctx, ctx->buffer, 64);
+ used = 0;
+ free = 64;
+ }
+
+ memset(&ctx->buffer[used], 0, free - 8);
+
+ // CHANGE FROM MD5: put lo*2|1 instead of ctx->lo at buffer[56]
+ MD5_u32plus lo2 = ctx->lo * 2 | 1;
+ ctx->lo <<= 3;
+ ctx->buffer[56] = lo2;
+ ctx->buffer[57] = lo2 >> 8;
+ ctx->buffer[58] = lo2 >> 16;
+ ctx->buffer[59] = lo2 >> 24;
+ ctx->buffer[60] = ctx->hi;
+ ctx->buffer[61] = ctx->hi >> 8;
+ ctx->buffer[62] = ctx->hi >> 16;
+ ctx->buffer[63] = ctx->hi >> 24;
+
+ // CHANGE FROM MD5: pass buffer-4 (that contains lo)
+ // instead of buffer. Essentially the buffer is shifted by 4 bytes
+ body(ctx, &ctx->lo, 64);
+
+ result[0] = ctx->a;
+ result[1] = ctx->a >> 8;
+ result[2] = ctx->a >> 16;
+ result[3] = ctx->a >> 24;
+ result[4] = ctx->b;
+ result[5] = ctx->b >> 8;
+ result[6] = ctx->b >> 16;
+ result[7] = ctx->b >> 24;
+ result[8] = ctx->c;
+ result[9] = ctx->c >> 8;
+ result[10] = ctx->c >> 16;
+ result[11] = ctx->c >> 24;
+ result[12] = ctx->d;
+ result[13] = ctx->d >> 8;
+ result[14] = ctx->d >> 16;
+ result[15] = ctx->d >> 24;
+
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+
+void D3DHash (const unsigned char* data, unsigned size, unsigned char res[16])
+{
+ MD5_CTX ctx;
+ MD5_Init (&ctx);
+ MD5_Update (&ctx, (void*)data, size);
+ MD5_Final (res, &ctx);
+}
diff --git a/Runtime/GfxDevice/d3d11/D3D11Includes.h b/Runtime/GfxDevice/d3d11/D3D11Includes.h
new file mode 100644
index 0000000..7629ef7
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/D3D11Includes.h
@@ -0,0 +1,49 @@
+#pragma once
+
+
+#if UNITY_WP8
+
+#include <initguid.h>
+
+#include <dxgi1_2.h>
+#include <d3d11_1.h>
+
+typedef struct _D3D11_SIGNATURE_PARAMETER_DESC
+{
+ LPCSTR SemanticName; // Name of the semantic
+ UINT SemanticIndex; // Index of the semantic
+ UINT Register; // Number of member variables
+ D3D_NAME SystemValueType;// A predefined system value, or D3D_NAME_UNDEFINED if not applicable
+ D3D_REGISTER_COMPONENT_TYPE ComponentType; // Scalar type (e.g. uint, float, etc.)
+ BYTE Mask; // Mask to indicate which components of the register
+ // are used (combination of D3D10_COMPONENT_MASK values)
+ BYTE ReadWriteMask; // Mask to indicate whether a given component is
+ // never written (if this is an output signature) or
+ // always read (if this is an input signature).
+ // (combination of D3D10_COMPONENT_MASK values)
+ UINT Stream; // Stream index
+ D3D_MIN_PRECISION MinPrecision; // Minimum desired interpolation precision
+} D3D11_SIGNATURE_PARAMETER_DESC;
+
+#elif UNITY_METRO
+
+#include <dxgi1_2.h>
+#include <d3d11_1.h>
+#if UNITY_METRO_VS2013
+#include <d3d11_2.h>
+#endif
+#include <d3d11shader.h>
+
+#else
+
+#include "External/DirectX/builds/dx11include/DXGI.h"
+#include "External/DirectX/builds/dx11include/D3D11.h"
+#include "External/DirectX/builds/dx11include/d3d11_1.h"
+#include "External/DirectX/builds/dx11include/D3D11Shader.h"
+
+#define D3D_NAME D3D10_NAME
+#define D3D_REGISTER_COMPONENT_TYPE D3D10_REGISTER_COMPONENT_TYPE
+
+#endif
+
+#include "D3D11Debug.h"
diff --git a/Runtime/GfxDevice/d3d11/D3D11Utils.cpp b/Runtime/GfxDevice/d3d11/D3D11Utils.cpp
new file mode 100644
index 0000000..55535f3
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/D3D11Utils.cpp
@@ -0,0 +1,85 @@
+#include "UnityPrefix.h"
+#include "D3D11Utils.h"
+
+
+#ifdef DUMMY_D3D11_CALLS
+HRESULT CallDummyD3D11Function()
+{
+ return S_OK;
+}
+#endif
+
+void ReportLiveObjectsD3D11 (ID3D11Device* dev)
+{
+ ID3D11Debug* dbg = NULL;
+ if (FAILED(dev->QueryInterface(IID_ID3D11Debug, (void**)&dbg)))
+ return;
+ if (!dbg)
+ return;
+
+ dbg->ReportLiveDeviceObjects (D3D11_RLDO_DETAIL);
+ dbg->Release();
+}
+
+
+void SetDebugNameD3D11 (ID3D11DeviceChild* obj, const std::string& name)
+{
+ if (obj)
+ obj->SetPrivateData (WKPDID_D3DDebugObjectName, name.size(), name.c_str());
+}
+
+std::string GetDebugNameD3D11 (ID3D11DeviceChild* obj)
+{
+ if (obj)
+ {
+ char tmp[1024];
+ int maxLength = sizeof(tmp) - 1;
+ UINT size = maxLength;
+ obj->GetPrivateData (WKPDID_D3DDebugObjectName, &size, tmp);
+ tmp[size] = '\0';
+ tmp[maxLength] = '\0';
+ return tmp;
+ }
+ return "";
+}
+
+
+
+int GetBPPFromDXGIFormat (DXGI_FORMAT fmt)
+{
+ if (fmt == DXGI_FORMAT_UNKNOWN)
+ return 0;
+ if (fmt >= DXGI_FORMAT_R32G32B32A32_TYPELESS && fmt <= DXGI_FORMAT_R32G32B32A32_SINT)
+ return 128;
+ if (fmt >= DXGI_FORMAT_R32G32B32_TYPELESS && fmt <= DXGI_FORMAT_R32G32B32_SINT)
+ return 96;
+ if (fmt >= DXGI_FORMAT_R16G16B16A16_TYPELESS && fmt <= DXGI_FORMAT_X32_TYPELESS_G8X24_UINT)
+ return 64;
+ if (fmt >= DXGI_FORMAT_R10G10B10A2_TYPELESS && fmt <= DXGI_FORMAT_X24_TYPELESS_G8_UINT)
+ return 32;
+ if (fmt >= DXGI_FORMAT_R8G8_TYPELESS && fmt <= DXGI_FORMAT_R16_SINT)
+ return 16;
+ if (fmt >= DXGI_FORMAT_R8_TYPELESS && fmt <= DXGI_FORMAT_A8_UNORM)
+ return 8;
+ if (fmt == DXGI_FORMAT_R1_UNORM)
+ return 1;
+ if (fmt >= DXGI_FORMAT_R9G9B9E5_SHAREDEXP && fmt <= DXGI_FORMAT_G8R8_G8B8_UNORM)
+ return 32;
+ if (fmt >= DXGI_FORMAT_BC1_TYPELESS && fmt <= DXGI_FORMAT_BC1_UNORM_SRGB) // DXT1
+ return 4;
+ if (fmt >= DXGI_FORMAT_BC2_TYPELESS && fmt <= DXGI_FORMAT_BC3_UNORM_SRGB) // DXT3/5
+ return 8;
+ if (fmt >= DXGI_FORMAT_BC4_TYPELESS && fmt <= DXGI_FORMAT_BC4_SNORM)
+ return 4;
+ if (fmt >= DXGI_FORMAT_BC5_TYPELESS && fmt <= DXGI_FORMAT_BC5_SNORM)
+ return 8;
+ if (fmt >= DXGI_FORMAT_B5G6R5_UNORM && fmt <= DXGI_FORMAT_B5G5R5A1_UNORM)
+ return 16;
+ if (fmt >= DXGI_FORMAT_B8G8R8A8_UNORM && fmt <= DXGI_FORMAT_B8G8R8X8_UNORM_SRGB)
+ return 32;
+ if (fmt >= DXGI_FORMAT_BC6H_TYPELESS && fmt <= DXGI_FORMAT_BC7_UNORM_SRGB)
+ return 8;
+ AssertString ("Unknown DXGI format");
+ return 0;
+}
+
diff --git a/Runtime/GfxDevice/d3d11/D3D11Utils.h b/Runtime/GfxDevice/d3d11/D3D11Utils.h
new file mode 100644
index 0000000..69c5f44
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/D3D11Utils.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "D3D11Includes.h"
+
+//#define DUMMY_D3D11_CALLS
+
+#ifndef DUMMY_D3D11_CALLS
+#define D3D11_CALL(x) x
+#define D3D11_CALL_HR(x) x
+#else
+HRESULT CallDummyD3D11Function();
+#define D3D11_CALL(x) CallDummyD3D11Function()
+#define D3D11_CALL_HR(x) CallDummyD3D11Function()
+#endif
+
+int GetBPPFromDXGIFormat (DXGI_FORMAT fmt);
+void ReportLiveObjectsD3D11 (ID3D11Device* dev);
+void SetDebugNameD3D11 (ID3D11DeviceChild* obj, const std::string& name);
+std::string GetDebugNameD3D11 (ID3D11DeviceChild* obj);
+
diff --git a/Runtime/GfxDevice/d3d11/D3D11VBO.cpp b/Runtime/GfxDevice/d3d11/D3D11VBO.cpp
new file mode 100644
index 0000000..e0beaf1
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/D3D11VBO.cpp
@@ -0,0 +1,1193 @@
+#include "UnityPrefix.h"
+#include "D3D11VBO.h"
+#include "D3D11Context.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Graphics/TriStripper.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "D3D11Utils.h"
+
+
+
+// defined in GfxDeviceD3D11.cpp
+ID3D11InputLayout* GetD3D11VertexDeclaration (const ChannelInfoArray& channels);
+void UpdateChannelBindingsD3D11 (const ChannelAssigns& channels);
+
+ID3D11InputLayout* g_ActiveInputLayoutD3D11;
+D3D11_PRIMITIVE_TOPOLOGY g_ActiveTopologyD3D11 = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
+
+
+
+static const D3D11_PRIMITIVE_TOPOLOGY kTopologyD3D11[kPrimitiveTypeCount] =
+{
+ D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
+ D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
+ D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
+ D3D11_PRIMITIVE_TOPOLOGY_LINELIST,
+ D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP,
+ D3D11_PRIMITIVE_TOPOLOGY_POINTLIST,
+};
+
+static const D3D11_PRIMITIVE_TOPOLOGY kTopologyD3D11Tess[kPrimitiveTypeCount] =
+{
+ D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST,
+ D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED,
+ D3D11_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST,
+ D3D11_PRIMITIVE_TOPOLOGY_2_CONTROL_POINT_PATCHLIST,
+ D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED,
+ D3D11_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST,
+};
+
+
+void SetInputLayoutD3D11 (ID3D11DeviceContext* ctx, ID3D11InputLayout* layout)
+{
+ if (g_ActiveInputLayoutD3D11 != layout)
+ {
+ g_ActiveInputLayoutD3D11 = layout;
+ D3D11_CALL (ctx->IASetInputLayout (layout));
+ }
+}
+
+
+static ID3D11InputLayout* GetD3D11VertexDeclaration (UInt32 shaderChannelsMap)
+{
+ ChannelInfoArray channels;
+ int offset = 0;
+ for (int i = 0; i < kShaderChannelCount; i++)
+ {
+ ChannelInfo& info = channels[i];
+ if (shaderChannelsMap & (1 << i))
+ {
+ info.stream = 0;
+ info.offset = offset;
+ info.format = VBO::GetDefaultChannelFormat( i );
+ info.dimension = VBO::GetDefaultChannelDimension( i );
+ offset += VBO::GetDefaultChannelByteSize( i );
+ }
+ else
+ info.Reset();
+ }
+ return GetD3D11VertexDeclaration (channels);
+}
+
+
+// -----------------------------------------------------------------------------
+
+ID3D11Buffer* D3D11VBO::ms_CustomIB = NULL;
+int D3D11VBO::ms_CustomIBSize = 0;
+UInt32 D3D11VBO::ms_CustomIBUsedBytes = 0;
+
+ID3D11Buffer* D3D11VBO::ms_AllWhiteBuffer = NULL;
+
+
+D3D11VBO::D3D11VBO()
+: m_IB(NULL)
+, m_StagingIB(NULL)
+, m_IBReadable(NULL)
+, m_IBSize(0)
+, m_useForSO(false)
+{
+ memset(m_VBStreams, 0, sizeof(m_VBStreams));
+ memset(m_StagingVB, 0, sizeof(m_StagingVB));
+}
+
+D3D11VBO::~D3D11VBO ()
+{
+ for (int s = 0; s < kMaxVertexStreams; ++s)
+ {
+ //std::string tmp = GetDebugNameD3D11(m_StagingVB[s]);
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_VBStreams[s]);
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_StagingVB[s]);
+ SAFE_RELEASE(m_VBStreams[s]);
+ SAFE_RELEASE(m_StagingVB[s]);
+ }
+ if (m_IB)
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_IB);
+ SAFE_RELEASE(m_IB);
+ }
+ if (m_StagingIB)
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_StagingIB);
+ SAFE_RELEASE(m_StagingIB);
+ }
+ delete[] m_IBReadable;
+}
+
+
+static ID3D11Buffer* CreateStagingBuffer (int size)
+{
+ ID3D11Device* dev = GetD3D11Device();
+ D3D11_BUFFER_DESC desc;
+ desc.ByteWidth = size;
+ desc.Usage = D3D11_USAGE_STAGING;
+ desc.BindFlags = 0;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+ desc.MiscFlags = 0;
+ desc.StructureByteStride = 0;
+
+ ID3D11Buffer* buffer = NULL;
+ HRESULT hr = dev->CreateBuffer (&desc, NULL, &buffer);
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(buffer,size,NULL);
+ AssertIf (FAILED(hr));
+ return buffer;
+}
+
+void D3D11VBO::CleanupSharedBuffers()
+{
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(ms_CustomIB);
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(ms_AllWhiteBuffer);
+ SAFE_RELEASE (ms_CustomIB);
+ SAFE_RELEASE (ms_AllWhiteBuffer);
+ ms_CustomIBSize = 0;
+ ms_CustomIBUsedBytes = 0;
+}
+
+
+void D3D11VBO::UpdateVertexStream (const VertexBufferData& sourceData, unsigned stream)
+{
+ DebugAssert (!m_IsStreamMapped[stream]);
+ const StreamInfo& srcStream = sourceData.streams[stream];
+ const int oldSize = CalculateVertexStreamSize(m_Streams[stream], m_VertexCount);
+
+#if UNITY_METRO
+ #pragma message("Fix ugly hack CreateStagingBuffer")
+ // So honestly I don't how what's happening here, but when running on ARM (Surface) with Feature Level 9.1 and if we create a vertex buffer with size 144
+ // Sometimes we crash in D3D11VBO dtor in this line SAFE_RELEASE(m_StagingVB[s]);
+ // Sometimes we get an access violation but sometimes it's Data misalignment exception like below
+ // First-chance exception at 0x75499B2A (setupapi.dll) in Drift Mania Championship 2.exe: 0x80000002: Datatype misalignment
+ // Repro case 495782
+ // Not reproducible on Win32 running Feature Level 9.1
+
+ // In any case it seems increasing min size up to 256, solves this issue for now
+ int newSize = CalculateVertexStreamSize(srcStream, sourceData.vertexCount);
+ int addon = 1;
+ while (newSize > 0 && newSize < 256)
+ {
+ newSize = CalculateVertexStreamSize(srcStream, sourceData.vertexCount + addon);
+ addon++;
+ }
+
+#else
+ const int newSize = CalculateVertexStreamSize(srcStream, sourceData.vertexCount);
+#endif
+
+
+ m_Streams[stream] = srcStream;
+ if (newSize == 0)
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_VBStreams[stream]);
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_StagingVB[stream]);
+ SAFE_RELEASE (m_VBStreams[stream]);
+ SAFE_RELEASE (m_StagingVB[stream]);
+ return;
+ }
+
+ const bool isDynamic = (m_StreamModes[stream] == kStreamModeDynamic);
+ const bool useStaging = !isDynamic;
+
+ if (m_VBStreams[stream] == NULL || newSize != oldSize)
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_VBStreams[stream]);
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_StagingVB[stream]);
+ SAFE_RELEASE (m_VBStreams[stream]);
+ SAFE_RELEASE (m_StagingVB[stream]); // release staging VB as well here
+
+ ID3D11Device* dev = GetD3D11Device();
+ D3D11_BUFFER_DESC desc;
+ desc.ByteWidth = newSize;
+ desc.Usage = isDynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
+ desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
+ if ( m_useForSO )
+ desc.BindFlags |= D3D11_BIND_STREAM_OUTPUT;
+ desc.CPUAccessFlags = isDynamic ? D3D11_CPU_ACCESS_WRITE : 0;
+ desc.MiscFlags = 0;
+ desc.StructureByteStride = 0;
+ HRESULT hr = dev->CreateBuffer (&desc, NULL, &m_VBStreams[stream]);
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(m_VBStreams[stream],newSize,this);
+ if (FAILED(hr))
+ {
+ printf_console ("d3d11: failed to create vertex buffer of size %d [0x%X]\n", newSize, hr);
+ return;
+ }
+ SetDebugNameD3D11 (m_VBStreams[stream], Format("VertexBuffer-%d", newSize));
+
+ if (useStaging)
+ {
+ m_StagingVB[stream] = CreateStagingBuffer (newSize);
+ SetDebugNameD3D11 (m_StagingVB[stream], Format("StagingVertexBuffer-%d", newSize));
+ }
+ }
+
+ // Don't update contents if there is no source data.
+ // This is used to update the vertex declaration only, leaving buffer intact.
+ // Also to create an empty buffer that is written to later.
+ if (!sourceData.buffer)
+ return;
+
+ HRESULT hr;
+
+ ID3D11Buffer* mapVB = NULL;
+ D3D11_MAP mapType;
+
+ if (useStaging)
+ {
+ mapVB = m_StagingVB[stream];
+ mapType = D3D11_MAP_WRITE;
+ }
+ else
+ {
+ mapVB = m_VBStreams[stream];
+ mapType = D3D11_MAP_WRITE_DISCARD;
+ }
+
+ Assert (mapVB);
+
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ hr = ctx->Map (mapVB, 0, mapType, 0, &mapped);
+ Assert (SUCCEEDED(hr));
+ CopyVertexStream (sourceData, reinterpret_cast<UInt8*>(mapped.pData), stream);
+ ctx->Unmap (mapVB, 0);
+
+ if (useStaging)
+ ctx->CopyResource (m_VBStreams[stream], m_StagingVB[stream]);
+}
+
+
+void D3D11VBO::UpdateIndexBufferData (const IndexBufferData& sourceData)
+{
+ if( !sourceData.indices )
+ {
+ m_IBSize = 0;
+ return;
+ }
+
+ Assert (m_IB);
+ HRESULT hr;
+
+ int size = sourceData.count * kVBOIndexSize;
+ if (sourceData.hasTopologies & ((1<<kPrimitiveTriangleStripDeprecated) | (1<<kPrimitiveQuads)))
+ {
+ delete[] m_IBReadable;
+ m_IBReadable = new UInt16[sourceData.count];
+ memcpy (m_IBReadable, sourceData.indices, size);
+ }
+
+ const D3D11_MAP mapType = m_IndicesDynamic ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE;
+ ID3D11Buffer* mapIB;
+ if (m_IndicesDynamic)
+ mapIB = m_IB;
+ else
+ {
+ if (!m_StagingIB)
+ m_StagingIB = CreateStagingBuffer(m_IBSize);
+ mapIB = m_StagingIB;
+ }
+
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ hr = ctx->Map (mapIB, 0, mapType, 0, &mapped);
+ Assert (SUCCEEDED(hr));
+
+ memcpy (mapped.pData, sourceData.indices, size);
+
+ ctx->Unmap (mapIB, 0);
+ if (!m_IndicesDynamic)
+ ctx->CopyResource (m_IB, m_StagingIB);
+}
+
+bool D3D11VBO::MapVertexStream( VertexStreamData& outData, unsigned stream )
+{
+ if (m_VBStreams[stream] == NULL)
+ {
+ printf_console ("d3d11: attempt to map null vertex buffer\n");
+ return false;
+ }
+ DebugAssert(!IsVertexBufferLost());
+ Assert(!m_IsStreamMapped[stream]);
+
+ const int vbSize = CalculateVertexStreamSize(m_Streams[stream], m_VertexCount);
+
+ const bool dynamic = (m_StreamModes[stream]==kStreamModeDynamic);
+
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ ID3D11Buffer* mapVB = dynamic ? m_VBStreams[stream] : m_StagingVB[stream];
+ D3D11_MAP mapType = dynamic ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE;
+ HRESULT hr = GetD3D11Context()->Map (mapVB, 0, mapType, 0, &mapped);
+ if( FAILED(hr) )
+ {
+ printf_console ("d3d11: failed to map vertex buffer %p of size %i [%x]\n", mapVB, vbSize, hr);
+ return false;
+ }
+ m_IsStreamMapped[stream] = true;
+
+ UInt8* buffer = (UInt8*)mapped.pData;
+
+ outData.buffer = buffer;
+ outData.channelMask = m_Streams[stream].channelMask;
+ outData.stride = m_Streams[stream].stride;
+ outData.vertexCount = m_VertexCount;
+
+ GetRealGfxDevice().GetFrameStats().AddUploadVBO(vbSize);
+
+ return true;
+}
+
+void D3D11VBO::UnmapVertexStream (unsigned stream)
+{
+ DebugAssert(m_VBStreams[stream]);
+ Assert(m_IsStreamMapped[stream]);
+ m_IsStreamMapped[stream] = false;
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ const bool dynamic = (m_StreamModes[stream]==kStreamModeDynamic);
+ ID3D11Buffer* mapVB = dynamic ? m_VBStreams[stream] : m_StagingVB[stream];
+ ctx->Unmap (mapVB, 0);
+
+ if (!dynamic)
+ ctx->CopyResource (m_VBStreams[stream], m_StagingVB[stream]);
+}
+
+bool D3D11VBO::IsVertexBufferLost() const
+{
+ for (int s = 0; s < kMaxVertexStreams; ++s)
+ if (m_Streams[s].channelMask && !m_VBStreams[s])
+ return true;
+ return false;
+}
+
+int D3D11VBO::GetRuntimeMemorySize() const
+{
+ int vertexSize = 0;
+ for( int s = 0; s < kMaxVertexStreams; s++ )
+ vertexSize += m_Streams[s].stride;
+ return vertexSize * m_VertexCount + m_IBSize;
+}
+
+
+bool SetTopologyD3D11 (GfxPrimitiveType topology, GfxDevice& device, ID3D11DeviceContext* ctx)
+{
+ bool tessellation = device.IsShaderActive (kShaderHull) || device.IsShaderActive (kShaderDomain);
+ D3D11_PRIMITIVE_TOPOLOGY topod3d = tessellation ? kTopologyD3D11Tess[topology] : kTopologyD3D11[topology];
+ if (topod3d == D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED)
+ return false;
+
+ if (topod3d != g_ActiveTopologyD3D11)
+ {
+ g_ActiveTopologyD3D11 = topod3d;
+ D3D11_CALL (ctx->IASetPrimitiveTopology (topod3d));
+ }
+ return true;
+}
+
+
+ID3D11Buffer* D3D11VBO::GetAllWhiteBuffer()
+{
+ if (!ms_AllWhiteBuffer)
+ {
+ int maxVerts = 0x10000;
+ int size = maxVerts * sizeof(UInt32);
+ UInt8* data = new UInt8[size];
+ memset (data, 0xFF, size);
+
+ D3D11_BUFFER_DESC desc;
+ desc.ByteWidth = size;
+ desc.Usage = D3D11_USAGE_IMMUTABLE;
+ desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
+ desc.CPUAccessFlags = 0;
+ desc.MiscFlags = 0;
+ desc.StructureByteStride = 0;
+ D3D11_SUBRESOURCE_DATA sdata;
+ sdata.pSysMem = data;
+ sdata.SysMemPitch = 0;
+ sdata.SysMemSlicePitch = 0;
+ HRESULT hr = GetD3D11Device()->CreateBuffer (&desc, &sdata, &ms_AllWhiteBuffer);
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(ms_AllWhiteBuffer,size,NULL);
+ delete[] data;
+ }
+ return ms_AllWhiteBuffer;
+}
+
+
+void D3D11VBO::BindVertexStreams(GfxDevice& device, ID3D11DeviceContext* ctx, const ChannelAssigns& channels)
+{
+ DX11_LOG_ENTER_FUNCTION("D3D11VBO::BindVertexStreams");
+ int freeStream = -1;
+ for (int s = 0; s < kMaxVertexStreams; ++s)
+ {
+ if (m_VBStreams[s])
+ {
+ UINT offset = 0;
+ UINT stride = m_Streams[s].stride;
+ D3D11_CALL (ctx->IASetVertexBuffers(s, 1, &m_VBStreams[s], &stride, &offset));
+ }
+ else
+ freeStream = s;
+ }
+
+ UpdateChannelBindingsD3D11(channels);
+
+ device.BeforeDrawCall( false );
+
+ const ChannelInfoArray* channelInfo = &m_ChannelInfo;
+ if ((channels.GetSourceMap() & VERTEX_FORMAT1(Color)) && !m_ChannelInfo[kShaderChannelColor].IsValid())
+ {
+ if (freeStream != -1)
+ {
+ static ChannelInfoArray colorChannelInfo;
+ memcpy(&colorChannelInfo, m_ChannelInfo, sizeof(colorChannelInfo));
+ ChannelInfo& colorInfo = colorChannelInfo[kShaderChannelColor];
+ colorInfo.stream = freeStream;
+ colorInfo.offset = 0;
+ colorInfo.format = kChannelFormatColor;
+ colorInfo.dimension = 1;
+ channelInfo = &colorChannelInfo;
+ ID3D11Buffer* whiteVB = GetAllWhiteBuffer();
+ UINT stride = 4;
+ UINT offset = 0;
+ D3D11_CALL (ctx->IASetVertexBuffers(freeStream, 1, &whiteVB, &stride, &offset));
+ }
+ else
+ ErrorString("Need a free stream to add default vertex colors!");
+ }
+ ID3D11InputLayout* inputLayout = GetD3D11VertexDeclaration(m_ChannelInfo);
+ SetInputLayoutD3D11 (ctx, inputLayout);
+}
+
+void D3D11VBO::BindToStreamOutput()
+{
+ const UINT offsets[] = { 0 };
+ GetD3D11Context()->SOSetTargets(1, m_VBStreams, offsets);
+}
+
+void D3D11VBO::UnbindFromStreamOutput()
+{
+ ID3D11Buffer* const buffers[] = { 0 };
+ const UINT offsets[] = { 0 };
+ GetD3D11Context()->SOSetTargets(1, buffers, offsets);
+}
+
+
+void D3D11VBO::DrawVBO (const ChannelAssigns& channels, UInt32 firstIndexByte, UInt32 indexCount, GfxPrimitiveType topology, UInt32 firstVertex, UInt32 vertexCount)
+{
+ DX11_LOG_ENTER_FUNCTION("D3D11VBO::DrawVBO");
+ // just return if no indices
+ if( m_IBSize == 0 )
+ return;
+
+ Assert(!m_IsStreamMapped[0]);
+ if (m_VBStreams[0] == 0 || m_IB == 0)
+ {
+ printf_console( "d3d: VB or IB is null\n" );
+ return;
+ }
+
+ GfxDevice& device = GetRealGfxDevice();
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ BindVertexStreams (device, ctx, channels);
+
+ bool tessellation = device.IsShaderActive (kShaderHull) || device.IsShaderActive (kShaderDomain);
+ if (tessellation && topology == kPrimitiveTriangleStripDeprecated)
+ {
+ if (!m_IBReadable)
+ return;
+
+ const UInt16* ibSrc = (const UInt16*)((const UInt8*)m_IBReadable + firstIndexByte);
+ const int triCount = CountTrianglesInStrip (ibSrc, indexCount);
+
+ UInt32 ibBytesLocked;
+ UInt16* ibPtr = MapDynamicIndexBuffer (triCount*3, ibBytesLocked);
+ if (!ibPtr)
+ return;
+ Destripify (ibSrc, indexCount, ibPtr, triCount*3);
+ UnmapDynamicIndexBuffer ();
+ firstIndexByte = ms_CustomIBUsedBytes;
+ ms_CustomIBUsedBytes += ibBytesLocked;
+ D3D11_CALL (ctx->IASetIndexBuffer (ms_CustomIB, DXGI_FORMAT_R16_UINT, 0));
+ indexCount = ibBytesLocked/kVBOIndexSize;
+ topology = kPrimitiveTriangles;
+ }
+ else if (topology == kPrimitiveQuads && !tessellation)
+ {
+ if (!m_IBReadable)
+ return;
+ UInt32 ibBytesLocked;
+ UInt16* ibPtr = MapDynamicIndexBuffer (indexCount/4*6, ibBytesLocked);
+ if (!ibPtr)
+ return;
+ const UInt16* ibSrc = (const UInt16*)((const UInt8*)m_IBReadable + firstIndexByte);
+ FillIndexBufferForQuads (ibPtr, ibBytesLocked, ibSrc, indexCount/4);
+ UnmapDynamicIndexBuffer ();
+ firstIndexByte = ms_CustomIBUsedBytes;
+ ms_CustomIBUsedBytes += ibBytesLocked;
+ D3D11_CALL (ctx->IASetIndexBuffer (ms_CustomIB, DXGI_FORMAT_R16_UINT, 0));
+ indexCount = ibBytesLocked/kVBOIndexSize;
+ }
+ else
+ {
+ D3D11_CALL (ctx->IASetIndexBuffer (m_IB, DXGI_FORMAT_R16_UINT, 0));
+ }
+
+ // draw
+ if (!SetTopologyD3D11 (topology, device, ctx))
+ return;
+ D3D11_CALL (ctx->DrawIndexed (indexCount, firstIndexByte/2, 0));
+ device.GetFrameStats().AddDrawCall (GetPrimitiveCount(indexCount,topology,false), vertexCount);
+ DX11_MARK_DRAWING(GetPrimitiveCount(indexCount,topology,false), vertexCount);
+
+}
+
+UInt16* D3D11VBO::MapDynamicIndexBuffer (int indexCount, UInt32& outBytesUsed)
+{
+ HRESULT hr;
+ const UInt32 maxIndices = 64000;
+ Assert (indexCount <= maxIndices);
+ indexCount = std::min<UInt32>(indexCount, maxIndices);
+
+ int ibCapacity = indexCount * kVBOIndexSize;
+ int newIBSize = std::max (ibCapacity, 32*1024); // 32k IB at least
+
+ if (newIBSize > ms_CustomIBSize)
+ {
+ if (ms_CustomIB)
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(ms_CustomIB);
+ ms_CustomIB->Release();
+ }
+ ms_CustomIBSize = newIBSize;
+ ms_CustomIBUsedBytes = 0;
+
+ ID3D11Device* dev = GetD3D11Device();
+
+ D3D11_BUFFER_DESC desc;
+ desc.ByteWidth = newIBSize;
+ desc.Usage = D3D11_USAGE_DYNAMIC;
+ desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+ desc.MiscFlags = 0;
+ desc.StructureByteStride = 0;
+ hr = dev->CreateBuffer (&desc, NULL, &ms_CustomIB);
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(ms_CustomIB,newIBSize,NULL);
+ if (FAILED(hr))
+ {
+ printf_console ("d3d11: failed to create custom index buffer of size %d [%x]\n", newIBSize, hr);
+ return NULL;
+ }
+ SetDebugNameD3D11 (ms_CustomIB, Format("IndexBufferCustomDynamic-%d", newIBSize));
+ }
+
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ if (ms_CustomIBUsedBytes + ibCapacity > ms_CustomIBSize)
+ {
+ hr = ctx->Map (ms_CustomIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
+ if (FAILED(hr))
+ {
+ printf_console ("d3d11: failed to lock shared index buffer with discard [%x]\n", hr);
+ return NULL;
+ }
+ ms_CustomIBUsedBytes = 0;
+ }
+ else
+ {
+ hr = ctx->Map (ms_CustomIB, 0, D3D11_MAP_WRITE_NO_OVERWRITE, 0, &mapped);
+ if (FAILED(hr))
+ {
+ printf_console( "d3d11: failed to lock shared index buffer, offset %i size %i [%x]\n", ms_CustomIBUsedBytes, ibCapacity, hr);
+ return NULL;
+ }
+ }
+ outBytesUsed = ibCapacity;
+
+ return (UInt16*)((UInt8*)mapped.pData + ms_CustomIBUsedBytes);
+}
+
+void D3D11VBO::UnmapDynamicIndexBuffer ()
+{
+ GetD3D11Context()->Unmap (ms_CustomIB, 0);
+}
+
+
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+void D3D11VBO::DrawCustomIndexed( const ChannelAssigns& channels, void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount )
+{
+ HRESULT hr;
+ Assert (!m_IsStreamMapped[0]);
+
+ if (m_VBStreams[0] == 0)
+ {
+ printf_console( "d3d11: VB is null\n" );
+ return;
+ }
+
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ UInt32 ibBytesUsed;
+ UInt16* ibPtr = MapDynamicIndexBuffer (indexCount, ibBytesUsed);
+ if (!ibPtr)
+ return;
+ memcpy (ibPtr, indices, ibBytesUsed);
+ UnmapDynamicIndexBuffer ();
+
+ // draw
+ GfxDevice& device = GetRealGfxDevice();
+ BindVertexStreams(device, ctx, channels);
+
+ D3D11_CALL (ctx->IASetIndexBuffer (ms_CustomIB, DXGI_FORMAT_R16_UINT, 0));
+
+ // draw
+ if (!SetTopologyD3D11 (topology, device, ctx))
+ return;
+ D3D11_CALL (ctx->DrawIndexed (indexCount, ms_CustomIBUsedBytes / kVBOIndexSize, 0));
+ device.GetFrameStats().AddDrawCall (GetPrimitiveCount(indexCount,topology,false), drawVertexCount);
+
+ ms_CustomIBUsedBytes += ibBytesUsed;
+}
+
+#endif // GFX_ENABLE_DRAW_CALL_BATCHING
+
+
+
+void D3D11VBO::UpdateVertexData( const VertexBufferData& buffer )
+{
+ for (unsigned stream = 0; stream < kMaxVertexStreams; stream++)
+ UpdateVertexStream (buffer, stream);
+
+ memcpy (m_ChannelInfo, buffer.channels, sizeof(m_ChannelInfo));
+ m_VertexCount = buffer.vertexCount;
+}
+
+void D3D11VBO::UpdateIndexData (const IndexBufferData& buffer)
+{
+ int newSize = CalculateIndexBufferSize(buffer);
+
+ // If we have old buffer, but need different size: delete old one
+ if (newSize != m_IBSize)
+ {
+ if (m_IB)
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_IB);
+ SAFE_RELEASE(m_IB);
+ }
+ if (m_StagingIB)
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_StagingIB);
+ SAFE_RELEASE(m_StagingIB);
+ }
+ }
+
+ // Create buffer if we need to
+ if (!m_IB)
+ {
+ ID3D11Device* dev = GetD3D11Device();
+ D3D11_BUFFER_DESC desc;
+ desc.ByteWidth = newSize;
+ desc.Usage = m_IndicesDynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
+ desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
+ desc.CPUAccessFlags = m_IndicesDynamic ? D3D11_CPU_ACCESS_WRITE : 0;
+ desc.MiscFlags = 0;
+ desc.StructureByteStride = 0;
+ HRESULT hr = dev->CreateBuffer (&desc, NULL, &m_IB);
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(m_IB,newSize,this);
+ if( FAILED(hr) )
+ {
+ printf_console( "d3d11: failed to create index buffer of size %d [0x%X]\n", newSize, hr );
+ return;
+ }
+ SetDebugNameD3D11 (m_IB, Format("IndexBuffer-%d", newSize));
+ }
+
+ m_IBSize = newSize;
+ UpdateIndexBufferData(buffer);
+}
+
+void D3D11VBO::SetIndicesDynamic(bool dynamic)
+{
+ // do nothing if a no-op
+ if (dynamic == m_IndicesDynamic)
+ return;
+
+ VBO::SetIndicesDynamic(dynamic);
+
+ // release current index buffers
+ if (m_IB)
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_IB);
+ SAFE_RELEASE(m_IB);
+ }
+ if (m_StagingIB)
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_StagingIB);
+ SAFE_RELEASE(m_StagingIB);
+ }
+}
+
+
+
+// -----------------------------------------------------------------------------
+
+
+DynamicD3D11VBO::DynamicD3D11VBO( UInt32 vbSize, UInt32 ibSize )
+: DynamicVBO()
+, m_VBSize(vbSize)
+, m_VBUsedBytes(0)
+, m_IBSize(ibSize)
+, m_IBUsedBytes(0)
+, m_VB(NULL)
+, m_IB(NULL)
+, m_LastChunkStartVertex(0)
+, m_LastChunkStartIndex(0)
+, m_QuadsIB(NULL)
+{
+}
+
+DynamicD3D11VBO::~DynamicD3D11VBO ()
+{
+ if( m_VB ) {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_VB);
+ ULONG refCount = m_VB->Release();
+ AssertIf( refCount != 0 );
+ }
+ if( m_IB ) {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_IB);
+ ULONG refCount = m_IB->Release();
+ AssertIf( refCount != 0 );
+ }
+ if( m_QuadsIB ) {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_QuadsIB);
+ ULONG refCount = m_QuadsIB->Release();
+ AssertIf( refCount != 0 );
+ }
+}
+
+void DynamicD3D11VBO::InitializeQuadsIB()
+{
+ AssertIf( m_QuadsIB );
+
+ const int kMaxQuads = 65536/4 - 4; // so we fit into 16 bit indices, minus some more just in case
+
+ UInt16* data = new UInt16[kMaxQuads*6];
+ UInt16* ib = data;
+ UInt32 baseIndex = 0;
+ for( int i = 0; i < kMaxQuads; ++i )
+ {
+ ib[0] = baseIndex + 1;
+ ib[1] = baseIndex + 2;
+ ib[2] = baseIndex;
+ ib[3] = baseIndex + 2;
+ ib[4] = baseIndex + 3;
+ ib[5] = baseIndex;
+ baseIndex += 4;
+ ib += 6;
+ }
+
+ ID3D11Device* dev = GetD3D11Device();
+ D3D11_BUFFER_DESC desc;
+ desc.ByteWidth = kMaxQuads * 6 * kVBOIndexSize;
+ desc.Usage = D3D11_USAGE_IMMUTABLE;
+ desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
+ desc.CPUAccessFlags = 0;
+ desc.MiscFlags = 0;
+ desc.StructureByteStride = 0;
+ D3D11_SUBRESOURCE_DATA srData;
+ srData.pSysMem = data;
+ srData.SysMemPitch = 0;
+ srData.SysMemSlicePitch = 0;
+ HRESULT hr = dev->CreateBuffer (&desc, &srData, &m_QuadsIB);
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(m_QuadsIB,desc.ByteWidth,this);
+ delete[] data;
+ if (FAILED(hr))
+ {
+ printf_console ("d3d11: failed to create quads index buffer [%x]\n", hr);
+ }
+ SetDebugNameD3D11 (m_QuadsIB, "IndexBufferQuads");
+}
+
+
+void DynamicD3D11VBO::DrawChunk (const ChannelAssigns& channels)
+{
+ DX11_LOG_ENTER_FUNCTION("DynamicD3D11VBO::DrawChunk");
+ // just return if nothing to render
+ if( !m_LastChunkShaderChannelMask )
+ return;
+
+ HRESULT hr;
+
+ AssertIf( !m_LastChunkShaderChannelMask || !m_LastChunkStride );
+ AssertIf( m_LendedChunk );
+
+ GfxDevice& device = GetRealGfxDevice();
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ // setup VBO
+ DebugAssert (m_VB);
+ UINT strides = m_LastChunkStride;
+ UINT offsets = 0;
+ D3D11_CALL (ctx->IASetVertexBuffers(0, 1, &m_VB, &strides, &offsets));
+
+ UpdateChannelBindingsD3D11(channels);
+ device.BeforeDrawCall (false);
+
+ ID3D11InputLayout* inputLayout = GetD3D11VertexDeclaration (m_LastChunkShaderChannelMask);
+ SetInputLayoutD3D11 (ctx, inputLayout);
+
+ // draw
+ GfxDeviceStats& stats = device.GetFrameStats();
+ int primCount = 0;
+ if (m_LastRenderMode == kDrawTriangleStrip)
+ {
+ if (!SetTopologyD3D11(kPrimitiveTriangleStripDeprecated,device,ctx))
+ return;
+ D3D11_CALL (ctx->Draw (m_LastChunkVertices, m_LastChunkStartVertex));
+ primCount = m_LastChunkVertices-2;
+ }
+ else if (m_LastRenderMode == kDrawIndexedTriangleStrip)
+ {
+ DebugAssert (m_IB);
+ if (!SetTopologyD3D11(kPrimitiveTriangleStripDeprecated,device,ctx))
+ return;
+ D3D11_CALL (ctx->IASetIndexBuffer (m_IB, DXGI_FORMAT_R16_UINT, 0));
+ D3D11_CALL (ctx->DrawIndexed (m_LastChunkIndices, m_LastChunkStartIndex, m_LastChunkStartVertex));
+ primCount = m_LastChunkIndices-2;
+ }
+ else if( m_LastRenderMode == kDrawQuads )
+ {
+ if (!SetTopologyD3D11(kPrimitiveTriangles,device,ctx))
+ return;
+ // initialize quads index buffer if needed
+ if (!m_QuadsIB)
+ InitializeQuadsIB();
+ // if quads index buffer has valid data, draw with it
+ if (m_QuadsIB)
+ {
+ D3D11_CALL (ctx->IASetIndexBuffer (m_QuadsIB, DXGI_FORMAT_R16_UINT, 0));
+ D3D11_CALL (ctx->DrawIndexed (m_LastChunkVertices/4*6, 0, m_LastChunkStartVertex));
+ primCount = m_LastChunkVertices/2;
+ }
+ }
+ else if (m_LastRenderMode == kDrawIndexedLines)
+ {
+ DebugAssert( m_IB );
+ if (!SetTopologyD3D11(kPrimitiveLines,device,ctx))
+ return;
+ D3D11_CALL (ctx->IASetIndexBuffer (m_IB, DXGI_FORMAT_R16_UINT, 0));
+ D3D11_CALL (ctx->DrawIndexed (m_LastChunkIndices, m_LastChunkStartIndex, m_LastChunkStartVertex));
+ primCount = m_LastChunkIndices/2;
+ }
+ else if (m_LastRenderMode == kDrawIndexedPoints)
+ {
+ DebugAssert (m_IB);
+ D3D11_CALL (ctx->IASetIndexBuffer (m_IB, DXGI_FORMAT_R16_UINT, 0));
+ if (!SetTopologyD3D11 (kPrimitivePoints, device, ctx))
+ return;
+ D3D11_CALL (ctx->DrawIndexed (m_LastChunkIndices, m_LastChunkStartIndex, m_LastChunkStartVertex));
+ primCount = m_LastChunkIndices;
+ }
+ else
+ {
+ DebugAssert (m_IB);
+ D3D11_CALL (ctx->IASetIndexBuffer (m_IB, DXGI_FORMAT_R16_UINT, 0));
+ if (!SetTopologyD3D11 (kPrimitiveTriangles, device, ctx))
+ return;
+ D3D11_CALL (ctx->DrawIndexed (m_LastChunkIndices, m_LastChunkStartIndex, m_LastChunkStartVertex));
+ primCount = m_LastChunkIndices/3;
+ }
+ stats.AddDrawCall (primCount, m_LastChunkVertices);
+ DX11_MARK_DRAWING(primCount, m_LastChunkVertices);
+}
+
+
+#if UNITY_EDITOR
+void DynamicD3D11VBO::DrawChunkUserPrimitives (GfxPrimitiveType type)
+{
+ // just return if nothing to render
+ if( !m_LastChunkShaderChannelMask )
+ return;
+
+ HRESULT hr;
+
+ AssertIf( !m_LastChunkShaderChannelMask || !m_LastChunkStride );
+ AssertIf( m_LendedChunk );
+
+ ChannelAssigns channels;
+ for( int i = 0; i < kShaderChannelCount; ++i )
+ {
+ if (!(m_LastChunkShaderChannelMask & (1<<i)))
+ continue;
+ VertexComponent destComponent = kSuitableVertexComponentForChannel[i];
+ channels.Bind ((ShaderChannel)i, destComponent);
+ }
+
+ GfxDevice& device = GetRealGfxDevice();
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ // setup VBO
+ DebugAssert (m_VB);
+ UINT strides = m_LastChunkStride;
+ UINT offsets = 0;
+ D3D11_CALL (ctx->IASetVertexBuffers(0, 1, &m_VB, &strides, &offsets));
+
+ UpdateChannelBindingsD3D11(channels);
+ device.BeforeDrawCall (false);
+
+ ID3D11InputLayout* inputLayout = GetD3D11VertexDeclaration (m_LastChunkShaderChannelMask);
+ SetInputLayoutD3D11 (ctx, inputLayout);
+
+ // draw
+ GfxDeviceStats& stats = device.GetFrameStats();
+ int primCount = 0;
+ switch (type)
+ {
+ case kPrimitiveTriangles:
+ if (!SetTopologyD3D11(kPrimitiveTriangles,device,ctx))
+ return;
+ D3D11_CALL (ctx->Draw (m_LastChunkVertices, m_LastChunkStartVertex));
+ primCount = m_LastChunkVertices/3;
+ break;
+ case kPrimitiveQuads:
+ if (!SetTopologyD3D11(kPrimitiveTriangles,device,ctx))
+ return;
+ // initialize quads index buffer if needed
+ if (!m_QuadsIB)
+ InitializeQuadsIB();
+ // if quads index buffer has valid data, draw with it
+ if (m_QuadsIB)
+ {
+ D3D11_CALL (ctx->IASetIndexBuffer (m_QuadsIB, DXGI_FORMAT_R16_UINT, 0));
+ D3D11_CALL (ctx->DrawIndexed (m_LastChunkVertices/4*6, 0, m_LastChunkStartVertex));
+ primCount = m_LastChunkVertices/2;
+ }
+ break;
+ case kPrimitiveLines:
+ if (!SetTopologyD3D11(kPrimitiveLines,device,ctx))
+ return;
+ D3D11_CALL (ctx->Draw (m_LastChunkVertices, m_LastChunkStartVertex));
+ primCount = m_LastChunkVertices/2;
+ break;
+ case kPrimitiveLineStrip:
+ if (!SetTopologyD3D11(kPrimitiveLineStrip,device,ctx))
+ return;
+ D3D11_CALL (ctx->Draw (m_LastChunkVertices, m_LastChunkStartVertex));
+ primCount = m_LastChunkVertices-1;
+ break;
+ default:
+ ErrorString("Primitive type not supported");
+ return;
+ }
+ stats.AddDrawCall (primCount, m_LastChunkVertices);
+}
+#endif // UNITY_EDITOR
+
+
+bool DynamicD3D11VBO::GetChunk( UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode renderMode, void** outVB, void** outIB )
+{
+ Assert( !m_LendedChunk );
+ Assert( maxVertices < 65536 && maxIndices < 65536*3 );
+ DebugAssertMsg(outVB != NULL && maxVertices > 0, "DynamicD3D11VBO::GetChunk - outVB: 0x%08x maxVertices: %d", outVB, maxVertices);
+ DebugAssertMsg(
+ (renderMode == kDrawIndexedQuads && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedPoints && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedLines && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedTriangles && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedTriangleStrip && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawTriangleStrip && (outIB == NULL && maxIndices == 0)) ||
+ (renderMode == kDrawQuads && (outIB == NULL && maxIndices == 0)),
+ "DynamicD3D11VBO::GetChunk - renderMode: %d outIB: 0x%08x maxIndices: %d", renderMode, outIB, maxIndices);
+
+ HRESULT hr;
+ bool success = true;
+
+ m_LendedChunk = true;
+ m_LastChunkShaderChannelMask = shaderChannelMask;
+ m_LastRenderMode = renderMode;
+
+ if( maxVertices == 0 )
+ maxVertices = 8;
+
+ m_LastChunkStride = 0;
+ for( int i = 0; i < kShaderChannelCount; ++i ) {
+ if( shaderChannelMask & (1<<i) )
+ m_LastChunkStride += VBO::GetDefaultChannelByteSize(i);
+ }
+ ID3D11Device* dev = GetD3D11Device();
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ // -------- vertex buffer
+
+ DebugAssertIf( !outVB );
+ UInt32 vbCapacity = maxVertices * m_LastChunkStride;
+ // check if requested chunk is larger than current buffer
+ if( vbCapacity > m_VBSize ) {
+ m_VBSize = vbCapacity * 2; // allocate more up front
+ if( m_VB )
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_VB);
+ m_VB->Release();
+ }
+ m_VB = NULL;
+ }
+ // allocate buffer if don't have it yet
+ if( !m_VB )
+ {
+ D3D11_BUFFER_DESC desc;
+ desc.ByteWidth = m_VBSize;
+ desc.Usage = D3D11_USAGE_DYNAMIC;
+ desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+ desc.MiscFlags = 0;
+ desc.StructureByteStride = 0;
+ hr = dev->CreateBuffer (&desc, NULL, &m_VB);
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(m_VB,m_VBSize,this);
+ if (FAILED(hr))
+ {
+ printf_console ("d3d11: failed to create dynamic vertex buffer of size %d [%x]\n", m_VBSize, hr);
+ success = false;
+ *outVB = NULL;
+ }
+ SetDebugNameD3D11 (m_VB, Format("VertexBufferDynamic-%d", m_VBSize));
+ }
+
+ // map, making sure the offset we lock is multiple of vertex stride
+ if (m_VB)
+ {
+ m_VBUsedBytes = ((m_VBUsedBytes + (m_LastChunkStride-1)) / m_LastChunkStride) * m_LastChunkStride;
+ if (m_VBUsedBytes + vbCapacity > m_VBSize)
+ {
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ hr = ctx->Map (m_VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
+ if (FAILED(hr))
+ {
+ printf_console ("d3d11: failed to lock dynamic vertex buffer with discard [%x]\n", hr);
+ *outVB = NULL;
+ success = false;
+ }
+ *outVB = mapped.pData;
+ m_VBUsedBytes = 0;
+ } else {
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ hr = ctx->Map (m_VB, 0, D3D11_MAP_WRITE_NO_OVERWRITE, 0, &mapped);
+ if (FAILED(hr))
+ {
+ printf_console ("d3d11: failed to lock vertex index buffer, offset %i size %i [%x]\n", m_VBUsedBytes, vbCapacity, hr);
+ *outVB = NULL;
+ success = false;
+ }
+ *outVB = ((UInt8*)mapped.pData) + m_VBUsedBytes;
+ }
+ m_LastChunkStartVertex = m_VBUsedBytes / m_LastChunkStride;
+ DebugAssertIf( m_LastChunkStartVertex * m_LastChunkStride != m_VBUsedBytes );
+ }
+
+ // -------- index buffer
+
+ const bool indexed = (renderMode != kDrawQuads) && (renderMode != kDrawTriangleStrip);
+ if( success && maxIndices && indexed )
+ {
+ UInt32 ibCapacity = maxIndices * kVBOIndexSize;
+ // check if requested chunk is larger than current buffer
+ if( ibCapacity > m_IBSize ) {
+ m_IBSize = ibCapacity * 2; // allocate more up front
+ if( m_IB )
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_IB);
+ m_IB->Release();
+ }
+ m_IB = NULL;
+ }
+ // allocate buffer if don't have it yet
+ if( !m_IB )
+ {
+ D3D11_BUFFER_DESC desc;
+ desc.ByteWidth = m_IBSize;
+ desc.Usage = D3D11_USAGE_DYNAMIC;
+ desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+ desc.MiscFlags = 0;
+ desc.StructureByteStride = 0;
+ hr = dev->CreateBuffer (&desc, NULL, &m_IB);
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(m_IB,m_IBSize,this);
+ if (FAILED(hr))
+ {
+ printf_console ("d3d11: failed to create dynamic index buffer of size %d [%x]\n", m_IBSize, hr);
+ if (m_VB)
+ ctx->Unmap (m_VB, 0);
+ }
+ SetDebugNameD3D11 (m_IB, Format("IndexBufferDynamic-%d", m_IBSize));
+ }
+ // lock it if we have IB created successfully
+ if( m_IB )
+ {
+ if( m_IBUsedBytes + ibCapacity > m_IBSize )
+ {
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ hr = ctx->Map (m_IB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
+ if (FAILED(hr))
+ {
+ printf_console ("d3d11: failed to lock dynamic index buffer with discard [%x]\n", hr);
+ *outIB = NULL;
+ success = false;
+ if (m_VB)
+ ctx->Unmap (m_VB, 0);
+ }
+ *outIB = mapped.pData;
+ m_IBUsedBytes = 0;
+ } else {
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ hr = ctx->Map (m_IB, 0, D3D11_MAP_WRITE_NO_OVERWRITE, 0, &mapped);
+ if (FAILED(hr))
+ {
+ printf_console ("d3d11: failed to lock dynamic index buffer, offset %i size %i [%x]\n", m_IBUsedBytes, ibCapacity, hr);
+ *outIB = NULL;
+ success = false;
+ if (m_VB)
+ ctx->Unmap (m_VB, 0);
+ }
+ *outIB = ((UInt8*)mapped.pData) + m_IBUsedBytes;
+ }
+ m_LastChunkStartIndex = m_IBUsedBytes / 2;
+ }
+ else
+ {
+ *outIB = NULL;
+ success = false;
+ }
+ }
+
+ if( !success )
+ m_LendedChunk = false;
+
+ return success;
+}
+
+void DynamicD3D11VBO::ReleaseChunk( UInt32 actualVertices, UInt32 actualIndices )
+{
+ Assert( m_LendedChunk );
+ Assert( m_LastRenderMode == kDrawIndexedTriangleStrip || m_LastRenderMode == kDrawIndexedQuads || m_LastRenderMode == kDrawIndexedPoints || m_LastRenderMode == kDrawIndexedLines || actualIndices % 3 == 0 );
+ m_LendedChunk = false;
+
+ const bool indexed = (m_LastRenderMode != kDrawQuads) && (m_LastRenderMode != kDrawTriangleStrip);
+
+ m_LastChunkVertices = actualVertices;
+ m_LastChunkIndices = actualIndices;
+
+ // unlock buffers
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ ctx->Unmap (m_VB, 0);
+ if (indexed)
+ ctx->Unmap (m_IB, 0);
+
+ if( !actualVertices || (indexed && !actualIndices) ) {
+ m_LastChunkShaderChannelMask = 0;
+ return;
+ }
+
+ UInt32 actualVBSize = actualVertices * m_LastChunkStride;
+ m_VBUsedBytes += actualVBSize;
+ UInt32 actualIBSize = actualIndices * kVBOIndexSize;
+ m_IBUsedBytes += actualIBSize;
+}
+
+
diff --git a/Runtime/GfxDevice/d3d11/D3D11VBO.h b/Runtime/GfxDevice/d3d11/D3D11VBO.h
new file mode 100644
index 0000000..9c0d0d2
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/D3D11VBO.h
@@ -0,0 +1,99 @@
+#pragma once
+
+#include "Runtime/Shaders/VBO.h"
+#include "D3D11Includes.h"
+
+class GfxDevice;
+
+class D3D11VBO : public VBO {
+public:
+ D3D11VBO();
+ virtual ~D3D11VBO();
+
+ virtual void UpdateVertexData( const VertexBufferData& buffer );
+ virtual void UpdateIndexData (const IndexBufferData& buffer);
+ virtual void DrawVBO (const ChannelAssigns& channels, UInt32 firstIndexByte, UInt32 indexCount, GfxPrimitiveType topology, UInt32 firstVertex, UInt32 vertexCount );
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ virtual void DrawCustomIndexed( const ChannelAssigns& channels, void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount ) ;
+ #endif
+ virtual bool MapVertexStream( VertexStreamData& outData, unsigned stream );
+ virtual void UnmapVertexStream( unsigned stream );
+ virtual bool IsVertexBufferLost() const;
+
+ virtual void SetIndicesDynamic(bool dynamic);
+
+ virtual int GetRuntimeMemorySize() const;
+
+ static void CleanupSharedBuffers();
+
+ virtual void UseAsStreamOutput() { m_useForSO = true; }
+ void BindToStreamOutput();
+ void UnbindFromStreamOutput();
+
+private:
+ void UpdateVertexStream (const VertexBufferData& sourceData, unsigned stream);
+ void UpdateIndexBufferData (const IndexBufferData& sourceData);
+ void BindVertexStreams (GfxDevice& device, ID3D11DeviceContext* ctx, const ChannelAssigns& channels);
+
+ static UInt16* MapDynamicIndexBuffer (int indexCount, UInt32& outBytesUsed);
+ static void UnmapDynamicIndexBuffer ();
+
+ static ID3D11Buffer* GetAllWhiteBuffer();
+
+private:
+ int m_VertexCount;
+
+ ID3D11Buffer* m_VBStreams[kMaxVertexStreams];
+ ChannelInfoArray m_ChannelInfo;
+ ID3D11Buffer* m_StagingVB[kMaxVertexStreams];
+ ID3D11Buffer* m_IB;
+ ID3D11Buffer* m_StagingIB;
+ UInt16* m_IBReadable;
+ int m_IBSize;
+ bool m_useForSO;
+
+ static ID3D11Buffer* ms_CustomIB;
+ static int ms_CustomIBSize;
+ static UInt32 ms_CustomIBUsedBytes;
+ static ID3D11Buffer* ms_AllWhiteBuffer;
+};
+
+
+class DynamicD3D11VBO : public DynamicVBO {
+public:
+ DynamicD3D11VBO( UInt32 vbSize, UInt32 ibSize );
+ virtual ~DynamicD3D11VBO();
+
+ virtual bool GetChunk( UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode mode, void** outVB, void** outIB );
+ virtual void ReleaseChunk( UInt32 actualVertices, UInt32 actualIndices );
+ virtual void DrawChunk (const ChannelAssigns& channels);
+
+ ID3D11Buffer* GetQuadsIB()
+ {
+ if (!m_QuadsIB)
+ InitializeQuadsIB();
+ return m_QuadsIB;
+ }
+
+ #if UNITY_EDITOR
+ void DrawChunkUserPrimitives (GfxPrimitiveType type);
+ #endif
+
+private:
+ void InitializeQuadsIB();
+
+private:
+ UInt32 m_VBSize;
+ UInt32 m_VBUsedBytes;
+ UInt32 m_IBSize;
+ UInt32 m_IBUsedBytes;
+
+ ID3D11Buffer* m_VB;
+ ID3D11Buffer* m_IB;
+
+ UInt32 m_LastChunkStartVertex;
+ UInt32 m_LastChunkStartIndex;
+
+ ID3D11Buffer* m_QuadsIB;
+};
diff --git a/Runtime/GfxDevice/d3d11/D3D11Window.cpp b/Runtime/GfxDevice/d3d11/D3D11Window.cpp
new file mode 100644
index 0000000..bdf2e1f
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/D3D11Window.cpp
@@ -0,0 +1,220 @@
+#include "UnityPrefix.h"
+#include "D3D11Window.h"
+#include "D3D11Context.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Misc/QualitySettings.h"
+
+
+#if UNITY_EDITOR
+
+static D3D11Window* s_CurrentD3D11Window = NULL;
+static int s_CurrentD3D11AA = 0;
+
+void SetNoRenderTextureActiveEditor(); // RenderTexture.cpp
+void InternalDestroyRenderSurfaceD3D11 (RenderSurfaceD3D11* rs, TexturesD3D11* textures); // RenderTextureD3D11.cpp
+bool InitD3D11RenderDepthSurface (RenderDepthSurfaceD3D11& rs, TexturesD3D11* textures, bool sampleOnly);
+
+
+D3D11Window::D3D11Window (HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias)
+: GfxDeviceWindow(window, width, height, depthFormat, antiAlias)
+, m_SwapChain(NULL)
+, m_AntiAlias(0)
+{
+ Reshape (width, height, depthFormat, antiAlias);
+}
+
+D3D11Window::~D3D11Window()
+{
+ if (s_CurrentD3D11Window == this)
+ {
+ s_CurrentD3D11Window = NULL;
+ s_CurrentD3D11AA = 0;
+ }
+
+ InternalDestroyRenderSurfaceD3D11 (&m_DepthStencil, NULL);
+ InternalDestroyRenderSurfaceD3D11 (&m_BackBuffer, NULL);
+ SAFE_RELEASE (m_SwapChain);
+}
+
+bool D3D11Window::Reshape (int width, int height, DepthBufferFormat depthFormat, int antiAlias)
+{
+ if (!GfxDeviceWindow::Reshape(width, height, depthFormat, antiAlias))
+ return false;
+
+ // release old
+ SAFE_RELEASE(m_DepthStencil.m_Texture);
+ SAFE_RELEASE(m_DepthStencil.m_SRView);
+ SAFE_RELEASE(m_DepthStencil.m_DSView);
+ m_BackBuffer.Reset();
+ SAFE_RELEASE(m_SwapChain);
+
+ // pick supported AA level
+ if (antiAlias == -1)
+ antiAlias = GetQualitySettings().GetCurrent().antiAliasing;
+ while (antiAlias > 1 && !(gGraphicsCaps.d3d11.msaa & (1<<antiAlias)))
+ --antiAlias;
+ antiAlias = std::max(antiAlias, 1);
+
+ HRESULT hr;
+
+ const bool sRGB = GetActiveColorSpace() == kLinearColorSpace;
+
+ const bool createSRV = (gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0); // swap chain can't have SRV before 10.0, it seems
+
+ DXGI_SWAP_CHAIN_DESC sd;
+ ZeroMemory (&sd, sizeof(DXGI_SWAP_CHAIN_DESC));
+ sd.BufferCount = 1;
+ sd.BufferDesc.Width = m_Width;
+ sd.BufferDesc.Height = m_Height;
+ sd.BufferDesc.Format = sRGB ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
+ sd.BufferDesc.RefreshRate.Numerator = 0;
+ sd.BufferDesc.RefreshRate.Denominator = 1;
+ sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ if (createSRV)
+ sd.BufferUsage |= DXGI_USAGE_SHADER_INPUT;
+ sd.OutputWindow = m_Window;
+ sd.SampleDesc.Count = antiAlias;
+ sd.SampleDesc.Quality = 0;
+ sd.Windowed = TRUE;
+
+ hr = GetDXGIFactory()->CreateSwapChain (GetD3D11Device(), &sd, &m_SwapChain);
+ Assert(SUCCEEDED(hr));
+
+ if (FAILED(hr))
+ {
+ printf_console ("d3d11: swap chain: w=%i h=%i fmt=%i\n",
+ sd.BufferDesc.Width, sd.BufferDesc.Height, sd.BufferDesc.Format);
+ printf_console ("d3d11: failed to create swap chain [0x%x]\n", hr);
+ m_InvalidState = true;
+ return !m_InvalidState;
+ }
+
+ //@TODO: false if AA is used?
+ m_CanUseBlitOptimization = true;
+ m_AntiAlias = 0;
+
+ // Swap Chain
+ ID3D11Texture2D* backBufferTexture;
+ hr = m_SwapChain->GetBuffer (0, __uuidof(*backBufferTexture), (void**)&backBufferTexture);
+ Assert(SUCCEEDED(hr));
+
+ // Set the primary backbuffer view
+ ID3D11RenderTargetView* rtView;
+ D3D11_RENDER_TARGET_VIEW_DESC rtDesc;
+ rtDesc.Format = sRGB ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
+ rtDesc.ViewDimension = antiAlias > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS : D3D11_RTV_DIMENSION_TEXTURE2D;
+ rtDesc.Texture2D.MipSlice = 0;
+ hr = GetD3D11Device()->CreateRenderTargetView (backBufferTexture, &rtDesc, &rtView);
+ Assert(SUCCEEDED(hr));
+
+ // Set the secondary backbuffer view
+ ID3D11RenderTargetView* rtViewSecondary;
+ D3D11_RENDER_TARGET_VIEW_DESC rtDescSecondary;
+ rtDescSecondary.Format = !sRGB ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
+ rtDescSecondary.ViewDimension = antiAlias > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS : D3D11_RTV_DIMENSION_TEXTURE2D;
+ rtDescSecondary.Texture2D.MipSlice = 0;
+ hr = GetD3D11Device()->CreateRenderTargetView (backBufferTexture, &rtDescSecondary, &rtViewSecondary);
+ Assert(SUCCEEDED(hr));
+
+ // Create shader resource view
+ if (createSRV)
+ {
+ D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
+ srvDesc.Format = sd.BufferDesc.Format;
+ srvDesc.ViewDimension = antiAlias > 1 ? D3D11_SRV_DIMENSION_TEXTURE2DMS : D3D11_SRV_DIMENSION_TEXTURE2D;
+ srvDesc.Texture2D.MostDetailedMip = 0;
+ srvDesc.Texture2D.MipLevels = 1;
+ ID3D11ShaderResourceView* srView = NULL;
+ hr = GetD3D11Device()->CreateShaderResourceView (backBufferTexture, &srvDesc, &m_BackBuffer.m_SRView);
+ Assert (SUCCEEDED(hr));
+ }
+
+ //Pass through flags
+ m_BackBuffer.m_Texture = backBufferTexture;
+ m_BackBuffer.SetRTV (0, 0, false, rtView);
+ m_BackBuffer.SetRTV (0, 0, true, rtViewSecondary);
+ m_BackBuffer.width = sd.BufferDesc.Width;
+ m_BackBuffer.height = sd.BufferDesc.Height;
+ m_BackBuffer.samples = antiAlias;
+ m_BackBuffer.format = kRTFormatARGB32;
+ m_BackBuffer.backBuffer = true;
+ m_BackBuffer.flags = sRGB ? (m_BackBuffer.flags | kSurfaceCreateSRGB) : (m_BackBuffer.flags & ~kSurfaceCreateSRGB);
+
+ // Depth stencil
+ m_DepthStencil.width = m_Width;
+ m_DepthStencil.height = m_Height;
+ m_DepthStencil.samples = antiAlias;
+ m_DepthStencil.depthFormat = depthFormat;
+ m_DepthStencil.backBuffer = true;
+
+ bool dsOk = InitD3D11RenderDepthSurface (m_DepthStencil, NULL, false);
+ Assert (dsOk);
+
+ return !m_InvalidState;
+}
+
+void D3D11Window::SetAsActiveWindow ()
+{
+ GfxDevice& device = GetRealGfxDevice();
+ device.SetRenderTargets(1, &GetBackBuffer(), GetDepthStencil());
+ device.SetActiveRenderTexture(NULL);
+ device.SetCurrentWindowSize(m_Width, m_Height);
+ device.SetInvertProjectionMatrix(false);
+
+ s_CurrentD3D11Window = this;
+ s_CurrentD3D11AA = m_AntiAlias;
+}
+
+bool D3D11Window::BeginRendering()
+{
+ if (!GfxDeviceWindow::BeginRendering())
+ return false;
+
+ SetAsActiveWindow ();
+
+ GfxDevice& device = GetRealGfxDevice();
+ if (device.IsInsideFrame())
+ {
+ ErrorString ("GUI Window tries to begin rendering while something else has not finished rendering! Either you have a recursive OnGUI rendering, or previous OnGUI did not clean up properly.");
+ }
+ device.SetInsideFrame(true);
+
+ return true;
+}
+
+bool D3D11Window::EndRendering( bool presentContent )
+{
+ if (!GfxDeviceWindow::EndRendering(presentContent))
+ return false;
+
+ GfxDevice& device = GetRealGfxDevice();
+ Assert(device.IsInsideFrame());
+ device.SetInsideFrame(false);
+
+ s_CurrentD3D11Window = NULL;
+
+ HRESULT hr;
+ if (m_SwapChain && presentContent)
+ {
+ HRESULT hr = m_SwapChain->Present (0, 0);
+ Assert (SUCCEEDED(hr));
+ }
+ return true;
+}
+
+RenderSurfaceHandle D3D11Window::GetBackBuffer()
+{
+ RenderSurfaceHandle handle;
+ handle.object = &m_BackBuffer;
+ return handle;
+}
+
+RenderSurfaceHandle D3D11Window::GetDepthStencil()
+{
+ RenderSurfaceHandle handle;
+ handle.object = &m_DepthStencil;
+ return handle;
+}
+
+#endif
diff --git a/Runtime/GfxDevice/d3d11/D3D11Window.h b/Runtime/GfxDevice/d3d11/D3D11Window.h
new file mode 100644
index 0000000..e269cf2
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/D3D11Window.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "External/DirectX/builds/dx11include/d3d11.h"
+#include "Runtime/GfxDevice/GfxDeviceWindow.h"
+#include "Runtime/GfxDevice/GfxDeviceObjects.h"
+#include "TexturesD3D11.h"
+
+class D3D11Window : public GfxDeviceWindow
+{
+private:
+ IDXGISwapChain* m_SwapChain;
+ RenderColorSurfaceD3D11 m_BackBuffer;
+ RenderDepthSurfaceD3D11 m_DepthStencil;
+ int m_AntiAlias;
+
+public:
+ D3D11Window (HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias);
+ ~D3D11Window ();
+
+ bool Reshape (int width, int height, DepthBufferFormat depthFormat, int antiAlias);
+
+ bool BeginRendering ();
+ bool EndRendering (bool presentContent);
+ void SetAsActiveWindow ();
+
+ RenderSurfaceHandle GetBackBuffer();
+ RenderSurfaceHandle GetDepthStencil();
+};
diff --git a/Runtime/GfxDevice/d3d11/FixedFunctionStateD3D11.h b/Runtime/GfxDevice/d3d11/FixedFunctionStateD3D11.h
new file mode 100644
index 0000000..e174f26
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/FixedFunctionStateD3D11.h
@@ -0,0 +1,98 @@
+#pragma once
+
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+
+enum TextureSourceD3D11 {
+ kTexSourceUV0,
+ kTexSourceUV1,
+ kTexSourceUV2,
+ kTexSourceUV3,
+ kTexSourceUV4,
+ kTexSourceUV5,
+ kTexSourceUV6,
+ kTexSourceUV7,
+ // match the order of TexGenMode!
+ kTexSourceSphereMap,
+ kTexSourceObject,
+ kTexSourceEyeLinear,
+ kTexSourceCubeReflect,
+ kTexSourceCubeNormal,
+ kTexSourceTypeCount
+};
+
+#define CMP_STATE(member) { \
+ if (a.member < b.member) \
+ return true; \
+ else if (b.member < a.member) \
+ return false; \
+ }
+
+
+struct FixedFunctionStateD3D11
+{
+ FixedFunctionStateD3D11()
+ : texUnitSources(0)
+ , texUnitCube(0)
+ , texUnit3D(0)
+ , texUnitProjected(0)
+ , texUnitCount(0)
+ , alphaTest(kFuncDisabled)
+ , useUniformInsteadOfVertexColor(false)
+ , lightingEnabled(false)
+ , specularEnabled(false)
+ , lightCount(0)
+ , colorMaterial(kColorMatDisabled)
+ , fogMode(kFogDisabled)
+ {
+ for (int i = 0; i < kMaxSupportedTextureUnits; ++i)
+ {
+ texUnitColorCombiner[i] = ~0U;
+ texUnitAlphaCombiner[i] = ~0U;
+ }
+ }
+
+ UInt64 texUnitSources; // 4 bits for each unit
+ UInt32 texUnitColorCombiner[kMaxSupportedTextureUnits];
+ UInt32 texUnitAlphaCombiner[kMaxSupportedTextureUnits];
+ UInt32 texUnitCube; // bit per unit
+ UInt32 texUnit3D; // bit per unit
+ UInt32 texUnitProjected; // bit per unit
+
+ int texUnitCount;
+ CompareFunction alphaTest;
+
+ bool useUniformInsteadOfVertexColor;
+ bool lightingEnabled;
+ bool specularEnabled;
+ int lightCount;
+ ColorMaterialMode colorMaterial;
+ FogMode fogMode;
+};
+
+
+struct FixedFuncStateCompareD3D11
+{
+ bool operator() (const FixedFunctionStateD3D11& a, const FixedFunctionStateD3D11& b) const
+ {
+ CMP_STATE(lightingEnabled);
+ CMP_STATE(specularEnabled);
+ CMP_STATE(lightCount);
+ CMP_STATE(texUnitCount);
+ CMP_STATE(texUnitSources);
+ CMP_STATE(texUnitCube);
+ CMP_STATE(texUnit3D);
+ CMP_STATE(texUnitProjected);
+ CMP_STATE(alphaTest);
+ for (int i = 0; i < a.texUnitCount; i++)
+ {
+ CMP_STATE(texUnitColorCombiner[i])
+ CMP_STATE(texUnitAlphaCombiner[i])
+ }
+ CMP_STATE(useUniformInsteadOfVertexColor);
+ CMP_STATE(colorMaterial);
+ CMP_STATE(fogMode);
+
+ return false;
+ }
+};
diff --git a/Runtime/GfxDevice/d3d11/GfxDeviceD3D11.cpp b/Runtime/GfxDevice/d3d11/GfxDeviceD3D11.cpp
new file mode 100644
index 0000000..237ae6c
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/GfxDeviceD3D11.cpp
@@ -0,0 +1,3133 @@
+#include "UnityPrefix.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "D3D11Context.h"
+#include "D3D11VBO.h"
+#include "External/shaderlab/Library/program.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "Runtime/GfxDevice/GpuProgramParamsApply.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "External/shaderlab/Library/properties.h"
+#include "D3D11Utils.h"
+#include "GpuProgramsD3D11.h"
+#include "ShaderPatchingD3D11.h"
+#include "TimerQueryD3D11.h"
+#include "PlatformDependent/Win/SmartComPointer.h"
+#include "PlatformDependent/Win/WinUnicode.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Graphics/GraphicsHelper.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Misc/Plugins.h"
+#if UNITY_EDITOR
+#include "D3D11Window.h"
+#endif
+#include "Runtime/GfxDevice/d3d11/StreamOutSkinnedMesh.h"
+
+class GfxDeviceD3D11;
+
+namespace ShaderLab {
+ TexEnv* GetTexEnvForBinding( const TextureBinding& binding, const PropertySheet* props ); // pass.cpp
+}
+
+
+#include "GfxDeviceD3D11.h"
+
+extern const InputSignatureD3D11* g_CurrentVSInputD3D11;
+extern ID3D11InputLayout* g_ActiveInputLayoutD3D11;
+extern D3D11_PRIMITIVE_TOPOLOGY g_ActiveTopologyD3D11;
+
+
+static ShaderLab::FastPropertyName kSLPropFogCB = ShaderLab::Property ("UnityFogPatchCB");
+
+
+
+static const D3D11_COMPARISON_FUNC kCmpFuncD3D11[] = {
+ D3D11_COMPARISON_ALWAYS, D3D11_COMPARISON_NEVER, D3D11_COMPARISON_LESS, D3D11_COMPARISON_EQUAL, D3D11_COMPARISON_LESS_EQUAL, D3D11_COMPARISON_GREATER, D3D11_COMPARISON_NOT_EQUAL, D3D11_COMPARISON_GREATER_EQUAL, D3D11_COMPARISON_ALWAYS
+};
+
+static const D3D11_STENCIL_OP kStencilOpD3D11[] = {
+ D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_ZERO, D3D11_STENCIL_OP_REPLACE, D3D11_STENCIL_OP_INCR_SAT,
+ D3D11_STENCIL_OP_DECR_SAT, D3D11_STENCIL_OP_INVERT, D3D11_STENCIL_OP_INCR, D3D11_STENCIL_OP_DECR
+};
+
+// Graphics device requires access to reset render textures
+bool RebindActiveRenderTargets (TexturesD3D11* textures);
+
+DXGI_FORMAT GetRenderTextureFormat (RenderTextureFormat format, bool sRGB);
+DXGI_FORMAT GetShaderResourceViewFormat (RenderTextureFormat format, bool sRGB);
+extern DXGI_FORMAT kD3D11RenderResourceFormats[kRTFormatCount];
+extern DXGI_FORMAT kD3D11RenderTextureFormatsNorm[kRTFormatCount];
+
+
+bool SetTopologyD3D11 (GfxPrimitiveType topology, GfxDevice& device, ID3D11DeviceContext* ctx);
+void SetInputLayoutD3D11 (ID3D11DeviceContext* ctx, ID3D11InputLayout* layout);
+
+
+// --------------------------------------------------------------------------
+
+
+ResolveTexturePool::ResolveTexturePool()
+: m_UseCounter(0)
+{
+ memset (m_Entries, 0, sizeof(m_Entries));
+}
+
+void ResolveTexturePool::Clear()
+{
+ for (int i = 0; i < ARRAY_SIZE(m_Entries); ++i)
+ {
+ SAFE_RELEASE(m_Entries[i].texture);
+ SAFE_RELEASE(m_Entries[i].srv);
+ }
+}
+
+ResolveTexturePool::Entry* ResolveTexturePool::GetResolveTexture (int width, int height, RenderTextureFormat fmt, bool sRGB)
+{
+ ++m_UseCounter;
+
+ // check if we have a suitable temporary resolve texture already?
+ int newIndex = -1;
+ int lruIndex = 0;
+ int lruScore = 0;
+ for (int i = 0; i < ARRAY_SIZE(m_Entries); ++i)
+ {
+ Entry& e = m_Entries[i];
+ if (e.width == width && e.height == height && e.format == fmt && e.sRGB == sRGB)
+ {
+ Assert (e.texture);
+ Assert (e.srv);
+ e.lastUse = m_UseCounter;
+ return &e;
+ }
+
+ if (e.width == 0)
+ {
+ // unused slot
+ Assert (e.height == 0 && !e.texture && !e.srv);
+ if (newIndex == -1)
+ newIndex = i;
+ }
+ else
+ {
+ // used slot
+ if (m_UseCounter - e.lastUse > lruScore)
+ {
+ lruIndex = i;
+ lruScore = m_UseCounter - e.lastUse;
+ }
+ }
+ }
+
+ // if all slots are used; release least recently used
+ if (newIndex == -1)
+ {
+ Entry& e = m_Entries[lruIndex];
+ Assert (e.texture);
+ e.width = e.height = 0;
+ SAFE_RELEASE(e.texture);
+ SAFE_RELEASE(e.srv);
+ newIndex = lruIndex;
+ }
+
+ Entry& ee = m_Entries[newIndex];
+
+ // create texture & SRV in this slot
+
+ ID3D11Device* dev = GetD3D11Device();
+ D3D11_TEXTURE2D_DESC tDesc;
+ tDesc.Width = width;
+ tDesc.Height = height;
+ tDesc.MipLevels = 1;
+ tDesc.ArraySize = 1;
+ tDesc.Format = (gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0 ? kD3D11RenderResourceFormats[fmt] : kD3D11RenderTextureFormatsNorm[fmt]);
+
+ tDesc.SampleDesc.Count = 1;
+ tDesc.SampleDesc.Quality = 0;
+ tDesc.Usage = D3D11_USAGE_DEFAULT;
+ tDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+ // 9.x feature levels require the resolved texture to also have a render target flag, otherwise
+ // CopySubresourceRegion will silently corrupt runtime/driver state.
+ if (gGraphicsCaps.d3d11.featureLevel < kDX11Level10_0)
+ tDesc.BindFlags |= D3D11_BIND_RENDER_TARGET;
+
+ tDesc.CPUAccessFlags = 0;
+ tDesc.MiscFlags = 0;
+
+ HRESULT hr = dev->CreateTexture2D (&tDesc, NULL, &ee.texture);
+ if (FAILED(hr))
+ return NULL;
+ SetDebugNameD3D11 (ee.texture, Format("ResolveTexture2D-%dx%d", width, height));
+
+ D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
+ srvDesc.Format = GetShaderResourceViewFormat (fmt, (gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0) ? sRGB : false);
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+ srvDesc.Texture2D.MostDetailedMip = 0;
+ srvDesc.Texture2D.MipLevels = 1;
+ hr = dev->CreateShaderResourceView (ee.texture, &srvDesc, &ee.srv);
+ if (FAILED(hr))
+ return NULL;
+ SetDebugNameD3D11 (ee.srv, Format("ResolveTexture2D-SRV-%dx%d", width, height));
+
+ ee.width = width;
+ ee.height = height;
+ ee.format = fmt;
+ ee.sRGB = sRGB;
+ ee.lastUse = m_UseCounter;
+
+ return &ee;
+}
+
+
+// --------------------------------------------------------------------------
+
+
+static FixedFunctionProgramD3D11* GetFixedFunctionProgram11 (FFProgramCacheD3D11& cache, const FixedFunctionStateD3D11& state)
+{
+ // Do we have one for this state already?
+ FFProgramCacheD3D11::iterator cachedProgIt = cache.find (state);
+ if (cachedProgIt != cache.end())
+ return cachedProgIt->second;
+
+ // Don't have one yet, create it
+ FixedFunctionProgramD3D11* ffProg = new FixedFunctionProgramD3D11 (state);
+ cache.insert (std::make_pair(state, ffProg));
+ return ffProg;
+}
+
+
+
+// --------------------------------------------------------------------------
+
+
+
+void GfxDeviceD3D11::SetupDeferredDepthStencilState ()
+{
+ ID3D11DepthStencilState* dss = m_CurrDSState;
+ if (!dss)
+ {
+ DepthStencilState state;
+ memset (&state, 0, sizeof(state));
+ if (m_CurrDepthState)
+ state.d = *m_CurrDepthState;
+ if (m_CurrStencilState)
+ state.s = *m_CurrStencilState;
+
+ CachedDepthStencilStates::iterator it = m_CachedDepthStencilStates.find(state);
+ if (it == m_CachedDepthStencilStates.end())
+ {
+ D3D11_DEPTH_STENCIL_DESC desc;
+ memset (&desc, 0, sizeof(desc));
+ if (m_CurrDepthState)
+ {
+ desc.DepthEnable = TRUE;
+ desc.DepthWriteMask = (m_CurrDepthState->sourceState.depthWrite ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO);
+ desc.DepthFunc = kCmpFuncD3D11[m_CurrDepthState->sourceState.depthFunc];
+ }
+ if (m_CurrStencilState)
+ {
+ desc.StencilEnable = m_CurrStencilState->sourceState.stencilEnable;
+ desc.StencilReadMask = m_CurrStencilState->sourceState.readMask;
+ desc.StencilWriteMask = m_CurrStencilState->sourceState.writeMask;
+ desc.FrontFace.StencilFunc = kCmpFuncD3D11[m_CurrStencilState->sourceState.stencilFuncFront];
+ desc.FrontFace.StencilFailOp = kStencilOpD3D11[m_CurrStencilState->sourceState.stencilFailOpFront];
+ desc.FrontFace.StencilDepthFailOp = kStencilOpD3D11[m_CurrStencilState->sourceState.stencilZFailOpFront];
+ desc.FrontFace.StencilPassOp = kStencilOpD3D11[m_CurrStencilState->sourceState.stencilPassOpFront];
+ desc.BackFace.StencilFunc = kCmpFuncD3D11[m_CurrStencilState->sourceState.stencilFuncBack];
+ desc.BackFace.StencilFailOp = kStencilOpD3D11[m_CurrStencilState->sourceState.stencilFailOpBack];
+ desc.BackFace.StencilDepthFailOp = kStencilOpD3D11[m_CurrStencilState->sourceState.stencilZFailOpBack];
+ desc.BackFace.StencilPassOp = kStencilOpD3D11[m_CurrStencilState->sourceState.stencilPassOpBack];
+ }
+
+ ID3D11DepthStencilState* d3dstate = NULL;
+ HRESULT hr = GetD3D11Device()->CreateDepthStencilState (&desc, &d3dstate);
+ Assert(SUCCEEDED(hr));
+ SetDebugNameD3D11 (d3dstate, Format("DepthStencilState-%d-%d", desc.DepthWriteMask, desc.DepthFunc));
+ it = m_CachedDepthStencilStates.insert (std::make_pair(state, d3dstate)).first;
+ }
+ dss = it->second;
+ }
+ if (dss != m_CurrDSState || m_StencilRef != m_CurrStencilRef)
+ {
+ GetD3D11Context()->OMSetDepthStencilState (dss, m_StencilRef);
+ m_CurrDSState = dss;
+ m_CurrStencilRef = m_StencilRef;
+ }
+}
+
+void GfxDeviceD3D11::SetupDeferredRasterState ()
+{
+ // raster state; needs to be deferred due to cull winding / scissor / wireframe
+ // not known at creation time
+ if (!m_CurrRasterState)
+ return;
+
+ ID3D11RasterizerState* rss = m_CurrRSState;
+ if (!rss)
+ {
+ FinalRasterState11 rsKey;
+ memcpy (&rsKey.raster, &m_CurrRasterState->sourceState, sizeof(rsKey.raster));
+ rsKey.backface = (m_CurrRasterState->sourceState.cullMode != kCullOff) && ((m_AppBackfaceMode==m_UserBackfaceMode) == m_InvertProjMatrix);
+ rsKey.wireframe = m_Wireframe;
+ rsKey.scissor = m_Scissor;
+
+ CachedFinalRasterStates::iterator it = m_CachedFinalRasterStates.find(rsKey);
+ if (it == m_CachedFinalRasterStates.end())
+ {
+ D3D11_RASTERIZER_DESC desc;
+ memset (&desc, 0, sizeof(desc));
+
+ desc.FrontCounterClockwise = rsKey.backface ? TRUE : FALSE;
+ //TODO: wtf??? DepthBias doesn't work at 9.1
+ if (gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0)
+ desc.DepthBias = rsKey.raster.sourceState.depthBias;
+ desc.SlopeScaledDepthBias = rsKey.raster.sourceState.slopeScaledDepthBias;
+ desc.ScissorEnable = m_Scissor;
+ desc.MultisampleEnable = TRUE; // only applies to line drawing in MSAA; if set to FALSE lines will be aliased even when MSAA is used
+ desc.DepthClipEnable = TRUE;
+ desc.FillMode = rsKey.wireframe ? D3D11_FILL_WIREFRAME : D3D11_FILL_SOLID;
+ switch (rsKey.raster.sourceState.cullMode)
+ {
+ case kCullOff: desc.CullMode = D3D11_CULL_NONE; break;
+ case kCullFront: desc.CullMode = D3D11_CULL_FRONT; break;
+ case kCullBack: desc.CullMode = D3D11_CULL_BACK; break;
+ default: AssertIf("Unsupported cull mode!");
+ }
+ ID3D11RasterizerState* d3dstate = NULL;
+ HRESULT hr = GetD3D11Device()->CreateRasterizerState (&desc, &d3dstate);
+ Assert(SUCCEEDED(hr));
+ SetDebugNameD3D11 (d3dstate, Format("RasterizerState-%d-%d", desc.FrontCounterClockwise, desc.FillMode));
+ it = m_CachedFinalRasterStates.insert (std::make_pair(rsKey, d3dstate)).first;
+ }
+ rss = it->second;
+ }
+ if (rss != m_CurrRSState)
+ {
+ GetD3D11Context()->RSSetState (rss);
+ m_CurrRSState = rss;
+ }
+}
+
+
+void UpdateChannelBindingsD3D11 (const ChannelAssigns& channels)
+{
+ DX11_LOG_ENTER_FUNCTION("UpdateChannelBindingsD3D11");
+ GfxDeviceD3D11& device = static_cast<GfxDeviceD3D11&>(GetRealGfxDevice());
+ if (!device.IsShaderActive(kShaderVertex))
+ {
+ const int maxTexCoords = gGraphicsCaps.maxTexCoords; // fetch here once
+ UInt64 textureSources = device.m_FFState.texUnitSources;
+ for (int i = 0; i < maxTexCoords; ++i)
+ {
+ UInt32 source = (textureSources >> (i*4)) & 0xF;
+ if (source > kTexSourceUV7)
+ continue;
+ ShaderChannel texCoordChannel = channels.GetSourceForTarget ((VertexComponent)(kVertexCompTexCoord0 + i));
+ if (texCoordChannel == kShaderChannelTexCoord0)
+ textureSources = textureSources & ~(0xFUL<<i*4) | (UInt64(kTexSourceUV0)<<i*4);
+ else if (texCoordChannel == kShaderChannelTexCoord1)
+ textureSources = textureSources & ~(0xFUL<<i*4) | (UInt64(kTexSourceUV1)<<i*4);
+ else if (texCoordChannel != kShaderChannelNone) {
+ AssertString( "Bad texcoord index" );
+ }
+ }
+ device.m_FFState.texUnitSources = textureSources;
+ }
+
+ device.m_FFState.useUniformInsteadOfVertexColor = !(channels.GetTargetMap() & (1<<kVertexCompColor));
+}
+
+
+struct SetValuesFunctorD3D11
+{
+ SetValuesFunctorD3D11(GfxDevice& device, ConstantBuffersD3D11& cbs) : m_Device(device), m_CBs(cbs) { }
+ GfxDevice& m_Device;
+ ConstantBuffersD3D11& m_CBs;
+ void SetVectorVal (ShaderType shaderType, ShaderParamType type, int index, const float* ptr, int cols, const GpuProgramParameters& params, int cbIndex)
+ {
+ const GpuProgramParameters::ConstantBuffer& cb = params.GetConstantBuffers()[cbIndex];
+ int idx = m_CBs.FindAndBindCB (cb.m_Name.index, shaderType, cb.m_BindIndex, cb.m_Size);
+ if (type != kShaderParamInt)
+ m_CBs.SetCBConstant (idx, index, ptr, cols*4);
+ else
+ {
+ int vali[4] = {ptr[0], ptr[1], ptr[2], ptr[3]};
+ m_CBs.SetCBConstant (idx, index, vali, cols*4);
+ }
+ }
+ void SetMatrixVal (ShaderType shaderType, int index, const Matrix4x4f* ptr, int rows, const GpuProgramParameters& params, int cbIndex)
+ {
+ DebugAssert(rows == 4);
+ const GpuProgramParameters::ConstantBuffer& cb = params.GetConstantBuffers()[cbIndex];
+ int idx = m_CBs.FindAndBindCB (cb.m_Name.index, shaderType, cb.m_BindIndex, cb.m_Size);
+ m_CBs.SetCBConstant (idx, index, ptr, 64);
+ }
+ void SetTextureVal (ShaderType shaderType, int index, int samplerIndex, TextureDimension dim, TextureID texID)
+ {
+ m_Device.SetTexture (shaderType, index, samplerIndex, texID, dim, std::numeric_limits<float>::infinity());
+ }
+};
+
+
+void GfxDeviceD3D11::BeforeDrawCall( bool immediateMode )
+{
+ DX11_LOG_ENTER_FUNCTION("GfxDeviceD3D11::BeforeDrawCall");
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ HRESULT hr;
+
+ SetupDeferredSRGBWrite ();
+ SetupDeferredDepthStencilState ();
+ SetupDeferredRasterState ();
+
+ m_TransformState.UpdateWorldViewMatrix (m_BuiltinParamValues);
+
+ if (m_FogParams.mode != kFogDisabled)
+ {
+ float diff = m_FogParams.mode == kFogLinear ? m_FogParams.end - m_FogParams.start : 0.0f;
+ float invDiff = Abs(diff) > 0.0001f ? 1.0f/diff : 0.0f;
+ Vector4f fogParams(m_FogParams.density * 1.2011224087f,
+ m_FogParams.density * 1.4426950408f,
+ m_FogParams.mode == kFogLinear ? -invDiff : 0.0f,
+ m_FogParams.mode == kFogLinear ? m_FogParams.end * invDiff : 0.0f
+ );
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFFogParams, fogParams);
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFFogColor, m_FogParams.color);
+ }
+
+ void* shader[kShaderTypeCount];
+ for (int pt = 0; pt < kShaderTypeCount; ++pt)
+ {
+ shader[pt] = NULL;
+ m_BuiltinParamIndices[pt] = &m_NullParamIndices;
+ }
+
+ if (m_ActiveGpuProgram[kShaderVertex] && m_ActiveGpuProgram[kShaderFragment])
+ {
+ // Programmable shaders
+ const bool haveDomainShader = m_ActiveGpuProgram[kShaderDomain];
+ bool resetToNoFog = false;
+ for (int pt = kShaderVertex; pt < kShaderTypeCount; ++pt)
+ {
+ if (m_ActiveGpuProgram[pt])
+ {
+ DebugAssert (!m_ActiveGpuProgram[pt] || m_ActiveGpuProgram[pt]->GetImplType() == pt);
+ m_BuiltinParamIndices[pt] = &m_ActiveGpuProgramParams[pt]->GetBuiltinParams();
+ D3D11CommonShader* prog = static_cast<D3D11CommonShader*>(m_ActiveGpuProgram[pt]);
+ shader[pt] = prog->GetShader(m_FogParams.mode, haveDomainShader, resetToNoFog);
+ if (resetToNoFog)
+ m_FogParams.mode = kFogDisabled;
+ }
+ }
+
+ // Apply fog parameters if needed
+ if (m_FogParams.mode > kFogDisabled)
+ {
+ const int cbIndex = m_CBs.FindAndBindCB (kSLPropFogCB.index, kShaderFragment, k11FogConstantBufferBind, k11FogSize*16);
+
+ m_CBs.SetCBConstant (cbIndex, k11FogColor*16, m_FogParams.color.GetPtr(), 16);
+
+ float params[4];
+ params[0] = m_FogParams.density * 1.2011224087f ; // density / sqrt(ln(2))
+ params[1] = m_FogParams.density * 1.4426950408f; // density / ln(2)
+ if (m_FogParams.mode == kFogLinear)
+ {
+ float diff = m_FogParams.end - m_FogParams.start;
+ float invDiff = Abs(diff) > 0.0001f ? 1.0f/diff : 0.0f;
+ params[2] = -invDiff;
+ params[3] = m_FogParams.end * invDiff;
+ }
+ else
+ {
+ params[2] = 0.0f;
+ params[3] = 0.0f;
+ }
+ m_CBs.SetCBConstant (cbIndex, k11FogParams*16, params, 16);
+ }
+ }
+ else
+ {
+ // Emulate fixed function
+ m_FFState.fogMode = m_FogParams.mode;
+ FixedFunctionProgramD3D11* program = GetFixedFunctionProgram11 (m_FFPrograms, m_FFState);
+
+ shader[kShaderVertex] = program->GetVertexShader();
+ shader[kShaderFragment] = program->GetPixelShader();
+
+ program->ApplyFFGpuProgram (m_BuiltinParamValues, m_CBs);
+
+ m_BuiltinParamIndices[kShaderVertex] = &program->GetVPMatrices();
+ }
+
+ // Set D3D shaders
+ for (int pt = kShaderVertex; pt < kShaderTypeCount; ++pt)
+ {
+ if (m_ActiveShaders[pt] == shader[pt])
+ continue;
+ switch (pt) {
+ case kShaderVertex: D3D11_CALL(ctx->VSSetShader ((ID3D11VertexShader*)shader[pt], NULL, 0)); break;
+ case kShaderFragment: D3D11_CALL(ctx->PSSetShader ((ID3D11PixelShader*)shader[pt], NULL, 0)); break;
+ case kShaderGeometry: D3D11_CALL(ctx->GSSetShader ((ID3D11GeometryShader*)shader[pt], NULL, 0)); break;
+ case kShaderHull: D3D11_CALL(ctx->HSSetShader ((ID3D11HullShader*)shader[pt], NULL, 0)); break;
+ case kShaderDomain: D3D11_CALL(ctx->DSSetShader ((ID3D11DomainShader*)shader[pt], NULL, 0)); break;
+ }
+ m_ActiveShaders[pt] = shader[pt];
+ }
+
+ // Set Unity built-in parameters
+ bool anyGpuIndexValid;
+ int gpuIndex[kShaderTypeCount];
+
+#define SET_BUILTIN_MATRIX_BEGIN(idx) \
+ anyGpuIndexValid = false; \
+ for (int pt = kShaderVertex; pt < kShaderTypeCount; ++pt) { \
+ int gi = m_BuiltinParamIndices[pt]->mat[idx].gpuIndex; \
+ if (gi >= 0) anyGpuIndexValid = true; \
+ gpuIndex[pt] = gi; \
+ } \
+ if (anyGpuIndexValid)
+
+#define SET_BUILTIN_MATRIX_END(idx,mtx) \
+ for (int pt = kShaderVertex; pt < kShaderTypeCount; ++pt) { \
+ int gi = gpuIndex[pt]; \
+ if (gi >= 0) m_CBs.SetBuiltinCBConstant (m_BuiltinParamIndices[pt]->mat[idx].cbID, gi, mtx.GetPtr(), sizeof(mtx)); \
+ }
+
+
+ // MVP matrix
+ SET_BUILTIN_MATRIX_BEGIN(kShaderInstanceMatMVP)
+ {
+ Matrix4x4f mat;
+ MultiplyMatrices4x4 (&m_BuiltinParamValues.GetMatrixParam(kShaderMatProj), &m_TransformState.worldViewMatrix, &mat);
+ SET_BUILTIN_MATRIX_END(kShaderInstanceMatMVP,mat);
+ }
+ // MV matrix
+ SET_BUILTIN_MATRIX_BEGIN(kShaderInstanceMatMV)
+ {
+ Matrix4x4f& mat = m_TransformState.worldViewMatrix;
+ SET_BUILTIN_MATRIX_END(kShaderInstanceMatMV,mat);
+ }
+ // Transpose MV matrix
+ SET_BUILTIN_MATRIX_BEGIN(kShaderInstanceMatTransMV)
+ {
+ Matrix4x4f mat;
+ TransposeMatrix4x4 (&m_TransformState.worldViewMatrix, &mat);
+ SET_BUILTIN_MATRIX_END(kShaderInstanceMatTransMV,mat);
+ }
+ // Inverse transpose of MV matrix
+ SET_BUILTIN_MATRIX_BEGIN(kShaderInstanceMatInvTransMV)
+ {
+ Matrix4x4f mat;
+ Matrix4x4f::Invert_Full (m_TransformState.worldViewMatrix, mat);
+ if (true) //@TODO m_VertexData.normalization == kNormalizationScale)
+ {
+ // Inverse transpose of modelview should be scaled by uniform
+ // normal scale (this will match state.matrix.invtrans.modelview
+ // and gl_NormalMatrix in OpenGL)
+ float scale = Magnitude (m_TransformState.worldMatrix.GetAxisX());
+ mat.Get (0, 0) *= scale;
+ mat.Get (1, 0) *= scale;
+ mat.Get (2, 0) *= scale;
+ mat.Get (0, 1) *= scale;
+ mat.Get (1, 1) *= scale;
+ mat.Get (2, 1) *= scale;
+ mat.Get (0, 2) *= scale;
+ mat.Get (1, 2) *= scale;
+ mat.Get (2, 2) *= scale;
+ }
+ Matrix4x4f transposed;
+ TransposeMatrix4x4 (&mat, &transposed);
+ SET_BUILTIN_MATRIX_END(kShaderInstanceMatInvTransMV,transposed);
+ }
+ // M matrix
+ SET_BUILTIN_MATRIX_BEGIN(kShaderInstanceMatM)
+ {
+ Matrix4x4f& mat = m_TransformState.worldMatrix;
+ SET_BUILTIN_MATRIX_END(kShaderInstanceMatM,mat);
+ }
+ // Inverse M matrix
+ SET_BUILTIN_MATRIX_BEGIN(kShaderInstanceMatInvM)
+ {
+ Matrix4x4f mat = m_TransformState.worldMatrix;
+ if (true) //@TODO m_VertexData.normalization == kNormalizationScale)
+ {
+ // Kill scale in the world matrix before inverse
+ float invScale = m_BuiltinParamValues.GetInstanceVectorParam(kShaderInstanceVecScale).w;
+ mat.Get (0, 0) *= invScale;
+ mat.Get (1, 0) *= invScale;
+ mat.Get (2, 0) *= invScale;
+ mat.Get (0, 1) *= invScale;
+ mat.Get (1, 1) *= invScale;
+ mat.Get (2, 1) *= invScale;
+ mat.Get (0, 2) *= invScale;
+ mat.Get (1, 2) *= invScale;
+ mat.Get (2, 2) *= invScale;
+ }
+ Matrix4x4f inverseMat;
+ Matrix4x4f::Invert_General3D (mat, inverseMat);
+ SET_BUILTIN_MATRIX_END(kShaderInstanceMatInvM,inverseMat);
+ }
+
+ // Set instance vector parameters
+ for (int i = 0; i < kShaderInstanceVecCount; ++i)
+ {
+ for (int pt = kShaderVertex; pt < kShaderTypeCount; ++pt)
+ {
+ int gi = m_BuiltinParamIndices[pt]->vec[i].gpuIndex;
+ if (gi >= 0)
+ {
+ m_CBs.SetBuiltinCBConstant (m_BuiltinParamIndices[pt]->vec[i].cbID, gi, m_BuiltinParamValues.GetInstanceVectorParam((ShaderBuiltinInstanceVectorParam)i).GetPtr(), m_BuiltinParamIndices[pt]->vec[i].dim*4);
+ }
+ }
+ }
+
+ // Texture matrices for vertex shader
+ for( int i = 0; i < 8; ++i )
+ {
+ const BuiltinShaderParamIndices::MatrixParamData* matParam = &m_BuiltinParamIndices[kShaderVertex]->mat[kShaderInstanceMatTexture0 + i];
+ if (matParam->gpuIndex >= 0)
+ {
+ const Matrix4x4f& mat = m_TextureUnits[i].matrix;
+ m_CBs.SetBuiltinCBConstant (matParam->cbID, matParam->gpuIndex, mat.GetPtr(), sizeof(mat));
+ }
+ }
+
+ // Material properties
+ SetValuesFunctorD3D11 setValuesFunc(*this, m_CBs);
+ ApplyMaterialPropertyBlockValues(m_MaterialProperties, m_ActiveGpuProgram, m_ActiveGpuProgramParams, setValuesFunc);
+
+ ///@TODO the rest
+
+ m_CBs.UpdateBuffers ();
+}
+
+static const D3D11_BLEND kBlendModeD3D11[] = {
+ D3D11_BLEND_ZERO, D3D11_BLEND_ONE, D3D11_BLEND_DEST_COLOR, D3D11_BLEND_SRC_COLOR, D3D11_BLEND_INV_DEST_COLOR, D3D11_BLEND_SRC_ALPHA, D3D11_BLEND_INV_SRC_COLOR,
+ D3D11_BLEND_DEST_ALPHA, D3D11_BLEND_INV_DEST_ALPHA, D3D11_BLEND_SRC_ALPHA_SAT, D3D11_BLEND_INV_SRC_ALPHA,
+};
+static const D3D11_BLEND kBlendModeAlphaD3D11[] = {
+ D3D11_BLEND_ZERO, D3D11_BLEND_ONE, D3D11_BLEND_DEST_ALPHA, D3D11_BLEND_SRC_ALPHA, D3D11_BLEND_INV_DEST_ALPHA, D3D11_BLEND_SRC_ALPHA, D3D11_BLEND_INV_SRC_ALPHA,
+ D3D11_BLEND_DEST_ALPHA, D3D11_BLEND_INV_DEST_ALPHA, D3D11_BLEND_SRC_ALPHA_SAT, D3D11_BLEND_INV_SRC_ALPHA,
+};
+static const D3D11_BLEND_OP kBlendOpD3D11[] = {
+ D3D11_BLEND_OP_ADD, D3D11_BLEND_OP_SUBTRACT, D3D11_BLEND_OP_REV_SUBTRACT, D3D11_BLEND_OP_MIN, D3D11_BLEND_OP_MAX,
+ /* ADD for all the logic op modes, used for fallback.*/
+ D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_OP_ADD,
+
+};
+
+static const D3D11_LOGIC_OP kLogicOpD3D11[] = {
+ /* Zeroes for the blend modes */
+ D3D11_LOGIC_OP_CLEAR,
+ D3D11_LOGIC_OP_CLEAR,
+ D3D11_LOGIC_OP_CLEAR,
+ D3D11_LOGIC_OP_CLEAR,
+ D3D11_LOGIC_OP_CLEAR,
+ /* Actual logic ops */
+ D3D11_LOGIC_OP_CLEAR,
+ D3D11_LOGIC_OP_SET,
+ D3D11_LOGIC_OP_COPY,
+ D3D11_LOGIC_OP_COPY_INVERTED,
+ D3D11_LOGIC_OP_NOOP,
+ D3D11_LOGIC_OP_INVERT,
+ D3D11_LOGIC_OP_AND,
+ D3D11_LOGIC_OP_NAND,
+ D3D11_LOGIC_OP_OR,
+ D3D11_LOGIC_OP_NOR,
+ D3D11_LOGIC_OP_XOR,
+ D3D11_LOGIC_OP_EQUIV,
+ D3D11_LOGIC_OP_AND_REVERSE,
+ D3D11_LOGIC_OP_AND_INVERTED,
+ D3D11_LOGIC_OP_OR_REVERSE,
+ D3D11_LOGIC_OP_OR_INVERTED
+};
+
+
+
+DeviceBlendState* GfxDeviceD3D11::CreateBlendState (const GfxBlendState& state)
+{
+ std::pair<CachedBlendStates::iterator, bool> result = m_CachedBlendStates.insert(std::make_pair(state, DeviceBlendStateD3D11()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceBlendStateD3D11& d3dstate = result.first->second;
+ memcpy (&d3dstate.sourceState, &state, sizeof(d3dstate.sourceState));
+
+ // DX11.1 logic ops, falls through to ADD blendop if not supported
+ if(state.blendOp >= kBlendOpLogicalClear && state.blendOp <= kBlendOpLogicalOrInverted
+ && gGraphicsCaps.hasBlendLogicOps)
+ {
+ D3D11_BLEND_DESC1 desc;
+ memset (&desc, 0, sizeof(desc));
+ if (gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0)
+ desc.AlphaToCoverageEnable = state.alphaToMask;
+
+ desc.IndependentBlendEnable = FALSE;
+
+ D3D11_RENDER_TARGET_BLEND_DESC1& dst = desc.RenderTarget[0];
+
+ dst.BlendEnable = false;
+ dst.LogicOpEnable = true;
+
+ dst.LogicOp = kLogicOpD3D11[state.blendOp];
+
+ DWORD d3dmask = 0;
+ const UInt8 mask = state.renderTargetWriteMask;
+ if( mask & kColorWriteR ) d3dmask |= D3D11_COLOR_WRITE_ENABLE_RED;
+ if( mask & kColorWriteG ) d3dmask |= D3D11_COLOR_WRITE_ENABLE_GREEN;
+ if( mask & kColorWriteB ) d3dmask |= D3D11_COLOR_WRITE_ENABLE_BLUE;
+ if( mask & kColorWriteA ) d3dmask |= D3D11_COLOR_WRITE_ENABLE_ALPHA;
+ dst.RenderTargetWriteMask = d3dmask;
+ // GetD3D11_1Device cannot return null if we're running on DX11 and have gGraphicsCaps.hasBlendLogicOps, so no need to check.
+
+ ID3D11BlendState1 *blendObj = NULL;
+ HRESULT hr = GetD3D11_1Device()->CreateBlendState1 (&desc, &blendObj);
+ d3dstate.deviceState = blendObj;
+ AssertIf(FAILED(hr));
+ SetDebugNameD3D11 (d3dstate.deviceState, Format("BlendState-%d-%d", dst.SrcBlend, dst.DestBlend));
+
+ }
+ else
+ {
+ D3D11_BLEND_DESC desc;
+ memset (&desc, 0, sizeof(desc));
+ if (gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0)
+ desc.AlphaToCoverageEnable = state.alphaToMask;
+ desc.IndependentBlendEnable = FALSE;
+
+ D3D11_RENDER_TARGET_BLEND_DESC& dst = desc.RenderTarget[0];
+ dst.BlendEnable = state.srcBlend != kBlendOne || state.dstBlend != kBlendZero || state.srcBlendAlpha != kBlendOne || state.dstBlendAlpha != kBlendZero;
+ dst.SrcBlend = kBlendModeD3D11[state.srcBlend];
+ dst.DestBlend = kBlendModeD3D11[state.dstBlend];
+ dst.BlendOp = kBlendOpD3D11[state.blendOp];
+ dst.SrcBlendAlpha = kBlendModeAlphaD3D11[state.srcBlendAlpha];
+ dst.DestBlendAlpha = kBlendModeAlphaD3D11[state.dstBlendAlpha];
+ dst.BlendOpAlpha = kBlendOpD3D11[state.blendOpAlpha];
+
+ DWORD d3dmask = 0;
+ const UInt8 mask = state.renderTargetWriteMask;
+ if( mask & kColorWriteR ) d3dmask |= D3D11_COLOR_WRITE_ENABLE_RED;
+ if( mask & kColorWriteG ) d3dmask |= D3D11_COLOR_WRITE_ENABLE_GREEN;
+ if( mask & kColorWriteB ) d3dmask |= D3D11_COLOR_WRITE_ENABLE_BLUE;
+ if( mask & kColorWriteA ) d3dmask |= D3D11_COLOR_WRITE_ENABLE_ALPHA;
+ dst.RenderTargetWriteMask = d3dmask;
+ HRESULT hr = GetD3D11Device()->CreateBlendState (&desc, &d3dstate.deviceState);
+ AssertIf(FAILED(hr));
+ SetDebugNameD3D11 (d3dstate.deviceState, Format("BlendState-%d-%d", dst.SrcBlend, dst.DestBlend));
+
+ }
+
+
+ return &result.first->second;
+}
+
+
+DeviceDepthState* GfxDeviceD3D11::CreateDepthState(const GfxDepthState& state)
+{
+ std::pair<CachedDepthStates::iterator, bool> result = m_CachedDepthStates.insert(std::make_pair(state, DeviceDepthState()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceDepthState& st = result.first->second;
+ memcpy(&st.sourceState, &state, sizeof(st.sourceState));
+ return &result.first->second;
+}
+
+DeviceStencilState* GfxDeviceD3D11::CreateStencilState(const GfxStencilState& state)
+{
+ std::pair<CachedStencilStates::iterator, bool> result = m_CachedStencilStates.insert(std::make_pair(state, DeviceStencilState()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceStencilState& st = result.first->second;
+ memcpy(&st.sourceState, &state, sizeof(state));
+ return &result.first->second;
+}
+
+
+DeviceRasterState* GfxDeviceD3D11::CreateRasterState(const GfxRasterState& state)
+{
+ std::pair<CachedRasterStates::iterator, bool> result = m_CachedRasterStates.insert(std::make_pair(state, DeviceRasterState()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceRasterState& st = result.first->second;
+ memcpy(&st.sourceState, &state, sizeof(state));
+ return &result.first->second;
+}
+
+
+void GfxDeviceD3D11::SetBlendState(const DeviceBlendState* state, float alphaRef)
+{
+ if (state != m_CurrBlendState)
+ {
+ m_CurrBlendState = state;
+ DeviceBlendStateD3D11* devstate = (DeviceBlendStateD3D11*)state;
+ GetD3D11Context()->OMSetBlendState (devstate->deviceState, NULL, 0xFFFFFFFF);
+ }
+
+ // alpha test
+ m_FFState.alphaTest = state->sourceState.alphaTest;
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFAlphaTestRef, Vector4f(alphaRef, alphaRef, alphaRef, alphaRef));
+}
+
+
+void GfxDeviceD3D11::SetRasterState(const DeviceRasterState* state)
+{
+ if (m_CurrRasterState != state)
+ {
+ m_CurrRasterState = state;
+ m_CurrRSState = NULL;
+ }
+}
+
+
+void GfxDeviceD3D11::SetDepthState (const DeviceDepthState* state)
+{
+ if (m_CurrDepthState != state)
+ {
+ m_CurrDepthState = state;
+ m_CurrDSState = NULL;
+ }
+}
+
+void GfxDeviceD3D11::SetStencilState(const DeviceStencilState* state, int stencilRef)
+{
+ if (m_CurrStencilState != state)
+ {
+ m_CurrStencilState = state;
+ m_CurrDSState = NULL;
+ }
+ m_StencilRef = stencilRef;
+}
+
+void GfxDeviceD3D11::SetupDeferredSRGBWrite()
+{
+ if (m_SRGBWrite == m_ActualSRGBWrite)
+ return;
+
+ // sRGB write is not just a render state on DX11; we need to actually rebind the render target views with
+ // a different format. Looks like some drivers do not optimize useless RT changes away, causing
+ // a lot of performance being wasted. So only apply sRGB write change when actually needed.
+ m_ActualSRGBWrite = m_SRGBWrite;
+ RebindActiveRenderTargets (&m_Textures);
+}
+
+void GfxDeviceD3D11::SetSRGBWrite (bool enable)
+{
+ m_SRGBWrite = enable;
+}
+
+bool GfxDeviceD3D11::GetSRGBWrite ()
+{
+ return m_SRGBWrite;
+}
+
+void GfxDeviceD3D11::DiscardContents (RenderSurfaceHandle& rs)
+{
+# if UNITY_WINRT // WSA/WP8 guaranteed to have DX11.1 runtime, needed for DiscardResource
+
+ if(!rs.IsValid())
+ return;
+
+ RenderSurfaceD3D11 *surf = reinterpret_cast<RenderSurfaceD3D11*>( rs.object );
+ if (surf->m_Texture)
+ {
+ ID3D11DeviceContext1 * ctx = (ID3D11DeviceContext1 *)GetD3D11Context();
+ DX11_CHK(ctx->DiscardResource(surf->m_Texture));
+ }
+
+# endif
+}
+
+GfxDevice* CreateD3D11GfxDevice()
+{
+ if( !InitializeD3D11() )
+ return NULL;
+
+ gGraphicsCaps.InitD3D11();
+
+ GfxDeviceD3D11* device = UNITY_NEW_AS_ROOT(GfxDeviceD3D11(), kMemGfxDevice, "D3D11GfxDevice", "");
+ return device;
+}
+
+
+GfxDeviceD3D11::GfxDeviceD3D11()
+{
+ m_DynamicVBO = NULL;
+ InvalidateState();
+ ResetFrameStats();
+
+ m_Renderer = kGfxRendererD3D11;
+ m_UsesOpenGLTextureCoords = false;
+ m_UsesHalfTexelOffset = false;
+ m_IsThreadable = true;
+
+ m_MaxBufferedFrames = -1; // no limiting
+
+ m_Viewport[0] = m_Viewport[1] = m_Viewport[2] = m_Viewport[3] = 0;
+ m_ScissorRect[0] = m_ScissorRect[1] = m_ScissorRect[2] = m_ScissorRect[3] = 0;
+ m_CurrTargetWidth = 0;
+ m_CurrTargetHeight = 0;
+ m_CurrWindowWidth = 0;
+ m_CurrWindowHeight = 0;
+
+ m_InvertProjMatrix = false;
+ m_AppBackfaceMode = false;
+ m_UserBackfaceMode = false;
+ m_Wireframe = false;
+ m_Scissor = false;
+ m_SRGBWrite = false;
+ m_ActualSRGBWrite = false;
+
+ m_FramebufferDepthFormat = kDepthFormat24;
+
+ // constant buffer for fog params
+ m_CBs.SetCBInfo (kSLPropFogCB.index, k11FogSize*16);
+
+ extern RenderSurfaceBase* DummyColorBackBuferD3D11();
+ SetBackBufferColorSurface(DummyColorBackBuferD3D11());
+
+ extern RenderSurfaceBase* DummyDepthBackBuferD3D11();
+ SetBackBufferDepthSurface(DummyDepthBackBuferD3D11());
+}
+
+GfxDeviceD3D11::~GfxDeviceD3D11()
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS_WORKER
+ PluginsSetGraphicsDevice (GetD3D11Device(), kGfxRendererD3D11, kGfxDeviceEventShutdown);
+#endif
+
+ StreamOutSkinningInfo::CleanUp();
+
+#if ENABLE_PROFILER
+ g_TimerQueriesD3D11.ReleaseAllQueries();
+#endif
+
+ D3D11VBO::CleanupSharedBuffers();
+ if( m_DynamicVBO )
+ delete m_DynamicVBO;
+ for (FFProgramCacheD3D11::iterator it = m_FFPrograms.begin(); it != m_FFPrograms.end(); ++it)
+ delete it->second;
+ for (CachedBlendStates::iterator it = m_CachedBlendStates.begin(); it != m_CachedBlendStates.end(); ++it)
+ it->second.deviceState->Release();
+ for (CachedDepthStencilStates::iterator it = m_CachedDepthStencilStates.begin(); it != m_CachedDepthStencilStates.end(); ++it)
+ it->second->Release();
+ for (CachedFinalRasterStates::iterator it = m_CachedFinalRasterStates.begin(); it != m_CachedFinalRasterStates.end(); ++it)
+ it->second->Release();
+ m_Imm.Cleanup();
+ m_VertexDecls.Clear();
+ m_CBs.Clear();
+ m_Textures.ClearTextureResources();
+ m_Resolves.Clear();
+ DestroyD3D11Device();
+ CleanupD3D11();
+}
+
+
+void GfxDeviceD3D11::InvalidateState()
+{
+ g_ActiveInputLayoutD3D11 = NULL;
+ g_CurrentVSInputD3D11 = NULL;
+ g_ActiveTopologyD3D11 = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
+
+ m_TransformState.Invalidate(m_BuiltinParamValues);
+
+ m_FogParams.Invalidate();
+
+ //m_State.Invalidate(*this);
+ m_Imm.Invalidate();
+ //m_VSConstantCache.Invalidate();
+ //m_PSConstantCache.Invalidate();
+
+ memset (&m_FFState, 0, sizeof(m_FFState));
+ m_FFState.useUniformInsteadOfVertexColor = true;
+
+ m_CurrBlendState = NULL;
+ m_CurrRasterState = NULL;
+ m_CurrDepthState = NULL;
+ m_CurrStencilState = NULL;
+ m_CurrRSState = NULL;
+ m_CurrDSState = NULL;
+ m_CurrStencilRef = -1;
+
+ for (int pt = 0; pt < kShaderTypeCount; ++pt)
+ {
+ m_ActiveGpuProgram[pt] = NULL;
+ m_ActiveGpuProgramParams[pt] = NULL;
+ m_ActiveShaders[pt] = NULL;
+ for (int i = 0; i < kMaxSupportedTextureUnits; ++i)
+ {
+ m_ActiveTextures[pt][i].m_ID = -1;
+ m_ActiveSamplers[pt][i].m_ID = -1;
+ }
+ }
+
+ m_Textures.InvalidateSamplers();
+ m_CBs.InvalidateState();
+
+ ID3D11DeviceContext* ctx = GetD3D11Context(true);
+ if (ctx)
+ {
+ D3D11_CALL(ctx->VSSetShader (NULL, NULL, 0));
+ D3D11_CALL(ctx->PSSetShader (NULL, NULL, 0));
+ D3D11_CALL(ctx->GSSetShader (NULL, NULL, 0));
+ D3D11_CALL(ctx->HSSetShader (NULL, NULL, 0));
+ D3D11_CALL(ctx->DSSetShader (NULL, NULL, 0));
+ }
+}
+
+
+void GfxDeviceD3D11::Clear (UInt32 clearFlags, const float color[4], float depth, int stencil)
+{
+ DX11_LOG_ENTER_FUNCTION("Clear(%d, (%.2f, %.2f, %.2f, %.2f), %.2f, %d)", clearFlags, color[0], color[1], color[2], color[3], depth, stencil);
+ SetupDeferredSRGBWrite ();
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ if ((clearFlags & kGfxClearColor) && g_D3D11CurrRT)
+ DX11_CHK(ctx->ClearRenderTargetView (g_D3D11CurrRT, color));
+ if ((clearFlags & kGfxClearDepthStencil) && g_D3D11CurrDS)
+ {
+ UINT flags = 0;
+ if (clearFlags & kGfxClearDepth)
+ flags |= D3D11_CLEAR_DEPTH;
+ if (clearFlags & kGfxClearStencil)
+ flags |= D3D11_CLEAR_STENCIL;
+ DX11_CHK(ctx->ClearDepthStencilView (g_D3D11CurrDS, flags, depth, stencil));
+ }
+}
+
+void GfxDeviceD3D11::SetUserBackfaceMode( bool enable )
+{
+ if (m_UserBackfaceMode != enable)
+ {
+ m_UserBackfaceMode = enable;
+ m_CurrRSState = NULL;
+ }
+}
+
+void GfxDeviceD3D11::SetWireframe (bool wire)
+{
+ if (m_Wireframe != wire)
+ {
+ m_Wireframe = wire;
+ m_CurrRSState = NULL;
+ }
+}
+
+bool GfxDeviceD3D11::GetWireframe() const
+{
+ return m_Wireframe;
+}
+
+
+
+void GfxDeviceD3D11::SetInvertProjectionMatrix( bool enable )
+{
+ if (m_InvertProjMatrix == enable)
+ return;
+
+ m_InvertProjMatrix = enable;
+
+ // When setting up "invert" flag, invert the matrix as well.
+ Matrix4x4f& m = m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatProj);
+ m.Get(1,1) = -m.Get(1,1);
+ m.Get(1,3) = -m.Get(1,3);
+ m_TransformState.dirtyFlags |= TransformState::kProjDirty;
+
+ m_CurrRSState = NULL;
+}
+
+bool GfxDeviceD3D11::GetInvertProjectionMatrix() const
+{
+ return m_InvertProjMatrix;
+}
+
+void GfxDeviceD3D11::SetWorldMatrix( const float matrix[16] )
+{
+ CopyMatrix( matrix, m_TransformState.worldMatrix.GetPtr() );
+ m_TransformState.dirtyFlags |= TransformState::kWorldDirty;
+}
+
+void GfxDeviceD3D11::SetViewMatrix( const float matrix[16] )
+{
+ m_TransformState.SetViewMatrix (matrix, m_BuiltinParamValues);
+}
+
+void GfxDeviceD3D11::SetProjectionMatrix (const Matrix4x4f& matrix)
+{
+ Matrix4x4f& m = m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatProj);
+ CopyMatrix (matrix.GetPtr(), m.GetPtr());
+ CopyMatrix (matrix.GetPtr(), m_TransformState.projectionMatrixOriginal.GetPtr());
+ CalculateDeviceProjectionMatrix (m, m_UsesOpenGLTextureCoords, m_InvertProjMatrix);
+ m_TransformState.dirtyFlags |= TransformState::kProjDirty;
+}
+
+void GfxDeviceD3D11::GetMatrix( float outMatrix[16] ) const
+{
+ m_TransformState.UpdateWorldViewMatrix (m_BuiltinParamValues);
+ CopyMatrix (m_TransformState.worldViewMatrix.GetPtr(), outMatrix);
+}
+
+const float* GfxDeviceD3D11::GetWorldMatrix() const
+{
+ return m_TransformState.worldMatrix.GetPtr();
+}
+
+const float* GfxDeviceD3D11::GetViewMatrix() const
+{
+ return m_BuiltinParamValues.GetMatrixParam(kShaderMatView).GetPtr();
+}
+
+const float* GfxDeviceD3D11::GetProjectionMatrix() const
+{
+ return m_TransformState.projectionMatrixOriginal.GetPtr();
+}
+
+const float* GfxDeviceD3D11::GetDeviceProjectionMatrix() const
+{
+ return m_BuiltinParamValues.GetMatrixParam(kShaderMatProj).GetPtr();
+}
+
+
+void GfxDeviceD3D11::SetNormalizationBackface( NormalizationMode mode, bool backface )
+{
+ if (m_AppBackfaceMode != backface)
+ {
+ m_AppBackfaceMode = backface;
+ m_CurrRSState = NULL;
+ }
+}
+
+void GfxDeviceD3D11::SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial )
+{
+ DX11_LOG_ENTER_FUNCTION("SetFFLighting(%s, %s, %d)", GetDX11BoolString(on), GetDX11BoolString(separateSpecular), colorMaterial);
+ DebugAssert(colorMaterial!=kColorMatUnknown);
+ m_FFState.lightingEnabled = on;
+ m_FFState.specularEnabled = on && separateSpecular;
+ m_FFState.colorMaterial = colorMaterial;
+}
+
+void GfxDeviceD3D11::SetMaterial( const float ambient[4], const float diffuse[4], const float specular[4], const float emissive[4], const float shininess )
+{
+ DX11_LOG_ENTER_FUNCTION("SetMaterial((%.2f, %.2f, %.2f, %.2f), (%.2f, %.2f, %.2f, %.2f), (%.2f, %.2f, %.2f, %.2f), (%.2f, %.2f, %.2f, %.2f), %.2f)",
+ ambient[0], ambient[1], ambient[2], ambient[3],
+ diffuse[0], diffuse[1], diffuse[2], diffuse[3],
+ specular[0], specular[1], specular[2], specular[3],
+ emissive[0], emissive[1], emissive[2], emissive[3],
+ shininess);
+
+ float glshine = clamp01 (shininess) * 128.0f;
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatAmbient, Vector4f(ambient[0], ambient[1], ambient[2], 1.0F));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatDiffuse, Vector4f(diffuse));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatSpecular, Vector4f(specular[0], specular[1], specular[2], glshine));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatEmission, Vector4f(emissive[0], emissive[1], emissive[2], 1.0F));
+}
+
+
+void GfxDeviceD3D11::SetColor( const float color[4] )
+{
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFColor, Vector4f(color));
+}
+
+
+
+void GfxDeviceD3D11::SetViewport( int x, int y, int width, int height )
+{
+ DX11_LOG_ENTER_FUNCTION("SetViewport(%d, %d, %d, %d)", x, y, width, height);
+ m_Viewport[0] = x;
+ m_Viewport[1] = y;
+ m_Viewport[2] = width;
+ m_Viewport[3] = height;
+
+ D3D11_VIEWPORT view;
+ view.TopLeftX = x;
+ view.TopLeftY = y;
+ view.Width = width;
+ view.Height = height;
+ view.MinDepth = 0.0f;
+ view.MaxDepth = 1.0f;
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ //@TODO
+ //if( !dev ) // happens on startup, when deleting all render textures
+ // return;
+ ctx->RSSetViewports (1, &view);
+}
+
+void GfxDeviceD3D11::GetViewport( int* port ) const
+{
+ port[0] = m_Viewport[0];
+ port[1] = m_Viewport[1];
+ port[2] = m_Viewport[2];
+ port[3] = m_Viewport[3];
+}
+
+
+void GfxDeviceD3D11::SetScissorRect (int x, int y, int width, int height)
+{
+ DX11_LOG_ENTER_FUNCTION("SetScissorRect(%d, %d, %d, %d)", x, y, width, height);
+ if (!m_Scissor)
+ {
+ m_Scissor = true;
+ m_CurrRSState = NULL;
+ }
+
+ m_ScissorRect[0] = x;
+ m_ScissorRect[1] = y;
+ m_ScissorRect[2] = width;
+ m_ScissorRect[3] = height;
+
+ D3D11_RECT rc;
+ rc.left = x;
+ rc.top = y;
+ rc.right = x + width;
+ rc.bottom = y + height;
+ GetD3D11Context()->RSSetScissorRects (1, &rc);
+}
+
+void GfxDeviceD3D11::DisableScissor()
+{
+ if (m_Scissor)
+ {
+ m_Scissor = false;
+ m_CurrRSState = NULL;
+ }
+}
+
+bool GfxDeviceD3D11::IsScissorEnabled() const
+{
+ return m_Scissor;
+}
+
+void GfxDeviceD3D11::GetScissorRect (int scissor[4]) const
+{
+ scissor[0] = m_ScissorRect[0];
+ scissor[1] = m_ScissorRect[1];
+ scissor[2] = m_ScissorRect[2];
+ scissor[3] = m_ScissorRect[3];
+}
+
+
+struct TextureCombiners11
+{
+ const ShaderLab::TextureBinding* texEnvs;
+ int count;
+};
+
+bool GfxDeviceD3D11::IsCombineModeSupported( unsigned int combiner )
+{
+ return true;
+}
+
+TextureCombinersHandle GfxDeviceD3D11::CreateTextureCombiners (int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular)
+{
+ DX11_LOG_ENTER_FUNCTION("CreateTextureCombiners()");
+ if (count > gGraphicsCaps.maxTexUnits)
+ return TextureCombinersHandle(NULL);
+
+ TextureCombiners11* combiners = new TextureCombiners11();
+ combiners->texEnvs = texEnvs;
+ combiners->count = count;
+ return TextureCombinersHandle(combiners);
+}
+
+void GfxDeviceD3D11::DeleteTextureCombiners (TextureCombinersHandle& textureCombiners)
+{
+ DX11_LOG_ENTER_FUNCTION("DeleteTextureCombiners()");
+ TextureCombiners11* combiners = OBJECT_FROM_HANDLE(textureCombiners,TextureCombiners11);
+ delete combiners;
+ textureCombiners.Reset();
+}
+
+void GfxDeviceD3D11::SetTextureCombinersThreadable( TextureCombinersHandle textureCombiners, const TexEnvData* texEnvData, const Vector4f* texColors )
+{
+ DX11_LOG_ENTER_FUNCTION("SetTextureCombinersThreadable()");
+ TextureCombiners11* combiners = OBJECT_FROM_HANDLE(textureCombiners,TextureCombiners11);
+ Assert (combiners);
+
+ const int count = std::min(combiners->count, gGraphicsCaps.maxTexUnits);
+ m_FFState.texUnitCount = count;
+ for (int i = 0; i < count; ++i)
+ {
+ const ShaderLab::TextureBinding& binding = combiners->texEnvs[i];
+ ApplyTexEnvData (i, i, texEnvData[i]);
+ m_BuiltinParamValues.SetVectorParam ((BuiltinShaderVectorParam)(kShaderVecFFTextureEnvColor0 + i), texColors[i]);
+ m_FFState.texUnitColorCombiner[i] = binding.m_CombColor;
+ m_FFState.texUnitAlphaCombiner[i] = binding.m_CombAlpha;
+ }
+
+ // unused textures
+ UInt32 mask = (1<<count)-1;
+ m_FFState.texUnitCube &= mask;
+ m_FFState.texUnit3D &= mask;
+ m_FFState.texUnitProjected &= mask;
+}
+
+void GfxDeviceD3D11::SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props )
+{
+ DX11_LOG_ENTER_FUNCTION("SetTextureCombiners()");
+ TextureCombiners11* combiners = OBJECT_FROM_HANDLE(textureCombiners,TextureCombiners11);
+ Assert(combiners);
+
+ const int count = std::min(combiners->count, gGraphicsCaps.maxTexUnits);
+
+ // Fill in arrays
+ TexEnvData* texEnvData;
+ ALLOC_TEMP (texEnvData, TexEnvData, count);
+ for (int i = 0; i < count; ++i)
+ {
+ const ShaderLab::TextureBinding& binding = combiners->texEnvs[i];
+ ShaderLab::TexEnv *te = ShaderLab::GetTexEnvForBinding(binding, props);
+ Assert(te != NULL);
+ te->PrepareData (binding.m_TextureName.index, binding.m_MatrixName, props, &texEnvData[i]);
+ }
+
+ Vector4f* texColors;
+ ALLOC_TEMP (texColors, Vector4f, count);
+ for (int i = 0; i < count; ++i)
+ {
+ const ShaderLab::TextureBinding& binding = combiners->texEnvs[i];
+ texColors[i] = binding.GetTexColor().Get (props);
+ }
+ GfxDeviceD3D11::SetTextureCombinersThreadable (textureCombiners, texEnvData, texColors);
+}
+
+
+
+void UnbindTextureD3D11 (TextureID texture)
+{
+ DX11_LOG_ENTER_FUNCTION("UnbindTextureD3D11(%d)", texture.m_ID);
+ GfxDeviceD3D11& device = static_cast<GfxDeviceD3D11&>(GetRealGfxDevice());
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ for (int pt = kShaderVertex; pt < kShaderTypeCount; ++pt)
+ {
+ for (int i = 0; i < kMaxSupportedTextureUnits; ++i)
+ {
+ if (device.m_ActiveTextures[pt][i]==texture)
+ {
+ ID3D11ShaderResourceView* srv = NULL;
+ switch (pt) {
+ case kShaderVertex: ctx->VSSetShaderResources (i, 1, &srv); break;
+ case kShaderFragment: ctx->PSSetShaderResources (i, 1, &srv); break;
+ case kShaderGeometry: ctx->GSSetShaderResources (i, 1, &srv); break;
+ case kShaderHull: ctx->HSSetShaderResources (i, 1, &srv); break;
+ case kShaderDomain: ctx->DSSetShaderResources (i, 1, &srv); break;
+ default: AssertString("unknown shader type");
+ }
+ device.m_ActiveTextures[pt][i].m_ID = -1;
+ }
+ if (device.m_ActiveSamplers[pt][i]==texture)
+ {
+ device.m_ActiveSamplers[pt][i].m_ID = -1;
+ }
+ }
+ }
+}
+
+
+
+void GfxDeviceD3D11::SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias)
+{
+ DebugAssertIf (dim < kTexDim2D || dim > kTexDimCUBE);
+ DebugAssertIf (unit < 0 || unit >= kMaxSupportedTextureUnits);
+
+ // WP8 seems to have a driver bug (?) with occasionally losing texture state; can't do redundant bind early out here.
+ // Repros on Shadowgun flyby on Nokia Not For Sale.
+ #if !UNITY_WP8
+ if (m_ActiveTextures[shaderType][unit] == texture && (samplerUnit >= 0 && m_ActiveSamplers[shaderType][samplerUnit] == texture))
+ return;
+ #endif
+
+ if (m_Textures.SetTexture (shaderType, unit, samplerUnit, texture, bias))
+ {
+ m_Stats.AddUsedTexture(texture);
+ m_ActiveTextures[shaderType][unit] = texture;
+ if (samplerUnit >= 0)
+ m_ActiveSamplers[shaderType][samplerUnit] = texture;
+ }
+
+ if (shaderType == kShaderFragment && unit < kMaxSupportedTextureCoords)
+ {
+ if (m_FFState.texUnitCount <= unit)
+ m_FFState.texUnitCount = unit+1;
+
+ UInt32 mask = 1<<unit;
+ if (dim==kTexDimCUBE)
+ m_FFState.texUnitCube |= mask;
+ else
+ m_FFState.texUnitCube &= ~mask;
+ if (dim==kTexDim3D)
+ m_FFState.texUnit3D |= mask;
+ else
+ m_FFState.texUnit3D &= ~mask;
+ }
+}
+
+
+void GfxDeviceD3D11::SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16])
+{
+ Assert (unit >= 0 && unit < kMaxSupportedTextureCoords);
+
+ TextureSourceD3D11 texSource = texGen == kTexGenDisabled ? kTexSourceUV0 : static_cast<TextureSourceD3D11>(texGen + kTexSourceUV7);
+ m_FFState.texUnitSources = m_FFState.texUnitSources & ~(15<<(unit*4)) | (UInt64(texSource)<<(unit*4));
+
+ if (identity)
+ m_TextureUnits[unit].matrix.SetIdentity();
+ else
+ CopyMatrix (matrix, m_TextureUnits[unit].matrix.GetPtr());
+
+ // Detect if we have a projective texture matrix
+ m_FFState.texUnitProjected &= ~(1<<unit);
+ if (!identity && dim==kTexDim2D)
+ {
+ if (matrix[3] != 0.0f || matrix[7] != 0.0f || matrix[11] != 0.0f || matrix[15] != 1.0f)
+ m_FFState.texUnitProjected |= (1<<unit);
+ }
+}
+
+void GfxDeviceD3D11::SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace )
+{
+ UnbindTextureD3D11 (texture);
+ m_Textures.SetTextureParams (texture, texDim, filter, wrap, anisoLevel, hasMipMap, colorSpace);
+}
+
+
+
+void GfxDeviceD3D11::SetShadersThreadable (GpuProgram* programs[kShaderTypeCount], const GpuProgramParameters* params[kShaderTypeCount], UInt8 const * const paramsBuffer[kShaderTypeCount])
+{
+ for (int pt = kShaderVertex; pt < kShaderTypeCount; ++pt)
+ {
+ m_ActiveGpuProgram[pt] = programs[pt];
+ m_ActiveGpuProgramParams[pt] = params[pt];
+ }
+
+ // Apply programmable shader parameters
+ if (m_ActiveGpuProgram[kShaderVertex] && m_ActiveGpuProgram[kShaderFragment])
+ {
+ for (int pt = kShaderVertex; pt < kShaderTypeCount; ++pt)
+ {
+ if (m_ActiveGpuProgram[pt])
+ {
+ DebugAssert (!m_ActiveGpuProgram[pt] || m_ActiveGpuProgram[pt]->GetImplType() == pt);
+ D3D11CommonShader* prog = static_cast<D3D11CommonShader*>(m_ActiveGpuProgram[pt]);
+ m_ActiveGpuProgram[pt]->ApplyGpuProgram (*params[pt], paramsBuffer[pt]);
+ }
+ }
+ }
+}
+
+
+bool GfxDeviceD3D11::IsShaderActive( ShaderType type ) const
+{
+ return (m_ActiveGpuProgram[type] != 0);
+}
+
+void GfxDeviceD3D11::DestroySubProgram( ShaderLab::SubProgram* subprogram )
+{
+ delete subprogram;
+}
+
+void GfxDeviceD3D11::SetConstantBufferInfo (int id, int size)
+{
+ m_CBs.SetCBInfo (id, size);
+}
+
+void GfxDeviceD3D11::DisableLights( int startLight )
+{
+ startLight = std::min (startLight, gGraphicsCaps.maxLights);
+ m_FFState.lightCount = startLight;
+
+ const Vector4f black(0.0F, 0.0F, 0.0F, 0.0F);
+ const Vector4f zpos(0.0F, 0.0F, 1.0F, 0.0F);
+ for (int i = startLight; i < gGraphicsCaps.maxLights; ++i)
+ {
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Position + i), zpos);
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Diffuse + i), black);
+ }
+}
+
+void GfxDeviceD3D11::SetLight( int light, const GfxVertexLight& data)
+{
+ if (light >= gGraphicsCaps.maxLights)
+ return;
+ SetupVertexLightParams (light, data);
+}
+
+void GfxDeviceD3D11::SetAmbient( const float ambient[4] )
+{
+ m_BuiltinParamValues.SetVectorParam(kShaderVecLightModelAmbient, Vector4f(ambient));
+}
+
+
+void GfxDeviceD3D11::EnableFog (const GfxFogParams& fog)
+{
+ DebugAssert (fog.mode > kFogDisabled);
+ m_FogParams = fog;
+
+ //@TODO: fog DXBC patching not implemented for 9.x level; and something still wrong with FF shaders in 9.x level as well
+ // (e.g. crashes WARP). Just disable fog for now.
+ if (gGraphicsCaps.d3d11.featureLevel < kDX11Level10_0)
+ m_FogParams.mode = kFogDisabled;
+}
+
+void GfxDeviceD3D11::DisableFog()
+{
+ m_FogParams.mode = kFogDisabled;
+ m_FogParams.density = 0.0f;
+}
+
+
+VBO* GfxDeviceD3D11::CreateVBO()
+{
+ VBO* vbo = new D3D11VBO();
+ OnCreateVBO(vbo);
+ return vbo;
+}
+
+void GfxDeviceD3D11::DeleteVBO( VBO* vbo )
+{
+ OnDeleteVBO(vbo);
+ delete vbo;
+}
+
+DynamicVBO& GfxDeviceD3D11::GetDynamicVBO()
+{
+ if( !m_DynamicVBO ) {
+ m_DynamicVBO = new DynamicD3D11VBO( 1024 * 1024, 65536 ); // initial 1 MiB VB, 64 KiB IB
+ }
+ return *m_DynamicVBO;
+}
+
+/*
+void GfxDeviceD3D11::ResetDynamicResources()
+{
+ delete m_DynamicVBO;
+ m_DynamicVBO = NULL;
+
+ CleanupEventQueries ();
+
+ for( ListIterator<D3D11VBO*> i = m_DynamicVBOs.begin(); i != m_DynamicVBOs.end(); ++i )
+ {
+ D3D11VBO* vbo = *i;
+ vbo->ResetDynamicVB();
+ }
+}
+*/
+
+GfxDeviceD3D11& GetD3D11GfxDevice()
+{
+ GfxDevice& device = GetRealGfxDevice();
+ DebugAssert(device.GetRenderer() == kGfxRendererD3D11);
+ return static_cast<GfxDeviceD3D11&>(device);
+}
+
+
+ID3D11InputLayout* GetD3D11VertexDeclaration (const ChannelInfoArray& channels)
+{
+ GfxDevice& device = GetRealGfxDevice();
+ DebugAssert(device.GetRenderer() == kGfxRendererD3D11);
+ GfxDeviceD3D11* deviceD3D = static_cast<GfxDeviceD3D11*>( &device );
+ return deviceD3D->GetVertexDecls().GetVertexDecl (channels, g_CurrentVSInputD3D11);
+}
+
+const InputSignatureD3D11* GetD3D11InputSignature (void* code, unsigned length)
+{
+ GfxDevice& device = GetRealGfxDevice();
+ DebugAssert(device.GetRenderer() == kGfxRendererD3D11);
+ GfxDeviceD3D11* deviceD3D = static_cast<GfxDeviceD3D11*>( &device );
+ return deviceD3D->GetVertexDecls().GetShaderInputSignature (code, length);
+}
+
+ConstantBuffersD3D11& GetD3D11ConstantBuffers (GfxDevice& device)
+{
+ Assert (device.GetRenderer() == kGfxRendererD3D11);
+ GfxDeviceD3D11& deviceD3D = static_cast<GfxDeviceD3D11&>(device);
+ return deviceD3D.GetConstantBuffers();
+}
+
+TexturesD3D11& GetD3D11Textures (GfxDevice& device)
+{
+ Assert (device.GetRenderer() == kGfxRendererD3D11);
+ GfxDeviceD3D11& deviceD3D = static_cast<GfxDeviceD3D11&>(device);
+ return deviceD3D.GetTextures();
+}
+
+
+// ---------- render textures
+
+
+RenderSurfaceHandle CreateRenderColorSurfaceD3D11( TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, UInt32 createFlags, RenderTextureFormat format, TexturesD3D11& textures);
+RenderSurfaceHandle CreateRenderDepthSurfaceD3D11( TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags, TexturesD3D11& textures);
+void DestroyRenderSurfaceD3D11 (RenderSurfaceHandle& rsHandle, TexturesD3D11& textures);
+bool SetRenderTargetD3D11 (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face, int* outTargetWidth, int* outTargetHeight, TexturesD3D11* textures);
+RenderSurfaceHandle GetActiveRenderColorSurfaceD3D11(int index);
+RenderSurfaceHandle GetActiveRenderDepthSurfaceD3D11();
+RenderSurfaceHandle GetActiveRenderColorSurfaceBBD3D11();
+
+RenderSurfaceHandle GfxDeviceD3D11::CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags)
+{
+ DX11_LOG_ENTER_FUNCTION("CreateRenderColorSurface(%d, %d, %d, %d, %d)", textureID.m_ID, width, height, format, createFlags);
+ return CreateRenderColorSurfaceD3D11 (textureID, width, height, samples, depth, dim, createFlags, format, m_Textures);
+}
+RenderSurfaceHandle GfxDeviceD3D11::CreateRenderDepthSurface (TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags)
+{
+ DX11_LOG_ENTER_FUNCTION("CreateRenderDepthSurface(%d, %d, %d, %d, %d, %d)", textureID.m_ID, width, height, dim, depthFormat, createFlags);
+ return CreateRenderDepthSurfaceD3D11 (textureID, width, height, samples, dim, depthFormat, createFlags, m_Textures);
+}
+void GfxDeviceD3D11::DestroyRenderSurface (RenderSurfaceHandle& rs)
+{
+ DX11_LOG_ENTER_FUNCTION("DestroyRenderSurface()");
+ DestroyRenderSurfaceD3D11 (rs, m_Textures);
+}
+void GfxDeviceD3D11::SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face)
+{
+ DX11_LOG_ENTER_FUNCTION("SetRenderTargets(%i, c0=%p, d=%p, mip=%i, f=%i)", count, colorHandles[0].object, depthHandle.object, mipLevel, face);
+ SetupDeferredSRGBWrite ();
+ m_CurrTargetWidth = m_CurrWindowWidth;
+ m_CurrTargetHeight = m_CurrWindowHeight;
+ if (SetRenderTargetD3D11 (count, colorHandles, depthHandle, mipLevel, face, &m_CurrTargetWidth, &m_CurrTargetHeight, &m_Textures))
+ {
+ // changing render target might mean different color clear flags; so reset current state
+ m_CurrBlendState = NULL;
+ }
+}
+
+void GfxDeviceD3D11::ResolveDepthIntoTexture (RenderSurfaceHandle colorHandle, RenderSurfaceHandle depthHandle)
+{
+ DX11_LOG_ENTER_FUNCTION("ResolveDepthIntoTexture(%p, %p)", colorHandle.object, depthHandle.object);
+ RenderSurfaceD3D11* depthSurf = reinterpret_cast<RenderSurfaceD3D11*>(depthHandle.object);
+ TexturesD3D11::D3D11Texture* destTexture = m_Textures.GetTexture (depthSurf->textureID);
+ DebugAssert (destTexture);
+ if (!destTexture)
+ return;
+ DebugAssert (g_D3D11CurrDepthRT);
+ if (!g_D3D11CurrDepthRT || !g_D3D11CurrDepthRT->m_Texture)
+ return;
+
+ GetD3D11Context()->CopyResource (destTexture->m_Texture, g_D3D11CurrDepthRT->m_Texture);
+}
+
+
+void GfxDeviceD3D11::ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle)
+{
+ Assert (srcHandle.IsValid());
+ Assert (dstHandle.IsValid());
+ RenderColorSurfaceD3D11* src = reinterpret_cast<RenderColorSurfaceD3D11*>(srcHandle.object);
+ RenderColorSurfaceD3D11* dst = reinterpret_cast<RenderColorSurfaceD3D11*>(dstHandle.object);
+ if (!src->colorSurface || !dst->colorSurface)
+ {
+ WarningString("RenderTexture: Resolving non-color surfaces.");
+ return;
+ }
+ if (src->dim != dst->dim)
+ {
+ WarningString("RenderTexture: Resolving surfaces of different types.");
+ return;
+ }
+ if (src->format != dst->format)
+ {
+ WarningString("RenderTexture: Resolving surfaces of different formats.");
+ return;
+ }
+ if (src->width != dst->width || src->height != dst->height)
+ {
+ WarningString("RenderTexture: Resolving surfaces of different sizes.");
+ return;
+ }
+
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ if (src->samples <= 1 && dst->samples <= 1)
+ {
+ ctx->CopyResource (dst->m_Texture, src->m_Texture);
+ }
+ else
+ {
+ extern DXGI_FORMAT kD3D11RenderTextureFormatsNorm[kRTFormatCount];
+ ctx->ResolveSubresource (dst->m_Texture, 0, src->m_Texture, 0, kD3D11RenderTextureFormatsNorm[dst->format]);
+ if ((dst->flags & kSurfaceCreateMipmap) &&
+ (dst->flags & kSurfaceCreateAutoGenMips) &&
+ dst->m_SRViewForMips)
+ {
+ ctx->GenerateMips (dst->m_SRViewForMips);
+ }
+ }
+}
+
+
+RenderSurfaceHandle GfxDeviceD3D11::GetActiveRenderColorSurface(int index)
+{
+ DX11_LOG_ENTER_FUNCTION("GetActiveRenderColorSurface(%d)", index);
+ return GetActiveRenderColorSurfaceD3D11(index);
+}
+RenderSurfaceHandle GfxDeviceD3D11::GetActiveRenderDepthSurface()
+{
+ DX11_LOG_ENTER_FUNCTION("GetActiveRenderDepthSurface");
+ return GetActiveRenderDepthSurfaceD3D11();
+}
+void GfxDeviceD3D11::SetSurfaceFlags (RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags)
+{
+}
+
+
+// ---------- uploading textures
+
+void GfxDeviceD3D11::UploadTexture2D( TextureID texture, TextureDimension dimension, UInt8* srcData, int srcSize, int width, int height, TextureFormat format, int mipCount, UInt32 uploadFlags, int skipMipLevels, TextureUsageMode usageMode, TextureColorSpace colorSpace )
+{
+ DX11_LOG_ENTER_FUNCTION("UploadTexture2D(%d, %d, <srcData>, %d, %d, %d, %d, %d, %d, %d)",
+ texture.m_ID, dimension, width, height, format, mipCount, uploadFlags, skipMipLevels, usageMode);
+ UnbindTextureD3D11 (texture);
+ m_Textures.UploadTexture2D (texture, dimension, srcData, width, height, format, mipCount, uploadFlags, skipMipLevels, usageMode, colorSpace);
+}
+void GfxDeviceD3D11::UploadTextureSubData2D( TextureID texture, UInt8* srcData, int srcSize, int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace )
+{
+ DX11_LOG_ENTER_FUNCTION("UploadTextureSubData2D(%d, <srcData>, ...)", texture.m_ID)
+ m_Textures.UploadTextureSubData2D (texture, srcData, mipLevel, x, y, width, height, format, colorSpace);
+}
+void GfxDeviceD3D11::UploadTextureCube( TextureID texture, UInt8* srcData, int srcSize, int faceDataSize, int size, TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace )
+{
+ DX11_LOG_ENTER_FUNCTION("UploadTextureCube(%d, <srcData>, ...)", texture.m_ID)
+ UnbindTextureD3D11 (texture);
+ m_Textures.UploadTextureCube (texture, srcData, faceDataSize, size, format, mipCount, uploadFlags, colorSpace);
+}
+void GfxDeviceD3D11::UploadTexture3D( TextureID texture, UInt8* srcData, int srcSize, int width, int height, int depth, TextureFormat format, int mipCount, UInt32 uploadFlags )
+{
+ DX11_LOG_ENTER_FUNCTION("UploadTexture3D(%d, <srcData>, ...)", texture.m_ID)
+ UnbindTextureD3D11 (texture);
+ m_Textures.UploadTexture3D (texture, srcData, width, height, depth, format, mipCount, uploadFlags);
+}
+void GfxDeviceD3D11::DeleteTexture( TextureID texture )
+{
+ DX11_LOG_ENTER_FUNCTION("DeleteTexture(%d)", texture.m_ID)
+ UnbindTextureD3D11 (texture);
+ m_Textures.DeleteTexture (texture);
+}
+
+
+// ---------- context
+
+GfxDevice::PresentMode GfxDeviceD3D11::GetPresentMode()
+{
+ return kPresentBeforeUpdate;
+}
+
+void GfxDeviceD3D11::BeginFrame()
+{
+ DX11_LOG_OUTPUT("*****************************************");
+ DX11_LOG_OUTPUT("*****************************************");
+ DX11_LOG_OUTPUT("*****************************************");
+ DX11_LOG_ENTER_FUNCTION("BeginFrame()");
+ DX11_MARK_FRAME_BEGIN();
+ m_InsideFrame = true;
+
+ #if UNITY_WINRT
+ ActivateD3D11BackBuffer(this); // ?!-
+ #endif
+}
+
+
+
+void GfxDeviceD3D11::EndFrame()
+{
+ DX11_LOG_ENTER_FUNCTION("EndFrame()");
+ DX11_MARK_FRAME_END();
+ m_InsideFrame = false;
+}
+
+bool GfxDeviceD3D11::IsValidState()
+{
+ return true;
+}
+
+void GfxDeviceD3D11::PresentFrame()
+{
+ #if !UNITY_WP8
+ IDXGISwapChain* swapChain = GetD3D11SwapChain();
+ if (swapChain)
+ swapChain->Present (GetD3D11SyncInterval(), 0);
+ #endif
+ m_CBs.NewFrame();
+}
+
+void GfxDeviceD3D11::FinishRendering()
+{
+ // not needed on D3D
+}
+
+
+
+// ---------- immediate mode rendering
+
+// we break very large immediate mode submissions into multiple batches internally
+const int kMaxImmediateVerticesPerDraw = 8192;
+
+
+ImmediateModeD3D11::ImmediateModeD3D11()
+: m_VB(NULL)
+, m_VBUsedBytes(0)
+, m_VBStartVertex(0)
+{
+}
+
+ImmediateModeD3D11::~ImmediateModeD3D11()
+{
+ Assert (!m_VB);
+}
+
+void ImmediateModeD3D11::Cleanup()
+{
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(m_VB);
+ SAFE_RELEASE(m_VB);
+ m_VBUsedBytes = 0;
+}
+
+
+void ImmediateModeD3D11::Invalidate()
+{
+ m_Vertices.clear();
+ memset( &m_Current, 0, sizeof(m_Current) );
+ m_HadColor = false;
+}
+
+void GfxDeviceD3D11::ImmediateVertex( float x, float y, float z )
+{
+ // If the current batch is becoming too large, internally end it and begin it again.
+ size_t currentSize = m_Imm.m_Vertices.size();
+ if( currentSize >= kMaxImmediateVerticesPerDraw - 4 )
+ {
+ GfxPrimitiveType mode = m_Imm.m_Mode;
+ // For triangles, break batch when multiple of 3's is reached.
+ if( mode == kPrimitiveTriangles && currentSize % 3 == 0 )
+ {
+ bool hadColor = m_Imm.m_HadColor;
+ ImmediateEnd();
+ ImmediateBegin( mode );
+ m_Imm.m_HadColor = hadColor;
+ }
+ // For other primitives, break on multiple of 4's.
+ // NOTE: This won't quite work for triangle strips, but we'll just pretend
+ // that will never happen.
+ else if( mode != kPrimitiveTriangles && currentSize % 4 == 0 )
+ {
+ bool hadColor = m_Imm.m_HadColor;
+ ImmediateEnd();
+ ImmediateBegin( mode );
+ m_Imm.m_HadColor = hadColor;
+ }
+ }
+ Vector3f& vert = m_Imm.m_Current.vertex;
+ vert.x = x;
+ vert.y = y;
+ vert.z = z;
+ m_Imm.m_Vertices.push_back( m_Imm.m_Current );
+}
+
+void GfxDeviceD3D11::ImmediateNormal( float x, float y, float z )
+{
+ m_Imm.m_Current.normal.x = x;
+ m_Imm.m_Current.normal.y = y;
+ m_Imm.m_Current.normal.z = z;
+}
+
+void GfxDeviceD3D11::ImmediateColor( float r, float g, float b, float a )
+{
+ m_Imm.m_Current.color.Set (ColorRGBAf(r,g,b,a));
+ m_Imm.m_HadColor = true;
+}
+
+void GfxDeviceD3D11::ImmediateTexCoordAll( float x, float y, float z )
+{
+ for( int i = 0; i < 8; ++i )
+ {
+ Vector3f& uv = m_Imm.m_Current.texCoords[i];
+ uv.x = x;
+ uv.y = y;
+ uv.z = z;
+ }
+}
+
+void GfxDeviceD3D11::ImmediateTexCoord( int unit, float x, float y, float z )
+{
+ if( unit < 0 || unit >= 8 )
+ {
+ ErrorString( "Invalid unit for texcoord" );
+ return;
+ }
+ Vector3f& uv = m_Imm.m_Current.texCoords[unit];
+ uv.x = x;
+ uv.y = y;
+ uv.z = z;
+}
+
+void GfxDeviceD3D11::ImmediateBegin( GfxPrimitiveType type )
+{
+ m_Imm.m_Mode = type;
+ m_Imm.m_Vertices.clear();
+ m_Imm.m_HadColor = false;
+}
+
+bool GfxDeviceD3D11::ImmediateEndSetup()
+{
+ if( m_Imm.m_Vertices.empty() )
+ return false;
+
+ HRESULT hr = S_OK;
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ // vertex buffer
+ const int kImmediateVBSize = kMaxImmediateVerticesPerDraw * sizeof(ImmediateVertexD3D11);
+ if (!m_Imm.m_VB)
+ {
+ ID3D11Device* dev = GetD3D11Device();
+ D3D11_BUFFER_DESC desc;
+ desc.ByteWidth = kImmediateVBSize;
+ desc.Usage = D3D11_USAGE_DYNAMIC;
+ desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+ desc.MiscFlags = 0;
+ desc.StructureByteStride = 0;
+ hr = dev->CreateBuffer (&desc, NULL, &m_Imm.m_VB);
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(m_Imm.m_VB,kImmediateVBSize,this);
+ SetDebugNameD3D11 (m_Imm.m_VB, "VertexBufferImmediate");
+ m_Imm.m_VBUsedBytes = 0;
+ m_Imm.m_VBStartVertex = 0;
+ }
+
+ const ImmediateVertexD3D11* vb = &m_Imm.m_Vertices[0];
+ const int vertexCount = m_Imm.m_Vertices.size();
+ const int vertexDataSize = vertexCount * sizeof(vb[0]);
+ D3D11_MAPPED_SUBRESOURCE mapped;
+
+ if (m_Imm.m_VBUsedBytes + vertexDataSize > kImmediateVBSize)
+ {
+ D3D11_CALL_HR(ctx->Map (m_Imm.m_VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped));
+ m_Imm.m_VBUsedBytes = 0;
+ }
+ else
+ {
+ D3D11_CALL_HR(ctx->Map (m_Imm.m_VB, 0, D3D11_MAP_WRITE_NO_OVERWRITE, 0, &mapped));
+ }
+ m_Imm.m_VBStartVertex = m_Imm.m_VBUsedBytes / sizeof(vb[0]);
+
+ memcpy ((UInt8*)mapped.pData + m_Imm.m_VBUsedBytes, vb, vertexDataSize);
+ D3D11_CALL_HR(ctx->Unmap (m_Imm.m_VB, 0));
+ m_Imm.m_VBUsedBytes += vertexDataSize;
+
+ UINT strides = sizeof(vb[0]);
+ UINT offsets = 0;
+ D3D11_CALL (ctx->IASetVertexBuffers(0, 1, &m_Imm.m_VB, &strides, &offsets));
+
+
+ m_FFState.useUniformInsteadOfVertexColor = !m_Imm.m_HadColor;
+ if (!IsShaderActive(kShaderVertex))
+ {
+ UInt64 textureSources = m_FFState.texUnitSources;
+ for (int i = 0; i < gGraphicsCaps.maxTexCoords; ++i)
+ {
+ UInt32 source = (textureSources >> (i*4)) & 0xF;
+ // In immediate mode, each texcoord binds to it's own stage, unless we have texgen
+ // on some of them.
+ if (source <= kTexSourceUV7)
+ {
+ textureSources = textureSources & ~(0xFUL<<i*4) | (UInt64(kTexSourceUV0+i) << i*4);
+ }
+ }
+ m_FFState.texUnitSources = textureSources;
+ }
+
+ BeforeDrawCall (true);
+ return true;
+}
+
+
+void GfxDeviceD3D11::ImmediateEndDraw()
+{
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ int vertexCount = m_Imm.m_Vertices.size();
+
+ // vertex layout
+ ID3D11InputLayout* inputLayout = m_VertexDecls.GetImmVertexDecl (g_CurrentVSInputD3D11);
+ if (inputLayout)
+ {
+ SetInputLayoutD3D11 (ctx, inputLayout);
+ if (SetTopologyD3D11 (m_Imm.m_Mode, *this, ctx))
+ {
+
+ // draw
+ switch (m_Imm.m_Mode)
+ {
+ case kPrimitiveTriangles:
+ D3D11_CALL(ctx->Draw (vertexCount, m_Imm.m_VBStartVertex));
+ m_Stats.AddDrawCall (vertexCount / 3, vertexCount);
+ break;
+ case kPrimitiveTriangleStripDeprecated:
+ D3D11_CALL(ctx->Draw (vertexCount, m_Imm.m_VBStartVertex));
+ m_Stats.AddDrawCall (vertexCount - 2, vertexCount);
+ break;
+ case kPrimitiveQuads:
+ GetDynamicVBO(); // ensure it's created
+ D3D11_CALL(ctx->IASetIndexBuffer (m_DynamicVBO->GetQuadsIB(), DXGI_FORMAT_R16_UINT, 0));
+ D3D11_CALL(ctx->DrawIndexed (vertexCount/4*6, 0, m_Imm.m_VBStartVertex));
+ m_Stats.AddDrawCall( vertexCount / 4 * 2, vertexCount );
+ break;
+ case kPrimitiveLines:
+ D3D11_CALL(ctx->Draw (vertexCount, m_Imm.m_VBStartVertex));
+ m_Stats.AddDrawCall( vertexCount / 2, vertexCount );
+ break;
+ default:
+ AssertString("ImmediateEnd: unknown draw mode");
+ }
+ }
+ }
+
+ // clear vertices
+ m_Imm.m_Vertices.clear();
+}
+
+
+void GfxDeviceD3D11::ImmediateEnd()
+{
+ if (ImmediateEndSetup())
+ ImmediateEndDraw();
+}
+
+
+typedef SmartComPointer<ID3D11RenderTargetView> RTVPointer;
+typedef SmartComPointer<ID3D11Resource> ResourcePointer;
+typedef SmartComPointer<ID3D11Texture2D> Texture2DPointer;
+
+
+bool GfxDeviceD3D11::CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 )
+{
+ DX11_LOG_ENTER_FUNCTION("CaptureScreenshot(%d, %d, %dx%d)", left, bottom, width, height);
+ HRESULT hr;
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ SetupDeferredSRGBWrite ();
+
+ RenderSurfaceHandle currColorSurface = GetActiveRenderColorSurfaceBBD3D11();
+ RenderColorSurfaceD3D11* colorSurf = reinterpret_cast<RenderColorSurfaceD3D11*>(currColorSurface.object);
+ if (!colorSurf)
+ return false;
+
+ RTVPointer rtView;
+ ctx->OMGetRenderTargets (1, &rtView, NULL);
+ if (!rtView)
+ return false;
+
+ ResourcePointer rtRes;
+ rtView->GetResource (&rtRes);
+ if (!rtRes)
+ return false;
+
+ D3D11_RESOURCE_DIMENSION rtType;
+ rtRes->GetType (&rtType);
+ if (rtType != D3D11_RESOURCE_DIMENSION_TEXTURE2D)
+ return false;
+
+ ID3D11Texture2D* rtTex = static_cast<ID3D11Texture2D*>((ID3D11Resource*)rtRes);
+ D3D11_TEXTURE2D_DESC rtDesc;
+ rtTex->GetDesc (&rtDesc);
+ if (rtDesc.Format != DXGI_FORMAT_R8G8B8A8_UNORM &&
+ rtDesc.Format != DXGI_FORMAT_R8G8B8A8_TYPELESS &&
+ rtDesc.Format != DXGI_FORMAT_R8G8B8A8_UNORM_SRGB &&
+ rtDesc.Format != DXGI_FORMAT_B8G8R8A8_UNORM)
+ return false;
+
+ ID3D11Device* dev = GetD3D11Device();
+ ResolveTexturePool::Entry* resolved = NULL;
+ if (rtDesc.SampleDesc.Count != 1)
+ {
+ resolved = m_Resolves.GetResolveTexture (rtDesc.Width, rtDesc.Height, colorSurf->format, m_SRGBWrite);
+ if (!resolved)
+ return false;
+
+ ctx->ResolveSubresource (resolved->texture, 0, rtTex, 0, rtDesc.Format);
+ rtTex = resolved->texture;
+ }
+
+ Texture2DPointer stagingTex;
+ D3D11_TEXTURE2D_DESC stagingDesc;
+ stagingDesc.Width = width;
+ stagingDesc.Height = height;
+ stagingDesc.MipLevels = 1;
+ stagingDesc.ArraySize = 1;
+
+ bool useRGBA = rtDesc.Format == DXGI_FORMAT_R8G8B8A8_UNORM ||
+ rtDesc.Format == DXGI_FORMAT_R8G8B8A8_TYPELESS ||
+ rtDesc.Format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
+
+ stagingDesc.Format = useRGBA ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_B8G8R8A8_UNORM;
+ stagingDesc.SampleDesc.Count = 1;
+ stagingDesc.SampleDesc.Quality = 0;
+ stagingDesc.Usage = D3D11_USAGE_STAGING;
+ stagingDesc.BindFlags = 0;
+ stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ stagingDesc.MiscFlags = 0;
+ hr = dev->CreateTexture2D (&stagingDesc, NULL, &stagingTex);
+ if (FAILED(hr))
+ return false;
+ SetDebugNameD3D11 (stagingTex, Format("CaptureScreenshot-Texture2D-%dx%d", width, height));
+
+ D3D11_BOX srcBox;
+ srcBox.left = left;
+ srcBox.right = left + width;
+#if UNITY_WP8
+ /* In WP8 m_CurrTargetHeight seems not to match
+ * ID3D11DeviceContext height */
+ srcBox.top = bottom;
+ srcBox.bottom = bottom + height;
+#else
+ srcBox.top = m_CurrTargetHeight - (bottom + height);
+ srcBox.bottom = m_CurrTargetHeight - (bottom);
+#endif
+ srcBox.front = 0;
+ srcBox.back = 1;
+ ctx->CopySubresourceRegion (stagingTex, 0, 0, 0, 0, rtTex, 0, &srcBox);
+
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ hr = ctx->Map (stagingTex, 0, D3D11_MAP_READ, 0, &mapped);
+ if (FAILED(hr))
+ return false;
+
+ rgba32 += (height-1) * width * sizeof(UInt32);
+ const UInt8* src = (const UInt8*)mapped.pData;
+ for (int y = 0; y < height; ++y)
+ {
+ if (useRGBA)
+ {
+ memcpy (rgba32, src, width*4);
+ }
+ else
+ {
+ for (int x = 0; x < width*4; x +=4)
+ {
+ rgba32[x] = src[x + 2];
+ rgba32[x + 1] = src[x + 1];
+ rgba32[x + 2] = src[x + 0];
+ rgba32[x + 3] = src[x + 3];
+ }
+ }
+
+ rgba32 -= width * sizeof(UInt32);
+ src += mapped.RowPitch;
+ }
+
+
+ ctx->Unmap (stagingTex, 0);
+ return true;
+}
+
+
+
+bool GfxDeviceD3D11::ReadbackImage( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY )
+{
+ Assert (image.GetFormat() == kTexFormatARGB32 || image.GetFormat() == kTexFormatRGB24);
+
+ SetupDeferredSRGBWrite ();
+
+ HRESULT hr;
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ RenderSurfaceHandle currColorSurface = GetActiveRenderColorSurfaceBBD3D11();
+ RenderColorSurfaceD3D11* colorSurf = reinterpret_cast<RenderColorSurfaceD3D11*>(currColorSurface.object);
+ if (!colorSurf)
+ return false;
+
+ RTVPointer rtView;
+ ctx->OMGetRenderTargets (1, &rtView, NULL);
+ if (!rtView)
+ return false;
+
+ ResourcePointer rtRes;
+ rtView->GetResource (&rtRes);
+ if (!rtRes)
+ return false;
+
+ D3D11_RESOURCE_DIMENSION rtType;
+ rtRes->GetType (&rtType);
+ if (rtType != D3D11_RESOURCE_DIMENSION_TEXTURE2D)
+ return false;
+
+ ID3D11Texture2D* rtTex = static_cast<ID3D11Texture2D*>((ID3D11Resource*)rtRes);
+ D3D11_TEXTURE2D_DESC rtDesc;
+ rtTex->GetDesc (&rtDesc);
+ if (rtDesc.Format != DXGI_FORMAT_R8G8B8A8_UNORM && rtDesc.Format != DXGI_FORMAT_R8G8B8A8_TYPELESS && rtDesc.Format != DXGI_FORMAT_R8G8B8A8_UNORM_SRGB)
+ return false;
+
+ ID3D11Device* dev = GetD3D11Device();
+ ResolveTexturePool::Entry* resolved = NULL;
+ if (rtDesc.SampleDesc.Count != 1)
+ {
+ resolved = m_Resolves.GetResolveTexture (rtDesc.Width, rtDesc.Height, colorSurf->format, m_SRGBWrite);
+ if (!resolved)
+ return false;
+
+ ctx->ResolveSubresource (resolved->texture, 0, rtTex, 0, rtDesc.Format);
+ rtTex = resolved->texture;
+ }
+
+ Texture2DPointer stagingTex;
+ D3D11_TEXTURE2D_DESC stagingDesc;
+ stagingDesc.Width = width;
+ stagingDesc.Height = height;
+ stagingDesc.MipLevels = 1;
+ stagingDesc.ArraySize = 1;
+ stagingDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ stagingDesc.SampleDesc.Count = 1;
+ stagingDesc.SampleDesc.Quality = 0;
+ stagingDesc.Usage = D3D11_USAGE_STAGING;
+ stagingDesc.BindFlags = 0;
+ stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ stagingDesc.MiscFlags = 0;
+ hr = dev->CreateTexture2D (&stagingDesc, NULL, &stagingTex);
+ if (FAILED(hr))
+ return false;
+ SetDebugNameD3D11 (stagingTex, Format("Readback-Texture2D-%dx%d", width, height));
+
+ D3D11_BOX srcBox;
+ srcBox.left = left;
+ srcBox.right = left + width;
+ srcBox.top = m_CurrTargetHeight - (bottom + height);
+ srcBox.bottom = m_CurrTargetHeight - (bottom);
+ srcBox.front = 0;
+ srcBox.back = 1;
+ ctx->CopySubresourceRegion (stagingTex, 0, 0, 0, 0, rtTex, 0, &srcBox);
+
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ hr = ctx->Map (stagingTex, 0, D3D11_MAP_READ, 0, &mapped);
+ if (FAILED(hr))
+ return false;
+
+ const UInt8* src = (const UInt8*)mapped.pData;
+
+ if (image.GetFormat() == kTexFormatARGB32)
+ {
+ for (int y = height-1; y >= 0; --y)
+ {
+ const UInt32* srcPtr = (const UInt32*)src;
+ UInt32* dstPtr = (UInt32*)(image.GetRowPtr(destY+y) + destX * 4);
+ for (int x = 0; x < width; ++x)
+ {
+ UInt32 abgrCol = *srcPtr;
+ UInt32 bgraCol = ((abgrCol & 0x00FFFFFF) << 8) | ((abgrCol&0xFF000000) >> 24);
+ *dstPtr = bgraCol;
+ ++srcPtr;
+ ++dstPtr;
+ }
+ src += mapped.RowPitch;
+ }
+ }
+ else if (image.GetFormat() == kTexFormatRGB24)
+ {
+ for (int y = height-1; y >= 0; --y)
+ {
+ const UInt32* srcPtr = (const UInt32*)src;
+ UInt8* dstPtr = image.GetRowPtr(destY+y) + destX * 3;
+ for (int x = 0; x < width; ++x)
+ {
+ UInt32 abgrCol = *srcPtr;
+ dstPtr[0] = (abgrCol & 0x000000FF);
+ dstPtr[1] = (abgrCol & 0x0000FF00) >> 8;
+ dstPtr[2] = (abgrCol & 0x00FF0000) >> 16;
+ ++srcPtr;
+ dstPtr += 3;
+ }
+ src += mapped.RowPitch;
+ }
+ }
+ ctx->Unmap (stagingTex, 0);
+ return true;
+}
+
+void GfxDeviceD3D11::GrabIntoRenderTexture(RenderSurfaceHandle rtHandle, RenderSurfaceHandle rd, int x, int y, int width, int height)
+{
+ DX11_LOG_ENTER_FUNCTION("GrabIntoRenderTexture(%p, %p, %d, %d, %dx%d)", rtHandle.object, rd.object, x, y, width, height);
+ if (!rtHandle.IsValid())
+ return;
+ if (!g_D3D11CurrColorRT)
+ return;
+ RenderSurfaceHandle currColorSurface = GetActiveRenderColorSurfaceBBD3D11();
+ RenderColorSurfaceD3D11* colorSurf = reinterpret_cast<RenderColorSurfaceD3D11*>(currColorSurface.object);
+ if (!colorSurf)
+ return;
+ const bool sRGB = (gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0) ? (colorSurf->flags & kSurfaceCreateSRGB) : false;
+
+ RenderColorSurfaceD3D11* renderTexture = reinterpret_cast<RenderColorSurfaceD3D11*>(rtHandle.object);
+ TexturesD3D11::D3D11Texture* texturePointer = m_Textures.GetTexture (renderTexture->textureID);
+ if (!texturePointer)
+ return;
+
+ SetupDeferredSRGBWrite ();
+
+ ID3D11Resource* srcResource = g_D3D11CurrColorRT->m_Texture;
+ ID3D11Texture2D* srcTex = static_cast<ID3D11Texture2D*>(srcResource);
+ D3D11_TEXTURE2D_DESC rtDesc;
+ srcTex->GetDesc (&rtDesc);
+ Assert (rtDesc.Width == colorSurf->width && rtDesc.Height == colorSurf->height);
+
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ ResolveTexturePool::Entry* resolved = NULL;
+ if (rtDesc.SampleDesc.Count != 1)
+ {
+ resolved = m_Resolves.GetResolveTexture (rtDesc.Width, rtDesc.Height, colorSurf->format, sRGB);
+ if (!resolved)
+ return;
+
+ ctx->ResolveSubresource (resolved->texture, 0, srcResource, 0, GetRenderTextureFormat(colorSurf->format, sRGB));
+ srcResource = resolved->texture;
+ }
+
+ ID3D11Texture2D* dstTex = static_cast<ID3D11Texture2D*>(texturePointer->m_Texture);
+ D3D11_TEXTURE2D_DESC dstDesc;
+ dstTex->GetDesc (&dstDesc);
+
+ if (GetBPPFromDXGIFormat(rtDesc.Format) == GetBPPFromDXGIFormat(dstDesc.Format))
+ {
+ D3D11_BOX srcBox;
+ srcBox.left = x;
+ srcBox.right = x + width;
+ srcBox.top = m_CurrTargetHeight - (y + height);
+ srcBox.bottom = m_CurrTargetHeight - (y);
+ srcBox.front = 0;
+ srcBox.back = 1;
+
+ ctx->CopySubresourceRegion (texturePointer->m_Texture, 0, 0, 0, 0, srcResource, 0, &srcBox);
+ }
+ else
+ {
+ // formats not compatible; have to draw a quad into destination, sampling the source texture
+ RenderColorSurfaceD3D11* currRT = g_D3D11CurrColorRT;
+ int oldTargetHeight = m_CurrTargetHeight;
+ int oldView[4];
+ GetViewport (oldView);
+ bool oldScissor = IsScissorEnabled();
+ int oldScissorRect[4];
+ GetScissorRect (oldScissorRect);
+
+ SetViewport (0, 0, dstDesc.Width, dstDesc.Height);
+ DisableScissor ();
+
+ RenderSurfaceHandle currColor = GetActiveRenderColorSurface(0);
+ RenderSurfaceHandle currDepth = GetActiveRenderDepthSurface();
+ SetRenderTargets (1, &rtHandle, rd, 0, kCubeFaceUnknown);
+
+ const float u0 = x / float(rtDesc.Width);
+ const float u1 = (x+width) / float(rtDesc.Width);
+ const float v0 = (rtDesc.Height - y) / float(rtDesc.Height);
+ const float v1 = (rtDesc.Height - (y+height)) / float(rtDesc.Height);
+
+ ID3D11ShaderResourceView* srv = currRT ? currRT->m_SRView : NULL;
+ if (resolved)
+ srv = resolved->srv;
+
+ DrawQuad (u0, v0, u1, v1, 0.0f, srv);
+ SetRenderTargets (1, &currColor, currDepth, 0, kCubeFaceUnknown);
+ SetViewport (oldView[0], oldView[1], oldView[2], oldView[3]);
+ if (oldScissor)
+ SetScissorRect (oldScissorRect[0], oldScissorRect[1], oldScissorRect[2], oldScissorRect[3]);
+ }
+}
+
+void GfxDeviceD3D11::DrawQuad (float u0, float v0, float u1, float v1, float z, ID3D11ShaderResourceView* texture)
+{
+ // Can't use DeviceMVPMatricesState since that tries to get potentially threaded device.
+ // We need to access our own device directly.
+ Matrix4x4f m_World, m_View, m_Proj;
+ CopyMatrix(GetViewMatrix(), m_View.GetPtr());
+ CopyMatrix(GetWorldMatrix(), m_World.GetPtr());
+ CopyMatrix(GetProjectionMatrix(), m_Proj.GetPtr());
+
+ // Can't use LoadFullScreenOrthoMatrix for the same reason.
+ Matrix4x4f matrix;
+ matrix.SetOrtho(0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 100.0f);
+ SetProjectionMatrix (matrix);
+ SetViewMatrix (Matrix4x4f::identity.GetPtr()); // implicitly sets world to identity
+
+ DisableFog ();
+
+ SetFFLighting (false, false, kColorMatDisabled);
+ DisableLights (0);
+
+ ShaderLab::SubProgram* programs[kShaderTypeCount] = {0};
+ GraphicsHelper::SetShaders (*this, programs, NULL);
+
+ GfxBlendState blendDesc;
+ DeviceBlendState* blendState = CreateBlendState(blendDesc);
+ SetBlendState (blendState, 0.0f);
+
+ GfxDepthState depthDesc; depthDesc.depthWrite = false; depthDesc.depthFunc = kFuncAlways;
+ DeviceDepthState* depthState = CreateDepthState(depthDesc);
+ SetDepthState (depthState);
+
+ GfxRasterState rasterDesc; rasterDesc.cullMode = kCullOff;
+ DeviceRasterState* rasterState = CreateRasterState(rasterDesc);
+ SetRasterState (rasterState);
+
+ ShaderLab::TextureBinding texEnv;
+ texEnv.m_TextureName.index = kShaderTexEnvWhite | ShaderLab::FastPropertyName::kBuiltinTexEnvMask;
+ TextureCombinersHandle combiners = CreateTextureCombiners (1, &texEnv, NULL, false, false);
+
+ // Can't call SetTextureCombiners here since that expects to be called on main thread,
+ // and we might be on the device thread here. So do the work manually and call
+ // SetTextureCombinersThreadable.
+
+ TexEnvData texEnvData;
+ memset(&texEnvData, 0, sizeof(texEnvData));
+ texEnvData.textureID = TextureID();
+ texEnvData.texDim = kTexDim2D;
+ texEnvData.texGen = kTexGenDisabled;
+ texEnvData.identityMatrix = true;
+ Vector4f texColors;
+ texColors.Set(1,1,1,1);
+ SetTextureCombinersThreadable (combiners, &texEnvData, &texColors);
+
+ ImmediateBegin (kPrimitiveQuads);
+ ImmediateTexCoord(0,u0,v0,0.0f); ImmediateVertex (0.0f, 0.0f, z);
+ ImmediateTexCoord(0,u0,v1,0.0f); ImmediateVertex (0.0f, 1.0f, z);
+ ImmediateTexCoord(0,u1,v1,0.0f); ImmediateVertex (1.0f, 1.0f, z);
+ ImmediateTexCoord(0,u1,v0,0.0f); ImmediateVertex (1.0f, 0.0f, z);
+ if (ImmediateEndSetup ())
+ {
+ ID3D11SamplerState* sampler = m_Textures.GetSampler (kSamplerPointClamp);
+ Assert (sampler);
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ ctx->PSSetShaderResources (0, 1, &texture);
+ ctx->PSSetSamplers (0, 1, &sampler);
+ m_Textures.InvalidateSampler (kShaderFragment, 0);
+ m_ActiveTextures[kShaderFragment][0].m_ID = -1;
+ m_ActiveSamplers[kShaderFragment][0].m_ID = -1;
+
+ ImmediateEndDraw ();
+
+ ID3D11ShaderResourceView* nullTex = NULL;
+ ctx->PSSetShaderResources (0, 1, &nullTex);
+ }
+
+ // restore matrices
+ SetViewMatrix(m_View.GetPtr());
+ SetWorldMatrix(m_World.GetPtr());
+ SetProjectionMatrix(m_Proj);
+}
+
+void* GfxDeviceD3D11::GetNativeGfxDevice()
+{
+ return GetD3D11Device();
+}
+
+void* GfxDeviceD3D11::GetNativeTexturePointer(TextureID id)
+{
+ TexturesD3D11::D3D11Texture* tex = m_Textures.GetTexture(id);
+ if (!tex)
+ return NULL;
+ return tex->m_Texture;
+}
+
+intptr_t GfxDeviceD3D11::CreateExternalTextureFromNative(intptr_t nativeTex)
+{
+ return m_Textures.RegisterNativeTexture((ID3D11ShaderResourceView*)nativeTex);
+}
+
+void GfxDeviceD3D11::UpdateExternalTextureFromNative(TextureID tex, intptr_t nativeTex)
+{
+ m_Textures.UpdateNativeTexture(tex, (ID3D11ShaderResourceView*)nativeTex);
+}
+
+
+#if ENABLE_PROFILER
+
+void GfxDeviceD3D11::BeginProfileEvent (const char* name)
+{
+ if (g_D3D11BeginEventFunc)
+ {
+ wchar_t wideName[100];
+ UTF8ToWide (name, wideName, 100);
+ g_D3D11BeginEventFunc (0, wideName);
+ }
+}
+
+void GfxDeviceD3D11::EndProfileEvent ()
+{
+ if (g_D3D11EndEventFunc)
+ {
+ g_D3D11EndEventFunc ();
+ }
+}
+
+GfxTimerQuery* GfxDeviceD3D11::CreateTimerQuery()
+{
+ Assert(gGraphicsCaps.hasTimerQuery);
+ return g_TimerQueriesD3D11.CreateTimerQuery();
+}
+
+void GfxDeviceD3D11::DeleteTimerQuery(GfxTimerQuery* query)
+{
+ delete query;
+}
+
+void GfxDeviceD3D11::BeginTimerQueries()
+{
+ if(!gGraphicsCaps.hasTimerQuery)
+ return;
+
+ g_TimerQueriesD3D11.BeginTimerQueries();
+ }
+
+void GfxDeviceD3D11::EndTimerQueries()
+{
+ if(!gGraphicsCaps.hasTimerQuery)
+ return;
+
+ g_TimerQueriesD3D11.EndTimerQueries();
+}
+
+#endif // ENABLE_PROFILER
+
+
+// -------- editor only functions
+
+#if UNITY_EDITOR
+
+void GfxDeviceD3D11::SetAntiAliasFlag (bool aa)
+{
+}
+
+
+void GfxDeviceD3D11::DrawUserPrimitives (GfxPrimitiveType type, int vertexCount, UInt32 vertexChannels, const void* data, int stride)
+{
+ if (vertexCount == 0)
+ return;
+
+ Assert(vertexCount <= 60000); // TODO: handle this by multi-batching
+
+ Assert(data && vertexCount >= 0 && vertexChannels != 0);
+
+ DynamicD3D11VBO& vbo = static_cast<DynamicD3D11VBO&>(GetDynamicVBO());
+
+ void* vbPtr;
+ //@TODO: hack to pass kDrawTriangleStrip, but we only need that to determine if we need index buffer or not (we don't)
+ if (!vbo.GetChunk(vertexChannels, vertexCount, 0, DynamicVBO::kDrawTriangleStrip, &vbPtr, NULL))
+ return;
+ memcpy (vbPtr, data, vertexCount * stride);
+ vbo.ReleaseChunk (vertexCount, 0);
+
+ vbo.DrawChunkUserPrimitives (type);
+}
+
+int GfxDeviceD3D11::GetCurrentTargetAA() const
+{
+ return 0; //@TODO
+}
+
+GfxDeviceWindow* GfxDeviceD3D11::CreateGfxWindow (HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias)
+{
+ return new D3D11Window(window, width, height, depthFormat, antiAlias);
+}
+
+static ID3D11Texture2D* FindD3D11TextureByID (TextureID tid)
+{
+ GfxDevice& device = GetRealGfxDevice();
+ if (device.GetRenderer() != kGfxRendererD3D11)
+ return NULL;
+ GfxDeviceD3D11& dev = static_cast<GfxDeviceD3D11&>(device);
+ TexturesD3D11::D3D11Texture* basetex = dev.GetTextures().GetTexture(tid);
+ if (!basetex || !basetex->m_Texture)
+ return NULL;
+ D3D11_RESOURCE_DIMENSION dim = D3D11_RESOURCE_DIMENSION_UNKNOWN;
+ basetex->m_Texture->GetType(&dim);
+ if (dim != D3D11_RESOURCE_DIMENSION_TEXTURE2D)
+ return NULL;
+ return static_cast<ID3D11Texture2D*>(basetex->m_Texture);
+}
+
+HDC AcquireHDCForTextureD3D11 (TextureID tid, int& outWidth, int& outHeight)
+{
+ ID3D11Texture2D* tex = FindD3D11TextureByID (tid);
+ if (!tex)
+ return NULL;
+
+ D3D11_TEXTURE2D_DESC desc;
+ tex->GetDesc (&desc);
+ outWidth = desc.Width;
+ outHeight = desc.Height;
+
+ IDXGISurface1* dxgiSurface = NULL;
+ tex->QueryInterface(__uuidof(IDXGISurface1), (void**)(&dxgiSurface));
+ HDC dc = NULL;
+ if (dxgiSurface)
+ {
+ dxgiSurface->GetDC (false, &dc);
+ dxgiSurface->Release();
+ }
+ return dc;
+}
+
+void ReleaseHDCForTextureD3D11 (TextureID tid, HDC dc)
+{
+ ID3D11Texture2D* tex = FindD3D11TextureByID (tid);
+ if (!tex)
+ return;
+
+ IDXGISurface1* dxgiSurface = NULL;
+ tex->QueryInterface(__uuidof(IDXGISurface1), (void**)(&dxgiSurface));
+ if (dxgiSurface)
+ {
+ dxgiSurface->ReleaseDC(NULL);
+ dxgiSurface->Release();
+ }
+}
+
+#endif // UNITY_EDITOR
+
+
+int GfxDeviceD3D11::GetCurrentTargetWidth() const
+{
+ return m_CurrTargetWidth;
+}
+
+int GfxDeviceD3D11::GetCurrentTargetHeight() const
+{
+ return m_CurrTargetHeight;
+}
+
+void GfxDeviceD3D11::SetCurrentTargetSize(int width, int height)
+{
+ m_CurrTargetWidth = width;
+ m_CurrTargetHeight = height;
+}
+
+void GfxDeviceD3D11::SetCurrentWindowSize(int width, int height)
+{
+ m_CurrWindowWidth = m_CurrTargetWidth = width;
+ m_CurrWindowHeight = m_CurrTargetHeight = height;
+}
+
+
+// ----------------------------------------------------------------------
+
+void GfxDeviceD3D11::SetComputeBuffer11 (ShaderType shaderType, int unit, ComputeBufferID bufferHandle)
+{
+ ComputeBuffer11* buffer = m_Textures.GetComputeBuffer(bufferHandle);
+ ID3D11ShaderResourceView* srv = buffer ? buffer->srv : NULL;
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ switch (shaderType) {
+ case kShaderVertex: ctx->VSSetShaderResources (unit, 1, &srv); break;
+ case kShaderFragment: ctx->PSSetShaderResources (unit, 1, &srv); break;
+ case kShaderGeometry: ctx->GSSetShaderResources (unit, 1, &srv); break;
+ case kShaderHull: ctx->HSSetShaderResources (unit, 1, &srv); break;
+ case kShaderDomain: ctx->DSSetShaderResources (unit, 1, &srv); break;
+ default: AssertString("unknown shader type");
+ }
+ m_ActiveTextures[shaderType][unit].m_ID = 0;
+}
+
+
+void GfxDeviceD3D11::SetComputeBufferData (ComputeBufferID bufferHandle, const void* data, size_t size)
+{
+ if (!data || !size)
+ return;
+ ComputeBuffer11* buffer = m_Textures.GetComputeBuffer(bufferHandle);
+ if (!buffer || !buffer->buffer)
+ return;
+
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ D3D11_BOX box;
+ box.left = 0;
+ box.top = 0;
+ box.front = 0;
+ box.right = size;
+ box.bottom = 1;
+ box.back = 1;
+ ctx->UpdateSubresource (buffer->buffer, 0, &box, data, 0, 0);
+}
+
+
+void GfxDeviceD3D11::GetComputeBufferData (ComputeBufferID bufferHandle, void* dest, size_t destSize)
+{
+ if (!dest || !destSize)
+ return;
+ ComputeBuffer11* buffer = m_Textures.GetComputeBuffer(bufferHandle);
+ if (!buffer || !buffer->buffer)
+ return;
+
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ ID3D11Buffer* cpuBuffer = NULL;
+ D3D11_BUFFER_DESC desc;
+ ZeroMemory (&desc, sizeof(desc));
+ buffer->buffer->GetDesc (&desc);
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ desc.Usage = D3D11_USAGE_STAGING;
+ desc.BindFlags = 0;
+ desc.MiscFlags = 0;
+ HRESULT hr = GetD3D11Device()->CreateBuffer(&desc, NULL, &cpuBuffer);
+ if (FAILED(hr))
+ return;
+ SetDebugNameD3D11 (cpuBuffer, Format("CSGetData-Staging-%d", desc.ByteWidth));
+
+ ctx->CopyResource (cpuBuffer, buffer->buffer);
+
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ hr = ctx->Map (cpuBuffer, 0, D3D11_MAP_READ, 0, &mapped);
+ if (SUCCEEDED(hr))
+ {
+ memcpy (dest, mapped.pData, destSize);
+ ctx->Unmap (cpuBuffer, 0);
+ }
+ SAFE_RELEASE (cpuBuffer);
+}
+
+
+
+void GfxDeviceD3D11::CopyComputeBufferCount (ComputeBufferID srcBuffer, ComputeBufferID dstBuffer, UInt32 dstOffset)
+{
+ ComputeBuffer11* src = m_Textures.GetComputeBuffer(srcBuffer);
+ if (!src || !src->uav)
+ return;
+ ComputeBuffer11* dst = m_Textures.GetComputeBuffer(dstBuffer);
+ if (!dst || !dst->buffer)
+ return;
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ ctx->CopyStructureCount (dst->buffer, dstOffset, src->uav);
+}
+
+
+
+void GfxDeviceD3D11::SetRandomWriteTargetTexture (int index, TextureID tid)
+{
+ void SetRandomWriteTargetTextureD3D11 (int index, TextureID tid);
+ SetRandomWriteTargetTextureD3D11 (index, tid);
+}
+
+void GfxDeviceD3D11::SetRandomWriteTargetBuffer (int index, ComputeBufferID bufferHandle)
+{
+ void SetRandomWriteTargetBufferD3D11 (int index, ComputeBufferID bufferHandle);
+ SetRandomWriteTargetBufferD3D11 (index, bufferHandle);
+}
+
+void GfxDeviceD3D11::ClearRandomWriteTargets ()
+{
+ void ClearRandomWriteTargetsD3D11 (TexturesD3D11* textures);
+ ClearRandomWriteTargetsD3D11 (&m_Textures);
+}
+
+
+ComputeProgramHandle GfxDeviceD3D11::CreateComputeProgram (const UInt8* code, size_t codeSize)
+{
+ ComputeProgramHandle cpHandle;
+ if (gGraphicsCaps.d3d11.featureLevel < kDX11Level11_0)
+ return cpHandle;
+
+ ID3D11Device* dev = GetD3D11Device();
+ HRESULT hr;
+ ID3D11ComputeShader* cs = NULL;
+ hr = dev->CreateComputeShader (code, codeSize, NULL, &cs);
+ if (FAILED(hr))
+ return cpHandle;
+ SetDebugNameD3D11 (cs, Format("ComputeShader-%d", (int)codeSize));
+
+ cpHandle.object = cs;
+ return cpHandle;
+}
+
+void GfxDeviceD3D11::DestroyComputeProgram (ComputeProgramHandle& cpHandle)
+{
+ if (!cpHandle.IsValid())
+ return;
+
+ ID3D11ComputeShader* cs = reinterpret_cast<ID3D11ComputeShader*>(cpHandle.object);
+ SAFE_RELEASE(cs);
+ cpHandle.Reset();
+}
+
+void GfxDeviceD3D11::CreateComputeConstantBuffers (unsigned count, const UInt32* sizes, ConstantBufferHandle* outCBs)
+{
+ ID3D11Device* dev = GetD3D11Device();
+ HRESULT hr;
+
+ D3D11_BUFFER_DESC desc;
+ desc.Usage = D3D11_USAGE_DYNAMIC;
+ desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+ desc.MiscFlags = 0;
+ desc.StructureByteStride = 0;
+ for (unsigned i = 0; i < count; ++i)
+ {
+ desc.ByteWidth = sizes[i];
+ ID3D11Buffer* cb = NULL;
+ hr = dev->CreateBuffer (&desc, NULL, &cb);
+ if (cb)
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(cb,sizes[i],this);
+ Assert (SUCCEEDED(hr));
+ outCBs[i].object = cb;
+
+ SetDebugNameD3D11 (cb, Format("CSConstantBuffer-%d-%d", i, sizes[i]));
+ }
+}
+
+void GfxDeviceD3D11::DestroyComputeConstantBuffers (unsigned count, ConstantBufferHandle* cbs)
+{
+ for (unsigned i = 0; i < count; ++i)
+ {
+ ID3D11Buffer* cb = reinterpret_cast<ID3D11Buffer*>(cbs[i].object);
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(cb);
+ SAFE_RELEASE(cb);
+ cbs[i].Reset();
+ }
+}
+
+
+void GfxDeviceD3D11::CreateComputeBuffer (ComputeBufferID id, size_t count, size_t stride, UInt32 flags)
+{
+ ComputeBuffer11 buffer;
+ buffer.buffer = NULL;
+ buffer.srv = NULL;
+ buffer.uav = NULL;
+ if (gGraphicsCaps.d3d11.featureLevel < kDX11Level10_0)
+ return;
+
+ ID3D11Device* dev = GetD3D11Device();
+ HRESULT hr;
+
+ // buffer
+ D3D11_BUFFER_DESC bufferDesc;
+ memset (&bufferDesc, 0, sizeof(bufferDesc));
+ bufferDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+ if (gGraphicsCaps.d3d11.featureLevel >= kDX11Level11_0)
+ bufferDesc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;
+ bufferDesc.ByteWidth = count * stride;
+ if (flags & kCBFlagDrawIndirect)
+ bufferDesc.MiscFlags = (gGraphicsCaps.d3d11.featureLevel >= kDX11Level11_0 ? D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS : 0);
+ else if (flags & kCBFlagRaw)
+ bufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS;
+ else
+ bufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
+ bufferDesc.StructureByteStride = stride;
+ bufferDesc.Usage = D3D11_USAGE_DEFAULT;
+ hr = dev->CreateBuffer (&bufferDesc, NULL, &buffer.buffer);
+ if (buffer.buffer)
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(buffer.buffer,count * stride,this);
+ Assert (SUCCEEDED(hr));
+ SetDebugNameD3D11 (buffer.buffer, Format("ComputeBuffer-%dx%d", (int)count, (int)stride));
+
+ // unordered access view, only on DX11+ HW
+ if (gGraphicsCaps.d3d11.featureLevel >= kDX11Level11_0 && !(flags & kCBFlagDrawIndirect))
+ {
+ D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
+ memset (&uavDesc, 0, sizeof(uavDesc));
+ uavDesc.Format = (flags & kCBFlagRaw) ? DXGI_FORMAT_R32_TYPELESS : DXGI_FORMAT_UNKNOWN;
+ uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
+ uavDesc.Buffer.FirstElement = 0;
+ uavDesc.Buffer.NumElements = count;
+ uavDesc.Buffer.Flags = flags & kCBFlagTypeMask;
+ hr = dev->CreateUnorderedAccessView (buffer.buffer, &uavDesc, &buffer.uav);
+ Assert (SUCCEEDED(hr));
+ SetDebugNameD3D11 (buffer.uav, Format("ComputeBuffer-UAV-%dx%d", (int)count, (int)stride));
+
+ // shader resource view
+ D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
+ memset (&srvDesc, 0, sizeof(srvDesc));
+ if (flags & kCBFlagRaw)
+ {
+ srvDesc.Format = DXGI_FORMAT_R32_TYPELESS;
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX;
+ srvDesc.BufferEx.FirstElement = 0;
+ srvDesc.BufferEx.NumElements = count;
+ srvDesc.BufferEx.Flags = D3D11_BUFFEREX_SRV_FLAG_RAW;
+ }
+ else
+ {
+ srvDesc.Format = DXGI_FORMAT_UNKNOWN;
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
+ srvDesc.Buffer.FirstElement = 0;
+ srvDesc.Buffer.NumElements = count;
+ }
+ hr = dev->CreateShaderResourceView (buffer.buffer, &srvDesc, &buffer.srv);
+ Assert (SUCCEEDED(hr));
+ SetDebugNameD3D11 (buffer.uav, Format("ComputeBuffer-SRV-%dx%d", (int)count, (int)stride));
+ }
+
+ m_Textures.AddComputeBuffer (id, buffer);
+}
+
+
+void GfxDeviceD3D11::DestroyComputeBuffer (ComputeBufferID handle)
+{
+ if (!handle.IsValid())
+ return;
+
+ ComputeBuffer11* buffer = m_Textures.GetComputeBuffer(handle);
+ if (buffer)
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(buffer->buffer);
+ SAFE_RELEASE(buffer->buffer);
+ SAFE_RELEASE(buffer->srv);
+ SAFE_RELEASE(buffer->uav);
+ }
+ m_Textures.RemoveComputeBuffer (handle);
+}
+
+
+void GfxDeviceD3D11::UpdateComputeConstantBuffers (unsigned count, ConstantBufferHandle* cbs, UInt32 cbDirty, size_t dataSize, const UInt8* data, const UInt32* cbSizes, const UInt32* cbOffsets, const int* bindPoints)
+{
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ // go over constant buffers in use
+ for (unsigned i = 0; i < count; ++i)
+ {
+ if (bindPoints[i] < 0)
+ continue; // CB not going to be used, no point in updating it
+
+ ID3D11Buffer* cb = reinterpret_cast<ID3D11Buffer*>(cbs[i].object);
+
+ // update buffer if dirty
+ UInt32 dirtyMask = (1<<i);
+ if (cbDirty & dirtyMask)
+ {
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ HRESULT hr;
+ hr = ctx->Map (cb, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
+ Assert (SUCCEEDED(hr));
+ memcpy (mapped.pData, data + cbOffsets[i], cbSizes[i]);
+ ctx->Unmap (cb, 0);
+ }
+
+ // bind it
+ ctx->CSSetConstantBuffers (bindPoints[i], 1, &cb);
+ }
+}
+
+
+
+void GfxDeviceD3D11::UpdateComputeResources (
+ unsigned texCount, const TextureID* textures, const int* texBindPoints,
+ unsigned samplerCount, const unsigned* samplers,
+ unsigned inBufferCount, const ComputeBufferID* inBuffers, const int* inBufferBindPoints,
+ unsigned outBufferCount, const ComputeBufferID* outBuffers, const TextureID* outTextures, const UInt32* outBufferBindPoints)
+{
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ for (unsigned i = 0; i < texCount; ++i)
+ {
+ if (textures[i].m_ID == 0)
+ continue;
+ TexturesD3D11::D3D11Texture* tex = m_Textures.GetTexture (textures[i]);
+ if (!tex)
+ continue;
+
+ // if texture is bound as render target: unbind it (set backbuffer as RT)
+ if ((g_D3D11CurrColorRT && g_D3D11CurrColorRT->m_Texture == tex->m_Texture) ||
+ (g_D3D11CurrDepthRT && g_D3D11CurrDepthRT->m_Texture == tex->m_Texture))
+ {
+ RenderSurfaceHandle defaultColor = GetBackBufferColorSurface();
+ RenderSurfaceHandle defaultDepth = GetBackBufferDepthSurface();
+ SetRenderTargets (1, &defaultColor, defaultDepth, 0, kCubeFaceUnknown);
+ }
+ ctx->CSSetShaderResources (texBindPoints[i] & 0xFFFF, 1, &tex->m_SRV);
+ unsigned samplerBindPoint = (texBindPoints[i] >> 16) & 0xFFFF;
+ if (samplerBindPoint != 0xFFFF)
+ {
+ ID3D11SamplerState* smp = m_Textures.GetSampler(tex->m_Sampler);
+ ctx->CSSetSamplers (samplerBindPoint, 1, &smp);
+ }
+ }
+
+ for (unsigned i = 0; i < samplerCount; ++i)
+ {
+ BuiltinSamplerState type = (BuiltinSamplerState)((samplers[i] & 0xFFFF0000) >> 16);
+ unsigned bindPoint = samplers[i] & 0xFFFF;
+ ID3D11SamplerState* smp = m_Textures.GetSampler (type);
+ Assert (smp);
+ ctx->CSSetSamplers (bindPoint, 1, &smp);
+ }
+
+ for (unsigned i = 0; i < inBufferCount; ++i)
+ {
+ ComputeBuffer11* buffer = m_Textures.GetComputeBuffer(inBuffers[i]);
+ if (!buffer)
+ continue;
+ ctx->CSSetShaderResources (inBufferBindPoints[i], 1, &buffer->srv);
+ }
+
+ for (unsigned i = 0; i < outBufferCount; ++i)
+ {
+ ID3D11UnorderedAccessView* uav = NULL;
+ if (outBufferBindPoints[i] & 0x80000000)
+ {
+ // UAV comes from texture
+ if (outTextures[i].m_ID == 0)
+ continue;
+ TexturesD3D11::D3D11Texture* tex = m_Textures.GetTexture (outTextures[i]);
+ if (!tex || !tex->m_UAV)
+ continue;
+ uav = tex->m_UAV;
+ }
+ else
+ {
+ // UAV is raw buffer
+ if (!outBuffers[i].IsValid())
+ continue;
+ ComputeBuffer11* buffer = m_Textures.GetComputeBuffer(outBuffers[i]);
+ if (!buffer)
+ continue;
+ uav = buffer->uav;
+ }
+ UINT uavInitialCounts[] = { -1 }; // keeps current offsets for Appendable/Consumeable UAVs
+ ctx->CSSetUnorderedAccessViews (outBufferBindPoints[i] & 0x7FFFFFFF, 1, &uav, uavInitialCounts);
+ }
+}
+
+
+
+void GfxDeviceD3D11::DispatchComputeProgram (ComputeProgramHandle cpHandle, unsigned threadsX, unsigned threadsY, unsigned threadsZ)
+{
+ if (!cpHandle.IsValid())
+ return;
+
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ ID3D11ComputeShader* cs = reinterpret_cast<ID3D11ComputeShader*>(cpHandle.object);
+ ctx->CSSetShader (cs, NULL, 0);
+ ctx->Dispatch (threadsX, threadsY, threadsZ);
+
+
+ // DEBUG: readback output UAV contents
+ #if 0 && !UNITY_RELEASE
+ ID3D11UnorderedAccessView* uav;
+ ctx->CSGetUnorderedAccessViews (0, 1, &uav);
+ ID3D11Buffer* res;
+ uav->GetResource ((ID3D11Resource**)&res);
+
+ ID3D11Buffer* debugbuf = NULL;
+ D3D11_BUFFER_DESC desc;
+ ZeroMemory (&desc, sizeof(desc));
+ res->GetDesc (&desc);
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ desc.Usage = D3D11_USAGE_STAGING;
+ desc.BindFlags = 0;
+ desc.MiscFlags = 0;
+ GetD3D11Device()->CreateBuffer(&desc, NULL, &debugbuf);
+ ctx->CopyResource (debugbuf, res);
+
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ ctx->Map(debugbuf, 0, D3D11_MAP_READ, 0, &mapped);
+ ctx->Unmap(debugbuf, 0);
+ SAFE_RELEASE(debugbuf);
+ #endif
+
+ ID3D11UnorderedAccessView* nullUAVs[8] = {0};
+ ctx->CSSetUnorderedAccessViews (0, 8, nullUAVs, NULL);
+}
+
+
+// ----------------------------------------------------------------------
+
+
+void GfxDeviceD3D11::DrawNullGeometry (GfxPrimitiveType topology, int vertexCount, int instanceCount)
+{
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ UINT strides = 0;
+ UINT offsets = 0;
+ ID3D11Buffer* vb = NULL;
+ D3D11_CALL (ctx->IASetVertexBuffers(0, 1, &vb, &strides, &offsets));
+
+ BeforeDrawCall (false);
+
+ // vertex layout
+ SetInputLayoutD3D11 (ctx, NULL);
+
+ // draw
+ if (!SetTopologyD3D11 (topology, *this, ctx))
+ return;
+ if (instanceCount > 1)
+ {
+ D3D11_CALL (ctx->DrawInstanced (vertexCount, instanceCount, 0, 0));
+ }
+ else
+ {
+ D3D11_CALL (ctx->Draw (vertexCount, 0));
+ }
+}
+
+void GfxDeviceD3D11::DrawNullGeometryIndirect (GfxPrimitiveType topology, ComputeBufferID bufferHandle, UInt32 bufferOffset)
+{
+ if (gGraphicsCaps.d3d11.featureLevel < kDX11Level11_0)
+ return;
+
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ UINT strides = 0;
+ UINT offsets = 0;
+ ID3D11Buffer* vb = NULL;
+ D3D11_CALL (ctx->IASetVertexBuffers(0, 1, &vb, &strides, &offsets));
+
+ BeforeDrawCall (false);
+
+ // vertex layout
+ SetInputLayoutD3D11 (ctx, NULL);
+
+ // draw
+ if (!SetTopologyD3D11 (topology, *this, ctx))
+ return;
+ ComputeBuffer11* buffer = m_Textures.GetComputeBuffer(bufferHandle);
+ if (!buffer || !buffer->buffer)
+ return;
+ D3D11_CALL (ctx->DrawInstancedIndirect (buffer->buffer, bufferOffset));
+}
+
+
+// GPU skinning functionality
+GPUSkinningInfo * GfxDeviceD3D11::CreateGPUSkinningInfo()
+{
+ // stream-out requires at least DX10.0
+ if (gGraphicsCaps.d3d11.featureLevel < kDX11Level10_0)
+ return NULL;
+
+ return new StreamOutSkinningInfo();
+}
+
+void GfxDeviceD3D11::DeleteGPUSkinningInfo(GPUSkinningInfo *info)
+{
+ delete reinterpret_cast<StreamOutSkinningInfo *>(info);
+}
+
+// All actual functionality is performed in StreamOutSkinningInfo, just forward the calls
+void GfxDeviceD3D11::SkinOnGPU( GPUSkinningInfo * info, bool lastThisFrame )
+{
+ reinterpret_cast<StreamOutSkinningInfo *>(info)->SkinMesh(lastThisFrame);
+}
+
+void GfxDeviceD3D11::UpdateSkinSourceData(GPUSkinningInfo *info, const void *vertData, const BoneInfluence *skinData, bool dirty)
+{
+ reinterpret_cast<StreamOutSkinningInfo *>(info)->UpdateSourceData(vertData, skinData, dirty);
+}
+
+void GfxDeviceD3D11::UpdateSkinBonePoses(GPUSkinningInfo *info, const int boneCount, const Matrix4x4f* poses)
+{
+ reinterpret_cast<StreamOutSkinningInfo *>(info)->UpdateSourceBones(boneCount, poses);
+}
+
+
+// ----------------------------------------------------------------------
+// verification of state
+
+#if GFX_DEVICE_VERIFY_ENABLE
+
+
+void GfxDeviceD3D11::VerifyState()
+{
+}
+#endif // GFX_DEVICE_VERIFY_ENABLE
diff --git a/Runtime/GfxDevice/d3d11/GfxDeviceD3D11.h b/Runtime/GfxDevice/d3d11/GfxDeviceD3D11.h
new file mode 100644
index 0000000..8a7595c
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/GfxDeviceD3D11.h
@@ -0,0 +1,357 @@
+#pragma once
+
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/GfxDevice/TransformState.h"
+#include "D3D11Includes.h"
+#include "VertexDeclarationsD3D11.h"
+#include "TexturesD3D11.h"
+#include "FixedFunctionStateD3D11.h"
+#include "ConstantBuffersD3D11.h"
+#include <map>
+
+class FixedFunctionProgramD3D11;
+class DynamicD3D11VBO;
+
+
+// TODO: optimize this. Right now we just send off whole 8 float3 UVs with each
+// immediate mode vertex. We could at least detect the number of them used from
+// ImmediateTexCoord calls.
+struct ImmediateVertexD3D11 {
+ Vector3f vertex;
+ Vector3f normal;
+ ColorRGBA32 color;
+ Vector3f texCoords[8];
+};
+
+struct ImmediateModeD3D11 {
+ std::vector<ImmediateVertexD3D11> m_Vertices;
+ ImmediateVertexD3D11 m_Current;
+ GfxPrimitiveType m_Mode;
+ ID3D11Buffer* m_VB;
+ int m_VBUsedBytes;
+ int m_VBStartVertex;
+ bool m_HadColor;
+
+ ImmediateModeD3D11();
+ ~ImmediateModeD3D11();
+ void Cleanup();
+ void Invalidate();
+};
+
+struct TextureUnitState11
+{
+ Matrix4x4f matrix;
+};
+
+
+
+typedef std::map<FixedFunctionStateD3D11, FixedFunctionProgramD3D11*, FixedFuncStateCompareD3D11> FFProgramCacheD3D11;
+
+struct ResolveTexturePool
+{
+ enum { kResolvePoolSize = 8 };
+ struct Entry
+ {
+ // key
+ int width;
+ int height;
+ RenderTextureFormat format;
+ bool sRGB;
+
+ // data
+ ID3D11Texture2D* texture;
+ ID3D11ShaderResourceView* srv;
+ int lastUse;
+ };
+
+ ResolveTexturePool();
+ void Clear();
+
+ Entry* GetResolveTexture (int width, int height, RenderTextureFormat fmt, bool sRGB);
+
+ Entry m_Entries[kResolvePoolSize];
+ int m_UseCounter;
+};
+
+class GfxDeviceD3D11 : public GfxThreadableDevice
+{
+public:
+ struct DeviceBlendStateD3D11 : public DeviceBlendState
+ {
+ ID3D11BlendState* deviceState;
+ };
+
+ typedef std::map< GfxBlendState, DeviceBlendStateD3D11, memcmp_less<GfxBlendState> > CachedBlendStates;
+ typedef std::map< GfxDepthState, DeviceDepthState, memcmp_less<GfxDepthState> > CachedDepthStates;
+ typedef std::map< GfxStencilState, DeviceStencilState, memcmp_less<GfxStencilState> > CachedStencilStates;
+ typedef std::map< GfxRasterState, DeviceRasterState, memcmp_less<GfxRasterState> > CachedRasterStates;
+
+ struct DepthStencilState {
+ DeviceDepthState d;
+ DeviceStencilState s;
+ };
+ typedef std::map< DepthStencilState, ID3D11DepthStencilState*, memcmp_less<DepthStencilState> > CachedDepthStencilStates;
+
+ struct FinalRasterState11 {
+ DeviceRasterState raster;
+ bool backface;
+ bool wireframe;
+ bool scissor;
+ };
+ typedef std::map< FinalRasterState11, ID3D11RasterizerState*, memcmp_less<FinalRasterState11> > CachedFinalRasterStates;
+
+public:
+ GfxDeviceD3D11();
+ GFX_API ~GfxDeviceD3D11();
+
+ GFX_API void InvalidateState();
+ #if GFX_DEVICE_VERIFY_ENABLE
+ GFX_API void VerifyState();
+ #endif
+
+ GFX_API void Clear (UInt32 clearFlags, const float color[4], float depth, int stencil);
+ GFX_API void SetUserBackfaceMode( bool enable );
+ GFX_API void SetWireframe (bool wire);
+ GFX_API bool GetWireframe() const;
+ GFX_API void SetInvertProjectionMatrix( bool enable );
+ GFX_API bool GetInvertProjectionMatrix() const;
+
+ GFX_API DeviceBlendState* CreateBlendState(const GfxBlendState& state);
+ GFX_API DeviceDepthState* CreateDepthState(const GfxDepthState& state);
+ GFX_API DeviceStencilState* CreateStencilState(const GfxStencilState& state);
+ GFX_API DeviceRasterState* CreateRasterState(const GfxRasterState& state);
+
+ GFX_API void SetBlendState(const DeviceBlendState* state, float alphaRef);
+ GFX_API void SetRasterState(const DeviceRasterState* state);
+ GFX_API void SetDepthState(const DeviceDepthState* state);
+ GFX_API void SetStencilState(const DeviceStencilState* state, int stencilRef);
+ GFX_API void SetSRGBWrite (const bool);
+ GFX_API bool GetSRGBWrite ();
+
+ /* GPU Skinning functions */
+ GFX_API GPUSkinningInfo *CreateGPUSkinningInfo();
+ GFX_API void DeleteGPUSkinningInfo(GPUSkinningInfo *info);
+ GFX_API void SkinOnGPU( GPUSkinningInfo * info, bool lastThisFrame );
+ GFX_API void UpdateSkinSourceData(GPUSkinningInfo *info, const void *vertData, const BoneInfluence *skinData, bool dirty);
+ GFX_API void UpdateSkinBonePoses(GPUSkinningInfo *info, const int boneCount, const Matrix4x4f* poses);
+
+ GFX_API void SetWorldMatrix( const float matrix[16] );
+ GFX_API void SetViewMatrix( const float matrix[16] );
+ GFX_API void SetProjectionMatrix (const Matrix4x4f& matrix);
+ GFX_API void GetMatrix( float outMatrix[16] ) const;
+
+ GFX_API const float* GetWorldMatrix() const ;
+ GFX_API const float* GetViewMatrix() const ;
+ GFX_API const float* GetProjectionMatrix() const ;
+ GFX_API const float* GetDeviceProjectionMatrix() const;
+
+ GFX_API void SetNormalizationBackface( NormalizationMode mode, bool backface );
+ GFX_API void SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial );
+ GFX_API void SetMaterial( const float ambient[4], const float diffuse[4], const float specular[4], const float emissive[4], const float shininess );
+ GFX_API void SetColor( const float color[4] );
+ GFX_API void SetViewport( int x, int y, int width, int height );
+ GFX_API void GetViewport( int* port ) const;
+
+ GFX_API void SetScissorRect( int x, int y, int width, int height );
+ GFX_API void DisableScissor();
+ GFX_API bool IsScissorEnabled() const;
+ GFX_API void GetScissorRect( int values[4] ) const;
+
+ GFX_API bool IsCombineModeSupported( unsigned int combiner );
+ GFX_API TextureCombinersHandle CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular );
+ GFX_API void DeleteTextureCombiners( TextureCombinersHandle& textureCombiners );
+ GFX_API void SetTextureCombinersThreadable( TextureCombinersHandle textureCombiners, const TexEnvData* texEnvData, const Vector4f* texColors );
+ GFX_API void SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props );
+
+ GFX_API void SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias);
+ GFX_API void SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace );
+ GFX_API void SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16]);
+ GFX_API void SetTextureName ( TextureID texture, const char* name ) { }
+
+ GFX_API void SetShadersThreadable (GpuProgram* programs[kShaderTypeCount], const GpuProgramParameters* params[kShaderTypeCount], UInt8 const * const paramsBuffer[kShaderTypeCount]);
+ GFX_API bool IsShaderActive( ShaderType type ) const;
+ GFX_API void DestroySubProgram( ShaderLab::SubProgram* subprogram );
+ GFX_API void SetConstantBufferInfo (int id, int size);
+
+ GFX_API void DisableLights( int startLight );
+ GFX_API void SetLight( int light, const GfxVertexLight& data);
+ GFX_API void SetAmbient( const float ambient[4] );
+
+ GFX_API void EnableFog (const GfxFogParams& fog);
+ GFX_API void DisableFog();
+
+ GFX_API VBO* CreateVBO();
+ GFX_API void DeleteVBO( VBO* vbo );
+ GFX_API DynamicVBO& GetDynamicVBO();
+
+ GFX_API void DiscardContents (RenderSurfaceHandle& rs);
+
+ GFX_API RenderSurfaceHandle CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags);
+ GFX_API RenderSurfaceHandle CreateRenderDepthSurface (TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags);
+ GFX_API void DestroyRenderSurface (RenderSurfaceHandle& rs);
+ GFX_API void SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face = kCubeFaceUnknown);
+ GFX_API void ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle);
+ GFX_API void ResolveDepthIntoTexture (RenderSurfaceHandle colorHandle, RenderSurfaceHandle depthHandle);
+ GFX_API RenderSurfaceHandle GetActiveRenderColorSurface (int index);
+ GFX_API RenderSurfaceHandle GetActiveRenderDepthSurface ();
+ GFX_API void SetSurfaceFlags(RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags);
+
+
+ GFX_API void UploadTexture2D( TextureID texture, TextureDimension dimension, UInt8* srcData, int srcSize, int width, int height, TextureFormat format, int mipCount, UInt32 uploadFlags, int skipMipLevels, TextureUsageMode usageMode, TextureColorSpace colorSpace );
+ GFX_API void UploadTextureSubData2D( TextureID texture, UInt8* srcData, int srcSize, int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace );
+ GFX_API void UploadTextureCube( TextureID texture, UInt8* srcData, int srcSize, int faceDataSize, int size, TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace );
+ GFX_API void UploadTexture3D( TextureID texture, UInt8* srcData, int srcSize, int width, int height, int depth, TextureFormat format, int mipCount, UInt32 uploadFlags );
+ GFX_API void DeleteTexture( TextureID texture );
+
+ GFX_API PresentMode GetPresentMode();
+
+ GFX_API void BeginFrame();
+ GFX_API void EndFrame();
+ GFX_API void PresentFrame();
+ GFX_API bool IsValidState();
+
+ GFX_API void FinishRendering();
+
+ // Immediate mode rendering
+ GFX_API void ImmediateVertex( float x, float y, float z );
+ GFX_API void ImmediateNormal( float x, float y, float z );
+ GFX_API void ImmediateColor( float r, float g, float b, float a );
+ GFX_API void ImmediateTexCoordAll( float x, float y, float z );
+ GFX_API void ImmediateTexCoord( int unit, float x, float y, float z );
+ GFX_API void ImmediateBegin( GfxPrimitiveType type );
+ GFX_API void ImmediateEnd();
+
+ GFX_API bool CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 );
+ GFX_API bool ReadbackImage( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY );
+ GFX_API void GrabIntoRenderTexture( RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height );
+
+ GFX_API void BeforeDrawCall( bool immediateMode );
+
+ GFX_API bool IsPositionRequiredForTexGen (int texStageIndex) const { return false; }
+ GFX_API bool IsNormalRequiredForTexGen (int texStageIndex) const { return false; }
+ GFX_API bool IsPositionRequiredForTexGen() const { return false; }
+ GFX_API bool IsNormalRequiredForTexGen() const { return false; }
+
+ #if ENABLE_PROFILER
+ GFX_API void BeginProfileEvent (const char* name);
+ GFX_API void EndProfileEvent ();
+
+ GFX_API GfxTimerQuery* CreateTimerQuery();
+ GFX_API void DeleteTimerQuery(GfxTimerQuery* query);
+ GFX_API void BeginTimerQueries();
+ GFX_API void EndTimerQueries();
+ #endif // ENABLE_PROFILER
+
+ #if UNITY_EDITOR
+ GFX_API void SetAntiAliasFlag (bool aa);
+ GFX_API void DrawUserPrimitives (GfxPrimitiveType type, int vertexCount, UInt32 vertexChannels, const void* data, int stride);
+ GFX_API int GetCurrentTargetAA() const;
+ GFX_API GfxDeviceWindow* CreateGfxWindow (HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias);
+ #endif
+
+ GFX_API int GetCurrentTargetWidth() const;
+ GFX_API int GetCurrentTargetHeight() const;
+ GFX_API void SetCurrentTargetSize(int width, int height);
+ GFX_API void SetCurrentWindowSize(int width, int height);
+
+ GFX_API void* GetNativeGfxDevice();
+ GFX_API void* GetNativeTexturePointer(TextureID id);
+ GFX_API intptr_t CreateExternalTextureFromNative(intptr_t nativeTex);
+ GFX_API void UpdateExternalTextureFromNative(TextureID tex, intptr_t nativeTex);
+
+ GFX_API void SetComputeBufferData (ComputeBufferID bufferHandle, const void* data, size_t size);
+ GFX_API void GetComputeBufferData (ComputeBufferID bufferHandle, void* dest, size_t destSize);
+ GFX_API void CopyComputeBufferCount (ComputeBufferID srcBuffer, ComputeBufferID dstBuffer, UInt32 dstOffset);
+
+ GFX_API void SetRandomWriteTargetTexture (int index, TextureID tid);
+ GFX_API void SetRandomWriteTargetBuffer (int index, ComputeBufferID bufferHandle);
+ GFX_API void ClearRandomWriteTargets ();
+
+ GFX_API ComputeProgramHandle CreateComputeProgram (const UInt8* code, size_t codeSize);
+ GFX_API void DestroyComputeProgram (ComputeProgramHandle& cpHandle);
+ GFX_API void CreateComputeConstantBuffers (unsigned count, const UInt32* sizes, ConstantBufferHandle* outCBs);
+ GFX_API void DestroyComputeConstantBuffers (unsigned count, ConstantBufferHandle* cbs);
+ GFX_API void CreateComputeBuffer (ComputeBufferID id, size_t count, size_t stride, UInt32 flags);
+ GFX_API void DestroyComputeBuffer (ComputeBufferID handle);
+ GFX_API void UpdateComputeConstantBuffers (unsigned count, ConstantBufferHandle* cbs, UInt32 cbDirty, size_t dataSize, const UInt8* data, const UInt32* cbSizes, const UInt32* cbOffsets, const int* bindPoints);
+ GFX_API void UpdateComputeResources (
+ unsigned texCount, const TextureID* textures, const int* texBindPoints,
+ unsigned samplerCount, const unsigned* samplers,
+ unsigned inBufferCount, const ComputeBufferID* inBuffers, const int* inBufferBindPoints,
+ unsigned outBufferCount, const ComputeBufferID* outBuffers, const TextureID* outTextures, const UInt32* outBufferBindPoints);
+ GFX_API void DispatchComputeProgram (ComputeProgramHandle cpHandle, unsigned threadsX, unsigned threadsY, unsigned threadsZ);
+
+ GFX_API void DrawNullGeometry (GfxPrimitiveType topology, int vertexCount, int instanceCount);
+ GFX_API void DrawNullGeometryIndirect (GfxPrimitiveType topology, ComputeBufferID bufferHandle, UInt32 bufferOffset);
+
+ ID3D11Buffer* GetAllWhiteVertexStream();
+ VertexDeclarationsD3D11& GetVertexDecls() { return m_VertexDecls; }
+ ConstantBuffersD3D11& GetConstantBuffers() { return m_CBs; }
+ TexturesD3D11& GetTextures() { return m_Textures; }
+ void SetComputeBuffer11 (ShaderType shaderType, int unit, ComputeBufferID bufferHandle);
+
+private:
+ void SetupDeferredDepthStencilState();
+ void SetupDeferredRasterState();
+ void SetupDeferredSRGBWrite();
+
+ void DrawQuad (float u0, float v0, float u1, float v1, float z, ID3D11ShaderResourceView* texture);
+ bool ImmediateEndSetup();
+ void ImmediateEndDraw();
+
+public:
+ ImmediateModeD3D11 m_Imm;
+ TransformState m_TransformState;
+
+ ConstantBuffersD3D11 m_CBs;
+ VertexDeclarationsD3D11 m_VertexDecls;
+ TexturesD3D11 m_Textures;
+ DynamicD3D11VBO* m_DynamicVBO;
+
+ FFProgramCacheD3D11 m_FFPrograms;
+ FixedFunctionStateD3D11 m_FFState;
+
+ CachedBlendStates m_CachedBlendStates;
+ CachedDepthStates m_CachedDepthStates;
+ CachedStencilStates m_CachedStencilStates;
+ CachedRasterStates m_CachedRasterStates;
+ CachedFinalRasterStates m_CachedFinalRasterStates;
+ CachedDepthStencilStates m_CachedDepthStencilStates;
+
+ TextureUnitState11 m_TextureUnits[kMaxSupportedTextureUnits];
+ TextureID m_ActiveTextures[kShaderTypeCount][kMaxSupportedTextureUnits];
+ TextureID m_ActiveSamplers[kShaderTypeCount][kMaxSupportedTextureUnits];
+
+ GpuProgram* m_ActiveGpuProgram[kShaderTypeCount];
+ const GpuProgramParameters* m_ActiveGpuProgramParams[kShaderTypeCount];
+
+ void* m_ActiveShaders[kShaderTypeCount];
+
+ const DeviceBlendState* m_CurrBlendState;
+ const DeviceRasterState* m_CurrRasterState;
+ const DeviceDepthState* m_CurrDepthState;
+ const DeviceStencilState* m_CurrStencilState;
+ ID3D11RasterizerState* m_CurrRSState;
+ ID3D11DepthStencilState* m_CurrDSState;
+ int m_StencilRef;
+ int m_CurrStencilRef;
+
+ bool m_InvertProjMatrix;
+ bool m_AppBackfaceMode;
+ bool m_UserBackfaceMode;
+ bool m_Wireframe;
+ bool m_Scissor;
+ bool m_SRGBWrite;
+ bool m_ActualSRGBWrite;
+
+ int m_Viewport[4];
+ int m_ScissorRect[4];
+ int m_CurrTargetWidth;
+ int m_CurrTargetHeight;
+ int m_CurrWindowWidth;
+ int m_CurrWindowHeight;
+
+ ResolveTexturePool m_Resolves;
+};
+
+GfxDeviceD3D11& GetD3D11GfxDevice();
diff --git a/Runtime/GfxDevice/d3d11/GpuProgramsD3D11.cpp b/Runtime/GfxDevice/d3d11/GpuProgramsD3D11.cpp
new file mode 100644
index 0000000..0cb9143
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/GpuProgramsD3D11.cpp
@@ -0,0 +1,703 @@
+#include "UnityPrefix.h"
+#include "GpuProgramsD3D11.h"
+#include "ConstantBuffersD3D11.h"
+#include "D3D11Context.h"
+#include "D3D11Utils.h"
+#include "GfxDeviceD3D11.h"
+#include "ShaderGeneratorD3D11.h"
+#include "Runtime/Math/Vector4.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/ComputeShader.h"
+#include "ShaderGeneratorD3D11.h"
+#include "ShaderPatchingD3D11.h"
+#include "FixedFunctionStateD3D11.h"
+#include "External/shaderlab/Library/properties.h"
+
+
+
+ConstantBuffersD3D11& GetD3D11ConstantBuffers (GfxDevice& device);
+const InputSignatureD3D11* GetD3D11InputSignature (void* code, unsigned length);
+
+const InputSignatureD3D11* g_CurrentVSInputD3D11;
+
+
+static GpuProgramLevel DecodeShader (const std::string& source, dynamic_array<UInt8>& output)
+{
+ GpuProgramLevel level = kGpuProgramNone;
+
+ // decode shader
+ int startSkip = 0;
+ if (gGraphicsCaps.d3d11.featureLevel < kDX11Level10_0)
+ {
+ if (strncmp(source.c_str()+1, "s_4_0_level_9_", strlen("s_4_0_level_9_")) == 0)
+ {
+ startSkip = strlen("vs_4_0_level_9_x") + 1;
+ level = kGpuProgramSM3;
+ }
+ Assert ("Unsupported shader found!");
+ }
+ else if (strncmp(source.c_str()+1, "s_dx11", 6) == 0)
+ {
+ startSkip = 7;
+ level = kGpuProgramSM4;
+ }
+ else if (strncmp(source.c_str()+1, "s_4_0", 5) == 0)
+ {
+ startSkip = 6;
+ level = kGpuProgramSM4;
+ }
+ else if (strncmp(source.c_str()+1, "s_5_0", 5) == 0)
+ {
+ startSkip = 6;
+ level = kGpuProgramSM5;
+ }
+ else
+ {
+ Assert ("Unknown shader prefix");
+ }
+ int sourceSize = source.size() - startSkip;
+ const char* sourcePtr = source.c_str() + startSkip;
+
+ output.reserve (sourceSize / 2);
+ int i = 0;
+ while (i < sourceSize)
+ {
+ char c1 = sourcePtr[i];
+ if (c1 >= 'a')
+ {
+ AssertIf (i+1 == sourceSize);
+ char c2 = sourcePtr[i+1];
+ output.push_back ((c1-'a') * 16 + (c2-'a'));
+ i += 2;
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+ // debug check: does our shader hashing code match D3Ds?
+ #if !UNITY_RELEASE
+ if (output.size() > 20)
+ {
+ void D3DHash (const unsigned char* data, unsigned size, unsigned char res[16]);
+ UInt8 hsh[16];
+ D3DHash (&output[20], output.size()-20, hsh);
+ DebugAssert (0 == memcmp(hsh,&output[4],16));
+ }
+ #endif
+
+ // patch shader code to do driver workarounds
+ if (level < kGpuProgramSM4 && gGraphicsCaps.d3d11.buggyPartialPrecision10Level9)
+ {
+ PatchRemovePartialPrecisionD3D11 (output);
+ }
+
+ // patch shader code to do driver workarounds
+ if (level < kGpuProgramSM4 && gGraphicsCaps.d3d11.buggyPartialPrecision10Level9)
+ {
+ PatchRemovePartialPrecisionD3D11 (output);
+ }
+
+ return level;
+}
+
+static const UInt8* ApplyValueParameters11 (ConstantBuffersD3D11& cbs, const UInt8* buffer, const GpuProgramParameters::ValueParameterArray& valueParams, int cbIndex)
+{
+ GpuProgramParameters::ValueParameterArray::const_iterator valueParamsEnd = valueParams.end();
+ for (GpuProgramParameters::ValueParameterArray::const_iterator i = valueParams.begin(); i != valueParamsEnd; ++i)
+ {
+ if (i->m_RowCount == 1)
+ {
+ // Vector
+ const Vector4f* val = reinterpret_cast<const Vector4f*>(buffer);
+ if (i->m_Type != kShaderParamInt)
+ {
+ cbs.SetCBConstant (cbIndex, i->m_Index, val->GetPtr(), i->m_ColCount*4);
+ }
+ else
+ {
+ int vali[4] = {val->x, val->y, val->z, val->w};
+ cbs.SetCBConstant (cbIndex, i->m_Index, vali, i->m_ColCount*4);
+ }
+ buffer += sizeof(Vector4f);
+ }
+ else
+ {
+ // matrix/array
+ int size = *reinterpret_cast<const int*>(buffer); buffer += sizeof(int);
+ Assert (i->m_RowCount == 4 && size == 16);
+ const Matrix4x4f* val = reinterpret_cast<const Matrix4x4f*>(buffer);
+ cbs.SetCBConstant (cbIndex, i->m_Index, val->GetPtr(), 64);
+ buffer += size * sizeof(float);
+ }
+ }
+ return buffer;
+}
+
+static const UInt8* ApplyBufferParameters11 (GfxDevice& device, ShaderType shaderType, const UInt8* buffer, const GpuProgramParameters::BufferParameterArray& bufferParams)
+{
+ GfxDeviceD3D11& device11 = static_cast<GfxDeviceD3D11&>(device);
+
+ GpuProgramParameters::BufferParameterArray::const_iterator bufferParamsEnd = bufferParams.end();
+ for (GpuProgramParameters::BufferParameterArray::const_iterator i = bufferParams.begin(); i != bufferParamsEnd; ++i)
+ {
+ ComputeBufferID buf = *reinterpret_cast<const ComputeBufferID*>(buffer);
+ device11.SetComputeBuffer11 (shaderType, i->m_Index, buf);
+ buffer += sizeof(ComputeBufferID);
+ }
+ return buffer;
+}
+
+
+// --------------------------------------------------------------------------
+
+
+D3D11CommonShader::~D3D11CommonShader ()
+{
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ SAFE_RELEASE(m_Shaders[i]);
+ }
+}
+
+IUnknown* D3D11CommonShader::GetShader(FogMode fog, bool haveDomainShader, bool& outResetToNoFog)
+{
+ outResetToNoFog = false;
+ // no fog?
+ if (fog <= kFogDisabled)
+ return m_Shaders[0];
+
+ // already have shader for this fog mode?
+ Assert (fog >= 0 && fog < kFogModeCount);
+ if (m_Shaders[fog])
+ return m_Shaders[fog];
+
+ // can't do fog for this mode?
+ unsigned fogBit = (1<<fog);
+ if (m_FogFailed & fogBit)
+ {
+ outResetToNoFog = true;
+ return m_Shaders[0];
+ }
+
+ // have domain shader and we're vertex - nothing to do; fog delegated to domain one
+ if (haveDomainShader && m_ImplType == kShaderImplVertex)
+ return m_Shaders[0];
+
+ // patch shader to handle fog
+ bool ok = PatchShaderForFog (fog);
+ if (!ok)
+ {
+ m_FogFailed |= fogBit;
+ return m_Shaders[0];
+ }
+
+ Assert(m_Shaders[fog]);
+ return m_Shaders[fog];
+}
+
+bool D3D11CommonShader::PatchShaderForFog (FogMode fog)
+{
+ Assert (fog > kFogDisabled && fog < kFogModeCount);
+ Assert (!m_Shaders[fog]);
+ IUnknown* s = m_Shaders[0];
+ m_Shaders[fog] = s;
+ if (s)
+ s->AddRef();
+ return true;
+}
+
+
+const UInt8* D3D11CommonShader::ApplyTextures (GfxDevice& device, ShaderType shaderType, const GpuProgramParameters& params, const UInt8* buffer)
+{
+ const GpuProgramParameters::TextureParameterList& textureParams = params.GetTextureParams();
+ const GpuProgramParameters::TextureParameterList::const_iterator textureParamsEnd = textureParams.end();
+ for (GpuProgramParameters::TextureParameterList::const_iterator i = textureParams.begin(); i != textureParamsEnd; ++i)
+ {
+ const GpuProgramParameters::TextureParameter& t = *i;
+ const TexEnvData* texdata = reinterpret_cast<const TexEnvData*>(buffer);
+ device.SetTexture (shaderType, t.m_Index, t.m_SamplerIndex, texdata->textureID, static_cast<TextureDimension>(texdata->texDim), 0);
+ buffer += sizeof(*texdata);
+ }
+ return buffer;
+}
+
+
+// --------------------------------------------------------------------------
+
+D3D11VertexShader::D3D11VertexShader (const std::string& compiledSource)
+: m_InputSignature(NULL)
+{
+ m_ImplType = kShaderImplVertex;
+ if (!Create(compiledSource))
+ m_NotSupported = true;
+}
+
+D3D11VertexShader::~D3D11VertexShader ()
+{
+}
+
+
+bool D3D11VertexShader::Create (const std::string& compiledSource)
+{
+ m_GpuProgramLevel = DecodeShader (compiledSource, m_ByteCode);
+
+ m_InputSignature = GetD3D11InputSignature (&m_ByteCode[0], m_ByteCode.size());
+
+ HRESULT hr = GetD3D11Device()->CreateVertexShader (&m_ByteCode[0], m_ByteCode.size(), NULL, (ID3D11VertexShader**)&m_Shaders[0]);
+ if( FAILED(hr) )
+ {
+ printf_console ("D3D shader create error for shader %s\n", compiledSource.c_str());
+ return false;
+ }
+
+ std::string debugName = Format("VS-%d", compiledSource.size());
+ hr = ((ID3D11DeviceChild*)m_Shaders[0])->SetPrivateData (WKPDID_D3DDebugObjectName, debugName.size(), debugName.c_str());
+
+ return true;
+}
+
+bool D3D11VertexShader::PatchShaderForFog (FogMode fog)
+{
+ // no fog patching for 9.x level yet
+ if (m_GpuProgramLevel < kGpuProgramSM4)
+ return false;
+
+ dynamic_array<UInt8> bc = m_ByteCode;
+ bool ok = PatchVertexOrDomainShaderFogD3D11 (bc);
+ if (!ok)
+ {
+ printf_console("DX11: failed to patch vertex shader for fog mode %d\n", fog);
+ return false;
+ }
+
+ Assert (!m_Shaders[fog]);
+ HRESULT hr = GetD3D11Device()->CreateVertexShader (&bc[0], bc.size(), NULL, (ID3D11VertexShader**)&m_Shaders[fog]);
+ if (FAILED(hr))
+ {
+ printf_console ("D3D11 shader create error for VS with fog mode %i\n", fog);
+ return false;
+ }
+
+ SetDebugNameD3D11 ((ID3D11DeviceChild*)m_Shaders[fog], Format("VS-%d-fog-%d", (int)bc.size(), fog));
+ return true;
+}
+
+
+void D3D11VertexShader::ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer)
+{
+ g_CurrentVSInputD3D11 = m_InputSignature;
+
+ GfxDevice& device = GetRealGfxDevice();
+ ConstantBuffersD3D11& cbs = GetD3D11ConstantBuffers(device);
+ cbs.ResetBinds (kShaderVertex);
+
+ for (GpuProgramParameters::ConstantBufferList::const_iterator cbi = params.GetConstantBuffers().begin(); cbi != params.GetConstantBuffers().end(); ++cbi)
+ {
+ const int cbIndex = cbs.FindAndBindCB (cbi->m_Name.index, kShaderVertex, cbi->m_BindIndex, cbi->m_Size);
+ buffer = ApplyValueParameters11 (cbs, buffer, cbi->m_ValueParams, cbIndex);
+ }
+ buffer = ApplyTextures (device, kShaderVertex, params, buffer);
+ buffer = ApplyBufferParameters11 (device, kShaderVertex, buffer, params.GetBufferParams());
+}
+
+
+// --------------------------------------------------------------------------
+
+
+D3D11PixelShader::D3D11PixelShader (const std::string& compiledSource)
+{
+ m_ImplType = kShaderImplFragment;
+ if (!Create(compiledSource))
+ m_NotSupported = true;
+}
+
+
+bool D3D11PixelShader::Create (const std::string& compiledSource)
+{
+ m_GpuProgramLevel = DecodeShader (compiledSource, m_ByteCode);
+
+ HRESULT hr = GetD3D11Device()->CreatePixelShader (&m_ByteCode[0], m_ByteCode.size(), NULL, (ID3D11PixelShader**)&m_Shaders[0]);
+ if( FAILED(hr) )
+ {
+ printf_console ("D3D shader create error for shader %s\n", compiledSource.c_str());
+ return false;
+ }
+
+ std::string debugName = Format("PS-%d", compiledSource.size());
+ hr = ((ID3D11DeviceChild*)m_Shaders[0])->SetPrivateData (WKPDID_D3DDebugObjectName, debugName.size(), debugName.c_str());
+
+ return true;
+}
+
+
+bool D3D11PixelShader::PatchShaderForFog (FogMode fog)
+{
+ // no fog patching for 9.x level yet
+ if (m_GpuProgramLevel < kGpuProgramSM4)
+ return false;
+
+ dynamic_array<UInt8> bc = m_ByteCode;
+ bool ok = PatchPixelShaderFogD3D11 (bc, fog);
+ if (!ok)
+ {
+ printf_console("DX11: failed to patch pixel shader for fog mode %d\n", fog);
+ return false;
+ }
+
+ Assert (!m_Shaders[fog]);
+ HRESULT hr = GetD3D11Device()->CreatePixelShader (&bc[0], bc.size(), NULL, (ID3D11PixelShader**)&m_Shaders[fog]);
+ if (FAILED(hr))
+ {
+ printf_console ("D3D11 shader create error for PS with fog mode %i\n", fog);
+ return false;
+ }
+
+ SetDebugNameD3D11 ((ID3D11DeviceChild*)m_Shaders[fog], Format("PS-%d-fog-%d", (int)bc.size(), fog));
+
+ return true;
+}
+
+
+void D3D11PixelShader::ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer)
+{
+ GfxDevice& device = GetRealGfxDevice();
+ ConstantBuffersD3D11& cbs = GetD3D11ConstantBuffers(device);
+ cbs.ResetBinds (kShaderFragment);
+
+ for (GpuProgramParameters::ConstantBufferList::const_iterator cbi = params.GetConstantBuffers().begin(); cbi != params.GetConstantBuffers().end(); ++cbi)
+ {
+ const int cbIndex = cbs.FindAndBindCB (cbi->m_Name.index, kShaderFragment, cbi->m_BindIndex, cbi->m_Size);
+ buffer = ApplyValueParameters11 (cbs, buffer, cbi->m_ValueParams, cbIndex);
+ }
+
+ // Apply textures
+ const GpuProgramParameters::TextureParameterList& textureParams = params.GetTextureParams();
+ GpuProgramParameters::TextureParameterList::const_iterator textureParamsEnd = textureParams.end();
+ for( GpuProgramParameters::TextureParameterList::const_iterator i = textureParams.begin(); i != textureParamsEnd; ++i )
+ {
+ const GpuProgramParameters::TextureParameter& t = *i;
+ const TexEnvData* texdata = reinterpret_cast<const TexEnvData*>(buffer);
+ ApplyTexEnvData (t.m_Index, t.m_SamplerIndex, *texdata);
+ buffer += sizeof(*texdata);
+ }
+
+ buffer = ApplyBufferParameters11 (device, kShaderFragment, buffer, params.GetBufferParams());
+}
+
+
+// --------------------------------------------------------------------------
+
+D3D11GeometryShader::D3D11GeometryShader (const std::string& compiledSource)
+{
+ m_ImplType = kShaderImplGeometry;
+ if (!Create(compiledSource))
+ m_NotSupported = true;
+}
+
+
+bool D3D11GeometryShader::Create (const std::string& compiledSource)
+{
+ m_GpuProgramLevel = DecodeShader (compiledSource, m_ByteCode);
+
+ HRESULT hr = GetD3D11Device()->CreateGeometryShader (&m_ByteCode[0], m_ByteCode.size(), NULL, (ID3D11GeometryShader**)&m_Shaders[0]);
+ if( FAILED(hr) )
+ {
+ printf_console ("D3D shader create error for shader %s\n", compiledSource.c_str());
+ return false;
+ }
+
+ std::string debugName = Format("GS-%d", compiledSource.size());
+ hr = ((ID3D11DeviceChild*)m_Shaders[0])->SetPrivateData (WKPDID_D3DDebugObjectName, debugName.size(), debugName.c_str());
+
+ return true;
+}
+
+void D3D11GeometryShader::ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer)
+{
+ GfxDevice& device = GetRealGfxDevice();
+ ConstantBuffersD3D11& cbs = GetD3D11ConstantBuffers(device);
+ cbs.ResetBinds (kShaderGeometry);
+
+ for (GpuProgramParameters::ConstantBufferList::const_iterator cbi = params.GetConstantBuffers().begin(); cbi != params.GetConstantBuffers().end(); ++cbi)
+ {
+ const int cbIndex = cbs.FindAndBindCB (cbi->m_Name.index, kShaderGeometry, cbi->m_BindIndex, cbi->m_Size);
+ buffer = ApplyValueParameters11 (cbs, buffer, cbi->m_ValueParams, cbIndex);
+ }
+ buffer = ApplyTextures (device, kShaderGeometry, params, buffer);
+ buffer = ApplyBufferParameters11 (device, kShaderGeometry, buffer, params.GetBufferParams());
+}
+
+
+// --------------------------------------------------------------------------
+
+D3D11HullShader::D3D11HullShader (const std::string& compiledSource)
+{
+ m_ImplType = kShaderImplHull;
+ if (!Create(compiledSource))
+ m_NotSupported = true;
+}
+
+
+bool D3D11HullShader::Create (const std::string& compiledSource)
+{
+ m_GpuProgramLevel = DecodeShader (compiledSource, m_ByteCode);
+
+ HRESULT hr = GetD3D11Device()->CreateHullShader (&m_ByteCode[0], m_ByteCode.size(), NULL, (ID3D11HullShader**)&m_Shaders[0]);
+ if( FAILED(hr) )
+ {
+ printf_console ("D3D shader create error for shader %s\n", compiledSource.c_str());
+ return false;
+ }
+
+ std::string debugName = Format("HS-%d", compiledSource.size());
+ hr = ((ID3D11DeviceChild*)m_Shaders[0])->SetPrivateData (WKPDID_D3DDebugObjectName, debugName.size(), debugName.c_str());
+
+ return true;
+}
+
+void D3D11HullShader::ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer)
+{
+ GfxDevice& device = GetRealGfxDevice();
+ ConstantBuffersD3D11& cbs = GetD3D11ConstantBuffers(device);
+ cbs.ResetBinds (kShaderHull);
+
+ for (GpuProgramParameters::ConstantBufferList::const_iterator cbi = params.GetConstantBuffers().begin(); cbi != params.GetConstantBuffers().end(); ++cbi)
+ {
+ const int cbIndex = cbs.FindAndBindCB (cbi->m_Name.index, kShaderHull, cbi->m_BindIndex, cbi->m_Size);
+ buffer = ApplyValueParameters11 (cbs, buffer, cbi->m_ValueParams, cbIndex);
+ }
+ buffer = ApplyTextures (device, kShaderHull, params, buffer);
+ buffer = ApplyBufferParameters11 (device, kShaderHull, buffer, params.GetBufferParams());
+}
+
+
+// --------------------------------------------------------------------------
+
+D3D11DomainShader::D3D11DomainShader (const std::string& compiledSource)
+{
+ m_ImplType = kShaderImplDomain;
+ if (!Create(compiledSource))
+ m_NotSupported = true;
+}
+
+bool D3D11DomainShader::Create (const std::string& compiledSource)
+{
+ m_GpuProgramLevel = DecodeShader (compiledSource, m_ByteCode);
+
+ HRESULT hr = GetD3D11Device()->CreateDomainShader (&m_ByteCode[0], m_ByteCode.size(), NULL, (ID3D11DomainShader**)&m_Shaders[0]);
+ if( FAILED(hr) )
+ {
+ printf_console ("D3D shader create error for shader %s\n", compiledSource.c_str());
+ return false;
+ }
+
+ std::string debugName = Format("DS-%d", compiledSource.size());
+ hr = ((ID3D11DeviceChild*)m_Shaders[0])->SetPrivateData (WKPDID_D3DDebugObjectName, debugName.size(), debugName.c_str());
+
+ return true;
+}
+
+bool D3D11DomainShader::PatchShaderForFog (FogMode fog)
+{
+ // no fog patching for 9.x level yet
+ if (m_GpuProgramLevel < kGpuProgramSM4)
+ return false;
+
+ dynamic_array<UInt8> bc = m_ByteCode;
+ bool ok = PatchVertexOrDomainShaderFogD3D11 (bc);
+ if (!ok)
+ {
+ printf_console("DX11: failed to patch domain shader for fog mode %d\n", fog);
+ return false;
+ }
+
+ Assert (!m_Shaders[fog]);
+ HRESULT hr = GetD3D11Device()->CreateDomainShader (&bc[0], bc.size(), NULL, (ID3D11DomainShader**)&m_Shaders[fog]);
+ if (FAILED(hr))
+ {
+ printf_console ("D3D11 shader create error for DS with fog mode %i\n", fog);
+ return false;
+ }
+
+ SetDebugNameD3D11 ((ID3D11DeviceChild*)m_Shaders[fog], Format("DS-%d-fog-%d", (int)bc.size(), fog));
+ return true;
+}
+
+
+void D3D11DomainShader::ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer)
+{
+ GfxDevice& device = GetRealGfxDevice();
+ ConstantBuffersD3D11& cbs = GetD3D11ConstantBuffers(device);
+ cbs.ResetBinds (kShaderDomain);
+
+ for (GpuProgramParameters::ConstantBufferList::const_iterator cbi = params.GetConstantBuffers().begin(); cbi != params.GetConstantBuffers().end(); ++cbi)
+ {
+ const int cbIndex = cbs.FindAndBindCB (cbi->m_Name.index, kShaderDomain, cbi->m_BindIndex, cbi->m_Size);
+ buffer = ApplyValueParameters11 (cbs, buffer, cbi->m_ValueParams, cbIndex);
+ }
+ buffer = ApplyTextures (device, kShaderDomain, params, buffer);
+ buffer = ApplyBufferParameters11 (device, kShaderDomain, buffer, params.GetBufferParams());
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void FixedFunctionProgramD3D11::ValueParameters::ApplyValues (const BuiltinShaderParamValues& values, ConstantBuffersD3D11& cbs, ShaderType shaderType) const
+{
+ int cbBindIndex = cbs.FindAndBindCB (m_CBID, shaderType, 0, m_CBSize);
+
+ ValueParameterArray::const_iterator valueParamsEnd = m_Params.end();
+ for (ValueParameterArray::const_iterator i = m_Params.begin(); i != valueParamsEnd; ++i)
+ {
+ const Vector4f& val = values.GetVectorParam((BuiltinShaderVectorParam)i->m_Name);
+ cbs.SetCBConstant (cbBindIndex, i->m_Index, &val, i->m_Bytes);
+ }
+}
+
+
+// --------------------------------------------------------------------------
+
+
+static std::string GetFixedFunctionStateDesc (const FixedFunctionStateD3D11& state)
+{
+ std::string res;
+ if (state.lightingEnabled)
+ {
+ res += Format(" lighting with %i lights\n", state.lightCount);
+ }
+ res += Format(" combiners: %i\n", state.texUnitCount);
+ for (int i = 0; i < state.texUnitCount; ++i)
+ {
+ res += Format (" #%i: %08x %08x uv=%i %s %s\n",
+ i, state.texUnitColorCombiner[i], state.texUnitAlphaCombiner[i],
+ unsigned((state.texUnitSources>>(i*4))&0xF),
+ (state.texUnitCube&(1<<i))?"cube":"2d",
+ (state.texUnitProjected&(1<<i))?"projected":"");
+ }
+ res += state.useUniformInsteadOfVertexColor ? " color from uniform" : " color from VBO\n";
+ if (state.alphaTest != kFuncDisabled && state.alphaTest != kFuncAlways)
+ res += Format(" alpha test: %d\n", state.alphaTest);
+ return res;
+}
+
+#if UNITY_METRO_VS2013 || (UNITY_WIN && !UNITY_WINRT)
+extern bool HasD3D11Linker();
+extern void* BuildVertexShaderD3D11_Link(const FixedFunctionStateD3D11& state, FixedFunctionProgramD3D11::ValueParameters& params, BuiltinShaderParamIndices& matrices, size_t& outSize);
+extern void* BuildFragmentShaderD3D11_Link(const FixedFunctionStateD3D11& state, FixedFunctionProgramD3D11::ValueParameters& params, size_t& outSize);
+#endif
+
+FixedFunctionProgramD3D11::FixedFunctionProgramD3D11 (const FixedFunctionStateD3D11& state)
+: m_VS(NULL)
+, m_PS(NULL)
+, m_InputSig(NULL)
+{
+ size_t sizeVS, sizePS;
+
+ void* codeVS = NULL;
+ void* codePS = NULL;
+
+ // Generate using DX11 shader linker if available
+ #if UNITY_METRO_VS2013 || (UNITY_WIN && !UNITY_WINRT)
+ if (HasD3D11Linker())
+ {
+ codeVS = BuildVertexShaderD3D11_Link(state, m_VPParams, m_VPMatrices, sizeVS);
+ if (codeVS)
+ codePS = BuildFragmentShaderD3D11_Link(state, m_FPParams, sizePS);
+ }
+ #endif
+
+ // If linker failed or not available, generate raw hlsl bytecode
+ if (!codeVS || !codePS)
+ {
+ if (codeVS)
+ free(codeVS);
+ if (codePS)
+ free(codePS);
+ codeVS = BuildVertexShaderD3D11(state, m_VPParams, m_VPMatrices, sizeVS);
+ codePS = BuildFragmentShaderD3D11(state, m_FPParams, sizePS);
+ }
+
+ // Both generators failed, give up
+ if (!codeVS || !codePS)
+ {
+ ErrorString ("Failed to create fixed function shader pair");
+ if (codeVS)
+ free(codeVS);
+ if (codePS)
+ free(codePS);
+ return;
+ }
+
+ /*
+ // debug check: does our shader hashing code match D3Ds?
+ #if !UNITY_RELEASE
+ if (sizeVS > 20 && sizePS > 20)
+ {
+ void D3DHash (const unsigned char* data, unsigned size, unsigned char res[16]);
+ UInt8 hsh[16];
+ D3DHash (&codeVS[20], sizeVS-20, hsh);
+ DebugAssert (0 == memcmp(hsh,&codeVS[4],16));
+ D3DHash (&codePS[20], sizePS-20, hsh);
+ DebugAssert (0 == memcmp(hsh,&codePS[4],16));
+ }
+ // dump vertex shader code
+ DXBCContainer* dxbc = dxbc_parse (codeVS, sizeVS);
+ dxbc_print (dxbc);
+ delete dxbc;
+ #endif
+ */
+
+#if _DEBUG && !UNITY_METRO && 0
+ static int s_Num = 0;
+ char sz[1024];
+
+ sprintf(sz,"dump%04d.vs",s_Num);
+ FILE* f = fopen(sz,"wb");
+ fwrite(codeVS,sizeVS,1,f);
+ fclose(f);
+ sprintf(sz,"dump%04d.ps",s_Num);
+ f = fopen(sz,"wb");
+ fwrite(codePS,sizePS,1,f);
+ fclose(f);
+ s_Num++;
+#endif
+
+
+ ID3D11Device* dev = GetD3D11Device();
+ HRESULT hr;
+ hr = dev->CreateVertexShader (codeVS, sizeVS, NULL, &m_VS);
+ Assert (SUCCEEDED(hr));
+ hr = dev->CreatePixelShader (codePS, sizePS, NULL, &m_PS);
+ Assert (SUCCEEDED(hr));
+
+ std::string debugName = Format("FixedFunctionVS-%d", sizeVS);
+ hr = m_VS->SetPrivateData (WKPDID_D3DDebugObjectName, debugName.size(), debugName.c_str());
+ debugName = Format("FixedFunctionPS-%d", sizePS);
+ hr = m_PS->SetPrivateData (WKPDID_D3DDebugObjectName, debugName.size(), debugName.c_str());
+
+ m_InputSig = GetD3D11InputSignature (codeVS, sizeVS);
+
+ free(codeVS);
+ free(codePS);
+}
+
+FixedFunctionProgramD3D11::~FixedFunctionProgramD3D11 ()
+{
+ SAFE_RELEASE(m_VS);
+ SAFE_RELEASE(m_PS);
+}
+
+
+void FixedFunctionProgramD3D11::ApplyFFGpuProgram (const BuiltinShaderParamValues& values, ConstantBuffersD3D11& cbs) const
+{
+ g_CurrentVSInputD3D11 = m_InputSig;
+
+ cbs.ResetBinds (kShaderVertex);
+ cbs.ResetBinds (kShaderFragment);
+
+ m_VPParams.ApplyValues (values, cbs, kShaderVertex);
+ m_FPParams.ApplyValues (values, cbs, kShaderFragment);
+}
diff --git a/Runtime/GfxDevice/d3d11/GpuProgramsD3D11.h b/Runtime/GfxDevice/d3d11/GpuProgramsD3D11.h
new file mode 100644
index 0000000..305144b
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/GpuProgramsD3D11.h
@@ -0,0 +1,125 @@
+#pragma once
+
+#include "Runtime/GfxDevice/GpuProgram.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "D3D11Includes.h"
+
+class GfxDevice;
+
+class ConstantBuffersD3D11;
+struct FixedFunctionStateD3D11;
+struct InputSignatureD3D11;
+
+class D3D11CommonShader : public GpuProgram {
+public:
+ virtual ~D3D11CommonShader();
+ IUnknown* GetShader(FogMode fog, bool haveDomainShader, bool& outResetToNoFog);
+
+protected:
+ D3D11CommonShader ()
+ : m_FogFailed(0)
+ {
+ for (int i = 0; i < kFogModeCount; ++i)
+ m_Shaders[i] = NULL;
+ }
+
+ const UInt8* ApplyTextures (GfxDevice& device, ShaderType shaderType, const GpuProgramParameters& params, const UInt8* buffer);
+
+ // default implementation just uses non-fog shader
+ virtual bool PatchShaderForFog (FogMode fog);
+
+protected:
+ IUnknown* m_Shaders[kFogModeCount];
+ dynamic_array<UInt8> m_ByteCode; // stored for fog patching
+ UInt32 m_FogFailed;
+};
+
+class D3D11VertexShader : public D3D11CommonShader {
+public:
+ D3D11VertexShader (const std::string& compiledSource);
+ virtual ~D3D11VertexShader();
+ virtual void ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer);
+
+protected:
+ virtual bool PatchShaderForFog (FogMode fog);
+private:
+ bool Create (const std::string& compiledSource);
+ const InputSignatureD3D11* m_InputSignature;
+};
+
+class D3D11PixelShader : public D3D11CommonShader {
+public:
+ D3D11PixelShader (const std::string& compiledSource);
+ virtual void ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer);
+protected:
+ virtual bool PatchShaderForFog (FogMode fog);
+private:
+ bool Create (const std::string& compiledSource);
+};
+
+class D3D11GeometryShader : public D3D11CommonShader {
+public:
+ D3D11GeometryShader (const std::string& compiledSource);
+ virtual void ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer);
+private:
+ bool Create (const std::string& compiledSource);
+};
+
+class D3D11HullShader : public D3D11CommonShader {
+public:
+ D3D11HullShader (const std::string& compiledSource);
+ virtual void ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer);
+private:
+ bool Create (const std::string& compiledSource);
+};
+
+class D3D11DomainShader : public D3D11CommonShader {
+public:
+ D3D11DomainShader (const std::string& compiledSource);
+ virtual void ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer);
+protected:
+ virtual bool PatchShaderForFog (FogMode fog);
+private:
+ bool Create (const std::string& compiledSource);
+};
+
+
+class FixedFunctionProgramD3D11
+{
+public:
+ struct ValueParameter {
+ int m_Name;
+ int m_Index;
+ int m_Bytes;
+ ValueParameter (int n, int idx, int bytes) : m_Name(n), m_Index(idx), m_Bytes(bytes) {}
+ };
+ typedef dynamic_array<ValueParameter> ValueParameterArray;
+ struct ValueParameters {
+ ValueParameters() : m_CBID(0), m_CBSize(0) { }
+ void AddVectorParam (int index, int dim, BuiltinShaderVectorParam name) { m_Params.push_back (ValueParameter (name, index, dim*4)); }
+ bool HasVectorParams() const { return !m_Params.empty(); }
+ void ApplyValues (const BuiltinShaderParamValues& values, ConstantBuffersD3D11& cbs, ShaderType shaderType) const;
+ ValueParameterArray m_Params;
+ int m_CBID;
+ int m_CBSize;
+ };
+
+public:
+ FixedFunctionProgramD3D11 (const FixedFunctionStateD3D11& state);
+ ~FixedFunctionProgramD3D11 ();
+
+ void ApplyFFGpuProgram (const BuiltinShaderParamValues& values, ConstantBuffersD3D11& cbs) const;
+
+ const BuiltinShaderParamIndices& GetVPMatrices() const { return m_VPMatrices; }
+
+ ID3D11VertexShader* GetVertexShader() { return m_VS; }
+ ID3D11PixelShader* GetPixelShader() { return m_PS; }
+
+private:
+ ValueParameters m_VPParams;
+ ValueParameters m_FPParams;
+ BuiltinShaderParamIndices m_VPMatrices;
+ ID3D11VertexShader* m_VS;
+ ID3D11PixelShader* m_PS;
+ const InputSignatureD3D11* m_InputSig;
+};
diff --git a/Runtime/GfxDevice/d3d11/GraphicsCapsD3D11.cpp b/Runtime/GfxDevice/d3d11/GraphicsCapsD3D11.cpp
new file mode 100644
index 0000000..6ad8356
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/GraphicsCapsD3D11.cpp
@@ -0,0 +1,363 @@
+#include "UnityPrefix.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "D3D11Context.h"
+
+
+extern DXGI_FORMAT kD3D11RenderTextureFormats[kRTFormatCount];
+
+
+
+enum {
+ kVendorDummyRef = 0x0000,
+ kVendor3DLabs = 0x3d3d,
+ kVendorMatrox = 0x102b,
+ kVendorS3 = 0x5333,
+ kVendorSIS = 0x1039,
+ kVendorXGI = 0x18ca,
+ kVendorIntel = 0x8086,
+ kVendorATI = 0x1002,
+ kVendorNVIDIA = 0x10de,
+ kVendorTrident = 0x1023,
+ kVendorImgTech = 0x104a,
+ kVendorVIAS3G = 0x1106,
+ kVendor3dfx = 0x121a,
+ kVendorParallels= 0x1ab8,
+ kVendorMicrosoft= 0x1414,
+ kVendorVMWare = 0x15ad,
+ kVendorQualcomm = 0x4d4f4351,
+};
+
+enum {
+ kRendererIntel3150 = 0xa011, //Intel(R) Graphics Media Accelerator 3150 (Microsoft Corporation - WDDM 1.0)
+};
+struct KnownVendors {
+ DWORD vendorId;
+ const char* name;
+};
+static KnownVendors s_KnownVendors[] = {
+ { kVendorDummyRef, "REFERENCE" },
+ { kVendor3DLabs, "3dLabs" },
+ { kVendorMatrox, "Matrox" },
+ { kVendorS3, "S3" },
+ { kVendorSIS, "SIS" },
+ { kVendorXGI, "XGI" },
+ { kVendorIntel, "Intel" },
+ { kVendorATI, "ATI" },
+ { kVendorNVIDIA, "NVIDIA" },
+ { kVendorTrident, "Trident" },
+ { kVendorImgTech, "Imagination Technologies" },
+ { kVendorVIAS3G, "VIA/S3" },
+ { kVendor3dfx, "3dfx" },
+ { kVendorParallels, "Parallels" },
+ { kVendorMicrosoft, "Microsoft" },
+ { kVendorVMWare, "VMWare" },
+ { kVendorQualcomm, "Qualcomm" },
+};
+static int kKnownVendorsSize = sizeof(s_KnownVendors)/sizeof(s_KnownVendors[0]);
+
+DX11FeatureLevel kD3D11RenderTextureFeatureLevels[kRTFormatCount] = {
+ kDX11Level9_1, // ARGB32: 9.1
+ kDX11Level10_0, // Depth: 10.0
+ kDX11Level9_3, // ARGBHalf: 9.3
+ kDX11Level10_0, // Shadowmap: 10.0
+ kDX11LevelCount, // RGB565, unsupported
+ kDX11LevelCount, // ARGB4444, unsupported
+ kDX11LevelCount, // ARGB1555, unsupported
+ kDX11LevelCount, // Default
+ kDX11Level10_0, // A2RGB10: 10.0
+ kDX11LevelCount, // DefaultHDR
+ kDX11Level10_1, // ARGB64: 10.1
+ kDX11Level10_0, // ARGBFloat: 10.0
+ kDX11Level10_0, // RGFloat: 10.0
+ kDX11Level10_0, // RGHalf: 10.0
+ kDX11Level10_0, // RFloat: 10.0
+ kDX11Level10_0, // RHalf: 10.0
+ kDX11Level10_0, // R8: 10.0
+ kDX11Level10_0, // ARGBInt: 10.0
+ kDX11Level10_0, // RGInt: 10.0
+ kDX11Level10_0, // RInt: 10.0
+};
+
+
+
+void GraphicsCaps::InitD3D11()
+{
+ ID3D11Device* d3d = GetD3D11Device();
+ HRESULT hr;
+
+ // get device information
+#if UNITY_WINRT
+ DXGI_ADAPTER_DESC2 adapterDesc;
+
+ IDXGIDevice2* dxgiDevice2;
+ hr = d3d->QueryInterface(__uuidof(IDXGIDevice2), (void**)&dxgiDevice2);
+ IDXGIAdapter* dxgiAdapter;
+ IDXGIAdapter2* dxgiAdapter2;
+ dxgiDevice2->GetAdapter (&dxgiAdapter);
+ dxgiAdapter->QueryInterface(__uuidof(IDXGIAdapter2), (void**)&dxgiAdapter2);
+ dxgiAdapter2->GetDesc2 (&adapterDesc);
+ dxgiAdapter2->Release();
+ dxgiAdapter->Release();
+ dxgiDevice2->Release();
+#else
+ DXGI_ADAPTER_DESC adapterDesc;
+
+ IDXGIDevice* dxgiDevice;
+ IDXGIAdapter* dxgiAdapter;
+ hr = d3d->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice);
+ dxgiDevice->GetAdapter (&dxgiAdapter);
+ dxgiAdapter->GetDesc (&adapterDesc);
+ dxgiAdapter->Release();
+ dxgiDevice->Release();
+#endif
+ adapterDesc.Description[127] = 0;
+
+ char bufUtf8[1024];
+ WideCharToMultiByte (CP_UTF8, 0, adapterDesc.Description, -1, bufUtf8, 1024, NULL, NULL );
+ rendererString = bufUtf8;
+
+ int i;
+ for( i = 0; i < kKnownVendorsSize; ++i )
+ {
+ if( s_KnownVendors[i].vendorId == adapterDesc.VendorId )
+ {
+ vendorString = s_KnownVendors[i].name;
+ break;
+ }
+ }
+ if( i == kKnownVendorsSize )
+ {
+ vendorString = Format( "Unknown (ID=%x)", adapterDesc.VendorId );
+ }
+
+ vendorID = adapterDesc.VendorId;
+ rendererID = adapterDesc.DeviceId;
+
+ // No easy way to get driver information in DXGI...
+ driverLibraryString.clear();
+ driverVersionString.clear();
+
+ D3D_FEATURE_LEVEL d3dlevel = d3d->GetFeatureLevel();
+ DX11FeatureLevel level = kDX11Level9_1;
+ switch (d3dlevel) {
+ case D3D_FEATURE_LEVEL_9_1: level = kDX11Level9_1; break;
+ case D3D_FEATURE_LEVEL_9_2: level = kDX11Level9_2; break;
+ case D3D_FEATURE_LEVEL_9_3: level = kDX11Level9_3; break;
+ case D3D_FEATURE_LEVEL_10_0: level = kDX11Level10_0; break;
+ case D3D_FEATURE_LEVEL_10_1: level = kDX11Level10_1; break;
+ case D3D_FEATURE_LEVEL_11_0: level = kDX11Level11_0; break;
+ default: AssertString ("Unknown feature level");
+ }
+ d3d11.featureLevel = level;
+
+ fixedVersionString = Format("Direct3D 11.0 [level %i.%i]",
+ (d3dlevel & 0xF000) >> 12,
+ (d3dlevel & 0x0F00) >> 8);
+
+#if UNITY_WINRT
+ // Note: On Intel HD 4000, adapterDesc.DedicatedVideoMemory was returning 32 MB, which wasn't enough to even have on render texture on Surface Pro
+ // That's why there were bugs with shadows
+ // If you change something here, do check https://fogbugz.unity3d.com/default.asp?546560#1066534481 on Surface Pro, if it's still works
+ #if defined(__arm__)
+ const float kSaneMinVRAM = 64;
+ #else
+ const float kSaneMinVRAM = 128;
+ #endif
+ videoMemoryMB = (adapterDesc.DedicatedVideoMemory == 0) ? (adapterDesc.SharedSystemMemory / 2) : adapterDesc.DedicatedVideoMemory;
+ videoMemoryMB = std::max(videoMemoryMB / (1024 * 1024), kSaneMinVRAM);
+#if UNITY_METRO && defined(__arm__)
+ // tested on Tegra Tablet (Tegra T300?) and Surface (Tegra 3) - tested on 2013.02.20
+ gGraphicsCaps.buggyShadowMapBilinearSampling = true;
+#else
+ gGraphicsCaps.buggyShadowMapBilinearSampling = false;
+#endif
+#else
+ videoMemoryMB = adapterDesc.DedicatedVideoMemory / 1024 / 1024;
+#endif
+
+ // Output D3D info to console
+ printf_console( "Direct3D:\n" );
+ printf_console( " Version: %s\n", fixedVersionString.c_str() );
+ printf_console( " Renderer: %s (ID=0x%x)\n", rendererString.c_str(), rendererID);
+ printf_console( " Vendor: %s\n", vendorString.c_str() );
+ printf_console( " VRAM: %i MB\n", (int)videoMemoryMB );
+
+ needsToSwizzleVertexColors = false;
+ maxVSyncInterval = 4;
+ maxLights = 8;
+
+ // Texture sizes
+ static const int kTextureSizes[kDX11LevelCount] = {2048, 2048, 4096, 8192, 8192, 16384};
+ static const int kCubemapSizes[kDX11LevelCount] = { 512, 512, 4096, 8192, 8192, 16384};
+ maxTextureSize = kTextureSizes[level];
+ maxRenderTextureSize = maxTextureSize;
+ maxCubeMapSize = kCubemapSizes[level];
+
+ // Vertex/Fragment program parts
+ hasFixedFunction = true;
+#if UNITY_METRO
+ // Disable fixed function shaders on crappy devices
+ // One fix would be to rewrite byte code emitter to target 9.1 feature level instead of 9.3...
+ // For now, let's just disable it on all Intel devices with feature level < 9.3
+ if (vendorID == kVendorIntel && level < kDX11Level9_3) // Internal driver error occurs when first fixed function shader is loaded
+ {
+ printf_console("WARNING: Disabling fixed function shaders.\n");
+ hasFixedFunction = false;
+ }
+ else
+#endif
+ has3DTexture = true;
+ maxTexUnits = kMaxSupportedTextureUnits;
+ maxTexImageUnits = kMaxSupportedTextureUnits;
+ maxTexCoords = 8;
+
+ hasAnisoFilter = true;
+ static const int kMaxAniso[kDX11LevelCount] = {2, 16, 16, 16, 16, 16};
+ maxAnisoLevel = kMaxAniso[level];
+ hasMipLevelBias = true;
+
+ hasS3TCCompression = true;
+ npotRT = npot = (level >= kDX11Level10_0) ? kNPOTFull : kNPOTRestricted;
+
+ hasBlendSquare = true;
+ hasSeparateAlphaBlend = level > kDX11Level9_1;
+#if UNITY_WP8
+ hasSeparateAlphaBlend = level > kDX11Level9_3; // WP8 uses 9.3 feature level, but seems to not support separate alpha blending
+#endif
+ hasBlendSub = true;
+ hasBlendMinMax = true;
+
+ hasBlendLogicOps = false;
+ if(GetD3D11_1Device())
+ {
+ D3D11_FEATURE_DATA_D3D11_OPTIONS opt;
+
+ HRESULT hr = GetD3D11_1Device()->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, (void *)&opt, sizeof(opt));
+ if(!FAILED(hr))
+ {
+ hasBlendLogicOps = opt.OutputMergerLogicOp;
+ }
+
+ }
+
+ hasAutoMipMapGeneration = true;
+
+ #pragma message ("Properly implement supported formats!")
+ for (int q = 0; q < kTexFormatPCCount; ++q)
+ supportsTextureFormat[q] = true; //@TODO
+
+ if (level < kDX11Level9_3)
+ {
+ supportsTextureFormat[kTexFormatARGBFloat] = false;
+ supportsTextureFormat[kTexFormatRGB565] = false;
+ }
+ // 9.1 level doesn't really support R16 format. But we pretend we do, and then do unpacking
+ // into R8G8B8A8 on load.
+ supportsTextureFormat[kTexFormatAlphaLum16] = true;
+
+ hasRenderToTexture = true;
+ for (int i = 0; i < kRTFormatCount; ++i)
+ {
+ if (i == kRTFormatDefault || i == kRTFormatDefaultHDR)
+ continue;
+ supportsRenderTextureFormat[i] = (level >= kD3D11RenderTextureFeatureLevels[i]);
+ }
+
+#if UNITY_METRO // works on adreno 305 devices (nokia lumia 620) but not adreno 225 (nokia lumia 920 and samsung ativ s)
+ if (level < D3D_FEATURE_LEVEL_10_0)
+ {
+ D3D11_FEATURE_DATA_D3D9_SHADOW_SUPPORT shadowSupport;
+ memset(&shadowSupport, 0, sizeof(shadowSupport));
+
+ // Devices which don't support this:
+ // * Intel 3150
+ // D3D11 ERROR: ID3D11Device::CreatePixelShader: Shader uses comparision filtering with shader target ps_4_0_level_9_*, but the device does not support this . To check for support, call the CheckFeatureSupport() API with D3D11_FEATURE_D3D9_SHADOW_SUPPORT. The SupportsDepthAsTextureWithLessEqualComparisonFilter member indicates support for comparison filtering on level_9 shaders. [ STATE_CREATION ERROR #192: CREATEPIXELSHADER_INVALIDSHADERBYTECODE]
+ // First-chance exception at 0x74DD277C in New Unity Project 10.exe: Microsoft C++ exception: _com_error at memory location 0x0996ED90.
+ // Would be nice, if you could ignore such shaders if this feature is not supported.
+
+ d3d->CheckFeatureSupport(D3D11_FEATURE_D3D9_SHADOW_SUPPORT, &shadowSupport, sizeof(shadowSupport));
+ gGraphicsCaps.supportsRenderTextureFormat[kRTFormatDepth] = shadowSupport.SupportsDepthAsTextureWithLessEqualComparisonFilter;
+ gGraphicsCaps.supportsRenderTextureFormat[kRTFormatShadowMap] = shadowSupport.SupportsDepthAsTextureWithLessEqualComparisonFilter;
+ gGraphicsCaps.d3d11.hasShadows10Level9 = (shadowSupport.SupportsDepthAsTextureWithLessEqualComparisonFilter != 0);
+ }
+#else
+ gGraphicsCaps.d3d11.hasShadows10Level9 = false;
+#endif
+
+ // Looks like 9.x levels can't easily render into cubemaps:
+ // * DepthStencil texture must be a cubemap as well (otherwise can't set a cubemap color & regular 2D depth)
+ // * But a cubemap doesn't support any of the depth buffer formats
+ // A workaround could be to create 2D resources for each cubemap face, render into them, and then blit into a face
+ // of the final cubemap. Bah.
+ hasRenderToCubemap = (level >= kDX11Level10_0);
+
+ hasRenderTo3D = (level >= kDX11Level11_0);
+ hasStencil = true;
+ hasRenderTargetStencil = true;
+ hasTwoSidedStencil = true;
+ hasNativeDepthTexture = true;
+ hasStencilInDepthTexture = true;
+ hasNativeShadowMap = true;
+
+ hasSRGBReadWrite = true;
+
+ hasComputeShader = (level >= kDX11Level11_0); //@TODO: some sort of limited CS support in SM4?
+ hasInstancing = (level >= kDX11Level9_3);
+ hasNonFullscreenClear = false;
+
+ hasMultiSample = true;
+ d3d11.msaa = d3d11.msaaSRGB = 0;
+ static const int kAASampleTests[] = { 2, 4, 8 };
+ for (int i = 0; i < ARRAY_SIZE(kAASampleTests); ++i)
+ {
+ int samples = kAASampleTests[i];
+ UInt32 mask = 1<<samples;
+ UINT levels = 0;
+ hr = d3d->CheckMultisampleQualityLevels (DXGI_FORMAT_R8G8B8A8_UNORM, samples, &levels);
+ if (SUCCEEDED(hr) && levels > 0)
+ d3d11.msaa |= mask;
+ d3d->CheckMultisampleQualityLevels (DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, samples, &levels);
+ if (SUCCEEDED(hr) && levels > 0)
+ d3d11.msaaSRGB |= mask;
+ }
+
+
+ // Crashes on ARM tablets
+#if defined(__arm__) && UNITY_WINRT
+ hasTimerQuery = false;
+#else
+ // In theory just doing a query create with NULL destination, like
+ // CreateQuery(&disjointQueryDesc, NULL) == S_FALSE
+ // should be enough. But this makes NVIDIA NSight 2.1 crash, so do full query creation.
+ const D3D11_QUERY_DESC disjointQueryDesc = { D3D11_QUERY_TIMESTAMP_DISJOINT, 0 };
+ const D3D11_QUERY_DESC timestampQueryDesc = { D3D11_QUERY_TIMESTAMP, 0 };
+ ID3D11Query *query1 = NULL, *query2 = NULL;
+ hasTimerQuery = true;
+ hasTimerQuery &= SUCCEEDED(d3d->CreateQuery(&disjointQueryDesc, &query1));
+ hasTimerQuery &= SUCCEEDED(d3d->CreateQuery(&timestampQueryDesc, &query2));
+ SAFE_RELEASE(query1);
+ SAFE_RELEASE(query2);
+#endif
+
+ static const int kMRTCount[kDX11LevelCount] = { 1, 1, 4, 8, 8, 8 };
+ maxMRTs = std::min<int> (kMRTCount[level], kMaxSupportedRenderTargets);
+
+ // in the very end, figure out shader capabilities level (after all workarounds are applied)
+ static const ShaderCapsLevel kShaderLevels[kDX11LevelCount] = {kShaderLevel2, kShaderLevel2, kShaderLevel2, kShaderLevel4, kShaderLevel4_1, kShaderLevel5};
+ shaderCaps = kShaderLevels[level];
+
+ // Looks like mipmapped cubemaps & 3D textures are broken on 9.x level;
+ // can't update proper faces via CopySubresourceRegion / UpdateSubresource.
+ buggyMipmappedCubemaps = buggyMipmapped3DTextures = (level < kDX11Level10_0);
+
+ d3d11.buggyPartialPrecision10Level9 = false;
+ if (level < kDX11Level10_0)
+ {
+ // Lumia 620 crashes on partial precision (on WP8 as of 2013 March), with renderer
+ // string "Qualcomm Adreno 305 (WDDM v1.2)". Let's try removing partial precision
+ // on all Adreno 3xx.
+ if (rendererString.find("Adreno 3") != std::string::npos)
+ d3d11.buggyPartialPrecision10Level9 = true;
+ }
+}
diff --git a/Runtime/GfxDevice/d3d11/InternalShaders/CompileShaderLib/CompileShaderLib.cpp b/Runtime/GfxDevice/d3d11/InternalShaders/CompileShaderLib/CompileShaderLib.cpp
new file mode 100644
index 0000000..f25090b
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/InternalShaders/CompileShaderLib/CompileShaderLib.cpp
@@ -0,0 +1,185 @@
+// This is a small program to precompile hlsl library to a header file,
+// used only for fixed function shader generation with d3d11 linker.
+// It also generates 8 * 4 combinations of texture sampling functions.
+//
+// Useful because fxc doesn't support lib_* targets,
+// but once it does it's probably a good idea to deprecate this.
+
+#include <windows.h>
+#include <d3d11.h>
+#include <d3dcommon.h>
+#include <d3dcompiler.h>
+
+#include <cstdio>
+#include <cassert>
+
+typedef HRESULT (WINAPI *D3DCompileFunc)(
+ const void* pSrcData,
+ unsigned long SrcDataSize,
+ const char* pFileName,
+ const D3D10_SHADER_MACRO* pDefines,
+ void* pInclude,
+ const char* pEntrypoint,
+ const char* pTarget,
+ unsigned int Flags1,
+ unsigned int Flags2,
+ ID3DBlob** ppCode,
+ ID3DBlob** ppErrorMsgs
+);
+
+const char* dllName = "D3DCompiler_47.dll";
+
+const char* readFile(const char* path)
+{
+ FILE* f = fopen(path, "rb");
+ if (!f)
+ return NULL;
+
+ fseek(f, 0, SEEK_END);
+ size_t s = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ char* out = (char*)malloc(s+1);
+ fread(out, s, 1, f);
+ out[s] = '\0';
+ fclose(f);
+ return out;
+}
+
+void printCompiledShader(ID3DBlob* shader, const char* name, FILE* out)
+{
+ const unsigned char* data = (const unsigned char*)shader->GetBufferPointer();
+ size_t size = (size_t)shader->GetBufferSize();
+
+ fprintf(out, "const BYTE %s[] = { \n", name);
+ for(int i = 0; i < size; ++i) {
+ fprintf(out, ((i==size-1) ? "%d" : "%d,"), (int)data[i]);
+ if((i+1) % 16 == 0)
+ fprintf(out, "\n");
+ }
+ fprintf(out, "\n};\n");
+}
+
+// type: 0 - 2d, 1 - 2d proj, 2 - 3d, 3 - cube
+const char* generateTextureSamplingLib(int type, int unit)
+{
+ const int maxSize = 2048;
+ static char hlsl[maxSize];
+
+ const char* textureTypes[] = { "Texture2D", "Texture2D", "Texture3D", "TextureCube" };
+ const char* coordTypes[] = {"float2", "float4", "float3", "float3" };
+ const char* coords[] = {"uv.xy", "uv.xy/uv.w", "uv.xyz", "uv.xyz" };
+
+ sprintf(hlsl,
+ "%s<float4> Tex%d : register(t%d);\n"
+ "SamplerState Smp%d : register(s%d);\n"
+ "export float4 LoadTex%d(%s uv) { return Tex%d.Sample(Smp%d, %s); }\n",
+ textureTypes[type], unit, unit, unit, unit, unit,
+ coordTypes[type], unit, unit, coords[type]
+ );
+
+ return hlsl;
+}
+
+const char* textureSamplingLibName(int type, int unit)
+{
+ static const char* typeStr[] = {"2D", "Proj", "3D", "Cube"};
+ static char name[32];
+ sprintf(name, "g_FFSampleTex%s%d", typeStr[type], unit);
+ return name;
+}
+
+// Since we can't bind multiple textures to the same texture register,
+// we'll have to generate 4 * 8 sampling functions and link the correct ones
+// at runtime.
+void printTextureSampling(D3DCompileFunc compileFunc, FILE* out)
+{
+ // Print compiled shaders
+ for (int type = 0; type < 4; ++type)
+ {
+ for (int unit = 0; unit < 8; ++unit)
+ {
+ const char* name = textureSamplingLibName(type, unit);
+ const char* hlsl = generateTextureSamplingLib(type, unit);
+
+ ID3DBlob* errors;
+ ID3DBlob* shader;
+ HRESULT hr = compileFunc(
+ hlsl, strlen(hlsl), name, NULL, NULL, NULL,
+ "lib_4_0_level_9_1", D3DCOMPILE_OPTIMIZATION_LEVEL3, 0, &shader, &errors
+ );
+
+ assert(SUCCEEDED(hr));
+
+ printCompiledShader(shader, name, out);
+
+ shader->Release();
+ }
+ }
+
+ // Print index to all shaders
+ fprintf(out, "const BYTE* g_FFSampleTexLib[] = { \n");
+ for (int type = 0; type < 4; ++type)
+ for (int unit = 0; unit < 8; ++unit)
+ fprintf(out, "%s,\n", textureSamplingLibName(type, unit));
+ fprintf(out, "};\n");
+
+ // Print sizes of all shaders
+ fprintf(out, "const size_t g_FFSampleTexLibSize[] = { \n");
+ for (int type = 0; type < 4; ++type)
+ for (int unit = 0; unit < 8; ++unit)
+ fprintf(out, "sizeof(%s),\n", textureSamplingLibName(type, unit));
+ fprintf(out, "};\n");
+}
+
+int main(int argc, const char** argv)
+{
+ if (argc != 3)
+ {
+ printf("Usage: CompileShaderLib.exe src.hlsl out.h\n");
+ return 1;
+ }
+
+ HMODULE dll = LoadLibraryA (dllName);
+ if (!dll)
+ {
+ printf("Can't load %s\n", dllName);
+ return 1;
+ }
+
+ D3DCompileFunc compileFunc = (D3DCompileFunc) GetProcAddress(dll, "D3DCompile");
+ if (!compileFunc)
+ {
+ printf("Can't get D3DCompile function address\n");
+ return 1;
+ }
+
+ const char* hlsl = readFile(argv[1]);
+
+ ID3DBlob* errors;
+ ID3DBlob* shader;
+ HRESULT hr = compileFunc(
+ hlsl, strlen(hlsl), argv[1], NULL, NULL, NULL,
+ "lib_4_0_level_9_1", D3DCOMPILE_OPTIMIZATION_LEVEL3, 0, &shader, &errors
+ );
+
+ if (FAILED(hr))
+ {
+ printf("Failed to compile, 0x%x:\n%s\n", hr, errors ? errors->GetBufferPointer() : "");
+ return 1;
+ }
+
+ free((void*)hlsl);
+ FILE* out = fopen(argv[2], "w");
+
+ fprintf(out, "// File autogenerated by CompileShaderLib.exe\n");
+
+ printCompiledShader(shader, "g_FFShaderLibrary", out);
+ shader->Release();
+
+ printTextureSampling(compileFunc, out);
+
+ fclose(out);
+
+ return 0;
+}
+
diff --git a/Runtime/GfxDevice/d3d11/InternalShaders/CompileShaderLib/CompileShaderLib.exe b/Runtime/GfxDevice/d3d11/InternalShaders/CompileShaderLib/CompileShaderLib.exe
new file mode 100644
index 0000000..9452400
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/InternalShaders/CompileShaderLib/CompileShaderLib.exe
Binary files differ
diff --git a/Runtime/GfxDevice/d3d11/InternalShaders/CompileShaderLib/d3dcompiler_47.dll b/Runtime/GfxDevice/d3d11/InternalShaders/CompileShaderLib/d3dcompiler_47.dll
new file mode 100644
index 0000000..f8708d3
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/InternalShaders/CompileShaderLib/d3dcompiler_47.dll
Binary files differ
diff --git a/Runtime/GfxDevice/d3d11/InternalShaders/FFShaderLib.h b/Runtime/GfxDevice/d3d11/InternalShaders/FFShaderLib.h
new file mode 100644
index 0000000..cfb7617
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/InternalShaders/FFShaderLib.h
@@ -0,0 +1,11341 @@
+// File autogenerated by CompileShaderLib.exe
+const BYTE g_FFShaderLibrary[] = {
+68,88,66,67,53,175,93,57,187,158,181,204,26,193,132,124,
+173,199,196,230,1,0,0,0,85,80,2,0,83,0,0,0,
+108,1,0,0,248,3,0,0,176,10,0,0,204,18,0,0,
+32,27,0,0,140,29,0,0,136,32,0,0,60,36,0,0,
+240,42,0,0,164,49,0,0,88,56,0,0,4,64,0,0,
+20,68,0,0,40,72,0,0,100,87,0,0,88,105,0,0,
+104,124,0,0,192,140,0,0,116,157,0,0,204,173,0,0,
+4,192,0,0,92,208,0,0,24,228,0,0,112,244,0,0,
+176,9,1,0,8,26,1,0,204,48,1,0,36,65,1,0,
+72,89,1,0,160,105,1,0,200,108,1,0,232,116,1,0,
+164,119,1,0,96,122,1,0,224,124,1,0,0,132,1,0,
+24,140,1,0,48,148,1,0,72,156,1,0,96,164,1,0,
+232,166,1,0,112,169,1,0,248,171,1,0,128,174,1,0,
+72,179,1,0,48,182,1,0,248,185,1,0,192,189,1,0,
+136,193,1,0,80,197,1,0,24,201,1,0,224,204,1,0,
+168,208,1,0,112,212,1,0,68,215,1,0,36,218,1,0,
+4,221,1,0,140,223,1,0,160,226,1,0,172,229,1,0,
+64,233,1,0,88,236,1,0,20,240,1,0,72,244,1,0,
+84,248,1,0,228,251,1,0,120,255,1,0,140,3,2,0,
+44,6,2,0,8,9,2,0,72,12,2,0,96,15,2,0,
+88,23,2,0,116,31,2,0,136,39,2,0,156,44,2,0,
+28,47,2,0,92,51,2,0,172,55,2,0,236,59,2,0,
+48,64,2,0,132,68,2,0,196,72,2,0,76,73,66,70,
+132,2,0,0,68,88,66,67,97,223,247,81,192,181,190,245,
+62,154,200,24,62,42,44,199,1,0,0,0,132,2,0,0,
+6,0,0,0,56,0,0,0,132,0,0,0,208,0,0,0,
+16,1,0,0,140,1,0,0,0,2,0,0,65,111,110,57,
+68,0,0,0,68,0,0,0,0,2,86,76,32,0,0,0,
+36,0,0,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,2,86,76,31,0,0,2,
+5,0,0,128,0,0,15,144,1,0,0,2,0,0,15,224,
+0,0,228,144,255,255,0,0,65,111,110,57,68,0,0,0,
+68,0,0,0,0,2,80,76,32,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,15,176,1,0,0,2,0,0,15,224,0,0,228,176,
+255,255,0,0,83,72,68,82,56,0,0,0,64,0,240,255,
+14,0,0,0,95,0,0,3,242,16,16,0,0,0,0,0,
+101,0,0,3,242,32,16,0,0,0,0,0,54,0,0,5,
+242,32,16,0,0,0,0,0,70,30,16,0,0,0,0,0,
+62,0,0,1,83,84,65,84,116,0,0,0,2,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,108,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+60,0,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,171,171,76,70,83,48,124,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,120,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,86,101,114,116,101,120,67,111,
+108,111,114,0,118,99,0,171,76,73,66,70,176,6,0,0,
+68,88,66,67,193,45,92,32,38,130,119,195,162,50,75,220,
+113,111,147,34,1,0,0,0,176,6,0,0,6,0,0,0,
+56,0,0,0,132,0,0,0,208,0,0,0,24,1,0,0,
+148,1,0,0,88,6,0,0,65,111,110,57,68,0,0,0,
+68,0,0,0,0,2,86,76,20,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,0,0,8,0,1,0,0,0,0,0,0,0,
+0,2,86,76,1,0,0,2,0,0,15,224,0,0,228,160,
+255,255,0,0,65,111,110,57,68,0,0,0,68,0,0,0,
+0,2,80,76,20,0,0,0,48,0,0,0,1,0,36,0,
+0,0,48,0,0,0,48,0,0,0,36,0,0,0,48,0,
+0,0,8,0,1,0,0,0,0,0,0,0,0,2,80,76,
+1,0,0,2,0,0,15,224,0,0,228,160,255,255,0,0,
+83,72,68,82,64,0,0,0,64,0,240,255,16,0,0,0,
+89,0,0,4,70,142,32,0,0,0,0,0,9,0,0,0,
+101,0,0,3,242,32,16,0,0,0,0,0,54,0,0,6,
+242,32,16,0,0,0,0,0,70,142,32,0,0,0,0,0,
+8,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,188,4,0,0,1,0,0,0,
+108,0,0,0,1,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,142,4,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,85,110,105,116,121,70,70,86,
+101,114,116,101,120,0,171,171,92,0,0,0,14,0,0,0,
+132,0,0,0,240,3,0,0,0,0,0,0,0,0,0,0,
+180,2,0,0,0,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,240,2,0,0,64,0,0,0,
+64,0,0,0,0,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+253,2,0,0,128,0,0,0,16,0,0,0,2,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,56,3,0,0,144,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+71,3,0,0,160,0,0,0,128,0,0,0,0,0,0,0,
+88,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,124,3,0,0,32,1,0,0,
+128,0,0,0,0,0,0,0,140,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+176,3,0,0,160,1,0,0,128,0,0,0,0,0,0,0,
+192,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,228,3,0,0,32,2,0,0,
+128,0,0,0,0,0,0,0,244,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+24,4,0,0,160,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,39,4,0,0,176,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+54,4,0,0,192,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,66,4,0,0,208,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+82,4,0,0,224,2,0,0,0,1,0,0,0,0,0,0,
+96,4,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,132,4,0,0,224,3,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+102,102,95,109,97,116,114,105,120,95,109,118,112,0,102,108,
+111,97,116,52,120,52,0,171,3,0,3,0,4,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,194,2,0,0,102,102,95,109,
+97,116,114,105,120,95,109,118,0,102,102,95,118,101,99,95,
+99,111,108,111,114,0,102,108,111,97,116,52,0,171,171,171,
+1,0,3,0,1,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,118,101,99,95,97,109,98,105,101,
+110,116,0,102,102,95,108,105,103,104,116,95,99,111,108,111,
+114,0,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,112,111,115,0,171,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,97,116,116,101,110,0,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,115,112,111,116,0,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,109,97,116,95,100,105,102,102,117,
+115,101,0,102,102,95,109,97,116,95,97,109,98,105,101,110,
+116,0,102,102,95,109,97,116,95,115,112,101,99,0,102,102,
+95,109,97,116,95,101,109,105,115,115,105,111,110,0,102,102,
+95,109,97,116,114,105,120,95,116,101,120,0,3,0,3,0,
+4,0,4,0,4,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,194,2,0,0,
+102,102,95,102,111,103,95,118,115,0,77,105,99,114,111,115,
+111,102,116,32,40,82,41,32,72,76,83,76,32,83,104,97,
+100,101,114,32,67,111,109,112,105,108,101,114,32,54,46,51,
+46,57,52,49,53,46,48,0,76,70,83,48,80,0,0,0,
+1,0,0,0,8,0,0,0,56,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,76,111,97,100,86,101,114,116,
+101,120,67,111,108,111,114,85,110,105,102,111,114,109,0,171,
+76,73,66,70,20,8,0,0,68,88,66,67,29,191,205,137,
+232,73,220,57,32,180,48,231,114,9,102,236,1,0,0,0,
+20,8,0,0,6,0,0,0,56,0,0,0,208,0,0,0,
+116,1,0,0,80,2,0,0,204,2,0,0,144,7,0,0,
+65,111,110,57,144,0,0,0,144,0,0,0,0,2,86,76,
+96,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,0,0,4,0,
+4,0,0,0,0,0,0,0,0,2,86,76,31,0,0,2,
+5,0,0,128,0,0,15,144,5,0,0,3,0,0,7,128,
+0,0,85,144,1,0,228,160,4,0,0,4,0,0,7,128,
+0,0,228,160,0,0,0,144,0,0,228,128,4,0,0,4,
+0,0,7,128,2,0,228,160,0,0,170,144,0,0,228,128,
+4,0,0,4,0,0,7,224,3,0,228,160,0,0,255,144,
+0,0,228,128,255,255,0,0,65,111,110,57,156,0,0,0,
+156,0,0,0,0,2,80,76,108,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,0,0,4,0,4,0,0,0,0,0,0,0,
+0,2,80,76,31,0,0,2,0,0,0,128,0,0,15,176,
+5,0,0,3,0,0,7,128,0,0,85,176,1,0,228,160,
+4,0,0,4,0,0,7,128,0,0,228,160,0,0,0,176,
+0,0,228,128,4,0,0,4,0,0,7,128,2,0,228,160,
+0,0,170,176,0,0,228,128,4,0,0,4,0,0,7,128,
+3,0,228,160,0,0,255,176,0,0,228,128,1,0,0,2,
+0,0,7,224,0,0,228,128,255,255,0,0,83,72,68,82,
+212,0,0,0,64,0,240,255,53,0,0,0,89,0,0,4,
+70,142,32,0,0,0,0,0,8,0,0,0,95,0,0,3,
+242,16,16,0,0,0,0,0,101,0,0,3,114,32,16,0,
+0,0,0,0,104,0,0,2,1,0,0,0,56,0,0,8,
+114,0,16,0,0,0,0,0,86,21,16,0,0,0,0,0,
+70,130,32,0,0,0,0,0,5,0,0,0,50,0,0,10,
+114,0,16,0,0,0,0,0,70,130,32,0,0,0,0,0,
+4,0,0,0,6,16,16,0,0,0,0,0,70,2,16,0,
+0,0,0,0,50,0,0,10,114,0,16,0,0,0,0,0,
+70,130,32,0,0,0,0,0,6,0,0,0,166,26,16,0,
+0,0,0,0,70,2,16,0,0,0,0,0,50,0,0,10,
+114,32,16,0,0,0,0,0,70,130,32,0,0,0,0,0,
+7,0,0,0,246,31,16,0,0,0,0,0,70,2,16,0,
+0,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+5,0,0,0,1,0,0,0,0,0,0,0,2,0,0,0,
+4,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,188,4,0,0,1,0,0,0,
+108,0,0,0,1,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,142,4,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,85,110,105,116,121,70,70,86,
+101,114,116,101,120,0,171,171,92,0,0,0,14,0,0,0,
+132,0,0,0,240,3,0,0,0,0,0,0,0,0,0,0,
+180,2,0,0,0,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,240,2,0,0,64,0,0,0,
+64,0,0,0,2,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+253,2,0,0,128,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,56,3,0,0,144,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+71,3,0,0,160,0,0,0,128,0,0,0,0,0,0,0,
+88,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,124,3,0,0,32,1,0,0,
+128,0,0,0,0,0,0,0,140,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+176,3,0,0,160,1,0,0,128,0,0,0,0,0,0,0,
+192,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,228,3,0,0,32,2,0,0,
+128,0,0,0,0,0,0,0,244,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+24,4,0,0,160,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,39,4,0,0,176,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+54,4,0,0,192,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,66,4,0,0,208,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+82,4,0,0,224,2,0,0,0,1,0,0,0,0,0,0,
+96,4,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,132,4,0,0,224,3,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+102,102,95,109,97,116,114,105,120,95,109,118,112,0,102,108,
+111,97,116,52,120,52,0,171,3,0,3,0,4,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,194,2,0,0,102,102,95,109,
+97,116,114,105,120,95,109,118,0,102,102,95,118,101,99,95,
+99,111,108,111,114,0,102,108,111,97,116,52,0,171,171,171,
+1,0,3,0,1,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,118,101,99,95,97,109,98,105,101,
+110,116,0,102,102,95,108,105,103,104,116,95,99,111,108,111,
+114,0,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,112,111,115,0,171,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,97,116,116,101,110,0,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,115,112,111,116,0,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,109,97,116,95,100,105,102,102,117,
+115,101,0,102,102,95,109,97,116,95,97,109,98,105,101,110,
+116,0,102,102,95,109,97,116,95,115,112,101,99,0,102,102,
+95,109,97,116,95,101,109,105,115,115,105,111,110,0,102,102,
+95,109,97,116,114,105,120,95,116,101,120,0,3,0,3,0,
+4,0,4,0,4,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,194,2,0,0,
+102,102,95,102,111,103,95,118,115,0,77,105,99,114,111,115,
+111,102,116,32,40,82,41,32,72,76,83,76,32,83,104,97,
+100,101,114,32,67,111,109,112,105,108,101,114,32,54,46,51,
+46,57,52,49,53,46,48,0,76,70,83,48,124,0,0,0,
+2,0,0,0,8,0,0,0,104,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,115,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,76,111,97,100,69,121,101,80,
+111,115,0,118,101,114,116,101,120,0,171,171,76,73,66,70,
+76,8,0,0,68,88,66,67,108,68,195,40,106,156,44,20,
+245,155,105,169,96,77,47,194,1,0,0,0,76,8,0,0,
+6,0,0,0,56,0,0,0,232,0,0,0,132,1,0,0,
+132,2,0,0,0,3,0,0,196,7,0,0,65,111,110,57,
+168,0,0,0,168,0,0,0,0,2,86,76,120,0,0,0,
+48,0,0,0,1,0,36,0,0,0,48,0,0,0,48,0,
+0,0,36,0,0,0,48,0,0,0,4,0,3,0,0,0,
+0,0,0,0,0,2,86,76,31,0,0,2,5,0,0,128,
+0,0,15,144,5,0,0,3,0,0,7,128,0,0,85,144,
+1,0,228,160,4,0,0,4,0,0,7,128,0,0,228,160,
+0,0,0,144,0,0,228,128,4,0,0,4,0,0,7,128,
+2,0,228,160,0,0,170,144,0,0,228,128,8,0,0,3,
+0,0,8,128,0,0,228,128,0,0,228,128,7,0,0,2,
+0,0,8,128,0,0,255,128,5,0,0,3,0,0,7,224,
+0,0,255,128,0,0,228,128,255,255,0,0,65,111,110,57,
+148,0,0,0,148,0,0,0,0,2,80,76,100,0,0,0,
+48,0,0,0,1,0,36,0,0,0,48,0,0,0,48,0,
+0,0,36,0,0,0,48,0,0,0,4,0,3,0,0,0,
+0,0,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,7,176,5,0,0,3,0,0,7,128,0,0,85,176,
+1,0,228,160,4,0,0,4,0,0,7,128,0,0,228,160,
+0,0,0,176,0,0,228,128,4,0,0,4,0,0,7,128,
+2,0,228,160,0,0,170,176,0,0,228,128,36,0,0,2,
+1,0,7,128,0,0,228,128,1,0,0,2,0,0,7,224,
+1,0,228,128,255,255,0,0,83,72,68,82,248,0,0,0,
+64,0,240,255,62,0,0,0,89,0,0,4,70,142,32,0,
+0,0,0,0,7,0,0,0,95,0,0,3,114,16,16,0,
+0,0,0,0,101,0,0,3,114,32,16,0,0,0,0,0,
+104,0,0,2,1,0,0,0,56,0,0,8,114,0,16,0,
+0,0,0,0,86,21,16,0,0,0,0,0,70,130,32,0,
+0,0,0,0,5,0,0,0,50,0,0,10,114,0,16,0,
+0,0,0,0,70,130,32,0,0,0,0,0,4,0,0,0,
+6,16,16,0,0,0,0,0,70,2,16,0,0,0,0,0,
+50,0,0,10,114,0,16,0,0,0,0,0,70,130,32,0,
+0,0,0,0,6,0,0,0,166,26,16,0,0,0,0,0,
+70,2,16,0,0,0,0,0,16,0,0,7,130,0,16,0,
+0,0,0,0,70,2,16,0,0,0,0,0,70,2,16,0,
+0,0,0,0,68,0,0,5,130,0,16,0,0,0,0,0,
+58,0,16,0,0,0,0,0,56,0,0,7,114,32,16,0,
+0,0,0,0,246,15,16,0,0,0,0,0,70,2,16,0,
+0,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+7,0,0,0,1,0,0,0,0,0,0,0,2,0,0,0,
+6,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,188,4,0,0,1,0,0,0,
+108,0,0,0,1,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,142,4,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,85,110,105,116,121,70,70,86,
+101,114,116,101,120,0,171,171,92,0,0,0,14,0,0,0,
+132,0,0,0,240,3,0,0,0,0,0,0,0,0,0,0,
+180,2,0,0,0,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,240,2,0,0,64,0,0,0,
+64,0,0,0,2,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+253,2,0,0,128,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,56,3,0,0,144,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+71,3,0,0,160,0,0,0,128,0,0,0,0,0,0,0,
+88,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,124,3,0,0,32,1,0,0,
+128,0,0,0,0,0,0,0,140,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+176,3,0,0,160,1,0,0,128,0,0,0,0,0,0,0,
+192,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,228,3,0,0,32,2,0,0,
+128,0,0,0,0,0,0,0,244,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+24,4,0,0,160,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,39,4,0,0,176,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+54,4,0,0,192,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,66,4,0,0,208,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+82,4,0,0,224,2,0,0,0,1,0,0,0,0,0,0,
+96,4,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,132,4,0,0,224,3,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+102,102,95,109,97,116,114,105,120,95,109,118,112,0,102,108,
+111,97,116,52,120,52,0,171,3,0,3,0,4,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,194,2,0,0,102,102,95,109,
+97,116,114,105,120,95,109,118,0,102,102,95,118,101,99,95,
+99,111,108,111,114,0,102,108,111,97,116,52,0,171,171,171,
+1,0,3,0,1,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,118,101,99,95,97,109,98,105,101,
+110,116,0,102,102,95,108,105,103,104,116,95,99,111,108,111,
+114,0,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,112,111,115,0,171,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,97,116,116,101,110,0,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,115,112,111,116,0,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,109,97,116,95,100,105,102,102,117,
+115,101,0,102,102,95,109,97,116,95,97,109,98,105,101,110,
+116,0,102,102,95,109,97,116,95,115,112,101,99,0,102,102,
+95,109,97,116,95,101,109,105,115,115,105,111,110,0,102,102,
+95,109,97,116,114,105,120,95,116,101,120,0,3,0,3,0,
+4,0,4,0,4,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,194,2,0,0,
+102,102,95,102,111,103,95,118,115,0,77,105,99,114,111,115,
+111,102,116,32,40,82,41,32,72,76,83,76,32,83,104,97,
+100,101,114,32,67,111,109,112,105,108,101,114,32,54,46,51,
+46,57,52,49,53,46,48,0,76,70,83,48,128,0,0,0,
+2,0,0,0,8,0,0,0,104,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,118,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,76,111,97,100,69,121,101,78,
+111,114,109,97,108,0,110,111,114,109,97,108,0,171,171,171,
+76,73,66,70,100,2,0,0,68,88,66,67,13,191,80,101,
+135,3,145,74,141,73,23,109,153,81,118,66,1,0,0,0,
+100,2,0,0,6,0,0,0,56,0,0,0,144,0,0,0,
+232,0,0,0,40,1,0,0,164,1,0,0,24,2,0,0,
+65,111,110,57,80,0,0,0,80,0,0,0,0,2,86,76,
+44,0,0,0,36,0,0,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,2,86,76,
+81,0,0,5,0,0,15,160,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,2,0,0,7,224,
+0,0,0,160,255,255,0,0,65,111,110,57,80,0,0,0,
+80,0,0,0,0,2,80,76,44,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,80,76,81,0,0,5,0,0,15,160,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,2,0,0,7,224,0,0,0,160,255,255,0,0,
+83,72,68,82,56,0,0,0,64,0,240,255,14,0,0,0,
+101,0,0,3,114,32,16,0,0,0,0,0,54,0,0,8,
+114,32,16,0,0,0,0,0,2,64,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,60,0,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+77,105,99,114,111,115,111,102,116,32,40,82,41,32,72,76,
+83,76,32,83,104,97,100,101,114,32,67,111,109,112,105,108,
+101,114,32,54,46,51,46,57,52,49,53,46,48,0,171,171,
+76,70,83,48,68,0,0,0,1,0,0,0,8,0,0,0,
+56,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,2,0,0,0,
+255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,
+76,111,97,100,90,101,114,111,0,171,171,171,76,73,66,70,
+244,2,0,0,68,88,66,67,53,78,34,111,89,96,24,187,
+132,251,160,114,111,40,100,183,1,0,0,0,244,2,0,0,
+6,0,0,0,56,0,0,0,144,0,0,0,232,0,0,0,
+128,1,0,0,252,1,0,0,112,2,0,0,65,111,110,57,
+80,0,0,0,80,0,0,0,0,2,86,76,44,0,0,0,
+36,0,0,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,2,86,76,31,0,0,2,
+5,0,0,128,0,0,15,144,36,0,0,2,0,0,7,128,
+0,0,228,144,1,0,0,2,0,0,7,224,0,0,228,129,
+255,255,0,0,65,111,110,57,80,0,0,0,80,0,0,0,
+0,2,80,76,44,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,80,76,31,0,0,2,0,0,0,128,0,0,7,176,
+36,0,0,2,0,0,7,128,0,0,228,176,1,0,0,2,
+0,0,7,224,0,0,228,129,255,255,0,0,83,72,68,82,
+144,0,0,0,64,0,240,255,36,0,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,101,0,0,3,114,32,16,0,
+0,0,0,0,104,0,0,2,1,0,0,0,16,0,0,7,
+18,0,16,0,0,0,0,0,70,18,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,68,0,0,5,18,0,16,0,
+0,0,0,0,10,0,16,0,0,0,0,0,56,0,0,7,
+114,0,16,0,0,0,0,0,6,0,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,54,0,0,6,114,32,16,0,
+0,0,0,0,70,2,16,128,65,0,0,0,0,0,0,0,
+62,0,0,1,83,84,65,84,116,0,0,0,5,0,0,0,
+1,0,0,0,0,0,0,0,2,0,0,0,4,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,108,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+60,0,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,171,171,76,70,83,48,124,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,116,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,86,105,101,119,68,105,114,0,
+101,121,101,80,111,115,0,171,76,73,66,70,172,3,0,0,
+68,88,66,67,254,31,22,103,65,178,142,215,125,145,113,223,
+36,2,176,21,1,0,0,0,172,3,0,0,6,0,0,0,
+56,0,0,0,196,0,0,0,92,1,0,0,252,1,0,0,
+120,2,0,0,236,2,0,0,65,111,110,57,132,0,0,0,
+132,0,0,0,0,2,86,76,96,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,86,76,31,0,0,2,5,0,0,128,
+0,0,15,144,31,0,0,2,5,0,1,128,1,0,15,144,
+1,0,0,2,0,0,7,128,0,0,228,144,8,0,0,3,
+0,0,8,128,0,0,228,128,1,0,228,144,2,0,0,3,
+0,0,8,128,0,0,255,128,0,0,255,128,4,0,0,4,
+0,0,7,224,0,0,255,128,1,0,228,144,0,0,228,129,
+255,255,0,0,65,111,110,57,144,0,0,0,144,0,0,0,
+0,2,80,76,108,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,80,76,31,0,0,2,0,0,0,128,0,0,7,176,
+31,0,0,2,0,0,0,128,1,0,7,176,1,0,0,2,
+0,0,7,128,0,0,228,176,8,0,0,3,0,0,8,128,
+0,0,228,128,1,0,228,176,2,0,0,3,0,0,8,128,
+0,0,255,128,0,0,255,128,4,0,0,4,0,0,7,128,
+0,0,255,128,1,0,228,176,0,0,228,129,1,0,0,2,
+0,0,7,224,0,0,228,128,255,255,0,0,83,72,68,82,
+152,0,0,0,64,0,240,255,38,0,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,95,0,0,3,114,16,16,0,
+1,0,0,0,101,0,0,3,114,32,16,0,0,0,0,0,
+104,0,0,2,1,0,0,0,16,0,0,7,18,0,16,0,
+0,0,0,0,70,18,16,0,0,0,0,0,70,18,16,0,
+1,0,0,0,0,0,0,7,18,0,16,0,0,0,0,0,
+10,0,16,0,0,0,0,0,10,0,16,0,0,0,0,0,
+50,0,0,10,114,32,16,0,0,0,0,0,6,0,16,0,
+0,0,0,0,70,18,16,0,1,0,0,0,70,18,16,128,
+65,0,0,0,0,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,4,0,0,0,1,0,0,0,0,0,0,0,
+3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,108,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,60,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,77,105,99,114,
+111,115,111,102,116,32,40,82,41,32,72,76,83,76,32,83,
+104,97,100,101,114,32,67,111,109,112,105,108,101,114,32,54,
+46,51,46,57,52,49,53,46,48,0,171,171,76,70,83,48,
+184,0,0,0,3,0,0,0,8,0,0,0,152,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,2,0,0,0,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,164,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,172,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,76,111,97,100,
+69,121,101,82,101,102,108,0,118,105,101,119,68,105,114,0,
+101,121,101,78,111,114,109,97,108,0,171,171,76,73,66,70,
+172,6,0,0,68,88,66,67,200,162,94,107,60,63,213,125,
+45,223,136,185,236,153,37,63,1,0,0,0,172,6,0,0,
+6,0,0,0,56,0,0,0,132,0,0,0,208,0,0,0,
+24,1,0,0,148,1,0,0,88,6,0,0,65,111,110,57,
+68,0,0,0,68,0,0,0,0,2,86,76,20,0,0,0,
+48,0,0,0,1,0,36,0,0,0,48,0,0,0,48,0,
+0,0,36,0,0,0,48,0,0,0,43,0,1,0,0,0,
+0,0,0,0,0,2,86,76,1,0,0,2,0,0,15,224,
+0,0,228,160,255,255,0,0,65,111,110,57,68,0,0,0,
+68,0,0,0,0,2,80,76,20,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,0,0,43,0,1,0,0,0,0,0,0,0,
+0,2,80,76,1,0,0,2,0,0,15,224,0,0,228,160,
+255,255,0,0,83,72,68,82,64,0,0,0,64,0,240,255,
+16,0,0,0,89,0,0,4,70,142,32,0,0,0,0,0,
+44,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+54,0,0,6,242,32,16,0,0,0,0,0,70,142,32,0,
+0,0,0,0,43,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,188,4,0,0,
+1,0,0,0,108,0,0,0,1,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,142,4,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,92,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,85,110,105,116,
+121,70,70,86,101,114,116,101,120,0,171,171,92,0,0,0,
+14,0,0,0,132,0,0,0,240,3,0,0,0,0,0,0,
+0,0,0,0,180,2,0,0,0,0,0,0,64,0,0,0,
+0,0,0,0,204,2,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,240,2,0,0,
+64,0,0,0,64,0,0,0,0,0,0,0,204,2,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,253,2,0,0,128,0,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,56,3,0,0,
+144,0,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,71,3,0,0,160,0,0,0,128,0,0,0,
+0,0,0,0,88,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,124,3,0,0,
+32,1,0,0,128,0,0,0,0,0,0,0,140,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,176,3,0,0,160,1,0,0,128,0,0,0,
+0,0,0,0,192,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,228,3,0,0,
+32,2,0,0,128,0,0,0,0,0,0,0,244,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,24,4,0,0,160,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,39,4,0,0,
+176,2,0,0,16,0,0,0,2,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,54,4,0,0,192,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,66,4,0,0,
+208,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,82,4,0,0,224,2,0,0,0,1,0,0,
+0,0,0,0,96,4,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,132,4,0,0,
+224,3,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,102,102,95,109,97,116,114,105,120,95,109,118,
+112,0,102,108,111,97,116,52,120,52,0,171,3,0,3,0,
+4,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,194,2,0,0,
+102,102,95,109,97,116,114,105,120,95,109,118,0,102,102,95,
+118,101,99,95,99,111,108,111,114,0,102,108,111,97,116,52,
+0,171,171,171,1,0,3,0,1,0,4,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,118,101,99,95,97,
+109,98,105,101,110,116,0,102,102,95,108,105,103,104,116,95,
+99,111,108,111,114,0,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,112,111,115,0,171,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,97,116,116,101,110,0,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,115,112,111,
+116,0,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,109,97,116,95,100,
+105,102,102,117,115,101,0,102,102,95,109,97,116,95,97,109,
+98,105,101,110,116,0,102,102,95,109,97,116,95,115,112,101,
+99,0,102,102,95,109,97,116,95,101,109,105,115,115,105,111,
+110,0,102,102,95,109,97,116,114,105,120,95,116,101,120,0,
+3,0,3,0,4,0,4,0,4,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+194,2,0,0,102,102,95,102,111,103,95,118,115,0,77,105,
+99,114,111,115,111,102,116,32,40,82,41,32,72,76,83,76,
+32,83,104,97,100,101,114,32,67,111,109,112,105,108,101,114,
+32,54,46,51,46,57,52,49,53,46,48,0,76,70,83,48,
+76,0,0,0,1,0,0,0,8,0,0,0,56,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,2,0,0,0,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,76,111,97,100,
+65,109,98,105,101,110,116,67,111,108,111,114,0,171,171,171,
+76,73,66,70,172,6,0,0,68,88,66,67,146,119,135,218,
+29,151,113,93,178,158,120,136,238,224,122,235,1,0,0,0,
+172,6,0,0,6,0,0,0,56,0,0,0,132,0,0,0,
+208,0,0,0,24,1,0,0,148,1,0,0,88,6,0,0,
+65,111,110,57,68,0,0,0,68,0,0,0,0,2,86,76,
+20,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,0,0,42,0,
+1,0,0,0,0,0,0,0,0,2,86,76,1,0,0,2,
+0,0,15,224,0,0,228,160,255,255,0,0,65,111,110,57,
+68,0,0,0,68,0,0,0,0,2,80,76,20,0,0,0,
+48,0,0,0,1,0,36,0,0,0,48,0,0,0,48,0,
+0,0,36,0,0,0,48,0,0,0,42,0,1,0,0,0,
+0,0,0,0,0,2,80,76,1,0,0,2,0,0,15,224,
+0,0,228,160,255,255,0,0,83,72,68,82,64,0,0,0,
+64,0,240,255,16,0,0,0,89,0,0,4,70,142,32,0,
+0,0,0,0,43,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,54,0,0,6,242,32,16,0,0,0,0,0,
+70,142,32,0,0,0,0,0,42,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+188,4,0,0,1,0,0,0,108,0,0,0,1,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,142,4,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+85,110,105,116,121,70,70,86,101,114,116,101,120,0,171,171,
+92,0,0,0,14,0,0,0,132,0,0,0,240,3,0,0,
+0,0,0,0,0,0,0,0,180,2,0,0,0,0,0,0,
+64,0,0,0,0,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+240,2,0,0,64,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,253,2,0,0,128,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+56,3,0,0,144,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,71,3,0,0,160,0,0,0,
+128,0,0,0,0,0,0,0,88,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+124,3,0,0,32,1,0,0,128,0,0,0,0,0,0,0,
+140,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,176,3,0,0,160,1,0,0,
+128,0,0,0,0,0,0,0,192,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+228,3,0,0,32,2,0,0,128,0,0,0,0,0,0,0,
+244,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,24,4,0,0,160,2,0,0,
+16,0,0,0,2,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+39,4,0,0,176,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,54,4,0,0,192,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+66,4,0,0,208,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,82,4,0,0,224,2,0,0,
+0,1,0,0,0,0,0,0,96,4,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+132,4,0,0,224,3,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,102,102,95,109,97,116,114,105,
+120,95,109,118,112,0,102,108,111,97,116,52,120,52,0,171,
+3,0,3,0,4,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+194,2,0,0,102,102,95,109,97,116,114,105,120,95,109,118,
+0,102,102,95,118,101,99,95,99,111,108,111,114,0,102,108,
+111,97,116,52,0,171,171,171,1,0,3,0,1,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,118,
+101,99,95,97,109,98,105,101,110,116,0,102,102,95,108,105,
+103,104,116,95,99,111,108,111,114,0,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,112,111,115,0,171,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,97,116,116,
+101,110,0,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,115,112,111,116,0,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,109,
+97,116,95,100,105,102,102,117,115,101,0,102,102,95,109,97,
+116,95,97,109,98,105,101,110,116,0,102,102,95,109,97,116,
+95,115,112,101,99,0,102,102,95,109,97,116,95,101,109,105,
+115,115,105,111,110,0,102,102,95,109,97,116,114,105,120,95,
+116,101,120,0,3,0,3,0,4,0,4,0,4,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,194,2,0,0,102,102,95,102,111,103,95,118,
+115,0,77,105,99,114,111,115,111,102,116,32,40,82,41,32,
+72,76,83,76,32,83,104,97,100,101,114,32,67,111,109,112,
+105,108,101,114,32,54,46,51,46,57,52,49,53,46,48,0,
+76,70,83,48,76,0,0,0,1,0,0,0,8,0,0,0,
+56,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,2,0,0,0,
+255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,
+76,111,97,100,68,105,102,102,117,115,101,67,111,108,111,114,
+0,171,171,171,76,73,66,70,172,6,0,0,68,88,66,67,
+94,177,69,203,112,152,93,11,209,9,165,81,59,198,204,73,
+1,0,0,0,172,6,0,0,6,0,0,0,56,0,0,0,
+132,0,0,0,208,0,0,0,24,1,0,0,148,1,0,0,
+88,6,0,0,65,111,110,57,68,0,0,0,68,0,0,0,
+0,2,86,76,20,0,0,0,48,0,0,0,1,0,36,0,
+0,0,48,0,0,0,48,0,0,0,36,0,0,0,48,0,
+0,0,45,0,1,0,0,0,0,0,0,0,0,2,86,76,
+1,0,0,2,0,0,15,224,0,0,228,160,255,255,0,0,
+65,111,110,57,68,0,0,0,68,0,0,0,0,2,80,76,
+20,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,0,0,45,0,
+1,0,0,0,0,0,0,0,0,2,80,76,1,0,0,2,
+0,0,15,224,0,0,228,160,255,255,0,0,83,72,68,82,
+64,0,0,0,64,0,240,255,16,0,0,0,89,0,0,4,
+70,142,32,0,0,0,0,0,46,0,0,0,101,0,0,3,
+242,32,16,0,0,0,0,0,54,0,0,6,242,32,16,0,
+0,0,0,0,70,142,32,0,0,0,0,0,45,0,0,0,
+62,0,0,1,83,84,65,84,116,0,0,0,2,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,188,4,0,0,1,0,0,0,108,0,0,0,
+1,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+142,4,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,92,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,85,110,105,116,121,70,70,86,101,114,116,101,
+120,0,171,171,92,0,0,0,14,0,0,0,132,0,0,0,
+240,3,0,0,0,0,0,0,0,0,0,0,180,2,0,0,
+0,0,0,0,64,0,0,0,0,0,0,0,204,2,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,240,2,0,0,64,0,0,0,64,0,0,0,
+0,0,0,0,204,2,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,253,2,0,0,
+128,0,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,56,3,0,0,144,0,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,71,3,0,0,
+160,0,0,0,128,0,0,0,0,0,0,0,88,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,124,3,0,0,32,1,0,0,128,0,0,0,
+0,0,0,0,140,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,176,3,0,0,
+160,1,0,0,128,0,0,0,0,0,0,0,192,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,228,3,0,0,32,2,0,0,128,0,0,0,
+0,0,0,0,244,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,24,4,0,0,
+160,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,39,4,0,0,176,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,54,4,0,0,
+192,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,66,4,0,0,208,2,0,0,16,0,0,0,
+2,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,82,4,0,0,
+224,2,0,0,0,1,0,0,0,0,0,0,96,4,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,132,4,0,0,224,3,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,102,102,95,109,
+97,116,114,105,120,95,109,118,112,0,102,108,111,97,116,52,
+120,52,0,171,3,0,3,0,4,0,4,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,194,2,0,0,102,102,95,109,97,116,114,105,
+120,95,109,118,0,102,102,95,118,101,99,95,99,111,108,111,
+114,0,102,108,111,97,116,52,0,171,171,171,1,0,3,0,
+1,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,118,101,99,95,97,109,98,105,101,110,116,0,102,
+102,95,108,105,103,104,116,95,99,111,108,111,114,0,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,112,111,115,
+0,171,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,97,116,116,101,110,0,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,115,112,111,116,0,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,109,97,116,95,100,105,102,102,117,115,101,0,102,
+102,95,109,97,116,95,97,109,98,105,101,110,116,0,102,102,
+95,109,97,116,95,115,112,101,99,0,102,102,95,109,97,116,
+95,101,109,105,115,115,105,111,110,0,102,102,95,109,97,116,
+114,105,120,95,116,101,120,0,3,0,3,0,4,0,4,0,
+4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,194,2,0,0,102,102,95,102,
+111,103,95,118,115,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,76,0,0,0,1,0,0,0,
+8,0,0,0,56,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,76,111,97,100,69,109,105,115,115,105,111,110,
+67,111,108,111,114,0,171,171,76,73,66,70,164,7,0,0,
+68,88,66,67,15,22,159,155,21,14,178,159,161,27,46,101,
+18,246,102,242,1,0,0,0,164,7,0,0,6,0,0,0,
+56,0,0,0,176,0,0,0,52,1,0,0,164,1,0,0,
+32,2,0,0,228,6,0,0,65,111,110,57,112,0,0,0,
+112,0,0,0,0,2,86,76,64,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,0,0,9,0,1,0,0,0,0,0,0,0,
+0,2,86,76,31,0,0,2,5,0,0,128,0,0,15,144,
+31,0,0,2,5,0,1,128,1,0,15,144,1,0,0,2,
+0,0,7,128,1,0,228,144,4,0,0,4,0,0,7,224,
+0,0,228,128,0,0,228,160,0,0,228,144,255,255,0,0,
+65,111,110,57,124,0,0,0,124,0,0,0,0,2,80,76,
+76,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,0,0,9,0,
+1,0,0,0,0,0,0,0,0,2,80,76,31,0,0,2,
+0,0,0,128,0,0,7,176,31,0,0,2,0,0,0,128,
+1,0,7,176,1,0,0,2,0,0,7,128,1,0,228,176,
+4,0,0,4,0,0,7,128,0,0,228,128,0,0,228,160,
+0,0,228,176,1,0,0,2,0,0,7,224,0,0,228,128,
+255,255,0,0,83,72,68,82,104,0,0,0,64,0,240,255,
+26,0,0,0,89,0,0,4,70,142,32,0,0,0,0,0,
+10,0,0,0,95,0,0,3,114,16,16,0,0,0,0,0,
+95,0,0,3,114,16,16,0,1,0,0,0,101,0,0,3,
+114,32,16,0,0,0,0,0,50,0,0,10,114,32,16,0,
+0,0,0,0,70,18,16,0,1,0,0,0,70,130,32,0,
+0,0,0,0,9,0,0,0,70,18,16,0,0,0,0,0,
+62,0,0,1,83,84,65,84,116,0,0,0,2,0,0,0,
+0,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,188,4,0,0,1,0,0,0,108,0,0,0,
+1,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+142,4,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,92,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,85,110,105,116,121,70,70,86,101,114,116,101,
+120,0,171,171,92,0,0,0,14,0,0,0,132,0,0,0,
+240,3,0,0,0,0,0,0,0,0,0,0,180,2,0,0,
+0,0,0,0,64,0,0,0,0,0,0,0,204,2,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,240,2,0,0,64,0,0,0,64,0,0,0,
+0,0,0,0,204,2,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,253,2,0,0,
+128,0,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,56,3,0,0,144,0,0,0,16,0,0,0,
+2,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,71,3,0,0,
+160,0,0,0,128,0,0,0,0,0,0,0,88,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,124,3,0,0,32,1,0,0,128,0,0,0,
+0,0,0,0,140,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,176,3,0,0,
+160,1,0,0,128,0,0,0,0,0,0,0,192,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,228,3,0,0,32,2,0,0,128,0,0,0,
+0,0,0,0,244,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,24,4,0,0,
+160,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,39,4,0,0,176,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,54,4,0,0,
+192,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,66,4,0,0,208,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,82,4,0,0,
+224,2,0,0,0,1,0,0,0,0,0,0,96,4,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,132,4,0,0,224,3,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,102,102,95,109,
+97,116,114,105,120,95,109,118,112,0,102,108,111,97,116,52,
+120,52,0,171,3,0,3,0,4,0,4,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,194,2,0,0,102,102,95,109,97,116,114,105,
+120,95,109,118,0,102,102,95,118,101,99,95,99,111,108,111,
+114,0,102,108,111,97,116,52,0,171,171,171,1,0,3,0,
+1,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,118,101,99,95,97,109,98,105,101,110,116,0,102,
+102,95,108,105,103,104,116,95,99,111,108,111,114,0,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,112,111,115,
+0,171,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,97,116,116,101,110,0,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,115,112,111,116,0,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,109,97,116,95,100,105,102,102,117,115,101,0,102,
+102,95,109,97,116,95,97,109,98,105,101,110,116,0,102,102,
+95,109,97,116,95,115,112,101,99,0,102,102,95,109,97,116,
+95,101,109,105,115,115,105,111,110,0,102,102,95,109,97,116,
+114,105,120,95,116,101,120,0,3,0,3,0,4,0,4,0,
+4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,194,2,0,0,102,102,95,102,
+111,103,95,118,115,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,184,0,0,0,3,0,0,0,
+8,0,0,0,152,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,167,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,176,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,1,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,73,110,105,116,76,105,103,104,116,67,111,108,
+111,114,0,101,109,105,115,115,105,111,110,0,97,109,98,105,
+101,110,116,0,76,73,66,70,8,4,0,0,68,88,66,67,
+217,57,65,149,73,15,7,156,48,31,51,6,237,141,183,152,
+1,0,0,0,8,4,0,0,6,0,0,0,56,0,0,0,
+156,0,0,0,0,1,0,0,108,1,0,0,232,1,0,0,
+92,2,0,0,65,111,110,57,92,0,0,0,92,0,0,0,
+0,2,86,76,56,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,86,76,31,0,0,2,5,0,4,128,4,0,15,144,
+31,0,0,2,5,0,5,128,5,0,15,144,1,0,0,2,
+0,0,7,224,5,0,228,144,1,0,0,2,1,0,7,224,
+4,0,228,144,255,255,0,0,65,111,110,57,92,0,0,0,
+92,0,0,0,0,2,80,76,56,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,80,76,31,0,0,2,0,0,0,128,
+4,0,7,176,31,0,0,2,0,0,0,128,5,0,7,176,
+1,0,0,2,0,0,7,224,5,0,228,176,1,0,0,2,
+1,0,7,224,4,0,228,176,255,255,0,0,83,72,68,82,
+100,0,0,0,64,0,240,255,25,0,0,0,95,0,0,3,
+114,16,16,0,4,0,0,0,95,0,0,3,114,16,16,0,
+5,0,0,0,101,0,0,3,114,32,16,0,0,0,0,0,
+101,0,0,3,114,32,16,0,1,0,0,0,54,0,0,5,
+114,32,16,0,0,0,0,0,70,18,16,0,5,0,0,0,
+54,0,0,5,114,32,16,0,1,0,0,0,70,18,16,0,
+4,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,108,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,60,0,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,77,105,99,114,111,115,111,102,
+116,32,40,82,41,32,72,76,83,76,32,83,104,97,100,101,
+114,32,67,111,109,112,105,108,101,114,32,54,46,51,46,57,
+52,49,53,46,48,0,171,171,76,70,83,48,164,1,0,0,
+7,0,0,0,8,0,0,0,88,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,106,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,118,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,128,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,136,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,149,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,3,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,159,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,5,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,67,111,109,112,117,116,101,83,
+112,111,116,76,105,103,104,116,48,0,101,121,101,80,111,115,
+105,116,105,111,110,0,101,121,101,78,111,114,109,97,108,0,
+118,105,101,119,68,105,114,0,100,105,102,102,117,115,101,67,
+111,108,111,114,0,115,112,101,99,67,111,108,111,114,0,97,
+109,98,0,171,76,73,66,70,12,4,0,0,68,88,66,67,
+168,113,54,160,157,176,131,165,153,237,223,53,171,61,110,8,
+1,0,0,0,12,4,0,0,6,0,0,0,56,0,0,0,
+156,0,0,0,0,1,0,0,108,1,0,0,232,1,0,0,
+92,2,0,0,65,111,110,57,92,0,0,0,92,0,0,0,
+0,2,86,76,56,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,86,76,31,0,0,2,5,0,4,128,4,0,15,144,
+31,0,0,2,5,0,5,128,5,0,15,144,1,0,0,2,
+0,0,7,224,5,0,228,144,1,0,0,2,1,0,7,224,
+4,0,228,144,255,255,0,0,65,111,110,57,92,0,0,0,
+92,0,0,0,0,2,80,76,56,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,80,76,31,0,0,2,0,0,0,128,
+4,0,7,176,31,0,0,2,0,0,0,128,5,0,7,176,
+1,0,0,2,0,0,7,224,5,0,228,176,1,0,0,2,
+1,0,7,224,4,0,228,176,255,255,0,0,83,72,68,82,
+100,0,0,0,64,0,240,255,25,0,0,0,95,0,0,3,
+114,16,16,0,4,0,0,0,95,0,0,3,114,16,16,0,
+5,0,0,0,101,0,0,3,114,32,16,0,0,0,0,0,
+101,0,0,3,114,32,16,0,1,0,0,0,54,0,0,5,
+114,32,16,0,0,0,0,0,70,18,16,0,5,0,0,0,
+54,0,0,5,114,32,16,0,1,0,0,0,70,18,16,0,
+4,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,108,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,60,0,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,77,105,99,114,111,115,111,102,
+116,32,40,82,41,32,72,76,83,76,32,83,104,97,100,101,
+114,32,67,111,109,112,105,108,101,114,32,54,46,51,46,57,
+52,49,53,46,48,0,171,171,76,70,83,48,168,1,0,0,
+7,0,0,0,8,0,0,0,88,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,110,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,122,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,132,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,140,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,153,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,3,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,163,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,5,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,67,111,109,112,117,116,101,83,
+112,111,116,76,105,103,104,116,83,112,101,99,48,0,101,121,
+101,80,111,115,105,116,105,111,110,0,101,121,101,78,111,114,
+109,97,108,0,118,105,101,119,68,105,114,0,100,105,102,102,
+117,115,101,67,111,108,111,114,0,115,112,101,99,67,111,108,
+111,114,0,97,109,98,0,171,76,73,66,70,52,15,0,0,
+68,88,66,67,19,20,51,10,247,197,70,16,164,41,109,237,
+111,182,64,127,1,0,0,0,52,15,0,0,6,0,0,0,
+56,0,0,0,156,2,0,0,248,4,0,0,72,8,0,0,
+196,8,0,0,136,13,0,0,65,111,110,57,92,2,0,0,
+92,2,0,0,0,2,86,76,8,2,0,0,84,0,0,0,
+4,0,36,0,0,0,84,0,0,0,84,0,0,0,36,0,
+0,0,84,0,0,0,10,0,1,0,0,0,0,0,0,0,
+0,0,18,0,1,0,1,0,0,0,0,0,0,0,26,0,
+1,0,2,0,0,0,0,0,0,0,34,0,1,0,3,0,
+0,0,0,0,0,2,86,76,81,0,0,5,4,0,15,160,
+0,0,128,63,0,0,0,0,0,0,0,0,0,0,0,0,
+31,0,0,2,5,0,0,128,0,0,15,144,31,0,0,2,
+5,0,1,128,1,0,15,144,31,0,0,2,5,0,3,128,
+3,0,15,144,31,0,0,2,5,0,4,128,4,0,15,144,
+31,0,0,2,5,0,5,128,5,0,15,144,5,0,0,3,
+0,0,1,128,1,0,255,160,1,0,255,160,12,0,0,3,
+0,0,1,128,0,0,0,129,0,0,0,128,4,0,0,4,
+0,0,14,128,0,0,144,144,1,0,255,161,1,0,144,160,
+8,0,0,3,1,0,1,128,0,0,249,128,0,0,249,128,
+12,0,0,3,1,0,2,128,2,0,255,160,1,0,0,128,
+5,0,0,3,0,0,1,128,0,0,0,128,1,0,85,128,
+1,0,0,2,2,0,1,128,4,0,0,160,4,0,0,4,
+1,0,2,128,2,0,170,160,1,0,0,128,2,0,0,128,
+7,0,0,2,1,0,1,128,1,0,0,128,5,0,0,3,
+0,0,14,128,0,0,228,128,1,0,0,128,6,0,0,2,
+1,0,1,128,1,0,85,128,4,0,0,4,0,0,1,128,
+0,0,0,128,1,0,0,129,1,0,0,128,8,0,0,3,
+1,0,1,128,0,0,249,128,3,0,228,160,8,0,0,3,
+0,0,2,128,1,0,228,144,0,0,249,128,11,0,0,3,
+0,0,2,128,0,0,85,128,4,0,85,160,5,0,0,3,
+0,0,14,128,0,0,85,128,3,0,144,144,5,0,0,3,
+0,0,14,128,0,0,228,128,0,0,144,160,11,0,0,3,
+1,0,1,128,1,0,0,128,4,0,85,160,2,0,0,3,
+1,0,1,128,1,0,0,128,2,0,0,161,5,0,0,3,
+1,0,1,128,1,0,0,128,2,0,85,160,11,0,0,3,
+1,0,1,128,1,0,0,128,4,0,85,160,10,0,0,3,
+1,0,1,128,1,0,0,128,4,0,0,160,5,0,0,3,
+0,0,1,128,0,0,0,128,1,0,0,128,5,0,0,3,
+0,0,7,128,0,0,0,128,0,0,249,128,10,0,0,3,
+0,0,7,128,0,0,228,128,4,0,0,160,2,0,0,3,
+0,0,7,224,0,0,228,128,5,0,228,144,1,0,0,2,
+1,0,7,224,4,0,228,144,255,255,0,0,65,111,110,57,
+84,2,0,0,84,2,0,0,0,2,80,76,0,2,0,0,
+84,0,0,0,4,0,36,0,0,0,84,0,0,0,84,0,
+0,0,36,0,0,0,84,0,0,0,10,0,1,0,0,0,
+0,0,0,0,0,0,18,0,1,0,1,0,0,0,0,0,
+0,0,26,0,1,0,2,0,0,0,0,0,0,0,34,0,
+1,0,3,0,0,0,0,0,0,2,80,76,81,0,0,5,
+4,0,15,160,0,0,128,63,0,0,0,128,0,0,128,191,
+0,0,0,0,31,0,0,2,0,0,0,128,0,0,7,176,
+31,0,0,2,0,0,0,128,1,0,7,176,31,0,0,2,
+0,0,0,128,3,0,7,176,31,0,0,2,0,0,0,128,
+4,0,7,176,31,0,0,2,0,0,0,128,5,0,7,176,
+4,0,0,4,0,0,7,128,0,0,228,176,1,0,255,161,
+1,0,228,160,8,0,0,3,0,0,8,128,0,0,228,128,
+0,0,228,128,7,0,0,2,1,0,8,128,0,0,255,128,
+5,0,0,3,0,0,7,128,0,0,228,128,1,0,255,128,
+8,0,0,3,1,0,1,128,0,0,228,128,3,0,228,160,
+8,0,0,3,0,0,1,128,1,0,228,176,0,0,228,128,
+11,0,0,3,0,0,2,128,1,0,0,128,4,0,255,160,
+2,0,0,3,0,0,2,128,0,0,85,128,2,0,0,161,
+5,0,0,3,0,0,18,128,0,0,85,128,2,0,85,160,
+1,0,0,2,1,0,1,128,4,0,0,160,4,0,0,4,
+0,0,4,128,2,0,170,160,0,0,255,128,1,0,0,128,
+2,0,0,3,0,0,8,128,0,0,255,129,2,0,255,160,
+88,0,0,4,0,0,8,128,0,0,255,128,4,0,85,160,
+4,0,170,160,6,0,0,2,0,0,4,128,0,0,170,128,
+5,0,0,3,0,0,2,128,0,0,85,128,0,0,170,128,
+5,0,0,3,0,0,4,128,1,0,255,160,1,0,255,160,
+88,0,0,4,0,0,4,128,0,0,170,129,4,0,255,160,
+0,0,255,128,88,0,0,4,0,0,2,128,0,0,170,128,
+0,0,85,128,4,0,255,160,5,0,0,3,1,0,7,128,
+0,0,0,128,3,0,228,176,5,0,0,3,1,0,7,128,
+1,0,228,128,0,0,228,160,88,0,0,4,1,0,7,128,
+0,0,0,128,1,0,228,128,4,0,255,160,5,0,0,3,
+0,0,7,128,0,0,85,128,1,0,228,128,10,0,0,3,
+1,0,7,128,0,0,228,128,4,0,0,160,2,0,0,3,
+0,0,7,128,1,0,228,128,5,0,228,176,1,0,0,2,
+0,0,7,224,0,0,228,128,1,0,0,2,1,0,7,224,
+4,0,228,176,255,255,0,0,83,72,68,82,72,3,0,0,
+64,0,240,255,210,0,0,0,89,0,0,4,70,142,32,0,
+0,0,0,0,35,0,0,0,95,0,0,3,114,16,16,0,
+0,0,0,0,95,0,0,3,114,16,16,0,1,0,0,0,
+95,0,0,3,114,16,16,0,3,0,0,0,95,0,0,3,
+114,16,16,0,4,0,0,0,95,0,0,3,114,16,16,0,
+5,0,0,0,101,0,0,3,114,32,16,0,0,0,0,0,
+101,0,0,3,114,32,16,0,1,0,0,0,104,0,0,2,
+2,0,0,0,57,0,0,8,18,0,16,0,0,0,0,0,
+58,128,32,0,0,0,0,0,18,0,0,0,1,64,0,0,
+0,0,0,0,50,0,0,12,226,0,16,0,0,0,0,0,
+6,25,16,128,65,0,0,0,0,0,0,0,246,143,32,0,
+0,0,0,0,18,0,0,0,6,137,32,0,0,0,0,0,
+18,0,0,0,16,0,0,7,18,0,16,0,1,0,0,0,
+150,7,16,0,0,0,0,0,150,7,16,0,0,0,0,0,
+49,0,0,8,34,0,16,0,1,0,0,0,58,128,32,0,
+0,0,0,0,26,0,0,0,10,0,16,0,1,0,0,0,
+1,0,0,7,18,0,16,0,0,0,0,0,10,0,16,0,
+0,0,0,0,26,0,16,0,1,0,0,0,50,0,0,10,
+34,0,16,0,1,0,0,0,42,128,32,0,0,0,0,0,
+26,0,0,0,10,0,16,0,1,0,0,0,1,64,0,0,
+0,0,128,63,68,0,0,5,18,0,16,0,1,0,0,0,
+10,0,16,0,1,0,0,0,56,0,0,7,226,0,16,0,
+0,0,0,0,86,14,16,0,0,0,0,0,6,0,16,0,
+1,0,0,0,14,0,0,10,18,0,16,0,1,0,0,0,
+2,64,0,0,0,0,128,63,0,0,128,63,0,0,128,63,
+0,0,128,63,26,0,16,0,1,0,0,0,55,0,0,9,
+18,0,16,0,0,0,0,0,10,0,16,0,0,0,0,0,
+1,64,0,0,0,0,0,0,10,0,16,0,1,0,0,0,
+16,0,0,8,18,0,16,0,1,0,0,0,150,7,16,0,
+0,0,0,0,70,130,32,0,0,0,0,0,34,0,0,0,
+16,0,0,7,34,0,16,0,0,0,0,0,70,18,16,0,
+1,0,0,0,150,7,16,0,0,0,0,0,52,0,0,7,
+34,0,16,0,0,0,0,0,26,0,16,0,0,0,0,0,
+1,64,0,0,0,0,0,0,56,0,0,7,226,0,16,0,
+0,0,0,0,86,5,16,0,0,0,0,0,6,25,16,0,
+3,0,0,0,56,0,0,8,226,0,16,0,0,0,0,0,
+86,14,16,0,0,0,0,0,6,137,32,0,0,0,0,0,
+10,0,0,0,52,0,0,7,18,0,16,0,1,0,0,0,
+10,0,16,0,1,0,0,0,1,64,0,0,0,0,0,0,
+0,0,0,9,18,0,16,0,1,0,0,0,10,0,16,0,
+1,0,0,0,10,128,32,128,65,0,0,0,0,0,0,0,
+26,0,0,0,56,32,0,8,18,0,16,0,1,0,0,0,
+10,0,16,0,1,0,0,0,26,128,32,0,0,0,0,0,
+26,0,0,0,56,0,0,7,18,0,16,0,0,0,0,0,
+10,0,16,0,0,0,0,0,10,0,16,0,1,0,0,0,
+56,0,0,7,114,0,16,0,0,0,0,0,6,0,16,0,
+0,0,0,0,150,7,16,0,0,0,0,0,51,0,0,10,
+114,0,16,0,0,0,0,0,70,2,16,0,0,0,0,0,
+2,64,0,0,0,0,128,63,0,0,128,63,0,0,128,63,
+0,0,0,0,0,0,0,7,114,32,16,0,0,0,0,0,
+70,2,16,0,0,0,0,0,70,18,16,0,5,0,0,0,
+54,0,0,5,114,32,16,0,1,0,0,0,70,18,16,0,
+4,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+24,0,0,0,2,0,0,0,0,0,0,0,7,0,0,0,
+20,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,188,4,0,0,1,0,0,0,
+108,0,0,0,1,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,142,4,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,85,110,105,116,121,70,70,86,
+101,114,116,101,120,0,171,171,92,0,0,0,14,0,0,0,
+132,0,0,0,240,3,0,0,0,0,0,0,0,0,0,0,
+180,2,0,0,0,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,240,2,0,0,64,0,0,0,
+64,0,0,0,0,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+253,2,0,0,128,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,56,3,0,0,144,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+71,3,0,0,160,0,0,0,128,0,0,0,2,0,0,0,
+88,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,124,3,0,0,32,1,0,0,
+128,0,0,0,2,0,0,0,140,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+176,3,0,0,160,1,0,0,128,0,0,0,2,0,0,0,
+192,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,228,3,0,0,32,2,0,0,
+128,0,0,0,2,0,0,0,244,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+24,4,0,0,160,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,39,4,0,0,176,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+54,4,0,0,192,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,66,4,0,0,208,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+82,4,0,0,224,2,0,0,0,1,0,0,0,0,0,0,
+96,4,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,132,4,0,0,224,3,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+102,102,95,109,97,116,114,105,120,95,109,118,112,0,102,108,
+111,97,116,52,120,52,0,171,3,0,3,0,4,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,194,2,0,0,102,102,95,109,
+97,116,114,105,120,95,109,118,0,102,102,95,118,101,99,95,
+99,111,108,111,114,0,102,108,111,97,116,52,0,171,171,171,
+1,0,3,0,1,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,118,101,99,95,97,109,98,105,101,
+110,116,0,102,102,95,108,105,103,104,116,95,99,111,108,111,
+114,0,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,112,111,115,0,171,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,97,116,116,101,110,0,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,115,112,111,116,0,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,109,97,116,95,100,105,102,102,117,
+115,101,0,102,102,95,109,97,116,95,97,109,98,105,101,110,
+116,0,102,102,95,109,97,116,95,115,112,101,99,0,102,102,
+95,109,97,116,95,101,109,105,115,115,105,111,110,0,102,102,
+95,109,97,116,114,105,120,95,116,101,120,0,3,0,3,0,
+4,0,4,0,4,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,194,2,0,0,
+102,102,95,102,111,103,95,118,115,0,77,105,99,114,111,115,
+111,102,116,32,40,82,41,32,72,76,83,76,32,83,104,97,
+100,101,114,32,67,111,109,112,105,108,101,114,32,54,46,51,
+46,57,52,49,53,46,48,0,76,70,83,48,164,1,0,0,
+7,0,0,0,8,0,0,0,88,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,106,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,118,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,128,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,136,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,149,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,3,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,159,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,5,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,67,111,109,112,117,116,101,83,
+112,111,116,76,105,103,104,116,49,0,101,121,101,80,111,115,
+105,116,105,111,110,0,101,121,101,78,111,114,109,97,108,0,
+118,105,101,119,68,105,114,0,100,105,102,102,117,115,101,67,
+111,108,111,114,0,115,112,101,99,67,111,108,111,114,0,97,
+109,98,0,171,76,73,66,70,236,17,0,0,68,88,66,67,
+216,144,48,14,233,134,86,193,213,85,149,228,66,253,63,44,
+1,0,0,0,236,17,0,0,6,0,0,0,56,0,0,0,
+60,3,0,0,52,6,0,0,252,10,0,0,120,11,0,0,
+60,16,0,0,65,111,110,57,252,2,0,0,252,2,0,0,
+0,2,86,76,156,2,0,0,96,0,0,0,5,0,36,0,
+0,0,96,0,0,0,96,0,0,0,36,0,0,0,96,0,
+0,0,10,0,1,0,0,0,0,0,0,0,0,0,18,0,
+1,0,1,0,0,0,0,0,0,0,26,0,1,0,2,0,
+0,0,0,0,0,0,34,0,1,0,3,0,0,0,0,0,
+0,0,44,0,1,0,4,0,0,0,0,0,0,2,86,76,
+81,0,0,5,5,0,15,160,0,0,128,63,0,0,0,0,
+0,0,0,0,0,0,0,0,31,0,0,2,5,0,0,128,
+0,0,15,144,31,0,0,2,5,0,1,128,1,0,15,144,
+31,0,0,2,5,0,2,128,2,0,15,144,31,0,0,2,
+5,0,3,128,3,0,15,144,31,0,0,2,5,0,4,128,
+4,0,15,144,31,0,0,2,5,0,5,128,5,0,15,144,
+5,0,0,3,0,0,1,128,1,0,255,160,1,0,255,160,
+12,0,0,3,0,0,1,128,0,0,0,129,0,0,0,128,
+4,0,0,4,0,0,14,128,0,0,144,144,1,0,255,161,
+1,0,144,160,8,0,0,3,1,0,1,128,0,0,249,128,
+0,0,249,128,12,0,0,3,1,0,2,128,2,0,255,160,
+1,0,0,128,5,0,0,3,0,0,1,128,0,0,0,128,
+1,0,85,128,1,0,0,2,2,0,1,128,5,0,0,160,
+4,0,0,4,1,0,2,128,2,0,170,160,1,0,0,128,
+2,0,0,128,7,0,0,2,1,0,1,128,1,0,0,128,
+6,0,0,2,1,0,2,128,1,0,85,128,4,0,0,4,
+0,0,1,128,0,0,0,128,1,0,85,129,1,0,85,128,
+5,0,0,3,1,0,14,128,0,0,228,128,1,0,0,128,
+4,0,0,4,0,0,14,128,0,0,228,128,1,0,0,128,
+2,0,144,144,36,0,0,2,2,0,7,128,0,0,249,128,
+8,0,0,3,0,0,2,128,1,0,228,144,2,0,228,128,
+11,0,0,3,0,0,2,128,0,0,85,128,5,0,85,160,
+32,0,0,3,1,0,1,128,0,0,85,128,4,0,255,160,
+10,0,0,3,0,0,2,128,1,0,0,128,5,0,0,160,
+8,0,0,3,0,0,4,128,1,0,249,128,3,0,228,160,
+8,0,0,3,0,0,8,128,1,0,228,144,1,0,249,128,
+11,0,0,3,0,0,12,128,0,0,228,128,5,0,85,160,
+2,0,0,3,0,0,4,128,0,0,170,128,2,0,0,161,
+5,0,0,3,0,0,4,128,0,0,170,128,2,0,85,160,
+11,0,0,3,0,0,4,128,0,0,170,128,5,0,85,160,
+10,0,0,3,0,0,4,128,0,0,170,128,5,0,0,160,
+5,0,0,3,0,0,1,128,0,0,170,128,0,0,0,128,
+5,0,0,3,0,0,2,128,0,0,85,128,0,0,0,128,
+5,0,0,3,1,0,7,128,0,0,85,128,0,0,228,160,
+12,0,0,3,0,0,2,128,5,0,85,160,0,0,255,128,
+5,0,0,3,2,0,7,128,0,0,255,128,3,0,228,144,
+5,0,0,3,2,0,7,128,2,0,228,128,0,0,228,160,
+5,0,0,3,0,0,13,128,0,0,0,128,2,0,148,128,
+10,0,0,3,0,0,13,128,0,0,228,128,5,0,0,160,
+2,0,0,3,0,0,7,224,0,0,248,128,5,0,228,144,
+4,0,0,4,1,0,7,224,0,0,85,128,1,0,228,128,
+4,0,228,144,255,255,0,0,65,111,110,57,240,2,0,0,
+240,2,0,0,0,2,80,76,144,2,0,0,96,0,0,0,
+5,0,36,0,0,0,96,0,0,0,96,0,0,0,36,0,
+0,0,96,0,0,0,10,0,1,0,0,0,0,0,0,0,
+0,0,18,0,1,0,1,0,0,0,0,0,0,0,26,0,
+1,0,2,0,0,0,0,0,0,0,34,0,1,0,3,0,
+0,0,0,0,0,0,44,0,1,0,4,0,0,0,0,0,
+0,2,80,76,81,0,0,5,5,0,15,160,0,0,128,63,
+0,0,0,128,0,0,128,191,0,0,0,0,31,0,0,2,
+0,0,0,128,0,0,7,176,31,0,0,2,0,0,0,128,
+1,0,7,176,31,0,0,2,0,0,0,128,2,0,7,176,
+31,0,0,2,0,0,0,128,3,0,7,176,31,0,0,2,
+0,0,0,128,4,0,7,176,31,0,0,2,0,0,0,128,
+5,0,7,176,4,0,0,4,0,0,7,128,0,0,228,176,
+1,0,255,161,1,0,228,160,8,0,0,3,0,0,8,128,
+0,0,228,128,0,0,228,128,7,0,0,2,1,0,8,128,
+0,0,255,128,5,0,0,3,1,0,7,128,0,0,228,128,
+1,0,255,128,4,0,0,4,0,0,7,128,0,0,228,128,
+1,0,255,128,2,0,228,176,36,0,0,2,2,0,7,128,
+0,0,228,128,8,0,0,3,1,0,8,128,1,0,228,176,
+2,0,228,128,11,0,0,3,0,0,1,128,1,0,255,128,
+5,0,255,160,32,0,0,3,1,0,24,128,0,0,0,128,
+4,0,255,160,8,0,0,3,0,0,1,128,1,0,228,128,
+3,0,228,160,8,0,0,3,0,0,2,128,1,0,228,176,
+1,0,228,128,11,0,0,3,1,0,1,128,0,0,85,128,
+5,0,255,160,11,0,0,3,1,0,2,128,0,0,0,128,
+5,0,255,160,2,0,0,3,0,0,1,128,1,0,85,128,
+2,0,0,161,5,0,0,3,0,0,17,128,0,0,0,128,
+2,0,85,160,1,0,0,2,2,0,1,128,5,0,0,160,
+4,0,0,4,0,0,2,128,2,0,170,160,0,0,255,128,
+2,0,0,128,2,0,0,3,0,0,4,128,0,0,255,129,
+2,0,255,160,88,0,0,4,0,0,4,128,0,0,170,128,
+5,0,85,160,5,0,170,160,6,0,0,2,0,0,2,128,
+0,0,85,128,5,0,0,3,0,0,1,128,0,0,0,128,
+0,0,85,128,5,0,0,3,0,0,2,128,1,0,255,160,
+1,0,255,160,88,0,0,4,0,0,2,128,0,0,85,129,
+5,0,255,160,0,0,170,128,88,0,0,4,0,0,1,128,
+0,0,85,128,0,0,0,128,5,0,255,160,5,0,0,3,
+0,0,14,128,1,0,0,128,3,0,27,176,5,0,0,3,
+0,0,14,128,0,0,228,128,0,0,27,160,5,0,0,3,
+0,0,14,128,0,0,0,128,0,0,228,128,5,0,0,3,
+0,0,1,128,1,0,255,128,0,0,0,128,4,0,0,4,
+1,0,14,128,0,0,0,128,0,0,27,160,4,0,27,176,
+88,0,0,4,1,0,7,128,1,0,0,129,4,0,228,176,
+1,0,27,128,1,0,0,2,1,0,7,224,1,0,228,128,
+10,0,0,3,1,0,7,128,0,0,27,128,5,0,0,160,
+2,0,0,3,0,0,7,128,1,0,228,128,5,0,228,176,
+1,0,0,2,0,0,7,224,0,0,228,128,255,255,0,0,
+83,72,68,82,192,4,0,0,64,0,240,255,48,1,0,0,
+89,0,0,4,70,142,32,0,0,0,0,0,45,0,0,0,
+95,0,0,3,114,16,16,0,0,0,0,0,95,0,0,3,
+114,16,16,0,1,0,0,0,95,0,0,3,114,16,16,0,
+2,0,0,0,95,0,0,3,114,16,16,0,3,0,0,0,
+95,0,0,3,114,16,16,0,4,0,0,0,95,0,0,3,
+114,16,16,0,5,0,0,0,101,0,0,3,114,32,16,0,
+0,0,0,0,101,0,0,3,114,32,16,0,1,0,0,0,
+104,0,0,2,2,0,0,0,57,0,0,8,18,0,16,0,
+0,0,0,0,58,128,32,0,0,0,0,0,18,0,0,0,
+1,64,0,0,0,0,0,0,50,0,0,12,226,0,16,0,
+0,0,0,0,6,25,16,128,65,0,0,0,0,0,0,0,
+246,143,32,0,0,0,0,0,18,0,0,0,6,137,32,0,
+0,0,0,0,18,0,0,0,16,0,0,7,18,0,16,0,
+1,0,0,0,150,7,16,0,0,0,0,0,150,7,16,0,
+0,0,0,0,49,0,0,8,34,0,16,0,1,0,0,0,
+58,128,32,0,0,0,0,0,26,0,0,0,10,0,16,0,
+1,0,0,0,1,0,0,7,18,0,16,0,0,0,0,0,
+10,0,16,0,0,0,0,0,26,0,16,0,1,0,0,0,
+50,0,0,10,34,0,16,0,1,0,0,0,42,128,32,0,
+0,0,0,0,26,0,0,0,10,0,16,0,1,0,0,0,
+1,64,0,0,0,0,128,63,68,0,0,5,18,0,16,0,
+1,0,0,0,10,0,16,0,1,0,0,0,14,0,0,10,
+34,0,16,0,1,0,0,0,2,64,0,0,0,0,128,63,
+0,0,128,63,0,0,128,63,0,0,128,63,26,0,16,0,
+1,0,0,0,55,0,0,9,18,0,16,0,0,0,0,0,
+10,0,16,0,0,0,0,0,1,64,0,0,0,0,0,0,
+26,0,16,0,1,0,0,0,56,0,0,7,226,0,16,0,
+1,0,0,0,86,14,16,0,0,0,0,0,6,0,16,0,
+1,0,0,0,50,0,0,9,226,0,16,0,0,0,0,0,
+86,14,16,0,0,0,0,0,6,0,16,0,1,0,0,0,
+6,25,16,0,2,0,0,0,16,0,0,8,18,0,16,0,
+1,0,0,0,150,7,16,0,1,0,0,0,70,130,32,0,
+0,0,0,0,34,0,0,0,16,0,0,7,34,0,16,0,
+1,0,0,0,70,18,16,0,1,0,0,0,150,7,16,0,
+1,0,0,0,52,0,0,10,50,0,16,0,1,0,0,0,
+70,0,16,0,1,0,0,0,2,64,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,
+18,0,16,0,1,0,0,0,10,0,16,0,1,0,0,0,
+10,128,32,128,65,0,0,0,0,0,0,0,26,0,0,0,
+56,32,0,8,18,0,16,0,1,0,0,0,10,0,16,0,
+1,0,0,0,26,128,32,0,0,0,0,0,26,0,0,0,
+56,0,0,7,18,0,16,0,0,0,0,0,10,0,16,0,
+0,0,0,0,10,0,16,0,1,0,0,0,56,0,0,7,
+210,0,16,0,1,0,0,0,86,5,16,0,1,0,0,0,
+6,25,16,0,3,0,0,0,49,0,0,7,34,0,16,0,
+1,0,0,0,1,64,0,0,0,0,0,0,26,0,16,0,
+1,0,0,0,56,0,0,8,210,0,16,0,1,0,0,0,
+6,14,16,0,1,0,0,0,6,137,32,0,0,0,0,0,
+10,0,0,0,56,0,0,7,210,0,16,0,1,0,0,0,
+6,0,16,0,0,0,0,0,6,14,16,0,1,0,0,0,
+51,0,0,10,210,0,16,0,1,0,0,0,6,14,16,0,
+1,0,0,0,2,64,0,0,0,0,128,63,0,0,0,0,
+0,0,128,63,0,0,128,63,0,0,0,7,114,32,16,0,
+0,0,0,0,134,3,16,0,1,0,0,0,70,18,16,0,
+5,0,0,0,16,0,0,7,18,0,16,0,1,0,0,0,
+150,7,16,0,0,0,0,0,150,7,16,0,0,0,0,0,
+68,0,0,5,18,0,16,0,1,0,0,0,10,0,16,0,
+1,0,0,0,56,0,0,7,226,0,16,0,0,0,0,0,
+86,14,16,0,0,0,0,0,6,0,16,0,1,0,0,0,
+16,0,0,7,34,0,16,0,0,0,0,0,70,18,16,0,
+1,0,0,0,150,7,16,0,0,0,0,0,52,0,0,7,
+34,0,16,0,0,0,0,0,26,0,16,0,0,0,0,0,
+1,64,0,0,0,0,0,0,47,0,0,5,34,0,16,0,
+0,0,0,0,26,0,16,0,0,0,0,0,56,0,0,8,
+34,0,16,0,0,0,0,0,26,0,16,0,0,0,0,0,
+58,128,32,0,0,0,0,0,44,0,0,0,25,0,0,5,
+34,0,16,0,0,0,0,0,26,0,16,0,0,0,0,0,
+51,0,0,7,34,0,16,0,0,0,0,0,26,0,16,0,
+0,0,0,0,1,64,0,0,0,0,128,63,56,0,0,7,
+18,0,16,0,0,0,0,0,26,0,16,0,0,0,0,0,
+10,0,16,0,0,0,0,0,50,0,0,10,114,0,16,0,
+0,0,0,0,6,0,16,0,0,0,0,0,70,130,32,0,
+0,0,0,0,10,0,0,0,70,18,16,0,4,0,0,0,
+55,0,0,9,114,32,16,0,1,0,0,0,86,5,16,0,
+1,0,0,0,70,2,16,0,0,0,0,0,70,18,16,0,
+4,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+36,0,0,0,2,0,0,0,0,0,0,0,8,0,0,0,
+32,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,188,4,0,0,1,0,0,0,
+108,0,0,0,1,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,142,4,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,85,110,105,116,121,70,70,86,
+101,114,116,101,120,0,171,171,92,0,0,0,14,0,0,0,
+132,0,0,0,240,3,0,0,0,0,0,0,0,0,0,0,
+180,2,0,0,0,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,240,2,0,0,64,0,0,0,
+64,0,0,0,0,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+253,2,0,0,128,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,56,3,0,0,144,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+71,3,0,0,160,0,0,0,128,0,0,0,2,0,0,0,
+88,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,124,3,0,0,32,1,0,0,
+128,0,0,0,2,0,0,0,140,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+176,3,0,0,160,1,0,0,128,0,0,0,2,0,0,0,
+192,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,228,3,0,0,32,2,0,0,
+128,0,0,0,2,0,0,0,244,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+24,4,0,0,160,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,39,4,0,0,176,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+54,4,0,0,192,2,0,0,16,0,0,0,2,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,66,4,0,0,208,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+82,4,0,0,224,2,0,0,0,1,0,0,0,0,0,0,
+96,4,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,132,4,0,0,224,3,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+102,102,95,109,97,116,114,105,120,95,109,118,112,0,102,108,
+111,97,116,52,120,52,0,171,3,0,3,0,4,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,194,2,0,0,102,102,95,109,
+97,116,114,105,120,95,109,118,0,102,102,95,118,101,99,95,
+99,111,108,111,114,0,102,108,111,97,116,52,0,171,171,171,
+1,0,3,0,1,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,118,101,99,95,97,109,98,105,101,
+110,116,0,102,102,95,108,105,103,104,116,95,99,111,108,111,
+114,0,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,112,111,115,0,171,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,97,116,116,101,110,0,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,115,112,111,116,0,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,109,97,116,95,100,105,102,102,117,
+115,101,0,102,102,95,109,97,116,95,97,109,98,105,101,110,
+116,0,102,102,95,109,97,116,95,115,112,101,99,0,102,102,
+95,109,97,116,95,101,109,105,115,115,105,111,110,0,102,102,
+95,109,97,116,114,105,120,95,116,101,120,0,3,0,3,0,
+4,0,4,0,4,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,194,2,0,0,
+102,102,95,102,111,103,95,118,115,0,77,105,99,114,111,115,
+111,102,116,32,40,82,41,32,72,76,83,76,32,83,104,97,
+100,101,114,32,67,111,109,112,105,108,101,114,32,54,46,51,
+46,57,52,49,53,46,48,0,76,70,83,48,168,1,0,0,
+7,0,0,0,8,0,0,0,88,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,110,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,122,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,132,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,140,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,153,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,3,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,163,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,5,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,67,111,109,112,117,116,101,83,
+112,111,116,76,105,103,104,116,83,112,101,99,49,0,101,121,
+101,80,111,115,105,116,105,111,110,0,101,121,101,78,111,114,
+109,97,108,0,118,105,101,119,68,105,114,0,100,105,102,102,
+117,115,101,67,111,108,111,114,0,115,112,101,99,67,111,108,
+111,114,0,97,109,98,0,171,76,73,66,70,8,19,0,0,
+68,88,66,67,162,247,150,119,108,183,177,195,169,152,12,225,
+187,188,175,22,1,0,0,0,8,19,0,0,6,0,0,0,
+56,0,0,0,32,4,0,0,252,7,0,0,28,12,0,0,
+152,12,0,0,92,17,0,0,65,111,110,57,224,3,0,0,
+224,3,0,0,0,2,86,76,140,3,0,0,84,0,0,0,
+4,0,36,0,0,0,84,0,0,0,84,0,0,0,36,0,
+0,0,84,0,0,0,10,0,2,0,0,0,0,0,0,0,
+0,0,18,0,2,0,2,0,0,0,0,0,0,0,26,0,
+2,0,4,0,0,0,0,0,0,0,34,0,2,0,6,0,
+0,0,0,0,0,2,86,76,81,0,0,5,8,0,15,160,
+0,0,128,63,0,0,0,0,0,0,0,0,0,0,0,0,
+31,0,0,2,5,0,0,128,0,0,15,144,31,0,0,2,
+5,0,1,128,1,0,15,144,31,0,0,2,5,0,3,128,
+3,0,15,144,31,0,0,2,5,0,4,128,4,0,15,144,
+31,0,0,2,5,0,5,128,5,0,15,144,5,0,0,3,
+0,0,1,128,2,0,255,160,2,0,255,160,12,0,0,3,
+0,0,1,128,0,0,0,129,0,0,0,128,4,0,0,4,
+0,0,14,128,0,0,144,144,2,0,255,161,2,0,144,160,
+8,0,0,3,1,0,1,128,0,0,249,128,0,0,249,128,
+12,0,0,3,1,0,2,128,4,0,255,160,1,0,0,128,
+5,0,0,3,0,0,1,128,0,0,0,128,1,0,85,128,
+1,0,0,2,2,0,1,128,8,0,0,160,4,0,0,4,
+1,0,2,128,4,0,170,160,1,0,0,128,2,0,0,128,
+7,0,0,2,1,0,1,128,1,0,0,128,5,0,0,3,
+0,0,14,128,0,0,228,128,1,0,0,128,6,0,0,2,
+1,0,1,128,1,0,85,128,4,0,0,4,0,0,1,128,
+0,0,0,128,1,0,0,129,1,0,0,128,8,0,0,3,
+1,0,1,128,0,0,249,128,6,0,228,160,8,0,0,3,
+0,0,2,128,1,0,228,144,0,0,249,128,11,0,0,3,
+0,0,2,128,0,0,85,128,8,0,85,160,5,0,0,3,
+0,0,14,128,0,0,85,128,3,0,144,144,5,0,0,3,
+0,0,14,128,0,0,228,128,0,0,144,160,11,0,0,3,
+1,0,1,128,1,0,0,128,8,0,85,160,2,0,0,3,
+1,0,1,128,1,0,0,128,4,0,0,161,5,0,0,3,
+1,0,1,128,1,0,0,128,4,0,85,160,11,0,0,3,
+1,0,1,128,1,0,0,128,8,0,85,160,10,0,0,3,
+1,0,1,128,1,0,0,128,8,0,0,160,5,0,0,3,
+0,0,1,128,0,0,0,128,1,0,0,128,5,0,0,3,
+0,0,7,128,0,0,0,128,0,0,249,128,10,0,0,3,
+0,0,7,128,0,0,228,128,8,0,0,160,2,0,0,3,
+0,0,7,128,0,0,228,128,5,0,228,144,5,0,0,3,
+0,0,8,128,3,0,255,160,3,0,255,160,12,0,0,3,
+0,0,8,128,0,0,255,129,0,0,255,128,4,0,0,4,
+1,0,7,128,0,0,228,144,3,0,255,161,3,0,228,160,
+8,0,0,3,1,0,8,128,1,0,228,128,1,0,228,128,
+12,0,0,3,2,0,2,128,5,0,255,160,1,0,255,128,
+5,0,0,3,0,0,8,128,0,0,255,128,2,0,85,128,
+4,0,0,4,2,0,1,128,5,0,170,160,1,0,255,128,
+2,0,0,128,7,0,0,2,1,0,8,128,1,0,255,128,
+5,0,0,3,1,0,7,128,1,0,255,128,1,0,228,128,
+6,0,0,2,1,0,8,128,2,0,0,128,4,0,0,4,
+0,0,8,128,0,0,255,128,1,0,255,129,1,0,255,128,
+8,0,0,3,1,0,8,128,1,0,228,128,7,0,228,160,
+8,0,0,3,1,0,1,128,1,0,228,144,1,0,228,128,
+11,0,0,3,1,0,9,128,1,0,228,128,8,0,85,160,
+5,0,0,3,1,0,7,128,1,0,0,128,3,0,228,144,
+5,0,0,3,1,0,7,128,1,0,228,128,1,0,228,160,
+2,0,0,3,1,0,8,128,1,0,255,128,5,0,0,161,
+5,0,0,3,1,0,8,128,1,0,255,128,5,0,85,160,
+11,0,0,3,1,0,8,128,1,0,255,128,8,0,85,160,
+10,0,0,3,1,0,8,128,1,0,255,128,8,0,0,160,
+5,0,0,3,0,0,8,128,0,0,255,128,1,0,255,128,
+5,0,0,3,1,0,7,128,0,0,255,128,1,0,228,128,
+10,0,0,3,1,0,7,128,1,0,228,128,8,0,0,160,
+2,0,0,3,0,0,7,224,0,0,228,128,1,0,228,128,
+1,0,0,2,1,0,7,224,4,0,228,144,255,255,0,0,
+65,111,110,57,212,3,0,0,212,3,0,0,0,2,80,76,
+128,3,0,0,84,0,0,0,4,0,36,0,0,0,84,0,
+0,0,84,0,0,0,36,0,0,0,84,0,0,0,10,0,
+2,0,0,0,0,0,0,0,0,0,18,0,2,0,2,0,
+0,0,0,0,0,0,26,0,2,0,4,0,0,0,0,0,
+0,0,34,0,2,0,6,0,0,0,0,0,0,2,80,76,
+81,0,0,5,8,0,15,160,0,0,128,63,0,0,0,128,
+0,0,128,191,0,0,0,0,31,0,0,2,0,0,0,128,
+0,0,7,176,31,0,0,2,0,0,0,128,1,0,7,176,
+31,0,0,2,0,0,0,128,3,0,7,176,31,0,0,2,
+0,0,0,128,4,0,7,176,31,0,0,2,0,0,0,128,
+5,0,7,176,4,0,0,4,0,0,7,128,0,0,228,176,
+2,0,255,161,2,0,228,160,8,0,0,3,0,0,8,128,
+0,0,228,128,0,0,228,128,7,0,0,2,1,0,8,128,
+0,0,255,128,5,0,0,3,0,0,7,128,0,0,228,128,
+1,0,255,128,8,0,0,3,1,0,1,128,0,0,228,128,
+6,0,228,160,8,0,0,3,0,0,1,128,1,0,228,176,
+0,0,228,128,11,0,0,3,0,0,2,128,1,0,0,128,
+8,0,255,160,2,0,0,3,0,0,2,128,0,0,85,128,
+4,0,0,161,5,0,0,3,0,0,18,128,0,0,85,128,
+4,0,85,160,1,0,0,2,1,0,1,128,8,0,0,160,
+4,0,0,4,0,0,4,128,4,0,170,160,0,0,255,128,
+1,0,0,128,2,0,0,3,0,0,8,128,0,0,255,129,
+4,0,255,160,88,0,0,4,0,0,8,128,0,0,255,128,
+8,0,85,160,8,0,170,160,6,0,0,2,0,0,4,128,
+0,0,170,128,5,0,0,3,0,0,2,128,0,0,85,128,
+0,0,170,128,5,0,0,3,0,0,4,128,2,0,255,160,
+2,0,255,160,88,0,0,4,0,0,4,128,0,0,170,129,
+8,0,255,160,0,0,255,128,88,0,0,4,0,0,2,128,
+0,0,170,128,0,0,85,128,8,0,255,160,5,0,0,3,
+1,0,14,128,0,0,0,128,3,0,27,176,5,0,0,3,
+1,0,14,128,1,0,228,128,0,0,27,160,88,0,0,4,
+1,0,14,128,0,0,0,128,1,0,228,128,8,0,255,160,
+5,0,0,3,0,0,7,128,0,0,85,128,1,0,27,128,
+10,0,0,3,1,0,14,128,0,0,27,128,8,0,0,160,
+2,0,0,3,0,0,7,128,1,0,27,128,5,0,228,176,
+4,0,0,4,2,0,7,128,0,0,228,176,3,0,255,161,
+3,0,228,160,8,0,0,3,0,0,8,128,2,0,228,128,
+2,0,228,128,7,0,0,2,2,0,8,128,0,0,255,128,
+5,0,0,3,2,0,7,128,2,0,255,128,2,0,228,128,
+8,0,0,3,2,0,8,128,2,0,228,128,7,0,228,160,
+8,0,0,3,1,0,2,128,1,0,228,176,2,0,228,128,
+11,0,0,3,1,0,4,128,2,0,255,128,8,0,255,160,
+2,0,0,3,1,0,4,128,1,0,170,128,5,0,0,161,
+5,0,0,3,1,0,20,128,1,0,170,128,5,0,85,160,
+4,0,0,4,1,0,1,128,5,0,170,160,0,0,255,128,
+1,0,0,128,2,0,0,3,0,0,8,128,0,0,255,129,
+5,0,255,160,88,0,0,4,0,0,8,128,0,0,255,128,
+8,0,85,160,8,0,170,160,6,0,0,2,1,0,1,128,
+1,0,0,128,5,0,0,3,1,0,1,128,1,0,170,128,
+1,0,0,128,5,0,0,3,1,0,4,128,3,0,255,160,
+3,0,255,160,88,0,0,4,0,0,8,128,1,0,170,129,
+8,0,255,160,0,0,255,128,88,0,0,4,0,0,8,128,
+0,0,255,128,1,0,0,128,8,0,255,160,5,0,0,3,
+2,0,7,128,1,0,85,128,3,0,228,176,5,0,0,3,
+2,0,7,128,2,0,228,128,1,0,228,160,88,0,0,4,
+1,0,7,128,1,0,85,128,2,0,228,128,8,0,255,160,
+5,0,0,3,1,0,7,128,0,0,255,128,1,0,228,128,
+10,0,0,3,2,0,7,128,1,0,228,128,8,0,0,160,
+2,0,0,3,0,0,7,128,0,0,228,128,2,0,228,128,
+1,0,0,2,0,0,7,224,0,0,228,128,1,0,0,2,
+1,0,7,224,4,0,228,176,255,255,0,0,83,72,68,82,
+24,4,0,0,64,0,240,255,6,1,0,0,89,8,0,4,
+70,142,32,0,0,0,0,0,37,0,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,95,0,0,3,114,16,16,0,
+1,0,0,0,95,0,0,3,114,16,16,0,3,0,0,0,
+95,0,0,3,114,16,16,0,4,0,0,0,95,0,0,3,
+114,16,16,0,5,0,0,0,101,0,0,3,114,32,16,0,
+0,0,0,0,101,0,0,3,114,32,16,0,1,0,0,0,
+104,0,0,2,3,0,0,0,54,0,0,5,114,0,16,0,
+0,0,0,0,70,18,16,0,5,0,0,0,54,0,0,5,
+130,0,16,0,0,0,0,0,1,64,0,0,0,0,0,0,
+48,0,0,1,33,0,0,7,18,0,16,0,1,0,0,0,
+58,0,16,0,0,0,0,0,1,64,0,0,2,0,0,0,
+3,0,4,3,10,0,16,0,1,0,0,0,50,0,0,16,
+114,0,16,0,1,0,0,0,70,18,16,128,65,0,0,0,
+0,0,0,0,246,143,32,6,0,0,0,0,18,0,0,0,
+58,0,16,0,0,0,0,0,70,130,32,6,0,0,0,0,
+18,0,0,0,58,0,16,0,0,0,0,0,16,0,0,7,
+130,0,16,0,1,0,0,0,70,2,16,0,1,0,0,0,
+70,2,16,0,1,0,0,0,50,0,0,12,18,0,16,0,
+2,0,0,0,42,128,32,6,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,58,0,16,0,1,0,0,0,
+1,64,0,0,0,0,128,63,14,0,0,10,18,0,16,0,
+2,0,0,0,2,64,0,0,0,0,128,63,0,0,128,63,
+0,0,128,63,0,0,128,63,10,0,16,0,2,0,0,0,
+57,0,0,10,34,0,16,0,2,0,0,0,1,64,0,0,
+0,0,0,0,58,128,32,6,0,0,0,0,18,0,0,0,
+58,0,16,0,0,0,0,0,49,0,0,10,66,0,16,0,
+2,0,0,0,58,128,32,6,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,58,0,16,0,1,0,0,0,
+1,0,0,7,34,0,16,0,2,0,0,0,42,0,16,0,
+2,0,0,0,26,0,16,0,2,0,0,0,55,0,0,9,
+18,0,16,0,2,0,0,0,26,0,16,0,2,0,0,0,
+1,64,0,0,0,0,0,0,10,0,16,0,2,0,0,0,
+68,0,0,5,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,56,0,0,7,114,0,16,0,1,0,0,0,
+246,15,16,0,1,0,0,0,70,2,16,0,1,0,0,0,
+16,0,0,10,130,0,16,0,1,0,0,0,70,2,16,0,
+1,0,0,0,70,130,32,6,0,0,0,0,34,0,0,0,
+58,0,16,0,0,0,0,0,52,0,0,7,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,1,64,0,0,
+0,0,0,0,0,0,0,11,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,10,128,32,134,65,0,0,0,
+0,0,0,0,26,0,0,0,58,0,16,0,0,0,0,0,
+56,32,0,10,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,26,128,32,6,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,56,0,0,7,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,10,0,16,0,
+2,0,0,0,16,0,0,7,18,0,16,0,1,0,0,0,
+70,18,16,0,1,0,0,0,70,2,16,0,1,0,0,0,
+52,0,0,7,18,0,16,0,1,0,0,0,10,0,16,0,
+1,0,0,0,1,64,0,0,0,0,0,0,56,0,0,7,
+114,0,16,0,1,0,0,0,6,0,16,0,1,0,0,0,
+70,18,16,0,3,0,0,0,56,0,0,10,114,0,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,70,130,32,6,
+0,0,0,0,10,0,0,0,58,0,16,0,0,0,0,0,
+56,0,0,7,114,0,16,0,1,0,0,0,246,15,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,51,0,0,10,
+114,0,16,0,1,0,0,0,70,2,16,0,1,0,0,0,
+2,64,0,0,0,0,128,63,0,0,128,63,0,0,128,63,
+0,0,0,0,0,0,0,7,114,0,16,0,0,0,0,0,
+70,2,16,0,0,0,0,0,70,2,16,0,1,0,0,0,
+30,0,0,7,130,0,16,0,0,0,0,0,58,0,16,0,
+0,0,0,0,1,64,0,0,1,0,0,0,22,0,0,1,
+54,0,0,5,114,32,16,0,0,0,0,0,70,2,16,0,
+0,0,0,0,54,0,0,5,114,32,16,0,1,0,0,0,
+70,18,16,0,4,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,32,0,0,0,3,0,0,0,0,0,0,0,
+7,0,0,0,20,0,0,0,2,0,0,0,1,0,0,0,
+1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+4,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,188,4,0,0,
+1,0,0,0,108,0,0,0,1,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,142,4,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,92,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,85,110,105,116,
+121,70,70,86,101,114,116,101,120,0,171,171,92,0,0,0,
+14,0,0,0,132,0,0,0,240,3,0,0,0,0,0,0,
+0,0,0,0,180,2,0,0,0,0,0,0,64,0,0,0,
+0,0,0,0,204,2,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,240,2,0,0,
+64,0,0,0,64,0,0,0,0,0,0,0,204,2,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,253,2,0,0,128,0,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,56,3,0,0,
+144,0,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,71,3,0,0,160,0,0,0,128,0,0,0,
+2,0,0,0,88,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,124,3,0,0,
+32,1,0,0,128,0,0,0,2,0,0,0,140,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,176,3,0,0,160,1,0,0,128,0,0,0,
+2,0,0,0,192,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,228,3,0,0,
+32,2,0,0,128,0,0,0,2,0,0,0,244,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,24,4,0,0,160,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,39,4,0,0,
+176,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,54,4,0,0,192,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,66,4,0,0,
+208,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,82,4,0,0,224,2,0,0,0,1,0,0,
+0,0,0,0,96,4,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,132,4,0,0,
+224,3,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,102,102,95,109,97,116,114,105,120,95,109,118,
+112,0,102,108,111,97,116,52,120,52,0,171,3,0,3,0,
+4,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,194,2,0,0,
+102,102,95,109,97,116,114,105,120,95,109,118,0,102,102,95,
+118,101,99,95,99,111,108,111,114,0,102,108,111,97,116,52,
+0,171,171,171,1,0,3,0,1,0,4,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,118,101,99,95,97,
+109,98,105,101,110,116,0,102,102,95,108,105,103,104,116,95,
+99,111,108,111,114,0,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,112,111,115,0,171,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,97,116,116,101,110,0,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,115,112,111,
+116,0,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,109,97,116,95,100,
+105,102,102,117,115,101,0,102,102,95,109,97,116,95,97,109,
+98,105,101,110,116,0,102,102,95,109,97,116,95,115,112,101,
+99,0,102,102,95,109,97,116,95,101,109,105,115,115,105,111,
+110,0,102,102,95,109,97,116,114,105,120,95,116,101,120,0,
+3,0,3,0,4,0,4,0,4,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+194,2,0,0,102,102,95,102,111,103,95,118,115,0,77,105,
+99,114,111,115,111,102,116,32,40,82,41,32,72,76,83,76,
+32,83,104,97,100,101,114,32,67,111,109,112,105,108,101,114,
+32,54,46,51,46,57,52,49,53,46,48,0,76,70,83,48,
+164,1,0,0,7,0,0,0,8,0,0,0,88,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,2,0,0,0,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,106,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,118,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,128,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,136,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,149,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,3,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,159,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,5,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,67,111,109,112,
+117,116,101,83,112,111,116,76,105,103,104,116,50,0,101,121,
+101,80,111,115,105,116,105,111,110,0,101,121,101,78,111,114,
+109,97,108,0,118,105,101,119,68,105,114,0,100,105,102,102,
+117,115,101,67,111,108,111,114,0,115,112,101,99,67,111,108,
+111,114,0,97,109,98,0,171,76,73,66,70,80,16,0,0,
+68,88,66,67,247,83,83,41,89,91,110,3,164,72,197,192,
+107,184,230,166,1,0,0,0,80,16,0,0,5,0,0,0,
+52,0,0,0,156,3,0,0,96,9,0,0,220,9,0,0,
+160,14,0,0,65,111,110,57,96,3,0,0,96,3,0,0,
+0,2,86,76,48,3,0,0,48,0,0,0,1,0,36,0,
+0,0,48,0,0,0,48,0,0,0,36,0,0,0,48,0,
+0,0,0,0,45,0,0,0,0,0,0,0,0,2,86,76,
+81,0,0,5,45,0,15,160,0,0,0,0,0,0,0,0,
+0,0,128,63,0,0,0,0,48,0,0,5,0,0,15,240,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+31,0,0,2,5,0,0,128,0,0,15,144,31,0,0,2,
+5,0,1,128,1,0,15,144,31,0,0,2,5,0,2,128,
+2,0,15,144,31,0,0,2,5,0,3,128,3,0,15,144,
+31,0,0,2,5,0,4,128,4,0,15,144,31,0,0,2,
+5,0,5,128,5,0,15,144,1,0,0,2,0,0,7,128,
+4,0,228,144,1,0,0,2,1,0,7,128,5,0,228,144,
+27,0,0,2,0,8,228,240,0,0,228,240,4,0,0,6,
+2,0,7,128,0,0,228,144,18,32,255,161,0,8,228,240,
+18,32,228,160,0,8,228,240,8,0,0,3,0,0,8,128,
+2,0,228,128,2,0,228,128,7,0,0,2,1,0,8,128,
+0,0,255,128,4,0,0,4,3,0,7,128,2,0,228,128,
+1,0,255,128,2,0,228,144,36,0,0,2,4,0,7,128,
+3,0,228,128,8,0,0,3,2,0,8,128,1,0,228,144,
+4,0,228,128,5,0,0,5,3,0,1,128,18,32,255,160,
+0,8,228,240,18,32,255,160,0,8,228,240,12,0,0,3,
+3,0,1,128,3,0,0,129,3,0,0,128,12,0,0,4,
+3,0,2,128,26,32,255,160,0,8,228,240,0,0,255,128,
+5,0,0,3,3,0,1,128,3,0,85,128,3,0,0,128,
+5,0,0,4,0,0,8,128,0,0,255,128,26,32,170,160,
+0,8,228,240,2,0,0,3,0,0,8,128,0,0,255,128,
+45,0,170,160,6,0,0,2,0,0,8,128,0,0,255,128,
+4,0,0,4,0,0,8,128,3,0,0,128,0,0,255,129,
+0,0,255,128,11,0,0,3,2,0,8,128,2,0,255,128,
+45,0,85,160,32,0,0,3,3,0,1,128,2,0,255,128,
+44,0,255,160,10,0,0,3,2,0,8,128,3,0,0,128,
+45,0,170,160,5,0,0,3,2,0,7,128,1,0,255,128,
+2,0,228,128,8,0,0,3,1,0,8,128,1,0,228,144,
+2,0,228,128,8,0,0,4,2,0,1,128,2,0,228,128,
+34,32,228,160,0,8,228,240,11,0,0,3,2,0,1,128,
+2,0,0,128,45,0,85,160,2,0,0,4,2,0,1,128,
+2,0,0,128,26,32,0,161,0,8,228,240,5,0,0,4,
+2,0,1,128,2,0,0,128,26,32,85,160,0,8,228,240,
+11,0,0,3,2,0,1,128,2,0,0,128,45,0,85,160,
+10,0,0,3,2,0,1,128,2,0,0,128,45,0,170,160,
+5,0,0,3,0,0,8,128,0,0,255,128,2,0,0,128,
+11,0,0,3,1,0,8,128,1,0,255,128,45,0,85,160,
+12,0,0,3,2,0,1,128,45,0,85,160,1,0,255,128,
+5,0,0,3,2,0,2,128,2,0,255,128,0,0,255,128,
+5,0,0,4,2,0,14,128,2,0,85,128,10,32,144,160,
+0,8,228,240,4,0,0,4,0,0,7,128,2,0,0,128,
+2,0,249,128,0,0,228,128,5,0,0,3,2,0,7,128,
+1,0,255,128,3,0,228,144,5,0,0,4,2,0,7,128,
+2,0,228,128,10,32,228,160,0,8,228,240,5,0,0,3,
+2,0,7,128,0,0,255,128,2,0,228,128,10,0,0,3,
+2,0,7,128,2,0,228,128,45,0,170,160,2,0,0,3,
+1,0,7,128,1,0,228,128,2,0,228,128,29,0,0,0,
+1,0,0,2,1,0,7,224,0,0,228,128,1,0,0,2,
+0,0,7,224,1,0,228,128,255,255,0,0,83,72,68,82,
+188,5,0,0,64,0,240,255,111,1,0,0,89,8,0,4,
+70,142,32,0,0,0,0,0,45,0,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,95,0,0,3,114,16,16,0,
+1,0,0,0,95,0,0,3,114,16,16,0,2,0,0,0,
+95,0,0,3,114,16,16,0,3,0,0,0,95,0,0,3,
+114,16,16,0,4,0,0,0,95,0,0,3,114,16,16,0,
+5,0,0,0,101,0,0,3,114,32,16,0,0,0,0,0,
+101,0,0,3,114,32,16,0,1,0,0,0,104,0,0,2,
+4,0,0,0,54,0,0,5,114,0,16,0,0,0,0,0,
+70,18,16,0,4,0,0,0,54,0,0,5,114,0,16,0,
+1,0,0,0,70,18,16,0,5,0,0,0,54,0,0,5,
+130,0,16,0,0,0,0,0,1,64,0,0,0,0,0,0,
+48,0,0,1,33,0,0,7,130,0,16,0,1,0,0,0,
+58,0,16,0,0,0,0,0,1,64,0,0,2,0,0,0,
+3,0,4,3,58,0,16,0,1,0,0,0,50,0,0,16,
+114,0,16,0,2,0,0,0,70,18,16,128,65,0,0,0,
+0,0,0,0,246,143,32,6,0,0,0,0,18,0,0,0,
+58,0,16,0,0,0,0,0,70,130,32,6,0,0,0,0,
+18,0,0,0,58,0,16,0,0,0,0,0,16,0,0,7,
+130,0,16,0,1,0,0,0,70,2,16,0,2,0,0,0,
+70,2,16,0,2,0,0,0,50,0,0,12,130,0,16,0,
+2,0,0,0,42,128,32,6,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,58,0,16,0,1,0,0,0,
+1,64,0,0,0,0,128,63,14,0,0,10,130,0,16,0,
+2,0,0,0,2,64,0,0,0,0,128,63,0,0,128,63,
+0,0,128,63,0,0,128,63,58,0,16,0,2,0,0,0,
+57,0,0,10,18,0,16,0,3,0,0,0,1,64,0,0,
+0,0,0,0,58,128,32,6,0,0,0,0,18,0,0,0,
+58,0,16,0,0,0,0,0,49,0,0,10,34,0,16,0,
+3,0,0,0,58,128,32,6,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,58,0,16,0,1,0,0,0,
+1,0,0,7,18,0,16,0,3,0,0,0,26,0,16,0,
+3,0,0,0,10,0,16,0,3,0,0,0,55,0,0,9,
+130,0,16,0,2,0,0,0,10,0,16,0,3,0,0,0,
+1,64,0,0,0,0,0,0,58,0,16,0,2,0,0,0,
+68,0,0,5,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,56,0,0,7,114,0,16,0,3,0,0,0,
+246,15,16,0,1,0,0,0,70,2,16,0,2,0,0,0,
+16,0,0,10,130,0,16,0,3,0,0,0,70,2,16,0,
+3,0,0,0,70,130,32,6,0,0,0,0,34,0,0,0,
+58,0,16,0,0,0,0,0,52,0,0,7,130,0,16,0,
+3,0,0,0,58,0,16,0,3,0,0,0,1,64,0,0,
+0,0,0,0,0,0,0,11,130,0,16,0,3,0,0,0,
+58,0,16,0,3,0,0,0,10,128,32,134,65,0,0,0,
+0,0,0,0,26,0,0,0,58,0,16,0,0,0,0,0,
+56,32,0,10,130,0,16,0,3,0,0,0,58,0,16,0,
+3,0,0,0,26,128,32,6,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,56,0,0,7,130,0,16,0,
+2,0,0,0,58,0,16,0,2,0,0,0,58,0,16,0,
+3,0,0,0,16,0,0,7,18,0,16,0,3,0,0,0,
+70,18,16,0,1,0,0,0,70,2,16,0,3,0,0,0,
+52,0,0,7,18,0,16,0,3,0,0,0,10,0,16,0,
+3,0,0,0,1,64,0,0,0,0,0,0,56,0,0,7,
+226,0,16,0,3,0,0,0,6,0,16,0,3,0,0,0,
+6,25,16,0,3,0,0,0,56,0,0,10,226,0,16,0,
+3,0,0,0,86,14,16,0,3,0,0,0,6,137,32,6,
+0,0,0,0,10,0,0,0,58,0,16,0,0,0,0,0,
+49,0,0,7,18,0,16,0,3,0,0,0,1,64,0,0,
+0,0,0,0,10,0,16,0,3,0,0,0,31,0,4,3,
+10,0,16,0,3,0,0,0,50,0,0,9,114,0,16,0,
+2,0,0,0,70,2,16,0,2,0,0,0,246,15,16,0,
+1,0,0,0,70,18,16,0,2,0,0,0,16,0,0,7,
+130,0,16,0,1,0,0,0,70,2,16,0,2,0,0,0,
+70,2,16,0,2,0,0,0,68,0,0,5,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,56,0,0,7,
+114,0,16,0,2,0,0,0,246,15,16,0,1,0,0,0,
+70,2,16,0,2,0,0,0,16,0,0,7,130,0,16,0,
+1,0,0,0,70,18,16,0,1,0,0,0,70,2,16,0,
+2,0,0,0,52,0,0,7,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,1,64,0,0,0,0,0,0,
+47,0,0,5,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,56,0,0,8,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,58,128,32,0,0,0,0,0,
+44,0,0,0,25,0,0,5,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,51,0,0,7,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,1,64,0,0,
+0,0,128,63,56,0,0,7,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,58,0,16,0,2,0,0,0,
+50,0,0,12,114,0,16,0,0,0,0,0,246,15,16,0,
+1,0,0,0,70,130,32,6,0,0,0,0,10,0,0,0,
+58,0,16,0,0,0,0,0,70,2,16,0,0,0,0,0,
+21,0,0,1,56,0,0,7,114,0,16,0,2,0,0,0,
+246,15,16,0,2,0,0,0,150,7,16,0,3,0,0,0,
+51,0,0,10,114,0,16,0,2,0,0,0,70,2,16,0,
+2,0,0,0,2,64,0,0,0,0,128,63,0,0,128,63,
+0,0,128,63,0,0,0,0,0,0,0,7,114,0,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,70,2,16,0,
+2,0,0,0,30,0,0,7,130,0,16,0,0,0,0,0,
+58,0,16,0,0,0,0,0,1,64,0,0,1,0,0,0,
+22,0,0,1,54,0,0,5,114,32,16,0,1,0,0,0,
+70,2,16,0,0,0,0,0,54,0,0,5,114,32,16,0,
+0,0,0,0,70,2,16,0,1,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,48,0,0,0,4,0,0,0,
+0,0,0,0,8,0,0,0,33,0,0,0,2,0,0,0,
+1,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,5,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+188,4,0,0,1,0,0,0,108,0,0,0,1,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,142,4,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+85,110,105,116,121,70,70,86,101,114,116,101,120,0,171,171,
+92,0,0,0,14,0,0,0,132,0,0,0,240,3,0,0,
+0,0,0,0,0,0,0,0,180,2,0,0,0,0,0,0,
+64,0,0,0,0,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+240,2,0,0,64,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,253,2,0,0,128,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+56,3,0,0,144,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,71,3,0,0,160,0,0,0,
+128,0,0,0,2,0,0,0,88,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+124,3,0,0,32,1,0,0,128,0,0,0,2,0,0,0,
+140,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,176,3,0,0,160,1,0,0,
+128,0,0,0,2,0,0,0,192,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+228,3,0,0,32,2,0,0,128,0,0,0,2,0,0,0,
+244,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,24,4,0,0,160,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+39,4,0,0,176,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,54,4,0,0,192,2,0,0,
+16,0,0,0,2,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+66,4,0,0,208,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,82,4,0,0,224,2,0,0,
+0,1,0,0,0,0,0,0,96,4,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+132,4,0,0,224,3,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,102,102,95,109,97,116,114,105,
+120,95,109,118,112,0,102,108,111,97,116,52,120,52,0,171,
+3,0,3,0,4,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+194,2,0,0,102,102,95,109,97,116,114,105,120,95,109,118,
+0,102,102,95,118,101,99,95,99,111,108,111,114,0,102,108,
+111,97,116,52,0,171,171,171,1,0,3,0,1,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,118,
+101,99,95,97,109,98,105,101,110,116,0,102,102,95,108,105,
+103,104,116,95,99,111,108,111,114,0,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,112,111,115,0,171,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,97,116,116,
+101,110,0,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,115,112,111,116,0,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,109,
+97,116,95,100,105,102,102,117,115,101,0,102,102,95,109,97,
+116,95,97,109,98,105,101,110,116,0,102,102,95,109,97,116,
+95,115,112,101,99,0,102,102,95,109,97,116,95,101,109,105,
+115,115,105,111,110,0,102,102,95,109,97,116,114,105,120,95,
+116,101,120,0,3,0,3,0,4,0,4,0,4,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,194,2,0,0,102,102,95,102,111,103,95,118,
+115,0,77,105,99,114,111,115,111,102,116,32,40,82,41,32,
+72,76,83,76,32,83,104,97,100,101,114,32,67,111,109,112,
+105,108,101,114,32,54,46,51,46,57,52,49,53,46,48,0,
+76,70,83,48,168,1,0,0,7,0,0,0,8,0,0,0,
+88,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,2,0,0,0,
+255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,
+110,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+122,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,
+1,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+132,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,
+2,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+140,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+153,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,3,0,0,0,
+4,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+163,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,
+5,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+67,111,109,112,117,116,101,83,112,111,116,76,105,103,104,116,
+83,112,101,99,50,0,101,121,101,80,111,115,105,116,105,111,
+110,0,101,121,101,78,111,114,109,97,108,0,118,105,101,119,
+68,105,114,0,100,105,102,102,117,115,101,67,111,108,111,114,
+0,115,112,101,99,67,111,108,111,114,0,97,109,98,0,171,
+76,73,66,70,172,16,0,0,68,88,66,67,196,226,164,1,
+85,73,127,248,194,26,134,144,141,122,227,103,1,0,0,0,
+172,16,0,0,5,0,0,0,52,0,0,0,160,5,0,0,
+192,9,0,0,60,10,0,0,0,15,0,0,65,111,110,57,
+100,5,0,0,100,5,0,0,0,2,86,76,16,5,0,0,
+84,0,0,0,4,0,36,0,0,0,84,0,0,0,84,0,
+0,0,36,0,0,0,84,0,0,0,10,0,3,0,0,0,
+0,0,0,0,0,0,18,0,3,0,3,0,0,0,0,0,
+0,0,26,0,3,0,6,0,0,0,0,0,0,0,34,0,
+3,0,9,0,0,0,0,0,0,2,86,76,81,0,0,5,
+12,0,15,160,0,0,128,63,0,0,0,0,0,0,0,0,
+0,0,0,0,31,0,0,2,5,0,0,128,0,0,15,144,
+31,0,0,2,5,0,1,128,1,0,15,144,31,0,0,2,
+5,0,3,128,3,0,15,144,31,0,0,2,5,0,4,128,
+4,0,15,144,31,0,0,2,5,0,5,128,5,0,15,144,
+5,0,0,3,0,0,1,128,3,0,255,160,3,0,255,160,
+12,0,0,3,0,0,1,128,0,0,0,129,0,0,0,128,
+4,0,0,4,0,0,14,128,0,0,144,144,3,0,255,161,
+3,0,144,160,8,0,0,3,1,0,1,128,0,0,249,128,
+0,0,249,128,12,0,0,3,1,0,2,128,6,0,255,160,
+1,0,0,128,5,0,0,3,0,0,1,128,0,0,0,128,
+1,0,85,128,1,0,0,2,2,0,1,128,12,0,0,160,
+4,0,0,4,1,0,2,128,6,0,170,160,1,0,0,128,
+2,0,0,128,7,0,0,2,1,0,1,128,1,0,0,128,
+5,0,0,3,0,0,14,128,0,0,228,128,1,0,0,128,
+6,0,0,2,1,0,1,128,1,0,85,128,4,0,0,4,
+0,0,1,128,0,0,0,128,1,0,0,129,1,0,0,128,
+8,0,0,3,1,0,1,128,0,0,249,128,9,0,228,160,
+8,0,0,3,0,0,2,128,1,0,228,144,0,0,249,128,
+11,0,0,3,0,0,2,128,0,0,85,128,12,0,85,160,
+5,0,0,3,0,0,14,128,0,0,85,128,3,0,144,144,
+5,0,0,3,0,0,14,128,0,0,228,128,0,0,144,160,
+11,0,0,3,1,0,1,128,1,0,0,128,12,0,85,160,
+2,0,0,3,1,0,1,128,1,0,0,128,6,0,0,161,
+5,0,0,3,1,0,1,128,1,0,0,128,6,0,85,160,
+11,0,0,3,1,0,1,128,1,0,0,128,12,0,85,160,
+10,0,0,3,1,0,1,128,1,0,0,128,12,0,0,160,
+5,0,0,3,0,0,1,128,0,0,0,128,1,0,0,128,
+5,0,0,3,0,0,7,128,0,0,0,128,0,0,249,128,
+10,0,0,3,0,0,7,128,0,0,228,128,12,0,0,160,
+2,0,0,3,0,0,7,128,0,0,228,128,5,0,228,144,
+5,0,0,3,0,0,8,128,4,0,255,160,4,0,255,160,
+12,0,0,3,0,0,8,128,0,0,255,129,0,0,255,128,
+4,0,0,4,1,0,7,128,0,0,228,144,4,0,255,161,
+4,0,228,160,8,0,0,3,1,0,8,128,1,0,228,128,
+1,0,228,128,12,0,0,3,2,0,2,128,7,0,255,160,
+1,0,255,128,5,0,0,3,0,0,8,128,0,0,255,128,
+2,0,85,128,4,0,0,4,2,0,2,128,7,0,170,160,
+1,0,255,128,2,0,0,128,7,0,0,2,1,0,8,128,
+1,0,255,128,5,0,0,3,1,0,7,128,1,0,255,128,
+1,0,228,128,6,0,0,2,1,0,8,128,2,0,85,128,
+4,0,0,4,0,0,8,128,0,0,255,128,1,0,255,129,
+1,0,255,128,8,0,0,3,1,0,8,128,1,0,228,128,
+10,0,228,160,8,0,0,3,1,0,1,128,1,0,228,144,
+1,0,228,128,11,0,0,3,1,0,9,128,1,0,228,128,
+12,0,85,160,5,0,0,3,1,0,7,128,1,0,0,128,
+3,0,228,144,5,0,0,3,1,0,7,128,1,0,228,128,
+1,0,228,160,2,0,0,3,1,0,8,128,1,0,255,128,
+7,0,0,161,5,0,0,3,1,0,8,128,1,0,255,128,
+7,0,85,160,11,0,0,3,1,0,8,128,1,0,255,128,
+12,0,85,160,10,0,0,3,1,0,8,128,1,0,255,128,
+12,0,0,160,5,0,0,3,0,0,8,128,0,0,255,128,
+1,0,255,128,5,0,0,3,1,0,7,128,0,0,255,128,
+1,0,228,128,10,0,0,3,1,0,7,128,1,0,228,128,
+12,0,0,160,2,0,0,3,0,0,7,128,0,0,228,128,
+1,0,228,128,5,0,0,3,0,0,8,128,5,0,255,160,
+5,0,255,160,12,0,0,3,0,0,8,128,0,0,255,129,
+0,0,255,128,4,0,0,4,1,0,7,128,0,0,228,144,
+5,0,255,161,5,0,228,160,8,0,0,3,1,0,8,128,
+1,0,228,128,1,0,228,128,12,0,0,3,2,0,2,128,
+8,0,255,160,1,0,255,128,5,0,0,3,0,0,8,128,
+0,0,255,128,2,0,85,128,4,0,0,4,2,0,1,128,
+8,0,170,160,1,0,255,128,2,0,0,128,7,0,0,2,
+1,0,8,128,1,0,255,128,5,0,0,3,1,0,7,128,
+1,0,255,128,1,0,228,128,6,0,0,2,1,0,8,128,
+2,0,0,128,4,0,0,4,0,0,8,128,0,0,255,128,
+1,0,255,129,1,0,255,128,8,0,0,3,1,0,8,128,
+1,0,228,128,11,0,228,160,8,0,0,3,1,0,1,128,
+1,0,228,144,1,0,228,128,11,0,0,3,1,0,9,128,
+1,0,228,128,12,0,85,160,5,0,0,3,1,0,7,128,
+1,0,0,128,3,0,228,144,5,0,0,3,1,0,7,128,
+1,0,228,128,2,0,228,160,2,0,0,3,1,0,8,128,
+1,0,255,128,8,0,0,161,5,0,0,3,1,0,8,128,
+1,0,255,128,8,0,85,160,11,0,0,3,1,0,8,128,
+1,0,255,128,12,0,85,160,10,0,0,3,1,0,8,128,
+1,0,255,128,12,0,0,160,5,0,0,3,0,0,8,128,
+0,0,255,128,1,0,255,128,5,0,0,3,1,0,7,128,
+0,0,255,128,1,0,228,128,10,0,0,3,1,0,7,128,
+1,0,228,128,12,0,0,160,2,0,0,3,0,0,7,224,
+0,0,228,128,1,0,228,128,1,0,0,2,1,0,7,224,
+4,0,228,144,255,255,0,0,83,72,68,82,24,4,0,0,
+64,0,240,255,6,1,0,0,89,8,0,4,70,142,32,0,
+0,0,0,0,38,0,0,0,95,0,0,3,114,16,16,0,
+0,0,0,0,95,0,0,3,114,16,16,0,1,0,0,0,
+95,0,0,3,114,16,16,0,3,0,0,0,95,0,0,3,
+114,16,16,0,4,0,0,0,95,0,0,3,114,16,16,0,
+5,0,0,0,101,0,0,3,114,32,16,0,0,0,0,0,
+101,0,0,3,114,32,16,0,1,0,0,0,104,0,0,2,
+3,0,0,0,54,0,0,5,114,0,16,0,0,0,0,0,
+70,18,16,0,5,0,0,0,54,0,0,5,130,0,16,0,
+0,0,0,0,1,64,0,0,0,0,0,0,48,0,0,1,
+33,0,0,7,18,0,16,0,1,0,0,0,58,0,16,0,
+0,0,0,0,1,64,0,0,3,0,0,0,3,0,4,3,
+10,0,16,0,1,0,0,0,50,0,0,16,114,0,16,0,
+1,0,0,0,70,18,16,128,65,0,0,0,0,0,0,0,
+246,143,32,6,0,0,0,0,18,0,0,0,58,0,16,0,
+0,0,0,0,70,130,32,6,0,0,0,0,18,0,0,0,
+58,0,16,0,0,0,0,0,16,0,0,7,130,0,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,70,2,16,0,
+1,0,0,0,50,0,0,12,18,0,16,0,2,0,0,0,
+42,128,32,6,0,0,0,0,26,0,0,0,58,0,16,0,
+0,0,0,0,58,0,16,0,1,0,0,0,1,64,0,0,
+0,0,128,63,14,0,0,10,18,0,16,0,2,0,0,0,
+2,64,0,0,0,0,128,63,0,0,128,63,0,0,128,63,
+0,0,128,63,10,0,16,0,2,0,0,0,57,0,0,10,
+34,0,16,0,2,0,0,0,1,64,0,0,0,0,0,0,
+58,128,32,6,0,0,0,0,18,0,0,0,58,0,16,0,
+0,0,0,0,49,0,0,10,66,0,16,0,2,0,0,0,
+58,128,32,6,0,0,0,0,26,0,0,0,58,0,16,0,
+0,0,0,0,58,0,16,0,1,0,0,0,1,0,0,7,
+34,0,16,0,2,0,0,0,42,0,16,0,2,0,0,0,
+26,0,16,0,2,0,0,0,55,0,0,9,18,0,16,0,
+2,0,0,0,26,0,16,0,2,0,0,0,1,64,0,0,
+0,0,0,0,10,0,16,0,2,0,0,0,68,0,0,5,
+130,0,16,0,1,0,0,0,58,0,16,0,1,0,0,0,
+56,0,0,7,114,0,16,0,1,0,0,0,246,15,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,16,0,0,10,
+130,0,16,0,1,0,0,0,70,2,16,0,1,0,0,0,
+70,130,32,6,0,0,0,0,34,0,0,0,58,0,16,0,
+0,0,0,0,52,0,0,7,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,1,64,0,0,0,0,0,0,
+0,0,0,11,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,10,128,32,134,65,0,0,0,0,0,0,0,
+26,0,0,0,58,0,16,0,0,0,0,0,56,32,0,10,
+130,0,16,0,1,0,0,0,58,0,16,0,1,0,0,0,
+26,128,32,6,0,0,0,0,26,0,0,0,58,0,16,0,
+0,0,0,0,56,0,0,7,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,10,0,16,0,2,0,0,0,
+16,0,0,7,18,0,16,0,1,0,0,0,70,18,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,52,0,0,7,
+18,0,16,0,1,0,0,0,10,0,16,0,1,0,0,0,
+1,64,0,0,0,0,0,0,56,0,0,7,114,0,16,0,
+1,0,0,0,6,0,16,0,1,0,0,0,70,18,16,0,
+3,0,0,0,56,0,0,10,114,0,16,0,1,0,0,0,
+70,2,16,0,1,0,0,0,70,130,32,6,0,0,0,0,
+10,0,0,0,58,0,16,0,0,0,0,0,56,0,0,7,
+114,0,16,0,1,0,0,0,246,15,16,0,1,0,0,0,
+70,2,16,0,1,0,0,0,51,0,0,10,114,0,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,2,64,0,0,
+0,0,128,63,0,0,128,63,0,0,128,63,0,0,0,0,
+0,0,0,7,114,0,16,0,0,0,0,0,70,2,16,0,
+0,0,0,0,70,2,16,0,1,0,0,0,30,0,0,7,
+130,0,16,0,0,0,0,0,58,0,16,0,0,0,0,0,
+1,64,0,0,1,0,0,0,22,0,0,1,54,0,0,5,
+114,32,16,0,0,0,0,0,70,2,16,0,0,0,0,0,
+54,0,0,5,114,32,16,0,1,0,0,0,70,18,16,0,
+4,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+32,0,0,0,3,0,0,0,0,0,0,0,7,0,0,0,
+20,0,0,0,2,0,0,0,1,0,0,0,1,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,188,4,0,0,1,0,0,0,
+108,0,0,0,1,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,142,4,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,85,110,105,116,121,70,70,86,
+101,114,116,101,120,0,171,171,92,0,0,0,14,0,0,0,
+132,0,0,0,240,3,0,0,0,0,0,0,0,0,0,0,
+180,2,0,0,0,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,240,2,0,0,64,0,0,0,
+64,0,0,0,0,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+253,2,0,0,128,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,56,3,0,0,144,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+71,3,0,0,160,0,0,0,128,0,0,0,2,0,0,0,
+88,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,124,3,0,0,32,1,0,0,
+128,0,0,0,2,0,0,0,140,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+176,3,0,0,160,1,0,0,128,0,0,0,2,0,0,0,
+192,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,228,3,0,0,32,2,0,0,
+128,0,0,0,2,0,0,0,244,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+24,4,0,0,160,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,39,4,0,0,176,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+54,4,0,0,192,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,66,4,0,0,208,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+82,4,0,0,224,2,0,0,0,1,0,0,0,0,0,0,
+96,4,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,132,4,0,0,224,3,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+102,102,95,109,97,116,114,105,120,95,109,118,112,0,102,108,
+111,97,116,52,120,52,0,171,3,0,3,0,4,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,194,2,0,0,102,102,95,109,
+97,116,114,105,120,95,109,118,0,102,102,95,118,101,99,95,
+99,111,108,111,114,0,102,108,111,97,116,52,0,171,171,171,
+1,0,3,0,1,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,118,101,99,95,97,109,98,105,101,
+110,116,0,102,102,95,108,105,103,104,116,95,99,111,108,111,
+114,0,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,112,111,115,0,171,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,97,116,116,101,110,0,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,115,112,111,116,0,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,109,97,116,95,100,105,102,102,117,
+115,101,0,102,102,95,109,97,116,95,97,109,98,105,101,110,
+116,0,102,102,95,109,97,116,95,115,112,101,99,0,102,102,
+95,109,97,116,95,101,109,105,115,115,105,111,110,0,102,102,
+95,109,97,116,114,105,120,95,116,101,120,0,3,0,3,0,
+4,0,4,0,4,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,194,2,0,0,
+102,102,95,102,111,103,95,118,115,0,77,105,99,114,111,115,
+111,102,116,32,40,82,41,32,72,76,83,76,32,83,104,97,
+100,101,114,32,67,111,109,112,105,108,101,114,32,54,46,51,
+46,57,52,49,53,46,48,0,76,70,83,48,164,1,0,0,
+7,0,0,0,8,0,0,0,88,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,106,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,118,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,128,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,136,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,149,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,3,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,159,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,5,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,67,111,109,112,117,116,101,83,
+112,111,116,76,105,103,104,116,51,0,101,121,101,80,111,115,
+105,116,105,111,110,0,101,121,101,78,111,114,109,97,108,0,
+118,105,101,119,68,105,114,0,100,105,102,102,117,115,101,67,
+111,108,111,114,0,115,112,101,99,67,111,108,111,114,0,97,
+109,98,0,171,76,73,66,70,80,16,0,0,68,88,66,67,
+170,38,218,251,246,216,107,194,180,253,56,94,218,55,46,138,
+1,0,0,0,80,16,0,0,5,0,0,0,52,0,0,0,
+156,3,0,0,96,9,0,0,220,9,0,0,160,14,0,0,
+65,111,110,57,96,3,0,0,96,3,0,0,0,2,86,76,
+48,3,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,0,0,0,0,
+45,0,0,0,0,0,0,0,0,2,86,76,81,0,0,5,
+45,0,15,160,0,0,0,0,0,0,0,0,0,0,128,63,
+0,0,0,0,48,0,0,5,0,0,15,240,3,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,2,
+5,0,0,128,0,0,15,144,31,0,0,2,5,0,1,128,
+1,0,15,144,31,0,0,2,5,0,2,128,2,0,15,144,
+31,0,0,2,5,0,3,128,3,0,15,144,31,0,0,2,
+5,0,4,128,4,0,15,144,31,0,0,2,5,0,5,128,
+5,0,15,144,1,0,0,2,0,0,7,128,4,0,228,144,
+1,0,0,2,1,0,7,128,5,0,228,144,27,0,0,2,
+0,8,228,240,0,0,228,240,4,0,0,6,2,0,7,128,
+0,0,228,144,18,32,255,161,0,8,228,240,18,32,228,160,
+0,8,228,240,8,0,0,3,0,0,8,128,2,0,228,128,
+2,0,228,128,7,0,0,2,1,0,8,128,0,0,255,128,
+4,0,0,4,3,0,7,128,2,0,228,128,1,0,255,128,
+2,0,228,144,36,0,0,2,4,0,7,128,3,0,228,128,
+8,0,0,3,2,0,8,128,1,0,228,144,4,0,228,128,
+5,0,0,5,3,0,1,128,18,32,255,160,0,8,228,240,
+18,32,255,160,0,8,228,240,12,0,0,3,3,0,1,128,
+3,0,0,129,3,0,0,128,12,0,0,4,3,0,2,128,
+26,32,255,160,0,8,228,240,0,0,255,128,5,0,0,3,
+3,0,1,128,3,0,85,128,3,0,0,128,5,0,0,4,
+0,0,8,128,0,0,255,128,26,32,170,160,0,8,228,240,
+2,0,0,3,0,0,8,128,0,0,255,128,45,0,170,160,
+6,0,0,2,0,0,8,128,0,0,255,128,4,0,0,4,
+0,0,8,128,3,0,0,128,0,0,255,129,0,0,255,128,
+11,0,0,3,2,0,8,128,2,0,255,128,45,0,85,160,
+32,0,0,3,3,0,1,128,2,0,255,128,44,0,255,160,
+10,0,0,3,2,0,8,128,3,0,0,128,45,0,170,160,
+5,0,0,3,2,0,7,128,1,0,255,128,2,0,228,128,
+8,0,0,3,1,0,8,128,1,0,228,144,2,0,228,128,
+8,0,0,4,2,0,1,128,2,0,228,128,34,32,228,160,
+0,8,228,240,11,0,0,3,2,0,1,128,2,0,0,128,
+45,0,85,160,2,0,0,4,2,0,1,128,2,0,0,128,
+26,32,0,161,0,8,228,240,5,0,0,4,2,0,1,128,
+2,0,0,128,26,32,85,160,0,8,228,240,11,0,0,3,
+2,0,1,128,2,0,0,128,45,0,85,160,10,0,0,3,
+2,0,1,128,2,0,0,128,45,0,170,160,5,0,0,3,
+0,0,8,128,0,0,255,128,2,0,0,128,11,0,0,3,
+1,0,8,128,1,0,255,128,45,0,85,160,12,0,0,3,
+2,0,1,128,45,0,85,160,1,0,255,128,5,0,0,3,
+2,0,2,128,2,0,255,128,0,0,255,128,5,0,0,4,
+2,0,14,128,2,0,85,128,10,32,144,160,0,8,228,240,
+4,0,0,4,0,0,7,128,2,0,0,128,2,0,249,128,
+0,0,228,128,5,0,0,3,2,0,7,128,1,0,255,128,
+3,0,228,144,5,0,0,4,2,0,7,128,2,0,228,128,
+10,32,228,160,0,8,228,240,5,0,0,3,2,0,7,128,
+0,0,255,128,2,0,228,128,10,0,0,3,2,0,7,128,
+2,0,228,128,45,0,170,160,2,0,0,3,1,0,7,128,
+1,0,228,128,2,0,228,128,29,0,0,0,1,0,0,2,
+1,0,7,224,0,0,228,128,1,0,0,2,0,0,7,224,
+1,0,228,128,255,255,0,0,83,72,68,82,188,5,0,0,
+64,0,240,255,111,1,0,0,89,8,0,4,70,142,32,0,
+0,0,0,0,45,0,0,0,95,0,0,3,114,16,16,0,
+0,0,0,0,95,0,0,3,114,16,16,0,1,0,0,0,
+95,0,0,3,114,16,16,0,2,0,0,0,95,0,0,3,
+114,16,16,0,3,0,0,0,95,0,0,3,114,16,16,0,
+4,0,0,0,95,0,0,3,114,16,16,0,5,0,0,0,
+101,0,0,3,114,32,16,0,0,0,0,0,101,0,0,3,
+114,32,16,0,1,0,0,0,104,0,0,2,4,0,0,0,
+54,0,0,5,114,0,16,0,0,0,0,0,70,18,16,0,
+4,0,0,0,54,0,0,5,114,0,16,0,1,0,0,0,
+70,18,16,0,5,0,0,0,54,0,0,5,130,0,16,0,
+0,0,0,0,1,64,0,0,0,0,0,0,48,0,0,1,
+33,0,0,7,130,0,16,0,1,0,0,0,58,0,16,0,
+0,0,0,0,1,64,0,0,3,0,0,0,3,0,4,3,
+58,0,16,0,1,0,0,0,50,0,0,16,114,0,16,0,
+2,0,0,0,70,18,16,128,65,0,0,0,0,0,0,0,
+246,143,32,6,0,0,0,0,18,0,0,0,58,0,16,0,
+0,0,0,0,70,130,32,6,0,0,0,0,18,0,0,0,
+58,0,16,0,0,0,0,0,16,0,0,7,130,0,16,0,
+1,0,0,0,70,2,16,0,2,0,0,0,70,2,16,0,
+2,0,0,0,50,0,0,12,130,0,16,0,2,0,0,0,
+42,128,32,6,0,0,0,0,26,0,0,0,58,0,16,0,
+0,0,0,0,58,0,16,0,1,0,0,0,1,64,0,0,
+0,0,128,63,14,0,0,10,130,0,16,0,2,0,0,0,
+2,64,0,0,0,0,128,63,0,0,128,63,0,0,128,63,
+0,0,128,63,58,0,16,0,2,0,0,0,57,0,0,10,
+18,0,16,0,3,0,0,0,1,64,0,0,0,0,0,0,
+58,128,32,6,0,0,0,0,18,0,0,0,58,0,16,0,
+0,0,0,0,49,0,0,10,34,0,16,0,3,0,0,0,
+58,128,32,6,0,0,0,0,26,0,0,0,58,0,16,0,
+0,0,0,0,58,0,16,0,1,0,0,0,1,0,0,7,
+18,0,16,0,3,0,0,0,26,0,16,0,3,0,0,0,
+10,0,16,0,3,0,0,0,55,0,0,9,130,0,16,0,
+2,0,0,0,10,0,16,0,3,0,0,0,1,64,0,0,
+0,0,0,0,58,0,16,0,2,0,0,0,68,0,0,5,
+130,0,16,0,1,0,0,0,58,0,16,0,1,0,0,0,
+56,0,0,7,114,0,16,0,3,0,0,0,246,15,16,0,
+1,0,0,0,70,2,16,0,2,0,0,0,16,0,0,10,
+130,0,16,0,3,0,0,0,70,2,16,0,3,0,0,0,
+70,130,32,6,0,0,0,0,34,0,0,0,58,0,16,0,
+0,0,0,0,52,0,0,7,130,0,16,0,3,0,0,0,
+58,0,16,0,3,0,0,0,1,64,0,0,0,0,0,0,
+0,0,0,11,130,0,16,0,3,0,0,0,58,0,16,0,
+3,0,0,0,10,128,32,134,65,0,0,0,0,0,0,0,
+26,0,0,0,58,0,16,0,0,0,0,0,56,32,0,10,
+130,0,16,0,3,0,0,0,58,0,16,0,3,0,0,0,
+26,128,32,6,0,0,0,0,26,0,0,0,58,0,16,0,
+0,0,0,0,56,0,0,7,130,0,16,0,2,0,0,0,
+58,0,16,0,2,0,0,0,58,0,16,0,3,0,0,0,
+16,0,0,7,18,0,16,0,3,0,0,0,70,18,16,0,
+1,0,0,0,70,2,16,0,3,0,0,0,52,0,0,7,
+18,0,16,0,3,0,0,0,10,0,16,0,3,0,0,0,
+1,64,0,0,0,0,0,0,56,0,0,7,226,0,16,0,
+3,0,0,0,6,0,16,0,3,0,0,0,6,25,16,0,
+3,0,0,0,56,0,0,10,226,0,16,0,3,0,0,0,
+86,14,16,0,3,0,0,0,6,137,32,6,0,0,0,0,
+10,0,0,0,58,0,16,0,0,0,0,0,49,0,0,7,
+18,0,16,0,3,0,0,0,1,64,0,0,0,0,0,0,
+10,0,16,0,3,0,0,0,31,0,4,3,10,0,16,0,
+3,0,0,0,50,0,0,9,114,0,16,0,2,0,0,0,
+70,2,16,0,2,0,0,0,246,15,16,0,1,0,0,0,
+70,18,16,0,2,0,0,0,16,0,0,7,130,0,16,0,
+1,0,0,0,70,2,16,0,2,0,0,0,70,2,16,0,
+2,0,0,0,68,0,0,5,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,56,0,0,7,114,0,16,0,
+2,0,0,0,246,15,16,0,1,0,0,0,70,2,16,0,
+2,0,0,0,16,0,0,7,130,0,16,0,1,0,0,0,
+70,18,16,0,1,0,0,0,70,2,16,0,2,0,0,0,
+52,0,0,7,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,1,64,0,0,0,0,0,0,47,0,0,5,
+130,0,16,0,1,0,0,0,58,0,16,0,1,0,0,0,
+56,0,0,8,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,58,128,32,0,0,0,0,0,44,0,0,0,
+25,0,0,5,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,51,0,0,7,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,1,64,0,0,0,0,128,63,
+56,0,0,7,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,58,0,16,0,2,0,0,0,50,0,0,12,
+114,0,16,0,0,0,0,0,246,15,16,0,1,0,0,0,
+70,130,32,6,0,0,0,0,10,0,0,0,58,0,16,0,
+0,0,0,0,70,2,16,0,0,0,0,0,21,0,0,1,
+56,0,0,7,114,0,16,0,2,0,0,0,246,15,16,0,
+2,0,0,0,150,7,16,0,3,0,0,0,51,0,0,10,
+114,0,16,0,2,0,0,0,70,2,16,0,2,0,0,0,
+2,64,0,0,0,0,128,63,0,0,128,63,0,0,128,63,
+0,0,0,0,0,0,0,7,114,0,16,0,1,0,0,0,
+70,2,16,0,1,0,0,0,70,2,16,0,2,0,0,0,
+30,0,0,7,130,0,16,0,0,0,0,0,58,0,16,0,
+0,0,0,0,1,64,0,0,1,0,0,0,22,0,0,1,
+54,0,0,5,114,32,16,0,1,0,0,0,70,2,16,0,
+0,0,0,0,54,0,0,5,114,32,16,0,0,0,0,0,
+70,2,16,0,1,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,48,0,0,0,4,0,0,0,0,0,0,0,
+8,0,0,0,33,0,0,0,2,0,0,0,1,0,0,0,
+1,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+5,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,188,4,0,0,
+1,0,0,0,108,0,0,0,1,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,142,4,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,92,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,85,110,105,116,
+121,70,70,86,101,114,116,101,120,0,171,171,92,0,0,0,
+14,0,0,0,132,0,0,0,240,3,0,0,0,0,0,0,
+0,0,0,0,180,2,0,0,0,0,0,0,64,0,0,0,
+0,0,0,0,204,2,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,240,2,0,0,
+64,0,0,0,64,0,0,0,0,0,0,0,204,2,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,253,2,0,0,128,0,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,56,3,0,0,
+144,0,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,71,3,0,0,160,0,0,0,128,0,0,0,
+2,0,0,0,88,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,124,3,0,0,
+32,1,0,0,128,0,0,0,2,0,0,0,140,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,176,3,0,0,160,1,0,0,128,0,0,0,
+2,0,0,0,192,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,228,3,0,0,
+32,2,0,0,128,0,0,0,2,0,0,0,244,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,24,4,0,0,160,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,39,4,0,0,
+176,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,54,4,0,0,192,2,0,0,16,0,0,0,
+2,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,66,4,0,0,
+208,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,82,4,0,0,224,2,0,0,0,1,0,0,
+0,0,0,0,96,4,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,132,4,0,0,
+224,3,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,102,102,95,109,97,116,114,105,120,95,109,118,
+112,0,102,108,111,97,116,52,120,52,0,171,3,0,3,0,
+4,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,194,2,0,0,
+102,102,95,109,97,116,114,105,120,95,109,118,0,102,102,95,
+118,101,99,95,99,111,108,111,114,0,102,108,111,97,116,52,
+0,171,171,171,1,0,3,0,1,0,4,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,118,101,99,95,97,
+109,98,105,101,110,116,0,102,102,95,108,105,103,104,116,95,
+99,111,108,111,114,0,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,112,111,115,0,171,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,97,116,116,101,110,0,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,115,112,111,
+116,0,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,109,97,116,95,100,
+105,102,102,117,115,101,0,102,102,95,109,97,116,95,97,109,
+98,105,101,110,116,0,102,102,95,109,97,116,95,115,112,101,
+99,0,102,102,95,109,97,116,95,101,109,105,115,115,105,111,
+110,0,102,102,95,109,97,116,114,105,120,95,116,101,120,0,
+3,0,3,0,4,0,4,0,4,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+194,2,0,0,102,102,95,102,111,103,95,118,115,0,77,105,
+99,114,111,115,111,102,116,32,40,82,41,32,72,76,83,76,
+32,83,104,97,100,101,114,32,67,111,109,112,105,108,101,114,
+32,54,46,51,46,57,52,49,53,46,48,0,76,70,83,48,
+168,1,0,0,7,0,0,0,8,0,0,0,88,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,2,0,0,0,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,110,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,122,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,132,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,140,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,153,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,3,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,163,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,5,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,67,111,109,112,
+117,116,101,83,112,111,116,76,105,103,104,116,83,112,101,99,
+51,0,101,121,101,80,111,115,105,116,105,111,110,0,101,121,
+101,78,111,114,109,97,108,0,118,105,101,119,68,105,114,0,
+100,105,102,102,117,115,101,67,111,108,111,114,0,115,112,101,
+99,67,111,108,111,114,0,97,109,98,0,171,76,73,66,70,
+48,18,0,0,68,88,66,67,2,125,230,23,98,206,70,179,
+136,149,189,146,48,38,11,143,1,0,0,0,48,18,0,0,
+5,0,0,0,52,0,0,0,36,7,0,0,68,11,0,0,
+192,11,0,0,132,16,0,0,65,111,110,57,232,6,0,0,
+232,6,0,0,0,2,86,76,148,6,0,0,84,0,0,0,
+4,0,36,0,0,0,84,0,0,0,84,0,0,0,36,0,
+0,0,84,0,0,0,10,0,4,0,0,0,0,0,0,0,
+0,0,18,0,4,0,4,0,0,0,0,0,0,0,26,0,
+4,0,8,0,0,0,0,0,0,0,34,0,4,0,12,0,
+0,0,0,0,0,2,86,76,81,0,0,5,16,0,15,160,
+0,0,128,63,0,0,0,0,0,0,0,0,0,0,0,0,
+31,0,0,2,5,0,0,128,0,0,15,144,31,0,0,2,
+5,0,1,128,1,0,15,144,31,0,0,2,5,0,3,128,
+3,0,15,144,31,0,0,2,5,0,4,128,4,0,15,144,
+31,0,0,2,5,0,5,128,5,0,15,144,5,0,0,3,
+0,0,1,128,4,0,255,160,4,0,255,160,12,0,0,3,
+0,0,1,128,0,0,0,129,0,0,0,128,4,0,0,4,
+0,0,14,128,0,0,144,144,4,0,255,161,4,0,144,160,
+8,0,0,3,1,0,1,128,0,0,249,128,0,0,249,128,
+12,0,0,3,1,0,2,128,8,0,255,160,1,0,0,128,
+5,0,0,3,0,0,1,128,0,0,0,128,1,0,85,128,
+1,0,0,2,2,0,1,128,16,0,0,160,4,0,0,4,
+1,0,2,128,8,0,170,160,1,0,0,128,2,0,0,128,
+7,0,0,2,1,0,1,128,1,0,0,128,5,0,0,3,
+0,0,14,128,0,0,228,128,1,0,0,128,6,0,0,2,
+1,0,1,128,1,0,85,128,4,0,0,4,0,0,1,128,
+0,0,0,128,1,0,0,129,1,0,0,128,8,0,0,3,
+1,0,1,128,0,0,249,128,12,0,228,160,8,0,0,3,
+0,0,2,128,1,0,228,144,0,0,249,128,11,0,0,3,
+0,0,2,128,0,0,85,128,16,0,85,160,5,0,0,3,
+0,0,14,128,0,0,85,128,3,0,144,144,5,0,0,3,
+0,0,14,128,0,0,228,128,0,0,144,160,11,0,0,3,
+1,0,1,128,1,0,0,128,16,0,85,160,2,0,0,3,
+1,0,1,128,1,0,0,128,8,0,0,161,5,0,0,3,
+1,0,1,128,1,0,0,128,8,0,85,160,11,0,0,3,
+1,0,1,128,1,0,0,128,16,0,85,160,10,0,0,3,
+1,0,1,128,1,0,0,128,16,0,0,160,5,0,0,3,
+0,0,1,128,0,0,0,128,1,0,0,128,5,0,0,3,
+0,0,7,128,0,0,0,128,0,0,249,128,10,0,0,3,
+0,0,7,128,0,0,228,128,16,0,0,160,2,0,0,3,
+0,0,7,128,0,0,228,128,5,0,228,144,5,0,0,3,
+0,0,8,128,5,0,255,160,5,0,255,160,12,0,0,3,
+0,0,8,128,0,0,255,129,0,0,255,128,4,0,0,4,
+1,0,7,128,0,0,228,144,5,0,255,161,5,0,228,160,
+8,0,0,3,1,0,8,128,1,0,228,128,1,0,228,128,
+12,0,0,3,2,0,2,128,9,0,255,160,1,0,255,128,
+5,0,0,3,0,0,8,128,0,0,255,128,2,0,85,128,
+4,0,0,4,2,0,2,128,9,0,170,160,1,0,255,128,
+2,0,0,128,7,0,0,2,1,0,8,128,1,0,255,128,
+5,0,0,3,1,0,7,128,1,0,255,128,1,0,228,128,
+6,0,0,2,1,0,8,128,2,0,85,128,4,0,0,4,
+0,0,8,128,0,0,255,128,1,0,255,129,1,0,255,128,
+8,0,0,3,1,0,8,128,1,0,228,128,13,0,228,160,
+8,0,0,3,1,0,1,128,1,0,228,144,1,0,228,128,
+11,0,0,3,1,0,9,128,1,0,228,128,16,0,85,160,
+5,0,0,3,1,0,7,128,1,0,0,128,3,0,228,144,
+5,0,0,3,1,0,7,128,1,0,228,128,1,0,228,160,
+2,0,0,3,1,0,8,128,1,0,255,128,9,0,0,161,
+5,0,0,3,1,0,8,128,1,0,255,128,9,0,85,160,
+11,0,0,3,1,0,8,128,1,0,255,128,16,0,85,160,
+10,0,0,3,1,0,8,128,1,0,255,128,16,0,0,160,
+5,0,0,3,0,0,8,128,0,0,255,128,1,0,255,128,
+5,0,0,3,1,0,7,128,0,0,255,128,1,0,228,128,
+10,0,0,3,1,0,7,128,1,0,228,128,16,0,0,160,
+2,0,0,3,0,0,7,128,0,0,228,128,1,0,228,128,
+5,0,0,3,0,0,8,128,6,0,255,160,6,0,255,160,
+12,0,0,3,0,0,8,128,0,0,255,129,0,0,255,128,
+4,0,0,4,1,0,7,128,0,0,228,144,6,0,255,161,
+6,0,228,160,8,0,0,3,1,0,8,128,1,0,228,128,
+1,0,228,128,12,0,0,3,2,0,2,128,10,0,255,160,
+1,0,255,128,5,0,0,3,0,0,8,128,0,0,255,128,
+2,0,85,128,4,0,0,4,2,0,2,128,10,0,170,160,
+1,0,255,128,2,0,0,128,7,0,0,2,1,0,8,128,
+1,0,255,128,5,0,0,3,1,0,7,128,1,0,255,128,
+1,0,228,128,6,0,0,2,1,0,8,128,2,0,85,128,
+4,0,0,4,0,0,8,128,0,0,255,128,1,0,255,129,
+1,0,255,128,8,0,0,3,1,0,8,128,1,0,228,128,
+14,0,228,160,8,0,0,3,1,0,1,128,1,0,228,144,
+1,0,228,128,11,0,0,3,1,0,9,128,1,0,228,128,
+16,0,85,160,5,0,0,3,1,0,7,128,1,0,0,128,
+3,0,228,144,5,0,0,3,1,0,7,128,1,0,228,128,
+2,0,228,160,2,0,0,3,1,0,8,128,1,0,255,128,
+10,0,0,161,5,0,0,3,1,0,8,128,1,0,255,128,
+10,0,85,160,11,0,0,3,1,0,8,128,1,0,255,128,
+16,0,85,160,10,0,0,3,1,0,8,128,1,0,255,128,
+16,0,0,160,5,0,0,3,0,0,8,128,0,0,255,128,
+1,0,255,128,5,0,0,3,1,0,7,128,0,0,255,128,
+1,0,228,128,10,0,0,3,1,0,7,128,1,0,228,128,
+16,0,0,160,2,0,0,3,0,0,7,128,0,0,228,128,
+1,0,228,128,5,0,0,3,0,0,8,128,7,0,255,160,
+7,0,255,160,12,0,0,3,0,0,8,128,0,0,255,129,
+0,0,255,128,4,0,0,4,1,0,7,128,0,0,228,144,
+7,0,255,161,7,0,228,160,8,0,0,3,1,0,8,128,
+1,0,228,128,1,0,228,128,12,0,0,3,2,0,2,128,
+11,0,255,160,1,0,255,128,5,0,0,3,0,0,8,128,
+0,0,255,128,2,0,85,128,4,0,0,4,2,0,1,128,
+11,0,170,160,1,0,255,128,2,0,0,128,7,0,0,2,
+1,0,8,128,1,0,255,128,5,0,0,3,1,0,7,128,
+1,0,255,128,1,0,228,128,6,0,0,2,1,0,8,128,
+2,0,0,128,4,0,0,4,0,0,8,128,0,0,255,128,
+1,0,255,129,1,0,255,128,8,0,0,3,1,0,8,128,
+1,0,228,128,15,0,228,160,8,0,0,3,1,0,1,128,
+1,0,228,144,1,0,228,128,11,0,0,3,1,0,9,128,
+1,0,228,128,16,0,85,160,5,0,0,3,1,0,7,128,
+1,0,0,128,3,0,228,144,5,0,0,3,1,0,7,128,
+1,0,228,128,3,0,228,160,2,0,0,3,1,0,8,128,
+1,0,255,128,11,0,0,161,5,0,0,3,1,0,8,128,
+1,0,255,128,11,0,85,160,11,0,0,3,1,0,8,128,
+1,0,255,128,16,0,85,160,10,0,0,3,1,0,8,128,
+1,0,255,128,16,0,0,160,5,0,0,3,0,0,8,128,
+0,0,255,128,1,0,255,128,5,0,0,3,1,0,7,128,
+0,0,255,128,1,0,228,128,10,0,0,3,1,0,7,128,
+1,0,228,128,16,0,0,160,2,0,0,3,0,0,7,224,
+0,0,228,128,1,0,228,128,1,0,0,2,1,0,7,224,
+4,0,228,144,255,255,0,0,83,72,68,82,24,4,0,0,
+64,0,240,255,6,1,0,0,89,8,0,4,70,142,32,0,
+0,0,0,0,39,0,0,0,95,0,0,3,114,16,16,0,
+0,0,0,0,95,0,0,3,114,16,16,0,1,0,0,0,
+95,0,0,3,114,16,16,0,3,0,0,0,95,0,0,3,
+114,16,16,0,4,0,0,0,95,0,0,3,114,16,16,0,
+5,0,0,0,101,0,0,3,114,32,16,0,0,0,0,0,
+101,0,0,3,114,32,16,0,1,0,0,0,104,0,0,2,
+3,0,0,0,54,0,0,5,114,0,16,0,0,0,0,0,
+70,18,16,0,5,0,0,0,54,0,0,5,130,0,16,0,
+0,0,0,0,1,64,0,0,0,0,0,0,48,0,0,1,
+33,0,0,7,18,0,16,0,1,0,0,0,58,0,16,0,
+0,0,0,0,1,64,0,0,4,0,0,0,3,0,4,3,
+10,0,16,0,1,0,0,0,50,0,0,16,114,0,16,0,
+1,0,0,0,70,18,16,128,65,0,0,0,0,0,0,0,
+246,143,32,6,0,0,0,0,18,0,0,0,58,0,16,0,
+0,0,0,0,70,130,32,6,0,0,0,0,18,0,0,0,
+58,0,16,0,0,0,0,0,16,0,0,7,130,0,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,70,2,16,0,
+1,0,0,0,50,0,0,12,18,0,16,0,2,0,0,0,
+42,128,32,6,0,0,0,0,26,0,0,0,58,0,16,0,
+0,0,0,0,58,0,16,0,1,0,0,0,1,64,0,0,
+0,0,128,63,14,0,0,10,18,0,16,0,2,0,0,0,
+2,64,0,0,0,0,128,63,0,0,128,63,0,0,128,63,
+0,0,128,63,10,0,16,0,2,0,0,0,57,0,0,10,
+34,0,16,0,2,0,0,0,1,64,0,0,0,0,0,0,
+58,128,32,6,0,0,0,0,18,0,0,0,58,0,16,0,
+0,0,0,0,49,0,0,10,66,0,16,0,2,0,0,0,
+58,128,32,6,0,0,0,0,26,0,0,0,58,0,16,0,
+0,0,0,0,58,0,16,0,1,0,0,0,1,0,0,7,
+34,0,16,0,2,0,0,0,42,0,16,0,2,0,0,0,
+26,0,16,0,2,0,0,0,55,0,0,9,18,0,16,0,
+2,0,0,0,26,0,16,0,2,0,0,0,1,64,0,0,
+0,0,0,0,10,0,16,0,2,0,0,0,68,0,0,5,
+130,0,16,0,1,0,0,0,58,0,16,0,1,0,0,0,
+56,0,0,7,114,0,16,0,1,0,0,0,246,15,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,16,0,0,10,
+130,0,16,0,1,0,0,0,70,2,16,0,1,0,0,0,
+70,130,32,6,0,0,0,0,34,0,0,0,58,0,16,0,
+0,0,0,0,52,0,0,7,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,1,64,0,0,0,0,0,0,
+0,0,0,11,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,10,128,32,134,65,0,0,0,0,0,0,0,
+26,0,0,0,58,0,16,0,0,0,0,0,56,32,0,10,
+130,0,16,0,1,0,0,0,58,0,16,0,1,0,0,0,
+26,128,32,6,0,0,0,0,26,0,0,0,58,0,16,0,
+0,0,0,0,56,0,0,7,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,10,0,16,0,2,0,0,0,
+16,0,0,7,18,0,16,0,1,0,0,0,70,18,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,52,0,0,7,
+18,0,16,0,1,0,0,0,10,0,16,0,1,0,0,0,
+1,64,0,0,0,0,0,0,56,0,0,7,114,0,16,0,
+1,0,0,0,6,0,16,0,1,0,0,0,70,18,16,0,
+3,0,0,0,56,0,0,10,114,0,16,0,1,0,0,0,
+70,2,16,0,1,0,0,0,70,130,32,6,0,0,0,0,
+10,0,0,0,58,0,16,0,0,0,0,0,56,0,0,7,
+114,0,16,0,1,0,0,0,246,15,16,0,1,0,0,0,
+70,2,16,0,1,0,0,0,51,0,0,10,114,0,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,2,64,0,0,
+0,0,128,63,0,0,128,63,0,0,128,63,0,0,0,0,
+0,0,0,7,114,0,16,0,0,0,0,0,70,2,16,0,
+0,0,0,0,70,2,16,0,1,0,0,0,30,0,0,7,
+130,0,16,0,0,0,0,0,58,0,16,0,0,0,0,0,
+1,64,0,0,1,0,0,0,22,0,0,1,54,0,0,5,
+114,32,16,0,0,0,0,0,70,2,16,0,0,0,0,0,
+54,0,0,5,114,32,16,0,1,0,0,0,70,18,16,0,
+4,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+32,0,0,0,3,0,0,0,0,0,0,0,7,0,0,0,
+20,0,0,0,2,0,0,0,1,0,0,0,1,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,188,4,0,0,1,0,0,0,
+108,0,0,0,1,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,142,4,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,85,110,105,116,121,70,70,86,
+101,114,116,101,120,0,171,171,92,0,0,0,14,0,0,0,
+132,0,0,0,240,3,0,0,0,0,0,0,0,0,0,0,
+180,2,0,0,0,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,240,2,0,0,64,0,0,0,
+64,0,0,0,0,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+253,2,0,0,128,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,56,3,0,0,144,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+71,3,0,0,160,0,0,0,128,0,0,0,2,0,0,0,
+88,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,124,3,0,0,32,1,0,0,
+128,0,0,0,2,0,0,0,140,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+176,3,0,0,160,1,0,0,128,0,0,0,2,0,0,0,
+192,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,228,3,0,0,32,2,0,0,
+128,0,0,0,2,0,0,0,244,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+24,4,0,0,160,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,39,4,0,0,176,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+54,4,0,0,192,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,66,4,0,0,208,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+82,4,0,0,224,2,0,0,0,1,0,0,0,0,0,0,
+96,4,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,132,4,0,0,224,3,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+102,102,95,109,97,116,114,105,120,95,109,118,112,0,102,108,
+111,97,116,52,120,52,0,171,3,0,3,0,4,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,194,2,0,0,102,102,95,109,
+97,116,114,105,120,95,109,118,0,102,102,95,118,101,99,95,
+99,111,108,111,114,0,102,108,111,97,116,52,0,171,171,171,
+1,0,3,0,1,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,118,101,99,95,97,109,98,105,101,
+110,116,0,102,102,95,108,105,103,104,116,95,99,111,108,111,
+114,0,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,112,111,115,0,171,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,97,116,116,101,110,0,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,115,112,111,116,0,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,109,97,116,95,100,105,102,102,117,
+115,101,0,102,102,95,109,97,116,95,97,109,98,105,101,110,
+116,0,102,102,95,109,97,116,95,115,112,101,99,0,102,102,
+95,109,97,116,95,101,109,105,115,115,105,111,110,0,102,102,
+95,109,97,116,114,105,120,95,116,101,120,0,3,0,3,0,
+4,0,4,0,4,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,194,2,0,0,
+102,102,95,102,111,103,95,118,115,0,77,105,99,114,111,115,
+111,102,116,32,40,82,41,32,72,76,83,76,32,83,104,97,
+100,101,114,32,67,111,109,112,105,108,101,114,32,54,46,51,
+46,57,52,49,53,46,48,0,76,70,83,48,164,1,0,0,
+7,0,0,0,8,0,0,0,88,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,106,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,118,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,128,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,136,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,149,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,3,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,159,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,5,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,67,111,109,112,117,116,101,83,
+112,111,116,76,105,103,104,116,52,0,101,121,101,80,111,115,
+105,116,105,111,110,0,101,121,101,78,111,114,109,97,108,0,
+118,105,101,119,68,105,114,0,100,105,102,102,117,115,101,67,
+111,108,111,114,0,115,112,101,99,67,111,108,111,114,0,97,
+109,98,0,171,76,73,66,70,80,16,0,0,68,88,66,67,
+50,83,242,196,114,66,252,96,155,185,91,130,235,4,7,30,
+1,0,0,0,80,16,0,0,5,0,0,0,52,0,0,0,
+156,3,0,0,96,9,0,0,220,9,0,0,160,14,0,0,
+65,111,110,57,96,3,0,0,96,3,0,0,0,2,86,76,
+48,3,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,0,0,0,0,
+45,0,0,0,0,0,0,0,0,2,86,76,81,0,0,5,
+45,0,15,160,0,0,0,0,0,0,0,0,0,0,128,63,
+0,0,0,0,48,0,0,5,0,0,15,240,4,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,2,
+5,0,0,128,0,0,15,144,31,0,0,2,5,0,1,128,
+1,0,15,144,31,0,0,2,5,0,2,128,2,0,15,144,
+31,0,0,2,5,0,3,128,3,0,15,144,31,0,0,2,
+5,0,4,128,4,0,15,144,31,0,0,2,5,0,5,128,
+5,0,15,144,1,0,0,2,0,0,7,128,4,0,228,144,
+1,0,0,2,1,0,7,128,5,0,228,144,27,0,0,2,
+0,8,228,240,0,0,228,240,4,0,0,6,2,0,7,128,
+0,0,228,144,18,32,255,161,0,8,228,240,18,32,228,160,
+0,8,228,240,8,0,0,3,0,0,8,128,2,0,228,128,
+2,0,228,128,7,0,0,2,1,0,8,128,0,0,255,128,
+4,0,0,4,3,0,7,128,2,0,228,128,1,0,255,128,
+2,0,228,144,36,0,0,2,4,0,7,128,3,0,228,128,
+8,0,0,3,2,0,8,128,1,0,228,144,4,0,228,128,
+5,0,0,5,3,0,1,128,18,32,255,160,0,8,228,240,
+18,32,255,160,0,8,228,240,12,0,0,3,3,0,1,128,
+3,0,0,129,3,0,0,128,12,0,0,4,3,0,2,128,
+26,32,255,160,0,8,228,240,0,0,255,128,5,0,0,3,
+3,0,1,128,3,0,85,128,3,0,0,128,5,0,0,4,
+0,0,8,128,0,0,255,128,26,32,170,160,0,8,228,240,
+2,0,0,3,0,0,8,128,0,0,255,128,45,0,170,160,
+6,0,0,2,0,0,8,128,0,0,255,128,4,0,0,4,
+0,0,8,128,3,0,0,128,0,0,255,129,0,0,255,128,
+11,0,0,3,2,0,8,128,2,0,255,128,45,0,85,160,
+32,0,0,3,3,0,1,128,2,0,255,128,44,0,255,160,
+10,0,0,3,2,0,8,128,3,0,0,128,45,0,170,160,
+5,0,0,3,2,0,7,128,1,0,255,128,2,0,228,128,
+8,0,0,3,1,0,8,128,1,0,228,144,2,0,228,128,
+8,0,0,4,2,0,1,128,2,0,228,128,34,32,228,160,
+0,8,228,240,11,0,0,3,2,0,1,128,2,0,0,128,
+45,0,85,160,2,0,0,4,2,0,1,128,2,0,0,128,
+26,32,0,161,0,8,228,240,5,0,0,4,2,0,1,128,
+2,0,0,128,26,32,85,160,0,8,228,240,11,0,0,3,
+2,0,1,128,2,0,0,128,45,0,85,160,10,0,0,3,
+2,0,1,128,2,0,0,128,45,0,170,160,5,0,0,3,
+0,0,8,128,0,0,255,128,2,0,0,128,11,0,0,3,
+1,0,8,128,1,0,255,128,45,0,85,160,12,0,0,3,
+2,0,1,128,45,0,85,160,1,0,255,128,5,0,0,3,
+2,0,2,128,2,0,255,128,0,0,255,128,5,0,0,4,
+2,0,14,128,2,0,85,128,10,32,144,160,0,8,228,240,
+4,0,0,4,0,0,7,128,2,0,0,128,2,0,249,128,
+0,0,228,128,5,0,0,3,2,0,7,128,1,0,255,128,
+3,0,228,144,5,0,0,4,2,0,7,128,2,0,228,128,
+10,32,228,160,0,8,228,240,5,0,0,3,2,0,7,128,
+0,0,255,128,2,0,228,128,10,0,0,3,2,0,7,128,
+2,0,228,128,45,0,170,160,2,0,0,3,1,0,7,128,
+1,0,228,128,2,0,228,128,29,0,0,0,1,0,0,2,
+1,0,7,224,0,0,228,128,1,0,0,2,0,0,7,224,
+1,0,228,128,255,255,0,0,83,72,68,82,188,5,0,0,
+64,0,240,255,111,1,0,0,89,8,0,4,70,142,32,0,
+0,0,0,0,45,0,0,0,95,0,0,3,114,16,16,0,
+0,0,0,0,95,0,0,3,114,16,16,0,1,0,0,0,
+95,0,0,3,114,16,16,0,2,0,0,0,95,0,0,3,
+114,16,16,0,3,0,0,0,95,0,0,3,114,16,16,0,
+4,0,0,0,95,0,0,3,114,16,16,0,5,0,0,0,
+101,0,0,3,114,32,16,0,0,0,0,0,101,0,0,3,
+114,32,16,0,1,0,0,0,104,0,0,2,4,0,0,0,
+54,0,0,5,114,0,16,0,0,0,0,0,70,18,16,0,
+4,0,0,0,54,0,0,5,114,0,16,0,1,0,0,0,
+70,18,16,0,5,0,0,0,54,0,0,5,130,0,16,0,
+0,0,0,0,1,64,0,0,0,0,0,0,48,0,0,1,
+33,0,0,7,130,0,16,0,1,0,0,0,58,0,16,0,
+0,0,0,0,1,64,0,0,4,0,0,0,3,0,4,3,
+58,0,16,0,1,0,0,0,50,0,0,16,114,0,16,0,
+2,0,0,0,70,18,16,128,65,0,0,0,0,0,0,0,
+246,143,32,6,0,0,0,0,18,0,0,0,58,0,16,0,
+0,0,0,0,70,130,32,6,0,0,0,0,18,0,0,0,
+58,0,16,0,0,0,0,0,16,0,0,7,130,0,16,0,
+1,0,0,0,70,2,16,0,2,0,0,0,70,2,16,0,
+2,0,0,0,50,0,0,12,130,0,16,0,2,0,0,0,
+42,128,32,6,0,0,0,0,26,0,0,0,58,0,16,0,
+0,0,0,0,58,0,16,0,1,0,0,0,1,64,0,0,
+0,0,128,63,14,0,0,10,130,0,16,0,2,0,0,0,
+2,64,0,0,0,0,128,63,0,0,128,63,0,0,128,63,
+0,0,128,63,58,0,16,0,2,0,0,0,57,0,0,10,
+18,0,16,0,3,0,0,0,1,64,0,0,0,0,0,0,
+58,128,32,6,0,0,0,0,18,0,0,0,58,0,16,0,
+0,0,0,0,49,0,0,10,34,0,16,0,3,0,0,0,
+58,128,32,6,0,0,0,0,26,0,0,0,58,0,16,0,
+0,0,0,0,58,0,16,0,1,0,0,0,1,0,0,7,
+18,0,16,0,3,0,0,0,26,0,16,0,3,0,0,0,
+10,0,16,0,3,0,0,0,55,0,0,9,130,0,16,0,
+2,0,0,0,10,0,16,0,3,0,0,0,1,64,0,0,
+0,0,0,0,58,0,16,0,2,0,0,0,68,0,0,5,
+130,0,16,0,1,0,0,0,58,0,16,0,1,0,0,0,
+56,0,0,7,114,0,16,0,3,0,0,0,246,15,16,0,
+1,0,0,0,70,2,16,0,2,0,0,0,16,0,0,10,
+130,0,16,0,3,0,0,0,70,2,16,0,3,0,0,0,
+70,130,32,6,0,0,0,0,34,0,0,0,58,0,16,0,
+0,0,0,0,52,0,0,7,130,0,16,0,3,0,0,0,
+58,0,16,0,3,0,0,0,1,64,0,0,0,0,0,0,
+0,0,0,11,130,0,16,0,3,0,0,0,58,0,16,0,
+3,0,0,0,10,128,32,134,65,0,0,0,0,0,0,0,
+26,0,0,0,58,0,16,0,0,0,0,0,56,32,0,10,
+130,0,16,0,3,0,0,0,58,0,16,0,3,0,0,0,
+26,128,32,6,0,0,0,0,26,0,0,0,58,0,16,0,
+0,0,0,0,56,0,0,7,130,0,16,0,2,0,0,0,
+58,0,16,0,2,0,0,0,58,0,16,0,3,0,0,0,
+16,0,0,7,18,0,16,0,3,0,0,0,70,18,16,0,
+1,0,0,0,70,2,16,0,3,0,0,0,52,0,0,7,
+18,0,16,0,3,0,0,0,10,0,16,0,3,0,0,0,
+1,64,0,0,0,0,0,0,56,0,0,7,226,0,16,0,
+3,0,0,0,6,0,16,0,3,0,0,0,6,25,16,0,
+3,0,0,0,56,0,0,10,226,0,16,0,3,0,0,0,
+86,14,16,0,3,0,0,0,6,137,32,6,0,0,0,0,
+10,0,0,0,58,0,16,0,0,0,0,0,49,0,0,7,
+18,0,16,0,3,0,0,0,1,64,0,0,0,0,0,0,
+10,0,16,0,3,0,0,0,31,0,4,3,10,0,16,0,
+3,0,0,0,50,0,0,9,114,0,16,0,2,0,0,0,
+70,2,16,0,2,0,0,0,246,15,16,0,1,0,0,0,
+70,18,16,0,2,0,0,0,16,0,0,7,130,0,16,0,
+1,0,0,0,70,2,16,0,2,0,0,0,70,2,16,0,
+2,0,0,0,68,0,0,5,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,56,0,0,7,114,0,16,0,
+2,0,0,0,246,15,16,0,1,0,0,0,70,2,16,0,
+2,0,0,0,16,0,0,7,130,0,16,0,1,0,0,0,
+70,18,16,0,1,0,0,0,70,2,16,0,2,0,0,0,
+52,0,0,7,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,1,64,0,0,0,0,0,0,47,0,0,5,
+130,0,16,0,1,0,0,0,58,0,16,0,1,0,0,0,
+56,0,0,8,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,58,128,32,0,0,0,0,0,44,0,0,0,
+25,0,0,5,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,51,0,0,7,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,1,64,0,0,0,0,128,63,
+56,0,0,7,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,58,0,16,0,2,0,0,0,50,0,0,12,
+114,0,16,0,0,0,0,0,246,15,16,0,1,0,0,0,
+70,130,32,6,0,0,0,0,10,0,0,0,58,0,16,0,
+0,0,0,0,70,2,16,0,0,0,0,0,21,0,0,1,
+56,0,0,7,114,0,16,0,2,0,0,0,246,15,16,0,
+2,0,0,0,150,7,16,0,3,0,0,0,51,0,0,10,
+114,0,16,0,2,0,0,0,70,2,16,0,2,0,0,0,
+2,64,0,0,0,0,128,63,0,0,128,63,0,0,128,63,
+0,0,0,0,0,0,0,7,114,0,16,0,1,0,0,0,
+70,2,16,0,1,0,0,0,70,2,16,0,2,0,0,0,
+30,0,0,7,130,0,16,0,0,0,0,0,58,0,16,0,
+0,0,0,0,1,64,0,0,1,0,0,0,22,0,0,1,
+54,0,0,5,114,32,16,0,1,0,0,0,70,2,16,0,
+0,0,0,0,54,0,0,5,114,32,16,0,0,0,0,0,
+70,2,16,0,1,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,48,0,0,0,4,0,0,0,0,0,0,0,
+8,0,0,0,33,0,0,0,2,0,0,0,1,0,0,0,
+1,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+5,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,188,4,0,0,
+1,0,0,0,108,0,0,0,1,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,142,4,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,92,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,85,110,105,116,
+121,70,70,86,101,114,116,101,120,0,171,171,92,0,0,0,
+14,0,0,0,132,0,0,0,240,3,0,0,0,0,0,0,
+0,0,0,0,180,2,0,0,0,0,0,0,64,0,0,0,
+0,0,0,0,204,2,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,240,2,0,0,
+64,0,0,0,64,0,0,0,0,0,0,0,204,2,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,253,2,0,0,128,0,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,56,3,0,0,
+144,0,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,71,3,0,0,160,0,0,0,128,0,0,0,
+2,0,0,0,88,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,124,3,0,0,
+32,1,0,0,128,0,0,0,2,0,0,0,140,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,176,3,0,0,160,1,0,0,128,0,0,0,
+2,0,0,0,192,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,228,3,0,0,
+32,2,0,0,128,0,0,0,2,0,0,0,244,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,24,4,0,0,160,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,39,4,0,0,
+176,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,54,4,0,0,192,2,0,0,16,0,0,0,
+2,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,66,4,0,0,
+208,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,82,4,0,0,224,2,0,0,0,1,0,0,
+0,0,0,0,96,4,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,132,4,0,0,
+224,3,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,102,102,95,109,97,116,114,105,120,95,109,118,
+112,0,102,108,111,97,116,52,120,52,0,171,3,0,3,0,
+4,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,194,2,0,0,
+102,102,95,109,97,116,114,105,120,95,109,118,0,102,102,95,
+118,101,99,95,99,111,108,111,114,0,102,108,111,97,116,52,
+0,171,171,171,1,0,3,0,1,0,4,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,118,101,99,95,97,
+109,98,105,101,110,116,0,102,102,95,108,105,103,104,116,95,
+99,111,108,111,114,0,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,112,111,115,0,171,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,97,116,116,101,110,0,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,115,112,111,
+116,0,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,109,97,116,95,100,
+105,102,102,117,115,101,0,102,102,95,109,97,116,95,97,109,
+98,105,101,110,116,0,102,102,95,109,97,116,95,115,112,101,
+99,0,102,102,95,109,97,116,95,101,109,105,115,115,105,111,
+110,0,102,102,95,109,97,116,114,105,120,95,116,101,120,0,
+3,0,3,0,4,0,4,0,4,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+194,2,0,0,102,102,95,102,111,103,95,118,115,0,77,105,
+99,114,111,115,111,102,116,32,40,82,41,32,72,76,83,76,
+32,83,104,97,100,101,114,32,67,111,109,112,105,108,101,114,
+32,54,46,51,46,57,52,49,53,46,48,0,76,70,83,48,
+168,1,0,0,7,0,0,0,8,0,0,0,88,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,2,0,0,0,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,110,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,122,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,132,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,140,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,153,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,3,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,163,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,5,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,67,111,109,112,
+117,116,101,83,112,111,116,76,105,103,104,116,83,112,101,99,
+52,0,101,121,101,80,111,115,105,116,105,111,110,0,101,121,
+101,78,111,114,109,97,108,0,118,105,101,119,68,105,114,0,
+100,105,102,102,117,115,101,67,111,108,111,114,0,115,112,101,
+99,67,111,108,111,114,0,97,109,98,0,171,76,73,66,70,
+180,19,0,0,68,88,66,67,240,160,5,118,168,200,121,161,
+74,215,24,12,8,202,65,250,1,0,0,0,180,19,0,0,
+5,0,0,0,52,0,0,0,168,8,0,0,200,12,0,0,
+68,13,0,0,8,18,0,0,65,111,110,57,108,8,0,0,
+108,8,0,0,0,2,86,76,24,8,0,0,84,0,0,0,
+4,0,36,0,0,0,84,0,0,0,84,0,0,0,36,0,
+0,0,84,0,0,0,10,0,5,0,0,0,0,0,0,0,
+0,0,18,0,5,0,5,0,0,0,0,0,0,0,26,0,
+5,0,10,0,0,0,0,0,0,0,34,0,5,0,15,0,
+0,0,0,0,0,2,86,76,81,0,0,5,20,0,15,160,
+0,0,128,63,0,0,0,0,0,0,0,0,0,0,0,0,
+31,0,0,2,5,0,0,128,0,0,15,144,31,0,0,2,
+5,0,1,128,1,0,15,144,31,0,0,2,5,0,3,128,
+3,0,15,144,31,0,0,2,5,0,4,128,4,0,15,144,
+31,0,0,2,5,0,5,128,5,0,15,144,5,0,0,3,
+0,0,1,128,5,0,255,160,5,0,255,160,12,0,0,3,
+0,0,1,128,0,0,0,129,0,0,0,128,4,0,0,4,
+0,0,14,128,0,0,144,144,5,0,255,161,5,0,144,160,
+8,0,0,3,1,0,1,128,0,0,249,128,0,0,249,128,
+12,0,0,3,1,0,2,128,10,0,255,160,1,0,0,128,
+5,0,0,3,0,0,1,128,0,0,0,128,1,0,85,128,
+1,0,0,2,2,0,1,128,20,0,0,160,4,0,0,4,
+1,0,2,128,10,0,170,160,1,0,0,128,2,0,0,128,
+7,0,0,2,1,0,1,128,1,0,0,128,5,0,0,3,
+0,0,14,128,0,0,228,128,1,0,0,128,6,0,0,2,
+1,0,1,128,1,0,85,128,4,0,0,4,0,0,1,128,
+0,0,0,128,1,0,0,129,1,0,0,128,8,0,0,3,
+1,0,1,128,0,0,249,128,15,0,228,160,8,0,0,3,
+0,0,2,128,1,0,228,144,0,0,249,128,11,0,0,3,
+0,0,2,128,0,0,85,128,20,0,85,160,5,0,0,3,
+0,0,14,128,0,0,85,128,3,0,144,144,5,0,0,3,
+0,0,14,128,0,0,228,128,0,0,144,160,11,0,0,3,
+1,0,1,128,1,0,0,128,20,0,85,160,2,0,0,3,
+1,0,1,128,1,0,0,128,10,0,0,161,5,0,0,3,
+1,0,1,128,1,0,0,128,10,0,85,160,11,0,0,3,
+1,0,1,128,1,0,0,128,20,0,85,160,10,0,0,3,
+1,0,1,128,1,0,0,128,20,0,0,160,5,0,0,3,
+0,0,1,128,0,0,0,128,1,0,0,128,5,0,0,3,
+0,0,7,128,0,0,0,128,0,0,249,128,10,0,0,3,
+0,0,7,128,0,0,228,128,20,0,0,160,2,0,0,3,
+0,0,7,128,0,0,228,128,5,0,228,144,5,0,0,3,
+0,0,8,128,6,0,255,160,6,0,255,160,12,0,0,3,
+0,0,8,128,0,0,255,129,0,0,255,128,4,0,0,4,
+1,0,7,128,0,0,228,144,6,0,255,161,6,0,228,160,
+8,0,0,3,1,0,8,128,1,0,228,128,1,0,228,128,
+12,0,0,3,2,0,2,128,11,0,255,160,1,0,255,128,
+5,0,0,3,0,0,8,128,0,0,255,128,2,0,85,128,
+4,0,0,4,2,0,2,128,11,0,170,160,1,0,255,128,
+2,0,0,128,7,0,0,2,1,0,8,128,1,0,255,128,
+5,0,0,3,1,0,7,128,1,0,255,128,1,0,228,128,
+6,0,0,2,1,0,8,128,2,0,85,128,4,0,0,4,
+0,0,8,128,0,0,255,128,1,0,255,129,1,0,255,128,
+8,0,0,3,1,0,8,128,1,0,228,128,16,0,228,160,
+8,0,0,3,1,0,1,128,1,0,228,144,1,0,228,128,
+11,0,0,3,1,0,9,128,1,0,228,128,20,0,85,160,
+5,0,0,3,1,0,7,128,1,0,0,128,3,0,228,144,
+5,0,0,3,1,0,7,128,1,0,228,128,1,0,228,160,
+2,0,0,3,1,0,8,128,1,0,255,128,11,0,0,161,
+5,0,0,3,1,0,8,128,1,0,255,128,11,0,85,160,
+11,0,0,3,1,0,8,128,1,0,255,128,20,0,85,160,
+10,0,0,3,1,0,8,128,1,0,255,128,20,0,0,160,
+5,0,0,3,0,0,8,128,0,0,255,128,1,0,255,128,
+5,0,0,3,1,0,7,128,0,0,255,128,1,0,228,128,
+10,0,0,3,1,0,7,128,1,0,228,128,20,0,0,160,
+2,0,0,3,0,0,7,128,0,0,228,128,1,0,228,128,
+5,0,0,3,0,0,8,128,7,0,255,160,7,0,255,160,
+12,0,0,3,0,0,8,128,0,0,255,129,0,0,255,128,
+4,0,0,4,1,0,7,128,0,0,228,144,7,0,255,161,
+7,0,228,160,8,0,0,3,1,0,8,128,1,0,228,128,
+1,0,228,128,12,0,0,3,2,0,2,128,12,0,255,160,
+1,0,255,128,5,0,0,3,0,0,8,128,0,0,255,128,
+2,0,85,128,4,0,0,4,2,0,2,128,12,0,170,160,
+1,0,255,128,2,0,0,128,7,0,0,2,1,0,8,128,
+1,0,255,128,5,0,0,3,1,0,7,128,1,0,255,128,
+1,0,228,128,6,0,0,2,1,0,8,128,2,0,85,128,
+4,0,0,4,0,0,8,128,0,0,255,128,1,0,255,129,
+1,0,255,128,8,0,0,3,1,0,8,128,1,0,228,128,
+17,0,228,160,8,0,0,3,1,0,1,128,1,0,228,144,
+1,0,228,128,11,0,0,3,1,0,9,128,1,0,228,128,
+20,0,85,160,5,0,0,3,1,0,7,128,1,0,0,128,
+3,0,228,144,5,0,0,3,1,0,7,128,1,0,228,128,
+2,0,228,160,2,0,0,3,1,0,8,128,1,0,255,128,
+12,0,0,161,5,0,0,3,1,0,8,128,1,0,255,128,
+12,0,85,160,11,0,0,3,1,0,8,128,1,0,255,128,
+20,0,85,160,10,0,0,3,1,0,8,128,1,0,255,128,
+20,0,0,160,5,0,0,3,0,0,8,128,0,0,255,128,
+1,0,255,128,5,0,0,3,1,0,7,128,0,0,255,128,
+1,0,228,128,10,0,0,3,1,0,7,128,1,0,228,128,
+20,0,0,160,2,0,0,3,0,0,7,128,0,0,228,128,
+1,0,228,128,5,0,0,3,0,0,8,128,8,0,255,160,
+8,0,255,160,12,0,0,3,0,0,8,128,0,0,255,129,
+0,0,255,128,4,0,0,4,1,0,7,128,0,0,228,144,
+8,0,255,161,8,0,228,160,8,0,0,3,1,0,8,128,
+1,0,228,128,1,0,228,128,12,0,0,3,2,0,2,128,
+13,0,255,160,1,0,255,128,5,0,0,3,0,0,8,128,
+0,0,255,128,2,0,85,128,4,0,0,4,2,0,2,128,
+13,0,170,160,1,0,255,128,2,0,0,128,7,0,0,2,
+1,0,8,128,1,0,255,128,5,0,0,3,1,0,7,128,
+1,0,255,128,1,0,228,128,6,0,0,2,1,0,8,128,
+2,0,85,128,4,0,0,4,0,0,8,128,0,0,255,128,
+1,0,255,129,1,0,255,128,8,0,0,3,1,0,8,128,
+1,0,228,128,18,0,228,160,8,0,0,3,1,0,1,128,
+1,0,228,144,1,0,228,128,11,0,0,3,1,0,9,128,
+1,0,228,128,20,0,85,160,5,0,0,3,1,0,7,128,
+1,0,0,128,3,0,228,144,5,0,0,3,1,0,7,128,
+1,0,228,128,3,0,228,160,2,0,0,3,1,0,8,128,
+1,0,255,128,13,0,0,161,5,0,0,3,1,0,8,128,
+1,0,255,128,13,0,85,160,11,0,0,3,1,0,8,128,
+1,0,255,128,20,0,85,160,10,0,0,3,1,0,8,128,
+1,0,255,128,20,0,0,160,5,0,0,3,0,0,8,128,
+0,0,255,128,1,0,255,128,5,0,0,3,1,0,7,128,
+0,0,255,128,1,0,228,128,10,0,0,3,1,0,7,128,
+1,0,228,128,20,0,0,160,2,0,0,3,0,0,7,128,
+0,0,228,128,1,0,228,128,5,0,0,3,0,0,8,128,
+9,0,255,160,9,0,255,160,12,0,0,3,0,0,8,128,
+0,0,255,129,0,0,255,128,4,0,0,4,1,0,7,128,
+0,0,228,144,9,0,255,161,9,0,228,160,8,0,0,3,
+1,0,8,128,1,0,228,128,1,0,228,128,12,0,0,3,
+2,0,2,128,14,0,255,160,1,0,255,128,5,0,0,3,
+0,0,8,128,0,0,255,128,2,0,85,128,4,0,0,4,
+2,0,1,128,14,0,170,160,1,0,255,128,2,0,0,128,
+7,0,0,2,1,0,8,128,1,0,255,128,5,0,0,3,
+1,0,7,128,1,0,255,128,1,0,228,128,6,0,0,2,
+1,0,8,128,2,0,0,128,4,0,0,4,0,0,8,128,
+0,0,255,128,1,0,255,129,1,0,255,128,8,0,0,3,
+1,0,8,128,1,0,228,128,19,0,228,160,8,0,0,3,
+1,0,1,128,1,0,228,144,1,0,228,128,11,0,0,3,
+1,0,9,128,1,0,228,128,20,0,85,160,5,0,0,3,
+1,0,7,128,1,0,0,128,3,0,228,144,5,0,0,3,
+1,0,7,128,1,0,228,128,4,0,228,160,2,0,0,3,
+1,0,8,128,1,0,255,128,14,0,0,161,5,0,0,3,
+1,0,8,128,1,0,255,128,14,0,85,160,11,0,0,3,
+1,0,8,128,1,0,255,128,20,0,85,160,10,0,0,3,
+1,0,8,128,1,0,255,128,20,0,0,160,5,0,0,3,
+0,0,8,128,0,0,255,128,1,0,255,128,5,0,0,3,
+1,0,7,128,0,0,255,128,1,0,228,128,10,0,0,3,
+1,0,7,128,1,0,228,128,20,0,0,160,2,0,0,3,
+0,0,7,224,0,0,228,128,1,0,228,128,1,0,0,2,
+1,0,7,224,4,0,228,144,255,255,0,0,83,72,68,82,
+24,4,0,0,64,0,240,255,6,1,0,0,89,8,0,4,
+70,142,32,0,0,0,0,0,40,0,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,95,0,0,3,114,16,16,0,
+1,0,0,0,95,0,0,3,114,16,16,0,3,0,0,0,
+95,0,0,3,114,16,16,0,4,0,0,0,95,0,0,3,
+114,16,16,0,5,0,0,0,101,0,0,3,114,32,16,0,
+0,0,0,0,101,0,0,3,114,32,16,0,1,0,0,0,
+104,0,0,2,3,0,0,0,54,0,0,5,114,0,16,0,
+0,0,0,0,70,18,16,0,5,0,0,0,54,0,0,5,
+130,0,16,0,0,0,0,0,1,64,0,0,0,0,0,0,
+48,0,0,1,33,0,0,7,18,0,16,0,1,0,0,0,
+58,0,16,0,0,0,0,0,1,64,0,0,5,0,0,0,
+3,0,4,3,10,0,16,0,1,0,0,0,50,0,0,16,
+114,0,16,0,1,0,0,0,70,18,16,128,65,0,0,0,
+0,0,0,0,246,143,32,6,0,0,0,0,18,0,0,0,
+58,0,16,0,0,0,0,0,70,130,32,6,0,0,0,0,
+18,0,0,0,58,0,16,0,0,0,0,0,16,0,0,7,
+130,0,16,0,1,0,0,0,70,2,16,0,1,0,0,0,
+70,2,16,0,1,0,0,0,50,0,0,12,18,0,16,0,
+2,0,0,0,42,128,32,6,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,58,0,16,0,1,0,0,0,
+1,64,0,0,0,0,128,63,14,0,0,10,18,0,16,0,
+2,0,0,0,2,64,0,0,0,0,128,63,0,0,128,63,
+0,0,128,63,0,0,128,63,10,0,16,0,2,0,0,0,
+57,0,0,10,34,0,16,0,2,0,0,0,1,64,0,0,
+0,0,0,0,58,128,32,6,0,0,0,0,18,0,0,0,
+58,0,16,0,0,0,0,0,49,0,0,10,66,0,16,0,
+2,0,0,0,58,128,32,6,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,58,0,16,0,1,0,0,0,
+1,0,0,7,34,0,16,0,2,0,0,0,42,0,16,0,
+2,0,0,0,26,0,16,0,2,0,0,0,55,0,0,9,
+18,0,16,0,2,0,0,0,26,0,16,0,2,0,0,0,
+1,64,0,0,0,0,0,0,10,0,16,0,2,0,0,0,
+68,0,0,5,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,56,0,0,7,114,0,16,0,1,0,0,0,
+246,15,16,0,1,0,0,0,70,2,16,0,1,0,0,0,
+16,0,0,10,130,0,16,0,1,0,0,0,70,2,16,0,
+1,0,0,0,70,130,32,6,0,0,0,0,34,0,0,0,
+58,0,16,0,0,0,0,0,52,0,0,7,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,1,64,0,0,
+0,0,0,0,0,0,0,11,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,10,128,32,134,65,0,0,0,
+0,0,0,0,26,0,0,0,58,0,16,0,0,0,0,0,
+56,32,0,10,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,26,128,32,6,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,56,0,0,7,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,10,0,16,0,
+2,0,0,0,16,0,0,7,18,0,16,0,1,0,0,0,
+70,18,16,0,1,0,0,0,70,2,16,0,1,0,0,0,
+52,0,0,7,18,0,16,0,1,0,0,0,10,0,16,0,
+1,0,0,0,1,64,0,0,0,0,0,0,56,0,0,7,
+114,0,16,0,1,0,0,0,6,0,16,0,1,0,0,0,
+70,18,16,0,3,0,0,0,56,0,0,10,114,0,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,70,130,32,6,
+0,0,0,0,10,0,0,0,58,0,16,0,0,0,0,0,
+56,0,0,7,114,0,16,0,1,0,0,0,246,15,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,51,0,0,10,
+114,0,16,0,1,0,0,0,70,2,16,0,1,0,0,0,
+2,64,0,0,0,0,128,63,0,0,128,63,0,0,128,63,
+0,0,0,0,0,0,0,7,114,0,16,0,0,0,0,0,
+70,2,16,0,0,0,0,0,70,2,16,0,1,0,0,0,
+30,0,0,7,130,0,16,0,0,0,0,0,58,0,16,0,
+0,0,0,0,1,64,0,0,1,0,0,0,22,0,0,1,
+54,0,0,5,114,32,16,0,0,0,0,0,70,2,16,0,
+0,0,0,0,54,0,0,5,114,32,16,0,1,0,0,0,
+70,18,16,0,4,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,32,0,0,0,3,0,0,0,0,0,0,0,
+7,0,0,0,20,0,0,0,2,0,0,0,1,0,0,0,
+1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+4,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,188,4,0,0,
+1,0,0,0,108,0,0,0,1,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,142,4,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,92,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,85,110,105,116,
+121,70,70,86,101,114,116,101,120,0,171,171,92,0,0,0,
+14,0,0,0,132,0,0,0,240,3,0,0,0,0,0,0,
+0,0,0,0,180,2,0,0,0,0,0,0,64,0,0,0,
+0,0,0,0,204,2,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,240,2,0,0,
+64,0,0,0,64,0,0,0,0,0,0,0,204,2,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,253,2,0,0,128,0,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,56,3,0,0,
+144,0,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,71,3,0,0,160,0,0,0,128,0,0,0,
+2,0,0,0,88,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,124,3,0,0,
+32,1,0,0,128,0,0,0,2,0,0,0,140,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,176,3,0,0,160,1,0,0,128,0,0,0,
+2,0,0,0,192,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,228,3,0,0,
+32,2,0,0,128,0,0,0,2,0,0,0,244,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,24,4,0,0,160,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,39,4,0,0,
+176,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,54,4,0,0,192,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,66,4,0,0,
+208,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,82,4,0,0,224,2,0,0,0,1,0,0,
+0,0,0,0,96,4,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,132,4,0,0,
+224,3,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,102,102,95,109,97,116,114,105,120,95,109,118,
+112,0,102,108,111,97,116,52,120,52,0,171,3,0,3,0,
+4,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,194,2,0,0,
+102,102,95,109,97,116,114,105,120,95,109,118,0,102,102,95,
+118,101,99,95,99,111,108,111,114,0,102,108,111,97,116,52,
+0,171,171,171,1,0,3,0,1,0,4,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,118,101,99,95,97,
+109,98,105,101,110,116,0,102,102,95,108,105,103,104,116,95,
+99,111,108,111,114,0,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,112,111,115,0,171,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,97,116,116,101,110,0,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,115,112,111,
+116,0,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,109,97,116,95,100,
+105,102,102,117,115,101,0,102,102,95,109,97,116,95,97,109,
+98,105,101,110,116,0,102,102,95,109,97,116,95,115,112,101,
+99,0,102,102,95,109,97,116,95,101,109,105,115,115,105,111,
+110,0,102,102,95,109,97,116,114,105,120,95,116,101,120,0,
+3,0,3,0,4,0,4,0,4,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+194,2,0,0,102,102,95,102,111,103,95,118,115,0,77,105,
+99,114,111,115,111,102,116,32,40,82,41,32,72,76,83,76,
+32,83,104,97,100,101,114,32,67,111,109,112,105,108,101,114,
+32,54,46,51,46,57,52,49,53,46,48,0,76,70,83,48,
+164,1,0,0,7,0,0,0,8,0,0,0,88,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,2,0,0,0,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,106,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,118,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,128,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,136,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,149,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,3,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,159,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,5,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,67,111,109,112,
+117,116,101,83,112,111,116,76,105,103,104,116,53,0,101,121,
+101,80,111,115,105,116,105,111,110,0,101,121,101,78,111,114,
+109,97,108,0,118,105,101,119,68,105,114,0,100,105,102,102,
+117,115,101,67,111,108,111,114,0,115,112,101,99,67,111,108,
+111,114,0,97,109,98,0,171,76,73,66,70,80,16,0,0,
+68,88,66,67,89,78,131,151,228,245,82,121,136,215,111,193,
+160,210,121,165,1,0,0,0,80,16,0,0,5,0,0,0,
+52,0,0,0,156,3,0,0,96,9,0,0,220,9,0,0,
+160,14,0,0,65,111,110,57,96,3,0,0,96,3,0,0,
+0,2,86,76,48,3,0,0,48,0,0,0,1,0,36,0,
+0,0,48,0,0,0,48,0,0,0,36,0,0,0,48,0,
+0,0,0,0,45,0,0,0,0,0,0,0,0,2,86,76,
+81,0,0,5,45,0,15,160,0,0,0,0,0,0,0,0,
+0,0,128,63,0,0,0,0,48,0,0,5,0,0,15,240,
+5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+31,0,0,2,5,0,0,128,0,0,15,144,31,0,0,2,
+5,0,1,128,1,0,15,144,31,0,0,2,5,0,2,128,
+2,0,15,144,31,0,0,2,5,0,3,128,3,0,15,144,
+31,0,0,2,5,0,4,128,4,0,15,144,31,0,0,2,
+5,0,5,128,5,0,15,144,1,0,0,2,0,0,7,128,
+4,0,228,144,1,0,0,2,1,0,7,128,5,0,228,144,
+27,0,0,2,0,8,228,240,0,0,228,240,4,0,0,6,
+2,0,7,128,0,0,228,144,18,32,255,161,0,8,228,240,
+18,32,228,160,0,8,228,240,8,0,0,3,0,0,8,128,
+2,0,228,128,2,0,228,128,7,0,0,2,1,0,8,128,
+0,0,255,128,4,0,0,4,3,0,7,128,2,0,228,128,
+1,0,255,128,2,0,228,144,36,0,0,2,4,0,7,128,
+3,0,228,128,8,0,0,3,2,0,8,128,1,0,228,144,
+4,0,228,128,5,0,0,5,3,0,1,128,18,32,255,160,
+0,8,228,240,18,32,255,160,0,8,228,240,12,0,0,3,
+3,0,1,128,3,0,0,129,3,0,0,128,12,0,0,4,
+3,0,2,128,26,32,255,160,0,8,228,240,0,0,255,128,
+5,0,0,3,3,0,1,128,3,0,85,128,3,0,0,128,
+5,0,0,4,0,0,8,128,0,0,255,128,26,32,170,160,
+0,8,228,240,2,0,0,3,0,0,8,128,0,0,255,128,
+45,0,170,160,6,0,0,2,0,0,8,128,0,0,255,128,
+4,0,0,4,0,0,8,128,3,0,0,128,0,0,255,129,
+0,0,255,128,11,0,0,3,2,0,8,128,2,0,255,128,
+45,0,85,160,32,0,0,3,3,0,1,128,2,0,255,128,
+44,0,255,160,10,0,0,3,2,0,8,128,3,0,0,128,
+45,0,170,160,5,0,0,3,2,0,7,128,1,0,255,128,
+2,0,228,128,8,0,0,3,1,0,8,128,1,0,228,144,
+2,0,228,128,8,0,0,4,2,0,1,128,2,0,228,128,
+34,32,228,160,0,8,228,240,11,0,0,3,2,0,1,128,
+2,0,0,128,45,0,85,160,2,0,0,4,2,0,1,128,
+2,0,0,128,26,32,0,161,0,8,228,240,5,0,0,4,
+2,0,1,128,2,0,0,128,26,32,85,160,0,8,228,240,
+11,0,0,3,2,0,1,128,2,0,0,128,45,0,85,160,
+10,0,0,3,2,0,1,128,2,0,0,128,45,0,170,160,
+5,0,0,3,0,0,8,128,0,0,255,128,2,0,0,128,
+11,0,0,3,1,0,8,128,1,0,255,128,45,0,85,160,
+12,0,0,3,2,0,1,128,45,0,85,160,1,0,255,128,
+5,0,0,3,2,0,2,128,2,0,255,128,0,0,255,128,
+5,0,0,4,2,0,14,128,2,0,85,128,10,32,144,160,
+0,8,228,240,4,0,0,4,0,0,7,128,2,0,0,128,
+2,0,249,128,0,0,228,128,5,0,0,3,2,0,7,128,
+1,0,255,128,3,0,228,144,5,0,0,4,2,0,7,128,
+2,0,228,128,10,32,228,160,0,8,228,240,5,0,0,3,
+2,0,7,128,0,0,255,128,2,0,228,128,10,0,0,3,
+2,0,7,128,2,0,228,128,45,0,170,160,2,0,0,3,
+1,0,7,128,1,0,228,128,2,0,228,128,29,0,0,0,
+1,0,0,2,1,0,7,224,0,0,228,128,1,0,0,2,
+0,0,7,224,1,0,228,128,255,255,0,0,83,72,68,82,
+188,5,0,0,64,0,240,255,111,1,0,0,89,8,0,4,
+70,142,32,0,0,0,0,0,45,0,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,95,0,0,3,114,16,16,0,
+1,0,0,0,95,0,0,3,114,16,16,0,2,0,0,0,
+95,0,0,3,114,16,16,0,3,0,0,0,95,0,0,3,
+114,16,16,0,4,0,0,0,95,0,0,3,114,16,16,0,
+5,0,0,0,101,0,0,3,114,32,16,0,0,0,0,0,
+101,0,0,3,114,32,16,0,1,0,0,0,104,0,0,2,
+4,0,0,0,54,0,0,5,114,0,16,0,0,0,0,0,
+70,18,16,0,4,0,0,0,54,0,0,5,114,0,16,0,
+1,0,0,0,70,18,16,0,5,0,0,0,54,0,0,5,
+130,0,16,0,0,0,0,0,1,64,0,0,0,0,0,0,
+48,0,0,1,33,0,0,7,130,0,16,0,1,0,0,0,
+58,0,16,0,0,0,0,0,1,64,0,0,5,0,0,0,
+3,0,4,3,58,0,16,0,1,0,0,0,50,0,0,16,
+114,0,16,0,2,0,0,0,70,18,16,128,65,0,0,0,
+0,0,0,0,246,143,32,6,0,0,0,0,18,0,0,0,
+58,0,16,0,0,0,0,0,70,130,32,6,0,0,0,0,
+18,0,0,0,58,0,16,0,0,0,0,0,16,0,0,7,
+130,0,16,0,1,0,0,0,70,2,16,0,2,0,0,0,
+70,2,16,0,2,0,0,0,50,0,0,12,130,0,16,0,
+2,0,0,0,42,128,32,6,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,58,0,16,0,1,0,0,0,
+1,64,0,0,0,0,128,63,14,0,0,10,130,0,16,0,
+2,0,0,0,2,64,0,0,0,0,128,63,0,0,128,63,
+0,0,128,63,0,0,128,63,58,0,16,0,2,0,0,0,
+57,0,0,10,18,0,16,0,3,0,0,0,1,64,0,0,
+0,0,0,0,58,128,32,6,0,0,0,0,18,0,0,0,
+58,0,16,0,0,0,0,0,49,0,0,10,34,0,16,0,
+3,0,0,0,58,128,32,6,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,58,0,16,0,1,0,0,0,
+1,0,0,7,18,0,16,0,3,0,0,0,26,0,16,0,
+3,0,0,0,10,0,16,0,3,0,0,0,55,0,0,9,
+130,0,16,0,2,0,0,0,10,0,16,0,3,0,0,0,
+1,64,0,0,0,0,0,0,58,0,16,0,2,0,0,0,
+68,0,0,5,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,56,0,0,7,114,0,16,0,3,0,0,0,
+246,15,16,0,1,0,0,0,70,2,16,0,2,0,0,0,
+16,0,0,10,130,0,16,0,3,0,0,0,70,2,16,0,
+3,0,0,0,70,130,32,6,0,0,0,0,34,0,0,0,
+58,0,16,0,0,0,0,0,52,0,0,7,130,0,16,0,
+3,0,0,0,58,0,16,0,3,0,0,0,1,64,0,0,
+0,0,0,0,0,0,0,11,130,0,16,0,3,0,0,0,
+58,0,16,0,3,0,0,0,10,128,32,134,65,0,0,0,
+0,0,0,0,26,0,0,0,58,0,16,0,0,0,0,0,
+56,32,0,10,130,0,16,0,3,0,0,0,58,0,16,0,
+3,0,0,0,26,128,32,6,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,56,0,0,7,130,0,16,0,
+2,0,0,0,58,0,16,0,2,0,0,0,58,0,16,0,
+3,0,0,0,16,0,0,7,18,0,16,0,3,0,0,0,
+70,18,16,0,1,0,0,0,70,2,16,0,3,0,0,0,
+52,0,0,7,18,0,16,0,3,0,0,0,10,0,16,0,
+3,0,0,0,1,64,0,0,0,0,0,0,56,0,0,7,
+226,0,16,0,3,0,0,0,6,0,16,0,3,0,0,0,
+6,25,16,0,3,0,0,0,56,0,0,10,226,0,16,0,
+3,0,0,0,86,14,16,0,3,0,0,0,6,137,32,6,
+0,0,0,0,10,0,0,0,58,0,16,0,0,0,0,0,
+49,0,0,7,18,0,16,0,3,0,0,0,1,64,0,0,
+0,0,0,0,10,0,16,0,3,0,0,0,31,0,4,3,
+10,0,16,0,3,0,0,0,50,0,0,9,114,0,16,0,
+2,0,0,0,70,2,16,0,2,0,0,0,246,15,16,0,
+1,0,0,0,70,18,16,0,2,0,0,0,16,0,0,7,
+130,0,16,0,1,0,0,0,70,2,16,0,2,0,0,0,
+70,2,16,0,2,0,0,0,68,0,0,5,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,56,0,0,7,
+114,0,16,0,2,0,0,0,246,15,16,0,1,0,0,0,
+70,2,16,0,2,0,0,0,16,0,0,7,130,0,16,0,
+1,0,0,0,70,18,16,0,1,0,0,0,70,2,16,0,
+2,0,0,0,52,0,0,7,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,1,64,0,0,0,0,0,0,
+47,0,0,5,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,56,0,0,8,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,58,128,32,0,0,0,0,0,
+44,0,0,0,25,0,0,5,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,51,0,0,7,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,1,64,0,0,
+0,0,128,63,56,0,0,7,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,58,0,16,0,2,0,0,0,
+50,0,0,12,114,0,16,0,0,0,0,0,246,15,16,0,
+1,0,0,0,70,130,32,6,0,0,0,0,10,0,0,0,
+58,0,16,0,0,0,0,0,70,2,16,0,0,0,0,0,
+21,0,0,1,56,0,0,7,114,0,16,0,2,0,0,0,
+246,15,16,0,2,0,0,0,150,7,16,0,3,0,0,0,
+51,0,0,10,114,0,16,0,2,0,0,0,70,2,16,0,
+2,0,0,0,2,64,0,0,0,0,128,63,0,0,128,63,
+0,0,128,63,0,0,0,0,0,0,0,7,114,0,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,70,2,16,0,
+2,0,0,0,30,0,0,7,130,0,16,0,0,0,0,0,
+58,0,16,0,0,0,0,0,1,64,0,0,1,0,0,0,
+22,0,0,1,54,0,0,5,114,32,16,0,1,0,0,0,
+70,2,16,0,0,0,0,0,54,0,0,5,114,32,16,0,
+0,0,0,0,70,2,16,0,1,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,48,0,0,0,4,0,0,0,
+0,0,0,0,8,0,0,0,33,0,0,0,2,0,0,0,
+1,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,5,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+188,4,0,0,1,0,0,0,108,0,0,0,1,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,142,4,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+85,110,105,116,121,70,70,86,101,114,116,101,120,0,171,171,
+92,0,0,0,14,0,0,0,132,0,0,0,240,3,0,0,
+0,0,0,0,0,0,0,0,180,2,0,0,0,0,0,0,
+64,0,0,0,0,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+240,2,0,0,64,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,253,2,0,0,128,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+56,3,0,0,144,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,71,3,0,0,160,0,0,0,
+128,0,0,0,2,0,0,0,88,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+124,3,0,0,32,1,0,0,128,0,0,0,2,0,0,0,
+140,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,176,3,0,0,160,1,0,0,
+128,0,0,0,2,0,0,0,192,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+228,3,0,0,32,2,0,0,128,0,0,0,2,0,0,0,
+244,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,24,4,0,0,160,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+39,4,0,0,176,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,54,4,0,0,192,2,0,0,
+16,0,0,0,2,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+66,4,0,0,208,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,82,4,0,0,224,2,0,0,
+0,1,0,0,0,0,0,0,96,4,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+132,4,0,0,224,3,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,102,102,95,109,97,116,114,105,
+120,95,109,118,112,0,102,108,111,97,116,52,120,52,0,171,
+3,0,3,0,4,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+194,2,0,0,102,102,95,109,97,116,114,105,120,95,109,118,
+0,102,102,95,118,101,99,95,99,111,108,111,114,0,102,108,
+111,97,116,52,0,171,171,171,1,0,3,0,1,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,118,
+101,99,95,97,109,98,105,101,110,116,0,102,102,95,108,105,
+103,104,116,95,99,111,108,111,114,0,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,112,111,115,0,171,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,97,116,116,
+101,110,0,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,115,112,111,116,0,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,109,
+97,116,95,100,105,102,102,117,115,101,0,102,102,95,109,97,
+116,95,97,109,98,105,101,110,116,0,102,102,95,109,97,116,
+95,115,112,101,99,0,102,102,95,109,97,116,95,101,109,105,
+115,115,105,111,110,0,102,102,95,109,97,116,114,105,120,95,
+116,101,120,0,3,0,3,0,4,0,4,0,4,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,194,2,0,0,102,102,95,102,111,103,95,118,
+115,0,77,105,99,114,111,115,111,102,116,32,40,82,41,32,
+72,76,83,76,32,83,104,97,100,101,114,32,67,111,109,112,
+105,108,101,114,32,54,46,51,46,57,52,49,53,46,48,0,
+76,70,83,48,168,1,0,0,7,0,0,0,8,0,0,0,
+88,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,2,0,0,0,
+255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,
+110,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+122,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,
+1,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+132,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,
+2,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+140,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+153,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,3,0,0,0,
+4,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+163,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,
+5,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+67,111,109,112,117,116,101,83,112,111,116,76,105,103,104,116,
+83,112,101,99,53,0,101,121,101,80,111,115,105,116,105,111,
+110,0,101,121,101,78,111,114,109,97,108,0,118,105,101,119,
+68,105,114,0,100,105,102,102,117,115,101,67,111,108,111,114,
+0,115,112,101,99,67,111,108,111,114,0,97,109,98,0,171,
+76,73,66,70,56,21,0,0,68,88,66,67,116,246,90,100,
+53,210,241,155,159,216,173,93,60,139,208,144,1,0,0,0,
+56,21,0,0,5,0,0,0,52,0,0,0,44,10,0,0,
+76,14,0,0,200,14,0,0,140,19,0,0,65,111,110,57,
+240,9,0,0,240,9,0,0,0,2,86,76,156,9,0,0,
+84,0,0,0,4,0,36,0,0,0,84,0,0,0,84,0,
+0,0,36,0,0,0,84,0,0,0,10,0,6,0,0,0,
+0,0,0,0,0,0,18,0,6,0,6,0,0,0,0,0,
+0,0,26,0,6,0,12,0,0,0,0,0,0,0,34,0,
+6,0,18,0,0,0,0,0,0,2,86,76,81,0,0,5,
+24,0,15,160,0,0,128,63,0,0,0,0,0,0,0,0,
+0,0,0,0,31,0,0,2,5,0,0,128,0,0,15,144,
+31,0,0,2,5,0,1,128,1,0,15,144,31,0,0,2,
+5,0,3,128,3,0,15,144,31,0,0,2,5,0,4,128,
+4,0,15,144,31,0,0,2,5,0,5,128,5,0,15,144,
+5,0,0,3,0,0,1,128,6,0,255,160,6,0,255,160,
+12,0,0,3,0,0,1,128,0,0,0,129,0,0,0,128,
+4,0,0,4,0,0,14,128,0,0,144,144,6,0,255,161,
+6,0,144,160,8,0,0,3,1,0,1,128,0,0,249,128,
+0,0,249,128,12,0,0,3,1,0,2,128,12,0,255,160,
+1,0,0,128,5,0,0,3,0,0,1,128,0,0,0,128,
+1,0,85,128,1,0,0,2,2,0,1,128,24,0,0,160,
+4,0,0,4,1,0,2,128,12,0,170,160,1,0,0,128,
+2,0,0,128,7,0,0,2,1,0,1,128,1,0,0,128,
+5,0,0,3,0,0,14,128,0,0,228,128,1,0,0,128,
+6,0,0,2,1,0,1,128,1,0,85,128,4,0,0,4,
+0,0,1,128,0,0,0,128,1,0,0,129,1,0,0,128,
+8,0,0,3,1,0,1,128,0,0,249,128,18,0,228,160,
+8,0,0,3,0,0,2,128,1,0,228,144,0,0,249,128,
+11,0,0,3,0,0,2,128,0,0,85,128,24,0,85,160,
+5,0,0,3,0,0,14,128,0,0,85,128,3,0,144,144,
+5,0,0,3,0,0,14,128,0,0,228,128,0,0,144,160,
+11,0,0,3,1,0,1,128,1,0,0,128,24,0,85,160,
+2,0,0,3,1,0,1,128,1,0,0,128,12,0,0,161,
+5,0,0,3,1,0,1,128,1,0,0,128,12,0,85,160,
+11,0,0,3,1,0,1,128,1,0,0,128,24,0,85,160,
+10,0,0,3,1,0,1,128,1,0,0,128,24,0,0,160,
+5,0,0,3,0,0,1,128,0,0,0,128,1,0,0,128,
+5,0,0,3,0,0,7,128,0,0,0,128,0,0,249,128,
+10,0,0,3,0,0,7,128,0,0,228,128,24,0,0,160,
+2,0,0,3,0,0,7,128,0,0,228,128,5,0,228,144,
+5,0,0,3,0,0,8,128,7,0,255,160,7,0,255,160,
+12,0,0,3,0,0,8,128,0,0,255,129,0,0,255,128,
+4,0,0,4,1,0,7,128,0,0,228,144,7,0,255,161,
+7,0,228,160,8,0,0,3,1,0,8,128,1,0,228,128,
+1,0,228,128,12,0,0,3,2,0,2,128,13,0,255,160,
+1,0,255,128,5,0,0,3,0,0,8,128,0,0,255,128,
+2,0,85,128,4,0,0,4,2,0,2,128,13,0,170,160,
+1,0,255,128,2,0,0,128,7,0,0,2,1,0,8,128,
+1,0,255,128,5,0,0,3,1,0,7,128,1,0,255,128,
+1,0,228,128,6,0,0,2,1,0,8,128,2,0,85,128,
+4,0,0,4,0,0,8,128,0,0,255,128,1,0,255,129,
+1,0,255,128,8,0,0,3,1,0,8,128,1,0,228,128,
+19,0,228,160,8,0,0,3,1,0,1,128,1,0,228,144,
+1,0,228,128,11,0,0,3,1,0,9,128,1,0,228,128,
+24,0,85,160,5,0,0,3,1,0,7,128,1,0,0,128,
+3,0,228,144,5,0,0,3,1,0,7,128,1,0,228,128,
+1,0,228,160,2,0,0,3,1,0,8,128,1,0,255,128,
+13,0,0,161,5,0,0,3,1,0,8,128,1,0,255,128,
+13,0,85,160,11,0,0,3,1,0,8,128,1,0,255,128,
+24,0,85,160,10,0,0,3,1,0,8,128,1,0,255,128,
+24,0,0,160,5,0,0,3,0,0,8,128,0,0,255,128,
+1,0,255,128,5,0,0,3,1,0,7,128,0,0,255,128,
+1,0,228,128,10,0,0,3,1,0,7,128,1,0,228,128,
+24,0,0,160,2,0,0,3,0,0,7,128,0,0,228,128,
+1,0,228,128,5,0,0,3,0,0,8,128,8,0,255,160,
+8,0,255,160,12,0,0,3,0,0,8,128,0,0,255,129,
+0,0,255,128,4,0,0,4,1,0,7,128,0,0,228,144,
+8,0,255,161,8,0,228,160,8,0,0,3,1,0,8,128,
+1,0,228,128,1,0,228,128,12,0,0,3,2,0,2,128,
+14,0,255,160,1,0,255,128,5,0,0,3,0,0,8,128,
+0,0,255,128,2,0,85,128,4,0,0,4,2,0,2,128,
+14,0,170,160,1,0,255,128,2,0,0,128,7,0,0,2,
+1,0,8,128,1,0,255,128,5,0,0,3,1,0,7,128,
+1,0,255,128,1,0,228,128,6,0,0,2,1,0,8,128,
+2,0,85,128,4,0,0,4,0,0,8,128,0,0,255,128,
+1,0,255,129,1,0,255,128,8,0,0,3,1,0,8,128,
+1,0,228,128,20,0,228,160,8,0,0,3,1,0,1,128,
+1,0,228,144,1,0,228,128,11,0,0,3,1,0,9,128,
+1,0,228,128,24,0,85,160,5,0,0,3,1,0,7,128,
+1,0,0,128,3,0,228,144,5,0,0,3,1,0,7,128,
+1,0,228,128,2,0,228,160,2,0,0,3,1,0,8,128,
+1,0,255,128,14,0,0,161,5,0,0,3,1,0,8,128,
+1,0,255,128,14,0,85,160,11,0,0,3,1,0,8,128,
+1,0,255,128,24,0,85,160,10,0,0,3,1,0,8,128,
+1,0,255,128,24,0,0,160,5,0,0,3,0,0,8,128,
+0,0,255,128,1,0,255,128,5,0,0,3,1,0,7,128,
+0,0,255,128,1,0,228,128,10,0,0,3,1,0,7,128,
+1,0,228,128,24,0,0,160,2,0,0,3,0,0,7,128,
+0,0,228,128,1,0,228,128,5,0,0,3,0,0,8,128,
+9,0,255,160,9,0,255,160,12,0,0,3,0,0,8,128,
+0,0,255,129,0,0,255,128,4,0,0,4,1,0,7,128,
+0,0,228,144,9,0,255,161,9,0,228,160,8,0,0,3,
+1,0,8,128,1,0,228,128,1,0,228,128,12,0,0,3,
+2,0,2,128,15,0,255,160,1,0,255,128,5,0,0,3,
+0,0,8,128,0,0,255,128,2,0,85,128,4,0,0,4,
+2,0,2,128,15,0,170,160,1,0,255,128,2,0,0,128,
+7,0,0,2,1,0,8,128,1,0,255,128,5,0,0,3,
+1,0,7,128,1,0,255,128,1,0,228,128,6,0,0,2,
+1,0,8,128,2,0,85,128,4,0,0,4,0,0,8,128,
+0,0,255,128,1,0,255,129,1,0,255,128,8,0,0,3,
+1,0,8,128,1,0,228,128,21,0,228,160,8,0,0,3,
+1,0,1,128,1,0,228,144,1,0,228,128,11,0,0,3,
+1,0,9,128,1,0,228,128,24,0,85,160,5,0,0,3,
+1,0,7,128,1,0,0,128,3,0,228,144,5,0,0,3,
+1,0,7,128,1,0,228,128,3,0,228,160,2,0,0,3,
+1,0,8,128,1,0,255,128,15,0,0,161,5,0,0,3,
+1,0,8,128,1,0,255,128,15,0,85,160,11,0,0,3,
+1,0,8,128,1,0,255,128,24,0,85,160,10,0,0,3,
+1,0,8,128,1,0,255,128,24,0,0,160,5,0,0,3,
+0,0,8,128,0,0,255,128,1,0,255,128,5,0,0,3,
+1,0,7,128,0,0,255,128,1,0,228,128,10,0,0,3,
+1,0,7,128,1,0,228,128,24,0,0,160,2,0,0,3,
+0,0,7,128,0,0,228,128,1,0,228,128,5,0,0,3,
+0,0,8,128,10,0,255,160,10,0,255,160,12,0,0,3,
+0,0,8,128,0,0,255,129,0,0,255,128,4,0,0,4,
+1,0,7,128,0,0,228,144,10,0,255,161,10,0,228,160,
+8,0,0,3,1,0,8,128,1,0,228,128,1,0,228,128,
+12,0,0,3,2,0,2,128,16,0,255,160,1,0,255,128,
+5,0,0,3,0,0,8,128,0,0,255,128,2,0,85,128,
+4,0,0,4,2,0,2,128,16,0,170,160,1,0,255,128,
+2,0,0,128,7,0,0,2,1,0,8,128,1,0,255,128,
+5,0,0,3,1,0,7,128,1,0,255,128,1,0,228,128,
+6,0,0,2,1,0,8,128,2,0,85,128,4,0,0,4,
+0,0,8,128,0,0,255,128,1,0,255,129,1,0,255,128,
+8,0,0,3,1,0,8,128,1,0,228,128,22,0,228,160,
+8,0,0,3,1,0,1,128,1,0,228,144,1,0,228,128,
+11,0,0,3,1,0,9,128,1,0,228,128,24,0,85,160,
+5,0,0,3,1,0,7,128,1,0,0,128,3,0,228,144,
+5,0,0,3,1,0,7,128,1,0,228,128,4,0,228,160,
+2,0,0,3,1,0,8,128,1,0,255,128,16,0,0,161,
+5,0,0,3,1,0,8,128,1,0,255,128,16,0,85,160,
+11,0,0,3,1,0,8,128,1,0,255,128,24,0,85,160,
+10,0,0,3,1,0,8,128,1,0,255,128,24,0,0,160,
+5,0,0,3,0,0,8,128,0,0,255,128,1,0,255,128,
+5,0,0,3,1,0,7,128,0,0,255,128,1,0,228,128,
+10,0,0,3,1,0,7,128,1,0,228,128,24,0,0,160,
+2,0,0,3,0,0,7,128,0,0,228,128,1,0,228,128,
+5,0,0,3,0,0,8,128,11,0,255,160,11,0,255,160,
+12,0,0,3,0,0,8,128,0,0,255,129,0,0,255,128,
+4,0,0,4,1,0,7,128,0,0,228,144,11,0,255,161,
+11,0,228,160,8,0,0,3,1,0,8,128,1,0,228,128,
+1,0,228,128,12,0,0,3,2,0,2,128,17,0,255,160,
+1,0,255,128,5,0,0,3,0,0,8,128,0,0,255,128,
+2,0,85,128,4,0,0,4,2,0,1,128,17,0,170,160,
+1,0,255,128,2,0,0,128,7,0,0,2,1,0,8,128,
+1,0,255,128,5,0,0,3,1,0,7,128,1,0,255,128,
+1,0,228,128,6,0,0,2,1,0,8,128,2,0,0,128,
+4,0,0,4,0,0,8,128,0,0,255,128,1,0,255,129,
+1,0,255,128,8,0,0,3,1,0,8,128,1,0,228,128,
+23,0,228,160,8,0,0,3,1,0,1,128,1,0,228,144,
+1,0,228,128,11,0,0,3,1,0,9,128,1,0,228,128,
+24,0,85,160,5,0,0,3,1,0,7,128,1,0,0,128,
+3,0,228,144,5,0,0,3,1,0,7,128,1,0,228,128,
+5,0,228,160,2,0,0,3,1,0,8,128,1,0,255,128,
+17,0,0,161,5,0,0,3,1,0,8,128,1,0,255,128,
+17,0,85,160,11,0,0,3,1,0,8,128,1,0,255,128,
+24,0,85,160,10,0,0,3,1,0,8,128,1,0,255,128,
+24,0,0,160,5,0,0,3,0,0,8,128,0,0,255,128,
+1,0,255,128,5,0,0,3,1,0,7,128,0,0,255,128,
+1,0,228,128,10,0,0,3,1,0,7,128,1,0,228,128,
+24,0,0,160,2,0,0,3,0,0,7,224,0,0,228,128,
+1,0,228,128,1,0,0,2,1,0,7,224,4,0,228,144,
+255,255,0,0,83,72,68,82,24,4,0,0,64,0,240,255,
+6,1,0,0,89,8,0,4,70,142,32,0,0,0,0,0,
+41,0,0,0,95,0,0,3,114,16,16,0,0,0,0,0,
+95,0,0,3,114,16,16,0,1,0,0,0,95,0,0,3,
+114,16,16,0,3,0,0,0,95,0,0,3,114,16,16,0,
+4,0,0,0,95,0,0,3,114,16,16,0,5,0,0,0,
+101,0,0,3,114,32,16,0,0,0,0,0,101,0,0,3,
+114,32,16,0,1,0,0,0,104,0,0,2,3,0,0,0,
+54,0,0,5,114,0,16,0,0,0,0,0,70,18,16,0,
+5,0,0,0,54,0,0,5,130,0,16,0,0,0,0,0,
+1,64,0,0,0,0,0,0,48,0,0,1,33,0,0,7,
+18,0,16,0,1,0,0,0,58,0,16,0,0,0,0,0,
+1,64,0,0,6,0,0,0,3,0,4,3,10,0,16,0,
+1,0,0,0,50,0,0,16,114,0,16,0,1,0,0,0,
+70,18,16,128,65,0,0,0,0,0,0,0,246,143,32,6,
+0,0,0,0,18,0,0,0,58,0,16,0,0,0,0,0,
+70,130,32,6,0,0,0,0,18,0,0,0,58,0,16,0,
+0,0,0,0,16,0,0,7,130,0,16,0,1,0,0,0,
+70,2,16,0,1,0,0,0,70,2,16,0,1,0,0,0,
+50,0,0,12,18,0,16,0,2,0,0,0,42,128,32,6,
+0,0,0,0,26,0,0,0,58,0,16,0,0,0,0,0,
+58,0,16,0,1,0,0,0,1,64,0,0,0,0,128,63,
+14,0,0,10,18,0,16,0,2,0,0,0,2,64,0,0,
+0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,
+10,0,16,0,2,0,0,0,57,0,0,10,34,0,16,0,
+2,0,0,0,1,64,0,0,0,0,0,0,58,128,32,6,
+0,0,0,0,18,0,0,0,58,0,16,0,0,0,0,0,
+49,0,0,10,66,0,16,0,2,0,0,0,58,128,32,6,
+0,0,0,0,26,0,0,0,58,0,16,0,0,0,0,0,
+58,0,16,0,1,0,0,0,1,0,0,7,34,0,16,0,
+2,0,0,0,42,0,16,0,2,0,0,0,26,0,16,0,
+2,0,0,0,55,0,0,9,18,0,16,0,2,0,0,0,
+26,0,16,0,2,0,0,0,1,64,0,0,0,0,0,0,
+10,0,16,0,2,0,0,0,68,0,0,5,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,56,0,0,7,
+114,0,16,0,1,0,0,0,246,15,16,0,1,0,0,0,
+70,2,16,0,1,0,0,0,16,0,0,10,130,0,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,70,130,32,6,
+0,0,0,0,34,0,0,0,58,0,16,0,0,0,0,0,
+52,0,0,7,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,1,64,0,0,0,0,0,0,0,0,0,11,
+130,0,16,0,1,0,0,0,58,0,16,0,1,0,0,0,
+10,128,32,134,65,0,0,0,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,56,32,0,10,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,26,128,32,6,
+0,0,0,0,26,0,0,0,58,0,16,0,0,0,0,0,
+56,0,0,7,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,10,0,16,0,2,0,0,0,16,0,0,7,
+18,0,16,0,1,0,0,0,70,18,16,0,1,0,0,0,
+70,2,16,0,1,0,0,0,52,0,0,7,18,0,16,0,
+1,0,0,0,10,0,16,0,1,0,0,0,1,64,0,0,
+0,0,0,0,56,0,0,7,114,0,16,0,1,0,0,0,
+6,0,16,0,1,0,0,0,70,18,16,0,3,0,0,0,
+56,0,0,10,114,0,16,0,1,0,0,0,70,2,16,0,
+1,0,0,0,70,130,32,6,0,0,0,0,10,0,0,0,
+58,0,16,0,0,0,0,0,56,0,0,7,114,0,16,0,
+1,0,0,0,246,15,16,0,1,0,0,0,70,2,16,0,
+1,0,0,0,51,0,0,10,114,0,16,0,1,0,0,0,
+70,2,16,0,1,0,0,0,2,64,0,0,0,0,128,63,
+0,0,128,63,0,0,128,63,0,0,0,0,0,0,0,7,
+114,0,16,0,0,0,0,0,70,2,16,0,0,0,0,0,
+70,2,16,0,1,0,0,0,30,0,0,7,130,0,16,0,
+0,0,0,0,58,0,16,0,0,0,0,0,1,64,0,0,
+1,0,0,0,22,0,0,1,54,0,0,5,114,32,16,0,
+0,0,0,0,70,2,16,0,0,0,0,0,54,0,0,5,
+114,32,16,0,1,0,0,0,70,18,16,0,4,0,0,0,
+62,0,0,1,83,84,65,84,116,0,0,0,32,0,0,0,
+3,0,0,0,0,0,0,0,7,0,0,0,20,0,0,0,
+2,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,4,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,188,4,0,0,1,0,0,0,108,0,0,0,
+1,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+142,4,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,92,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,85,110,105,116,121,70,70,86,101,114,116,101,
+120,0,171,171,92,0,0,0,14,0,0,0,132,0,0,0,
+240,3,0,0,0,0,0,0,0,0,0,0,180,2,0,0,
+0,0,0,0,64,0,0,0,0,0,0,0,204,2,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,240,2,0,0,64,0,0,0,64,0,0,0,
+0,0,0,0,204,2,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,253,2,0,0,
+128,0,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,56,3,0,0,144,0,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,71,3,0,0,
+160,0,0,0,128,0,0,0,2,0,0,0,88,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,124,3,0,0,32,1,0,0,128,0,0,0,
+2,0,0,0,140,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,176,3,0,0,
+160,1,0,0,128,0,0,0,2,0,0,0,192,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,228,3,0,0,32,2,0,0,128,0,0,0,
+2,0,0,0,244,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,24,4,0,0,
+160,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,39,4,0,0,176,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,54,4,0,0,
+192,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,66,4,0,0,208,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,82,4,0,0,
+224,2,0,0,0,1,0,0,0,0,0,0,96,4,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,132,4,0,0,224,3,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,102,102,95,109,
+97,116,114,105,120,95,109,118,112,0,102,108,111,97,116,52,
+120,52,0,171,3,0,3,0,4,0,4,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,194,2,0,0,102,102,95,109,97,116,114,105,
+120,95,109,118,0,102,102,95,118,101,99,95,99,111,108,111,
+114,0,102,108,111,97,116,52,0,171,171,171,1,0,3,0,
+1,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,118,101,99,95,97,109,98,105,101,110,116,0,102,
+102,95,108,105,103,104,116,95,99,111,108,111,114,0,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,112,111,115,
+0,171,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,97,116,116,101,110,0,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,115,112,111,116,0,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,109,97,116,95,100,105,102,102,117,115,101,0,102,
+102,95,109,97,116,95,97,109,98,105,101,110,116,0,102,102,
+95,109,97,116,95,115,112,101,99,0,102,102,95,109,97,116,
+95,101,109,105,115,115,105,111,110,0,102,102,95,109,97,116,
+114,105,120,95,116,101,120,0,3,0,3,0,4,0,4,0,
+4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,194,2,0,0,102,102,95,102,
+111,103,95,118,115,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,164,1,0,0,7,0,0,0,
+8,0,0,0,88,1,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,106,1,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,118,1,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,1,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,128,1,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,2,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,136,1,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,149,1,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+3,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,159,1,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,5,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,67,111,109,112,117,116,101,83,112,111,116,76,
+105,103,104,116,54,0,101,121,101,80,111,115,105,116,105,111,
+110,0,101,121,101,78,111,114,109,97,108,0,118,105,101,119,
+68,105,114,0,100,105,102,102,117,115,101,67,111,108,111,114,
+0,115,112,101,99,67,111,108,111,114,0,97,109,98,0,171,
+76,73,66,70,80,16,0,0,68,88,66,67,234,246,83,85,
+128,3,122,132,33,202,50,54,82,138,167,19,1,0,0,0,
+80,16,0,0,5,0,0,0,52,0,0,0,156,3,0,0,
+96,9,0,0,220,9,0,0,160,14,0,0,65,111,110,57,
+96,3,0,0,96,3,0,0,0,2,86,76,48,3,0,0,
+48,0,0,0,1,0,36,0,0,0,48,0,0,0,48,0,
+0,0,36,0,0,0,48,0,0,0,0,0,45,0,0,0,
+0,0,0,0,0,2,86,76,81,0,0,5,45,0,15,160,
+0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,
+48,0,0,5,0,0,15,240,6,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,31,0,0,2,5,0,0,128,
+0,0,15,144,31,0,0,2,5,0,1,128,1,0,15,144,
+31,0,0,2,5,0,2,128,2,0,15,144,31,0,0,2,
+5,0,3,128,3,0,15,144,31,0,0,2,5,0,4,128,
+4,0,15,144,31,0,0,2,5,0,5,128,5,0,15,144,
+1,0,0,2,0,0,7,128,4,0,228,144,1,0,0,2,
+1,0,7,128,5,0,228,144,27,0,0,2,0,8,228,240,
+0,0,228,240,4,0,0,6,2,0,7,128,0,0,228,144,
+18,32,255,161,0,8,228,240,18,32,228,160,0,8,228,240,
+8,0,0,3,0,0,8,128,2,0,228,128,2,0,228,128,
+7,0,0,2,1,0,8,128,0,0,255,128,4,0,0,4,
+3,0,7,128,2,0,228,128,1,0,255,128,2,0,228,144,
+36,0,0,2,4,0,7,128,3,0,228,128,8,0,0,3,
+2,0,8,128,1,0,228,144,4,0,228,128,5,0,0,5,
+3,0,1,128,18,32,255,160,0,8,228,240,18,32,255,160,
+0,8,228,240,12,0,0,3,3,0,1,128,3,0,0,129,
+3,0,0,128,12,0,0,4,3,0,2,128,26,32,255,160,
+0,8,228,240,0,0,255,128,5,0,0,3,3,0,1,128,
+3,0,85,128,3,0,0,128,5,0,0,4,0,0,8,128,
+0,0,255,128,26,32,170,160,0,8,228,240,2,0,0,3,
+0,0,8,128,0,0,255,128,45,0,170,160,6,0,0,2,
+0,0,8,128,0,0,255,128,4,0,0,4,0,0,8,128,
+3,0,0,128,0,0,255,129,0,0,255,128,11,0,0,3,
+2,0,8,128,2,0,255,128,45,0,85,160,32,0,0,3,
+3,0,1,128,2,0,255,128,44,0,255,160,10,0,0,3,
+2,0,8,128,3,0,0,128,45,0,170,160,5,0,0,3,
+2,0,7,128,1,0,255,128,2,0,228,128,8,0,0,3,
+1,0,8,128,1,0,228,144,2,0,228,128,8,0,0,4,
+2,0,1,128,2,0,228,128,34,32,228,160,0,8,228,240,
+11,0,0,3,2,0,1,128,2,0,0,128,45,0,85,160,
+2,0,0,4,2,0,1,128,2,0,0,128,26,32,0,161,
+0,8,228,240,5,0,0,4,2,0,1,128,2,0,0,128,
+26,32,85,160,0,8,228,240,11,0,0,3,2,0,1,128,
+2,0,0,128,45,0,85,160,10,0,0,3,2,0,1,128,
+2,0,0,128,45,0,170,160,5,0,0,3,0,0,8,128,
+0,0,255,128,2,0,0,128,11,0,0,3,1,0,8,128,
+1,0,255,128,45,0,85,160,12,0,0,3,2,0,1,128,
+45,0,85,160,1,0,255,128,5,0,0,3,2,0,2,128,
+2,0,255,128,0,0,255,128,5,0,0,4,2,0,14,128,
+2,0,85,128,10,32,144,160,0,8,228,240,4,0,0,4,
+0,0,7,128,2,0,0,128,2,0,249,128,0,0,228,128,
+5,0,0,3,2,0,7,128,1,0,255,128,3,0,228,144,
+5,0,0,4,2,0,7,128,2,0,228,128,10,32,228,160,
+0,8,228,240,5,0,0,3,2,0,7,128,0,0,255,128,
+2,0,228,128,10,0,0,3,2,0,7,128,2,0,228,128,
+45,0,170,160,2,0,0,3,1,0,7,128,1,0,228,128,
+2,0,228,128,29,0,0,0,1,0,0,2,1,0,7,224,
+0,0,228,128,1,0,0,2,0,0,7,224,1,0,228,128,
+255,255,0,0,83,72,68,82,188,5,0,0,64,0,240,255,
+111,1,0,0,89,8,0,4,70,142,32,0,0,0,0,0,
+45,0,0,0,95,0,0,3,114,16,16,0,0,0,0,0,
+95,0,0,3,114,16,16,0,1,0,0,0,95,0,0,3,
+114,16,16,0,2,0,0,0,95,0,0,3,114,16,16,0,
+3,0,0,0,95,0,0,3,114,16,16,0,4,0,0,0,
+95,0,0,3,114,16,16,0,5,0,0,0,101,0,0,3,
+114,32,16,0,0,0,0,0,101,0,0,3,114,32,16,0,
+1,0,0,0,104,0,0,2,4,0,0,0,54,0,0,5,
+114,0,16,0,0,0,0,0,70,18,16,0,4,0,0,0,
+54,0,0,5,114,0,16,0,1,0,0,0,70,18,16,0,
+5,0,0,0,54,0,0,5,130,0,16,0,0,0,0,0,
+1,64,0,0,0,0,0,0,48,0,0,1,33,0,0,7,
+130,0,16,0,1,0,0,0,58,0,16,0,0,0,0,0,
+1,64,0,0,6,0,0,0,3,0,4,3,58,0,16,0,
+1,0,0,0,50,0,0,16,114,0,16,0,2,0,0,0,
+70,18,16,128,65,0,0,0,0,0,0,0,246,143,32,6,
+0,0,0,0,18,0,0,0,58,0,16,0,0,0,0,0,
+70,130,32,6,0,0,0,0,18,0,0,0,58,0,16,0,
+0,0,0,0,16,0,0,7,130,0,16,0,1,0,0,0,
+70,2,16,0,2,0,0,0,70,2,16,0,2,0,0,0,
+50,0,0,12,130,0,16,0,2,0,0,0,42,128,32,6,
+0,0,0,0,26,0,0,0,58,0,16,0,0,0,0,0,
+58,0,16,0,1,0,0,0,1,64,0,0,0,0,128,63,
+14,0,0,10,130,0,16,0,2,0,0,0,2,64,0,0,
+0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,
+58,0,16,0,2,0,0,0,57,0,0,10,18,0,16,0,
+3,0,0,0,1,64,0,0,0,0,0,0,58,128,32,6,
+0,0,0,0,18,0,0,0,58,0,16,0,0,0,0,0,
+49,0,0,10,34,0,16,0,3,0,0,0,58,128,32,6,
+0,0,0,0,26,0,0,0,58,0,16,0,0,0,0,0,
+58,0,16,0,1,0,0,0,1,0,0,7,18,0,16,0,
+3,0,0,0,26,0,16,0,3,0,0,0,10,0,16,0,
+3,0,0,0,55,0,0,9,130,0,16,0,2,0,0,0,
+10,0,16,0,3,0,0,0,1,64,0,0,0,0,0,0,
+58,0,16,0,2,0,0,0,68,0,0,5,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,56,0,0,7,
+114,0,16,0,3,0,0,0,246,15,16,0,1,0,0,0,
+70,2,16,0,2,0,0,0,16,0,0,10,130,0,16,0,
+3,0,0,0,70,2,16,0,3,0,0,0,70,130,32,6,
+0,0,0,0,34,0,0,0,58,0,16,0,0,0,0,0,
+52,0,0,7,130,0,16,0,3,0,0,0,58,0,16,0,
+3,0,0,0,1,64,0,0,0,0,0,0,0,0,0,11,
+130,0,16,0,3,0,0,0,58,0,16,0,3,0,0,0,
+10,128,32,134,65,0,0,0,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,56,32,0,10,130,0,16,0,
+3,0,0,0,58,0,16,0,3,0,0,0,26,128,32,6,
+0,0,0,0,26,0,0,0,58,0,16,0,0,0,0,0,
+56,0,0,7,130,0,16,0,2,0,0,0,58,0,16,0,
+2,0,0,0,58,0,16,0,3,0,0,0,16,0,0,7,
+18,0,16,0,3,0,0,0,70,18,16,0,1,0,0,0,
+70,2,16,0,3,0,0,0,52,0,0,7,18,0,16,0,
+3,0,0,0,10,0,16,0,3,0,0,0,1,64,0,0,
+0,0,0,0,56,0,0,7,226,0,16,0,3,0,0,0,
+6,0,16,0,3,0,0,0,6,25,16,0,3,0,0,0,
+56,0,0,10,226,0,16,0,3,0,0,0,86,14,16,0,
+3,0,0,0,6,137,32,6,0,0,0,0,10,0,0,0,
+58,0,16,0,0,0,0,0,49,0,0,7,18,0,16,0,
+3,0,0,0,1,64,0,0,0,0,0,0,10,0,16,0,
+3,0,0,0,31,0,4,3,10,0,16,0,3,0,0,0,
+50,0,0,9,114,0,16,0,2,0,0,0,70,2,16,0,
+2,0,0,0,246,15,16,0,1,0,0,0,70,18,16,0,
+2,0,0,0,16,0,0,7,130,0,16,0,1,0,0,0,
+70,2,16,0,2,0,0,0,70,2,16,0,2,0,0,0,
+68,0,0,5,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,56,0,0,7,114,0,16,0,2,0,0,0,
+246,15,16,0,1,0,0,0,70,2,16,0,2,0,0,0,
+16,0,0,7,130,0,16,0,1,0,0,0,70,18,16,0,
+1,0,0,0,70,2,16,0,2,0,0,0,52,0,0,7,
+130,0,16,0,1,0,0,0,58,0,16,0,1,0,0,0,
+1,64,0,0,0,0,0,0,47,0,0,5,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,56,0,0,8,
+130,0,16,0,1,0,0,0,58,0,16,0,1,0,0,0,
+58,128,32,0,0,0,0,0,44,0,0,0,25,0,0,5,
+130,0,16,0,1,0,0,0,58,0,16,0,1,0,0,0,
+51,0,0,7,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,1,64,0,0,0,0,128,63,56,0,0,7,
+130,0,16,0,1,0,0,0,58,0,16,0,1,0,0,0,
+58,0,16,0,2,0,0,0,50,0,0,12,114,0,16,0,
+0,0,0,0,246,15,16,0,1,0,0,0,70,130,32,6,
+0,0,0,0,10,0,0,0,58,0,16,0,0,0,0,0,
+70,2,16,0,0,0,0,0,21,0,0,1,56,0,0,7,
+114,0,16,0,2,0,0,0,246,15,16,0,2,0,0,0,
+150,7,16,0,3,0,0,0,51,0,0,10,114,0,16,0,
+2,0,0,0,70,2,16,0,2,0,0,0,2,64,0,0,
+0,0,128,63,0,0,128,63,0,0,128,63,0,0,0,0,
+0,0,0,7,114,0,16,0,1,0,0,0,70,2,16,0,
+1,0,0,0,70,2,16,0,2,0,0,0,30,0,0,7,
+130,0,16,0,0,0,0,0,58,0,16,0,0,0,0,0,
+1,64,0,0,1,0,0,0,22,0,0,1,54,0,0,5,
+114,32,16,0,1,0,0,0,70,2,16,0,0,0,0,0,
+54,0,0,5,114,32,16,0,0,0,0,0,70,2,16,0,
+1,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+48,0,0,0,4,0,0,0,0,0,0,0,8,0,0,0,
+33,0,0,0,2,0,0,0,1,0,0,0,1,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,188,4,0,0,1,0,0,0,
+108,0,0,0,1,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,142,4,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,85,110,105,116,121,70,70,86,
+101,114,116,101,120,0,171,171,92,0,0,0,14,0,0,0,
+132,0,0,0,240,3,0,0,0,0,0,0,0,0,0,0,
+180,2,0,0,0,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,240,2,0,0,64,0,0,0,
+64,0,0,0,0,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+253,2,0,0,128,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,56,3,0,0,144,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+71,3,0,0,160,0,0,0,128,0,0,0,2,0,0,0,
+88,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,124,3,0,0,32,1,0,0,
+128,0,0,0,2,0,0,0,140,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+176,3,0,0,160,1,0,0,128,0,0,0,2,0,0,0,
+192,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,228,3,0,0,32,2,0,0,
+128,0,0,0,2,0,0,0,244,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+24,4,0,0,160,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,39,4,0,0,176,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+54,4,0,0,192,2,0,0,16,0,0,0,2,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,66,4,0,0,208,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+82,4,0,0,224,2,0,0,0,1,0,0,0,0,0,0,
+96,4,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,132,4,0,0,224,3,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+102,102,95,109,97,116,114,105,120,95,109,118,112,0,102,108,
+111,97,116,52,120,52,0,171,3,0,3,0,4,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,194,2,0,0,102,102,95,109,
+97,116,114,105,120,95,109,118,0,102,102,95,118,101,99,95,
+99,111,108,111,114,0,102,108,111,97,116,52,0,171,171,171,
+1,0,3,0,1,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,118,101,99,95,97,109,98,105,101,
+110,116,0,102,102,95,108,105,103,104,116,95,99,111,108,111,
+114,0,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,112,111,115,0,171,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,97,116,116,101,110,0,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,115,112,111,116,0,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,109,97,116,95,100,105,102,102,117,
+115,101,0,102,102,95,109,97,116,95,97,109,98,105,101,110,
+116,0,102,102,95,109,97,116,95,115,112,101,99,0,102,102,
+95,109,97,116,95,101,109,105,115,115,105,111,110,0,102,102,
+95,109,97,116,114,105,120,95,116,101,120,0,3,0,3,0,
+4,0,4,0,4,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,194,2,0,0,
+102,102,95,102,111,103,95,118,115,0,77,105,99,114,111,115,
+111,102,116,32,40,82,41,32,72,76,83,76,32,83,104,97,
+100,101,114,32,67,111,109,112,105,108,101,114,32,54,46,51,
+46,57,52,49,53,46,48,0,76,70,83,48,168,1,0,0,
+7,0,0,0,8,0,0,0,88,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,110,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,122,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,132,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,140,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,153,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,3,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,163,1,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,5,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,67,111,109,112,117,116,101,83,
+112,111,116,76,105,103,104,116,83,112,101,99,54,0,101,121,
+101,80,111,115,105,116,105,111,110,0,101,121,101,78,111,114,
+109,97,108,0,118,105,101,119,68,105,114,0,100,105,102,102,
+117,115,101,67,111,108,111,114,0,115,112,101,99,67,111,108,
+111,114,0,97,109,98,0,171,76,73,66,70,188,22,0,0,
+68,88,66,67,203,163,211,164,74,232,227,110,255,5,54,13,
+127,129,226,75,1,0,0,0,188,22,0,0,5,0,0,0,
+52,0,0,0,176,11,0,0,208,15,0,0,76,16,0,0,
+16,21,0,0,65,111,110,57,116,11,0,0,116,11,0,0,
+0,2,86,76,32,11,0,0,84,0,0,0,4,0,36,0,
+0,0,84,0,0,0,84,0,0,0,36,0,0,0,84,0,
+0,0,10,0,7,0,0,0,0,0,0,0,0,0,18,0,
+7,0,7,0,0,0,0,0,0,0,26,0,7,0,14,0,
+0,0,0,0,0,0,34,0,7,0,21,0,0,0,0,0,
+0,2,86,76,81,0,0,5,28,0,15,160,0,0,128,63,
+0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,2,
+5,0,0,128,0,0,15,144,31,0,0,2,5,0,1,128,
+1,0,15,144,31,0,0,2,5,0,3,128,3,0,15,144,
+31,0,0,2,5,0,4,128,4,0,15,144,31,0,0,2,
+5,0,5,128,5,0,15,144,5,0,0,3,0,0,1,128,
+7,0,255,160,7,0,255,160,12,0,0,3,0,0,1,128,
+0,0,0,129,0,0,0,128,4,0,0,4,0,0,14,128,
+0,0,144,144,7,0,255,161,7,0,144,160,8,0,0,3,
+1,0,1,128,0,0,249,128,0,0,249,128,12,0,0,3,
+1,0,2,128,14,0,255,160,1,0,0,128,5,0,0,3,
+0,0,1,128,0,0,0,128,1,0,85,128,1,0,0,2,
+2,0,1,128,28,0,0,160,4,0,0,4,1,0,2,128,
+14,0,170,160,1,0,0,128,2,0,0,128,7,0,0,2,
+1,0,1,128,1,0,0,128,5,0,0,3,0,0,14,128,
+0,0,228,128,1,0,0,128,6,0,0,2,1,0,1,128,
+1,0,85,128,4,0,0,4,0,0,1,128,0,0,0,128,
+1,0,0,129,1,0,0,128,8,0,0,3,1,0,1,128,
+0,0,249,128,21,0,228,160,8,0,0,3,0,0,2,128,
+1,0,228,144,0,0,249,128,11,0,0,3,0,0,2,128,
+0,0,85,128,28,0,85,160,5,0,0,3,0,0,14,128,
+0,0,85,128,3,0,144,144,5,0,0,3,0,0,14,128,
+0,0,228,128,0,0,144,160,11,0,0,3,1,0,1,128,
+1,0,0,128,28,0,85,160,2,0,0,3,1,0,1,128,
+1,0,0,128,14,0,0,161,5,0,0,3,1,0,1,128,
+1,0,0,128,14,0,85,160,11,0,0,3,1,0,1,128,
+1,0,0,128,28,0,85,160,10,0,0,3,1,0,1,128,
+1,0,0,128,28,0,0,160,5,0,0,3,0,0,1,128,
+0,0,0,128,1,0,0,128,5,0,0,3,0,0,7,128,
+0,0,0,128,0,0,249,128,10,0,0,3,0,0,7,128,
+0,0,228,128,28,0,0,160,2,0,0,3,0,0,7,128,
+0,0,228,128,5,0,228,144,5,0,0,3,0,0,8,128,
+8,0,255,160,8,0,255,160,12,0,0,3,0,0,8,128,
+0,0,255,129,0,0,255,128,4,0,0,4,1,0,7,128,
+0,0,228,144,8,0,255,161,8,0,228,160,8,0,0,3,
+1,0,8,128,1,0,228,128,1,0,228,128,12,0,0,3,
+2,0,2,128,15,0,255,160,1,0,255,128,5,0,0,3,
+0,0,8,128,0,0,255,128,2,0,85,128,4,0,0,4,
+2,0,2,128,15,0,170,160,1,0,255,128,2,0,0,128,
+7,0,0,2,1,0,8,128,1,0,255,128,5,0,0,3,
+1,0,7,128,1,0,255,128,1,0,228,128,6,0,0,2,
+1,0,8,128,2,0,85,128,4,0,0,4,0,0,8,128,
+0,0,255,128,1,0,255,129,1,0,255,128,8,0,0,3,
+1,0,8,128,1,0,228,128,22,0,228,160,8,0,0,3,
+1,0,1,128,1,0,228,144,1,0,228,128,11,0,0,3,
+1,0,9,128,1,0,228,128,28,0,85,160,5,0,0,3,
+1,0,7,128,1,0,0,128,3,0,228,144,5,0,0,3,
+1,0,7,128,1,0,228,128,1,0,228,160,2,0,0,3,
+1,0,8,128,1,0,255,128,15,0,0,161,5,0,0,3,
+1,0,8,128,1,0,255,128,15,0,85,160,11,0,0,3,
+1,0,8,128,1,0,255,128,28,0,85,160,10,0,0,3,
+1,0,8,128,1,0,255,128,28,0,0,160,5,0,0,3,
+0,0,8,128,0,0,255,128,1,0,255,128,5,0,0,3,
+1,0,7,128,0,0,255,128,1,0,228,128,10,0,0,3,
+1,0,7,128,1,0,228,128,28,0,0,160,2,0,0,3,
+0,0,7,128,0,0,228,128,1,0,228,128,5,0,0,3,
+0,0,8,128,9,0,255,160,9,0,255,160,12,0,0,3,
+0,0,8,128,0,0,255,129,0,0,255,128,4,0,0,4,
+1,0,7,128,0,0,228,144,9,0,255,161,9,0,228,160,
+8,0,0,3,1,0,8,128,1,0,228,128,1,0,228,128,
+12,0,0,3,2,0,2,128,16,0,255,160,1,0,255,128,
+5,0,0,3,0,0,8,128,0,0,255,128,2,0,85,128,
+4,0,0,4,2,0,2,128,16,0,170,160,1,0,255,128,
+2,0,0,128,7,0,0,2,1,0,8,128,1,0,255,128,
+5,0,0,3,1,0,7,128,1,0,255,128,1,0,228,128,
+6,0,0,2,1,0,8,128,2,0,85,128,4,0,0,4,
+0,0,8,128,0,0,255,128,1,0,255,129,1,0,255,128,
+8,0,0,3,1,0,8,128,1,0,228,128,23,0,228,160,
+8,0,0,3,1,0,1,128,1,0,228,144,1,0,228,128,
+11,0,0,3,1,0,9,128,1,0,228,128,28,0,85,160,
+5,0,0,3,1,0,7,128,1,0,0,128,3,0,228,144,
+5,0,0,3,1,0,7,128,1,0,228,128,2,0,228,160,
+2,0,0,3,1,0,8,128,1,0,255,128,16,0,0,161,
+5,0,0,3,1,0,8,128,1,0,255,128,16,0,85,160,
+11,0,0,3,1,0,8,128,1,0,255,128,28,0,85,160,
+10,0,0,3,1,0,8,128,1,0,255,128,28,0,0,160,
+5,0,0,3,0,0,8,128,0,0,255,128,1,0,255,128,
+5,0,0,3,1,0,7,128,0,0,255,128,1,0,228,128,
+10,0,0,3,1,0,7,128,1,0,228,128,28,0,0,160,
+2,0,0,3,0,0,7,128,0,0,228,128,1,0,228,128,
+5,0,0,3,0,0,8,128,10,0,255,160,10,0,255,160,
+12,0,0,3,0,0,8,128,0,0,255,129,0,0,255,128,
+4,0,0,4,1,0,7,128,0,0,228,144,10,0,255,161,
+10,0,228,160,8,0,0,3,1,0,8,128,1,0,228,128,
+1,0,228,128,12,0,0,3,2,0,2,128,17,0,255,160,
+1,0,255,128,5,0,0,3,0,0,8,128,0,0,255,128,
+2,0,85,128,4,0,0,4,2,0,2,128,17,0,170,160,
+1,0,255,128,2,0,0,128,7,0,0,2,1,0,8,128,
+1,0,255,128,5,0,0,3,1,0,7,128,1,0,255,128,
+1,0,228,128,6,0,0,2,1,0,8,128,2,0,85,128,
+4,0,0,4,0,0,8,128,0,0,255,128,1,0,255,129,
+1,0,255,128,8,0,0,3,1,0,8,128,1,0,228,128,
+24,0,228,160,8,0,0,3,1,0,1,128,1,0,228,144,
+1,0,228,128,11,0,0,3,1,0,9,128,1,0,228,128,
+28,0,85,160,5,0,0,3,1,0,7,128,1,0,0,128,
+3,0,228,144,5,0,0,3,1,0,7,128,1,0,228,128,
+3,0,228,160,2,0,0,3,1,0,8,128,1,0,255,128,
+17,0,0,161,5,0,0,3,1,0,8,128,1,0,255,128,
+17,0,85,160,11,0,0,3,1,0,8,128,1,0,255,128,
+28,0,85,160,10,0,0,3,1,0,8,128,1,0,255,128,
+28,0,0,160,5,0,0,3,0,0,8,128,0,0,255,128,
+1,0,255,128,5,0,0,3,1,0,7,128,0,0,255,128,
+1,0,228,128,10,0,0,3,1,0,7,128,1,0,228,128,
+28,0,0,160,2,0,0,3,0,0,7,128,0,0,228,128,
+1,0,228,128,5,0,0,3,0,0,8,128,11,0,255,160,
+11,0,255,160,12,0,0,3,0,0,8,128,0,0,255,129,
+0,0,255,128,4,0,0,4,1,0,7,128,0,0,228,144,
+11,0,255,161,11,0,228,160,8,0,0,3,1,0,8,128,
+1,0,228,128,1,0,228,128,12,0,0,3,2,0,2,128,
+18,0,255,160,1,0,255,128,5,0,0,3,0,0,8,128,
+0,0,255,128,2,0,85,128,4,0,0,4,2,0,2,128,
+18,0,170,160,1,0,255,128,2,0,0,128,7,0,0,2,
+1,0,8,128,1,0,255,128,5,0,0,3,1,0,7,128,
+1,0,255,128,1,0,228,128,6,0,0,2,1,0,8,128,
+2,0,85,128,4,0,0,4,0,0,8,128,0,0,255,128,
+1,0,255,129,1,0,255,128,8,0,0,3,1,0,8,128,
+1,0,228,128,25,0,228,160,8,0,0,3,1,0,1,128,
+1,0,228,144,1,0,228,128,11,0,0,3,1,0,9,128,
+1,0,228,128,28,0,85,160,5,0,0,3,1,0,7,128,
+1,0,0,128,3,0,228,144,5,0,0,3,1,0,7,128,
+1,0,228,128,4,0,228,160,2,0,0,3,1,0,8,128,
+1,0,255,128,18,0,0,161,5,0,0,3,1,0,8,128,
+1,0,255,128,18,0,85,160,11,0,0,3,1,0,8,128,
+1,0,255,128,28,0,85,160,10,0,0,3,1,0,8,128,
+1,0,255,128,28,0,0,160,5,0,0,3,0,0,8,128,
+0,0,255,128,1,0,255,128,5,0,0,3,1,0,7,128,
+0,0,255,128,1,0,228,128,10,0,0,3,1,0,7,128,
+1,0,228,128,28,0,0,160,2,0,0,3,0,0,7,128,
+0,0,228,128,1,0,228,128,5,0,0,3,0,0,8,128,
+12,0,255,160,12,0,255,160,12,0,0,3,0,0,8,128,
+0,0,255,129,0,0,255,128,4,0,0,4,1,0,7,128,
+0,0,228,144,12,0,255,161,12,0,228,160,8,0,0,3,
+1,0,8,128,1,0,228,128,1,0,228,128,12,0,0,3,
+2,0,2,128,19,0,255,160,1,0,255,128,5,0,0,3,
+0,0,8,128,0,0,255,128,2,0,85,128,4,0,0,4,
+2,0,2,128,19,0,170,160,1,0,255,128,2,0,0,128,
+7,0,0,2,1,0,8,128,1,0,255,128,5,0,0,3,
+1,0,7,128,1,0,255,128,1,0,228,128,6,0,0,2,
+1,0,8,128,2,0,85,128,4,0,0,4,0,0,8,128,
+0,0,255,128,1,0,255,129,1,0,255,128,8,0,0,3,
+1,0,8,128,1,0,228,128,26,0,228,160,8,0,0,3,
+1,0,1,128,1,0,228,144,1,0,228,128,11,0,0,3,
+1,0,9,128,1,0,228,128,28,0,85,160,5,0,0,3,
+1,0,7,128,1,0,0,128,3,0,228,144,5,0,0,3,
+1,0,7,128,1,0,228,128,5,0,228,160,2,0,0,3,
+1,0,8,128,1,0,255,128,19,0,0,161,5,0,0,3,
+1,0,8,128,1,0,255,128,19,0,85,160,11,0,0,3,
+1,0,8,128,1,0,255,128,28,0,85,160,10,0,0,3,
+1,0,8,128,1,0,255,128,28,0,0,160,5,0,0,3,
+0,0,8,128,0,0,255,128,1,0,255,128,5,0,0,3,
+1,0,7,128,0,0,255,128,1,0,228,128,10,0,0,3,
+1,0,7,128,1,0,228,128,28,0,0,160,2,0,0,3,
+0,0,7,128,0,0,228,128,1,0,228,128,5,0,0,3,
+0,0,8,128,13,0,255,160,13,0,255,160,12,0,0,3,
+0,0,8,128,0,0,255,129,0,0,255,128,4,0,0,4,
+1,0,7,128,0,0,228,144,13,0,255,161,13,0,228,160,
+8,0,0,3,1,0,8,128,1,0,228,128,1,0,228,128,
+12,0,0,3,2,0,2,128,20,0,255,160,1,0,255,128,
+5,0,0,3,0,0,8,128,0,0,255,128,2,0,85,128,
+4,0,0,4,2,0,1,128,20,0,170,160,1,0,255,128,
+2,0,0,128,7,0,0,2,1,0,8,128,1,0,255,128,
+5,0,0,3,1,0,7,128,1,0,255,128,1,0,228,128,
+6,0,0,2,1,0,8,128,2,0,0,128,4,0,0,4,
+0,0,8,128,0,0,255,128,1,0,255,129,1,0,255,128,
+8,0,0,3,1,0,8,128,1,0,228,128,27,0,228,160,
+8,0,0,3,1,0,1,128,1,0,228,144,1,0,228,128,
+11,0,0,3,1,0,9,128,1,0,228,128,28,0,85,160,
+5,0,0,3,1,0,7,128,1,0,0,128,3,0,228,144,
+5,0,0,3,1,0,7,128,1,0,228,128,6,0,228,160,
+2,0,0,3,1,0,8,128,1,0,255,128,20,0,0,161,
+5,0,0,3,1,0,8,128,1,0,255,128,20,0,85,160,
+11,0,0,3,1,0,8,128,1,0,255,128,28,0,85,160,
+10,0,0,3,1,0,8,128,1,0,255,128,28,0,0,160,
+5,0,0,3,0,0,8,128,0,0,255,128,1,0,255,128,
+5,0,0,3,1,0,7,128,0,0,255,128,1,0,228,128,
+10,0,0,3,1,0,7,128,1,0,228,128,28,0,0,160,
+2,0,0,3,0,0,7,224,0,0,228,128,1,0,228,128,
+1,0,0,2,1,0,7,224,4,0,228,144,255,255,0,0,
+83,72,68,82,24,4,0,0,64,0,240,255,6,1,0,0,
+89,8,0,4,70,142,32,0,0,0,0,0,42,0,0,0,
+95,0,0,3,114,16,16,0,0,0,0,0,95,0,0,3,
+114,16,16,0,1,0,0,0,95,0,0,3,114,16,16,0,
+3,0,0,0,95,0,0,3,114,16,16,0,4,0,0,0,
+95,0,0,3,114,16,16,0,5,0,0,0,101,0,0,3,
+114,32,16,0,0,0,0,0,101,0,0,3,114,32,16,0,
+1,0,0,0,104,0,0,2,3,0,0,0,54,0,0,5,
+114,0,16,0,0,0,0,0,70,18,16,0,5,0,0,0,
+54,0,0,5,130,0,16,0,0,0,0,0,1,64,0,0,
+0,0,0,0,48,0,0,1,33,0,0,7,18,0,16,0,
+1,0,0,0,58,0,16,0,0,0,0,0,1,64,0,0,
+7,0,0,0,3,0,4,3,10,0,16,0,1,0,0,0,
+50,0,0,16,114,0,16,0,1,0,0,0,70,18,16,128,
+65,0,0,0,0,0,0,0,246,143,32,6,0,0,0,0,
+18,0,0,0,58,0,16,0,0,0,0,0,70,130,32,6,
+0,0,0,0,18,0,0,0,58,0,16,0,0,0,0,0,
+16,0,0,7,130,0,16,0,1,0,0,0,70,2,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,50,0,0,12,
+18,0,16,0,2,0,0,0,42,128,32,6,0,0,0,0,
+26,0,0,0,58,0,16,0,0,0,0,0,58,0,16,0,
+1,0,0,0,1,64,0,0,0,0,128,63,14,0,0,10,
+18,0,16,0,2,0,0,0,2,64,0,0,0,0,128,63,
+0,0,128,63,0,0,128,63,0,0,128,63,10,0,16,0,
+2,0,0,0,57,0,0,10,34,0,16,0,2,0,0,0,
+1,64,0,0,0,0,0,0,58,128,32,6,0,0,0,0,
+18,0,0,0,58,0,16,0,0,0,0,0,49,0,0,10,
+66,0,16,0,2,0,0,0,58,128,32,6,0,0,0,0,
+26,0,0,0,58,0,16,0,0,0,0,0,58,0,16,0,
+1,0,0,0,1,0,0,7,34,0,16,0,2,0,0,0,
+42,0,16,0,2,0,0,0,26,0,16,0,2,0,0,0,
+55,0,0,9,18,0,16,0,2,0,0,0,26,0,16,0,
+2,0,0,0,1,64,0,0,0,0,0,0,10,0,16,0,
+2,0,0,0,68,0,0,5,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,56,0,0,7,114,0,16,0,
+1,0,0,0,246,15,16,0,1,0,0,0,70,2,16,0,
+1,0,0,0,16,0,0,10,130,0,16,0,1,0,0,0,
+70,2,16,0,1,0,0,0,70,130,32,6,0,0,0,0,
+34,0,0,0,58,0,16,0,0,0,0,0,52,0,0,7,
+130,0,16,0,1,0,0,0,58,0,16,0,1,0,0,0,
+1,64,0,0,0,0,0,0,0,0,0,11,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,10,128,32,134,
+65,0,0,0,0,0,0,0,26,0,0,0,58,0,16,0,
+0,0,0,0,56,32,0,10,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,26,128,32,6,0,0,0,0,
+26,0,0,0,58,0,16,0,0,0,0,0,56,0,0,7,
+130,0,16,0,1,0,0,0,58,0,16,0,1,0,0,0,
+10,0,16,0,2,0,0,0,16,0,0,7,18,0,16,0,
+1,0,0,0,70,18,16,0,1,0,0,0,70,2,16,0,
+1,0,0,0,52,0,0,7,18,0,16,0,1,0,0,0,
+10,0,16,0,1,0,0,0,1,64,0,0,0,0,0,0,
+56,0,0,7,114,0,16,0,1,0,0,0,6,0,16,0,
+1,0,0,0,70,18,16,0,3,0,0,0,56,0,0,10,
+114,0,16,0,1,0,0,0,70,2,16,0,1,0,0,0,
+70,130,32,6,0,0,0,0,10,0,0,0,58,0,16,0,
+0,0,0,0,56,0,0,7,114,0,16,0,1,0,0,0,
+246,15,16,0,1,0,0,0,70,2,16,0,1,0,0,0,
+51,0,0,10,114,0,16,0,1,0,0,0,70,2,16,0,
+1,0,0,0,2,64,0,0,0,0,128,63,0,0,128,63,
+0,0,128,63,0,0,0,0,0,0,0,7,114,0,16,0,
+0,0,0,0,70,2,16,0,0,0,0,0,70,2,16,0,
+1,0,0,0,30,0,0,7,130,0,16,0,0,0,0,0,
+58,0,16,0,0,0,0,0,1,64,0,0,1,0,0,0,
+22,0,0,1,54,0,0,5,114,32,16,0,0,0,0,0,
+70,2,16,0,0,0,0,0,54,0,0,5,114,32,16,0,
+1,0,0,0,70,18,16,0,4,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,32,0,0,0,3,0,0,0,
+0,0,0,0,7,0,0,0,20,0,0,0,2,0,0,0,
+1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,4,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+188,4,0,0,1,0,0,0,108,0,0,0,1,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,142,4,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+85,110,105,116,121,70,70,86,101,114,116,101,120,0,171,171,
+92,0,0,0,14,0,0,0,132,0,0,0,240,3,0,0,
+0,0,0,0,0,0,0,0,180,2,0,0,0,0,0,0,
+64,0,0,0,0,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+240,2,0,0,64,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,253,2,0,0,128,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+56,3,0,0,144,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,71,3,0,0,160,0,0,0,
+128,0,0,0,2,0,0,0,88,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+124,3,0,0,32,1,0,0,128,0,0,0,2,0,0,0,
+140,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,176,3,0,0,160,1,0,0,
+128,0,0,0,2,0,0,0,192,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+228,3,0,0,32,2,0,0,128,0,0,0,2,0,0,0,
+244,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,24,4,0,0,160,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+39,4,0,0,176,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,54,4,0,0,192,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+66,4,0,0,208,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,82,4,0,0,224,2,0,0,
+0,1,0,0,0,0,0,0,96,4,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+132,4,0,0,224,3,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,102,102,95,109,97,116,114,105,
+120,95,109,118,112,0,102,108,111,97,116,52,120,52,0,171,
+3,0,3,0,4,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+194,2,0,0,102,102,95,109,97,116,114,105,120,95,109,118,
+0,102,102,95,118,101,99,95,99,111,108,111,114,0,102,108,
+111,97,116,52,0,171,171,171,1,0,3,0,1,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,118,
+101,99,95,97,109,98,105,101,110,116,0,102,102,95,108,105,
+103,104,116,95,99,111,108,111,114,0,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,112,111,115,0,171,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,97,116,116,
+101,110,0,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,115,112,111,116,0,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,109,
+97,116,95,100,105,102,102,117,115,101,0,102,102,95,109,97,
+116,95,97,109,98,105,101,110,116,0,102,102,95,109,97,116,
+95,115,112,101,99,0,102,102,95,109,97,116,95,101,109,105,
+115,115,105,111,110,0,102,102,95,109,97,116,114,105,120,95,
+116,101,120,0,3,0,3,0,4,0,4,0,4,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,194,2,0,0,102,102,95,102,111,103,95,118,
+115,0,77,105,99,114,111,115,111,102,116,32,40,82,41,32,
+72,76,83,76,32,83,104,97,100,101,114,32,67,111,109,112,
+105,108,101,114,32,54,46,51,46,57,52,49,53,46,48,0,
+76,70,83,48,164,1,0,0,7,0,0,0,8,0,0,0,
+88,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,2,0,0,0,
+255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,
+106,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+118,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,
+1,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+128,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,
+2,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+136,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+149,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,3,0,0,0,
+4,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+159,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,
+5,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+67,111,109,112,117,116,101,83,112,111,116,76,105,103,104,116,
+55,0,101,121,101,80,111,115,105,116,105,111,110,0,101,121,
+101,78,111,114,109,97,108,0,118,105,101,119,68,105,114,0,
+100,105,102,102,117,115,101,67,111,108,111,114,0,115,112,101,
+99,67,111,108,111,114,0,97,109,98,0,171,76,73,66,70,
+80,16,0,0,68,88,66,67,54,132,209,166,254,167,118,10,
+196,111,133,126,129,212,65,104,1,0,0,0,80,16,0,0,
+5,0,0,0,52,0,0,0,156,3,0,0,96,9,0,0,
+220,9,0,0,160,14,0,0,65,111,110,57,96,3,0,0,
+96,3,0,0,0,2,86,76,48,3,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,0,0,0,0,45,0,0,0,0,0,0,0,
+0,2,86,76,81,0,0,5,45,0,15,160,0,0,0,0,
+0,0,0,0,0,0,128,63,0,0,0,0,48,0,0,5,
+0,0,15,240,7,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,31,0,0,2,5,0,0,128,0,0,15,144,
+31,0,0,2,5,0,1,128,1,0,15,144,31,0,0,2,
+5,0,2,128,2,0,15,144,31,0,0,2,5,0,3,128,
+3,0,15,144,31,0,0,2,5,0,4,128,4,0,15,144,
+31,0,0,2,5,0,5,128,5,0,15,144,1,0,0,2,
+0,0,7,128,4,0,228,144,1,0,0,2,1,0,7,128,
+5,0,228,144,27,0,0,2,0,8,228,240,0,0,228,240,
+4,0,0,6,2,0,7,128,0,0,228,144,18,32,255,161,
+0,8,228,240,18,32,228,160,0,8,228,240,8,0,0,3,
+0,0,8,128,2,0,228,128,2,0,228,128,7,0,0,2,
+1,0,8,128,0,0,255,128,4,0,0,4,3,0,7,128,
+2,0,228,128,1,0,255,128,2,0,228,144,36,0,0,2,
+4,0,7,128,3,0,228,128,8,0,0,3,2,0,8,128,
+1,0,228,144,4,0,228,128,5,0,0,5,3,0,1,128,
+18,32,255,160,0,8,228,240,18,32,255,160,0,8,228,240,
+12,0,0,3,3,0,1,128,3,0,0,129,3,0,0,128,
+12,0,0,4,3,0,2,128,26,32,255,160,0,8,228,240,
+0,0,255,128,5,0,0,3,3,0,1,128,3,0,85,128,
+3,0,0,128,5,0,0,4,0,0,8,128,0,0,255,128,
+26,32,170,160,0,8,228,240,2,0,0,3,0,0,8,128,
+0,0,255,128,45,0,170,160,6,0,0,2,0,0,8,128,
+0,0,255,128,4,0,0,4,0,0,8,128,3,0,0,128,
+0,0,255,129,0,0,255,128,11,0,0,3,2,0,8,128,
+2,0,255,128,45,0,85,160,32,0,0,3,3,0,1,128,
+2,0,255,128,44,0,255,160,10,0,0,3,2,0,8,128,
+3,0,0,128,45,0,170,160,5,0,0,3,2,0,7,128,
+1,0,255,128,2,0,228,128,8,0,0,3,1,0,8,128,
+1,0,228,144,2,0,228,128,8,0,0,4,2,0,1,128,
+2,0,228,128,34,32,228,160,0,8,228,240,11,0,0,3,
+2,0,1,128,2,0,0,128,45,0,85,160,2,0,0,4,
+2,0,1,128,2,0,0,128,26,32,0,161,0,8,228,240,
+5,0,0,4,2,0,1,128,2,0,0,128,26,32,85,160,
+0,8,228,240,11,0,0,3,2,0,1,128,2,0,0,128,
+45,0,85,160,10,0,0,3,2,0,1,128,2,0,0,128,
+45,0,170,160,5,0,0,3,0,0,8,128,0,0,255,128,
+2,0,0,128,11,0,0,3,1,0,8,128,1,0,255,128,
+45,0,85,160,12,0,0,3,2,0,1,128,45,0,85,160,
+1,0,255,128,5,0,0,3,2,0,2,128,2,0,255,128,
+0,0,255,128,5,0,0,4,2,0,14,128,2,0,85,128,
+10,32,144,160,0,8,228,240,4,0,0,4,0,0,7,128,
+2,0,0,128,2,0,249,128,0,0,228,128,5,0,0,3,
+2,0,7,128,1,0,255,128,3,0,228,144,5,0,0,4,
+2,0,7,128,2,0,228,128,10,32,228,160,0,8,228,240,
+5,0,0,3,2,0,7,128,0,0,255,128,2,0,228,128,
+10,0,0,3,2,0,7,128,2,0,228,128,45,0,170,160,
+2,0,0,3,1,0,7,128,1,0,228,128,2,0,228,128,
+29,0,0,0,1,0,0,2,1,0,7,224,0,0,228,128,
+1,0,0,2,0,0,7,224,1,0,228,128,255,255,0,0,
+83,72,68,82,188,5,0,0,64,0,240,255,111,1,0,0,
+89,8,0,4,70,142,32,0,0,0,0,0,45,0,0,0,
+95,0,0,3,114,16,16,0,0,0,0,0,95,0,0,3,
+114,16,16,0,1,0,0,0,95,0,0,3,114,16,16,0,
+2,0,0,0,95,0,0,3,114,16,16,0,3,0,0,0,
+95,0,0,3,114,16,16,0,4,0,0,0,95,0,0,3,
+114,16,16,0,5,0,0,0,101,0,0,3,114,32,16,0,
+0,0,0,0,101,0,0,3,114,32,16,0,1,0,0,0,
+104,0,0,2,4,0,0,0,54,0,0,5,114,0,16,0,
+0,0,0,0,70,18,16,0,4,0,0,0,54,0,0,5,
+114,0,16,0,1,0,0,0,70,18,16,0,5,0,0,0,
+54,0,0,5,130,0,16,0,0,0,0,0,1,64,0,0,
+0,0,0,0,48,0,0,1,33,0,0,7,130,0,16,0,
+1,0,0,0,58,0,16,0,0,0,0,0,1,64,0,0,
+7,0,0,0,3,0,4,3,58,0,16,0,1,0,0,0,
+50,0,0,16,114,0,16,0,2,0,0,0,70,18,16,128,
+65,0,0,0,0,0,0,0,246,143,32,6,0,0,0,0,
+18,0,0,0,58,0,16,0,0,0,0,0,70,130,32,6,
+0,0,0,0,18,0,0,0,58,0,16,0,0,0,0,0,
+16,0,0,7,130,0,16,0,1,0,0,0,70,2,16,0,
+2,0,0,0,70,2,16,0,2,0,0,0,50,0,0,12,
+130,0,16,0,2,0,0,0,42,128,32,6,0,0,0,0,
+26,0,0,0,58,0,16,0,0,0,0,0,58,0,16,0,
+1,0,0,0,1,64,0,0,0,0,128,63,14,0,0,10,
+130,0,16,0,2,0,0,0,2,64,0,0,0,0,128,63,
+0,0,128,63,0,0,128,63,0,0,128,63,58,0,16,0,
+2,0,0,0,57,0,0,10,18,0,16,0,3,0,0,0,
+1,64,0,0,0,0,0,0,58,128,32,6,0,0,0,0,
+18,0,0,0,58,0,16,0,0,0,0,0,49,0,0,10,
+34,0,16,0,3,0,0,0,58,128,32,6,0,0,0,0,
+26,0,0,0,58,0,16,0,0,0,0,0,58,0,16,0,
+1,0,0,0,1,0,0,7,18,0,16,0,3,0,0,0,
+26,0,16,0,3,0,0,0,10,0,16,0,3,0,0,0,
+55,0,0,9,130,0,16,0,2,0,0,0,10,0,16,0,
+3,0,0,0,1,64,0,0,0,0,0,0,58,0,16,0,
+2,0,0,0,68,0,0,5,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,56,0,0,7,114,0,16,0,
+3,0,0,0,246,15,16,0,1,0,0,0,70,2,16,0,
+2,0,0,0,16,0,0,10,130,0,16,0,3,0,0,0,
+70,2,16,0,3,0,0,0,70,130,32,6,0,0,0,0,
+34,0,0,0,58,0,16,0,0,0,0,0,52,0,0,7,
+130,0,16,0,3,0,0,0,58,0,16,0,3,0,0,0,
+1,64,0,0,0,0,0,0,0,0,0,11,130,0,16,0,
+3,0,0,0,58,0,16,0,3,0,0,0,10,128,32,134,
+65,0,0,0,0,0,0,0,26,0,0,0,58,0,16,0,
+0,0,0,0,56,32,0,10,130,0,16,0,3,0,0,0,
+58,0,16,0,3,0,0,0,26,128,32,6,0,0,0,0,
+26,0,0,0,58,0,16,0,0,0,0,0,56,0,0,7,
+130,0,16,0,2,0,0,0,58,0,16,0,2,0,0,0,
+58,0,16,0,3,0,0,0,16,0,0,7,18,0,16,0,
+3,0,0,0,70,18,16,0,1,0,0,0,70,2,16,0,
+3,0,0,0,52,0,0,7,18,0,16,0,3,0,0,0,
+10,0,16,0,3,0,0,0,1,64,0,0,0,0,0,0,
+56,0,0,7,226,0,16,0,3,0,0,0,6,0,16,0,
+3,0,0,0,6,25,16,0,3,0,0,0,56,0,0,10,
+226,0,16,0,3,0,0,0,86,14,16,0,3,0,0,0,
+6,137,32,6,0,0,0,0,10,0,0,0,58,0,16,0,
+0,0,0,0,49,0,0,7,18,0,16,0,3,0,0,0,
+1,64,0,0,0,0,0,0,10,0,16,0,3,0,0,0,
+31,0,4,3,10,0,16,0,3,0,0,0,50,0,0,9,
+114,0,16,0,2,0,0,0,70,2,16,0,2,0,0,0,
+246,15,16,0,1,0,0,0,70,18,16,0,2,0,0,0,
+16,0,0,7,130,0,16,0,1,0,0,0,70,2,16,0,
+2,0,0,0,70,2,16,0,2,0,0,0,68,0,0,5,
+130,0,16,0,1,0,0,0,58,0,16,0,1,0,0,0,
+56,0,0,7,114,0,16,0,2,0,0,0,246,15,16,0,
+1,0,0,0,70,2,16,0,2,0,0,0,16,0,0,7,
+130,0,16,0,1,0,0,0,70,18,16,0,1,0,0,0,
+70,2,16,0,2,0,0,0,52,0,0,7,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,1,64,0,0,
+0,0,0,0,47,0,0,5,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,56,0,0,8,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,58,128,32,0,
+0,0,0,0,44,0,0,0,25,0,0,5,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,51,0,0,7,
+130,0,16,0,1,0,0,0,58,0,16,0,1,0,0,0,
+1,64,0,0,0,0,128,63,56,0,0,7,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,58,0,16,0,
+2,0,0,0,50,0,0,12,114,0,16,0,0,0,0,0,
+246,15,16,0,1,0,0,0,70,130,32,6,0,0,0,0,
+10,0,0,0,58,0,16,0,0,0,0,0,70,2,16,0,
+0,0,0,0,21,0,0,1,56,0,0,7,114,0,16,0,
+2,0,0,0,246,15,16,0,2,0,0,0,150,7,16,0,
+3,0,0,0,51,0,0,10,114,0,16,0,2,0,0,0,
+70,2,16,0,2,0,0,0,2,64,0,0,0,0,128,63,
+0,0,128,63,0,0,128,63,0,0,0,0,0,0,0,7,
+114,0,16,0,1,0,0,0,70,2,16,0,1,0,0,0,
+70,2,16,0,2,0,0,0,30,0,0,7,130,0,16,0,
+0,0,0,0,58,0,16,0,0,0,0,0,1,64,0,0,
+1,0,0,0,22,0,0,1,54,0,0,5,114,32,16,0,
+1,0,0,0,70,2,16,0,0,0,0,0,54,0,0,5,
+114,32,16,0,0,0,0,0,70,2,16,0,1,0,0,0,
+62,0,0,1,83,84,65,84,116,0,0,0,48,0,0,0,
+4,0,0,0,0,0,0,0,8,0,0,0,33,0,0,0,
+2,0,0,0,1,0,0,0,1,0,0,0,2,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,5,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,188,4,0,0,1,0,0,0,108,0,0,0,
+1,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+142,4,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,92,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,85,110,105,116,121,70,70,86,101,114,116,101,
+120,0,171,171,92,0,0,0,14,0,0,0,132,0,0,0,
+240,3,0,0,0,0,0,0,0,0,0,0,180,2,0,0,
+0,0,0,0,64,0,0,0,0,0,0,0,204,2,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,240,2,0,0,64,0,0,0,64,0,0,0,
+0,0,0,0,204,2,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,253,2,0,0,
+128,0,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,56,3,0,0,144,0,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,71,3,0,0,
+160,0,0,0,128,0,0,0,2,0,0,0,88,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,124,3,0,0,32,1,0,0,128,0,0,0,
+2,0,0,0,140,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,176,3,0,0,
+160,1,0,0,128,0,0,0,2,0,0,0,192,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,228,3,0,0,32,2,0,0,128,0,0,0,
+2,0,0,0,244,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,24,4,0,0,
+160,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,39,4,0,0,176,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,54,4,0,0,
+192,2,0,0,16,0,0,0,2,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,66,4,0,0,208,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,82,4,0,0,
+224,2,0,0,0,1,0,0,0,0,0,0,96,4,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,132,4,0,0,224,3,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,102,102,95,109,
+97,116,114,105,120,95,109,118,112,0,102,108,111,97,116,52,
+120,52,0,171,3,0,3,0,4,0,4,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,194,2,0,0,102,102,95,109,97,116,114,105,
+120,95,109,118,0,102,102,95,118,101,99,95,99,111,108,111,
+114,0,102,108,111,97,116,52,0,171,171,171,1,0,3,0,
+1,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,118,101,99,95,97,109,98,105,101,110,116,0,102,
+102,95,108,105,103,104,116,95,99,111,108,111,114,0,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,112,111,115,
+0,171,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,97,116,116,101,110,0,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,115,112,111,116,0,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,109,97,116,95,100,105,102,102,117,115,101,0,102,
+102,95,109,97,116,95,97,109,98,105,101,110,116,0,102,102,
+95,109,97,116,95,115,112,101,99,0,102,102,95,109,97,116,
+95,101,109,105,115,115,105,111,110,0,102,102,95,109,97,116,
+114,105,120,95,116,101,120,0,3,0,3,0,4,0,4,0,
+4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,194,2,0,0,102,102,95,102,
+111,103,95,118,115,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,168,1,0,0,7,0,0,0,
+8,0,0,0,88,1,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,110,1,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,122,1,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,1,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,132,1,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,2,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,140,1,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,153,1,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+3,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,163,1,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,5,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,67,111,109,112,117,116,101,83,112,111,116,76,
+105,103,104,116,83,112,101,99,55,0,101,121,101,80,111,115,
+105,116,105,111,110,0,101,121,101,78,111,114,109,97,108,0,
+118,105,101,119,68,105,114,0,100,105,102,102,117,115,101,67,
+111,108,111,114,0,115,112,101,99,67,111,108,111,114,0,97,
+109,98,0,171,76,73,66,70,28,24,0,0,68,88,66,67,
+86,133,213,195,182,2,158,132,118,12,245,138,38,54,53,58,
+1,0,0,0,28,24,0,0,5,0,0,0,52,0,0,0,
+16,13,0,0,48,17,0,0,172,17,0,0,112,22,0,0,
+65,111,110,57,212,12,0,0,212,12,0,0,0,2,86,76,
+164,12,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,0,0,10,0,
+32,0,0,0,0,0,0,0,0,2,86,76,81,0,0,5,
+32,0,15,160,0,0,128,63,0,0,0,0,0,0,0,0,
+0,0,0,0,31,0,0,2,5,0,0,128,0,0,15,144,
+31,0,0,2,5,0,1,128,1,0,15,144,31,0,0,2,
+5,0,3,128,3,0,15,144,31,0,0,2,5,0,4,128,
+4,0,15,144,31,0,0,2,5,0,5,128,5,0,15,144,
+5,0,0,3,0,0,1,128,8,0,255,160,8,0,255,160,
+12,0,0,3,0,0,1,128,0,0,0,129,0,0,0,128,
+4,0,0,4,0,0,14,128,0,0,144,144,8,0,255,161,
+8,0,144,160,8,0,0,3,1,0,1,128,0,0,249,128,
+0,0,249,128,12,0,0,3,1,0,2,128,16,0,255,160,
+1,0,0,128,5,0,0,3,0,0,1,128,0,0,0,128,
+1,0,85,128,1,0,0,2,2,0,1,128,32,0,0,160,
+4,0,0,4,1,0,2,128,16,0,170,160,1,0,0,128,
+2,0,0,128,7,0,0,2,1,0,1,128,1,0,0,128,
+5,0,0,3,0,0,14,128,0,0,228,128,1,0,0,128,
+6,0,0,2,1,0,1,128,1,0,85,128,4,0,0,4,
+0,0,1,128,0,0,0,128,1,0,0,129,1,0,0,128,
+8,0,0,3,1,0,1,128,0,0,249,128,24,0,228,160,
+8,0,0,3,0,0,2,128,1,0,228,144,0,0,249,128,
+11,0,0,3,0,0,2,128,0,0,85,128,32,0,85,160,
+5,0,0,3,0,0,14,128,0,0,85,128,3,0,144,144,
+5,0,0,3,0,0,14,128,0,0,228,128,0,0,144,160,
+11,0,0,3,1,0,1,128,1,0,0,128,32,0,85,160,
+2,0,0,3,1,0,1,128,1,0,0,128,16,0,0,161,
+5,0,0,3,1,0,1,128,1,0,0,128,16,0,85,160,
+11,0,0,3,1,0,1,128,1,0,0,128,32,0,85,160,
+10,0,0,3,1,0,1,128,1,0,0,128,32,0,0,160,
+5,0,0,3,0,0,1,128,0,0,0,128,1,0,0,128,
+5,0,0,3,0,0,7,128,0,0,0,128,0,0,249,128,
+10,0,0,3,0,0,7,128,0,0,228,128,32,0,0,160,
+2,0,0,3,0,0,7,128,0,0,228,128,5,0,228,144,
+5,0,0,3,0,0,8,128,9,0,255,160,9,0,255,160,
+12,0,0,3,0,0,8,128,0,0,255,129,0,0,255,128,
+4,0,0,4,1,0,7,128,0,0,228,144,9,0,255,161,
+9,0,228,160,8,0,0,3,1,0,8,128,1,0,228,128,
+1,0,228,128,12,0,0,3,2,0,2,128,17,0,255,160,
+1,0,255,128,5,0,0,3,0,0,8,128,0,0,255,128,
+2,0,85,128,4,0,0,4,2,0,2,128,17,0,170,160,
+1,0,255,128,2,0,0,128,7,0,0,2,1,0,8,128,
+1,0,255,128,5,0,0,3,1,0,7,128,1,0,255,128,
+1,0,228,128,6,0,0,2,1,0,8,128,2,0,85,128,
+4,0,0,4,0,0,8,128,0,0,255,128,1,0,255,129,
+1,0,255,128,8,0,0,3,1,0,8,128,1,0,228,128,
+25,0,228,160,8,0,0,3,1,0,1,128,1,0,228,144,
+1,0,228,128,11,0,0,3,1,0,9,128,1,0,228,128,
+32,0,85,160,5,0,0,3,1,0,7,128,1,0,0,128,
+3,0,228,144,5,0,0,3,1,0,7,128,1,0,228,128,
+1,0,228,160,2,0,0,3,1,0,8,128,1,0,255,128,
+17,0,0,161,5,0,0,3,1,0,8,128,1,0,255,128,
+17,0,85,160,11,0,0,3,1,0,8,128,1,0,255,128,
+32,0,85,160,10,0,0,3,1,0,8,128,1,0,255,128,
+32,0,0,160,5,0,0,3,0,0,8,128,0,0,255,128,
+1,0,255,128,5,0,0,3,1,0,7,128,0,0,255,128,
+1,0,228,128,10,0,0,3,1,0,7,128,1,0,228,128,
+32,0,0,160,2,0,0,3,0,0,7,128,0,0,228,128,
+1,0,228,128,5,0,0,3,0,0,8,128,10,0,255,160,
+10,0,255,160,12,0,0,3,0,0,8,128,0,0,255,129,
+0,0,255,128,4,0,0,4,1,0,7,128,0,0,228,144,
+10,0,255,161,10,0,228,160,8,0,0,3,1,0,8,128,
+1,0,228,128,1,0,228,128,12,0,0,3,2,0,2,128,
+18,0,255,160,1,0,255,128,5,0,0,3,0,0,8,128,
+0,0,255,128,2,0,85,128,4,0,0,4,2,0,2,128,
+18,0,170,160,1,0,255,128,2,0,0,128,7,0,0,2,
+1,0,8,128,1,0,255,128,5,0,0,3,1,0,7,128,
+1,0,255,128,1,0,228,128,6,0,0,2,1,0,8,128,
+2,0,85,128,4,0,0,4,0,0,8,128,0,0,255,128,
+1,0,255,129,1,0,255,128,8,0,0,3,1,0,8,128,
+1,0,228,128,26,0,228,160,8,0,0,3,1,0,1,128,
+1,0,228,144,1,0,228,128,11,0,0,3,1,0,9,128,
+1,0,228,128,32,0,85,160,5,0,0,3,1,0,7,128,
+1,0,0,128,3,0,228,144,5,0,0,3,1,0,7,128,
+1,0,228,128,2,0,228,160,2,0,0,3,1,0,8,128,
+1,0,255,128,18,0,0,161,5,0,0,3,1,0,8,128,
+1,0,255,128,18,0,85,160,11,0,0,3,1,0,8,128,
+1,0,255,128,32,0,85,160,10,0,0,3,1,0,8,128,
+1,0,255,128,32,0,0,160,5,0,0,3,0,0,8,128,
+0,0,255,128,1,0,255,128,5,0,0,3,1,0,7,128,
+0,0,255,128,1,0,228,128,10,0,0,3,1,0,7,128,
+1,0,228,128,32,0,0,160,2,0,0,3,0,0,7,128,
+0,0,228,128,1,0,228,128,5,0,0,3,0,0,8,128,
+11,0,255,160,11,0,255,160,12,0,0,3,0,0,8,128,
+0,0,255,129,0,0,255,128,4,0,0,4,1,0,7,128,
+0,0,228,144,11,0,255,161,11,0,228,160,8,0,0,3,
+1,0,8,128,1,0,228,128,1,0,228,128,12,0,0,3,
+2,0,2,128,19,0,255,160,1,0,255,128,5,0,0,3,
+0,0,8,128,0,0,255,128,2,0,85,128,4,0,0,4,
+2,0,2,128,19,0,170,160,1,0,255,128,2,0,0,128,
+7,0,0,2,1,0,8,128,1,0,255,128,5,0,0,3,
+1,0,7,128,1,0,255,128,1,0,228,128,6,0,0,2,
+1,0,8,128,2,0,85,128,4,0,0,4,0,0,8,128,
+0,0,255,128,1,0,255,129,1,0,255,128,8,0,0,3,
+1,0,8,128,1,0,228,128,27,0,228,160,8,0,0,3,
+1,0,1,128,1,0,228,144,1,0,228,128,11,0,0,3,
+1,0,9,128,1,0,228,128,32,0,85,160,5,0,0,3,
+1,0,7,128,1,0,0,128,3,0,228,144,5,0,0,3,
+1,0,7,128,1,0,228,128,3,0,228,160,2,0,0,3,
+1,0,8,128,1,0,255,128,19,0,0,161,5,0,0,3,
+1,0,8,128,1,0,255,128,19,0,85,160,11,0,0,3,
+1,0,8,128,1,0,255,128,32,0,85,160,10,0,0,3,
+1,0,8,128,1,0,255,128,32,0,0,160,5,0,0,3,
+0,0,8,128,0,0,255,128,1,0,255,128,5,0,0,3,
+1,0,7,128,0,0,255,128,1,0,228,128,10,0,0,3,
+1,0,7,128,1,0,228,128,32,0,0,160,2,0,0,3,
+0,0,7,128,0,0,228,128,1,0,228,128,5,0,0,3,
+0,0,8,128,12,0,255,160,12,0,255,160,12,0,0,3,
+0,0,8,128,0,0,255,129,0,0,255,128,4,0,0,4,
+1,0,7,128,0,0,228,144,12,0,255,161,12,0,228,160,
+8,0,0,3,1,0,8,128,1,0,228,128,1,0,228,128,
+12,0,0,3,2,0,2,128,20,0,255,160,1,0,255,128,
+5,0,0,3,0,0,8,128,0,0,255,128,2,0,85,128,
+4,0,0,4,2,0,2,128,20,0,170,160,1,0,255,128,
+2,0,0,128,7,0,0,2,1,0,8,128,1,0,255,128,
+5,0,0,3,1,0,7,128,1,0,255,128,1,0,228,128,
+6,0,0,2,1,0,8,128,2,0,85,128,4,0,0,4,
+0,0,8,128,0,0,255,128,1,0,255,129,1,0,255,128,
+8,0,0,3,1,0,8,128,1,0,228,128,28,0,228,160,
+8,0,0,3,1,0,1,128,1,0,228,144,1,0,228,128,
+11,0,0,3,1,0,9,128,1,0,228,128,32,0,85,160,
+5,0,0,3,1,0,7,128,1,0,0,128,3,0,228,144,
+5,0,0,3,1,0,7,128,1,0,228,128,4,0,228,160,
+2,0,0,3,1,0,8,128,1,0,255,128,20,0,0,161,
+5,0,0,3,1,0,8,128,1,0,255,128,20,0,85,160,
+11,0,0,3,1,0,8,128,1,0,255,128,32,0,85,160,
+10,0,0,3,1,0,8,128,1,0,255,128,32,0,0,160,
+5,0,0,3,0,0,8,128,0,0,255,128,1,0,255,128,
+5,0,0,3,1,0,7,128,0,0,255,128,1,0,228,128,
+10,0,0,3,1,0,7,128,1,0,228,128,32,0,0,160,
+2,0,0,3,0,0,7,128,0,0,228,128,1,0,228,128,
+5,0,0,3,0,0,8,128,13,0,255,160,13,0,255,160,
+12,0,0,3,0,0,8,128,0,0,255,129,0,0,255,128,
+4,0,0,4,1,0,7,128,0,0,228,144,13,0,255,161,
+13,0,228,160,8,0,0,3,1,0,8,128,1,0,228,128,
+1,0,228,128,12,0,0,3,2,0,2,128,21,0,255,160,
+1,0,255,128,5,0,0,3,0,0,8,128,0,0,255,128,
+2,0,85,128,4,0,0,4,2,0,2,128,21,0,170,160,
+1,0,255,128,2,0,0,128,7,0,0,2,1,0,8,128,
+1,0,255,128,5,0,0,3,1,0,7,128,1,0,255,128,
+1,0,228,128,6,0,0,2,1,0,8,128,2,0,85,128,
+4,0,0,4,0,0,8,128,0,0,255,128,1,0,255,129,
+1,0,255,128,8,0,0,3,1,0,8,128,1,0,228,128,
+29,0,228,160,8,0,0,3,1,0,1,128,1,0,228,144,
+1,0,228,128,11,0,0,3,1,0,9,128,1,0,228,128,
+32,0,85,160,5,0,0,3,1,0,7,128,1,0,0,128,
+3,0,228,144,5,0,0,3,1,0,7,128,1,0,228,128,
+5,0,228,160,2,0,0,3,1,0,8,128,1,0,255,128,
+21,0,0,161,5,0,0,3,1,0,8,128,1,0,255,128,
+21,0,85,160,11,0,0,3,1,0,8,128,1,0,255,128,
+32,0,85,160,10,0,0,3,1,0,8,128,1,0,255,128,
+32,0,0,160,5,0,0,3,0,0,8,128,0,0,255,128,
+1,0,255,128,5,0,0,3,1,0,7,128,0,0,255,128,
+1,0,228,128,10,0,0,3,1,0,7,128,1,0,228,128,
+32,0,0,160,2,0,0,3,0,0,7,128,0,0,228,128,
+1,0,228,128,5,0,0,3,0,0,8,128,14,0,255,160,
+14,0,255,160,12,0,0,3,0,0,8,128,0,0,255,129,
+0,0,255,128,4,0,0,4,1,0,7,128,0,0,228,144,
+14,0,255,161,14,0,228,160,8,0,0,3,1,0,8,128,
+1,0,228,128,1,0,228,128,12,0,0,3,2,0,2,128,
+22,0,255,160,1,0,255,128,5,0,0,3,0,0,8,128,
+0,0,255,128,2,0,85,128,4,0,0,4,2,0,2,128,
+22,0,170,160,1,0,255,128,2,0,0,128,7,0,0,2,
+1,0,8,128,1,0,255,128,5,0,0,3,1,0,7,128,
+1,0,255,128,1,0,228,128,6,0,0,2,1,0,8,128,
+2,0,85,128,4,0,0,4,0,0,8,128,0,0,255,128,
+1,0,255,129,1,0,255,128,8,0,0,3,1,0,8,128,
+1,0,228,128,30,0,228,160,8,0,0,3,1,0,1,128,
+1,0,228,144,1,0,228,128,11,0,0,3,1,0,9,128,
+1,0,228,128,32,0,85,160,5,0,0,3,1,0,7,128,
+1,0,0,128,3,0,228,144,5,0,0,3,1,0,7,128,
+1,0,228,128,6,0,228,160,2,0,0,3,1,0,8,128,
+1,0,255,128,22,0,0,161,5,0,0,3,1,0,8,128,
+1,0,255,128,22,0,85,160,11,0,0,3,1,0,8,128,
+1,0,255,128,32,0,85,160,10,0,0,3,1,0,8,128,
+1,0,255,128,32,0,0,160,5,0,0,3,0,0,8,128,
+0,0,255,128,1,0,255,128,5,0,0,3,1,0,7,128,
+0,0,255,128,1,0,228,128,10,0,0,3,1,0,7,128,
+1,0,228,128,32,0,0,160,2,0,0,3,0,0,7,128,
+0,0,228,128,1,0,228,128,5,0,0,3,0,0,8,128,
+15,0,255,160,15,0,255,160,12,0,0,3,0,0,8,128,
+0,0,255,129,0,0,255,128,4,0,0,4,1,0,7,128,
+0,0,228,144,15,0,255,161,15,0,228,160,8,0,0,3,
+1,0,8,128,1,0,228,128,1,0,228,128,12,0,0,3,
+2,0,2,128,23,0,255,160,1,0,255,128,5,0,0,3,
+0,0,8,128,0,0,255,128,2,0,85,128,4,0,0,4,
+2,0,1,128,23,0,170,160,1,0,255,128,2,0,0,128,
+7,0,0,2,1,0,8,128,1,0,255,128,5,0,0,3,
+1,0,7,128,1,0,255,128,1,0,228,128,6,0,0,2,
+1,0,8,128,2,0,0,128,4,0,0,4,0,0,8,128,
+0,0,255,128,1,0,255,129,1,0,255,128,8,0,0,3,
+1,0,8,128,1,0,228,128,31,0,228,160,8,0,0,3,
+1,0,1,128,1,0,228,144,1,0,228,128,11,0,0,3,
+1,0,9,128,1,0,228,128,32,0,85,160,5,0,0,3,
+1,0,7,128,1,0,0,128,3,0,228,144,5,0,0,3,
+1,0,7,128,1,0,228,128,7,0,228,160,2,0,0,3,
+1,0,8,128,1,0,255,128,23,0,0,161,5,0,0,3,
+1,0,8,128,1,0,255,128,23,0,85,160,11,0,0,3,
+1,0,8,128,1,0,255,128,32,0,85,160,10,0,0,3,
+1,0,8,128,1,0,255,128,32,0,0,160,5,0,0,3,
+0,0,8,128,0,0,255,128,1,0,255,128,5,0,0,3,
+1,0,7,128,0,0,255,128,1,0,228,128,10,0,0,3,
+1,0,7,128,1,0,228,128,32,0,0,160,2,0,0,3,
+0,0,7,224,0,0,228,128,1,0,228,128,1,0,0,2,
+1,0,7,224,4,0,228,144,255,255,0,0,83,72,68,82,
+24,4,0,0,64,0,240,255,6,1,0,0,89,8,0,4,
+70,142,32,0,0,0,0,0,42,0,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,95,0,0,3,114,16,16,0,
+1,0,0,0,95,0,0,3,114,16,16,0,3,0,0,0,
+95,0,0,3,114,16,16,0,4,0,0,0,95,0,0,3,
+114,16,16,0,5,0,0,0,101,0,0,3,114,32,16,0,
+0,0,0,0,101,0,0,3,114,32,16,0,1,0,0,0,
+104,0,0,2,3,0,0,0,54,0,0,5,114,0,16,0,
+0,0,0,0,70,18,16,0,5,0,0,0,54,0,0,5,
+130,0,16,0,0,0,0,0,1,64,0,0,0,0,0,0,
+48,0,0,1,33,0,0,7,18,0,16,0,1,0,0,0,
+58,0,16,0,0,0,0,0,1,64,0,0,8,0,0,0,
+3,0,4,3,10,0,16,0,1,0,0,0,50,0,0,16,
+114,0,16,0,1,0,0,0,70,18,16,128,65,0,0,0,
+0,0,0,0,246,143,32,6,0,0,0,0,18,0,0,0,
+58,0,16,0,0,0,0,0,70,130,32,6,0,0,0,0,
+18,0,0,0,58,0,16,0,0,0,0,0,16,0,0,7,
+130,0,16,0,1,0,0,0,70,2,16,0,1,0,0,0,
+70,2,16,0,1,0,0,0,50,0,0,12,18,0,16,0,
+2,0,0,0,42,128,32,6,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,58,0,16,0,1,0,0,0,
+1,64,0,0,0,0,128,63,14,0,0,10,18,0,16,0,
+2,0,0,0,2,64,0,0,0,0,128,63,0,0,128,63,
+0,0,128,63,0,0,128,63,10,0,16,0,2,0,0,0,
+57,0,0,10,34,0,16,0,2,0,0,0,1,64,0,0,
+0,0,0,0,58,128,32,6,0,0,0,0,18,0,0,0,
+58,0,16,0,0,0,0,0,49,0,0,10,66,0,16,0,
+2,0,0,0,58,128,32,6,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,58,0,16,0,1,0,0,0,
+1,0,0,7,34,0,16,0,2,0,0,0,42,0,16,0,
+2,0,0,0,26,0,16,0,2,0,0,0,55,0,0,9,
+18,0,16,0,2,0,0,0,26,0,16,0,2,0,0,0,
+1,64,0,0,0,0,0,0,10,0,16,0,2,0,0,0,
+68,0,0,5,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,56,0,0,7,114,0,16,0,1,0,0,0,
+246,15,16,0,1,0,0,0,70,2,16,0,1,0,0,0,
+16,0,0,10,130,0,16,0,1,0,0,0,70,2,16,0,
+1,0,0,0,70,130,32,6,0,0,0,0,34,0,0,0,
+58,0,16,0,0,0,0,0,52,0,0,7,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,1,64,0,0,
+0,0,0,0,0,0,0,11,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,10,128,32,134,65,0,0,0,
+0,0,0,0,26,0,0,0,58,0,16,0,0,0,0,0,
+56,32,0,10,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,26,128,32,6,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,56,0,0,7,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,10,0,16,0,
+2,0,0,0,16,0,0,7,18,0,16,0,1,0,0,0,
+70,18,16,0,1,0,0,0,70,2,16,0,1,0,0,0,
+52,0,0,7,18,0,16,0,1,0,0,0,10,0,16,0,
+1,0,0,0,1,64,0,0,0,0,0,0,56,0,0,7,
+114,0,16,0,1,0,0,0,6,0,16,0,1,0,0,0,
+70,18,16,0,3,0,0,0,56,0,0,10,114,0,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,70,130,32,6,
+0,0,0,0,10,0,0,0,58,0,16,0,0,0,0,0,
+56,0,0,7,114,0,16,0,1,0,0,0,246,15,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,51,0,0,10,
+114,0,16,0,1,0,0,0,70,2,16,0,1,0,0,0,
+2,64,0,0,0,0,128,63,0,0,128,63,0,0,128,63,
+0,0,0,0,0,0,0,7,114,0,16,0,0,0,0,0,
+70,2,16,0,0,0,0,0,70,2,16,0,1,0,0,0,
+30,0,0,7,130,0,16,0,0,0,0,0,58,0,16,0,
+0,0,0,0,1,64,0,0,1,0,0,0,22,0,0,1,
+54,0,0,5,114,32,16,0,0,0,0,0,70,2,16,0,
+0,0,0,0,54,0,0,5,114,32,16,0,1,0,0,0,
+70,18,16,0,4,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,32,0,0,0,3,0,0,0,0,0,0,0,
+7,0,0,0,20,0,0,0,2,0,0,0,1,0,0,0,
+1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+4,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,188,4,0,0,
+1,0,0,0,108,0,0,0,1,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,142,4,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,92,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,85,110,105,116,
+121,70,70,86,101,114,116,101,120,0,171,171,92,0,0,0,
+14,0,0,0,132,0,0,0,240,3,0,0,0,0,0,0,
+0,0,0,0,180,2,0,0,0,0,0,0,64,0,0,0,
+0,0,0,0,204,2,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,240,2,0,0,
+64,0,0,0,64,0,0,0,0,0,0,0,204,2,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,253,2,0,0,128,0,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,56,3,0,0,
+144,0,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,71,3,0,0,160,0,0,0,128,0,0,0,
+2,0,0,0,88,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,124,3,0,0,
+32,1,0,0,128,0,0,0,2,0,0,0,140,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,176,3,0,0,160,1,0,0,128,0,0,0,
+2,0,0,0,192,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,228,3,0,0,
+32,2,0,0,128,0,0,0,2,0,0,0,244,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,24,4,0,0,160,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,39,4,0,0,
+176,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,54,4,0,0,192,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,66,4,0,0,
+208,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,82,4,0,0,224,2,0,0,0,1,0,0,
+0,0,0,0,96,4,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,132,4,0,0,
+224,3,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,102,102,95,109,97,116,114,105,120,95,109,118,
+112,0,102,108,111,97,116,52,120,52,0,171,3,0,3,0,
+4,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,194,2,0,0,
+102,102,95,109,97,116,114,105,120,95,109,118,0,102,102,95,
+118,101,99,95,99,111,108,111,114,0,102,108,111,97,116,52,
+0,171,171,171,1,0,3,0,1,0,4,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,118,101,99,95,97,
+109,98,105,101,110,116,0,102,102,95,108,105,103,104,116,95,
+99,111,108,111,114,0,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,112,111,115,0,171,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,97,116,116,101,110,0,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,115,112,111,
+116,0,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,109,97,116,95,100,
+105,102,102,117,115,101,0,102,102,95,109,97,116,95,97,109,
+98,105,101,110,116,0,102,102,95,109,97,116,95,115,112,101,
+99,0,102,102,95,109,97,116,95,101,109,105,115,115,105,111,
+110,0,102,102,95,109,97,116,114,105,120,95,116,101,120,0,
+3,0,3,0,4,0,4,0,4,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+194,2,0,0,102,102,95,102,111,103,95,118,115,0,77,105,
+99,114,111,115,111,102,116,32,40,82,41,32,72,76,83,76,
+32,83,104,97,100,101,114,32,67,111,109,112,105,108,101,114,
+32,54,46,51,46,57,52,49,53,46,48,0,76,70,83,48,
+164,1,0,0,7,0,0,0,8,0,0,0,88,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,2,0,0,0,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,106,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,118,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,128,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,136,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,149,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,3,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,159,1,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,5,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,67,111,109,112,
+117,116,101,83,112,111,116,76,105,103,104,116,56,0,101,121,
+101,80,111,115,105,116,105,111,110,0,101,121,101,78,111,114,
+109,97,108,0,118,105,101,119,68,105,114,0,100,105,102,102,
+117,115,101,67,111,108,111,114,0,115,112,101,99,67,111,108,
+111,114,0,97,109,98,0,171,76,73,66,70,80,16,0,0,
+68,88,66,67,235,115,185,156,43,13,47,75,143,138,114,228,
+61,12,79,253,1,0,0,0,80,16,0,0,5,0,0,0,
+52,0,0,0,156,3,0,0,96,9,0,0,220,9,0,0,
+160,14,0,0,65,111,110,57,96,3,0,0,96,3,0,0,
+0,2,86,76,48,3,0,0,48,0,0,0,1,0,36,0,
+0,0,48,0,0,0,48,0,0,0,36,0,0,0,48,0,
+0,0,0,0,45,0,0,0,0,0,0,0,0,2,86,76,
+81,0,0,5,45,0,15,160,0,0,0,0,0,0,0,0,
+0,0,128,63,0,0,0,0,48,0,0,5,0,0,15,240,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+31,0,0,2,5,0,0,128,0,0,15,144,31,0,0,2,
+5,0,1,128,1,0,15,144,31,0,0,2,5,0,2,128,
+2,0,15,144,31,0,0,2,5,0,3,128,3,0,15,144,
+31,0,0,2,5,0,4,128,4,0,15,144,31,0,0,2,
+5,0,5,128,5,0,15,144,1,0,0,2,0,0,7,128,
+4,0,228,144,1,0,0,2,1,0,7,128,5,0,228,144,
+27,0,0,2,0,8,228,240,0,0,228,240,4,0,0,6,
+2,0,7,128,0,0,228,144,18,32,255,161,0,8,228,240,
+18,32,228,160,0,8,228,240,8,0,0,3,0,0,8,128,
+2,0,228,128,2,0,228,128,7,0,0,2,1,0,8,128,
+0,0,255,128,4,0,0,4,3,0,7,128,2,0,228,128,
+1,0,255,128,2,0,228,144,36,0,0,2,4,0,7,128,
+3,0,228,128,8,0,0,3,2,0,8,128,1,0,228,144,
+4,0,228,128,5,0,0,5,3,0,1,128,18,32,255,160,
+0,8,228,240,18,32,255,160,0,8,228,240,12,0,0,3,
+3,0,1,128,3,0,0,129,3,0,0,128,12,0,0,4,
+3,0,2,128,26,32,255,160,0,8,228,240,0,0,255,128,
+5,0,0,3,3,0,1,128,3,0,85,128,3,0,0,128,
+5,0,0,4,0,0,8,128,0,0,255,128,26,32,170,160,
+0,8,228,240,2,0,0,3,0,0,8,128,0,0,255,128,
+45,0,170,160,6,0,0,2,0,0,8,128,0,0,255,128,
+4,0,0,4,0,0,8,128,3,0,0,128,0,0,255,129,
+0,0,255,128,11,0,0,3,2,0,8,128,2,0,255,128,
+45,0,85,160,32,0,0,3,3,0,1,128,2,0,255,128,
+44,0,255,160,10,0,0,3,2,0,8,128,3,0,0,128,
+45,0,170,160,5,0,0,3,2,0,7,128,1,0,255,128,
+2,0,228,128,8,0,0,3,1,0,8,128,1,0,228,144,
+2,0,228,128,8,0,0,4,2,0,1,128,2,0,228,128,
+34,32,228,160,0,8,228,240,11,0,0,3,2,0,1,128,
+2,0,0,128,45,0,85,160,2,0,0,4,2,0,1,128,
+2,0,0,128,26,32,0,161,0,8,228,240,5,0,0,4,
+2,0,1,128,2,0,0,128,26,32,85,160,0,8,228,240,
+11,0,0,3,2,0,1,128,2,0,0,128,45,0,85,160,
+10,0,0,3,2,0,1,128,2,0,0,128,45,0,170,160,
+5,0,0,3,0,0,8,128,0,0,255,128,2,0,0,128,
+11,0,0,3,1,0,8,128,1,0,255,128,45,0,85,160,
+12,0,0,3,2,0,1,128,45,0,85,160,1,0,255,128,
+5,0,0,3,2,0,2,128,2,0,255,128,0,0,255,128,
+5,0,0,4,2,0,14,128,2,0,85,128,10,32,144,160,
+0,8,228,240,4,0,0,4,0,0,7,128,2,0,0,128,
+2,0,249,128,0,0,228,128,5,0,0,3,2,0,7,128,
+1,0,255,128,3,0,228,144,5,0,0,4,2,0,7,128,
+2,0,228,128,10,32,228,160,0,8,228,240,5,0,0,3,
+2,0,7,128,0,0,255,128,2,0,228,128,10,0,0,3,
+2,0,7,128,2,0,228,128,45,0,170,160,2,0,0,3,
+1,0,7,128,1,0,228,128,2,0,228,128,29,0,0,0,
+1,0,0,2,1,0,7,224,0,0,228,128,1,0,0,2,
+0,0,7,224,1,0,228,128,255,255,0,0,83,72,68,82,
+188,5,0,0,64,0,240,255,111,1,0,0,89,8,0,4,
+70,142,32,0,0,0,0,0,45,0,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,95,0,0,3,114,16,16,0,
+1,0,0,0,95,0,0,3,114,16,16,0,2,0,0,0,
+95,0,0,3,114,16,16,0,3,0,0,0,95,0,0,3,
+114,16,16,0,4,0,0,0,95,0,0,3,114,16,16,0,
+5,0,0,0,101,0,0,3,114,32,16,0,0,0,0,0,
+101,0,0,3,114,32,16,0,1,0,0,0,104,0,0,2,
+4,0,0,0,54,0,0,5,114,0,16,0,0,0,0,0,
+70,18,16,0,4,0,0,0,54,0,0,5,114,0,16,0,
+1,0,0,0,70,18,16,0,5,0,0,0,54,0,0,5,
+130,0,16,0,0,0,0,0,1,64,0,0,0,0,0,0,
+48,0,0,1,33,0,0,7,130,0,16,0,1,0,0,0,
+58,0,16,0,0,0,0,0,1,64,0,0,8,0,0,0,
+3,0,4,3,58,0,16,0,1,0,0,0,50,0,0,16,
+114,0,16,0,2,0,0,0,70,18,16,128,65,0,0,0,
+0,0,0,0,246,143,32,6,0,0,0,0,18,0,0,0,
+58,0,16,0,0,0,0,0,70,130,32,6,0,0,0,0,
+18,0,0,0,58,0,16,0,0,0,0,0,16,0,0,7,
+130,0,16,0,1,0,0,0,70,2,16,0,2,0,0,0,
+70,2,16,0,2,0,0,0,50,0,0,12,130,0,16,0,
+2,0,0,0,42,128,32,6,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,58,0,16,0,1,0,0,0,
+1,64,0,0,0,0,128,63,14,0,0,10,130,0,16,0,
+2,0,0,0,2,64,0,0,0,0,128,63,0,0,128,63,
+0,0,128,63,0,0,128,63,58,0,16,0,2,0,0,0,
+57,0,0,10,18,0,16,0,3,0,0,0,1,64,0,0,
+0,0,0,0,58,128,32,6,0,0,0,0,18,0,0,0,
+58,0,16,0,0,0,0,0,49,0,0,10,34,0,16,0,
+3,0,0,0,58,128,32,6,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,58,0,16,0,1,0,0,0,
+1,0,0,7,18,0,16,0,3,0,0,0,26,0,16,0,
+3,0,0,0,10,0,16,0,3,0,0,0,55,0,0,9,
+130,0,16,0,2,0,0,0,10,0,16,0,3,0,0,0,
+1,64,0,0,0,0,0,0,58,0,16,0,2,0,0,0,
+68,0,0,5,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,56,0,0,7,114,0,16,0,3,0,0,0,
+246,15,16,0,1,0,0,0,70,2,16,0,2,0,0,0,
+16,0,0,10,130,0,16,0,3,0,0,0,70,2,16,0,
+3,0,0,0,70,130,32,6,0,0,0,0,34,0,0,0,
+58,0,16,0,0,0,0,0,52,0,0,7,130,0,16,0,
+3,0,0,0,58,0,16,0,3,0,0,0,1,64,0,0,
+0,0,0,0,0,0,0,11,130,0,16,0,3,0,0,0,
+58,0,16,0,3,0,0,0,10,128,32,134,65,0,0,0,
+0,0,0,0,26,0,0,0,58,0,16,0,0,0,0,0,
+56,32,0,10,130,0,16,0,3,0,0,0,58,0,16,0,
+3,0,0,0,26,128,32,6,0,0,0,0,26,0,0,0,
+58,0,16,0,0,0,0,0,56,0,0,7,130,0,16,0,
+2,0,0,0,58,0,16,0,2,0,0,0,58,0,16,0,
+3,0,0,0,16,0,0,7,18,0,16,0,3,0,0,0,
+70,18,16,0,1,0,0,0,70,2,16,0,3,0,0,0,
+52,0,0,7,18,0,16,0,3,0,0,0,10,0,16,0,
+3,0,0,0,1,64,0,0,0,0,0,0,56,0,0,7,
+226,0,16,0,3,0,0,0,6,0,16,0,3,0,0,0,
+6,25,16,0,3,0,0,0,56,0,0,10,226,0,16,0,
+3,0,0,0,86,14,16,0,3,0,0,0,6,137,32,6,
+0,0,0,0,10,0,0,0,58,0,16,0,0,0,0,0,
+49,0,0,7,18,0,16,0,3,0,0,0,1,64,0,0,
+0,0,0,0,10,0,16,0,3,0,0,0,31,0,4,3,
+10,0,16,0,3,0,0,0,50,0,0,9,114,0,16,0,
+2,0,0,0,70,2,16,0,2,0,0,0,246,15,16,0,
+1,0,0,0,70,18,16,0,2,0,0,0,16,0,0,7,
+130,0,16,0,1,0,0,0,70,2,16,0,2,0,0,0,
+70,2,16,0,2,0,0,0,68,0,0,5,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,56,0,0,7,
+114,0,16,0,2,0,0,0,246,15,16,0,1,0,0,0,
+70,2,16,0,2,0,0,0,16,0,0,7,130,0,16,0,
+1,0,0,0,70,18,16,0,1,0,0,0,70,2,16,0,
+2,0,0,0,52,0,0,7,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,1,64,0,0,0,0,0,0,
+47,0,0,5,130,0,16,0,1,0,0,0,58,0,16,0,
+1,0,0,0,56,0,0,8,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,58,128,32,0,0,0,0,0,
+44,0,0,0,25,0,0,5,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,51,0,0,7,130,0,16,0,
+1,0,0,0,58,0,16,0,1,0,0,0,1,64,0,0,
+0,0,128,63,56,0,0,7,130,0,16,0,1,0,0,0,
+58,0,16,0,1,0,0,0,58,0,16,0,2,0,0,0,
+50,0,0,12,114,0,16,0,0,0,0,0,246,15,16,0,
+1,0,0,0,70,130,32,6,0,0,0,0,10,0,0,0,
+58,0,16,0,0,0,0,0,70,2,16,0,0,0,0,0,
+21,0,0,1,56,0,0,7,114,0,16,0,2,0,0,0,
+246,15,16,0,2,0,0,0,150,7,16,0,3,0,0,0,
+51,0,0,10,114,0,16,0,2,0,0,0,70,2,16,0,
+2,0,0,0,2,64,0,0,0,0,128,63,0,0,128,63,
+0,0,128,63,0,0,0,0,0,0,0,7,114,0,16,0,
+1,0,0,0,70,2,16,0,1,0,0,0,70,2,16,0,
+2,0,0,0,30,0,0,7,130,0,16,0,0,0,0,0,
+58,0,16,0,0,0,0,0,1,64,0,0,1,0,0,0,
+22,0,0,1,54,0,0,5,114,32,16,0,1,0,0,0,
+70,2,16,0,0,0,0,0,54,0,0,5,114,32,16,0,
+0,0,0,0,70,2,16,0,1,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,48,0,0,0,4,0,0,0,
+0,0,0,0,8,0,0,0,33,0,0,0,2,0,0,0,
+1,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,5,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+188,4,0,0,1,0,0,0,108,0,0,0,1,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,142,4,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+85,110,105,116,121,70,70,86,101,114,116,101,120,0,171,171,
+92,0,0,0,14,0,0,0,132,0,0,0,240,3,0,0,
+0,0,0,0,0,0,0,0,180,2,0,0,0,0,0,0,
+64,0,0,0,0,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+240,2,0,0,64,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,253,2,0,0,128,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+56,3,0,0,144,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,71,3,0,0,160,0,0,0,
+128,0,0,0,2,0,0,0,88,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+124,3,0,0,32,1,0,0,128,0,0,0,2,0,0,0,
+140,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,176,3,0,0,160,1,0,0,
+128,0,0,0,2,0,0,0,192,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+228,3,0,0,32,2,0,0,128,0,0,0,2,0,0,0,
+244,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,24,4,0,0,160,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+39,4,0,0,176,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,54,4,0,0,192,2,0,0,
+16,0,0,0,2,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+66,4,0,0,208,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,82,4,0,0,224,2,0,0,
+0,1,0,0,0,0,0,0,96,4,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+132,4,0,0,224,3,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,102,102,95,109,97,116,114,105,
+120,95,109,118,112,0,102,108,111,97,116,52,120,52,0,171,
+3,0,3,0,4,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+194,2,0,0,102,102,95,109,97,116,114,105,120,95,109,118,
+0,102,102,95,118,101,99,95,99,111,108,111,114,0,102,108,
+111,97,116,52,0,171,171,171,1,0,3,0,1,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,118,
+101,99,95,97,109,98,105,101,110,116,0,102,102,95,108,105,
+103,104,116,95,99,111,108,111,114,0,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,112,111,115,0,171,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,97,116,116,
+101,110,0,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,115,112,111,116,0,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,109,
+97,116,95,100,105,102,102,117,115,101,0,102,102,95,109,97,
+116,95,97,109,98,105,101,110,116,0,102,102,95,109,97,116,
+95,115,112,101,99,0,102,102,95,109,97,116,95,101,109,105,
+115,115,105,111,110,0,102,102,95,109,97,116,114,105,120,95,
+116,101,120,0,3,0,3,0,4,0,4,0,4,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,194,2,0,0,102,102,95,102,111,103,95,118,
+115,0,77,105,99,114,111,115,111,102,116,32,40,82,41,32,
+72,76,83,76,32,83,104,97,100,101,114,32,67,111,109,112,
+105,108,101,114,32,54,46,51,46,57,52,49,53,46,48,0,
+76,70,83,48,168,1,0,0,7,0,0,0,8,0,0,0,
+88,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,2,0,0,0,
+255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,
+110,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+122,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,
+1,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+132,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,
+2,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+140,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+3,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+153,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,3,0,0,0,
+4,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+163,1,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,
+5,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+67,111,109,112,117,116,101,83,112,111,116,76,105,103,104,116,
+83,112,101,99,56,0,101,121,101,80,111,115,105,116,105,111,
+110,0,101,121,101,78,111,114,109,97,108,0,118,105,101,119,
+68,105,114,0,100,105,102,102,117,115,101,67,111,108,111,114,
+0,115,112,101,99,67,111,108,111,114,0,97,109,98,0,171,
+76,73,66,70,32,3,0,0,68,88,66,67,131,242,33,177,
+67,199,181,81,188,12,223,170,153,36,248,104,1,0,0,0,
+32,3,0,0,6,0,0,0,56,0,0,0,156,0,0,0,
+12,1,0,0,108,1,0,0,232,1,0,0,92,2,0,0,
+65,111,110,57,92,0,0,0,92,0,0,0,0,2,86,76,
+56,0,0,0,36,0,0,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,2,86,76,
+31,0,0,2,5,0,0,128,0,0,15,144,31,0,0,2,
+5,0,1,128,1,0,15,144,1,0,0,2,0,0,7,224,
+0,0,228,144,1,0,0,2,0,0,8,224,1,0,255,144,
+255,255,0,0,65,111,110,57,104,0,0,0,104,0,0,0,
+0,2,80,76,68,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,80,76,31,0,0,2,0,0,0,128,0,0,7,176,
+31,0,0,2,0,0,0,128,1,0,8,176,1,0,0,2,
+0,0,7,128,0,0,228,176,1,0,0,2,0,0,8,128,
+1,0,255,176,1,0,0,2,0,0,15,224,0,0,228,128,
+255,255,0,0,83,72,68,82,88,0,0,0,64,0,240,255,
+22,0,0,0,95,0,0,3,114,16,16,0,0,0,0,0,
+95,0,0,3,130,16,16,0,1,0,0,0,101,0,0,3,
+242,32,16,0,0,0,0,0,54,0,0,5,114,32,16,0,
+0,0,0,0,70,18,16,0,0,0,0,0,54,0,0,5,
+130,32,16,0,0,0,0,0,58,16,16,0,1,0,0,0,
+62,0,0,1,83,84,65,84,116,0,0,0,3,0,0,0,
+0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,108,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+60,0,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,171,171,76,70,83,48,188,0,0,0,3,0,0,0,
+8,0,0,0,152,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,170,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,177,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,1,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,76,105,103,104,116,105,110,103,
+67,111,108,111,114,0,108,99,111,108,111,114,0,100,105,102,
+102,99,111,108,111,114,0,171,76,73,66,70,24,8,0,0,
+68,88,66,67,172,130,85,234,66,7,19,66,150,202,205,16,
+164,226,78,167,1,0,0,0,24,8,0,0,6,0,0,0,
+56,0,0,0,208,0,0,0,116,1,0,0,80,2,0,0,
+204,2,0,0,144,7,0,0,65,111,110,57,144,0,0,0,
+144,0,0,0,0,2,86,76,96,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,0,0,0,0,4,0,0,0,0,0,0,0,
+0,2,86,76,31,0,0,2,5,0,0,128,0,0,15,144,
+5,0,0,3,0,0,15,128,0,0,85,144,1,0,228,160,
+4,0,0,4,0,0,15,128,0,0,228,160,0,0,0,144,
+0,0,228,128,4,0,0,4,0,0,15,128,2,0,228,160,
+0,0,170,144,0,0,228,128,4,0,0,4,0,0,15,224,
+3,0,228,160,0,0,255,144,0,0,228,128,255,255,0,0,
+65,111,110,57,156,0,0,0,156,0,0,0,0,2,80,76,
+108,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,0,0,0,0,
+4,0,0,0,0,0,0,0,0,2,80,76,31,0,0,2,
+0,0,0,128,0,0,15,176,5,0,0,3,0,0,15,128,
+0,0,85,176,1,0,228,160,4,0,0,4,0,0,15,128,
+0,0,228,160,0,0,0,176,0,0,228,128,4,0,0,4,
+0,0,15,128,2,0,228,160,0,0,170,176,0,0,228,128,
+4,0,0,4,0,0,15,128,3,0,228,160,0,0,255,176,
+0,0,228,128,1,0,0,2,0,0,15,224,0,0,228,128,
+255,255,0,0,83,72,68,82,212,0,0,0,64,0,240,255,
+53,0,0,0,89,0,0,4,70,142,32,0,0,0,0,0,
+4,0,0,0,95,0,0,3,242,16,16,0,0,0,0,0,
+101,0,0,3,242,32,16,0,0,0,0,0,104,0,0,2,
+1,0,0,0,56,0,0,8,242,0,16,0,0,0,0,0,
+86,21,16,0,0,0,0,0,70,142,32,0,0,0,0,0,
+1,0,0,0,50,0,0,10,242,0,16,0,0,0,0,0,
+70,142,32,0,0,0,0,0,0,0,0,0,6,16,16,0,
+0,0,0,0,70,14,16,0,0,0,0,0,50,0,0,10,
+242,0,16,0,0,0,0,0,70,142,32,0,0,0,0,0,
+2,0,0,0,166,26,16,0,0,0,0,0,70,14,16,0,
+0,0,0,0,50,0,0,10,242,32,16,0,0,0,0,0,
+70,142,32,0,0,0,0,0,3,0,0,0,246,31,16,0,
+0,0,0,0,70,14,16,0,0,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,5,0,0,0,1,0,0,0,
+0,0,0,0,2,0,0,0,4,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+188,4,0,0,1,0,0,0,108,0,0,0,1,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,142,4,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+85,110,105,116,121,70,70,86,101,114,116,101,120,0,171,171,
+92,0,0,0,14,0,0,0,132,0,0,0,240,3,0,0,
+0,0,0,0,0,0,0,0,180,2,0,0,0,0,0,0,
+64,0,0,0,2,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+240,2,0,0,64,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,253,2,0,0,128,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+56,3,0,0,144,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,71,3,0,0,160,0,0,0,
+128,0,0,0,0,0,0,0,88,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+124,3,0,0,32,1,0,0,128,0,0,0,0,0,0,0,
+140,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,176,3,0,0,160,1,0,0,
+128,0,0,0,0,0,0,0,192,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+228,3,0,0,32,2,0,0,128,0,0,0,0,0,0,0,
+244,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,24,4,0,0,160,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+39,4,0,0,176,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,54,4,0,0,192,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+66,4,0,0,208,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,82,4,0,0,224,2,0,0,
+0,1,0,0,0,0,0,0,96,4,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+132,4,0,0,224,3,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,102,102,95,109,97,116,114,105,
+120,95,109,118,112,0,102,108,111,97,116,52,120,52,0,171,
+3,0,3,0,4,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+194,2,0,0,102,102,95,109,97,116,114,105,120,95,109,118,
+0,102,102,95,118,101,99,95,99,111,108,111,114,0,102,108,
+111,97,116,52,0,171,171,171,1,0,3,0,1,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,118,
+101,99,95,97,109,98,105,101,110,116,0,102,102,95,108,105,
+103,104,116,95,99,111,108,111,114,0,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,112,111,115,0,171,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,97,116,116,
+101,110,0,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,115,112,111,116,0,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,109,
+97,116,95,100,105,102,102,117,115,101,0,102,102,95,109,97,
+116,95,97,109,98,105,101,110,116,0,102,102,95,109,97,116,
+95,115,112,101,99,0,102,102,95,109,97,116,95,101,109,105,
+115,115,105,111,110,0,102,102,95,109,97,116,114,105,120,95,
+116,101,120,0,3,0,3,0,4,0,4,0,4,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,194,2,0,0,102,102,95,102,111,103,95,118,
+115,0,77,105,99,114,111,115,111,102,116,32,40,82,41,32,
+72,76,83,76,32,83,104,97,100,101,114,32,67,111,109,112,
+105,108,101,114,32,54,46,51,46,57,52,49,53,46,48,0,
+76,70,83,48,128,0,0,0,2,0,0,0,8,0,0,0,
+104,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,2,0,0,0,
+255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,
+120,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+84,114,97,110,115,102,111,114,109,86,101,114,116,101,120,0,
+118,101,114,116,101,120,0,171,76,73,66,70,180,2,0,0,
+68,88,66,67,62,110,178,176,113,161,39,113,135,74,135,255,
+184,2,122,27,1,0,0,0,180,2,0,0,6,0,0,0,
+56,0,0,0,176,0,0,0,8,1,0,0,72,1,0,0,
+196,1,0,0,56,2,0,0,65,111,110,57,112,0,0,0,
+112,0,0,0,0,2,86,76,76,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,86,76,81,0,0,5,0,0,15,160,
+0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,
+31,0,0,2,5,0,0,128,0,0,15,144,11,0,0,3,
+0,0,15,128,0,0,228,144,0,0,0,160,10,0,0,3,
+0,0,15,224,0,0,228,128,0,0,85,160,255,255,0,0,
+65,111,110,57,80,0,0,0,80,0,0,0,0,2,80,76,
+44,0,0,0,36,0,0,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,2,80,76,
+31,0,0,2,0,0,0,128,0,0,15,176,1,0,0,2,
+0,0,31,128,0,0,228,176,1,0,0,2,0,0,15,224,
+0,0,228,128,255,255,0,0,83,72,68,82,56,0,0,0,
+64,0,240,255,14,0,0,0,95,0,0,3,242,16,16,0,
+0,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+54,32,0,5,242,32,16,0,0,0,0,0,70,30,16,0,
+0,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,108,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,60,0,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,77,105,99,114,111,115,111,102,
+116,32,40,82,41,32,72,76,83,76,32,83,104,97,100,101,
+114,32,67,111,109,112,105,108,101,114,32,54,46,51,46,57,
+52,49,53,46,48,0,171,171,76,70,83,48,116,0,0,0,
+2,0,0,0,8,0,0,0,104,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,114,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,83,97,116,117,114,97,116,101,
+52,0,99,0,76,73,66,70,180,2,0,0,68,88,66,67,
+240,200,99,27,197,129,185,234,49,50,47,69,172,40,172,184,
+1,0,0,0,180,2,0,0,6,0,0,0,56,0,0,0,
+176,0,0,0,8,1,0,0,72,1,0,0,196,1,0,0,
+56,2,0,0,65,111,110,57,112,0,0,0,112,0,0,0,
+0,2,86,76,76,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,86,76,81,0,0,5,0,0,15,160,0,0,0,0,
+0,0,128,63,0,0,0,0,0,0,0,0,31,0,0,2,
+5,0,0,128,0,0,15,144,11,0,0,3,0,0,7,128,
+0,0,228,144,0,0,0,160,10,0,0,3,0,0,7,224,
+0,0,228,128,0,0,85,160,255,255,0,0,65,111,110,57,
+80,0,0,0,80,0,0,0,0,2,80,76,44,0,0,0,
+36,0,0,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,2,80,76,31,0,0,2,
+0,0,0,128,0,0,7,176,1,0,0,2,0,0,23,128,
+0,0,228,176,1,0,0,2,0,0,7,224,0,0,228,128,
+255,255,0,0,83,72,68,82,56,0,0,0,64,0,240,255,
+14,0,0,0,95,0,0,3,114,16,16,0,0,0,0,0,
+101,0,0,3,114,32,16,0,0,0,0,0,54,32,0,5,
+114,32,16,0,0,0,0,0,70,18,16,0,0,0,0,0,
+62,0,0,1,83,84,65,84,116,0,0,0,2,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,108,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+60,0,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,171,171,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,114,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,83,97,116,117,114,97,116,101,51,0,99,0,
+76,73,66,70,120,2,0,0,68,88,66,67,138,49,223,44,
+107,242,58,167,5,75,190,32,216,167,145,36,1,0,0,0,
+120,2,0,0,6,0,0,0,56,0,0,0,132,0,0,0,
+208,0,0,0,16,1,0,0,140,1,0,0,0,2,0,0,
+65,111,110,57,68,0,0,0,68,0,0,0,0,2,86,76,
+32,0,0,0,36,0,0,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,2,86,76,
+31,0,0,2,5,0,0,128,0,0,15,144,1,0,0,2,
+0,0,7,224,0,0,228,144,255,255,0,0,65,111,110,57,
+68,0,0,0,68,0,0,0,0,2,80,76,32,0,0,0,
+36,0,0,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,2,80,76,31,0,0,2,
+0,0,0,128,0,0,7,176,1,0,0,2,0,0,7,224,
+0,0,228,176,255,255,0,0,83,72,68,82,56,0,0,0,
+64,0,240,255,14,0,0,0,95,0,0,3,114,16,16,0,
+0,0,0,0,101,0,0,3,114,32,16,0,0,0,0,0,
+54,0,0,5,114,32,16,0,0,0,0,0,70,18,16,0,
+0,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,108,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,60,0,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,77,105,99,114,111,115,111,102,
+116,32,40,82,41,32,72,76,83,76,32,83,104,97,100,101,
+114,32,67,111,109,112,105,108,101,114,32,54,46,51,46,57,
+52,49,53,46,48,0,171,171,76,70,83,48,112,0,0,0,
+2,0,0,0,8,0,0,0,104,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,110,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,76,111,97,100,51,0,99,0,
+76,73,66,70,24,7,0,0,68,88,66,67,113,18,103,154,
+83,56,52,120,187,13,106,3,58,124,196,9,1,0,0,0,
+24,7,0,0,6,0,0,0,56,0,0,0,148,0,0,0,
+252,0,0,0,88,1,0,0,212,1,0,0,152,6,0,0,
+65,111,110,57,84,0,0,0,84,0,0,0,0,2,86,76,
+36,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,0,0,44,0,
+1,0,0,0,0,0,0,0,0,2,86,76,31,0,0,2,
+5,0,0,128,0,0,15,144,5,0,0,3,0,0,7,224,
+0,0,228,144,0,0,228,160,255,255,0,0,65,111,110,57,
+96,0,0,0,96,0,0,0,0,2,80,76,48,0,0,0,
+48,0,0,0,1,0,36,0,0,0,48,0,0,0,48,0,
+0,0,36,0,0,0,48,0,0,0,44,0,1,0,0,0,
+0,0,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,7,176,5,0,0,3,0,0,7,128,0,0,228,176,
+0,0,228,160,1,0,0,2,0,0,7,224,0,0,228,128,
+255,255,0,0,83,72,68,82,84,0,0,0,64,0,240,255,
+21,0,0,0,89,0,0,4,70,142,32,0,0,0,0,0,
+45,0,0,0,95,0,0,3,114,16,16,0,0,0,0,0,
+101,0,0,3,114,32,16,0,0,0,0,0,56,0,0,8,
+114,32,16,0,0,0,0,0,70,18,16,0,0,0,0,0,
+70,130,32,0,0,0,0,0,44,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+188,4,0,0,1,0,0,0,108,0,0,0,1,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,142,4,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+85,110,105,116,121,70,70,86,101,114,116,101,120,0,171,171,
+92,0,0,0,14,0,0,0,132,0,0,0,240,3,0,0,
+0,0,0,0,0,0,0,0,180,2,0,0,0,0,0,0,
+64,0,0,0,0,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+240,2,0,0,64,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,253,2,0,0,128,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+56,3,0,0,144,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,71,3,0,0,160,0,0,0,
+128,0,0,0,0,0,0,0,88,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+124,3,0,0,32,1,0,0,128,0,0,0,0,0,0,0,
+140,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,176,3,0,0,160,1,0,0,
+128,0,0,0,0,0,0,0,192,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+228,3,0,0,32,2,0,0,128,0,0,0,0,0,0,0,
+244,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,24,4,0,0,160,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+39,4,0,0,176,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,54,4,0,0,192,2,0,0,
+16,0,0,0,2,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+66,4,0,0,208,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,82,4,0,0,224,2,0,0,
+0,1,0,0,0,0,0,0,96,4,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+132,4,0,0,224,3,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,102,102,95,109,97,116,114,105,
+120,95,109,118,112,0,102,108,111,97,116,52,120,52,0,171,
+3,0,3,0,4,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+194,2,0,0,102,102,95,109,97,116,114,105,120,95,109,118,
+0,102,102,95,118,101,99,95,99,111,108,111,114,0,102,108,
+111,97,116,52,0,171,171,171,1,0,3,0,1,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,118,
+101,99,95,97,109,98,105,101,110,116,0,102,102,95,108,105,
+103,104,116,95,99,111,108,111,114,0,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,112,111,115,0,171,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,97,116,116,
+101,110,0,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,115,112,111,116,0,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,109,
+97,116,95,100,105,102,102,117,115,101,0,102,102,95,109,97,
+116,95,97,109,98,105,101,110,116,0,102,102,95,109,97,116,
+95,115,112,101,99,0,102,102,95,109,97,116,95,101,109,105,
+115,115,105,111,110,0,102,102,95,109,97,116,114,105,120,95,
+116,101,120,0,3,0,3,0,4,0,4,0,4,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,194,2,0,0,102,102,95,102,111,103,95,118,
+115,0,77,105,99,114,111,115,111,102,116,32,40,82,41,32,
+72,76,83,76,32,83,104,97,100,101,114,32,67,111,109,112,
+105,108,101,114,32,54,46,51,46,57,52,49,53,46,48,0,
+76,70,83,48,120,0,0,0,2,0,0,0,8,0,0,0,
+104,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,2,0,0,0,
+255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,
+117,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,3,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+77,111,100,117,108,97,116,101,83,112,101,99,0,99,0,171,
+76,73,66,70,16,8,0,0,68,88,66,67,38,168,251,188,
+91,77,207,32,129,166,165,241,221,147,229,117,1,0,0,0,
+16,8,0,0,6,0,0,0,56,0,0,0,208,0,0,0,
+116,1,0,0,80,2,0,0,204,2,0,0,144,7,0,0,
+65,111,110,57,144,0,0,0,144,0,0,0,0,2,86,76,
+96,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,0,0,46,0,
+4,0,0,0,0,0,0,0,0,2,86,76,31,0,0,2,
+5,0,0,128,0,0,15,144,5,0,0,3,0,0,15,128,
+0,0,85,144,1,0,228,160,4,0,0,4,0,0,15,128,
+0,0,228,160,0,0,0,144,0,0,228,128,4,0,0,4,
+0,0,15,128,2,0,228,160,0,0,170,144,0,0,228,128,
+4,0,0,4,0,0,15,224,3,0,228,160,0,0,255,144,
+0,0,228,128,255,255,0,0,65,111,110,57,156,0,0,0,
+156,0,0,0,0,2,80,76,108,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,0,0,46,0,4,0,0,0,0,0,0,0,
+0,2,80,76,31,0,0,2,0,0,0,128,0,0,15,176,
+5,0,0,3,0,0,15,128,0,0,85,176,1,0,228,160,
+4,0,0,4,0,0,15,128,0,0,228,160,0,0,0,176,
+0,0,228,128,4,0,0,4,0,0,15,128,2,0,228,160,
+0,0,170,176,0,0,228,128,4,0,0,4,0,0,15,128,
+3,0,228,160,0,0,255,176,0,0,228,128,1,0,0,2,
+0,0,15,224,0,0,228,128,255,255,0,0,83,72,68,82,
+212,0,0,0,64,0,240,255,53,0,0,0,89,0,0,4,
+70,142,32,0,0,0,0,0,50,0,0,0,95,0,0,3,
+242,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,104,0,0,2,1,0,0,0,56,0,0,8,
+242,0,16,0,0,0,0,0,86,21,16,0,0,0,0,0,
+70,142,32,0,0,0,0,0,47,0,0,0,50,0,0,10,
+242,0,16,0,0,0,0,0,70,142,32,0,0,0,0,0,
+46,0,0,0,6,16,16,0,0,0,0,0,70,14,16,0,
+0,0,0,0,50,0,0,10,242,0,16,0,0,0,0,0,
+70,142,32,0,0,0,0,0,48,0,0,0,166,26,16,0,
+0,0,0,0,70,14,16,0,0,0,0,0,50,0,0,10,
+242,32,16,0,0,0,0,0,70,142,32,0,0,0,0,0,
+49,0,0,0,246,31,16,0,0,0,0,0,70,14,16,0,
+0,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+5,0,0,0,1,0,0,0,0,0,0,0,2,0,0,0,
+4,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,188,4,0,0,1,0,0,0,
+108,0,0,0,1,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,142,4,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,85,110,105,116,121,70,70,86,
+101,114,116,101,120,0,171,171,92,0,0,0,14,0,0,0,
+132,0,0,0,240,3,0,0,0,0,0,0,0,0,0,0,
+180,2,0,0,0,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,240,2,0,0,64,0,0,0,
+64,0,0,0,0,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+253,2,0,0,128,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,56,3,0,0,144,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+71,3,0,0,160,0,0,0,128,0,0,0,0,0,0,0,
+88,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,124,3,0,0,32,1,0,0,
+128,0,0,0,0,0,0,0,140,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+176,3,0,0,160,1,0,0,128,0,0,0,0,0,0,0,
+192,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,228,3,0,0,32,2,0,0,
+128,0,0,0,0,0,0,0,244,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+24,4,0,0,160,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,39,4,0,0,176,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+54,4,0,0,192,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,66,4,0,0,208,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+82,4,0,0,224,2,0,0,0,1,0,0,2,0,0,0,
+96,4,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,132,4,0,0,224,3,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+102,102,95,109,97,116,114,105,120,95,109,118,112,0,102,108,
+111,97,116,52,120,52,0,171,3,0,3,0,4,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,194,2,0,0,102,102,95,109,
+97,116,114,105,120,95,109,118,0,102,102,95,118,101,99,95,
+99,111,108,111,114,0,102,108,111,97,116,52,0,171,171,171,
+1,0,3,0,1,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,118,101,99,95,97,109,98,105,101,
+110,116,0,102,102,95,108,105,103,104,116,95,99,111,108,111,
+114,0,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,112,111,115,0,171,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,97,116,116,101,110,0,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,115,112,111,116,0,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,109,97,116,95,100,105,102,102,117,
+115,101,0,102,102,95,109,97,116,95,97,109,98,105,101,110,
+116,0,102,102,95,109,97,116,95,115,112,101,99,0,102,102,
+95,109,97,116,95,101,109,105,115,115,105,111,110,0,102,102,
+95,109,97,116,114,105,120,95,116,101,120,0,3,0,3,0,
+4,0,4,0,4,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,194,2,0,0,
+102,102,95,102,111,103,95,118,115,0,77,105,99,114,111,115,
+111,102,116,32,40,82,41,32,72,76,83,76,32,83,104,97,
+100,101,114,32,67,111,109,112,105,108,101,114,32,54,46,51,
+46,57,52,49,53,46,48,0,76,70,83,48,120,0,0,0,
+2,0,0,0,8,0,0,0,104,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,116,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,77,117,108,116,105,112,108,121,
+85,86,48,0,117,118,0,171,76,73,66,70,16,8,0,0,
+68,88,66,67,89,231,147,186,246,126,177,127,29,163,1,103,
+12,212,226,6,1,0,0,0,16,8,0,0,6,0,0,0,
+56,0,0,0,208,0,0,0,116,1,0,0,80,2,0,0,
+204,2,0,0,144,7,0,0,65,111,110,57,144,0,0,0,
+144,0,0,0,0,2,86,76,96,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,0,0,50,0,4,0,0,0,0,0,0,0,
+0,2,86,76,31,0,0,2,5,0,0,128,0,0,15,144,
+5,0,0,3,0,0,15,128,0,0,85,144,1,0,228,160,
+4,0,0,4,0,0,15,128,0,0,228,160,0,0,0,144,
+0,0,228,128,4,0,0,4,0,0,15,128,2,0,228,160,
+0,0,170,144,0,0,228,128,4,0,0,4,0,0,15,224,
+3,0,228,160,0,0,255,144,0,0,228,128,255,255,0,0,
+65,111,110,57,156,0,0,0,156,0,0,0,0,2,80,76,
+108,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,0,0,50,0,
+4,0,0,0,0,0,0,0,0,2,80,76,31,0,0,2,
+0,0,0,128,0,0,15,176,5,0,0,3,0,0,15,128,
+0,0,85,176,1,0,228,160,4,0,0,4,0,0,15,128,
+0,0,228,160,0,0,0,176,0,0,228,128,4,0,0,4,
+0,0,15,128,2,0,228,160,0,0,170,176,0,0,228,128,
+4,0,0,4,0,0,15,128,3,0,228,160,0,0,255,176,
+0,0,228,128,1,0,0,2,0,0,15,224,0,0,228,128,
+255,255,0,0,83,72,68,82,212,0,0,0,64,0,240,255,
+53,0,0,0,89,0,0,4,70,142,32,0,0,0,0,0,
+54,0,0,0,95,0,0,3,242,16,16,0,0,0,0,0,
+101,0,0,3,242,32,16,0,0,0,0,0,104,0,0,2,
+1,0,0,0,56,0,0,8,242,0,16,0,0,0,0,0,
+86,21,16,0,0,0,0,0,70,142,32,0,0,0,0,0,
+51,0,0,0,50,0,0,10,242,0,16,0,0,0,0,0,
+70,142,32,0,0,0,0,0,50,0,0,0,6,16,16,0,
+0,0,0,0,70,14,16,0,0,0,0,0,50,0,0,10,
+242,0,16,0,0,0,0,0,70,142,32,0,0,0,0,0,
+52,0,0,0,166,26,16,0,0,0,0,0,70,14,16,0,
+0,0,0,0,50,0,0,10,242,32,16,0,0,0,0,0,
+70,142,32,0,0,0,0,0,53,0,0,0,246,31,16,0,
+0,0,0,0,70,14,16,0,0,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,5,0,0,0,1,0,0,0,
+0,0,0,0,2,0,0,0,4,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+188,4,0,0,1,0,0,0,108,0,0,0,1,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,142,4,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+85,110,105,116,121,70,70,86,101,114,116,101,120,0,171,171,
+92,0,0,0,14,0,0,0,132,0,0,0,240,3,0,0,
+0,0,0,0,0,0,0,0,180,2,0,0,0,0,0,0,
+64,0,0,0,0,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+240,2,0,0,64,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,253,2,0,0,128,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+56,3,0,0,144,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,71,3,0,0,160,0,0,0,
+128,0,0,0,0,0,0,0,88,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+124,3,0,0,32,1,0,0,128,0,0,0,0,0,0,0,
+140,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,176,3,0,0,160,1,0,0,
+128,0,0,0,0,0,0,0,192,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+228,3,0,0,32,2,0,0,128,0,0,0,0,0,0,0,
+244,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,24,4,0,0,160,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+39,4,0,0,176,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,54,4,0,0,192,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+66,4,0,0,208,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,82,4,0,0,224,2,0,0,
+0,1,0,0,2,0,0,0,96,4,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+132,4,0,0,224,3,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,102,102,95,109,97,116,114,105,
+120,95,109,118,112,0,102,108,111,97,116,52,120,52,0,171,
+3,0,3,0,4,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+194,2,0,0,102,102,95,109,97,116,114,105,120,95,109,118,
+0,102,102,95,118,101,99,95,99,111,108,111,114,0,102,108,
+111,97,116,52,0,171,171,171,1,0,3,0,1,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,118,
+101,99,95,97,109,98,105,101,110,116,0,102,102,95,108,105,
+103,104,116,95,99,111,108,111,114,0,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,112,111,115,0,171,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,97,116,116,
+101,110,0,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,115,112,111,116,0,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,109,
+97,116,95,100,105,102,102,117,115,101,0,102,102,95,109,97,
+116,95,97,109,98,105,101,110,116,0,102,102,95,109,97,116,
+95,115,112,101,99,0,102,102,95,109,97,116,95,101,109,105,
+115,115,105,111,110,0,102,102,95,109,97,116,114,105,120,95,
+116,101,120,0,3,0,3,0,4,0,4,0,4,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,194,2,0,0,102,102,95,102,111,103,95,118,
+115,0,77,105,99,114,111,115,111,102,116,32,40,82,41,32,
+72,76,83,76,32,83,104,97,100,101,114,32,67,111,109,112,
+105,108,101,114,32,54,46,51,46,57,52,49,53,46,48,0,
+76,70,83,48,120,0,0,0,2,0,0,0,8,0,0,0,
+104,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,2,0,0,0,
+255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,
+116,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+77,117,108,116,105,112,108,121,85,86,49,0,117,118,0,171,
+76,73,66,70,16,8,0,0,68,88,66,67,41,244,254,181,
+196,180,14,116,129,203,162,222,199,240,214,248,1,0,0,0,
+16,8,0,0,6,0,0,0,56,0,0,0,208,0,0,0,
+116,1,0,0,80,2,0,0,204,2,0,0,144,7,0,0,
+65,111,110,57,144,0,0,0,144,0,0,0,0,2,86,76,
+96,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,0,0,54,0,
+4,0,0,0,0,0,0,0,0,2,86,76,31,0,0,2,
+5,0,0,128,0,0,15,144,5,0,0,3,0,0,15,128,
+0,0,85,144,1,0,228,160,4,0,0,4,0,0,15,128,
+0,0,228,160,0,0,0,144,0,0,228,128,4,0,0,4,
+0,0,15,128,2,0,228,160,0,0,170,144,0,0,228,128,
+4,0,0,4,0,0,15,224,3,0,228,160,0,0,255,144,
+0,0,228,128,255,255,0,0,65,111,110,57,156,0,0,0,
+156,0,0,0,0,2,80,76,108,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,0,0,54,0,4,0,0,0,0,0,0,0,
+0,2,80,76,31,0,0,2,0,0,0,128,0,0,15,176,
+5,0,0,3,0,0,15,128,0,0,85,176,1,0,228,160,
+4,0,0,4,0,0,15,128,0,0,228,160,0,0,0,176,
+0,0,228,128,4,0,0,4,0,0,15,128,2,0,228,160,
+0,0,170,176,0,0,228,128,4,0,0,4,0,0,15,128,
+3,0,228,160,0,0,255,176,0,0,228,128,1,0,0,2,
+0,0,15,224,0,0,228,128,255,255,0,0,83,72,68,82,
+212,0,0,0,64,0,240,255,53,0,0,0,89,0,0,4,
+70,142,32,0,0,0,0,0,58,0,0,0,95,0,0,3,
+242,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,104,0,0,2,1,0,0,0,56,0,0,8,
+242,0,16,0,0,0,0,0,86,21,16,0,0,0,0,0,
+70,142,32,0,0,0,0,0,55,0,0,0,50,0,0,10,
+242,0,16,0,0,0,0,0,70,142,32,0,0,0,0,0,
+54,0,0,0,6,16,16,0,0,0,0,0,70,14,16,0,
+0,0,0,0,50,0,0,10,242,0,16,0,0,0,0,0,
+70,142,32,0,0,0,0,0,56,0,0,0,166,26,16,0,
+0,0,0,0,70,14,16,0,0,0,0,0,50,0,0,10,
+242,32,16,0,0,0,0,0,70,142,32,0,0,0,0,0,
+57,0,0,0,246,31,16,0,0,0,0,0,70,14,16,0,
+0,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+5,0,0,0,1,0,0,0,0,0,0,0,2,0,0,0,
+4,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,188,4,0,0,1,0,0,0,
+108,0,0,0,1,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,142,4,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,85,110,105,116,121,70,70,86,
+101,114,116,101,120,0,171,171,92,0,0,0,14,0,0,0,
+132,0,0,0,240,3,0,0,0,0,0,0,0,0,0,0,
+180,2,0,0,0,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,240,2,0,0,64,0,0,0,
+64,0,0,0,0,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+253,2,0,0,128,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,56,3,0,0,144,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+71,3,0,0,160,0,0,0,128,0,0,0,0,0,0,0,
+88,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,124,3,0,0,32,1,0,0,
+128,0,0,0,0,0,0,0,140,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+176,3,0,0,160,1,0,0,128,0,0,0,0,0,0,0,
+192,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,228,3,0,0,32,2,0,0,
+128,0,0,0,0,0,0,0,244,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+24,4,0,0,160,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,39,4,0,0,176,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+54,4,0,0,192,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,66,4,0,0,208,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+82,4,0,0,224,2,0,0,0,1,0,0,2,0,0,0,
+96,4,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,132,4,0,0,224,3,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+102,102,95,109,97,116,114,105,120,95,109,118,112,0,102,108,
+111,97,116,52,120,52,0,171,3,0,3,0,4,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,194,2,0,0,102,102,95,109,
+97,116,114,105,120,95,109,118,0,102,102,95,118,101,99,95,
+99,111,108,111,114,0,102,108,111,97,116,52,0,171,171,171,
+1,0,3,0,1,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,118,101,99,95,97,109,98,105,101,
+110,116,0,102,102,95,108,105,103,104,116,95,99,111,108,111,
+114,0,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,112,111,115,0,171,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,97,116,116,101,110,0,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,115,112,111,116,0,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,109,97,116,95,100,105,102,102,117,
+115,101,0,102,102,95,109,97,116,95,97,109,98,105,101,110,
+116,0,102,102,95,109,97,116,95,115,112,101,99,0,102,102,
+95,109,97,116,95,101,109,105,115,115,105,111,110,0,102,102,
+95,109,97,116,114,105,120,95,116,101,120,0,3,0,3,0,
+4,0,4,0,4,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,194,2,0,0,
+102,102,95,102,111,103,95,118,115,0,77,105,99,114,111,115,
+111,102,116,32,40,82,41,32,72,76,83,76,32,83,104,97,
+100,101,114,32,67,111,109,112,105,108,101,114,32,54,46,51,
+46,57,52,49,53,46,48,0,76,70,83,48,120,0,0,0,
+2,0,0,0,8,0,0,0,104,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,116,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,77,117,108,116,105,112,108,121,
+85,86,50,0,117,118,0,171,76,73,66,70,16,8,0,0,
+68,88,66,67,79,24,223,202,176,61,38,187,194,246,39,54,
+93,187,78,10,1,0,0,0,16,8,0,0,6,0,0,0,
+56,0,0,0,208,0,0,0,116,1,0,0,80,2,0,0,
+204,2,0,0,144,7,0,0,65,111,110,57,144,0,0,0,
+144,0,0,0,0,2,86,76,96,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,0,0,58,0,4,0,0,0,0,0,0,0,
+0,2,86,76,31,0,0,2,5,0,0,128,0,0,15,144,
+5,0,0,3,0,0,15,128,0,0,85,144,1,0,228,160,
+4,0,0,4,0,0,15,128,0,0,228,160,0,0,0,144,
+0,0,228,128,4,0,0,4,0,0,15,128,2,0,228,160,
+0,0,170,144,0,0,228,128,4,0,0,4,0,0,15,224,
+3,0,228,160,0,0,255,144,0,0,228,128,255,255,0,0,
+65,111,110,57,156,0,0,0,156,0,0,0,0,2,80,76,
+108,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,0,0,58,0,
+4,0,0,0,0,0,0,0,0,2,80,76,31,0,0,2,
+0,0,0,128,0,0,15,176,5,0,0,3,0,0,15,128,
+0,0,85,176,1,0,228,160,4,0,0,4,0,0,15,128,
+0,0,228,160,0,0,0,176,0,0,228,128,4,0,0,4,
+0,0,15,128,2,0,228,160,0,0,170,176,0,0,228,128,
+4,0,0,4,0,0,15,128,3,0,228,160,0,0,255,176,
+0,0,228,128,1,0,0,2,0,0,15,224,0,0,228,128,
+255,255,0,0,83,72,68,82,212,0,0,0,64,0,240,255,
+53,0,0,0,89,0,0,4,70,142,32,0,0,0,0,0,
+62,0,0,0,95,0,0,3,242,16,16,0,0,0,0,0,
+101,0,0,3,242,32,16,0,0,0,0,0,104,0,0,2,
+1,0,0,0,56,0,0,8,242,0,16,0,0,0,0,0,
+86,21,16,0,0,0,0,0,70,142,32,0,0,0,0,0,
+59,0,0,0,50,0,0,10,242,0,16,0,0,0,0,0,
+70,142,32,0,0,0,0,0,58,0,0,0,6,16,16,0,
+0,0,0,0,70,14,16,0,0,0,0,0,50,0,0,10,
+242,0,16,0,0,0,0,0,70,142,32,0,0,0,0,0,
+60,0,0,0,166,26,16,0,0,0,0,0,70,14,16,0,
+0,0,0,0,50,0,0,10,242,32,16,0,0,0,0,0,
+70,142,32,0,0,0,0,0,61,0,0,0,246,31,16,0,
+0,0,0,0,70,14,16,0,0,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,5,0,0,0,1,0,0,0,
+0,0,0,0,2,0,0,0,4,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+188,4,0,0,1,0,0,0,108,0,0,0,1,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,142,4,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+85,110,105,116,121,70,70,86,101,114,116,101,120,0,171,171,
+92,0,0,0,14,0,0,0,132,0,0,0,240,3,0,0,
+0,0,0,0,0,0,0,0,180,2,0,0,0,0,0,0,
+64,0,0,0,0,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+240,2,0,0,64,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,253,2,0,0,128,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+56,3,0,0,144,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,71,3,0,0,160,0,0,0,
+128,0,0,0,0,0,0,0,88,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+124,3,0,0,32,1,0,0,128,0,0,0,0,0,0,0,
+140,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,176,3,0,0,160,1,0,0,
+128,0,0,0,0,0,0,0,192,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+228,3,0,0,32,2,0,0,128,0,0,0,0,0,0,0,
+244,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,24,4,0,0,160,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+39,4,0,0,176,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,54,4,0,0,192,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+66,4,0,0,208,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,82,4,0,0,224,2,0,0,
+0,1,0,0,2,0,0,0,96,4,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+132,4,0,0,224,3,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,102,102,95,109,97,116,114,105,
+120,95,109,118,112,0,102,108,111,97,116,52,120,52,0,171,
+3,0,3,0,4,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+194,2,0,0,102,102,95,109,97,116,114,105,120,95,109,118,
+0,102,102,95,118,101,99,95,99,111,108,111,114,0,102,108,
+111,97,116,52,0,171,171,171,1,0,3,0,1,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,118,
+101,99,95,97,109,98,105,101,110,116,0,102,102,95,108,105,
+103,104,116,95,99,111,108,111,114,0,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,112,111,115,0,171,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,97,116,116,
+101,110,0,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,115,112,111,116,0,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,109,
+97,116,95,100,105,102,102,117,115,101,0,102,102,95,109,97,
+116,95,97,109,98,105,101,110,116,0,102,102,95,109,97,116,
+95,115,112,101,99,0,102,102,95,109,97,116,95,101,109,105,
+115,115,105,111,110,0,102,102,95,109,97,116,114,105,120,95,
+116,101,120,0,3,0,3,0,4,0,4,0,4,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,194,2,0,0,102,102,95,102,111,103,95,118,
+115,0,77,105,99,114,111,115,111,102,116,32,40,82,41,32,
+72,76,83,76,32,83,104,97,100,101,114,32,67,111,109,112,
+105,108,101,114,32,54,46,51,46,57,52,49,53,46,48,0,
+76,70,83,48,120,0,0,0,2,0,0,0,8,0,0,0,
+104,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,2,0,0,0,
+255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,
+116,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+77,117,108,116,105,112,108,121,85,86,51,0,117,118,0,171,
+76,73,66,70,128,2,0,0,68,88,66,67,70,241,160,65,
+61,184,241,235,1,168,145,104,13,89,175,213,1,0,0,0,
+128,2,0,0,6,0,0,0,56,0,0,0,132,0,0,0,
+208,0,0,0,16,1,0,0,140,1,0,0,0,2,0,0,
+65,111,110,57,68,0,0,0,68,0,0,0,0,2,86,76,
+32,0,0,0,36,0,0,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,2,86,76,
+31,0,0,2,5,0,0,128,0,0,15,144,1,0,0,2,
+0,0,15,224,0,0,228,144,255,255,0,0,65,111,110,57,
+68,0,0,0,68,0,0,0,0,2,80,76,32,0,0,0,
+36,0,0,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,2,80,76,31,0,0,2,
+0,0,0,128,0,0,15,176,1,0,0,2,0,0,15,224,
+0,0,228,176,255,255,0,0,83,72,68,82,56,0,0,0,
+64,0,240,255,14,0,0,0,95,0,0,3,242,16,16,0,
+0,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+54,0,0,5,242,32,16,0,0,0,0,0,70,30,16,0,
+0,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,108,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,60,0,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,77,105,99,114,111,115,111,102,
+116,32,40,82,41,32,72,76,83,76,32,83,104,97,100,101,
+114,32,67,111,109,112,105,108,101,114,32,54,46,51,46,57,
+52,49,53,46,48,0,171,171,76,70,83,48,120,0,0,0,
+2,0,0,0,8,0,0,0,104,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,116,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,77,117,108,116,105,112,108,121,
+85,86,52,0,117,118,0,171,76,73,66,70,128,2,0,0,
+68,88,66,67,121,42,193,146,188,175,215,160,65,233,118,29,
+149,27,229,44,1,0,0,0,128,2,0,0,6,0,0,0,
+56,0,0,0,132,0,0,0,208,0,0,0,16,1,0,0,
+140,1,0,0,0,2,0,0,65,111,110,57,68,0,0,0,
+68,0,0,0,0,2,86,76,32,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,86,76,31,0,0,2,5,0,0,128,
+0,0,15,144,1,0,0,2,0,0,15,224,0,0,228,144,
+255,255,0,0,65,111,110,57,68,0,0,0,68,0,0,0,
+0,2,80,76,32,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,80,76,31,0,0,2,0,0,0,128,0,0,15,176,
+1,0,0,2,0,0,15,224,0,0,228,176,255,255,0,0,
+83,72,68,82,56,0,0,0,64,0,240,255,14,0,0,0,
+95,0,0,3,242,16,16,0,0,0,0,0,101,0,0,3,
+242,32,16,0,0,0,0,0,54,0,0,5,242,32,16,0,
+0,0,0,0,70,30,16,0,0,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,60,0,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+77,105,99,114,111,115,111,102,116,32,40,82,41,32,72,76,
+83,76,32,83,104,97,100,101,114,32,67,111,109,112,105,108,
+101,114,32,54,46,51,46,57,52,49,53,46,48,0,171,171,
+76,70,83,48,120,0,0,0,2,0,0,0,8,0,0,0,
+104,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,2,0,0,0,
+255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,
+116,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+77,117,108,116,105,112,108,121,85,86,53,0,117,118,0,171,
+76,73,66,70,128,2,0,0,68,88,66,67,43,144,230,30,
+211,110,217,234,138,146,61,118,62,126,189,42,1,0,0,0,
+128,2,0,0,6,0,0,0,56,0,0,0,132,0,0,0,
+208,0,0,0,16,1,0,0,140,1,0,0,0,2,0,0,
+65,111,110,57,68,0,0,0,68,0,0,0,0,2,86,76,
+32,0,0,0,36,0,0,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,2,86,76,
+31,0,0,2,5,0,0,128,0,0,15,144,1,0,0,2,
+0,0,15,224,0,0,228,144,255,255,0,0,65,111,110,57,
+68,0,0,0,68,0,0,0,0,2,80,76,32,0,0,0,
+36,0,0,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,2,80,76,31,0,0,2,
+0,0,0,128,0,0,15,176,1,0,0,2,0,0,15,224,
+0,0,228,176,255,255,0,0,83,72,68,82,56,0,0,0,
+64,0,240,255,14,0,0,0,95,0,0,3,242,16,16,0,
+0,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+54,0,0,5,242,32,16,0,0,0,0,0,70,30,16,0,
+0,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,108,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,60,0,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,77,105,99,114,111,115,111,102,
+116,32,40,82,41,32,72,76,83,76,32,83,104,97,100,101,
+114,32,67,111,109,112,105,108,101,114,32,54,46,51,46,57,
+52,49,53,46,48,0,171,171,76,70,83,48,120,0,0,0,
+2,0,0,0,8,0,0,0,104,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,116,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,77,117,108,116,105,112,108,121,
+85,86,54,0,117,118,0,171,76,73,66,70,128,2,0,0,
+68,88,66,67,225,123,250,221,76,72,10,52,198,89,252,247,
+202,18,5,35,1,0,0,0,128,2,0,0,6,0,0,0,
+56,0,0,0,132,0,0,0,208,0,0,0,16,1,0,0,
+140,1,0,0,0,2,0,0,65,111,110,57,68,0,0,0,
+68,0,0,0,0,2,86,76,32,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,86,76,31,0,0,2,5,0,0,128,
+0,0,15,144,1,0,0,2,0,0,15,224,0,0,228,144,
+255,255,0,0,65,111,110,57,68,0,0,0,68,0,0,0,
+0,2,80,76,32,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,80,76,31,0,0,2,0,0,0,128,0,0,15,176,
+1,0,0,2,0,0,15,224,0,0,228,176,255,255,0,0,
+83,72,68,82,56,0,0,0,64,0,240,255,14,0,0,0,
+95,0,0,3,242,16,16,0,0,0,0,0,101,0,0,3,
+242,32,16,0,0,0,0,0,54,0,0,5,242,32,16,0,
+0,0,0,0,70,30,16,0,0,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,60,0,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+77,105,99,114,111,115,111,102,116,32,40,82,41,32,72,76,
+83,76,32,83,104,97,100,101,114,32,67,111,109,112,105,108,
+101,114,32,54,46,51,46,57,52,49,53,46,48,0,171,171,
+76,70,83,48,120,0,0,0,2,0,0,0,8,0,0,0,
+104,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,2,0,0,0,
+255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,
+116,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+77,117,108,116,105,112,108,121,85,86,55,0,117,118,0,171,
+76,73,66,70,192,4,0,0,68,88,66,67,55,177,167,35,
+230,168,3,26,213,51,153,178,57,116,234,20,1,0,0,0,
+192,4,0,0,6,0,0,0,56,0,0,0,40,1,0,0,
+40,2,0,0,76,3,0,0,200,3,0,0,60,4,0,0,
+65,111,110,57,232,0,0,0,232,0,0,0,0,2,86,76,
+196,0,0,0,36,0,0,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,2,86,76,
+81,0,0,5,0,0,15,160,0,0,128,63,0,0,0,63,
+0,0,0,0,0,0,0,0,31,0,0,2,5,0,0,128,
+0,0,15,144,5,0,0,3,0,0,3,128,0,0,228,144,
+0,0,228,144,2,0,0,3,0,0,1,128,0,0,85,128,
+0,0,0,128,2,0,0,3,0,0,2,128,0,0,170,144,
+0,0,0,160,4,0,0,4,0,0,1,128,0,0,85,128,
+0,0,85,128,0,0,0,128,7,0,0,2,0,0,1,128,
+0,0,0,128,6,0,0,2,0,0,1,128,0,0,0,128,
+2,0,0,3,0,0,1,128,0,0,0,128,0,0,0,128,
+6,0,0,2,0,0,1,128,0,0,0,128,4,0,0,4,
+0,0,3,224,0,0,228,144,0,0,0,128,0,0,85,160,
+1,0,0,2,0,0,12,224,0,0,36,160,255,255,0,0,
+65,111,110,57,248,0,0,0,248,0,0,0,0,2,80,76,
+212,0,0,0,36,0,0,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,2,80,76,
+81,0,0,5,0,0,15,160,0,0,128,63,0,0,0,63,
+0,0,0,0,0,0,128,63,31,0,0,2,0,0,0,128,
+0,0,7,176,5,0,0,3,0,0,8,128,0,0,85,176,
+0,0,85,176,4,0,0,4,0,0,1,128,0,0,0,176,
+0,0,0,176,0,0,255,128,2,0,0,3,0,0,2,128,
+0,0,170,176,0,0,0,160,4,0,0,4,0,0,1,128,
+0,0,85,128,0,0,85,128,0,0,0,128,7,0,0,2,
+0,0,1,128,0,0,0,128,6,0,0,2,0,0,1,128,
+0,0,0,128,2,0,0,3,0,0,1,128,0,0,0,128,
+0,0,0,128,6,0,0,2,0,0,1,128,0,0,0,128,
+4,0,0,4,0,0,3,128,0,0,228,176,0,0,0,128,
+0,0,85,160,1,0,0,2,0,0,12,128,0,0,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,28,1,0,0,64,0,240,255,71,0,0,0,
+95,0,0,3,114,16,16,0,0,0,0,0,101,0,0,3,
+242,32,16,0,0,0,0,0,104,0,0,2,1,0,0,0,
+15,0,0,7,18,0,16,0,0,0,0,0,70,16,16,0,
+0,0,0,0,70,16,16,0,0,0,0,0,0,0,0,7,
+34,0,16,0,0,0,0,0,42,16,16,0,0,0,0,0,
+1,64,0,0,0,0,128,63,50,0,0,9,18,0,16,0,
+0,0,0,0,26,0,16,0,0,0,0,0,26,0,16,0,
+0,0,0,0,10,0,16,0,0,0,0,0,75,0,0,5,
+18,0,16,0,0,0,0,0,10,0,16,0,0,0,0,0,
+0,0,0,7,18,0,16,0,0,0,0,0,10,0,16,0,
+0,0,0,0,10,0,16,0,0,0,0,0,14,0,0,7,
+50,0,16,0,0,0,0,0,70,16,16,0,0,0,0,0,
+6,0,16,0,0,0,0,0,0,0,0,10,50,32,16,0,
+0,0,0,0,70,0,16,0,0,0,0,0,2,64,0,0,
+0,0,0,63,0,0,0,63,0,0,0,0,0,0,0,0,
+54,0,0,8,194,32,16,0,0,0,0,0,2,64,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,63,
+62,0,0,1,83,84,65,84,116,0,0,0,9,0,0,0,
+1,0,0,0,0,0,0,0,2,0,0,0,7,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,108,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+60,0,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,171,171,76,70,83,48,124,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,116,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,85,86,83,112,104,101,114,101,77,97,112,0,
+101,121,101,82,101,102,108,0,76,73,66,70,224,2,0,0,
+68,88,66,67,230,26,65,230,192,146,124,110,146,230,36,50,
+205,51,213,62,1,0,0,0,224,2,0,0,6,0,0,0,
+56,0,0,0,164,0,0,0,32,1,0,0,116,1,0,0,
+240,1,0,0,100,2,0,0,65,111,110,57,100,0,0,0,
+100,0,0,0,0,2,86,76,64,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,86,76,81,0,0,5,0,0,15,160,
+0,0,128,63,0,0,0,0,0,0,0,0,0,0,0,0,
+31,0,0,2,5,0,0,128,0,0,15,144,4,0,0,4,
+0,0,15,224,0,0,36,144,0,0,64,160,0,0,21,160,
+255,255,0,0,65,111,110,57,116,0,0,0,116,0,0,0,
+0,2,80,76,80,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,80,76,81,0,0,5,0,0,15,160,0,0,128,63,
+0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,2,
+0,0,0,128,0,0,7,176,1,0,0,2,0,0,7,128,
+0,0,228,176,1,0,0,2,0,0,8,128,0,0,0,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,76,0,0,0,64,0,240,255,19,0,0,0,
+95,0,0,3,114,16,16,0,0,0,0,0,101,0,0,3,
+242,32,16,0,0,0,0,0,54,0,0,5,114,32,16,0,
+0,0,0,0,70,18,16,0,0,0,0,0,54,0,0,5,
+130,32,16,0,0,0,0,0,1,64,0,0,0,0,128,63,
+62,0,0,1,83,84,65,84,116,0,0,0,3,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,108,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+60,0,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,171,171,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,114,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,70,108,111,97,116,51,116,111,52,0,118,0,
+76,73,66,70,192,3,0,0,68,88,66,67,70,116,161,59,
+42,125,161,227,92,25,246,147,196,134,44,94,1,0,0,0,
+192,3,0,0,6,0,0,0,56,0,0,0,132,0,0,0,
+208,0,0,0,24,1,0,0,148,1,0,0,108,3,0,0,
+65,111,110,57,68,0,0,0,68,0,0,0,0,2,86,76,
+20,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,1,0,0,0,
+1,0,0,0,0,0,0,0,0,2,86,76,1,0,0,2,
+0,0,15,224,0,0,228,160,255,255,0,0,65,111,110,57,
+68,0,0,0,68,0,0,0,0,2,80,76,20,0,0,0,
+48,0,0,0,1,0,36,0,0,0,48,0,0,0,48,0,
+0,0,36,0,0,0,48,0,1,0,0,0,1,0,0,0,
+0,0,0,0,0,2,80,76,1,0,0,2,0,0,15,224,
+0,0,228,160,255,255,0,0,83,72,68,82,64,0,0,0,
+64,0,240,255,16,0,0,0,89,0,0,4,70,142,32,0,
+1,0,0,0,1,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,54,0,0,6,242,32,16,0,0,0,0,0,
+70,142,32,0,1,0,0,0,0,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+208,1,0,0,1,0,0,0,108,0,0,0,1,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,160,1,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,
+85,110,105,116,121,70,70,80,105,120,101,108,0,171,171,171,
+92,0,0,0,3,0,0,0,132,0,0,0,160,0,0,0,
+0,0,0,0,0,0,0,0,252,0,0,0,0,0,0,0,
+128,0,0,0,2,0,0,0,20,1,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+56,1,0,0,128,0,0,0,4,0,0,0,0,0,0,0,
+76,1,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,112,1,0,0,144,0,0,0,
+16,0,0,0,0,0,0,0,124,1,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+102,102,95,118,101,99,95,99,111,108,111,114,115,0,102,108,
+111,97,116,52,0,171,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,1,0,0,102,102,95,97,
+108,112,104,97,95,114,101,102,0,102,108,111,97,116,0,171,
+0,0,3,0,1,0,1,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+69,1,0,0,102,102,95,102,111,103,95,112,115,0,171,171,
+1,0,3,0,1,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,1,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,171,171,76,70,83,48,76,0,0,0,1,0,0,0,
+8,0,0,0,56,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,76,111,97,100,67,111,110,115,116,97,110,116,
+67,111,108,111,114,48,0,171,76,73,66,70,192,3,0,0,
+68,88,66,67,20,238,20,114,172,178,29,247,21,3,60,240,
+206,249,78,81,1,0,0,0,192,3,0,0,6,0,0,0,
+56,0,0,0,132,0,0,0,208,0,0,0,24,1,0,0,
+148,1,0,0,108,3,0,0,65,111,110,57,68,0,0,0,
+68,0,0,0,0,2,86,76,20,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,1,0,1,0,1,0,0,0,0,0,0,0,
+0,2,86,76,1,0,0,2,0,0,15,224,0,0,228,160,
+255,255,0,0,65,111,110,57,68,0,0,0,68,0,0,0,
+0,2,80,76,20,0,0,0,48,0,0,0,1,0,36,0,
+0,0,48,0,0,0,48,0,0,0,36,0,0,0,48,0,
+1,0,1,0,1,0,0,0,0,0,0,0,0,2,80,76,
+1,0,0,2,0,0,15,224,0,0,228,160,255,255,0,0,
+83,72,68,82,64,0,0,0,64,0,240,255,16,0,0,0,
+89,0,0,4,70,142,32,0,1,0,0,0,2,0,0,0,
+101,0,0,3,242,32,16,0,0,0,0,0,54,0,0,6,
+242,32,16,0,0,0,0,0,70,142,32,0,1,0,0,0,
+1,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,208,1,0,0,1,0,0,0,
+108,0,0,0,1,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,160,1,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+1,0,0,0,0,0,0,0,85,110,105,116,121,70,70,80,
+105,120,101,108,0,171,171,171,92,0,0,0,3,0,0,0,
+132,0,0,0,160,0,0,0,0,0,0,0,0,0,0,0,
+252,0,0,0,0,0,0,0,128,0,0,0,2,0,0,0,
+20,1,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,56,1,0,0,128,0,0,0,
+4,0,0,0,0,0,0,0,76,1,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+112,1,0,0,144,0,0,0,16,0,0,0,0,0,0,0,
+124,1,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,102,102,95,118,101,99,95,99,
+111,108,111,114,115,0,102,108,111,97,116,52,0,171,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,1,0,0,102,102,95,97,108,112,104,97,95,114,101,102,
+0,102,108,111,97,116,0,171,0,0,3,0,1,0,1,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,69,1,0,0,102,102,95,102,
+111,103,95,112,115,0,171,171,1,0,3,0,1,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,1,0,0,77,105,99,114,
+111,115,111,102,116,32,40,82,41,32,72,76,83,76,32,83,
+104,97,100,101,114,32,67,111,109,112,105,108,101,114,32,54,
+46,51,46,57,52,49,53,46,48,0,171,171,76,70,83,48,
+76,0,0,0,1,0,0,0,8,0,0,0,56,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,2,0,0,0,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,76,111,97,100,
+67,111,110,115,116,97,110,116,67,111,108,111,114,49,0,171,
+76,73,66,70,192,3,0,0,68,88,66,67,116,104,252,128,
+151,194,73,196,15,52,199,114,96,200,114,242,1,0,0,0,
+192,3,0,0,6,0,0,0,56,0,0,0,132,0,0,0,
+208,0,0,0,24,1,0,0,148,1,0,0,108,3,0,0,
+65,111,110,57,68,0,0,0,68,0,0,0,0,2,86,76,
+20,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,1,0,2,0,
+1,0,0,0,0,0,0,0,0,2,86,76,1,0,0,2,
+0,0,15,224,0,0,228,160,255,255,0,0,65,111,110,57,
+68,0,0,0,68,0,0,0,0,2,80,76,20,0,0,0,
+48,0,0,0,1,0,36,0,0,0,48,0,0,0,48,0,
+0,0,36,0,0,0,48,0,1,0,2,0,1,0,0,0,
+0,0,0,0,0,2,80,76,1,0,0,2,0,0,15,224,
+0,0,228,160,255,255,0,0,83,72,68,82,64,0,0,0,
+64,0,240,255,16,0,0,0,89,0,0,4,70,142,32,0,
+1,0,0,0,3,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,54,0,0,6,242,32,16,0,0,0,0,0,
+70,142,32,0,1,0,0,0,2,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+208,1,0,0,1,0,0,0,108,0,0,0,1,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,160,1,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,
+85,110,105,116,121,70,70,80,105,120,101,108,0,171,171,171,
+92,0,0,0,3,0,0,0,132,0,0,0,160,0,0,0,
+0,0,0,0,0,0,0,0,252,0,0,0,0,0,0,0,
+128,0,0,0,2,0,0,0,20,1,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+56,1,0,0,128,0,0,0,4,0,0,0,0,0,0,0,
+76,1,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,112,1,0,0,144,0,0,0,
+16,0,0,0,0,0,0,0,124,1,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+102,102,95,118,101,99,95,99,111,108,111,114,115,0,102,108,
+111,97,116,52,0,171,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,1,0,0,102,102,95,97,
+108,112,104,97,95,114,101,102,0,102,108,111,97,116,0,171,
+0,0,3,0,1,0,1,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+69,1,0,0,102,102,95,102,111,103,95,112,115,0,171,171,
+1,0,3,0,1,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,1,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,171,171,76,70,83,48,76,0,0,0,1,0,0,0,
+8,0,0,0,56,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,76,111,97,100,67,111,110,115,116,97,110,116,
+67,111,108,111,114,50,0,171,76,73,66,70,192,3,0,0,
+68,88,66,67,131,41,35,159,159,146,237,240,170,111,184,2,
+39,243,88,214,1,0,0,0,192,3,0,0,6,0,0,0,
+56,0,0,0,132,0,0,0,208,0,0,0,24,1,0,0,
+148,1,0,0,108,3,0,0,65,111,110,57,68,0,0,0,
+68,0,0,0,0,2,86,76,20,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,1,0,3,0,1,0,0,0,0,0,0,0,
+0,2,86,76,1,0,0,2,0,0,15,224,0,0,228,160,
+255,255,0,0,65,111,110,57,68,0,0,0,68,0,0,0,
+0,2,80,76,20,0,0,0,48,0,0,0,1,0,36,0,
+0,0,48,0,0,0,48,0,0,0,36,0,0,0,48,0,
+1,0,3,0,1,0,0,0,0,0,0,0,0,2,80,76,
+1,0,0,2,0,0,15,224,0,0,228,160,255,255,0,0,
+83,72,68,82,64,0,0,0,64,0,240,255,16,0,0,0,
+89,0,0,4,70,142,32,0,1,0,0,0,4,0,0,0,
+101,0,0,3,242,32,16,0,0,0,0,0,54,0,0,6,
+242,32,16,0,0,0,0,0,70,142,32,0,1,0,0,0,
+3,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,208,1,0,0,1,0,0,0,
+108,0,0,0,1,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,160,1,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+1,0,0,0,0,0,0,0,85,110,105,116,121,70,70,80,
+105,120,101,108,0,171,171,171,92,0,0,0,3,0,0,0,
+132,0,0,0,160,0,0,0,0,0,0,0,0,0,0,0,
+252,0,0,0,0,0,0,0,128,0,0,0,2,0,0,0,
+20,1,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,56,1,0,0,128,0,0,0,
+4,0,0,0,0,0,0,0,76,1,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+112,1,0,0,144,0,0,0,16,0,0,0,0,0,0,0,
+124,1,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,102,102,95,118,101,99,95,99,
+111,108,111,114,115,0,102,108,111,97,116,52,0,171,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,1,0,0,102,102,95,97,108,112,104,97,95,114,101,102,
+0,102,108,111,97,116,0,171,0,0,3,0,1,0,1,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,69,1,0,0,102,102,95,102,
+111,103,95,112,115,0,171,171,1,0,3,0,1,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,1,0,0,77,105,99,114,
+111,115,111,102,116,32,40,82,41,32,72,76,83,76,32,83,
+104,97,100,101,114,32,67,111,109,112,105,108,101,114,32,54,
+46,51,46,57,52,49,53,46,48,0,171,171,76,70,83,48,
+76,0,0,0,1,0,0,0,8,0,0,0,56,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,2,0,0,0,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,76,111,97,100,
+67,111,110,115,116,97,110,116,67,111,108,111,114,51,0,171,
+76,73,66,70,192,3,0,0,68,88,66,67,3,40,62,77,
+224,140,92,152,89,140,38,56,207,39,135,89,1,0,0,0,
+192,3,0,0,6,0,0,0,56,0,0,0,132,0,0,0,
+208,0,0,0,24,1,0,0,148,1,0,0,108,3,0,0,
+65,111,110,57,68,0,0,0,68,0,0,0,0,2,86,76,
+20,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,1,0,4,0,
+1,0,0,0,0,0,0,0,0,2,86,76,1,0,0,2,
+0,0,15,224,0,0,228,160,255,255,0,0,65,111,110,57,
+68,0,0,0,68,0,0,0,0,2,80,76,20,0,0,0,
+48,0,0,0,1,0,36,0,0,0,48,0,0,0,48,0,
+0,0,36,0,0,0,48,0,1,0,4,0,1,0,0,0,
+0,0,0,0,0,2,80,76,1,0,0,2,0,0,15,224,
+0,0,228,160,255,255,0,0,83,72,68,82,64,0,0,0,
+64,0,240,255,16,0,0,0,89,0,0,4,70,142,32,0,
+1,0,0,0,5,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,54,0,0,6,242,32,16,0,0,0,0,0,
+70,142,32,0,1,0,0,0,4,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+208,1,0,0,1,0,0,0,108,0,0,0,1,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,160,1,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,
+85,110,105,116,121,70,70,80,105,120,101,108,0,171,171,171,
+92,0,0,0,3,0,0,0,132,0,0,0,160,0,0,0,
+0,0,0,0,0,0,0,0,252,0,0,0,0,0,0,0,
+128,0,0,0,2,0,0,0,20,1,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+56,1,0,0,128,0,0,0,4,0,0,0,0,0,0,0,
+76,1,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,112,1,0,0,144,0,0,0,
+16,0,0,0,0,0,0,0,124,1,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+102,102,95,118,101,99,95,99,111,108,111,114,115,0,102,108,
+111,97,116,52,0,171,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,1,0,0,102,102,95,97,
+108,112,104,97,95,114,101,102,0,102,108,111,97,116,0,171,
+0,0,3,0,1,0,1,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+69,1,0,0,102,102,95,102,111,103,95,112,115,0,171,171,
+1,0,3,0,1,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,1,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,171,171,76,70,83,48,76,0,0,0,1,0,0,0,
+8,0,0,0,56,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,76,111,97,100,67,111,110,115,116,97,110,116,
+67,111,108,111,114,52,0,171,76,73,66,70,192,3,0,0,
+68,88,66,67,247,173,93,73,196,111,229,44,163,237,132,230,
+106,105,144,109,1,0,0,0,192,3,0,0,6,0,0,0,
+56,0,0,0,132,0,0,0,208,0,0,0,24,1,0,0,
+148,1,0,0,108,3,0,0,65,111,110,57,68,0,0,0,
+68,0,0,0,0,2,86,76,20,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,1,0,5,0,1,0,0,0,0,0,0,0,
+0,2,86,76,1,0,0,2,0,0,15,224,0,0,228,160,
+255,255,0,0,65,111,110,57,68,0,0,0,68,0,0,0,
+0,2,80,76,20,0,0,0,48,0,0,0,1,0,36,0,
+0,0,48,0,0,0,48,0,0,0,36,0,0,0,48,0,
+1,0,5,0,1,0,0,0,0,0,0,0,0,2,80,76,
+1,0,0,2,0,0,15,224,0,0,228,160,255,255,0,0,
+83,72,68,82,64,0,0,0,64,0,240,255,16,0,0,0,
+89,0,0,4,70,142,32,0,1,0,0,0,6,0,0,0,
+101,0,0,3,242,32,16,0,0,0,0,0,54,0,0,6,
+242,32,16,0,0,0,0,0,70,142,32,0,1,0,0,0,
+5,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,208,1,0,0,1,0,0,0,
+108,0,0,0,1,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,160,1,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+1,0,0,0,0,0,0,0,85,110,105,116,121,70,70,80,
+105,120,101,108,0,171,171,171,92,0,0,0,3,0,0,0,
+132,0,0,0,160,0,0,0,0,0,0,0,0,0,0,0,
+252,0,0,0,0,0,0,0,128,0,0,0,2,0,0,0,
+20,1,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,56,1,0,0,128,0,0,0,
+4,0,0,0,0,0,0,0,76,1,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+112,1,0,0,144,0,0,0,16,0,0,0,0,0,0,0,
+124,1,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,102,102,95,118,101,99,95,99,
+111,108,111,114,115,0,102,108,111,97,116,52,0,171,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,1,0,0,102,102,95,97,108,112,104,97,95,114,101,102,
+0,102,108,111,97,116,0,171,0,0,3,0,1,0,1,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,69,1,0,0,102,102,95,102,
+111,103,95,112,115,0,171,171,1,0,3,0,1,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,1,0,0,77,105,99,114,
+111,115,111,102,116,32,40,82,41,32,72,76,83,76,32,83,
+104,97,100,101,114,32,67,111,109,112,105,108,101,114,32,54,
+46,51,46,57,52,49,53,46,48,0,171,171,76,70,83,48,
+76,0,0,0,1,0,0,0,8,0,0,0,56,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,2,0,0,0,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,76,111,97,100,
+67,111,110,115,116,97,110,116,67,111,108,111,114,53,0,171,
+76,73,66,70,192,3,0,0,68,88,66,67,48,33,196,39,
+86,158,25,201,60,227,193,209,75,77,76,7,1,0,0,0,
+192,3,0,0,6,0,0,0,56,0,0,0,132,0,0,0,
+208,0,0,0,24,1,0,0,148,1,0,0,108,3,0,0,
+65,111,110,57,68,0,0,0,68,0,0,0,0,2,86,76,
+20,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,1,0,6,0,
+1,0,0,0,0,0,0,0,0,2,86,76,1,0,0,2,
+0,0,15,224,0,0,228,160,255,255,0,0,65,111,110,57,
+68,0,0,0,68,0,0,0,0,2,80,76,20,0,0,0,
+48,0,0,0,1,0,36,0,0,0,48,0,0,0,48,0,
+0,0,36,0,0,0,48,0,1,0,6,0,1,0,0,0,
+0,0,0,0,0,2,80,76,1,0,0,2,0,0,15,224,
+0,0,228,160,255,255,0,0,83,72,68,82,64,0,0,0,
+64,0,240,255,16,0,0,0,89,0,0,4,70,142,32,0,
+1,0,0,0,7,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,54,0,0,6,242,32,16,0,0,0,0,0,
+70,142,32,0,1,0,0,0,6,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+208,1,0,0,1,0,0,0,108,0,0,0,1,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,160,1,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,
+85,110,105,116,121,70,70,80,105,120,101,108,0,171,171,171,
+92,0,0,0,3,0,0,0,132,0,0,0,160,0,0,0,
+0,0,0,0,0,0,0,0,252,0,0,0,0,0,0,0,
+128,0,0,0,2,0,0,0,20,1,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+56,1,0,0,128,0,0,0,4,0,0,0,0,0,0,0,
+76,1,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,112,1,0,0,144,0,0,0,
+16,0,0,0,0,0,0,0,124,1,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+102,102,95,118,101,99,95,99,111,108,111,114,115,0,102,108,
+111,97,116,52,0,171,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,1,0,0,102,102,95,97,
+108,112,104,97,95,114,101,102,0,102,108,111,97,116,0,171,
+0,0,3,0,1,0,1,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+69,1,0,0,102,102,95,102,111,103,95,112,115,0,171,171,
+1,0,3,0,1,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,1,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,171,171,76,70,83,48,76,0,0,0,1,0,0,0,
+8,0,0,0,56,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,76,111,97,100,67,111,110,115,116,97,110,116,
+67,111,108,111,114,54,0,171,76,73,66,70,192,3,0,0,
+68,88,66,67,169,198,138,5,205,98,94,205,122,79,240,230,
+184,15,46,76,1,0,0,0,192,3,0,0,6,0,0,0,
+56,0,0,0,132,0,0,0,208,0,0,0,24,1,0,0,
+148,1,0,0,108,3,0,0,65,111,110,57,68,0,0,0,
+68,0,0,0,0,2,86,76,20,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,1,0,7,0,1,0,0,0,0,0,0,0,
+0,2,86,76,1,0,0,2,0,0,15,224,0,0,228,160,
+255,255,0,0,65,111,110,57,68,0,0,0,68,0,0,0,
+0,2,80,76,20,0,0,0,48,0,0,0,1,0,36,0,
+0,0,48,0,0,0,48,0,0,0,36,0,0,0,48,0,
+1,0,7,0,1,0,0,0,0,0,0,0,0,2,80,76,
+1,0,0,2,0,0,15,224,0,0,228,160,255,255,0,0,
+83,72,68,82,64,0,0,0,64,0,240,255,16,0,0,0,
+89,0,0,4,70,142,32,0,1,0,0,0,8,0,0,0,
+101,0,0,3,242,32,16,0,0,0,0,0,54,0,0,6,
+242,32,16,0,0,0,0,0,70,142,32,0,1,0,0,0,
+7,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,208,1,0,0,1,0,0,0,
+108,0,0,0,1,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,160,1,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+1,0,0,0,0,0,0,0,85,110,105,116,121,70,70,80,
+105,120,101,108,0,171,171,171,92,0,0,0,3,0,0,0,
+132,0,0,0,160,0,0,0,0,0,0,0,0,0,0,0,
+252,0,0,0,0,0,0,0,128,0,0,0,2,0,0,0,
+20,1,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,56,1,0,0,128,0,0,0,
+4,0,0,0,0,0,0,0,76,1,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+112,1,0,0,144,0,0,0,16,0,0,0,0,0,0,0,
+124,1,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,102,102,95,118,101,99,95,99,
+111,108,111,114,115,0,102,108,111,97,116,52,0,171,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,1,0,0,102,102,95,97,108,112,104,97,95,114,101,102,
+0,102,108,111,97,116,0,171,0,0,3,0,1,0,1,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,69,1,0,0,102,102,95,102,
+111,103,95,112,115,0,171,171,1,0,3,0,1,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,1,0,0,77,105,99,114,
+111,115,111,102,116,32,40,82,41,32,72,76,83,76,32,83,
+104,97,100,101,114,32,67,111,109,112,105,108,101,114,32,54,
+46,51,46,57,52,49,53,46,48,0,171,171,76,70,83,48,
+76,0,0,0,1,0,0,0,8,0,0,0,56,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,2,0,0,0,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,76,111,97,100,
+67,111,110,115,116,97,110,116,67,111,108,111,114,55,0,171,
+76,73,66,70,204,2,0,0,68,88,66,67,45,108,242,17,
+41,178,132,75,103,221,15,167,11,128,240,127,1,0,0,0,
+204,2,0,0,6,0,0,0,56,0,0,0,160,0,0,0,
+20,1,0,0,96,1,0,0,220,1,0,0,80,2,0,0,
+65,111,110,57,96,0,0,0,96,0,0,0,0,2,86,76,
+60,0,0,0,36,0,0,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,2,86,76,
+81,0,0,5,0,0,15,160,0,0,128,63,0,0,0,0,
+0,0,0,0,0,0,0,0,31,0,0,2,5,0,0,128,
+0,0,15,144,2,0,0,3,0,0,1,224,0,0,0,145,
+0,0,0,160,255,255,0,0,65,111,110,57,108,0,0,0,
+108,0,0,0,0,2,80,76,72,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,80,76,81,0,0,5,0,0,15,160,
+0,0,128,63,0,0,0,0,0,0,0,0,0,0,0,0,
+31,0,0,2,0,0,0,128,0,0,1,176,2,0,0,3,
+0,0,8,128,0,0,0,177,0,0,0,160,1,0,0,2,
+0,0,1,224,0,0,255,128,255,255,0,0,83,72,68,82,
+68,0,0,0,64,0,240,255,17,0,0,0,95,0,0,3,
+18,16,16,0,0,0,0,0,101,0,0,3,18,32,16,0,
+0,0,0,0,0,0,0,8,18,32,16,0,0,0,0,0,
+10,16,16,128,65,0,0,0,0,0,0,0,1,64,0,0,
+0,0,128,63,62,0,0,1,83,84,65,84,116,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,108,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,60,0,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,77,105,99,114,111,115,111,102,
+116,32,40,82,41,32,72,76,83,76,32,83,104,97,100,101,
+114,32,67,111,109,112,105,108,101,114,32,54,46,51,46,57,
+52,49,53,46,48,0,171,171,76,70,83,48,116,0,0,0,
+2,0,0,0,8,0,0,0,104,0,0,0,0,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,114,0,0,0,0,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,79,110,101,77,105,110,117,115,
+49,0,118,0,76,73,66,70,216,2,0,0,68,88,66,67,
+158,168,1,106,99,49,31,82,200,140,198,11,9,75,78,147,
+1,0,0,0,216,2,0,0,6,0,0,0,56,0,0,0,
+160,0,0,0,20,1,0,0,108,1,0,0,232,1,0,0,
+92,2,0,0,65,111,110,57,96,0,0,0,96,0,0,0,
+0,2,86,76,60,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,86,76,81,0,0,5,0,0,15,160,0,0,128,63,
+0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,2,
+5,0,0,128,0,0,15,144,2,0,0,3,0,0,7,224,
+0,0,228,145,0,0,0,160,255,255,0,0,65,111,110,57,
+108,0,0,0,108,0,0,0,0,2,80,76,72,0,0,0,
+36,0,0,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,2,80,76,81,0,0,5,
+0,0,15,160,0,0,128,63,0,0,0,0,0,0,0,0,
+0,0,0,0,31,0,0,2,0,0,0,128,0,0,7,176,
+2,0,0,3,0,0,7,128,0,0,228,177,0,0,0,160,
+1,0,0,2,0,0,7,224,0,0,228,128,255,255,0,0,
+83,72,68,82,80,0,0,0,64,0,240,255,20,0,0,0,
+95,0,0,3,114,16,16,0,0,0,0,0,101,0,0,3,
+114,32,16,0,0,0,0,0,0,0,0,11,114,32,16,0,
+0,0,0,0,70,18,16,128,65,0,0,0,0,0,0,0,
+2,64,0,0,0,0,128,63,0,0,128,63,0,0,128,63,
+0,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,108,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,60,0,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,77,105,99,114,111,115,111,102,
+116,32,40,82,41,32,72,76,83,76,32,83,104,97,100,101,
+114,32,67,111,109,112,105,108,101,114,32,54,46,51,46,57,
+52,49,53,46,48,0,171,171,76,70,83,48,116,0,0,0,
+2,0,0,0,8,0,0,0,104,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,114,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,79,110,101,77,105,110,117,115,
+51,0,118,0,76,73,66,70,216,2,0,0,68,88,66,67,
+11,213,169,220,3,17,184,207,196,164,42,102,227,86,202,46,
+1,0,0,0,216,2,0,0,6,0,0,0,56,0,0,0,
+160,0,0,0,20,1,0,0,108,1,0,0,232,1,0,0,
+92,2,0,0,65,111,110,57,96,0,0,0,96,0,0,0,
+0,2,86,76,60,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,86,76,81,0,0,5,0,0,15,160,0,0,128,63,
+0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,2,
+5,0,0,128,0,0,15,144,2,0,0,3,0,0,15,224,
+0,0,228,145,0,0,0,160,255,255,0,0,65,111,110,57,
+108,0,0,0,108,0,0,0,0,2,80,76,72,0,0,0,
+36,0,0,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,2,80,76,81,0,0,5,
+0,0,15,160,0,0,128,63,0,0,0,0,0,0,0,0,
+0,0,0,0,31,0,0,2,0,0,0,128,0,0,15,176,
+2,0,0,3,0,0,15,128,0,0,228,177,0,0,0,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,80,0,0,0,64,0,240,255,20,0,0,0,
+95,0,0,3,242,16,16,0,0,0,0,0,101,0,0,3,
+242,32,16,0,0,0,0,0,0,0,0,11,242,32,16,0,
+0,0,0,0,70,30,16,128,65,0,0,0,0,0,0,0,
+2,64,0,0,0,0,128,63,0,0,128,63,0,0,128,63,
+0,0,128,63,62,0,0,1,83,84,65,84,116,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,108,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,60,0,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,77,105,99,114,111,115,111,102,
+116,32,40,82,41,32,72,76,83,76,32,83,104,97,100,101,
+114,32,67,111,109,112,105,108,101,114,32,54,46,51,46,57,
+52,49,53,46,48,0,171,171,76,70,83,48,116,0,0,0,
+2,0,0,0,8,0,0,0,104,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,114,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,79,110,101,77,105,110,117,115,
+52,0,118,0,76,73,66,70,128,2,0,0,68,88,66,67,
+187,71,126,136,198,179,185,17,98,92,175,72,62,254,64,135,
+1,0,0,0,128,2,0,0,6,0,0,0,56,0,0,0,
+132,0,0,0,208,0,0,0,16,1,0,0,140,1,0,0,
+0,2,0,0,65,111,110,57,68,0,0,0,68,0,0,0,
+0,2,86,76,32,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,86,76,31,0,0,2,5,0,0,128,0,0,15,144,
+1,0,0,2,0,0,15,224,0,0,228,144,255,255,0,0,
+65,111,110,57,68,0,0,0,68,0,0,0,0,2,80,76,
+32,0,0,0,36,0,0,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,2,80,76,
+31,0,0,2,0,0,0,128,0,0,15,176,1,0,0,2,
+0,0,15,224,0,0,228,176,255,255,0,0,83,72,68,82,
+56,0,0,0,64,0,240,255,14,0,0,0,95,0,0,3,
+242,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,54,0,0,5,242,32,16,0,0,0,0,0,
+70,30,16,0,0,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,108,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,60,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,77,105,99,114,
+111,115,111,102,116,32,40,82,41,32,72,76,83,76,32,83,
+104,97,100,101,114,32,67,111,109,112,105,108,101,114,32,54,
+46,51,46,57,52,49,53,46,48,0,171,171,76,70,83,48,
+120,0,0,0,2,0,0,0,8,0,0,0,104,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,2,0,0,0,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,116,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,67,111,109,98,
+82,101,112,108,97,99,101,0,97,0,171,171,76,73,66,70,
+12,3,0,0,68,88,66,67,149,143,87,109,142,38,148,229,
+142,231,17,129,220,71,27,94,1,0,0,0,12,3,0,0,
+6,0,0,0,56,0,0,0,160,0,0,0,20,1,0,0,
+104,1,0,0,228,1,0,0,88,2,0,0,65,111,110,57,
+96,0,0,0,96,0,0,0,0,2,86,76,60,0,0,0,
+36,0,0,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,2,86,76,31,0,0,2,
+5,0,0,128,0,0,15,144,31,0,0,2,5,0,1,128,
+1,0,15,144,1,0,0,2,0,0,15,128,0,0,228,144,
+5,0,0,3,0,0,15,224,0,0,228,128,1,0,228,144,
+255,255,0,0,65,111,110,57,108,0,0,0,108,0,0,0,
+0,2,80,76,72,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,80,76,31,0,0,2,0,0,0,128,0,0,15,176,
+31,0,0,2,0,0,0,128,1,0,15,176,1,0,0,2,
+0,0,15,128,0,0,228,176,5,0,0,3,0,0,15,128,
+0,0,228,128,1,0,228,176,1,0,0,2,0,0,15,224,
+0,0,228,128,255,255,0,0,83,72,68,82,76,0,0,0,
+64,0,240,255,19,0,0,0,95,0,0,3,242,16,16,0,
+0,0,0,0,95,0,0,3,242,16,16,0,1,0,0,0,
+101,0,0,3,242,32,16,0,0,0,0,0,56,0,0,7,
+242,32,16,0,0,0,0,0,70,30,16,0,0,0,0,0,
+70,30,16,0,1,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,108,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,60,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,77,105,99,114,
+111,115,111,102,116,32,40,82,41,32,72,76,83,76,32,83,
+104,97,100,101,114,32,67,111,109,112,105,108,101,114,32,54,
+46,51,46,57,52,49,53,46,48,0,171,171,76,70,83,48,
+172,0,0,0,3,0,0,0,8,0,0,0,152,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,2,0,0,0,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,165,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,167,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,67,111,109,98,
+77,111,100,117,108,97,116,101,0,97,0,98,0,171,171,171,
+76,73,66,70,4,3,0,0,68,88,66,67,230,76,60,128,
+196,190,237,250,58,85,119,85,43,12,167,22,1,0,0,0,
+4,3,0,0,6,0,0,0,56,0,0,0,160,0,0,0,
+20,1,0,0,104,1,0,0,228,1,0,0,88,2,0,0,
+65,111,110,57,96,0,0,0,96,0,0,0,0,2,86,76,
+60,0,0,0,36,0,0,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,2,86,76,
+31,0,0,2,5,0,0,128,0,0,15,144,31,0,0,2,
+5,0,1,128,1,0,15,144,1,0,0,2,0,0,15,128,
+0,0,228,144,2,0,0,3,0,0,15,224,0,0,228,128,
+1,0,228,144,255,255,0,0,65,111,110,57,108,0,0,0,
+108,0,0,0,0,2,80,76,72,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,15,176,31,0,0,2,0,0,0,128,1,0,15,176,
+1,0,0,2,0,0,15,128,0,0,228,176,2,0,0,3,
+0,0,15,128,0,0,228,128,1,0,228,176,1,0,0,2,
+0,0,15,224,0,0,228,128,255,255,0,0,83,72,68,82,
+76,0,0,0,64,0,240,255,19,0,0,0,95,0,0,3,
+242,16,16,0,0,0,0,0,95,0,0,3,242,16,16,0,
+1,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+0,0,0,7,242,32,16,0,0,0,0,0,70,30,16,0,
+0,0,0,0,70,30,16,0,1,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,60,0,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+77,105,99,114,111,115,111,102,116,32,40,82,41,32,72,76,
+83,76,32,83,104,97,100,101,114,32,67,111,109,112,105,108,
+101,114,32,54,46,51,46,57,52,49,53,46,48,0,171,171,
+76,70,83,48,164,0,0,0,3,0,0,0,8,0,0,0,
+152,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,2,0,0,0,
+255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,
+160,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+162,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+1,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+67,111,109,98,65,100,100,0,97,0,98,0,76,73,66,70,
+140,3,0,0,68,88,66,67,152,199,141,230,40,126,172,158,
+226,182,226,112,150,175,84,205,1,0,0,0,140,3,0,0,
+6,0,0,0,56,0,0,0,200,0,0,0,100,1,0,0,
+232,1,0,0,100,2,0,0,216,2,0,0,65,111,110,57,
+136,0,0,0,136,0,0,0,0,2,86,76,100,0,0,0,
+36,0,0,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,2,86,76,81,0,0,5,
+0,0,15,160,0,0,0,191,0,0,0,0,0,0,0,0,
+0,0,0,0,31,0,0,2,5,0,0,128,0,0,15,144,
+31,0,0,2,5,0,1,128,1,0,15,144,1,0,0,2,
+0,0,15,128,0,0,228,144,2,0,0,3,0,0,15,128,
+0,0,228,128,1,0,228,144,2,0,0,3,0,0,15,224,
+0,0,228,128,0,0,0,160,255,255,0,0,65,111,110,57,
+148,0,0,0,148,0,0,0,0,2,80,76,112,0,0,0,
+36,0,0,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,2,80,76,81,0,0,5,
+0,0,15,160,0,0,0,191,0,0,0,0,0,0,0,0,
+0,0,0,0,31,0,0,2,0,0,0,128,0,0,15,176,
+31,0,0,2,0,0,0,128,1,0,15,176,1,0,0,2,
+0,0,15,128,0,0,228,176,2,0,0,3,0,0,15,128,
+0,0,228,128,1,0,228,176,2,0,0,3,0,0,15,128,
+0,0,228,128,0,0,0,160,1,0,0,2,0,0,15,224,
+0,0,228,128,255,255,0,0,83,72,68,82,124,0,0,0,
+64,0,240,255,31,0,0,0,95,0,0,3,242,16,16,0,
+0,0,0,0,95,0,0,3,242,16,16,0,1,0,0,0,
+101,0,0,3,242,32,16,0,0,0,0,0,104,0,0,2,
+1,0,0,0,0,0,0,7,242,0,16,0,0,0,0,0,
+70,30,16,0,0,0,0,0,70,30,16,0,1,0,0,0,
+0,0,0,10,242,32,16,0,0,0,0,0,70,14,16,0,
+0,0,0,0,2,64,0,0,0,0,0,191,0,0,0,191,
+0,0,0,191,0,0,0,191,62,0,0,1,83,84,65,84,
+116,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,
+3,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,108,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,60,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,77,105,99,114,
+111,115,111,102,116,32,40,82,41,32,72,76,83,76,32,83,
+104,97,100,101,114,32,67,111,109,112,105,108,101,114,32,54,
+46,51,46,57,52,49,53,46,48,0,171,171,76,70,83,48,
+172,0,0,0,3,0,0,0,8,0,0,0,152,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,2,0,0,0,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,166,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,168,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,67,111,109,98,
+65,100,100,83,105,103,110,101,100,0,97,0,98,0,171,171,
+76,73,66,70,16,3,0,0,68,88,66,67,183,114,174,20,
+132,250,51,162,45,140,19,76,71,64,111,25,1,0,0,0,
+16,3,0,0,6,0,0,0,56,0,0,0,160,0,0,0,
+20,1,0,0,108,1,0,0,232,1,0,0,92,2,0,0,
+65,111,110,57,96,0,0,0,96,0,0,0,0,2,86,76,
+60,0,0,0,36,0,0,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,2,86,76,
+31,0,0,2,5,0,0,128,0,0,15,144,31,0,0,2,
+5,0,1,128,1,0,15,144,1,0,0,2,0,0,15,128,
+0,0,228,144,2,0,0,3,0,0,15,224,0,0,228,128,
+1,0,228,145,255,255,0,0,65,111,110,57,108,0,0,0,
+108,0,0,0,0,2,80,76,72,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,15,176,31,0,0,2,0,0,0,128,1,0,15,176,
+1,0,0,2,0,0,15,128,0,0,228,176,2,0,0,3,
+0,0,15,128,0,0,228,128,1,0,228,177,1,0,0,2,
+0,0,15,224,0,0,228,128,255,255,0,0,83,72,68,82,
+80,0,0,0,64,0,240,255,20,0,0,0,95,0,0,3,
+242,16,16,0,0,0,0,0,95,0,0,3,242,16,16,0,
+1,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+0,0,0,8,242,32,16,0,0,0,0,0,70,30,16,0,
+0,0,0,0,70,30,16,128,65,0,0,0,1,0,0,0,
+62,0,0,1,83,84,65,84,116,0,0,0,2,0,0,0,
+0,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,108,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+60,0,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,171,171,76,70,83,48,172,0,0,0,3,0,0,0,
+8,0,0,0,152,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,165,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,167,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,1,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,67,111,109,98,83,117,98,116,114,97,99,116,
+0,97,0,98,0,171,171,171,76,73,66,70,180,3,0,0,
+68,88,66,67,152,163,17,37,211,107,50,197,55,83,131,3,
+147,212,251,180,1,0,0,0,180,3,0,0,6,0,0,0,
+56,0,0,0,192,0,0,0,84,1,0,0,228,1,0,0,
+96,2,0,0,212,2,0,0,65,111,110,57,128,0,0,0,
+128,0,0,0,0,2,86,76,92,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,86,76,31,0,0,2,5,0,0,128,
+0,0,15,144,31,0,0,2,5,0,1,128,1,0,15,144,
+31,0,0,2,5,0,2,128,2,0,15,144,1,0,0,2,
+0,0,15,128,1,0,228,144,2,0,0,3,1,0,15,128,
+0,0,228,129,0,0,228,144,4,0,0,4,0,0,15,224,
+2,0,255,144,1,0,228,128,0,0,228,128,255,255,0,0,
+65,111,110,57,140,0,0,0,140,0,0,0,0,2,80,76,
+104,0,0,0,36,0,0,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,2,80,76,
+31,0,0,2,0,0,0,128,0,0,15,176,31,0,0,2,
+0,0,0,128,1,0,15,176,31,0,0,2,0,0,0,128,
+2,0,8,176,1,0,0,2,0,0,15,128,1,0,228,176,
+2,0,0,3,1,0,15,128,0,0,228,129,0,0,228,176,
+4,0,0,4,0,0,15,128,2,0,255,176,1,0,228,128,
+0,0,228,128,1,0,0,2,0,0,15,224,0,0,228,128,
+255,255,0,0,83,72,68,82,136,0,0,0,64,0,240,255,
+34,0,0,0,95,0,0,3,242,16,16,0,0,0,0,0,
+95,0,0,3,242,16,16,0,1,0,0,0,95,0,0,3,
+130,16,16,0,2,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,104,0,0,2,1,0,0,0,0,0,0,8,
+242,0,16,0,0,0,0,0,70,30,16,0,0,0,0,0,
+70,30,16,128,65,0,0,0,1,0,0,0,50,0,0,9,
+242,32,16,0,0,0,0,0,246,31,16,0,2,0,0,0,
+70,14,16,0,0,0,0,0,70,30,16,0,1,0,0,0,
+62,0,0,1,83,84,65,84,116,0,0,0,3,0,0,0,
+1,0,0,0,0,0,0,0,4,0,0,0,2,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,108,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+60,0,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,171,171,76,70,83,48,216,0,0,0,4,0,0,0,
+8,0,0,0,200,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,209,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,211,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,1,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,213,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,2,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,67,111,109,98,76,101,114,112,0,97,0,98,
+0,99,0,171,76,73,66,70,44,4,0,0,68,88,66,67,
+51,90,121,247,48,76,173,77,104,101,2,120,144,179,245,77,
+1,0,0,0,44,4,0,0,6,0,0,0,56,0,0,0,
+232,0,0,0,164,1,0,0,140,2,0,0,8,3,0,0,
+124,3,0,0,65,111,110,57,168,0,0,0,168,0,0,0,
+0,2,86,76,132,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,86,76,81,0,0,5,0,0,15,160,0,0,0,191,
+0,0,128,64,0,0,0,0,0,0,0,0,31,0,0,2,
+5,0,0,128,0,0,15,144,31,0,0,2,5,0,1,128,
+1,0,15,144,2,0,0,3,0,0,7,128,0,0,228,144,
+0,0,0,160,2,0,0,3,1,0,7,128,1,0,228,144,
+0,0,0,160,8,0,0,3,0,0,1,128,0,0,228,128,
+1,0,228,128,5,0,0,3,0,0,7,224,0,0,0,128,
+0,0,85,160,1,0,0,2,0,0,8,224,0,0,255,144,
+255,255,0,0,65,111,110,57,180,0,0,0,180,0,0,0,
+0,2,80,76,144,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,80,76,81,0,0,5,0,0,15,160,0,0,0,191,
+0,0,128,64,0,0,0,0,0,0,0,0,31,0,0,2,
+0,0,0,128,0,0,15,176,31,0,0,2,0,0,0,128,
+1,0,7,176,2,0,0,3,0,0,7,128,0,0,228,176,
+0,0,0,160,2,0,0,3,1,0,7,128,1,0,228,176,
+0,0,0,160,8,0,0,3,0,0,1,128,0,0,228,128,
+1,0,228,128,5,0,0,3,0,0,7,128,0,0,0,128,
+0,0,85,160,1,0,0,2,0,0,8,128,0,0,255,176,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,224,0,0,0,64,0,240,255,56,0,0,0,
+95,0,0,3,242,16,16,0,0,0,0,0,95,0,0,3,
+114,16,16,0,1,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,104,0,0,2,2,0,0,0,0,0,0,10,
+114,0,16,0,0,0,0,0,70,18,16,0,0,0,0,0,
+2,64,0,0,0,0,0,191,0,0,0,191,0,0,0,191,
+0,0,0,0,0,0,0,10,114,0,16,0,1,0,0,0,
+70,18,16,0,1,0,0,0,2,64,0,0,0,0,0,191,
+0,0,0,191,0,0,0,191,0,0,0,0,16,0,0,7,
+18,0,16,0,0,0,0,0,70,2,16,0,0,0,0,0,
+70,2,16,0,1,0,0,0,56,0,0,10,114,32,16,0,
+0,0,0,0,6,0,16,0,0,0,0,0,2,64,0,0,
+0,0,128,64,0,0,128,64,0,0,128,64,0,0,0,0,
+54,0,0,5,130,32,16,0,0,0,0,0,58,16,16,0,
+0,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+6,0,0,0,2,0,0,0,0,0,0,0,3,0,0,0,
+4,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,108,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,60,0,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,77,105,99,114,111,115,111,102,
+116,32,40,82,41,32,72,76,83,76,32,83,104,97,100,101,
+114,32,67,111,109,112,105,108,101,114,32,54,46,51,46,57,
+52,49,53,46,48,0,171,171,76,70,83,48,168,0,0,0,
+3,0,0,0,8,0,0,0,152,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,161,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,163,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,67,111,109,98,68,111,116,51,
+0,97,0,98,0,171,171,171,76,73,66,70,4,4,0,0,
+68,88,66,67,136,183,202,100,86,249,89,61,211,252,59,81,
+134,90,81,47,1,0,0,0,4,4,0,0,6,0,0,0,
+56,0,0,0,220,0,0,0,140,1,0,0,96,2,0,0,
+220,2,0,0,80,3,0,0,65,111,110,57,156,0,0,0,
+156,0,0,0,0,2,86,76,120,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,86,76,81,0,0,5,0,0,15,160,
+0,0,0,191,0,0,128,64,0,0,0,0,0,0,0,0,
+31,0,0,2,5,0,0,128,0,0,15,144,31,0,0,2,
+5,0,1,128,1,0,15,144,2,0,0,3,0,0,7,128,
+0,0,228,144,0,0,0,160,2,0,0,3,1,0,7,128,
+1,0,228,144,0,0,0,160,8,0,0,3,0,0,1,128,
+0,0,228,128,1,0,228,128,5,0,0,3,0,0,15,224,
+0,0,0,128,0,0,85,160,255,255,0,0,65,111,110,57,
+168,0,0,0,168,0,0,0,0,2,80,76,132,0,0,0,
+36,0,0,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,2,80,76,81,0,0,5,
+0,0,15,160,0,0,0,191,0,0,128,64,0,0,0,0,
+0,0,0,0,31,0,0,2,0,0,0,128,0,0,7,176,
+31,0,0,2,0,0,0,128,1,0,7,176,2,0,0,3,
+0,0,7,128,0,0,228,176,0,0,0,160,2,0,0,3,
+1,0,7,128,1,0,228,176,0,0,0,160,8,0,0,3,
+0,0,1,128,0,0,228,128,1,0,228,128,5,0,0,3,
+0,0,15,128,0,0,0,128,0,0,85,160,1,0,0,2,
+0,0,15,224,0,0,228,128,255,255,0,0,83,72,68,82,
+204,0,0,0,64,0,240,255,51,0,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,95,0,0,3,114,16,16,0,
+1,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+104,0,0,2,2,0,0,0,0,0,0,10,114,0,16,0,
+0,0,0,0,70,18,16,0,0,0,0,0,2,64,0,0,
+0,0,0,191,0,0,0,191,0,0,0,191,0,0,0,0,
+0,0,0,10,114,0,16,0,1,0,0,0,70,18,16,0,
+1,0,0,0,2,64,0,0,0,0,0,191,0,0,0,191,
+0,0,0,191,0,0,0,0,16,0,0,7,18,0,16,0,
+0,0,0,0,70,2,16,0,0,0,0,0,70,2,16,0,
+1,0,0,0,56,0,0,10,242,32,16,0,0,0,0,0,
+6,0,16,0,0,0,0,0,2,64,0,0,0,0,128,64,
+0,0,128,64,0,0,128,64,0,0,128,64,62,0,0,1,
+83,84,65,84,116,0,0,0,5,0,0,0,2,0,0,0,
+0,0,0,0,3,0,0,0,4,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,60,0,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+77,105,99,114,111,115,111,102,116,32,40,82,41,32,72,76,
+83,76,32,83,104,97,100,101,114,32,67,111,109,112,105,108,
+101,114,32,54,46,51,46,57,52,49,53,46,48,0,171,171,
+76,70,83,48,172,0,0,0,3,0,0,0,8,0,0,0,
+152,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,2,0,0,0,
+255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,
+165,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+167,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+1,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+67,111,109,98,68,111,116,51,114,103,98,97,0,97,0,98,
+0,171,171,171,76,73,66,70,136,3,0,0,68,88,66,67,
+31,76,227,248,96,59,115,47,100,193,168,174,45,34,12,208,
+1,0,0,0,136,3,0,0,6,0,0,0,56,0,0,0,
+188,0,0,0,76,1,0,0,180,1,0,0,48,2,0,0,
+164,2,0,0,65,111,110,57,124,0,0,0,124,0,0,0,
+0,2,86,76,88,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,86,76,31,0,0,2,5,0,0,128,0,0,15,144,
+31,0,0,2,5,0,1,128,1,0,15,144,31,0,0,2,
+5,0,2,128,2,0,15,144,1,0,0,2,0,0,15,128,
+0,0,228,144,1,0,0,2,1,0,8,128,2,0,255,144,
+4,0,0,4,0,0,15,224,0,0,228,128,1,0,255,128,
+1,0,228,144,255,255,0,0,65,111,110,57,136,0,0,0,
+136,0,0,0,0,2,80,76,100,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,15,176,31,0,0,2,0,0,0,128,1,0,15,176,
+31,0,0,2,0,0,0,128,2,0,8,176,1,0,0,2,
+0,0,15,128,0,0,228,176,1,0,0,2,1,0,8,128,
+2,0,255,176,4,0,0,4,0,0,15,128,0,0,228,128,
+1,0,255,128,1,0,228,176,1,0,0,2,0,0,15,224,
+0,0,228,128,255,255,0,0,83,72,68,82,96,0,0,0,
+64,0,240,255,24,0,0,0,95,0,0,3,242,16,16,0,
+0,0,0,0,95,0,0,3,242,16,16,0,1,0,0,0,
+95,0,0,3,130,16,16,0,2,0,0,0,101,0,0,3,
+242,32,16,0,0,0,0,0,50,0,0,9,242,32,16,0,
+0,0,0,0,70,30,16,0,0,0,0,0,246,31,16,0,
+2,0,0,0,70,30,16,0,1,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,4,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,60,0,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+77,105,99,114,111,115,111,102,116,32,40,82,41,32,72,76,
+83,76,32,83,104,97,100,101,114,32,67,111,109,112,105,108,
+101,114,32,54,46,51,46,57,52,49,53,46,48,0,171,171,
+76,70,83,48,220,0,0,0,4,0,0,0,8,0,0,0,
+200,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,2,0,0,0,
+255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,
+211,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+213,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+1,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+215,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+2,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+67,111,109,98,77,117,108,65,100,100,0,97,0,98,0,99,
+0,171,171,171,76,73,66,70,140,3,0,0,68,88,66,67,
+127,205,158,183,165,163,232,169,189,8,89,19,233,185,188,242,
+1,0,0,0,140,3,0,0,6,0,0,0,56,0,0,0,
+188,0,0,0,76,1,0,0,184,1,0,0,52,2,0,0,
+168,2,0,0,65,111,110,57,124,0,0,0,124,0,0,0,
+0,2,86,76,88,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,86,76,31,0,0,2,5,0,0,128,0,0,15,144,
+31,0,0,2,5,0,1,128,1,0,15,144,31,0,0,2,
+5,0,2,128,2,0,15,144,1,0,0,2,0,0,15,128,
+0,0,228,144,1,0,0,2,1,0,8,128,2,0,255,144,
+4,0,0,4,0,0,15,224,0,0,228,128,1,0,255,128,
+1,0,228,145,255,255,0,0,65,111,110,57,136,0,0,0,
+136,0,0,0,0,2,80,76,100,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,15,176,31,0,0,2,0,0,0,128,1,0,15,176,
+31,0,0,2,0,0,0,128,2,0,8,176,1,0,0,2,
+0,0,15,128,0,0,228,176,1,0,0,2,1,0,8,128,
+2,0,255,176,4,0,0,4,0,0,15,128,0,0,228,128,
+1,0,255,128,1,0,228,177,1,0,0,2,0,0,15,224,
+0,0,228,128,255,255,0,0,83,72,68,82,100,0,0,0,
+64,0,240,255,25,0,0,0,95,0,0,3,242,16,16,0,
+0,0,0,0,95,0,0,3,242,16,16,0,1,0,0,0,
+95,0,0,3,130,16,16,0,2,0,0,0,101,0,0,3,
+242,32,16,0,0,0,0,0,50,0,0,10,242,32,16,0,
+0,0,0,0,70,30,16,0,0,0,0,0,246,31,16,0,
+2,0,0,0,70,30,16,128,65,0,0,0,1,0,0,0,
+62,0,0,1,83,84,65,84,116,0,0,0,2,0,0,0,
+0,0,0,0,0,0,0,0,4,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,108,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+60,0,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,171,171,76,70,83,48,220,0,0,0,4,0,0,0,
+8,0,0,0,200,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,211,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,213,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,1,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,215,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,2,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,67,111,109,98,77,117,108,83,117,98,0,97,
+0,98,0,99,0,171,171,171,76,73,66,70,12,4,0,0,
+68,88,66,67,83,111,20,102,197,156,233,182,54,7,106,223,
+34,245,237,46,1,0,0,0,12,4,0,0,6,0,0,0,
+56,0,0,0,228,0,0,0,156,1,0,0,52,2,0,0,
+176,2,0,0,36,3,0,0,65,111,110,57,164,0,0,0,
+164,0,0,0,0,2,86,76,128,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,86,76,81,0,0,5,0,0,15,160,
+0,0,0,191,0,0,0,0,0,0,0,0,0,0,0,0,
+31,0,0,2,5,0,0,128,0,0,15,144,31,0,0,2,
+5,0,1,128,1,0,15,144,31,0,0,2,5,0,2,128,
+2,0,15,144,1,0,0,2,0,0,15,128,0,0,228,144,
+1,0,0,2,1,0,8,128,2,0,255,144,4,0,0,4,
+0,0,15,128,0,0,228,128,1,0,255,128,1,0,228,144,
+2,0,0,3,0,0,15,224,0,0,228,128,0,0,0,160,
+255,255,0,0,65,111,110,57,176,0,0,0,176,0,0,0,
+0,2,80,76,140,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,80,76,81,0,0,5,0,0,15,160,0,0,0,191,
+0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,2,
+0,0,0,128,0,0,15,176,31,0,0,2,0,0,0,128,
+1,0,15,176,31,0,0,2,0,0,0,128,2,0,8,176,
+1,0,0,2,0,0,15,128,0,0,228,176,1,0,0,2,
+1,0,8,128,2,0,255,176,4,0,0,4,0,0,15,128,
+0,0,228,128,1,0,255,128,1,0,228,176,2,0,0,3,
+0,0,15,128,0,0,228,128,0,0,0,160,1,0,0,2,
+0,0,15,224,0,0,228,128,255,255,0,0,83,72,68,82,
+144,0,0,0,64,0,240,255,36,0,0,0,95,0,0,3,
+242,16,16,0,0,0,0,0,95,0,0,3,242,16,16,0,
+1,0,0,0,95,0,0,3,130,16,16,0,2,0,0,0,
+101,0,0,3,242,32,16,0,0,0,0,0,104,0,0,2,
+1,0,0,0,50,0,0,9,242,0,16,0,0,0,0,0,
+70,30,16,0,0,0,0,0,246,31,16,0,2,0,0,0,
+70,30,16,0,1,0,0,0,0,0,0,10,242,32,16,0,
+0,0,0,0,70,14,16,0,0,0,0,0,2,64,0,0,
+0,0,0,191,0,0,0,191,0,0,0,191,0,0,0,191,
+62,0,0,1,83,84,65,84,116,0,0,0,3,0,0,0,
+1,0,0,0,0,0,0,0,4,0,0,0,2,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,108,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+60,0,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,171,171,76,70,83,48,224,0,0,0,4,0,0,0,
+8,0,0,0,200,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,217,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,219,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,1,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,221,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,2,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,67,111,109,98,77,117,108,65,100,100,83,105,
+103,110,101,100,0,97,0,98,0,99,0,171,76,73,66,70,
+152,2,0,0,68,88,66,67,104,50,51,52,155,142,16,144,
+69,96,27,4,5,193,22,178,1,0,0,0,152,2,0,0,
+6,0,0,0,56,0,0,0,136,0,0,0,228,0,0,0,
+44,1,0,0,168,1,0,0,28,2,0,0,65,111,110,57,
+72,0,0,0,72,0,0,0,0,2,86,76,36,0,0,0,
+36,0,0,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,2,86,76,31,0,0,2,
+5,0,0,128,0,0,15,144,2,0,0,3,0,0,15,224,
+0,0,228,144,0,0,228,144,255,255,0,0,65,111,110,57,
+84,0,0,0,84,0,0,0,0,2,80,76,48,0,0,0,
+36,0,0,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,2,80,76,31,0,0,2,
+0,0,0,128,0,0,15,176,2,0,0,3,0,0,15,128,
+0,0,228,176,0,0,228,176,1,0,0,2,0,0,15,224,
+0,0,228,128,255,255,0,0,83,72,68,82,64,0,0,0,
+64,0,240,255,16,0,0,0,95,0,0,3,242,16,16,0,
+0,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+0,0,0,7,242,32,16,0,0,0,0,0,70,30,16,0,
+0,0,0,0,70,30,16,0,0,0,0,0,62,0,0,1,
+83,84,65,84,116,0,0,0,2,0,0,0,0,0,0,0,
+0,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,82,68,69,70,
+108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+60,0,0,0,0,4,70,76,0,129,0,0,60,0,0,0,
+82,68,49,49,60,0,0,0,24,0,0,0,32,0,0,0,
+40,0,0,0,36,0,0,0,12,0,0,0,0,0,0,0,
+77,105,99,114,111,115,111,102,116,32,40,82,41,32,72,76,
+83,76,32,83,104,97,100,101,114,32,67,111,109,112,105,108,
+101,114,32,54,46,51,46,57,52,49,53,46,48,0,171,171,
+76,70,83,48,116,0,0,0,2,0,0,0,8,0,0,0,
+104,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,2,0,0,0,
+255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,
+111,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+83,99,97,108,101,50,0,97,0,171,171,171,76,73,66,70,
+212,2,0,0,68,88,66,67,57,34,29,67,178,80,106,40,
+94,22,39,177,107,192,81,162,1,0,0,0,212,2,0,0,
+6,0,0,0,56,0,0,0,160,0,0,0,20,1,0,0,
+104,1,0,0,228,1,0,0,88,2,0,0,65,111,110,57,
+96,0,0,0,96,0,0,0,0,2,86,76,60,0,0,0,
+36,0,0,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,2,86,76,81,0,0,5,
+0,0,15,160,0,0,128,64,0,0,0,0,0,0,0,0,
+0,0,0,0,31,0,0,2,5,0,0,128,0,0,15,144,
+5,0,0,3,0,0,15,224,0,0,228,144,0,0,0,160,
+255,255,0,0,65,111,110,57,108,0,0,0,108,0,0,0,
+0,2,80,76,72,0,0,0,36,0,0,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,2,80,76,81,0,0,5,0,0,15,160,0,0,128,64,
+0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,2,
+0,0,0,128,0,0,15,176,5,0,0,3,0,0,15,128,
+0,0,228,176,0,0,0,160,1,0,0,2,0,0,15,224,
+0,0,228,128,255,255,0,0,83,72,68,82,76,0,0,0,
+64,0,240,255,19,0,0,0,95,0,0,3,242,16,16,0,
+0,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+56,0,0,10,242,32,16,0,0,0,0,0,70,30,16,0,
+0,0,0,0,2,64,0,0,0,0,128,64,0,0,128,64,
+0,0,128,64,0,0,128,64,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,108,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,60,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,77,105,99,114,
+111,115,111,102,116,32,40,82,41,32,72,76,83,76,32,83,
+104,97,100,101,114,32,67,111,109,112,105,108,101,114,32,54,
+46,51,46,57,52,49,53,46,48,0,171,171,76,70,83,48,
+116,0,0,0,2,0,0,0,8,0,0,0,104,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,2,0,0,0,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,111,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,83,99,97,108,
+101,52,0,97,0,171,171,171,76,73,66,70,56,3,0,0,
+68,88,66,67,20,232,184,35,253,143,9,50,109,2,17,57,
+26,201,43,39,1,0,0,0,56,3,0,0,6,0,0,0,
+56,0,0,0,172,0,0,0,44,1,0,0,148,1,0,0,
+16,2,0,0,132,2,0,0,65,111,110,57,108,0,0,0,
+108,0,0,0,0,2,86,76,72,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,86,76,31,0,0,2,5,0,0,128,
+0,0,15,144,31,0,0,2,5,0,1,128,1,0,15,144,
+1,0,0,2,0,0,7,128,0,0,228,144,2,0,0,3,
+0,0,7,224,0,0,228,128,1,0,228,144,1,0,0,2,
+0,0,8,224,0,0,255,144,255,255,0,0,65,111,110,57,
+120,0,0,0,120,0,0,0,0,2,80,76,84,0,0,0,
+36,0,0,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,2,80,76,31,0,0,2,
+0,0,0,128,0,0,15,176,31,0,0,2,0,0,0,128,
+1,0,7,176,1,0,0,2,0,0,7,128,0,0,228,176,
+2,0,0,3,0,0,7,128,0,0,228,128,1,0,228,176,
+1,0,0,2,0,0,8,128,0,0,255,176,1,0,0,2,
+0,0,15,224,0,0,228,128,255,255,0,0,83,72,68,82,
+96,0,0,0,64,0,240,255,24,0,0,0,95,0,0,3,
+242,16,16,0,0,0,0,0,95,0,0,3,114,16,16,0,
+1,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+0,0,0,7,114,32,16,0,0,0,0,0,70,18,16,0,
+0,0,0,0,70,18,16,0,1,0,0,0,54,0,0,5,
+130,32,16,0,0,0,0,0,58,16,16,0,0,0,0,0,
+62,0,0,1,83,84,65,84,116,0,0,0,3,0,0,0,
+0,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,108,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+60,0,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,171,171,76,70,83,48,172,0,0,0,3,0,0,0,
+8,0,0,0,152,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,160,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,164,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,1,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,65,100,100,83,112,101,99,0,99,111,108,0,
+115,112,101,99,0,171,171,171,76,73,66,70,16,3,0,0,
+68,88,66,67,32,99,27,168,13,179,154,91,100,39,27,10,
+175,105,117,37,1,0,0,0,16,3,0,0,6,0,0,0,
+56,0,0,0,156,0,0,0,12,1,0,0,108,1,0,0,
+232,1,0,0,92,2,0,0,65,111,110,57,92,0,0,0,
+92,0,0,0,0,2,86,76,56,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,86,76,31,0,0,2,5,0,0,128,
+0,0,15,144,31,0,0,2,5,0,1,128,1,0,15,144,
+1,0,0,2,0,0,7,224,0,0,228,144,1,0,0,2,
+0,0,8,224,1,0,255,144,255,255,0,0,65,111,110,57,
+104,0,0,0,104,0,0,0,0,2,80,76,68,0,0,0,
+36,0,0,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,0,36,0,0,2,80,76,31,0,0,2,
+0,0,0,128,0,0,7,176,31,0,0,2,0,0,0,128,
+1,0,8,176,1,0,0,2,0,0,7,128,0,0,228,176,
+1,0,0,2,0,0,8,128,1,0,255,176,1,0,0,2,
+0,0,15,224,0,0,228,128,255,255,0,0,83,72,68,82,
+88,0,0,0,64,0,240,255,22,0,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,95,0,0,3,130,16,16,0,
+1,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+54,0,0,5,114,32,16,0,0,0,0,0,70,18,16,0,
+0,0,0,0,54,0,0,5,130,32,16,0,0,0,0,0,
+58,16,16,0,1,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,108,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,60,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,77,105,99,114,
+111,115,111,102,116,32,40,82,41,32,72,76,83,76,32,83,
+104,97,100,101,114,32,67,111,109,112,105,108,101,114,32,54,
+46,51,46,57,52,49,53,46,48,0,171,171,76,70,83,48,
+172,0,0,0,3,0,0,0,8,0,0,0,152,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,2,0,0,0,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,165,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,167,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,67,111,109,98,
+105,110,101,65,108,112,104,97,0,99,0,97,0,171,171,171,
+76,73,66,70,240,7,0,0,68,88,66,67,140,46,150,255,
+109,51,190,32,65,61,106,69,213,176,193,187,1,0,0,0,
+240,7,0,0,6,0,0,0,56,0,0,0,248,0,0,0,
+140,1,0,0,44,2,0,0,168,2,0,0,108,7,0,0,
+65,111,110,57,184,0,0,0,184,0,0,0,0,2,86,76,
+136,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,0,0,62,0,
+1,0,0,0,0,0,0,0,0,2,86,76,81,0,0,5,
+1,0,15,160,0,0,0,0,0,0,128,63,0,0,0,0,
+0,0,0,0,31,0,0,2,5,0,0,128,0,0,15,144,
+8,0,0,3,0,0,1,128,0,0,228,144,0,0,228,144,
+7,0,0,2,0,0,1,128,0,0,0,128,6,0,0,2,
+0,0,1,128,0,0,0,128,4,0,0,4,0,0,1,128,
+0,0,0,128,0,0,170,160,0,0,255,160,11,0,0,3,
+0,0,1,128,0,0,0,128,1,0,0,160,10,0,0,3,
+0,0,1,224,0,0,0,128,1,0,85,160,255,255,0,0,
+65,111,110,57,140,0,0,0,140,0,0,0,0,2,80,76,
+92,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,0,0,62,0,
+1,0,0,0,0,0,0,0,0,2,80,76,31,0,0,2,
+0,0,0,128,0,0,7,176,8,0,0,3,0,0,8,128,
+0,0,228,176,0,0,228,176,7,0,0,2,0,0,1,128,
+0,0,255,128,6,0,0,2,0,0,1,128,0,0,0,128,
+4,0,0,4,0,0,17,128,0,0,0,128,0,0,170,160,
+0,0,255,160,1,0,0,2,0,0,1,224,0,0,0,128,
+255,255,0,0,83,72,68,82,152,0,0,0,64,0,240,255,
+38,0,0,0,89,0,0,4,70,142,32,0,0,0,0,0,
+63,0,0,0,95,0,0,3,114,16,16,0,0,0,0,0,
+101,0,0,3,18,32,16,0,0,0,0,0,104,0,0,2,
+1,0,0,0,16,0,0,7,18,0,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,70,18,16,0,0,0,0,0,
+75,0,0,5,18,0,16,0,0,0,0,0,10,0,16,0,
+0,0,0,0,50,32,0,11,18,32,16,0,0,0,0,0,
+10,0,16,0,0,0,0,0,42,128,32,0,0,0,0,0,
+62,0,0,0,58,128,32,0,0,0,0,0,62,0,0,0,
+62,0,0,1,83,84,65,84,116,0,0,0,4,0,0,0,
+1,0,0,0,0,0,0,0,2,0,0,0,3,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,188,4,0,0,1,0,0,0,108,0,0,0,
+1,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+142,4,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,92,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,85,110,105,116,121,70,70,86,101,114,116,101,
+120,0,171,171,92,0,0,0,14,0,0,0,132,0,0,0,
+240,3,0,0,0,0,0,0,0,0,0,0,180,2,0,0,
+0,0,0,0,64,0,0,0,0,0,0,0,204,2,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,240,2,0,0,64,0,0,0,64,0,0,0,
+0,0,0,0,204,2,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,253,2,0,0,
+128,0,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,56,3,0,0,144,0,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,71,3,0,0,
+160,0,0,0,128,0,0,0,0,0,0,0,88,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,124,3,0,0,32,1,0,0,128,0,0,0,
+0,0,0,0,140,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,176,3,0,0,
+160,1,0,0,128,0,0,0,0,0,0,0,192,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,228,3,0,0,32,2,0,0,128,0,0,0,
+0,0,0,0,244,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,24,4,0,0,
+160,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,39,4,0,0,176,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,54,4,0,0,
+192,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,66,4,0,0,208,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,82,4,0,0,
+224,2,0,0,0,1,0,0,0,0,0,0,96,4,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,132,4,0,0,224,3,0,0,16,0,0,0,
+2,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,102,102,95,109,
+97,116,114,105,120,95,109,118,112,0,102,108,111,97,116,52,
+120,52,0,171,3,0,3,0,4,0,4,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,194,2,0,0,102,102,95,109,97,116,114,105,
+120,95,109,118,0,102,102,95,118,101,99,95,99,111,108,111,
+114,0,102,108,111,97,116,52,0,171,171,171,1,0,3,0,
+1,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,118,101,99,95,97,109,98,105,101,110,116,0,102,
+102,95,108,105,103,104,116,95,99,111,108,111,114,0,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,112,111,115,
+0,171,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,97,116,116,101,110,0,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,115,112,111,116,0,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,109,97,116,95,100,105,102,102,117,115,101,0,102,
+102,95,109,97,116,95,97,109,98,105,101,110,116,0,102,102,
+95,109,97,116,95,115,112,101,99,0,102,102,95,109,97,116,
+95,101,109,105,115,115,105,111,110,0,102,102,95,109,97,116,
+114,105,120,95,116,101,120,0,3,0,3,0,4,0,4,0,
+4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,194,2,0,0,102,102,95,102,
+111,103,95,118,115,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,124,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,114,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,70,111,103,76,105,110,101,97,114,0,101,121,
+101,80,111,115,0,171,171,171,76,73,66,70,20,8,0,0,
+68,88,66,67,214,169,140,84,231,146,163,76,182,11,101,69,
+234,130,85,253,1,0,0,0,20,8,0,0,6,0,0,0,
+56,0,0,0,240,0,0,0,140,1,0,0,84,2,0,0,
+208,2,0,0,148,7,0,0,65,111,110,57,176,0,0,0,
+176,0,0,0,0,2,86,76,128,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,0,0,62,0,1,0,0,0,0,0,0,0,
+0,2,86,76,81,0,0,5,1,0,15,160,0,0,128,63,
+0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,2,
+5,0,0,128,0,0,15,144,8,0,0,3,0,0,1,128,
+0,0,228,144,0,0,228,144,7,0,0,2,0,0,1,128,
+0,0,0,128,6,0,0,2,0,0,1,128,0,0,0,128,
+5,0,0,3,0,0,1,128,0,0,0,128,0,0,85,160,
+14,0,0,2,0,0,1,128,0,0,0,129,10,0,0,3,
+0,0,1,224,0,0,0,128,1,0,0,160,255,255,0,0,
+65,111,110,57,148,0,0,0,148,0,0,0,0,2,80,76,
+100,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,0,0,62,0,
+1,0,0,0,0,0,0,0,0,2,80,76,31,0,0,2,
+0,0,0,128,0,0,7,176,8,0,0,3,0,0,8,128,
+0,0,228,176,0,0,228,176,7,0,0,2,0,0,1,128,
+0,0,255,128,6,0,0,2,0,0,1,128,0,0,0,128,
+5,0,0,3,0,0,1,128,0,0,0,128,0,0,85,160,
+14,0,0,2,0,0,17,128,0,0,0,129,1,0,0,2,
+0,0,1,224,0,0,0,128,255,255,0,0,83,72,68,82,
+192,0,0,0,64,0,240,255,48,0,0,0,89,0,0,4,
+70,142,32,0,0,0,0,0,63,0,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,101,0,0,3,18,32,16,0,
+0,0,0,0,104,0,0,2,1,0,0,0,16,0,0,7,
+18,0,16,0,0,0,0,0,70,18,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,75,0,0,5,18,0,16,0,
+0,0,0,0,10,0,16,0,0,0,0,0,56,0,0,8,
+18,0,16,0,0,0,0,0,10,0,16,0,0,0,0,0,
+26,128,32,0,0,0,0,0,62,0,0,0,25,0,0,6,
+18,0,16,0,0,0,0,0,10,0,16,128,65,0,0,0,
+0,0,0,0,51,0,0,7,18,32,16,0,0,0,0,0,
+10,0,16,0,0,0,0,0,1,64,0,0,0,0,128,63,
+62,0,0,1,83,84,65,84,116,0,0,0,6,0,0,0,
+1,0,0,0,0,0,0,0,2,0,0,0,5,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,188,4,0,0,1,0,0,0,108,0,0,0,
+1,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+142,4,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,92,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,85,110,105,116,121,70,70,86,101,114,116,101,
+120,0,171,171,92,0,0,0,14,0,0,0,132,0,0,0,
+240,3,0,0,0,0,0,0,0,0,0,0,180,2,0,0,
+0,0,0,0,64,0,0,0,0,0,0,0,204,2,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,240,2,0,0,64,0,0,0,64,0,0,0,
+0,0,0,0,204,2,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,253,2,0,0,
+128,0,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,56,3,0,0,144,0,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,71,3,0,0,
+160,0,0,0,128,0,0,0,0,0,0,0,88,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,124,3,0,0,32,1,0,0,128,0,0,0,
+0,0,0,0,140,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,176,3,0,0,
+160,1,0,0,128,0,0,0,0,0,0,0,192,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,228,3,0,0,32,2,0,0,128,0,0,0,
+0,0,0,0,244,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,24,4,0,0,
+160,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,39,4,0,0,176,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,54,4,0,0,
+192,2,0,0,16,0,0,0,0,0,0,0,20,3,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,66,4,0,0,208,2,0,0,16,0,0,0,
+0,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,82,4,0,0,
+224,2,0,0,0,1,0,0,0,0,0,0,96,4,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,132,4,0,0,224,3,0,0,16,0,0,0,
+2,0,0,0,20,3,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,102,102,95,109,
+97,116,114,105,120,95,109,118,112,0,102,108,111,97,116,52,
+120,52,0,171,3,0,3,0,4,0,4,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,194,2,0,0,102,102,95,109,97,116,114,105,
+120,95,109,118,0,102,102,95,118,101,99,95,99,111,108,111,
+114,0,102,108,111,97,116,52,0,171,171,171,1,0,3,0,
+1,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,118,101,99,95,97,109,98,105,101,110,116,0,102,
+102,95,108,105,103,104,116,95,99,111,108,111,114,0,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,108,105,103,104,116,95,112,111,115,
+0,171,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,97,116,116,101,110,0,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,115,112,111,116,0,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,109,97,116,95,100,105,102,102,117,115,101,0,102,
+102,95,109,97,116,95,97,109,98,105,101,110,116,0,102,102,
+95,109,97,116,95,115,112,101,99,0,102,102,95,109,97,116,
+95,101,109,105,115,115,105,111,110,0,102,102,95,109,97,116,
+114,105,120,95,116,101,120,0,3,0,3,0,4,0,4,0,
+4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,194,2,0,0,102,102,95,102,
+111,103,95,118,115,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,120,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,111,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,70,111,103,69,120,112,0,101,121,101,80,111,
+115,0,171,171,76,73,66,70,12,8,0,0,68,88,66,67,
+220,44,237,65,218,78,106,179,241,49,18,96,157,132,44,139,
+1,0,0,0,12,8,0,0,6,0,0,0,56,0,0,0,
+216,0,0,0,132,1,0,0,76,2,0,0,200,2,0,0,
+140,7,0,0,65,111,110,57,152,0,0,0,152,0,0,0,
+0,2,86,76,104,0,0,0,48,0,0,0,1,0,36,0,
+0,0,48,0,0,0,48,0,0,0,36,0,0,0,48,0,
+0,0,62,0,1,0,0,0,0,0,0,0,0,2,86,76,
+31,0,0,2,5,0,0,128,0,0,15,144,8,0,0,3,
+0,0,1,128,0,0,228,144,0,0,228,144,7,0,0,2,
+0,0,1,128,0,0,0,128,6,0,0,2,0,0,1,128,
+0,0,0,128,5,0,0,3,0,0,1,128,0,0,0,128,
+0,0,85,160,5,0,0,3,0,0,1,128,0,0,0,128,
+0,0,0,129,14,0,0,2,0,0,1,224,0,0,0,128,
+255,255,0,0,65,111,110,57,164,0,0,0,164,0,0,0,
+0,2,80,76,116,0,0,0,48,0,0,0,1,0,36,0,
+0,0,48,0,0,0,48,0,0,0,36,0,0,0,48,0,
+0,0,62,0,1,0,0,0,0,0,0,0,0,2,80,76,
+31,0,0,2,0,0,0,128,0,0,7,176,8,0,0,3,
+0,0,8,128,0,0,228,176,0,0,228,176,7,0,0,2,
+0,0,1,128,0,0,255,128,6,0,0,2,0,0,1,128,
+0,0,0,128,5,0,0,3,0,0,1,128,0,0,0,128,
+0,0,85,160,5,0,0,3,0,0,1,128,0,0,0,128,
+0,0,0,129,14,0,0,2,0,0,1,128,0,0,0,128,
+1,0,0,2,0,0,1,224,0,0,0,128,255,255,0,0,
+83,72,68,82,192,0,0,0,64,0,240,255,48,0,0,0,
+89,0,0,4,70,142,32,0,0,0,0,0,63,0,0,0,
+95,0,0,3,114,16,16,0,0,0,0,0,101,0,0,3,
+18,32,16,0,0,0,0,0,104,0,0,2,1,0,0,0,
+16,0,0,7,18,0,16,0,0,0,0,0,70,18,16,0,
+0,0,0,0,70,18,16,0,0,0,0,0,75,0,0,5,
+18,0,16,0,0,0,0,0,10,0,16,0,0,0,0,0,
+56,0,0,8,18,0,16,0,0,0,0,0,10,0,16,0,
+0,0,0,0,26,128,32,0,0,0,0,0,62,0,0,0,
+56,0,0,8,18,0,16,0,0,0,0,0,10,0,16,0,
+0,0,0,0,10,0,16,128,65,0,0,0,0,0,0,0,
+25,0,0,5,18,32,16,0,0,0,0,0,10,0,16,0,
+0,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+6,0,0,0,1,0,0,0,0,0,0,0,2,0,0,0,
+5,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,188,4,0,0,1,0,0,0,
+108,0,0,0,1,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,142,4,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,85,110,105,116,121,70,70,86,
+101,114,116,101,120,0,171,171,92,0,0,0,14,0,0,0,
+132,0,0,0,240,3,0,0,0,0,0,0,0,0,0,0,
+180,2,0,0,0,0,0,0,64,0,0,0,0,0,0,0,
+204,2,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,240,2,0,0,64,0,0,0,
+64,0,0,0,0,0,0,0,204,2,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+253,2,0,0,128,0,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,56,3,0,0,144,0,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+71,3,0,0,160,0,0,0,128,0,0,0,0,0,0,0,
+88,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,124,3,0,0,32,1,0,0,
+128,0,0,0,0,0,0,0,140,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+176,3,0,0,160,1,0,0,128,0,0,0,0,0,0,0,
+192,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,228,3,0,0,32,2,0,0,
+128,0,0,0,0,0,0,0,244,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+24,4,0,0,160,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,39,4,0,0,176,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+54,4,0,0,192,2,0,0,16,0,0,0,0,0,0,0,
+20,3,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,66,4,0,0,208,2,0,0,
+16,0,0,0,0,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+82,4,0,0,224,2,0,0,0,1,0,0,0,0,0,0,
+96,4,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,132,4,0,0,224,3,0,0,
+16,0,0,0,2,0,0,0,20,3,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+102,102,95,109,97,116,114,105,120,95,109,118,112,0,102,108,
+111,97,116,52,120,52,0,171,3,0,3,0,4,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,194,2,0,0,102,102,95,109,
+97,116,114,105,120,95,109,118,0,102,102,95,118,101,99,95,
+99,111,108,111,114,0,102,108,111,97,116,52,0,171,171,171,
+1,0,3,0,1,0,4,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,118,101,99,95,97,109,98,105,101,
+110,116,0,102,102,95,108,105,103,104,116,95,99,111,108,111,
+114,0,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,3,0,0,102,102,95,108,105,103,104,116,
+95,112,111,115,0,171,171,171,1,0,3,0,1,0,4,0,
+8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,3,0,0,102,102,95,108,
+105,103,104,116,95,97,116,116,101,110,0,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,3,0,0,
+102,102,95,108,105,103,104,116,95,115,112,111,116,0,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,3,0,0,102,102,95,109,97,116,95,100,105,102,102,117,
+115,101,0,102,102,95,109,97,116,95,97,109,98,105,101,110,
+116,0,102,102,95,109,97,116,95,115,112,101,99,0,102,102,
+95,109,97,116,95,101,109,105,115,115,105,111,110,0,102,102,
+95,109,97,116,114,105,120,95,116,101,120,0,3,0,3,0,
+4,0,4,0,4,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,194,2,0,0,
+102,102,95,102,111,103,95,118,115,0,77,105,99,114,111,115,
+111,102,116,32,40,82,41,32,72,76,83,76,32,83,104,97,
+100,101,114,32,67,111,109,112,105,108,101,114,32,54,46,51,
+46,57,52,49,53,46,48,0,76,70,83,48,120,0,0,0,
+2,0,0,0,8,0,0,0,104,0,0,0,0,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,112,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,70,111,103,69,120,112,50,0,
+101,121,101,80,111,115,0,171,76,73,66,70,12,5,0,0,
+68,88,66,67,43,165,60,118,238,80,194,137,137,145,19,237,
+8,42,67,215,1,0,0,0,12,5,0,0,6,0,0,0,
+56,0,0,0,192,0,0,0,84,1,0,0,4,2,0,0,
+128,2,0,0,88,4,0,0,65,111,110,57,128,0,0,0,
+128,0,0,0,0,2,86,76,80,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,1,0,9,0,1,0,0,0,0,0,0,0,
+0,2,86,76,31,0,0,2,5,0,0,128,0,0,15,144,
+31,0,0,2,5,0,1,128,1,0,15,144,2,0,0,3,
+0,0,7,128,0,0,228,144,0,0,228,161,4,0,0,4,
+0,0,7,224,1,0,0,144,0,0,228,128,0,0,228,160,
+1,0,0,2,0,0,8,224,0,0,255,144,255,255,0,0,
+65,111,110,57,140,0,0,0,140,0,0,0,0,2,80,76,
+92,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,1,0,9,0,
+1,0,0,0,0,0,0,0,0,2,80,76,31,0,0,2,
+0,0,0,128,0,0,15,176,31,0,0,2,0,0,0,128,
+1,0,1,176,2,0,0,3,0,0,7,128,0,0,228,176,
+0,0,228,161,4,0,0,4,0,0,7,128,1,0,0,176,
+0,0,228,128,0,0,228,160,1,0,0,2,0,0,8,128,
+0,0,255,176,1,0,0,2,0,0,15,224,0,0,228,128,
+255,255,0,0,83,72,68,82,168,0,0,0,64,0,240,255,
+42,0,0,0,89,0,0,4,70,142,32,0,1,0,0,0,
+10,0,0,0,95,0,0,3,242,16,16,0,0,0,0,0,
+95,0,0,3,18,16,16,0,1,0,0,0,101,0,0,3,
+242,32,16,0,0,0,0,0,104,0,0,2,1,0,0,0,
+0,0,0,9,114,0,16,0,0,0,0,0,70,18,16,0,
+0,0,0,0,70,130,32,128,65,0,0,0,1,0,0,0,
+9,0,0,0,50,0,0,10,114,32,16,0,0,0,0,0,
+6,16,16,0,1,0,0,0,70,2,16,0,0,0,0,0,
+70,130,32,0,1,0,0,0,9,0,0,0,54,0,0,5,
+130,32,16,0,0,0,0,0,58,16,16,0,0,0,0,0,
+62,0,0,1,83,84,65,84,116,0,0,0,4,0,0,0,
+1,0,0,0,0,0,0,0,3,0,0,0,2,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,208,1,0,0,1,0,0,0,108,0,0,0,
+1,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+160,1,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,92,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,85,110,105,116,121,70,70,80,105,120,101,108,
+0,171,171,171,92,0,0,0,3,0,0,0,132,0,0,0,
+160,0,0,0,0,0,0,0,0,0,0,0,252,0,0,0,
+0,0,0,0,128,0,0,0,0,0,0,0,20,1,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,56,1,0,0,128,0,0,0,4,0,0,0,
+0,0,0,0,76,1,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,112,1,0,0,
+144,0,0,0,16,0,0,0,2,0,0,0,124,1,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,102,102,95,118,101,99,95,99,111,108,111,114,
+115,0,102,108,111,97,116,52,0,171,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,1,0,0,
+102,102,95,97,108,112,104,97,95,114,101,102,0,102,108,111,
+97,116,0,171,0,0,3,0,1,0,1,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,69,1,0,0,102,102,95,102,111,103,95,112,
+115,0,171,171,1,0,3,0,1,0,4,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,1,0,0,77,105,99,114,111,115,111,102,
+116,32,40,82,41,32,72,76,83,76,32,83,104,97,100,101,
+114,32,67,111,109,112,105,108,101,114,32,54,46,51,46,57,
+52,49,53,46,48,0,171,171,76,70,83,48,172,0,0,0,
+3,0,0,0,8,0,0,0,152,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,161,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,165,0,0,0,0,0,0,0,
+3,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,65,112,112,108,121,70,111,103,
+0,99,111,108,0,105,102,111,103,0,171,171,76,73,66,70,
+120,2,0,0,68,88,66,67,228,129,43,236,118,165,218,53,
+151,25,162,98,135,202,141,31,1,0,0,0,120,2,0,0,
+5,0,0,0,52,0,0,0,184,0,0,0,4,1,0,0,
+128,1,0,0,244,1,0,0,65,111,110,57,124,0,0,0,
+124,0,0,0,0,2,80,76,88,0,0,0,36,0,0,0,
+0,0,36,0,0,0,36,0,0,0,36,0,0,0,36,0,
+0,0,36,0,0,2,80,76,81,0,0,5,0,0,15,160,
+0,0,128,191,0,0,0,0,0,0,0,0,0,0,0,0,
+31,0,0,2,0,0,0,128,0,0,15,176,1,0,0,2,
+0,0,15,128,0,0,0,160,65,0,0,1,0,0,15,128,
+1,0,0,2,0,0,15,128,0,0,228,176,1,0,0,2,
+0,0,15,224,0,0,228,128,255,255,0,0,83,72,68,82,
+68,0,0,0,64,0,240,255,17,0,0,0,95,0,0,3,
+242,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,13,0,4,3,1,64,0,0,255,255,255,255,
+54,0,0,5,242,32,16,0,0,0,0,0,70,30,16,0,
+0,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,108,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,60,0,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,77,105,99,114,111,115,111,102,
+116,32,40,82,41,32,72,76,83,76,32,83,104,97,100,101,
+114,32,67,111,109,112,105,108,101,114,32,54,46,51,46,57,
+52,49,53,46,48,0,171,171,76,70,83,48,124,0,0,0,
+2,0,0,0,8,0,0,0,104,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,119,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,65,108,112,104,97,84,101,115,
+116,78,101,118,101,114,0,99,111,108,0,171,76,73,66,70,
+56,4,0,0,68,88,66,67,217,165,212,19,118,106,154,93,
+60,2,230,192,32,38,222,58,1,0,0,0,56,4,0,0,
+5,0,0,0,52,0,0,0,220,0,0,0,96,1,0,0,
+220,1,0,0,180,3,0,0,65,111,110,57,160,0,0,0,
+160,0,0,0,0,2,80,76,112,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,1,0,8,0,1,0,0,0,0,0,0,0,
+0,2,80,76,81,0,0,5,1,0,15,160,0,0,128,191,
+0,0,0,128,0,0,0,0,0,0,0,0,31,0,0,2,
+0,0,0,128,0,0,15,176,2,0,0,3,0,0,8,128,
+0,0,255,176,0,0,0,161,88,0,0,4,0,0,15,128,
+0,0,255,128,1,0,0,160,1,0,85,160,65,0,0,1,
+0,0,15,128,1,0,0,2,0,0,15,128,0,0,228,176,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,124,0,0,0,64,0,240,255,31,0,0,0,
+89,0,0,4,70,142,32,0,1,0,0,0,9,0,0,0,
+95,0,0,3,242,16,16,0,0,0,0,0,101,0,0,3,
+242,32,16,0,0,0,0,0,104,0,0,2,1,0,0,0,
+29,0,0,8,18,0,16,0,0,0,0,0,58,16,16,0,
+0,0,0,0,10,128,32,0,1,0,0,0,8,0,0,0,
+13,0,4,3,10,0,16,0,0,0,0,0,54,0,0,5,
+242,32,16,0,0,0,0,0,70,30,16,0,0,0,0,0,
+62,0,0,1,83,84,65,84,116,0,0,0,4,0,0,0,
+1,0,0,0,0,0,0,0,2,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,208,1,0,0,1,0,0,0,108,0,0,0,
+1,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+160,1,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,92,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,85,110,105,116,121,70,70,80,105,120,101,108,
+0,171,171,171,92,0,0,0,3,0,0,0,132,0,0,0,
+160,0,0,0,0,0,0,0,0,0,0,0,252,0,0,0,
+0,0,0,0,128,0,0,0,0,0,0,0,20,1,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,56,1,0,0,128,0,0,0,4,0,0,0,
+2,0,0,0,76,1,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,112,1,0,0,
+144,0,0,0,16,0,0,0,0,0,0,0,124,1,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,102,102,95,118,101,99,95,99,111,108,111,114,
+115,0,102,108,111,97,116,52,0,171,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,1,0,0,
+102,102,95,97,108,112,104,97,95,114,101,102,0,102,108,111,
+97,116,0,171,0,0,3,0,1,0,1,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,69,1,0,0,102,102,95,102,111,103,95,112,
+115,0,171,171,1,0,3,0,1,0,4,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,1,0,0,77,105,99,114,111,115,111,102,
+116,32,40,82,41,32,72,76,83,76,32,83,104,97,100,101,
+114,32,67,111,109,112,105,108,101,114,32,54,46,51,46,57,
+52,49,53,46,48,0,171,171,76,70,83,48,124,0,0,0,
+2,0,0,0,8,0,0,0,104,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,118,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,65,108,112,104,97,84,101,115,
+116,76,101,115,115,0,99,111,108,0,171,171,76,73,66,70,
+72,4,0,0,68,88,66,67,9,212,183,148,40,9,204,128,
+14,203,169,16,22,156,51,83,1,0,0,0,72,4,0,0,
+5,0,0,0,52,0,0,0,236,0,0,0,112,1,0,0,
+236,1,0,0,196,3,0,0,65,111,110,57,176,0,0,0,
+176,0,0,0,0,2,80,76,128,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,1,0,8,0,1,0,0,0,0,0,0,0,
+0,2,80,76,81,0,0,5,1,0,15,160,0,0,0,128,
+0,0,128,191,0,0,0,0,0,0,0,0,31,0,0,2,
+0,0,0,128,0,0,15,176,2,0,0,3,0,0,8,128,
+0,0,255,176,0,0,0,161,5,0,0,3,0,0,1,128,
+0,0,255,128,0,0,255,128,88,0,0,4,0,0,15,128,
+0,0,0,129,1,0,0,160,1,0,85,160,65,0,0,1,
+0,0,15,128,1,0,0,2,0,0,15,128,0,0,228,176,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,124,0,0,0,64,0,240,255,31,0,0,0,
+89,0,0,4,70,142,32,0,1,0,0,0,9,0,0,0,
+95,0,0,3,242,16,16,0,0,0,0,0,101,0,0,3,
+242,32,16,0,0,0,0,0,104,0,0,2,1,0,0,0,
+57,0,0,8,18,0,16,0,0,0,0,0,58,16,16,0,
+0,0,0,0,10,128,32,0,1,0,0,0,8,0,0,0,
+13,0,4,3,10,0,16,0,0,0,0,0,54,0,0,5,
+242,32,16,0,0,0,0,0,70,30,16,0,0,0,0,0,
+62,0,0,1,83,84,65,84,116,0,0,0,4,0,0,0,
+1,0,0,0,0,0,0,0,2,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,208,1,0,0,1,0,0,0,108,0,0,0,
+1,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+160,1,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,92,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,85,110,105,116,121,70,70,80,105,120,101,108,
+0,171,171,171,92,0,0,0,3,0,0,0,132,0,0,0,
+160,0,0,0,0,0,0,0,0,0,0,0,252,0,0,0,
+0,0,0,0,128,0,0,0,0,0,0,0,20,1,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,56,1,0,0,128,0,0,0,4,0,0,0,
+2,0,0,0,76,1,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,112,1,0,0,
+144,0,0,0,16,0,0,0,0,0,0,0,124,1,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,102,102,95,118,101,99,95,99,111,108,111,114,
+115,0,102,108,111,97,116,52,0,171,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,1,0,0,
+102,102,95,97,108,112,104,97,95,114,101,102,0,102,108,111,
+97,116,0,171,0,0,3,0,1,0,1,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,69,1,0,0,102,102,95,102,111,103,95,112,
+115,0,171,171,1,0,3,0,1,0,4,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,1,0,0,77,105,99,114,111,115,111,102,
+116,32,40,82,41,32,72,76,83,76,32,83,104,97,100,101,
+114,32,67,111,109,112,105,108,101,114,32,54,46,51,46,57,
+52,49,53,46,48,0,171,171,76,70,83,48,124,0,0,0,
+2,0,0,0,8,0,0,0,104,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,119,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,65,108,112,104,97,84,101,115,
+116,69,113,117,97,108,0,99,111,108,0,171,76,73,66,70,
+56,4,0,0,68,88,66,67,49,14,159,37,228,78,150,41,
+132,60,204,137,11,50,70,216,1,0,0,0,56,4,0,0,
+5,0,0,0,52,0,0,0,220,0,0,0,96,1,0,0,
+220,1,0,0,180,3,0,0,65,111,110,57,160,0,0,0,
+160,0,0,0,0,2,80,76,112,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,1,0,8,0,1,0,0,0,0,0,0,0,
+0,2,80,76,81,0,0,5,1,0,15,160,0,0,0,128,
+0,0,128,191,0,0,0,0,0,0,0,0,31,0,0,2,
+0,0,0,128,0,0,15,176,2,0,0,3,0,0,8,128,
+0,0,255,177,0,0,0,160,88,0,0,4,0,0,15,128,
+0,0,255,128,1,0,0,160,1,0,85,160,65,0,0,1,
+0,0,15,128,1,0,0,2,0,0,15,128,0,0,228,176,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,124,0,0,0,64,0,240,255,31,0,0,0,
+89,0,0,4,70,142,32,0,1,0,0,0,9,0,0,0,
+95,0,0,3,242,16,16,0,0,0,0,0,101,0,0,3,
+242,32,16,0,0,0,0,0,104,0,0,2,1,0,0,0,
+49,0,0,8,18,0,16,0,0,0,0,0,10,128,32,0,
+1,0,0,0,8,0,0,0,58,16,16,0,0,0,0,0,
+13,0,4,3,10,0,16,0,0,0,0,0,54,0,0,5,
+242,32,16,0,0,0,0,0,70,30,16,0,0,0,0,0,
+62,0,0,1,83,84,65,84,116,0,0,0,4,0,0,0,
+1,0,0,0,0,0,0,0,2,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,208,1,0,0,1,0,0,0,108,0,0,0,
+1,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+160,1,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,92,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,85,110,105,116,121,70,70,80,105,120,101,108,
+0,171,171,171,92,0,0,0,3,0,0,0,132,0,0,0,
+160,0,0,0,0,0,0,0,0,0,0,0,252,0,0,0,
+0,0,0,0,128,0,0,0,0,0,0,0,20,1,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,56,1,0,0,128,0,0,0,4,0,0,0,
+2,0,0,0,76,1,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,112,1,0,0,
+144,0,0,0,16,0,0,0,0,0,0,0,124,1,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,102,102,95,118,101,99,95,99,111,108,111,114,
+115,0,102,108,111,97,116,52,0,171,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,1,0,0,
+102,102,95,97,108,112,104,97,95,114,101,102,0,102,108,111,
+97,116,0,171,0,0,3,0,1,0,1,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,69,1,0,0,102,102,95,102,111,103,95,112,
+115,0,171,171,1,0,3,0,1,0,4,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,1,0,0,77,105,99,114,111,115,111,102,
+116,32,40,82,41,32,72,76,83,76,32,83,104,97,100,101,
+114,32,67,111,109,112,105,108,101,114,32,54,46,51,46,57,
+52,49,53,46,48,0,171,171,76,70,83,48,124,0,0,0,
+2,0,0,0,8,0,0,0,104,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,120,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,65,108,112,104,97,84,101,115,
+116,76,69,113,117,97,108,0,99,111,108,0,76,73,66,70,
+60,4,0,0,68,88,66,67,144,90,107,7,148,126,119,93,
+47,65,200,242,161,144,148,20,1,0,0,0,60,4,0,0,
+5,0,0,0,52,0,0,0,220,0,0,0,96,1,0,0,
+220,1,0,0,180,3,0,0,65,111,110,57,160,0,0,0,
+160,0,0,0,0,2,80,76,112,0,0,0,48,0,0,0,
+1,0,36,0,0,0,48,0,0,0,48,0,0,0,36,0,
+0,0,48,0,1,0,8,0,1,0,0,0,0,0,0,0,
+0,2,80,76,81,0,0,5,1,0,15,160,0,0,128,191,
+0,0,0,128,0,0,0,0,0,0,0,0,31,0,0,2,
+0,0,0,128,0,0,15,176,2,0,0,3,0,0,8,128,
+0,0,255,177,0,0,0,160,88,0,0,4,0,0,15,128,
+0,0,255,128,1,0,0,160,1,0,85,160,65,0,0,1,
+0,0,15,128,1,0,0,2,0,0,15,128,0,0,228,176,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,124,0,0,0,64,0,240,255,31,0,0,0,
+89,0,0,4,70,142,32,0,1,0,0,0,9,0,0,0,
+95,0,0,3,242,16,16,0,0,0,0,0,101,0,0,3,
+242,32,16,0,0,0,0,0,104,0,0,2,1,0,0,0,
+29,0,0,8,18,0,16,0,0,0,0,0,10,128,32,0,
+1,0,0,0,8,0,0,0,58,16,16,0,0,0,0,0,
+13,0,4,3,10,0,16,0,0,0,0,0,54,0,0,5,
+242,32,16,0,0,0,0,0,70,30,16,0,0,0,0,0,
+62,0,0,1,83,84,65,84,116,0,0,0,4,0,0,0,
+1,0,0,0,0,0,0,0,2,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+82,68,69,70,208,1,0,0,1,0,0,0,108,0,0,0,
+1,0,0,0,60,0,0,0,0,4,70,76,0,129,0,0,
+160,1,0,0,82,68,49,49,60,0,0,0,24,0,0,0,
+32,0,0,0,40,0,0,0,36,0,0,0,12,0,0,0,
+0,0,0,0,92,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,
+0,0,0,0,85,110,105,116,121,70,70,80,105,120,101,108,
+0,171,171,171,92,0,0,0,3,0,0,0,132,0,0,0,
+160,0,0,0,0,0,0,0,0,0,0,0,252,0,0,0,
+0,0,0,0,128,0,0,0,0,0,0,0,20,1,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,56,1,0,0,128,0,0,0,4,0,0,0,
+2,0,0,0,76,1,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,112,1,0,0,
+144,0,0,0,16,0,0,0,0,0,0,0,124,1,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,102,102,95,118,101,99,95,99,111,108,111,114,
+115,0,102,108,111,97,116,52,0,171,171,171,1,0,3,0,
+1,0,4,0,8,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,1,0,0,
+102,102,95,97,108,112,104,97,95,114,101,102,0,102,108,111,
+97,116,0,171,0,0,3,0,1,0,1,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,69,1,0,0,102,102,95,102,111,103,95,112,
+115,0,171,171,1,0,3,0,1,0,4,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,1,0,0,77,105,99,114,111,115,111,102,
+116,32,40,82,41,32,72,76,83,76,32,83,104,97,100,101,
+114,32,67,111,109,112,105,108,101,114,32,54,46,51,46,57,
+52,49,53,46,48,0,171,171,76,70,83,48,128,0,0,0,
+2,0,0,0,8,0,0,0,104,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,2,0,0,0,255,255,255,255,255,255,255,255,
+0,0,0,0,0,0,0,0,121,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,
+0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+255,255,255,255,255,255,255,255,65,108,112,104,97,84,101,115,
+116,71,114,101,97,116,101,114,0,99,111,108,0,171,171,171,
+76,73,66,70,76,4,0,0,68,88,66,67,197,56,7,226,
+68,202,40,47,130,164,2,187,83,69,103,33,1,0,0,0,
+76,4,0,0,5,0,0,0,52,0,0,0,236,0,0,0,
+112,1,0,0,236,1,0,0,196,3,0,0,65,111,110,57,
+176,0,0,0,176,0,0,0,0,2,80,76,128,0,0,0,
+48,0,0,0,1,0,36,0,0,0,48,0,0,0,48,0,
+0,0,36,0,0,0,48,0,1,0,8,0,1,0,0,0,
+0,0,0,0,0,2,80,76,81,0,0,5,1,0,15,160,
+0,0,128,191,0,0,0,128,0,0,0,0,0,0,0,0,
+31,0,0,2,0,0,0,128,0,0,15,176,2,0,0,3,
+0,0,8,128,0,0,255,176,0,0,0,161,5,0,0,3,
+0,0,1,128,0,0,255,128,0,0,255,128,88,0,0,4,
+0,0,15,128,0,0,0,129,1,0,0,160,1,0,85,160,
+65,0,0,1,0,0,15,128,1,0,0,2,0,0,15,128,
+0,0,228,176,1,0,0,2,0,0,15,224,0,0,228,128,
+255,255,0,0,83,72,68,82,124,0,0,0,64,0,240,255,
+31,0,0,0,89,0,0,4,70,142,32,0,1,0,0,0,
+9,0,0,0,95,0,0,3,242,16,16,0,0,0,0,0,
+101,0,0,3,242,32,16,0,0,0,0,0,104,0,0,2,
+1,0,0,0,24,0,0,8,18,0,16,0,0,0,0,0,
+58,16,16,0,0,0,0,0,10,128,32,0,1,0,0,0,
+8,0,0,0,13,0,4,3,10,0,16,0,0,0,0,0,
+54,0,0,5,242,32,16,0,0,0,0,0,70,30,16,0,
+0,0,0,0,62,0,0,1,83,84,65,84,116,0,0,0,
+4,0,0,0,1,0,0,0,0,0,0,0,2,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,82,68,69,70,208,1,0,0,1,0,0,0,
+108,0,0,0,1,0,0,0,60,0,0,0,0,4,70,76,
+0,129,0,0,160,1,0,0,82,68,49,49,60,0,0,0,
+24,0,0,0,32,0,0,0,40,0,0,0,36,0,0,0,
+12,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+1,0,0,0,0,0,0,0,85,110,105,116,121,70,70,80,
+105,120,101,108,0,171,171,171,92,0,0,0,3,0,0,0,
+132,0,0,0,160,0,0,0,0,0,0,0,0,0,0,0,
+252,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,
+20,1,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,56,1,0,0,128,0,0,0,
+4,0,0,0,2,0,0,0,76,1,0,0,0,0,0,0,
+255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,
+112,1,0,0,144,0,0,0,16,0,0,0,0,0,0,0,
+124,1,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+255,255,255,255,0,0,0,0,102,102,95,118,101,99,95,99,
+111,108,111,114,115,0,102,108,111,97,116,52,0,171,171,171,
+1,0,3,0,1,0,4,0,8,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+10,1,0,0,102,102,95,97,108,112,104,97,95,114,101,102,
+0,102,108,111,97,116,0,171,0,0,3,0,1,0,1,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,69,1,0,0,102,102,95,102,
+111,103,95,112,115,0,171,171,1,0,3,0,1,0,4,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,10,1,0,0,77,105,99,114,
+111,115,111,102,116,32,40,82,41,32,72,76,83,76,32,83,
+104,97,100,101,114,32,67,111,109,112,105,108,101,114,32,54,
+46,51,46,57,52,49,53,46,48,0,171,171,76,70,83,48,
+128,0,0,0,2,0,0,0,8,0,0,0,104,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,2,0,0,0,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,122,0,0,0,
+0,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,
+4,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+0,0,0,0,255,255,255,255,255,255,255,255,65,108,112,104,
+97,84,101,115,116,78,111,116,69,113,117,97,108,0,99,111,
+108,0,171,171,76,73,66,70,56,4,0,0,68,88,66,67,
+115,13,35,167,25,6,43,221,107,197,197,253,203,93,251,92,
+1,0,0,0,56,4,0,0,5,0,0,0,52,0,0,0,
+220,0,0,0,96,1,0,0,220,1,0,0,180,3,0,0,
+65,111,110,57,160,0,0,0,160,0,0,0,0,2,80,76,
+112,0,0,0,48,0,0,0,1,0,36,0,0,0,48,0,
+0,0,48,0,0,0,36,0,0,0,48,0,1,0,8,0,
+1,0,0,0,0,0,0,0,0,2,80,76,81,0,0,5,
+1,0,15,160,0,0,0,128,0,0,128,191,0,0,0,0,
+0,0,0,0,31,0,0,2,0,0,0,128,0,0,15,176,
+2,0,0,3,0,0,8,128,0,0,255,176,0,0,0,161,
+88,0,0,4,0,0,15,128,0,0,255,128,1,0,0,160,
+1,0,85,160,65,0,0,1,0,0,15,128,1,0,0,2,
+0,0,15,128,0,0,228,176,1,0,0,2,0,0,15,224,
+0,0,228,128,255,255,0,0,83,72,68,82,124,0,0,0,
+64,0,240,255,31,0,0,0,89,0,0,4,70,142,32,0,
+1,0,0,0,9,0,0,0,95,0,0,3,242,16,16,0,
+0,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+104,0,0,2,1,0,0,0,49,0,0,8,18,0,16,0,
+0,0,0,0,58,16,16,0,0,0,0,0,10,128,32,0,
+1,0,0,0,8,0,0,0,13,0,4,3,10,0,16,0,
+0,0,0,0,54,0,0,5,242,32,16,0,0,0,0,0,
+70,30,16,0,0,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,4,0,0,0,1,0,0,0,0,0,0,0,
+2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,208,1,0,0,
+1,0,0,0,108,0,0,0,1,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,160,1,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,92,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,1,0,0,0,0,0,0,0,85,110,105,116,
+121,70,70,80,105,120,101,108,0,171,171,171,92,0,0,0,
+3,0,0,0,132,0,0,0,160,0,0,0,0,0,0,0,
+0,0,0,0,252,0,0,0,0,0,0,0,128,0,0,0,
+0,0,0,0,20,1,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,56,1,0,0,
+128,0,0,0,4,0,0,0,2,0,0,0,76,1,0,0,
+0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,
+0,0,0,0,112,1,0,0,144,0,0,0,16,0,0,0,
+0,0,0,0,124,1,0,0,0,0,0,0,255,255,255,255,
+0,0,0,0,255,255,255,255,0,0,0,0,102,102,95,118,
+101,99,95,99,111,108,111,114,115,0,102,108,111,97,116,52,
+0,171,171,171,1,0,3,0,1,0,4,0,8,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,10,1,0,0,102,102,95,97,108,112,104,97,
+95,114,101,102,0,102,108,111,97,116,0,171,0,0,3,0,
+1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,69,1,0,0,
+102,102,95,102,111,103,95,112,115,0,171,171,1,0,3,0,
+1,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,10,1,0,0,
+77,105,99,114,111,115,111,102,116,32,40,82,41,32,72,76,
+83,76,32,83,104,97,100,101,114,32,67,111,109,112,105,108,
+101,114,32,54,46,51,46,57,52,49,53,46,48,0,171,171,
+76,70,83,48,124,0,0,0,2,0,0,0,8,0,0,0,
+104,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,2,0,0,0,
+255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,
+120,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,
+1,0,0,0,4,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,
+65,108,112,104,97,84,101,115,116,71,69,113,117,97,108,0,
+99,111,108,0,76,73,66,72,137,7,0,0,1,0,0,0,
+164,2,0,0,0,0,0,0,82,0,0,0,20,0,0,0,
+3,0,0,0,210,2,0,0,3,0,0,0,226,2,0,0,
+3,0,0,0,249,2,0,0,3,0,0,0,4,3,0,0,
+3,0,0,0,18,3,0,0,3,0,0,0,27,3,0,0,
+3,0,0,0,39,3,0,0,3,0,0,0,51,3,0,0,
+3,0,0,0,68,3,0,0,3,0,0,0,85,3,0,0,
+3,0,0,0,103,3,0,0,3,0,0,0,118,3,0,0,
+3,0,0,0,136,3,0,0,3,0,0,0,158,3,0,0,
+3,0,0,0,176,3,0,0,3,0,0,0,198,3,0,0,
+1,0,0,0,216,3,0,0,1,0,0,0,238,3,0,0,
+1,0,0,0,0,4,0,0,1,0,0,0,22,4,0,0,
+1,0,0,0,40,4,0,0,1,0,0,0,62,4,0,0,
+1,0,0,0,80,4,0,0,1,0,0,0,102,4,0,0,
+1,0,0,0,120,4,0,0,1,0,0,0,142,4,0,0,
+1,0,0,0,160,4,0,0,1,0,0,0,182,4,0,0,
+1,0,0,0,200,4,0,0,3,0,0,0,222,4,0,0,
+3,0,0,0,240,4,0,0,3,0,0,0,0,5,0,0,
+3,0,0,0,10,5,0,0,3,0,0,0,20,5,0,0,
+3,0,0,0,26,5,0,0,3,0,0,0,39,5,0,0,
+3,0,0,0,51,5,0,0,3,0,0,0,63,5,0,0,
+3,0,0,0,75,5,0,0,3,0,0,0,87,5,0,0,
+3,0,0,0,99,5,0,0,3,0,0,0,111,5,0,0,
+3,0,0,0,123,5,0,0,3,0,0,0,135,5,0,0,
+3,0,0,0,147,5,0,0,3,0,0,0,157,5,0,0,
+3,0,0,0,176,5,0,0,3,0,0,0,195,5,0,0,
+3,0,0,0,214,5,0,0,3,0,0,0,233,5,0,0,
+3,0,0,0,252,5,0,0,3,0,0,0,15,6,0,0,
+3,0,0,0,34,6,0,0,3,0,0,0,53,6,0,0,
+3,0,0,0,63,6,0,0,3,0,0,0,73,6,0,0,
+3,0,0,0,83,6,0,0,3,0,0,0,95,6,0,0,
+3,0,0,0,108,6,0,0,3,0,0,0,116,6,0,0,
+3,0,0,0,130,6,0,0,3,0,0,0,143,6,0,0,
+3,0,0,0,152,6,0,0,3,0,0,0,161,6,0,0,
+3,0,0,0,174,6,0,0,3,0,0,0,185,6,0,0,
+3,0,0,0,196,6,0,0,3,0,0,0,213,6,0,0,
+3,0,0,0,220,6,0,0,3,0,0,0,227,6,0,0,
+3,0,0,0,235,6,0,0,3,0,0,0,248,6,0,0,
+3,0,0,0,2,7,0,0,3,0,0,0,9,7,0,0,
+3,0,0,0,17,7,0,0,2,0,0,0,26,7,0,0,
+2,0,0,0,41,7,0,0,2,0,0,0,55,7,0,0,
+2,0,0,0,70,7,0,0,2,0,0,0,86,7,0,0,
+2,0,0,0,103,7,0,0,2,0,0,0,121,7,0,0,
+77,105,99,114,111,115,111,102,116,32,40,82,41,32,72,76,
+83,76,32,83,104,97,100,101,114,32,67,111,109,112,105,108,
+101,114,32,54,46,51,46,57,52,49,53,46,48,0,76,111,
+97,100,86,101,114,116,101,120,67,111,108,111,114,0,76,111,
+97,100,86,101,114,116,101,120,67,111,108,111,114,85,110,105,
+102,111,114,109,0,76,111,97,100,69,121,101,80,111,115,0,
+76,111,97,100,69,121,101,78,111,114,109,97,108,0,76,111,
+97,100,90,101,114,111,0,76,111,97,100,86,105,101,119,68,
+105,114,0,76,111,97,100,69,121,101,82,101,102,108,0,76,
+111,97,100,65,109,98,105,101,110,116,67,111,108,111,114,0,
+76,111,97,100,68,105,102,102,117,115,101,67,111,108,111,114,
+0,76,111,97,100,69,109,105,115,115,105,111,110,67,111,108,
+111,114,0,73,110,105,116,76,105,103,104,116,67,111,108,111,
+114,0,67,111,109,112,117,116,101,83,112,111,116,76,105,103,
+104,116,48,0,67,111,109,112,117,116,101,83,112,111,116,76,
+105,103,104,116,83,112,101,99,48,0,67,111,109,112,117,116,
+101,83,112,111,116,76,105,103,104,116,49,0,67,111,109,112,
+117,116,101,83,112,111,116,76,105,103,104,116,83,112,101,99,
+49,0,67,111,109,112,117,116,101,83,112,111,116,76,105,103,
+104,116,50,0,67,111,109,112,117,116,101,83,112,111,116,76,
+105,103,104,116,83,112,101,99,50,0,67,111,109,112,117,116,
+101,83,112,111,116,76,105,103,104,116,51,0,67,111,109,112,
+117,116,101,83,112,111,116,76,105,103,104,116,83,112,101,99,
+51,0,67,111,109,112,117,116,101,83,112,111,116,76,105,103,
+104,116,52,0,67,111,109,112,117,116,101,83,112,111,116,76,
+105,103,104,116,83,112,101,99,52,0,67,111,109,112,117,116,
+101,83,112,111,116,76,105,103,104,116,53,0,67,111,109,112,
+117,116,101,83,112,111,116,76,105,103,104,116,83,112,101,99,
+53,0,67,111,109,112,117,116,101,83,112,111,116,76,105,103,
+104,116,54,0,67,111,109,112,117,116,101,83,112,111,116,76,
+105,103,104,116,83,112,101,99,54,0,67,111,109,112,117,116,
+101,83,112,111,116,76,105,103,104,116,55,0,67,111,109,112,
+117,116,101,83,112,111,116,76,105,103,104,116,83,112,101,99,
+55,0,67,111,109,112,117,116,101,83,112,111,116,76,105,103,
+104,116,56,0,67,111,109,112,117,116,101,83,112,111,116,76,
+105,103,104,116,83,112,101,99,56,0,76,111,97,100,76,105,
+103,104,116,105,110,103,67,111,108,111,114,0,84,114,97,110,
+115,102,111,114,109,86,101,114,116,101,120,0,83,97,116,117,
+114,97,116,101,52,0,83,97,116,117,114,97,116,101,51,0,
+76,111,97,100,51,0,77,111,100,117,108,97,116,101,83,112,
+101,99,0,77,117,108,116,105,112,108,121,85,86,48,0,77,
+117,108,116,105,112,108,121,85,86,49,0,77,117,108,116,105,
+112,108,121,85,86,50,0,77,117,108,116,105,112,108,121,85,
+86,51,0,77,117,108,116,105,112,108,121,85,86,52,0,77,
+117,108,116,105,112,108,121,85,86,53,0,77,117,108,116,105,
+112,108,121,85,86,54,0,77,117,108,116,105,112,108,121,85,
+86,55,0,85,86,83,112,104,101,114,101,77,97,112,0,70,
+108,111,97,116,51,116,111,52,0,76,111,97,100,67,111,110,
+115,116,97,110,116,67,111,108,111,114,48,0,76,111,97,100,
+67,111,110,115,116,97,110,116,67,111,108,111,114,49,0,76,
+111,97,100,67,111,110,115,116,97,110,116,67,111,108,111,114,
+50,0,76,111,97,100,67,111,110,115,116,97,110,116,67,111,
+108,111,114,51,0,76,111,97,100,67,111,110,115,116,97,110,
+116,67,111,108,111,114,52,0,76,111,97,100,67,111,110,115,
+116,97,110,116,67,111,108,111,114,53,0,76,111,97,100,67,
+111,110,115,116,97,110,116,67,111,108,111,114,54,0,76,111,
+97,100,67,111,110,115,116,97,110,116,67,111,108,111,114,55,
+0,79,110,101,77,105,110,117,115,49,0,79,110,101,77,105,
+110,117,115,51,0,79,110,101,77,105,110,117,115,52,0,67,
+111,109,98,82,101,112,108,97,99,101,0,67,111,109,98,77,
+111,100,117,108,97,116,101,0,67,111,109,98,65,100,100,0,
+67,111,109,98,65,100,100,83,105,103,110,101,100,0,67,111,
+109,98,83,117,98,116,114,97,99,116,0,67,111,109,98,76,
+101,114,112,0,67,111,109,98,68,111,116,51,0,67,111,109,
+98,68,111,116,51,114,103,98,97,0,67,111,109,98,77,117,
+108,65,100,100,0,67,111,109,98,77,117,108,83,117,98,0,
+67,111,109,98,77,117,108,65,100,100,83,105,103,110,101,100,
+0,83,99,97,108,101,50,0,83,99,97,108,101,52,0,65,
+100,100,83,112,101,99,0,67,111,109,98,105,110,101,65,108,
+112,104,97,0,70,111,103,76,105,110,101,97,114,0,70,111,
+103,69,120,112,0,70,111,103,69,120,112,50,0,65,112,112,
+108,121,70,111,103,0,65,108,112,104,97,84,101,115,116,78,
+101,118,101,114,0,65,108,112,104,97,84,101,115,116,76,101,
+115,115,0,65,108,112,104,97,84,101,115,116,69,113,117,97,
+108,0,65,108,112,104,97,84,101,115,116,76,69,113,117,97,
+108,0,65,108,112,104,97,84,101,115,116,71,114,101,97,116,
+101,114,0,65,108,112,104,97,84,101,115,116,78,111,116,69,
+113,117,97,108,0,65,108,112,104,97,84,101,115,116,71,69,
+113,117,97,108,0
+};
+const BYTE g_FFSampleTex2D0[] = {
+68,88,66,67,186,190,133,217,122,113,117,157,223,22,77,225,
+182,226,68,58,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,167,63,28,244,217,66,235,37,252,144,14,203,
+197,4,28,142,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+0,0,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,3,176,31,0,0,2,0,0,0,144,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,0,0,0,0,88,24,0,4,
+0,112,16,0,0,0,0,0,85,85,0,0,95,0,0,3,
+50,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,16,16,0,0,0,0,0,70,126,16,0,0,0,0,0,
+0,96,16,0,0,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,4,0,0,0,255,255,255,255,
+0,0,0,0,1,0,0,0,13,0,0,0,83,109,112,48,
+0,84,101,120,48,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,48,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,48,0
+};
+const BYTE g_FFSampleTex2D1[] = {
+68,88,66,67,47,67,190,161,200,111,42,45,200,138,126,191,
+157,88,130,65,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,254,4,120,248,75,193,95,168,115,4,59,237,
+17,210,26,42,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+1,1,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,3,176,31,0,0,2,0,0,0,144,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,1,0,0,0,88,24,0,4,
+0,112,16,0,1,0,0,0,85,85,0,0,95,0,0,3,
+50,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,16,16,0,0,0,0,0,70,126,16,0,1,0,0,0,
+0,96,16,0,1,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,4,0,0,0,255,255,255,255,
+1,0,0,0,1,0,0,0,13,0,0,0,83,109,112,49,
+0,84,101,120,49,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,49,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,49,0
+};
+const BYTE g_FFSampleTex2D2[] = {
+68,88,66,67,180,208,133,71,201,51,63,167,99,13,252,147,
+105,15,242,34,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,162,103,158,112,144,136,184,255,249,212,11,72,
+140,25,192,113,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+2,2,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,3,176,31,0,0,2,0,0,0,144,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,2,0,0,0,88,24,0,4,
+0,112,16,0,2,0,0,0,85,85,0,0,95,0,0,3,
+50,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,16,16,0,0,0,0,0,70,126,16,0,2,0,0,0,
+0,96,16,0,2,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,4,0,0,0,255,255,255,255,
+2,0,0,0,1,0,0,0,13,0,0,0,83,109,112,50,
+0,84,101,120,50,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,50,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,50,0
+};
+const BYTE g_FFSampleTex2D3[] = {
+68,88,66,67,149,238,135,1,0,112,221,254,146,94,115,12,
+68,5,172,135,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,196,71,209,198,86,237,243,139,237,193,9,14,
+69,112,32,203,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+3,3,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,3,176,31,0,0,2,0,0,0,144,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,3,0,0,0,88,24,0,4,
+0,112,16,0,3,0,0,0,85,85,0,0,95,0,0,3,
+50,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,16,16,0,0,0,0,0,70,126,16,0,3,0,0,0,
+0,96,16,0,3,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,4,0,0,0,255,255,255,255,
+3,0,0,0,1,0,0,0,13,0,0,0,83,109,112,51,
+0,84,101,120,51,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,51,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,51,0
+};
+const BYTE g_FFSampleTex2D4[] = {
+68,88,66,67,1,143,168,112,164,72,178,93,61,206,79,16,
+192,187,155,32,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,209,234,176,153,155,94,152,9,241,37,65,106,
+131,216,55,96,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+4,4,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,3,176,31,0,0,2,0,0,0,144,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,4,0,0,0,88,24,0,4,
+0,112,16,0,4,0,0,0,85,85,0,0,95,0,0,3,
+50,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,16,16,0,0,0,0,0,70,126,16,0,4,0,0,0,
+0,96,16,0,4,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+4,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,4,0,0,0,255,255,255,255,
+4,0,0,0,1,0,0,0,13,0,0,0,83,109,112,52,
+0,84,101,120,52,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,52,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,52,0
+};
+const BYTE g_FFSampleTex2D5[] = {
+68,88,66,67,146,103,79,44,172,131,19,17,83,225,225,20,
+193,21,8,43,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,121,249,154,239,231,85,182,236,126,123,45,2,
+117,82,141,241,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+5,5,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,3,176,31,0,0,2,0,0,0,144,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,5,0,0,0,88,24,0,4,
+0,112,16,0,5,0,0,0,85,85,0,0,95,0,0,3,
+50,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,16,16,0,0,0,0,0,70,126,16,0,5,0,0,0,
+0,96,16,0,5,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+5,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,4,0,0,0,255,255,255,255,
+5,0,0,0,1,0,0,0,13,0,0,0,83,109,112,53,
+0,84,101,120,53,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,53,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,53,0
+};
+const BYTE g_FFSampleTex2D6[] = {
+68,88,66,67,240,29,95,192,17,112,54,92,138,198,156,88,
+34,54,205,170,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,66,194,12,143,10,1,221,231,219,62,49,251,
+254,63,175,14,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+6,6,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,3,176,31,0,0,2,0,0,0,144,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,6,0,0,0,88,24,0,4,
+0,112,16,0,6,0,0,0,85,85,0,0,95,0,0,3,
+50,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,16,16,0,0,0,0,0,70,126,16,0,6,0,0,0,
+0,96,16,0,6,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+6,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,4,0,0,0,255,255,255,255,
+6,0,0,0,1,0,0,0,13,0,0,0,83,109,112,54,
+0,84,101,120,54,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,54,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,54,0
+};
+const BYTE g_FFSampleTex2D7[] = {
+68,88,66,67,128,254,127,89,212,237,60,112,109,237,190,159,
+68,41,91,237,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,26,171,200,29,118,90,223,37,168,34,191,21,
+246,151,59,230,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+7,7,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,3,176,31,0,0,2,0,0,0,144,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,7,0,0,0,88,24,0,4,
+0,112,16,0,7,0,0,0,85,85,0,0,95,0,0,3,
+50,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,16,16,0,0,0,0,0,70,126,16,0,7,0,0,0,
+0,96,16,0,7,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+7,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,4,0,0,0,255,255,255,255,
+7,0,0,0,1,0,0,0,13,0,0,0,83,109,112,55,
+0,84,101,120,55,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,55,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,55,0
+};
+const BYTE g_FFSampleTexProj0[] = {
+68,88,66,67,237,34,106,171,253,60,160,245,196,118,246,108,
+124,200,6,0,1,0,0,0,139,3,0,0,2,0,0,0,
+40,0,0,0,48,3,0,0,76,73,66,70,0,3,0,0,
+68,88,66,67,230,60,101,10,132,76,15,42,191,248,157,64,
+112,9,91,75,1,0,0,0,0,3,0,0,5,0,0,0,
+52,0,0,0,188,0,0,0,76,1,0,0,200,1,0,0,
+132,2,0,0,65,111,110,57,128,0,0,0,128,0,0,0,
+0,2,80,76,88,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+0,0,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,11,176,31,0,0,2,0,0,0,144,0,8,15,160,
+6,0,0,2,0,0,8,128,0,0,255,176,5,0,0,3,
+0,0,3,128,0,0,255,128,0,0,228,176,66,0,0,3,
+0,0,15,128,0,0,228,128,0,8,228,160,1,0,0,2,
+0,0,15,224,0,0,228,128,255,255,0,0,83,72,68,82,
+136,0,0,0,64,0,240,255,34,0,0,0,90,0,0,3,
+0,96,16,0,0,0,0,0,88,24,0,4,0,112,16,0,
+0,0,0,0,85,85,0,0,95,0,0,3,178,16,16,0,
+0,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+104,0,0,2,1,0,0,0,14,0,0,7,50,0,16,0,
+0,0,0,0,70,16,16,0,0,0,0,0,246,31,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,0,16,0,0,0,0,0,70,126,16,0,0,0,0,0,
+0,96,16,0,0,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,
+2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,4,0,0,0,255,255,255,255,
+0,0,0,0,1,0,0,0,13,0,0,0,83,109,112,48,
+0,84,101,120,48,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,48,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,48,0
+};
+const BYTE g_FFSampleTexProj1[] = {
+68,88,66,67,4,136,237,251,219,54,28,116,17,188,142,117,
+202,37,0,216,1,0,0,0,139,3,0,0,2,0,0,0,
+40,0,0,0,48,3,0,0,76,73,66,70,0,3,0,0,
+68,88,66,67,227,64,211,49,27,178,55,234,32,218,202,171,
+120,32,81,116,1,0,0,0,0,3,0,0,5,0,0,0,
+52,0,0,0,188,0,0,0,76,1,0,0,200,1,0,0,
+132,2,0,0,65,111,110,57,128,0,0,0,128,0,0,0,
+0,2,80,76,88,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+1,1,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,11,176,31,0,0,2,0,0,0,144,0,8,15,160,
+6,0,0,2,0,0,8,128,0,0,255,176,5,0,0,3,
+0,0,3,128,0,0,255,128,0,0,228,176,66,0,0,3,
+0,0,15,128,0,0,228,128,0,8,228,160,1,0,0,2,
+0,0,15,224,0,0,228,128,255,255,0,0,83,72,68,82,
+136,0,0,0,64,0,240,255,34,0,0,0,90,0,0,3,
+0,96,16,0,1,0,0,0,88,24,0,4,0,112,16,0,
+1,0,0,0,85,85,0,0,95,0,0,3,178,16,16,0,
+0,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+104,0,0,2,1,0,0,0,14,0,0,7,50,0,16,0,
+0,0,0,0,70,16,16,0,0,0,0,0,246,31,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,0,16,0,0,0,0,0,70,126,16,0,1,0,0,0,
+0,96,16,0,1,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,
+2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,4,0,0,0,255,255,255,255,
+1,0,0,0,1,0,0,0,13,0,0,0,83,109,112,49,
+0,84,101,120,49,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,49,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,49,0
+};
+const BYTE g_FFSampleTexProj2[] = {
+68,88,66,67,2,30,122,43,115,147,198,113,36,147,136,65,
+91,229,165,118,1,0,0,0,139,3,0,0,2,0,0,0,
+40,0,0,0,48,3,0,0,76,73,66,70,0,3,0,0,
+68,88,66,67,158,195,81,208,65,178,14,56,95,97,26,50,
+228,55,13,138,1,0,0,0,0,3,0,0,5,0,0,0,
+52,0,0,0,188,0,0,0,76,1,0,0,200,1,0,0,
+132,2,0,0,65,111,110,57,128,0,0,0,128,0,0,0,
+0,2,80,76,88,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+2,2,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,11,176,31,0,0,2,0,0,0,144,0,8,15,160,
+6,0,0,2,0,0,8,128,0,0,255,176,5,0,0,3,
+0,0,3,128,0,0,255,128,0,0,228,176,66,0,0,3,
+0,0,15,128,0,0,228,128,0,8,228,160,1,0,0,2,
+0,0,15,224,0,0,228,128,255,255,0,0,83,72,68,82,
+136,0,0,0,64,0,240,255,34,0,0,0,90,0,0,3,
+0,96,16,0,2,0,0,0,88,24,0,4,0,112,16,0,
+2,0,0,0,85,85,0,0,95,0,0,3,178,16,16,0,
+0,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+104,0,0,2,1,0,0,0,14,0,0,7,50,0,16,0,
+0,0,0,0,70,16,16,0,0,0,0,0,246,31,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,0,16,0,0,0,0,0,70,126,16,0,2,0,0,0,
+0,96,16,0,2,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,
+2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,4,0,0,0,255,255,255,255,
+2,0,0,0,1,0,0,0,13,0,0,0,83,109,112,50,
+0,84,101,120,50,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,50,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,50,0
+};
+const BYTE g_FFSampleTexProj3[] = {
+68,88,66,67,198,103,138,26,135,26,62,28,241,113,110,230,
+103,182,28,127,1,0,0,0,139,3,0,0,2,0,0,0,
+40,0,0,0,48,3,0,0,76,73,66,70,0,3,0,0,
+68,88,66,67,190,50,195,62,15,53,121,224,115,82,118,6,
+11,177,14,169,1,0,0,0,0,3,0,0,5,0,0,0,
+52,0,0,0,188,0,0,0,76,1,0,0,200,1,0,0,
+132,2,0,0,65,111,110,57,128,0,0,0,128,0,0,0,
+0,2,80,76,88,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+3,3,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,11,176,31,0,0,2,0,0,0,144,0,8,15,160,
+6,0,0,2,0,0,8,128,0,0,255,176,5,0,0,3,
+0,0,3,128,0,0,255,128,0,0,228,176,66,0,0,3,
+0,0,15,128,0,0,228,128,0,8,228,160,1,0,0,2,
+0,0,15,224,0,0,228,128,255,255,0,0,83,72,68,82,
+136,0,0,0,64,0,240,255,34,0,0,0,90,0,0,3,
+0,96,16,0,3,0,0,0,88,24,0,4,0,112,16,0,
+3,0,0,0,85,85,0,0,95,0,0,3,178,16,16,0,
+0,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+104,0,0,2,1,0,0,0,14,0,0,7,50,0,16,0,
+0,0,0,0,70,16,16,0,0,0,0,0,246,31,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,0,16,0,0,0,0,0,70,126,16,0,3,0,0,0,
+0,96,16,0,3,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,
+2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,4,0,0,0,255,255,255,255,
+3,0,0,0,1,0,0,0,13,0,0,0,83,109,112,51,
+0,84,101,120,51,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,51,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,51,0
+};
+const BYTE g_FFSampleTexProj4[] = {
+68,88,66,67,214,247,95,61,160,17,24,2,54,171,136,221,
+162,93,207,123,1,0,0,0,139,3,0,0,2,0,0,0,
+40,0,0,0,48,3,0,0,76,73,66,70,0,3,0,0,
+68,88,66,67,230,212,227,79,165,100,103,2,70,84,103,231,
+199,191,4,95,1,0,0,0,0,3,0,0,5,0,0,0,
+52,0,0,0,188,0,0,0,76,1,0,0,200,1,0,0,
+132,2,0,0,65,111,110,57,128,0,0,0,128,0,0,0,
+0,2,80,76,88,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+4,4,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,11,176,31,0,0,2,0,0,0,144,0,8,15,160,
+6,0,0,2,0,0,8,128,0,0,255,176,5,0,0,3,
+0,0,3,128,0,0,255,128,0,0,228,176,66,0,0,3,
+0,0,15,128,0,0,228,128,0,8,228,160,1,0,0,2,
+0,0,15,224,0,0,228,128,255,255,0,0,83,72,68,82,
+136,0,0,0,64,0,240,255,34,0,0,0,90,0,0,3,
+0,96,16,0,4,0,0,0,88,24,0,4,0,112,16,0,
+4,0,0,0,85,85,0,0,95,0,0,3,178,16,16,0,
+0,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+104,0,0,2,1,0,0,0,14,0,0,7,50,0,16,0,
+0,0,0,0,70,16,16,0,0,0,0,0,246,31,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,0,16,0,0,0,0,0,70,126,16,0,4,0,0,0,
+0,96,16,0,4,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,
+2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+4,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,4,0,0,0,255,255,255,255,
+4,0,0,0,1,0,0,0,13,0,0,0,83,109,112,52,
+0,84,101,120,52,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,52,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,52,0
+};
+const BYTE g_FFSampleTexProj5[] = {
+68,88,66,67,66,189,97,195,183,152,219,151,58,228,33,211,
+23,65,5,98,1,0,0,0,139,3,0,0,2,0,0,0,
+40,0,0,0,48,3,0,0,76,73,66,70,0,3,0,0,
+68,88,66,67,161,32,90,6,181,122,141,232,231,32,178,86,
+153,147,189,100,1,0,0,0,0,3,0,0,5,0,0,0,
+52,0,0,0,188,0,0,0,76,1,0,0,200,1,0,0,
+132,2,0,0,65,111,110,57,128,0,0,0,128,0,0,0,
+0,2,80,76,88,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+5,5,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,11,176,31,0,0,2,0,0,0,144,0,8,15,160,
+6,0,0,2,0,0,8,128,0,0,255,176,5,0,0,3,
+0,0,3,128,0,0,255,128,0,0,228,176,66,0,0,3,
+0,0,15,128,0,0,228,128,0,8,228,160,1,0,0,2,
+0,0,15,224,0,0,228,128,255,255,0,0,83,72,68,82,
+136,0,0,0,64,0,240,255,34,0,0,0,90,0,0,3,
+0,96,16,0,5,0,0,0,88,24,0,4,0,112,16,0,
+5,0,0,0,85,85,0,0,95,0,0,3,178,16,16,0,
+0,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+104,0,0,2,1,0,0,0,14,0,0,7,50,0,16,0,
+0,0,0,0,70,16,16,0,0,0,0,0,246,31,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,0,16,0,0,0,0,0,70,126,16,0,5,0,0,0,
+0,96,16,0,5,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,
+2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+5,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,4,0,0,0,255,255,255,255,
+5,0,0,0,1,0,0,0,13,0,0,0,83,109,112,53,
+0,84,101,120,53,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,53,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,53,0
+};
+const BYTE g_FFSampleTexProj6[] = {
+68,88,66,67,153,221,7,160,36,89,179,148,98,194,126,246,
+177,187,106,246,1,0,0,0,139,3,0,0,2,0,0,0,
+40,0,0,0,48,3,0,0,76,73,66,70,0,3,0,0,
+68,88,66,67,46,167,174,151,195,138,31,77,170,240,199,31,
+49,127,212,92,1,0,0,0,0,3,0,0,5,0,0,0,
+52,0,0,0,188,0,0,0,76,1,0,0,200,1,0,0,
+132,2,0,0,65,111,110,57,128,0,0,0,128,0,0,0,
+0,2,80,76,88,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+6,6,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,11,176,31,0,0,2,0,0,0,144,0,8,15,160,
+6,0,0,2,0,0,8,128,0,0,255,176,5,0,0,3,
+0,0,3,128,0,0,255,128,0,0,228,176,66,0,0,3,
+0,0,15,128,0,0,228,128,0,8,228,160,1,0,0,2,
+0,0,15,224,0,0,228,128,255,255,0,0,83,72,68,82,
+136,0,0,0,64,0,240,255,34,0,0,0,90,0,0,3,
+0,96,16,0,6,0,0,0,88,24,0,4,0,112,16,0,
+6,0,0,0,85,85,0,0,95,0,0,3,178,16,16,0,
+0,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+104,0,0,2,1,0,0,0,14,0,0,7,50,0,16,0,
+0,0,0,0,70,16,16,0,0,0,0,0,246,31,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,0,16,0,0,0,0,0,70,126,16,0,6,0,0,0,
+0,96,16,0,6,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,
+2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+6,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,4,0,0,0,255,255,255,255,
+6,0,0,0,1,0,0,0,13,0,0,0,83,109,112,54,
+0,84,101,120,54,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,54,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,54,0
+};
+const BYTE g_FFSampleTexProj7[] = {
+68,88,66,67,54,198,9,208,91,47,140,152,149,52,162,217,
+161,54,143,152,1,0,0,0,139,3,0,0,2,0,0,0,
+40,0,0,0,48,3,0,0,76,73,66,70,0,3,0,0,
+68,88,66,67,2,126,106,25,61,155,75,228,172,90,27,233,
+211,161,173,74,1,0,0,0,0,3,0,0,5,0,0,0,
+52,0,0,0,188,0,0,0,76,1,0,0,200,1,0,0,
+132,2,0,0,65,111,110,57,128,0,0,0,128,0,0,0,
+0,2,80,76,88,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+7,7,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,11,176,31,0,0,2,0,0,0,144,0,8,15,160,
+6,0,0,2,0,0,8,128,0,0,255,176,5,0,0,3,
+0,0,3,128,0,0,255,128,0,0,228,176,66,0,0,3,
+0,0,15,128,0,0,228,128,0,8,228,160,1,0,0,2,
+0,0,15,224,0,0,228,128,255,255,0,0,83,72,68,82,
+136,0,0,0,64,0,240,255,34,0,0,0,90,0,0,3,
+0,96,16,0,7,0,0,0,88,24,0,4,0,112,16,0,
+7,0,0,0,85,85,0,0,95,0,0,3,178,16,16,0,
+0,0,0,0,101,0,0,3,242,32,16,0,0,0,0,0,
+104,0,0,2,1,0,0,0,14,0,0,7,50,0,16,0,
+0,0,0,0,70,16,16,0,0,0,0,0,246,31,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,0,16,0,0,0,0,0,70,126,16,0,7,0,0,0,
+0,96,16,0,7,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,
+2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+7,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,4,0,0,0,255,255,255,255,
+7,0,0,0,1,0,0,0,13,0,0,0,83,109,112,55,
+0,84,101,120,55,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,55,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,55,0
+};
+const BYTE g_FFSampleTex3D0[] = {
+68,88,66,67,206,176,17,24,163,228,56,124,213,217,89,247,
+168,217,106,41,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,14,80,190,175,153,165,204,147,108,17,98,29,
+189,179,225,238,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+0,0,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,7,176,31,0,0,2,0,0,0,160,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,0,0,0,0,88,40,0,4,
+0,112,16,0,0,0,0,0,85,85,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,70,126,16,0,0,0,0,0,
+0,96,16,0,0,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,8,0,0,0,255,255,255,255,
+0,0,0,0,1,0,0,0,13,0,0,0,83,109,112,48,
+0,84,101,120,48,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,48,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,48,0
+};
+const BYTE g_FFSampleTex3D1[] = {
+68,88,66,67,54,231,211,65,158,32,30,68,185,17,249,76,
+2,7,22,248,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,171,59,174,152,183,87,216,198,114,154,0,113,
+150,163,208,243,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+1,1,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,7,176,31,0,0,2,0,0,0,160,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,1,0,0,0,88,40,0,4,
+0,112,16,0,1,0,0,0,85,85,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,70,126,16,0,1,0,0,0,
+0,96,16,0,1,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,8,0,0,0,255,255,255,255,
+1,0,0,0,1,0,0,0,13,0,0,0,83,109,112,49,
+0,84,101,120,49,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,49,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,49,0
+};
+const BYTE g_FFSampleTex3D2[] = {
+68,88,66,67,153,118,160,252,192,90,141,221,27,255,54,21,
+121,98,195,236,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,201,9,226,184,184,198,44,161,181,91,189,22,
+164,160,102,27,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+2,2,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,7,176,31,0,0,2,0,0,0,160,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,2,0,0,0,88,40,0,4,
+0,112,16,0,2,0,0,0,85,85,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,70,126,16,0,2,0,0,0,
+0,96,16,0,2,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,8,0,0,0,255,255,255,255,
+2,0,0,0,1,0,0,0,13,0,0,0,83,109,112,50,
+0,84,101,120,50,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,50,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,50,0
+};
+const BYTE g_FFSampleTex3D3[] = {
+68,88,66,67,115,199,248,60,148,239,79,221,248,116,74,189,
+183,197,234,230,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,184,154,16,255,1,250,209,159,118,180,70,123,
+159,161,218,107,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+3,3,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,7,176,31,0,0,2,0,0,0,160,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,3,0,0,0,88,40,0,4,
+0,112,16,0,3,0,0,0,85,85,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,70,126,16,0,3,0,0,0,
+0,96,16,0,3,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,8,0,0,0,255,255,255,255,
+3,0,0,0,1,0,0,0,13,0,0,0,83,109,112,51,
+0,84,101,120,51,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,51,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,51,0
+};
+const BYTE g_FFSampleTex3D4[] = {
+68,88,66,67,175,88,14,186,68,209,197,62,115,210,225,43,
+70,17,156,98,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,141,231,106,237,112,235,84,199,112,98,106,110,
+179,136,3,131,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+4,4,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,7,176,31,0,0,2,0,0,0,160,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,4,0,0,0,88,40,0,4,
+0,112,16,0,4,0,0,0,85,85,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,70,126,16,0,4,0,0,0,
+0,96,16,0,4,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+4,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,8,0,0,0,255,255,255,255,
+4,0,0,0,1,0,0,0,13,0,0,0,83,109,112,52,
+0,84,101,120,52,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,52,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,52,0
+};
+const BYTE g_FFSampleTex3D5[] = {
+68,88,66,67,21,86,181,156,18,14,15,61,232,192,100,191,
+113,227,128,184,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,175,14,52,173,92,80,63,137,150,55,52,202,
+102,138,186,177,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+5,5,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,7,176,31,0,0,2,0,0,0,160,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,5,0,0,0,88,40,0,4,
+0,112,16,0,5,0,0,0,85,85,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,70,126,16,0,5,0,0,0,
+0,96,16,0,5,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+5,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,8,0,0,0,255,255,255,255,
+5,0,0,0,1,0,0,0,13,0,0,0,83,109,112,53,
+0,84,101,120,53,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,53,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,53,0
+};
+const BYTE g_FFSampleTex3D6[] = {
+68,88,66,67,238,177,249,89,9,22,115,2,214,9,191,51,
+202,23,0,173,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,213,181,254,210,42,235,198,99,147,100,249,38,
+66,54,186,94,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+6,6,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,7,176,31,0,0,2,0,0,0,160,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,6,0,0,0,88,40,0,4,
+0,112,16,0,6,0,0,0,85,85,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,70,126,16,0,6,0,0,0,
+0,96,16,0,6,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+6,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,8,0,0,0,255,255,255,255,
+6,0,0,0,1,0,0,0,13,0,0,0,83,109,112,54,
+0,84,101,120,54,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,54,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,54,0
+};
+const BYTE g_FFSampleTex3D7[] = {
+68,88,66,67,125,134,173,155,128,60,127,159,47,32,107,157,
+237,228,160,55,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,207,197,246,6,50,87,29,171,224,83,43,237,
+229,87,179,100,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+7,7,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,7,176,31,0,0,2,0,0,0,160,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,7,0,0,0,88,40,0,4,
+0,112,16,0,7,0,0,0,85,85,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,70,126,16,0,7,0,0,0,
+0,96,16,0,7,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+7,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,8,0,0,0,255,255,255,255,
+7,0,0,0,1,0,0,0,13,0,0,0,83,109,112,55,
+0,84,101,120,55,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,55,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,55,0
+};
+const BYTE g_FFSampleTexCube0[] = {
+68,88,66,67,33,244,32,125,20,149,175,63,113,66,150,137,
+236,158,74,161,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,113,62,226,192,223,150,191,199,17,37,188,138,
+110,126,21,124,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+0,0,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,7,176,31,0,0,2,0,0,0,152,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,0,0,0,0,88,48,0,4,
+0,112,16,0,0,0,0,0,85,85,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,70,126,16,0,0,0,0,0,
+0,96,16,0,0,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,9,0,0,0,255,255,255,255,
+0,0,0,0,1,0,0,0,13,0,0,0,83,109,112,48,
+0,84,101,120,48,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,48,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,48,0
+};
+const BYTE g_FFSampleTexCube1[] = {
+68,88,66,67,22,79,13,114,141,19,54,166,178,73,140,214,
+63,36,100,186,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,70,89,149,240,122,13,155,26,14,223,34,217,
+148,48,93,72,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+1,1,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,7,176,31,0,0,2,0,0,0,152,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,1,0,0,0,88,48,0,4,
+0,112,16,0,1,0,0,0,85,85,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,70,126,16,0,1,0,0,0,
+0,96,16,0,1,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,9,0,0,0,255,255,255,255,
+1,0,0,0,1,0,0,0,13,0,0,0,83,109,112,49,
+0,84,101,120,49,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,49,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,49,0
+};
+const BYTE g_FFSampleTexCube2[] = {
+68,88,66,67,7,165,35,142,143,214,18,167,193,132,142,213,
+160,150,93,133,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,98,252,179,13,153,4,140,224,14,185,204,193,
+153,209,189,169,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+2,2,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,7,176,31,0,0,2,0,0,0,152,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,2,0,0,0,88,48,0,4,
+0,112,16,0,2,0,0,0,85,85,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,70,126,16,0,2,0,0,0,
+0,96,16,0,2,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,9,0,0,0,255,255,255,255,
+2,0,0,0,1,0,0,0,13,0,0,0,83,109,112,50,
+0,84,101,120,50,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,50,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,50,0
+};
+const BYTE g_FFSampleTexCube3[] = {
+68,88,66,67,70,205,12,245,181,82,205,63,112,32,70,85,
+27,139,180,41,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,180,171,156,216,69,235,175,170,109,153,73,113,
+56,216,63,162,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+3,3,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,7,176,31,0,0,2,0,0,0,152,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,3,0,0,0,88,48,0,4,
+0,112,16,0,3,0,0,0,85,85,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,70,126,16,0,3,0,0,0,
+0,96,16,0,3,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+3,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,9,0,0,0,255,255,255,255,
+3,0,0,0,1,0,0,0,13,0,0,0,83,109,112,51,
+0,84,101,120,51,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,51,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,51,0
+};
+const BYTE g_FFSampleTexCube4[] = {
+68,88,66,67,135,31,144,5,60,204,204,187,32,54,117,90,
+26,33,4,165,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,221,103,249,252,98,213,207,165,83,169,29,179,
+159,36,23,98,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+4,4,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,7,176,31,0,0,2,0,0,0,152,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,4,0,0,0,88,48,0,4,
+0,112,16,0,4,0,0,0,85,85,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,70,126,16,0,4,0,0,0,
+0,96,16,0,4,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+4,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,9,0,0,0,255,255,255,255,
+4,0,0,0,1,0,0,0,13,0,0,0,83,109,112,52,
+0,84,101,120,52,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,52,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,52,0
+};
+const BYTE g_FFSampleTexCube5[] = {
+68,88,66,67,9,169,174,254,19,74,128,174,170,108,76,74,
+237,75,117,36,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,50,41,254,184,236,74,224,61,194,211,228,155,
+17,193,157,56,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+5,5,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,7,176,31,0,0,2,0,0,0,152,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,5,0,0,0,88,48,0,4,
+0,112,16,0,5,0,0,0,85,85,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,70,126,16,0,5,0,0,0,
+0,96,16,0,5,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+5,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,9,0,0,0,255,255,255,255,
+5,0,0,0,1,0,0,0,13,0,0,0,83,109,112,53,
+0,84,101,120,53,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,53,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,53,0
+};
+const BYTE g_FFSampleTexCube6[] = {
+68,88,66,67,91,64,54,105,105,23,11,213,252,58,251,141,
+250,247,181,206,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,123,101,100,54,254,198,63,167,228,138,15,149,
+11,230,115,30,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+6,6,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,7,176,31,0,0,2,0,0,0,152,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,6,0,0,0,88,48,0,4,
+0,112,16,0,6,0,0,0,85,85,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,70,126,16,0,6,0,0,0,
+0,96,16,0,6,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+6,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,9,0,0,0,255,255,255,255,
+6,0,0,0,1,0,0,0,13,0,0,0,83,109,112,54,
+0,84,101,120,54,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,54,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,54,0
+};
+const BYTE g_FFSampleTexCube7[] = {
+68,88,66,67,188,238,36,82,77,132,123,123,98,251,103,197,
+127,182,224,79,1,0,0,0,75,3,0,0,2,0,0,0,
+40,0,0,0,240,2,0,0,76,73,66,70,192,2,0,0,
+68,88,66,67,221,32,245,102,89,40,196,37,67,14,199,175,
+106,249,102,241,1,0,0,0,192,2,0,0,5,0,0,0,
+52,0,0,0,160,0,0,0,12,1,0,0,136,1,0,0,
+68,2,0,0,65,111,110,57,100,0,0,0,100,0,0,0,
+0,2,80,76,60,0,0,0,40,0,0,0,0,0,40,0,
+0,0,40,0,0,0,40,0,1,0,36,0,0,0,40,0,
+7,7,0,0,0,2,80,76,31,0,0,2,0,0,0,128,
+0,0,7,176,31,0,0,2,0,0,0,152,0,8,15,160,
+66,0,0,3,0,0,15,128,0,0,228,176,0,8,228,160,
+1,0,0,2,0,0,15,224,0,0,228,128,255,255,0,0,
+83,72,68,82,100,0,0,0,64,0,240,255,25,0,0,0,
+90,0,0,3,0,96,16,0,7,0,0,0,88,48,0,4,
+0,112,16,0,7,0,0,0,85,85,0,0,95,0,0,3,
+114,16,16,0,0,0,0,0,101,0,0,3,242,32,16,0,
+0,0,0,0,69,0,0,9,242,32,16,0,0,0,0,0,
+70,18,16,0,0,0,0,0,70,126,16,0,7,0,0,0,
+0,96,16,0,7,0,0,0,62,0,0,1,83,84,65,84,
+116,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
+2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,82,68,69,70,180,0,0,0,
+0,0,0,0,0,0,0,0,2,0,0,0,60,0,0,0,
+0,4,70,76,0,129,0,0,134,0,0,0,82,68,49,49,
+60,0,0,0,24,0,0,0,32,0,0,0,40,0,0,0,
+36,0,0,0,12,0,0,0,0,0,0,0,124,0,0,0,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+7,0,0,0,1,0,0,0,1,0,0,0,129,0,0,0,
+2,0,0,0,5,0,0,0,9,0,0,0,255,255,255,255,
+7,0,0,0,1,0,0,0,13,0,0,0,83,109,112,55,
+0,84,101,120,55,0,77,105,99,114,111,115,111,102,116,32,
+40,82,41,32,72,76,83,76,32,83,104,97,100,101,114,32,
+67,111,109,112,105,108,101,114,32,54,46,51,46,57,52,49,
+53,46,48,0,76,70,83,48,116,0,0,0,2,0,0,0,
+8,0,0,0,104,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,
+2,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,
+0,0,0,0,113,0,0,0,0,0,0,0,3,0,0,0,
+1,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,
+1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+255,255,255,255,76,111,97,100,84,101,120,55,0,117,118,0,
+76,73,66,72,83,0,0,0,1,0,0,0,28,0,0,0,
+0,0,0,0,1,0,0,0,20,0,0,0,2,0,0,0,
+74,0,0,0,77,105,99,114,111,115,111,102,116,32,40,82,
+41,32,72,76,83,76,32,83,104,97,100,101,114,32,67,111,
+109,112,105,108,101,114,32,54,46,51,46,57,52,49,53,46,
+48,0,76,111,97,100,84,101,120,55,0
+};
+const BYTE* g_FFSampleTexLib[] = {
+g_FFSampleTex2D0,
+g_FFSampleTex2D1,
+g_FFSampleTex2D2,
+g_FFSampleTex2D3,
+g_FFSampleTex2D4,
+g_FFSampleTex2D5,
+g_FFSampleTex2D6,
+g_FFSampleTex2D7,
+g_FFSampleTexProj0,
+g_FFSampleTexProj1,
+g_FFSampleTexProj2,
+g_FFSampleTexProj3,
+g_FFSampleTexProj4,
+g_FFSampleTexProj5,
+g_FFSampleTexProj6,
+g_FFSampleTexProj7,
+g_FFSampleTex3D0,
+g_FFSampleTex3D1,
+g_FFSampleTex3D2,
+g_FFSampleTex3D3,
+g_FFSampleTex3D4,
+g_FFSampleTex3D5,
+g_FFSampleTex3D6,
+g_FFSampleTex3D7,
+g_FFSampleTexCube0,
+g_FFSampleTexCube1,
+g_FFSampleTexCube2,
+g_FFSampleTexCube3,
+g_FFSampleTexCube4,
+g_FFSampleTexCube5,
+g_FFSampleTexCube6,
+g_FFSampleTexCube7,
+};
+const size_t g_FFSampleTexLibSize[] = {
+sizeof(g_FFSampleTex2D0),
+sizeof(g_FFSampleTex2D1),
+sizeof(g_FFSampleTex2D2),
+sizeof(g_FFSampleTex2D3),
+sizeof(g_FFSampleTex2D4),
+sizeof(g_FFSampleTex2D5),
+sizeof(g_FFSampleTex2D6),
+sizeof(g_FFSampleTex2D7),
+sizeof(g_FFSampleTexProj0),
+sizeof(g_FFSampleTexProj1),
+sizeof(g_FFSampleTexProj2),
+sizeof(g_FFSampleTexProj3),
+sizeof(g_FFSampleTexProj4),
+sizeof(g_FFSampleTexProj5),
+sizeof(g_FFSampleTexProj6),
+sizeof(g_FFSampleTexProj7),
+sizeof(g_FFSampleTex3D0),
+sizeof(g_FFSampleTex3D1),
+sizeof(g_FFSampleTex3D2),
+sizeof(g_FFSampleTex3D3),
+sizeof(g_FFSampleTex3D4),
+sizeof(g_FFSampleTex3D5),
+sizeof(g_FFSampleTex3D6),
+sizeof(g_FFSampleTex3D7),
+sizeof(g_FFSampleTexCube0),
+sizeof(g_FFSampleTexCube1),
+sizeof(g_FFSampleTexCube2),
+sizeof(g_FFSampleTexCube3),
+sizeof(g_FFSampleTexCube4),
+sizeof(g_FFSampleTexCube5),
+sizeof(g_FFSampleTexCube6),
+sizeof(g_FFSampleTexCube7),
+};
diff --git a/Runtime/GfxDevice/d3d11/InternalShaders/FFShaderLib.hlsl b/Runtime/GfxDevice/d3d11/InternalShaders/FFShaderLib.hlsl
new file mode 100644
index 0000000..fa2a89a
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/InternalShaders/FFShaderLib.hlsl
@@ -0,0 +1,172 @@
+
+cbuffer UnityFFVertex {
+ float4x4 ff_matrix_mvp; // 0
+ float4x4 ff_matrix_mv; // 4
+ float4 ff_vec_color; // 8
+ float4 ff_vec_ambient; // 9
+ float4 ff_light_color[8]; // 10
+ float4 ff_light_pos[8]; // 18
+ float4 ff_light_atten[8]; // 26
+ float4 ff_light_spot[8]; // 34
+ float4 ff_mat_diffuse; // 42
+ float4 ff_mat_ambient; // 43
+ float4 ff_mat_spec; // 44
+ float4 ff_mat_emission; // 45
+ float4x4 ff_matrix_tex[4]; // 46
+ float4 ff_fog_vs; // 62
+}; // 62
+
+cbuffer UnityFFPixel {
+ float4 ff_vec_colors[8]; // 0
+ float ff_alpha_ref; // 8
+ float4 ff_fog_ps; // 9
+};
+
+
+export float4 LoadVertexColor(float4 vc) { return vc; }
+export float4 LoadVertexColorUniform() { return ff_vec_color; }
+export float3 LoadEyePos(float4 vertex) { return mul (ff_matrix_mv, vertex).xyz; }
+export float3 LoadEyeNormal(float3 normal) { return normalize (mul ((float3x3)ff_matrix_mv, normal).xyz); } //@TODO: proper normal matrix
+export float3 LoadZero() { return 0.0; }
+export float3 LoadViewDir(float3 eyePos) { return -normalize(eyePos); }
+export float3 LoadEyeRefl(float3 viewDir, float3 eyeNormal) { return 2.0f * dot (viewDir, eyeNormal) * eyeNormal - viewDir; }
+export float4 LoadAmbientColor() { return ff_mat_ambient; }
+export float4 LoadDiffuseColor() { return ff_mat_diffuse; }
+export float4 LoadEmissionColor() { return ff_mat_emission; }
+
+export float3 InitLightColor(float4 emission, float4 ambient) { return emission.rgb + ambient.rgb * ff_vec_ambient.rgb; }
+
+float3 ComputeLighting (int idx, float3 dirToLight, float3 eyeNormal, float3 viewDir, float4 diffuseColor, float atten, inout float3 specColor) {
+ float NdotL = max(dot(eyeNormal, dirToLight), 0.0);
+ float3 color = NdotL * diffuseColor.rgb * ff_light_color[idx].rgb;
+ return color * atten;
+}
+float3 ComputeLightingSpec (int idx, float3 dirToLight, float3 eyeNormal, float3 viewDir, float4 diffuseColor, float atten, inout float3 specColor) {
+ float NdotL = max(dot(eyeNormal, dirToLight), 0.0);
+ float3 color = NdotL * diffuseColor.rgb * ff_light_color[idx].rgb;
+ if (NdotL > 0.0) {
+ float3 h = normalize(dirToLight + viewDir);
+ float HdotN = max(dot(eyeNormal, h), 0.0);
+ float sp = saturate(pow(HdotN, ff_mat_spec.w));
+ specColor += atten * sp * ff_light_color[idx].rgb;
+ }
+ return color * atten;
+}
+float3 ComputeSpotLight(int idx, float3 eyePosition, float3 eyeNormal, float3 viewDir, float4 diffuseColor, inout float3 specColor) {
+ float3 dirToLight = ff_light_pos[idx].xyz - eyePosition * ff_light_pos[idx].w;
+ float distSqr = dot(dirToLight, dirToLight);
+ float att = 1.0 / (1.0 + ff_light_atten[idx].z * distSqr);
+ if (ff_light_pos[idx].w != 0 && distSqr > ff_light_atten[idx].w) att = 0.0; // set to 0 if outside of range
+ dirToLight *= rsqrt(distSqr);
+ float rho = max(dot(dirToLight, ff_light_spot[idx].xyz), 0.0);
+ float spotAtt = (rho - ff_light_atten[idx].x) * ff_light_atten[idx].y;
+ spotAtt = saturate(spotAtt);
+ return min (ComputeLighting (idx, dirToLight, eyeNormal, viewDir, diffuseColor, att*spotAtt, specColor), 1.0);
+}
+float3 ComputeSpotLightSpec(int idx, float3 eyePosition, float3 eyeNormal, float3 viewDir, float4 diffuseColor, inout float3 specColor) {
+ float3 dirToLight = ff_light_pos[idx].xyz - eyePosition * ff_light_pos[idx].w;
+ float distSqr = dot(dirToLight, dirToLight);
+ float att = 1.0 / (1.0 + ff_light_atten[idx].z * distSqr);
+ if (ff_light_pos[idx].w != 0 && distSqr > ff_light_atten[idx].w) att = 0.0; // set to 0 if outside of range
+ dirToLight *= rsqrt(distSqr);
+ float rho = max(dot(dirToLight, ff_light_spot[idx].xyz), 0.0);
+ float spotAtt = (rho - ff_light_atten[idx].x) * ff_light_atten[idx].y;
+ spotAtt = saturate(spotAtt);
+ return min (ComputeLightingSpec (idx, dirToLight, eyeNormal, viewDir, diffuseColor, att*spotAtt, specColor), 1.0);
+}
+#define SPOT_LIGHT(n) \
+export float3 ComputeSpotLight##n(float3 eyePosition, float3 eyeNormal, float3 viewDir, float4 diffuseColor, inout float3 specColor, float3 amb) { \
+ float3 l = amb; \
+ for (int i = 0; i < n; ++i) \
+ l += ComputeSpotLight(i, eyePosition, eyeNormal, viewDir, diffuseColor, specColor); \
+ return l; \
+} \
+export float3 ComputeSpotLightSpec##n(float3 eyePosition, float3 eyeNormal, float3 viewDir, float4 diffuseColor, inout float3 specColor, float3 amb) { \
+ float3 l = amb; \
+ for (int i = 0; i < n; ++i) \
+ l += ComputeSpotLightSpec(i, eyePosition, eyeNormal, viewDir, diffuseColor, specColor); \
+ return l; \
+}
+SPOT_LIGHT(0)
+SPOT_LIGHT(1)
+SPOT_LIGHT(2)
+SPOT_LIGHT(3)
+SPOT_LIGHT(4)
+SPOT_LIGHT(5)
+SPOT_LIGHT(6)
+SPOT_LIGHT(7)
+SPOT_LIGHT(8)
+
+export float4 LoadLightingColor(float3 lcolor, float4 diffcolor) { return float4(lcolor.rgb, diffcolor.a); }
+
+export float4 TransformVertex(float4 vertex) { return mul (ff_matrix_mvp, vertex); }
+
+export float4 Saturate4(float4 c) { return saturate(c); }
+export float3 Saturate3(float3 c) { return saturate(c); }
+export float3 Load3(float3 c) { return c; }
+export float3 ModulateSpec(float3 c) { return c * ff_mat_spec.rgb; }
+
+export float4 MultiplyUV0(float4 uv) { return mul(ff_matrix_tex[0], uv); }
+export float4 MultiplyUV1(float4 uv) { return mul(ff_matrix_tex[1], uv); }
+export float4 MultiplyUV2(float4 uv) { return mul(ff_matrix_tex[2], uv); }
+export float4 MultiplyUV3(float4 uv) { return mul(ff_matrix_tex[3], uv); }
+export float4 MultiplyUV4(float4 uv) { return uv; }
+export float4 MultiplyUV5(float4 uv) { return uv; }
+export float4 MultiplyUV6(float4 uv) { return uv; }
+export float4 MultiplyUV7(float4 uv) { return uv; }
+
+export float4 UVSphereMap(float3 eyeRefl) { return float4(eyeRefl.xy / (2.0*sqrt(eyeRefl.x*eyeRefl.x + eyeRefl.y*eyeRefl.y + (eyeRefl.z+1)*(eyeRefl.z+1))) + 0.5, 0, 1); }
+export float4 Float3to4(float3 v) { return float4(v.xyz,1); }
+
+export float4 LoadConstantColor0() { return ff_vec_colors[0]; }
+export float4 LoadConstantColor1() { return ff_vec_colors[1]; }
+export float4 LoadConstantColor2() { return ff_vec_colors[2]; }
+export float4 LoadConstantColor3() { return ff_vec_colors[3]; }
+export float4 LoadConstantColor4() { return ff_vec_colors[4]; }
+export float4 LoadConstantColor5() { return ff_vec_colors[5]; }
+export float4 LoadConstantColor6() { return ff_vec_colors[6]; }
+export float4 LoadConstantColor7() { return ff_vec_colors[7]; }
+
+export float OneMinus1(float v) { return 1.0-v; }
+export float3 OneMinus3(float3 v) { return 1.0-v; }
+export float4 OneMinus4(float4 v) { return 1.0-v; }
+
+export float4 CombReplace (float4 a) { return a; }
+export float4 CombModulate (float4 a, float4 b) { return a * b; }
+export float4 CombAdd (float4 a, float4 b) { return a + b; }
+export float4 CombAddSigned(float4 a, float4 b) { return a + b - 0.5; }
+export float4 CombSubtract (float4 a, float4 b) { return a - b; }
+export float4 CombLerp (float4 a, float4 b, float4 c) { return lerp(b, a, c.a); }
+export float4 CombDot3 (float4 a, float4 b) { float3 r = 4.0 * dot(a.rgb-0.5, b.rgb-0.5); return float4(r, a.a); }
+export float4 CombDot3rgba (float4 a, float4 b) { return 4.0 * dot(a.rgb-0.5, b.rgb-0.5); }
+export float4 CombMulAdd (float4 a, float4 b, float4 c) { return a * c.a + b; }
+export float4 CombMulSub (float4 a, float4 b, float4 c) { return a * c.a - b; }
+export float4 CombMulAddSigned(float4 a, float4 b, float4 c) { return a * c.a + b - 0.5; }
+
+export float4 Scale2(float4 a) { return a + a; }
+export float4 Scale4(float4 a) { return a * 4; }
+
+export float4 AddSpec(float4 col, float3 spec) { col.rgb += spec; return col; }
+export float4 CombineAlpha(float4 c, float4 a) { return float4(c.rgb, a.a); }
+
+export float FogLinear(float3 eyePos) {
+ return saturate(length(eyePos) * ff_fog_vs.z + ff_fog_vs.w);
+}
+export float FogExp(float3 eyePos) {
+ return saturate(exp2(-(length(eyePos) * ff_fog_vs.y)));
+}
+export float FogExp2(float3 eyePos) {
+ float f = length(eyePos) * ff_fog_vs.y;
+ return saturate(exp2(-f * f));
+}
+export float4 ApplyFog(float4 col, float ifog) {
+ return float4(lerp(ff_fog_ps.rgb, col.rgb, ifog), col.a);
+}
+
+export float4 AlphaTestNever(float4 col) { discard; return col; }
+export float4 AlphaTestLess(float4 col) { if (!(col.a < ff_alpha_ref)) discard; return col; }
+export float4 AlphaTestEqual(float4 col) { if (!(col.a == ff_alpha_ref)) discard; return col; }
+export float4 AlphaTestLEqual(float4 col) { if (!(col.a <= ff_alpha_ref)) discard; return col; }
+export float4 AlphaTestGreater(float4 col) { if (!(col.a > ff_alpha_ref)) discard; return col; }
+export float4 AlphaTestNotEqual(float4 col) { if (!(col.a != ff_alpha_ref)) discard; return col; }
+export float4 AlphaTestGEqual(float4 col) { if (!(col.a >= ff_alpha_ref)) discard; return col; }
diff --git a/Runtime/GfxDevice/d3d11/InternalShaders/builtin.h b/Runtime/GfxDevice/d3d11/InternalShaders/builtin.h
new file mode 100644
index 0000000..737bd42
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/InternalShaders/builtin.h
@@ -0,0 +1,19755 @@
+//
+//
+// Autogenerated file. Do not modify!
+//
+//
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_1_32
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_1_32._fxctmp
+// /EStreamOutSkinVS_Position_1_32 /D BONESPERVERTEX=1 /D BONECOUNT=32
+// internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[32]; // Offset: 0 Size: 1536
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[96], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+imul null, r0.x, v3.x, l(3)
+mov r1.xyz, v0.xyzx
+mov r1.w, l(1.000000)
+dp4 o0.x, r1.xyzw, cb0[r0.x + 0].xyzw
+dp4 o0.y, r1.xyzw, cb0[r0.x + 1].xyzw
+dp4 o0.z, r1.xyzw, cb0[r0.x + 2].xyzw
+mov o1.xyz, l(0,0,0,0)
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 9 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_1_32[] =
+{
+ 68, 88, 66, 67, 172, 110,
+ 173, 231, 150, 179, 82, 99,
+ 5, 94, 37, 107, 119, 107,
+ 152, 219, 1, 0, 0, 0,
+ 220, 3, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 96, 3, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 6, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 92, 1, 0, 0,
+ 64, 0, 1, 0, 87, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 96, 0, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 38, 0, 0, 8,
+ 0, 208, 0, 0, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 16, 16, 0, 3, 0,
+ 0, 0, 1, 64, 0, 0,
+ 3, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 1, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 8, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 9, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_2_32
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_2_32._fxctmp
+// /EStreamOutSkinVS_Position_2_32 /D BONESPERVERTEX=2 /D BONECOUNT=32
+// internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[32]; // Offset: 0 Size: 1536
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[96], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 3
+imul null, r0.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r1.xyzw
+dp4 o0.y, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r0.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r1.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+mov o1.xyz, l(0,0,0,0)
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 15 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_2_32[] =
+{
+ 68, 88, 66, 67, 78, 255,
+ 107, 230, 57, 57, 29, 189,
+ 107, 149, 130, 121, 255, 237,
+ 206, 251, 1, 0, 0, 0,
+ 252, 4, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 128, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 6, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 84, 2, 0, 0, 64, 0,
+ 1, 0, 149, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 96, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 3, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 17, 0,
+ 0, 7, 34, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 0, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 8, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 15, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 6, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_4_32
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_4_32._fxctmp
+// /EStreamOutSkinVS_Position_4_32 /D BONESPERVERTEX=4 /D BONECOUNT=32
+// internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[32]; // Offset: 0 Size: 1536
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[96], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 3
+imul null, r0.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mad r1.xyzw, v3.zzzz, cb0[r0.z + 0].xyzw, r1.xyzw
+mad r1.xyzw, v3.wwww, cb0[r0.w + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r1.xyzw
+mad r1.xyzw, v3.zzzz, cb0[r0.z + 1].xyzw, r1.xyzw
+mad r1.xyzw, v3.wwww, cb0[r0.w + 1].xyzw, r1.xyzw
+dp4 o0.y, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r1.xyzw
+mad r1.xyzw, v3.zzzz, cb0[r0.z + 2].xyzw, r1.xyzw
+mad r0.xyzw, v3.wwww, cb0[r0.w + 2].xyzw, r1.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+mov o1.xyz, l(0,0,0,0)
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 21 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_4_32[] =
+{
+ 68, 88, 66, 67, 190, 235,
+ 32, 161, 70, 54, 170, 157,
+ 239, 18, 118, 92, 60, 255,
+ 126, 161, 1, 0, 0, 0,
+ 20, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 152, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 6, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 108, 3, 0, 0, 64, 0,
+ 1, 0, 219, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 96, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 3, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 30,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 1, 0, 0, 0, 166, 26,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 42, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 2, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 1, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 246, 31, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 166, 26,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 42, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 0, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 17, 0,
+ 0, 7, 66, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 8,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 242, 32, 16, 0,
+ 2, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 21, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 6, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_1_64
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_1_64._fxctmp
+// /EStreamOutSkinVS_Position_1_64 /D BONESPERVERTEX=1 /D BONECOUNT=64
+// internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[64]; // Offset: 0 Size: 3072
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[192], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+imul null, r0.x, v3.x, l(3)
+mov r1.xyz, v0.xyzx
+mov r1.w, l(1.000000)
+dp4 o0.x, r1.xyzw, cb0[r0.x + 0].xyzw
+dp4 o0.y, r1.xyzw, cb0[r0.x + 1].xyzw
+dp4 o0.z, r1.xyzw, cb0[r0.x + 2].xyzw
+mov o1.xyz, l(0,0,0,0)
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 9 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_1_64[] =
+{
+ 68, 88, 66, 67, 147, 148,
+ 150, 219, 6, 76, 210, 165,
+ 175, 243, 177, 17, 178, 32,
+ 216, 209, 1, 0, 0, 0,
+ 220, 3, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 96, 3, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 12, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 12,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 92, 1, 0, 0,
+ 64, 0, 1, 0, 87, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 192, 0, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 38, 0, 0, 8,
+ 0, 208, 0, 0, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 16, 16, 0, 3, 0,
+ 0, 0, 1, 64, 0, 0,
+ 3, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 1, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 8, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 9, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_2_64
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_2_64._fxctmp
+// /EStreamOutSkinVS_Position_2_64 /D BONESPERVERTEX=2 /D BONECOUNT=64
+// internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[64]; // Offset: 0 Size: 3072
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[192], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 3
+imul null, r0.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r1.xyzw
+dp4 o0.y, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r0.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r1.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+mov o1.xyz, l(0,0,0,0)
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 15 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_2_64[] =
+{
+ 68, 88, 66, 67, 2, 81,
+ 122, 207, 247, 163, 197, 92,
+ 153, 201, 157, 75, 235, 157,
+ 250, 13, 1, 0, 0, 0,
+ 252, 4, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 128, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 12, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 12,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 84, 2, 0, 0, 64, 0,
+ 1, 0, 149, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 192, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 3, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 17, 0,
+ 0, 7, 34, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 0, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 8, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 15, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 6, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_4_64
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_4_64._fxctmp
+// /EStreamOutSkinVS_Position_4_64 /D BONESPERVERTEX=4 /D BONECOUNT=64
+// internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[64]; // Offset: 0 Size: 3072
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[192], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 3
+imul null, r0.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mad r1.xyzw, v3.zzzz, cb0[r0.z + 0].xyzw, r1.xyzw
+mad r1.xyzw, v3.wwww, cb0[r0.w + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r1.xyzw
+mad r1.xyzw, v3.zzzz, cb0[r0.z + 1].xyzw, r1.xyzw
+mad r1.xyzw, v3.wwww, cb0[r0.w + 1].xyzw, r1.xyzw
+dp4 o0.y, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r1.xyzw
+mad r1.xyzw, v3.zzzz, cb0[r0.z + 2].xyzw, r1.xyzw
+mad r0.xyzw, v3.wwww, cb0[r0.w + 2].xyzw, r1.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+mov o1.xyz, l(0,0,0,0)
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 21 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_4_64[] =
+{
+ 68, 88, 66, 67, 11, 70,
+ 190, 106, 178, 229, 109, 109,
+ 89, 69, 130, 9, 207, 137,
+ 221, 41, 1, 0, 0, 0,
+ 20, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 152, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 12, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 12,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 108, 3, 0, 0, 64, 0,
+ 1, 0, 219, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 192, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 3, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 30,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 1, 0, 0, 0, 166, 26,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 42, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 2, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 1, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 246, 31, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 166, 26,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 42, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 0, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 17, 0,
+ 0, 7, 66, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 8,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 242, 32, 16, 0,
+ 2, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 21, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 6, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_1_128
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_1_128._fxctmp
+// /EStreamOutSkinVS_Position_1_128 /D BONESPERVERTEX=1 /D BONECOUNT=128
+// internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[128]; // Offset: 0 Size: 6144
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[384], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+imul null, r0.x, v3.x, l(3)
+mov r1.xyz, v0.xyzx
+mov r1.w, l(1.000000)
+dp4 o0.x, r1.xyzw, cb0[r0.x + 0].xyzw
+dp4 o0.y, r1.xyzw, cb0[r0.x + 1].xyzw
+dp4 o0.z, r1.xyzw, cb0[r0.x + 2].xyzw
+mov o1.xyz, l(0,0,0,0)
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 9 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_1_128[] =
+{
+ 68, 88, 66, 67, 7, 222,
+ 0, 205, 11, 21, 104, 139,
+ 8, 62, 57, 246, 1, 70,
+ 233, 230, 1, 0, 0, 0,
+ 220, 3, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 96, 3, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 24, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 24,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 128, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 92, 1, 0, 0,
+ 64, 0, 1, 0, 87, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 128, 1, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 38, 0, 0, 8,
+ 0, 208, 0, 0, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 16, 16, 0, 3, 0,
+ 0, 0, 1, 64, 0, 0,
+ 3, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 1, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 8, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 9, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_2_128
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_2_128._fxctmp
+// /EStreamOutSkinVS_Position_2_128 /D BONESPERVERTEX=2 /D BONECOUNT=128
+// internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[128]; // Offset: 0 Size: 6144
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[384], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 3
+imul null, r0.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r1.xyzw
+dp4 o0.y, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r0.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r1.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+mov o1.xyz, l(0,0,0,0)
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 15 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_2_128[] =
+{
+ 68, 88, 66, 67, 131, 165,
+ 122, 210, 205, 5, 71, 238,
+ 145, 203, 104, 57, 229, 1,
+ 240, 89, 1, 0, 0, 0,
+ 252, 4, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 128, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 24, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 24,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 128, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 84, 2, 0, 0, 64, 0,
+ 1, 0, 149, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 128, 1, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 3, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 17, 0,
+ 0, 7, 34, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 0, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 8, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 15, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 6, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_4_128
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_4_128._fxctmp
+// /EStreamOutSkinVS_Position_4_128 /D BONESPERVERTEX=4 /D BONECOUNT=128
+// internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[128]; // Offset: 0 Size: 6144
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[384], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 3
+imul null, r0.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mad r1.xyzw, v3.zzzz, cb0[r0.z + 0].xyzw, r1.xyzw
+mad r1.xyzw, v3.wwww, cb0[r0.w + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r1.xyzw
+mad r1.xyzw, v3.zzzz, cb0[r0.z + 1].xyzw, r1.xyzw
+mad r1.xyzw, v3.wwww, cb0[r0.w + 1].xyzw, r1.xyzw
+dp4 o0.y, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r1.xyzw
+mad r1.xyzw, v3.zzzz, cb0[r0.z + 2].xyzw, r1.xyzw
+mad r0.xyzw, v3.wwww, cb0[r0.w + 2].xyzw, r1.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+mov o1.xyz, l(0,0,0,0)
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 21 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_4_128[] =
+{
+ 68, 88, 66, 67, 57, 216,
+ 246, 73, 126, 196, 169, 105,
+ 89, 156, 29, 146, 202, 235,
+ 72, 227, 1, 0, 0, 0,
+ 20, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 152, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 24, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 24,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 128, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 108, 3, 0, 0, 64, 0,
+ 1, 0, 219, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 128, 1, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 3, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 30,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 1, 0, 0, 0, 166, 26,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 42, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 2, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 1, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 246, 31, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 166, 26,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 42, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 0, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 17, 0,
+ 0, 7, 66, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 8,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 242, 32, 16, 0,
+ 2, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 21, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 6, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_1_512
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_1_512._fxctmp
+// /EStreamOutSkinVS_Position_1_512 /D BONESPERVERTEX=1 /D BONECOUNT=512
+// internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[512]; // Offset: 0 Size: 24576
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[1536], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+imul null, r0.x, v3.x, l(3)
+mov r1.xyz, v0.xyzx
+mov r1.w, l(1.000000)
+dp4 o0.x, r1.xyzw, cb0[r0.x + 0].xyzw
+dp4 o0.y, r1.xyzw, cb0[r0.x + 1].xyzw
+dp4 o0.z, r1.xyzw, cb0[r0.x + 2].xyzw
+mov o1.xyz, l(0,0,0,0)
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 9 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_1_512[] =
+{
+ 68, 88, 66, 67, 109, 180,
+ 27, 53, 43, 110, 111, 181,
+ 254, 232, 170, 150, 51, 248,
+ 173, 45, 1, 0, 0, 0,
+ 220, 3, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 96, 3, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 96, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 96,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 92, 1, 0, 0,
+ 64, 0, 1, 0, 87, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 0, 6, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 38, 0, 0, 8,
+ 0, 208, 0, 0, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 16, 16, 0, 3, 0,
+ 0, 0, 1, 64, 0, 0,
+ 3, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 1, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 8, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 9, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_2_512
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_2_512._fxctmp
+// /EStreamOutSkinVS_Position_2_512 /D BONESPERVERTEX=2 /D BONECOUNT=512
+// internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[512]; // Offset: 0 Size: 24576
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[1536], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 3
+imul null, r0.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r1.xyzw
+dp4 o0.y, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r0.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r1.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+mov o1.xyz, l(0,0,0,0)
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 15 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_2_512[] =
+{
+ 68, 88, 66, 67, 191, 248,
+ 159, 81, 159, 73, 255, 194,
+ 53, 47, 141, 25, 191, 205,
+ 45, 208, 1, 0, 0, 0,
+ 252, 4, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 128, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 96, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 96,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 84, 2, 0, 0, 64, 0,
+ 1, 0, 149, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 6, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 3, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 17, 0,
+ 0, 7, 34, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 0, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 8, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 15, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 6, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_4_512
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_4_512._fxctmp
+// /EStreamOutSkinVS_Position_4_512 /D BONESPERVERTEX=4 /D BONECOUNT=512
+// internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[512]; // Offset: 0 Size: 24576
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[1536], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 3
+imul null, r0.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mad r1.xyzw, v3.zzzz, cb0[r0.z + 0].xyzw, r1.xyzw
+mad r1.xyzw, v3.wwww, cb0[r0.w + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r1.xyzw
+mad r1.xyzw, v3.zzzz, cb0[r0.z + 1].xyzw, r1.xyzw
+mad r1.xyzw, v3.wwww, cb0[r0.w + 1].xyzw, r1.xyzw
+dp4 o0.y, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r1.xyzw
+mad r1.xyzw, v3.zzzz, cb0[r0.z + 2].xyzw, r1.xyzw
+mad r0.xyzw, v3.wwww, cb0[r0.w + 2].xyzw, r1.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+mov o1.xyz, l(0,0,0,0)
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 21 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_4_512[] =
+{
+ 68, 88, 66, 67, 70, 119,
+ 204, 225, 31, 226, 142, 0,
+ 115, 56, 144, 156, 34, 203,
+ 86, 29, 1, 0, 0, 0,
+ 20, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 152, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 96, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 96,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 108, 3, 0, 0, 64, 0,
+ 1, 0, 219, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 6, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 3, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 30,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 1, 0, 0, 0, 166, 26,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 42, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 2, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 1, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 246, 31, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 166, 26,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 42, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 0, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 17, 0,
+ 0, 7, 66, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 8,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 242, 32, 16, 0,
+ 2, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 21, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 6, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_1_1024
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_1_1024._fxctmp
+// /EStreamOutSkinVS_Position_1_1024 /D BONESPERVERTEX=1 /D BONECOUNT=1024
+// internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[1024]; // Offset: 0 Size: 49152
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[3072], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+imul null, r0.x, v3.x, l(3)
+mov r1.xyz, v0.xyzx
+mov r1.w, l(1.000000)
+dp4 o0.x, r1.xyzw, cb0[r0.x + 0].xyzw
+dp4 o0.y, r1.xyzw, cb0[r0.x + 1].xyzw
+dp4 o0.z, r1.xyzw, cb0[r0.x + 2].xyzw
+mov o1.xyz, l(0,0,0,0)
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 9 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_1_1024[] =
+{
+ 68, 88, 66, 67, 117, 227,
+ 218, 150, 67, 53, 102, 246,
+ 156, 73, 114, 242, 63, 65,
+ 53, 148, 1, 0, 0, 0,
+ 220, 3, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 96, 3, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 192, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 192,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 92, 1, 0, 0,
+ 64, 0, 1, 0, 87, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 0, 12, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 38, 0, 0, 8,
+ 0, 208, 0, 0, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 16, 16, 0, 3, 0,
+ 0, 0, 1, 64, 0, 0,
+ 3, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 1, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 8, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 9, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_2_1024
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_2_1024._fxctmp
+// /EStreamOutSkinVS_Position_2_1024 /D BONESPERVERTEX=2 /D BONECOUNT=1024
+// internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[1024]; // Offset: 0 Size: 49152
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[3072], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 3
+imul null, r0.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r1.xyzw
+dp4 o0.y, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r0.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r1.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+mov o1.xyz, l(0,0,0,0)
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 15 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_2_1024[] =
+{
+ 68, 88, 66, 67, 13, 71,
+ 157, 50, 29, 201, 82, 174,
+ 198, 214, 113, 46, 102, 98,
+ 97, 207, 1, 0, 0, 0,
+ 252, 4, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 128, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 192, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 192,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 84, 2, 0, 0, 64, 0,
+ 1, 0, 149, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 12, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 3, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 17, 0,
+ 0, 7, 34, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 0, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 8, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 15, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 6, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_4_1024
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_4_1024._fxctmp
+// /EStreamOutSkinVS_Position_4_1024 /D BONESPERVERTEX=4 /D BONECOUNT=1024
+// internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[1024]; // Offset: 0 Size: 49152
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[3072], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 3
+imul null, r0.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mad r1.xyzw, v3.zzzz, cb0[r0.z + 0].xyzw, r1.xyzw
+mad r1.xyzw, v3.wwww, cb0[r0.w + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r1.xyzw
+mad r1.xyzw, v3.zzzz, cb0[r0.z + 1].xyzw, r1.xyzw
+mad r1.xyzw, v3.wwww, cb0[r0.w + 1].xyzw, r1.xyzw
+dp4 o0.y, r2.xyzw, r1.xyzw
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r1.xyzw
+mad r1.xyzw, v3.zzzz, cb0[r0.z + 2].xyzw, r1.xyzw
+mad r0.xyzw, v3.wwww, cb0[r0.w + 2].xyzw, r1.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+mov o1.xyz, l(0,0,0,0)
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 21 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_4_1024[] =
+{
+ 68, 88, 66, 67, 204, 116,
+ 52, 195, 65, 155, 137, 172,
+ 38, 110, 158, 215, 19, 229,
+ 210, 140, 1, 0, 0, 0,
+ 20, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 152, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 192, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 192,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 108, 3, 0, 0, 64, 0,
+ 1, 0, 219, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 12, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 3, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 30,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 1, 0, 0, 0, 166, 26,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 42, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 2, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 1, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 246, 31, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 166, 26,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 42, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 0, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 17, 0,
+ 0, 7, 66, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 8,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 242, 32, 16, 0,
+ 2, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 21, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 6, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_1_32
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Normal_1_32._fxctmp
+// /EStreamOutSkinVS_Position_Normal_1_32 /D BONESPERVERTEX=1 /D
+// BONECOUNT=32 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[32]; // Offset: 0 Size: 1536
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[96], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.x, v3.x, l(3)
+dp4 o0.x, r0.xyzw, cb0[r1.x + 0].xyzw
+dp4 o0.y, r0.xyzw, cb0[r1.x + 1].xyzw
+dp4 o0.z, r0.xyzw, cb0[r1.x + 2].xyzw
+dp3 r0.x, v1.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v1.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v1.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 14 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_1_32[] =
+{
+ 68, 88, 66, 67, 37, 181,
+ 180, 243, 4, 107, 240, 75,
+ 171, 127, 67, 83, 143, 167,
+ 109, 6, 1, 0, 0, 0,
+ 136, 4, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 12, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 6, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 8, 2, 0, 0,
+ 64, 0, 1, 0, 130, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 96, 0, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 1, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 38, 0, 0, 8, 0, 208,
+ 0, 0, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 16,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 3, 0,
+ 0, 0, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 4, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 10,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 130,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 68, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 242, 32, 16, 0,
+ 2, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 14, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 9, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_2_32
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Normal_2_32._fxctmp
+// /EStreamOutSkinVS_Position_Normal_2_32 /D BONESPERVERTEX=2 /D
+// BONECOUNT=32 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[32]; // Offset: 0 Size: 1536
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[96], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+imul null, r0.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+dp3 r1.x, v1.xyzx, r1.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r3.xyzw
+dp4 o0.y, r2.xyzw, r3.xyzw
+dp3 r1.y, v1.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r0.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r3.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+dp3 r1.z, v1.xyzx, r0.xyzx
+dp3 r0.x, r1.xyzx, r1.xyzx
+rsq r0.x, r0.x
+mul o1.xyz, r0.xxxx, r1.xyzx
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 20 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_2_32[] =
+{
+ 68, 88, 66, 67, 80, 207,
+ 201, 191, 158, 92, 237, 100,
+ 214, 39, 226, 181, 219, 93,
+ 192, 204, 1, 0, 0, 0,
+ 136, 5, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 12, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 6, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 224, 2, 0, 0, 64, 0,
+ 1, 0, 184, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 96, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 16, 0, 0, 7, 34, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 66, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 68, 0, 0, 5,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 1, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 54, 0, 0, 8,
+ 242, 32, 16, 0, 2, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 20, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 12, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_4_32
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Normal_4_32._fxctmp
+// /EStreamOutSkinVS_Position_Normal_4_32 /D BONESPERVERTEX=4 /D
+// BONECOUNT=32 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[32]; // Offset: 0 Size: 1536
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[96], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.zzzz, cb0[r1.z + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.wwww, cb0[r1.w + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+dp3 r2.x, v1.xyzx, r2.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.wwww, cb0[r1.w + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+dp3 r2.y, v1.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 2].xyzw, r3.xyzw
+mad r1.xyzw, v3.wwww, cb0[r1.w + 2].xyzw, r3.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r2.z, v1.xyzx, r1.xyzx
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o1.xyz, r0.xxxx, r2.xyzx
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 26 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_4_32[] =
+{
+ 68, 88, 66, 67, 206, 29,
+ 212, 36, 176, 114, 235, 5,
+ 52, 112, 248, 230, 26, 143,
+ 41, 13, 1, 0, 0, 0,
+ 160, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 36, 6, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 6, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 248, 3, 0, 0, 64, 0,
+ 1, 0, 254, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 96, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 30, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 2, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 34, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 66, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 68, 0, 0, 5, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 6, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 54, 0, 0, 8, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 26, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 12, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_1_64
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Normal_1_64._fxctmp
+// /EStreamOutSkinVS_Position_Normal_1_64 /D BONESPERVERTEX=1 /D
+// BONECOUNT=64 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[64]; // Offset: 0 Size: 3072
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[192], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.x, v3.x, l(3)
+dp4 o0.x, r0.xyzw, cb0[r1.x + 0].xyzw
+dp4 o0.y, r0.xyzw, cb0[r1.x + 1].xyzw
+dp4 o0.z, r0.xyzw, cb0[r1.x + 2].xyzw
+dp3 r0.x, v1.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v1.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v1.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 14 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_1_64[] =
+{
+ 68, 88, 66, 67, 50, 106,
+ 7, 74, 77, 162, 98, 80,
+ 50, 217, 99, 60, 79, 64,
+ 217, 67, 1, 0, 0, 0,
+ 136, 4, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 12, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 12, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 12,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 8, 2, 0, 0,
+ 64, 0, 1, 0, 130, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 192, 0, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 1, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 38, 0, 0, 8, 0, 208,
+ 0, 0, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 16,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 3, 0,
+ 0, 0, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 4, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 10,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 130,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 68, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 242, 32, 16, 0,
+ 2, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 14, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 9, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_2_64
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Normal_2_64._fxctmp
+// /EStreamOutSkinVS_Position_Normal_2_64 /D BONESPERVERTEX=2 /D
+// BONECOUNT=64 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[64]; // Offset: 0 Size: 3072
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[192], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+imul null, r0.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+dp3 r1.x, v1.xyzx, r1.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r3.xyzw
+dp4 o0.y, r2.xyzw, r3.xyzw
+dp3 r1.y, v1.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r0.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r3.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+dp3 r1.z, v1.xyzx, r0.xyzx
+dp3 r0.x, r1.xyzx, r1.xyzx
+rsq r0.x, r0.x
+mul o1.xyz, r0.xxxx, r1.xyzx
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 20 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_2_64[] =
+{
+ 68, 88, 66, 67, 249, 238,
+ 191, 110, 90, 243, 91, 253,
+ 39, 115, 116, 175, 14, 174,
+ 116, 168, 1, 0, 0, 0,
+ 136, 5, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 12, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 12, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 12,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 224, 2, 0, 0, 64, 0,
+ 1, 0, 184, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 192, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 16, 0, 0, 7, 34, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 66, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 68, 0, 0, 5,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 1, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 54, 0, 0, 8,
+ 242, 32, 16, 0, 2, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 20, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 12, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_4_64
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Normal_4_64._fxctmp
+// /EStreamOutSkinVS_Position_Normal_4_64 /D BONESPERVERTEX=4 /D
+// BONECOUNT=64 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[64]; // Offset: 0 Size: 3072
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[192], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.zzzz, cb0[r1.z + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.wwww, cb0[r1.w + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+dp3 r2.x, v1.xyzx, r2.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.wwww, cb0[r1.w + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+dp3 r2.y, v1.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 2].xyzw, r3.xyzw
+mad r1.xyzw, v3.wwww, cb0[r1.w + 2].xyzw, r3.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r2.z, v1.xyzx, r1.xyzx
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o1.xyz, r0.xxxx, r2.xyzx
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 26 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_4_64[] =
+{
+ 68, 88, 66, 67, 205, 190,
+ 48, 68, 39, 226, 36, 48,
+ 236, 161, 124, 195, 48, 97,
+ 86, 179, 1, 0, 0, 0,
+ 160, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 36, 6, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 12, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 12,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 248, 3, 0, 0, 64, 0,
+ 1, 0, 254, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 192, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 30, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 2, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 34, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 66, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 68, 0, 0, 5, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 6, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 54, 0, 0, 8, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 26, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 12, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_1_128
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Normal_1_128._fxctmp
+// /EStreamOutSkinVS_Position_Normal_1_128 /D BONESPERVERTEX=1 /D
+// BONECOUNT=128 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[128]; // Offset: 0 Size: 6144
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[384], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.x, v3.x, l(3)
+dp4 o0.x, r0.xyzw, cb0[r1.x + 0].xyzw
+dp4 o0.y, r0.xyzw, cb0[r1.x + 1].xyzw
+dp4 o0.z, r0.xyzw, cb0[r1.x + 2].xyzw
+dp3 r0.x, v1.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v1.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v1.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 14 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_1_128[] =
+{
+ 68, 88, 66, 67, 238, 117,
+ 193, 23, 105, 229, 96, 236,
+ 185, 115, 197, 17, 52, 153,
+ 70, 113, 1, 0, 0, 0,
+ 136, 4, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 12, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 24, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 24,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 128, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 8, 2, 0, 0,
+ 64, 0, 1, 0, 130, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 128, 1, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 1, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 38, 0, 0, 8, 0, 208,
+ 0, 0, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 16,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 3, 0,
+ 0, 0, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 4, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 10,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 130,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 68, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 242, 32, 16, 0,
+ 2, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 14, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 9, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_2_128
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Normal_2_128._fxctmp
+// /EStreamOutSkinVS_Position_Normal_2_128 /D BONESPERVERTEX=2 /D
+// BONECOUNT=128 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[128]; // Offset: 0 Size: 6144
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[384], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+imul null, r0.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+dp3 r1.x, v1.xyzx, r1.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r3.xyzw
+dp4 o0.y, r2.xyzw, r3.xyzw
+dp3 r1.y, v1.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r0.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r3.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+dp3 r1.z, v1.xyzx, r0.xyzx
+dp3 r0.x, r1.xyzx, r1.xyzx
+rsq r0.x, r0.x
+mul o1.xyz, r0.xxxx, r1.xyzx
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 20 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_2_128[] =
+{
+ 68, 88, 66, 67, 166, 32,
+ 136, 74, 54, 144, 150, 156,
+ 117, 193, 191, 235, 43, 166,
+ 113, 77, 1, 0, 0, 0,
+ 136, 5, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 12, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 24, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 24,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 128, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 224, 2, 0, 0, 64, 0,
+ 1, 0, 184, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 128, 1, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 16, 0, 0, 7, 34, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 66, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 68, 0, 0, 5,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 1, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 54, 0, 0, 8,
+ 242, 32, 16, 0, 2, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 20, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 12, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_4_128
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Normal_4_128._fxctmp
+// /EStreamOutSkinVS_Position_Normal_4_128 /D BONESPERVERTEX=4 /D
+// BONECOUNT=128 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[128]; // Offset: 0 Size: 6144
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[384], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.zzzz, cb0[r1.z + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.wwww, cb0[r1.w + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+dp3 r2.x, v1.xyzx, r2.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.wwww, cb0[r1.w + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+dp3 r2.y, v1.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 2].xyzw, r3.xyzw
+mad r1.xyzw, v3.wwww, cb0[r1.w + 2].xyzw, r3.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r2.z, v1.xyzx, r1.xyzx
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o1.xyz, r0.xxxx, r2.xyzx
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 26 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_4_128[] =
+{
+ 68, 88, 66, 67, 60, 35,
+ 123, 52, 14, 142, 67, 125,
+ 230, 117, 131, 72, 141, 182,
+ 246, 69, 1, 0, 0, 0,
+ 160, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 36, 6, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 24, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 24,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 128, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 248, 3, 0, 0, 64, 0,
+ 1, 0, 254, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 128, 1, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 30, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 2, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 34, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 66, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 68, 0, 0, 5, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 6, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 54, 0, 0, 8, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 26, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 12, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_1_512
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Normal_1_512._fxctmp
+// /EStreamOutSkinVS_Position_Normal_1_512 /D BONESPERVERTEX=1 /D
+// BONECOUNT=512 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[512]; // Offset: 0 Size: 24576
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[1536], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.x, v3.x, l(3)
+dp4 o0.x, r0.xyzw, cb0[r1.x + 0].xyzw
+dp4 o0.y, r0.xyzw, cb0[r1.x + 1].xyzw
+dp4 o0.z, r0.xyzw, cb0[r1.x + 2].xyzw
+dp3 r0.x, v1.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v1.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v1.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 14 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_1_512[] =
+{
+ 68, 88, 66, 67, 212, 203,
+ 229, 99, 44, 100, 72, 180,
+ 127, 187, 124, 99, 124, 29,
+ 226, 208, 1, 0, 0, 0,
+ 136, 4, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 12, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 96, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 96,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 8, 2, 0, 0,
+ 64, 0, 1, 0, 130, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 0, 6, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 1, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 38, 0, 0, 8, 0, 208,
+ 0, 0, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 16,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 3, 0,
+ 0, 0, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 4, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 10,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 130,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 68, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 242, 32, 16, 0,
+ 2, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 14, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 9, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_2_512
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Normal_2_512._fxctmp
+// /EStreamOutSkinVS_Position_Normal_2_512 /D BONESPERVERTEX=2 /D
+// BONECOUNT=512 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[512]; // Offset: 0 Size: 24576
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[1536], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+imul null, r0.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+dp3 r1.x, v1.xyzx, r1.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r3.xyzw
+dp4 o0.y, r2.xyzw, r3.xyzw
+dp3 r1.y, v1.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r0.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r3.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+dp3 r1.z, v1.xyzx, r0.xyzx
+dp3 r0.x, r1.xyzx, r1.xyzx
+rsq r0.x, r0.x
+mul o1.xyz, r0.xxxx, r1.xyzx
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 20 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_2_512[] =
+{
+ 68, 88, 66, 67, 204, 75,
+ 190, 164, 162, 21, 187, 0,
+ 156, 250, 209, 189, 194, 167,
+ 84, 205, 1, 0, 0, 0,
+ 136, 5, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 12, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 96, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 96,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 224, 2, 0, 0, 64, 0,
+ 1, 0, 184, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 6, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 16, 0, 0, 7, 34, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 66, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 68, 0, 0, 5,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 1, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 54, 0, 0, 8,
+ 242, 32, 16, 0, 2, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 20, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 12, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_4_512
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Normal_4_512._fxctmp
+// /EStreamOutSkinVS_Position_Normal_4_512 /D BONESPERVERTEX=4 /D
+// BONECOUNT=512 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[512]; // Offset: 0 Size: 24576
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[1536], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.zzzz, cb0[r1.z + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.wwww, cb0[r1.w + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+dp3 r2.x, v1.xyzx, r2.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.wwww, cb0[r1.w + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+dp3 r2.y, v1.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 2].xyzw, r3.xyzw
+mad r1.xyzw, v3.wwww, cb0[r1.w + 2].xyzw, r3.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r2.z, v1.xyzx, r1.xyzx
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o1.xyz, r0.xxxx, r2.xyzx
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 26 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_4_512[] =
+{
+ 68, 88, 66, 67, 167, 117,
+ 189, 161, 236, 51, 44, 164,
+ 35, 250, 193, 15, 208, 58,
+ 236, 51, 1, 0, 0, 0,
+ 160, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 36, 6, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 96, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 96,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 248, 3, 0, 0, 64, 0,
+ 1, 0, 254, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 6, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 30, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 2, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 34, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 66, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 68, 0, 0, 5, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 6, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 54, 0, 0, 8, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 26, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 12, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_1_1024
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Normal_1_1024._fxctmp
+// /EStreamOutSkinVS_Position_Normal_1_1024 /D BONESPERVERTEX=1 /D
+// BONECOUNT=1024 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[1024]; // Offset: 0 Size: 49152
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[3072], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.x, v3.x, l(3)
+dp4 o0.x, r0.xyzw, cb0[r1.x + 0].xyzw
+dp4 o0.y, r0.xyzw, cb0[r1.x + 1].xyzw
+dp4 o0.z, r0.xyzw, cb0[r1.x + 2].xyzw
+dp3 r0.x, v1.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v1.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v1.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 14 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_1_1024[] =
+{
+ 68, 88, 66, 67, 133, 164,
+ 172, 63, 67, 0, 73, 148,
+ 24, 137, 147, 85, 55, 244,
+ 27, 171, 1, 0, 0, 0,
+ 136, 4, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 12, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 192, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 192,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 8, 2, 0, 0,
+ 64, 0, 1, 0, 130, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 0, 12, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 1, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 38, 0, 0, 8, 0, 208,
+ 0, 0, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 16,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 3, 0,
+ 0, 0, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 4, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 10,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 130,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 68, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 242, 32, 16, 0,
+ 2, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 14, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 9, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_2_1024
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Normal_2_1024._fxctmp
+// /EStreamOutSkinVS_Position_Normal_2_1024 /D BONESPERVERTEX=2 /D
+// BONECOUNT=1024 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[1024]; // Offset: 0 Size: 49152
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[3072], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+imul null, r0.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+dp3 r1.x, v1.xyzx, r1.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r3.xyzw
+dp4 o0.y, r2.xyzw, r3.xyzw
+dp3 r1.y, v1.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r0.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r3.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+dp3 r1.z, v1.xyzx, r0.xyzx
+dp3 r0.x, r1.xyzx, r1.xyzx
+rsq r0.x, r0.x
+mul o1.xyz, r0.xxxx, r1.xyzx
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 20 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_2_1024[] =
+{
+ 68, 88, 66, 67, 130, 225,
+ 168, 26, 4, 102, 26, 127,
+ 102, 67, 185, 3, 166, 201,
+ 236, 118, 1, 0, 0, 0,
+ 136, 5, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 12, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 192, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 192,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 224, 2, 0, 0, 64, 0,
+ 1, 0, 184, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 12, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 16, 0, 0, 7, 34, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 66, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 68, 0, 0, 5,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 1, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 54, 0, 0, 8,
+ 242, 32, 16, 0, 2, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 20, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 12, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_4_1024
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Normal_4_1024._fxctmp
+// /EStreamOutSkinVS_Position_Normal_4_1024 /D BONESPERVERTEX=4 /D
+// BONECOUNT=1024 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[1024]; // Offset: 0 Size: 49152
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[3072], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.zzzz, cb0[r1.z + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.wwww, cb0[r1.w + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+dp3 r2.x, v1.xyzx, r2.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.wwww, cb0[r1.w + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+dp3 r2.y, v1.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 2].xyzw, r3.xyzw
+mad r1.xyzw, v3.wwww, cb0[r1.w + 2].xyzw, r3.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r2.z, v1.xyzx, r1.xyzx
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o1.xyz, r0.xxxx, r2.xyzx
+mov o2.xyzw, l(0,0,0,0)
+ret
+// Approximately 26 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_4_1024[] =
+{
+ 68, 88, 66, 67, 32, 205,
+ 153, 254, 94, 143, 155, 24,
+ 190, 41, 240, 122, 149, 200,
+ 107, 78, 1, 0, 0, 0,
+ 160, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 36, 6, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 192, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 192,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 0, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 248, 3, 0, 0, 64, 0,
+ 1, 0, 254, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 12, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 30, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 2, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 34, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 66, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 68, 0, 0, 5, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 6, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 54, 0, 0, 8, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 26, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 12, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_Tangent_1_32
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0
+// /FhStreamOutSkinVS_Position_Normal_Tangent_1_32._fxctmp
+// /EStreamOutSkinVS_Position_Normal_Tangent_1_32 /D BONESPERVERTEX=1 /D
+// BONECOUNT=32 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[32]; // Offset: 0 Size: 1536
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[96], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v2.xyzw
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.x, v3.x, l(3)
+dp4 o0.x, r0.xyzw, cb0[r1.x + 0].xyzw
+dp4 o0.y, r0.xyzw, cb0[r1.x + 1].xyzw
+dp4 o0.z, r0.xyzw, cb0[r1.x + 2].xyzw
+dp3 r0.x, v1.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v1.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v1.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+dp3 r0.x, v2.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v2.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v2.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o2.xyz, r0.wwww, r0.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 20 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_Tangent_1_32[] =
+{
+ 68, 88, 66, 67, 183, 181,
+ 30, 175, 253, 170, 103, 5,
+ 88, 56, 199, 145, 45, 171,
+ 177, 63, 1, 0, 0, 0,
+ 72, 5, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 204, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 6, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 200, 2, 0, 0,
+ 64, 0, 1, 0, 178, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 96, 0, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 1, 0, 0, 0,
+ 95, 0, 0, 3, 242, 16,
+ 16, 0, 2, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 38, 0, 0, 8, 0, 208,
+ 0, 0, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 16,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 3, 0,
+ 0, 0, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 4, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 10,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 130,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 68, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 130, 32, 4, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 10,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 2, 0, 0, 0, 70, 130,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 68, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 2, 0, 0, 0, 58, 16,
+ 16, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 20, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 15, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_Tangent_2_32
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0
+// /FhStreamOutSkinVS_Position_Normal_Tangent_2_32._fxctmp
+// /EStreamOutSkinVS_Position_Normal_Tangent_2_32 /D BONESPERVERTEX=2 /D
+// BONECOUNT=32 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[32]; // Offset: 0 Size: 1536
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[96], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v2.xyzw
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 5
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+mul r4.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r4.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r0.x, v1.xyzx, r2.xyzx
+dp3 r2.x, v2.xyzx, r2.xyzx
+dp3 r0.y, v1.xyzx, r3.xyzx
+dp3 r2.y, v2.xyzx, r3.xyzx
+dp3 r0.z, v1.xyzx, r1.xyzx
+dp3 r2.z, v2.xyzx, r1.xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r2.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 26 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_Tangent_2_32[] =
+{
+ 68, 88, 66, 67, 181, 251,
+ 117, 126, 214, 41, 151, 11,
+ 206, 111, 25, 183, 118, 223,
+ 26, 216, 1, 0, 0, 0,
+ 40, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 172, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 6, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 128, 3, 0, 0, 64, 0,
+ 1, 0, 224, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 96, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 5, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 50, 0, 16, 0, 1, 0,
+ 0, 0, 70, 16, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 26, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 3, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 17, 0,
+ 0, 7, 34, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 4, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 4, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 2, 0,
+ 0, 0, 70, 18, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 16, 0, 0, 7, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 16, 0, 0, 7,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 130, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 68, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 1, 0, 0, 0, 246, 15,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 68, 0, 0, 5, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 2, 0,
+ 0, 0, 6, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 54, 0, 0, 5, 130, 32,
+ 16, 0, 2, 0, 0, 0,
+ 58, 16, 16, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 26, 0, 0, 0,
+ 5, 0, 0, 0, 0, 0,
+ 0, 0, 8, 0, 0, 0,
+ 18, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_Tangent_4_32
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0
+// /FhStreamOutSkinVS_Position_Normal_Tangent_4_32._fxctmp
+// /EStreamOutSkinVS_Position_Normal_Tangent_4_32 /D BONESPERVERTEX=4 /D
+// BONECOUNT=32 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[32]; // Offset: 0 Size: 1536
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[96], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v2.xyzw
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 5
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.zzzz, cb0[r1.z + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.wwww, cb0[r1.w + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.wwww, cb0[r1.w + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+mul r4.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r4.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r4.xyzw
+mad r4.xyzw, v3.zzzz, cb0[r1.z + 2].xyzw, r4.xyzw
+mad r1.xyzw, v3.wwww, cb0[r1.w + 2].xyzw, r4.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r0.x, v1.xyzx, r2.xyzx
+dp3 r2.x, v2.xyzx, r2.xyzx
+dp3 r0.y, v1.xyzx, r3.xyzx
+dp3 r2.y, v2.xyzx, r3.xyzx
+dp3 r0.z, v1.xyzx, r1.xyzx
+dp3 r2.z, v2.xyzx, r1.xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r2.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 32 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_Tangent_4_32[] =
+{
+ 68, 88, 66, 67, 19, 221,
+ 121, 113, 127, 28, 29, 87,
+ 46, 3, 179, 249, 93, 103,
+ 188, 212, 1, 0, 0, 0,
+ 64, 7, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 196, 6, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 6, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 152, 4, 0, 0, 64, 0,
+ 1, 0, 38, 1, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 96, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 5, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 30, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 2, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 42, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 246, 31, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 4, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 26, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 4, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 4, 0, 0, 0, 166, 26,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 58, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 17, 0,
+ 0, 7, 66, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 16, 0, 0, 7,
+ 34, 0, 16, 0, 2, 0,
+ 0, 0, 70, 18, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 66, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 68, 0, 0, 5, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 68, 0,
+ 0, 5, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 2, 0, 0, 0, 58, 16,
+ 16, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 32, 0, 0, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 0, 0, 0, 18, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_Tangent_1_64
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0
+// /FhStreamOutSkinVS_Position_Normal_Tangent_1_64._fxctmp
+// /EStreamOutSkinVS_Position_Normal_Tangent_1_64 /D BONESPERVERTEX=1 /D
+// BONECOUNT=64 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[64]; // Offset: 0 Size: 3072
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[192], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v2.xyzw
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.x, v3.x, l(3)
+dp4 o0.x, r0.xyzw, cb0[r1.x + 0].xyzw
+dp4 o0.y, r0.xyzw, cb0[r1.x + 1].xyzw
+dp4 o0.z, r0.xyzw, cb0[r1.x + 2].xyzw
+dp3 r0.x, v1.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v1.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v1.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+dp3 r0.x, v2.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v2.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v2.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o2.xyz, r0.wwww, r0.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 20 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_Tangent_1_64[] =
+{
+ 68, 88, 66, 67, 67, 223,
+ 94, 248, 228, 40, 152, 39,
+ 164, 84, 254, 169, 168, 108,
+ 132, 56, 1, 0, 0, 0,
+ 72, 5, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 204, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 12, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 12,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 200, 2, 0, 0,
+ 64, 0, 1, 0, 178, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 192, 0, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 1, 0, 0, 0,
+ 95, 0, 0, 3, 242, 16,
+ 16, 0, 2, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 38, 0, 0, 8, 0, 208,
+ 0, 0, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 16,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 3, 0,
+ 0, 0, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 4, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 10,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 130,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 68, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 130, 32, 4, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 10,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 2, 0, 0, 0, 70, 130,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 68, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 2, 0, 0, 0, 58, 16,
+ 16, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 20, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 15, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_Tangent_2_64
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0
+// /FhStreamOutSkinVS_Position_Normal_Tangent_2_64._fxctmp
+// /EStreamOutSkinVS_Position_Normal_Tangent_2_64 /D BONESPERVERTEX=2 /D
+// BONECOUNT=64 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[64]; // Offset: 0 Size: 3072
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[192], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v2.xyzw
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 5
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+mul r4.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r4.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r0.x, v1.xyzx, r2.xyzx
+dp3 r2.x, v2.xyzx, r2.xyzx
+dp3 r0.y, v1.xyzx, r3.xyzx
+dp3 r2.y, v2.xyzx, r3.xyzx
+dp3 r0.z, v1.xyzx, r1.xyzx
+dp3 r2.z, v2.xyzx, r1.xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r2.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 26 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_Tangent_2_64[] =
+{
+ 68, 88, 66, 67, 91, 181,
+ 220, 66, 242, 236, 115, 101,
+ 110, 154, 12, 56, 22, 247,
+ 167, 255, 1, 0, 0, 0,
+ 40, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 172, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 12, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 12,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 128, 3, 0, 0, 64, 0,
+ 1, 0, 224, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 192, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 5, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 50, 0, 16, 0, 1, 0,
+ 0, 0, 70, 16, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 26, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 3, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 17, 0,
+ 0, 7, 34, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 4, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 4, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 2, 0,
+ 0, 0, 70, 18, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 16, 0, 0, 7, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 16, 0, 0, 7,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 130, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 68, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 1, 0, 0, 0, 246, 15,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 68, 0, 0, 5, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 2, 0,
+ 0, 0, 6, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 54, 0, 0, 5, 130, 32,
+ 16, 0, 2, 0, 0, 0,
+ 58, 16, 16, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 26, 0, 0, 0,
+ 5, 0, 0, 0, 0, 0,
+ 0, 0, 8, 0, 0, 0,
+ 18, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_Tangent_4_64
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0
+// /FhStreamOutSkinVS_Position_Normal_Tangent_4_64._fxctmp
+// /EStreamOutSkinVS_Position_Normal_Tangent_4_64 /D BONESPERVERTEX=4 /D
+// BONECOUNT=64 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[64]; // Offset: 0 Size: 3072
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[192], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v2.xyzw
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 5
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.zzzz, cb0[r1.z + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.wwww, cb0[r1.w + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.wwww, cb0[r1.w + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+mul r4.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r4.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r4.xyzw
+mad r4.xyzw, v3.zzzz, cb0[r1.z + 2].xyzw, r4.xyzw
+mad r1.xyzw, v3.wwww, cb0[r1.w + 2].xyzw, r4.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r0.x, v1.xyzx, r2.xyzx
+dp3 r2.x, v2.xyzx, r2.xyzx
+dp3 r0.y, v1.xyzx, r3.xyzx
+dp3 r2.y, v2.xyzx, r3.xyzx
+dp3 r0.z, v1.xyzx, r1.xyzx
+dp3 r2.z, v2.xyzx, r1.xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r2.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 32 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_Tangent_4_64[] =
+{
+ 68, 88, 66, 67, 119, 209,
+ 156, 16, 140, 0, 156, 224,
+ 87, 117, 200, 45, 39, 172,
+ 135, 2, 1, 0, 0, 0,
+ 64, 7, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 196, 6, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 12, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 12,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 152, 4, 0, 0, 64, 0,
+ 1, 0, 38, 1, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 192, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 5, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 30, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 2, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 42, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 246, 31, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 4, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 26, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 4, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 4, 0, 0, 0, 166, 26,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 58, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 17, 0,
+ 0, 7, 66, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 16, 0, 0, 7,
+ 34, 0, 16, 0, 2, 0,
+ 0, 0, 70, 18, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 66, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 68, 0, 0, 5, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 68, 0,
+ 0, 5, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 2, 0, 0, 0, 58, 16,
+ 16, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 32, 0, 0, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 0, 0, 0, 18, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_Tangent_1_128
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0
+// /FhStreamOutSkinVS_Position_Normal_Tangent_1_128._fxctmp
+// /EStreamOutSkinVS_Position_Normal_Tangent_1_128 /D BONESPERVERTEX=1 /D
+// BONECOUNT=128 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[128]; // Offset: 0 Size: 6144
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[384], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v2.xyzw
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.x, v3.x, l(3)
+dp4 o0.x, r0.xyzw, cb0[r1.x + 0].xyzw
+dp4 o0.y, r0.xyzw, cb0[r1.x + 1].xyzw
+dp4 o0.z, r0.xyzw, cb0[r1.x + 2].xyzw
+dp3 r0.x, v1.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v1.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v1.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+dp3 r0.x, v2.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v2.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v2.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o2.xyz, r0.wwww, r0.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 20 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_Tangent_1_128[] =
+{
+ 68, 88, 66, 67, 209, 116,
+ 248, 215, 32, 190, 187, 116,
+ 43, 145, 110, 83, 6, 70,
+ 244, 224, 1, 0, 0, 0,
+ 72, 5, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 204, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 24, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 24,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 128, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 200, 2, 0, 0,
+ 64, 0, 1, 0, 178, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 128, 1, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 1, 0, 0, 0,
+ 95, 0, 0, 3, 242, 16,
+ 16, 0, 2, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 38, 0, 0, 8, 0, 208,
+ 0, 0, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 16,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 3, 0,
+ 0, 0, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 4, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 10,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 130,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 68, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 130, 32, 4, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 10,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 2, 0, 0, 0, 70, 130,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 68, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 2, 0, 0, 0, 58, 16,
+ 16, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 20, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 15, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_Tangent_2_128
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0
+// /FhStreamOutSkinVS_Position_Normal_Tangent_2_128._fxctmp
+// /EStreamOutSkinVS_Position_Normal_Tangent_2_128 /D BONESPERVERTEX=2 /D
+// BONECOUNT=128 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[128]; // Offset: 0 Size: 6144
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[384], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v2.xyzw
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 5
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+mul r4.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r4.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r0.x, v1.xyzx, r2.xyzx
+dp3 r2.x, v2.xyzx, r2.xyzx
+dp3 r0.y, v1.xyzx, r3.xyzx
+dp3 r2.y, v2.xyzx, r3.xyzx
+dp3 r0.z, v1.xyzx, r1.xyzx
+dp3 r2.z, v2.xyzx, r1.xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r2.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 26 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_Tangent_2_128[] =
+{
+ 68, 88, 66, 67, 240, 99,
+ 184, 65, 42, 15, 79, 214,
+ 188, 103, 143, 167, 243, 249,
+ 180, 251, 1, 0, 0, 0,
+ 40, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 172, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 24, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 24,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 128, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 128, 3, 0, 0, 64, 0,
+ 1, 0, 224, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 128, 1, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 5, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 50, 0, 16, 0, 1, 0,
+ 0, 0, 70, 16, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 26, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 3, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 17, 0,
+ 0, 7, 34, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 4, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 4, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 2, 0,
+ 0, 0, 70, 18, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 16, 0, 0, 7, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 16, 0, 0, 7,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 130, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 68, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 1, 0, 0, 0, 246, 15,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 68, 0, 0, 5, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 2, 0,
+ 0, 0, 6, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 54, 0, 0, 5, 130, 32,
+ 16, 0, 2, 0, 0, 0,
+ 58, 16, 16, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 26, 0, 0, 0,
+ 5, 0, 0, 0, 0, 0,
+ 0, 0, 8, 0, 0, 0,
+ 18, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_Tangent_4_128
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0
+// /FhStreamOutSkinVS_Position_Normal_Tangent_4_128._fxctmp
+// /EStreamOutSkinVS_Position_Normal_Tangent_4_128 /D BONESPERVERTEX=4 /D
+// BONECOUNT=128 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[128]; // Offset: 0 Size: 6144
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[384], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v2.xyzw
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 5
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.zzzz, cb0[r1.z + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.wwww, cb0[r1.w + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.wwww, cb0[r1.w + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+mul r4.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r4.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r4.xyzw
+mad r4.xyzw, v3.zzzz, cb0[r1.z + 2].xyzw, r4.xyzw
+mad r1.xyzw, v3.wwww, cb0[r1.w + 2].xyzw, r4.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r0.x, v1.xyzx, r2.xyzx
+dp3 r2.x, v2.xyzx, r2.xyzx
+dp3 r0.y, v1.xyzx, r3.xyzx
+dp3 r2.y, v2.xyzx, r3.xyzx
+dp3 r0.z, v1.xyzx, r1.xyzx
+dp3 r2.z, v2.xyzx, r1.xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r2.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 32 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_Tangent_4_128[] =
+{
+ 68, 88, 66, 67, 186, 105,
+ 76, 213, 38, 43, 19, 216,
+ 240, 103, 215, 141, 162, 38,
+ 94, 232, 1, 0, 0, 0,
+ 64, 7, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 196, 6, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 24, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 24,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 128, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 152, 4, 0, 0, 64, 0,
+ 1, 0, 38, 1, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 128, 1, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 5, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 30, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 2, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 42, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 246, 31, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 4, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 26, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 4, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 4, 0, 0, 0, 166, 26,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 58, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 17, 0,
+ 0, 7, 66, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 16, 0, 0, 7,
+ 34, 0, 16, 0, 2, 0,
+ 0, 0, 70, 18, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 66, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 68, 0, 0, 5, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 68, 0,
+ 0, 5, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 2, 0, 0, 0, 58, 16,
+ 16, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 32, 0, 0, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 0, 0, 0, 18, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_Tangent_1_512
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0
+// /FhStreamOutSkinVS_Position_Normal_Tangent_1_512._fxctmp
+// /EStreamOutSkinVS_Position_Normal_Tangent_1_512 /D BONESPERVERTEX=1 /D
+// BONECOUNT=512 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[512]; // Offset: 0 Size: 24576
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[1536], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v2.xyzw
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.x, v3.x, l(3)
+dp4 o0.x, r0.xyzw, cb0[r1.x + 0].xyzw
+dp4 o0.y, r0.xyzw, cb0[r1.x + 1].xyzw
+dp4 o0.z, r0.xyzw, cb0[r1.x + 2].xyzw
+dp3 r0.x, v1.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v1.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v1.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+dp3 r0.x, v2.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v2.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v2.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o2.xyz, r0.wwww, r0.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 20 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_Tangent_1_512[] =
+{
+ 68, 88, 66, 67, 248, 168,
+ 89, 63, 27, 139, 214, 175,
+ 110, 127, 239, 55, 216, 27,
+ 36, 163, 1, 0, 0, 0,
+ 72, 5, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 204, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 96, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 96,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 200, 2, 0, 0,
+ 64, 0, 1, 0, 178, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 0, 6, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 1, 0, 0, 0,
+ 95, 0, 0, 3, 242, 16,
+ 16, 0, 2, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 38, 0, 0, 8, 0, 208,
+ 0, 0, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 16,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 3, 0,
+ 0, 0, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 4, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 10,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 130,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 68, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 130, 32, 4, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 10,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 2, 0, 0, 0, 70, 130,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 68, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 2, 0, 0, 0, 58, 16,
+ 16, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 20, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 15, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_Tangent_2_512
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0
+// /FhStreamOutSkinVS_Position_Normal_Tangent_2_512._fxctmp
+// /EStreamOutSkinVS_Position_Normal_Tangent_2_512 /D BONESPERVERTEX=2 /D
+// BONECOUNT=512 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[512]; // Offset: 0 Size: 24576
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[1536], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v2.xyzw
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 5
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+mul r4.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r4.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r0.x, v1.xyzx, r2.xyzx
+dp3 r2.x, v2.xyzx, r2.xyzx
+dp3 r0.y, v1.xyzx, r3.xyzx
+dp3 r2.y, v2.xyzx, r3.xyzx
+dp3 r0.z, v1.xyzx, r1.xyzx
+dp3 r2.z, v2.xyzx, r1.xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r2.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 26 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_Tangent_2_512[] =
+{
+ 68, 88, 66, 67, 106, 64,
+ 83, 181, 46, 115, 93, 157,
+ 45, 156, 32, 62, 119, 179,
+ 192, 138, 1, 0, 0, 0,
+ 40, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 172, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 96, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 96,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 128, 3, 0, 0, 64, 0,
+ 1, 0, 224, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 6, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 5, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 50, 0, 16, 0, 1, 0,
+ 0, 0, 70, 16, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 26, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 3, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 17, 0,
+ 0, 7, 34, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 4, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 4, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 2, 0,
+ 0, 0, 70, 18, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 16, 0, 0, 7, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 16, 0, 0, 7,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 130, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 68, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 1, 0, 0, 0, 246, 15,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 68, 0, 0, 5, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 2, 0,
+ 0, 0, 6, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 54, 0, 0, 5, 130, 32,
+ 16, 0, 2, 0, 0, 0,
+ 58, 16, 16, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 26, 0, 0, 0,
+ 5, 0, 0, 0, 0, 0,
+ 0, 0, 8, 0, 0, 0,
+ 18, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_Tangent_4_512
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0
+// /FhStreamOutSkinVS_Position_Normal_Tangent_4_512._fxctmp
+// /EStreamOutSkinVS_Position_Normal_Tangent_4_512 /D BONESPERVERTEX=4 /D
+// BONECOUNT=512 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[512]; // Offset: 0 Size: 24576
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[1536], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v2.xyzw
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 5
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.zzzz, cb0[r1.z + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.wwww, cb0[r1.w + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.wwww, cb0[r1.w + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+mul r4.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r4.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r4.xyzw
+mad r4.xyzw, v3.zzzz, cb0[r1.z + 2].xyzw, r4.xyzw
+mad r1.xyzw, v3.wwww, cb0[r1.w + 2].xyzw, r4.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r0.x, v1.xyzx, r2.xyzx
+dp3 r2.x, v2.xyzx, r2.xyzx
+dp3 r0.y, v1.xyzx, r3.xyzx
+dp3 r2.y, v2.xyzx, r3.xyzx
+dp3 r0.z, v1.xyzx, r1.xyzx
+dp3 r2.z, v2.xyzx, r1.xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r2.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 32 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_Tangent_4_512[] =
+{
+ 68, 88, 66, 67, 87, 16,
+ 47, 46, 228, 240, 151, 146,
+ 31, 201, 168, 179, 229, 105,
+ 94, 127, 1, 0, 0, 0,
+ 64, 7, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 196, 6, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 96, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 96,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 152, 4, 0, 0, 64, 0,
+ 1, 0, 38, 1, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 6, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 5, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 30, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 2, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 42, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 246, 31, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 4, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 26, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 4, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 4, 0, 0, 0, 166, 26,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 58, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 17, 0,
+ 0, 7, 66, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 16, 0, 0, 7,
+ 34, 0, 16, 0, 2, 0,
+ 0, 0, 70, 18, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 66, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 68, 0, 0, 5, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 68, 0,
+ 0, 5, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 2, 0, 0, 0, 58, 16,
+ 16, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 32, 0, 0, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 0, 0, 0, 18, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_Tangent_1_1024
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0
+// /FhStreamOutSkinVS_Position_Normal_Tangent_1_1024._fxctmp
+// /EStreamOutSkinVS_Position_Normal_Tangent_1_1024 /D BONESPERVERTEX=1 /D
+// BONECOUNT=1024 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[1024]; // Offset: 0 Size: 49152
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[3072], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v2.xyzw
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.x, v3.x, l(3)
+dp4 o0.x, r0.xyzw, cb0[r1.x + 0].xyzw
+dp4 o0.y, r0.xyzw, cb0[r1.x + 1].xyzw
+dp4 o0.z, r0.xyzw, cb0[r1.x + 2].xyzw
+dp3 r0.x, v1.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v1.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v1.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+dp3 r0.x, v2.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v2.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v2.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o2.xyz, r0.wwww, r0.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 20 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_Tangent_1_1024[] =
+{
+ 68, 88, 66, 67, 77, 124,
+ 209, 92, 234, 48, 143, 191,
+ 150, 28, 205, 73, 128, 69,
+ 35, 47, 1, 0, 0, 0,
+ 72, 5, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 204, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 192, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 192,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 200, 2, 0, 0,
+ 64, 0, 1, 0, 178, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 0, 12, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 1, 0, 0, 0,
+ 95, 0, 0, 3, 242, 16,
+ 16, 0, 2, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 38, 0, 0, 8, 0, 208,
+ 0, 0, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 16,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 3, 0,
+ 0, 0, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 4, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 10,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 130,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 68, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 130, 32, 4, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 10,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 2, 0, 0, 0, 70, 130,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 68, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 2, 0, 0, 0, 58, 16,
+ 16, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 20, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 15, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_Tangent_2_1024
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0
+// /FhStreamOutSkinVS_Position_Normal_Tangent_2_1024._fxctmp
+// /EStreamOutSkinVS_Position_Normal_Tangent_2_1024 /D BONESPERVERTEX=2 /D
+// BONECOUNT=1024 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[1024]; // Offset: 0 Size: 49152
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[3072], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v2.xyzw
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 5
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+mul r4.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r4.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r0.x, v1.xyzx, r2.xyzx
+dp3 r2.x, v2.xyzx, r2.xyzx
+dp3 r0.y, v1.xyzx, r3.xyzx
+dp3 r2.y, v2.xyzx, r3.xyzx
+dp3 r0.z, v1.xyzx, r1.xyzx
+dp3 r2.z, v2.xyzx, r1.xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r2.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 26 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_Tangent_2_1024[] =
+{
+ 68, 88, 66, 67, 141, 10,
+ 58, 114, 205, 161, 106, 233,
+ 192, 250, 130, 22, 138, 17,
+ 51, 208, 1, 0, 0, 0,
+ 40, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 172, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 192, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 192,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 128, 3, 0, 0, 64, 0,
+ 1, 0, 224, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 12, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 5, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 50, 0, 16, 0, 1, 0,
+ 0, 0, 70, 16, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 26, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 3, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 17, 0,
+ 0, 7, 34, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 4, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 4, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 2, 0,
+ 0, 0, 70, 18, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 16, 0, 0, 7, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 16, 0, 0, 7,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 130, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 68, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 1, 0, 0, 0, 246, 15,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 68, 0, 0, 5, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 2, 0,
+ 0, 0, 6, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 54, 0, 0, 5, 130, 32,
+ 16, 0, 2, 0, 0, 0,
+ 58, 16, 16, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 26, 0, 0, 0,
+ 5, 0, 0, 0, 0, 0,
+ 0, 0, 8, 0, 0, 0,
+ 18, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Normal_Tangent_4_1024
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0
+// /FhStreamOutSkinVS_Position_Normal_Tangent_4_1024._fxctmp
+// /EStreamOutSkinVS_Position_Normal_Tangent_4_1024 /D BONESPERVERTEX=4 /D
+// BONECOUNT=1024 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[1024]; // Offset: 0 Size: 49152
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float xyz
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[3072], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v1.xyz
+dcl_input v2.xyzw
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 5
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.zzzz, cb0[r1.z + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.wwww, cb0[r1.w + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.wwww, cb0[r1.w + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+mul r4.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r4.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r4.xyzw
+mad r4.xyzw, v3.zzzz, cb0[r1.z + 2].xyzw, r4.xyzw
+mad r1.xyzw, v3.wwww, cb0[r1.w + 2].xyzw, r4.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r0.x, v1.xyzx, r2.xyzx
+dp3 r2.x, v2.xyzx, r2.xyzx
+dp3 r0.y, v1.xyzx, r3.xyzx
+dp3 r2.y, v2.xyzx, r3.xyzx
+dp3 r0.z, v1.xyzx, r1.xyzx
+dp3 r2.z, v2.xyzx, r1.xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o1.xyz, r0.wwww, r0.xyzx
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r2.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 32 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Normal_Tangent_4_1024[] =
+{
+ 68, 88, 66, 67, 235, 12,
+ 76, 146, 76, 200, 134, 63,
+ 160, 113, 162, 246, 112, 11,
+ 54, 15, 1, 0, 0, 0,
+ 64, 7, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 196, 6, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 192, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 192,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 7, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 152, 4, 0, 0, 64, 0,
+ 1, 0, 38, 1, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 12, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 1, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 5, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 30, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 2, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 42, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 246, 31, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 4, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 26, 0, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 4, 0, 0, 0, 6, 16,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 4, 0, 0, 0, 166, 26,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 58, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 17, 0,
+ 0, 7, 66, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 16, 0, 0, 7,
+ 34, 0, 16, 0, 2, 0,
+ 0, 0, 70, 18, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 66, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 68, 0, 0, 5, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 68, 0,
+ 0, 5, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 2, 0, 0, 0, 58, 16,
+ 16, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 32, 0, 0, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 0, 0, 0, 18, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Tangent_1_32
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Tangent_1_32._fxctmp
+// /EStreamOutSkinVS_Position_Tangent_1_32 /D BONESPERVERTEX=1 /D
+// BONECOUNT=32 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[32]; // Offset: 0 Size: 1536
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[96], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v2.xyzw
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.x, v3.x, l(3)
+dp4 o0.x, r0.xyzw, cb0[r1.x + 0].xyzw
+dp4 o0.y, r0.xyzw, cb0[r1.x + 1].xyzw
+dp4 o0.z, r0.xyzw, cb0[r1.x + 2].xyzw
+mov o1.xyz, l(0,0,0,0)
+dp3 r0.x, v2.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v2.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v2.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o2.xyz, r0.wwww, r0.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 15 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Tangent_1_32[] =
+{
+ 68, 88, 66, 67, 155, 136,
+ 95, 216, 115, 102, 245, 76,
+ 254, 126, 247, 116, 91, 41,
+ 64, 92, 1, 0, 0, 0,
+ 156, 4, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 32, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 6, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 28, 2, 0, 0,
+ 64, 0, 1, 0, 135, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 96, 0, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 242, 16,
+ 16, 0, 2, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 38, 0, 0, 8, 0, 208,
+ 0, 0, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 16,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 3, 0,
+ 0, 0, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 9, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 130, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 10, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 130, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 66, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 68, 0, 0, 5, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 2, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 32,
+ 16, 0, 2, 0, 0, 0,
+ 58, 16, 16, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 15, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 9, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Tangent_2_32
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Tangent_2_32._fxctmp
+// /EStreamOutSkinVS_Position_Tangent_2_32 /D BONESPERVERTEX=2 /D
+// BONECOUNT=32 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[32]; // Offset: 0 Size: 1536
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[96], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v2.xyzw
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+imul null, r0.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+dp3 r1.x, v2.xyzx, r1.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r3.xyzw
+dp4 o0.y, r2.xyzw, r3.xyzw
+dp3 r1.y, v2.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r0.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r3.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+dp3 r1.z, v2.xyzx, r0.xyzx
+mov o1.xyz, l(0,0,0,0)
+dp3 r0.x, r1.xyzx, r1.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r1.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 21 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Tangent_2_32[] =
+{
+ 68, 88, 66, 67, 74, 166,
+ 39, 5, 4, 115, 242, 165,
+ 31, 126, 146, 216, 128, 93,
+ 62, 203, 1, 0, 0, 0,
+ 156, 5, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 32, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 6, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 244, 2, 0, 0, 64, 0,
+ 1, 0, 189, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 96, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 16, 0, 0, 7, 34, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 66, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 5, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 2, 0, 0, 0, 58, 16,
+ 16, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 21, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 12, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Tangent_4_32
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Tangent_4_32._fxctmp
+// /EStreamOutSkinVS_Position_Tangent_4_32 /D BONESPERVERTEX=4 /D
+// BONECOUNT=32 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[32]; // Offset: 0 Size: 1536
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[96], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v2.xyzw
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.zzzz, cb0[r1.z + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.wwww, cb0[r1.w + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+dp3 r2.x, v2.xyzx, r2.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.wwww, cb0[r1.w + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+dp3 r2.y, v2.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 2].xyzw, r3.xyzw
+mad r1.xyzw, v3.wwww, cb0[r1.w + 2].xyzw, r3.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r2.z, v2.xyzx, r1.xyzx
+mov o1.xyz, l(0,0,0,0)
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r2.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 27 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Tangent_4_32[] =
+{
+ 68, 88, 66, 67, 101, 9,
+ 26, 176, 87, 130, 115, 71,
+ 112, 118, 24, 231, 13, 111,
+ 146, 143, 1, 0, 0, 0,
+ 180, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 56, 6, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 6, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 12, 4, 0, 0, 64, 0,
+ 1, 0, 3, 1, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 96, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 30, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 2, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 34, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 66, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 54, 0, 0, 8,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 68, 0, 0, 5,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 2, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 32, 16, 0, 2, 0,
+ 0, 0, 58, 16, 16, 0,
+ 2, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 27, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 12, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Tangent_1_64
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Tangent_1_64._fxctmp
+// /EStreamOutSkinVS_Position_Tangent_1_64 /D BONESPERVERTEX=1 /D
+// BONECOUNT=64 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[64]; // Offset: 0 Size: 3072
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[192], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v2.xyzw
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.x, v3.x, l(3)
+dp4 o0.x, r0.xyzw, cb0[r1.x + 0].xyzw
+dp4 o0.y, r0.xyzw, cb0[r1.x + 1].xyzw
+dp4 o0.z, r0.xyzw, cb0[r1.x + 2].xyzw
+mov o1.xyz, l(0,0,0,0)
+dp3 r0.x, v2.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v2.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v2.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o2.xyz, r0.wwww, r0.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 15 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Tangent_1_64[] =
+{
+ 68, 88, 66, 67, 223, 179,
+ 178, 177, 55, 242, 85, 24,
+ 184, 123, 141, 221, 32, 76,
+ 190, 235, 1, 0, 0, 0,
+ 156, 4, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 32, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 12, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 12,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 28, 2, 0, 0,
+ 64, 0, 1, 0, 135, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 192, 0, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 242, 16,
+ 16, 0, 2, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 38, 0, 0, 8, 0, 208,
+ 0, 0, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 16,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 3, 0,
+ 0, 0, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 9, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 130, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 10, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 130, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 66, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 68, 0, 0, 5, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 2, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 32,
+ 16, 0, 2, 0, 0, 0,
+ 58, 16, 16, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 15, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 9, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Tangent_2_64
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Tangent_2_64._fxctmp
+// /EStreamOutSkinVS_Position_Tangent_2_64 /D BONESPERVERTEX=2 /D
+// BONECOUNT=64 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[64]; // Offset: 0 Size: 3072
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[192], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v2.xyzw
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+imul null, r0.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+dp3 r1.x, v2.xyzx, r1.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r3.xyzw
+dp4 o0.y, r2.xyzw, r3.xyzw
+dp3 r1.y, v2.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r0.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r3.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+dp3 r1.z, v2.xyzx, r0.xyzx
+mov o1.xyz, l(0,0,0,0)
+dp3 r0.x, r1.xyzx, r1.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r1.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 21 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Tangent_2_64[] =
+{
+ 68, 88, 66, 67, 17, 28,
+ 161, 25, 179, 10, 98, 159,
+ 87, 246, 21, 62, 158, 233,
+ 50, 151, 1, 0, 0, 0,
+ 156, 5, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 32, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 12, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 12,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 244, 2, 0, 0, 64, 0,
+ 1, 0, 189, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 192, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 16, 0, 0, 7, 34, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 66, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 5, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 2, 0, 0, 0, 58, 16,
+ 16, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 21, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 12, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Tangent_4_64
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Tangent_4_64._fxctmp
+// /EStreamOutSkinVS_Position_Tangent_4_64 /D BONESPERVERTEX=4 /D
+// BONECOUNT=64 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[64]; // Offset: 0 Size: 3072
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[192], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v2.xyzw
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.zzzz, cb0[r1.z + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.wwww, cb0[r1.w + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+dp3 r2.x, v2.xyzx, r2.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.wwww, cb0[r1.w + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+dp3 r2.y, v2.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 2].xyzw, r3.xyzw
+mad r1.xyzw, v3.wwww, cb0[r1.w + 2].xyzw, r3.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r2.z, v2.xyzx, r1.xyzx
+mov o1.xyz, l(0,0,0,0)
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r2.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 27 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Tangent_4_64[] =
+{
+ 68, 88, 66, 67, 229, 79,
+ 236, 197, 243, 207, 198, 124,
+ 162, 172, 47, 223, 207, 244,
+ 139, 83, 1, 0, 0, 0,
+ 180, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 56, 6, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 12, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 12,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 12, 4, 0, 0, 64, 0,
+ 1, 0, 3, 1, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 192, 0, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 30, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 2, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 34, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 66, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 54, 0, 0, 8,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 68, 0, 0, 5,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 2, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 32, 16, 0, 2, 0,
+ 0, 0, 58, 16, 16, 0,
+ 2, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 27, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 12, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Tangent_1_128
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Tangent_1_128._fxctmp
+// /EStreamOutSkinVS_Position_Tangent_1_128 /D BONESPERVERTEX=1 /D
+// BONECOUNT=128 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[128]; // Offset: 0 Size: 6144
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[384], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v2.xyzw
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.x, v3.x, l(3)
+dp4 o0.x, r0.xyzw, cb0[r1.x + 0].xyzw
+dp4 o0.y, r0.xyzw, cb0[r1.x + 1].xyzw
+dp4 o0.z, r0.xyzw, cb0[r1.x + 2].xyzw
+mov o1.xyz, l(0,0,0,0)
+dp3 r0.x, v2.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v2.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v2.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o2.xyz, r0.wwww, r0.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 15 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Tangent_1_128[] =
+{
+ 68, 88, 66, 67, 155, 62,
+ 239, 20, 245, 251, 229, 47,
+ 64, 93, 111, 50, 202, 43,
+ 65, 96, 1, 0, 0, 0,
+ 156, 4, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 32, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 24, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 24,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 128, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 28, 2, 0, 0,
+ 64, 0, 1, 0, 135, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 128, 1, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 242, 16,
+ 16, 0, 2, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 38, 0, 0, 8, 0, 208,
+ 0, 0, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 16,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 3, 0,
+ 0, 0, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 9, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 130, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 10, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 130, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 66, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 68, 0, 0, 5, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 2, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 32,
+ 16, 0, 2, 0, 0, 0,
+ 58, 16, 16, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 15, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 9, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Tangent_2_128
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Tangent_2_128._fxctmp
+// /EStreamOutSkinVS_Position_Tangent_2_128 /D BONESPERVERTEX=2 /D
+// BONECOUNT=128 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[128]; // Offset: 0 Size: 6144
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[384], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v2.xyzw
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+imul null, r0.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+dp3 r1.x, v2.xyzx, r1.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r3.xyzw
+dp4 o0.y, r2.xyzw, r3.xyzw
+dp3 r1.y, v2.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r0.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r3.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+dp3 r1.z, v2.xyzx, r0.xyzx
+mov o1.xyz, l(0,0,0,0)
+dp3 r0.x, r1.xyzx, r1.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r1.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 21 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Tangent_2_128[] =
+{
+ 68, 88, 66, 67, 62, 144,
+ 168, 178, 158, 150, 177, 42,
+ 146, 79, 210, 175, 39, 30,
+ 191, 85, 1, 0, 0, 0,
+ 156, 5, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 32, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 24, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 24,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 128, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 244, 2, 0, 0, 64, 0,
+ 1, 0, 189, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 128, 1, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 16, 0, 0, 7, 34, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 66, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 5, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 2, 0, 0, 0, 58, 16,
+ 16, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 21, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 12, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Tangent_4_128
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Tangent_4_128._fxctmp
+// /EStreamOutSkinVS_Position_Tangent_4_128 /D BONESPERVERTEX=4 /D
+// BONECOUNT=128 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[128]; // Offset: 0 Size: 6144
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[384], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v2.xyzw
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.zzzz, cb0[r1.z + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.wwww, cb0[r1.w + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+dp3 r2.x, v2.xyzx, r2.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.wwww, cb0[r1.w + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+dp3 r2.y, v2.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 2].xyzw, r3.xyzw
+mad r1.xyzw, v3.wwww, cb0[r1.w + 2].xyzw, r3.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r2.z, v2.xyzx, r1.xyzx
+mov o1.xyz, l(0,0,0,0)
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r2.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 27 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Tangent_4_128[] =
+{
+ 68, 88, 66, 67, 137, 190,
+ 252, 77, 211, 109, 185, 9,
+ 192, 197, 98, 56, 223, 218,
+ 111, 4, 1, 0, 0, 0,
+ 180, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 56, 6, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 24, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 24,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 128, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 12, 4, 0, 0, 64, 0,
+ 1, 0, 3, 1, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 128, 1, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 30, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 2, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 34, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 66, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 54, 0, 0, 8,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 68, 0, 0, 5,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 2, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 32, 16, 0, 2, 0,
+ 0, 0, 58, 16, 16, 0,
+ 2, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 27, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 12, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Tangent_1_512
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Tangent_1_512._fxctmp
+// /EStreamOutSkinVS_Position_Tangent_1_512 /D BONESPERVERTEX=1 /D
+// BONECOUNT=512 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[512]; // Offset: 0 Size: 24576
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[1536], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v2.xyzw
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.x, v3.x, l(3)
+dp4 o0.x, r0.xyzw, cb0[r1.x + 0].xyzw
+dp4 o0.y, r0.xyzw, cb0[r1.x + 1].xyzw
+dp4 o0.z, r0.xyzw, cb0[r1.x + 2].xyzw
+mov o1.xyz, l(0,0,0,0)
+dp3 r0.x, v2.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v2.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v2.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o2.xyz, r0.wwww, r0.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 15 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Tangent_1_512[] =
+{
+ 68, 88, 66, 67, 129, 231,
+ 80, 65, 53, 30, 137, 110,
+ 197, 69, 143, 55, 142, 52,
+ 43, 188, 1, 0, 0, 0,
+ 156, 4, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 32, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 96, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 96,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 28, 2, 0, 0,
+ 64, 0, 1, 0, 135, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 0, 6, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 242, 16,
+ 16, 0, 2, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 38, 0, 0, 8, 0, 208,
+ 0, 0, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 16,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 3, 0,
+ 0, 0, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 9, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 130, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 10, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 130, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 66, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 68, 0, 0, 5, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 2, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 32,
+ 16, 0, 2, 0, 0, 0,
+ 58, 16, 16, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 15, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 9, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Tangent_2_512
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Tangent_2_512._fxctmp
+// /EStreamOutSkinVS_Position_Tangent_2_512 /D BONESPERVERTEX=2 /D
+// BONECOUNT=512 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[512]; // Offset: 0 Size: 24576
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[1536], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v2.xyzw
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+imul null, r0.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+dp3 r1.x, v2.xyzx, r1.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r3.xyzw
+dp4 o0.y, r2.xyzw, r3.xyzw
+dp3 r1.y, v2.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r0.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r3.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+dp3 r1.z, v2.xyzx, r0.xyzx
+mov o1.xyz, l(0,0,0,0)
+dp3 r0.x, r1.xyzx, r1.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r1.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 21 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Tangent_2_512[] =
+{
+ 68, 88, 66, 67, 113, 49,
+ 101, 137, 248, 85, 224, 14,
+ 56, 186, 158, 161, 211, 124,
+ 112, 55, 1, 0, 0, 0,
+ 156, 5, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 32, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 96, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 96,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 244, 2, 0, 0, 64, 0,
+ 1, 0, 189, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 6, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 16, 0, 0, 7, 34, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 66, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 5, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 2, 0, 0, 0, 58, 16,
+ 16, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 21, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 12, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Tangent_4_512
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Tangent_4_512._fxctmp
+// /EStreamOutSkinVS_Position_Tangent_4_512 /D BONESPERVERTEX=4 /D
+// BONECOUNT=512 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[512]; // Offset: 0 Size: 24576
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[1536], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v2.xyzw
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.zzzz, cb0[r1.z + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.wwww, cb0[r1.w + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+dp3 r2.x, v2.xyzx, r2.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.wwww, cb0[r1.w + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+dp3 r2.y, v2.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 2].xyzw, r3.xyzw
+mad r1.xyzw, v3.wwww, cb0[r1.w + 2].xyzw, r3.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r2.z, v2.xyzx, r1.xyzx
+mov o1.xyz, l(0,0,0,0)
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r2.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 27 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Tangent_4_512[] =
+{
+ 68, 88, 66, 67, 123, 183,
+ 28, 216, 174, 74, 90, 228,
+ 114, 33, 239, 11, 70, 207,
+ 115, 126, 1, 0, 0, 0,
+ 180, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 56, 6, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 96, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 96,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 12, 4, 0, 0, 64, 0,
+ 1, 0, 3, 1, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 6, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 30, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 2, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 34, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 66, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 54, 0, 0, 8,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 68, 0, 0, 5,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 2, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 32, 16, 0, 2, 0,
+ 0, 0, 58, 16, 16, 0,
+ 2, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 27, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 12, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Tangent_1_1024
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Tangent_1_1024._fxctmp
+// /EStreamOutSkinVS_Position_Tangent_1_1024 /D BONESPERVERTEX=1 /D
+// BONECOUNT=1024 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[1024]; // Offset: 0 Size: 49152
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BONEINDEX 0 x 3 NONE int x
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[3072], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v2.xyzw
+dcl_input v3.x
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 2
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.x, v3.x, l(3)
+dp4 o0.x, r0.xyzw, cb0[r1.x + 0].xyzw
+dp4 o0.y, r0.xyzw, cb0[r1.x + 1].xyzw
+dp4 o0.z, r0.xyzw, cb0[r1.x + 2].xyzw
+mov o1.xyz, l(0,0,0,0)
+dp3 r0.x, v2.xyzx, cb0[r1.x + 0].xyzx
+dp3 r0.y, v2.xyzx, cb0[r1.x + 1].xyzx
+dp3 r0.z, v2.xyzx, cb0[r1.x + 2].xyzx
+dp3 r0.w, r0.xyzx, r0.xyzx
+rsq r0.w, r0.w
+mul o2.xyz, r0.wwww, r0.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 15 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Tangent_1_1024[] =
+{
+ 68, 88, 66, 67, 38, 248,
+ 167, 170, 23, 21, 153, 218,
+ 35, 216, 26, 106, 21, 87,
+ 80, 123, 1, 0, 0, 0,
+ 156, 4, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 144, 1,
+ 0, 0, 252, 1, 0, 0,
+ 32, 4, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 192, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 192,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 140, 0,
+ 0, 0, 4, 0, 0, 0,
+ 8, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 1, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 79,
+ 78, 69, 73, 78, 68, 69,
+ 88, 0, 171, 171, 79, 83,
+ 71, 78, 100, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 7, 8,
+ 0, 0, 89, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 15, 0,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 84,
+ 69, 88, 67, 79, 79, 82,
+ 68, 0, 171, 171, 83, 72,
+ 68, 82, 28, 2, 0, 0,
+ 64, 0, 1, 0, 135, 0,
+ 0, 0, 89, 8, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 0, 12, 0, 0,
+ 95, 0, 0, 3, 114, 16,
+ 16, 0, 0, 0, 0, 0,
+ 95, 0, 0, 3, 242, 16,
+ 16, 0, 2, 0, 0, 0,
+ 95, 0, 0, 3, 18, 16,
+ 16, 0, 3, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 18, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 38, 0, 0, 8, 0, 208,
+ 0, 0, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 16,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 3, 0,
+ 0, 0, 17, 0, 0, 9,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 17, 0, 0, 10,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 17, 0, 0, 10, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 9, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 130, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 10, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 130, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 10, 66, 0, 16, 0,
+ 0, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 130, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 7,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 68, 0, 0, 5, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 32, 16, 0, 2, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 32,
+ 16, 0, 2, 0, 0, 0,
+ 58, 16, 16, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 15, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 9, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Tangent_2_1024
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Tangent_2_1024._fxctmp
+// /EStreamOutSkinVS_Position_Tangent_2_1024 /D BONESPERVERTEX=2 /D
+// BONECOUNT=1024 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[1024]; // Offset: 0 Size: 49152
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xy 3 NONE float xy
+// BLENDINDICES 0 xy 4 NONE int xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[3072], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v2.xyzw
+dcl_input v3.xy
+dcl_input v4.xy
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+imul null, r0.xy, v4.xyxx, l(3, 3, 0, 0)
+mul r1.xyzw, v3.yyyy, cb0[r0.y + 0].xyzw
+mad r1.xyzw, v3.xxxx, cb0[r0.x + 0].xyzw, r1.xyzw
+mov r2.xyz, v0.xyzx
+mov r2.w, l(1.000000)
+dp4 o0.x, r2.xyzw, r1.xyzw
+dp3 r1.x, v2.xyzx, r1.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r0.x + 1].xyzw, r3.xyzw
+dp4 o0.y, r2.xyzw, r3.xyzw
+dp3 r1.y, v2.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r0.y + 2].xyzw
+mad r0.xyzw, v3.xxxx, cb0[r0.x + 2].xyzw, r3.xyzw
+dp4 o0.z, r2.xyzw, r0.xyzw
+dp3 r1.z, v2.xyzx, r0.xyzx
+mov o1.xyz, l(0,0,0,0)
+dp3 r0.x, r1.xyzx, r1.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r1.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 21 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Tangent_2_1024[] =
+{
+ 68, 88, 66, 67, 145, 156,
+ 116, 21, 77, 210, 128, 144,
+ 84, 248, 102, 43, 138, 123,
+ 120, 5, 1, 0, 0, 0,
+ 156, 5, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 32, 5, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 192, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 192,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 3, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 244, 2, 0, 0, 64, 0,
+ 1, 0, 189, 0, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 12, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 38, 0, 0, 11, 0, 208,
+ 0, 0, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 4, 0, 0, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 56, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 114, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 17, 0, 0, 7,
+ 18, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 34, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 16, 0, 0, 7, 34, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 21,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 6, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 50, 0, 0, 12,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 6, 16, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 17, 0, 0, 7,
+ 66, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 66, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 18, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 8, 114, 32, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 7, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 5, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 5, 130, 32, 16, 0,
+ 2, 0, 0, 0, 58, 16,
+ 16, 0, 2, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 21, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 12, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+//--------------------------------------------------------------
+// StreamOutSkinVS_Position_Tangent_4_1024
+//--------------------------------------------------------------
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//
+// fxc /nologo /T vs_4_0 /FhStreamOutSkinVS_Position_Tangent_4_1024._fxctmp
+// /EStreamOutSkinVS_Position_Tangent_4_1024 /D BONESPERVERTEX=4 /D
+// BONECOUNT=1024 internalshaders.hlsl
+//
+//
+// Buffer Definitions:
+//
+// cbuffer cbBones
+// {
+//
+// float4x3 bones[1024]; // Offset: 0 Size: 49152
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim Slot Elements
+// ------------------------------ ---------- ------- ----------- ---- --------
+// cbBones cbuffer NA NA 0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// NORMAL 0 xyz 1 NONE float
+// TANGENT 0 xyzw 2 NONE float xyzw
+// BLENDWEIGHT 0 xyzw 3 NONE float xyzw
+// BLENDINDICES 0 xyzw 4 NONE int xyzw
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------ ------
+// POSITION 0 xyz 0 NONE float xyz
+// TEXCOORD 0 xyz 1 NONE float xyz
+// TEXCOORD 1 xyzw 2 NONE float xyzw
+//
+vs_4_0
+dcl_constantbuffer cb0[3072], dynamicIndexed
+dcl_input v0.xyz
+dcl_input v2.xyzw
+dcl_input v3.xyzw
+dcl_input v4.xyzw
+dcl_output o0.xyz
+dcl_output o1.xyz
+dcl_output o2.xyzw
+dcl_temps 4
+mov r0.xyz, v0.xyzx
+mov r0.w, l(1.000000)
+imul null, r1.xyzw, v4.xyzw, l(3, 3, 3, 3)
+mul r2.xyzw, v3.yyyy, cb0[r1.y + 0].xyzw
+mad r2.xyzw, v3.xxxx, cb0[r1.x + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.zzzz, cb0[r1.z + 0].xyzw, r2.xyzw
+mad r2.xyzw, v3.wwww, cb0[r1.w + 0].xyzw, r2.xyzw
+dp4 o0.x, r0.xyzw, r2.xyzw
+dp3 r2.x, v2.xyzx, r2.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 1].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 1].xyzw, r3.xyzw
+mad r3.xyzw, v3.wwww, cb0[r1.w + 1].xyzw, r3.xyzw
+dp4 o0.y, r0.xyzw, r3.xyzw
+dp3 r2.y, v2.xyzx, r3.xyzx
+mul r3.xyzw, v3.yyyy, cb0[r1.y + 2].xyzw
+mad r3.xyzw, v3.xxxx, cb0[r1.x + 2].xyzw, r3.xyzw
+mad r3.xyzw, v3.zzzz, cb0[r1.z + 2].xyzw, r3.xyzw
+mad r1.xyzw, v3.wwww, cb0[r1.w + 2].xyzw, r3.xyzw
+dp4 o0.z, r0.xyzw, r1.xyzw
+dp3 r2.z, v2.xyzx, r1.xyzx
+mov o1.xyz, l(0,0,0,0)
+dp3 r0.x, r2.xyzx, r2.xyzx
+rsq r0.x, r0.x
+mul o2.xyz, r0.xxxx, r2.xyzx
+mov o2.w, v2.w
+ret
+// Approximately 27 instruction slots used
+#endif
+
+const BYTE g_StreamOutSkinVS_Position_Tangent_4_1024[] =
+{
+ 68, 88, 66, 67, 134, 193,
+ 76, 236, 200, 241, 162, 242,
+ 117, 255, 185, 145, 58, 140,
+ 105, 114, 1, 0, 0, 0,
+ 180, 6, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 252, 0, 0, 0, 184, 1,
+ 0, 0, 36, 2, 0, 0,
+ 56, 6, 0, 0, 82, 68,
+ 69, 70, 192, 0, 0, 0,
+ 1, 0, 0, 0, 68, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 140, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 99, 98, 66, 111, 110, 101,
+ 115, 0, 60, 0, 0, 0,
+ 1, 0, 0, 0, 92, 0,
+ 0, 0, 0, 192, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 192,
+ 0, 0, 2, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 115, 0, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 73, 83, 71, 78, 180, 0,
+ 0, 0, 5, 0, 0, 0,
+ 8, 0, 0, 0, 128, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 137, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 7, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 15, 15, 0, 0, 152, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 15, 15, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 15, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 78, 79, 82, 77, 65,
+ 76, 0, 84, 65, 78, 71,
+ 69, 78, 84, 0, 66, 76,
+ 69, 78, 68, 87, 69, 73,
+ 71, 72, 84, 0, 66, 76,
+ 69, 78, 68, 73, 78, 68,
+ 73, 67, 69, 83, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 7, 8, 0, 0,
+ 89, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 15, 0, 0, 0,
+ 80, 79, 83, 73, 84, 73,
+ 79, 78, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 83, 72, 68, 82,
+ 12, 4, 0, 0, 64, 0,
+ 1, 0, 3, 1, 0, 0,
+ 89, 8, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 12, 0, 0, 95, 0,
+ 0, 3, 114, 16, 16, 0,
+ 0, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 2, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 3, 0, 0, 0, 95, 0,
+ 0, 3, 242, 16, 16, 0,
+ 4, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 0, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 54, 0, 0, 5, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 18, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 38, 0,
+ 0, 11, 0, 208, 0, 0,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 30, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 56, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 86, 21, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 11, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 4,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 50, 0, 0, 11,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 4, 0, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 242, 0, 16, 0,
+ 2, 0, 0, 0, 246, 31,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 4, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 17, 0, 0, 7, 18, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 1, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 34, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 16, 0,
+ 0, 7, 34, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 21, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 6, 0, 0, 0, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 6, 16, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 166, 26, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 12, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 246, 31, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 6,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 17, 0, 0, 7, 66, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 7, 66, 0, 16, 0,
+ 2, 0, 0, 0, 70, 18,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 54, 0, 0, 8,
+ 114, 32, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 68, 0, 0, 5,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 2, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 32, 16, 0, 2, 0,
+ 0, 0, 58, 16, 16, 0,
+ 2, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 27, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 12, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0
+};
+const BYTE * g_StreamOutShaders[4][5][3] = {
+{
+{
+g_StreamOutSkinVS_Position_1_32,
+g_StreamOutSkinVS_Position_2_32,
+g_StreamOutSkinVS_Position_4_32,
+},
+{
+g_StreamOutSkinVS_Position_1_64,
+g_StreamOutSkinVS_Position_2_64,
+g_StreamOutSkinVS_Position_4_64,
+},
+{
+g_StreamOutSkinVS_Position_1_128,
+g_StreamOutSkinVS_Position_2_128,
+g_StreamOutSkinVS_Position_4_128,
+},
+{
+g_StreamOutSkinVS_Position_1_512,
+g_StreamOutSkinVS_Position_2_512,
+g_StreamOutSkinVS_Position_4_512,
+},
+{
+g_StreamOutSkinVS_Position_1_1024,
+g_StreamOutSkinVS_Position_2_1024,
+g_StreamOutSkinVS_Position_4_1024,
+},
+},
+{
+{
+g_StreamOutSkinVS_Position_Normal_1_32,
+g_StreamOutSkinVS_Position_Normal_2_32,
+g_StreamOutSkinVS_Position_Normal_4_32,
+},
+{
+g_StreamOutSkinVS_Position_Normal_1_64,
+g_StreamOutSkinVS_Position_Normal_2_64,
+g_StreamOutSkinVS_Position_Normal_4_64,
+},
+{
+g_StreamOutSkinVS_Position_Normal_1_128,
+g_StreamOutSkinVS_Position_Normal_2_128,
+g_StreamOutSkinVS_Position_Normal_4_128,
+},
+{
+g_StreamOutSkinVS_Position_Normal_1_512,
+g_StreamOutSkinVS_Position_Normal_2_512,
+g_StreamOutSkinVS_Position_Normal_4_512,
+},
+{
+g_StreamOutSkinVS_Position_Normal_1_1024,
+g_StreamOutSkinVS_Position_Normal_2_1024,
+g_StreamOutSkinVS_Position_Normal_4_1024,
+},
+},
+{
+{
+g_StreamOutSkinVS_Position_Normal_Tangent_1_32,
+g_StreamOutSkinVS_Position_Normal_Tangent_2_32,
+g_StreamOutSkinVS_Position_Normal_Tangent_4_32,
+},
+{
+g_StreamOutSkinVS_Position_Normal_Tangent_1_64,
+g_StreamOutSkinVS_Position_Normal_Tangent_2_64,
+g_StreamOutSkinVS_Position_Normal_Tangent_4_64,
+},
+{
+g_StreamOutSkinVS_Position_Normal_Tangent_1_128,
+g_StreamOutSkinVS_Position_Normal_Tangent_2_128,
+g_StreamOutSkinVS_Position_Normal_Tangent_4_128,
+},
+{
+g_StreamOutSkinVS_Position_Normal_Tangent_1_512,
+g_StreamOutSkinVS_Position_Normal_Tangent_2_512,
+g_StreamOutSkinVS_Position_Normal_Tangent_4_512,
+},
+{
+g_StreamOutSkinVS_Position_Normal_Tangent_1_1024,
+g_StreamOutSkinVS_Position_Normal_Tangent_2_1024,
+g_StreamOutSkinVS_Position_Normal_Tangent_4_1024,
+},
+},
+{
+{
+g_StreamOutSkinVS_Position_Tangent_1_32,
+g_StreamOutSkinVS_Position_Tangent_2_32,
+g_StreamOutSkinVS_Position_Tangent_4_32,
+},
+{
+g_StreamOutSkinVS_Position_Tangent_1_64,
+g_StreamOutSkinVS_Position_Tangent_2_64,
+g_StreamOutSkinVS_Position_Tangent_4_64,
+},
+{
+g_StreamOutSkinVS_Position_Tangent_1_128,
+g_StreamOutSkinVS_Position_Tangent_2_128,
+g_StreamOutSkinVS_Position_Tangent_4_128,
+},
+{
+g_StreamOutSkinVS_Position_Tangent_1_512,
+g_StreamOutSkinVS_Position_Tangent_2_512,
+g_StreamOutSkinVS_Position_Tangent_4_512,
+},
+{
+g_StreamOutSkinVS_Position_Tangent_1_1024,
+g_StreamOutSkinVS_Position_Tangent_2_1024,
+g_StreamOutSkinVS_Position_Tangent_4_1024,
+},
+},
+};
+const size_t g_StreamOutShaderSizes[4][5][3] = {
+{
+{
+sizeof(g_StreamOutSkinVS_Position_1_32),
+sizeof(g_StreamOutSkinVS_Position_2_32),
+sizeof(g_StreamOutSkinVS_Position_4_32),
+},
+{
+sizeof(g_StreamOutSkinVS_Position_1_64),
+sizeof(g_StreamOutSkinVS_Position_2_64),
+sizeof(g_StreamOutSkinVS_Position_4_64),
+},
+{
+sizeof(g_StreamOutSkinVS_Position_1_128),
+sizeof(g_StreamOutSkinVS_Position_2_128),
+sizeof(g_StreamOutSkinVS_Position_4_128),
+},
+{
+sizeof(g_StreamOutSkinVS_Position_1_512),
+sizeof(g_StreamOutSkinVS_Position_2_512),
+sizeof(g_StreamOutSkinVS_Position_4_512),
+},
+{
+sizeof(g_StreamOutSkinVS_Position_1_1024),
+sizeof(g_StreamOutSkinVS_Position_2_1024),
+sizeof(g_StreamOutSkinVS_Position_4_1024),
+},
+},
+{
+{
+sizeof(g_StreamOutSkinVS_Position_Normal_1_32),
+sizeof(g_StreamOutSkinVS_Position_Normal_2_32),
+sizeof(g_StreamOutSkinVS_Position_Normal_4_32),
+},
+{
+sizeof(g_StreamOutSkinVS_Position_Normal_1_64),
+sizeof(g_StreamOutSkinVS_Position_Normal_2_64),
+sizeof(g_StreamOutSkinVS_Position_Normal_4_64),
+},
+{
+sizeof(g_StreamOutSkinVS_Position_Normal_1_128),
+sizeof(g_StreamOutSkinVS_Position_Normal_2_128),
+sizeof(g_StreamOutSkinVS_Position_Normal_4_128),
+},
+{
+sizeof(g_StreamOutSkinVS_Position_Normal_1_512),
+sizeof(g_StreamOutSkinVS_Position_Normal_2_512),
+sizeof(g_StreamOutSkinVS_Position_Normal_4_512),
+},
+{
+sizeof(g_StreamOutSkinVS_Position_Normal_1_1024),
+sizeof(g_StreamOutSkinVS_Position_Normal_2_1024),
+sizeof(g_StreamOutSkinVS_Position_Normal_4_1024),
+},
+},
+{
+{
+sizeof(g_StreamOutSkinVS_Position_Normal_Tangent_1_32),
+sizeof(g_StreamOutSkinVS_Position_Normal_Tangent_2_32),
+sizeof(g_StreamOutSkinVS_Position_Normal_Tangent_4_32),
+},
+{
+sizeof(g_StreamOutSkinVS_Position_Normal_Tangent_1_64),
+sizeof(g_StreamOutSkinVS_Position_Normal_Tangent_2_64),
+sizeof(g_StreamOutSkinVS_Position_Normal_Tangent_4_64),
+},
+{
+sizeof(g_StreamOutSkinVS_Position_Normal_Tangent_1_128),
+sizeof(g_StreamOutSkinVS_Position_Normal_Tangent_2_128),
+sizeof(g_StreamOutSkinVS_Position_Normal_Tangent_4_128),
+},
+{
+sizeof(g_StreamOutSkinVS_Position_Normal_Tangent_1_512),
+sizeof(g_StreamOutSkinVS_Position_Normal_Tangent_2_512),
+sizeof(g_StreamOutSkinVS_Position_Normal_Tangent_4_512),
+},
+{
+sizeof(g_StreamOutSkinVS_Position_Normal_Tangent_1_1024),
+sizeof(g_StreamOutSkinVS_Position_Normal_Tangent_2_1024),
+sizeof(g_StreamOutSkinVS_Position_Normal_Tangent_4_1024),
+},
+},
+{
+{
+sizeof(g_StreamOutSkinVS_Position_Tangent_1_32),
+sizeof(g_StreamOutSkinVS_Position_Tangent_2_32),
+sizeof(g_StreamOutSkinVS_Position_Tangent_4_32),
+},
+{
+sizeof(g_StreamOutSkinVS_Position_Tangent_1_64),
+sizeof(g_StreamOutSkinVS_Position_Tangent_2_64),
+sizeof(g_StreamOutSkinVS_Position_Tangent_4_64),
+},
+{
+sizeof(g_StreamOutSkinVS_Position_Tangent_1_128),
+sizeof(g_StreamOutSkinVS_Position_Tangent_2_128),
+sizeof(g_StreamOutSkinVS_Position_Tangent_4_128),
+},
+{
+sizeof(g_StreamOutSkinVS_Position_Tangent_1_512),
+sizeof(g_StreamOutSkinVS_Position_Tangent_2_512),
+sizeof(g_StreamOutSkinVS_Position_Tangent_4_512),
+},
+{
+sizeof(g_StreamOutSkinVS_Position_Tangent_1_1024),
+sizeof(g_StreamOutSkinVS_Position_Tangent_2_1024),
+sizeof(g_StreamOutSkinVS_Position_Tangent_4_1024),
+},
+},
+};
diff --git a/Runtime/GfxDevice/d3d11/InternalShaders/compile_all.bat b/Runtime/GfxDevice/d3d11/InternalShaders/compile_all.bat
new file mode 100644
index 0000000..055f100
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/InternalShaders/compile_all.bat
@@ -0,0 +1,82 @@
+@echo off
+del FFShaderLib.h
+call "CompileShaderLib\CompileShaderLib.exe" FFShaderLib.hlsl FFShaderLib.h
+
+del builtin.h
+
+echo // > builtin.h
+echo // >> builtin.h
+echo // Autogenerated file. Do not modify! >> builtin.h
+echo // >> builtin.h
+echo // >> builtin.h
+
+echo const BYTE * g_StreamOutShaders[4][5][3] = { >tmp.h
+echo const size_t g_StreamOutShaderSizes[4][5][3] = { >tmp2.h
+
+call:compile StreamOutSkinVS_Position
+call:compile StreamOutSkinVS_Position_Normal
+call:compile StreamOutSkinVS_Position_Normal_Tangent
+call:compile StreamOutSkinVS_Position_Tangent
+
+echo }; >>tmp.h
+echo }; >>tmp2.h
+
+type tmp.h >>builtin.h
+type tmp2.h >>builtin.h
+
+del tmp.h
+del tmp2.h
+
+goto:eof
+
+:compile
+
+echo { >>tmp.h
+echo { >>tmp2.h
+
+call:compilecbvariants %~1 32
+call:compilecbvariants %~1 64
+call:compilecbvariants %~1 128
+call:compilecbvariants %~1 512
+call:compilecbvariants %~1 1024
+
+echo }, >>tmp.h
+echo }, >>tmp2.h
+
+
+goto:eof
+
+::- Compiles all variants for a given bone constant buffer size
+::- Arguments: %~1 = entrypoint name, %~2 = max bone count
+:compilecbvariants
+
+echo { >>tmp.h
+echo { >>tmp2.h
+
+call:compilevariant %~1 1 %~2
+call:compilevariant %~1 2 %~2
+call:compilevariant %~1 4 %~2
+
+echo },>>tmp.h
+echo },>>tmp2.h
+
+
+goto:eof
+
+::- Compiles a single shader variant
+::- Arguments: %~1 = entrypoint name %~2 = bones per vertex %~3 = max bone count
+:compilevariant
+
+call "%DXSDK_DIR%\Utilities\bin\x64\fxc.exe" /nologo /T vs_4_0 /Fh%~1_%~2_%~3._fxctmp /E%~1_%~2_%~3 /D BONESPERVERTEX=%~2 /D BONECOUNT=%~3 internalshaders.hlsl
+
+echo //-------------------------------------------------------------- >> builtin.h
+echo // %~1_%~2_%~3 >> builtin.h
+echo //-------------------------------------------------------------- >> builtin.h
+
+type %~1_%~2_%~3._fxctmp >> builtin.h
+del %~1_%~2_%~3._fxctmp
+
+echo g_%~1_%~2_%~3, >>tmp.h
+echo sizeof(g_%~1_%~2_%~3), >> tmp2.h
+
+goto:eof
diff --git a/Runtime/GfxDevice/d3d11/InternalShaders/internalshaders.hlsl b/Runtime/GfxDevice/d3d11/InternalShaders/internalshaders.hlsl
new file mode 100644
index 0000000..2dce211
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/InternalShaders/internalshaders.hlsl
@@ -0,0 +1,119 @@
+//--------------------------------------------------------------------------------------
+// Vertex shaders for stream-out GPU skinning.
+//--------------------------------------------------------------------------------------
+
+//--------------------------------------------------------------------------------------
+// Globals
+//--------------------------------------------------------------------------------------
+cbuffer cbBones : register ( b0 )
+{
+ float4x3 bones[BONECOUNT];
+}
+
+//--------------------------------------------------------------------------------------
+// Input / Output structures
+//--------------------------------------------------------------------------------------
+struct VS_IN_MEX
+{
+ // Stream 0
+ float3 Position : POSITION;
+ float3 Normal : NORMAL;
+ float4 Tangent : TANGENT;
+
+ // Stream 1
+#if BONESPERVERTEX == 4
+ float4 BoneWeights : BLENDWEIGHT;
+ int4 BoneIndices : BLENDINDICES;
+
+#elif BONESPERVERTEX == 2
+ float2 BoneWeights : BLENDWEIGHT;
+ int2 BoneIndices : BLENDINDICES;
+
+#else // 1 bone per vertex
+ int BoneIndices : BONEINDEX;
+
+#endif
+};
+
+struct VS_OUTPUT
+{
+ float3 vPosition : POSITION;
+ float3 vNormal : TEXCOORD0;
+ float4 vTangent : TEXCOORD1;
+};
+
+// --- Bones ---
+inline float4x3 FetchBoneMatrix( int boneIndex )
+{
+ return (float4x3)(bones[boneIndex]);
+}
+//--------------------------------------------------------------------------------------
+// Vertex Shader
+//--------------------------------------------------------------------------------------
+VS_OUTPUT VSMain( VS_IN_MEX Input, uniform bool useNormal, uniform bool useTangent)
+{
+ VS_OUTPUT Output = (VS_OUTPUT)0;
+
+#if BONESPERVERTEX == 4
+ float boneWeights[4] = (float[4])Input.BoneWeights;
+ int boneIndices[4] = (int[4])Input.BoneIndices;
+
+ float4x3 localToWorldMatrix = boneWeights[0] * FetchBoneMatrix(boneIndices[0]) +
+ boneWeights[1] * FetchBoneMatrix(boneIndices[1]) +
+ boneWeights[2] * FetchBoneMatrix(boneIndices[2]) +
+ boneWeights[3] * FetchBoneMatrix(boneIndices[3]);
+
+#elif BONESPERVERTEX == 2
+ float boneWeights[2] = (float[2])Input.BoneWeights;
+ int boneIndices[2] = (int[2])Input.BoneIndices;
+
+ float4x3 localToWorldMatrix = boneWeights[0] * FetchBoneMatrix(boneIndices[0]) +
+ boneWeights[1] * FetchBoneMatrix(boneIndices[1]);
+
+
+#else // 1
+ int boneIndex = Input.BoneIndices;
+
+ float4x3 localToWorldMatrix = FetchBoneMatrix(boneIndex);
+#endif
+
+ // Position
+ Output.vPosition = mul( float4(Input.Position.xyz, 1.0), localToWorldMatrix ).xyz;
+ if (useNormal)
+ {
+ Output.vNormal = normalize( mul( float4(Input.Normal.xyz,0.0f), localToWorldMatrix ) ).xyz;
+ }
+
+ // Tangent
+ if (useTangent)
+ {
+ float3 outTangent3 = normalize( mul( float4(Input.Tangent.xyz,0.0f), localToWorldMatrix ) ).xyz;
+ Output.vTangent = float4(outTangent3, Input.Tangent.w);
+ }
+
+ return Output;
+}
+
+// Functions are named StreamOutSkinVS_<components>_<bonespervertex>_<maxbonecount>
+
+#define MERGE(a, b, c, delim) a##delim##b##delim##c
+
+VS_OUTPUT MERGE(StreamOutSkinVS_Position,BONESPERVERTEX,BONECOUNT,_)(VS_IN_MEX Input)
+{
+ return VSMain(Input, false, false);
+}
+
+VS_OUTPUT MERGE(StreamOutSkinVS_Position_Normal,BONESPERVERTEX,BONECOUNT,_)(VS_IN_MEX Input)
+{
+ return VSMain(Input, true, false);
+}
+
+VS_OUTPUT MERGE(StreamOutSkinVS_Position_Normal_Tangent,BONESPERVERTEX,BONECOUNT,_)(VS_IN_MEX Input)
+{
+ return VSMain(Input, true, true);
+}
+
+VS_OUTPUT MERGE(StreamOutSkinVS_Position_Tangent,BONESPERVERTEX,BONECOUNT,_)(VS_IN_MEX Input)
+{
+ return VSMain(Input, false, true);
+}
diff --git a/Runtime/GfxDevice/d3d11/RenderTextureD3D11.cpp b/Runtime/GfxDevice/d3d11/RenderTextureD3D11.cpp
new file mode 100644
index 0000000..9c50172
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/RenderTextureD3D11.cpp
@@ -0,0 +1,805 @@
+#include "UnityPrefix.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "D3D11Context.h"
+#include "D3D11Utils.h"
+#include "TexturesD3D11.h"
+
+void UnbindTextureD3D11 (TextureID texture);
+
+//Resource format
+DXGI_FORMAT kD3D11RenderResourceFormats[kRTFormatCount] = {
+ DXGI_FORMAT_R8G8B8A8_TYPELESS,
+ DXGI_FORMAT_R24G8_TYPELESS,
+ DXGI_FORMAT_R16G16B16A16_TYPELESS,
+ DXGI_FORMAT_R16_TYPELESS,
+ (DXGI_FORMAT)-1, // RGB565, unsupported
+ (DXGI_FORMAT)-1, // ARGB4444, unsupported
+ (DXGI_FORMAT)-1, // ARGB1555, unsupported
+ (DXGI_FORMAT)-1, // Default
+ DXGI_FORMAT_R10G10B10A2_TYPELESS,
+ (DXGI_FORMAT)-1, // DefaultHDR
+ DXGI_FORMAT_R16G16B16A16_TYPELESS,
+ DXGI_FORMAT_R32G32B32A32_TYPELESS,
+ DXGI_FORMAT_R32G32_TYPELESS,
+ DXGI_FORMAT_R16G16_TYPELESS,
+ DXGI_FORMAT_R32_TYPELESS,
+ DXGI_FORMAT_R16_TYPELESS,
+ DXGI_FORMAT_R8_TYPELESS, // R8
+ DXGI_FORMAT_R32G32B32A32_TYPELESS, // ARGBInt
+ DXGI_FORMAT_R32G32_TYPELESS, // RGInt
+ DXGI_FORMAT_R32_TYPELESS, // RInt
+ DXGI_FORMAT_B8G8R8A8_TYPELESS,
+};
+
+//Standard view
+DXGI_FORMAT kD3D11RenderTextureFormatsNorm[kRTFormatCount] = {
+ DXGI_FORMAT_R8G8B8A8_UNORM,
+ DXGI_FORMAT_D24_UNORM_S8_UINT,
+ DXGI_FORMAT_R16G16B16A16_FLOAT,
+ DXGI_FORMAT_D16_UNORM,
+ (DXGI_FORMAT)-1, // RGB565, unsupported
+ (DXGI_FORMAT)-1, // ARGB4444, unsupported
+ (DXGI_FORMAT)-1, // ARGB1555, unsupported
+ (DXGI_FORMAT)-1, // Default
+ DXGI_FORMAT_R10G10B10A2_UNORM,
+ (DXGI_FORMAT)-1, // DefaultHDR
+ DXGI_FORMAT_R16G16B16A16_UNORM,
+ DXGI_FORMAT_R32G32B32A32_FLOAT,
+ DXGI_FORMAT_R32G32_FLOAT,
+ DXGI_FORMAT_R16G16_FLOAT,
+ DXGI_FORMAT_R32_FLOAT,
+ DXGI_FORMAT_R16_FLOAT,
+ DXGI_FORMAT_R8_UNORM, // R8
+ DXGI_FORMAT_R32G32B32A32_SINT, // ARGBInt
+ DXGI_FORMAT_R32G32_SINT, // RGInt
+ DXGI_FORMAT_R32_SINT, // RInt
+ DXGI_FORMAT_B8G8R8A8_UNORM, // BGRA32
+};
+
+// SRGBView... only used for RGBA8 buffers really.
+DXGI_FORMAT kD3D11RenderTextureFormatsSRGB[kRTFormatCount] = {
+ DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
+ DXGI_FORMAT_D24_UNORM_S8_UINT,
+ DXGI_FORMAT_R16G16B16A16_FLOAT,
+ DXGI_FORMAT_D16_UNORM,
+ (DXGI_FORMAT)-1, // RGB565, unsupported
+ (DXGI_FORMAT)-1, // ARGB4444, unsupported
+ (DXGI_FORMAT)-1, // ARGB1555, unsupported
+ (DXGI_FORMAT)-1, // Default
+ DXGI_FORMAT_R10G10B10A2_UNORM,
+ (DXGI_FORMAT)-1, // DefaultHDR
+ DXGI_FORMAT_R16G16B16A16_UNORM,
+ DXGI_FORMAT_R32G32B32A32_FLOAT,
+ DXGI_FORMAT_R32G32_FLOAT,
+ DXGI_FORMAT_R16G16_FLOAT,
+ DXGI_FORMAT_R32_FLOAT,
+ DXGI_FORMAT_R16_FLOAT,
+ DXGI_FORMAT_R8_UNORM, // R8
+ DXGI_FORMAT_R32G32B32A32_SINT, // ARGBInt
+ DXGI_FORMAT_R32G32_SINT, // RGInt
+ DXGI_FORMAT_R32_SINT, // RInt
+ DXGI_FORMAT_B8G8R8A8_UNORM_SRGB,
+};
+
+DXGI_FORMAT GetRenderTextureFormat (RenderTextureFormat format, bool sRGB)
+{
+ return sRGB ? kD3D11RenderTextureFormatsSRGB[format] : kD3D11RenderTextureFormatsNorm[format];
+}
+
+DXGI_FORMAT GetShaderResourceViewFormat (RenderTextureFormat format, bool sRGB)
+{
+ if (format == kRTFormatDepth)
+ {
+ return DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
+ }
+ else if (format == kRTFormatShadowMap)
+ {
+ return DXGI_FORMAT_R16_UNORM;
+ }
+ else
+ {
+ return sRGB ? kD3D11RenderTextureFormatsSRGB[format] : kD3D11RenderTextureFormatsNorm[format];
+ }
+}
+
+
+static ID3D11Resource* CreateTextureD3D11 (int width, int height, int depth, int mipLevels, DXGI_FORMAT format, UINT bindFlags, TextureDimension dim, int antiAlias)
+{
+ if (dim == kTexDim3D)
+ {
+ if (gGraphicsCaps.buggyMipmapped3DTextures)
+ mipLevels = 1;
+ D3D11_TEXTURE3D_DESC desc;
+ desc.Width = width;
+ desc.Height = height;
+ desc.Depth = depth;
+ desc.MipLevels = mipLevels;
+ desc.Format = format;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ desc.BindFlags = bindFlags;
+ desc.CPUAccessFlags = 0;
+ desc.MiscFlags = 0;
+ if (mipLevels > 1) desc.MiscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS;
+ ID3D11Texture3D* res = NULL;
+ HRESULT hr = GetD3D11Device()->CreateTexture3D (&desc, NULL, &res);
+ Assert (SUCCEEDED(hr));
+ SetDebugNameD3D11 (res, Format("RenderTexture-3D-%dx%dx%d", width, height, depth));
+ return res;
+ }
+ else
+ {
+ if (dim == kTexDimCUBE && gGraphicsCaps.buggyMipmappedCubemaps)
+ mipLevels = 1;
+ D3D11_TEXTURE2D_DESC desc;
+ desc.Width = width;
+ desc.Height = height;
+ desc.MipLevels = mipLevels;
+ desc.ArraySize = dim==kTexDimCUBE ? 6 : 1;
+ desc.Format = format;
+ desc.SampleDesc.Count = antiAlias;
+ desc.SampleDesc.Quality = 0;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ desc.BindFlags = bindFlags;
+ desc.CPUAccessFlags = 0;
+ desc.MiscFlags = 0;
+ if (dim == kTexDimCUBE) desc.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE;
+ if (mipLevels > 1) desc.MiscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS;
+ ID3D11Texture2D* res = NULL;
+ HRESULT hr = GetD3D11Device()->CreateTexture2D (&desc, NULL, &res);
+ Assert (SUCCEEDED(hr));
+ SetDebugNameD3D11 (res, Format("RenderTexture-2D-%dx%d", width, height));
+ return res;
+ }
+}
+
+
+static bool InitD3D11RenderColorSurface (RenderColorSurfaceD3D11& rs, TexturesD3D11& textures)
+{
+ HRESULT hr;
+ ID3D11Device* dev = GetD3D11Device();
+
+ bool sRGBPrimary = (rs.flags & kSurfaceCreateSRGB);
+
+ UINT bindFlags = 0;
+ if (!IsDepthRTFormat (rs.format))
+ bindFlags |= D3D11_BIND_RENDER_TARGET;
+ if (rs.textureID.m_ID)
+ bindFlags |= D3D11_BIND_SHADER_RESOURCE;
+ if (rs.flags & kSurfaceCreateRandomWrite && gGraphicsCaps.d3d11.featureLevel >= kDX11Level11_0)
+ bindFlags |= D3D11_BIND_UNORDERED_ACCESS;
+ int mipLevels = 1;
+ int accessibleMipLevels = 1;
+ if ((rs.flags & kSurfaceCreateMipmap) && !IsDepthRTFormat(rs.format))
+ {
+ mipLevels = CalculateMipMapCount3D (rs.width, rs.height, rs.depth);
+ if (!(rs.flags & kSurfaceCreateAutoGenMips))
+ accessibleMipLevels = mipLevels;
+ }
+ const DXGI_FORMAT texFormat = (gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0 ? kD3D11RenderResourceFormats[rs.format] : kD3D11RenderTextureFormatsNorm[rs.format]);
+ if (bindFlags != 0)
+ rs.m_Texture = CreateTextureD3D11 (rs.width, rs.height, rs.depth, mipLevels, texFormat, bindFlags, rs.dim, rs.samples);
+ else
+ rs.m_Texture = NULL;
+ // Render Target View
+ if (!IsDepthRTFormat (rs.format))
+ {
+ D3D11_RENDER_TARGET_VIEW_DESC desc, descSecondary;
+ desc.Format = GetRenderTextureFormat (rs.format, gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0 ? sRGBPrimary : false);
+ descSecondary.Format = GetRenderTextureFormat (rs.format, gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0 ? !sRGBPrimary : false);
+ ID3D11RenderTargetView* rtv = NULL;
+ if (rs.dim == kTexDim2D)
+ {
+ desc.ViewDimension = descSecondary.ViewDimension = rs.samples > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS : D3D11_RTV_DIMENSION_TEXTURE2D;
+ for (int im = 0; im < accessibleMipLevels; ++im)
+ {
+ desc.Texture2D.MipSlice = descSecondary.Texture2D.MipSlice = im;
+ hr = dev->CreateRenderTargetView (rs.m_Texture, &desc, &rtv);
+ Assert (SUCCEEDED(hr));
+ rs.SetRTV (0, im, false, rtv);
+ hr = dev->CreateRenderTargetView (rs.m_Texture, &descSecondary, &rtv);
+ Assert (SUCCEEDED(hr));
+ rs.SetRTV (0, im, true, rtv);
+ }
+ }
+ else if (rs.dim == kTexDimCUBE)
+ {
+ desc.ViewDimension = descSecondary.ViewDimension = rs.samples > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY : D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
+ desc.Texture2DArray.ArraySize = descSecondary.Texture2DArray.ArraySize = 1;
+ for (int im = 0; im < accessibleMipLevels; ++im)
+ {
+ desc.Texture2DArray.MipSlice = descSecondary.Texture2DArray.MipSlice = im;
+ for (int i = 0; i < 6; ++i)
+ {
+ desc.Texture2DArray.FirstArraySlice = descSecondary.Texture2DArray.FirstArraySlice = i;
+ hr = dev->CreateRenderTargetView (rs.m_Texture, &desc, &rtv);
+ Assert (SUCCEEDED(hr));
+ rs.SetRTV (i, im, false, rtv);
+
+ hr = dev->CreateRenderTargetView (rs.m_Texture, &descSecondary, &rtv);
+ Assert (SUCCEEDED(hr));
+ rs.SetRTV (i, im, true, rtv);
+ }
+ }
+ }
+ else if (rs.dim == kTexDim3D)
+ {
+ desc.ViewDimension = descSecondary.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
+ desc.Texture3D.MipSlice = descSecondary.Texture3D.MipSlice = 0;
+ desc.Texture3D.FirstWSlice = descSecondary.Texture3D.FirstWSlice = 0;
+ desc.Texture3D.WSize = descSecondary.Texture3D.WSize = -1;
+ hr = dev->CreateRenderTargetView (rs.m_Texture, &desc, &rtv);
+ Assert (SUCCEEDED(hr));
+ rs.SetRTV (0, 0, false, rtv);
+
+ hr = dev->CreateRenderTargetView (rs.m_Texture, &descSecondary, &rtv);
+ Assert (SUCCEEDED(hr));
+ rs.SetRTV (0, 0, true, rtv);
+ }
+ }
+
+ // Shader Resource View if needed
+ if (rs.textureID.m_ID)
+ {
+ D3D11_SHADER_RESOURCE_VIEW_DESC desc;
+ desc.Format = GetShaderResourceViewFormat (rs.format, (gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0) ? sRGBPrimary : false);
+ switch (rs.dim) {
+ case kTexDimCUBE: desc.ViewDimension = rs.samples > 1 ? D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY : D3D11_SRV_DIMENSION_TEXTURECUBE; break;
+ case kTexDim3D: desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; break;
+ default: desc.ViewDimension = rs.samples > 1 ? D3D11_SRV_DIMENSION_TEXTURE2DMS : D3D11_SRV_DIMENSION_TEXTURE2D; break;
+ }
+ desc.Texture2D.MostDetailedMip = 0;
+ desc.Texture2D.MipLevels = mipLevels;
+
+ ID3D11ShaderResourceView* srView = NULL;
+ hr = dev->CreateShaderResourceView (rs.m_Texture, &desc, &rs.m_SRView);
+ Assert (SUCCEEDED(hr));
+
+ SetDebugNameD3D11 (rs.m_SRView, Format("RenderTexture-SRV-%d-color-%dx%d", rs.textureID.m_ID, rs.width, rs.height));
+
+ // If we need to generate mips, create view without sRGB format. Seems like some drivers
+ // have slow paths for updating sRGB formats, and from reading the docs, it's not clear if
+ // mip update for sRGB formats is supported everywhere.
+ if (mipLevels > 1)
+ {
+ desc.Format = GetShaderResourceViewFormat (rs.format, false);
+ hr = dev->CreateShaderResourceView (rs.m_Texture, &desc, &rs.m_SRViewForMips);
+ Assert (SUCCEEDED(hr));
+ SetDebugNameD3D11 (rs.m_SRViewForMips, Format("RenderTexture-SRV-%d-color-%dx%d-mips", rs.textureID.m_ID, rs.width, rs.height));
+ }
+ }
+
+ // UAV if needed
+ if (rs.flags & kSurfaceCreateRandomWrite && gGraphicsCaps.d3d11.featureLevel >= kDX11Level11_0)
+ {
+ D3D11_UNORDERED_ACCESS_VIEW_DESC desc;
+ desc.Format = GetShaderResourceViewFormat (rs.format, false); // UAV formats don't support sRGB
+ if (rs.dim == kTexDim3D)
+ {
+ desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D;
+ desc.Texture3D.MipSlice = 0;
+ desc.Texture3D.FirstWSlice = 0;
+ desc.Texture3D.WSize = -1;
+ }
+ else
+ {
+ desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
+ desc.Texture2D.MipSlice = 0;
+ }
+
+ hr = dev->CreateUnorderedAccessView (rs.m_Texture, &desc, &rs.m_UAView);
+ Assert (SUCCEEDED(hr));
+ SetDebugNameD3D11 (rs.m_UAView, Format("RenderTexture-UAV-%d-color-%dx%d", rs.textureID.m_ID, rs.width, rs.height));
+ }
+
+ // add to textures map
+ if (rs.textureID.m_ID)
+ {
+ textures.AddTexture (rs.textureID, rs.m_Texture, rs.m_SRView, rs.m_UAView, false);
+ }
+
+ return true;
+}
+
+bool InitD3D11RenderDepthSurface (RenderDepthSurfaceD3D11& rs, TexturesD3D11* textures, bool sampleOnly)
+{
+ HRESULT hr;
+ ID3D11Device* dev = GetD3D11Device();
+
+ const bool shadowMap = rs.flags & kSurfaceCreateShadowmap;
+ const bool useTypeless = (gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0 || gGraphicsCaps.d3d11.hasShadows10Level9);
+ const bool createZ = (rs.depthFormat != kDepthFormatNone);
+
+ rs.m_Texture = NULL;
+ rs.m_DSView = NULL;
+ rs.m_SRView = NULL;
+
+ DXGI_FORMAT formatResource;
+ DXGI_FORMAT formatDSV;
+ DXGI_FORMAT formatSRV;
+ if (shadowMap)
+ {
+ formatResource = DXGI_FORMAT_R16_TYPELESS;
+ formatDSV = DXGI_FORMAT_D16_UNORM;
+ formatSRV = DXGI_FORMAT_R16_UNORM;
+ }
+ else if (rs.depthFormat == kDepthFormat16)
+ {
+ formatResource = useTypeless ? DXGI_FORMAT_R16_TYPELESS : DXGI_FORMAT_D16_UNORM;
+ formatDSV = DXGI_FORMAT_D16_UNORM;
+ formatSRV = useTypeless ? DXGI_FORMAT_R16_UNORM : DXGI_FORMAT_D16_UNORM;
+ }
+ else
+ {
+ formatResource = useTypeless ? DXGI_FORMAT_R24G8_TYPELESS : DXGI_FORMAT_D24_UNORM_S8_UINT;
+ formatDSV = DXGI_FORMAT_D24_UNORM_S8_UINT;
+ formatSRV = useTypeless ? DXGI_FORMAT_R24_UNORM_X8_TYPELESS : DXGI_FORMAT_D24_UNORM_S8_UINT;
+ }
+
+ // Pre-DX11 feature level hardware can't do depth buffer sampling & have it as a depth stencil,
+ // but DX11 can.
+ if (gGraphicsCaps.d3d11.featureLevel >= kDX11Level11_0)
+ sampleOnly = false;
+
+ if (rs.dim != kTexDim2D)
+ {
+ // Starting with 10.1 level, we can have a cubemap color surface and a regular 2D depth surface. Before that,
+ // have to create the depth surface as a fake cubemap as well. Leave dimension as cubemap only when
+ // cubemap was requested AND we're below 10.1
+ if (rs.dim != kTexDimCUBE || gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_1)
+ rs.dim = kTexDim2D;
+ }
+
+ if (createZ)
+ {
+ UINT bindFlags = sampleOnly ? 0 : D3D11_BIND_DEPTH_STENCIL;
+ if (rs.textureID.m_ID && (gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0 || gGraphicsCaps.d3d11.hasShadows10Level9))
+ bindFlags |= D3D11_BIND_SHADER_RESOURCE;
+ rs.m_Texture = CreateTextureD3D11 (rs.width, rs.height, 1, 1, formatResource, bindFlags, rs.dim, rs.samples);
+ }
+
+ // Depth Stencil view if needed
+ if (createZ && !sampleOnly)
+ {
+ D3D11_DEPTH_STENCIL_VIEW_DESC desc;
+ desc.Format = formatDSV;
+ desc.ViewDimension = rs.samples > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS : D3D11_DSV_DIMENSION_TEXTURE2D;
+ desc.Flags = 0;
+ desc.Texture2D.MipSlice = 0;
+ hr = dev->CreateDepthStencilView (rs.m_Texture, &desc, &rs.m_DSView);
+ Assert (SUCCEEDED(hr));
+ }
+
+ // Shader Resource View if needed
+ if (createZ && rs.textureID.m_ID && (gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0 || gGraphicsCaps.d3d11.hasShadows10Level9))
+ {
+ D3D11_SHADER_RESOURCE_VIEW_DESC desc;
+ desc.Format = formatSRV;
+ desc.ViewDimension = rs.samples > 1 ? D3D11_SRV_DIMENSION_TEXTURE2DMS : D3D11_SRV_DIMENSION_TEXTURE2D;
+ desc.Texture2D.MostDetailedMip = 0;
+ desc.Texture2D.MipLevels = 1;
+
+ ID3D11ShaderResourceView* srView = NULL;
+ hr = dev->CreateShaderResourceView (rs.m_Texture, &desc, &rs.m_SRView);
+ Assert (SUCCEEDED(hr));
+ SetDebugNameD3D11 (rs.m_SRView, Format("RenderTexture-SRV-%d-depth-%dx%d", rs.textureID.m_ID, rs.width, rs.height));
+ }
+
+ if (createZ && rs.textureID.m_ID && textures)
+ textures->AddTexture (rs.textureID, rs.m_Texture, rs.m_SRView, rs.m_UAView, rs.flags & kSurfaceCreateShadowmap);
+
+ return true;
+}
+
+static RenderColorSurfaceD3D11* s_ActiveColorTargets[kMaxSupportedRenderTargets];
+static int s_ActiveColorTargetCount;
+static bool s_SRGBWrite;
+static RenderDepthSurfaceD3D11* s_ActiveDepthTarget = NULL;
+static CubemapFace s_ActiveFace = kCubeFaceUnknown;
+static int s_ActiveMip = 0;
+
+static RenderColorSurfaceD3D11* s_ActiveColorBackBuffer = NULL;
+static RenderDepthSurfaceD3D11* s_ActiveDepthBackBuffer = NULL;
+
+// on dx editor we can switch swapchain underneath
+// so lets do smth like gl's default FBO
+// it will be used only from "user" code and we will select proper swap chain here
+static RenderColorSurfaceD3D11* s_DummyColorBackBuffer = NULL;
+static RenderDepthSurfaceD3D11* s_DummyDepthBackBuffer = NULL;
+
+RenderSurfaceBase* DummyColorBackBuferD3D11()
+{
+ if(s_DummyColorBackBuffer == 0)
+ {
+ static RenderColorSurfaceD3D11 __bb;
+ RenderSurfaceBase_InitColor(__bb);
+ __bb.backBuffer = true;
+
+ s_DummyColorBackBuffer = &__bb;
+ }
+ return s_DummyColorBackBuffer;
+}
+
+RenderSurfaceBase* DummyDepthBackBuferD3D11()
+{
+ if(s_DummyDepthBackBuffer == 0)
+ {
+ static RenderDepthSurfaceD3D11 __bb;
+ RenderSurfaceBase_InitDepth(__bb);
+ __bb.backBuffer = true;
+
+ s_DummyDepthBackBuffer = &__bb;
+ }
+ return s_DummyDepthBackBuffer;
+}
+
+
+static int s_UAVMaxIndex = -1;
+static TextureID s_UAVTextures[kMaxSupportedRenderTargets];
+static ComputeBufferID s_UAVBuffers[kMaxSupportedRenderTargets];
+
+
+
+static bool SetRenderTargetD3D11Internal (int count, RenderColorSurfaceD3D11** colorSurfaces, RenderDepthSurfaceD3D11* depthSurface, int mipLevel, CubemapFace face, int* outTargetWidth, int* outTargetHeight, TexturesD3D11* textures, bool forceRebind)
+{
+ RenderColorSurfaceD3D11* rcolorZero = colorSurfaces[0];
+
+ // check context is created
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ if (!ctx)
+ {
+ Assert (!rcolorZero && !depthSurface);
+ return false;
+ }
+
+ bool sRGBWriteDesired = GetRealGfxDevice().GetSRGBWrite();
+
+ // Exit if nothing to do
+ if (!forceRebind && count == s_ActiveColorTargetCount && sRGBWriteDesired == s_SRGBWrite && s_ActiveDepthTarget == depthSurface && s_ActiveFace == face && s_ActiveMip == mipLevel)
+ {
+ bool colorsSame = true;
+ for (int i = 0; i < count; ++i)
+ {
+ if (s_ActiveColorTargets[i] != colorSurfaces[i])
+ colorsSame = false;
+ }
+ if (colorsSame)
+ {
+ if (rcolorZero != s_DummyColorBackBuffer)
+ {
+ if (outTargetWidth) *outTargetWidth = rcolorZero->width;
+ if (outTargetHeight) *outTargetHeight = rcolorZero->height;
+ }
+ return false;
+ }
+ }
+
+ Assert(rcolorZero->backBuffer == depthSurface->backBuffer);
+ bool isBackBuffer = rcolorZero->backBuffer;
+
+
+ GfxDevice& device = GetRealGfxDevice();
+ if (!isBackBuffer)
+ device.GetFrameStats().AddRenderTextureChange(); // stats
+
+ if(rcolorZero->backBuffer && rcolorZero == s_DummyColorBackBuffer)
+ rcolorZero = colorSurfaces[0] = s_ActiveColorBackBuffer;
+ if(depthSurface->backBuffer && depthSurface == s_DummyDepthBackBuffer)
+ depthSurface = s_ActiveDepthBackBuffer;
+
+
+ HRESULT hr;
+
+ if (rcolorZero)
+ {
+ if (depthSurface && depthSurface->textureID.m_ID)
+ UnbindTextureD3D11 (depthSurface->textureID);
+
+ Assert (rcolorZero->colorSurface);
+ Assert (!depthSurface || !depthSurface->colorSurface);
+
+ int faceIndex = clamp<int>(face, 0, 5);
+ ID3D11RenderTargetView* rtvs[kMaxSupportedRenderTargets];
+ for (int i = 0; i < count; ++i)
+ {
+ RenderColorSurfaceD3D11* rcolor = colorSurfaces[i];
+ if (rcolor->textureID.m_ID)
+ UnbindTextureD3D11 (rcolor->textureID);
+
+ bool wantSecondaryView = ((rcolor->flags & kSurfaceCreateSRGB) != 0) != sRGBWriteDesired;
+ rtvs[i] = rcolor->GetRTV(faceIndex, mipLevel, wantSecondaryView);
+ }
+
+ const int uavCount = s_UAVMaxIndex - (count-1);
+ if (uavCount <= 0)
+ {
+ // set render targets
+ ctx->OMSetRenderTargets (count, rtvs, depthSurface ? depthSurface->m_DSView : NULL);
+ }
+ else
+ {
+ DebugAssert (uavCount > 0 && uavCount <= kMaxSupportedRenderTargets);
+ // set render targets and UAVs
+ ID3D11UnorderedAccessView* uavs[kMaxSupportedRenderTargets];
+ for (int i = 0; i < uavCount; ++i)
+ {
+ int idx = i + count;
+ DebugAssert (idx >= 0 && idx < kMaxSupportedRenderTargets);
+ ID3D11UnorderedAccessView* uav = NULL;
+ if (s_UAVTextures[idx].m_ID && textures)
+ {
+ TexturesD3D11::D3D11Texture* tex = textures->GetTexture (s_UAVTextures[idx]);
+ if (tex)
+ uav = tex->m_UAV;
+ }
+ else if (s_UAVBuffers[idx].IsValid() && textures)
+ {
+ ComputeBuffer11* cb = textures->GetComputeBuffer (s_UAVBuffers[idx]);
+ if (cb)
+ uav = cb->uav;
+ }
+ uavs[i] = uav;
+ }
+ UINT uavInitialCounts[kMaxSupportedRenderTargets];
+ for (int i = 0; i < kMaxSupportedRenderTargets; ++i)
+ uavInitialCounts[i] = 0; // reset offsets for Countable/Appendable/Consumeable UAVs
+ ctx->OMSetRenderTargetsAndUnorderedAccessViews (count, rtvs, depthSurface ? depthSurface->m_DSView : NULL, count, uavCount, uavs, uavInitialCounts);
+ }
+
+ g_D3D11CurrRT = rtvs[0];
+ g_D3D11CurrColorRT = rcolorZero;
+ g_D3D11CurrDS = depthSurface ? depthSurface->m_DSView : NULL;
+ g_D3D11CurrDepthRT = depthSurface;
+ if (outTargetWidth) *outTargetWidth = rcolorZero->width;
+ if (outTargetHeight)*outTargetHeight = rcolorZero->height;
+ }
+
+ // If we previously had a mip-mapped render texture, generate mip levels for it now.
+ RenderColorSurfaceD3D11* prevRT = s_ActiveColorTargets[0];
+ RenderColorSurfaceD3D11* currRT = colorSurfaces[0];
+ if (prevRT &&
+ (prevRT->flags & kSurfaceCreateMipmap) &&
+ (prevRT->flags & kSurfaceCreateAutoGenMips) &&
+ prevRT->m_SRViewForMips &&
+ currRT != prevRT)
+ {
+ ctx->GenerateMips (prevRT->m_SRViewForMips);
+ }
+
+ for (int i = 0; i < count; ++i)
+ s_ActiveColorTargets[i] = colorSurfaces[i];
+
+ s_ActiveColorTargetCount = count;
+ s_ActiveDepthTarget = depthSurface;
+ s_ActiveFace = face;
+ s_ActiveMip = mipLevel;
+
+ if (isBackBuffer)
+ {
+ s_ActiveColorBackBuffer = rcolorZero;
+ s_ActiveDepthBackBuffer = depthSurface;
+
+ // we are rendering to "default FBO", so current target is dummy
+ // as a side effect, if we change swap chain, it will be set correctly, and active remain valid
+ s_ActiveColorTargets[0] = s_DummyColorBackBuffer;
+ s_ActiveDepthTarget = s_DummyDepthBackBuffer;
+ }
+
+ s_SRGBWrite = sRGBWriteDesired;
+ return true;
+}
+
+bool SetRenderTargetD3D11 (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face, int* outTargetWidth, int* outTargetHeight, TexturesD3D11* textures)
+{
+ RenderColorSurfaceD3D11* colorTargets[kMaxSupportedRenderTargets];
+ RenderDepthSurfaceD3D11* rdepth = reinterpret_cast<RenderDepthSurfaceD3D11*>( depthHandle.object );
+ for (int i = 0; i < count; ++i)
+ {
+ colorTargets[i] = reinterpret_cast<RenderColorSurfaceD3D11*>(colorHandles[i].object);
+ }
+
+ return SetRenderTargetD3D11Internal (count, colorTargets, rdepth, mipLevel, face, outTargetWidth, outTargetHeight, textures, false);
+}
+
+bool RebindActiveRenderTargets(TexturesD3D11* textures)
+{
+ int width, height;
+ return SetRenderTargetD3D11Internal (s_ActiveColorTargetCount, s_ActiveColorTargets, s_ActiveDepthTarget, s_ActiveMip, s_ActiveFace, &width, &height, textures, true);
+}
+
+
+void SetRandomWriteTargetTextureD3D11 (int index, TextureID tid)
+{
+ Assert (index >= 0 && index < ARRAY_SIZE(s_UAVTextures));
+ s_UAVMaxIndex = std::max (s_UAVMaxIndex, index);
+ s_UAVTextures[index] = tid;
+ s_UAVBuffers[index] = ComputeBufferID();
+}
+
+void SetRandomWriteTargetBufferD3D11 (int index, ComputeBufferID bufferHandle)
+{
+ Assert (index >= 0 && index < ARRAY_SIZE(s_UAVBuffers));
+ s_UAVMaxIndex = std::max (s_UAVMaxIndex, index);
+ s_UAVBuffers[index] = bufferHandle;
+ s_UAVTextures[index].m_ID = 0;
+}
+
+void ClearRandomWriteTargetsD3D11 (TexturesD3D11* textures)
+{
+ const bool resetRenderTargets = (s_UAVMaxIndex != -1);
+
+ s_UAVMaxIndex = -1;
+ for (int i = 0; i < ARRAY_SIZE(s_UAVTextures); ++i)
+ s_UAVTextures[i].m_ID = 0;
+ for (int i = 0; i < ARRAY_SIZE(s_UAVBuffers); ++i)
+ s_UAVBuffers[i] = ComputeBufferID();
+
+ if (resetRenderTargets)
+ RebindActiveRenderTargets (textures);
+}
+
+
+RenderSurfaceHandle GetActiveRenderColorSurfaceD3D11(int index)
+{
+ return RenderSurfaceHandle(s_ActiveColorTargets[index]);
+}
+RenderSurfaceHandle GetActiveRenderDepthSurfaceD3D11()
+{
+ return RenderSurfaceHandle(s_ActiveDepthTarget);
+}
+
+RenderSurfaceHandle GetActiveRenderColorSurfaceBBD3D11()
+{
+ RenderColorSurfaceD3D11* ret = s_ActiveColorTargets[0];
+ if(ret == s_DummyColorBackBuffer)
+ ret = s_ActiveColorBackBuffer;
+
+ return RenderSurfaceHandle(ret);
+}
+
+bool IsActiveRenderTargetWithColorD3D11()
+{
+ return !s_ActiveColorTargets[0] || s_ActiveColorTargets[0]->backBuffer || s_ActiveColorTargets[0]->m_Texture;
+}
+
+RenderSurfaceHandle CreateRenderColorSurfaceD3D11 (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, UInt32 createFlags, RenderTextureFormat format, TexturesD3D11& textures)
+{
+ RenderSurfaceHandle rsHandle;
+
+ if( !gGraphicsCaps.hasRenderToTexture )
+ return rsHandle;
+ if( !gGraphicsCaps.supportsRenderTextureFormat[format] )
+ return rsHandle;
+
+ RenderColorSurfaceD3D11* rs = new RenderColorSurfaceD3D11;
+ rs->width = width;
+ rs->height = height;
+ rs->samples = samples;
+ rs->depth = depth;
+ rs->format = format;
+ rs->textureID = textureID;
+ rs->dim = dim;
+ rs->flags = createFlags;
+
+ // Create it
+ if (!InitD3D11RenderColorSurface(*rs, textures))
+ {
+ delete rs;
+ return rsHandle;
+ }
+
+ rsHandle.object = rs;
+ return rsHandle;
+}
+
+RenderSurfaceHandle CreateRenderDepthSurfaceD3D11 (TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags, TexturesD3D11& textures)
+{
+ RenderSurfaceHandle rsHandle;
+
+ if( !gGraphicsCaps.hasRenderToTexture )
+ return rsHandle;
+
+ RenderDepthSurfaceD3D11* rs = new RenderDepthSurfaceD3D11;
+ rs->width = width;
+ rs->height = height;
+ rs->samples = samples;
+ rs->dim = dim;
+ rs->depthFormat = depthFormat;
+ rs->textureID = textureID;
+ rs->flags = createFlags;
+
+ // Create it
+ bool sampleOnly = (createFlags & kSurfaceCreateSampleOnly) != 0;
+ if (!InitD3D11RenderDepthSurface (*rs, &textures, sampleOnly))
+ {
+ delete rs;
+ return rsHandle;
+ }
+
+ rsHandle.object = rs;
+ return rsHandle;
+}
+
+void InternalDestroyRenderSurfaceD3D11 (RenderSurfaceD3D11* rs, TexturesD3D11* textures)
+{
+ AssertIf( !rs );
+
+ if(rs == s_ActiveColorBackBuffer || rs == s_ActiveDepthBackBuffer)
+ {
+ s_ActiveColorBackBuffer = NULL;
+ s_ActiveDepthBackBuffer = NULL;
+ }
+
+ RenderSurfaceHandle defaultColor(s_DummyColorBackBuffer);
+ RenderSurfaceHandle defaultDepth(s_DummyDepthBackBuffer);
+
+ for (int i = 0; i < s_ActiveColorTargetCount; ++i)
+ {
+ if (s_ActiveColorTargets[i] == rs)
+ {
+ ErrorString( "RenderTexture warning: Destroying active render texture. Switching to main context." );
+ SetRenderTargetD3D11 (1, &defaultColor, defaultDepth, 0, kCubeFaceUnknown, NULL, NULL, textures);
+ }
+ }
+ if (s_ActiveDepthTarget == rs)
+ {
+ ErrorString( "RenderTexture warning: Destroying active render texture. Switching to main context." );
+ SetRenderTargetD3D11 (1, &defaultColor, defaultDepth, 0, kCubeFaceUnknown, NULL, NULL, textures);
+ }
+
+ if (rs->m_Texture || rs->textureID.m_ID)
+ {
+ UnbindTextureD3D11 (rs->textureID);
+ if (textures)
+ textures->RemoveTexture (rs->textureID);
+ }
+
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(rs->m_Texture);
+ if (rs->colorSurface)
+ {
+ RenderColorSurfaceD3D11* colorRS = static_cast<RenderColorSurfaceD3D11*>(rs);
+ colorRS->Reset();
+ }
+ else
+ {
+ RenderDepthSurfaceD3D11* depthRS = static_cast<RenderDepthSurfaceD3D11*>(rs);
+ depthRS->Reset();
+ }
+}
+
+void DestroyRenderSurfaceD3D11 (RenderSurfaceHandle& rsHandle, TexturesD3D11& textures)
+{
+ if( !rsHandle.IsValid() )
+ return;
+
+ RenderSurfaceD3D11* rs = reinterpret_cast<RenderSurfaceD3D11*>( rsHandle.object );
+ InternalDestroyRenderSurfaceD3D11 (rs, &textures);
+ delete rs;
+ rsHandle.object = NULL;
+}
+
+
+
+// --------------------------------------------------------------------------
+
+
+#if ENABLE_UNIT_TESTS
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (RenderTextureD3D11Tests)
+{
+TEST(RenderTextureD3D11_FormatTableCorrect)
+{
+ // checks that you did not forget to update format table when adding a new format :)
+ for (int i = 0; i < kRTFormatCount; ++i)
+ {
+ CHECK(kD3D11RenderResourceFormats[i] != 0);
+ CHECK(kD3D11RenderTextureFormatsNorm[i] != 0);
+ CHECK(kD3D11RenderTextureFormatsSRGB[i] != 0);
+ }
+}
+}
+
+#endif
diff --git a/Runtime/GfxDevice/d3d11/ShaderGeneratorD3D11.cpp b/Runtime/GfxDevice/d3d11/ShaderGeneratorD3D11.cpp
new file mode 100644
index 0000000..4752142
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/ShaderGeneratorD3D11.cpp
@@ -0,0 +1,1504 @@
+#include "UnityPrefix.h"
+#include "ShaderGeneratorD3D11.h"
+#include "FixedFunctionStateD3D11.h"
+#include "ConstantBuffersD3D11.h"
+#include "D3D11Context.h"
+#include "Runtime/GfxDevice/GpuProgram.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "D3D11ByteCode.h"
+
+#define DEBUG_D3D11_FF_SHADERS (!UNITY_RELEASE && 0)
+#define DEBUG_D3D11_COMPARE_WITH_HLSL (DEBUG_D3D11_FF_SHADERS && 0)
+
+ConstantBuffersD3D11& GetD3D11ConstantBuffers (GfxDevice& device);
+
+
+// --- Debugging ---------------------------------------------------------------------------------
+
+
+#if DEBUG_D3D11_FF_SHADERS
+
+#include "Runtime/GfxDevice/d3d11/D3D11Compiler.h"
+#if UNITY_WINRT
+#include "PlatformDependent/MetroPlayer/MetroUtils.h"
+#endif
+
+static D3D11Compiler s_Compiler;
+
+static bool HasD3D11Compiler()
+{
+ static bool initialized = false;
+ if (!initialized)
+ {
+ //s_Compiler.Initialize (kD3D11CompilerDLL);
+ const char* dllName = kD3D11CompilerDLL;
+ s_Compiler.compileFunc = NULL;
+ s_Compiler.stripShaderFunc = NULL;
+ s_Compiler.reflectFunc = NULL;
+ s_Compiler.disassembleFunc = NULL;
+ s_Compiler.createBlobFunc = NULL;
+
+ #if UNITY_WINRT
+ HMODULE dll = LoadPackagedLibrary (ConvertToWindowsPath(dllName)->Data(), 0);
+ #else
+ HMODULE dll = LoadLibraryA (dllName);
+ #endif
+ if (dll)
+ {
+ s_Compiler.compileFunc = (D3D11Compiler::D3DCompileFunc) GetProcAddress (dll, "D3DCompile");
+ s_Compiler.stripShaderFunc = (D3D11Compiler::D3DStripShaderFunc) GetProcAddress (dll, "D3DStripShader");
+ s_Compiler.reflectFunc = (D3D11Compiler::D3DReflectFunc) GetProcAddress (dll, "D3DReflect");
+ s_Compiler.disassembleFunc = (D3D11Compiler::D3DDisassembleFunc) GetProcAddress (dll, "D3DDisassemble");
+ s_Compiler.createBlobFunc = (D3D11Compiler::D3DCreateBlobFunc) GetProcAddress (dll, "D3DCreateBlob");
+ }
+ }
+ return s_Compiler.IsValid();
+}
+
+#endif // #if DEBUG_D3D11_FF_SHADERS
+
+
+#if DEBUG_D3D11_COMPARE_WITH_HLSL
+
+enum D3DCOMPILER_STRIP_FLAGS
+{
+ D3DCOMPILER_STRIP_REFLECTION_DATA = 1,
+ D3DCOMPILER_STRIP_DEBUG_INFO = 2,
+ D3DCOMPILER_STRIP_TEST_BLOBS = 4,
+ D3DCOMPILER_STRIP_FORCE_DWORD = 0x7fffffff,
+};
+
+#define D3D_DISASM_ENABLE_COLOR_CODE 1
+#define D3D_DISASM_ENABLE_DEFAULT_VALUE_PRINTS 2
+#define D3D_DISASM_ENABLE_INSTRUCTION_NUMBERING 4
+#define D3D_DISASM_ENABLE_INSTRUCTION_CYCLE 8
+
+static void DebugCompileHLSLShaderD3D11 (const std::string& source, bool vertex)
+{
+ if (!HasD3D11Compiler())
+ return;
+
+ ID3D10Blob* shader = NULL;
+ ID3D10Blob* errors;
+ Assert (s_Compiler.compileFunc);
+ HRESULT hr = s_Compiler.compileFunc (
+ source.c_str(),
+ source.size(),
+ "source",
+ NULL,
+ NULL,
+ "main",
+ gGraphicsCaps.d3d11.featureLevel < kDX11Level10_0
+ ? (vertex ? "vs_4_0_level_9_3" : "ps_4_0_level_9_3")
+ : (vertex ? "vs_4_0" : "ps_4_0"),
+ 0,
+ 0,
+ &shader,
+ &errors);
+
+ if (FAILED(hr))
+ {
+ printf_console ("Failed to compile D3D11 shader:\n%s\n", source.c_str());
+ if (errors)
+ {
+ std::string msg (reinterpret_cast<const char*>(errors->GetBufferPointer()), errors->GetBufferSize());
+ printf_console ("\nErrors:\n%s\n", msg.c_str());
+ errors->Release();
+ }
+ else
+ {
+ printf_console ("\nErrors unknown!\n");
+ }
+ AssertString ("Failed to compile fixed function D3D11 shader");
+ return;
+ }
+
+ if (shader && s_Compiler.stripShaderFunc)
+ {
+ ID3D10Blob* strippedShader = NULL;
+
+ hr = s_Compiler.stripShaderFunc (shader->GetBufferPointer(), shader->GetBufferSize(), D3DCOMPILER_STRIP_REFLECTION_DATA | D3DCOMPILER_STRIP_DEBUG_INFO | D3DCOMPILER_STRIP_TEST_BLOBS, &strippedShader);
+ if (SUCCEEDED(hr))
+ {
+ SAFE_RELEASE(shader);
+ shader = strippedShader;
+ }
+ }
+
+ SAFE_RELEASE(errors);
+
+ if (shader && s_Compiler.disassembleFunc)
+ {
+ ID3D10Blob* disasm = NULL;
+ hr = s_Compiler.disassembleFunc (shader->GetBufferPointer(), shader->GetBufferSize(), D3D_DISASM_ENABLE_DEFAULT_VALUE_PRINTS, NULL, &disasm);
+ if (SUCCEEDED(hr) && disasm)
+ {
+ printf_console ("disasm:\n%s\n\n", disasm->GetBufferPointer());
+ }
+ SAFE_RELEASE(disasm);
+ }
+
+ SAFE_RELEASE(shader);
+}
+
+static inline void AddToStringList (std::string& str, const char* s)
+{
+ if (!str.empty())
+ str += ',';
+ str += s;
+}
+
+#endif // #if DEBUG_D3D11_COMPARE_WITH_HLSL
+
+
+
+// --- Constant buffers & utilities --------------------------------------------------------------
+
+
+static const char* kD3D11VertexCB = "UnityFFVertex";
+static const char* kD3D11PixelCB = "UnityFFPixel";
+
+enum {
+ k11VertexMVP = 0,
+ k11VertexMV = 4,
+ k11VertexColor = 8,
+ k11VertexAmbient = 9,
+ k11VertexLightColor = 10,
+ k11VertexLightPos = 18,
+ k11VertexLightAtten = 26,
+ k11VertexLightSpot = 34,
+ k11VertexMatDiffuse = 42,
+ k11VertexMatAmbient = 43,
+ k11VertexMatSpec = 44,
+ k11VertexMatEmission = 45,
+ k11VertexTex = 46,
+ k11VertexFog = 62,
+ k11VertexSize = 63,
+ k11VertexPosOffset9x = k11VertexSize+1,
+};
+//k11VertexPosOffset9x will be used like that:
+// mad oPos.xy, v0.w, c63, v0
+// mov oPos.zw, v0
+
+#if DEBUG_D3D11_COMPARE_WITH_HLSL
+static const char* kD3D11VertexPrefix =
+ "cbuffer UnityFFVertex {\n"
+ " float4x4 ff_matrix_mvp;\n" // 0
+ " float4x4 ff_matrix_mv;\n" // 4
+ " float4 ff_vec_color;\n" // 8
+ " float4 ff_vec_ambient;\n" // 9
+ " float4 ff_light_color[8];\n" // 10
+ " float4 ff_light_pos[8];\n" // 18
+ " float4 ff_light_atten[8];\n" // 26
+ " float4 ff_light_spot[8];\n" // 34
+ " float4 ff_mat_diffuse;\n" // 42
+ " float4 ff_mat_ambient;\n" // 43
+ " float4 ff_mat_spec;\n" // 44
+ " float4 ff_mat_emission;\n" // 45
+ " float4x4 ff_matrix_tex[4];\n" // 46
+ " float4 ff_fog;\n" // 62
+ "};\n"; // 62
+#endif // #if DEBUG_D3D11_COMPARE_WITH_HLSL
+
+
+enum {
+ k11PixelColors = 0,
+ k11PixelAlphaRef = 8,
+ k11PixelFog = 9,
+ k11PixelSize = 10
+};
+#if DEBUG_D3D11_COMPARE_WITH_HLSL
+static const char* kD3D11PixelPrefix =
+"cbuffer UnityFFPixel {\n"
+" float4 ff_vec_colors[8];\n" // 0
+" float ff_alpha_ref;\n" // 8
+" float4 ff_fog;\n" // 9
+"};\n"
+"float4 main (\n ";
+#endif // # if DEBUG_D3D11_COMPARE_WITH_HLSL
+
+
+static void* BuildShaderD3D11 (DXBCBuilder* builder, size_t& outSize)
+{
+ Assert(builder);
+
+ void* dxbc = dxb_build (builder, outSize);
+ Assert(dxbc);
+ dxb_destroy (builder);
+
+ #if DEBUG_D3D11_FF_SHADERS
+ if (HasD3D11Compiler() && s_Compiler.disassembleFunc)
+ {
+ ID3D10Blob* disasm = NULL;
+ HRESULT hr = s_Compiler.disassembleFunc (dxbc, outSize, D3D_DISASM_ENABLE_DEFAULT_VALUE_PRINTS, NULL, &disasm);
+ if (SUCCEEDED(hr) && disasm)
+ {
+ printf_console ("disasm dxbc:\n%s\n\n", disasm->GetBufferPointer());
+ }
+ SAFE_RELEASE(disasm);
+ }
+ #endif
+
+ return dxbc;
+}
+
+
+// --- VERTEX program ----------------------------------------------------------------------------
+
+
+static void EmitMatrixMul(DXBCBuilderStream& bld, int cbIndex, char srcType, int srcIndex, char dstType, int dstIndex, int tmpIndex, bool wAlways1)
+{
+ bld.op(kSM4Op_MUL).reg('r',tmpIndex).swz(srcType,srcIndex,kSM4SwzRepY).swz('c',cbIndex+1);
+ bld.op(kSM4Op_MAD).reg('r',tmpIndex).swz('c',cbIndex+0).swz(srcType,srcIndex,kSM4SwzRepX).swz('r',tmpIndex);
+ bld.op(kSM4Op_MAD).reg('r',tmpIndex).swz('c',cbIndex+2).swz(srcType,srcIndex,kSM4SwzRepZ).swz('r',tmpIndex);
+ if (!wAlways1)
+ bld.op(kSM4Op_MAD).reg(dstType,dstIndex).swz('c',cbIndex+3).swz(srcType,srcIndex,kSM4SwzRepW).swz('r',tmpIndex);
+ else
+ bld.op(kSM4Op_ADD).reg(dstType,dstIndex).swz('c',cbIndex+3).swz('r',tmpIndex);
+}
+
+
+void* BuildVertexShaderD3D11 (const FixedFunctionStateD3D11& state, FixedFunctionProgramD3D11::ValueParameters& params, BuiltinShaderParamIndices& matrices, size_t& outSize)
+{
+ ShaderLab::FastPropertyName cbName; cbName.SetName(kD3D11VertexCB);
+ GetD3D11ConstantBuffers(GetRealGfxDevice()).SetCBInfo (cbName.index, k11VertexSize*16);
+ params.m_CBID = cbName.index; params.m_CBSize = k11VertexSize*16;
+
+ DXBCBuilder* builder = dxb_create(4, 0, kSM4Shader_Vertex);
+ DXBCBuilderStream bld(builder);
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ std::string helpers, inputs, outputs, code;
+ #endif
+
+ bool hasLights = (state.lightingEnabled && state.lightCount > 0);
+
+ bool eyePositionRequired =
+ hasLights ||
+ (state.fogMode != kFogDisabled);
+ bool eyeNormalRequired = hasLights;
+ bool viewDirRequired = hasLights && state.specularEnabled;
+ bool eyeReflRequired = false;
+ {
+ UInt64 texSources = state.texUnitSources;
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ UInt32 uvSource = texSources & 0xF;
+ if (uvSource == kTexSourceEyeLinear)
+ eyePositionRequired = true;
+ if (uvSource == kTexSourceCubeNormal)
+ eyeNormalRequired = true;
+ if (uvSource == kTexSourceCubeReflect || uvSource == kTexSourceSphereMap)
+ eyeReflRequired = viewDirRequired = eyePositionRequired = eyeNormalRequired = true;
+ texSources >>= 4;
+ }
+ }
+ if (eyePositionRequired || eyeNormalRequired || eyeReflRequired)
+ {
+ matrices.mat[kShaderInstanceMatMV].gpuIndex = k11VertexMV*16;
+ matrices.mat[kShaderInstanceMatMV].rows = 4;
+ matrices.mat[kShaderInstanceMatMV].cols = 4;
+ matrices.mat[kShaderInstanceMatMV].cbID = params.m_CBID;
+ }
+
+ dxb_dcl_cb(builder, 0, k11VertexSize);
+
+ int inputRegCounter = 0, outputRegCounter = 0, tempRegCounter = 0;
+ int inPosReg = 0, inColorReg = 0, inNormalReg = 0;
+ int inUVReg[8] = {0};
+ int outColor0Reg = 0, outColor1Reg = 0, outPosReg = 0;
+ int outUVReg[8] = {0};
+ int eyePosReg = 0, eyeNormalReg = 0, viewDirReg = 0, eyeReflReg = 0, lcolorReg = 0, specColorReg = 0;
+
+ dxb_dcl_input(builder, "POSITION", 0, inPosReg = inputRegCounter++);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ AddToStringList (inputs, "float4 vertex : POSITION");
+ #endif
+
+ // color = Vertex or uniform color
+ char inColorType;
+ if (state.useUniformInsteadOfVertexColor)
+ {
+ params.AddVectorParam (k11VertexColor*16, 4, kShaderVecFFColor);
+ inColorType = 'c';
+ inColorReg = k11VertexColor;
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "float4 color = ff_vec_color;\n";
+ #endif
+ }
+ else
+ {
+ inColorType = 'v';
+ dxb_dcl_input(builder, "COLOR", 0, inColorReg = inputRegCounter++);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ AddToStringList (inputs, "float4 vertexColor : COLOR");
+ code += "float4 color = vertexColor;\n";
+ #endif
+ }
+
+ // eyePos = eye position
+ if (eyePositionRequired)
+ {
+ eyePosReg = tempRegCounter++;
+ EmitMatrixMul (bld, k11VertexMV, 'v',inPosReg, 'r',eyePosReg, eyePosReg, false);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "float3 eyePos = mul (ff_matrix_mv, vertex).xyz;\n";
+ #endif
+ }
+
+ // eyeNormal = normalize(normalMatrix * normal)
+ if (eyeNormalRequired)
+ {
+ dxb_dcl_input(builder, "NORMAL", 0, inNormalReg = inputRegCounter++, 0x7);
+ eyeNormalReg = tempRegCounter++;
+ // mul
+ bld.op(kSM4Op_MUL).reg('r',eyeNormalReg,7).swz('v',inNormalReg,kSM4SwzRepY).swz('c',k11VertexMV+1);
+ bld.op(kSM4Op_MAD).reg('r',eyeNormalReg,7).swz('c',k11VertexMV+0).swz('v',inNormalReg,kSM4SwzRepX).swz('r',eyeNormalReg);
+ bld.op(kSM4Op_MAD).reg('r',eyeNormalReg,7).swz('c',k11VertexMV+2).swz('v',inNormalReg,kSM4SwzRepZ).swz('r',eyeNormalReg);
+ // normalize
+ bld.op(kSM4Op_DP3).reg('r',eyeNormalReg,8).swz('r',eyeNormalReg,kSM4SwzNone).swz('r',eyeNormalReg,kSM4SwzNone);
+ bld.op(kSM4Op_RSQ).reg('r',eyeNormalReg,8).swz('r',eyeNormalReg,kSM4SwzRepW);
+ bld.op(kSM4Op_MUL).reg('r',eyeNormalReg,7).swz('r',eyeNormalReg,kSM4SwzRepW).swz('r',eyeNormalReg,kSM4SwzXYZX);
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ AddToStringList (inputs, "float3 normal : NORMAL");
+ code += "float3 eyeNormal = normalize (mul ((float3x3)ff_matrix_mv, normal).xyz);\n"; //@TODO: proper normal matrix
+ #endif
+ }
+
+ // view dir
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "float3 viewDir = 0.0;";
+ #endif
+ if (viewDirRequired)
+ {
+ // viewDir = normalized vertex-to-eye
+ viewDirReg = tempRegCounter++;
+ // -normalize
+ bld.op(kSM4Op_DP3).reg('r',viewDirReg,8).swz('r',eyePosReg,kSM4SwzNone).swz('r',eyePosReg,kSM4SwzNone);
+ bld.op(kSM4Op_RSQ).reg('r',viewDirReg,8).swz('r',viewDirReg,kSM4SwzRepW);
+ bld.op(kSM4Op_MUL).reg('r',viewDirReg,7).swz('r',viewDirReg,kSM4SwzRepW).swz('r',eyePosReg,kSM4SwzXYZX,true);
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "viewDir = -normalize (eyePos);\n";
+ #endif
+ }
+
+ // eyeRefl
+ if (eyeReflRequired)
+ {
+ DebugAssert (viewDirRequired);
+ // eyeRefl = reflection vector, 2*dot(V,N)*N-V
+ eyeReflReg = tempRegCounter++;
+ bld.op(kSM4Op_DP3).reg('r',eyeReflReg,8).swz('r',viewDirReg,kSM4SwzNone).swz('r',eyeNormalReg,kSM4SwzNone);
+ bld.op(kSM4Op_ADD).reg('r',eyeReflReg,8).swz('r',eyeReflReg,kSM4SwzRepW).swz('r',eyeReflReg,kSM4SwzRepW);
+ bld.op(kSM4Op_MAD).reg('r',eyeReflReg,7).swz('r',eyeReflReg,kSM4SwzRepW).swz('r',eyeNormalReg,kSM4SwzXYZX).swz('r',viewDirReg,kSM4SwzXYZX,true);
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "float3 eyeRefl = 2.0f * dot (viewDir, eyeNormal) * eyeNormal - viewDir;\n";
+ #endif
+ }
+
+ // Lighting
+ if (state.lightingEnabled)
+ {
+ char ambientType, diffuseType, emissionType;
+ int ambientReg, diffuseReg, emissionReg;
+ if (state.colorMaterial==kColorMatAmbientAndDiffuse)
+ {
+ ambientType = diffuseType = inColorType;
+ ambientReg = diffuseReg = inColorReg;
+ }
+ else
+ {
+ ambientType = diffuseType = 'c';
+ ambientReg = k11VertexMatAmbient;
+ diffuseReg = k11VertexMatDiffuse;
+ }
+ if (state.colorMaterial==kColorMatEmission)
+ {
+ emissionType = inColorType;
+ emissionReg = inColorReg;
+ }
+ else
+ {
+ emissionType = 'c';
+ emissionReg = k11VertexMatEmission;
+ }
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ std::string ambientColor = (state.colorMaterial==kColorMatAmbientAndDiffuse) ? "color" : "ff_mat_ambient";
+ std::string diffuseColor = (state.colorMaterial==kColorMatAmbientAndDiffuse) ? "color" : "ff_mat_diffuse";
+ std::string emissionColor = (state.colorMaterial==kColorMatEmission) ? "color" : "ff_mat_emission";
+ #endif
+
+ params.AddVectorParam (k11VertexAmbient*16, 4, kShaderVecLightModelAmbient);
+ params.AddVectorParam (k11VertexMatAmbient*16, 4, kShaderVecFFMatAmbient);
+ params.AddVectorParam (k11VertexMatDiffuse*16, 4, kShaderVecFFMatDiffuse);
+ params.AddVectorParam (k11VertexMatSpec*16, 4, kShaderVecFFMatSpecular);
+ params.AddVectorParam (k11VertexMatEmission*16, 4, kShaderVecFFMatEmission);
+
+ lcolorReg = tempRegCounter++;
+ bld.op(kSM4Op_MAD).reg('r',lcolorReg,7).swz(ambientType,ambientReg,kSM4SwzXYZX).swz('c',k11VertexAmbient,kSM4SwzXYZX).swz(emissionType,emissionReg,kSM4SwzXYZX);
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "float3 lcolor = " + emissionColor + ".rgb + " + ambientColor + ".rgb * ff_vec_ambient.rgb;\n";
+ code += "float3 specColor = 0.0;\n";
+ if (state.lightCount > 0)
+ {
+ helpers += "float3 computeLighting (int idx, float3 dirToLight, float3 eyeNormal, float3 viewDir, float4 diffuseColor, float atten, inout float3 specColor) {\n";
+ helpers += " float NdotL = max(dot(eyeNormal, dirToLight), 0.0);\n";
+ helpers += " float3 color = NdotL * diffuseColor.rgb * ff_light_color[idx].rgb;\n";
+ if (state.specularEnabled)
+ {
+ helpers += " if (NdotL > 0.0) {\n";
+ helpers += " float3 h = normalize(dirToLight + viewDir);\n";
+ helpers += " float HdotN = max(dot(eyeNormal, h), 0.0);\n";
+ helpers += " float sp = saturate(pow(HdotN, ff_mat_spec.w));\n";
+ helpers += " specColor += atten * sp * ff_light_color[idx].rgb;\n";
+ helpers += " }\n";
+ }
+ helpers += " return color * atten;\n";
+ helpers += "}\n";
+
+ helpers += "float3 computeSpotLight(int idx, float3 eyePosition, float3 eyeNormal, float3 viewDir, float4 diffuseColor, inout float3 specColor) {\n";
+ helpers += " float3 dirToLight = ff_light_pos[idx].xyz - eyePosition * ff_light_pos[idx].w;\n";
+ helpers += " float distSqr = dot(dirToLight, dirToLight);\n";
+ helpers += " float att = 1.0 / (1.0 + ff_light_atten[idx].z * distSqr);\n";
+ helpers += " if (ff_light_pos[idx].w != 0 && distSqr > ff_light_atten[idx].w) att = 0.0;\n"; // set to 0 if outside of range
+ helpers += " dirToLight *= rsqrt(distSqr);\n";
+
+ helpers += " float rho = max(dot(dirToLight, ff_light_spot[idx].xyz), 0.0);\n";
+ helpers += " float spotAtt = (rho - ff_light_atten[idx].x) * ff_light_atten[idx].y;\n";
+ helpers += " spotAtt = saturate(spotAtt);\n";
+ helpers += " return min (computeLighting (idx, dirToLight, eyeNormal, viewDir, diffuseColor, att*spotAtt, specColor), 1.0);\n";
+ helpers += "}\n";
+ }
+ #endif // DEBUG_D3D11_COMPARE_WITH_HLSL
+
+ if (state.specularEnabled)
+ {
+ specColorReg = tempRegCounter++;
+ bld.op(kSM4Op_MOV).reg('r',specColorReg,7).float4(0,0,0,0);
+ }
+
+ for (int i = 0; i < state.lightCount; ++i)
+ {
+ params.AddVectorParam ((k11VertexLightPos+i)*16, 4, BuiltinShaderVectorParam(kShaderVecLight0Position+i));
+ params.AddVectorParam ((k11VertexLightAtten+i)*16, 4, BuiltinShaderVectorParam(kShaderVecLight0Atten+i));
+ params.AddVectorParam ((k11VertexLightColor+i)*16, 4, BuiltinShaderVectorParam(kShaderVecLight0Diffuse+i));
+ params.AddVectorParam ((k11VertexLightSpot+i)*16, 4, BuiltinShaderVectorParam(kShaderVecLight0SpotDirection+i));
+
+ Assert(eyePositionRequired);
+ Assert(eyeNormalRequired);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "lcolor += computeSpotLight(" + IntToString(i) + ", eyePos, eyeNormal, viewDir, " + diffuseColor + ", specColor);\n";
+ #endif
+
+ int ldirReg = tempRegCounter;
+ int miscReg = tempRegCounter+1;
+ int diffReg = tempRegCounter+2;
+
+ //
+ // attenuation
+
+ // float3 dirToLight = ff_light_pos[idx].xyz - eyePosition * ff_light_pos[idx].w;
+ // float distSqr = dot(dirToLight, dirToLight);
+ // float att = 1.0 / (1.0 + ff_light_atten[idx].z * distSqr);
+ // if (ff_light_pos[idx].w != 0 && distSqr > ff_light_atten[idx].w) att = 0.0;
+ // dirToLight *= rsqrt(distSqr);
+ // float rho = max(dot(dirToLight, ff_light_spot[idx].xyz), 0.0);
+ // float spotAtt = (rho - ff_light_atten[idx].x) * ff_light_atten[idx].y;
+ // spotAtt = saturate(spotAtt);
+
+ // dirToLight = ff_light_pos[idx].xyz - eyePosition * ff_light_pos[idx].w
+ bld.op(kSM4Op_MAD).reg('r',ldirReg,7).swz('r',eyePosReg,kSM4SwzXYZX,true).swz('c',k11VertexLightPos+i,kSM4SwzRepW).swz('c',k11VertexLightPos+i,kSM4SwzXYZX);
+ // normalize, distSqr in miscReg.w
+ bld.op(kSM4Op_DP3).reg('r',miscReg,8).swz('r',ldirReg,kSM4SwzNone).swz('r',ldirReg,kSM4SwzNone);
+ bld.op(kSM4Op_RSQ).reg('r',ldirReg,8).swz('r',miscReg,kSM4SwzRepW);
+ bld.op(kSM4Op_MUL).reg('r',ldirReg,7).swz('r',ldirReg,kSM4SwzRepW).swz('r',ldirReg,kSM4SwzXYZX);
+
+ // miscReg.z = float rho = max(dot(dirToLight, ff_light_spot[idx].xyz), 0.0)
+ bld.op(kSM4Op_DP3).reg('r',miscReg,4).swz('r',ldirReg,kSM4SwzNone).swz('c',k11VertexLightSpot+i,kSM4SwzNone);
+ bld.op(kSM4Op_MAX).reg('r',miscReg,4).swz('r',miscReg,kSM4SwzRepZ).float1(0.0f);
+ // miscReg.z = spotAtt = saturate ( (rho - ff_light_atten[idx].x) * ff_light_atten[idx].y )
+ bld.op(kSM4Op_ADD).reg('r',miscReg,4).swz('r',miscReg,kSM4SwzRepZ).swz('c',k11VertexLightAtten+i,kSM4SwzRepX,true);
+ bld.op_sat(kSM4Op_MUL,miscReg).reg('r',miscReg,4).swz('r',miscReg,kSM4SwzRepZ).swz('c',k11VertexLightAtten+i,kSM4SwzRepY);
+
+ // miscReg.y = float att = 1.0 / (1.0 + ff_light_atten[idx].z * distSqr)
+ bld.op(kSM4Op_MAD).reg('r',miscReg,2).swz('c',k11VertexLightAtten+i,kSM4SwzRepZ).swz('r',miscReg,kSM4SwzRepW).float1(1.0f);
+ bld.noAutoSM2();
+ bld.op(kSM4Op_DIV).reg('r',miscReg,2).float4(1,1,1,1).swz('r',miscReg,kSM4SwzRepY);
+ bld.op2(kSM2Op_RCP).reg2('r',miscReg,2).swz2('r',miscReg,kSM4SwzRepY);
+ bld.autoSM2();
+
+ // miscReg.y = att * spotAtt
+ bld.op(kSM4Op_MUL).reg('r',miscReg,2).swz('r',miscReg,kSM4SwzRepY).swz('r',miscReg,kSM4SwzRepZ);
+ // if (ff_light_pos[idx].w != 0 && distSqr > ff_light_atten[idx].w) att = 0.0
+ bld.noAutoSM2();
+ bld.op(kSM4Op_LT).reg('r',miscReg,1).swz('c',k11VertexLightAtten+i,kSM4SwzRepW).swz('r',miscReg,kSM4SwzRepW);
+ bld.op(kSM4Op_NE).reg('r',miscReg,4).swz('c',k11VertexLightPos+i,kSM4SwzRepW).float1(0.0);
+ bld.op(kSM4Op_AND).reg('r',miscReg,1).swz('r',miscReg,kSM4SwzRepX).swz('r',miscReg,kSM4SwzRepZ);
+ bld.op(kSM4Op_MOVC).reg('r',miscReg,2).swz('r',miscReg,kSM4SwzRepX).float1(0.0).swz('r',miscReg,kSM4SwzRepY);
+ //SM2
+ bld.op2(kSM2Op_SLT).reg2('r',miscReg,1).swz2('c',k11VertexLightAtten+i,kSM4SwzRepW).swz2('r',miscReg,kSM4SwzRepW);
+ bld.op2(kSM2Op_MUL).reg2('r',miscReg,4).swz2('c',k11VertexLightPos+i,kSM4SwzRepW).swz2('c',k11VertexLightPos+i,kSM4SwzRepW);
+ bld.op2(kSM2Op_SLT).reg2('r',miscReg,4).swz2('r',miscReg,kSM4SwzRepZ,true).swz2('r',miscReg,kSM4SwzRepZ);
+ bld.op2(kSM2Op_MUL).reg2('r',miscReg,1).swz2('r',miscReg,kSM4SwzRepX).swz2('r',miscReg,kSM4SwzRepZ);
+ bld.op2(kSM2Op_MAD).reg2('r',miscReg,2).swz2('r',miscReg,kSM4SwzRepX).swz2('r',miscReg,kSM4SwzRepY,true).swz2('r',miscReg,kSM4SwzRepY);
+ bld.autoSM2();
+
+ //
+ // diffuse
+
+ // float NdotL = max(dot(eyeNormal, dirToLight), 0.0);
+ // float3 color = NdotL * diffuseColor.rgb * ff_light_color[idx].rgb;
+ // lcolor += color * atten
+
+ // miscReg.z = float NdotL = max(dot(eyeNormal, dirToLight), 0.0)
+ bld.op(kSM4Op_DP3).reg('r',miscReg,4).swz('r',eyeNormalReg,kSM4SwzNone).swz('r',ldirReg,kSM4SwzNone);
+ bld.op(kSM4Op_MAX).reg('r',miscReg,4).swz('r',miscReg,kSM4SwzRepZ).float1(0.0f);
+ // diffReg.xyz = float3 color = NdotL * diffuseColor.rgb * ff_light_color[idx].rgb
+ bld.op(kSM4Op_MUL).reg('r',diffReg,7).swz('r',miscReg,kSM4SwzRepZ).swz(diffuseType,diffuseReg,kSM4SwzXYZX);
+ bld.op(kSM4Op_MUL).reg('r',diffReg,7).swz('r',diffReg,kSM4SwzXYZX).swz('c',k11VertexLightColor+i,kSM4SwzXYZX);
+ // diffReg.xyz = saturate(color*atten, 1)
+ bld.op_sat(kSM4Op_MUL,diffReg).reg('r',diffReg,7).swz('r',diffReg,kSM4SwzXYZX).swz('r',miscReg,kSM4SwzRepY);
+ // lcolor += diffReg
+ bld.op(kSM4Op_ADD).reg('r',lcolorReg,7).swz('r',lcolorReg,kSM4SwzXYZX).swz('r',diffReg,kSM4SwzXYZX);
+
+ //
+ // specular
+
+ if (state.specularEnabled)
+ {
+ // if (NdotL > 0.0) {
+ // float3 h = normalize(dirToLight + viewDir);
+ // float HdotN = max(dot(eyeNormal, h), 0.0);
+ // float sp = saturate(pow(HdotN, ff_mat_spec.w));
+ // specColor += atten * sp * ff_light_color[idx].rgb;
+ // }
+
+ // ldirReg.xyz = h = normalize(dirToLight + viewDir)
+ bld.op(kSM4Op_ADD).reg('r',ldirReg,7).swz('r',ldirReg,kSM4SwzXYZX).swz('r',viewDirReg,kSM4SwzXYZX);
+ bld.op(kSM4Op_DP3).reg('r',ldirReg,8).swz('r',ldirReg,kSM4SwzNone).swz('r',ldirReg,kSM4SwzNone);
+ bld.op(kSM4Op_RSQ).reg('r',ldirReg,8).swz('r',ldirReg,kSM4SwzRepW);
+ bld.op(kSM4Op_MUL).reg('r',ldirReg,7).swz('r',ldirReg,kSM4SwzXYZX).swz('r',ldirReg,kSM4SwzRepW);
+ // ldirReg.w = HdotN = max(dot(eyeNormal,h),0)
+ bld.op(kSM4Op_DP3).reg('r',ldirReg,8).swz('r',ldirReg,kSM4SwzNone).swz('r',eyeNormalReg,kSM4SwzNone);
+ bld.op(kSM4Op_MAX).reg('r',ldirReg,8).swz('r',ldirReg,kSM4SwzRepW).float1(0.0f);
+ // float sp = saturate(pow(HdotN, ff_mat_spec.w))
+ bld.op(kSM4Op_LOG).reg('r',ldirReg,8).swz('r',ldirReg,kSM4SwzRepW);
+ bld.op(kSM4Op_MUL).reg('r',ldirReg,8).swz('r',ldirReg,kSM4SwzRepW).swz('c',k11VertexMatSpec,kSM4SwzRepW);
+ bld.op(kSM4Op_EXP).reg('r',ldirReg,8).swz('r',ldirReg,kSM4SwzRepW);
+ bld.op(kSM4Op_MIN).reg('r',ldirReg,8).swz('r',ldirReg,kSM4SwzRepW).float1(1.0f);
+ // atten * sp * ff_light_color[idx].rgb
+ bld.op(kSM4Op_MUL).reg('r',ldirReg,8).swz('r',ldirReg,kSM4SwzRepW).swz('r',miscReg,kSM4SwzRepY);
+ bld.op(kSM4Op_MUL).reg('r',diffReg,7).swz('r',ldirReg,kSM4SwzRepW).swz('c',k11VertexLightColor+i,kSM4SwzXYZX);
+ // nuke specular if NdotL <= 0
+ bld.op(kSM4Op_LT).reg('r',miscReg,1).float1(0.0).swz('r',miscReg,kSM4SwzRepZ);
+ bld.noAutoSM2();
+ bld.op(kSM4Op_AND).reg('r',diffReg,7).swz('r',diffReg,kSM4SwzXYZX).swz('r',miscReg,kSM4SwzRepX);
+ bld.op2(kSM2Op_MUL).reg2('r',diffReg,7).swz2('r',diffReg,kSM4SwzXYZX).swz2('r',miscReg,kSM4SwzRepX);
+ bld.autoSM2();
+ // specColor += computed spec color
+ bld.op(kSM4Op_ADD).reg('r',specColorReg,7).swz('r',specColorReg,kSM4SwzXYZX).swz('r',diffReg,kSM4SwzXYZX);
+ }
+ }
+
+ bld.op(kSM4Op_MOV).reg('r',lcolorReg,8).swz(diffuseType,diffuseReg,kSM4SwzRepW);
+ inColorReg = lcolorReg;
+ inColorType = 'r';
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "color.rgb = lcolor.rgb;\n";
+ code += "color.a = " + diffuseColor + ".a;\n";
+ #endif
+
+ if (state.specularEnabled)
+ {
+ bld.op(kSM4Op_MUL).reg('r',specColorReg,7).swz('r',specColorReg,kSM4SwzXYZX).swz('c',k11VertexMatSpec,kSM4SwzXYZX);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "specColor *= ff_mat_spec.rgb;\n";
+ #endif
+ }
+ }
+
+ // Output final color
+ dxb_dcl_output(builder, "COLOR", 0, outColor0Reg = outputRegCounter++);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ AddToStringList (outputs, "out float4 ocolor : COLOR0");
+ #endif
+
+ bld.op_sat(kSM4Op_MOV,tempRegCounter).reg('o',outColor0Reg).swz(inColorType,inColorReg);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "ocolor = saturate(color);\n";
+ #endif
+
+ if (state.lightingEnabled && state.specularEnabled)
+ {
+ dxb_dcl_output(builder, "COLOR", 1, outColor1Reg = outputRegCounter++, 0x7);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ AddToStringList (outputs, "out float3 ospec : COLOR1");
+ #endif
+
+ bld.op_sat(kSM4Op_MOV,tempRegCounter).reg('o',outColor1Reg,7).swz('r',specColorReg,kSM4SwzXYZX);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "ospec = saturate(specColor);\n";
+ #endif
+ }
+
+ // we don't need temporary registers from lighting calculations anymore after this point
+ if (state.lightingEnabled)
+ --tempRegCounter;
+
+
+ // Pass & transform texture coordinates
+ UInt32 gotInputs = 0;
+ UInt32 gotOutputs = 0;
+ UInt64 texSources = state.texUnitSources;
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ matrices.mat[kShaderInstanceMatTexture0+i].gpuIndex = (k11VertexTex+i*4)*16;
+ matrices.mat[kShaderInstanceMatTexture0+i].rows = 4;
+ matrices.mat[kShaderInstanceMatTexture0+i].cols = 4;
+ matrices.mat[kShaderInstanceMatTexture0+i].cbID = params.m_CBID;
+
+ std::string iname = IntToString(i);
+ dxb_dcl_output(builder, "TEXCOORD", i, outUVReg[i] = outputRegCounter++);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ AddToStringList (outputs, ("out float4 ouv" + iname + " : TEXCOORD" + iname).c_str());
+ #endif
+
+ UInt32 uvSource = texSources & 0xF;
+ if (uvSource >= kTexSourceUV0 && uvSource <= kTexSourceUV7)
+ {
+ unsigned uv = uvSource-kTexSourceUV0;
+ std::string uvStr = IntToString(uv);
+ if (!(gotInputs & (1<<uv)))
+ {
+ dxb_dcl_input(builder, "TEXCOORD", uv, inUVReg[uv] = inputRegCounter++);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ AddToStringList (inputs, ("float4 uv"+uvStr+" : TEXCOORD"+uvStr).c_str());
+ #endif
+ gotInputs |= (1<<uv);
+ }
+ EmitMatrixMul (bld, k11VertexTex+4*i, 'v',inUVReg[uv], 'o',outUVReg[i], tempRegCounter, false);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "ouv"+iname + " = mul(ff_matrix_tex["+iname + "], uv"+uvStr+");\n";
+ #endif
+ }
+ else if (uvSource == kTexSourceSphereMap)
+ {
+ // m = 2*sqrt(Rx*Rx + Ry*Ry + (Rz+1)*(Rz+1))
+ // SPHR = Rx/m + 0.5, Ry/m + 0.5
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "ouv"+iname + " = mul(ff_matrix_tex["+iname +"], float4(\n";
+ code += " eyeRefl.xy / (2.0*sqrt(eyeRefl.x*eyeRefl.x + eyeRefl.y*eyeRefl.y + (eyeRefl.z+1)*(eyeRefl.z+1))) + 0.5,\n";
+ code += " 0,1));\n";
+ #endif
+
+ // HLSL generates code like:
+ // dp2 r0.w, r0.xyxx, r0.xyxx
+ // add r0.z, r0.z, l(1.0)
+ // mad r0.z, r0.z, r0.z, r0.w
+ // sqrt r0.z, r0.z
+ // add r0.z, r0.z, r0.z
+ // div r0.xy, r0.xyxx, r0.zzzz
+ // add r0.xy, r0.xyxx, l(0.5, 0.5, 0.0, 0.0)
+#if 0
+ bld.op(kSM4Op_DP2).reg('r',tempRegCounter,8).swz('r',eyeReflReg,kSM4SwzXYXX).swz('r',eyeReflReg,kSM4SwzXYXX);
+ bld.op(kSM4Op_ADD).reg('r',tempRegCounter,4).swz('r',eyeReflReg,kSM4SwzRepZ).float1(1.0);
+ bld.op(kSM4Op_MAD).reg('r',tempRegCounter,4).swz('r',tempRegCounter,kSM4SwzRepZ).swz('r',tempRegCounter,kSM4SwzRepZ).swz('r',tempRegCounter,kSM4SwzRepW);
+ bld.op(kSM4Op_SQRT).reg('r',tempRegCounter,4).swz('r',tempRegCounter,kSM4SwzRepZ);
+ bld.op(kSM4Op_ADD).reg('r',tempRegCounter,4).swz('r',tempRegCounter,kSM4SwzRepZ).swz('r',tempRegCounter,kSM4SwzRepZ);
+ bld.op(kSM4Op_DIV).reg('r',tempRegCounter,3).swz('r',eyeReflReg,kSM4SwzXYXX).swz('r',tempRegCounter,kSM4SwzRepZ);
+ bld.op(kSM4Op_ADD).reg('r',tempRegCounter,3).swz('r',tempRegCounter,kSM4SwzXYXX).float4(0.5f,0.5f,0,0);
+#else
+ //SM2 compatible
+ bld.op(kSM4Op_ADD).reg('r',tempRegCounter,7).swz('r',eyeReflReg,kSM4SwzXYZX).float4(0.0f,0.0f,1.0f,0.0f);
+ bld.op(kSM4Op_DP3).reg('r',tempRegCounter,8).swz('r',tempRegCounter,kSM4SwzNone).swz('r',tempRegCounter,kSM4SwzNone);
+ bld.op(kSM4Op_RSQ).reg('r',tempRegCounter,8).swz('r',tempRegCounter,kSM4SwzRepW);
+ bld.op(kSM4Op_MUL).reg('r',tempRegCounter,8).swz('r',tempRegCounter,kSM4SwzRepW).float4(0.5f,0.5f,0.5f,0.5f);
+ bld.op(kSM4Op_MAD).reg('r',tempRegCounter,3).swz('r',eyeReflReg,kSM4SwzXYXX).swz('r',tempRegCounter,kSM4SwzRepW).float4(0.5f,0.5f,0.5f,0.5f);
+#endif
+ EmitMatrixMul (bld, k11VertexTex+4*i, 'r',tempRegCounter, 'o',outUVReg[i], tempRegCounter+1, true);
+ }
+ else if (uvSource == kTexSourceObject)
+ {
+ EmitMatrixMul (bld, k11VertexTex+4*i, 'v',inPosReg, 'o',outUVReg[i], tempRegCounter, false);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "ouv"+iname + " = mul(ff_matrix_tex["+iname +"], vertex);\n";
+ #endif
+ }
+ else if (uvSource == kTexSourceEyeLinear)
+ {
+ EmitMatrixMul (bld, k11VertexTex+4*i, 'r',eyePosReg, 'o',outUVReg[i], tempRegCounter, true);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "ouv"+iname + " = mul(ff_matrix_tex["+iname +"], float4(eyePos,1.0));\n";
+ #endif
+ }
+ else if (uvSource == kTexSourceCubeNormal)
+ {
+ EmitMatrixMul (bld, k11VertexTex+4*i, 'r',eyeNormalReg, 'o',outUVReg[i], tempRegCounter, true);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "ouv"+iname + " = mul(ff_matrix_tex["+iname +"], float4(eyeNormal,1.0));\n";
+ #endif
+ }
+ else if (uvSource == kTexSourceCubeReflect)
+ {
+ EmitMatrixMul (bld, k11VertexTex+4*i, 'r',eyeReflReg, 'o',outUVReg[i], tempRegCounter, true);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "ouv"+iname + " = mul(ff_matrix_tex["+iname +"], float4(eyeRefl,1.0));\n";
+ #endif
+ }
+ else
+ {
+ AssertString("Unknown texgen mode");
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "ouv"+iname + " = 0.5;\n";
+ #endif
+ }
+ texSources >>= 4;
+ }
+
+ // fog if we have a spare varying
+ if (state.fogMode != kFogDisabled && outputRegCounter < 8)
+ {
+ Assert(eyePositionRequired);
+ int outFogReg;
+ dxb_dcl_output(builder, "FOG", 0, outFogReg = outputRegCounter++, 0x1);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ AddToStringList (outputs, "out float ofog : FOG0");
+ #endif
+
+ params.AddVectorParam (k11VertexFog*16, 4, kShaderVecFFFogParams);
+
+ int fogReg = tempRegCounter++;
+
+ // fogCoord = length(eyePosition.xyz), for radial fog
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "float fogCoord = length(eyePos.xyz);\n";
+ #endif
+
+ bld.op(kSM4Op_DP3).reg('r',fogReg,1).swz('r',eyePosReg,kSM4SwzNone).swz('r',eyePosReg,kSM4SwzNone);
+#if 0
+ bld.op(kSM4Op_SQRT).reg('r',fogReg,1).swz('r',fogReg,kSM4SwzRepX);
+#else
+ //SM2 compatible
+ bld.op(kSM4Op_RSQ).reg('r',fogReg,1).swz('r',fogReg,kSM4SwzRepX);
+ bld.op(kSM4Op_RCP).reg('r',fogReg,1).swz('r',fogReg,kSM4SwzRepX);
+#endif
+ if (state.fogMode == kFogLinear)
+ {
+ // fogParams.z * fogCoord + fogParams.w
+ bld.op_sat(kSM4Op_MAD,tempRegCounter).reg('o',outFogReg,1).swz('r',fogReg,kSM4SwzRepX).swz('c',k11VertexFog,kSM4SwzRepZ).swz('c',k11VertexFog,kSM4SwzRepW);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "ofog = saturate(fogCoord * ff_fog.z + ff_fog.w);\n";
+ #endif
+ }
+ else if (state.fogMode == kFogExp)
+ {
+ // fogArg = fogParams.y * fogCoord
+ // exp2(-fogArg)
+ bld.op(kSM4Op_MUL).reg('r',fogReg,1).swz('r',fogReg,kSM4SwzRepX).swz('c',k11VertexFog,kSM4SwzRepY);
+ bld.op_sat(kSM4Op_EXP,tempRegCounter).reg('o',outFogReg,1).swz('r',fogReg,kSM4SwzRepX,true);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "ofog = saturate(exp2(-(fogCoord * ff_fog.y)));\n";
+ #endif
+ }
+ else if (state.fogMode == kFogExp2)
+ {
+ // fogArg = fogParams.y * fogCoord
+ // exp2(-fogArg*fogArg)
+ bld.op(kSM4Op_MUL).reg('r',fogReg,1).swz('r',fogReg,kSM4SwzRepX).swz('c',k11VertexFog,kSM4SwzRepY);
+ bld.op(kSM4Op_MUL).reg('r',fogReg,1).swz('r',fogReg,kSM4SwzRepX).swz('r',fogReg,kSM4SwzRepX);
+ bld.op_sat(kSM4Op_EXP,tempRegCounter).reg('o',outFogReg,1).swz('r',fogReg,kSM4SwzRepX,true);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "fogCoord = fogCoord * ff_fog.y;\n";
+ code += "ofog = saturate(exp2(-fogCoord * fogCoord));\n";
+ #endif
+ }
+ --tempRegCounter;
+ }
+
+ dxb_dcl_output(builder, "SV_POSITION", 0, outPosReg = outputRegCounter++);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ AddToStringList (outputs, "out float4 overtex : SV_POSITION");
+ #endif
+
+ // Vertex transformation
+ matrices.mat[kShaderInstanceMatMVP].gpuIndex = k11VertexMVP*16;
+ matrices.mat[kShaderInstanceMatMVP].rows = 4;
+ matrices.mat[kShaderInstanceMatMVP].cols = 4;
+ matrices.mat[kShaderInstanceMatMVP].cbID = params.m_CBID;
+ bld.op(kSM4Op_MUL).reg('r',0).swz('v',inPosReg,kSM4SwzRepY).swz('c',k11VertexMVP+1);
+ bld.op(kSM4Op_MAD).reg('r',0).swz('c',k11VertexMVP+0).swz('v',inPosReg,kSM4SwzRepX).swz('r',0);
+ bld.op(kSM4Op_MAD).reg('r',0).swz('c',k11VertexMVP+2).swz('v',inPosReg,kSM4SwzRepZ).swz('r',0);
+ bld.op(kSM4Op_MAD).reg('r',0).swz('c',k11VertexMVP+3).swz('v',inPosReg,kSM4SwzRepW).swz('r',0);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "overtex = mul (ff_matrix_mvp, vertex);\n";
+ #endif
+
+ //correct output pos with Vertex Shader position offset
+ //mad oPos.xy, v0.w, c63, v0
+ bld.op2(kSM2Op_MAD).reg2('o',outPosReg,3).swz2('r',0,kSM4SwzRepW).swz2('c',k11VertexPosOffset9x).swz2('r',0);
+ //mov oPos.zw, v0
+ bld.op2(kSM2Op_MOV).reg2('o',outPosReg,12).swz2('r',0);
+
+ //copy output pos for sm40
+ bld.noAutoSM2();
+ bld.op(kSM4Op_MOV).reg('o',outPosReg).swz('r',0);
+ bld.autoSM2();
+
+ bld.op(kSM4Op_RET);
+
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ std::string src =
+ kD3D11VertexPrefix +
+ helpers + '\n' +
+ "void main (\n " +
+ inputs + ",\n " +
+ outputs + ") {\n" +
+ code + "\n}";
+ printf_console ("d3d11 FF VS HLSL:\n%s\n", src.c_str());
+ DebugCompileHLSLShaderD3D11 (src, true);
+ #endif
+
+
+ void* blob = BuildShaderD3D11 (builder, outSize);
+ return blob;
+}
+
+
+// --- FRAGMENT program ----------------------------------------------------------------------------
+
+enum CombinerWriteMask { kCombWriteRGBA, kCombWriteRGB, kCombWriteA };
+
+static bool EmitCombinerMath11 (
+ int stage,
+ UInt32 combiner,
+ CombinerWriteMask writeMaskMode,
+ int texUnitCount,
+ DXBCBuilderStream& bld
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ , std::string& code
+ #endif
+)
+{
+ Assert (texUnitCount < 10 && stage < 10);
+
+ combiner::Source sources[3];
+ combiner::Operand operands[3];
+ combiner::Operation op;
+ int scale;
+ combiner::DecodeTextureCombinerDescriptor (combiner, op, sources, operands, scale, true);
+
+ // dot3 and dot3rgba write into RGBA; alpha combiner is always ignored
+ if (op == combiner::kOpDot3RGB || op == combiner::kOpDot3RGBA)
+ {
+ if (writeMaskMode == kCombWriteA)
+ return false;
+ writeMaskMode = kCombWriteRGBA;
+ }
+
+ unsigned tmpIdx = 1;
+
+ bool usedConstant = false;
+ char regFile[3];
+ unsigned regIdx[3];
+ unsigned regSrcAlphaSwz[3];
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ std::string reg[3];
+ #endif
+ for (int r = 0; r < 3; ++r)
+ {
+ combiner::Source source = sources[r];
+ regSrcAlphaSwz[r] = kSM4SwzRepW;
+ if (stage == 0 && source == combiner::kSrcPrevious)
+ source = combiner::kSrcPrimaryColor; // first stage, "previous" the same as "primary"
+ switch (source)
+ {
+ case combiner::kSrcPrimaryColor:
+ regFile[r] = 'v'; regIdx[r] = 0;
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ reg[r] = "icolor";
+ #endif
+ break;
+ case combiner::kSrcPrevious:
+ regFile[r] = 'r'; regIdx[r] = 0;
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ reg[r] = "col";
+ #endif
+ break;
+ case combiner::kSrcTexture:
+ regFile[r] = 'r'; regIdx[r] = 1; tmpIdx = 2;
+ regSrcAlphaSwz[r] = kSM4SwzRepW;
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ reg[r] = "tex";
+ #endif
+ break;
+ case combiner::kSrcConstant:
+ usedConstant |= true; regFile[r] = 'c'; regIdx[r] = stage;
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ reg[r] = std::string("ff_vec_colors[") + char('0'+stage) + ']';
+ #endif
+ break;
+ default:
+ AssertString("unknown source"); //reg[r] = "foo";
+ }
+ }
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ const char* writeMask = "";
+ #endif
+ unsigned writeMaskBin = 0xF; // rgba
+ if (writeMaskMode == kCombWriteRGB)
+ {
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ writeMask = ".rgb";
+ #endif
+ writeMaskBin = 0x7; // rgb
+ }
+ else if (writeMaskMode == kCombWriteA)
+ {
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ writeMask = ".a";
+ #endif
+ writeMaskBin = 0x8; // a
+ }
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ const char* regSwizzle[3];
+ #endif
+ unsigned regSwizzleBin[3];
+ for (int r = 0; r < 3; ++r)
+ {
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ regSwizzle[r] = "";
+ #endif
+ regSwizzleBin[r] = kSM4SwzNone;
+ // 1-x: into tmpN and use that
+ if (operands[r] == combiner::kOperOneMinusSrcColor || operands[r] == combiner::kOperOneMinusSrcAlpha)
+ {
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += std::string("tmp")+char('0'+r)+" = 1.0 - " + reg[r]+regSwizzle[r] + ";\n";
+ reg[r] = std::string("tmp")+char('0'+r);
+ #endif
+ bld.op(kSM4Op_ADD).reg('r', tmpIdx, writeMaskBin).swz(regFile[r], regIdx[r], regSwizzleBin[r], true).float1(1.0f);
+ regFile[r] = 'r';
+ regIdx[r] = tmpIdx;
+ ++tmpIdx;
+ }
+ // replicate alpha swizzle?
+ if (operands[r] == combiner::kOperSrcAlpha || operands[r] == combiner::kOperOneMinusSrcAlpha)
+ {
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ regSwizzle[r] = ".a";
+ #endif
+ regSwizzleBin[r] = kSM4SwzRepW;
+ }
+ }
+ switch (op)
+ {
+ case combiner::kOpReplace:
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += std::string("col")+writeMask + " = " + reg[0]+regSwizzle[0] + ";\n";
+ #endif
+ bld.op(kSM4Op_MOV).reg('r', 0, writeMaskBin).swz(regFile[0], regIdx[0], regSwizzleBin[0]);
+ break;
+ case combiner::kOpModulate:
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += std::string("col")+writeMask + " = " + reg[0]+regSwizzle[0] + " * " + reg[1]+regSwizzle[1] + ";\n";
+ #endif
+ bld.op(kSM4Op_MUL);
+ bld.reg('r', 0, writeMaskBin);
+ bld.swz(regFile[0], regIdx[0], regSwizzleBin[0]);
+ bld.swz(regFile[1], regIdx[1], regSwizzleBin[1]);
+ break;
+ case combiner::kOpAdd:
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += std::string("col")+writeMask + " = " + reg[0]+regSwizzle[0] + " + " + reg[1]+regSwizzle[1] + ";\n";
+ #endif
+ bld.op(kSM4Op_ADD);
+ bld.reg('r', 0, writeMaskBin);
+ bld.swz(regFile[0], regIdx[0], regSwizzleBin[0]);
+ bld.swz(regFile[1], regIdx[1], regSwizzleBin[1]);
+ break;
+ case combiner::kOpAddSigned:
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += std::string("col")+writeMask + " = " + reg[0]+regSwizzle[0] + " + " + reg[1]+regSwizzle[1] + " - 0.5;\n";
+ #endif
+ bld.op(kSM4Op_ADD);
+ bld.reg('r', 0, writeMaskBin);
+ bld.swz(regFile[0], regIdx[0], regSwizzleBin[0]);
+ bld.swz(regFile[1], regIdx[1], regSwizzleBin[1]);
+ bld.op(kSM4Op_ADD);
+ bld.reg('r', 0, writeMaskBin);
+ bld.swz('r', 0);
+ bld.float4(-.5f,-.5f,-.5f,-.5f);
+ break;
+ case combiner::kOpSubtract:
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += std::string("col")+writeMask + " = " + reg[0]+regSwizzle[0] + " - " + reg[1]+regSwizzle[1] + ";\n";
+ #endif
+ bld.op(kSM4Op_ADD);
+ bld.reg('r', 0, writeMaskBin);
+ bld.swz(regFile[0], regIdx[0], regSwizzleBin[0]);
+ bld.swz(regFile[1], regIdx[1], regSwizzleBin[1], true);
+ break;
+ case combiner::kOpLerp:
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += std::string("col")+writeMask + " = lerp (" + reg[1]+regSwizzle[1] + ", " + reg[0]+regSwizzle[0] + ", " + reg[2]+ ".a);\n";
+ #endif
+ // tmp = r0-r1
+ // res = tmp * r2 + r1
+ bld.op(kSM4Op_ADD);
+ bld.reg('r', tmpIdx, writeMaskBin);
+ bld.swz(regFile[0], regIdx[0], regSwizzleBin[0]);
+ bld.swz(regFile[1], regIdx[1], regSwizzleBin[1], true);
+ bld.op(kSM4Op_MAD);
+ bld.reg('r', 0, writeMaskBin);
+ bld.swz('r', tmpIdx);
+ bld.swz(regFile[2], regIdx[2], regSrcAlphaSwz[2]);
+ bld.swz(regFile[1], regIdx[1], regSwizzleBin[1]);
+ ++tmpIdx;
+ break;
+ case combiner::kOpDot3RGB:
+ DebugAssert(writeMaskMode == kCombWriteRGBA);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += std::string("col.rgb = 4.0 * dot ((") + reg[0]+regSwizzle[0] + ")-0.5, (" + reg[1]+regSwizzle[1] + ")-0.5);\n";
+ code += std::string("col.a = ") + reg[0]+".a;\n";
+ #endif
+
+ // tmp+0 = r0-0.5
+ bld.op(kSM4Op_ADD);
+ bld.reg('r', tmpIdx+0, writeMaskBin);
+ bld.swz(regFile[0], regIdx[0], regSwizzleBin[0]);
+ bld.float4(-.5f,-.5f,-.5f,-.5f);
+ // tmp+1 = r1-0.5
+ bld.op(kSM4Op_ADD);
+ bld.reg('r', tmpIdx+1, writeMaskBin);
+ bld.swz(regFile[1], regIdx[1], regSwizzleBin[1]);
+ bld.float4(-.5f,-.5f,-.5f,-.5f);
+ // tmp0.rgb = dp4(tmp+0, tmp+1)
+ bld.op(kSM4Op_DP3);
+ bld.reg('r', 0, 0x7);
+ bld.swz('r', tmpIdx+0);
+ bld.swz('r', tmpIdx+1);
+ // tmp0.rgb *= 4
+ bld.op(kSM4Op_MUL);
+ bld.reg('r', 0, 0x7);
+ bld.swz('r', 0);
+ bld.float4(4.0f,4.0f,4.0f,4.0f);
+ // tmp0.a = r0.a
+ bld.op(kSM4Op_MOV);
+ bld.reg('r', 0, 0x8);
+ bld.swz(regFile[0], regIdx[0], kSM4SwzRepW);
+ tmpIdx += 2;
+ break;
+ case combiner::kOpDot3RGBA:
+ DebugAssert(writeMaskMode == kCombWriteRGBA);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += std::string("col")+writeMask + " = 4.0 * dot ((" + reg[0]+regSwizzle[0] + ")-0.5, (" + reg[1]+regSwizzle[1] + ")-0.5);\n";
+ #endif
+ // tmp+0 = r0-0.5
+ bld.op(kSM4Op_ADD);
+ bld.reg('r', tmpIdx+0, writeMaskBin);
+ bld.swz(regFile[0], regIdx[0], regSwizzleBin[0]);
+ bld.float4(-.5f,-.5f,-.5f,-.5f);
+ // tmp+1 = r1-0.5
+ bld.op(kSM4Op_ADD);
+ bld.reg('r', tmpIdx+1, writeMaskBin);
+ bld.swz(regFile[1], regIdx[1], regSwizzleBin[1]);
+ bld.float4(-.5f,-.5f,-.5f,-.5f);
+ // tmp0 = dp4(tmp+0, tmp+1)
+ bld.op(kSM4Op_DP3);
+ bld.reg('r', 0, writeMaskBin);
+ bld.swz('r', tmpIdx+0);
+ bld.swz('r', tmpIdx+1);
+ // tmp0 *= 4
+ bld.op(kSM4Op_MUL);
+ bld.reg('r', 0, writeMaskBin);
+ bld.swz('r', 0);
+ bld.float4(4.0f,4.0f,4.0f,4.0f);
+ tmpIdx += 2;
+ break;
+ case combiner::kOpMulAdd:
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += std::string("col")+writeMask + " = " + reg[0]+regSwizzle[0] + " * " + reg[2]+".a + " + reg[1]+regSwizzle[1] + ";\n";
+ #endif
+ bld.op(kSM4Op_MAD);
+ bld.reg('r', 0, writeMaskBin);
+ bld.swz(regFile[0], regIdx[0], regSwizzleBin[0]);
+ bld.swz(regFile[2], regIdx[2], regSrcAlphaSwz[2]);
+ bld.swz(regFile[1], regIdx[1], regSwizzleBin[1]);
+ break;
+ case combiner::kOpMulSub:
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += std::string("col")+writeMask + " = " + reg[0]+regSwizzle[0] + " * " + reg[2]+".a - " + reg[1]+regSwizzle[1] + ";\n";
+ #endif
+ bld.op(kSM4Op_MAD);
+ bld.reg('r', 0, writeMaskBin);
+ bld.swz(regFile[0], regIdx[0], regSwizzleBin[0]);
+ bld.swz(regFile[2], regIdx[2], regSrcAlphaSwz[2]);
+ bld.swz(regFile[1], regIdx[1], regSwizzleBin[1], true);
+ break;
+ case combiner::kOpMulAddSigned:
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += std::string("col")+writeMask + " = " + reg[0]+regSwizzle[0] + " * " + reg[2]+".a + " + reg[1]+regSwizzle[1] + " - 0.5;\n";
+ #endif
+ bld.op(kSM4Op_MAD);
+ bld.reg('r', 0, writeMaskBin);
+ bld.swz(regFile[0], regIdx[0], regSwizzleBin[0]);
+ bld.swz(regFile[2], regIdx[2], regSrcAlphaSwz[2]);
+ bld.swz(regFile[1], regIdx[1], regSwizzleBin[1]);
+ bld.op(kSM4Op_ADD);
+ bld.reg('r', 0, writeMaskBin);
+ bld.swz('r', 0);
+ bld.float4(-.5f,-.5f,-.5f,-.5f);
+ break;
+ default:
+ AssertString ("Unknown combiner op!");
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += std::string("col")+writeMask + " = " + reg[0]+regSwizzle[0] + ";\n";
+ #endif
+ break;
+ }
+
+ // scale
+ if (scale > 1)
+ {
+ DebugAssert (scale == 2 || scale == 4);
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += std::string("col *= ") + char('0'+scale) + ".0;\n";
+ #endif
+ if (scale == 2)
+ {
+ bld.op(kSM4Op_ADD);
+ bld.reg('r', 0, writeMaskBin);
+ bld.swz('r', 0);
+ bld.swz('r', 0);
+ }
+ else if (scale == 4)
+ {
+ bld.op(kSM4Op_MUL);
+ bld.reg('r', 0, writeMaskBin);
+ bld.swz('r', 0);
+ bld.float4(4.0f,4.0f,4.0f,4.0f);
+ }
+ }
+
+ return usedConstant;
+}
+
+void* BuildFragmentShaderD3D11 (const FixedFunctionStateD3D11& state, FixedFunctionProgramD3D11::ValueParameters& params, size_t& outSize)
+{
+ ShaderLab::FastPropertyName cbName; cbName.SetName(kD3D11PixelCB);
+ GetD3D11ConstantBuffers(GetRealGfxDevice()).SetCBInfo (cbName.index, k11PixelSize*16);
+ params.m_CBID = cbName.index; params.m_CBSize = k11PixelSize*16;
+
+ DXBCBuilder* builder = dxb_create(4, 0, kSM4Shader_Pixel);
+ DXBCBuilderStream bld(builder);
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ std::string textures, inputs, code;
+ #endif
+
+ dxb_dcl_output(builder, "SV_Target", 0, 0);
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ AddToStringList (inputs, "float4 icolor : COLOR0");
+ #endif
+ int inputRegCounter = 0;
+ dxb_dcl_input(builder, "COLOR", 0, inputRegCounter++);
+
+ if (state.lightingEnabled && state.specularEnabled)
+ {
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ AddToStringList (inputs, "float3 ispec : COLOR1");
+ #endif
+ dxb_dcl_input(builder, "COLOR", 1, inputRegCounter++, 0x7);
+ }
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "float4 col;\n";
+ #endif
+
+ if (state.texUnitCount == 0)
+ {
+ // No combiners is special case: output primary color
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "col = icolor;\n";
+ #endif
+ bld.op(kSM4Op_MOV).reg('r', 0).swz('v', 0);
+
+ // BUG, using for ex.,
+ // SubShader { Pass { Color (1,0,0,0) } }
+ // produces white color instead of red on IvyBridge UltraBook
+ }
+ else
+ {
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "float4 tex, tmp0, tmp1, tmp2;\n";
+ #endif
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ std::string iname = IntToString(i);
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ AddToStringList (inputs, ("float4 iuv"+iname + " : TEXCOORD"+iname).c_str());
+ textures += "SamplerState ff_smp"+iname + " : register(s"+iname+");\n";
+ #endif
+
+ // sample the texture into tmp1
+ if (state.texUnit3D & (1<<i)) // 3D
+ {
+ dxb_dcl_input(builder, "TEXCOORD", i, inputRegCounter,0x7);
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ textures += "Texture3D ff_tex"+iname + " : register(t"+iname+");\n";
+ code += "tex = ff_tex"+iname + ".Sample(ff_smp"+iname + ", iuv"+iname + ".xyz);\n";
+ #endif
+
+ dxb_dcl_tex(builder, i, kSM4Target_TEXTURE3D);
+ bld.op(kSM4Op_SAMPLE).reg('r', 1).swz('v', inputRegCounter, kSM4SwzXYZX).swz('t', i).reg('s', i);
+ }
+ else if (state.texUnitCube & (1<<i)) // cubemap
+ {
+ dxb_dcl_input(builder, "TEXCOORD", i, inputRegCounter,0x7);
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ textures += "TextureCube ff_tex"+iname + " : register(t"+iname+");\n";
+ code += "tex = ff_tex"+iname + ".Sample(ff_smp"+iname + ", iuv"+iname + ".xyz);\n";
+ #endif
+
+ dxb_dcl_tex(builder, i, kSM4Target_TEXTURECUBE);
+ bld.op(kSM4Op_SAMPLE).reg('r', 1).swz('v', inputRegCounter, kSM4SwzXYZX).swz('t', i).reg('s', i);
+ }
+ else if (state.texUnitProjected & (1<<i)) // projected sample
+ {
+ dxb_dcl_input(builder, "TEXCOORD", i, inputRegCounter,0xB); // xyw mask
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ textures += "Texture2D ff_tex"+iname + " : register(t"+iname+");\n";
+ code += "tex = ff_tex"+iname + ".Sample(ff_smp"+iname + ", iuv"+iname + ".xy / iuv"+iname + ".w);\n";
+ #endif
+
+ dxb_dcl_tex(builder, i, kSM4Target_TEXTURE2D);
+
+ // SM4: use DIV; Intel IvyBridge seems to prefer that
+ bld.noAutoSM2();
+ bld.op(kSM4Op_DIV).reg('r', 1, 0x3).swz('v', inputRegCounter, kSM4SwzXYXX).swz('v', inputRegCounter, kSM4SwzRepW);
+ bld.autoSM2();
+
+ // SM2: use RCP+MUL
+ bld.op2(kSM2Op_RCP).reg2('r', 1, 8).swz2('v', inputRegCounter, kSM4SwzRepW);
+ bld.op2(kSM2Op_MUL).reg2('r', 1, 0x3).swz2('v', inputRegCounter, kSM4SwzXYXX).swz2('r',1, kSM4SwzRepW);
+
+ bld.op(kSM4Op_SAMPLE).reg('r', 1).swz('r', 1, kSM4SwzXYXX).swz('t', i).reg('s', i);
+ }
+ else // regular sample
+ {
+ dxb_dcl_input(builder, "TEXCOORD", i, inputRegCounter,0x3);
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ textures += "Texture2D ff_tex"+iname + " : register(t"+iname+");\n";
+ code += "tex = ff_tex"+iname + ".Sample(ff_smp"+iname + ", iuv"+iname + ".xy);\n";
+ #endif
+
+ dxb_dcl_tex(builder, i, kSM4Target_TEXTURE2D);
+ bld.op(kSM4Op_SAMPLE).reg('r', 1).swz('v', inputRegCounter, kSM4SwzXYXX).swz('t', i).reg('s', i);
+ }
+
+ // emit color & alpha combiners; result in tmp0
+ UInt32 colorComb = state.texUnitColorCombiner[i];
+ UInt32 alphaComb = state.texUnitAlphaCombiner[i];
+ bool usedConstant = false;
+ if (colorComb == alphaComb)
+ {
+ usedConstant |= EmitCombinerMath11 (i, colorComb, kCombWriteRGBA, state.texUnitCount, bld
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ , code
+ #endif
+ );
+ }
+ else
+ {
+ usedConstant |= EmitCombinerMath11 (i, colorComb, kCombWriteRGB, state.texUnitCount, bld
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ , code
+ #endif
+ );
+ usedConstant |= EmitCombinerMath11 (i, alphaComb, kCombWriteA, state.texUnitCount, bld
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ , code
+ #endif
+ );
+ }
+
+ if (usedConstant)
+ params.AddVectorParam ((k11PixelColors+i)*16, 4, BuiltinShaderVectorParam(kShaderVecFFTextureEnvColor0+i));
+ ++inputRegCounter;
+ }
+ }
+
+ // alpha test
+ if (state.alphaTest != kFuncDisabled && state.alphaTest != kFuncAlways)
+ {
+ params.AddVectorParam (k11PixelAlphaRef*16, 1, kShaderVecFFAlphaTestRef);
+ if (state.alphaTest == kFuncNever)
+ {
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "discard;\n";
+ #endif
+ bld.op(kSM4Op_DISCARD).float1(-1); // int is not sm20 compatible; old comment: HLSL emits 'l(-1)' for plain discard; with the value being integer -1 (all bits set)
+ }
+ else
+ {
+ // Reverse logic because we're using here 'discard'
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ static const char* kCmpOps[] =
+ {
+ "", // kFuncDisabled
+ "", // kFuncNever
+ ">=", // kFuncLess
+ "!=", // kFuncEqual
+ ">", // kFuncLEqual
+ "<=", // kFuncGreater
+ "==", // kFuncNotEqual
+ "<", // kFuncGEqual
+ "", // kFuncAlways
+ };
+ #endif // #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ static SM4Opcode kCmpOpcodes[] =
+ {
+ kSM4Op_ADD, // kFuncDisabled
+ kSM4Op_ADD, // kFuncNever
+ kSM4Op_GE, // kFuncLess
+ kSM4Op_NE, // kFuncEqual
+ kSM4Op_LT, // kFuncLEqual
+ kSM4Op_GE, // kFuncGreater
+ kSM4Op_EQ, // kFuncNotEqual
+ kSM4Op_LT, // kFuncGEqual
+ kSM4Op_ADD, // kFuncAlways
+ };
+ static bool kCmpOrder[] =
+ {
+ false, // kFuncDisabled
+ false, // kFuncNever
+ true, // kFuncLess
+ true, // kFuncEqual
+ false, // kFuncLEqual
+ false, // kFuncGreater
+ true, // kFuncNotEqual
+ true, // kFuncGEqual
+ false, // kFuncAlways
+ };
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += std::string("if (col.a ") + kCmpOps[state.alphaTest] + " ff_alpha_ref) discard;\n";
+ #endif
+
+ bld.noAutoSM2();
+ bld.op(kCmpOpcodes[state.alphaTest]).reg('r', 1, 0x1);
+ if (kCmpOrder[state.alphaTest])
+ {
+ bld.swz('r', 0, kSM4SwzRepW);
+ bld.swz('c', k11PixelAlphaRef, kSM4SwzRepX);
+ }
+ else
+ {
+ bld.swz('c', k11PixelAlphaRef, kSM4SwzRepX);
+ bld.swz('r', 0, kSM4SwzRepW);
+ }
+ bld.op(kSM4Op_DISCARD).reg('r', 1, 1);
+ bld.autoSM2();
+
+ //SM20
+ static float bConst[][2] =
+ {
+ {0,0},
+ {0,0},
+
+ {0,-1},
+ {0,-1},
+ {-1,0},
+ {0,-1},
+ {-1,0},
+ {-1,0},
+
+ {0,0},
+ };
+ static bool bRefSign[] =
+ {
+ false,
+ false,
+
+ false,
+ true,
+ true,
+ true,
+ true,
+ false,
+ false,
+ };
+
+ bld.op2(kSM2Op_ADD).
+ reg2('r',1,1).
+ swz2('c',k11PixelAlphaRef, kSM4SwzRepX,bRefSign[state.alphaTest]).
+ swz2('r', 0, kSM4SwzRepW,!bRefSign[state.alphaTest]);
+ if (state.alphaTest == kFuncEqual || state.alphaTest == kFuncNotEqual)
+ bld.op2(kSM2Op_MUL).reg2('r',1,1).swz2('r',1,kSM4SwzRepX).swz2('r',1,kSM4SwzRepX);
+ bld.op2(kSM2Op_CMP).reg2('r',1).
+ swz2('r',1,kSM4SwzRepX,state.alphaTest == kFuncEqual || state.alphaTest == kFuncNotEqual).
+ float1_2(bConst[state.alphaTest][0]).
+ float1_2(bConst[state.alphaTest][1]);
+ bld.op2(kSM2Op_TEXKILL).reg2('r', 1);
+ }
+ }
+
+ // add specular
+ if (state.lightingEnabled && state.specularEnabled)
+ {
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "col.rgb += ispec;\n";
+ #endif
+ // add r0.xyz, r0.xyz, v1.xyz
+ bld.op(kSM4Op_ADD).reg('r', 0, 0x7).swz('r', 0, kSM4SwzXYZX).swz('v', 1, kSM4SwzXYZX);
+ }
+
+ // fog
+ if (state.fogMode != kFogDisabled && inputRegCounter < 8)
+ {
+ int fogVar = inputRegCounter;
+ dxb_dcl_input(builder, "FOG", 0, fogVar, 0x1);
+ params.AddVectorParam (k11PixelFog*16, 4, kShaderVecFFFogColor);
+ // color.rgb = lerp (fogColor.rgb, color.rgb, fogVar) =
+ // (color.rgb-fogColor.rgb) * fogVar + fogColor.rgb
+ bld.op(kSM4Op_ADD).reg('r',0,7).swz('r',0,kSM4SwzXYZX).swz('c',k11PixelFog,kSM4SwzXYZX, true);
+ bld.op(kSM4Op_MAD).reg('r',0,7).swz('r',0,kSM4SwzXYZX).swz('v',fogVar,kSM4SwzRepX).swz('c',k11PixelFog,kSM4SwzXYZX);
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ AddToStringList (inputs, "float ifog : FOG");
+ code += "col.rgb = lerp (ff_fog.rgb, col.rgb, ifog);\n";
+ #endif
+ }
+
+ if (params.HasVectorParams())
+ dxb_dcl_cb(builder, 0, k11PixelSize);
+
+ // mov o0.xyzw, r0.xyzw
+ bld.op(kSM4Op_MOV).reg('o', 0).swz('r', 0);
+ // ret
+ bld.op(kSM4Op_RET);
+
+ #if DEBUG_D3D11_COMPARE_WITH_HLSL
+ code += "return col;\n";
+ std::string src = textures + kD3D11PixelPrefix + inputs + ") : SV_TARGET {\n" + code + "\n}";
+ printf_console ("d3d11 FF PS HLSL:\n%s\n", src.c_str());
+ DebugCompileHLSLShaderD3D11 (src, false);
+ #endif
+
+ void* blob = BuildShaderD3D11 (builder, outSize);
+ return blob;
+}
diff --git a/Runtime/GfxDevice/d3d11/ShaderGeneratorD3D11.h b/Runtime/GfxDevice/d3d11/ShaderGeneratorD3D11.h
new file mode 100644
index 0000000..6316275
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/ShaderGeneratorD3D11.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "D3D11Includes.h"
+#include "GpuProgramsD3D11.h"
+
+
+struct FixedFunctionStateD3D11;
+
+void* BuildVertexShaderD3D11 (const FixedFunctionStateD3D11& state, FixedFunctionProgramD3D11::ValueParameters& params, BuiltinShaderParamIndices& matrices, size_t& outSize);
+void* BuildFragmentShaderD3D11 (const FixedFunctionStateD3D11& state, FixedFunctionProgramD3D11::ValueParameters& params, size_t& outSize);
diff --git a/Runtime/GfxDevice/d3d11/ShaderGeneratorLinkD3D11.cpp b/Runtime/GfxDevice/d3d11/ShaderGeneratorLinkD3D11.cpp
new file mode 100644
index 0000000..eb64cfd
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/ShaderGeneratorLinkD3D11.cpp
@@ -0,0 +1,985 @@
+#include "UnityPrefix.h"
+
+#if UNITY_METRO_VS2013 || (UNITY_WIN && !UNITY_WINRT)
+
+#include "ShaderGeneratorD3D11.h"
+#include "FixedFunctionStateD3D11.h"
+#include "ConstantBuffersD3D11.h"
+#include "Runtime/GfxDevice/GpuProgram.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "PlatformDependent/Win/SmartComPointer.h"
+#include "Runtime/Utilities/File.h"
+
+#include "Runtime/GfxDevice/d3d11/InternalShaders/FFShaderLib.h"
+
+#if UNITY_WINRT
+#include <D3DCompiler.h>
+#endif
+
+ConstantBuffersD3D11& GetD3D11ConstantBuffers (GfxDevice& device);
+
+
+typedef SmartComPointer<ID3D11LinkingNode> NodePtr;
+typedef SmartComPointer<ID3D11ModuleInstance> ModuleInstancePtr;
+typedef SmartComPointer<ID3D11Module> ModulePtr;
+typedef SmartComPointer<ID3D11Linker> LinkerPtr;
+typedef SmartComPointer<ID3D11FunctionLinkingGraph> FLGPtr;
+typedef SmartComPointer<ID3DBlob> BlobPtr;
+
+const int kMaxSignatureParams = 12;
+const int kSamplingModuleCount = 32;
+
+
+struct D3D11ShaderLinker
+{
+ D3D11ShaderLinker()
+ : createLinkerFunc(0)
+ , loadModuleFunc(0)
+ , createFunctionLinkingGraphFunc(0)
+ , m_Dll(0)
+ , m_Initialized(false)
+ , m_Valid(false)
+ {
+ }
+
+ ~D3D11ShaderLinker()
+ {
+ m_Module.Release();
+ m_ModuleInstanceVS.Release();
+ m_ModuleInstancePS.Release();
+
+ for (int i = 0; i < kSamplingModuleCount; ++i)
+ {
+ m_SamplingModules[i].Release();
+ m_SamplingModuleInstances[i].Release();
+ }
+
+ if (m_Dll)
+ FreeLibrary(m_Dll);
+ }
+
+ void Initialize (
+ const char* dllName,
+ const BYTE* shaderLibraryCode, size_t shaderLibrarySize,
+ const BYTE** samplingLibraryCodes, const size_t* samplingLibrarySizes);
+
+ typedef HRESULT (WINAPI *D3DCreateLinkerFunc)(ID3D11Linker **ppLinker);
+ typedef HRESULT (WINAPI *D3DLoadModuleFunc)(
+ const void* srcData,
+ SIZE_T srcDataSize,
+ ID3D11Module** ppModule);
+ typedef HRESULT (WINAPI *D3DCreateFunctionLinkingGraphFunc)(
+ UINT uFlags,
+ ID3D11FunctionLinkingGraph **ppFunctionLinkingGraph);
+
+ D3DCreateLinkerFunc createLinkerFunc;
+ D3DLoadModuleFunc loadModuleFunc;
+ D3DCreateFunctionLinkingGraphFunc createFunctionLinkingGraphFunc;
+
+ HMODULE m_Dll;
+ bool m_Initialized;
+ bool m_Valid;
+
+ ModulePtr m_Module;
+ ModuleInstancePtr m_ModuleInstanceVS, m_ModuleInstancePS;
+
+ ModulePtr m_SamplingModules[kSamplingModuleCount];
+ ModuleInstancePtr m_SamplingModuleInstances[kSamplingModuleCount];
+};
+
+static D3D11ShaderLinker s_Linker;
+
+void D3D11ShaderLinker::Initialize (
+ const char* dllName,
+ const BYTE* shaderLibraryCode, size_t shaderLibrarySize,
+ const BYTE** samplingLibraryCodes, const size_t* samplingLibrarySizes)
+{
+ if (m_Initialized)
+ return;
+
+ m_Valid = false;
+ m_Initialized = true;
+
+ // Load DLL
+ #if UNITY_WINRT
+ // We use a proper linked library on Metro Blue
+ //m_Dll = LoadPackagedLibrary (ConvertToWindowsPath(dllName)->Data(), 0);
+ #else
+ m_Dll = LoadLibraryA (dllName);
+ if (!m_Dll)
+ return;
+ #endif
+
+ // Get functions
+#if UNITY_WINRT
+ createLinkerFunc = (D3DCreateLinkerFunc)&D3DCreateLinker;
+ loadModuleFunc = (D3DLoadModuleFunc)&D3DLoadModule;
+ createFunctionLinkingGraphFunc = (D3DCreateFunctionLinkingGraphFunc)&D3DCreateFunctionLinkingGraph;
+#else
+ createLinkerFunc = (D3DCreateLinkerFunc) GetProcAddress (m_Dll, "D3DCreateLinker");
+ loadModuleFunc = (D3DLoadModuleFunc) GetProcAddress (m_Dll, "D3DLoadModule");
+ createFunctionLinkingGraphFunc = (D3DCreateFunctionLinkingGraphFunc) GetProcAddress (m_Dll, "D3DCreateFunctionLinkingGraph");
+#endif
+
+ if (createLinkerFunc == 0 || loadModuleFunc == 0 || createFunctionLinkingGraphFunc == 0)
+ return;
+
+ HRESULT hr = loadModuleFunc(shaderLibraryCode, shaderLibrarySize, &m_Module);
+ if (FAILED(hr))
+ {
+ printf("DX11: Failed to load compiled library: 0x%x\n", hr);
+ return;
+ }
+
+ // Setup HLSL linker
+ hr = m_Module->CreateInstance ("", &m_ModuleInstanceVS);
+ if (FAILED(hr))
+ {
+ printf("DX11: Failed to create compiled library instance: 0x%x\n", hr);
+ return;
+ }
+ hr = m_ModuleInstanceVS->BindConstantBufferByName("UnityFFVertex",0,0);
+
+ hr = m_Module->CreateInstance ("", &m_ModuleInstancePS);
+ if (FAILED(hr))
+ {
+ printf("DX11: Failed to create compiled library instance: 0x%x\n", hr);
+ return;
+ }
+ hr = m_ModuleInstancePS->BindConstantBufferByName("UnityFFPixel",0,0);
+
+
+ // Setup sampling modules
+ for (int i = 0; i < kSamplingModuleCount; ++i)
+ {
+ hr = loadModuleFunc(samplingLibraryCodes[i], samplingLibrarySizes[i], &m_SamplingModules[i]);
+ AssertIf(FAILED(hr));
+ hr = m_SamplingModules[i]->CreateInstance ("", &m_SamplingModuleInstances[i]);
+ AssertIf(FAILED(hr));
+
+ int unit = i % 8;
+ hr = m_SamplingModuleInstances[i]->BindResource(unit, unit, 1);
+ hr = m_SamplingModuleInstances[i]->BindSampler(unit, unit, 1);
+ }
+
+ m_Valid = true;
+}
+
+bool HasD3D11Linker()
+{
+ const char* dllName = "D3DCompiler_47.dll";
+ s_Linker.Initialize(dllName, g_FFShaderLibrary, sizeof(g_FFShaderLibrary), g_FFSampleTexLib, g_FFSampleTexLibSize);
+ return s_Linker.m_Valid;
+}
+
+
+// --- Constant buffers & utilities --------------------------------------------------------------
+
+
+static const char* kD3D11VertexCB = "UnityFFVertex";
+static const char* kD3D11PixelCB = "UnityFFPixel";
+
+enum {
+ k11VertexMVP = 0,
+ k11VertexMV = 4,
+ k11VertexColor = 8,
+ k11VertexAmbient = 9,
+ k11VertexLightColor = 10,
+ k11VertexLightPos = 18,
+ k11VertexLightAtten = 26,
+ k11VertexLightSpot = 34,
+ k11VertexMatDiffuse = 42,
+ k11VertexMatAmbient = 43,
+ k11VertexMatSpec = 44,
+ k11VertexMatEmission = 45,
+ k11VertexTex = 46,
+ k11VertexFog = 62,
+ k11VertexSize = 63,
+};
+enum {
+ k11PixelColors = 0,
+ k11PixelAlphaRef = 8,
+ k11PixelFog = 9,
+ k11PixelSize = 10
+};
+
+
+// --- VERTEX program ----------------------------------------------------------------------------
+
+
+static D3D11_PARAMETER_DESC CreateParamDesc(const char* name, const char* sem, int dim)
+{
+ D3D11_PARAMETER_DESC desc = {
+ name,
+ sem,
+ D3D_SVT_FLOAT,
+ dim == 1 ? D3D_SVC_SCALAR : D3D_SVC_VECTOR,
+ 1,
+ dim,
+ D3D_INTERPOLATION_UNDEFINED,
+ D3D_PF_NONE,
+ 0, 0, 0, 0
+ };
+ return desc;
+}
+
+
+void* BuildVertexShaderD3D11_Link (const FixedFunctionStateD3D11& state, FixedFunctionProgramD3D11::ValueParameters& params, BuiltinShaderParamIndices& matrices, size_t& outSize)
+{
+ ShaderLab::FastPropertyName cbName; cbName.SetName(kD3D11VertexCB);
+ GetD3D11ConstantBuffers(GetRealGfxDevice()).SetCBInfo (cbName.index, k11VertexSize*16);
+ params.m_CBID = cbName.index; params.m_CBSize = k11VertexSize*16;
+
+ HRESULT hr = S_OK;
+ const bool hasLinker = HasD3D11Linker();
+ Assert(hasLinker);
+ FLGPtr flg;
+ s_Linker.createFunctionLinkingGraphFunc (0, &flg);
+ D3D11_PARAMETER_DESC inputSig[kMaxSignatureParams];
+ D3D11_PARAMETER_DESC outputSig[kMaxSignatureParams];
+
+ bool hasLights = (state.lightingEnabled && state.lightCount > 0);
+
+ bool eyePositionRequired =
+ hasLights ||
+ (state.fogMode != kFogDisabled);
+ bool eyeNormalRequired = hasLights;
+ bool viewDirRequired = hasLights && state.specularEnabled;
+ bool eyeReflRequired = false;
+ {
+ UInt64 texSources = state.texUnitSources;
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ UInt32 uvSource = texSources & 0xF;
+ if (uvSource == kTexSourceEyeLinear)
+ eyePositionRequired = true;
+ if (uvSource == kTexSourceCubeNormal)
+ eyeNormalRequired = true;
+ if (uvSource == kTexSourceCubeReflect || uvSource == kTexSourceSphereMap)
+ eyeReflRequired = viewDirRequired = eyePositionRequired = eyeNormalRequired = true;
+ texSources >>= 4;
+ }
+ }
+ if (eyePositionRequired || eyeNormalRequired || eyeReflRequired)
+ {
+ matrices.mat[kShaderInstanceMatMV].gpuIndex = k11VertexMV*16;
+ matrices.mat[kShaderInstanceMatMV].rows = 4;
+ matrices.mat[kShaderInstanceMatMV].cols = 4;
+ matrices.mat[kShaderInstanceMatMV].cbID = params.m_CBID;
+ }
+
+ int inputRegCounter = 0, outputRegCounter = 0;
+ int inPosReg = 0, inColorReg = 0, inNormalReg = 0;
+ int inUVReg[8] = {0};
+ int outColor0Reg = 0, outColor1Reg = 0, outPosReg = 0;
+ int outUVReg[8] = {0};
+ int outFogReg = -1;
+
+
+ // ---- Figure out input signature ----------------------------------------
+
+ // Position
+ inputSig[inPosReg = inputRegCounter++] = CreateParamDesc("vertex", "POSITION", 4);
+ // Vertex color
+ if (!state.useUniformInsteadOfVertexColor)
+ inputSig[inColorReg = inputRegCounter++] = CreateParamDesc("vertexColor", "COLOR", 4);
+ // Normal
+ if (eyeNormalRequired)
+ inputSig[inNormalReg = inputRegCounter++] = CreateParamDesc("normal", "NORMAL", 3);
+ // UVs
+ UInt32 gotInputs = 0;
+ static const char* kUVNames[kMaxSupportedTextureCoords] = {"uv0","uv1","uv2","uv3","uv4","uv5","uv6","uv7"};
+ static const char* kUVOutNames[kMaxSupportedTextureCoords] = {"ouv0","ouv1","ouv2","ouv3","ouv4","ouv5","ouv6","ouv7"};
+ static const char* kUVSemantics[kMaxSupportedTextureCoords] = {"TEXCOORD0","TEXCOORD1","TEXCOORD2","TEXCOORD3","TEXCOORD4","TEXCOORD5","TEXCOORD6","TEXCOORD7"};
+ UInt64 texSources = state.texUnitSources;
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ UInt32 uvSource = texSources & 0xF;
+ if (uvSource >= kTexSourceUV0 && uvSource <= kTexSourceUV7)
+ {
+ unsigned uv = uvSource-kTexSourceUV0;
+ if (!(gotInputs & (1<<uv)))
+ {
+ inputSig[inUVReg[uv] = inputRegCounter++] = CreateParamDesc(kUVNames[uv], kUVSemantics[uv], 4);
+ gotInputs |= (1<<uv);
+ }
+ }
+ texSources >>= 4;
+ }
+
+ NodePtr inputNode;
+ Assert(inputRegCounter <= kMaxSignatureParams);
+ hr = flg->SetInputSignature(inputSig, inputRegCounter, &inputNode);
+
+
+ // ---- Figure out output signature ---------------------------------------
+
+ // color
+ outputSig[outColor0Reg = outputRegCounter++] = CreateParamDesc("ocolor", "COLOR0", 4);
+ // spec color
+ if (state.lightingEnabled && state.specularEnabled)
+ outputSig[outColor1Reg = outputRegCounter++] = CreateParamDesc("ospec", "COLOR1", 3);
+ // UVs
+ for (int i = 0; i < state.texUnitCount; i++)
+ outputSig[outUVReg[i] = outputRegCounter++] = CreateParamDesc(kUVOutNames[i], kUVSemantics[i], 4);
+ // Fog
+ if (state.fogMode != kFogDisabled && outputRegCounter < 8)
+ outputSig[outFogReg = outputRegCounter++] = CreateParamDesc("ofog", "FOG0", 1);
+ // position
+ outputSig[outPosReg = outputRegCounter++] = CreateParamDesc("overtex", "SV_POSITION", 4);
+
+ // ---- Build code --------------------------------------------------------
+
+
+ NodePtr colorNode, eyePosNode, eyeNormalNode, viewDirNode, eyeReflNode;
+ NodePtr ambientNode, diffuseNode, emissionNode;
+ NodePtr ambientNodeToUse, diffuseNodeToUse, emissionNodeToUse;
+ NodePtr lightNode, specNode, fogNode;
+
+ // color = Vertex or uniform color
+ if (state.useUniformInsteadOfVertexColor)
+ {
+ params.AddVectorParam (k11VertexColor*16, 4, kShaderVecFFColor);
+ hr = flg->CallFunction("", s_Linker.m_Module, "LoadVertexColorUniform", &colorNode);
+ }
+ else
+ {
+ hr = flg->CallFunction("", s_Linker.m_Module, "LoadVertexColor", &colorNode);
+ hr = flg->PassValue(inputNode, inColorReg, colorNode, 0);
+ }
+
+ // eyePos = eye position
+ if (eyePositionRequired)
+ {
+ hr = flg->CallFunction("", s_Linker.m_Module, "LoadEyePos", &eyePosNode);
+ hr = flg->PassValue(inputNode, inPosReg, eyePosNode, 0);
+ }
+
+ // eyeNormal = normalize(normalMatrix * normal)
+ if (eyeNormalRequired)
+ {
+ hr = flg->CallFunction("", s_Linker.m_Module, "LoadEyeNormal", &eyeNormalNode);
+ hr = flg->PassValue(inputNode, inNormalReg, eyeNormalNode, 0);
+ }
+
+ // view dir
+ if (viewDirRequired)
+ {
+ Assert(eyePosNode);
+ hr = flg->CallFunction("", s_Linker.m_Module, "LoadViewDir", &viewDirNode);
+ hr = flg->PassValue(eyePosNode, D3D_RETURN_PARAMETER_INDEX, viewDirNode, 0);
+ }
+ else
+ {
+ hr = flg->CallFunction("", s_Linker.m_Module, "LoadZero", &viewDirNode);
+ }
+
+ // eyeRefl
+ if (eyeReflRequired)
+ {
+ DebugAssert (viewDirRequired);
+ // eyeRefl = reflection vector, 2*dot(V,N)*N-V
+ Assert(eyeNormalNode);
+ Assert(viewDirNode);
+ hr = flg->CallFunction("", s_Linker.m_Module, "LoadEyeRefl", &eyeReflNode);
+ hr = flg->PassValue(viewDirNode, D3D_RETURN_PARAMETER_INDEX, eyeReflNode, 0);
+ hr = flg->PassValue(eyeNormalNode, D3D_RETURN_PARAMETER_INDEX, eyeReflNode, 1);
+ }
+
+ // Lighting
+ if (state.lightingEnabled)
+ {
+ if (state.colorMaterial==kColorMatAmbientAndDiffuse)
+ {
+ ambientNodeToUse = colorNode;
+ diffuseNodeToUse = colorNode;
+ }
+ else
+ {
+ hr = flg->CallFunction("", s_Linker.m_Module, "LoadAmbientColor", &ambientNode);
+ ambientNodeToUse = ambientNode;
+ hr = flg->CallFunction("", s_Linker.m_Module, "LoadDiffuseColor", &diffuseNode);
+ diffuseNodeToUse = diffuseNode;
+ }
+ if (state.colorMaterial==kColorMatEmission)
+ {
+ emissionNodeToUse = colorNode;
+ }
+ else
+ {
+ hr = flg->CallFunction("", s_Linker.m_Module, "LoadEmissionColor", &emissionNode);
+ emissionNodeToUse = emissionNode;
+ }
+
+ params.AddVectorParam (k11VertexAmbient*16, 4, kShaderVecLightModelAmbient);
+ params.AddVectorParam (k11VertexMatAmbient*16, 4, kShaderVecFFMatAmbient);
+ params.AddVectorParam (k11VertexMatDiffuse*16, 4, kShaderVecFFMatDiffuse);
+ params.AddVectorParam (k11VertexMatSpec*16, 4, kShaderVecFFMatSpecular);
+ params.AddVectorParam (k11VertexMatEmission*16, 4, kShaderVecFFMatEmission);
+
+ Assert(emissionNodeToUse);
+ Assert(ambientNodeToUse);
+ hr = flg->CallFunction("", s_Linker.m_Module, "InitLightColor", &lightNode);
+ hr = flg->PassValue(emissionNodeToUse, D3D_RETURN_PARAMETER_INDEX, lightNode, 0);
+ hr = flg->PassValue(ambientNodeToUse, D3D_RETURN_PARAMETER_INDEX, lightNode, 1);
+ hr = flg->CallFunction("", s_Linker.m_Module, "LoadZero", &specNode);
+ if (state.lightCount > 0)
+ {
+ NodePtr lightCallNode;
+ std::string lightFuncName = state.specularEnabled ? "ComputeSpotLightSpec" : "ComputeSpotLight";
+ lightFuncName += ('0' + state.lightCount);
+ hr = flg->CallFunction("", s_Linker.m_Module, lightFuncName.c_str(), &lightCallNode);
+ hr = flg->PassValue(eyePosNode, D3D_RETURN_PARAMETER_INDEX, lightCallNode, 0);
+ hr = flg->PassValue(eyeNormalNode, D3D_RETURN_PARAMETER_INDEX, lightCallNode, 1);
+ hr = flg->PassValue(viewDirNode, D3D_RETURN_PARAMETER_INDEX, lightCallNode, 2);
+ hr = flg->PassValue(diffuseNodeToUse, D3D_RETURN_PARAMETER_INDEX, lightCallNode, 3);
+ hr = flg->PassValue(specNode, D3D_RETURN_PARAMETER_INDEX, lightCallNode, 4);
+ hr = flg->PassValue(lightNode, D3D_RETURN_PARAMETER_INDEX, lightCallNode, 5);
+
+ NodePtr specMoveNode;
+ hr = flg->CallFunction("", s_Linker.m_Module, "Load3", &specMoveNode);
+ hr = flg->PassValue(lightCallNode, 4, specMoveNode, 0);
+ specNode = specMoveNode;
+
+ lightNode = lightCallNode;
+ }
+
+ for (int i = 0; i < state.lightCount; ++i)
+ {
+ params.AddVectorParam ((k11VertexLightPos+i)*16, 4, BuiltinShaderVectorParam(kShaderVecLight0Position+i));
+ params.AddVectorParam ((k11VertexLightAtten+i)*16, 4, BuiltinShaderVectorParam(kShaderVecLight0Atten+i));
+ params.AddVectorParam ((k11VertexLightColor+i)*16, 4, BuiltinShaderVectorParam(kShaderVecLight0Diffuse+i));
+ params.AddVectorParam ((k11VertexLightSpot+i)*16, 4, BuiltinShaderVectorParam(kShaderVecLight0SpotDirection+i));
+ }
+
+ NodePtr finalLightColor;
+ hr = flg->CallFunction("", s_Linker.m_Module, "LoadLightingColor", &finalLightColor);
+ hr = flg->PassValue(lightNode, D3D_RETURN_PARAMETER_INDEX, finalLightColor, 0);
+ hr = flg->PassValue(diffuseNodeToUse, D3D_RETURN_PARAMETER_INDEX, finalLightColor, 1);
+ colorNode = finalLightColor;
+
+ if (state.specularEnabled)
+ {
+ NodePtr finalSpecColor;
+ hr = flg->CallFunction("", s_Linker.m_Module, "ModulateSpec", &finalSpecColor);
+ hr = flg->PassValue(specNode, D3D_RETURN_PARAMETER_INDEX, finalSpecColor, 0);
+ specNode = finalSpecColor;
+ }
+ }
+
+ // Output final color
+ NodePtr saturatedColorNode;
+ NodePtr saturatedSpecNode;
+ hr = flg->CallFunction("", s_Linker.m_Module, "Saturate4", &saturatedColorNode);
+ hr = flg->PassValue(colorNode, D3D_RETURN_PARAMETER_INDEX, saturatedColorNode, 0);
+
+ if (state.lightingEnabled && state.specularEnabled)
+ {
+ Assert(specNode);
+ hr = flg->CallFunction("", s_Linker.m_Module, "Saturate3", &saturatedSpecNode);
+ hr = flg->PassValue(specNode, D3D_RETURN_PARAMETER_INDEX, saturatedSpecNode, 0);
+ }
+
+ // Pass & transform texture coordinates
+ NodePtr texNodes[kMaxSupportedTextureCoords] = {0};
+
+ texSources = state.texUnitSources;
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ matrices.mat[kShaderInstanceMatTexture0+i].gpuIndex = (k11VertexTex+i*4)*16;
+ matrices.mat[kShaderInstanceMatTexture0+i].rows = 4;
+ matrices.mat[kShaderInstanceMatTexture0+i].cols = 4;
+ matrices.mat[kShaderInstanceMatTexture0+i].cbID = params.m_CBID;
+
+ std::string iname = IntToString(i);
+ std::string texFuncName = "MultiplyUV";
+ texFuncName += iname;
+
+ UInt32 uvSource = texSources & 0xF;
+ if (uvSource >= kTexSourceUV0 && uvSource <= kTexSourceUV7)
+ {
+ unsigned uv = uvSource-kTexSourceUV0;
+ hr = flg->CallFunction("", s_Linker.m_Module, texFuncName.c_str(), &texNodes[i]);
+ hr = flg->PassValue(inputNode, inUVReg[uv], texNodes[i], 0);
+ }
+ else if (uvSource == kTexSourceSphereMap)
+ {
+ // m = 2*sqrt(Rx*Rx + Ry*Ry + (Rz+1)*(Rz+1))
+ // SPHR = Rx/m + 0.5, Ry/m + 0.5
+ NodePtr texGenNode;
+ hr = flg->CallFunction("", s_Linker.m_Module, "UVSphereMap", &texGenNode);
+ hr = flg->PassValue(eyeReflNode, D3D_RETURN_PARAMETER_INDEX, texGenNode, 0);
+ hr = flg->CallFunction("", s_Linker.m_Module, texFuncName.c_str(), &texNodes[i]);
+ hr = flg->PassValue(texGenNode, D3D_RETURN_PARAMETER_INDEX, texNodes[i], 0);
+ }
+ else if (uvSource == kTexSourceObject)
+ {
+ NodePtr texGenNode;
+ hr = flg->CallFunction("", s_Linker.m_Module, texFuncName.c_str(), &texNodes[i]);
+ hr = flg->PassValue(inputNode, inPosReg, texNodes[i], 0);
+ }
+ else if (uvSource == kTexSourceEyeLinear)
+ {
+ NodePtr texGenNode;
+ hr = flg->CallFunction("", s_Linker.m_Module, "Float3to4", &texGenNode);
+ hr = flg->PassValue(eyePosNode, D3D_RETURN_PARAMETER_INDEX, texGenNode, 0);
+ hr = flg->CallFunction("", s_Linker.m_Module, texFuncName.c_str(), &texNodes[i]);
+ hr = flg->PassValue(texGenNode, D3D_RETURN_PARAMETER_INDEX, texNodes[i], 0);
+ }
+ else if (uvSource == kTexSourceCubeNormal)
+ {
+ NodePtr texGenNode;
+ hr = flg->CallFunction("", s_Linker.m_Module, "Float3to4", &texGenNode);
+ hr = flg->PassValue(eyeNormalNode, D3D_RETURN_PARAMETER_INDEX, texGenNode, 0);
+ hr = flg->CallFunction("", s_Linker.m_Module, texFuncName.c_str(), &texNodes[i]);
+ hr = flg->PassValue(texGenNode, D3D_RETURN_PARAMETER_INDEX, texNodes[i], 0);
+ }
+ else if (uvSource == kTexSourceCubeReflect)
+ {
+ NodePtr texGenNode;
+ hr = flg->CallFunction("", s_Linker.m_Module, "Float3to4", &texGenNode);
+ hr = flg->PassValue(eyeReflNode, D3D_RETURN_PARAMETER_INDEX, texGenNode, 0);
+ hr = flg->CallFunction("", s_Linker.m_Module, texFuncName.c_str(), &texNodes[i]);
+ hr = flg->PassValue(texGenNode, D3D_RETURN_PARAMETER_INDEX, texNodes[i], 0);
+ }
+ else
+ {
+ AssertString("Unknown texgen mode");
+ }
+ texSources >>= 4;
+ }
+
+ // fog if we have a spare varying
+ if (state.fogMode != kFogDisabled && outFogReg != -1)
+ {
+ Assert(eyePositionRequired);
+
+ params.AddVectorParam (k11VertexFog*16, 4, kShaderVecFFFogParams);
+
+ static const char* kFogFunction[] =
+ {
+ "", // kFogDisabled
+ "FogLinear", // kFogLinear
+ "FogExp", // kFogExp
+ "FogExp2" // kFogExp2
+ };
+
+ hr = flg->CallFunction("", s_Linker.m_Module, kFogFunction[state.fogMode], &fogNode);
+ hr = flg->PassValue(eyePosNode, D3D_RETURN_PARAMETER_INDEX, fogNode, 0);
+ }
+
+ // Vertex transformation
+ matrices.mat[kShaderInstanceMatMVP].gpuIndex = k11VertexMVP*16;
+ matrices.mat[kShaderInstanceMatMVP].rows = 4;
+ matrices.mat[kShaderInstanceMatMVP].cols = 4;
+ matrices.mat[kShaderInstanceMatMVP].cbID = params.m_CBID;
+ NodePtr vertexNode;
+ hr = flg->CallFunction("", s_Linker.m_Module, "TransformVertex", &vertexNode);
+ hr = flg->PassValue(inputNode, inPosReg, vertexNode, 0);
+
+
+ NodePtr outputNode;
+ Assert(outputRegCounter <= kMaxSignatureParams);
+ hr = flg->SetOutputSignature(outputSig, outputRegCounter, &outputNode);
+
+ hr = flg->PassValue(vertexNode, D3D_RETURN_PARAMETER_INDEX, outputNode, outPosReg);
+ hr = flg->PassValue(saturatedColorNode, D3D_RETURN_PARAMETER_INDEX, outputNode, outColor0Reg);
+ if (saturatedSpecNode)
+ hr = flg->PassValue(saturatedSpecNode, D3D_RETURN_PARAMETER_INDEX, outputNode, outColor1Reg);
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ if (texNodes[i])
+ hr = flg->PassValue(texNodes[i], D3D_RETURN_PARAMETER_INDEX, outputNode, outUVReg[i]);
+ }
+ if (state.fogMode != kFogDisabled && outFogReg != -1)
+ {
+ hr = flg->PassValue(fogNode, D3D_RETURN_PARAMETER_INDEX, outputNode, outFogReg);
+ }
+
+ #if 0
+ // Print generated hlsl for debugging
+ BlobPtr hlslOutput;
+ hr = flg->GenerateHlsl(0, &hlslOutput);
+ if (SUCCEEDED(hr))
+ {
+ printf_console("DX11 debug linked VS:\n%s\n", hlslOutput->GetBufferPointer());
+ }
+ #endif
+
+
+ ModuleInstancePtr flgModule;
+ BlobPtr flgErrors;
+ hr = flg->CreateModuleInstance(&flgModule, &flgErrors);
+ if (FAILED(hr))
+ {
+ const char* errorMsg = (const char*)flgErrors->GetBufferPointer();
+ printf_console("DX11: Failed to create FF VS module: %s\n", errorMsg);
+ }
+
+ LinkerPtr linker;
+ hr = s_Linker.createLinkerFunc(&linker);
+ hr = linker->UseLibrary (s_Linker.m_ModuleInstanceVS);
+
+
+ #if UNITY_WINRT
+ const char* target = "vs_4_0_level_9_1";
+ #else
+ const char* target = "vs_4_0";
+ #endif
+
+ BlobPtr linkedCode;
+ BlobPtr linkedErrors;
+ hr = linker->Link(flgModule, "main", target, 0, &linkedCode, &linkedErrors);
+ if (FAILED(hr))
+ {
+ const char* errorMsg = (const char*)linkedErrors->GetBufferPointer();
+ printf_console("\nDX11: Failed to link FF VS: %s\n", errorMsg);
+ }
+
+ if (!linkedCode)
+ {
+ outSize = 0;
+ return NULL;
+ }
+ outSize = linkedCode->GetBufferSize();
+ void* finalCode = malloc(outSize);
+ memcpy (finalCode, linkedCode->GetBufferPointer(), outSize);
+ return finalCode;
+}
+
+
+
+// --- FRAGMENT program ----------------------------------------------------------------------------
+
+
+enum CombinerWriteMask { kCombWriteRGBA, kCombWriteRGB, kCombWriteA };
+static const char* k11LinkCombOpNames[combiner::kCombinerOpCount] = {
+ "CombReplace", "CombModulate", "CombAdd", "CombAddSigned", "CombSubtract", "CombLerp", "CombDot3", "CombDot3rgba", "CombMulAdd", "CombMulSub", "CombMulAddSigned"
+};
+static int k11LinkCompOpArgs[combiner::kCombinerOpCount] = {
+ 1, 2, 2, 2, 2, 3, 2, 2, 3, 3, 3
+};
+
+static bool EmitCombinerMath11Link (
+ int stage,
+ UInt32 combiner,
+ CombinerWriteMask writeMaskMode,
+ int texUnitCount,
+ FLGPtr& flg,
+ NodePtr& inputNode, NodePtr& prevNode, NodePtr& texNode,
+ NodePtr& outNewNode)
+{
+ Assert (texUnitCount < 10 && stage < 10);
+
+ HRESULT hr = S_OK;
+
+ combiner::Source sources[3];
+ combiner::Operand operands[3];
+ combiner::Operation op;
+ int scale;
+ combiner::DecodeTextureCombinerDescriptor (combiner, op, sources, operands, scale, true);
+
+ // dot3 and dot3rgba write into RGBA; alpha combiner is always ignored
+ if (op == combiner::kOpDot3RGB || op == combiner::kOpDot3RGBA)
+ {
+ if (writeMaskMode == kCombWriteA)
+ {
+ outNewNode.Release();
+ return false;
+ }
+ writeMaskMode = kCombWriteRGBA;
+ }
+
+ bool usedConstant = false;
+ NodePtr reg[3];
+ int regIndex[3] = {D3D_RETURN_PARAMETER_INDEX, D3D_RETURN_PARAMETER_INDEX, D3D_RETURN_PARAMETER_INDEX};
+ for (int r = 0; r < 3; ++r)
+ {
+ combiner::Source source = sources[r];
+ if (stage == 0 && source == combiner::kSrcPrevious)
+ source = combiner::kSrcPrimaryColor; // first stage, "previous" the same as "primary"
+ switch (source)
+ {
+ case combiner::kSrcPrimaryColor:
+ reg[r] = inputNode;
+ regIndex[r] = 0;
+ break;
+ case combiner::kSrcPrevious:
+ reg[r] = prevNode;
+ break;
+ case combiner::kSrcTexture:
+ reg[r] = texNode;
+ break;
+ case combiner::kSrcConstant:
+ usedConstant |= true;
+ {
+ std::string funcName = "LoadConstantColor";
+ funcName += ('0'+stage);
+ hr = flg->CallFunction("", s_Linker.m_Module, funcName.c_str(), &reg[r]);
+ }
+ break;
+ default:
+ AssertString("unknown source"); //reg[r] = "foo";
+ }
+ }
+
+ const char* regSwizzle[3];
+ for (int r = 0; r < 3; ++r)
+ {
+ regSwizzle[r] = "rgba";
+ // 1-x: into tmpN and use that
+ if (operands[r] == combiner::kOperOneMinusSrcColor || operands[r] == combiner::kOperOneMinusSrcAlpha)
+ {
+ NodePtr tmpNode;
+ hr = flg->CallFunction("", s_Linker.m_Module, "OneMinus4", &tmpNode);
+ hr = flg->PassValue(reg[r], regIndex[r], tmpNode, 0);
+ reg[r] = tmpNode;
+ regIndex[r] = D3D_RETURN_PARAMETER_INDEX;
+ }
+ // replicate alpha swizzle?
+ if (operands[r] == combiner::kOperSrcAlpha || operands[r] == combiner::kOperOneMinusSrcAlpha)
+ {
+ regSwizzle[r] = "aaaa";
+ }
+ }
+
+ // combiner op
+ NodePtr opNode;
+ hr = flg->CallFunction("", s_Linker.m_Module, k11LinkCombOpNames[op], &opNode);
+ for (int i = 0; i < k11LinkCompOpArgs[op]; ++i)
+ hr = flg->PassValueWithSwizzle(reg[i], regIndex[i], regSwizzle[i], opNode, i, "rgba");
+
+ // scale
+ if (scale > 1)
+ {
+ DebugAssert (scale == 2 || scale == 4);
+ NodePtr scaleNode;
+ hr = flg->CallFunction("", s_Linker.m_Module, scale == 2 ? "Scale2" : "Scale4", &scaleNode);
+ hr = flg->PassValue(opNode, D3D_RETURN_PARAMETER_INDEX, scaleNode, 0);
+ opNode = scaleNode;
+ }
+
+ outNewNode = opNode;
+ return usedConstant;
+}
+
+
+void* BuildFragmentShaderD3D11_Link (const FixedFunctionStateD3D11& state, FixedFunctionProgramD3D11::ValueParameters& params, size_t& outSize)
+{
+ ShaderLab::FastPropertyName cbName; cbName.SetName(kD3D11PixelCB);
+ GetD3D11ConstantBuffers(GetRealGfxDevice()).SetCBInfo (cbName.index, k11PixelSize*16);
+ params.m_CBID = cbName.index; params.m_CBSize = k11PixelSize*16;
+
+ HRESULT hr = S_OK;
+ const bool hasLinker = HasD3D11Linker();
+ Assert(hasLinker);
+
+ FLGPtr flg;
+ s_Linker.createFunctionLinkingGraphFunc (0, &flg);
+
+ LinkerPtr linker;
+ hr = s_Linker.createLinkerFunc(&linker);
+
+ // ---- Figure out input signature ----------------------------------------
+
+ D3D11_PARAMETER_DESC inputSig[kMaxSignatureParams];
+ int inputRegCounter = 0;
+ int inColorReg = 0, inSpecReg = 0, inFogReg = 0;
+ int inUVReg[kMaxSupportedTextureCoords] = {0};
+ static const char* kUVNames[kMaxSupportedTextureCoords] = {"uv0","uv1","uv2","uv3","uv4","uv5","uv6","uv7"};
+ static const char* kUVSemantics[kMaxSupportedTextureCoords] = {"TEXCOORD0","TEXCOORD1","TEXCOORD2","TEXCOORD3","TEXCOORD4","TEXCOORD5","TEXCOORD6","TEXCOORD7"};
+
+ inputSig[inColorReg = inputRegCounter++] = CreateParamDesc("icolor", "COLOR0", 4);
+ if (state.lightingEnabled && state.specularEnabled)
+ inputSig[inSpecReg = inputRegCounter++] = CreateParamDesc("ispec", "COLOR1", 3);
+ for (int i = 0; i < state.texUnitCount; i++)
+ inputSig[inUVReg[i] = inputRegCounter++] = CreateParamDesc(kUVNames[i], kUVSemantics[i], 4);
+ if (state.fogMode != kFogDisabled && inputRegCounter < 8)
+ inputSig[inFogReg = inputRegCounter++] = CreateParamDesc("ifog", "FOG0", 1);
+
+ NodePtr inputNode;
+ Assert(inputRegCounter <= kMaxSignatureParams);
+ hr = flg->SetInputSignature(inputSig, inputRegCounter, &inputNode);
+
+
+ // ---- Figure out output signature ---------------------------------------
+
+ D3D11_PARAMETER_DESC outputSig[kMaxSignatureParams];
+ int outputRegCounter = 0;
+ int outColorReg = 0;
+
+ outputSig[outColorReg = outputRegCounter++] = CreateParamDesc("ocolor", "SV_Target", 4);
+
+
+ // ---- Build code --------------------------------------------------------
+
+ NodePtr colorNode;
+
+ if (state.texUnitCount == 0)
+ {
+ // No combiners is special case: output primary color
+ flg->CallFunction("", s_Linker.m_Module, "LoadVertexColor", &colorNode);
+ flg->PassValue(inputNode, inColorReg, colorNode, 0);
+ }
+ else
+ {
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ std::string funcName = "LoadTex";
+ funcName += ('0'+i);
+
+ // type: 0 - 2d, 1 - 2d proj, 2 - 3d, 3 - cube
+ int type = 0;
+ if (state.texUnit3D & (1<<i))
+ type = 2;
+ else if (state.texUnitCube & (1<<i))
+ type = 3;
+ else if (state.texUnitProjected & (1<<i))
+ type = 1;
+
+ // Sampling modules are layed out by InternalShader/CompileShaderLib/CompileShaderLib.cpp
+ int samplingModule = 8 * type + i;
+
+ hr = linker->UseLibrary(s_Linker.m_SamplingModuleInstances[samplingModule]);
+ AssertIf(FAILED(hr));
+
+ NodePtr texNode;
+ hr = flg->CallFunction("", s_Linker.m_SamplingModules[samplingModule], funcName.c_str(), &texNode);
+ AssertIf(FAILED(hr));
+ hr = flg->PassValue(inputNode, inUVReg[i], texNode, 0);
+ AssertIf(FAILED(hr));
+
+ // emit color & alpha combiners
+ NodePtr newColorNode;
+ UInt32 colorComb = state.texUnitColorCombiner[i];
+ UInt32 alphaComb = state.texUnitAlphaCombiner[i];
+ bool usedConstant = false;
+ if (colorComb == alphaComb)
+ {
+ usedConstant |= EmitCombinerMath11Link (i, colorComb, kCombWriteRGBA, state.texUnitCount, flg, inputNode, colorNode, texNode, newColorNode);
+ }
+ else
+ {
+ usedConstant |= EmitCombinerMath11Link (i, colorComb, kCombWriteRGB, state.texUnitCount, flg, inputNode, colorNode, texNode, newColorNode);
+ NodePtr newAlphaNode;
+ usedConstant |= EmitCombinerMath11Link (i, alphaComb, kCombWriteA, state.texUnitCount, flg, inputNode, colorNode, texNode, newAlphaNode);
+ if (newAlphaNode)
+ {
+ NodePtr combinedNode;
+ flg->CallFunction("", s_Linker.m_Module, "CombineAlpha", &combinedNode);
+ flg->PassValue(newColorNode, D3D_RETURN_PARAMETER_INDEX, combinedNode, 0);
+ flg->PassValue(newAlphaNode, D3D_RETURN_PARAMETER_INDEX, combinedNode, 1);
+ newColorNode = combinedNode;
+ }
+ }
+
+ if (usedConstant)
+ params.AddVectorParam ((k11PixelColors+i)*16, 4, BuiltinShaderVectorParam(kShaderVecFFTextureEnvColor0+i));
+
+ colorNode = newColorNode;
+ }
+ }
+
+ if (state.alphaTest != kFuncDisabled && state.alphaTest != kFuncAlways)
+ {
+ params.AddVectorParam (k11PixelAlphaRef*16, 1, kShaderVecFFAlphaTestRef);
+
+ static const char* kCmpFunc[] =
+ {
+ "", // kFuncDisabled
+ "AlphaTestNever", // kFuncNever
+ "AlphaTestLess", // kFuncLess
+ "AlphaTestEqual", // kFuncEqual
+ "AlphaTestLEqual", // kFuncLEqual
+ "AlphaTestGreater", // kFuncGreater
+ "AlphaTestNotEqual", // kFuncNotEqual
+ "AlphaTestGEqual", // kFuncGEqual
+ "", // kFuncAlways
+ };
+
+ NodePtr alphaTestNode;
+ flg->CallFunction("", s_Linker.m_Module, kCmpFunc[state.alphaTest], &alphaTestNode);
+ flg->PassValue(colorNode, D3D_RETURN_PARAMETER_INDEX, alphaTestNode, 0);
+ colorNode = alphaTestNode;
+ }
+
+ // add specular
+ if (state.lightingEnabled && state.specularEnabled)
+ {
+ NodePtr specNode;
+ flg->CallFunction("", s_Linker.m_Module, "AddSpec", &specNode);
+ flg->PassValue(colorNode, D3D_RETURN_PARAMETER_INDEX, specNode, 0);
+ flg->PassValue(inputNode, inSpecReg, specNode, 1);
+ colorNode = specNode;
+ }
+
+ // fog
+ if (state.fogMode != kFogDisabled && inputRegCounter < 8)
+ {
+
+ params.AddVectorParam (k11PixelFog*16, 4, kShaderVecFFFogColor);
+
+ NodePtr fogNode;
+ flg->CallFunction("", s_Linker.m_Module, "ApplyFog", &fogNode);
+ flg->PassValue(colorNode, D3D_RETURN_PARAMETER_INDEX, fogNode, 0);
+ flg->PassValue(inputNode, inFogReg, fogNode, 1);
+ colorNode = fogNode;
+ }
+
+ // ---- final steps
+
+ NodePtr outputNode;
+ Assert(outputRegCounter <= kMaxSignatureParams);
+ hr = flg->SetOutputSignature(outputSig, outputRegCounter, &outputNode);
+
+ hr = flg->PassValue(colorNode, D3D_RETURN_PARAMETER_INDEX, outputNode, outColorReg);
+
+ #if 0
+ // Print generated hlsl for debugging
+ BlobPtr hlslOutput;
+ hr = flg->GenerateHlsl(0, &hlslOutput);
+ if (SUCCEEDED(hr))
+ {
+ printf_console("DX11 debug linked PS:\n%s\n", hlslOutput->GetBufferPointer());
+ }
+ #endif
+
+ ModuleInstancePtr flgModule;
+ BlobPtr flgErrors;
+ hr = flg->CreateModuleInstance(&flgModule, &flgErrors);
+ if (FAILED(hr))
+ {
+ const char* errorMsg = (const char*)flgErrors->GetBufferPointer();
+ printf_console("DX11: Failed to create FF PS module: %s\n", errorMsg);
+ }
+
+ hr = linker->UseLibrary (s_Linker.m_ModuleInstancePS);
+
+ #if UNITY_WINRT
+ const char* target = "ps_4_0_level_9_1";
+ #else
+ const char* target = "ps_4_0";
+ #endif
+
+ BlobPtr linkedCode;
+ BlobPtr linkedErrors;
+ hr = linker->Link(flgModule, "main", target, 0, &linkedCode, &linkedErrors);
+ if (FAILED(hr))
+ {
+ const char* errorMsg = (const char*)linkedErrors->GetBufferPointer();
+ printf_console("\nDX11: Failed to link FF PS: %s\n", errorMsg);
+ }
+
+ if (!linkedCode)
+ {
+ outSize = 0;
+ return NULL;
+ }
+ outSize = linkedCode->GetBufferSize();
+ void* finalCode = malloc(outSize);
+ memcpy (finalCode, linkedCode->GetBufferPointer(), outSize);
+ return finalCode;
+}
+
+#endif
diff --git a/Runtime/GfxDevice/d3d11/ShaderPatchingD3D11.cpp b/Runtime/GfxDevice/d3d11/ShaderPatchingD3D11.cpp
new file mode 100644
index 0000000..01d9f69
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/ShaderPatchingD3D11.cpp
@@ -0,0 +1,702 @@
+#include "UnityPrefix.h"
+#include "ShaderPatchingD3D11.h"
+#include "D3D11ByteCode.h"
+#include "External/DirectX/builds/dx9include/d3d9.h"
+
+
+#define DEBUG_D3D11_SHADER_PATCHING 0
+
+
+// Vertex & pixel shaders might have different set of interpolated registers, so hard to adaptively
+// pick register that would always fit. So instead always put fog into this register; and in case of
+// shaders already using this one, we'll just fail and there will be no fog.
+// SM4 has 16 output registers, let's use second-to-last.
+const int kFogInterpRegister = 14;
+
+
+// -------------------------------------------------------------------
+
+#if DEBUG_D3D11_SHADER_PATCHING
+struct D3D10BlobHack
+{
+ virtual HRESULT WINAPI QueryInterface(const int& iid, void** ppv) = 0;
+ virtual ULONG WINAPI AddRef() = 0;
+ virtual ULONG WINAPI Release() = 0;
+ virtual void* WINAPI GetBufferPointer() = 0;
+ virtual SIZE_T WINAPI GetBufferSize() = 0;
+};
+
+typedef HRESULT (WINAPI *D3DDisassembleFunc)(const void* pSrcData, SIZE_T SrcDataSize, UINT Flags, const char* szComments, D3D10BlobHack** ppDisassembly);
+
+static void DisassembleShader(const char* name, const void* data, size_t size)
+{
+ static D3DDisassembleFunc func = NULL;
+ if (!func)
+ {
+ HMODULE dll;
+#if !UNITY_METRO
+ dll = LoadLibraryA("d3dcompiler_43.dll");
+#else
+ dll = LoadPackagedLibrary(L"d3dcompiler_46.dll", 0); // _46.dll is ARM
+#endif
+ if (!dll)
+ {
+ printf_console("--- Failed to load d3dcompiler dll - error code: %d", GetLastError());
+ return;
+ }
+ func = (D3DDisassembleFunc)GetProcAddress(dll,"D3DDisassemble");
+ if (!func)
+ return;
+ }
+ D3D10BlobHack* blob = NULL;
+ HRESULT hr = func(data, size, 0, NULL, &blob);
+ if (FAILED(hr))
+ {
+ printf_console("Failed to disasm shader!\n");
+ }
+ if (blob)
+ {
+ std::string str((const char*)blob->GetBufferPointer(), blob->GetBufferSize());
+ printf_console("==== %s:\n%s\n", name, str.c_str());
+ blob->Release();
+ }
+}
+#endif // #if DEBUG_D3D11_SHADER_PATCHING
+
+
+
+// -------------------------------------------------------------------
+
+
+
+static DXBCChunkSig* AddFogToSignatureChunk (const DXBCChunkSig* in)
+{
+ // check for max number of registers used
+ UInt32 maxRegNum = 0;
+ for (int i = 0; i < in->count; ++i)
+ maxRegNum = std::max(maxRegNum, in->elements[i].register_num);
+ if (maxRegNum >= kFogInterpRegister)
+ return NULL;
+
+ //@TODO: check for FOG already being used?
+ const char* kFogName = "FOG";
+
+ // size = header + existing size + one more DXBCSignatureElement + name
+ unsigned size = sizeof(DXBCChunkHeader) + in->size + sizeof(DXBCSignatureElement);
+ const unsigned nameOffset = size;
+ size += strlen(kFogName)+1;
+ size = (size + 3) & ~3; // align to next dword
+
+ UInt8* buf = (UInt8*)malloc(size);
+ DXBCChunkSig* chunk = (DXBCChunkSig*)buf;
+ memset (chunk, 0xAB, size);
+ chunk->fourcc = in->fourcc;
+ chunk->size = size - 8;
+ chunk->count = in->count + 1;
+ chunk->unk8 = in->unk8;
+ // copy existing signature elements
+ memcpy (chunk->elements, in->elements, sizeof(chunk->elements[0]) * in->count);
+ // move name offsets
+ for (int i = 0; i < in->count; ++i)
+ chunk->elements[i].name_offset += sizeof(DXBCSignatureElement);
+
+ // add our signature element
+ DXBCSignatureElement& el = chunk->elements[in->count];
+ el.name_offset = nameOffset - 8;
+ el.semantic_index = 0;
+ el.system_value_type = 0;
+ el.component_type = 3; // float
+ el.register_num = kFogInterpRegister;
+ el.mask = 1;
+ el.read_write_mask = (in->fourcc == kFOURCC_ISGN) ? 1 : (~1 & 0xF);
+ el.stream = 0;
+ el.unused = 0;
+
+ // copy old names
+ const unsigned oldNamesOffset = sizeof(DXBCChunkHeader) + 8 + sizeof(DXBCSignatureElement) * in->count;
+ const unsigned newNamesOffset = oldNamesOffset + sizeof(DXBCSignatureElement);
+ memcpy (buf + newNamesOffset, ((const char*)in) + oldNamesOffset, nameOffset - newNamesOffset);
+
+ // add our name
+ memcpy (buf + nameOffset, kFogName, strlen(kFogName)+1);
+
+ return chunk;
+}
+
+
+static bool IsDclOpcode (SM4Opcode op)
+{
+ return
+ op >= kSM4Op_DCL_RESOURCE && op <= kSM4Op_DCL_GLOBAL_FLAGS ||
+ op >= kSM4Op_DCL_STREAM && op <= kSM4Op_DCL_RESOURCE_STRUCTURED ||
+ op == kSM4Op_DCL_GS_INSTANCE_COUNT;
+}
+
+
+static int FindOutputRegister(const dynamic_array<UInt32>& tokens, bool vertex)
+{
+ SM4TokInstruction instr;
+ instr.dword = 0;
+ for (size_t idx = 0; idx < tokens.size(); idx += instr.length)
+ {
+ instr.dword = tokens[idx];
+ if (vertex && instr.opcode == kSM4Op_DCL_OUTPUT_SIV && instr.length == 4)
+ {
+ UInt32 siv = tokens[idx+3];
+ if (siv == 1) // D3D10_NAME_POSITION
+ return tokens[idx+2];
+ }
+ if (!vertex && instr.opcode == kSM4Op_DCL_OUTPUT && instr.length == 3)
+ {
+ //@TODO: what happens when shader writes to both color & depth, in various orderings?
+ return tokens[idx+2];
+ }
+ }
+ return -1;
+}
+
+
+static size_t FindAfterLastDclPos(const dynamic_array<UInt32>& tokens)
+{
+ SM4TokInstruction instr;
+ instr.dword = 0;
+ size_t afterLastDclPos = 0;
+ for (size_t idx = 0; idx < tokens.size(); idx += instr.length)
+ {
+ instr.dword = tokens[idx];
+ if (IsDclOpcode((SM4Opcode)instr.opcode))
+ afterLastDclPos = idx + instr.length;
+ else
+ break;
+ }
+ return afterLastDclPos;
+}
+
+
+static int AddTempRegisters(dynamic_array<UInt32>& tokens, int tempsToAdd)
+{
+ size_t afterLastDclPos = FindAfterLastDclPos(tokens);
+
+ SM4TokInstruction instr;
+ instr.dword = 0;
+ for (size_t idx = 0; idx < afterLastDclPos; idx += instr.length)
+ {
+ instr.dword = tokens[idx];
+ if (instr.opcode == kSM4Op_DCL_TEMPS && instr.length == 2)
+ {
+ int tempsCount = tokens[idx+1];
+ tokens[idx+1] += tempsToAdd;
+ return tempsCount;
+ }
+ }
+
+ // no temps used, insert dcl_temps after all other declaration statements
+ SM4TokInstruction tok;
+ tok.dword = 0;
+ tok.opcode = kSM4Op_DCL_TEMPS;
+ tok.length = 2;
+ tokens.insert(tokens.begin()+(afterLastDclPos++), tok.dword);
+ tokens.insert(tokens.begin()+(afterLastDclPos++), tempsToAdd);
+
+ return 0;
+}
+
+
+static void AddFogRegister(dynamic_array<UInt32>& tokens, bool output)
+{
+ size_t afterLastDclPos = FindAfterLastDclPos(tokens);
+
+ // add dcl statement for fog register after all other declarations
+ SM4TokInstruction dcl;
+ dcl.dword = 0;
+ dcl.opcode = output ? kSM4Op_DCL_OUTPUT : kSM4Op_DCL_INPUT_PS;
+ if (dcl.opcode == kSM4Op_DCL_INPUT_PS)
+ dcl.dcl_input_ps.interpolation = kSM4Interp_LINEAR;
+ dcl.length = 3;
+ SM4TokOperand op;
+ op.dword = 0;
+ op.comps_enum = kSM4OperComp4;
+ op.mode = SM4_OPERAND_MODE_MASK;
+ op.sel = 1;
+ op.file = output ? kSM4File_OUTPUT : kSM4File_INPUT;
+ op.num_indices = 1;
+ op.index0_repr = SM4_OPERAND_INDEX_REPR_IMM32;
+
+ tokens.insert(tokens.begin()+(afterLastDclPos++), dcl.dword);
+ tokens.insert(tokens.begin()+(afterLastDclPos++), op.dword);
+ tokens.insert(tokens.begin()+(afterLastDclPos++), kFogInterpRegister);
+}
+
+
+static bool AddConstantBufferRegister(dynamic_array<UInt32>& tokens)
+{
+ size_t afterLastDclPos = FindAfterLastDclPos(tokens);
+
+ // are we already using the needed CB bind point?
+ SM4TokInstruction instr;
+ instr.dword = 0;
+ for (size_t idx = 0; idx < afterLastDclPos; idx += instr.length)
+ {
+ instr.dword = tokens[idx];
+ if (instr.opcode == kSM4Op_DCL_CONSTANT_BUFFER && instr.length == 4)
+ if (tokens[idx+2] == k11FogConstantBufferBind)
+ return false;
+ }
+
+ // add dcl statement for constant buffer after all other declarations
+ SM4TokInstruction dcl;
+ dcl.dword = 0;
+ dcl.opcode = kSM4Op_DCL_CONSTANT_BUFFER;
+ dcl.length = 4;
+ SM4TokOperand op;
+ op.dword = 0;
+ op.comps_enum = kSM4OperComp4;
+ op.file = kSM4File_CONSTANT_BUFFER;
+ op.num_indices = 2;
+
+ tokens.insert(tokens.begin()+(afterLastDclPos++), dcl.dword);
+ tokens.insert(tokens.begin()+(afterLastDclPos++), op.dword);
+ tokens.insert(tokens.begin()+(afterLastDclPos++), k11FogConstantBufferBind);
+ tokens.insert(tokens.begin()+(afterLastDclPos++), k11FogSize);
+
+ return true;
+}
+
+
+
+static bool SupportedIndexRepr(SM4OperIndexRepr repr)
+{
+ return repr == SM4_OPERAND_INDEX_REPR_IMM32 || repr == SM4_OPERAND_INDEX_REPR_REG_IMM32;
+}
+
+static void RemapRegisterToTemp(dynamic_array<UInt32>& tokens, SM4RegFile type, int num, int tempNum)
+{
+ SM4TokInstruction instr;
+ instr.dword = 0;
+ for (size_t idx = 0; idx < tokens.size(); idx += instr.length)
+ {
+ instr.dword = tokens[idx];
+ if (IsDclOpcode((SM4Opcode)instr.opcode) || instr.opcode == kSM4Op_CUSTOMDATA)
+ continue;
+
+ size_t idxEnd = idx + instr.length;
+
+ // skip over extended tokens
+ size_t jj = idx;
+ SM4TokInstructionEx exttok;
+ exttok.dword = instr.dword;
+ while (exttok.extended)
+ exttok.dword = tokens[++jj];
+ Assert(jj < idxEnd);
+
+ // go over operands
+ while (jj+1 < idxEnd)
+ {
+ SM4TokOperand optok;
+ optok.dword = tokens[++jj];
+ size_t opIdx = jj;
+ if (optok.extended)
+ ++jj;
+
+ // only remap simple register references: needed file, one simple index
+ if (optok.file == type && optok.num_indices == 1)
+ {
+ if (optok.index0_repr == SM4_OPERAND_INDEX_REPR_IMM32 || optok.index0_repr == SM4_OPERAND_INDEX_REPR_REG_IMM32)
+ {
+ if (tokens[jj+1] == num)
+ {
+ optok.file = kSM4File_TEMP;
+ tokens[opIdx] = optok.dword;
+ tokens[jj+1] = tempNum;
+ }
+ }
+ }
+
+ // skip over data for this operand
+ if (optok.num_indices >= 1) {
+ if (!SupportedIndexRepr((SM4OperIndexRepr)optok.index0_repr))
+ break;
+ ++jj;
+ }
+ if (optok.num_indices >= 2) {
+ if (!SupportedIndexRepr((SM4OperIndexRepr)optok.index1_repr))
+ break;
+ ++jj;
+ }
+ if (optok.num_indices >= 3) {
+ if (!SupportedIndexRepr((SM4OperIndexRepr)optok.index2_repr))
+ break;
+ ++jj;
+ }
+ if (optok.file == kSM4File_IMMEDIATE32)
+ ++jj;
+ if (optok.file == kSM4File_IMMEDIATE64)
+ jj += 2;
+ }
+ }
+}
+
+
+static void RemoveRetFromEnd (dynamic_array<UInt32>& tokens)
+{
+ if (tokens.empty())
+ return;
+
+ SM4TokInstruction instr;
+ instr.dword = tokens.back();
+ if (instr.opcode == kSM4Op_RET && instr.length == 1)
+ tokens.pop_back();
+}
+
+
+static DXBCChunkCode* AddFogToVertexCodeChunk (const DXBCChunkCode* in)
+{
+ const UInt32* inTokens = (const UInt32*)&in->version;
+
+ dynamic_array<UInt32> newTokens;
+ newTokens.insert(newTokens.end(), inTokens + 2, inTokens + in->length); // don't add version & length words
+
+ int posRegister = FindOutputRegister (newTokens, true);
+ if (posRegister < 0)
+ {
+ #if DEBUG_D3D11_SHADER_PATCHING
+ printf_console("Fog patch failed: output register not found\n");
+ #endif
+ return NULL;
+ }
+ AddFogRegister (newTokens, true);
+ int tmpRegister = AddTempRegisters (newTokens, 1);
+ RemapRegisterToTemp (newTokens, kSM4File_OUTPUT, posRegister, tmpRegister);
+ RemoveRetFromEnd (newTokens);
+
+ DXBCCodeBuilder* codeBuilder = dxb_create_code(newTokens);
+ DXBCBuilderStream bld(codeBuilder);
+ bld.op(kSM4Op_MOV).reg('o',posRegister,0xF).swz('r',tmpRegister,kSM4SwzNone); // pos.xyzw = tmp.xyzw
+ bld.op(kSM4Op_MOV).reg('o',kFogInterpRegister,0x1).swz('r',tmpRegister,kSM4SwzRepZ); // fog.x = tmp.z
+ bld.op(kSM4Op_RET);
+ dxb_destroy_code(codeBuilder);
+
+ const unsigned chunkSize = sizeof(DXBCChunkCode) + newTokens.size()*sizeof(newTokens[0]);
+ UInt8* buf = (UInt8*)malloc(chunkSize);
+ DXBCChunkCode* chunk = (DXBCChunkCode*)buf;
+ chunk->fourcc = in->fourcc;
+ chunk->size = chunkSize - sizeof(DXBCChunkHeader);
+ chunk->version = inTokens[0];
+ chunk->length = newTokens.size() + 2;
+ memcpy (buf + sizeof(DXBCChunkCode), newTokens.data(), newTokens.size()*sizeof(newTokens[0]));
+
+ return chunk;
+}
+
+
+static DXBCChunkCode* AddFogToPixelCodeChunk (const DXBCChunkCode* in, FogMode fogMode)
+{
+ const UInt32* inTokens = (const UInt32*)&in->version;
+
+ dynamic_array<UInt32> newTokens;
+ newTokens.insert(newTokens.end(), inTokens + 2, inTokens + in->length); // don't add version & length words
+
+ int colRegister = FindOutputRegister (newTokens, false);
+ if (colRegister < 0)
+ {
+ #if DEBUG_D3D11_SHADER_PATCHING
+ printf_console("Fog patch failed: output register not found\n");
+ #endif
+ return NULL;
+ }
+ AddFogRegister (newTokens, false);
+ if (!AddConstantBufferRegister (newTokens))
+ {
+ #if DEBUG_D3D11_SHADER_PATCHING
+ printf_console("Fog patch failed: can't add constant buffer\n");
+ #endif
+ return NULL;
+ }
+
+ int tmpReg = AddTempRegisters (newTokens, 2);
+ const int tmp2 = tmpReg+1;
+ const UInt32 fogParamsReg = (k11FogConstantBufferBind << 16) | k11FogParams;
+ const UInt32 fogColorReg = (k11FogConstantBufferBind << 16) | k11FogColor;
+
+ RemapRegisterToTemp (newTokens, kSM4File_OUTPUT, colRegister, tmpReg);
+ RemoveRetFromEnd (newTokens);
+
+ // add fog handling code
+ DXBCCodeBuilder* codeBuilder = dxb_create_code(newTokens);
+ DXBCBuilderStream bld(codeBuilder);
+
+ if (fogMode == kFogExp2)
+ {
+ // fog = exp(-(density*z)^2)
+ bld.op(kSM4Op_MUL).reg('r',tmp2,1).swz('c',fogParamsReg,kSM4SwzRepX).swz('v',kFogInterpRegister,kSM4SwzRepX); // tmp = (density/sqrt(ln(2))) * fog
+ bld.op(kSM4Op_MUL).reg('r',tmp2,1).swz('r',tmp2,kSM4SwzRepX).swz('r',tmp2,kSM4SwzRepX); // tmp = tmp * tmp
+ bld.op_sat(kSM4Op_EXP,tmp2).reg('r',tmp2,1).swz('r',tmp2,kSM4SwzRepX,true); // tmp = saturate (exp2 (-tmp))
+ }
+ else if (fogMode == kFogExp)
+ {
+ // fog = exp(-density*z)
+ bld.op(kSM4Op_MUL).reg('r',tmp2,1).swz('c',fogParamsReg,kSM4SwzRepX).swz('v',kFogInterpRegister,kSM4SwzRepX); // tmp = (density/sqrt(ln(2))) * fog
+ bld.op_sat(kSM4Op_EXP,tmp2).reg('r',tmp2,1).swz('r',tmp2,kSM4SwzRepX,true); // tmp = saturate (exp2 (-tmp))
+ }
+ else if (fogMode == kFogLinear)
+ {
+ // fog = (end-z)/(end-start)
+ // -> tmp = (-1/(end-start)) * fog + (end/(end-start))
+ bld.op_sat(kSM4Op_MAD,tmp2).reg('r',tmp2,1).swz('v',kFogInterpRegister,kSM4SwzRepX).swz('c',fogParamsReg,kSM4SwzRepZ).swz('c',fogParamsReg,kSM4SwzRepW);
+ }
+ else
+ {
+ AssertString("unknown fog mode");
+ }
+
+ // color.rgb = lerp (fogColor.rgb, color.rgb, fogVar) =
+ // (color.rgb-fogColor.rgb) * fogVar + fogColor.rgb
+ bld.op(kSM4Op_ADD).reg('r',tmpReg,7).swz('r',tmpReg,kSM4SwzXYZX).swz('c',fogColorReg,kSM4SwzXYZX, true);
+ bld.op(kSM4Op_MAD).reg('r',tmpReg,7).swz('r',tmpReg,kSM4SwzXYZX).swz('r',tmp2,kSM4SwzRepX).swz('c',fogColorReg,kSM4SwzXYZX);
+
+ // move into final output
+ bld.op(kSM4Op_MOV).reg('o',colRegister,0xF).swz('r',tmpReg,kSM4SwzNone); // col.xyzw = tmp.xyzw
+ bld.op(kSM4Op_RET);
+ dxb_destroy_code(codeBuilder);
+
+ const unsigned chunkSize = sizeof(DXBCChunkCode) + newTokens.size()*sizeof(newTokens[0]);
+ UInt8* buf = (UInt8*)malloc(chunkSize);
+ DXBCChunkCode* chunk = (DXBCChunkCode*)buf;
+ chunk->fourcc = in->fourcc;
+ chunk->size = chunkSize - sizeof(DXBCChunkHeader);
+ chunk->version = inTokens[0];
+ chunk->length = newTokens.size() + 2;
+ memcpy (buf + sizeof(DXBCChunkCode), newTokens.data(), newTokens.size()*sizeof(newTokens[0]));
+
+ return chunk;
+}
+
+
+// -------------------------------------------------------------------
+
+
+static bool FindShaderChunks (const DXBCContainer* dxbc, int* inputIdx, int* outputIdx, int* codeIdx)
+{
+ *inputIdx = -1;
+ *outputIdx = -1;
+ *codeIdx = -1;
+ for (int i = 0; i < dxbc->chunks.size(); ++i)
+ {
+ DXBCChunkHeader* c = dxbc->chunks[i];
+ if (c->fourcc == kFOURCC_ISGN)
+ *inputIdx = i;
+ else if (c->fourcc == kFOURCC_OSGN)
+ *outputIdx = i;
+ else if (c->fourcc == kFOURCC_SHDR || c->fourcc == kFOURCC_SHEX)
+ *codeIdx = i;
+ }
+ if (*inputIdx < 0 || *outputIdx < 0 || *codeIdx < 0)
+ return false;
+ return true;
+}
+
+
+// -------------------------------------------------------------------
+
+
+
+bool PatchVertexOrDomainShaderFogD3D11 (dynamic_array<UInt8>& byteCode)
+{
+ std::auto_ptr<DXBCContainer> dxbc (dxbc_parse (byteCode.data(), byteCode.size()));
+ if (dxbc.get() == NULL)
+ return false;
+
+ #if DEBUG_D3D11_SHADER_PATCHING
+ DisassembleShader ("VS before patching", byteCode.data(), byteCode.size());
+ #endif
+
+ int inputIdx, outputIdx, codeIdx;
+ if (!FindShaderChunks (dxbc.get(), &inputIdx, &outputIdx, &codeIdx))
+ {
+ #if DEBUG_D3D11_SHADER_PATCHING
+ printf_console("Fog patch failed: shader chunks not found?\n");
+ #endif
+ return false;
+ }
+ const DXBCChunkSig* inputChunk = (DXBCChunkSig*)dxbc->chunks[inputIdx];
+ const DXBCChunkSig* outputChunk = (DXBCChunkSig*)dxbc->chunks[outputIdx];
+ const DXBCChunkCode* codeChunk = (DXBCChunkCode*)dxbc->chunks[codeIdx];
+
+ DXBCChunkSig* newOutputChunk = AddFogToSignatureChunk (outputChunk);
+ if (!newOutputChunk)
+ {
+ #if DEBUG_D3D11_SHADER_PATCHING
+ printf_console("Fog patch failed: can't add fog output signature\n");
+ #endif
+ return false;
+ }
+
+ DXBCChunkCode* newCodeChunk = AddFogToVertexCodeChunk (codeChunk);
+ if (!newCodeChunk)
+ {
+ free (newOutputChunk);
+ return false;
+ }
+
+ dxbc->chunks[outputIdx] = newOutputChunk;
+ dxbc->chunks[codeIdx] = newCodeChunk;
+ dynamic_array<UInt8> newByteCode = byteCode;
+ dxbc_create(dxbc->chunks.data(), dxbc->chunks.size(), newByteCode);
+ byteCode = newByteCode;
+
+ #if DEBUG_D3D11_SHADER_PATCHING
+ DisassembleShader ("VS after patching", byteCode.data(), byteCode.size());
+ #endif
+
+ free (newOutputChunk);
+ free (newCodeChunk);
+
+ return true;
+}
+
+
+// -------------------------------------------------------------------
+
+
+bool PatchPixelShaderFogD3D11 (dynamic_array<UInt8>& byteCode, FogMode fog)
+{
+ std::auto_ptr<DXBCContainer> dxbc (dxbc_parse (byteCode.data(), byteCode.size()));
+ if (dxbc.get() == NULL)
+ return false;
+
+ #if DEBUG_D3D11_SHADER_PATCHING
+ printf_console("Patching fog mode %d\n", fog);
+ DisassembleShader ("PS before patching", byteCode.data(), byteCode.size());
+ #endif
+
+ int inputIdx, outputIdx, codeIdx;
+ if (!FindShaderChunks (dxbc.get(), &inputIdx, &outputIdx, &codeIdx))
+ {
+ #if DEBUG_D3D11_SHADER_PATCHING
+ printf_console("Fog patch failed: shader chunks not found?\n");
+ #endif
+ return false;
+ }
+ const DXBCChunkSig* inputChunk = (DXBCChunkSig*)dxbc->chunks[inputIdx];
+ const DXBCChunkSig* outputChunk = (DXBCChunkSig*)dxbc->chunks[outputIdx];
+ const DXBCChunkCode* codeChunk = (DXBCChunkCode*)dxbc->chunks[codeIdx];
+
+ DXBCChunkSig* newInputChunk = AddFogToSignatureChunk (inputChunk);
+ if (!newInputChunk)
+ {
+ #if DEBUG_D3D11_SHADER_PATCHING
+ printf_console("Fog patch failed: can't add fog input signature\n");
+ #endif
+ return false;
+ }
+
+ DXBCChunkCode* newCodeChunk = AddFogToPixelCodeChunk (codeChunk, fog);
+ if (!newCodeChunk)
+ {
+ free (newInputChunk);
+ return false;
+ }
+
+ dxbc->chunks[inputIdx] = newInputChunk;
+ dxbc->chunks[codeIdx] = newCodeChunk;
+
+ dynamic_array<UInt8> newByteCode = byteCode;
+ dxbc_create(dxbc->chunks.data(), dxbc->chunks.size(), newByteCode);
+ byteCode = newByteCode;
+
+ #if DEBUG_D3D11_SHADER_PATCHING
+ DisassembleShader ("PS after patching", byteCode.data(), byteCode.size());
+ #endif
+
+ free (newInputChunk);
+ free (newCodeChunk);
+
+ return true;
+}
+
+
+
+bool PatchRemovePartialPrecisionD3D11 (dynamic_array<UInt8>& byteCode)
+{
+ DXBCChunkSM20* codeChunk = static_cast<DXBCChunkSM20*>(dxbc_find_chunk(byteCode.data(), byteCode.size(), kFOURCC_SM20));
+ if (!codeChunk)
+ return false;
+
+ // Reference for the bytecode format: "Direct3D Shader Codes" on MSDN,
+ // http://msdn.microsoft.com/en-us/library/windows/hardware/ff552891(v=vs.85).aspx
+
+ #if DEBUG_D3D11_SHADER_PATCHING
+ dynamic_array<UInt8> origBC = byteCode;
+ #endif
+
+ const UInt32* codeEnd = (const UInt32*)(((const UInt8*)codeChunk) + sizeof(DXBCChunkHeader) + codeChunk->size);
+
+ // Go past chunk header into chunk content
+ UInt32* codePtr = (UInt32*)(codeChunk+1);
+ // Table, ints: version, shader size, code offset
+ if (codePtr+2 >= codeEnd)
+ return false;
+ const UInt32 codeOffset = codePtr[2];
+
+ // Get to actual byte code start.
+ // Code offset in the table is from end of DXBCChunkHeader
+ codePtr = (UInt32*)(((UInt8*)codeChunk) + sizeof(DXBCChunkHeader) + codeOffset);
+ codePtr++; // skip version token
+
+ // Go over all instructions
+ bool didChanges = false;
+ while (codePtr < codeEnd)
+ {
+ SM2TokInstruction* insn = (SM2TokInstruction*)codePtr;
+ const int length = insn->length;
+ const SM2Opcode op = (SM2Opcode)insn->opcode;
+ if (op == kSM2Op_DCL)
+ {
+ // DCL instructions have special format: destination token is the 2nd token
+ if (length == 2)
+ {
+ SM2TokDst* dst = (SM2TokDst*)(codePtr+2);
+ if (dst->res_mod & (D3DSPDM_PARTIALPRECISION>>D3DSP_DSTMOD_SHIFT))
+ {
+ dst->res_mod &= ~(D3DSPDM_PARTIALPRECISION>>D3DSP_DSTMOD_SHIFT);
+ didChanges = true;
+ }
+ }
+ }
+ else if (op == kSM2Op_DEF || op == kSM2Op_DEFI || op == kSM2Op_DEFB)
+ {
+ // DEF* instructions have special format, don't have to do anything
+ }
+ else if (op == kSM2Op_CALLNZ || op == D3DSIO_LOOP || op == kSM2Op_IFC || op == kSM2Op_BREAKP || op == kSM2Op_BREAKC)
+ {
+ // these instructions don't have destination token
+ }
+ else if (length > 1) // must be a more than 2 argument instruction to have a destination token
+ {
+ SM2TokDst* dst = (SM2TokDst*)(codePtr+1);
+ if (dst->res_mod & (D3DSPDM_PARTIALPRECISION>>D3DSP_DSTMOD_SHIFT))
+ {
+ dst->res_mod &= ~(D3DSPDM_PARTIALPRECISION>>D3DSP_DSTMOD_SHIFT);
+ didChanges = true;
+ }
+ }
+ codePtr += length + 1;
+ }
+
+ // Update shader checksum if we did changes
+ if (didChanges && byteCode.size() > 20)
+ {
+ void D3DHash (const unsigned char* data, unsigned size, unsigned char res[16]);
+ UInt8 hsh[16];
+ D3DHash (&byteCode[20], byteCode.size()-20, &byteCode[4]);
+
+ #if DEBUG_D3D11_SHADER_PATCHING
+ DisassembleShader ("Shader before partial precision remove", origBC.data(), origBC.size());
+ DisassembleShader ("Shader after partial precision remove", byteCode.data(), byteCode.size());
+ #endif
+ }
+
+
+ return true;
+}
+
diff --git a/Runtime/GfxDevice/d3d11/ShaderPatchingD3D11.h b/Runtime/GfxDevice/d3d11/ShaderPatchingD3D11.h
new file mode 100644
index 0000000..51b2854
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/ShaderPatchingD3D11.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+enum {
+ k11FogColor = 0,
+ k11FogParams = 1,
+ k11FogSize = 2
+};
+
+enum {
+ // DX11 has 14 constant buffers, and for safety reasons
+ // let's use 3rd to last one, 11. If that is already
+ // in use by shader, there will be no fog.
+ k11FogConstantBufferBind = 11,
+};
+
+bool PatchVertexOrDomainShaderFogD3D11 (dynamic_array<UInt8>& byteCode);
+bool PatchPixelShaderFogD3D11 (dynamic_array<UInt8>& byteCode, FogMode fog);
+
+bool PatchRemovePartialPrecisionD3D11 (dynamic_array<UInt8>& byteCode);
+
+bool PatchRemovePartialPrecisionD3D11 (dynamic_array<UInt8>& byteCode);
diff --git a/Runtime/GfxDevice/d3d11/StreamOutSkinnedMesh.cpp b/Runtime/GfxDevice/d3d11/StreamOutSkinnedMesh.cpp
new file mode 100644
index 0000000..b1f3bd6
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/StreamOutSkinnedMesh.cpp
@@ -0,0 +1,416 @@
+#include "UnityPrefix.h"
+#include "Runtime/GfxDevice/d3d11/GfxDeviceD3D11.h"
+#include "Runtime/GfxDevice/d3d11/StreamOutSkinnedMesh.h"
+#include "Runtime/GfxDevice/d3d11/D3D11Context.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/GfxDevice/d3d11/InternalShaders/builtin.h"
+#include "Runtime/GfxDevice/d3d11/D3D11VBO.h"
+#include "Runtime/Threads/ThreadedStreamBuffer.h"
+#include "Runtime/GfxDevice/threaded/ThreadedDeviceStates.h"
+#include "Runtime/GfxDevice/threaded/ThreadedVBO.h"
+
+struct ShaderPair
+{
+ ID3D11GeometryShader* m_GeometryShader;
+ ID3D11VertexShader* m_VertexShader;
+ ID3D11InputLayout* m_InputLayout;
+};
+
+typedef UInt32 InputSpec; // shaderChannelsMap + (bonesPerVertex << 16) + (maxBonesBits << 19)
+
+typedef std::map< InputSpec, ShaderPair> StreamOutShaderMap;
+
+struct SkinningGlobalsD3D11
+{
+ SkinningGlobalsD3D11()
+ : m_BoundSP(0)
+ , m_OldGS(0)
+ , m_OldPS(0)
+ , m_OldVS(0)
+ , m_OldTopo(D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED)
+ , m_DSState(0)
+ , m_OldCB(0)
+ {
+ }
+
+ StreamOutShaderMap m_ShaderMap;
+ ShaderPair* m_BoundSP;
+ ID3D11GeometryShader* m_OldGS;
+ ID3D11PixelShader* m_OldPS;
+ ID3D11VertexShader* m_OldVS;
+ D3D11_PRIMITIVE_TOPOLOGY m_OldTopo;
+ ID3D11DepthStencilState* m_DSState;
+ ID3D11Buffer* m_OldCB;
+};
+static SkinningGlobalsD3D11 s_SkinningGlobals;
+
+enum MemExShaderChannel
+{
+ kMEXC_Position = VERTEX_FORMAT1(Vertex),
+ kMEXC_Normal = VERTEX_FORMAT1(Normal),
+ kMEXC_Tangent = VERTEX_FORMAT1(Tangent),
+};
+
+void StreamOutSkinningInfo::CleanUp()
+{
+ StreamOutShaderMap::iterator it;
+ for (it = s_SkinningGlobals.m_ShaderMap.begin(); it != s_SkinningGlobals.m_ShaderMap.end(); ++it)
+ {
+ if (it->second.m_GeometryShader)
+ {
+ ULONG refCount = it->second.m_GeometryShader->Release();
+ AssertIf(refCount != 0);
+ }
+ if (it->second.m_VertexShader)
+ {
+ ULONG refCount = it->second.m_VertexShader->Release();
+ AssertIf(refCount != 0);
+ }
+ }
+ s_SkinningGlobals.m_ShaderMap.clear();
+
+ s_SkinningGlobals.m_BoundSP = NULL;
+ SAFE_RELEASE(s_SkinningGlobals.m_OldVS);
+ SAFE_RELEASE(s_SkinningGlobals.m_OldGS);
+ SAFE_RELEASE(s_SkinningGlobals.m_OldPS);
+ SAFE_RELEASE(s_SkinningGlobals.m_OldCB);
+ s_SkinningGlobals.m_OldTopo = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
+
+ SAFE_RELEASE(s_SkinningGlobals.m_DSState);
+}
+
+static UInt32 roundUpToNextPowerOf2(UInt32 in)
+{
+ // Round up to nearest power of 2
+ // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
+ in--;
+ in |= in >> 1;
+ in |= in >> 2;
+ in |= in >> 4;
+ in |= in >> 8;
+ in |= in >> 16;
+ in++;
+ return in;
+}
+// Get the bones bit index based on bone count. Assumes bonecount is power of 2
+static int getBonesBits(UInt32 boneCount)
+{
+ // Calculate ln2
+ // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn
+
+ static const int MultiplyDeBruijnBitPosition2[32] =
+ {
+ 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
+ 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
+ };
+ UInt32 res = MultiplyDeBruijnBitPosition2[(UInt32)(boneCount * 0x077CB531U) >> 27];
+
+ if(res < 5) // Minimum size is 32 (= 0)
+ return 0;
+
+ return res-5; // Adjust so that 32 = 0, 64 = 1 etc.
+}
+
+
+// maxBonesBits == Max bone count: 0 = 32, 1 = 64, etc until 5 = 1024
+
+static const BYTE* GetMemExShaderCode(UInt32 shaderChannelsMap, int bonesPerVertex, UInt32 maxBonesBits, UInt32& bytecodeSize)
+{
+ int channelIdx = 0;
+ switch(shaderChannelsMap)
+ {
+ case kMEXC_Position: channelIdx = 0; break;
+ case kMEXC_Position|kMEXC_Normal: channelIdx = 1; break;
+ case kMEXC_Position|kMEXC_Normal|kMEXC_Tangent: channelIdx = 2; break;
+ case kMEXC_Position|kMEXC_Tangent: channelIdx = 3; break;
+ default:
+ Assert(0 && "Unsupported vertex format for GPU skinning.");
+ bytecodeSize = 0;
+ return 0;
+ };
+
+ if(maxBonesBits > 5)
+ maxBonesBits = 5; // TODO: Alert of too many bones per vertex
+
+ // Adjust bonespervertex to dense array (1, 2, 4 => 0, 1, 2)
+ if(bonesPerVertex == 4)
+ bonesPerVertex--;
+ bonesPerVertex--;
+
+ bytecodeSize = g_StreamOutShaderSizes[channelIdx][maxBonesBits][bonesPerVertex];
+ return g_StreamOutShaders[channelIdx][maxBonesBits][bonesPerVertex];
+}
+
+static ShaderPair* GetMemExShader(UInt32 shaderChannelsMap, UInt32 bonesPerVertex, UInt32 maxBonesBits)
+{
+ // Already have vertex shader for this format?
+ InputSpec is = shaderChannelsMap + (bonesPerVertex << 16) + (maxBonesBits << 19);
+
+ StreamOutShaderMap::iterator it = s_SkinningGlobals.m_ShaderMap.find(is);
+ if (it != s_SkinningGlobals.m_ShaderMap.end())
+ return &it->second;
+
+ UInt32 codeSize = 0;
+ const BYTE* code = GetMemExShaderCode(shaderChannelsMap, bonesPerVertex, maxBonesBits, codeSize);
+ if (!code)
+ return NULL;
+
+ D3D11_SO_DECLARATION_ENTRY entries[16] = {0};
+ D3D11_SO_DECLARATION_ENTRY* entry = entries;
+
+
+ entry->SemanticName = "POSITION";
+ entry->SemanticIndex = 0;
+ entry->StartComponent = 0;
+ entry->ComponentCount = 3;
+ entry->OutputSlot = 0;
+ entry++;
+
+ UINT soStride = sizeof(float) * 3;
+ UInt32 semanticIndex = 0;
+
+ if(shaderChannelsMap & kMEXC_Normal)
+ {
+ entry->SemanticName = "TEXCOORD";
+ entry->SemanticIndex = semanticIndex++;
+ entry->StartComponent = 0;
+ entry->ComponentCount = 3;
+ entry->OutputSlot = 0;
+ entry++;
+
+ soStride += 3 * sizeof(float);
+ }
+
+ if(shaderChannelsMap & kMEXC_Tangent)
+ {
+ entry->SemanticName = "TEXCOORD";
+ entry->SemanticIndex = semanticIndex++;
+ entry->StartComponent = 0;
+ entry->ComponentCount = 4;
+ entry->OutputSlot = 0;
+ entry++;
+
+ soStride += 4 * sizeof(float);
+ }
+
+ ShaderPair sp;
+ GetD3D11Device()->CreateVertexShader(
+ code,
+ codeSize,
+ 0, &sp.m_VertexShader);
+
+ const DX11FeatureLevel fl = gGraphicsCaps.d3d11.featureLevel;
+
+ GetD3D11Device()->CreateGeometryShaderWithStreamOutput(
+ code,
+ codeSize,
+ entries,
+ (entry-entries),
+ &soStride,
+ 1,
+ (fl >= kDX11Level11_0) ? D3D11_SO_NO_RASTERIZED_STREAM : 0,
+ 0,
+ &sp.m_GeometryShader);
+
+ sp.m_InputLayout = GetD3D11GfxDevice().GetVertexDecls().GetVertexDecl(shaderChannelsMap, (void*)code, codeSize, true, bonesPerVertex);
+
+ s_SkinningGlobals.m_ShaderMap.insert(std::make_pair(is, sp));
+
+ return &s_SkinningGlobals.m_ShaderMap.find( is )->second;
+}
+
+// Note: we might not support all formats all the time.
+static bool DoesVertexFormatQualifyForMemExport(UInt32 shaderChannelsMap)
+{
+ // 1. Channel filter
+ UInt32 unsupported = ~(kMEXC_Position | kMEXC_Normal | kMEXC_Tangent); // TODO: support more channels
+ //UInt32 unsupported = ~(kMEXC_Position | kMEXC_Normal | kMEXC_Color | kMEXC_UV0 | kMEXC_UV1 | kMEXC_Tangent);
+ bool qualify = ((shaderChannelsMap & unsupported) == 0);
+ // 3. Must have position
+ qualify &= (shaderChannelsMap & kMEXC_Position) != 0;
+
+ return qualify;
+
+}
+
+void StreamOutSkinningInfo::SkinMesh(bool last)
+{
+// Assert(DoesVertexFormatQualifyForMemExport(info.channelMap));
+ if (!DoesVertexFormatQualifyForMemExport(GetChannelMap()))
+ return;
+
+ // Get the vertex shader
+ //...
+ ShaderPair* sp = GetMemExShader(GetChannelMap(), GetBonesPerVertex(), getBonesBits(m_BoneCount));
+
+ UINT offsets[] = {0};
+ D3D11VBO* vbo = (D3D11VBO*)GetDestVBO();
+ vbo->BindToStreamOutput();
+
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ if (s_SkinningGlobals.m_BoundSP == 0)
+ {
+ ctx->VSGetShader( &s_SkinningGlobals.m_OldVS, 0, 0 );
+ ctx->GSGetShader( &s_SkinningGlobals.m_OldGS, 0, 0 );
+ ctx->PSGetShader( &s_SkinningGlobals.m_OldPS, 0, 0 );
+ ctx->IAGetPrimitiveTopology(&s_SkinningGlobals.m_OldTopo);
+ ctx->VSGetConstantBuffers(0, 1, &s_SkinningGlobals.m_OldCB);
+ }
+
+ if (s_SkinningGlobals.m_BoundSP != sp)
+ {
+ ctx->VSSetShader( sp->m_VertexShader, 0, 0 );
+ ctx->GSSetShader( sp->m_GeometryShader, 0, 0 );
+ ctx->PSSetShader( 0, 0, 0 );
+ s_SkinningGlobals.m_BoundSP = sp;
+ }
+ ID3D11Buffer* const cbs[] = { m_SourceBones };
+ ctx->VSSetConstantBuffers(0, 1, cbs);
+
+ UInt32 skinStride = 4; // For 1 bone, just the index
+ if(GetBonesPerVertex() == 2)
+ skinStride *= 4; // 2 indices, 2 weights
+ else if(GetBonesPerVertex() == 4)
+ skinStride *= 8;
+
+ ID3D11Buffer* const vsStreams[] = { m_SourceVBO, m_SourceSkin };
+ const UINT streamStrides[]={ GetStride(), skinStride };
+ const UINT streamOffsets[] = { 0,0 };
+
+ ctx->IASetVertexBuffers(0, 2, vsStreams, streamStrides, streamOffsets);
+
+ // Call our own wrapper for DX11 input layout that does redundant layout change
+ // tracking.
+ extern void SetInputLayoutD3D11 (ID3D11DeviceContext* ctx, ID3D11InputLayout* layout);
+ SetInputLayoutD3D11(ctx, sp->m_InputLayout);
+
+ if (s_SkinningGlobals.m_DSState == 0)
+ {
+ D3D11_DEPTH_STENCIL_DESC dsstatedesc = CD3D11_DEPTH_STENCIL_DESC(CD3D11_DEFAULT());
+ dsstatedesc.DepthEnable = FALSE;
+ dsstatedesc.StencilEnable = FALSE;
+ GetD3D11Device()->CreateDepthStencilState(&dsstatedesc, &s_SkinningGlobals.m_DSState);
+ }
+
+ ID3D11DepthStencilState* bdsstate = 0;
+ UINT bref;
+ ctx->OMGetDepthStencilState(&bdsstate, &bref);
+ ctx->OMSetDepthStencilState(s_SkinningGlobals.m_DSState, 0);
+
+ ctx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_POINTLIST );
+ ctx->Draw(GetVertexCount(), 0);
+
+ ctx->OMSetDepthStencilState(bdsstate, bref);
+ SAFE_RELEASE(bdsstate);
+
+ if ( last )
+ {
+ ctx->VSSetShader( s_SkinningGlobals.m_OldVS, 0, 0 );
+ ctx->GSSetShader( s_SkinningGlobals.m_OldGS, 0, 0 );
+ ctx->PSSetShader( s_SkinningGlobals.m_OldPS, 0, 0 );
+ ctx->IASetPrimitiveTopology(s_SkinningGlobals.m_OldTopo);
+ ctx->VSSetConstantBuffers( 0, 1, &s_SkinningGlobals.m_OldCB );
+ SAFE_RELEASE(s_SkinningGlobals.m_OldVS);
+ SAFE_RELEASE(s_SkinningGlobals.m_OldGS);
+ SAFE_RELEASE(s_SkinningGlobals.m_OldPS);
+ SAFE_RELEASE(s_SkinningGlobals.m_OldCB);
+ s_SkinningGlobals.m_BoundSP = 0;
+ }
+ vbo->UnbindFromStreamOutput();
+ //TODO
+}
+
+// ----------------------------------------------------------------------------
+static ID3D11Buffer* UpdateStaticVBO(ID3D11Buffer* vbo, const size_t size, const void* data, bool dirty)
+{
+ D3D11_BUFFER_DESC desc = CD3D11_BUFFER_DESC(size, D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE);
+
+ if (!vbo)
+ {
+ GetD3D11Device()->CreateBuffer(&desc, 0, &vbo);
+ dirty = true;
+ }
+ else
+ {
+ D3D11_BUFFER_DESC curDesc;
+ vbo->GetDesc(&curDesc);
+ if (curDesc.ByteWidth != size)
+ {
+ vbo->Release();
+ GetD3D11Device()->CreateBuffer(&desc, 0, &vbo);
+ dirty = true;
+ }
+ }
+
+ if (dirty)
+ {
+ D3D11_MAPPED_SUBRESOURCE map;
+ GetD3D11Context()->Map(vbo, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
+ memcpy(map.pData, data, size);
+ GetD3D11Context()->Unmap(vbo, 0);
+ }
+
+ return vbo;
+}
+
+StreamOutSkinningInfo::~StreamOutSkinningInfo()
+{
+ if(m_SourceSkin)
+ m_SourceSkin->Release();
+ if(m_SourceVBO)
+ m_SourceVBO->Release();
+ if(m_SourceBones)
+ m_SourceBones->Release();
+}
+
+void StreamOutSkinningInfo::UpdateSourceData(const void *vertData, const BoneInfluence *skinData, bool dirty)
+{
+ // If only one bone, it's just one int per vertex
+ UINT32 skinSize = GetVertexCount() * sizeof(UInt32);
+ if(GetBonesPerVertex() == 2)
+ skinSize *= 4; // 2 weights, 2 indices
+ else if(GetBonesPerVertex() == 4)
+ skinSize *= 8; // 4 weights, 4 indices
+
+ m_SourceVBO = UpdateStaticVBO(m_SourceVBO, GetVertexCount() * GetStride(), vertData, dirty);
+ m_SourceSkin = UpdateStaticVBO(m_SourceSkin, skinSize, skinData, dirty);
+}
+
+
+void StreamOutSkinningInfo::UpdateSourceBones(const int boneCount, const Matrix4x4f* cachedPose)
+{
+ Assert(boneCount > 0);
+
+ UInt32 uBoneCount = std::min(1024, boneCount);
+ uBoneCount = std::max(32, boneCount); // Clamp to 32 - 1024, as that's what our shaders expect.
+
+ if (m_BoneCount < roundUpToNextPowerOf2(uBoneCount) && m_SourceBones)
+ {
+ // Bone count changed, resize the bone constant buffer
+ m_SourceBones->Release();
+ m_SourceBones = NULL;
+ }
+ m_BoneCount = roundUpToNextPowerOf2(uBoneCount);
+
+ if (!m_SourceBones)
+ {
+ const UInt32 bufSize = m_BoneCount * 48; // Must match the max bone count in internalshaders.hlsl
+ D3D11_BUFFER_DESC desc = CD3D11_BUFFER_DESC(bufSize, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE);
+ GetD3D11Device()->CreateBuffer(&desc, 0, &m_SourceBones);
+ }
+
+ D3D11_MAPPED_SUBRESOURCE map;
+ GetD3D11Context()->Map(m_SourceBones, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
+ UInt8* dest = (UInt8*)map.pData;
+
+ for (int i = 0; i < uBoneCount; ++i)
+ {
+ Matrix4x4f mat = cachedPose[i];
+ mat.Transpose();
+ memcpy(dest + i * 48, &mat, 48);
+ }
+
+ GetD3D11Context()->Unmap(m_SourceBones, 0);
+}
+
diff --git a/Runtime/GfxDevice/d3d11/StreamOutSkinnedMesh.h b/Runtime/GfxDevice/d3d11/StreamOutSkinnedMesh.h
new file mode 100644
index 0000000..7d95232
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/StreamOutSkinnedMesh.h
@@ -0,0 +1,42 @@
+#ifndef __STREAMOUTSKINNEDMESH_H__
+#define __STREAMOUTSKINNEDMESH_H__
+
+#include "External/DirectX/builds/dx11include/d3d11.h"
+#include "Runtime/GfxDevice/GPUSkinningInfo.h"
+class VBO;
+class Matrix4x4f;
+class ThreadedStreamBuffer;
+class GfxDeviceD3D11;
+
+// MemExport mesh skinning data.
+class StreamOutSkinningInfo : public GPUSkinningInfo
+{
+ friend class GfxDeviceD3D11;
+private:
+ ID3D11Buffer* m_SourceVBO;
+ ID3D11Buffer* m_SourceSkin;
+ ID3D11Buffer* m_SourceBones;
+
+ //! Stores the bone count from the previous call to UpdateSourceBones. Used to select the most suitable shader version.
+ int m_BoneCount;
+
+ // Private constructor and destructor , called from GfxDeviceD3D11
+ StreamOutSkinningInfo() : GPUSkinningInfo(), m_SourceVBO(NULL), m_SourceSkin(NULL), m_SourceBones(NULL) {}
+ virtual ~StreamOutSkinningInfo();
+
+
+ // Actual implementation methods, called from GfxDeviceD3D11
+
+ void UpdateSourceData(const void *vertData, const BoneInfluence *skinData, bool dirty);
+ void UpdateSourceBones(const int boneCount, const Matrix4x4f* cachedPose);
+ void SkinMesh(bool last);
+
+public:
+
+ //! Clean up created shaders
+ static void CleanUp();
+
+};
+
+
+#endif
diff --git a/Runtime/GfxDevice/d3d11/TexturesD3D11.cpp b/Runtime/GfxDevice/d3d11/TexturesD3D11.cpp
new file mode 100644
index 0000000..12f61b0
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/TexturesD3D11.cpp
@@ -0,0 +1,1067 @@
+#include "UnityPrefix.h"
+#include "TexturesD3D11.h"
+#include "D3D11Context.h"
+#include "D3D11Utils.h"
+#include "Runtime/Allocator/FixedSizeAllocator.h"
+#include "Runtime/Graphics/TextureFormat.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/GfxDevice/TextureUploadUtils.h"
+#include "Runtime/GfxDevice/TextureIdMap.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "External/ProphecySDK/include/prcore/Surface.hpp"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+
+
+//#define ENABLE_OLD_TEXTURE_UPLOAD UNITY_WINRT
+#define ENABLE_OLD_TEXTURE_UPLOAD 0
+
+typedef FixedSizeAllocator<sizeof(TexturesD3D11::D3D11Texture)> TextureAllocator;
+static TextureAllocator* _TextureAlloc = NULL;
+
+namespace TextureD3D11Alloc
+{
+ void StaticInitialize()
+ {
+ _TextureAlloc = UNITY_NEW_AS_ROOT(TextureAllocator(kMemGfxDevice),kMemGfxDevice, "TextureStructs", "");
+ }
+
+ void StaticDestroy()
+ {
+ UNITY_DELETE(_TextureAlloc, kMemGfxDevice);
+ }
+}
+
+static RegisterRuntimeInitializeAndCleanup s_TextureAllocManagerCallbacks(TextureD3D11Alloc::StaticInitialize, TextureD3D11Alloc::StaticDestroy);
+
+static inline intptr_t AllocD3DTexture(ID3D11Resource* tex, ID3D11ShaderResourceView* srv, ID3D11UnorderedAccessView* uav, bool shadowMap)
+{
+ return (intptr_t)(new (_TextureAlloc->alloc()) TexturesD3D11::D3D11Texture(tex, srv, uav, shadowMap));
+}
+
+static inline TexturesD3D11::D3D11Texture* QueryD3DTexture(TextureID textureID)
+{
+ return (TexturesD3D11::D3D11Texture*)TextureIdMap::QueryNativeTexture(textureID);
+}
+
+
+static void SwizzleToRGBA (const UInt8* src, UInt8* dst, int width, int height, int dstPitch, TextureFormat format)
+{
+ if (format == kTexFormatAlphaLum16)
+ {
+ // Handle AlphaLum16 case. ProphecySDK does not support 16 bit/channel formats,
+ // so we blit manually.
+ UInt32 rowBytes = GetRowBytesFromWidthAndFormat(width,kTexFormatAlphaLum16);
+ const UInt8* srcRowData = src;
+ UInt8* destRowData = dst;
+ for( int r = 0; r < height; ++r )
+ {
+ for( int c = 0; c < width; ++c )
+ {
+ DWORD val = srcRowData[c*2+1];
+ ((DWORD*)destRowData)[c] = 0xFF000000 | (val<<16) | (val<<8) | (val);
+ }
+ srcRowData += rowBytes;
+ destRowData += dstPitch;
+ }
+ }
+ else
+ {
+ prcore::Surface srcSurface (width, height, GetRowBytesFromWidthAndFormat(width,format), GetProphecyPixelFormat(format), (void*)src);
+ prcore::Surface dstSurface (width, height, dstPitch, prcore::PixelFormat(32,0x000000ff,0x0000ff00,0x00ff0000,0xff000000), dst);
+ dstSurface.BlitImage (srcSurface, prcore::Surface::BLIT_COPY);
+ }
+}
+
+
+struct FormatDesc11 {
+ TextureFormat unityformat;
+ DXGI_FORMAT d3dformat;
+ DXGI_FORMAT sRGBD3dformat;
+};
+
+const static FormatDesc11 kTextureFormatTable[kTexFormatPCCount+1] =
+{
+ { kTexFormatPCCount, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN },
+ { kTexFormatAlpha8, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM }, // Alpha8
+ { kTexFormatARGB4444, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB }, // ARGB4444
+ { kTexFormatRGB24, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB }, // RGB24
+ { kTexFormatRGBA32, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB }, // RGBA32
+ { kTexFormatARGB32, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB }, // ARGB32
+ { kTexFormatARGBFloat, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN }, // ARGBFloat
+ { kTexFormatRGB565, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB }, // RGB565
+ { kTexFormatBGR24, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB }, // BGR24
+ { kTexFormatAlphaLum16, DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_R16_UNORM }, // AlphaLum16
+ { kTexFormatDXT1, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC1_UNORM_SRGB }, // DXT1
+ { kTexFormatDXT3, DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_BC2_UNORM_SRGB }, // DXT3
+ { kTexFormatDXT5, DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_BC3_UNORM_SRGB }, // DXT5
+ { kTexFormatRGBA4444, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB }, // RGBA4444
+};
+
+
+const static FormatDesc11 kTextureFormatA8Workaround =
+ { kTexFormatAlpha8, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM };
+const static FormatDesc11 kTextureFormatR16Workaround =
+ { kTexFormatAlphaLum16, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM };
+const static FormatDesc11 kTextureFormatBGRA32Workaround =
+ { kTexFormatBGRA32, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM };
+
+
+static const FormatDesc11& GetUploadFormat (TextureFormat inFormat)
+{
+ // 9.1 doesn't support A8 at all; other 9.x levels only support without mipmaps.
+ // To simplify things we just always expand to R8G8B8A8 on 9.x.
+ if (gGraphicsCaps.d3d11.featureLevel < kDX11Level10_0 && inFormat == kTexFormatAlpha8)
+ return kTextureFormatA8Workaround;
+
+ // 9.1 doesn't support R16 for AlphaLum16, so expand
+ if (gGraphicsCaps.d3d11.featureLevel <= kDX11Level9_1 && inFormat == kTexFormatAlphaLum16)
+ return kTextureFormatR16Workaround;
+
+ // kTexFormatBGRA32 is one of those "esoteric" formats that is widely used for webcam textures
+ if (inFormat == kTexFormatBGRA32)
+ return kTextureFormatBGRA32Workaround;
+
+ return kTextureFormatTable[inFormat];
+}
+
+TexturesD3D11::TexturesD3D11()
+{
+ InvalidateSamplers();
+}
+
+TexturesD3D11::~TexturesD3D11()
+{
+ #if !UNITY_EDITOR
+ Assert(m_ComputeBuffers.empty());
+ Assert(m_StagingTextures.empty());
+ #endif
+}
+
+void TexturesD3D11::TextureFromShaderResourceView(ID3D11ShaderResourceView* resourceView, ID3D11Texture2D** texture) const
+{
+ ID3D11Resource* resource = NULL;
+ resourceView->GetResource(&resource);
+ Assert(resource && "ID3D11Resource is NULL");
+ resource->Release(); // GetResource() calls AddRef()
+ D3D11_RESOURCE_DIMENSION rType;
+ resource->GetType(&rType);
+ Assert(rType == D3D11_RESOURCE_DIMENSION_TEXTURE2D && "ID3D11Resource is not Texture2D");
+ resource->QueryInterface(texture);
+ Assert(texture && "Texture is NULL");
+ (*texture)->Release(); // QueryInterface() calls AddRef()
+}
+
+intptr_t TexturesD3D11::RegisterNativeTexture(ID3D11ShaderResourceView* resourceView) const
+{
+ ID3D11Texture2D* texture = NULL;
+ TextureFromShaderResourceView(resourceView, &texture);
+ return AllocD3DTexture(texture, resourceView, 0, false);
+}
+
+void TexturesD3D11::UpdateNativeTexture(TextureID textureID, ID3D11ShaderResourceView* resourceView)
+{
+ ID3D11Texture2D* texture = NULL;
+ TextureFromShaderResourceView(resourceView, &texture);
+ D3D11Texture* target = QueryD3DTexture(textureID);
+ if(target)
+ {
+ target->m_Texture = texture;
+ target->m_SRV = resourceView;
+ }
+ else
+ AddTexture(textureID, texture, resourceView, 0, false);
+}
+
+void TexturesD3D11::AddTexture (TextureID textureID, ID3D11Resource* texture, ID3D11ShaderResourceView* srv, ID3D11UnorderedAccessView* uav, bool shadowMap)
+{
+ TextureIdMap::UpdateTexture(textureID, AllocD3DTexture(texture,srv,uav,shadowMap));
+}
+
+void TexturesD3D11::RemoveTexture( TextureID textureID )
+{
+ D3D11Texture* target = QueryD3DTexture(textureID);
+ if(target)
+ {
+ target->~D3D11Texture();
+ _TextureAlloc->free(target);
+ }
+ TextureIdMap::RemoveTexture(textureID);
+}
+
+TexturesD3D11::D3D11Texture* TexturesD3D11::GetTexture (TextureID textureID)
+{
+ return QueryD3DTexture(textureID);
+}
+
+void TexturesD3D11::AddComputeBuffer (ComputeBufferID id, const ComputeBuffer11& buf)
+{
+ m_ComputeBuffers.insert (std::make_pair(id, buf));
+}
+
+void TexturesD3D11::RemoveComputeBuffer (ComputeBufferID id)
+{
+ m_ComputeBuffers.erase (id);
+}
+
+
+ComputeBuffer11* TexturesD3D11::GetComputeBuffer (ComputeBufferID id)
+{
+ ComputeBufferMap::iterator it = m_ComputeBuffers.find(id);
+ return it==m_ComputeBuffers.end() ? NULL : &it->second;
+}
+
+
+void TexturesD3D11::Upload2DData (const UInt8* dataPtr, TextureFormat dataFormat, int width, int height, bool decompressData, ID3D11Resource* dst, DXGI_FORMAT dstFormat, bool bgra, int dstSubResource)
+{
+ DX11_LOG_ENTER_FUNCTION("TexturesD3D11::Upload2DData (0x%08x, %d, %d, %d, %s, 0x%08x, %d, %d)",
+ dataPtr, dataFormat, width, height, GetDX11BoolString(decompressData), dst, dstFormat, dstSubResource);
+
+ if (bgra)
+ {
+ if (dstFormat == DXGI_FORMAT_R8G8B8A8_UNORM)
+ dstFormat = DXGI_FORMAT_B8G8R8A8_UNORM;
+ else if (dstFormat == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB)
+ dstFormat = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
+ }
+
+ HRESULT hr;
+
+ bool isDXT = false;
+ int texWidth = width;
+ int texHeight = height;
+ int texMips = 1;
+ if (!decompressData && IsCompressedDXTTextureFormat(dataFormat))
+ {
+ isDXT = true;
+ while (texWidth < 4 || texHeight < 4)
+ {
+ texWidth *= 2;
+ texHeight *= 2;
+ ++texMips;
+ }
+ }
+
+ // find or create a staging texture of needed size & format
+ UInt64 stagingKey = (UInt64(width) << kStagingWidthShift) | (UInt64(height) << kStagingHeightShift) | (UInt64(dstFormat) << kStagingFormatShift);
+ StagingTextureMap::iterator it = m_StagingTextures.find (stagingKey);
+ if (it == m_StagingTextures.end())
+ {
+ D3D11_TEXTURE2D_DESC desc;
+ desc.Width = texWidth;
+ desc.Height = texHeight;
+ desc.MipLevels = texMips;
+ desc.ArraySize = 1;
+ desc.Format = dstFormat;
+ desc.SampleDesc.Count = 1;
+ desc.SampleDesc.Quality = 0;
+ desc.Usage = D3D11_USAGE_STAGING;
+ desc.BindFlags = 0;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+ desc.MiscFlags = 0;
+
+ ID3D11Device* dev = GetD3D11Device();
+ ID3D11Texture2D* texture = NULL;
+ hr = dev->CreateTexture2D (&desc, NULL, &texture);
+ if (FAILED(hr))
+ {
+ printf_console ("d3d11: failed to create staging 2D texture w=%i h=%i d3dfmt=%i [%x]\n", width, height, dstFormat, hr);
+ return;
+ }
+ SetDebugNameD3D11 (texture, Format("Staging-Texture2D-%dx%d-fmt%d", width, height, dstFormat));
+
+ it = m_StagingTextures.insert (std::make_pair(stagingKey, texture)).first;
+ }
+
+ // copy/convert source data into the staging texture
+ const int stagingSubresource = texMips-1;
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ hr = ctx->Map(it->second, stagingSubresource, D3D11_MAP_WRITE, 0, &mapped);
+ if (FAILED(hr))
+ {
+ printf_console ("d3d11: failed to map staging 2D texture w=%i h=%i d3dfmt=%i [%x]\n", width, height, dstFormat, hr);
+ return;
+ }
+
+ ///@TODO: more format conversions?
+ if (decompressData)
+ {
+ //int tmpWidth = std::max(width,4);
+ int tmpHeight = std::max(height,4);
+ DecompressNativeTextureFormatWithMipLevel (dataFormat, width, height, 0 /*TODO*/, (const UInt32*)dataPtr, mapped.RowPitch/4, tmpHeight, (UInt32*)mapped.pData);
+ //PerformUploadConversions (width, height, rgba, tempBufferPitch, usageMode, colorSpace, GetProphecyPixelFormat(kTexFormatRGBA32)); //@TODO?
+ }
+ else if (dstFormat == DXGI_FORMAT_R8G8B8A8_UNORM || dstFormat == DXGI_FORMAT_B8G8R8A8_UNORM)
+ {
+ SwizzleToRGBA (dataPtr, (UInt8*)mapped.pData, width, height, mapped.RowPitch, dataFormat);
+ }
+ else
+ {
+ size_t dataSize = CalculateImageSize(width,height,dataFormat);
+ const int minSize = isDXT ? 4 : 1;
+ int mipWidth = std::max(texWidth >> stagingSubresource, minSize);
+ int mipHeight = std::max(texHeight >> stagingSubresource, minSize);
+ size_t mappedSize = mapped.RowPitch * mipHeight;
+ // pitch for compressed formats is for full row of blocks
+ if (isDXT)
+ {
+ mappedSize /= 4;
+ mipHeight /= 4;
+ }
+ UInt8* mappedData = (UInt8*)mapped.pData;
+ if (dataSize == mappedSize)
+ {
+ memcpy(mappedData, dataPtr, dataSize);
+ }
+ else
+ {
+ // For compressed formats, height was already divided by block height, so this works out to operate
+ // on full row blocks
+ const size_t dataRowPitch = dataSize / mipHeight;
+ for (int y = 0; y < mipHeight; ++y)
+ {
+ memcpy(mappedData, dataPtr, dataRowPitch);
+ mappedData += mapped.RowPitch;
+ dataPtr += dataRowPitch;
+ }
+ }
+ }
+ ctx->Unmap(it->second, stagingSubresource);
+
+
+ // copy from staging into destination
+ ctx->CopySubresourceRegion (dst, dstSubResource, 0, 0, 0, it->second, stagingSubresource, NULL);
+ // Sometime CopySubresourceRegion hangs, that's why we have a message here
+ DX11_LOG_OUTPUT("TexturesD3D11::Upload2DData done");
+}
+
+
+void TexturesD3D11::UploadTexture2D(
+ TextureID tid, TextureDimension dimension, UInt8* srcData, int width, int height,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, int masterTextureLimit, TextureUsageMode usageMode, TextureColorSpace colorSpace )
+{
+ AssertIf( srcData == NULL );
+ AssertIf( (!IsPowerOfTwo(width) || !IsPowerOfTwo(height)) && !IsNPOTTextureAllowed(mipCount > 1) );
+
+ if( dimension != kTexDim2D )
+ {
+ ErrorString( "Incorrect texture dimension!" );
+ return;
+ }
+
+ // Nothing to do here. Early out instead of failing, empty textures are serialized by dynamic fonts.
+ if( width == 0 || height == 0 )
+ return;
+
+ // Figure out whether we'll upload compressed or decompress on the fly
+ bool uploadIsCompressed, decompressOnTheFly;
+ HandleFormatDecompression (format, &usageMode, colorSpace, &uploadIsCompressed, &decompressOnTheFly);
+ if (decompressOnTheFly)
+ uploadIsCompressed = false;
+
+ // find the texture
+ D3D11Texture* target = QueryD3DTexture(tid);
+
+ // For compressed textures, stop applying masterTextureLimit if texture size drops below 4
+ if( uploadIsCompressed )
+ {
+ while( masterTextureLimit > 0 && ((width >> masterTextureLimit) < 4 || (height >> masterTextureLimit) < 4) )
+ {
+ --masterTextureLimit;
+ }
+ }
+
+ // skip several levels in data based on masterTextureLimit
+ int maxLevel = mipCount - 1;
+ int baseLevel = std::min( masterTextureLimit, maxLevel );
+ int level;
+ for( level = 0; level < baseLevel; ++level )
+ {
+ srcData += CalculateImageSize (width, height, format);
+ AssertIf( width == 1 && height == 1 && level != maxLevel );
+ width = std::max( width / 2, 1 );
+ height = std::max( height / 2, 1 );
+ }
+
+ const FormatDesc11& uploadFormat = GetUploadFormat(decompressOnTheFly ? kTexFormatRGBA32 : format);
+ DXGI_FORMAT d3dFormat = (colorSpace == kTexColorSpaceSRGBXenon || colorSpace == kTexColorSpaceSRGB) ? uploadFormat.sRGBD3dformat : uploadFormat.d3dformat;
+ const bool needBGRA = (uploadFlags & GfxDevice::kUploadTextureOSDrawingCompatible);
+
+ // While texture is too large for hardware limits, skip mip levels.
+ int texWidth = width, texHeight = height;
+ prcore::Surface::BlitMode blitMode = prcore::Surface::BLIT_COPY;
+ while( texWidth > gGraphicsCaps.maxTextureSize || texHeight > gGraphicsCaps.maxTextureSize )
+ {
+ if( baseLevel < maxLevel )
+ {
+ srcData += CalculateImageSize (texWidth, texHeight, format);
+ width = std::max( width / 2, 1 );
+ height = std::max( height / 2, 1 );
+ ++baseLevel;
+ }
+ texWidth = std::max( texWidth / 2, 1 );
+ texHeight = std::max( texHeight / 2, 1 );
+ blitMode = prcore::Surface::BLIT_SCALE;
+ if( texWidth <= 4 && texHeight <= 4 )
+ break;
+ }
+
+ ID3D11Device* dev = GetD3D11Device();
+
+ // create texture if it does not exist already
+ ID3D11Texture2D* texture = NULL;
+ if(!target)
+ {
+ D3D11_TEXTURE2D_DESC desc;
+ desc.Width = texWidth;
+ desc.Height = texHeight;
+ desc.MipLevels = mipCount - baseLevel;
+ desc.ArraySize = 1;
+ desc.Format = d3dFormat;
+ desc.SampleDesc.Count = 1;
+ desc.SampleDesc.Quality = 0;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+ desc.CPUAccessFlags = 0;
+ desc.MiscFlags = 0;
+
+ // GDI-compatible textures require certain restrictions
+ if (needBGRA)
+ {
+ desc.MiscFlags |= D3D11_RESOURCE_MISC_GDI_COMPATIBLE;
+ desc.BindFlags |= D3D11_BIND_RENDER_TARGET;
+ // BGRA format
+ if (desc.Format == DXGI_FORMAT_R8G8B8A8_UNORM)
+ desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ else if (desc.Format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB)
+ desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
+ else {
+ AssertString("invalid d3d11 texture format for OS compatible texture");
+ }
+ }
+ HRESULT hr = dev->CreateTexture2D (&desc, NULL, &texture);
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(texture, CalculateImageSize(texWidth, texHeight,format)*(mipCount>1?1.33:1),tid.m_ID);
+ if( FAILED(hr) )
+ printf_console( "d3d11: failed to create 2D texture id=%i w=%i h=%i mips=%i d3dfmt=%i [%x]\n", tid, texWidth, texHeight, mipCount-baseLevel, d3dFormat, hr );
+ SetDebugNameD3D11 (texture, Format("Texture2D-%d-%dx%d", tid.m_ID, texWidth, texHeight));
+
+ // Create the shader resource view
+ D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc;
+ viewDesc.Format = desc.Format;
+ viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+ viewDesc.Texture2D.MostDetailedMip = 0;
+ viewDesc.Texture2D.MipLevels = mipCount - baseLevel;
+
+ ID3D11ShaderResourceView* srView = NULL;
+ hr = dev->CreateShaderResourceView (texture, &viewDesc, &srView);
+ if (FAILED(hr))
+ printf_console ("d3d11: failed to create 2D texture view id=%i [%x]\n", tid, hr);
+
+ SetDebugNameD3D11 (srView, Format("Texture2D-SRV-%d-%dx%d", tid.m_ID, texWidth, texHeight));
+ TextureIdMap::UpdateTexture(tid, AllocD3DTexture(texture, srView, NULL, false));
+ }
+ else
+ {
+ texture = (ID3D11Texture2D*)target->m_Texture;
+ }
+ if( !texture )
+ {
+ AssertString( "failed to create 2D texture" );
+ return;
+ }
+
+ prcore::PixelFormat pf;
+
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ UInt8* rgba = NULL;
+ if (uploadFormat.d3dformat == DXGI_FORMAT_R8G8B8A8_UNORM)
+ {
+ int tmpWidth = std::max(width,4);
+ int tmpHeight = std::max(height,4);
+ rgba = new UInt8[tmpWidth*tmpHeight*4];
+ }
+
+ // Upload the mip levels
+ for( level = baseLevel; level <= maxLevel; ++level )
+ {
+#if !ENABLE_OLD_TEXTURE_UPLOAD
+ if(decompressOnTheFly && IsCompressedFlashATFTextureFormat(format)){
+ //Workaround for flash decompression, where we do not know the miplevel sizes.
+ Image tempImage (width, height, kTexFormatRGBA32);
+ DecompressNativeTextureFormatWithMipLevel(format, width, height, level, (UInt32*)srcData, width, height, (UInt32*)tempImage.GetImageData());
+ Upload2DData (tempImage.GetImageData(), kTexFormatRGBA32, width, height, false, texture, uploadFormat.d3dformat, needBGRA, level-baseLevel);
+ }else{
+ Upload2DData (srcData, format, width, height, decompressOnTheFly, texture, uploadFormat.d3dformat, needBGRA, level-baseLevel);
+ }
+#else
+ if (!uploadIsCompressed)
+ {
+ int srcPitch = 0;
+ const UInt8* finalData = srcData;
+
+ if (decompressOnTheFly)
+ {
+ int tmpWidth = std::max(width,4);
+ int tmpHeight = std::max(height,4);
+ DecompressNativeTextureFormatWithMipLevel (format, width, height, level, (UInt32*)srcData, tmpWidth,tmpHeight, (UInt32*)rgba);
+ //PerformUploadConversions (width, height, rgba, tempBufferPitch, usageMode, colorSpace, GetProphecyPixelFormat(kTexFormatRGBA32)); //@TODO?
+ srcPitch = width * 4;
+ finalData = rgba;
+ }
+ else if (uploadFormat.d3dformat == DXGI_FORMAT_R8G8B8A8_UNORM)
+ {
+ srcPitch = width * 4;
+ SwizzleToRGBA (srcData, rgba, width, height, srcPitch, format);
+ finalData = rgba;
+ }
+ else
+ {
+ srcPitch = GetRowBytesFromWidthAndFormat(width,format);
+ }
+
+ ///@TODO: more format conversions?
+ ctx->UpdateSubresource (texture, level-baseLevel, NULL, finalData, srcPitch, 0);
+ }
+ else
+ {
+ int dxtWidth = std::max(width,4);
+ int srcPitch = (format==kTexFormatDXT1) ? dxtWidth*2 : dxtWidth*4;
+ if (width == texWidth && height == texHeight)
+ {
+ ctx->UpdateSubresource (texture, level-baseLevel, NULL, srcData, srcPitch, 0);
+ }
+ else
+ {
+ // TODO: fill with garbage?
+ }
+ }
+#endif
+ // Go to next level
+ srcData += CalculateImageSize (width, height, format);
+ AssertIf( width == 1 && height == 1 && level != maxLevel );
+ width = std::max( width / 2, 1 );
+ height = std::max( height / 2, 1 );
+ texWidth = std::max( texWidth / 2, 1 );
+ texHeight = std::max( texHeight / 2, 1 );
+ }
+
+ delete[] rgba;
+}
+
+
+void TexturesD3D11::UploadTextureSubData2D(
+ TextureID tid, UInt8* srcData, int mipLevel,
+ int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace )
+{
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ if (!ctx)
+ return;
+
+ Assert(srcData != NULL);
+ Assert(!IsAnyCompressedTextureFormat(format));
+
+ D3D11Texture* target = QueryD3DTexture(tid);
+ if (!target)
+ {
+ AssertString("Texture not found");
+ return;
+ }
+
+ const FormatDesc11& uploadFormat = GetUploadFormat(format);
+ ID3D11Resource* texture = (ID3D11Resource*)target->m_Texture;
+ Assert(texture);
+
+ D3D11_BOX destRegion;
+ destRegion.left = x;
+ destRegion.right = x + width;
+ destRegion.top = y;
+ destRegion.bottom = y + height;
+ destRegion.front = 0;
+ destRegion.back = 1;
+
+ ///@TODO: other format conversions?
+ bool releaseUploadData = false;
+ UInt8* uploadData = srcData;
+ int srcPitch = GetRowBytesFromWidthAndFormat(width,format);
+ if (uploadFormat.d3dformat == DXGI_FORMAT_R8G8B8A8_UNORM)
+ {
+ uploadData = new UInt8[width*height*4];
+ SwizzleToRGBA (srcData, uploadData, width, height, width*4, format);
+ releaseUploadData = true;
+ srcPitch = width*4;
+ }
+ ctx->UpdateSubresource (texture, mipLevel, &destRegion, uploadData, srcPitch, 0);
+ if (releaseUploadData)
+ delete[] uploadData;
+}
+
+
+void TexturesD3D11::UploadTextureCube(
+ TextureID tid, UInt8* srcData, int faceDataSize, int size,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace )
+{
+ ID3D11Device* dev = GetD3D11Device();
+ if (!dev)
+ return;
+
+ if (gGraphicsCaps.buggyMipmappedCubemaps)
+ mipCount = 1;
+
+ // find the texture
+ D3D11Texture* target = QueryD3DTexture(tid);
+
+ // create texture if it does not exist already
+ const FormatDesc11& uploadFormat = GetUploadFormat( format );
+ bool sRGBUpload = (colorSpace == kTexColorSpaceSRGBXenon || colorSpace == kTexColorSpaceSRGB);
+
+#if ENABLE_OLD_TEXTURE_UPLOAD
+ // Due to confirmed Microsoft DirectX Runtime bug we can't supply cubemap subresources via UpdateSubresource() function
+ // Prepare subresource data for CreateTexture2D() instead
+
+ const int maxLevel = mipCount - 1;
+
+ size_t uploadBufferSize = 0;
+ TextureFormat formatForSizeCalc = (uploadFormat.d3dformat == DXGI_FORMAT_R8G8B8A8_UNORM) ? kTexFormatARGB32 : format;
+ int mipSizeForCalc = size;
+ for (int level = 0; level <= maxLevel; ++level)
+ {
+ uploadBufferSize += CalculateImageSize (mipSizeForCalc, mipSizeForCalc, formatForSizeCalc);
+ mipSizeForCalc = std::max(mipSizeForCalc / 2, 1);
+ }
+ uploadBufferSize *= 6;
+
+ UInt8* rawData = new UInt8[uploadBufferSize];
+ UInt8* rawDataPtr = rawData;
+ D3D11_SUBRESOURCE_DATA subresourceData[6*20]; //2^20 should be enough
+ {
+ bool uploadIsCompressed = IsCompressedDXTTextureFormat(format);
+
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ for (int face=0;face<6;face++)
+ {
+ int mipSize = size;
+ UInt8* data = srcData + face * faceDataSize;
+
+ // Upload the mip levels
+ for (int level = 0; level <= maxLevel; ++level)
+ {
+ const UInt32 nLevelSize = CalculateImageSize( mipSize, mipSize, format );
+ UInt32 levelRawSize = nLevelSize;
+
+ ///@TODO: handle format conversions
+ int pitch = GetRowBytesFromWidthAndFormat(mipSize,format);
+ if (uploadFormat.d3dformat == DXGI_FORMAT_R8G8B8A8_UNORM)
+ {
+ pitch = mipSize * 4;
+ SwizzleToRGBA (data, rawDataPtr, mipSize, mipSize, pitch, format);
+ levelRawSize = mipSize*mipSize*4;
+ }
+ else
+ {
+ memcpy (rawDataPtr, data, nLevelSize);
+ }
+
+ subresourceData[face*mipCount + level].pSysMem = rawDataPtr;
+ subresourceData[face*mipCount + level].SysMemPitch = pitch;
+ subresourceData[face*mipCount + level].SysMemSlicePitch = 0;
+
+ // Go to next level
+ data += nLevelSize;
+ rawDataPtr += levelRawSize;
+ AssertIf( mipSize == 1 && level != maxLevel );
+ mipSize = std::max( mipSize / 2, 1 );
+ }
+ }
+ }
+#endif
+
+ ID3D11Texture2D* texture = NULL;
+ if (!target)
+ {
+ D3D11_TEXTURE2D_DESC desc;
+ desc.Width = size;
+ desc.Height = size;
+ desc.MipLevels = mipCount;
+ desc.ArraySize = 6;
+ desc.Format = sRGBUpload ? uploadFormat.sRGBD3dformat : uploadFormat.d3dformat;
+ desc.SampleDesc.Count = 1;
+ desc.SampleDesc.Quality = 0;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+ desc.CPUAccessFlags = 0;
+ desc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
+#if ENABLE_OLD_TEXTURE_UPLOAD
+ HRESULT hr = dev->CreateTexture2D (&desc, subresourceData, &texture);
+#else
+ HRESULT hr = dev->CreateTexture2D (&desc, NULL, &texture);
+#endif
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(texture, 6 * CalculateImageSize(size, size, format)*(mipCount>1?1.33:1),tid.m_ID);
+ if (FAILED(hr))
+ printf_console ("d3d11: failed to create Cube texture id=%i s=%i mips=%i d3dfmt=%i [%x]\n", tid, size, mipCount, sRGBUpload ? uploadFormat.sRGBD3dformat : uploadFormat.d3dformat, hr);
+ SetDebugNameD3D11 (texture, Format("TextureCube-%d-%dx%d", tid.m_ID, size));
+
+ // Create the shader resource view
+ D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc;
+ viewDesc.Format = desc.Format;
+ viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
+ viewDesc.Texture2D.MostDetailedMip = 0;
+ viewDesc.Texture2D.MipLevels = mipCount;
+
+ ID3D11ShaderResourceView* srView = NULL;
+ hr = dev->CreateShaderResourceView (texture, &viewDesc, &srView);
+ if (FAILED(hr))
+ printf_console ("d3d11: failed to create Cube texture view id=%i [%x]\n", tid, hr);
+
+ SetDebugNameD3D11 (srView, Format("TextureCube-SRV-%d-%d", tid.m_ID, size));
+
+ TextureIdMap::UpdateTexture(tid, AllocD3DTexture(texture, srView, NULL, false));
+ }
+ else
+ {
+ texture = (ID3D11Texture2D*)target->m_Texture;
+ }
+#if ENABLE_OLD_TEXTURE_UPLOAD
+ delete[] rawData;
+#endif
+
+ if (!texture)
+ {
+ AssertString( "failed to create cubemap" );
+ return;
+ }
+#if !ENABLE_OLD_TEXTURE_UPLOAD
+ // Upload data
+ // Note: DX11 Runtime on 9.x levels has a bug where setting cubemap data via UpdateSubresource doesn't work (only first
+ // subresource is ever updated). We use staging resources for upload here, but keep in mind if at some point you want
+ // to switch away from them.
+ const int maxLevel = mipCount - 1;
+ for (int face=0;face<6;face++)
+ {
+ int mipSize = size;
+ UInt8* data = srcData + face * faceDataSize;
+
+ // Upload the mip levels
+ for (int level = 0; level <= maxLevel; ++level)
+ {
+ Upload2DData (data, format, mipSize, mipSize, false, texture, uploadFormat.d3dformat, false, D3D11CalcSubresource(level,face,mipCount));
+
+ data += CalculateImageSize(mipSize, mipSize, format);
+ Assert(mipSize != 1 || level == maxLevel);
+ mipSize = std::max(mipSize/2, 1);
+ }
+ }
+#endif
+}
+
+void TexturesD3D11::UploadTexture3D(
+ TextureID tid, UInt8* srcData, int width, int height, int depth,
+ TextureFormat format, int mipCount, UInt32 uploadFlags )
+{
+ ID3D11Device* dev = GetD3D11Device();
+ if (!dev)
+ return;
+
+ if (gGraphicsCaps.buggyMipmapped3DTextures)
+ mipCount = 1;
+
+ // find the texture
+ D3D11Texture* target = QueryD3DTexture(tid);
+
+ // create texture if it does not exist already
+ const FormatDesc11& uploadFormat = GetUploadFormat(format);
+
+ ID3D11Texture3D* texture = NULL;
+ if (!target)
+ {
+ D3D11_TEXTURE3D_DESC desc;
+ desc.Width = width;
+ desc.Height = height;
+ desc.Depth = depth;
+ desc.MipLevels = mipCount;
+ desc.Format = uploadFormat.d3dformat;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+ desc.CPUAccessFlags = 0;
+ desc.MiscFlags = 0;
+ HRESULT hr = dev->CreateTexture3D (&desc, NULL, &texture);
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(texture, depth * CalculateImageSize(width, height, format)*(mipCount>1?1.33:1),tid.m_ID);
+ if (FAILED(hr))
+ printf_console ("d3d11: failed to create 3D texture id=%i s=%ix%ix%i mips=%i d3dfmt=%i [%x]\n", tid, width, height, depth, mipCount, uploadFormat.d3dformat, hr);
+ SetDebugNameD3D11 (texture, Format("Texture3D-%d-%dx%dx%d", tid.m_ID, width, height, depth));
+
+ // Create the shader resource view
+ D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc;
+ viewDesc.Format = desc.Format;
+ viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
+ viewDesc.Texture3D.MostDetailedMip = 0;
+ viewDesc.Texture3D.MipLevels = mipCount;
+
+ ID3D11ShaderResourceView* srView = NULL;
+ hr = dev->CreateShaderResourceView (texture, &viewDesc, &srView);
+ if (FAILED(hr))
+ printf_console ("d3d11: failed to create 3D texture view id=%i [%x]\n", tid, hr);
+
+ SetDebugNameD3D11 (srView, Format("Texture3D-SRV-%d-%dx%dx%d", tid.m_ID, width, height, depth));
+
+ TextureIdMap::UpdateTexture(tid, AllocD3DTexture(texture, srView, NULL, false));
+ }
+ else
+ {
+ texture = (ID3D11Texture3D*)target->m_Texture;
+ }
+ if (!texture)
+ {
+ AssertString("failed to create 3D texture");
+ return;
+ }
+
+ // Upload data
+ bool uploadIsCompressed = IsCompressedDXTTextureFormat(format);
+
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+
+ int maxLevel = mipCount - 1;
+
+ UInt8* rgba = NULL;
+ if (uploadFormat.d3dformat == DXGI_FORMAT_R8G8B8A8_UNORM)
+ rgba = new UInt8[width*height*depth*4];
+
+ for (int level = 0; level <= maxLevel; ++level)
+ {
+ ///@TODO: handle format conversions
+ const UInt8* finalData = srcData;
+ int pitch = GetRowBytesFromWidthAndFormat(width,format);
+ if (uploadFormat.d3dformat == DXGI_FORMAT_R8G8B8A8_UNORM)
+ {
+ const UInt8* srcSlice = srcData;
+ UInt8* dstSlice = rgba;
+ for (int d = 0; d < depth; ++d)
+ {
+ SwizzleToRGBA (srcSlice, dstSlice, width, height, width*4, format);
+ srcSlice += pitch * height;
+ dstSlice += 4 * width * height;
+ }
+ finalData = rgba;
+ pitch = width * 4;
+ }
+
+ ctx->UpdateSubresource (texture, level, NULL, finalData, pitch, pitch * height);
+
+ // Go to next level
+ srcData += CalculateImageSize(width, height, format) * height;
+ width = std::max(width / 2, 1);
+ height = std::max(height / 2, 1);
+ depth = std::max(depth / 2, 1);
+ }
+ delete[] rgba;
+}
+
+static D3D11_TEXTURE_ADDRESS_MODE s_D3DWrapModes[kTexWrapCount] = {
+ D3D11_TEXTURE_ADDRESS_WRAP,
+ D3D11_TEXTURE_ADDRESS_CLAMP,
+};
+static D3D11_FILTER s_D3DFilters[kTexFilterCount] = {
+ D3D11_FILTER_MIN_MAG_MIP_POINT,
+ D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT,
+ D3D11_FILTER_MIN_MAG_MIP_LINEAR,
+};
+static D3D11_FILTER s_D3DFiltersShadow[kTexFilterCount] = {
+ D3D11_FILTER_COMPARISON_MIN_MAG_MIP_POINT,
+ D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT,
+ D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT,
+};
+
+ID3D11SamplerState* TexturesD3D11::GetSampler(BuiltinSamplerState sampler)
+{
+ D3D11Sampler state;
+ switch (sampler) {
+ case kSamplerPointClamp:
+ state.filter = kTexFilterNearest;
+ state.wrap = kTexWrapClamp;
+ break;
+ case kSamplerLinearClamp:
+ state.filter = kTexFilterBilinear;
+ state.wrap = kTexWrapClamp;
+ break;
+ case kSamplerPointRepeat:
+ state.filter = kTexFilterNearest;
+ state.wrap = kTexWrapRepeat;
+ break;
+ case kSamplerLinearRepeat:
+ state.filter = kTexFilterBilinear;
+ state.wrap = kTexWrapRepeat;
+ break;
+ default: AssertString("unknown builtin sampler type");
+ }
+ return GetSampler (state);
+}
+
+
+ID3D11SamplerState* TexturesD3D11::GetSampler(const D3D11Sampler& texSampler)
+{
+ SamplerMap::iterator sit = m_Samplers.find(texSampler);
+ if (sit != m_Samplers.end())
+ return sit->second;
+
+ ID3D11Device* dev = GetD3D11Device();
+ ID3D11SamplerState* sampler = NULL;
+
+ D3D11_SAMPLER_DESC desc;
+ if (texSampler.flags & kSamplerShadowMap)
+ desc.Filter = s_D3DFiltersShadow[texSampler.filter];
+ else if (texSampler.anisoLevel > 1 && gGraphicsCaps.d3d11.featureLevel >= kDX11Level10_0)
+ desc.Filter = D3D11_FILTER_ANISOTROPIC;
+ else
+ desc.Filter = s_D3DFilters[texSampler.filter];
+ desc.AddressU = desc.AddressV = desc.AddressW = s_D3DWrapModes[texSampler.wrap];
+ desc.MipLODBias = texSampler.bias;
+ desc.MaxAnisotropy = texSampler.anisoLevel;
+ desc.ComparisonFunc = (gGraphicsCaps.d3d11.featureLevel < kDX11Level10_0) ? D3D11_COMPARISON_LESS_EQUAL : D3D11_COMPARISON_LESS;
+ desc.BorderColor[0] = desc.BorderColor[1] = desc.BorderColor[2] = desc.BorderColor[3] = 0.0f;
+ desc.MinLOD = -FLT_MAX;
+ desc.MaxLOD = FLT_MAX;
+ HRESULT hr = dev->CreateSamplerState (&desc, &sampler);
+ SetDebugNameD3D11 (sampler, Format("SamplerState-%d-%d", texSampler.filter, texSampler.wrap));
+
+ sit = m_Samplers.insert (std::make_pair(texSampler,sampler)).first;
+ return sit->second;
+}
+
+
+bool TexturesD3D11::SetTexture (ShaderType shaderType, int unit, int sampler, TextureID textureID, float bias)
+{
+ D3D11Texture* target = QueryD3DTexture(textureID);
+ if (!target)
+ return false;
+
+ D3D11Texture& tex = *target;
+ // Do not bind texture if it's currently being used as a render target
+ if (g_D3D11CurrColorRT && g_D3D11CurrColorRT->m_Texture == tex.m_Texture && !tex.m_UAV)
+ return false;
+
+ if (bias != std::numeric_limits<float>::infinity())
+ tex.m_Sampler.bias = bias;
+
+ // set sampler state
+ ID3D11SamplerState* smp = GetSampler(tex.m_Sampler);
+
+ ID3D11DeviceContext* ctx = GetD3D11Context();
+ switch (shaderType) {
+ case kShaderVertex:
+ ctx->VSSetShaderResources (unit, 1, &tex.m_SRV);
+ if (sampler >= 0 && m_ActiveD3DSamplers[kShaderVertex][sampler] != smp)
+ {
+ ctx->VSSetSamplers (sampler, 1, &smp);
+ m_ActiveD3DSamplers[kShaderVertex][sampler] = smp;
+ }
+ break;
+ case kShaderFragment:
+ ctx->PSSetShaderResources (unit, 1, &tex.m_SRV);
+ if (sampler >= 0 && m_ActiveD3DSamplers[kShaderFragment][sampler] != smp)
+ {
+ ctx->PSSetSamplers (sampler, 1, &smp);
+ m_ActiveD3DSamplers[kShaderFragment][sampler] = smp;
+ }
+ break;
+ case kShaderGeometry:
+ ctx->GSSetShaderResources (unit, 1, &tex.m_SRV);
+ if (sampler >= 0 && m_ActiveD3DSamplers[kShaderGeometry][sampler] != smp)
+ {
+ ctx->GSSetSamplers (sampler, 1, &smp);
+ m_ActiveD3DSamplers[kShaderGeometry][sampler] = smp;
+ }
+ break;
+ case kShaderHull:
+ ctx->HSSetShaderResources (unit, 1, &tex.m_SRV);
+ if (sampler >= 0 && m_ActiveD3DSamplers[kShaderHull][sampler] != smp)
+ {
+ ctx->HSSetSamplers (sampler, 1, &smp);
+ m_ActiveD3DSamplers[kShaderHull][sampler] = smp;
+ }
+ break;
+ case kShaderDomain:
+ ctx->DSSetShaderResources (unit, 1, &tex.m_SRV);
+ if (sampler >= 0 && m_ActiveD3DSamplers[kShaderDomain][sampler] != smp)
+ {
+ ctx->DSSetSamplers (sampler, 1, &smp);
+ m_ActiveD3DSamplers[kShaderDomain][sampler] = smp;
+ }
+ break;
+ default: AssertString ("unknown shader type");
+ }
+
+ return true;
+}
+
+void TexturesD3D11::InvalidateSamplers()
+{
+ memset (m_ActiveD3DSamplers, 0, sizeof(m_ActiveD3DSamplers));
+}
+
+
+void TexturesD3D11::SetTextureParams( TextureID textureID, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace )
+{
+ D3D11Texture* target = QueryD3DTexture(textureID);
+ if (!target)
+ return;
+
+ D3D11Sampler& s = target->m_Sampler;
+ s.filter = filter;
+ s.wrap = wrap;
+ s.anisoLevel = anisoLevel;
+ if (hasMipMap)
+ s.flags |= kSamplerHasMipMap;
+ else
+ s.flags &= ~kSamplerHasMipMap;
+}
+
+
+void TexturesD3D11::DeleteTexture( TextureID textureID )
+{
+ D3D11Texture* target = QueryD3DTexture(textureID);
+ if( !target )
+ return;
+
+ // texture can be null if texture creation failed. At least don't make it crash here
+ if (target->m_Texture)
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(target->m_Texture);
+ ULONG refCount = target->m_Texture->Release();
+ DebugAssert(!refCount);
+ }
+ if (target->m_UAV)
+ {
+ ULONG refCount = target->m_UAV->Release();
+ DebugAssert(!refCount);
+ }
+ if (target->m_SRV)
+ {
+ ULONG refCount = target->m_SRV->Release();
+ DebugAssert(!refCount);
+ }
+ target->~D3D11Texture();
+ _TextureAlloc->free(target);
+ TextureIdMap::RemoveTexture(textureID);
+}
+
+void TexturesD3D11::ClearTextureResources()
+{
+ for (SamplerMap::iterator it = m_Samplers.begin(); it != m_Samplers.end(); ++it)
+ {
+ if (it->second)
+ it->second->Release();
+ }
+ m_Samplers.clear();
+
+ for (StagingTextureMap::iterator it = m_StagingTextures.begin(), itEnd = m_StagingTextures.end(); it != itEnd; ++it)
+ {
+ if (it->second)
+ it->second->Release();
+ }
+ m_StagingTextures.clear();
+}
+
diff --git a/Runtime/GfxDevice/d3d11/TexturesD3D11.h b/Runtime/GfxDevice/d3d11/TexturesD3D11.h
new file mode 100644
index 0000000..d5971f9
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/TexturesD3D11.h
@@ -0,0 +1,201 @@
+#pragma once
+
+#include "D3D11Includes.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/Graphics/RenderSurface.h"
+#include "Runtime/Threads/AtomicOps.h"
+#include "Runtime/Utilities/NonCopyable.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include <map>
+
+class ImageReference;
+
+struct ComputeBuffer11
+{
+ ID3D11Buffer* buffer;
+ ID3D11ShaderResourceView* srv;
+ ID3D11UnorderedAccessView* uav;
+};
+
+
+class TexturesD3D11
+{
+public:
+ enum { kSamplerHasMipMap = (1<<0), kSamplerShadowMap = (1<<1) };
+ struct D3D11Sampler {
+ D3D11Sampler() {
+ memset (this, 0, sizeof(*this));
+ }
+ float bias;
+ UInt8 filter; // TextureFilterMode
+ UInt8 wrap; // TextureWrapMode
+ UInt8 anisoLevel;
+ UInt8 flags;
+ };
+ struct D3D11Texture {
+ D3D11Texture() : m_Texture(NULL), m_SRV(NULL), m_UAV(NULL) { }
+ explicit D3D11Texture (ID3D11Resource* tex, ID3D11ShaderResourceView* srv, ID3D11UnorderedAccessView* uav, bool shadowMap)
+ : m_Texture(tex), m_SRV(srv), m_UAV(uav) { if (shadowMap) m_Sampler.flags |= kSamplerShadowMap; }
+ ID3D11Resource* m_Texture;
+ ID3D11ShaderResourceView* m_SRV;
+ ID3D11UnorderedAccessView* m_UAV;
+ D3D11Sampler m_Sampler;
+ };
+
+public:
+ TexturesD3D11();
+ ~TexturesD3D11();
+
+ void ClearTextureResources();
+
+ bool SetTexture (ShaderType shaderType, int unit, int sampler, TextureID textureID, float bias);
+ void SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace );
+
+ void DeleteTexture( TextureID textureID );
+
+ void UploadTexture2D(
+ TextureID tid, TextureDimension dimension, UInt8* srcData, int width, int height,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, int masterTextureLimit, TextureUsageMode usageMode, TextureColorSpace colorSpace );
+
+ void UploadTextureSubData2D(
+ TextureID tid, UInt8* srcData, int mipLevel,
+ int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace );
+
+ void UploadTextureCube(
+ TextureID tid, UInt8* srcData, int faceDataSize, int size,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace );
+
+ void UploadTexture3D(
+ TextureID tid, UInt8* srcData, int width, int height, int depth,
+ TextureFormat format, int mipCount, UInt32 uploadFlags );
+
+ void AddTexture (TextureID textureID, ID3D11Resource* texture, ID3D11ShaderResourceView* srv, ID3D11UnorderedAccessView* uav, bool shadowMap);
+ void RemoveTexture( TextureID textureID );
+ D3D11Texture* GetTexture(TextureID textureID);
+ ID3D11SamplerState* GetSampler(const D3D11Sampler& texSampler);
+ ID3D11SamplerState* GetSampler(BuiltinSamplerState sampler);
+
+ void AddComputeBuffer (ComputeBufferID id, const ComputeBuffer11& buf);
+ void RemoveComputeBuffer (ComputeBufferID id);
+ ComputeBuffer11* GetComputeBuffer (ComputeBufferID id);
+
+ void InvalidateSamplers();
+ void InvalidateSampler (ShaderType shader, int samplerUnit) { m_ActiveD3DSamplers[shader][samplerUnit] = NULL; }
+
+ intptr_t RegisterNativeTexture(ID3D11ShaderResourceView* resourceView) const;
+ void UpdateNativeTexture(TextureID textureID, ID3D11ShaderResourceView* resourceView);
+
+private:
+ void Upload2DData (const UInt8* dataPtr, TextureFormat dataFormat, int width, int height, bool decompressData, ID3D11Resource* dst, DXGI_FORMAT dstFormat, bool bgra, int dstSubResource);
+
+private:
+ typedef std::map< D3D11Sampler, ID3D11SamplerState*, memcmp_less<D3D11Sampler> > SamplerMap;
+ SamplerMap m_Samplers;
+
+ ID3D11SamplerState* m_ActiveD3DSamplers[kShaderTypeCount][kMaxSupportedTextureUnits];
+
+ // staging texture key: width, height, dxgi format
+ enum {
+ kStagingWidthShift = 0ULL,
+ kStagingHeightShift = 20ULL,
+ kStagingFormatShift = 40ULL,
+ };
+ typedef std::map<UInt64, ID3D11Resource*> StagingTextureMap;
+ StagingTextureMap m_StagingTextures;
+
+ typedef std::map<ComputeBufferID, ComputeBuffer11> ComputeBufferMap;
+ ComputeBufferMap m_ComputeBuffers;
+
+ void TextureFromShaderResourceView(ID3D11ShaderResourceView* resourceView, ID3D11Texture2D** texture) const;
+};
+
+
+struct RenderSurfaceD3D11 : RenderSurfaceBase, NonCopyable
+{
+ RenderSurfaceD3D11()
+ : m_Texture(NULL)
+ , m_SRView(NULL)
+ , m_SRViewForMips(NULL)
+ , m_UAView(NULL)
+ , depth(0)
+ , dim(kTexDim2D)
+ {
+ RenderSurfaceBase_Init(*this);
+ }
+ ID3D11Resource* m_Texture;
+ ID3D11ShaderResourceView* m_SRView;
+ ID3D11ShaderResourceView* m_SRViewForMips; // always without sRGB
+ ID3D11UnorderedAccessView* m_UAView;
+ int depth;
+ TextureDimension dim;
+protected:
+ void Reset()
+ {
+ SAFE_RELEASE(m_Texture);
+ SAFE_RELEASE(m_SRView);
+ SAFE_RELEASE(m_SRViewForMips);
+ SAFE_RELEASE(m_UAView);
+ RenderSurfaceBase_Init(*this);
+ depth = 0;
+ dim = kTexDim2D;
+ }
+};
+
+struct RenderColorSurfaceD3D11 : public RenderSurfaceD3D11
+{
+ RenderColorSurfaceD3D11()
+ : format(kRTFormatARGB32)
+ {
+ colorSurface = true;
+ }
+
+ static UInt32 GetRTVKey(int face, int mipLevel, bool secondary) {
+ return (face) | (secondary ? 8 : 0) | (mipLevel << 4);
+ }
+ ID3D11RenderTargetView* GetRTV(int face, int mipLevel, bool secondary) {
+ UInt32 key = GetRTVKey(face, mipLevel, secondary);
+ for (int i = 0, n = m_RTVs.size(); i != n; ++i)
+ if (m_RTVs[i].first == key)
+ return m_RTVs[i].second;
+ return NULL;
+ }
+ void SetRTV(int face, int mipLevel, bool secondary, ID3D11RenderTargetView* rtv) {
+ DebugAssert(GetRTV(face, mipLevel, secondary) == NULL);
+ UInt32 key = GetRTVKey(face, mipLevel, secondary);
+ m_RTVs.push_back (std::make_pair(key, rtv));
+ }
+
+ void Reset()
+ {
+ RenderSurfaceD3D11::Reset();
+ colorSurface = true;
+ for (int i = 0, n = m_RTVs.size(); i != n; ++i)
+ {
+ SAFE_RELEASE(m_RTVs[i].second);
+ }
+ m_RTVs.resize_uninitialized(0);
+ }
+
+ typedef std::pair<UInt32, ID3D11RenderTargetView*> RTVPair;
+ dynamic_array<RTVPair> m_RTVs;
+ RenderTextureFormat format;
+};
+
+struct RenderDepthSurfaceD3D11 : public RenderSurfaceD3D11
+{
+ RenderDepthSurfaceD3D11()
+ : m_DSView(NULL)
+ , depthFormat(kDepthFormatNone)
+ {
+ colorSurface = false;
+ }
+ ID3D11DepthStencilView* m_DSView;
+ DepthBufferFormat depthFormat;
+ void Reset()
+ {
+ RenderSurfaceD3D11::Reset();
+ colorSurface = false;
+ SAFE_RELEASE(m_DSView);
+ depthFormat = kDepthFormatNone;
+ }
+};
diff --git a/Runtime/GfxDevice/d3d11/TimerQueryD3D11.cpp b/Runtime/GfxDevice/d3d11/TimerQueryD3D11.cpp
new file mode 100644
index 0000000..31afc52
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/TimerQueryD3D11.cpp
@@ -0,0 +1,209 @@
+#include "UnityPrefix.h"
+#if ENABLE_PROFILER
+#include "GfxDeviceD3D11.h"
+#include "D3D11Context.h"
+#include "TimerQueryD3D11.h"
+
+
+TimerQueryD3D11::TimerQueryD3D11()
+ : m_Query(NULL), m_Time(0), m_Active(false)
+{
+ D3D11_QUERY_DESC QueryDesc;
+ QueryDesc.Query = D3D11_QUERY_TIMESTAMP;
+ QueryDesc.MiscFlags = 0;
+ GetD3D11Device()->CreateQuery(&QueryDesc, &m_Query);
+ m_TimeMultiplier = 0.0f;
+}
+
+TimerQueryD3D11::~TimerQueryD3D11()
+{
+ SAFE_RELEASE(m_Query);
+}
+
+void TimerQueryD3D11::Measure()
+{
+ // Flush previous result
+ GetElapsed(kWaitRenderThread);
+
+ if (m_Query)
+ {
+ g_TimerQueriesD3D11.AddActiveTimerQuery(this);
+ GetD3D11Context()->End(m_Query);
+ m_Active = true;
+ m_Time = kInvalidProfileTime;
+ }
+ else
+ m_Time = 0;
+ m_TimeMultiplier = 0.0f;
+}
+
+ProfileTimeFormat TimerQueryD3D11::GetElapsed(UInt32 flags)
+{
+ while (m_Active)
+ {
+ bool wait = (flags & kWaitRenderThread) != 0;
+ if (!g_TimerQueriesD3D11.PollNextTimerQuery(wait))
+ break;
+ }
+ return m_Time;
+}
+
+bool TimerQueryD3D11::PollResult(UInt64& prevTime, bool wait)
+{
+ for (;;)
+ {
+ UINT64 time;
+ UINT flags = wait ? 0 : D3D11_ASYNC_GETDATA_DONOTFLUSH;
+ HRESULT hr = GetD3D11Context()->GetData(m_Query, &time, sizeof(time), flags);
+ if (hr == S_OK)
+ {
+ UInt64 elapsed = prevTime ? (time - prevTime) : 0;
+ m_Time = ProfileTimeFormat(elapsed * m_TimeMultiplier);
+ prevTime = time;
+ return true;
+ }
+ // Stop polling on unknown result (e.g D3DERR_DEVICELOST)
+ if (hr != S_FALSE)
+ {
+ m_Time = 0;
+ prevTime = 0;
+ return true;
+ }
+ if (!wait)
+ break;
+ }
+ return false;
+}
+
+TimerQueriesD3D11::TimerQueriesD3D11()
+{
+ m_LastQueryTime = 0;
+ m_DisjointQuery = NULL;
+ memset(m_StartTimeQueries, 0, sizeof(m_StartTimeQueries));
+ m_StartTimeQueryIndex = 0;
+}
+
+void TimerQueriesD3D11::ReleaseAllQueries()
+{
+ SAFE_RELEASE(m_DisjointQuery);
+ for (int i = 0; i < kStartTimeQueryCount; i++)
+ {
+ delete m_StartTimeQueries[i];
+ m_StartTimeQueries[i] = NULL;
+ }
+ m_InactiveTimerQueries.append(m_ActiveTimerQueries);
+ m_InactiveTimerQueries.append(m_PolledTimerQueries);
+ TimerQueryList& queries = m_InactiveTimerQueries;
+ for (TimerQueryList::iterator it = queries.begin(); it != queries.end(); ++it)
+ {
+ TimerQueryD3D11& query = *it;
+ query.m_Active = false;
+ query.m_Time = 0;
+ SAFE_RELEASE(query.m_Query);
+ }
+}
+
+void TimerQueriesD3D11::RecreateAllQueries()
+{
+ Assert(m_ActiveTimerQueries.empty());
+ Assert(m_PolledTimerQueries.empty());
+ TimerQueryList& queries = m_InactiveTimerQueries;
+ for (TimerQueryList::iterator it = queries.begin(); it != queries.end(); ++it)
+ {
+ TimerQueryD3D11& query = *it;
+
+ D3D11_QUERY_DESC QueryDesc;
+ QueryDesc.Query = D3D11_QUERY_TIMESTAMP;
+ QueryDesc.MiscFlags = 0;
+ GetD3D11Device()->CreateQuery(&QueryDesc, &query.m_Query);
+ }
+}
+
+void TimerQueriesD3D11::BeginTimerQueries()
+{
+ // Poll queries from previous frames
+ PollTimerQueries();
+
+ if (m_DisjointQuery == NULL)
+ {
+ D3D11_QUERY_DESC QueryDesc;
+ QueryDesc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT;
+ QueryDesc.MiscFlags = 0;
+ GetD3D11Device()->CreateQuery(&QueryDesc, &m_DisjointQuery);
+ }
+ GetD3D11Context()->Begin(m_DisjointQuery);
+
+ int& index = m_StartTimeQueryIndex;
+ if (m_StartTimeQueries[index] == NULL)
+ {
+ m_StartTimeQueries[index] = new TimerQueryD3D11;
+ }
+ m_StartTimeQueries[index]->Measure();
+ index = (index + 1) % kStartTimeQueryCount;
+}
+
+void TimerQueriesD3D11::EndTimerQueries()
+{
+ if(m_DisjointQuery == NULL)
+ return;
+
+ GetD3D11Context()->End(m_DisjointQuery);
+
+ HRESULT hr;
+ D3D11_QUERY_DATA_TIMESTAMP_DISJOINT disjointQueryResult;
+ do
+ {
+ hr = GetD3D11Context()->GetData(m_DisjointQuery, &disjointQueryResult, sizeof(disjointQueryResult), 0);
+ } while (hr == S_FALSE);
+ if (hr == S_OK && !disjointQueryResult.Disjoint)
+ {
+ float timeMult = float(1000000000.0 / (double)disjointQueryResult.Frequency);
+ TimerQueryList::iterator query, queryEnd = m_ActiveTimerQueries.end();
+ for (query = m_ActiveTimerQueries.begin(); query != queryEnd; ++query)
+ query->SetTimeMultiplier(timeMult);
+ }
+ // Move queries from active to polled list
+ m_PolledTimerQueries.append(m_ActiveTimerQueries);
+}
+
+TimerQueryD3D11* TimerQueriesD3D11::CreateTimerQuery()
+{
+ TimerQueryD3D11* query = new TimerQueryD3D11;
+ m_InactiveTimerQueries.push_back(*query);
+ return query;
+}
+
+void TimerQueriesD3D11::AddActiveTimerQuery(TimerQueryD3D11* query)
+{
+ query->RemoveFromList();
+ m_ActiveTimerQueries.push_back(*query);
+}
+
+void TimerQueriesD3D11::PollTimerQueries()
+{
+ for (;;)
+ {
+ if (!PollNextTimerQuery(false))
+ break;
+ }
+}
+
+bool TimerQueriesD3D11::PollNextTimerQuery(bool wait)
+{
+ if (m_PolledTimerQueries.empty())
+ return false;
+
+ TimerQueryD3D11& query = m_PolledTimerQueries.front();
+ if (query.PollResult(m_LastQueryTime, wait))
+ {
+ query.m_Active = false;
+ query.RemoveFromList();
+ m_InactiveTimerQueries.push_back(query);
+ return true;
+ }
+ return false;
+}
+
+TimerQueriesD3D11 g_TimerQueriesD3D11;
+
+#endif
diff --git a/Runtime/GfxDevice/d3d11/TimerQueryD3D11.h b/Runtime/GfxDevice/d3d11/TimerQueryD3D11.h
new file mode 100644
index 0000000..3f46a5c
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/TimerQueryD3D11.h
@@ -0,0 +1,67 @@
+#ifndef TIMERQUERYD3D11_H
+#define TIMERQUERYD3D11_H
+
+#if ENABLE_PROFILER
+
+#include "Runtime/GfxDevice/GfxTimerQuery.h"
+
+class TimerQueriesD3D11;
+
+class TimerQueryD3D11 : public GfxTimerQuery
+{
+public:
+ ~TimerQueryD3D11();
+
+ virtual void Measure();
+ virtual ProfileTimeFormat GetElapsed(UInt32 flags);
+
+ bool PollResult(UInt64& prevTime, bool wait);
+ void SetTimeMultiplier(float tm) { m_TimeMultiplier = tm; }
+
+private:
+ friend TimerQueriesD3D11;
+ TimerQueryD3D11();
+
+ ID3D11Query* m_Query;
+ ProfileTimeFormat m_Time;
+ float m_TimeMultiplier;
+ bool m_Active;
+};
+
+class TimerQueriesD3D11
+{
+public:
+ TimerQueriesD3D11();
+
+ void ReleaseAllQueries();
+ void RecreateAllQueries();
+
+ void BeginTimerQueries();
+ void EndTimerQueries();
+
+ TimerQueryD3D11* CreateTimerQuery();
+
+ void AddActiveTimerQuery(TimerQueryD3D11* query);
+ void PollTimerQueries();
+ bool PollNextTimerQuery(bool wait);
+
+private:
+ enum
+ {
+ kStartTimeQueryCount = 3
+ };
+
+ UInt64 m_LastQueryTime;
+ ID3D11Query* m_DisjointQuery;
+ TimerQueryD3D11* m_StartTimeQueries[kStartTimeQueryCount];
+ int m_StartTimeQueryIndex;
+ typedef List<TimerQueryD3D11> TimerQueryList;
+ TimerQueryList m_InactiveTimerQueries;
+ TimerQueryList m_ActiveTimerQueries;
+ TimerQueryList m_PolledTimerQueries;
+};
+
+extern TimerQueriesD3D11 g_TimerQueriesD3D11;
+
+#endif
+#endif
diff --git a/Runtime/GfxDevice/d3d11/VertexDeclarationsD3D11.cpp b/Runtime/GfxDevice/d3d11/VertexDeclarationsD3D11.cpp
new file mode 100644
index 0000000..e98779b
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/VertexDeclarationsD3D11.cpp
@@ -0,0 +1,282 @@
+#include "UnityPrefix.h"
+#include "VertexDeclarationsD3D11.h"
+#include "D3D11Context.h"
+#include "D3D11ByteCode.h"
+#include "D3D11Utils.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Shaders/VBO.h"
+
+
+extern ID3D11InputLayout* g_ActiveInputLayoutD3D11;
+
+
+bool VertexDeclarationsD3D11::KeyType::operator < (const KeyType& rhs) const
+{
+ if (inputSig != rhs.inputSig)
+ return inputSig < rhs.inputSig;
+ if (extraBits != rhs.extraBits)
+ return extraBits < rhs.extraBits;
+ return memcmp(channels, rhs.channels, sizeof(channels)) < 0;
+}
+
+
+
+VertexDeclarationsD3D11::VertexDeclarationsD3D11()
+{
+}
+
+VertexDeclarationsD3D11::~VertexDeclarationsD3D11()
+{
+ Clear();
+}
+
+static D3D11_INPUT_ELEMENT_DESC kChannelVertexElems[kShaderChannelCount] = {
+ // semantic name, semantic index, format, input slot, aligned byte offset, input slot class, instance data step rate
+ { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "TANGENT", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+};
+static const int kDefaultChannelSizes[kShaderChannelCount] = {
+ 12, // position
+ 12, // normal
+ 4, // color
+ 8, // uv
+ 8, // uv2
+ 16, // tangent
+};
+
+static const int kChannelSkinningCount = 2;
+static D3D11_INPUT_ELEMENT_DESC kChannelSkinning4[kChannelSkinningCount] = {
+ // Stream 1
+ { "BLENDWEIGHT", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "BLENDINDICES", 0, DXGI_FORMAT_R32G32B32A32_SINT, 1, 16, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+};
+
+static D3D11_INPUT_ELEMENT_DESC kChannelSkinning2[kChannelSkinningCount] = {
+ // Stream 1
+ { "BLENDWEIGHT", 0, DXGI_FORMAT_R32G32_FLOAT, 1, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "BLENDINDICES", 0, DXGI_FORMAT_R32G32_SINT, 1, 8, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+};
+
+static D3D11_INPUT_ELEMENT_DESC kChannelSkinning1[kChannelSkinningCount] = {
+ // Stream 1
+ { "BONEINDEX", 0, DXGI_FORMAT_R32_SINT, 1, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+};
+
+static const D3D11_INPUT_ELEMENT_DESC kImmChannelVertexElems[] = {
+ // semantic name, semantic index, format, input slot, aligned byte offset, input slot class, instance data step rate
+ { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "TEXCOORD", 1, DXGI_FORMAT_R32G32B32_FLOAT, 0, 40, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "TEXCOORD", 2, DXGI_FORMAT_R32G32B32_FLOAT, 0, 52, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "TEXCOORD", 3, DXGI_FORMAT_R32G32B32_FLOAT, 0, 64, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "TEXCOORD", 4, DXGI_FORMAT_R32G32B32_FLOAT, 0, 76, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "TEXCOORD", 5, DXGI_FORMAT_R32G32B32_FLOAT, 0, 88, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "TEXCOORD", 6, DXGI_FORMAT_R32G32B32_FLOAT, 0,100, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ { "TEXCOORD", 7, DXGI_FORMAT_R32G32B32_FLOAT, 0,112, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ // feed position as tangent0 data, just in case we use shaders that pretend to want tangents
+ { "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+};
+
+static FORCE_INLINE DXGI_FORMAT GetD3D11VertexDeclType(const ChannelInfo& info)
+{
+ switch (info.format)
+ {
+ case kChannelFormatFloat:
+ {
+ switch (info.dimension)
+ {
+ case 1: return DXGI_FORMAT_R32_FLOAT;
+ case 2: return DXGI_FORMAT_R32G32_FLOAT;
+ case 3: return DXGI_FORMAT_R32G32B32_FLOAT;
+ case 4: return DXGI_FORMAT_R32G32B32A32_FLOAT;
+ }
+ break;
+ }
+ case kChannelFormatFloat16:
+ {
+ switch (info.dimension)
+ {
+ case 2: return DXGI_FORMAT_R16G16_FLOAT;
+ case 4: return DXGI_FORMAT_R16G16B16A16_FLOAT;
+ }
+ break;
+ }
+ case kChannelFormatColor:
+ {
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+ }
+ }
+ Assert("No matching D3D11 vertex decl type!");
+ return DXGI_FORMAT_UNKNOWN;
+}
+
+ID3D11InputLayout* VertexDeclarationsD3D11::GetVertexDecl( UInt32 shaderChannelsMap, void* vertexShaderCode, unsigned vertexShaderLength, bool memExportSkin, unsigned int bonesPerVertex)
+{
+ ChannelInfoArray channels;
+ int offset = 0;
+ for (int i = 0; i < kShaderChannelCount; i++)
+ {
+ ChannelInfo& info = channels[i];
+ if (shaderChannelsMap & (1 << i))
+ {
+ info.stream = 0;
+ info.offset = offset;
+ info.format = VBO::GetDefaultChannelFormat( i );
+ info.dimension = VBO::GetDefaultChannelDimension( i );
+ offset += VBO::GetDefaultChannelByteSize( i );
+ }
+ else
+ info.Reset();
+ }
+ return GetVertexDecl( channels, GetShaderInputSignature(vertexShaderCode, vertexShaderLength), memExportSkin, bonesPerVertex );
+}
+
+
+ID3D11InputLayout* VertexDeclarationsD3D11::GetVertexDecl (const ChannelInfoArray& channels, const InputSignatureD3D11* inputSig, bool streamOutSkin, unsigned int bonesPerVertex)
+{
+ if (!inputSig)
+ {
+ AssertString("DX11 shader input signature is null");
+ return NULL;
+ }
+
+ KeyType key;
+ memcpy(key.channels, channels, sizeof(key.channels));
+ key.extraBits = streamOutSkin ? bonesPerVertex : 0; // Set bones-per-vertex count for memExportSkin
+ key.inputSig = inputSig;
+
+ // already have vertex declaration for this format/shader?
+ VertexDeclMap::iterator it = m_VertexDeclMap.find (key);
+ if( it != m_VertexDeclMap.end() )
+ return it->second;
+
+ // don't have this declaration yet - create one
+ D3D11_INPUT_ELEMENT_DESC elements[kShaderChannelCount + kChannelSkinningCount];
+ int elIndex = 0;
+ for( int chan = 0; chan < kShaderChannelCount; chan++ )
+ {
+ DebugAssert(elIndex < kShaderChannelCount);
+ if (!channels[chan].IsValid() )
+ {
+ ///@TODO: for now, hack in all shader channels to pretend to be there
+ elements[elIndex] = kChannelVertexElems[chan];
+ elements[elIndex].AlignedByteOffset = 0;
+ ++elIndex;
+ continue;
+ }
+ elements[elIndex] = kChannelVertexElems[chan];
+ elements[elIndex].InputSlot = channels[chan].stream;
+ elements[elIndex].AlignedByteOffset = channels[chan].offset;
+ elements[elIndex].Format = GetD3D11VertexDeclType(channels[chan]);
+ ++elIndex;
+ }
+
+ if (streamOutSkin) // Append extra elements required for streamout
+ {
+ switch(bonesPerVertex)
+ {
+ default:
+ case 1:
+ elements[elIndex] = kChannelSkinning1[0];
+ ++elIndex;
+ break;
+ case 2:
+ for (int i = 0; i < kChannelSkinningCount; ++i)
+ {
+ elements[elIndex] = kChannelSkinning2[i];
+ ++elIndex;
+ }
+ break;
+ case 4:
+ for (int i = 0; i < kChannelSkinningCount; ++i)
+ {
+ elements[elIndex] = kChannelSkinning4[i];
+ ++elIndex;
+ }
+ break;
+ }
+ }
+
+ ID3D11InputLayout* decl = NULL;
+ HRESULT hr = GetD3D11Device()->CreateInputLayout (elements, elIndex, inputSig->blob.data(), inputSig->blob.size(), &decl );
+ if( FAILED(hr) ) {
+ AssertString ("Failed to create vertex declaration\n");
+ // TODO: error!
+ }
+ SetDebugNameD3D11 (decl, Format("InputLayout-%d", elIndex));
+ m_VertexDeclMap.insert( std::make_pair( key, decl ) );
+
+ return decl;
+}
+
+ID3D11InputLayout* VertexDeclarationsD3D11::GetImmVertexDecl (const InputSignatureD3D11* inputSig)
+{
+ if (!inputSig)
+ {
+ AssertString("DX11 shader input signature is null");
+ return NULL;
+ }
+
+ // already have vertex declaration for this shader?
+ ImmVertexDeclMap::iterator it = m_ImmVertexDeclMap.find (inputSig);
+ if (it != m_ImmVertexDeclMap.end())
+ return it->second;
+
+ // don't have this declaration yet - create one
+ ID3D11InputLayout* decl = NULL;
+ HRESULT hr = GetD3D11Device()->CreateInputLayout (kImmChannelVertexElems,ARRAY_SIZE(kImmChannelVertexElems), inputSig->blob.data(), inputSig->blob.size(), &decl);
+ if (FAILED(hr))
+ {
+ AssertString ("Failed to create vertex declaration for GL.Begin\n");
+ // TODO: error!
+ }
+ SetDebugNameD3D11 (decl, "InputLayoutImmediate");
+ m_ImmVertexDeclMap.insert(std::make_pair(inputSig, decl));
+
+ return decl;
+}
+
+void VertexDeclarationsD3D11::Clear()
+{
+ g_ActiveInputLayoutD3D11 = NULL;
+
+ for (VertexDeclMap::iterator it = m_VertexDeclMap.begin(); it != m_VertexDeclMap.end(); ++it)
+ {
+ if (it->second) {
+ ULONG refCount = it->second->Release();
+ AssertIf( refCount != 0 );
+ }
+ }
+ m_VertexDeclMap.clear();
+
+ for (ImmVertexDeclMap::iterator it = m_ImmVertexDeclMap.begin(); it != m_ImmVertexDeclMap.end(); ++it)
+ {
+ if (it->second) {
+ ULONG refCount = it->second->Release();
+ AssertIf( refCount != 0 );
+ }
+ }
+ m_ImmVertexDeclMap.clear();
+}
+
+
+const InputSignatureD3D11* VertexDeclarationsD3D11::GetShaderInputSignature (void* code, unsigned length)
+{
+ DXBCChunkHeader* isigChunk = dxbc_find_chunk (code, length, kFOURCC_ISGN);
+ DebugAssert (isigChunk);
+ if (!isigChunk)
+ return NULL;
+
+ InputSignatureD3D11 isig;
+ dxbc_create (&isigChunk, 1, isig.blob);
+
+ InputSignatures::iterator it = m_InputSignatures.insert(isig).first;
+ return &(*it);
+}
diff --git a/Runtime/GfxDevice/d3d11/VertexDeclarationsD3D11.h b/Runtime/GfxDevice/d3d11/VertexDeclarationsD3D11.h
new file mode 100644
index 0000000..cd7e981
--- /dev/null
+++ b/Runtime/GfxDevice/d3d11/VertexDeclarationsD3D11.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include "D3D11Includes.h"
+#include "Runtime/Filters/Mesh/VertexData.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include <map>
+
+struct InputSignatureD3D11
+{
+ dynamic_array<UInt8> blob;
+ bool operator < (const InputSignatureD3D11& o) const
+ {
+ size_t sizeA = blob.size();
+ size_t sizeB = o.blob.size();
+ if (sizeA != sizeB)
+ return sizeA < sizeB;
+ int res = memcmp (blob.data(), o.blob.data(), sizeA);
+ return res < 0;
+ }
+};
+
+class VertexDeclarationsD3D11
+{
+public:
+ VertexDeclarationsD3D11();
+ ~VertexDeclarationsD3D11();
+
+ ID3D11InputLayout* GetVertexDecl( UInt32 shaderChannelsMap, void* vertexShaderCode, unsigned vertexShaderLength, bool streamOutSkin = false, unsigned int bonesPerVertex = 4 );
+ ID3D11InputLayout* GetVertexDecl (const ChannelInfoArray& channels, const InputSignatureD3D11* inputSig, bool streamOutSkin = false, unsigned int bonesPerVertex = 4);
+ ID3D11InputLayout* GetImmVertexDecl (const InputSignatureD3D11* inputSig);
+ void Clear();
+
+ const InputSignatureD3D11* GetShaderInputSignature (void* code, unsigned length);
+
+private:
+ typedef std::set<InputSignatureD3D11> InputSignatures;
+ InputSignatures m_InputSignatures;
+
+ struct KeyType
+ {
+ bool operator < (const KeyType& rhs) const;
+ ChannelInfoArray channels;
+ const InputSignatureD3D11* inputSig;
+ UInt32 extraBits;
+ };
+ typedef UNITY_MAP(kMemVertexData, KeyType, ID3D11InputLayout*) VertexDeclMap;
+ VertexDeclMap m_VertexDeclMap;
+
+ typedef UNITY_MAP(kMemVertexData, const InputSignatureD3D11*, ID3D11InputLayout*) ImmVertexDeclMap;
+ ImmVertexDeclMap m_ImmVertexDeclMap;
+};
diff --git a/Runtime/GfxDevice/null/GfxDeviceNull.cpp b/Runtime/GfxDevice/null/GfxDeviceNull.cpp
new file mode 100644
index 0000000..4981f54
--- /dev/null
+++ b/Runtime/GfxDevice/null/GfxDeviceNull.cpp
@@ -0,0 +1,251 @@
+#include "UnityPrefix.h"
+#include "GfxDeviceNull.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Graphics/RenderSurface.h"
+#include "GfxNullVBO.h"
+#include "Runtime/GfxDevice/GfxDeviceWindow.h"
+#include "External/shaderlab/Library/program.h"
+
+
+static DeviceBlendState g_NullBlendState;
+static DeviceDepthState g_NullDepthState;
+static DeviceStencilState g_NullStencilState;
+static DeviceRasterState g_NullRasterState;
+
+static float g_NullWorldMatrix[16];
+static float g_NullViewMatrix[16];
+static float g_NullProjectionMatrix[16];
+
+static RenderSurfaceBase _ColorDefaultSurface;
+static RenderSurfaceBase _DepthDefaultSurface;
+
+
+void GraphicsCaps::InitNull(void)
+{
+ rendererString = "Null Device";
+ vendorString = "Unity Technologies";
+ driverVersionString = "1.0";
+ fixedVersionString = "NULL 1.0 [1.0]";
+ driverLibraryString = "(null)";
+ videoMemoryMB = 64.0f;
+ rendererID = 0;
+ vendorID = 0;
+
+ printf_console( "NullGfxDevice:\n" );
+ printf_console( " Version: %s\n", fixedVersionString.c_str() );
+ printf_console( " Renderer: %s\n", rendererString.c_str() );
+ printf_console( " Vendor: %s\n", vendorString.c_str() );
+
+ shaderCaps = kShaderLevel2;
+
+ maxLights = 8;
+ hasAnisoFilter = false;
+ maxAnisoLevel = 0;
+ hasMipLevelBias = false;
+
+ hasMultiSample = false;
+
+ hasBlendSquare = false;
+ hasSeparateAlphaBlend = false;
+
+ hasS3TCCompression = false;
+
+ hasAutoMipMapGeneration = false;
+
+ maxTexImageUnits = 2;
+ maxTexCoords = 2;
+
+
+ maxTexUnits = 2;
+
+ maxTextureSize = 1024;
+ maxCubeMapSize = 0;
+ maxRenderTextureSize = 0;
+
+ for (int i = 0; i < kRTFormatCount; ++i)
+ {
+ supportsRenderTextureFormat[i] = false;
+ }
+
+ has3DTexture = false;
+ npotRT = npot = kNPOTNone;
+
+ hasRenderToTexture = false;
+ hasRenderToCubemap = false;
+ hasRenderTargetStencil = false;
+ hasTwoSidedStencil = false;
+ hasHighPrecisionTextureCombiners = false;
+ hasNativeDepthTexture = false;
+ hasStencilInDepthTexture = false;
+ hasNativeShadowMap = false;
+
+ needsToSwizzleVertexColors = false;
+}
+
+
+GfxDevice* CreateNullGfxDevice()
+{
+ ::gGraphicsCaps.InitNull();
+
+ return UNITY_NEW_AS_ROOT(GfxDeviceNull(), kMemGfxDevice, "NullGfxDevice","");
+}
+
+GfxDeviceNull::GfxDeviceNull(void)
+: dynamicVBO(NULL)
+{
+ InvalidateState();
+ ResetFrameStats();
+
+ m_Renderer = kGfxRendererNull;
+ m_UsesOpenGLTextureCoords = false;
+ m_UsesHalfTexelOffset = false;
+ m_MaxBufferedFrames = -1; // no limiting
+
+ RenderSurfaceBase_InitColor(_ColorDefaultSurface);
+ _ColorDefaultSurface.backBuffer = true;
+ SetBackBufferColorSurface(&_ColorDefaultSurface);
+
+ RenderSurfaceBase_InitDepth(_DepthDefaultSurface);
+ _DepthDefaultSurface.backBuffer = true;
+ SetBackBufferDepthSurface(&_DepthDefaultSurface);
+}
+
+GfxDeviceNull::~GfxDeviceNull(void)
+{
+ delete this->dynamicVBO;
+}
+
+void GfxDeviceNull::GetMatrix( float outMatrix[16] ) const
+{
+ for( int i = 0; i < 16; ++i )
+ outMatrix[i] = 0.0f;
+}
+
+
+void GfxDeviceNull::GetViewport( int* port ) const
+{
+ AssertIf(!port);
+ port[0] = 0;
+ port[1] = 0;
+ port[2] = 1;
+ port[3] = 1;
+}
+
+
+void GfxDeviceNull::GetScissorRect( int scissor[4] ) const
+{
+ AssertIf(!scissor);
+ scissor[0] = 0;
+ scissor[1] = 0;
+ scissor[2] = 1;
+ scissor[3] = 1;
+}
+
+TextureCombinersHandle GfxDeviceNull::CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular )
+{
+ return TextureCombinersHandle(this); // just have to pass non-NULL to the handle
+}
+
+void GfxDeviceNull::DeleteTextureCombiners( TextureCombinersHandle& textureCombiners )
+{
+ textureCombiners.Reset();
+}
+
+void GfxDeviceNull::DestroySubProgram( ShaderLab::SubProgram* subprogram )
+{
+ delete subprogram;
+}
+
+VBO* GfxDeviceNull::CreateVBO()
+{
+ VBO* vbo = new GfxNullVBO();
+ OnCreateVBO(vbo);
+ return vbo;
+}
+
+void GfxDeviceNull::DeleteVBO( VBO* vbo )
+{
+ AssertIf(!vbo);
+ OnDeleteVBO(vbo);
+ delete vbo;
+}
+
+DynamicVBO& GfxDeviceNull::GetDynamicVBO()
+{
+ if (NULL == this->dynamicVBO)
+ {
+ this->dynamicVBO = new GfxDynamicNullVBO();
+ }
+
+ return *this->dynamicVBO;
+}
+
+
+
+bool GfxDeviceNull::CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 )
+{
+ return true;
+}
+
+
+bool GfxDeviceNull::ReadbackImage( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY )
+{
+ return true;
+}
+
+DeviceBlendState* GfxDeviceNull::CreateBlendState(const GfxBlendState& state)
+{
+ return & g_NullBlendState;
+}
+
+DeviceDepthState* GfxDeviceNull::CreateDepthState(const GfxDepthState& state)
+{
+ return & g_NullDepthState;
+}
+
+DeviceStencilState* GfxDeviceNull::CreateStencilState(const GfxStencilState& state)
+{
+ return & g_NullStencilState;
+}
+
+DeviceRasterState* GfxDeviceNull::CreateRasterState(const GfxRasterState& state)
+{
+ return & g_NullRasterState;
+}
+
+const float* GfxDeviceNull::GetWorldMatrix() const
+{
+ return g_NullWorldMatrix;
+}
+
+const float* GfxDeviceNull::GetViewMatrix() const
+{
+ return g_NullViewMatrix;
+}
+
+const float* GfxDeviceNull::GetProjectionMatrix() const
+{
+ return g_NullProjectionMatrix;
+}
+
+
+
+RenderSurfaceHandle GfxDeviceNull::CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags)
+{
+ return RenderSurfaceHandle(&_ColorDefaultSurface);
+}
+RenderSurfaceHandle GfxDeviceNull::CreateRenderDepthSurface (TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags)
+{
+ return RenderSurfaceHandle(&_DepthDefaultSurface);
+}
+
+// -------- editor only functions
+
+#if UNITY_EDITOR
+
+GfxDeviceWindow* GfxDeviceNull::CreateGfxWindow( HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias )
+{
+ return new GfxDeviceWindow(window,width, height, depthFormat, antiAlias);
+}
+
+#endif
diff --git a/Runtime/GfxDevice/null/GfxDeviceNull.h b/Runtime/GfxDevice/null/GfxDeviceNull.h
new file mode 100644
index 0000000..d96d016
--- /dev/null
+++ b/Runtime/GfxDevice/null/GfxDeviceNull.h
@@ -0,0 +1,158 @@
+#pragma once
+
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+
+
+class GfxDeviceNull : public GfxDevice
+{
+public:
+ GfxDeviceNull();
+ GFX_API ~GfxDeviceNull();
+
+ GFX_API void InvalidateState() { }
+ #if GFX_DEVICE_VERIFY_ENABLE
+ GFX_API void VerifyState() { }
+ #endif
+
+ GFX_API void Clear (UInt32 clearFlags, const float color[4], float depth, int stencil) { }
+ GFX_API void SetUserBackfaceMode( bool enable ) { }
+ GFX_API void SetWireframe(bool wire) { }
+ GFX_API bool GetWireframe() const { return false; }
+ GFX_API void SetInvertProjectionMatrix( bool enable ) { }
+ GFX_API bool GetInvertProjectionMatrix() const { return false; }
+
+ GFX_API void SetWorldMatrix( const float matrix[16] ) {};
+ GFX_API void SetViewMatrix( const float matrix[16] ) {};
+ GFX_API void SetProjectionMatrix (const Matrix4x4f& matrix) { }
+ GFX_API void GetMatrix( float outMatrix[16] ) const;
+
+ GFX_API const float* GetWorldMatrix() const;
+ GFX_API const float* GetViewMatrix() const;
+ GFX_API const float* GetProjectionMatrix() const;
+ GFX_API const float* GetDeviceProjectionMatrix() const { return GetProjectionMatrix(); }
+
+ GFX_API void SetNormalizationBackface( NormalizationMode mode, bool backface ) { }
+ GFX_API void SetAlpha( BlendMode src, BlendMode dst, CompareFunction mode, float value, bool alphaToMask ) { }
+ GFX_API void SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial ) {}
+ GFX_API void SetMaterial( const float ambient[4], const float diffuse[4], const float specular[4], const float emissive[4], const float shininess ) { }
+ GFX_API void SetColor( const float color[4] ) { }
+ GFX_API void SetViewport( int x, int y, int width, int height ) { }
+ GFX_API void GetViewport( int* port ) const;
+
+ GFX_API void SetScissorRect( int x, int y, int width, int height ) { }
+ GFX_API void DisableScissor() { }
+ GFX_API bool IsScissorEnabled() const { return false; }
+ GFX_API void GetScissorRect( int values[4] ) const;
+ GFX_API void DiscardContents (RenderSurfaceHandle& rs) {}
+
+ GFX_API TextureCombinersHandle CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular );
+ GFX_API void DeleteTextureCombiners( TextureCombinersHandle& texturesCombiners );
+ GFX_API void SetTextureCombiners( TextureCombinersHandle texturesCombiners, const ShaderLab::PropertySheet* props ) { }
+
+ GFX_API void SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias) { }
+ GFX_API void SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace ) { }
+ GFX_API void SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16]) { }
+ GFX_API void SetTextureName ( TextureID texture, const char* name ) { }
+
+ GFX_API void SetShadersMainThread( ShaderLab::SubProgram* programs[kShaderTypeCount], const ShaderLab::PropertySheet* props ) { }
+ GFX_API bool IsShaderActive( ShaderType type ) const { return false; }
+ GFX_API void DestroySubProgram( ShaderLab::SubProgram* subprogram );
+
+ GFX_API void DisableLights( int startLight ) { }
+ GFX_API void SetLight( int light, const GfxVertexLight& data) { }
+ GFX_API void SetAmbient( const float ambient[4] ) { }
+
+ GFX_API void EnableFog (const GfxFogParams& fog) { }
+ GFX_API void DisableFog() { }
+
+ GFX_API GPUSkinningInfo *CreateGPUSkinningInfo() { return NULL; }
+ GFX_API void DeleteGPUSkinningInfo(GPUSkinningInfo *info) { AssertBreak(false); }
+ GFX_API void SkinOnGPU( GPUSkinningInfo * info, bool lastThisFrame ) { AssertBreak(false); }
+ GFX_API void UpdateSkinSourceData(GPUSkinningInfo *info, const void *vertData, const BoneInfluence *skinData, bool dirty) { AssertBreak(false); }
+ GFX_API void UpdateSkinBonePoses(GPUSkinningInfo *info, const int boneCount, const Matrix4x4f* poses) { AssertBreak(false); }
+
+ GFX_API VBO* CreateVBO();
+ GFX_API void DeleteVBO( VBO* vbo );
+ GFX_API DynamicVBO& GetDynamicVBO();
+
+ GFX_API RenderSurfaceHandle CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags);
+ GFX_API RenderSurfaceHandle CreateRenderDepthSurface (TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags);
+ GFX_API void DestroyRenderSurface (RenderSurfaceHandle& rs) {}
+ GFX_API void SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face = kCubeFaceUnknown) {}
+ GFX_API void ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle) { }
+ GFX_API RenderSurfaceHandle GetActiveRenderColorSurface (int index) { return RenderSurfaceHandle(); }
+ GFX_API RenderSurfaceHandle GetActiveRenderDepthSurface () { return RenderSurfaceHandle(); }
+ GFX_API void SetSurfaceFlags(RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags) { }
+
+ GFX_API void UploadTexture2D( TextureID texture, TextureDimension dimension, UInt8* srcData, int srcSize, int width, int height, TextureFormat format, int mipCount, UInt32 uploadFlags, int skipMipLevels, TextureUsageMode usageMode, TextureColorSpace colorSpace ) { }
+ GFX_API void UploadTextureSubData2D( TextureID texture, UInt8* srcData, int srcSize, int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace ) { }
+ GFX_API void UploadTextureCube( TextureID texture, UInt8* srcData, int srcSize, int faceDataSize, int size, TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace ) { }
+ GFX_API void UploadTexture3D( TextureID texture, UInt8* srcData, int srcSize, int width, int height, int depth, TextureFormat format, int mipCount, UInt32 uploadFlags ) { }
+ GFX_API void DeleteTexture( TextureID texture ) { }
+
+ GFX_API PresentMode GetPresentMode() { return kPresentBeforeUpdate; }
+
+ GFX_API void BeginFrame() { m_InsideFrame = true; }
+ GFX_API void EndFrame() { m_InsideFrame = false; }
+ GFX_API void PresentFrame() { }
+ GFX_API bool IsValidState() { return true; }
+
+ GFX_API void FinishRendering() { }
+
+ // Immediate mode rendering
+ GFX_API void ImmediateVertex( float x, float y, float z ) { }
+ GFX_API void ImmediateNormal( float x, float y, float z ) { }
+ GFX_API void ImmediateColor( float r, float g, float b, float a ) { }
+ GFX_API void ImmediateTexCoordAll( float x, float y, float z ) { }
+ GFX_API void ImmediateTexCoord( int unit, float x, float y, float z ) { }
+ GFX_API void ImmediateBegin( GfxPrimitiveType type ) { }
+ GFX_API void ImmediateEnd() { }
+
+ GFX_API bool CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 );
+ GFX_API bool ReadbackImage( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY );
+ GFX_API void GrabIntoRenderTexture( RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height ) { }
+
+ GFX_API void BeforeDrawCall( bool immediateMode ) { }
+
+ GFX_API void SetBlendState(const DeviceBlendState* state, float alphaRef) {}
+ GFX_API void SetRasterState(const DeviceRasterState* state) {}
+ GFX_API void SetDepthState(const DeviceDepthState* state) {}
+ GFX_API void SetStencilState(const DeviceStencilState* state, int stencilRef) {};
+ GFX_API void SetSRGBWrite (const bool) {};
+ GFX_API bool GetSRGBWrite () {return false;};
+
+ GFX_API DeviceBlendState* CreateBlendState(const GfxBlendState& state);
+ GFX_API DeviceDepthState* CreateDepthState(const GfxDepthState& state);
+ GFX_API DeviceStencilState* CreateStencilState(const GfxStencilState& state);
+ GFX_API DeviceRasterState* CreateRasterState(const GfxRasterState& state);
+
+ GFX_API bool IsPositionRequiredForTexGen (int texStageIndex) const { return false; }
+ GFX_API bool IsNormalRequiredForTexGen (int texStageIndex) const { return false; }
+ GFX_API bool IsPositionRequiredForTexGen() const { return false; }
+ GFX_API bool IsNormalRequiredForTexGen() const { return false; }
+
+ GFX_API int GetCurrentTargetWidth() const { return 0; }
+ GFX_API int GetCurrentTargetHeight() const { return 0; }
+ GFX_API void SetCurrentTargetSize(int width, int height) { }
+
+ #if ENABLE_PROFILER
+ GFX_API GfxTimerQuery* CreateTimerQuery() { return NULL; }
+ GFX_API void DeleteTimerQuery(GfxTimerQuery* query) {}
+ GFX_API void BeginTimerQueries() {}
+ GFX_API void EndTimerQueries() {}
+ #endif
+ #if UNITY_EDITOR
+ GFX_API void SetAntiAliasFlag( bool aa ) { }
+ GFX_API void DrawUserPrimitives( GfxPrimitiveType type, int vertexCount, UInt32 vertexChannels, const void* data, int stride ) { }
+ GFX_API int GetCurrentTargetAA() const { return 0; }
+ GFX_API GfxDeviceWindow* CreateGfxWindow( HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias ) ;
+ #endif
+ #if GFX_USES_VIEWPORT_OFFSET
+ GFX_API void SetViewportOffset( float x, float y ){ }
+ GFX_API void GetViewportOffset( float &x, float &y ) const{ x = y = 0; }
+ #endif
+
+private:
+ DynamicVBO *dynamicVBO;
+};
diff --git a/Runtime/GfxDevice/null/GfxNullVBO.cpp b/Runtime/GfxDevice/null/GfxNullVBO.cpp
new file mode 100644
index 0000000..b624160
--- /dev/null
+++ b/Runtime/GfxDevice/null/GfxNullVBO.cpp
@@ -0,0 +1,107 @@
+#include "UnityPrefix.h"
+#include "Runtime/Shaders/VBO.h"
+#include "GfxNullVBO.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+
+
+void GfxNullVBO::UpdateVertexData( const VertexBufferData& buffer )
+{
+ BufferAccessibleVertexData(buffer);
+ }
+
+void GfxNullVBO::UpdateIndexData (const IndexBufferData& buffer)
+{
+}
+
+void GfxNullVBO::DrawVBO (const ChannelAssigns& channels, UInt32 firstIndexByte, UInt32 indexCount, GfxPrimitiveType topology, UInt32 firstVertex, UInt32 vertexCount )
+{
+}
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+ void GfxNullVBO::DrawCustomIndexed( const ChannelAssigns& channels, void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount )
+ {
+ }
+#endif
+
+
+bool GfxDynamicNullVBO::GetChunk( UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode renderMode, void** outVB, void** outIB )
+{
+ AssertIf(this->m_LendedChunk);
+ AssertIf((maxVertices >= 65536) || (maxIndices >= (65536 * 3)));
+ bool indexed = IsIndexed(renderMode);
+ DebugAssertIf(indexed && (outIB == NULL));
+ DebugAssertIf(!indexed && ((maxIndices != 0) || (outIB != NULL)));
+
+ this->m_LendedChunk = true;
+ this->m_LastChunkShaderChannelMask = shaderChannelMask;
+ this->m_LastRenderMode = renderMode;
+
+ if (maxVertices == 0)
+ {
+ maxVertices = 8;
+ }
+
+ this->m_LastChunkStride = 0;
+
+ for (int i = 0; i < kShaderChannelCount; ++i)
+ {
+ if (shaderChannelMask & (1 << i))
+ {
+ this->m_LastChunkStride += GfxNullVBO::GetDefaultChannelByteSize(i);
+ }
+ }
+
+ //
+
+ DebugAssertIf(NULL == outVB);
+
+ UInt32 vbCapacity = (maxVertices * this->m_LastChunkStride);
+
+ if (vbCapacity > this->vertexBufferSize)
+ {
+ delete[] this->vertexBuffer;
+ this->vertexBuffer = new UInt8[vbCapacity];
+ this->vertexBufferSize = vbCapacity;
+ }
+
+ *outVB = this->vertexBuffer;
+
+ //
+
+ if (maxIndices && indexed)
+ {
+ UInt32 ibCapacity = maxIndices * kVBOIndexSize;
+
+ if (ibCapacity > this->indexBufferSize)
+ {
+ delete[] this->indexBuffer;
+ this->indexBuffer = new UInt8[ibCapacity];
+ this->indexBufferSize = ibCapacity;
+ }
+
+ *outIB = this->indexBuffer;
+ }
+
+ //
+
+ return true;
+}
+
+void GfxDynamicNullVBO::ReleaseChunk( UInt32 actualVertices, UInt32 actualIndices )
+{
+ AssertIf(!m_LendedChunk);
+ this->m_LendedChunk = false;
+
+ this->m_LastChunkVertices = actualVertices;
+ this->m_LastChunkIndices = actualIndices;
+
+ if (!actualVertices || (IsIndexed(m_LastRenderMode) && !actualIndices))
+ {
+ this->m_LastChunkShaderChannelMask = 0;
+ }
+}
+
+void GfxDynamicNullVBO::DrawChunk (const ChannelAssigns& channels)
+{
+}
diff --git a/Runtime/GfxDevice/null/GfxNullVBO.h b/Runtime/GfxDevice/null/GfxNullVBO.h
new file mode 100644
index 0000000..ce4d0fa
--- /dev/null
+++ b/Runtime/GfxDevice/null/GfxNullVBO.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include "Runtime/Shaders/BufferedVBO.h"
+
+
+class GfxNullVBO : public BufferedVBO
+{
+public:
+ inline GfxNullVBO(void) {}
+ virtual ~GfxNullVBO(void) {}
+
+ virtual void UpdateVertexData( const VertexBufferData& buffer );
+ virtual void UpdateIndexData (const IndexBufferData& buffer);
+ virtual void DrawVBO (const ChannelAssigns& channels, UInt32 firstIndexByte, UInt32 indexCount, GfxPrimitiveType topology, UInt32 firstVertex, UInt32 vertexCount );
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ virtual void DrawCustomIndexed( const ChannelAssigns& channels, void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount );
+ #endif
+
+ virtual bool IsVertexBufferLost() const { return false; }
+
+ virtual int GetRuntimeMemorySize() const { return 0; }
+};
+
+
+
+class GfxDynamicNullVBO :
+ public DynamicVBO
+{
+private:
+ UInt8 *vertexBuffer;
+ UInt32 vertexBufferSize;
+ UInt8 *indexBuffer;
+ UInt32 indexBufferSize;
+
+public:
+ inline GfxDynamicNullVBO(void) :
+ vertexBuffer(NULL),
+ vertexBufferSize(0),
+ indexBuffer(NULL),
+ indexBufferSize(0)
+ {
+ }
+
+ virtual ~GfxDynamicNullVBO(void)
+ {
+ delete[] this->vertexBuffer;
+ delete[] this->indexBuffer;
+ }
+
+ virtual bool GetChunk( UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode renderMode, void** outVB, void** outIB );
+ virtual void ReleaseChunk( UInt32 actualVertices, UInt32 actualIndices );
+ virtual void DrawChunk (const ChannelAssigns& channels);
+
+}; \ No newline at end of file
diff --git a/Runtime/GfxDevice/opengl/ARBVBO.cpp b/Runtime/GfxDevice/opengl/ARBVBO.cpp
new file mode 100644
index 0000000..f90415b
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/ARBVBO.cpp
@@ -0,0 +1,611 @@
+#include "UnityPrefix.h"
+#include "ARBVBO.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "GLAssert.h"
+#include "ChannelsGL.h"
+#include "UnityGL.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+#include "Runtime/Misc/Allocator.h"
+
+
+extern GLenum kTopologyGL[kPrimitiveTypeCount];
+
+
+// -----------------------------------------------------------------------------
+
+// 0 = no stats, 1 = overview stats, 2 = detailed stats
+#define DEBUG_GL_VBO 0
+
+#if DEBUG_GL_VBO == 2
+#define LOGVBO(x) printf_console( "vbo: " x "\n" )
+#else
+#define LOGVBO(x)
+#endif
+
+
+
+#if DEBUG_GL_VBO
+static int gActiveVBOs = 0;
+static int gActiveVBs = 0;
+static int gActiveIBs = 0;
+static int gUpdatedVBs = 0;
+static int gUpdatedIBs = 0;
+#endif
+
+
+// In VBOs, the pointers are just offsets. Just fill start
+// of VBO with this number of bytes so that the real offsets are never null.
+const int kDummyVBStartBytes = 32;
+
+
+// Cache the current vertex/index buffers. Don't ever call
+// glBindBuffer from anywhere, always use these calls!
+// Also make sure to call UnbindVertexBuffersGL() whenever you're starting
+// immediate mode rendering or drawing from plain arrays.
+
+static int s_VBOCurrentVB = 0;
+static int s_VBOCurrentIB = 0;
+
+void BindARBVertexBuffer( int id )
+{
+ if( s_VBOCurrentVB != id )
+ {
+ OGL_CALL(glBindBufferARB( GL_ARRAY_BUFFER_ARB, id ));
+ s_VBOCurrentVB = id;
+ }
+
+ #if GFX_DEVICE_VERIFY_ENABLE
+ #ifndef DUMMY_OPENGL_CALLS
+ int glbuffer;
+ glGetIntegerv( GL_ARRAY_BUFFER_BINDING_ARB, &glbuffer );
+ if( glbuffer != id )
+ {
+ ErrorString( Format( "VBO vertex buffer binding differs from cache (%i != %i)\n", glbuffer, id ) );
+ }
+ #endif
+ #endif
+}
+
+void BindARBIndexBuffer( int id )
+{
+ if( s_VBOCurrentIB != id )
+ {
+ OGL_CALL(glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, id ));
+ s_VBOCurrentIB = id;
+ }
+
+ #if GFX_DEVICE_VERIFY_ENABLE
+ #ifndef DUMMY_OPENGL_CALLS
+ int glbuffer;
+ glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &glbuffer );
+ if( glbuffer != id )
+ {
+ ErrorString( Format( "VBO index buffer binding differs from cache (%i != %i)\n", glbuffer, id ) );
+ }
+ #endif
+ #endif
+}
+
+void UnbindVertexBuffersGL()
+{
+ OGL_CALL(glBindBufferARB( GL_ARRAY_BUFFER_ARB, 0 ));
+ OGL_CALL(glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, 0 ));
+ s_VBOCurrentVB = 0;
+ s_VBOCurrentIB = 0;
+}
+
+
+// -----------------------------------------------------------------------------
+
+ARBVBO::ARBVBO()
+: m_VertexCount(0)
+, m_VertexBindID(0)
+, m_IndexBindID(0)
+, m_VBSize(0)
+, m_IBSize(0)
+{
+}
+
+ARBVBO::~ARBVBO ()
+{
+ if( m_VertexBindID )
+ {
+ if (m_VertexBindID == s_VBOCurrentVB)
+ s_VBOCurrentVB = 0;
+
+ glDeleteBuffersARB( 1, (GLuint*)&m_VertexBindID );
+ }
+
+ if( m_IndexBindID )
+ {
+ if (m_IndexBindID == s_VBOCurrentIB)
+ s_VBOCurrentIB = 0;
+
+ glDeleteBuffersARB( 1, (GLuint*)&m_IndexBindID );
+ }
+}
+
+void ARBVBO::VerifyVertexBuffer()
+{
+ #if GFX_DEVICE_VERIFY_ENABLE && !defined(DUMMY_OPENGL_CALLS)
+ int glbuffer;
+ glGetIntegerv( GL_ARRAY_BUFFER_BINDING_ARB, &glbuffer );
+ if( glbuffer != s_VBOCurrentVB )
+ {
+ ErrorString( Format( "VBO vertex buffer binding differs from cache (%i != %i)\n", glbuffer, s_VBOCurrentVB ) );
+ }
+ glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &glbuffer );
+ if( glbuffer != s_VBOCurrentIB )
+ {
+ ErrorString( Format( "VBO vertex buffer binding differs from cache (%i != %i)\n", glbuffer, s_VBOCurrentIB ) );
+ }
+ #endif
+}
+
+
+void ARBVBO::UpdateIndexBufferData (const IndexBufferData& sourceData)
+{
+ if( !sourceData.indices )
+ {
+ return;
+ }
+
+ LOGVBO( "Update IB" );
+ UInt8* buffer = (UInt8*)glMapBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB );
+ if( !buffer )
+ {
+ LOGVBO( "Error mapping IB!" );
+ return; // TBD: error!
+ }
+
+ // Setup index buffer
+ memcpy (buffer, sourceData.indices, sourceData.count * kVBOIndexSize);
+
+ glUnmapBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB );
+
+ #if GFX_DEVICE_VERIFY_ENABLE && !defined(DUMMY_OPENGL_CALLS)
+ int glbuffer;
+ glGetIntegerv( GL_ARRAY_BUFFER_BINDING_ARB, &glbuffer );
+ if( glbuffer != s_VBOCurrentVB )
+ {
+ ErrorString( Format( "VBO vertex buffer binding differs from cache (%i != %i)\n", glbuffer, s_VBOCurrentVB ) );
+ }
+ glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &glbuffer );
+ if( glbuffer != s_VBOCurrentIB )
+ {
+ ErrorString( Format( "VBO vertex buffer binding differs from cache (%i != %i)\n", glbuffer, s_VBOCurrentIB ) );
+ }
+ #endif
+}
+
+bool ARBVBO::MapVertexStream( VertexStreamData& outData, unsigned stream )
+{
+ Assert(stream == 0);
+ DebugAssertIf( m_VertexBindID == 0 );
+ AssertIf( m_IsStreamMapped[stream] );
+ m_IsStreamMapped[stream] = true;
+ const StreamInfo& info = m_Streams[stream];
+ int streamSize = m_VertexCount * info.stride;
+
+ LOGVBO( "Map VB" );
+ BindARBVertexBuffer( m_VertexBindID );
+
+ // Check if there are other streams in buffer
+ bool mapRange = false;
+ for (int s = 0; s < kMaxVertexStreams && !mapRange; s++)
+ if (s != stream && m_Streams[s].channelMask)
+ mapRange = true;
+
+ bool isDynamic = (m_StreamModes[stream] != kStreamModeDynamic);
+
+ UInt8* buffer = NULL;
+ if (!UNITY_OSX && gGraphicsCaps.gl.hasArbMapBufferRange)
+ {
+ GLbitfield access = GL_MAP_WRITE_BIT;
+ if (isDynamic)
+ {
+ // With streams we invalidate the range but preserve the rest of the buffer
+ access |= mapRange ? GL_MAP_INVALIDATE_RANGE_BIT : GL_MAP_INVALIDATE_BUFFER_BIT;
+ }
+ buffer = (UInt8*)glMapBufferRange( GL_ARRAY_BUFFER_ARB, info.offset, streamSize, access );
+ }
+ else
+ {
+ // Wipe vertex buffer if we rewrite the whole thing
+ if (isDynamic && !mapRange)
+ glBufferDataARB( GL_ARRAY_BUFFER_ARB, m_VBSize, NULL, GL_DYNAMIC_DRAW_ARB );
+
+ #if UNITY_OSX
+ // Enable explicit flushing of modified data
+ if (gGraphicsCaps.gl.hasAppleFlushBufferRange)
+ glBufferParameteriAPPLE(GL_ARRAY_BUFFER, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE);
+ #endif
+
+ buffer = (UInt8*)glMapBufferARB( GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB );
+
+ // We got the start of the buffer so apply offset
+ if (buffer)
+ buffer += info.offset;
+ }
+
+ if( !buffer )
+ {
+ LOGVBO( "Error mapping VB!" );
+ return false;
+ }
+
+ outData.buffer = buffer;
+ outData.channelMask = info.channelMask;
+ outData.stride = info.stride;
+ outData.vertexCount = m_VertexCount;
+
+ #if GFX_DEVICE_VERIFY_ENABLE && !defined(DUMMY_OPENGL_CALLS)
+ int glbuffer;
+ glGetIntegerv( GL_ARRAY_BUFFER_BINDING_ARB, &glbuffer );
+ if( glbuffer != s_VBOCurrentVB )
+ {
+ ErrorString( Format( "VBO vertex buffer binding differs from cache (%i != %i)\n", glbuffer, s_VBOCurrentVB ) );
+ }
+ glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &glbuffer );
+ if( glbuffer != s_VBOCurrentIB )
+ {
+ ErrorString( Format( "VBO vertex buffer binding differs from cache (%i != %i)\n", glbuffer, s_VBOCurrentIB ) );
+ }
+ #endif
+
+ GetRealGfxDevice().GetFrameStats().AddUploadVBO( streamSize );
+
+ return true;
+}
+
+void ARBVBO::UnmapVertexStream( unsigned stream )
+{
+ AssertIf( !m_IsStreamMapped[stream] );
+ m_IsStreamMapped[stream] = false;
+
+ // Important: bind the needed buffer. Mostly because of multithreaded skinning, the code does not necessarily
+ // follow the pattern of bind,map,unmap, bind,map,unmap.
+ BindARBVertexBuffer( m_VertexBindID );
+
+ #if UNITY_OSX
+ // We disabled implicit flushing on unmap, explicitly flush the range we wrote to
+ const StreamInfo& info = m_Streams[stream];
+ int streamSize = m_VertexCount * info.stride;
+ if (gGraphicsCaps.gl.hasAppleFlushBufferRange)
+ glFlushMappedBufferRangeAPPLE(GL_ARRAY_BUFFER, info.offset, streamSize);
+ #endif
+
+ glUnmapBufferARB( GL_ARRAY_BUFFER_ARB );
+}
+
+
+void ARBVBO::DrawVBO (const ChannelAssigns& channels, UInt32 firstIndexByte, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 firstVertex, UInt32 vertexCount )
+{
+ // just return if no indices
+ if (m_IBSize == 0)
+ return;
+
+ BindARBIndexBuffer( m_IndexBindID );
+
+ // With an index buffer bound OpenGL interprets pointer as an offset
+ DrawInternal(channels, reinterpret_cast<const void*>(firstIndexByte), indexCount, topology, vertexCount);
+}
+
+void ARBVBO::DrawCustomIndexed( const ChannelAssigns& channels, void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount )
+{
+ BindARBIndexBuffer( 0 );
+
+ DrawInternal(channels, indices, indexCount, topology, drawVertexCount);
+}
+
+void ARBVBO::DrawInternal( const ChannelAssigns& channels, const void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 drawVertexCount )
+{
+ // setup VBO
+ DebugAssertIf( IsAnyStreamMapped() );
+ DebugAssertIf( m_VertexBindID == 0 || m_IndexBindID == 0 );
+
+ BindARBVertexBuffer( m_VertexBindID );
+
+ ClearActiveChannelsGL();
+ UInt32 targetMap = channels.GetTargetMap();
+ for( int i = 0; i < kVertexCompCount; ++i )
+ {
+ if( !( targetMap & (1<<i) ) )
+ continue;
+ ShaderChannel src = channels.GetSourceForTarget( (VertexComponent)i );
+ if( !m_Channels[src].IsValid() )
+ continue;
+
+ SetChannelDataGL( m_Channels[src], m_Streams, (VertexComponent)i );
+ }
+ GfxDevice& device = GetRealGfxDevice();
+ ActivateChannelsGL();
+ device.BeforeDrawCall( false );
+
+ // draw
+ UInt16* indices16 = (UInt16*)indices;
+ static UInt32 maxIndices = 63000/6*6;
+ // must render in multiples of 3 (triangles) and 2 (tri strips)
+ UInt32 offset = 0;
+ while( offset < indexCount )
+ {
+ int drawIndices = std::min( indexCount - offset, maxIndices );
+ OGL_CALL(glDrawElements(kTopologyGL[topology], drawIndices, GL_UNSIGNED_SHORT, &indices16[offset] ));
+ offset += drawIndices;
+ if (offset < indexCount)
+ {
+ if (topology == kPrimitiveTriangleStripDeprecated)
+ offset -= 2; // primitives overlap by 2 indices
+ else if (topology == kPrimitiveLineStrip)
+ offset -= 1; // primitives overlap by 2 indices
+ }
+ }
+
+ device.GetFrameStats().AddDrawCall (GetPrimitiveCount(indexCount, topology, true), drawVertexCount);
+}
+
+
+
+void ARBVBO::UpdateVertexData( const VertexBufferData& buffer )
+{
+ std::copy(buffer.channels, buffer.channels + kShaderChannelCount, m_Channels);
+ std::copy(buffer.streams, buffer.streams + kMaxVertexStreams, m_Streams);
+
+ int bufferMode = GL_STATIC_DRAW_ARB;
+ // Use dynamic if uploading the second time
+ // Or when forced to dynamic
+ if (GetVertexStreamMode(0) == kStreamModeNoAccess && m_VertexBindID)
+ bufferMode = GL_DYNAMIC_DRAW_ARB;
+ else if (GetVertexStreamMode(0) == kStreamModeDynamic)
+ bufferMode = GL_DYNAMIC_DRAW_ARB;
+
+ if( !m_VertexBindID )
+ glGenBuffersARB( 1, (GLuint*)&m_VertexBindID );
+
+ BindARBVertexBuffer( m_VertexBindID );
+ glBufferDataARB( GL_ARRAY_BUFFER_ARB, buffer.bufferSize, buffer.buffer, bufferMode );
+ GetRealGfxDevice().GetFrameStats().AddUploadVBO( buffer.bufferSize );
+ m_VBSize = buffer.bufferSize;
+ m_VertexCount = buffer.vertexCount;
+
+ VerifyVertexBuffer();
+
+ DebugAssertIf(m_VertexBindID == 0);
+}
+
+void ARBVBO::UpdateIndexData (const IndexBufferData& buffer)
+{
+ int buffersize = CalculateIndexBufferSize(buffer);
+ m_IBSize = buffersize;
+
+ if (m_IBSize == 0)
+ {
+ Assert (buffer.count == 0);
+ return;
+ }
+
+ if( !m_IndexBindID )
+ {
+ // initially, generate buffer, set its size and static usage
+ glGenBuffersARB( 1, (GLuint*)&m_IndexBindID );
+ BindARBIndexBuffer( m_IndexBindID );
+ glBufferDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, m_IBSize, NULL, GL_STATIC_DRAW_ARB );
+ GetRealGfxDevice().GetFrameStats().AddUploadIB( m_IBSize );
+ }
+ else
+ {
+ // discard old and rebuild whole buffer
+ #if DEBUG_GL_VBO
+ ++gUpdatedIBs;
+ #endif
+ BindARBIndexBuffer( m_IndexBindID );
+ glBufferDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, m_IBSize, NULL, GL_DYNAMIC_DRAW_ARB ); // discard old and mark new as dynamic
+ GetRealGfxDevice().GetFrameStats().AddUploadIB( m_IBSize );
+ }
+
+ UpdateIndexBufferData(buffer);
+ DebugAssertIf(m_IndexBindID == 0);
+}
+
+
+// -----------------------------------------------------------------------------
+
+#if 0 // see comment in header file
+
+DynamicARBVBO::DynamicARBVBO( UInt32 vbSize )
+: DynamicVBO()
+, m_VBSize(vbSize)
+, m_VBUsedBytes(0)
+, m_VertexBindID(0)
+, m_VBChunkSize(0)
+, m_IBChunk(NULL)
+, m_IBChunkSize(0)
+{
+}
+
+DynamicARBVBO::~DynamicARBVBO ()
+{
+ if( m_VertexBindID )
+ glDeleteBuffersARB( 1, (GLuint*)&m_VertexBindID );
+ delete[] m_IBChunk;
+}
+
+void DynamicARBVBO::DrawChunk (const ChannelAssigns& channels)
+{
+ // just return if nothing to render
+ if( !m_LastChunkShaderChannelMask )
+ return;
+
+ AssertIf( !m_LastChunkShaderChannelMask || !m_LastChunkStride );
+ AssertIf( m_LendedChunk );
+
+ // setup VBO
+ DebugAssertIf( m_VertexBindID == 0 );
+
+ BindARBIndexBuffer( 0 );
+ BindARBVertexBuffer( m_VertexBindID );
+
+ ClearActiveChannelsGL();
+ UInt32 targetMap = channels.GetTargetMap();
+ for( int i = 0; i < kVertexCompCount; ++i )
+ {
+ if( !( targetMap & (1<<i) ) )
+ continue;
+ ShaderChannel src = channels.GetSourceForTarget( (VertexComponent)i );
+ if( !( m_LastChunkShaderChannelMask & (1<<src) ) )
+ continue;
+
+ SetChannelDataGL( src, (VertexComponent)i, reinterpret_cast<const void*>(m_BufferChannelOffsets[src]), m_LastChunkStride );
+ }
+ GfxDevice& device = GetRealGfxDevice();
+ ActivateChannelsGL();
+ device.BeforeDrawCall( false );
+
+ // draw
+ GfxDeviceStats& stats = device.GetFrameStats();
+ int primCount = 0;
+ if( m_LastRenderMode == kDrawTriangleStrip )
+ {
+ OGL_CALL(glDrawArrays( GL_TRIANGLE_STRIP, 0, m_LastChunkVertices ));
+ primCount = m_LastChunkVertices-2;
+ }
+ else if (m_LastRenderMode == kDrawQuads)
+ {
+ OGL_CALL(glDrawArrays( GL_QUADS, 0, m_LastChunkVertices ));
+ primCount = m_LastChunkVertices/2;
+ }
+ else
+ {
+ DebugAssertIf( !m_IBChunk );
+ OGL_CALL(glDrawElements( GL_TRIANGLES, m_LastChunkIndices, GL_UNSIGNED_SHORT, m_IBChunk ));
+ primCount = (m_LastRenderMode == kDrawIndexedTriangleStrip) ? m_LastChunkIndices-2 : m_LastChunkIndices/3;
+ }
+ stats.AddDrawCall (primCount, m_LastChunkVertices);
+
+ GLAssert();
+}
+
+bool DynamicARBVBO::GetChunk( UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode renderMode, void** outVB, void** outIB )
+{
+ AssertIf( m_LendedChunk );
+ AssertIf( maxVertices >= 65536 || maxIndices >= 65536*3 );
+ DebugAssertIf( renderMode == kDrawIndexedTriangles && outIB == NULL );
+ DebugAssertIf( renderMode != kDrawIndexedTriangles && (maxIndices != 0 || outIB != NULL) );
+
+ m_LendedChunk = true;
+ m_LastChunkShaderChannelMask = shaderChannelMask;
+ m_LastRenderMode = renderMode;
+ if( maxVertices == 0 )
+ maxVertices = 8;
+
+ m_LastChunkStride = 0;
+ for( int i = 0; i < kShaderChannelCount; ++i ) {
+ if( shaderChannelMask & (1<<i) )
+ m_LastChunkStride += VBO::GetChannelByteSize(i);
+ }
+
+ DebugAssertIf( !outVB );
+ UInt32 vbCapacity = maxVertices * m_LastChunkStride;
+
+ bool resizeVB = false;
+ if( !m_VertexBindID ) {
+ // generate buffer
+ glGenBuffersARB( 1, (GLuint*)&m_VertexBindID );
+ resizeVB = true;
+ m_VBUsedBytes = 0;
+ }
+
+ // check if requested chunk is larger than current buffer
+ if( vbCapacity > m_VBSize ) {
+ m_VBSize = vbCapacity * 2; // allocate more up front
+ resizeVB = true;
+ m_VBUsedBytes = 0;
+ }
+
+ // if we'll be past the end of buffer, restart from beginning and discard the old one
+ if( m_VBUsedBytes + vbCapacity > m_VBSize ) {
+ resizeVB = true;
+ }
+
+ // initialize or resize or discard the buffer
+ BindARBVertexBuffer( m_VertexBindID );
+ if( resizeVB ) {
+ glBufferDataARB( GL_ARRAY_BUFFER_ARB, m_VBSize + kDummyVBStartBytes, NULL, GL_DYNAMIC_DRAW_ARB );
+ #if UNITY_OSX
+ if (gGraphicsCaps.gl.hasAppleFlushBufferRange) {
+ glBufferParameteriAPPLE(GL_ARRAY_BUFFER, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE);
+ }
+ #endif
+ m_VBUsedBytes = 0;
+ }
+
+ // map the buffer
+ UInt8* buffer = (UInt8*)glMapBufferARB( GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB );
+ if( !buffer ) {
+ LOGVBO( "Error mapping VB!" );
+ *outVB = NULL;
+ m_LendedChunk = false;
+ return false;
+ }
+
+ *outVB = buffer + kDummyVBStartBytes + m_VBUsedBytes;
+
+ if( maxIndices && renderMode == kDrawIndexedTriangles )
+ {
+ UInt32 ibCapacity = maxIndices * kVBOIndexSize;
+ if( ibCapacity > m_IBChunkSize )
+ {
+ delete[] m_IBChunk;
+ m_IBChunk = new UInt8[ ibCapacity ];
+ m_IBChunkSize = ibCapacity;
+ }
+ *outIB = m_IBChunk;
+ }
+ return true;
+}
+
+void DynamicARBVBO::ReleaseChunk( UInt32 actualVertices, UInt32 actualIndices )
+{
+ AssertIf( !m_LendedChunk );
+ AssertIf( actualIndices % 3 != 0 );
+ m_LendedChunk = false;
+
+ m_LastChunkVertices = actualVertices;
+ m_LastChunkIndices = actualIndices;
+
+ AssertIf (!m_VertexBindID);
+ BindARBVertexBuffer( m_VertexBindID );
+
+ UInt32 actualVBSize = actualVertices * m_LastChunkStride;
+ #if UNITY_OSX
+ if (gGraphicsCaps.gl.hasAppleFlushBufferRange) {
+ glFlushMappedBufferRangeAPPLE(GL_ARRAY_BUFFER_ARB, kDummyVBStartBytes + m_VBUsedBytes, actualVBSize);
+ }
+ #endif
+
+ glUnmapBufferARB( GL_ARRAY_BUFFER_ARB );
+
+ if( !actualVertices || (m_LastRenderMode == kDrawIndexedTriangles && !actualIndices) ) {
+ m_LastChunkShaderChannelMask = 0;
+ return;
+ }
+
+ // -------- Vertex buffer
+
+ size_t channelOffset = kDummyVBStartBytes + m_VBUsedBytes;
+ for( int i = 0; i < kShaderChannelCount; ++i ) {
+ if( m_LastChunkShaderChannelMask & (1<<i) ) {
+ m_BufferChannelOffsets[i] = channelOffset;
+ channelOffset += VBO::GetChannelByteSize(i);
+ }
+ }
+ m_VBUsedBytes += actualVBSize;
+
+ GLAssert();
+}
+
+#endif
diff --git a/Runtime/GfxDevice/opengl/ARBVBO.h b/Runtime/GfxDevice/opengl/ARBVBO.h
new file mode 100644
index 0000000..f9513f4
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/ARBVBO.h
@@ -0,0 +1,75 @@
+#pragma once
+
+#include "Runtime/Shaders/VBO.h"
+
+void BindARBVertexBuffer( int id );
+void BindARBIndexBuffer( int id );
+
+// Implements VBO through ARB_vertex_buffer_object.
+class ARBVBO : public VBO {
+public:
+ ARBVBO();
+ virtual ~ARBVBO();
+
+ virtual void UpdateVertexData( const VertexBufferData& buffer );
+ virtual void UpdateIndexData (const IndexBufferData& buffer);
+ virtual void DrawVBO (const ChannelAssigns& channels, UInt32 firstIndexByte, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 firstVertex, UInt32 vertexCount);
+ virtual void DrawCustomIndexed( const ChannelAssigns& channels, void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount );
+ virtual int GetRuntimeMemorySize() const { return m_VBSize + m_IBSize; }
+
+ virtual bool MapVertexStream( VertexStreamData& outData, unsigned stream );
+ virtual void UnmapVertexStream( unsigned stream );
+ virtual bool IsVertexBufferLost() const { return false; }
+
+
+private:
+ void DrawInternal( const ChannelAssigns& channels, const void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 drawVertexCount );
+ void VerifyVertexBuffer();
+ void UpdateIndexBufferData (const IndexBufferData& sourceData);
+
+private:
+ ChannelInfoArray m_Channels;
+ int m_VertexCount;
+ int m_VertexBindID;
+ int m_IndexBindID;
+ int m_VBSize;
+ int m_IBSize;
+};
+
+
+#if 0
+
+// Ok, dynamic VBOs with updating just the chunks with BufferSubData seem to be slower (by 30% - 50%)
+// than just rendering from memory arrays. This is on MacBook Pro RX1600 (10.4.9), MBP GeForce8600 (10.5.7)
+// and PC GeForce 6800 (93.xx). Using APPLE_flush_buffer_range does not help with performance.
+// So we just don't use them for now!
+
+// This is dynamic vertex buffer, with index buffer just fed from memory.
+// The reason is that BufferSubData on index buffers is really buggy on some drivers
+// (e.g. Mac OS X 10.4.8/10.4.9 on X1600).
+class DynamicARBVBO : public DynamicVBO {
+public:
+ DynamicARBVBO( UInt32 vbSize );
+ virtual ~DynamicARBVBO();
+
+ virtual bool GetChunk( UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode renderMode, void** outVB, void** outIB );
+ virtual void ReleaseChunk( UInt32 actualVertices, UInt32 actualIndices );
+ virtual void DrawChunk (const ChannelAssigns& channels);
+
+private:
+ size_t m_BufferChannelOffsets[kShaderChannelCount];
+
+ UInt32 m_VBSize;
+ UInt32 m_VBUsedBytes;
+
+ int m_VertexBindID;
+ UInt32 m_VBChunkSize;
+ UInt8* m_IBChunk;
+ UInt32 m_IBChunkSize;
+};
+
+#endif
+
diff --git a/Runtime/GfxDevice/opengl/ArbGpuProgamGL.cpp b/Runtime/GfxDevice/opengl/ArbGpuProgamGL.cpp
new file mode 100644
index 0000000..18a5e10
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/ArbGpuProgamGL.cpp
@@ -0,0 +1,458 @@
+#include "UnityPrefix.h"
+#include "ArbGpuProgamGL.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Math/Vector4.h"
+#include "UnityGL.h"
+#include "GLAssert.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "Runtime/Utilities/Word.h"
+
+unsigned int GetGLShaderImplTarget( ShaderImplType implType ); // GfxDeviceGL.cpp
+void InvalidateActiveShaderStateGL( ShaderType type ); // GfxDeviceGL.cpp
+
+
+/*
+For ARB fragment programs on OS X, Intel, ATI cards, cache some first parameter values and
+use Env paramters instead of Local ones.
+Otherwise OS X 10.4.10 will insist on recompiling some shaders behind the scenes, or
+some other funky stuff - which will make shadows be extremely slow:
+
+ From: Aras Pranckevicius <aras@otee.dk>
+ To: Michel Castejon <mcast@apple.com>
+ Date: Jul 3, 2007 11:35 AM
+ <...>
+ On my MacBookPro/X1600 it gives 14 FPS with the glProgramLocalParameter4fv call, and 77 FPS
+ without the call. That call is the only difference between the two cases - when drawing the
+ objects receiving the shadow, the call sets the [1] local parameter (and the parameter is not
+ actually used anywhere). The call does not actually change the value, i.e. it always sets it to
+ {0.3, 0.7, 0.05, -4}.
+
+ So, issuing this glProgramLocalParameter4fv that does not change the value of a local parameter
+ that is not actually used, makes each glDrawElements take about 1.3 milliseconds (from GL profiler
+ trace). If this call is not made, then each glDrawElements takes about 0.025 milliseconds (after
+ the first draw with the shader one which is longer). I can't really tell what's happening from
+ the Shark profile, as the difference is somewhere in the driver where I have no symbols.
+ <...>
+
+BUT do not do this on PPC Macs, Windows or other cards on Intel Macs. On PPC Macs using Env paramters
+will make shadows completely randomly disappear. Oh the joy!
+*/
+
+const int kFPParamCacheSize = 32; // don't make this larger than 32 (won't fit into s_FPParamCacheValid mask)
+UInt32 s_FPParamCacheValid = 0;
+Vector4f s_FPParamCache[kFPParamCacheSize];
+
+void InvalidateFPParamCacheGL()
+{
+ s_FPParamCacheValid = 0;
+}
+
+
+ArbGpuProgram::ArbGpuProgram( const std::string& source, ShaderType type, ShaderImplType implType )
+{
+ for (int i = 0; i < kFogModeCount; ++i)
+ m_FogFailed[i] = false;
+ m_ImplType = implType;
+ if( !Create(source,type) )
+ m_NotSupported = true;
+}
+
+ArbGpuProgram::~ArbGpuProgram ()
+{
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ if (m_Programs[i] != 0)
+ {
+ glDeleteProgramsARB (1, &m_Programs[i]);
+ }
+ }
+}
+
+
+static std::string PreprocessARBShader( const std::string& source, ShaderImplType implType )
+{
+ std::string processed;
+ processed = source;
+
+ // Remove ARB precision hint if it's broken on this GPU
+ if( gGraphicsCaps.gl.buggyArbPrecisionHint )
+ {
+ std::string::size_type pos;
+ while ((pos = processed.find ("OPTION ARB_precision_hint_fastest")) != std::string::npos)
+ {
+ std::string::size_type end = processed.find ("\n", pos);
+ if (end != std::string::npos)
+ processed.erase (pos, end - pos);
+ }
+ }
+
+ // In fragment programs on hardware where we have to use&cache Env params instead of Local
+ // ones, do replacement in the shader.
+ if( implType == kShaderImplFragment && gGraphicsCaps.gl.cacheFPParamsWithEnvs )
+ {
+ std::string kProgramLocal( "program.local" );
+ std::string kProgramEnv( "program.env" );
+ size_t pos = processed.find( kProgramLocal );
+ while( pos != std::string::npos )
+ {
+ processed.erase( pos, kProgramLocal.size() );
+ processed.insert( pos, kProgramEnv );
+ pos = processed.find( kProgramLocal, pos );
+ }
+ }
+
+ return processed;
+}
+
+
+bool ArbGpuProgram::Create( const std::string& source, ShaderType type )
+{
+ // We specially prefix ARB shaders that are compiled for SM3.0-like limits. If a shader is
+ // prefixed this way, we can quickly skip loading & compiling it by just checking graphics caps.
+ // Saves from weird driver bugs, does not spam the log and is faster.
+ bool isShaderModel3 = false;
+ if( !strncmp (source.c_str(), "3.0-!!", 6) )
+ {
+ if( gGraphicsCaps.shaderCaps < kShaderLevel3 )
+ return false;
+ isShaderModel3 = true;
+ }
+
+ m_GpuProgramLevel = isShaderModel3 ? kGpuProgramSM3 : kGpuProgramSM2;
+
+ GLAssert(); // Clear any GL errors
+
+ GLenum target = GetGLShaderImplTarget( m_ImplType );
+ glGenProgramsARB (1, &m_Programs[0]);
+ glBindProgramARB (target, m_Programs[0]);
+ InvalidateActiveShaderStateGL( type );
+ std::string processed = PreprocessARBShader( source, m_ImplType );
+
+ glProgramStringARB( target, GL_PROGRAM_FORMAT_ASCII_ARB, processed.size() - (isShaderModel3?4:0), processed.c_str() + (isShaderModel3?4:0) );
+
+ // Check for errors
+ GLint errorPos;
+ glGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, &errorPos );
+ if( errorPos != -1 )
+ {
+ printf_console( "ARB shader compile error: %s for %s\n", glGetString (GL_PROGRAM_ERROR_STRING_ARB), processed.c_str() );
+ int errorCounter = 0;
+ while( errorCounter < 10 && glGetError () != GL_NO_ERROR ) // prevent infinite loops for bad drivers
+ {
+ ++errorCounter;
+ }
+
+ glDeleteProgramsARB (1, &m_Programs[0]);
+ m_Programs[0] = 0;
+ return false;
+ }
+
+ m_SourceForFog = processed.c_str() + (isShaderModel3?4:0);
+
+ return true;
+}
+
+void ArbGpuProgram::ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer)
+{
+ GLenum target = GetGLShaderImplTarget( m_ImplType );
+
+ if( m_ImplType == kShaderImplFragment && gGraphicsCaps.gl.cacheFPParamsWithEnvs )
+ {
+ // If we are fragment program and have to cache parameters, do that
+ // Apply vector and matrix parameters with cache
+ const GpuProgramParameters::ValueParameterArray& valueParams = params.GetValueParams();
+ GpuProgramParameters::ValueParameterArray::const_iterator valueParamsEnd = valueParams.end();
+ for( GpuProgramParameters::ValueParameterArray::const_iterator i = valueParams.begin(); i != valueParamsEnd; ++i )
+ {
+ if (i->m_RowCount == 1 && i->m_ArraySize == 1)
+ {
+ const Vector4f& val = *reinterpret_cast<const Vector4f*>(buffer);
+ int idx = i->m_Index;
+ UInt32 mask = 1 << idx;
+ if( idx >= kFPParamCacheSize )
+ {
+ OGL_CALL(glProgramEnvParameter4fvARB( target, idx, val.GetPtr() ));
+ }
+ else
+ {
+ if( !(s_FPParamCacheValid & mask) || s_FPParamCache[idx] != val )
+ {
+ OGL_CALL(glProgramEnvParameter4fvARB( target, idx, val.GetPtr() ));
+ s_FPParamCache[idx] = val;
+ }
+ s_FPParamCacheValid |= mask;
+ #if GFX_DEVICE_VERIFY_ENABLE
+ Vector4f testVal;
+ OGL_CALL(glGetProgramEnvParameterfvARB( target, idx, testVal.GetPtr() ));
+ if( s_FPParamCache[idx] != testVal )
+ ErrorString( Format("FP local cache mismatch at index %i", idx) );
+ #endif
+ }
+ buffer += sizeof(Vector4f);
+ }
+ else
+ {
+ // Apply matrix parameters, with cache
+ DebugAssert(i->m_RowCount == 4);
+ int size = *reinterpret_cast<const int*> (buffer); buffer += sizeof(int);
+ DebugAssert (size == 16);
+ const Matrix4x4f* val = reinterpret_cast<const Matrix4x4f*>(buffer);
+ const float *ptr = val->GetPtr();
+ int idx = i->m_Index;
+ s_FPParamCacheValid &= ~(15 << idx); // mark 4 consecutive parameters as invalid in the cache
+ OGL_CALL(glProgramEnvParameter4fARB( target, idx+0, ptr[0], ptr[4], ptr[8], ptr[12] ));
+ OGL_CALL(glProgramEnvParameter4fARB( target, idx+1, ptr[1], ptr[5], ptr[9], ptr[13] ));
+ OGL_CALL(glProgramEnvParameter4fARB( target, idx+2, ptr[2], ptr[6], ptr[10], ptr[14] ));
+ OGL_CALL(glProgramEnvParameter4fARB( target, idx+3, ptr[3], ptr[7], ptr[11], ptr[15] ));
+ buffer += size * sizeof(float);
+ }
+ }
+ }
+ else
+ {
+ // Otherwise we are not FP or we don't have to cache - do the usual code path
+
+ // Apply vector and matrix parameters
+ const GpuProgramParameters::ValueParameterArray& valueParams = params.GetValueParams();
+ GpuProgramParameters::ValueParameterArray::const_iterator valueParamsEnd = valueParams.end();
+ for( GpuProgramParameters::ValueParameterArray::const_iterator i = valueParams.begin(); i != valueParamsEnd; ++i )
+ {
+ if( i->m_RowCount == 1 )
+ {
+ const Vector4f& val = *reinterpret_cast<const Vector4f*>(buffer);
+ OGL_CALL(glProgramLocalParameter4fvARB( target, i->m_Index, val.GetPtr() ));
+ buffer += sizeof(Vector4f);
+ }
+ else
+ {
+ DebugAssert(i->m_RowCount == 4);
+ int size = *reinterpret_cast<const int*> (buffer); buffer += sizeof(int);
+ DebugAssert (size == 16);
+ const Matrix4x4f* val = reinterpret_cast<const Matrix4x4f*>(buffer);
+ const float *ptr = val->GetPtr();
+ OGL_CALL(glProgramLocalParameter4fARB( target, i->m_Index+0, ptr[0], ptr[4], ptr[8], ptr[12] ));
+ OGL_CALL(glProgramLocalParameter4fARB( target, i->m_Index+1, ptr[1], ptr[5], ptr[9], ptr[13] ));
+ OGL_CALL(glProgramLocalParameter4fARB( target, i->m_Index+2, ptr[2], ptr[6], ptr[10], ptr[14] ));
+ OGL_CALL(glProgramLocalParameter4fARB( target, i->m_Index+3, ptr[3], ptr[7], ptr[11], ptr[15] ));
+ buffer += size * sizeof(float);
+ }
+ }
+ }
+
+ // Apply textures for fragment programs
+ if (m_ImplType == kShaderImplFragment)
+ {
+ const GpuProgramParameters::TextureParameterList& textureParams = params.GetTextureParams();
+ const GpuProgramParameters::TextureParameterList::const_iterator textureParamsEnd = textureParams.end();
+ for( GpuProgramParameters::TextureParameterList::const_iterator i = textureParams.begin(); i != textureParamsEnd; ++i )
+ {
+ const GpuProgramParameters::TextureParameter& t = *i;
+ const TexEnvData* texdata = reinterpret_cast<const TexEnvData*>(buffer);
+ ApplyTexEnvData (t.m_Index, t.m_SamplerIndex, *texdata);
+ buffer += sizeof(*texdata);
+ }
+ }
+}
+
+
+
+// --------------------------------------------------------------------------
+// ARB patching for fog
+
+#define DEBUG_FOG_PATCHING 0
+
+static inline bool IsNewline( char c ) { return c == '\n' || c == '\r'; }
+
+bool PatchVertexShaderFogARB (std::string& src)
+{
+ #if DEBUG_FOG_PATCHING
+ printf_console ("ARB fog patching: original vertex shader:\n%s\n", src.c_str());
+ #endif
+
+ if (src.find("result.fogcoord") != std::string::npos)
+ return false; // already has hardcoded fog mode
+
+ // find write to result.position, and do the same for result.fogcoord
+ size_t posWrite = src.find ("result.position.z");
+ bool writesFullPos = false;
+ if (posWrite == std::string::npos)
+ {
+ posWrite = src.find ("result.position");
+ if (posWrite == std::string::npos)
+ {
+ DebugAssert (!"couldn't find write to result.position");
+ return false;
+ }
+ writesFullPos = true;
+ }
+
+ // get whole line
+ size_t n = src.size();
+ size_t posWriteStart = posWrite, posWriteEnd = posWrite;
+ while (posWriteStart > 0 && !IsNewline(src[posWriteStart])) --posWriteStart;
+ ++posWriteStart;
+ while (posWriteEnd < n && !IsNewline(src[posWriteEnd])) ++posWriteEnd;
+
+ std::string instr = src.substr (posWriteStart, posWriteEnd-posWriteStart);
+ if (writesFullPos)
+ {
+ replace_string (instr, "result.position", "result.fogcoord", 0);
+ instr.resize(instr.size()-1);
+ instr += ".z;";
+ }
+ else
+ {
+ replace_string (instr, "result.position.z", "result.fogcoord", 0);
+ }
+ instr += '\n';
+
+ // insert fog code just after write to position
+ src.insert (posWriteEnd+1, instr);
+
+ #if DEBUG_FOG_PATCHING
+ printf_console ("ARB fog patching: after patching:\n%s\n", src.c_str());
+ #endif
+ return true;
+}
+
+bool PatchPixelShaderFogARB (std::string& src, FogMode fog)
+{
+ #if DEBUG_FOG_PATCHING
+ printf_console ("ARB fog patching: original pixel shader:\n%s\n", src.c_str());
+ #endif
+
+ if (src.find("OPTION ARB_fog_") != std::string::npos)
+ return false; // already has hardcoded fog mode
+
+ // skip until next line after !!ARBfp1.0
+ size_t pos = src.find("!!ARBfp1.0");
+ if (pos == std::string::npos)
+ return false; // something is wrong
+ pos += 10; // skip !!ARBfp1.0
+
+ size_t n = src.size();
+ while (pos < n && !IsNewline(src[pos])) ++pos; // skip until newline
+ while (pos < n && IsNewline(src[pos])) ++pos; // skip newlines
+ if (pos >= n)
+ return false;
+
+ // insert fog option
+ static const char* kFogOptions[kFogModeCount] = {"", "OPTION ARB_fog_linear;\n", "OPTION ARB_fog_exp;\n", "OPTION ARB_fog_exp2;\n"};
+ src.insert (pos, kFogOptions[fog]);
+
+ #if DEBUG_FOG_PATCHING
+ printf_console ("ARB fog patching: after patching, fog mode %d:\n%s\n", fog, src.c_str());
+ #endif
+ return true;
+}
+
+GLShaderID ArbGpuProgram::GetGLProgram (FogMode fog)
+{
+ int index = 0;
+ if (fog > kFogDisabled && !m_SourceForFog.empty() && !m_FogFailed[fog])
+ {
+ index = fog;
+ Assert (index >= 0 && index < kFogModeCount);
+
+ // create patched fog program if needed
+ if (!m_Programs[index])
+ {
+ std::string src = m_SourceForFog;
+ if (m_ImplType == kShaderImplFragment && PatchPixelShaderFogARB (src, fog))
+ {
+ // create program
+ glGenProgramsARB (1, &m_Programs[index]);
+ glBindProgramARB (GL_FRAGMENT_PROGRAM_ARB, m_Programs[index]);
+ InvalidateActiveShaderStateGL (kShaderFragment);
+ glProgramStringARB (GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, src.size(), src.c_str());
+ GLint errorPos;
+ glGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, &errorPos );
+ if (errorPos != -1)
+ {
+ glDeleteProgramsARB (1, &m_Programs[index]);
+ m_Programs[index] = 0;
+ m_FogFailed[index] = true;
+ index = 0;
+ }
+ }
+ else if (m_ImplType == kShaderImplVertex && PatchVertexShaderFogARB (src))
+ {
+ // create program
+ glGenProgramsARB (1, &m_Programs[index]);
+ glBindProgramARB (GL_VERTEX_PROGRAM_ARB, m_Programs[index]);
+ InvalidateActiveShaderStateGL (kShaderVertex);
+ glProgramStringARB (GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, src.size(), src.c_str());
+ GLint errorPos;
+ glGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, &errorPos );
+ if (errorPos != -1)
+ {
+ glDeleteProgramsARB (1, &m_Programs[index]);
+ m_Programs[index] = 0;
+ m_FogFailed[index] = true;
+ index = 0;
+ }
+ }
+ else
+ {
+ m_FogFailed[index] = true;
+ index = 0;
+ }
+ }
+ }
+ return m_Programs[index];
+}
+
+// --------------------------------------------------------------------------
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (ShaderPatchingARBTests)
+{
+ TEST(PatchVSZWrite)
+ {
+ std::string s;
+ s = "!!ARBvp1.0\n"
+ "DP4 result.position.z, vertex.position, c[0];\n"
+ "END";
+ CHECK (PatchVertexShaderFogARB(s));
+ CHECK_EQUAL(
+ "!!ARBvp1.0\n"
+ "DP4 result.position.z, vertex.position, c[0];\n"
+ "DP4 result.fogcoord, vertex.position, c[0];\n"
+ "END", s);
+ }
+ TEST(PatchVSFullWrite)
+ {
+ std::string s;
+ s = "!!ARBvp1.0\n"
+ "MOV result.position, vertex.position;\n"
+ "END";
+ CHECK (PatchVertexShaderFogARB(s));
+ CHECK_EQUAL(
+ "!!ARBvp1.0\n"
+ "MOV result.position, vertex.position;\n"
+ "MOV result.fogcoord, vertex.position.z;\n"
+ "END", s);
+ }
+ TEST(PatchVSWriteNotAtEnd)
+ {
+ std::string s;
+ s = "!!ARBvp1.0\n"
+ "MOV result.position, R0;\n"
+ "MOV R0, R1;\n"
+ "END";
+ CHECK (PatchVertexShaderFogARB(s));
+ CHECK_EQUAL(
+ "!!ARBvp1.0\n"
+ "MOV result.position, R0;\n"
+ "MOV result.fogcoord, R0.z;\n"
+ "MOV R0, R1;\n"
+ "END", s);
+ }
+
+} // SUITE
+
+#endif // ENABLE_UNIT_TESTS
diff --git a/Runtime/GfxDevice/opengl/ArbGpuProgamGL.h b/Runtime/GfxDevice/opengl/ArbGpuProgamGL.h
new file mode 100644
index 0000000..404c3e7
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/ArbGpuProgamGL.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "Runtime/GfxDevice/GpuProgram.h"
+
+class ArbGpuProgram : public GpuProgramGL {
+public:
+ ArbGpuProgram( const std::string& source, ShaderType type, ShaderImplType implType );
+ virtual ~ArbGpuProgram();
+
+ virtual void ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer);
+
+ GLShaderID GetGLProgram(FogMode fog);
+
+private:
+ bool Create( const std::string& source, ShaderType type );
+
+private:
+ bool m_FogFailed[kFogModeCount];
+};
diff --git a/Runtime/GfxDevice/opengl/ChannelsGL.cpp b/Runtime/GfxDevice/opengl/ChannelsGL.cpp
new file mode 100644
index 0000000..3b4f275
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/ChannelsGL.cpp
@@ -0,0 +1,252 @@
+#include "UnityPrefix.h"
+#include "ChannelsGL.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "UnityGL.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "GLAssert.h"
+
+// dimensionality of the different channels (element count)
+static const int kDefaultChannelSizes[kShaderChannelCount] = {
+ 3, // pos
+ 3, // normal
+ 4, // color
+ 2, // uv
+ 2, // uv2
+ 4, // tangent
+};
+
+static const GLenum kDefaultChannelTypes[kShaderChannelCount] = {
+ GL_FLOAT, // pos
+ GL_FLOAT, // normal
+ GL_UNSIGNED_BYTE, // color
+ GL_FLOAT, // UV0
+ GL_FLOAT, // UV1
+ GL_FLOAT, // tangent
+};
+
+
+static void SetVertexComponentData( VertexComponent comp, int size, unsigned int type, int stride, const void *pointer )
+{
+ // We allow pointer to be used as an offset
+ //DebugAssertIf( !pointer );
+
+ switch (comp) {
+ case kVertexCompColor:
+ OGL_CALL(glColorPointer (size, type, stride, pointer));
+ break;
+ case kVertexCompVertex:
+ OGL_CALL(glVertexPointer (size, type, stride, pointer));
+ break;
+ case kVertexCompNormal:
+ DebugAssert( size >= 3 );
+ OGL_CALL(glNormalPointer (type, stride, pointer));
+ break;
+ case kVertexCompTexCoord0:
+ case kVertexCompTexCoord1:
+ case kVertexCompTexCoord2:
+ case kVertexCompTexCoord3:
+ case kVertexCompTexCoord4:
+ case kVertexCompTexCoord5:
+ case kVertexCompTexCoord6:
+ case kVertexCompTexCoord7:
+ if (gGraphicsCaps.maxTexCoords > 1) {
+ OGL_CALL(glClientActiveTextureARB( GL_TEXTURE0_ARB + comp - kVertexCompTexCoord0 ));
+ }
+ OGL_CALL(glTexCoordPointer (size, type, stride, pointer));
+ break;
+ case kVertexCompTexCoord:
+ printf_console( "Warning: unspecified texcoord bound\n" );
+ break;
+ case kVertexCompAttrib0: case kVertexCompAttrib1: case kVertexCompAttrib2: case kVertexCompAttrib3:
+ case kVertexCompAttrib4: case kVertexCompAttrib5: case kVertexCompAttrib6: case kVertexCompAttrib7:
+ case kVertexCompAttrib8: case kVertexCompAttrib9: case kVertexCompAttrib10: case kVertexCompAttrib11:
+ case kVertexCompAttrib12: case kVertexCompAttrib13: case kVertexCompAttrib14: case kVertexCompAttrib15:
+ OGL_CALL(glVertexAttribPointerARB( comp - kVertexCompAttrib0, size, type, true, stride, pointer ));
+ break;
+ }
+}
+
+static signed char s_EnabledVertexComps[kVertexCompCount]; // 0/1 or -1 if unknown
+static unsigned int s_EnabledTargetsMask; // Bitfield of which targets are currently valid
+
+void ClearActiveChannelsGL()
+{
+ s_EnabledTargetsMask = 0;
+}
+
+static UInt32 GetGLType(const ChannelInfo& source)
+{
+ switch (source.format)
+ {
+ case kChannelFormatFloat: return GL_FLOAT;
+ case kChannelFormatFloat16: return GL_HALF_FLOAT_ARB;
+ case kChannelFormatColor: return GL_UNSIGNED_BYTE;
+ case kChannelFormatByte: return GL_BYTE;
+ }
+ ErrorString("Unknown channel format!");
+ return 0;
+}
+
+static int GetGLDimension(const ChannelInfo& source)
+{
+ return (source.format == kChannelFormatColor) ? 4 : source.dimension;
+}
+
+void SetChannelDataGL( const ChannelInfo& source, const StreamInfoArray streams, VertexComponent target, const UInt8* buffer )
+{
+ AssertIf( target == kVertexCompNone || target == kVertexCompTexCoord );
+ int stride = source.CalcStride( streams );
+ int offset = source.CalcOffset( streams );
+ SetVertexComponentData( target, GetGLDimension(source), GetGLType(source), stride, buffer + offset );
+ s_EnabledTargetsMask |= (1 << target);
+}
+
+void SetChannelDataGL( ShaderChannel source, VertexComponent target, const void *pointer, int stride )
+{
+ AssertIf( source == kShaderChannelNone );
+ if( !pointer )
+ return;
+
+ DebugAssertIf( target == kVertexCompNone || target == kVertexCompTexCoord );
+ SetVertexComponentData( target, kDefaultChannelSizes[source], kDefaultChannelTypes[source], stride, pointer );
+ s_EnabledTargetsMask |= (1 << target);
+}
+
+#if UNITY_EDITOR
+// only used by GizmoUtil in the editor
+void SetChannelDataGL( ShaderChannel source, const ChannelAssigns& channels, const void *pointer, int stride )
+{
+ AssertIf( source == kShaderChannelNone );
+ if( !pointer )
+ return;
+
+ for( int i = 0; i < kVertexCompCount; ++i )
+ {
+ if( channels.GetSourceForTarget( (VertexComponent)i ) == source )
+ {
+ AssertIf( i == kVertexCompNone || i == kVertexCompTexCoord );
+ SetVertexComponentData( (VertexComponent)i, kDefaultChannelSizes[source], kDefaultChannelTypes[source], stride, pointer );
+ s_EnabledTargetsMask |= (1 << i);
+ }
+ }
+}
+
+#endif
+
+
+void ActivateChannelsGL()
+{
+ unsigned int compMask = s_EnabledTargetsMask;
+ int i;
+
+ // fetch now to prevent aliasing
+ GLint vaCount = gGraphicsCaps.gl.vertexAttribCount;
+ //GLint texUnitCount = gGraphicsCaps.maxTexUnits;
+ GLint texCoordCount = gGraphicsCaps.maxTexCoords;
+
+ static GLenum gltargets[] = { 0, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_NORMAL_ARRAY };
+
+ // There's a dark corner of GL spec that says that generic and conventional vertex
+ // attributes (and arrays) may be aliased (most decent drivers don't alias them).
+ // http://oss.sgi.com/projects/ogl-sample/registry/NV/vertex_program.txt
+ //
+ // So we first disable all vertex components that we don't use; then enable all
+ // that we do use. This should work both in aliased and non-aliased implementations.
+ //
+ // On some systems (XP, Catalyst 5.7, R9600) this makes a difference!
+ // Without it e.g. a single GUIText is not visible.
+
+ // --------------------------------
+ // First disable components we don't use
+
+ for( i = kVertexCompVertex; i <= kVertexCompNormal; ++i )
+ {
+ if( !(compMask & (1<<i)) )
+ {
+ if( s_EnabledVertexComps[i] != 0 )
+ {
+ OGL_CALL(glDisableClientState( gltargets[i] ));
+ s_EnabledVertexComps[i] = 0;
+ }
+ }
+ }
+
+ for( i = 0; i < texCoordCount; ++i )
+ {
+ int compIndex = kVertexCompTexCoord0 + i;
+ if( !(compMask & (1 << compIndex)) )
+ {
+ if( s_EnabledVertexComps[compIndex] != 0 )
+ {
+ if( texCoordCount > 1 )
+ OGL_CALL(glClientActiveTextureARB(GL_TEXTURE0_ARB + i));
+ OGL_CALL(glDisableClientState (GL_TEXTURE_COORD_ARRAY));
+ s_EnabledVertexComps[compIndex] = 0;
+ }
+ }
+ }
+
+ for( i = 0; i < vaCount; ++i )
+ {
+ int compIndex = kVertexCompAttrib0 + i;
+ if( !(compMask & (1 << compIndex)) )
+ {
+ if( s_EnabledVertexComps[compIndex] != 0 )
+ {
+ OGL_CALL(glDisableVertexAttribArrayARB (i));
+ s_EnabledVertexComps[compIndex] = 0;
+ }
+ }
+ }
+
+ // --------------------------------
+ // Then enable components we use
+
+ for( i = kVertexCompVertex; i <= kVertexCompNormal; ++i )
+ {
+ if( compMask & (1<<i) )
+ {
+ if( s_EnabledVertexComps[i] != 1 )
+ {
+ OGL_CALL(glEnableClientState( gltargets[i] ));
+ s_EnabledVertexComps[i] = 1;
+ }
+ }
+ }
+
+ for( i = 0; i < texCoordCount; ++i )
+ {
+ int compIndex = kVertexCompTexCoord0 + i;
+ if( compMask & (1 << compIndex) )
+ {
+ if( s_EnabledVertexComps[compIndex] != 1 )
+ {
+ if( texCoordCount > 1 )
+ OGL_CALL(glClientActiveTextureARB(GL_TEXTURE0_ARB + i));
+ OGL_CALL(glEnableClientState (GL_TEXTURE_COORD_ARRAY));
+ s_EnabledVertexComps[compIndex] = 1;
+ }
+ }
+ }
+
+ for( i = 0; i < vaCount; ++i )
+ {
+ int compIndex = kVertexCompAttrib0 + i;
+ if( compMask & (1 << compIndex) )
+ {
+ if( s_EnabledVertexComps[compIndex] != 1 )
+ {
+ OGL_CALL(glEnableVertexAttribArrayARB( i ));
+ s_EnabledVertexComps[compIndex] = 1;
+ }
+ }
+ }
+
+ GLAssert();
+}
+
+void InvalidateChannelStateGL()
+{
+ for( int i = 0; i < kVertexCompCount; ++i )
+ s_EnabledVertexComps[i] = -1;
+}
diff --git a/Runtime/GfxDevice/opengl/ChannelsGL.h b/Runtime/GfxDevice/opengl/ChannelsGL.h
new file mode 100644
index 0000000..43e7cad
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/ChannelsGL.h
@@ -0,0 +1,17 @@
+#ifndef __CHANNELS_GL_H__
+#define __CHANNELS_GL_H__
+
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "Runtime/Filters/Mesh/VertexData.h"
+
+void ClearActiveChannelsGL();
+void SetChannelDataGL( const ChannelInfo& source, const StreamInfoArray streams, VertexComponent target, const UInt8* buffer = NULL );
+void SetChannelDataGL( ShaderChannel source, VertexComponent target, const void *pointer, int stride );
+#if UNITY_EDITOR
+void SetChannelDataGL( ShaderChannel source, const ChannelAssigns& channels, const void *pointer, int stride );
+#endif
+void ActivateChannelsGL();
+void InvalidateChannelStateGL();
+
+#endif
diff --git a/Runtime/GfxDevice/opengl/CombinerGL.cpp b/Runtime/GfxDevice/opengl/CombinerGL.cpp
new file mode 100644
index 0000000..4ed2ab9
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/CombinerGL.cpp
@@ -0,0 +1,201 @@
+#include "UnityPrefix.h"
+#include "CombinerGL.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "UnityGL.h"
+#include "GLAssert.h"
+#include "External/shaderlab/Library/pass.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+static const unsigned int kCombinerFuncTable[] = {
+ GL_REPLACE, GL_MODULATE, GL_ADD, GL_ADD_SIGNED_ARB, GL_SUBTRACT_ARB, GL_INTERPOLATE_ARB, GL_DOT3_RGB_ARB, GL_DOT3_RGBA_ARB
+};
+static const unsigned int kCombinerBlendFuncTableATI[4] = {
+ GL_INTERPOLATE_ARB, GL_MODULATE_ADD_ATI, GL_MODULATE_SIGNED_ADD_ATI, GL_MODULATE_SUBTRACT_ATI
+};
+static const unsigned int kCombinerBlendFuncTableNV[4] = {
+ GL_INTERPOLATE_ARB, GL_ADD, GL_ADD_SIGNED_EXT, 0
+};
+static const unsigned int kCombinerSourceTable[4] = {
+ GL_PREVIOUS_ARB, GL_TEXTURE, GL_CONSTANT_ARB, GL_PRIMARY_COLOR_ARB
+};
+static const unsigned int kCombinerOperandTable[4] = {
+ GL_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR, GL_ONE_MINUS_SRC_ALPHA
+};
+
+
+bool TextureCombinersGL::IsCombineModeSupported( unsigned int combiner )
+{
+ int cf = COMBINER_GET_FUNC(combiner);
+ if( cf & combiner::kBlendFuncMask )
+ {
+ int blendF = COMBINER_GET_BLEND_FUNC_INDEX(cf);
+ if( blendF != 0 ) // anything other than GL_INTERPOLATE_ARB requires extension
+ {
+ if( gGraphicsCaps.gl.hasTextureEnvCombine3ATI )
+ return true;
+ if( gGraphicsCaps.gl.hasTextureEnvCombine3NV && blendF != 3 ) // NV fallback can't do a*b-c
+ return true;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void ApplyCombinerGL( unsigned int& currentCombColor, unsigned int& currentCombAlpha, unsigned int combcolor, unsigned int combalpha )
+{
+ // only change combiner color setup if different from the cached version.
+ if( currentCombColor != combcolor )
+ {
+ // Handle RGB
+ int s0 = (combcolor >> combiner::kSrcZeroShift) & 0xFF;
+ int cf = COMBINER_GET_FUNC(combcolor);
+ int s1 = (combcolor) & 0xFF;
+
+ GLint source0 = kCombinerSourceTable[s0 & combiner::kSourceMask];
+ GLint oper0 = kCombinerOperandTable[s0 >> combiner::kOperandShift];
+ GLint source1 = kCombinerSourceTable[s1 & combiner::kSourceMask];
+ GLint oper1 = kCombinerOperandTable[s1 >> combiner::kOperandShift];
+
+ OGL_CALL(glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB));
+ if( !(cf & combiner::kBlendFuncMask) )
+ {
+ OGL_CALL(glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, kCombinerFuncTable[cf]));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, source0 ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, oper0 ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, source1 ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, oper1 ));
+ }
+ else
+ {
+ int blendF = COMBINER_GET_BLEND_FUNC_INDEX(cf);
+ int src2 = cf & combiner::kSourceMask;
+ int oper2 = ((cf & combiner::kOperandTwo) >> combiner::kOperandShift) | 1;
+ if( blendF == 0 || gGraphicsCaps.gl.hasTextureEnvCombine3ATI )
+ {
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, source0 ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, oper0 ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, source1 ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, oper1 ));
+
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, kCombinerBlendFuncTableATI[blendF] ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, kCombinerSourceTable[src2] ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, kCombinerOperandTable[oper2] ));
+ }
+ else
+ {
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE4_NV ));
+
+ // TNT combiners use a different argument order than ARB/ATI combiners...
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, source0 ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, oper0 ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, source1 ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, oper1 ));
+
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, kCombinerBlendFuncTableNV[blendF] ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, kCombinerSourceTable[src2] ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, kCombinerOperandTable[oper2] ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, GL_ZERO ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_ONE_MINUS_SRC_COLOR ));
+ }
+ }
+ OGL_CALL(glTexEnvf( GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, (combcolor >> combiner::kScaleShift) ));
+
+ currentCombColor = combcolor;
+ }
+
+ // only change combiner alpha setup if different from the cached version.
+ if( currentCombAlpha != combalpha )
+ {
+ // Handle alpha
+ int s0 = (combalpha >> combiner::kSrcZeroShift) & 0xFF;
+ int cf = COMBINER_GET_FUNC(combalpha);
+ int s1 = (combalpha) & 0xFF;
+ if ((cf & 7) == 6) // dot3 combiner doesn't touch alpha, so use just source0
+ cf = 0;
+
+ GLint source0 = kCombinerSourceTable[s0 & combiner::kSourceMask];
+ GLint oper0 = kCombinerOperandTable[(s0 >> combiner::kOperandShift)|1];
+ GLint source1 = kCombinerSourceTable[s1 & combiner::kSourceMask];
+ GLint oper1 = kCombinerOperandTable[(s1 >> combiner::kOperandShift)|1];
+
+ if (!(cf & combiner::kBlendFuncMask))
+ {
+ OGL_CALL(glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, kCombinerFuncTable[cf]));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, source0 ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, oper0 ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, source1 ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, oper1 ));
+ }
+ else
+ {
+ int blendF = COMBINER_GET_BLEND_FUNC_INDEX(cf);
+ int src2 = cf & combiner::kSourceMask;
+ int oper2 = ((cf & combiner::kOperandTwo) >> combiner::kOperandShift) | 1;
+ if( blendF == 0 || gGraphicsCaps.gl.hasTextureEnvCombine3ATI )
+ {
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, source0 ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, oper0 ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, source1 ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, oper1 ));
+
+ OGL_CALL(glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, kCombinerBlendFuncTableATI[blendF]));
+ OGL_CALL(glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE2_ALPHA_ARB, kCombinerSourceTable[src2]));
+ OGL_CALL(glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND2_ALPHA_ARB, kCombinerOperandTable[oper2]));
+ }
+ else
+ {
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE4_NV ));
+
+ // TNT combiners use a different argument order than ARB/ATI combiners...
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, source0 ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, oper0 ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_ALPHA_ARB, source1 ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_ALPHA_ARB, oper1 ));
+
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, kCombinerBlendFuncTableNV[blendF] ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, kCombinerSourceTable[src2] ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, kCombinerOperandTable[oper2] ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE3_ALPHA_NV, GL_ZERO ));
+ OGL_CALL(glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND3_ALPHA_NV, GL_ONE_MINUS_SRC_ALPHA ));
+ }
+ }
+ OGL_CALL(glTexEnvf( GL_TEXTURE_ENV, GL_ALPHA_SCALE, (combalpha >> combiner::kScaleShift)));
+
+ currentCombAlpha = combalpha;
+ }
+ GLAssert();
+}
+
+
+TextureCombinersGL* TextureCombinersGL::Create( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props )
+{
+ // check if supported
+ if( count > gGraphicsCaps.maxTexUnits )
+ return NULL;
+
+ // TODO: move this to client thread for threaded rendering
+ for( int i = 0; i < count; ++i )
+ if( !IsCombineModeSupported( texEnvs[i].m_CombColor ) )
+ return NULL;
+
+ // For threaded rendering this check is done on the client side (and we get NULL here)
+ if( props ) {
+ for( int i = 0; i < count; ++i ) {
+ TextureDimension texDim;
+ TexGenMode texGen;
+ GetTexEnvInfoFromName( texEnvs[i].m_TextureName, texDim, texGen, props );
+ if( !ShaderLab::IsTexEnvSupported( texEnvs[i].m_TextureName, texDim, texGen ) )
+ return NULL;
+ }
+ }
+
+ // create
+ TextureCombinersGL* combiners = new TextureCombinersGL();
+ combiners->count = count;
+ combiners->texEnvs = texEnvs;
+ return combiners;
+}
+
diff --git a/Runtime/GfxDevice/opengl/CombinerGL.h b/Runtime/GfxDevice/opengl/CombinerGL.h
new file mode 100644
index 0000000..fcc1307
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/CombinerGL.h
@@ -0,0 +1,18 @@
+#pragma once
+
+namespace ShaderLab {
+ struct TextureBinding;
+ class PropertySheet;
+}
+
+
+struct TextureCombinersGL
+{
+ static bool IsCombineModeSupported( unsigned int combiner );
+ static TextureCombinersGL* Create( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props );
+
+ int count;
+ const ShaderLab::TextureBinding* texEnvs;
+};
+
+void ApplyCombinerGL( unsigned int& currentCombColor, unsigned int& currentCombAlpha, unsigned int combcolor, unsigned int combalpha );
diff --git a/Runtime/GfxDevice/opengl/GLAssert.cpp b/Runtime/GfxDevice/opengl/GLAssert.cpp
new file mode 100644
index 0000000..4f3501d
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/GLAssert.cpp
@@ -0,0 +1,42 @@
+#include "UnityPrefix.h"
+#include "GLAssert.h"
+#include "UnityGL.h"
+
+#if UNITY_WIN || UNITY_LINUX
+#include <GL/glu.h>
+#else
+#include <OpenGL/glu.h>
+#endif
+
+using namespace std;
+
+void CheckOpenGLError (const char *prefix, const char* file, long line)
+{
+ const int kMaxErrors = 10;
+ int counter = 0;
+
+ GLenum glerr;
+ while( (glerr = glGetError ()) != GL_NO_ERROR )
+ {
+ if (prefix)
+ {
+ string errorString = prefix;
+ errorString += ": ";
+ const char* gluMsg = reinterpret_cast<const char*>(gluErrorString (glerr));
+ errorString += gluMsg ? gluMsg : Format("unknown error 0x%x", glerr);
+ DebugStringToFile (errorString.c_str(), 0, file, line, kAssert);
+ }
+ else
+ {
+ const char* gluMsg = reinterpret_cast<const char*>(gluErrorString (glerr));
+ string errorString = gluMsg ? gluMsg : Format("unknown error 0x%x", glerr);
+ DebugStringToFile (errorString.c_str(), 0, file, line, kAssert);
+ }
+ ++counter;
+ if( counter > kMaxErrors )
+ {
+ printf_console( "GL: error count exceeds %i, stop reporting errors\n", kMaxErrors );
+ return;
+ }
+ }
+}
diff --git a/Runtime/GfxDevice/opengl/GLAssert.h b/Runtime/GfxDevice/opengl/GLAssert.h
new file mode 100644
index 0000000..7693012
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/GLAssert.h
@@ -0,0 +1,25 @@
+#ifndef GLASSERT_H
+#define GLASSERT_H
+
+#include "Runtime/Utilities/LogAssert.h"
+
+void CheckOpenGLError (const char *prefix, const char* file, long line);
+
+#if !UNITY_RELEASE
+ #ifndef GLAssert
+ /// Asserts for checking the OpenGL error state
+ #define GLAssert() { CheckOpenGLError (NULL, __FILE__, __LINE__); }
+ #endif
+ #define GLAssertString(x) { CheckOpenGLError (x, __FILE__, __LINE__); }
+ #define GL_CHK(x) do { {x;} GLAssert(); } while(0)
+#else
+
+ #ifndef GLAssert
+ #define GLAssert()
+ #endif
+ #define GLAssertString(x)
+ #define GL_CHK(x) x
+
+#endif
+
+#endif
diff --git a/Runtime/GfxDevice/opengl/GLContext.cpp b/Runtime/GfxDevice/opengl/GLContext.cpp
new file mode 100644
index 0000000..047bc26
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/GLContext.cpp
@@ -0,0 +1,506 @@
+#include "UnityPrefix.h"
+#include "GLContext.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/GfxDevice/GfxDeviceSetup.h"
+#include <set>
+#include "UnityGL.h"
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/Threads/Thread.h"
+#if UNITY_WIN
+#include "PlatformDependent/Win/WinUtils.h"
+#endif
+#if UNITY_OSX
+#if SUPPORT_AGL
+#include <AGL/agl.h>
+#endif
+#include <OpenGL/OpenGL.h>
+#include "Runtime/Graphics/ScreenManager.h"
+#elif UNITY_LINUX
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <GL/glx.h>
+#endif
+
+
+// define to 1 to print lots of context info
+#define DEBUG_GL_CONTEXT 0
+
+#if UNITY_OSX
+void CleanupMasterContextOSX (GraphicsContextGL* context); // GLContextOSX.cpp
+#endif
+
+
+static GraphicsContextHandle gMainGraphicsContext;
+static GraphicsContextHandle gMasterGraphicsContext;
+static GraphicsContexts gContexts;
+
+
+GraphicsContexts& GetGLContexts()
+{
+ return gContexts;
+}
+
+GraphicsContextHandle GetMainGraphicsContext()
+{
+ return gMainGraphicsContext;
+}
+
+void SetMainGraphicsContext( GraphicsContextHandle context )
+{
+ Assert(context.IsValid());
+ gMainGraphicsContext = context;
+}
+
+GraphicsContextHandle GetMasterGraphicsContext()
+{
+ #if UNITY_WIN
+ Assert(gMasterGraphicsContext.IsValid());
+ #elif UNITY_OSX || UNITY_LINUX
+ if( !gMasterGraphicsContext.IsValid() )
+ MakeMasterGLContext();
+ #else
+ #error "Unknown platform"
+ #endif
+
+ return gMasterGraphicsContext;
+}
+
+bool IsMasterGraphicsContextValid()
+{
+ return gMasterGraphicsContext.IsValid();
+}
+
+void PresentContextGL( GraphicsContextHandle contextHandle )
+{
+ AutoGfxDeviceAcquireThreadOwnership autoOwn;
+ if( !contextHandle.IsValid() )
+ return;
+
+ GraphicsContextGL* context = OBJECT_FROM_HANDLE(contextHandle,GraphicsContextGL);
+
+#if UNITY_WIN
+ BOOL ok = ::SwapBuffers( context->hdc );
+ if( !ok ) {
+ DWORD err = GetLastError();
+ }
+#elif UNITY_LINUX
+ if (gGraphicsCaps.gl.hasArbSync)
+ {
+ if (context->presentSync)
+ {
+ OGL_CALL(glClientWaitSync(static_cast<GLsync> (context->presentSync), 0, GL_TIMEOUT_IGNORED));
+ OGL_CALL(glDeleteSync(static_cast<GLsync> (context->presentSync)));
+ }
+ context->presentSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+ }
+
+ #if !WEBPLUG && !UNITY_EDITOR
+ GLint curFBO;
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &curFBO);
+ GetScreenManager().PreBlit();
+ #endif
+
+ glXSwapBuffers(reinterpret_cast<Display*> (context->display), context->window);
+
+ #if !WEBPLUG && !UNITY_EDITOR
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, curFBO);
+ #endif
+#elif UNITY_OSX
+ if (gGraphicsCaps.gl.hasAppleFence)
+ {
+ if (!context->appleFenceValid)
+ {
+ OGL_CALL(glGenFencesAPPLE(1, &context->appleFence));
+ context->appleFenceValid = true;
+ }
+ else
+ OGL_CALL(glFinishFenceAPPLE(context->appleFence));
+
+ OGL_CALL(glSetFenceAPPLE(context->appleFence));
+ }
+
+
+ #if WEBPLUG
+ // No need to swap in this case.
+ if ( GetScreenManager().IsUsingCoreAnimation() && !GetScreenManager().IsFullScreen() )
+ return;
+ #endif
+
+ #if !WEBPLUG && !UNITY_EDITOR
+ GLint curFBO;
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &curFBO);
+ GetScreenManager().PreBlit();
+ #endif
+
+ CGLFlushDrawable( context->cgl );
+ #if !WEBPLUG && !UNITY_EDITOR
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, curFBO);
+ #endif
+#else
+ #error "Unknown platform"
+#endif
+}
+
+#if UNITY_OSX
+void CleanupPresentSync (GraphicsContextGL* context)
+{
+ if (context == NULL)
+ return;
+
+ if (context->presentSync)
+ {
+ glDeleteSync(static_cast<GLsync> (context->presentSync));
+ context->presentSync = NULL;
+ }
+
+ if (context->appleFenceValid)
+ {
+ glDeleteFencesAPPLE(1, &context->appleFence);
+ context->appleFenceValid = false;
+ context->appleFence = 0;
+ }
+}
+#endif
+
+#if UNITY_LINUX
+void CleanupPresentSync (GraphicsContextGL* context)
+{
+ if (context && context->presentSync)
+ {
+ glDeleteSync(static_cast<GLsync> (context->presentSync));
+ context->presentSync = NULL;
+ }
+}
+#endif
+
+void DestroyContextGL( GraphicsContextHandle& contextHandle )
+{
+ AutoGfxDeviceAcquireThreadOwnership autoOwn;
+ ErrorIf( GetMasterGraphicsContext () == contextHandle );
+ if ( !contextHandle.IsValid() )
+ return;
+
+ // prevent stale pointer to main context
+ if( contextHandle == GetMainGraphicsContext() )
+ {
+ SetMainGraphicsContext( GetMasterGraphicsContext() );
+ }
+
+ GraphicsContextGL* context = OBJECT_FROM_HANDLE(contextHandle, GraphicsContextGL);
+
+ gContexts.erase( *context );
+
+ #if UNITY_WIN
+
+ #if DEBUG_GL_CONTEXT
+ printf_console( "CTX: destroy context %p\n", (DWORD)context->hglrc );
+ #endif
+
+ if (IsGfxDevice())
+ {
+ HGLRC curGL = wglGetCurrentContext ();
+ HDC curDC = wglGetCurrentDC ();
+ wglMakeCurrent (context->hdc, context->hglrc);
+ GetRealGfxDevice().UnbindObjects();
+ wglMakeCurrent (curDC, curGL);
+ }
+
+ wglDeleteContext( context->hglrc );
+ ReleaseDC( context->hwnd, context->hdc );
+
+ #elif UNITY_OSX
+
+ CleanupPresentSync(context);
+
+ #if DEBUG_GL_CONTEXT
+ printf_console( "CTX: destroy context %p\n", context->cgl );
+ #endif
+
+ if (IsGfxDevice())
+ {
+ CGLContextObj currentCtx = CGLGetCurrentContext();
+ CGLSetCurrentContext(context->cgl);
+ GetRealGfxDevice().UnbindObjects();
+ CGLSetCurrentContext (currentCtx);
+ }
+
+#if SUPPORT_AGL
+ if (context->agl)
+ {
+ if( aglDestroyContext(context->agl)==GL_FALSE )
+ printf_console( "aglDestroyContext failed!\n" );
+ }
+ else
+#endif
+ {
+ CGLError err = CGLDestroyContext(context->cgl);
+ if ( err )
+ printf_console( "CGLDestroyContext failed: %s!\n", CGLErrorString(err) );
+ }
+ #elif UNITY_LINUX
+
+ CleanupPresentSync(context);
+
+ #if DEBUG_GL_CONTEXT
+ printf_console( "CTX: destroy context %p\n", context->context);
+ #endif
+
+ Display *display = reinterpret_cast<Display*> (context->display);
+ GLXContext glxcontext = reinterpret_cast <GLXContext> (context->context);
+ if (IsGfxDevice())
+ {
+ GLXContext curGL = glXGetCurrentContext();
+ GLXDrawable curDrawable = glXGetCurrentDrawable ();
+ glXMakeCurrent(display, context->window, glxcontext);
+ GetRealGfxDevice().UnbindObjects();
+ glXMakeCurrent(display, curDrawable, curGL);
+ }
+
+ glXDestroyContext(display, glxcontext);
+
+ #else
+ #error "Unknown platform"
+ #endif
+
+ delete context;
+ contextHandle.Reset();
+}
+
+void DestroyMainContextGL()
+{
+ AutoGfxDeviceAcquireThreadOwnership autoOwn;
+ GraphicsContextHandle ctx = GetMainGraphicsContext();
+ if( ctx == GetMasterGraphicsContext() )
+ return;
+
+ if( ctx.IsValid() )
+ {
+ glFinish();
+ ActivateGraphicsContext( GetMasterGraphicsContext() );
+ DestroyContextGL( ctx );
+ }
+ SetMainGraphicsContext( GetMasterGraphicsContext() );
+}
+
+
+void CleanupMasterContext()
+{
+ AutoGfxDeviceAcquireThreadOwnership autoOwn;
+ Assert(gMasterGraphicsContext.IsValid());
+ GraphicsContextGL* context = OBJECT_FROM_HANDLE(gMasterGraphicsContext,GraphicsContextGL);
+ gContexts.erase( *context );
+
+ #if UNITY_WIN
+
+ #if DEBUG_GL_CONTEXT
+ printf_console( "GLDebug context: cleanup master context %x %s\n", (DWORD)context->hglrc, GetMasterContextClassName().c_str() );
+ #endif
+ wglMakeCurrent(NULL, NULL);
+
+ wglDeleteContext( context->hglrc );
+ #if DEBUG_GL_CONTEXT
+ printf_console( "GLDebug context: deleted glrc %x\n", context->hglrc );
+ #endif
+ ReleaseDC( context->hwnd, context->hdc );
+ #if DEBUG_GL_CONTEXT
+ printf_console( "GLDebug context: released dc %x for window %x\n", context->hdc, context->hwnd );
+ #endif
+ DestroyWindow( context->hwnd );
+ #if DEBUG_GL_CONTEXT
+ printf_console( "GLDebug context: destroyed window %x\n", context->hwnd );
+ #endif
+ winutils::UnregisterWindowClass( GetMasterContextClassName().c_str() );
+
+ #elif UNITY_OSX
+
+ CleanupMasterContextOSX (context);
+
+ #elif UNITY_LINUX
+ Display *display = reinterpret_cast <Display*> (context->display);
+ glFlush ();
+ glXMakeCurrent(display, None, NULL);
+ glXDestroyContext(display, reinterpret_cast<GLXContext> (context->context));
+
+ #else
+ #error "Unknown platform"
+ #endif
+
+ delete context;
+ gMasterGraphicsContext.Reset();
+
+ #if !UNITY_EDITOR // editor does not do cleanup
+ Assert(gContexts.empty());
+ #endif
+}
+
+
+bool ActivateGraphicsContextGL( const GraphicsContextGL& context, int flags )
+{
+ Assert(IsRealGfxDeviceThreadOwner());
+ bool result = true;
+
+ // In Web Player, do not unbind GL objects here:
+ // At least on Chrome/OSX, when switching to fullscreen (sometimes) calls
+ // into the plugin with an already destroyed
+ // GL context set as active and so we crash if we use that.
+ // Instead, unbind happens elsewhere for that case.
+ //
+ // However, have to unbind in the editor. Mostly when switching
+ // graphics emulation, multiple contexts for each view can get old
+ // stale state and picking would get wrong, case 408464.
+ #if !WEBPLUG
+ if( !(flags & kGLContextSkipUnbindObjects) && IsGfxDevice() )
+ GetRealGfxDevice().UnbindObjects();
+ #endif
+
+ #if UNITY_WIN
+ Assert(context.hglrc);
+
+ if( !wglMakeCurrent(context.hdc, context.hglrc) )
+ {
+ printf_console( "GLContext: failed to activate %x: %s\n", context.hglrc, WIN_LAST_ERROR_TEXT );
+ result = false;
+ }
+
+ #elif UNITY_OSX
+
+ Assert(context.cgl != NULL);
+ #if DEBUG_GL_CONTEXT
+ char const* master = gMasterGraphicsContext.object ? (&context == gMasterGraphicsContext.object ? " (MASTER)" : "") : " (MASTER NULL)";
+ printf_console( "CTX: activate %p%s\n", context.cgl, master );
+ #endif
+ // In general the previous context needs to be flushed whenever switching to a new one.
+ // This is even more important in case of multithreaded GL, where not flushing can
+ // result in random crashes.
+ //
+ // However, don't flush if explicitly told not to. We don't flush when activating context
+ // at start of web player loop, just after creating web player window, and so on (i.e. whenever
+ // context activation happens because we initialize something). Skipping flush gets rid of
+ // "first frame shows garbage or previous game" in most cases.
+ if( !(flags & kGLContextSkipFlush) && CGLGetCurrentContext() != NULL)
+ glFlush();
+
+ CGLError err = CGLSetCurrentContext(context.cgl);
+ if( err )
+ printf_console( "GLContext: CGLSetCurrentContext() failed: %s\n", CGLErrorString(err) );
+
+ #elif UNITY_LINUX
+ Assert (context.window);
+
+ if( !(flags & kGLContextSkipFlush) && glXGetCurrentContext() != NULL)
+ glFlush();
+
+ glXMakeCurrent(reinterpret_cast<Display*> (context.display), context.window, reinterpret_cast<GLXContext> (context.context));
+
+ #else
+ #error "Unknown platform"
+ #endif
+
+ if( !(flags & kGLContextSkipUnbindObjects) && IsGfxDevice() )
+ {
+ // Must unbind objects otherwise stuff falls apart in other places, like read pixels stop
+ // to work when OpenGL ES 1.1 is being emulated in the editor.
+ GetRealGfxDevice().UnbindObjects();
+ }
+ if( !(flags & kGLContextSkipInvalidateState) && IsGfxDevice() )
+ {
+ // In some cases we cannot invalidate, e.g. when destroying render textures (case 490767).
+ GetRealGfxDevice().InvalidateState();
+ }
+ return result;
+}
+
+
+bool ActivateGraphicsContext (GraphicsContextHandle ctx, bool currentThreadOnly, int flags)
+{
+ bool isMainThread = Thread::CurrentThreadIsMainThread();
+ if (isMainThread && IsGfxDevice())
+ {
+ GetGfxDevice().AcquireThreadOwnership();
+ }
+#if ENABLE_PROFILER
+ bool activeTimerQueries = false;
+ if (IsGfxDevice())
+ {
+ activeTimerQueries = GetRealGfxDevice().TimerQueriesIsActive();
+ if (activeTimerQueries)
+ GetRealGfxDevice().EndTimerQueries();
+ }
+#endif
+ Assert(isMainThread || IsRealGfxDeviceThreadOwner());
+ Assert (gMasterGraphicsContext.IsValid());
+ GraphicsContextGL* context = OBJECT_FROM_HANDLE(ctx,GraphicsContextGL);
+ bool res = ActivateGraphicsContextGL( *context, flags );
+#if ENABLE_PROFILER
+ if (activeTimerQueries)
+ GetRealGfxDevice().BeginTimerQueries();
+#endif
+ if (isMainThread && IsGfxDevice())
+ {
+ GetGfxDevice().ReleaseThreadOwnership();
+ }
+ if (!currentThreadOnly && IsGfxDevice() && isMainThread)
+ {
+ GetGfxDevice().SetActiveContext (context);
+ }
+ return res;
+}
+
+bool ActivateMasterContextGL()
+{
+ GraphicsContextHandle ctx = GetMasterGraphicsContext();
+ ErrorIf( !ctx.IsValid() );
+ return ActivateGraphicsContext (ctx);
+}
+
+bool ActivateMainContextGL ()
+{
+ GraphicsContextHandle ctx = GetMainGraphicsContext();
+ if( ctx.IsValid() )return ActivateGraphicsContext (ctx);
+ return false;
+}
+
+void AssignMasterGraphicsContextGL( GraphicsContextGL* ctx )
+{
+ Assert(!gMasterGraphicsContext.IsValid());
+ gMasterGraphicsContext.object = ctx;
+}
+
+
+
+#if UNITY_OSX
+
+GraphicsContextGL GetCurrentGraphicsContext ()
+{
+ GraphicsContextGL ctx;
+ ctx.cgl = CGLGetCurrentContext();
+#if SUPPORT_AGL
+ ctx.agl = aglGetCurrentContext();
+#endif
+ return ctx;
+}
+
+#elif UNITY_WIN
+
+GraphicsContextGL GetCurrentGraphicsContext ()
+{
+ GraphicsContextGL ctx;
+ ctx.hdc = wglGetCurrentDC();
+ ctx.hglrc = wglGetCurrentContext();
+ return ctx;
+}
+
+#elif UNITY_LINUX
+
+GraphicsContextGL GetCurrentGraphicsContext ()
+{
+ GraphicsContextGL ctx;
+ ctx.context = glXGetCurrentContext();
+ ctx.window = (Window)glXGetCurrentDrawable();
+ ctx.display = GetDisplay();
+ return ctx;
+}
+
+#else
+#error "Unknown platform"
+#endif
diff --git a/Runtime/GfxDevice/opengl/GLContext.h b/Runtime/GfxDevice/opengl/GLContext.h
new file mode 100644
index 0000000..42e628a
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/GLContext.h
@@ -0,0 +1,195 @@
+#pragma once
+
+#include "Runtime/GfxDevice/GfxDeviceObjects.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+#if UNITY_WIN
+ struct GraphicsContextGL
+ {
+ HDC hdc;
+ HGLRC hglrc;
+ HWND hwnd;
+
+ GraphicsContextGL() : hdc(NULL), hwnd(NULL), hglrc(NULL)
+ {
+ }
+ ~GraphicsContextGL()
+ {
+ hdc = NULL;hwnd = NULL; hglrc = NULL;
+ }
+
+ bool IsValid()const { return hglrc != NULL; }
+ friend bool operator == (const GraphicsContextGL& lhs, const GraphicsContextGL& rhs) { return lhs.hglrc == rhs.hglrc; }
+ friend bool operator < (const GraphicsContextGL& lhs, const GraphicsContextGL& rhs) { return lhs.hglrc < rhs.hglrc; }
+ friend bool operator != (const GraphicsContextGL& lhs, const GraphicsContextGL& rhs) { return lhs.hglrc != rhs.hglrc; }
+ };
+#elif UNITY_OSX
+ #define SUPPORT_AGL (!UNITY_64)
+ struct __AGLContextRec;
+ typedef struct __AGLContextRec *AGLContext;
+
+ struct _CGLContextObject;
+ typedef struct _CGLContextObject *CGLContext;
+
+ struct _CGLPixelFormatObject;
+ typedef struct _CGLPixelFormatObject *CGLPixelFormatObj;
+
+ // 10.5 SDK defines all CGL functions to use GLint, whereas 10.4 is long. Make this work in both.
+ #ifndef MAC_OS_X_VERSION_MAX_ALLOWED
+ #error Fail
+ #elif MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
+ #define CGLint long
+ #else
+ #define CGLint int
+ #endif
+
+ struct GraphicsContextGL
+ {
+ CGLContext cgl;
+ #if SUPPORT_AGL
+ AGLContext agl;
+ #endif
+
+ void* presentSync;
+ uint appleFence;
+ bool appleFenceValid;
+
+ GraphicsContextGL() {
+ cgl = NULL;
+ #if SUPPORT_AGL
+ agl = NULL;
+ #endif
+ presentSync = 0;
+ appleFence = 0;
+ appleFenceValid = false;
+ }
+
+ friend bool operator == (const GraphicsContextGL& lhs, const GraphicsContextGL& rhs) { return lhs.cgl == rhs.cgl; }
+ friend bool operator < (const GraphicsContextGL& lhs, const GraphicsContextGL& rhs) { return lhs.cgl < rhs.cgl; }
+ friend bool operator != (const GraphicsContextGL& lhs, const GraphicsContextGL& rhs) { return lhs.cgl != rhs.cgl; }
+ };
+
+ #define CGL_FROM_HANDLE(context) (*OBJECT_FROM_HANDLE(context,GraphicsContextGL)).cgl
+#if SUPPORT_AGL
+ #define AGL_FROM_HANDLE(context) (*OBJECT_FROM_HANDLE(context,GraphicsContextGL)).agl
+#endif
+#elif UNITY_LINUX
+ #include "UnityGL.h"
+ #include "PlatformDependent/Linux/X11Quarantine.h"
+
+ struct GraphicsContextGL
+ {
+ NativeDisplayPtr display;
+ NativeWindow window;
+ void *context;
+ void *presentSync;
+
+ GraphicsContextGL() : window(0), context(NULL), presentSync(NULL)
+ {
+ }
+ ~GraphicsContextGL()
+ {
+ window = 0;
+ context = NULL;
+ }
+
+ bool IsValid()const { return window != 0; }
+ friend bool operator == (const GraphicsContextGL& lhs, const GraphicsContextGL& rhs) { return lhs.context == rhs.context; }
+ friend bool operator < (const GraphicsContextGL& lhs, const GraphicsContextGL& rhs) { return lhs.context < rhs.context; }
+ friend bool operator != (const GraphicsContextGL& lhs, const GraphicsContextGL& rhs) { return lhs.context != rhs.context; }
+ };
+ #define GLX_FROM_HANDLE(context) (*OBJECT_FROM_HANDLE(context,GraphicsContextGL)).context
+#else
+ #error "Unknown platform"
+#endif
+
+// Returns the master context
+// Mac: if necessary creates it
+// Windows: explicitly create master context earlier, with CreateMasterGraphicsContext()
+GraphicsContextHandle GetMasterGraphicsContext();
+bool IsMasterGraphicsContextValid();
+
+void SetMainGraphicsContext( GraphicsContextHandle ctx );
+GraphicsContextHandle GetMainGraphicsContext ();
+
+void DestroyContextGL( GraphicsContextHandle& context );
+void DestroyMainContextGL();
+
+// Makes the context active. And sets up some default state.
+bool ActivateGraphicsContext (GraphicsContextHandle ctx, bool currentThreadOnly = false, int flags = 0);
+bool ActivateMasterContextGL ();
+bool ActivateMainContextGL ();
+bool ActivateGraphicsContextGL ( const GraphicsContextGL& ctx, int flags );
+void AssignMasterGraphicsContextGL ( GraphicsContextGL* ctx );
+GraphicsContextGL GetCurrentGraphicsContext ();
+
+void PresentContextGL ( GraphicsContextHandle ctx );
+
+#if UNITY_WIN
+ void SetMasterContextClassName ( const std::wstring& windowClassName );
+ const std::wstring& GetMasterContextClassName ();
+
+ GraphicsContextHandle SetupGraphicsContextFromWindow ( HWND window, int width, int height, int inFSAA, int& outFSAA );
+#endif
+
+
+
+
+typedef std::set<GraphicsContextGL> GraphicsContexts;
+GraphicsContexts& GetGLContexts();
+
+enum {
+ kGLContextSkipInvalidateState = 1 << 0,
+ kGLContextSkipUnbindObjects = 1 << 1,
+ kGLContextSkipFlush = 2 << 2,
+};
+
+
+#if UNITY_OSX
+ void CleanupMasterContext();
+
+ GraphicsContextHandle MakeNewContext( int width, int height, int fullscreen, bool doubleBuffer, bool pbuffer, DepthBufferFormat depthFormat, int* inoutAA, bool agl);
+ GraphicsContextHandle MakeOffScreenContext (int width, int height, int depthBits, int stencilBits);
+ GraphicsContextGL MakeNewContextGL( int width, int height, int fullscreen, bool doubleBuffer, bool pbuffer, bool depthTexture, DepthBufferFormat depthFormat, int* inoutAA, bool agl);
+ GraphicsContextGL MakeOffScreenContextGL (int width, int height, int depthBits, int stencilBits);
+ void GeneratePixelAttributes (int bits, int depthBits, int stencilBits, const void* cglPixelFormatAttributes, bool doubleBuffer,bool fullscreen, int supersample, bool pbuffer);
+ void SetupDefaultContextState( GraphicsContextGL &context, bool setMultiThreaded, int fsaa );
+
+ void CleanupPresentSync (GraphicsContextGL* context);
+
+ void SetSyncToVBL (GraphicsContextHandle context, int syncCount);
+
+ void SetDisplayID( CGDirectDisplayID display );
+
+ void MakeMasterGLContext();
+
+ #if UNITY_EDITOR
+ void GLFinishAllGraphicsContexts ();
+ void SetContextDrawable( GraphicsContextHandle context, CGrafPtr port, const float* frame );
+ void UpdateContextDrawable( GraphicsContextHandle context, const float* frame );
+ #endif
+#elif UNITY_LINUX
+ void CleanupMasterContext();
+ void CleanupPresentSync (GraphicsContextGL* context);
+
+ GraphicsContextHandle MakeNewContext( int width, int height, int fullscreen, bool doubleBuffer, bool pbuffer, DepthBufferFormat depthFormat, int antiAlias);
+ GraphicsContextHandle MakeOffScreenContext (int width, int height, int depthBits, int stencilBits);
+ GraphicsContextGL MakeNewContextGL (NativeDisplayPtr display, NativeWindow window, int width, int height, int fullscreen, bool doubleBuffer, bool pbuffer, bool depthTexture, DepthBufferFormat depthFormat, int antiAlias);
+ GraphicsContextGL MakeOffScreenContextGL (int width, int height, int depthBits, int stencilBits);
+ void SetupDefaultContextState( GraphicsContextGL &context, bool setMultiThreaded, int fsaa );
+
+ void SetSyncToVBL (GraphicsContextHandle context, int syncCount);
+ void SetDisplay(NativeDisplayPtr display);
+ NativeDisplayPtr GetDisplay();
+
+ void MakeMasterGLContext();
+ void MakeMasterGLContext(NativeDisplayPtr display, NativeWindow window);
+ GraphicsContextGL* MakeGLContextForWindow(NativeDisplayPtr display, NativeWindow window, bool shareWithMaster=true);
+ VisualInfoPtr CreateVisualInfo (NativeDisplayPtr display, int antialiasingLevel=0);
+
+ #if UNITY_EDITOR
+ void GLFinishAllGraphicsContexts ();
+ void UpdateContextDrawable( GraphicsContextHandle context, const float* frame );
+ GraphicsContextHandle SetupGraphicsContextFromWindow (NativeWindow window, int width, int height, int inFSAA, int& outFSAA);
+ #endif
+#endif
diff --git a/Runtime/GfxDevice/opengl/GLExtensionDefs.h b/Runtime/GfxDevice/opengl/GLExtensionDefs.h
new file mode 100644
index 0000000..ba5f055
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/GLExtensionDefs.h
@@ -0,0 +1,247 @@
+// This file is automatically generated with Runtime/GfxDevice/opengl/GenerateGLExtensionDef.pl.
+// It is generated from GLExtensionDefs.txt
+
+
+//Tex coords
+DEF (PFNGLACTIVETEXTUREPROC, glActiveTextureARB);
+#define glActiveTextureARB UNITYGL_glActiveTextureARB
+DEF (PFNGLCLIENTACTIVETEXTUREPROC, glClientActiveTextureARB);
+#define glClientActiveTextureARB UNITYGL_glClientActiveTextureARB
+DEF (PFNGLMULTITEXCOORD3FPROC, glMultiTexCoord3fARB);
+#define glMultiTexCoord3fARB UNITYGL_glMultiTexCoord3fARB
+DEF (PFNGLMULTITEXCOORD4FPROC, glMultiTexCoord4fARB);
+#define glMultiTexCoord4fARB UNITYGL_glMultiTexCoord4fARB
+DEF (PFNGLMULTITEXCOORD3FVPROC, glMultiTexCoord3fvARB);
+#define glMultiTexCoord3fvARB UNITYGL_glMultiTexCoord3fvARB
+DEF (PFNGLMULTITEXCOORD4FVPROC, glMultiTexCoord4fvARB);
+#define glMultiTexCoord4fvARB UNITYGL_glMultiTexCoord4fvARB
+
+//Blending
+DEF (PFNGLBLENDCOLORPROC, glBlendColor);
+#define glBlendColor UNITYGL_glBlendColor
+DEF (PFNGLBLENDFUNCSEPARATEEXTPROC, glBlendFuncSeparateEXT);
+#define glBlendFuncSeparateEXT UNITYGL_glBlendFuncSeparateEXT
+DEF (PFNGLBLENDEQUATIONPROC, glBlendEquation);
+#define glBlendEquation UNITYGL_glBlendEquation
+DEF (PFNGLBLENDEQUATIONSEPARATEEXTPROC, glBlendEquationSeparateEXT);
+#define glBlendEquationSeparateEXT UNITYGL_glBlendEquationSeparateEXT
+
+// ARB_VP, ARB FP
+DEF (PFNGLDELETEPROGRAMSARBPROC, glDeleteProgramsARB);
+#define glDeleteProgramsARB UNITYGL_glDeleteProgramsARB
+DEF (PFNGLGENPROGRAMSARBPROC, glGenProgramsARB);
+#define glGenProgramsARB UNITYGL_glGenProgramsARB
+DEF (PFNGLBINDPROGRAMARBPROC, glBindProgramARB);
+#define glBindProgramARB UNITYGL_glBindProgramARB
+DEF (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC, glProgramLocalParameter4fvARB);
+#define glProgramLocalParameter4fvARB UNITYGL_glProgramLocalParameter4fvARB
+DEF (PFNGLPROGRAMENVPARAMETER4FVARBPROC, glProgramEnvParameter4fvARB);
+#define glProgramEnvParameter4fvARB UNITYGL_glProgramEnvParameter4fvARB
+DEF (PFNGLPROGRAMLOCALPARAMETER4FARBPROC, glProgramLocalParameter4fARB);
+#define glProgramLocalParameter4fARB UNITYGL_glProgramLocalParameter4fARB
+DEF (PFNGLPROGRAMENVPARAMETER4FARBPROC, glProgramEnvParameter4fARB);
+#define glProgramEnvParameter4fARB UNITYGL_glProgramEnvParameter4fARB
+DEF (PFNGLPROGRAMSTRINGARBPROC, glProgramStringARB);
+#define glProgramStringARB UNITYGL_glProgramStringARB
+DEF (PFNGLGETPROGRAMENVPARAMETERFVARBPROC, glGetProgramEnvParameterfvARB);
+#define glGetProgramEnvParameterfvARB UNITYGL_glGetProgramEnvParameterfvARB
+
+// Textures
+DEF (PFNGLCOMPRESSEDTEXIMAGE2DARBPROC, glCompressedTexImage2DARB);
+#define glCompressedTexImage2DARB UNITYGL_glCompressedTexImage2DARB
+DEF (PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC, glCompressedTexSubImage2DARB);
+#define glCompressedTexSubImage2DARB UNITYGL_glCompressedTexSubImage2DARB
+DEF (PFNGLTEXIMAGE3DPROC, glTexImage3D);
+#define glTexImage3D UNITYGL_glTexImage3D
+
+#if UNITY_OSX
+// Apple fences
+DEF (PFNGLGENFENCESAPPLEPROC, glGenFencesAPPLE);
+#define glGenFencesAPPLE UNITYGL_glGenFencesAPPLE
+DEF (PFNGLDELETEFENCESAPPLEPROC, glDeleteFencesAPPLE);
+#define glDeleteFencesAPPLE UNITYGL_glDeleteFencesAPPLE
+DEF (PFNGLSETFENCEAPPLEPROC, glSetFenceAPPLE);
+#define glSetFenceAPPLE UNITYGL_glSetFenceAPPLE
+DEF (PFNGLISFENCEAPPLEPROC, glIsFenceAPPLE);
+#define glIsFenceAPPLE UNITYGL_glIsFenceAPPLE
+DEF (PFNGLTESTFENCEAPPLEPROC, glTestFenceAPPLE);
+#define glTestFenceAPPLE UNITYGL_glTestFenceAPPLE
+DEF (PFNGLFINISHFENCEAPPLEPROC, glFinishFenceAPPLE);
+#define glFinishFenceAPPLE UNITYGL_glFinishFenceAPPLE
+DEF (PFNGLFINISHOBJECTAPPLEPROC, glFinishObjectAPPLE);
+#define glFinishObjectAPPLE UNITYGL_glFinishObjectAPPLE
+DEF (PFNGLTESTOBJECTAPPLEPROC, glTestObjectAPPLE);
+#define glTestObjectAPPLE UNITYGL_glTestObjectAPPLE
+
+// APPLE_flush_buffer_range
+DEF (PFNGLBUFFERPARAMETERIAPPLEPROC, glBufferParameteriAPPLE);
+#define glBufferParameteriAPPLE UNITYGL_glBufferParameteriAPPLE
+DEF (PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC, glFlushMappedBufferRangeAPPLE);
+#define glFlushMappedBufferRangeAPPLE UNITYGL_glFlushMappedBufferRangeAPPLE
+#endif
+
+// ARB_vertex_buffer_object
+DEF (PFNGLGENBUFFERSARBPROC, glGenBuffersARB);
+#define glGenBuffersARB UNITYGL_glGenBuffersARB
+DEF (PFNGLDELETEBUFFERSARBPROC, glDeleteBuffersARB);
+#define glDeleteBuffersARB UNITYGL_glDeleteBuffersARB
+DEF (PFNGLBUFFERDATAARBPROC, glBufferDataARB);
+#define glBufferDataARB UNITYGL_glBufferDataARB
+DEF (PFNGLBINDBUFFERARBPROC, glBindBufferARB);
+#define glBindBufferARB UNITYGL_glBindBufferARB
+DEF (PFNGLMAPBUFFERARBPROC, glMapBufferARB);
+#define glMapBufferARB UNITYGL_glMapBufferARB
+DEF (PFNGLUNMAPBUFFERARBPROC, glUnmapBufferARB);
+#define glUnmapBufferARB UNITYGL_glUnmapBufferARB
+DEF (PFNGLBUFFERSUBDATAARBPROC, glBufferSubDataARB);
+#define glBufferSubDataARB UNITYGL_glBufferSubDataARB
+
+// ARB_map_buffer_range
+DEF (PFNGLMAPBUFFERRANGEPROC, glMapBufferRange);
+#define glMapBufferRange UNITYGL_glMapBufferRange
+
+// ARB_vertex_program
+DEF (PFNGLGETPROGRAMIVARBPROC, glGetProgramivARB);
+#define glGetProgramivARB UNITYGL_glGetProgramivARB
+DEF (PFNGLVERTEXATTRIBPOINTERARBPROC, glVertexAttribPointerARB);
+#define glVertexAttribPointerARB UNITYGL_glVertexAttribPointerARB
+DEF (PFNGLENABLEVERTEXATTRIBARRAYARBPROC, glEnableVertexAttribArrayARB);
+#define glEnableVertexAttribArrayARB UNITYGL_glEnableVertexAttribArrayARB
+DEF (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC, glDisableVertexAttribArrayARB);
+#define glDisableVertexAttribArrayARB UNITYGL_glDisableVertexAttribArrayARB
+
+// ARB_occlusion query
+DEF (PFNGLGENQUERIESARBPROC, glGenQueriesARB);
+#define glGenQueriesARB UNITYGL_glGenQueriesARB
+DEF (PFNGLBEGINQUERYARBPROC, glBeginQueryARB);
+#define glBeginQueryARB UNITYGL_glBeginQueryARB
+DEF (PFNGLENDQUERYARBPROC, glEndQueryARB);
+#define glEndQueryARB UNITYGL_glEndQueryARB
+DEF (PFNGLGETQUERYOBJECTIVARBPROC, glGetQueryObjectivARB);
+#define glGetQueryObjectivARB UNITYGL_glGetQueryObjectivARB
+DEF (PFNGLGETQUERYOBJECTUIVARBPROC, glGetQueryObjectuivARB);
+#define glGetQueryObjectuivARB UNITYGL_glGetQueryObjectuivARB
+DEF (PFNGLDELETEQUERIESARBPROC, glDeleteQueriesARB);
+#define glDeleteQueriesARB UNITYGL_glDeleteQueriesARB
+
+// EXT_timer query
+DEF (PFNGLGETQUERYOBJECTI64VEXTPROC, glGetQueryObjecti64vEXT);
+#define glGetQueryObjecti64vEXT UNITYGL_glGetQueryObjecti64vEXT
+DEF (PFNGLGETQUERYOBJECTUI64VEXTPROC, glGetQueryObjectui64vEXT);
+#define glGetQueryObjectui64vEXT UNITYGL_glGetQueryObjectui64vEXT
+
+// ARB_sync
+DEF (PFNGLFENCESYNCPROC, glFenceSync);
+#define glFenceSync UNITYGL_glFenceSync
+DEF (PFNGLDELETESYNCPROC, glDeleteSync);
+#define glDeleteSync UNITYGL_glDeleteSync
+DEF (PFNGLCLIENTWAITSYNCPROC, glClientWaitSync);
+#define glClientWaitSync UNITYGL_glClientWaitSync
+
+// GLSL
+DEF (PFNGLCREATEPROGRAMOBJECTARBPROC, glCreateProgramObjectARB);
+#define glCreateProgramObjectARB UNITYGL_glCreateProgramObjectARB
+DEF (PFNGLCREATESHADEROBJECTARBPROC, glCreateShaderObjectARB);
+#define glCreateShaderObjectARB UNITYGL_glCreateShaderObjectARB
+DEF (PFNGLSHADERSOURCEARBPROC, glShaderSourceARB);
+#define glShaderSourceARB UNITYGL_glShaderSourceARB
+DEF (PFNGLCOMPILESHADERARBPROC, glCompileShaderARB);
+#define glCompileShaderARB UNITYGL_glCompileShaderARB
+DEF (PFNGLATTACHOBJECTARBPROC, glAttachObjectARB);
+#define glAttachObjectARB UNITYGL_glAttachObjectARB
+DEF (PFNGLLINKPROGRAMARBPROC, glLinkProgramARB);
+#define glLinkProgramARB UNITYGL_glLinkProgramARB
+DEF (PFNGLUSEPROGRAMOBJECTARBPROC, glUseProgramObjectARB);
+#define glUseProgramObjectARB UNITYGL_glUseProgramObjectARB
+DEF (PFNGLDELETEOBJECTARBPROC, glDeleteObjectARB);
+#define glDeleteObjectARB UNITYGL_glDeleteObjectARB
+DEF (PFNGLGETHANDLEARBPROC, glGetHandleARB);
+#define glGetHandleARB UNITYGL_glGetHandleARB
+DEF (PFNGLGETINFOLOGARBPROC, glGetInfoLogARB);
+#define glGetInfoLogARB UNITYGL_glGetInfoLogARB
+DEF (PFNGLGETACTIVEATTRIBARBPROC, glGetActiveAttribARB);
+#define glGetActiveAttribARB UNITYGL_glGetActiveAttribARB
+DEF (PFNGLGETACTIVEUNIFORMARBPROC, glGetActiveUniformARB);
+#define glGetActiveUniformARB UNITYGL_glGetActiveUniformARB
+DEF (PFNGLGETUNIFORMLOCATIONARBPROC, glGetUniformLocationARB);
+#define glGetUniformLocationARB UNITYGL_glGetUniformLocationARB
+DEF (PFNGLGETATTRIBLOCATIONARBPROC, glGetAttribLocationARB);
+#define glGetAttribLocationARB UNITYGL_glGetAttribLocationARB
+DEF (PFNGLGETOBJECTPARAMETERIVARBPROC, glGetObjectParameterivARB);
+#define glGetObjectParameterivARB UNITYGL_glGetObjectParameterivARB
+DEF (PFNGLUNIFORM1FVARBPROC, glUniform1fvARB);
+#define glUniform1fvARB UNITYGL_glUniform1fvARB
+DEF (PFNGLUNIFORM2FVARBPROC, glUniform2fvARB);
+#define glUniform2fvARB UNITYGL_glUniform2fvARB
+DEF (PFNGLUNIFORM3FVARBPROC, glUniform3fvARB);
+#define glUniform3fvARB UNITYGL_glUniform3fvARB
+DEF (PFNGLUNIFORM4FVARBPROC, glUniform4fvARB);
+#define glUniform4fvARB UNITYGL_glUniform4fvARB
+DEF (PFNGLUNIFORMMATRIX4FVARBPROC, glUniformMatrix4fvARB);
+#define glUniformMatrix4fvARB UNITYGL_glUniformMatrix4fvARB
+DEF (PFNGLUNIFORM1IVARBPROC, glUniform1ivARB);
+#define glUniform1ivARB UNITYGL_glUniform1ivARB
+DEF (PFNGLUNIFORM2IVARBPROC, glUniform2ivARB);
+#define glUniform2ivARB UNITYGL_glUniform2ivARB
+DEF (PFNGLUNIFORM3IVARBPROC, glUniform3ivARB);
+#define glUniform3ivARB UNITYGL_glUniform3ivARB
+DEF (PFNGLUNIFORM4IVARBPROC, glUniform4ivARB);
+#define glUniform4ivARB UNITYGL_glUniform4ivARB
+
+#if UNITY_WIN
+// Windows P-buffers
+DEF (PFNWGLCHOOSEPIXELFORMATARBPROC, wglChoosePixelFormatARB);
+#define wglChoosePixelFormatARB UNITYGL_wglChoosePixelFormatARB
+DEF (PFNWGLGETPIXELFORMATATTRIBIVARBPROC, wglGetPixelFormatAttribivARB);
+#define wglGetPixelFormatAttribivARB UNITYGL_wglGetPixelFormatAttribivARB
+DEF (PFNWGLGETEXTENSIONSSTRINGARBPROC, wglGetExtensionsStringARB);
+#define wglGetExtensionsStringARB UNITYGL_wglGetExtensionsStringARB
+DEF (PFNWGLGETEXTENSIONSSTRINGEXTPROC, wglGetExtensionsStringEXT);
+#define wglGetExtensionsStringEXT UNITYGL_wglGetExtensionsStringEXT
+
+// Windows VSync
+DEF (PFNWGLSWAPINTERVALEXTPROC, wglSwapIntervalEXT);
+#define wglSwapIntervalEXT UNITYGL_wglSwapIntervalEXT
+#endif
+
+// GL_EXT_framebuffer_object
+DEF (PFNGLGENFRAMEBUFFERSEXTPROC, glGenFramebuffersEXT);
+#define glGenFramebuffersEXT UNITYGL_glGenFramebuffersEXT
+DEF (PFNGLBINDFRAMEBUFFEREXTPROC, glBindFramebufferEXT);
+#define glBindFramebufferEXT UNITYGL_glBindFramebufferEXT
+DEF (PFNGLGENRENDERBUFFERSEXTPROC, glGenRenderbuffersEXT);
+#define glGenRenderbuffersEXT UNITYGL_glGenRenderbuffersEXT
+DEF (PFNGLBINDRENDERBUFFEREXTPROC, glBindRenderbufferEXT);
+#define glBindRenderbufferEXT UNITYGL_glBindRenderbufferEXT
+DEF (PFNGLRENDERBUFFERSTORAGEEXTPROC, glRenderbufferStorageEXT);
+#define glRenderbufferStorageEXT UNITYGL_glRenderbufferStorageEXT
+DEF (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC, glFramebufferRenderbufferEXT);
+#define glFramebufferRenderbufferEXT UNITYGL_glFramebufferRenderbufferEXT
+DEF (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC, glFramebufferTexture2DEXT);
+#define glFramebufferTexture2DEXT UNITYGL_glFramebufferTexture2DEXT
+DEF (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC, glCheckFramebufferStatusEXT);
+#define glCheckFramebufferStatusEXT UNITYGL_glCheckFramebufferStatusEXT
+DEF (PFNGLDELETEFRAMEBUFFERSEXTPROC, glDeleteFramebuffersEXT);
+#define glDeleteFramebuffersEXT UNITYGL_glDeleteFramebuffersEXT
+DEF (PFNGLDELETERENDERBUFFERSEXTPROC, glDeleteRenderbuffersEXT);
+#define glDeleteRenderbuffersEXT UNITYGL_glDeleteRenderbuffersEXT
+DEF (PFNGLGENERATEMIPMAPEXTPROC, glGenerateMipmapEXT);
+#define glGenerateMipmapEXT UNITYGL_glGenerateMipmapEXT
+
+// GL_ARB_draw_buffers
+DEF (PFNGLDRAWBUFFERSARBPROC, glDrawBuffersARB);
+#define glDrawBuffersARB UNITYGL_glDrawBuffersARB
+
+// GL_EXT_framebuffer_blit
+DEF (PFNGLBLITFRAMEBUFFEREXTPROC, glBlitFramebufferEXT);
+#define glBlitFramebufferEXT UNITYGL_glBlitFramebufferEXT
+
+// GL_EXT_framebuffer_multisample
+DEF (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC, glRenderbufferStorageMultisampleEXT);
+#define glRenderbufferStorageMultisampleEXT UNITYGL_glRenderbufferStorageMultisampleEXT
+
+// Separate stencil in GL2.0
+DEF (PFNGLSTENCILFUNCSEPARATEPROC, glStencilFuncSeparate);
+#define glStencilFuncSeparate UNITYGL_glStencilFuncSeparate
+DEF (PFNGLSTENCILOPSEPARATEPROC, glStencilOpSeparate);
+#define glStencilOpSeparate UNITYGL_glStencilOpSeparate
diff --git a/Runtime/GfxDevice/opengl/GLExtensionDefs.txt b/Runtime/GfxDevice/opengl/GLExtensionDefs.txt
new file mode 100644
index 0000000..6cc3d6d
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/GLExtensionDefs.txt
@@ -0,0 +1,145 @@
+
+
+//Tex coords
+glActiveTextureARB->glActiveTexture
+glClientActiveTextureARB->glClientActiveTexture
+glMultiTexCoord3fARB->glMultiTexCoord3f
+glMultiTexCoord4fARB->glMultiTexCoord4f
+glMultiTexCoord3fvARB->glMultiTexCoord3fv
+glMultiTexCoord4fvARB->glMultiTexCoord4fv
+
+//Blending
+glBlendColor
+glBlendFuncSeparateEXT
+glBlendEquation
+glBlendEquationSeparateEXT
+
+// ARB_VP, ARB FP
+glDeleteProgramsARB
+glGenProgramsARB
+glBindProgramARB
+glProgramLocalParameter4fvARB
+glProgramEnvParameter4fvARB
+glProgramLocalParameter4fARB
+glProgramEnvParameter4fARB
+glProgramStringARB
+glGetProgramEnvParameterfvARB
+
+// Textures
+glCompressedTexImage2DARB
+glCompressedTexSubImage2DARB
+glTexImage3D
+
+#if UNITY_OSX
+// Apple fences
+glGenFencesAPPLE
+glDeleteFencesAPPLE
+glSetFenceAPPLE
+glIsFenceAPPLE
+glTestFenceAPPLE
+glFinishFenceAPPLE
+glFinishObjectAPPLE
+glTestObjectAPPLE
+
+// APPLE_flush_buffer_range
+glBufferParameteriAPPLE
+glFlushMappedBufferRangeAPPLE
+#endif
+
+// ARB_vertex_buffer_object
+glGenBuffersARB
+glDeleteBuffersARB
+glBufferDataARB
+glBindBufferARB
+glMapBufferARB
+glUnmapBufferARB
+glBufferSubDataARB
+
+// ARB_map_buffer_range
+glMapBufferRange
+
+// ARB_vertex_program
+glGetProgramivARB
+glVertexAttribPointerARB
+glEnableVertexAttribArrayARB
+glDisableVertexAttribArrayARB
+
+// ARB_occlusion query
+glGenQueriesARB
+glBeginQueryARB
+glEndQueryARB
+glGetQueryObjectivARB
+glGetQueryObjectuivARB
+glDeleteQueriesARB
+
+// EXT_timer query
+glGetQueryObjecti64vEXT
+glGetQueryObjectui64vEXT
+
+// ARB_sync
+glFenceSync
+glDeleteSync
+glClientWaitSync
+
+// GLSL
+glCreateProgramObjectARB
+glCreateShaderObjectARB
+glShaderSourceARB
+glCompileShaderARB
+glAttachObjectARB
+glLinkProgramARB
+glUseProgramObjectARB
+glDeleteObjectARB
+glGetHandleARB
+glGetInfoLogARB
+glGetActiveAttribARB
+glGetActiveUniformARB
+glGetUniformLocationARB
+glGetAttribLocationARB
+glGetObjectParameterivARB
+glUniform1fvARB
+glUniform2fvARB
+glUniform3fvARB
+glUniform4fvARB
+glUniformMatrix4fvARB
+glUniform1ivARB
+glUniform2ivARB
+glUniform3ivARB
+glUniform4ivARB
+
+#if UNITY_WIN
+// Windows P-buffers
+wglChoosePixelFormatARB
+wglGetPixelFormatAttribivARB
+wglGetExtensionsStringARB
+wglGetExtensionsStringEXT
+
+// Windows VSync
+wglSwapIntervalEXT
+#endif
+
+// GL_EXT_framebuffer_object
+glGenFramebuffersEXT
+glBindFramebufferEXT
+glGenRenderbuffersEXT
+glBindRenderbufferEXT
+glRenderbufferStorageEXT
+glFramebufferRenderbufferEXT
+glFramebufferTexture2DEXT
+glCheckFramebufferStatusEXT
+glDeleteFramebuffersEXT
+glDeleteRenderbuffersEXT
+glGenerateMipmapEXT
+
+// GL_ARB_draw_buffers
+glDrawBuffersARB
+
+// GL_EXT_framebuffer_blit
+glBlitFramebufferEXT
+
+// GL_EXT_framebuffer_multisample
+glRenderbufferStorageMultisampleEXT
+
+// Separate stencil in GL2.0
+glStencilFuncSeparate
+glStencilOpSeparate
diff --git a/Runtime/GfxDevice/opengl/GLWindow.h b/Runtime/GfxDevice/opengl/GLWindow.h
new file mode 100644
index 0000000..53f79b9
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/GLWindow.h
@@ -0,0 +1,29 @@
+#ifndef GLWINDOW_H
+#define GLWINDOW_H
+
+#include "UnityGL.h"
+#include "Runtime/GfxDevice/GfxDeviceWindow.h"
+#include "GLContext.h"
+
+class GLWindow : public GfxDeviceWindow
+{
+private:
+ GraphicsContextHandle m_GLContext;
+ int cc;
+public:
+ GLWindow (NativeWindow window, int width, int height, DepthBufferFormat depthFormat, int antiAlias);
+ ~GLWindow();
+
+ bool Reshape( int width, int height, DepthBufferFormat depthFormat, int antiAlias );
+
+ bool BeginRendering();
+ bool EndRendering( bool presentContent );
+
+public:
+ static GLWindow* Current();
+
+
+};
+
+
+#endif
diff --git a/Runtime/GfxDevice/opengl/GenerateGLExtensionDef.pl b/Runtime/GfxDevice/opengl/GenerateGLExtensionDef.pl
new file mode 100644
index 0000000..81310df
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/GenerateGLExtensionDef.pl
@@ -0,0 +1,48 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+open FH, "GLExtensionDefs.txt" or die $_;
+
+my$ output = "// This file is automatically generated with Runtime/GfxDevice/opengl/GenerateGLExtensionDef.pl.\n// It is generated from GLExtensionDefs.txt\n";
+
+LINE:
+while (<FH>)
+{
+ my $line = $_;
+ chomp ($line);
+
+ if ($line =~ /^\s*\/\/\s*(.*)/)
+ {
+ $output = $output . "$line\n";
+ }
+ elsif ($line =~ /^\s*\#\s*(.*)/)
+ {
+ $output = $output . "$line\n";
+ }
+ elsif ($line =~ /^\s* s*(.*)/)
+ {
+ $output = $output . "$line\n";
+ }
+ elsif ($line =~ /^\s*$/)
+ {
+ $output = $output . "$line\n";
+ }
+ else
+ {
+ my$ name = $line;
+ my$ pfn = uc ($line);
+ if ($line =~ /^(.+)->(.+)/)
+ {
+ $name = $1;
+ $pfn = uc ($2);
+ }
+ my $upperLine = uc ($line);
+ $output = $output . "DEF (PFN" . $pfn . "PROC, $name);\n";
+ $output = $output . "#define $name UNITYGL_$name\n";
+ }
+}
+
+open OUT, "> GLExtensionDefs.h";
+print OUT $output; \ No newline at end of file
diff --git a/Runtime/GfxDevice/opengl/GfxDeviceGL.cpp b/Runtime/GfxDevice/opengl/GfxDeviceGL.cpp
new file mode 100644
index 0000000..dfc2926
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/GfxDeviceGL.cpp
@@ -0,0 +1,3079 @@
+#include "UnityPrefix.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Shaders/MaterialProperties.h"
+#include "UnityGL.h"
+#include "GLContext.h"
+#include "TexturesGL.h"
+#include "TimerQueryGL.h"
+#include "ARBVBO.h"
+#include "NullVBO.h"
+#include "CombinerGL.h"
+#include "External/shaderlab/Library/program.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "ChannelsGL.h"
+#include "Runtime/Threads/AtomicOps.h"
+#include "Runtime/Allocator/LinearAllocator.h"
+#include "GpuProgramsGL.h"
+#include "ArbGpuProgamGL.h"
+#include "TextureIdMapGL.h"
+#include "Runtime/Graphics/GraphicsHelper.h"
+#include "Runtime/GfxDevice/GpuProgramParamsApply.h"
+#if UNITY_EDITOR && UNITY_WIN
+#include "GLWindow.h"
+#endif
+#include "Runtime/Misc/Plugins.h"
+
+
+void InvalidateChannelStateGL(); // ChannelsGL.cpp
+void InvalidateFPParamCacheGL(); // GpuProgramsGL.cpp
+bool IsActiveRenderTargetWithColorGL(); // RenderTextureGL.cpp
+
+namespace ShaderLab {
+ TexEnv* GetTexEnvForBinding( const TextureBinding& binding, const PropertySheet* props ); // pass.cpp
+}
+
+static const unsigned int kBlendModeGL[] = {
+ GL_ZERO, GL_ONE, GL_DST_COLOR, GL_SRC_COLOR, GL_ONE_MINUS_DST_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR,
+ GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA_SATURATE, GL_ONE_MINUS_SRC_ALPHA,
+};
+
+static const unsigned int kBlendFuncGL[] = {
+ GL_FUNC_ADD, GL_FUNC_SUBTRACT,
+ GL_FUNC_REVERSE_SUBTRACT, GL_MIN, GL_MAX,
+};
+
+static const unsigned int kCmpFuncGL[] = {
+ GL_NEVER, GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL, GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS
+};
+
+static const unsigned int kStencilOpGL[] = {
+ GL_KEEP, GL_ZERO, GL_REPLACE, GL_INCR,
+ GL_DECR, GL_INVERT, GL_INCR_WRAP, GL_DECR_WRAP
+};
+
+static const GLenum kColorMatModeGL[kColorMatTypeCount] = { 0, GL_EMISSION, GL_AMBIENT_AND_DIFFUSE };
+
+static const unsigned long kTexDimTableGL[kTexDimCount] = {0, GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, 0};
+
+static GLenum kWrapModeGL[kTexWrapCount] = { GL_REPEAT, GL_CLAMP_TO_EDGE };
+
+static const GLint kMinFilterGL[kTexFilterCount] = { GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR };
+
+static const unsigned int kFogModeGL[kFogModeCount] = { GL_LINEAR, GL_LINEAR, GL_EXP, GL_EXP2 };
+
+GLenum kTopologyGL[kPrimitiveTypeCount] = { GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_QUADS, GL_LINES, GL_LINE_STRIP, GL_POINTS };
+
+
+GfxThreadableDevice* CreateGLGfxDevice();
+const unsigned long* GetGLTextureDimensionTable();
+unsigned int GetGLShaderImplTarget( ShaderImplType implType );
+void InvalidateActiveShaderStateGL( ShaderType type );
+
+#if SUPPORT_REPRODUCE_LOG_GFX_TRACE
+#include "Runtime/Misc/ReproductionLog.h"
+#define GFX_LOG(pattern) LogToScreenshotLog(__FUNCTION__+(" "+Format pattern))
+#else
+#define GFX_LOG(pattern)
+#endif
+
+
+static inline void ActivateTextureUnitGL (int unit)
+{
+ // When using the fixed-function texture, the correct assert is:
+ //Assert(unit >= 0 && unit < gGraphicsCaps.maxTexUnits);
+
+ Assert(unit >= 0 && unit < gGraphicsCaps.maxTexImageUnits);
+
+ GFX_LOG(("%d", unit));
+ if (gGraphicsCaps.maxTexImageUnits > 1)
+ OGL_CALL(glActiveTextureARB(GL_TEXTURE0_ARB + unit));
+}
+
+
+// --------------------------------------------------------------------------
+
+struct DeviceBlendStateGL : public DeviceBlendState
+{
+ UInt32 alphaFunc;
+};
+
+
+struct DeviceDepthStateGL : public DeviceDepthState
+{
+ UInt32 depthFunc;
+};
+
+struct DeviceStencilStateGL : public DeviceStencilState
+{
+ GLenum stencilFuncFront;
+ GLenum stencilFailOpFront;
+ GLenum depthFailOpFront;
+ GLenum depthPassOpFront;
+ GLenum stencilFuncBack;
+ GLenum stencilFailOpBack;
+ GLenum depthFailOpBack;
+ GLenum depthPassOpBack;
+};
+
+
+
+typedef std::map< GfxBlendState, DeviceBlendStateGL, memcmp_less<GfxBlendState> > CachedBlendStates;
+typedef std::map< GfxDepthState, DeviceDepthStateGL, memcmp_less<GfxDepthState> > CachedDepthStates;
+typedef std::map< GfxStencilState, DeviceStencilStateGL, memcmp_less<GfxStencilState> > CachedStencilStates;
+typedef std::map< GfxRasterState, DeviceRasterState, memcmp_less<GfxRasterState> > CachedRasterStates;
+
+
+// --------------------------------------------------------------------------
+
+
+struct TextureUnitStateGL
+{
+ GLuint texID;
+ TextureDimension texDim;
+ unsigned int combColor, combAlpha;
+ Vector4f color;
+ TexGenMode texGen;
+ float bias;
+
+ void Invalidate();
+ void Verify( int unit );
+};
+
+void TextureUnitStateGL::Invalidate()
+{
+ texID = -1;
+ texDim = kTexDimUnknown;
+ combColor = combAlpha = 0xFFFFFFFF;
+ color.Set( -1, -1, -1, -1 );
+ texGen = kTexGenUnknown;
+ bias = 1.0e6f;
+}
+
+// --------------------------------------------------------------------------
+
+
+struct VertexLightStateGL
+{
+ int enabled; // 0/1 or -1 if unknown
+ float attenQuad;
+ float spotAngle;
+
+ void Invalidate();
+ void Verify( int unit );
+};
+
+void VertexLightStateGL::Invalidate()
+{
+ enabled = -1;
+ attenQuad = -1.0f;
+ spotAngle = -1000.0f; // -1.0f is used to flag non-spot lights, so it would be a valid value
+}
+
+// --------------------------------------------------------------------------
+
+
+struct DeviceStateGL
+{
+ GLuint m_SharedFBO;
+ GLuint m_HelperFBO;
+ int m_TextureIDGenerator;
+
+ int depthFunc;
+ int depthWrite; // 0/1 or -1
+
+ int blending;
+ int srcBlend, destBlend, srcBlendAlpha, destBlendAlpha; // GL modes
+ int blendOp, blendOpAlpha; // GL modes
+ CompareFunction alphaFunc;
+ float alphaValue;
+ int alphaToMask;
+
+ CullMode culling;
+ bool appBackfaceMode, userBackfaceMode;
+ bool wireframe;
+ NormalizationMode normalization;
+ int scissor;
+
+
+ int lighting;
+ Vector4f matDiffuse, matAmbient, matSpecular, matEmissive;
+ Vector4f ambient;
+ float matShininess;
+ ColorMaterialMode colorMaterial;
+
+ float offsetFactor, offsetUnits;
+
+ GpuProgram* activeGpuProgram[kShaderTypeCount];
+ const GpuProgramParameters* activeGpuProgramParams[kShaderTypeCount];
+ ShaderImplType shaderEnabledImpl[kShaderTypeCount];
+ int shaderEnabledID[kShaderTypeCount];
+
+ int separateSpecular;
+
+ int colorWriteMask; // ColorWriteMask combinations
+
+ TextureUnitStateGL textures[kMaxSupportedTextureUnits];
+ VertexLightStateGL vertexLights[kMaxSupportedVertexLights];
+
+ DynamicVBO* m_DynamicVBO;
+
+ int viewport[4];
+ int scissorRect[4];
+
+ GfxPrimitiveType m_ImmediateMode;
+ dynamic_array<UInt8> m_ImmediateVertices;
+
+ #if GFX_USES_VIEWPORT_OFFSET
+ float viewportOffsetX, viewportOffsetY;
+ #endif
+
+ CachedBlendStates m_CachedBlendStates;
+ CachedDepthStates m_CachedDepthStates;
+ CachedStencilStates m_CachedStencilStates;
+ CachedRasterStates m_CachedRasterStates;
+
+ DeviceBlendStateGL* m_CurrBlendState;
+ DeviceDepthStateGL* m_CurrDepthState;
+ const DeviceStencilStateGL* m_CurrStencilState;
+ int m_StencilRef;
+ DeviceRasterState* m_CurrRasterState;
+ Matrix4x4f m_WorldMatrix;
+
+ void Invalidate(BuiltinShaderParamValues& builtins);
+ void Verify();
+};
+
+static void ApplyBackfaceMode( const DeviceStateGL& state );
+
+
+void DeviceStateGL::Invalidate (BuiltinShaderParamValues& builtins)
+{
+ int i;
+
+ depthFunc = kFuncUnknown;
+ depthWrite = -1;
+
+ blending = -1; // unknown
+ srcBlend = destBlend = srcBlendAlpha = destBlendAlpha = -1; // won't match any GL mode
+ blendOp = blendOpAlpha = -1; // won't match any GL mode
+ alphaFunc = kFuncUnknown;
+ alphaValue = -1.0f;
+ alphaToMask = -1; // unknown
+
+ culling = kCullUnknown;
+ normalization = kNormalizationUnknown;
+ scissor = -1;
+
+ lighting = -1;
+ matDiffuse.Set( -1, -1, -1, -1 );
+ matAmbient.Set( -1, -1, -1, -1 );
+ matSpecular.Set( -1, -1, -1, -1 );
+ matEmissive.Set( -1, -1, -1, -1 );
+ ambient.Set( -1, -1, -1, -1 );
+ matShininess = -1.0f;
+ colorMaterial = kColorMatUnknown;
+
+ offsetFactor = offsetUnits = -1000.0f;
+ for( i = 0; i < kShaderTypeCount; ++i )
+ {
+ activeGpuProgram[i] = NULL;
+ activeGpuProgramParams[i] = NULL;
+ shaderEnabledImpl[i] = kShaderImplUndefined;
+ shaderEnabledID[i] = -1;
+ }
+
+ separateSpecular = -1;
+
+ colorWriteMask = -1; // TBD ?
+
+ m_StencilRef = -1;
+
+ for( i = 0; i < kMaxSupportedTextureUnits; ++i )
+ textures[i].Invalidate();
+ for( i = 0; i < kMaxSupportedVertexLights; ++i )
+ vertexLights[i].Invalidate();
+ InvalidateChannelStateGL();
+ InvalidateFPParamCacheGL();
+
+ // make sure we aren't using any programs
+ if( gGraphicsCaps.gl.hasGLSL )
+ glUseProgramObjectARB( 0 );
+ glDisable( GL_VERTEX_PROGRAM_ARB );
+ glDisable( GL_FRAGMENT_PROGRAM_ARB );
+
+ // misc. state
+ glHint (GL_FOG_HINT, GL_NICEST);
+ glLightModelf (GL_LIGHT_MODEL_LOCAL_VIEWER, 1.0F);
+
+ // Setup GL_EYE_PLANE for R coordinate to reasonable value. This has to be done initially,
+ // and NOT when some arbitrary matrix is already set up. So it's important
+ // to load identity matrix here and only then setup GL_EYE_PLANE value.
+ glLoadIdentity();
+ m_WorldMatrix.SetIdentity();
+ builtins.GetWritableMatrixParam(kShaderMatView).SetIdentity();
+ builtins.GetWritableMatrixParam(kShaderMatProj).SetIdentity();
+ builtins.GetWritableMatrixParam(kShaderMatViewProj).SetIdentity();
+ const float zplane[4] = {0.0f,0.0f,1.0f,0.0f};
+ for( i = 0; i < gGraphicsCaps.maxTexUnits; ++i )
+ {
+ ActivateTextureUnitGL (i);
+ glTexGenfv (GL_R, GL_EYE_PLANE, zplane);
+ }
+
+ // make sure backface mode is in synch
+ ApplyBackfaceMode( *this );
+
+ // make sure no vertex buffers are bound
+ UnbindVertexBuffersGL();
+
+ m_ImmediateMode = kPrimitiveTriangles;
+ m_ImmediateVertices.clear();
+
+ #if GFX_DEVICE_VERIFY_ENABLE
+ Verify();
+ #endif
+}
+
+#include "GfxDeviceGL.h"
+
+// GLContext.cpp
+#if UNITY_WIN
+bool CreateMasterGraphicsContext();
+#endif
+void CleanupMasterContext();
+
+
+GfxThreadableDevice* CreateGLGfxDevice()
+{
+ #if UNITY_WIN
+ SetMasterContextClassName(L"WindowGLClassName");
+ if( !CreateMasterGraphicsContext() )
+ return NULL;
+ #endif
+ GraphicsContextHandle context = GetMasterGraphicsContext();
+ if( !context.IsValid() )
+ return NULL;
+
+ SetMainGraphicsContext( context );
+ ActivateGraphicsContext (context, true);
+
+ InitGLExtensions();
+ gGraphicsCaps.InitGL();
+ GLAssert();
+
+ return UNITY_NEW_AS_ROOT(GFX_GL_IMPL(), kMemGfxDevice, "GLGfxDevice", "");
+}
+
+
+GFX_GL_IMPL::GFX_GL_IMPL()
+{
+ #if !GFX_DEVICE_VIRTUAL
+ impl = new GfxDeviceImpl();
+ #endif
+ STATE.m_SharedFBO = 0;
+ STATE.m_HelperFBO = 0;
+ STATE.m_TextureIDGenerator = 0;
+ STATE.appBackfaceMode = false;
+ STATE.userBackfaceMode = false;
+ STATE.m_DynamicVBO = NULL;
+ STATE.wireframe = false;
+ #if GFX_USES_VIEWPORT_OFFSET
+ STATE.viewportOffsetX = 0;
+ STATE.viewportOffsetY = 0;
+ #endif
+
+ OnCreate();
+ InvalidateState();
+
+ m_Renderer = kGfxRendererOpenGL;
+ m_UsesOpenGLTextureCoords = true;
+ m_UsesHalfTexelOffset = false;
+ // Should be safe to assume we can get 24 bits for the framebuffer on desktop.
+ m_FramebufferDepthFormat = kDepthFormat24;
+ m_IsThreadable = true;
+
+ STATE.m_CurrBlendState = NULL;
+ STATE.m_CurrDepthState = NULL;
+ STATE.m_CurrStencilState = NULL;
+ STATE.m_CurrRasterState = NULL;
+
+ STATE.m_WorldMatrix.SetIdentity();
+
+ m_MaxBufferedFrames = -1; // no limiting
+
+ STATE.viewport[0] = STATE.viewport[1] = STATE.viewport[2] = STATE.viewport[3] = 0;
+ STATE.scissorRect[0] = STATE.scissorRect[1] = STATE.scissorRect[2] = STATE.scissorRect[3] = 0;
+
+ extern RenderSurfaceBase* CreateBackBufferColorSurfaceGL();
+ SetBackBufferColorSurface(CreateBackBufferColorSurfaceGL());
+
+ extern RenderSurfaceBase* CreateBackBufferDepthSurfaceGL();
+ SetBackBufferDepthSurface(CreateBackBufferDepthSurfaceGL());
+}
+
+GFX_GL_IMPL::~GFX_GL_IMPL()
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS_WORKER
+ PluginsSetGraphicsDevice (NULL, kGfxRendererOpenGL, kGfxDeviceEventShutdown);
+#endif
+ if (STATE.m_SharedFBO)
+ glDeleteFramebuffersEXT (1, &STATE.m_SharedFBO);
+ if (STATE.m_HelperFBO)
+ glDeleteFramebuffersEXT (1, &STATE.m_HelperFBO);
+ delete STATE.m_DynamicVBO;
+
+ #if !GFX_DEVICE_VIRTUAL
+ delete impl;
+ #endif
+
+ #if UNITY_WIN
+ CleanupMasterContext();
+ #endif
+ CleanupGLExtensions();
+}
+
+void GFX_GL_IMPL::OnDeviceCreated (bool callingFromRenderThread)
+{
+ // needs to activate graphics context on both main & render threads
+ ActivateGraphicsContext (GetMainGraphicsContext(), true);
+}
+
+void GFX_GL_IMPL::InvalidateState()
+{
+ m_FogParams.Invalidate();
+ STATE.Invalidate(m_BuiltinParamValues);
+
+ // Disable fog initially. At least on Mac/Intel, initial state seems to be off
+ // for fixed function (correct), but on for fragment/vertex programs (incorrect).
+ glFogf( GL_FOG_DENSITY, 0.0f );
+ glDisable( GL_FOG );
+ m_FogParams.mode = kFogDisabled;
+ m_FogParams.density = 0.0f;
+}
+
+
+const int kFPParamCacheSize = 32; // don't make this larger than 32 (won't fit into s_FPParamCacheValid mask)
+extern UInt32 s_FPParamCacheValid;
+extern Vector4f s_FPParamCache[kFPParamCacheSize];
+
+
+#define SET_LOCAL_MATRIX_PARAM( type, index, ptr ) \
+ OGL_CALL(glProgramLocalParameter4fARB( type, index+0, ptr[0], ptr[4], ptr[8], ptr[12] )); \
+ OGL_CALL(glProgramLocalParameter4fARB( type, index+1, ptr[1], ptr[5], ptr[9], ptr[13] )); \
+ OGL_CALL(glProgramLocalParameter4fARB( type, index+2, ptr[2], ptr[6], ptr[10], ptr[14] )); \
+ OGL_CALL(glProgramLocalParameter4fARB( type, index+3, ptr[3], ptr[7], ptr[11], ptr[15] )); \
+
+
+#define SET_ENV_MATRIX_PARAM( type, index, ptr ) \
+ OGL_CALL(glProgramEnvParameter4fARB( type, index+0, ptr[0], ptr[4], ptr[8], ptr[12] )); \
+ OGL_CALL(glProgramEnvParameter4fARB( type, index+1, ptr[1], ptr[5], ptr[9], ptr[13] )); \
+ OGL_CALL(glProgramEnvParameter4fARB( type, index+2, ptr[2], ptr[6], ptr[10], ptr[14] )); \
+ OGL_CALL(glProgramEnvParameter4fARB( type, index+3, ptr[3], ptr[7], ptr[11], ptr[15] )); \
+
+
+static void GLSetShaderMatrixConstant (ShaderImplType type, int index, int rows, int cols, const float* ptr)
+{
+ if (type == kShaderImplVertex)
+ {
+ SET_LOCAL_MATRIX_PARAM(GL_VERTEX_PROGRAM_ARB, index, ptr);
+ }
+ else if (type == kShaderImplFragment)
+ {
+ if (gGraphicsCaps.gl.cacheFPParamsWithEnvs)
+ {
+ // don't care here about cache
+ UInt32 mask0 = index+0 < kFPParamCacheSize ? 1 << (index+0) : 0;
+ UInt32 mask1 = index+1 < kFPParamCacheSize ? 1 << (index+1) : 0;
+ UInt32 mask2 = index+2 < kFPParamCacheSize ? 1 << (index+2) : 0;
+ UInt32 mask3 = index+3 < kFPParamCacheSize ? 1 << (index+3) : 0;
+
+ s_FPParamCacheValid &= ~mask0;
+ s_FPParamCacheValid &= ~mask1;
+ s_FPParamCacheValid &= ~mask2;
+ s_FPParamCacheValid &= ~mask3;
+
+ SET_ENV_MATRIX_PARAM(GL_FRAGMENT_PROGRAM_ARB, index, ptr);
+ }
+ else
+ {
+ SET_LOCAL_MATRIX_PARAM(GL_FRAGMENT_PROGRAM_ARB, index, ptr);
+ }
+ }
+ else if (type == kShaderImplBoth)
+ {
+ Assert(rows == 4 && cols == 4);
+ OGL_CALL(glUniformMatrix4fvARB(index, 1, false, ptr));
+ }
+ else
+ {
+ AssertString("unknown shader type");
+ }
+}
+
+
+#undef SET_ENV_MATRIX_PARAM
+#undef SET_LOCAL_MATRIX_PARAM
+
+static void GLSetShaderVectorConstant (ShaderImplType shaderType, ShaderParamType type, int index, int dim, const Vector4f& val)
+{
+ if (shaderType == kShaderImplVertex)
+ {
+ OGL_CALL(glProgramLocalParameter4fvARB(GL_VERTEX_PROGRAM_ARB, index, val.GetPtr()));
+ }
+ else if (shaderType == kShaderImplFragment)
+ {
+ if (gGraphicsCaps.gl.cacheFPParamsWithEnvs)
+ {
+ UInt32 mask = 1 << index;
+ if (index >= kFPParamCacheSize)
+ {
+ OGL_CALL(glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index, val.GetPtr()));
+ }
+ else
+ {
+ if( !(s_FPParamCacheValid & mask) || s_FPParamCache[index] != val )
+ {
+ OGL_CALL(glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index, val.GetPtr()));
+ s_FPParamCache[index] = val;
+ }
+ s_FPParamCacheValid |= mask;
+ }
+ }
+ else
+ OGL_CALL(glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index, val.GetPtr()));
+ }
+ else if (shaderType == kShaderImplBoth)
+ {
+ if (type == kShaderParamFloat)
+ {
+ switch (dim) {
+ case 1: OGL_CALL(glUniform1fvARB(index, 1, val.GetPtr())); break;
+ case 2: OGL_CALL(glUniform2fvARB(index, 1, val.GetPtr())); break;
+ case 3: OGL_CALL(glUniform3fvARB(index, 1, val.GetPtr())); break;
+ case 4: OGL_CALL(glUniform4fvARB(index, 1, val.GetPtr())); break;
+ default: AssertString ("unknown uniform dimension"); break;
+ }
+ }
+ else
+ {
+ // In theory Uniform*f can also be used to load bool uniforms, in practice
+ // some drivers don't like that. So load both integers and bools via *i functions.
+ int ival[4] = {val.x, val.y, val.z, val.w};
+ switch (dim) {
+ case 1: OGL_CALL(glUniform1ivARB(index, 1, ival)); break;
+ case 2: OGL_CALL(glUniform2ivARB(index, 1, ival)); break;
+ case 3: OGL_CALL(glUniform3ivARB(index, 1, ival)); break;
+ case 4: OGL_CALL(glUniform4ivARB(index, 1, ival)); break;
+ default: AssertString ("unknown uniform dimension"); break;
+ }
+ }
+ }
+ else
+ {
+ AssertString("unknown shader type");
+ }
+}
+
+
+struct SetValuesFunctorGL
+{
+ SetValuesFunctorGL(GfxDevice& device, const ShaderImplType* shaderEnabledImpl) : m_Device(device), m_ShaderEnabledImpl(shaderEnabledImpl) { }
+ GfxDevice& m_Device;
+ const ShaderImplType* m_ShaderEnabledImpl;
+ void SetVectorVal (ShaderType shaderType, ShaderParamType type, int index, const float* ptr, int cols, const GpuProgramParameters& params, int cbIndex)
+ {
+ GLSetShaderVectorConstant (m_ShaderEnabledImpl[shaderType], type, index, cols, *(const Vector4f*)ptr);
+ }
+ void SetMatrixVal (ShaderType shaderType, int index, const Matrix4x4f* ptr, int rows, const GpuProgramParameters& params, int cbIndex)
+ {
+ GLSetShaderMatrixConstant (m_ShaderEnabledImpl[shaderType], index, rows, 4, (const float*)ptr);
+ }
+ void SetTextureVal (ShaderType shaderType, int index, int samplerIndex, TextureDimension dim, TextureID texID)
+ {
+ UInt32 texIndex = UInt32(index) >> 24;
+ m_Device.SetTexture (shaderType, texIndex, samplerIndex, texID, dim, std::numeric_limits<float>::infinity());
+ }
+};
+
+
+void GFX_GL_IMPL::BeforeDrawCall( bool immediateMode )
+{
+ GFX_LOG(("%d",immediateMode));
+
+ // Special Matrix parameters (defined by Unity as built-ins
+ // but not present in GL default state)
+ const BuiltinShaderParamIndices& paramsVS = *m_BuiltinParamIndices[kShaderVertex];
+ const BuiltinShaderParamIndices& paramsFS = *m_BuiltinParamIndices[kShaderFragment];
+
+ // M matrix
+ if (paramsVS.mat[kShaderInstanceMatM].gpuIndex >= 0)
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = paramsVS.mat[kShaderInstanceMatM];
+ const Matrix4x4f& mat = STATE.m_WorldMatrix;
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLSetShaderMatrixConstant (STATE.shaderEnabledImpl[kShaderVertex], matParam.gpuIndex, matParam.rows, matParam.cols, mat.GetPtr());
+ }
+ if (paramsFS.mat[kShaderInstanceMatM].gpuIndex >= 0)
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = paramsFS.mat[kShaderInstanceMatM];
+ const Matrix4x4f& mat = STATE.m_WorldMatrix;
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLSetShaderMatrixConstant (STATE.shaderEnabledImpl[kShaderFragment], matParam.gpuIndex, matParam.rows, matParam.cols, mat.GetPtr());
+ }
+ // Inverse M matrix
+ if (paramsVS.mat[kShaderInstanceMatInvM].gpuIndex >= 0 || paramsFS.mat[kShaderInstanceMatInvM].gpuIndex >= 0)
+ {
+ Matrix4x4f mat = STATE.m_WorldMatrix;
+ if (STATE.normalization == kNormalizationScale)
+ {
+ // Kill scale in the world matrix before inverse
+ float invScale = m_BuiltinParamValues.GetInstanceVectorParam(kShaderInstanceVecScale).w;
+ mat.Get (0, 0) *= invScale;
+ mat.Get (1, 0) *= invScale;
+ mat.Get (2, 0) *= invScale;
+ mat.Get (0, 1) *= invScale;
+ mat.Get (1, 1) *= invScale;
+ mat.Get (2, 1) *= invScale;
+ mat.Get (0, 2) *= invScale;
+ mat.Get (1, 2) *= invScale;
+ mat.Get (2, 2) *= invScale;
+ }
+ Matrix4x4f inverseMat;
+ Matrix4x4f::Invert_General3D (mat, inverseMat);
+ if (paramsVS.mat[kShaderInstanceMatInvM].gpuIndex >= 0)
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = paramsVS.mat[kShaderInstanceMatInvM];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLSetShaderMatrixConstant (STATE.shaderEnabledImpl[kShaderVertex], matParam.gpuIndex, matParam.rows, matParam.cols, inverseMat.GetPtr());
+ }
+ if (paramsFS.mat[kShaderInstanceMatInvM].gpuIndex >= 0)
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = paramsFS.mat[kShaderInstanceMatInvM];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLSetShaderMatrixConstant (STATE.shaderEnabledImpl[kShaderFragment], matParam.gpuIndex, matParam.rows, matParam.cols, inverseMat.GetPtr());
+ }
+ }
+
+ // Set instance vector parameters
+ for (int i = 0; i < kShaderInstanceVecCount; ++i)
+ {
+ const BuiltinShaderParamIndices::VectorParamData& parVS = paramsVS.vec[i];
+ if (parVS.gpuIndex >= 0)
+ GLSetShaderVectorConstant (STATE.shaderEnabledImpl[kShaderVertex], kShaderParamFloat, parVS.gpuIndex, parVS.dim, m_BuiltinParamValues.GetInstanceVectorParam((ShaderBuiltinInstanceVectorParam)i));
+ const BuiltinShaderParamIndices::VectorParamData& parFS = paramsFS.vec[i];
+ if (parFS.gpuIndex >= 0)
+ GLSetShaderVectorConstant (STATE.shaderEnabledImpl[kShaderFragment], kShaderParamFloat, parFS.gpuIndex, parFS.dim, m_BuiltinParamValues.GetInstanceVectorParam((ShaderBuiltinInstanceVectorParam)i));
+ }
+
+ SetValuesFunctorGL setValuesFunc(*this, STATE.shaderEnabledImpl);
+ ApplyMaterialPropertyBlockValues(m_MaterialProperties, STATE.activeGpuProgram, STATE.activeGpuProgramParams, setValuesFunc);
+}
+
+
+DeviceBlendState* GFX_GL_IMPL::CreateBlendState(const GfxBlendState& state)
+{
+
+ std::pair<CachedBlendStates::iterator, bool> result = STATE.m_CachedBlendStates.insert(std::make_pair(state, DeviceBlendStateGL()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceBlendStateGL& glstate = result.first->second;
+ memcpy(&glstate.sourceState, &state, sizeof(GfxBlendState));
+ DebugAssertIf(kFuncUnknown==state.alphaTest);
+ glstate.alphaFunc = kCmpFuncGL[state.alphaTest];
+
+ return &result.first->second;
+}
+
+
+DeviceDepthState* GFX_GL_IMPL::CreateDepthState(const GfxDepthState& state)
+{
+ std::pair<CachedDepthStates::iterator, bool> result = STATE.m_CachedDepthStates.insert(std::make_pair(state, DeviceDepthStateGL()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceDepthStateGL& glstate = result.first->second;
+ memcpy(&glstate.sourceState, &state, sizeof(GfxDepthState));
+ glstate.depthFunc = kCmpFuncGL[state.depthFunc];
+ return &result.first->second;
+}
+
+DeviceStencilState* GFX_GL_IMPL::CreateStencilState(const GfxStencilState& state)
+{
+ std::pair<CachedStencilStates::iterator, bool> result = STATE.m_CachedStencilStates.insert(std::make_pair(state, DeviceStencilStateGL()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceStencilStateGL& st = result.first->second;
+ memcpy (&st.sourceState, &state, sizeof(state));
+ st.stencilFuncFront = kCmpFuncGL[state.stencilFuncFront];
+ st.stencilFailOpFront = kStencilOpGL[state.stencilFailOpFront];
+ st.depthFailOpFront = kStencilOpGL[state.stencilZFailOpFront];
+ st.depthPassOpFront = kStencilOpGL[state.stencilPassOpFront];
+ st.stencilFuncBack = kCmpFuncGL[state.stencilFuncBack];
+ st.stencilFailOpBack = kStencilOpGL[state.stencilFailOpBack];
+ st.depthFailOpBack = kStencilOpGL[state.stencilZFailOpBack];
+ st.depthPassOpBack = kStencilOpGL[state.stencilPassOpBack];
+ return &result.first->second;
+}
+
+
+DeviceRasterState* GFX_GL_IMPL::CreateRasterState(const GfxRasterState& state)
+{
+ std::pair<CachedRasterStates::iterator, bool> result = STATE.m_CachedRasterStates.insert(std::make_pair(state, DeviceRasterState()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceRasterState& glstate = result.first->second;
+ memcpy(&glstate.sourceState, &state, sizeof(DeviceRasterState));
+
+ return &result.first->second;
+}
+
+void GFX_GL_IMPL::SetBlendState(const DeviceBlendState* state, float alphaRef)
+{
+ DeviceBlendStateGL* devstate = (DeviceBlendStateGL*)state;
+
+ if (STATE.m_CurrBlendState == devstate && alphaRef == STATE.alphaValue)
+ return;
+
+ STATE.m_CurrBlendState = devstate;
+ if (!STATE.m_CurrBlendState)
+ return;
+
+ int mask = devstate->sourceState.renderTargetWriteMask;
+ if (!IsActiveRenderTargetWithColorGL())
+ mask = 0;
+ if( mask != STATE.colorWriteMask ) {
+ OGL_CALL(glColorMask( (mask & kColorWriteR) != 0, (mask & kColorWriteG) != 0, (mask & kColorWriteB) != 0, (mask & kColorWriteA) != 0 ));
+ STATE.colorWriteMask = mask;
+ }
+
+ const GfxBlendState& desc = state->sourceState;
+ const CompareFunction mode = state->sourceState.alphaTest;
+ const GLenum glsrc = kBlendModeGL[desc.srcBlend];
+ const GLenum gldst = kBlendModeGL[desc.dstBlend];
+ const GLenum glsrca = kBlendModeGL[desc.srcBlendAlpha];
+ const GLenum gldsta = kBlendModeGL[desc.dstBlendAlpha];
+ const GLenum glfunc = kBlendFuncGL[desc.blendOp];
+ const GLenum glfunca = kBlendFuncGL[desc.blendOpAlpha];
+ const bool blendDisabled = (glsrc == GL_ONE && gldst == GL_ZERO && glsrca == GL_ONE && gldsta == GL_ZERO);
+
+ GFX_LOG(("%d, %d, %d, %f", desc.srcBlend, desc.dstBlend, mode, alphaRef));
+
+ // alpha blending states
+
+ if( blendDisabled )
+ {
+ if( STATE.blending != 0 )
+ {
+ OGL_CALL(glDisable (GL_BLEND));
+ STATE.blending = 0;
+ }
+ }
+ else
+ {
+ if( glsrc != STATE.srcBlend || gldst != STATE.destBlend || glsrca != STATE.srcBlendAlpha || gldsta != STATE.destBlendAlpha )
+ {
+ if (gGraphicsCaps.hasSeparateAlphaBlend)
+ OGL_CALL(glBlendFuncSeparateEXT (glsrc, gldst, glsrca, gldsta));
+ else
+ OGL_CALL(glBlendFunc( glsrc, gldst ));
+ STATE.srcBlend = glsrc;
+ STATE.destBlend = gldst;
+ STATE.srcBlendAlpha = glsrca;
+ STATE.destBlendAlpha = gldsta;
+ }
+ if (glfunc != STATE.blendOp || glfunca != STATE.blendOpAlpha)
+ {
+ bool supports = true;
+ if( (glfunc == GL_FUNC_SUBTRACT || glfunc == GL_FUNC_REVERSE_SUBTRACT) && !gGraphicsCaps.hasBlendSub )
+ supports = false;
+ if( (glfunca == GL_FUNC_SUBTRACT || glfunca == GL_FUNC_REVERSE_SUBTRACT) && !gGraphicsCaps.hasBlendSub )
+ supports = false;
+ if( (glfunc == GL_MIN || glfunc == GL_MAX) && !gGraphicsCaps.hasBlendMinMax )
+ supports = false;
+ if( (glfunca == GL_MIN || glfunca == GL_MAX) && !gGraphicsCaps.hasBlendMinMax )
+ supports = false;
+
+ if(supports)
+ {
+ if (gGraphicsCaps.hasSeparateAlphaBlend)
+ OGL_CALL(glBlendEquationSeparateEXT (glfunc, glfunca));
+ else
+ OGL_CALL(glBlendEquation (glfunc));
+ STATE.blendOp = glfunc;
+ STATE.blendOpAlpha = glfunca;
+ }
+ }
+ if( STATE.blending != 1 )
+ {
+ OGL_CALL(glEnable( GL_BLEND ));
+ STATE.blending = 1;
+ }
+ }
+
+ // alpha testing states
+#if UNITY_EDITOR // gles2.0 doesn't have FF alpha testing(only discard/clip on shader side), so disable on editor while emulating
+ bool skipAlphaTestFF = (gGraphicsCaps.IsEmulatingGLES20() && IsShaderActive(kShaderFragment));
+ // possible that vertex shader will be used with FF "frag shader" (like Transparent/vertexlit.shader),
+ // which will change alphatesting. So later on when real frag shaders come, we need to force disable alpha
+ // testing or enjoy nasty artefacts (like active alpha testing messing up the whole scene).
+ if ( skipAlphaTestFF && STATE.alphaFunc!=kFuncDisabled )
+ {
+ OGL_CALL(glDisable (GL_ALPHA_TEST));
+ STATE.alphaFunc = kFuncDisabled;
+ }
+
+ if ( !skipAlphaTestFF )
+ {
+#endif
+ if( mode != STATE.alphaFunc || alphaRef != STATE.alphaValue )
+ {
+ if( mode != kFuncDisabled )
+ {
+ OGL_CALL(glAlphaFunc( kCmpFuncGL[mode], alphaRef ));
+ OGL_CALL(glEnable( GL_ALPHA_TEST ));
+ }
+ else
+ {
+ OGL_CALL(glDisable (GL_ALPHA_TEST));
+ }
+
+ STATE.alphaFunc = mode;
+ STATE.alphaValue = alphaRef;
+ }
+#if UNITY_EDITOR
+ }
+#endif
+
+#if 0
+ int atomask = alphaToMask ? 1 : 0;
+ if( atomask != STATE.alphaToMask && gGraphicsCaps.hasMultiSample )
+ {
+ if( atomask )
+ OGL_CALL(glEnable( GL_SAMPLE_ALPHA_TO_COVERAGE ));
+ else
+ OGL_CALL(glDisable( GL_SAMPLE_ALPHA_TO_COVERAGE ));
+ STATE.alphaToMask = atomask;
+ }
+#endif
+}
+
+
+void GFX_GL_IMPL::SetRasterState(const DeviceRasterState* state)
+{
+ DeviceRasterState* devstate = (DeviceRasterState*)state;
+ if(!devstate)
+ {
+ STATE.m_CurrRasterState = NULL;
+ return;
+ }
+
+ STATE.m_CurrRasterState = devstate;
+
+ CullMode cull = devstate->sourceState.cullMode;
+ if( cull != STATE.culling )
+ {
+ switch (cull) {
+ case kCullOff:
+ OGL_CALL(glDisable (GL_CULL_FACE));
+ break;
+ case kCullFront:
+ OGL_CALL(glCullFace (GL_FRONT));
+ OGL_CALL(glEnable (GL_CULL_FACE));
+ break;
+ case kCullBack:
+ OGL_CALL(glCullFace (GL_BACK));
+ OGL_CALL(glEnable (GL_CULL_FACE));
+ break;
+ }
+ STATE.culling = cull;
+ }
+
+ float zFactor = devstate->sourceState.slopeScaledDepthBias;
+ float zUnits = devstate->sourceState.depthBias;
+ if( zFactor != STATE.offsetFactor || zUnits != STATE.offsetUnits )
+ {
+ OGL_CALL(glPolygonOffset( zFactor, zUnits ));
+ if( zFactor || zUnits )
+ OGL_CALL(glEnable (GL_POLYGON_OFFSET_FILL));
+ else
+ OGL_CALL(glDisable (GL_POLYGON_OFFSET_FILL));
+
+ STATE.offsetFactor = zFactor;
+ STATE.offsetUnits = zUnits;
+ }
+}
+
+
+void GFX_GL_IMPL::SetDepthState(const DeviceDepthState* state)
+{
+ DeviceDepthStateGL* devstate = (DeviceDepthStateGL*)state;
+ if (STATE.m_CurrDepthState == devstate)
+ return;
+
+ STATE.m_CurrDepthState = devstate;
+
+ if (!STATE.m_CurrDepthState)
+ return;
+
+ const CompareFunction testFunc = devstate->sourceState.depthFunc;
+ if( testFunc != STATE.depthFunc )
+ {
+ if( testFunc != kFuncDisabled ) {
+ OGL_CALL(glDepthFunc (kCmpFuncGL[testFunc]));
+ OGL_CALL(glEnable (GL_DEPTH_TEST));
+ } else {
+ OGL_CALL(glDisable (GL_DEPTH_TEST));
+ }
+
+ STATE.depthFunc = testFunc;
+ }
+
+ const int writeMode = devstate->sourceState.depthWrite ? GL_TRUE : GL_FALSE;
+ if( writeMode != STATE.depthWrite )
+ {
+ OGL_CALL(glDepthMask (writeMode));
+ STATE.depthWrite = writeMode;
+ }
+}
+
+void GFX_GL_IMPL::SetStencilState (const DeviceStencilState* state, int stencilRef)
+{
+ if (STATE.m_CurrStencilState == state && STATE.m_StencilRef == stencilRef)
+ return;
+ const DeviceStencilStateGL* st = static_cast<const DeviceStencilStateGL*>(state);
+ STATE.m_CurrStencilState = st;
+ if (!st)
+ return;
+
+ if (st->sourceState.stencilEnable)
+ glEnable (GL_STENCIL_TEST);
+ else
+ glDisable (GL_STENCIL_TEST);
+ if (gGraphicsCaps.hasTwoSidedStencil)
+ {
+ glStencilFuncSeparate (GL_FRONT, st->stencilFuncFront, stencilRef, st->sourceState.readMask);
+ glStencilOpSeparate (GL_FRONT, st->stencilFailOpFront, st->depthFailOpFront, st->depthPassOpFront);
+ glStencilFuncSeparate (GL_BACK, st->stencilFuncBack, stencilRef, st->sourceState.readMask);
+ glStencilOpSeparate (GL_BACK, st->stencilFailOpBack, st->depthFailOpBack, st->depthPassOpBack);
+ }
+ else
+ {
+ glStencilFunc (st->stencilFuncFront, stencilRef, st->sourceState.readMask);
+ glStencilOp (st->stencilFailOpFront, st->depthFailOpFront, st->depthPassOpFront);
+ }
+ glStencilMask (st->sourceState.writeMask);
+
+ STATE.m_StencilRef = stencilRef;
+}
+
+
+void GFX_GL_IMPL::SetSRGBWrite (bool enable)
+{
+ if (!gGraphicsCaps.hasSRGBReadWrite)
+ return;
+
+ if (enable)
+ OGL_CALL(glEnable(GL_FRAMEBUFFER_SRGB_EXT));
+ else
+ OGL_CALL(glDisable(GL_FRAMEBUFFER_SRGB_EXT));
+}
+
+bool GFX_GL_IMPL::GetSRGBWrite ()
+{
+ return gGraphicsCaps.hasSRGBReadWrite ?
+ glIsEnabled(GL_FRAMEBUFFER_SRGB_EXT):
+ false;
+}
+
+void GFX_GL_IMPL::Clear (UInt32 clearFlags, const float color[4], float depth, int stencil)
+{
+ GFX_LOG(("%d, {%f, %f, %f, %f}, %f, %d", clearFlags, color[0], color[1], color[2], color[3], depth, stencil));
+
+ if (!IsActiveRenderTargetWithColorGL())
+ clearFlags &= ~kGfxClearColor;
+
+ // In OpenGL, clears are affected by color write mask and depth writing parameters.
+ // So make sure to set them!
+ GLbitfield flags = 0;
+ if (clearFlags & kGfxClearColor)
+ {
+ if (STATE.colorWriteMask != 15)
+ {
+ OGL_CALL(glColorMask( true, true, true, true ));
+ STATE.colorWriteMask = 15;
+ STATE.m_CurrBlendState = NULL;
+ }
+ flags |= GL_COLOR_BUFFER_BIT;
+ glClearColor (color[0], color[1], color[2], color[3]);
+ }
+ if (clearFlags & kGfxClearDepth)
+ {
+ OGL_CALL(glDepthMask (GL_TRUE));
+ STATE.depthWrite = GL_TRUE;
+ STATE.m_CurrDepthState = NULL;
+ flags |= GL_DEPTH_BUFFER_BIT;
+ glClearDepth (depth);
+ }
+ if (clearFlags & kGfxClearStencil)
+ {
+ //@TODO: need to set stencil writes on?
+ flags |= GL_STENCIL_BUFFER_BIT;
+ glClearStencil (stencil);
+ }
+ glClear (flags);
+}
+
+
+static void ApplyBackfaceMode( const DeviceStateGL& state )
+{
+ const bool bFlip = (state.appBackfaceMode != state.userBackfaceMode);
+
+ if( bFlip )
+ OGL_CALL(glFrontFace( GL_CCW ));
+ else
+ OGL_CALL(glFrontFace( GL_CW ));
+
+ if(state.culling == kCullUnknown || state.culling == kCullOff)
+ return;
+
+ DeviceRasterState* devstate = state.m_CurrRasterState;
+ if(!devstate)
+ return;
+
+ GfxDevice &device = GetRealGfxDevice();
+ device.SetRasterState(0);
+ device.SetRasterState(devstate);
+}
+
+
+void GFX_GL_IMPL::SetUserBackfaceMode( bool enable )
+{
+ GFX_LOG(("%d", enable));
+ if( STATE.userBackfaceMode == enable )
+ return;
+ STATE.userBackfaceMode = enable;
+ ApplyBackfaceMode( STATE );
+}
+
+
+void GFX_GL_IMPL::SetWireframe( bool wire )
+{
+ if( wire )
+ {
+ OGL_CALL(glEnable (GL_POLYGON_OFFSET_LINE));
+ OGL_CALL(glPolygonMode (GL_FRONT_AND_BACK, GL_LINE));
+ STATE.wireframe = true;
+ }
+ else
+ {
+ OGL_CALL(glDisable (GL_POLYGON_OFFSET_LINE));
+ OGL_CALL(glPolygonMode (GL_FRONT_AND_BACK, GL_FILL));
+ STATE.wireframe = false;
+ }
+}
+
+bool GFX_GL_IMPL::GetWireframe() const
+{
+ return STATE.wireframe;
+}
+
+
+
+void GFX_GL_IMPL::SetInvertProjectionMatrix (bool enable)
+{
+ Assert (!enable); // projection should never be flipped upside down on OpenGL
+}
+
+bool GFX_GL_IMPL::GetInvertProjectionMatrix() const
+{
+ return false;
+}
+
+#if GFX_USES_VIEWPORT_OFFSET
+void GFX_GL_IMPL::SetViewportOffset( float x, float y )
+{
+ GFX_LOG(("%f, %f", x, y));
+ STATE.viewportOffsetX = x;
+ STATE.viewportOffsetY = y;
+}
+
+void GFX_GL_IMPL::GetViewportOffset( float &x, float &y ) const
+{
+ x = STATE.viewportOffsetX;
+ y = STATE.viewportOffsetY;
+}
+#endif
+
+void GFX_GL_IMPL::SetProjectionMatrix (const Matrix4x4f& matrix)
+{
+ GFX_LOG(("{%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f}",
+ matrix[0], matrix[1], matrix[2], matrix[3],
+ matrix[4], matrix[5], matrix[6], matrix[7],
+ matrix[8], matrix[9], matrix[10], matrix[11],
+ matrix[12], matrix[13], matrix[14], matrix[15]));
+
+ Matrix4x4f& projMat = m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatProj);
+ CopyMatrix (matrix.GetPtr(), projMat.GetPtr());
+ OGL_CALL(glMatrixMode( GL_PROJECTION ));
+ OGL_CALL(glLoadMatrixf (matrix.GetPtr()));
+ OGL_CALL(glMatrixMode( GL_MODELVIEW ));
+}
+
+void GFX_GL_IMPL::SetWorldMatrix( const float matrix[16] )
+{
+ GFX_LOG(("{%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f}",
+ matrix[0], matrix[1], matrix[2], matrix[3],
+ matrix[4], matrix[5], matrix[6], matrix[7],
+ matrix[8], matrix[9], matrix[10], matrix[11],
+ matrix[12], matrix[13], matrix[14], matrix[15]));
+
+ CopyMatrix(matrix, STATE.m_WorldMatrix.GetPtr());
+
+ OGL_CALL(glLoadMatrixf(m_BuiltinParamValues.GetMatrixParam(kShaderMatView).GetPtr()));
+ OGL_CALL(glMultMatrixf(matrix));
+}
+
+void GFX_GL_IMPL::SetViewMatrix( const float matrix[16] )
+{
+ glMatrixMode(GL_MODELVIEW);
+ glLoadMatrixf(matrix);
+
+ const Matrix4x4f& projMat = m_BuiltinParamValues.GetMatrixParam(kShaderMatProj);
+ Matrix4x4f& viewMat = m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatView);
+ Matrix4x4f& viewProjMat = m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatViewProj);
+ CopyMatrix (matrix, viewMat.GetPtr());
+ MultiplyMatrices4x4 (&projMat, &viewMat, &viewProjMat);
+ STATE.m_WorldMatrix.SetIdentity();
+}
+
+
+void GFX_GL_IMPL::GetMatrix( float outMatrix[16] ) const
+{
+ OGL_CALL(glGetFloatv( GL_MODELVIEW_MATRIX, outMatrix ));
+}
+
+
+const float* GFX_GL_IMPL::GetWorldMatrix() const
+{
+ return STATE.m_WorldMatrix.GetPtr();
+}
+
+const float* GFX_GL_IMPL::GetViewMatrix() const
+{
+ return m_BuiltinParamValues.GetMatrixParam(kShaderMatView).GetPtr();
+}
+
+const float* GFX_GL_IMPL::GetProjectionMatrix() const
+{
+ return m_BuiltinParamValues.GetMatrixParam(kShaderMatProj).GetPtr();
+}
+
+const float* GFX_GL_IMPL::GetDeviceProjectionMatrix() const
+{
+ return GetProjectionMatrix();
+}
+
+
+
+void GFX_GL_IMPL::SetNormalizationBackface( NormalizationMode mode, bool backface )
+{
+ GFX_LOG(("%d, %d", mode, backface));
+
+ if( mode != STATE.normalization )
+ {
+ if( mode == kNormalizationDisabled )
+ {
+ OGL_CALL(glDisable (GL_NORMALIZE));
+ OGL_CALL(glDisable (GL_RESCALE_NORMAL));
+ }
+ else if( mode == kNormalizationScale )
+ {
+ OGL_CALL(glDisable (GL_NORMALIZE));
+ OGL_CALL(glEnable (GL_RESCALE_NORMAL));
+ }
+ else
+ {
+ OGL_CALL(glEnable (GL_NORMALIZE));
+ OGL_CALL(glDisable (GL_RESCALE_NORMAL));
+ }
+
+ STATE.normalization = mode;
+ }
+ if( STATE.appBackfaceMode != backface )
+ {
+ STATE.appBackfaceMode = backface;
+ ApplyBackfaceMode( STATE );
+ }
+}
+
+void GFX_GL_IMPL::SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial )
+{
+ GFX_LOG(("%d, %d, %d", on, separateSpecular, colorMaterial));
+
+ int lighting = on ? 1 : 0;
+ if( lighting != STATE.lighting )
+ {
+ if( lighting )
+ OGL_CALL(glEnable( GL_LIGHTING ));
+ else
+ OGL_CALL(glDisable( GL_LIGHTING ));
+ STATE.lighting = lighting;
+ }
+
+ int sepSpec = separateSpecular ? 1 : 0;
+
+ if (STATE.separateSpecular != sepSpec)
+ {
+ // Never set separate specular and color sum to different values.
+ // Otherwise SIS 76x on OpenGL will hang up the machine.
+ // And we probably never need them to be different anyway :)
+ if( separateSpecular )
+ {
+ OGL_CALL(glLightModeli( GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR ));
+ OGL_CALL(glEnable( GL_COLOR_SUM_EXT ));
+ }
+ else
+ {
+ OGL_CALL(glLightModeli( GL_LIGHT_MODEL_COLOR_CONTROL, GL_SINGLE_COLOR ));
+ OGL_CALL(glDisable( GL_COLOR_SUM_EXT ));
+ }
+ STATE.separateSpecular = sepSpec;
+ }
+
+ if( colorMaterial != STATE.colorMaterial )
+ {
+ if( colorMaterial != kColorMatDisabled )
+ {
+ OGL_CALL(glColorMaterial( GL_FRONT_AND_BACK, kColorMatModeGL[colorMaterial] ));
+ OGL_CALL(glEnable( GL_COLOR_MATERIAL ));
+ }
+ else
+ {
+ OGL_CALL(glDisable( GL_COLOR_MATERIAL ));
+
+ // looks like by disabling ColorMaterial OpenGL driver will reset material props
+ // therefore we invalidate materials cache
+ STATE.matAmbient.Set(-1, -1, -1, -1);
+ STATE.matDiffuse.Set(-1, -1, -1, -1);
+ STATE.matSpecular.Set(-1, -1, -1, -1);
+ STATE.matEmissive.Set(-1, -1, -1, -1);
+ }
+
+ STATE.colorMaterial = colorMaterial;
+ }
+}
+
+void GFX_GL_IMPL::SetMaterial( const float ambient[4], const float diffuse[4], const float specular[4], const float emissive[4], const float shininess )
+{
+ GFX_LOG(("{%f, %f, %f, %f}, {%f, %f, %f, %f}, {%f, %f, %f, %f}, {%f, %f, %f, %f}, %f",
+ ambient[0], ambient[1], ambient[2], ambient[3],
+ diffuse[0], diffuse[1], diffuse[2], diffuse[3],
+ specular[0], specular[1], specular[2], specular[3],
+ emissive[0], emissive[1], emissive[2], emissive[3],
+ shininess));
+ if (STATE.matAmbient != ambient) {
+ OGL_CALL(glMaterialfv (GL_FRONT, GL_AMBIENT, ambient));
+ STATE.matAmbient.Set (ambient);
+ }
+
+ if (STATE.matDiffuse != diffuse) {
+ OGL_CALL(glMaterialfv (GL_FRONT, GL_DIFFUSE, diffuse));
+ STATE.matDiffuse.Set (diffuse);
+ }
+
+ if (STATE.matSpecular != specular) {
+ OGL_CALL(glMaterialfv (GL_FRONT, GL_SPECULAR, specular));
+ STATE.matSpecular.Set (specular);
+ }
+
+ if (STATE.matEmissive != emissive) {
+ OGL_CALL(glMaterialfv (GL_FRONT, GL_EMISSION, emissive));
+ STATE.matEmissive.Set (emissive);
+ }
+
+ if( STATE.matShininess != shininess ) {
+ float glshine = std::max<float>( std::min<float>(shininess,1.0f), 0.0f) * 128.0f;
+ OGL_CALL(glMaterialf (GL_FRONT, GL_SHININESS, glshine));
+ STATE.matShininess = shininess;
+ }
+
+ // From shaderstate, the material is set after the ColorMaterial.
+ // So here if color material is used, setup invalid values to material
+ // cache; otherwise they would get out of sync.
+
+ switch( STATE.colorMaterial ) {
+ case kColorMatEmission: STATE.matEmissive.Set(-1, -1, -1, -1); break;
+ case kColorMatAmbientAndDiffuse:
+ STATE.matAmbient.Set(-1, -1, -1, -1);
+ STATE.matDiffuse.Set(-1, -1, -1, -1);
+ break;
+ }
+}
+
+
+void GFX_GL_IMPL::SetColor( const float color[4] )
+{
+ GFX_LOG(("{%f, %f, %f, %f}", color[0], color[1], color[2], color[3]));
+ OGL_CALL(glColor4fv( color ));
+}
+
+
+void GFX_GL_IMPL::SetViewport( int x, int y, int width, int height )
+{
+ GFX_LOG(("{%d, %d, %d, %d}", x, y, width, height));
+ STATE.viewport[0] = x;
+ STATE.viewport[1] = y;
+ STATE.viewport[2] = width;
+ STATE.viewport[3] = height;
+ OGL_CALL(glViewport( x, y, width, height ));
+}
+
+void GFX_GL_IMPL::GetViewport( int* port ) const
+{
+ port[0] = STATE.viewport[0];
+ port[1] = STATE.viewport[1];
+ port[2] = STATE.viewport[2];
+ port[3] = STATE.viewport[3];
+}
+
+void GFX_GL_IMPL::SetScissorRect( int x, int y, int width, int height )
+{
+ GFX_LOG(("{%d, %d, %d, %d}", x, y, width, height));
+ if (STATE.scissor != 1)
+ {
+ OGL_CALL(glEnable( GL_SCISSOR_TEST ));
+ STATE.scissor = 1;
+ }
+
+ STATE.scissorRect[0] = x;
+ STATE.scissorRect[1] = y;
+ STATE.scissorRect[2] = width;
+ STATE.scissorRect[3] = height;
+ OGL_CALL(glScissor( x, y, width, height ));
+
+}
+
+void GFX_GL_IMPL::DisableScissor()
+{
+ GFX_LOG((""));
+ if (STATE.scissor != 0)
+ {
+ OGL_CALL(glDisable( GL_SCISSOR_TEST ));
+ STATE.scissor = 0;
+ }
+}
+
+bool GFX_GL_IMPL::IsScissorEnabled() const
+{
+ return STATE.scissor == 1;
+}
+
+void GFX_GL_IMPL::GetScissorRect( int scissor[4] ) const
+{
+ scissor[0] = STATE.scissorRect[0];
+ scissor[1] = STATE.scissorRect[1];
+ scissor[2] = STATE.scissorRect[2];
+ scissor[3] = STATE.scissorRect[3];
+}
+
+bool GFX_GL_IMPL::IsCombineModeSupported( unsigned int combiner )
+{
+ return TextureCombinersGL::IsCombineModeSupported( combiner );
+}
+
+TextureCombinersHandle GFX_GL_IMPL::CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular )
+{
+ GFX_LOG(("%d, %d, %d", count, hasVertexColorOrLighting, usesAddSpecular));
+ TextureCombinersGL* implGL = TextureCombinersGL::Create( count, texEnvs, props );
+ return TextureCombinersHandle( implGL );
+}
+
+void GFX_GL_IMPL::DeleteTextureCombiners( TextureCombinersHandle& textureCombiners )
+{
+ GFX_LOG((""));
+ TextureCombinersGL* implGL = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersGL);
+ delete implGL;
+ textureCombiners.Reset();
+}
+
+static void ClearTextureUnitGL (DeviceStateGL& state, int unit)
+{
+ GFX_LOG(("%d", unit));
+ DebugAssert (unit >= 0 && unit < gGraphicsCaps.maxTexUnits);
+
+ TextureUnitStateGL& currTex = state.textures[unit];
+ if (currTex.texDim == kTexDimNone)
+ return;
+
+ if (state.shaderEnabledImpl[kShaderFragment] == kShaderImplUndefined)
+ {
+ ActivateTextureUnitGL (unit);
+
+ if( currTex.texDim != kTexDimUnknown )
+ {
+ OGL_CALL(glDisable( kTexDimTableGL[currTex.texDim] ));
+ }
+ else
+ {
+ OGL_CALL(glDisable( GL_TEXTURE_2D ));
+ OGL_CALL(glDisable( GL_TEXTURE_1D ));
+ OGL_CALL(glDisable( GL_TEXTURE_CUBE_MAP_ARB ));
+ if( gGraphicsCaps.has3DTexture )
+ OGL_CALL(glDisable( GL_TEXTURE_3D ));
+ }
+ currTex.texDim = kTexDimNone;
+ }
+ else
+ {
+ currTex.texDim = kTexDimUnknown;
+ }
+}
+
+
+void GFX_GL_IMPL::SetTextureCombinersThreadable( TextureCombinersHandle textureCombiners, const TexEnvData* texEnvData, const Vector4f* texColors )
+{
+ TextureCombinersGL* implGL = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersGL);
+ AssertIf( !implGL );
+ AssertIf (IsShaderActive( kShaderFragment ));
+
+ int i = 0;
+ for( ; i < gGraphicsCaps.maxTexUnits && i < implGL->count; ++i )
+ {
+ const ShaderLab::TextureBinding& binding = implGL->texEnvs[i];
+
+ // set the texture
+ ApplyTexEnvData (i, i, texEnvData[i]);
+ // apply the combiner
+ Vector4f texcolorVal = texColors[i];
+ TextureUnitStateGL& unitState = STATE.textures[i];
+
+ // TODO: on Joe's machine this is sometimes failing (FPS tutorial, rotate scene view).
+ // So we just don't check, which disables the cache.
+ //if (current.texColor != texcolorVal) {
+ unitState.color = texcolorVal;
+ OGL_CALL(glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, texcolorVal.GetPtr() ));
+ //}
+
+ // setup combine modes
+ ApplyCombinerGL( unitState.combColor, unitState.combAlpha, binding.m_CombColor, binding.m_CombAlpha );
+ }
+
+ // clear unused textures
+ for (; i < gGraphicsCaps.maxTexUnits; ++i)
+ ClearTextureUnitGL (STATE, i);
+
+ // Get us back to TU 0, so we know where we stand
+ ActivateTextureUnitGL (0);
+}
+
+void GFX_GL_IMPL::SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props )
+{
+ TextureCombinersGL* implGL = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersGL);
+ AssertIf( !implGL );
+
+ int count = std::min(implGL->count, gGraphicsCaps.maxTexUnits);
+
+ // Fill in arrays
+ TexEnvData* texEnvData;
+ ALLOC_TEMP (texEnvData, TexEnvData, count);
+ for( int i = 0; i < count; ++i )
+ {
+ ShaderLab::TexEnv *te = ShaderLab::GetTexEnvForBinding( implGL->texEnvs[i], props );
+ Assert( te != NULL );
+ te->PrepareData (implGL->texEnvs[i].m_TextureName.index, implGL->texEnvs[i].m_MatrixName, props, &texEnvData[i]);
+ }
+
+ Vector4f* texColors;
+ ALLOC_TEMP (texColors, Vector4f, count);
+ for( int i = 0; i < count; ++i )
+ {
+ const ShaderLab::TextureBinding& binding = implGL->texEnvs[i];
+ texColors[i] = binding.GetTexColor().Get (props);
+ }
+ GFX_GL_IMPL::SetTextureCombinersThreadable(textureCombiners, texEnvData, texColors);
+}
+
+const unsigned long* GetGLTextureDimensionTable() { return kTexDimTableGL; }
+
+void GFX_GL_IMPL::SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias)
+{
+ GFX_LOG(("%d, %d, %d", unit, texture.m_ID, dim));
+ DebugAssertIf( dim < kTexDim2D || dim > kTexDimCUBE );
+
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(texture);
+ if(targetTex == 0)
+ return;
+
+ TextureUnitStateGL& currTex = STATE.textures[unit];
+
+ DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexImageUnits );
+ ActivateTextureUnitGL (unit);
+
+ int dimGLTarget = kTexDimTableGL[dim];
+
+
+ if (!IsShaderActive(kShaderFragment))
+ {
+ switch( currTex.texDim ) {
+ case kTexDimUnknown:
+ // We don't know which target is enabled, so disable all but the one we
+ // want to set, and enable only that one
+ if( dim != kTexDimDeprecated1D ) OGL_CALL(glDisable( GL_TEXTURE_1D ));
+ if( dim != kTexDim2D ) OGL_CALL(glDisable( GL_TEXTURE_2D ));
+ if( dim != kTexDimCUBE ) OGL_CALL(glDisable( GL_TEXTURE_CUBE_MAP_ARB ));
+ if( dim != kTexDim3D && gGraphicsCaps.has3DTexture ) OGL_CALL(glDisable( GL_TEXTURE_3D ));
+ OGL_CALL(glEnable( dimGLTarget ));
+ break;
+ case kTexDimNone:
+ // Texture unit was disabled. So simply enable our target.
+ OGL_CALL(glEnable( dimGLTarget ));
+ break;
+ default:
+ // Disable current and enable ours if they're different.
+ if( currTex.texDim != dim ) {
+ OGL_CALL(glDisable( kTexDimTableGL[currTex.texDim] ));
+ OGL_CALL(glEnable( dimGLTarget ));
+ }
+ break;
+ }
+ currTex.texDim = dim;
+ }
+ else
+ {
+ currTex.texDim = kTexDimUnknown;
+ }
+
+ if( targetTex != currTex.texID )
+ {
+ OGL_CALL(glBindTexture(dimGLTarget, targetTex));
+ currTex.texID = targetTex;
+ }
+ m_Stats.AddUsedTexture(texture);
+ if (gGraphicsCaps.hasMipLevelBias && bias != currTex.bias && bias != std::numeric_limits<float>::infinity())
+ {
+ OGL_CALL(glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, bias ));
+ currTex.bias = bias;
+ }
+}
+
+
+void GFX_GL_IMPL::SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16])
+{
+ GFX_LOG(("%d, %d, %d, %d, {%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f}",
+ unit, dim, texGen, identity,
+ matrix[0], matrix[1], matrix[2], matrix[3],
+ matrix[4], matrix[5], matrix[6], matrix[7],
+ matrix[8], matrix[9], matrix[10], matrix[11],
+ matrix[12], matrix[13], matrix[14], matrix[15]
+ ));
+
+ Assert (unit >= 0 && unit < kMaxSupportedTextureCoords);
+
+ // This assumes the current texture unit is already set!!!
+ TextureUnitStateGL& unitState = STATE.textures[unit];
+
+ // -------- texture matrix
+
+ if (unit < gGraphicsCaps.maxTexUnits)
+ {
+ OGL_CALL(glMatrixMode( GL_TEXTURE ));
+ OGL_CALL(glLoadMatrixf( matrix ));
+ OGL_CALL(glMatrixMode( GL_MODELVIEW ));
+ }
+
+ // -------- texture coordinate generation
+
+ if( IsShaderActive(kShaderVertex) )
+ {
+ // Ok, in theory we could just return here, as texgen is completely ignored
+ // with vertex shaders. However, on Mac OS X 10.4.10, Radeon X1600 and GeForce 8600M,
+ // there seems to be a bug. Repro case: shader that uses a single cubemap texture and
+ // CubeReflect texgen, and use glow on the camera = glow is wrong.
+ //
+ // On Windows / GeForce 7600 on OpenGL (93.71) the behaviour is correct. But I feel safer
+ // just using the workaround everywhere...
+ //
+ // So: with vertex shaders, force texgen to be disabled. The redundant change check below
+ // will catch most of the cases cheaply anyway.
+ texGen = kTexGenDisabled;
+ }
+
+ if( texGen == unitState.texGen )
+ return;
+
+ switch( texGen )
+ {
+ case kTexGenDisabled:
+ OGL_CALL(glDisable (GL_TEXTURE_GEN_S));
+ OGL_CALL(glDisable (GL_TEXTURE_GEN_T));
+ OGL_CALL(glDisable (GL_TEXTURE_GEN_R));
+ break;
+ case kTexGenSphereMap:
+ OGL_CALL(glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP));
+ OGL_CALL(glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_S));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_T));
+ OGL_CALL(glDisable(GL_TEXTURE_GEN_R));
+ break;
+ case kTexGenObject: {
+ float zplane[4] = {0,0,1,0};
+ OGL_CALL(glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR));
+ OGL_CALL(glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR));
+ OGL_CALL(glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR));
+ OGL_CALL(glTexGenfv (GL_R, GL_OBJECT_PLANE, zplane));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_S));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_T));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_R));
+ break;
+ }
+ case kTexGenEyeLinear: {
+ OGL_CALL(glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR));
+ OGL_CALL(glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR));
+ OGL_CALL(glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_S));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_T));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_R));
+ break;
+ }
+ case kTexGenCubeReflect:
+ OGL_CALL(glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB));
+ OGL_CALL(glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB));
+ OGL_CALL(glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_S));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_T));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_R));
+ break;
+ case kTexGenCubeNormal:
+ OGL_CALL(glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB));
+ OGL_CALL(glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB));
+ OGL_CALL(glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_S));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_T));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_R));
+ break;
+ }
+
+ unitState.texGen = texGen;
+}
+
+void GFX_GL_IMPL::SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace )
+{
+ GFX_LOG(("%d, %d, %d, %d, %d, %d, %d", texture.m_ID, texDim, filter, wrap, anisoLevel, hasMipMap, colorSpace));
+
+ TextureIdMapGL_QueryOrCreate(texture);
+
+ GLuint target = GetGLTextureDimensionTable()[texDim];
+ SetTexture (kShaderFragment, 0, 0, texture, texDim, std::numeric_limits<float>::infinity());
+
+ // Anisotropic texturing...
+ if( gGraphicsCaps.hasAnisoFilter && target != GL_TEXTURE_3D )
+ {
+ anisoLevel = std::min( anisoLevel, gGraphicsCaps.maxAnisoLevel );
+ OGL_CALL(glTexParameteri( target, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoLevel ));
+ }
+
+ OGL_CALL(glTexParameteri( target, GL_TEXTURE_WRAP_S, kWrapModeGL[wrap] ));
+ OGL_CALL(glTexParameteri( target, GL_TEXTURE_WRAP_T, kWrapModeGL[wrap] ));
+ if( target == GL_TEXTURE_3D || target == GL_TEXTURE_CUBE_MAP_ARB )
+ OGL_CALL(glTexParameteri( target, GL_TEXTURE_WRAP_R, kWrapModeGL[wrap] ));
+
+ if( !hasMipMap && filter == kTexFilterTrilinear )
+ filter = kTexFilterBilinear;
+
+ OGL_CALL(glTexParameteri( target, GL_TEXTURE_MAG_FILTER, filter != kTexFilterNearest ? GL_LINEAR : GL_NEAREST ));
+ if( hasMipMap )
+ OGL_CALL(glTexParameteri( target, GL_TEXTURE_MIN_FILTER, kMinFilterGL[filter] ));
+ else
+ OGL_CALL(glTexParameteri (target, GL_TEXTURE_MIN_FILTER, filter != kTexFilterNearest ? GL_LINEAR : GL_NEAREST));
+}
+
+
+
+
+// Dark corner of OpenGL:
+// (this is at least on OS X 10.4.9, MacBook Pro)
+// Before switching to new context, unbind any textures/shaders/whatever in the previous one!
+//
+// Otherwise is some _rare_ cases the textures will leak VRAM. Cases like this:
+// 1) create a depth render texture (this creates actual GL texture; as copy is required)
+// 2) create another render texture, render into it using the depth texture
+// 3) release both depth RT and this other RT
+// 4) the memory used by GL texture from step 1 is leaked!
+//
+// It seems like OS X is refcounting resources between contexts, yet when
+// a context is destroyed the refcounts are not released. Or something like that.
+//
+// The texture leak also happens if shader programs are not unbound (hard to correlate why, but...)!
+void GFX_GL_IMPL::UnbindObjects()
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+
+ // unbind textures
+ int texUnits = gGraphicsCaps.maxTexUnits;
+ for( int i = 0; i < texUnits; ++i )
+ ClearTextureUnitGL (GetGLDeviceState(device), i);
+ // unbind shaders
+ ShaderLab::SubProgram* programs[kShaderTypeCount] = {0};
+ GraphicsHelper::SetShaders (*this, programs, NULL);
+ // unbind VBOs
+ UnbindVertexBuffersGL();
+}
+
+
+
+unsigned int GetGLShaderImplTarget( ShaderImplType implType )
+{
+ switch( implType )
+ {
+ case kShaderImplVertex: return GL_VERTEX_PROGRAM_ARB;
+ case kShaderImplFragment: return GL_FRAGMENT_PROGRAM_ARB;
+ }
+ DebugAssertIf( true );
+ return 0;
+}
+
+void InvalidateActiveShaderStateGL( ShaderType type )
+{
+ GfxDevice& device = GetRealGfxDevice();
+ AssertIf( device.GetRenderer() != kGfxRendererOpenGL );
+ GFX_GL_IMPL& deviceGL = static_cast<GFX_GL_IMPL&>( device );
+
+ DeviceStateGL& state = GetGLDeviceState(deviceGL);
+ state.activeGpuProgram[type] = NULL;
+ state.activeGpuProgramParams[type] = NULL;
+ state.shaderEnabledImpl[type] = kShaderImplUndefined;
+ state.shaderEnabledID[type] = -1;
+}
+
+
+static void InternalSetShader( DeviceStateGL& state, GfxDeviceStats& stats, FogMode fog, ShaderType type, GpuProgram* program, const GpuProgramParameters* params )
+{
+ if( program )
+ {
+ GpuProgramGL& shaderGL = static_cast<GpuProgramGL&>(*program);
+ ShaderImplType implType = shaderGL.GetImplType();
+ GLShaderID id = 0;
+ if (implType == kShaderImplBoth) {
+ id = static_cast<GlslGpuProgram&>(shaderGL).GetGLProgram(fog, const_cast<GpuProgramParameters&>(*params));
+ } else {
+ id = static_cast<ArbGpuProgram&>(shaderGL).GetGLProgram(fog);
+ }
+ // set the shader
+ DebugAssert (id > 0);
+
+ ShaderImplType oldType = state.shaderEnabledImpl[type];
+ if( oldType != implType )
+ {
+ // impl type is different, disable old one and enable new one
+ if( oldType == kShaderImplBoth )
+ OGL_CALL(glUseProgramObjectARB( 0 ));
+ else if( oldType != kShaderImplUndefined )
+ OGL_CALL(glDisable( GetGLShaderImplTarget(oldType) ));
+ else {
+ // we don't know which one is currently enabled, so disable everything we have!
+ if (type == kShaderVertex)
+ OGL_CALL(glDisable(GL_VERTEX_PROGRAM_ARB));
+ if (type == kShaderFragment)
+ OGL_CALL(glDisable(GL_FRAGMENT_PROGRAM_ARB));
+ }
+
+ if( implType == kShaderImplBoth )
+ {
+ Assert (type == kShaderVertex);
+ OGL_CALL(glUseProgramObjectARB( id ));
+ }
+ else
+ {
+ GLenum gltarget = GetGLShaderImplTarget( implType );
+ OGL_CALL(glEnable( gltarget ));
+ OGL_CALL(glBindProgramARB( gltarget, id ));
+ }
+
+ state.shaderEnabledImpl[type] = implType;
+ state.shaderEnabledID[type] = id;
+ }
+ else
+ {
+ // impl type is the same, just use new shader (if different)
+
+ if( state.shaderEnabledID[type] != id )
+ {
+ if( implType == kShaderImplBoth )
+ {
+ OGL_CALL(glUseProgramObjectARB( id ));
+ }
+ else
+ {
+ OGL_CALL(glBindProgramARB( GetGLShaderImplTarget(implType), id ));
+ }
+ state.shaderEnabledID[type] = id;
+ }
+ }
+ }
+ else
+ {
+ // clear the shader
+ if( state.shaderEnabledID[type] == 0 )
+ {
+ // shader disabled, do nothing
+ DebugAssertIf( state.shaderEnabledImpl[type] != kShaderImplUndefined );
+ }
+ else if( state.shaderEnabledID[type] == -1 )
+ {
+ // shader state unknown, disable everything
+ DebugAssertIf( state.shaderEnabledImpl[type] != kShaderImplUndefined );
+ if( gGraphicsCaps.gl.hasGLSL )
+ OGL_CALL(glUseProgramObjectARB( 0 ));
+ if( type == kShaderVertex )
+ {
+ OGL_CALL(glDisable( GL_VERTEX_PROGRAM_ARB ));
+ }
+ else if( type == kShaderFragment )
+ {
+ OGL_CALL(glDisable( GL_FRAGMENT_PROGRAM_ARB ));
+ }
+ else
+ {
+ AssertIf( true );
+ }
+ state.shaderEnabledID[type] = 0;
+ }
+ else
+ {
+ // some shader enabled, disable just that one
+ ShaderImplType oldType = state.shaderEnabledImpl[type];
+ DebugAssertIf( oldType == kShaderImplUndefined );
+ if( oldType == kShaderImplBoth )
+ {
+ OGL_CALL(glUseProgramObjectARB( 0 ));
+ }
+ else
+ OGL_CALL(glDisable( GetGLShaderImplTarget(oldType) ));
+ state.shaderEnabledImpl[type] = kShaderImplUndefined;
+ state.shaderEnabledID[type] = 0;
+ }
+ }
+
+ // Note: set activeGpuProgram after doing everything above. Above code might be
+ // creating shader combinations for Fog on demand, which can reset activeGpuProgram to NULL.
+ state.activeGpuProgram[type] = program;
+ state.activeGpuProgramParams[type] = params;
+}
+
+void GFX_GL_IMPL::SetShadersThreadable(GpuProgram* programs[kShaderTypeCount], const GpuProgramParameters* params[kShaderTypeCount], UInt8 const * const paramsBuffer[kShaderTypeCount])
+{
+ GpuProgram* vertexProgram = programs[kShaderVertex];
+ GpuProgram* fragmentProgram = programs[kShaderFragment];
+
+ GFX_LOG(("%d, %d",
+ (vertexProgram? vertexProgram->GetImplType() : kShaderImplUndefined),
+ (fragmentProgram? fragmentProgram->GetImplType() : kShaderImplUndefined)));
+
+ // GLSL is only supported like this:
+ // vertex shader actually is both vertex & fragment linked program
+ // fragment shader is unused
+
+ FogMode fogMode = m_FogParams.mode;
+ if (vertexProgram && vertexProgram->GetImplType() == kShaderImplBoth)
+ {
+ Assert (fragmentProgram == 0 || fragmentProgram->GetImplType() == kShaderImplBoth);
+
+ InternalSetShader( STATE, m_Stats, m_FogParams.mode, kShaderVertex, vertexProgram, params[kShaderVertex] );
+ }
+ else
+ {
+ Assert ((fragmentProgram ? fragmentProgram->GetImplType() != kShaderImplBoth : true));
+
+ InternalSetShader( STATE, m_Stats, fogMode, kShaderVertex, vertexProgram, params[kShaderVertex] );
+ InternalSetShader( STATE, m_Stats, fogMode, kShaderFragment, fragmentProgram, params[kShaderFragment] );
+ }
+
+ for (int pt = 0; pt < kShaderTypeCount; ++pt)
+ {
+ if (programs[pt])
+ {
+ programs[pt]->ApplyGpuProgram (*params[pt], paramsBuffer[pt]);
+ m_BuiltinParamIndices[pt] = &params[pt]->GetBuiltinParams();
+ }
+ else
+ {
+ m_BuiltinParamIndices[pt] = &m_NullParamIndices;
+ }
+ }
+}
+
+
+void GFX_GL_IMPL::CreateShaderParameters( ShaderLab::SubProgram* program, FogMode fogMode )
+{
+ GpuProgramGL& shaderGL = static_cast<GpuProgramGL&>(program->GetGpuProgram());
+ ShaderImplType implType = shaderGL.GetImplType();
+ if (implType == kShaderImplBoth) {
+ static_cast<GlslGpuProgram&>(shaderGL).GetGLProgram(fogMode, program->GetParams(fogMode));
+ } else {
+ static_cast<ArbGpuProgram&>(shaderGL).GetGLProgram(fogMode);
+ }
+}
+
+bool GFX_GL_IMPL::IsShaderActive( ShaderType type ) const
+{
+ return STATE.shaderEnabledImpl[type] != kShaderImplUndefined;
+}
+
+void GFX_GL_IMPL::DestroySubProgram( ShaderLab::SubProgram* subprogram )
+{
+ GpuProgram& program = subprogram->GetGpuProgram();
+ GpuProgramGL& shaderGL = static_cast<GpuProgramGL&>(program);
+ ShaderType lastType = kShaderFragment;
+ if (shaderGL.GetImplType() == kShaderImplBoth)
+ lastType = kShaderVertex;
+
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ GLShaderID id = shaderGL.GetGLProgramIfCreated(FogMode(i));
+ for (int j = kShaderVertex; j <= lastType; ++j)
+ {
+ if (id && STATE.shaderEnabledID[j] == id)
+ {
+ STATE.activeGpuProgram[j] = NULL;
+ STATE.activeGpuProgramParams[j] = NULL;
+ STATE.shaderEnabledID[j] = -1;
+ STATE.shaderEnabledImpl[j] = kShaderImplUndefined;
+ }
+ }
+ }
+ delete subprogram;
+}
+
+void GFX_GL_IMPL::DisableLights( int startLight )
+{
+ GFX_LOG(("%d", startLight));
+
+ DebugAssertIf( startLight < 0 || startLight > kMaxSupportedVertexLights );
+ const Vector4f black(0.0F, 0.0F, 0.0F, 0.0F);
+
+ const int maxLights = gGraphicsCaps.maxLights;
+ for (int i = startLight; i < maxLights; ++i)
+ {
+ if( STATE.vertexLights[i].enabled != 0 )
+ {
+ OGL_CALL(glLightfv (GL_LIGHT0 + i, GL_DIFFUSE, black.GetPtr()));
+ OGL_CALL(glDisable( GL_LIGHT0 + i ));
+
+ STATE.vertexLights[i].enabled = 0;
+ }
+ }
+
+ for (int i = startLight; i < gGraphicsCaps.maxLights; ++i)
+ {
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Diffuse + i), black);
+ }
+}
+
+void GFX_GL_IMPL::SetLight( int light, const GfxVertexLight& data)
+{
+ GFX_LOG(("%d, [{%f, %f, %f, %f}, {%f, %f, %f, %f}, {%f, %f, %f, %f}, %f, %f, %f, %d]",
+ light,
+ data.position.x, data.position.y, data.position.z, data.position.w,
+ data.spotDirection.x, data.spotDirection.y, data.spotDirection.z, data.spotDirection.w,
+ data.color.x, data.color.y, data.color.z, data.color.w,
+ data.range, data.quadAtten, data.spotAngle, data.type));
+
+ DebugAssert(light >= 0 && light < kMaxSupportedVertexLights);
+
+ VertexLightStateGL& state = STATE.vertexLights[light];
+
+ if (state.enabled != 1)
+ {
+ OGL_CALL(glEnable( GL_LIGHT0 + light ));
+ state.enabled = 1;
+ }
+
+ float dir[4];
+
+ // position
+ if( data.position.w == 0.0f )
+ {
+ // negate directional light for OpenGL
+ dir[0] = -data.position.x;
+ dir[1] = -data.position.y;
+ dir[2] = -data.position.z;
+ dir[3] = 0.0f;
+ OGL_CALL(glLightfv( GL_LIGHT0 + light, GL_POSITION, dir ));
+ }
+ else
+ {
+ OGL_CALL(glLightfv( GL_LIGHT0 + light, GL_POSITION, data.position.GetPtr() ));
+ }
+
+ // spot direction
+ if( data.spotAngle != -1.0f )
+ OGL_CALL(glLightfv( GL_LIGHT0 + light, GL_SPOT_DIRECTION, data.spotDirection.GetPtr() ));
+
+ // colors
+ OGL_CALL(glLightfv( GL_LIGHT0 + light, GL_DIFFUSE, data.color.GetPtr() ));
+ OGL_CALL(glLightfv( GL_LIGHT0 + light, GL_SPECULAR, data.color.GetPtr() ));
+ static float zeroColor[4] = { 0, 0, 0, 0 };
+ OGL_CALL(glLightfv( GL_LIGHT0 + light, GL_AMBIENT, zeroColor ));
+
+ // attenuation
+ if( state.attenQuad != data.quadAtten ) {
+ OGL_CALL(glLightf( GL_LIGHT0 + light, GL_CONSTANT_ATTENUATION, 1.0f ));
+ OGL_CALL(glLightf( GL_LIGHT0 + light, GL_QUADRATIC_ATTENUATION, data.quadAtten ));
+ state.attenQuad = data.quadAtten;
+ }
+
+ // angles
+ if( state.spotAngle != data.spotAngle )
+ {
+ if( data.spotAngle == -1.0f )
+ {
+ // non-spot light
+ OGL_CALL(glLightf( GL_LIGHT0 + light, GL_SPOT_CUTOFF, 180.0f ));
+ }
+ else
+ {
+ // spot light
+ OGL_CALL(glLightf( GL_LIGHT0 + light, GL_SPOT_CUTOFF, data.spotAngle * 0.5f ));
+ OGL_CALL(glLightf( GL_LIGHT0 + light, GL_SPOT_EXPONENT, 18.0f - data.spotAngle * 0.1f ));
+ }
+ state.spotAngle = data.spotAngle;
+ }
+
+ SetupVertexLightParams (light, data);
+}
+
+
+void GFX_GL_IMPL::SetAmbient( const float ambient[4] )
+{
+ GFX_LOG(("{%f, %f, %f, %f}", ambient[0], ambient[1], ambient[2], ambient[3]));
+ if( STATE.ambient != ambient )
+ {
+ OGL_CALL(glLightModelfv( GL_LIGHT_MODEL_AMBIENT, ambient ));
+ STATE.ambient.Set( ambient );
+ }
+}
+
+void GFX_GL_IMPL::EnableFog (const GfxFogParams& fog)
+{
+ GFX_LOG(("%d, %f, %f, %f, {%f, %f, %f, %f}", fog.mode, fog.start, fog.end, fog.density, fog.color.x, fog.color.y, fog.color.z, fog.color.w));
+
+ DebugAssertIf( fog.mode <= kFogDisabled );
+ if( m_FogParams.mode != fog.mode )
+ {
+ OGL_CALL(glFogi( GL_FOG_MODE, kFogModeGL[fog.mode] ));
+ OGL_CALL(glEnable( GL_FOG ));
+ m_FogParams.mode = fog.mode;
+ }
+ if( m_FogParams.start != fog.start )
+ {
+ OGL_CALL(glFogf( GL_FOG_START, fog.start ));
+ m_FogParams.start = fog.start;
+ }
+ if( m_FogParams.end != fog.end )
+ {
+ OGL_CALL(glFogf( GL_FOG_END, fog.end ));
+ m_FogParams.end = fog.end;
+ }
+ if( m_FogParams.density != fog.density )
+ {
+ OGL_CALL(glFogf( GL_FOG_DENSITY, fog.density ));
+ m_FogParams.density = fog.density;
+ }
+ if( m_FogParams.color != fog.color )
+ {
+ OGL_CALL(glFogfv( GL_FOG_COLOR, fog.color.GetPtr() ));
+ m_FogParams.color = fog.color;
+ }
+}
+
+void GFX_GL_IMPL::DisableFog()
+{
+ GFX_LOG((""));
+
+ if( m_FogParams.mode != kFogDisabled )
+ {
+ OGL_CALL(glFogf( GL_FOG_DENSITY, 0.0f ));
+ OGL_CALL(glDisable( GL_FOG ));
+
+ m_FogParams.mode = kFogDisabled;
+ m_FogParams.density = 0.0f;
+ }
+}
+
+
+VBO* GFX_GL_IMPL::CreateVBO()
+{
+ VBO* vbo = new ARBVBO();
+ OnCreateVBO(vbo);
+ return vbo;
+}
+
+void GFX_GL_IMPL::DeleteVBO( VBO* vbo )
+{
+ OnDeleteVBO(vbo);
+ delete vbo;
+}
+
+DynamicVBO& GFX_GL_IMPL::GetDynamicVBO()
+{
+ if( STATE.m_DynamicVBO == NULL )
+ {
+ // DynamicARBVBO is slower! Just don't use it for now.
+ // STATE.m_DynamicVBO = new DynamicARBVBO( 1024 * 1024 ); // initial size: 1MiB VB
+ STATE.m_DynamicVBO = new DynamicNullVBO();
+ }
+ return *STATE.m_DynamicVBO;
+}
+
+
+
+// ---------- render textures
+
+
+// defined in RenderTextureGL
+RenderSurfaceHandle CreateRenderColorSurfaceGL (TextureID textureID, int width, int height, int samples, TextureDimension dim, UInt32 createFlags, RenderTextureFormat format, GLuint globalSharedFBO);
+RenderSurfaceHandle CreateRenderDepthSurfaceGL (TextureID textureID, int width, int height, int samples, UInt32 createFlags, DepthBufferFormat depthFormat, GLuint globalSharedFBO);
+void DestroyRenderSurfaceGL (RenderSurfaceHandle& rsHandle);
+bool SetRenderTargetGL (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face, GLuint globalSharedFBO);
+RenderSurfaceHandle GetActiveRenderColorSurfaceGL(int index);
+RenderSurfaceHandle GetActiveRenderDepthSurfaceGL();
+void ResolveDepthIntoTextureGL (RenderSurfaceHandle colorHandle, RenderSurfaceHandle depthHandle, GLuint globalSharedFBO, GLuint helperFBO);
+void GrabIntoRenderTextureGL (RenderSurfaceHandle rs, int x, int y, int width, int height, GLuint globalSharedFBO);
+
+
+static GLuint GetFBO (GLuint* fbo)
+{
+ if (!(*fbo) && gGraphicsCaps.hasRenderToTexture)
+ {
+ glGenFramebuffersEXT (1, fbo);
+ }
+ return *fbo;
+}
+
+static GLuint GetSharedFBO (DeviceStateGL& state)
+{
+ return GetFBO (&state.m_SharedFBO);
+}
+
+static GLuint GetHelperFBO (DeviceStateGL& state)
+{
+ return GetFBO (&state.m_HelperFBO);
+}
+
+RenderSurfaceHandle GFX_GL_IMPL::CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags)
+{
+ GFX_LOG(("%d, %d, %d, %d, %d", textureID.m_ID, width, height, createFlags, format));
+ GLuint fbo = GetSharedFBO(STATE);
+ RenderSurfaceHandle rs = CreateRenderColorSurfaceGL (textureID, width, height, samples, dim, createFlags, format, fbo);
+ #if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+ #endif
+ return rs;
+}
+RenderSurfaceHandle GFX_GL_IMPL::CreateRenderDepthSurface (TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags)
+{
+ GFX_LOG(("%d, %d, %d, %d", textureID.m_ID, width, height, depthFormat));
+ GLuint fbo = GetSharedFBO(STATE);
+ RenderSurfaceHandle rs = CreateRenderDepthSurfaceGL (textureID, width, height, samples, createFlags, depthFormat, fbo);
+ #if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+ #endif
+ return rs;
+}
+void GFX_GL_IMPL::DestroyRenderSurface (RenderSurfaceHandle& rs)
+{
+ GFX_LOG((""));
+
+ // Early out if render surface is not created (don't do flush in that case)
+ if( !rs.IsValid() )
+ return;
+
+ DestroyRenderSurfaceGL (rs);
+
+ #if UNITY_WIN
+ // Invalidating state causes problems with Windows OpenGL editor (case 490767).
+ int flags = kGLContextSkipInvalidateState;
+ #else
+ // Not invalidating state makes the cached state not match the actual state on OS X (case 494066).
+ int flags = 0;
+ #endif
+
+ GraphicsContextHandle ctx = GetMainGraphicsContext();
+ if( ctx.IsValid() )
+ ActivateGraphicsContext (ctx, false, flags);
+
+ #if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+ #endif
+}
+void GFX_GL_IMPL::SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face)
+{
+ GFX_LOG(("%d", face));
+ #if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+ #endif
+ SetRenderTargetGL (count, colorHandles, depthHandle, mipLevel, face, GetSharedFBO(STATE));
+ // changing render target might mean different color clear flags; so reset current state
+ STATE.m_CurrBlendState = NULL;
+ #if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+ #endif
+}
+void GFX_GL_IMPL::ResolveDepthIntoTexture (RenderSurfaceHandle colorHandle, RenderSurfaceHandle depthHandle)
+{
+ GFX_LOG((""));
+ ::ResolveDepthIntoTextureGL (colorHandle, depthHandle, GetSharedFBO(STATE), GetHelperFBO(STATE));
+}
+void GFX_GL_IMPL::ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle)
+{
+ void ResolveColorSurfaceGL (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle, GLuint globalSharedFBO, GLuint helperFBO);
+
+ ResolveColorSurfaceGL (srcHandle, dstHandle, GetSharedFBO(STATE), GetHelperFBO(STATE));
+}
+RenderSurfaceHandle GFX_GL_IMPL::GetActiveRenderColorSurface(int index)
+{
+ return GetActiveRenderColorSurfaceGL(index);
+}
+RenderSurfaceHandle GFX_GL_IMPL::GetActiveRenderDepthSurface()
+{
+ return GetActiveRenderDepthSurfaceGL();
+}
+void GFX_GL_IMPL::SetSurfaceFlags (RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags)
+{
+}
+
+// ---------- uploading textures
+
+void GFX_GL_IMPL::UploadTexture2D( TextureID texture, TextureDimension dimension, UInt8* srcData, int srcSize, int width, int height, TextureFormat format, int mipCount, UInt32 uploadFlags, int skipMipLevels, TextureUsageMode usageMode, TextureColorSpace colorSpace )
+{
+ GFX_LOG(("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d", texture.m_ID, dimension, width, height, format, mipCount, uploadFlags, skipMipLevels, usageMode, colorSpace));
+ ::UploadTexture2DGL( texture, dimension, srcData, width, height, format, mipCount, uploadFlags, skipMipLevels, usageMode, colorSpace );
+}
+
+void GFX_GL_IMPL::UploadTextureSubData2D( TextureID texture, UInt8* srcData, int srcSize, int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace )
+{
+ GFX_LOG(("%d, %d, %d, %d, %d, %d, %d, %d", texture.m_ID, mipLevel, x, y, width, height, format, colorSpace));
+ ::UploadTextureSubData2DGL( texture, srcData, mipLevel, x, y, width, height, format, colorSpace );
+}
+
+void GFX_GL_IMPL::UploadTextureCube( TextureID texture, UInt8* srcData, int srcSize, int faceDataSize, int size, TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace )
+{
+ GFX_LOG(("%d, %d, %d, %d, %d, %d, %d", texture.m_ID, faceDataSize, size, format, mipCount, uploadFlags, colorSpace));
+ ::UploadTextureCubeGL( texture, srcData, faceDataSize, size, format, mipCount, uploadFlags, colorSpace );
+}
+
+void GFX_GL_IMPL::UploadTexture3D( TextureID texture, UInt8* srcData, int srcSize, int width, int height, int depth, TextureFormat format, int mipCount, UInt32 uploadFlags )
+{
+ GFX_LOG(("%d, %d, %d, %d, %d, %d, %d", texture.m_ID, width, height, depth, format, mipCount, uploadFlags ));
+ ::UploadTexture3DGL( texture, srcData, width, height, depth, format, mipCount, uploadFlags );
+}
+
+void GFX_GL_IMPL::DeleteTexture( TextureID texture )
+{
+ GFX_LOG(("%d", texture.m_ID));
+ GLuint texname = (GLuint)TextureIdMap::QueryNativeTexture(texture);
+ if(texname == 0)
+ return;
+
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(texture.m_ID);
+ glDeleteTextures( 1, &texname );
+
+ // invalidate texture unit states that used this texture
+ for( int i = 0; i < kMaxSupportedTextureUnits; ++i )
+ {
+ TextureUnitStateGL& currTex = STATE.textures[i];
+ if( currTex.texID == texname )
+ currTex.Invalidate();
+ }
+
+ TextureIdMap::RemoveTexture(texture);
+}
+
+
+// ---------- context
+
+GfxDevice::PresentMode GFX_GL_IMPL::GetPresentMode()
+{
+ return kPresentBeforeUpdate;
+}
+void GFX_GL_IMPL::BeginFrame()
+{
+ GFX_LOG((""));
+ m_InsideFrame = true;
+}
+void GFX_GL_IMPL::EndFrame()
+{
+ GFX_LOG((""));
+ m_InsideFrame = false;
+}
+bool GFX_GL_IMPL::IsValidState()
+{
+ #if UNITY_LINUX
+ // The Linux webplayer sometimes appears to lose sync with the GL cache.
+ // This is a quick, best-guess test for that which allows us to recover gracefully.
+ float temp;
+ glGetFloatv (GL_FRONT_FACE, &temp);
+ return CompareApproximately(temp,(STATE.appBackfaceMode != STATE.userBackfaceMode)?GL_CCW:GL_CW,0.0001f);
+ #endif
+ return true;
+}
+
+void GFX_GL_IMPL::PresentFrame()
+{
+ GFX_LOG((""));
+ PresentContextGL( GetMainGraphicsContext() );
+ #if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+ #endif
+}
+
+void GFX_GL_IMPL::FinishRendering()
+{
+ GFX_LOG((""));
+ OGL_CALL(glFinish());
+}
+
+
+// ---------- immediate mode rendering
+
+template <class T>
+void WriteToArray(const T& data, dynamic_array<UInt8>& dest)
+{
+ int offset = dest.size();
+ dest.resize_uninitialized(offset + sizeof(T), true);
+ *reinterpret_cast<T*>(&dest[offset]) = data;
+}
+
+
+void GFX_GL_IMPL::ImmediateVertex( float x, float y, float z )
+{
+ GFX_LOG(("%f, %f, %f", x, y ,z));
+ WriteToArray(UInt32(kImmediateVertex), STATE.m_ImmediateVertices);
+ WriteToArray(Vector3f(x, y, z), STATE.m_ImmediateVertices);
+}
+
+void GFX_GL_IMPL::ImmediateNormal( float x, float y, float z )
+{
+ GFX_LOG(("%f, %f, %f", x, y ,z));
+ WriteToArray(UInt32(kImmediateNormal), STATE.m_ImmediateVertices);
+ WriteToArray(Vector3f(x, y, z), STATE.m_ImmediateVertices);
+}
+
+void GFX_GL_IMPL::ImmediateColor( float r, float g, float b, float a )
+{
+ GFX_LOG(("%f, %f, %f, %f", r, g, b, a));
+ WriteToArray(UInt32(kImmediateColor), STATE.m_ImmediateVertices);
+ WriteToArray(ColorRGBAf(r, g, b, a), STATE.m_ImmediateVertices);
+}
+
+void GFX_GL_IMPL::ImmediateTexCoordAll( float x, float y, float z )
+{
+ GFX_LOG(("%f, %f, %f", x, y ,z));
+ WriteToArray(UInt32(kImmediateTexCoordAll), STATE.m_ImmediateVertices);
+ WriteToArray(Vector3f(x, y, z), STATE.m_ImmediateVertices);
+}
+
+void GFX_GL_IMPL::ImmediateTexCoord( int unit, float x, float y, float z )
+{
+ GFX_LOG(("%d, %f, %f, %f", unit, x, y ,z));
+ WriteToArray(UInt32(kImmediateTexCoord), STATE.m_ImmediateVertices);
+ WriteToArray(UInt32(unit), STATE.m_ImmediateVertices);
+ WriteToArray(Vector3f(x, y, z), STATE.m_ImmediateVertices);
+}
+
+void GFX_GL_IMPL::ImmediateBegin( GfxPrimitiveType type )
+{
+ GFX_LOG(("%d", type));
+ STATE.m_ImmediateMode = type;
+ STATE.m_ImmediateVertices.clear();
+}
+
+void GFX_GL_IMPL::ImmediateEnd()
+{
+ GFX_LOG((""));
+
+ int size = STATE.m_ImmediateVertices.size();
+ if (size == 0)
+ return;
+
+ // when beginning immediate mode, turn off any vertex array components that might be
+ // activated
+ UnbindVertexBuffersGL();
+ ClearActiveChannelsGL();
+ ActivateChannelsGL();
+ BeforeDrawCall( true );
+ glBegin (kTopologyGL[STATE.m_ImmediateMode]);
+
+ const UInt8* src = STATE.m_ImmediateVertices.data();
+ const UInt8* end = src + size;
+ int vertexCount = 0;
+ while (src < end)
+ {
+ UInt32 type = *reinterpret_cast<const UInt32*>(src);
+ src += sizeof(UInt32);
+ switch (type)
+ {
+ case kImmediateVertex:
+ {
+ const Vector3f& v = *reinterpret_cast<const Vector3f*>(src);
+ src += sizeof(Vector3f);
+ glVertex3f( v.x, v.y, v.z );
+ ++vertexCount;
+ break;
+ }
+ case kImmediateNormal:
+ {
+ const Vector3f& v = *reinterpret_cast<const Vector3f*>(src);
+ src += sizeof(Vector3f);
+ glNormal3f( v.x, v.y, v.z );
+ break;
+ }
+ case kImmediateColor:
+ {
+ const ColorRGBAf& col = *reinterpret_cast<const ColorRGBAf*>(src);
+ src += sizeof(ColorRGBAf);
+ glColor4f( col.r, col.g, col.b, col.a );
+ break;
+ }
+ case kImmediateTexCoordAll:
+ {
+ const Vector3f& v = *reinterpret_cast<const Vector3f*>(src);
+ src += sizeof(Vector3f);
+ for( int i = gGraphicsCaps.maxTexCoords; i--; )
+ {
+ if( STATE.textures[i].texGen <= kTexGenDisabled )
+ glMultiTexCoord3fARB( GL_TEXTURE0_ARB + i, v.x, v.y, v.z );
+ }
+ break;
+ }
+ case kImmediateTexCoord:
+ {
+ UInt32 unit = *reinterpret_cast<const UInt32*>(src);
+ src += sizeof(UInt32);
+ const Vector3f& v = *reinterpret_cast<const Vector3f*>(src);
+ src += sizeof(Vector3f);
+ glMultiTexCoord3fARB( GL_TEXTURE0_ARB + unit, v.x, v.y, v.z );
+ break;
+ }
+ default:
+ ErrorString("Unknown immediate type!");
+ break;
+ }
+ }
+
+ glEnd();
+ // NOTE: all immediate mode calls CAN'T call glGetError - this will result in an error!
+ // So all of Immediate* calls do not use OGL_CALL macro; instead we're just checking
+ // for errors after glEnd.
+ GLAssert();
+
+ STATE.m_ImmediateVertices.clear();
+
+ // stats
+ switch( STATE.m_ImmediateMode ) {
+ case kPrimitiveTriangles:
+ m_Stats.AddDrawCall( vertexCount / 3, vertexCount );
+ break;
+ case kPrimitiveTriangleStripDeprecated:
+ m_Stats.AddDrawCall( vertexCount - 2, vertexCount );
+ break;
+ case kPrimitiveQuads:
+ m_Stats.AddDrawCall( vertexCount / 4 * 2, vertexCount );
+ break;
+ case kPrimitiveLines:
+ m_Stats.AddDrawCall( vertexCount / 2, vertexCount );
+ break;
+ }
+}
+
+extern GLint gDefaultFBOGL;
+
+
+void GLReadPixelsWrapper (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels)
+{
+ // When running in Safari 64-bit, we cannot just perform a normal glReadPixels.
+ // This is because we are rendering into an FBO which is allocated by the Safari process.
+ // Apparently there are some memory access restrictions, I have not been able to read from that fbo.
+ // Also, reading from FBOs with FSAA is not possible.
+ // We work around this by allocating an FBO of our own, blitting into that, and reading from there.
+
+ GLuint fbo;
+ GLuint fboTexture;
+ GLint oldTexture;
+ bool useWorkaround = (gDefaultFBOGL != 0);
+
+ if (useWorkaround)
+ {
+ GLint curFBO;
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &curFBO);
+ if (curFBO != gDefaultFBOGL)
+ useWorkaround = false;
+ }
+ if (useWorkaround)
+ {
+ if( !gGraphicsCaps.gl.hasFrameBufferBlit )
+ {
+ ErrorString ("Cannot read from image without framebuffer blit extension!");
+ return;
+ }
+
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTexture);
+
+ // Create FBO
+ glGenTextures(1, &fboTexture);
+ glBindTexture(GL_TEXTURE_2D, fboTexture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ glGenFramebuffersEXT(1, &fbo);
+ glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, fbo );
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, fboTexture, 0);
+ GLAssert();
+
+ // Blit into it
+ glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, gDefaultFBOGL);
+ glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fbo);
+ glBlitFramebufferEXT(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
+ GLAssert();
+ }
+
+ glReadPixels( x, y, width, height, format, type, pixels );
+
+ if (useWorkaround)
+ {
+ // Restore old state
+ glBindTexture(GL_TEXTURE_2D, oldTexture);
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, gDefaultFBOGL);
+
+ // Destroy FBO
+ glDeleteFramebuffersEXT( 1, &fbo );
+ glDeleteTextures(1, &fboTexture);
+ GLAssert();
+ }
+}
+
+bool GFX_GL_IMPL::CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 )
+{
+ GFX_LOG(("%d, %d, %d, %d", left, bottom, width, height));
+ GLReadPixelsWrapper ( left, bottom, width, height, GL_RGBA, GL_UNSIGNED_BYTE, rgba32 );
+ return true;
+}
+
+bool GFX_GL_IMPL::ReadbackImage( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY )
+{
+ GFX_LOG(("%d, %d, %d, %d, %d, %d", left, bottom, width, height, destX, destY));
+ return ReadbackTextureGL( image, left, bottom, width, height, destX, destY );
+}
+
+void GFX_GL_IMPL::GrabIntoRenderTexture (RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height )
+{
+ GFX_LOG(("%d, %d, %d, %d", x, y, width, height));
+ ::GrabIntoRenderTextureGL (rs, x, y, width, height, GetHelperFBO (STATE));
+}
+
+RenderTextureFormat GFX_GL_IMPL::GetDefaultRTFormat() const { return kRTFormatARGB32; }
+RenderTextureFormat GFX_GL_IMPL::GetDefaultHDRRTFormat() const { return kRTFormatARGBHalf; }
+
+void* GFX_GL_IMPL::GetNativeTexturePointer(TextureID id)
+{
+ return (void*)TextureIdMap::QueryNativeTexture(id);
+}
+
+
+void GFX_GL_IMPL::SetActiveContext (void* ctx)
+{
+ GraphicsContextGL* glctx = reinterpret_cast<GraphicsContextGL*> (ctx);
+ ActivateGraphicsContextGL (*glctx, kGLContextSkipInvalidateState | kGLContextSkipUnbindObjects | kGLContextSkipFlush);
+}
+
+
+#if ENABLE_PROFILER
+
+GfxTimerQuery* GFX_GL_IMPL::CreateTimerQuery()
+{
+ TimerQueryGL* query = new TimerQueryGL;
+ return query;
+}
+
+void GFX_GL_IMPL::DeleteTimerQuery(GfxTimerQuery* query)
+{
+ delete query;
+}
+
+void GFX_GL_IMPL::BeginTimerQueries()
+{
+ if( !gGraphicsCaps.hasTimerQuery )
+ return;
+
+ g_TimerQueriesGL.BeginTimerQueries();
+}
+
+void GFX_GL_IMPL::EndTimerQueries()
+{
+ if (!gGraphicsCaps.hasTimerQuery )
+ return;
+
+ g_TimerQueriesGL.EndTimerQueries();
+}
+
+bool GFX_GL_IMPL::TimerQueriesIsActive()
+{
+ if (!gGraphicsCaps.hasTimerQuery )
+ return false;
+
+ return g_TimerQueriesGL.IsActive();
+}
+
+#endif // ENABLE_PROFILER
+
+// -------- editor-only functions
+
+#if UNITY_EDITOR
+
+void GFX_GL_IMPL::SetAntiAliasFlag( bool aa )
+{
+ if( aa )
+ {
+ OGL_CALL(glHint (GL_LINE_SMOOTH_HINT, GL_NICEST));
+ OGL_CALL(glEnable (GL_LINE_SMOOTH));
+ }
+ else
+ {
+ OGL_CALL(glHint (GL_LINE_SMOOTH_HINT, GL_FASTEST));
+ OGL_CALL(glDisable (GL_LINE_SMOOTH));
+ }
+}
+
+
+void GFX_GL_IMPL::DrawUserPrimitives( GfxPrimitiveType type, int vertexCount, UInt32 vertexChannels, const void* data, int stride )
+{
+ if( vertexCount == 0 )
+ return;
+
+ AssertIf( !data || vertexCount < 0 || vertexChannels == 0 );
+
+ UnbindVertexBuffersGL();
+ ClearActiveChannelsGL();
+ int offset = 0;
+ for( int i = 0; i < kShaderChannelCount; ++i )
+ {
+ if( !( vertexChannels & (1<<i) ) )
+ continue;
+ VertexComponent destComponent = kSuitableVertexComponentForChannel[i];
+ SetChannelDataGL( (ShaderChannel)i, destComponent, (const UInt8*)data + offset, stride );
+ offset += VBO::GetDefaultChannelByteSize(i);
+ }
+ ActivateChannelsGL();
+ BeforeDrawCall(false);
+
+ OGL_CALL(glDrawArrays(kTopologyGL[type], 0, vertexCount));
+
+ // stats
+ switch (type) {
+ case kPrimitiveTriangles:
+ m_Stats.AddDrawCall( vertexCount / 3, vertexCount );
+ break;
+ case kPrimitiveQuads:
+ m_Stats.AddDrawCall( vertexCount / 4 * 2, vertexCount );
+ break;
+ case kPrimitiveLines:
+ m_Stats.AddDrawCall( vertexCount / 2, vertexCount );
+ break;
+ case kPrimitiveLineStrip:
+ m_Stats.AddDrawCall( vertexCount-1, vertexCount );
+ break;
+ }
+}
+
+int GFX_GL_IMPL::GetCurrentTargetAA() const
+{
+ int fsaa = 0;
+ if( gGraphicsCaps.hasMultiSample )
+ glGetIntegerv( GL_SAMPLES_ARB, &fsaa );
+ return fsaa;
+}
+
+#if UNITY_WIN
+ GfxDeviceWindow* GFX_GL_IMPL::CreateGfxWindow( HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias )
+ {
+ return new GLWindow(window, width, height, depthFormat, antiAlias);
+ }
+#endif
+
+
+#endif // UNITY_EDITOR
+
+
+
+// --------------------------------------------------------------------------
+// -------- verification of state
+
+
+
+#if GFX_DEVICE_VERIFY_ENABLE
+
+
+void VerifyGLState(GLenum gl, float val, const char *str);
+#define VERIFY(s,t) VerifyGLState (s, t, #s " (" #t ")")
+void VerifyGLStateI(GLenum gl, int val, const char *str);
+#define VERIFYI(s,t) VerifyGLStateI (s, t, #s " (" #t ")")
+void VerifyGLState4(GLenum gl, const float *val, const char *str);
+#define VERIFY4(s,t) VerifyGLState4 (s, t, #s " (" #t ")")
+
+void VerifyEnabled(GLenum gl, bool val, const char *str);
+#define VERIFYENAB(s,t) VerifyEnabled ( s, t, #s " (" #t ")")
+
+void VerifyMaterial (GLenum gl, const float *val, const char *str);
+#define VERIFYMAT(s,t) VerifyMaterial ( s, t, #s " (" #t ")")
+
+void VerifyTexParam (int target, GLenum gl, float val, const char *str);
+#define VERIFYTP(s,t) VerifyTexParam (target, s, t, #s " (" #t ")")
+
+void VerifyTexEnv (GLenum target, GLenum gl, int val, const char *str);
+#define VERIFYTE(g,s,t) VerifyTexEnv (g, s, t, #s " (" #t ")")
+
+void VerifyTexEnv4 (GLenum target, GLenum gl, const float *val, const char *str);
+#define VERIFYTE4(g,s,t) VerifyTexEnv4(g, s, t, #s " (" #t ")")
+
+void VerifyTexGen (GLenum coord, GLenum gl, int val, const char *str);
+#define VERIFYTG(c,s,t) VerifyTexGen (c, s, t, #s " (" #t ")")
+
+
+static void VERIFY_PRINT( const char* format, ... )
+{
+ va_list va;
+ va_start (va, format);
+ ErrorString( VFormat( format, va ) );
+ va_end (va);
+}
+
+const float kVerifyDelta = 0.0001f;
+
+void VerifyGLState(GLenum gl, float val, const char *str)
+{
+ float temp;
+ glGetFloatv (gl, &temp);
+ if( !CompareApproximately(temp,val,kVerifyDelta) ) {
+ VERIFY_PRINT ("%s differs from cache (%f != %f)\n", str, val, temp);
+ }
+}
+
+void VerifyGLStateI(GLenum gl, int val, const char *str)
+{
+ int temp;
+ glGetIntegerv (gl, &temp);
+ if (temp != val) {
+ VERIFY_PRINT ("%s differs from cache (0x%x != 0x%x)\n", str, val, temp);
+ }
+}
+
+void VerifyGLState4 (GLenum gl, const float *val, const char *str)
+{
+ GLfloat temp[4];
+ glGetFloatv (gl, temp);
+ if( !CompareApproximately(temp[0],val[0],kVerifyDelta) ||
+ !CompareApproximately(temp[1],val[1],kVerifyDelta) ||
+ !CompareApproximately(temp[2],val[2],kVerifyDelta) ||
+ !CompareApproximately(temp[3],val[3],kVerifyDelta) )
+ {
+ VERIFY_PRINT ("%s differs from cache (%f,%f,%f,%f) != (%f,%f,%f,%f)\n", str, val[0],val[1],val[2],val[3], temp[0],temp[1],temp[2],temp[3]);
+ }
+}
+
+void VerifyEnabled(GLenum gl, bool val, const char *str)
+{
+ bool temp = glIsEnabled (gl) ? true : false;
+ if (temp != val) {
+ VERIFY_PRINT ("%s differs from cache (%d != %d)\n", str, val, temp);
+ }
+}
+
+void VerifyMaterial (GLenum gl, const float *val, const char *str)
+{
+ GLfloat temp[4];
+ glGetMaterialfv (GL_FRONT, gl, temp);
+ if( !CompareApproximately(temp[0],val[0],kVerifyDelta) ||
+ !CompareApproximately(temp[1],val[1],kVerifyDelta) ||
+ !CompareApproximately(temp[2],val[2],kVerifyDelta) ||
+ !CompareApproximately(temp[3],val[3],kVerifyDelta) )
+ {
+ VERIFY_PRINT ("%s differs from cache (%f,%f,%f,%f) != (%f,%f,%f,%f)\n",str, val[0],val[1],val[2],val[3], temp[0],temp[1],temp[2],temp[3]);
+ }
+}
+
+void VerifyTexParam (int target, GLenum gl, float val, const char *str)
+{
+ float temp;
+ glGetTexParameterfv (target, gl, &temp);
+ if( !CompareApproximately(temp,val,kVerifyDelta) ) {
+ VERIFY_PRINT ("%s differs from cache (%f != %f)\n", str, val, temp);
+ }
+}
+
+void VerifyTexEnv (GLenum target, GLenum gl, int val, const char *str)
+{
+ GLint temp;
+ glGetTexEnviv (target, gl, &temp);
+ if (temp != val) {
+ VERIFY_PRINT ("%s differs from cache (%d != %d)\n", str, (int)val, (int)temp);
+ }
+}
+
+void VerifyTexEnv4( GLenum target, GLenum gl, const float *val, const char *str )
+{
+ GLfloat temp[4];
+ glGetTexEnvfv(target, gl, temp);
+ float val0 = clamp01(val[0]);
+ float val1 = clamp01(val[1]);
+ float val2 = clamp01(val[2]);
+ float val3 = clamp01(val[3]);
+ if( !CompareApproximately(temp[0],val0,kVerifyDelta) ||
+ !CompareApproximately(temp[1],val1,kVerifyDelta) ||
+ !CompareApproximately(temp[2],val2,kVerifyDelta) ||
+ !CompareApproximately(temp[3],val3,kVerifyDelta) )
+ {
+ VERIFY_PRINT ("%s differs from cache (%f,%f,%f,%f) != (%f,%f,%f,%f)\n", str, val0,val1,val2,val3, temp[0],temp[1],temp[2],temp[3]);
+ }
+}
+
+
+void VerifyTexGen(GLenum coord, GLenum gl, int val, const char *str)
+{
+ GLint temp;
+ glGetTexGeniv (coord, gl, &temp);
+ if (temp != val) {
+ VERIFY_PRINT ("TexGen %s differs from cache (%d != %d)\n", str, val, (int)temp);
+ }
+}
+
+
+void GFX_GL_IMPL::VerifyState()
+{
+ STATE.Verify();
+
+ if( m_FogParams.mode != kFogUnknown )
+ {
+ VERIFYENAB( GL_FOG, m_FogParams.mode != 0 );
+ //VERIFY( GL_FOG_MODE, GetFogMode()[m_FogParams.mode] ); // TBD
+ if( m_FogParams.mode != kFogDisabled )
+ {
+ VERIFY( GL_FOG_START, m_FogParams.start );
+ VERIFY( GL_FOG_END, m_FogParams.end );
+ VERIFY( GL_FOG_DENSITY, m_FogParams.density );
+ float clampedFogColor[4];
+ clampedFogColor[0] = clamp01(m_FogParams.color.x);
+ clampedFogColor[1] = clamp01(m_FogParams.color.y);
+ clampedFogColor[2] = clamp01(m_FogParams.color.z);
+ clampedFogColor[3] = clamp01(m_FogParams.color.w);
+
+ VERIFY4( GL_FOG_COLOR, clampedFogColor );
+ }
+ }
+}
+
+
+void DeviceStateGL::Verify()
+{
+ GLAssert();
+ if( depthFunc != kFuncUnknown ) {
+ VERIFYENAB( GL_DEPTH_TEST, depthFunc != kFuncDisabled );
+ if( depthFunc != kFuncDisabled ) {
+ VERIFY( GL_DEPTH_FUNC, kCmpFuncGL[depthFunc] );
+ }
+ }
+ if( depthWrite != -1 ) {
+ GLboolean temp;
+ glGetBooleanv( GL_DEPTH_WRITEMASK, &temp );
+ if( temp != depthWrite )
+ printf_console( "DepthWrite mismatch (%d != %d)\n", (int)temp, (int)depthWrite );
+ }
+ if( blending != -1 ) {
+ VERIFYENAB( GL_BLEND, blending != 0 );
+ if( blending ) {
+ VERIFYI( GL_BLEND_SRC, srcBlend );
+ VERIFYI( GL_BLEND_DST, destBlend );
+ VERIFYI( GL_BLEND_EQUATION_RGB, blendOp );
+ if( gGraphicsCaps.hasSeparateAlphaBlend ) {
+ VERIFYI( GL_BLEND_SRC_ALPHA, srcBlendAlpha );
+ VERIFYI( GL_BLEND_DST_ALPHA, destBlendAlpha );
+ VERIFYI( GL_BLEND_EQUATION_ALPHA, blendOpAlpha );
+ }
+
+ }
+ }
+
+ if( alphaFunc != kFuncUnknown ) {
+ VERIFYENAB( GL_ALPHA_TEST, alphaFunc != kFuncDisabled );
+ if( alphaFunc != kFuncDisabled ) {
+ VERIFY( GL_ALPHA_TEST_FUNC, kCmpFuncGL[alphaFunc] );
+ if( alphaValue != -1 )
+ VERIFY( GL_ALPHA_TEST_REF, alphaValue );
+ }
+ }
+
+ if( culling != kCullUnknown ) {
+ VERIFYENAB( GL_CULL_FACE, culling != kCullOff );
+ switch (culling) {
+ case kCullOff: break;
+ case kCullFront: VERIFY (GL_CULL_FACE_MODE, GL_FRONT); break;
+ case kCullBack: VERIFY (GL_CULL_FACE_MODE, GL_BACK); break;
+ default: printf_console( "Invalid face cull mode: %d\n", (int)culling ); break;
+ }
+ }
+ VERIFY( GL_FRONT_FACE, (appBackfaceMode != userBackfaceMode) ? GL_CCW : GL_CW );
+
+ if( scissor != -1 ) {
+ VERIFYENAB( GL_SCISSOR_TEST, scissor != 0 );
+ }
+
+ if( lighting != -1 ) {
+ VERIFYENAB( GL_LIGHTING, lighting != 0 );
+ if( lighting && colorMaterial == kColorMatDisabled ) {
+ if( matAmbient.x != -1.0f )
+ VERIFYMAT( GL_AMBIENT, matAmbient.GetPtr() );
+ if( matDiffuse.x != -1.0f )
+ VERIFYMAT( GL_DIFFUSE, matDiffuse.GetPtr() );
+ if( matSpecular.x != -1.0f )
+ VERIFYMAT( GL_SPECULAR, matSpecular.GetPtr() );
+ if( matEmissive.x != -1.0f )
+ VERIFYMAT( GL_EMISSION, matEmissive.GetPtr() );
+ if( matShininess != -1.0f )
+ {
+ GLfloat temp;
+ glGetMaterialfv( GL_FRONT, GL_SHININESS, &temp );
+ if( !CompareApproximately(temp, matShininess * 128.0f, kVerifyDelta) )
+ printf_console( "Shininess mismatch: %f != %f\n", (float)temp, (float)matShininess );
+ }
+ }
+ }
+
+ // verify bound/enabled programs
+ for( int type = kShaderVertex; type < kShaderTypeCount; ++type )
+ {
+ ShaderImplType currType = shaderEnabledImpl[type];
+ if( currType == kShaderImplBoth )
+ {
+ GLhandleARB gl = glGetHandleARB( GL_PROGRAM_OBJECT_ARB );
+ if( gl != shaderEnabledID[type] )
+ {
+ VERIFY_PRINT( "GLSL program differs from cache (%d != %d)\n", gl, shaderEnabledID[type] );
+ }
+ }
+ else if( currType == kShaderImplUndefined )
+ {
+ AssertIf( shaderEnabledID[type] != -1 && shaderEnabledID[type] != 0 );
+ if( shaderEnabledID[type] == 0 )
+ {
+ if( type == kShaderVertex )
+ {
+ VERIFYENAB( GL_VERTEX_PROGRAM_ARB, false );
+ }
+ else
+ {
+ VERIFYENAB( GL_FRAGMENT_PROGRAM_ARB, false );
+ }
+ }
+ }
+ else
+ {
+ GLenum gltarget = GetGLShaderImplTarget( currType );
+ VERIFYENAB( gltarget, true );
+ int gl;
+ glGetProgramivARB( gltarget, GL_PROGRAM_BINDING_ARB, &gl );
+ if( gl != shaderEnabledID[type] )
+ {
+ VERIFY_PRINT( "Program #%i differs from cache (%d != %d)\n", type, gl, shaderEnabledID[type] );
+ }
+ }
+ }
+
+ // Verify all texture units
+ for( int i = 0; i < gGraphicsCaps.maxTexUnits; ++i ) {
+ textures[i].Verify(i);
+ }
+}
+
+static const unsigned long kTextureBindingTable[6] = {0, GL_TEXTURE_BINDING_1D, GL_TEXTURE_BINDING_2D, GL_TEXTURE_BINDING_3D, GL_TEXTURE_BINDING_CUBE_MAP_ARB};
+
+void TextureUnitStateGL::Verify( int texUnit )
+{
+ if( gGraphicsCaps.maxTexUnits <= 1 ) // do not crash on no-multi-texture machines
+ return;
+
+ if( texDim == kTexDimUnknown )
+ return;
+
+ glActiveTextureARB(GL_TEXTURE0_ARB + texUnit);
+
+ VERIFYI(GL_TEXTURE_2D, texDim == kTexDim2D);
+ VERIFYI(GL_TEXTURE_CUBE_MAP_ARB, texDim == kTexDimCUBE);
+ if( gGraphicsCaps.has3DTexture ) {
+ VERIFYI(GL_TEXTURE_3D, texDim == kTexDim3D);
+ }
+
+ if( color.x != -1 && color.y != -1 && color.z != -1 && color.w != -1 )
+ VERIFYTE4( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color.GetPtr() );
+
+ if( texDim != kTexDimNone && texID != -1 )
+ VERIFYI( kTextureBindingTable[texDim], texID );
+
+ if( texGen != kTexGenUnknown )
+ {
+ switch( texGen )
+ {
+ case kTexGenDisabled:
+ VERIFYENAB( GL_TEXTURE_GEN_S, false );
+ VERIFYENAB( GL_TEXTURE_GEN_T, false );
+ VERIFYENAB( GL_TEXTURE_GEN_R, false );
+ break;
+ case kTexGenSphereMap:
+ VERIFYTG(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
+ VERIFYTG(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
+ VERIFYENAB( GL_TEXTURE_GEN_S, true );
+ VERIFYENAB( GL_TEXTURE_GEN_T, true );
+ VERIFYENAB( GL_TEXTURE_GEN_R, false );
+ break;
+ case kTexGenObject: {
+ VERIFYTG(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ VERIFYTG(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ VERIFYTG(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ VERIFYENAB( GL_TEXTURE_GEN_S, true );
+ VERIFYENAB( GL_TEXTURE_GEN_T, true );
+ VERIFYENAB( GL_TEXTURE_GEN_R, true );
+ break;
+ }
+ case kTexGenEyeLinear: {
+ VERIFYTG(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+ VERIFYTG(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+ VERIFYTG(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+ VERIFYENAB( GL_TEXTURE_GEN_S, true );
+ VERIFYENAB( GL_TEXTURE_GEN_T, true );
+ VERIFYENAB( GL_TEXTURE_GEN_R, true );
+ break;
+ }
+ case kTexGenCubeReflect:
+ VERIFYTG(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB);
+ VERIFYTG(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB);
+ VERIFYTG(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB);
+ VERIFYENAB( GL_TEXTURE_GEN_S, true );
+ VERIFYENAB( GL_TEXTURE_GEN_T, true );
+ VERIFYENAB( GL_TEXTURE_GEN_R, true );
+ break;
+ case kTexGenCubeNormal:
+ VERIFYTG(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
+ VERIFYTG(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
+ VERIFYTG(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
+ VERIFYENAB( GL_TEXTURE_GEN_S, true );
+ VERIFYENAB( GL_TEXTURE_GEN_T, true );
+ VERIFYENAB( GL_TEXTURE_GEN_R, true );
+ break;
+ }
+ }
+}
+
+
+#endif // GFX_DEVICE_VERIFY_ENABLE
diff --git a/Runtime/GfxDevice/opengl/GfxDeviceGL.h b/Runtime/GfxDevice/opengl/GfxDeviceGL.h
new file mode 100644
index 0000000..59831a2
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/GfxDeviceGL.h
@@ -0,0 +1,191 @@
+#pragma once
+
+#if GFX_DEVICE_VIRTUAL
+
+class GFX_GL_IMPL : public GfxThreadableDevice {
+public:
+ GFX_GL_IMPL();
+ GFX_API ~GFX_GL_IMPL();
+ GFX_API void OnDeviceCreated (bool callingFromRenderThread);
+
+ GFX_API void InvalidateState();
+ #if GFX_DEVICE_VERIFY_ENABLE
+ GFX_API void VerifyState();
+ #endif
+
+ GFX_API void Clear (UInt32 clearFlags, const float color[4], float depth, int stencil);
+ GFX_API void SetUserBackfaceMode( bool enable );
+ GFX_API void SetWireframe(bool wire);
+ GFX_API bool GetWireframe() const;
+
+ GFX_API void SetInvertProjectionMatrix( bool enable );
+ GFX_API bool GetInvertProjectionMatrix() const;
+ #if GFX_USES_VIEWPORT_OFFSET
+ GFX_API void SetViewportOffset( float x, float y );
+ GFX_API void GetViewportOffset( float &x, float &y ) const;
+ #endif
+
+ GFX_API void SetWorldMatrix( const float matrix[16] );
+ GFX_API void SetViewMatrix( const float matrix[16] );
+
+ GFX_API void SetProjectionMatrix (const Matrix4x4f& matrix);
+ GFX_API void GetMatrix( float outMatrix[16] ) const;
+
+ GFX_API const float* GetWorldMatrix() const ;
+ GFX_API const float* GetViewMatrix() const;
+ GFX_API const float* GetProjectionMatrix() const;
+ GFX_API const float* GetDeviceProjectionMatrix() const;
+
+ GFX_API void SetNormalizationBackface( NormalizationMode mode, bool backface );
+ GFX_API void SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial );
+ GFX_API void SetMaterial( const float ambient[4], const float diffuse[4], const float specular[4], const float emissive[4], const float shininess );
+ GFX_API void SetColor( const float color[4] );
+ GFX_API void SetViewport( int x, int y, int width, int height );
+ GFX_API void GetViewport( int* values ) const;
+
+ GFX_API void SetScissorRect( int x, int y, int width, int height );
+ GFX_API void DisableScissor();
+ GFX_API bool IsScissorEnabled() const;
+ GFX_API void GetScissorRect( int values[4] ) const;
+ GFX_API void DiscardContents (RenderSurfaceHandle& rs) {}
+
+ GFX_API GPUSkinningInfo *CreateGPUSkinningInfo() { return NULL; }
+ GFX_API void DeleteGPUSkinningInfo(GPUSkinningInfo *info) { AssertBreak(false); }
+ GFX_API void SkinOnGPU( GPUSkinningInfo * info, bool lastThisFrame ) { AssertBreak(false); }
+ GFX_API void UpdateSkinSourceData(GPUSkinningInfo *info, const void *vertData, const BoneInfluence *skinData, bool dirty) { AssertBreak(false); }
+ GFX_API void UpdateSkinBonePoses(GPUSkinningInfo *info, const int boneCount, const Matrix4x4f* poses) { AssertBreak(false); }
+
+ GFX_API bool IsCombineModeSupported( unsigned int combiner );
+ GFX_API TextureCombinersHandle CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular );
+ GFX_API void DeleteTextureCombiners( TextureCombinersHandle& textureCombiners );
+ GFX_API void SetTextureCombinersThreadable( TextureCombinersHandle textureCombiners, const TexEnvData* texEnvData, const Vector4f* texColors );
+ GFX_API void SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props );
+
+ GFX_API void SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias);
+ GFX_API void SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace );
+ GFX_API void SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16]);
+ GFX_API void SetTextureName ( TextureID texture, const char* name ) { }
+
+ GFX_API void SetShadersThreadable (GpuProgram* programs[kShaderTypeCount], const GpuProgramParameters* params[kShaderTypeCount], UInt8 const * const paramsBuffer[kShaderTypeCount]);
+ GFX_API void CreateShaderParameters( ShaderLab::SubProgram* program, FogMode fogMode );
+ GFX_API bool IsShaderActive( ShaderType type ) const;
+ GFX_API void DestroySubProgram( ShaderLab::SubProgram* subprogram );
+
+ GFX_API void DisableLights( int startLight );
+ GFX_API void SetLight( int light, const GfxVertexLight& data);
+ GFX_API void SetAmbient( const float ambient[4] );
+
+ GFX_API void EnableFog (const GfxFogParams& fog);
+ GFX_API void DisableFog();
+
+ GFX_API VBO* CreateVBO();
+ GFX_API void DeleteVBO( VBO* vbo );
+ GFX_API DynamicVBO& GetDynamicVBO();
+
+ GFX_API RenderSurfaceHandle CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags);
+ GFX_API RenderSurfaceHandle CreateRenderDepthSurface (TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags);
+ GFX_API void DestroyRenderSurface (RenderSurfaceHandle& rs);
+ GFX_API void SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face = kCubeFaceUnknown);
+ GFX_API void ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle);
+ GFX_API void ResolveDepthIntoTexture (RenderSurfaceHandle colorHandle, RenderSurfaceHandle depthHandle);
+ GFX_API RenderSurfaceHandle GetActiveRenderColorSurface (int index);
+ GFX_API RenderSurfaceHandle GetActiveRenderDepthSurface ();
+ GFX_API void SetSurfaceFlags(RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags);
+
+ GFX_API void UploadTexture2D( TextureID texture, TextureDimension dimension, UInt8* srcData, int srcSize, int width, int height, TextureFormat format, int mipCount, UInt32 uploadFlags, int skipMipLevels, TextureUsageMode usageMode, TextureColorSpace colorSpace);
+ GFX_API void UploadTextureSubData2D( TextureID texture, UInt8* srcData, int srcSize, int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace );
+ GFX_API void UploadTextureCube( TextureID texture, UInt8* srcData, int srcSize, int faceDataSize, int size, TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace );
+ GFX_API void UploadTexture3D( TextureID texture, UInt8* srcData, int srcSize, int width, int height, int depth, TextureFormat format, int mipCount, UInt32 uploadFlags );
+ GFX_API void DeleteTexture( TextureID texture );
+
+ GFX_API PresentMode GetPresentMode();
+
+ GFX_API void BeginFrame();
+ GFX_API void EndFrame();
+ GFX_API void PresentFrame();
+ GFX_API bool IsValidState();
+
+ GFX_API void FinishRendering();
+
+ // Immediate mode rendering
+ GFX_API void ImmediateVertex( float x, float y, float z );
+ GFX_API void ImmediateNormal( float x, float y, float z );
+ GFX_API void ImmediateColor( float r, float g, float b, float a );
+ GFX_API void ImmediateTexCoordAll( float x, float y, float z );
+ GFX_API void ImmediateTexCoord( int unit, float x, float y, float z );
+ GFX_API void ImmediateBegin( GfxPrimitiveType type );
+ GFX_API void ImmediateEnd();
+
+ GFX_API bool CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 );
+ GFX_API bool ReadbackImage( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY );
+ GFX_API void GrabIntoRenderTexture( RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height );
+
+ GFX_API void BeforeDrawCall( bool immediateMode );
+
+ GFX_API void SetBlendState(const DeviceBlendState* state, float alphaRef);
+ GFX_API void SetRasterState(const DeviceRasterState* state);
+ GFX_API void SetDepthState(const DeviceDepthState* state);
+ GFX_API void SetStencilState(const DeviceStencilState* state, int stencilRef);
+ GFX_API void SetSRGBWrite (const bool);
+ GFX_API bool GetSRGBWrite ();
+
+ GFX_API DeviceBlendState* CreateBlendState(const GfxBlendState& state);
+ GFX_API DeviceDepthState* CreateDepthState(const GfxDepthState& state);
+ GFX_API DeviceStencilState* CreateStencilState(const GfxStencilState& state);
+ GFX_API DeviceRasterState* CreateRasterState(const GfxRasterState& state);
+
+ GFX_API RenderTextureFormat GetDefaultRTFormat() const;
+ GFX_API RenderTextureFormat GetDefaultHDRRTFormat() const;
+
+ GFX_API bool IsPositionRequiredForTexGen (int texStageIndex) const { return false; }
+ GFX_API bool IsNormalRequiredForTexGen (int texStageIndex) const { return false; }
+ GFX_API bool IsPositionRequiredForTexGen() const { return false; }
+ GFX_API bool IsNormalRequiredForTexGen() const { return false; }
+
+ GFX_API void SetActiveContext (void* ctx);
+ GFX_API void UnbindObjects ();
+
+ GFX_API void* GetNativeTexturePointer(TextureID id);
+
+ #if ENABLE_PROFILER
+ GFX_API GfxTimerQuery* CreateTimerQuery();
+ GFX_API void DeleteTimerQuery(GfxTimerQuery* query);
+ GFX_API void BeginTimerQueries();
+ GFX_API void EndTimerQueries();
+ GFX_API bool TimerQueriesIsActive();
+ #endif
+
+ #if UNITY_EDITOR
+ GFX_API void SetAntiAliasFlag( bool aa );
+ GFX_API void DrawUserPrimitives( GfxPrimitiveType type, int vertexCount, UInt32 vertexChannels, const void* data, int stride );
+ GFX_API int GetCurrentTargetAA() const;
+
+ #if UNITY_WIN
+ GFX_API GfxDeviceWindow* CreateGfxWindow( HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias );
+ #endif
+
+ #endif
+
+ DeviceStateGL& GetState() { return m_State; }
+
+private:
+ DeviceStateGL m_State;
+};
+
+#define STATE this->m_State
+#define GetGLDeviceState(device) device.GetState()
+
+#else
+
+struct GfxDeviceImpl {
+#if UNITY_WII
+ DeviceStateWii state;
+#else
+ DeviceStateGL state;
+#endif
+};
+
+#define STATE impl->state
+#define GetGLDeviceState(device) device.GetImpl()->state
+
+#endif
diff --git a/Runtime/GfxDevice/opengl/GpuProgramsGL.cpp b/Runtime/GfxDevice/opengl/GpuProgramsGL.cpp
new file mode 100644
index 0000000..430f342
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/GpuProgramsGL.cpp
@@ -0,0 +1,562 @@
+#include "UnityPrefix.h"
+#include "GpuProgramsGL.h"
+#include "External/shaderlab/Library/ShaderLabErrors.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Math/Vector4.h"
+#include "UnityGL.h"
+#include "GLAssert.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/shaderlab/Library/program.h"
+#include "Runtime/Utilities/GLSLUtilities.h"
+
+
+#define DEBUG_GLSL_BINDINGS 0
+
+
+unsigned int GetGLShaderImplTarget( ShaderImplType implType ); // GfxDeviceGL.cpp
+void InvalidateActiveShaderStateGL( ShaderType type ); // GfxDeviceGL.cpp
+
+void InvalidateFPParamCacheGL();
+
+
+// --------------------------------------------------------------------------
+// GLSL
+
+// AttributeConversionTable
+const static int kAttribLookupTableSize = 12;
+const static char* s_GLSLAttributes[kAttribLookupTableSize] = {
+ "gl_Vertex", "gl_Color", "gl_Normal",
+ "gl__unused__", // unused
+ "gl_MultiTexCoord0", "gl_MultiTexCoord1", "gl_MultiTexCoord2", "gl_MultiTexCoord3",
+ "gl_MultiTexCoord4", "gl_MultiTexCoord5", "gl_MultiTexCoord6", "gl_MultiTexCoord7"
+};
+const static char* s_UnityAttributes[kAttribLookupTableSize] = {
+ "Vertex", "Color", "Normal",
+ "", // unused
+ "TexCoord", "TexCoord1", "TexCoord2", "TexCoord3", // TODO: all the rest texcoord channels!
+ "TexCoord4", "TexCoord5", "TexCoord6", "TexCoord7"
+};
+
+GlslGpuProgram::GlslGpuProgram( const std::string& source, CreateGpuProgramOutput& output )
+{
+ m_ImplType = kShaderImplBoth;
+ output.SetPerFogModeParamsEnabled(true);
+ m_GpuProgramLevel = kGpuProgramSM2;
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ m_GLSLVertexShader[i] = 0;
+ m_GLSLFragmentShader[i] = 0;
+ m_FogFailed[i] = false;
+ }
+ // Fragment shaders come out as dummy GLSL text. Just ignore them; the real shader was part of
+ // the vertex shader text anyway.
+ if (source.empty())
+ return;
+ if (Create (source, output.CreateShaderErrors()))
+ {
+ GpuProgramParameters& params = output.CreateParams();
+ FillParams (m_Programs[kFogDisabled], params, NULL);
+ FillChannels (output.CreateChannelAssigns());
+// GpuProgramParameters& params = parent.GetParams (kFogDisabled);
+// FillParams (m_Programs[kFogDisabled], params, outNames);
+// FillChannels (parent.GetChannels());
+ if (params.GetTextureParams().size() > gGraphicsCaps.maxTexImageUnits)
+ m_NotSupported = true;
+ }
+ else
+ {
+ m_NotSupported = true;
+ }
+}
+
+GlslGpuProgram::~GlslGpuProgram ()
+{
+ Assert (m_ImplType == kShaderImplBoth);
+
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ if (m_GLSLVertexShader[i]) glDeleteObjectARB (m_GLSLVertexShader[i]);
+ if (m_GLSLFragmentShader[i]) glDeleteObjectARB (m_GLSLFragmentShader[i]);
+ if (m_Programs[i]) glDeleteObjectARB (m_Programs[i]);
+ }
+}
+
+
+
+
+static bool ParseGLSLErrors (GLuint handle , GLSLErrorType errorType, ShaderErrors& outErrors)
+{
+ bool hadErrors = false;
+
+ int status;
+ char log[4096];
+ GLsizei infoLogLength = 0;
+ switch(errorType)
+ {
+ case kErrorCompileVertexShader:
+ case kErrorCompileFragShader:
+ glGetObjectParameterivARB(handle, GL_OBJECT_COMPILE_STATUS_ARB, &status);
+ hadErrors = status == 0;
+ glGetInfoLogARB(handle, sizeof(log), &infoLogLength, log);
+ break;
+ case kErrorLinkProgram:
+ glGetObjectParameterivARB(handle, GL_OBJECT_LINK_STATUS_ARB, &status);
+ hadErrors = status == 0;
+ glGetInfoLogARB(handle, sizeof(log), &infoLogLength, log);
+ break;
+ default:
+ FatalErrorMsg("Unknown error type");
+ break;
+ }
+
+ if(!hadErrors)return false;
+
+ OutputGLSLShaderError(log, errorType, outErrors);
+
+ return hadErrors;
+}
+
+
+static void AddSizedVectorParam (GpuProgramParameters& params, ShaderParamType type, int vectorSize, int uniformNumber, int arraySize, const char* name, char* indexName, PropertyNamesSet* outNames)
+{
+ if (arraySize <= 1)
+ {
+ params.AddVectorParam (uniformNumber, type, vectorSize, name, -1, outNames);
+ }
+ else
+ {
+ for (int j = 0; j < arraySize; ++j) {
+ if (j < 10) {
+ indexName[0] = '0' + j;
+ indexName[1] = 0;
+ } else {
+ indexName[0] = '0' + j/10;
+ indexName[1] = '0' + j%10;
+ indexName[2] = 0;
+ }
+ params.AddVectorParam (uniformNumber+j, type, vectorSize, name, -1, outNames);
+ }
+ }
+}
+
+static void AddSizedMatrixParam (GpuProgramParameters& params, int rows, int cols, int uniformNumber, int arraySize, const char* name, char* indexName, PropertyNamesSet* outNames)
+{
+ if (arraySize <= 1)
+ {
+ params.AddMatrixParam (uniformNumber, name, rows, cols, -1, outNames);
+ }
+ else
+ {
+ for (int j = 0; j < arraySize; ++j) {
+ if (j < 10) {
+ indexName[0] = '0' + j;
+ indexName[1] = 0;
+ } else {
+ indexName[0] = '0' + j/10;
+ indexName[1] = '0' + j%10;
+ indexName[2] = 0;
+ }
+ params.AddMatrixParam (uniformNumber+j, name, rows, cols, -1, outNames);
+ }
+ }
+}
+
+static GLShaderID CompileVertexShader (const std::string& source, ShaderErrors& outErrors)
+{
+ const char* text = source.c_str();
+ GLShaderID vertexId = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
+ glShaderSourceARB (vertexId, 1, &text, NULL);
+ glCompileShaderARB (vertexId);
+
+ if (ParseGLSLErrors( vertexId, kErrorCompileVertexShader, outErrors))
+ {
+ glDeleteObjectARB(vertexId);
+ return 0;
+ }
+
+ return vertexId;
+}
+
+static GLShaderID CompileFragmentShader (const std::string& source, ShaderErrors& outErrors)
+{
+ const char* text = source.c_str();
+ GLShaderID fragId = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB );
+ glShaderSourceARB (fragId, 1, &text, NULL);
+ glCompileShaderARB (fragId);
+
+ if (ParseGLSLErrors( fragId, kErrorCompileFragShader, outErrors))
+ {
+ glDeleteObjectARB(fragId);
+ return 0;
+ }
+
+ return fragId;
+}
+
+
+
+bool GlslGpuProgram::Create (const std::string &shaderString, ShaderErrors& outErrors)
+{
+ if( !gGraphicsCaps.gl.hasGLSL )
+ return false;
+
+ GLAssert(); // Clear any GL errors
+
+ m_Programs[0] = glCreateProgramObjectARB();
+
+ m_ImplType = kShaderImplBoth;
+ m_GpuProgramLevel = kGpuProgramSM2;
+
+ std::string vertexShaderSource;
+ std::string fragmentShaderSource;
+
+
+ vertexShaderSource = "#define VERTEX\n" + shaderString;
+ fragmentShaderSource = "#define FRAGMENT\n" + shaderString;
+
+ if( (m_GLSLVertexShader[0] = CompileVertexShader(vertexShaderSource, outErrors)) == 0 )
+ {
+ return false;
+ }
+
+ if( (m_GLSLFragmentShader[0] = CompileFragmentShader(fragmentShaderSource, outErrors)) == 0 )
+ {
+ return false;
+ }
+
+ glAttachObjectARB(m_Programs[0], m_GLSLVertexShader[0]);
+ glAttachObjectARB(m_Programs[0], m_GLSLFragmentShader[0]);
+ glLinkProgramARB(m_Programs[0]);
+
+ if (ParseGLSLErrors (m_Programs[0], kErrorLinkProgram, outErrors))
+ {
+ return false;
+ }
+
+ m_SourceForFog = shaderString;
+
+ GLAssert();
+ return true;
+}
+
+
+void GlslGpuProgram::FillChannels (ChannelAssigns& channels)
+{
+ if (!m_Programs[0])
+ return;
+
+ // Figure out the attributes (vertex inputs)
+ const int kBufSize = 1024;
+ char name[kBufSize];
+
+ GLAssert();
+
+ GLenum type;
+ int arraySize=0, nameLength=0, activeCount=0;
+ glGetObjectParameterivARB(m_Programs[0], GL_OBJECT_ACTIVE_ATTRIBUTES_ARB, &activeCount);
+
+ for(int i = 0; i < activeCount; i++)
+ {
+ glGetActiveAttribARB(m_Programs[0], i, kBufSize, &nameLength, &arraySize, &type, name);
+ int attribNumber = 0;
+ std::string stringName( name );
+ // Find number for gl_ attribs in conversion table
+ if (!strncmp (name, "gl_", 3)) {
+ for(int j = 0; j < kAttribLookupTableSize; j++)
+ {
+ if(!strncmp(s_GLSLAttributes[j], stringName.c_str(), strlen(s_GLSLAttributes[j])))
+ {
+ attribNumber = j+1;
+ stringName = s_UnityAttributes[j];
+ break;
+ }
+ }
+ if( attribNumber == 0 )
+ ErrorString( "Unrecognized vertex attribute: " + stringName ); // TODO: SL error
+ }
+ else
+ {
+ // Check in names for non gl_ attribs. if not gl_ name get number:
+ attribNumber = glGetAttribLocationARB(m_Programs[0], name) + kVertexCompAttrib0;
+ }
+
+ ShaderChannel shaderChannel = GetShaderChannelFromName(stringName);
+ if( shaderChannel != kShaderChannelNone )
+ {
+ channels.Bind (shaderChannel, (VertexComponent)attribNumber);
+ }
+ }
+
+ GLAssert();
+}
+
+void GlslGpuProgram::FillParams (GLShaderID programID, GpuProgramParameters& params, PropertyNamesSet* outNames)
+{
+ if (!programID)
+ return;
+
+ // Figure out the attributes (vertex inputs)
+ const int kBufSize = 1024;
+ char name[kBufSize];
+
+ GLAssert();
+
+ GLenum type;
+ int arraySize=0, nameLength=0, activeCount=0;
+
+ // Figure out the uniforms
+ glGetObjectParameterivARB (programID, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &activeCount);
+ for(int i=0; i < activeCount; i++)
+ {
+ glGetActiveUniformARB (programID, i, kBufSize, &nameLength, &arraySize, &type, name);
+ if (!strncmp (name, "gl_", 3))
+ continue;
+
+ if (!strcmp (name, "_unity_FogParams") || !strcmp(name, "_unity_FogColor"))
+ continue;
+
+ int uniformNumber = glGetUniformLocationARB (programID, name);
+ char* indexName = name;
+
+ bool isElemZero = false;
+ bool isArray = IsShaderParameterArray(name, nameLength, arraySize, &isElemZero);
+ if (isArray)
+ {
+ // for array parameters, transform name a bit: Foo[0] becomes Foo0
+ if (arraySize >= 100) {
+ ErrorString( "GLSL: array sizes larger than 99 not supported" ); // TODO: SL error
+ arraySize = 99;
+ }
+ if (isElemZero)
+ {
+ indexName = name+nameLength-3;
+ indexName[0] = '0';
+ indexName[1] = 0;
+ }
+ else
+ {
+ indexName = name+nameLength;
+ }
+ }
+
+ if (type == GL_FLOAT)
+ AddSizedVectorParam (params, kShaderParamFloat, 1, uniformNumber, arraySize, name, indexName, outNames);
+ else if (type == GL_FLOAT_VEC2_ARB)
+ AddSizedVectorParam (params, kShaderParamFloat, 2, uniformNumber, arraySize, name, indexName, outNames);
+ else if (type == GL_FLOAT_VEC3_ARB)
+ AddSizedVectorParam (params, kShaderParamFloat, 3, uniformNumber, arraySize, name, indexName, outNames);
+ else if (type == GL_FLOAT_VEC4_ARB)
+ AddSizedVectorParam (params, kShaderParamFloat, 4, uniformNumber, arraySize, name, indexName, outNames);
+ else if (type == GL_INT)
+ AddSizedVectorParam (params, kShaderParamInt, 1, uniformNumber, arraySize, name, indexName, outNames);
+ else if (type == GL_INT_VEC2_ARB)
+ AddSizedVectorParam (params, kShaderParamInt, 2, uniformNumber, arraySize, name, indexName, outNames);
+ else if (type == GL_INT_VEC3_ARB)
+ AddSizedVectorParam (params, kShaderParamInt, 3, uniformNumber, arraySize, name, indexName, outNames);
+ else if (type == GL_INT_VEC4_ARB)
+ AddSizedVectorParam (params, kShaderParamInt, 4, uniformNumber, arraySize, name, indexName, outNames);
+ else if (type == GL_BOOL)
+ AddSizedVectorParam (params, kShaderParamBool, 1, uniformNumber, arraySize, name, indexName, outNames);
+ else if (type == GL_BOOL_VEC2_ARB)
+ AddSizedVectorParam (params, kShaderParamBool, 2, uniformNumber, arraySize, name, indexName, outNames);
+ else if (type == GL_BOOL_VEC3_ARB)
+ AddSizedVectorParam (params, kShaderParamBool, 3, uniformNumber, arraySize, name, indexName, outNames);
+ else if (type == GL_BOOL_VEC4_ARB)
+ AddSizedVectorParam (params, kShaderParamBool, 4, uniformNumber, arraySize, name, indexName, outNames);
+ else if (type == GL_FLOAT_MAT4_ARB)
+ AddSizedMatrixParam (params, 4, 4, uniformNumber, arraySize, name, indexName, outNames);
+ // For all the textures, we could just statically bind the uniform to the sequential texture index,
+ // and save all the glUniform1i calls later. However one gfx test on Linux is failing with the change,
+ // and I can't figure out why. So instead I'll pack the uniform number & the index :(
+ else if (type == GL_SAMPLER_2D_ARB || type == GL_SAMPLER_2D_RECT_ARB || type == GL_SAMPLER_2D_SHADOW_ARB)
+ {
+ const UInt32 texIndex = params.GetTextureParams().size();
+ const UInt32 packedIndex = (texIndex << 24) | (uniformNumber & 0xFFFFFF);
+ params.AddTextureParam (packedIndex, -1, name, kTexDim2D, outNames);
+ }
+ else if (type == GL_SAMPLER_CUBE_ARB)
+ {
+ const UInt32 texIndex = params.GetTextureParams().size();
+ const UInt32 packedIndex = (texIndex << 24) | (uniformNumber & 0xFFFFFF);
+ params.AddTextureParam (packedIndex, -1, name, kTexDimCUBE, outNames);
+ }
+ else if (type == GL_SAMPLER_3D_ARB)
+ {
+ const UInt32 texIndex = params.GetTextureParams().size();
+ const UInt32 packedIndex = (texIndex << 24) | (uniformNumber & 0xFFFFFF);
+ params.AddTextureParam (packedIndex, -1, name, kTexDim3D, outNames);
+ }
+ else {
+ AssertString( "Unrecognized GLSL uniform type" );
+ }
+ }
+
+ GLAssert();
+}
+
+
+void GlslGpuProgram::ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer)
+{
+ #if DEBUG_GLSL_BINDINGS
+ printf_console("GLSL: apply program id=%i\n", m_ShaderID);
+ #endif
+
+ // Fog parameters if needed
+ const GfxFogParams& fog = GetRealGfxDevice().GetFogParams();
+ if (fog.mode > kFogDisabled && m_Programs[fog.mode])
+ {
+ OGL_CALL(glUniform4fvARB (m_FogColorIndex[fog.mode], 1, fog.color.GetPtr()));
+ float params[4];
+ params[0] = fog.density * 1.2011224087f ; // density / sqrt(ln(2))
+ params[1] = fog.density * 1.4426950408f; // density / ln(2)
+ if (fog.mode == kFogLinear)
+ {
+ float diff = fog.end - fog.start;
+ float invDiff = Abs(diff) > 0.0001f ? 1.0f/diff : 0.0f;
+ params[2] = -invDiff;
+ params[3] = fog.end * invDiff;
+ }
+ else
+ {
+ params[2] = 0.0f;
+ params[3] = 0.0f;
+ }
+ OGL_CALL(glUniform4fvARB (m_FogParamsIndex[fog.mode], 1, params));
+ }
+
+ const GpuProgramParameters::ValueParameterArray& valueParams = params.GetValueParams();
+ GpuProgramParameters::ValueParameterArray::const_iterator valueParamsEnd = valueParams.end();
+ for( GpuProgramParameters::ValueParameterArray::const_iterator i = valueParams.begin(); i != valueParamsEnd; ++i )
+ {
+ if(i->m_RowCount == 1 && i->m_ArraySize == 1)
+ {
+ // Apply float/vector parameters
+ const Vector4f& val = *reinterpret_cast<const Vector4f*>(buffer);
+ if (i->m_Type == kShaderParamFloat)
+ {
+ if (i->m_ColCount == 1)
+ OGL_CALL(glUniform1fvARB (i->m_Index, 1, val.GetPtr()));
+ else if(i->m_ColCount == 2)
+ OGL_CALL(glUniform2fvARB (i->m_Index, 1, val.GetPtr()));
+ else if(i->m_ColCount == 3)
+ OGL_CALL(glUniform3fvARB (i->m_Index, 1, val.GetPtr()));
+ else if(i->m_ColCount == 4)
+ OGL_CALL(glUniform4fvARB (i->m_Index, 1, val.GetPtr()));
+ }
+ else
+ {
+ // In theory Uniform*f can also be used to load bool uniforms, in practice
+ // some drivers don't like that. So load both integers and bools via *i functions.
+ int ival[4] = {val.x, val.y, val.z, val.w};
+ if (i->m_ColCount == 1)
+ OGL_CALL(glUniform1ivARB (i->m_Index, 1, ival));
+ else if(i->m_ColCount == 2)
+ OGL_CALL(glUniform2ivARB (i->m_Index, 1, ival));
+ else if(i->m_ColCount == 3)
+ OGL_CALL(glUniform3ivARB (i->m_Index, 1, ival));
+ else if(i->m_ColCount == 4)
+ OGL_CALL(glUniform4ivARB (i->m_Index, 1, ival));
+ }
+ #if DEBUG_GLSL_BINDINGS
+ printf_console(" vector %i dim=%i\n", i->m_Index, i->m_Dim );
+ #endif
+ buffer += sizeof(Vector4f);
+ }
+ else
+ {
+ // Apply matrix parameters
+ DebugAssert(i->m_RowCount == 4);
+ int size = *reinterpret_cast<const int*> (buffer); buffer += sizeof(int);
+ DebugAssert (size == 16);
+ const Matrix4x4f* mat = reinterpret_cast<const Matrix4x4f*>(buffer);
+ const float *ptr = mat->GetPtr ();
+ OGL_CALL(glUniformMatrix4fvARB (i->m_Index, 1, false, ptr));
+ #if DEBUG_GLSL_BINDINGS
+ printf_console(" matrix %i (%s)\n", i->m_Index, i->m_Name.GetName() );
+ #endif
+ buffer += size * sizeof(float);
+ }
+ }
+
+ // Apply textures
+ const GpuProgramParameters::TextureParameterList& textureParams = params.GetTextureParams();
+ GpuProgramParameters::TextureParameterList::const_iterator textureParamsEnd = textureParams.end();
+ for( GpuProgramParameters::TextureParameterList::const_iterator i = textureParams.begin(); i != textureParamsEnd; ++i )
+ {
+ const GpuProgramParameters::TextureParameter& t = *i;
+ const TexEnvData* texdata = reinterpret_cast<const TexEnvData*>(buffer);
+ int texIndex = int(UInt32(t.m_Index) >> 24);
+ Assert(texIndex < gGraphicsCaps.maxTexImageUnits);
+ UInt32 uniformIndex = t.m_Index & 0xFFFFFF;
+ OGL_CALL(glUniform1ivARB( uniformIndex, 1, &texIndex ));
+ #if DEBUG_GLSL_BINDINGS
+ printf_console(" sampler %i to unit %i (%s)\n", uniformIndex, texIndex, t.m_Name.GetName() );
+ #endif
+ ApplyTexEnvData (texIndex, texIndex, *texdata);
+ buffer += sizeof(*texdata);
+ }
+
+ GLAssert();
+}
+
+
+GLShaderID GlslGpuProgram::GetGLProgram (FogMode fog, GpuProgramParameters& outParams)
+{
+ int index = 0;
+ if (fog > kFogDisabled && !m_SourceForFog.empty() && !m_FogFailed[fog])
+ {
+ index = fog;
+ Assert (index >= 0 && index < kFogModeCount);
+
+ // create patched fog program if needed
+ if (!m_Programs[index])
+ {
+ std::string src = m_SourceForFog;
+ if (PatchShaderFogGLSL (src, fog))
+ {
+ // create program, fragment shader, link
+ m_Programs[index] = glCreateProgramObjectARB();
+ ShaderErrors errors;
+ if(
+ (m_GLSLVertexShader[index] = CompileVertexShader("#define VERTEX\n" + src, errors)) != 0 &&
+ (m_GLSLFragmentShader[index] = CompileFragmentShader("#define FRAGMENT\n" + src, errors)) != 0 )
+ {
+ glAttachObjectARB(m_Programs[index], m_GLSLVertexShader[index]);
+ glAttachObjectARB(m_Programs[index], m_GLSLFragmentShader[index]);
+ glLinkProgramARB(m_Programs[index]);
+ if (!ParseGLSLErrors (m_Programs[index], kErrorLinkProgram, errors))
+ {
+ FillParams (m_Programs[index], outParams, NULL);
+ m_FogParamsIndex[index] = glGetUniformLocationARB(m_Programs[index], "_unity_FogParams");
+ m_FogColorIndex[index] = glGetUniformLocationARB(m_Programs[index], "_unity_FogColor");
+ }
+ else
+ {
+ glDeleteObjectARB(m_GLSLVertexShader[index]); m_GLSLVertexShader[index] = 0;
+ glDeleteObjectARB(m_GLSLFragmentShader[index]); m_GLSLFragmentShader[index] = 0;
+ glDeleteObjectARB(m_Programs[index]); m_Programs[index] = 0;
+ m_FogFailed[index] = true;
+ index = 0;
+ }
+ }
+ else
+ {
+ glDeleteObjectARB(m_GLSLVertexShader[index]); m_GLSLVertexShader[index] = 0;
+ glDeleteObjectARB(m_GLSLFragmentShader[index]); m_GLSLFragmentShader[index] = 0;
+ glDeleteObjectARB(m_Programs[index]); m_Programs[index] = 0;
+ m_FogFailed[index] = true;
+ index = 0;
+ }
+ }
+ else
+ {
+ m_FogFailed[index] = true;
+ index = 0;
+ }
+ }
+ }
+ return m_Programs[index];
+}
diff --git a/Runtime/GfxDevice/opengl/GpuProgramsGL.h b/Runtime/GfxDevice/opengl/GpuProgramsGL.h
new file mode 100644
index 0000000..f9649dd
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/GpuProgramsGL.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "Runtime/GfxDevice/GpuProgram.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+
+class ShaderErrors;
+
+class GlslGpuProgram : public GpuProgramGL {
+public:
+ GlslGpuProgram( const std::string& source, CreateGpuProgramOutput& output );
+ virtual ~GlslGpuProgram();
+
+ virtual void ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer);
+
+ GLShaderID GetGLProgram (FogMode fog, GpuProgramParameters& outParams);
+
+private:
+ bool Create( const std::string& source, ShaderErrors& outErrors );
+ static void FillParams (GLShaderID programID, GpuProgramParameters& params, PropertyNamesSet* outNames);
+ void FillChannels (ChannelAssigns& channels);
+
+private:
+ GLShaderID m_GLSLVertexShader[kFogModeCount];
+ GLShaderID m_GLSLFragmentShader[kFogModeCount];
+ int m_FogColorIndex[kFogModeCount];
+ int m_FogParamsIndex[kFogModeCount];
+ bool m_FogFailed[kFogModeCount];
+};
diff --git a/Runtime/GfxDevice/opengl/GraphicsCapsGL.cpp b/Runtime/GfxDevice/opengl/GraphicsCapsGL.cpp
new file mode 100644
index 0000000..407b3c3
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/GraphicsCapsGL.cpp
@@ -0,0 +1,1176 @@
+#include "UnityPrefix.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "UnityGL.h"
+#include <string>
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Shaders/ShaderKeywords.h"
+#include "GLContext.h"
+
+#define GL_RT_COMMON_GL 1
+#include "Runtime/GfxDevice/GLRTCommon.h"
+#undef GL_RT_COMMON_GL
+
+
+#if UNITY_OSX
+#include <OpenGL/OpenGL.h>
+#include "GLContext.h"
+#endif
+
+#if UNITY_LINUX
+#include <dirent.h>
+#include <GL/glx.h>
+#endif
+
+using namespace std;
+
+
+bool QueryExtensionGL (const char* ext)
+{
+ static const char* extensions = NULL;
+ if (!extensions)
+ extensions = (const char*)glGetString(GL_EXTENSIONS);
+ return FindGLExtension (extensions, ext);
+}
+
+#if UNITY_LINUX
+static bool QueryExtensionGLX (const char* ext)
+{
+ static const char* glxExtensions = NULL;
+ if (!glxExtensions) {
+ Display *display = reinterpret_cast<Display*> (GetScreenManager ().GetDisplay ());
+ glxExtensions = glXQueryExtensionsString (display, 0);
+ printf_console ("GLX Extensions: %s\n", glxExtensions);
+ }
+ return FindGLExtension (glxExtensions, ext);
+}
+#endif
+
+
+#if UNITY_WIN
+
+#include "PlatformDependent/Win/wglext.h"
+#include "PlatformDependent/Win/WinDriverUtils.h"
+
+static bool WglQueryExtension( const char* extension )
+{
+ static bool gotExtensions = false;
+ static const char* extensions = NULL;
+
+ if( !gotExtensions )
+ {
+ gotExtensions = true;
+
+ if (wglGetExtensionsStringARB)
+ extensions = wglGetExtensionsStringARB( wglGetCurrentDC() );
+ else if (wglGetExtensionsStringEXT)
+ extensions = wglGetExtensionsStringEXT();
+
+ // always print WGL extensions
+ if( extensions )
+ printf_console( " WGL extensions: %s\n", extensions );
+ else
+ printf_console( " WGL extensions: none!\n" );
+ }
+
+ if( extensions == NULL || extensions[0] == '\0' )
+ return false;
+
+ int extLen = strlen( extension );
+ const char* start;
+ const char* where, *terminator;
+ start = extensions;
+ for(;;) {
+ where = strstr(start, extension);
+ if(!where) break;
+ terminator = where + extLen;
+ if( where == start || *(where - 1) == ' ' ) {
+ if(*terminator == ' ' || *terminator == '\0')
+ return true;
+ }
+ start = terminator;
+ }
+
+ return false;
+}
+
+#endif
+
+
+// Checks if all given formats are supported. Terminate the array with -1.
+static bool CheckCompressedFormatsSupport( const GLint* formats )
+{
+ if( !QueryExtensionGL( "GL_EXT_texture_compression_s3tc" ) )
+ return false;
+
+ GLint numSupportedFormats = 0;
+ glGetIntegerv( GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB, &numSupportedFormats );
+ GLint* supportedFormats = new GLint[numSupportedFormats+1];
+ glGetIntegerv( GL_COMPRESSED_TEXTURE_FORMATS_ARB, supportedFormats );
+ GLint* supportedFormatsEnd = supportedFormats + numSupportedFormats;
+
+ bool result = true;
+
+ // for each given format, try to find it in the supported formats list
+ while( *formats != -1 )
+ {
+ if( std::find( supportedFormats, supportedFormatsEnd, *formats ) == supportedFormatsEnd )
+ {
+ result = false;
+ break;
+ }
+ ++formats;
+ }
+
+ delete[] supportedFormats;
+
+ return result;
+}
+
+static bool IsRendererAppleFX5200( const std::string& renderer )
+{
+ return
+ (renderer.find("NVIDIA NV34MAP OpenGL Engine") != string::npos) ||
+ (renderer.find("NVIDIA GeForce FX 5200") != string::npos);
+}
+
+
+#if UNITY_WIN
+static void GetVideoCardIDsWin (int& outVendorID, int& outDeviceID)
+{
+ outVendorID = 0;
+ outDeviceID = 0;
+
+ DISPLAY_DEVICEA dev;
+ dev.cb = sizeof(dev);
+ int i = 0;
+ while (EnumDisplayDevicesA(NULL, i, &dev, 0))
+ {
+ if (dev.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
+ {
+ const char* sVendorID = strstr (dev.DeviceID, "VEN_");
+ if (sVendorID)
+ sscanf(sVendorID, "VEN_%x", &outVendorID);
+ const char *sDeviceID = strstr (dev.DeviceID, "DEV_");
+ if (sDeviceID)
+ sscanf(sDeviceID, "DEV_%x", &outDeviceID);
+ return;
+ }
+ ++i;
+ }
+}
+#endif
+
+#if UNITY_OSX
+static UInt32 GetOSXVersion()
+{
+ UInt32 response = 0;
+
+ Gestalt(gestaltSystemVersion, (MacSInt32*) &response);
+ return response;
+}
+
+static CFTypeRef SearchPortForProperty(io_registry_entry_t dspPort, CFStringRef propertyName)
+{
+ return IORegistryEntrySearchCFProperty(dspPort,
+ kIOServicePlane,
+ propertyName,
+ kCFAllocatorDefault,
+ kIORegistryIterateRecursively | kIORegistryIterateParents);
+}
+
+static UInt32 IntValueOfCFData(CFDataRef d)
+{
+ UInt32 value = 0;
+ if (d) {
+ const UInt32 *vp = reinterpret_cast<const UInt32*>(CFDataGetBytePtr(d));
+ if (vp != NULL)
+ value = *vp;
+ }
+ return value;
+}
+
+static void GetVideoCardIDsOSX (int& outVendorID, int& outDeviceID)
+{
+ CFStringRef strVendorID = CFStringCreateWithCString (NULL, "vendor-id", kCFStringEncodingASCII);
+ CFStringRef strDeviceID = CFStringCreateWithCString (NULL, "device-id", kCFStringEncodingASCII);
+
+ CGDirectDisplayID displayID = kCGDirectMainDisplay;
+ io_registry_entry_t dspPort = CGDisplayIOServicePort (displayID);
+ CFTypeRef vendorIDRef = SearchPortForProperty (dspPort, strVendorID);
+ CFTypeRef deviceIDRef = SearchPortForProperty (dspPort, strDeviceID);
+
+ CFRelease (strDeviceID);
+ CFRelease (strVendorID);
+
+ outVendorID = IntValueOfCFData ((CFDataRef)vendorIDRef);
+ outDeviceID = IntValueOfCFData ((CFDataRef)deviceIDRef);
+
+ if (vendorIDRef) CFRelease (vendorIDRef);
+ if (deviceIDRef) CFRelease (deviceIDRef);
+}
+
+static float GetVideoMemoryMBOSX()
+{
+ float vramMB = 0.0f;
+
+ // Adapted from "Custom Cocoa OpenGL" sample.
+ // We used code from VideoHardwareInfo sample before, but that reports wrong VRAM
+ // amount in some cases (e.g. Intel GMA 950 reports 256MB, but it has 64MB; GeForce 9600 reports
+ // 256MB, but it has 512MB).
+
+ const int kMaxDisplays = 8;
+ CGDirectDisplayID displays[kMaxDisplays];
+ CGDisplayCount displayCount;
+ CGGetActiveDisplayList( kMaxDisplays, displays, &displayCount );
+ CGOpenGLDisplayMask openGLDisplayMask = CGDisplayIDToOpenGLDisplayMask(displays[0]);
+
+ CGLRendererInfoObj info;
+ CGLint numRenderers = 0;
+ CGLError err = CGLQueryRendererInfo( openGLDisplayMask, &info, &numRenderers );
+ if( 0 == err ) {
+ CGLDescribeRenderer( info, 0, kCGLRPRendererCount, &numRenderers );
+ for( int j = 0; j < numRenderers; ++j )
+ {
+ CGLint rv = 0;
+ // find accelerated renderer (assume only one)
+ CGLDescribeRenderer( info, j, kCGLRPAccelerated, &rv );
+ if( true == rv )
+ {
+ CGLint vram = 0;
+ CGLDescribeRenderer( info, j, kCGLRPVideoMemory, &vram );
+ vramMB = double(vram)/1024.0/1024.0;
+ break;
+ }
+ }
+ CGLDestroyRendererInfo (info);
+ }
+
+ // safe guards
+ if( vramMB == 0.0f ) // if some device reports zero (integrated cards?), treat as 64MB
+ vramMB = 64.0f;
+ return vramMB;
+}
+#endif
+
+#if UNITY_LINUX
+typedef struct {
+ unsigned int vendor;
+ unsigned int device;
+ unsigned long vramSize;
+} card_info_t;
+
+static void GetPrimaryGraphicsCardInfo(card_info_t *info)
+{
+ struct dirent *pDir;
+
+ // Open the /sys/devices directory and iterate over for PCI busses
+ // It appears that most modern graphics cards will be mapped as a PCI device.
+ // That includes integrated chipsets and AGP cards as well. AGP cards get connected
+ // through an AGP to PCI bus link
+ const char *dirPath = "/sys/devices/";
+ const char *PCI_BASE_CLASS_DISPLAY = "0x03";
+
+ DIR *devicesDir = opendir(dirPath);
+ while ((pDir=readdir(devicesDir)) != NULL)
+ {
+ if(strncmp(pDir->d_name, "pci", 3) == 0)
+ {
+ char busID[PATH_MAX];
+ memcpy(busID, pDir->d_name+3, strlen(pDir->d_name));
+
+ // Now iterate over each directory for this bus
+ struct dirent *pPCIDir;
+ char domainPath[PATH_MAX];
+ strcpy(domainPath, dirPath);
+ strcat(domainPath, pDir->d_name);
+ DIR *pciDeviceDir = opendir(domainPath);
+ while ((pPCIDir=readdir(pciDeviceDir)) != NULL)
+ {
+ if(strstr(pPCIDir->d_name, busID) != NULL)
+ {
+ char filePath[PATH_MAX];
+ char line[512];
+ char pciSubDevPath[PATH_MAX];
+ char domain[PATH_MAX];
+ struct dirent *pSubDevDir;
+
+ // Get a copy of the PCI domain
+ memcpy(domain, pPCIDir->d_name, 4);
+
+ // Ok, now open and read the first line of the class file.
+ // We're only concerned with display class devices
+ sprintf(filePath, "%s/%s", domainPath, pPCIDir->d_name);
+
+ FILE *classFile = fopen(filePath, "r");
+ fgets(line, sizeof(line), classFile);
+ fclose(classFile);
+
+ // Now iterate over each directory for this device
+
+ sprintf(pciSubDevPath, "%s/%s", domainPath, pPCIDir->d_name);
+ DIR *pciSubDeviceDir = opendir(pciSubDevPath);
+ while ((pSubDevDir=readdir(pciSubDeviceDir)) != NULL)
+ {
+ char devPath[PATH_MAX];
+
+ // Ok, now open and read the first line of the class file.
+ // We're only concerned with display class devices
+ sprintf(filePath, "%s/%s", pciSubDevPath, pSubDevDir->d_name);
+ strcpy(devPath, filePath);
+ strcat(filePath, "/class");
+
+ FILE *classFile = fopen(filePath, "r");
+ if(classFile)
+ {
+ fgets(line, sizeof(line), classFile);
+ fclose(classFile);
+
+ if(strstr(line, PCI_BASE_CLASS_DISPLAY) != NULL)
+ {
+ // Device ID is in the device file
+ strcpy(filePath, devPath);
+ strcat(filePath, "/device");
+ FILE *f = fopen(filePath, "r");
+ if(f)
+ {
+ fgets(line, sizeof(line), f);
+ fclose(f);
+ info->device = strtol(line, NULL, 16);
+ }
+
+ // Vendor ID is in the vendor file
+ strcpy(filePath, devPath);
+ strcat(filePath, "/vendor");
+ f = fopen(filePath, "r");
+ if(f)
+ {
+ fgets(line, sizeof(line), f);
+ fclose(f);
+ info->vendor = strtol(line, NULL, 16);
+ }
+
+ // Now for the tricky part. VRAM size.
+ // Each line in the resource file represents the memory range of a PCI resource
+ // Start address, end address and flags. Separated by spaces. VRAM is most often
+ // the only prefetchable resource so we can identify it by checking for that flag.
+ strcpy(filePath, devPath);
+ strcat(filePath, "/resource");
+ f = fopen(filePath, "r");
+ if(f)
+ {
+ // Loop through each resource
+ while(fgets(line, sizeof(line), f))
+ {
+ unsigned long low, high, flags;
+ char *next = line;
+ low = strtoull(next, &next, 16);
+ high = strtoull(next, &next, 16);
+ flags = strtoull(next, &next, 16);
+ // Check for the prefetchable flag
+ if((flags & 0x08))
+ {
+ info->vramSize += (high-low)+1;
+ }
+ }
+ fclose(f);
+ }
+ }
+ }
+ }
+ }
+ }
+ closedir(pciDeviceDir);
+ }
+ }
+ closedir(devicesDir);
+}
+
+static card_info_t gCardInfo;
+static void InitCardInfoLinux(){
+ gCardInfo.device = 0;
+ gCardInfo.vendor = 0;
+ gCardInfo.vramSize = 0;
+ GetPrimaryGraphicsCardInfo(&gCardInfo);
+
+ if(gCardInfo.vramSize < (64*1024*1024)){
+ // Default to 64MB
+ gCardInfo.vramSize = 64*1024*1024;
+ }
+}
+
+static void GetVideoCardIDsLinux (int& outVendorID, int& outDeviceID)
+{
+ outVendorID = gCardInfo.vendor;
+ outDeviceID = gCardInfo.device;
+}
+
+static float GetVideoMemoryMBLinux()
+{
+ // Nvidia
+ if (QueryExtensionGL ("GL_NVX_gpu_memory_info")) {
+ GLint vram = 0;
+ glGetIntegerv (GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &vram);
+ printf_console ("GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX: %ld\n", vram);
+ GLAssert ();
+ return vram / 1024.0f;
+ }
+ // ATI
+ if (QueryExtensionGL ("GL_ATI_meminfo")) {
+ GLint vram[4] = {0,0,0,0};
+ glGetIntegerv (VBO_FREE_MEMORY_ATI, vram);
+ printf_console ("VBO_FREE_MEMORY_ATI: %ld\n", vram[0]);
+ glGetIntegerv (RENDERBUFFER_FREE_MEMORY_ATI, vram);
+ printf_console ("RENDERBUFFER_FREE_MEMORY_ATI: %ld\n", vram[0]);
+ glGetIntegerv (TEXTURE_FREE_MEMORY_ATI, vram);
+ printf_console ("TEXTURE_FREE_MEMORY_ATI: %ld\n", vram[0]);
+ GLAssert ();
+ return vram[0] / 1024.0f;
+ }
+
+ // Fallback to pci aperture size
+ return gCardInfo.vramSize / 1048576.0f;
+}
+#endif
+
+
+void GraphicsCaps::InitGL()
+{
+ // Figure out major & minor version numbers.
+ // Some drivers do not report this according to the spec, and sometimes mix in some letters and whatnot (e.g. "1.08a" - SIS, I'm looking at you).
+ // So try to parse one full number, a period and one digit, and stop parsing on anything that we can' t recognize.
+ const char *versionStr = (const char *)glGetString (GL_VERSION);
+ driverVersionString = versionStr;
+ int major = 0, minor = 0;
+ bool gotMajor = false;
+ while( versionStr && *versionStr ) {
+ char c = *versionStr;
+ if( isdigit(c) )
+ {
+ if( !gotMajor )
+ {
+ major = major * 10 + (c-'0');
+ }
+ else
+ {
+ minor = c-'0';
+ break; // only take first digit of minor version
+ }
+ }
+ else if( c == '.' )
+ {
+ gotMajor = true;
+ }
+ else
+ {
+ // unknown symbol, stop parsing
+ break;
+ }
+ ++versionStr;
+ }
+
+ // Important: only take first digit of minor version.
+ // Some cards have version like 1.12.
+ int version = major * 10 + minor % 10;
+
+ rendererString = (const char*)glGetString( GL_RENDERER );
+ vendorString = (const char*)glGetString( GL_VENDOR );
+
+ // some drivers are absolutely horrible!
+ AdjustBuggyVersionGL( version, major, minor );
+
+ gl.glVersion = version;
+
+ char buffer[200];
+ snprintf( buffer, 200, "OpenGL %d.%d [%s]", major, minor, driverVersionString.c_str() );
+ fixedVersionString = buffer;
+
+ #if UNITY_WIN
+ GetVideoCardIDsWin (vendorID, rendererID);
+ #elif UNITY_OSX
+ GetVideoCardIDsOSX (vendorID, rendererID);
+ #elif UNITY_LINUX
+ InitCardInfoLinux();
+ GetVideoCardIDsLinux(vendorID, rendererID);
+ #else
+ #error "Unknown platform"
+ #endif
+
+ #if UNITY_OSX
+ videoMemoryMB = GetVideoMemoryMBOSX();
+ #elif UNITY_LINUX
+ videoMemoryMB = GetVideoMemoryMBLinux();
+ #endif
+
+ driverLibraryString = "n/a";
+
+ // On windows, we always output GL info. There is so much variety that
+ // it always helps!
+ printf_console( "OpenGL:\n" );
+ printf_console( " Version: %s\n", fixedVersionString.c_str() );
+ printf_console( " Renderer: %s\n", rendererString.c_str() );
+ printf_console( " Vendor: %s\n", vendorString.c_str() );
+ #if UNITY_WIN
+ windriverutils::VersionInfo driverVersion;
+ std::string driverName;
+ if( windriverutils::GetDisplayDriverInfoRegistry( NULL, &driverName, driverVersion ) )
+ {
+ driverLibraryString = Format("%s %i.%i.%i.%i",
+ driverName.c_str(),
+ driverVersion.hipart >> 16, driverVersion.hipart & 0xFFFF,
+ driverVersion.lopart >> 16, driverVersion.lopart & 0xFFFF );
+ printf_console( " Driver: %s\n", driverLibraryString.c_str() );
+ }
+ const char* vramMethod = "";
+ int vramMB = windriverutils::GetVideoMemorySizeMB (MonitorFromWindow(GetDesktopWindow(),MONITOR_DEFAULTTOPRIMARY), &vramMethod);
+ videoMemoryMB = vramMB;
+ printf_console( " VRAM: %i MB (via %s)\n", (int)videoMemoryMB, vramMethod );
+ #else
+ driverLibraryString = driverVersionString;
+ printf_console( " VRAM: %i MB\n", (int)videoMemoryMB );
+ #endif
+
+ printf_console( " Extensions: %s\n", glGetString(GL_EXTENSIONS) );
+
+#if UNITY_OSX
+ maxVSyncInterval = 4;
+#elif UNITY_LINUX
+ if (QueryExtensionGLX ("GLX_SGI_swap_control"))
+ maxVSyncInterval = 4;
+ printf_console ("Setting maxVSyncInterval to %d\n", maxVSyncInterval);
+#else
+// TODO: What should max vsync interval be?
+#endif
+ has16BitFloatVertex = QueryExtensionGL( "GL_ARB_half_float_vertex" );
+ needsToSwizzleVertexColors = false;
+
+ glGetIntegerv( GL_MAX_LIGHTS, &maxLights );
+ maxLights = clamp<int>( maxLights, 0, 8 );
+
+ glGetIntegerv( GL_MAX_TEXTURE_SIZE, &maxTextureSize );
+ glGetIntegerv( GL_MAX_CUBE_MAP_TEXTURE_SIZE, &maxCubeMapSize );
+ maxRenderTextureSize = maxTextureSize;
+
+ hasBlendSquare = QueryExtensionGL( "GL_NV_blend_square" );
+ hasSeparateAlphaBlend = QueryExtensionGL ("GL_EXT_blend_func_separate");
+
+ // Treat blendOps supported only if we have a full set (minmax & subtract; separate
+ // blend equation if have separate alpha func)
+ hasBlendSub = QueryExtensionGL("GL_EXT_blend_subtract");
+ hasBlendMinMax = QueryExtensionGL("GL_EXT_blend_minmax");
+ if (hasSeparateAlphaBlend)
+ {
+ hasBlendSub &= QueryExtensionGL("GL_EXT_blend_equation_separate");
+ hasBlendMinMax &= QueryExtensionGL("GL_EXT_blend_equation_separate");
+ }
+
+ // GLSL Vertex/Fragment shaders
+ gl.hasGLSL = QueryExtensionGL("GL_ARB_shader_objects") && QueryExtensionGL("GL_ARB_vertex_shader") && QueryExtensionGL("GL_ARB_fragment_shader");
+
+
+ gl.nativeVPInstructions = gl.vertexAttribCount = 0;
+ // Instruction count
+ glGetProgramivARB (GL_VERTEX_PROGRAM_ARB, GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB, &gl.nativeVPInstructions);
+ // Vertex atrribute count
+ glGetProgramivARB (GL_VERTEX_PROGRAM_ARB, GL_MAX_PROGRAM_ATTRIBS_ARB, &gl.vertexAttribCount);
+ if( gl.vertexAttribCount > 16 )
+ gl.vertexAttribCount = 16;
+
+ gl.nativeFPInstructions = gl.nativeFPTemporaries = gl.nativeFPALUInstructions = gl.nativeFPTexInstructions = 0;
+ // FP capabilities
+ glGetProgramivARB (GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB, &gl.nativeFPInstructions);
+ glGetProgramivARB (GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB, &gl.nativeFPTemporaries);
+ glGetProgramivARB (GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB, &gl.nativeFPALUInstructions);
+ glGetProgramivARB (GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB, &gl.nativeFPTexInstructions);
+
+ gl.hasTextureEnvCombine3ATI = QueryExtensionGL( "GL_ATI_texture_env_combine3" );
+ gl.hasTextureEnvCombine3NV = QueryExtensionGL( "GL_NV_texture_env_combine4" );
+
+ maxTexUnits = maxTexImageUnits = maxTexCoords = 1;
+ glGetIntegerv (GL_MAX_TEXTURE_UNITS_ARB, (GLint *)&maxTexUnits);
+ glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS_ARB, (GLint *)&maxTexImageUnits);
+ glGetIntegerv( GL_MAX_TEXTURE_COORDS_ARB, (GLint *)&maxTexCoords );
+
+ // Clamp max counts. Some recent GPUs (e.g. Radeon 5xxx on Windows)
+ // report 16 texture coordinates, which wreak havoc in the channel bindings
+ // (those assume 8 coords maximum, and more than that starts overwriting
+ // generic attribute bindings)
+ maxTexCoords = std::min<int> (maxTexCoords, kMaxSupportedTextureCoords);
+ maxTexImageUnits = std::min<int> (maxTexImageUnits, kMaxSupportedTextureUnits);
+ maxTexUnits = std::max<int>( std::min<int>( maxTexUnits, kMaxSupportedTextureUnits ), 1 );
+
+ hasAnisoFilter = QueryExtensionGL( "GL_EXT_texture_filter_anisotropic" );
+ if( hasAnisoFilter )
+ glGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, (GLint *)&maxAnisoLevel );
+ else
+ maxAnisoLevel = 1;
+ hasMipLevelBias = version >= 14 || QueryExtensionGL( "GL_EXT_texture_lod_bias" );
+
+ static GLint requiredCompressedFormats[] = { GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, -1 };
+ hasS3TCCompression = CheckCompressedFormatsSupport( requiredCompressedFormats );
+
+ // Set bits defined by GL version
+ gl.hasArbMapBufferRange = QueryExtensionGL ("GL_ARB_map_buffer_range");
+
+ // ARB_sync seems to be flaky on OS X 10.7
+ // Discovered as part of case 441958, when switching fullscreen mode.
+ // it will fall back to GL_APPLE_fence which seems to work reliably
+ gl.hasArbSync = UNITY_LINUX && QueryExtensionGL ("GL_ARB_sync");
+
+ #if UNITY_OSX
+ gl.hasAppleFence = QueryExtensionGL ("GL_APPLE_fence");
+ gl.hasAppleFlushBufferRange = QueryExtensionGL ("GL_APPLE_flush_buffer_range");
+ #endif
+
+ has3DTexture = QueryExtensionGL ("GL_EXT_texture3D") || version >= 12;
+
+ npot = QueryExtensionGL ("GL_ARB_texture_non_power_of_two") ? kNPOTFull : kNPOTNone;
+ // On OS X, pre-DX10 ATI cards can't really fully handle it.
+#if UNITY_OSX
+ if (npot == kNPOTFull && gl.nativeFPInstructions < 4096) {
+ npot = kNPOTRestricted;
+ }
+#endif
+ npotRT = npot;
+
+ hasTimerQuery = QueryExtensionGL ("GL_EXT_timer_query");
+
+ // has multi sampling?
+ #if UNITY_WIN
+ hasMultiSample = QueryExtensionGL( "GL_ARB_multisample" ) && WglQueryExtension( "WGL_ARB_multisample" );
+ gl.hasWglARBPixelFormat = WglQueryExtension( "WGL_ARB_pixel_format" ) && wglGetPixelFormatAttribivARB && wglChoosePixelFormatARB;
+ if( !gl.hasWglARBPixelFormat )
+ {
+ hasMultiSample = false;
+ printf_console( "GL: disabling multisample because WGL_ARB_pixel_format not fully supported\n" );
+ }
+ #else
+ hasMultiSample = QueryExtensionGL ("GL_ARB_multisample");
+ #endif
+
+ hasMultiSample &= QueryExtensionGL ("GL_EXT_framebuffer_multisample");
+ gl.maxSamples = 1;
+ if (hasMultiSample)
+ glGetIntegerv(GL_MAX_SAMPLES_EXT, &gl.maxSamples);
+
+ // has WGL swap control?
+ #if UNITY_WIN
+ gl.hasWglSwapControl = WglQueryExtension( "WGL_EXT_swap_control" );
+ #endif
+
+ hasSRGBReadWrite = QueryExtensionGL("GL_EXT_texture_sRGB");
+ hasSRGBReadWrite = hasSRGBReadWrite && QueryExtensionGL("GL_EXT_framebuffer_sRGB");
+
+ // Has render textures? We only support FBOs, not p-buffers.
+ hasRenderToTexture = QueryExtensionGL("GL_EXT_framebuffer_object");
+ hasRenderToCubemap = hasRenderToTexture;
+ hasRenderTargetStencil = QueryExtensionGL("GL_EXT_packed_depth_stencil");
+ if (QueryExtensionGL("GL_ARB_draw_buffers"))
+ {
+ glGetIntegerv (GL_MAX_DRAW_BUFFERS_ARB, &maxMRTs);
+ maxMRTs = clamp<int> (maxMRTs, 1, kMaxSupportedRenderTargets);
+ }
+ else
+ {
+ maxMRTs = 1;
+ }
+ hasStencil = true;
+ hasTwoSidedStencil = (version >= 20);
+ hasNativeDepthTexture = true;
+ hasStencilInDepthTexture = true;
+
+
+ #pragma message ("Properly implement supported formats!")
+ for (int q = 0; q < kTexFormatPCCount; ++q)
+ supportsTextureFormat[q] = true;
+
+
+ supportsRenderTextureFormat[kRTFormatARGB32] = hasRenderToTexture;
+ supportsRenderTextureFormat[kRTFormatDepth] = QueryExtensionGL ("GL_ARB_depth_texture");
+ supportsRenderTextureFormat[kRTFormatA2R10G10B10] = false;
+ supportsRenderTextureFormat[kRTFormatARGB64] = false;
+
+ if( hasRenderToTexture )
+ {
+ FBColorFormatCheckerGL checker;
+
+ supportsRenderTextureFormat[kRTFormatRGB565] = checker.CheckFormatSupported(GL_RGB5, GL_RGB, GL_UNSIGNED_SHORT_5_6_5);
+ supportsRenderTextureFormat[kRTFormatARGB4444] = checker.CheckFormatSupported(GL_RGBA4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4);
+ supportsRenderTextureFormat[kRTFormatARGB1555] = checker.CheckFormatSupported(GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_5_5_5_1);
+ }
+
+ {
+ const bool supportFloatTex = QueryExtensionGL("GL_ARB_texture_float");
+ const bool supportsHalfRT = supportFloatTex && QueryExtensionGL("GL_ARB_half_float_pixel");
+ const bool supportsFloatRT = supportFloatTex && (QueryExtensionGL("GL_ARB_color_buffer_float") || QueryExtensionGL("GL_APPLE_float_pixels"));
+ const bool supportsIntRT = QueryExtensionGL("GL_EXT_texture_integer");
+ const bool supportsRG = QueryExtensionGL("GL_ARB_texture_rg");
+
+ supportsRenderTextureFormat[kRTFormatARGBHalf] = hasRenderToTexture && supportsHalfRT;
+ supportsRenderTextureFormat[kRTFormatARGBFloat] = hasRenderToTexture && supportsFloatRT;
+ supportsRenderTextureFormat[kRTFormatR8] = hasRenderToTexture && supportsRG;
+ supportsRenderTextureFormat[kRTFormatRHalf] = hasRenderToTexture && supportsHalfRT && supportsRG;
+ supportsRenderTextureFormat[kRTFormatRFloat] = hasRenderToTexture && supportsFloatRT && supportsRG;
+ supportsRenderTextureFormat[kRTFormatRGHalf] = hasRenderToTexture && supportsHalfRT && supportsRG;
+ supportsRenderTextureFormat[kRTFormatRGFloat] = hasRenderToTexture && supportsFloatRT && supportsRG;
+ }
+
+ supportsRenderTextureFormat[kRTFormatDefault] = hasRenderToTexture;
+
+ hasNativeShadowMap = supportsRenderTextureFormat[kRTFormatDepth] && QueryExtensionGL("GL_ARB_fragment_program_shadow");
+ // GL_ARB_fragment_program_shadow does not work on OS X (at least on 10.4.8-10.4.10)
+ // Seems to work on 10.6.7 & 10.7.x (nvidia), so for safety just disable it on < 10.6.7.
+ #if UNITY_OSX
+ if (GetOSXVersion() < 0x01067)
+ hasNativeShadowMap = false;
+ #endif
+ gl.originalHasNativeShadowMaps = hasNativeShadowMap;
+ supportsRenderTextureFormat[kRTFormatShadowMap] = hasNativeShadowMap;
+
+ // Framebuffer blit
+ gl.hasFrameBufferBlit = QueryExtensionGL( "GL_EXT_framebuffer_blit" );
+
+ hasAutoMipMapGeneration = version >= 14 || QueryExtensionGL("GL_SGIS_generate_mipmap");
+
+ //// *** Unity bug workarounds following:
+
+ // Driver bugs/workarounds following
+ DetectDriverBugsGL( version );
+
+ // in the very end, figure out shader capabilities level (after all workarounds are applied)
+ if (gl.nativeFPInstructions < 1024 || gl.nativeFPTexInstructions < 512 || gl.nativeFPALUInstructions < 512 || gl.nativeFPTemporaries < 32)
+ {
+ // under 1024 instructions (512 tex + 512 alu) or under 32 temps: 2.x shaders
+ shaderCaps = kShaderLevel2;
+ }
+ else
+ {
+ // has everything we care about!
+ shaderCaps = kShaderLevel3;
+ }
+}
+
+static bool DisableGraphicsWorkarounds()
+{
+ // Disable workarounds if we have env set
+ if( getenv ("UNITY_DISABLE_GRAPHICS_DRIVER_WORKAROUNDS") && StrICmp (getenv ("UNITY_DISABLE_GRAPHICS_DRIVER_WORKAROUNDS"), "yes") == 0 )
+ {
+ printf_console( "GL: disabling graphics workarounds\n" );
+ return true;
+ }
+ return false;
+}
+
+void GraphicsCaps::AdjustBuggyVersionGL( int& version, int& major, int& minor ) const
+{
+ if( DisableGraphicsWorkarounds() )
+ return;
+
+ // SiS stuff
+ if( (vendorString.find("SiS") == 0) )
+ {
+ // Compatible VGA / MMX: reports as 2.06a.00, seems like 1.1
+ if( (rendererString.find("Compatible VGA") != string::npos) && version >= 20 )
+ {
+ printf_console("GL: Wrong GL version reported, treating as GL 1.1\n");
+ version = 11;
+ major = 1;
+ minor = 1;
+ return;
+ }
+ // 630/730 / MMX: reports as 2.03 to 2.08, seems like 1.1
+ if( (rendererString.find("630/730") != string::npos) && version >= 20 )
+ {
+ printf_console("GL: Wrong GL version reported, treating as GL 1.1\n");
+ version = 11;
+ major = 1;
+ minor = 1;
+ return;
+ }
+ }
+}
+
+#if UNITY_WIN
+#include "PlatformDependent/Win/WinUtils.h"
+#endif
+
+void GraphicsCaps::DetectDriverBugsGL( int version )
+{
+ gl.buggyArbPrecisionHint = false;
+ gl.force24DepthForFBO = false;
+ gl.cacheFPParamsWithEnvs = false;
+ gl.forceColorBufferWithDepthFBO = false;
+ gl.buggyPackedDepthStencil = false;
+ gl.mustWriteToDepthBufferBeforeClear = false;
+
+ const bool isNvidia = (vendorString.find("NVIDIA") != string::npos) || (vendorString.find("Nvidia") != string::npos);
+ const bool isRadeon = (rendererString.find("RADEON") != string::npos) || (rendererString.find("Radeon") != string::npos);
+
+#if UNITY_WIN || UNITY_LINUX
+ // vendorString on Intel Gfx / Linux contains Tungsten graphics instead of Intel so we check the rendererString too...
+ bool isIntel = (vendorString.find ("INTEL") != string::npos) || (vendorString.find ("Intel") != string::npos) || (rendererString.find("Intel") != string::npos);
+#endif
+
+
+ // Driver version numbers
+#if UNITY_WIN || UNITY_LINUX
+ int majorVersion = 0, minorVersion = 0;
+ bool parsedDriverVersion = false;
+#endif
+#if UNITY_WIN
+ int buildVersion = 0;
+#endif
+#if UNITY_LINUX
+ int seriesVersion = 0;
+ bool parsedSeriesVersion = false;
+#endif
+
+#if UNITY_WIN
+ int driverFields = sscanf( driverVersionString.c_str(), "%i.%i.%i", &majorVersion, &minorVersion, &buildVersion );
+ parsedDriverVersion = (driverFields == 3);
+#endif
+#if UNITY_LINUX
+ if (isNvidia)
+ {
+ int temp1, temp2, temp3 = 0;
+ int driverFields = sscanf (driverVersionString.c_str(), "%i.%i.%i NVIDIA %i.%i", &temp1, &temp2, &temp3, &majorVersion, &minorVersion);
+ parsedDriverVersion = (driverFields == 5);
+ }
+ if (isRadeon)
+ {
+ char temp[BUFSIZ];
+ int seriesFields = sscanf (rendererString.c_str(), "%s Radeon HD %i %*", temp, &seriesVersion);
+ parsedSeriesVersion = (seriesFields == 2);
+ }
+#endif
+
+ if( DisableGraphicsWorkarounds() )
+ return;
+
+
+ // Timer queries are broken on OS X + older NVIDIA cards. Before 10.8 they just return bogus data;
+ // starting with 10.8 they actually lock up. It seems that everything before GeForce 6xx is affected
+ // (8800, 9400, 9600, GT120, 320, GT330). But GT650 is not affected and has GPU profiler working properly.
+ // From https://developer.apple.com/graphicsimaging/opengl/capabilities/ looks like earlier GPUs have ~60
+ // varying floats, and 6xx has ~120, so let's detect on that.
+ #if UNITY_OSX
+ if (isNvidia && gl.hasGLSL)
+ {
+ // Case 562054:
+ // It looks that timer queries are broken on OS X + Nvidia cards again.
+ // For now, just disable timer queries on all Nvidia cars + OS X.
+
+ //GLint maxVarying = 0;
+ //glGetIntegerv (GL_MAX_VARYING_FLOATS, &maxVarying);
+ //if (maxVarying < 100)
+ hasTimerQuery = false;
+ }
+ #endif // #if UNITY_OSX
+
+
+ // DynamicVBO with tangents is buggy. From comment by rej in dynamic batching code:
+ // "For some (yet) unkown reason DynamicNullVBO under OpenGL fail rendering geometry with Tangents
+ // we have to avoid batching for geometry with Tangents as long as DynamicNullVBO is going to be used"
+ // No one remembers the OS/GPU details by now (was around Unity 3.0 release), and looks like it's not a problem
+ // around Unity 4.0 release. So we'll assume that some recent OS X fixed the underlying issue. Let's say
+ // starting with 10.6.8 the problem does not exist...
+#if UNITY_OSX
+ if (GetOSXVersion() < 0x01068)
+ buggyDynamicVBOWithTangents = true;
+#endif
+
+#if UNITY_LINUX
+ if (isNvidia && parsedDriverVersion && (majorVersion <= 195))
+ {
+ printf_console ("GL: enabling buggyPackedDepthStencil due to driver bug\n");
+ gl.buggyPackedDepthStencil = true;
+ buggyTextureBothColorAndDepth = true;
+ }
+#endif
+
+#if UNITY_OSX || UNITY_LINUX
+ bool isGMA950 = (rendererString == "Intel GMA 950 OpenGL Engine");
+ bool isGMA3100 = (rendererString == "Intel GMA X3100 OpenGL Engine");
+#endif
+
+
+#if UNITY_OSX
+ bool hasLeakingFBOWhenUsingMipMaps = rendererString == "NVIDIA GeForce 8600M GT OpenGL Engine";
+ if (hasLeakingFBOWhenUsingMipMaps)
+ {
+ printf_console( "GL: disabling mip map calculation on Render Textures due to memory leak on NVIDIA GeForce 8600 graphics drivers.\n");
+ hasAutoMipMapGeneration = false;
+ }
+#endif
+
+
+#if UNITY_OSX
+ // On OS X, trying to create depth texture/buffer in 16 bits fails on pre-10.6.
+ if (GetOSXVersion() < 0x01062)
+ {
+ gl.force24DepthForFBO = true;
+ }
+#endif
+
+ // Macs with Radeon HD cards (2400-4850) have bugs when using depthstencil texture
+ // as both depth buffer for testing and reading as a texture. Happens at least on 10.5.8 to 10.6.4.
+ // Same happens on Radeon X1600 as well, so we just do the workaround for all Radeons.
+ //
+ // Also, 10.6.4 graphics driver update broke regular sampling of depth stencil textures. Some words from
+ // Apple's Matt Collins:
+ // "What I suspect is happening is that ATI doesn't handle sampling from depth
+ // textures correctly if Hi-Z/Z-compression is on AND that texture has no mipmapping. I want to see
+ // if we can force the driver to alloc mipmaps for you. Typically we check a couple things to
+ // determine whether to allocate: whether the app has requested mipmaps, whether one the sampling mode is
+ // a mipmap filter, if mipmaps have been manually specified, or if MAX_LEVELS has been changed from
+ // the default value."
+ // Setting TEXTURE_MAX_LEVELS to zero before creating the texture fixes this particular problem; but sampling
+ // of the texture while using it for depth testing is still broken.
+#if UNITY_OSX
+ if (hasRenderTargetStencil && isRadeon)
+ {
+ printf_console( "GL: buggy packed depth stencil; Deferred rendering will use slower rendering path\n" );
+ gl.buggyPackedDepthStencil = true;
+ buggyTextureBothColorAndDepth = true;
+ }
+#endif
+
+ // Disable 16-bit float vertex channels on Radeon cards as the driver hits a super slow path
+ // when using three 16-bit components of a normal and 16-bits of padding to keep it aligned.
+ if (isRadeon)
+ has16BitFloatVertex = false;
+
+#if UNITY_OSX && WEBPLUG
+ if (GetOSXVersion() >= 0x01072 && isNvidia)
+ gl.mustWriteToDepthBufferBeforeClear = true;
+#endif
+
+#if UNITY_OSX
+ // Macs with GeForce 7300 cards have broken packed depth stencil implementation. Happens at least
+ // up to OS X 10.6.3. We can tell pre-DX10 cards by looking at max. texture size; it is always less
+ // than 8192 on Macs.
+ const bool isGeForcePreDX10 = isNvidia && (maxTextureSize < 8192);
+ if (hasRenderTargetStencil && isGeForcePreDX10)
+ {
+ printf_console( "GL: disabling deferred lighting (buggy packed depth stencil on pre-DX10 GeForce cards)\n" );
+ hasRenderTargetStencil = false;
+ buggyTextureBothColorAndDepth = true;
+ gl.force24DepthForFBO = true; // looks like that still can't handle 16 bit FBO depth (OS X 10.6.3)
+ }
+
+ // Also, GeForce 7300 seems to bail out on anti-aliasing switches. OS X 10.5.8; anti-aliasing switching
+ // graphics tests produce "NVDA(OpenGL): Channel exception! status = 0xffff info32 = 0x6 = Fifo: Parse Error".
+ if (isGeForcePreDX10 && GetOSXVersion() < 0x01062)
+ {
+ printf_console( "GL: disabling anti-aliasing (buggy drivers on OS X 10.5 on pre-DX10 GeForce cards)\n" );
+ hasMultiSample = false;
+ }
+
+# if WEBPLUG
+ if (isNvidia && GetOSXVersion () < 0x01090)
+ {
+ // NVIDIA drivers crash on OS X earlier than 10.9.0 when multisampling is enabled (case 521161):
+ // - when right-clicking the Unity content in Firefox
+ // - when exiting fullscreen in Chrome and Safari
+ printf_console( "GL: disabling anti-aliasing (buggy drivers on OS X pre-10.9.0 on NVIDIA cards give kernel panics)\n" );
+ hasMultiSample = false;
+ }
+# endif
+#endif
+
+#if UNITY_OSX
+ // OS X with GMA X3100: lots of problems! (on OS X earlier than 10.5.3)
+ if( isGMA3100 && GetOSXVersion() < 0x01053 )
+ {
+ // Crashes if depth component FBO is used without color attachment.
+ if( hasRenderToTexture )
+ gl.forceColorBufferWithDepthFBO = true;
+
+ // Depth textures just contain garbage
+ printf_console( "GL: disabling shadows (buggy on GMA X3100 pre-10.5.3)\n" );
+ supportsRenderTextureFormat[kRTFormatDepth] = false;
+ // Cubemap pbuffers/FBOs contain garbage
+ printf_console( "GL: disabling render to cubemap (buggy on GMA X3100 pre-10.5.3)\n" );
+ hasRenderToCubemap = false;
+ }
+
+ // OS X 10.5.3 and 10.5.4 with non-updated drivers on Radeon X1xxx cards: shadows and render textures broken!
+ // Do not disable them though, sometimes they work... just show error message.
+ if( rendererString.find("ATI Radeon X1") != string::npos && GetOSXVersion() >= 0x01053 )
+ {
+ // Driver 2.0 ATI-1.5.28 is broken (10.5.3, looks like 10.5.4 has the same driver).
+ // Pre-release driver 2.0 ATI-1.5.30 is good.
+ int driverBuild = 0;
+ sscanf( driverVersionString.c_str(), "2.0 ATI-1.5.%i", &driverBuild );
+ if( driverBuild >= 28 && driverBuild < 30 ) {
+ ErrorString( "GL: broken Radeon X1xxx driver, shadows and render textures might behave funny. Wait for Apple to fix it..." );
+ }
+ }
+#endif
+
+#if UNITY_WIN || UNITY_OSX || UNITY_LINUX
+ // When creating the FBO and not attaching any color buffer, the FBO will return 'FBO fail: INCOMPLETE_DIMENSIONS'.
+ gl.forceColorBufferWithDepthFBO = true;
+#endif
+
+#if UNITY_OSX
+ bool isGeForceGT650M = (rendererString == "NVIDIA GeForce GT 650M OpenGL Engine");
+ if (isGeForceGT650M)
+ {
+ // MBP Retina June 2012 with OS X 10.8.2 freezes when at least one dimension of the texture is 8192
+ maxTextureSize = 4096;
+ }
+#endif
+
+#if UNITY_OSX
+ // Some NVIDIA cards have a broken ARB precision hint. We postprocess all fragment
+ // programs to not use it.
+ // It is definitely broken on a GeForce 5200 mobility on tiger 10.4.1
+ gl.buggyArbPrecisionHint |= IsRendererAppleFX5200(rendererString);
+#endif
+
+#if UNITY_OSX
+ if( isGMA950 )
+ {
+ printf_console("GL: Disabling soft shadows because of GMA950 driver bugs\n");
+ disableSoftShadows = true;
+
+ // OS X: occasional crashes in auto FBO mipmap generation, OS X 10.5 & 10.6, case 372004
+ printf_console( "GL: disabling mip map calculation on Render Textures due to GMA950 driver bugs\n");
+ hasAutoMipMapGeneration = false;
+
+ // Something wrong with FBOs on GMA 950, try disabling depth+stencil & color+depth FBOs.
+ hasRenderTargetStencil = false;
+ buggyTextureBothColorAndDepth = true;
+ }
+#endif
+
+#if UNITY_OSX && UNITY_LITTLE_ENDIAN
+ // On Intel Macs with ATI cards, use Env parameters for fragment programs instead of Local
+ // ones, and cache the redundant changes.
+ // Do not do this on any other cards, as that will make shadows randomly disappear on PPC Macs.
+ gl.cacheFPParamsWithEnvs |= vendorString.find("ATI Technologies") != string::npos;
+#endif
+
+
+#if UNITY_WIN
+ if( isIntel )
+ {
+ // Intel 965 seems to be randomly broken with shadows, and sometimes with render
+ // textures. Sometimes they hang with them (e.g. in terrain unit test). Just disable.
+ if( rendererString.find("965/963") != string::npos || rendererString.find("Broadwater") != string::npos )
+ {
+ if( hasRenderToTexture )
+ {
+ printf_console( "GL: Disabling render to texture on Intel card (buggy)\n" );
+ hasRenderToTexture = false;
+ hasRenderToCubemap = false;
+ hasAutoMipMapGeneration = false;
+ for (int i = 0; i < kRTFormatCount; ++i)
+ supportsRenderTextureFormat[i] = false;
+ maxMRTs = 1;
+ }
+ }
+ }
+#endif
+
+#if UNITY_LINUX
+ // Hardware shadow maps on spot light shadows seem to be weird,
+ // at least on gfx test agent (GeForce GTX 260 OpenGL 3.3 [3.3.0 NVIDIA 295.40])
+ buggySpotNativeShadowMap = true;
+#endif
+
+#if UNITY_WIN || UNITY_LINUX
+ if( isIntel )
+ {
+ // Intel 9xx have lots of problems with shadows. Just disable them.
+ printf_console( "GL: disabling shadows on Intel 9xx (buggy)\n" );
+ supportsRenderTextureFormat[kRTFormatDepth] = false;
+ hasRenderToCubemap = false;
+ hasAutoMipMapGeneration = false;
+ }
+#endif
+
+#if UNITY_LINUX
+ if (isIntel)
+ {
+ printf_console ("GL: disabling framebuffer blit, antialiasing, SRGB on Intel\n");
+ gl.hasFrameBufferBlit = false;
+ hasMultiSample = false;
+ hasSRGBReadWrite = false;
+
+ }
+#endif
+
+ // S3 UniChrome does not have 3D textures, even if they say it's OpenGL 1.2.
+ // To be safe just disable them on all S3 cards that don't explicitly expose it.
+ if( (vendorString.find("S3 ") == 0) && !QueryExtensionGL("GL_EXT_texture3D") )
+ {
+ printf_console( "GL: Disabling 3D textures because the card does not actually have them\n" );
+ has3DTexture = false;
+ }
+
+
+ // Many Radeon drivers on windows are quite bad
+#if UNITY_WIN
+ if( (isRadeon || (rendererString.find("FIREGL") != string::npos) || (rendererString.find("FireGL") != string::npos)) && (vendorString.find("ATI ") != string::npos) )
+ {
+ // FireGL with version 2.0.5284 crashes when destroying render textures. The driver is
+ // from about year 2005. Disable RTs.
+ if( parsedDriverVersion && version <= 20 && buildVersion <= 5284 && rendererString.find("FireGL") != string::npos )
+ {
+ hasRenderToTexture = hasRenderToCubemap = false;
+ for (int i = 0; i < kRTFormatCount; ++i)
+ supportsRenderTextureFormat[i] = false;
+ maxMRTs = 1;
+ printf_console( "GL: disabling render textures due to FireGL driver bugs\n" );
+ }
+
+ /* The issue description from Emil Persson <epersson@ati.com>:
+
+ The problem is that the shaders are using constructs like this:
+ PARAM c[4] = { program.local[0..2], { 2 } };
+ Older drivers didn't handle mixed declaration well. Are you using Cg to
+ compile the shaders? They switched to using mixed declarations quite a
+ while ago, which exposed the bug. If you revert to an older version of
+ the compiler (I think it was 1.3 that caused the problem, so try 1.2)
+ that should dodge the problem for 5.3.
+ <...>
+ It's not the range, but the mix of params and immediate values that
+ causes the problem. Both work fine separately, but if you put both in
+ the same array it's going to break. So what you can do is split it into
+ two arrays, one for program.local and one for immediate values.
+ PARAM c[4] = { program.local[0..2], { 2 } };
+ becomes
+ PARAM c[3] = { program.local[0..2] };
+ PARAM d[1] = { { 2 } };
+ Then you'll have to replace all occurrences of c[3] with d[0] in the
+ shader.
+ */
+
+ // Even with latest drivers today (7.4, May 2007) render-to-cubemap
+ // does not quite work (faces inverted/flipped/etc.). Disable!
+ printf_console( "GL: disabling render-to-cubemap due to Radeon driver bugs\n" );
+ hasRenderToCubemap = false;
+
+ // Mip-mapped render textures seem to crash randomly, at least
+ // on MBP/Vista (GL version 2.0.6347). E.g. the mipmapped RT unit test scene crashes.
+ // Does work correctly on XP.
+ //
+ // The same also crashes on iMac Radeon HD 2600, Windows XP (GL version 2.0.6641). So we detect Vista
+ // or Radeon HD and disable.
+ if( winutils::GetWindowsVersion() >= winutils::kWindowsVista || rendererString.find("Radeon HD") != string::npos )
+ {
+ printf_console( "GL: disabling mipmapped render textures due to Radeon driver bugs\n" );
+ hasAutoMipMapGeneration = false;
+ }
+ }
+#endif
+
+ // Sanitize VRAM amount
+ if( videoMemoryMB < 32 ) {
+ printf_console("GL: VRAM amount suspiciously low (less than 32MB for fragment program card)\n");
+ videoMemoryMB = 32;
+ }
+}
diff --git a/Runtime/GfxDevice/opengl/NullVBO.cpp b/Runtime/GfxDevice/opengl/NullVBO.cpp
new file mode 100644
index 0000000..acc802f
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/NullVBO.cpp
@@ -0,0 +1,168 @@
+#include "UnityPrefix.h"
+#include "NullVBO.h"
+#include "UnityGL.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "ChannelsGL.h"
+
+
+extern GLenum kTopologyGL[kPrimitiveTypeCount];
+
+
+
+DynamicNullVBO::DynamicNullVBO()
+: DynamicVBO()
+, m_VBChunk(NULL)
+, m_VBChunkSize(0)
+, m_IBChunk(NULL)
+, m_IBChunkSize(0)
+{
+}
+
+DynamicNullVBO::~DynamicNullVBO ()
+{
+ delete[] m_VBChunk;
+ delete[] m_IBChunk;
+}
+
+void DynamicNullVBO::DrawChunk (const ChannelAssigns& channels)
+{
+ // just return if nothing to render
+ if( !m_LastChunkShaderChannelMask )
+ return;
+
+ Assert(m_LastChunkShaderChannelMask && m_LastChunkStride);
+ Assert(!m_LendedChunk);
+
+ // setup VBO
+ UnbindVertexBuffersGL();
+ ClearActiveChannelsGL();
+ UInt32 targetMap = channels.GetTargetMap();
+ for( int i = 0; i < kVertexCompCount; ++i )
+ {
+ if( !( targetMap & (1<<i) ) )
+ continue;
+ ShaderChannel src = channels.GetSourceForTarget( (VertexComponent)i );
+ if( !( m_LastChunkShaderChannelMask & (1<<src) ) )
+ continue;
+
+ SetChannelDataGL( src, (VertexComponent)i, (UInt8*)m_BufferChannel[src], m_LastChunkStride );
+ }
+ GfxDevice& device = GetRealGfxDevice();
+ ActivateChannelsGL();
+ device.BeforeDrawCall( false );
+
+ // draw
+ int primCount = 0;
+ if( m_LastRenderMode == kDrawTriangleStrip )
+ {
+ Assert(m_LastChunkIndices == 0);
+ OGL_CALL(glDrawArrays( GL_TRIANGLE_STRIP, 0, m_LastChunkVertices ));
+ primCount = m_LastChunkVertices-2;
+ }
+ else if (m_LastRenderMode == kDrawQuads)
+ {
+ Assert(m_LastChunkIndices == 0);
+ OGL_CALL(glDrawArrays( GL_QUADS, 0, m_LastChunkVertices ));
+ primCount = m_LastChunkVertices/2;
+ }
+ else if (m_LastRenderMode == kDrawIndexedTriangleStrip)
+ {
+ DebugAssert(m_LastChunkIndices > 0);
+ OGL_CALL(glDrawElements( GL_TRIANGLE_STRIP, m_LastChunkIndices, GL_UNSIGNED_SHORT, m_IBChunk ));
+ primCount = m_LastChunkIndices-2;
+ }
+ else if (m_LastRenderMode == kDrawIndexedLines)
+ {
+ DebugAssert(m_LastChunkIndices > 0);
+ OGL_CALL(glDrawElements( GL_LINES, m_LastChunkIndices, GL_UNSIGNED_SHORT, m_IBChunk ));
+ primCount = m_LastChunkIndices/2;
+ }
+ else if (m_LastRenderMode == kDrawIndexedPoints)
+ {
+ DebugAssert(m_LastChunkIndices > 0);
+ OGL_CALL(glDrawElements( GL_POINTS, m_LastChunkIndices, GL_UNSIGNED_SHORT, m_IBChunk ));
+ primCount = m_LastChunkIndices;
+ }
+ else
+ {
+ DebugAssert(m_LastChunkIndices > 0);
+ OGL_CALL(glDrawElements( GL_TRIANGLES, m_LastChunkIndices, GL_UNSIGNED_SHORT, m_IBChunk ));
+ primCount = m_LastChunkIndices/3;
+ }
+ device.GetFrameStats().AddDrawCall (primCount, m_LastChunkVertices);
+}
+
+bool DynamicNullVBO::GetChunk( UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode renderMode, void** outVB, void** outIB )
+{
+ Assert( !m_LendedChunk );
+ Assert( maxVertices < 65536 && maxIndices < 65536*3 );
+ DebugAssert( outVB != NULL && maxVertices > 0 );
+ DebugAssert(
+ (renderMode == kDrawIndexedQuads && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedPoints && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedLines && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedTriangles && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedTriangleStrip && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawTriangleStrip && (outIB == NULL && maxIndices == 0)) ||
+ (renderMode == kDrawQuads && (outIB == NULL && maxIndices == 0)));
+
+ m_LendedChunk = true;
+ m_LastChunkShaderChannelMask = shaderChannelMask;
+ m_LastRenderMode = renderMode;
+ if( maxVertices == 0 )
+ maxVertices = 8;
+
+ m_LastChunkStride = 0;
+ for( int i = 0; i < kShaderChannelCount; ++i ) {
+ if( shaderChannelMask & (1<<i) )
+ m_LastChunkStride += VBO::GetDefaultChannelByteSize(i);
+ }
+
+ UInt32 vbCapacity = maxVertices * m_LastChunkStride;
+ if( vbCapacity > m_VBChunkSize )
+ {
+ delete[] m_VBChunk;
+ m_VBChunk = new UInt8[ vbCapacity ];
+ m_VBChunkSize = vbCapacity;
+ }
+ *outVB = m_VBChunk;
+
+ const bool indexed = (renderMode != kDrawQuads) && (renderMode != kDrawTriangleStrip);
+ if( maxIndices && indexed)
+ {
+ UInt32 ibCapacity = maxIndices * kVBOIndexSize;
+ if( ibCapacity > m_IBChunkSize )
+ {
+ delete[] m_IBChunk;
+ m_IBChunk = new UInt8[ ibCapacity ];
+ m_IBChunkSize = ibCapacity;
+ }
+ *outIB = m_IBChunk;
+ }
+
+ return true;
+}
+
+void DynamicNullVBO::ReleaseChunk( UInt32 actualVertices, UInt32 actualIndices )
+{
+ Assert( m_LendedChunk );
+ Assert( m_LastRenderMode == kDrawIndexedTriangleStrip || m_LastRenderMode == kDrawIndexedQuads || m_LastRenderMode == kDrawIndexedPoints || m_LastRenderMode == kDrawIndexedLines || actualIndices % 3 == 0 );
+ m_LendedChunk = false;
+
+ m_LastChunkVertices = actualVertices;
+ m_LastChunkIndices = actualIndices;
+
+ const bool indexed = (m_LastRenderMode != kDrawQuads) && (m_LastRenderMode != kDrawTriangleStrip);
+ if( !actualVertices || (indexed && !actualIndices) ) {
+ m_LastChunkShaderChannelMask = 0;
+ return;
+ }
+
+ UInt8* channelOffset = (UInt8*)m_VBChunk;
+ for( int i = 0; i < kShaderChannelCount; ++i ) {
+ if( m_LastChunkShaderChannelMask & (1<<i) ) {
+ m_BufferChannel[i] = channelOffset;
+ channelOffset += VBO::GetDefaultChannelByteSize(i);
+ }
+ }
+}
diff --git a/Runtime/GfxDevice/opengl/NullVBO.h b/Runtime/GfxDevice/opengl/NullVBO.h
new file mode 100644
index 0000000..a916f5a
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/NullVBO.h
@@ -0,0 +1,27 @@
+#ifndef NULL_VBO_H
+#define NULL_VBO_H
+
+#include "Runtime/Shaders/BufferedVBO.h"
+#include "Configuration/UnityConfigure.h"
+
+
+class DynamicNullVBO : public DynamicVBO {
+public:
+ DynamicNullVBO();
+ virtual ~DynamicNullVBO();
+
+ virtual bool GetChunk( UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode renderMode, void** outVB, void** outIB );
+ virtual void ReleaseChunk( UInt32 actualVertices, UInt32 actualIndices );
+ virtual void DrawChunk (const ChannelAssigns& channels);
+
+private:
+ void* m_BufferChannel[kShaderChannelCount];
+
+ UInt8* m_VBChunk;
+ UInt32 m_VBChunkSize;
+ UInt8* m_IBChunk;
+ UInt32 m_IBChunkSize;
+};
+
+
+#endif
diff --git a/Runtime/GfxDevice/opengl/RenderTextureGL.cpp b/Runtime/GfxDevice/opengl/RenderTextureGL.cpp
new file mode 100644
index 0000000..c20c3e5
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/RenderTextureGL.cpp
@@ -0,0 +1,751 @@
+#include "UnityPrefix.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "UnityGL.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "GLAssert.h"
+#include "GLContext.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Graphics/RenderSurface.h"
+#include "TexturesGL.h"
+#include "TextureIdMapGL.h"
+
+#if UNITY_OSX
+#include <OpenGL/OpenGL.h>
+#elif UNITY_WIN
+#include <windows.h>
+#include "PlatformDependent/Win/wglext.h"
+#elif UNITY_LINUX
+#include <GL/glext.h>
+#else
+#error "Unknown platform"
+#endif
+
+#define GL_RT_COMMON_GL 1
+#include "Runtime/GfxDevice/GLRTCommon.h"
+#undef GL_RT_COMMON_GL
+
+
+extern GLint gDefaultFBOGL = 0;
+
+// define to 1 to print lots of activity info
+#define DEBUG_RENDER_TEXTURES 0
+
+
+struct RenderColorSurfaceGL : public RenderSurfaceBase
+{
+ GLuint m_ColorBuffer;
+ RenderTextureFormat format;
+ TextureDimension dim;
+};
+
+struct RenderDepthSurfaceGL : public RenderSurfaceBase
+{
+ GLuint m_DepthBuffer;
+ DepthBufferFormat depthFormat;
+};
+
+const unsigned long* GetGLTextureDimensionTable(); // GfxDeviceGL.cpp
+
+
+static RenderColorSurfaceGL* s_ActiveColorTargets[kMaxSupportedRenderTargets];
+static int s_ActiveColorTargetCount = 0;
+static int s_AttachedColorTargetCount = 0;
+static RenderDepthSurfaceGL* s_ActiveDepthTarget = NULL;
+static int s_ActiveMip = 0;
+static CubemapFace s_ActiveFace = kCubeFaceUnknown;
+
+static const char* GetFBOStatusError( GLenum status )
+{
+ Assert(status != GL_FRAMEBUFFER_COMPLETE_EXT); // should not be called when everything is ok...
+ switch( status )
+ {
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: return "INCOMPLETE_ATTACHMENT";
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: return "INCOMPLETE_MISSING_ATTACHMENT";
+ case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT: return "INCOMPLETE_DUPLICATE_ATTACHMENT";
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: return "INCOMPLETE_DIMENSIONS";
+ case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: return "INCOMPLETE_FORMATS";
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: return "INCOMPLETE_DRAW_BUFFER";
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: return "INCOMPLETE_READ_BUFFER";
+ case GL_FRAMEBUFFER_UNSUPPORTED_EXT: return "UNSUPPORTED";
+ default: return "unknown error";
+ }
+}
+
+static void DetachPreviousColorTargets (int startIndex, int endIndex)
+{
+ // Detach color buffers from the attachments that were not touched this time
+ for (int i = startIndex; i < endIndex; ++i)
+ {
+ GLenum colorAttach = GL_COLOR_ATTACHMENT0_EXT + i;
+
+ // Even though we were attaching a 2D texture, a cubemap face or a render buffer,
+ // we can detach either of them like a 2D texture.
+ glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, colorAttach, GL_TEXTURE_2D, 0, 0);
+ }
+}
+
+bool SetRenderTargetGL (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face, GLuint globalSharedFBO)
+{
+ RenderColorSurfaceGL* rcolorZero = reinterpret_cast<RenderColorSurfaceGL*>(colorHandles[0].object);
+ RenderDepthSurfaceGL* rdepth = reinterpret_cast<RenderDepthSurfaceGL*>( depthHandle.object );
+
+ #if DEBUG_RENDER_TEXTURES
+ printf_console( "RT: SetRenderTargetGL color=%i depth=%i mip=%i face=%i\n",
+ rcolorZero ? rcolorZero->textureID.m_ID : 0,
+ rdepth ? rdepth->textureID.m_ID : 0,
+ mipLevel, face );
+ #endif
+
+ // Exit if nothing to do
+ if (count == s_ActiveColorTargetCount && s_ActiveDepthTarget == rdepth && s_ActiveMip == mipLevel && s_ActiveFace == face)
+ {
+ bool colorsSame = true;
+ for (int i = 0; i < count; ++i)
+ {
+ if (s_ActiveColorTargets[i] != reinterpret_cast<RenderColorSurfaceGL*>(colorHandles[i].object))
+ colorsSame = false;
+ }
+ if (colorsSame)
+ return false;
+ }
+
+ Assert (gGraphicsCaps.hasRenderToTexture);
+ GetRealGfxDevice().GetFrameStats().AddRenderTextureChange(); // stats
+
+ // cant mix default fbo and user fbo
+ Assert(colorHandles[0].IsValid() && depthHandle.IsValid());
+ Assert(colorHandles[0].object->backBuffer == depthHandle.object->backBuffer);
+
+
+ if (!rcolorZero->backBuffer)
+ {
+ // color surfaces
+ glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, globalSharedFBO);
+ GLenum drawBuffers[kMaxSupportedRenderTargets];
+ bool readBufferNone = false;
+ for (int i = 0; i < count; ++i)
+ {
+ GLenum colorAttach = GL_COLOR_ATTACHMENT0_EXT + i;
+ RenderColorSurfaceGL* rcolor = reinterpret_cast<RenderColorSurfaceGL*>(colorHandles[i].object);
+ Assert (rcolor->colorSurface);
+ if (rcolor->textureID.m_ID)
+ {
+ #if DEBUG_RENDER_TEXTURES
+ printf_console( " RT: color buffer texture %i\n", rcolor->textureID.m_ID );
+ #endif
+ drawBuffers[i] = colorAttach;
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(rcolor->textureID);
+ if (rcolor->dim == kTexDimCUBE)
+ {
+ glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, colorAttach, GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + clamp<int>(face,0,5), targetTex, mipLevel );
+ }
+ else
+ {
+ glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, colorAttach, GL_TEXTURE_2D, targetTex, mipLevel );
+ }
+ }
+ else
+ {
+ if (rcolor->m_ColorBuffer)
+ {
+ #if DEBUG_RENDER_TEXTURES
+ printf_console( " RT: color buffer plain %i\n", rcolor->m_ColorBuffer );
+ #endif
+ glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, colorAttach, GL_RENDERBUFFER_EXT, rcolor->m_ColorBuffer);
+ drawBuffers[i] = colorAttach;
+ }
+ else
+ {
+ #if DEBUG_RENDER_TEXTURES
+ printf_console( " RT: color buffer none\n" );
+ #endif
+ glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, colorAttach, GL_TEXTURE_2D, 0);
+ drawBuffers[i] = GL_NONE;
+ if (i == 0)
+ readBufferNone = true;
+ }
+ }
+ }
+
+ DetachPreviousColorTargets (count, s_AttachedColorTargetCount);
+ s_AttachedColorTargetCount = count;
+
+ if (count <= 1 || gGraphicsCaps.maxMRTs <= 1)
+ glDrawBuffer (drawBuffers[0]);
+ else
+ glDrawBuffersARB (count, drawBuffers);
+ glReadBuffer (readBufferNone ? GL_NONE : GL_COLOR_ATTACHMENT0_EXT);
+
+ // depth surface
+ Assert (!rdepth->colorSurface);
+ bool bindDepthTexture = true;
+ if (gGraphicsCaps.gl.buggyPackedDepthStencil)
+ {
+ if (rcolorZero && rcolorZero->textureID.m_ID && rdepth->depthFormat == kDepthFormat24)
+ {
+ bindDepthTexture = false;
+ }
+ }
+
+ if (rdepth->textureID.m_ID && bindDepthTexture)
+ {
+ #if DEBUG_RENDER_TEXTURES
+ printf_console( " RT: depth buffer texture %i\n", rdepth->textureID.m_ID );
+ #endif
+
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(rdepth->textureID);
+
+ glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, targetTex, 0);
+ if (gGraphicsCaps.hasRenderTargetStencil && rdepth->depthFormat == kDepthFormat24 && !gGraphicsCaps.gl.buggyPackedDepthStencil)
+ glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, targetTex, 0);
+ else
+ glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
+ }
+ else
+ {
+ #if DEBUG_RENDER_TEXTURES
+ printf_console( " RT: depth buffer plain %i\n", rdepth->m_DepthBuffer );
+ #endif
+ glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rdepth->m_DepthBuffer);
+ if (gGraphicsCaps.hasRenderTargetStencil && rdepth->depthFormat == kDepthFormat24)
+ glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rdepth->m_DepthBuffer);
+ else
+ glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0);
+ }
+
+ #if GFX_DEVICE_VERIFY_ENABLE
+ GLenum status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
+ if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+ {
+ std::string msg = Format("OpenGL failed to activate FBO: %s (color tex %i buf %i fmt %i) (depth tex %i buf %i fmt %i)",
+ GetFBOStatusError(status),
+ rcolorZero->textureID.m_ID,
+ rcolorZero->m_ColorBuffer,
+ rcolorZero->format,
+ rdepth->textureID.m_ID,
+ rdepth->m_DepthBuffer,
+ rdepth->depthFormat
+ );
+ AssertString (msg);
+ }
+ #endif
+ }
+ else
+ {
+ Assert (rcolorZero->backBuffer && rdepth->backBuffer); // OpenGL can't mix FBO and native window at once
+ #if DEBUG_RENDER_TEXTURES
+ printf_console( " RT: backbuffer\n" );
+ #endif
+ glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, gDefaultFBOGL );
+ }
+
+ // If we previously had a mip-mapped render texture, generate mip levels
+ // for it now. Just using GL_GENERATE_MIPMAP does not work on OS X, and the explicit
+ // should probably be faster.
+ if (s_ActiveColorTargets[0] &&
+ (s_ActiveColorTargets[0]->flags & kSurfaceCreateMipmap) &&
+ (s_ActiveColorTargets[0]->flags & kSurfaceCreateAutoGenMips))
+ {
+ const int textureTarget = GetGLTextureDimensionTable()[s_ActiveColorTargets[0]->dim];
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, s_ActiveColorTargets[0]->textureID, s_ActiveColorTargets[0]->dim, std::numeric_limits<float>::infinity());
+ glGenerateMipmapEXT(textureTarget);
+ }
+
+ for (int i = 0; i < count; ++i)
+ s_ActiveColorTargets[i] = reinterpret_cast<RenderColorSurfaceGL*>(colorHandles[i].object);
+ s_ActiveColorTargetCount = count;
+ s_ActiveDepthTarget = rdepth;
+ s_ActiveMip = mipLevel;
+ s_ActiveFace = face;
+ return true;
+}
+
+RenderSurfaceHandle GetActiveRenderColorSurfaceGL(int index)
+{
+ return RenderSurfaceHandle(s_ActiveColorTargets[index]);
+}
+RenderSurfaceHandle GetActiveRenderDepthSurfaceGL()
+{
+ return RenderSurfaceHandle(s_ActiveDepthTarget);
+}
+
+bool IsActiveRenderTargetWithColorGL()
+{
+ return !s_ActiveColorTargets[0] || s_ActiveColorTargets[0]->backBuffer || !IsDepthRTFormat (s_ActiveColorTargets[0]->format);
+}
+
+static int GetBytesFromGLFormat( GLenum fmt )
+{
+ switch(fmt) {
+ case 0: return 0;
+ case GL_RGBA4: return 2;
+ case GL_RGB5: return 2;
+ case GL_RGBA8: return 4;
+ case GL_DEPTH_COMPONENT16: return 2;
+ case GL_DEPTH_COMPONENT24: return 4; // all hardware uses 4 bytes for 24 bit depth
+ case GL_DEPTH_COMPONENT32: return 4;
+ case GL_DEPTH24_STENCIL8_EXT: return 4;
+ default:
+ AssertString("unknown GL format");
+ return 0;
+ }
+}
+
+static void CreateFBORenderColorSurfaceGL (RenderColorSurfaceGL& rs)
+{
+ int textureTarget = GetGLTextureDimensionTable()[rs.dim];
+
+ if( !IsDepthRTFormat(rs.format) )
+ {
+ GLenum internalFormat = (rs.flags & kSurfaceCreateSRGB) ? RTColorInternalFormatSRGBGL(rs.format) : RTColorInternalFormatGL(rs.format);
+ GLenum textureFormat = RTColorTextureFormatGL(rs.format);
+ GLenum textureType = RTColorTextureTypeGL(rs.format);
+
+ // Create texture to render for color
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, rs.textureID, rs.dim, std::numeric_limits<float>::infinity());
+ if (rs.dim == kTexDimCUBE)
+ {
+ // cubemap: initialize all faces
+ for( int f = 0; f < 6; ++f )
+ {
+ glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + f, 0, internalFormat, rs.width, rs.height, 0, textureFormat, textureType, NULL );
+ }
+ if (rs.flags & kSurfaceCreateMipmap)
+ {
+ // establish mip map chain if needed
+ glGenerateMipmapEXT (textureTarget);
+ }
+ }
+ else if (!rs.textureID.m_ID)
+ {
+ // MSAA render buffer
+ glGenRenderbuffersEXT( 1, &rs.m_ColorBuffer );
+ glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, rs.m_ColorBuffer );
+ for (int samples = std::min(rs.samples, gGraphicsCaps.gl.maxSamples); samples >= 1; samples--)
+ {
+ if (samples > 1)
+ {
+ glRenderbufferStorageMultisampleEXT( GL_RENDERBUFFER_EXT, samples, internalFormat, rs.width, rs.height );
+ if (glGetError() == GL_NO_ERROR)
+ break;
+ }
+ else
+ {
+ glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, internalFormat, rs.width, rs.height );
+ GLAssert();
+ }
+ }
+ }
+ else
+ {
+ // regular texture: initialize
+ glTexImage2D( textureTarget, 0, internalFormat, rs.width, rs.height, 0, textureFormat, textureType, NULL );
+ if (rs.flags & kSurfaceCreateMipmap)
+ {
+ // establish mip map chain if needed
+ glGenerateMipmapEXT (textureTarget);
+ }
+ }
+ }
+ else
+ {
+ // Depth render texture
+ // We're prefer to just have no color buffer, but some GL implementations don't like that
+ // (PPC NVIDIA drivers on OS X). So if we have that, we try color buffers starting from
+ // low-memory formats.
+ if (gGraphicsCaps.gl.forceColorBufferWithDepthFBO)
+ {
+ glGenRenderbuffersEXT( 1, &rs.m_ColorBuffer );
+ glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, rs.m_ColorBuffer );
+ glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_RGBA8, rs.width, rs.height );
+ }
+ }
+}
+
+
+static void CreateFBORenderDepthSurfaceGL (RenderDepthSurfaceGL& rs)
+{
+ int textureTarget = GetGLTextureDimensionTable()[kTexDim2D];
+
+ GLenum depthFormat;
+ GLenum genericFormat;
+ GLenum typeFormat;
+ if (rs.depthFormat == kDepthFormat16)
+ {
+ depthFormat = GL_DEPTH_COMPONENT16;
+ genericFormat = GL_DEPTH_COMPONENT;
+ typeFormat = GL_UNSIGNED_BYTE;
+ }
+ else if (gGraphicsCaps.hasRenderTargetStencil && rs.depthFormat == kDepthFormat24)
+ {
+ depthFormat = GL_DEPTH24_STENCIL8_EXT;
+ genericFormat = GL_DEPTH_STENCIL_EXT;
+ typeFormat = GL_UNSIGNED_INT_24_8_EXT;
+ }
+ else
+ {
+ depthFormat = GL_DEPTH_COMPONENT24;
+ genericFormat = GL_DEPTH_COMPONENT;
+ typeFormat = GL_UNSIGNED_BYTE;
+ }
+ if (depthFormat == GL_DEPTH_COMPONENT16 && gGraphicsCaps.gl.force24DepthForFBO)
+ depthFormat = GL_DEPTH_COMPONENT24;
+ if (!rs.textureID.m_ID)
+ {
+ // create just depth surface
+ if( rs.depthFormat != kDepthFormatNone )
+ {
+ glGenRenderbuffersEXT( 1, &rs.m_DepthBuffer );
+ glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, rs.m_DepthBuffer );
+ for (int samples = std::min(rs.samples, gGraphicsCaps.gl.maxSamples); samples >= 1; samples--)
+ {
+ if (samples > 1)
+ {
+ glRenderbufferStorageMultisampleEXT( GL_RENDERBUFFER_EXT, samples, depthFormat, rs.width, rs.height );
+ if (glGetError() == GL_NO_ERROR)
+ break;
+ }
+ else
+ {
+ glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, depthFormat, rs.width, rs.height );
+ GLAssert();
+ }
+ }
+ }
+ }
+ else
+ {
+ // create depth texture
+
+ // If packed depth stencil is buggy as a texture, create just the depth texture. We still need packed depth
+ // stencil for the non-texture case, so do not do this above in the format selection.
+ if (gGraphicsCaps.gl.buggyPackedDepthStencil && genericFormat == GL_DEPTH_STENCIL_EXT)
+ {
+ depthFormat = GL_DEPTH_COMPONENT24;
+ genericFormat = GL_DEPTH_COMPONENT;
+ typeFormat = GL_UNSIGNED_BYTE;
+ }
+
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, rs.textureID, kTexDim2D, std::numeric_limits<float>::infinity());
+ glTexImage2D( textureTarget, 0, depthFormat, rs.width, rs.height, 0, genericFormat, typeFormat, NULL );
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ if (rs.flags & kSurfaceCreateShadowmap)
+ {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
+ }
+ }
+}
+
+
+RenderSurfaceHandle CreateRenderColorSurfaceGL (TextureID textureID, int width, int height, int samples, TextureDimension dim, UInt32 createFlags, RenderTextureFormat format, GLuint globalSharedFBO)
+{
+ RenderSurfaceHandle rsHandle;
+
+ Assert(GetMainGraphicsContext().IsValid());
+
+ if( !gGraphicsCaps.hasRenderToTexture )
+ return rsHandle;
+ if( !gGraphicsCaps.supportsRenderTextureFormat[format] )
+ return rsHandle;
+
+ RenderColorSurfaceGL* rs = new RenderColorSurfaceGL();
+ RenderSurfaceBase_InitColor(*rs);
+ rs->width = width;
+ rs->height = height;
+ rs->samples = samples;
+ rs->format = format;
+ rs->textureID = textureID;
+ rs->dim = dim;
+ rs->flags = createFlags;
+ rs->m_ColorBuffer = 0;
+
+ if(textureID.m_ID)
+ TextureIdMapGL_QueryOrCreate(textureID);
+
+ #if DEBUG_RENDER_TEXTURES
+ printf_console ("RT: create color id=%i, %ix%i, flags=%i, fmt=%i\n", textureID.m_ID, width, height, createFlags, format);
+ #endif
+
+ CreateFBORenderColorSurfaceGL (*rs);
+
+ rsHandle.object = rs;
+ return rsHandle;
+}
+
+
+RenderSurfaceHandle CreateRenderDepthSurfaceGL (TextureID textureID, int width, int height, int samples, UInt32 createFlags, DepthBufferFormat depthFormat, GLuint globalSharedFBO)
+{
+ RenderSurfaceHandle rsHandle;
+
+ Assert(GetMainGraphicsContext().IsValid());
+
+ if( !gGraphicsCaps.hasRenderToTexture )
+ return rsHandle;
+
+ RenderDepthSurfaceGL* rs = new RenderDepthSurfaceGL;
+ RenderSurfaceBase_InitDepth(*rs);
+ rs->width = width;
+ rs->height = height;
+ rs->samples = samples;
+ rs->depthFormat = depthFormat;
+ rs->textureID = textureID;
+ rs->flags = createFlags;
+
+ rs->m_DepthBuffer = 0;
+
+ #if DEBUG_RENDER_TEXTURES
+ printf_console ("RT: create depth id=%i, %ix%i, d=%i flags=%i\n", textureID.m_ID, width, height, depthFormat, createFlags);
+ #endif
+
+ if(textureID.m_ID)
+ TextureIdMapGL_QueryOrCreate(textureID);
+
+ CreateFBORenderDepthSurfaceGL (*rs);
+
+ rsHandle.object = rs;
+ return rsHandle;
+}
+
+
+static void InternalDestroyRenderSurfaceGL (RenderSurfaceBase* rs)
+{
+ Assert(rs);
+
+ #if DEBUG_RENDER_TEXTURES
+ printf_console( "RT: destroy id=%i, %ix%i\n", rs->textureID.m_ID, rs->width, rs->height );
+ #endif
+
+ if (rs->textureID.m_ID)
+ GetRealGfxDevice().DeleteTexture( rs->textureID );
+ GLAssert ();
+
+ RenderSurfaceHandle defaultColor = GetRealGfxDevice().GetBackBufferColorSurface();
+ RenderSurfaceHandle defaultDepth = GetRealGfxDevice().GetBackBufferDepthSurface();
+
+
+ if (s_ActiveDepthTarget == rs)
+ {
+ ErrorString( "RenderTexture warning: Destroying active render texture. Switching to main context." );
+ SetRenderTargetGL (1, &defaultColor, defaultDepth, 0, kCubeFaceUnknown, gDefaultFBOGL);
+ }
+ for (int i = 0; i < s_ActiveColorTargetCount; ++i)
+ {
+ if (s_ActiveColorTargets[i] == rs)
+ {
+ ErrorString( "RenderTexture warning: Destroying active render texture. Switching to main context." );
+ SetRenderTargetGL (1, &defaultColor, defaultDepth, 0, kCubeFaceUnknown, gDefaultFBOGL);
+ }
+ }
+ if (rs->colorSurface)
+ {
+ RenderColorSurfaceGL* rsc = static_cast<RenderColorSurfaceGL*>(rs);
+ if( rsc->m_ColorBuffer )
+ glDeleteRenderbuffersEXT( 1, &rsc->m_ColorBuffer );
+ }
+ else
+ {
+ RenderDepthSurfaceGL* rsd = static_cast<RenderDepthSurfaceGL*>(rs);
+ if( rsd->m_DepthBuffer )
+ glDeleteRenderbuffersEXT( 1, &rsd->m_DepthBuffer );
+ }
+}
+
+static void AssertFBOIsCurrentFBO (GLuint currentFBO)
+{
+#if !UNITY_RELEASE
+ // verify that globalSharedFBO is currently bound,
+ // don't query OpenGL in release mode though
+ GLint realCurrentFBO;
+ glGetIntegerv (GL_FRAMEBUFFER_BINDING_EXT, &realCurrentFBO);
+ DebugAssert (realCurrentFBO == currentFBO);
+#endif
+}
+
+void ResolveColorSurfaceGL (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle, GLuint globalSharedFBO, GLuint helperFBO)
+{
+ Assert (srcHandle.IsValid());
+ Assert (dstHandle.IsValid());
+ RenderColorSurfaceGL* src = reinterpret_cast<RenderColorSurfaceGL*>(srcHandle.object);
+ RenderColorSurfaceGL* dst = reinterpret_cast<RenderColorSurfaceGL*>(dstHandle.object);
+ if (!src->colorSurface || !dst->colorSurface)
+ {
+ WarningString("RenderTexture: Resolving non-color surfaces.");
+ return;
+ }
+
+ GLuint targetTex = (GLuint)TextureIdMapGL_QueryOrCreate(dst->textureID);
+ if (!src->m_ColorBuffer || !targetTex)
+ {
+ WarningString("RenderTexture: Resolving NULL buffers.");
+ return;
+ }
+
+ DebugAssert(gGraphicsCaps.gl.hasFrameBufferBlit);
+ AssertFBOIsCurrentFBO(globalSharedFBO);
+ GLuint currentFBO = globalSharedFBO;
+
+ // attach source render buffer
+ glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, currentFBO );
+ glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, src->m_ColorBuffer );
+
+ // attach destination texture
+ glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, helperFBO );
+ glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, targetTex, 0 );
+
+ // perform blit
+ glBindFramebufferEXT( GL_READ_FRAMEBUFFER_EXT, currentFBO );
+ glBindFramebufferEXT( GL_DRAW_FRAMEBUFFER_EXT, helperFBO );
+
+ // set read & draw buffers to be from FBO color attachemnts
+ glReadBuffer (GL_COLOR_ATTACHMENT0_EXT);
+ glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT);
+
+ glBlitFramebufferEXT( 0, 0, src->width, src->height, 0, 0, dst->width, dst->height, GL_COLOR_BUFFER_BIT, GL_NEAREST );
+ GLAssert();
+
+ // clean up helperFBO
+ glFramebufferTexture2DEXT( GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0 );
+
+ // restore the previously bound FBO
+ glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, currentFBO);
+}
+
+void DestroyRenderSurfaceGL (RenderSurfaceHandle& rsHandle)
+{
+ if(rsHandle.object->backBuffer)
+ return;
+
+ RenderSurfaceBase* rs = rsHandle.object;
+ InternalDestroyRenderSurfaceGL (rs);
+ delete rs;
+ rsHandle.object = NULL;
+}
+
+void ResolveDepthIntoTextureGL (RenderSurfaceHandle colorHandle, RenderSurfaceHandle depthHandle, GLuint globalSharedFBO, GLuint helperFBO)
+{
+ RenderDepthSurfaceGL* rdepth = reinterpret_cast<RenderDepthSurfaceGL*>(depthHandle.object);
+
+ // use the full size of the depth buffer, sub-rects are not needed and might not work on some hardware
+ GLint x = 0;
+ GLint y = 0;
+ GLint width = rdepth->width;
+ GLint height = rdepth->height;
+
+ if (gGraphicsCaps.gl.hasFrameBufferBlit)
+ {
+ AssertFBOIsCurrentFBO (globalSharedFBO);
+ GLuint currentFBO = globalSharedFBO;
+
+ // bind helper FBO
+ glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, helperFBO);
+
+ // attach the color buffer if needed (see gGraphicsCaps.gl.forceColorBufferWithDepthFBO)
+ RenderColorSurfaceGL* rcolor = reinterpret_cast<RenderColorSurfaceGL*>(colorHandle.object);
+ const bool attachColorBuffer = (!rcolor->textureID.m_ID && rcolor->m_ColorBuffer);
+
+ if (attachColorBuffer)
+ glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, rcolor->m_ColorBuffer);
+
+ // attach the depth buffer
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(rdepth->textureID);
+ glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, targetTex, 0);
+ glDrawBuffer (GL_NONE);
+ glReadBuffer (GL_NONE);
+
+ // check the FBO
+ GLenum framebufferStatus = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
+ AssertMsg (framebufferStatus == GL_FRAMEBUFFER_COMPLETE_EXT, GetFBOStatusError (framebufferStatus));
+
+ // blit
+ glBindFramebufferEXT (GL_READ_FRAMEBUFFER_EXT, currentFBO);
+ glBindFramebufferEXT (GL_DRAW_FRAMEBUFFER_EXT, helperFBO);
+ glBlitFramebufferEXT (x, y, x + width, y + height, x, y, x + width, y + height, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
+
+ // clean up helperFBO
+ glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
+ if (attachColorBuffer)
+ glFramebufferRenderbufferEXT (GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, 0);
+
+ // restore the previously bound FBO
+ glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, currentFBO);
+ }
+ else
+ {
+ GetRealGfxDevice ().SetTexture (kShaderFragment, 0, 0, rdepth->textureID, kTexDim2D, std::numeric_limits<float>::infinity ());
+ glCopyTexSubImage2D (GL_TEXTURE_2D, 0, x, y, x, y, width, height);
+ }
+ GLAssert();
+}
+
+void GrabIntoRenderTextureGL (RenderSurfaceHandle rsHandle, int x, int y, int width, int height, GLuint helperFBO)
+{
+ if( !rsHandle.IsValid() )
+ return;
+
+ RenderColorSurfaceGL* rs = reinterpret_cast<RenderColorSurfaceGL*>( rsHandle.object );
+
+ // If we are not using the back buffer, we cannot copy using glTexSubImage2D
+ // when we have a proxy FBO with FSAA. This also affects Safari with
+ // the CoreAnimation drawing model.
+ // So, instead we blit from the proxy FBO to a helper FBO with our output texture attached.
+ // (Related issue in GLReadPixelsWrapper())
+ GLint curFBO;
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &curFBO);
+ if (gDefaultFBOGL != 0 && gDefaultFBOGL == curFBO && gGraphicsCaps.gl.hasFrameBufferBlit)
+ {
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(rs->textureID);
+
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, helperFBO);
+ glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, targetTex, 0 );
+
+ glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, gDefaultFBOGL);
+ glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, helperFBO);
+
+ // Ensure that color0 is always set here,
+ // because we attach to color0 when setting up the FBO for scaled fullscreen mode
+ glReadBuffer (GL_COLOR_ATTACHMENT0_EXT);
+ glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT);
+
+ glBlitFramebufferEXT(x, y, x+width, y+height, x, y, x+width, y+height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ // clean up helperFBO
+ glFramebufferTexture2DEXT (GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0 );
+
+ glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, curFBO );
+ }
+ else
+ {
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, rs->textureID, kTexDim2D, std::numeric_limits<float>::infinity());
+ glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
+ glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+ glCopyTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, x, y, width, height);
+ }
+ GLAssert ();
+}
+
+
+RenderSurfaceBase* CreateBackBufferColorSurfaceGL()
+{
+ RenderColorSurfaceGL* rs = new RenderColorSurfaceGL();
+ ::memset(rs, 0x00, sizeof(RenderColorSurfaceGL));
+ RenderSurfaceBase_InitColor(*rs);
+ rs->backBuffer = true;
+
+ return rs;
+}
+RenderSurfaceBase* CreateBackBufferDepthSurfaceGL()
+{
+ RenderDepthSurfaceGL* rs = new RenderDepthSurfaceGL();
+ ::memset(rs, 0x00, sizeof(RenderDepthSurfaceGL));
+ RenderSurfaceBase_InitDepth(*rs);
+ rs->backBuffer = true;
+
+ return rs;
+}
+
diff --git a/Runtime/GfxDevice/opengl/TextureIdMapGL.h b/Runtime/GfxDevice/opengl/TextureIdMapGL.h
new file mode 100644
index 0000000..3781901
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/TextureIdMapGL.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/GfxDevice/TextureIdMap.h"
+
+inline GLuint TextureIdMapGL_QueryOrCreate(TextureID texid)
+{
+ GLuint ret = (GLuint)TextureIdMap::QueryNativeTexture(texid);
+ if(ret == 0)
+ {
+ glGenTextures(1, &ret);
+ TextureIdMap::UpdateTexture(texid, ret);
+ }
+
+ return ret;
+}
diff --git a/Runtime/GfxDevice/opengl/TexturesGL.cpp b/Runtime/GfxDevice/opengl/TexturesGL.cpp
new file mode 100644
index 0000000..8d79219
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/TexturesGL.cpp
@@ -0,0 +1,494 @@
+#include "UnityPrefix.h"
+#include "TexturesGL.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Graphics/S3Decompression.h"
+#include "UnityGL.h"
+#include "GLAssert.h"
+#include "TextureIdMapGL.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/VramLimits.h"
+#include "Runtime/GfxDevice/TextureUploadUtils.h"
+
+
+// define to 1 to print lots of texture creation/destroy info
+#define DEBUG_GL_TEXTURE 0
+
+static inline bool SupportsS3Compression () { return gGraphicsCaps.hasS3TCCompression; }
+
+const static int kTextureFormatTable[kTexFormatPCCount*4] =
+{
+ // internal format, internal format sRGB, input format, datatype
+ 0, 0, 0, 0,
+ GL_ALPHA, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, // Alpha8
+ GL_RGBA, GL_RGBA, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV, // ARGB4444
+ GL_RGB, GL_SRGB8_EXT, GL_RGB, GL_UNSIGNED_BYTE, // RGB24
+ GL_RGBA, GL_SRGB8_ALPHA8_EXT, GL_RGBA, GL_UNSIGNED_BYTE, // RGBA32
+#if UNITY_BIG_ENDIAN
+ GL_RGBA, GL_SRGB8_ALPHA8_EXT, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, // ARGB32
+#else
+ GL_RGBA, GL_SRGB8_ALPHA8_EXT, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, // ARGB32
+#endif
+ 0, 0, 0, 0, // ARGBFloat
+ GL_RGB, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, // RGB565 ///////@TODO: What to do with 16 bit textures?????
+ GL_RGB, GL_SRGB8_EXT, GL_BGR, GL_UNSIGNED_BYTE, // BGR24
+ GL_ALPHA16, GL_ALPHA16, GL_ALPHA, GL_UNSIGNED_SHORT, // AlphaLum16
+ GL_COMPRESSED_RGB_S3TC_DXT1_EXT,GL_COMPRESSED_SRGB_S3TC_DXT1_EXT, -1, -1, // DXT1
+ GL_COMPRESSED_RGBA_S3TC_DXT3_EXT,GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, -1, -1,// DXT3
+ GL_COMPRESSED_RGBA_S3TC_DXT5_EXT,GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, -1, -1,// DXT5
+ GL_RGBA, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, // RGBA4444
+};
+
+
+static void GetUncompressedTextureFormat (int inFormat, bool sRGB, int* internalFormat, int* inputFormat, int* dataType)
+{
+ DebugAssertIf( IsAnyCompressedTextureFormat(inFormat) );
+ *internalFormat = kTextureFormatTable[inFormat*4+(sRGB ? 1 : 0)];
+ *inputFormat = kTextureFormatTable[inFormat*4+2];
+ *dataType = kTextureFormatTable[inFormat*4+3];
+}
+
+static void GetCompressedTextureFormat (int inFormat, bool sRGB, int width, int height, int* internalFormat, int* size)
+{
+ *internalFormat = kTextureFormatTable[inFormat*4+(sRGB ? 1 : 0)];
+ switch (inFormat)
+ {
+ case kTexFormatDXT1:
+ case kTexFormatATC_RGB4:
+ *size = ((width + 3) / 4) * ((height + 3) / 4) * 8;
+ break;
+ case kTexFormatDXT3:
+ case kTexFormatDXT5:
+ case kTexFormatATC_RGBA8:
+ *size = ((width + 3) / 4) * ((height + 3) / 4) * 16;
+ break;
+ case kTexFormatETC_RGB4:
+ *size = 8 * ((width+3)>>2) * ((height+3)>>2); // 8 bytes per 4x4 block
+ break;
+ default:
+ Assert(false && "Texture is not compressed");
+ }
+}
+
+#if THREADED_LOADING_DEBUG
+float totalTextureUploadTime = 0.0F;
+double GetTimeSinceStartup();
+#endif
+
+
+
+
+void UploadTexture2DGL(
+ TextureID tid, TextureDimension dimension, UInt8* srcData, int width, int height,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, int masterTextureLimit, TextureUsageMode usageMode, TextureColorSpace colorSpace)
+{
+#if THREADED_LOADING_DEBUG
+ double begin = GetTimeSinceStartup();
+#endif
+
+ AssertIf( srcData == NULL );
+ AssertIf( (!IsPowerOfTwo(width) || !IsPowerOfTwo(height)) && !IsNPOTTextureAllowed(mipCount > 1) );
+ if( dimension != kTexDim2D )
+ {
+ ErrorString( "Incorrect texture dimension!" );
+ return;
+ }
+
+ bool uploadIsCompressed, decompressOnTheFly;
+ HandleFormatDecompression (format, &usageMode, colorSpace, &uploadIsCompressed, &decompressOnTheFly);
+
+ TextureFormat uploadFormat;
+ if( decompressOnTheFly )
+ {
+ uploadFormat = kTexFormatRGBA32;
+ uploadIsCompressed = false;
+ }
+ else
+ {
+ uploadFormat = format;
+
+ if (usageMode == kTexUsageLightmapDoubleLDR || usageMode == kTexUsageNormalmapPlain)
+ {
+ // make sure we have a format with alpha
+ uploadFormat = kTexFormatRGBA32;
+ }
+
+ uploadIsCompressed = IsAnyCompressedTextureFormat( format );
+ }
+
+ int baseLevel, maxLevel, texWidth, texHeight;
+ size_t textureSize;
+ SkipLevelsForMasterTextureLimit (masterTextureLimit, format, uploadFormat, mipCount, uploadIsCompressed, &srcData, &width, &height, &baseLevel, &maxLevel, &texWidth, &texHeight, &textureSize);
+
+ if (!glIsTexture(tid.m_ID))
+ uploadFlags |= GfxDevice::kUploadTextureDontUseSubImage;
+
+#if DEBUG_GL_TEXTURE
+ printf_console( "GLDebug texture: upload %i (%ix%i). Unity format: %d\n", tid, texWidth, texHeight ,format );
+#endif
+ GLAssert ();
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(tid.m_ID);
+
+ TextureIdMapGL_QueryOrCreate(tid);
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, tid, dimension, std::numeric_limits<float>::infinity());
+
+ glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
+ glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+
+ // glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
+
+ UInt8* decompressBuffer = NULL;
+ UInt8* tempBuffer = NULL;
+ UInt8* scaleBuffer = NULL;
+
+ int bufferPitch;
+
+ int internalFormat = 0, datatype = 0, inputFormat = 0, size = 0;
+ int uploadedSize = 0;
+ for( int level = baseLevel; level <= maxLevel; ++level )
+ {
+ UInt8* feedSourceData;
+ const int uploadLevel = level-baseLevel;
+
+ if( decompressOnTheFly )
+ {
+ ConvertCompressedTextureUpload (width, height, format, srcData, decompressBuffer, bufferPitch, usageMode, colorSpace, level);
+ feedSourceData = decompressBuffer;
+ }
+ // Allocate temporary memory and swizzle texture
+ else if (uploadFormat != format || usageMode != kTexUsageNone)
+ {
+ InitImageBuffer (width, height, decompressBuffer, uploadFormat);
+ bufferPitch = GetRowBytesFromWidthAndFormat(width, uploadFormat);
+ feedSourceData = decompressBuffer;
+
+ prcore::Surface srcSurface (width, height, GetRowBytesFromWidthAndFormat (width, format), GetProphecyPixelFormat(format), srcData);
+ prcore::Surface dstSurface (width, height, GetRowBytesFromWidthAndFormat (width, uploadFormat), GetProphecyPixelFormat(uploadFormat), feedSourceData);
+
+ if (!ConvertUncompressedTextureUpload(srcSurface, dstSurface, prcore::Surface::BLIT_COPY, uploadFormat, usageMode, colorSpace, width, height, feedSourceData, bufferPitch, GetProphecyPixelFormat(uploadFormat), tempBuffer, bufferPitch))
+ {
+ dstSurface.BlitImage( srcSurface, prcore::Surface::BLIT_COPY );
+ }
+ }
+ // Just feed the data
+ else
+ {
+ feedSourceData = srcData;
+ }
+
+ // Don't use SubImage when uploading smaller than 4*4 compressed texture
+ if( uploadIsCompressed && (texWidth < 4 || texHeight < 4) )
+ uploadFlags |= GfxDevice::kUploadTextureDontUseSubImage;
+
+ // Now, if we're uploading non-mipmapped texture that is too large, the actual
+ // data has to be downscaled.
+ UInt8* feedRealData = feedSourceData;
+ if( !uploadIsCompressed && (width != texWidth || height != texHeight) ) {
+ if( !scaleBuffer )
+ scaleBuffer = new UInt8[CalculateImageSize( texWidth, texHeight, uploadFormat )];
+ feedRealData = scaleBuffer;
+ prcore::Surface srcSurface( width, height, GetRowBytesFromWidthAndFormat(width,uploadFormat), GetProphecyPixelFormat(uploadFormat), feedSourceData );
+ prcore::Surface dstSurface( texWidth, texHeight, GetRowBytesFromWidthAndFormat(texWidth,uploadFormat), GetProphecyPixelFormat(uploadFormat), feedRealData );
+ dstSurface.BlitImage( srcSurface, prcore::Surface::BLIT_SCALE );
+ }
+
+ // Complete texture image
+ if (uploadFlags & GfxDevice::kUploadTextureDontUseSubImage)
+ {
+ if (uploadIsCompressed)
+ {
+ GetCompressedTextureFormat (uploadFormat, colorSpace == kTexColorSpaceSRGBXenon || colorSpace == kTexColorSpaceSRGB, texWidth, texHeight, &internalFormat, &size);
+#if DEBUG_GL_TEXTURE
+ printf_console( "GLDebug texture: glCompressedTexImage2DARB: level=%i ifmt=%i width=%i height=%i size=%i data=%x\n", uploadLevel, internalFormat, texWidth, texHeight, size, (UInt32)feedRealData );
+#endif
+ glCompressedTexImage2DARB (GL_TEXTURE_2D, uploadLevel, internalFormat, texWidth, texHeight, 0, size, feedRealData);
+ }
+ else
+ {
+ GetUncompressedTextureFormat (uploadFormat, colorSpace == kTexColorSpaceSRGBXenon || colorSpace == kTexColorSpaceSRGB, &internalFormat, &inputFormat, &datatype);
+#if DEBUG_GL_TEXTURE
+ printf_console( "GLDebug texture: glTexImage2D: level=%i ifmt=%i width=%i height=%i fmt=%i type=%i data=%x\n", uploadLevel, internalFormat, texWidth, texHeight, inputFormat,datatype, (UInt32)feedRealData );
+#endif
+ glTexImage2D (GL_TEXTURE_2D, uploadLevel, internalFormat, texWidth, texHeight, 0, inputFormat, datatype, feedRealData);
+ }
+ }
+ // Upload texture with subimage
+ else
+ {
+ if (uploadIsCompressed)
+ {
+ GetCompressedTextureFormat (uploadFormat, colorSpace == kTexColorSpaceSRGBXenon || colorSpace == kTexColorSpaceSRGB, texWidth, texHeight, &internalFormat, &size);
+#if DEBUG_GL_TEXTURE
+ printf_console( "GLDebug texture: glCompressedTexSubImage2DARB: level=%i width=%i height=%i fmt=%i size=%i data=%x\n", uploadLevel, texWidth, texHeight, internalFormat, size, (UInt32)feedRealData );
+#endif
+ glCompressedTexSubImage2DARB (GL_TEXTURE_2D, uploadLevel, 0, 0, texWidth, texHeight, internalFormat, size, feedRealData);
+ }
+ else
+ {
+ GetUncompressedTextureFormat (uploadFormat, colorSpace == kTexColorSpaceSRGBXenon || colorSpace == kTexColorSpaceSRGB, &internalFormat, &inputFormat, &datatype);
+#if DEBUG_GL_TEXTURE
+ printf_console( "GLDebug texture: glTexSubImage2D: level=%i width=%i height=%i fmt=%i type=%i data=%x\n", uploadLevel, texWidth, texHeight, inputFormat,datatype, (UInt32)feedRealData );
+#endif
+ glTexSubImage2D (GL_TEXTURE_2D, uploadLevel, 0, 0, texWidth, texHeight, inputFormat, datatype, feedRealData);
+ }
+ }
+ uploadedSize += CalculateImageSize( texWidth, texHeight, uploadFormat );
+ // Go to next mip level
+ AssertIf( width == 1 && height == 1 && level != maxLevel );
+ AdvanceToNextMipLevel (format, srcData, width, height, texWidth, texHeight);
+ }
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(tid.m_ID,uploadedSize,tid.m_ID);
+ AssertIf( baseLevel > maxLevel );
+
+ GLAssert ();
+
+ delete[] decompressBuffer;
+ delete[] tempBuffer;
+ delete[] scaleBuffer;
+
+ // glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
+
+#if THREADED_LOADING_DEBUG
+ totalTextureUploadTime += GetTimeSinceStartup() - begin;
+ printf_console("Total Texture upload time %f\n", totalTextureUploadTime);
+#endif
+}
+
+
+void UploadTextureSubData2DGL(
+ TextureID tid, UInt8* srcData,
+ int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace )
+{
+ AssertIf( IsAnyCompressedTextureFormat( format ) );
+
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(tid);
+
+ Assert(targetTex != 0);
+ if(targetTex == 0)
+ return;
+
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, tid, kTexDim2D, std::numeric_limits<float>::infinity());
+
+ TextureFormat uploadFormat = format;
+
+ UInt8* decompressBuffer = NULL;
+
+ UInt8* feedData = srcData;
+ // Allocate temporary memory and swizzle texture
+ if( uploadFormat != format )
+ {
+ int decompressedSize = CalculateImageSize (width, height, uploadFormat);
+ if (decompressBuffer == NULL)
+ decompressBuffer = new UInt8[decompressedSize];
+ feedData = decompressBuffer;
+
+ ImageReference src (width, height, GetRowBytesFromWidthAndFormat (width, format), format, srcData);
+ ImageReference dst (width, height, GetRowBytesFromWidthAndFormat (width, uploadFormat), uploadFormat, feedData);
+ dst.BlitImage( src );
+ }
+
+ glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
+ glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+
+ int internalFormat, datatype, inputFormat;
+ GetUncompressedTextureFormat (uploadFormat, colorSpace == kTexColorSpaceSRGBXenon || colorSpace == kTexColorSpaceSRGB, &internalFormat, &inputFormat, &datatype);
+ glTexSubImage2D( GL_TEXTURE_2D, mipLevel, x, y, width, height, inputFormat, datatype, feedData );
+
+ GLAssert ();
+
+ if( decompressBuffer )
+ delete[] decompressBuffer;
+}
+
+
+void UploadTextureCubeGL(
+ TextureID tid, UInt8* srcData, int faceDataSize, int size,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace )
+{
+ if (!glIsTexture(tid.m_ID))
+ uploadFlags |= GfxDevice::kUploadTextureDontUseSubImage;
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(tid.m_ID);
+
+ TextureIdMapGL_QueryOrCreate(tid);
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, tid, kTexDimCUBE, std::numeric_limits<float>::infinity());
+
+ glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
+ glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+
+ // We have a compressed format but the hardware doesnt support it.
+ // - setup format to be uncompressed
+ // - dont use client storage (we are using temporary memory to store the texture)
+ TextureFormat uploadFormat;
+ bool uploadIsCompressed;
+ const bool decompressOnTheFly = IsAnyCompressedTextureFormat(format) && !gGraphicsCaps.hasS3TCCompression;
+ if( decompressOnTheFly )
+ {
+ uploadFormat = kTexFormatRGBA32;
+ uploadIsCompressed = false;
+ }
+ else
+ {
+ uploadFormat = format;
+ uploadIsCompressed = IsAnyCompressedTextureFormat( format );
+ }
+
+ UInt8* decompressBuffer = NULL;
+
+ const GLenum faces[6] =
+ {
+ GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
+ GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
+ GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
+ };
+
+ int maxLevel = mipCount - 1;
+ int uploadSize = 0;
+ for (int face=0;face<6;face++)
+ {
+ int mipSize = size;
+ UInt8* data = srcData + face * faceDataSize;
+ int internalFormat = 0, datatype = 0, inputFormat = 0, size = 0;
+
+ for( int level=0;level<=maxLevel;level++ )
+ {
+ UInt8* feedData;
+
+ // Allocate temporary memory and decompress texture
+ if( decompressOnTheFly )
+ {
+ int dstSize = std::max( mipSize, 4 );
+ int decompressedSize = CalculateImageSize( dstSize, dstSize, uploadFormat );
+ if( decompressBuffer == NULL )
+ decompressBuffer = new UInt8[decompressedSize];
+ feedData = decompressBuffer;
+
+ DecompressNativeTextureFormat( format, mipSize, mipSize, (UInt32*)data, dstSize, dstSize, (UInt32*)feedData );
+ }
+ // Allocate temporary memory and swizzle texture
+ else if( uploadFormat != format )
+ {
+ int decompressedSize = CalculateImageSize (mipSize, mipSize, uploadFormat);
+ if (decompressBuffer == NULL)
+ decompressBuffer = new UInt8[decompressedSize];
+ feedData = decompressBuffer;
+
+ ImageReference src (mipSize, mipSize, GetRowBytesFromWidthAndFormat (mipSize, format), format, data);
+ ImageReference dst (mipSize, mipSize, GetRowBytesFromWidthAndFormat (mipSize, uploadFormat), uploadFormat, feedData);
+ dst.BlitImage( src );
+ }
+ // Just feed the data
+ else
+ {
+ feedData = data;
+ }
+
+ // Upload
+ if( uploadIsCompressed )
+ {
+ GetCompressedTextureFormat( uploadFormat, colorSpace == kTexColorSpaceSRGBXenon || colorSpace == kTexColorSpaceSRGB, mipSize, mipSize, &internalFormat, &size );
+ glCompressedTexImage2DARB (faces[face], level, internalFormat, mipSize, mipSize, 0, size, feedData);
+ }
+ else
+ {
+ GetUncompressedTextureFormat( uploadFormat, colorSpace == kTexColorSpaceSRGBXenon || colorSpace == kTexColorSpaceSRGB, &internalFormat, &inputFormat, &datatype );
+ glTexImage2D (faces[face], level, internalFormat, mipSize, mipSize, 0, inputFormat, datatype, feedData);
+ }
+
+ GLAssert ();
+ int levelSize = CalculateImageSize( mipSize, mipSize, format );
+ uploadSize += levelSize;
+ data += levelSize;
+ AssertIf( mipSize == 1 && level != maxLevel );
+
+ mipSize = std::max( mipSize / 2, 1 );
+ }
+ }
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(tid.m_ID,uploadSize,tid.m_ID);
+ if( decompressBuffer )
+ delete[] decompressBuffer;
+}
+
+
+void UploadTexture3DGL(
+ TextureID tid, UInt8* srcData, int width, int height, int depth,
+ TextureFormat format, int mipCount, UInt32 uploadFlags )
+{
+ if (!gGraphicsCaps.has3DTexture)
+ return;
+
+ if (!glIsTexture(tid.m_ID))
+ uploadFlags |= GfxDevice::kUploadTextureDontUseSubImage;
+
+#if DEBUG_GL_TEXTURE
+ printf_console( "GLDebug texture 3D: upload %i (%ix%ix%i)\n", tid, width, height, depth );
+#endif
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(tid.m_ID);
+
+ TextureIdMapGL_QueryOrCreate(tid);
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, tid, kTexDim3D, std::numeric_limits<float>::infinity());
+
+ glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
+ glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+
+ int internalFormat, datatype, inputFormat;
+ int maxLevel = mipCount - 1;
+ int bpp = GetBytesFromTextureFormat(format);
+ int uploadSize = 0;
+
+ for (int level=0;level<=maxLevel;level++)
+ {
+ GetUncompressedTextureFormat (format, false, &internalFormat, &inputFormat, &datatype);
+#if DEBUG_GL_TEXTURE
+ printf_console( "GLDebug texture 3D: glTexImage3D: level=%i ifmt=%i width=%i height=%i depth=%i fmt=%i type=%i data=%x\n", level, internalFormat, width, height, depth, inputFormat,datatype, (UInt32)srcData );
+#endif
+ glTexImage3D (GL_TEXTURE_3D, level, internalFormat, width, height, depth, 0, inputFormat, datatype, srcData);
+ GLAssert ();
+ uploadSize += width*height*depth*bpp;
+
+ // Go to next level
+ srcData += CalculateImageSize(width, height, format) * depth;
+ width = std::max(width / 2, 1);
+ height = std::max(height / 2, 1);
+ depth = std::max(depth / 2, 1);
+ }
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(tid.m_ID,uploadSize,tid.m_ID);
+}
+
+
+void GLReadPixelsWrapper (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);
+
+bool ReadbackTextureGL( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY )
+{
+ int internalFormat, datatype, inputFormat;
+ GetUncompressedTextureFormat( image.GetFormat(), false, &internalFormat, &inputFormat, &datatype );
+
+ // The whole image we're reading into can be larger than the rect we read.
+ // So setup the alignment.
+ glPixelStorei( GL_PACK_ROW_LENGTH, image.GetRowBytes() / GetBytesFromTextureFormat(image.GetFormat()) );
+ glPixelStorei( GL_PACK_ALIGNMENT, 1 );
+
+ switch( image.GetFormat() ) {
+ case kTexFormatARGB32:
+ GLReadPixelsWrapper(left,bottom,width,height,inputFormat,datatype,image.GetRowPtr(destY) + destX * 4);
+ break;
+ case kTexFormatRGB24:
+ GLReadPixelsWrapper(left,bottom,width,height,inputFormat,datatype,image.GetRowPtr(destY) + destX * 3);
+ break;
+ case kTexFormatAlpha8:
+ GLReadPixelsWrapper(left,bottom,width,height,inputFormat,datatype,image.GetRowPtr(destY) + destX);
+ break;
+ default:
+ AssertString ("Not Supported");
+ glPixelStorei( GL_PACK_ROW_LENGTH, 0 );
+ return false;
+ }
+
+ glPixelStorei( GL_PACK_ROW_LENGTH, 0 );
+
+ return true;
+}
diff --git a/Runtime/GfxDevice/opengl/TexturesGL.h b/Runtime/GfxDevice/opengl/TexturesGL.h
new file mode 100644
index 0000000..746c342
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/TexturesGL.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "Runtime/Graphics/TextureFormat.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Configuration/UnityConfigure.h"
+
+class ImageReference;
+
+void UploadTexture2DGL(
+ TextureID tid, TextureDimension dimension, UInt8* srcData, int width, int height,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, int masterTextureLimit, TextureUsageMode usageMode, TextureColorSpace colorSpace );
+
+void UploadTextureSubData2DGL(
+ TextureID tid, UInt8* srcData,
+ int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace );
+
+void UploadTextureCubeGL(
+ TextureID tid, UInt8* srcData, int faceDataSize, int size,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace );
+
+void UploadTexture3DGL(
+ TextureID tid, UInt8* srcData, int width, int height, int depth,
+ TextureFormat format, int mipCount, UInt32 uploadFlags );
+
+bool ReadbackTextureGL( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY );
diff --git a/Runtime/GfxDevice/opengl/TimerQueryGL.cpp b/Runtime/GfxDevice/opengl/TimerQueryGL.cpp
new file mode 100644
index 0000000..8f218d8
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/TimerQueryGL.cpp
@@ -0,0 +1,162 @@
+#include "UnityPrefix.h"
+#if ENABLE_PROFILER
+#include "UnityGL.h"
+#include "TimerQueryGL.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+
+TimerQueryGL::TimerQueryGL()
+{
+ m_Query = g_TimerQueriesGL.AllocateQuery();
+}
+
+TimerQueryGL::~TimerQueryGL()
+{
+ g_TimerQueriesGL.ReleaseQuery(m_Query);
+}
+
+void TimerQueryGL::Measure()
+{
+ // Finish previous timer query
+ OGL_CALL(glEndQueryARB(GL_TIME_ELAPSED_EXT));
+
+ MeasureBegin();
+}
+
+void TimerQueryGL::MeasureBegin()
+{
+ // Flush previous result
+ GetElapsed(kWaitRenderThread);
+
+ OGL_CALL(glBeginQueryARB(GL_TIME_ELAPSED_EXT, m_Query));
+
+ g_TimerQueriesGL.AddActiveTimerQuery(this);
+ m_Time = kInvalidProfileTime;
+}
+
+ProfileTimeFormat TimerQueryGL::GetElapsed(UInt32 flags)
+{
+ bool wait = (flags & kWaitRenderThread) != 0;
+ // We need to return a valid time if waiting
+ if (wait && m_Time == kInvalidProfileTime)
+ m_Time = 0;
+ while (IsInList())
+ {
+ if (!g_TimerQueriesGL.PollNextTimerQuery(wait))
+ break;
+ }
+ return m_Time;
+}
+
+bool TimerQueryGL::PollResult(UInt64& prevTime, bool wait)
+{
+ for (;;)
+ {
+ // Currently we always wait on result
+ //GLint available = 0;
+ //OGL_CALL(glGetQueryObjectivARB(m_Query, GL_QUERY_RESULT_AVAILABLE, &available));
+ //if (available)
+ {
+ GLuint64EXT time;
+ OGL_CALL(glGetQueryObjectui64vEXT(m_Query, GL_QUERY_RESULT, &time));
+ // Some Nvidia cards return invalid results, sanity check!
+ if (time > GLuint64EXT(0xffffffff))
+ gGraphicsCaps.buggyTimerQuery = true;
+ // We actually want previous query's time elapsed
+ // Save current returned result for next query
+ m_Time = prevTime;
+ prevTime = time;
+ return true;
+ }
+ if (!wait)
+ break;
+ }
+ return false;
+}
+
+TimerQueriesGL::TimerQueriesGL()
+{
+ memset(m_FreeQueries, 0, sizeof(m_FreeQueries));
+ m_NumFreeQueries = 0;
+ m_LastQueryTime = 0;
+ memset(m_StartTimeQueries, 0, sizeof(m_StartTimeQueries));
+ m_StartTimeQueryIndex = 0;
+ m_Active = false;
+}
+
+GLuint TimerQueriesGL::AllocateQuery()
+{
+ if (m_NumFreeQueries == 0)
+ {
+ OGL_CALL(glGenQueriesARB(kMaxFreeQueries, m_FreeQueries));
+ m_NumFreeQueries = kMaxFreeQueries;
+ }
+ return m_FreeQueries[--m_NumFreeQueries];
+}
+
+void TimerQueriesGL::ReleaseQuery(GLuint query)
+{
+ if (m_NumFreeQueries == kMaxFreeQueries)
+ {
+ OGL_CALL(glDeleteQueriesARB(kMaxFreeQueries, m_FreeQueries));
+ m_NumFreeQueries = 0;
+ }
+ m_FreeQueries[m_NumFreeQueries++] = query;
+}
+
+void TimerQueriesGL::AddActiveTimerQuery(TimerQueryGL* query)
+{
+ m_ActiveTimerQueries.push_back(*query);
+}
+
+void TimerQueriesGL::BeginTimerQueries()
+{
+ Assert(!m_Active);
+ int& index = m_StartTimeQueryIndex;
+ if(m_StartTimeQueries[index] == NULL)
+ {
+ m_StartTimeQueries[index] = new TimerQueryGL;
+ }
+ m_StartTimeQueries[index]->MeasureBegin();
+ index = (index + 1) % kStartTimeQueryCount;
+ m_Active = true;
+}
+
+void TimerQueriesGL::EndTimerQueries()
+{
+ Assert(m_Active);
+ OGL_CALL(glEndQueryARB(GL_TIME_ELAPSED_EXT));
+ OGL_CALL(glFlush());
+
+ // Move queries from active to polled list
+ m_PolledTimerQueries.append(m_ActiveTimerQueries);
+
+ g_TimerQueriesGL.PollTimerQueries(true);
+ m_Active = false;
+}
+
+void TimerQueriesGL::PollTimerQueries(bool wait)
+{
+ for (;;)
+ {
+ if (!PollNextTimerQuery(wait))
+ break;
+ }
+}
+
+bool TimerQueriesGL::PollNextTimerQuery(bool wait)
+{
+ if (m_PolledTimerQueries.empty())
+ return false;
+
+ TimerQueryGL& query = m_PolledTimerQueries.front();
+ if (query.PollResult(m_LastQueryTime, wait))
+ {
+ m_PolledTimerQueries.pop_front();
+ return true;
+ }
+ return false;
+}
+
+TimerQueriesGL g_TimerQueriesGL;
+
+#endif
diff --git a/Runtime/GfxDevice/opengl/TimerQueryGL.h b/Runtime/GfxDevice/opengl/TimerQueryGL.h
new file mode 100644
index 0000000..eb95f56
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/TimerQueryGL.h
@@ -0,0 +1,63 @@
+#ifndef TIMERQUERYGL_H
+#define TIMERQUERYGL_H
+
+#if ENABLE_PROFILER
+
+#include "Runtime/GfxDevice/GfxTimerQuery.h"
+
+class TimerQueryGL : public GfxTimerQuery
+{
+public:
+ TimerQueryGL();
+ ~TimerQueryGL();
+
+ virtual void Measure();
+ void MeasureBegin();
+ virtual ProfileTimeFormat GetElapsed(UInt32 flags);
+
+ bool PollResult(UInt64& prevTime, bool wait);
+
+private:
+ GLuint m_Query;
+ ProfileTimeFormat m_Time;
+};
+
+class TimerQueriesGL
+{
+public:
+ TimerQueriesGL();
+
+ GLuint AllocateQuery();
+ void ReleaseQuery(GLuint query);
+
+ void BeginTimerQueries();
+ void EndTimerQueries();
+
+ bool IsActive() const { return m_Active; }
+
+ void AddActiveTimerQuery(TimerQueryGL* query);
+ void PollTimerQueries(bool wait);
+ bool PollNextTimerQuery(bool wait);
+
+private:
+ enum
+ {
+ kStartTimeQueryCount = 3,
+ kMaxFreeQueries = 128
+ };
+
+ GLuint m_FreeQueries[kMaxFreeQueries];
+ int m_NumFreeQueries;
+ UInt64 m_LastQueryTime;
+ TimerQueryGL* m_StartTimeQueries[kStartTimeQueryCount];
+ int m_StartTimeQueryIndex;
+ typedef List<TimerQueryGL> TimerQueryList;
+ TimerQueryList m_ActiveTimerQueries;
+ TimerQueryList m_PolledTimerQueries;
+ bool m_Active;
+};
+
+extern TimerQueriesGL g_TimerQueriesGL;
+
+#endif
+#endif
diff --git a/Runtime/GfxDevice/opengl/UnityGL.h b/Runtime/GfxDevice/opengl/UnityGL.h
new file mode 100644
index 0000000..e7f4bb2
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/UnityGL.h
@@ -0,0 +1,176 @@
+#ifndef UNITYGL_H
+#define UNITYGL_H
+
+#ifdef GLES_INCLUDES_H
+# error "Don't include UnityGL.h and GLESIncludes.h at the same time!"
+#endif
+
+#if UNITY_OSX
+ #include "unity_gl.h"
+ #undef GL_VERSION_1_2
+ #include "unity_glext.h"
+#elif UNITY_WIN
+ #undef NOMINMAX
+ #define NOMINMAX 1
+ #include <windows.h>
+ #include "unity_gl.h"
+ #undef GL_VERSION_1_2
+ #include "unity_glext.h"
+ #include "PlatformDependent/Win/wglext.h"
+#elif UNITY_WII
+ #include <revolution.h>
+#elif UNITY_ANDROID
+ #include <GLES/gl.h>
+#elif UNITY_LINUX
+ #include "unity_gl.h"
+ #undef GL_VERSION_1_2
+ #include "unity_glext.h"
+#else
+#error "Unknown platform"
+#endif
+
+#if UNITY_WII
+
+typedef unsigned int GLenum;
+typedef unsigned char GLboolean;
+typedef unsigned int GLbitfield;
+typedef signed char GLbyte;
+typedef short GLshort;
+typedef int GLint;
+typedef int GLsizei;
+typedef unsigned char GLubyte;
+typedef unsigned short GLushort;
+typedef unsigned int GLuint;
+typedef float GLfloat;
+typedef float GLclampf;
+typedef double GLdouble;
+typedef double GLclampd;
+typedef void GLvoid;
+
+void glBegin (GLenum mode);
+void glEnd (void);
+
+void glVertex2f (GLfloat x, GLfloat y);
+void glVertex3f (GLfloat x, GLfloat y, GLfloat z);
+void glVertex4f (GLfloat x, GLfloat y, GLfloat z, GLfloat w );
+void glVertex3fv (const GLfloat *v);
+void glColor4fv (const GLfloat *v);
+void glColor4f (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+void glTexCoord1f (GLfloat s);
+void glTexCoord2f (GLfloat s, GLfloat t);
+void glTexCoord3f (GLfloat s, GLfloat t, GLfloat r);
+void glTexCoord4f (GLfloat s, GLfloat t, GLfloat r, GLfloat q);
+void glTexCoord1fv (const GLfloat *v);
+void glTexCoord2fv (const GLfloat *v);
+void glTexCoord3fv (const GLfloat *v);
+void glTexCoord4fv (const GLfloat *v);
+//void glNormal3f (GLfloat nx, GLfloat ny, GLfloat nz);
+void glNormal3fv (const GLfloat *v);
+void glNormal3f (GLfloat nx, GLfloat ny, GLfloat nz);
+
+void glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
+void glDisableClientState (GLenum array);
+void glClientActiveTextureARB (GLenum);
+void glPolygonMode (GLenum face, GLenum mode);
+void glActiveTextureARB (GLenum);
+void glMultiTexCoord3fvARB (GLenum, const GLfloat *);
+void glMultiTexCoord3fARB (GLenum, GLfloat, GLfloat, GLfloat);
+void glMultiTexCoord4fARB (GLenum, GLfloat, GLfloat, GLfloat, GLfloat);
+void glMultiTexCoord4fvARB (GLenum, const GLfloat *);
+void glDrawElements (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
+void glDrawArrays (GLenum mode, GLint first, GLsizei count);
+void glDisableVertexAttribArrayARB (GLuint);
+void glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
+
+void glBindTexture (GLenum target, GLuint texture);
+void glTexParameterfv (GLenum target, GLenum pname, const GLfloat *params);
+void glTexParameteri (GLenum target, GLenum pname, GLint param);
+void glTranslatef (GLfloat x, GLfloat y, GLfloat z);
+void glScalef (GLfloat x, GLfloat y, GLfloat z);
+
+void glMatrixMode (GLenum mode);
+void glMultMatrixf (const GLfloat *m);
+void glPushMatrix (void);
+void glPopMatrix (void);
+void glLoadIdentity (void);
+
+#define GL_UNSIGNED_BYTE 0x1401
+#define GL_UNSIGNED_SHORT 0x1403
+#define GL_FLOAT 0x1406
+
+#define GL_ZERO 0
+#define GL_ONE 1
+//#define GL_SRC_COLOR 0x0300
+//#define GL_ONE_MINUS_SRC_COLOR 0x0301
+//#define GL_SRC_ALPHA 0x0302
+//#define GL_ONE_MINUS_SRC_ALPHA 0x0303
+//#define GL_DST_ALPHA 0x0304
+//#define GL_ONE_MINUS_DST_ALPHA 0x0305
+
+#define GL_FRONT_AND_BACK 0x0408
+
+#define GL_LINES 0x0001
+#define GL_TRIANGLES 0x0004
+#define GL_TRIANGLE_STRIP 0x0005
+#define GL_QUADS 0x0007
+
+#define GL_TEXTURE_2D 0x0DE1
+
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x8515
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x8516
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x8517
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x8518
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x8519
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x851A
+
+#define GL_TEXTURE_BORDER_COLOR 0x1004
+#define GL_TEXTURE_WRAP_S 0x2802
+#define GL_TEXTURE_WRAP_T 0x2803
+#define GL_CLAMP_TO_BORDER_ARB 0x812D
+
+#define GL_LINE 0x1B01
+#define GL_FILL 0x1B02
+
+#define GL_MODELVIEW 0x1700
+#define GL_PROJECTION 0x1701
+#define GL_TEXTURE 0x1702
+
+#define GL_COLOR_ARRAY 0x8076
+#define GL_VERTEX_ARRAY 0x8074
+#define GL_NORMAL_ARRAY 0x8075
+#define GL_TEXTURE_COORD_ARRAY 0x8078
+
+#define GL_TEXTURE0_ARB 0x84C0
+
+#elif UNITY_ANDROID
+#else
+
+#if GFX_SUPPORTS_OPENGL
+#define DEF(a,b) extern a UNITYGL_##b
+#include "GLExtensionDefs.h"
+#undef DEF
+#endif
+
+#endif
+
+#if GFX_SUPPORTS_OPENGL
+void InitGLExtensions();
+void CleanupGLExtensions();
+void UnbindVertexBuffersGL(); // defined in GfxDeviceGL.cpp
+#include "GLAssert.h"
+#endif
+
+
+//#define DUMMY_OPENGL_CALLS
+
+
+#ifndef DUMMY_OPENGL_CALLS
+#define OGL_CALL(x) do { x; GLAssert(); } while(0)
+#else
+void DummyOpenGLFunction();
+#define OGL_CALL(x) DummyOpenGLFunction()
+#endif
+
+
+
+#endif
diff --git a/Runtime/GfxDevice/opengl/unity_gl.h b/Runtime/GfxDevice/opengl/unity_gl.h
new file mode 100644
index 0000000..2097ef3
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/unity_gl.h
@@ -0,0 +1,1914 @@
+#ifndef __gl_h_
+#define __gl_h_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+** The contents of this file are subject to the GLX Public License Version 1.0
+** (the "License"). You may not use this file except in compliance with the
+** License. You may obtain a copy of the License at Silicon Graphics, Inc.,
+** attn: Legal Services, 2011 N. Shoreline Blvd., Mountain View, CA 94043
+** or at http://www.sgi.com/software/opensource/glx/license.html.
+**
+** Software distributed under the License is distributed on an "AS IS"
+** basis. ALL WARRANTIES ARE DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY
+** IMPLIED WARRANTIES OF MERCHANTABILITY, OF FITNESS FOR A PARTICULAR
+** PURPOSE OR OF NON- INFRINGEMENT. See the License for the specific
+** language governing rights and limitations under the License.
+**
+** The Original Software is GLX version 1.2 source code, released February,
+** 1999. The developer of the Original Software is Silicon Graphics, Inc.
+** Those portions of the Subject Software created by Silicon Graphics, Inc.
+** are Copyright (c) 1991-9 Silicon Graphics, Inc. All Rights Reserved.
+*/
+
+#ifndef _WIN32
+#define WINGDIAPI
+#define APIENTRY
+#endif
+
+typedef unsigned int GLenum;
+typedef unsigned char GLboolean;
+typedef unsigned int GLbitfield;
+typedef signed char GLbyte;
+typedef short GLshort;
+typedef int GLint;
+typedef int GLsizei;
+typedef unsigned char GLubyte;
+typedef unsigned short GLushort;
+typedef unsigned int GLuint;
+typedef float GLfloat;
+typedef float GLclampf;
+typedef double GLdouble;
+typedef double GLclampd;
+typedef void GLvoid;
+
+/*************************************************************/
+
+/* Version */
+#define GL_VERSION_1_1 1
+#define GL_VERSION_1_2 1
+
+/* Extensions */
+#define GL_ARB_imaging 1
+#define GL_ARB_multitexture 1
+
+/* AccumOp */
+#define GL_ACCUM 0x0100
+#define GL_LOAD 0x0101
+#define GL_RETURN 0x0102
+#define GL_MULT 0x0103
+#define GL_ADD 0x0104
+
+/* AlphaFunction */
+#define GL_NEVER 0x0200
+#define GL_LESS 0x0201
+#define GL_EQUAL 0x0202
+#define GL_LEQUAL 0x0203
+#define GL_GREATER 0x0204
+#define GL_NOTEQUAL 0x0205
+#define GL_GEQUAL 0x0206
+#define GL_ALWAYS 0x0207
+
+/* AttribMask */
+#define GL_CURRENT_BIT 0x00000001
+#define GL_POINT_BIT 0x00000002
+#define GL_LINE_BIT 0x00000004
+#define GL_POLYGON_BIT 0x00000008
+#define GL_POLYGON_STIPPLE_BIT 0x00000010
+#define GL_PIXEL_MODE_BIT 0x00000020
+#define GL_LIGHTING_BIT 0x00000040
+#define GL_FOG_BIT 0x00000080
+#define GL_DEPTH_BUFFER_BIT 0x00000100
+#define GL_ACCUM_BUFFER_BIT 0x00000200
+#define GL_STENCIL_BUFFER_BIT 0x00000400
+#define GL_VIEWPORT_BIT 0x00000800
+#define GL_TRANSFORM_BIT 0x00001000
+#define GL_ENABLE_BIT 0x00002000
+#define GL_COLOR_BUFFER_BIT 0x00004000
+#define GL_HINT_BIT 0x00008000
+#define GL_EVAL_BIT 0x00010000
+#define GL_LIST_BIT 0x00020000
+#define GL_TEXTURE_BIT 0x00040000
+#define GL_SCISSOR_BIT 0x00080000
+#define GL_ALL_ATTRIB_BITS 0x000fffff
+
+/* BeginMode */
+#define GL_POINTS 0x0000
+#define GL_LINES 0x0001
+#define GL_LINE_LOOP 0x0002
+#define GL_LINE_STRIP 0x0003
+#define GL_TRIANGLES 0x0004
+#define GL_TRIANGLE_STRIP 0x0005
+#define GL_TRIANGLE_FAN 0x0006
+#define GL_QUADS 0x0007
+#define GL_QUAD_STRIP 0x0008
+#define GL_POLYGON 0x0009
+
+/* BlendEquationMode */
+/* GL_LOGIC_OP */
+/* GL_FUNC_ADD */
+/* GL_MIN */
+/* GL_MAX */
+/* GL_FUNC_SUBTRACT */
+/* GL_FUNC_REVERSE_SUBTRACT */
+
+/* BlendingFactorDest */
+#define GL_ZERO 0
+#define GL_ONE 1
+#define GL_SRC_COLOR 0x0300
+#define GL_ONE_MINUS_SRC_COLOR 0x0301
+#define GL_SRC_ALPHA 0x0302
+#define GL_ONE_MINUS_SRC_ALPHA 0x0303
+#define GL_DST_ALPHA 0x0304
+#define GL_ONE_MINUS_DST_ALPHA 0x0305
+/* GL_CONSTANT_COLOR */
+/* GL_ONE_MINUS_CONSTANT_COLOR */
+/* GL_CONSTANT_ALPHA */
+/* GL_ONE_MINUS_CONSTANT_ALPHA */
+
+/* BlendingFactorSrc */
+/* GL_ZERO */
+/* GL_ONE */
+#define GL_DST_COLOR 0x0306
+#define GL_ONE_MINUS_DST_COLOR 0x0307
+#define GL_SRC_ALPHA_SATURATE 0x0308
+/* GL_SRC_ALPHA */
+/* GL_ONE_MINUS_SRC_ALPHA */
+/* GL_DST_ALPHA */
+/* GL_ONE_MINUS_DST_ALPHA */
+/* GL_CONSTANT_COLOR */
+/* GL_ONE_MINUS_CONSTANT_COLOR */
+/* GL_CONSTANT_ALPHA */
+/* GL_ONE_MINUS_CONSTANT_ALPHA */
+
+/* Boolean */
+#define GL_TRUE 1
+#define GL_FALSE 0
+
+/* ClearBufferMask */
+/* GL_COLOR_BUFFER_BIT */
+/* GL_ACCUM_BUFFER_BIT */
+/* GL_STENCIL_BUFFER_BIT */
+/* GL_DEPTH_BUFFER_BIT */
+
+/* ClientArrayType */
+/* GL_VERTEX_ARRAY */
+/* GL_NORMAL_ARRAY */
+/* GL_COLOR_ARRAY */
+/* GL_INDEX_ARRAY */
+/* GL_TEXTURE_COORD_ARRAY */
+/* GL_EDGE_FLAG_ARRAY */
+
+/* ClipPlaneName */
+#define GL_CLIP_PLANE0 0x3000
+#define GL_CLIP_PLANE1 0x3001
+#define GL_CLIP_PLANE2 0x3002
+#define GL_CLIP_PLANE3 0x3003
+#define GL_CLIP_PLANE4 0x3004
+#define GL_CLIP_PLANE5 0x3005
+
+/* ColorMaterialFace */
+/* GL_FRONT */
+/* GL_BACK */
+/* GL_FRONT_AND_BACK */
+
+/* ColorMaterialParameter */
+/* GL_AMBIENT */
+/* GL_DIFFUSE */
+/* GL_SPECULAR */
+/* GL_EMISSION */
+/* GL_AMBIENT_AND_DIFFUSE */
+
+/* ColorPointerType */
+/* GL_BYTE */
+/* GL_UNSIGNED_BYTE */
+/* GL_SHORT */
+/* GL_UNSIGNED_SHORT */
+/* GL_INT */
+/* GL_UNSIGNED_INT */
+/* GL_FLOAT */
+/* GL_DOUBLE */
+
+/* ColorTableParameterPName */
+/* GL_COLOR_TABLE_SCALE */
+/* GL_COLOR_TABLE_BIAS */
+
+/* ColorTableTarget */
+/* GL_COLOR_TABLE */
+/* GL_POST_CONVOLUTION_COLOR_TABLE */
+/* GL_POST_COLOR_MATRIX_COLOR_TABLE */
+/* GL_PROXY_COLOR_TABLE */
+/* GL_PROXY_POST_CONVOLUTION_COLOR_TABLE */
+/* GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE */
+
+/* ConvolutionBorderMode */
+/* GL_REDUCE */
+/* GL_IGNORE_BORDER */
+/* GL_CONSTANT_BORDER */
+
+/* ConvolutionParameter */
+/* GL_CONVOLUTION_BORDER_MODE */
+/* GL_CONVOLUTION_FILTER_SCALE */
+/* GL_CONVOLUTION_FILTER_BIAS */
+
+/* ConvolutionTarget */
+/* GL_CONVOLUTION_1D */
+/* GL_CONVOLUTION_2D */
+
+/* CullFaceMode */
+/* GL_FRONT */
+/* GL_BACK */
+/* GL_FRONT_AND_BACK */
+
+/* DataType */
+#define GL_BYTE 0x1400
+#define GL_UNSIGNED_BYTE 0x1401
+#define GL_SHORT 0x1402
+#define GL_UNSIGNED_SHORT 0x1403
+#define GL_INT 0x1404
+#define GL_UNSIGNED_INT 0x1405
+#define GL_FLOAT 0x1406
+#define GL_2_BYTES 0x1407
+#define GL_3_BYTES 0x1408
+#define GL_4_BYTES 0x1409
+#define GL_DOUBLE 0x140A
+
+/* DepthFunction */
+/* GL_NEVER */
+/* GL_LESS */
+/* GL_EQUAL */
+/* GL_LEQUAL */
+/* GL_GREATER */
+/* GL_NOTEQUAL */
+/* GL_GEQUAL */
+/* GL_ALWAYS */
+
+/* DrawBufferMode */
+#define GL_NONE 0
+#define GL_FRONT_LEFT 0x0400
+#define GL_FRONT_RIGHT 0x0401
+#define GL_BACK_LEFT 0x0402
+#define GL_BACK_RIGHT 0x0403
+#define GL_FRONT 0x0404
+#define GL_BACK 0x0405
+#define GL_LEFT 0x0406
+#define GL_RIGHT 0x0407
+#define GL_FRONT_AND_BACK 0x0408
+#define GL_AUX0 0x0409
+#define GL_AUX1 0x040A
+#define GL_AUX2 0x040B
+#define GL_AUX3 0x040C
+
+/* Enable */
+/* GL_FOG */
+/* GL_LIGHTING */
+/* GL_TEXTURE_1D */
+/* GL_TEXTURE_2D */
+/* GL_LINE_STIPPLE */
+/* GL_POLYGON_STIPPLE */
+/* GL_CULL_FACE */
+/* GL_ALPHA_TEST */
+/* GL_BLEND */
+/* GL_INDEX_LOGIC_OP */
+/* GL_COLOR_LOGIC_OP */
+/* GL_DITHER */
+/* GL_STENCIL_TEST */
+/* GL_DEPTH_TEST */
+/* GL_CLIP_PLANE0 */
+/* GL_CLIP_PLANE1 */
+/* GL_CLIP_PLANE2 */
+/* GL_CLIP_PLANE3 */
+/* GL_CLIP_PLANE4 */
+/* GL_CLIP_PLANE5 */
+/* GL_LIGHT0 */
+/* GL_LIGHT1 */
+/* GL_LIGHT2 */
+/* GL_LIGHT3 */
+/* GL_LIGHT4 */
+/* GL_LIGHT5 */
+/* GL_LIGHT6 */
+/* GL_LIGHT7 */
+/* GL_TEXTURE_GEN_S */
+/* GL_TEXTURE_GEN_T */
+/* GL_TEXTURE_GEN_R */
+/* GL_TEXTURE_GEN_Q */
+/* GL_MAP1_VERTEX_3 */
+/* GL_MAP1_VERTEX_4 */
+/* GL_MAP1_COLOR_4 */
+/* GL_MAP1_INDEX */
+/* GL_MAP1_NORMAL */
+/* GL_MAP1_TEXTURE_COORD_1 */
+/* GL_MAP1_TEXTURE_COORD_2 */
+/* GL_MAP1_TEXTURE_COORD_3 */
+/* GL_MAP1_TEXTURE_COORD_4 */
+/* GL_MAP2_VERTEX_3 */
+/* GL_MAP2_VERTEX_4 */
+/* GL_MAP2_COLOR_4 */
+/* GL_MAP2_INDEX */
+/* GL_MAP2_NORMAL */
+/* GL_MAP2_TEXTURE_COORD_1 */
+/* GL_MAP2_TEXTURE_COORD_2 */
+/* GL_MAP2_TEXTURE_COORD_3 */
+/* GL_MAP2_TEXTURE_COORD_4 */
+/* GL_POINT_SMOOTH */
+/* GL_LINE_SMOOTH */
+/* GL_POLYGON_SMOOTH */
+/* GL_SCISSOR_TEST */
+/* GL_COLOR_MATERIAL */
+/* GL_NORMALIZE */
+/* GL_AUTO_NORMAL */
+/* GL_VERTEX_ARRAY */
+/* GL_NORMAL_ARRAY */
+/* GL_COLOR_ARRAY */
+/* GL_INDEX_ARRAY */
+/* GL_TEXTURE_COORD_ARRAY */
+/* GL_EDGE_FLAG_ARRAY */
+/* GL_POLYGON_OFFSET_POINT */
+/* GL_POLYGON_OFFSET_LINE */
+/* GL_POLYGON_OFFSET_FILL */
+/* GL_COLOR_TABLE */
+/* GL_POST_CONVOLUTION_COLOR_TABLE */
+/* GL_POST_COLOR_MATRIX_COLOR_TABLE */
+/* GL_CONVOLUTION_1D */
+/* GL_CONVOLUTION_2D */
+/* GL_SEPARABLE_2D */
+/* GL_HISTOGRAM */
+/* GL_MINMAX */
+/* GL_RESCALE_NORMAL */
+/* GL_TEXTURE_3D */
+
+/* ErrorCode */
+#define GL_NO_ERROR 0
+#define GL_INVALID_ENUM 0x0500
+#define GL_INVALID_VALUE 0x0501
+#define GL_INVALID_OPERATION 0x0502
+#define GL_STACK_OVERFLOW 0x0503
+#define GL_STACK_UNDERFLOW 0x0504
+#define GL_OUT_OF_MEMORY 0x0505
+/* GL_TABLE_TOO_LARGE */
+
+/* FeedBackMode */
+#define GL_2D 0x0600
+#define GL_3D 0x0601
+#define GL_3D_COLOR 0x0602
+#define GL_3D_COLOR_TEXTURE 0x0603
+#define GL_4D_COLOR_TEXTURE 0x0604
+
+/* FeedBackToken */
+#define GL_PASS_THROUGH_TOKEN 0x0700
+#define GL_POINT_TOKEN 0x0701
+#define GL_LINE_TOKEN 0x0702
+#define GL_POLYGON_TOKEN 0x0703
+#define GL_BITMAP_TOKEN 0x0704
+#define GL_DRAW_PIXEL_TOKEN 0x0705
+#define GL_COPY_PIXEL_TOKEN 0x0706
+#define GL_LINE_RESET_TOKEN 0x0707
+
+/* FogMode */
+/* GL_LINEAR */
+#define GL_EXP 0x0800
+#define GL_EXP2 0x0801
+
+/* FogParameter */
+/* GL_FOG_COLOR */
+/* GL_FOG_DENSITY */
+/* GL_FOG_END */
+/* GL_FOG_INDEX */
+/* GL_FOG_MODE */
+/* GL_FOG_START */
+
+/* FrontFaceDirection */
+#define GL_CW 0x0900
+#define GL_CCW 0x0901
+
+/* GetColorTableParameterPName */
+/* GL_COLOR_TABLE_SCALE */
+/* GL_COLOR_TABLE_BIAS */
+/* GL_COLOR_TABLE_FORMAT */
+/* GL_COLOR_TABLE_WIDTH */
+/* GL_COLOR_TABLE_RED_SIZE */
+/* GL_COLOR_TABLE_GREEN_SIZE */
+/* GL_COLOR_TABLE_BLUE_SIZE */
+/* GL_COLOR_TABLE_ALPHA_SIZE */
+/* GL_COLOR_TABLE_LUMINANCE_SIZE */
+/* GL_COLOR_TABLE_INTENSITY_SIZE */
+
+/* GetConvolutionParameterPName */
+/* GL_CONVOLUTION_BORDER_COLOR */
+/* GL_CONVOLUTION_BORDER_MODE */
+/* GL_CONVOLUTION_FILTER_SCALE */
+/* GL_CONVOLUTION_FILTER_BIAS */
+/* GL_CONVOLUTION_FORMAT */
+/* GL_CONVOLUTION_WIDTH */
+/* GL_CONVOLUTION_HEIGHT */
+/* GL_MAX_CONVOLUTION_WIDTH */
+/* GL_MAX_CONVOLUTION_HEIGHT */
+
+/* GetHistogramParameterPName */
+/* GL_HISTOGRAM_WIDTH */
+/* GL_HISTOGRAM_FORMAT */
+/* GL_HISTOGRAM_RED_SIZE */
+/* GL_HISTOGRAM_GREEN_SIZE */
+/* GL_HISTOGRAM_BLUE_SIZE */
+/* GL_HISTOGRAM_ALPHA_SIZE */
+/* GL_HISTOGRAM_LUMINANCE_SIZE */
+/* GL_HISTOGRAM_SINK */
+
+/* GetMapTarget */
+#define GL_COEFF 0x0A00
+#define GL_ORDER 0x0A01
+#define GL_DOMAIN 0x0A02
+
+/* GetMinmaxParameterPName */
+/* GL_MINMAX_FORMAT */
+/* GL_MINMAX_SINK */
+
+/* GetPixelMap */
+/* GL_PIXEL_MAP_I_TO_I */
+/* GL_PIXEL_MAP_S_TO_S */
+/* GL_PIXEL_MAP_I_TO_R */
+/* GL_PIXEL_MAP_I_TO_G */
+/* GL_PIXEL_MAP_I_TO_B */
+/* GL_PIXEL_MAP_I_TO_A */
+/* GL_PIXEL_MAP_R_TO_R */
+/* GL_PIXEL_MAP_G_TO_G */
+/* GL_PIXEL_MAP_B_TO_B */
+/* GL_PIXEL_MAP_A_TO_A */
+
+/* GetPointerTarget */
+/* GL_VERTEX_ARRAY_POINTER */
+/* GL_NORMAL_ARRAY_POINTER */
+/* GL_COLOR_ARRAY_POINTER */
+/* GL_INDEX_ARRAY_POINTER */
+/* GL_TEXTURE_COORD_ARRAY_POINTER */
+/* GL_EDGE_FLAG_ARRAY_POINTER */
+
+/* GetTarget */
+#define GL_CURRENT_COLOR 0x0B00
+#define GL_CURRENT_INDEX 0x0B01
+#define GL_CURRENT_NORMAL 0x0B02
+#define GL_CURRENT_TEXTURE_COORDS 0x0B03
+#define GL_CURRENT_RASTER_COLOR 0x0B04
+#define GL_CURRENT_RASTER_INDEX 0x0B05
+#define GL_CURRENT_RASTER_TEXTURE_COORDS 0x0B06
+#define GL_CURRENT_RASTER_POSITION 0x0B07
+#define GL_CURRENT_RASTER_POSITION_VALID 0x0B08
+#define GL_CURRENT_RASTER_DISTANCE 0x0B09
+#define GL_POINT_SMOOTH 0x0B10
+#define GL_POINT_SIZE 0x0B11
+#define GL_POINT_SIZE_RANGE 0x0B12
+#define GL_POINT_SIZE_GRANULARITY 0x0B13
+#define GL_LINE_SMOOTH 0x0B20
+#define GL_LINE_WIDTH 0x0B21
+#define GL_LINE_WIDTH_RANGE 0x0B22
+#define GL_LINE_WIDTH_GRANULARITY 0x0B23
+#define GL_LINE_STIPPLE 0x0B24
+#define GL_LINE_STIPPLE_PATTERN 0x0B25
+#define GL_LINE_STIPPLE_REPEAT 0x0B26
+/* GL_SMOOTH_POINT_SIZE_RANGE */
+/* GL_SMOOTH_POINT_SIZE_GRANULARITY */
+/* GL_SMOOTH_LINE_WIDTH_RANGE */
+/* GL_SMOOTH_LINE_WIDTH_GRANULARITY */
+/* GL_ALIASED_POINT_SIZE_RANGE */
+/* GL_ALIASED_LINE_WIDTH_RANGE */
+#define GL_LIST_MODE 0x0B30
+#define GL_MAX_LIST_NESTING 0x0B31
+#define GL_LIST_BASE 0x0B32
+#define GL_LIST_INDEX 0x0B33
+#define GL_POLYGON_MODE 0x0B40
+#define GL_POLYGON_SMOOTH 0x0B41
+#define GL_POLYGON_STIPPLE 0x0B42
+#define GL_EDGE_FLAG 0x0B43
+#define GL_CULL_FACE 0x0B44
+#define GL_CULL_FACE_MODE 0x0B45
+#define GL_FRONT_FACE 0x0B46
+#define GL_LIGHTING 0x0B50
+#define GL_LIGHT_MODEL_LOCAL_VIEWER 0x0B51
+#define GL_LIGHT_MODEL_TWO_SIDE 0x0B52
+#define GL_LIGHT_MODEL_AMBIENT 0x0B53
+#define GL_SHADE_MODEL 0x0B54
+#define GL_COLOR_MATERIAL_FACE 0x0B55
+#define GL_COLOR_MATERIAL_PARAMETER 0x0B56
+#define GL_COLOR_MATERIAL 0x0B57
+#define GL_FOG 0x0B60
+#define GL_FOG_INDEX 0x0B61
+#define GL_FOG_DENSITY 0x0B62
+#define GL_FOG_START 0x0B63
+#define GL_FOG_END 0x0B64
+#define GL_FOG_MODE 0x0B65
+#define GL_FOG_COLOR 0x0B66
+#define GL_DEPTH_RANGE 0x0B70
+#define GL_DEPTH_TEST 0x0B71
+#define GL_DEPTH_WRITEMASK 0x0B72
+#define GL_DEPTH_CLEAR_VALUE 0x0B73
+#define GL_DEPTH_FUNC 0x0B74
+#define GL_ACCUM_CLEAR_VALUE 0x0B80
+#define GL_STENCIL_TEST 0x0B90
+#define GL_STENCIL_CLEAR_VALUE 0x0B91
+#define GL_STENCIL_FUNC 0x0B92
+#define GL_STENCIL_VALUE_MASK 0x0B93
+#define GL_STENCIL_FAIL 0x0B94
+#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95
+#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96
+#define GL_STENCIL_REF 0x0B97
+#define GL_STENCIL_WRITEMASK 0x0B98
+#define GL_MATRIX_MODE 0x0BA0
+#define GL_NORMALIZE 0x0BA1
+#define GL_VIEWPORT 0x0BA2
+#define GL_MODELVIEW_STACK_DEPTH 0x0BA3
+#define GL_PROJECTION_STACK_DEPTH 0x0BA4
+#define GL_TEXTURE_STACK_DEPTH 0x0BA5
+#define GL_MODELVIEW_MATRIX 0x0BA6
+#define GL_PROJECTION_MATRIX 0x0BA7
+#define GL_TEXTURE_MATRIX 0x0BA8
+#define GL_ATTRIB_STACK_DEPTH 0x0BB0
+#define GL_CLIENT_ATTRIB_STACK_DEPTH 0x0BB1
+#define GL_ALPHA_TEST 0x0BC0
+#define GL_ALPHA_TEST_FUNC 0x0BC1
+#define GL_ALPHA_TEST_REF 0x0BC2
+#define GL_DITHER 0x0BD0
+#define GL_BLEND_DST 0x0BE0
+#define GL_BLEND_SRC 0x0BE1
+#define GL_BLEND 0x0BE2
+#define GL_LOGIC_OP_MODE 0x0BF0
+#define GL_INDEX_LOGIC_OP 0x0BF1
+#define GL_COLOR_LOGIC_OP 0x0BF2
+#define GL_AUX_BUFFERS 0x0C00
+#define GL_DRAW_BUFFER 0x0C01
+#define GL_READ_BUFFER 0x0C02
+#define GL_SCISSOR_BOX 0x0C10
+#define GL_SCISSOR_TEST 0x0C11
+#define GL_INDEX_CLEAR_VALUE 0x0C20
+#define GL_INDEX_WRITEMASK 0x0C21
+#define GL_COLOR_CLEAR_VALUE 0x0C22
+#define GL_COLOR_WRITEMASK 0x0C23
+#define GL_INDEX_MODE 0x0C30
+#define GL_RGBA_MODE 0x0C31
+#define GL_DOUBLEBUFFER 0x0C32
+#define GL_STEREO 0x0C33
+#define GL_RENDER_MODE 0x0C40
+#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50
+#define GL_POINT_SMOOTH_HINT 0x0C51
+#define GL_LINE_SMOOTH_HINT 0x0C52
+#define GL_POLYGON_SMOOTH_HINT 0x0C53
+#define GL_FOG_HINT 0x0C54
+#define GL_TEXTURE_GEN_S 0x0C60
+#define GL_TEXTURE_GEN_T 0x0C61
+#define GL_TEXTURE_GEN_R 0x0C62
+#define GL_TEXTURE_GEN_Q 0x0C63
+#define GL_PIXEL_MAP_I_TO_I 0x0C70
+#define GL_PIXEL_MAP_S_TO_S 0x0C71
+#define GL_PIXEL_MAP_I_TO_R 0x0C72
+#define GL_PIXEL_MAP_I_TO_G 0x0C73
+#define GL_PIXEL_MAP_I_TO_B 0x0C74
+#define GL_PIXEL_MAP_I_TO_A 0x0C75
+#define GL_PIXEL_MAP_R_TO_R 0x0C76
+#define GL_PIXEL_MAP_G_TO_G 0x0C77
+#define GL_PIXEL_MAP_B_TO_B 0x0C78
+#define GL_PIXEL_MAP_A_TO_A 0x0C79
+#define GL_PIXEL_MAP_I_TO_I_SIZE 0x0CB0
+#define GL_PIXEL_MAP_S_TO_S_SIZE 0x0CB1
+#define GL_PIXEL_MAP_I_TO_R_SIZE 0x0CB2
+#define GL_PIXEL_MAP_I_TO_G_SIZE 0x0CB3
+#define GL_PIXEL_MAP_I_TO_B_SIZE 0x0CB4
+#define GL_PIXEL_MAP_I_TO_A_SIZE 0x0CB5
+#define GL_PIXEL_MAP_R_TO_R_SIZE 0x0CB6
+#define GL_PIXEL_MAP_G_TO_G_SIZE 0x0CB7
+#define GL_PIXEL_MAP_B_TO_B_SIZE 0x0CB8
+#define GL_PIXEL_MAP_A_TO_A_SIZE 0x0CB9
+#define GL_UNPACK_SWAP_BYTES 0x0CF0
+#define GL_UNPACK_LSB_FIRST 0x0CF1
+#define GL_UNPACK_ROW_LENGTH 0x0CF2
+#define GL_UNPACK_SKIP_ROWS 0x0CF3
+#define GL_UNPACK_SKIP_PIXELS 0x0CF4
+#define GL_UNPACK_ALIGNMENT 0x0CF5
+#define GL_PACK_SWAP_BYTES 0x0D00
+#define GL_PACK_LSB_FIRST 0x0D01
+#define GL_PACK_ROW_LENGTH 0x0D02
+#define GL_PACK_SKIP_ROWS 0x0D03
+#define GL_PACK_SKIP_PIXELS 0x0D04
+#define GL_PACK_ALIGNMENT 0x0D05
+#define GL_MAP_COLOR 0x0D10
+#define GL_MAP_STENCIL 0x0D11
+#define GL_INDEX_SHIFT 0x0D12
+#define GL_INDEX_OFFSET 0x0D13
+#define GL_RED_SCALE 0x0D14
+#define GL_RED_BIAS 0x0D15
+#define GL_ZOOM_X 0x0D16
+#define GL_ZOOM_Y 0x0D17
+#define GL_GREEN_SCALE 0x0D18
+#define GL_GREEN_BIAS 0x0D19
+#define GL_BLUE_SCALE 0x0D1A
+#define GL_BLUE_BIAS 0x0D1B
+#define GL_ALPHA_SCALE 0x0D1C
+#define GL_ALPHA_BIAS 0x0D1D
+#define GL_DEPTH_SCALE 0x0D1E
+#define GL_DEPTH_BIAS 0x0D1F
+#define GL_MAX_EVAL_ORDER 0x0D30
+#define GL_MAX_LIGHTS 0x0D31
+#define GL_MAX_CLIP_PLANES 0x0D32
+#define GL_MAX_TEXTURE_SIZE 0x0D33
+#define GL_MAX_PIXEL_MAP_TABLE 0x0D34
+#define GL_MAX_ATTRIB_STACK_DEPTH 0x0D35
+#define GL_MAX_MODELVIEW_STACK_DEPTH 0x0D36
+#define GL_MAX_NAME_STACK_DEPTH 0x0D37
+#define GL_MAX_PROJECTION_STACK_DEPTH 0x0D38
+#define GL_MAX_TEXTURE_STACK_DEPTH 0x0D39
+#define GL_MAX_VIEWPORT_DIMS 0x0D3A
+#define GL_MAX_CLIENT_ATTRIB_STACK_DEPTH 0x0D3B
+#define GL_SUBPIXEL_BITS 0x0D50
+#define GL_INDEX_BITS 0x0D51
+#define GL_RED_BITS 0x0D52
+#define GL_GREEN_BITS 0x0D53
+#define GL_BLUE_BITS 0x0D54
+#define GL_ALPHA_BITS 0x0D55
+#define GL_DEPTH_BITS 0x0D56
+#define GL_STENCIL_BITS 0x0D57
+#define GL_ACCUM_RED_BITS 0x0D58
+#define GL_ACCUM_GREEN_BITS 0x0D59
+#define GL_ACCUM_BLUE_BITS 0x0D5A
+#define GL_ACCUM_ALPHA_BITS 0x0D5B
+#define GL_NAME_STACK_DEPTH 0x0D70
+#define GL_AUTO_NORMAL 0x0D80
+#define GL_MAP1_COLOR_4 0x0D90
+#define GL_MAP1_INDEX 0x0D91
+#define GL_MAP1_NORMAL 0x0D92
+#define GL_MAP1_TEXTURE_COORD_1 0x0D93
+#define GL_MAP1_TEXTURE_COORD_2 0x0D94
+#define GL_MAP1_TEXTURE_COORD_3 0x0D95
+#define GL_MAP1_TEXTURE_COORD_4 0x0D96
+#define GL_MAP1_VERTEX_3 0x0D97
+#define GL_MAP1_VERTEX_4 0x0D98
+#define GL_MAP2_COLOR_4 0x0DB0
+#define GL_MAP2_INDEX 0x0DB1
+#define GL_MAP2_NORMAL 0x0DB2
+#define GL_MAP2_TEXTURE_COORD_1 0x0DB3
+#define GL_MAP2_TEXTURE_COORD_2 0x0DB4
+#define GL_MAP2_TEXTURE_COORD_3 0x0DB5
+#define GL_MAP2_TEXTURE_COORD_4 0x0DB6
+#define GL_MAP2_VERTEX_3 0x0DB7
+#define GL_MAP2_VERTEX_4 0x0DB8
+#define GL_MAP1_GRID_DOMAIN 0x0DD0
+#define GL_MAP1_GRID_SEGMENTS 0x0DD1
+#define GL_MAP2_GRID_DOMAIN 0x0DD2
+#define GL_MAP2_GRID_SEGMENTS 0x0DD3
+#define GL_TEXTURE_1D 0x0DE0
+#define GL_TEXTURE_2D 0x0DE1
+#define GL_FEEDBACK_BUFFER_POINTER 0x0DF0
+#define GL_FEEDBACK_BUFFER_SIZE 0x0DF1
+#define GL_FEEDBACK_BUFFER_TYPE 0x0DF2
+#define GL_SELECTION_BUFFER_POINTER 0x0DF3
+#define GL_SELECTION_BUFFER_SIZE 0x0DF4
+/* GL_TEXTURE_BINDING_1D */
+/* GL_TEXTURE_BINDING_2D */
+/* GL_TEXTURE_BINDING_3D */
+/* GL_VERTEX_ARRAY */
+/* GL_NORMAL_ARRAY */
+/* GL_COLOR_ARRAY */
+/* GL_INDEX_ARRAY */
+/* GL_TEXTURE_COORD_ARRAY */
+/* GL_EDGE_FLAG_ARRAY */
+/* GL_VERTEX_ARRAY_SIZE */
+/* GL_VERTEX_ARRAY_TYPE */
+/* GL_VERTEX_ARRAY_STRIDE */
+/* GL_NORMAL_ARRAY_TYPE */
+/* GL_NORMAL_ARRAY_STRIDE */
+/* GL_COLOR_ARRAY_SIZE */
+/* GL_COLOR_ARRAY_TYPE */
+/* GL_COLOR_ARRAY_STRIDE */
+/* GL_INDEX_ARRAY_TYPE */
+/* GL_INDEX_ARRAY_STRIDE */
+/* GL_TEXTURE_COORD_ARRAY_SIZE */
+/* GL_TEXTURE_COORD_ARRAY_TYPE */
+/* GL_TEXTURE_COORD_ARRAY_STRIDE */
+/* GL_EDGE_FLAG_ARRAY_STRIDE */
+/* GL_POLYGON_OFFSET_FACTOR */
+/* GL_POLYGON_OFFSET_UNITS */
+/* GL_COLOR_TABLE */
+/* GL_POST_CONVOLUTION_COLOR_TABLE */
+/* GL_POST_COLOR_MATRIX_COLOR_TABLE */
+/* GL_CONVOLUTION_1D */
+/* GL_CONVOLUTION_2D */
+/* GL_SEPARABLE_2D */
+/* GL_POST_CONVOLUTION_RED_SCALE */
+/* GL_POST_CONVOLUTION_GREEN_SCALE */
+/* GL_POST_CONVOLUTION_BLUE_SCALE */
+/* GL_POST_CONVOLUTION_ALPHA_SCALE */
+/* GL_POST_CONVOLUTION_RED_BIAS */
+/* GL_POST_CONVOLUTION_GREEN_BIAS */
+/* GL_POST_CONVOLUTION_BLUE_BIAS */
+/* GL_POST_CONVOLUTION_ALPHA_BIAS */
+/* GL_COLOR_MATRIX */
+/* GL_COLOR_MATRIX_STACK_DEPTH */
+/* GL_MAX_COLOR_MATRIX_STACK_DEPTH */
+/* GL_POST_COLOR_MATRIX_RED_SCALE */
+/* GL_POST_COLOR_MATRIX_GREEN_SCALE */
+/* GL_POST_COLOR_MATRIX_BLUE_SCALE */
+/* GL_POST_COLOR_MATRIX_ALPHA_SCALE */
+/* GL_POST_COLOR_MATRIX_RED_BIAS */
+/* GL_POST_COLOR_MATRIX_GREEN_BIAS */
+/* GL_POST_COLOR_MATRIX_BLUE_BIAS */
+/* GL_POST_COLOR_MATRIX_ALPHA_BIAS */
+/* GL_HISTOGRAM */
+/* GL_MINMAX */
+/* GL_MAX_ELEMENTS_VERTICES */
+/* GL_MAX_ELEMENTS_INDICES */
+/* GL_RESCALE_NORMAL */
+/* GL_LIGHT_MODEL_COLOR_CONTROL */
+/* GL_PACK_SKIP_IMAGES */
+/* GL_PACK_IMAGE_HEIGHT */
+/* GL_UNPACK_SKIP_IMAGES */
+/* GL_UNPACK_IMAGE_HEIGHT */
+/* GL_TEXTURE_3D */
+/* GL_MAX_3D_TEXTURE_SIZE */
+/* GL_BLEND_COLOR */
+/* GL_BLEND_EQUATION */
+/* GL_ACTIVE_TEXTURE_ARB */
+/* GL_CLIENT_ACTIVE_TEXTURE_ARB */
+/* GL_MAX_TEXTURE_UNITS_ARB */
+
+/* GetTextureParameter */
+/* GL_TEXTURE_MAG_FILTER */
+/* GL_TEXTURE_MIN_FILTER */
+/* GL_TEXTURE_WRAP_S */
+/* GL_TEXTURE_WRAP_T */
+#define GL_TEXTURE_WIDTH 0x1000
+#define GL_TEXTURE_HEIGHT 0x1001
+#define GL_TEXTURE_INTERNAL_FORMAT 0x1003
+#define GL_TEXTURE_BORDER_COLOR 0x1004
+#define GL_TEXTURE_BORDER 0x1005
+/* GL_TEXTURE_RED_SIZE */
+/* GL_TEXTURE_GREEN_SIZE */
+/* GL_TEXTURE_BLUE_SIZE */
+/* GL_TEXTURE_ALPHA_SIZE */
+/* GL_TEXTURE_LUMINANCE_SIZE */
+/* GL_TEXTURE_INTENSITY_SIZE */
+/* GL_TEXTURE_PRIORITY */
+/* GL_TEXTURE_RESIDENT */
+/* GL_TEXTURE_DEPTH */
+/* GL_TEXTURE_WRAP_R */
+/* GL_TEXTURE_MIN_LOD */
+/* GL_TEXTURE_MAX_LOD */
+/* GL_TEXTURE_BASE_LEVEL */
+/* GL_TEXTURE_MAX_LEVEL */
+
+/* HintMode */
+#define GL_DONT_CARE 0x1100
+#define GL_FASTEST 0x1101
+#define GL_NICEST 0x1102
+
+/* HintTarget */
+/* GL_PERSPECTIVE_CORRECTION_HINT */
+/* GL_POINT_SMOOTH_HINT */
+/* GL_LINE_SMOOTH_HINT */
+/* GL_POLYGON_SMOOTH_HINT */
+/* GL_FOG_HINT */
+
+/* HistogramTarget */
+/* GL_HISTOGRAM */
+/* GL_PROXY_HISTOGRAM */
+
+/* IndexPointerType */
+/* GL_SHORT */
+/* GL_INT */
+/* GL_FLOAT */
+/* GL_DOUBLE */
+
+/* LightModelColorControl */
+/* GL_SINGLE_COLOR */
+/* GL_SEPARATE_SPECULAR_COLOR */
+
+/* LightModelParameter */
+/* GL_LIGHT_MODEL_AMBIENT */
+/* GL_LIGHT_MODEL_LOCAL_VIEWER */
+/* GL_LIGHT_MODEL_TWO_SIDE */
+/* GL_LIGHT_MODEL_COLOR_CONTROL */
+
+/* LightName */
+#define GL_LIGHT0 0x4000
+#define GL_LIGHT1 0x4001
+#define GL_LIGHT2 0x4002
+#define GL_LIGHT3 0x4003
+#define GL_LIGHT4 0x4004
+#define GL_LIGHT5 0x4005
+#define GL_LIGHT6 0x4006
+#define GL_LIGHT7 0x4007
+
+/* LightParameter */
+#define GL_AMBIENT 0x1200
+#define GL_DIFFUSE 0x1201
+#define GL_SPECULAR 0x1202
+#define GL_POSITION 0x1203
+#define GL_SPOT_DIRECTION 0x1204
+#define GL_SPOT_EXPONENT 0x1205
+#define GL_SPOT_CUTOFF 0x1206
+#define GL_CONSTANT_ATTENUATION 0x1207
+#define GL_LINEAR_ATTENUATION 0x1208
+#define GL_QUADRATIC_ATTENUATION 0x1209
+
+/* InterleavedArrays */
+/* GL_V2F */
+/* GL_V3F */
+/* GL_C4UB_V2F */
+/* GL_C4UB_V3F */
+/* GL_C3F_V3F */
+/* GL_N3F_V3F */
+/* GL_C4F_N3F_V3F */
+/* GL_T2F_V3F */
+/* GL_T4F_V4F */
+/* GL_T2F_C4UB_V3F */
+/* GL_T2F_C3F_V3F */
+/* GL_T2F_N3F_V3F */
+/* GL_T2F_C4F_N3F_V3F */
+/* GL_T4F_C4F_N3F_V4F */
+
+/* ListMode */
+#define GL_COMPILE 0x1300
+#define GL_COMPILE_AND_EXECUTE 0x1301
+
+/* ListNameType */
+/* GL_BYTE */
+/* GL_UNSIGNED_BYTE */
+/* GL_SHORT */
+/* GL_UNSIGNED_SHORT */
+/* GL_INT */
+/* GL_UNSIGNED_INT */
+/* GL_FLOAT */
+/* GL_2_BYTES */
+/* GL_3_BYTES */
+/* GL_4_BYTES */
+
+/* LogicOp */
+#define GL_CLEAR 0x1500
+#define GL_AND 0x1501
+#define GL_AND_REVERSE 0x1502
+#define GL_COPY 0x1503
+#define GL_AND_INVERTED 0x1504
+#define GL_NOOP 0x1505
+#define GL_XOR 0x1506
+#define GL_OR 0x1507
+#define GL_NOR 0x1508
+#define GL_EQUIV 0x1509
+#define GL_INVERT 0x150A
+#define GL_OR_REVERSE 0x150B
+#define GL_COPY_INVERTED 0x150C
+#define GL_OR_INVERTED 0x150D
+#define GL_NAND 0x150E
+#define GL_SET 0x150F
+
+/* MapTarget */
+/* GL_MAP1_COLOR_4 */
+/* GL_MAP1_INDEX */
+/* GL_MAP1_NORMAL */
+/* GL_MAP1_TEXTURE_COORD_1 */
+/* GL_MAP1_TEXTURE_COORD_2 */
+/* GL_MAP1_TEXTURE_COORD_3 */
+/* GL_MAP1_TEXTURE_COORD_4 */
+/* GL_MAP1_VERTEX_3 */
+/* GL_MAP1_VERTEX_4 */
+/* GL_MAP2_COLOR_4 */
+/* GL_MAP2_INDEX */
+/* GL_MAP2_NORMAL */
+/* GL_MAP2_TEXTURE_COORD_1 */
+/* GL_MAP2_TEXTURE_COORD_2 */
+/* GL_MAP2_TEXTURE_COORD_3 */
+/* GL_MAP2_TEXTURE_COORD_4 */
+/* GL_MAP2_VERTEX_3 */
+/* GL_MAP2_VERTEX_4 */
+
+/* MaterialFace */
+/* GL_FRONT */
+/* GL_BACK */
+/* GL_FRONT_AND_BACK */
+
+/* MaterialParameter */
+#define GL_EMISSION 0x1600
+#define GL_SHININESS 0x1601
+#define GL_AMBIENT_AND_DIFFUSE 0x1602
+#define GL_COLOR_INDEXES 0x1603
+/* GL_AMBIENT */
+/* GL_DIFFUSE */
+/* GL_SPECULAR */
+
+/* MatrixMode */
+#define GL_MODELVIEW 0x1700
+#define GL_PROJECTION 0x1701
+#define GL_TEXTURE 0x1702
+
+/* MeshMode1 */
+/* GL_POINT */
+/* GL_LINE */
+
+/* MeshMode2 */
+/* GL_POINT */
+/* GL_LINE */
+/* GL_FILL */
+
+/* MinmaxTarget */
+/* GL_MINMAX */
+
+/* NormalPointerType */
+/* GL_BYTE */
+/* GL_SHORT */
+/* GL_INT */
+/* GL_FLOAT */
+/* GL_DOUBLE */
+
+/* PixelCopyType */
+#define GL_COLOR 0x1800
+#define GL_DEPTH 0x1801
+#define GL_STENCIL 0x1802
+
+/* PixelFormat */
+#define GL_COLOR_INDEX 0x1900
+#define GL_STENCIL_INDEX 0x1901
+#define GL_DEPTH_COMPONENT 0x1902
+#define GL_RED 0x1903
+#define GL_GREEN 0x1904
+#define GL_BLUE 0x1905
+#define GL_ALPHA 0x1906
+#define GL_RGB 0x1907
+#define GL_RGBA 0x1908
+#define GL_LUMINANCE 0x1909
+#define GL_LUMINANCE_ALPHA 0x190A
+/* GL_ABGR */
+
+/* PixelInternalFormat */
+/* GL_ALPHA4 */
+/* GL_ALPHA8 */
+/* GL_ALPHA12 */
+/* GL_ALPHA16 */
+/* GL_LUMINANCE4 */
+/* GL_LUMINANCE8 */
+/* GL_LUMINANCE12 */
+/* GL_LUMINANCE16 */
+/* GL_LUMINANCE4_ALPHA4 */
+/* GL_LUMINANCE6_ALPHA2 */
+/* GL_LUMINANCE8_ALPHA8 */
+/* GL_LUMINANCE12_ALPHA4 */
+/* GL_LUMINANCE12_ALPHA12 */
+/* GL_LUMINANCE16_ALPHA16 */
+/* GL_INTENSITY */
+/* GL_INTENSITY4 */
+/* GL_INTENSITY8 */
+/* GL_INTENSITY12 */
+/* GL_INTENSITY16 */
+/* GL_R3_G3_B2 */
+/* GL_RGB4 */
+/* GL_RGB5 */
+/* GL_RGB8 */
+/* GL_RGB10 */
+/* GL_RGB12 */
+/* GL_RGB16 */
+/* GL_RGBA2 */
+/* GL_RGBA4 */
+/* GL_RGB5_A1 */
+/* GL_RGBA8 */
+/* GL_RGB10_A2 */
+/* GL_RGBA12 */
+/* GL_RGBA16 */
+
+/* PixelMap */
+/* GL_PIXEL_MAP_I_TO_I */
+/* GL_PIXEL_MAP_S_TO_S */
+/* GL_PIXEL_MAP_I_TO_R */
+/* GL_PIXEL_MAP_I_TO_G */
+/* GL_PIXEL_MAP_I_TO_B */
+/* GL_PIXEL_MAP_I_TO_A */
+/* GL_PIXEL_MAP_R_TO_R */
+/* GL_PIXEL_MAP_G_TO_G */
+/* GL_PIXEL_MAP_B_TO_B */
+/* GL_PIXEL_MAP_A_TO_A */
+
+/* PixelStore */
+/* GL_UNPACK_SWAP_BYTES */
+/* GL_UNPACK_LSB_FIRST */
+/* GL_UNPACK_ROW_LENGTH */
+/* GL_UNPACK_SKIP_ROWS */
+/* GL_UNPACK_SKIP_PIXELS */
+/* GL_UNPACK_ALIGNMENT */
+/* GL_PACK_SWAP_BYTES */
+/* GL_PACK_LSB_FIRST */
+/* GL_PACK_ROW_LENGTH */
+/* GL_PACK_SKIP_ROWS */
+/* GL_PACK_SKIP_PIXELS */
+/* GL_PACK_ALIGNMENT */
+/* GL_PACK_SKIP_IMAGES */
+/* GL_PACK_IMAGE_HEIGHT */
+/* GL_UNPACK_SKIP_IMAGES */
+/* GL_UNPACK_IMAGE_HEIGHT */
+
+/* PixelTransfer */
+/* GL_MAP_COLOR */
+/* GL_MAP_STENCIL */
+/* GL_INDEX_SHIFT */
+/* GL_INDEX_OFFSET */
+/* GL_RED_SCALE */
+/* GL_RED_BIAS */
+/* GL_GREEN_SCALE */
+/* GL_GREEN_BIAS */
+/* GL_BLUE_SCALE */
+/* GL_BLUE_BIAS */
+/* GL_ALPHA_SCALE */
+/* GL_ALPHA_BIAS */
+/* GL_DEPTH_SCALE */
+/* GL_DEPTH_BIAS */
+/* GL_POST_CONVOLUTION_RED_SCALE */
+/* GL_POST_CONVOLUTION_GREEN_SCALE */
+/* GL_POST_CONVOLUTION_BLUE_SCALE */
+/* GL_POST_CONVOLUTION_ALPHA_SCALE */
+/* GL_POST_CONVOLUTION_RED_BIAS */
+/* GL_POST_CONVOLUTION_GREEN_BIAS */
+/* GL_POST_CONVOLUTION_BLUE_BIAS */
+/* GL_POST_CONVOLUTION_ALPHA_BIAS */
+/* GL_POST_COLOR_MATRIX_RED_SCALE */
+/* GL_POST_COLOR_MATRIX_GREEN_SCALE */
+/* GL_POST_COLOR_MATRIX_BLUE_SCALE */
+/* GL_POST_COLOR_MATRIX_ALPHA_SCALE */
+/* GL_POST_COLOR_MATRIX_RED_BIAS */
+/* GL_POST_COLOR_MATRIX_GREEN_BIAS */
+/* GL_POST_COLOR_MATRIX_BLUE_BIAS */
+/* GL_POST_COLOR_MATRIX_ALPHA_BIAS */
+
+/* PixelType */
+#define GL_BITMAP 0x1A00
+/* GL_BYTE */
+/* GL_UNSIGNED_BYTE */
+/* GL_SHORT */
+/* GL_UNSIGNED_SHORT */
+/* GL_INT */
+/* GL_UNSIGNED_INT */
+/* GL_FLOAT */
+/* GL_BGR */
+/* GL_BGRA */
+/* GL_UNSIGNED_BYTE_3_3_2 */
+/* GL_UNSIGNED_SHORT_4_4_4_4 */
+/* GL_UNSIGNED_SHORT_5_5_5_1 */
+/* GL_UNSIGNED_INT_8_8_8_8 */
+/* GL_UNSIGNED_INT_10_10_10_2 */
+/* GL_UNSIGNED_SHORT_5_6_5 */
+/* GL_UNSIGNED_BYTE_2_3_3_REV */
+/* GL_UNSIGNED_SHORT_5_6_5_REV */
+/* GL_UNSIGNED_SHORT_4_4_4_4_REV */
+/* GL_UNSIGNED_SHORT_1_5_5_5_REV */
+/* GL_UNSIGNED_INT_8_8_8_8_REV */
+/* GL_UNSIGNED_INT_2_10_10_10_REV */
+
+/* PolygonMode */
+#define GL_POINT 0x1B00
+#define GL_LINE 0x1B01
+#define GL_FILL 0x1B02
+
+/* ReadBufferMode */
+/* GL_FRONT_LEFT */
+/* GL_FRONT_RIGHT */
+/* GL_BACK_LEFT */
+/* GL_BACK_RIGHT */
+/* GL_FRONT */
+/* GL_BACK */
+/* GL_LEFT */
+/* GL_RIGHT */
+/* GL_AUX0 */
+/* GL_AUX1 */
+/* GL_AUX2 */
+/* GL_AUX3 */
+
+/* RenderingMode */
+#define GL_RENDER 0x1C00
+#define GL_FEEDBACK 0x1C01
+#define GL_SELECT 0x1C02
+
+/* SeparableTarget */
+/* GL_SEPARABLE_2D */
+
+/* ShadingModel */
+#define GL_FLAT 0x1D00
+#define GL_SMOOTH 0x1D01
+
+/* StencilFunction */
+/* GL_NEVER */
+/* GL_LESS */
+/* GL_EQUAL */
+/* GL_LEQUAL */
+/* GL_GREATER */
+/* GL_NOTEQUAL */
+/* GL_GEQUAL */
+/* GL_ALWAYS */
+
+/* StencilOp */
+/* GL_ZERO */
+#define GL_KEEP 0x1E00
+#define GL_REPLACE 0x1E01
+#define GL_INCR 0x1E02
+#define GL_DECR 0x1E03
+/* GL_INVERT */
+
+/* StringName */
+#define GL_VENDOR 0x1F00
+#define GL_RENDERER 0x1F01
+#define GL_VERSION 0x1F02
+#define GL_EXTENSIONS 0x1F03
+
+/* TextureCoordName */
+#define GL_S 0x2000
+#define GL_T 0x2001
+#define GL_R 0x2002
+#define GL_Q 0x2003
+
+/* TexCoordPointerType */
+/* GL_SHORT */
+/* GL_INT */
+/* GL_FLOAT */
+/* GL_DOUBLE */
+
+/* TextureEnvMode */
+#define GL_MODULATE 0x2100
+#define GL_DECAL 0x2101
+/* GL_BLEND */
+/* GL_REPLACE */
+
+/* TextureEnvParameter */
+#define GL_TEXTURE_ENV_MODE 0x2200
+#define GL_TEXTURE_ENV_COLOR 0x2201
+
+/* TextureEnvTarget */
+#define GL_TEXTURE_ENV 0x2300
+
+/* TextureGenMode */
+#define GL_EYE_LINEAR 0x2400
+#define GL_OBJECT_LINEAR 0x2401
+#define GL_SPHERE_MAP 0x2402
+
+/* TextureGenParameter */
+#define GL_TEXTURE_GEN_MODE 0x2500
+#define GL_OBJECT_PLANE 0x2501
+#define GL_EYE_PLANE 0x2502
+
+/* TextureMagFilter */
+#define GL_NEAREST 0x2600
+#define GL_LINEAR 0x2601
+
+/* TextureMinFilter */
+/* GL_NEAREST */
+/* GL_LINEAR */
+#define GL_NEAREST_MIPMAP_NEAREST 0x2700
+#define GL_LINEAR_MIPMAP_NEAREST 0x2701
+#define GL_NEAREST_MIPMAP_LINEAR 0x2702
+#define GL_LINEAR_MIPMAP_LINEAR 0x2703
+
+/* TextureParameterName */
+#define GL_TEXTURE_MAG_FILTER 0x2800
+#define GL_TEXTURE_MIN_FILTER 0x2801
+#define GL_TEXTURE_WRAP_S 0x2802
+#define GL_TEXTURE_WRAP_T 0x2803
+/* GL_TEXTURE_BORDER_COLOR */
+/* GL_TEXTURE_PRIORITY */
+/* GL_TEXTURE_WRAP_R */
+/* GL_TEXTURE_MIN_LOD */
+/* GL_TEXTURE_MAX_LOD */
+/* GL_TEXTURE_BASE_LEVEL */
+/* GL_TEXTURE_MAX_LEVEL */
+
+/* TextureTarget */
+/* GL_TEXTURE_1D */
+/* GL_TEXTURE_2D */
+/* GL_PROXY_TEXTURE_1D */
+/* GL_PROXY_TEXTURE_2D */
+/* GL_TEXTURE_3D */
+/* GL_PROXY_TEXTURE_3D */
+
+/* TextureUnit */
+/* GL_TEXTURE0_ARB */
+/* GL_TEXTURE1_ARB */
+/* GL_TEXTURE2_ARB */
+/* GL_TEXTURE3_ARB */
+/* GL_TEXTURE4_ARB */
+/* GL_TEXTURE5_ARB */
+/* GL_TEXTURE6_ARB */
+/* GL_TEXTURE7_ARB */
+/* GL_TEXTURE8_ARB */
+/* GL_TEXTURE9_ARB */
+/* GL_TEXTURE10_ARB */
+/* GL_TEXTURE11_ARB */
+/* GL_TEXTURE12_ARB */
+/* GL_TEXTURE13_ARB */
+/* GL_TEXTURE14_ARB */
+/* GL_TEXTURE15_ARB */
+/* GL_TEXTURE16_ARB */
+/* GL_TEXTURE17_ARB */
+/* GL_TEXTURE18_ARB */
+/* GL_TEXTURE19_ARB */
+/* GL_TEXTURE20_ARB */
+/* GL_TEXTURE21_ARB */
+/* GL_TEXTURE22_ARB */
+/* GL_TEXTURE23_ARB */
+/* GL_TEXTURE24_ARB */
+/* GL_TEXTURE25_ARB */
+/* GL_TEXTURE26_ARB */
+/* GL_TEXTURE27_ARB */
+/* GL_TEXTURE28_ARB */
+/* GL_TEXTURE29_ARB */
+/* GL_TEXTURE30_ARB */
+/* GL_TEXTURE31_ARB */
+
+/* TextureWrapMode */
+#define GL_CLAMP 0x2900
+#define GL_REPEAT 0x2901
+/* GL_CLAMP_TO_EDGE */
+
+/* VertexPointerType */
+/* GL_SHORT */
+/* GL_INT */
+/* GL_FLOAT */
+/* GL_DOUBLE */
+
+/* ClientAttribMask */
+#define GL_CLIENT_PIXEL_STORE_BIT 0x00000001
+#define GL_CLIENT_VERTEX_ARRAY_BIT 0x00000002
+#define GL_CLIENT_ALL_ATTRIB_BITS 0xffffffff
+
+/* polygon_offset */
+#define GL_POLYGON_OFFSET_FACTOR 0x8038
+#define GL_POLYGON_OFFSET_UNITS 0x2A00
+#define GL_POLYGON_OFFSET_POINT 0x2A01
+#define GL_POLYGON_OFFSET_LINE 0x2A02
+#define GL_POLYGON_OFFSET_FILL 0x8037
+
+/* texture */
+#define GL_ALPHA4 0x803B
+#define GL_ALPHA8 0x803C
+#define GL_ALPHA12 0x803D
+#define GL_ALPHA16 0x803E
+#define GL_LUMINANCE4 0x803F
+#define GL_LUMINANCE8 0x8040
+#define GL_LUMINANCE12 0x8041
+#define GL_LUMINANCE16 0x8042
+#define GL_LUMINANCE4_ALPHA4 0x8043
+#define GL_LUMINANCE6_ALPHA2 0x8044
+#define GL_LUMINANCE8_ALPHA8 0x8045
+#define GL_LUMINANCE12_ALPHA4 0x8046
+#define GL_LUMINANCE12_ALPHA12 0x8047
+#define GL_LUMINANCE16_ALPHA16 0x8048
+#define GL_INTENSITY 0x8049
+#define GL_INTENSITY4 0x804A
+#define GL_INTENSITY8 0x804B
+#define GL_INTENSITY12 0x804C
+#define GL_INTENSITY16 0x804D
+#define GL_R3_G3_B2 0x2A10
+#define GL_RGB4 0x804F
+#define GL_RGB5 0x8050
+#define GL_RGB8 0x8051
+#define GL_RGB10 0x8052
+#define GL_RGB12 0x8053
+#define GL_RGB16 0x8054
+#define GL_RGBA2 0x8055
+#define GL_RGBA4 0x8056
+#define GL_RGB5_A1 0x8057
+#define GL_RGBA8 0x8058
+#define GL_RGB10_A2 0x8059
+#define GL_RGBA12 0x805A
+#define GL_RGBA16 0x805B
+#define GL_TEXTURE_RED_SIZE 0x805C
+#define GL_TEXTURE_GREEN_SIZE 0x805D
+#define GL_TEXTURE_BLUE_SIZE 0x805E
+#define GL_TEXTURE_ALPHA_SIZE 0x805F
+#define GL_TEXTURE_LUMINANCE_SIZE 0x8060
+#define GL_TEXTURE_INTENSITY_SIZE 0x8061
+#define GL_PROXY_TEXTURE_1D 0x8063
+#define GL_PROXY_TEXTURE_2D 0x8064
+
+/* texture_object */
+#define GL_TEXTURE_PRIORITY 0x8066
+#define GL_TEXTURE_RESIDENT 0x8067
+#define GL_TEXTURE_BINDING_1D 0x8068
+#define GL_TEXTURE_BINDING_2D 0x8069
+#define GL_TEXTURE_BINDING_3D 0x806A
+
+/* vertex_array */
+#define GL_VERTEX_ARRAY 0x8074
+#define GL_NORMAL_ARRAY 0x8075
+#define GL_COLOR_ARRAY 0x8076
+#define GL_INDEX_ARRAY 0x8077
+#define GL_TEXTURE_COORD_ARRAY 0x8078
+#define GL_EDGE_FLAG_ARRAY 0x8079
+#define GL_VERTEX_ARRAY_SIZE 0x807A
+#define GL_VERTEX_ARRAY_TYPE 0x807B
+#define GL_VERTEX_ARRAY_STRIDE 0x807C
+#define GL_NORMAL_ARRAY_TYPE 0x807E
+#define GL_NORMAL_ARRAY_STRIDE 0x807F
+#define GL_COLOR_ARRAY_SIZE 0x8081
+#define GL_COLOR_ARRAY_TYPE 0x8082
+#define GL_COLOR_ARRAY_STRIDE 0x8083
+#define GL_INDEX_ARRAY_TYPE 0x8085
+#define GL_INDEX_ARRAY_STRIDE 0x8086
+#define GL_TEXTURE_COORD_ARRAY_SIZE 0x8088
+#define GL_TEXTURE_COORD_ARRAY_TYPE 0x8089
+#define GL_TEXTURE_COORD_ARRAY_STRIDE 0x808A
+#define GL_EDGE_FLAG_ARRAY_STRIDE 0x808C
+#define GL_VERTEX_ARRAY_POINTER 0x808E
+#define GL_NORMAL_ARRAY_POINTER 0x808F
+#define GL_COLOR_ARRAY_POINTER 0x8090
+#define GL_INDEX_ARRAY_POINTER 0x8091
+#define GL_TEXTURE_COORD_ARRAY_POINTER 0x8092
+#define GL_EDGE_FLAG_ARRAY_POINTER 0x8093
+#define GL_V2F 0x2A20
+#define GL_V3F 0x2A21
+#define GL_C4UB_V2F 0x2A22
+#define GL_C4UB_V3F 0x2A23
+#define GL_C3F_V3F 0x2A24
+#define GL_N3F_V3F 0x2A25
+#define GL_C4F_N3F_V3F 0x2A26
+#define GL_T2F_V3F 0x2A27
+#define GL_T4F_V4F 0x2A28
+#define GL_T2F_C4UB_V3F 0x2A29
+#define GL_T2F_C3F_V3F 0x2A2A
+#define GL_T2F_N3F_V3F 0x2A2B
+#define GL_T2F_C4F_N3F_V3F 0x2A2C
+#define GL_T4F_C4F_N3F_V4F 0x2A2D
+
+/* bgra */
+#define GL_BGR 0x80E0
+#define GL_BGRA 0x80E1
+
+/* blend_color */
+#define GL_CONSTANT_COLOR 0x8001
+#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002
+#define GL_CONSTANT_ALPHA 0x8003
+#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004
+#define GL_BLEND_COLOR 0x8005
+
+/* blend_minmax */
+#define GL_FUNC_ADD 0x8006
+#define GL_MIN 0x8007
+#define GL_MAX 0x8008
+#define GL_BLEND_EQUATION 0x8009
+
+/* blend_subtract */
+#define GL_FUNC_SUBTRACT 0x800A
+#define GL_FUNC_REVERSE_SUBTRACT 0x800B
+
+/* color_matrix */
+#define GL_COLOR_MATRIX 0x80B1
+#define GL_COLOR_MATRIX_STACK_DEPTH 0x80B2
+#define GL_MAX_COLOR_MATRIX_STACK_DEPTH 0x80B3
+#define GL_POST_COLOR_MATRIX_RED_SCALE 0x80B4
+#define GL_POST_COLOR_MATRIX_GREEN_SCALE 0x80B5
+#define GL_POST_COLOR_MATRIX_BLUE_SCALE 0x80B6
+#define GL_POST_COLOR_MATRIX_ALPHA_SCALE 0x80B7
+#define GL_POST_COLOR_MATRIX_RED_BIAS 0x80B8
+#define GL_POST_COLOR_MATRIX_GREEN_BIAS 0x80B9
+#define GL_POST_COLOR_MATRIX_BLUE_BIAS 0x80BA
+#define GL_POST_COLOR_MATRIX_ALPHA_BIAS 0x80BB
+
+/* color_table */
+#define GL_COLOR_TABLE 0x80D0
+#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1
+#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2
+#define GL_PROXY_COLOR_TABLE 0x80D3
+#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4
+#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5
+#define GL_COLOR_TABLE_SCALE 0x80D6
+#define GL_COLOR_TABLE_BIAS 0x80D7
+#define GL_COLOR_TABLE_FORMAT 0x80D8
+#define GL_COLOR_TABLE_WIDTH 0x80D9
+#define GL_COLOR_TABLE_RED_SIZE 0x80DA
+#define GL_COLOR_TABLE_GREEN_SIZE 0x80DB
+#define GL_COLOR_TABLE_BLUE_SIZE 0x80DC
+#define GL_COLOR_TABLE_ALPHA_SIZE 0x80DD
+#define GL_COLOR_TABLE_LUMINANCE_SIZE 0x80DE
+#define GL_COLOR_TABLE_INTENSITY_SIZE 0x80DF
+
+/* convolution */
+#define GL_CONVOLUTION_1D 0x8010
+#define GL_CONVOLUTION_2D 0x8011
+#define GL_SEPARABLE_2D 0x8012
+#define GL_CONVOLUTION_BORDER_MODE 0x8013
+#define GL_CONVOLUTION_FILTER_SCALE 0x8014
+#define GL_CONVOLUTION_FILTER_BIAS 0x8015
+#define GL_REDUCE 0x8016
+#define GL_CONVOLUTION_FORMAT 0x8017
+#define GL_CONVOLUTION_WIDTH 0x8018
+#define GL_CONVOLUTION_HEIGHT 0x8019
+#define GL_MAX_CONVOLUTION_WIDTH 0x801A
+#define GL_MAX_CONVOLUTION_HEIGHT 0x801B
+#define GL_POST_CONVOLUTION_RED_SCALE 0x801C
+#define GL_POST_CONVOLUTION_GREEN_SCALE 0x801D
+#define GL_POST_CONVOLUTION_BLUE_SCALE 0x801E
+#define GL_POST_CONVOLUTION_ALPHA_SCALE 0x801F
+#define GL_POST_CONVOLUTION_RED_BIAS 0x8020
+#define GL_POST_CONVOLUTION_GREEN_BIAS 0x8021
+#define GL_POST_CONVOLUTION_BLUE_BIAS 0x8022
+#define GL_POST_CONVOLUTION_ALPHA_BIAS 0x8023
+#define GL_CONSTANT_BORDER 0x8151
+#define GL_REPLICATE_BORDER 0x8153
+#define GL_CONVOLUTION_BORDER_COLOR 0x8154
+
+/* draw_range_elements */
+#define GL_MAX_ELEMENTS_VERTICES 0x80E8
+#define GL_MAX_ELEMENTS_INDICES 0x80E9
+
+/* histogram */
+#define GL_HISTOGRAM 0x8024
+#define GL_PROXY_HISTOGRAM 0x8025
+#define GL_HISTOGRAM_WIDTH 0x8026
+#define GL_HISTOGRAM_FORMAT 0x8027
+#define GL_HISTOGRAM_RED_SIZE 0x8028
+#define GL_HISTOGRAM_GREEN_SIZE 0x8029
+#define GL_HISTOGRAM_BLUE_SIZE 0x802A
+#define GL_HISTOGRAM_ALPHA_SIZE 0x802B
+#define GL_HISTOGRAM_LUMINANCE_SIZE 0x802C
+#define GL_HISTOGRAM_SINK 0x802D
+#define GL_MINMAX 0x802E
+#define GL_MINMAX_FORMAT 0x802F
+#define GL_MINMAX_SINK 0x8030
+#define GL_TABLE_TOO_LARGE 0x8031
+
+/* packed_pixels */
+#define GL_UNSIGNED_BYTE_3_3_2 0x8032
+#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
+#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
+#define GL_UNSIGNED_INT_8_8_8_8 0x8035
+#define GL_UNSIGNED_INT_10_10_10_2 0x8036
+#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362
+#define GL_UNSIGNED_SHORT_5_6_5 0x8363
+#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364
+#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365
+#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
+#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
+#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
+
+/* rescale_normal */
+#define GL_RESCALE_NORMAL 0x803A
+
+/* separate_specular_color */
+#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8
+#define GL_SINGLE_COLOR 0x81F9
+#define GL_SEPARATE_SPECULAR_COLOR 0x81FA
+
+/* texture3D */
+#define GL_PACK_SKIP_IMAGES 0x806B
+#define GL_PACK_IMAGE_HEIGHT 0x806C
+#define GL_UNPACK_SKIP_IMAGES 0x806D
+#define GL_UNPACK_IMAGE_HEIGHT 0x806E
+#define GL_TEXTURE_3D 0x806F
+#define GL_PROXY_TEXTURE_3D 0x8070
+#define GL_TEXTURE_DEPTH 0x8071
+#define GL_TEXTURE_WRAP_R 0x8072
+#define GL_MAX_3D_TEXTURE_SIZE 0x8073
+
+/* texture_edge_clamp */
+#define GL_CLAMP_TO_EDGE 0x812F
+
+/* texture_lod */
+#define GL_TEXTURE_MIN_LOD 0x813A
+#define GL_TEXTURE_MAX_LOD 0x813B
+#define GL_TEXTURE_BASE_LEVEL 0x813C
+#define GL_TEXTURE_MAX_LEVEL 0x813D
+
+/* GetTarget1_2 */
+#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12
+#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13
+#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22
+#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23
+#define GL_ALIASED_POINT_SIZE_RANGE 0x846D
+#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E
+
+/* multitexture */
+#define GL_TEXTURE0_ARB 0x84C0
+#define GL_TEXTURE1_ARB 0x84C1
+#define GL_TEXTURE2_ARB 0x84C2
+#define GL_TEXTURE3_ARB 0x84C3
+#define GL_TEXTURE4_ARB 0x84C4
+#define GL_TEXTURE5_ARB 0x84C5
+#define GL_TEXTURE6_ARB 0x84C6
+#define GL_TEXTURE7_ARB 0x84C7
+#define GL_TEXTURE8_ARB 0x84C8
+#define GL_TEXTURE9_ARB 0x84C9
+#define GL_TEXTURE10_ARB 0x84CA
+#define GL_TEXTURE11_ARB 0x84CB
+#define GL_TEXTURE12_ARB 0x84CC
+#define GL_TEXTURE13_ARB 0x84CD
+#define GL_TEXTURE14_ARB 0x84CE
+#define GL_TEXTURE15_ARB 0x84CF
+#define GL_TEXTURE16_ARB 0x84D0
+#define GL_TEXTURE17_ARB 0x84D1
+#define GL_TEXTURE18_ARB 0x84D2
+#define GL_TEXTURE19_ARB 0x84D3
+#define GL_TEXTURE20_ARB 0x84D4
+#define GL_TEXTURE21_ARB 0x84D5
+#define GL_TEXTURE22_ARB 0x84D6
+#define GL_TEXTURE23_ARB 0x84D7
+#define GL_TEXTURE24_ARB 0x84D8
+#define GL_TEXTURE25_ARB 0x84D9
+#define GL_TEXTURE26_ARB 0x84DA
+#define GL_TEXTURE27_ARB 0x84DB
+#define GL_TEXTURE28_ARB 0x84DC
+#define GL_TEXTURE29_ARB 0x84DD
+#define GL_TEXTURE30_ARB 0x84DE
+#define GL_TEXTURE31_ARB 0x84DF
+#define GL_ACTIVE_TEXTURE_ARB 0x84E0
+#define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1
+#define GL_MAX_TEXTURE_UNITS_ARB 0x84E2
+
+/* Extensions */
+#define GL_EXT_abgr 1
+#define GL_EXT_blend_color 1
+#define GL_EXT_blend_minmax 1
+#define GL_EXT_blend_subtract 1
+#define GL_EXT_texture_env_combine 1
+#define GL_EXT_texture_env_add 1
+
+/* EXT_abgr */
+#define GL_ABGR_EXT 0x8000
+
+/* EXT_blend_color */
+#define GL_CONSTANT_COLOR_EXT 0x8001
+#define GL_ONE_MINUS_CONSTANT_COLOR_EXT 0x8002
+#define GL_CONSTANT_ALPHA_EXT 0x8003
+#define GL_ONE_MINUS_CONSTANT_ALPHA_EXT 0x8004
+#define GL_BLEND_COLOR_EXT 0x8005
+
+/* EXT_blend_minmax */
+#define GL_FUNC_ADD_EXT 0x8006
+#define GL_MIN_EXT 0x8007
+#define GL_MAX_EXT 0x8008
+#define GL_BLEND_EQUATION_EXT 0x8009
+
+/* EXT_blend_subtract */
+#define GL_FUNC_SUBTRACT_EXT 0x800A
+#define GL_FUNC_REVERSE_SUBTRACT_EXT 0x800B
+
+/* EXT_texture_env_combine */
+#define GL_COMBINE_EXT 0x8570
+#define GL_COMBINE_RGB_EXT 0x8571
+#define GL_COMBINE_ALPHA_EXT 0x8572
+#define GL_RGB_SCALE_EXT 0x8573
+#define GL_ADD_SIGNED_EXT 0x8574
+#define GL_INTERPOLATE_EXT 0x8575
+#define GL_CONSTANT_EXT 0x8576
+#define GL_PRIMARY_COLOR_EXT 0x8577
+#define GL_PREVIOUS_EXT 0x8578
+#define GL_SOURCE0_RGB_EXT 0x8580
+#define GL_SOURCE1_RGB_EXT 0x8581
+#define GL_SOURCE2_RGB_EXT 0x8582
+#define GL_SOURCE0_ALPHA_EXT 0x8588
+#define GL_SOURCE1_ALPHA_EXT 0x8589
+#define GL_SOURCE2_ALPHA_EXT 0x858A
+#define GL_OPERAND0_RGB_EXT 0x8590
+#define GL_OPERAND1_RGB_EXT 0x8591
+#define GL_OPERAND2_RGB_EXT 0x8592
+#define GL_OPERAND0_ALPHA_EXT 0x8598
+#define GL_OPERAND1_ALPHA_EXT 0x8599
+#define GL_OPERAND2_ALPHA_EXT 0x859A
+
+/* For compatibility with OpenGL v1.0 */
+#define GL_LOGIC_OP GL_INDEX_LOGIC_OP
+#define GL_TEXTURE_COMPONENTS GL_TEXTURE_INTERNAL_FORMAT
+
+/*************************************************************/
+
+WINGDIAPI void APIENTRY glAccum (GLenum op, GLfloat value);
+WINGDIAPI void APIENTRY glAlphaFunc (GLenum func, GLclampf ref);
+WINGDIAPI GLboolean APIENTRY glAreTexturesResident (GLsizei n, const GLuint *textures, GLboolean *residences);
+WINGDIAPI void APIENTRY glArrayElement (GLint i);
+WINGDIAPI void APIENTRY glBegin (GLenum mode);
+WINGDIAPI void APIENTRY glBindTexture (GLenum target, GLuint texture);
+WINGDIAPI void APIENTRY glBitmap (GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap);
+WINGDIAPI void APIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor);
+WINGDIAPI void APIENTRY glCallList (GLuint list);
+WINGDIAPI void APIENTRY glCallLists (GLsizei n, GLenum type, const GLvoid *lists);
+WINGDIAPI void APIENTRY glClear (GLbitfield mask);
+WINGDIAPI void APIENTRY glClearAccum (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+WINGDIAPI void APIENTRY glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
+WINGDIAPI void APIENTRY glClearDepth (GLclampd depth);
+WINGDIAPI void APIENTRY glClearIndex (GLfloat c);
+WINGDIAPI void APIENTRY glClearStencil (GLint s);
+WINGDIAPI void APIENTRY glClipPlane (GLenum plane, const GLdouble *equation);
+WINGDIAPI void APIENTRY glColor3b (GLbyte red, GLbyte green, GLbyte blue);
+WINGDIAPI void APIENTRY glColor3bv (const GLbyte *v);
+WINGDIAPI void APIENTRY glColor3d (GLdouble red, GLdouble green, GLdouble blue);
+WINGDIAPI void APIENTRY glColor3dv (const GLdouble *v);
+WINGDIAPI void APIENTRY glColor3f (GLfloat red, GLfloat green, GLfloat blue);
+WINGDIAPI void APIENTRY glColor3fv (const GLfloat *v);
+WINGDIAPI void APIENTRY glColor3i (GLint red, GLint green, GLint blue);
+WINGDIAPI void APIENTRY glColor3iv (const GLint *v);
+WINGDIAPI void APIENTRY glColor3s (GLshort red, GLshort green, GLshort blue);
+WINGDIAPI void APIENTRY glColor3sv (const GLshort *v);
+WINGDIAPI void APIENTRY glColor3ub (GLubyte red, GLubyte green, GLubyte blue);
+WINGDIAPI void APIENTRY glColor3ubv (const GLubyte *v);
+WINGDIAPI void APIENTRY glColor3ui (GLuint red, GLuint green, GLuint blue);
+WINGDIAPI void APIENTRY glColor3uiv (const GLuint *v);
+WINGDIAPI void APIENTRY glColor3us (GLushort red, GLushort green, GLushort blue);
+WINGDIAPI void APIENTRY glColor3usv (const GLushort *v);
+WINGDIAPI void APIENTRY glColor4b (GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha);
+WINGDIAPI void APIENTRY glColor4bv (const GLbyte *v);
+WINGDIAPI void APIENTRY glColor4d (GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha);
+WINGDIAPI void APIENTRY glColor4dv (const GLdouble *v);
+WINGDIAPI void APIENTRY glColor4f (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+WINGDIAPI void APIENTRY glColor4fv (const GLfloat *v);
+WINGDIAPI void APIENTRY glColor4i (GLint red, GLint green, GLint blue, GLint alpha);
+WINGDIAPI void APIENTRY glColor4iv (const GLint *v);
+WINGDIAPI void APIENTRY glColor4s (GLshort red, GLshort green, GLshort blue, GLshort alpha);
+WINGDIAPI void APIENTRY glColor4sv (const GLshort *v);
+WINGDIAPI void APIENTRY glColor4ub (GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha);
+WINGDIAPI void APIENTRY glColor4ubv (const GLubyte *v);
+WINGDIAPI void APIENTRY glColor4ui (GLuint red, GLuint green, GLuint blue, GLuint alpha);
+WINGDIAPI void APIENTRY glColor4uiv (const GLuint *v);
+WINGDIAPI void APIENTRY glColor4us (GLushort red, GLushort green, GLushort blue, GLushort alpha);
+WINGDIAPI void APIENTRY glColor4usv (const GLushort *v);
+WINGDIAPI void APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
+WINGDIAPI void APIENTRY glColorMaterial (GLenum face, GLenum mode);
+WINGDIAPI void APIENTRY glColorPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
+WINGDIAPI void APIENTRY glCopyPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum type);
+WINGDIAPI void APIENTRY glCopyTexImage1D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border);
+WINGDIAPI void APIENTRY glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
+WINGDIAPI void APIENTRY glCopyTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);
+WINGDIAPI void APIENTRY glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
+WINGDIAPI void APIENTRY glCullFace (GLenum mode);
+WINGDIAPI void APIENTRY glDeleteLists (GLuint list, GLsizei range);
+WINGDIAPI void APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures);
+WINGDIAPI void APIENTRY glDepthFunc (GLenum func);
+WINGDIAPI void APIENTRY glDepthMask (GLboolean flag);
+WINGDIAPI void APIENTRY glDepthRange (GLclampd zNear, GLclampd zFar);
+WINGDIAPI void APIENTRY glDisable (GLenum cap);
+WINGDIAPI void APIENTRY glDisableClientState (GLenum array);
+WINGDIAPI void APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count);
+WINGDIAPI void APIENTRY glDrawBuffer (GLenum mode);
+WINGDIAPI void APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
+WINGDIAPI void APIENTRY glDrawPixels (GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);
+WINGDIAPI void APIENTRY glEdgeFlag (GLboolean flag);
+WINGDIAPI void APIENTRY glEdgeFlagPointer (GLsizei stride, const GLboolean *pointer);
+WINGDIAPI void APIENTRY glEdgeFlagv (const GLboolean *flag);
+WINGDIAPI void APIENTRY glEnable (GLenum cap);
+WINGDIAPI void APIENTRY glEnableClientState (GLenum array);
+WINGDIAPI void APIENTRY glEnd (void);
+WINGDIAPI void APIENTRY glEndList (void);
+WINGDIAPI void APIENTRY glEvalCoord1d (GLdouble u);
+WINGDIAPI void APIENTRY glEvalCoord1dv (const GLdouble *u);
+WINGDIAPI void APIENTRY glEvalCoord1f (GLfloat u);
+WINGDIAPI void APIENTRY glEvalCoord1fv (const GLfloat *u);
+WINGDIAPI void APIENTRY glEvalCoord2d (GLdouble u, GLdouble v);
+WINGDIAPI void APIENTRY glEvalCoord2dv (const GLdouble *u);
+WINGDIAPI void APIENTRY glEvalCoord2f (GLfloat u, GLfloat v);
+WINGDIAPI void APIENTRY glEvalCoord2fv (const GLfloat *u);
+WINGDIAPI void APIENTRY glEvalMesh1 (GLenum mode, GLint i1, GLint i2);
+WINGDIAPI void APIENTRY glEvalMesh2 (GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2);
+WINGDIAPI void APIENTRY glEvalPoint1 (GLint i);
+WINGDIAPI void APIENTRY glEvalPoint2 (GLint i, GLint j);
+WINGDIAPI void APIENTRY glFeedbackBuffer (GLsizei size, GLenum type, GLfloat *buffer);
+WINGDIAPI void APIENTRY glFinish (void);
+WINGDIAPI void APIENTRY glFlush (void);
+WINGDIAPI void APIENTRY glFogf (GLenum pname, GLfloat param);
+WINGDIAPI void APIENTRY glFogfv (GLenum pname, const GLfloat *params);
+WINGDIAPI void APIENTRY glFogi (GLenum pname, GLint param);
+WINGDIAPI void APIENTRY glFogiv (GLenum pname, const GLint *params);
+WINGDIAPI void APIENTRY glFrontFace (GLenum mode);
+WINGDIAPI void APIENTRY glFrustum (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);
+WINGDIAPI GLuint APIENTRY glGenLists (GLsizei range);
+WINGDIAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures);
+WINGDIAPI void APIENTRY glGetBooleanv (GLenum pname, GLboolean *params);
+WINGDIAPI void APIENTRY glGetClipPlane (GLenum plane, GLdouble *equation);
+WINGDIAPI void APIENTRY glGetDoublev (GLenum pname, GLdouble *params);
+WINGDIAPI GLenum APIENTRY glGetError (void);
+WINGDIAPI void APIENTRY glGetFloatv (GLenum pname, GLfloat *params);
+WINGDIAPI void APIENTRY glGetIntegerv (GLenum pname, GLint *params);
+WINGDIAPI void APIENTRY glGetLightfv (GLenum light, GLenum pname, GLfloat *params);
+WINGDIAPI void APIENTRY glGetLightiv (GLenum light, GLenum pname, GLint *params);
+WINGDIAPI void APIENTRY glGetMapdv (GLenum target, GLenum query, GLdouble *v);
+WINGDIAPI void APIENTRY glGetMapfv (GLenum target, GLenum query, GLfloat *v);
+WINGDIAPI void APIENTRY glGetMapiv (GLenum target, GLenum query, GLint *v);
+WINGDIAPI void APIENTRY glGetMaterialfv (GLenum face, GLenum pname, GLfloat *params);
+WINGDIAPI void APIENTRY glGetMaterialiv (GLenum face, GLenum pname, GLint *params);
+WINGDIAPI void APIENTRY glGetPixelMapfv (GLenum map, GLfloat *values);
+WINGDIAPI void APIENTRY glGetPixelMapuiv (GLenum map, GLuint *values);
+WINGDIAPI void APIENTRY glGetPixelMapusv (GLenum map, GLushort *values);
+WINGDIAPI void APIENTRY glGetPointerv (GLenum pname, GLvoid* *params);
+WINGDIAPI void APIENTRY glGetPolygonStipple (GLubyte *mask);
+WINGDIAPI const GLubyte * APIENTRY glGetString (GLenum name);
+WINGDIAPI void APIENTRY glGetTexEnvfv (GLenum target, GLenum pname, GLfloat *params);
+WINGDIAPI void APIENTRY glGetTexEnviv (GLenum target, GLenum pname, GLint *params);
+WINGDIAPI void APIENTRY glGetTexGendv (GLenum coord, GLenum pname, GLdouble *params);
+WINGDIAPI void APIENTRY glGetTexGenfv (GLenum coord, GLenum pname, GLfloat *params);
+WINGDIAPI void APIENTRY glGetTexGeniv (GLenum coord, GLenum pname, GLint *params);
+WINGDIAPI void APIENTRY glGetTexImage (GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels);
+WINGDIAPI void APIENTRY glGetTexLevelParameterfv (GLenum target, GLint level, GLenum pname, GLfloat *params);
+WINGDIAPI void APIENTRY glGetTexLevelParameteriv (GLenum target, GLint level, GLenum pname, GLint *params);
+WINGDIAPI void APIENTRY glGetTexParameterfv (GLenum target, GLenum pname, GLfloat *params);
+WINGDIAPI void APIENTRY glGetTexParameteriv (GLenum target, GLenum pname, GLint *params);
+WINGDIAPI void APIENTRY glHint (GLenum target, GLenum mode);
+WINGDIAPI void APIENTRY glIndexMask (GLuint mask);
+WINGDIAPI void APIENTRY glIndexPointer (GLenum type, GLsizei stride, const GLvoid *pointer);
+WINGDIAPI void APIENTRY glIndexd (GLdouble c);
+WINGDIAPI void APIENTRY glIndexdv (const GLdouble *c);
+WINGDIAPI void APIENTRY glIndexf (GLfloat c);
+WINGDIAPI void APIENTRY glIndexfv (const GLfloat *c);
+WINGDIAPI void APIENTRY glIndexi (GLint c);
+WINGDIAPI void APIENTRY glIndexiv (const GLint *c);
+WINGDIAPI void APIENTRY glIndexs (GLshort c);
+WINGDIAPI void APIENTRY glIndexsv (const GLshort *c);
+WINGDIAPI void APIENTRY glIndexub (GLubyte c);
+WINGDIAPI void APIENTRY glIndexubv (const GLubyte *c);
+WINGDIAPI void APIENTRY glInitNames (void);
+WINGDIAPI void APIENTRY glInterleavedArrays (GLenum format, GLsizei stride, const GLvoid *pointer);
+WINGDIAPI GLboolean APIENTRY glIsEnabled (GLenum cap);
+WINGDIAPI GLboolean APIENTRY glIsList (GLuint list);
+WINGDIAPI GLboolean APIENTRY glIsTexture (GLuint texture);
+WINGDIAPI void APIENTRY glLightModelf (GLenum pname, GLfloat param);
+WINGDIAPI void APIENTRY glLightModelfv (GLenum pname, const GLfloat *params);
+WINGDIAPI void APIENTRY glLightModeli (GLenum pname, GLint param);
+WINGDIAPI void APIENTRY glLightModeliv (GLenum pname, const GLint *params);
+WINGDIAPI void APIENTRY glLightf (GLenum light, GLenum pname, GLfloat param);
+WINGDIAPI void APIENTRY glLightfv (GLenum light, GLenum pname, const GLfloat *params);
+WINGDIAPI void APIENTRY glLighti (GLenum light, GLenum pname, GLint param);
+WINGDIAPI void APIENTRY glLightiv (GLenum light, GLenum pname, const GLint *params);
+WINGDIAPI void APIENTRY glLineStipple (GLint factor, GLushort pattern);
+WINGDIAPI void APIENTRY glLineWidth (GLfloat width);
+WINGDIAPI void APIENTRY glListBase (GLuint base);
+WINGDIAPI void APIENTRY glLoadIdentity (void);
+WINGDIAPI void APIENTRY glLoadMatrixd (const GLdouble *m);
+WINGDIAPI void APIENTRY glLoadMatrixf (const GLfloat *m);
+WINGDIAPI void APIENTRY glLoadName (GLuint name);
+WINGDIAPI void APIENTRY glLogicOp (GLenum opcode);
+WINGDIAPI void APIENTRY glMap1d (GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points);
+WINGDIAPI void APIENTRY glMap1f (GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points);
+WINGDIAPI void APIENTRY glMap2d (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points);
+WINGDIAPI void APIENTRY glMap2f (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points);
+WINGDIAPI void APIENTRY glMapGrid1d (GLint un, GLdouble u1, GLdouble u2);
+WINGDIAPI void APIENTRY glMapGrid1f (GLint un, GLfloat u1, GLfloat u2);
+WINGDIAPI void APIENTRY glMapGrid2d (GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2);
+WINGDIAPI void APIENTRY glMapGrid2f (GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2);
+WINGDIAPI void APIENTRY glMaterialf (GLenum face, GLenum pname, GLfloat param);
+WINGDIAPI void APIENTRY glMaterialfv (GLenum face, GLenum pname, const GLfloat *params);
+WINGDIAPI void APIENTRY glMateriali (GLenum face, GLenum pname, GLint param);
+WINGDIAPI void APIENTRY glMaterialiv (GLenum face, GLenum pname, const GLint *params);
+WINGDIAPI void APIENTRY glMatrixMode (GLenum mode);
+WINGDIAPI void APIENTRY glMultMatrixd (const GLdouble *m);
+WINGDIAPI void APIENTRY glMultMatrixf (const GLfloat *m);
+WINGDIAPI void APIENTRY glNewList (GLuint list, GLenum mode);
+WINGDIAPI void APIENTRY glNormal3b (GLbyte nx, GLbyte ny, GLbyte nz);
+WINGDIAPI void APIENTRY glNormal3bv (const GLbyte *v);
+WINGDIAPI void APIENTRY glNormal3d (GLdouble nx, GLdouble ny, GLdouble nz);
+WINGDIAPI void APIENTRY glNormal3dv (const GLdouble *v);
+WINGDIAPI void APIENTRY glNormal3f (GLfloat nx, GLfloat ny, GLfloat nz);
+WINGDIAPI void APIENTRY glNormal3fv (const GLfloat *v);
+WINGDIAPI void APIENTRY glNormal3i (GLint nx, GLint ny, GLint nz);
+WINGDIAPI void APIENTRY glNormal3iv (const GLint *v);
+WINGDIAPI void APIENTRY glNormal3s (GLshort nx, GLshort ny, GLshort nz);
+WINGDIAPI void APIENTRY glNormal3sv (const GLshort *v);
+WINGDIAPI void APIENTRY glNormalPointer (GLenum type, GLsizei stride, const GLvoid *pointer);
+WINGDIAPI void APIENTRY glOrtho (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);
+WINGDIAPI void APIENTRY glPassThrough (GLfloat token);
+WINGDIAPI void APIENTRY glPixelMapfv (GLenum map, GLint mapsize, const GLfloat *values);
+WINGDIAPI void APIENTRY glPixelMapuiv (GLenum map, GLint mapsize, const GLuint *values);
+WINGDIAPI void APIENTRY glPixelMapusv (GLenum map, GLint mapsize, const GLushort *values);
+WINGDIAPI void APIENTRY glPixelStoref (GLenum pname, GLfloat param);
+WINGDIAPI void APIENTRY glPixelStorei (GLenum pname, GLint param);
+WINGDIAPI void APIENTRY glPixelTransferf (GLenum pname, GLfloat param);
+WINGDIAPI void APIENTRY glPixelTransferi (GLenum pname, GLint param);
+WINGDIAPI void APIENTRY glPixelZoom (GLfloat xfactor, GLfloat yfactor);
+WINGDIAPI void APIENTRY glPointSize (GLfloat size);
+WINGDIAPI void APIENTRY glPolygonMode (GLenum face, GLenum mode);
+WINGDIAPI void APIENTRY glPolygonOffset (GLfloat factor, GLfloat units);
+WINGDIAPI void APIENTRY glPolygonStipple (const GLubyte *mask);
+WINGDIAPI void APIENTRY glPopAttrib (void);
+WINGDIAPI void APIENTRY glPopClientAttrib (void);
+WINGDIAPI void APIENTRY glPopMatrix (void);
+WINGDIAPI void APIENTRY glPopName (void);
+WINGDIAPI void APIENTRY glPrioritizeTextures (GLsizei n, const GLuint *textures, const GLclampf *priorities);
+WINGDIAPI void APIENTRY glPushAttrib (GLbitfield mask);
+WINGDIAPI void APIENTRY glPushClientAttrib (GLbitfield mask);
+WINGDIAPI void APIENTRY glPushMatrix (void);
+WINGDIAPI void APIENTRY glPushName (GLuint name);
+WINGDIAPI void APIENTRY glRasterPos2d (GLdouble x, GLdouble y);
+WINGDIAPI void APIENTRY glRasterPos2dv (const GLdouble *v);
+WINGDIAPI void APIENTRY glRasterPos2f (GLfloat x, GLfloat y);
+WINGDIAPI void APIENTRY glRasterPos2fv (const GLfloat *v);
+WINGDIAPI void APIENTRY glRasterPos2i (GLint x, GLint y);
+WINGDIAPI void APIENTRY glRasterPos2iv (const GLint *v);
+WINGDIAPI void APIENTRY glRasterPos2s (GLshort x, GLshort y);
+WINGDIAPI void APIENTRY glRasterPos2sv (const GLshort *v);
+WINGDIAPI void APIENTRY glRasterPos3d (GLdouble x, GLdouble y, GLdouble z);
+WINGDIAPI void APIENTRY glRasterPos3dv (const GLdouble *v);
+WINGDIAPI void APIENTRY glRasterPos3f (GLfloat x, GLfloat y, GLfloat z);
+WINGDIAPI void APIENTRY glRasterPos3fv (const GLfloat *v);
+WINGDIAPI void APIENTRY glRasterPos3i (GLint x, GLint y, GLint z);
+WINGDIAPI void APIENTRY glRasterPos3iv (const GLint *v);
+WINGDIAPI void APIENTRY glRasterPos3s (GLshort x, GLshort y, GLshort z);
+WINGDIAPI void APIENTRY glRasterPos3sv (const GLshort *v);
+WINGDIAPI void APIENTRY glRasterPos4d (GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+WINGDIAPI void APIENTRY glRasterPos4dv (const GLdouble *v);
+WINGDIAPI void APIENTRY glRasterPos4f (GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+WINGDIAPI void APIENTRY glRasterPos4fv (const GLfloat *v);
+WINGDIAPI void APIENTRY glRasterPos4i (GLint x, GLint y, GLint z, GLint w);
+WINGDIAPI void APIENTRY glRasterPos4iv (const GLint *v);
+WINGDIAPI void APIENTRY glRasterPos4s (GLshort x, GLshort y, GLshort z, GLshort w);
+WINGDIAPI void APIENTRY glRasterPos4sv (const GLshort *v);
+WINGDIAPI void APIENTRY glReadBuffer (GLenum mode);
+WINGDIAPI void APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);
+WINGDIAPI void APIENTRY glRectd (GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2);
+WINGDIAPI void APIENTRY glRectdv (const GLdouble *v1, const GLdouble *v2);
+WINGDIAPI void APIENTRY glRectf (GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2);
+WINGDIAPI void APIENTRY glRectfv (const GLfloat *v1, const GLfloat *v2);
+WINGDIAPI void APIENTRY glRecti (GLint x1, GLint y1, GLint x2, GLint y2);
+WINGDIAPI void APIENTRY glRectiv (const GLint *v1, const GLint *v2);
+WINGDIAPI void APIENTRY glRects (GLshort x1, GLshort y1, GLshort x2, GLshort y2);
+WINGDIAPI void APIENTRY glRectsv (const GLshort *v1, const GLshort *v2);
+WINGDIAPI GLint APIENTRY glRenderMode (GLenum mode);
+WINGDIAPI void APIENTRY glRotated (GLdouble angle, GLdouble x, GLdouble y, GLdouble z);
+WINGDIAPI void APIENTRY glRotatef (GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
+WINGDIAPI void APIENTRY glScaled (GLdouble x, GLdouble y, GLdouble z);
+WINGDIAPI void APIENTRY glScalef (GLfloat x, GLfloat y, GLfloat z);
+WINGDIAPI void APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height);
+WINGDIAPI void APIENTRY glSelectBuffer (GLsizei size, GLuint *buffer);
+WINGDIAPI void APIENTRY glShadeModel (GLenum mode);
+WINGDIAPI void APIENTRY glStencilFunc (GLenum func, GLint ref, GLuint mask);
+WINGDIAPI void APIENTRY glStencilMask (GLuint mask);
+WINGDIAPI void APIENTRY glStencilOp (GLenum fail, GLenum zfail, GLenum zpass);
+WINGDIAPI void APIENTRY glTexCoord1d (GLdouble s);
+WINGDIAPI void APIENTRY glTexCoord1dv (const GLdouble *v);
+WINGDIAPI void APIENTRY glTexCoord1f (GLfloat s);
+WINGDIAPI void APIENTRY glTexCoord1fv (const GLfloat *v);
+WINGDIAPI void APIENTRY glTexCoord1i (GLint s);
+WINGDIAPI void APIENTRY glTexCoord1iv (const GLint *v);
+WINGDIAPI void APIENTRY glTexCoord1s (GLshort s);
+WINGDIAPI void APIENTRY glTexCoord1sv (const GLshort *v);
+WINGDIAPI void APIENTRY glTexCoord2d (GLdouble s, GLdouble t);
+WINGDIAPI void APIENTRY glTexCoord2dv (const GLdouble *v);
+WINGDIAPI void APIENTRY glTexCoord2f (GLfloat s, GLfloat t);
+WINGDIAPI void APIENTRY glTexCoord2fv (const GLfloat *v);
+WINGDIAPI void APIENTRY glTexCoord2i (GLint s, GLint t);
+WINGDIAPI void APIENTRY glTexCoord2iv (const GLint *v);
+WINGDIAPI void APIENTRY glTexCoord2s (GLshort s, GLshort t);
+WINGDIAPI void APIENTRY glTexCoord2sv (const GLshort *v);
+WINGDIAPI void APIENTRY glTexCoord3d (GLdouble s, GLdouble t, GLdouble r);
+WINGDIAPI void APIENTRY glTexCoord3dv (const GLdouble *v);
+WINGDIAPI void APIENTRY glTexCoord3f (GLfloat s, GLfloat t, GLfloat r);
+WINGDIAPI void APIENTRY glTexCoord3fv (const GLfloat *v);
+WINGDIAPI void APIENTRY glTexCoord3i (GLint s, GLint t, GLint r);
+WINGDIAPI void APIENTRY glTexCoord3iv (const GLint *v);
+WINGDIAPI void APIENTRY glTexCoord3s (GLshort s, GLshort t, GLshort r);
+WINGDIAPI void APIENTRY glTexCoord3sv (const GLshort *v);
+WINGDIAPI void APIENTRY glTexCoord4d (GLdouble s, GLdouble t, GLdouble r, GLdouble q);
+WINGDIAPI void APIENTRY glTexCoord4dv (const GLdouble *v);
+WINGDIAPI void APIENTRY glTexCoord4f (GLfloat s, GLfloat t, GLfloat r, GLfloat q);
+WINGDIAPI void APIENTRY glTexCoord4fv (const GLfloat *v);
+WINGDIAPI void APIENTRY glTexCoord4i (GLint s, GLint t, GLint r, GLint q);
+WINGDIAPI void APIENTRY glTexCoord4iv (const GLint *v);
+WINGDIAPI void APIENTRY glTexCoord4s (GLshort s, GLshort t, GLshort r, GLshort q);
+WINGDIAPI void APIENTRY glTexCoord4sv (const GLshort *v);
+WINGDIAPI void APIENTRY glTexCoordPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
+WINGDIAPI void APIENTRY glTexEnvf (GLenum target, GLenum pname, GLfloat param);
+WINGDIAPI void APIENTRY glTexEnvfv (GLenum target, GLenum pname, const GLfloat *params);
+WINGDIAPI void APIENTRY glTexEnvi (GLenum target, GLenum pname, GLint param);
+WINGDIAPI void APIENTRY glTexEnviv (GLenum target, GLenum pname, const GLint *params);
+WINGDIAPI void APIENTRY glTexGend (GLenum coord, GLenum pname, GLdouble param);
+WINGDIAPI void APIENTRY glTexGendv (GLenum coord, GLenum pname, const GLdouble *params);
+WINGDIAPI void APIENTRY glTexGenf (GLenum coord, GLenum pname, GLfloat param);
+WINGDIAPI void APIENTRY glTexGenfv (GLenum coord, GLenum pname, const GLfloat *params);
+WINGDIAPI void APIENTRY glTexGeni (GLenum coord, GLenum pname, GLint param);
+WINGDIAPI void APIENTRY glTexGeniv (GLenum coord, GLenum pname, const GLint *params);
+WINGDIAPI void APIENTRY glTexImage1D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
+WINGDIAPI void APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
+WINGDIAPI void APIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param);
+WINGDIAPI void APIENTRY glTexParameterfv (GLenum target, GLenum pname, const GLfloat *params);
+WINGDIAPI void APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param);
+WINGDIAPI void APIENTRY glTexParameteriv (GLenum target, GLenum pname, const GLint *params);
+WINGDIAPI void APIENTRY glTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels);
+WINGDIAPI void APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);
+WINGDIAPI void APIENTRY glTranslated (GLdouble x, GLdouble y, GLdouble z);
+WINGDIAPI void APIENTRY glTranslatef (GLfloat x, GLfloat y, GLfloat z);
+WINGDIAPI void APIENTRY glVertex2d (GLdouble x, GLdouble y);
+WINGDIAPI void APIENTRY glVertex2dv (const GLdouble *v);
+WINGDIAPI void APIENTRY glVertex2f (GLfloat x, GLfloat y);
+WINGDIAPI void APIENTRY glVertex2fv (const GLfloat *v);
+WINGDIAPI void APIENTRY glVertex2i (GLint x, GLint y);
+WINGDIAPI void APIENTRY glVertex2iv (const GLint *v);
+WINGDIAPI void APIENTRY glVertex2s (GLshort x, GLshort y);
+WINGDIAPI void APIENTRY glVertex2sv (const GLshort *v);
+WINGDIAPI void APIENTRY glVertex3d (GLdouble x, GLdouble y, GLdouble z);
+WINGDIAPI void APIENTRY glVertex3dv (const GLdouble *v);
+WINGDIAPI void APIENTRY glVertex3f (GLfloat x, GLfloat y, GLfloat z);
+WINGDIAPI void APIENTRY glVertex3fv (const GLfloat *v);
+WINGDIAPI void APIENTRY glVertex3i (GLint x, GLint y, GLint z);
+WINGDIAPI void APIENTRY glVertex3iv (const GLint *v);
+WINGDIAPI void APIENTRY glVertex3s (GLshort x, GLshort y, GLshort z);
+WINGDIAPI void APIENTRY glVertex3sv (const GLshort *v);
+WINGDIAPI void APIENTRY glVertex4d (GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+WINGDIAPI void APIENTRY glVertex4dv (const GLdouble *v);
+WINGDIAPI void APIENTRY glVertex4f (GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+WINGDIAPI void APIENTRY glVertex4fv (const GLfloat *v);
+WINGDIAPI void APIENTRY glVertex4i (GLint x, GLint y, GLint z, GLint w);
+WINGDIAPI void APIENTRY glVertex4iv (const GLint *v);
+WINGDIAPI void APIENTRY glVertex4s (GLshort x, GLshort y, GLshort z, GLshort w);
+WINGDIAPI void APIENTRY glVertex4sv (const GLshort *v);
+WINGDIAPI void APIENTRY glVertexPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
+WINGDIAPI void APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __gl_h_ */
diff --git a/Runtime/GfxDevice/opengl/unity_glext.h b/Runtime/GfxDevice/opengl/unity_glext.h
new file mode 100644
index 0000000..b048ad8
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/unity_glext.h
@@ -0,0 +1,6704 @@
+#ifndef __glext_h_
+#define __glext_h_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+** License Applicability. Except to the extent portions of this file are
+** made subject to an alternative license as permitted in the SGI Free
+** Software License B, Version 1.1 (the "License"), the contents of this
+** file are subject only to the provisions of the License. You may not use
+** this file except in compliance with the License. You may obtain a copy
+** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
+** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
+**
+** http://oss.sgi.com/projects/FreeB
+**
+** Note that, as provided in the License, the Software is distributed on an
+** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
+** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
+** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
+** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
+**
+** Original Code. The Original Code is: OpenGL Sample Implementation,
+** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
+** Inc. The Original Code is Copyright (c) 1991-2004 Silicon Graphics, Inc.
+** Copyright in any portions created by third parties is as indicated
+** elsewhere herein. All Rights Reserved.
+**
+** Additional Notice Provisions: This software was created using the
+** OpenGL(R) version 1.2.1 Sample Implementation published by SGI, but has
+** not been independently verified as being compliant with the OpenGL(R)
+** version 1.2.1 Specification.
+*/
+
+#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
+#define WIN32_LEAN_AND_MEAN 1
+#include <windows.h>
+#endif
+
+#ifndef APIENTRY
+#define APIENTRY
+#endif
+#ifndef APIENTRYP
+#define APIENTRYP APIENTRY *
+#endif
+#ifndef GLAPI
+#define GLAPI extern
+#endif
+
+/*************************************************************/
+
+/* Header file version number, required by OpenGL ABI for Linux */
+/* glext.h last updated 2005/06/20 */
+/* Current version at http://oss.sgi.com/projects/ogl-sample/registry/ */
+#define GL_GLEXT_VERSION 29
+
+#ifndef GL_VERSION_1_2
+#define GL_UNSIGNED_BYTE_3_3_2 0x8032
+#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
+#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
+#define GL_UNSIGNED_INT_8_8_8_8 0x8035
+#define GL_UNSIGNED_INT_10_10_10_2 0x8036
+#define GL_RESCALE_NORMAL 0x803A
+#define GL_TEXTURE_BINDING_3D 0x806A
+#define GL_PACK_SKIP_IMAGES 0x806B
+#define GL_PACK_IMAGE_HEIGHT 0x806C
+#define GL_UNPACK_SKIP_IMAGES 0x806D
+#define GL_UNPACK_IMAGE_HEIGHT 0x806E
+#define GL_TEXTURE_3D 0x806F
+#define GL_PROXY_TEXTURE_3D 0x8070
+#define GL_TEXTURE_DEPTH 0x8071
+#define GL_TEXTURE_WRAP_R 0x8072
+#define GL_MAX_3D_TEXTURE_SIZE 0x8073
+#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362
+#define GL_UNSIGNED_SHORT_5_6_5 0x8363
+#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364
+#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365
+#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
+#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
+#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
+#define GL_BGR 0x80E0
+#define GL_BGRA 0x80E1
+#define GL_MAX_ELEMENTS_VERTICES 0x80E8
+#define GL_MAX_ELEMENTS_INDICES 0x80E9
+#define GL_CLAMP_TO_EDGE 0x812F
+#define GL_TEXTURE_MIN_LOD 0x813A
+#define GL_TEXTURE_MAX_LOD 0x813B
+#define GL_TEXTURE_BASE_LEVEL 0x813C
+#define GL_TEXTURE_MAX_LEVEL 0x813D
+#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8
+#define GL_SINGLE_COLOR 0x81F9
+#define GL_SEPARATE_SPECULAR_COLOR 0x81FA
+#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12
+#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13
+#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22
+#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23
+#define GL_ALIASED_POINT_SIZE_RANGE 0x846D
+#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E
+#endif
+
+#ifndef GL_ARB_imaging
+#define GL_CONSTANT_COLOR 0x8001
+#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002
+#define GL_CONSTANT_ALPHA 0x8003
+#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004
+#define GL_BLEND_COLOR 0x8005
+#define GL_FUNC_ADD 0x8006
+#define GL_MIN 0x8007
+#define GL_MAX 0x8008
+#define GL_BLEND_EQUATION 0x8009
+#define GL_FUNC_SUBTRACT 0x800A
+#define GL_FUNC_REVERSE_SUBTRACT 0x800B
+#define GL_CONVOLUTION_1D 0x8010
+#define GL_CONVOLUTION_2D 0x8011
+#define GL_SEPARABLE_2D 0x8012
+#define GL_CONVOLUTION_BORDER_MODE 0x8013
+#define GL_CONVOLUTION_FILTER_SCALE 0x8014
+#define GL_CONVOLUTION_FILTER_BIAS 0x8015
+#define GL_REDUCE 0x8016
+#define GL_CONVOLUTION_FORMAT 0x8017
+#define GL_CONVOLUTION_WIDTH 0x8018
+#define GL_CONVOLUTION_HEIGHT 0x8019
+#define GL_MAX_CONVOLUTION_WIDTH 0x801A
+#define GL_MAX_CONVOLUTION_HEIGHT 0x801B
+#define GL_POST_CONVOLUTION_RED_SCALE 0x801C
+#define GL_POST_CONVOLUTION_GREEN_SCALE 0x801D
+#define GL_POST_CONVOLUTION_BLUE_SCALE 0x801E
+#define GL_POST_CONVOLUTION_ALPHA_SCALE 0x801F
+#define GL_POST_CONVOLUTION_RED_BIAS 0x8020
+#define GL_POST_CONVOLUTION_GREEN_BIAS 0x8021
+#define GL_POST_CONVOLUTION_BLUE_BIAS 0x8022
+#define GL_POST_CONVOLUTION_ALPHA_BIAS 0x8023
+#define GL_HISTOGRAM 0x8024
+#define GL_PROXY_HISTOGRAM 0x8025
+#define GL_HISTOGRAM_WIDTH 0x8026
+#define GL_HISTOGRAM_FORMAT 0x8027
+#define GL_HISTOGRAM_RED_SIZE 0x8028
+#define GL_HISTOGRAM_GREEN_SIZE 0x8029
+#define GL_HISTOGRAM_BLUE_SIZE 0x802A
+#define GL_HISTOGRAM_ALPHA_SIZE 0x802B
+#define GL_HISTOGRAM_LUMINANCE_SIZE 0x802C
+#define GL_HISTOGRAM_SINK 0x802D
+#define GL_MINMAX 0x802E
+#define GL_MINMAX_FORMAT 0x802F
+#define GL_MINMAX_SINK 0x8030
+#define GL_TABLE_TOO_LARGE 0x8031
+#define GL_COLOR_MATRIX 0x80B1
+#define GL_COLOR_MATRIX_STACK_DEPTH 0x80B2
+#define GL_MAX_COLOR_MATRIX_STACK_DEPTH 0x80B3
+#define GL_POST_COLOR_MATRIX_RED_SCALE 0x80B4
+#define GL_POST_COLOR_MATRIX_GREEN_SCALE 0x80B5
+#define GL_POST_COLOR_MATRIX_BLUE_SCALE 0x80B6
+#define GL_POST_COLOR_MATRIX_ALPHA_SCALE 0x80B7
+#define GL_POST_COLOR_MATRIX_RED_BIAS 0x80B8
+#define GL_POST_COLOR_MATRIX_GREEN_BIAS 0x80B9
+#define GL_POST_COLOR_MATRIX_BLUE_BIAS 0x80BA
+#define GL_POST_COLOR_MATRIX_ALPHA_BIAS 0x80BB
+#define GL_COLOR_TABLE 0x80D0
+#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1
+#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2
+#define GL_PROXY_COLOR_TABLE 0x80D3
+#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4
+#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5
+#define GL_COLOR_TABLE_SCALE 0x80D6
+#define GL_COLOR_TABLE_BIAS 0x80D7
+#define GL_COLOR_TABLE_FORMAT 0x80D8
+#define GL_COLOR_TABLE_WIDTH 0x80D9
+#define GL_COLOR_TABLE_RED_SIZE 0x80DA
+#define GL_COLOR_TABLE_GREEN_SIZE 0x80DB
+#define GL_COLOR_TABLE_BLUE_SIZE 0x80DC
+#define GL_COLOR_TABLE_ALPHA_SIZE 0x80DD
+#define GL_COLOR_TABLE_LUMINANCE_SIZE 0x80DE
+#define GL_COLOR_TABLE_INTENSITY_SIZE 0x80DF
+#define GL_CONSTANT_BORDER 0x8151
+#define GL_REPLICATE_BORDER 0x8153
+#define GL_CONVOLUTION_BORDER_COLOR 0x8154
+#endif
+
+#ifndef GL_VERSION_1_3
+#define GL_TEXTURE0 0x84C0
+#define GL_TEXTURE1 0x84C1
+#define GL_TEXTURE2 0x84C2
+#define GL_TEXTURE3 0x84C3
+#define GL_TEXTURE4 0x84C4
+#define GL_TEXTURE5 0x84C5
+#define GL_TEXTURE6 0x84C6
+#define GL_TEXTURE7 0x84C7
+#define GL_TEXTURE8 0x84C8
+#define GL_TEXTURE9 0x84C9
+#define GL_TEXTURE10 0x84CA
+#define GL_TEXTURE11 0x84CB
+#define GL_TEXTURE12 0x84CC
+#define GL_TEXTURE13 0x84CD
+#define GL_TEXTURE14 0x84CE
+#define GL_TEXTURE15 0x84CF
+#define GL_TEXTURE16 0x84D0
+#define GL_TEXTURE17 0x84D1
+#define GL_TEXTURE18 0x84D2
+#define GL_TEXTURE19 0x84D3
+#define GL_TEXTURE20 0x84D4
+#define GL_TEXTURE21 0x84D5
+#define GL_TEXTURE22 0x84D6
+#define GL_TEXTURE23 0x84D7
+#define GL_TEXTURE24 0x84D8
+#define GL_TEXTURE25 0x84D9
+#define GL_TEXTURE26 0x84DA
+#define GL_TEXTURE27 0x84DB
+#define GL_TEXTURE28 0x84DC
+#define GL_TEXTURE29 0x84DD
+#define GL_TEXTURE30 0x84DE
+#define GL_TEXTURE31 0x84DF
+#define GL_ACTIVE_TEXTURE 0x84E0
+#define GL_CLIENT_ACTIVE_TEXTURE 0x84E1
+#define GL_MAX_TEXTURE_UNITS 0x84E2
+#define GL_TRANSPOSE_MODELVIEW_MATRIX 0x84E3
+#define GL_TRANSPOSE_PROJECTION_MATRIX 0x84E4
+#define GL_TRANSPOSE_TEXTURE_MATRIX 0x84E5
+#define GL_TRANSPOSE_COLOR_MATRIX 0x84E6
+#define GL_MULTISAMPLE 0x809D
+#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E
+#define GL_SAMPLE_ALPHA_TO_ONE 0x809F
+#define GL_SAMPLE_COVERAGE 0x80A0
+#define GL_SAMPLE_BUFFERS 0x80A8
+#define GL_SAMPLES 0x80A9
+#define GL_SAMPLE_COVERAGE_VALUE 0x80AA
+#define GL_SAMPLE_COVERAGE_INVERT 0x80AB
+#define GL_MULTISAMPLE_BIT 0x20000000
+#define GL_NORMAL_MAP 0x8511
+#define GL_REFLECTION_MAP 0x8512
+#define GL_TEXTURE_CUBE_MAP 0x8513
+#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A
+#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B
+#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C
+#define GL_COMPRESSED_ALPHA 0x84E9
+#define GL_COMPRESSED_LUMINANCE 0x84EA
+#define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB
+#define GL_COMPRESSED_INTENSITY 0x84EC
+#define GL_COMPRESSED_RGB 0x84ED
+#define GL_COMPRESSED_RGBA 0x84EE
+#define GL_TEXTURE_COMPRESSION_HINT 0x84EF
+#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0
+#define GL_TEXTURE_COMPRESSED 0x86A1
+#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2
+#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3
+#define GL_CLAMP_TO_BORDER 0x812D
+#define GL_COMBINE 0x8570
+#define GL_COMBINE_RGB 0x8571
+#define GL_COMBINE_ALPHA 0x8572
+#define GL_SOURCE0_RGB 0x8580
+#define GL_SOURCE1_RGB 0x8581
+#define GL_SOURCE2_RGB 0x8582
+#define GL_SOURCE0_ALPHA 0x8588
+#define GL_SOURCE1_ALPHA 0x8589
+#define GL_SOURCE2_ALPHA 0x858A
+#define GL_OPERAND0_RGB 0x8590
+#define GL_OPERAND1_RGB 0x8591
+#define GL_OPERAND2_RGB 0x8592
+#define GL_OPERAND0_ALPHA 0x8598
+#define GL_OPERAND1_ALPHA 0x8599
+#define GL_OPERAND2_ALPHA 0x859A
+#define GL_RGB_SCALE 0x8573
+#define GL_ADD_SIGNED 0x8574
+#define GL_INTERPOLATE 0x8575
+#define GL_SUBTRACT 0x84E7
+#define GL_CONSTANT 0x8576
+#define GL_PRIMARY_COLOR 0x8577
+#define GL_PREVIOUS 0x8578
+#define GL_DOT3_RGB 0x86AE
+#define GL_DOT3_RGBA 0x86AF
+#endif
+
+#ifndef GL_VERSION_1_4
+#define GL_BLEND_DST_RGB 0x80C8
+#define GL_BLEND_SRC_RGB 0x80C9
+#define GL_BLEND_DST_ALPHA 0x80CA
+#define GL_BLEND_SRC_ALPHA 0x80CB
+#define GL_POINT_SIZE_MIN 0x8126
+#define GL_POINT_SIZE_MAX 0x8127
+#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128
+#define GL_POINT_DISTANCE_ATTENUATION 0x8129
+#define GL_GENERATE_MIPMAP 0x8191
+#define GL_GENERATE_MIPMAP_HINT 0x8192
+#define GL_DEPTH_COMPONENT16 0x81A5
+#define GL_DEPTH_COMPONENT24 0x81A6
+#define GL_DEPTH_COMPONENT32 0x81A7
+#define GL_MIRRORED_REPEAT 0x8370
+#define GL_FOG_COORDINATE_SOURCE 0x8450
+#define GL_FOG_COORDINATE 0x8451
+#define GL_FRAGMENT_DEPTH 0x8452
+#define GL_CURRENT_FOG_COORDINATE 0x8453
+#define GL_FOG_COORDINATE_ARRAY_TYPE 0x8454
+#define GL_FOG_COORDINATE_ARRAY_STRIDE 0x8455
+#define GL_FOG_COORDINATE_ARRAY_POINTER 0x8456
+#define GL_FOG_COORDINATE_ARRAY 0x8457
+#define GL_COLOR_SUM 0x8458
+#define GL_CURRENT_SECONDARY_COLOR 0x8459
+#define GL_SECONDARY_COLOR_ARRAY_SIZE 0x845A
+#define GL_SECONDARY_COLOR_ARRAY_TYPE 0x845B
+#define GL_SECONDARY_COLOR_ARRAY_STRIDE 0x845C
+#define GL_SECONDARY_COLOR_ARRAY_POINTER 0x845D
+#define GL_SECONDARY_COLOR_ARRAY 0x845E
+#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD
+#define GL_TEXTURE_FILTER_CONTROL 0x8500
+#define GL_TEXTURE_LOD_BIAS 0x8501
+#define GL_INCR_WRAP 0x8507
+#define GL_DECR_WRAP 0x8508
+#define GL_TEXTURE_DEPTH_SIZE 0x884A
+#define GL_DEPTH_TEXTURE_MODE 0x884B
+#define GL_TEXTURE_COMPARE_MODE 0x884C
+#define GL_TEXTURE_COMPARE_FUNC 0x884D
+#define GL_COMPARE_R_TO_TEXTURE 0x884E
+#endif
+
+#ifndef GL_VERSION_1_5
+#define GL_BUFFER_SIZE 0x8764
+#define GL_BUFFER_USAGE 0x8765
+#define GL_QUERY_COUNTER_BITS 0x8864
+#define GL_CURRENT_QUERY 0x8865
+#define GL_QUERY_RESULT 0x8866
+#define GL_QUERY_RESULT_AVAILABLE 0x8867
+#define GL_ARRAY_BUFFER 0x8892
+#define GL_ELEMENT_ARRAY_BUFFER 0x8893
+#define GL_ARRAY_BUFFER_BINDING 0x8894
+#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
+#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896
+#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897
+#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898
+#define GL_INDEX_ARRAY_BUFFER_BINDING 0x8899
+#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A
+#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING 0x889B
+#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING 0x889C
+#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING 0x889D
+#define GL_WEIGHT_ARRAY_BUFFER_BINDING 0x889E
+#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F
+#define GL_READ_ONLY 0x88B8
+#define GL_WRITE_ONLY 0x88B9
+#define GL_READ_WRITE 0x88BA
+#define GL_BUFFER_ACCESS 0x88BB
+#define GL_BUFFER_MAPPED 0x88BC
+#define GL_BUFFER_MAP_POINTER 0x88BD
+#define GL_STREAM_DRAW 0x88E0
+#define GL_STREAM_READ 0x88E1
+#define GL_STREAM_COPY 0x88E2
+#define GL_STATIC_DRAW 0x88E4
+#define GL_STATIC_READ 0x88E5
+#define GL_STATIC_COPY 0x88E6
+#define GL_DYNAMIC_DRAW 0x88E8
+#define GL_DYNAMIC_READ 0x88E9
+#define GL_DYNAMIC_COPY 0x88EA
+#define GL_SAMPLES_PASSED 0x8914
+#define GL_FOG_COORD_SRC GL_FOG_COORDINATE_SOURCE
+#define GL_FOG_COORD GL_FOG_COORDINATE
+#define GL_CURRENT_FOG_COORD GL_CURRENT_FOG_COORDINATE
+#define GL_FOG_COORD_ARRAY_TYPE GL_FOG_COORDINATE_ARRAY_TYPE
+#define GL_FOG_COORD_ARRAY_STRIDE GL_FOG_COORDINATE_ARRAY_STRIDE
+#define GL_FOG_COORD_ARRAY_POINTER GL_FOG_COORDINATE_ARRAY_POINTER
+#define GL_FOG_COORD_ARRAY GL_FOG_COORDINATE_ARRAY
+#define GL_FOG_COORD_ARRAY_BUFFER_BINDING GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING
+#define GL_SRC0_RGB GL_SOURCE0_RGB
+#define GL_SRC1_RGB GL_SOURCE1_RGB
+#define GL_SRC2_RGB GL_SOURCE2_RGB
+#define GL_SRC0_ALPHA GL_SOURCE0_ALPHA
+#define GL_SRC1_ALPHA GL_SOURCE1_ALPHA
+#define GL_SRC2_ALPHA GL_SOURCE2_ALPHA
+#endif
+
+#ifndef GL_VERSION_2_0
+#define GL_BLEND_EQUATION_RGB GL_BLEND_EQUATION
+#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622
+#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623
+#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624
+#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625
+#define GL_CURRENT_VERTEX_ATTRIB 0x8626
+#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642
+#define GL_VERTEX_PROGRAM_TWO_SIDE 0x8643
+#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645
+#define GL_STENCIL_BACK_FUNC 0x8800
+#define GL_STENCIL_BACK_FAIL 0x8801
+#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802
+#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803
+#define GL_MAX_DRAW_BUFFERS 0x8824
+#define GL_DRAW_BUFFER0 0x8825
+#define GL_DRAW_BUFFER1 0x8826
+#define GL_DRAW_BUFFER2 0x8827
+#define GL_DRAW_BUFFER3 0x8828
+#define GL_DRAW_BUFFER4 0x8829
+#define GL_DRAW_BUFFER5 0x882A
+#define GL_DRAW_BUFFER6 0x882B
+#define GL_DRAW_BUFFER7 0x882C
+#define GL_DRAW_BUFFER8 0x882D
+#define GL_DRAW_BUFFER9 0x882E
+#define GL_DRAW_BUFFER10 0x882F
+#define GL_DRAW_BUFFER11 0x8830
+#define GL_DRAW_BUFFER12 0x8831
+#define GL_DRAW_BUFFER13 0x8832
+#define GL_DRAW_BUFFER14 0x8833
+#define GL_DRAW_BUFFER15 0x8834
+#define GL_BLEND_EQUATION_ALPHA 0x883D
+#define GL_POINT_SPRITE 0x8861
+#define GL_COORD_REPLACE 0x8862
+#define GL_MAX_VERTEX_ATTRIBS 0x8869
+#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A
+#define GL_MAX_TEXTURE_COORDS 0x8871
+#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872
+#define GL_FRAGMENT_SHADER 0x8B30
+#define GL_VERTEX_SHADER 0x8B31
+#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49
+#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A
+#define GL_MAX_VARYING_FLOATS 0x8B4B
+#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C
+#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D
+#define GL_SHADER_TYPE 0x8B4F
+#define GL_FLOAT_VEC2 0x8B50
+#define GL_FLOAT_VEC3 0x8B51
+#define GL_FLOAT_VEC4 0x8B52
+#define GL_INT_VEC2 0x8B53
+#define GL_INT_VEC3 0x8B54
+#define GL_INT_VEC4 0x8B55
+#define GL_BOOL 0x8B56
+#define GL_BOOL_VEC2 0x8B57
+#define GL_BOOL_VEC3 0x8B58
+#define GL_BOOL_VEC4 0x8B59
+#define GL_FLOAT_MAT2 0x8B5A
+#define GL_FLOAT_MAT3 0x8B5B
+#define GL_FLOAT_MAT4 0x8B5C
+#define GL_SAMPLER_1D 0x8B5D
+#define GL_SAMPLER_2D 0x8B5E
+#define GL_SAMPLER_3D 0x8B5F
+#define GL_SAMPLER_CUBE 0x8B60
+#define GL_SAMPLER_1D_SHADOW 0x8B61
+#define GL_SAMPLER_2D_SHADOW 0x8B62
+#define GL_DELETE_STATUS 0x8B80
+#define GL_COMPILE_STATUS 0x8B81
+#define GL_LINK_STATUS 0x8B82
+#define GL_VALIDATE_STATUS 0x8B83
+#define GL_INFO_LOG_LENGTH 0x8B84
+#define GL_ATTACHED_SHADERS 0x8B85
+#define GL_ACTIVE_UNIFORMS 0x8B86
+#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87
+#define GL_SHADER_SOURCE_LENGTH 0x8B88
+#define GL_ACTIVE_ATTRIBUTES 0x8B89
+#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A
+#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B
+#define GL_SHADING_LANGUAGE_VERSION 0x8B8C
+#define GL_CURRENT_PROGRAM 0x8B8D
+#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0
+#define GL_LOWER_LEFT 0x8CA1
+#define GL_UPPER_LEFT 0x8CA2
+#define GL_STENCIL_BACK_REF 0x8CA3
+#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4
+#define GL_STENCIL_BACK_WRITEMASK 0x8CA5
+#endif
+
+#ifndef GL_ARB_multitexture
+#define GL_TEXTURE0_ARB 0x84C0
+#define GL_TEXTURE1_ARB 0x84C1
+#define GL_TEXTURE2_ARB 0x84C2
+#define GL_TEXTURE3_ARB 0x84C3
+#define GL_TEXTURE4_ARB 0x84C4
+#define GL_TEXTURE5_ARB 0x84C5
+#define GL_TEXTURE6_ARB 0x84C6
+#define GL_TEXTURE7_ARB 0x84C7
+#define GL_TEXTURE8_ARB 0x84C8
+#define GL_TEXTURE9_ARB 0x84C9
+#define GL_TEXTURE10_ARB 0x84CA
+#define GL_TEXTURE11_ARB 0x84CB
+#define GL_TEXTURE12_ARB 0x84CC
+#define GL_TEXTURE13_ARB 0x84CD
+#define GL_TEXTURE14_ARB 0x84CE
+#define GL_TEXTURE15_ARB 0x84CF
+#define GL_TEXTURE16_ARB 0x84D0
+#define GL_TEXTURE17_ARB 0x84D1
+#define GL_TEXTURE18_ARB 0x84D2
+#define GL_TEXTURE19_ARB 0x84D3
+#define GL_TEXTURE20_ARB 0x84D4
+#define GL_TEXTURE21_ARB 0x84D5
+#define GL_TEXTURE22_ARB 0x84D6
+#define GL_TEXTURE23_ARB 0x84D7
+#define GL_TEXTURE24_ARB 0x84D8
+#define GL_TEXTURE25_ARB 0x84D9
+#define GL_TEXTURE26_ARB 0x84DA
+#define GL_TEXTURE27_ARB 0x84DB
+#define GL_TEXTURE28_ARB 0x84DC
+#define GL_TEXTURE29_ARB 0x84DD
+#define GL_TEXTURE30_ARB 0x84DE
+#define GL_TEXTURE31_ARB 0x84DF
+#define GL_ACTIVE_TEXTURE_ARB 0x84E0
+#define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1
+#define GL_MAX_TEXTURE_UNITS_ARB 0x84E2
+#endif
+
+#ifndef GL_ARB_transpose_matrix
+#define GL_TRANSPOSE_MODELVIEW_MATRIX_ARB 0x84E3
+#define GL_TRANSPOSE_PROJECTION_MATRIX_ARB 0x84E4
+#define GL_TRANSPOSE_TEXTURE_MATRIX_ARB 0x84E5
+#define GL_TRANSPOSE_COLOR_MATRIX_ARB 0x84E6
+#endif
+
+#ifndef GL_ARB_multisample
+#define GL_MULTISAMPLE_ARB 0x809D
+#define GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E
+#define GL_SAMPLE_ALPHA_TO_ONE_ARB 0x809F
+#define GL_SAMPLE_COVERAGE_ARB 0x80A0
+#define GL_SAMPLE_BUFFERS_ARB 0x80A8
+#define GL_SAMPLES_ARB 0x80A9
+#define GL_SAMPLE_COVERAGE_VALUE_ARB 0x80AA
+#define GL_SAMPLE_COVERAGE_INVERT_ARB 0x80AB
+#define GL_MULTISAMPLE_BIT_ARB 0x20000000
+#endif
+
+#ifndef GL_ARB_texture_env_add
+#endif
+
+#ifndef GL_ARB_texture_cube_map
+#define GL_NORMAL_MAP_ARB 0x8511
+#define GL_REFLECTION_MAP_ARB 0x8512
+#define GL_TEXTURE_CUBE_MAP_ARB 0x8513
+#define GL_TEXTURE_BINDING_CUBE_MAP_ARB 0x8514
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x8515
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x8516
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x8517
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x8518
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x8519
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x851A
+#define GL_PROXY_TEXTURE_CUBE_MAP_ARB 0x851B
+#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB 0x851C
+#endif
+
+#ifndef GL_ARB_texture_compression
+#define GL_COMPRESSED_ALPHA_ARB 0x84E9
+#define GL_COMPRESSED_LUMINANCE_ARB 0x84EA
+#define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB
+#define GL_COMPRESSED_INTENSITY_ARB 0x84EC
+#define GL_COMPRESSED_RGB_ARB 0x84ED
+#define GL_COMPRESSED_RGBA_ARB 0x84EE
+#define GL_TEXTURE_COMPRESSION_HINT_ARB 0x84EF
+#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB 0x86A0
+#define GL_TEXTURE_COMPRESSED_ARB 0x86A1
+#define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2
+#define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3
+#endif
+
+#ifndef GL_ARB_texture_border_clamp
+#define GL_CLAMP_TO_BORDER_ARB 0x812D
+#endif
+
+#ifndef GL_ARB_point_parameters
+#define GL_POINT_SIZE_MIN_ARB 0x8126
+#define GL_POINT_SIZE_MAX_ARB 0x8127
+#define GL_POINT_FADE_THRESHOLD_SIZE_ARB 0x8128
+#define GL_POINT_DISTANCE_ATTENUATION_ARB 0x8129
+#endif
+
+#ifndef GL_ARB_vertex_blend
+#define GL_MAX_VERTEX_UNITS_ARB 0x86A4
+#define GL_ACTIVE_VERTEX_UNITS_ARB 0x86A5
+#define GL_WEIGHT_SUM_UNITY_ARB 0x86A6
+#define GL_VERTEX_BLEND_ARB 0x86A7
+#define GL_CURRENT_WEIGHT_ARB 0x86A8
+#define GL_WEIGHT_ARRAY_TYPE_ARB 0x86A9
+#define GL_WEIGHT_ARRAY_STRIDE_ARB 0x86AA
+#define GL_WEIGHT_ARRAY_SIZE_ARB 0x86AB
+#define GL_WEIGHT_ARRAY_POINTER_ARB 0x86AC
+#define GL_WEIGHT_ARRAY_ARB 0x86AD
+#define GL_MODELVIEW0_ARB 0x1700
+#define GL_MODELVIEW1_ARB 0x850A
+#define GL_MODELVIEW2_ARB 0x8722
+#define GL_MODELVIEW3_ARB 0x8723
+#define GL_MODELVIEW4_ARB 0x8724
+#define GL_MODELVIEW5_ARB 0x8725
+#define GL_MODELVIEW6_ARB 0x8726
+#define GL_MODELVIEW7_ARB 0x8727
+#define GL_MODELVIEW8_ARB 0x8728
+#define GL_MODELVIEW9_ARB 0x8729
+#define GL_MODELVIEW10_ARB 0x872A
+#define GL_MODELVIEW11_ARB 0x872B
+#define GL_MODELVIEW12_ARB 0x872C
+#define GL_MODELVIEW13_ARB 0x872D
+#define GL_MODELVIEW14_ARB 0x872E
+#define GL_MODELVIEW15_ARB 0x872F
+#define GL_MODELVIEW16_ARB 0x8730
+#define GL_MODELVIEW17_ARB 0x8731
+#define GL_MODELVIEW18_ARB 0x8732
+#define GL_MODELVIEW19_ARB 0x8733
+#define GL_MODELVIEW20_ARB 0x8734
+#define GL_MODELVIEW21_ARB 0x8735
+#define GL_MODELVIEW22_ARB 0x8736
+#define GL_MODELVIEW23_ARB 0x8737
+#define GL_MODELVIEW24_ARB 0x8738
+#define GL_MODELVIEW25_ARB 0x8739
+#define GL_MODELVIEW26_ARB 0x873A
+#define GL_MODELVIEW27_ARB 0x873B
+#define GL_MODELVIEW28_ARB 0x873C
+#define GL_MODELVIEW29_ARB 0x873D
+#define GL_MODELVIEW30_ARB 0x873E
+#define GL_MODELVIEW31_ARB 0x873F
+#endif
+
+#ifndef GL_ARB_matrix_palette
+#define GL_MATRIX_PALETTE_ARB 0x8840
+#define GL_MAX_MATRIX_PALETTE_STACK_DEPTH_ARB 0x8841
+#define GL_MAX_PALETTE_MATRICES_ARB 0x8842
+#define GL_CURRENT_PALETTE_MATRIX_ARB 0x8843
+#define GL_MATRIX_INDEX_ARRAY_ARB 0x8844
+#define GL_CURRENT_MATRIX_INDEX_ARB 0x8845
+#define GL_MATRIX_INDEX_ARRAY_SIZE_ARB 0x8846
+#define GL_MATRIX_INDEX_ARRAY_TYPE_ARB 0x8847
+#define GL_MATRIX_INDEX_ARRAY_STRIDE_ARB 0x8848
+#define GL_MATRIX_INDEX_ARRAY_POINTER_ARB 0x8849
+#endif
+
+#ifndef GL_ARB_texture_env_combine
+#define GL_COMBINE_ARB 0x8570
+#define GL_COMBINE_RGB_ARB 0x8571
+#define GL_COMBINE_ALPHA_ARB 0x8572
+#define GL_SOURCE0_RGB_ARB 0x8580
+#define GL_SOURCE1_RGB_ARB 0x8581
+#define GL_SOURCE2_RGB_ARB 0x8582
+#define GL_SOURCE0_ALPHA_ARB 0x8588
+#define GL_SOURCE1_ALPHA_ARB 0x8589
+#define GL_SOURCE2_ALPHA_ARB 0x858A
+#define GL_OPERAND0_RGB_ARB 0x8590
+#define GL_OPERAND1_RGB_ARB 0x8591
+#define GL_OPERAND2_RGB_ARB 0x8592
+#define GL_OPERAND0_ALPHA_ARB 0x8598
+#define GL_OPERAND1_ALPHA_ARB 0x8599
+#define GL_OPERAND2_ALPHA_ARB 0x859A
+#define GL_RGB_SCALE_ARB 0x8573
+#define GL_ADD_SIGNED_ARB 0x8574
+#define GL_INTERPOLATE_ARB 0x8575
+#define GL_SUBTRACT_ARB 0x84E7
+#define GL_CONSTANT_ARB 0x8576
+#define GL_PRIMARY_COLOR_ARB 0x8577
+#define GL_PREVIOUS_ARB 0x8578
+#endif
+
+#ifndef GL_ARB_texture_env_crossbar
+#endif
+
+#ifndef GL_ARB_texture_env_dot3
+#define GL_DOT3_RGB_ARB 0x86AE
+#define GL_DOT3_RGBA_ARB 0x86AF
+#endif
+
+#ifndef GL_ARB_texture_mirrored_repeat
+#define GL_MIRRORED_REPEAT_ARB 0x8370
+#endif
+
+#ifndef GL_ARB_depth_texture
+#define GL_DEPTH_COMPONENT16_ARB 0x81A5
+#define GL_DEPTH_COMPONENT24_ARB 0x81A6
+#define GL_DEPTH_COMPONENT32_ARB 0x81A7
+#define GL_TEXTURE_DEPTH_SIZE_ARB 0x884A
+#define GL_DEPTH_TEXTURE_MODE_ARB 0x884B
+#endif
+
+#ifndef GL_ARB_shadow
+#define GL_TEXTURE_COMPARE_MODE_ARB 0x884C
+#define GL_TEXTURE_COMPARE_FUNC_ARB 0x884D
+#define GL_COMPARE_R_TO_TEXTURE_ARB 0x884E
+#endif
+
+#ifndef GL_ARB_shadow_ambient
+#define GL_TEXTURE_COMPARE_FAIL_VALUE_ARB 0x80BF
+#endif
+
+#ifndef GL_ARB_window_pos
+#endif
+
+#ifndef GL_ARB_vertex_program
+#define GL_COLOR_SUM_ARB 0x8458
+#define GL_VERTEX_PROGRAM_ARB 0x8620
+#define GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB 0x8622
+#define GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB 0x8623
+#define GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB 0x8624
+#define GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB 0x8625
+#define GL_CURRENT_VERTEX_ATTRIB_ARB 0x8626
+#define GL_PROGRAM_LENGTH_ARB 0x8627
+#define GL_PROGRAM_STRING_ARB 0x8628
+#define GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB 0x862E
+#define GL_MAX_PROGRAM_MATRICES_ARB 0x862F
+#define GL_CURRENT_MATRIX_STACK_DEPTH_ARB 0x8640
+#define GL_CURRENT_MATRIX_ARB 0x8641
+#define GL_VERTEX_PROGRAM_POINT_SIZE_ARB 0x8642
+#define GL_VERTEX_PROGRAM_TWO_SIDE_ARB 0x8643
+#define GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB 0x8645
+#define GL_PROGRAM_ERROR_POSITION_ARB 0x864B
+#define GL_PROGRAM_BINDING_ARB 0x8677
+#define GL_MAX_VERTEX_ATTRIBS_ARB 0x8869
+#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB 0x886A
+#define GL_PROGRAM_ERROR_STRING_ARB 0x8874
+#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875
+#define GL_PROGRAM_FORMAT_ARB 0x8876
+#define GL_PROGRAM_INSTRUCTIONS_ARB 0x88A0
+#define GL_MAX_PROGRAM_INSTRUCTIONS_ARB 0x88A1
+#define GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A2
+#define GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A3
+#define GL_PROGRAM_TEMPORARIES_ARB 0x88A4
+#define GL_MAX_PROGRAM_TEMPORARIES_ARB 0x88A5
+#define GL_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A6
+#define GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A7
+#define GL_PROGRAM_PARAMETERS_ARB 0x88A8
+#define GL_MAX_PROGRAM_PARAMETERS_ARB 0x88A9
+#define GL_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AA
+#define GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AB
+#define GL_PROGRAM_ATTRIBS_ARB 0x88AC
+#define GL_MAX_PROGRAM_ATTRIBS_ARB 0x88AD
+#define GL_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AE
+#define GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AF
+#define GL_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B0
+#define GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B1
+#define GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B2
+#define GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B3
+#define GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB 0x88B4
+#define GL_MAX_PROGRAM_ENV_PARAMETERS_ARB 0x88B5
+#define GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB 0x88B6
+#define GL_TRANSPOSE_CURRENT_MATRIX_ARB 0x88B7
+#define GL_MATRIX0_ARB 0x88C0
+#define GL_MATRIX1_ARB 0x88C1
+#define GL_MATRIX2_ARB 0x88C2
+#define GL_MATRIX3_ARB 0x88C3
+#define GL_MATRIX4_ARB 0x88C4
+#define GL_MATRIX5_ARB 0x88C5
+#define GL_MATRIX6_ARB 0x88C6
+#define GL_MATRIX7_ARB 0x88C7
+#define GL_MATRIX8_ARB 0x88C8
+#define GL_MATRIX9_ARB 0x88C9
+#define GL_MATRIX10_ARB 0x88CA
+#define GL_MATRIX11_ARB 0x88CB
+#define GL_MATRIX12_ARB 0x88CC
+#define GL_MATRIX13_ARB 0x88CD
+#define GL_MATRIX14_ARB 0x88CE
+#define GL_MATRIX15_ARB 0x88CF
+#define GL_MATRIX16_ARB 0x88D0
+#define GL_MATRIX17_ARB 0x88D1
+#define GL_MATRIX18_ARB 0x88D2
+#define GL_MATRIX19_ARB 0x88D3
+#define GL_MATRIX20_ARB 0x88D4
+#define GL_MATRIX21_ARB 0x88D5
+#define GL_MATRIX22_ARB 0x88D6
+#define GL_MATRIX23_ARB 0x88D7
+#define GL_MATRIX24_ARB 0x88D8
+#define GL_MATRIX25_ARB 0x88D9
+#define GL_MATRIX26_ARB 0x88DA
+#define GL_MATRIX27_ARB 0x88DB
+#define GL_MATRIX28_ARB 0x88DC
+#define GL_MATRIX29_ARB 0x88DD
+#define GL_MATRIX30_ARB 0x88DE
+#define GL_MATRIX31_ARB 0x88DF
+#endif
+
+#ifndef GL_ARB_fragment_program
+#define GL_FRAGMENT_PROGRAM_ARB 0x8804
+#define GL_PROGRAM_ALU_INSTRUCTIONS_ARB 0x8805
+#define GL_PROGRAM_TEX_INSTRUCTIONS_ARB 0x8806
+#define GL_PROGRAM_TEX_INDIRECTIONS_ARB 0x8807
+#define GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x8808
+#define GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x8809
+#define GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x880A
+#define GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB 0x880B
+#define GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB 0x880C
+#define GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB 0x880D
+#define GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x880E
+#define GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x880F
+#define GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x8810
+#define GL_MAX_TEXTURE_COORDS_ARB 0x8871
+#define GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872
+#endif
+
+#ifndef GL_ARB_vertex_buffer_object
+#define GL_BUFFER_SIZE_ARB 0x8764
+#define GL_BUFFER_USAGE_ARB 0x8765
+#define GL_ARRAY_BUFFER_ARB 0x8892
+#define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893
+#define GL_ARRAY_BUFFER_BINDING_ARB 0x8894
+#define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895
+#define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896
+#define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897
+#define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898
+#define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899
+#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A
+#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B
+#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C
+#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D
+#define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E
+#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F
+#define GL_READ_ONLY_ARB 0x88B8
+#define GL_WRITE_ONLY_ARB 0x88B9
+#define GL_READ_WRITE_ARB 0x88BA
+#define GL_BUFFER_ACCESS_ARB 0x88BB
+#define GL_BUFFER_MAPPED_ARB 0x88BC
+#define GL_BUFFER_MAP_POINTER_ARB 0x88BD
+#define GL_STREAM_DRAW_ARB 0x88E0
+#define GL_STREAM_READ_ARB 0x88E1
+#define GL_STREAM_COPY_ARB 0x88E2
+#define GL_STATIC_DRAW_ARB 0x88E4
+#define GL_STATIC_READ_ARB 0x88E5
+#define GL_STATIC_COPY_ARB 0x88E6
+#define GL_DYNAMIC_DRAW_ARB 0x88E8
+#define GL_DYNAMIC_READ_ARB 0x88E9
+#define GL_DYNAMIC_COPY_ARB 0x88EA
+#endif
+
+#ifndef GL_ARB_map_buffer_range
+#define GL_MAP_READ_BIT 0x0001
+#define GL_MAP_WRITE_BIT 0x0002
+#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004
+#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008
+#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010
+#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020
+#endif
+
+#ifndef GL_ARB_occlusion_query
+#define GL_QUERY_COUNTER_BITS_ARB 0x8864
+#define GL_CURRENT_QUERY_ARB 0x8865
+#define GL_QUERY_RESULT_ARB 0x8866
+#define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867
+#define GL_SAMPLES_PASSED_ARB 0x8914
+#endif
+
+#ifndef GL_EXT_timer_query
+#define GL_TIME_ELAPSED_EXT 0x88BF
+#endif
+
+#ifndef GL_ARB_sync
+#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111
+#define GL_OBJECT_TYPE 0x9112
+#define GL_SYNC_CONDITION 0x9113
+#define GL_SYNC_STATUS 0x9114
+#define GL_SYNC_FLAGS 0x9115
+#define GL_SYNC_FENCE 0x9116
+#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
+#define GL_UNSIGNALED 0x9118
+#define GL_SIGNALED 0x9119
+#define GL_ALREADY_SIGNALED 0x911A
+#define GL_TIMEOUT_EXPIRED 0x911B
+#define GL_CONDITION_SATISFIED 0x911C
+#define GL_WAIT_FAILED 0x911D
+#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001
+#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull
+#endif
+
+#ifndef GL_ARB_shader_objects
+#define GL_PROGRAM_OBJECT_ARB 0x8B40
+#define GL_SHADER_OBJECT_ARB 0x8B48
+#define GL_OBJECT_TYPE_ARB 0x8B4E
+#define GL_OBJECT_SUBTYPE_ARB 0x8B4F
+#define GL_FLOAT_VEC2_ARB 0x8B50
+#define GL_FLOAT_VEC3_ARB 0x8B51
+#define GL_FLOAT_VEC4_ARB 0x8B52
+#define GL_INT_VEC2_ARB 0x8B53
+#define GL_INT_VEC3_ARB 0x8B54
+#define GL_INT_VEC4_ARB 0x8B55
+#define GL_BOOL_ARB 0x8B56
+#define GL_BOOL_VEC2_ARB 0x8B57
+#define GL_BOOL_VEC3_ARB 0x8B58
+#define GL_BOOL_VEC4_ARB 0x8B59
+#define GL_FLOAT_MAT2_ARB 0x8B5A
+#define GL_FLOAT_MAT3_ARB 0x8B5B
+#define GL_FLOAT_MAT4_ARB 0x8B5C
+#define GL_SAMPLER_1D_ARB 0x8B5D
+#define GL_SAMPLER_2D_ARB 0x8B5E
+#define GL_SAMPLER_3D_ARB 0x8B5F
+#define GL_SAMPLER_CUBE_ARB 0x8B60
+#define GL_SAMPLER_1D_SHADOW_ARB 0x8B61
+#define GL_SAMPLER_2D_SHADOW_ARB 0x8B62
+#define GL_SAMPLER_2D_RECT_ARB 0x8B63
+#define GL_SAMPLER_2D_RECT_SHADOW_ARB 0x8B64
+#define GL_OBJECT_DELETE_STATUS_ARB 0x8B80
+#define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81
+#define GL_OBJECT_LINK_STATUS_ARB 0x8B82
+#define GL_OBJECT_VALIDATE_STATUS_ARB 0x8B83
+#define GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84
+#define GL_OBJECT_ATTACHED_OBJECTS_ARB 0x8B85
+#define GL_OBJECT_ACTIVE_UNIFORMS_ARB 0x8B86
+#define GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB 0x8B87
+#define GL_OBJECT_SHADER_SOURCE_LENGTH_ARB 0x8B88
+#endif
+
+#ifndef GL_ARB_vertex_shader
+#define GL_VERTEX_SHADER_ARB 0x8B31
+#define GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB 0x8B4A
+#define GL_MAX_VARYING_FLOATS_ARB 0x8B4B
+#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB 0x8B4C
+#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB 0x8B4D
+#define GL_OBJECT_ACTIVE_ATTRIBUTES_ARB 0x8B89
+#define GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB 0x8B8A
+#endif
+
+#ifndef GL_ARB_fragment_shader
+#define GL_FRAGMENT_SHADER_ARB 0x8B30
+#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB 0x8B49
+#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB 0x8B8B
+#endif
+
+#ifndef GL_ARB_shading_language_100
+#define GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C
+#endif
+
+#ifndef GL_ARB_texture_non_power_of_two
+#endif
+
+#ifndef GL_ARB_point_sprite
+#define GL_POINT_SPRITE_ARB 0x8861
+#define GL_COORD_REPLACE_ARB 0x8862
+#endif
+
+#ifndef GL_ARB_fragment_program_shadow
+#endif
+
+#ifndef GL_ARB_draw_buffers
+#define GL_MAX_DRAW_BUFFERS_ARB 0x8824
+#define GL_DRAW_BUFFER0_ARB 0x8825
+#define GL_DRAW_BUFFER1_ARB 0x8826
+#define GL_DRAW_BUFFER2_ARB 0x8827
+#define GL_DRAW_BUFFER3_ARB 0x8828
+#define GL_DRAW_BUFFER4_ARB 0x8829
+#define GL_DRAW_BUFFER5_ARB 0x882A
+#define GL_DRAW_BUFFER6_ARB 0x882B
+#define GL_DRAW_BUFFER7_ARB 0x882C
+#define GL_DRAW_BUFFER8_ARB 0x882D
+#define GL_DRAW_BUFFER9_ARB 0x882E
+#define GL_DRAW_BUFFER10_ARB 0x882F
+#define GL_DRAW_BUFFER11_ARB 0x8830
+#define GL_DRAW_BUFFER12_ARB 0x8831
+#define GL_DRAW_BUFFER13_ARB 0x8832
+#define GL_DRAW_BUFFER14_ARB 0x8833
+#define GL_DRAW_BUFFER15_ARB 0x8834
+#endif
+
+#ifndef GL_ARB_texture_rectangle
+#define GL_TEXTURE_RECTANGLE_ARB 0x84F5
+#define GL_TEXTURE_BINDING_RECTANGLE_ARB 0x84F6
+#define GL_PROXY_TEXTURE_RECTANGLE_ARB 0x84F7
+#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8
+#endif
+
+#ifndef GL_ARB_color_buffer_float
+#define GL_RGBA_FLOAT_MODE_ARB 0x8820
+#define GL_CLAMP_VERTEX_COLOR_ARB 0x891A
+#define GL_CLAMP_FRAGMENT_COLOR_ARB 0x891B
+#define GL_CLAMP_READ_COLOR_ARB 0x891C
+#define GL_FIXED_ONLY_ARB 0x891D
+#endif
+
+#ifndef GL_ARB_half_float_pixel
+#define GL_HALF_FLOAT_ARB 0x140B
+#endif
+
+#ifndef GL_ARB_texture_float
+#define GL_TEXTURE_RED_TYPE_ARB 0x8C10
+#define GL_TEXTURE_GREEN_TYPE_ARB 0x8C11
+#define GL_TEXTURE_BLUE_TYPE_ARB 0x8C12
+#define GL_TEXTURE_ALPHA_TYPE_ARB 0x8C13
+#define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14
+#define GL_TEXTURE_INTENSITY_TYPE_ARB 0x8C15
+#define GL_TEXTURE_DEPTH_TYPE_ARB 0x8C16
+#define GL_UNSIGNED_NORMALIZED_ARB 0x8C17
+#define GL_RGBA32F_ARB 0x8814
+#define GL_RGB32F_ARB 0x8815
+#define GL_ALPHA32F_ARB 0x8816
+#define GL_INTENSITY32F_ARB 0x8817
+#define GL_LUMINANCE32F_ARB 0x8818
+#define GL_LUMINANCE_ALPHA32F_ARB 0x8819
+#define GL_RGBA16F_ARB 0x881A
+#define GL_RGB16F_ARB 0x881B
+#define GL_ALPHA16F_ARB 0x881C
+#define GL_INTENSITY16F_ARB 0x881D
+#define GL_LUMINANCE16F_ARB 0x881E
+#define GL_LUMINANCE_ALPHA16F_ARB 0x881F
+#endif
+
+#ifndef GL_ARB_pixel_buffer_object
+#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB
+#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC
+#define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED
+#define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF
+#endif
+
+#ifndef GL_EXT_abgr
+#define GL_ABGR_EXT 0x8000
+#endif
+
+#ifndef GL_EXT_blend_color
+#define GL_CONSTANT_COLOR_EXT 0x8001
+#define GL_ONE_MINUS_CONSTANT_COLOR_EXT 0x8002
+#define GL_CONSTANT_ALPHA_EXT 0x8003
+#define GL_ONE_MINUS_CONSTANT_ALPHA_EXT 0x8004
+#define GL_BLEND_COLOR_EXT 0x8005
+#endif
+
+#ifndef GL_EXT_polygon_offset
+#define GL_POLYGON_OFFSET_EXT 0x8037
+#define GL_POLYGON_OFFSET_FACTOR_EXT 0x8038
+#define GL_POLYGON_OFFSET_BIAS_EXT 0x8039
+#endif
+
+#ifndef GL_EXT_texture
+#define GL_ALPHA4_EXT 0x803B
+#define GL_ALPHA8_EXT 0x803C
+#define GL_ALPHA12_EXT 0x803D
+#define GL_ALPHA16_EXT 0x803E
+#define GL_LUMINANCE4_EXT 0x803F
+#define GL_LUMINANCE8_EXT 0x8040
+#define GL_LUMINANCE12_EXT 0x8041
+#define GL_LUMINANCE16_EXT 0x8042
+#define GL_LUMINANCE4_ALPHA4_EXT 0x8043
+#define GL_LUMINANCE6_ALPHA2_EXT 0x8044
+#define GL_LUMINANCE8_ALPHA8_EXT 0x8045
+#define GL_LUMINANCE12_ALPHA4_EXT 0x8046
+#define GL_LUMINANCE12_ALPHA12_EXT 0x8047
+#define GL_LUMINANCE16_ALPHA16_EXT 0x8048
+#define GL_INTENSITY_EXT 0x8049
+#define GL_INTENSITY4_EXT 0x804A
+#define GL_INTENSITY8_EXT 0x804B
+#define GL_INTENSITY12_EXT 0x804C
+#define GL_INTENSITY16_EXT 0x804D
+#define GL_RGB2_EXT 0x804E
+#define GL_RGB4_EXT 0x804F
+#define GL_RGB5_EXT 0x8050
+#define GL_RGB8_EXT 0x8051
+#define GL_RGB10_EXT 0x8052
+#define GL_RGB12_EXT 0x8053
+#define GL_RGB16_EXT 0x8054
+#define GL_RGBA2_EXT 0x8055
+#define GL_RGBA4_EXT 0x8056
+#define GL_RGB5_A1_EXT 0x8057
+#define GL_RGBA8_EXT 0x8058
+#define GL_RGB10_A2_EXT 0x8059
+#define GL_RGBA12_EXT 0x805A
+#define GL_RGBA16_EXT 0x805B
+#define GL_TEXTURE_RED_SIZE_EXT 0x805C
+#define GL_TEXTURE_GREEN_SIZE_EXT 0x805D
+#define GL_TEXTURE_BLUE_SIZE_EXT 0x805E
+#define GL_TEXTURE_ALPHA_SIZE_EXT 0x805F
+#define GL_TEXTURE_LUMINANCE_SIZE_EXT 0x8060
+#define GL_TEXTURE_INTENSITY_SIZE_EXT 0x8061
+#define GL_REPLACE_EXT 0x8062
+#define GL_PROXY_TEXTURE_1D_EXT 0x8063
+#define GL_PROXY_TEXTURE_2D_EXT 0x8064
+#define GL_TEXTURE_TOO_LARGE_EXT 0x8065
+#endif
+
+#ifndef GL_EXT_texture3D
+#define GL_PACK_SKIP_IMAGES_EXT 0x806B
+#define GL_PACK_IMAGE_HEIGHT_EXT 0x806C
+#define GL_UNPACK_SKIP_IMAGES_EXT 0x806D
+#define GL_UNPACK_IMAGE_HEIGHT_EXT 0x806E
+#define GL_TEXTURE_3D_EXT 0x806F
+#define GL_PROXY_TEXTURE_3D_EXT 0x8070
+#define GL_TEXTURE_DEPTH_EXT 0x8071
+#define GL_TEXTURE_WRAP_R_EXT 0x8072
+#define GL_MAX_3D_TEXTURE_SIZE_EXT 0x8073
+#endif
+
+#ifndef GL_SGIS_texture_filter4
+#define GL_FILTER4_SGIS 0x8146
+#define GL_TEXTURE_FILTER4_SIZE_SGIS 0x8147
+#endif
+
+#ifndef GL_EXT_subtexture
+#endif
+
+#ifndef GL_EXT_copy_texture
+#endif
+
+#ifndef GL_EXT_histogram
+#define GL_HISTOGRAM_EXT 0x8024
+#define GL_PROXY_HISTOGRAM_EXT 0x8025
+#define GL_HISTOGRAM_WIDTH_EXT 0x8026
+#define GL_HISTOGRAM_FORMAT_EXT 0x8027
+#define GL_HISTOGRAM_RED_SIZE_EXT 0x8028
+#define GL_HISTOGRAM_GREEN_SIZE_EXT 0x8029
+#define GL_HISTOGRAM_BLUE_SIZE_EXT 0x802A
+#define GL_HISTOGRAM_ALPHA_SIZE_EXT 0x802B
+#define GL_HISTOGRAM_LUMINANCE_SIZE_EXT 0x802C
+#define GL_HISTOGRAM_SINK_EXT 0x802D
+#define GL_MINMAX_EXT 0x802E
+#define GL_MINMAX_FORMAT_EXT 0x802F
+#define GL_MINMAX_SINK_EXT 0x8030
+#define GL_TABLE_TOO_LARGE_EXT 0x8031
+#endif
+
+#ifndef GL_EXT_convolution
+#define GL_CONVOLUTION_1D_EXT 0x8010
+#define GL_CONVOLUTION_2D_EXT 0x8011
+#define GL_SEPARABLE_2D_EXT 0x8012
+#define GL_CONVOLUTION_BORDER_MODE_EXT 0x8013
+#define GL_CONVOLUTION_FILTER_SCALE_EXT 0x8014
+#define GL_CONVOLUTION_FILTER_BIAS_EXT 0x8015
+#define GL_REDUCE_EXT 0x8016
+#define GL_CONVOLUTION_FORMAT_EXT 0x8017
+#define GL_CONVOLUTION_WIDTH_EXT 0x8018
+#define GL_CONVOLUTION_HEIGHT_EXT 0x8019
+#define GL_MAX_CONVOLUTION_WIDTH_EXT 0x801A
+#define GL_MAX_CONVOLUTION_HEIGHT_EXT 0x801B
+#define GL_POST_CONVOLUTION_RED_SCALE_EXT 0x801C
+#define GL_POST_CONVOLUTION_GREEN_SCALE_EXT 0x801D
+#define GL_POST_CONVOLUTION_BLUE_SCALE_EXT 0x801E
+#define GL_POST_CONVOLUTION_ALPHA_SCALE_EXT 0x801F
+#define GL_POST_CONVOLUTION_RED_BIAS_EXT 0x8020
+#define GL_POST_CONVOLUTION_GREEN_BIAS_EXT 0x8021
+#define GL_POST_CONVOLUTION_BLUE_BIAS_EXT 0x8022
+#define GL_POST_CONVOLUTION_ALPHA_BIAS_EXT 0x8023
+#endif
+
+#ifndef GL_SGI_color_matrix
+#define GL_COLOR_MATRIX_SGI 0x80B1
+#define GL_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B2
+#define GL_MAX_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B3
+#define GL_POST_COLOR_MATRIX_RED_SCALE_SGI 0x80B4
+#define GL_POST_COLOR_MATRIX_GREEN_SCALE_SGI 0x80B5
+#define GL_POST_COLOR_MATRIX_BLUE_SCALE_SGI 0x80B6
+#define GL_POST_COLOR_MATRIX_ALPHA_SCALE_SGI 0x80B7
+#define GL_POST_COLOR_MATRIX_RED_BIAS_SGI 0x80B8
+#define GL_POST_COLOR_MATRIX_GREEN_BIAS_SGI 0x80B9
+#define GL_POST_COLOR_MATRIX_BLUE_BIAS_SGI 0x80BA
+#define GL_POST_COLOR_MATRIX_ALPHA_BIAS_SGI 0x80BB
+#endif
+
+#ifndef GL_SGI_color_table
+#define GL_COLOR_TABLE_SGI 0x80D0
+#define GL_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D1
+#define GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D2
+#define GL_PROXY_COLOR_TABLE_SGI 0x80D3
+#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D4
+#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D5
+#define GL_COLOR_TABLE_SCALE_SGI 0x80D6
+#define GL_COLOR_TABLE_BIAS_SGI 0x80D7
+#define GL_COLOR_TABLE_FORMAT_SGI 0x80D8
+#define GL_COLOR_TABLE_WIDTH_SGI 0x80D9
+#define GL_COLOR_TABLE_RED_SIZE_SGI 0x80DA
+#define GL_COLOR_TABLE_GREEN_SIZE_SGI 0x80DB
+#define GL_COLOR_TABLE_BLUE_SIZE_SGI 0x80DC
+#define GL_COLOR_TABLE_ALPHA_SIZE_SGI 0x80DD
+#define GL_COLOR_TABLE_LUMINANCE_SIZE_SGI 0x80DE
+#define GL_COLOR_TABLE_INTENSITY_SIZE_SGI 0x80DF
+#endif
+
+#ifndef GL_SGIS_pixel_texture
+#define GL_PIXEL_TEXTURE_SGIS 0x8353
+#define GL_PIXEL_FRAGMENT_RGB_SOURCE_SGIS 0x8354
+#define GL_PIXEL_FRAGMENT_ALPHA_SOURCE_SGIS 0x8355
+#define GL_PIXEL_GROUP_COLOR_SGIS 0x8356
+#endif
+
+#ifndef GL_SGIX_pixel_texture
+#define GL_PIXEL_TEX_GEN_SGIX 0x8139
+#define GL_PIXEL_TEX_GEN_MODE_SGIX 0x832B
+#endif
+
+#ifndef GL_SGIS_texture4D
+#define GL_PACK_SKIP_VOLUMES_SGIS 0x8130
+#define GL_PACK_IMAGE_DEPTH_SGIS 0x8131
+#define GL_UNPACK_SKIP_VOLUMES_SGIS 0x8132
+#define GL_UNPACK_IMAGE_DEPTH_SGIS 0x8133
+#define GL_TEXTURE_4D_SGIS 0x8134
+#define GL_PROXY_TEXTURE_4D_SGIS 0x8135
+#define GL_TEXTURE_4DSIZE_SGIS 0x8136
+#define GL_TEXTURE_WRAP_Q_SGIS 0x8137
+#define GL_MAX_4D_TEXTURE_SIZE_SGIS 0x8138
+#define GL_TEXTURE_4D_BINDING_SGIS 0x814F
+#endif
+
+#ifndef GL_SGI_texture_color_table
+#define GL_TEXTURE_COLOR_TABLE_SGI 0x80BC
+#define GL_PROXY_TEXTURE_COLOR_TABLE_SGI 0x80BD
+#endif
+
+#ifndef GL_EXT_cmyka
+#define GL_CMYK_EXT 0x800C
+#define GL_CMYKA_EXT 0x800D
+#define GL_PACK_CMYK_HINT_EXT 0x800E
+#define GL_UNPACK_CMYK_HINT_EXT 0x800F
+#endif
+
+#ifndef GL_EXT_texture_object
+#define GL_TEXTURE_PRIORITY_EXT 0x8066
+#define GL_TEXTURE_RESIDENT_EXT 0x8067
+#define GL_TEXTURE_1D_BINDING_EXT 0x8068
+#define GL_TEXTURE_2D_BINDING_EXT 0x8069
+#define GL_TEXTURE_3D_BINDING_EXT 0x806A
+#endif
+
+#ifndef GL_SGIS_detail_texture
+#define GL_DETAIL_TEXTURE_2D_SGIS 0x8095
+#define GL_DETAIL_TEXTURE_2D_BINDING_SGIS 0x8096
+#define GL_LINEAR_DETAIL_SGIS 0x8097
+#define GL_LINEAR_DETAIL_ALPHA_SGIS 0x8098
+#define GL_LINEAR_DETAIL_COLOR_SGIS 0x8099
+#define GL_DETAIL_TEXTURE_LEVEL_SGIS 0x809A
+#define GL_DETAIL_TEXTURE_MODE_SGIS 0x809B
+#define GL_DETAIL_TEXTURE_FUNC_POINTS_SGIS 0x809C
+#endif
+
+#ifndef GL_SGIS_sharpen_texture
+#define GL_LINEAR_SHARPEN_SGIS 0x80AD
+#define GL_LINEAR_SHARPEN_ALPHA_SGIS 0x80AE
+#define GL_LINEAR_SHARPEN_COLOR_SGIS 0x80AF
+#define GL_SHARPEN_TEXTURE_FUNC_POINTS_SGIS 0x80B0
+#endif
+
+#ifndef GL_EXT_packed_pixels
+#define GL_UNSIGNED_BYTE_3_3_2_EXT 0x8032
+#define GL_UNSIGNED_SHORT_4_4_4_4_EXT 0x8033
+#define GL_UNSIGNED_SHORT_5_5_5_1_EXT 0x8034
+#define GL_UNSIGNED_INT_8_8_8_8_EXT 0x8035
+#define GL_UNSIGNED_INT_10_10_10_2_EXT 0x8036
+#endif
+
+#ifndef GL_SGIS_texture_lod
+#define GL_TEXTURE_MIN_LOD_SGIS 0x813A
+#define GL_TEXTURE_MAX_LOD_SGIS 0x813B
+#define GL_TEXTURE_BASE_LEVEL_SGIS 0x813C
+#define GL_TEXTURE_MAX_LEVEL_SGIS 0x813D
+#endif
+
+#ifndef GL_SGIS_multisample
+#define GL_MULTISAMPLE_SGIS 0x809D
+#define GL_SAMPLE_ALPHA_TO_MASK_SGIS 0x809E
+#define GL_SAMPLE_ALPHA_TO_ONE_SGIS 0x809F
+#define GL_SAMPLE_MASK_SGIS 0x80A0
+#define GL_1PASS_SGIS 0x80A1
+#define GL_2PASS_0_SGIS 0x80A2
+#define GL_2PASS_1_SGIS 0x80A3
+#define GL_4PASS_0_SGIS 0x80A4
+#define GL_4PASS_1_SGIS 0x80A5
+#define GL_4PASS_2_SGIS 0x80A6
+#define GL_4PASS_3_SGIS 0x80A7
+#define GL_SAMPLE_BUFFERS_SGIS 0x80A8
+#define GL_SAMPLES_SGIS 0x80A9
+#define GL_SAMPLE_MASK_VALUE_SGIS 0x80AA
+#define GL_SAMPLE_MASK_INVERT_SGIS 0x80AB
+#define GL_SAMPLE_PATTERN_SGIS 0x80AC
+#endif
+
+#ifndef GL_EXT_rescale_normal
+#define GL_RESCALE_NORMAL_EXT 0x803A
+#endif
+
+#ifndef GL_EXT_vertex_array
+#define GL_VERTEX_ARRAY_EXT 0x8074
+#define GL_NORMAL_ARRAY_EXT 0x8075
+#define GL_COLOR_ARRAY_EXT 0x8076
+#define GL_INDEX_ARRAY_EXT 0x8077
+#define GL_TEXTURE_COORD_ARRAY_EXT 0x8078
+#define GL_EDGE_FLAG_ARRAY_EXT 0x8079
+#define GL_VERTEX_ARRAY_SIZE_EXT 0x807A
+#define GL_VERTEX_ARRAY_TYPE_EXT 0x807B
+#define GL_VERTEX_ARRAY_STRIDE_EXT 0x807C
+#define GL_VERTEX_ARRAY_COUNT_EXT 0x807D
+#define GL_NORMAL_ARRAY_TYPE_EXT 0x807E
+#define GL_NORMAL_ARRAY_STRIDE_EXT 0x807F
+#define GL_NORMAL_ARRAY_COUNT_EXT 0x8080
+#define GL_COLOR_ARRAY_SIZE_EXT 0x8081
+#define GL_COLOR_ARRAY_TYPE_EXT 0x8082
+#define GL_COLOR_ARRAY_STRIDE_EXT 0x8083
+#define GL_COLOR_ARRAY_COUNT_EXT 0x8084
+#define GL_INDEX_ARRAY_TYPE_EXT 0x8085
+#define GL_INDEX_ARRAY_STRIDE_EXT 0x8086
+#define GL_INDEX_ARRAY_COUNT_EXT 0x8087
+#define GL_TEXTURE_COORD_ARRAY_SIZE_EXT 0x8088
+#define GL_TEXTURE_COORD_ARRAY_TYPE_EXT 0x8089
+#define GL_TEXTURE_COORD_ARRAY_STRIDE_EXT 0x808A
+#define GL_TEXTURE_COORD_ARRAY_COUNT_EXT 0x808B
+#define GL_EDGE_FLAG_ARRAY_STRIDE_EXT 0x808C
+#define GL_EDGE_FLAG_ARRAY_COUNT_EXT 0x808D
+#define GL_VERTEX_ARRAY_POINTER_EXT 0x808E
+#define GL_NORMAL_ARRAY_POINTER_EXT 0x808F
+#define GL_COLOR_ARRAY_POINTER_EXT 0x8090
+#define GL_INDEX_ARRAY_POINTER_EXT 0x8091
+#define GL_TEXTURE_COORD_ARRAY_POINTER_EXT 0x8092
+#define GL_EDGE_FLAG_ARRAY_POINTER_EXT 0x8093
+#endif
+
+#ifndef GL_EXT_misc_attribute
+#endif
+
+#ifndef GL_SGIS_generate_mipmap
+#define GL_GENERATE_MIPMAP_SGIS 0x8191
+#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192
+#endif
+
+#ifndef GL_SGIX_clipmap
+#define GL_LINEAR_CLIPMAP_LINEAR_SGIX 0x8170
+#define GL_TEXTURE_CLIPMAP_CENTER_SGIX 0x8171
+#define GL_TEXTURE_CLIPMAP_FRAME_SGIX 0x8172
+#define GL_TEXTURE_CLIPMAP_OFFSET_SGIX 0x8173
+#define GL_TEXTURE_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8174
+#define GL_TEXTURE_CLIPMAP_LOD_OFFSET_SGIX 0x8175
+#define GL_TEXTURE_CLIPMAP_DEPTH_SGIX 0x8176
+#define GL_MAX_CLIPMAP_DEPTH_SGIX 0x8177
+#define GL_MAX_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8178
+#define GL_NEAREST_CLIPMAP_NEAREST_SGIX 0x844D
+#define GL_NEAREST_CLIPMAP_LINEAR_SGIX 0x844E
+#define GL_LINEAR_CLIPMAP_NEAREST_SGIX 0x844F
+#endif
+
+#ifndef GL_SGIX_shadow
+#define GL_TEXTURE_COMPARE_SGIX 0x819A
+#define GL_TEXTURE_COMPARE_OPERATOR_SGIX 0x819B
+#define GL_TEXTURE_LEQUAL_R_SGIX 0x819C
+#define GL_TEXTURE_GEQUAL_R_SGIX 0x819D
+#endif
+
+#ifndef GL_SGIS_texture_edge_clamp
+#define GL_CLAMP_TO_EDGE_SGIS 0x812F
+#endif
+
+#ifndef GL_SGIS_texture_border_clamp
+#define GL_CLAMP_TO_BORDER_SGIS 0x812D
+#endif
+
+#ifndef GL_EXT_blend_minmax
+#define GL_FUNC_ADD_EXT 0x8006
+#define GL_MIN_EXT 0x8007
+#define GL_MAX_EXT 0x8008
+#define GL_BLEND_EQUATION_EXT 0x8009
+#endif
+
+#ifndef GL_EXT_blend_subtract
+#define GL_FUNC_SUBTRACT_EXT 0x800A
+#define GL_FUNC_REVERSE_SUBTRACT_EXT 0x800B
+#endif
+
+#ifndef GL_EXT_blend_logic_op
+#endif
+
+#ifndef GL_SGIX_interlace
+#define GL_INTERLACE_SGIX 0x8094
+#endif
+
+#ifndef GL_SGIX_pixel_tiles
+#define GL_PIXEL_TILE_BEST_ALIGNMENT_SGIX 0x813E
+#define GL_PIXEL_TILE_CACHE_INCREMENT_SGIX 0x813F
+#define GL_PIXEL_TILE_WIDTH_SGIX 0x8140
+#define GL_PIXEL_TILE_HEIGHT_SGIX 0x8141
+#define GL_PIXEL_TILE_GRID_WIDTH_SGIX 0x8142
+#define GL_PIXEL_TILE_GRID_HEIGHT_SGIX 0x8143
+#define GL_PIXEL_TILE_GRID_DEPTH_SGIX 0x8144
+#define GL_PIXEL_TILE_CACHE_SIZE_SGIX 0x8145
+#endif
+
+#ifndef GL_SGIS_texture_select
+#define GL_DUAL_ALPHA4_SGIS 0x8110
+#define GL_DUAL_ALPHA8_SGIS 0x8111
+#define GL_DUAL_ALPHA12_SGIS 0x8112
+#define GL_DUAL_ALPHA16_SGIS 0x8113
+#define GL_DUAL_LUMINANCE4_SGIS 0x8114
+#define GL_DUAL_LUMINANCE8_SGIS 0x8115
+#define GL_DUAL_LUMINANCE12_SGIS 0x8116
+#define GL_DUAL_LUMINANCE16_SGIS 0x8117
+#define GL_DUAL_INTENSITY4_SGIS 0x8118
+#define GL_DUAL_INTENSITY8_SGIS 0x8119
+#define GL_DUAL_INTENSITY12_SGIS 0x811A
+#define GL_DUAL_INTENSITY16_SGIS 0x811B
+#define GL_DUAL_LUMINANCE_ALPHA4_SGIS 0x811C
+#define GL_DUAL_LUMINANCE_ALPHA8_SGIS 0x811D
+#define GL_QUAD_ALPHA4_SGIS 0x811E
+#define GL_QUAD_ALPHA8_SGIS 0x811F
+#define GL_QUAD_LUMINANCE4_SGIS 0x8120
+#define GL_QUAD_LUMINANCE8_SGIS 0x8121
+#define GL_QUAD_INTENSITY4_SGIS 0x8122
+#define GL_QUAD_INTENSITY8_SGIS 0x8123
+#define GL_DUAL_TEXTURE_SELECT_SGIS 0x8124
+#define GL_QUAD_TEXTURE_SELECT_SGIS 0x8125
+#endif
+
+#ifndef GL_SGIX_sprite
+#define GL_SPRITE_SGIX 0x8148
+#define GL_SPRITE_MODE_SGIX 0x8149
+#define GL_SPRITE_AXIS_SGIX 0x814A
+#define GL_SPRITE_TRANSLATION_SGIX 0x814B
+#define GL_SPRITE_AXIAL_SGIX 0x814C
+#define GL_SPRITE_OBJECT_ALIGNED_SGIX 0x814D
+#define GL_SPRITE_EYE_ALIGNED_SGIX 0x814E
+#endif
+
+#ifndef GL_SGIX_texture_multi_buffer
+#define GL_TEXTURE_MULTI_BUFFER_HINT_SGIX 0x812E
+#endif
+
+#ifndef GL_EXT_point_parameters
+#define GL_POINT_SIZE_MIN_EXT 0x8126
+#define GL_POINT_SIZE_MAX_EXT 0x8127
+#define GL_POINT_FADE_THRESHOLD_SIZE_EXT 0x8128
+#define GL_DISTANCE_ATTENUATION_EXT 0x8129
+#endif
+
+#ifndef GL_SGIS_point_parameters
+#define GL_POINT_SIZE_MIN_SGIS 0x8126
+#define GL_POINT_SIZE_MAX_SGIS 0x8127
+#define GL_POINT_FADE_THRESHOLD_SIZE_SGIS 0x8128
+#define GL_DISTANCE_ATTENUATION_SGIS 0x8129
+#endif
+
+#ifndef GL_SGIX_instruments
+#define GL_INSTRUMENT_BUFFER_POINTER_SGIX 0x8180
+#define GL_INSTRUMENT_MEASUREMENTS_SGIX 0x8181
+#endif
+
+#ifndef GL_SGIX_texture_scale_bias
+#define GL_POST_TEXTURE_FILTER_BIAS_SGIX 0x8179
+#define GL_POST_TEXTURE_FILTER_SCALE_SGIX 0x817A
+#define GL_POST_TEXTURE_FILTER_BIAS_RANGE_SGIX 0x817B
+#define GL_POST_TEXTURE_FILTER_SCALE_RANGE_SGIX 0x817C
+#endif
+
+#ifndef GL_SGIX_framezoom
+#define GL_FRAMEZOOM_SGIX 0x818B
+#define GL_FRAMEZOOM_FACTOR_SGIX 0x818C
+#define GL_MAX_FRAMEZOOM_FACTOR_SGIX 0x818D
+#endif
+
+#ifndef GL_SGIX_tag_sample_buffer
+#endif
+
+#ifndef GL_FfdMaskSGIX
+#define GL_TEXTURE_DEFORMATION_BIT_SGIX 0x00000001
+#define GL_GEOMETRY_DEFORMATION_BIT_SGIX 0x00000002
+#endif
+
+#ifndef GL_SGIX_polynomial_ffd
+#define GL_GEOMETRY_DEFORMATION_SGIX 0x8194
+#define GL_TEXTURE_DEFORMATION_SGIX 0x8195
+#define GL_DEFORMATIONS_MASK_SGIX 0x8196
+#define GL_MAX_DEFORMATION_ORDER_SGIX 0x8197
+#endif
+
+#ifndef GL_SGIX_reference_plane
+#define GL_REFERENCE_PLANE_SGIX 0x817D
+#define GL_REFERENCE_PLANE_EQUATION_SGIX 0x817E
+#endif
+
+#ifndef GL_SGIX_flush_raster
+#endif
+
+#ifndef GL_SGIX_depth_texture
+#define GL_DEPTH_COMPONENT16_SGIX 0x81A5
+#define GL_DEPTH_COMPONENT24_SGIX 0x81A6
+#define GL_DEPTH_COMPONENT32_SGIX 0x81A7
+#endif
+
+#ifndef GL_SGIS_fog_function
+#define GL_FOG_FUNC_SGIS 0x812A
+#define GL_FOG_FUNC_POINTS_SGIS 0x812B
+#define GL_MAX_FOG_FUNC_POINTS_SGIS 0x812C
+#endif
+
+#ifndef GL_SGIX_fog_offset
+#define GL_FOG_OFFSET_SGIX 0x8198
+#define GL_FOG_OFFSET_VALUE_SGIX 0x8199
+#endif
+
+#ifndef GL_HP_image_transform
+#define GL_IMAGE_SCALE_X_HP 0x8155
+#define GL_IMAGE_SCALE_Y_HP 0x8156
+#define GL_IMAGE_TRANSLATE_X_HP 0x8157
+#define GL_IMAGE_TRANSLATE_Y_HP 0x8158
+#define GL_IMAGE_ROTATE_ANGLE_HP 0x8159
+#define GL_IMAGE_ROTATE_ORIGIN_X_HP 0x815A
+#define GL_IMAGE_ROTATE_ORIGIN_Y_HP 0x815B
+#define GL_IMAGE_MAG_FILTER_HP 0x815C
+#define GL_IMAGE_MIN_FILTER_HP 0x815D
+#define GL_IMAGE_CUBIC_WEIGHT_HP 0x815E
+#define GL_CUBIC_HP 0x815F
+#define GL_AVERAGE_HP 0x8160
+#define GL_IMAGE_TRANSFORM_2D_HP 0x8161
+#define GL_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8162
+#define GL_PROXY_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8163
+#endif
+
+#ifndef GL_HP_convolution_border_modes
+#define GL_IGNORE_BORDER_HP 0x8150
+#define GL_CONSTANT_BORDER_HP 0x8151
+#define GL_REPLICATE_BORDER_HP 0x8153
+#define GL_CONVOLUTION_BORDER_COLOR_HP 0x8154
+#endif
+
+#ifndef GL_INGR_palette_buffer
+#endif
+
+#ifndef GL_SGIX_texture_add_env
+#define GL_TEXTURE_ENV_BIAS_SGIX 0x80BE
+#endif
+
+#ifndef GL_EXT_color_subtable
+#endif
+
+#ifndef GL_PGI_vertex_hints
+#define GL_VERTEX_DATA_HINT_PGI 0x1A22A
+#define GL_VERTEX_CONSISTENT_HINT_PGI 0x1A22B
+#define GL_MATERIAL_SIDE_HINT_PGI 0x1A22C
+#define GL_MAX_VERTEX_HINT_PGI 0x1A22D
+#define GL_COLOR3_BIT_PGI 0x00010000
+#define GL_COLOR4_BIT_PGI 0x00020000
+#define GL_EDGEFLAG_BIT_PGI 0x00040000
+#define GL_INDEX_BIT_PGI 0x00080000
+#define GL_MAT_AMBIENT_BIT_PGI 0x00100000
+#define GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI 0x00200000
+#define GL_MAT_DIFFUSE_BIT_PGI 0x00400000
+#define GL_MAT_EMISSION_BIT_PGI 0x00800000
+#define GL_MAT_COLOR_INDEXES_BIT_PGI 0x01000000
+#define GL_MAT_SHININESS_BIT_PGI 0x02000000
+#define GL_MAT_SPECULAR_BIT_PGI 0x04000000
+#define GL_NORMAL_BIT_PGI 0x08000000
+#define GL_TEXCOORD1_BIT_PGI 0x10000000
+#define GL_TEXCOORD2_BIT_PGI 0x20000000
+#define GL_TEXCOORD3_BIT_PGI 0x40000000
+#define GL_TEXCOORD4_BIT_PGI 0x80000000
+#define GL_VERTEX23_BIT_PGI 0x00000004
+#define GL_VERTEX4_BIT_PGI 0x00000008
+#endif
+
+#ifndef GL_PGI_misc_hints
+#define GL_PREFER_DOUBLEBUFFER_HINT_PGI 0x1A1F8
+#define GL_CONSERVE_MEMORY_HINT_PGI 0x1A1FD
+#define GL_RECLAIM_MEMORY_HINT_PGI 0x1A1FE
+#define GL_NATIVE_GRAPHICS_HANDLE_PGI 0x1A202
+#define GL_NATIVE_GRAPHICS_BEGIN_HINT_PGI 0x1A203
+#define GL_NATIVE_GRAPHICS_END_HINT_PGI 0x1A204
+#define GL_ALWAYS_FAST_HINT_PGI 0x1A20C
+#define GL_ALWAYS_SOFT_HINT_PGI 0x1A20D
+#define GL_ALLOW_DRAW_OBJ_HINT_PGI 0x1A20E
+#define GL_ALLOW_DRAW_WIN_HINT_PGI 0x1A20F
+#define GL_ALLOW_DRAW_FRG_HINT_PGI 0x1A210
+#define GL_ALLOW_DRAW_MEM_HINT_PGI 0x1A211
+#define GL_STRICT_DEPTHFUNC_HINT_PGI 0x1A216
+#define GL_STRICT_LIGHTING_HINT_PGI 0x1A217
+#define GL_STRICT_SCISSOR_HINT_PGI 0x1A218
+#define GL_FULL_STIPPLE_HINT_PGI 0x1A219
+#define GL_CLIP_NEAR_HINT_PGI 0x1A220
+#define GL_CLIP_FAR_HINT_PGI 0x1A221
+#define GL_WIDE_LINE_HINT_PGI 0x1A222
+#define GL_BACK_NORMALS_HINT_PGI 0x1A223
+#endif
+
+#ifndef GL_EXT_paletted_texture
+#define GL_COLOR_INDEX1_EXT 0x80E2
+#define GL_COLOR_INDEX2_EXT 0x80E3
+#define GL_COLOR_INDEX4_EXT 0x80E4
+#define GL_COLOR_INDEX8_EXT 0x80E5
+#define GL_COLOR_INDEX12_EXT 0x80E6
+#define GL_COLOR_INDEX16_EXT 0x80E7
+#define GL_TEXTURE_INDEX_SIZE_EXT 0x80ED
+#endif
+
+#ifndef GL_EXT_clip_volume_hint
+#define GL_CLIP_VOLUME_CLIPPING_HINT_EXT 0x80F0
+#endif
+
+#ifndef GL_SGIX_list_priority
+#define GL_LIST_PRIORITY_SGIX 0x8182
+#endif
+
+#ifndef GL_SGIX_ir_instrument1
+#define GL_IR_INSTRUMENT1_SGIX 0x817F
+#endif
+
+#ifndef GL_SGIX_calligraphic_fragment
+#define GL_CALLIGRAPHIC_FRAGMENT_SGIX 0x8183
+#endif
+
+#ifndef GL_SGIX_texture_lod_bias
+#define GL_TEXTURE_LOD_BIAS_S_SGIX 0x818E
+#define GL_TEXTURE_LOD_BIAS_T_SGIX 0x818F
+#define GL_TEXTURE_LOD_BIAS_R_SGIX 0x8190
+#endif
+
+#ifndef GL_SGIX_shadow_ambient
+#define GL_SHADOW_AMBIENT_SGIX 0x80BF
+#endif
+
+#ifndef GL_EXT_index_texture
+#endif
+
+#ifndef GL_EXT_index_material
+#define GL_INDEX_MATERIAL_EXT 0x81B8
+#define GL_INDEX_MATERIAL_PARAMETER_EXT 0x81B9
+#define GL_INDEX_MATERIAL_FACE_EXT 0x81BA
+#endif
+
+#ifndef GL_EXT_index_func
+#define GL_INDEX_TEST_EXT 0x81B5
+#define GL_INDEX_TEST_FUNC_EXT 0x81B6
+#define GL_INDEX_TEST_REF_EXT 0x81B7
+#endif
+
+#ifndef GL_EXT_index_array_formats
+#define GL_IUI_V2F_EXT 0x81AD
+#define GL_IUI_V3F_EXT 0x81AE
+#define GL_IUI_N3F_V2F_EXT 0x81AF
+#define GL_IUI_N3F_V3F_EXT 0x81B0
+#define GL_T2F_IUI_V2F_EXT 0x81B1
+#define GL_T2F_IUI_V3F_EXT 0x81B2
+#define GL_T2F_IUI_N3F_V2F_EXT 0x81B3
+#define GL_T2F_IUI_N3F_V3F_EXT 0x81B4
+#endif
+
+#ifndef GL_EXT_compiled_vertex_array
+#define GL_ARRAY_ELEMENT_LOCK_FIRST_EXT 0x81A8
+#define GL_ARRAY_ELEMENT_LOCK_COUNT_EXT 0x81A9
+#endif
+
+#ifndef GL_EXT_cull_vertex
+#define GL_CULL_VERTEX_EXT 0x81AA
+#define GL_CULL_VERTEX_EYE_POSITION_EXT 0x81AB
+#define GL_CULL_VERTEX_OBJECT_POSITION_EXT 0x81AC
+#endif
+
+#ifndef GL_SGIX_ycrcb
+#define GL_YCRCB_422_SGIX 0x81BB
+#define GL_YCRCB_444_SGIX 0x81BC
+#endif
+
+#ifndef GL_SGIX_fragment_lighting
+#define GL_FRAGMENT_LIGHTING_SGIX 0x8400
+#define GL_FRAGMENT_COLOR_MATERIAL_SGIX 0x8401
+#define GL_FRAGMENT_COLOR_MATERIAL_FACE_SGIX 0x8402
+#define GL_FRAGMENT_COLOR_MATERIAL_PARAMETER_SGIX 0x8403
+#define GL_MAX_FRAGMENT_LIGHTS_SGIX 0x8404
+#define GL_MAX_ACTIVE_LIGHTS_SGIX 0x8405
+#define GL_CURRENT_RASTER_NORMAL_SGIX 0x8406
+#define GL_LIGHT_ENV_MODE_SGIX 0x8407
+#define GL_FRAGMENT_LIGHT_MODEL_LOCAL_VIEWER_SGIX 0x8408
+#define GL_FRAGMENT_LIGHT_MODEL_TWO_SIDE_SGIX 0x8409
+#define GL_FRAGMENT_LIGHT_MODEL_AMBIENT_SGIX 0x840A
+#define GL_FRAGMENT_LIGHT_MODEL_NORMAL_INTERPOLATION_SGIX 0x840B
+#define GL_FRAGMENT_LIGHT0_SGIX 0x840C
+#define GL_FRAGMENT_LIGHT1_SGIX 0x840D
+#define GL_FRAGMENT_LIGHT2_SGIX 0x840E
+#define GL_FRAGMENT_LIGHT3_SGIX 0x840F
+#define GL_FRAGMENT_LIGHT4_SGIX 0x8410
+#define GL_FRAGMENT_LIGHT5_SGIX 0x8411
+#define GL_FRAGMENT_LIGHT6_SGIX 0x8412
+#define GL_FRAGMENT_LIGHT7_SGIX 0x8413
+#endif
+
+#ifndef GL_IBM_rasterpos_clip
+#define GL_RASTER_POSITION_UNCLIPPED_IBM 0x19262
+#endif
+
+#ifndef GL_HP_texture_lighting
+#define GL_TEXTURE_LIGHTING_MODE_HP 0x8167
+#define GL_TEXTURE_POST_SPECULAR_HP 0x8168
+#define GL_TEXTURE_PRE_SPECULAR_HP 0x8169
+#endif
+
+#ifndef GL_EXT_draw_range_elements
+#define GL_MAX_ELEMENTS_VERTICES_EXT 0x80E8
+#define GL_MAX_ELEMENTS_INDICES_EXT 0x80E9
+#endif
+
+#ifndef GL_WIN_phong_shading
+#define GL_PHONG_WIN 0x80EA
+#define GL_PHONG_HINT_WIN 0x80EB
+#endif
+
+#ifndef GL_WIN_specular_fog
+#define GL_FOG_SPECULAR_TEXTURE_WIN 0x80EC
+#endif
+
+#ifndef GL_EXT_light_texture
+#define GL_FRAGMENT_MATERIAL_EXT 0x8349
+#define GL_FRAGMENT_NORMAL_EXT 0x834A
+#define GL_FRAGMENT_COLOR_EXT 0x834C
+#define GL_ATTENUATION_EXT 0x834D
+#define GL_SHADOW_ATTENUATION_EXT 0x834E
+#define GL_TEXTURE_APPLICATION_MODE_EXT 0x834F
+#define GL_TEXTURE_LIGHT_EXT 0x8350
+#define GL_TEXTURE_MATERIAL_FACE_EXT 0x8351
+#define GL_TEXTURE_MATERIAL_PARAMETER_EXT 0x8352
+/* reuse GL_FRAGMENT_DEPTH_EXT */
+#endif
+
+#ifndef GL_SGIX_blend_alpha_minmax
+#define GL_ALPHA_MIN_SGIX 0x8320
+#define GL_ALPHA_MAX_SGIX 0x8321
+#endif
+
+#ifndef GL_SGIX_impact_pixel_texture
+#define GL_PIXEL_TEX_GEN_Q_CEILING_SGIX 0x8184
+#define GL_PIXEL_TEX_GEN_Q_ROUND_SGIX 0x8185
+#define GL_PIXEL_TEX_GEN_Q_FLOOR_SGIX 0x8186
+#define GL_PIXEL_TEX_GEN_ALPHA_REPLACE_SGIX 0x8187
+#define GL_PIXEL_TEX_GEN_ALPHA_NO_REPLACE_SGIX 0x8188
+#define GL_PIXEL_TEX_GEN_ALPHA_LS_SGIX 0x8189
+#define GL_PIXEL_TEX_GEN_ALPHA_MS_SGIX 0x818A
+#endif
+
+#ifndef GL_EXT_bgra
+#define GL_BGR_EXT 0x80E0
+#define GL_BGRA_EXT 0x80E1
+#endif
+
+#ifndef GL_SGIX_async
+#define GL_ASYNC_MARKER_SGIX 0x8329
+#endif
+
+#ifndef GL_SGIX_async_pixel
+#define GL_ASYNC_TEX_IMAGE_SGIX 0x835C
+#define GL_ASYNC_DRAW_PIXELS_SGIX 0x835D
+#define GL_ASYNC_READ_PIXELS_SGIX 0x835E
+#define GL_MAX_ASYNC_TEX_IMAGE_SGIX 0x835F
+#define GL_MAX_ASYNC_DRAW_PIXELS_SGIX 0x8360
+#define GL_MAX_ASYNC_READ_PIXELS_SGIX 0x8361
+#endif
+
+#ifndef GL_SGIX_async_histogram
+#define GL_ASYNC_HISTOGRAM_SGIX 0x832C
+#define GL_MAX_ASYNC_HISTOGRAM_SGIX 0x832D
+#endif
+
+#ifndef GL_INTEL_texture_scissor
+#endif
+
+#ifndef GL_INTEL_parallel_arrays
+#define GL_PARALLEL_ARRAYS_INTEL 0x83F4
+#define GL_VERTEX_ARRAY_PARALLEL_POINTERS_INTEL 0x83F5
+#define GL_NORMAL_ARRAY_PARALLEL_POINTERS_INTEL 0x83F6
+#define GL_COLOR_ARRAY_PARALLEL_POINTERS_INTEL 0x83F7
+#define GL_TEXTURE_COORD_ARRAY_PARALLEL_POINTERS_INTEL 0x83F8
+#endif
+
+#ifndef GL_HP_occlusion_test
+#define GL_OCCLUSION_TEST_HP 0x8165
+#define GL_OCCLUSION_TEST_RESULT_HP 0x8166
+#endif
+
+#ifndef GL_EXT_pixel_transform
+#define GL_PIXEL_TRANSFORM_2D_EXT 0x8330
+#define GL_PIXEL_MAG_FILTER_EXT 0x8331
+#define GL_PIXEL_MIN_FILTER_EXT 0x8332
+#define GL_PIXEL_CUBIC_WEIGHT_EXT 0x8333
+#define GL_CUBIC_EXT 0x8334
+#define GL_AVERAGE_EXT 0x8335
+#define GL_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8336
+#define GL_MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8337
+#define GL_PIXEL_TRANSFORM_2D_MATRIX_EXT 0x8338
+#endif
+
+#ifndef GL_EXT_pixel_transform_color_table
+#endif
+
+#ifndef GL_EXT_shared_texture_palette
+#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB
+#endif
+
+#ifndef GL_EXT_separate_specular_color
+#define GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8
+#define GL_SINGLE_COLOR_EXT 0x81F9
+#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA
+#endif
+
+#ifndef GL_EXT_secondary_color
+#define GL_COLOR_SUM_EXT 0x8458
+#define GL_CURRENT_SECONDARY_COLOR_EXT 0x8459
+#define GL_SECONDARY_COLOR_ARRAY_SIZE_EXT 0x845A
+#define GL_SECONDARY_COLOR_ARRAY_TYPE_EXT 0x845B
+#define GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT 0x845C
+#define GL_SECONDARY_COLOR_ARRAY_POINTER_EXT 0x845D
+#define GL_SECONDARY_COLOR_ARRAY_EXT 0x845E
+#endif
+
+#ifndef GL_EXT_texture_perturb_normal
+#define GL_PERTURB_EXT 0x85AE
+#define GL_TEXTURE_NORMAL_EXT 0x85AF
+#endif
+
+#ifndef GL_EXT_multi_draw_arrays
+#endif
+
+#ifndef GL_EXT_fog_coord
+#define GL_FOG_COORDINATE_SOURCE_EXT 0x8450
+#define GL_FOG_COORDINATE_EXT 0x8451
+#define GL_FRAGMENT_DEPTH_EXT 0x8452
+#define GL_CURRENT_FOG_COORDINATE_EXT 0x8453
+#define GL_FOG_COORDINATE_ARRAY_TYPE_EXT 0x8454
+#define GL_FOG_COORDINATE_ARRAY_STRIDE_EXT 0x8455
+#define GL_FOG_COORDINATE_ARRAY_POINTER_EXT 0x8456
+#define GL_FOG_COORDINATE_ARRAY_EXT 0x8457
+#endif
+
+#ifndef GL_REND_screen_coordinates
+#define GL_SCREEN_COORDINATES_REND 0x8490
+#define GL_INVERTED_SCREEN_W_REND 0x8491
+#endif
+
+#ifndef GL_EXT_coordinate_frame
+#define GL_TANGENT_ARRAY_EXT 0x8439
+#define GL_BINORMAL_ARRAY_EXT 0x843A
+#define GL_CURRENT_TANGENT_EXT 0x843B
+#define GL_CURRENT_BINORMAL_EXT 0x843C
+#define GL_TANGENT_ARRAY_TYPE_EXT 0x843E
+#define GL_TANGENT_ARRAY_STRIDE_EXT 0x843F
+#define GL_BINORMAL_ARRAY_TYPE_EXT 0x8440
+#define GL_BINORMAL_ARRAY_STRIDE_EXT 0x8441
+#define GL_TANGENT_ARRAY_POINTER_EXT 0x8442
+#define GL_BINORMAL_ARRAY_POINTER_EXT 0x8443
+#define GL_MAP1_TANGENT_EXT 0x8444
+#define GL_MAP2_TANGENT_EXT 0x8445
+#define GL_MAP1_BINORMAL_EXT 0x8446
+#define GL_MAP2_BINORMAL_EXT 0x8447
+#endif
+
+#ifndef GL_EXT_texture_env_combine
+#define GL_COMBINE_EXT 0x8570
+#define GL_COMBINE_RGB_EXT 0x8571
+#define GL_COMBINE_ALPHA_EXT 0x8572
+#define GL_RGB_SCALE_EXT 0x8573
+#define GL_ADD_SIGNED_EXT 0x8574
+#define GL_INTERPOLATE_EXT 0x8575
+#define GL_CONSTANT_EXT 0x8576
+#define GL_PRIMARY_COLOR_EXT 0x8577
+#define GL_PREVIOUS_EXT 0x8578
+#define GL_SOURCE0_RGB_EXT 0x8580
+#define GL_SOURCE1_RGB_EXT 0x8581
+#define GL_SOURCE2_RGB_EXT 0x8582
+#define GL_SOURCE0_ALPHA_EXT 0x8588
+#define GL_SOURCE1_ALPHA_EXT 0x8589
+#define GL_SOURCE2_ALPHA_EXT 0x858A
+#define GL_OPERAND0_RGB_EXT 0x8590
+#define GL_OPERAND1_RGB_EXT 0x8591
+#define GL_OPERAND2_RGB_EXT 0x8592
+#define GL_OPERAND0_ALPHA_EXT 0x8598
+#define GL_OPERAND1_ALPHA_EXT 0x8599
+#define GL_OPERAND2_ALPHA_EXT 0x859A
+#endif
+
+#ifndef GL_APPLE_specular_vector
+#define GL_LIGHT_MODEL_SPECULAR_VECTOR_APPLE 0x85B0
+#endif
+
+#ifndef GL_APPLE_transform_hint
+#define GL_TRANSFORM_HINT_APPLE 0x85B1
+#endif
+
+#ifndef GL_SGIX_fog_scale
+#define GL_FOG_SCALE_SGIX 0x81FC
+#define GL_FOG_SCALE_VALUE_SGIX 0x81FD
+#endif
+
+#ifndef GL_SUNX_constant_data
+#define GL_UNPACK_CONSTANT_DATA_SUNX 0x81D5
+#define GL_TEXTURE_CONSTANT_DATA_SUNX 0x81D6
+#endif
+
+#ifndef GL_SUN_global_alpha
+#define GL_GLOBAL_ALPHA_SUN 0x81D9
+#define GL_GLOBAL_ALPHA_FACTOR_SUN 0x81DA
+#endif
+
+#ifndef GL_SUN_triangle_list
+#define GL_RESTART_SUN 0x0001
+#define GL_REPLACE_MIDDLE_SUN 0x0002
+#define GL_REPLACE_OLDEST_SUN 0x0003
+#define GL_TRIANGLE_LIST_SUN 0x81D7
+#define GL_REPLACEMENT_CODE_SUN 0x81D8
+#define GL_REPLACEMENT_CODE_ARRAY_SUN 0x85C0
+#define GL_REPLACEMENT_CODE_ARRAY_TYPE_SUN 0x85C1
+#define GL_REPLACEMENT_CODE_ARRAY_STRIDE_SUN 0x85C2
+#define GL_REPLACEMENT_CODE_ARRAY_POINTER_SUN 0x85C3
+#define GL_R1UI_V3F_SUN 0x85C4
+#define GL_R1UI_C4UB_V3F_SUN 0x85C5
+#define GL_R1UI_C3F_V3F_SUN 0x85C6
+#define GL_R1UI_N3F_V3F_SUN 0x85C7
+#define GL_R1UI_C4F_N3F_V3F_SUN 0x85C8
+#define GL_R1UI_T2F_V3F_SUN 0x85C9
+#define GL_R1UI_T2F_N3F_V3F_SUN 0x85CA
+#define GL_R1UI_T2F_C4F_N3F_V3F_SUN 0x85CB
+#endif
+
+#ifndef GL_SUN_vertex
+#endif
+
+#ifndef GL_EXT_blend_func_separate
+#define GL_BLEND_DST_RGB_EXT 0x80C8
+#define GL_BLEND_SRC_RGB_EXT 0x80C9
+#define GL_BLEND_DST_ALPHA_EXT 0x80CA
+#define GL_BLEND_SRC_ALPHA_EXT 0x80CB
+#endif
+
+#ifndef GL_INGR_color_clamp
+#define GL_RED_MIN_CLAMP_INGR 0x8560
+#define GL_GREEN_MIN_CLAMP_INGR 0x8561
+#define GL_BLUE_MIN_CLAMP_INGR 0x8562
+#define GL_ALPHA_MIN_CLAMP_INGR 0x8563
+#define GL_RED_MAX_CLAMP_INGR 0x8564
+#define GL_GREEN_MAX_CLAMP_INGR 0x8565
+#define GL_BLUE_MAX_CLAMP_INGR 0x8566
+#define GL_ALPHA_MAX_CLAMP_INGR 0x8567
+#endif
+
+#ifndef GL_INGR_interlace_read
+#define GL_INTERLACE_READ_INGR 0x8568
+#endif
+
+#ifndef GL_EXT_stencil_wrap
+#define GL_INCR_WRAP_EXT 0x8507
+#define GL_DECR_WRAP_EXT 0x8508
+#endif
+
+#ifndef GL_EXT_422_pixels
+#define GL_422_EXT 0x80CC
+#define GL_422_REV_EXT 0x80CD
+#define GL_422_AVERAGE_EXT 0x80CE
+#define GL_422_REV_AVERAGE_EXT 0x80CF
+#endif
+
+#ifndef GL_NV_texgen_reflection
+#define GL_NORMAL_MAP_NV 0x8511
+#define GL_REFLECTION_MAP_NV 0x8512
+#endif
+
+#ifndef GL_EXT_texture_cube_map
+#define GL_NORMAL_MAP_EXT 0x8511
+#define GL_REFLECTION_MAP_EXT 0x8512
+#define GL_TEXTURE_CUBE_MAP_EXT 0x8513
+#define GL_TEXTURE_BINDING_CUBE_MAP_EXT 0x8514
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT 0x8515
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT 0x8516
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT 0x8517
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT 0x8518
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT 0x8519
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT 0x851A
+#define GL_PROXY_TEXTURE_CUBE_MAP_EXT 0x851B
+#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_EXT 0x851C
+#endif
+
+#ifndef GL_SUN_convolution_border_modes
+#define GL_WRAP_BORDER_SUN 0x81D4
+#endif
+
+#ifndef GL_EXT_texture_env_add
+#endif
+
+#ifndef GL_EXT_texture_lod_bias
+#define GL_MAX_TEXTURE_LOD_BIAS_EXT 0x84FD
+#define GL_TEXTURE_FILTER_CONTROL_EXT 0x8500
+#define GL_TEXTURE_LOD_BIAS_EXT 0x8501
+#endif
+
+#ifndef GL_EXT_texture_filter_anisotropic
+#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
+#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
+#endif
+
+#ifndef GL_EXT_vertex_weighting
+#define GL_MODELVIEW0_STACK_DEPTH_EXT GL_MODELVIEW_STACK_DEPTH
+#define GL_MODELVIEW1_STACK_DEPTH_EXT 0x8502
+#define GL_MODELVIEW0_MATRIX_EXT GL_MODELVIEW_MATRIX
+#define GL_MODELVIEW1_MATRIX_EXT 0x8506
+#define GL_VERTEX_WEIGHTING_EXT 0x8509
+#define GL_MODELVIEW0_EXT GL_MODELVIEW
+#define GL_MODELVIEW1_EXT 0x850A
+#define GL_CURRENT_VERTEX_WEIGHT_EXT 0x850B
+#define GL_VERTEX_WEIGHT_ARRAY_EXT 0x850C
+#define GL_VERTEX_WEIGHT_ARRAY_SIZE_EXT 0x850D
+#define GL_VERTEX_WEIGHT_ARRAY_TYPE_EXT 0x850E
+#define GL_VERTEX_WEIGHT_ARRAY_STRIDE_EXT 0x850F
+#define GL_VERTEX_WEIGHT_ARRAY_POINTER_EXT 0x8510
+#endif
+
+#ifndef GL_NV_light_max_exponent
+#define GL_MAX_SHININESS_NV 0x8504
+#define GL_MAX_SPOT_EXPONENT_NV 0x8505
+#endif
+
+#ifndef GL_NV_vertex_array_range
+#define GL_VERTEX_ARRAY_RANGE_NV 0x851D
+#define GL_VERTEX_ARRAY_RANGE_LENGTH_NV 0x851E
+#define GL_VERTEX_ARRAY_RANGE_VALID_NV 0x851F
+#define GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV 0x8520
+#define GL_VERTEX_ARRAY_RANGE_POINTER_NV 0x8521
+#endif
+
+#ifndef GL_NV_register_combiners
+#define GL_REGISTER_COMBINERS_NV 0x8522
+#define GL_VARIABLE_A_NV 0x8523
+#define GL_VARIABLE_B_NV 0x8524
+#define GL_VARIABLE_C_NV 0x8525
+#define GL_VARIABLE_D_NV 0x8526
+#define GL_VARIABLE_E_NV 0x8527
+#define GL_VARIABLE_F_NV 0x8528
+#define GL_VARIABLE_G_NV 0x8529
+#define GL_CONSTANT_COLOR0_NV 0x852A
+#define GL_CONSTANT_COLOR1_NV 0x852B
+#define GL_PRIMARY_COLOR_NV 0x852C
+#define GL_SECONDARY_COLOR_NV 0x852D
+#define GL_SPARE0_NV 0x852E
+#define GL_SPARE1_NV 0x852F
+#define GL_DISCARD_NV 0x8530
+#define GL_E_TIMES_F_NV 0x8531
+#define GL_SPARE0_PLUS_SECONDARY_COLOR_NV 0x8532
+#define GL_UNSIGNED_IDENTITY_NV 0x8536
+#define GL_UNSIGNED_INVERT_NV 0x8537
+#define GL_EXPAND_NORMAL_NV 0x8538
+#define GL_EXPAND_NEGATE_NV 0x8539
+#define GL_HALF_BIAS_NORMAL_NV 0x853A
+#define GL_HALF_BIAS_NEGATE_NV 0x853B
+#define GL_SIGNED_IDENTITY_NV 0x853C
+#define GL_SIGNED_NEGATE_NV 0x853D
+#define GL_SCALE_BY_TWO_NV 0x853E
+#define GL_SCALE_BY_FOUR_NV 0x853F
+#define GL_SCALE_BY_ONE_HALF_NV 0x8540
+#define GL_BIAS_BY_NEGATIVE_ONE_HALF_NV 0x8541
+#define GL_COMBINER_INPUT_NV 0x8542
+#define GL_COMBINER_MAPPING_NV 0x8543
+#define GL_COMBINER_COMPONENT_USAGE_NV 0x8544
+#define GL_COMBINER_AB_DOT_PRODUCT_NV 0x8545
+#define GL_COMBINER_CD_DOT_PRODUCT_NV 0x8546
+#define GL_COMBINER_MUX_SUM_NV 0x8547
+#define GL_COMBINER_SCALE_NV 0x8548
+#define GL_COMBINER_BIAS_NV 0x8549
+#define GL_COMBINER_AB_OUTPUT_NV 0x854A
+#define GL_COMBINER_CD_OUTPUT_NV 0x854B
+#define GL_COMBINER_SUM_OUTPUT_NV 0x854C
+#define GL_MAX_GENERAL_COMBINERS_NV 0x854D
+#define GL_NUM_GENERAL_COMBINERS_NV 0x854E
+#define GL_COLOR_SUM_CLAMP_NV 0x854F
+#define GL_COMBINER0_NV 0x8550
+#define GL_COMBINER1_NV 0x8551
+#define GL_COMBINER2_NV 0x8552
+#define GL_COMBINER3_NV 0x8553
+#define GL_COMBINER4_NV 0x8554
+#define GL_COMBINER5_NV 0x8555
+#define GL_COMBINER6_NV 0x8556
+#define GL_COMBINER7_NV 0x8557
+/* reuse GL_TEXTURE0_ARB */
+/* reuse GL_TEXTURE1_ARB */
+/* reuse GL_ZERO */
+/* reuse GL_NONE */
+/* reuse GL_FOG */
+#endif
+
+#ifndef GL_NV_fog_distance
+#define GL_FOG_DISTANCE_MODE_NV 0x855A
+#define GL_EYE_RADIAL_NV 0x855B
+#define GL_EYE_PLANE_ABSOLUTE_NV 0x855C
+/* reuse GL_EYE_PLANE */
+#endif
+
+#ifndef GL_NV_texgen_emboss
+#define GL_EMBOSS_LIGHT_NV 0x855D
+#define GL_EMBOSS_CONSTANT_NV 0x855E
+#define GL_EMBOSS_MAP_NV 0x855F
+#endif
+
+#ifndef GL_NV_blend_square
+#endif
+
+#ifndef GL_NV_texture_env_combine4
+#define GL_COMBINE4_NV 0x8503
+#define GL_SOURCE3_RGB_NV 0x8583
+#define GL_SOURCE3_ALPHA_NV 0x858B
+#define GL_OPERAND3_RGB_NV 0x8593
+#define GL_OPERAND3_ALPHA_NV 0x859B
+#endif
+
+#ifndef GL_MESA_resize_buffers
+#endif
+
+#ifndef GL_MESA_window_pos
+#endif
+
+#ifndef GL_EXT_texture_compression_s3tc
+#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
+#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
+#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
+#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
+#endif
+
+#ifndef GL_IBM_cull_vertex
+#define GL_CULL_VERTEX_IBM 103050
+#endif
+
+#ifndef GL_IBM_multimode_draw_arrays
+#endif
+
+#ifndef GL_IBM_vertex_array_lists
+#define GL_VERTEX_ARRAY_LIST_IBM 103070
+#define GL_NORMAL_ARRAY_LIST_IBM 103071
+#define GL_COLOR_ARRAY_LIST_IBM 103072
+#define GL_INDEX_ARRAY_LIST_IBM 103073
+#define GL_TEXTURE_COORD_ARRAY_LIST_IBM 103074
+#define GL_EDGE_FLAG_ARRAY_LIST_IBM 103075
+#define GL_FOG_COORDINATE_ARRAY_LIST_IBM 103076
+#define GL_SECONDARY_COLOR_ARRAY_LIST_IBM 103077
+#define GL_VERTEX_ARRAY_LIST_STRIDE_IBM 103080
+#define GL_NORMAL_ARRAY_LIST_STRIDE_IBM 103081
+#define GL_COLOR_ARRAY_LIST_STRIDE_IBM 103082
+#define GL_INDEX_ARRAY_LIST_STRIDE_IBM 103083
+#define GL_TEXTURE_COORD_ARRAY_LIST_STRIDE_IBM 103084
+#define GL_EDGE_FLAG_ARRAY_LIST_STRIDE_IBM 103085
+#define GL_FOG_COORDINATE_ARRAY_LIST_STRIDE_IBM 103086
+#define GL_SECONDARY_COLOR_ARRAY_LIST_STRIDE_IBM 103087
+#endif
+
+#ifndef GL_SGIX_subsample
+#define GL_PACK_SUBSAMPLE_RATE_SGIX 0x85A0
+#define GL_UNPACK_SUBSAMPLE_RATE_SGIX 0x85A1
+#define GL_PIXEL_SUBSAMPLE_4444_SGIX 0x85A2
+#define GL_PIXEL_SUBSAMPLE_2424_SGIX 0x85A3
+#define GL_PIXEL_SUBSAMPLE_4242_SGIX 0x85A4
+#endif
+
+#ifndef GL_SGIX_ycrcb_subsample
+#endif
+
+#ifndef GL_SGIX_ycrcba
+#define GL_YCRCB_SGIX 0x8318
+#define GL_YCRCBA_SGIX 0x8319
+#endif
+
+#ifndef GL_SGI_depth_pass_instrument
+#define GL_DEPTH_PASS_INSTRUMENT_SGIX 0x8310
+#define GL_DEPTH_PASS_INSTRUMENT_COUNTERS_SGIX 0x8311
+#define GL_DEPTH_PASS_INSTRUMENT_MAX_SGIX 0x8312
+#endif
+
+#ifndef GL_3DFX_texture_compression_FXT1
+#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0
+#define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1
+#endif
+
+#ifndef GL_3DFX_multisample
+#define GL_MULTISAMPLE_3DFX 0x86B2
+#define GL_SAMPLE_BUFFERS_3DFX 0x86B3
+#define GL_SAMPLES_3DFX 0x86B4
+#define GL_MULTISAMPLE_BIT_3DFX 0x20000000
+#endif
+
+#ifndef GL_3DFX_tbuffer
+#endif
+
+#ifndef GL_EXT_multisample
+#define GL_MULTISAMPLE_EXT 0x809D
+#define GL_SAMPLE_ALPHA_TO_MASK_EXT 0x809E
+#define GL_SAMPLE_ALPHA_TO_ONE_EXT 0x809F
+#define GL_SAMPLE_MASK_EXT 0x80A0
+#define GL_1PASS_EXT 0x80A1
+#define GL_2PASS_0_EXT 0x80A2
+#define GL_2PASS_1_EXT 0x80A3
+#define GL_4PASS_0_EXT 0x80A4
+#define GL_4PASS_1_EXT 0x80A5
+#define GL_4PASS_2_EXT 0x80A6
+#define GL_4PASS_3_EXT 0x80A7
+#define GL_SAMPLE_BUFFERS_EXT 0x80A8
+#define GL_SAMPLES_EXT 0x80A9
+#define GL_SAMPLE_MASK_VALUE_EXT 0x80AA
+#define GL_SAMPLE_MASK_INVERT_EXT 0x80AB
+#define GL_SAMPLE_PATTERN_EXT 0x80AC
+#define GL_MULTISAMPLE_BIT_EXT 0x20000000
+#endif
+
+#ifndef GL_SGIX_vertex_preclip
+#define GL_VERTEX_PRECLIP_SGIX 0x83EE
+#define GL_VERTEX_PRECLIP_HINT_SGIX 0x83EF
+#endif
+
+#ifndef GL_SGIX_convolution_accuracy
+#define GL_CONVOLUTION_HINT_SGIX 0x8316
+#endif
+
+#ifndef GL_SGIX_resample
+#define GL_PACK_RESAMPLE_SGIX 0x842C
+#define GL_UNPACK_RESAMPLE_SGIX 0x842D
+#define GL_RESAMPLE_REPLICATE_SGIX 0x842E
+#define GL_RESAMPLE_ZERO_FILL_SGIX 0x842F
+#define GL_RESAMPLE_DECIMATE_SGIX 0x8430
+#endif
+
+#ifndef GL_SGIS_point_line_texgen
+#define GL_EYE_DISTANCE_TO_POINT_SGIS 0x81F0
+#define GL_OBJECT_DISTANCE_TO_POINT_SGIS 0x81F1
+#define GL_EYE_DISTANCE_TO_LINE_SGIS 0x81F2
+#define GL_OBJECT_DISTANCE_TO_LINE_SGIS 0x81F3
+#define GL_EYE_POINT_SGIS 0x81F4
+#define GL_OBJECT_POINT_SGIS 0x81F5
+#define GL_EYE_LINE_SGIS 0x81F6
+#define GL_OBJECT_LINE_SGIS 0x81F7
+#endif
+
+#ifndef GL_SGIS_texture_color_mask
+#define GL_TEXTURE_COLOR_WRITEMASK_SGIS 0x81EF
+#endif
+
+#ifndef GL_EXT_texture_env_dot3
+#define GL_DOT3_RGB_EXT 0x8740
+#define GL_DOT3_RGBA_EXT 0x8741
+#endif
+
+#ifndef GL_ATI_texture_mirror_once
+#define GL_MIRROR_CLAMP_ATI 0x8742
+#define GL_MIRROR_CLAMP_TO_EDGE_ATI 0x8743
+#endif
+
+#ifndef GL_NV_fence
+#define GL_ALL_COMPLETED_NV 0x84F2
+#define GL_FENCE_STATUS_NV 0x84F3
+#define GL_FENCE_CONDITION_NV 0x84F4
+#endif
+
+#ifndef GL_IBM_texture_mirrored_repeat
+#define GL_MIRRORED_REPEAT_IBM 0x8370
+#endif
+
+#ifndef GL_NV_evaluators
+#define GL_EVAL_2D_NV 0x86C0
+#define GL_EVAL_TRIANGULAR_2D_NV 0x86C1
+#define GL_MAP_TESSELLATION_NV 0x86C2
+#define GL_MAP_ATTRIB_U_ORDER_NV 0x86C3
+#define GL_MAP_ATTRIB_V_ORDER_NV 0x86C4
+#define GL_EVAL_FRACTIONAL_TESSELLATION_NV 0x86C5
+#define GL_EVAL_VERTEX_ATTRIB0_NV 0x86C6
+#define GL_EVAL_VERTEX_ATTRIB1_NV 0x86C7
+#define GL_EVAL_VERTEX_ATTRIB2_NV 0x86C8
+#define GL_EVAL_VERTEX_ATTRIB3_NV 0x86C9
+#define GL_EVAL_VERTEX_ATTRIB4_NV 0x86CA
+#define GL_EVAL_VERTEX_ATTRIB5_NV 0x86CB
+#define GL_EVAL_VERTEX_ATTRIB6_NV 0x86CC
+#define GL_EVAL_VERTEX_ATTRIB7_NV 0x86CD
+#define GL_EVAL_VERTEX_ATTRIB8_NV 0x86CE
+#define GL_EVAL_VERTEX_ATTRIB9_NV 0x86CF
+#define GL_EVAL_VERTEX_ATTRIB10_NV 0x86D0
+#define GL_EVAL_VERTEX_ATTRIB11_NV 0x86D1
+#define GL_EVAL_VERTEX_ATTRIB12_NV 0x86D2
+#define GL_EVAL_VERTEX_ATTRIB13_NV 0x86D3
+#define GL_EVAL_VERTEX_ATTRIB14_NV 0x86D4
+#define GL_EVAL_VERTEX_ATTRIB15_NV 0x86D5
+#define GL_MAX_MAP_TESSELLATION_NV 0x86D6
+#define GL_MAX_RATIONAL_EVAL_ORDER_NV 0x86D7
+#endif
+
+#ifndef GL_NV_packed_depth_stencil
+#define GL_DEPTH_STENCIL_NV 0x84F9
+#define GL_UNSIGNED_INT_24_8_NV 0x84FA
+#endif
+
+#ifndef GL_NV_register_combiners2
+#define GL_PER_STAGE_CONSTANTS_NV 0x8535
+#endif
+
+#ifndef GL_NV_texture_compression_vtc
+#endif
+
+#ifndef GL_NV_texture_rectangle
+#define GL_TEXTURE_RECTANGLE_NV 0x84F5
+#define GL_TEXTURE_BINDING_RECTANGLE_NV 0x84F6
+#define GL_PROXY_TEXTURE_RECTANGLE_NV 0x84F7
+#define GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 0x84F8
+#endif
+
+#ifndef GL_NV_texture_shader
+#define GL_OFFSET_TEXTURE_RECTANGLE_NV 0x864C
+#define GL_OFFSET_TEXTURE_RECTANGLE_SCALE_NV 0x864D
+#define GL_DOT_PRODUCT_TEXTURE_RECTANGLE_NV 0x864E
+#define GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV 0x86D9
+#define GL_UNSIGNED_INT_S8_S8_8_8_NV 0x86DA
+#define GL_UNSIGNED_INT_8_8_S8_S8_REV_NV 0x86DB
+#define GL_DSDT_MAG_INTENSITY_NV 0x86DC
+#define GL_SHADER_CONSISTENT_NV 0x86DD
+#define GL_TEXTURE_SHADER_NV 0x86DE
+#define GL_SHADER_OPERATION_NV 0x86DF
+#define GL_CULL_MODES_NV 0x86E0
+#define GL_OFFSET_TEXTURE_MATRIX_NV 0x86E1
+#define GL_OFFSET_TEXTURE_SCALE_NV 0x86E2
+#define GL_OFFSET_TEXTURE_BIAS_NV 0x86E3
+#define GL_OFFSET_TEXTURE_2D_MATRIX_NV GL_OFFSET_TEXTURE_MATRIX_NV
+#define GL_OFFSET_TEXTURE_2D_SCALE_NV GL_OFFSET_TEXTURE_SCALE_NV
+#define GL_OFFSET_TEXTURE_2D_BIAS_NV GL_OFFSET_TEXTURE_BIAS_NV
+#define GL_PREVIOUS_TEXTURE_INPUT_NV 0x86E4
+#define GL_CONST_EYE_NV 0x86E5
+#define GL_PASS_THROUGH_NV 0x86E6
+#define GL_CULL_FRAGMENT_NV 0x86E7
+#define GL_OFFSET_TEXTURE_2D_NV 0x86E8
+#define GL_DEPENDENT_AR_TEXTURE_2D_NV 0x86E9
+#define GL_DEPENDENT_GB_TEXTURE_2D_NV 0x86EA
+#define GL_DOT_PRODUCT_NV 0x86EC
+#define GL_DOT_PRODUCT_DEPTH_REPLACE_NV 0x86ED
+#define GL_DOT_PRODUCT_TEXTURE_2D_NV 0x86EE
+#define GL_DOT_PRODUCT_TEXTURE_CUBE_MAP_NV 0x86F0
+#define GL_DOT_PRODUCT_DIFFUSE_CUBE_MAP_NV 0x86F1
+#define GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV 0x86F2
+#define GL_DOT_PRODUCT_CONST_EYE_REFLECT_CUBE_MAP_NV 0x86F3
+#define GL_HILO_NV 0x86F4
+#define GL_DSDT_NV 0x86F5
+#define GL_DSDT_MAG_NV 0x86F6
+#define GL_DSDT_MAG_VIB_NV 0x86F7
+#define GL_HILO16_NV 0x86F8
+#define GL_SIGNED_HILO_NV 0x86F9
+#define GL_SIGNED_HILO16_NV 0x86FA
+#define GL_SIGNED_RGBA_NV 0x86FB
+#define GL_SIGNED_RGBA8_NV 0x86FC
+#define GL_SIGNED_RGB_NV 0x86FE
+#define GL_SIGNED_RGB8_NV 0x86FF
+#define GL_SIGNED_LUMINANCE_NV 0x8701
+#define GL_SIGNED_LUMINANCE8_NV 0x8702
+#define GL_SIGNED_LUMINANCE_ALPHA_NV 0x8703
+#define GL_SIGNED_LUMINANCE8_ALPHA8_NV 0x8704
+#define GL_SIGNED_ALPHA_NV 0x8705
+#define GL_SIGNED_ALPHA8_NV 0x8706
+#define GL_SIGNED_INTENSITY_NV 0x8707
+#define GL_SIGNED_INTENSITY8_NV 0x8708
+#define GL_DSDT8_NV 0x8709
+#define GL_DSDT8_MAG8_NV 0x870A
+#define GL_DSDT8_MAG8_INTENSITY8_NV 0x870B
+#define GL_SIGNED_RGB_UNSIGNED_ALPHA_NV 0x870C
+#define GL_SIGNED_RGB8_UNSIGNED_ALPHA8_NV 0x870D
+#define GL_HI_SCALE_NV 0x870E
+#define GL_LO_SCALE_NV 0x870F
+#define GL_DS_SCALE_NV 0x8710
+#define GL_DT_SCALE_NV 0x8711
+#define GL_MAGNITUDE_SCALE_NV 0x8712
+#define GL_VIBRANCE_SCALE_NV 0x8713
+#define GL_HI_BIAS_NV 0x8714
+#define GL_LO_BIAS_NV 0x8715
+#define GL_DS_BIAS_NV 0x8716
+#define GL_DT_BIAS_NV 0x8717
+#define GL_MAGNITUDE_BIAS_NV 0x8718
+#define GL_VIBRANCE_BIAS_NV 0x8719
+#define GL_TEXTURE_BORDER_VALUES_NV 0x871A
+#define GL_TEXTURE_HI_SIZE_NV 0x871B
+#define GL_TEXTURE_LO_SIZE_NV 0x871C
+#define GL_TEXTURE_DS_SIZE_NV 0x871D
+#define GL_TEXTURE_DT_SIZE_NV 0x871E
+#define GL_TEXTURE_MAG_SIZE_NV 0x871F
+#endif
+
+#ifndef GL_NV_texture_shader2
+#define GL_DOT_PRODUCT_TEXTURE_3D_NV 0x86EF
+#endif
+
+#ifndef GL_NV_vertex_array_range2
+#define GL_VERTEX_ARRAY_RANGE_WITHOUT_FLUSH_NV 0x8533
+#endif
+
+#ifndef GL_NV_vertex_program
+#define GL_VERTEX_PROGRAM_NV 0x8620
+#define GL_VERTEX_STATE_PROGRAM_NV 0x8621
+#define GL_ATTRIB_ARRAY_SIZE_NV 0x8623
+#define GL_ATTRIB_ARRAY_STRIDE_NV 0x8624
+#define GL_ATTRIB_ARRAY_TYPE_NV 0x8625
+#define GL_CURRENT_ATTRIB_NV 0x8626
+#define GL_PROGRAM_LENGTH_NV 0x8627
+#define GL_PROGRAM_STRING_NV 0x8628
+#define GL_MODELVIEW_PROJECTION_NV 0x8629
+#define GL_IDENTITY_NV 0x862A
+#define GL_INVERSE_NV 0x862B
+#define GL_TRANSPOSE_NV 0x862C
+#define GL_INVERSE_TRANSPOSE_NV 0x862D
+#define GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV 0x862E
+#define GL_MAX_TRACK_MATRICES_NV 0x862F
+#define GL_MATRIX0_NV 0x8630
+#define GL_MATRIX1_NV 0x8631
+#define GL_MATRIX2_NV 0x8632
+#define GL_MATRIX3_NV 0x8633
+#define GL_MATRIX4_NV 0x8634
+#define GL_MATRIX5_NV 0x8635
+#define GL_MATRIX6_NV 0x8636
+#define GL_MATRIX7_NV 0x8637
+#define GL_CURRENT_MATRIX_STACK_DEPTH_NV 0x8640
+#define GL_CURRENT_MATRIX_NV 0x8641
+#define GL_VERTEX_PROGRAM_POINT_SIZE_NV 0x8642
+#define GL_VERTEX_PROGRAM_TWO_SIDE_NV 0x8643
+#define GL_PROGRAM_PARAMETER_NV 0x8644
+#define GL_ATTRIB_ARRAY_POINTER_NV 0x8645
+#define GL_PROGRAM_TARGET_NV 0x8646
+#define GL_PROGRAM_RESIDENT_NV 0x8647
+#define GL_TRACK_MATRIX_NV 0x8648
+#define GL_TRACK_MATRIX_TRANSFORM_NV 0x8649
+#define GL_VERTEX_PROGRAM_BINDING_NV 0x864A
+#define GL_PROGRAM_ERROR_POSITION_NV 0x864B
+#define GL_VERTEX_ATTRIB_ARRAY0_NV 0x8650
+#define GL_VERTEX_ATTRIB_ARRAY1_NV 0x8651
+#define GL_VERTEX_ATTRIB_ARRAY2_NV 0x8652
+#define GL_VERTEX_ATTRIB_ARRAY3_NV 0x8653
+#define GL_VERTEX_ATTRIB_ARRAY4_NV 0x8654
+#define GL_VERTEX_ATTRIB_ARRAY5_NV 0x8655
+#define GL_VERTEX_ATTRIB_ARRAY6_NV 0x8656
+#define GL_VERTEX_ATTRIB_ARRAY7_NV 0x8657
+#define GL_VERTEX_ATTRIB_ARRAY8_NV 0x8658
+#define GL_VERTEX_ATTRIB_ARRAY9_NV 0x8659
+#define GL_VERTEX_ATTRIB_ARRAY10_NV 0x865A
+#define GL_VERTEX_ATTRIB_ARRAY11_NV 0x865B
+#define GL_VERTEX_ATTRIB_ARRAY12_NV 0x865C
+#define GL_VERTEX_ATTRIB_ARRAY13_NV 0x865D
+#define GL_VERTEX_ATTRIB_ARRAY14_NV 0x865E
+#define GL_VERTEX_ATTRIB_ARRAY15_NV 0x865F
+#define GL_MAP1_VERTEX_ATTRIB0_4_NV 0x8660
+#define GL_MAP1_VERTEX_ATTRIB1_4_NV 0x8661
+#define GL_MAP1_VERTEX_ATTRIB2_4_NV 0x8662
+#define GL_MAP1_VERTEX_ATTRIB3_4_NV 0x8663
+#define GL_MAP1_VERTEX_ATTRIB4_4_NV 0x8664
+#define GL_MAP1_VERTEX_ATTRIB5_4_NV 0x8665
+#define GL_MAP1_VERTEX_ATTRIB6_4_NV 0x8666
+#define GL_MAP1_VERTEX_ATTRIB7_4_NV 0x8667
+#define GL_MAP1_VERTEX_ATTRIB8_4_NV 0x8668
+#define GL_MAP1_VERTEX_ATTRIB9_4_NV 0x8669
+#define GL_MAP1_VERTEX_ATTRIB10_4_NV 0x866A
+#define GL_MAP1_VERTEX_ATTRIB11_4_NV 0x866B
+#define GL_MAP1_VERTEX_ATTRIB12_4_NV 0x866C
+#define GL_MAP1_VERTEX_ATTRIB13_4_NV 0x866D
+#define GL_MAP1_VERTEX_ATTRIB14_4_NV 0x866E
+#define GL_MAP1_VERTEX_ATTRIB15_4_NV 0x866F
+#define GL_MAP2_VERTEX_ATTRIB0_4_NV 0x8670
+#define GL_MAP2_VERTEX_ATTRIB1_4_NV 0x8671
+#define GL_MAP2_VERTEX_ATTRIB2_4_NV 0x8672
+#define GL_MAP2_VERTEX_ATTRIB3_4_NV 0x8673
+#define GL_MAP2_VERTEX_ATTRIB4_4_NV 0x8674
+#define GL_MAP2_VERTEX_ATTRIB5_4_NV 0x8675
+#define GL_MAP2_VERTEX_ATTRIB6_4_NV 0x8676
+#define GL_MAP2_VERTEX_ATTRIB7_4_NV 0x8677
+#define GL_MAP2_VERTEX_ATTRIB8_4_NV 0x8678
+#define GL_MAP2_VERTEX_ATTRIB9_4_NV 0x8679
+#define GL_MAP2_VERTEX_ATTRIB10_4_NV 0x867A
+#define GL_MAP2_VERTEX_ATTRIB11_4_NV 0x867B
+#define GL_MAP2_VERTEX_ATTRIB12_4_NV 0x867C
+#define GL_MAP2_VERTEX_ATTRIB13_4_NV 0x867D
+#define GL_MAP2_VERTEX_ATTRIB14_4_NV 0x867E
+#define GL_MAP2_VERTEX_ATTRIB15_4_NV 0x867F
+#endif
+
+#ifndef GL_SGIX_texture_coordinate_clamp
+#define GL_TEXTURE_MAX_CLAMP_S_SGIX 0x8369
+#define GL_TEXTURE_MAX_CLAMP_T_SGIX 0x836A
+#define GL_TEXTURE_MAX_CLAMP_R_SGIX 0x836B
+#endif
+
+#ifndef GL_SGIX_scalebias_hint
+#define GL_SCALEBIAS_HINT_SGIX 0x8322
+#endif
+
+#ifndef GL_OML_interlace
+#define GL_INTERLACE_OML 0x8980
+#define GL_INTERLACE_READ_OML 0x8981
+#endif
+
+#ifndef GL_OML_subsample
+#define GL_FORMAT_SUBSAMPLE_24_24_OML 0x8982
+#define GL_FORMAT_SUBSAMPLE_244_244_OML 0x8983
+#endif
+
+#ifndef GL_OML_resample
+#define GL_PACK_RESAMPLE_OML 0x8984
+#define GL_UNPACK_RESAMPLE_OML 0x8985
+#define GL_RESAMPLE_REPLICATE_OML 0x8986
+#define GL_RESAMPLE_ZERO_FILL_OML 0x8987
+#define GL_RESAMPLE_AVERAGE_OML 0x8988
+#define GL_RESAMPLE_DECIMATE_OML 0x8989
+#endif
+
+#ifndef GL_NV_copy_depth_to_color
+#define GL_DEPTH_STENCIL_TO_RGBA_NV 0x886E
+#define GL_DEPTH_STENCIL_TO_BGRA_NV 0x886F
+#endif
+
+#ifndef GL_ATI_envmap_bumpmap
+#define GL_BUMP_ROT_MATRIX_ATI 0x8775
+#define GL_BUMP_ROT_MATRIX_SIZE_ATI 0x8776
+#define GL_BUMP_NUM_TEX_UNITS_ATI 0x8777
+#define GL_BUMP_TEX_UNITS_ATI 0x8778
+#define GL_DUDV_ATI 0x8779
+#define GL_DU8DV8_ATI 0x877A
+#define GL_BUMP_ENVMAP_ATI 0x877B
+#define GL_BUMP_TARGET_ATI 0x877C
+#endif
+
+#ifndef GL_ATI_fragment_shader
+#define GL_FRAGMENT_SHADER_ATI 0x8920
+#define GL_REG_0_ATI 0x8921
+#define GL_REG_1_ATI 0x8922
+#define GL_REG_2_ATI 0x8923
+#define GL_REG_3_ATI 0x8924
+#define GL_REG_4_ATI 0x8925
+#define GL_REG_5_ATI 0x8926
+#define GL_REG_6_ATI 0x8927
+#define GL_REG_7_ATI 0x8928
+#define GL_REG_8_ATI 0x8929
+#define GL_REG_9_ATI 0x892A
+#define GL_REG_10_ATI 0x892B
+#define GL_REG_11_ATI 0x892C
+#define GL_REG_12_ATI 0x892D
+#define GL_REG_13_ATI 0x892E
+#define GL_REG_14_ATI 0x892F
+#define GL_REG_15_ATI 0x8930
+#define GL_REG_16_ATI 0x8931
+#define GL_REG_17_ATI 0x8932
+#define GL_REG_18_ATI 0x8933
+#define GL_REG_19_ATI 0x8934
+#define GL_REG_20_ATI 0x8935
+#define GL_REG_21_ATI 0x8936
+#define GL_REG_22_ATI 0x8937
+#define GL_REG_23_ATI 0x8938
+#define GL_REG_24_ATI 0x8939
+#define GL_REG_25_ATI 0x893A
+#define GL_REG_26_ATI 0x893B
+#define GL_REG_27_ATI 0x893C
+#define GL_REG_28_ATI 0x893D
+#define GL_REG_29_ATI 0x893E
+#define GL_REG_30_ATI 0x893F
+#define GL_REG_31_ATI 0x8940
+#define GL_CON_0_ATI 0x8941
+#define GL_CON_1_ATI 0x8942
+#define GL_CON_2_ATI 0x8943
+#define GL_CON_3_ATI 0x8944
+#define GL_CON_4_ATI 0x8945
+#define GL_CON_5_ATI 0x8946
+#define GL_CON_6_ATI 0x8947
+#define GL_CON_7_ATI 0x8948
+#define GL_CON_8_ATI 0x8949
+#define GL_CON_9_ATI 0x894A
+#define GL_CON_10_ATI 0x894B
+#define GL_CON_11_ATI 0x894C
+#define GL_CON_12_ATI 0x894D
+#define GL_CON_13_ATI 0x894E
+#define GL_CON_14_ATI 0x894F
+#define GL_CON_15_ATI 0x8950
+#define GL_CON_16_ATI 0x8951
+#define GL_CON_17_ATI 0x8952
+#define GL_CON_18_ATI 0x8953
+#define GL_CON_19_ATI 0x8954
+#define GL_CON_20_ATI 0x8955
+#define GL_CON_21_ATI 0x8956
+#define GL_CON_22_ATI 0x8957
+#define GL_CON_23_ATI 0x8958
+#define GL_CON_24_ATI 0x8959
+#define GL_CON_25_ATI 0x895A
+#define GL_CON_26_ATI 0x895B
+#define GL_CON_27_ATI 0x895C
+#define GL_CON_28_ATI 0x895D
+#define GL_CON_29_ATI 0x895E
+#define GL_CON_30_ATI 0x895F
+#define GL_CON_31_ATI 0x8960
+#define GL_MOV_ATI 0x8961
+#define GL_ADD_ATI 0x8963
+#define GL_MUL_ATI 0x8964
+#define GL_SUB_ATI 0x8965
+#define GL_DOT3_ATI 0x8966
+#define GL_DOT4_ATI 0x8967
+#define GL_MAD_ATI 0x8968
+#define GL_LERP_ATI 0x8969
+#define GL_CND_ATI 0x896A
+#define GL_CND0_ATI 0x896B
+#define GL_DOT2_ADD_ATI 0x896C
+#define GL_SECONDARY_INTERPOLATOR_ATI 0x896D
+#define GL_NUM_FRAGMENT_REGISTERS_ATI 0x896E
+#define GL_NUM_FRAGMENT_CONSTANTS_ATI 0x896F
+#define GL_NUM_PASSES_ATI 0x8970
+#define GL_NUM_INSTRUCTIONS_PER_PASS_ATI 0x8971
+#define GL_NUM_INSTRUCTIONS_TOTAL_ATI 0x8972
+#define GL_NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI 0x8973
+#define GL_NUM_LOOPBACK_COMPONENTS_ATI 0x8974
+#define GL_COLOR_ALPHA_PAIRING_ATI 0x8975
+#define GL_SWIZZLE_STR_ATI 0x8976
+#define GL_SWIZZLE_STQ_ATI 0x8977
+#define GL_SWIZZLE_STR_DR_ATI 0x8978
+#define GL_SWIZZLE_STQ_DQ_ATI 0x8979
+#define GL_SWIZZLE_STRQ_ATI 0x897A
+#define GL_SWIZZLE_STRQ_DQ_ATI 0x897B
+#define GL_RED_BIT_ATI 0x00000001
+#define GL_GREEN_BIT_ATI 0x00000002
+#define GL_BLUE_BIT_ATI 0x00000004
+#define GL_2X_BIT_ATI 0x00000001
+#define GL_4X_BIT_ATI 0x00000002
+#define GL_8X_BIT_ATI 0x00000004
+#define GL_HALF_BIT_ATI 0x00000008
+#define GL_QUARTER_BIT_ATI 0x00000010
+#define GL_EIGHTH_BIT_ATI 0x00000020
+#define GL_SATURATE_BIT_ATI 0x00000040
+#define GL_COMP_BIT_ATI 0x00000002
+#define GL_NEGATE_BIT_ATI 0x00000004
+#define GL_BIAS_BIT_ATI 0x00000008
+#endif
+
+#ifndef GL_ATI_pn_triangles
+#define GL_PN_TRIANGLES_ATI 0x87F0
+#define GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F1
+#define GL_PN_TRIANGLES_POINT_MODE_ATI 0x87F2
+#define GL_PN_TRIANGLES_NORMAL_MODE_ATI 0x87F3
+#define GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F4
+#define GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI 0x87F5
+#define GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI 0x87F6
+#define GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI 0x87F7
+#define GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI 0x87F8
+#endif
+
+#ifndef GL_ATI_vertex_array_object
+#define GL_STATIC_ATI 0x8760
+#define GL_DYNAMIC_ATI 0x8761
+#define GL_PRESERVE_ATI 0x8762
+#define GL_DISCARD_ATI 0x8763
+#define GL_OBJECT_BUFFER_SIZE_ATI 0x8764
+#define GL_OBJECT_BUFFER_USAGE_ATI 0x8765
+#define GL_ARRAY_OBJECT_BUFFER_ATI 0x8766
+#define GL_ARRAY_OBJECT_OFFSET_ATI 0x8767
+#endif
+
+#ifndef GL_EXT_vertex_shader
+#define GL_VERTEX_SHADER_EXT 0x8780
+#define GL_VERTEX_SHADER_BINDING_EXT 0x8781
+#define GL_OP_INDEX_EXT 0x8782
+#define GL_OP_NEGATE_EXT 0x8783
+#define GL_OP_DOT3_EXT 0x8784
+#define GL_OP_DOT4_EXT 0x8785
+#define GL_OP_MUL_EXT 0x8786
+#define GL_OP_ADD_EXT 0x8787
+#define GL_OP_MADD_EXT 0x8788
+#define GL_OP_FRAC_EXT 0x8789
+#define GL_OP_MAX_EXT 0x878A
+#define GL_OP_MIN_EXT 0x878B
+#define GL_OP_SET_GE_EXT 0x878C
+#define GL_OP_SET_LT_EXT 0x878D
+#define GL_OP_CLAMP_EXT 0x878E
+#define GL_OP_FLOOR_EXT 0x878F
+#define GL_OP_ROUND_EXT 0x8790
+#define GL_OP_EXP_BASE_2_EXT 0x8791
+#define GL_OP_LOG_BASE_2_EXT 0x8792
+#define GL_OP_POWER_EXT 0x8793
+#define GL_OP_RECIP_EXT 0x8794
+#define GL_OP_RECIP_SQRT_EXT 0x8795
+#define GL_OP_SUB_EXT 0x8796
+#define GL_OP_CROSS_PRODUCT_EXT 0x8797
+#define GL_OP_MULTIPLY_MATRIX_EXT 0x8798
+#define GL_OP_MOV_EXT 0x8799
+#define GL_OUTPUT_VERTEX_EXT 0x879A
+#define GL_OUTPUT_COLOR0_EXT 0x879B
+#define GL_OUTPUT_COLOR1_EXT 0x879C
+#define GL_OUTPUT_TEXTURE_COORD0_EXT 0x879D
+#define GL_OUTPUT_TEXTURE_COORD1_EXT 0x879E
+#define GL_OUTPUT_TEXTURE_COORD2_EXT 0x879F
+#define GL_OUTPUT_TEXTURE_COORD3_EXT 0x87A0
+#define GL_OUTPUT_TEXTURE_COORD4_EXT 0x87A1
+#define GL_OUTPUT_TEXTURE_COORD5_EXT 0x87A2
+#define GL_OUTPUT_TEXTURE_COORD6_EXT 0x87A3
+#define GL_OUTPUT_TEXTURE_COORD7_EXT 0x87A4
+#define GL_OUTPUT_TEXTURE_COORD8_EXT 0x87A5
+#define GL_OUTPUT_TEXTURE_COORD9_EXT 0x87A6
+#define GL_OUTPUT_TEXTURE_COORD10_EXT 0x87A7
+#define GL_OUTPUT_TEXTURE_COORD11_EXT 0x87A8
+#define GL_OUTPUT_TEXTURE_COORD12_EXT 0x87A9
+#define GL_OUTPUT_TEXTURE_COORD13_EXT 0x87AA
+#define GL_OUTPUT_TEXTURE_COORD14_EXT 0x87AB
+#define GL_OUTPUT_TEXTURE_COORD15_EXT 0x87AC
+#define GL_OUTPUT_TEXTURE_COORD16_EXT 0x87AD
+#define GL_OUTPUT_TEXTURE_COORD17_EXT 0x87AE
+#define GL_OUTPUT_TEXTURE_COORD18_EXT 0x87AF
+#define GL_OUTPUT_TEXTURE_COORD19_EXT 0x87B0
+#define GL_OUTPUT_TEXTURE_COORD20_EXT 0x87B1
+#define GL_OUTPUT_TEXTURE_COORD21_EXT 0x87B2
+#define GL_OUTPUT_TEXTURE_COORD22_EXT 0x87B3
+#define GL_OUTPUT_TEXTURE_COORD23_EXT 0x87B4
+#define GL_OUTPUT_TEXTURE_COORD24_EXT 0x87B5
+#define GL_OUTPUT_TEXTURE_COORD25_EXT 0x87B6
+#define GL_OUTPUT_TEXTURE_COORD26_EXT 0x87B7
+#define GL_OUTPUT_TEXTURE_COORD27_EXT 0x87B8
+#define GL_OUTPUT_TEXTURE_COORD28_EXT 0x87B9
+#define GL_OUTPUT_TEXTURE_COORD29_EXT 0x87BA
+#define GL_OUTPUT_TEXTURE_COORD30_EXT 0x87BB
+#define GL_OUTPUT_TEXTURE_COORD31_EXT 0x87BC
+#define GL_OUTPUT_FOG_EXT 0x87BD
+#define GL_SCALAR_EXT 0x87BE
+#define GL_VECTOR_EXT 0x87BF
+#define GL_MATRIX_EXT 0x87C0
+#define GL_VARIANT_EXT 0x87C1
+#define GL_INVARIANT_EXT 0x87C2
+#define GL_LOCAL_CONSTANT_EXT 0x87C3
+#define GL_LOCAL_EXT 0x87C4
+#define GL_MAX_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87C5
+#define GL_MAX_VERTEX_SHADER_VARIANTS_EXT 0x87C6
+#define GL_MAX_VERTEX_SHADER_INVARIANTS_EXT 0x87C7
+#define GL_MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87C8
+#define GL_MAX_VERTEX_SHADER_LOCALS_EXT 0x87C9
+#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CA
+#define GL_MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT 0x87CB
+#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87CC
+#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT 0x87CD
+#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT 0x87CE
+#define GL_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CF
+#define GL_VERTEX_SHADER_VARIANTS_EXT 0x87D0
+#define GL_VERTEX_SHADER_INVARIANTS_EXT 0x87D1
+#define GL_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87D2
+#define GL_VERTEX_SHADER_LOCALS_EXT 0x87D3
+#define GL_VERTEX_SHADER_OPTIMIZED_EXT 0x87D4
+#define GL_X_EXT 0x87D5
+#define GL_Y_EXT 0x87D6
+#define GL_Z_EXT 0x87D7
+#define GL_W_EXT 0x87D8
+#define GL_NEGATIVE_X_EXT 0x87D9
+#define GL_NEGATIVE_Y_EXT 0x87DA
+#define GL_NEGATIVE_Z_EXT 0x87DB
+#define GL_NEGATIVE_W_EXT 0x87DC
+#define GL_ZERO_EXT 0x87DD
+#define GL_ONE_EXT 0x87DE
+#define GL_NEGATIVE_ONE_EXT 0x87DF
+#define GL_NORMALIZED_RANGE_EXT 0x87E0
+#define GL_FULL_RANGE_EXT 0x87E1
+#define GL_CURRENT_VERTEX_EXT 0x87E2
+#define GL_MVP_MATRIX_EXT 0x87E3
+#define GL_VARIANT_VALUE_EXT 0x87E4
+#define GL_VARIANT_DATATYPE_EXT 0x87E5
+#define GL_VARIANT_ARRAY_STRIDE_EXT 0x87E6
+#define GL_VARIANT_ARRAY_TYPE_EXT 0x87E7
+#define GL_VARIANT_ARRAY_EXT 0x87E8
+#define GL_VARIANT_ARRAY_POINTER_EXT 0x87E9
+#define GL_INVARIANT_VALUE_EXT 0x87EA
+#define GL_INVARIANT_DATATYPE_EXT 0x87EB
+#define GL_LOCAL_CONSTANT_VALUE_EXT 0x87EC
+#define GL_LOCAL_CONSTANT_DATATYPE_EXT 0x87ED
+#endif
+
+#ifndef GL_ATI_vertex_streams
+#define GL_MAX_VERTEX_STREAMS_ATI 0x876B
+#define GL_VERTEX_STREAM0_ATI 0x876C
+#define GL_VERTEX_STREAM1_ATI 0x876D
+#define GL_VERTEX_STREAM2_ATI 0x876E
+#define GL_VERTEX_STREAM3_ATI 0x876F
+#define GL_VERTEX_STREAM4_ATI 0x8770
+#define GL_VERTEX_STREAM5_ATI 0x8771
+#define GL_VERTEX_STREAM6_ATI 0x8772
+#define GL_VERTEX_STREAM7_ATI 0x8773
+#define GL_VERTEX_SOURCE_ATI 0x8774
+#endif
+
+#ifndef GL_ATI_element_array
+#define GL_ELEMENT_ARRAY_ATI 0x8768
+#define GL_ELEMENT_ARRAY_TYPE_ATI 0x8769
+#define GL_ELEMENT_ARRAY_POINTER_ATI 0x876A
+#endif
+
+#ifndef GL_SUN_mesh_array
+#define GL_QUAD_MESH_SUN 0x8614
+#define GL_TRIANGLE_MESH_SUN 0x8615
+#endif
+
+#ifndef GL_SUN_slice_accum
+#define GL_SLICE_ACCUM_SUN 0x85CC
+#endif
+
+#ifndef GL_NV_multisample_filter_hint
+#define GL_MULTISAMPLE_FILTER_HINT_NV 0x8534
+#endif
+
+#ifndef GL_NV_depth_clamp
+#define GL_DEPTH_CLAMP_NV 0x864F
+#endif
+
+#ifndef GL_NV_occlusion_query
+#define GL_PIXEL_COUNTER_BITS_NV 0x8864
+#define GL_CURRENT_OCCLUSION_QUERY_ID_NV 0x8865
+#define GL_PIXEL_COUNT_NV 0x8866
+#define GL_PIXEL_COUNT_AVAILABLE_NV 0x8867
+#endif
+
+#ifndef GL_NV_point_sprite
+#define GL_POINT_SPRITE_NV 0x8861
+#define GL_COORD_REPLACE_NV 0x8862
+#define GL_POINT_SPRITE_R_MODE_NV 0x8863
+#endif
+
+#ifndef GL_NV_texture_shader3
+#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_NV 0x8850
+#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_SCALE_NV 0x8851
+#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8852
+#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_SCALE_NV 0x8853
+#define GL_OFFSET_HILO_TEXTURE_2D_NV 0x8854
+#define GL_OFFSET_HILO_TEXTURE_RECTANGLE_NV 0x8855
+#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_2D_NV 0x8856
+#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8857
+#define GL_DEPENDENT_HILO_TEXTURE_2D_NV 0x8858
+#define GL_DEPENDENT_RGB_TEXTURE_3D_NV 0x8859
+#define GL_DEPENDENT_RGB_TEXTURE_CUBE_MAP_NV 0x885A
+#define GL_DOT_PRODUCT_PASS_THROUGH_NV 0x885B
+#define GL_DOT_PRODUCT_TEXTURE_1D_NV 0x885C
+#define GL_DOT_PRODUCT_AFFINE_DEPTH_REPLACE_NV 0x885D
+#define GL_HILO8_NV 0x885E
+#define GL_SIGNED_HILO8_NV 0x885F
+#define GL_FORCE_BLUE_TO_ONE_NV 0x8860
+#endif
+
+#ifndef GL_NV_vertex_program1_1
+#endif
+
+#ifndef GL_EXT_shadow_funcs
+#endif
+
+#ifndef GL_EXT_stencil_two_side
+#define GL_STENCIL_TEST_TWO_SIDE_EXT 0x8910
+#define GL_ACTIVE_STENCIL_FACE_EXT 0x8911
+#endif
+
+#ifndef GL_ATI_text_fragment_shader
+#define GL_TEXT_FRAGMENT_SHADER_ATI 0x8200
+#endif
+
+#ifndef GL_APPLE_client_storage
+#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2
+#endif
+
+/* The values of those in the official file and the extension spec are different. However, the values
+in the headers on OS X are those. So we prefer OS X version. */
+#ifndef GL_APPLE_element_array
+#define GL_ELEMENT_ARRAY_APPLE 0x8A0C
+#define GL_ELEMENT_ARRAY_TYPE_APPLE 0x8A0D
+#define GL_ELEMENT_ARRAY_POINTER_APPLE 0x8A0E
+#endif
+
+#ifndef GL_APPLE_fence
+#define GL_DRAW_PIXELS_APPLE 0x8A0A
+#define GL_FENCE_APPLE 0x8A0B
+#endif
+
+#ifndef GL_APPLE_vertex_array_object
+#define GL_VERTEX_ARRAY_BINDING_APPLE 0x85B5
+#endif
+
+#ifndef GL_APPLE_vertex_array_range
+#define GL_VERTEX_ARRAY_RANGE_APPLE 0x851D
+#define GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE 0x851E
+#define GL_VERTEX_ARRAY_STORAGE_HINT_APPLE 0x851F
+#define GL_VERTEX_ARRAY_RANGE_POINTER_APPLE 0x8521
+#define GL_STORAGE_CACHED_APPLE 0x85BE
+#define GL_STORAGE_SHARED_APPLE 0x85BF
+#endif
+
+#ifndef GL_APPLE_flush_buffer_range
+#define GL_BUFFER_FLUSHING_UNMAP_APPLE 0x8A13
+#endif
+
+#ifndef GL_APPLE_ycbcr_422
+#define GL_YCBCR_422_APPLE 0x85B9
+#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA
+#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB
+#endif
+
+#ifndef GL_S3_s3tc
+#define GL_RGB_S3TC 0x83A0
+#define GL_RGB4_S3TC 0x83A1
+#define GL_RGBA_S3TC 0x83A2
+#define GL_RGBA4_S3TC 0x83A3
+#endif
+
+#ifndef GL_ATI_draw_buffers
+#define GL_MAX_DRAW_BUFFERS_ATI 0x8824
+#define GL_DRAW_BUFFER0_ATI 0x8825
+#define GL_DRAW_BUFFER1_ATI 0x8826
+#define GL_DRAW_BUFFER2_ATI 0x8827
+#define GL_DRAW_BUFFER3_ATI 0x8828
+#define GL_DRAW_BUFFER4_ATI 0x8829
+#define GL_DRAW_BUFFER5_ATI 0x882A
+#define GL_DRAW_BUFFER6_ATI 0x882B
+#define GL_DRAW_BUFFER7_ATI 0x882C
+#define GL_DRAW_BUFFER8_ATI 0x882D
+#define GL_DRAW_BUFFER9_ATI 0x882E
+#define GL_DRAW_BUFFER10_ATI 0x882F
+#define GL_DRAW_BUFFER11_ATI 0x8830
+#define GL_DRAW_BUFFER12_ATI 0x8831
+#define GL_DRAW_BUFFER13_ATI 0x8832
+#define GL_DRAW_BUFFER14_ATI 0x8833
+#define GL_DRAW_BUFFER15_ATI 0x8834
+#endif
+
+#ifndef GL_ATI_pixel_format_float
+#define GL_TYPE_RGBA_FLOAT_ATI 0x8820
+#define GL_COLOR_CLEAR_UNCLAMPED_VALUE_ATI 0x8835
+#endif
+
+#ifndef GL_ATI_texture_env_combine3
+#define GL_MODULATE_ADD_ATI 0x8744
+#define GL_MODULATE_SIGNED_ADD_ATI 0x8745
+#define GL_MODULATE_SUBTRACT_ATI 0x8746
+#endif
+
+#ifndef GL_ATI_texture_float
+#define GL_RGBA_FLOAT32_ATI 0x8814
+#define GL_RGB_FLOAT32_ATI 0x8815
+#define GL_ALPHA_FLOAT32_ATI 0x8816
+#define GL_INTENSITY_FLOAT32_ATI 0x8817
+#define GL_LUMINANCE_FLOAT32_ATI 0x8818
+#define GL_LUMINANCE_ALPHA_FLOAT32_ATI 0x8819
+#define GL_RGBA_FLOAT16_ATI 0x881A
+#define GL_RGB_FLOAT16_ATI 0x881B
+#define GL_ALPHA_FLOAT16_ATI 0x881C
+#define GL_INTENSITY_FLOAT16_ATI 0x881D
+#define GL_LUMINANCE_FLOAT16_ATI 0x881E
+#define GL_LUMINANCE_ALPHA_FLOAT16_ATI 0x881F
+#endif
+
+#ifndef GL_NV_float_buffer
+#define GL_FLOAT_R_NV 0x8880
+#define GL_FLOAT_RG_NV 0x8881
+#define GL_FLOAT_RGB_NV 0x8882
+#define GL_FLOAT_RGBA_NV 0x8883
+#define GL_FLOAT_R16_NV 0x8884
+#define GL_FLOAT_R32_NV 0x8885
+#define GL_FLOAT_RG16_NV 0x8886
+#define GL_FLOAT_RG32_NV 0x8887
+#define GL_FLOAT_RGB16_NV 0x8888
+#define GL_FLOAT_RGB32_NV 0x8889
+#define GL_FLOAT_RGBA16_NV 0x888A
+#define GL_FLOAT_RGBA32_NV 0x888B
+#define GL_TEXTURE_FLOAT_COMPONENTS_NV 0x888C
+#define GL_FLOAT_CLEAR_COLOR_VALUE_NV 0x888D
+#define GL_FLOAT_RGBA_MODE_NV 0x888E
+#endif
+
+#ifndef GL_NV_fragment_program
+#define GL_MAX_FRAGMENT_PROGRAM_LOCAL_PARAMETERS_NV 0x8868
+#define GL_FRAGMENT_PROGRAM_NV 0x8870
+#define GL_MAX_TEXTURE_COORDS_NV 0x8871
+#define GL_MAX_TEXTURE_IMAGE_UNITS_NV 0x8872
+#define GL_FRAGMENT_PROGRAM_BINDING_NV 0x8873
+#define GL_PROGRAM_ERROR_STRING_NV 0x8874
+#endif
+
+#ifndef GL_NV_half_float
+#define GL_HALF_FLOAT_NV 0x140B
+#endif
+
+#ifndef GL_NV_pixel_data_range
+#define GL_WRITE_PIXEL_DATA_RANGE_NV 0x8878
+#define GL_READ_PIXEL_DATA_RANGE_NV 0x8879
+#define GL_WRITE_PIXEL_DATA_RANGE_LENGTH_NV 0x887A
+#define GL_READ_PIXEL_DATA_RANGE_LENGTH_NV 0x887B
+#define GL_WRITE_PIXEL_DATA_RANGE_POINTER_NV 0x887C
+#define GL_READ_PIXEL_DATA_RANGE_POINTER_NV 0x887D
+#endif
+
+#ifndef GL_NV_primitive_restart
+#define GL_PRIMITIVE_RESTART_NV 0x8558
+#define GL_PRIMITIVE_RESTART_INDEX_NV 0x8559
+#endif
+
+#ifndef GL_NV_texture_expand_normal
+#define GL_TEXTURE_UNSIGNED_REMAP_MODE_NV 0x888F
+#endif
+
+#ifndef GL_NV_vertex_program2
+#endif
+
+#ifndef GL_ATI_map_object_buffer
+#endif
+
+#ifndef GL_ATI_separate_stencil
+#define GL_STENCIL_BACK_FUNC_ATI 0x8800
+#define GL_STENCIL_BACK_FAIL_ATI 0x8801
+#define GL_STENCIL_BACK_PASS_DEPTH_FAIL_ATI 0x8802
+#define GL_STENCIL_BACK_PASS_DEPTH_PASS_ATI 0x8803
+#endif
+
+#ifndef GL_ATI_vertex_attrib_array_object
+#endif
+
+#ifndef GL_OES_read_format
+#define GL_IMPLEMENTATION_COLOR_READ_TYPE_OES 0x8B9A
+#define GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES 0x8B9B
+#endif
+
+#ifndef GL_EXT_depth_bounds_test
+#define GL_DEPTH_BOUNDS_TEST_EXT 0x8890
+#define GL_DEPTH_BOUNDS_EXT 0x8891
+#endif
+
+#ifndef GL_EXT_texture_mirror_clamp
+#define GL_MIRROR_CLAMP_EXT 0x8742
+#define GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743
+#define GL_MIRROR_CLAMP_TO_BORDER_EXT 0x8912
+#endif
+
+#ifndef GL_EXT_blend_equation_separate
+#define GL_BLEND_EQUATION_RGB_EXT GL_BLEND_EQUATION
+#define GL_BLEND_EQUATION_ALPHA_EXT 0x883D
+#endif
+
+#ifndef GL_MESA_pack_invert
+#define GL_PACK_INVERT_MESA 0x8758
+#endif
+
+#ifndef GL_MESA_ycbcr_texture
+#define GL_UNSIGNED_SHORT_8_8_MESA 0x85BA
+#define GL_UNSIGNED_SHORT_8_8_REV_MESA 0x85BB
+#define GL_YCBCR_MESA 0x8757
+#endif
+
+#ifndef GL_EXT_pixel_buffer_object
+#define GL_PIXEL_PACK_BUFFER_EXT 0x88EB
+#define GL_PIXEL_UNPACK_BUFFER_EXT 0x88EC
+#define GL_PIXEL_PACK_BUFFER_BINDING_EXT 0x88ED
+#define GL_PIXEL_UNPACK_BUFFER_BINDING_EXT 0x88EF
+#endif
+
+#ifndef GL_NV_fragment_program_option
+#endif
+
+#ifndef GL_NV_fragment_program2
+#define GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV 0x88F4
+#define GL_MAX_PROGRAM_CALL_DEPTH_NV 0x88F5
+#define GL_MAX_PROGRAM_IF_DEPTH_NV 0x88F6
+#define GL_MAX_PROGRAM_LOOP_DEPTH_NV 0x88F7
+#define GL_MAX_PROGRAM_LOOP_COUNT_NV 0x88F8
+#endif
+
+#ifndef GL_NV_vertex_program2_option
+/* reuse GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV */
+/* reuse GL_MAX_PROGRAM_CALL_DEPTH_NV */
+#endif
+
+#ifndef GL_NV_vertex_program3
+/* reuse GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB */
+#endif
+
+#ifndef GL_EXT_framebuffer_object
+#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506
+#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8
+#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6
+#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7
+#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0
+#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4
+#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5
+#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6
+#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7
+#define GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT 0x8CD8
+#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9
+#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA
+#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB
+#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC
+#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD
+#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF
+#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0
+#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1
+#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2
+#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3
+#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4
+#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5
+#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6
+#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7
+#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8
+#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9
+#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA
+#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB
+#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC
+#define GL_COLOR_ATTACHMENT13_EXT 0x8CED
+#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE
+#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF
+#define GL_DEPTH_ATTACHMENT_EXT 0x8D00
+#define GL_STENCIL_ATTACHMENT_EXT 0x8D20
+#define GL_FRAMEBUFFER_EXT 0x8D40
+#define GL_RENDERBUFFER_EXT 0x8D41
+#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42
+#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43
+#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44
+#define GL_STENCIL_INDEX1_EXT 0x8D46
+#define GL_STENCIL_INDEX4_EXT 0x8D47
+#define GL_STENCIL_INDEX8_EXT 0x8D48
+#define GL_STENCIL_INDEX16_EXT 0x8D49
+#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50
+#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51
+#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52
+#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53
+#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54
+#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55
+#define GL_MAX_SAMPLES_EXT 0x8D57
+#endif
+
+#ifndef GL_EXT_framebuffer_blit
+#define GL_READ_FRAMEBUFFER_EXT 0x8CA8
+#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9
+#define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6
+#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA
+#endif
+
+#ifndef GL_EXT_packed_depth_stencil
+#define GL_DEPTH_STENCIL_EXT 0x84F9
+#define GL_UNSIGNED_INT_24_8_EXT 0x84FA
+#define GL_DEPTH24_STENCIL8_EXT 0x88F0
+#define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1
+#endif
+
+#ifndef GL_EXT_texture_sRGB
+#define GL_SRGB_EXT 0x8C40
+#define GL_SRGB8_EXT 0x8C41
+#define GL_SRGB_ALPHA_EXT 0x8C42
+#define GL_SRGB8_ALPHA8_EXT 0x8C43
+#define GL_SLUMINANCE_ALPHA_EXT 0x8C44
+#define GL_SLUMINANCE8_ALPHA8_EXT 0x8C45
+#define GL_SLUMINANCE_EXT 0x8C46
+#define GL_SLUMINANCE8_EXT 0x8C47
+#define GL_COMPRESSED_SRGB_EXT 0x8C48
+#define GL_COMPRESSED_SRGB_ALPHA_EXT 0x8C49
+#define GL_COMPRESSED_SLUMINANCE_EXT 0x8C4A
+#define GL_COMPRESSED_SLUMINANCE_ALPHA_EXT 0x8C4B
+#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C
+#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D
+#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E
+#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F
+#endif
+
+#ifndef GL_EXT_framebuffer_sRGB
+#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9
+#define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA
+#endif
+
+
+/*************************************************************/
+
+#include <stddef.h>
+#ifndef GL_VERSION_2_0
+/* GL type for program/shader text */
+typedef char GLchar; /* native character */
+#endif
+
+#ifndef GL_VERSION_1_5
+/* GL types for handling large vertex buffer objects */
+typedef ptrdiff_t GLintptr;
+typedef ptrdiff_t GLsizeiptr;
+#endif
+
+#ifndef GL_ARB_vertex_buffer_object
+/* GL types for handling large vertex buffer objects */
+typedef ptrdiff_t GLintptrARB;
+typedef ptrdiff_t GLsizeiptrARB;
+#endif
+
+#ifndef GL_ARB_shader_objects
+/* GL types for handling shader object handles and program/shader text */
+typedef char GLcharARB; /* native character */
+typedef unsigned int GLhandleARB; /* shader object handle */
+#endif
+
+/* GL types for "half" precision (s10e5) float data in host memory */
+#ifndef GL_ARB_half_float_pixel
+typedef unsigned short GLhalfARB;
+#endif
+
+#ifndef GL_NV_half_float
+typedef unsigned short GLhalfNV;
+#endif
+
+#ifndef GLEXT_64_TYPES_DEFINED
+/* This code block is duplicated in glxext.h, so must be protected */
+#define GLEXT_64_TYPES_DEFINED
+/* Define int32_t, int64_t, and uint64_t types for UST/MSC */
+/* (as used in the GL_EXT_timer_query extension). */
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#include <inttypes.h>
+#elif defined(__sun__) || defined(__digital__)
+#include <inttypes.h>
+#if defined(__STDC__)
+#if defined(__arch64__) || defined(_LP64)
+typedef long int int64_t;
+typedef unsigned long int uint64_t;
+#else
+typedef long long int int64_t;
+typedef unsigned long long int uint64_t;
+#endif /* __arch64__ */
+#endif /* __STDC__ */
+#elif defined( __VMS ) || defined(__sgi)
+#include <inttypes.h>
+#elif defined(__SCO__) || defined(__USLC__)
+#include <stdint.h>
+#elif defined(__UNIXOS2__) || defined(__SOL64__)
+typedef long int int32_t;
+typedef long long int int64_t;
+typedef unsigned long long int uint64_t;
+#elif defined(_WIN32) && defined(__GNUC__)
+#include <stdint.h>
+#elif defined(_WIN32)
+typedef __int32 int32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#else
+/* Fallback if nothing above works */
+#include <inttypes.h>
+#endif
+#endif
+
+#ifndef GL_EXT_timer_query
+typedef int64_t GLint64EXT;
+typedef uint64_t GLuint64EXT;
+#endif
+
+#if !(defined GL_ARB_sync || defined GL_VERSION_3_0)
+typedef int64_t GLint64;
+typedef uint64_t GLuint64;
+typedef struct __GLsync *GLsync;
+#endif
+
+#ifndef GL_VERSION_1_2
+#define GL_VERSION_1_2 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glBlendColor (GLclampf, GLclampf, GLclampf, GLclampf);
+GLAPI void APIENTRY glBlendEquation (GLenum);
+GLAPI void APIENTRY glDrawRangeElements (GLenum, GLuint, GLuint, GLsizei, GLenum, const GLvoid *);
+GLAPI void APIENTRY glColorTable (GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid *);
+GLAPI void APIENTRY glColorTableParameterfv (GLenum, GLenum, const GLfloat *);
+GLAPI void APIENTRY glColorTableParameteriv (GLenum, GLenum, const GLint *);
+GLAPI void APIENTRY glCopyColorTable (GLenum, GLenum, GLint, GLint, GLsizei);
+GLAPI void APIENTRY glGetColorTable (GLenum, GLenum, GLenum, GLvoid *);
+GLAPI void APIENTRY glGetColorTableParameterfv (GLenum, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetColorTableParameteriv (GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glColorSubTable (GLenum, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *);
+GLAPI void APIENTRY glCopyColorSubTable (GLenum, GLsizei, GLint, GLint, GLsizei);
+GLAPI void APIENTRY glConvolutionFilter1D (GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid *);
+GLAPI void APIENTRY glConvolutionFilter2D (GLenum, GLenum, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *);
+GLAPI void APIENTRY glConvolutionParameterf (GLenum, GLenum, GLfloat);
+GLAPI void APIENTRY glConvolutionParameterfv (GLenum, GLenum, const GLfloat *);
+GLAPI void APIENTRY glConvolutionParameteri (GLenum, GLenum, GLint);
+GLAPI void APIENTRY glConvolutionParameteriv (GLenum, GLenum, const GLint *);
+GLAPI void APIENTRY glCopyConvolutionFilter1D (GLenum, GLenum, GLint, GLint, GLsizei);
+GLAPI void APIENTRY glCopyConvolutionFilter2D (GLenum, GLenum, GLint, GLint, GLsizei, GLsizei);
+GLAPI void APIENTRY glGetConvolutionFilter (GLenum, GLenum, GLenum, GLvoid *);
+GLAPI void APIENTRY glGetConvolutionParameterfv (GLenum, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetConvolutionParameteriv (GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glGetSeparableFilter (GLenum, GLenum, GLenum, GLvoid *, GLvoid *, GLvoid *);
+GLAPI void APIENTRY glSeparableFilter2D (GLenum, GLenum, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *, const GLvoid *);
+GLAPI void APIENTRY glGetHistogram (GLenum, GLboolean, GLenum, GLenum, GLvoid *);
+GLAPI void APIENTRY glGetHistogramParameterfv (GLenum, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetHistogramParameteriv (GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glGetMinmax (GLenum, GLboolean, GLenum, GLenum, GLvoid *);
+GLAPI void APIENTRY glGetMinmaxParameterfv (GLenum, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetMinmaxParameteriv (GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glHistogram (GLenum, GLsizei, GLenum, GLboolean);
+GLAPI void APIENTRY glMinmax (GLenum, GLenum, GLboolean);
+GLAPI void APIENTRY glResetHistogram (GLenum);
+GLAPI void APIENTRY glResetMinmax (GLenum);
+GLAPI void APIENTRY glTexImage3D (GLenum, GLint, GLint, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid *);
+GLAPI void APIENTRY glTexSubImage3D (GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *);
+GLAPI void APIENTRY glCopyTexSubImage3D (GLenum, GLint, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
+typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode);
+typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices);
+typedef void (APIENTRYP PFNGLCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table);
+typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params);
+typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params);
+typedef void (APIENTRYP PFNGLCOPYCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width);
+typedef void (APIENTRYP PFNGLGETCOLORTABLEPROC) (GLenum target, GLenum format, GLenum type, GLvoid *table);
+typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const GLvoid *data);
+typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width);
+typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *image);
+typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *image);
+typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat params);
+typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params);
+typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIPROC) (GLenum target, GLenum pname, GLint params);
+typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params);
+typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width);
+typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height);
+typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTERPROC) (GLenum target, GLenum format, GLenum type, GLvoid *image);
+typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETSEPARABLEFILTERPROC) (GLenum target, GLenum format, GLenum type, GLvoid *row, GLvoid *column, GLvoid *span);
+typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *row, const GLvoid *column);
+typedef void (APIENTRYP PFNGLGETHISTOGRAMPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values);
+typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETMINMAXPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values);
+typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLHISTOGRAMPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink);
+typedef void (APIENTRYP PFNGLMINMAXPROC) (GLenum target, GLenum internalformat, GLboolean sink);
+typedef void (APIENTRYP PFNGLRESETHISTOGRAMPROC) (GLenum target);
+typedef void (APIENTRYP PFNGLRESETMINMAXPROC) (GLenum target);
+typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
+typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels);
+typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);
+#endif
+
+#ifndef GL_VERSION_1_3
+#define GL_VERSION_1_3 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glActiveTexture (GLenum);
+GLAPI void APIENTRY glClientActiveTexture (GLenum);
+GLAPI void APIENTRY glMultiTexCoord1d (GLenum, GLdouble);
+GLAPI void APIENTRY glMultiTexCoord1dv (GLenum, const GLdouble *);
+GLAPI void APIENTRY glMultiTexCoord1f (GLenum, GLfloat);
+GLAPI void APIENTRY glMultiTexCoord1fv (GLenum, const GLfloat *);
+GLAPI void APIENTRY glMultiTexCoord1i (GLenum, GLint);
+GLAPI void APIENTRY glMultiTexCoord1iv (GLenum, const GLint *);
+GLAPI void APIENTRY glMultiTexCoord1s (GLenum, GLshort);
+GLAPI void APIENTRY glMultiTexCoord1sv (GLenum, const GLshort *);
+GLAPI void APIENTRY glMultiTexCoord2d (GLenum, GLdouble, GLdouble);
+GLAPI void APIENTRY glMultiTexCoord2dv (GLenum, const GLdouble *);
+GLAPI void APIENTRY glMultiTexCoord2f (GLenum, GLfloat, GLfloat);
+GLAPI void APIENTRY glMultiTexCoord2fv (GLenum, const GLfloat *);
+GLAPI void APIENTRY glMultiTexCoord2i (GLenum, GLint, GLint);
+GLAPI void APIENTRY glMultiTexCoord2iv (GLenum, const GLint *);
+GLAPI void APIENTRY glMultiTexCoord2s (GLenum, GLshort, GLshort);
+GLAPI void APIENTRY glMultiTexCoord2sv (GLenum, const GLshort *);
+GLAPI void APIENTRY glMultiTexCoord3d (GLenum, GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glMultiTexCoord3dv (GLenum, const GLdouble *);
+GLAPI void APIENTRY glMultiTexCoord3f (GLenum, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glMultiTexCoord3fv (GLenum, const GLfloat *);
+GLAPI void APIENTRY glMultiTexCoord3i (GLenum, GLint, GLint, GLint);
+GLAPI void APIENTRY glMultiTexCoord3iv (GLenum, const GLint *);
+GLAPI void APIENTRY glMultiTexCoord3s (GLenum, GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glMultiTexCoord3sv (GLenum, const GLshort *);
+GLAPI void APIENTRY glMultiTexCoord4d (GLenum, GLdouble, GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glMultiTexCoord4dv (GLenum, const GLdouble *);
+GLAPI void APIENTRY glMultiTexCoord4f (GLenum, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glMultiTexCoord4fv (GLenum, const GLfloat *);
+GLAPI void APIENTRY glMultiTexCoord4i (GLenum, GLint, GLint, GLint, GLint);
+GLAPI void APIENTRY glMultiTexCoord4iv (GLenum, const GLint *);
+GLAPI void APIENTRY glMultiTexCoord4s (GLenum, GLshort, GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glMultiTexCoord4sv (GLenum, const GLshort *);
+GLAPI void APIENTRY glLoadTransposeMatrixf (const GLfloat *);
+GLAPI void APIENTRY glLoadTransposeMatrixd (const GLdouble *);
+GLAPI void APIENTRY glMultTransposeMatrixf (const GLfloat *);
+GLAPI void APIENTRY glMultTransposeMatrixd (const GLdouble *);
+GLAPI void APIENTRY glSampleCoverage (GLclampf, GLboolean);
+GLAPI void APIENTRY glCompressedTexImage3D (GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glCompressedTexImage2D (GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glCompressedTexImage1D (GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glCompressedTexSubImage3D (GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glCompressedTexSubImage2D (GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glCompressedTexSubImage1D (GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glGetCompressedTexImage (GLenum, GLint, GLvoid *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture);
+typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREPROC) (GLenum texture);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1DPROC) (GLenum target, GLdouble s);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVPROC) (GLenum target, const GLdouble *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1FPROC) (GLenum target, GLfloat s);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVPROC) (GLenum target, const GLfloat *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1IPROC) (GLenum target, GLint s);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVPROC) (GLenum target, const GLint *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1SPROC) (GLenum target, GLshort s);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVPROC) (GLenum target, const GLshort *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2DPROC) (GLenum target, GLdouble s, GLdouble t);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVPROC) (GLenum target, const GLdouble *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2FPROC) (GLenum target, GLfloat s, GLfloat t);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVPROC) (GLenum target, const GLfloat *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2IPROC) (GLenum target, GLint s, GLint t);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVPROC) (GLenum target, const GLint *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2SPROC) (GLenum target, GLshort s, GLshort t);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVPROC) (GLenum target, const GLshort *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3DPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVPROC) (GLenum target, const GLdouble *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3FPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVPROC) (GLenum target, const GLfloat *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3IPROC) (GLenum target, GLint s, GLint t, GLint r);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVPROC) (GLenum target, const GLint *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3SPROC) (GLenum target, GLshort s, GLshort t, GLshort r);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVPROC) (GLenum target, const GLshort *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4DPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVPROC) (GLenum target, const GLdouble *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4FPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVPROC) (GLenum target, const GLfloat *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4IPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVPROC) (GLenum target, const GLint *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4SPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVPROC) (GLenum target, const GLshort *v);
+typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFPROC) (const GLfloat *m);
+typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDPROC) (const GLdouble *m);
+typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFPROC) (const GLfloat *m);
+typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDPROC) (const GLdouble *m);
+typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLclampf value, GLboolean invert);
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data);
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data);
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *data);
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data);
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data);
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data);
+typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, GLvoid *img);
+#endif
+
+#ifndef GL_VERSION_1_4
+#define GL_VERSION_1_4 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glBlendFuncSeparate (GLenum, GLenum, GLenum, GLenum);
+GLAPI void APIENTRY glFogCoordf (GLfloat);
+GLAPI void APIENTRY glFogCoordfv (const GLfloat *);
+GLAPI void APIENTRY glFogCoordd (GLdouble);
+GLAPI void APIENTRY glFogCoorddv (const GLdouble *);
+GLAPI void APIENTRY glFogCoordPointer (GLenum, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glMultiDrawArrays (GLenum, GLint *, GLsizei *, GLsizei);
+GLAPI void APIENTRY glMultiDrawElements (GLenum, const GLsizei *, GLenum, const GLvoid* *, GLsizei);
+GLAPI void APIENTRY glPointParameterf (GLenum, GLfloat);
+GLAPI void APIENTRY glPointParameterfv (GLenum, const GLfloat *);
+GLAPI void APIENTRY glPointParameteri (GLenum, GLint);
+GLAPI void APIENTRY glPointParameteriv (GLenum, const GLint *);
+GLAPI void APIENTRY glSecondaryColor3b (GLbyte, GLbyte, GLbyte);
+GLAPI void APIENTRY glSecondaryColor3bv (const GLbyte *);
+GLAPI void APIENTRY glSecondaryColor3d (GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glSecondaryColor3dv (const GLdouble *);
+GLAPI void APIENTRY glSecondaryColor3f (GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glSecondaryColor3fv (const GLfloat *);
+GLAPI void APIENTRY glSecondaryColor3i (GLint, GLint, GLint);
+GLAPI void APIENTRY glSecondaryColor3iv (const GLint *);
+GLAPI void APIENTRY glSecondaryColor3s (GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glSecondaryColor3sv (const GLshort *);
+GLAPI void APIENTRY glSecondaryColor3ub (GLubyte, GLubyte, GLubyte);
+GLAPI void APIENTRY glSecondaryColor3ubv (const GLubyte *);
+GLAPI void APIENTRY glSecondaryColor3ui (GLuint, GLuint, GLuint);
+GLAPI void APIENTRY glSecondaryColor3uiv (const GLuint *);
+GLAPI void APIENTRY glSecondaryColor3us (GLushort, GLushort, GLushort);
+GLAPI void APIENTRY glSecondaryColor3usv (const GLushort *);
+GLAPI void APIENTRY glSecondaryColorPointer (GLint, GLenum, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glWindowPos2d (GLdouble, GLdouble);
+GLAPI void APIENTRY glWindowPos2dv (const GLdouble *);
+GLAPI void APIENTRY glWindowPos2f (GLfloat, GLfloat);
+GLAPI void APIENTRY glWindowPos2fv (const GLfloat *);
+GLAPI void APIENTRY glWindowPos2i (GLint, GLint);
+GLAPI void APIENTRY glWindowPos2iv (const GLint *);
+GLAPI void APIENTRY glWindowPos2s (GLshort, GLshort);
+GLAPI void APIENTRY glWindowPos2sv (const GLshort *);
+GLAPI void APIENTRY glWindowPos3d (GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glWindowPos3dv (const GLdouble *);
+GLAPI void APIENTRY glWindowPos3f (GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glWindowPos3fv (const GLfloat *);
+GLAPI void APIENTRY glWindowPos3i (GLint, GLint, GLint);
+GLAPI void APIENTRY glWindowPos3iv (const GLint *);
+GLAPI void APIENTRY glWindowPos3s (GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glWindowPos3sv (const GLshort *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
+typedef void (APIENTRYP PFNGLFOGCOORDFPROC) (GLfloat coord);
+typedef void (APIENTRYP PFNGLFOGCOORDFVPROC) (const GLfloat *coord);
+typedef void (APIENTRYP PFNGLFOGCOORDDPROC) (GLdouble coord);
+typedef void (APIENTRYP PFNGLFOGCOORDDVPROC) (const GLdouble *coord);
+typedef void (APIENTRYP PFNGLFOGCOORDPOINTERPROC) (GLenum type, GLsizei stride, const GLvoid *pointer);
+typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount);
+typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount);
+typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param);
+typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params);
+typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC) (GLenum pname, GLint param);
+typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC) (GLenum pname, const GLint *params);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BPROC) (GLbyte red, GLbyte green, GLbyte blue);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVPROC) (const GLbyte *v);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DPROC) (GLdouble red, GLdouble green, GLdouble blue);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVPROC) (const GLdouble *v);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FPROC) (GLfloat red, GLfloat green, GLfloat blue);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVPROC) (const GLfloat *v);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IPROC) (GLint red, GLint green, GLint blue);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVPROC) (const GLint *v);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SPROC) (GLshort red, GLshort green, GLshort blue);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVPROC) (const GLshort *v);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBPROC) (GLubyte red, GLubyte green, GLubyte blue);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVPROC) (const GLubyte *v);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIPROC) (GLuint red, GLuint green, GLuint blue);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVPROC) (const GLuint *v);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USPROC) (GLushort red, GLushort green, GLushort blue);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVPROC) (const GLushort *v);
+typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
+typedef void (APIENTRYP PFNGLWINDOWPOS2DPROC) (GLdouble x, GLdouble y);
+typedef void (APIENTRYP PFNGLWINDOWPOS2DVPROC) (const GLdouble *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS2FPROC) (GLfloat x, GLfloat y);
+typedef void (APIENTRYP PFNGLWINDOWPOS2FVPROC) (const GLfloat *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS2IPROC) (GLint x, GLint y);
+typedef void (APIENTRYP PFNGLWINDOWPOS2IVPROC) (const GLint *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS2SPROC) (GLshort x, GLshort y);
+typedef void (APIENTRYP PFNGLWINDOWPOS2SVPROC) (const GLshort *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS3DPROC) (GLdouble x, GLdouble y, GLdouble z);
+typedef void (APIENTRYP PFNGLWINDOWPOS3DVPROC) (const GLdouble *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS3FPROC) (GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLWINDOWPOS3FVPROC) (const GLfloat *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS3IPROC) (GLint x, GLint y, GLint z);
+typedef void (APIENTRYP PFNGLWINDOWPOS3IVPROC) (const GLint *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS3SPROC) (GLshort x, GLshort y, GLshort z);
+typedef void (APIENTRYP PFNGLWINDOWPOS3SVPROC) (const GLshort *v);
+#endif
+
+#ifndef GL_VERSION_1_5
+#define GL_VERSION_1_5 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glGenQueries (GLsizei, GLuint *);
+GLAPI void APIENTRY glDeleteQueries (GLsizei, const GLuint *);
+GLAPI GLboolean APIENTRY glIsQuery (GLuint);
+GLAPI void APIENTRY glBeginQuery (GLenum, GLuint);
+GLAPI void APIENTRY glEndQuery (GLenum);
+GLAPI void APIENTRY glGetQueryiv (GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glGetQueryObjectiv (GLuint, GLenum, GLint *);
+GLAPI void APIENTRY glGetQueryObjectuiv (GLuint, GLenum, GLuint *);
+GLAPI void APIENTRY glBindBuffer (GLenum, GLuint);
+GLAPI void APIENTRY glDeleteBuffers (GLsizei, const GLuint *);
+GLAPI void APIENTRY glGenBuffers (GLsizei, GLuint *);
+GLAPI GLboolean APIENTRY glIsBuffer (GLuint);
+GLAPI void APIENTRY glBufferData (GLenum, GLsizeiptr, const GLvoid *, GLenum);
+GLAPI void APIENTRY glBufferSubData (GLenum, GLintptr, GLsizeiptr, const GLvoid *);
+GLAPI void APIENTRY glGetBufferSubData (GLenum, GLintptr, GLsizeiptr, GLvoid *);
+GLAPI GLvoid* APIENTRY glMapBuffer (GLenum, GLenum);
+GLAPI GLboolean APIENTRY glUnmapBuffer (GLenum);
+GLAPI void APIENTRY glGetBufferParameteriv (GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glGetBufferPointerv (GLenum, GLenum, GLvoid* *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids);
+typedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids);
+typedef GLboolean (APIENTRYP PFNGLISQUERYPROC) (GLuint id);
+typedef void (APIENTRYP PFNGLBEGINQUERYPROC) (GLenum target, GLuint id);
+typedef void (APIENTRYP PFNGLENDQUERYPROC) (GLenum target);
+typedef void (APIENTRYP PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC) (GLuint id, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint *params);
+typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);
+typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
+typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);
+typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer);
+typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage);
+typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data);
+typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data);
+typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access);
+typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target);
+typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC) (GLenum target, GLenum pname, GLvoid* *params);
+#endif
+
+#ifndef GL_VERSION_2_0
+#define GL_VERSION_2_0 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glBlendEquationSeparate (GLenum, GLenum);
+GLAPI void APIENTRY glDrawBuffers (GLsizei, const GLenum *);
+GLAPI void APIENTRY glStencilOpSeparate (GLenum, GLenum, GLenum, GLenum);
+GLAPI void APIENTRY glStencilFuncSeparate (GLenum, GLenum, GLint, GLuint);
+GLAPI void APIENTRY glStencilMaskSeparate (GLenum, GLuint);
+GLAPI void APIENTRY glAttachShader (GLuint, GLuint);
+GLAPI void APIENTRY glBindAttribLocation (GLuint, GLuint, const GLchar *);
+GLAPI void APIENTRY glCompileShader (GLuint);
+GLAPI GLuint APIENTRY glCreateProgram (void);
+GLAPI GLuint APIENTRY glCreateShader (GLenum);
+GLAPI void APIENTRY glDeleteProgram (GLuint);
+GLAPI void APIENTRY glDeleteShader (GLuint);
+GLAPI void APIENTRY glDetachShader (GLuint, GLuint);
+GLAPI void APIENTRY glDisableVertexAttribArray (GLuint);
+GLAPI void APIENTRY glEnableVertexAttribArray (GLuint);
+GLAPI void APIENTRY glGetActiveAttrib (GLuint, GLuint, GLsizei, GLsizei *, GLint *, GLenum *, GLchar *);
+GLAPI void APIENTRY glGetActiveUniform (GLuint, GLuint, GLsizei, GLsizei *, GLint *, GLenum *, GLchar *);
+GLAPI void APIENTRY glGetAttachedShaders (GLuint, GLsizei, GLsizei *, GLuint *);
+GLAPI GLint APIENTRY glGetAttribLocation (GLuint, const GLchar *);
+GLAPI void APIENTRY glGetProgramiv (GLuint, GLenum, GLint *);
+GLAPI void APIENTRY glGetProgramInfoLog (GLuint, GLsizei, GLsizei *, GLchar *);
+GLAPI void APIENTRY glGetShaderiv (GLuint, GLenum, GLint *);
+GLAPI void APIENTRY glGetShaderInfoLog (GLuint, GLsizei, GLsizei *, GLchar *);
+GLAPI void APIENTRY glGetShaderSource (GLuint, GLsizei, GLsizei *, GLchar *);
+GLAPI GLint APIENTRY glGetUniformLocation (GLuint, const GLchar *);
+GLAPI void APIENTRY glGetUniformfv (GLuint, GLint, GLfloat *);
+GLAPI void APIENTRY glGetUniformiv (GLuint, GLint, GLint *);
+GLAPI void APIENTRY glGetVertexAttribdv (GLuint, GLenum, GLdouble *);
+GLAPI void APIENTRY glGetVertexAttribfv (GLuint, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetVertexAttribiv (GLuint, GLenum, GLint *);
+GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint, GLenum, GLvoid* *);
+GLAPI GLboolean APIENTRY glIsProgram (GLuint);
+GLAPI GLboolean APIENTRY glIsShader (GLuint);
+GLAPI void APIENTRY glLinkProgram (GLuint);
+GLAPI void APIENTRY glShaderSource (GLuint, GLsizei, const GLchar* *, const GLint *);
+GLAPI void APIENTRY glUseProgram (GLuint);
+GLAPI void APIENTRY glUniform1f (GLint, GLfloat);
+GLAPI void APIENTRY glUniform2f (GLint, GLfloat, GLfloat);
+GLAPI void APIENTRY glUniform3f (GLint, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glUniform4f (GLint, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glUniform1i (GLint, GLint);
+GLAPI void APIENTRY glUniform2i (GLint, GLint, GLint);
+GLAPI void APIENTRY glUniform3i (GLint, GLint, GLint, GLint);
+GLAPI void APIENTRY glUniform4i (GLint, GLint, GLint, GLint, GLint);
+GLAPI void APIENTRY glUniform1fv (GLint, GLsizei, const GLfloat *);
+GLAPI void APIENTRY glUniform2fv (GLint, GLsizei, const GLfloat *);
+GLAPI void APIENTRY glUniform3fv (GLint, GLsizei, const GLfloat *);
+GLAPI void APIENTRY glUniform4fv (GLint, GLsizei, const GLfloat *);
+GLAPI void APIENTRY glUniform1iv (GLint, GLsizei, const GLint *);
+GLAPI void APIENTRY glUniform2iv (GLint, GLsizei, const GLint *);
+GLAPI void APIENTRY glUniform3iv (GLint, GLsizei, const GLint *);
+GLAPI void APIENTRY glUniform4iv (GLint, GLsizei, const GLint *);
+GLAPI void APIENTRY glUniformMatrix2fv (GLint, GLsizei, GLboolean, const GLfloat *);
+GLAPI void APIENTRY glUniformMatrix3fv (GLint, GLsizei, GLboolean, const GLfloat *);
+GLAPI void APIENTRY glUniformMatrix4fv (GLint, GLsizei, GLboolean, const GLfloat *);
+GLAPI void APIENTRY glValidateProgram (GLuint);
+GLAPI void APIENTRY glVertexAttrib1d (GLuint, GLdouble);
+GLAPI void APIENTRY glVertexAttrib1dv (GLuint, const GLdouble *);
+GLAPI void APIENTRY glVertexAttrib1f (GLuint, GLfloat);
+GLAPI void APIENTRY glVertexAttrib1fv (GLuint, const GLfloat *);
+GLAPI void APIENTRY glVertexAttrib1s (GLuint, GLshort);
+GLAPI void APIENTRY glVertexAttrib1sv (GLuint, const GLshort *);
+GLAPI void APIENTRY glVertexAttrib2d (GLuint, GLdouble, GLdouble);
+GLAPI void APIENTRY glVertexAttrib2dv (GLuint, const GLdouble *);
+GLAPI void APIENTRY glVertexAttrib2f (GLuint, GLfloat, GLfloat);
+GLAPI void APIENTRY glVertexAttrib2fv (GLuint, const GLfloat *);
+GLAPI void APIENTRY glVertexAttrib2s (GLuint, GLshort, GLshort);
+GLAPI void APIENTRY glVertexAttrib2sv (GLuint, const GLshort *);
+GLAPI void APIENTRY glVertexAttrib3d (GLuint, GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glVertexAttrib3dv (GLuint, const GLdouble *);
+GLAPI void APIENTRY glVertexAttrib3f (GLuint, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glVertexAttrib3fv (GLuint, const GLfloat *);
+GLAPI void APIENTRY glVertexAttrib3s (GLuint, GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glVertexAttrib3sv (GLuint, const GLshort *);
+GLAPI void APIENTRY glVertexAttrib4Nbv (GLuint, const GLbyte *);
+GLAPI void APIENTRY glVertexAttrib4Niv (GLuint, const GLint *);
+GLAPI void APIENTRY glVertexAttrib4Nsv (GLuint, const GLshort *);
+GLAPI void APIENTRY glVertexAttrib4Nub (GLuint, GLubyte, GLubyte, GLubyte, GLubyte);
+GLAPI void APIENTRY glVertexAttrib4Nubv (GLuint, const GLubyte *);
+GLAPI void APIENTRY glVertexAttrib4Nuiv (GLuint, const GLuint *);
+GLAPI void APIENTRY glVertexAttrib4Nusv (GLuint, const GLushort *);
+GLAPI void APIENTRY glVertexAttrib4bv (GLuint, const GLbyte *);
+GLAPI void APIENTRY glVertexAttrib4d (GLuint, GLdouble, GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glVertexAttrib4dv (GLuint, const GLdouble *);
+GLAPI void APIENTRY glVertexAttrib4f (GLuint, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glVertexAttrib4fv (GLuint, const GLfloat *);
+GLAPI void APIENTRY glVertexAttrib4iv (GLuint, const GLint *);
+GLAPI void APIENTRY glVertexAttrib4s (GLuint, GLshort, GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glVertexAttrib4sv (GLuint, const GLshort *);
+GLAPI void APIENTRY glVertexAttrib4ubv (GLuint, const GLubyte *);
+GLAPI void APIENTRY glVertexAttrib4uiv (GLuint, const GLuint *);
+GLAPI void APIENTRY glVertexAttrib4usv (GLuint, const GLushort *);
+GLAPI void APIENTRY glVertexAttribPointer (GLuint, GLint, GLenum, GLboolean, GLsizei, const GLvoid *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha);
+typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC) (GLsizei n, const GLenum *bufs);
+typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);
+typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC) (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask);
+typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC) (GLenum face, GLuint mask);
+typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader);
+typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar *name);
+typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader);
+typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void);
+typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type);
+typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program);
+typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader);
+typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader);
+typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index);
+typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index);
+typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name);
+typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name);
+typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *obj);
+typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name);
+typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
+typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
+typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source);
+typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name);
+typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint *params);
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC) (GLuint index, GLenum pname, GLdouble *params);
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC) (GLuint index, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, GLvoid* *pointer);
+typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program);
+typedef GLboolean (APIENTRYP PFNGLISSHADERPROC) (GLuint shader);
+typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program);
+typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar* *string, const GLint *length);
+typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program);
+typedef void (APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0);
+typedef void (APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1);
+typedef void (APIENTRYP PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
+typedef void (APIENTRYP PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
+typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0);
+typedef void (APIENTRYP PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1);
+typedef void (APIENTRYP PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2);
+typedef void (APIENTRYP PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3);
+typedef void (APIENTRYP PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat *value);
+typedef void (APIENTRYP PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat *value);
+typedef void (APIENTRYP PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat *value);
+typedef void (APIENTRYP PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat *value);
+typedef void (APIENTRYP PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint *value);
+typedef void (APIENTRYP PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint *value);
+typedef void (APIENTRYP PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint *value);
+typedef void (APIENTRYP PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint *value);
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC) (GLuint index, GLdouble x);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC) (GLuint index, const GLdouble *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC) (GLuint index, GLshort x);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC) (GLuint index, const GLshort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC) (GLuint index, GLdouble x, GLdouble y);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC) (GLuint index, const GLdouble *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC) (GLuint index, GLshort x, GLshort y);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC) (GLuint index, const GLshort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC) (GLuint index, const GLdouble *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC) (GLuint index, GLshort x, GLshort y, GLshort z);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC) (GLuint index, const GLshort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC) (GLuint index, const GLbyte *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC) (GLuint index, const GLint *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC) (GLuint index, const GLshort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC) (GLuint index, const GLubyte *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC) (GLuint index, const GLuint *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC) (GLuint index, const GLushort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC) (GLuint index, const GLbyte *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC) (GLuint index, const GLdouble *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC) (GLuint index, const GLint *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC) (GLuint index, const GLshort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC) (GLuint index, const GLubyte *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC) (GLuint index, const GLuint *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC) (GLuint index, const GLushort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer);
+#endif
+
+#ifndef GL_ARB_multitexture
+#define GL_ARB_multitexture 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glActiveTextureARB (GLenum);
+GLAPI void APIENTRY glClientActiveTextureARB (GLenum);
+GLAPI void APIENTRY glMultiTexCoord1dARB (GLenum, GLdouble);
+GLAPI void APIENTRY glMultiTexCoord1dvARB (GLenum, const GLdouble *);
+GLAPI void APIENTRY glMultiTexCoord1fARB (GLenum, GLfloat);
+GLAPI void APIENTRY glMultiTexCoord1fvARB (GLenum, const GLfloat *);
+GLAPI void APIENTRY glMultiTexCoord1iARB (GLenum, GLint);
+GLAPI void APIENTRY glMultiTexCoord1ivARB (GLenum, const GLint *);
+GLAPI void APIENTRY glMultiTexCoord1sARB (GLenum, GLshort);
+GLAPI void APIENTRY glMultiTexCoord1svARB (GLenum, const GLshort *);
+GLAPI void APIENTRY glMultiTexCoord2dARB (GLenum, GLdouble, GLdouble);
+GLAPI void APIENTRY glMultiTexCoord2dvARB (GLenum, const GLdouble *);
+GLAPI void APIENTRY glMultiTexCoord2fARB (GLenum, GLfloat, GLfloat);
+GLAPI void APIENTRY glMultiTexCoord2fvARB (GLenum, const GLfloat *);
+GLAPI void APIENTRY glMultiTexCoord2iARB (GLenum, GLint, GLint);
+GLAPI void APIENTRY glMultiTexCoord2ivARB (GLenum, const GLint *);
+GLAPI void APIENTRY glMultiTexCoord2sARB (GLenum, GLshort, GLshort);
+GLAPI void APIENTRY glMultiTexCoord2svARB (GLenum, const GLshort *);
+GLAPI void APIENTRY glMultiTexCoord3dARB (GLenum, GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glMultiTexCoord3dvARB (GLenum, const GLdouble *);
+GLAPI void APIENTRY glMultiTexCoord3fARB (GLenum, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glMultiTexCoord3fvARB (GLenum, const GLfloat *);
+GLAPI void APIENTRY glMultiTexCoord3iARB (GLenum, GLint, GLint, GLint);
+GLAPI void APIENTRY glMultiTexCoord3ivARB (GLenum, const GLint *);
+GLAPI void APIENTRY glMultiTexCoord3sARB (GLenum, GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glMultiTexCoord3svARB (GLenum, const GLshort *);
+GLAPI void APIENTRY glMultiTexCoord4dARB (GLenum, GLdouble, GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glMultiTexCoord4dvARB (GLenum, const GLdouble *);
+GLAPI void APIENTRY glMultiTexCoord4fARB (GLenum, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glMultiTexCoord4fvARB (GLenum, const GLfloat *);
+GLAPI void APIENTRY glMultiTexCoord4iARB (GLenum, GLint, GLint, GLint, GLint);
+GLAPI void APIENTRY glMultiTexCoord4ivARB (GLenum, const GLint *);
+GLAPI void APIENTRY glMultiTexCoord4sARB (GLenum, GLshort, GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glMultiTexCoord4svARB (GLenum, const GLshort *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLACTIVETEXTUREARBPROC) (GLenum texture);
+typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREARBPROC) (GLenum texture);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1DARBPROC) (GLenum target, GLdouble s);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVARBPROC) (GLenum target, const GLdouble *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1FARBPROC) (GLenum target, GLfloat s);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVARBPROC) (GLenum target, const GLfloat *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1IARBPROC) (GLenum target, GLint s);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVARBPROC) (GLenum target, const GLint *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1SARBPROC) (GLenum target, GLshort s);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVARBPROC) (GLenum target, const GLshort *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2DARBPROC) (GLenum target, GLdouble s, GLdouble t);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVARBPROC) (GLenum target, const GLdouble *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2FARBPROC) (GLenum target, GLfloat s, GLfloat t);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVARBPROC) (GLenum target, const GLfloat *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2IARBPROC) (GLenum target, GLint s, GLint t);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVARBPROC) (GLenum target, const GLint *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2SARBPROC) (GLenum target, GLshort s, GLshort t);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVARBPROC) (GLenum target, const GLshort *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVARBPROC) (GLenum target, const GLdouble *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVARBPROC) (GLenum target, const GLfloat *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3IARBPROC) (GLenum target, GLint s, GLint t, GLint r);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVARBPROC) (GLenum target, const GLint *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVARBPROC) (GLenum target, const GLshort *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVARBPROC) (GLenum target, const GLdouble *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVARBPROC) (GLenum target, const GLfloat *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4IARBPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVARBPROC) (GLenum target, const GLint *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVARBPROC) (GLenum target, const GLshort *v);
+#endif
+
+#ifndef GL_ARB_transpose_matrix
+#define GL_ARB_transpose_matrix 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glLoadTransposeMatrixfARB (const GLfloat *);
+GLAPI void APIENTRY glLoadTransposeMatrixdARB (const GLdouble *);
+GLAPI void APIENTRY glMultTransposeMatrixfARB (const GLfloat *);
+GLAPI void APIENTRY glMultTransposeMatrixdARB (const GLdouble *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFARBPROC) (const GLfloat *m);
+typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDARBPROC) (const GLdouble *m);
+typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFARBPROC) (const GLfloat *m);
+typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDARBPROC) (const GLdouble *m);
+#endif
+
+#ifndef GL_ARB_multisample
+#define GL_ARB_multisample 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glSampleCoverageARB (GLclampf, GLboolean);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLSAMPLECOVERAGEARBPROC) (GLclampf value, GLboolean invert);
+#endif
+
+#ifndef GL_ARB_texture_env_add
+#define GL_ARB_texture_env_add 1
+#endif
+
+#ifndef GL_ARB_texture_cube_map
+#define GL_ARB_texture_cube_map 1
+#endif
+
+#ifndef GL_ARB_texture_compression
+#define GL_ARB_texture_compression 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glCompressedTexImage3DARB (GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glCompressedTexImage2DARB (GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glCompressedTexImage1DARB (GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glCompressedTexSubImage3DARB (GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glCompressedTexSubImage2DARB (GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glCompressedTexSubImage1DARB (GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glGetCompressedTexImageARB (GLenum, GLint, GLvoid *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data);
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data);
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *data);
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data);
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data);
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data);
+typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint level, GLvoid *img);
+#endif
+
+#ifndef GL_ARB_texture_border_clamp
+#define GL_ARB_texture_border_clamp 1
+#endif
+
+#ifndef GL_ARB_point_parameters
+#define GL_ARB_point_parameters 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glPointParameterfARB (GLenum, GLfloat);
+GLAPI void APIENTRY glPointParameterfvARB (GLenum, const GLfloat *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLPOINTPARAMETERFARBPROC) (GLenum pname, GLfloat param);
+typedef void (APIENTRYP PFNGLPOINTPARAMETERFVARBPROC) (GLenum pname, const GLfloat *params);
+#endif
+
+#ifndef GL_ARB_vertex_blend
+#define GL_ARB_vertex_blend 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glWeightbvARB (GLint, const GLbyte *);
+GLAPI void APIENTRY glWeightsvARB (GLint, const GLshort *);
+GLAPI void APIENTRY glWeightivARB (GLint, const GLint *);
+GLAPI void APIENTRY glWeightfvARB (GLint, const GLfloat *);
+GLAPI void APIENTRY glWeightdvARB (GLint, const GLdouble *);
+GLAPI void APIENTRY glWeightubvARB (GLint, const GLubyte *);
+GLAPI void APIENTRY glWeightusvARB (GLint, const GLushort *);
+GLAPI void APIENTRY glWeightuivARB (GLint, const GLuint *);
+GLAPI void APIENTRY glWeightPointerARB (GLint, GLenum, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glVertexBlendARB (GLint);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLWEIGHTBVARBPROC) (GLint size, const GLbyte *weights);
+typedef void (APIENTRYP PFNGLWEIGHTSVARBPROC) (GLint size, const GLshort *weights);
+typedef void (APIENTRYP PFNGLWEIGHTIVARBPROC) (GLint size, const GLint *weights);
+typedef void (APIENTRYP PFNGLWEIGHTFVARBPROC) (GLint size, const GLfloat *weights);
+typedef void (APIENTRYP PFNGLWEIGHTDVARBPROC) (GLint size, const GLdouble *weights);
+typedef void (APIENTRYP PFNGLWEIGHTUBVARBPROC) (GLint size, const GLubyte *weights);
+typedef void (APIENTRYP PFNGLWEIGHTUSVARBPROC) (GLint size, const GLushort *weights);
+typedef void (APIENTRYP PFNGLWEIGHTUIVARBPROC) (GLint size, const GLuint *weights);
+typedef void (APIENTRYP PFNGLWEIGHTPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
+typedef void (APIENTRYP PFNGLVERTEXBLENDARBPROC) (GLint count);
+#endif
+
+#ifndef GL_ARB_matrix_palette
+#define GL_ARB_matrix_palette 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glCurrentPaletteMatrixARB (GLint);
+GLAPI void APIENTRY glMatrixIndexubvARB (GLint, const GLubyte *);
+GLAPI void APIENTRY glMatrixIndexusvARB (GLint, const GLushort *);
+GLAPI void APIENTRY glMatrixIndexuivARB (GLint, const GLuint *);
+GLAPI void APIENTRY glMatrixIndexPointerARB (GLint, GLenum, GLsizei, const GLvoid *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLCURRENTPALETTEMATRIXARBPROC) (GLint index);
+typedef void (APIENTRYP PFNGLMATRIXINDEXUBVARBPROC) (GLint size, const GLubyte *indices);
+typedef void (APIENTRYP PFNGLMATRIXINDEXUSVARBPROC) (GLint size, const GLushort *indices);
+typedef void (APIENTRYP PFNGLMATRIXINDEXUIVARBPROC) (GLint size, const GLuint *indices);
+typedef void (APIENTRYP PFNGLMATRIXINDEXPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
+#endif
+
+#ifndef GL_ARB_texture_env_combine
+#define GL_ARB_texture_env_combine 1
+#endif
+
+#ifndef GL_ARB_texture_env_crossbar
+#define GL_ARB_texture_env_crossbar 1
+#endif
+
+#ifndef GL_ARB_texture_env_dot3
+#define GL_ARB_texture_env_dot3 1
+#endif
+
+#ifndef GL_ARB_texture_mirrored_repeat
+#define GL_ARB_texture_mirrored_repeat 1
+#endif
+
+#ifndef GL_ARB_depth_texture
+#define GL_ARB_depth_texture 1
+#endif
+
+#ifndef GL_ARB_shadow
+#define GL_ARB_shadow 1
+#endif
+
+#ifndef GL_ARB_shadow_ambient
+#define GL_ARB_shadow_ambient 1
+#endif
+
+#ifndef GL_ARB_window_pos
+#define GL_ARB_window_pos 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glWindowPos2dARB (GLdouble, GLdouble);
+GLAPI void APIENTRY glWindowPos2dvARB (const GLdouble *);
+GLAPI void APIENTRY glWindowPos2fARB (GLfloat, GLfloat);
+GLAPI void APIENTRY glWindowPos2fvARB (const GLfloat *);
+GLAPI void APIENTRY glWindowPos2iARB (GLint, GLint);
+GLAPI void APIENTRY glWindowPos2ivARB (const GLint *);
+GLAPI void APIENTRY glWindowPos2sARB (GLshort, GLshort);
+GLAPI void APIENTRY glWindowPos2svARB (const GLshort *);
+GLAPI void APIENTRY glWindowPos3dARB (GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glWindowPos3dvARB (const GLdouble *);
+GLAPI void APIENTRY glWindowPos3fARB (GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glWindowPos3fvARB (const GLfloat *);
+GLAPI void APIENTRY glWindowPos3iARB (GLint, GLint, GLint);
+GLAPI void APIENTRY glWindowPos3ivARB (const GLint *);
+GLAPI void APIENTRY glWindowPos3sARB (GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glWindowPos3svARB (const GLshort *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLWINDOWPOS2DARBPROC) (GLdouble x, GLdouble y);
+typedef void (APIENTRYP PFNGLWINDOWPOS2DVARBPROC) (const GLdouble *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS2FARBPROC) (GLfloat x, GLfloat y);
+typedef void (APIENTRYP PFNGLWINDOWPOS2FVARBPROC) (const GLfloat *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS2IARBPROC) (GLint x, GLint y);
+typedef void (APIENTRYP PFNGLWINDOWPOS2IVARBPROC) (const GLint *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS2SARBPROC) (GLshort x, GLshort y);
+typedef void (APIENTRYP PFNGLWINDOWPOS2SVARBPROC) (const GLshort *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS3DARBPROC) (GLdouble x, GLdouble y, GLdouble z);
+typedef void (APIENTRYP PFNGLWINDOWPOS3DVARBPROC) (const GLdouble *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS3FARBPROC) (GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLWINDOWPOS3FVARBPROC) (const GLfloat *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS3IARBPROC) (GLint x, GLint y, GLint z);
+typedef void (APIENTRYP PFNGLWINDOWPOS3IVARBPROC) (const GLint *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS3SARBPROC) (GLshort x, GLshort y, GLshort z);
+typedef void (APIENTRYP PFNGLWINDOWPOS3SVARBPROC) (const GLshort *v);
+#endif
+
+#ifndef GL_ARB_vertex_program
+#define GL_ARB_vertex_program 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glVertexAttrib1dARB (GLuint, GLdouble);
+GLAPI void APIENTRY glVertexAttrib1dvARB (GLuint, const GLdouble *);
+GLAPI void APIENTRY glVertexAttrib1fARB (GLuint, GLfloat);
+GLAPI void APIENTRY glVertexAttrib1fvARB (GLuint, const GLfloat *);
+GLAPI void APIENTRY glVertexAttrib1sARB (GLuint, GLshort);
+GLAPI void APIENTRY glVertexAttrib1svARB (GLuint, const GLshort *);
+GLAPI void APIENTRY glVertexAttrib2dARB (GLuint, GLdouble, GLdouble);
+GLAPI void APIENTRY glVertexAttrib2dvARB (GLuint, const GLdouble *);
+GLAPI void APIENTRY glVertexAttrib2fARB (GLuint, GLfloat, GLfloat);
+GLAPI void APIENTRY glVertexAttrib2fvARB (GLuint, const GLfloat *);
+GLAPI void APIENTRY glVertexAttrib2sARB (GLuint, GLshort, GLshort);
+GLAPI void APIENTRY glVertexAttrib2svARB (GLuint, const GLshort *);
+GLAPI void APIENTRY glVertexAttrib3dARB (GLuint, GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glVertexAttrib3dvARB (GLuint, const GLdouble *);
+GLAPI void APIENTRY glVertexAttrib3fARB (GLuint, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glVertexAttrib3fvARB (GLuint, const GLfloat *);
+GLAPI void APIENTRY glVertexAttrib3sARB (GLuint, GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glVertexAttrib3svARB (GLuint, const GLshort *);
+GLAPI void APIENTRY glVertexAttrib4NbvARB (GLuint, const GLbyte *);
+GLAPI void APIENTRY glVertexAttrib4NivARB (GLuint, const GLint *);
+GLAPI void APIENTRY glVertexAttrib4NsvARB (GLuint, const GLshort *);
+GLAPI void APIENTRY glVertexAttrib4NubARB (GLuint, GLubyte, GLubyte, GLubyte, GLubyte);
+GLAPI void APIENTRY glVertexAttrib4NubvARB (GLuint, const GLubyte *);
+GLAPI void APIENTRY glVertexAttrib4NuivARB (GLuint, const GLuint *);
+GLAPI void APIENTRY glVertexAttrib4NusvARB (GLuint, const GLushort *);
+GLAPI void APIENTRY glVertexAttrib4bvARB (GLuint, const GLbyte *);
+GLAPI void APIENTRY glVertexAttrib4dARB (GLuint, GLdouble, GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glVertexAttrib4dvARB (GLuint, const GLdouble *);
+GLAPI void APIENTRY glVertexAttrib4fARB (GLuint, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glVertexAttrib4fvARB (GLuint, const GLfloat *);
+GLAPI void APIENTRY glVertexAttrib4ivARB (GLuint, const GLint *);
+GLAPI void APIENTRY glVertexAttrib4sARB (GLuint, GLshort, GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glVertexAttrib4svARB (GLuint, const GLshort *);
+GLAPI void APIENTRY glVertexAttrib4ubvARB (GLuint, const GLubyte *);
+GLAPI void APIENTRY glVertexAttrib4uivARB (GLuint, const GLuint *);
+GLAPI void APIENTRY glVertexAttrib4usvARB (GLuint, const GLushort *);
+GLAPI void APIENTRY glVertexAttribPointerARB (GLuint, GLint, GLenum, GLboolean, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glEnableVertexAttribArrayARB (GLuint);
+GLAPI void APIENTRY glDisableVertexAttribArrayARB (GLuint);
+GLAPI void APIENTRY glProgramStringARB (GLenum, GLenum, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glBindProgramARB (GLenum, GLuint);
+GLAPI void APIENTRY glDeleteProgramsARB (GLsizei, const GLuint *);
+GLAPI void APIENTRY glGenProgramsARB (GLsizei, GLuint *);
+GLAPI void APIENTRY glProgramEnvParameter4dARB (GLenum, GLuint, GLdouble, GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glProgramEnvParameter4dvARB (GLenum, GLuint, const GLdouble *);
+GLAPI void APIENTRY glProgramEnvParameter4fARB (GLenum, GLuint, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glProgramEnvParameter4fvARB (GLenum, GLuint, const GLfloat *);
+GLAPI void APIENTRY glProgramLocalParameter4dARB (GLenum, GLuint, GLdouble, GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glProgramLocalParameter4dvARB (GLenum, GLuint, const GLdouble *);
+GLAPI void APIENTRY glProgramLocalParameter4fARB (GLenum, GLuint, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glProgramLocalParameter4fvARB (GLenum, GLuint, const GLfloat *);
+GLAPI void APIENTRY glGetProgramEnvParameterdvARB (GLenum, GLuint, GLdouble *);
+GLAPI void APIENTRY glGetProgramEnvParameterfvARB (GLenum, GLuint, GLfloat *);
+GLAPI void APIENTRY glGetProgramLocalParameterdvARB (GLenum, GLuint, GLdouble *);
+GLAPI void APIENTRY glGetProgramLocalParameterfvARB (GLenum, GLuint, GLfloat *);
+GLAPI void APIENTRY glGetProgramivARB (GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glGetProgramStringARB (GLenum, GLenum, GLvoid *);
+GLAPI void APIENTRY glGetVertexAttribdvARB (GLuint, GLenum, GLdouble *);
+GLAPI void APIENTRY glGetVertexAttribfvARB (GLuint, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetVertexAttribivARB (GLuint, GLenum, GLint *);
+GLAPI void APIENTRY glGetVertexAttribPointervARB (GLuint, GLenum, GLvoid* *);
+GLAPI GLboolean APIENTRY glIsProgramARB (GLuint);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1DARBPROC) (GLuint index, GLdouble x);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVARBPROC) (GLuint index, const GLdouble *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1FARBPROC) (GLuint index, GLfloat x);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVARBPROC) (GLuint index, const GLfloat *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1SARBPROC) (GLuint index, GLshort x);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVARBPROC) (GLuint index, const GLshort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2DARBPROC) (GLuint index, GLdouble x, GLdouble y);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVARBPROC) (GLuint index, const GLdouble *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2FARBPROC) (GLuint index, GLfloat x, GLfloat y);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVARBPROC) (GLuint index, const GLfloat *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2SARBPROC) (GLuint index, GLshort x, GLshort y);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVARBPROC) (GLuint index, const GLshort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVARBPROC) (GLuint index, const GLdouble *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVARBPROC) (GLuint index, const GLfloat *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVARBPROC) (GLuint index, const GLshort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVARBPROC) (GLuint index, const GLbyte *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVARBPROC) (GLuint index, const GLint *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVARBPROC) (GLuint index, const GLshort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBARBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVARBPROC) (GLuint index, const GLubyte *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVARBPROC) (GLuint index, const GLuint *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVARBPROC) (GLuint index, const GLushort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVARBPROC) (GLuint index, const GLbyte *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVARBPROC) (GLuint index, const GLdouble *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVARBPROC) (GLuint index, const GLfloat *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVARBPROC) (GLuint index, const GLint *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVARBPROC) (GLuint index, const GLshort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVARBPROC) (GLuint index, const GLubyte *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVARBPROC) (GLuint index, const GLuint *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVARBPROC) (GLuint index, const GLushort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERARBPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer);
+typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYARBPROC) (GLuint index);
+typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) (GLuint index);
+typedef void (APIENTRYP PFNGLPROGRAMSTRINGARBPROC) (GLenum target, GLenum format, GLsizei len, const GLvoid *string);
+typedef void (APIENTRYP PFNGLBINDPROGRAMARBPROC) (GLenum target, GLuint program);
+typedef void (APIENTRYP PFNGLDELETEPROGRAMSARBPROC) (GLsizei n, const GLuint *programs);
+typedef void (APIENTRYP PFNGLGENPROGRAMSARBPROC) (GLsizei n, GLuint *programs);
+typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params);
+typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params);
+typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params);
+typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params);
+typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params);
+typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params);
+typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETPROGRAMIVARBPROC) (GLenum target, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGARBPROC) (GLenum target, GLenum pname, GLvoid *string);
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVARBPROC) (GLuint index, GLenum pname, GLdouble *params);
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVARBPROC) (GLuint index, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVARBPROC) (GLuint index, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVARBPROC) (GLuint index, GLenum pname, GLvoid* *pointer);
+typedef GLboolean (APIENTRYP PFNGLISPROGRAMARBPROC) (GLuint program);
+#endif
+
+#ifndef GL_ARB_fragment_program
+#define GL_ARB_fragment_program 1
+/* All ARB_fragment_program entry points are shared with ARB_vertex_program. */
+#endif
+
+#ifndef GL_ARB_vertex_buffer_object
+#define GL_ARB_vertex_buffer_object 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glBindBufferARB (GLenum, GLuint);
+GLAPI void APIENTRY glDeleteBuffersARB (GLsizei, const GLuint *);
+GLAPI void APIENTRY glGenBuffersARB (GLsizei, GLuint *);
+GLAPI GLboolean APIENTRY glIsBufferARB (GLuint);
+GLAPI void APIENTRY glBufferDataARB (GLenum, GLsizeiptrARB, const GLvoid *, GLenum);
+GLAPI void APIENTRY glBufferSubDataARB (GLenum, GLintptrARB, GLsizeiptrARB, const GLvoid *);
+GLAPI void APIENTRY glGetBufferSubDataARB (GLenum, GLintptrARB, GLsizeiptrARB, GLvoid *);
+GLAPI GLvoid* APIENTRY glMapBufferARB (GLenum, GLenum);
+GLAPI GLboolean APIENTRY glUnmapBufferARB (GLenum);
+GLAPI void APIENTRY glGetBufferParameterivARB (GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glGetBufferPointervARB (GLenum, GLenum, GLvoid* *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLBINDBUFFERARBPROC) (GLenum target, GLuint buffer);
+typedef void (APIENTRYP PFNGLDELETEBUFFERSARBPROC) (GLsizei n, const GLuint *buffers);
+typedef void (APIENTRYP PFNGLGENBUFFERSARBPROC) (GLsizei n, GLuint *buffers);
+typedef GLboolean (APIENTRYP PFNGLISBUFFERARBPROC) (GLuint buffer);
+typedef void (APIENTRYP PFNGLBUFFERDATAARBPROC) (GLenum target, GLsizeiptrARB size, const GLvoid *data, GLenum usage);
+typedef void (APIENTRYP PFNGLBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid *data);
+typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid *data);
+typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERARBPROC) (GLenum target, GLenum access);
+typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERARBPROC) (GLenum target);
+typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVARBPROC) (GLenum target, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVARBPROC) (GLenum target, GLenum pname, GLvoid* *params);
+#endif
+
+#ifndef GL_ARB_map_buffer_range
+#define GL_ARB_map_buffer_range 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI GLvoid* APIENTRY glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
+GLAPI void APIENTRY glFlushMappedBufferRange (GLenum target, GLintptr offset, GLsizeiptr length);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
+typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length);
+#endif
+
+#ifndef GL_ARB_occlusion_query
+#define GL_ARB_occlusion_query 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glGenQueriesARB (GLsizei, GLuint *);
+GLAPI void APIENTRY glDeleteQueriesARB (GLsizei, const GLuint *);
+GLAPI GLboolean APIENTRY glIsQueryARB (GLuint);
+GLAPI void APIENTRY glBeginQueryARB (GLenum, GLuint);
+GLAPI void APIENTRY glEndQueryARB (GLenum);
+GLAPI void APIENTRY glGetQueryivARB (GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glGetQueryObjectivARB (GLuint, GLenum, GLint *);
+GLAPI void APIENTRY glGetQueryObjectuivARB (GLuint, GLenum, GLuint *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLGENQUERIESARBPROC) (GLsizei n, GLuint *ids);
+typedef void (APIENTRYP PFNGLDELETEQUERIESARBPROC) (GLsizei n, const GLuint *ids);
+typedef GLboolean (APIENTRYP PFNGLISQUERYARBPROC) (GLuint id);
+typedef void (APIENTRYP PFNGLBEGINQUERYARBPROC) (GLenum target, GLuint id);
+typedef void (APIENTRYP PFNGLENDQUERYARBPROC) (GLenum target);
+typedef void (APIENTRYP PFNGLGETQUERYIVARBPROC) (GLenum target, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVARBPROC) (GLuint id, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVARBPROC) (GLuint id, GLenum pname, GLuint *params);
+#endif
+
+#ifndef GL_EXT_timer_query
+#define GL_EXT_timer_query 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glGetQueryObjecti64vEXT (GLuint id, GLenum pname, GLint64EXT *params);
+GLAPI void APIENTRY glGetQueryObjectui64vEXT (GLuint id, GLenum pname, GLuint64EXT *params);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VEXTPROC) (GLuint id, GLenum pname, GLint64EXT *params);
+typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VEXTPROC) (GLuint id, GLenum pname, GLuint64EXT *params);
+#endif
+
+#ifndef GL_ARB_sync
+#define GL_ARB_sync 1
+#ifdef GL3_PROTOTYPES
+GLAPI GLsync APIENTRY glFenceSync (GLenum condition, GLbitfield flags);
+GLAPI GLboolean APIENTRY glIsSync (GLsync sync);
+GLAPI void APIENTRY glDeleteSync (GLsync sync);
+GLAPI GLenum APIENTRY glClientWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout);
+GLAPI void APIENTRY glWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout);
+GLAPI void APIENTRY glGetInteger64v (GLenum pname, GLint64 *params);
+GLAPI void APIENTRY glGetSynciv (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values);
+#endif /* GL3_PROTOTYPES */
+typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC) (GLenum condition, GLbitfield flags);
+typedef GLboolean (APIENTRYP PFNGLISSYNCPROC) (GLsync sync);
+typedef void (APIENTRYP PFNGLDELETESYNCPROC) (GLsync sync);
+typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout);
+typedef void (APIENTRYP PFNGLWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout);
+typedef void (APIENTRYP PFNGLGETINTEGER64VPROC) (GLenum pname, GLint64 *params);
+typedef void (APIENTRYP PFNGLGETSYNCIVPROC) (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values);
+#endif
+
+#ifndef GL_ARB_shader_objects
+#define GL_ARB_shader_objects 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glDeleteObjectARB (GLhandleARB);
+GLAPI GLhandleARB APIENTRY glGetHandleARB (GLenum);
+GLAPI void APIENTRY glDetachObjectARB (GLhandleARB, GLhandleARB);
+GLAPI GLhandleARB APIENTRY glCreateShaderObjectARB (GLenum);
+GLAPI void APIENTRY glShaderSourceARB (GLhandleARB, GLsizei, const GLcharARB* *, const GLint *);
+GLAPI void APIENTRY glCompileShaderARB (GLhandleARB);
+GLAPI GLhandleARB APIENTRY glCreateProgramObjectARB (void);
+GLAPI void APIENTRY glAttachObjectARB (GLhandleARB, GLhandleARB);
+GLAPI void APIENTRY glLinkProgramARB (GLhandleARB);
+GLAPI void APIENTRY glUseProgramObjectARB (GLhandleARB);
+GLAPI void APIENTRY glValidateProgramARB (GLhandleARB);
+GLAPI void APIENTRY glUniform1fARB (GLint, GLfloat);
+GLAPI void APIENTRY glUniform2fARB (GLint, GLfloat, GLfloat);
+GLAPI void APIENTRY glUniform3fARB (GLint, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glUniform4fARB (GLint, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glUniform1iARB (GLint, GLint);
+GLAPI void APIENTRY glUniform2iARB (GLint, GLint, GLint);
+GLAPI void APIENTRY glUniform3iARB (GLint, GLint, GLint, GLint);
+GLAPI void APIENTRY glUniform4iARB (GLint, GLint, GLint, GLint, GLint);
+GLAPI void APIENTRY glUniform1fvARB (GLint, GLsizei, const GLfloat *);
+GLAPI void APIENTRY glUniform2fvARB (GLint, GLsizei, const GLfloat *);
+GLAPI void APIENTRY glUniform3fvARB (GLint, GLsizei, const GLfloat *);
+GLAPI void APIENTRY glUniform4fvARB (GLint, GLsizei, const GLfloat *);
+GLAPI void APIENTRY glUniform1ivARB (GLint, GLsizei, const GLint *);
+GLAPI void APIENTRY glUniform2ivARB (GLint, GLsizei, const GLint *);
+GLAPI void APIENTRY glUniform3ivARB (GLint, GLsizei, const GLint *);
+GLAPI void APIENTRY glUniform4ivARB (GLint, GLsizei, const GLint *);
+GLAPI void APIENTRY glUniformMatrix2fvARB (GLint, GLsizei, GLboolean, const GLfloat *);
+GLAPI void APIENTRY glUniformMatrix3fvARB (GLint, GLsizei, GLboolean, const GLfloat *);
+GLAPI void APIENTRY glUniformMatrix4fvARB (GLint, GLsizei, GLboolean, const GLfloat *);
+GLAPI void APIENTRY glGetObjectParameterfvARB (GLhandleARB, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetObjectParameterivARB (GLhandleARB, GLenum, GLint *);
+GLAPI void APIENTRY glGetInfoLogARB (GLhandleARB, GLsizei, GLsizei *, GLcharARB *);
+GLAPI void APIENTRY glGetAttachedObjectsARB (GLhandleARB, GLsizei, GLsizei *, GLhandleARB *);
+GLAPI GLint APIENTRY glGetUniformLocationARB (GLhandleARB, const GLcharARB *);
+GLAPI void APIENTRY glGetActiveUniformARB (GLhandleARB, GLuint, GLsizei, GLsizei *, GLint *, GLenum *, GLcharARB *);
+GLAPI void APIENTRY glGetUniformfvARB (GLhandleARB, GLint, GLfloat *);
+GLAPI void APIENTRY glGetUniformivARB (GLhandleARB, GLint, GLint *);
+GLAPI void APIENTRY glGetShaderSourceARB (GLhandleARB, GLsizei, GLsizei *, GLcharARB *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLDELETEOBJECTARBPROC) (GLhandleARB obj);
+typedef GLhandleARB (APIENTRYP PFNGLGETHANDLEARBPROC) (GLenum pname);
+typedef void (APIENTRYP PFNGLDETACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB attachedObj);
+typedef GLhandleARB (APIENTRYP PFNGLCREATESHADEROBJECTARBPROC) (GLenum shaderType);
+typedef void (APIENTRYP PFNGLSHADERSOURCEARBPROC) (GLhandleARB shaderObj, GLsizei count, const GLcharARB* *string, const GLint *length);
+typedef void (APIENTRYP PFNGLCOMPILESHADERARBPROC) (GLhandleARB shaderObj);
+typedef GLhandleARB (APIENTRYP PFNGLCREATEPROGRAMOBJECTARBPROC) (void);
+typedef void (APIENTRYP PFNGLATTACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB obj);
+typedef void (APIENTRYP PFNGLLINKPROGRAMARBPROC) (GLhandleARB programObj);
+typedef void (APIENTRYP PFNGLUSEPROGRAMOBJECTARBPROC) (GLhandleARB programObj);
+typedef void (APIENTRYP PFNGLVALIDATEPROGRAMARBPROC) (GLhandleARB programObj);
+typedef void (APIENTRYP PFNGLUNIFORM1FARBPROC) (GLint location, GLfloat v0);
+typedef void (APIENTRYP PFNGLUNIFORM2FARBPROC) (GLint location, GLfloat v0, GLfloat v1);
+typedef void (APIENTRYP PFNGLUNIFORM3FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
+typedef void (APIENTRYP PFNGLUNIFORM4FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
+typedef void (APIENTRYP PFNGLUNIFORM1IARBPROC) (GLint location, GLint v0);
+typedef void (APIENTRYP PFNGLUNIFORM2IARBPROC) (GLint location, GLint v0, GLint v1);
+typedef void (APIENTRYP PFNGLUNIFORM3IARBPROC) (GLint location, GLint v0, GLint v1, GLint v2);
+typedef void (APIENTRYP PFNGLUNIFORM4IARBPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3);
+typedef void (APIENTRYP PFNGLUNIFORM1FVARBPROC) (GLint location, GLsizei count, const GLfloat *value);
+typedef void (APIENTRYP PFNGLUNIFORM2FVARBPROC) (GLint location, GLsizei count, const GLfloat *value);
+typedef void (APIENTRYP PFNGLUNIFORM3FVARBPROC) (GLint location, GLsizei count, const GLfloat *value);
+typedef void (APIENTRYP PFNGLUNIFORM4FVARBPROC) (GLint location, GLsizei count, const GLfloat *value);
+typedef void (APIENTRYP PFNGLUNIFORM1IVARBPROC) (GLint location, GLsizei count, const GLint *value);
+typedef void (APIENTRYP PFNGLUNIFORM2IVARBPROC) (GLint location, GLsizei count, const GLint *value);
+typedef void (APIENTRYP PFNGLUNIFORM3IVARBPROC) (GLint location, GLsizei count, const GLint *value);
+typedef void (APIENTRYP PFNGLUNIFORM4IVARBPROC) (GLint location, GLsizei count, const GLint *value);
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERFVARBPROC) (GLhandleARB obj, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVARBPROC) (GLhandleARB obj, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETINFOLOGARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog);
+typedef void (APIENTRYP PFNGLGETATTACHEDOBJECTSARBPROC) (GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj);
+typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name);
+typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMARBPROC) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name);
+typedef void (APIENTRYP PFNGLGETUNIFORMFVARBPROC) (GLhandleARB programObj, GLint location, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETUNIFORMIVARBPROC) (GLhandleARB programObj, GLint location, GLint *params);
+typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source);
+#endif
+
+#ifndef GL_ARB_vertex_shader
+#define GL_ARB_vertex_shader 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glBindAttribLocationARB (GLhandleARB, GLuint, const GLcharARB *);
+GLAPI void APIENTRY glGetActiveAttribARB (GLhandleARB, GLuint, GLsizei, GLsizei *, GLint *, GLenum *, GLcharARB *);
+GLAPI GLint APIENTRY glGetAttribLocationARB (GLhandleARB, const GLcharARB *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONARBPROC) (GLhandleARB programObj, GLuint index, const GLcharARB *name);
+typedef void (APIENTRYP PFNGLGETACTIVEATTRIBARBPROC) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name);
+typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name);
+#endif
+
+#ifndef GL_ARB_fragment_shader
+#define GL_ARB_fragment_shader 1
+#endif
+
+#ifndef GL_ARB_shading_language_100
+#define GL_ARB_shading_language_100 1
+#endif
+
+#ifndef GL_ARB_texture_non_power_of_two
+#define GL_ARB_texture_non_power_of_two 1
+#endif
+
+#ifndef GL_ARB_point_sprite
+#define GL_ARB_point_sprite 1
+#endif
+
+#ifndef GL_ARB_fragment_program_shadow
+#define GL_ARB_fragment_program_shadow 1
+#endif
+
+#ifndef GL_ARB_draw_buffers
+#define GL_ARB_draw_buffers 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glDrawBuffersARB (GLsizei, const GLenum *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLDRAWBUFFERSARBPROC) (GLsizei n, const GLenum *bufs);
+#endif
+
+#ifndef GL_ARB_texture_rectangle
+#define GL_ARB_texture_rectangle 1
+#endif
+
+#ifndef GL_ARB_color_buffer_float
+#define GL_ARB_color_buffer_float 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glClampColorARB (GLenum, GLenum);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLCLAMPCOLORARBPROC) (GLenum target, GLenum clamp);
+#endif
+
+#ifndef GL_ARB_half_float_pixel
+#define GL_ARB_half_float_pixel 1
+#endif
+
+#ifndef GL_ARB_texture_float
+#define GL_ARB_texture_float 1
+#endif
+
+#ifndef GL_ARB_pixel_buffer_object
+#define GL_ARB_pixel_buffer_object 1
+#endif
+
+#ifndef GL_EXT_abgr
+#define GL_EXT_abgr 1
+#endif
+
+#ifndef GL_EXT_blend_color
+#define GL_EXT_blend_color 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glBlendColorEXT (GLclampf, GLclampf, GLclampf, GLclampf);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLBLENDCOLOREXTPROC) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
+#endif
+
+#ifndef GL_EXT_polygon_offset
+#define GL_EXT_polygon_offset 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glPolygonOffsetEXT (GLfloat, GLfloat);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLPOLYGONOFFSETEXTPROC) (GLfloat factor, GLfloat bias);
+#endif
+
+#ifndef GL_EXT_texture
+#define GL_EXT_texture 1
+#endif
+
+#ifndef GL_EXT_texture3D
+#define GL_EXT_texture3D 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glTexImage3DEXT (GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid *);
+GLAPI void APIENTRY glTexSubImage3DEXT (GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLTEXIMAGE3DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
+typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels);
+#endif
+
+#ifndef GL_SGIS_texture_filter4
+#define GL_SGIS_texture_filter4 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glGetTexFilterFuncSGIS (GLenum, GLenum, GLfloat *);
+GLAPI void APIENTRY glTexFilterFuncSGIS (GLenum, GLenum, GLsizei, const GLfloat *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLGETTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLfloat *weights);
+typedef void (APIENTRYP PFNGLTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLsizei n, const GLfloat *weights);
+#endif
+
+#ifndef GL_EXT_subtexture
+#define GL_EXT_subtexture 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glTexSubImage1DEXT (GLenum, GLint, GLint, GLsizei, GLenum, GLenum, const GLvoid *);
+GLAPI void APIENTRY glTexSubImage2DEXT (GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels);
+typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);
+#endif
+
+#ifndef GL_EXT_copy_texture
+#define GL_EXT_copy_texture 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glCopyTexImage1DEXT (GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLint);
+GLAPI void APIENTRY glCopyTexImage2DEXT (GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint);
+GLAPI void APIENTRY glCopyTexSubImage1DEXT (GLenum, GLint, GLint, GLint, GLint, GLsizei);
+GLAPI void APIENTRY glCopyTexSubImage2DEXT (GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei);
+GLAPI void APIENTRY glCopyTexSubImage3DEXT (GLenum, GLint, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border);
+typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
+typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);
+typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
+typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);
+#endif
+
+#ifndef GL_EXT_histogram
+#define GL_EXT_histogram 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glGetHistogramEXT (GLenum, GLboolean, GLenum, GLenum, GLvoid *);
+GLAPI void APIENTRY glGetHistogramParameterfvEXT (GLenum, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetHistogramParameterivEXT (GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glGetMinmaxEXT (GLenum, GLboolean, GLenum, GLenum, GLvoid *);
+GLAPI void APIENTRY glGetMinmaxParameterfvEXT (GLenum, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetMinmaxParameterivEXT (GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glHistogramEXT (GLenum, GLsizei, GLenum, GLboolean);
+GLAPI void APIENTRY glMinmaxEXT (GLenum, GLenum, GLboolean);
+GLAPI void APIENTRY glResetHistogramEXT (GLenum);
+GLAPI void APIENTRY glResetMinmaxEXT (GLenum);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLGETHISTOGRAMEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values);
+typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETMINMAXEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values);
+typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLHISTOGRAMEXTPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink);
+typedef void (APIENTRYP PFNGLMINMAXEXTPROC) (GLenum target, GLenum internalformat, GLboolean sink);
+typedef void (APIENTRYP PFNGLRESETHISTOGRAMEXTPROC) (GLenum target);
+typedef void (APIENTRYP PFNGLRESETMINMAXEXTPROC) (GLenum target);
+#endif
+
+#ifndef GL_EXT_convolution
+#define GL_EXT_convolution 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glConvolutionFilter1DEXT (GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid *);
+GLAPI void APIENTRY glConvolutionFilter2DEXT (GLenum, GLenum, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *);
+GLAPI void APIENTRY glConvolutionParameterfEXT (GLenum, GLenum, GLfloat);
+GLAPI void APIENTRY glConvolutionParameterfvEXT (GLenum, GLenum, const GLfloat *);
+GLAPI void APIENTRY glConvolutionParameteriEXT (GLenum, GLenum, GLint);
+GLAPI void APIENTRY glConvolutionParameterivEXT (GLenum, GLenum, const GLint *);
+GLAPI void APIENTRY glCopyConvolutionFilter1DEXT (GLenum, GLenum, GLint, GLint, GLsizei);
+GLAPI void APIENTRY glCopyConvolutionFilter2DEXT (GLenum, GLenum, GLint, GLint, GLsizei, GLsizei);
+GLAPI void APIENTRY glGetConvolutionFilterEXT (GLenum, GLenum, GLenum, GLvoid *);
+GLAPI void APIENTRY glGetConvolutionParameterfvEXT (GLenum, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetConvolutionParameterivEXT (GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glGetSeparableFilterEXT (GLenum, GLenum, GLenum, GLvoid *, GLvoid *, GLvoid *);
+GLAPI void APIENTRY glSeparableFilter2DEXT (GLenum, GLenum, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *, const GLvoid *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *image);
+typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *image);
+typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat params);
+typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params);
+typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint params);
+typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params);
+typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width);
+typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height);
+typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, GLvoid *image);
+typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETSEPARABLEFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, GLvoid *row, GLvoid *column, GLvoid *span);
+typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *row, const GLvoid *column);
+#endif
+
+#ifndef GL_EXT_color_matrix
+#define GL_EXT_color_matrix 1
+#endif
+
+#ifndef GL_SGI_color_table
+#define GL_SGI_color_table 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glColorTableSGI (GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid *);
+GLAPI void APIENTRY glColorTableParameterfvSGI (GLenum, GLenum, const GLfloat *);
+GLAPI void APIENTRY glColorTableParameterivSGI (GLenum, GLenum, const GLint *);
+GLAPI void APIENTRY glCopyColorTableSGI (GLenum, GLenum, GLint, GLint, GLsizei);
+GLAPI void APIENTRY glGetColorTableSGI (GLenum, GLenum, GLenum, GLvoid *);
+GLAPI void APIENTRY glGetColorTableParameterfvSGI (GLenum, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetColorTableParameterivSGI (GLenum, GLenum, GLint *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table);
+typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, const GLfloat *params);
+typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, const GLint *params);
+typedef void (APIENTRYP PFNGLCOPYCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width);
+typedef void (APIENTRYP PFNGLGETCOLORTABLESGIPROC) (GLenum target, GLenum format, GLenum type, GLvoid *table);
+typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, GLint *params);
+#endif
+
+#ifndef GL_SGIX_pixel_texture
+#define GL_SGIX_pixel_texture 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glPixelTexGenSGIX (GLenum);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLPIXELTEXGENSGIXPROC) (GLenum mode);
+#endif
+
+#ifndef GL_SGIS_pixel_texture
+#define GL_SGIS_pixel_texture 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glPixelTexGenParameteriSGIS (GLenum, GLint);
+GLAPI void APIENTRY glPixelTexGenParameterivSGIS (GLenum, const GLint *);
+GLAPI void APIENTRY glPixelTexGenParameterfSGIS (GLenum, GLfloat);
+GLAPI void APIENTRY glPixelTexGenParameterfvSGIS (GLenum, const GLfloat *);
+GLAPI void APIENTRY glGetPixelTexGenParameterivSGIS (GLenum, GLint *);
+GLAPI void APIENTRY glGetPixelTexGenParameterfvSGIS (GLenum, GLfloat *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERISGISPROC) (GLenum pname, GLint param);
+typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERIVSGISPROC) (GLenum pname, const GLint *params);
+typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFSGISPROC) (GLenum pname, GLfloat param);
+typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFVSGISPROC) (GLenum pname, const GLfloat *params);
+typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERIVSGISPROC) (GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERFVSGISPROC) (GLenum pname, GLfloat *params);
+#endif
+
+#ifndef GL_SGIS_texture4D
+#define GL_SGIS_texture4D 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glTexImage4DSGIS (GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid *);
+GLAPI void APIENTRY glTexSubImage4DSGIS (GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLTEXIMAGE4DSGISPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
+typedef void (APIENTRYP PFNGLTEXSUBIMAGE4DSGISPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLenum format, GLenum type, const GLvoid *pixels);
+#endif
+
+#ifndef GL_SGI_texture_color_table
+#define GL_SGI_texture_color_table 1
+#endif
+
+#ifndef GL_EXT_cmyka
+#define GL_EXT_cmyka 1
+#endif
+
+#ifndef GL_EXT_texture_object
+#define GL_EXT_texture_object 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI GLboolean APIENTRY glAreTexturesResidentEXT (GLsizei, const GLuint *, GLboolean *);
+GLAPI void APIENTRY glBindTextureEXT (GLenum, GLuint);
+GLAPI void APIENTRY glDeleteTexturesEXT (GLsizei, const GLuint *);
+GLAPI void APIENTRY glGenTexturesEXT (GLsizei, GLuint *);
+GLAPI GLboolean APIENTRY glIsTextureEXT (GLuint);
+GLAPI void APIENTRY glPrioritizeTexturesEXT (GLsizei, const GLuint *, const GLclampf *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef GLboolean (APIENTRYP PFNGLARETEXTURESRESIDENTEXTPROC) (GLsizei n, const GLuint *textures, GLboolean *residences);
+typedef void (APIENTRYP PFNGLBINDTEXTUREEXTPROC) (GLenum target, GLuint texture);
+typedef void (APIENTRYP PFNGLDELETETEXTURESEXTPROC) (GLsizei n, const GLuint *textures);
+typedef void (APIENTRYP PFNGLGENTEXTURESEXTPROC) (GLsizei n, GLuint *textures);
+typedef GLboolean (APIENTRYP PFNGLISTEXTUREEXTPROC) (GLuint texture);
+typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESEXTPROC) (GLsizei n, const GLuint *textures, const GLclampf *priorities);
+#endif
+
+#ifndef GL_SGIS_detail_texture
+#define GL_SGIS_detail_texture 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glDetailTexFuncSGIS (GLenum, GLsizei, const GLfloat *);
+GLAPI void APIENTRY glGetDetailTexFuncSGIS (GLenum, GLfloat *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLDETAILTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points);
+typedef void (APIENTRYP PFNGLGETDETAILTEXFUNCSGISPROC) (GLenum target, GLfloat *points);
+#endif
+
+#ifndef GL_SGIS_sharpen_texture
+#define GL_SGIS_sharpen_texture 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glSharpenTexFuncSGIS (GLenum, GLsizei, const GLfloat *);
+GLAPI void APIENTRY glGetSharpenTexFuncSGIS (GLenum, GLfloat *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLSHARPENTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points);
+typedef void (APIENTRYP PFNGLGETSHARPENTEXFUNCSGISPROC) (GLenum target, GLfloat *points);
+#endif
+
+#ifndef GL_EXT_packed_pixels
+#define GL_EXT_packed_pixels 1
+#endif
+
+#ifndef GL_SGIS_texture_lod
+#define GL_SGIS_texture_lod 1
+#endif
+
+#ifndef GL_SGIS_multisample
+#define GL_SGIS_multisample 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glSampleMaskSGIS (GLclampf, GLboolean);
+GLAPI void APIENTRY glSamplePatternSGIS (GLenum);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLSAMPLEMASKSGISPROC) (GLclampf value, GLboolean invert);
+typedef void (APIENTRYP PFNGLSAMPLEPATTERNSGISPROC) (GLenum pattern);
+#endif
+
+#ifndef GL_EXT_rescale_normal
+#define GL_EXT_rescale_normal 1
+#endif
+
+#ifndef GL_EXT_vertex_array
+#define GL_EXT_vertex_array 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glArrayElementEXT (GLint);
+GLAPI void APIENTRY glColorPointerEXT (GLint, GLenum, GLsizei, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glDrawArraysEXT (GLenum, GLint, GLsizei);
+GLAPI void APIENTRY glEdgeFlagPointerEXT (GLsizei, GLsizei, const GLboolean *);
+GLAPI void APIENTRY glGetPointervEXT (GLenum, GLvoid* *);
+GLAPI void APIENTRY glIndexPointerEXT (GLenum, GLsizei, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glNormalPointerEXT (GLenum, GLsizei, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glTexCoordPointerEXT (GLint, GLenum, GLsizei, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glVertexPointerEXT (GLint, GLenum, GLsizei, GLsizei, const GLvoid *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLARRAYELEMENTEXTPROC) (GLint i);
+typedef void (APIENTRYP PFNGLCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer);
+typedef void (APIENTRYP PFNGLDRAWARRAYSEXTPROC) (GLenum mode, GLint first, GLsizei count);
+typedef void (APIENTRYP PFNGLEDGEFLAGPOINTEREXTPROC) (GLsizei stride, GLsizei count, const GLboolean *pointer);
+typedef void (APIENTRYP PFNGLGETPOINTERVEXTPROC) (GLenum pname, GLvoid* *params);
+typedef void (APIENTRYP PFNGLINDEXPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer);
+typedef void (APIENTRYP PFNGLNORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer);
+typedef void (APIENTRYP PFNGLTEXCOORDPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer);
+typedef void (APIENTRYP PFNGLVERTEXPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer);
+#endif
+
+#ifndef GL_EXT_misc_attribute
+#define GL_EXT_misc_attribute 1
+#endif
+
+#ifndef GL_SGIS_generate_mipmap
+#define GL_SGIS_generate_mipmap 1
+#endif
+
+#ifndef GL_SGIX_clipmap
+#define GL_SGIX_clipmap 1
+#endif
+
+#ifndef GL_SGIX_shadow
+#define GL_SGIX_shadow 1
+#endif
+
+#ifndef GL_SGIS_texture_edge_clamp
+#define GL_SGIS_texture_edge_clamp 1
+#endif
+
+#ifndef GL_SGIS_texture_border_clamp
+#define GL_SGIS_texture_border_clamp 1
+#endif
+
+#ifndef GL_EXT_blend_minmax
+#define GL_EXT_blend_minmax 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glBlendEquationEXT (GLenum);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLBLENDEQUATIONEXTPROC) (GLenum mode);
+#endif
+
+#ifndef GL_EXT_blend_subtract
+#define GL_EXT_blend_subtract 1
+#endif
+
+#ifndef GL_EXT_blend_logic_op
+#define GL_EXT_blend_logic_op 1
+#endif
+
+#ifndef GL_SGIX_interlace
+#define GL_SGIX_interlace 1
+#endif
+
+#ifndef GL_SGIX_pixel_tiles
+#define GL_SGIX_pixel_tiles 1
+#endif
+
+#ifndef GL_SGIX_texture_select
+#define GL_SGIX_texture_select 1
+#endif
+
+#ifndef GL_SGIX_sprite
+#define GL_SGIX_sprite 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glSpriteParameterfSGIX (GLenum, GLfloat);
+GLAPI void APIENTRY glSpriteParameterfvSGIX (GLenum, const GLfloat *);
+GLAPI void APIENTRY glSpriteParameteriSGIX (GLenum, GLint);
+GLAPI void APIENTRY glSpriteParameterivSGIX (GLenum, const GLint *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLSPRITEPARAMETERFSGIXPROC) (GLenum pname, GLfloat param);
+typedef void (APIENTRYP PFNGLSPRITEPARAMETERFVSGIXPROC) (GLenum pname, const GLfloat *params);
+typedef void (APIENTRYP PFNGLSPRITEPARAMETERISGIXPROC) (GLenum pname, GLint param);
+typedef void (APIENTRYP PFNGLSPRITEPARAMETERIVSGIXPROC) (GLenum pname, const GLint *params);
+#endif
+
+#ifndef GL_SGIX_texture_multi_buffer
+#define GL_SGIX_texture_multi_buffer 1
+#endif
+
+#ifndef GL_EXT_point_parameters
+#define GL_EXT_point_parameters 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glPointParameterfEXT (GLenum, GLfloat);
+GLAPI void APIENTRY glPointParameterfvEXT (GLenum, const GLfloat *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLPOINTPARAMETERFEXTPROC) (GLenum pname, GLfloat param);
+typedef void (APIENTRYP PFNGLPOINTPARAMETERFVEXTPROC) (GLenum pname, const GLfloat *params);
+#endif
+
+#ifndef GL_SGIS_point_parameters
+#define GL_SGIS_point_parameters 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glPointParameterfSGIS (GLenum, GLfloat);
+GLAPI void APIENTRY glPointParameterfvSGIS (GLenum, const GLfloat *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLPOINTPARAMETERFSGISPROC) (GLenum pname, GLfloat param);
+typedef void (APIENTRYP PFNGLPOINTPARAMETERFVSGISPROC) (GLenum pname, const GLfloat *params);
+#endif
+
+#ifndef GL_SGIX_instruments
+#define GL_SGIX_instruments 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI GLint APIENTRY glGetInstrumentsSGIX (void);
+GLAPI void APIENTRY glInstrumentsBufferSGIX (GLsizei, GLint *);
+GLAPI GLint APIENTRY glPollInstrumentsSGIX (GLint *);
+GLAPI void APIENTRY glReadInstrumentsSGIX (GLint);
+GLAPI void APIENTRY glStartInstrumentsSGIX (void);
+GLAPI void APIENTRY glStopInstrumentsSGIX (GLint);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef GLint (APIENTRYP PFNGLGETINSTRUMENTSSGIXPROC) (void);
+typedef void (APIENTRYP PFNGLINSTRUMENTSBUFFERSGIXPROC) (GLsizei size, GLint *buffer);
+typedef GLint (APIENTRYP PFNGLPOLLINSTRUMENTSSGIXPROC) (GLint *marker_p);
+typedef void (APIENTRYP PFNGLREADINSTRUMENTSSGIXPROC) (GLint marker);
+typedef void (APIENTRYP PFNGLSTARTINSTRUMENTSSGIXPROC) (void);
+typedef void (APIENTRYP PFNGLSTOPINSTRUMENTSSGIXPROC) (GLint marker);
+#endif
+
+#ifndef GL_SGIX_texture_scale_bias
+#define GL_SGIX_texture_scale_bias 1
+#endif
+
+#ifndef GL_SGIX_framezoom
+#define GL_SGIX_framezoom 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glFrameZoomSGIX (GLint);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLFRAMEZOOMSGIXPROC) (GLint factor);
+#endif
+
+#ifndef GL_SGIX_tag_sample_buffer
+#define GL_SGIX_tag_sample_buffer 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glTagSampleBufferSGIX (void);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLTAGSAMPLEBUFFERSGIXPROC) (void);
+#endif
+
+#ifndef GL_SGIX_polynomial_ffd
+#define GL_SGIX_polynomial_ffd 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glDeformationMap3dSGIX (GLenum, GLdouble, GLdouble, GLint, GLint, GLdouble, GLdouble, GLint, GLint, GLdouble, GLdouble, GLint, GLint, const GLdouble *);
+GLAPI void APIENTRY glDeformationMap3fSGIX (GLenum, GLfloat, GLfloat, GLint, GLint, GLfloat, GLfloat, GLint, GLint, GLfloat, GLfloat, GLint, GLint, const GLfloat *);
+GLAPI void APIENTRY glDeformSGIX (GLbitfield);
+GLAPI void APIENTRY glLoadIdentityDeformationMapSGIX (GLbitfield);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLDEFORMATIONMAP3DSGIXPROC) (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, GLdouble w1, GLdouble w2, GLint wstride, GLint worder, const GLdouble *points);
+typedef void (APIENTRYP PFNGLDEFORMATIONMAP3FSGIXPROC) (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, GLfloat w1, GLfloat w2, GLint wstride, GLint worder, const GLfloat *points);
+typedef void (APIENTRYP PFNGLDEFORMSGIXPROC) (GLbitfield mask);
+typedef void (APIENTRYP PFNGLLOADIDENTITYDEFORMATIONMAPSGIXPROC) (GLbitfield mask);
+#endif
+
+#ifndef GL_SGIX_reference_plane
+#define GL_SGIX_reference_plane 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glReferencePlaneSGIX (const GLdouble *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLREFERENCEPLANESGIXPROC) (const GLdouble *equation);
+#endif
+
+#ifndef GL_SGIX_flush_raster
+#define GL_SGIX_flush_raster 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glFlushRasterSGIX (void);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLFLUSHRASTERSGIXPROC) (void);
+#endif
+
+#ifndef GL_SGIX_depth_texture
+#define GL_SGIX_depth_texture 1
+#endif
+
+#ifndef GL_SGIS_fog_function
+#define GL_SGIS_fog_function 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glFogFuncSGIS (GLsizei, const GLfloat *);
+GLAPI void APIENTRY glGetFogFuncSGIS (GLfloat *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLFOGFUNCSGISPROC) (GLsizei n, const GLfloat *points);
+typedef void (APIENTRYP PFNGLGETFOGFUNCSGISPROC) (GLfloat *points);
+#endif
+
+#ifndef GL_SGIX_fog_offset
+#define GL_SGIX_fog_offset 1
+#endif
+
+#ifndef GL_HP_image_transform
+#define GL_HP_image_transform 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glImageTransformParameteriHP (GLenum, GLenum, GLint);
+GLAPI void APIENTRY glImageTransformParameterfHP (GLenum, GLenum, GLfloat);
+GLAPI void APIENTRY glImageTransformParameterivHP (GLenum, GLenum, const GLint *);
+GLAPI void APIENTRY glImageTransformParameterfvHP (GLenum, GLenum, const GLfloat *);
+GLAPI void APIENTRY glGetImageTransformParameterivHP (GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glGetImageTransformParameterfvHP (GLenum, GLenum, GLfloat *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIHPPROC) (GLenum target, GLenum pname, GLint param);
+typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFHPPROC) (GLenum target, GLenum pname, GLfloat param);
+typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIVHPPROC) (GLenum target, GLenum pname, const GLint *params);
+typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFVHPPROC) (GLenum target, GLenum pname, const GLfloat *params);
+typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERIVHPPROC) (GLenum target, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERFVHPPROC) (GLenum target, GLenum pname, GLfloat *params);
+#endif
+
+#ifndef GL_HP_convolution_border_modes
+#define GL_HP_convolution_border_modes 1
+#endif
+
+#ifndef GL_SGIX_texture_add_env
+#define GL_SGIX_texture_add_env 1
+#endif
+
+#ifndef GL_EXT_color_subtable
+#define GL_EXT_color_subtable 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glColorSubTableEXT (GLenum, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *);
+GLAPI void APIENTRY glCopyColorSubTableEXT (GLenum, GLsizei, GLint, GLint, GLsizei);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const GLvoid *data);
+typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width);
+#endif
+
+#ifndef GL_PGI_vertex_hints
+#define GL_PGI_vertex_hints 1
+#endif
+
+#ifndef GL_PGI_misc_hints
+#define GL_PGI_misc_hints 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glHintPGI (GLenum, GLint);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLHINTPGIPROC) (GLenum target, GLint mode);
+#endif
+
+#ifndef GL_EXT_paletted_texture
+#define GL_EXT_paletted_texture 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glColorTableEXT (GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid *);
+GLAPI void APIENTRY glGetColorTableEXT (GLenum, GLenum, GLenum, GLvoid *);
+GLAPI void APIENTRY glGetColorTableParameterivEXT (GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glGetColorTableParameterfvEXT (GLenum, GLenum, GLfloat *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLCOLORTABLEEXTPROC) (GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const GLvoid *table);
+typedef void (APIENTRYP PFNGLGETCOLORTABLEEXTPROC) (GLenum target, GLenum format, GLenum type, GLvoid *data);
+typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params);
+#endif
+
+#ifndef GL_EXT_clip_volume_hint
+#define GL_EXT_clip_volume_hint 1
+#endif
+
+#ifndef GL_SGIX_list_priority
+#define GL_SGIX_list_priority 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glGetListParameterfvSGIX (GLuint, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetListParameterivSGIX (GLuint, GLenum, GLint *);
+GLAPI void APIENTRY glListParameterfSGIX (GLuint, GLenum, GLfloat);
+GLAPI void APIENTRY glListParameterfvSGIX (GLuint, GLenum, const GLfloat *);
+GLAPI void APIENTRY glListParameteriSGIX (GLuint, GLenum, GLint);
+GLAPI void APIENTRY glListParameterivSGIX (GLuint, GLenum, const GLint *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLGETLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLLISTPARAMETERFSGIXPROC) (GLuint list, GLenum pname, GLfloat param);
+typedef void (APIENTRYP PFNGLLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum pname, const GLfloat *params);
+typedef void (APIENTRYP PFNGLLISTPARAMETERISGIXPROC) (GLuint list, GLenum pname, GLint param);
+typedef void (APIENTRYP PFNGLLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum pname, const GLint *params);
+#endif
+
+#ifndef GL_SGIX_ir_instrument1
+#define GL_SGIX_ir_instrument1 1
+#endif
+
+#ifndef GL_SGIX_calligraphic_fragment
+#define GL_SGIX_calligraphic_fragment 1
+#endif
+
+#ifndef GL_SGIX_texture_lod_bias
+#define GL_SGIX_texture_lod_bias 1
+#endif
+
+#ifndef GL_SGIX_shadow_ambient
+#define GL_SGIX_shadow_ambient 1
+#endif
+
+#ifndef GL_EXT_index_texture
+#define GL_EXT_index_texture 1
+#endif
+
+#ifndef GL_EXT_index_material
+#define GL_EXT_index_material 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glIndexMaterialEXT (GLenum, GLenum);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLINDEXMATERIALEXTPROC) (GLenum face, GLenum mode);
+#endif
+
+#ifndef GL_EXT_index_func
+#define GL_EXT_index_func 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glIndexFuncEXT (GLenum, GLclampf);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLINDEXFUNCEXTPROC) (GLenum func, GLclampf ref);
+#endif
+
+#ifndef GL_EXT_index_array_formats
+#define GL_EXT_index_array_formats 1
+#endif
+
+#ifndef GL_EXT_compiled_vertex_array
+#define GL_EXT_compiled_vertex_array 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glLockArraysEXT (GLint, GLsizei);
+GLAPI void APIENTRY glUnlockArraysEXT (void);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLLOCKARRAYSEXTPROC) (GLint first, GLsizei count);
+typedef void (APIENTRYP PFNGLUNLOCKARRAYSEXTPROC) (void);
+#endif
+
+#ifndef GL_EXT_cull_vertex
+#define GL_EXT_cull_vertex 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glCullParameterdvEXT (GLenum, GLdouble *);
+GLAPI void APIENTRY glCullParameterfvEXT (GLenum, GLfloat *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLCULLPARAMETERDVEXTPROC) (GLenum pname, GLdouble *params);
+typedef void (APIENTRYP PFNGLCULLPARAMETERFVEXTPROC) (GLenum pname, GLfloat *params);
+#endif
+
+#ifndef GL_SGIX_ycrcb
+#define GL_SGIX_ycrcb 1
+#endif
+
+#ifndef GL_SGIX_fragment_lighting
+#define GL_SGIX_fragment_lighting 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glFragmentColorMaterialSGIX (GLenum, GLenum);
+GLAPI void APIENTRY glFragmentLightfSGIX (GLenum, GLenum, GLfloat);
+GLAPI void APIENTRY glFragmentLightfvSGIX (GLenum, GLenum, const GLfloat *);
+GLAPI void APIENTRY glFragmentLightiSGIX (GLenum, GLenum, GLint);
+GLAPI void APIENTRY glFragmentLightivSGIX (GLenum, GLenum, const GLint *);
+GLAPI void APIENTRY glFragmentLightModelfSGIX (GLenum, GLfloat);
+GLAPI void APIENTRY glFragmentLightModelfvSGIX (GLenum, const GLfloat *);
+GLAPI void APIENTRY glFragmentLightModeliSGIX (GLenum, GLint);
+GLAPI void APIENTRY glFragmentLightModelivSGIX (GLenum, const GLint *);
+GLAPI void APIENTRY glFragmentMaterialfSGIX (GLenum, GLenum, GLfloat);
+GLAPI void APIENTRY glFragmentMaterialfvSGIX (GLenum, GLenum, const GLfloat *);
+GLAPI void APIENTRY glFragmentMaterialiSGIX (GLenum, GLenum, GLint);
+GLAPI void APIENTRY glFragmentMaterialivSGIX (GLenum, GLenum, const GLint *);
+GLAPI void APIENTRY glGetFragmentLightfvSGIX (GLenum, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetFragmentLightivSGIX (GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glGetFragmentMaterialfvSGIX (GLenum, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetFragmentMaterialivSGIX (GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glLightEnviSGIX (GLenum, GLint);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLFRAGMENTCOLORMATERIALSGIXPROC) (GLenum face, GLenum mode);
+typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFSGIXPROC) (GLenum light, GLenum pname, GLfloat param);
+typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, const GLfloat *params);
+typedef void (APIENTRYP PFNGLFRAGMENTLIGHTISGIXPROC) (GLenum light, GLenum pname, GLint param);
+typedef void (APIENTRYP PFNGLFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, const GLint *params);
+typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFSGIXPROC) (GLenum pname, GLfloat param);
+typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFVSGIXPROC) (GLenum pname, const GLfloat *params);
+typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELISGIXPROC) (GLenum pname, GLint param);
+typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELIVSGIXPROC) (GLenum pname, const GLint *params);
+typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFSGIXPROC) (GLenum face, GLenum pname, GLfloat param);
+typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, const GLfloat *params);
+typedef void (APIENTRYP PFNGLFRAGMENTMATERIALISGIXPROC) (GLenum face, GLenum pname, GLint param);
+typedef void (APIENTRYP PFNGLFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, const GLint *params);
+typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLLIGHTENVISGIXPROC) (GLenum pname, GLint param);
+#endif
+
+#ifndef GL_IBM_rasterpos_clip
+#define GL_IBM_rasterpos_clip 1
+#endif
+
+#ifndef GL_HP_texture_lighting
+#define GL_HP_texture_lighting 1
+#endif
+
+#ifndef GL_EXT_draw_range_elements
+#define GL_EXT_draw_range_elements 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glDrawRangeElementsEXT (GLenum, GLuint, GLuint, GLsizei, GLenum, const GLvoid *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSEXTPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices);
+#endif
+
+#ifndef GL_WIN_phong_shading
+#define GL_WIN_phong_shading 1
+#endif
+
+#ifndef GL_WIN_specular_fog
+#define GL_WIN_specular_fog 1
+#endif
+
+#ifndef GL_EXT_light_texture
+#define GL_EXT_light_texture 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glApplyTextureEXT (GLenum);
+GLAPI void APIENTRY glTextureLightEXT (GLenum);
+GLAPI void APIENTRY glTextureMaterialEXT (GLenum, GLenum);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLAPPLYTEXTUREEXTPROC) (GLenum mode);
+typedef void (APIENTRYP PFNGLTEXTURELIGHTEXTPROC) (GLenum pname);
+typedef void (APIENTRYP PFNGLTEXTUREMATERIALEXTPROC) (GLenum face, GLenum mode);
+#endif
+
+#ifndef GL_SGIX_blend_alpha_minmax
+#define GL_SGIX_blend_alpha_minmax 1
+#endif
+
+#ifndef GL_EXT_bgra
+#define GL_EXT_bgra 1
+#endif
+
+#ifndef GL_SGIX_async
+#define GL_SGIX_async 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glAsyncMarkerSGIX (GLuint);
+GLAPI GLint APIENTRY glFinishAsyncSGIX (GLuint *);
+GLAPI GLint APIENTRY glPollAsyncSGIX (GLuint *);
+GLAPI GLuint APIENTRY glGenAsyncMarkersSGIX (GLsizei);
+GLAPI void APIENTRY glDeleteAsyncMarkersSGIX (GLuint, GLsizei);
+GLAPI GLboolean APIENTRY glIsAsyncMarkerSGIX (GLuint);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLASYNCMARKERSGIXPROC) (GLuint marker);
+typedef GLint (APIENTRYP PFNGLFINISHASYNCSGIXPROC) (GLuint *markerp);
+typedef GLint (APIENTRYP PFNGLPOLLASYNCSGIXPROC) (GLuint *markerp);
+typedef GLuint (APIENTRYP PFNGLGENASYNCMARKERSSGIXPROC) (GLsizei range);
+typedef void (APIENTRYP PFNGLDELETEASYNCMARKERSSGIXPROC) (GLuint marker, GLsizei range);
+typedef GLboolean (APIENTRYP PFNGLISASYNCMARKERSGIXPROC) (GLuint marker);
+#endif
+
+#ifndef GL_SGIX_async_pixel
+#define GL_SGIX_async_pixel 1
+#endif
+
+#ifndef GL_SGIX_async_histogram
+#define GL_SGIX_async_histogram 1
+#endif
+
+#ifndef GL_INTEL_parallel_arrays
+#define GL_INTEL_parallel_arrays 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glVertexPointervINTEL (GLint, GLenum, const GLvoid* *);
+GLAPI void APIENTRY glNormalPointervINTEL (GLenum, const GLvoid* *);
+GLAPI void APIENTRY glColorPointervINTEL (GLint, GLenum, const GLvoid* *);
+GLAPI void APIENTRY glTexCoordPointervINTEL (GLint, GLenum, const GLvoid* *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLVERTEXPOINTERVINTELPROC) (GLint size, GLenum type, const GLvoid* *pointer);
+typedef void (APIENTRYP PFNGLNORMALPOINTERVINTELPROC) (GLenum type, const GLvoid* *pointer);
+typedef void (APIENTRYP PFNGLCOLORPOINTERVINTELPROC) (GLint size, GLenum type, const GLvoid* *pointer);
+typedef void (APIENTRYP PFNGLTEXCOORDPOINTERVINTELPROC) (GLint size, GLenum type, const GLvoid* *pointer);
+#endif
+
+#ifndef GL_HP_occlusion_test
+#define GL_HP_occlusion_test 1
+#endif
+
+#ifndef GL_EXT_pixel_transform
+#define GL_EXT_pixel_transform 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glPixelTransformParameteriEXT (GLenum, GLenum, GLint);
+GLAPI void APIENTRY glPixelTransformParameterfEXT (GLenum, GLenum, GLfloat);
+GLAPI void APIENTRY glPixelTransformParameterivEXT (GLenum, GLenum, const GLint *);
+GLAPI void APIENTRY glPixelTransformParameterfvEXT (GLenum, GLenum, const GLfloat *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint param);
+typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat param);
+typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params);
+typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params);
+#endif
+
+#ifndef GL_EXT_pixel_transform_color_table
+#define GL_EXT_pixel_transform_color_table 1
+#endif
+
+#ifndef GL_EXT_shared_texture_palette
+#define GL_EXT_shared_texture_palette 1
+#endif
+
+#ifndef GL_EXT_separate_specular_color
+#define GL_EXT_separate_specular_color 1
+#endif
+
+#ifndef GL_EXT_secondary_color
+#define GL_EXT_secondary_color 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glSecondaryColor3bEXT (GLbyte, GLbyte, GLbyte);
+GLAPI void APIENTRY glSecondaryColor3bvEXT (const GLbyte *);
+GLAPI void APIENTRY glSecondaryColor3dEXT (GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glSecondaryColor3dvEXT (const GLdouble *);
+GLAPI void APIENTRY glSecondaryColor3fEXT (GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glSecondaryColor3fvEXT (const GLfloat *);
+GLAPI void APIENTRY glSecondaryColor3iEXT (GLint, GLint, GLint);
+GLAPI void APIENTRY glSecondaryColor3ivEXT (const GLint *);
+GLAPI void APIENTRY glSecondaryColor3sEXT (GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glSecondaryColor3svEXT (const GLshort *);
+GLAPI void APIENTRY glSecondaryColor3ubEXT (GLubyte, GLubyte, GLubyte);
+GLAPI void APIENTRY glSecondaryColor3ubvEXT (const GLubyte *);
+GLAPI void APIENTRY glSecondaryColor3uiEXT (GLuint, GLuint, GLuint);
+GLAPI void APIENTRY glSecondaryColor3uivEXT (const GLuint *);
+GLAPI void APIENTRY glSecondaryColor3usEXT (GLushort, GLushort, GLushort);
+GLAPI void APIENTRY glSecondaryColor3usvEXT (const GLushort *);
+GLAPI void APIENTRY glSecondaryColorPointerEXT (GLint, GLenum, GLsizei, const GLvoid *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BEXTPROC) (GLbyte red, GLbyte green, GLbyte blue);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVEXTPROC) (const GLbyte *v);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DEXTPROC) (GLdouble red, GLdouble green, GLdouble blue);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVEXTPROC) (const GLdouble *v);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FEXTPROC) (GLfloat red, GLfloat green, GLfloat blue);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVEXTPROC) (const GLfloat *v);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IEXTPROC) (GLint red, GLint green, GLint blue);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVEXTPROC) (const GLint *v);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SEXTPROC) (GLshort red, GLshort green, GLshort blue);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVEXTPROC) (const GLshort *v);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBEXTPROC) (GLubyte red, GLubyte green, GLubyte blue);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVEXTPROC) (const GLubyte *v);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIEXTPROC) (GLuint red, GLuint green, GLuint blue);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVEXTPROC) (const GLuint *v);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USEXTPROC) (GLushort red, GLushort green, GLushort blue);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVEXTPROC) (const GLushort *v);
+typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
+#endif
+
+#ifndef GL_EXT_texture_perturb_normal
+#define GL_EXT_texture_perturb_normal 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glTextureNormalEXT (GLenum);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLTEXTURENORMALEXTPROC) (GLenum mode);
+#endif
+
+#ifndef GL_EXT_multi_draw_arrays
+#define GL_EXT_multi_draw_arrays 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glMultiDrawArraysEXT (GLenum, GLint *, GLsizei *, GLsizei);
+GLAPI void APIENTRY glMultiDrawElementsEXT (GLenum, const GLsizei *, GLenum, const GLvoid* *, GLsizei);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC) (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount);
+typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount);
+#endif
+
+#ifndef GL_EXT_fog_coord
+#define GL_EXT_fog_coord 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glFogCoordfEXT (GLfloat);
+GLAPI void APIENTRY glFogCoordfvEXT (const GLfloat *);
+GLAPI void APIENTRY glFogCoorddEXT (GLdouble);
+GLAPI void APIENTRY glFogCoorddvEXT (const GLdouble *);
+GLAPI void APIENTRY glFogCoordPointerEXT (GLenum, GLsizei, const GLvoid *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLFOGCOORDFEXTPROC) (GLfloat coord);
+typedef void (APIENTRYP PFNGLFOGCOORDFVEXTPROC) (const GLfloat *coord);
+typedef void (APIENTRYP PFNGLFOGCOORDDEXTPROC) (GLdouble coord);
+typedef void (APIENTRYP PFNGLFOGCOORDDVEXTPROC) (const GLdouble *coord);
+typedef void (APIENTRYP PFNGLFOGCOORDPOINTEREXTPROC) (GLenum type, GLsizei stride, const GLvoid *pointer);
+#endif
+
+#ifndef GL_REND_screen_coordinates
+#define GL_REND_screen_coordinates 1
+#endif
+
+#ifndef GL_EXT_coordinate_frame
+#define GL_EXT_coordinate_frame 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glTangent3bEXT (GLbyte, GLbyte, GLbyte);
+GLAPI void APIENTRY glTangent3bvEXT (const GLbyte *);
+GLAPI void APIENTRY glTangent3dEXT (GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glTangent3dvEXT (const GLdouble *);
+GLAPI void APIENTRY glTangent3fEXT (GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glTangent3fvEXT (const GLfloat *);
+GLAPI void APIENTRY glTangent3iEXT (GLint, GLint, GLint);
+GLAPI void APIENTRY glTangent3ivEXT (const GLint *);
+GLAPI void APIENTRY glTangent3sEXT (GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glTangent3svEXT (const GLshort *);
+GLAPI void APIENTRY glBinormal3bEXT (GLbyte, GLbyte, GLbyte);
+GLAPI void APIENTRY glBinormal3bvEXT (const GLbyte *);
+GLAPI void APIENTRY glBinormal3dEXT (GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glBinormal3dvEXT (const GLdouble *);
+GLAPI void APIENTRY glBinormal3fEXT (GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glBinormal3fvEXT (const GLfloat *);
+GLAPI void APIENTRY glBinormal3iEXT (GLint, GLint, GLint);
+GLAPI void APIENTRY glBinormal3ivEXT (const GLint *);
+GLAPI void APIENTRY glBinormal3sEXT (GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glBinormal3svEXT (const GLshort *);
+GLAPI void APIENTRY glTangentPointerEXT (GLenum, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glBinormalPointerEXT (GLenum, GLsizei, const GLvoid *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLTANGENT3BEXTPROC) (GLbyte tx, GLbyte ty, GLbyte tz);
+typedef void (APIENTRYP PFNGLTANGENT3BVEXTPROC) (const GLbyte *v);
+typedef void (APIENTRYP PFNGLTANGENT3DEXTPROC) (GLdouble tx, GLdouble ty, GLdouble tz);
+typedef void (APIENTRYP PFNGLTANGENT3DVEXTPROC) (const GLdouble *v);
+typedef void (APIENTRYP PFNGLTANGENT3FEXTPROC) (GLfloat tx, GLfloat ty, GLfloat tz);
+typedef void (APIENTRYP PFNGLTANGENT3FVEXTPROC) (const GLfloat *v);
+typedef void (APIENTRYP PFNGLTANGENT3IEXTPROC) (GLint tx, GLint ty, GLint tz);
+typedef void (APIENTRYP PFNGLTANGENT3IVEXTPROC) (const GLint *v);
+typedef void (APIENTRYP PFNGLTANGENT3SEXTPROC) (GLshort tx, GLshort ty, GLshort tz);
+typedef void (APIENTRYP PFNGLTANGENT3SVEXTPROC) (const GLshort *v);
+typedef void (APIENTRYP PFNGLBINORMAL3BEXTPROC) (GLbyte bx, GLbyte by, GLbyte bz);
+typedef void (APIENTRYP PFNGLBINORMAL3BVEXTPROC) (const GLbyte *v);
+typedef void (APIENTRYP PFNGLBINORMAL3DEXTPROC) (GLdouble bx, GLdouble by, GLdouble bz);
+typedef void (APIENTRYP PFNGLBINORMAL3DVEXTPROC) (const GLdouble *v);
+typedef void (APIENTRYP PFNGLBINORMAL3FEXTPROC) (GLfloat bx, GLfloat by, GLfloat bz);
+typedef void (APIENTRYP PFNGLBINORMAL3FVEXTPROC) (const GLfloat *v);
+typedef void (APIENTRYP PFNGLBINORMAL3IEXTPROC) (GLint bx, GLint by, GLint bz);
+typedef void (APIENTRYP PFNGLBINORMAL3IVEXTPROC) (const GLint *v);
+typedef void (APIENTRYP PFNGLBINORMAL3SEXTPROC) (GLshort bx, GLshort by, GLshort bz);
+typedef void (APIENTRYP PFNGLBINORMAL3SVEXTPROC) (const GLshort *v);
+typedef void (APIENTRYP PFNGLTANGENTPOINTEREXTPROC) (GLenum type, GLsizei stride, const GLvoid *pointer);
+typedef void (APIENTRYP PFNGLBINORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, const GLvoid *pointer);
+#endif
+
+#ifndef GL_EXT_texture_env_combine
+#define GL_EXT_texture_env_combine 1
+#endif
+
+#ifndef GL_APPLE_specular_vector
+#define GL_APPLE_specular_vector 1
+#endif
+
+#ifndef GL_APPLE_transform_hint
+#define GL_APPLE_transform_hint 1
+#endif
+
+#ifndef GL_SGIX_fog_scale
+#define GL_SGIX_fog_scale 1
+#endif
+
+#ifndef GL_SUNX_constant_data
+#define GL_SUNX_constant_data 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glFinishTextureSUNX (void);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLFINISHTEXTURESUNXPROC) (void);
+#endif
+
+#ifndef GL_SUN_global_alpha
+#define GL_SUN_global_alpha 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glGlobalAlphaFactorbSUN (GLbyte);
+GLAPI void APIENTRY glGlobalAlphaFactorsSUN (GLshort);
+GLAPI void APIENTRY glGlobalAlphaFactoriSUN (GLint);
+GLAPI void APIENTRY glGlobalAlphaFactorfSUN (GLfloat);
+GLAPI void APIENTRY glGlobalAlphaFactordSUN (GLdouble);
+GLAPI void APIENTRY glGlobalAlphaFactorubSUN (GLubyte);
+GLAPI void APIENTRY glGlobalAlphaFactorusSUN (GLushort);
+GLAPI void APIENTRY glGlobalAlphaFactoruiSUN (GLuint);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORBSUNPROC) (GLbyte factor);
+typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORSSUNPROC) (GLshort factor);
+typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORISUNPROC) (GLint factor);
+typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORFSUNPROC) (GLfloat factor);
+typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORDSUNPROC) (GLdouble factor);
+typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUBSUNPROC) (GLubyte factor);
+typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUSSUNPROC) (GLushort factor);
+typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUISUNPROC) (GLuint factor);
+#endif
+
+#ifndef GL_SUN_triangle_list
+#define GL_SUN_triangle_list 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glReplacementCodeuiSUN (GLuint);
+GLAPI void APIENTRY glReplacementCodeusSUN (GLushort);
+GLAPI void APIENTRY glReplacementCodeubSUN (GLubyte);
+GLAPI void APIENTRY glReplacementCodeuivSUN (const GLuint *);
+GLAPI void APIENTRY glReplacementCodeusvSUN (const GLushort *);
+GLAPI void APIENTRY glReplacementCodeubvSUN (const GLubyte *);
+GLAPI void APIENTRY glReplacementCodePointerSUN (GLenum, GLsizei, const GLvoid* *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUISUNPROC) (GLuint code);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSSUNPROC) (GLushort code);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBSUNPROC) (GLubyte code);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVSUNPROC) (const GLuint *code);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSVSUNPROC) (const GLushort *code);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBVSUNPROC) (const GLubyte *code);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEPOINTERSUNPROC) (GLenum type, GLsizei stride, const GLvoid* *pointer);
+#endif
+
+#ifndef GL_SUN_vertex
+#define GL_SUN_vertex 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glColor4ubVertex2fSUN (GLubyte, GLubyte, GLubyte, GLubyte, GLfloat, GLfloat);
+GLAPI void APIENTRY glColor4ubVertex2fvSUN (const GLubyte *, const GLfloat *);
+GLAPI void APIENTRY glColor4ubVertex3fSUN (GLubyte, GLubyte, GLubyte, GLubyte, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glColor4ubVertex3fvSUN (const GLubyte *, const GLfloat *);
+GLAPI void APIENTRY glColor3fVertex3fSUN (GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glColor3fVertex3fvSUN (const GLfloat *, const GLfloat *);
+GLAPI void APIENTRY glNormal3fVertex3fSUN (GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glNormal3fVertex3fvSUN (const GLfloat *, const GLfloat *);
+GLAPI void APIENTRY glColor4fNormal3fVertex3fSUN (GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glColor4fNormal3fVertex3fvSUN (const GLfloat *, const GLfloat *, const GLfloat *);
+GLAPI void APIENTRY glTexCoord2fVertex3fSUN (GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glTexCoord2fVertex3fvSUN (const GLfloat *, const GLfloat *);
+GLAPI void APIENTRY glTexCoord4fVertex4fSUN (GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glTexCoord4fVertex4fvSUN (const GLfloat *, const GLfloat *);
+GLAPI void APIENTRY glTexCoord2fColor4ubVertex3fSUN (GLfloat, GLfloat, GLubyte, GLubyte, GLubyte, GLubyte, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glTexCoord2fColor4ubVertex3fvSUN (const GLfloat *, const GLubyte *, const GLfloat *);
+GLAPI void APIENTRY glTexCoord2fColor3fVertex3fSUN (GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glTexCoord2fColor3fVertex3fvSUN (const GLfloat *, const GLfloat *, const GLfloat *);
+GLAPI void APIENTRY glTexCoord2fNormal3fVertex3fSUN (GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glTexCoord2fNormal3fVertex3fvSUN (const GLfloat *, const GLfloat *, const GLfloat *);
+GLAPI void APIENTRY glTexCoord2fColor4fNormal3fVertex3fSUN (GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glTexCoord2fColor4fNormal3fVertex3fvSUN (const GLfloat *, const GLfloat *, const GLfloat *, const GLfloat *);
+GLAPI void APIENTRY glTexCoord4fColor4fNormal3fVertex4fSUN (GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glTexCoord4fColor4fNormal3fVertex4fvSUN (const GLfloat *, const GLfloat *, const GLfloat *, const GLfloat *);
+GLAPI void APIENTRY glReplacementCodeuiVertex3fSUN (GLuint, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glReplacementCodeuiVertex3fvSUN (const GLuint *, const GLfloat *);
+GLAPI void APIENTRY glReplacementCodeuiColor4ubVertex3fSUN (GLuint, GLubyte, GLubyte, GLubyte, GLubyte, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glReplacementCodeuiColor4ubVertex3fvSUN (const GLuint *, const GLubyte *, const GLfloat *);
+GLAPI void APIENTRY glReplacementCodeuiColor3fVertex3fSUN (GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glReplacementCodeuiColor3fVertex3fvSUN (const GLuint *, const GLfloat *, const GLfloat *);
+GLAPI void APIENTRY glReplacementCodeuiNormal3fVertex3fSUN (GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glReplacementCodeuiNormal3fVertex3fvSUN (const GLuint *, const GLfloat *, const GLfloat *);
+GLAPI void APIENTRY glReplacementCodeuiColor4fNormal3fVertex3fSUN (GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glReplacementCodeuiColor4fNormal3fVertex3fvSUN (const GLuint *, const GLfloat *, const GLfloat *, const GLfloat *);
+GLAPI void APIENTRY glReplacementCodeuiTexCoord2fVertex3fSUN (GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glReplacementCodeuiTexCoord2fVertex3fvSUN (const GLuint *, const GLfloat *, const GLfloat *);
+GLAPI void APIENTRY glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN (GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN (const GLuint *, const GLfloat *, const GLfloat *, const GLfloat *);
+GLAPI void APIENTRY glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN (GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN (const GLuint *, const GLfloat *, const GLfloat *, const GLfloat *, const GLfloat *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FSUNPROC) (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y);
+typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FVSUNPROC) (const GLubyte *c, const GLfloat *v);
+typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FSUNPROC) (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FVSUNPROC) (const GLubyte *c, const GLfloat *v);
+typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FSUNPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FVSUNPROC) (const GLfloat *c, const GLfloat *v);
+typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FSUNPROC) (GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *n, const GLfloat *v);
+typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *c, const GLfloat *n, const GLfloat *v);
+typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *v);
+typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FSUNPROC) (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FVSUNPROC) (const GLfloat *tc, const GLfloat *v);
+typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FVSUNPROC) (const GLfloat *tc, const GLubyte *c, const GLfloat *v);
+typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *v);
+typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *n, const GLfloat *v);
+typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v);
+typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FSUNPROC) (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FSUNPROC) (GLuint rc, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *v);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FSUNPROC) (GLuint rc, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FVSUNPROC) (const GLuint *rc, const GLubyte *c, const GLfloat *v);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FSUNPROC) (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *c, const GLfloat *v);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *n, const GLfloat *v);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *c, const GLfloat *n, const GLfloat *v);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *v);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *n, const GLfloat *v);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v);
+#endif
+
+#ifndef GL_EXT_blend_func_separate
+#define GL_EXT_blend_func_separate 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glBlendFuncSeparateEXT (GLenum, GLenum, GLenum, GLenum);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEEXTPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
+#endif
+
+#ifndef GL_INGR_blend_func_separate
+#define GL_INGR_blend_func_separate 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glBlendFuncSeparateINGR (GLenum, GLenum, GLenum, GLenum);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINGRPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
+#endif
+
+#ifndef GL_INGR_color_clamp
+#define GL_INGR_color_clamp 1
+#endif
+
+#ifndef GL_INGR_interlace_read
+#define GL_INGR_interlace_read 1
+#endif
+
+#ifndef GL_EXT_stencil_wrap
+#define GL_EXT_stencil_wrap 1
+#endif
+
+#ifndef GL_EXT_422_pixels
+#define GL_EXT_422_pixels 1
+#endif
+
+#ifndef GL_NV_texgen_reflection
+#define GL_NV_texgen_reflection 1
+#endif
+
+#ifndef GL_SUN_convolution_border_modes
+#define GL_SUN_convolution_border_modes 1
+#endif
+
+#ifndef GL_EXT_texture_env_add
+#define GL_EXT_texture_env_add 1
+#endif
+
+#ifndef GL_EXT_texture_lod_bias
+#define GL_EXT_texture_lod_bias 1
+#endif
+
+#ifndef GL_EXT_texture_filter_anisotropic
+#define GL_EXT_texture_filter_anisotropic 1
+#endif
+
+#ifndef GL_EXT_vertex_weighting
+#define GL_EXT_vertex_weighting 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glVertexWeightfEXT (GLfloat);
+GLAPI void APIENTRY glVertexWeightfvEXT (const GLfloat *);
+GLAPI void APIENTRY glVertexWeightPointerEXT (GLsizei, GLenum, GLsizei, const GLvoid *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLVERTEXWEIGHTFEXTPROC) (GLfloat weight);
+typedef void (APIENTRYP PFNGLVERTEXWEIGHTFVEXTPROC) (const GLfloat *weight);
+typedef void (APIENTRYP PFNGLVERTEXWEIGHTPOINTEREXTPROC) (GLsizei size, GLenum type, GLsizei stride, const GLvoid *pointer);
+#endif
+
+#ifndef GL_NV_light_max_exponent
+#define GL_NV_light_max_exponent 1
+#endif
+
+#ifndef GL_NV_vertex_array_range
+#define GL_NV_vertex_array_range 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glFlushVertexArrayRangeNV (void);
+GLAPI void APIENTRY glVertexArrayRangeNV (GLsizei, const GLvoid *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGENVPROC) (void);
+typedef void (APIENTRYP PFNGLVERTEXARRAYRANGENVPROC) (GLsizei length, const GLvoid *pointer);
+#endif
+
+#ifndef GL_NV_register_combiners
+#define GL_NV_register_combiners 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glCombinerParameterfvNV (GLenum, const GLfloat *);
+GLAPI void APIENTRY glCombinerParameterfNV (GLenum, GLfloat);
+GLAPI void APIENTRY glCombinerParameterivNV (GLenum, const GLint *);
+GLAPI void APIENTRY glCombinerParameteriNV (GLenum, GLint);
+GLAPI void APIENTRY glCombinerInputNV (GLenum, GLenum, GLenum, GLenum, GLenum, GLenum);
+GLAPI void APIENTRY glCombinerOutputNV (GLenum, GLenum, GLenum, GLenum, GLenum, GLenum, GLenum, GLboolean, GLboolean, GLboolean);
+GLAPI void APIENTRY glFinalCombinerInputNV (GLenum, GLenum, GLenum, GLenum);
+GLAPI void APIENTRY glGetCombinerInputParameterfvNV (GLenum, GLenum, GLenum, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetCombinerInputParameterivNV (GLenum, GLenum, GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glGetCombinerOutputParameterfvNV (GLenum, GLenum, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetCombinerOutputParameterivNV (GLenum, GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glGetFinalCombinerInputParameterfvNV (GLenum, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetFinalCombinerInputParameterivNV (GLenum, GLenum, GLint *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFVNVPROC) (GLenum pname, const GLfloat *params);
+typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFNVPROC) (GLenum pname, GLfloat param);
+typedef void (APIENTRYP PFNGLCOMBINERPARAMETERIVNVPROC) (GLenum pname, const GLint *params);
+typedef void (APIENTRYP PFNGLCOMBINERPARAMETERINVPROC) (GLenum pname, GLint param);
+typedef void (APIENTRYP PFNGLCOMBINERINPUTNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage);
+typedef void (APIENTRYP PFNGLCOMBINEROUTPUTNVPROC) (GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum);
+typedef void (APIENTRYP PFNGLFINALCOMBINERINPUTNVPROC) (GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage);
+typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC) (GLenum variable, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC) (GLenum variable, GLenum pname, GLint *params);
+#endif
+
+#ifndef GL_NV_fog_distance
+#define GL_NV_fog_distance 1
+#endif
+
+#ifndef GL_NV_texgen_emboss
+#define GL_NV_texgen_emboss 1
+#endif
+
+#ifndef GL_NV_blend_square
+#define GL_NV_blend_square 1
+#endif
+
+#ifndef GL_NV_texture_env_combine4
+#define GL_NV_texture_env_combine4 1
+#endif
+
+#ifndef GL_MESA_resize_buffers
+#define GL_MESA_resize_buffers 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glResizeBuffersMESA (void);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLRESIZEBUFFERSMESAPROC) (void);
+#endif
+
+#ifndef GL_MESA_window_pos
+#define GL_MESA_window_pos 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glWindowPos2dMESA (GLdouble, GLdouble);
+GLAPI void APIENTRY glWindowPos2dvMESA (const GLdouble *);
+GLAPI void APIENTRY glWindowPos2fMESA (GLfloat, GLfloat);
+GLAPI void APIENTRY glWindowPos2fvMESA (const GLfloat *);
+GLAPI void APIENTRY glWindowPos2iMESA (GLint, GLint);
+GLAPI void APIENTRY glWindowPos2ivMESA (const GLint *);
+GLAPI void APIENTRY glWindowPos2sMESA (GLshort, GLshort);
+GLAPI void APIENTRY glWindowPos2svMESA (const GLshort *);
+GLAPI void APIENTRY glWindowPos3dMESA (GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glWindowPos3dvMESA (const GLdouble *);
+GLAPI void APIENTRY glWindowPos3fMESA (GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glWindowPos3fvMESA (const GLfloat *);
+GLAPI void APIENTRY glWindowPos3iMESA (GLint, GLint, GLint);
+GLAPI void APIENTRY glWindowPos3ivMESA (const GLint *);
+GLAPI void APIENTRY glWindowPos3sMESA (GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glWindowPos3svMESA (const GLshort *);
+GLAPI void APIENTRY glWindowPos4dMESA (GLdouble, GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glWindowPos4dvMESA (const GLdouble *);
+GLAPI void APIENTRY glWindowPos4fMESA (GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glWindowPos4fvMESA (const GLfloat *);
+GLAPI void APIENTRY glWindowPos4iMESA (GLint, GLint, GLint, GLint);
+GLAPI void APIENTRY glWindowPos4ivMESA (const GLint *);
+GLAPI void APIENTRY glWindowPos4sMESA (GLshort, GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glWindowPos4svMESA (const GLshort *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLWINDOWPOS2DMESAPROC) (GLdouble x, GLdouble y);
+typedef void (APIENTRYP PFNGLWINDOWPOS2DVMESAPROC) (const GLdouble *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS2FMESAPROC) (GLfloat x, GLfloat y);
+typedef void (APIENTRYP PFNGLWINDOWPOS2FVMESAPROC) (const GLfloat *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS2IMESAPROC) (GLint x, GLint y);
+typedef void (APIENTRYP PFNGLWINDOWPOS2IVMESAPROC) (const GLint *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS2SMESAPROC) (GLshort x, GLshort y);
+typedef void (APIENTRYP PFNGLWINDOWPOS2SVMESAPROC) (const GLshort *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS3DMESAPROC) (GLdouble x, GLdouble y, GLdouble z);
+typedef void (APIENTRYP PFNGLWINDOWPOS3DVMESAPROC) (const GLdouble *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS3FMESAPROC) (GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLWINDOWPOS3FVMESAPROC) (const GLfloat *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS3IMESAPROC) (GLint x, GLint y, GLint z);
+typedef void (APIENTRYP PFNGLWINDOWPOS3IVMESAPROC) (const GLint *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS3SMESAPROC) (GLshort x, GLshort y, GLshort z);
+typedef void (APIENTRYP PFNGLWINDOWPOS3SVMESAPROC) (const GLshort *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS4DMESAPROC) (GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+typedef void (APIENTRYP PFNGLWINDOWPOS4DVMESAPROC) (const GLdouble *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS4FMESAPROC) (GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+typedef void (APIENTRYP PFNGLWINDOWPOS4FVMESAPROC) (const GLfloat *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS4IMESAPROC) (GLint x, GLint y, GLint z, GLint w);
+typedef void (APIENTRYP PFNGLWINDOWPOS4IVMESAPROC) (const GLint *v);
+typedef void (APIENTRYP PFNGLWINDOWPOS4SMESAPROC) (GLshort x, GLshort y, GLshort z, GLshort w);
+typedef void (APIENTRYP PFNGLWINDOWPOS4SVMESAPROC) (const GLshort *v);
+#endif
+
+#ifndef GL_IBM_cull_vertex
+#define GL_IBM_cull_vertex 1
+#endif
+
+#ifndef GL_IBM_multimode_draw_arrays
+#define GL_IBM_multimode_draw_arrays 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glMultiModeDrawArraysIBM (const GLenum *, const GLint *, const GLsizei *, GLsizei, GLint);
+GLAPI void APIENTRY glMultiModeDrawElementsIBM (const GLenum *, const GLsizei *, GLenum, const GLvoid* const *, GLsizei, GLint);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLMULTIMODEDRAWARRAYSIBMPROC) (const GLenum *mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride);
+typedef void (APIENTRYP PFNGLMULTIMODEDRAWELEMENTSIBMPROC) (const GLenum *mode, const GLsizei *count, GLenum type, const GLvoid* const *indices, GLsizei primcount, GLint modestride);
+#endif
+
+#ifndef GL_IBM_vertex_array_lists
+#define GL_IBM_vertex_array_lists 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glColorPointerListIBM (GLint, GLenum, GLint, const GLvoid* *, GLint);
+GLAPI void APIENTRY glSecondaryColorPointerListIBM (GLint, GLenum, GLint, const GLvoid* *, GLint);
+GLAPI void APIENTRY glEdgeFlagPointerListIBM (GLint, const GLboolean* *, GLint);
+GLAPI void APIENTRY glFogCoordPointerListIBM (GLenum, GLint, const GLvoid* *, GLint);
+GLAPI void APIENTRY glIndexPointerListIBM (GLenum, GLint, const GLvoid* *, GLint);
+GLAPI void APIENTRY glNormalPointerListIBM (GLenum, GLint, const GLvoid* *, GLint);
+GLAPI void APIENTRY glTexCoordPointerListIBM (GLint, GLenum, GLint, const GLvoid* *, GLint);
+GLAPI void APIENTRY glVertexPointerListIBM (GLint, GLenum, GLint, const GLvoid* *, GLint);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
+typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
+typedef void (APIENTRYP PFNGLEDGEFLAGPOINTERLISTIBMPROC) (GLint stride, const GLboolean* *pointer, GLint ptrstride);
+typedef void (APIENTRYP PFNGLFOGCOORDPOINTERLISTIBMPROC) (GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
+typedef void (APIENTRYP PFNGLINDEXPOINTERLISTIBMPROC) (GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
+typedef void (APIENTRYP PFNGLNORMALPOINTERLISTIBMPROC) (GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
+typedef void (APIENTRYP PFNGLTEXCOORDPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
+typedef void (APIENTRYP PFNGLVERTEXPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
+#endif
+
+#ifndef GL_SGIX_subsample
+#define GL_SGIX_subsample 1
+#endif
+
+#ifndef GL_SGIX_ycrcba
+#define GL_SGIX_ycrcba 1
+#endif
+
+#ifndef GL_SGIX_ycrcb_subsample
+#define GL_SGIX_ycrcb_subsample 1
+#endif
+
+#ifndef GL_SGIX_depth_pass_instrument
+#define GL_SGIX_depth_pass_instrument 1
+#endif
+
+#ifndef GL_3DFX_texture_compression_FXT1
+#define GL_3DFX_texture_compression_FXT1 1
+#endif
+
+#ifndef GL_3DFX_multisample
+#define GL_3DFX_multisample 1
+#endif
+
+#ifndef GL_3DFX_tbuffer
+#define GL_3DFX_tbuffer 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glTbufferMask3DFX (GLuint);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLTBUFFERMASK3DFXPROC) (GLuint mask);
+#endif
+
+#ifndef GL_EXT_multisample
+#define GL_EXT_multisample 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glSampleMaskEXT (GLclampf, GLboolean);
+GLAPI void APIENTRY glSamplePatternEXT (GLenum);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLSAMPLEMASKEXTPROC) (GLclampf value, GLboolean invert);
+typedef void (APIENTRYP PFNGLSAMPLEPATTERNEXTPROC) (GLenum pattern);
+#endif
+
+#ifndef GL_SGIX_vertex_preclip
+#define GL_SGIX_vertex_preclip 1
+#endif
+
+#ifndef GL_SGIX_convolution_accuracy
+#define GL_SGIX_convolution_accuracy 1
+#endif
+
+#ifndef GL_SGIX_resample
+#define GL_SGIX_resample 1
+#endif
+
+#ifndef GL_SGIS_point_line_texgen
+#define GL_SGIS_point_line_texgen 1
+#endif
+
+#ifndef GL_SGIS_texture_color_mask
+#define GL_SGIS_texture_color_mask 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glTextureColorMaskSGIS (GLboolean, GLboolean, GLboolean, GLboolean);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLTEXTURECOLORMASKSGISPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
+#endif
+
+#ifndef GL_SGIX_igloo_interface
+#define GL_SGIX_igloo_interface 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glIglooInterfaceSGIX (GLenum, const GLvoid *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLIGLOOINTERFACESGIXPROC) (GLenum pname, const GLvoid *params);
+#endif
+
+#ifndef GL_EXT_texture_env_dot3
+#define GL_EXT_texture_env_dot3 1
+#endif
+
+#ifndef GL_ATI_texture_mirror_once
+#define GL_ATI_texture_mirror_once 1
+#endif
+
+#ifndef GL_NV_fence
+#define GL_NV_fence 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glDeleteFencesNV (GLsizei, const GLuint *);
+GLAPI void APIENTRY glGenFencesNV (GLsizei, GLuint *);
+GLAPI GLboolean APIENTRY glIsFenceNV (GLuint);
+GLAPI GLboolean APIENTRY glTestFenceNV (GLuint);
+GLAPI void APIENTRY glGetFenceivNV (GLuint, GLenum, GLint *);
+GLAPI void APIENTRY glFinishFenceNV (GLuint);
+GLAPI void APIENTRY glSetFenceNV (GLuint, GLenum);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLDELETEFENCESNVPROC) (GLsizei n, const GLuint *fences);
+typedef void (APIENTRYP PFNGLGENFENCESNVPROC) (GLsizei n, GLuint *fences);
+typedef GLboolean (APIENTRYP PFNGLISFENCENVPROC) (GLuint fence);
+typedef GLboolean (APIENTRYP PFNGLTESTFENCENVPROC) (GLuint fence);
+typedef void (APIENTRYP PFNGLGETFENCEIVNVPROC) (GLuint fence, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLFINISHFENCENVPROC) (GLuint fence);
+typedef void (APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition);
+#endif
+
+#ifndef GL_NV_evaluators
+#define GL_NV_evaluators 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glMapControlPointsNV (GLenum, GLuint, GLenum, GLsizei, GLsizei, GLint, GLint, GLboolean, const GLvoid *);
+GLAPI void APIENTRY glMapParameterivNV (GLenum, GLenum, const GLint *);
+GLAPI void APIENTRY glMapParameterfvNV (GLenum, GLenum, const GLfloat *);
+GLAPI void APIENTRY glGetMapControlPointsNV (GLenum, GLuint, GLenum, GLsizei, GLsizei, GLboolean, GLvoid *);
+GLAPI void APIENTRY glGetMapParameterivNV (GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glGetMapParameterfvNV (GLenum, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetMapAttribParameterivNV (GLenum, GLuint, GLenum, GLint *);
+GLAPI void APIENTRY glGetMapAttribParameterfvNV (GLenum, GLuint, GLenum, GLfloat *);
+GLAPI void APIENTRY glEvalMapsNV (GLenum, GLenum);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLMAPCONTROLPOINTSNVPROC) (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLint uorder, GLint vorder, GLboolean packed, const GLvoid *points);
+typedef void (APIENTRYP PFNGLMAPPARAMETERIVNVPROC) (GLenum target, GLenum pname, const GLint *params);
+typedef void (APIENTRYP PFNGLMAPPARAMETERFVNVPROC) (GLenum target, GLenum pname, const GLfloat *params);
+typedef void (APIENTRYP PFNGLGETMAPCONTROLPOINTSNVPROC) (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLboolean packed, GLvoid *points);
+typedef void (APIENTRYP PFNGLGETMAPPARAMETERIVNVPROC) (GLenum target, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETMAPPARAMETERFVNVPROC) (GLenum target, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERIVNVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERFVNVPROC) (GLenum target, GLuint index, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLEVALMAPSNVPROC) (GLenum target, GLenum mode);
+#endif
+
+#ifndef GL_NV_packed_depth_stencil
+#define GL_NV_packed_depth_stencil 1
+#endif
+
+#ifndef GL_NV_register_combiners2
+#define GL_NV_register_combiners2 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glCombinerStageParameterfvNV (GLenum, GLenum, const GLfloat *);
+GLAPI void APIENTRY glGetCombinerStageParameterfvNV (GLenum, GLenum, GLfloat *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLCOMBINERSTAGEPARAMETERFVNVPROC) (GLenum stage, GLenum pname, const GLfloat *params);
+typedef void (APIENTRYP PFNGLGETCOMBINERSTAGEPARAMETERFVNVPROC) (GLenum stage, GLenum pname, GLfloat *params);
+#endif
+
+#ifndef GL_NV_texture_compression_vtc
+#define GL_NV_texture_compression_vtc 1
+#endif
+
+#ifndef GL_NV_texture_rectangle
+#define GL_NV_texture_rectangle 1
+#endif
+
+#ifndef GL_NV_texture_shader
+#define GL_NV_texture_shader 1
+#endif
+
+#ifndef GL_NV_texture_shader2
+#define GL_NV_texture_shader2 1
+#endif
+
+#ifndef GL_NV_vertex_array_range2
+#define GL_NV_vertex_array_range2 1
+#endif
+
+#ifndef GL_NV_vertex_program
+#define GL_NV_vertex_program 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI GLboolean APIENTRY glAreProgramsResidentNV (GLsizei, const GLuint *, GLboolean *);
+GLAPI void APIENTRY glBindProgramNV (GLenum, GLuint);
+GLAPI void APIENTRY glDeleteProgramsNV (GLsizei, const GLuint *);
+GLAPI void APIENTRY glExecuteProgramNV (GLenum, GLuint, const GLfloat *);
+GLAPI void APIENTRY glGenProgramsNV (GLsizei, GLuint *);
+GLAPI void APIENTRY glGetProgramParameterdvNV (GLenum, GLuint, GLenum, GLdouble *);
+GLAPI void APIENTRY glGetProgramParameterfvNV (GLenum, GLuint, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetProgramivNV (GLuint, GLenum, GLint *);
+GLAPI void APIENTRY glGetProgramStringNV (GLuint, GLenum, GLubyte *);
+GLAPI void APIENTRY glGetTrackMatrixivNV (GLenum, GLuint, GLenum, GLint *);
+GLAPI void APIENTRY glGetVertexAttribdvNV (GLuint, GLenum, GLdouble *);
+GLAPI void APIENTRY glGetVertexAttribfvNV (GLuint, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetVertexAttribivNV (GLuint, GLenum, GLint *);
+GLAPI void APIENTRY glGetVertexAttribPointervNV (GLuint, GLenum, GLvoid* *);
+GLAPI GLboolean APIENTRY glIsProgramNV (GLuint);
+GLAPI void APIENTRY glLoadProgramNV (GLenum, GLuint, GLsizei, const GLubyte *);
+GLAPI void APIENTRY glProgramParameter4dNV (GLenum, GLuint, GLdouble, GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glProgramParameter4dvNV (GLenum, GLuint, const GLdouble *);
+GLAPI void APIENTRY glProgramParameter4fNV (GLenum, GLuint, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glProgramParameter4fvNV (GLenum, GLuint, const GLfloat *);
+GLAPI void APIENTRY glProgramParameters4dvNV (GLenum, GLuint, GLuint, const GLdouble *);
+GLAPI void APIENTRY glProgramParameters4fvNV (GLenum, GLuint, GLuint, const GLfloat *);
+GLAPI void APIENTRY glRequestResidentProgramsNV (GLsizei, const GLuint *);
+GLAPI void APIENTRY glTrackMatrixNV (GLenum, GLuint, GLenum, GLenum);
+GLAPI void APIENTRY glVertexAttribPointerNV (GLuint, GLint, GLenum, GLsizei, const GLvoid *);
+GLAPI void APIENTRY glVertexAttrib1dNV (GLuint, GLdouble);
+GLAPI void APIENTRY glVertexAttrib1dvNV (GLuint, const GLdouble *);
+GLAPI void APIENTRY glVertexAttrib1fNV (GLuint, GLfloat);
+GLAPI void APIENTRY glVertexAttrib1fvNV (GLuint, const GLfloat *);
+GLAPI void APIENTRY glVertexAttrib1sNV (GLuint, GLshort);
+GLAPI void APIENTRY glVertexAttrib1svNV (GLuint, const GLshort *);
+GLAPI void APIENTRY glVertexAttrib2dNV (GLuint, GLdouble, GLdouble);
+GLAPI void APIENTRY glVertexAttrib2dvNV (GLuint, const GLdouble *);
+GLAPI void APIENTRY glVertexAttrib2fNV (GLuint, GLfloat, GLfloat);
+GLAPI void APIENTRY glVertexAttrib2fvNV (GLuint, const GLfloat *);
+GLAPI void APIENTRY glVertexAttrib2sNV (GLuint, GLshort, GLshort);
+GLAPI void APIENTRY glVertexAttrib2svNV (GLuint, const GLshort *);
+GLAPI void APIENTRY glVertexAttrib3dNV (GLuint, GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glVertexAttrib3dvNV (GLuint, const GLdouble *);
+GLAPI void APIENTRY glVertexAttrib3fNV (GLuint, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glVertexAttrib3fvNV (GLuint, const GLfloat *);
+GLAPI void APIENTRY glVertexAttrib3sNV (GLuint, GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glVertexAttrib3svNV (GLuint, const GLshort *);
+GLAPI void APIENTRY glVertexAttrib4dNV (GLuint, GLdouble, GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glVertexAttrib4dvNV (GLuint, const GLdouble *);
+GLAPI void APIENTRY glVertexAttrib4fNV (GLuint, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glVertexAttrib4fvNV (GLuint, const GLfloat *);
+GLAPI void APIENTRY glVertexAttrib4sNV (GLuint, GLshort, GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glVertexAttrib4svNV (GLuint, const GLshort *);
+GLAPI void APIENTRY glVertexAttrib4ubNV (GLuint, GLubyte, GLubyte, GLubyte, GLubyte);
+GLAPI void APIENTRY glVertexAttrib4ubvNV (GLuint, const GLubyte *);
+GLAPI void APIENTRY glVertexAttribs1dvNV (GLuint, GLsizei, const GLdouble *);
+GLAPI void APIENTRY glVertexAttribs1fvNV (GLuint, GLsizei, const GLfloat *);
+GLAPI void APIENTRY glVertexAttribs1svNV (GLuint, GLsizei, const GLshort *);
+GLAPI void APIENTRY glVertexAttribs2dvNV (GLuint, GLsizei, const GLdouble *);
+GLAPI void APIENTRY glVertexAttribs2fvNV (GLuint, GLsizei, const GLfloat *);
+GLAPI void APIENTRY glVertexAttribs2svNV (GLuint, GLsizei, const GLshort *);
+GLAPI void APIENTRY glVertexAttribs3dvNV (GLuint, GLsizei, const GLdouble *);
+GLAPI void APIENTRY glVertexAttribs3fvNV (GLuint, GLsizei, const GLfloat *);
+GLAPI void APIENTRY glVertexAttribs3svNV (GLuint, GLsizei, const GLshort *);
+GLAPI void APIENTRY glVertexAttribs4dvNV (GLuint, GLsizei, const GLdouble *);
+GLAPI void APIENTRY glVertexAttribs4fvNV (GLuint, GLsizei, const GLfloat *);
+GLAPI void APIENTRY glVertexAttribs4svNV (GLuint, GLsizei, const GLshort *);
+GLAPI void APIENTRY glVertexAttribs4ubvNV (GLuint, GLsizei, const GLubyte *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef GLboolean (APIENTRYP PFNGLAREPROGRAMSRESIDENTNVPROC) (GLsizei n, const GLuint *programs, GLboolean *residences);
+typedef void (APIENTRYP PFNGLBINDPROGRAMNVPROC) (GLenum target, GLuint id);
+typedef void (APIENTRYP PFNGLDELETEPROGRAMSNVPROC) (GLsizei n, const GLuint *programs);
+typedef void (APIENTRYP PFNGLEXECUTEPROGRAMNVPROC) (GLenum target, GLuint id, const GLfloat *params);
+typedef void (APIENTRYP PFNGLGENPROGRAMSNVPROC) (GLsizei n, GLuint *programs);
+typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERDVNVPROC) (GLenum target, GLuint index, GLenum pname, GLdouble *params);
+typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERFVNVPROC) (GLenum target, GLuint index, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETPROGRAMIVNVPROC) (GLuint id, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGNVPROC) (GLuint id, GLenum pname, GLubyte *program);
+typedef void (APIENTRYP PFNGLGETTRACKMATRIXIVNVPROC) (GLenum target, GLuint address, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVNVPROC) (GLuint index, GLenum pname, GLdouble *params);
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVNVPROC) (GLuint index, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVNVPROC) (GLuint index, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVNVPROC) (GLuint index, GLenum pname, GLvoid* *pointer);
+typedef GLboolean (APIENTRYP PFNGLISPROGRAMNVPROC) (GLuint id);
+typedef void (APIENTRYP PFNGLLOADPROGRAMNVPROC) (GLenum target, GLuint id, GLsizei len, const GLubyte *program);
+typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DNVPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DVNVPROC) (GLenum target, GLuint index, const GLdouble *v);
+typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FNVPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FVNVPROC) (GLenum target, GLuint index, const GLfloat *v);
+typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4DVNVPROC) (GLenum target, GLuint index, GLuint count, const GLdouble *v);
+typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4FVNVPROC) (GLenum target, GLuint index, GLuint count, const GLfloat *v);
+typedef void (APIENTRYP PFNGLREQUESTRESIDENTPROGRAMSNVPROC) (GLsizei n, const GLuint *programs);
+typedef void (APIENTRYP PFNGLTRACKMATRIXNVPROC) (GLenum target, GLuint address, GLenum matrix, GLenum transform);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERNVPROC) (GLuint index, GLint fsize, GLenum type, GLsizei stride, const GLvoid *pointer);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1DNVPROC) (GLuint index, GLdouble x);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVNVPROC) (GLuint index, const GLdouble *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1FNVPROC) (GLuint index, GLfloat x);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVNVPROC) (GLuint index, const GLfloat *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1SNVPROC) (GLuint index, GLshort x);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVNVPROC) (GLuint index, const GLshort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2DNVPROC) (GLuint index, GLdouble x, GLdouble y);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVNVPROC) (GLuint index, const GLdouble *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2FNVPROC) (GLuint index, GLfloat x, GLfloat y);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVNVPROC) (GLuint index, const GLfloat *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2SNVPROC) (GLuint index, GLshort x, GLshort y);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVNVPROC) (GLuint index, const GLshort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3DNVPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVNVPROC) (GLuint index, const GLdouble *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3FNVPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVNVPROC) (GLuint index, const GLfloat *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3SNVPROC) (GLuint index, GLshort x, GLshort y, GLshort z);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVNVPROC) (GLuint index, const GLshort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4DNVPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVNVPROC) (GLuint index, const GLdouble *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4FNVPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVNVPROC) (GLuint index, const GLfloat *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4SNVPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVNVPROC) (GLuint index, const GLshort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBNVPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVNVPROC) (GLuint index, const GLubyte *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBS1DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBS1FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBS1SVNVPROC) (GLuint index, GLsizei count, const GLshort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBS2DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBS2FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBS2SVNVPROC) (GLuint index, GLsizei count, const GLshort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBS3DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBS3FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBS3SVNVPROC) (GLuint index, GLsizei count, const GLshort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBS4DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBS4FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBS4SVNVPROC) (GLuint index, GLsizei count, const GLshort *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBS4UBVNVPROC) (GLuint index, GLsizei count, const GLubyte *v);
+#endif
+
+#ifndef GL_SGIX_texture_coordinate_clamp
+#define GL_SGIX_texture_coordinate_clamp 1
+#endif
+
+#ifndef GL_SGIX_scalebias_hint
+#define GL_SGIX_scalebias_hint 1
+#endif
+
+#ifndef GL_OML_interlace
+#define GL_OML_interlace 1
+#endif
+
+#ifndef GL_OML_subsample
+#define GL_OML_subsample 1
+#endif
+
+#ifndef GL_OML_resample
+#define GL_OML_resample 1
+#endif
+
+#ifndef GL_NV_copy_depth_to_color
+#define GL_NV_copy_depth_to_color 1
+#endif
+
+#ifndef GL_ATI_envmap_bumpmap
+#define GL_ATI_envmap_bumpmap 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glTexBumpParameterivATI (GLenum, const GLint *);
+GLAPI void APIENTRY glTexBumpParameterfvATI (GLenum, const GLfloat *);
+GLAPI void APIENTRY glGetTexBumpParameterivATI (GLenum, GLint *);
+GLAPI void APIENTRY glGetTexBumpParameterfvATI (GLenum, GLfloat *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERIVATIPROC) (GLenum pname, const GLint *param);
+typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERFVATIPROC) (GLenum pname, const GLfloat *param);
+typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERIVATIPROC) (GLenum pname, GLint *param);
+typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERFVATIPROC) (GLenum pname, GLfloat *param);
+#endif
+
+#ifndef GL_ATI_fragment_shader
+#define GL_ATI_fragment_shader 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI GLuint APIENTRY glGenFragmentShadersATI (GLuint);
+GLAPI void APIENTRY glBindFragmentShaderATI (GLuint);
+GLAPI void APIENTRY glDeleteFragmentShaderATI (GLuint);
+GLAPI void APIENTRY glBeginFragmentShaderATI (void);
+GLAPI void APIENTRY glEndFragmentShaderATI (void);
+GLAPI void APIENTRY glPassTexCoordATI (GLuint, GLuint, GLenum);
+GLAPI void APIENTRY glSampleMapATI (GLuint, GLuint, GLenum);
+GLAPI void APIENTRY glColorFragmentOp1ATI (GLenum, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint);
+GLAPI void APIENTRY glColorFragmentOp2ATI (GLenum, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint);
+GLAPI void APIENTRY glColorFragmentOp3ATI (GLenum, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint);
+GLAPI void APIENTRY glAlphaFragmentOp1ATI (GLenum, GLuint, GLuint, GLuint, GLuint, GLuint);
+GLAPI void APIENTRY glAlphaFragmentOp2ATI (GLenum, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint);
+GLAPI void APIENTRY glAlphaFragmentOp3ATI (GLenum, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint);
+GLAPI void APIENTRY glSetFragmentShaderConstantATI (GLuint, const GLfloat *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef GLuint (APIENTRYP PFNGLGENFRAGMENTSHADERSATIPROC) (GLuint range);
+typedef void (APIENTRYP PFNGLBINDFRAGMENTSHADERATIPROC) (GLuint id);
+typedef void (APIENTRYP PFNGLDELETEFRAGMENTSHADERATIPROC) (GLuint id);
+typedef void (APIENTRYP PFNGLBEGINFRAGMENTSHADERATIPROC) (void);
+typedef void (APIENTRYP PFNGLENDFRAGMENTSHADERATIPROC) (void);
+typedef void (APIENTRYP PFNGLPASSTEXCOORDATIPROC) (GLuint dst, GLuint coord, GLenum swizzle);
+typedef void (APIENTRYP PFNGLSAMPLEMAPATIPROC) (GLuint dst, GLuint interp, GLenum swizzle);
+typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP1ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod);
+typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP2ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod);
+typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP3ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod);
+typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP1ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod);
+typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP2ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod);
+typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP3ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod);
+typedef void (APIENTRYP PFNGLSETFRAGMENTSHADERCONSTANTATIPROC) (GLuint dst, const GLfloat *value);
+#endif
+
+#ifndef GL_ATI_pn_triangles
+#define GL_ATI_pn_triangles 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glPNTrianglesiATI (GLenum, GLint);
+GLAPI void APIENTRY glPNTrianglesfATI (GLenum, GLfloat);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLPNTRIANGLESIATIPROC) (GLenum pname, GLint param);
+typedef void (APIENTRYP PFNGLPNTRIANGLESFATIPROC) (GLenum pname, GLfloat param);
+#endif
+
+#ifndef GL_ATI_vertex_array_object
+#define GL_ATI_vertex_array_object 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI GLuint APIENTRY glNewObjectBufferATI (GLsizei, const GLvoid *, GLenum);
+GLAPI GLboolean APIENTRY glIsObjectBufferATI (GLuint);
+GLAPI void APIENTRY glUpdateObjectBufferATI (GLuint, GLuint, GLsizei, const GLvoid *, GLenum);
+GLAPI void APIENTRY glGetObjectBufferfvATI (GLuint, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetObjectBufferivATI (GLuint, GLenum, GLint *);
+GLAPI void APIENTRY glFreeObjectBufferATI (GLuint);
+GLAPI void APIENTRY glArrayObjectATI (GLenum, GLint, GLenum, GLsizei, GLuint, GLuint);
+GLAPI void APIENTRY glGetArrayObjectfvATI (GLenum, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetArrayObjectivATI (GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glVariantArrayObjectATI (GLuint, GLenum, GLsizei, GLuint, GLuint);
+GLAPI void APIENTRY glGetVariantArrayObjectfvATI (GLuint, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetVariantArrayObjectivATI (GLuint, GLenum, GLint *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef GLuint (APIENTRYP PFNGLNEWOBJECTBUFFERATIPROC) (GLsizei size, const GLvoid *pointer, GLenum usage);
+typedef GLboolean (APIENTRYP PFNGLISOBJECTBUFFERATIPROC) (GLuint buffer);
+typedef void (APIENTRYP PFNGLUPDATEOBJECTBUFFERATIPROC) (GLuint buffer, GLuint offset, GLsizei size, const GLvoid *pointer, GLenum preserve);
+typedef void (APIENTRYP PFNGLGETOBJECTBUFFERFVATIPROC) (GLuint buffer, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETOBJECTBUFFERIVATIPROC) (GLuint buffer, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLFREEOBJECTBUFFERATIPROC) (GLuint buffer);
+typedef void (APIENTRYP PFNGLARRAYOBJECTATIPROC) (GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset);
+typedef void (APIENTRYP PFNGLGETARRAYOBJECTFVATIPROC) (GLenum array, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETARRAYOBJECTIVATIPROC) (GLenum array, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLVARIANTARRAYOBJECTATIPROC) (GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset);
+typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTFVATIPROC) (GLuint id, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTIVATIPROC) (GLuint id, GLenum pname, GLint *params);
+#endif
+
+#ifndef GL_EXT_vertex_shader
+#define GL_EXT_vertex_shader 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glBeginVertexShaderEXT (void);
+GLAPI void APIENTRY glEndVertexShaderEXT (void);
+GLAPI void APIENTRY glBindVertexShaderEXT (GLuint);
+GLAPI GLuint APIENTRY glGenVertexShadersEXT (GLuint);
+GLAPI void APIENTRY glDeleteVertexShaderEXT (GLuint);
+GLAPI void APIENTRY glShaderOp1EXT (GLenum, GLuint, GLuint);
+GLAPI void APIENTRY glShaderOp2EXT (GLenum, GLuint, GLuint, GLuint);
+GLAPI void APIENTRY glShaderOp3EXT (GLenum, GLuint, GLuint, GLuint, GLuint);
+GLAPI void APIENTRY glSwizzleEXT (GLuint, GLuint, GLenum, GLenum, GLenum, GLenum);
+GLAPI void APIENTRY glWriteMaskEXT (GLuint, GLuint, GLenum, GLenum, GLenum, GLenum);
+GLAPI void APIENTRY glInsertComponentEXT (GLuint, GLuint, GLuint);
+GLAPI void APIENTRY glExtractComponentEXT (GLuint, GLuint, GLuint);
+GLAPI GLuint APIENTRY glGenSymbolsEXT (GLenum, GLenum, GLenum, GLuint);
+GLAPI void APIENTRY glSetInvariantEXT (GLuint, GLenum, const GLvoid *);
+GLAPI void APIENTRY glSetLocalConstantEXT (GLuint, GLenum, const GLvoid *);
+GLAPI void APIENTRY glVariantbvEXT (GLuint, const GLbyte *);
+GLAPI void APIENTRY glVariantsvEXT (GLuint, const GLshort *);
+GLAPI void APIENTRY glVariantivEXT (GLuint, const GLint *);
+GLAPI void APIENTRY glVariantfvEXT (GLuint, const GLfloat *);
+GLAPI void APIENTRY glVariantdvEXT (GLuint, const GLdouble *);
+GLAPI void APIENTRY glVariantubvEXT (GLuint, const GLubyte *);
+GLAPI void APIENTRY glVariantusvEXT (GLuint, const GLushort *);
+GLAPI void APIENTRY glVariantuivEXT (GLuint, const GLuint *);
+GLAPI void APIENTRY glVariantPointerEXT (GLuint, GLenum, GLuint, const GLvoid *);
+GLAPI void APIENTRY glEnableVariantClientStateEXT (GLuint);
+GLAPI void APIENTRY glDisableVariantClientStateEXT (GLuint);
+GLAPI GLuint APIENTRY glBindLightParameterEXT (GLenum, GLenum);
+GLAPI GLuint APIENTRY glBindMaterialParameterEXT (GLenum, GLenum);
+GLAPI GLuint APIENTRY glBindTexGenParameterEXT (GLenum, GLenum, GLenum);
+GLAPI GLuint APIENTRY glBindTextureUnitParameterEXT (GLenum, GLenum);
+GLAPI GLuint APIENTRY glBindParameterEXT (GLenum);
+GLAPI GLboolean APIENTRY glIsVariantEnabledEXT (GLuint, GLenum);
+GLAPI void APIENTRY glGetVariantBooleanvEXT (GLuint, GLenum, GLboolean *);
+GLAPI void APIENTRY glGetVariantIntegervEXT (GLuint, GLenum, GLint *);
+GLAPI void APIENTRY glGetVariantFloatvEXT (GLuint, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetVariantPointervEXT (GLuint, GLenum, GLvoid* *);
+GLAPI void APIENTRY glGetInvariantBooleanvEXT (GLuint, GLenum, GLboolean *);
+GLAPI void APIENTRY glGetInvariantIntegervEXT (GLuint, GLenum, GLint *);
+GLAPI void APIENTRY glGetInvariantFloatvEXT (GLuint, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetLocalConstantBooleanvEXT (GLuint, GLenum, GLboolean *);
+GLAPI void APIENTRY glGetLocalConstantIntegervEXT (GLuint, GLenum, GLint *);
+GLAPI void APIENTRY glGetLocalConstantFloatvEXT (GLuint, GLenum, GLfloat *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLBEGINVERTEXSHADEREXTPROC) (void);
+typedef void (APIENTRYP PFNGLENDVERTEXSHADEREXTPROC) (void);
+typedef void (APIENTRYP PFNGLBINDVERTEXSHADEREXTPROC) (GLuint id);
+typedef GLuint (APIENTRYP PFNGLGENVERTEXSHADERSEXTPROC) (GLuint range);
+typedef void (APIENTRYP PFNGLDELETEVERTEXSHADEREXTPROC) (GLuint id);
+typedef void (APIENTRYP PFNGLSHADEROP1EXTPROC) (GLenum op, GLuint res, GLuint arg1);
+typedef void (APIENTRYP PFNGLSHADEROP2EXTPROC) (GLenum op, GLuint res, GLuint arg1, GLuint arg2);
+typedef void (APIENTRYP PFNGLSHADEROP3EXTPROC) (GLenum op, GLuint res, GLuint arg1, GLuint arg2, GLuint arg3);
+typedef void (APIENTRYP PFNGLSWIZZLEEXTPROC) (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW);
+typedef void (APIENTRYP PFNGLWRITEMASKEXTPROC) (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW);
+typedef void (APIENTRYP PFNGLINSERTCOMPONENTEXTPROC) (GLuint res, GLuint src, GLuint num);
+typedef void (APIENTRYP PFNGLEXTRACTCOMPONENTEXTPROC) (GLuint res, GLuint src, GLuint num);
+typedef GLuint (APIENTRYP PFNGLGENSYMBOLSEXTPROC) (GLenum datatype, GLenum storagetype, GLenum range, GLuint components);
+typedef void (APIENTRYP PFNGLSETINVARIANTEXTPROC) (GLuint id, GLenum type, const GLvoid *addr);
+typedef void (APIENTRYP PFNGLSETLOCALCONSTANTEXTPROC) (GLuint id, GLenum type, const GLvoid *addr);
+typedef void (APIENTRYP PFNGLVARIANTBVEXTPROC) (GLuint id, const GLbyte *addr);
+typedef void (APIENTRYP PFNGLVARIANTSVEXTPROC) (GLuint id, const GLshort *addr);
+typedef void (APIENTRYP PFNGLVARIANTIVEXTPROC) (GLuint id, const GLint *addr);
+typedef void (APIENTRYP PFNGLVARIANTFVEXTPROC) (GLuint id, const GLfloat *addr);
+typedef void (APIENTRYP PFNGLVARIANTDVEXTPROC) (GLuint id, const GLdouble *addr);
+typedef void (APIENTRYP PFNGLVARIANTUBVEXTPROC) (GLuint id, const GLubyte *addr);
+typedef void (APIENTRYP PFNGLVARIANTUSVEXTPROC) (GLuint id, const GLushort *addr);
+typedef void (APIENTRYP PFNGLVARIANTUIVEXTPROC) (GLuint id, const GLuint *addr);
+typedef void (APIENTRYP PFNGLVARIANTPOINTEREXTPROC) (GLuint id, GLenum type, GLuint stride, const GLvoid *addr);
+typedef void (APIENTRYP PFNGLENABLEVARIANTCLIENTSTATEEXTPROC) (GLuint id);
+typedef void (APIENTRYP PFNGLDISABLEVARIANTCLIENTSTATEEXTPROC) (GLuint id);
+typedef GLuint (APIENTRYP PFNGLBINDLIGHTPARAMETEREXTPROC) (GLenum light, GLenum value);
+typedef GLuint (APIENTRYP PFNGLBINDMATERIALPARAMETEREXTPROC) (GLenum face, GLenum value);
+typedef GLuint (APIENTRYP PFNGLBINDTEXGENPARAMETEREXTPROC) (GLenum unit, GLenum coord, GLenum value);
+typedef GLuint (APIENTRYP PFNGLBINDTEXTUREUNITPARAMETEREXTPROC) (GLenum unit, GLenum value);
+typedef GLuint (APIENTRYP PFNGLBINDPARAMETEREXTPROC) (GLenum value);
+typedef GLboolean (APIENTRYP PFNGLISVARIANTENABLEDEXTPROC) (GLuint id, GLenum cap);
+typedef void (APIENTRYP PFNGLGETVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data);
+typedef void (APIENTRYP PFNGLGETVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data);
+typedef void (APIENTRYP PFNGLGETVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data);
+typedef void (APIENTRYP PFNGLGETVARIANTPOINTERVEXTPROC) (GLuint id, GLenum value, GLvoid* *data);
+typedef void (APIENTRYP PFNGLGETINVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data);
+typedef void (APIENTRYP PFNGLGETINVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data);
+typedef void (APIENTRYP PFNGLGETINVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data);
+typedef void (APIENTRYP PFNGLGETLOCALCONSTANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data);
+typedef void (APIENTRYP PFNGLGETLOCALCONSTANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data);
+typedef void (APIENTRYP PFNGLGETLOCALCONSTANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data);
+#endif
+
+#ifndef GL_ATI_vertex_streams
+#define GL_ATI_vertex_streams 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glVertexStream1sATI (GLenum, GLshort);
+GLAPI void APIENTRY glVertexStream1svATI (GLenum, const GLshort *);
+GLAPI void APIENTRY glVertexStream1iATI (GLenum, GLint);
+GLAPI void APIENTRY glVertexStream1ivATI (GLenum, const GLint *);
+GLAPI void APIENTRY glVertexStream1fATI (GLenum, GLfloat);
+GLAPI void APIENTRY glVertexStream1fvATI (GLenum, const GLfloat *);
+GLAPI void APIENTRY glVertexStream1dATI (GLenum, GLdouble);
+GLAPI void APIENTRY glVertexStream1dvATI (GLenum, const GLdouble *);
+GLAPI void APIENTRY glVertexStream2sATI (GLenum, GLshort, GLshort);
+GLAPI void APIENTRY glVertexStream2svATI (GLenum, const GLshort *);
+GLAPI void APIENTRY glVertexStream2iATI (GLenum, GLint, GLint);
+GLAPI void APIENTRY glVertexStream2ivATI (GLenum, const GLint *);
+GLAPI void APIENTRY glVertexStream2fATI (GLenum, GLfloat, GLfloat);
+GLAPI void APIENTRY glVertexStream2fvATI (GLenum, const GLfloat *);
+GLAPI void APIENTRY glVertexStream2dATI (GLenum, GLdouble, GLdouble);
+GLAPI void APIENTRY glVertexStream2dvATI (GLenum, const GLdouble *);
+GLAPI void APIENTRY glVertexStream3sATI (GLenum, GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glVertexStream3svATI (GLenum, const GLshort *);
+GLAPI void APIENTRY glVertexStream3iATI (GLenum, GLint, GLint, GLint);
+GLAPI void APIENTRY glVertexStream3ivATI (GLenum, const GLint *);
+GLAPI void APIENTRY glVertexStream3fATI (GLenum, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glVertexStream3fvATI (GLenum, const GLfloat *);
+GLAPI void APIENTRY glVertexStream3dATI (GLenum, GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glVertexStream3dvATI (GLenum, const GLdouble *);
+GLAPI void APIENTRY glVertexStream4sATI (GLenum, GLshort, GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glVertexStream4svATI (GLenum, const GLshort *);
+GLAPI void APIENTRY glVertexStream4iATI (GLenum, GLint, GLint, GLint, GLint);
+GLAPI void APIENTRY glVertexStream4ivATI (GLenum, const GLint *);
+GLAPI void APIENTRY glVertexStream4fATI (GLenum, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glVertexStream4fvATI (GLenum, const GLfloat *);
+GLAPI void APIENTRY glVertexStream4dATI (GLenum, GLdouble, GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glVertexStream4dvATI (GLenum, const GLdouble *);
+GLAPI void APIENTRY glNormalStream3bATI (GLenum, GLbyte, GLbyte, GLbyte);
+GLAPI void APIENTRY glNormalStream3bvATI (GLenum, const GLbyte *);
+GLAPI void APIENTRY glNormalStream3sATI (GLenum, GLshort, GLshort, GLshort);
+GLAPI void APIENTRY glNormalStream3svATI (GLenum, const GLshort *);
+GLAPI void APIENTRY glNormalStream3iATI (GLenum, GLint, GLint, GLint);
+GLAPI void APIENTRY glNormalStream3ivATI (GLenum, const GLint *);
+GLAPI void APIENTRY glNormalStream3fATI (GLenum, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glNormalStream3fvATI (GLenum, const GLfloat *);
+GLAPI void APIENTRY glNormalStream3dATI (GLenum, GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glNormalStream3dvATI (GLenum, const GLdouble *);
+GLAPI void APIENTRY glClientActiveVertexStreamATI (GLenum);
+GLAPI void APIENTRY glVertexBlendEnviATI (GLenum, GLint);
+GLAPI void APIENTRY glVertexBlendEnvfATI (GLenum, GLfloat);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLVERTEXSTREAM1SATIPROC) (GLenum stream, GLshort x);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM1SVATIPROC) (GLenum stream, const GLshort *coords);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM1IATIPROC) (GLenum stream, GLint x);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM1IVATIPROC) (GLenum stream, const GLint *coords);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM1FATIPROC) (GLenum stream, GLfloat x);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM1FVATIPROC) (GLenum stream, const GLfloat *coords);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM1DATIPROC) (GLenum stream, GLdouble x);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM1DVATIPROC) (GLenum stream, const GLdouble *coords);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM2SATIPROC) (GLenum stream, GLshort x, GLshort y);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM2SVATIPROC) (GLenum stream, const GLshort *coords);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM2IATIPROC) (GLenum stream, GLint x, GLint y);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM2IVATIPROC) (GLenum stream, const GLint *coords);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM2FATIPROC) (GLenum stream, GLfloat x, GLfloat y);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM2FVATIPROC) (GLenum stream, const GLfloat *coords);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM2DATIPROC) (GLenum stream, GLdouble x, GLdouble y);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM2DVATIPROC) (GLenum stream, const GLdouble *coords);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM3SATIPROC) (GLenum stream, GLshort x, GLshort y, GLshort z);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM3SVATIPROC) (GLenum stream, const GLshort *coords);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM3IATIPROC) (GLenum stream, GLint x, GLint y, GLint z);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM3IVATIPROC) (GLenum stream, const GLint *coords);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM3FATIPROC) (GLenum stream, GLfloat x, GLfloat y, GLfloat z);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM3FVATIPROC) (GLenum stream, const GLfloat *coords);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM3DATIPROC) (GLenum stream, GLdouble x, GLdouble y, GLdouble z);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM3DVATIPROC) (GLenum stream, const GLdouble *coords);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM4SATIPROC) (GLenum stream, GLshort x, GLshort y, GLshort z, GLshort w);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM4SVATIPROC) (GLenum stream, const GLshort *coords);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM4IATIPROC) (GLenum stream, GLint x, GLint y, GLint z, GLint w);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM4IVATIPROC) (GLenum stream, const GLint *coords);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM4FATIPROC) (GLenum stream, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM4FVATIPROC) (GLenum stream, const GLfloat *coords);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM4DATIPROC) (GLenum stream, GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+typedef void (APIENTRYP PFNGLVERTEXSTREAM4DVATIPROC) (GLenum stream, const GLdouble *coords);
+typedef void (APIENTRYP PFNGLNORMALSTREAM3BATIPROC) (GLenum stream, GLbyte nx, GLbyte ny, GLbyte nz);
+typedef void (APIENTRYP PFNGLNORMALSTREAM3BVATIPROC) (GLenum stream, const GLbyte *coords);
+typedef void (APIENTRYP PFNGLNORMALSTREAM3SATIPROC) (GLenum stream, GLshort nx, GLshort ny, GLshort nz);
+typedef void (APIENTRYP PFNGLNORMALSTREAM3SVATIPROC) (GLenum stream, const GLshort *coords);
+typedef void (APIENTRYP PFNGLNORMALSTREAM3IATIPROC) (GLenum stream, GLint nx, GLint ny, GLint nz);
+typedef void (APIENTRYP PFNGLNORMALSTREAM3IVATIPROC) (GLenum stream, const GLint *coords);
+typedef void (APIENTRYP PFNGLNORMALSTREAM3FATIPROC) (GLenum stream, GLfloat nx, GLfloat ny, GLfloat nz);
+typedef void (APIENTRYP PFNGLNORMALSTREAM3FVATIPROC) (GLenum stream, const GLfloat *coords);
+typedef void (APIENTRYP PFNGLNORMALSTREAM3DATIPROC) (GLenum stream, GLdouble nx, GLdouble ny, GLdouble nz);
+typedef void (APIENTRYP PFNGLNORMALSTREAM3DVATIPROC) (GLenum stream, const GLdouble *coords);
+typedef void (APIENTRYP PFNGLCLIENTACTIVEVERTEXSTREAMATIPROC) (GLenum stream);
+typedef void (APIENTRYP PFNGLVERTEXBLENDENVIATIPROC) (GLenum pname, GLint param);
+typedef void (APIENTRYP PFNGLVERTEXBLENDENVFATIPROC) (GLenum pname, GLfloat param);
+#endif
+
+#ifndef GL_ATI_element_array
+#define GL_ATI_element_array 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glElementPointerATI (GLenum, const GLvoid *);
+GLAPI void APIENTRY glDrawElementArrayATI (GLenum, GLsizei);
+GLAPI void APIENTRY glDrawRangeElementArrayATI (GLenum, GLuint, GLuint, GLsizei);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLELEMENTPOINTERATIPROC) (GLenum type, const GLvoid *pointer);
+typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYATIPROC) (GLenum mode, GLsizei count);
+typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYATIPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count);
+#endif
+
+#ifndef GL_SUN_mesh_array
+#define GL_SUN_mesh_array 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glDrawMeshArraysSUN (GLenum, GLint, GLsizei, GLsizei);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLDRAWMESHARRAYSSUNPROC) (GLenum mode, GLint first, GLsizei count, GLsizei width);
+#endif
+
+#ifndef GL_SUN_slice_accum
+#define GL_SUN_slice_accum 1
+#endif
+
+#ifndef GL_NV_multisample_filter_hint
+#define GL_NV_multisample_filter_hint 1
+#endif
+
+#ifndef GL_NV_depth_clamp
+#define GL_NV_depth_clamp 1
+#endif
+
+#ifndef GL_NV_occlusion_query
+#define GL_NV_occlusion_query 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glGenOcclusionQueriesNV (GLsizei, GLuint *);
+GLAPI void APIENTRY glDeleteOcclusionQueriesNV (GLsizei, const GLuint *);
+GLAPI GLboolean APIENTRY glIsOcclusionQueryNV (GLuint);
+GLAPI void APIENTRY glBeginOcclusionQueryNV (GLuint);
+GLAPI void APIENTRY glEndOcclusionQueryNV (void);
+GLAPI void APIENTRY glGetOcclusionQueryivNV (GLuint, GLenum, GLint *);
+GLAPI void APIENTRY glGetOcclusionQueryuivNV (GLuint, GLenum, GLuint *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLGENOCCLUSIONQUERIESNVPROC) (GLsizei n, GLuint *ids);
+typedef void (APIENTRYP PFNGLDELETEOCCLUSIONQUERIESNVPROC) (GLsizei n, const GLuint *ids);
+typedef GLboolean (APIENTRYP PFNGLISOCCLUSIONQUERYNVPROC) (GLuint id);
+typedef void (APIENTRYP PFNGLBEGINOCCLUSIONQUERYNVPROC) (GLuint id);
+typedef void (APIENTRYP PFNGLENDOCCLUSIONQUERYNVPROC) (void);
+typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYIVNVPROC) (GLuint id, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYUIVNVPROC) (GLuint id, GLenum pname, GLuint *params);
+#endif
+
+#ifndef GL_NV_point_sprite
+#define GL_NV_point_sprite 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glPointParameteriNV (GLenum, GLint);
+GLAPI void APIENTRY glPointParameterivNV (GLenum, const GLint *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLPOINTPARAMETERINVPROC) (GLenum pname, GLint param);
+typedef void (APIENTRYP PFNGLPOINTPARAMETERIVNVPROC) (GLenum pname, const GLint *params);
+#endif
+
+#ifndef GL_NV_texture_shader3
+#define GL_NV_texture_shader3 1
+#endif
+
+#ifndef GL_NV_vertex_program1_1
+#define GL_NV_vertex_program1_1 1
+#endif
+
+#ifndef GL_EXT_shadow_funcs
+#define GL_EXT_shadow_funcs 1
+#endif
+
+#ifndef GL_EXT_stencil_two_side
+#define GL_EXT_stencil_two_side 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glActiveStencilFaceEXT (GLenum);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLACTIVESTENCILFACEEXTPROC) (GLenum face);
+#endif
+
+#ifndef GL_ATI_text_fragment_shader
+#define GL_ATI_text_fragment_shader 1
+#endif
+
+#ifndef GL_APPLE_client_storage
+#define GL_APPLE_client_storage 1
+#endif
+
+#ifndef GL_APPLE_element_array
+#define GL_APPLE_element_array 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glElementPointerAPPLE (GLenum, const GLvoid *);
+GLAPI void APIENTRY glDrawElementArrayAPPLE (GLenum, GLint, GLsizei);
+GLAPI void APIENTRY glDrawRangeElementArrayAPPLE (GLenum, GLuint, GLuint, GLint, GLsizei);
+GLAPI void APIENTRY glMultiDrawElementArrayAPPLE (GLenum, const GLint *, const GLsizei *, GLsizei);
+GLAPI void APIENTRY glMultiDrawRangeElementArrayAPPLE (GLenum, GLuint, GLuint, const GLint *, const GLsizei *, GLsizei);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLELEMENTPOINTERAPPLEPROC) (GLenum type, const GLvoid *pointer);
+typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYAPPLEPROC) (GLenum mode, GLint first, GLsizei count);
+typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYAPPLEPROC) (GLenum mode, GLuint start, GLuint end, GLint first, GLsizei count);
+typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTARRAYAPPLEPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount);
+typedef void (APIENTRYP PFNGLMULTIDRAWRANGEELEMENTARRAYAPPLEPROC) (GLenum mode, GLuint start, GLuint end, const GLint *first, const GLsizei *count, GLsizei primcount);
+#endif
+
+#ifndef GL_APPLE_fence
+#define GL_APPLE_fence 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glGenFencesAPPLE (GLsizei, GLuint *);
+GLAPI void APIENTRY glDeleteFencesAPPLE (GLsizei, const GLuint *);
+GLAPI void APIENTRY glSetFenceAPPLE (GLuint);
+GLAPI GLboolean APIENTRY glIsFenceAPPLE (GLuint);
+GLAPI GLboolean APIENTRY glTestFenceAPPLE (GLuint);
+GLAPI void APIENTRY glFinishFenceAPPLE (GLuint);
+GLAPI GLboolean APIENTRY glTestObjectAPPLE (GLenum, GLuint);
+GLAPI void APIENTRY glFinishObjectAPPLE (GLenum, GLint);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLGENFENCESAPPLEPROC) (GLsizei n, GLuint *fences);
+typedef void (APIENTRYP PFNGLDELETEFENCESAPPLEPROC) (GLsizei n, const GLuint *fences);
+typedef void (APIENTRYP PFNGLSETFENCEAPPLEPROC) (GLuint fence);
+typedef GLboolean (APIENTRYP PFNGLISFENCEAPPLEPROC) (GLuint fence);
+typedef GLboolean (APIENTRYP PFNGLTESTFENCEAPPLEPROC) (GLuint fence);
+typedef void (APIENTRYP PFNGLFINISHFENCEAPPLEPROC) (GLuint fence);
+typedef GLboolean (APIENTRYP PFNGLTESTOBJECTAPPLEPROC) (GLenum object, GLuint name);
+typedef void (APIENTRYP PFNGLFINISHOBJECTAPPLEPROC) (GLenum object, GLint name);
+#endif
+
+
+#ifndef GL_APPLE_flush_buffer_range
+#define GL_APPLE_flush_buffer_range 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void glBufferParameteriAPPLE(GLenum target, GLenum pname, GLint param);
+GLAPI void glFlushMappedBufferRangeAPPLE(GLenum target, GLintptr offset, GLsizeiptr size);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLBUFFERPARAMETERIAPPLEPROC) (GLenum target, GLenum pname, GLint param);
+typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC) (GLenum target, GLintptr offset, GLsizeiptr size);
+#endif
+
+
+#ifndef GL_APPLE_vertex_array_object
+#define GL_APPLE_vertex_array_object 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glBindVertexArrayAPPLE (GLuint);
+GLAPI void APIENTRY glDeleteVertexArraysAPPLE (GLsizei, const GLuint *);
+GLAPI void APIENTRY glGenVertexArraysAPPLE (GLsizei, const GLuint *);
+GLAPI GLboolean APIENTRY glIsVertexArrayAPPLE (GLuint);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLBINDVERTEXARRAYAPPLEPROC) (GLuint array);
+typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSAPPLEPROC) (GLsizei n, const GLuint *arrays);
+typedef void (APIENTRYP PFNGLGENVERTEXARRAYSAPPLEPROC) (GLsizei n, const GLuint *arrays);
+typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYAPPLEPROC) (GLuint array);
+#endif
+
+#ifndef GL_APPLE_vertex_array_range
+#define GL_APPLE_vertex_array_range 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glVertexArrayRangeAPPLE (GLsizei, GLvoid *);
+GLAPI void APIENTRY glFlushVertexArrayRangeAPPLE (GLsizei, GLvoid *);
+GLAPI void APIENTRY glVertexArrayParameteriAPPLE (GLenum, GLint);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLVERTEXARRAYRANGEAPPLEPROC) (GLsizei length, GLvoid *pointer);
+typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGEAPPLEPROC) (GLsizei length, GLvoid *pointer);
+typedef void (APIENTRYP PFNGLVERTEXARRAYPARAMETERIAPPLEPROC) (GLenum pname, GLint param);
+#endif
+
+#ifndef GL_APPLE_ycbcr_422
+#define GL_APPLE_ycbcr_422 1
+#endif
+
+#ifndef GL_S3_s3tc
+#define GL_S3_s3tc 1
+#endif
+
+#ifndef GL_ATI_draw_buffers
+#define GL_ATI_draw_buffers 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glDrawBuffersATI (GLsizei, const GLenum *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLDRAWBUFFERSATIPROC) (GLsizei n, const GLenum *bufs);
+#endif
+
+#ifndef GL_ATI_pixel_format_float
+#define GL_ATI_pixel_format_float 1
+/* This is really a WGL extension, but defines some associated GL enums.
+ * ATI does not export "GL_ATI_pixel_format_float" in the GL_EXTENSIONS string.
+ */
+#endif
+
+#ifndef GL_ATI_texture_env_combine3
+#define GL_ATI_texture_env_combine3 1
+#endif
+
+#ifndef GL_ATI_texture_float
+#define GL_ATI_texture_float 1
+#endif
+
+#ifndef GL_NV_float_buffer
+#define GL_NV_float_buffer 1
+#endif
+
+#ifndef GL_NV_fragment_program
+#define GL_NV_fragment_program 1
+/* Some NV_fragment_program entry points are shared with ARB_vertex_program. */
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glProgramNamedParameter4fNV (GLuint, GLsizei, const GLubyte *, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI void APIENTRY glProgramNamedParameter4dNV (GLuint, GLsizei, const GLubyte *, GLdouble, GLdouble, GLdouble, GLdouble);
+GLAPI void APIENTRY glProgramNamedParameter4fvNV (GLuint, GLsizei, const GLubyte *, const GLfloat *);
+GLAPI void APIENTRY glProgramNamedParameter4dvNV (GLuint, GLsizei, const GLubyte *, const GLdouble *);
+GLAPI void APIENTRY glGetProgramNamedParameterfvNV (GLuint, GLsizei, const GLubyte *, GLfloat *);
+GLAPI void APIENTRY glGetProgramNamedParameterdvNV (GLuint, GLsizei, const GLubyte *, GLdouble *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v);
+typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v);
+typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERFVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERDVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble *params);
+#endif
+
+#ifndef GL_NV_half_float
+#define GL_NV_half_float 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glVertex2hNV (GLhalfNV, GLhalfNV);
+GLAPI void APIENTRY glVertex2hvNV (const GLhalfNV *);
+GLAPI void APIENTRY glVertex3hNV (GLhalfNV, GLhalfNV, GLhalfNV);
+GLAPI void APIENTRY glVertex3hvNV (const GLhalfNV *);
+GLAPI void APIENTRY glVertex4hNV (GLhalfNV, GLhalfNV, GLhalfNV, GLhalfNV);
+GLAPI void APIENTRY glVertex4hvNV (const GLhalfNV *);
+GLAPI void APIENTRY glNormal3hNV (GLhalfNV, GLhalfNV, GLhalfNV);
+GLAPI void APIENTRY glNormal3hvNV (const GLhalfNV *);
+GLAPI void APIENTRY glColor3hNV (GLhalfNV, GLhalfNV, GLhalfNV);
+GLAPI void APIENTRY glColor3hvNV (const GLhalfNV *);
+GLAPI void APIENTRY glColor4hNV (GLhalfNV, GLhalfNV, GLhalfNV, GLhalfNV);
+GLAPI void APIENTRY glColor4hvNV (const GLhalfNV *);
+GLAPI void APIENTRY glTexCoord1hNV (GLhalfNV);
+GLAPI void APIENTRY glTexCoord1hvNV (const GLhalfNV *);
+GLAPI void APIENTRY glTexCoord2hNV (GLhalfNV, GLhalfNV);
+GLAPI void APIENTRY glTexCoord2hvNV (const GLhalfNV *);
+GLAPI void APIENTRY glTexCoord3hNV (GLhalfNV, GLhalfNV, GLhalfNV);
+GLAPI void APIENTRY glTexCoord3hvNV (const GLhalfNV *);
+GLAPI void APIENTRY glTexCoord4hNV (GLhalfNV, GLhalfNV, GLhalfNV, GLhalfNV);
+GLAPI void APIENTRY glTexCoord4hvNV (const GLhalfNV *);
+GLAPI void APIENTRY glMultiTexCoord1hNV (GLenum, GLhalfNV);
+GLAPI void APIENTRY glMultiTexCoord1hvNV (GLenum, const GLhalfNV *);
+GLAPI void APIENTRY glMultiTexCoord2hNV (GLenum, GLhalfNV, GLhalfNV);
+GLAPI void APIENTRY glMultiTexCoord2hvNV (GLenum, const GLhalfNV *);
+GLAPI void APIENTRY glMultiTexCoord3hNV (GLenum, GLhalfNV, GLhalfNV, GLhalfNV);
+GLAPI void APIENTRY glMultiTexCoord3hvNV (GLenum, const GLhalfNV *);
+GLAPI void APIENTRY glMultiTexCoord4hNV (GLenum, GLhalfNV, GLhalfNV, GLhalfNV, GLhalfNV);
+GLAPI void APIENTRY glMultiTexCoord4hvNV (GLenum, const GLhalfNV *);
+GLAPI void APIENTRY glFogCoordhNV (GLhalfNV);
+GLAPI void APIENTRY glFogCoordhvNV (const GLhalfNV *);
+GLAPI void APIENTRY glSecondaryColor3hNV (GLhalfNV, GLhalfNV, GLhalfNV);
+GLAPI void APIENTRY glSecondaryColor3hvNV (const GLhalfNV *);
+GLAPI void APIENTRY glVertexWeighthNV (GLhalfNV);
+GLAPI void APIENTRY glVertexWeighthvNV (const GLhalfNV *);
+GLAPI void APIENTRY glVertexAttrib1hNV (GLuint, GLhalfNV);
+GLAPI void APIENTRY glVertexAttrib1hvNV (GLuint, const GLhalfNV *);
+GLAPI void APIENTRY glVertexAttrib2hNV (GLuint, GLhalfNV, GLhalfNV);
+GLAPI void APIENTRY glVertexAttrib2hvNV (GLuint, const GLhalfNV *);
+GLAPI void APIENTRY glVertexAttrib3hNV (GLuint, GLhalfNV, GLhalfNV, GLhalfNV);
+GLAPI void APIENTRY glVertexAttrib3hvNV (GLuint, const GLhalfNV *);
+GLAPI void APIENTRY glVertexAttrib4hNV (GLuint, GLhalfNV, GLhalfNV, GLhalfNV, GLhalfNV);
+GLAPI void APIENTRY glVertexAttrib4hvNV (GLuint, const GLhalfNV *);
+GLAPI void APIENTRY glVertexAttribs1hvNV (GLuint, GLsizei, const GLhalfNV *);
+GLAPI void APIENTRY glVertexAttribs2hvNV (GLuint, GLsizei, const GLhalfNV *);
+GLAPI void APIENTRY glVertexAttribs3hvNV (GLuint, GLsizei, const GLhalfNV *);
+GLAPI void APIENTRY glVertexAttribs4hvNV (GLuint, GLsizei, const GLhalfNV *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLVERTEX2HNVPROC) (GLhalfNV x, GLhalfNV y);
+typedef void (APIENTRYP PFNGLVERTEX2HVNVPROC) (const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLVERTEX3HNVPROC) (GLhalfNV x, GLhalfNV y, GLhalfNV z);
+typedef void (APIENTRYP PFNGLVERTEX3HVNVPROC) (const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLVERTEX4HNVPROC) (GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w);
+typedef void (APIENTRYP PFNGLVERTEX4HVNVPROC) (const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLNORMAL3HNVPROC) (GLhalfNV nx, GLhalfNV ny, GLhalfNV nz);
+typedef void (APIENTRYP PFNGLNORMAL3HVNVPROC) (const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue);
+typedef void (APIENTRYP PFNGLCOLOR3HVNVPROC) (const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLCOLOR4HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue, GLhalfNV alpha);
+typedef void (APIENTRYP PFNGLCOLOR4HVNVPROC) (const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLTEXCOORD1HNVPROC) (GLhalfNV s);
+typedef void (APIENTRYP PFNGLTEXCOORD1HVNVPROC) (const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLTEXCOORD2HNVPROC) (GLhalfNV s, GLhalfNV t);
+typedef void (APIENTRYP PFNGLTEXCOORD2HVNVPROC) (const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLTEXCOORD3HNVPROC) (GLhalfNV s, GLhalfNV t, GLhalfNV r);
+typedef void (APIENTRYP PFNGLTEXCOORD3HVNVPROC) (const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLTEXCOORD4HNVPROC) (GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q);
+typedef void (APIENTRYP PFNGLTEXCOORD4HVNVPROC) (const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1HNVPROC) (GLenum target, GLhalfNV s);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1HVNVPROC) (GLenum target, const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2HVNVPROC) (GLenum target, const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3HVNVPROC) (GLenum target, const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4HVNVPROC) (GLenum target, const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLFOGCOORDHNVPROC) (GLhalfNV fog);
+typedef void (APIENTRYP PFNGLFOGCOORDHVNVPROC) (const GLhalfNV *fog);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue);
+typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HVNVPROC) (const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLVERTEXWEIGHTHNVPROC) (GLhalfNV weight);
+typedef void (APIENTRYP PFNGLVERTEXWEIGHTHVNVPROC) (const GLhalfNV *weight);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1HNVPROC) (GLuint index, GLhalfNV x);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1HVNVPROC) (GLuint index, const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2HVNVPROC) (GLuint index, const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3HVNVPROC) (GLuint index, const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w);
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4HVNVPROC) (GLuint index, const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBS1HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBS2HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBS3HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v);
+typedef void (APIENTRYP PFNGLVERTEXATTRIBS4HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v);
+#endif
+
+#ifndef GL_NV_pixel_data_range
+#define GL_NV_pixel_data_range 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glPixelDataRangeNV (GLenum, GLsizei, GLvoid *);
+GLAPI void APIENTRY glFlushPixelDataRangeNV (GLenum);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLPIXELDATARANGENVPROC) (GLenum target, GLsizei length, GLvoid *pointer);
+typedef void (APIENTRYP PFNGLFLUSHPIXELDATARANGENVPROC) (GLenum target);
+#endif
+
+#ifndef GL_NV_primitive_restart
+#define GL_NV_primitive_restart 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glPrimitiveRestartNV (void);
+GLAPI void APIENTRY glPrimitiveRestartIndexNV (GLuint);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLPRIMITIVERESTARTNVPROC) (void);
+typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXNVPROC) (GLuint index);
+#endif
+
+#ifndef GL_NV_texture_expand_normal
+#define GL_NV_texture_expand_normal 1
+#endif
+
+#ifndef GL_NV_vertex_program2
+#define GL_NV_vertex_program2 1
+#endif
+
+#ifndef GL_ATI_map_object_buffer
+#define GL_ATI_map_object_buffer 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI GLvoid* APIENTRY glMapObjectBufferATI (GLuint);
+GLAPI void APIENTRY glUnmapObjectBufferATI (GLuint);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef GLvoid* (APIENTRYP PFNGLMAPOBJECTBUFFERATIPROC) (GLuint buffer);
+typedef void (APIENTRYP PFNGLUNMAPOBJECTBUFFERATIPROC) (GLuint buffer);
+#endif
+
+#ifndef GL_ATI_separate_stencil
+#define GL_ATI_separate_stencil 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glStencilOpSeparateATI (GLenum, GLenum, GLenum, GLenum);
+GLAPI void APIENTRY glStencilFuncSeparateATI (GLenum, GLenum, GLint, GLuint);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEATIPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);
+typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEATIPROC) (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask);
+#endif
+
+#ifndef GL_ATI_vertex_attrib_array_object
+#define GL_ATI_vertex_attrib_array_object 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glVertexAttribArrayObjectATI (GLuint, GLint, GLenum, GLboolean, GLsizei, GLuint, GLuint);
+GLAPI void APIENTRY glGetVertexAttribArrayObjectfvATI (GLuint, GLenum, GLfloat *);
+GLAPI void APIENTRY glGetVertexAttribArrayObjectivATI (GLuint, GLenum, GLint *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLVERTEXATTRIBARRAYOBJECTATIPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset);
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTFVATIPROC) (GLuint index, GLenum pname, GLfloat *params);
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTIVATIPROC) (GLuint index, GLenum pname, GLint *params);
+#endif
+
+#ifndef GL_OES_read_format
+#define GL_OES_read_format 1
+#endif
+
+#ifndef GL_EXT_depth_bounds_test
+#define GL_EXT_depth_bounds_test 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glDepthBoundsEXT (GLclampd, GLclampd);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLDEPTHBOUNDSEXTPROC) (GLclampd zmin, GLclampd zmax);
+#endif
+
+#ifndef GL_EXT_texture_mirror_clamp
+#define GL_EXT_texture_mirror_clamp 1
+#endif
+
+#ifndef GL_EXT_blend_equation_separate
+#define GL_EXT_blend_equation_separate 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glBlendEquationSeparateEXT (GLenum, GLenum);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEEXTPROC) (GLenum modeRGB, GLenum modeAlpha);
+#endif
+
+#ifndef GL_MESA_pack_invert
+#define GL_MESA_pack_invert 1
+#endif
+
+#ifndef GL_MESA_ycbcr_texture
+#define GL_MESA_ycbcr_texture 1
+#endif
+
+#ifndef GL_EXT_pixel_buffer_object
+#define GL_EXT_pixel_buffer_object 1
+#endif
+
+#ifndef GL_NV_fragment_program_option
+#define GL_NV_fragment_program_option 1
+#endif
+
+#ifndef GL_NV_fragment_program2
+#define GL_NV_fragment_program2 1
+#endif
+
+#ifndef GL_NV_vertex_program2_option
+#define GL_NV_vertex_program2_option 1
+#endif
+
+#ifndef GL_NV_vertex_program3
+#define GL_NV_vertex_program3 1
+#endif
+
+#ifndef GL_EXT_framebuffer_object
+#define GL_EXT_framebuffer_object 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI GLboolean APIENTRY glIsRenderbufferEXT (GLuint);
+GLAPI void APIENTRY glBindRenderbufferEXT (GLenum, GLuint);
+GLAPI void APIENTRY glDeleteRenderbuffersEXT (GLsizei, const GLuint *);
+GLAPI void APIENTRY glGenRenderbuffersEXT (GLsizei, GLuint *);
+GLAPI void APIENTRY glRenderbufferStorageEXT (GLenum, GLenum, GLsizei, GLsizei);
+GLAPI void APIENTRY glRenderbufferStorageMultisampleEXT (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
+GLAPI void APIENTRY glGetRenderbufferParameterivEXT (GLenum, GLenum, GLint *);
+GLAPI GLboolean APIENTRY glIsFramebufferEXT (GLuint);
+GLAPI void APIENTRY glBindFramebufferEXT (GLenum, GLuint);
+GLAPI void APIENTRY glDeleteFramebuffersEXT (GLsizei, const GLuint *);
+GLAPI void APIENTRY glGenFramebuffersEXT (GLsizei, GLuint *);
+GLAPI GLenum APIENTRY glCheckFramebufferStatusEXT (GLenum);
+GLAPI void APIENTRY glFramebufferTexture1DEXT (GLenum, GLenum, GLenum, GLuint, GLint);
+GLAPI void APIENTRY glFramebufferTexture2DEXT (GLenum, GLenum, GLenum, GLuint, GLint);
+GLAPI void APIENTRY glFramebufferTexture3DEXT (GLenum, GLenum, GLenum, GLuint, GLint, GLint);
+GLAPI void APIENTRY glFramebufferRenderbufferEXT (GLenum, GLenum, GLenum, GLuint);
+GLAPI void APIENTRY glGetFramebufferAttachmentParameterivEXT (GLenum, GLenum, GLenum, GLint *);
+GLAPI void APIENTRY glGenerateMipmapEXT (GLenum);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFEREXTPROC) (GLuint renderbuffer);
+typedef void (APIENTRYP PFNGLBINDRENDERBUFFEREXTPROC) (GLenum target, GLuint renderbuffer);
+typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC) (GLsizei n, const GLuint *renderbuffers);
+typedef void (APIENTRYP PFNGLGENRENDERBUFFERSEXTPROC) (GLsizei n, GLuint *renderbuffers);
+typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
+typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
+typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params);
+typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFEREXTPROC) (GLuint framebuffer);
+typedef void (APIENTRYP PFNGLBINDFRAMEBUFFEREXTPROC) (GLenum target, GLuint framebuffer);
+typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSEXTPROC) (GLsizei n, const GLuint *framebuffers);
+typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSEXTPROC) (GLsizei n, GLuint *framebuffers);
+typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) (GLenum target);
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset);
+typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
+typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC) (GLenum target);
+#endif
+
+#ifndef GL_GREMEDY_string_marker
+#define GL_GREMEDY_string_marker 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glStringMarkerGREMEDY (GLsizei, const GLvoid *);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLSTRINGMARKERGREMEDYPROC) (GLsizei len, const GLvoid *string);
+#endif
+
+#ifndef GL_EXT_framebuffer_blit
+#define GL_EXT_framebuffer_blit 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glBlitFramebufferEXT (GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLbitfield, GLenum);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLBLITFRAMEBUFFEREXTPROC) (GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLbitfield, GLenum);
+#endif
+
+// http://developer.download.nvidia.com/opengl/specs/GL_NVX_gpu_memory_info.txt
+#ifndef GL_NVX_gpu_memory_info
+#define GL_NVX_gpu_memory_info 1
+#define GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX 0x9047
+#define GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX 0x9048
+#define GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX 0x9049
+#define GPU_MEMORY_INFO_EVICTION_COUNT_NVX 0x904A
+#define GPU_MEMORY_INFO_EVICTED_MEMORY_NVX 0x904B
+#endif
+
+#ifndef GL_ATI_meminfo
+#define GL_ATI_meminfo 1
+#define VBO_FREE_MEMORY_ATI 0x87FB
+#define TEXTURE_FREE_MEMORY_ATI 0x87FC
+#define RENDERBUFFER_FREE_MEMORY_ATI 0x87FD
+#endif
+
+#ifndef GL_EXT_framebuffer_multisample
+#define GL_EXT_framebuffer_multisample 1
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glRenderbufferStorageMultisampleEXT (GLenum, GLsizei, GLenum, GLsizei, GLsizei);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLenum, GLsizei, GLenum, GLsizei, GLsizei);
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/Runtime/GfxDevice/opengles/ExtensionsGLES.cpp b/Runtime/GfxDevice/opengles/ExtensionsGLES.cpp
new file mode 100644
index 0000000..a948883
--- /dev/null
+++ b/Runtime/GfxDevice/opengles/ExtensionsGLES.cpp
@@ -0,0 +1,30 @@
+#include "UnityPrefix.h"
+
+#include "ExtensionsGLES.h"
+#include "IncludesGLES.h"
+
+#if GFX_SUPPORTS_OPENGLESXX
+
+#if UNITY_IPHONE
+ #include <dlfcn.h>
+#endif
+
+void* GetGLExtProcAddress(const char* name)
+{
+#if GFX_SUPPORTS_EGL && !UNITY_WIN
+ return (void*) eglGetProcAddress(name);
+#elif UNITY_IPHONE
+
+ // on ios we link to framework, so symbols are already resolved
+ static void* selfHandle = 0;
+ if(!selfHandle)
+ selfHandle = dlopen(0, RTLD_LOCAL | RTLD_LAZY);
+
+ return selfHandle ? dlsym(selfHandle, name) : 0;
+#else
+ return 0;
+
+#endif
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/GfxDevice/opengles/ExtensionsGLES.h b/Runtime/GfxDevice/opengles/ExtensionsGLES.h
new file mode 100644
index 0000000..51de6ad
--- /dev/null
+++ b/Runtime/GfxDevice/opengles/ExtensionsGLES.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "IncludesGLES.h"
+
+#if GFX_SUPPORTS_OPENGLESXX
+
+void* GetGLExtProcAddress(const char* name);
+
+#endif \ No newline at end of file
diff --git a/Runtime/GfxDevice/opengles/IncludesGLES.h b/Runtime/GfxDevice/opengles/IncludesGLES.h
new file mode 100644
index 0000000..300199d
--- /dev/null
+++ b/Runtime/GfxDevice/opengles/IncludesGLES.h
@@ -0,0 +1,71 @@
+#pragma once
+
+#ifdef UNITYGL_H
+# error "Don't Mix with UnityGL!!!"
+#endif
+
+#define GFX_SUPPORTS_OPENGLESXX (GFX_SUPPORTS_OPENGLES20 || GFX_SUPPORTS_OPENGLES30)
+
+#if GFX_SUPPORTS_OPENGLESXX
+
+#define GFX_SUPPORTS_EGL (UNITY_WIN || UNITY_LINUX || UNITY_ANDROID || UNITY_TIZEN || UNITY_BB10)
+
+#if UNITY_ANDROID || UNITY_WEBGL
+# define GL_GLEXT_PROTOTYPES
+#endif
+
+#if GFX_SUPPORTS_OPENGLES30
+# if UNITY_WIN
+# include "PlatformDependent/WinPlayer/unityes_egl.h"
+# include "PlatformDependent/WinPlayer/unityes_gl2.h"
+ // \todo [pyry] Remove gl2ext include since most of the extensions are now in core
+# include "PlatformDependent/WinPlayer/unityes_gl2ext.h"
+# include "PlatformDependent/WinPlayer/unityes_gl3.h"
+# define INCLUDE_GLES_2X 1
+# define INCLUDE_GLES_3X 1
+# define DEF(ret,name,args) extern ret (WINAPI *name) args
+# include "PlatformDependent/WinPlayer/GLESFunctionDefs.h"
+# elif UNITY_ANDROID
+# include <EGL/egl.h>
+# include "PlatformDependent/AndroidPlayer/unityes_gl3.h"
+ // \todo [pyry] Remove gl2ext include since most of the extensions are now in core
+# include <GLES2/gl2ext.h>
+# include "Runtime/GfxDevice/opengles20/UnityGLES20Ext.h"
+# else
+# error "Unknown platform"
+# endif
+#
+# include "Runtime/GfxDevice/opengles30//UnityGLES30Ext.h"
+
+#elif GFX_SUPPORTS_OPENGLES20
+# if UNITY_WIN
+# include "PlatformDependent/WinPlayer/unityes_egl.h"
+# include "PlatformDependent/WinPlayer/unityes_gl2.h"
+# include "PlatformDependent/WinPlayer/unityes_gl2ext.h"
+# define INCLUDE_GLES_2X 1
+# define DEF(ret,name,args) extern ret (WINAPI *name) args
+# include "PlatformDependent/WinPlayer/GLESFunctionDefs.h"
+# elif UNITY_IPHONE
+# include <OpenGLES/ES2/gl.h>
+# include <OpenGLES/ES2/glext.h>
+# elif UNITY_LINUX || UNITY_ANDROID || UNITY_BB10
+# include <EGL/egl.h>
+# include <GLES2/gl2.h>
+# include <GLES2/gl2ext.h>
+# elif UNITY_PEPPER || UNITY_WEBGL
+# include <GLES2/gl2.h>
+# include <GLES2/gl2ext.h>
+# elif UNITY_TIZEN
+# include <FGraphics.h>
+# include <FGraphicsOpengl2.h>
+using namespace Tizen::Graphics;
+using namespace Tizen::Graphics::Opengl;
+# else
+# error "Unknown platform"
+# endif
+#
+# include "Runtime/GfxDevice/opengles20/UnityGLES20Ext.h"
+#
+#endif
+
+#endif // GFX_SUPPORTS_OPENGLESXX \ No newline at end of file
diff --git a/Runtime/GfxDevice/opengles20/AssertGLES20.cpp b/Runtime/GfxDevice/opengles20/AssertGLES20.cpp
new file mode 100644
index 0000000..1b77623
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/AssertGLES20.cpp
@@ -0,0 +1,58 @@
+#include "UnityPrefix.h"
+#include "AssertGLES20.h"
+#include "IncludesGLES20.h"
+
+#if GFX_SUPPORTS_OPENGLES20
+
+using namespace std;
+
+static const char* GetErrorString(GLenum glerr)
+{
+ // Error descriptions taken from OpenGLES 1.1 specification (page 13)
+ switch(glerr)
+ {
+ case GL_NO_ERROR:
+ return "GL_NO_ERROR: No error occured";
+ case GL_INVALID_ENUM:
+ return "GL_INVALID_ENUM: enum argument out of range";
+ case GL_INVALID_VALUE:
+ return "GL_INVALID_VALUE: Numeric argument out of range";
+ case GL_INVALID_OPERATION:
+ return "GL_INVALID_OPERATION: Operation illegal in current state";
+ case GL_OUT_OF_MEMORY:
+ return "GL_OUT_OF_MEMORY: Not enough memory left to execute command";
+ case 0x0506: // INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506
+ return "GL_INVALID_FRAMEBUFFER_OPERATION_EXT";
+ default:
+#if UNITY_WEBGL
+ printf_console("AssertGles20::GetErrorString invoked for unknown error %d",glerr);
+#endif
+ return "Unknown error";
+ }
+}
+
+void CheckOpenGLES2Error (const char *prefix, const char* file, long line)
+{
+ const int kMaxErrors = 10;
+ int counter = 0;
+
+ GLenum glerr;
+ while( (glerr = glGetError ()) != GL_NO_ERROR )
+ {
+ string errorString(GetErrorString(glerr));
+
+ if (prefix)
+ errorString = string(prefix) + ": " + errorString;
+
+ DebugStringToFile (errorString.c_str(), 0, file, line, kAssert);
+
+ ++counter;
+ if( counter > kMaxErrors )
+ {
+ printf_console( "GLES: error count exceeds %i, stop reporting errors\n", kMaxErrors );
+ return;
+ }
+ }
+}
+
+#endif // GFX_SUPPORTS_OPENGLES20
diff --git a/Runtime/GfxDevice/opengles20/AssertGLES20.h b/Runtime/GfxDevice/opengles20/AssertGLES20.h
new file mode 100644
index 0000000..4a9809c
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/AssertGLES20.h
@@ -0,0 +1,29 @@
+#ifndef GLES_ASSERTGLES20_H
+#define GLES_ASSERTGLES20_H
+
+#include "Runtime/Utilities/LogAssert.h"
+
+void CheckOpenGLES2Error (const char *prefix, const char* file, long line);
+
+#if !UNITY_RELEASE
+ #ifndef GLESAssert
+ /// Asserts for checking the OpenGL error state
+ #define GLESAssert() { CheckOpenGLES2Error (NULL, __FILE__, __LINE__); }
+ #endif
+ #define GLESAssertString(x) { CheckOpenGLES2Error (x, __FILE__, __LINE__); }
+ #define GLES_CHK(x) do { {x;} GLESAssert(); } while(0)
+#else
+
+ #ifndef GLESAssert
+ #define GLESAssert()
+ #endif
+ #define GLESAssertString(x)
+ #define GLES_CHK(x) x
+#endif
+
+
+//#define GLES_CHK(x) do {} while(0)
+//#define GLES_CHK(x)
+//#define GLES_CHK(x) do { printf_console("GLES: %s %d\n", __FILE__, __LINE__); } while(0)
+
+#endif
diff --git a/Runtime/GfxDevice/opengles20/CombinerGLES20.cpp b/Runtime/GfxDevice/opengles20/CombinerGLES20.cpp
new file mode 100644
index 0000000..eaff5fb
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/CombinerGLES20.cpp
@@ -0,0 +1,21 @@
+#include "UnityPrefix.h"
+#include "CombinerGLES20.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "VBOGLES20.h"
+
+#if GFX_SUPPORTS_OPENGLES20
+
+TextureCombinersGLES2* TextureCombinersGLES2::Create (int count, const ShaderLab::TextureBinding* texEnvs)
+{
+ // check if we have enough vertex attributes to emulate this combiner
+ if (count + GL_TEXTURE_ARRAY0 >= gGraphicsCaps.gles20.maxAttributes)
+ return NULL;
+
+ // create struct that holds texture combiner info object
+ TextureCombinersGLES2* combiners = new TextureCombinersGLES2();
+ combiners->count = count;
+ combiners->texEnvs = texEnvs;
+ return combiners;
+}
+
+#endif // GFX_SUPPORTS_OPENGLES20
diff --git a/Runtime/GfxDevice/opengles20/CombinerGLES20.h b/Runtime/GfxDevice/opengles20/CombinerGLES20.h
new file mode 100644
index 0000000..6c144c5
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/CombinerGLES20.h
@@ -0,0 +1,10 @@
+#pragma once
+
+namespace ShaderLab { struct TextureBinding; }
+
+struct TextureCombinersGLES2
+{
+ static TextureCombinersGLES2* Create (int count, const ShaderLab::TextureBinding* texEnvs);
+ int count;
+ const ShaderLab::TextureBinding* texEnvs;
+};
diff --git a/Runtime/GfxDevice/opengles20/ContextGLES20.cpp b/Runtime/GfxDevice/opengles20/ContextGLES20.cpp
new file mode 100644
index 0000000..1eaa8fd
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/ContextGLES20.cpp
@@ -0,0 +1,237 @@
+#include "UnityPrefix.h"
+#include "ContextGLES20.h"
+#include "IncludesGLES20.h"
+#include "AssertGLES20.h"
+#include "Runtime/Graphics/ScreenManager.h"
+
+#if GFX_SUPPORTS_OPENGLES20
+
+#if UNITY_WIN
+
+
+struct EGLESData
+{
+ void* dsp;
+ void* cfg;
+ void* cxt;
+ void* surf;
+ EGLESData():dsp(NULL),cfg(NULL),cxt(NULL),surf(NULL){}
+};
+
+
+static EGLESData sOpenGLESData;
+
+bool InitializeGLES20 ()
+{
+ HWND hwnd = GetScreenManager().GetWindow();
+ if (hwnd)
+ {
+ CreateContextGLES20 (hwnd);
+ return true;
+ }
+ ErrorString ("gles20: Can't initialize because HWND not set up");
+ return false;
+}
+void ShutdownGLES20 ()
+{
+ DestroyContextGLES20();
+}
+bool IsContextGLES20Created()
+{
+ return sOpenGLESData.surf != NULL &&
+ sOpenGLESData.cxt != NULL &&
+ sOpenGLESData.cfg != NULL &&
+ sOpenGLESData.dsp != NULL;
+}
+
+bool CreateContextGLES20(HWND hWnd)
+{
+ //Just in case
+ DestroyContextGLES20();
+
+ EGLint numConfigs;
+ EGLint majorVersion;
+ EGLint minorVersion;
+
+#if UNITY_WIN
+
+ /// Build up the attribute list
+ const EGLint configAttribs[] =
+ {
+ EGL_LEVEL, 0,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NATIVE_RENDERABLE, EGL_FALSE,
+ EGL_DEPTH_SIZE, 16,
+ EGL_NONE
+ };
+
+ // Get Display
+ sOpenGLESData.dsp = eglGetDisplay( hWnd?GetDC(hWnd):EGL_DEFAULT_DISPLAY );
+ if ( sOpenGLESData.dsp == EGL_NO_DISPLAY )
+ {
+ printf_console("GLES20: eglGetDisplay failed\n" );
+ return false;
+ }
+ //Hack : eglInitialize invokes WM_ACTIVATE message, and gAppActive is already true, so Unity will try to call some functions which requires some initialization,
+ // and this is not done yet
+ extern bool gAlreadyClosing;
+ bool last = gAlreadyClosing;
+ gAlreadyClosing = true;
+ // Initialize EGL
+ if ( ! eglInitialize( sOpenGLESData.dsp, &majorVersion, &minorVersion) )
+ {
+ printf_console("GLES20: eglInitialize failed\n");
+ return false;
+ }
+
+
+ // Choose config
+ if ( !eglChooseConfig(sOpenGLESData.dsp, configAttribs, &sOpenGLESData.cfg, 1, &numConfigs) )
+ {
+ printf_console("GLES20: eglChooseConfig failed\n");
+ return false;
+ }
+
+
+ // Create a surface
+ sOpenGLESData.surf = eglCreateWindowSurface( sOpenGLESData.dsp, sOpenGLESData.cfg, NativeWindowType( hWnd ), NULL );
+ if ( sOpenGLESData.surf == EGL_NO_SURFACE )
+ {
+ printf_console("GLES20: eglCreateWindowSurface failed\n");
+ return false;
+ }
+
+ // Create a GL context
+ EGLint ctxAttribList[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
+ sOpenGLESData.cxt = eglCreateContext( sOpenGLESData.dsp, sOpenGLESData.cfg, EGL_NO_CONTEXT, ctxAttribList );
+ if ( sOpenGLESData.cxt == EGL_NO_CONTEXT )
+ {
+ printf_console("GLES20: eglCreateContext failed\n");
+ return false;
+ }
+
+ // Make the context current
+ if ( ! eglMakeCurrent( sOpenGLESData.dsp, sOpenGLESData.surf, sOpenGLESData.surf, sOpenGLESData.cxt ) )
+ {
+ printf_console("GLES20: eglMakeCurrent failed\n");
+ return false;
+ }
+
+ gAlreadyClosing = last;
+#endif
+
+ GLESAssert();
+
+ return true;
+}
+
+
+void DestroyContextGLES20()
+{
+ if(sOpenGLESData.dsp)
+ {
+ eglMakeCurrent(sOpenGLESData.dsp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) ;
+ eglDestroyContext( sOpenGLESData.dsp, sOpenGLESData.cxt );
+ eglDestroySurface( sOpenGLESData.dsp, sOpenGLESData.surf );
+ eglTerminate( sOpenGLESData.dsp);
+ }
+ sOpenGLESData.surf = NULL;
+ sOpenGLESData.cxt = NULL;
+ sOpenGLESData.cfg = NULL;
+ sOpenGLESData.dsp = NULL;
+}
+void PresentContextGLES20()
+{
+ eglSwapBuffers( sOpenGLESData.dsp, sOpenGLESData.surf );
+}
+
+#elif UNITY_LINUX
+
+static EGLDisplay gEGLDisplay = EGL_NO_DISPLAY;
+static EGLConfig gEGLConfig;
+static EGLSurface gEGLSurface = EGL_NO_SURFACE;
+static EGLContext gEGLContext = EGL_NO_CONTEXT;
+
+void SetEGLDisplay(EGLDisplay display)
+{
+ gEGLDisplay = display;
+}
+
+void SetEGLConfig(const EGLConfig &config)
+{
+ gEGLConfig = config;
+}
+
+bool InitializeGLES20 ()
+{
+ Window window = 0;
+ window = GetScreenManager().GetWindow();
+
+ if(window)
+ {
+ CreateContextGLES20(window);
+ return true;
+ }
+
+ return false;
+}
+
+void ShutdownGLES20 ()
+{
+ DestroyContextGLES20();
+}
+
+bool IsContextGLES20Created()
+{
+ return gEGLSurface != EGL_NO_SURFACE && gEGLContext != EGL_NO_CONTEXT;
+}
+
+bool CreateContextGLES20(Window window)
+{
+ DestroyContextGLES20();
+
+ gEGLSurface = eglCreateWindowSurface(gEGLDisplay, gEGLConfig, window, NULL);
+ if(gEGLSurface == EGL_NO_SURFACE)
+ {
+ printf_console("eglCreateWindowSurface failed\n");
+ return false;
+ }
+
+ // Create a context
+ printf_console("Creating context\n");
+ EGLint ctxAttribList[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
+ gEGLContext = eglCreateContext(gEGLDisplay, gEGLConfig, EGL_NO_CONTEXT, ctxAttribList );
+ if ( gEGLContext == EGL_NO_CONTEXT )
+ {
+ printf_console( "eglCreateContext failed\n" );
+ return false;
+ }
+
+ if(!eglMakeCurrent(gEGLDisplay, gEGLSurface, gEGLSurface, gEGLContext))
+ {
+ printf_console("eglMakeCurrent failed\n");
+ }
+
+ return true;
+}
+
+void DestroyContextGLES20()
+{
+ if(IsContextGLES20Created())
+ {
+ eglMakeCurrent(gEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroyContext(gEGLDisplay, gEGLContext);
+ eglDestroySurface(gEGLDisplay, gEGLSurface);
+ }
+
+ gEGLSurface = EGL_NO_SURFACE;
+ gEGLContext = EGL_NO_CONTEXT;
+}
+
+void PresentContextGLES()
+{
+ eglSwapBuffers(gEGLDisplay, gEGLSurface);
+}
+#endif // UNITY_WIN
+#endif // GFX_SUPPORTS_OPENGLES20
diff --git a/Runtime/GfxDevice/opengles20/ContextGLES20.h b/Runtime/GfxDevice/opengles20/ContextGLES20.h
new file mode 100644
index 0000000..4fab18b
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/ContextGLES20.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "Runtime/GfxDevice/GfxDeviceObjects.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+#if UNITY_LINUX
+#include <X11/Xlib.h>
+#include <GLES2/gl2.h>
+#endif
+
+#if UNITY_BB10
+#include <GLES2/gl2.h>
+#include <screen/screen.h>
+#endif
+
+#if UNITY_WIN || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN || UNITY_ANDROID
+bool InitializeGLES20 ();
+void ShutdownGLES20 ();
+bool IsContextGLES20Created();
+#if UNITY_WIN
+bool CreateContextGLES20(HWND hWnd);
+#elif UNITY_LINUX
+bool CreateContextGLES20(Window window);
+#elif UNITY_BB10
+bool CreateContextGLES20(screen_window_t window);
+void ResizeContextGLES20(screen_window_t window, int width, int height);
+void AdjustVsync(int val);
+#elif UNITY_TIZEN
+bool CreateContextGLES20();
+void ResizeContextGLES20(int width, int height);
+#endif
+#if !UNITY_ANDROID
+void DestroyContextGLES20();
+#endif
+#endif
+void PresentContextGLES();
+void PresentContextGLES20();
+
+#if UNITY_ANDROID
+void ReleaseGLES20Context();
+
+void AcquireGLES20Context();
+#endif
diff --git a/Runtime/GfxDevice/opengles20/DebugGLES20.cpp b/Runtime/GfxDevice/opengles20/DebugGLES20.cpp
new file mode 100644
index 0000000..d087e07
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/DebugGLES20.cpp
@@ -0,0 +1,38 @@
+#include "UnityPrefix.h"
+#include "IncludesGLES20.h"
+#include "DebugGLES20.h"
+#include "AssertGLES20.h"
+
+
+void DumpVertexArrayStateGLES20()
+{
+#if GFX_SUPPORTS_OPENGLES20
+ GLint maxVertexAttribs = 0;
+ GLES_CHK(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs));
+
+ GLint vbo = 0;
+ GLint ibo = 0;
+ GLES_CHK(glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vbo));
+ GLES_CHK(glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &ibo));
+
+ printf_console("---> VertexArray State: vbo:%d ibo:%d\n", vbo, ibo);
+
+ for (int q = 0; q < maxVertexAttribs; ++q)
+ {
+ int enabled, size, stride, normalized, type, vbo;
+ GLES_CHK(glGetVertexAttribiv(q, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled));
+ GLES_CHK(glGetVertexAttribiv(q, GL_VERTEX_ATTRIB_ARRAY_SIZE, &size));
+ GLES_CHK(glGetVertexAttribiv(q, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &stride));
+ GLES_CHK(glGetVertexAttribiv(q, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &normalized));
+ GLES_CHK(glGetVertexAttribiv(q, GL_VERTEX_ATTRIB_ARRAY_TYPE, &type));
+ GLES_CHK(glGetVertexAttribiv(q, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &vbo));
+
+ GLvoid* ptr;
+ GLES_CHK(glGetVertexAttribPointerv(q, GL_VERTEX_ATTRIB_ARRAY_POINTER, &ptr));
+
+ printf_console(" attr[%d] --- %s type:%d size:%d stride:%d norm:%d vbo:%d, %p\n",
+ q, enabled? "On ": "Off",
+ type, size, stride, normalized, vbo, ptr);
+ }
+#endif
+}
diff --git a/Runtime/GfxDevice/opengles20/DebugGLES20.h b/Runtime/GfxDevice/opengles20/DebugGLES20.h
new file mode 100644
index 0000000..b8c0004
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/DebugGLES20.h
@@ -0,0 +1,139 @@
+#ifndef DEBUGGLES20_H
+#define DEBUGGLES20_H
+
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/Math/Matrix4x4.h"
+
+
+void DumpVertexArrayStateGLES20();
+
+#if !UNITY_RELEASE
+ #define DBG_LOG_GLES20_ACTIVE 0
+ #define DBG_TEXTURE_VERBOSE_GLES20_ACTIVE 0
+ #define DBG_SHADER_VERBOSE_GLES20_ACTIVE 0
+ #define DBG_GLSL_BINDINGS_GLES20_ACTIVE 0
+#else
+ #define DBG_LOG_GLES20_ACTIVE 0
+ #define DBG_TEXTURE_VERBOSE_GLES20_ACTIVE 0
+ #define DBG_SHADER_VERBOSE_GLES20_ACTIVE 0
+ #define DBG_GLSL_BINDINGS_GLES20_ACTIVE 0
+#endif
+
+#if DBG_LOG_GLES20_ACTIVE
+ #define DBG_LOG_GLES20(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+
+ inline std::string GetMatrixString(const Matrix4x4f& mtx)
+ {
+ return Format("%.2f, %.2f, %.2f, %2f,\n"
+ "%.2f, %.2f, %.2f, %2f,\n"
+ "%.2f, %.2f, %.2f, %2f,\n"
+ "%.2f, %.2f, %.2f, %2f",
+ mtx[0], mtx[1], mtx[2], mtx[3],
+ mtx[4], mtx[5], mtx[6], mtx[7],
+ mtx[8], mtx[9], mtx[10],mtx[11],
+ mtx[12],mtx[13],mtx[14],mtx[15]);
+ }
+
+ inline const char* GetBoolString(bool type)
+ {
+ return type?"True":"False";
+ }
+
+ inline const char * GetBlendModeString(BlendMode type)
+ {
+ switch(type)
+ {
+ case kBlendZero:return "kBlendZero";
+ case kBlendOne:return "kBlendOne";
+ case kBlendDstColor:return "kBlendDstColor";
+ case kBlendSrcColor:return "kBlendSrcColor";
+ case kBlendOneMinusDstColor:return "kBlendOneMinusDstColor";
+ case kBlendSrcAlpha:return "kBlendSrcAlpha";
+ case kBlendOneMinusSrcColor:return "kBlendOneMinusSrcColor";
+ case kBlendDstAlpha:return "kBlendDstAlpha";
+ case kBlendOneMinusDstAlpha:return "kBlendOneMinusDstAlpha";
+ case kBlendSrcAlphaSaturate:return "kBlendSrcAlphaSaturate";
+ case kBlendOneMinusSrcAlpha:return "kBlendOneMinusSrcAlpha";
+ default:return "GetBlendModeString<Undefined>";
+ }
+ }
+ inline const char * GetCullModeString(CullMode type)
+ {
+ switch(type)
+ {
+ case kCullUnknown:return "kCullUnknown";
+ case kCullOff:return "kCullOff:return";
+ case kCullFront:return "kCullFront";
+ case kCullBack:return "kCullBack";;
+ default:return "GetCullMode<undefined>";
+ }
+ }
+
+ inline const char * GetCompareFunctionString(CompareFunction type)
+ {
+ switch(type)
+ {
+ case kFuncUnknown:return "kFuncUnknown";
+ case kFuncDisabled:return "kFuncDisabled";
+ case kFuncNever:return "kFuncNever";
+ case kFuncLess:return "kFuncLess";
+ case kFuncEqual:return "kFuncEqual";
+ case kFuncLEqual:return "kFuncLEqual";
+ case kFuncGreater:return "kFuncGreater";
+ case kFuncNotEqual:return "kFuncNotEqual";
+ case kFuncGEqual:return "kFuncGEqual";
+ case kFuncAlways:return "kFuncAlways";
+ default:return "GetCompareFunctionString<Undefined>";
+ }
+ }
+
+ inline const char * GetShaderTypeString(ShaderType type)
+ {
+ switch(type)
+ {
+ case kShaderNone:return "kShaderNone";
+ case kShaderVertex:return "kShaderVertex";
+ case kShaderFragment:return "kShaderFragment";
+ default:return "GetShaderTypeString<undefined>";
+ }
+ }
+
+ inline const char * GetShaderImplTypeString(ShaderImplType type)
+ {
+ switch(type)
+ {
+ case kShaderImplUndefined: return "kShaderImplUndefined";
+ case kShaderImplVertex: return "kShaderImplVertex";
+ case kShaderImplFragment: return "kShaderImplFragment";
+ case kShaderImplBoth: return "kShaderImplBoth";
+ default:return "GetShaderImplTypeString<Undefined>";
+ }
+ }
+
+#else
+ #define DBG_LOG_GLES20(...)
+#endif
+
+#if DBG_TEXTURE_VERBOSE_GLES20_ACTIVE
+#define DBG_TEXTURE_VERBOSE_GLES20(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+#else
+#define DBG_TEXTURE_VERBOSE_GLES20(...)
+#endif
+
+#if DBG_SHADER_VERBOSE_GLES20_ACTIVE
+ #define DBG_SHADER_VERBOSE_GLES20(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+ #define DBG_SHADER_VERBOSE_GLES20_DUMP_SHADER(prefix, text) { printf_console("%s\n", prefix);DebugTextLineByLine(text);printf_console("\n---\n");}
+#else
+ #define DBG_SHADER_VERBOSE_GLES20(...)
+ #define DBG_SHADER_VERBOSE_GLES20_DUMP_SHADER(prefix, text)
+#endif
+
+#if DBG_GLSL_BINDINGS_GLES20_ACTIVE
+ #define DBG_GLSL_BINDINGS_GLES20(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+#else
+ #define DBG_GLSL_BINDINGS_GLES20(...)
+#endif
+
+
+#endif
diff --git a/Runtime/GfxDevice/opengles20/FixedFunctionStateGLES20.cpp b/Runtime/GfxDevice/opengles20/FixedFunctionStateGLES20.cpp
new file mode 100644
index 0000000..4df51e1
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/FixedFunctionStateGLES20.cpp
@@ -0,0 +1,73 @@
+#include "UnityPrefix.h"
+#include "FixedFunctionStateGLES20.h"
+#include <sstream>
+
+
+FixedFunctionStateGLES20::FixedFunctionStateGLES20 ()
+: texUnitCount(0),
+ lightType(0),
+ texUnitMatrix(0),
+ lightingEnabled(false),
+ specularEnabled(true),
+ onlyDirectionalLights(false),
+ lightCount(0),
+ useUniformInsteadOfVertexColor(false),
+ useVertexColorAsAmbientAndDiffuse(false),
+ useVertexColorAsEmission(false),
+ fogMode(kFogDisabled),
+ addSpecularAfterTexturing(false),
+ alphaTest(kFuncDisabled),
+ setupPointSize(false)
+{
+ for (int q = 0; q < kMaxSupportedTextureUnitsGLES; ++q)
+ {
+ texUnitCube[q] = false;
+ texUnitGen[q] = kTexGenDisabled;
+ texUnitColorCombiner[q] = ~0UL;
+ texUnitAlphaCombiner[q] = ~0UL;
+ }
+}
+
+static std::string CombinerToString (unsigned int combinerDesc)
+{
+ std::ostringstream s;
+ s << combinerDesc;
+ return s.str().c_str();
+}
+
+
+std::string FixedFunctionStateGLES20::ToString () const
+{
+ std::ostringstream s;
+
+ s << "FixedFunctionStateGLES20::ToString():\n";
+ s << " lightingEnabled = " << lightingEnabled << "\n";
+ s << " specularEnabled = " << specularEnabled << "\n";
+ s << " lights = " << lightCount << "\n";
+ for (int i = 0; i < lightCount; ++i)
+ s << " light" << i << " : " << GetLightType(i) << "\n";
+
+ s << " useUniformInsteadOfVertexColor = " << useUniformInsteadOfVertexColor << "\n";
+ s << " useVertexColorAsAmbientAndDiffuse = " << useVertexColorAsAmbientAndDiffuse << "\n";
+ s << " useVertexColorAsEmission = " << useVertexColorAsEmission << "\n";
+
+ s << " fogMode = " << fogMode << "\n";
+
+ for (int i = 0; i < texUnitCount; ++i)
+ {
+ s << " texture " << i << "\n";
+
+ s << " CUBE = " << ((texUnitCube[i])? "true": "false") << "\n";
+ s << " rgb combiner = " << CombinerToString(texUnitColorCombiner[i]) << "\n";
+ s << " alpba combiner = " << CombinerToString(texUnitAlphaCombiner[i]) << "\n";
+ s << " texGen = " << texUnitGen[i] << "\n";
+ s << " need matrix: " << (NeedTexUnitMatrix(i)?"true":"false") << "\n";
+ s << " need perspective divide: " << (IsTexUnitProjected(i)?"true":"false") << "\n";
+ }
+
+ s << " addSpecularafterTexturing = " << addSpecularAfterTexturing << "\n";
+ s << " alphaTest = " << alphaTest << "\n";
+ s << " setupPointSize = " << setupPointSize << "\n";
+
+ return s.str().c_str();
+}
diff --git a/Runtime/GfxDevice/opengles20/FixedFunctionStateGLES20.h b/Runtime/GfxDevice/opengles20/FixedFunctionStateGLES20.h
new file mode 100644
index 0000000..9fe723e
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/FixedFunctionStateGLES20.h
@@ -0,0 +1,66 @@
+#ifndef FIXEDFUNCTIONSTATE_GLES20_H
+#define FIXEDFUNCTIONSTATE_GLES20_H
+
+#include "IncludesGLES20.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include <string>
+
+// we can use one var to determine both shift and mask, but let help out the compiler ;-)
+#define FFPSTATE_SET_MASK(target, idx, val, shift_mul, mask) \
+do{ \
+ target &= ~(mask << ((idx)*shift_mul)); \
+ target |= (val << ((idx)*shift_mul)); \
+}while(0) \
+
+
+
+class FixedFunctionStateGLES20
+{
+public:
+ FixedFunctionStateGLES20();
+
+ UInt32 texUnitColorCombiner[kMaxSupportedTextureUnitsGLES];
+ UInt32 texUnitAlphaCombiner[kMaxSupportedTextureUnitsGLES];
+ bool texUnitCube[kMaxSupportedTextureUnitsGLES];
+ int texUnitGen[kMaxSupportedTextureUnitsGLES];
+ int texUnitCount;
+
+ // we will use 4bits per light - this way we can handle up to 8 lights (though kMaxEmulatedVertexLights = 4)
+ UInt32 lightType;
+ // we will use 2 bits per tex unit - one for perspective divide, the other one for if we need matrix mul at all
+ // this way we can store 16 texunit info (though kMaxSupportedTextureUnitsGLES = 8)
+ UInt32 texUnitMatrix;
+
+ int lightCount : 8;
+ FogMode fogMode : 8;
+ CompareFunction alphaTest : 8;
+
+ bool lightingEnabled;
+ bool specularEnabled;
+ bool onlyDirectionalLights;
+ bool setupPointSize;
+
+ bool useUniformInsteadOfVertexColor;
+ bool useVertexColorAsAmbientAndDiffuse;
+ bool useVertexColorAsEmission;
+ bool addSpecularAfterTexturing;
+
+ unsigned GetLightType(int i) const { return (lightType >> (i*4)) & 0xF; }
+ void SetLightType(int i, unsigned type) { FFPSTATE_SET_MASK(lightType, i, type, 4, 0xF); }
+
+ bool NeedTexUnitMatrix(int i) const { return ((texUnitMatrix >> (i*2)) & 0x1) != 0; }
+ bool IsTexUnitProjected(int i) const { return ((texUnitMatrix >> (i*2)) & 0x2) != 0; }
+
+ void SetTexUnitMatrixParam(int i, bool hasMatrix, bool isProjected)
+ {
+ int mask = (hasMatrix ? 1 : 0) | (isProjected ? 2 : 0);
+ FFPSTATE_SET_MASK(texUnitMatrix, i, mask, 2, 0x3);
+ }
+
+ std::string ToString () const;
+};
+
+
+#undef FFPSTATE_SET_MASK
+
+#endif /* FIXEDFUNCTIONSTATE_GLES20_H */
diff --git a/Runtime/GfxDevice/opengles20/GfxDeviceGLES20.cpp b/Runtime/GfxDevice/opengles20/GfxDeviceGLES20.cpp
new file mode 100644
index 0000000..cf59ce6
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/GfxDeviceGLES20.cpp
@@ -0,0 +1,2815 @@
+#include "UnityPrefix.h"
+#if GFX_SUPPORTS_OPENGLES20
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Shaders/MaterialProperties.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Quaternion.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/program.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Threads/AtomicOps.h"
+#include "Runtime/GfxDevice/TransformState.h"
+#include "Runtime/GfxDevice/GpuProgramParamsApply.h"
+#include "IncludesGLES20.h"
+#include "AssertGLES20.h"
+#include "ContextGLES20.h"
+#include "VBOGLES20.h"
+#include "TexturesGLES20.h"
+#include "CombinerGLES20.h"
+#include "GpuProgramsGLES20.h"
+#include "FixedFunctionStateGLES20.h"
+#include "ShaderGeneratorGLES20.h"
+#include "RenderTextureGLES20.h"
+#include "DebugGLES20.h"
+#include "TimerQueryGLES20.h"
+#include "TextureIdMapGLES20.h"
+#include "UnityGLES20Ext.h"
+
+#if UNITY_IPHONE
+ #include "PlatformDependent/iPhonePlayer/iPhoneSettings.h"
+#elif UNITY_ANDROID
+ #include "PlatformDependent/AndroidPlayer/ContextGLES.h"
+ #include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+ #include "PlatformDependent/AndroidPlayer/AndroidSystemInfo.h"
+#endif
+
+#include "Runtime/GfxDevice/GLDataBufferCommon.h"
+
+#if NV_STATE_FILTERING
+
+void filteredInitGLES20();
+void filteredBindBufferGLES20(GLenum target, GLuint buffer, bool isImmediate);
+void filteredVertexAttribPointerGLES20(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer);
+void filteredDeleteBuffersGLES20(GLsizei n, const GLuint *buffers);
+
+#ifdef glBindBuffer
+#undef glBindBuffer
+#endif
+#define glBindBuffer(a, b) filteredBindBufferGLES20(a, b, true)
+#ifndef glDeleteBuffers
+#define glDeleteBuffers filteredDeleteBuffersGLES20
+#endif
+#ifndef glVertexAttribPointer
+#define glVertexAttribPointer filteredVertexAttribPointerGLES20
+#endif
+
+#endif
+
+// let's play safe here:
+// ios/glesemu works just fine
+// and shadows demands more careful eps choice - do it later
+#define WORKAROUND_POLYGON_OFFSET UNITY_ANDROID
+
+// forward declarations
+
+bool IsActiveRenderTargetWithColorGLES2(); // RenderTextureGL.cpp
+namespace ShaderLab {
+ TexEnv* GetTexEnvForBinding( const TextureBinding& binding, const PropertySheet* props ); // pass.cpp
+}
+
+// local forward declarations
+struct DeviceStateGLES20;
+static void ApplyBackfaceMode( const DeviceStateGLES20& state );
+static GLuint GetSharedFBO (DeviceStateGLES20& state);
+
+extern GLint gDefaultFBO;
+
+// NOTE: GLES2.0 supports only 4 lights for now
+enum { kMaxSupportedVertexLightsByGLES20 = 4 };
+
+// Constant tables
+static const unsigned int kBlendModeES2[] = {
+ GL_ZERO, GL_ONE, GL_DST_COLOR, GL_SRC_COLOR, GL_ONE_MINUS_DST_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR,
+ GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA_SATURATE, GL_ONE_MINUS_SRC_ALPHA,
+};
+
+static const unsigned int kBlendFuncES2[] = {
+ GL_FUNC_ADD, GL_FUNC_SUBTRACT,
+ GL_FUNC_REVERSE_SUBTRACT, GL_MIN_EXT, GL_MAX_EXT,
+};
+
+static const unsigned int kCmpFuncES2[] = {
+ GL_NEVER, GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL, GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS
+};
+
+static const unsigned int kStencilOpES2[] = {
+ GL_KEEP, GL_ZERO, GL_REPLACE, GL_INCR,
+ GL_DECR, GL_INVERT, GL_INCR_WRAP, GL_DECR_WRAP
+};
+
+static const GLenum kWrapModeES2[kTexWrapCount] = {
+ GL_REPEAT,
+ GL_CLAMP_TO_EDGE,
+};
+
+static const GLint kMinFilterES2[kTexFilterCount] = { GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR };
+
+// --------------------------------------------------------------------------
+
+struct DeviceDepthStateGLES20 : public DeviceDepthState
+{
+ UInt32 depthFunc;
+};
+
+struct DeviceStencilStateGLES20 : public DeviceStencilState
+{
+ GLenum stencilFuncFront;
+ GLenum stencilFailOpFront;
+ GLenum depthFailOpFront;
+ GLenum depthPassOpFront;
+ GLenum stencilFuncBack;
+ GLenum stencilFailOpBack;
+ GLenum depthFailOpBack;
+ GLenum depthPassOpBack;
+};
+
+
+typedef std::map< GfxBlendState, DeviceBlendState, memcmp_less<GfxBlendState> > CachedBlendStates;
+typedef std::map< GfxDepthState, DeviceDepthStateGLES20, memcmp_less<GfxDepthState> > CachedDepthStates;
+typedef std::map< GfxStencilState, DeviceStencilStateGLES20, memcmp_less<GfxStencilState> > CachedStencilStates;
+typedef std::map< GfxRasterState, DeviceRasterState, memcmp_less<GfxRasterState> > CachedRasterStates;
+
+// --------------------------------------------------------------------------
+struct TextureUnitStateGLES2
+{
+ GLuint texID;
+ TextureDimension texDim;
+ unsigned int combColor, combAlpha;
+ Vector4f color;
+ TexGenMode texGen;
+ Matrix4x4f textureMatrix;
+ float bias;
+
+ // right let waste space here instead of device-level int
+ bool identityMatrix;
+ bool isProjected;
+ bool posForTexGen;
+ bool nrmForTexGen;
+
+ void Invalidate();
+
+ static bool PositionRequiredForTexGen(TexGenMode mode)
+ {
+ return (mode == kTexGenObject || mode == kTexGenEyeLinear);
+ }
+
+ static bool NormalRequiredForTexGen(TexGenMode mode)
+ {
+ return (mode == kTexGenSphereMap || mode == kTexGenCubeReflect || mode == kTexGenCubeNormal);
+ }
+
+ void SetTexGen( TexGenMode mode )
+ {
+ posForTexGen = PositionRequiredForTexGen(mode);
+ nrmForTexGen = NormalRequiredForTexGen(mode);
+
+ texGen = mode;
+ }
+};
+
+void TextureUnitStateGLES2::Invalidate()
+{
+ texID = -1;
+ texDim = kTexDimNone;
+ combColor = combAlpha = 0xFFFFFFFF;
+ color.Set( -1, -1, -1, -1 );
+ texGen = kTexGenUnknown;
+ posForTexGen = 0;
+ nrmForTexGen = 0;
+ textureMatrix.SetIdentity();
+ identityMatrix = true;
+ isProjected = false;
+ bias = 1.0e6f;
+}
+
+
+// --------------------------------------------------------------------------
+// TODO: optimize this. Right now we just send off whole 8 float3 UVs with each
+// immediate mode vertex. We could at least detect the number of them used from
+// ImmediateTexCoord calls.
+struct ImmediateVertexGLES20 {
+ Vector3f vertex;
+ Vector3f normal;
+ UInt32 color;
+ Vector3f texCoords[8];
+};
+
+struct ImmediateModeGLES20 {
+ std::vector<ImmediateVertexGLES20> m_Vertices;
+ ImmediateVertexGLES20 m_Current;
+ GfxPrimitiveType m_Mode;
+ UInt16* m_QuadsIB;
+
+ int m_IndexBufferQuadsID;
+
+ ImmediateModeGLES20();
+ ~ImmediateModeGLES20();
+ void Invalidate();
+};
+
+// --------------------------------------------------------------------------
+struct DeviceStateGLES20
+{
+ GLuint m_SharedFBO;
+ GLuint m_HelperFBO;
+ int m_TextureIDGenerator;
+
+ int depthFunc;
+ int depthWrite; // 0/1 or -1
+
+ int blending;
+ int srcBlend, destBlend, srcBlendAlpha, destBlendAlpha; // Blend modes
+ int blendOp, blendOpAlpha;
+ CompareFunction alphaTest;
+ float alphaValue;
+
+ CullMode culling;
+ bool appBackfaceMode, userBackfaceMode;
+ NormalizationMode
+ normalization;
+ int scissor;
+
+ bool lighting;
+ bool separateSpecular;
+ SimpleVec4 matDiffuse, matAmbient, matSpecular, matEmissive;
+ SimpleVec4 ambient;
+ float matShininess;
+ ColorMaterialMode
+ colorMaterial;
+
+ float offsetFactor, offsetUnits;
+
+ int colorWriteMask; // ColorWriteMask combinations
+ SimpleVec4 color;
+
+ TextureUnitStateGLES2
+ textures[kMaxSupportedTextureUnitsGLES];
+ int textureCount;
+ int activeTextureUnit;
+
+ // pure optimization: texGen is very special case and is used sparingly
+ UInt32 positionTexGen;
+ UInt32 normalTexGen;
+
+ int vertexLightCount;
+ LightType vertexLightTypes[kMaxSupportedVertexLights];
+
+ TransformState transformState;
+
+ DynamicVBO* m_DynamicVBO;
+ bool vboContainsColor;
+
+ int viewport[4];
+ int scissorRect[4];
+
+ // should be set before BeforeDrawCall call
+ GfxPrimitiveType drawCallTopology;
+
+
+ GpuProgram* activeProgram;
+ const GpuProgramParameters* activeProgramParams;
+ dynamic_array<UInt8> activeProgramParamsBuffer;
+ UInt32 activeProgramID;
+
+
+ CachedBlendStates m_CachedBlendStates;
+ CachedDepthStates m_CachedDepthStates;
+ CachedStencilStates m_CachedStencilStates;
+ CachedRasterStates m_CachedRasterStates;
+
+ const DeviceBlendState* m_CurrBlendState;
+ const DeviceDepthStateGLES20* m_CurrDepthState;
+ const DeviceStencilStateGLES20* m_CurrStencilState;
+ int m_StencilRef;
+ const DeviceRasterState* m_CurrRasterState;
+
+ ImmediateModeGLES20 m_Imm;
+
+public:
+ DeviceStateGLES20();
+ void Invalidate();
+ void ComputeFixedFunctionState(FixedFunctionStateGLES20& state, const GfxFogParams& fog) const;
+
+ inline void ApplyTexGen( UInt32 unit );
+ inline void DropTexGen( UInt32 unit );
+};
+
+DeviceStateGLES20::DeviceStateGLES20()
+: m_DynamicVBO(0)
+{
+ m_TextureIDGenerator = 0;
+}
+
+void DeviceStateGLES20::Invalidate()
+{
+ DBG_LOG_GLES20("Invalidate");
+ int i;
+
+ depthFunc = -1; //unknown
+ depthWrite = -1;
+
+ blending = -1; // unknown
+ srcBlend = destBlend = srcBlendAlpha = destBlendAlpha = -1; // won't match any GL mode
+ blendOp = blendOpAlpha = -1;
+ alphaTest = kFuncUnknown;
+ alphaValue = -1.0f;
+
+ culling = kCullUnknown;
+ normalization = kNormalizationUnknown;
+ scissor = -1;
+
+ lighting = false;
+ separateSpecular = false;
+
+ matDiffuse.set( -1, -1, -1, -1 );
+ matAmbient.set( -1, -1, -1, -1 );
+ matSpecular.set( -1, -1, -1, -1 );
+ matEmissive.set( -1, -1, -1, -1 );
+ ambient.set( -1, -1, -1, -1 );
+ matShininess = -1.0f;
+ colorMaterial = kColorMatUnknown;
+
+ offsetFactor = offsetUnits = -1000.0f;
+
+ colorWriteMask = -1; // TBD ?
+ m_StencilRef = -1;
+
+ color.set( -1, -1, -1, -1 );
+
+ activeTextureUnit = -1;
+ for( i = 0; i < kMaxSupportedTextureUnitsGLES; ++i )
+ textures[i].Invalidate();
+ textureCount = 0;
+
+ positionTexGen = 0;
+ normalTexGen = 0;
+
+ vertexLightCount = 0;
+ for ( i = 0; i < kMaxSupportedVertexLights; ++i)
+ vertexLightTypes[i] = kLightDirectional;
+
+ // make sure backface mode is in sync
+ appBackfaceMode = false;
+ userBackfaceMode = false;
+ ApplyBackfaceMode( *this );
+
+ vboContainsColor = true;
+
+ viewport[0] = 0;
+ viewport[1] = 0;
+ viewport[2] = 0;
+ viewport[3] = 0;
+
+ scissorRect[0] = 0;
+ scissorRect[1] = 0;
+ scissorRect[2] = 0;
+ scissorRect[3] = 0;
+
+ activeProgram = 0;
+ activeProgramParams = 0;
+ activeProgramParamsBuffer.resize_uninitialized(0);
+ activeProgramID = -1;
+
+ m_Imm.Invalidate();
+
+ InvalidateVertexInputCacheGLES20();
+
+ GLESAssert();
+}
+
+void DeviceStateGLES20::ComputeFixedFunctionState(FixedFunctionStateGLES20& state, const GfxFogParams& fog) const
+{
+ if (lighting)
+ {
+ int numLights = vertexLightCount;
+ bool onlyDir = true;
+ for(int i = 0 ; i < numLights ; ++i)
+ {
+ onlyDir = onlyDir && (vertexLightTypes[i] == kLightDirectional);
+ state.SetLightType(i,vertexLightTypes[i]);
+ }
+
+ state.lightingEnabled = true;
+ state.lightCount = numLights;
+ state.onlyDirectionalLights = onlyDir;
+ state.specularEnabled = separateSpecular;
+
+ switch (colorMaterial)
+ {
+ case kColorMatDisabled:
+ break;
+
+ case kColorMatAmbientAndDiffuse:
+ state.useVertexColorAsAmbientAndDiffuse = true;
+ break;
+
+ case kColorMatEmission:
+ state.useVertexColorAsEmission = true;
+ break;
+
+ default:
+ ErrorString("Unsupported color material mode");
+ break;
+ }
+ }
+ else
+ {
+ state.lightingEnabled = false;
+ state.lightCount = 0;
+ }
+
+ state.useUniformInsteadOfVertexColor = !vboContainsColor;
+ state.texUnitCount = textureCount;
+
+ for (int i = 0; i < textureCount; i++)
+ {
+ Assert(textures[i].texDim != kTexDimUnknown);
+ Assert(textures[i].texDim != kTexDimNone);
+ Assert(textures[i].texDim != kTexDimAny);
+ Assert(textures[i].texDim != kTexDim3D); // OpenGLES2.0 does NOT supports 3D textures
+ state.texUnitCube[i] = (textures[i].texDim == kTexDimCUBE);
+ state.texUnitColorCombiner[i] = textures[i].combColor,
+ state.texUnitAlphaCombiner[i] = textures[i].combAlpha;
+ state.texUnitGen[i] = textures[i].texGen;
+
+ bool needMatrix = !textures[i].identityMatrix || textures[i].texGen > kTexGenDisabled;
+ state.SetTexUnitMatrixParam(i, needMatrix, textures[i].isProjected);
+ }
+
+ state.fogMode = fog.mode;
+ switch (fog.mode)
+ {
+ case kFogUnknown:
+ case kFogDisabled:
+ state.fogMode = kFogDisabled;
+ default:
+ break;
+ }
+
+ if(gGraphicsCaps.gles20.hasAlphaTestQCOM)
+ {
+ // we dont want to generate special shader if we have alpha-test done gl style
+ state.alphaTest = kFuncDisabled;
+ }
+ else
+ {
+ state.alphaTest = alphaTest;
+ switch (alphaTest)
+ {
+ case kFuncNever: /* \todo Disable drawing. */
+ case kFuncUnknown:
+ case kFuncDisabled:
+ case kFuncAlways:
+ state.alphaTest = kFuncDisabled;
+ default:
+ break;
+ }
+ }
+
+ state.setupPointSize = drawCallTopology == kPrimitivePoints;
+}
+
+inline void DeviceStateGLES20::ApplyTexGen( UInt32 unit )
+{
+ const TextureUnitStateGLES2& state = textures[unit];
+
+ positionTexGen = state.posForTexGen ? positionTexGen | (1<<unit)
+ : positionTexGen & ~(1<<unit);
+
+ normalTexGen = state.nrmForTexGen ? normalTexGen | (1<<unit)
+ : normalTexGen & ~(1<<unit);
+}
+
+inline void DeviceStateGLES20::DropTexGen( UInt32 unit )
+{
+ positionTexGen &= ~(1<<unit);
+ normalTexGen &= ~(1<<unit);
+}
+
+
+#include "GfxDeviceGLES20.h"
+#include "Runtime/GfxDevice/GLESCommon.h"
+
+void GfxDeviceGLES20_MarkWorldViewProjDirty()
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ GetGLES20DeviceState(device).transformState.dirtyFlags |= TransformState::kWorldViewProjDirty;
+}
+
+void GfxDeviceGLES20_DisableDepthTest()
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ GetGLES20DeviceState(device).depthFunc = GL_NEVER;
+ GLES_CHK(glDisable(GL_DEPTH_TEST));
+}
+
+void GfxDeviceGLES20_SetDrawCallTopology(GfxPrimitiveType topology)
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ GetGLES20DeviceState(device).drawCallTopology = topology;
+}
+
+
+#define GL_RT_COMMON_GLES2 1
+#include "Runtime/GfxDevice/GLRTCommon.h"
+#undef GL_RT_COMMON_GLES2
+
+void GraphicsCaps::InitGLES20()
+{
+ GLES_InitCommonCaps(this);
+ gGlesExtFunc.InitExtFunc();
+
+ shaderCaps = kShaderLevel3;
+
+ maxLights = kMaxSupportedVertexLightsByGLES20; // vertex light count
+ hasAnisoFilter = QueryExtension("GL_EXT_texture_filter_anisotropic"); // has anisotropic filtering?
+ if (hasAnisoFilter)
+ {
+ #if UNITY_PEPPER
+ // Google's implementation incorrectly reports a maxAnisoLevel of 1.
+ // Until they fix this, just set it here.
+ maxAnisoLevel = 16;
+ #else
+ GLES_CHK(glGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, (GLint *)&maxAnisoLevel ));
+ #endif
+ }
+ else
+ maxAnisoLevel = 1;
+
+
+ maxTexImageUnits = 8;
+ GLES_CHK(glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTexImageUnits));
+ maxTexImageUnits = std::max<int>(std::min<int>( maxTexImageUnits, kMaxSupportedTextureUnitsGLES ), 1);
+
+ maxTexUnits = maxTexImageUnits;
+ maxTexCoords = maxTexImageUnits;
+
+ GLES_CHK(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize));
+ GLES_CHK(glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &maxCubeMapSize));
+ GLES_CHK(glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxRenderTextureSize));
+
+ hasMipLevelBias = QueryExtension("GL_EXT_texture_lod_bias"); // can apply bias for mips?
+ hasMipMaxLevel = QueryExtension("GL_APPLE_texture_max_level");
+
+ gles20.hasAppleMSAA = QueryExtension("GL_APPLE_framebuffer_multisample");
+ gles20.hasImgMSAA = QueryExtension("GL_IMG_multisampled_render_to_texture") && gGlesExtFunc.glRenderbufferStorageMultisampleIMG && gGlesExtFunc.glFramebufferTexture2DMultisampleIMG;
+
+ hasMultiSampleAutoResolve = gles20.hasImgMSAA;
+ hasMultiSample = gles20.hasAppleMSAA || gles20.hasImgMSAA;
+
+
+ hasBlendSquare = true;
+ hasSeparateAlphaBlend = true;
+ hasBlendSub = true;
+ hasBlendMinMax = QueryExtension("GL_EXT_blend_minmax");
+
+ hasS3TCCompression = false;
+
+ hasAutoMipMapGeneration = true;
+
+ has3DTexture = false;
+
+ npot = kNPOTRestricted;
+ if( QueryExtension("GL_OES_texture_npot") || QueryExtension("GL_ARB_texture_non_power_of_two") )
+ npot = kNPOTFull;
+
+ npotRT = npot;
+
+ hasRenderToTexture = true; // We have render-to-texture functionality.
+ hasShadowCollectorPass = false;
+
+ hasHighPrecisionTextureCombiners = false;
+
+ hasRenderToCubemap = true;
+
+ supportsTextureFormat[kTexFormatBGRA32] = QueryExtension("GL_APPLE_texture_format_BGRA8888") || QueryExtension("GL_EXT_texture_format_BGRA8888");
+ supportsTextureFormat[kTexFormatAlphaLum16] = false;
+ supportsTextureFormat[kTexFormatARGB32] = false; // OpenGL ES has no support for INT_8_8_8_8 format, so don't bother with Unity reversed formats at all
+ supportsTextureFormat[kTexFormatBGR24] = false; // OpenGL ES has no support for INT_8_8_8_8 format, so don't bother with Unity reversed formats at all
+ supportsTextureFormat[kTexFormatDXT1] = QueryExtension("GL_EXT_texture_compression_s3tc") || QueryExtension("GL_EXT_texture_compression_dxt1");
+ supportsTextureFormat[kTexFormatDXT3] = QueryExtension("GL_EXT_texture_compression_s3tc") || QueryExtension("GL_CHROMIUM_texture_compression_dxt3");
+ supportsTextureFormat[kTexFormatDXT5] = QueryExtension("GL_EXT_texture_compression_s3tc") || QueryExtension("GL_CHROMIUM_texture_compression_dxt5");
+ supportsTextureFormat[kTexFormatPVRTC_RGB2] = QueryExtension("GL_IMG_texture_compression_pvrtc");
+ supportsTextureFormat[kTexFormatPVRTC_RGBA2] = QueryExtension("GL_IMG_texture_compression_pvrtc");
+ supportsTextureFormat[kTexFormatPVRTC_RGB4] = QueryExtension("GL_IMG_texture_compression_pvrtc");
+ supportsTextureFormat[kTexFormatPVRTC_RGBA4] = QueryExtension("GL_IMG_texture_compression_pvrtc");
+ supportsTextureFormat[kTexFormatETC_RGB4] = QueryExtension("GL_OES_compressed_ETC1_RGB8_texture");
+ supportsTextureFormat[kTexFormatATC_RGB4] = QueryExtension("GL_AMD_compressed_ATC_texture") || QueryExtension("GL_ATI_texture_compression_atitc");
+ supportsTextureFormat[kTexFormatATC_RGBA8] = QueryExtension("GL_AMD_compressed_ATC_texture") || QueryExtension("GL_ATI_texture_compression_atitc");
+
+ supportsRenderTextureFormat[kRTFormatARGB32] = true;
+
+ {
+ const bool supportsHalfRB = QueryExtension("GL_EXT_color_buffer_half_float");
+ // if we dont have OES_texture_half_float we cant have half texture (only RB)
+ const bool supportsHalfRT = supportsHalfRB && QueryExtension("GL_OES_texture_half_float");
+ const bool supportsRG = QueryExtension("GL_EXT_texture_rg");
+
+ supportsRenderTextureFormat[kRTFormatARGBHalf] = supportsHalfRT;
+ supportsRenderTextureFormat[kRTFormatR8] = supportsRG;
+ supportsRenderTextureFormat[kRTFormatRHalf] = supportsHalfRT && supportsRG;
+ supportsRenderTextureFormat[kRTFormatRGHalf] = supportsHalfRT && supportsRG;
+ }
+
+ {
+ FBColorFormatCheckerGLES2 checker;
+
+ supportsRenderTextureFormat[kRTFormatRGB565] = checker.CheckFormatSupported(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5);
+ supportsRenderTextureFormat[kRTFormatARGB4444] = checker.CheckFormatSupported(GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4);
+ supportsRenderTextureFormat[kRTFormatARGB1555] = checker.CheckFormatSupported(GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1);
+
+ // for float formats there is no ext to check rif they are renderable, so do manually
+ if(QueryExtension("OES_texture_float"))
+ {
+ supportsRenderTextureFormat[kRTFormatARGBHalf] = checker.CheckFormatSupported(GL_RGBA, GL_RGBA, GL_FLOAT);
+ if(QueryExtension("GL_EXT_texture_rg"))
+ {
+ // TODO: move all this unity gles ext header
+ supportsRenderTextureFormat[kRTFormatRFloat] = checker.CheckFormatSupported(0x1903, 0x1903, GL_FLOAT);
+ supportsRenderTextureFormat[kRTFormatRGFloat] = checker.CheckFormatSupported(0x8227, 0x8227, GL_FLOAT);
+ }
+ }
+ }
+
+ const bool isPvrGpu = (rendererString.find("PowerVR") != string::npos);
+ const bool isMaliGpu = (rendererString.find("Mali") != string::npos);
+ const bool isAdrenoGpu = (rendererString.find("Adreno") != string::npos);
+ const bool isTegraGpu = (rendererString.find("Tegra") != string::npos);
+
+ // Only support stencil when we have packed depth stencil, for simplicity
+ hasStencil = hasTwoSidedStencil = hasRenderTargetStencil = QueryExtension("GL_OES_packed_depth_stencil");
+
+#if UNITY_BB10
+ if(isAdrenoGpu)
+ hasRenderTargetStencil = false;
+#endif
+
+ // Adreno 2xx seems to not like a texture attached to color & depth & stencil at once;
+ // Adreno 3xx seems to be fine. Most 3xx devices have GL_OES_depth_texture_cube_map extension
+ // present, and 2xx do not. So detect based on that.
+ const bool isAdreno2xx = isAdrenoGpu && !QueryExtension("GL_OES_depth_texture_cube_map");
+ if (isAdreno2xx)
+ hasRenderTargetStencil = false;
+
+ hasNativeDepthTexture = QueryExtension("GL_OES_depth_texture") || QueryExtension ("GL_GOOGLE_depth_texture");
+#if UNITY_ANDROID
+ if(android::systeminfo::ApiLevel() < android::apiHoneycomb || (android::systeminfo::ApiLevel() < android::apiIceCreamSandwich && isPvrGpu))
+ hasNativeDepthTexture = false;
+#endif
+
+ hasStencilInDepthTexture = hasRenderTargetStencil && hasNativeDepthTexture;
+ gles20.has24DepthForFBO = QueryExtension("GL_OES_depth24");
+
+ hasNativeShadowMap = QueryExtension("GL_EXT_shadow_samplers");
+
+ supportsRenderTextureFormat[kRTFormatA2R10G10B10] = false;
+ supportsRenderTextureFormat[kRTFormatARGB64] = false;
+ supportsRenderTextureFormat[kRTFormatDepth] = hasNativeDepthTexture;
+ supportsRenderTextureFormat[kRTFormatShadowMap] = hasNativeShadowMap;
+
+
+ has16BitFloatVertex = QueryExtension("GL_OES_vertex_half_float");
+ needsToSwizzleVertexColors = false;
+
+ // ---- driver bug/workaround flags
+
+#if UNITY_LINUX
+ if(isTegraGpu)
+ {
+ supportsRenderTextureFormat[kRTFormatDepth] = hasNativeDepthTexture = true;
+ hasRenderTargetStencil = true;
+ hasStencilInDepthTexture = true;
+ hasTwoSidedStencil = true;
+ }
+#endif
+
+ hasSRGBReadWrite = QueryExtension("GL_EXT_sRGB");
+ // TODO: we should check srgb+compressed on gpu-case basis, but for now it is tegra only anyway ;-)
+ hasSRGBReadWrite = hasSRGBReadWrite && QueryExtension("GL_NV_sRGB_formats");
+
+ disableSoftShadows = true;
+
+ hasShadowCollectorPass = false;
+
+ // ---- gles20 specifics
+
+ gles20.hasGLSL = true;
+ gles20.maxAttributes = 16;
+ gles20.hasNLZ = QueryExtension("GL_NV_depth_nonlinear");
+ gles20.hasAlphaTestQCOM = QueryExtension("GL_QCOM_alpha_test") && gGlesExtFunc.glAlphaFuncQCOM;
+
+ gles20.hasMapbuffer = QueryExtension("GL_OES_mapbuffer") && gGlesExtFunc.glMapBufferOES && gGlesExtFunc.glUnmapBufferOES;
+ gles20.hasMapbufferRange= QueryExtension("GL_EXT_map_buffer_range") && gGlesExtFunc.glMapBufferRangeEXT && gGlesExtFunc.glUnmapBufferOES;
+
+#if UNITY_PEPPER
+ // it was cut out in ifdef before, so lets play safe and assume we dont have map
+ gles20.hasMapbuffer = gles20.hasMapbufferRange = false;
+#endif
+
+ gles20.hasBinaryShaders = (UNITY_ANDROID || UNITY_BLACKBERRY || UNITY_TIZEN) && QueryExtension("GL_OES_get_program_binary") && GlslGpuProgramGLES20::InitBinaryShadersSupport();
+
+ gles20.hasNVMRT = QueryExtension("GL_NV_draw_buffers") && QueryExtension("GL_NV_fbo_color_attachments") && gGlesExtFunc.glDrawBuffersNV;
+ if(gles20.hasNVMRT)
+ {
+ GLES_CHK(glGetIntegerv(GL_MAX_DRAW_BUFFERS_NV, &maxMRTs));
+ maxMRTs = clamp<int> (maxMRTs, 1, kMaxSupportedRenderTargets);
+ }
+
+#if UNITY_IPHONE
+ if( iphone::GetDeviceGeneration() == iphone::kiPhoneGenerationiPad2Gen && iphone::isIOSVersionOrNewerButBefore("4.3", "5.0") )
+ gles20.buggyColorMaskBlendMSAA = true;
+#endif
+
+#if UNITY_ANDROID || UNITY_BB10
+ // Adreno (Qualcomm chipsets) have a driver bug (Android2.2)
+ // which fails to patch vfetch instructions under certain circumstances
+ gles20.buggyVFetchPatching = isAdrenoGpu;
+#endif
+
+ // Mali-400 MP? They promised to fix drivers (they know about the issue)
+ gles20.buggyDisableVAttrKeepsActive = isMaliGpu;
+
+#if UNITY_ANDROID
+ // samsung galaxy on mali + ics update ruined shader compiler
+ // most osam part: on some shaders tex sample in vprog result in crash close to kernel (no callstack in logcat)
+ gles20.buggyVprogTextures = isMaliGpu && (android::systeminfo::ApiLevel() >= android::apiIceCreamSandwich);
+#endif
+
+ // on android pvr if we enable dynamic geom drawing from memory sometimes we hit crash in the driver
+ // it seems that some internal caching goes crazy - solved by actually drawing watermark
+ // This is also needed on BB10 devices (both pvr and adreno) to fix video display issues
+ gles20.needToRenderWatermarkDueToDrawFromMemoryBuggy = (UNITY_ANDROID && isPvrGpu) || UNITY_BB10;
+
+ // PowerVR GPUs have slow discard
+ gles20.slowAlphaTest = isPvrGpu;
+
+ // on adreno devices drivers don't like if+discard combo
+ // so set slowAlphaTest here too, to remove trivial clip
+ gles20.slowAlphaTest |= isAdrenoGpu;
+
+ // orphaning path is terribly slower universally
+ // apart from some corner cases on ios where it is not faster anyway
+ gles20.hasVBOOrphaning = false;
+
+ gles20.hasDebugMarkers = QueryExtension("GL_EXT_debug_marker") && gGlesExtFunc.glPushGroupMarkerEXT && gGlesExtFunc.glPopGroupMarkerEXT;
+ gles20.hasDiscardFramebuffer = QueryExtension("GL_EXT_discard_framebuffer") && gGlesExtFunc.glDiscardFramebufferEXT;
+ gles20.hasHalfLinearFilter = QueryExtension("GL_OES_texture_half_float_linear");
+
+
+ gles20.slowDynamicVBO = isPvrGpu || isAdrenoGpu || isMaliGpu;
+ gles20.forceStaticBatchFromMem = isMaliGpu;
+#if UNITY_IPHONE
+ gles20.forceStaticBatchFromMem = true;
+#endif
+ gles20.needFlushAfterTextureUpload = isAdrenoGpu;
+
+ gles20.needFlushAfterTextureUpload = isAdrenoGpu;
+
+ hasTiledGPU = isPvrGpu || isAdrenoGpu || isMaliGpu;
+
+ // on Tegra (for now all drivers) there is a bug that sometimes results in samplers not being reported
+ // the only known workaround is to force highp default precision
+ gles20.forceHighpFSPrec = isTegraGpu;
+
+ // on Adreno (Nexus4 at least) there is a bug that sometimes results in program link crash
+ // the only known workaround is to force highp default precision
+ gles20.forceHighpFSPrec = isAdrenoGpu;
+
+
+ GLES_CHK(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &gles20.maxAttributes));
+ GLES_CHK(glGetIntegerv(GL_MAX_VARYING_VECTORS, &gles20.maxVaryings));
+
+ if(gles20.hasAppleMSAA)
+ GLES_CHK(glGetIntegerv(GL_MAX_SAMPLES_APPLE, &gles20.maxSamples));
+ if(gles20.hasImgMSAA)
+ GLES_CHK(glGetIntegerv(GL_MAX_SAMPLES_IMG, &gles20.maxSamples));
+
+#if ENABLE_PROFILER
+ g_TimerQueriesGLES.Init();
+#endif
+#if NV_STATE_FILTERING
+ filteredInitGLES20();
+#endif
+}
+
+//chai: extern º¯Êý
+GfxDevice* CreateGLES20GfxDevice()
+{
+#if UNITY_WIN || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN || UNITY_ANDROID
+ InitializeGLES20();
+#endif
+ gGraphicsCaps.InitGLES20();
+#if UNITY_EDITOR
+ return NULL;
+#else
+ return UNITY_NEW_AS_ROOT(GFX_GL_IMPL(), kMemGfxDevice, "GLES20GfxDevice","");
+#endif
+}
+
+GFX_GL_IMPL::GFX_GL_IMPL()
+{
+ ;;printf_console("Creating OpenGLES2.0 graphics device\n");
+ #if !GFX_DEVICE_VIRTUAL
+ impl = new GfxDeviceImpl();
+ #endif
+
+ // ??? ReJ: (Q for Tomas) WHY InvalidateState was commented out here? InvalidateState is necessary to enable correct back-face culling
+ OnCreate();
+ InvalidateState();
+#if UNITY_PEPPER || UNITY_WEBGL
+ m_Renderer = kGfxRendererOpenGLES20Desktop;
+#else
+ m_Renderer = kGfxRendererOpenGLES20Mobile;
+#endif
+ m_IsThreadable = true;
+
+ m_UsesOpenGLTextureCoords = true;
+ m_UsesHalfTexelOffset = false;
+
+ STATE.m_CurrBlendState = NULL;
+ STATE.m_CurrDepthState = NULL;
+ STATE.m_CurrStencilState = NULL;
+ STATE.m_CurrRasterState = NULL;
+ STATE.m_SharedFBO = STATE.m_HelperFBO = 0;
+
+ InitBackBufferGLES2(&m_BackBufferColor.object, &m_BackBufferDepth.object);
+}
+
+GFX_GL_IMPL::~GFX_GL_IMPL()
+{
+ delete STATE.m_DynamicVBO;
+
+ #if !GFX_DEVICE_VIRTUAL
+ delete impl;
+ #endif
+#if UNITY_WIN || UNITY_ANDROID
+ ShutdownGLES20();
+#endif
+}
+
+
+static void ActivateTextureUnitGLES2 (DeviceStateGLES20& state, int unit)
+{
+ if (state.activeTextureUnit == unit)
+ return;
+ GLES_CHK(glActiveTexture(GL_TEXTURE0 + unit));
+ state.activeTextureUnit = unit;
+}
+
+
+void GFX_GL_IMPL::InvalidateState()
+{
+ DBG_LOG_GLES20("InvalidateState");
+ m_FogParams.Invalidate();
+ STATE.transformState.Invalidate(m_BuiltinParamValues);
+ STATE.Invalidate();
+
+#if NV_STATE_FILTERING
+ StateFiltering_InvalidateVBOCacheGLES20();
+#endif
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatEmission, Vector4f(1,1,1,1));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatAmbient, Vector4f(1,1,1,1));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatDiffuse, Vector4f(1,1,1,1));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatSpecular, Vector4f(1,1,1,1));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatShininess, Vector4f(1,1,1,1));
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFFogColor, Vector4f(0,0,0,0));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFFogParams, Vector4f(0,0,0,0));
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFAlphaTestRef, Vector4f(0,0,0,0));
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFColor, Vector4f(1,1,1,1));
+ for (int i = 0; i < kMaxSupportedTextureUnitsGLES; i++)
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecFFTextureEnvColor0+i), Vector4f(0,0,0,0));
+}
+
+
+
+DeviceBlendState* GFX_GL_IMPL::CreateBlendState(const GfxBlendState& state)
+{
+ std::pair<CachedBlendStates::iterator, bool> result = STATE.m_CachedBlendStates.insert(std::make_pair(state, DeviceBlendState()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceBlendState& glstate = result.first->second;
+ memcpy(&glstate.sourceState, &state, sizeof(glstate.sourceState));
+ DebugAssertIf(kFuncUnknown==state.alphaTest);
+
+ return &result.first->second;
+}
+
+
+DeviceDepthState* GFX_GL_IMPL::CreateDepthState(const GfxDepthState& state)
+{
+ std::pair<CachedDepthStates::iterator, bool> result = STATE.m_CachedDepthStates.insert(std::make_pair(state, DeviceDepthStateGLES20()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceDepthStateGLES20& glstate = result.first->second;
+ memcpy(&glstate.sourceState, &state, sizeof(glstate.sourceState));
+ glstate.depthFunc = kCmpFuncES2[state.depthFunc];
+ return &result.first->second;
+}
+
+DeviceStencilState* GFX_GL_IMPL::CreateStencilState(const GfxStencilState& state)
+{
+ std::pair<CachedStencilStates::iterator, bool> result = STATE.m_CachedStencilStates.insert(std::make_pair(state, DeviceStencilStateGLES20()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceStencilStateGLES20& st = result.first->second;
+ memcpy (&st.sourceState, &state, sizeof(st.sourceState));
+ st.stencilFuncFront = kCmpFuncES2[state.stencilFuncFront];
+ st.stencilFailOpFront = kStencilOpES2[state.stencilFailOpFront];
+ st.depthFailOpFront = kStencilOpES2[state.stencilZFailOpFront];
+ st.depthPassOpFront = kStencilOpES2[state.stencilPassOpFront];
+ st.stencilFuncBack = kCmpFuncES2[state.stencilFuncBack];
+ st.stencilFailOpBack = kStencilOpES2[state.stencilFailOpBack];
+ st.depthFailOpBack = kStencilOpES2[state.stencilZFailOpBack];
+ st.depthPassOpBack = kStencilOpES2[state.stencilPassOpBack];
+ return &result.first->second;
+}
+
+DeviceRasterState* GFX_GL_IMPL::CreateRasterState(const GfxRasterState& state)
+{
+ std::pair<CachedRasterStates::iterator, bool> result = STATE.m_CachedRasterStates.insert(std::make_pair(state, DeviceRasterState()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceRasterState& glstate = result.first->second;
+ memcpy(&glstate.sourceState, &state, sizeof(glstate.sourceState));
+
+ return &result.first->second;
+}
+
+void GFX_GL_IMPL::SetBlendState(const DeviceBlendState* state, float alphaRef)
+{
+ DeviceBlendState* devstate = (DeviceBlendState*)state;
+
+ if (STATE.m_CurrBlendState == devstate && alphaRef == STATE.alphaValue)
+ return;
+
+ STATE.m_CurrBlendState = devstate;
+ if (!STATE.m_CurrBlendState)
+ return;
+
+ const GfxBlendState& desc = state->sourceState;
+ const GLenum glsrc = kBlendModeES2[desc.srcBlend];
+ const GLenum gldst = kBlendModeES2[desc.dstBlend];
+ const GLenum glsrca = kBlendModeES2[desc.srcBlendAlpha];
+ const GLenum gldsta = kBlendModeES2[desc.dstBlendAlpha];
+ const GLenum glfunc = kBlendFuncES2[desc.blendOp];
+ const GLenum glfunca = kBlendFuncES2[desc.blendOpAlpha];
+ const bool blendDisabled = (glsrc == GL_ONE && gldst == GL_ZERO && glsrca == GL_ONE && gldsta == GL_ZERO);
+
+ int mask = devstate->sourceState.renderTargetWriteMask;
+
+ if (!IsActiveRenderTargetWithColorGLES2())
+ mask = 0;
+
+#if UNITY_IPHONE
+ if( (mask && mask != 15) && gGraphicsCaps.gles20.buggyColorMaskBlendMSAA && !blendDisabled && IsActiveMSAARenderTargetGLES2() )
+ mask = 15;
+#endif
+
+ if( mask != STATE.colorWriteMask )
+ {
+ GLES_CHK(glColorMask( (mask & kColorWriteR) != 0, (mask & kColorWriteG) != 0, (mask & kColorWriteB) != 0, (mask & kColorWriteA) != 0 ));
+ STATE.colorWriteMask = mask;
+ }
+
+ if( blendDisabled )
+ {
+ if( STATE.blending != 0 )
+ {
+ GLES_CHK(glDisable (GL_BLEND));
+ STATE.blending = 0;
+ }
+ }
+ else
+ {
+ if( glsrc != STATE.srcBlend || gldst != STATE.destBlend || glsrca != STATE.srcBlendAlpha || gldsta != STATE.destBlendAlpha )
+ {
+ GLES_CHK(glBlendFuncSeparate(glsrc, gldst, glsrca, gldsta));
+ STATE.srcBlend = glsrc;
+ STATE.destBlend = gldst;
+ STATE.srcBlendAlpha = glsrca;
+ STATE.destBlendAlpha = gldsta;
+ }
+ if (glfunc != STATE.blendOp || glfunca != STATE.blendOpAlpha)
+ {
+ bool supports = true;
+ if( (glfunc == GL_MIN_EXT || glfunc == GL_MAX_EXT) && !gGraphicsCaps.hasBlendMinMax )
+ supports = false;
+ if( (glfunca == GL_MIN_EXT || glfunca == GL_MAX_EXT) && !gGraphicsCaps.hasBlendMinMax )
+ supports = false;
+
+ if(supports)
+ {
+ GLES_CHK(glBlendEquationSeparate(glfunc, glfunca));
+ STATE.blendOp = glfunc;
+ STATE.blendOpAlpha = glfunca;
+ }
+ }
+ if( STATE.blending != 1 )
+ {
+ GLES_CHK(glEnable( GL_BLEND ));
+ STATE.blending = 1;
+ }
+ }
+ // fragment shader is expected to implement per fragment culling
+ CompareFunction alphaTest = devstate->sourceState.alphaTest;
+
+ if (gGraphicsCaps.gles20.slowAlphaTest)
+ {
+ // Alpha testing is slow on some GPUs
+ // skip trivial cases such as (alpha > 0)
+ if (alphaTest == kFuncGreater && alphaRef <= 0.01)
+ alphaTest = kFuncDisabled;
+ }
+
+ if( gGraphicsCaps.gles20.hasAlphaTestQCOM && (alphaTest != STATE.alphaTest || alphaRef != STATE.alphaValue) )
+ {
+ if( alphaTest != kFuncDisabled )
+ {
+ GLES_CHK(gGlesExtFunc.glAlphaFuncQCOM(kCmpFuncES2[alphaTest], alphaRef));
+ GLES_CHK(glEnable(GL_ALPHA_TEST_QCOM));
+ }
+ else
+ {
+ GLES_CHK(glDisable(GL_ALPHA_TEST_QCOM));
+ }
+ }
+
+ STATE.alphaTest = alphaTest;
+ STATE.alphaValue = alphaRef;
+}
+
+
+void GFX_GL_IMPL::SetRasterState(const DeviceRasterState* state)
+{
+ DeviceRasterState* devstate = (DeviceRasterState*)state;
+ if(!devstate)
+ {
+ STATE.m_CurrRasterState = NULL;
+ return;
+ }
+
+ STATE.m_CurrRasterState = devstate;
+
+ CullMode cull = devstate->sourceState.cullMode;
+ if( cull != STATE.culling )
+ {
+ switch (cull) {
+ case kCullOff:
+ GLES_CHK(glDisable (GL_CULL_FACE));
+ break;
+ case kCullFront:
+ GLES_CHK(glCullFace (GL_FRONT));
+ GLES_CHK(glEnable (GL_CULL_FACE));
+ break;
+ case kCullBack:
+ GLES_CHK(glCullFace (GL_BACK));
+ GLES_CHK(glEnable (GL_CULL_FACE));
+ break;
+ default:
+ break;
+ }
+ STATE.culling = cull;
+ }
+
+ float zFactor = devstate->sourceState.slopeScaledDepthBias;
+ float zUnits = devstate->sourceState.depthBias;
+ if( zFactor != STATE.offsetFactor || zUnits != STATE.offsetUnits )
+ {
+
+#if WORKAROUND_POLYGON_OFFSET
+ // on some androids polygon offset work the other way (positive push to viewer)
+ // so we tweak projection matrix to do offset
+ // also use available depth precision better (on Adreno for example fix bugs with z-fighting)
+ // Game Programming Gems Vol1
+ // Eric Lengyel's "Tweaking a Vertex's Projected Depth Value"
+ // we use simplified formula: just smallest possible eps directly (multiplied by zUnits)
+ // we calculate eps for 16bit depth (good enough for larger depth)
+ // in projection matrix [2,2] = (f+n)/(n-f)
+ // so eps would be BitsMult * -1/proj[2,2]
+
+ static const float _BitsMult = -1.0f / (float)0xFFFF; // FFFF = 2^16-1, minus sign incorporated here
+
+ float* matrixElem = &m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatProj).Get(2,2);
+
+ const float eps = _BitsMult / *matrixElem;
+ *matrixElem *= (1.0f + zUnits*eps);
+#else
+ GLES_CHK(glPolygonOffset( zFactor, zUnits ));
+ if( zFactor || zUnits )
+ GLES_CHK(glEnable (GL_POLYGON_OFFSET_FILL));
+ else
+ GLES_CHK(glDisable (GL_POLYGON_OFFSET_FILL));
+#endif
+ STATE.offsetFactor = zFactor;
+ STATE.offsetUnits = zUnits;
+ }
+}
+
+
+void GFX_GL_IMPL::SetDepthState(const DeviceDepthState* state)
+{
+ DeviceDepthStateGLES20* devstate = (DeviceDepthStateGLES20*)state;
+ if (STATE.m_CurrDepthState == devstate)
+ return;
+
+ STATE.m_CurrDepthState = devstate;
+
+ if (!STATE.m_CurrDepthState)
+ return;
+
+ const int depthFunc = devstate->depthFunc;
+ if( depthFunc != STATE.depthFunc )
+ {
+ if( depthFunc != GL_NEVER ) {
+ GLES_CHK(glDepthFunc (depthFunc));
+ GLES_CHK(glEnable (GL_DEPTH_TEST));
+ } else {
+ GLES_CHK(glDisable (GL_DEPTH_TEST));
+ }
+
+ STATE.depthFunc = depthFunc;
+ }
+
+ const int writeMode = devstate->sourceState.depthWrite ? GL_TRUE : GL_FALSE;
+ if( writeMode != STATE.depthWrite )
+ {
+ GLES_CHK(glDepthMask (writeMode));
+ STATE.depthWrite = writeMode;
+ }
+}
+
+void GFX_GL_IMPL::SetStencilState (const DeviceStencilState* state, int stencilRef)
+{
+ if (STATE.m_CurrStencilState == state && STATE.m_StencilRef == stencilRef)
+ return;
+ const DeviceStencilStateGLES20* st = static_cast<const DeviceStencilStateGLES20*>(state);
+ STATE.m_CurrStencilState = st;
+ if (!st)
+ return;
+
+ if (st->sourceState.stencilEnable)
+ GLES_CHK(glEnable (GL_STENCIL_TEST));
+ else
+ GLES_CHK(glDisable (GL_STENCIL_TEST));
+ if (gGraphicsCaps.hasTwoSidedStencil)
+ {
+ GLES_CHK(glStencilFuncSeparate (GL_FRONT, st->stencilFuncFront, stencilRef, st->sourceState.readMask));
+ GLES_CHK(glStencilOpSeparate (GL_FRONT, st->stencilFailOpFront, st->depthFailOpFront, st->depthPassOpFront));
+ GLES_CHK(glStencilFuncSeparate (GL_BACK, st->stencilFuncBack, stencilRef, st->sourceState.readMask));
+ GLES_CHK(glStencilOpSeparate (GL_BACK, st->stencilFailOpBack, st->depthFailOpBack, st->depthPassOpBack));
+ }
+ else
+ {
+ GLES_CHK(glStencilFunc (st->stencilFuncFront, stencilRef, st->sourceState.readMask));
+ GLES_CHK(glStencilOp (st->stencilFailOpFront, st->depthFailOpFront, st->depthPassOpFront));
+ }
+ GLES_CHK(glStencilMask (st->sourceState.writeMask));
+
+ STATE.m_StencilRef = stencilRef;
+}
+
+void GFX_GL_IMPL::SetSRGBWrite (bool enable)
+{
+}
+
+bool GFX_GL_IMPL::GetSRGBWrite ()
+{
+ return false;
+}
+
+void GFX_GL_IMPL::Clear (UInt32 clearFlags, const float color[4], float depth, int stencil)
+{
+ DBG_LOG_GLES20("Clear(%d, (%.2f, %.2f, %.2f, %.2f), %.2f, %d", clearFlags, color[0], color[1], color[2], color[3], depth, stencil);
+
+ EnsureDefaultFBInitedGLES2();
+
+ if (!IsActiveRenderTargetWithColorGLES2())
+ clearFlags &= ~kGfxClearColor;
+
+ float clearColorAlpha = color[3];
+
+ // In OpenGL, clears are affected by color write mask and depth writing parameters.
+ // So make sure to set them!
+ GLbitfield flags = 0;
+ if (clearFlags & kGfxClearColor)
+ {
+ if (STATE.colorWriteMask != 15)
+ {
+ GLES_CHK(glColorMask( true, true, true, true ));
+ STATE.colorWriteMask = 15;
+ STATE.m_CurrBlendState = NULL;
+ }
+ flags |= GL_COLOR_BUFFER_BIT;
+ GLES_CHK(glClearColor( color[0], color[1], color[2], clearColorAlpha ));
+ }
+ if (clearFlags & kGfxClearDepth)
+ {
+ GLES_CHK(glDepthMask (GL_TRUE));
+ STATE.depthWrite = GL_TRUE;
+ STATE.m_CurrDepthState = NULL;
+ flags |= GL_DEPTH_BUFFER_BIT;
+ GLES_CHK(glClearDepthf( depth ));
+ }
+ if (clearFlags & kGfxClearStencil)
+ {
+ //@TODO: need to set stencil writes on?
+ flags |= GL_STENCIL_BUFFER_BIT;
+ GLES_CHK(glClearStencil (stencil));
+ }
+ GLES_CHK(glClear(flags));
+}
+
+static void ApplyBackfaceMode( const DeviceStateGLES20& state )
+{
+ DBG_LOG_GLES20("ApplyBackfaceMode");
+ if (state.appBackfaceMode != state.userBackfaceMode)
+ GLES_CHK(glFrontFace( GL_CCW ));
+ else
+ GLES_CHK(glFrontFace( GL_CW ));
+}
+
+
+void GFX_GL_IMPL::SetUserBackfaceMode( bool enable )
+{
+ DBG_LOG_GLES20("SetUserBackfaceMode(%s)", GetBoolString(enable));
+ if( STATE.userBackfaceMode == enable )
+ return;
+
+ STATE.userBackfaceMode = enable;
+ ApplyBackfaceMode( STATE );
+}
+
+void GFX_GL_IMPL::SetInvertProjectionMatrix( bool enable )
+{
+ Assert (!enable); // projection should never be flipped upside down on OpenGL
+}
+
+bool GFX_GL_IMPL::GetInvertProjectionMatrix() const
+{
+ return false;
+}
+
+void GFX_GL_IMPL::SetProjectionMatrix (const Matrix4x4f& matrix)
+{
+ DBG_LOG_GLES20("SetProjectionMatrix(...)");
+
+ CopyMatrix(matrix.GetPtr(), STATE.transformState.projectionMatrixOriginal.GetPtr());
+ CopyMatrix (matrix.GetPtr(), m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatProj).GetPtr());
+ STATE.transformState.dirtyFlags |= TransformState::kProjDirty;
+}
+
+
+void GFX_GL_IMPL::SetWorldMatrix( const float matrix[16] )
+{
+ CopyMatrix( matrix, STATE.transformState.worldMatrix.GetPtr() );
+ STATE.transformState.dirtyFlags |= TransformState::kWorldDirty;
+}
+
+void GFX_GL_IMPL::SetViewMatrix( const float matrix[16] )
+{
+ STATE.transformState.SetViewMatrix (matrix, m_BuiltinParamValues);
+}
+
+
+void GFX_GL_IMPL::GetMatrix( float outMatrix[16] ) const
+{
+ STATE.transformState.UpdateWorldViewMatrix (m_BuiltinParamValues);
+ CopyMatrix (STATE.transformState.worldViewMatrix.GetPtr(), outMatrix);
+}
+
+const float* GFX_GL_IMPL::GetWorldMatrix() const
+{
+ return STATE.transformState.worldMatrix.GetPtr();
+}
+
+const float* GFX_GL_IMPL::GetViewMatrix() const
+{
+ return m_BuiltinParamValues.GetMatrixParam(kShaderMatView).GetPtr();
+}
+
+const float* GFX_GL_IMPL::GetProjectionMatrix() const
+{
+ return STATE.transformState.projectionMatrixOriginal.GetPtr();
+}
+
+const float* GFX_GL_IMPL::GetDeviceProjectionMatrix() const
+{
+ return GetProjectionMatrix();
+}
+
+void GFX_GL_IMPL::SetNormalizationBackface( NormalizationMode mode, bool backface )
+{
+ DBG_LOG_GLES20("SetNormalizationBackface(%d %s)", mode, backface?"back":"front");
+ STATE.normalization = mode;
+ if( STATE.appBackfaceMode != backface )
+ {
+ STATE.appBackfaceMode = backface;
+ ApplyBackfaceMode( STATE );
+ }
+}
+
+void GFX_GL_IMPL::SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial )
+{
+ DBG_LOG_GLES20("SetFFLighting(%s, %s, %d)", on?"True":"False", separateSpecular?"True":"False", colorMaterial);
+ STATE.lighting = on;
+ STATE.separateSpecular = separateSpecular;
+ STATE.colorMaterial = colorMaterial;
+}
+
+void GFX_GL_IMPL::SetMaterial( const float ambient[4], const float diffuse[4], const float specular[4], const float emissive[4], const float shininess )
+{
+ DBG_LOG_GLES20("SetMaterial()");
+ STATE.matAmbient.set (ambient[0], ambient[1], ambient[2], 1.0F);
+ STATE.matDiffuse.set (diffuse);
+ STATE.matSpecular.set (specular[0], specular[1], specular[2], 1.0F);
+ STATE.matEmissive.set (emissive[0], emissive[1], emissive[2], 1.0F);
+ float glshine = std::max<float>(std::min<float>(shininess,1.0f), 0.0f) * 128.0f;
+ STATE.matShininess = glshine;
+}
+
+
+void GFX_GL_IMPL::SetColor( const float color[4] )
+{
+ DBG_LOG_GLES20("SetColor()");
+ STATE.color.set( color );
+ // Emulate OpenGL's behaviour
+ ImmediateColor( color[0], color[1], color[2], color[3] );
+}
+
+void GFX_GL_IMPL::SetViewport( int x, int y, int width, int height )
+{
+ DBG_LOG_GLES20("SetViewport(%d, %d, %d, %d)", x, y, width, height);
+ STATE.viewport[0] = x;
+ STATE.viewport[1] = y;
+ STATE.viewport[2] = width;
+ STATE.viewport[3] = height;
+ GLES_CHK(glViewport( x, y, width, height ));
+}
+
+void GFX_GL_IMPL::GetViewport( int* port ) const
+{
+ DBG_LOG_GLES20("GetViewport()");
+ port[0] = STATE.viewport[0];
+ port[1] = STATE.viewport[1];
+ port[2] = STATE.viewport[2];
+ port[3] = STATE.viewport[3];
+}
+
+
+
+void GFX_GL_IMPL::SetScissorRect( int x, int y, int width, int height )
+{
+ DBG_LOG_GLES20("SetScissorRect(%d, %d, %d, %d)", x, y, width, height);
+ if (STATE.scissor != 1)
+ {
+ GLES_CHK(glEnable( GL_SCISSOR_TEST ));
+ STATE.scissor = 1;
+ }
+
+
+ STATE.scissorRect[0] = x;
+ STATE.scissorRect[1] = y;
+ STATE.scissorRect[2] = width;
+ STATE.scissorRect[3] = height;
+ GLES_CHK(glScissor( x, y, width, height ));
+
+}
+
+bool GFX_GL_IMPL::IsCombineModeSupported( unsigned int combiner )
+{
+ return true;
+}
+
+void GFX_GL_IMPL::DisableScissor()
+{
+ DBG_LOG_GLES20("DisableScissor()");
+ if (STATE.scissor != 0)
+ {
+ GLES_CHK(glDisable( GL_SCISSOR_TEST ));
+ STATE.scissor = 0;
+ }
+}
+
+bool GFX_GL_IMPL::IsScissorEnabled() const
+{
+ DBG_LOG_GLES20("IsScissorEnabled():returns %s", STATE.scissor == 1?"True":"False");
+ return STATE.scissor == 1;
+}
+
+void GFX_GL_IMPL::GetScissorRect( int scissor[4] ) const
+{
+ DBG_LOG_GLES20("GetScissorRect()");
+ scissor[0] = STATE.scissorRect[0];
+ scissor[1] = STATE.scissorRect[1];
+ scissor[2] = STATE.scissorRect[2];
+ scissor[3] = STATE.scissorRect[3];
+}
+
+TextureCombinersHandle GFX_GL_IMPL::CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular )
+{
+ DBG_LOG_GLES20("CreateTextureCombiners()");
+ TextureCombinersGLES2* implGLES = TextureCombinersGLES2::Create (count, texEnvs);
+ return TextureCombinersHandle (implGLES);
+}
+
+
+void GFX_GL_IMPL::SetTextureCombinersThreadable( TextureCombinersHandle textureCombiners, const TexEnvData* texEnvData, const Vector4f* texColors )
+{
+ DBG_LOG_GLES20("SetTextureCombiners()");
+ TextureCombinersGLES2* implGLES = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersGLES2);
+ Assert (implGLES);
+
+ const int count = std::min(gGraphicsCaps.maxTexUnits, implGLES->count);
+ STATE.textureCount = count;
+ for (int i = 0; i < count; ++i)
+ {
+ const ShaderLab::TextureBinding& binding = implGLES->texEnvs[i];
+
+ // set the texture
+ ApplyTexEnvData (i, i, texEnvData[i]);
+
+ // setup texture unit state
+ TextureUnitStateGLES2& texUnitState = STATE.textures[i];
+ texUnitState.color = texColors[i];
+ texUnitState.combColor = binding.m_CombColor;
+ texUnitState.combAlpha = binding.m_CombAlpha;
+ }
+ // we can just create mask and "and" with it
+ // but consider me lazy
+ for( int i = count ; i < gGraphicsCaps.maxTexUnits ; ++i)
+ STATE.DropTexGen(i);
+
+ STATE.activeProgram = 0;
+
+ // Get us back to TU 0, so we know where we stand
+ ActivateTextureUnitGLES2 (STATE, 0);
+}
+
+void GFX_GL_IMPL::DeleteTextureCombiners( TextureCombinersHandle& textureCombiners )
+{
+ DBG_LOG_GLES20("DeleteTextureCombiners()");
+ TextureCombinersGLES2* implGLES = OBJECT_FROM_HANDLE(textureCombiners, TextureCombinersGLES2);
+ delete implGLES;
+ textureCombiners.Reset();
+}
+
+void GFX_GL_IMPL::SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props )
+{
+ DBG_LOG_GLES20("SetTextureCombiners()");
+ TextureCombinersGLES2* implGLES = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersGLES2);
+ Assert (implGLES);
+
+ const int count = std::min(gGraphicsCaps.maxTexUnits, implGLES->count);
+ // Fill in arrays
+ TexEnvData* texEnvData;
+ ALLOC_TEMP (texEnvData, TexEnvData, count);
+ for( int i = 0; i < count; ++i )
+ {
+ ShaderLab::TexEnv *te = ShaderLab::GetTexEnvForBinding( implGLES->texEnvs[i], props );
+ Assert( te != NULL );
+ te->PrepareData (implGLES->texEnvs[i].m_TextureName.index, implGLES->texEnvs[i].m_MatrixName, props, &texEnvData[i]);
+ }
+
+ Vector4f* texColors;
+ ALLOC_TEMP (texColors, Vector4f, implGLES->count);
+ for( int i = 0; i < implGLES->count; ++i )
+ {
+ const ShaderLab::TextureBinding& binding = implGLES->texEnvs[i];
+ texColors[i] = binding.GetTexColor().Get (props);
+ }
+ GFX_GL_IMPL::SetTextureCombinersThreadable(textureCombiners, texEnvData, texColors);
+}
+
+void GFX_GL_IMPL::SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias)
+{
+ DBG_LOG_GLES20("SetTexture(%d %d)", unit, texture.m_ID);
+ DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexUnits );
+
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(texture);
+ if(targetTex == 0)
+ return;
+
+ TextureUnitStateGLES2& currTex = STATE.textures[unit];
+ if (STATE.textureCount > unit && targetTex == currTex.texID)
+ {
+ return;
+ }
+ ActivateTextureUnitGLES2 (STATE, unit);
+
+ switch (dim)
+ {
+ case kTexDim2D: GLES_CHK(glBindTexture(GL_TEXTURE_2D, targetTex)); break;
+ case kTexDimCUBE: GLES_CHK(glBindTexture(GL_TEXTURE_CUBE_MAP, targetTex)); break;
+ default: break;
+ }
+
+ if (STATE.textureCount <= unit)
+ STATE.textureCount = unit+1;
+ currTex.texID = targetTex;
+ currTex.texDim = dim;
+ if (currTex.texGen == kTexGenUnknown)
+ currTex.texGen = kTexGenDisabled;
+
+ STATE.ApplyTexGen(unit);
+
+ GLESAssert();
+
+ #if defined(GL_EXT_texture_lod_bias) && !UNITY_LINUX
+ if (gGraphicsCaps.hasMipLevelBias && unitState.bias != bias && bias != std::numeric_limits<float>::infinity())
+ {
+ GL_CHK(glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, bias ));
+ unitState.bias = bias;
+ }
+ #endif
+}
+
+void GFX_GL_IMPL::SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16])
+{
+ DBG_LOG_GLES20("SetTextureTransform()");
+ DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexUnits );
+ TextureUnitStateGLES2& unitState = STATE.textures[unit];
+
+ unitState.SetTexGen(texGen);
+ unitState.textureMatrix = *(Matrix4x4f const*)matrix;
+
+ // Since we will set texture matrix even if TexGen is disabled (since matrix can contain scale/offset information)
+ // we set textureMatrix to identity to be on a safe side
+ if (identity)
+ unitState.textureMatrix.SetIdentity();
+ unitState.identityMatrix = identity;
+
+ unitState.isProjected = false;
+ if (!identity && dim==kTexDim2D)
+ unitState.isProjected = (matrix[3] != 0.0f || matrix[7] != 0.0f || matrix[11] != 0.0f || matrix[15] != 1.0f);
+
+ STATE.ApplyTexGen(unit);
+}
+
+static const unsigned long kGLES20TextureDimensionTable[kTexDimCount] = {0, -1/*GL_TEXTURE_1D*/, GL_TEXTURE_2D, -1/*GL_TEXTURE_3D*/, GL_TEXTURE_CUBE_MAP, 0};
+
+void GFX_GL_IMPL::SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace )
+{
+ DBG_LOG_GLES20("SetTextureParams()");
+
+ TextureIdMapGLES20_QueryOrCreate(texture);
+
+ GLuint target = kGLES20TextureDimensionTable[texDim];
+ SetTexture (kShaderFragment, 0, 0, texture, texDim, std::numeric_limits<float>::infinity());
+
+ // Anisotropic texturing...
+ if( gGraphicsCaps.hasAnisoFilter )
+ {
+ anisoLevel = std::min( anisoLevel, gGraphicsCaps.maxAnisoLevel );
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoLevel ));
+ }
+
+ GLenum wrapMode = kWrapModeES2[wrap];
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_WRAP_S, wrapMode ));
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_WRAP_T, wrapMode ));
+
+ if( !hasMipMap && filter == kTexFilterTrilinear )
+ filter = kTexFilterBilinear;
+
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_MAG_FILTER, filter != kTexFilterNearest ? GL_LINEAR : GL_NEAREST ));
+ if( hasMipMap )
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_MIN_FILTER, kMinFilterES2[filter] ));
+ else
+ GLES_CHK(glTexParameteri (target, GL_TEXTURE_MIN_FILTER, filter != kTexFilterNearest ? GL_LINEAR : GL_NEAREST));
+
+ GLESAssert();
+}
+
+void GFX_GL_IMPL::SetShadersThreadable (GpuProgram* programs[kShaderTypeCount], const GpuProgramParameters* params[kShaderTypeCount], UInt8 const * const paramsBuffer[kShaderTypeCount])
+{
+ DBG_LOG_GLES20("SetShadersThreadable()");
+ GpuProgram* vertexProgram = programs[kShaderVertex];
+ GpuProgram* fragmentProgram = programs[kShaderFragment];
+
+ DBG_LOG_GLES20("SetShaders(%s, %s)",
+ GetShaderImplTypeString(vertexProgram? vertexProgram->GetImplType():kShaderImplUndefined),
+ GetShaderImplTypeString(fragmentProgram? fragmentProgram->GetImplType():kShaderImplUndefined));
+
+ // GLSL is only supported like this:
+ // vertex shader actually is both vertex & fragment linked program
+ // fragment shader is unused
+ if (vertexProgram && vertexProgram->GetImplType() == kShaderImplBoth)
+ {
+ Assert(fragmentProgram == 0 || fragmentProgram->GetImplType() == kShaderImplBoth);
+ STATE.activeProgram = vertexProgram;
+ STATE.activeProgramParams = params[kShaderVertex];
+ DebugAssert(STATE.activeProgramParams->IsReady());
+ int bufferSize = STATE.activeProgramParams->GetValuesSize();
+ STATE.activeProgramParamsBuffer.resize_uninitialized(bufferSize);
+ memcpy(STATE.activeProgramParamsBuffer.data(), paramsBuffer[kShaderVertex], bufferSize);
+ }
+ else
+ {
+ Assert(vertexProgram == 0);
+ STATE.activeProgram = 0;
+ STATE.activeProgramParams = 0;
+ STATE.activeProgramParamsBuffer.resize_uninitialized(0);
+ }
+}
+
+void GFX_GL_IMPL::CreateShaderParameters( ShaderLab::SubProgram* program, FogMode fogMode )
+{
+ GlslGpuProgramGLES20& programGLES = static_cast<GlslGpuProgramGLES20&>(program->GetGpuProgram());
+ programGLES.GetGLProgram(fogMode, program->GetParams(fogMode), program->GetChannels());
+}
+
+bool GFX_GL_IMPL::IsShaderActive( ShaderType type ) const
+{
+ //DBG_LOG_GLES20("IsShaderActive(%s): returns %s", GetShaderTypeString(type), STATE.shaderEnabledImpl[type] != kShaderImplUndefined?"True":"False");
+ //return STATE.shaderEnabledImpl[type] != kShaderImplUndefined;
+ DBG_LOG_GLES20("IsShaderActive(%s):", GetShaderTypeString(type));
+ return (STATE.activeProgram != 0);
+}
+
+void GFX_GL_IMPL::DestroySubProgram( ShaderLab::SubProgram* subprogram )
+{
+ //@TODO
+ //if (STATE.activeProgram == program)
+ //{
+ // STATE.activeProgram = NULL;
+ // STATE.activeProgramProps = NULL;
+ //}
+ delete subprogram;
+}
+
+
+
+void GFX_GL_IMPL::DisableLights( int startLight )
+{
+ DBG_LOG_GLES20("DisableLights(%d)", startLight);
+ startLight = std::min (startLight, gGraphicsCaps.maxLights);
+ STATE.vertexLightCount = startLight;
+ for (int i = startLight; i < kMaxSupportedVertexLightsByGLES20; ++i)
+ {
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Position + i), Vector4f(0.0f, 0.0f, 1.0f, 0.0f));
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Diffuse + i), Vector4f(0.0f, 0.0f, 0.0f, 0.0f));
+ }
+}
+
+
+void GFX_GL_IMPL::SetLight( int light, const GfxVertexLight& data)
+{
+ DBG_LOG_GLES20("SetLight(%d), [{%f, %f, %f, %f}, {%f, %f, %f, %f}, {%f, %f, %f, %f}, %f, %f, %f, %d]",
+ light,
+ data.position[0], data.position[1], data.position[2], data.position[3],
+ data.spotDirection[0], data.spotDirection[1], data.spotDirection[2], data.spotDirection[3],
+ data.color[0], data.color[1], data.color[2], data.color[3],
+ data.range, data.quadAtten, data.spotAngle, data.type);
+ Assert( light >= 0 && light < kMaxSupportedVertexLights );
+
+ if (light >= kMaxSupportedVertexLightsByGLES20)
+ return;
+
+ STATE.vertexLightTypes[light] = data.type;
+
+ SetupVertexLightParams (light, data);
+}
+
+void GFX_GL_IMPL::SetAmbient( const float ambient[4] )
+{
+ DBG_LOG_GLES20("SetAmbient()");
+ STATE.ambient.set (ambient[0], ambient[1], ambient[2], ambient[3]);
+ m_BuiltinParamValues.SetVectorParam(kShaderVecLightModelAmbient, Vector4f(ambient));
+}
+
+void GFX_GL_IMPL::EnableFog (const GfxFogParams& fog)
+{
+ DBG_LOG_GLES20("EnableFog()");
+ DebugAssertIf( fog.mode <= kFogDisabled );
+ m_FogParams = fog;
+}
+
+void GFX_GL_IMPL::DisableFog()
+{
+ DBG_LOG_GLES20("DisableFog()");
+
+ if( m_FogParams.mode != kFogDisabled )
+ {
+ m_FogParams.mode = kFogDisabled;
+ m_FogParams.density = 0.0f;
+ }
+}
+
+VBO* GFX_GL_IMPL::CreateVBO()
+{
+ VBO* vbo = new GLES2VBO();
+ OnCreateVBO(vbo);
+ return vbo;
+}
+
+void GFX_GL_IMPL::DeleteVBO( VBO* vbo )
+{
+ OnDeleteVBO(vbo);
+ delete vbo;
+}
+
+DynamicVBO& GFX_GL_IMPL::GetDynamicVBO()
+{
+ if( !STATE.m_DynamicVBO ) {
+ STATE.m_DynamicVBO = new DynamicGLES2VBO();
+ }
+ return *STATE.m_DynamicVBO;
+}
+
+// ---------- render textures
+
+static GLuint GetFBO(GLuint* fbo)
+{
+ if (!(*fbo) && gGraphicsCaps.hasRenderToTexture)
+ glGenFramebuffers(1, fbo);
+
+ return *fbo;
+}
+static GLuint GetSharedFBO (DeviceStateGLES20& state) { return GetFBO (&state.m_SharedFBO); }
+static GLuint GetHelperFBO (DeviceStateGLES20& state) { return GetFBO (&state.m_HelperFBO); }
+
+RenderSurfaceHandle GFX_GL_IMPL::CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags)
+{
+ RenderSurfaceHandle rs = CreateRenderColorSurfaceGLES2 (textureID, 0, width, height, dim, createFlags, format, samples);
+#if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+#endif
+ return rs;
+}
+RenderSurfaceHandle GFX_GL_IMPL::CreateRenderDepthSurface (TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags)
+{
+ RenderSurfaceHandle rs = CreateRenderDepthSurfaceGLES2 (textureID, 0, width, height, createFlags, depthFormat, samples);
+#if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+#endif
+ return rs;
+}
+void GFX_GL_IMPL::DestroyRenderSurface (RenderSurfaceHandle& rs)
+{
+ // Early out if render surface is not created (don't do invalidate/flush in that case)
+ if( !rs.IsValid() )
+ return;
+
+ DestroyRenderSurfaceGLES2 (rs);
+
+ InvalidateState();
+#if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+#endif
+}
+void GFX_GL_IMPL::SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face)
+{
+#if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+#endif
+ if (SetRenderTargetGLES2 (count, colorHandles, depthHandle, mipLevel, face, GetSharedFBO(STATE)))
+ {
+ // changing render target might mean different color clear flags; so reset current state
+ STATE.m_CurrBlendState = NULL;
+ }
+#if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+#endif
+}
+
+void GFX_GL_IMPL::ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle)
+{
+ ResolveColorSurfaceGLES2(srcHandle, dstHandle, GetSharedFBO(STATE), GetHelperFBO(STATE));
+}
+
+RenderSurfaceHandle GFX_GL_IMPL::GetActiveRenderColorSurface(int index)
+{
+ Assert (index == 0);
+ return GetActiveRenderColorSurfaceGLES2();
+}
+RenderSurfaceHandle GFX_GL_IMPL::GetActiveRenderDepthSurface()
+{
+ return GetActiveRenderDepthSurfaceGLES2();
+}
+void GFX_GL_IMPL::SetSurfaceFlags (RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags)
+{
+}
+void GFX_GL_IMPL::DiscardContents (RenderSurfaceHandle& rs)
+{
+ DiscardContentsGLES2(rs);
+}
+
+
+// ---------- uploading textures
+
+void GFX_GL_IMPL::UploadTexture2D( TextureID texture, TextureDimension dimension, UInt8* srcData, int srcSize, int width, int height, TextureFormat format, int mipCount, UInt32 uploadFlags, int skipMipLevels, TextureUsageMode usageMode, TextureColorSpace colorSpace )
+{
+ ::UploadTexture2DGLES2( texture, dimension, srcData, width, height, format, mipCount, uploadFlags, skipMipLevels, colorSpace );
+}
+
+void GFX_GL_IMPL::UploadTextureSubData2D( TextureID texture, UInt8* srcData, int srcSize, int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace )
+{
+ ::UploadTextureSubData2DGLES2( texture, srcData, mipLevel, x, y, width, height, format, colorSpace );
+}
+
+void GFX_GL_IMPL::UploadTextureCube( TextureID texture, UInt8* srcData, int srcSize, int faceDataSize, int size, TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace )
+{
+ ::UploadTextureCubeGLES2( texture, srcData, faceDataSize, size, format, mipCount, uploadFlags, colorSpace );
+}
+
+void GFX_GL_IMPL::UploadTexture3D( TextureID texture, UInt8* srcData, int srcSize, int width, int height, int depth, TextureFormat format, int mipCount, UInt32 uploadFlags )
+{
+ ErrorString("3D textures are not supported by OpenGLES!");
+}
+
+void GFX_GL_IMPL::DeleteTexture(TextureID texture)
+{
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(texture);
+ if(targetTex == 0)
+ return;
+
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(texture.m_ID);
+ GLES_CHK(glDeleteTextures(1, &targetTex));
+
+ // invalidate texture unit states that used this texture
+ for( int i = 0; i < gGraphicsCaps.maxTexUnits; ++i )
+ {
+ TextureUnitStateGLES2& currTex = STATE.textures[i];
+ if( currTex.texID == targetTex )
+ currTex.Invalidate();
+ }
+
+ TextureIdMap::RemoveTexture(texture);
+}
+
+// ---------- context
+
+GfxDevice::PresentMode GFX_GL_IMPL::GetPresentMode()
+{
+ return UNITY_IPHONE ? kPresentAfterDraw : kPresentBeforeUpdate;
+}
+void GFX_GL_IMPL::BeginFrame()
+{
+ DBG_LOG_GLES20("BeginFrame()");
+ m_InsideFrame = true;
+
+ if(gGraphicsCaps.hasTiledGPU)
+ {
+ SetBackBufferGLES2();
+
+ extern void ClearCurrentFBImpl(bool clearColor, bool clearDepth);
+ ClearCurrentFBImpl(true,true);
+ }
+}
+void GFX_GL_IMPL::EndFrame()
+{
+ // on ios we do it in trampoline
+ // also we handle BackBuffer MSAA and Render Resolution ourselves, so discard is a bit more complicated
+#if !UNITY_IPHONE
+ if(gGraphicsCaps.gles20.hasDiscardFramebuffer)
+ {
+ SetBackBufferGLES2();
+
+ extern void DiscardCurrentFBImpl(bool discardColor, bool discardDepth, GLenum target);
+ DiscardCurrentFBImpl(false, true, GL_FRAMEBUFFER);
+ }
+#endif
+
+
+ DBG_LOG_GLES20("EndFrame()");
+ m_InsideFrame = false;
+}
+
+bool GFX_GL_IMPL::IsValidState()
+{
+#if UNITY_ANDROID
+ return ContextGLES::IsValid();
+#else
+ return true;
+#endif
+}
+
+bool GFX_GL_IMPL::HandleInvalidState()
+{
+#if UNITY_ANDROID
+ bool needReload;
+ if (!ContextGLES::HandleInvalidState(&needReload))
+ return false;
+ if (needReload)
+ ReloadResources();
+ return true;
+#else
+ return true;
+#endif
+}
+
+void GFX_GL_IMPL::PresentFrame()
+{
+ DBG_LOG_GLES20("====================================");
+ DBG_LOG_GLES20("====================================");
+ DBG_LOG_GLES20("PresentFrame");
+ DBG_LOG_GLES20("====================================");
+ DBG_LOG_GLES20("====================================");
+
+#if UNITY_WIN
+ PresentContextGLES20();
+#else
+ PresentContextGLES();
+#endif
+}
+
+void GFX_GL_IMPL::FinishRendering()
+{
+ GLES_CHK(glFinish());
+}
+
+
+// ---------- immediate mode rendering
+
+// we break very large immediate mode submissions into multiple batches internally
+const int kMaxImmediateVerticesPerDraw = 2048;
+
+
+ImmediateModeGLES20::ImmediateModeGLES20()
+: m_Mode(kPrimitiveTriangles)
+, m_QuadsIB(0)
+, m_IndexBufferQuadsID(0)
+{
+ m_QuadsIB = (UInt16*)UNITY_MALLOC(kMemGeometry, kMaxImmediateVerticesPerDraw*6*sizeof(UInt16));
+ UInt32 baseIndex = 0;
+ UInt16* ibPtr = m_QuadsIB;
+ for( int i = 0; i < kMaxImmediateVerticesPerDraw; ++i )
+ {
+ ibPtr[0] = baseIndex + 1;
+ ibPtr[1] = baseIndex + 2;
+ ibPtr[2] = baseIndex;
+ ibPtr[3] = baseIndex + 2;
+ ibPtr[4] = baseIndex + 3;
+ ibPtr[5] = baseIndex;
+ baseIndex += 4;
+ ibPtr += 6;
+ }
+}
+
+ImmediateModeGLES20::~ImmediateModeGLES20()
+{
+ Invalidate();
+ UNITY_FREE(kMemGeometry,m_QuadsIB);
+}
+
+void ImmediateModeGLES20::Invalidate()
+{
+ if( m_IndexBufferQuadsID )
+ {
+ glDeregisterBufferData(1, (GLuint*)&m_IndexBufferQuadsID);
+ GLES_CHK(glDeleteBuffers(1, (GLuint*)&m_IndexBufferQuadsID));
+ m_IndexBufferQuadsID = 0;
+ }
+ m_Vertices.clear();
+ memset( &m_Current, 0, sizeof(m_Current) );
+}
+
+void GFX_GL_IMPL::ImmediateVertex( float x, float y, float z )
+{
+ // If the current batch is becoming too large, internally end it and begin it again.
+ size_t currentSize = STATE.m_Imm.m_Vertices.size();
+ if( currentSize >= kMaxImmediateVerticesPerDraw - 4 )
+ {
+ GfxPrimitiveType mode = STATE.m_Imm.m_Mode;
+ // For triangles, break batch when multiple of 3's is reached.
+ if( mode == kPrimitiveTriangles && currentSize % 3 == 0 )
+ {
+ ImmediateEnd();
+ ImmediateBegin( mode );
+ }
+ // For other primitives, break on multiple of 4's.
+ // NOTE: This won't quite work for triangle strips, but we'll just pretend
+ // that will never happen.
+ else if( mode != kPrimitiveTriangles && currentSize % 4 == 0 )
+ {
+ ImmediateEnd();
+ ImmediateBegin( mode );
+ }
+ }
+ Vector3f& vert = STATE.m_Imm.m_Current.vertex;
+ vert.x = x;
+ vert.y = y;
+ vert.z = z;
+ STATE.m_Imm.m_Vertices.push_back( STATE.m_Imm.m_Current );
+}
+
+void GFX_GL_IMPL::ImmediateNormal( float x, float y, float z )
+{
+ STATE.m_Imm.m_Current.normal.x = x;
+ STATE.m_Imm.m_Current.normal.y = y;
+ STATE.m_Imm.m_Current.normal.z = z;
+}
+
+void GFX_GL_IMPL::ImmediateColor( float r, float g, float b, float a )
+{
+ r = clamp01(r);
+ g = clamp01(g);
+ b = clamp01(b);
+ a = clamp01(a);
+
+ STATE.m_Imm.m_Current.color =
+ ((UInt32)(a * 255.0f) << 24) |
+ ((UInt32)(b * 255.0f) << 16) |
+ ((UInt32)(g * 255.0f) << 8) |
+ ((UInt32)(r * 255.0f));
+}
+
+void GFX_GL_IMPL::ImmediateTexCoordAll( float x, float y, float z )
+{
+ for( int i = 0; i < gGraphicsCaps.maxTexCoords; ++i )
+ {
+ Vector3f& uv = STATE.m_Imm.m_Current.texCoords[i];
+ uv.x = x;
+ uv.y = y;
+ uv.z = z;
+ }
+}
+
+void GFX_GL_IMPL::ImmediateTexCoord( int unit, float x, float y, float z )
+{
+ if( unit < 0 || unit >= 8 )
+ {
+ ErrorString( "Invalid unit for texcoord" );
+ return;
+ }
+ Vector3f& uv = STATE.m_Imm.m_Current.texCoords[unit];
+ uv.x = x;
+ uv.y = y;
+ uv.z = z;
+}
+
+void GFX_GL_IMPL::ImmediateBegin( GfxPrimitiveType type )
+{
+ STATE.m_Imm.m_Mode = type;
+ STATE.m_Imm.m_Vertices.clear();
+}
+
+void GFX_GL_IMPL::ImmediateEnd()
+{
+ int vertexCount = STATE.m_Imm.m_Vertices.size();
+ if( vertexCount == 0 )
+ return;
+
+ InvalidateVertexInputCacheGLES20();
+
+ const size_t stride = sizeof(ImmediateVertexGLES20);
+ const ImmediateVertexGLES20* vertices = &STATE.m_Imm.m_Vertices[0];
+
+ void* dstVertices = LockSharedBufferGLES20 (GL_ARRAY_BUFFER, stride * vertexCount);
+ DebugAssert (dstVertices);
+ memcpy (dstVertices, vertices, stride * vertexCount);
+
+ if(STATE.m_Imm.m_Mode == kPrimitiveQuads && !STATE.m_Imm.m_IndexBufferQuadsID)
+ {
+ GLES_CHK(glGenBuffers( 1, (GLuint*)&STATE.m_Imm.m_IndexBufferQuadsID));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, STATE.m_Imm.m_IndexBufferQuadsID));
+ GLES_CHK(glBufferData(GL_ELEMENT_ARRAY_BUFFER, kMaxImmediateVerticesPerDraw * 6, STATE.m_Imm.m_QuadsIB, GL_STATIC_DRAW));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ }
+
+ const GLuint vbo = UnlockSharedBufferGLES20 ();
+ const GLuint ibo = (STATE.m_Imm.m_Mode == kPrimitiveQuads)? STATE.m_Imm.m_IndexBufferQuadsID: 0;
+
+ //;;printf_console("ImmediateEnd> vbo: %d ibo: %d\n", vbo, ibo);
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, vbo));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo));
+
+ size_t offset = 0;
+ if(gGraphicsCaps.gles20.slowDynamicVBO)
+ offset = (size_t)static_cast<DynamicGLES2VBO&> (GetRealGfxDevice ().GetDynamicVBO ()).GetVertexMemory(stride * vertexCount);
+
+ GLES_CHK(glEnableVertexAttribArray(GL_VERTEX_ARRAY));
+ //;;printf_console("ImmediateEnd> vertex %d\n", offset);
+ GLES_CHK(glVertexAttribPointer(GL_VERTEX_ARRAY, 3, GL_FLOAT, false, stride, (void*)offset)); offset += sizeof(Vector3f);
+ GLES_CHK(glEnableVertexAttribArray(GL_NORMAL_ARRAY));
+ //;;printf_console("ImmediateEnd> normal %d\n", offset);
+ GLES_CHK(glVertexAttribPointer(GL_NORMAL_ARRAY, 3, GL_FLOAT, false, stride, (void*)offset)); offset += sizeof(Vector3f);
+ GLES_CHK(glEnableVertexAttribArray(GL_COLOR_ARRAY));
+ //;;printf_console("ImmediateEnd> color %d\n", offset);
+ GLES_CHK(glVertexAttribPointer(GL_COLOR_ARRAY, 4, GL_UNSIGNED_BYTE, true, stride, (void*)offset)); offset += sizeof(UInt32);
+ for (size_t q = 0; q < gGraphicsCaps.maxTexUnits; ++q)
+ {
+ if (GL_TEXTURE_ARRAY0 + q < gGraphicsCaps.gles20.maxAttributes)
+ {
+ GLES_CHK(glEnableVertexAttribArray(GL_TEXTURE_ARRAY0 + q));
+ //;;printf_console("ImmediateEnd> tex%d %d\n", q, offset);
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY0 + q, 3, GL_FLOAT, false, stride, (void*)offset)); offset += sizeof(Vector3f);
+ }
+ else
+ {
+ #pragma message ("ToDo")
+ }
+ }
+
+ if(!gGraphicsCaps.gles20.slowDynamicVBO)
+ {
+ DebugAssert (offset <= stride);
+ }
+
+ BeforeDrawCall (true);
+
+ //;;printf_console("ImmediateEnd> dip %d %d\n", STATE.m_Imm.m_Mode, vertexCount);
+ switch (STATE.m_Imm.m_Mode)
+ {
+ case kPrimitiveTriangles:
+ GLES_CHK(glDrawArrays(GL_TRIANGLES, 0, vertexCount));
+ m_Stats.AddDrawCall( vertexCount / 3, vertexCount );
+ break;
+ case kPrimitiveTriangleStripDeprecated:
+ GLES_CHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexCount));
+ m_Stats.AddDrawCall( vertexCount - 2, vertexCount );
+ break;
+ case kPrimitiveQuads:
+ GLES_CHK(glDrawElements(GL_TRIANGLES, (vertexCount/2)*3, GL_UNSIGNED_SHORT, 0));
+ m_Stats.AddDrawCall( vertexCount / 2, vertexCount );
+ break;
+ case kPrimitiveLines:
+ GLES_CHK(glDrawArrays(GL_LINES, 0, vertexCount));
+ m_Stats.AddDrawCall( vertexCount / 2, vertexCount );
+ break;
+ default:
+ AssertString("ImmediateEnd: unknown draw mode");
+ }
+
+ // reset VBO bindings
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, 0));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ InvalidateVertexInputCacheGLES20();
+
+ // clear vertices
+ STATE.m_Imm.m_Vertices.clear();
+}
+
+// ---------- readback path
+
+bool GFX_GL_IMPL::CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 )
+{
+ GLES_CHK(glReadPixels( left, bottom, width, height, GL_RGBA, GL_UNSIGNED_BYTE, rgba32 ));
+ return true;
+}
+
+bool GFX_GL_IMPL::ReadbackImage( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY )
+{
+ return ReadbackTextureGLES2(image, left, bottom, width, height, destX, destY, GetSharedFBO(STATE), GetHelperFBO(STATE));
+}
+void GFX_GL_IMPL::GrabIntoRenderTexture( RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height )
+{
+ ::GrabIntoRenderTextureGLES2(rs, rd, x, y, width, height, GetSharedFBO(STATE), GetHelperFBO(STATE));
+}
+
+
+#if ENABLE_PROFILER
+
+
+void GFX_GL_IMPL::BeginProfileEvent(const char* name)
+{
+ if(gGraphicsCaps.gles20.hasDebugMarkers)
+ gGlesExtFunc.glPushGroupMarkerEXT(0, name);
+}
+
+void GFX_GL_IMPL::EndProfileEvent()
+{
+ if(gGraphicsCaps.gles20.hasDebugMarkers)
+ gGlesExtFunc.glPopGroupMarkerEXT();
+}
+
+GfxTimerQuery* GFX_GL_IMPL::CreateTimerQuery()
+{
+ if( gGraphicsCaps.hasTimerQuery )
+ return new TimerQueryGLES;
+ return NULL;
+}
+
+void GFX_GL_IMPL::DeleteTimerQuery(GfxTimerQuery* query)
+{
+ delete query;
+}
+
+void GFX_GL_IMPL::BeginTimerQueries()
+{
+ if( !gGraphicsCaps.hasTimerQuery )
+ return;
+
+ g_TimerQueriesGLES.BeginTimerQueries();
+}
+
+void GFX_GL_IMPL::EndTimerQueries()
+{
+ if( !gGraphicsCaps.hasTimerQuery )
+ return;
+
+ g_TimerQueriesGLES.EndTimerQueries();
+}
+#endif // ENABLE_PROFILER
+
+typedef std::map<FixedFunctionStateGLES20, FixedFunctionProgramGLES20*, FullStateCompareGLES20> FFProgramCacheT;
+typedef std::map<FixedFunctionStateGLES20, GLShaderID, VertexStateCompareGLES20> FFVertexProgramCacheT;
+typedef std::map<FixedFunctionStateGLES20, GLShaderID, FragmentStateCompareGLES20> FFFragmentProgramCacheT;
+
+static FFProgramCacheT g_FixedFunctionProgramCache;
+static FFVertexProgramCacheT g_FFVertexProgramCache;
+static FFFragmentProgramCacheT g_FFFragmentProgramCache;
+
+void ClearFixedFunctionPrograms()
+{
+ for (FFVertexProgramCacheT::iterator it = g_FFVertexProgramCache.begin(); it != g_FFVertexProgramCache.end(); ++it)
+ {
+ GLES_CHK(glDeleteShader(it->second));
+ }
+ g_FFVertexProgramCache.clear();
+ for (FFFragmentProgramCacheT::iterator it = g_FFFragmentProgramCache.begin(); it != g_FFFragmentProgramCache.end(); ++it)
+ {
+ GLES_CHK(glDeleteShader(it->second));
+ }
+ g_FFFragmentProgramCache.clear();
+
+ for(FFProgramCacheT::iterator i = g_FixedFunctionProgramCache.begin(); i != g_FixedFunctionProgramCache.end(); ++i)
+ {
+ delete i->second;
+ }
+ g_FixedFunctionProgramCache.clear();
+
+}
+
+static const FixedFunctionProgramGLES20* GetFixedFunctionProgram(const FixedFunctionStateGLES20& state)
+{
+ FFProgramCacheT::const_iterator cachedProgIt = g_FixedFunctionProgramCache.find(state);
+ if (cachedProgIt != g_FixedFunctionProgramCache.end())
+ return cachedProgIt->second;
+
+ // Cache miss, create fixed function program
+ // NOTE: don't worry too much about performance of vertex/fragment maps
+ // shader building/compilation is crazy expensive anyway
+ FFVertexProgramCacheT::const_iterator vertexProgIt = g_FFVertexProgramCache.find(state);
+ FFFragmentProgramCacheT::const_iterator fragmentProgIt = g_FFFragmentProgramCache.find(state);
+
+ GLShaderID vertexShader = (vertexProgIt != g_FFVertexProgramCache.end())? vertexProgIt->second: 0;
+ GLShaderID fragmentShader = (fragmentProgIt != g_FFFragmentProgramCache.end())? fragmentProgIt->second: 0;
+
+ if (vertexShader == 0)
+ {
+ vertexShader = glCreateShader(GL_VERTEX_SHADER);
+ std::string src = BuildVertexShaderSourceGLES20(state);
+ const char* cStr = src.c_str();
+
+ DBG_SHADER_VERBOSE_GLES20("Compiling generated vertex shader");
+ CompileGlslShader(vertexShader, kErrorCompileVertexShader, cStr);
+ GLESAssert();
+
+ g_FFVertexProgramCache[state] = vertexShader;
+ }
+
+ if (fragmentShader == 0)
+ {
+ fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
+ std::string src = BuildFragmentShaderSourceGLES20(state);
+ const char* cStr = src.c_str();
+
+ DBG_SHADER_VERBOSE_GLES20("Compiling generated fragment shader");
+ CompileGlslShader(fragmentShader, kErrorCompileFragShader, cStr);
+ GLESAssert();
+
+ g_FFFragmentProgramCache[state] = fragmentShader;
+ }
+
+ DBG_SHADER_VERBOSE_GLES20("Creating and linking GLES program");
+ FixedFunctionProgramGLES20* ffProg = new FixedFunctionProgramGLES20(vertexShader, fragmentShader);
+ g_FixedFunctionProgramCache[state] = ffProg;
+
+ return ffProg;
+}
+
+static bool ComputeTextureTransformMatrix(TextureUnitStateGLES2 const& tex, Matrix4x4f const& worldViewMatrix, Matrix4x4f const& worldMatrix,
+ Matrix4x4f& outMatrix)
+{
+ switch (tex.texGen)
+ {
+ case kTexGenDisabled:
+ // NOTE: although tex-gen can be disabled
+ // textureMatrix can contain UV scale/offset
+ // so we will set it
+ case kTexGenObject:
+ if (tex.identityMatrix)
+ {
+ outMatrix.SetIdentity();
+ return false;
+ }
+ CopyMatrix(tex.textureMatrix.GetPtr(), outMatrix.GetPtr());
+ break;
+ case kTexGenSphereMap:
+ {
+ float invScale = 1.0f / Magnitude (worldViewMatrix.GetAxisX());
+
+ Matrix4x4f scaleOffsetMatrix;
+ scaleOffsetMatrix.SetScale(Vector3f(0.5*invScale, 0.5*invScale, 0.0));
+ scaleOffsetMatrix.SetPosition(Vector3f(0.5, 0.5, 0.0));
+
+ Matrix4x4f worldViewMatrixRotation = worldViewMatrix;
+ worldViewMatrixRotation.SetPosition(Vector3f::zero);
+ Matrix4x4f combo;
+ MultiplyMatrices4x4(&scaleOffsetMatrix, &worldViewMatrixRotation, &combo);
+ MultiplyMatrices4x4(&tex.textureMatrix, &combo, &outMatrix);
+ break;
+ }
+ case kTexGenEyeLinear:
+ MultiplyMatrices4x4(&tex.textureMatrix, &worldViewMatrix, &outMatrix);
+ break;
+ case kTexGenCubeNormal:
+ case kTexGenCubeReflect:
+ {
+ float invScale = 1.0f / Magnitude (worldMatrix.GetAxisX());
+ CopyMatrix(worldViewMatrix.GetPtr(), outMatrix.GetPtr());
+ outMatrix.Scale(Vector3f(invScale, invScale, invScale));
+ outMatrix.SetPosition(Vector3f::zero);
+ break;
+ }
+ default:
+ ErrorString( Format("Unknown TexGen mode %d", tex.texGen) );
+ }
+ return true;
+}
+
+void VBOContainsColorGLES20(bool flag)
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ GetGLES20DeviceState(device).vboContainsColor = flag;
+}
+
+//chai: glUseProgram
+void GLSLUseProgramGLES20(UInt32 programID)
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ if (GetGLES20DeviceState(device).activeProgramID == programID)
+ {
+#if UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+ if (gGraphicsCaps.gles20.buggyVFetchPatching)
+ {
+ // NOTE: Qualcomm driver (Android2.2) fails to re-patch vfetch instructions
+ // if shader stays the same, but vertex layout has been changed
+ // as a temporary workaround just reset the shader
+ GLES_CHK(glUseProgram (0)); // driver caches shaderid, so set to 0 first
+ GLES_CHK(glUseProgram (programID));
+ return;
+ }
+#endif
+ return;
+ }
+
+#if NV_STATE_FILTERING
+ GetGLES20DeviceState(device).transformState.dirtyFlags |= TransformState::kWorldViewProjDirty;
+#endif
+
+ GLES_CHK(glUseProgram (programID));
+ GetGLES20DeviceState(device).activeProgramID = programID;
+}
+
+
+struct SetValuesFunctorES2
+{
+ SetValuesFunctorES2(GfxDevice& device, UniformCacheGLES20* targetCache) : m_Device(device), m_TargetCache(targetCache) { }
+ GfxDevice& m_Device;
+ UniformCacheGLES20* m_TargetCache;
+ void SetVectorVal (ShaderParamType type, int index, const float* ptr, int cols)
+ {
+ switch(cols) {
+ case 1: CachedUniform1(m_TargetCache, type, index, ptr); break;
+ case 2: CachedUniform2(m_TargetCache, type, index, ptr); break;
+ case 3: CachedUniform3(m_TargetCache, type, index, ptr); break;
+ case 4: CachedUniform4(m_TargetCache, type, index, ptr); break;
+ }
+ }
+ void SetMatrixVal (int index, const Matrix4x4f* ptr, int rows)
+ {
+ DebugAssert(rows == 4);
+ GLES_CHK(glUniformMatrix4fv (index, 1, GL_FALSE, ptr->GetPtr()));
+ }
+ void SetTextureVal (ShaderType shaderType, int index, int samplerIndex, TextureDimension dim, TextureID texID)
+ {
+ m_Device.SetTexture (shaderType, index, samplerIndex, texID, dim, std::numeric_limits<float>::infinity());
+ }
+};
+
+
+//chai: ÉèÖÃshader uniforms
+void GFX_GL_IMPL::BeforeDrawCall(bool immediateMode)
+{
+ DBG_LOG_GLES20("BeforeDrawCall(%s)", GetBoolString(immediateMode));
+
+ ShaderLab::PropertySheet *props = ShaderLab::g_GlobalProperties;
+ Assert(props);
+
+ // WorldView Matrix
+#if NV_STATE_FILTERING
+ // UpdateWorldViewMatrix will reset the dirty flags, however we want to forward any modified matrices
+ // to the shader, so we "reset" the dirty state after UpdateWorldViewMatrix has been called
+ UInt32 savedDirty = STATE.transformState.dirtyFlags;
+ STATE.transformState.UpdateWorldViewMatrix (m_BuiltinParamValues);
+ STATE.transformState.dirtyFlags = savedDirty;
+#else
+ STATE.transformState.UpdateWorldViewMatrix (m_BuiltinParamValues);
+#endif
+
+ // Materials
+ if (STATE.lighting)
+ {
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatEmission, Vector4f(STATE.matEmissive.GetPtr()));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatAmbient, Vector4f(STATE.matAmbient.GetPtr()));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatDiffuse, Vector4f(STATE.matDiffuse.GetPtr()));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatSpecular, Vector4f(STATE.matSpecular.GetPtr()));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatShininess, Vector4f(STATE.matShininess, STATE.matShininess, STATE.matShininess, STATE.matShininess));
+ }
+
+ // Fog
+ if (m_FogParams.mode > kFogDisabled)
+ {
+ float diff = m_FogParams.mode == kFogLinear ? m_FogParams.end - m_FogParams.start : 0.0f;
+ float invDiff = Abs(diff) > 0.0001f ? 1.0f/diff : 0.0f;
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFFogColor, m_FogParams.color);
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFFogParams, Vector4f(m_FogParams.density * 1.2011224087f,
+ m_FogParams.density * 1.4426950408f,
+ m_FogParams.mode == kFogLinear ? -invDiff : 0.0f,
+ m_FogParams.mode == kFogLinear ? m_FogParams.end * invDiff : 0.0f
+ ));
+ }
+ // Alpha-test
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFAlphaTestRef, Vector4f(STATE.alphaValue, STATE.alphaValue, STATE.alphaValue, STATE.alphaValue));
+
+ UniformCacheGLES20* targetCache = 0;
+ if (STATE.activeProgram)
+ {
+ //chai: glUseProgram
+ //
+ // Apply GPU program
+ //ת»»ÀàÐÍ
+ GlslGpuProgramGLES20& program = static_cast<GlslGpuProgramGLES20&>(*STATE.activeProgram);
+ int fogIndex = program.ApplyGpuProgramES20 (*STATE.activeProgramParams, STATE.activeProgramParamsBuffer.data());
+ m_BuiltinParamIndices[kShaderVertex] = &STATE.activeProgramParams->GetBuiltinParams();
+
+ targetCache = &program.m_UniformCache[fogIndex];
+ }
+ else
+ {
+ //chai:Ä£Äâ¹Ì¶¨¹ÜÏß
+ // Emulate Fixed Function pipe
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFColor, Vector4f(STATE.color.GetPtr()));
+ for (int i = 0; i < STATE.textureCount; ++i)
+ {
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecFFTextureEnvColor0+i), STATE.textures[i].color);
+ }
+
+ // generate program from fixed function state
+ DBG_LOG_GLES20(" using fixed-function");
+ FixedFunctionStateGLES20 ffstate;
+ STATE.ComputeFixedFunctionState(ffstate, m_FogParams);
+ const FixedFunctionProgramGLES20* program = GetFixedFunctionProgram(ffstate);
+ program->ApplyFFGpuProgram(m_BuiltinParamValues);
+ m_BuiltinParamIndices[kShaderVertex] = &program->GetBuiltinParams();
+
+ targetCache = &program->m_UniformCache;
+ }
+
+ //chai: ÉèÖÃUnityÄÚÖÃuniforms
+
+ // Set Unity built-in parameters
+ {
+ Assert(m_BuiltinParamIndices[kShaderVertex]);
+ const BuiltinShaderParamIndices& params = *m_BuiltinParamIndices[kShaderVertex];
+
+ // MVP matrix
+ if (params.mat[kShaderInstanceMatMVP].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldViewProjDirty)
+#endif
+ )
+ {
+ Matrix4x4f wvp;
+ MultiplyMatrices4x4(&m_BuiltinParamValues.GetMatrixParam(kShaderMatProj), &STATE.transformState.worldViewMatrix, &wvp);
+
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatMVP];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, wvp.GetPtr()));
+ }
+ // MV matrix
+ if (params.mat[kShaderInstanceMatMV].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldViewDirty)
+#endif
+ )
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatMV];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, STATE.transformState.worldViewMatrix.GetPtr()));
+ }
+ // Transpose of MV matrix
+ if (params.mat[kShaderInstanceMatTransMV].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldViewDirty)
+#endif
+ )
+ {
+ Matrix4x4f tWV;
+ TransposeMatrix4x4(&STATE.transformState.worldViewMatrix, &tWV);
+
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatTransMV];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, tWV.GetPtr()));
+ }
+ // Inverse transpose of MV matrix
+ if (params.mat[kShaderInstanceMatInvTransMV].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldViewDirty)
+#endif
+ )
+ {
+ // Inverse transpose of modelview should be scaled by uniform
+ // normal scale (this will match state.matrix.invtrans.modelview
+ // and gl_NormalMatrix in OpenGL)
+ Matrix4x4f mat = STATE.transformState.worldViewMatrix;
+ if (STATE.normalization == kNormalizationScale)
+ {
+ float invScale = m_BuiltinParamValues.GetInstanceVectorParam(kShaderInstanceVecScale).w;
+ mat.Get (0, 0) *= invScale;
+ mat.Get (1, 0) *= invScale;
+ mat.Get (2, 0) *= invScale;
+ mat.Get (0, 1) *= invScale;
+ mat.Get (1, 1) *= invScale;
+ mat.Get (2, 1) *= invScale;
+ mat.Get (0, 2) *= invScale;
+ mat.Get (1, 2) *= invScale;
+ mat.Get (2, 2) *= invScale;
+ }
+ Matrix4x4f invWV, tInvWV;
+ Matrix4x4f::Invert_General3D (mat, invWV);
+ TransposeMatrix4x4(&invWV, &tInvWV);
+
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatInvTransMV];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, tInvWV.GetPtr()));
+ }
+ // M matrix
+ if (params.mat[kShaderInstanceMatM].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldDirty)
+#endif
+ )
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatM];
+ const Matrix4x4f& mat = STATE.transformState.worldMatrix;
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, mat.GetPtr()));
+ }
+ // Inverse M matrix
+ if (params.mat[kShaderInstanceMatInvM].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldDirty)
+#endif
+ )
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatInvM];
+ Matrix4x4f mat = STATE.transformState.worldMatrix;
+ if (STATE.normalization == kNormalizationScale)
+ {
+ // Kill scale in the world matrix before inverse
+ float invScale = m_BuiltinParamValues.GetInstanceVectorParam(kShaderInstanceVecScale).w;
+ mat.Get (0, 0) *= invScale;
+ mat.Get (1, 0) *= invScale;
+ mat.Get (2, 0) *= invScale;
+ mat.Get (0, 1) *= invScale;
+ mat.Get (1, 1) *= invScale;
+ mat.Get (2, 1) *= invScale;
+ mat.Get (0, 2) *= invScale;
+ mat.Get (1, 2) *= invScale;
+ mat.Get (2, 2) *= invScale;
+ }
+ Matrix4x4f inverseMat;
+ Matrix4x4f::Invert_General3D (mat, inverseMat);
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, inverseMat.GetPtr()));
+ }
+
+ // Normal matrix
+ if (params.mat[kShaderInstanceMatNormalMatrix].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldViewDirty)
+#endif
+ )
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatNormalMatrix];
+
+ // @TBD: remove normalization in fixed function emulation after Normal matrix multiply.
+ Matrix4x4f rotWV;
+ rotWV = STATE.transformState.worldViewMatrix;
+ rotWV.SetPosition(Vector3f::zero); // reset translation
+
+ if (STATE.normalization == kNormalizationScale) // reset scale
+ {
+ float invScale = m_BuiltinParamValues.GetInstanceVectorParam(kShaderInstanceVecScale).w;
+ rotWV.Get (0, 0) *= invScale;
+ rotWV.Get (1, 0) *= invScale;
+ rotWV.Get (2, 0) *= invScale;
+ rotWV.Get (0, 1) *= invScale;
+ rotWV.Get (1, 1) *= invScale;
+ rotWV.Get (2, 1) *= invScale;
+ rotWV.Get (0, 2) *= invScale;
+ rotWV.Get (1, 2) *= invScale;
+ rotWV.Get (2, 2) *= invScale;
+ }
+ Matrix3x3f rotWV33 = Matrix3x3f(rotWV);
+
+ GLES_CHK(glUniformMatrix3fv (matParam.gpuIndex, 1, GL_FALSE, rotWV33.GetPtr()));
+ }
+
+
+ //chai: ÉèÖÃÏòÁ¿uniforms
+ // Set instance vector parameters
+ for (int i = 0; i < kShaderInstanceVecCount; ++i)
+ {
+ int gpuIndexVS = params.vec[i].gpuIndex;
+ if (gpuIndexVS >= 0)
+ {
+ const float* val = m_BuiltinParamValues.GetInstanceVectorParam((ShaderBuiltinInstanceVectorParam)i).GetPtr();
+ switch (params.vec[i].dim) {
+ case 1: CachedUniform1(targetCache, kShaderParamFloat, gpuIndexVS, val); break;
+ case 2: CachedUniform2(targetCache, kShaderParamFloat, gpuIndexVS, val); break;
+ case 3: CachedUniform3(targetCache, kShaderParamFloat, gpuIndexVS, val); break;
+ case 4: CachedUniform4(targetCache, kShaderParamFloat, gpuIndexVS, val); break;
+ }
+ GLESAssert();
+ }
+ }
+
+ // Texture Matrices
+ Matrix4x4f texM;
+ for (int i = 0; i < kMaxSupportedTextureUnitsGLES; ++i)
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatTexture0 + i];
+ if (matParam.gpuIndex >= 0)
+ {
+ if (i < STATE.textureCount)
+ ComputeTextureTransformMatrix(STATE.textures[i], STATE.transformState.worldViewMatrix, STATE.transformState.worldMatrix, texM);
+ else
+ texM.SetIdentity();
+
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, texM.GetPtr()));
+ }
+ }
+#if NV_STATE_FILTERING
+ STATE.transformState.dirtyFlags = 0;
+#endif
+ }
+
+ // Set per-drawcall properties
+ SetValuesFunctorES2 setValuesFunc(*this, targetCache);
+ ApplyMaterialPropertyBlockValuesES(m_MaterialProperties, STATE.activeProgram, STATE.activeProgramParams, setValuesFunc);
+}
+
+bool GFX_GL_IMPL::IsPositionRequiredForTexGen(int unit) const
+{
+ if (unit >= STATE.textureCount)
+ return false;
+ if (STATE.activeProgram)
+ return false;
+
+ //DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexUnits);
+ const TextureUnitStateGLES2& unitState = STATE.textures[unit];
+ return TextureUnitStateGLES2::PositionRequiredForTexGen(unitState.texGen);
+}
+
+bool GFX_GL_IMPL::IsNormalRequiredForTexGen(int unit) const
+{
+ if (unit >= STATE.textureCount)
+ return false;
+ if (STATE.activeProgram)
+ return false;
+
+ //DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexUnits );
+ const TextureUnitStateGLES2& unitState = STATE.textures[unit];
+ return TextureUnitStateGLES2::NormalRequiredForTexGen(unitState.texGen);
+}
+
+
+bool GFX_GL_IMPL::IsPositionRequiredForTexGen() const
+{
+ return ( STATE.positionTexGen != 0 && !STATE.activeProgram );
+}
+
+bool GFX_GL_IMPL::IsNormalRequiredForTexGen() const
+{
+ return ( STATE.normalTexGen != 0 && !STATE.activeProgram );
+}
+
+RenderTextureFormat GFX_GL_IMPL::GetDefaultRTFormat() const
+{
+#if UNITY_PEPPER
+ // desktop platfrom = noone cares
+ return kRTFormatARGB32;
+#else
+
+ RenderTextureFormat defaultFormat = GetCurrentFBColorFormatGLES20();
+ return gGraphicsCaps.supportsRenderTextureFormat[defaultFormat] ? defaultFormat : kRTFormatARGB32;
+
+#endif
+}
+
+RenderTextureFormat GFX_GL_IMPL::GetDefaultHDRRTFormat() const
+{
+#if UNITY_PEPPER
+ // desktop platfrom = noone cares
+ return kRTFormatARGB32;
+#else
+
+ if(gGraphicsCaps.supportsRenderTextureFormat[kRTFormatARGBHalf])
+ return kRTFormatARGBHalf;
+
+ RenderTextureFormat defaultFormat = GetCurrentFBColorFormatGLES20();
+ return gGraphicsCaps.supportsRenderTextureFormat[defaultFormat] ? defaultFormat : kRTFormatARGB32;
+
+#endif
+}
+
+
+void* GFX_GL_IMPL::GetNativeTexturePointer(TextureID id)
+{
+ return (void*)TextureIdMap::QueryNativeTexture(id);
+}
+
+
+
+void GFX_GL_IMPL::ReloadResources()
+{
+ MarkAllVBOsLost();
+ GetDynamicVBO().Recreate();
+
+ GfxDevice::CommonReloadResources(kReleaseRenderTextures | kReloadShaders | kReloadTextures);
+ ClearFixedFunctionPrograms();
+
+ extern void ClearFBMapping();
+ ClearFBMapping();
+ STATE.m_SharedFBO = STATE.m_HelperFBO = 0;
+
+ InvalidateState();
+}
+
+void GFX_GL_IMPL::InitFramebufferDepthFormat()
+{
+ m_FramebufferDepthFormat = QueryFBDepthFormatGLES2();
+}
+
+// Acquire thread ownership on the calling thread. Worker releases ownership.
+void GFX_GL_IMPL::AcquireThreadOwnership()
+{
+#if UNITY_ANDROID
+ AcquireGLES20Context();
+#endif
+}
+
+// Release thread ownership on the calling thread. Worker acquires ownership.
+void GFX_GL_IMPL::ReleaseThreadOwnership()
+{
+#if UNITY_ANDROID
+ ReleaseGLES20Context();
+#endif
+}
+
+// and now the real magic
+void GfxDeviceGLES20_InitFramebufferDepthFormat()
+{
+ ((GFX_GL_IMPL&)GetRealGfxDevice()).InitFramebufferDepthFormat();
+}
+
+// ---------- verify state
+#if GFX_DEVICE_VERIFY_ENABLE
+void GFX_GL_IMPL::VerifyState()
+{
+}
+
+#endif // GFX_DEVICE_VERIFY_ENABLE
+
+#if NV_STATE_FILTERING
+ #ifdef glDeleteBuffers
+ #undef glDeleteBuffers
+ #endif
+ #ifdef glBindBuffer
+ #undef glBindBuffer
+ #endif
+ #ifdef glVertexAttribPointer
+ #undef glVertexAttribPointer
+ #endif
+#endif
+
+#endif // GFX_SUPPORTS_OPENGLES20
diff --git a/Runtime/GfxDevice/opengles20/GfxDeviceGLES20.h b/Runtime/GfxDevice/opengles20/GfxDeviceGLES20.h
new file mode 100644
index 0000000..265477e
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/GfxDeviceGLES20.h
@@ -0,0 +1,190 @@
+#pragma once
+
+#if GFX_DEVICE_VIRTUAL
+
+#ifdef GFX_GL_IMPL
+#undef GFX_GL_IMPL
+#endif
+#define GFX_GL_IMPL GfxDeviceGLES20
+
+class GFX_GL_IMPL : public GfxThreadableDevice {
+public:
+ GFX_GL_IMPL();
+ GFX_API ~GFX_GL_IMPL();
+
+ GFX_API void InvalidateState();
+ #if GFX_DEVICE_VERIFY_ENABLE
+ GFX_API void VerifyState();
+ #endif
+
+ GFX_API void Clear (UInt32 clearFlags, const float color[4], float depth, int stencil);
+ GFX_API void SetUserBackfaceMode( bool enable );
+ GFX_API void SetWireframe(bool wire) { } // not possible in GLES
+ GFX_API bool GetWireframe() const { return false; } // not possible in GLES
+ GFX_API void SetInvertProjectionMatrix( bool enable );
+ GFX_API bool GetInvertProjectionMatrix() const;
+
+ GFX_API void SetWorldMatrix( const float matrix[16] );
+ GFX_API void SetViewMatrix( const float matrix[16] );
+
+ GFX_API void SetProjectionMatrix (const Matrix4x4f& matrix);
+ GFX_API void GetMatrix( float outMatrix[16] ) const;
+
+ GFX_API GPUSkinningInfo *CreateGPUSkinningInfo() { return NULL; }
+ GFX_API void DeleteGPUSkinningInfo(GPUSkinningInfo *info) { AssertBreak(false); }
+ GFX_API void SkinOnGPU( GPUSkinningInfo * info, bool lastThisFrame ) { AssertBreak(false); }
+ GFX_API void UpdateSkinSourceData(GPUSkinningInfo *info, const void *vertData, const BoneInfluence *skinData, bool dirty) { AssertBreak(false); }
+ GFX_API void UpdateSkinBonePoses(GPUSkinningInfo *info, const int boneCount, const Matrix4x4f* poses) { AssertBreak(false); }
+
+ GFX_API const float* GetWorldMatrix() const ;
+ GFX_API const float* GetViewMatrix() const;
+ GFX_API const float* GetProjectionMatrix() const;
+ GFX_API const float* GetDeviceProjectionMatrix() const;
+
+ GFX_API void SetNormalizationBackface( NormalizationMode mode, bool backface );
+ GFX_API void SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial );
+ GFX_API void SetMaterial( const float ambient[4], const float diffuse[4], const float specular[4], const float emissive[4], const float shininess );
+ GFX_API void SetColor( const float color[4] );
+ GFX_API void SetViewport( int x, int y, int width, int height );
+ GFX_API void GetViewport( int* values ) const;
+
+ GFX_API void SetScissorRect( int x, int y, int width, int height );
+ GFX_API void DisableScissor();
+ GFX_API bool IsScissorEnabled() const;
+ GFX_API void GetScissorRect( int values[4] ) const;
+ GFX_API void SetSRGBWrite (const bool);
+ GFX_API bool GetSRGBWrite ();
+
+ GFX_API TextureCombinersHandle CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular );
+ GFX_API void DeleteTextureCombiners( TextureCombinersHandle& textureCombiners );
+ GFX_API void SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props );
+
+ GFX_API void SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias);
+ GFX_API void SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace );
+ GFX_API void SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16]);
+ GFX_API void SetTextureName ( TextureID texture, const char* name ) { }
+
+ GFX_API void SetShadersThreadable (GpuProgram* programs[kShaderTypeCount], const GpuProgramParameters* params[kShaderTypeCount], UInt8 const * const paramsBuffer[kShaderTypeCount]);
+ GFX_API void CreateShaderParameters( ShaderLab::SubProgram* program, FogMode fogMode );
+
+ GFX_API bool IsCombineModeSupported( unsigned int combiner );
+ GFX_API void SetTextureCombinersThreadable( TextureCombinersHandle textureCombiners, const TexEnvData* texEnvData, const Vector4f* texColors );
+
+ GFX_API bool IsShaderActive( ShaderType type ) const;
+ GFX_API void DestroySubProgram( ShaderLab::SubProgram* subprogram );
+
+ GFX_API void DisableLights( int startLight );
+ GFX_API void SetLight( int light, const GfxVertexLight& data);
+ GFX_API void SetAmbient( const float ambient[4] );
+
+ GFX_API void EnableFog (const GfxFogParams& fog);
+ GFX_API void DisableFog();
+
+ GFX_API VBO* CreateVBO();
+ GFX_API void DeleteVBO( VBO* vbo );
+ GFX_API DynamicVBO& GetDynamicVBO();
+
+ GFX_API RenderSurfaceHandle CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags);
+ GFX_API RenderSurfaceHandle CreateRenderDepthSurface (TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags);
+ GFX_API void DestroyRenderSurface (RenderSurfaceHandle& rs);
+ GFX_API void SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face = kCubeFaceUnknown);
+ GFX_API void ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle);
+ GFX_API RenderSurfaceHandle GetActiveRenderColorSurface (int index);
+ GFX_API RenderSurfaceHandle GetActiveRenderDepthSurface ();
+ GFX_API void SetSurfaceFlags(RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags);
+ GFX_API void DiscardContents (RenderSurfaceHandle& rs);
+
+ GFX_API void UploadTexture2D( TextureID texture, TextureDimension dimension, UInt8* srcData, int srcSize, int width, int height, TextureFormat format, int mipCount, UInt32 uploadFlags, int skipMipLevels, TextureUsageMode usageMode, TextureColorSpace colorSpace );
+ GFX_API void UploadTextureSubData2D( TextureID texture, UInt8* srcData, int srcSize, int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace );
+ GFX_API void UploadTextureCube( TextureID texture, UInt8* srcData, int srcSize, int faceDataSize, int size, TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace );
+ GFX_API void UploadTexture3D( TextureID texture, UInt8* srcData, int srcSize, int width, int height, int depth, TextureFormat format, int mipCount, UInt32 uploadFlags );
+ GFX_API void DeleteTexture( TextureID texture );
+
+ GFX_API PresentMode GetPresentMode();
+
+ GFX_API void BeginFrame();
+ GFX_API void EndFrame();
+ GFX_API void PresentFrame();
+
+ GFX_API bool IsValidState();
+ GFX_API bool HandleInvalidState();
+
+ GFX_API void FinishRendering();
+
+ // Immediate mode rendering
+ GFX_API void ImmediateVertex( float x, float y, float z );
+ GFX_API void ImmediateNormal( float x, float y, float z );
+ GFX_API void ImmediateColor( float r, float g, float b, float a );
+ GFX_API void ImmediateTexCoordAll( float x, float y, float z );
+ GFX_API void ImmediateTexCoord( int unit, float x, float y, float z );
+ GFX_API void ImmediateBegin( GfxPrimitiveType type );
+ GFX_API void ImmediateEnd();
+
+ GFX_API void AcquireThreadOwnership();
+ GFX_API void ReleaseThreadOwnership();
+
+ protected:
+
+
+ GFX_API bool CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 );
+ GFX_API bool ReadbackImage( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY );
+ GFX_API void GrabIntoRenderTexture( RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height );
+
+ GFX_API void BeforeDrawCall( bool immediateMode);
+
+ GFX_API void SetBlendState(const DeviceBlendState* state, float alphaRef);
+ GFX_API void SetRasterState(const DeviceRasterState* state);
+ GFX_API void SetDepthState(const DeviceDepthState* state);
+ GFX_API void SetStencilState(const DeviceStencilState* state, int stencilRef);
+
+ GFX_API DeviceBlendState* CreateBlendState(const GfxBlendState& state);
+ GFX_API DeviceDepthState* CreateDepthState(const GfxDepthState& state);
+ GFX_API DeviceStencilState* CreateStencilState(const GfxStencilState& state);
+ GFX_API DeviceRasterState* CreateRasterState(const GfxRasterState& state);
+
+#if ENABLE_PROFILER
+ GFX_API void BeginProfileEvent (const char* name);
+ GFX_API void EndProfileEvent ();
+
+ GFX_API GfxTimerQuery* CreateTimerQuery();
+ GFX_API void DeleteTimerQuery(GfxTimerQuery* query);
+ GFX_API void BeginTimerQueries();
+ GFX_API void EndTimerQueries();
+#endif
+
+public:
+ // OpenGLES specific
+ GFX_API void ReloadResources();
+ GFX_API bool IsPositionRequiredForTexGen(int texStageIndex) const;
+ GFX_API bool IsNormalRequiredForTexGen(int texStageIndex) const;
+
+ GFX_API bool IsPositionRequiredForTexGen() const;
+ GFX_API bool IsNormalRequiredForTexGen() const;
+
+ GFX_API RenderTextureFormat GetDefaultRTFormat() const;
+ GFX_API RenderTextureFormat GetDefaultHDRRTFormat() const;
+
+ GFX_API void* GetNativeTexturePointer(TextureID id);
+
+ DeviceStateGLES20& GetState() { return state; }
+
+ void InitFramebufferDepthFormat();
+
+private:
+ DeviceStateGLES20 state;
+};
+
+#define STATE this->state
+#define GetGLES20DeviceState(device) device.GetState()
+
+#else // GFX_DEVICE_VIRTUAL
+
+
+struct GfxDeviceImpl {
+ DeviceStateGLES20 state;
+};
+
+#define STATE impl->state
+#define GetGLES20DeviceState(device) device.GetImpl()->state
+
+#endif // GFX_DEVICE_VIRTUAL
diff --git a/Runtime/GfxDevice/opengles20/GpuProgramsGLES20.cpp b/Runtime/GfxDevice/opengles20/GpuProgramsGLES20.cpp
new file mode 100644
index 0000000..e28fbde
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/GpuProgramsGLES20.cpp
@@ -0,0 +1,1120 @@
+#include "UnityPrefix.h"
+#if GFX_SUPPORTS_OPENGLES20
+#include "GpuProgramsGLES20.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/File/ApplicationSpecificPersistentDataPath.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "External/shaderlab/Library/ShaderLabErrors.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/shaderlab/Library/program.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Matrix3x3.h"
+#include "Runtime/Math/Vector4.h"
+
+
+#include "IncludesGLES20.h"
+#include "AssertGLES20.h"
+#include "GpuPropertiesGLES20.h"
+#include "Runtime/Utilities/GLSLUtilities.h"
+#include "VBOGLES20.h"
+#include "DebugGLES20.h"
+#include "UnityGLES20Ext.h"
+
+#if UNITY_ANDROID
+ #include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+ #include "PlatformDependent/AndroidPlayer/AndroidSystemInfo.h"
+#endif
+
+#if UNITY_BLACKBERRY
+ #include "ctype.h"
+#endif
+
+
+#include <stdio.h>
+
+#define DEBUG_GLSL_BINDINGS 0
+#define DEBUG_SHADER_CACHE 0
+
+
+// from shader_yacc.hpp
+extern std::string g_LastParsedShaderName;
+
+void GLSLUseProgramGLES20 (UInt32 programID); // defined in GfxDeviceGLES20.cpp
+
+bool CompileGLSLVertexShader (const std::string& source, ChannelAssigns& channels, GLShaderID programID, GLShaderID parentProgramID, GLShaderID* outShaderID);
+bool CompileGLSLFragmentShader (const std::string& source, GLShaderID* outShaderID);
+bool BindVProgAttrbutes(const std::string& source, ChannelAssigns& channels, GLShaderID programID);
+bool RebindVProgAttrbutes(GLShaderID programID, GLShaderID parentProgramID);
+
+
+static void GetCachedBinaryName(const std::string& vprog, const std::string& fshader, char filename[33]);
+
+
+// --------------------------------------------------------------------------
+// GLSL
+
+// AttributeConversionTable
+const static UInt32 kAttribLookupTableSize = 12;
+
+const static char* s_GLSLESAttributes[kAttribLookupTableSize] = {
+ "_glesVertex", "_glesColor", "_glesNormal",
+ "_gles_unused__", // unused
+ "_glesMultiTexCoord0", "_glesMultiTexCoord1", "_glesMultiTexCoord2", "_glesMultiTexCoord3",
+ "_glesMultiTexCoord4", "_glesMultiTexCoord5", "_glesMultiTexCoord6", "_glesMultiTexCoord7"
+};
+const static char* s_UnityAttributes[kAttribLookupTableSize] = {
+ "Vertex", "Color", "Normal",
+ "", // unused
+ "TexCoord", "TexCoord1", "TexCoord2", "TexCoord3",
+ "TexCoord4", "TexCoord5", "TexCoord6", "TexCoord7"
+};
+
+
+const VertexComponent s_UnityVertexComponents[kAttribLookupTableSize] = {
+ kVertexCompVertex,
+ kVertexCompColor,
+ kVertexCompNormal,
+ kVertexCompTexCoord,
+ kVertexCompTexCoord0, kVertexCompTexCoord1, kVertexCompTexCoord2, kVertexCompTexCoord3,
+ kVertexCompTexCoord4, kVertexCompTexCoord5, kVertexCompTexCoord6, kVertexCompTexCoord7
+};
+
+const GLuint s_GLESVertexComponents[kAttribLookupTableSize] = {
+ GL_VERTEX_ARRAY,
+ GL_COLOR_ARRAY,
+ GL_NORMAL_ARRAY,
+ ~0 /*kVertexCompTexCoord*/,
+ GL_TEXTURE_ARRAY0, GL_TEXTURE_ARRAY1, GL_TEXTURE_ARRAY2, GL_TEXTURE_ARRAY3,
+ GL_TEXTURE_ARRAY4, GL_TEXTURE_ARRAY5, GL_TEXTURE_ARRAY6, GL_TEXTURE_ARRAY7
+};
+
+GlslGpuProgramGLES20::GlslGpuProgramGLES20 (const std::string& source, CreateGpuProgramOutput& output)
+{
+ output.SetPerFogModeParamsEnabled(true);
+ m_ImplType = kShaderImplBoth;
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ m_GLSLVertexShader[i] = 0;
+ m_GLSLFragmentShader[i] = 0;
+ m_FogColorIndex[i] = -1;
+ m_FogParamsIndex[i] = -1;
+ m_FogFailed[i] = false;
+ }
+
+ // Fragment shaders come out as dummy GLSL text. Just ignore them; the real shader was part of
+ // the vertex shader text anyway.
+ if (source.empty())
+ return;
+
+ if (Create (source, output.CreateChannelAssigns()))
+ {
+ GpuProgramParameters& params = output.CreateParams();
+ FillParams (m_Programs[kFogDisabled], params, output.GetOutNames());
+ if (params.GetTextureParams().size() > gGraphicsCaps.maxTexImageUnits)
+ m_NotSupported = true;
+
+ m_UniformCache[kFogDisabled].Create(&params, -1, -1);
+ }
+ else
+ {
+ m_NotSupported = true;
+ }
+}
+
+GlslGpuProgramGLES20::~GlslGpuProgramGLES20 ()
+{
+ Assert (m_ImplType == kShaderImplBoth);
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ if (m_GLSLVertexShader[i]) { GLES_CHK(glDeleteShader(m_GLSLVertexShader[i])); }
+ if (m_GLSLFragmentShader[i]) { GLES_CHK(glDeleteShader(m_GLSLFragmentShader[i])); }
+ if (m_Programs[i]) { GLES_CHK(glDeleteProgram(m_Programs[i])); }
+ m_UniformCache[i].Destroy();
+ }
+}
+
+static bool ParseGlslErrors (GLuint type, GLSLErrorType errorType, const char* source = 0)
+{
+ bool hadErrors = false;
+ char compileInfoLog[4096];
+ GLsizei infoLogLength = 0;
+ switch(errorType)
+ {
+ case kErrorCompileVertexShader:
+ case kErrorCompileFragShader:
+ glGetShaderInfoLog(type, 4096, &infoLogLength, compileInfoLog);
+ break;
+ case kErrorLinkProgram:
+ glGetProgramInfoLog(type, 4096, &infoLogLength, compileInfoLog);
+ break;
+ default:
+ FatalErrorMsg("Unknown error type");
+ break;
+ }
+
+ if(infoLogLength)
+ {
+ hadErrors = true;
+ if(source)
+ {
+ ErrorStringMsg("-------- failed compiling %s:\n", errorType==kErrorCompileVertexShader?"vertex program":"fragment shader");
+ DebugTextLineByLine(source);
+ }
+
+ ErrorStringMsg("-------- GLSL error: %s\n\n", compileInfoLog);
+ }
+
+ return hadErrors;
+}
+
+static std::string ExtractDefineBock(const std::string& defineName, const std::string& str, std::string* remainderStr)
+{
+ const std::string beginsWith = "#ifdef " + defineName;
+ const std::string endsWith = "#endif";
+
+ size_t b;
+ if ((b = str.find(beginsWith))==std::string::npos)
+ return "";
+
+ b += beginsWith.size();
+
+ size_t e = b;
+ size_t n = 1;
+ do
+ {
+ size_t nextEnd = str.find(endsWith, e);
+ size_t nextIf = str.find("#if", e);
+
+ if (nextEnd == std::string::npos)
+ return "";
+
+ if (nextIf != std::string::npos && nextIf < nextEnd)
+ {
+ ++n;
+ e = nextIf + 1;
+ }
+ else
+ {
+ --n;
+ e = nextEnd + 1;
+ }
+ }
+ while (n > 0);
+
+ std::string retVal = str.substr(b, e-b-1);
+ if (remainderStr)
+ {
+ *remainderStr = str.substr(0, b - beginsWith.size());
+ if (e + endsWith.size() < str.length()) *remainderStr += str.substr(e + endsWith.size());
+ }
+
+ return retVal;
+}
+
+static void FindProgramStart(const char* source, std::string* header, std::string* prog)
+{
+ const char* line_start = source;
+ while(*line_start)
+ {
+ while(isspace(*line_start))
+ ++line_start;
+ if(*line_start == '#')
+ {
+ while(*line_start != '\n' && *line_start != '\r')
+ ++line_start;
+ }
+ else
+ {
+ header->assign(source, line_start - source);
+ prog->assign(line_start);
+ break;
+ }
+ }
+}
+
+bool CompileGlslShader(GLShaderID shader, GLSLErrorType type, const char* source)
+{
+ GLES_CHK(glShaderSource(shader, 1, &source, NULL));
+ GLES_CHK(glCompileShader(shader));
+
+ int compiled = 10;
+ GLES_CHK(glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled));
+
+ if (compiled==0)
+ {
+ ParseGlslErrors(shader, type, source);
+ return false;
+ }
+
+ return true;
+}
+
+bool GlslGpuProgramGLES20::Create (const std::string &shaderString, ChannelAssigns& channels)
+{
+ if( !gGraphicsCaps.gles20.hasGLSL )
+ return false;
+
+ GLESAssert(); // Clear any GL errors
+
+ GLES_CHK(m_Programs[0] = glCreateProgram());
+
+ m_ImplType = kShaderImplBoth;
+
+ // NOTE: pre-pending shader with VERTEX/FRAGMENT defines doesn't work with ES/GLSL compilers for some reason
+ // therefore we extract VERTEX/FRAGMENT sections from the shaderString
+
+ std::string remainder = shaderString;
+ std::string vertexShaderSource = ExtractDefineBock("VERTEX", shaderString, &remainder);
+ std::string fragmentShaderSource = ExtractDefineBock("FRAGMENT", remainder, &remainder);
+
+ vertexShaderSource = remainder + vertexShaderSource;
+ fragmentShaderSource = remainder + fragmentShaderSource;
+
+ if(!CompileProgram(0, vertexShaderSource, fragmentShaderSource, channels))
+ {
+ ParseGlslErrors(m_Programs[0], kErrorLinkProgram);
+
+ // TODO: cleanup
+ return false;
+ }
+
+ m_VertexShaderSourceForFog = vertexShaderSource;
+ m_SourceForFog = fragmentShaderSource;
+
+ return true;
+}
+
+std::string GlslGpuProgramGLES20::_CachePath;
+
+bool GlslGpuProgramGLES20::InitBinaryShadersSupport()
+{
+#if UNITY_ANDROID
+ // the implementation is broken on pre-HC on pvr at least, but we'd rather play safe with this dino stuff
+ if(android::systeminfo::ApiLevel() < android::apiHoneycomb)
+ return false;
+
+ // Huawei is osam in general
+ if(gGraphicsCaps.rendererString.find("Immersion") != std::string::npos)
+ return false;
+#endif
+
+ if(QueryExtension("GL_OES_get_program_binary") && gGlesExtFunc.glGetProgramBinaryOES && gGlesExtFunc.glProgramBinaryOES)
+ {
+ int binFormatCount = 0;
+ GLES_CHK(glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS_OES, &binFormatCount));
+
+ if(binFormatCount > 0)
+ {
+ _CachePath = GetTemporaryCachePathApplicationSpecific() + "/UnityShaderCache/";
+ if(!IsDirectoryCreated(_CachePath))
+ CreateDirectory(_CachePath);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Internal_ClearShaderCache()
+{
+ std::string shaderCache = GetTemporaryCachePathApplicationSpecific() + "/UnityShaderCache/";
+ DeleteFileOrDirectory(shaderCache);
+ CreateDirectory(shaderCache);
+}
+
+bool GlslGpuProgramGLES20::CompileProgram(unsigned index, const std::string& vprog, const std::string& fshader, ChannelAssigns& channels)
+{
+ std::string binaryPath;
+ if(gGraphicsCaps.gles20.hasBinaryShaders)
+ {
+ char filename[33] = {0};
+ GetCachedBinaryName(vprog, fshader, filename);
+
+ binaryPath = _CachePath + filename;
+ #if DEBUG_SHADER_CACHE
+ ::printf_console("Starting compilation \"%s\" variation with md5:%s\n", g_LastParsedShaderName.c_str(), filename);
+ #endif
+ }
+
+ // first 4 bytes are for binary format
+ char* binaryData = 0;
+ char* binaryProgram = 0;
+ unsigned binaryLength = 0;
+
+ bool loadedBinary = false;
+ if(gGraphicsCaps.gles20.hasBinaryShaders)
+ {
+ FILE* binaryFile = ::fopen(binaryPath.c_str(), "rb");
+ if(binaryFile)
+ {
+ ::fseek(binaryFile, 0, SEEK_END);
+ unsigned datasz = (unsigned)::ftell(binaryFile);
+ ::fseek(binaryFile, 0, SEEK_SET);
+
+ binaryData = (char*)::malloc(datasz);
+ binaryProgram = binaryData + sizeof(GLenum);
+ binaryLength = datasz - sizeof(GLenum);
+ ::fread(binaryData, datasz, 1, binaryFile);
+ ::fclose(binaryFile);
+
+ loadedBinary = true;
+ #if DEBUG_SHADER_CACHE
+ ::printf_console("Loaded from cache");
+ #endif
+ }
+ }
+
+ if(loadedBinary)
+ {
+ loadedBinary = false;
+
+ bool attrBound = index==0 ? BindVProgAttrbutes(vprog, channels, m_Programs[0])
+ : RebindVProgAttrbutes(m_Programs[index], m_Programs[0]);
+ if(attrBound)
+ {
+ GLES_CHK(gGlesExtFunc.glProgramBinaryOES(m_Programs[index], *((GLenum*)binaryData), binaryProgram, binaryLength));
+
+ int linked = 0;
+ GLES_CHK(glGetProgramiv(m_Programs[index], GL_LINK_STATUS, &linked));
+ loadedBinary = linked != 0;
+ }
+
+ if(!loadedBinary)
+ {
+ #if DEBUG_SHADER_CACHE
+ ::printf_console("Bad cached version\n");
+ #endif
+
+ ::free(binaryData);
+ binaryProgram = binaryData = 0;
+ }
+ }
+
+ // fallback to compiling shaders at runtime
+ if(!loadedBinary)
+ {
+ DBG_SHADER_VERBOSE_GLES20("Compiling shader: %s\n", g_LastParsedShaderName.c_str());
+ #if DEBUG_SHADER_CACHE
+ ::printf_console("Actually compiling");
+ #endif
+
+ if(!CompileGLSLVertexShader(vprog, channels, m_Programs[index], m_Programs[0], &m_GLSLVertexShader[index]))
+ return false;
+ if(!CompileGLSLFragmentShader(fshader, &m_GLSLFragmentShader[index]))
+ return false;
+
+ GLES_CHK(glAttachShader(m_Programs[index], m_GLSLVertexShader[index]));
+ GLES_CHK(glAttachShader(m_Programs[index], m_GLSLFragmentShader[index]));
+ GLES_CHK(glLinkProgram(m_Programs[index]));
+
+ int linked = 0;
+ GLES_CHK(glGetProgramiv(m_Programs[index], GL_LINK_STATUS, &linked));
+ if(linked == 0)
+ return false;
+
+ if(gGraphicsCaps.gles20.hasBinaryShaders)
+ {
+ Assert(binaryData == 0 && binaryProgram == 0);
+
+ GLES_CHK(glGetProgramiv(m_Programs[index], GL_PROGRAM_BINARY_LENGTH_OES, (GLint*)&binaryLength));
+ binaryData = (char*)::malloc(binaryLength + sizeof(GLenum));
+ binaryProgram = binaryData + sizeof(GLenum);
+ GLES_CHK(gGlesExtFunc.glGetProgramBinaryOES(m_Programs[index], binaryLength, 0, (GLenum*)binaryData, binaryProgram));
+
+ FILE* binaryFile = ::fopen(binaryPath.c_str(), "wb");
+ if(binaryFile)
+ {
+ ::fwrite(binaryData, binaryLength + sizeof(GLenum), 1, binaryFile);
+ ::fclose(binaryFile);
+
+ #if DEBUG_SHADER_CACHE
+ ::printf_console("Saved to cache\n");
+ #endif
+ }
+ }
+ }
+
+ if(binaryData)
+ ::free(binaryData);
+
+ return true;
+}
+
+
+
+static void PrintNumber (char* s, int i, bool brackets)
+{
+ DebugAssert (i >= 0 && i < 100);
+ if (brackets)
+ *s++ = '[';
+
+ if (i < 10) {
+ *s++ = '0' + i;
+ } else {
+ *s++ = '0' + i/10;
+ *s++ = '0' + i%10;
+ }
+
+ if (brackets)
+ *s++ = ']';
+ *s++ = 0;
+}
+
+static void AddSizedVectorParam (GpuProgramParameters& params, ShaderParamType type, GLuint program, int vectorSize, int uniformNumber, int arraySize, const char* unityName, char* glName, int glNameIndexOffset, PropertyNamesSet* outNames)
+{
+ if (arraySize <= 1)
+ {
+ params.AddVectorParam (uniformNumber, type, vectorSize, unityName, -1, outNames);
+ }
+ else
+ {
+ for (int j = 0; j < arraySize; ++j)
+ {
+ PrintNumber (glName+glNameIndexOffset, j, true);
+ uniformNumber = glGetUniformLocation (program, glName);
+ PrintNumber (glName+glNameIndexOffset, j, false);
+ params.AddVectorParam (uniformNumber, type, vectorSize, glName, -1, outNames);
+ }
+ }
+}
+
+static void AddSizedMatrixParam (GpuProgramParameters& params, GLuint program, int rows, int cols, int uniformNumber, int arraySize, const char* unityName, char* glName, int glNameIndexOffset, PropertyNamesSet* outNames)
+{
+ if (arraySize <= 1)
+ {
+ params.AddMatrixParam (uniformNumber, unityName, rows, cols, -1, outNames);
+ }
+ else
+ {
+ for (int j = 0; j < arraySize; ++j)
+ {
+ PrintNumber (glName+glNameIndexOffset, j, true);
+ uniformNumber = glGetUniformLocation (program, glName);
+ PrintNumber (glName+glNameIndexOffset, j, false);
+ params.AddMatrixParam (uniformNumber, glName, rows, cols, -1, outNames);
+ }
+ }
+}
+
+
+void GlslGpuProgramGLES20::FillParams (unsigned int programID, GpuProgramParameters& params, PropertyNamesSet* outNames)
+{
+ if (!programID)
+ return;
+
+ int activeUniforms;
+
+ char name[1024];
+ GLenum type;
+ int arraySize = 0,
+ nameLength = 0,
+ bufSize = sizeof(name);
+
+ DBG_LOG_GLES20("GLSL: apply params to program id=%i\n", programID);
+ GLSLUseProgramGLES20 (programID);
+
+ // Figure out the uniforms
+ GLES_CHK(glGetProgramiv (programID, GL_ACTIVE_UNIFORMS, &activeUniforms));
+ for(int i=0; i < activeUniforms; i++)
+ {
+ //chai: »ñµÃUniform±äÁ¿Ãû
+ GLES_CHK(glGetActiveUniform (programID, i, bufSize, &nameLength, &arraySize, &type, name));
+
+ if (!strcmp (name, "_unity_FogParams") || !strcmp(name, "_unity_FogColor"))
+ continue;
+
+ // some Unity builtin properties are mapped to GLSL structure fields
+ // hijack them here
+ const char* glslName = GetGLSLESPropertyNameRemap(name);
+ const char* unityName = glslName ? glslName : name;
+
+ if (!strncmp (name, "gl_", 3)) // skip "gl_" names
+ continue;
+
+ int uniformNumber = glGetUniformLocation (programID, name);
+ Assert(uniformNumber != -1);
+
+ char* glName = name;
+ int glNameIndexOffset = 0;
+
+ bool isElemZero = false;
+ bool isArray = IsShaderParameterArray(name, nameLength, arraySize, &isElemZero);
+ if (isArray)
+ {
+ // for array parameters, transform name a bit: Foo[0] becomes Foo0
+ if (arraySize >= 100) {
+ ErrorString( "GLSL: array sizes larger than 99 not supported" ); // TODO: SL error
+ arraySize = 99;
+ }
+ // TODO: wrong? what if we use only array[1] for example
+ if (isElemZero)
+ {
+ glNameIndexOffset = nameLength-3;
+ glName[glNameIndexOffset] = '0';
+ glName[glNameIndexOffset+1] = 0;
+ }
+ else
+ {
+ glNameIndexOffset = nameLength;
+ }
+ }
+
+ if (type == GL_FLOAT)
+ AddSizedVectorParam (params, kShaderParamFloat, programID, 1, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_FLOAT_VEC2)
+ AddSizedVectorParam (params, kShaderParamFloat, programID, 2, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_FLOAT_VEC3)
+ AddSizedVectorParam (params, kShaderParamFloat, programID, 3, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_FLOAT_VEC4)
+ AddSizedVectorParam (params, kShaderParamFloat, programID, 4, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_INT)
+ AddSizedVectorParam (params, kShaderParamInt, programID, 1, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_INT_VEC2)
+ AddSizedVectorParam (params, kShaderParamInt, programID, 2, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_INT_VEC3)
+ AddSizedVectorParam (params, kShaderParamInt, programID, 3, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_INT_VEC4)
+ AddSizedVectorParam (params, kShaderParamInt, programID, 4, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_BOOL)
+ AddSizedVectorParam (params, kShaderParamBool, programID, 1, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_BOOL_VEC2)
+ AddSizedVectorParam (params, kShaderParamBool, programID, 2, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_BOOL_VEC3)
+ AddSizedVectorParam (params, kShaderParamBool, programID, 3, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_BOOL_VEC4)
+ AddSizedVectorParam (params, kShaderParamBool, programID, 4, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_FLOAT_MAT4)
+ AddSizedMatrixParam (params, programID, 4, 4, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_FLOAT_MAT3)
+ AddSizedMatrixParam (params, programID, 3, 3, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+
+ else if (type == GL_SAMPLER_2D || type == GL_SAMPLER_2D_SHADOW_EXT) {
+ const int texIndex = params.GetTextureParams().size(); // statically bind this uniform to sequential texture index
+ GLES_CHK(glUniform1i (uniformNumber, texIndex));
+ params.AddTextureParam (texIndex, -1, unityName, kTexDim2D, outNames);
+ }
+ else if (type == GL_SAMPLER_CUBE) {
+ const int texIndex = params.GetTextureParams().size(); // statically bind this uniform to sequential texture index
+ GLES_CHK(glUniform1i (uniformNumber, texIndex));
+ params.AddTextureParam (texIndex, -1, unityName, kTexDimCUBE, outNames);
+ }
+ /*
+ else if(type == GL_SAMPLER_3D) {
+ GLES_CHK(glUniform1i (uniformNumber, params.GetTextureParams().size()));
+ params.AddTextureParam( name, kTexDim3D, uniformNumber );
+ }
+ else if(type == GL_SAMPLER_2D_SHADOW) {
+ GLES_CHK(glUniform1i (uniformNumber, params.GetTextureParams().size()));
+ params.AddTextureParam( name, kTexDim2D, uniformNumber );
+ }
+
+ */
+ else {
+ AssertString( "Unrecognized GLSL uniform type" );
+ }
+ }
+
+ GLESAssert();
+}
+
+bool RebindVProgAttrbutes(GLShaderID programID, GLShaderID parentProgramID)
+{
+ int attribCount = 0;
+ GLES_CHK(glGetProgramiv(parentProgramID, GL_ACTIVE_ATTRIBUTES, &attribCount));
+
+ const int kBufSize = 256;
+ char name[kBufSize];
+ for(int i = 0 ; i < attribCount ; ++i)
+ {
+ int nameLength = 0, arraySize = 0;
+ GLenum type;
+ GLES_CHK(glGetActiveAttrib (parentProgramID, i, kBufSize, &nameLength, &arraySize, &type, name));
+ int location = glGetAttribLocation (parentProgramID, name);
+ if (location != -1)
+ GLES_CHK(glBindAttribLocation(programID, location, name));
+ }
+
+ return true;
+}
+
+bool BindVProgAttrbutes(const std::string& source, ChannelAssigns& channels, GLShaderID programID)
+{
+ // Add necessary attribute tags
+ for (UInt32 j = 0; j < kAttribLookupTableSize; j++)
+ {
+ if (source.find(s_GLSLESAttributes[j]) != std::string::npos)
+ {
+ if (s_GLESVertexComponents[j] >= gGraphicsCaps.gles20.maxAttributes)
+ {
+ ErrorString("Shader uses too many vertex attributes for this platform");
+ return false;
+ }
+ GLES_CHK(glBindAttribLocation(programID, s_GLESVertexComponents[j], s_GLSLESAttributes[j]));
+ ShaderChannel shaderChannel = GetShaderChannelFromName(s_UnityAttributes[j]);
+ if( shaderChannel != kShaderChannelNone )
+ channels.Bind (shaderChannel, s_UnityVertexComponents[j]);
+ }
+ }
+
+ // UGLY HACK:
+ // somewhere deep inside shader generation we just put attribute TANGENT
+ // without ever using it after
+ // look for TANGENT twice to be sure we use it
+ size_t firstTangentUsage = source.find("TANGENT");
+ if( firstTangentUsage != std::string::npos && source.find("TANGENT", firstTangentUsage+1) != std::string::npos )
+ {
+ // Find first free slot for tangents and use it.
+ // Unity normally supports 2 UV slots (0&1), so start looking from
+ // kVertexTexCoord2
+ for (int i = kVertexCompTexCoord2; i < kVertexCompTexCoord7; i++)
+ {
+ if (channels.GetSourceForTarget((VertexComponent)i) == kShaderChannelNone)
+ {
+ channels.Bind (kShaderChannelTangent, (VertexComponent)i);
+ GLES_CHK(glBindAttribLocation(programID, GL_TEXTURE_ARRAY0 + i - kVertexCompTexCoord0, "_glesTANGENT"));
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+bool CompileGLSLVertexShader (const std::string& source, ChannelAssigns& channels, GLShaderID programID, GLShaderID parentProgramID, GLShaderID* outShaderID)
+{
+ GLES_CHK(*outShaderID = glCreateShader(GL_VERTEX_SHADER));
+
+ if(parentProgramID == programID)
+ BindVProgAttrbutes(source, channels, programID);
+ else
+ RebindVProgAttrbutes(programID, parentProgramID);
+
+
+#if UNITY_ANDROID
+ if(gGraphicsCaps.gles20.buggyVprogTextures)
+ {
+ if( source.find("texture2D") != std::string::npos || source.find("tex2D") != std::string::npos )
+ {
+ ErrorString("GLES20: Running on platform with buggy vprog textures.\n");
+ ErrorString("GLES20: Compiling this shader may result in crash.\n");
+ ErrorString("GLES20: Shader in question:\n");
+ DebugTextLineByLine(source.c_str());
+ ErrorString("\n---------------\n");
+ }
+ }
+#endif
+
+ const char* text = source.c_str();
+ DBG_SHADER_VERBOSE_GLES20_DUMP_SHADER("Compiling VERTEX program:", text);
+
+ return CompileGlslShader(*outShaderID, kErrorCompileVertexShader, text);
+}
+
+
+bool CompileGLSLFragmentShader (const std::string& source, GLShaderID* outShaderID)
+{
+ *outShaderID = glCreateShader(GL_FRAGMENT_SHADER);
+
+ ///@TODO: find any existing precision statement
+
+ std::string modSourceHeader, modSourceProg;
+ FindProgramStart(source.c_str(), &modSourceHeader, &modSourceProg);
+
+ std::string prec_modifier = gGraphicsCaps.gles20.forceHighpFSPrec ? "precision highp float;\n" : "precision mediump float;\n";
+
+ std::string modSource = modSourceHeader + prec_modifier + modSourceProg;
+
+ const char* text = modSource.c_str();
+ DBG_SHADER_VERBOSE_GLES20_DUMP_SHADER("Compiling FRAGMENT program:", text);
+
+ return CompileGlslShader(*outShaderID, kErrorCompileFragShader, text);
+}
+
+int GlslGpuProgramGLES20::GetGLProgram (FogMode fog, GpuProgramParameters& outParams, ChannelAssigns &channels)
+{
+ int index = 0;
+ if (fog > kFogDisabled && !m_FogFailed[fog] && !m_SourceForFog.empty())
+ {
+ index = fog;
+ Assert (index >= 0 && index < kFogModeCount);
+
+ // create patched fog program if needed
+ if(!m_Programs[index])
+ {
+ std::string srcVS = m_VertexShaderSourceForFog;
+ std::string srcPS = m_SourceForFog;
+
+ if(PatchShaderFogGLES (srcVS, srcPS, fog, CanUseOptimizedFogCodeGLES(srcVS)))
+ {
+ // create program, shaders, link
+ GLES_CHK(m_Programs[index] = glCreateProgram());
+ ShaderErrors errors;
+ if(CompileProgram(index, srcVS, srcPS, channels))
+ {
+ FillParams (m_Programs[index], outParams, NULL);
+ m_FogParamsIndex[index] = glGetUniformLocation(m_Programs[index], "_unity_FogParams");
+ m_FogColorIndex[index] = glGetUniformLocation(m_Programs[index], "_unity_FogColor");
+
+ m_UniformCache[index].Create(&outParams, m_FogParamsIndex[index], m_FogColorIndex[index]);
+ }
+ else
+ {
+ if(m_GLSLVertexShader[index])
+ glDeleteShader(m_GLSLVertexShader[index]);
+
+ if(m_GLSLFragmentShader[index])
+ glDeleteShader(m_GLSLFragmentShader[index]);
+
+ m_GLSLVertexShader[index] = 0;
+ m_GLSLFragmentShader[index] = 0;
+
+ glDeleteProgram(m_Programs[index]);
+ m_Programs[index] = 0;
+
+ m_FogFailed[index] = true;
+ index = 0;
+ }
+ }
+ else
+ {
+ m_FogFailed[index] = true;
+ index = 0;
+ }
+ }
+ }
+ return index;
+}
+
+// chai: ÔÚBeforDrawCallµ÷ÓÃ
+int GlslGpuProgramGLES20::ApplyGpuProgramES20 (const GpuProgramParameters& params, const UInt8 *buffer)
+{
+ DBG_LOG_GLES20("GlslGpuProgramGLES20::ApplyGpuProgramES20()");
+
+ // m_Programs[0] == 0, is when Unity tries to build a dummy shader with empty source for fragment shaders, do nothing in this case
+ if (m_Programs[0] == 0)
+ return 0;
+
+ GfxDevice& device = GetRealGfxDevice();
+
+ const GfxFogParams& fog = device.GetFogParams();
+ const int index = (int)fog.mode;
+
+ DBG_LOG_GLES20("GLSL: apply program id=%i\n", m_Programs[index]);
+
+ //chai£º glUseProgram
+ GLSLUseProgramGLES20 (m_Programs[index]);
+
+ // Apply value parameters
+ const GpuProgramParameters::ValueParameterArray& valueParams = params.GetValueParams();
+ GpuProgramParameters::ValueParameterArray::const_iterator valueParamsEnd = valueParams.end();
+ for( GpuProgramParameters::ValueParameterArray::const_iterator i = valueParams.begin(); i != valueParamsEnd; ++i )
+ {
+ if (i->m_RowCount == 1 && i->m_ArraySize == 1)
+ {
+ UniformCacheGLES20* cache = m_UniformCache+index;
+ const float * val = reinterpret_cast<const float*>(buffer);
+
+ switch (i->m_ColCount)
+ {
+ case 1: CachedUniform1(cache, i->m_Type, i->m_Index, val); break;
+ case 2: CachedUniform2(cache, i->m_Type, i->m_Index, val); break;
+ case 3: CachedUniform3(cache, i->m_Type, i->m_Index, val); break;
+ case 4: CachedUniform4(cache, i->m_Type, i->m_Index, val); break;
+ default: break;
+ }
+
+ #if DEBUG_GLSL_BINDINGS
+ ;;printf_console(" vector %i dim=%i\n", i->m_Index, i->m_Dim );
+ #endif
+ buffer += 4*sizeof(float);
+ }
+ else
+ {
+ // Apply matrix parameters
+ DebugAssert (i->m_ArraySize == 1);
+ int size = *reinterpret_cast<const int*>(buffer); buffer += sizeof(int);
+ Assert (size == 16);
+ const Matrix4x4f* mat = reinterpret_cast<const Matrix4x4f*>(buffer);
+ if (i->m_RowCount == 3 && i->m_ColCount == 3)
+ {
+ Matrix3x3f m33 = Matrix3x3f(*mat);
+ GLES_CHK(glUniformMatrix3fv (i->m_Index, 1, GL_FALSE, m33.GetPtr()));
+ }
+ else
+ {
+ const float *ptr = mat->GetPtr ();
+ GLES_CHK(glUniformMatrix4fv (i->m_Index, 1, GL_FALSE, ptr));
+ }
+ #if DEBUG_GLSL_BINDINGS
+ ;;printf_console(" matrix %i (%s)\n", i->m_Index, i->m_Name.GetName() );
+ #endif
+ buffer += size * sizeof(float);
+ }
+ }
+
+ // Apply textures
+ const GpuProgramParameters::TextureParameterList& textureParams = params.GetTextureParams();
+ GpuProgramParameters::TextureParameterList::const_iterator textureParamsEnd = textureParams.end();
+ for( GpuProgramParameters::TextureParameterList::const_iterator i = textureParams.begin(); i != textureParamsEnd; ++i )
+ {
+ const GpuProgramParameters::TextureParameter& t = *i;
+ const TexEnvData* texdata = reinterpret_cast<const TexEnvData*>(buffer);
+ #if DEBUG_GLSL_BINDINGS
+ ;;printf_console(" sampler %i (%s) id=%i dim=%i\n", t.m_Index, t.m_Name.GetName(), tex->GetActualTextureID().m_ID, tex->GetTexDim() );
+ #endif
+ ApplyTexEnvData (t.m_Index, t.m_Index, *texdata);
+ buffer += sizeof(TexEnvData);
+ }
+
+ // Fog parameters if needed
+ if (index > 0)
+ {
+ if (m_FogColorIndex[fog.mode] >= 0)
+ CachedUniform4(m_UniformCache+index, kShaderParamFloat, m_FogColorIndex[fog.mode], fog.color.GetPtr());
+
+ Vector4f params(
+ fog.density * 1.2011224087f,// density / sqrt(ln(2))
+ fog.density * 1.4426950408f, // density / ln(2)
+ 0.0f,
+ 0.0f
+ );
+
+ if (fog.mode == kFogLinear)
+ {
+ float diff = fog.end - fog.start;
+ float invDiff = Abs(diff) > 0.0001f ? 1.0f/diff : 0.0f;
+ params[2] = -invDiff;
+ params[3] = fog.end * invDiff;
+ }
+
+ if (m_FogParamsIndex[fog.mode] >= 0)
+ CachedUniform4(m_UniformCache+index, kShaderParamFloat, m_FogParamsIndex[fog.mode], params.GetPtr());
+ }
+
+ GLESAssert();
+
+ return index;
+}
+
+
+// --------------------------------------------------------------------------
+
+
+FixedFunctionProgramGLES20::FixedFunctionProgramGLES20(GLShaderID vertexShader, GLShaderID fragmentShader)
+: m_GLSLProgram(0)
+, m_GLSLVertexShader(vertexShader)
+, m_GLSLFragmentShader(fragmentShader)
+{
+ m_GLSLProgram = Create(m_GLSLVertexShader, m_GLSLFragmentShader);
+}
+
+FixedFunctionProgramGLES20::~FixedFunctionProgramGLES20 ()
+{
+ // NOTE: do not delete vertex/fragment shaders; they can be shared between multiple programs
+ // and are deleted in ClearFixedFunctionPrograms
+ if (m_GLSLProgram != 0 ) // only delete valid programs
+ GLES_CHK(glDeleteProgram(m_GLSLProgram));
+
+ m_UniformCache.Destroy();
+}
+
+GLShaderID FixedFunctionProgramGLES20::Create(GLShaderID vertexShader, GLShaderID fragmentShader)
+{
+ if( !gGraphicsCaps.gles20.hasGLSL )
+ return false;
+
+ GLESAssert(); // Clear any GL errors
+
+ GLuint program;
+ GLES_CHK(program = glCreateProgram());
+
+ for (int i = 0; i < kAttribLookupTableSize; i++)
+ {
+ if(s_GLESVertexComponents[i] < gGraphicsCaps.gles20.maxAttributes)
+ GLES_CHK(glBindAttribLocation(program, s_GLESVertexComponents[i], s_GLSLESAttributes[i]));
+ }
+
+ GLES_CHK(glAttachShader(program, m_GLSLVertexShader));
+ GLES_CHK(glAttachShader(program, m_GLSLFragmentShader));
+
+ //We must link only after binding the attributes
+ GLES_CHK(glLinkProgram(program));
+
+ int linked = 0;
+ GLES_CHK(glGetProgramiv(program, GL_LINK_STATUS, &linked));
+ if (linked == 0)
+ {
+ ParseGlslErrors(program, kErrorLinkProgram);
+ GLES_CHK(glDeleteProgram(program));
+ return 0;
+ }
+
+ // Figure out the uniforms
+ int activeUniforms;
+
+ char name[1024];
+ GLenum type;
+ int size = 0,
+ length = 0,
+ bufSize = sizeof(name);
+
+ // Fetch texture stage samplers. Bind GLSL uniform to the OpenGL texture unit
+ // just once, cause it never changes after that for this program; and could cause
+ // internal shader recompiles when changing them.
+ DBG_LOG_GLES20("GLSL: apply fixed-function program id=%i\n", program);
+ GLSLUseProgramGLES20 (program);
+
+ for (int i = 0; i < kMaxSupportedTextureUnitsGLES; i++)
+ {
+ std::string samplerName = Format("u_sampler%d", i);
+ GLint uniformNumber = glGetUniformLocation(program, samplerName.c_str());
+
+ if (uniformNumber != -1)
+ {
+ GLES_CHK(glUniform1i (uniformNumber, i));
+ DBG_GLSL_BINDINGS_GLES20(" FFsampler: %s nr=%d", samplerName.c_str(), uniformNumber);
+ }
+ }
+
+ // fetch generic params
+ GLES_CHK(glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &activeUniforms));
+ for(int i=0; i < activeUniforms; i++)
+ {
+ GLES_CHK(glGetActiveUniform(program, i, bufSize, &length, &size, &type, name));
+ int uniformNumber = glGetUniformLocation(program, name);
+
+ const char* glslName = GetGLSLESPropertyNameRemap(name);
+ const char* unityName = glslName ? glslName : name;
+
+ if(type == GL_FLOAT) {
+ m_Params.AddVectorParam(uniformNumber, kShaderParamFloat, 1, unityName, -1, NULL);
+ }
+ else if(type == GL_FLOAT_VEC2) {
+ m_Params.AddVectorParam(uniformNumber, kShaderParamFloat, 2, unityName, -1, NULL);
+ }
+ else if(type == GL_FLOAT_VEC3) {
+ m_Params.AddVectorParam(uniformNumber, kShaderParamFloat, 3, unityName, -1, NULL);
+ }
+ else if(type == GL_FLOAT_VEC4) {
+ m_Params.AddVectorParam(uniformNumber, kShaderParamFloat, 4, unityName, -1, NULL);
+ }
+ else if(type == GL_FLOAT_MAT4) {
+ m_Params.AddMatrixParam(uniformNumber, unityName, 4, 4, -1, NULL);
+ }
+ else if(type == GL_FLOAT_MAT3) {
+ m_Params.AddMatrixParam(uniformNumber, unityName, 3, 3, -1, NULL);
+ }
+ else if(type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE) {
+ }
+ else {
+ AssertString( "Unrecognized GLSL uniform type" );
+ }
+ DBG_GLSL_BINDINGS_GLES20(" FFuniform: %s nr=%d type=%d", unityName, uniformNumber, type);
+ }
+
+ m_UniformCache.Create(&m_Params, -1,-1);
+
+ GLESAssert();
+ return program;
+}
+
+void FixedFunctionProgramGLES20::ApplyFFGpuProgram(const BuiltinShaderParamValues& values) const
+{
+ DBG_LOG_GLES20("FixedFunctionProgramGLES20::ApplyFFGpuProgram()");
+
+ DBG_LOG_GLES20("GLSL: apply fixed-function program id=%i\n", m_GLSLProgram);
+ GLSLUseProgramGLES20 (m_GLSLProgram);
+
+ // Apply float/vector parameters
+ const GpuProgramParameters::ValueParameterArray& valueParams = m_Params.GetValueParams();
+ GpuProgramParameters::ValueParameterArray::const_iterator valueParamsEnd = valueParams.end();
+ for( GpuProgramParameters::ValueParameterArray::const_iterator i = valueParams.begin(); i != valueParamsEnd; ++i )
+ {
+ if(i->m_RowCount == 1 && i->m_ArraySize == 1)
+ {
+ UniformCacheGLES20* cache = &m_UniformCache;
+ const float * val = values.GetVectorParam((BuiltinShaderVectorParam)i->m_Name.BuiltinIndex()).GetPtr();
+ switch (i->m_ColCount)
+ {
+ case 1: CachedUniform1(cache, kShaderParamFloat, i->m_Index, val); break;
+ case 2: CachedUniform2(cache, kShaderParamFloat, i->m_Index, val); break;
+ case 3: CachedUniform3(cache, kShaderParamFloat, i->m_Index, val); break;
+ case 4: CachedUniform4(cache, kShaderParamFloat, i->m_Index, val); break;
+ default: break;
+ }
+ }
+ else
+ {
+ // Apply matrix parameters
+ DebugAssert (i->m_ArraySize == 1);
+ const Matrix4x4f& mat = values.GetMatrixParam((BuiltinShaderMatrixParam)i->m_Name.BuiltinIndex());
+ if (i->m_RowCount == 3 && i->m_ColCount == 3)
+ {
+ Matrix3x3f m33 = Matrix3x3f(mat);
+ GLES_CHK(glUniformMatrix3fv (i->m_Index, 1, GL_FALSE, m33.GetPtr()));
+ }
+ else
+ {
+ const float *ptr = mat.GetPtr ();
+ GLES_CHK(glUniformMatrix4fv (i->m_Index, 1, GL_FALSE, ptr));
+ }
+ DBG_GLSL_BINDINGS_GLES20(" FFmatrix %i (%s)", i->m_Index, i->m_Name.GetName() );
+ }
+ }
+
+ GLESAssert();
+}
+
+
+#if UNITY_ANDROID || UNITY_BLACKBERRY || UNITY_TIZEN
+//-----------------------------------------------------------------------------
+// md5 internals are extracted from External/MurmurHash/md5.cpp
+ struct
+ md5_context
+ {
+ unsigned long total[2];
+ unsigned long state[4];
+ unsigned char buffer[64];
+ unsigned char ipad[64];
+ unsigned char opad[64];
+ };
+
+ extern void md5_starts(md5_context* ctx);
+ extern void md5_update(md5_context* ctx, unsigned char* input, int ilen);
+ extern void md5_finish(md5_context* ctx, unsigned char output[16]);
+#endif
+
+
+static void GetCachedBinaryName(const std::string& vprog, const std::string& fshader, char filename[33])
+{
+// we have caching only on android, so no need to worry (at least for now) about md5
+#if UNITY_ANDROID || UNITY_BLACKBERRY || UNITY_TIZEN
+
+ unsigned char hash[16] = {0};
+
+ md5_context ctx;
+ md5_starts(&ctx);
+ md5_update(&ctx, (unsigned char*)vprog.c_str(), vprog.length());
+ md5_update(&ctx, (unsigned char*)fshader.c_str(), fshader.length());
+ md5_finish(&ctx, hash);
+
+ BytesToHexString(hash, 16, filename);
+
+#else
+
+ (void)vprog;
+ (void)fshader;
+ ::memset(filename, '1', 33);
+
+#endif
+}
+
+
+#endif // GFX_SUPPORTS_OPENGLES20
diff --git a/Runtime/GfxDevice/opengles20/GpuProgramsGLES20.h b/Runtime/GfxDevice/opengles20/GpuProgramsGLES20.h
new file mode 100644
index 0000000..a9a14d2
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/GpuProgramsGLES20.h
@@ -0,0 +1,78 @@
+#ifndef GPUPROGRAMSGLES20_H
+#define GPUPROGRAMSGLES20_H
+
+
+#if !GFX_SUPPORTS_OPENGLES20
+#error "Should not include GpuProgramsGLES20 on this platform"
+#endif
+
+
+#include "Runtime/GfxDevice/GpuProgram.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "Runtime/Utilities/GLSLUtilities.h"
+#include "IncludesGLES20.h"
+#include "GpuProgramsGLES20_UniformCache.h"
+
+class GlslGpuProgramGLES20 : public GpuProgramGL
+{
+public:
+ GlslGpuProgramGLES20 (const std::string& source, CreateGpuProgramOutput& output);
+ ~GlslGpuProgramGLES20();
+
+ virtual void ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer) { Assert(!"Should not be used"); }
+
+ // Returns the permutation index used
+ int ApplyGpuProgramES20 (const GpuProgramParameters& params, const UInt8 *buffer);
+ int GetGLProgram (FogMode fog, GpuProgramParameters& outParams, ChannelAssigns& channels);
+
+ static bool InitBinaryShadersSupport();
+
+
+ UniformCacheGLES20 m_UniformCache[kFogModeCount];
+
+private:
+ static void FillParams (unsigned int programID, GpuProgramParameters& params, PropertyNamesSet* outNames);
+
+ bool Create (const std::string& source, ChannelAssigns& channels);
+
+ bool CompileProgram(unsigned index, const std::string& vprog, const std::string& fshader, ChannelAssigns& channels);
+
+private:
+ std::string m_VertexShaderSourceForFog;
+ GLShaderID m_GLSLVertexShader[kFogModeCount];
+ GLShaderID m_GLSLFragmentShader[kFogModeCount];
+ int m_FogColorIndex[kFogModeCount];
+ int m_FogParamsIndex[kFogModeCount];
+ bool m_FogFailed[kFogModeCount];
+
+ static std::string _CachePath;
+ static glGetProgramBinaryOESFunc _glGetProgramBinaryOES;
+ static glProgramBinaryOESFunc _glProgramBinaryOES;
+};
+
+
+class FixedFunctionProgramGLES20
+{
+public:
+ FixedFunctionProgramGLES20(GLShaderID vertexShader, GLShaderID fragmentShader);
+ ~FixedFunctionProgramGLES20();
+
+ void ApplyFFGpuProgram(const BuiltinShaderParamValues& values) const;
+ const BuiltinShaderParamIndices& GetBuiltinParams() const { return m_Params.GetBuiltinParams(); }
+
+ mutable UniformCacheGLES20 m_UniformCache;
+
+protected:
+ GLShaderID Create(GLShaderID vertexShader, GLShaderID fragmentShader);
+
+private:
+ GLShaderID m_GLSLProgram;
+ GLShaderID m_GLSLVertexShader, m_GLSLFragmentShader;
+
+ GpuProgramParameters m_Params;
+};
+
+bool CompileGlslShader(GLShaderID shader, GLSLErrorType type, const char* source);
+
+#endif
diff --git a/Runtime/GfxDevice/opengles20/GpuProgramsGLES20_UniformCache.cpp b/Runtime/GfxDevice/opengles20/GpuProgramsGLES20_UniformCache.cpp
new file mode 100644
index 0000000..db725d2
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/GpuProgramsGLES20_UniformCache.cpp
@@ -0,0 +1,72 @@
+#include "UnityPrefix.h"
+#if !GFX_SUPPORTS_OPENGLES20
+#error "Should not include GpuProgramsGLES20 on this platform"
+#endif
+
+#include "GpuProgramsGLES20_UniformCache.h"
+
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/GfxDevice/GpuProgram.h"
+#include "IncludesGLES20.h"
+#include "AssertGLES20.h"
+
+
+void UniformCacheGLES20::Create(const GpuProgramParameters* params, int fogParamsIndex, int fogColorIndex)
+{
+ int lastUsedUniform = -1;
+
+ // we will track only float/vector uniforms
+ GpuProgramParameters::ValueParameterArray::const_iterator paramI = params->GetValueParams().begin();
+ GpuProgramParameters::ValueParameterArray::const_iterator paramEnd = params->GetValueParams().end();
+ while(paramI != paramEnd)
+ {
+ if(paramI->m_RowCount == 1 && paramI->m_ArraySize == 1 && paramI->m_Index > lastUsedUniform)
+ lastUsedUniform = paramI->m_Index;
+
+ ++paramI;
+ }
+
+ const BuiltinShaderParamIndices& builtinParam = params->GetBuiltinParams();
+ for(unsigned i = 0 ; i < kShaderInstanceVecCount ; ++i)
+ {
+ if(builtinParam.vec[i].gpuIndex > lastUsedUniform)
+ lastUsedUniform = builtinParam.vec[i].gpuIndex;
+ }
+
+ if(fogParamsIndex > lastUsedUniform) lastUsedUniform = fogParamsIndex;
+ if(fogColorIndex > lastUsedUniform) lastUsedUniform = fogColorIndex;
+
+ count = lastUsedUniform + 1;
+ uniform = (float*)UNITY_MALLOC_ALIGNED(kMemShader, count*4 * sizeof(float), 16);
+ memset(uniform, 0xff /* NaN */, count*4 * sizeof(float));
+}
+
+void UniformCacheGLES20::Destroy()
+{
+ count = 0;
+
+ UNITY_FREE(kMemShader, uniform);
+ uniform = 0;
+}
+
+
+// In theory Uniform*f can also be used to load bool uniforms, in practice
+// some drivers don't like that (e.g. PVR GLES2.0 Emu wants bools to be loaded
+// via Uniform*i). So load both integers and bools via *i functions.
+#define CACHED_UNIFORM_IMPL(Count) \
+void CachedUniform##Count(UniformCacheGLES20* cache, ShaderParamType type, int index, const float* val) \
+{ \
+ if (cache->UpdateUniform(index, val, Count)) { \
+ if (type == kShaderParamFloat) \
+ GLES_CHK(glUniform##Count##fv(index, 1, val)); \
+ else { \
+ int ival[4] = {val[0],val[1],val[2],val[3]}; \
+ GLES_CHK(glUniform##Count##iv(index, 1, ival)); \
+ } \
+ } \
+} \
+
+CACHED_UNIFORM_IMPL(1);
+CACHED_UNIFORM_IMPL(2);
+CACHED_UNIFORM_IMPL(3);
+CACHED_UNIFORM_IMPL(4);
diff --git a/Runtime/GfxDevice/opengles20/GpuProgramsGLES20_UniformCache.h b/Runtime/GfxDevice/opengles20/GpuProgramsGLES20_UniformCache.h
new file mode 100644
index 0000000..6f01fc4
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/GpuProgramsGLES20_UniformCache.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#if !GFX_SUPPORTS_OPENGLES20
+#error "Should not include GpuProgramsGLES20 on this platform"
+#endif
+
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include <string.h>
+
+class GpuProgramParameters;
+
+struct
+UniformCacheGLES20
+{
+ // for gles we must set values per-uniform (not per-registers like in dx)
+ // so there is no real need for dirty tracking.
+ // TODO: do unified impl with dirty tracking if/when we do "everything is an array" in gles
+ float* uniform;
+ unsigned count;
+
+ UniformCacheGLES20() : uniform(0), count(0) {}
+
+ // we will pre-alloc memory. Fog params are handled differently (not added to gpu params).
+ // TODO: make it more general, int* perhaps, or some struct
+ void Create(const GpuProgramParameters* params, int fogParamsIndex, int fogColorIndex);
+ void Destroy();
+
+ // returns true if you need to update for real
+ bool UpdateUniform(int index, const float* val, unsigned floatCount);
+};
+
+void CachedUniform1(UniformCacheGLES20* cache, ShaderParamType type, int index, const float* val);
+void CachedUniform2(UniformCacheGLES20* cache, ShaderParamType type, int index, const float* val);
+void CachedUniform3(UniformCacheGLES20* cache, ShaderParamType type, int index, const float* val);
+void CachedUniform4(UniformCacheGLES20* cache, ShaderParamType type, int index, const float* val);
+
+
+inline bool UniformCacheGLES20::UpdateUniform(int index, const float* val, unsigned floatCount)
+{
+ Assert(index < count);
+ const unsigned mem_sz = floatCount*sizeof(float);
+
+ float* target = uniform + 4*index;
+ if(::memcmp(target, val, mem_sz))
+ {
+ ::memcpy(target, val, mem_sz);
+ return true;
+ }
+ return false;
+}
diff --git a/Runtime/GfxDevice/opengles20/GpuPropertiesGLES20.cpp b/Runtime/GfxDevice/opengles20/GpuPropertiesGLES20.cpp
new file mode 100644
index 0000000..0d808a1
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/GpuPropertiesGLES20.cpp
@@ -0,0 +1,70 @@
+#include "UnityPrefix.h"
+#include "GpuPropertiesGLES20.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/GfxDevice/BuiltinShaderParams.h"
+#include "Runtime/GfxDevice/BuiltinShaderParamsNames.h"
+
+#if GFX_SUPPORTS_OPENGLES20
+
+struct GLSLESProperty
+{
+ GLSLESProperty(const char* _glName, const char* _glesName) : glName(_glName), unityName(_glesName) { }
+ const char* glName;
+ const char* unityName;
+};
+
+#define DEF_MAT_INTERNAL(name, builtin) GLSLESProperty(name, GetShaderInstanceMatrixParamName(builtin))
+#define DEF_MAT_BUILTIN(name, builtin) GLSLESProperty(name, GetBuiltinMatrixParamName(builtin))
+#define BIND_VEC_BUILTIN(name, builtin) GLSLESProperty(name, GetBuiltinVectorParamName(builtin))
+
+
+static const GLSLESProperty kglslesProperties[] =
+{
+ DEF_MAT_BUILTIN("gl_ProjectionMatrix", kShaderMatProj),
+ DEF_MAT_INTERNAL("gl_NormalMatrix", kShaderInstanceMatNormalMatrix),
+ DEF_MAT_INTERNAL("gl_ModelViewProjectionMatrix", kShaderInstanceMatMVP),
+ DEF_MAT_INTERNAL("gl_ModelViewMatrixTranspose", kShaderInstanceMatTransMV),
+ DEF_MAT_INTERNAL("gl_ModelViewMatrixInverseTranspose", kShaderInstanceMatInvTransMV),
+ DEF_MAT_INTERNAL("gl_ModelViewMatrix", kShaderInstanceMatMV),
+
+ DEF_MAT_INTERNAL("gl_TextureMatrix0", kShaderInstanceMatTexture0),
+ DEF_MAT_INTERNAL("gl_TextureMatrix1", kShaderInstanceMatTexture1),
+ DEF_MAT_INTERNAL("gl_TextureMatrix2", kShaderInstanceMatTexture2),
+ DEF_MAT_INTERNAL("gl_TextureMatrix3", kShaderInstanceMatTexture3),
+ DEF_MAT_INTERNAL("gl_TextureMatrix4", kShaderInstanceMatTexture4),
+ DEF_MAT_INTERNAL("gl_TextureMatrix5", kShaderInstanceMatTexture5),
+ DEF_MAT_INTERNAL("gl_TextureMatrix6", kShaderInstanceMatTexture6),
+ DEF_MAT_INTERNAL("gl_TextureMatrix7", kShaderInstanceMatTexture7),
+
+ BIND_VEC_BUILTIN("_glesLightSource[0].diffuse", kShaderVecLight0Diffuse),
+ BIND_VEC_BUILTIN("_glesLightSource[1].diffuse", kShaderVecLight1Diffuse),
+ BIND_VEC_BUILTIN("_glesLightSource[2].diffuse", kShaderVecLight2Diffuse),
+ BIND_VEC_BUILTIN("_glesLightSource[3].diffuse", kShaderVecLight3Diffuse),
+ BIND_VEC_BUILTIN("_glesLightSource[0].position", kShaderVecLight0Position),
+ BIND_VEC_BUILTIN("_glesLightSource[1].position", kShaderVecLight1Position),
+ BIND_VEC_BUILTIN("_glesLightSource[2].position", kShaderVecLight2Position),
+ BIND_VEC_BUILTIN("_glesLightSource[3].position", kShaderVecLight3Position),
+ BIND_VEC_BUILTIN("_glesLightSource[0].spotDirection", kShaderVecLight0SpotDirection),
+ BIND_VEC_BUILTIN("_glesLightSource[1].spotDirection", kShaderVecLight1SpotDirection),
+ BIND_VEC_BUILTIN("_glesLightSource[2].spotDirection", kShaderVecLight2SpotDirection),
+ BIND_VEC_BUILTIN("_glesLightSource[3].spotDirection", kShaderVecLight3SpotDirection),
+ BIND_VEC_BUILTIN("_glesLightSource[0].atten", kShaderVecLight0Atten),
+ BIND_VEC_BUILTIN("_glesLightSource[1].atten", kShaderVecLight1Atten),
+ BIND_VEC_BUILTIN("_glesLightSource[2].atten", kShaderVecLight2Atten),
+ BIND_VEC_BUILTIN("_glesLightSource[3].atten", kShaderVecLight3Atten),
+ BIND_VEC_BUILTIN("_glesLightModel.ambient", kShaderVecLightModelAmbient),
+};
+
+
+const char* GetGLSLESPropertyNameRemap (const char* name)
+{
+ for (int i = 0; i < ARRAY_SIZE(kglslesProperties); i++)
+ {
+ const GLSLESProperty& prop = kglslesProperties[i];
+ if (strcmp(name, prop.glName) == 0)
+ return prop.unityName;
+ }
+ return NULL;
+}
+
+#endif // GFX_SUPPORTS_OPENGLES20
diff --git a/Runtime/GfxDevice/opengles20/GpuPropertiesGLES20.h b/Runtime/GfxDevice/opengles20/GpuPropertiesGLES20.h
new file mode 100644
index 0000000..9caae9a
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/GpuPropertiesGLES20.h
@@ -0,0 +1,3 @@
+#pragma once
+
+const char* GetGLSLESPropertyNameRemap (const char* name);
diff --git a/Runtime/GfxDevice/opengles20/IncludesGLES20.h b/Runtime/GfxDevice/opengles20/IncludesGLES20.h
new file mode 100644
index 0000000..260bea5
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/IncludesGLES20.h
@@ -0,0 +1 @@
+#include "Runtime/GfxDevice/opengles/IncludesGLES.h" \ No newline at end of file
diff --git a/Runtime/GfxDevice/opengles20/RenderTextureGLES20.cpp b/Runtime/GfxDevice/opengles20/RenderTextureGLES20.cpp
new file mode 100644
index 0000000..d397c6e
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/RenderTextureGLES20.cpp
@@ -0,0 +1,958 @@
+#include "UnityPrefix.h"
+#include "RenderTextureGLES20.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Graphics/RenderSurface.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "IncludesGLES20.h"
+#include "AssertGLES20.h"
+#include "DebugGLES20.h"
+#include "TextureIdMapGLES20.h"
+#include "UnityGLES20Ext.h"
+
+// seems like code is the best place to leave TODO:
+// GetCurrentFBImpl - it is called in random places - should just put somehwere (begin frame?)
+// discard logic - both color/depth have flags, but only color is used, also - mrt case
+
+
+
+#if UNITY_ANDROID
+ #include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+#endif
+
+#if 1
+ #define DBG_LOG_RT_GLES20(...) {}
+#else
+ #define DBG_LOG_RT_GLES20(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+#endif
+
+
+#if GFX_SUPPORTS_OPENGLES20
+
+#define GL_RT_COMMON_GLES2 1
+#include "Runtime/GfxDevice/GLRTCommon.h"
+#undef GL_RT_COMMON_GLES2
+
+#if UNITY_IPHONE
+ extern "C" bool UnityDefaultFBOHasMSAA();
+ extern "C" void* UnityDefaultFBOColorBuffer();
+#endif
+
+
+struct RenderColorSurfaceGLES2 : public RenderSurfaceBase
+{
+ GLuint m_ColorBuffer;
+ RenderTextureFormat format;
+ TextureDimension dim;
+};
+
+struct RenderDepthSurfaceGLES2 : public RenderSurfaceBase
+{
+ GLuint m_DepthBuffer;
+ DepthBufferFormat depthFormat;
+ bool depthWithStencil;
+};
+
+extern GLint gDefaultFBO;
+static bool gDefaultFboInited = false;
+
+static const unsigned long kOpenGLESTextureDimensionTable[kTexDimCount] = {0, 0, GL_TEXTURE_2D, 0, GL_TEXTURE_CUBE_MAP, 0};
+
+static RenderColorSurfaceGLES2* s_ActiveColorTarget[kMaxSupportedRenderTargets] = {0};
+static int s_ActiveColorTargetCount = 0;
+static int s_ActiveMip = 0;
+static CubemapFace s_ActiveFace = kCubeFaceUnknown;
+static RenderDepthSurfaceGLES2* s_ActiveDepthTarget = 0;
+
+static RenderColorSurfaceGLES2 s_BackBufferColor;
+static RenderDepthSurfaceGLES2 s_BackBufferDepth;
+
+
+// at least on ios when changing ext of attachments there is huge perf penalty
+// so do fbo per rt-ext
+
+typedef std::pair<unsigned, unsigned> FBKey;
+typedef std::map<FBKey, GLuint> FBMap;
+
+static FBMap _FBMap;
+static GLuint GetFBFromAttachments(RenderColorSurfaceGLES2* color, RenderDepthSurfaceGLES2* depth)
+{
+ // while it may seems bad that we freely mix rb/texture - we do the distinction per-gpu/per-platform mostly
+ // so it is actually ok
+ unsigned ckey = color->textureID.m_ID ? color->textureID.m_ID : color->m_ColorBuffer;
+ unsigned dkey = depth->textureID.m_ID ? depth->textureID.m_ID : depth->m_DepthBuffer;
+ FBKey key = std::make_pair(ckey, dkey);
+
+
+ FBMap::iterator fbi = _FBMap.find(key);
+ if(fbi == _FBMap.end())
+ {
+ GLuint fb=0;
+ GLES_CHK(glGenFramebuffers(1, &fb));
+
+ fbi = _FBMap.insert(std::make_pair(key, fb)).first;
+ }
+
+ Assert(fbi != _FBMap.end());
+ return fbi->second;
+}
+
+// this should be called on context loss
+// TODO: need we care about the case of actually deleting FBOs
+void ClearFBMapping()
+{
+ _FBMap.clear();
+}
+
+// this is not the best way, but unless we have proper rt->fbo mapping let's live with that
+// the reason is to avoid leaks when we destroy rb/tex that is still attached to some FBO
+static void OnFBOAttachmentDelete(RenderSurfaceBase* rs)
+{
+ unsigned key = rs->textureID.m_ID;
+ if(key == 0)
+ key = rs->colorSurface ? ((RenderColorSurfaceGLES2*)rs)->m_ColorBuffer : ((RenderDepthSurfaceGLES2*)rs)->m_DepthBuffer;
+
+ int curFB = GetCurrentFBGLES2();
+
+ for(FBMap::iterator i = _FBMap.begin() ; i != _FBMap.end() ; )
+ {
+ if(i->first.first == key || i->first.second == key)
+ {
+ // make sure we also drop attachments before killing FBO (some drivers may leak otherwise)
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, i->second));
+ GLES_CHK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0));
+ GLES_CHK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0));
+ GLES_CHK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0));
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, curFB));
+
+ GLES_CHK(glDeleteFramebuffers(1, &i->second));
+ FBMap::iterator rem = i;
+ ++i;
+ _FBMap.erase(rem);
+ }
+ else
+ {
+ ++i;
+ }
+ }
+}
+
+static const char* GetFBOStatusError( GLenum status )
+{
+ Assert( status != GL_FRAMEBUFFER_COMPLETE ); // should not be called when everything is ok...
+ switch( status )
+ {
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return "INCOMPLETE_ATTACHMENT";
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return "INCOMPLETE_MISSING_ATTACHMENT";
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: return "INCOMPLETE_DIMENSIONS";
+ case GL_FRAMEBUFFER_UNSUPPORTED: return "UNSUPPORTED";
+ default: return "unknown error";
+ }
+}
+static const char* GetFBOAttachementType(GLint type)
+{
+ switch (type)
+ {
+ case GL_RENDERBUFFER: return "GL_RENDERBUFFER";
+ case GL_TEXTURE: return "GL_TEXTURE";
+ default: return "GL_NONE";
+ }
+}
+
+static int GetCurrentFBImpl()
+{
+ GLint curFB;
+ GLES_CHK(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &curFB));
+
+ return (int)curFB;
+}
+
+int GetCurrentFBGLES2()
+{
+ return GetCurrentFBImpl();
+}
+
+void EnsureDefaultFBInitedGLES2()
+{
+ if(!gDefaultFboInited)
+ {
+ gDefaultFBO = GetCurrentFBImpl();
+ // this will be called from correct thread, so no need to sync or whatever
+ extern void GfxDeviceGLES20_InitFramebufferDepthFormat();
+ GfxDeviceGLES20_InitFramebufferDepthFormat();
+
+ gDefaultFboInited = true;
+ }
+}
+
+void DiscardCurrentFBImpl(bool discardColor, bool discardDepth, GLenum target=GL_FRAMEBUFFER)
+{
+ if(gGraphicsCaps.gles20.hasDiscardFramebuffer)
+ {
+ // TODO: mrt case?
+ GLenum discardUserAttach[] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
+ GLenum discardSystemAttach[] = {GL_COLOR_EXT, GL_DEPTH_EXT, GL_STENCIL_EXT};
+
+ const GLenum* discardTarget = discardUserAttach;
+ if(s_ActiveColorTarget[0]->backBuffer && GetCurrentFBGLES2() == 0)
+ discardTarget = discardSystemAttach;
+
+
+ if(!discardColor) discardTarget += 1;
+
+ unsigned discardCount = 0;
+ if(discardColor) discardCount += 1;
+ if(discardDepth) discardCount += 2;
+
+ if(discardCount)
+ GLES_CHK(gGlesExtFunc.glDiscardFramebufferEXT(GL_FRAMEBUFFER, discardCount, discardTarget));
+ }
+}
+
+void ClearCurrentFBImpl(bool clearColor, bool clearDepth)
+{
+ if(gGraphicsCaps.hasTiledGPU)
+ {
+ UInt32 clearFlags = (clearColor ? kGfxClearColor : 0) | (clearDepth ? kGfxClearDepthStencil : 0);
+ float clearColor[] = {0.0f, 0.0f, 0.0f, 1.0f};
+
+ GetRealGfxDevice().Clear(clearFlags, clearColor, 1.0f, 0);
+ }
+}
+
+enum
+{
+ discardDiscardFB = 0,
+ discardClearFB = 1
+};
+
+static void DiscardContentsImpl(int discardPhase)
+{
+ // TODO: mrt?
+ if(s_ActiveColorTarget[0] || s_ActiveDepthTarget)
+ {
+ bool* colorVar = discardPhase == discardDiscardFB ? &s_ActiveColorTarget[0]->shouldDiscard : &s_ActiveColorTarget[0]->shouldClear;
+ bool* depthVar = discardPhase == discardDiscardFB ? &s_ActiveDepthTarget->shouldDiscard : &s_ActiveDepthTarget->shouldClear;
+
+ bool color = *colorVar, depth = *depthVar;
+
+ *colorVar = *depthVar = false;
+
+ if(color || depth)
+ {
+ if(discardPhase == discardDiscardFB) DiscardCurrentFBImpl(color, depth);
+ else ClearCurrentFBImpl(color, depth);
+ }
+ }
+}
+
+
+void DiscardContentsGLES2(RenderSurfaceHandle rs)
+{
+ // TODO: handle bb
+ if(rs.IsValid())
+ {
+ RenderColorSurfaceGLES2* rsgles = reinterpret_cast<RenderColorSurfaceGLES2*>(rs.object);
+ // discard only makes sense for current active rt
+ rsgles->shouldDiscard = gGraphicsCaps.gles20.hasDiscardFramebuffer && rsgles == s_ActiveColorTarget[0];
+ rsgles->shouldClear = gGraphicsCaps.hasTiledGPU;
+ }
+}
+
+bool SetRenderTargetGLES2 (int count, RenderSurfaceHandle* colorHandle, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face, GLuint globalSharedFBO)
+{
+ RenderColorSurfaceGLES2* rcolor0 = reinterpret_cast<RenderColorSurfaceGLES2*>( colorHandle[0].object );
+ RenderDepthSurfaceGLES2* rdepth = reinterpret_cast<RenderDepthSurfaceGLES2*>( depthHandle.object );
+
+ // Exit if nothing to do
+ if( s_ActiveColorTargetCount == count && s_ActiveDepthTarget == rdepth && s_ActiveFace == face && s_ActiveMip == mipLevel )
+ {
+ bool colorSame = true;
+ for(int i = 0 ; i < count && colorSame ; ++i)
+ {
+ if(s_ActiveColorTarget[i] != reinterpret_cast<RenderColorSurfaceGLES2*>(colorHandle[i].object))
+ colorSame = false;
+ }
+ if (colorSame)
+ return false;
+ }
+
+ EnsureDefaultFBInitedGLES2();
+ DiscardContentsImpl(discardDiscardFB);
+
+ AssertIf (!gGraphicsCaps.hasRenderToTexture);
+ GetRealGfxDevice().GetFrameStats().AddRenderTextureChange(); // stats
+
+
+ Assert(colorHandle[0].IsValid() && depthHandle.IsValid());
+ Assert(colorHandle[0].object->backBuffer == depthHandle.object->backBuffer);
+
+
+ GLenum colorAttachStart = GL_COLOR_ATTACHMENT0;
+ if(gGraphicsCaps.gles20.hasNVMRT)
+ colorAttachStart = GL_COLOR_ATTACHMENT0_NV;
+
+ Assert(gGraphicsCaps.gles20.hasNVMRT || count==1);
+
+ if(!rcolor0->backBuffer)
+ {
+ GLuint fb = GetFBFromAttachments(rcolor0, rdepth);
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, fb));
+
+ GLenum drawBuffers[kMaxSupportedRenderTargets] = {0};
+ for(int i = 0 ; i < count ; ++i)
+ {
+ // when we start to support EXT variant - add check here
+ GLenum colorAttach = colorAttachStart + i;
+ RenderColorSurfaceGLES2* rcolor = reinterpret_cast<RenderColorSurfaceGLES2*>(colorHandle[i].object);
+ Assert(rcolor->colorSurface);
+
+ GLuint targetColorTex = (GLuint)TextureIdMap::QueryNativeTexture(rcolor->textureID);
+
+ drawBuffers[i] = colorAttach;
+ if(!IsDepthRTFormat(rcolor->format) && targetColorTex)
+ {
+ if (rcolor->dim == kTexDimCUBE)
+ {
+ GLES_CHK(glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttach, GL_TEXTURE_CUBE_MAP_POSITIVE_X + clamp<int>(face,0,5), targetColorTex, mipLevel));
+ }
+ else
+ {
+ if(rcolor->samples > 1 && gGraphicsCaps.gles20.hasImgMSAA)
+ GLES_CHK(gGlesExtFunc.glFramebufferTexture2DMultisampleIMG(GL_FRAMEBUFFER, colorAttach, GL_TEXTURE_2D, targetColorTex, mipLevel, rcolor->samples));
+ else
+ GLES_CHK(glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttach, GL_TEXTURE_2D, targetColorTex, mipLevel));
+ }
+ }
+ else
+ {
+ GLES_CHK(glFramebufferRenderbuffer (GL_FRAMEBUFFER, colorAttach, GL_RENDERBUFFER, rcolor->m_ColorBuffer));
+ if(rcolor->m_ColorBuffer == 0)
+ drawBuffers[i] = GL_NONE;
+ }
+ }
+
+ if(gGraphicsCaps.gles20.hasNVMRT)
+ gGlesExtFunc.glDrawBuffersNV(count, drawBuffers);
+
+ GLuint targetDepthTex = (GLuint)TextureIdMap::QueryNativeTexture(rdepth->textureID);
+ bool needAttachStencil = gGraphicsCaps.hasStencil && rdepth->depthWithStencil;
+
+ // depth surface
+ AssertIf (rdepth->colorSurface);
+ if (targetDepthTex
+#if UNITY_PEPPER
+// Workaround for http://code.google.com/p/angleproject/issues/detail?id=211
+ || rdepth->m_DepthBuffer == 0
+#endif
+ )
+ {
+ DBG_LOG_RT_GLES20("glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, %d, 0);", rdepth->textureID.m_ID);
+ GLES_CHK(glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, targetDepthTex, 0));
+ // A driver might not support attaching depth & stencil as textures here.
+ if (!gGraphicsCaps.hasRenderTargetStencil)
+ needAttachStencil = false;
+ if (needAttachStencil)
+ GLES_CHK(glFramebufferTexture2D (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, targetDepthTex, 0));
+ else
+ GLES_CHK(glFramebufferTexture2D (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0));
+ }
+ else
+ {
+ DBG_LOG_RT_GLES20("glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, %d, 0);", rdepth->m_DepthBuffer);
+ GLES_CHK(glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rdepth->m_DepthBuffer));
+ if(needAttachStencil)
+ GLES_CHK(glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rdepth->m_DepthBuffer));
+ else
+ GLES_CHK(glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0));
+ }
+
+ GLenum status = glCheckFramebufferStatus (GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE)
+ {
+ int colorParam, depthParam, stencilParam;
+ int colorValue, depthValue, stencilValue;
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &colorParam);
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &depthParam);
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &stencilParam);
+
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &colorValue);
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthValue);
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &stencilValue);
+
+ AssertString (Format(
+ "FBO fail: %s\nDetailed description:\n"
+ "GL_COLOR_ATTACHMENT0 Type:%s Value:%d\n"
+ "GL_DEPTH_ATTACHMENT Type:%s Value:%d\n"
+ "GL_STENCIL_ATTACHEMENT Type:%s Value:%d\n",
+ GetFBOStatusError(status),
+ GetFBOAttachementType(colorParam), colorValue,
+ GetFBOAttachementType(depthParam), depthValue,
+ GetFBOAttachementType(stencilParam), stencilValue));
+ }
+ Assert(status == GL_FRAMEBUFFER_COMPLETE);
+ }
+ else
+ {
+ Assert(rcolor0->backBuffer && rdepth->backBuffer); // OpenGL can't mix FBO and native window at once
+ GLES_CHK(glBindFramebuffer( GL_FRAMEBUFFER, gDefaultFBO ));
+ }
+
+ // If we previously had a mip-mapped render texture, generate mip levels for it now.
+ if (s_ActiveColorTarget[0] &&
+ (s_ActiveColorTarget[0]->flags & kSurfaceCreateMipmap) &&
+ (s_ActiveColorTarget[0]->flags & kSurfaceCreateAutoGenMips))
+ {
+ const int textureTarget = kOpenGLESTextureDimensionTable[s_ActiveColorTarget[0]->dim];
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, s_ActiveColorTarget[0]->textureID, s_ActiveColorTarget[0]->dim, std::numeric_limits<float>::infinity());
+ GLES_CHK(glGenerateMipmap(textureTarget));
+ }
+
+ s_ActiveColorTargetCount = count;
+ for(int i = 0 ; i < count ; ++i)
+ s_ActiveColorTarget[i] = reinterpret_cast<RenderColorSurfaceGLES2*>(colorHandle[i].object);
+
+ s_ActiveDepthTarget = rdepth;
+ s_ActiveFace = face;
+ s_ActiveMip = mipLevel;
+
+ DiscardContentsImpl(discardClearFB);
+
+ return true;
+}
+
+// TODO: take index into account here too
+RenderSurfaceHandle GetActiveRenderColorSurfaceGLES2()
+{
+ return RenderSurfaceHandle(s_ActiveColorTarget[0]);
+}
+RenderSurfaceHandle GetActiveRenderDepthSurfaceGLES2()
+{
+ return RenderSurfaceHandle(s_ActiveDepthTarget);
+}
+
+bool IsActiveRenderTargetWithColorGLES2()
+{
+ return !s_ActiveColorTarget[0] || s_ActiveColorTarget[0]->backBuffer || !IsDepthRTFormat (s_ActiveColorTarget[0]->format);
+}
+
+
+static void CreateFBORenderColorSurfaceGLES (RenderColorSurfaceGLES2& rs)
+{
+ int textureTarget = kOpenGLESTextureDimensionTable[rs.dim];
+
+ if( !IsDepthRTFormat(rs.format) )
+ {
+ GLenum internalFormat = (rs.flags & kSurfaceCreateSRGB) ? RTColorInternalFormatSRGBGLES2(rs.format) : RTColorInternalFormatGLES2(rs.format);
+ GLenum textureFormat = (rs.flags & kSurfaceCreateSRGB) ? RTColorTextureFormatSRGBGLES2(rs.format) : RTColorTextureFormatGLES2(rs.format);
+ GLenum textureType = RTColorTextureTypeGLES2(rs.format);
+
+ // Create texture to render for color
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, rs.textureID, rs.dim, std::numeric_limits<float>::infinity());
+
+ if (rs.dim == kTexDimCUBE)
+ {
+ // cubemap: initialize all faces
+ for( int f = 0; f < 6; ++f )
+ {
+ glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, 0, internalFormat, rs.width, rs.height, 0, textureFormat, textureType, NULL );
+ }
+ DBG_LOG_RT_GLES20("Creating FBO Color Surface (GL_TEXTURE_CUBE_MAP) [%d] - Width(%d) x Height(%d)", rs.textureID.m_ID, rs.width, rs.height);
+ if (rs.flags & kSurfaceCreateMipmap) { // establish mip map chain if needed
+ GLES_CHK(glGenerateMipmap( textureTarget ));
+ }
+ }
+ // not the best way around, but oh well
+ else if(!rs.textureID.m_ID)
+ {
+ internalFormat = RBColorInternalFormatGLES2(rs.format);
+ GLES_CHK(glGenRenderbuffers(1, &rs.m_ColorBuffer));
+ GLES_CHK(glBindRenderbuffer(GL_RENDERBUFFER, rs.m_ColorBuffer));
+ if(rs.samples > 1 && gGraphicsCaps.gles20.hasAppleMSAA)
+ GLES_CHK(gGlesExtFunc.glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, rs.samples, internalFormat, rs.width, rs.height));
+ else
+ GLES_CHK(glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, rs.width, rs.height));
+ }
+ else
+ {
+ // regular texture: initialize
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+ GLES_CHK(glTexImage2D( textureTarget, 0, internalFormat, rs.width, rs.height, 0, textureFormat, textureType, NULL ));
+
+ DBG_LOG_RT_GLES20("Creating FBO Color Surface (GL_TEXTURE_2D) [%d] - Width(%d) x Height(%d)", rs.textureID.m_ID, rs.width, rs.height);
+ if (rs.flags & kSurfaceCreateMipmap) { // establish mip map chain if needed
+ GLES_CHK(glGenerateMipmap( textureTarget ));
+ }
+ }
+ }
+ else
+ {
+ // Note : For GLES2.0Emu and NaCl we must create RenderBuffer even if no color
+ #if UNITY_WIN || UNITY_NACL
+ glGenRenderbuffers( 1, &rs.m_ColorBuffer );
+ glBindRenderbuffer( GL_RENDERBUFFER, rs.m_ColorBuffer );
+ glRenderbufferStorage( GL_RENDERBUFFER, GL_RGBA4, rs.width, rs.height );
+ DBG_LOG_RT_GLES20("Creating dummy color buffer (GL_RENDERBUFFER) [%d] - Width(%d) x Height(%d)", rs.m_ColorBuffer, rs.width, rs.height);
+ #endif
+ }
+}
+
+
+static void CreateFBORenderDepthSurfaceGLES (RenderDepthSurfaceGLES2& rs)
+{
+ int textureTarget = kOpenGLESTextureDimensionTable[kTexDim2D];
+
+ GLenum depthFormat;
+ GLenum genericFormat;
+ GLenum typeFormat;
+
+ // TODO 24bit depth should be revisited
+ if (rs.depthFormat == kDepthFormat24 && gGraphicsCaps.hasStencil)
+ {
+ rs.depthWithStencil = true;
+ depthFormat = GL_DEPTH24_STENCIL8_OES;
+ genericFormat = GL_DEPTH_STENCIL_OES;
+ typeFormat = GL_UNSIGNED_INT_24_8_OES;
+ }
+ else if (rs.depthFormat == kDepthFormat24 && gGraphicsCaps.gles20.has24DepthForFBO)
+ {
+ depthFormat = GL_DEPTH_COMPONENT24_OES;
+ genericFormat = GL_DEPTH_COMPONENT;
+ typeFormat = GL_UNSIGNED_BYTE;
+ }
+ else if (gGraphicsCaps.gles20.hasNLZ)
+ {
+ depthFormat = 0x8E2C; // GL_DEPTH_COMPONENT16_NONLINEAR_NV
+ genericFormat = GL_DEPTH_COMPONENT;
+ typeFormat = GL_UNSIGNED_SHORT;
+ }
+ else
+ {
+ depthFormat = GL_DEPTH_COMPONENT16;
+ genericFormat = GL_DEPTH_COMPONENT;
+ typeFormat = GL_UNSIGNED_SHORT;
+ }
+
+ GLuint targetDepthTex = (GLuint)TextureIdMap::QueryNativeTexture(rs.textureID);
+ if (!targetDepthTex)
+ {
+ // Note : For GLES2.0Emu we must create RenderBuffer even if depth format is none
+#if !UNITY_WIN
+ if( rs.depthFormat != kDepthFormatNone )
+#endif
+ {
+ GLES_CHK(glGenRenderbuffers( 1, &rs.m_DepthBuffer ));
+ GLES_CHK(glBindRenderbuffer( GL_RENDERBUFFER, rs.m_DepthBuffer ));
+
+ if(rs.samples > 1)
+ {
+ if(gGraphicsCaps.gles20.hasAppleMSAA)
+ GLES_CHK(gGlesExtFunc.glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, rs.samples, depthFormat, rs.width, rs.height));
+ else if(gGraphicsCaps.gles20.hasImgMSAA)
+ GLES_CHK(gGlesExtFunc.glRenderbufferStorageMultisampleIMG(GL_RENDERBUFFER, rs.samples, depthFormat, rs.width, rs.height));
+ }
+ else
+ {
+ GLES_CHK(glRenderbufferStorage( GL_RENDERBUFFER, depthFormat, rs.width, rs.height ));
+ }
+
+ DBG_LOG_RT_GLES20("Creating FBO Depth Surface (GL_RENDERBUFFER) [%d] - Width(%d) x Height(%d)", rs.m_DepthBuffer, rs.width, rs.height);
+ }
+ }
+ else
+ {
+ // create depth texture
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, rs.textureID, kTexDim2D, std::numeric_limits<float>::infinity());
+ GLES_CHK(glTexImage2D( textureTarget, 0, genericFormat, rs.width, rs.height, 0, genericFormat, typeFormat, NULL ));
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ if (rs.flags & kSurfaceCreateShadowmap)
+ {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_EXT, GL_COMPARE_REF_TO_TEXTURE_EXT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_EXT, GL_LEQUAL);
+ }
+
+ DBG_LOG_RT_GLES20("Creating FBO Depth Surface (GL_TEXTURE_2D) [%d] - Width(%d) x Height(%d)", rs.textureID.m_ID, rs.width, rs.height);
+ }
+}
+
+static RenderColorSurfaceGLES2* CreateRenderColorSurfaceGLES2Impl(void* rs, TextureID textureID, unsigned rbID, int width, int height, TextureDimension dim, UInt32 createFlags, RenderTextureFormat format, int samples)
+{
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(textureID);
+ Assert(rbID == 0 || targetTex == 0);
+
+ if( !gGraphicsCaps.hasRenderToTexture || !gGraphicsCaps.supportsRenderTextureFormat[format] )
+ return 0;
+
+ RenderColorSurfaceGLES2* ret = rs ? (RenderColorSurfaceGLES2*)rs : new RenderColorSurfaceGLES2;
+ RenderSurfaceBase_InitColor(*ret);
+ ret->width = width;
+ ret->height = height;
+ ret->format = format;
+ ret->dim = dim;
+ ret->flags = createFlags;
+ ret->samples = samples > gGraphicsCaps.gles20.maxSamples ? gGraphicsCaps.gles20.maxSamples : samples;
+
+ ret->textureID = textureID;
+ ret->m_ColorBuffer = rbID;
+
+ if(textureID.m_ID && !rbID)
+ TextureIdMapGLES20_QueryOrCreate(textureID);
+
+ return ret;
+}
+
+RenderSurfaceHandle CreateRenderColorSurfaceGLES2 (TextureID textureID, unsigned rbID, int width, int height, TextureDimension dim, UInt32 createFlags, RenderTextureFormat format, int samples)
+{
+ RenderColorSurfaceGLES2* rs = CreateRenderColorSurfaceGLES2Impl(0, textureID, rbID, width, height, dim, createFlags, format, samples);
+ if(rs)
+ CreateFBORenderColorSurfaceGLES(*rs);
+
+ return RenderSurfaceHandle(rs);
+}
+
+static RenderDepthSurfaceGLES2* CreateRenderDepthSurfaceGLES2Impl(void* rs, TextureID textureID, unsigned rbID, int width, int height, UInt32 createFlags, DepthBufferFormat depthFormat, int samples)
+{
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(textureID);
+ Assert(rbID == 0 || targetTex == 0);
+
+ if( !gGraphicsCaps.hasRenderToTexture )
+ return 0;
+
+ RenderDepthSurfaceGLES2* ret = rs ? (RenderDepthSurfaceGLES2*)rs : new RenderDepthSurfaceGLES2;
+ RenderSurfaceBase_InitDepth(*ret);
+ ret->depthWithStencil = false;
+ ret->width = width;
+ ret->height = height;
+ ret->depthFormat = depthFormat;
+ ret->flags = createFlags;
+ ret->samples = samples > gGraphicsCaps.gles20.maxSamples ? gGraphicsCaps.gles20.maxSamples : samples;
+
+ ret->textureID = textureID;
+ ret->m_DepthBuffer = rbID;
+
+ if(textureID.m_ID && !rbID)
+ TextureIdMapGLES20_QueryOrCreate(textureID);
+
+ return ret;
+}
+
+
+RenderSurfaceHandle CreateRenderDepthSurfaceGLES2 (TextureID textureID, unsigned rbID, int width, int height, UInt32 createFlags, DepthBufferFormat depthFormat, int samples)
+{
+ RenderDepthSurfaceGLES2* rs = CreateRenderDepthSurfaceGLES2Impl(0, textureID, rbID, width, height, createFlags, depthFormat, samples);
+ if(rs)
+ CreateFBORenderDepthSurfaceGLES(*rs);
+
+ return RenderSurfaceHandle(rs);
+}
+
+
+static void InternalDestroyRenderSurfaceGLES (RenderSurfaceBase* rs)
+{
+ AssertIf( !rs );
+
+ if (rs->textureID.m_ID)
+ {
+ GetRealGfxDevice().DeleteTexture( rs->textureID );
+ DBG_LOG_RT_GLES20("Destroying GL_TEXTURE_2D/GL_CUBE_MAP %d", rs->textureID.m_ID);
+ }
+ GLESAssert ();
+
+
+ RenderSurfaceHandle defaultColor = GetRealGfxDevice().GetBackBufferColorSurface();
+ RenderSurfaceHandle defaultDepth = GetRealGfxDevice().GetBackBufferDepthSurface();
+
+
+ for (int i = 0 ; i < s_ActiveColorTargetCount ; ++i)
+ {
+ if (s_ActiveColorTarget[i] == rs)
+ {
+ ErrorString( "RenderTexture warning: Destroying active render texture. Switching to main context." );
+ SetRenderTargetGLES2 (1, &defaultColor, defaultDepth, 0, kCubeFaceUnknown, gDefaultFBO);
+ }
+ }
+ if (s_ActiveDepthTarget == rs)
+ {
+ ErrorString( "RenderTexture warning: Destroying active render texture. Switching to main context." );
+ SetRenderTargetGLES2 (1, &defaultColor, defaultDepth, 0, kCubeFaceUnknown, gDefaultFBO);
+ }
+
+ OnFBOAttachmentDelete(rs);
+
+ if (rs->colorSurface)
+ {
+ RenderColorSurfaceGLES2* rsc = static_cast<RenderColorSurfaceGLES2*>(rs);
+ if( rsc->m_ColorBuffer )
+ {
+ GLES_CHK(glDeleteRenderbuffers( 1, &rsc->m_ColorBuffer ));
+ DBG_LOG_RT_GLES20("Destroying GL_RENDER_BUFFER %d", rsc->m_ColorBuffer);
+ }
+ }
+ else
+ {
+ RenderDepthSurfaceGLES2* rsd = static_cast<RenderDepthSurfaceGLES2*>(rs);
+ if( rsd->m_DepthBuffer )
+ {
+ GLES_CHK(glDeleteRenderbuffers( 1, &rsd->m_DepthBuffer ));
+ DBG_LOG_RT_GLES20("Destroying GL_RENDER_BUFFER %d", rsd->m_DepthBuffer);
+ }
+ }
+}
+
+
+void DestroyRenderSurfaceGLES2 (RenderSurfaceHandle& rsHandle)
+{
+ if(rsHandle.object->backBuffer)
+ return;
+
+ RenderSurfaceBase* rs = rsHandle.object;
+ InternalDestroyRenderSurfaceGLES (rs);
+ delete rs;
+ rsHandle.object = NULL;
+}
+
+void ResolveMSAA(GLuint dstTex, GLuint srcRB, GLuint globalSharedFBO, GLuint helperFBO)
+{
+ if(gGraphicsCaps.gles20.hasAppleMSAA)
+ {
+ GLint oldFBO;
+ GLES_CHK(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO));
+
+ GLES_CHK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, globalSharedFBO));
+ GLES_CHK(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_APPLE, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dstTex, 0));
+ GLES_CHK(glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER_APPLE, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0));
+ GLES_CHK(glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER_APPLE, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0));
+ ClearCurrentFBImpl(true, false);
+
+ GLES_CHK(glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, helperFBO));
+ GLES_CHK(glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER_APPLE, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, srcRB));
+ GLES_CHK(gGlesExtFunc.glResolveMultisampleFramebufferAPPLE());
+
+ DiscardCurrentFBImpl(true, false, GL_READ_FRAMEBUFFER_APPLE);
+ GLES_CHK(glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER_APPLE, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0));
+
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, oldFBO));
+ }
+}
+
+RenderSurfaceBase* ResolveMSAASetupFBO(void* screenRS, int format, GLuint globalSharedFBO, GLuint helperFBO)
+{
+ RenderSurfaceHandle ret;
+
+ RenderColorSurfaceGLES2* screen = (RenderColorSurfaceGLES2*)screenRS;
+ if(gGraphicsCaps.gles20.hasAppleMSAA)
+ {
+ TextureID texid = GetRealGfxDevice().CreateTextureID();
+ ret = CreateRenderColorSurfaceGLES2(texid, 0, screen->width, screen->height, kTexDim2D, 0, (RenderTextureFormat)format, 1);
+
+ GLuint gltex = (GLuint)TextureIdMap::QueryNativeTexture(texid);
+ ResolveMSAA(gltex, screen->m_ColorBuffer, globalSharedFBO, helperFBO);
+
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, globalSharedFBO));
+ GLES_CHK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gltex, 0));
+ }
+
+ return ret.object;
+}
+
+void ResolveMSAASetupFBO_Cleanup(RenderSurfaceBase* rs)
+{
+ RenderSurfaceHandle handle(rs);
+
+ if(handle.IsValid())
+ DestroyRenderSurfaceGLES2(handle);
+}
+
+
+void ResolveColorSurfaceGLES2(RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle, GLuint globalSharedFBO, GLuint helperFBO)
+{
+ // TODO: unify all the places we do resolve
+
+ Assert (srcHandle.IsValid());
+ Assert (dstHandle.IsValid());
+ RenderColorSurfaceGLES2* src = reinterpret_cast<RenderColorSurfaceGLES2*>(srcHandle.object);
+ RenderColorSurfaceGLES2* dst = reinterpret_cast<RenderColorSurfaceGLES2*>(dstHandle.object);
+ if (!src->colorSurface || !dst->colorSurface)
+ {
+ WarningString("RenderTexture: Resolving non-color surfaces.");
+ return;
+ }
+
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(dst->textureID);
+ if (!src->m_ColorBuffer || !targetTex)
+ {
+ WarningString("RenderTexture: Resolving NULL buffers.");
+ return;
+ }
+
+ ResolveMSAA(targetTex, src->m_ColorBuffer, globalSharedFBO, helperFBO);
+}
+
+void GrabIntoRenderTextureGLES2 (RenderSurfaceHandle rsHandle, RenderSurfaceHandle rdHandle, int x, int y, int width, int height, GLuint globalSharedFBO, GLuint helperFBO)
+{
+ if(!rsHandle.IsValid() || rsHandle.object->backBuffer)
+ return;
+
+ RenderColorSurfaceGLES2* rs = reinterpret_cast<RenderColorSurfaceGLES2*>(rsHandle.object);
+
+#if UNITY_IPHONE
+ GLint oldFBO = 0;
+ RenderSurfaceBase* resolveRS = 0;
+
+ if(IsActiveMSAARenderTargetGLES2() && gGraphicsCaps.gles20.hasAppleMSAA)
+ {
+ RenderColorSurfaceGLES2* screen = (RenderColorSurfaceGLES2*)UnityDefaultFBOColorBuffer();
+
+ bool fullScreenResolve = (x==0 && y==0 && width == screen->width && height == screen->height);
+ if(fullScreenResolve)
+ {
+ GLuint texColor = (GLuint)TextureIdMap::QueryNativeTexture(rs->textureID);
+ ResolveMSAA(texColor, screen->m_ColorBuffer, globalSharedFBO, helperFBO);
+
+ return;
+ }
+ else
+ {
+ // intentional fall-through - we setup resolved FBO
+ GLES_CHK(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO));
+ resolveRS = ResolveMSAASetupFBO(screen, rs->format, globalSharedFBO, helperFBO);
+ }
+ }
+#endif
+
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, rs->textureID, kTexDim2D, std::numeric_limits<float>::infinity());
+ GLES_CHK(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+
+ bool needsReadPixelsFallback = false;
+#if UNITY_WIN
+ // GLES 2.0 Emulator seems to have a bug, in that it can only do glCopyTexSubImage2D for power of two textures.
+ needsReadPixelsFallback = !IsPowerOfTwo(rs->width) || !IsPowerOfTwo(rs->height);
+#endif
+#if UNITY_ANDROID
+ // on pre-honeycomb devices AND in case of EGL_ANDROID_framebuffer_target
+ // we may end up with alphabits=0 explicitely, which, on some devices, cases FB to be considered RGB
+ {
+ GLint rbits=0, gbits=0, bbits=0, abits=0;
+ CHECK(glGetIntegerv(GL_RED_BITS, &rbits));
+ CHECK(glGetIntegerv(GL_GREEN_BITS, &gbits));
+ CHECK(glGetIntegerv(GL_BLUE_BITS, &bbits));
+ CHECK(glGetIntegerv(GL_ALPHA_BITS, &abits));
+
+ if(rbits==8 && gbits==8 && bbits==8 && abits==0)
+ needsReadPixelsFallback = true;
+ }
+#endif
+
+ if (needsReadPixelsFallback)
+ {
+ UInt8* data = NULL;
+ switch (rs->format)
+ {
+ case kRTFormatARGB32:
+ data = new UInt8[width*height*4];
+ GLES_CHK(glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data));
+ GLES_CHK(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data));
+ break;
+
+ default:
+ ErrorStringMsg ("Unsupported render texture format :%d", rs->format);
+ break;
+ }
+ delete [] data;
+ }
+ else
+ {
+ GLES_CHK(glCopyTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, x, y, width, height));
+ }
+
+#if UNITY_IPHONE
+ if(resolveRS)
+ {
+ ResolveMSAASetupFBO_Cleanup(resolveRS);
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, oldFBO));
+ }
+#endif
+}
+
+RenderTextureFormat GetCurrentFBColorFormatGLES20()
+{
+ if( s_ActiveColorTarget[0] )
+ return s_ActiveColorTarget[0]->format;
+
+ return QueryFBColorFormatGLES2();
+}
+
+void InitBackBufferGLES2(RenderSurfaceBase** outColor, RenderSurfaceBase** outDepth)
+{
+ RenderSurfaceBase_InitColor(s_BackBufferColor);
+ s_BackBufferColor.backBuffer = true;
+
+ RenderSurfaceBase_InitDepth(s_BackBufferDepth);
+ s_BackBufferDepth.backBuffer = true;
+
+ *outColor = s_ActiveColorTarget[0] = &s_BackBufferColor;
+ *outDepth = s_ActiveDepthTarget = &s_BackBufferDepth;
+}
+
+void SetBackBufferGLES2()
+{
+ s_ActiveColorTarget[0] = &s_BackBufferColor;
+ s_ActiveColorTargetCount= 0;
+ s_ActiveMip = 0;
+ s_ActiveFace = kCubeFaceUnknown;
+
+ s_ActiveDepthTarget = &s_BackBufferDepth;
+
+ EnsureDefaultFBInitedGLES2();
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, gDefaultFBO));
+}
+
+
+#if UNITY_IPHONE
+bool IsActiveMSAARenderTargetGLES2()
+{
+ return s_ActiveColorTarget[0] && !s_ActiveColorTarget[0]->backBuffer ? s_ActiveColorTarget[0]->samples > 1 : UnityDefaultFBOHasMSAA();
+}
+
+void* UnityCreateUpdateExternalColorSurfaceGLES2(void* surf, unsigned texid, unsigned rbid, int width, int height, bool is32bit)
+{
+ TextureID tex = surf ? ((RenderSurfaceBase*)surf)->textureID : GetUncheckedGfxDevice().CreateTextureID();
+ TextureIdMap::UpdateTexture(tex, texid);
+
+ // TODO for android we will probably need to properly distinct 24/32
+ RenderTextureFormat format = is32bit ? kRTFormatARGB32 : kRTFormatRGB565;
+ return CreateRenderColorSurfaceGLES2Impl(surf, tex, rbid, width, height, kTexDim2D, 0, format, 1);
+}
+void* UnityCreateUpdateExternalDepthSurfaceGLES2(void* surf, unsigned texid, unsigned rbid, int width, int height, bool is24bit)
+{
+ TextureID tex = surf ? ((RenderSurfaceBase*)surf)->textureID : GetUncheckedGfxDevice().CreateTextureID();
+ TextureIdMap::UpdateTexture(tex, texid);
+
+ DepthBufferFormat format = is24bit ? kDepthFormat24 : kDepthFormat16;
+ return CreateRenderDepthSurfaceGLES2Impl(surf, tex, rbid, width, height, 0, format, 1);
+}
+
+void UnityDestroyExternalColorSurfaceGLES2(void* surf)
+{
+ delete (RenderColorSurfaceGLES2*)surf;
+}
+void UnityDestroyExternalDepthSurfaceGLES2(void* surf)
+{
+ delete (RenderDepthSurfaceGLES2*)surf;
+}
+#endif
+
+
+#endif // GFX_SUPPORTS_OPENGLES20
diff --git a/Runtime/GfxDevice/opengles20/RenderTextureGLES20.h b/Runtime/GfxDevice/opengles20/RenderTextureGLES20.h
new file mode 100644
index 0000000..b80a5a6
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/RenderTextureGLES20.h
@@ -0,0 +1,40 @@
+#ifndef UNITY_RENDER_TEXTURES_GLES20_H_
+#define UNITY_RENDER_TEXTURES_GLES20_H_
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/GfxDevice/GfxDeviceConfigure.h"
+#include "Runtime/GfxDevice/GfxDeviceObjects.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "IncludesGLES20.h"
+
+RenderSurfaceHandle CreateRenderColorSurfaceGLES2 (TextureID textureID, unsigned rbID, int width, int height, TextureDimension dim, UInt32 createFlags, RenderTextureFormat format, int samples);
+RenderSurfaceHandle CreateRenderDepthSurfaceGLES2 (TextureID textureID, unsigned rbID, int width, int height, UInt32 createFlags, DepthBufferFormat depthFormat, int samples);
+void DestroyRenderSurfaceGLES2 (RenderSurfaceHandle& rsHandle);
+bool SetRenderTargetGLES2 (int count, RenderSurfaceHandle* colorHandle, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face, unsigned int globalSharedFBO);
+RenderSurfaceHandle GetActiveRenderColorSurfaceGLES2 ();
+RenderSurfaceHandle GetActiveRenderDepthSurfaceGLES2 ();
+void GrabIntoRenderTextureGLES2 (RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height, GLuint globalSharedFBO, GLuint helperFBO);
+void ResolveColorSurfaceGLES2(RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle, GLuint globalSharedFBO, GLuint helperFBO);
+void DiscardContentsGLES2(RenderSurfaceHandle rs);
+
+int GetCurrentFBGLES2();
+void EnsureDefaultFBInitedGLES2();
+
+RenderTextureFormat GetCurrentFBColorFormatGLES20();
+
+void InitBackBufferGLES2(RenderSurfaceBase** color, RenderSurfaceBase** depth);
+void SetBackBufferGLES2();
+
+#if UNITY_IPHONE
+void* UnityCreateExternalColorSurfaceGLES2(unsigned texid, unsigned rbid, int width, int height, bool is32bit);
+void* UnityCreateExternalDepthSurfaceGLES2(unsigned texid, unsigned rbid, int width, int height, bool is24bit);
+void UnityDestroyExternalColorSurfaceGLES2(void* surf);
+void UnityDestroyExternalDepthSurfaceGLES2(void* surf);
+#endif
+
+#if UNITY_IPHONE
+bool IsActiveMSAARenderTargetGLES2();
+#endif
+
+
+#endif
diff --git a/Runtime/GfxDevice/opengles20/ShaderGeneratorGLES20.cpp b/Runtime/GfxDevice/opengles20/ShaderGeneratorGLES20.cpp
new file mode 100644
index 0000000..c9f4e78
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/ShaderGeneratorGLES20.cpp
@@ -0,0 +1,749 @@
+#include "UnityPrefix.h"
+#include "FixedFunctionStateGLES20.h"
+#include "ShaderGeneratorGLES20.h"
+#include "IncludesGLES20.h"
+#include "DebugGLES20.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/BuiltinShaderParams.h"
+#include "Runtime/GfxDevice/BuiltinShaderParamsNames.h"
+#include "Runtime/Utilities/BitUtility.h"
+
+#include <sstream>
+#include <assert.h>
+
+
+#define CMP_STATE(member) { \
+ if (a.member < b.member) \
+ return true; \
+ else if (b.member < a.member) \
+ return false; \
+}
+
+// builtins support only 4 lights
+const int kMaxEmulatedVertexLights = 4;//kMaxSupportedVertexLights;
+
+bool FullStateCompareGLES20::operator() (FixedFunctionStateGLES20 const& a, const FixedFunctionStateGLES20& b) const
+{
+ CMP_STATE(lightingEnabled)
+ CMP_STATE(specularEnabled)
+ CMP_STATE(lightCount)
+ CMP_STATE(onlyDirectionalLights)
+ CMP_STATE(lightType)
+ CMP_STATE(texUnitMatrix)
+ CMP_STATE(useUniformInsteadOfVertexColor)
+ CMP_STATE(useVertexColorAsAmbientAndDiffuse)
+ CMP_STATE(useVertexColorAsEmission)
+
+ CMP_STATE(fogMode)
+
+ CMP_STATE(texUnitCount);
+ for (int i = 0; i < a.texUnitCount; i++)
+ {
+ CMP_STATE(texUnitCube[i])
+ CMP_STATE(texUnitGen[i])
+ CMP_STATE(texUnitColorCombiner[i])
+ CMP_STATE(texUnitAlphaCombiner[i])
+ }
+
+ CMP_STATE(addSpecularAfterTexturing)
+ CMP_STATE(alphaTest)
+ CMP_STATE(setupPointSize)
+
+ return false; /* All equal, not lesser. */
+}
+
+bool VertexStateCompareGLES20::operator() (FixedFunctionStateGLES20 const& a, FixedFunctionStateGLES20 const& b) const
+{
+ CMP_STATE(lightingEnabled)
+ CMP_STATE(specularEnabled)
+ CMP_STATE(lightCount)
+ CMP_STATE(onlyDirectionalLights)
+ CMP_STATE(lightType)
+ CMP_STATE(texUnitMatrix)
+ CMP_STATE(useUniformInsteadOfVertexColor)
+ CMP_STATE(useVertexColorAsAmbientAndDiffuse)
+ CMP_STATE(useVertexColorAsEmission)
+
+ CMP_STATE(fogMode)
+ CMP_STATE(setupPointSize)
+
+ CMP_STATE(texUnitCount);
+ for (int i = 0; i < a.texUnitCount; i++)
+ {
+ CMP_STATE(texUnitCube[i])
+ CMP_STATE(texUnitGen[i])
+ }
+ return false; /* All equal, not lesser. */
+}
+
+bool FragmentStateCompareGLES20::operator() (FixedFunctionStateGLES20 const& a, FixedFunctionStateGLES20 const& b) const
+{
+ CMP_STATE(fogMode)
+ CMP_STATE(texUnitCount)
+
+ for (int i = 0; i < a.texUnitCount; i++)
+ {
+ CMP_STATE(texUnitCube[i])
+ CMP_STATE(texUnitGen[i])
+ CMP_STATE(texUnitColorCombiner[i])
+ CMP_STATE(texUnitAlphaCombiner[i])
+ }
+
+ CMP_STATE(addSpecularAfterTexturing)
+ CMP_STATE(alphaTest)
+
+ return false; /* All equal, not lesser. */
+}
+
+
+// --- VERTEX program ----------------------------------------------------------------------------
+
+std::string BuildVertexShaderSourceGLES20 (const FixedFunctionStateGLES20& state)
+{
+ DBG_SHADER_VERBOSE_GLES20("ShaderGeneratorGLES20::BuildVertexShaderSource()\n");
+ DBG_SHADER_VERBOSE_GLES20(" state: %s\n", state.ToString().c_str());
+
+ bool eyePositionRequired = (state.lightingEnabled && state.lightCount > 0 && !state.onlyDirectionalLights);
+ for (int i = 0; i < state.texUnitCount; i++)
+ if (state.texUnitGen[i]==kTexGenCubeReflect)
+ eyePositionRequired = true;
+
+ std::ostringstream src;
+
+ /* Standard uniforms. */
+ src << "uniform highp mat4 " << GetShaderInstanceMatrixParamName (kShaderInstanceMatMVP) << ";\n";
+ if (eyePositionRequired)
+ {
+ src << "uniform highp mat4 " << GetShaderInstanceMatrixParamName (kShaderInstanceMatMV) << ";\n";
+ }
+
+ /* Default attributes. */
+ src << "attribute highp vec4 _glesVertex;\n";
+ src << "attribute mediump vec3 _glesNormal;\n";
+ if (state.useUniformInsteadOfVertexColor)
+ src << "uniform lowp vec4 _glesFFColor;\n";
+ else
+ src << "attribute lowp vec4 _glesColor;\n";
+
+ /* Default varyings. */
+ src << "varying lowp vec4 v_color;\n";
+
+ if (state.fogMode > kFogDisabled)
+ {
+ src << "uniform highp vec4 _glesFogParams;\n";
+ src << "uniform lowp vec4 _glesFogColor;\n";
+ src << "varying lowp vec4 _glesFogColorPreMul;\n";
+ src << "varying lowp vec4 _glesFogVar;\n";
+ }
+
+ /* Texture coordinates and transformation matrices. */
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ src << "attribute " << (state.NeedTexUnitMatrix(i)?"highp":"mediump") << " vec4 _glesMultiTexCoord" << i << ";\n";
+ if (!state.texUnitCube[i])
+ {
+ if (state.texUnitGen[i] == kTexGenObject)
+ src << "varying highp vec4 v_texGenObjCoord" << i << ";\n";
+ else
+ src << "varying mediump vec2 v_texCoord" << i << ";\n";
+ }
+ else
+ {
+ src << "varying highp vec3 v_texCoord" << i << ";\n";
+ }
+
+ if(state.NeedTexUnitMatrix(i))
+ src << "uniform highp mat4 " << GetShaderInstanceMatrixParamName (kShaderInstanceMatTexture0 + i) << ";\n";
+ }
+
+ /* Handle color -> material mapping. */
+ const char* ambientColor = state.useVertexColorAsAmbientAndDiffuse ? "vertexColor" : "_glesFrontMaterial.ambient";
+ const char* diffuseColor = state.useVertexColorAsAmbientAndDiffuse ? "vertexColor" : "_glesFrontMaterial.diffuse";
+ const char* emissionColor = state.useVertexColorAsEmission ? "vertexColor" : "_glesFrontMaterial.emission";
+
+ /* Light params. */
+ if (state.lightingEnabled)
+ {
+ src << "struct LightModelParameters {\n";
+ src << " vec4 ambient;\n";
+ src << "};\n";
+
+ src << "struct MaterialParameters {\n";
+ src << " vec4 emission;\n";
+ src << " vec4 ambient;\n";
+ src << " vec4 diffuse;\n";
+ src << " vec4 specular;\n";
+ src << " float shininess;\n";
+ src << "};\n";
+
+ src << "uniform LightModelParameters _glesLightModel;\n";
+ src << "uniform MaterialParameters _glesFrontMaterial;\n";
+
+ if (state.lightCount > 0)
+ {
+ src << "struct LightSourceParameters {\n";
+ src << " vec4 diffuse;\n";
+ src << " vec4 position;\n";
+ src << " vec3 spotDirection;\n";
+ src << " vec4 atten;\n";
+ src << "};\n";
+
+ src << "uniform vec4 " << GetBuiltinVectorParamName(kShaderVecLightModelAmbient) << ";\n";
+ for (int q = 0; q < kMaxEmulatedVertexLights; ++q)
+ {
+ src << "uniform vec4 " << GetBuiltinVectorParamName(kShaderVecLight0Diffuse + q) << ";\n";
+ src << "uniform vec4 " << GetBuiltinVectorParamName(kShaderVecLight0Position + q) << ";\n";
+ src << "uniform vec4 " << GetBuiltinVectorParamName(kShaderVecLight0SpotDirection + q) << ";\n";
+ src << "uniform vec4 " << GetBuiltinVectorParamName(kShaderVecLight0Atten + q) << ";\n";
+ }
+
+ src << "uniform mat3 _glesNormalMatrix;\n";
+
+ /* Compute functions. */
+ src << "\nvec3 direction (vec4 from, vec4 to)\n";
+ src << "{\n";
+ src << " return (to.xyz * from.w - from.xyz * to.w);\n";
+ src << "}\n";
+
+ src << "\nvec3 computeLighting(LightSourceParameters light, vec3 dirToLight, vec3 eyeNormal, vec4 vertexColor)\n";
+ src << "{\n";
+ src << " float NdotL = max(dot(eyeNormal, dirToLight), 0.0);\n";
+ // \note in Unity, light ambient is always zero
+ src << " vec3 color = NdotL * " << diffuseColor << ".rgb * light.diffuse.rgb;\n";
+ if (state.specularEnabled)
+ {
+ src << " if (NdotL > 0.0)\n";
+ src << " {\n";
+ src << " vec3 h = normalize(dirToLight + vec3(0.0, 0.0, 1.0));\n";
+ src << " float HdotN = max(dot(eyeNormal, h), 0.0);\n";
+ // \note in Unity, light specular color is always the same as diffuse color
+ src << " color += pow(HdotN, _glesFrontMaterial.shininess) * _glesFrontMaterial.specular.rgb * light.diffuse.rgb;\n";
+ src << " }\n";
+ }
+ src << " return color;\n";
+ src << "}\n";
+
+ src << "\nvec3 computeDirLight(LightSourceParameters light, vec3 eyeNormal, vec4 vertexColor)\n";
+ src << "{\n";
+ src << " vec3 dirToLight = light.position.xyz;\n";
+ // \note D3D backend uses min(val, 1.0). We use clamp(), because Img's wrapper is buggy!
+ src << " return clamp(computeLighting(light, dirToLight, eyeNormal, vertexColor), 0.0, 1.0);\n";
+ src << "}\n";
+
+ src << "\nvec3 computePointLight(LightSourceParameters light, vec4 eyePosition, vec3 eyeNormal, vec4 vertexColor)\n";
+ src << "{\n";
+ src << " vec3 dirToLight = direction(eyePosition, light.position);\n";
+ src << " float distSqr = dot(dirToLight, dirToLight);\n";
+ // \note in Unity, const attenuation=1.0, linear=0.0
+ src << " float att = 1.0 / (1.0 + light.atten.z * distSqr);\n";
+ src << " dirToLight *= inversesqrt(distSqr);\n";
+ // \note D3D backend uses min(val, 1.0). We use clamp(), because Img's wrapper is buggy!
+ src << " return clamp(att * computeLighting(light, dirToLight, eyeNormal, vertexColor), 0.0, 1.0);\n";
+ src << "}\n";
+
+ src << "\nvec3 computeSpotLight(LightSourceParameters light, vec4 eyePosition, vec3 eyeNormal, vec4 vertexColor)\n";
+ src << "{\n";
+ src << " vec3 dirToLight = direction(eyePosition, light.position);\n";
+ src << " float distSqr = dot(dirToLight, dirToLight);\n";
+ // \note in Unity, const atten=1.0, linear=0.0
+ src << " float att = 1.0 / (1.0 + light.atten.z * distSqr);\n";
+ src << " dirToLight *= inversesqrt(distSqr);\n";
+ src << " float rho = max(dot(dirToLight, light.spotDirection), 0.0);\n";
+ src << " float spotAtt = (rho - light.atten.x) * light.atten.y;\n";
+ src << " spotAtt = clamp(spotAtt, 0.0, 1.0);\n";
+ // \note D3D backend uses min(val, 1.0). We use clamp(), because Img's wrapper is buggy!
+ src << " return clamp(att * spotAtt * computeLighting(light, dirToLight, eyeNormal, vertexColor), 0.0, 1.0);\n";
+ //src << " return computeLighting(light, dirToLight, eyeNormal, vertexColor);\n"; // DEBUG DEBUG
+
+ src << "}\n";
+ }
+ }
+
+
+
+ /* Main body. */
+ src << "\nvoid main()\n";
+ src << "{\n";
+
+ /* Vertex transformation. */
+ src << " gl_Position = " << GetShaderInstanceMatrixParamName (kShaderInstanceMatMVP) << " * _glesVertex;\n";
+ if(state.setupPointSize)
+ src << " gl_PointSize = 1.0;\n";
+
+ /* Unpack vertex color if necessary. */
+ if (state.useUniformInsteadOfVertexColor)
+ src << " vec4 vertexColor = _glesFFColor;\n";
+ else
+ src << " vec4 vertexColor = _glesColor;\n";
+
+ if (eyePositionRequired)
+ src << " highp vec4 eyePosition = " << GetShaderInstanceMatrixParamName (kShaderInstanceMatMV) << " * _glesVertex;\n";
+
+ /* Pass and transform texture coordinates. */
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ if (!state.texUnitCube[i])
+ {
+ if (state.texUnitGen[i] == kTexGenObject)
+ {
+ Assert(state.NeedTexUnitMatrix(i));
+ src << " v_texGenObjCoord" << i << " = ";
+ src << "(" << GetShaderInstanceMatrixParamName (kShaderInstanceMatTexture0 + i) << " * _glesMultiTexCoord" << i << ").xyzw;\n";
+ }
+ else
+ {
+ if(state.NeedTexUnitMatrix(i))
+ {
+ src << " vec4 tmpTexCoord" << i << " = (" << GetShaderInstanceMatrixParamName (kShaderInstanceMatTexture0 + i) << " * _glesMultiTexCoord" << i << ").xyzw;\n";
+ if(state.IsTexUnitProjected(i))
+ src << " v_texCoord" << i << " = tmpTexCoord" << i << ".xy / tmpTexCoord" << i << ".w;\n";
+ else
+ src << " v_texCoord" << i << " = tmpTexCoord" << i << ".xy;\n";
+ }
+ else
+ {
+ src << " v_texCoord" << i << " = _glesMultiTexCoord" << i << ".xy;\n";
+ }
+ }
+ }
+ else
+ {
+ src << " v_texCoord" << i << " = ";
+ src << "vec3(" << GetShaderInstanceMatrixParamName (kShaderInstanceMatTexture0 + i) << " * _glesMultiTexCoord" << i << ");\n";
+ if (state.texUnitGen[i] == kTexGenCubeReflect)
+ {
+ Assert(eyePositionRequired);
+ src << "{\n";
+ src << " vec3 n = v_texCoord"<< i <<".xyz;\n";
+ src << " v_texCoord"<< i <<" = reflect(eyePosition.xyz * eyePosition.w, n);\n";
+ src << "}\n";
+ }
+ }
+ }
+
+ switch (state.fogMode)
+ {
+ case kFogLinear:
+ src << " _glesFogVar = vec4(clamp (_glesFogParams.z * gl_Position.z + _glesFogParams.w, 0.0, 1.0)); _glesFogVar.a = 1.0;\n";
+ src << " _glesFogColorPreMul = _glesFogColor * (vec4(1.0)-_glesFogVar);\n";
+ break;
+ case kFogExp:
+ src << " float _patchFog = _glesFogParams.y * gl_Position.z;\n";
+ src << " _glesFogVar = vec4(clamp (exp2(-_patchFog), 0.0, 1.0)); _glesFogVar.a = 1.0;\n";
+ src << " _glesFogColorPreMul = _glesFogColor * (vec4(1.0)-_glesFogVar);\n";
+ break;
+ case kFogExp2:
+ src << " float _patchFog = _glesFogParams.x * gl_Position.z;\n";
+ src << " _patchFog = _patchFog * _patchFog;\n";
+ src << " _glesFogVar = vec4(clamp (exp2(-_patchFog), 0.0, 1.0)); _glesFogVar.a = 1.0;\n";
+ src << " _glesFogColorPreMul = _glesFogColor * (vec4(1.0)-_glesFogVar);\n";
+ break;
+ default:
+ break;
+ }
+
+ /* Vertex color computation. */
+ if (state.lightingEnabled)
+ {
+ src << " vec3 color = " << emissionColor << ".rgb + " << ambientColor << ".rgb * _glesLightModel.ambient.rgb;\n";
+// src << " color = vec3(0.0);\n"; // DEBUG DEBUG
+
+ if (state.lightCount > 0)
+ {
+ src << " vec3 eyeNormal = normalize(_glesNormalMatrix * _glesNormal);\n";
+ for (int i = 0; i < state.lightCount && i < kMaxEmulatedVertexLights; i++)
+ {
+ src << " LightSourceParameters light" << i << ";\n";
+ src << " light" << i << ".diffuse = " << GetBuiltinVectorParamName(kShaderVecLight0Diffuse + i) << ";\n";
+ src << " light" << i << ".position = " << GetBuiltinVectorParamName(kShaderVecLight0Position + i) << ";\n";
+ src << " light" << i << ".spotDirection = " << GetBuiltinVectorParamName(kShaderVecLight0SpotDirection + i) << ".xyz;\n";
+ src << " light" << i << ".atten = " << GetBuiltinVectorParamName(kShaderVecLight0Atten + i) << ";\n";
+
+ if(state.GetLightType(i) == kLightDirectional)
+ src << " color += computeDirLight(light" << i << ", eyeNormal, vertexColor);\n";
+ else if(state.GetLightType(i) == kLightSpot)
+ src << " color += computeSpotLight(light" << i << ", eyePosition, eyeNormal, vertexColor);\n";
+ else
+ src << " color += computePointLight(light" << i << ", eyePosition, eyeNormal, vertexColor);\n";
+
+ Assert(eyePositionRequired || state.GetLightType(i) == kLightDirectional);
+ }
+ }
+
+ src << " float alpha = " << diffuseColor << ".a;\n";
+ src << " v_color = vec4(color, alpha);\n";
+ }
+ else
+ {
+ src << " v_color = vertexColor;\n";
+ }
+
+ src << "}\n";
+
+ DBG_SHADER_VERBOSE_GLES20("Generated VERTEX program:\n%s\n---\n", src.str().c_str());
+
+ return src.str().c_str();
+}
+
+// --- FRAGMENT program ----------------------------------------------------------------------------
+
+
+static void AddTexOperandSrc (
+ std::ostringstream& src,
+ int unitNdx,
+ combiner::Channels channels,
+ combiner::Operand operand,
+ combiner::Source source)
+{
+ src << "(";
+
+ if (operand == combiner::kOperOneMinusSrcAlpha ||
+ operand == combiner::kOperOneMinusSrcColor)
+ {
+ src << "vec4(1.0) - ";
+ }
+
+ switch (source)
+ {
+ case combiner::kSrcPrimaryColor:
+ src << "v_color";
+ break;
+
+ case combiner::kSrcPrevious:
+ src << "prev";
+ break;
+
+ case combiner::kSrcTexture:
+ src << "texture";
+ break;
+
+ case combiner::kSrcConstant:
+ src << "_glesTextureEnvColor" << unitNdx;
+ break;
+
+ default:
+ printf_console("Error: Unsupported combiner source %d\n", source);
+ src << "vec4(1.0)"; /* Dummy value. */
+ }
+
+ src << ")";
+
+ switch (operand)
+ {
+ case combiner::kOperSrcColor:
+ case combiner::kOperOneMinusSrcColor:
+ if (channels == combiner::kRGBA)
+ src << ".rgba";
+ else if (channels == combiner::kRGB)
+ src << ".rgb";
+ else if (channels == combiner::kAlpha)
+ src << ".a";
+ break;
+
+ case combiner::kOperSrcAlpha:
+ case combiner::kOperOneMinusSrcAlpha:
+ if (channels == combiner::kRGBA)
+ src << ".aaaa";
+ else if (channels == combiner::kRGB)
+ src << ".aaa";
+ else if (channels == combiner::kAlpha)
+ src << ".a";
+ break;
+ }
+}
+
+static void AddTextureCombinerBody (std::ostringstream& src, int unitNdx, UInt32 combinerDesc, combiner::Channels channels)
+{
+ Assert(combiner::kRGBA == 0);
+ Assert(combiner::kRGB == 1);
+ Assert(combiner::kAlpha == 2);
+
+ const std::string channelTypes[] = { "vec4", "vec3", "float" };
+ const std::string channelMask[] = { "", ".rgb", ".a" };
+
+
+ combiner::Source sources[3];
+ combiner::Operand operands[3];
+ combiner::Operation op;
+ int scale;
+
+ combiner::DecodeTextureCombinerDescriptor(combinerDesc,
+ op, sources, operands, scale, false);
+
+ if ((op == combiner::kOpDot3RGBA || op == combiner::kOpDot3RGB))
+ {
+ if (channels == combiner::kAlpha)
+ return;
+ channels = combiner::kRGBA;
+ }
+
+ src << " color" << channelMask[channels] << " = ";
+
+ switch (op)
+ {
+ case combiner::kOpReplace:
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ break;
+
+ case combiner::kOpModulate:
+ {
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << " * ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ break;
+ }
+
+ case combiner::kOpAdd:
+ {
+ src << "(";
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << " + ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ src << ")";
+ break;
+ }
+
+ case combiner::kOpSubtract:
+ {
+ src << "(";
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << " - ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ src << ")";
+ break;
+ }
+
+ case combiner::kOpAddSigned:
+ {
+ src << "(";
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << " + ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ src << " - " << channelTypes[channels] << "(0.5)";
+ src << ")";
+ break;
+ }
+
+ case combiner::kOpLerp:
+ {
+ // NOTE: arguments of Unity LERP combiner are reversed for some reason
+ src << "mix(";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ src << ", ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << ", ";
+ AddTexOperandSrc(src, unitNdx, combiner::kAlpha, operands[2], sources[2]);
+ src << ")";
+ break;
+ }
+
+ case combiner::kOpDot3RGB:
+ {
+ if (channels == combiner::kRGBA)
+ {
+ src << channelTypes[channels] << "(vec3(4.0 * dot(";
+ AddTexOperandSrc(src, unitNdx, combiner::kRGB, operands[0], sources[0]);
+ src << " - vec3(0.5), ";
+ AddTexOperandSrc(src, unitNdx, combiner::kRGB, operands[1], sources[1]);
+ src << " - vec3(0.5))), ";
+ AddTexOperandSrc(src, unitNdx, combiner::kAlpha, operands[0], sources[0]);
+ src << ")";
+ // Note: I am really not sure what goes into alpha channel when dot3_rgb is performed, it's definetly not the from dot
+ // My best guess, that original alpha value is kept
+
+ }
+ else
+ {
+ src << channelTypes[channels] << "(4.0* dot(";
+ AddTexOperandSrc(src, unitNdx, combiner::kRGB, operands[0], sources[0]);
+ src << " - vec3(0.5), ";
+ AddTexOperandSrc(src, unitNdx, combiner::kRGB, operands[1], sources[1]);
+ src << " - vec3(0.5)))";
+ }
+ break;
+ }
+
+ case combiner::kOpDot3RGBA:
+ {
+ src << channelTypes[channels] << "(4.0 * dot(";
+ AddTexOperandSrc(src, unitNdx, combiner::kRGB, operands[0], sources[0]);
+ src << " - vec3(0.5), ";
+ AddTexOperandSrc(src, unitNdx, combiner::kRGB, operands[1], sources[1]);
+ src << " - vec3(0.5)))";
+ break;
+ }
+ case combiner::kOpMulAdd:
+ {
+ src << "(";
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << " * ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[2], sources[2]);
+ src << " + ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ src << ")";
+ break;
+ }
+ case combiner::kOpMulSub:
+ {
+ src << "(";
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << " * ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[2], sources[2]);
+ src << " - ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ src << ")";
+ break;
+ }
+ default:
+ ErrorString(Format("Error: Unsupported combiner operation %d\n", op).c_str());
+
+ /* Dummy value. */
+ src << channelTypes[channels] << "(1.0)";
+ break;
+ }
+
+ if (scale != 1)
+ src << " * float(" << scale << ".0)";
+ src << ";\n";
+}
+
+static void AddTextureCombinerSrc (std::ostringstream& src, int unitNdx, bool isCube, UInt32 colorCombiner, UInt32 alphaCombiner)
+{
+ src << " {\n /* Combiner " << unitNdx << " */\n";
+
+ /* Perform lookup. */
+ src << " lowp vec4 texture = " << (isCube ? "textureCube" : "texture2D") << "(u_sampler" << unitNdx << ", v_texCoord" << unitNdx << ");\n";
+
+ src << " lowp vec4 prev = " << ((unitNdx > 0)? "color": "v_color") << ";\n";
+
+ /* Combine. */
+ if (colorCombiner == alphaCombiner)
+ {
+ // In case of color and alpha combiner being the same
+ // we calc all 4 channels in a single operation
+ // as some GLSL compilers (iPhone) will silently fail on following:
+ // color.rgb = arg0.rgb (op) arg1.rgb
+ // color.a = arg0.a (op) arg1.a
+ // instead we spit:
+ // color = arg0 (op) arg1
+ // plus it is more readable
+ AddTextureCombinerBody(src, unitNdx, colorCombiner, combiner::kRGBA);
+ }
+ else
+ {
+ AddTextureCombinerBody(src, unitNdx, colorCombiner, combiner::kRGB);
+ AddTextureCombinerBody(src, unitNdx, alphaCombiner, combiner::kAlpha);
+ }
+
+ src << " }\n";
+}
+
+
+std::string BuildFragmentShaderSourceGLES20 (const FixedFunctionStateGLES20& state)
+{
+ DBG_SHADER_VERBOSE_GLES20("ShaderGeneratorGLES20::BuildFragmentShaderSource()\n");
+ DBG_SHADER_VERBOSE_GLES20(" state: %s\n", state.ToString().c_str());
+
+ std::ostringstream src;
+
+ bool alphaTestEnabled = state.alphaTest != kFuncDisabled &&
+ state.alphaTest != kFuncAlways;
+
+ /* Default varyings. */
+ src << "varying lowp vec4 v_color;\n";
+
+ /* Uniforms. */
+ if (alphaTestEnabled)
+ src << "uniform lowp float _glesAlphaTestReference;\n";
+
+ if (state.fogMode > kFogDisabled)
+ {
+ src << "varying lowp vec4 _glesFogColorPreMul;\n";
+ src << "varying lowp vec4 _glesFogVar;\n";
+ }
+
+ /* Texture units. */
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ if (!state.texUnitCube[i])
+ {
+ if (state.texUnitGen[i] == kTexGenObject)
+ src << "varying highp vec4 v_texGenObjCoord" << i << ";\n";
+ else
+ src << "varying mediump vec2 v_texCoord" << i << ";\n";
+
+ src << "uniform sampler2D u_sampler" << i << ";\n";
+ }
+ else
+ {
+ src << "varying highp vec3 v_texCoord" << i << ";\n";
+ src << "uniform samplerCube u_sampler" << i << ";\n";
+ }
+
+ src << "uniform lowp vec4 _glesTextureEnvColor" << i << ";\n";
+ }
+
+
+ /* Main body. */
+ src << "\nvoid main()\n";
+ src << "{\n";
+
+ /* Initialize color. */
+ src << " lowp vec4 color = v_color;\n";
+
+ /* Generate correct texCoords if we have texGenObject */
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ if (!state.texUnitCube[i] && state.texUnitGen[i] == kTexGenObject)
+ src << " highp vec2 v_texCoord" << i << " = v_texGenObjCoord" << i << ".xy / v_texGenObjCoord" << i << ".w;\n";
+ }
+
+ /* Texturing. */
+ for (int i = 0; i < state.texUnitCount; i++)
+ AddTextureCombinerSrc(src, i, state.texUnitCube[i], state.texUnitColorCombiner[i], state.texUnitAlphaCombiner[i]);
+
+ if (state.fogMode > kFogDisabled)
+ src << " gl_FragColor = color * _glesFogVar + _glesFogColorPreMul;\n";
+ else
+ src << " gl_FragColor = color;\n";
+
+ /* Alpha test. */
+ if (alphaTestEnabled)
+ {
+ Assert(gGraphicsCaps.gles20.hasAlphaTestQCOM == false);
+
+ if (state.alphaTest == kFuncNever)
+ {
+ // ToDo: Do we just discard everything, or skip drawing itself at vbo level?
+ src << " discard;\n";
+ }
+ else
+ {
+ // Reverse logic because we're using here 'discard'
+ static const char* s_cmpOps[] =
+ {
+ "", // kFuncDisabled
+ "", // kFuncNever
+ ">=", // kFuncLess
+ "!=", // kFuncEqual
+ ">", // kFuncLEqual
+ "<=", // kFuncGreater
+ "==", // kFuncNotEqual
+ "<", // kFuncGEqual
+ "", // kFuncAlways
+ };
+
+ src << " if (color.a " << s_cmpOps[state.alphaTest] << "_glesAlphaTestReference)\n";
+ src << " discard;\n";
+ }
+ }
+// src << " gl_FragColor = vec4(v_color.xyz, 1.0);\n"; // DEBUG DEBUG
+// src << " gl_FragColor = 0.5 * texture2D(u_sampler0, v_texCoord0);\n"; // DEBUG DEBUG
+// src << " gl_FragColor = vec4(_glesTextureEnvColor0.rgb, 1.0);\n"; // DEBUG DEBUG
+ src << "}\n";
+
+ DBG_SHADER_VERBOSE_GLES20("Generated FRAGMENT program:\n%s\n---\n", src.str().c_str());
+
+ return src.str().c_str();
+}
diff --git a/Runtime/GfxDevice/opengles20/ShaderGeneratorGLES20.h b/Runtime/GfxDevice/opengles20/ShaderGeneratorGLES20.h
new file mode 100644
index 0000000..26669a1
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/ShaderGeneratorGLES20.h
@@ -0,0 +1,24 @@
+#ifndef SHADERGENERATOR_GLES20_H
+#define SHADERGENERATOR_GLES20_H
+
+#include <string>
+
+class FixedFunctionStateGLES20;
+
+std::string BuildVertexShaderSourceGLES20 (const FixedFunctionStateGLES20& state);
+std::string BuildFragmentShaderSourceGLES20 (const FixedFunctionStateGLES20& state);
+
+struct FullStateCompareGLES20
+{
+ bool operator() (FixedFunctionStateGLES20 const& a, FixedFunctionStateGLES20 const& b) const;
+};
+struct VertexStateCompareGLES20
+{
+ bool operator() (FixedFunctionStateGLES20 const& a, FixedFunctionStateGLES20 const& b) const;
+};
+struct FragmentStateCompareGLES20
+{
+ bool operator() (FixedFunctionStateGLES20 const& a, FixedFunctionStateGLES20 const& b) const;
+};
+
+#endif /* SHADERGENERATOR_GLES20_H */
diff --git a/Runtime/GfxDevice/opengles20/TextureIdMapGLES20.h b/Runtime/GfxDevice/opengles20/TextureIdMapGLES20.h
new file mode 100644
index 0000000..eb0c6dd
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/TextureIdMapGLES20.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/GfxDevice/TextureIdMap.h"
+#include "IncludesGLES20.h"
+
+inline GLuint TextureIdMapGLES20_QueryOrCreate(TextureID texid)
+{
+ GLuint ret = (GLuint)TextureIdMap::QueryNativeTexture(texid);
+ if(ret == 0)
+ {
+ GLES_CHK(glGenTextures(1, &ret));
+ TextureIdMap::UpdateTexture(texid, ret);
+ }
+
+ return ret;
+}
diff --git a/Runtime/GfxDevice/opengles20/TexturesGLES20.cpp b/Runtime/GfxDevice/opengles20/TexturesGLES20.cpp
new file mode 100644
index 0000000..b300e85
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/TexturesGLES20.cpp
@@ -0,0 +1,633 @@
+#include "UnityPrefix.h"
+#include "TexturesGLES20.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/GfxDevice/TextureUploadUtils.h"
+#include "Runtime/Graphics/S3Decompression.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "IncludesGLES20.h"
+#include "AssertGLES20.h"
+#include "DebugGLES20.h"
+#include "TextureIdMapGLES20.h"
+
+
+#if GFX_SUPPORTS_OPENGLES20
+
+// these are used to fill in table without using too much width
+#define GL_ETC1 GL_ETC1_RGB8_OES
+#define GL_DXT1 GL_COMPRESSED_RGB_S3TC_DXT1_EXT
+#define GL_DXT3 GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
+#define GL_DXT5 GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
+#define GL_SRGBDXT1 GL_COMPRESSED_SRGB_S3TC_DXT1_NV
+#define GL_SRGBDXT3 GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_NV
+#define GL_SRGBDXT5 GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_NV
+#define GL_PVR1RGB2 GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG
+#define GL_PVR1RGBA2 GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG
+#define GL_PVR1RGB4 GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG
+#define GL_PVR1RGBA4 GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG
+#define GL_ATC4 GL_ATC_RGB_AMD
+#define GL_ATC8 GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD
+
+#define RESERVED_FORMAT -1,-1,-1,0,
+
+const static int kTextureFormatTable[kTexFormatTotalCount*4] =
+{
+ // for GLES format == internal format, so we keep only one
+ // format, srgb format, datatype, alternative format
+ 0, 0, 0, 0,
+ GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, 0, // Alpha8
+ GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 0, // ARGB4444
+ GL_RGB, GL_SRGB_EXT, GL_UNSIGNED_BYTE, 0, // RGB24
+ GL_RGBA, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE, 0, // RGBA32
+ -1, -1, -1, kTexFormatRGBA32, // ARGB32
+ GL_RGBA, GL_SRGB_ALPHA_EXT, GL_HALF_FLOAT_OES, kTexFormatRGBA32, // ARGBFloat
+ GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0, // RGB565
+ -1, -1, -1, kTexFormatRGB24, // BGR24
+ -1, -1, -1, kTexFormatAlpha8, // AlphaLum16
+ GL_DXT1, GL_SRGBDXT1, -1, 0, // DXT1
+ GL_DXT3, GL_SRGBDXT3, -1, 0, // DXT3
+ GL_DXT5, GL_SRGBDXT5, -1, 0, // DXT5
+ GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 0, // RGBA4444
+
+ RESERVED_FORMAT /*14*/ RESERVED_FORMAT /*15*/ RESERVED_FORMAT /*16*/ RESERVED_FORMAT /*17*/
+ RESERVED_FORMAT /*18*/ RESERVED_FORMAT /*19*/ RESERVED_FORMAT /*20*/ RESERVED_FORMAT /*21*/
+ RESERVED_FORMAT /*22*/ RESERVED_FORMAT /*23*/ RESERVED_FORMAT /*24*/ RESERVED_FORMAT /*25*/
+ RESERVED_FORMAT /*26*/ RESERVED_FORMAT /*27*/ RESERVED_FORMAT /*28*/ RESERVED_FORMAT /*29*/
+
+ GL_PVR1RGB2, -1, -1, 0, // PVRTC_RGB2
+ GL_PVR1RGBA2, -1, -1, 0, // PVRTC_RGBA2
+ GL_PVR1RGB4, -1, -1, 0, // PVRTC_RGB4
+ GL_PVR1RGBA4, -1, -1, 0, // PVRTC_RGBA4
+ GL_ETC1, -1, -1, 0, // ETC_RGB4
+ GL_ATC4, -1, -1, 0, // ATC_RGB4
+ GL_ATC8, -1, -1, 0, // ATC_RGBA8
+ GL_BGRA_EXT, GL_BGRA_EXT, GL_UNSIGNED_BYTE, 0, // BGRA32
+};
+
+#undef GL_ETC1
+#undef GL_DXT1
+#undef GL_DXT3
+#undef GL_DXT5
+#undef GL_PVR1RGB2
+#undef GL_PVR1RGBA2
+#undef GL_PVR1RGB4
+#undef GL_PVR1RGBA4
+#undef GL_ATC4
+#undef GL_ATC8
+#undef RESERVED_FORMAT
+
+
+static int IsSupportedTextureFormat(TextureFormat inFormat)
+{
+ return (kTextureFormatTable[inFormat*4+0] > 0) && gGraphicsCaps.supportsTextureFormat[inFormat];
+}
+
+static int RemapToAlternativeFormat (TextureFormat inFormat)
+{
+ int altFormat = kTextureFormatTable[inFormat*4+3];
+ return altFormat > 0 ? altFormat : kTexFormatRGBA32;
+}
+
+static void GetUncompressedTextureFormat (int inFormat, bool srgb, int* format, int* dataType)
+{
+ DebugAssertIf( IsAnyCompressedTextureFormat(inFormat) );
+ *format = kTextureFormatTable[inFormat*4 + (srgb?1:0)];
+ *dataType = kTextureFormatTable[inFormat*4 + 2];
+}
+
+static void GetCompressedTextureFormat (int inFormat, bool srgb, int width, int height, int* internalFormat, int* size)
+{
+ // srgb support will be enabled only if compressed srgb is supported
+ // also we will ever use only supported formats
+ *internalFormat = kTextureFormatTable[inFormat*4 + (srgb?1:0)];
+ Assert(*internalFormat != -1);
+
+ switch(inFormat)
+ {
+ case kTexFormatPVRTC_RGB4:
+ case kTexFormatPVRTC_RGBA4:
+ *size = (std::max(width, 8) * std::max(height, 8) * 4 + 7) / 8;
+ break;
+ case kTexFormatPVRTC_RGB2:
+ case kTexFormatPVRTC_RGBA2:
+ *size = (std::max(width, 16) * std::max(height, 8) * 2 + 7) / 8;
+ break;
+ case kTexFormatDXT1:
+ case kTexFormatATC_RGB4:
+ *size = ((width + 3) / 4) * ((height + 3) / 4) * 8;
+ break;
+ case kTexFormatDXT5:
+ case kTexFormatATC_RGBA8:
+ *size = ((width + 3) / 4) * ((height + 3) / 4) * 16;
+ break;
+ case kTexFormatETC_RGB4:
+ *size = 8 * ((width+3)>>2) * ((height+3)>>2); // 8 bytes per 4x4 block
+ break;
+ default:
+ Assert(false && "Texture is not compressed");
+ }
+}
+
+static TextureFormat GetUploadFormat(TextureFormat format, bool& uploadIsCompressed, bool& decompressOnTheFly)
+{
+ TextureFormat uploadFormat = format;
+ uploadIsCompressed = IsAnyCompressedTextureFormat(format);
+ decompressOnTheFly = false;
+ if (!IsSupportedTextureFormat(format))
+ {
+ if( uploadIsCompressed )
+ {
+ uploadIsCompressed = false;
+ decompressOnTheFly = true;
+ }
+
+ uploadFormat = RemapToAlternativeFormat(format);
+ }
+
+ if( decompressOnTheFly )
+ {
+ if (IsCompressedPVRTCTextureFormat(format))
+ {
+ printf_console ("WARNING: PVRTC texture format is not supported, decompressing texture\n");
+ }
+ else if (IsCompressedDXTTextureFormat(format))
+ {
+ printf_console ("WARNING: DXT texture format is not supported, decompressing texture\n");
+ }
+ else if (IsCompressedETCTextureFormat(format))
+ {
+ printf_console ("WARNING: ETC texture format is not supported, decompressing texture\n");
+ }
+ else if (IsCompressedATCTextureFormat(format))
+ {
+ printf_console ("WARNING: ATC texture format is not supported, decompressing texture\n");
+ }
+ }
+
+ return uploadFormat;
+}
+
+#if UNITY_PEPPER
+// Unity generates textures as ARGB4444, but GLES only supports RGBA4444.
+// On mobile GLES, we swizzle textures at build time, but on NaCl, we use the same data format as used
+// in the normal web player, so we cannot do that here. So we swizzle at runtime.
+// TODO for 4.0: See if there is any good reason to use ARGB4444 at all, or if we should switch to RGBA4444 completely!
+UInt8 *SwizzleRGBA4444 (UInt8 *srcData, UInt8 **decompressBuffer, int width, int height)
+{
+ if( *decompressBuffer == NULL )
+ *decompressBuffer = new UInt8[width * height * 2];
+
+ UInt8 *feedData = *decompressBuffer;
+ for (int q = 0; q < width * height; ++q)
+ {
+ // RGBA4444 <- ARGB4444
+ UInt32 argb = *(UInt16*)(srcData + q * 2);
+ UInt16 rgba = (argb << 4) | (0x0F & (argb >> 12));
+
+ feedData[q * 2 + 0] = (UInt8)rgba;
+ feedData[q * 2 + 1] = (UInt8)(rgba >> 8);
+ }
+ return feedData;
+}
+#endif
+
+void UploadTexture2DGLES2(
+ TextureID tid, TextureDimension dimension, UInt8* srcData, int width, int height,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, int masterTextureLimit, TextureColorSpace colorSpace )
+{
+ Assert( srcData != NULL );
+ AssertIf( (!IsPowerOfTwo(width) || !IsPowerOfTwo(height)) && !IsNPOTTextureAllowed(mipCount > 1) );
+ if( dimension != kTexDim2D )
+ {
+ ErrorStringMsg( "Incorrect texture dimension! (dimension = %d)", dimension );
+ return;
+ }
+
+ bool uploadIsCompressed, decompressOnTheFly;
+ TextureFormat uploadFormat = GetUploadFormat(format, uploadIsCompressed, decompressOnTheFly);
+
+ DBG_TEXTURE_VERBOSE_GLES20("Texture2D #%i (%ix%i) unityFmt: %d, uploadFmt: %d, compressed: %d, decompressOnCpu: %d",
+ tid.m_ID, width, height, format, uploadFormat, (int)uploadIsCompressed, (int)decompressOnTheFly);
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(tid.m_ID);
+
+ TextureIdMapGLES20_QueryOrCreate(tid);
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, tid, dimension, std::numeric_limits<float>::infinity());
+ GLES_CHK(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+
+ int maxLevel = mipCount - 1;
+ int baseLevel = std::min( masterTextureLimit, maxLevel );
+
+#if UNITY_IPHONE && GL_APPLE_texture_max_level
+ if( gGraphicsCaps.hasMipMaxLevel )
+ {
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL_APPLE, maxLevel));
+ }
+#endif
+
+ // xenon, for real?
+ bool isSRGB = (colorSpace == kTexColorSpaceSRGBXenon || colorSpace == kTexColorSpaceSRGB);
+
+ UInt8* decompressBuffer = NULL;
+ int glesFormat, datatype;
+ int uploadedSize = 0;
+ int skippedMipCount = 0;
+ for( int level = 0; level <= maxLevel; ++level )
+ {
+ UInt8* feedData;
+ int uploadLevel = level - baseLevel - skippedMipCount;
+
+ // Should this level be skipped because of master texture limit?
+ if( level < baseLevel )
+ {
+ feedData = NULL;
+ }
+ // Should this level be skipped because it is too large for hardware limits?
+ else if (width > gGraphicsCaps.maxTextureSize || height > gGraphicsCaps.maxTextureSize)
+ {
+ skippedMipCount++;
+ DBG_TEXTURE_VERBOSE_GLES20("Texture2D #%i mip level %i (%ix%i) is too big, skipping!", tid.m_ID, level, width, height);
+ feedData = NULL;
+ }
+ // Allocate temporary memory and decompress texture
+ else if( decompressOnTheFly )
+ {
+ Assert(uploadFormat == kTexFormatRGBA32);
+
+ int dstWidth = std::max( width, 4 );
+ int dstHeight = std::max( height, 4 );
+ int decompressedSize = CalculateImageSize( dstWidth, dstHeight, uploadFormat );
+ if( decompressBuffer == NULL )
+ decompressBuffer = new UInt8[decompressedSize];
+ feedData = decompressBuffer;
+ DecompressNativeTextureFormat ( format, width, height, (UInt32*)srcData, dstWidth, dstHeight, (UInt32*)feedData );
+
+ if (UNITY_IPHONE && (format == kTexFormatDXT3 || format == kTexFormatDXT5))
+ {
+ printf_console ("swapping DXT channels\n");
+
+ for (int q = 0; q < width * height; ++q)
+ {
+ std::swap(feedData[q * 4 + 0], feedData[q * 4 + 3]);
+ std::swap(feedData[q * 4 + 1], feedData[q * 4 + 2]);
+ }
+ }
+ }
+ // Allocate temporary memory and swizzle texture
+ else if (uploadFormat != format)
+ {
+ int decompressedSize = CalculateImageSize (width, height, uploadFormat);
+ if (decompressBuffer == NULL)
+ decompressBuffer = new UInt8[decompressedSize];
+ feedData = decompressBuffer;
+
+ ImageReference src (width, height, GetRowBytesFromWidthAndFormat (width, format), format, srcData);
+ ImageReference dst (width, height, GetRowBytesFromWidthAndFormat (width, uploadFormat), uploadFormat, feedData);
+ dst.BlitImage( src );
+ }
+#if UNITY_PEPPER
+ else if (uploadFormat == kTexFormatARGB4444)
+ {
+ feedData = SwizzleRGBA4444 (srcData, &decompressBuffer, width, height);
+ }
+#endif
+ else
+ {
+ // Just feed the data
+ feedData = srcData;
+ }
+
+ // If this level should be skipped, just do nothing
+ if( feedData == NULL )
+ {
+ }
+ else
+ {
+ if (uploadIsCompressed)
+ {
+ int size;
+ GetCompressedTextureFormat (uploadFormat, isSRGB, width, height, &glesFormat, &size);
+ if( glesFormat <= 0 )
+ {
+ ErrorString(Format("Format not supported: %u!", (unsigned)uploadFormat));
+ return;
+ }
+
+ DBG_TEXTURE_VERBOSE_GLES20("GLESDebug texture: glCompressedTexImage2D: level=%i fmt=%i width=%i height=%i type=%i data=%x",
+ uploadLevel, glesFormat, width, height, datatype, (UInt32)feedData);
+ GLES_CHK(glCompressedTexImage2D (GL_TEXTURE_2D, uploadLevel, glesFormat, width, height, 0, size, feedData));
+ }
+ else
+ {
+ GetUncompressedTextureFormat (uploadFormat, isSRGB, &glesFormat, &datatype);
+ if( glesFormat <= 0 )
+ {
+ ErrorString(Format("Format not supported: %u!", (unsigned)uploadFormat));
+ return;
+ }
+
+ int internalFormat = glesFormat;
+ #if UNITY_IPHONE
+ //APPLE_texture_format_BGRA8888 dictates internal format for GL_BGRA_EXT to be GL_RGBA
+ if(glesFormat == GL_BGRA_EXT)
+ internalFormat = GL_RGBA;
+ #endif
+
+ DBG_TEXTURE_VERBOSE_GLES20("GLESDebug texture: glTexImage2D: level=%i fmt=%i width=%i height=%i type=%i data=%x",
+ uploadLevel, glesFormat, width, height, datatype, (UInt32)feedData);
+ GLES_CHK(glTexImage2D (GL_TEXTURE_2D, uploadLevel, internalFormat, width, height, 0, glesFormat, datatype, feedData));
+ }
+ }
+ if( gGraphicsCaps.gles20.needFlushAfterTextureUpload )
+ GLES_CHK(glFlush());
+
+ // Go to next mip level
+ int levelSize = CalculateImageSize (width, height, format);;
+ uploadedSize += levelSize;
+ srcData += levelSize;
+ AssertIf( width == 1 && height == 1 && level != maxLevel );
+ width = std::max( width / 2, 1 );
+ height = std::max( height / 2, 1 );
+ }
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(tid.m_ID, uploadedSize, tid.m_ID);
+
+ AssertIf( baseLevel > maxLevel );
+
+ if( decompressBuffer )
+ delete[] decompressBuffer;
+}
+
+void UploadTextureSubData2DGLES2( TextureID glname, UInt8* srcData,
+ int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace )
+{
+ Assert( !IsAnyCompressedTextureFormat( format ) );
+
+ bool uploadIsCompressed, decompressOnTheFly;
+ TextureFormat uploadFormat = GetUploadFormat(format, uploadIsCompressed, decompressOnTheFly);
+ Assert( !uploadIsCompressed );
+ Assert( !decompressOnTheFly );
+
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(glname);
+
+ Assert(targetTex != 0);
+ if(targetTex == 0)
+ return;
+
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, glname, kTexDim2D, std::numeric_limits<float>::infinity());
+ GLES_CHK(glActiveTexture(GL_TEXTURE0));
+ GLES_CHK(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+
+ UInt8* decompressBuffer = NULL;
+ UInt8* feedData = srcData;
+
+ // Allocate temporary memory and swizzle texture
+ if( uploadFormat != format )
+ {
+ int decompressedSize = CalculateImageSize (width, height, uploadFormat);
+ if (decompressBuffer == NULL)
+ decompressBuffer = new UInt8[decompressedSize];
+ feedData = decompressBuffer;
+
+ ImageReference src (width, height, GetRowBytesFromWidthAndFormat (width, format), format, srcData);
+ ImageReference dst (width, height, GetRowBytesFromWidthAndFormat (width, uploadFormat), uploadFormat, feedData);
+ dst.BlitImage( src );
+ }
+#if UNITY_PEPPER
+ else if (uploadFormat == kTexFormatARGB4444)
+ {
+ feedData = SwizzleRGBA4444 (srcData, &decompressBuffer, width, height);
+ }
+#endif
+
+ // xenon, for real?
+ bool isSRGB = (colorSpace == kTexColorSpaceSRGBXenon || colorSpace == kTexColorSpaceSRGB);
+
+ int glesFormat, datatype;
+ GetUncompressedTextureFormat (uploadFormat, isSRGB, &glesFormat, &datatype);
+
+ GLES_CHK(glTexSubImage2D( GL_TEXTURE_2D, mipLevel, x, y, width, height, glesFormat, datatype, feedData ));
+
+ if( gGraphicsCaps.gles20.needFlushAfterTextureUpload )
+ GLES_CHK(glFlush());
+
+ if( decompressBuffer )
+ delete[] decompressBuffer;
+}
+
+void UploadTextureCubeGLES2(
+ TextureID tid, UInt8* srcData, int faceDataSize, int size,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace )
+{
+ bool uploadIsCompressed, decompressOnTheFly;
+ TextureFormat uploadFormat = GetUploadFormat(format, uploadIsCompressed, decompressOnTheFly);
+ DBG_TEXTURE_VERBOSE_GLES20("TextureCUBE #%i (%ix%ix6) unityFmt: %d, uploadFmt: %d, compressed: %d, decompressOnCpu: %d",
+ tid.m_ID, size, size, format, uploadFormat, (int)uploadIsCompressed, (int)decompressOnTheFly);
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(tid.m_ID);
+
+ TextureIdMapGLES20_QueryOrCreate(tid);
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, tid, kTexDimCUBE, std::numeric_limits<float>::infinity());
+ glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+
+ // xenon, for real?
+ bool isSRGB = (colorSpace == kTexColorSpaceSRGBXenon || colorSpace == kTexColorSpaceSRGB);
+
+ UInt8* decompressBuffer = NULL;
+
+ const GLenum faces[6] =
+ {
+ GL_TEXTURE_CUBE_MAP_POSITIVE_X,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
+ GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
+ GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
+ };
+ int uploadedSize = 0;
+ int maxLevel = mipCount - 1;
+ for (int face=0;face<6;face++)
+ {
+ int mipSize = size;
+ UInt8* data = srcData + face * faceDataSize;
+ int glesFormat, datatype, size;
+
+ for( int level=0;level<=maxLevel;level++ )
+ {
+ UInt8* feedData;
+
+ // Allocate temporary memory and decompress texture
+ if( decompressOnTheFly )
+ {
+ int dstSize = std::max( mipSize, 4 );
+ int decompressedSize = CalculateImageSize( dstSize, dstSize, uploadFormat );
+ if( decompressBuffer == NULL )
+ decompressBuffer = new UInt8[decompressedSize];
+ feedData = decompressBuffer;
+ DecompressNativeTextureFormat( format, mipSize, mipSize, (UInt32*)data, dstSize, dstSize, (UInt32*)feedData );
+ //SetUnpackClientStorage (false);
+ }
+ // Allocate temporary memory and swizzle texture
+ else if( uploadFormat != format )
+ {
+ int decompressedSize = CalculateImageSize (mipSize, mipSize, uploadFormat);
+ if (decompressBuffer == NULL)
+ decompressBuffer = new UInt8[decompressedSize];
+ feedData = decompressBuffer;
+
+ ImageReference src (mipSize, mipSize, GetRowBytesFromWidthAndFormat (mipSize, format), format, data);
+ ImageReference dst (mipSize, mipSize, GetRowBytesFromWidthAndFormat (mipSize, uploadFormat), uploadFormat, feedData);
+ dst.BlitImage( src );
+ }
+#if UNITY_PEPPER
+ else if (uploadFormat == kTexFormatARGB4444)
+ {
+ feedData = SwizzleRGBA4444 (srcData, &decompressBuffer, mipSize, mipSize);
+ }
+#endif
+ // Just feed the data
+ else
+ {
+ feedData = data;
+ }
+
+ // Upload
+ if( uploadIsCompressed )
+ {
+ GetCompressedTextureFormat( uploadFormat, isSRGB, mipSize, mipSize, &glesFormat, &size );
+ GLES_CHK(glCompressedTexImage2D (faces[face], level, glesFormat, mipSize, mipSize, 0, size, feedData));
+ }
+ else
+ {
+ GetUncompressedTextureFormat( uploadFormat, isSRGB, &glesFormat, &datatype );
+
+ int internalFormat = glesFormat;
+ #if UNITY_IPHONE
+ //APPLE_texture_format_BGRA8888 dictates internal format for GL_BGRA_EXT to be GL_RGBA
+ if(glesFormat == GL_BGRA_EXT)
+ internalFormat = GL_RGBA;
+ #endif
+
+ GLES_CHK(glTexImage2D (faces[face], level, internalFormat, mipSize, mipSize, 0, glesFormat, datatype, feedData));
+ }
+
+ if( gGraphicsCaps.gles20.needFlushAfterTextureUpload )
+ GLES_CHK(glFlush());
+
+ //GLAssert ();
+ int levelSize = CalculateImageSize( mipSize, mipSize, format );
+ uploadedSize += levelSize;
+ data += levelSize;
+ AssertIf( mipSize == 1 && level != maxLevel );
+
+ mipSize = std::max( mipSize / 2, 1 );
+ }
+ }
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(tid.m_ID, uploadedSize, tid.m_ID);
+
+ if( decompressBuffer )
+ delete[] decompressBuffer;
+}
+
+#if UNITY_IPHONE
+ extern bool IsActiveMSAARenderTargetGLES2();
+ extern RenderSurfaceBase* ResolveMSAASetupFBO(void*, int, GLuint, GLuint);
+ extern void ResolveMSAASetupFBO_Cleanup(RenderSurfaceBase*);
+
+ extern "C" void* UnityDefaultFBOColorBuffer();
+#endif
+
+bool ReadbackTextureGLES2(ImageReference& image, int left, int bottom, int width, int height, int destX, int destY, GLuint globalSharedFBO, GLuint helperFBO)
+{
+ bool result = true;
+
+#if UNITY_IPHONE
+ RenderSurfaceBase* resolveRS = 0;
+ GLint oldFBO = 0;
+ if(IsActiveMSAARenderTargetGLES2() && gGraphicsCaps.gles20.hasAppleMSAA)
+ {
+ GLES_CHK(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO));
+ resolveRS = ResolveMSAASetupFBO(UnityDefaultFBOColorBuffer(), 0, globalSharedFBO, helperFBO);
+ }
+#endif
+
+ int glesFormat, datatype;
+
+
+ // The whole image we're reading into can be larger than the rect we read, so setup the alignment.
+ GLES_CHK(glPixelStorei( GL_PACK_ALIGNMENT, 1 ));
+
+ void* dstImagePtr[kTexFormatPCCount] =
+ {
+ 0, //
+ image.GetRowPtr(destY) + destX, // kTexFormatAlpha8
+ 0, // kTexFormatARGB4444
+ image.GetRowPtr(destY) + destX * 3, // kTexFormatRGB24
+ image.GetRowPtr(destY) + destX * 4, // kTexFormatRGBA32
+ image.GetRowPtr(destY) + destX * 4, // kTexFormatARGB32
+ 0, // kTexFormatARGBFloat
+ image.GetRowPtr(destY) + destX * 2, // kTexFormatRGB565
+ 0, // kTexFormatBGR24
+
+ 0, // kTexFormatAlphaLum16
+ 0, // kTexFormatDXT1
+ 0, // kTexFormatDXT3
+ 0, // kTexFormatDXT5
+ 0 // kTexFormatRGBA4444
+ };
+
+ GetUncompressedTextureFormat( image.GetFormat(), false, &glesFormat, &datatype );
+ void* dstPtr = dstImagePtr[image.GetFormat()];
+ switch( image.GetFormat() )
+ {
+ case kTexFormatRGB565:
+ case kTexFormatRGBA32:
+ case kTexFormatARGB32:
+ case kTexFormatRGB24:
+ case kTexFormatAlpha8:
+ {
+ int readFormat, readType;
+ GLES_CHK(glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat));
+ GLES_CHK(glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType));
+
+ // Check if reading format/type matches our image's format/type, and width (as glReadPixels knows nothing about dst pitch)
+ if (glesFormat == readFormat && datatype == readType && width == image.GetWidth())
+ {
+ GLES_CHK(glReadPixels(left, bottom, width, height, glesFormat, datatype, dstPtr));
+ }
+ else
+ {
+ // Read as GL_RGBA/GL_UNSIGNED_BYTE, because it's always valid in OpenGL ES, and convert it to specified image format
+ int readFormat = kTexFormatRGBA32;
+ int readFormatSize = CalculateImageSize (width, height, readFormat);
+
+ GetUncompressedTextureFormat( readFormat, false, &glesFormat, &datatype );
+
+ UInt8* data = new UInt8[readFormatSize];
+ GLES_CHK(glReadPixels(left, bottom, width, height, glesFormat, datatype, data));
+
+ ImageReference src (width, height, GetRowBytesFromWidthAndFormat (width, readFormat), readFormat, data);
+ ImageReference dst (width, height, image.GetRowBytes(), image.GetFormat(), dstPtr);
+ dst.BlitImage( src );
+
+ delete[]data;
+ }
+ }
+ break;
+ default:
+ AssertString ("Not Supported");
+ result = false;
+ }
+
+#if UNITY_IPHONE
+ if(resolveRS)
+ {
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, oldFBO));
+ ResolveMSAASetupFBO_Cleanup(resolveRS);
+ }
+#endif
+
+ return result;
+}
+
+#endif // GFX_SUPPORTS_OPENGLES20
diff --git a/Runtime/GfxDevice/opengles20/TexturesGLES20.h b/Runtime/GfxDevice/opengles20/TexturesGLES20.h
new file mode 100644
index 0000000..ded70e9
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/TexturesGLES20.h
@@ -0,0 +1,21 @@
+#ifndef UNITY_TEXTURES_GLES_H_
+#define UNITY_TEXTURES_GLES_H_
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Graphics/TextureFormat.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+class ImageReference;
+
+void UploadTexture2DGLES2(
+ TextureID glname, TextureDimension dimension, UInt8* srcData, int width, int height,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, int masterTextureLimit, TextureColorSpace colorSpace );
+void UploadTextureSubData2DGLES2(
+ TextureID glname, UInt8* srcData,
+ int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace );
+void UploadTextureCubeGLES2(
+ TextureID tid, UInt8* srcData, int faceDataSize, int size,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace );
+bool ReadbackTextureGLES2(ImageReference& image, int left, int bottom, int width, int height, int destX, int destY, unsigned int globalSharedFBO, unsigned int helperFBO);
+
+#endif // UNITY_TEXTURES_GLES_H_
diff --git a/Runtime/GfxDevice/opengles20/TimerQueryGLES20.cpp b/Runtime/GfxDevice/opengles20/TimerQueryGLES20.cpp
new file mode 100644
index 0000000..1a5887e
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/TimerQueryGLES20.cpp
@@ -0,0 +1,109 @@
+#include "UnityPrefix.h"
+#if ENABLE_PROFILER && GFX_SUPPORTS_OPENGLES20
+#include "TimerQueryGLES20.h"
+#include "AssertGLES20.h"
+#include "UnityGLES20Ext.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+
+bool TimerQueriesGLES::Init()
+{
+ bool hasHWSupport = false;
+ if( QueryExtension("GL_NV_timer_query") )
+ {
+ hasHWSupport = gGlesExtFunc.glGetQueryObjectuivEXT && gGlesExtFunc.glGenQueriesEXT;
+
+ #if UNITY_ANDROID
+ hasHWSupport = hasHWSupport && gGlesExtFunc.glQueryCounterNV && gGlesExtFunc.glGetQueryObjectui64vNV;
+ #endif
+ }
+
+ gGraphicsCaps.hasTimerQuery = hasHWSupport;
+ if(hasHWSupport)
+ GLES_CHK(gGlesExtFunc.glGenQueriesEXT(kQueriesCount, timestamp_gl));
+
+ nextIndex = 0;
+ return true;
+}
+
+void TimerQueriesGLES::BeginTimerQueries()
+{
+ GLES_CHK(glFlush());
+ SetTimestamp();
+}
+
+void TimerQueriesGLES::EndTimerQueries()
+{
+ GLES_CHK(glFlush());
+}
+
+unsigned TimerQueriesGLES::SetTimestamp()
+{
+#if UNITY_ANDROID
+ GLES_CHK(gGlesExtFunc.glQueryCounterNV(timestamp_gl[nextIndex], GL_TIMESTAMP_NV));
+#endif
+ unsigned ret = nextIndex;
+
+ ++nextIndex;
+ if(nextIndex == kQueriesCount)
+ nextIndex = 0;
+
+ return ret;
+}
+
+UInt64 TimerQueriesGLES::GetElapsedTime(unsigned idx, bool wait)
+{
+#if UNITY_ANDROID
+ GLuint available = 0;
+ GLES_CHK(gGlesExtFunc.glGetQueryObjectuivEXT(timestamp_gl[idx], GL_QUERY_RESULT_AVAILABLE_EXT, &available));
+ // sometimes timestamp might be not ready (we still dont know why)
+ // the only workaround would be to add glFlush into SetTimestamp
+ // but then some timings will be a bit off
+ if(wait)
+ {
+ for(unsigned i = 0 ; i < 100 && !available ; ++i)
+ {
+ GLES_CHK(gGlesExtFunc.glGetQueryObjectuivEXT(timestamp_gl[idx], GL_QUERY_RESULT_AVAILABLE_EXT, &available));
+ }
+ }
+
+ if(available)
+ {
+ unsigned prev_idx = idx > 0 ? idx-1 : kQueriesCount-1;
+
+ EGLuint64NV time1, time2;
+ GLES_CHK(gGlesExtFunc.glGetQueryObjectui64vNV(timestamp_gl[prev_idx], GL_QUERY_RESULT_EXT, &time1));
+ GLES_CHK(gGlesExtFunc.glGetQueryObjectui64vNV(timestamp_gl[idx], GL_QUERY_RESULT_EXT, &time2));
+
+ return time2-time1;
+ }
+#endif
+
+ return kInvalidProfileTime;
+
+}
+
+
+TimerQueryGLES::TimerQueryGLES()
+ : m_Index(0),
+ m_Time(kInvalidProfileTime)
+{
+}
+
+void TimerQueryGLES::Measure()
+{
+ m_Index = g_TimerQueriesGLES.SetTimestamp();
+ m_Time = kInvalidProfileTime;
+}
+
+ProfileTimeFormat TimerQueryGLES::GetElapsed(UInt32 flags)
+{
+ if(m_Time == kInvalidProfileTime)
+ m_Time = g_TimerQueriesGLES.GetElapsedTime(m_Index, (flags & kWaitRenderThread) != 0);
+
+ return m_Time;
+}
+
+TimerQueriesGLES g_TimerQueriesGLES;
+
+
+#endif
diff --git a/Runtime/GfxDevice/opengles20/TimerQueryGLES20.h b/Runtime/GfxDevice/opengles20/TimerQueryGLES20.h
new file mode 100644
index 0000000..67137eb
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/TimerQueryGLES20.h
@@ -0,0 +1,47 @@
+#ifndef TIMERQUERYGLES_H
+#define TIMERQUERYGLES_H
+
+#if ENABLE_PROFILER && GFX_SUPPORTS_OPENGLES20
+
+#include "IncludesGLES20.h"
+#include "Runtime/GfxDevice/GfxTimerQuery.h"
+
+struct TimerQueriesGLES
+{
+ enum
+ {
+ kQueriesCount = 128,
+ };
+
+ GLuint timestamp_gl[kQueriesCount];
+ unsigned nextIndex;
+
+ bool Init();
+
+ void BeginTimerQueries();
+ void EndTimerQueries();
+
+ unsigned SetTimestamp();
+ UInt64 GetElapsedTime(unsigned idx, bool wait);
+};
+extern TimerQueriesGLES g_TimerQueriesGLES;
+
+
+class TimerQueryGLES
+ : public GfxTimerQuery
+{
+public:
+
+ TimerQueryGLES();
+
+ virtual void Measure();
+ virtual ProfileTimeFormat GetElapsed(UInt32 flags);
+
+private:
+
+ unsigned m_Index;
+ ProfileTimeFormat m_Time;
+};
+
+#endif
+#endif
diff --git a/Runtime/GfxDevice/opengles20/UnityGLES20Ext.cpp b/Runtime/GfxDevice/opengles20/UnityGLES20Ext.cpp
new file mode 100644
index 0000000..5ba6532
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/UnityGLES20Ext.cpp
@@ -0,0 +1,40 @@
+#include "UnityPrefix.h"
+#include "IncludesGLES20.h"
+#include "UnityGLES20Ext.h"
+#include "Runtime/GfxDevice/opengles/ExtensionsGLES.h"
+
+void GlesExtFunc::InitExtFunc()
+{
+ glPushGroupMarkerEXT = (glPushGroupMarkerEXTFunc)GetGLExtProcAddress("glPushGroupMarkerEXT");
+ glPopGroupMarkerEXT = (glPopGroupMarkerEXTFunc)GetGLExtProcAddress("glPopGroupMarkerEXT");
+ glDiscardFramebufferEXT = (glDiscardFramebufferEXTFunc)GetGLExtProcAddress("glDiscardFramebufferEXT");
+ glGenQueriesEXT = (glGenQueriesEXTFunc)GetGLExtProcAddress("glGenQueriesEXT");
+ glDeleteQueriesEXT = (glDeleteQueriesEXTFunc)GetGLExtProcAddress("glDeleteQueriesEXT");
+ glGetQueryObjectuivEXT = (glGetQueryObjectuivEXTFunc)GetGLExtProcAddress("glGetQueryObjectuivEXT");
+
+ glGetProgramBinaryOES = (glGetProgramBinaryOESFunc)GetGLExtProcAddress("glGetProgramBinaryOES");
+ glProgramBinaryOES = (glProgramBinaryOESFunc)GetGLExtProcAddress("glProgramBinaryOES");
+
+ glMapBufferOES = (glMapBufferOESFunc)GetGLExtProcAddress("glMapBufferOES");
+ glUnmapBufferOES = (glUnmapBufferOESFunc)GetGLExtProcAddress("glUnmapBufferOES");
+
+ glMapBufferRangeEXT = (glMapBufferRangeEXTFunc)GetGLExtProcAddress("glMapBufferRangeEXT");
+ glFlushMappedBufferRangeEXT = (glFlushMappedBufferRangeEXTFunc)GetGLExtProcAddress("glFlushMappedBufferRangeEXT");
+
+ glRenderbufferStorageMultisampleAPPLE = (glRenderbufferStorageMultisampleAPPLEFunc)GetGLExtProcAddress("glRenderbufferStorageMultisampleAPPLE");
+ glResolveMultisampleFramebufferAPPLE = (glResolveMultisampleFramebufferAPPLEFunc)GetGLExtProcAddress("glResolveMultisampleFramebufferAPPLE");
+
+ glRenderbufferStorageMultisampleIMG = (glRenderbufferStorageMultisampleIMGFunc)GetGLExtProcAddress("glRenderbufferStorageMultisampleIMG");
+ glFramebufferTexture2DMultisampleIMG = (glFramebufferTexture2DMultisampleIMGFunc)GetGLExtProcAddress("glFramebufferTexture2DMultisampleIMG");
+
+ glRenderbufferStorageMultisampleEXT = (glRenderbufferStorageMultisampleEXTFunc)GetGLExtProcAddress("glRenderbufferStorageMultisampleEXT");
+ glFramebufferTexture2DMultisampleEXT = (glFramebufferTexture2DMultisampleEXTFunc)GetGLExtProcAddress("glFramebufferTexture2DMultisampleEXT");
+
+ glDrawBuffersNV = (glDrawBuffersNVFunc)GetGLExtProcAddress("glDrawBuffersNV");
+ glQueryCounterNV = (glQueryCounterNVFunc)GetGLExtProcAddress("glQueryCounterNV");
+ glGetQueryObjectui64vNV = (glGetQueryObjectui64vNVFunc)GetGLExtProcAddress("glGetQueryObjectui64vNV");
+
+ glAlphaFuncQCOM = (glAlphaFuncQCOMFunc)GetGLExtProcAddress("glAlphaFuncQCOM");
+}
+
+GlesExtFunc gGlesExtFunc;
diff --git a/Runtime/GfxDevice/opengles20/UnityGLES20Ext.h b/Runtime/GfxDevice/opengles20/UnityGLES20Ext.h
new file mode 100644
index 0000000..4ab0e1d
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/UnityGLES20Ext.h
@@ -0,0 +1,342 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/GfxDevice/opengles/ExtensionsGLES.h"
+
+// The reason to have this file is that we use plenty of gles extensions,
+// and not all of them are handled in (all) sdk we build with.
+// Still we do guard all the usages with runtime ifs, so there is no need to intro addidtional preprocessor magic
+
+
+// ----------------------------------------------------------------------------
+// Texture Formats
+//
+
+#ifndef GL_BGRA_EXT
+ #define GL_BGRA_EXT 0x80E1
+#endif
+#ifndef GL_ETC1_RGB8_OES
+ #define GL_ETC1_RGB8_OES 0x8D64
+#endif
+#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT
+ #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
+#endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
+ #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
+#endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
+ #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
+#endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
+ #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
+#endif
+#ifndef GL_COMPRESSED_SRGB_S3TC_DXT1_NV
+ #define GL_COMPRESSED_SRGB_S3TC_DXT1_NV 0x8C4C
+#endif
+#ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_NV
+ #define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_NV 0x8C4D
+#endif
+#ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_NV
+ #define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_NV 0x8C4E
+#endif
+#ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_NV
+ #define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_NV 0x8C4F
+#endif
+#ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG
+ #define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
+#endif
+#ifndef GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG
+ #define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
+#endif
+#ifndef GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG
+ #define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
+#endif
+#ifndef GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG
+ #define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
+#endif
+#ifndef GL_ATC_RGB_AMD
+ #define GL_ATC_RGB_AMD 0x8C92
+#endif
+#ifndef GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD
+ #define GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE
+#endif
+
+#ifndef GL_HALF_FLOAT_OES
+ #define GL_HALF_FLOAT_OES 0x8D61
+#endif
+#ifndef GL_SRGB_EXT
+ #define GL_SRGB_EXT 0x8C40
+#endif
+#ifndef GL_SRGB_ALPHA_EXT
+ #define GL_SRGB_ALPHA_EXT 0x8C42
+#endif
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_blend_minmax
+//
+
+#ifndef GL_MIN_EXT
+ #define GL_MIN_EXT 0x8007
+#endif
+
+#ifndef GL_MAX_EXT
+ #define GL_MAX_EXT 0x8008
+#endif
+
+// ----------------------------------------------------------------------------
+// GL_EXT_debug_marker
+//
+
+typedef void (*glPushGroupMarkerEXTFunc)(int len, const char* name);
+typedef void (*glPopGroupMarkerEXTFunc)();
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_discard_framebuffer
+//
+
+#ifndef GL_COLOR_EXT
+ #define GL_COLOR_EXT 0x1800
+#endif
+#ifndef GL_DEPTH_EXT
+ #define GL_DEPTH_EXT 0x1801
+#endif
+#ifndef GL_STENCIL_EXT
+ #define GL_STENCIL_EXT 0x1802
+#endif
+
+typedef void (*glDiscardFramebufferEXTFunc)(GLenum target, GLsizei numAttachments, const GLenum *attachments);
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_occlusion_query_boolean
+// Note: while we dont use occlusion queries, all queries ext are based on that one
+//
+
+#ifndef GL_QUERY_RESULT_EXT
+ #define GL_QUERY_RESULT_EXT 0x8866
+#endif
+#ifndef GL_QUERY_RESULT_AVAILABLE_EXT
+ #define GL_QUERY_RESULT_AVAILABLE_EXT 0x8867
+#endif
+
+typedef void (*glGenQueriesEXTFunc)(GLuint n, GLuint *ids);
+typedef void (*glDeleteQueriesEXTFunc)(GLuint n, const GLuint *ids);
+typedef void (*glBeginQueryEXTFunc)(GLuint target, GLuint id);
+typedef void (*glEndQueryEXTFunc)(GLuint target);
+typedef void (*glGetQueryObjectuivEXTFunc)(GLuint id, GLuint pname, GLuint *params);
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_shadow_samplers
+//
+
+#ifndef GL_TEXTURE_COMPARE_MODE_EXT
+ #define GL_TEXTURE_COMPARE_MODE_EXT 0x884C
+#endif
+#ifndef GL_TEXTURE_COMPARE_FUNC_EXT
+ #define GL_TEXTURE_COMPARE_FUNC_EXT 0x884D
+#endif
+#ifndef GL_COMPARE_REF_TO_TEXTURE_EXT
+ #define GL_COMPARE_REF_TO_TEXTURE_EXT 0x884E
+#endif
+#ifndef GL_SAMPLER_2D_SHADOW_EXT
+ #define GL_SAMPLER_2D_SHADOW_EXT 0x8B62
+#endif
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_texture_rg
+//
+
+#ifndef GL_RED_EXT
+ #define GL_RED_EXT 0x1903
+#endif
+#ifndef GL_RG_EXT
+ #define GL_RG_EXT 0x8227
+#endif
+
+
+// ----------------------------------------------------------------------------
+// GL_OES_get_program_binary
+//
+
+#ifndef GL_PROGRAM_BINARY_LENGTH_OES
+ #define GL_PROGRAM_BINARY_LENGTH_OES 0x8741
+#endif
+
+#ifndef GL_NUM_PROGRAM_BINARY_FORMATS_OES
+ #define GL_NUM_PROGRAM_BINARY_FORMATS_OES 0x87FE
+#endif
+
+typedef void (*glGetProgramBinaryOESFunc)(GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, GLvoid* binary);
+typedef void (*glProgramBinaryOESFunc)(GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length);
+
+
+// ----------------------------------------------------------------------------
+// GL_OES_mapbuffer
+//
+
+#ifndef GL_WRITE_ONLY_OES
+ #define GL_WRITE_ONLY_OES 0x88B9
+#endif
+
+typedef void* (*glMapBufferOESFunc)(GLenum target, GLenum access);
+typedef GLboolean (*glUnmapBufferOESFunc)(GLenum target);
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_map_buffer_range
+//
+
+#ifndef GL_MAP_READ_BIT_EXT
+ #define GL_MAP_READ_BIT_EXT 0x0001
+#endif
+#ifndef GL_MAP_WRITE_BIT_EXT
+ #define GL_MAP_WRITE_BIT_EXT 0x0002
+#endif
+#ifndef GL_MAP_INVALIDATE_RANGE_BIT_EXT
+ #define GL_MAP_INVALIDATE_RANGE_BIT_EXT 0x0004
+#endif
+#ifndef GL_MAP_INVALIDATE_BUFFER_BIT_EXT
+ #define GL_MAP_INVALIDATE_BUFFER_BIT_EXT 0x0008
+#endif
+#ifndef GL_MAP_FLUSH_EXPLICIT_BIT_EXT
+ #define GL_MAP_FLUSH_EXPLICIT_BIT_EXT 0x0010
+#endif
+#ifndef GL_MAP_UNSYNCHRONIZED_BIT_EXT
+ #define GL_MAP_UNSYNCHRONIZED_BIT_EXT 0x0020
+#endif
+
+typedef void* (*glMapBufferRangeEXTFunc)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
+typedef void (*glFlushMappedBufferRangeEXTFunc)(GLenum target, GLintptr offset, GLsizeiptr length);
+
+
+
+// ----------------------------------------------------------------------------
+// GL_APPLE_framebuffer_multisample
+//
+
+#ifndef GL_MAX_SAMPLES_APPLE
+ #define GL_MAX_SAMPLES_APPLE 0x8D57
+#endif
+#ifndef GL_READ_FRAMEBUFFER_APPLE
+ #define GL_READ_FRAMEBUFFER_APPLE 0x8CA8
+#endif
+#ifndef GL_DRAW_FRAMEBUFFER_APPLE
+ #define GL_DRAW_FRAMEBUFFER_APPLE 0x8CA9
+#endif
+
+
+typedef void (*glRenderbufferStorageMultisampleAPPLEFunc)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
+typedef void (*glResolveMultisampleFramebufferAPPLEFunc)(void);
+
+
+// ----------------------------------------------------------------------------
+// GL_IMG_multisampled_render_to_texture
+//
+
+#ifndef GL_MAX_SAMPLES_IMG
+ #define GL_MAX_SAMPLES_IMG 0x9135
+#endif
+
+typedef void (*glRenderbufferStorageMultisampleIMGFunc)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
+typedef void (*glFramebufferTexture2DMultisampleIMGFunc)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples);
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_multisampled_render_to_texture
+//
+
+#ifndef GL_MAX_SAMPLES_EXT
+#define GL_MAX_SAMPLES_EXT 0x8D57
+#endif
+
+typedef void (*glRenderbufferStorageMultisampleEXTFunc)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
+typedef void (*glFramebufferTexture2DMultisampleEXTFunc)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples);
+
+
+// ----------------------------------------------------------------------------
+// GL_NV_draw_buffers
+//
+
+#ifndef GL_MAX_DRAW_BUFFERS_NV
+ #define GL_MAX_DRAW_BUFFERS_NV 0x8824
+#endif
+#ifndef GL_COLOR_ATTACHMENT0_NV
+ #define GL_COLOR_ATTACHMENT0_NV 0x8CE0
+#endif
+
+typedef void (*glDrawBuffersNVFunc)(int len, const GLenum* bufs);
+
+
+// ----------------------------------------------------------------------------
+// GL_NV_timer_query
+//
+
+#ifndef GL_TIME_ELAPSED_NV
+ #define GL_TIME_ELAPSED_NV 0x88BF
+#endif
+#ifndef GL_TIMESTAMP_NV
+ #define GL_TIMESTAMP_NV 0x8E28
+#endif
+
+typedef unsigned long long int EGLuint64NV;
+
+typedef void (*glQueryCounterNVFunc)(GLuint target, GLuint id);
+typedef void (*glGetQueryObjectui64vNVFunc)(GLuint id, GLuint pname, EGLuint64NV *params);
+
+
+// ----------------------------------------------------------------------------
+// GL_QCOM_alpha_test
+//
+
+#ifndef GL_ALPHA_TEST_QCOM
+ #define GL_ALPHA_TEST_QCOM 0x0BC0
+#endif
+
+typedef void (*glAlphaFuncQCOMFunc)(GLenum func, GLfloat ref);
+
+
+// ----------------------------------------------------------------------------
+// common place to get function pointers
+//
+
+struct
+GlesExtFunc
+{
+ glPushGroupMarkerEXTFunc glPushGroupMarkerEXT;
+ glPopGroupMarkerEXTFunc glPopGroupMarkerEXT;
+ glDiscardFramebufferEXTFunc glDiscardFramebufferEXT;
+ glGenQueriesEXTFunc glGenQueriesEXT;
+ glDeleteQueriesEXTFunc glDeleteQueriesEXT;
+ glGetQueryObjectuivEXTFunc glGetQueryObjectuivEXT;
+
+ glGetProgramBinaryOESFunc glGetProgramBinaryOES;
+ glProgramBinaryOESFunc glProgramBinaryOES;
+
+ glMapBufferOESFunc glMapBufferOES;
+ glUnmapBufferOESFunc glUnmapBufferOES;
+
+ glMapBufferRangeEXTFunc glMapBufferRangeEXT;
+ glFlushMappedBufferRangeEXTFunc glFlushMappedBufferRangeEXT;
+
+
+ glRenderbufferStorageMultisampleAPPLEFunc glRenderbufferStorageMultisampleAPPLE;
+ glResolveMultisampleFramebufferAPPLEFunc glResolveMultisampleFramebufferAPPLE;
+
+ glRenderbufferStorageMultisampleIMGFunc glRenderbufferStorageMultisampleIMG;
+ glFramebufferTexture2DMultisampleIMGFunc glFramebufferTexture2DMultisampleIMG;
+
+ glRenderbufferStorageMultisampleEXTFunc glRenderbufferStorageMultisampleEXT;
+ glFramebufferTexture2DMultisampleEXTFunc glFramebufferTexture2DMultisampleEXT;
+
+ glDrawBuffersNVFunc glDrawBuffersNV;
+ glQueryCounterNVFunc glQueryCounterNV;
+ glGetQueryObjectui64vNVFunc glGetQueryObjectui64vNV;
+
+ glAlphaFuncQCOMFunc glAlphaFuncQCOM;
+
+ void InitExtFunc();
+};
+extern GlesExtFunc gGlesExtFunc;
diff --git a/Runtime/GfxDevice/opengles20/VBOGLES20.cpp b/Runtime/GfxDevice/opengles20/VBOGLES20.cpp
new file mode 100644
index 0000000..01b715b
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/VBOGLES20.cpp
@@ -0,0 +1,1559 @@
+#include "UnityPrefix.h"
+
+//chai
+//#define GFX_SUPPORTS_OPENGLES20 1
+
+#if GFX_SUPPORTS_OPENGLES20
+#include "VBOGLES20.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "Runtime/Utilities/Prefetch.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/GfxDevice/BatchRendering.h"
+#include "IncludesGLES20.h"
+#include "AssertGLES20.h"
+#include "GpuProgramsGLES20.h"
+#include "DebugGLES20.h"
+#include "Runtime/Profiler/TimeHelper.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/GfxDevice/GLESChannels.h"
+#include "Runtime/GfxDevice/GLDataBufferCommon.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+
+
+static const GLenum kTopologyGLES2[kPrimitiveTypeCount] = {
+ GL_TRIANGLES,
+ GL_TRIANGLE_STRIP,
+ GL_TRIANGLES,
+ GL_LINES,
+ GL_LINE_STRIP,
+ GL_POINTS,
+};
+
+template <typename T>
+inline T Align (T v, size_t alignment)
+{
+ return (v + (alignment-1)) & ~(alignment-1);
+}
+
+extern void VBOContainsColorGLES20 (bool flag);
+extern void GfxDeviceGLES20_SetDrawCallTopology(GfxPrimitiveType topology);
+
+#define DISABLE_GLES_CALLS 0
+#define DISABLE_DRAW_CALLS_ONLY 0
+
+
+static UInt32 sCurrentTargetMap = 0;
+void InvalidateVertexInputCacheGLES20()
+{
+ GLES_CHK(glDisableVertexAttribArray(GL_VERTEX_ARRAY));
+ GLES_CHK(glDisableVertexAttribArray(GL_NORMAL_ARRAY));
+ GLES_CHK(glDisableVertexAttribArray(GL_COLOR_ARRAY));
+ for (size_t q = 0; q < gGraphicsCaps.maxTexImageUnits; ++q)
+ {
+ if (GL_TEXTURE_ARRAY0 + q < gGraphicsCaps.gles20.maxAttributes)
+ {
+ GLES_CHK(glDisableVertexAttribArray(GL_TEXTURE_ARRAY0 + q));
+ }
+ }
+ sCurrentTargetMap = 0;
+}
+
+static UInt32 MaskUnavailableChannels(UInt32 targetMap, UInt32 unavailableChannels)
+{
+ if (unavailableChannels == 0)
+ return targetMap;
+
+ #define MASK_UNAVAILABLE_CHANNEL(schnl, vchnl) if (unavailableChannels & schnl) targetMap &= ~vchnl;
+
+ MASK_UNAVAILABLE_CHANNEL(VERTEX_FORMAT1(Vertex), kVtxChnVertex);
+ MASK_UNAVAILABLE_CHANNEL(VERTEX_FORMAT1(Color), kVtxChnColor);
+ MASK_UNAVAILABLE_CHANNEL(VERTEX_FORMAT1(Normal), kVtxChnNormal);
+ MASK_UNAVAILABLE_CHANNEL(VERTEX_FORMAT1(TexCoord0), kVtxChnTexCoord0);
+ MASK_UNAVAILABLE_CHANNEL(VERTEX_FORMAT1(TexCoord1), kVtxChnTexCoord1);
+ MASK_UNAVAILABLE_CHANNEL(VERTEX_FORMAT1(Tangent), kVtxChnTexCoord2);
+
+ #undef MASK_UNAVAILABLE_CHANNEL
+
+ return targetMap;
+}
+
+#if NV_STATE_FILTERING
+typedef struct
+{
+ GLint size;
+ GLenum type;
+ GLboolean normalized;
+ GLsizei stride;
+ GLuint buffer;
+ const GLvoid* pointer;
+} FilteredVertexAttribPointer;
+static GLuint boundBuffers[2];
+static FilteredVertexAttribPointer* currPointers = 0;
+static GLint max_attribs;
+
+void filteredInitGLES20()
+{
+ static bool firstCall = true;
+ if (!firstCall)
+ return;
+ firstCall = false;
+
+ memset(boundBuffers, 0, 2*sizeof(GLuint));
+
+ glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_attribs);
+ currPointers = new FilteredVertexAttribPointer[max_attribs]; // this memory is considered 'static' and is never freed.
+ memset(currPointers, 0x00, max_attribs * sizeof(FilteredVertexAttribPointer));
+}
+
+void filteredBindBufferGLES20(GLenum target, GLuint buffer, bool isImmediate)
+{
+ int index = 0;
+ if (target == GL_ELEMENT_ARRAY_BUFFER)
+ index = 1;
+
+ if ( (target != GL_ELEMENT_ARRAY_BUFFER && target != GL_ARRAY_BUFFER) ||
+ (boundBuffers[index] != buffer))
+ {
+ boundBuffers[index] = buffer;
+ glBindBuffer(target, buffer);
+
+ if (buffer && !isImmediate)
+ {
+ // TODO: This is to flush the matrix to the shader, but why is it needed?
+ // Isn't the matrix set when switching buffer elsewhere?
+ void GfxDeviceGLES20_MarkWorldViewProjDirty();
+ GfxDeviceGLES20_MarkWorldViewProjDirty();
+ }
+ }
+}
+
+void filteredVertexAttribPointerGLES20(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer)
+{
+ bool doCall = false;
+ if (index < 0 || index >= max_attribs)
+ {
+ doCall = true;
+ }
+ else
+ {
+ FilteredVertexAttribPointer &p = currPointers[index];
+ if (p.buffer != boundBuffers[0] || p.size != size || p.type != type || p.normalized != normalized || p.stride != stride || p.pointer != pointer)
+ {
+ doCall = true;
+ p.buffer = boundBuffers[0];
+ p.size = size;
+ p.type = type;
+ p.normalized = normalized;
+ p.stride = stride;
+ p.pointer = pointer;
+ }
+ }
+ if (doCall)
+ {
+ glVertexAttribPointer(index, size, type, normalized, stride, pointer);
+ }
+}
+
+void filteredDeleteBuffersGLES20(GLsizei n, const GLuint *buffers)
+{
+ if (buffers && n)
+ {
+ for (int i = 0; i < n; i++)
+ {
+ for (int j = 0; j < max_attribs; j++)
+ {
+ if (currPointers[j].buffer == buffers[i])
+ {
+ memset(&currPointers[j], 0, sizeof(FilteredVertexAttribPointer));
+ }
+ }
+ }
+ }
+ glDeleteBuffers(n, buffers);
+}
+
+#ifndef glBindBuffer
+#define glBindBuffer filteredBindBufferGLES20
+#endif
+#ifndef glDeleteBuffers
+#define glDeleteBuffers filteredDeleteBuffersGLES20
+#endif
+#ifndef glVertexAttribPointer
+#define glVertexAttribPointer filteredVertexAttribPointerGLES20
+#endif
+
+void StateFiltering_InvalidateVBOCacheGLES20()
+{
+ memset(boundBuffers, 0, 2*sizeof(GLuint));
+ if(currPointers)
+ memset(currPointers, 0x00, max_attribs * sizeof(FilteredVertexAttribPointer));
+}
+
+
+#endif
+
+
+#define SETUP_VERTEX_CHANNEL(vchnl, vcomp, glArray, norm, channelSize, channelType, stride, ptr) \
+ if (targetMap & vchnl) \
+ { \
+ if (channelsToEnable & vchnl) { \
+ GLES_CHK(glEnableVertexAttribArray(glArray)); \
+ } \
+ const ShaderChannel src = channels.GetSourceForTarget( vcomp ); \
+ GLES_CHK(glVertexAttribPointer(glArray, channelSize, channelType, norm, stride, ptr));\
+ } else if (channelsToDisable & vchnl) { \
+ GLES_CHK(glDisableVertexAttribArray(glArray)); \
+ } \
+
+#define SETUP_TEXCOORD_CHANNEL(vchnl, vcomp, glTex, setFunc) \
+ if (targetMap & vchnl) \
+ { \
+ if (channelsToEnable & vchnl) { \
+ GLES_CHK(glEnableVertexAttribArray(GL_TEXTURE_COORD_ARRAY)); \
+ } \
+ const ShaderChannel src = channels.GetSourceForTarget( vcomp ); \
+ setFunc; \
+ } else if (channelsToDisable & vchnl) { \
+ GLES_CHK(glEnableVertexAttribArray(GL_TEXTURE_COORD_ARRAY)); \
+ }
+
+// TODO: normalize normals???
+#define LINK_TEXCOORD_CHANNEL(glTex) \
+ GLES_CHK(glEnableVertexAttribArray(glTex)); \
+ GLES_CHK(glVertexAttribPointer(glTex, channelSizes[src], channelTypes[src], false, \
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src])));
+
+
+#if UNITY_ANDROID || UNITY_TIZEN
+static void WorkaroundMaliBug(const UInt32 strides[kShaderChannelCount], void* dataChannel[kShaderChannelCount])
+{
+ // on Mali (first devices) there is bug in driver
+ // that results in attributes from interleaved streams
+ // remain active, even though they are disabled
+ // as a workaround, find any non null ptr in dataChannel and set it
+
+ void* readPtr = 0;
+ UInt32 stride = 0;
+ for( unsigned i = 0 ; i < kShaderChannelCount && !readPtr ; ++i)
+ {
+ readPtr = dataChannel[i];
+ stride = strides[i];
+ }
+
+ GLES_CHK(glVertexAttribPointer(GL_VERTEX_ARRAY, 3, GL_FLOAT, false, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_COLOR_ARRAY, 4, GL_UNSIGNED_BYTE, true, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_NORMAL_ARRAY, 3, GL_FLOAT, false, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY0, 2, GL_FLOAT, false, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY1, 2, GL_FLOAT, false, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY2, 2, GL_FLOAT, false, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY3, 2, GL_FLOAT, false, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY4, 2, GL_FLOAT, false, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY5, 2, GL_FLOAT, false, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY6, 2, GL_FLOAT, false, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY7, 2, GL_FLOAT, false, stride, readPtr));
+
+ InvalidateVertexInputCacheGLES20();
+}
+#endif
+
+//Èç¹ûÊDz»ÓÃVBOµÄģʽ£¬Ö±½ÓÉÏ´«¶¥µãÊý¾Ý
+static void SetupVertexInput(const ChannelAssigns& channels, void* dataChannel[kShaderChannelCount], const UInt32 strides[kShaderChannelCount], const int channelSizes[kShaderChannelCount], const GLenum channelTypes[kShaderChannelCount], UInt32 unavailableChannels = 0)
+{
+#if DISABLE_GLES_CALLS
+ return;
+#endif
+ GfxDevice& device = GetRealGfxDevice();
+
+#if UNITY_ANDROID || UNITY_TIZEN
+ if( gGraphicsCaps.gles20.buggyDisableVAttrKeepsActive )
+ WorkaroundMaliBug(strides, dataChannel);
+#endif
+
+ UInt32 targetMap = MaskUnavailableChannels(channels.GetTargetMap(), unavailableChannels);
+
+ const UInt32 channelsDiff = sCurrentTargetMap ^ targetMap;
+ const UInt32 channelsToEnable = channelsDiff & targetMap;
+ const UInt32 channelsToDisable = channelsDiff & (~targetMap);
+
+ SETUP_VERTEX_CHANNEL(kVtxChnVertex, kVertexCompVertex, GL_VERTEX_ARRAY, false,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+
+ SETUP_VERTEX_CHANNEL(kVtxChnColor, kVertexCompColor, GL_COLOR_ARRAY, true,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+
+ SETUP_VERTEX_CHANNEL(kVtxChnNormal, kVertexCompNormal, GL_NORMAL_ARRAY, channelTypes[src] == GL_BYTE,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+
+
+ SETUP_VERTEX_CHANNEL(kVtxChnTexCoord0, kVertexCompTexCoord0, GL_TEXTURE_ARRAY0, channelTypes[src] == GL_BYTE,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+ SETUP_VERTEX_CHANNEL(kVtxChnTexCoord1, kVertexCompTexCoord1, GL_TEXTURE_ARRAY1, channelTypes[src] == GL_BYTE,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+ SETUP_VERTEX_CHANNEL(kVtxChnTexCoord2, kVertexCompTexCoord2, GL_TEXTURE_ARRAY2, channelTypes[src] == GL_BYTE,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+ SETUP_VERTEX_CHANNEL(kVtxChnTexCoord3, kVertexCompTexCoord3, GL_TEXTURE_ARRAY3, channelTypes[src] == GL_BYTE,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+ SETUP_VERTEX_CHANNEL(kVtxChnTexCoord4, kVertexCompTexCoord4, GL_TEXTURE_ARRAY4, channelTypes[src] == GL_BYTE,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+ SETUP_VERTEX_CHANNEL(kVtxChnTexCoord5, kVertexCompTexCoord5, GL_TEXTURE_ARRAY5, channelTypes[src] == GL_BYTE,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+ SETUP_VERTEX_CHANNEL(kVtxChnTexCoord6, kVertexCompTexCoord6, GL_TEXTURE_ARRAY6, channelTypes[src] == GL_BYTE,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+ SETUP_VERTEX_CHANNEL(kVtxChnTexCoord7, kVertexCompTexCoord7, GL_TEXTURE_ARRAY7, channelTypes[src] == GL_BYTE,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+
+ sCurrentTargetMap = targetMap;
+
+ // setup fixed function texGens
+ const UInt32 sourceMap = channels.GetSourceMap();
+ {
+ const ShaderChannel src = kShaderChannelVertex;
+ if( device.IsPositionRequiredForTexGen() && (sourceMap & (1 << src)) )
+ {
+ for (int texUnit = 0; texUnit < gGraphicsCaps.maxTexImageUnits; ++texUnit)
+ {
+ if( device.IsPositionRequiredForTexGen(texUnit) )
+ {
+ // pass position as tex-coord, if required by texgen operation
+ LINK_TEXCOORD_CHANNEL(GL_TEXTURE_ARRAY0 + texUnit);
+ Assert(texUnit < ARRAY_SIZE(sTexCoordChannels));
+ sCurrentTargetMap |= sTexCoordChannels[texUnit];
+ }
+ }
+ }
+ }
+
+ {
+ const ShaderChannel src = kShaderChannelNormal;
+ if( device.IsNormalRequiredForTexGen() && (sourceMap & (1 << src)) )
+ {
+ for (int texUnit = 0; texUnit < gGraphicsCaps.maxTexImageUnits; ++texUnit)
+ {
+ if( device.IsNormalRequiredForTexGen(texUnit) )
+ {
+ // pass normal as tex-coord, if required by texgen operation
+ LINK_TEXCOORD_CHANNEL(GL_TEXTURE_ARRAY0 + texUnit);
+ Assert(texUnit < ARRAY_SIZE(sTexCoordChannels));
+ sCurrentTargetMap |= sTexCoordChannels[texUnit];
+ }
+ }
+ }
+ }
+}
+
+/*
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+*/
+
+
+//chai:
+// called by
+// DrawVBO()
+// DrawCustomIndexed()
+void GLES2VBO::DrawInternal(int vertexBufferID, int indexBufferID, const ChannelAssigns& channels, void* indices, UInt32 indexCount, GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount)
+{
+ UInt32 unavailableChannels = GetUnavailableChannels(channels);
+
+ // should never happen; a dummy all-white vertex color array is always created by Mesh code
+ AssertIf( unavailableChannels & (1<<kShaderChannelColor) );
+
+#if DISABLE_GLES_CALLS
+ return;
+#endif
+
+ DBG_LOG_GLES20("---> GLES2VBO::DrawVBO indexCount:%d channels: %04X/%04X, unavailable: %04X", (int)indexCount, channels.GetTargetMap(), channels.GetSourceMap(), unavailableChannels, unavailableChannels);
+
+ VBOContainsColorGLES20 (channels.GetSourceForTarget (kVertexCompColor) == kShaderChannelColor);
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID));
+
+ // setup vertex state
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID));
+ void* channelData[kShaderChannelCount];
+ UInt32 channelStrides[kShaderChannelCount];
+ if (vertexBufferID != 0)
+ GetChannelOffsetsAndStrides(channelData, channelStrides);
+ else
+ GetChannelDataAndStrides(channelData, channelStrides);
+
+ int channelSizes[kShaderChannelCount];
+ GLenum channelTypes[kShaderChannelCount];
+ SetupGLESChannelSizes(m_Channels, channelSizes);
+ SetupGLESChannelTypes(m_Channels, channelTypes);
+ SetupVertexInput(channels, channelData, channelStrides, channelSizes, channelTypes, unavailableChannels);
+
+ GfxDeviceGLES20_SetDrawCallTopology(topology);
+
+ //chai: ÔÚÕâÀïÉèÖÃʹÓõÄshader£¬²¢ÉèÖÃuniforms
+ //stack:
+ // GfxDevice::BeforeDrawCall
+ // GpuProgramsGLES20::ApplyGpuProgramES20
+ GetRealGfxDevice().BeforeDrawCall(false);
+
+#if DBG_LOG_GLES20_ACTIVE
+ DumpVertexArrayStateGLES20();
+#endif
+
+ // draw
+#if !DISABLE_DRAW_CALLS_ONLY
+ ABSOLUTE_TIME drawDt = START_TIME;
+ GLenum gltopo = kTopologyGLES2[topology];
+ GLES_CHK(glDrawElements(gltopo, indexCount, GL_UNSIGNED_SHORT, indices));
+ drawDt = ELAPSED_TIME(drawDt);
+
+ int primCount = GetPrimitiveCount(indexCount, topology, false);
+ GetRealGfxDevice().GetFrameStats().AddDrawCall (primCount, drawVertexCount, drawDt);
+#endif
+}
+
+void GLES2VBO::DrawVBO (const ChannelAssigns& channels, UInt32 firstIndexByte, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 firstVertex, UInt32 vertexCount )
+{
+ int indexBufferID = m_IndexBufferID;
+ void* indexBufferData = (UInt8*)(m_IndexBufferID ? 0 : m_IBData.indices) + firstIndexByte;
+
+ // If we're drawing quads, convert them into triangles; into a temporary index buffer area
+ void* tempIndexBuffer = NULL;
+ if (topology == kPrimitiveQuads)
+ {
+ UInt32 ibCapacityNeeded = indexCount/4*6*2;
+
+ // Get IB space from shared buffer or just allocate
+ void* ibPtr;
+ if (gGraphicsCaps.gles20.slowDynamicVBO)
+ {
+ ibPtr = UNITY_MALLOC(kMemDynamicGeometry, ibCapacityNeeded);
+ tempIndexBuffer = ibPtr;
+ }
+ else
+ {
+ ibPtr = LockSharedBufferGLES20 (GL_ELEMENT_ARRAY_BUFFER, ibCapacityNeeded);
+ }
+ DebugAssert (ibPtr);
+
+ // Convert quads into triangles
+ FillIndexBufferForQuads ((UInt16*)ibPtr, ibCapacityNeeded, (const UInt16*)((UInt8*)m_ReadableIndices + firstIndexByte), indexCount/4);
+
+ // Finish up with temporary space
+ if (gGraphicsCaps.gles20.slowDynamicVBO)
+ {
+ indexBufferID = 0;
+ indexBufferData = ibPtr;
+ }
+ else
+ {
+ indexBufferID = UnlockSharedBufferGLES20 ();
+ indexBufferData = NULL;
+ }
+ indexCount = indexCount/4*6;
+ }
+
+ // Draw!
+ DrawInternal( m_UsesVBO ? m_VertexBufferID[m_CurrentBufferIndex] : 0, indexBufferID, channels, indexBufferData, indexCount,
+ topology, firstVertex, firstVertex+vertexCount, vertexCount);
+
+ // Release any temporary buffer we might have allocated
+ if (tempIndexBuffer)
+ {
+ UNITY_FREE(kMemDynamicGeometry, tempIndexBuffer);
+ }
+}
+
+void GLES2VBO::DrawCustomIndexed( const ChannelAssigns& channels, void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount )
+{
+ Assert (topology != kPrimitiveQuads); // only called by static batching; which only handles triangles
+
+ int ibo = 0;
+ if(!gGraphicsCaps.gles20.forceStaticBatchFromMem)
+ {
+ // we expect static batches to be quite large ibos, and in that case drawing from memory is worst case scenario
+ // sure, unless running on some buggy piece of sh**t
+ const size_t ibCapacity = indexCount * kVBOIndexSize;
+ void* dstIndices = LockSharedBufferGLES20 (GL_ELEMENT_ARRAY_BUFFER, ibCapacity, true);
+ DebugAssert (dstIndices);
+ memcpy (dstIndices, indices, ibCapacity);
+ ibo = UnlockSharedBufferGLES20 (0, true);
+ indices = 0;
+ }
+
+ DrawInternal( m_UsesVBO ? m_VertexBufferID[m_CurrentBufferIndex] : 0, ibo, channels, indices, indexCount,
+ topology, vertexRangeBegin, vertexRangeEnd, drawVertexCount);
+}
+
+GLES2VBO::GLES2VBO()
+: m_CurrentBufferIndex(0)
+, m_IndexBufferID(0)
+, m_IBSize(0)
+, m_ReadableIndices(0)
+, m_VBOUsage(GL_STATIC_DRAW)
+, m_IBOUsage(GL_STATIC_DRAW)
+, m_UsesVBO(false)
+, m_UsesIBO(false)
+{
+ ::memset(m_VertexBufferID, 0x0, sizeof(m_VertexBufferID));
+ ::memset(&m_IBData, 0, sizeof(m_IBData));
+}
+
+GLES2VBO::~GLES2VBO()
+{
+ Cleanup ();
+}
+
+void GLES2VBO::Cleanup()
+{
+ int bufferCount = HasStreamWithMode(kStreamModeDynamic) ? DynamicVertexBufferCount : 1;
+ glDeregisterBufferData(bufferCount, (GLuint*)m_VertexBufferID);
+ GLES_CHK(glDeleteBuffers(bufferCount, (GLuint*)m_VertexBufferID));
+ ::memset(m_VertexBufferID, 0x0, sizeof(m_VertexBufferID));
+
+ if (m_IndexBufferID)
+ {
+ glDeregisterBufferData(1, (GLuint*)&m_IndexBufferID);
+ GLES_CHK(glDeleteBuffers(1, (GLuint*)&m_IndexBufferID));
+ m_IndexBufferID = 0;
+ }
+
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, 0));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+}
+
+void GLES2VBO::Recreate()
+{
+ MarkBuffersLost();
+
+ if(m_VertexData.bufferSize > 0)
+ EnsureVerticesInited(true);
+
+ if(m_IBSize > 0)
+ EnsureIndicesInited();
+}
+
+bool GLES2VBO::IsVertexBufferLost() const
+{
+ return (m_VertexBufferID[m_CurrentBufferIndex] == 0 && m_UsesVBO);
+}
+
+bool GLES2VBO::IsIndexBufferLost() const
+{
+ return (m_IndexBufferID == 0 && m_UsesIBO);
+}
+
+void GLES2VBO::MarkBuffersLost()
+{
+ ::memset(m_VertexBufferID, 0x0, sizeof(m_VertexBufferID));
+ m_IndexBufferID = 0;
+
+ // we also want to pretend we use vbo/ibo to hit "lost" path
+ // in case of vbo we also need to get rid of bufferedvbo copy
+
+ if(!m_UsesVBO)
+ {
+ UnbufferVertexData();
+ m_UsesVBO = true;
+ }
+ m_UsesIBO = true;
+}
+
+
+bool GLES2VBO::MapVertexStream(VertexStreamData& outData, unsigned stream)
+{
+ DebugAssert(!IsAnyStreamMapped());
+ Assert(m_VertexData.bufferSize > 0);
+
+ if(HasStreamWithMode(kStreamModeDynamic))
+ m_CurrentBufferIndex = (m_CurrentBufferIndex + 1) % DynamicVertexBufferCount;
+ else
+ m_CurrentBufferIndex = 0;
+
+ // TODO: make it possible to change m_UsesVBO at runtime
+ // for now once we use mapbuffer, the buffered data is lost so no going back
+
+ if(m_UsesVBO && (gGraphicsCaps.gles20.hasMapbuffer || gGraphicsCaps.gles20.hasMapbufferRange))
+ {
+ static const int _MapRangeFlags = GL_MAP_WRITE_BIT_EXT | GL_MAP_INVALIDATE_BUFFER_BIT_EXT;
+
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, m_VertexBufferID[m_CurrentBufferIndex]));
+
+ void* buf = 0;
+ if(gGraphicsCaps.gles20.hasMapbufferRange)
+ buf = gGlesExtFunc.glMapBufferRangeEXT(GL_ARRAY_BUFFER, 0, m_VertexData.bufferSize, _MapRangeFlags);
+ else
+ buf = gGlesExtFunc.glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);
+
+ GLESAssert();
+ if(buf)
+ {
+ const StreamInfo& info = m_VertexData.streams[stream];
+ outData.buffer = (UInt8*)buf + info.offset;
+ outData.channelMask = info.channelMask;
+ outData.stride = info.stride;
+ outData.vertexCount = m_VertexData.vertexCount;
+
+ UnbufferVertexData();
+ m_IsStreamMapped[stream] = true;
+ }
+ return buf != 0;
+ }
+
+ return BufferedVBO::MapVertexStream(outData, stream);
+}
+
+void GLES2VBO::UnmapVertexStream( unsigned stream )
+{
+ Assert(m_IsStreamMapped[stream]);
+ Assert(m_VertexData.bufferSize > 0);
+
+ m_IsStreamMapped[stream] = false;
+ if(m_UsesVBO)
+ {
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, m_VertexBufferID[m_CurrentBufferIndex]));
+ if(gGraphicsCaps.gles20.hasMapbuffer || gGraphicsCaps.gles20.hasMapbufferRange)
+ GLES_CHK(gGlesExtFunc.glUnmapBufferOES(GL_ARRAY_BUFFER));
+ else
+ GLES_CHK(glBufferSubData(GL_ARRAY_BUFFER, 0, m_VertexData.bufferSize, m_VertexData.buffer));
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, 0));
+ }
+}
+
+// the threshold is pretty arbitrary:
+// from our tests on "slowDynamicVBO" gpus "small" buffers are faster drawn from mem, and "bigger" ones as buffers (like normal case)
+// so we just pick something as small buffer threshold and be gone
+
+static const int _SmallBufferSizeThreshold = 1024;
+
+bool GLES2VBO::ShouldUseVBO()
+{
+ if(HasStreamWithMode(kStreamModeDynamic) && gGraphicsCaps.gles20.slowDynamicVBO)
+ return m_VertexData.bufferSize > _SmallBufferSizeThreshold;
+
+ return true;
+}
+
+bool GLES2VBO::ShouldUseIBO()
+{
+ if(AreIndicesDynamic() && gGraphicsCaps.gles20.slowDynamicVBO)
+ return m_IBData.count * kVBOIndexSize > _SmallBufferSizeThreshold;
+
+ return true;
+}
+
+
+void GLES2VBO::EnsureVerticesInited(bool newBuffers)
+{
+ bool isDynamic = HasStreamWithMode(kStreamModeDynamic);
+
+ int createStart = 0;
+ int createCount = 0;
+ if(m_VertexBufferID[0] == 0)
+ {
+ // initial create
+ createCount = isDynamic ? DynamicVertexBufferCount : 1;
+ newBuffers = true;
+ }
+ else if(m_VertexBufferID[1] == 0 && isDynamic)
+ {
+ // changed to dynamic
+ createStart = 1;
+ createCount = DynamicVertexBufferCount - 1;
+ newBuffers = true;
+ }
+ else if(m_VertexBufferID[1] != 0 && !isDynamic)
+ {
+ // changed to static
+ glDeregisterBufferData(DynamicVertexBufferCount-1, (GLuint*)&m_VertexBufferID[1]);
+ GLES_CHK(glDeleteBuffers(DynamicVertexBufferCount-1, (GLuint*)&m_VertexBufferID[1]));
+ ::memset(&m_VertexBufferID[1], 0x0, (DynamicVertexBufferCount-1)*sizeof(int));
+ }
+
+ m_VBOUsage = isDynamic ? GL_STREAM_DRAW : GL_STATIC_DRAW;
+ m_UsesVBO = ShouldUseVBO();
+ if(m_UsesVBO)
+ {
+ if(newBuffers && createCount)
+ GLES_CHK(glGenBuffers(createCount, (GLuint*)&m_VertexBufferID[createStart]));
+
+ // TODO: the only reason to fill whole VB is for multi-stream support
+ // TODO: somehow check if we can get away with less data copied
+ for(int i = 0, count = isDynamic ? DynamicVertexBufferCount : 1 ; i < count ; ++i)
+ {
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, m_VertexBufferID[i]));
+ if(newBuffers)
+ {
+ GLES_CHK(glBufferData(GL_ARRAY_BUFFER, m_VertexData.bufferSize, m_VertexData.buffer, m_VBOUsage));
+ glRegisterBufferData(m_VertexBufferID[i], m_VertexData.bufferSize, this);
+ }
+ else
+ {
+ GLES_CHK(glBufferSubData(GL_ARRAY_BUFFER, 0, m_VertexData.bufferSize, m_VertexData.buffer));
+ }
+ GetRealGfxDevice().GetFrameStats().AddUploadVBO(m_VertexData.bufferSize);
+ }
+
+ // now the hacky stuff - we dont actually need extra mem copy as we will recreate VBO from mesh
+ // on the other hand we still need it if we want to map stream
+ // by coincidence ;-) we will map only when having dynamic streams, and kStreamModeWritePersist (for cloth)
+ if(!HasStreamWithMode(kStreamModeDynamic) && !HasStreamWithMode(kStreamModeWritePersist))
+ UnbufferVertexData();
+ }
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, 0));
+ m_CurrentBufferIndex = 0;
+}
+
+void GLES2VBO::EnsureIndicesInited()
+{
+ m_IBOUsage = AreIndicesDynamic() ? GL_STREAM_DRAW : GL_STATIC_DRAW;
+ m_UsesIBO = ShouldUseIBO();
+ if(m_UsesIBO)
+ {
+ const size_t size = CalculateIndexBufferSize(m_IBData);
+
+ if(!m_IndexBufferID)
+ GLES_CHK(glGenBuffers(1, (GLuint*)&m_IndexBufferID));
+
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IndexBufferID));
+ GLES_CHK(glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, m_IBData.indices, m_IBOUsage));
+ glRegisterBufferData(m_IndexBufferID, size, this);
+ GetRealGfxDevice().GetFrameStats().AddUploadIB(size);
+ m_IBSize = size;
+
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ }
+ else if(m_IndexBufferID)
+ {
+ glDeregisterBufferData(1, (GLuint*)&m_IndexBufferID);
+ GLES_CHK(glDeleteBuffers(1, (GLuint*)&m_IndexBufferID));
+ m_IndexBufferID = 0;
+ }
+}
+
+void GLES2VBO::UpdateVertexData( const VertexBufferData& srcBuffer )
+{
+ int oldVBSize = m_VertexData.bufferSize;
+
+ BufferedVBO::BufferAllVertexData(srcBuffer);
+ std::copy(srcBuffer.streams, srcBuffer.streams + kMaxVertexStreams, m_Streams);
+ std::copy(srcBuffer.channels, srcBuffer.channels + kShaderChannelCount, m_Channels);
+
+ EnsureVerticesInited(oldVBSize < m_VertexData.bufferSize);
+}
+
+void GLES2VBO::UpdateIndexData (const IndexBufferData& buffer)
+{
+ m_IBData.indices = buffer.indices;
+ m_IBData.count = buffer.count;
+ EnsureIndicesInited();
+
+ m_ReadableIndices = buffer.indices;
+}
+
+UInt32 GLES2VBO::GetUnavailableChannels(const ChannelAssigns& channels) const
+{
+ // Figure out which channels we can't find in the streams
+ UInt32 unavailableChannels = channels.GetSourceMap();
+ for (int stream = 0; stream < kMaxVertexStreams; stream++)
+ {
+ unavailableChannels &= ~m_Streams[stream].channelMask;
+ }
+ return unavailableChannels;
+}
+
+int GLES2VBO::GetRuntimeMemorySize() const
+{
+#if ENABLE_MEM_PROFILER
+ return GetMemoryProfiler()->GetRelatedMemorySize(this) +
+ GetMemoryProfiler()->GetRelatedIDMemorySize((UInt32)this);
+#else
+ return 0;
+#endif
+}
+
+/*
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+*/
+
+DynamicGLES2VBO::DynamicGLES2VBO()
+: m_LastRenderMode (kDrawIndexedTriangles)
+, m_VB (0)
+, m_LargeVB (0)
+, m_ActiveVB (0)
+, m_IB (0)
+, m_LargeIB (0)
+, m_ActiveIB (0)
+, m_QuadsIB (0)
+, m_IndexBufferQuadsID (0)
+, m_VtxSysMemStorage(0)
+, m_VtxSysMemStorageSize(0)
+, m_IdxSysMemStorage(0)
+, m_IdxSysMemStorageSize(0)
+{
+ for( int i = 0; i < kShaderChannelCount; ++i )
+ {
+ m_BufferChannel[i] = 0;
+ }
+
+ //chai: https://stackoverflow.com/questions/15297773/opengl-es-ios-drawing-performance-a-lot-slower-with-vbos-than-without
+ const bool willUseMemory = gGraphicsCaps.gles20.slowDynamicVBO;
+ const bool willUseOnlyMemory = willUseMemory && gGraphicsCaps.gles20.forceStaticBatchFromMem;
+
+ if(willUseMemory)
+ {
+ m_VtxSysMemStorageSize = 8096;
+ m_VtxSysMemStorage = (UInt8*)UNITY_MALLOC_ALIGNED(kMemDynamicGeometry, m_VtxSysMemStorageSize, 32);
+ m_IdxSysMemStorageSize = 4096;
+ m_IdxSysMemStorage = (UInt16*)UNITY_MALLOC_ALIGNED(kMemDynamicGeometry, m_IdxSysMemStorageSize, 32);
+ }
+
+ if(!willUseOnlyMemory)
+ {
+ if(!gGraphicsCaps.gles20.hasVBOOrphaning)
+ {
+ // total preallocated memory ~2MB, but it can grow!
+ // we expect mostly small VBs
+ m_VB = UNITY_NEW(SharedBuffer,kMemDynamicGeometry) (GL_ARRAY_BUFFER, 8096, 32, false);
+ m_IB = UNITY_NEW(SharedBuffer,kMemDynamicGeometry) (GL_ELEMENT_ARRAY_BUFFER, 4096, 16, false);
+ m_LargeVB = UNITY_NEW(SharedBuffer,kMemDynamicGeometry) (GL_ARRAY_BUFFER, 32768, 32, false);
+ m_LargeIB = UNITY_NEW(SharedBuffer,kMemDynamicGeometry) (GL_ELEMENT_ARRAY_BUFFER, 16384, 32, false);
+ }
+ else
+ {
+ //chai: Ö§³ÖVBO orphaning£¬±ÜÃâimplicit synchronization
+ m_VB = UNITY_NEW(SharedBuffer,kMemDynamicGeometry) (GL_ARRAY_BUFFER, 32768);
+ m_IB = UNITY_NEW(SharedBuffer,kMemDynamicGeometry) (GL_ELEMENT_ARRAY_BUFFER, 8096);
+ }
+ }
+}
+
+DynamicGLES2VBO::~DynamicGLES2VBO ()
+{
+ UNITY_FREE(kMemDynamicGeometry, m_VtxSysMemStorage);
+ UNITY_FREE(kMemDynamicGeometry, m_IdxSysMemStorage);
+
+ UNITY_DELETE (m_VB, kMemDynamicGeometry);
+ UNITY_DELETE (m_IB, kMemDynamicGeometry);
+ UNITY_DELETE (m_LargeVB, kMemDynamicGeometry);
+ UNITY_DELETE (m_LargeIB, kMemDynamicGeometry);
+
+ if (m_IndexBufferQuadsID)
+ {
+ glDeregisterBufferData(1, (GLuint*)&m_IndexBufferQuadsID);
+ GLES_CHK(glDeleteBuffers(1, (GLuint*)&m_IndexBufferQuadsID));
+ }
+ UNITY_FREE (kMemDynamicGeometry, m_QuadsIB);
+}
+
+void DynamicGLES2VBO::Recreate()
+{
+ m_IndexBufferQuadsID = 0;
+
+ if(m_VB) m_VB->Recreate();
+ if(m_IB) m_IB->Recreate();
+ if(m_LargeVB) m_LargeVB->Recreate();
+ if(m_LargeIB) m_LargeIB->Recreate();
+}
+
+inline void DynamicGLES2VBO::InitializeQuadsIB()
+{
+ Assert(m_IndexBufferQuadsID == 0);
+
+ if(!m_QuadsIB)
+ {
+ m_QuadsIB = (UInt16*)UNITY_MALLOC_ALIGNED(kMemDynamicGeometry, VBO::kMaxQuads * 6 * kVBOIndexSize, 32);
+ Assert (m_QuadsIB);
+
+ UInt16* ib = m_QuadsIB;
+ UInt32 baseIndex = 0;
+ for( int i = 0; i < VBO::kMaxQuads; ++i )
+ {
+ ib[0] = baseIndex + 1;
+ ib[1] = baseIndex + 2;
+ ib[2] = baseIndex;
+ ib[3] = baseIndex + 2;
+ ib[4] = baseIndex + 3;
+ ib[5] = baseIndex;
+ baseIndex += 4;
+ ib += 6;
+ }
+ }
+
+ GLES_CHK(glGenBuffers( 1, (GLuint*)&m_IndexBufferQuadsID ));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IndexBufferQuadsID));
+ GLES_CHK(glBufferData(GL_ELEMENT_ARRAY_BUFFER, VBO::kMaxQuads * 6 * kVBOIndexSize, m_QuadsIB, GL_STATIC_DRAW));
+ glRegisterBufferData(m_IndexBufferQuadsID, VBO::kMaxQuads * 6 * kVBOIndexSize, this);
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+}
+
+// chai: »æÖƺ¯ÊýÖ®Ò»
+void DynamicGLES2VBO::DrawChunk (const ChannelAssigns& channels)
+{
+#if DISABLE_GLES_CALLS
+ return;
+#endif
+
+ // just return if nothing to render
+ if( !m_LastChunkShaderChannelMask )
+ return;
+
+ AssertIf ( !m_LastChunkShaderChannelMask || !m_LastChunkStride );
+ Assert (!m_LendedChunk);
+
+ UInt8* ibPointer = (UInt8*)m_IdxSysMemStorage;
+
+ GLuint vbo = m_VtxSysMemStorage ? 0 : m_ActiveVB->GetDrawable ();
+ GLuint ibo = 0;
+ if (m_LastRenderMode == kDrawQuads)
+ {
+ if (!m_IndexBufferQuadsID)
+ InitializeQuadsIB();
+ ibo = m_IndexBufferQuadsID;
+ ibPointer = 0;
+ }
+ else if (m_ActiveIB)
+ {
+ ibo = m_ActiveIB->GetDrawable ();
+ }
+
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, vbo));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo));
+
+ switch(m_LastRenderMode)
+ {
+ case kDrawIndexedTriangles:
+ case kDrawIndexedQuads:
+ case kDrawQuads:
+ GfxDeviceGLES20_SetDrawCallTopology(kPrimitiveTriangles); break;
+ case kDrawTriangleStrip:
+ case kDrawIndexedTriangleStrip:
+ GfxDeviceGLES20_SetDrawCallTopology(kPrimitiveTriangleStripDeprecated); break;
+ case kDrawIndexedLines:
+ GfxDeviceGLES20_SetDrawCallTopology(kPrimitiveLines); break;
+ case kDrawIndexedPoints:
+ GfxDeviceGLES20_SetDrawCallTopology(kPrimitivePoints); break;
+ }
+
+
+ VBOContainsColorGLES20(m_LastChunkShaderChannelMask & (1<<kShaderChannelColor));
+
+ //chai: ÔÚÕâÀïÉèÖÃʹÓõÄshader£¬²¢ÉèÖÃuniforms
+ //stack:
+ // GfxDevice::BeforeDrawCall
+ // GpuProgramsGLES20::ApplyGpuProgramES20
+ GetRealGfxDevice().BeforeDrawCall( false );
+
+ //-------------------
+ // chai: µ½ÕâÀï¶¥µãÊý¾Ý\uniforms¶¼ÉèÖúÃÁË
+ //-------------------
+
+ // ûÓпªÆôµÄchannel
+ const UInt32 unavailableChannels = channels.GetSourceMap() & ~m_LastChunkShaderChannelMask;
+ UInt32 strides[kShaderChannelCount];
+ std::fill(strides, strides + kShaderChannelCount, m_LastChunkStride);
+ SetupVertexInput(channels, m_BufferChannel, strides, kDefaultChannelSizes, kDefaultChannelTypes, unavailableChannels);
+
+ DBG_LOG_GLES20("--->");
+ DBG_LOG_GLES20("--->DrawChunk");
+ DBG_LOG_GLES20("--->");
+ // draw
+#if !DISABLE_DRAW_CALLS_ONLY
+ ABSOLUTE_TIME drawStartTime = START_TIME;
+ int primCount = 0; // ͼԪÊýÁ¿
+ switch (m_LastRenderMode)
+ {
+ case kDrawIndexedTriangles:
+ Assert (m_LastChunkIndices != 0);
+ GLES_CHK(glDrawElements(GL_TRIANGLES, m_LastChunkIndices, GL_UNSIGNED_SHORT, ibPointer));
+ primCount = m_LastChunkIndices/3;
+ break;
+ case kDrawTriangleStrip:
+ Assert (m_LastChunkIndices == 0);
+ GLES_CHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, m_LastChunkVertices));
+ primCount = m_LastChunkVertices-2;
+ break;
+ case kDrawQuads:
+ GLES_CHK(glDrawElements(GL_TRIANGLES, (m_LastChunkVertices/2)*3, GL_UNSIGNED_SHORT, ibPointer));
+ primCount = m_LastChunkVertices/2;
+ break;
+ case kDrawIndexedTriangleStrip:
+ Assert (m_LastChunkIndices != 0);
+ GLES_CHK(glDrawElements(GL_TRIANGLE_STRIP, m_LastChunkIndices, GL_UNSIGNED_SHORT, ibPointer));
+ primCount = m_LastChunkIndices-2;
+ break;
+ case kDrawIndexedLines:
+ Assert(m_LastChunkIndices != 0);
+ GLES_CHK(glDrawElements(GL_LINES, m_LastChunkIndices, GL_UNSIGNED_SHORT, ibPointer));
+ primCount = m_LastChunkIndices/2;
+ break;
+ case kDrawIndexedPoints:
+ Assert(m_LastChunkIndices != 0);
+ GLES_CHK(glDrawElements(GL_POINTS, m_LastChunkIndices, GL_UNSIGNED_SHORT, ibPointer));
+ primCount = m_LastChunkIndices;
+ break;
+ case kDrawIndexedQuads:
+ Assert(m_LastChunkIndices != 0);
+ GLES_CHK(glDrawElements(GL_TRIANGLES, m_LastChunkIndices, GL_UNSIGNED_SHORT, ibPointer));
+ primCount = m_LastChunkIndices/3;
+ break;
+ }
+ GetRealGfxDevice().GetFrameStats().AddDrawCall (primCount, m_LastChunkVertices, ELAPSED_TIME(drawStartTime));
+#endif
+}
+
+bool DynamicGLES2VBO::GetChunk( UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, DynamicVBO::RenderMode renderMode, void** outVB, void** outIB )
+{
+ Assert( !m_LendedChunk );
+ Assert( maxVertices < 65536 && maxIndices < 65536*3 );
+ Assert(!((renderMode == kDrawQuads) && (VBO::kMaxQuads*4 < maxVertices)));
+ DebugAssert( outVB != NULL && maxVertices > 0 );
+ DebugAssert(
+ (renderMode == kDrawIndexedQuads && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedPoints && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedLines && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedTriangles && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedTriangleStrip && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawTriangleStrip && (outIB == NULL && maxIndices == 0)) ||
+ (renderMode == kDrawQuads && (outIB == NULL && maxIndices == 0)));
+
+ m_LendedChunk = true;
+ m_LastChunkShaderChannelMask = shaderChannelMask; //chai: ÉèÖÃÆôÓõÄchannel
+ m_LastRenderMode = renderMode;
+
+ if( maxVertices == 0 )
+ maxVertices = 8;
+
+ m_LastChunkStride = 0;
+ for( int i = 0; i < kShaderChannelCount; ++i ) {
+ if( shaderChannelMask & (1<<i) )
+ m_LastChunkStride += VBO::GetDefaultChannelByteSize(i);
+ }
+ DebugAssert (outVB);
+ const size_t vbCapacity = Align (maxVertices * m_LastChunkStride, 1024); // if size would require growing buffer, make sure to grow in descreet steps
+
+ if(m_VtxSysMemStorage)
+ {
+ //chai: ÔÚÄÚ´æÀï·ÖÅävbCapacity´óСµÄÇøÓò
+ *outVB = GetVertexMemory(vbCapacity);
+ m_ActiveVB = 0;
+ }
+ else
+ {
+ //chai: ÉèÖÃдÈëGPU
+ m_ActiveVB = GetSharedVB (vbCapacity);
+ *outVB = m_ActiveVB->Lock (vbCapacity);
+ if (!*outVB)
+ return false;
+ }
+
+ const bool indexed = (renderMode != kDrawQuads) && (renderMode != kDrawTriangleStrip);
+ if (maxIndices && indexed)
+ {
+ DebugAssert (outIB);
+ const size_t ibCapacity = Align( maxIndices * 2, 1024); // if size would require growing buffer, make sure to grow in discrete steps
+
+ if(m_IdxSysMemStorage)
+ {
+ *outIB = GetIndexMemory(ibCapacity);
+ m_ActiveIB = 0;
+ }
+ else
+ {
+ m_ActiveIB = GetSharedIB (ibCapacity);
+ *outIB = m_ActiveIB->Lock (ibCapacity);
+ if (!*outIB)
+ return false;
+ }
+ }
+ else
+ {
+ DebugAssert (!outIB);
+ m_ActiveIB = NULL;
+ }
+
+ return true;
+}
+
+
+// chai: ½«bufferµÄÊý¾ÝÉÏ´«µ½GPU
+void DynamicGLES2VBO::ReleaseChunk( UInt32 actualVertices, UInt32 actualIndices )
+{
+ Assert( m_LendedChunk );
+ Assert( m_LastRenderMode == kDrawIndexedTriangleStrip || m_LastRenderMode == kDrawIndexedQuads || m_LastRenderMode == kDrawIndexedPoints || m_LastRenderMode == kDrawIndexedLines || actualIndices % 3 == 0 );
+ m_LendedChunk = false;
+
+ m_LastChunkVertices = actualVertices;
+ m_LastChunkIndices = actualIndices;
+
+ // chai: Èç¹û¹¹Ô캯ÊýÀïûÓÐÉèÖÃm_VtxSysMemStorage£¬ÉÏ´«µ½GPU
+ // gles20.slowDynamicVBO = isPvrGpu || isAdrenoGpu || isMaliGpu;
+ // ÒÔÉϼ¸¿îGPUʹÓÃDynamic VBO»¹Ã»ÓдÓÄÚ´æÖ±½Ó¿½±´¿ì
+ if(!m_VtxSysMemStorage)
+ {
+ DebugAssert (m_ActiveVB);
+ m_ActiveVB->Unlock (actualVertices * m_LastChunkStride);
+ }
+
+ if(!m_IdxSysMemStorage)
+ {
+ if (m_ActiveIB)
+ m_ActiveIB->Unlock (actualIndices * kVBOIndexSize);
+ }
+
+ const bool indexed = (m_LastRenderMode != kDrawQuads) && (m_LastRenderMode != kDrawTriangleStrip);
+ if( !actualVertices || (indexed && !actualIndices) )
+ {
+ m_LastChunkShaderChannelMask = 0;
+ return;
+ }
+
+ UInt8* channelOffset = m_VtxSysMemStorage;
+ for( int i = 0; i < kShaderChannelCount; ++i )
+ {
+ //chai: Èç¹û¿ªÆôÁËÕâ¸öchannel
+ if( m_LastChunkShaderChannelMask & (1<<i) )
+ {
+ //chai: ¼Ç¼Õâ¸öchannelµÄÊý¾ÝÔÚm_VtxSysMemStorageÖÐµÄÆ«ÒƺóµÄÊ×µØÖ·
+ m_BufferChannel[i] = channelOffset;
+ channelOffset += VBO::GetDefaultChannelByteSize(i);
+ }
+ else
+ m_BufferChannel[i] = 0;
+ }
+}
+
+//chai: µ÷Õûm_VtxSysMemStorage´óС
+void* DynamicGLES2VBO::GetVertexMemory(size_t bytes)
+{
+ if(m_VtxSysMemStorageSize < bytes)
+ {
+ UNITY_FREE(kMemDynamicGeometry, m_VtxSysMemStorage);
+
+ m_VtxSysMemStorageSize = bytes;
+ m_VtxSysMemStorage = (UInt8*)UNITY_MALLOC_ALIGNED(kMemDynamicGeometry, m_VtxSysMemStorageSize, 32);
+ }
+ return m_VtxSysMemStorage;
+}
+
+void* DynamicGLES2VBO::GetIndexMemory(size_t bytes)
+{
+ if(m_IdxSysMemStorageSize < bytes)
+ {
+ UNITY_FREE(kMemDynamicGeometry, m_IdxSysMemStorage);
+
+ m_IdxSysMemStorageSize = bytes;
+ m_IdxSysMemStorage = (UInt16*)UNITY_MALLOC_ALIGNED(kMemDynamicGeometry, m_IdxSysMemStorageSize, 32);
+ }
+ return m_IdxSysMemStorage;
+}
+
+static SharedBuffer* ChooseBestBuffer (SharedBuffer* smallBuffer, SharedBuffer* largeBuffer, size_t bytes)
+{
+ if (!largeBuffer)
+ return smallBuffer;
+
+ const size_t couldGrow = smallBuffer->GetAvailableBytes () * 2;
+ if (couldGrow >= bytes && couldGrow < largeBuffer->GetAvailableBytes () / 2)
+ return smallBuffer;
+
+ return largeBuffer;
+}
+
+SharedBuffer* DynamicGLES2VBO::GetSharedVB (size_t bytes)
+{
+ return ChooseBestBuffer (m_VB, m_LargeVB, bytes);
+}
+
+SharedBuffer* DynamicGLES2VBO::GetSharedIB (size_t bytes)
+{
+ return ChooseBestBuffer (m_IB, m_LargeIB, bytes);
+}
+
+/*
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~¨~
+*/
+
+class SharedBuffer
+{
+public:
+ SharedBuffer(int bufferType, size_t bytesPerBlock, size_t blocks = 1, bool driverSupportsBufferOrphaning = true);
+ ~SharedBuffer();
+ void Recreate();
+
+ void* Lock(size_t bytes);
+ void Unlock(size_t actualBytes = 0);
+
+ typedef int BufferId;
+ BufferId GetDrawable() const;
+ void MarkAsDrawn(BufferId bufferId) {}
+
+ size_t GetAvailableBytes() const;
+
+private:
+ void* OrphanLock(size_t bytes);
+ void OrphanUnlock(size_t actualBytes);
+
+ void* SimpleLock(size_t bytes);
+ void SimpleUnlock(size_t actualBytes);
+
+private:
+ int m_BufferType; // GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER
+ vector<BufferId> m_BufferIDs;
+ vector<size_t> m_BufferSizes;
+ bool m_DriverSupportsBufferOrphaning;
+
+ size_t m_NextBufferIndex;
+ size_t m_ReadyBufferIndex; // chai: ready for drawing
+
+ UInt8* m_TemporaryDataStorage;
+ size_t m_TemporaryDataStorageSize;
+
+ size_t m_LockedBytes;
+};
+
+inline size_t AlignTemporaryDataStorageSize (size_t size)
+{
+ // NOTE: to simplify neon optimization - pad to 64 bit (neon register size)
+ size = Align (size, 8);
+ Assert (size % 8 == 0);
+ Assert (size > 0);
+ return size;
+}
+
+SharedBuffer::SharedBuffer (int bufferType, size_t bytesPerBlock, size_t blocks, bool driverSupportsBufferOrphaning)
+: m_BufferType (bufferType)
+, m_DriverSupportsBufferOrphaning (driverSupportsBufferOrphaning)
+, m_NextBufferIndex (0)
+, m_ReadyBufferIndex (~0UL)
+, m_TemporaryDataStorage (NULL)
+, m_TemporaryDataStorageSize (0)
+, m_LockedBytes (0)
+{
+ Assert (bufferType == GL_ARRAY_BUFFER || bufferType == GL_ELEMENT_ARRAY_BUFFER);
+ Assert (blocks > 0);
+ Assert (bytesPerBlock > 0);
+ bytesPerBlock = AlignTemporaryDataStorageSize (bytesPerBlock);
+
+ if (m_DriverSupportsBufferOrphaning)
+ {
+ Assert (blocks == 1);
+ }
+
+ if (!gGraphicsCaps.gles20.hasMapbuffer || !m_DriverSupportsBufferOrphaning)
+ {
+ m_TemporaryDataStorageSize = bytesPerBlock;
+ m_TemporaryDataStorage = (UInt8*)UNITY_MALLOC_ALIGNED (kMemDynamicGeometry, m_TemporaryDataStorageSize, 32);
+ Assert (m_TemporaryDataStorage);
+ memset (m_TemporaryDataStorage, 0x00, m_TemporaryDataStorageSize);
+ }
+
+ m_BufferSizes.resize (blocks);
+ m_BufferIDs.resize (blocks);
+
+ for (size_t q = 0; q < m_BufferSizes.size(); ++q)
+ m_BufferSizes[q] = bytesPerBlock;
+
+ Recreate();
+
+ DebugAssert (m_BufferIDs.size() == m_BufferSizes.size ());
+}
+
+void SharedBuffer::Recreate()
+{
+ GLES_CHK (glGenBuffers (m_BufferIDs.size(), (GLuint*)&m_BufferIDs[0]));
+
+ if (!m_DriverSupportsBufferOrphaning)
+ {
+ for (size_t q = 0; q < m_BufferIDs.size(); ++q)
+ {
+ GLES_CHK (glBindBuffer (m_BufferType, m_BufferIDs[q]));
+ GLES_CHK(glBufferData(m_BufferType, m_BufferSizes[q], m_TemporaryDataStorage, GL_STREAM_DRAW));
+ glRegisterBufferData(m_BufferIDs[q], m_BufferSizes[q], this);
+ }
+ GLES_CHK (glBindBuffer (m_BufferType, 0));
+ }
+}
+
+SharedBuffer::~SharedBuffer ()
+{
+ glDeregisterBufferData(m_BufferIDs.size (), (GLuint*)&m_BufferIDs[0]);
+ GLES_CHK(glDeleteBuffers(m_BufferIDs.size (), (GLuint*)&m_BufferIDs[0]));
+ UNITY_FREE (kMemDynamicGeometry, m_TemporaryDataStorage);
+}
+
+void* SharedBuffer::Lock (size_t bytes)
+{
+ Assert (m_LockedBytes == 0);
+
+ m_LockedBytes = bytes;
+
+ if (m_TemporaryDataStorage && m_TemporaryDataStorageSize < bytes)
+ {
+ m_TemporaryDataStorageSize = AlignTemporaryDataStorageSize (bytes);
+ m_TemporaryDataStorage = (UInt8*)UNITY_REALLOC (kMemDynamicGeometry, m_TemporaryDataStorage, m_TemporaryDataStorageSize, 32);
+ Assert (m_TemporaryDataStorage);
+ }
+
+ // chai: ²ßÂÔģʽ£¬Á½ÖÖ²ßÂÔ¡£
+ if (m_DriverSupportsBufferOrphaning)
+ return OrphanLock (bytes);
+ else
+ return SimpleLock (bytes);
+}
+
+void SharedBuffer::Unlock (size_t actualBytes)
+{
+ if (actualBytes == 0)
+ actualBytes = m_LockedBytes;
+
+ Assert (actualBytes <= m_LockedBytes);
+
+ if (m_DriverSupportsBufferOrphaning)
+ OrphanUnlock (actualBytes);
+ else
+ SimpleUnlock (actualBytes);
+
+ DebugAssert (m_NextBufferIndex != ~0UL);
+ DebugAssert (m_ReadyBufferIndex != ~0UL);
+
+ DebugAssert (m_NextBufferIndex < m_BufferIDs.size());
+ DebugAssert (m_ReadyBufferIndex < m_BufferIDs.size());
+
+ m_LockedBytes = 0;
+}
+
+void* SharedBuffer::OrphanLock (size_t bytes)
+{
+ DebugAssert (m_DriverSupportsBufferOrphaning);
+ DebugAssert (m_BufferIDs.size () == 1);
+ DebugAssert (m_NextBufferIndex == 0);
+
+ UInt8* lockedBuffer = NULL;
+
+ if (gGraphicsCaps.gles20.hasMapbuffer)
+ {
+ GLES_CHK (glBindBuffer (m_BufferType, m_BufferIDs[0]));
+ GLES_CHK (glBufferData (m_BufferType, bytes, 0, GL_STREAM_DRAW)); // orphan old buffer, driver will allocate new storage
+ //chai: µ÷ÓÃglBufferData(,,NULL,)ÄÜÈÃÇý¶¯ÖªµÀ½ÓÏÂÀ´¶Ô´ËbufferµÄbufferdata²Ù×÷²»ÐèҪ֮ǰµÄÊý¾Ý
+ // ¿ÉÒÔÖØÐ´´½¨Ò»¸öbuffer,ʹÁ½Õß¿ÉÒÔ²¢ÐÐ
+ lockedBuffer = reinterpret_cast<UInt8*> (gGlesExtFunc.glMapBufferOES(m_BufferType, GL_WRITE_ONLY_OES));
+ GLESAssert();
+ }
+ else
+ {
+ Assert (m_TemporaryDataStorage);
+ lockedBuffer = m_TemporaryDataStorage;
+
+ GLES_CHK (glBindBuffer (m_BufferType, m_BufferIDs[0]));
+ GLES_CHK (glBufferData (m_BufferType, 0, 0, GL_STREAM_DRAW)); // orphan old buffer, driver will allocate new storage
+ }
+
+ Assert (lockedBuffer);
+ return lockedBuffer;
+}
+
+void SharedBuffer::OrphanUnlock (size_t actualBytes)
+{
+ Assert (m_NextBufferIndex == 0);
+
+ GLES_CHK (glBindBuffer (m_BufferType, m_BufferIDs[0]));
+
+ if (gGraphicsCaps.gles20.hasMapbuffer)
+ GLES_CHK(gGlesExtFunc.glUnmapBufferOES (m_BufferType));
+ else
+ GLES_CHK(glBufferData (m_BufferType, actualBytes, m_TemporaryDataStorage, GL_STREAM_DRAW));
+
+ GLES_CHK (glBindBuffer (m_BufferType, 0));
+
+ m_NextBufferIndex = 0;
+ m_ReadyBufferIndex = 0;
+}
+
+void* SharedBuffer::SimpleLock (size_t bytes)
+{
+ DebugAssert (!m_DriverSupportsBufferOrphaning);
+ DebugAssert (!m_BufferIDs.empty ());
+
+ Assert (m_TemporaryDataStorageSize >= bytes);
+ Assert (m_TemporaryDataStorage);
+ return m_TemporaryDataStorage;
+}
+
+//chai: ÊÖ¶¯ÊµÏÖbuffer object orphaning
+void SharedBuffer::SimpleUnlock (size_t actualBytes)
+{
+ DebugAssert (m_BufferIDs.size() == m_BufferSizes.size ());
+ // NOTE: current implementation with naively pick next buffer
+ // it is possible that GPU haven't rendered it and CPU will stall
+ // however we expect that enough buffers are allocated and stall doesn't happen in practice
+
+ m_ReadyBufferIndex = m_NextBufferIndex;
+ ++m_NextBufferIndex;
+ if (m_NextBufferIndex >= m_BufferIDs.size ())
+ m_NextBufferIndex = 0;
+ DebugAssert (m_NextBufferIndex != m_ReadyBufferIndex);
+
+ const size_t bufferIndex = m_ReadyBufferIndex;
+ DebugAssert (bufferIndex < m_BufferIDs.size());
+
+ Assert (m_TemporaryDataStorageSize >= actualBytes);
+
+ GLES_CHK (glBindBuffer (m_BufferType, m_BufferIDs[bufferIndex]));
+ if (m_BufferSizes[bufferIndex] < actualBytes)
+ {
+ GLES_CHK (glBufferData (m_BufferType, actualBytes, m_TemporaryDataStorage, GL_STREAM_DRAW));
+ m_BufferSizes[bufferIndex] = actualBytes;
+ }
+ else
+ {
+ GLES_CHK (glBufferSubData (m_BufferType, 0, actualBytes, m_TemporaryDataStorage));
+ }
+ GLES_CHK (glBindBuffer (m_BufferType, 0));
+}
+
+size_t SharedBuffer::GetAvailableBytes () const
+{
+ return m_BufferSizes[m_NextBufferIndex];
+}
+
+SharedBuffer::BufferId SharedBuffer::GetDrawable () const
+{
+ DebugAssert (m_ReadyBufferIndex != ~0UL);
+ DebugAssert (m_ReadyBufferIndex < m_BufferIDs.size());
+ if (m_ReadyBufferIndex >= m_BufferIDs.size ())
+ return 0;
+
+ return m_BufferIDs[m_ReadyBufferIndex];
+}
+
+
+// WARNING: this is just a temp solution, as we really need to clean up all this mess
+
+static SharedBuffer* lockedSharedBuffer = NULL;
+void* LockSharedBufferGLES20 (int bufferType, size_t bytes, bool forceBufferObject)
+{
+ Assert (bufferType == GL_ARRAY_BUFFER || bufferType == GL_ELEMENT_ARRAY_BUFFER);
+ DynamicGLES2VBO& dynamicVBO = static_cast<DynamicGLES2VBO&> (GetRealGfxDevice ().GetDynamicVBO ());
+
+ if(gGraphicsCaps.gles20.slowDynamicVBO && !forceBufferObject)
+ {
+ if (bufferType == GL_ARRAY_BUFFER)
+ return dynamicVBO.GetVertexMemory(bytes);
+ else if (bufferType == GL_ELEMENT_ARRAY_BUFFER)
+ return dynamicVBO.GetIndexMemory(bytes);
+ }
+ else
+ {
+ DebugAssert (lockedSharedBuffer == NULL);
+ if (bufferType == GL_ARRAY_BUFFER)
+ lockedSharedBuffer = dynamicVBO.GetSharedVB (bytes);
+ else if (bufferType == GL_ELEMENT_ARRAY_BUFFER)
+ lockedSharedBuffer = dynamicVBO.GetSharedIB (bytes);
+
+ Assert (lockedSharedBuffer);
+ return lockedSharedBuffer->Lock (bytes);
+ }
+
+ return 0;
+}
+
+int UnlockSharedBufferGLES20 (size_t actualBytes, bool forceBufferObject)
+{
+ if(gGraphicsCaps.gles20.slowDynamicVBO && !forceBufferObject)
+ {
+ return 0;
+ }
+ else
+ {
+ Assert (lockedSharedBuffer);
+ lockedSharedBuffer->Unlock (actualBytes);
+ const int bufferId = lockedSharedBuffer->GetDrawable ();
+ lockedSharedBuffer = NULL;
+ return bufferId;
+ }
+}
+
+#if NV_STATE_FILTERING
+ #ifdef glDeleteBuffers
+ #undef glDeleteBuffers
+ #endif
+ #ifdef glBindBuffer
+ #undef glBindBuffer
+ #endif
+ #ifdef glVertexAttribPointer
+ #undef glVertexAttribPointer
+ #endif
+#endif
+
+#endif // GFX_SUPPORTS_OPENGLES20
diff --git a/Runtime/GfxDevice/opengles20/VBOGLES20.h b/Runtime/GfxDevice/opengles20/VBOGLES20.h
new file mode 100644
index 0000000..62ce55d
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/VBOGLES20.h
@@ -0,0 +1,147 @@
+#ifndef VBO_GLES20_H
+#define VBO_GLES20_H
+
+#include "Runtime/Shaders/BufferedVBO.h"
+#include "IncludesGLES20.h"
+#include <map>
+
+class GLES2VBO : public BufferedVBO {
+public:
+ GLES2VBO();
+ virtual ~GLES2VBO ();
+
+ virtual void UpdateVertexData( const VertexBufferData& buffer );
+ virtual void UpdateIndexData (const IndexBufferData& buffer);
+ virtual void DrawVBO (const ChannelAssigns& channels, UInt32 firstIndexByte, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 firstVertex, UInt32 vertexCount );
+ virtual void DrawCustomIndexed( const ChannelAssigns& channels, void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount );
+
+ virtual bool MapVertexStream( VertexStreamData& outData, unsigned stream );
+ virtual void UnmapVertexStream( unsigned stream );
+
+ virtual void Cleanup();
+ virtual void Recreate();
+ virtual bool IsVertexBufferLost() const;
+ virtual bool IsIndexBufferLost() const;
+ virtual void MarkBuffersLost();
+
+ virtual bool IsUsingSourceVertices() const { return m_VertexBufferID[0] == 0; }
+ virtual bool IsUsingSourceIndices() const { return m_IndexBufferID == 0; }
+
+ virtual int GetRuntimeMemorySize() const;
+
+private:
+
+ void DrawInternal(int vertexBufferID, int indexBufferID, const ChannelAssigns& channels, void* indices, UInt32 indexCount, GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount);
+
+ UInt32 GetUnavailableChannels(const ChannelAssigns& channels) const;
+
+ ChannelInfoArray m_Channels;
+ IndexBufferData m_IBData;
+ enum { DynamicVertexBufferCount = 3 };
+ int m_VertexBufferID[DynamicVertexBufferCount];
+ int m_CurrentBufferIndex;
+ int m_IndexBufferID;
+ int m_IBSize;
+ void* m_ReadableIndices;
+
+ int m_VBOUsage;
+ int m_IBOUsage;
+
+
+ bool m_UsesVBO;
+ bool m_UsesIBO;
+
+ // for now these are only called on UpdateVertexData/UpdateIndexData
+ // so they are extracted only for convinience
+ bool ShouldUseVBO();
+ bool ShouldUseIBO();
+
+ void EnsureVerticesInited(bool newBuffers);
+ void EnsureIndicesInited();
+};
+
+class SharedBuffer;
+
+// Usage:
+// 1) GetChunk
+// if this returns false, don't use data pointers, bail out
+// 2) fill with data
+// 3) ReleaseChunk
+// 4) DrawChunk (possibly multiple times)
+//
+// The key is that drawing must happen immediately after filling the chunk, because the next
+// GetChunk might destroy the previous chunk's data. So never count on chunks being persistent.
+class DynamicGLES2VBO : public DynamicVBO
+{
+public:
+ DynamicGLES2VBO();
+ ~DynamicGLES2VBO();
+
+ virtual bool GetChunk( UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode renderMode, void** outVB, void** outIB );
+ virtual void ReleaseChunk( UInt32 actualVertices, UInt32 actualIndices );
+ virtual void DrawChunk (const ChannelAssigns& channels);
+
+ virtual void Recreate();
+
+ SharedBuffer* GetSharedVB (size_t bytes);
+ SharedBuffer* GetSharedIB (size_t bytes);
+
+ void* GetVertexMemory(size_t bytes);
+ void* GetIndexMemory(size_t bytes);
+
+private:
+ void InitializeQuadsIB();
+
+private:
+ //chai: ¼Ç¼channelÔÚm_VtxSysMemStorageÖеĸ÷¸öchannelµÄÊ×µØÖ·
+ void* m_BufferChannel[kShaderChannelCount];
+ RenderMode m_LastRenderMode;
+
+ //chai: ¼Ç¼¿ªÆôµÄchannel
+ //UInt32 m_LastChunkShaderChannelMask;
+
+
+ SharedBuffer* m_VB;
+ SharedBuffer* m_LargeVB;
+ SharedBuffer* m_ActiveVB;
+ SharedBuffer* m_IB;
+ SharedBuffer* m_LargeIB;
+ SharedBuffer* m_ActiveIB;
+
+ UInt16* m_QuadsIB;
+ int m_IndexBufferQuadsID;
+
+ UInt8* m_VtxSysMemStorage;
+ unsigned m_VtxSysMemStorageSize;
+
+ UInt16* m_IdxSysMemStorage;
+ unsigned m_IdxSysMemStorageSize;
+};
+
+void InvalidateVertexInputCacheGLES20();
+// WARNING: forceBufferObject is just a temp solution, as we really need to clean up all this mess
+// we need it for bigger dynamic vbos
+void* LockSharedBufferGLES20 (int bufferType, size_t bytes, bool forceBufferObject=false);
+int UnlockSharedBufferGLES20 (size_t actualBytes = 0, bool forceBufferObject=false);
+
+#define GL_VERTEX_ARRAY 0
+#define GL_COLOR_ARRAY 1
+#define GL_NORMAL_ARRAY 2
+#define GL_TEXTURE_ARRAY0 3
+#define GL_TEXTURE_ARRAY1 GL_TEXTURE_ARRAY0 + 1
+#define GL_TEXTURE_ARRAY2 GL_TEXTURE_ARRAY0 + 2
+#define GL_TEXTURE_ARRAY3 GL_TEXTURE_ARRAY0 + 3
+#define GL_TEXTURE_ARRAY4 GL_TEXTURE_ARRAY0 + 4
+#define GL_TEXTURE_ARRAY5 GL_TEXTURE_ARRAY0 + 5
+#define GL_TEXTURE_ARRAY6 GL_TEXTURE_ARRAY0 + 6
+#define GL_TEXTURE_ARRAY7 GL_TEXTURE_ARRAY0 + 7
+
+#if NV_STATE_FILTERING
+void filteredBindBufferGLES20(GLenum target, GLuint buffer,bool isImmediate=false);
+void filteredVertexAttribPointerGLES20(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer);
+void StateFiltering_InvalidateVBOCacheGLES20();
+#endif
+
+#endif
diff --git a/Runtime/GfxDevice/opengles20/_DebugStuffGLES20.cpp b/Runtime/GfxDevice/opengles20/_DebugStuffGLES20.cpp
new file mode 100644
index 0000000..a72f01a
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/_DebugStuffGLES20.cpp
@@ -0,0 +1,438 @@
+#include "UnityPrefix.h"
+#include "AssertGLES20.h"
+#include "IncludesGLES20.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "_DebugStuffGLES20.h"
+#include "VBOGLES20.h"
+
+void PrintMatrix(const char* name, const Matrix4x4f& m)
+{
+ printf_console("Outputing:%s\n------------\n"
+ "mat4 mat = (%.2f, %.2f, %.2f, %2f,\n"
+ "%.2f, %.2f, %.2f, %2f,\n"
+ "%.2f, %.2f, %.2f, %2f,\n"
+ "%.2f, %.2f, %.2f, %2f);\n\n",name,
+ m[0], m[1], m[2], m[3],
+ m[4], m[5], m[6], m[7],
+ m[8], m[9], m[10], m[11],
+ m[12], m[13], m[14], m[15]);
+}
+void _Debug::MiniLoopGLES20()
+{
+
+ #define VERTEX_ARRAY 0
+ float pfIdentity[] =
+ {
+ 1.0f,0.0f,0.0f,0.0f,
+ 0.0f,1.0f,0.0f,0.0f,
+ 0.0f,0.0f,1.0f,0.0f,
+ 0.0f,0.0f,0.0f,1.0f
+ };
+
+ // Fragment and vertex shaders code
+ char* pszFragShader = "\
+ void main (void)\
+ {\
+ gl_FragColor = vec4(1.0, 1.0, 0.66 ,1.0);\
+ }";
+ char* pszVertShader = "\
+ attribute highp vec3 myVertex;\
+ uniform mediump mat4 myPMVMatrix;\
+ void main(void)\
+ {\
+ gl_Position = myPMVMatrix * vec4(myVertex,1.0);\
+ }";
+
+
+ GLuint uiFragShader, uiVertShader; /* Used to hold the fragment and vertex shader handles */
+ GLuint uiProgramObject; /* Used to hold the program handle (made out of the two previous shaders */
+
+ // Create the fragment shader object
+ uiFragShader = glCreateShader(GL_FRAGMENT_SHADER);
+
+ // Load the source code into it
+ glShaderSource(uiFragShader, 1, (const char**)&pszFragShader, NULL);
+
+ // Compile the source code
+ glCompileShader(uiFragShader);
+
+ // Check if compilation succeeded
+ GLint bShaderCompiled;
+ bShaderCompiled = 0;
+ glGetShaderiv(uiFragShader, GL_COMPILE_STATUS, &bShaderCompiled);
+
+ if (!bShaderCompiled)
+ {
+#ifndef NO_GDI
+ // An error happened, first retrieve the length of the log message
+ int i32InfoLogLength, i32CharsWritten;
+ glGetShaderiv(uiFragShader, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
+
+ // Allocate enough space for the message and retrieve it
+ char* pszInfoLog = new char[i32InfoLogLength];
+ glGetShaderInfoLog(uiFragShader, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
+
+ printf_console(pszInfoLog);
+ // Displays the error in a dialog box
+ delete[] pszInfoLog;
+#endif
+ return;
+ }
+
+ // Loads the vertex shader in the same way
+ uiVertShader = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(uiVertShader, 1, (const char**)&pszVertShader, NULL);
+ glCompileShader(uiVertShader);
+ glGetShaderiv(uiVertShader, GL_COMPILE_STATUS, &bShaderCompiled);
+ if (!bShaderCompiled)
+ {
+#ifndef NO_GDI
+ int i32InfoLogLength, i32CharsWritten;
+ glGetShaderiv(uiVertShader, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
+ char* pszInfoLog = new char[i32InfoLogLength];
+ glGetShaderInfoLog(uiVertShader, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
+ printf_console(pszInfoLog);
+ delete[] pszInfoLog;
+#endif
+ return;
+ }
+
+ // Create the shader program
+ uiProgramObject = glCreateProgram();
+ // Bind the custom vertex attribute "myVertex" to location VERTEX_ARRAY
+ glBindAttribLocation(uiProgramObject, VERTEX_ARRAY, "myVertex");
+ // Attach the fragment and vertex shaders to it
+ glAttachShader(uiProgramObject, uiFragShader);
+ glAttachShader(uiProgramObject, uiVertShader);
+
+
+
+ // Link the program
+ glLinkProgram(uiProgramObject);
+
+ // Check if linking succeeded in the same way we checked for compilation success
+ GLint bLinked;
+ glGetProgramiv(uiProgramObject, GL_LINK_STATUS, &bLinked);
+ if (!bLinked)
+ {
+#ifndef NO_GDI
+ int i32InfoLogLength, i32CharsWritten;
+ glGetProgramiv(uiProgramObject, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
+ char* pszInfoLog = new char[i32InfoLogLength];
+ glGetProgramInfoLog(uiProgramObject, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
+ printf_console(pszInfoLog);
+ delete[] pszInfoLog;
+#endif
+ return;
+ }
+
+
+ // Actually use the created program
+ glUseProgram(uiProgramObject);
+ GLESAssert();
+
+ // Sets the clear color.
+ // The colours are passed per channel (red,green,blue,alpha) as float values from 0.0 to 1.0
+ glClearColor(0.6f, 0.8f, 1.0f, 1.0f);
+ GLESAssert();
+
+ // We're going to draw a triangle to the screen so create a vertex buffer object for our triangle
+ GLuint ui32Vbo; // Vertex buffer object handle
+
+ // Interleaved vertex data
+ GLfloat afVertices[] = { -0.1f,-0.4f,0.0f, // Position
+ 0.4f ,-0.4f,0.0f,
+ 0.0f ,0.4f ,0.0f};
+
+ // Generate the vertex buffer object (VBO)
+ glGenBuffers(1, &ui32Vbo);
+ GLESAssert();
+
+ // Bind the VBO so we can fill it with data
+ glBindBuffer(GL_ARRAY_BUFFER, ui32Vbo);
+ GLESAssert();
+
+ // Set the buffer's data
+ unsigned int uiSize = 3 * (sizeof(GLfloat) * 3); // Calc afVertices size (3 vertices * stride (3 GLfloats per vertex))
+ glBufferData(GL_ARRAY_BUFFER, uiSize, afVertices, GL_STATIC_DRAW);
+ GLESAssert();
+
+ glClearDepthf(1.0f);
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LEQUAL);
+ glDisable(GL_CULL_FACE);
+ printf_console("Loop launched\n");
+ while (1)
+ {
+ glClearColor(1.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ GLESAssert();
+
+
+ /*
+ Bind the projection model view matrix (PMVMatrix) to
+ the associated uniform variable in the shader
+ */
+
+ // First gets the location of that variable in the shader using its name
+ int i32Location = glGetUniformLocation(uiProgramObject, "myPMVMatrix");
+
+ // Then passes the matrix to that variable
+ glUniformMatrix4fv( i32Location, 1, GL_FALSE, pfIdentity);
+ GLESAssert();
+
+ /*
+ Enable the custom vertex attribute at index VERTEX_ARRAY.
+ We previously binded that index to the variable in our shader "vec4 MyVertex;"
+ */
+ glEnableVertexAttribArray(VERTEX_ARRAY);
+ GLESAssert();
+
+ // Sets the vertex data to this attribute index
+ glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, 0);
+
+ /*
+ Draws a non-indexed triangle array from the pointers previously given.
+ This function allows the use of other primitive types : triangle strips, lines, ...
+ For indexed geometry, use the function glDrawElements() with an index list.
+ */
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+ GLESAssert();
+
+ void PresentContextGLES();
+ PresentContextGLES();
+ }
+}
+int _Debug::CreateDefaultShader (bool texturing)
+{
+ char* strVertexShader = NULL;
+ char* strFragmentShader = NULL;
+ if (texturing)
+ {
+ strVertexShader =
+ "attribute highp vec3 _glesVertex;\n"
+ "attribute highp vec4 _glesUV0;\n"
+ "uniform mediump mat4 _glesMVP;\n"
+ "varying mediump vec2 _glesVarUV0;\n"
+ "void main(void)\n"
+ "{\n"
+ " gl_Position = _glesMVP * vec4(_glesVertex,1.0);\n"
+ " _glesVarUV0 = _glesUV0.st;\n"
+ "}";
+
+ strFragmentShader =
+ "uniform sampler2D texDiffuse;\n"
+ "varying mediump vec2 _glesVarUV0;\n"
+ "void main (void)\n"
+ "{\n"
+ " gl_FragColor = texture2D(texDiffuse, _glesVarUV0);\n"
+ "}";
+ }
+ else
+ {
+ strVertexShader =
+ "attribute highp vec3 _glesVertex;\n"
+ "uniform mediump mat4 _glesMVP;\n"
+ "void main(void)\n"
+ "{\n"
+ " gl_Position = _glesMVP * vec4(_glesVertex,1.0);\n"
+ "}";
+
+
+ strFragmentShader =
+ "void main (void)\n"
+ "{\n"
+ " gl_FragColor = vec4(1.0, 1.0, 0.66 ,1.0);\n"
+ "}";
+
+ }
+ return CreateShader(strVertexShader, strFragmentShader);
+
+}
+int _Debug::CreateShader(const char* vertexShader, const char* fragmenShader)
+{
+ GLuint vertShader, fragShader;
+ GLuint shader;
+
+ GLint compiled, linked;
+
+ //Vertex shader
+ GLES_CHK(vertShader = glCreateShader(GL_VERTEX_SHADER));
+ GLES_CHK(glShaderSource(vertShader, 1, (const char**)&vertexShader, NULL));
+ GLES_CHK(glCompileShader(vertShader));
+
+ compiled = 0;
+ GLES_CHK(glGetShaderiv(vertShader, GL_COMPILE_STATUS, &compiled));
+
+ if (!compiled)
+ {
+ int i32InfoLogLength, i32CharsWritten;
+ GLES_CHK(glGetShaderiv(vertShader, GL_INFO_LOG_LENGTH, &i32InfoLogLength));
+ char* pszInfoLog = new char[i32InfoLogLength];
+ GLES_CHK(glGetShaderInfoLog(vertShader, i32InfoLogLength, &i32CharsWritten, pszInfoLog));
+ printf_console(pszInfoLog);
+ delete[] pszInfoLog;
+ return 0;
+ }
+
+ //Fragment shader
+ GLES_CHK(fragShader = glCreateShader(GL_FRAGMENT_SHADER));
+ GLES_CHK(glShaderSource(fragShader, 1, (const char**)&fragmenShader, NULL));
+ GLES_CHK(glCompileShader(fragShader));
+
+ compiled = 0;
+ GLES_CHK(glGetShaderiv(fragShader, GL_COMPILE_STATUS, &compiled));
+
+ if (!compiled)
+ {
+ int i32InfoLogLength, i32CharsWritten;
+ GLES_CHK(glGetShaderiv(fragShader, GL_INFO_LOG_LENGTH, &i32InfoLogLength));
+ char* pszInfoLog = new char[i32InfoLogLength];
+ GLES_CHK(glGetShaderInfoLog(fragShader, i32InfoLogLength, &i32CharsWritten, pszInfoLog));
+ printf_console(pszInfoLog);
+ delete[] pszInfoLog;
+ return 0;
+ }
+
+ // Final Step, attacha and link shaders
+ shader = glCreateProgram();
+ (glBindAttribLocation(shader, GL_VERTEX_ARRAY, "_glesVertex"));
+ (glBindAttribLocation(shader, GL_TEXTURE_ARRAY0, "_glesUV0"));
+
+ GLES_CHK(glAttachShader(shader, fragShader));
+ GLES_CHK(glAttachShader(shader, vertShader));
+ GLES_CHK(glLinkProgram(shader));
+
+ linked = 0;
+ GLES_CHK(glGetProgramiv(shader, GL_LINK_STATUS, &linked));
+ if (!linked)
+ {
+ int i32InfoLogLength, i32CharsWritten;
+ GLES_CHK(glGetProgramiv(shader, GL_INFO_LOG_LENGTH, &i32InfoLogLength));
+ char* pszInfoLog = new char[i32InfoLogLength];
+ GLES_CHK(glGetProgramInfoLog(shader, i32InfoLogLength, &i32CharsWritten, pszInfoLog));
+ printf_console(pszInfoLog);
+ delete[] pszInfoLog;
+ return 0;
+ }
+ return shader;
+}
+
+int _Debug::CreateDefaultVBO (bool texCoords)
+{
+ float verts[] =
+ {
+ -0.5f, -0.5f, 0.0f,
+ 0.5f, -0.5f, 0.0f,
+ 0.5f, 0.5f, 0.0f,
+ -0.5f, -0.5f, 0.0f,
+ 0.5f, 0.5f, 0.0f,
+ -0.5f, 0.5f, 0.0f
+ };
+
+ float vertsUV[] =
+ {
+ -0.5f, -0.5f, 0.0f,
+ 0.0f, 0.0f,
+ 0.5f, -0.5f, 0.0f,
+ 1.0f, 0.0f,
+ 0.5f, 0.5f, 0.0f,
+ 1.0f, 1.0f,
+ -0.5f, -0.5f, 0.0f,
+ 0.0f, 0.0f,
+ 0.5f, 0.5f, 0.0f,
+ 1.0f, 1.0f,
+ -0.5f, 0.5f, 0.0f,
+ 0.0f, 1.0f,
+ };
+
+ if (texCoords)
+ {
+ return CreateVBO(vertsUV, 6, sizeof(float) * 5);
+ }
+ else
+ {
+ return CreateVBO(vertsUV, 6, sizeof(float) * 3);
+ }
+}
+
+
+int _Debug::CreateVBO(const float* data, int vertexCount, int vertexSize)
+{
+ GLuint vbo;
+ GLES_CHK(glGenBuffers(1, &vbo));
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, vbo));
+ GLES_CHK(glBufferData(GL_ARRAY_BUFFER, vertexSize * vertexCount, data, GL_STATIC_DRAW));
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, 0));
+ return vbo;
+}
+
+void _Debug::SetUniformMatrix4x4f(int shaderID, const char* name, const Matrix4x4f& mat)
+{
+ int loc ;
+ GLES_CHK(loc = glGetUniformLocation(shaderID, name));
+
+ if (loc != -1)GLES_CHK(glUniformMatrix4fv(loc, 1, GL_FALSE, mat.GetPtr()));
+ else printf_console("Failed to find uniform %s\n", name);
+}
+void _Debug::SetUniformSample2D (int shaderID, const char* name, int texUnit)
+{
+ int loc ;
+ GLES_CHK(loc = glGetUniformLocation(shaderID, name));
+
+ if (loc != -1)GLES_CHK(glUniform1i(loc, texUnit));
+ else printf_console("Failed to find uniform %s\n", name);
+}
+void _Debug::UseShader(int shaderID)
+{
+ GLES_CHK(glUseProgram(shaderID));
+}
+void _Debug::DrawVBO(int vboID, int vertexCount, bool texCoords)
+{
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, vboID));
+ if (texCoords)
+ {
+ glEnableVertexAttribArray(GL_VERTEX_ARRAY);
+ glVertexAttribPointer(GL_VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, 0);
+ glEnableVertexAttribArray(GL_TEXTURE_ARRAY0);
+ glVertexAttribPointer(GL_TEXTURE_ARRAY0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (void*) (3 * sizeof(GLfloat)));
+
+ GLES_CHK(glDrawArrays(GL_TRIANGLES, 0, vertexCount));
+
+ GLES_CHK(glDisableVertexAttribArray(GL_VERTEX_ARRAY));
+ GLES_CHK(glDisableVertexAttribArray(GL_TEXTURE_ARRAY0));
+ }
+ else
+ {
+ GLES_CHK(glEnableVertexAttribArray(GL_VERTEX_ARRAY));
+ GLES_CHK(glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, 0));
+ GLES_CHK(glDrawArrays(GL_TRIANGLES, 0, vertexCount));
+ GLES_CHK(glDisableVertexAttribArray(GL_VERTEX_ARRAY));
+ }
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, 0));
+}
+void _Debug::DisplayTextureMiniLoop(int textureID)
+{
+ bool texCoords = true;
+ int vbo = CreateDefaultVBO(texCoords);
+ int shader = CreateDefaultShader(texCoords);
+ GLES_CHK(glDisable(GL_DEPTH_TEST));
+ GLES_CHK(glDisable(GL_CULL_FACE));
+ GLES_CHK(glDisable(GL_BLEND));
+ GLES_CHK(glBindFramebuffer (GL_FRAMEBUFFER, 0));
+ while (1)
+ {
+ GLES_CHK(glClearColor(1.0f, 1.0f, 0.0f, 1.0f));
+ GLES_CHK(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
+ GLES_CHK(glActiveTexture(GL_TEXTURE0));
+ GLES_CHK(glBindTexture(GL_TEXTURE_2D, textureID));
+
+ UseShader(shader);
+ SetUniformMatrix4x4f(shader, "_glesMVP", Matrix4x4f::identity);
+ SetUniformSample2D(shader, "texDiffuse", 0);
+ DrawVBO(vbo, 6, texCoords);
+
+ void PresentContextGLES();
+ PresentContextGLES();
+ }
+} \ No newline at end of file
diff --git a/Runtime/GfxDevice/opengles30/AssertGLES30.cpp b/Runtime/GfxDevice/opengles30/AssertGLES30.cpp
new file mode 100644
index 0000000..9f20a92
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/AssertGLES30.cpp
@@ -0,0 +1,58 @@
+#include "UnityPrefix.h"
+#include "AssertGLES30.h"
+#include "IncludesGLES30.h"
+
+#if GFX_SUPPORTS_OPENGLES30
+
+using namespace std;
+
+static const char* GetErrorString(GLenum glerr)
+{
+ // Error descriptions taken from OpenGLES 1.1 specification (page 13)
+ switch(glerr)
+ {
+ case GL_NO_ERROR:
+ return "GL_NO_ERROR: No error occured";
+ case GL_INVALID_ENUM:
+ return "GL_INVALID_ENUM: enum argument out of range";
+ case GL_INVALID_VALUE:
+ return "GL_INVALID_VALUE: Numeric argument out of range";
+ case GL_INVALID_OPERATION:
+ return "GL_INVALID_OPERATION: Operation illegal in current state";
+ case GL_OUT_OF_MEMORY:
+ return "GL_OUT_OF_MEMORY: Not enough memory left to execute command";
+ case GL_INVALID_FRAMEBUFFER_OPERATION:
+ return "GL_INVALID_FRAMEBUFFER_OPERATION: Framebuffer is not complete or incompatible with command";
+ default:
+#if UNTIY_WEBGL
+ printf_console("AssertGles20::GetErrorString invoked for unknown error %d",glerr);
+#endif
+ return "Unknown error";
+ }
+}
+
+void CheckOpenGLES3Error (const char *prefix, const char* file, long line)
+{
+ const int kMaxErrors = 10;
+ int counter = 0;
+
+ GLenum glerr;
+ while( (glerr = glGetError ()) != GL_NO_ERROR )
+ {
+ string errorString(GetErrorString(glerr));
+
+ if (prefix)
+ errorString = string(prefix) + ": " + errorString;
+
+ DebugStringToFile (errorString.c_str(), 0, file, line, kAssert);
+
+ ++counter;
+ if( counter > kMaxErrors )
+ {
+ printf_console( "GLES: error count exceeds %i, stop reporting errors\n", kMaxErrors );
+ return;
+ }
+ }
+}
+
+#endif // GFX_SUPPORTS_OPENGLES30
diff --git a/Runtime/GfxDevice/opengles30/AssertGLES30.h b/Runtime/GfxDevice/opengles30/AssertGLES30.h
new file mode 100644
index 0000000..bb9c26d
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/AssertGLES30.h
@@ -0,0 +1,40 @@
+#ifndef GLES_ASSERTGLES30_H
+#define GLES_ASSERTGLES30_H
+
+#include "Runtime/Utilities/LogAssert.h"
+
+void CheckOpenGLES3Error (const char *prefix, const char* file, long line);
+
+#if !UNITY_RELEASE || UNITY_WEBGL
+ #ifndef GLESAssert
+ /// Asserts for checking the OpenGL error state
+ #define GLESAssert() { CheckOpenGLES3Error (NULL, __FILE__, __LINE__); }
+ #endif
+ #define GLESAssertString(x) { CheckOpenGLES3Error (x, __FILE__, __LINE__); }
+
+ #define GLES30_PRINT_GL_TRACE 0
+ #if GLES30_PRINT_GL_TRACE
+ #define GLESCHKSTRINGIFY(x) GLESCHKSTRINGIFY2(x)
+ #define GLESCHKSTRINGIFY2(x) #x
+
+ #define GLES_CHK(x) do { {printf_console("GLES: %s %s %d\n", GLESCHKSTRINGIFY(x), __FILE__, __LINE__); x;} GLESAssert(); } while(0)
+ #else
+ #define GLES_CHK(x) do { {x;} GLESAssert(); } while(0)
+ #endif
+#else
+
+ #ifndef GLESAssert
+ #define GLESAssert()
+ #endif
+ #define GLESAssertString(x)
+ #define GLES_CHK(x) x
+
+
+#endif
+
+
+//#define GLES_CHK(x) do {} while(0)
+//#define GLES_CHK(x)
+//#define GLES_CHK(x) do { printf_console("GLES: %s %d\n", __FILE__, __LINE__); } while(0)
+
+#endif
diff --git a/Runtime/GfxDevice/opengles30/CombinerGLES30.cpp b/Runtime/GfxDevice/opengles30/CombinerGLES30.cpp
new file mode 100644
index 0000000..51f64c4
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/CombinerGLES30.cpp
@@ -0,0 +1,21 @@
+#include "UnityPrefix.h"
+#include "CombinerGLES30.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "VBOGLES30.h"
+
+#if GFX_SUPPORTS_OPENGLES30
+
+TextureCombinersGLES3* TextureCombinersGLES3::Create (int count, const ShaderLab::TextureBinding* texEnvs)
+{
+ // check if we have enough vertex attributes to emulate this combiner
+ if (count + kGLES3AttribLocationTexCoord0 >= gGraphicsCaps.gles30.maxAttributes)
+ return NULL;
+
+ // create struct that holds texture combiner info object
+ TextureCombinersGLES3* combiners = new TextureCombinersGLES3();
+ combiners->count = count;
+ combiners->texEnvs = texEnvs;
+ return combiners;
+}
+
+#endif // GFX_SUPPORTS_OPENGLES30
diff --git a/Runtime/GfxDevice/opengles30/CombinerGLES30.h b/Runtime/GfxDevice/opengles30/CombinerGLES30.h
new file mode 100644
index 0000000..4073e99
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/CombinerGLES30.h
@@ -0,0 +1,10 @@
+#pragma once
+
+namespace ShaderLab { struct TextureBinding; }
+
+struct TextureCombinersGLES3
+{
+ static TextureCombinersGLES3* Create (int count, const ShaderLab::TextureBinding* texEnvs);
+ int count;
+ const ShaderLab::TextureBinding* texEnvs;
+};
diff --git a/Runtime/GfxDevice/opengles30/ConstantBuffersGLES30.cpp b/Runtime/GfxDevice/opengles30/ConstantBuffersGLES30.cpp
new file mode 100644
index 0000000..4a64c08
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/ConstantBuffersGLES30.cpp
@@ -0,0 +1,338 @@
+#include "UnityPrefix.h"
+#include "ConstantBuffersGLES30.h"
+#include "AssertGLES30.h"
+
+// NEVER enable this for release! Turns off all CB caching and makes things
+// very slow, for debugging.
+#define DEBUG_DISABLE_CONSTANT_BUFFER_CACHES (0 && !UNITY_RELEASE)
+
+#define CONSTANT_BUFFER_ID_MASK 0xB0000000
+#define MAKE_CONSTANT_BUFFER_ID(id) (id|CONSTANT_BUFFER_ID_MASK)
+
+
+#if DEBUG_GLES30_UNIFORM_BUFFER_STATS
+#include "External/shaderlab/Library/FastPropertyName.h"
+extern std::string g_LastParsedShaderName;
+#endif
+
+ConstantBuffersGLES30::ConstantBuffersGLES30()
+{
+ memset (m_ActiveBuffers, 0, sizeof(m_ActiveBuffers));
+}
+
+void ConstantBuffersGLES30::Clear()
+{
+ memset (m_ActiveBuffers, 0, sizeof(m_ActiveBuffers));
+ for (size_t i = 0; i < m_Buffers.size(); ++i)
+ {
+ ConstBuffer& cb = m_Buffers[i];
+ delete[] cb.data;
+ if (cb.buffer)
+ {
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(MAKE_CONSTANT_BUFFER_ID(cb.buffer));
+ GLES_CHK(glDeleteBuffers(1, (GLuint*)&cb.buffer));
+ }
+ #if DEBUG_GLES30_CONSTANT_BUFFER_STATS
+ delete[] cb.changeCounts;
+ delete[] cb.tryCounts;
+ #endif
+ }
+ m_Buffers.clear();
+ m_BufferKeys.clear();
+}
+
+void ConstantBuffersGLES30::SetCBInfo(int id, int size)
+{
+ size_t n = m_Buffers.size();
+ Assert (m_BufferKeys.size() == n);
+ UInt32 key = id | (size<<16);
+ for (size_t i = 0; i < n; ++i)
+ {
+ if (m_BufferKeys[i] == key)
+ return;
+ }
+
+ // not found, create one
+ ConstBuffer cb;
+ cb.data = new UInt8[size];
+ memset (cb.data, 0, size);
+ cb.dirty = true;
+ cb.bindIndex = -1;
+ #if DEBUG_GLES30_CONSTANT_BUFFER_STATS
+ cb.statsDirty = 0;
+ cb.stats = 0;
+ ShaderLab::FastPropertyName name;
+ name.index = id;
+ printf_console ("GLES30 Uniform Buffer Info: new %s size=%i shader=%s\n", name.GetName(), size, g_LastParsedShaderName.c_str());
+ cb.changeCounts = new int[size/4];
+ memset (cb.changeCounts, 0, size);
+ cb.tryCounts = new int[size/4];
+ memset (cb.tryCounts, 0, size);
+ #endif
+
+ GLES_CHK(glGenBuffers(1, (GLuint*)&cb.buffer));
+ GLES_CHK(glBindBuffer(GL_UNIFORM_BUFFER, cb.buffer));
+ GLES_CHK(glBufferData(GL_UNIFORM_BUFFER, size, NULL, GL_DYNAMIC_DRAW));
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(MAKE_CONSTANT_BUFFER_ID(cb.buffer),size,this);
+
+ m_Buffers.push_back (cb);
+ m_BufferKeys.push_back (key);
+}
+
+int ConstantBuffersGLES30::FindAndBindCB (int id, int bind, int size)
+{
+ UInt32 key = id | (size<<16);
+ int idx = 0;
+ for (ConstBufferKeys::const_iterator it = m_BufferKeys.begin(), itEnd = m_BufferKeys.end(); it != itEnd; ++it, ++idx)
+ {
+ if (*it == key)
+ {
+ ConstBuffer& cb = m_Buffers[idx];
+ if (bind >= 0)
+ {
+ cb.bindIndex = bind;
+ }
+ return idx;
+ }
+ }
+ Assert (false);
+ return -1;
+}
+
+void ConstantBuffersGLES30::ResetBinds()
+{
+ for (ConstBuffers::iterator it = m_Buffers.begin(), itEnd = m_Buffers.end(); it != itEnd; ++it)
+ {
+ it->bindIndex = -1;
+ }
+}
+
+void ConstantBuffersGLES30::SetCBConstant (int idx, int offset, const void* data, int size)
+{
+ Assert (idx >= 0 && idx < m_Buffers.size());
+ ConstBuffer& cb = m_Buffers[idx];
+ Assert (offset >= 0 && offset+size <= (m_BufferKeys[idx]>>16) && size > 0);
+
+ #if DEBUG_GLES30_CONSTANT_BUFFER_STATS
+ for (int i = offset/4; i < offset/4+size/4; ++i)
+ ++cb.tryCounts[i];
+ #endif
+
+ if (size == 4)
+ {
+ UInt32* dstData = (UInt32*)(cb.data+offset);
+ UInt32 srcData = *(UInt32*)data;
+ if (DEBUG_DISABLE_CONSTANT_BUFFER_CACHES || *dstData != srcData)
+ {
+ *dstData = srcData;
+ cb.dirty = true;
+
+ #if DEBUG_GLES30_CONSTANT_BUFFER_STATS
+ for (int i = offset/4; i < offset/4+size/4; ++i)
+ ++cb.changeCounts[i];
+ #endif
+ }
+ }
+ else
+ {
+ if (DEBUG_DISABLE_CONSTANT_BUFFER_CACHES || memcmp(cb.data+offset, data, size) != 0)
+ {
+ memcpy (cb.data+offset, data, size);
+ cb.dirty = true;
+
+ #if DEBUG_GLES30_CONSTANT_BUFFER_STATS
+ for (int i = offset/4; i < offset/4+size/4; ++i)
+ ++cb.changeCounts[i];
+ #endif
+ }
+ }
+}
+
+void ConstantBuffersGLES30::UpdateBuffers ()
+{
+ size_t n = m_Buffers.size();
+
+ #if !UNITY_RELEASE
+ // check if we have duplicate buffers bound to the same slot (should never happen!)
+ UInt32 bound = 0;
+ for (size_t i = 0; i < n; ++i)
+ {
+ ConstBuffer& cb = m_Buffers[i];
+ int bind = cb.bindIndex;
+ if (bind >= 0 && bind < 32)
+ {
+ Assert (!(bound & (1<<bind)));
+ bound |= (1<<bind);
+ }
+ }
+ #endif
+
+
+ for (size_t i = 0; i < n; ++i)
+ {
+ ConstBuffer& cb = m_Buffers[i];
+ if (DEBUG_DISABLE_CONSTANT_BUFFER_CACHES || cb.dirty)
+ {
+ GLES_CHK(glBindBuffer(GL_UNIFORM_BUFFER, cb.buffer));
+
+ UInt32 bufferSize = (m_BufferKeys[i]>>16);
+ GLES_CHK(glBufferData(GL_UNIFORM_BUFFER, bufferSize, cb.data, GL_DYNAMIC_DRAW));
+ //void* data = glMapBufferRange(GL_UNIFORM_BUFFER, 0, bufferSize, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
+ //GLESAssert();
+ //if (data != NULL)
+ //{
+ // ::memcpy(data, cb.data, bufferSize);
+ //}
+ //GLES_CHK(glUnmapBuffer(GL_UNIFORM_BUFFER));
+ #if DEBUG_GLES30_CONSTANT_BUFFER_STATS
+ ++cb.statsDirty;
+ #endif
+ }
+
+ // Bind
+ int bindIndex = cb.bindIndex;
+ if (bindIndex >= 0 && (DEBUG_DISABLE_CONSTANT_BUFFER_CACHES || m_ActiveBuffers[bindIndex] != cb.buffer))
+ {
+ GLES_CHK(glBindBufferBase(GL_UNIFORM_BUFFER, bindIndex, cb.buffer));
+ }
+ cb.dirty = false;
+ }
+}
+
+#if 0
+
+void ConstantBuffersD3D11::SetBuiltinCBConstant (int id, int offset, const void* data, int size)
+{
+ int idx = GetCBIndexByID (id);
+ ConstBuffer& cb = m_Buffers[idx];
+ Assert (offset >= 0 && offset+size <= (m_BufferKeys[idx]>>16) && size > 0);
+
+ #if DEBUG_D3D11_CONSTANT_BUFFER_STATS
+ for (int i = offset/4; i < offset/4+size/4; ++i)
+ ++cb.tryCounts[i];
+ #endif
+
+ if (DEBUG_DISABLE_CONSTANT_BUFFER_CACHES || memcmp(cb.data+offset, data, size) != 0)
+ {
+ memcpy (cb.data+offset, data, size);
+ cb.dirty = true;
+
+ #if DEBUG_D3D11_CONSTANT_BUFFER_STATS
+ for (int i = offset/4; i < offset/4+size/4; ++i)
+ ++cb.changeCounts[i];
+ #endif
+ }
+}
+
+#if DEBUG_D3D11_CONSTANT_BUFFER_STATS
+
+static void WriteTGAFile (const char* filename, int width, int height, const UInt8* bgr)
+{
+ FILE* f = fopen(filename, "wb");
+ // header
+ putc(0,f);
+ putc(0,f);
+ putc(2,f); // uncompressed RGB
+ putc(0,f); putc(0,f);
+ putc(0,f); putc(0,f);
+ putc(0,f);
+ putc(0,f); putc(0,f);
+ putc(0,f); putc(0,f);
+ putc((width & 0x00FF),f);
+ putc((width & 0xFF00)>>8,f);
+ putc((height & 0x00FF),f);
+ putc((height & 0xFF00)>>8,f);
+ putc(24,f); // 24 bit
+ putc(0x20,f); // vertical flip
+ // data
+ fwrite (bgr, 3, width*height, f);
+ fclose (f);
+}
+
+static void DensityToBGR (int density, UInt8* bgr)
+{
+ if (density < 1)
+ {
+ bgr[0] = bgr[1] = bgr[2] = 0;
+ return;
+ }
+ bgr[0] = clamp(40+density/4, 0, 255);
+ bgr[1] = clamp(40+density/4, 0, 255);
+ bgr[2] = clamp(40+density/4, 0, 255);
+}
+
+static void PutBGRPixelBlock (UInt8* img, int imgWidth, int x, int y, const UInt8* bgr)
+{
+ for (int i = 0; i < 4; ++i)
+ {
+ UInt8* ptr = img + ((y+i)*imgWidth+x) * 3;
+ for (int j = 0; j < 4; ++j, ptr += 3)
+ {
+ ptr[0] = bgr[0];
+ ptr[1] = bgr[1];
+ ptr[2] = bgr[2];
+ }
+ }
+}
+
+
+void ConstantBuffersD3D11::NewFrame()
+{
+ if (GetAsyncKeyState(VK_F7))
+ {
+ printf_console ("DX11 Constant Buffer stats:\n");
+ float traffic = 0.0f;
+ int uploads = 0;
+ int maxSize = 0;
+ for (size_t i = 0; i < m_BufferKeys.size(); ++i)
+ maxSize = std::max(int(m_BufferKeys[i]>>16), maxSize);
+
+ int imgWidth = maxSize+1;
+ int imgHeight = m_Buffers.size()*3*4;
+ UInt8* imgData = new UInt8[imgWidth*imgHeight*3];
+ memset (imgData, 0, imgWidth*imgHeight*3);
+
+ for (size_t i = 0; i < m_Buffers.size(); ++i)
+ {
+ ConstBuffer& cb = m_Buffers[i];
+ int cbId = (m_BufferKeys[i]&0xFFFF);
+ int cbSize = (m_BufferKeys[i]>>16);
+ ShaderLab::FastPropertyName name;
+ name.index = cbId;
+ traffic += (cbSize*cb.statsDirty)/1024.0f;
+ uploads += cb.statsDirty;
+ printf_console (" %s size:%i (%.1fkB in %i upl) vs:%i ps:%i\n", name.GetName(), cbSize, (cbSize*cb.statsDirty)/1024.0f, cb.statsDirty, cb.statsVS, cb.statsPS);
+ if (cb.statsDirty > 0)
+ {
+ for (int j = 0; j < cbSize/4; ++j)
+ {
+ UInt8 bgr[3];
+ DensityToBGR (cb.tryCounts[j], bgr);
+ PutBGRPixelBlock (imgData, imgWidth, j*4, i*3*4, bgr);
+ DensityToBGR (cb.changeCounts[j], bgr);
+ PutBGRPixelBlock (imgData, imgWidth, j*4, i*3*4+4, bgr);
+ }
+ }
+ for (int j = 0; j < 8; ++j)
+ {
+ imgData[((i*3*4+j)*imgWidth + cbSize)*3 + 1] = 255;
+ }
+ }
+ WriteTGAFile ("cbStats.tga", imgWidth, imgHeight, imgData);
+ delete[] imgData;
+ printf_console (" =%i uploads, %.1fkB traffic\n\n", uploads, traffic);
+ }
+
+ // reset stats
+ for (size_t i = 0; i < m_Buffers.size(); ++i)
+ {
+ ConstBuffer& cb = m_Buffers[i];
+ int cbSize = (m_BufferKeys[i]>>16);
+ cb.statsDirty = cb.statsVS = cb.statsPS = 0;
+ memset (cb.changeCounts, 0, cbSize/4*4);
+ memset (cb.tryCounts, 0, cbSize/4*4);
+ }
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/Runtime/GfxDevice/opengles30/ConstantBuffersGLES30.h b/Runtime/GfxDevice/opengles30/ConstantBuffersGLES30.h
new file mode 100644
index 0000000..a22eba2
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/ConstantBuffersGLES30.h
@@ -0,0 +1,72 @@
+#pragma once
+
+#include "IncludesGLES30.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+
+#define DEBUG_GLES30_CONSTANT_BUFFER_STATS 0
+
+#if DEBUG_GLES30_CONSTANT_BUFFER_STATS
+#include <map>
+#endif
+
+
+class ConstantBuffersGLES30
+{
+public:
+ ConstantBuffersGLES30();
+ ~ConstantBuffersGLES30() { Clear(); }
+
+ void Clear();
+
+ struct ConstBuffer {
+ int bindIndex;
+ bool dirty;
+ UInt8* data;
+ UInt32 buffer;
+ #if DEBUG_GLES30_CONSTANT_BUFFER_STATS
+ int statsDirty;
+ int stats
+ int* tryCounts;
+ int* changeCounts;
+ #endif
+ };
+
+ void SetCBInfo (int id, int size);
+ int FindAndBindCB (int id, int bind, int size);
+ void ResetBinds ();
+
+ void SetBuiltinCBConstant (int id, int offset, const void* data, int size);
+ void SetCBConstant (int index, int offset, const void* data, int size);
+
+ void UpdateBuffers();
+ void NewFrame();
+
+private:
+ inline int GetCBIndexByID (int id) const
+ {
+ UInt32 key = id;
+ int n = m_BufferKeys.size();
+ for (int i = 0; i < n; ++i)
+ {
+ if ((m_BufferKeys[i]&0xFFFF) == key)
+ return i;
+ }
+ Assert (false);
+ return -1;
+ }
+
+private:
+ typedef std::vector<UInt32> ConstBufferKeys;
+ typedef std::vector<ConstBuffer> ConstBuffers;
+ ConstBufferKeys m_BufferKeys;
+ ConstBuffers m_Buffers;
+
+ UInt32 m_ActiveBuffers[16];
+};
+
+
+#if !DEBUG_GLES30_CONSTANT_BUFFER_STATS
+inline void ConstantBuffersGLES30::NewFrame() { }
+#endif
+
diff --git a/Runtime/GfxDevice/opengles30/ContextGLES30.cpp b/Runtime/GfxDevice/opengles30/ContextGLES30.cpp
new file mode 100644
index 0000000..812d81b
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/ContextGLES30.cpp
@@ -0,0 +1,288 @@
+#include "UnityPrefix.h"
+#include "ContextGLES30.h"
+#include "IncludesGLES30.h"
+#include "AssertGLES30.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Misc/QualitySettings.h"
+
+#if GFX_SUPPORTS_OPENGLES30
+
+#if UNITY_WIN
+
+#define EGL_OPENGL_ES3_BIT_KHR 0x00000040
+
+
+struct EGLESData
+{
+ void* dsp;
+ void* cfg;
+ void* cxt;
+ void* surf;
+ EGLESData():dsp(NULL),cfg(NULL),cxt(NULL),surf(NULL){}
+};
+
+
+static EGLESData sOpenGLESData;
+
+bool InitializeGLES30 ()
+{
+ HWND hwnd = GetScreenManager().GetWindow();
+ if (hwnd)
+ {
+ CreateContextGLES30 (hwnd);
+ return true;
+ }
+ ErrorString ("gles20: Can't initialize because HWND not set up");
+ return false;
+}
+void ShutdownGLES30 ()
+{
+ DestroyContextGLES30();
+}
+bool IsContextGLES30Created()
+{
+ return sOpenGLESData.surf != NULL &&
+ sOpenGLESData.cxt != NULL &&
+ sOpenGLESData.cfg != NULL &&
+ sOpenGLESData.dsp != NULL;
+}
+
+bool CreateContextGLES30(HWND hWnd)
+{
+ //Just in case
+ DestroyContextGLES30();
+
+ EGLint numConfigs;
+ EGLint majorVersion;
+ EGLint minorVersion;
+
+#if UNITY_WIN
+
+ int sampleCount = GetQualitySettings().GetCurrent().antiAliasing;
+
+ /// Build up the attribute list
+ const EGLint configAttribs[] =
+ {
+ EGL_LEVEL, 0,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
+ EGL_NATIVE_RENDERABLE, EGL_FALSE,
+ EGL_DEPTH_SIZE, 16,
+ EGL_ALPHA_SIZE, 1,
+ EGL_STENCIL_SIZE, 1,
+ EGL_SAMPLES, sampleCount,
+ EGL_NONE
+ };
+
+ // Get Display
+ sOpenGLESData.dsp = eglGetDisplay( hWnd?GetDC(hWnd):EGL_DEFAULT_DISPLAY );
+ if ( sOpenGLESData.dsp == EGL_NO_DISPLAY )
+ {
+ printf_console("GLES30: eglGetDisplay failed\n" );
+ return false;
+ }
+ //Hack : eglInitialize invokes WM_ACTIVATE message, and gAppActive is already true, so Unity will try to call some functions which requires some initialization,
+ // and this is not done yet
+ extern bool gAlreadyClosing;
+ bool last = gAlreadyClosing;
+ gAlreadyClosing = true;
+ // Initialize EGL
+ if ( ! eglInitialize( sOpenGLESData.dsp, &majorVersion, &minorVersion) )
+ {
+ printf_console("GLES30: eglInitialize failed\n");
+ return false;
+ }
+
+
+ // Choose config
+ if ( !eglChooseConfig(sOpenGLESData.dsp, configAttribs, &sOpenGLESData.cfg, 1, &numConfigs) )
+ {
+ printf_console("GLES30: eglChooseConfig failed\n");
+ return false;
+ }
+
+
+ // Create a surface
+ sOpenGLESData.surf = eglCreateWindowSurface( sOpenGLESData.dsp, sOpenGLESData.cfg, NativeWindowType( hWnd ), NULL );
+ if ( sOpenGLESData.surf == EGL_NO_SURFACE )
+ {
+ printf_console("GLES30: eglCreateWindowSurface failed\n");
+ return false;
+ }
+
+ // Create a GL context
+ EGLint ctxAttribList[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
+ sOpenGLESData.cxt = eglCreateContext( sOpenGLESData.dsp, sOpenGLESData.cfg, EGL_NO_CONTEXT, ctxAttribList );
+ if ( sOpenGLESData.cxt == EGL_NO_CONTEXT )
+ {
+ printf_console("GLES30: eglCreateContext failed\n");
+ return false;
+ }
+
+ // Make the context current
+ if ( ! eglMakeCurrent( sOpenGLESData.dsp, sOpenGLESData.surf, sOpenGLESData.surf, sOpenGLESData.cxt ) )
+ {
+ printf_console("GLES30: eglMakeCurrent failed\n");
+ return false;
+ }
+
+ gAlreadyClosing = last;
+#endif
+
+ GLESAssert();
+
+ return true;
+}
+
+
+void DestroyContextGLES30()
+{
+ if(sOpenGLESData.dsp)
+ {
+ eglMakeCurrent(sOpenGLESData.dsp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) ;
+ eglDestroyContext( sOpenGLESData.dsp, sOpenGLESData.cxt );
+ eglDestroySurface( sOpenGLESData.dsp, sOpenGLESData.surf );
+ eglTerminate( sOpenGLESData.dsp);
+ }
+ sOpenGLESData.surf = NULL;
+ sOpenGLESData.cxt = NULL;
+ sOpenGLESData.cfg = NULL;
+ sOpenGLESData.dsp = NULL;
+}
+void PresentContextGLES30()
+{
+ eglSwapBuffers( sOpenGLESData.dsp, sOpenGLESData.surf );
+}
+
+void AcquireGLES30Context()
+{
+ if(sOpenGLESData.dsp)
+ {
+ // Make the context current
+ if ( ! eglMakeCurrent( sOpenGLESData.dsp, sOpenGLESData.surf, sOpenGLESData.surf, sOpenGLESData.cxt ) )
+ {
+ printf_console("GLES30: eglMakeCurrent failed\n");
+ }
+ }
+}
+
+void ReleaseGLES30Context()
+{
+ if(sOpenGLESData.dsp)
+ {
+ eglMakeCurrent(sOpenGLESData.dsp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) ;
+ }
+}
+
+
+#elif UNITY_LINUX
+
+static EGLDisplay gEGLDisplay = EGL_NO_DISPLAY;
+static EGLConfig gEGLConfig;
+static EGLSurface gEGLSurface = EGL_NO_SURFACE;
+static EGLContext gEGLContext = EGL_NO_CONTEXT;
+
+void SetEGLDisplay(EGLDisplay display)
+{
+ gEGLDisplay = display;
+}
+
+void SetEGLConfig(const EGLConfig &config)
+{
+ gEGLConfig = config;
+}
+
+bool InitializeGLES30 ()
+{
+ Window window = 0;
+ window = GetScreenManager().GetWindow();
+
+ if(window)
+ {
+ CreateContextGLES30(window);
+ return true;
+ }
+
+ return false;
+}
+
+void ShutdownGLES30 ()
+{
+ DestroyContextGLES30();
+}
+
+bool IsContextGLES30Created()
+{
+ return gEGLSurface != EGL_NO_SURFACE && gEGLContext != EGL_NO_CONTEXT;
+}
+
+bool CreateContextGLES30(Window window)
+{
+ DestroyContextGLES30();
+
+ gEGLSurface = eglCreateWindowSurface(gEGLDisplay, gEGLConfig, window, NULL);
+ if(gEGLSurface == EGL_NO_SURFACE)
+ {
+ printf_console("eglCreateWindowSurface failed\n");
+ return false;
+ }
+
+ // Create a context
+ printf_console("Creating context\n");
+ EGLint ctxAttribList[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
+ gEGLContext = eglCreateContext(gEGLDisplay, gEGLConfig, EGL_NO_CONTEXT, ctxAttribList );
+ if ( gEGLContext == EGL_NO_CONTEXT )
+ {
+ printf_console( "eglCreateContext failed\n" );
+ return false;
+ }
+
+ if(!eglMakeCurrent(gEGLDisplay, gEGLSurface, gEGLSurface, gEGLContext))
+ {
+ printf_console("eglMakeCurrent failed\n");
+ }
+
+ return true;
+}
+
+void DestroyContextGLES30()
+{
+ if(IsContextGLES30Created())
+ {
+ eglMakeCurrent(gEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroyContext(gEGLDisplay, gEGLContext);
+ eglDestroySurface(gEGLDisplay, gEGLSurface);
+ }
+
+ gEGLSurface = EGL_NO_SURFACE;
+ gEGLContext = EGL_NO_CONTEXT;
+}
+
+void PresentContextGLES()
+{
+ eglSwapBuffers(gEGLDisplay, gEGLSurface);
+}
+
+void AcquireGLES30Context()
+{
+ if(gEGLDisplay)
+ {
+ // Make the context current
+ if(!eglMakeCurrent(gEGLDisplay, gEGLSurface, gEGLSurface, gEGLContext))
+ {
+ printf_console("GLES30: eglMakeCurrent failed\n");
+ }
+ }
+}
+
+void ReleaseGLES30Context()
+{
+ if(gEGLDisplay)
+ {
+ eglMakeCurrent(gEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ }
+}
+
+
+#endif // UNITY_WIN
+#endif // GFX_SUPPORTS_OPENGLES30
diff --git a/Runtime/GfxDevice/opengles30/ContextGLES30.h b/Runtime/GfxDevice/opengles30/ContextGLES30.h
new file mode 100644
index 0000000..c509f57
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/ContextGLES30.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include "Runtime/GfxDevice/GfxDeviceObjects.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+#if UNITY_LINUX
+#include <X11/Xlib.h>
+#include <GLES3/gl2.h>
+#endif
+
+#if UNITY_BB10
+#include <GLES3/gl2.h>
+#include <screen/screen.h>
+#endif
+
+#if UNITY_WIN || UNITY_LINUX || UNITY_BB10 || UNITY_ANDROID
+bool InitializeGLES30 ();
+void ShutdownGLES30 ();
+bool IsContextGLES30Created();
+#if UNITY_WIN
+bool CreateContextGLES30(HWND hWnd);
+#elif UNITY_LINUX
+bool CreateContextGLES30(Window window);
+#elif UNITY_BB10
+bool CreateContextGLES30(screen_window_t window);
+void ResizeContextGLES30(screen_window_t window, int width, int height);
+#endif
+void DestroyContextGLES30();
+#endif
+void PresentContextGLES();
+void PresentContextGLES30();
+
+void ReleaseGLES30Context();
+
+void AcquireGLES30Context();
+
+
diff --git a/Runtime/GfxDevice/opengles30/DataBuffersGLES30.cpp b/Runtime/GfxDevice/opengles30/DataBuffersGLES30.cpp
new file mode 100644
index 0000000..24484d7
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/DataBuffersGLES30.cpp
@@ -0,0 +1,429 @@
+#include "UnityPrefix.h"
+#include "DataBuffersGLES30.h"
+
+#if GFX_SUPPORTS_OPENGLES30
+
+#include "AssertGLES30.h"
+#include "IncludesGLES30.h"
+#include "Runtime/GfxDevice/GLDataBufferCommon.h"
+
+#include <algorithm>
+
+#if 1
+ #define DBG_LOG_BUF_GLES30(...) {}
+#else
+ #define DBG_LOG_BUF_GLES30(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+#endif
+
+static const float kBufferAllocateWeight = 10.0f; //! Default weight for buffer allocation from scratch.
+static const float kBufferSizeWeightFactor = 1.f/8096.f; //!< Weight factor for size difference.
+static const float kBufferUsageDiffWeight = 8.f; //!< Weight factor for usage difference.
+
+static const float kBufferDeleteSizeWeightFactor = 1.f/16384.f; //!< Deleting scale factor.
+static const float kBufferDeleteMaxWeightFactor = 300.0f; //!< ...
+static const float kBufferDeleteMinWeight = 30.0f; //!< Required weight for deleting buffer.
+
+static const UInt32 kBufferPruneFrequency = 10; //!< Unneeded buffers are pruned once per kBufferPruneFrequency frames.
+
+bool BufferUpdateCausesStallGLES30 (const DataBufferGLES30* buffer)
+{
+ // \note If needed, the min age should me made ES3 specific graphics capability.
+ return buffer->GetRenderAge() < kBufferUpdateMinAgeGLES30;
+}
+
+//! Computes weight for sorting buffers
+static float ComputeBufferWeight (const DataBufferGLES30* buffer, int desiredSize, UInt32 desiredUsage)
+{
+ const int bufferSize = buffer->GetSize();
+ const UInt32 bufferUsage = buffer->GetUsage();
+
+ if (buffer->GetSize() == 0)
+ return kBufferAllocateWeight;
+
+ const int sizeDiff = std::abs(bufferSize-desiredSize);
+
+ return float(sizeDiff)*kBufferSizeWeightFactor + ((bufferUsage != desiredUsage) ? kBufferUsageDiffWeight : 0.f);
+}
+
+//! Compute weight for eliminating old buffers.
+static float ComputeBufferDeleteWeight (const DataBufferGLES30* buffer)
+{
+ // \todo [2013-05-13 pyry] Take into account current memory pressure and buffer size.
+ const UInt32 renderAge = buffer->GetRenderAge();
+ return float(renderAge) - std::min(kBufferDeleteMaxWeightFactor, float(buffer->GetSize())*kBufferDeleteSizeWeightFactor);
+}
+
+struct WeightedBufferGLES3
+{
+ int sizeClass;
+ int bufferNdx;
+ float weight;
+
+ WeightedBufferGLES3 (int sizeClass_, int bufferNdx_, float weight_)
+ : sizeClass (sizeClass_)
+ , bufferNdx (bufferNdx_)
+ , weight (weight_)
+ {
+ }
+};
+
+struct CompareWeightsLess
+{
+ inline bool operator() (const WeightedBufferGLES3& a, const WeightedBufferGLES3& b) const
+ {
+ return a.weight < b.weight;
+ }
+};
+
+struct CompareWeightsGreater
+{
+ inline bool operator() (const WeightedBufferGLES3& a, const WeightedBufferGLES3& b) const
+ {
+ return a.weight > b.weight;
+ }
+};
+
+// DataBufferGLES30
+
+static const GLenum kBufferTarget = GL_COPY_WRITE_BUFFER; //!< Target used for buffer operations.
+
+DataBufferGLES30::DataBufferGLES30 (BufferManagerGLES30& bufferManager)
+ : m_manager (bufferManager)
+ , m_buffer (0)
+ , m_size (0)
+ , m_usage (0)
+ , m_lastRecreated (0)
+ , m_lastUpdated (0)
+ , m_lastRendered (0)
+{
+ GLES_CHK(glGenBuffers(1, (GLuint*)&m_buffer));
+}
+
+DataBufferGLES30::~DataBufferGLES30 (void)
+{
+ if (m_buffer)
+ glDeleteBuffers(1, (GLuint*)&m_buffer);
+}
+
+void DataBufferGLES30::Disown (void)
+{
+ m_buffer = 0;
+}
+
+void DataBufferGLES30::Release (void)
+{
+ m_manager.ReleaseBuffer(this);
+}
+
+void DataBufferGLES30::RecreateStorage (int size, UInt32 usage)
+{
+ RecreateWithData(size, usage, 0);
+}
+
+void DataBufferGLES30::RecreateWithData (int size, UInt32 usage, const void* data)
+{
+ glBindBuffer(kBufferTarget, m_buffer);
+ glBufferData(kBufferTarget, size, data, usage);
+ glBindBuffer(kBufferTarget, 0);
+ GLESAssert();
+
+ RecordRecreate(size, usage);
+}
+
+void DataBufferGLES30::Upload (int offset, int size, const void* data)
+{
+ glBindBuffer(kBufferTarget, m_buffer);
+ glBufferSubData(kBufferTarget, offset, size, data);
+ glBindBuffer(kBufferTarget, 0);
+ GLESAssert();
+
+ RecordUpdate();
+}
+
+void* DataBufferGLES30::Map (int offset, int size, UInt32 mapBits)
+{
+ glBindBuffer(kBufferTarget, m_buffer);
+ void* ptr = glMapBufferRange(kBufferTarget, offset, size, mapBits);
+ glBindBuffer(kBufferTarget, 0);
+ GLESAssert();
+
+ return ptr;
+}
+
+void DataBufferGLES30::FlushMappedRange (int offset, int size)
+{
+ glBindBuffer(kBufferTarget, m_buffer);
+ glFlushMappedBufferRange(kBufferTarget, offset, size);
+ glBindBuffer(kBufferTarget, 0);
+ GLESAssert();
+}
+
+void DataBufferGLES30::Unmap (void)
+{
+ glBindBuffer(kBufferTarget, m_buffer);
+ glUnmapBuffer(kBufferTarget);
+ glBindBuffer(kBufferTarget, 0);
+ GLESAssert();
+}
+
+void DataBufferGLES30::RecordRecreate (int size, UInt32 usage)
+{
+ if (BufferUpdateCausesStallGLES30(this))
+ DBG_LOG_BUF_GLES30("DataBufferGLES30: Warning: buffer with render age %u was recreated!", GetRenderAge());
+
+ m_size = size;
+ m_usage = usage;
+ m_lastRecreated = m_manager.GetFrameIndex();
+
+ // Update GFX mem allocation stats
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(MAKE_DATA_BUFFER_ID(m_buffer));
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(MAKE_DATA_BUFFER_ID(m_buffer), size, this);
+}
+
+void DataBufferGLES30::RecordUpdate (void)
+{
+ if (BufferUpdateCausesStallGLES30(this))
+ DBG_LOG_BUF_GLES30("DataBufferGLES30: Warning: buffer with render age %u was updated!", GetRenderAge());
+
+ m_lastUpdated = m_manager.GetFrameIndex();
+}
+
+void DataBufferGLES30::RecordRender (void)
+{
+ m_lastRendered = m_manager.GetFrameIndex();
+}
+
+// \note Overflow is perfectly ok here.
+
+UInt32 DataBufferGLES30::GetRecreateAge (void) const
+{
+ return m_manager.GetFrameIndex() - m_lastRecreated;
+}
+
+UInt32 DataBufferGLES30::GetUpdateAge (void) const
+{
+ return m_manager.GetFrameIndex() - m_lastUpdated;
+}
+
+UInt32 DataBufferGLES30::GetRenderAge (void) const
+{
+ return m_manager.GetFrameIndex() - m_lastRendered;
+}
+
+// BufferManagerGLES30
+
+BufferManagerGLES30::BufferManagerGLES30 (void)
+ : m_frameIndex(0)
+{
+}
+
+BufferManagerGLES30::~BufferManagerGLES30 (void)
+{
+ Clear();
+}
+
+void BufferManagerGLES30::Clear (void)
+{
+ for (std::vector<DataBufferGLES30*>::iterator i = m_pendingBuffers.begin(); i != m_pendingBuffers.end(); i++)
+ delete *i;
+ m_pendingBuffers.clear();
+
+ for (int ndx = 0; ndx < kSizeClassCount; ndx++)
+ {
+ for (std::vector<DataBufferGLES30*>::iterator i = m_liveBuffers[ndx].begin(); i != m_liveBuffers[ndx].end(); i++)
+ delete *i;
+ m_liveBuffers[ndx].clear();
+ }
+}
+
+inline int BufferManagerGLES30::GetSizeClass (int size)
+{
+ for (int ndx = 0; ndx < kSizeClassCount; ndx++)
+ {
+ if (size < GetSizeClassLimit(ndx))
+ return ndx;
+ }
+ Assert(false);
+ return 0;
+}
+
+DataBufferGLES30* BufferManagerGLES30::AcquireBuffer (int size, UInt32 usage)
+{
+ const float maxWeight = kBufferAllocateWeight;
+ const int maxCandidates = 5; // Number of potential candidates to consider actually.
+
+ const int sizeClass = GetSizeClass(size); // Try only that size class
+ int numCandidates = 0; // Number of potential candidates considered
+ int bestBufferNdx = -1;
+ float bestWeight = std::numeric_limits<float>::infinity();
+
+ for (int bufferNdx = 0; bufferNdx < (int)m_liveBuffers[sizeClass].size(); bufferNdx++)
+ {
+ DataBufferGLES30* buffer = m_liveBuffers[sizeClass][bufferNdx];
+ const float weight = ComputeBufferWeight(buffer, size, usage);
+
+ if (weight <= maxWeight && weight < bestWeight)
+ {
+ bestBufferNdx = bufferNdx;
+ bestWeight = weight;
+
+ numCandidates += 1;
+ }
+
+ if (numCandidates >= maxCandidates)
+ break; // Do not try other buffers, sorry.
+ }
+
+ DBG_LOG_BUF_GLES30("BufferManagerGLES30::AcquireBuffer(%d, 0x%04x): tried %d candidates", size, usage, numCandidates);
+
+ if (bestBufferNdx >= 0)
+ {
+ const int bufferNdx = bestBufferNdx;
+ DataBufferGLES30* selectedBuffer = m_liveBuffers[sizeClass][bufferNdx];
+
+ if (bufferNdx+1 != m_liveBuffers[sizeClass].size())
+ std::swap(m_liveBuffers[sizeClass][bufferNdx], m_liveBuffers[sizeClass].back());
+ m_liveBuffers[sizeClass].pop_back();
+
+ DBG_LOG_BUF_GLES30(" => selected buffer [%d]%d, weight = %f", sizeClass, bufferNdx, ComputeBufferWeight(selectedBuffer, size, usage));
+ return selectedBuffer;
+ }
+ else
+ {
+ DBG_LOG_BUF_GLES30(" => creating new buffer");
+ return new DataBufferGLES30(*this);
+ }
+}
+
+void BufferManagerGLES30::ReleaseBuffer (DataBufferGLES30* buffer)
+{
+ if (!BufferUpdateCausesStallGLES30(buffer))
+ InsertIntoLive(buffer);
+ else
+ m_pendingBuffers.push_back(buffer);
+}
+
+void BufferManagerGLES30::AdvanceFrame (void)
+{
+ m_frameIndex += 1; // \note Overflow is ok.
+
+ UpdateLiveSetFromPending();
+
+ // \todo [2013-05-13 pyry] Do we want to do pruning somewhere else as well?
+ if ((m_frameIndex % kBufferPruneFrequency) == 0)
+ PruneFreeBuffers();
+}
+
+void BufferManagerGLES30::UpdateLiveSetFromPending (void)
+{
+ int bufNdx = 0;
+ while (bufNdx < (int)m_pendingBuffers.size())
+ {
+ if (!BufferUpdateCausesStallGLES30(m_pendingBuffers[bufNdx]))
+ {
+ DataBufferGLES30* newLiveBuffer = m_pendingBuffers[bufNdx];
+
+ if (bufNdx+1 != m_pendingBuffers.size())
+ std::swap(m_pendingBuffers[bufNdx], m_pendingBuffers.back());
+ m_pendingBuffers.pop_back();
+
+ InsertIntoLive(newLiveBuffer);
+ // \note bufNdx now contains a new buffer and it must be processed as well. Thus bufNdx is not incremented.
+ }
+ else
+ bufNdx += 1;
+ }
+}
+
+void BufferManagerGLES30::InsertIntoLive (DataBufferGLES30* buffer)
+{
+ const int bufSize = buffer->GetSize();
+ const int sizeClass = GetSizeClass(bufSize);
+
+ m_liveBuffers[sizeClass].push_back(buffer);
+}
+
+UInt32 BufferManagerGLES30::GetTotalFreeSize (void)
+{
+ UInt32 totalBytes = 0;
+
+ for (std::vector<DataBufferGLES30*>::const_iterator bufIter = m_pendingBuffers.begin(); bufIter != m_pendingBuffers.end(); ++bufIter)
+ totalBytes += (*bufIter)->GetSize();
+
+ for (int ndx = 0; ndx < kSizeClassCount; ndx++)
+ {
+ for (std::vector<DataBufferGLES30*>::const_iterator bufIter = m_liveBuffers[ndx].begin(); bufIter != m_liveBuffers[ndx].end(); ++bufIter)
+ totalBytes += (*bufIter)->GetSize();
+ }
+
+ return totalBytes;
+}
+
+void BufferManagerGLES30::PruneFreeBuffers (void)
+{
+ const UInt32 numBytesInFreeList = GetTotalFreeSize();
+ DBG_LOG_BUF_GLES30("BufferManagerGLES30: %u B / %.2f MiB in free buffers", numBytesInFreeList, float(numBytesInFreeList) / float(1<<20));
+
+ // \todo [2013-05-13 pyry] Do this properly - take into account allocated memory size.
+
+ UInt32 numBytesFreed = 0;
+ int numBuffersDeleted = 0;
+
+ // \note pending buffers are ignored. They will end up in live soon anyway.
+ for (int sizeClass = 0; sizeClass < kSizeClassCount; sizeClass++)
+ {
+ int bufNdx = 0;
+ while (bufNdx < m_liveBuffers[sizeClass].size())
+ {
+ DataBufferGLES30* buffer = m_liveBuffers[sizeClass][bufNdx];
+ const float weight = ComputeBufferDeleteWeight(buffer);
+
+ if (weight >= kBufferDeleteMinWeight)
+ {
+ if (bufNdx+1 != m_liveBuffers[sizeClass].size())
+ std::swap(m_liveBuffers[sizeClass][bufNdx], m_liveBuffers[sizeClass].back());
+ m_liveBuffers[sizeClass].pop_back();
+
+ numBytesFreed += buffer->GetSize();
+ numBuffersDeleted += 1;
+
+ delete buffer;
+ }
+ else
+ bufNdx += 1;
+ }
+ }
+
+ DBG_LOG_BUF_GLES30(" => freed %d buffers, %u B / %.2f MiB", numBuffersDeleted, numBytesFreed, float(numBytesFreed) / float(1<<20));
+}
+
+void BufferManagerGLES30::InvalidateAll (void)
+{
+ for (std::vector<DataBufferGLES30*>::iterator iter = m_pendingBuffers.begin(); iter != m_pendingBuffers.end(); ++iter)
+ {
+ (*iter)->Disown();
+ delete *iter;
+ }
+ m_pendingBuffers.clear();
+
+ for (int classNdx = 0; classNdx < kSizeClassCount; classNdx++)
+ {
+ for (std::vector<DataBufferGLES30*>::iterator iter = m_liveBuffers[classNdx].begin(); iter != m_liveBuffers[classNdx].end(); ++iter)
+ {
+ (*iter)->Disown();
+ delete *iter;
+ }
+ m_liveBuffers[classNdx].clear();
+ }
+}
+
+BufferManagerGLES30* g_bufferManager = 0;
+
+BufferManagerGLES30* GetBufferManagerGLES30 (void)
+{
+ if (!g_bufferManager)
+ g_bufferManager = new BufferManagerGLES30();
+ return g_bufferManager;
+}
+
+#endif // GFX_SUPPORTS_OPENGLES30
diff --git a/Runtime/GfxDevice/opengles30/DataBuffersGLES30.h b/Runtime/GfxDevice/opengles30/DataBuffersGLES30.h
new file mode 100644
index 0000000..524dc53
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/DataBuffersGLES30.h
@@ -0,0 +1,137 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/GfxDevice/GfxDeviceConfigure.h"
+
+#if GFX_SUPPORTS_OPENGLES30
+
+#include "Runtime/GfxDevice/GfxDeviceObjects.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+class BufferManagerGLES30;
+
+enum
+{
+ kBufferUpdateMinAgeGLES30 = 2 //!< This many frames must be elapsed since last render before next buffer update. \todo [2013-05-31 pyry] From GfxDevice caps
+};
+
+class DataBufferGLES30
+{
+public:
+ DataBufferGLES30 (BufferManagerGLES30& bufferManager);
+ ~DataBufferGLES30 (void);
+
+ void Release (void); //!< Release to BufferManager.
+
+ UInt32 GetBuffer (void) const { return m_buffer; }
+ int GetSize (void) const { return m_size; }
+ UInt32 GetUsage (void) const { return m_usage; }
+
+ void RecreateStorage (int size, UInt32 usage);
+ void RecreateWithData (int size, UInt32 usage, const void* data);
+
+ void Upload (int offset, int size, const void* data);
+
+ void* Map (int offset, int size, UInt32 mapBits);
+ void FlushMappedRange (int offset, int size);
+ void Unmap (void);
+
+ void RecordRecreate (int size, UInt32 usage); //!< Updates storage parameters and recreate time.
+ void RecordUpdate (void); //!< Updates last update time if buffer was updated manually.
+ void RecordRender (void); //!< Updates last render time.
+
+ UInt32 GetRecreateAge (void) const;
+ UInt32 GetUpdateAge (void) const;
+ UInt32 GetRenderAge (void) const;
+
+ //! Disown and remove buffer handle. Used if destructor should not try to delete buffer..
+ void Disown (void);
+
+private:
+ DataBufferGLES30 (const DataBufferGLES30& other);
+ DataBufferGLES30& operator= (const DataBufferGLES30& other);
+
+ BufferManagerGLES30& m_manager;
+ UInt32 m_buffer;
+ int m_size;
+ UInt32 m_usage;
+
+ // \note Always used to compute relative age and overflow is handled
+ // in computation. Thus frame index can safely overflow.
+ UInt32 m_lastRecreated; //!< Last recreated.
+ UInt32 m_lastUpdated; //!< Frame index when last updated.
+ UInt32 m_lastRendered; //!< Frame index when last rendered.
+};
+
+// BufferManager
+//
+// BufferManager is responsible for allocating and maintaining list of free buffer objects that
+// could be recycled later on. Buffers are either allocated or recycled based on their properties.
+// Most important property for proper use of buffers is to make sure they are not recycled
+// too soon after using them for rendering.
+//
+// BufferManager is only responsible for managing currently free buffers. So user must either
+// release or destroy buffer objects manually. User is also responsible of implementing sane
+// usage patterns for buffers that it owns (for example not updating data right after buffer
+// has been submitted for rendering).
+//
+// Buffers are associated to the BufferManager that was used to create them. Thus user must either
+// destroy buffer, or release it back to same BufferManager.
+//
+// The best usage pattern for leveraging BufferManager is to always release buffers when there
+// is no longer need to preserve the data in buffer object. That way BufferManager takes care
+// of recycling buffer when it is appropriate.
+
+class BufferManagerGLES30
+{
+public:
+ BufferManagerGLES30 (void);
+ ~BufferManagerGLES30 (void);
+
+ //! Acquire a new or recycled buffer. Returns either buffer object that can fit data, or empty buffer (GetSize() == 0).
+ DataBufferGLES30* AcquireBuffer (int size, UInt32 usage);
+ void ReleaseBuffer (DataBufferGLES30* buffer);
+
+ void AdvanceFrame (void); //!< Advance frame index. Must be called at the end of frame.
+ UInt32 GetFrameIndex (void) const { return m_frameIndex; }
+
+ //!< Invalidate all owned buffers. Used on context loss.
+ void InvalidateAll (void);
+
+private:
+ BufferManagerGLES30 (const BufferManagerGLES30& other);
+ BufferManagerGLES30& operator= (const BufferManagerGLES30& other);
+
+ void Clear (void);
+
+ void PruneFreeBuffers (void);
+ UInt32 GetTotalFreeSize (void);
+
+ static inline int GetSizeClassLimit (int classNdx) { return classNdx+1 < kSizeClassCount ? (1<<(classNdx*kSizeClassStepLog2 + kSizeClassBaseLog2)) : INT_MAX; }
+
+ void UpdateLiveSetFromPending(void);
+ void InsertIntoLive (DataBufferGLES30* buffer);
+ static int GetSizeClass (int bufSize);
+
+ UInt32 m_frameIndex; //!< Frame index for computing buffer ages.
+
+ enum
+ {
+ kSizeClassBaseLog2 = 10,
+ kSizeClassStepLog2 = 1,
+ kSizeClassCount = 7
+ };
+
+ // Buffers that can not be selected are in pendingBuffers. Live buffers contain
+ // buffers organized by size into kSizeClassCount classes.
+ std::vector<DataBufferGLES30*> m_pendingBuffers;
+ std::vector<DataBufferGLES30*> m_liveBuffers[kSizeClassCount];
+};
+
+// \todo [2013-05-10 pyry] Do not use singletons...
+BufferManagerGLES30* GetBufferManagerGLES30 (void);
+
+//! Determine if buffer update will likely cause GPU stall.
+bool BufferUpdateCausesStallGLES30 (const DataBufferGLES30* buffer);
+
+#endif // GFX_SUPPORTS_OPENGLES30
diff --git a/Runtime/GfxDevice/opengles30/DebugGLES30.cpp b/Runtime/GfxDevice/opengles30/DebugGLES30.cpp
new file mode 100644
index 0000000..af5efad
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/DebugGLES30.cpp
@@ -0,0 +1,38 @@
+#include "UnityPrefix.h"
+#include "IncludesGLES30.h"
+#include "DebugGLES30.h"
+#include "AssertGLES30.h"
+
+
+void DumpVertexArrayStateGLES30()
+{
+#if GFX_SUPPORTS_OPENGLES30
+ GLint maxVertexAttribs = 0;
+ GLES_CHK(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs));
+
+ GLint vbo = 0;
+ GLint ibo = 0;
+ GLES_CHK(glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vbo));
+ GLES_CHK(glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &ibo));
+
+ printf_console("---> VertexArray State: vbo:%d ibo:%d\n", vbo, ibo);
+
+ for (int q = 0; q < maxVertexAttribs; ++q)
+ {
+ int enabled, size, stride, normalized, type, vbo;
+ GLES_CHK(glGetVertexAttribiv(q, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled));
+ GLES_CHK(glGetVertexAttribiv(q, GL_VERTEX_ATTRIB_ARRAY_SIZE, &size));
+ GLES_CHK(glGetVertexAttribiv(q, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &stride));
+ GLES_CHK(glGetVertexAttribiv(q, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &normalized));
+ GLES_CHK(glGetVertexAttribiv(q, GL_VERTEX_ATTRIB_ARRAY_TYPE, &type));
+ GLES_CHK(glGetVertexAttribiv(q, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &vbo));
+
+ GLvoid* ptr;
+ GLES_CHK(glGetVertexAttribPointerv(q, GL_VERTEX_ATTRIB_ARRAY_POINTER, &ptr));
+
+ printf_console(" attr[%d] --- %s type:%d size:%d stride:%d norm:%d vbo:%d, %p\n",
+ q, enabled? "On ": "Off",
+ type, size, stride, normalized, vbo, ptr);
+ }
+#endif
+}
diff --git a/Runtime/GfxDevice/opengles30/DebugGLES30.h b/Runtime/GfxDevice/opengles30/DebugGLES30.h
new file mode 100644
index 0000000..0468cdf
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/DebugGLES30.h
@@ -0,0 +1,139 @@
+#ifndef DEBUGGLES30_H
+#define DEBUGGLES30_H
+
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/Math/Matrix4x4.h"
+
+
+void DumpVertexArrayStateGLES30();
+
+#if !UNITY_RELEASE
+ #define DBG_LOG_GLES30_ACTIVE 0
+ #define DBG_TEXTURE_VERBOSE_GLES30_ACTIVE 0
+ #define DBG_SHADER_VERBOSE_GLES30_ACTIVE 0
+ #define DBG_GLSL_BINDINGS_GLES30_ACTIVE 0
+#else
+ #define DBG_LOG_GLES30_ACTIVE UNITY_WEBGL
+ #define DBG_TEXTURE_VERBOSE_GLES30_ACTIVE UNITY_WEBGL
+ #define DBG_SHADER_VERBOSE_GLES30_ACTIVE 0
+ #define DBG_GLSL_BINDINGS_GLES30_ACTIVE 0
+#endif
+
+#if DBG_LOG_GLES30_ACTIVE
+ #define DBG_LOG_GLES30(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+
+ inline std::string GetMatrixString(const Matrix4x4f& mtx)
+ {
+ return Format("%.2f, %.2f, %.2f, %2f,\n"
+ "%.2f, %.2f, %.2f, %2f,\n"
+ "%.2f, %.2f, %.2f, %2f,\n"
+ "%.2f, %.2f, %.2f, %2f",
+ mtx[0], mtx[1], mtx[2], mtx[3],
+ mtx[4], mtx[5], mtx[6], mtx[7],
+ mtx[8], mtx[9], mtx[10],mtx[11],
+ mtx[12],mtx[13],mtx[14],mtx[15]);
+ }
+
+ inline const char* GetBoolString(bool type)
+ {
+ return type?"True":"False";
+ }
+
+ inline const char * GetBlendModeString(BlendMode type)
+ {
+ switch(type)
+ {
+ case kBlendZero:return "kBlendZero";
+ case kBlendOne:return "kBlendOne";
+ case kBlendDstColor:return "kBlendDstColor";
+ case kBlendSrcColor:return "kBlendSrcColor";
+ case kBlendOneMinusDstColor:return "kBlendOneMinusDstColor";
+ case kBlendSrcAlpha:return "kBlendSrcAlpha";
+ case kBlendOneMinusSrcColor:return "kBlendOneMinusSrcColor";
+ case kBlendDstAlpha:return "kBlendDstAlpha";
+ case kBlendOneMinusDstAlpha:return "kBlendOneMinusDstAlpha";
+ case kBlendSrcAlphaSaturate:return "kBlendSrcAlphaSaturate";
+ case kBlendOneMinusSrcAlpha:return "kBlendOneMinusSrcAlpha";
+ default:return "GetBlendModeString<Undefined>";
+ }
+ }
+ inline const char * GetCullModeString(CullMode type)
+ {
+ switch(type)
+ {
+ case kCullUnknown:return "kCullUnknown";
+ case kCullOff:return "kCullOff:return";
+ case kCullFront:return "kCullFront";
+ case kCullBack:return "kCullBack";;
+ default:return "GetCullMode<undefined>";
+ }
+ }
+
+ inline const char * GetCompareFunctionString(CompareFunction type)
+ {
+ switch(type)
+ {
+ case kFuncUnknown:return "kFuncUnknown";
+ case kFuncDisabled:return "kFuncDisabled";
+ case kFuncNever:return "kFuncNever";
+ case kFuncLess:return "kFuncLess";
+ case kFuncEqual:return "kFuncEqual";
+ case kFuncLEqual:return "kFuncLEqual";
+ case kFuncGreater:return "kFuncGreater";
+ case kFuncNotEqual:return "kFuncNotEqual";
+ case kFuncGEqual:return "kFuncGEqual";
+ case kFuncAlways:return "kFuncAlways";
+ default:return "GetCompareFunctionString<Undefined>";
+ }
+ }
+
+ inline const char * GetShaderTypeString(ShaderType type)
+ {
+ switch(type)
+ {
+ case kShaderNone:return "kShaderNone";
+ case kShaderVertex:return "kShaderVertex";
+ case kShaderFragment:return "kShaderFragment";
+ default:return "GetShaderTypeString<undefined>";
+ }
+ }
+
+ inline const char * GetShaderImplTypeString(ShaderImplType type)
+ {
+ switch(type)
+ {
+ case kShaderImplUndefined: return "kShaderImplUndefined";
+ case kShaderImplVertex: return "kShaderImplVertex";
+ case kShaderImplFragment: return "kShaderImplFragment";
+ case kShaderImplBoth: return "kShaderImplBoth";
+ default:return "GetShaderImplTypeString<Undefined>";
+ }
+ }
+
+#else
+ #define DBG_LOG_GLES30(...)
+#endif
+
+#if DBG_TEXTURE_VERBOSE_GLES30_ACTIVE
+#define DBG_TEXTURE_VERBOSE_GLES30(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+#else
+#define DBG_TEXTURE_VERBOSE_GLES30(...)
+#endif
+
+#if DBG_SHADER_VERBOSE_GLES30_ACTIVE
+ #define DBG_SHADER_VERBOSE_GLES30(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+ #define DBG_SHADER_VERBOSE_GLES30_DUMP_SHADER(prefix, text) { printf_console("%s\n", prefix);DebugTextLineByLine(text);printf_console("\n---\n");}
+#else
+ #define DBG_SHADER_VERBOSE_GLES30(...)
+ #define DBG_SHADER_VERBOSE_GLES30_DUMP_SHADER(prefix, text)
+#endif
+
+#if DBG_GLSL_BINDINGS_GLES30_ACTIVE
+ #define DBG_GLSL_BINDINGS_GLES30(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+#else
+ #define DBG_GLSL_BINDINGS_GLES30(...)
+#endif
+
+
+#endif
diff --git a/Runtime/GfxDevice/opengles30/FixedFunctionStateGLES30.cpp b/Runtime/GfxDevice/opengles30/FixedFunctionStateGLES30.cpp
new file mode 100644
index 0000000..de052b9
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/FixedFunctionStateGLES30.cpp
@@ -0,0 +1,71 @@
+#include "UnityPrefix.h"
+#include "FixedFunctionStateGLES30.h"
+#include <sstream>
+
+
+FixedFunctionStateGLES30::FixedFunctionStateGLES30 ()
+: texUnitCount(0),
+ lightType(0),
+ texUnitMatrix(0),
+ lightingEnabled(false),
+ specularEnabled(true),
+ onlyDirectionalLights(false),
+ lightCount(0),
+ useUniformInsteadOfVertexColor(false),
+ useVertexColorAsAmbientAndDiffuse(false),
+ useVertexColorAsEmission(false),
+ fogMode(kFogDisabled),
+ addSpecularAfterTexturing(false),
+ alphaTest(kFuncDisabled)
+{
+ for (int q = 0; q < kMaxSupportedTextureUnitsGLES; ++q)
+ {
+ texUnitCube[q] = false;
+ texUnitGen[q] = kTexGenDisabled;
+ texUnitColorCombiner[q] = ~0UL;
+ texUnitAlphaCombiner[q] = ~0UL;
+ }
+}
+
+static std::string CombinerToString (unsigned int combinerDesc)
+{
+ std::ostringstream s;
+ s << combinerDesc;
+ return s.str().c_str();
+}
+
+
+std::string FixedFunctionStateGLES30::ToString () const
+{
+ std::ostringstream s;
+
+ s << "FixedFunctionStateGLES30::ToString():\n";
+ s << " lightingEnabled = " << lightingEnabled << "\n";
+ s << " specularEnabled = " << specularEnabled << "\n";
+ s << " lights = " << lightCount << "\n";
+ for (int i = 0; i < lightCount; ++i)
+ s << " light" << i << " : " << GetLightType(i) << "\n";
+
+ s << " useUniformInsteadOfVertexColor = " << useUniformInsteadOfVertexColor << "\n";
+ s << " useVertexColorAsAmbientAndDiffuse = " << useVertexColorAsAmbientAndDiffuse << "\n";
+ s << " useVertexColorAsEmission = " << useVertexColorAsEmission << "\n";
+
+ s << " fogMode = " << fogMode << "\n";
+
+ for (int i = 0; i < texUnitCount; ++i)
+ {
+ s << " texture " << i << "\n";
+
+ s << " CUBE = " << ((texUnitCube[i])? "true": "false") << "\n";
+ s << " rgb combiner = " << CombinerToString(texUnitColorCombiner[i]) << "\n";
+ s << " alpba combiner = " << CombinerToString(texUnitAlphaCombiner[i]) << "\n";
+ s << " texGen = " << texUnitGen[i] << "\n";
+ s << " need matrix: " << (NeedTexUnitMatrix(i)?"true":"false") << "\n";
+ s << " need perspective divide: " << (IsTexUnitProjected(i)?"true":"false") << "\n";
+ }
+
+ s << " addSpecularafterTexturing = " << addSpecularAfterTexturing << "\n";
+ s << " alphaTest = " << alphaTest << "\n";
+
+ return s.str().c_str();
+}
diff --git a/Runtime/GfxDevice/opengles30/FixedFunctionStateGLES30.h b/Runtime/GfxDevice/opengles30/FixedFunctionStateGLES30.h
new file mode 100644
index 0000000..8de6be8
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/FixedFunctionStateGLES30.h
@@ -0,0 +1,65 @@
+#ifndef FIXEDFUNCTIONSTATE_GLES30_H
+#define FIXEDFUNCTIONSTATE_GLES30_H
+
+#include "IncludesGLES30.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include <string>
+
+// we can use one var to determine both shift and mask, but let help out the compiler ;-)
+#define FFPSTATE_SET_MASK(target, idx, val, shift_mul, mask) \
+do{ \
+ target &= ~(mask << ((idx)*shift_mul)); \
+ target |= (val << ((idx)*shift_mul)); \
+}while(0) \
+
+
+
+class FixedFunctionStateGLES30
+{
+public:
+ FixedFunctionStateGLES30();
+
+ UInt32 texUnitColorCombiner[kMaxSupportedTextureUnitsGLES];
+ UInt32 texUnitAlphaCombiner[kMaxSupportedTextureUnitsGLES];
+ bool texUnitCube[kMaxSupportedTextureUnitsGLES];
+ int texUnitGen[kMaxSupportedTextureUnitsGLES];
+ int texUnitCount;
+
+ // we will use 4bits per light - this way we can handle up to 8 lights (though kMaxEmulatedVertexLights = 4)
+ UInt32 lightType;
+ // we will use 2 bits per tex unit - one for perspective divide, the other one for if we need matrix mul at all
+ // this way we can store 16 texunit info (though kMaxSupportedTextureUnitsGLES = 8)
+ UInt32 texUnitMatrix;
+
+
+ bool lightingEnabled;
+ bool specularEnabled;
+ bool onlyDirectionalLights;
+ int lightCount : 8;
+ FogMode fogMode : 8;
+ CompareFunction alphaTest : 8;
+
+ bool useUniformInsteadOfVertexColor;
+ bool useVertexColorAsAmbientAndDiffuse;
+ bool useVertexColorAsEmission;
+ bool addSpecularAfterTexturing;
+
+ unsigned GetLightType(int i) const { return (lightType >> (i*4)) & 0xF; }
+ void SetLightType(int i, unsigned type) { FFPSTATE_SET_MASK(lightType, i, type, 4, 0xF); }
+
+ bool NeedTexUnitMatrix(int i) const { return ((texUnitMatrix >> (i*2)) & 0x1) != 0; }
+ bool IsTexUnitProjected(int i) const { return ((texUnitMatrix >> (i*2)) & 0x2) != 0; }
+
+ void SetTexUnitMatrixParam(int i, bool hasMatrix, bool isProjected)
+ {
+ int mask = (hasMatrix ? 1 : 0) | (isProjected ? 2 : 0);
+ FFPSTATE_SET_MASK(texUnitMatrix, i, mask, 2, 0x3);
+ }
+
+ std::string ToString () const;
+};
+
+
+#undef FFPSTATE_SET_MASK
+
+#endif /* FIXEDFUNCTIONSTATE_GLES30_H */
diff --git a/Runtime/GfxDevice/opengles30/GfxDeviceGLES30.cpp b/Runtime/GfxDevice/opengles30/GfxDeviceGLES30.cpp
new file mode 100644
index 0000000..240cb58
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/GfxDeviceGLES30.cpp
@@ -0,0 +1,3328 @@
+#include "UnityPrefix.h"
+#if GFX_SUPPORTS_OPENGLES30
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Shaders/MaterialProperties.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Quaternion.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/program.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Threads/AtomicOps.h"
+#include "Runtime/GfxDevice/TransformState.h"
+#include "IncludesGLES30.h"
+#include "AssertGLES30.h"
+#include "ConstantBuffersGLES30.h"
+#include "ContextGLES30.h"
+#include "VBOGLES30.h"
+#include "TexturesGLES30.h"
+#include "CombinerGLES30.h"
+#include "GpuProgramsGLES30.h"
+#include "FixedFunctionStateGLES30.h"
+#include "ShaderGeneratorGLES30.h"
+#include "RenderTextureGLES30.h"
+#include "DebugGLES30.h"
+#include "TimerQueryGLES30.h"
+#include "TextureIdMapGLES30.h"
+#include "UtilsGLES30.h"
+#include "Runtime/GfxDevice/opengles30/TransformFeedbackSkinnedMesh.h"
+#include "DataBuffersGLES30.h"
+#include "Runtime/GfxDevice/opengles/ExtensionsGLES.h"
+
+#if UNITY_IPHONE
+ #include "PlatformDependent/iPhonePlayer/iPhoneSettings.h"
+#elif UNITY_ANDROID
+ #include "PlatformDependent/AndroidPlayer/ContextGLES.h"
+ #include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+ #include "PlatformDependent/AndroidPlayer/AndroidSystemInfo.h"
+#endif
+
+#include "Runtime/GfxDevice/GLDataBufferCommon.h"
+
+#define UNITY_GLES3_ENTRYPOINTS_FROM_GETPROCADDR 0
+
+#if UNITY_ANDROID
+#undef UNITY_GLES3_ENTRYPOINTS_FROM_GETPROCADDR
+#define UNITY_GLES3_ENTRYPOINTS_FROM_GETPROCADDR 1
+#endif
+
+#if UNITY_GLES3_ENTRYPOINTS_FROM_GETPROCADDR
+
+#define STRINGIFY(x) STRINGIFY2(x)
+#define STRINGIFY2(x) #x
+
+#define DO_GLFUNC(retval, name, ...) typedef void (GL_APIENTRYP PFN##name) (__VA_ARGS__); \
+ retval GL_APIENTRY name (__VA_ARGS__) \
+{ \
+ static PFN##name addr = NULL; \
+ if(!addr) \
+ { \
+ /*DBG_LOG_GLES30("Retrieving GLES3 proc address " STRINGIFY(name) );*/ \
+ addr = (PFN##name) GetGLExtProcAddress(STRINGIFY(name)); \
+ if(!addr) \
+ { \
+ Assert("Could not find GLES 3.0 entry point" STRINGIFY(name)); \
+ return (retval)0; \
+ } \
+ DBG_LOG_GLES30("Success\n"); \
+ } \
+ __builtin_return(__builtin_apply((void (*)(...))addr, __builtin_apply_args(), 100)); \
+ /*DBG_LOG_GLES30("Called " STRINGIFY(name) " successfully\n");*/ \
+}
+
+DO_GLFUNC(void, glBindBufferBase, GLenum target, GLuint index, GLuint buffer)
+DO_GLFUNC(void, glBindBufferRange, GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr range)
+DO_GLFUNC(void, glBlitFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
+DO_GLFUNC(void, glDeleteTransformFeedbacks, GLsizei n, const GLuint* ids)
+DO_GLFUNC(void, glDrawBuffers, GLsizei n, const GLenum* bufs)
+DO_GLFUNC(void, glGenQueries, GLsizei n, GLuint* ids)
+DO_GLFUNC(void, glGetActiveUniformBlockiv, GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint* params)
+DO_GLFUNC(void, glGetActiveUniformBlockName, GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName)
+DO_GLFUNC(void, glGetActiveUniformsiv, GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params)
+DO_GLFUNC(void, glGetProgramBinary, GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, GLvoid* binary)
+DO_GLFUNC(void, glGetQueryObjectuiv, GLuint id, GLenum pname, GLuint* params)
+DO_GLFUNC(GLuint, glGetUniformBlockIndex, GLuint program, const GLchar *uniformBlockName)
+DO_GLFUNC(void, glInvalidateFramebuffer, GLenum target, GLsizei numAttachments, const GLenum* attachments)
+DO_GLFUNC(void *, glMapBufferRange, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access)
+DO_GLFUNC(void, glProgramBinary, GLuint program, GLenum binaryFormat, const GLvoid* binary, GLsizei length)
+DO_GLFUNC(void, glReadBuffer, GLenum buf)
+DO_GLFUNC(void, glRenderbufferStorageMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
+DO_GLFUNC(void, glUniformBlockBinding, GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding)
+DO_GLFUNC(GLboolean, glUnmapBuffer, GLenum target)
+DO_GLFUNC(void, glGenTransformFeedbacks, GLsizei n, GLuint *ids)
+DO_GLFUNC(void, glVertexAttribIPointer, GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid* pointer)
+DO_GLFUNC(void, glTransformFeedbackVaryings, GLuint program, GLsizei count, const char* const* varyings, GLenum bufferMode)
+DO_GLFUNC(void, glBeginTransformFeedback, GLenum primitivemode)
+DO_GLFUNC(void, glEndTransformFeedback)
+DO_GLFUNC(void, glFlushMappedBufferRange, GLenum target, GLintptr offset, GLsizeiptr length)
+DO_GLFUNC(void, glGenVertexArrays, GLsizei n, GLuint* arrays)
+DO_GLFUNC(void, glDeleteVertexArrays, GLsizei n, const GLuint* arrays)
+DO_GLFUNC(void, glBindVertexArray, GLuint array)
+
+#undef DO_GLFUNC
+#undef STRINGIFY
+#undef STRINGIFY2
+
+#endif
+static void ClearFixedFunctionPrograms();
+
+// \todo [2013-04-16 pyry] UtilsGLES30 or similar for these?
+
+static int queryInt (UInt32 pname)
+{
+ int value = 0;
+ GLES_CHK(glGetIntegerv(pname, &value));
+ return value;
+}
+
+// let's play safe here:
+// ios/glesemu works just fine
+// and shadows demands more careful eps choice - do it later
+#define WORKAROUND_POLYGON_OFFSET UNITY_ANDROID
+
+// forward declarations
+
+namespace ShaderLab {
+ TexEnv* GetTexEnvForBinding( const TextureBinding& binding, const PropertySheet* props ); // pass.cpp
+}
+
+// local forward declarations
+struct DeviceStateGLES30;
+static void ApplyBackfaceMode( const DeviceStateGLES30& state );
+
+static FramebufferObjectManagerGLES30& GetFBOManager (DeviceStateGLES30& deviceState);
+
+// NOTE: GLES3.0 supports only 4 lights for now
+enum { kMaxSupportedVertexLightsByGLES30 = 4 };
+
+// Constant tables
+static const unsigned int kBlendModeES2[] = {
+ GL_ZERO, GL_ONE, GL_DST_COLOR, GL_SRC_COLOR, GL_ONE_MINUS_DST_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR,
+ GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA_SATURATE, GL_ONE_MINUS_SRC_ALPHA,
+};
+
+static const unsigned int kBlendFuncES2[] = {
+ GL_FUNC_ADD, GL_FUNC_SUBTRACT,
+ GL_FUNC_REVERSE_SUBTRACT, GL_MIN, GL_MAX,
+};
+
+static const unsigned int kCmpFuncES2[] = {
+ GL_NEVER, GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL, GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS
+};
+
+static const unsigned int kStencilOpES2[] = {
+ GL_KEEP, GL_ZERO, GL_REPLACE, GL_INCR,
+ GL_DECR, GL_INVERT, GL_INCR_WRAP, GL_DECR_WRAP
+};
+
+static const GLenum kWrapModeES2[kTexWrapCount] = {
+ GL_REPEAT,
+ GL_CLAMP_TO_EDGE,
+};
+
+static const GLenum kGLES30TextureDimensionTable[kTexDimCount] = {0, -1/*GL_TEXTURE_1D*/, GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP, 0};
+
+static const GLint kMinFilterES2[kTexFilterCount] = { GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR };
+
+// --------------------------------------------------------------------------
+
+struct DeviceDepthStateGLES30 : public DeviceDepthState
+{
+ UInt32 depthFunc;
+};
+
+struct DeviceStencilStateGLES30 : public DeviceStencilState
+{
+ GLenum stencilFuncFront;
+ GLenum stencilFailOpFront;
+ GLenum depthFailOpFront;
+ GLenum depthPassOpFront;
+ GLenum stencilFuncBack;
+ GLenum stencilFailOpBack;
+ GLenum depthFailOpBack;
+ GLenum depthPassOpBack;
+};
+
+
+typedef std::map< GfxBlendState, DeviceBlendState, memcmp_less<GfxBlendState> > CachedBlendStates;
+typedef std::map< GfxDepthState, DeviceDepthStateGLES30, memcmp_less<GfxDepthState> > CachedDepthStates;
+typedef std::map< GfxStencilState, DeviceStencilStateGLES30, memcmp_less<GfxStencilState> > CachedStencilStates;
+typedef std::map< GfxRasterState, DeviceRasterState, memcmp_less<GfxRasterState> > CachedRasterStates;
+
+// --------------------------------------------------------------------------
+struct TextureUnitStateGLES3
+{
+ GLuint texID;
+ TextureDimension texDim;
+ unsigned int combColor, combAlpha;
+ Vector4f color;
+ TexGenMode texGen;
+ Matrix4x4f textureMatrix;
+ float bias;
+
+ // right let waste space here instead of device-level int
+ bool identityMatrix;
+ bool isProjected;
+ bool posForTexGen;
+ bool nrmForTexGen;
+
+ void Invalidate();
+
+ static bool PositionRequiredForTexGen(TexGenMode mode)
+ {
+ return (mode == kTexGenObject || mode == kTexGenEyeLinear);
+ }
+
+ static bool NormalRequiredForTexGen(TexGenMode mode)
+ {
+ return (mode == kTexGenSphereMap || mode == kTexGenCubeReflect || mode == kTexGenCubeNormal);
+ }
+
+ void SetTexGen( TexGenMode mode )
+ {
+ posForTexGen = PositionRequiredForTexGen(mode);
+ nrmForTexGen = NormalRequiredForTexGen(mode);
+
+ texGen = mode;
+ }
+};
+
+void TextureUnitStateGLES3::Invalidate()
+{
+ texID = -1;
+ texDim = kTexDimNone;
+ combColor = combAlpha = 0xFFFFFFFF;
+ color.Set( -1, -1, -1, -1 );
+ texGen = kTexGenUnknown;
+ posForTexGen = 0;
+ nrmForTexGen = 0;
+ textureMatrix.SetIdentity();
+ identityMatrix = true;
+ isProjected = false;
+ bias = 1.0e6f;
+}
+
+
+// --------------------------------------------------------------------------
+// TODO: optimize this. Right now we just send off whole 8 float3 UVs with each
+// immediate mode vertex. We could at least detect the number of them used from
+// ImmediateTexCoord calls.
+struct ImmediateVertexGLES30 {
+ Vector3f vertex;
+ Vector3f normal;
+ UInt32 color;
+ Vector3f texCoords[8];
+};
+
+struct ImmediateModeGLES30 {
+ std::vector<ImmediateVertexGLES30> m_Vertices;
+ ImmediateVertexGLES30 m_Current;
+ GfxPrimitiveType m_Mode;
+
+ DataBufferGLES30* m_IndexBufferQuads;
+
+ ImmediateModeGLES30();
+ ~ImmediateModeGLES30();
+ void Invalidate();
+};
+
+// --------------------------------------------------------------------------
+struct DeviceStateGLES30
+{
+ int m_TextureIDGenerator;
+
+ int depthFunc;
+ int depthWrite; // 0/1 or -1
+
+ int blending;
+ int srcBlend, destBlend, srcBlendAlpha, destBlendAlpha; // Blend modes
+ int blendOp, blendOpAlpha;
+ CompareFunction alphaTest;
+ float alphaValue;
+
+ CullMode culling;
+ bool appBackfaceMode, userBackfaceMode;
+ NormalizationMode
+ normalization;
+ int scissor;
+
+ bool lighting;
+ bool separateSpecular;
+ SimpleVec4 matDiffuse, matAmbient, matSpecular, matEmissive;
+ SimpleVec4 ambient;
+ float matShininess;
+ ColorMaterialMode
+ colorMaterial;
+
+ float offsetFactor, offsetUnits;
+
+ int colorWriteMask; // ColorWriteMask combinations
+ SimpleVec4 color;
+
+ TextureUnitStateGLES3
+ textures[kMaxSupportedTextureUnitsGLES];
+ int textureCount;
+ int activeTextureUnit;
+
+ // pure optimization: texGen is very special case and is used sparingly
+ UInt32 positionTexGen;
+ UInt32 normalTexGen;
+
+ int vertexLightCount;
+ LightType vertexLightTypes[kMaxSupportedVertexLights];
+
+ TransformState transformState;
+
+ ConstantBuffersGLES30
+ m_CBs;
+
+ DynamicVBO* m_DynamicVBO;
+ bool vboContainsColor;
+
+ int viewport[4];
+ int scissorRect[4];
+
+
+ GpuProgram* activeProgram;
+ const GpuProgramParameters* activeProgramParams;
+ dynamic_array<UInt8> activeProgramParamsBuffer;
+ UInt32 activeProgramID;
+
+
+ CachedBlendStates m_CachedBlendStates;
+ CachedDepthStates m_CachedDepthStates;
+ CachedStencilStates m_CachedStencilStates;
+ CachedRasterStates m_CachedRasterStates;
+
+ const DeviceBlendState* m_CurrBlendState;
+ const DeviceDepthStateGLES30* m_CurrDepthState;
+ const DeviceStencilStateGLES30* m_CurrStencilState;
+ int m_StencilRef;
+ const DeviceRasterState* m_CurrRasterState;
+
+ MaterialPropertyBlock m_MaterialProperties;
+ ImmediateModeGLES30 m_Imm;
+
+ // Framebuffer objects.
+ FramebufferObjectManagerGLES30* m_fboManager;
+ FramebufferObjectGLES30* m_activeFbo; //!< Currently bound FBO.
+ FramebufferObjectGLES30* m_defaultFbo; //!< Default render target FBO or null if using default framebuffer (0).
+
+public:
+ DeviceStateGLES30();
+ void Invalidate();
+ void ComputeFixedFunctionState(FixedFunctionStateGLES30& state, const GfxFogParams& fog) const;
+
+ inline void ApplyTexGen( UInt32 unit );
+ inline void DropTexGen( UInt32 unit );
+};
+
+DeviceStateGLES30::DeviceStateGLES30()
+: m_DynamicVBO(0)
+, m_fboManager(0)
+, m_activeFbo (0)
+, m_defaultFbo(0)
+{
+ m_TextureIDGenerator = 0;
+}
+
+void DeviceStateGLES30::Invalidate()
+{
+ DBG_LOG_GLES30("Invalidate");
+ int i;
+
+ depthFunc = -1; //unknown
+ depthWrite = -1;
+
+ blending = -1; // unknown
+ srcBlend = destBlend = -1; // won't match any GL mode
+ blendOp = blendOpAlpha = -1;
+ alphaTest = kFuncUnknown;
+ alphaValue = -1.0f;
+
+ culling = kCullUnknown;
+ normalization = kNormalizationUnknown;
+ scissor = -1;
+
+ lighting = false;
+ separateSpecular = false;
+
+ matDiffuse.set( -1, -1, -1, -1 );
+ matAmbient.set( -1, -1, -1, -1 );
+ matSpecular.set( -1, -1, -1, -1 );
+ matEmissive.set( -1, -1, -1, -1 );
+ ambient.set( -1, -1, -1, -1 );
+ matShininess = -1.0f;
+ colorMaterial = kColorMatUnknown;
+
+ offsetFactor = offsetUnits = -1000.0f;
+
+ colorWriteMask = -1; // TBD ?
+ m_StencilRef = -1;
+
+ color.set( -1, -1, -1, -1 );
+
+ activeTextureUnit = -1;
+ for( i = 0; i < kMaxSupportedTextureUnitsGLES; ++i )
+ textures[i].Invalidate();
+ textureCount = 0;
+
+ positionTexGen = 0;
+ normalTexGen = 0;
+
+ vertexLightCount = 0;
+ for ( i = 0; i < kMaxSupportedVertexLights; ++i)
+ vertexLightTypes[i] = kLightDirectional;
+
+ // make sure backface mode is in sync
+ appBackfaceMode = false;
+ userBackfaceMode = false;
+ ApplyBackfaceMode( *this );
+
+ vboContainsColor = true;
+
+ viewport[0] = 0;
+ viewport[1] = 0;
+ viewport[2] = 0;
+ viewport[3] = 0;
+
+ scissorRect[0] = 0;
+ scissorRect[1] = 0;
+ scissorRect[2] = 0;
+ scissorRect[3] = 0;
+
+ activeProgram = 0;
+ activeProgramParams = 0;
+ activeProgramParamsBuffer.resize_uninitialized(0);
+ activeProgramID = -1;
+
+ m_Imm.Invalidate();
+
+ m_activeFbo = 0;
+
+ InvalidateVertexInputCacheGLES30();
+
+ GLESAssert();
+}
+
+void DeviceStateGLES30::ComputeFixedFunctionState(FixedFunctionStateGLES30& state, const GfxFogParams& fog) const
+{
+ if (lighting)
+ {
+ int numLights = vertexLightCount;
+ bool onlyDir = true;
+ for(int i = 0 ; i < numLights ; ++i)
+ {
+ onlyDir = onlyDir && (vertexLightTypes[i] == kLightDirectional);
+ state.SetLightType(i,vertexLightTypes[i]);
+ }
+
+ state.lightingEnabled = true;
+ state.lightCount = numLights;
+ state.onlyDirectionalLights = onlyDir;
+ state.specularEnabled = separateSpecular;
+
+ switch (colorMaterial)
+ {
+ case kColorMatDisabled:
+ break;
+
+ case kColorMatAmbientAndDiffuse:
+ state.useVertexColorAsAmbientAndDiffuse = true;
+ break;
+
+ case kColorMatEmission:
+ state.useVertexColorAsEmission = true;
+ break;
+
+ default:
+ ErrorString("Unsupported color material mode");
+ break;
+ }
+ }
+ else
+ {
+ state.lightingEnabled = false;
+ state.lightCount = 0;
+ }
+
+ state.useUniformInsteadOfVertexColor = !vboContainsColor;
+ state.texUnitCount = textureCount;
+
+ for (int i = 0; i < textureCount; i++)
+ {
+ Assert(textures[i].texDim != kTexDimUnknown);
+ Assert(textures[i].texDim != kTexDimNone);
+ Assert(textures[i].texDim != kTexDimAny);
+ Assert(textures[i].texDim != kTexDim3D); // OpenGLES3.0 does NOT supports 3D textures
+ state.texUnitCube[i] = (textures[i].texDim == kTexDimCUBE);
+ state.texUnitColorCombiner[i] = textures[i].combColor,
+ state.texUnitAlphaCombiner[i] = textures[i].combAlpha;
+ state.texUnitGen[i] = textures[i].texGen;
+
+ bool needMatrix = !textures[i].identityMatrix || textures[i].texGen > kTexGenDisabled;
+ state.SetTexUnitMatrixParam(i, needMatrix, textures[i].isProjected);
+ }
+
+ state.fogMode = fog.mode;
+ switch (fog.mode)
+ {
+ case kFogUnknown:
+ case kFogDisabled:
+ state.fogMode = kFogDisabled;
+ default:
+ break;
+ }
+
+ if(gGraphicsCaps.gles30.hasAlphaTestQCOM)
+ {
+ // we dont want to generate special shader if we have alpha-test done gl style
+ state.alphaTest = kFuncDisabled;
+ }
+ else
+ {
+ state.alphaTest = alphaTest;
+ switch (alphaTest)
+ {
+ case kFuncNever:
+ /* \todo Disable drawing. */
+ break;
+ case kFuncUnknown:
+ case kFuncDisabled:
+ case kFuncAlways:
+ state.alphaTest = kFuncDisabled;
+ default:
+ break;
+ }
+ }
+}
+
+inline void DeviceStateGLES30::ApplyTexGen( UInt32 unit )
+{
+ const TextureUnitStateGLES3& state = textures[unit];
+
+ positionTexGen = state.posForTexGen ? positionTexGen | (1<<unit)
+ : positionTexGen & ~(1<<unit);
+
+ normalTexGen = state.nrmForTexGen ? normalTexGen | (1<<unit)
+ : normalTexGen & ~(1<<unit);
+}
+
+inline void DeviceStateGLES30::DropTexGen( UInt32 unit )
+{
+ positionTexGen &= ~(1<<unit);
+ normalTexGen &= ~(1<<unit);
+}
+
+
+#include "GfxDeviceGLES30.h"
+#include "Runtime/GfxDevice/GLESCommon.h"
+
+void GfxDeviceGLES30_MarkWorldViewProjDirty()
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ GetGLES30DeviceState(device).transformState.dirtyFlags |= TransformState::kWorldViewProjDirty;
+}
+
+void GfxDeviceGLES30_DisableDepthTest()
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ GetGLES30DeviceState(device).depthFunc = GL_NEVER;
+ GLES_CHK(glDisable(GL_DEPTH_TEST));
+}
+
+void GraphicsCaps::InitGLES30()
+{
+ // \todo [2013-04-16 pyry] Requires some serious cleanaup:
+ // - This functions shouldn't be member of GraphicsCaps (why it is?)
+ // - query extensions once, split to set and check from there
+ // - nicer wrapper for int caps queries
+
+ GLES_InitCommonCaps(this);
+ gGles3ExtFunc.InitExtFunc();
+
+ shaderCaps = kShaderLevel3;
+
+ maxLights = kMaxSupportedVertexLightsByGLES30; // vertex light count
+ hasAnisoFilter = QueryExtension("GL_EXT_texture_filter_anisotropic"); // has anisotropic filtering?
+ if (hasAnisoFilter)
+ GLES_CHK(glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, (GLint *)&maxAnisoLevel));
+ else
+ maxAnisoLevel = 1;
+
+ maxTexImageUnits = 8;
+ GLES_CHK(glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTexImageUnits));
+ maxTexImageUnits = std::max<int>(std::min<int>( maxTexImageUnits, kMaxSupportedTextureUnitsGLES ), 1);
+
+ maxTexUnits = maxTexImageUnits;
+ maxTexCoords = maxTexImageUnits;
+ maxMRTs = std::min<int>(FramebufferAttachmentsGLES30::kMaxColorAttachments, queryInt(GL_MAX_COLOR_ATTACHMENTS));
+
+ GLES_CHK(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize));
+ GLES_CHK(glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &maxCubeMapSize));
+ GLES_CHK(glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxRenderTextureSize));
+
+ hasMipLevelBias = true;
+ hasMipMaxLevel = true;
+
+ hasMultiSampleAutoResolve = false; // ES3 requires explicit glBlitFramebuffer() to do resolve. This affects how RenderTexture behaves.
+ hasMultiSample = true;
+
+ hasBlendSquare = true;
+ hasSeparateAlphaBlend = true;
+ hasBlendSub = true;
+ hasBlendMinMax = true;
+
+ hasS3TCCompression = false;
+
+ hasAutoMipMapGeneration = false; // \todo [2013-04-16 pyry] glGenMipmap() does exist
+
+ has3DTexture = false; // \todo [2013-04-16 pyry] Expose 3D textures
+
+ npot = kNPOTFull;
+ npotRT = npot;
+
+ hasRenderToTexture = true;
+ hasShadowCollectorPass = false;
+
+ hasHighPrecisionTextureCombiners = false; // \todo [2013-04-16 pyry] Yep. We can use highp (fp32) in FS. Should be enough.
+
+ hasRenderToCubemap = true;
+
+ // \note supportsTextureFormat[N < kTexFormatPCCount] = true
+
+ supportsTextureFormat[kTexFormatBGRA32] = QueryExtension("GL_APPLE_texture_format_BGRA8888");
+ supportsTextureFormat[kTexFormatDXT1] = QueryExtension("GL_EXT_texture_compression_s3tc") || QueryExtension("GL_EXT_texture_compression_dxt1");
+ supportsTextureFormat[kTexFormatDXT3] = QueryExtension("GL_EXT_texture_compression_s3tc") || QueryExtension("GL_CHROMIUM_texture_compression_dxt3");
+ supportsTextureFormat[kTexFormatDXT5] = QueryExtension("GL_EXT_texture_compression_s3tc") || QueryExtension("GL_CHROMIUM_texture_compression_dxt5");
+ supportsTextureFormat[kTexFormatPVRTC_RGB2] = QueryExtension("GL_IMG_texture_compression_pvrtc");
+ supportsTextureFormat[kTexFormatPVRTC_RGBA2] = QueryExtension("GL_IMG_texture_compression_pvrtc");
+ supportsTextureFormat[kTexFormatPVRTC_RGB4] = QueryExtension("GL_IMG_texture_compression_pvrtc");
+ supportsTextureFormat[kTexFormatPVRTC_RGBA4] = QueryExtension("GL_IMG_texture_compression_pvrtc");
+ supportsTextureFormat[kTexFormatATC_RGB4] = QueryExtension("GL_AMD_compressed_ATC_texture") || QueryExtension("GL_ATI_texture_compression_atitc");
+ supportsTextureFormat[kTexFormatATC_RGBA8] = QueryExtension("GL_AMD_compressed_ATC_texture") || QueryExtension("GL_ATI_texture_compression_atitc");
+
+ supportsTextureFormat[kTexFormatETC_RGB4] = true; // \note Mapped to ETC2.
+ supportsTextureFormat[kTexFormatEAC_R] = true;
+ supportsTextureFormat[kTexFormatEAC_R_SIGNED] = true;
+ supportsTextureFormat[kTexFormatEAC_RG] = true;
+ supportsTextureFormat[kTexFormatEAC_RG_SIGNED] = true;
+ supportsTextureFormat[kTexFormatETC2_RGB] = true;
+ supportsTextureFormat[kTexFormatETC2_RGBA1] = true;
+ supportsTextureFormat[kTexFormatETC2_RGBA8] = true;
+
+ const bool supportsASTC = QueryExtension("GL_KHR_texture_compression_astc_ldr");
+ for(int loop = kTexFormatASTC_RGB_4x4; loop <= kTexFormatASTC_RGBA_12x12; loop++)
+ supportsTextureFormat[loop] = supportsASTC;
+
+ // \todo [2013-04-16 pyry] Support means nothing until we can expose these to shaders as well.
+ // It requires changes to shader translator.
+ supportsRenderTextureFormat[kRTFormatARGB32] = true;
+ supportsRenderTextureFormat[kRTFormatDepth] = true;
+ supportsRenderTextureFormat[kRTFormatShadowMap] = true;
+ supportsRenderTextureFormat[kRTFormatRGB565] = true;
+ supportsRenderTextureFormat[kRTFormatARGB4444] = true;
+ supportsRenderTextureFormat[kRTFormatARGB1555] = true;
+ supportsRenderTextureFormat[kRTFormatDefault] = true;
+ supportsRenderTextureFormat[kRTFormatA2R10G10B10] = true;
+ supportsRenderTextureFormat[kRTFormatARGB64] = false;
+ supportsRenderTextureFormat[kRTFormatR8] = true;
+ supportsRenderTextureFormat[kRTFormatARGBInt] = true;
+ supportsRenderTextureFormat[kRTFormatRGInt] = true;
+ supportsRenderTextureFormat[kRTFormatRInt] = true;
+
+ if (QueryExtension("GL_EXT_color_buffer_float"))
+ {
+ // Support for fp render targets was stripped from spec as last minute decision. Sigh..
+ supportsRenderTextureFormat[kRTFormatARGBHalf] = true;
+ supportsRenderTextureFormat[kRTFormatRGHalf] = true;
+ supportsRenderTextureFormat[kRTFormatRHalf] = true;
+
+ supportsRenderTextureFormat[kRTFormatARGBFloat] = true;
+ supportsRenderTextureFormat[kRTFormatRGFloat] = true;
+ supportsRenderTextureFormat[kRTFormatRFloat] = true;
+
+ supportsRenderTextureFormat[kRTFormatDefaultHDR] = true; // ES3 backend uses R11F_G11F_B10F as default HDR format
+ }
+
+ const bool isPvrGpu = (rendererString.find("PowerVR") != string::npos);
+ const bool isMaliGpu = (rendererString.find("Mali") != string::npos);
+ const bool isAdrenoGpu = (rendererString.find("Adreno") != string::npos);
+ const bool isTegraGpu = (rendererString.find("Tegra") != string::npos);
+
+ hasNativeDepthTexture = true;
+ hasNativeShadowMap = true;
+
+ hasRenderTargetStencil = true;
+ hasTwoSidedStencil = true;
+ hasStencilInDepthTexture = true;
+ hasStencil = true;
+
+ has16BitFloatVertex = true;
+ needsToSwizzleVertexColors = false;
+
+ hasSRGBReadWrite = false; // \todo [2013-06-05 pyry] Doesn't function properly
+
+ disableSoftShadows = false;
+ buggyCameraRenderToCubemap = false;
+
+ hasShadowCollectorPass = false;
+
+ // Timer queries
+ gGraphicsCaps.hasTimerQuery = QueryExtension("GL_NV_timer_query");
+
+ // GLES3-specific variables.
+ gles30.maxAttributes = std::min<int>(kGLES3MaxVertexAttribs, queryInt(GL_MAX_VERTEX_ATTRIBS));
+ gles30.maxVaryings = queryInt(GL_MAX_VARYING_VECTORS);
+ gles30.maxSamples = queryInt(GL_MAX_SAMPLES);
+
+ hasTiledGPU = isPvrGpu || isAdrenoGpu || isMaliGpu;
+
+ const bool isAdreno330 = (rendererString.find("Adreno (TM) 330") != string::npos); // MSM8974 dev device, with rather unstable drivers.
+ const bool isMali628 = (rendererString.find("Mali-T628") != string::npos); // ARM TG4 dev device, mapbuffer broken.
+ const bool isGLESEmu = (rendererString.find("Mali OpenGL ES Emulator") != string::npos); // ARM OpenGL ES 3.0 emulator, glGetProgramBinary broken
+
+ gles30.useMapBuffer = true;
+ if(isAdrenoGpu || !isAdreno330)
+ gles30.useMapBuffer = false; // Buffer mapping is dead-slow on Adreno, but using BufferData() is broken on Adreno 330
+
+ if(isMali628)
+ gles30.useMapBuffer = false; // Mapbuffer broken on TG4
+
+ gles30.useProgramBinary = true; // Set true first, check actual support in Init call below.
+ gles30.useProgramBinary = GlslGpuProgramGLES30::InitBinaryShadersSupport();
+
+
+ gles30.useTFSkinning = true; // TF skinning used to be broken on adreno, worked around now.
+
+
+ // \todo [2013-04-17 pyry] Extension queries.
+
+ // \todo [2013-04-16 pyry] Figure out which gles20 flags make sense for ES3 as well.
+
+ // \todo [2013-04-16 pyry] Why init timer queries here?
+#if ENABLE_PROFILER
+ g_TimerQueriesGLES30.Init();
+#endif
+}
+
+GfxDevice* CreateGLES30GfxDevice()
+{
+#if UNITY_WIN || UNITY_LINUX || UNITY_BB10 || UNITY_ANDROID
+ InitializeGLES30();
+#endif
+
+ gGraphicsCaps.InitGLES30();
+
+#if UNITY_EDITOR
+ return NULL;
+#else
+ return UNITY_NEW_AS_ROOT(GFX_GL_IMPL(), kMemGfxDevice, "GLES30GfxDevice","");
+#endif
+}
+
+GFX_GL_IMPL::GFX_GL_IMPL()
+{
+ printf_console("Creating OpenGL ES 3.0 graphics device\n");
+ #if !GFX_DEVICE_VIRTUAL
+ impl = new GfxDeviceImpl();
+ #endif
+
+ OnCreate();
+ InvalidateState();
+ m_Renderer = kGfxRendererOpenGLES30;
+ m_IsThreadable = true;
+
+ m_UsesOpenGLTextureCoords = true;
+ m_UsesHalfTexelOffset = false;
+
+ STATE.m_CurrBlendState = NULL;
+ STATE.m_CurrDepthState = NULL;
+ STATE.m_CurrStencilState = NULL;
+ STATE.m_CurrRasterState = NULL;
+
+ extern RenderSurfaceBase* CreateBackBufferColorSurfaceGLES3();
+ SetBackBufferColorSurface(CreateBackBufferColorSurfaceGLES3());
+
+ extern RenderSurfaceBase* CreateBackBufferDepthSurfaceGLES3();
+ SetBackBufferDepthSurface(CreateBackBufferDepthSurfaceGLES3());
+}
+
+GFX_GL_IMPL::~GFX_GL_IMPL()
+{
+ TransformFeedbackSkinningInfo::CleanupTransformFeedbackShaders();
+ ClearFixedFunctionPrograms();
+ STATE.m_CBs.Clear();
+ delete STATE.m_DynamicVBO;
+ delete STATE.m_fboManager;
+
+ #if !GFX_DEVICE_VIRTUAL
+ delete impl;
+ #endif
+#if UNITY_WIN || UNITY_ANDROID
+ ShutdownGLES30();
+#endif
+}
+
+static void ActivateTextureUnitGLES3 (DeviceStateGLES30& state, int unit)
+{
+ if (state.activeTextureUnit == unit)
+ return;
+ GLES_CHK(glActiveTexture(GL_TEXTURE0 + unit));
+ state.activeTextureUnit = unit;
+}
+
+void GFX_GL_IMPL::InvalidateState()
+{
+ DBG_LOG_GLES30("InvalidateState");
+ m_FogParams.Invalidate();
+ STATE.transformState.Invalidate(m_BuiltinParamValues);
+ STATE.Invalidate();
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatEmission, Vector4f(1,1,1,1));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatAmbient, Vector4f(1,1,1,1));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatDiffuse, Vector4f(1,1,1,1));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatSpecular, Vector4f(1,1,1,1));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatShininess, Vector4f(1,1,1,1));
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFFogColor, Vector4f(0,0,0,0));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFFogParams, Vector4f(0,0,0,0));
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFAlphaTestRef, Vector4f(0,0,0,0));
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFColor, Vector4f(1,1,1,1));
+ for (int i = 0; i < kMaxSupportedTextureUnitsGLES; i++)
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecFFTextureEnvColor0+i), Vector4f(0,0,0,0));
+}
+
+DeviceBlendState* GFX_GL_IMPL::CreateBlendState(const GfxBlendState& state)
+{
+ std::pair<CachedBlendStates::iterator, bool> result = STATE.m_CachedBlendStates.insert(std::make_pair(state, DeviceBlendState()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceBlendState& glstate = result.first->second;
+ memcpy(&glstate.sourceState, &state, sizeof(glstate.sourceState));
+ DebugAssertIf(kFuncUnknown==state.alphaTest);
+
+ return &result.first->second;
+}
+
+DeviceDepthState* GFX_GL_IMPL::CreateDepthState(const GfxDepthState& state)
+{
+ std::pair<CachedDepthStates::iterator, bool> result = STATE.m_CachedDepthStates.insert(std::make_pair(state, DeviceDepthStateGLES30()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceDepthStateGLES30& glstate = result.first->second;
+ memcpy(&glstate.sourceState, &state, sizeof(glstate.sourceState));
+ glstate.depthFunc = kCmpFuncES2[state.depthFunc];
+ return &result.first->second;
+}
+
+DeviceStencilState* GFX_GL_IMPL::CreateStencilState(const GfxStencilState& state)
+{
+ std::pair<CachedStencilStates::iterator, bool> result = STATE.m_CachedStencilStates.insert(std::make_pair(state, DeviceStencilStateGLES30()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceStencilStateGLES30& st = result.first->second;
+ memcpy (&st.sourceState, &state, sizeof(st.sourceState));
+ st.stencilFuncFront = kCmpFuncES2[state.stencilFuncFront];
+ st.stencilFailOpFront = kStencilOpES2[state.stencilFailOpFront];
+ st.depthFailOpFront = kStencilOpES2[state.stencilZFailOpFront];
+ st.depthPassOpFront = kStencilOpES2[state.stencilPassOpFront];
+ st.stencilFuncBack = kCmpFuncES2[state.stencilFuncBack];
+ st.stencilFailOpBack = kStencilOpES2[state.stencilFailOpBack];
+ st.depthFailOpBack = kStencilOpES2[state.stencilZFailOpBack];
+ st.depthPassOpBack = kStencilOpES2[state.stencilPassOpBack];
+ return &result.first->second;
+}
+
+DeviceRasterState* GFX_GL_IMPL::CreateRasterState(const GfxRasterState& state)
+{
+ std::pair<CachedRasterStates::iterator, bool> result = STATE.m_CachedRasterStates.insert(std::make_pair(state, DeviceRasterState()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceRasterState& glstate = result.first->second;
+ memcpy(&glstate.sourceState, &state, sizeof(glstate.sourceState));
+
+ return &result.first->second;
+}
+
+void GFX_GL_IMPL::SetBlendState(const DeviceBlendState* state, float alphaRef)
+{
+ DeviceBlendState* devstate = (DeviceBlendState*)state;
+
+ if (STATE.m_CurrBlendState == devstate && alphaRef == STATE.alphaValue)
+ return;
+
+ STATE.m_CurrBlendState = devstate;
+ if (!STATE.m_CurrBlendState)
+ return;
+
+ const GfxBlendState& desc = state->sourceState;
+ const GLenum glsrc = kBlendModeES2[desc.srcBlend];
+ const GLenum gldst = kBlendModeES2[desc.dstBlend];
+ const GLenum glsrca = kBlendModeES2[desc.srcBlendAlpha];
+ const GLenum gldsta = kBlendModeES2[desc.dstBlendAlpha];
+ const GLenum glfunc = kBlendFuncES2[desc.blendOp];
+ const GLenum glfunca = kBlendFuncES2[desc.blendOpAlpha];
+ const bool blendDisabled = (glsrc == GL_ONE && gldst == GL_ZERO && glsrca == GL_ONE && gldsta == GL_ZERO);
+
+ int mask = devstate->sourceState.renderTargetWriteMask;
+ if (STATE.m_activeFbo && STATE.m_activeFbo->GetNumColorAttachments() == 0)
+ mask = 0;
+
+ if( mask != STATE.colorWriteMask )
+ {
+ GLES_CHK(glColorMask( (mask & kColorWriteR) != 0, (mask & kColorWriteG) != 0, (mask & kColorWriteB) != 0, (mask & kColorWriteA) != 0 ));
+ STATE.colorWriteMask = mask;
+ }
+
+ if( blendDisabled )
+ {
+ if( STATE.blending != 0 )
+ {
+ GLES_CHK(glDisable (GL_BLEND));
+ STATE.blending = 0;
+ }
+ }
+ else
+ {
+ if( glsrc != STATE.srcBlend || gldst != STATE.destBlend || glsrca != STATE.srcBlendAlpha || gldsta != STATE.destBlendAlpha )
+ {
+ GLES_CHK(glBlendFuncSeparate(glsrc, gldst, glsrca, gldsta));
+ STATE.srcBlend = glsrc;
+ STATE.destBlend = gldst;
+ STATE.srcBlendAlpha = glsrca;
+ STATE.destBlendAlpha = gldsta;
+ }
+ if (glfunc != STATE.blendOp || glfunca != STATE.blendOpAlpha)
+ {
+ GLES_CHK(glBlendEquationSeparate(glfunc, glfunca));
+ STATE.blendOp = glfunc;
+ STATE.blendOpAlpha = glfunca;
+ }
+ if( STATE.blending != 1 )
+ {
+ GLES_CHK(glEnable( GL_BLEND ));
+ STATE.blending = 1;
+ }
+ }
+ // fragment shader is expected to implement per fragment culling
+ CompareFunction alphaTest = devstate->sourceState.alphaTest;
+
+ // \todo [2013-04-16 pyry] Alpha testing should be moved to shaders
+
+ if (gGraphicsCaps.gles30.hasAlphaTestQCOM && (alphaTest != STATE.alphaTest || alphaRef != STATE.alphaValue))
+ {
+ if (alphaTest != kFuncDisabled)
+ {
+ GLES_CHK(gGles3ExtFunc.glAlphaFuncQCOM(kCmpFuncES2[alphaTest], alphaRef));
+ GLES_CHK(glEnable(GL_ALPHA_TEST_QCOM));
+ }
+ else
+ {
+ GLES_CHK(glDisable(GL_ALPHA_TEST_QCOM));
+ }
+ }
+
+ STATE.alphaTest = alphaTest;
+ STATE.alphaValue = alphaRef;
+}
+
+void GFX_GL_IMPL::SetRasterState(const DeviceRasterState* state)
+{
+ DeviceRasterState* devstate = (DeviceRasterState*)state;
+ if(!devstate)
+ {
+ STATE.m_CurrRasterState = NULL;
+ return;
+ }
+
+ STATE.m_CurrRasterState = devstate;
+
+ CullMode cull = devstate->sourceState.cullMode;
+ if( cull != STATE.culling )
+ {
+ switch (cull) {
+ case kCullOff:
+ GLES_CHK(glDisable (GL_CULL_FACE));
+ break;
+ case kCullFront:
+ GLES_CHK(glCullFace (GL_FRONT));
+ GLES_CHK(glEnable (GL_CULL_FACE));
+ break;
+ case kCullBack:
+ GLES_CHK(glCullFace (GL_BACK));
+ GLES_CHK(glEnable (GL_CULL_FACE));
+ break;
+ default:
+ break;
+ }
+ STATE.culling = cull;
+ }
+
+ float zFactor = devstate->sourceState.slopeScaledDepthBias;
+ float zUnits = devstate->sourceState.depthBias;
+ if( zFactor != STATE.offsetFactor || zUnits != STATE.offsetUnits )
+ {
+
+#if WORKAROUND_POLYGON_OFFSET
+ // on some androids polygon offset work the other way (positive push to viewer)
+ // so we tweak projection matrix to do offset
+ // also use available depth precision better (on Adreno for example fix bugs with z-fighting)
+ // Game Programming Gems Vol1
+ // Eric Lengyel's "Tweaking a Vertex's Projected Depth Value"
+ // we use simplified formula: just smallest possible eps directly (multiplied by zUnits)
+ // we calculate eps for 16bit depth (good enough for larger depth)
+ // in projection matrix [2,2] = (f+n)/(n-f)
+ // so eps would be BitsMult * -1/proj[2,2]
+
+ static const float _BitsMult = -1.0f / (float)0xFFFF; // FFFF = 2^16-1, minus sign incorporated here
+
+ float* matrixElem = &m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatProj).Get(2,2);
+
+ const float eps = _BitsMult / *matrixElem;
+ *matrixElem *= (1.0f + zUnits*eps);
+#else
+ GLES_CHK(glPolygonOffset( zFactor, zUnits ));
+ if( zFactor || zUnits )
+ GLES_CHK(glEnable (GL_POLYGON_OFFSET_FILL));
+ else
+ GLES_CHK(glDisable (GL_POLYGON_OFFSET_FILL));
+#endif
+ STATE.offsetFactor = zFactor;
+ STATE.offsetUnits = zUnits;
+ }
+}
+
+
+void GFX_GL_IMPL::SetDepthState(const DeviceDepthState* state)
+{
+ DeviceDepthStateGLES30* devstate = (DeviceDepthStateGLES30*)state;
+ if (STATE.m_CurrDepthState == devstate)
+ return;
+
+ STATE.m_CurrDepthState = devstate;
+
+ if (!STATE.m_CurrDepthState)
+ return;
+
+ const int depthFunc = devstate->depthFunc;
+ if( depthFunc != STATE.depthFunc )
+ {
+ if( depthFunc != GL_NEVER ) {
+ GLES_CHK(glDepthFunc (depthFunc));
+ GLES_CHK(glEnable (GL_DEPTH_TEST));
+ } else {
+ GLES_CHK(glDisable (GL_DEPTH_TEST));
+ }
+
+ STATE.depthFunc = depthFunc;
+ }
+
+ const int writeMode = devstate->sourceState.depthWrite ? GL_TRUE : GL_FALSE;
+ if( writeMode != STATE.depthWrite )
+ {
+ GLES_CHK(glDepthMask (writeMode));
+ STATE.depthWrite = writeMode;
+ }
+}
+
+void GFX_GL_IMPL::SetStencilState (const DeviceStencilState* state, int stencilRef)
+{
+ if (STATE.m_CurrStencilState == state && STATE.m_StencilRef == stencilRef)
+ return;
+ const DeviceStencilStateGLES30* st = static_cast<const DeviceStencilStateGLES30*>(state);
+ STATE.m_CurrStencilState = st;
+ if (!st)
+ return;
+
+ if (st->sourceState.stencilEnable)
+ GLES_CHK(glEnable (GL_STENCIL_TEST));
+ else
+ GLES_CHK(glDisable (GL_STENCIL_TEST));
+ if (gGraphicsCaps.hasTwoSidedStencil)
+ {
+ GLES_CHK(glStencilFuncSeparate (GL_FRONT, st->stencilFuncFront, stencilRef, st->sourceState.readMask));
+ GLES_CHK(glStencilOpSeparate (GL_FRONT, st->stencilFailOpFront, st->depthFailOpFront, st->depthPassOpFront));
+ GLES_CHK(glStencilFuncSeparate (GL_BACK, st->stencilFuncBack, stencilRef, st->sourceState.readMask));
+ GLES_CHK(glStencilOpSeparate (GL_BACK, st->stencilFailOpBack, st->depthFailOpBack, st->depthPassOpBack));
+ }
+ else
+ {
+ GLES_CHK(glStencilFunc (st->stencilFuncFront, stencilRef, st->sourceState.readMask));
+ GLES_CHK(glStencilOp (st->stencilFailOpFront, st->depthFailOpFront, st->depthPassOpFront));
+ }
+ GLES_CHK(glStencilMask (st->sourceState.writeMask));
+
+ STATE.m_StencilRef = stencilRef;
+}
+
+void GFX_GL_IMPL::SetSRGBWrite (bool enable)
+{
+ // \todo [2013-04-16 pyry] Implement sRGB support:
+ // - In ES3 sRGB bit is tied to format and there is no way to create views with different format
+ // - This is used rather liberally from Camera
+ // -> Use sRGB FBO for emulation, and defer necessary blits until it is known whether they are needed
+ Assert("Not implemented");
+}
+
+bool GFX_GL_IMPL::GetSRGBWrite ()
+{
+ return false;
+}
+
+void GFX_GL_IMPL::Clear (UInt32 clearFlags, const float color[4], float depth, int stencil)
+{
+ DBG_LOG_GLES30("Clear(%d, (%.2f, %.2f, %.2f, %.2f), %.2f, %d", clearFlags, color[0], color[1], color[2], color[3], depth, stencil);
+
+ // \todo [2013-04-16 pyry] Integer render targets require use of glClearBuffer()
+ // \todo [2013-04-29 pyry] Here was a call that restored FBO binding to default one. Why?
+
+ if (STATE.m_activeFbo && STATE.m_activeFbo->GetNumColorAttachments() == 0)
+ clearFlags &= ~kGfxClearColor;
+
+ // In OpenGL, clears are affected by color write mask and depth writing parameters.
+ // So make sure to set them!
+ GLbitfield flags = 0;
+ if (clearFlags & kGfxClearColor)
+ {
+ if (STATE.colorWriteMask != 15)
+ {
+ GLES_CHK(glColorMask( true, true, true, true ));
+ STATE.colorWriteMask = 15;
+ STATE.m_CurrBlendState = NULL;
+ }
+ flags |= GL_COLOR_BUFFER_BIT;
+ GLES_CHK(glClearColor( color[0], color[1], color[2], color[3] ));
+ }
+ if (clearFlags & kGfxClearDepth)
+ {
+ GLES_CHK(glDepthMask (GL_TRUE));
+ STATE.depthWrite = GL_TRUE;
+ STATE.m_CurrDepthState = NULL;
+ flags |= GL_DEPTH_BUFFER_BIT;
+ GLES_CHK(glClearDepthf( depth ));
+ }
+ if (clearFlags & kGfxClearStencil)
+ {
+ //@TODO: need to set stencil writes on?
+ flags |= GL_STENCIL_BUFFER_BIT;
+ GLES_CHK(glClearStencil (stencil));
+ }
+ GLES_CHK(glClear(flags));
+}
+
+static void ApplyBackfaceMode( const DeviceStateGLES30& state )
+{
+ DBG_LOG_GLES30("ApplyBackfaceMode");
+ if (state.appBackfaceMode != state.userBackfaceMode)
+ GLES_CHK(glFrontFace( GL_CCW ));
+ else
+ GLES_CHK(glFrontFace( GL_CW ));
+}
+
+void GFX_GL_IMPL::SetUserBackfaceMode( bool enable )
+{
+ DBG_LOG_GLES30("SetUserBackfaceMode(%s)", GetBoolString(enable));
+ if( STATE.userBackfaceMode == enable )
+ return;
+
+ STATE.userBackfaceMode = enable;
+ ApplyBackfaceMode( STATE );
+}
+
+void GFX_GL_IMPL::SetInvertProjectionMatrix( bool enable )
+{
+ Assert (!enable); // projection should never be flipped upside down on OpenGL
+}
+
+bool GFX_GL_IMPL::GetInvertProjectionMatrix() const
+{
+ return false;
+}
+
+void GFX_GL_IMPL::SetProjectionMatrix (const Matrix4x4f& matrix)
+{
+ DBG_LOG_GLES30("SetProjectionMatrix(...)");
+
+ CopyMatrix(matrix.GetPtr(), STATE.transformState.projectionMatrixOriginal.GetPtr());
+ CopyMatrix (matrix.GetPtr(), m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatProj).GetPtr());
+ STATE.transformState.dirtyFlags |= TransformState::kProjDirty;
+}
+
+void GFX_GL_IMPL::SetWorldMatrix( const float matrix[16] )
+{
+ CopyMatrix( matrix, STATE.transformState.worldMatrix.GetPtr() );
+ STATE.transformState.dirtyFlags |= TransformState::kWorldDirty;
+}
+
+void GFX_GL_IMPL::SetViewMatrix( const float matrix[16] )
+{
+ STATE.transformState.SetViewMatrix (matrix, m_BuiltinParamValues);
+}
+
+void GFX_GL_IMPL::GetMatrix( float outMatrix[16] ) const
+{
+ STATE.transformState.UpdateWorldViewMatrix (m_BuiltinParamValues);
+ CopyMatrix (STATE.transformState.worldViewMatrix.GetPtr(), outMatrix);
+}
+
+const float* GFX_GL_IMPL::GetWorldMatrix() const
+{
+ return STATE.transformState.worldMatrix.GetPtr();
+}
+
+const float* GFX_GL_IMPL::GetViewMatrix() const
+{
+ return m_BuiltinParamValues.GetMatrixParam(kShaderMatView).GetPtr();
+}
+
+const float* GFX_GL_IMPL::GetProjectionMatrix() const
+{
+ return STATE.transformState.projectionMatrixOriginal.GetPtr();
+}
+
+const float* GFX_GL_IMPL::GetDeviceProjectionMatrix() const
+{
+ return GetProjectionMatrix();
+}
+
+void GFX_GL_IMPL::SetNormalizationBackface( NormalizationMode mode, bool backface )
+{
+ DBG_LOG_GLES30("SetNormalizationBackface(%d %s)", mode, backface?"back":"front");
+ STATE.normalization = mode;
+ if( STATE.appBackfaceMode != backface )
+ {
+ STATE.appBackfaceMode = backface;
+ ApplyBackfaceMode( STATE );
+ }
+}
+
+void GFX_GL_IMPL::SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial )
+{
+ DBG_LOG_GLES30("SetFFLighting(%s, %s, %d)", on?"True":"False", separateSpecular?"True":"False", colorMaterial);
+ STATE.lighting = on;
+ STATE.separateSpecular = separateSpecular;
+ STATE.colorMaterial = colorMaterial;
+}
+
+void GFX_GL_IMPL::SetMaterial( const float ambient[4], const float diffuse[4], const float specular[4], const float emissive[4], const float shininess )
+{
+ DBG_LOG_GLES30("SetMaterial()");
+ STATE.matAmbient.set (ambient[0], ambient[1], ambient[2], 1.0F);
+ STATE.matDiffuse.set (diffuse);
+ STATE.matSpecular.set (specular[0], specular[1], specular[2], 1.0F);
+ STATE.matEmissive.set (emissive[0], emissive[1], emissive[2], 1.0F);
+ float glshine = std::max<float>(std::min<float>(shininess,1.0f), 0.0f) * 128.0f;
+ STATE.matShininess = glshine;
+}
+
+void GFX_GL_IMPL::SetColor( const float color[4] )
+{
+ DBG_LOG_GLES30("SetColor()");
+ STATE.color.set( color );
+ // Emulate OpenGL's behaviour
+ ImmediateColor( color[0], color[1], color[2], color[3] );
+}
+
+void GFX_GL_IMPL::SetViewport( int x, int y, int width, int height )
+{
+ DBG_LOG_GLES30("SetViewport(%d, %d, %d, %d)", x, y, width, height);
+ STATE.viewport[0] = x;
+ STATE.viewport[1] = y;
+ STATE.viewport[2] = width;
+ STATE.viewport[3] = height;
+ GLES_CHK(glViewport( x, y, width, height ));
+}
+
+void GFX_GL_IMPL::GetViewport( int* port ) const
+{
+ DBG_LOG_GLES30("GetViewport()");
+ port[0] = STATE.viewport[0];
+ port[1] = STATE.viewport[1];
+ port[2] = STATE.viewport[2];
+ port[3] = STATE.viewport[3];
+}
+
+void GFX_GL_IMPL::SetScissorRect( int x, int y, int width, int height )
+{
+ DBG_LOG_GLES30("SetScissorRect(%d, %d, %d, %d)", x, y, width, height);
+
+ if (STATE.scissor != 1)
+ {
+ GLES_CHK(glEnable( GL_SCISSOR_TEST ));
+ STATE.scissor = 1;
+ }
+
+ STATE.scissorRect[0] = x;
+ STATE.scissorRect[1] = y;
+ STATE.scissorRect[2] = width;
+ STATE.scissorRect[3] = height;
+ GLES_CHK(glScissor( x, y, width, height ));
+
+}
+
+void GFX_GL_IMPL::DisableScissor()
+{
+ DBG_LOG_GLES30("DisableScissor()");
+ if (STATE.scissor != 0)
+ {
+ GLES_CHK(glDisable( GL_SCISSOR_TEST ));
+ STATE.scissor = 0;
+ }
+}
+
+bool GFX_GL_IMPL::IsScissorEnabled() const
+{
+ DBG_LOG_GLES30("IsScissorEnabled():returns %s", STATE.scissor == 1?"True":"False");
+ return STATE.scissor == 1;
+}
+
+void GFX_GL_IMPL::GetScissorRect( int scissor[4] ) const
+{
+ DBG_LOG_GLES30("GetScissorRect()");
+ scissor[0] = STATE.scissorRect[0];
+ scissor[1] = STATE.scissorRect[1];
+ scissor[2] = STATE.scissorRect[2];
+ scissor[3] = STATE.scissorRect[3];
+}
+bool GFX_GL_IMPL::IsCombineModeSupported( unsigned int combiner )
+{
+ return true;
+}
+
+TextureCombinersHandle GFX_GL_IMPL::CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular )
+{
+ DBG_LOG_GLES30("CreateTextureCombiners()");
+ TextureCombinersGLES3* implGLES = TextureCombinersGLES3::Create (count, texEnvs);
+ return TextureCombinersHandle (implGLES);
+}
+
+void GFX_GL_IMPL::DeleteTextureCombiners( TextureCombinersHandle& textureCombiners )
+{
+ DBG_LOG_GLES30("DeleteTextureCombiners()");
+ TextureCombinersGLES3* implGLES = OBJECT_FROM_HANDLE(textureCombiners, TextureCombinersGLES3);
+ delete implGLES;
+ textureCombiners.Reset();
+}
+
+void GFX_GL_IMPL::SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props )
+{
+ DBG_LOG_GLES30("SetTextureCombiners()");
+ TextureCombinersGLES3* implGLES = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersGLES3);
+ Assert (implGLES);
+
+ const int count = std::min(gGraphicsCaps.maxTexUnits, implGLES->count);
+ // Fill in arrays
+ TexEnvData* texEnvData;
+ ALLOC_TEMP (texEnvData, TexEnvData, count);
+ for( int i = 0; i < count; ++i )
+ {
+ ShaderLab::TexEnv *te = ShaderLab::GetTexEnvForBinding( implGLES->texEnvs[i], props );
+ Assert( te != NULL );
+ te->PrepareData (implGLES->texEnvs[i].m_TextureName.index, implGLES->texEnvs[i].m_MatrixName, props, &texEnvData[i]);
+ }
+
+ Vector4f* texColors;
+ ALLOC_TEMP (texColors, Vector4f, implGLES->count);
+ for( int i = 0; i < implGLES->count; ++i )
+ {
+ const ShaderLab::TextureBinding& binding = implGLES->texEnvs[i];
+ texColors[i] = binding.GetTexColor().Get (props);
+ }
+ GFX_GL_IMPL::SetTextureCombinersThreadable(textureCombiners, texEnvData, texColors);
+
+}
+
+
+void GFX_GL_IMPL::SetTextureCombinersThreadable( TextureCombinersHandle textureCombiners, const TexEnvData* texEnvData, const Vector4f* texColors )
+{
+ DBG_LOG_GLES30("SetTextureCombiners()");
+ TextureCombinersGLES3* implGLES = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersGLES3);
+ Assert (implGLES);
+
+ const int count = std::min(gGraphicsCaps.maxTexUnits, implGLES->count);
+ STATE.textureCount = count;
+ for (int i = 0; i < count; ++i)
+ {
+ const ShaderLab::TextureBinding& binding = implGLES->texEnvs[i];
+
+ // set the texture
+ ApplyTexEnvData (i, i, texEnvData[i]);
+
+ // setup texture unit state
+ TextureUnitStateGLES3& texUnitState = STATE.textures[i];
+ texUnitState.color = texColors[i];
+ texUnitState.combColor = binding.m_CombColor;
+ texUnitState.combAlpha = binding.m_CombAlpha;
+ }
+ // we can just create mask and "and" with it
+ // but consider me lazy
+ for( int i = count ; i < gGraphicsCaps.maxTexUnits ; ++i)
+ STATE.DropTexGen(i);
+
+ STATE.activeProgram = 0;
+
+ // Get us back to TU 0, so we know where we stand
+ ActivateTextureUnitGLES3 (STATE, 0);
+
+}
+
+void GFX_GL_IMPL::SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias)
+{
+ DBG_LOG_GLES30("SetTexture(%d %d)", unit, texture.m_ID);
+ DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexUnits );
+
+ GLenum texType = kGLES30TextureDimensionTable[dim];
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(texture);
+
+ if (texType == (GLenum)-1)
+ {
+ Assert("Not supported");
+ return;
+ }
+
+ // \todo [2013-04-16 pyry] Shouldn't we clear state still?
+ if (targetTex == 0)
+ return;
+
+ TextureUnitStateGLES3& currTex = STATE.textures[unit];
+ if (STATE.textureCount > unit && targetTex == currTex.texID)
+ return; // Already bound.
+
+ ActivateTextureUnitGLES3 (STATE, unit);
+
+ GLES_CHK(glBindTexture(texType, targetTex));
+
+ if (STATE.textureCount <= unit)
+ STATE.textureCount = unit+1;
+ currTex.texID = targetTex;
+ currTex.texDim = dim;
+ if (currTex.texGen == kTexGenUnknown)
+ currTex.texGen = kTexGenDisabled;
+
+ STATE.ApplyTexGen(unit);
+
+ GLESAssert();
+
+ // \todo [2013-04-16 pyry] Lod bias is given from shader.
+ if (bias != std::numeric_limits<float>::infinity())
+ currTex.bias = bias;
+}
+
+void GFX_GL_IMPL::SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16])
+{
+ DBG_LOG_GLES30("SetTextureTransform()");
+ DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexUnits );
+ TextureUnitStateGLES3& unitState = STATE.textures[unit];
+
+ unitState.SetTexGen(texGen);
+ unitState.textureMatrix = *(Matrix4x4f const*)matrix;
+
+ // Since we will set texture matrix even if TexGen is disabled (since matrix can contain scale/offset information)
+ // we set textureMatrix to identity to be on a safe side
+ if (identity)
+ unitState.textureMatrix.SetIdentity();
+ unitState.identityMatrix = identity;
+
+ unitState.isProjected = false;
+ if (!identity && dim==kTexDim2D)
+ unitState.isProjected = (matrix[3] != 0.0f || matrix[7] != 0.0f || matrix[11] != 0.0f || matrix[15] != 1.0f);
+
+ STATE.ApplyTexGen(unit);
+}
+
+void GFX_GL_IMPL::SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace )
+{
+ DBG_LOG_GLES30("SetTextureParams()");
+
+ TextureIdMapGLES30_QueryOrCreate(texture);
+
+ GLuint target = kGLES30TextureDimensionTable[texDim];
+ SetTexture (kShaderFragment, 0, 0, texture, texDim, std::numeric_limits<float>::infinity());
+
+ // Anisotropic texturing...
+ if( gGraphicsCaps.hasAnisoFilter )
+ {
+ anisoLevel = std::min( anisoLevel, gGraphicsCaps.maxAnisoLevel );
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoLevel ));
+ }
+
+ GLenum wrapMode = kWrapModeES2[wrap];
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_WRAP_S, wrapMode ));
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_WRAP_T, wrapMode ));
+
+ if( !hasMipMap && filter == kTexFilterTrilinear )
+ filter = kTexFilterBilinear;
+
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_MAG_FILTER, filter != kTexFilterNearest ? GL_LINEAR : GL_NEAREST ));
+ if( hasMipMap )
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_MIN_FILTER, kMinFilterES2[filter] ));
+ else
+ GLES_CHK(glTexParameteri (target, GL_TEXTURE_MIN_FILTER, filter != kTexFilterNearest ? GL_LINEAR : GL_NEAREST));
+
+ GLESAssert();
+}
+
+void GFX_GL_IMPL::SetMaterialProperties( const MaterialPropertyBlock& block )
+{
+ STATE.m_MaterialProperties = block;
+}
+
+void GFX_GL_IMPL::SetShadersThreadable (GpuProgram* programs[kShaderTypeCount], const GpuProgramParameters* params[kShaderTypeCount], UInt8 const * const paramsBuffer[kShaderTypeCount])
+{
+ GpuProgram* vertexProgram = programs[kShaderVertex];
+ GpuProgram* fragmentProgram = programs[kShaderFragment];
+
+ DBG_LOG_GLES30("SetShaders(%s, %s)",
+ GetShaderImplTypeString(vertexProgram? vertexProgram->GetImplType():kShaderImplUndefined),
+ GetShaderImplTypeString(fragmentProgram? fragmentProgram->GetImplType():kShaderImplUndefined));
+
+ // GLSL is only supported like this:
+ // vertex shader actually is both vertex & fragment linked program
+ // fragment shader is unused
+
+ if (vertexProgram && vertexProgram->GetImplType() == kShaderImplBoth)
+ {
+ Assert(fragmentProgram == 0 || fragmentProgram->GetImplType() == kShaderImplBoth);
+ STATE.activeProgram = vertexProgram;
+ STATE.activeProgramParams = params[kShaderVertex];
+ DebugAssert(STATE.activeProgramParams->IsReady());
+ int bufferSize = STATE.activeProgramParams->GetValuesSize();
+ STATE.activeProgramParamsBuffer.resize_uninitialized(bufferSize);
+ memcpy(STATE.activeProgramParamsBuffer.data(), paramsBuffer[kShaderVertex], bufferSize);
+ }
+ else
+ {
+ Assert(vertexProgram == 0);
+ STATE.activeProgram = 0;
+ STATE.activeProgramParams = 0;
+ STATE.activeProgramParamsBuffer.resize_uninitialized(0);
+ }
+}
+
+void GFX_GL_IMPL::CreateShaderParameters( ShaderLab::SubProgram* program, FogMode fogMode )
+{
+ GlslGpuProgramGLES30& programGLES = static_cast<GlslGpuProgramGLES30&>(program->GetGpuProgram());
+ programGLES.GetGLProgram(fogMode, program->GetParams(fogMode), program->GetChannels());
+}
+
+bool GFX_GL_IMPL::IsShaderActive( ShaderType type ) const
+{
+ //DBG_LOG_GLES30("IsShaderActive(%s): returns %s", GetShaderTypeString(type), STATE.shaderEnabledImpl[type] != kShaderImplUndefined?"True":"False");
+ //return STATE.shaderEnabledImpl[type] != kShaderImplUndefined;
+ DBG_LOG_GLES30("IsShaderActive(%s):", GetShaderTypeString(type));
+ return (STATE.activeProgram != 0);
+}
+
+void GFX_GL_IMPL::DestroySubProgram( ShaderLab::SubProgram* subprogram )
+{
+ //@TODO
+ //if (STATE.activeProgram == program)
+ //{
+ // STATE.activeProgram = NULL;
+ // STATE.activeProgramProps = NULL;
+ //}
+ delete subprogram;
+}
+
+void GFX_GL_IMPL::SetConstantBufferInfo( int id, int size )
+{
+ STATE.m_CBs.SetCBInfo(id, size);
+}
+
+void GFX_GL_IMPL::DisableLights( int startLight )
+{
+ DBG_LOG_GLES30("DisableLights(%d)", startLight);
+ startLight = std::min (startLight, gGraphicsCaps.maxLights);
+ STATE.vertexLightCount = startLight;
+ for (int i = startLight; i < kMaxSupportedVertexLightsByGLES30; ++i)
+ {
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Position + i), Vector4f(0.0f, 0.0f, 1.0f, 0.0f));
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Diffuse + i), Vector4f(0.0f, 0.0f, 0.0f, 0.0f));
+ }
+}
+
+void GFX_GL_IMPL::SetLight( int light, const GfxVertexLight& data)
+{
+ DBG_LOG_GLES30("SetLight(%d), [{%f, %f, %f, %f}, {%f, %f, %f, %f}, {%f, %f, %f, %f}, %f, %f, %f, %d]",
+ light,
+ data.position[0], data.position[1], data.position[2], data.position[3],
+ data.spotDirection[0], data.spotDirection[1], data.spotDirection[2], data.spotDirection[3],
+ data.color[0], data.color[1], data.color[2], data.color[3],
+ data.range, data.quadAtten, data.spotAngle, data.type);
+ Assert( light >= 0 && light < kMaxSupportedVertexLights );
+
+ if (light >= kMaxSupportedVertexLightsByGLES30)
+ return;
+
+ STATE.vertexLightTypes[light] = data.type;
+
+ // Transform lighting into view space
+ const Matrix4x4f& viewMat = m_BuiltinParamValues.GetMatrixParam(kShaderMatView);
+
+ // spot direction
+ Vector4f& spotDirection = m_BuiltinParamValues.GetWritableVectorParam(BuiltinShaderVectorParam(kShaderVecLight0SpotDirection + light));
+ if( data.spotAngle > 0.0f )
+ {
+ Vector3f d = viewMat.MultiplyVector3( (const Vector3f&)data.spotDirection );
+ spotDirection.Set(-d.x, -d.y, -d.z, 0.0f);
+ }
+ else
+ {
+ spotDirection.Set(0.0f, 0.0f, 1.0f, 0.0f);
+ }
+
+ Vector4f& position = m_BuiltinParamValues.GetWritableVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Position + light));
+ if (data.type == kLightDirectional)
+ {
+ Vector3f p = viewMat.MultiplyVector3( (const Vector3f&)data.position );
+ position.Set(-p.x, -p.y, -p.z, 0.0f);
+ }
+ else
+ {
+ Vector3f p = viewMat.MultiplyPoint3( (const Vector3f&)data.position );
+ position.Set(p.x, p.y, p.z, 1.0f);
+ }
+
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Diffuse + light), data.color);
+ if (data.spotAngle > 0.0f)
+ {
+ // spot attenuation formula taken from D3D9 fixed-function emulation
+ // see: VertexPipeD3D9.cpp
+ const float cosTheta = cosf(Deg2Rad(data.spotAngle)*0.25f);
+ const float cosPhi = cosf(Deg2Rad(data.spotAngle)*0.5f);
+ const float cosDiff = cosTheta - cosPhi;
+ m_BuiltinParamValues.GetWritableVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Atten + light)).Set(cosPhi, (cosDiff != 0.0f) ? 1.0f / cosDiff : 1.0f, data.quadAtten, data.range*data.range);
+ }
+ else
+ {
+ // non-spot light
+ m_BuiltinParamValues.GetWritableVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Atten + light)).Set(-1.0f, 1.0f, data.quadAtten, data.range*data.range);
+ }
+}
+
+void GFX_GL_IMPL::SetAmbient( const float ambient[4] )
+{
+ DBG_LOG_GLES30("SetAmbient()");
+ STATE.ambient.set (ambient[0], ambient[1], ambient[2], ambient[3]);
+ m_BuiltinParamValues.SetVectorParam(kShaderVecLightModelAmbient, Vector4f(ambient));
+}
+
+void GFX_GL_IMPL::EnableFog (const GfxFogParams& fog)
+{
+ DBG_LOG_GLES30("EnableFog()");
+ DebugAssertIf( fog.mode <= kFogDisabled );
+ m_FogParams = fog;
+}
+
+void GFX_GL_IMPL::DisableFog()
+{
+ DBG_LOG_GLES30("DisableFog()");
+
+ if( m_FogParams.mode != kFogDisabled )
+ {
+ m_FogParams.mode = kFogDisabled;
+ m_FogParams.density = 0.0f;
+ }
+}
+
+VBO* GFX_GL_IMPL::CreateVBO()
+{
+ VBO* vbo = new GLES3VBO();
+ OnCreateVBO(vbo);
+ return vbo;
+}
+
+void GFX_GL_IMPL::DeleteVBO( VBO* vbo )
+{
+ OnDeleteVBO(vbo);
+ delete vbo;
+}
+
+DynamicVBO& GFX_GL_IMPL::GetDynamicVBO()
+{
+ if( !STATE.m_DynamicVBO ) {
+ STATE.m_DynamicVBO = new DynamicGLES3VBO();
+ }
+ return *STATE.m_DynamicVBO;
+}
+
+// ---------- render textures
+
+static FramebufferObjectManagerGLES30& GetFBOManager (DeviceStateGLES30& deviceState)
+{
+ if (!deviceState.m_fboManager)
+ deviceState.m_fboManager = new FramebufferObjectManagerGLES30();
+ return *deviceState.m_fboManager;
+}
+
+static RenderSurfaceGLES30* CreateRenderTexture (TextureDimension dim, TextureID texID, UInt32 internalFormat, int width, int height, int depth)
+{
+ if (dim == kTexDim2D)
+ {
+ Assert(depth == 1);
+ return new RenderTexture2DGLES30(texID, internalFormat, width, height);
+ }
+ else if (dim == kTexDimCUBE)
+ {
+ Assert(width == height && depth == 1);
+ return new RenderTextureCubeGLES30(texID, internalFormat, width, height);
+ }
+ else
+ {
+ Assert(!"Not supported");
+ return 0;
+ }
+}
+
+static RenderSurfaceGLES30* CreateShadowMapRenderTexture (TextureDimension dim, TextureID texID, UInt32 internalFormat, int width, int height, int depth)
+{
+ // \note Assumes that constructor binds texID!
+ if (dim == kTexDim2D)
+ {
+ Assert(depth == 1);
+ RenderTexture2DGLES30* tex = new RenderTexture2DGLES30(texID, internalFormat, width, height);
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE));
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL));
+ return tex;
+ }
+ else if (dim == kTexDimCUBE)
+ {
+ Assert(width == height && depth == 1);
+ RenderTextureCubeGLES30* tex = new RenderTextureCubeGLES30(texID, internalFormat, width, height);
+ GLES_CHK(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE));
+ GLES_CHK(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL));
+ return tex;
+ }
+ else
+ {
+ Assert(!"Not supported");
+ return 0;
+ }
+}
+
+static RenderSurfaceGLES30* CreateRenderBuffer (TextureDimension dim, UInt32 internalFormat, int width, int height, int depth, int numSamples)
+{
+ if (dim == kTexDim2D)
+ {
+ Assert(depth == 1);
+ return new RenderBufferGLES30(internalFormat, width, height, numSamples);
+ }
+ else if (dim == kTexDimCUBE)
+ {
+ Assert(width == height && depth == 1);
+ return new RenderBufferCubeGLES30(internalFormat, width, height, numSamples);
+ }
+ else
+ {
+ Assert(!"Not supported");
+ return 0;
+ }
+}
+
+RenderSurfaceHandle GFX_GL_IMPL::CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags)
+{
+ DBG_LOG_GLES30("CreateRenderColorSurface(id = %d, %dx%dx%d, samples = %d, dim = %d, fmt = %d, flags = 0x%04x)", textureID.m_ID, width, height, depth, samples, dim, format, createFlags);
+
+ // \note Sample count 0 and 1 both map to non-multisampled textures.
+
+ if (createFlags & kSurfaceCreateNeverUsed)
+ {
+ // Use dummy surface that is not backed by any real GL object.
+ return RenderSurfaceHandle(new DummyRenderSurfaceGLES30(width, height));
+ }
+ else
+ {
+ const bool isSRGB = (createFlags & kSurfaceCreateSRGB) != 0;
+ const UInt32 internalFormat = isSRGB ? GL_SRGB8_ALPHA8 : GetColorFormatGLES30(format);
+ const bool useRBO = textureID.m_ID == 0 || samples > 1;
+
+ Assert(internalFormat != GL_NONE);
+ Assert((createFlags & kSurfaceCreateShadowmap) == 0);
+
+ if (useRBO)
+ {
+ // \todo [2013-06-04 pyry] There is no global graphics caps for max samples.
+ const int numSamplesToUse = std::min(gGraphicsCaps.gles30.maxSamples, std::max(samples,1));
+ return RenderSurfaceHandle(CreateRenderBuffer(dim, internalFormat, width, height, depth, numSamplesToUse));
+ }
+ else
+ return RenderSurfaceHandle(CreateRenderTexture(dim, textureID, internalFormat, width, height, depth));
+ }
+
+ return RenderSurfaceHandle(0);
+}
+
+RenderSurfaceHandle GFX_GL_IMPL::CreateRenderDepthSurface (TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags)
+{
+ DBG_LOG_GLES30("CreateRenderDepthSurface(id = %d, %dx%d, samples = %d, dim = %d, fmt = %d, createFlags = 0x%04x)", textureID.m_ID, width, height, samples, dim, depthFormat, createFlags);
+
+ // \note Sample count 0 and 1 both map to non-multisampled textures.
+
+ if (depthFormat == kDepthFormatNone || (createFlags & kSurfaceCreateNeverUsed) != 0)
+ {
+ // Umm... Assuming that we don't want depth buffer at all, but still this is called?
+ return RenderSurfaceHandle(new DummyRenderSurfaceGLES30(width, height));
+ }
+ else
+ {
+ const bool shadowMap = (createFlags & kSurfaceCreateShadowmap) != 0;
+ const bool useStencil = !shadowMap;
+ const UInt32 internalFormat = useStencil ? GetDepthStencilFormatGLES30(depthFormat) : GetDepthOnlyFormatGLES30(depthFormat);
+ const bool useRBO = textureID.m_ID == 0 || samples > 1;
+
+ Assert(internalFormat != GL_NONE);
+ Assert(!shadowMap || !useRBO);
+
+ if (useRBO)
+ {
+ // \todo [2013-06-04 pyry] There is no global graphics caps for max samples.
+ const int numSamplesToUse = std::min(gGraphicsCaps.gles30.maxSamples, std::max(samples,1));
+ return RenderSurfaceHandle(CreateRenderBuffer(dim, internalFormat, width, height, 1, numSamplesToUse));
+ }
+ else if (shadowMap)
+ return RenderSurfaceHandle(CreateShadowMapRenderTexture(dim, textureID, internalFormat, width, height, 1));
+ else
+ return RenderSurfaceHandle(CreateRenderTexture(dim, textureID, internalFormat, width, height, 1));
+ }
+
+ return RenderSurfaceHandle(0);
+}
+
+void GFX_GL_IMPL::DestroyRenderSurface (RenderSurfaceHandle& rs)
+{
+ DBG_LOG_GLES30("DestroyRenderSurface(%p)", rs.object);
+
+ if (!rs.IsValid())
+ return;
+
+ RenderSurfaceGLES30* renderSurface = static_cast<RenderSurfaceGLES30*>(rs.object);
+
+ // Default FBO should not be managed from outside.
+ Assert(!STATE.m_defaultFbo || !IsInFramebufferAttachmentsGLES30(*STATE.m_defaultFbo->GetAttachments(), renderSurface));
+
+ // If rs was attached to current FBO, unbind it.
+ if (STATE.m_activeFbo && IsInFramebufferAttachmentsGLES30(*STATE.m_activeFbo->GetAttachments(), renderSurface))
+ {
+ if (STATE.m_defaultFbo)
+ BindFramebufferObjectGLES30(STATE.m_defaultFbo);
+ else
+ BindDefaultFramebufferGLES30();
+
+ STATE.m_activeFbo = STATE.m_defaultFbo;
+ }
+
+ // Delete FBOs where renderSurface was attached.
+ STATE.m_fboManager->InvalidateSurface(renderSurface);
+
+ delete renderSurface;
+ rs.object = 0;
+}
+
+static bool IsAnyRSHandleValid (const RenderSurfaceHandle* handles, int numHandles)
+{
+ for (int ndx = 0; ndx < numHandles; ndx++)
+ {
+ if (handles[ndx].IsValid())
+ return true;
+ }
+ return false;
+}
+
+void GFX_GL_IMPL::SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face)
+{
+ bool hasColor = !colorHandles[0].object->backBuffer;
+ bool hasDepth = !depthHandle.object->backBuffer;
+
+ DBG_LOG_GLES30("SetRenderTargets(count = %d, color = %s, depth = %s, mip = %d, face = %d)", count, (hasColor ? "yes" : "no"), (hasDepth ? "yes" : "no"), mipLevel, face);
+
+ // \todo [2013-05-06 pyry] Enable SetRenderTargets() variant with flags and check if we should discard buffers.
+ // \todo [2013-05-02 pyry] It probably makes sense to do RT change deferred.
+
+ Assert(count <= FramebufferAttachmentsGLES30::kMaxColorAttachments);
+
+ Assert(colorHandles[0].IsValid() && depthHandle.IsValid());
+ Assert(colorHandles[0].object->backBuffer == depthHandle.object->backBuffer);
+
+ if(!hasColor && !hasDepth)
+ {
+ // Assuming default FB - right?
+ if (STATE.m_activeFbo != STATE.m_defaultFbo)
+ {
+ if (STATE.m_defaultFbo)
+ BindFramebufferObjectGLES30(STATE.m_defaultFbo);
+ else
+ BindDefaultFramebufferGLES30();
+
+ STATE.m_activeFbo = STATE.m_defaultFbo;
+ GetRealGfxDevice().GetFrameStats().AddRenderTextureChange();
+ }
+ }
+ else
+ {
+ // Translate to FramebufferAttachments
+ FramebufferAttachmentsGLES30 attachments;
+
+ attachments.numColorAttachments = count;
+ attachments.depthStencil = static_cast<RenderSurfaceGLES30*>(depthHandle.object);
+ attachments.cubemapFace = face;
+
+ for (int ndx = 0; ndx < count; ndx++)
+ attachments.color[ndx] = static_cast<RenderSurfaceGLES30*>(colorHandles[ndx].object);
+
+ // Create (or fetch from cache) FBO
+ FramebufferObjectGLES30* fbo = GetFBOManager(STATE).GetFramebufferObject(attachments);
+
+ if (STATE.m_activeFbo != fbo)
+ {
+ BindFramebufferObjectGLES30(fbo);
+ STATE.m_activeFbo = fbo;
+ GetRealGfxDevice().GetFrameStats().AddRenderTextureChange();
+ }
+ }
+}
+
+void GFX_GL_IMPL::ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle)
+{
+ DBG_LOG_GLES30("ResolveColorSurface(src = %p, dst = %p)", srcHandle.object, dstHandle.object);
+
+ // Fetch temporary FBOs for resolve - use single color attachment.
+ FramebufferObjectGLES30* srcFbo = 0;
+ FramebufferObjectGLES30* dstFbo = 0;
+
+ Assert(srcHandle.object && dstHandle.object);
+
+ for (int ndx = 0; ndx < 2; ndx++)
+ {
+ FramebufferAttachmentsGLES30 attachments;
+ attachments.numColorAttachments = 1;
+ attachments.color[0] = static_cast<RenderSurfaceGLES30*>(ndx ? srcHandle.object : dstHandle.object);
+
+ (ndx ? srcFbo : dstFbo) = GetFBOManager(STATE).GetFramebufferObject(attachments);
+ }
+
+ Assert(srcFbo && dstFbo);
+
+ int width = static_cast<RenderSurfaceGLES30*>(dstHandle.object)->GetWidth();
+ int height = static_cast<RenderSurfaceGLES30*>(dstHandle.object)->GetHeight();
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFbo->GetFboID());
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFbo->GetFboID());
+ GLES_CHK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST));
+
+ // Restore old FBO binding
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, STATE.m_activeFbo ? STATE.m_activeFbo->GetFboID() : 0));
+}
+
+RenderSurfaceHandle GFX_GL_IMPL::GetActiveRenderColorSurface(int index)
+{
+ Assert(0 <= index && index <= FramebufferAttachmentsGLES30::kMaxColorAttachments);
+
+ if (STATE.m_activeFbo)
+ return RenderSurfaceHandle(STATE.m_activeFbo->GetColorAttachment(index));
+ else
+ return RenderSurfaceHandle(0);
+}
+
+RenderSurfaceHandle GFX_GL_IMPL::GetActiveRenderDepthSurface()
+{
+ if (STATE.m_activeFbo)
+ return RenderSurfaceHandle(STATE.m_activeFbo->GetDepthStencilAttachment());
+ else
+ return RenderSurfaceHandle(0);
+}
+
+void GFX_GL_IMPL::SetSurfaceFlags (RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags)
+{
+ // \todo [2013-04-29 pyry] Implement handling for flags!
+}
+
+void GFX_GL_IMPL::DiscardContents (RenderSurfaceHandle& rs)
+{
+ DBG_LOG_GLES30("DiscardContents(%p)", rs.object);
+
+ if (!rs.IsValid())
+ return; // \todo [2013-06-05 pyry] Bug in threaded device code causes DiscardContents() calls to invalid handles.
+
+ FramebufferObjectGLES30* curFbo = STATE.m_activeFbo;
+ GLenum discardAttachments[FramebufferAttachmentsGLES30::kMaxColorAttachments+1];
+ int attachNdx = 0;
+
+ if (curFbo)
+ {
+ // Check if rs is attached to current fbo.
+ for (int colorNdx = 0; colorNdx < curFbo->GetNumColorAttachments(); colorNdx++)
+ {
+ if (rs.object == curFbo->GetColorAttachment(colorNdx))
+ discardAttachments[attachNdx++] = GL_COLOR_ATTACHMENT0+colorNdx;
+ }
+
+ if (rs.object == curFbo->GetDepthStencilAttachment())
+ {
+ // \todo [2013-05-02 pyry] Should we check if FBO actually has stencil attachment enabled?
+ discardAttachments[attachNdx++] = GL_DEPTH_STENCIL_ATTACHMENT;
+ }
+ }
+
+ Assert(attachNdx <= (int)(sizeof(discardAttachments)/sizeof(discardAttachments[0])));
+ if (attachNdx > 0)
+ GLES_CHK(glInvalidateFramebuffer(GL_FRAMEBUFFER, attachNdx, &discardAttachments[attachNdx]));
+
+ DBG_LOG_GLES30(" .. discarded in current FBO = %s", (attachNdx > 0) ? "true" : "false");
+
+ // If attachment was not discarded yet, do it later when it is bound.
+ if (attachNdx == 0)
+ {
+ RenderSurfaceGLES30* renderSurface = static_cast<RenderSurfaceGLES30*>(rs.object);
+ renderSurface->SetFlags(renderSurface->GetFlags() | RenderSurfaceGLES30::kDiscardOnBind);
+ }
+}
+
+// ---------- uploading textures
+
+void GFX_GL_IMPL::UploadTexture2D( TextureID texture, TextureDimension dimension, UInt8* srcData, int srcSize, int width, int height, TextureFormat format, int mipCount, UInt32 uploadFlags, int skipMipLevels, TextureUsageMode usageMode, TextureColorSpace colorSpace )
+{
+ ::UploadTexture2DGLES3( texture, dimension, srcData, width, height, format, mipCount, uploadFlags, skipMipLevels, colorSpace );
+}
+
+void GFX_GL_IMPL::UploadTextureSubData2D( TextureID texture, UInt8* srcData, int srcSize, int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace )
+{
+ ::UploadTextureSubData2DGLES3( texture, srcData, mipLevel, x, y, width, height, format, colorSpace );
+}
+
+void GFX_GL_IMPL::UploadTextureCube( TextureID texture, UInt8* srcData, int srcSize, int faceDataSize, int size, TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace )
+{
+ ::UploadTextureCubeGLES3( texture, srcData, faceDataSize, size, format, mipCount, uploadFlags, colorSpace );
+}
+
+void GFX_GL_IMPL::UploadTexture3D( TextureID texture, UInt8* srcData, int srcSize, int width, int height, int depth, TextureFormat format, int mipCount, UInt32 uploadFlags )
+{
+ // \todo [2013-04-16 pyry] Add support.
+ ErrorString("3D textures are not supported by OpenGLES!");
+}
+
+void GFX_GL_IMPL::DeleteTexture(TextureID texture)
+{
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(texture);
+ if(targetTex == 0)
+ return;
+
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(texture.m_ID);
+ GLES_CHK(glDeleteTextures(1, &targetTex));
+
+ // invalidate texture unit states that used this texture
+ for( int i = 0; i < gGraphicsCaps.maxTexUnits; ++i )
+ {
+ TextureUnitStateGLES3& currTex = STATE.textures[i];
+ if( currTex.texID == targetTex )
+ currTex.Invalidate();
+ }
+
+ TextureIdMap::RemoveTexture(texture);
+}
+
+// ---------- context
+
+GfxDevice::PresentMode GFX_GL_IMPL::GetPresentMode()
+{
+ return UNITY_IPHONE ? kPresentAfterDraw : kPresentBeforeUpdate;
+}
+
+static void ResetFboBindingToDefault (DeviceStateGLES30& state)
+{
+ if (state.m_activeFbo != state.m_defaultFbo)
+ {
+ if (state.m_defaultFbo)
+ BindFramebufferObjectGLES30(state.m_defaultFbo);
+ else
+ BindDefaultFramebufferGLES30();
+ state.m_activeFbo = state.m_defaultFbo;
+ }
+}
+
+void GFX_GL_IMPL::BeginFrame()
+{
+ DBG_LOG_GLES30("BeginFrame()");
+ m_InsideFrame = true;
+
+ if (gGraphicsCaps.hasTiledGPU)
+ {
+ // \todo [2013-05-02 pyry] Should we reset FBO binding here?
+ ResetFboBindingToDefault(STATE);
+ if(STATE.scissor)
+ GLES_CHK(glDisable(GL_SCISSOR_TEST));
+ GLES_CHK(glViewport(0, 0, 65536, 65536));
+ GLES_CHK(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
+ GLES_CHK(glClearStencil(0));
+ GLES_CHK(glClearDepthf(1.0f));
+ GLES_CHK(glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT));
+ GLES_CHK(glViewport(STATE.viewport[0], STATE.viewport[1], STATE.viewport[2], STATE.viewport[3]));
+ if(STATE.scissor)
+ GLES_CHK(glEnable(GL_SCISSOR_TEST));
+
+ }
+}
+void GFX_GL_IMPL::EndFrame()
+{
+ // \todo [2013-05-02 pyry] Do we really want to reset FBO binding here?
+ ResetFboBindingToDefault(STATE);
+ if (STATE.m_activeFbo != 0)
+ {
+ // If rendering to FBO, discard contents.
+ static const GLenum attachments[] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_STENCIL_ATTACHMENT };
+ GLES_CHK(glInvalidateFramebuffer(GL_FRAMEBUFFER, sizeof(attachments)/sizeof(attachments[0]), &attachments[0]));
+ }
+ else
+ {
+ // System "FBO", discard only depthstencil
+ static const GLenum attachments[] = { GL_DEPTH, GL_STENCIL };
+ GLES_CHK(glInvalidateFramebuffer(GL_FRAMEBUFFER, sizeof(attachments)/sizeof(attachments[0]), &attachments[0]));
+ }
+
+ GetBufferManagerGLES30()->AdvanceFrame();
+
+ DBG_LOG_GLES30("EndFrame()");
+ m_InsideFrame = false;
+}
+
+bool GFX_GL_IMPL::IsValidState()
+{
+#if UNITY_ANDROID
+ return ContextGLES::IsValid();
+#else
+ return true;
+#endif
+}
+
+bool GFX_GL_IMPL::HandleInvalidState()
+{
+#if UNITY_ANDROID
+ bool needReload;
+ if (!ContextGLES::HandleInvalidState(&needReload))
+ return false;
+ if (needReload)
+ ReloadResources();
+ return true;
+#else
+ return true;
+#endif
+}
+
+void GFX_GL_IMPL::PresentFrame()
+{
+ DBG_LOG_GLES30("====================================");
+ DBG_LOG_GLES30("====================================");
+ DBG_LOG_GLES30("PresentFrame");
+ DBG_LOG_GLES30("====================================");
+ DBG_LOG_GLES30("====================================");
+
+#if UNITY_WIN
+ PresentContextGLES30();
+#else
+ PresentContextGLES();
+#endif
+}
+
+void GFX_GL_IMPL::FinishRendering()
+{
+ GLES_CHK(glFinish());
+}
+
+// ---------- immediate mode rendering
+
+// we break very large immediate mode submissions into multiple batches internally
+const int kMaxImmediateVerticesPerDraw = 2048;
+
+
+ImmediateModeGLES30::ImmediateModeGLES30()
+ : m_Mode (kPrimitiveTriangles)
+ , m_IndexBufferQuads(0)
+{
+#if 0
+ m_QuadsIB = (UInt16*)UNITY_MALLOC(kMemGeometry, kMaxImmediateVerticesPerDraw*6*sizeof(UInt16));
+ UInt32 baseIndex = 0;
+ UInt16* ibPtr = m_QuadsIB;
+ for( int i = 0; i < kMaxImmediateVerticesPerDraw; ++i )
+ {
+ ibPtr[0] = baseIndex + 1;
+ ibPtr[1] = baseIndex + 2;
+ ibPtr[2] = baseIndex;
+ ibPtr[3] = baseIndex + 2;
+ ibPtr[4] = baseIndex + 3;
+ ibPtr[5] = baseIndex;
+ baseIndex += 4;
+ ibPtr += 6;
+ }
+#endif
+}
+
+ImmediateModeGLES30::~ImmediateModeGLES30()
+{
+ Invalidate();
+}
+
+void ImmediateModeGLES30::Invalidate()
+{
+ if (m_IndexBufferQuads)
+ {
+ m_IndexBufferQuads->Release();
+ m_IndexBufferQuads = 0;
+ }
+
+ m_Vertices.clear();
+ memset(&m_Current, 0, sizeof(m_Current));
+}
+
+void GFX_GL_IMPL::ImmediateVertex( float x, float y, float z )
+{
+ // If the current batch is becoming too large, internally end it and begin it again.
+ size_t currentSize = STATE.m_Imm.m_Vertices.size();
+ if( currentSize >= kMaxImmediateVerticesPerDraw - 4 )
+ {
+ GfxPrimitiveType mode = STATE.m_Imm.m_Mode;
+ // For triangles, break batch when multiple of 3's is reached.
+ if( mode == kPrimitiveTriangles && currentSize % 3 == 0 )
+ {
+ ImmediateEnd();
+ ImmediateBegin( mode );
+ }
+ // For other primitives, break on multiple of 4's.
+ // NOTE: This won't quite work for triangle strips, but we'll just pretend
+ // that will never happen.
+ else if( mode != kPrimitiveTriangles && currentSize % 4 == 0 )
+ {
+ ImmediateEnd();
+ ImmediateBegin( mode );
+ }
+ }
+ Vector3f& vert = STATE.m_Imm.m_Current.vertex;
+ vert.x = x;
+ vert.y = y;
+ vert.z = z;
+ STATE.m_Imm.m_Vertices.push_back( STATE.m_Imm.m_Current );
+}
+
+void GFX_GL_IMPL::ImmediateNormal( float x, float y, float z )
+{
+ STATE.m_Imm.m_Current.normal.x = x;
+ STATE.m_Imm.m_Current.normal.y = y;
+ STATE.m_Imm.m_Current.normal.z = z;
+}
+
+void GFX_GL_IMPL::ImmediateColor( float r, float g, float b, float a )
+{
+ r = clamp01(r);
+ g = clamp01(g);
+ b = clamp01(b);
+ a = clamp01(a);
+
+ STATE.m_Imm.m_Current.color =
+ ((UInt32)(a * 255.0f) << 24) |
+ ((UInt32)(b * 255.0f) << 16) |
+ ((UInt32)(g * 255.0f) << 8) |
+ ((UInt32)(r * 255.0f));
+}
+
+void GFX_GL_IMPL::ImmediateTexCoordAll( float x, float y, float z )
+{
+ for( int i = 0; i < gGraphicsCaps.maxTexCoords; ++i )
+ {
+ Vector3f& uv = STATE.m_Imm.m_Current.texCoords[i];
+ uv.x = x;
+ uv.y = y;
+ uv.z = z;
+ }
+}
+
+void GFX_GL_IMPL::ImmediateTexCoord( int unit, float x, float y, float z )
+{
+ if( unit < 0 || unit >= 8 )
+ {
+ ErrorString( "Invalid unit for texcoord" );
+ return;
+ }
+ Vector3f& uv = STATE.m_Imm.m_Current.texCoords[unit];
+ uv.x = x;
+ uv.y = y;
+ uv.z = z;
+}
+
+void GFX_GL_IMPL::ImmediateBegin( GfxPrimitiveType type )
+{
+ STATE.m_Imm.m_Mode = type;
+ STATE.m_Imm.m_Vertices.clear();
+}
+
+void GFX_GL_IMPL::ImmediateEnd()
+{
+ if (STATE.m_Imm.m_Vertices.empty())
+ return;
+
+ const UInt32 minBufferThreshold = 2048;
+
+ const int numVertices = STATE.m_Imm.m_Vertices.size();
+ const UInt32 vertexBufStride = (UInt32)sizeof(ImmediateVertexGLES30);
+ const UInt32 vertexBufUsage = GL_STREAM_DRAW;
+ const UInt32 vertexBufSize = vertexBufStride*numVertices;
+ const bool useBuffer = vertexBufSize >= minBufferThreshold;
+ DataBufferGLES30* vertexBuffer = useBuffer ? GetBufferManagerGLES30()->AcquireBuffer(numVertices*vertexBufStride, vertexBufUsage) : 0;
+ VertexArrayInfoGLES30 vertexSetup;
+
+ // \todo [2013-05-31 pyry] Recreate or update?
+ if (vertexBuffer)
+ vertexBuffer->RecreateWithData(vertexBufSize, vertexBufUsage, &STATE.m_Imm.m_Vertices[0]);
+
+ if (STATE.m_Imm.m_Mode == kPrimitiveQuads && !STATE.m_Imm.m_IndexBufferQuads)
+ {
+ // \todo [2013-05-31 pyry] Move somewhere else.
+ const int quadCount = kMaxImmediateVerticesPerDraw/4;
+ const int quadIndexCount = quadCount * 6;
+ const int indexBufferSize = quadIndexCount * sizeof(UInt16);
+ const UInt32 indexBufferUsage = GL_STATIC_DRAW;
+ std::vector<UInt16> quadIndices (quadIndexCount);
+
+ for (int quadNdx = 0; quadNdx < quadCount; ++quadNdx)
+ {
+ const UInt16 srcBaseNdx = quadNdx*4;
+ const int dstBaseNdx = quadNdx*6;
+ quadIndices[dstBaseNdx + 0] = srcBaseNdx + 1;
+ quadIndices[dstBaseNdx + 1] = srcBaseNdx + 2;
+ quadIndices[dstBaseNdx + 2] = srcBaseNdx;
+ quadIndices[dstBaseNdx + 3] = srcBaseNdx + 2;
+ quadIndices[dstBaseNdx + 4] = srcBaseNdx + 3;
+ quadIndices[dstBaseNdx + 5] = srcBaseNdx;
+ }
+
+ STATE.m_Imm.m_IndexBufferQuads = GetBufferManagerGLES30()->AcquireBuffer(indexBufferSize, indexBufferUsage);
+ STATE.m_Imm.m_IndexBufferQuads->RecreateWithData(indexBufferSize, indexBufferUsage, &quadIndices[0]);
+ }
+
+ // Fill in vertex setup info.
+ {
+ const UInt8* basePtr = vertexBuffer ? 0 : (const UInt8*)&STATE.m_Imm.m_Vertices[0];
+ const UInt32 buffer = vertexBuffer ? vertexBuffer->GetBuffer() : 0;
+
+ // \todo [2013-05-31 pyry] Do not send unused attributes!
+ vertexSetup.enabledArrays = (1<<kGLES3AttribLocationPosition)
+ | (1<<kGLES3AttribLocationColor)
+ | (1<<kGLES3AttribLocationNormal);
+
+ // All are sourcing from same buffer
+ for (int i = 0; i < kGLES3MaxVertexAttribs; i++)
+ vertexSetup.buffers[i] = buffer;
+
+ vertexSetup.arrays[kGLES3AttribLocationPosition] = VertexInputInfoGLES30(basePtr + 0, kChannelFormatFloat, 3, vertexBufStride);
+ vertexSetup.arrays[kGLES3AttribLocationNormal] = VertexInputInfoGLES30(basePtr + 3*sizeof(float), kChannelFormatFloat, 3, vertexBufStride);
+ vertexSetup.arrays[kGLES3AttribLocationColor] = VertexInputInfoGLES30(basePtr + 6*sizeof(float), kChannelFormatColor, 4, vertexBufStride);
+ UInt32 curOffset = 6*sizeof(float) + sizeof(UInt32);
+
+ for (int texCoordNdx = 0; texCoordNdx < (kGLES3MaxVertexAttribs-kGLES3AttribLocationTexCoord0); texCoordNdx++)
+ {
+ const int attribLoc = kGLES3AttribLocationTexCoord0+texCoordNdx;
+
+ if (attribLoc < gGraphicsCaps.gles30.maxAttributes)
+ {
+ vertexSetup.enabledArrays |= (1<<attribLoc);
+ vertexSetup.arrays[kGLES3AttribLocationTexCoord0+texCoordNdx] =
+ VertexInputInfoGLES30(basePtr + curOffset, kChannelFormatFloat, 3, vertexBufStride);
+ curOffset += 3*sizeof(float);
+ }
+ }
+ }
+
+ // Setup state
+ BeforeDrawCall(true /* immediate */);
+ SetupDefaultVertexArrayStateGLES30(vertexSetup);
+
+ switch (STATE.m_Imm.m_Mode)
+ {
+ case kPrimitiveTriangles:
+ GLES_CHK(glDrawArrays(GL_TRIANGLES, 0, numVertices));
+ m_Stats.AddDrawCall(numVertices / 3, numVertices);
+ break;
+
+ case kPrimitiveTriangleStripDeprecated:
+ GLES_CHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
+ m_Stats.AddDrawCall(numVertices - 2, numVertices);
+ break;
+
+ case kPrimitiveLines:
+ GLES_CHK(glDrawArrays(GL_LINES, 0, numVertices));
+ m_Stats.AddDrawCall(numVertices / 2, numVertices);
+ break;
+
+ case kPrimitiveQuads:
+ Assert(STATE.m_Imm.m_IndexBufferQuads);
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, STATE.m_Imm.m_IndexBufferQuads->GetBuffer()));
+ GLES_CHK(glDrawElements(GL_TRIANGLES, (numVertices/4)*6, GL_UNSIGNED_SHORT, 0));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ m_Stats.AddDrawCall(numVertices / 2, numVertices);
+ break;
+
+ default:
+ AssertString("ImmediateEnd: unknown draw mode");
+ }
+
+ if (vertexBuffer)
+ vertexBuffer->Release();
+}
+
+// ---------- readback path
+
+static int GetFirstValidColorAttachmentNdx (const FramebufferObjectGLES30* fbo)
+{
+ for (int colorNdx = 0; colorNdx < fbo->GetNumColorAttachments(); colorNdx++)
+ {
+ if (fbo->GetColorAttachment(colorNdx))
+ return colorNdx;
+ }
+
+ return -1;
+}
+
+static bool IsRenderSurfaceFormatCompatibleWithRGBA8Read (UInt32 format)
+{
+ switch (format)
+ {
+ case GL_RGBA8:
+ case GL_RGB8:
+ case GL_RG8:
+ case GL_R8:
+ case GL_RGB565:
+ case GL_RGB10_A2:
+ case GL_RGBA4:
+ case GL_RGB5_A1:
+ case GL_SRGB8_ALPHA8:
+ case GL_SRGB8:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool CanReadRGBA8FromFirstColorAttachment (const FramebufferObjectGLES30* fbo)
+{
+ int ndx = GetFirstValidColorAttachmentNdx(fbo);
+ if (ndx >= 0)
+ return IsRenderSurfaceFormatCompatibleWithRGBA8Read(fbo->GetColorAttachment(ndx)->GetFormat());
+ else
+ return false;
+}
+
+static void ReadPixelsFromDefaultFramebufferRGBA8 (int x, int y, int width, int height, UInt8* dstPtr)
+{
+ GLES_CHK(glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, dstPtr));
+}
+
+static void ReadPixelsFromActiveFramebufferObjectRGBA8 (DeviceStateGLES30& state, int x, int y, int width, int height, UInt8* dstPtr)
+{
+ Assert(state.m_activeFbo != 0);
+ Assert(CanReadRGBA8FromFirstColorAttachment(state.m_activeFbo));
+
+ FramebufferObjectGLES30* activeFbo = state.m_activeFbo;
+ const int colorNdx = GetFirstValidColorAttachmentNdx(state.m_activeFbo);
+ const RenderSurfaceGLES30* colorSurface = activeFbo->GetColorAttachment(colorNdx);
+ const bool requiresResolve = colorSurface->GetNumSamples() > 1;
+
+ if (requiresResolve)
+ {
+ FramebufferObjectManagerGLES30& fboManager = GetFBOManager(state);
+ FramebufferObjectGLES30* resolveBuffer = GetResolveFramebufferObjectGLES30(&fboManager, colorSurface->GetFormat(), 0,
+ colorSurface->GetWidth(), colorSurface->GetHeight());
+
+ GLenum drawBuffer = GL_COLOR_ATTACHMENT0;
+ GLES_CHK(glDrawBuffers(1, &drawBuffer));
+ GLES_CHK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveBuffer->GetFboID()));
+
+ // \note active FBO is already GL_READ_FRAMEBUFFER
+ GLES_CHK(glReadBuffer(GL_COLOR_ATTACHMENT0+colorNdx));
+
+ // Resolve blit.
+ GLES_CHK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST));
+
+ // Read from resolve buffer.
+ GLES_CHK(glReadBuffer(GL_COLOR_ATTACHMENT0));
+ GLES_CHK(glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, dstPtr));
+
+ // Restore binding.
+ BindFramebufferObjectGLES30(activeFbo);
+ }
+ else
+ {
+ GLES_CHK(glReadBuffer(GL_COLOR_ATTACHMENT0+colorNdx));
+ GLES_CHK(glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, dstPtr));
+ }
+}
+
+void GFX_GL_IMPL::ResolveDepthIntoTexture (RenderSurfaceHandle colorHandle, RenderSurfaceHandle depthHandle)
+{
+ FramebufferAttachmentsGLES30 att;
+ att.numColorAttachments = 1;
+ att.color[0] = static_cast<RenderSurfaceGLES30 *>(colorHandle.object);
+ att.depthStencil = static_cast<RenderSurfaceGLES30 *>(depthHandle.object);
+ att.cubemapFace = kCubeFaceUnknown;
+
+ FramebufferObjectGLES30 *helperFBO = GetFBOManager(STATE).GetFramebufferObject(att);
+ FramebufferObjectGLES30 *oldFBO = STATE.m_activeFbo;
+
+
+ // use the full size of the depth buffer, sub-rects are not needed and might not work on some hardware
+ GLint x = 0;
+ GLint y = 0;
+ GLint width = att.depthStencil->GetWidth();
+ GLint height = att.depthStencil->GetHeight();
+
+ // bind helper FBO
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, helperFBO->GetFboID()));
+
+ GLES_CHK(glReadBuffer (GL_NONE));
+
+ // blit
+ GLES_CHK(glBindFramebuffer (GL_READ_FRAMEBUFFER, oldFBO->GetFboID()));
+ GLES_CHK(glBindFramebuffer (GL_DRAW_FRAMEBUFFER, helperFBO->GetFboID()));
+ GLES_CHK(glBlitFramebuffer (x, y, x + width, y + height, x, y, x + width, y + height, GL_DEPTH_BUFFER_BIT, GL_NEAREST));
+
+ // restore the previously bound FBO
+ GLES_CHK(glBindFramebuffer (GL_FRAMEBUFFER, oldFBO->GetFboID()));
+
+
+}
+
+
+bool GFX_GL_IMPL::CaptureScreenshot (int left, int bottom, int width, int height, UInt8* rgba32)
+{
+ if (STATE.m_activeFbo)
+ {
+ if (CanReadRGBA8FromFirstColorAttachment(STATE.m_activeFbo))
+ {
+ ReadPixelsFromActiveFramebufferObjectRGBA8(STATE, left, bottom, width, height, rgba32);
+ return true;
+ }
+ else
+ {
+ ErrorString("Active FBO is not compatible with screenshots");
+ return false;
+ }
+ }
+ else
+ {
+ ReadPixelsFromDefaultFramebufferRGBA8(left, bottom, width, height, rgba32);
+ return true;
+ }
+}
+
+bool GFX_GL_IMPL::ReadbackImage (ImageReference& image, int left, int bottom, int width, int height, int destX, int destY)
+{
+ const bool coordsOk = destX == 0;
+ const bool formatOk = image.GetFormat() == kTexFormatRGBA32;
+ const bool strideOk = image.GetRowBytes() == 4*width;
+ const bool directRead = coordsOk && formatOk && strideOk;
+
+ if (directRead)
+ return CaptureScreenshot(left, bottom, width, height, image.GetRowPtr(destY));
+ else
+ {
+ std::vector<UInt8> tmpBuf(width*height*4);
+ if (!CaptureScreenshot(left, bottom, width, height, &tmpBuf[0]))
+ return false;
+
+ ImageReference blitSrc(width, height, 4*width, kTexFormatRGBA32, &tmpBuf[0]);
+ image.BlitImage(destX, destY, blitSrc);
+
+ return true;
+ }
+}
+
+void GFX_GL_IMPL::GrabIntoRenderTexture( RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height )
+{
+ if (!rs.IsValid())
+ return;
+
+ RenderSurfaceGLES30* dstColorSurface = static_cast<RenderSurfaceGLES30*>(rs.object);
+ RenderSurfaceGLES30* dstDepthSurface = static_cast<RenderSurfaceGLES30*>(rd.object);
+
+ // Grabbing to MSAA targets is not supported.
+ if (dstColorSurface->GetNumSamples() > 1 || (dstDepthSurface && dstDepthSurface->GetNumSamples() > 1))
+ {
+ ErrorString("GrabIntoRenderTexture(): Grabbing to MSAA RenderSurfaces is not supported");
+ return;
+ }
+
+ if (dstColorSurface->GetType() == RenderSurfaceGLES30::kTypeTexture2D && !dstDepthSurface && !STATE.m_activeFbo)
+ {
+ // Simple path: use glCopyTexSubImage()
+ RenderTexture2DGLES30* dstColorTex = static_cast<RenderTexture2DGLES30*>(dstColorSurface);
+
+ GetRealGfxDevice().SetTexture(kShaderFragment, 0, 0, dstColorTex->GetTextureID(), kTexDim2D, std::numeric_limits<float>::infinity());
+ GLES_CHK(glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, x, y, width, height));
+ }
+ else
+ {
+ // Temporary dst fbo.
+ FramebufferObjectManagerGLES30& fboManager = GetFBOManager(STATE);
+ FramebufferObjectGLES30* dstFbo = 0;
+ {
+ FramebufferAttachmentsGLES30 attachments;
+ attachments.numColorAttachments = 1;
+ attachments.color[0] = dstColorSurface;
+ attachments.depthStencil = dstDepthSurface;
+
+ dstFbo = fboManager.GetFramebufferObject(attachments);
+ }
+
+ bool copyColor = false;
+ bool copyDepth = false;
+ bool colorNeedsResolve = false;
+ bool depthNeedsResolve = false;
+ UInt32 srcColorFormat = 0;
+ UInt32 srcDepthFormat = 0;
+ GLenum srcColorAttachment = 0;
+
+ if (STATE.m_activeFbo)
+ {
+ const int srcColorNdx = GetFirstValidColorAttachmentNdx(STATE.m_activeFbo);
+ const RenderSurfaceGLES30* srcColorSurface = srcColorNdx >= 0 ? STATE.m_activeFbo->GetColorAttachment(srcColorNdx) : 0;
+ const RenderSurfaceGLES30* srcDepthSurface = STATE.m_activeFbo->GetDepthStencilAttachment() ? STATE.m_activeFbo->GetDepthStencilAttachment() : 0;
+
+ copyColor = srcColorSurface && dstColorSurface;
+ copyDepth = dstColorSurface && dstDepthSurface;
+ colorNeedsResolve = copyColor && srcColorSurface->GetNumSamples() > 1;
+ depthNeedsResolve = copyDepth && srcDepthSurface->GetNumSamples() > 1;
+ srcColorFormat = srcColorSurface ? srcColorSurface->GetFormat() : 0;
+ srcDepthFormat = srcDepthSurface ? srcDepthSurface->GetFormat() : 0;
+ srcColorAttachment = GL_COLOR_ATTACHMENT0 + srcColorNdx;
+ }
+ else
+ {
+ const bool isMSAA = queryInt(GL_SAMPLE_BUFFERS) > 0;
+
+ srcColorFormat = GetDefaultFramebufferColorFormatGLES30();
+ srcDepthFormat = GetDefaultFramebufferDepthFormatGLES30();
+ copyColor = srcColorFormat != 0 && dstColorSurface;
+ copyDepth = srcDepthFormat != 0 && dstDepthSurface;
+ colorNeedsResolve = isMSAA;
+ depthNeedsResolve = isMSAA;
+ srcColorAttachment = GL_BACK;
+ }
+
+ const bool colorFormatMatch = !copyColor || srcColorFormat == dstColorSurface->GetFormat();
+ const bool depthFormatMatch = !copyDepth || srcDepthFormat == dstDepthSurface->GetFormat();
+ const bool copyBoundsOk = x == 0 && y == 0;
+ const bool copyDirectly = (!colorNeedsResolve || (colorFormatMatch && copyBoundsOk)) && (!depthNeedsResolve || (depthFormatMatch && copyBoundsOk));
+ const UInt32 blitBuffers = (copyColor?GL_COLOR_BUFFER_BIT:0)|(copyDepth?GL_DEPTH_BUFFER_BIT:0);
+
+ // \note There are blits that are not supported altogether. For example blitting
+ // from unorm to integer format. Supporting them would require emulating the
+ // blit and even in such case the semantics are rather vague. So we just rely
+ // on GL giving an error if user attempts to do something strange.
+
+ if (copyDirectly)
+ {
+ GLenum drawBuffer = GL_COLOR_ATTACHMENT0;
+
+ GLES_CHK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFbo->GetFboID()));
+ GLES_CHK(glDrawBuffers(1, &drawBuffer));
+
+ // \note active FBO is already GL_READ_FRAMEBUFFER
+ GLES_CHK(glReadBuffer(srcColorAttachment));
+
+ GLES_CHK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, blitBuffers, GL_NEAREST));
+ }
+ else
+ {
+ Assert(colorNeedsResolve || depthNeedsResolve);
+
+ GLenum drawBuffer = GL_COLOR_ATTACHMENT0;
+ GLES_CHK(glDrawBuffers(1, &drawBuffer));
+
+ FramebufferObjectGLES30* resolveBuffer = GetResolveFramebufferObjectGLES30(&fboManager,
+ copyColor ? srcColorFormat : 0,
+ copyDepth ? srcDepthFormat : 0,
+ width, height);
+
+ GLES_CHK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveBuffer->GetFboID()));
+
+ // \note active FBO is already GL_READ_FRAMEBUFFER
+ GLES_CHK(glReadBuffer(srcColorAttachment));
+
+ // Resolve blit.
+ GLES_CHK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, blitBuffers, GL_NEAREST));
+
+ // Blit from resolve buffer to destination.
+ GLES_CHK(glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveBuffer->GetFboID()));
+ GLES_CHK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFbo->GetFboID()));
+ GLES_CHK(glReadBuffer(GL_COLOR_ATTACHMENT0));
+ GLES_CHK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, blitBuffers, GL_NEAREST));
+ }
+
+ // Restore readbuffer state.
+ GLES_CHK(glReadBuffer(GL_BACK));
+
+ // Restore binding.
+ if (!STATE.m_activeFbo)
+ BindDefaultFramebufferGLES30();
+ else
+ BindFramebufferObjectGLES30(STATE.m_activeFbo);
+ }
+}
+
+#if ENABLE_PROFILER
+
+void GFX_GL_IMPL::BeginProfileEvent(const char* name)
+{
+ if(gGraphicsCaps.gles30.hasDebugMarkers)
+ gGles3ExtFunc.glPushGroupMarkerEXT(0, name);
+}
+
+void GFX_GL_IMPL::EndProfileEvent()
+{
+ if(gGraphicsCaps.gles30.hasDebugMarkers)
+ gGles3ExtFunc.glPopGroupMarkerEXT();
+}
+
+GfxTimerQuery* GFX_GL_IMPL::CreateTimerQuery()
+{
+ if( gGraphicsCaps.hasTimerQuery )
+ return new TimerQueryGLES30;
+ return NULL;
+}
+
+void GFX_GL_IMPL::DeleteTimerQuery(GfxTimerQuery* query)
+{
+ delete query;
+}
+
+void GFX_GL_IMPL::BeginTimerQueries()
+{
+ if( !gGraphicsCaps.hasTimerQuery )
+ return;
+
+ g_TimerQueriesGLES30.BeginTimerQueries();
+}
+
+void GFX_GL_IMPL::EndTimerQueries()
+{
+ if( !gGraphicsCaps.hasTimerQuery )
+ return;
+
+ g_TimerQueriesGLES30.EndTimerQueries();
+}
+
+#endif // ENABLE_PROFILER
+
+typedef std::map<FixedFunctionStateGLES30, FixedFunctionProgramGLES30*, FullStateCompareGLES30> FFProgramCacheT;
+typedef std::map<FixedFunctionStateGLES30, GLShaderID, VertexStateCompareGLES30> FFVertexProgramCacheT;
+typedef std::map<FixedFunctionStateGLES30, GLShaderID, FragmentStateCompareGLES30> FFFragmentProgramCacheT;
+
+static FFProgramCacheT g_FixedFunctionProgramCache;
+static FFVertexProgramCacheT g_FFVertexProgramCache;
+static FFFragmentProgramCacheT g_FFFragmentProgramCache;
+
+static void ClearFixedFunctionPrograms()
+{
+ for (FFVertexProgramCacheT::iterator it = g_FFVertexProgramCache.begin(); it != g_FFVertexProgramCache.end(); ++it)
+ {
+ GLES_CHK(glDeleteShader(it->second));
+ }
+ g_FFVertexProgramCache.clear();
+ for (FFFragmentProgramCacheT::iterator it = g_FFFragmentProgramCache.begin(); it != g_FFFragmentProgramCache.end(); ++it)
+ {
+ GLES_CHK(glDeleteShader(it->second));
+ }
+ g_FFFragmentProgramCache.clear();
+
+ for(FFProgramCacheT::iterator i = g_FixedFunctionProgramCache.begin(); i != g_FixedFunctionProgramCache.end(); ++i)
+ {
+ delete i->second;
+ }
+ g_FixedFunctionProgramCache.clear();
+
+}
+
+static const FixedFunctionProgramGLES30* GetFixedFunctionProgram(const FixedFunctionStateGLES30& state)
+{
+ FFProgramCacheT::const_iterator cachedProgIt = g_FixedFunctionProgramCache.find(state);
+ if (cachedProgIt != g_FixedFunctionProgramCache.end())
+ return cachedProgIt->second;
+
+ // Cache miss, create fixed function program
+ // NOTE: don't worry too much about performance of vertex/fragment maps
+ // shader building/compilation is crazy expensive anyway
+ FFVertexProgramCacheT::const_iterator vertexProgIt = g_FFVertexProgramCache.find(state);
+ FFFragmentProgramCacheT::const_iterator fragmentProgIt = g_FFFragmentProgramCache.find(state);
+
+ GLShaderID vertexShader = (vertexProgIt != g_FFVertexProgramCache.end())? vertexProgIt->second: 0;
+ GLShaderID fragmentShader = (fragmentProgIt != g_FFFragmentProgramCache.end())? fragmentProgIt->second: 0;
+
+ if (vertexShader == 0)
+ {
+ vertexShader = glCreateShader(GL_VERTEX_SHADER);
+ std::string src = BuildVertexShaderSourceGLES30(state);
+ const char* cStr = src.c_str();
+
+ DBG_SHADER_VERBOSE_GLES30("Compiling generated vertex shader");
+ GlslGpuProgramGLES30::CompileGlslShader(vertexShader, cStr);
+ GLESAssert();
+
+ g_FFVertexProgramCache[state] = vertexShader;
+ }
+
+ if (fragmentShader == 0)
+ {
+ fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
+ std::string src = BuildFragmentShaderSourceGLES30(state);
+ const char* cStr = src.c_str();
+
+ DBG_SHADER_VERBOSE_GLES30("Compiling generated fragment shader");
+ GlslGpuProgramGLES30::CompileGlslShader(fragmentShader, cStr);
+ GLESAssert();
+
+ g_FFFragmentProgramCache[state] = fragmentShader;
+ }
+
+ DBG_SHADER_VERBOSE_GLES30("Creating and linking GLES program");
+ FixedFunctionProgramGLES30* ffProg = new FixedFunctionProgramGLES30(vertexShader, fragmentShader);
+ g_FixedFunctionProgramCache[state] = ffProg;
+
+ return ffProg;
+}
+
+static bool ComputeTextureTransformMatrix(TextureUnitStateGLES3 const& tex, Matrix4x4f const& worldViewMatrix, Matrix4x4f const& worldMatrix,
+ Matrix4x4f& outMatrix)
+{
+ switch (tex.texGen)
+ {
+ case kTexGenDisabled:
+ // NOTE: although tex-gen can be disabled
+ // textureMatrix can contain UV scale/offset
+ // so we will set it
+ case kTexGenObject:
+ if (tex.identityMatrix)
+ {
+ outMatrix.SetIdentity();
+ return false;
+ }
+ CopyMatrix(tex.textureMatrix.GetPtr(), outMatrix.GetPtr());
+ break;
+ case kTexGenSphereMap:
+ {
+ float invScale = 1.0f / Magnitude (worldViewMatrix.GetAxisX());
+
+ Matrix4x4f scaleOffsetMatrix;
+ scaleOffsetMatrix.SetScale(Vector3f(0.5*invScale, 0.5*invScale, 0.0));
+ scaleOffsetMatrix.SetPosition(Vector3f(0.5, 0.5, 0.0));
+
+ Matrix4x4f worldViewMatrixRotation = worldViewMatrix;
+ worldViewMatrixRotation.SetPosition(Vector3f::zero);
+ Matrix4x4f combo;
+ MultiplyMatrices4x4(&scaleOffsetMatrix, &worldViewMatrixRotation, &combo);
+ MultiplyMatrices4x4(&tex.textureMatrix, &combo, &outMatrix);
+ break;
+ }
+ case kTexGenEyeLinear:
+ MultiplyMatrices4x4(&tex.textureMatrix, &worldViewMatrix, &outMatrix);
+ break;
+ case kTexGenCubeNormal:
+ case kTexGenCubeReflect:
+ {
+ float invScale = 1.0f / Magnitude (worldMatrix.GetAxisX());
+ CopyMatrix(worldViewMatrix.GetPtr(), outMatrix.GetPtr());
+ outMatrix.Scale(Vector3f(invScale, invScale, invScale));
+ outMatrix.SetPosition(Vector3f::zero);
+ break;
+ }
+ default:
+ ErrorString( Format("Unknown TexGen mode %d", tex.texGen) );
+ }
+ return true;
+}
+
+void VBOContainsColorGLES30(bool flag)
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ GetGLES30DeviceState(device).vboContainsColor = flag;
+}
+
+void GLSLUseProgramGLES30(UInt32 programID)
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ if (GetGLES30DeviceState(device).activeProgramID == programID)
+ return;
+
+ GLES_CHK(glUseProgram (programID));
+ GetGLES30DeviceState(device).activeProgramID = programID;
+}
+
+static void UploadUniformMatrix4(BuiltinShaderParamIndices::MatrixParamData& matParam, const GpuProgramParameters::ConstantBufferList* constantBuffers, const float* dataPtr, ConstantBuffersGLES30& cbs)
+{
+ Assert(matParam.cols == 4 && matParam.rows == 4);
+ if (matParam.cbID == -1)
+ {
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, dataPtr));
+ }
+ else if (constantBuffers != NULL)
+ {
+ for (int i = 0; i < constantBuffers->size(); ++i)
+ {
+ if ((*constantBuffers)[i].m_Name.index == matParam.cbID)
+ {
+ const GpuProgramParameters::ConstantBuffer& cb = (*constantBuffers)[i];
+ int idx = cbs.FindAndBindCB(cb.m_Name.index, cb.m_BindIndex, cb.m_Size);
+ cbs.SetCBConstant(idx, matParam.gpuIndex, dataPtr, sizeof(Matrix4x4f));
+ break;
+ }
+ }
+ }
+}
+
+static void UploadUniformMatrix3(BuiltinShaderParamIndices::MatrixParamData& matParam, const GpuProgramParameters::ConstantBufferList* constantBuffers, const float* dataPtr, ConstantBuffersGLES30& cbs)
+{
+ Assert(matParam.cols == 3 && matParam.rows == 3);
+ if (matParam.cbID == -1)
+ {
+ GLES_CHK(glUniformMatrix3fv (matParam.gpuIndex, 1, GL_FALSE, dataPtr));
+ }
+ else if (constantBuffers != NULL)
+ {
+ for (int i = 0; i < constantBuffers->size(); ++i)
+ {
+ if ((*constantBuffers)[i].m_Name.index == matParam.cbID)
+ {
+ const GpuProgramParameters::ConstantBuffer& cb = (*constantBuffers)[i];
+ int idx = cbs.FindAndBindCB(cb.m_Name.index, cb.m_BindIndex, cb.m_Size);
+ cbs.SetCBConstant(idx, matParam.gpuIndex, dataPtr, sizeof(Matrix3x3f));
+ break;
+ }
+ }
+ }
+}
+
+void GFX_GL_IMPL::BeforeDrawCall(bool immediateMode)
+{
+ DBG_LOG_GLES30("BeforeDrawCall(%s)", GetBoolString(immediateMode));
+
+ ShaderLab::PropertySheet *props = ShaderLab::g_GlobalProperties;
+ Assert(props);
+
+ // WorldView Matrix
+ STATE.transformState.UpdateWorldViewMatrix (m_BuiltinParamValues);
+
+ // Materials
+ if (STATE.lighting)
+ {
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatEmission, Vector4f(STATE.matEmissive.GetPtr()));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatAmbient, Vector4f(STATE.matAmbient.GetPtr()));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatDiffuse, Vector4f(STATE.matDiffuse.GetPtr()));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatSpecular, Vector4f(STATE.matSpecular.GetPtr()));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatShininess, Vector4f(STATE.matShininess, STATE.matShininess, STATE.matShininess, STATE.matShininess));
+ }
+
+ // Fog
+ if (m_FogParams.mode > kFogDisabled)
+ {
+ float diff = m_FogParams.mode == kFogLinear ? m_FogParams.end - m_FogParams.start : 0.0f;
+ float invDiff = Abs(diff) > 0.0001f ? 1.0f/diff : 0.0f;
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFFogColor, m_FogParams.color);
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFFogParams, Vector4f(m_FogParams.density * 1.2011224087f,
+ m_FogParams.density * 1.4426950408f,
+ m_FogParams.mode == kFogLinear ? -invDiff : 0.0f,
+ m_FogParams.mode == kFogLinear ? m_FogParams.end * invDiff : 0.0f
+ ));
+ }
+
+ // Alpha-test
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFAlphaTestRef, Vector4f(STATE.alphaValue, STATE.alphaValue, STATE.alphaValue, STATE.alphaValue));
+
+ UniformCacheGLES30* targetCache = 0;
+ const GpuProgramParameters::ConstantBufferList* constantBuffers = NULL;
+ if (STATE.activeProgram)
+ {
+ // Apply GPU program
+ GlslGpuProgramGLES30& prog = static_cast<GlslGpuProgramGLES30&>(*STATE.activeProgram);
+ int fogIndex = prog.ApplyGpuProgramES30 (*STATE.activeProgramParams, STATE.activeProgramParamsBuffer.data());
+ constantBuffers = &STATE.activeProgramParams->GetConstantBuffers();
+ m_BuiltinParamIndices[kShaderVertex] = &STATE.activeProgramParams->GetBuiltinParams();
+
+ targetCache = &prog.m_UniformCache[fogIndex];
+ }
+ else
+ {
+ // Emulate Fixed Function pipe
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFColor, Vector4f(STATE.color.GetPtr()));
+ for (int i = 0; i < STATE.textureCount; ++i)
+ {
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecFFTextureEnvColor0+i), STATE.textures[i].color);
+ }
+
+ // generate program from fixed function state
+ DBG_LOG_GLES30(" using fixed-function");
+ FixedFunctionStateGLES30 ffstate;
+ STATE.ComputeFixedFunctionState(ffstate, m_FogParams);
+ const FixedFunctionProgramGLES30* program = GetFixedFunctionProgram(ffstate);
+ program->ApplyFFGpuProgram(m_BuiltinParamValues, STATE.m_CBs);
+ m_BuiltinParamIndices[kShaderVertex] = &program->GetBuiltinParams();
+ constantBuffers = &program->GetConstantBuffers();
+
+ targetCache = &program->m_UniformCache;
+ }
+
+ // Set Unity built-in parameters
+ {
+ Assert(m_BuiltinParamIndices[kShaderVertex]);
+ const BuiltinShaderParamIndices& params = *m_BuiltinParamIndices[kShaderVertex];
+
+ // MVP matrix
+ if (params.mat[kShaderInstanceMatMVP].gpuIndex >= 0)
+ {
+ Matrix4x4f wvp;
+ MultiplyMatrices4x4(&m_BuiltinParamValues.GetMatrixParam(kShaderMatProj), &STATE.transformState.worldViewMatrix, &wvp);
+
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatMVP];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ UploadUniformMatrix4(matParam, constantBuffers, wvp.GetPtr(), STATE.m_CBs);
+ }
+ // MV matrix
+ if (params.mat[kShaderInstanceMatMV].gpuIndex >= 0)
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatMV];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ UploadUniformMatrix4(matParam, constantBuffers, STATE.transformState.worldViewMatrix.GetPtr(), STATE.m_CBs);
+ }
+ // Transpose of MV matrix
+ if (params.mat[kShaderInstanceMatTransMV].gpuIndex >= 0)
+ {
+ Matrix4x4f tWV;
+ TransposeMatrix4x4(&STATE.transformState.worldViewMatrix, &tWV);
+
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatTransMV];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ UploadUniformMatrix4(matParam, constantBuffers, tWV.GetPtr(), STATE.m_CBs);
+ }
+ // Inverse transpose of MV matrix
+ if (params.mat[kShaderInstanceMatInvTransMV].gpuIndex >= 0)
+ {
+ // Inverse transpose of modelview should be scaled by uniform
+ // normal scale (this will match state.matrix.invtrans.modelview
+ // and gl_NormalMatrix in OpenGL)
+ Matrix4x4f mat = STATE.transformState.worldViewMatrix;
+ if (STATE.normalization == kNormalizationScale)
+ {
+ float invScale = m_BuiltinParamValues.GetInstanceVectorParam(kShaderInstanceVecScale).w;
+ mat.Get (0, 0) *= invScale;
+ mat.Get (1, 0) *= invScale;
+ mat.Get (2, 0) *= invScale;
+ mat.Get (0, 1) *= invScale;
+ mat.Get (1, 1) *= invScale;
+ mat.Get (2, 1) *= invScale;
+ mat.Get (0, 2) *= invScale;
+ mat.Get (1, 2) *= invScale;
+ mat.Get (2, 2) *= invScale;
+ }
+ Matrix4x4f invWV, tInvWV;
+ Matrix4x4f::Invert_General3D (mat, invWV);
+ TransposeMatrix4x4(&invWV, &tInvWV);
+
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatInvTransMV];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ UploadUniformMatrix4(matParam, constantBuffers, tInvWV.GetPtr(), STATE.m_CBs);
+ }
+ // M matrix
+ if (params.mat[kShaderInstanceMatM].gpuIndex >= 0)
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatM];
+ const Matrix4x4f& mat = STATE.transformState.worldMatrix;
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ UploadUniformMatrix4(matParam, constantBuffers, mat.GetPtr(), STATE.m_CBs);
+ }
+ // Inverse M matrix
+ if (params.mat[kShaderInstanceMatInvM].gpuIndex >= 0)
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatInvM];
+ Matrix4x4f mat = STATE.transformState.worldMatrix;
+ if (STATE.normalization == kNormalizationScale)
+ {
+ // Kill scale in the world matrix before inverse
+ float invScale = m_BuiltinParamValues.GetInstanceVectorParam(kShaderInstanceVecScale).w;
+ mat.Get (0, 0) *= invScale;
+ mat.Get (1, 0) *= invScale;
+ mat.Get (2, 0) *= invScale;
+ mat.Get (0, 1) *= invScale;
+ mat.Get (1, 1) *= invScale;
+ mat.Get (2, 1) *= invScale;
+ mat.Get (0, 2) *= invScale;
+ mat.Get (1, 2) *= invScale;
+ mat.Get (2, 2) *= invScale;
+ }
+ Matrix4x4f inverseMat;
+ Matrix4x4f::Invert_General3D (mat, inverseMat);
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ UploadUniformMatrix4(matParam, constantBuffers, inverseMat.GetPtr(), STATE.m_CBs);
+ }
+
+ // Normal matrix
+ if (params.mat[kShaderInstanceMatNormalMatrix].gpuIndex >= 0)
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatNormalMatrix];
+
+ // @TBD: remove normalization in fixed function emulation after Normal matrix multiply.
+ Matrix4x4f rotWV;
+ rotWV = STATE.transformState.worldViewMatrix;
+ rotWV.SetPosition(Vector3f::zero); // reset translation
+
+ if (STATE.normalization == kNormalizationScale) // reset scale
+ {
+ float invScale = m_BuiltinParamValues.GetInstanceVectorParam(kShaderInstanceVecScale).w;
+ rotWV.Get (0, 0) *= invScale;
+ rotWV.Get (1, 0) *= invScale;
+ rotWV.Get (2, 0) *= invScale;
+ rotWV.Get (0, 1) *= invScale;
+ rotWV.Get (1, 1) *= invScale;
+ rotWV.Get (2, 1) *= invScale;
+ rotWV.Get (0, 2) *= invScale;
+ rotWV.Get (1, 2) *= invScale;
+ rotWV.Get (2, 2) *= invScale;
+ }
+ Matrix3x3f rotWV33 = Matrix3x3f(rotWV);
+ UploadUniformMatrix3(matParam, constantBuffers, rotWV33.GetPtr(), STATE.m_CBs);
+ }
+
+ // Set instance vector parameters
+ for (int i = 0; i < kShaderInstanceVecCount; ++i)
+ {
+ int gpuIndexVS = params.vec[i].gpuIndex;
+ if (gpuIndexVS >= 0)
+ {
+ const float* val = m_BuiltinParamValues.GetInstanceVectorParam((ShaderBuiltinInstanceVectorParam)i).GetPtr();
+ switch (params.vec[i].dim) {
+ case 1: CachedUniform1(targetCache, gpuIndexVS, val); break;
+ case 2: CachedUniform2(targetCache, gpuIndexVS, val); break;
+ case 3: CachedUniform3(targetCache, gpuIndexVS, val); break;
+ case 4: CachedUniform4(targetCache, gpuIndexVS, val); break;
+ }
+ GLESAssert();
+ }
+ }
+
+ // Texture Matrices
+ Matrix4x4f texM;
+ for (int i = 0; i < kMaxSupportedTextureUnitsGLES; ++i)
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatTexture0 + i];
+ if (matParam.gpuIndex >= 0)
+ {
+ if (i < STATE.textureCount)
+ ComputeTextureTransformMatrix(STATE.textures[i], STATE.transformState.worldViewMatrix, STATE.transformState.worldMatrix, texM);
+ else
+ texM.SetIdentity();
+
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ UploadUniformMatrix4(matParam, constantBuffers, texM.GetPtr(), STATE.m_CBs);
+ }
+ }
+ }
+
+ // Set per-drawcall properties
+ GpuProgram* subprogram = STATE.activeProgram;
+ if (subprogram)
+ {
+ const MaterialPropertyBlock::Property* curProp = STATE.m_MaterialProperties.GetPropertiesBegin();
+ const MaterialPropertyBlock::Property* propEnd = STATE.m_MaterialProperties.GetPropertiesEnd();
+ const float* propBuffer = STATE.m_MaterialProperties.GetBufferBegin();
+ while (curProp != propEnd)
+ {
+ FastPropertyName name;
+ name.index = curProp->nameIndex;
+ const GpuProgramParameters::ValueParameter* param = STATE.activeProgramParams->FindParam(name);
+ if (param && curProp->rows == param->m_RowCount)
+ {
+ if (curProp->rows == 1)
+ {
+ const float* src = &propBuffer[curProp->offset];
+ switch (param->m_ColCount) {
+ case 1: CachedUniform1(targetCache, param->m_Index, src); break;
+ case 2: CachedUniform2(targetCache, param->m_Index, src); break;
+ case 3: CachedUniform3(targetCache, param->m_Index, src); break;
+ case 4: CachedUniform4(targetCache, param->m_Index, src); break;
+ }
+ GLESAssert();
+ }
+ else if (curProp->rows == 4)
+ {
+ DebugAssert(curProp->cols == 4);
+ const Matrix4x4f* mat = (const Matrix4x4f*)&propBuffer[curProp->offset];
+ GLES_CHK(glUniformMatrix4fv (param->m_Index, 1, GL_FALSE, mat->GetPtr()));
+ }
+ else
+ {
+ AssertString("Unknown property dimensions");
+ }
+ }
+ ++curProp;
+ }
+ }
+ STATE.m_MaterialProperties.Clear();
+
+ STATE.m_CBs.UpdateBuffers ();
+}
+
+bool GFX_GL_IMPL::IsPositionRequiredForTexGen(int unit) const
+{
+ if (unit >= STATE.textureCount)
+ return false;
+ if (STATE.activeProgram)
+ return false;
+
+ //DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexUnits);
+ const TextureUnitStateGLES3& unitState = STATE.textures[unit];
+ return TextureUnitStateGLES3::PositionRequiredForTexGen(unitState.texGen);
+}
+
+bool GFX_GL_IMPL::IsNormalRequiredForTexGen(int unit) const
+{
+ if (unit >= STATE.textureCount)
+ return false;
+ if (STATE.activeProgram)
+ return false;
+
+ //DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexUnits );
+ const TextureUnitStateGLES3& unitState = STATE.textures[unit];
+ return TextureUnitStateGLES3::NormalRequiredForTexGen(unitState.texGen);
+}
+
+bool GFX_GL_IMPL::IsPositionRequiredForTexGen() const
+{
+ return ( STATE.positionTexGen != 0 && !STATE.activeProgram );
+}
+
+bool GFX_GL_IMPL::IsNormalRequiredForTexGen() const
+{
+ return ( STATE.normalTexGen != 0 && !STATE.activeProgram );
+}
+
+void* GFX_GL_IMPL::GetNativeTexturePointer(TextureID id)
+{
+ return (void*)TextureIdMap::QueryNativeTexture(id);
+}
+
+void GFX_GL_IMPL::ReloadResources()
+{
+ // Buffers in BufferManager must be cleared before recreating VBOs.
+ GetBufferManagerGLES30()->InvalidateAll();
+
+ RecreateAllVBOs();
+ GfxDevice::CommonReloadResources(kReleaseRenderTextures | kReloadShaders | kReloadTextures);
+ ClearFixedFunctionPrograms();
+
+ if (STATE.m_fboManager)
+ STATE.m_fboManager->InvalidateObjects();
+
+ InvalidateState();
+}
+
+// GPU skinning functionality
+GPUSkinningInfo * GFX_GL_IMPL::CreateGPUSkinningInfo()
+{
+ if (gGraphicsCaps.gles30.useTFSkinning)
+ return new TransformFeedbackSkinningInfo();
+ else
+ return 0;
+}
+
+void GFX_GL_IMPL::DeleteGPUSkinningInfo(GPUSkinningInfo *info)
+{
+ delete reinterpret_cast<TransformFeedbackSkinningInfo *>(info);
+}
+
+// All actual functionality is performed in TransformFeedbackSkinningInfo, just forward the calls
+void GFX_GL_IMPL::SkinOnGPU( GPUSkinningInfo * info, bool lastThisFrame )
+{
+ reinterpret_cast<TransformFeedbackSkinningInfo *>(info)->SkinMesh(lastThisFrame);
+}
+
+void GFX_GL_IMPL::UpdateSkinSourceData(GPUSkinningInfo *info, const void *vertData, const BoneInfluence *skinData, bool dirty)
+{
+ reinterpret_cast<TransformFeedbackSkinningInfo *>(info)->UpdateSourceData(vertData, skinData, dirty);
+}
+
+void GFX_GL_IMPL::UpdateSkinBonePoses(GPUSkinningInfo *info, const int boneCount, const Matrix4x4f* poses)
+{
+ reinterpret_cast<TransformFeedbackSkinningInfo *>(info)->UpdateSourceBones(boneCount, poses);
+}
+
+// Acquire thread ownership on the calling thread. Worker releases ownership.
+void GFX_GL_IMPL::AcquireThreadOwnership()
+{
+ AcquireGLES30Context();
+}
+
+// Release thread ownership on the calling thread. Worker acquires ownership.
+void GFX_GL_IMPL::ReleaseThreadOwnership()
+{
+ ReleaseGLES30Context();
+}
+
+
+
+// ---------- verify state
+#if GFX_DEVICE_VERIFY_ENABLE
+void GFX_GL_IMPL::VerifyState()
+{
+}
+
+#endif // GFX_DEVICE_VERIFY_ENABLE
+
+#endif // GFX_SUPPORTS_OPENGLES30
diff --git a/Runtime/GfxDevice/opengles30/GfxDeviceGLES30.h b/Runtime/GfxDevice/opengles30/GfxDeviceGLES30.h
new file mode 100644
index 0000000..dfc716b
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/GfxDeviceGLES30.h
@@ -0,0 +1,191 @@
+#pragma once
+
+#if GFX_DEVICE_VIRTUAL
+
+#ifdef GFX_GL_IMPL
+#undef GFX_GL_IMPL
+#endif
+#define GFX_GL_IMPL GfxDeviceGLES30
+
+class GFX_GL_IMPL : public GfxThreadableDevice {
+public:
+ GFX_GL_IMPL();
+ GFX_API ~GFX_GL_IMPL();
+
+ GFX_API void InvalidateState();
+ #if GFX_DEVICE_VERIFY_ENABLE
+ GFX_API void VerifyState();
+ #endif
+
+ GFX_API void Clear (UInt32 clearFlags, const float color[4], float depth, int stencil);
+ GFX_API void SetUserBackfaceMode( bool enable );
+ GFX_API void SetWireframe(bool wire) { } // not possible in GLES
+ GFX_API bool GetWireframe() const { return false; } // not possible in GLES
+ GFX_API void SetInvertProjectionMatrix( bool enable );
+ GFX_API bool GetInvertProjectionMatrix() const;
+
+ GFX_API void SetWorldMatrix( const float matrix[16] );
+ GFX_API void SetViewMatrix( const float matrix[16] );
+
+ GFX_API void SetProjectionMatrix (const Matrix4x4f& matrix);
+ GFX_API void GetMatrix( float outMatrix[16] ) const;
+
+ GFX_API const float* GetWorldMatrix() const ;
+ GFX_API const float* GetViewMatrix() const;
+ GFX_API const float* GetProjectionMatrix() const;
+ GFX_API const float* GetDeviceProjectionMatrix() const;
+
+ GFX_API void SetNormalizationBackface( NormalizationMode mode, bool backface );
+ GFX_API void SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial );
+ GFX_API void SetMaterial( const float ambient[4], const float diffuse[4], const float specular[4], const float emissive[4], const float shininess );
+ GFX_API void SetColor( const float color[4] );
+ GFX_API void SetViewport( int x, int y, int width, int height );
+ GFX_API void GetViewport( int* values ) const;
+
+ GFX_API void SetScissorRect( int x, int y, int width, int height );
+ GFX_API void DisableScissor();
+ GFX_API bool IsScissorEnabled() const;
+ GFX_API void GetScissorRect( int values[4] ) const;
+ GFX_API void SetSRGBWrite (const bool);
+ GFX_API bool GetSRGBWrite ();
+
+ GFX_API void ResolveDepthIntoTexture (RenderSurfaceHandle colorHandle, RenderSurfaceHandle depthHandle);
+
+ GFX_API TextureCombinersHandle CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular );
+ GFX_API void DeleteTextureCombiners( TextureCombinersHandle& textureCombiners );
+ GFX_API void SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props );
+
+ GFX_API void SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias);
+ GFX_API void SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace );
+ GFX_API void SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16]);
+ GFX_API void SetTextureName ( TextureID texture, const char* name ) { }
+
+ GFX_API void SetMaterialProperties( const MaterialPropertyBlock& block );
+
+ GFX_API void SetShadersThreadable (GpuProgram* programs[kShaderTypeCount], const GpuProgramParameters* params[kShaderTypeCount], UInt8 const * const paramsBuffer[kShaderTypeCount]);
+ GFX_API void CreateShaderParameters( ShaderLab::SubProgram* program, FogMode fogMode );
+
+ GFX_API bool IsCombineModeSupported( unsigned int combiner );
+ GFX_API void SetTextureCombinersThreadable( TextureCombinersHandle textureCombiners, const TexEnvData* texEnvData, const Vector4f* texColors );
+
+ GFX_API bool IsShaderActive( ShaderType type ) const;
+ GFX_API void DestroySubProgram( ShaderLab::SubProgram* subprogram );
+ GFX_API void SetConstantBufferInfo( int id, int size );
+
+ GFX_API void DisableLights( int startLight );
+ GFX_API void SetLight( int light, const GfxVertexLight& data);
+ GFX_API void SetAmbient( const float ambient[4] );
+
+ GFX_API void EnableFog (const GfxFogParams& fog);
+ GFX_API void DisableFog();
+
+ GFX_API VBO* CreateVBO();
+ GFX_API void DeleteVBO( VBO* vbo );
+ GFX_API DynamicVBO& GetDynamicVBO();
+
+ GFX_API RenderSurfaceHandle CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags);
+ GFX_API RenderSurfaceHandle CreateRenderDepthSurface (TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags);
+ GFX_API void SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face = kCubeFaceUnknown);
+ GFX_API void DestroyRenderSurface (RenderSurfaceHandle& rs);
+ GFX_API void ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle);
+ GFX_API RenderSurfaceHandle GetActiveRenderColorSurface (int index);
+ GFX_API RenderSurfaceHandle GetActiveRenderDepthSurface ();
+ GFX_API void SetSurfaceFlags(RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags);
+ GFX_API void DiscardContents (RenderSurfaceHandle& rs);
+
+ GFX_API void UploadTexture2D( TextureID texture, TextureDimension dimension, UInt8* srcData, int srcSize, int width, int height, TextureFormat format, int mipCount, UInt32 uploadFlags, int skipMipLevels, TextureUsageMode usageMode, TextureColorSpace colorSpace );
+ GFX_API void UploadTextureSubData2D( TextureID texture, UInt8* srcData, int srcSize, int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace );
+ GFX_API void UploadTextureCube( TextureID texture, UInt8* srcData, int srcSize, int faceDataSize, int size, TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace );
+ GFX_API void UploadTexture3D( TextureID texture, UInt8* srcData, int srcSize, int width, int height, int depth, TextureFormat format, int mipCount, UInt32 uploadFlags );
+ GFX_API void DeleteTexture( TextureID texture );
+
+ GFX_API PresentMode GetPresentMode();
+
+ GFX_API void BeginFrame();
+ GFX_API void EndFrame();
+ GFX_API void PresentFrame();
+
+ GFX_API bool IsValidState();
+ GFX_API bool HandleInvalidState();
+
+ GFX_API void FinishRendering();
+
+ // Immediate mode rendering
+ GFX_API void ImmediateVertex( float x, float y, float z );
+ GFX_API void ImmediateNormal( float x, float y, float z );
+ GFX_API void ImmediateColor( float r, float g, float b, float a );
+ GFX_API void ImmediateTexCoordAll( float x, float y, float z );
+ GFX_API void ImmediateTexCoord( int unit, float x, float y, float z );
+ GFX_API void ImmediateBegin( GfxPrimitiveType type );
+ GFX_API void ImmediateEnd();
+
+ // Acquire thread ownership on the calling thread. Worker releases ownership.
+ GFX_API void AcquireThreadOwnership();
+ // Release thread ownership on the calling thread. Worker acquires ownership.
+ GFX_API void ReleaseThreadOwnership();
+
+ protected:
+
+ GFX_API bool CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 );
+ GFX_API bool ReadbackImage( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY );
+ GFX_API void GrabIntoRenderTexture( RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height );
+
+ GFX_API void BeforeDrawCall( bool immediateMode);
+
+ GFX_API void SetBlendState(const DeviceBlendState* state, float alphaRef);
+ GFX_API void SetRasterState(const DeviceRasterState* state);
+ GFX_API void SetDepthState(const DeviceDepthState* state);
+ GFX_API void SetStencilState(const DeviceStencilState* state, int stencilRef);
+
+ GFX_API DeviceBlendState* CreateBlendState(const GfxBlendState& state);
+ GFX_API DeviceDepthState* CreateDepthState(const GfxDepthState& state);
+ GFX_API DeviceStencilState* CreateStencilState(const GfxStencilState& state);
+ GFX_API DeviceRasterState* CreateRasterState(const GfxRasterState& state);
+
+ GFX_API GPUSkinningInfo *CreateGPUSkinningInfo();
+ GFX_API void DeleteGPUSkinningInfo(GPUSkinningInfo *info);
+ GFX_API void SkinOnGPU( GPUSkinningInfo * info, bool lastThisFrame );
+ GFX_API void UpdateSkinSourceData(GPUSkinningInfo *info, const void *vertData, const BoneInfluence *skinData, bool dirty);
+ GFX_API void UpdateSkinBonePoses(GPUSkinningInfo *info, const int boneCount, const Matrix4x4f* poses);
+
+#if ENABLE_PROFILER
+ GFX_API void BeginProfileEvent (const char* name);
+ GFX_API void EndProfileEvent ();
+
+ GFX_API GfxTimerQuery* CreateTimerQuery();
+ GFX_API void DeleteTimerQuery(GfxTimerQuery* query);
+ GFX_API void BeginTimerQueries();
+ GFX_API void EndTimerQueries();
+#endif
+
+public:
+ // OpenGLES specific
+ GFX_API void ReloadResources();
+ GFX_API bool IsPositionRequiredForTexGen(int texStageIndex) const;
+ GFX_API bool IsNormalRequiredForTexGen(int texStageIndex) const;
+
+ GFX_API bool IsPositionRequiredForTexGen() const;
+ GFX_API bool IsNormalRequiredForTexGen() const;
+
+ GFX_API void* GetNativeTexturePointer(TextureID id);
+
+ DeviceStateGLES30& GetState() { return state; }
+
+private:
+ DeviceStateGLES30 state;
+};
+
+#define STATE this->state
+#define GetGLES30DeviceState(device) device.GetState()
+
+#else // GFX_DEVICE_VIRTUAL
+
+
+struct GfxDeviceImpl {
+ DeviceStateGLES30 state;
+};
+
+#define STATE impl->state
+#define GetGLES30DeviceState(device) device.GetImpl()->state
+
+#endif // GFX_DEVICE_VIRTUAL
diff --git a/Runtime/GfxDevice/opengles30/GpuProgramsGLES30.cpp b/Runtime/GfxDevice/opengles30/GpuProgramsGLES30.cpp
new file mode 100644
index 0000000..42d468f
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/GpuProgramsGLES30.cpp
@@ -0,0 +1,1176 @@
+#include "UnityPrefix.h"
+#if GFX_SUPPORTS_OPENGLES30
+#include "GpuProgramsGLES30.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/File/ApplicationSpecificPersistentDataPath.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "External/shaderlab/Library/ShaderLabErrors.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/shaderlab/Library/program.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Matrix3x3.h"
+#include "Runtime/Math/Vector4.h"
+
+
+#include "IncludesGLES30.h"
+#include "AssertGLES30.h"
+#include "GpuPropertiesGLES30.h"
+#include "Runtime/Utilities/GLSLUtilities.h"
+#include "VBOGLES30.h"
+#include "ConstantBuffersGLES30.h"
+#include "DebugGLES30.h"
+
+#if UNITY_ANDROID
+ #include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+ #include "PlatformDependent/AndroidPlayer/AndroidSystemInfo.h"
+#endif
+
+#if UNITY_BLACKBERRY
+ #include "ctype.h"
+#endif
+
+
+#include <stdio.h>
+
+#define DEBUG_GLSL_BINDINGS 0
+#define DEBUG_SHADER_CACHE 0
+
+
+// from shader_yacc.hpp
+extern std::string g_LastParsedShaderName;
+
+void GLSLUseProgramGLES30 (UInt32 programID); // defined in GfxDeviceGLES30.cpp
+
+static bool CompileGLSLVertexShader (const std::string& source, ChannelAssigns& channels, GLShaderID programID, GLShaderID parentProgramID, GLShaderID* outShaderID);
+static bool CompileGLSLFragmentShader (const std::string& source, GLShaderID* outShaderID);
+static bool BindVProgAttrbutes(const std::string& source, ChannelAssigns& channels, GLShaderID programID);
+static bool RebindVProgAttrbutes(GLShaderID programID, GLShaderID parentProgramID);
+
+
+static void GetCachedBinaryName(const std::string& vprog, const std::string& fshader, char filename[33]);
+
+
+// --------------------------------------------------------------------------
+// GLSL
+
+// AttributeConversionTable
+const static UInt32 kAttribLookupTableSize = 12;
+
+const static char* s_GLSLESAttributes[kAttribLookupTableSize] = {
+ "_glesVertex", "_glesColor", "_glesNormal",
+ "_gles_unused__", // unused
+ "_glesMultiTexCoord0", "_glesMultiTexCoord1", "_glesMultiTexCoord2", "_glesMultiTexCoord3",
+ "_glesMultiTexCoord4", "_glesMultiTexCoord5", "_glesMultiTexCoord6", "_glesMultiTexCoord7"
+};
+const static char* s_UnityAttributes[kAttribLookupTableSize] = {
+ "Vertex", "Color", "Normal",
+ "", // unused
+ "TexCoord", "TexCoord1", "TexCoord2", "TexCoord3",
+ "TexCoord4", "TexCoord5", "TexCoord6", "TexCoord7"
+};
+
+
+const VertexComponent s_UnityVertexComponents[kAttribLookupTableSize] = {
+ kVertexCompVertex,
+ kVertexCompColor,
+ kVertexCompNormal,
+ kVertexCompTexCoord,
+ kVertexCompTexCoord0, kVertexCompTexCoord1, kVertexCompTexCoord2, kVertexCompTexCoord3,
+ kVertexCompTexCoord4, kVertexCompTexCoord5, kVertexCompTexCoord6, kVertexCompTexCoord7
+};
+
+const int s_GLESVertexComponents[kAttribLookupTableSize] = {
+ kGLES3AttribLocationPosition,
+ kGLES3AttribLocationColor,
+ kGLES3AttribLocationNormal,
+ -1 /*kVertexCompTexCoord*/,
+ kGLES3AttribLocationTexCoord0,
+ kGLES3AttribLocationTexCoord1,
+ kGLES3AttribLocationTexCoord2,
+ kGLES3AttribLocationTexCoord3,
+ kGLES3AttribLocationTexCoord4,
+ kGLES3AttribLocationTexCoord5,
+ kGLES3AttribLocationTexCoord6,
+ kGLES3AttribLocationTexCoord7,
+};
+
+GlslGpuProgramGLES30::GlslGpuProgramGLES30 (const std::string& source, CreateGpuProgramOutput& output)
+{
+ output.SetPerFogModeParamsEnabled(true);
+ m_ImplType = kShaderImplBoth;
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ m_GLSLVertexShader[i] = 0;
+ m_GLSLFragmentShader[i] = 0;
+ m_FogColorIndex[i] = -1;
+ m_FogParamsIndex[i] = -1;
+ m_FogFailed[i] = false;
+ }
+
+ // Fragment shaders come out as dummy GLSL text. Just ignore them; the real shader was part of
+ // the vertex shader text anyway.
+ if (source.empty())
+ return;
+
+ if (Create (source, output.CreateChannelAssigns()))
+ {
+ GpuProgramParameters& params = output.CreateParams ();
+ FillParams (m_Programs[kFogDisabled], params, output.GetOutNames());
+ if (params.GetTextureParams().size() > gGraphicsCaps.maxTexImageUnits)
+ m_NotSupported = true;
+
+ m_UniformCache[kFogDisabled].Create(&params, -1, -1);
+ }
+ else
+ {
+ m_NotSupported = true;
+ }
+}
+
+GlslGpuProgramGLES30::~GlslGpuProgramGLES30 ()
+{
+ Assert (m_ImplType == kShaderImplBoth);
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ if (m_GLSLVertexShader[i]) { GLES_CHK(glDeleteShader(m_GLSLVertexShader[i])); }
+ if (m_GLSLFragmentShader[i]) { GLES_CHK(glDeleteShader(m_GLSLFragmentShader[i])); }
+ if (m_Programs[i]) { GLES_CHK(glDeleteProgram(m_Programs[i])); }
+ m_UniformCache[i].Destroy();
+ }
+}
+
+static bool ParseGlslErrors (GLuint type , GLSLErrorType errorType, const char* source = 0)
+{
+ bool hadErrors = false;
+ char compileInfoLog[4096];
+ GLsizei infoLogLength = 0;
+ switch(errorType)
+ {
+ case kErrorCompileVertexShader:
+ case kErrorCompileFragShader:
+ glGetShaderInfoLog(type, 4096, &infoLogLength, compileInfoLog );
+ break;
+ case kErrorLinkProgram:
+ glGetProgramInfoLog(type, 4096, &infoLogLength, compileInfoLog );
+ break;
+ default:
+ FatalErrorMsg("Unknown error type");
+ break;
+ }
+
+ // Make sure it is null-terminated
+ compileInfoLog[std::min<int>(infoLogLength, 4096)] = 0;
+
+ if (strlen (compileInfoLog) > 0)
+ {
+ hadErrors = true;
+ if (source)
+ {
+ printf_console("-------- GLSL source: \n");
+ DebugTextLineByLine(source);
+ }
+
+ printf_console("-------- GLSL error:\n%s\n\n", compileInfoLog);
+ }
+
+ return hadErrors;
+}
+
+static std::string ExtractDefineBock(const std::string& defineName, const std::string& str, std::string* remainderStr)
+{
+ const std::string beginsWith = "#ifdef " + defineName;
+ const std::string endsWith = "#endif";
+
+ size_t b;
+ if ((b = str.find(beginsWith))==std::string::npos)
+ return "";
+
+ b += beginsWith.size();
+
+ size_t e = b;
+ size_t n = 1;
+ do
+ {
+ size_t nextEnd = str.find(endsWith, e);
+ size_t nextIf = str.find("#if", e);
+
+ if (nextEnd == std::string::npos)
+ return "";
+
+ if (nextIf != std::string::npos && nextIf < nextEnd)
+ {
+ ++n;
+ e = nextIf + 1;
+ }
+ else
+ {
+ --n;
+ e = nextEnd + 1;
+ }
+ }
+ while (n > 0);
+
+ std::string retVal = str.substr(b, e-b-1);
+ if (remainderStr)
+ {
+ *remainderStr = str.substr(0, b - beginsWith.size());
+ if (e + endsWith.size() < str.length()) *remainderStr += str.substr(e + endsWith.size());
+ }
+
+ return retVal;
+}
+
+static void FindProgramStart(const char* source, std::string* header, std::string* prog)
+{
+ const char* line_start = source;
+ while(*line_start)
+ {
+ while(isspace(*line_start))
+ ++line_start;
+ if(*line_start == '#')
+ {
+ while(*line_start != '\n' && *line_start != '\r')
+ ++line_start;
+ }
+ else
+ {
+ header->assign(source, line_start - source);
+ prog->assign(line_start);
+ break;
+ }
+ }
+}
+
+bool GlslGpuProgramGLES30::CompileGlslShader(GLShaderID shader, const char* source)
+{
+ GLES_CHK(glShaderSource(shader, 1, &source, NULL));
+ GLES_CHK(glCompileShader(shader));
+
+ int compiled = 10;
+ GLES_CHK(glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled));
+
+ if (compiled==0)
+ {
+ ParseGlslErrors(shader, kErrorCompileFragShader, source);
+ return false;
+ }
+
+ return true;
+}
+
+bool GlslGpuProgramGLES30::Create (const std::string &shaderString, ChannelAssigns& channels)
+{
+ GLESAssert(); // Clear any GL errors
+
+ GLES_CHK(m_Programs[0] = glCreateProgram());
+
+ m_ImplType = kShaderImplBoth;
+
+ // NOTE: pre-pending shader with VERTEX/FRAGMENT defines doesn't work with ES/GLSL compilers for some reason
+ // therefore we extract VERTEX/FRAGMENT sections from the shaderString
+
+ std::string remainder = shaderString;
+ std::string vertexShaderSource = ExtractDefineBock("VERTEX", shaderString, &remainder);
+ std::string fragmentShaderSource = ExtractDefineBock("FRAGMENT", remainder, &remainder);
+
+ vertexShaderSource = remainder + vertexShaderSource;
+ fragmentShaderSource = remainder + fragmentShaderSource;
+
+ if(!CompileProgram(0, vertexShaderSource, fragmentShaderSource, channels))
+ {
+ ParseGlslErrors( m_Programs[0], kErrorLinkProgram );
+
+ int charsWritten=0;
+
+ if(m_GLSLVertexShader[0])
+ {
+ int vertShaderLength = 0;
+ GLES_CHK(glGetShaderiv(m_GLSLVertexShader[0], GL_SHADER_SOURCE_LENGTH, &vertShaderLength));
+ char* modVertexShader = new char[vertShaderLength];
+ GLES_CHK(glGetShaderSource(m_GLSLVertexShader[0], vertShaderLength, &charsWritten, modVertexShader));
+
+ #if UNITY_WIN
+ OutputDebugString(Format("Vertex Shader:\n%s\n", modVertexShader).c_str());
+ #else
+ ::printf_console("Vertex Shader:\n");
+ DebugTextLineByLine(modVertexShader);
+ #endif
+
+ delete[] modVertexShader;
+ }
+
+ if(m_GLSLFragmentShader[0])
+ {
+ int fragShaderLength = 0;
+ GLES_CHK(glGetShaderiv(m_GLSLFragmentShader[0], GL_SHADER_SOURCE_LENGTH, &fragShaderLength));
+ char* modFragmentShader = new char[fragShaderLength];
+ GLES_CHK(glGetShaderSource(m_GLSLFragmentShader[0], fragShaderLength, &charsWritten, modFragmentShader));
+
+ #if UNITY_WIN
+ OutputDebugString(Format("Fragment Shader:\n%s\n", modFragmentShader).c_str());
+ #else
+ ::printf_console("Vertex Shader:\n");
+ DebugTextLineByLine(modFragmentShader);
+ #endif
+
+ delete[] modFragmentShader;
+ }
+
+ // TODO: cleanup
+ return false;
+ }
+
+ m_VertexShaderSourceForFog = vertexShaderSource;
+ m_SourceForFog = fragmentShaderSource;
+
+ return true;
+}
+
+// \todo [2013-04-17 pyry] Uh...
+std::string GlslGpuProgramGLES30::_CachePath;
+
+bool GlslGpuProgramGLES30::InitBinaryShadersSupport()
+{
+ if (gGraphicsCaps.gles30.useProgramBinary)
+ {
+ int binFormatCount = 0;
+ GLES_CHK(glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS_OES, &binFormatCount));
+
+ if(binFormatCount > 0)
+ {
+ _CachePath = GetPersistentDataPathApplicationSpecific() + "/shader/";
+ if(!IsDirectoryCreated(_CachePath))
+ CreateDirectory(_CachePath);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void Internal_ClearShaderCache()
+{
+ std::string shaderCache = GetPersistentDataPathApplicationSpecific() + "/shader/";
+ DeleteFileOrDirectory(shaderCache);
+ CreateDirectory(shaderCache);
+}
+
+bool GlslGpuProgramGLES30::CompileProgram(unsigned index, const std::string& vprog, const std::string& fshader, ChannelAssigns& channels)
+{
+ std::string binaryPath;
+
+ if (gGraphicsCaps.gles30.useProgramBinary)
+ {
+ char filename[33] = {0};
+ GetCachedBinaryName(vprog, fshader, filename);
+
+ binaryPath = _CachePath + filename;
+ #if DEBUG_SHADER_CACHE
+ ::printf_console("Starting compilation \"%s\" variation with md5:%s\n", g_LastParsedShaderName.c_str(), filename);
+ #endif
+ }
+
+ // first 4 bytes are for binary format
+ char* binaryData = 0;
+ char* binaryProgram = 0;
+ unsigned binaryLength = 0;
+
+ bool loadedBinary = false;
+ if (gGraphicsCaps.gles30.useProgramBinary)
+ {
+ FILE* binaryFile = ::fopen(binaryPath.c_str(), "rb");
+ if(binaryFile)
+ {
+ ::fseek(binaryFile, 0, SEEK_END);
+ unsigned datasz = (unsigned)::ftell(binaryFile);
+ ::fseek(binaryFile, 0, SEEK_SET);
+
+ binaryData = (char*)::malloc(datasz);
+ binaryProgram = binaryData + sizeof(GLenum);
+ binaryLength = datasz - sizeof(GLenum);
+ ::fread(binaryData, datasz, 1, binaryFile);
+ ::fclose(binaryFile);
+
+ loadedBinary = true;
+ #if DEBUG_SHADER_CACHE
+ ::printf_console("Loaded from cache");
+ #endif
+ }
+ }
+
+ if(loadedBinary)
+ {
+ loadedBinary = false;
+
+ bool attrBound = index==0 ? BindVProgAttrbutes(vprog, channels, m_Programs[0])
+ : RebindVProgAttrbutes(m_Programs[index], m_Programs[0]);
+ if(attrBound)
+ {
+ GLES_CHK(glProgramBinary(m_Programs[index], *((GLenum*)binaryData), binaryProgram, binaryLength));
+
+ int linked = 0;
+ GLES_CHK(glGetProgramiv(m_Programs[index], GL_LINK_STATUS, &linked));
+ loadedBinary = linked != 0;
+ }
+
+ if(!loadedBinary)
+ {
+ #if DEBUG_SHADER_CACHE
+ ::printf_console("Bad cached version\n");
+ #endif
+
+ ::free(binaryData);
+ binaryProgram = binaryData = 0;
+ }
+ }
+
+ // fallback to compiling shaders at runtime
+ if(!loadedBinary)
+ {
+ DBG_SHADER_VERBOSE_GLES30("Compiling shader: %s\n", g_LastParsedShaderName.c_str());
+ #if DEBUG_SHADER_CACHE
+ ::printf_console("Actually compiling");
+ #endif
+
+ if(!CompileGLSLVertexShader(vprog, channels, m_Programs[index], m_Programs[0], &m_GLSLVertexShader[index]))
+ return false;
+ if(!CompileGLSLFragmentShader(fshader, &m_GLSLFragmentShader[index]))
+ return false;
+
+ GLES_CHK(glAttachShader(m_Programs[index], m_GLSLVertexShader[index]));
+ GLES_CHK(glAttachShader(m_Programs[index], m_GLSLFragmentShader[index]));
+ GLES_CHK(glLinkProgram(m_Programs[index]));
+
+ int linked = 0;
+ GLES_CHK(glGetProgramiv(m_Programs[index], GL_LINK_STATUS, &linked));
+ if(linked == 0)
+ return false;
+
+ if (gGraphicsCaps.gles30.useProgramBinary)
+ {
+ Assert(binaryData == 0 && binaryProgram == 0);
+
+ GLES_CHK(glGetProgramiv(m_Programs[index], GL_PROGRAM_BINARY_LENGTH_OES, (GLint*)&binaryLength));
+ binaryData = (char*)::malloc(binaryLength + sizeof(GLenum));
+ binaryProgram = binaryData + sizeof(GLenum);
+ GLES_CHK(glGetProgramBinary(m_Programs[index], binaryLength, 0, (GLenum*)binaryData, binaryProgram));
+
+ FILE* binaryFile = ::fopen(binaryPath.c_str(), "wb");
+ if(binaryFile)
+ {
+ ::fwrite(binaryData, binaryLength + sizeof(GLenum), 1, binaryFile);
+ ::fclose(binaryFile);
+
+ #if DEBUG_SHADER_CACHE
+ ::printf_console("Saved to cache\n");
+ #endif
+ }
+ }
+ }
+
+ if(binaryData)
+ ::free(binaryData);
+
+ return true;
+}
+
+
+
+static void PrintNumber (char* s, int i, bool brackets)
+{
+ DebugAssert (i >= 0 && i < 100);
+ if (brackets)
+ *s++ = '[';
+
+ if (i < 10) {
+ *s++ = '0' + i;
+ } else {
+ *s++ = '0' + i/10;
+ *s++ = '0' + i%10;
+ }
+
+ if (brackets)
+ *s++ = ']';
+ *s++ = 0;
+}
+
+static void AddSizedVectorParam (GpuProgramParameters& params, ShaderParamType type, GLuint program, int vectorSize, int uniformNumber, int arraySize, const char* unityName, char* glName, int glNameIndexOffset, PropertyNamesSet* outNames)
+{
+ if (arraySize <= 1)
+ {
+ params.AddVectorParam (uniformNumber, type, vectorSize, unityName, -1, outNames);
+ }
+ else
+ {
+ for (int j = 0; j < arraySize; ++j)
+ {
+ PrintNumber (glName+glNameIndexOffset, j, true);
+ uniformNumber = glGetUniformLocation (program, glName);
+ PrintNumber (glName+glNameIndexOffset, j, false);
+ params.AddVectorParam (uniformNumber, type, vectorSize, glName, -1, outNames);
+ }
+ }
+}
+
+static void AddSizedMatrixParam (GpuProgramParameters& params, GLuint program, int rows, int cols, int uniformNumber, int arraySize, const char* unityName, char* glName, int glNameIndexOffset, PropertyNamesSet* outNames)
+{
+ if (arraySize <= 1)
+ {
+ params.AddMatrixParam (uniformNumber, unityName, rows, cols, -1, outNames);
+ }
+ else
+ {
+ for (int j = 0; j < arraySize; ++j)
+ {
+ PrintNumber (glName+glNameIndexOffset, j, true);
+ uniformNumber = glGetUniformLocation (program, glName);
+ PrintNumber (glName+glNameIndexOffset, j, false);
+ params.AddMatrixParam (uniformNumber, glName, rows, cols, -1, outNames);
+ }
+ }
+}
+
+
+void GlslGpuProgramGLES30::FillParams (unsigned int programID, GpuProgramParameters& params, PropertyNamesSet* outNames)
+{
+ if (!programID)
+ return;
+
+ int activeUniforms;
+
+ char name[1024];
+ GLenum type;
+ int arraySize = 0,
+ nameLength = 0,
+ bufSize = sizeof(name);
+
+ DBG_LOG_GLES30("GLSL: apply params to program id=%i\n", programID);
+ GLSLUseProgramGLES30 (programID);
+
+ // Figure out the uniforms
+ GLES_CHK(glGetProgramiv (programID, GL_ACTIVE_UNIFORMS, &activeUniforms));
+ for(int i=0; i < activeUniforms; i++)
+ {
+ GLES_CHK(glGetActiveUniform (programID, i, bufSize, &nameLength, &arraySize, &type, name));
+
+ if (!strcmp (name, "_unity_FogParams") || !strcmp(name, "_unity_FogColor"))
+ continue;
+
+ // some Unity builtin properties are mapped to GLSL structure fields
+ // hijack them here
+ const char* glslName = GetGLSLES3PropertyNameRemap(name);
+ const char* unityName = glslName ? glslName : name;
+
+ if (!strncmp (name, "gl_", 3)) // skip "gl_" names
+ continue;
+
+ int uniformNumber = glGetUniformLocation (programID, name);
+ Assert(uniformNumber != -1);
+
+ char* glName = name;
+ int glNameIndexOffset = 0;
+
+ bool isElemZero = false;
+ bool isArray = IsShaderParameterArray(name, nameLength, arraySize, &isElemZero);
+ if (isArray)
+ {
+ // for array parameters, transform name a bit: Foo[0] becomes Foo0
+ if (arraySize >= 100) {
+ ErrorString( "GLSL: array sizes larger than 99 not supported" ); // TODO: SL error
+ arraySize = 99;
+ }
+ // TODO: wrong? what if we use only array[1] for example
+ if (isElemZero)
+ {
+ glNameIndexOffset = nameLength-3;
+ if(glNameIndexOffset < 0)
+ glNameIndexOffset = 0;
+ glName[glNameIndexOffset] = '0';
+ glName[glNameIndexOffset+1] = 0;
+ }
+ else
+ {
+ glNameIndexOffset = nameLength;
+ }
+ }
+
+ if(type == GL_FLOAT) {
+ AddSizedVectorParam (params, kShaderParamFloat, programID, 1, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ }
+ else if(type == GL_FLOAT_VEC2) {
+ AddSizedVectorParam (params, kShaderParamFloat, programID, 2, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ }
+ else if(type == GL_FLOAT_VEC3) {
+ AddSizedVectorParam (params, kShaderParamFloat, programID, 3, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ }
+ else if(type == GL_FLOAT_VEC4) {
+ AddSizedVectorParam (params, kShaderParamFloat, programID, 4, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ }
+ else if(type == GL_FLOAT_MAT4) {
+ AddSizedMatrixParam (params, programID, 4, 4, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ }
+ else if(type == GL_FLOAT_MAT3) {
+ AddSizedMatrixParam (params, programID, 3, 3, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ }
+
+ else if (type == GL_SAMPLER_2D || type == GL_SAMPLER_2D_SHADOW) {
+ GLES_CHK(glUniform1i (uniformNumber, params.GetTextureParams().size()));
+ params.AddTextureParam (uniformNumber, -1, unityName, kTexDim2D, outNames);
+ }
+ else if (type == GL_SAMPLER_CUBE || type == GL_SAMPLER_CUBE_SHADOW) {
+ GLES_CHK(glUniform1i (uniformNumber, params.GetTextureParams().size()));
+ params.AddTextureParam (uniformNumber, -1, unityName, kTexDimCUBE, outNames);
+ }
+ /*
+ else if(type == GL_SAMPLER_3D) {
+ GLES_CHK(glUniform1i (uniformNumber, params.GetTextureParams().size()));
+ params.AddTextureParam( name, kTexDim3D, uniformNumber );
+ }
+ else if(type == GL_SAMPLER_2D_SHADOW) {
+ GLES_CHK(glUniform1i (uniformNumber, params.GetTextureParams().size()));
+ params.AddTextureParam( name, kTexDim2D, uniformNumber );
+ }
+
+ */
+ else {
+ AssertString( "Unrecognized GLSL uniform type" );
+ }
+ }
+
+ GLESAssert();
+}
+
+static bool RebindVProgAttrbutes(GLShaderID programID, GLShaderID parentProgramID)
+{
+ int attribCount = 0;
+ GLES_CHK(glGetProgramiv(parentProgramID, GL_ACTIVE_ATTRIBUTES, &attribCount));
+
+ const int kBufSize = 256;
+ char name[kBufSize];
+ for(int i = 0 ; i < attribCount ; ++i)
+ {
+ int nameLength = 0, arraySize = 0;
+ GLenum type;
+ GLES_CHK(glGetActiveAttrib (parentProgramID, i, kBufSize, &nameLength, &arraySize, &type, name));
+ int location = glGetAttribLocation (parentProgramID, name);
+ if (location != -1)
+ GLES_CHK(glBindAttribLocation(programID, location, name));
+ }
+
+ return true;
+}
+
+static bool BindVProgAttrbutes(const std::string& source, ChannelAssigns& channels, GLShaderID programID)
+{
+ // Add necessary attribute tags
+ for (UInt32 j = 0; j < kAttribLookupTableSize; j++)
+ {
+ if (source.find(s_GLSLESAttributes[j]) != std::string::npos)
+ {
+ if (s_GLESVertexComponents[j] >= gGraphicsCaps.gles30.maxAttributes)
+ {
+ ErrorString("Shader uses too many vertex attributes for this platform");
+ return false;
+ }
+ GLES_CHK(glBindAttribLocation(programID, s_GLESVertexComponents[j], s_GLSLESAttributes[j]));
+ ShaderChannel shaderChannel = GetShaderChannelFromName(s_UnityAttributes[j]);
+ if( shaderChannel != kShaderChannelNone )
+ channels.Bind (shaderChannel, s_UnityVertexComponents[j]);
+ }
+ }
+
+ // UGLY HACK:
+ // somewhere deep inside shader generation we just put attribute TANGENT
+ // without ever using it after
+ // look for TANGENT twice to be sure we use it
+ size_t firstTangentUsage = source.find("TANGENT");
+ if( firstTangentUsage != std::string::npos && source.find("TANGENT", firstTangentUsage+1) != std::string::npos )
+ {
+ // Find first free slot for tangents and use it.
+ // Unity normally supports 2 UV slots (0&1), so start looking from
+ // kVertexTexCoord2
+ for (int i = kVertexCompTexCoord2; i < kVertexCompTexCoord7; i++)
+ {
+ if (channels.GetSourceForTarget((VertexComponent)i) == kShaderChannelNone)
+ {
+ channels.Bind (kShaderChannelTangent, (VertexComponent)i);
+ GLES_CHK(glBindAttribLocation(programID, kGLES3AttribLocationTexCoord0 + i - kVertexCompTexCoord0, "_glesTANGENT"));
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool CompileGLSLVertexShader (const std::string& source, ChannelAssigns& channels, GLShaderID programID, GLShaderID parentProgramID, GLShaderID* outShaderID)
+{
+ GLES_CHK(*outShaderID = glCreateShader(GL_VERTEX_SHADER));
+
+ if(parentProgramID == programID)
+ BindVProgAttrbutes(source, channels, programID);
+ else
+ RebindVProgAttrbutes(programID, parentProgramID);
+
+ const char* text = source.c_str();
+ DBG_SHADER_VERBOSE_GLES30_DUMP_SHADER("Compiling VERTEX program:", text);
+
+ if (GlslGpuProgramGLES30::CompileGlslShader(*outShaderID, text))
+ return true;
+
+ printf_console ("GLES30: failed to compile vertex shader:\n%s\n", text);
+
+ // TODO: cleanup
+ return false;
+}
+
+static bool CompileGLSLFragmentShader (const std::string& source, GLShaderID* outShaderID)
+{
+ *outShaderID = glCreateShader(GL_FRAGMENT_SHADER);
+
+ ///@TODO: find any existing precision statement
+
+ std::string modSourceHeader, modSourceProg;
+ FindProgramStart(source.c_str(), &modSourceHeader, &modSourceProg);
+
+ // \todo [2013-04-17 pyry] Should we default to highp or mediump?
+ std::string modSource = modSourceHeader
+ + std::string("precision highp float;\n")
+ + modSourceProg;
+
+ const char* text = modSource.c_str();
+ DBG_SHADER_VERBOSE_GLES30_DUMP_SHADER("Compiling FRAGMENT program:", text);
+
+ if (GlslGpuProgramGLES30::CompileGlslShader(*outShaderID, text))
+ return true;
+
+ printf_console ("GLES30: failed to compile fragment shader:\n%s\n", text);
+
+ // TODO: cleanup
+ return false;
+}
+
+int GlslGpuProgramGLES30::GetGLProgram (FogMode fog, GpuProgramParameters& outParams, ChannelAssigns &channels)
+{
+ int index = 0;
+ if (fog > kFogDisabled && !m_FogFailed[fog] && !m_SourceForFog.empty())
+ {
+ index = fog;
+ Assert (index >= 0 && index < kFogModeCount);
+
+ // create patched fog program if needed
+ if(!m_Programs[index])
+ {
+ std::string srcVS = m_VertexShaderSourceForFog;
+ std::string srcPS = m_SourceForFog;
+
+ if(PatchShaderFogGLES (srcVS, srcPS, fog, CanUseOptimizedFogCodeGLES(srcVS)))
+ {
+ // create program, shaders, link
+ GLES_CHK(m_Programs[index] = glCreateProgram());
+ ShaderErrors errors;
+ if(CompileProgram(index, srcVS, srcPS, channels))
+ {
+ FillParams (m_Programs[index], outParams, NULL);
+ m_FogParamsIndex[index] = glGetUniformLocation(m_Programs[index], "_unity_FogParams");
+ m_FogColorIndex[index] = glGetUniformLocation(m_Programs[index], "_unity_FogColor");
+
+ m_UniformCache[index].Create(&outParams, m_FogParamsIndex[index], m_FogColorIndex[index]);
+ }
+ else
+ {
+ if(m_GLSLVertexShader[index])
+ glDeleteShader(m_GLSLVertexShader[index]);
+
+ if(m_GLSLFragmentShader[index])
+ glDeleteShader(m_GLSLFragmentShader[index]);
+
+ m_GLSLVertexShader[index] = 0;
+ m_GLSLFragmentShader[index] = 0;
+
+ glDeleteProgram(m_Programs[index]);
+ m_Programs[index] = 0;
+
+ m_FogFailed[index] = true;
+ index = 0;
+ }
+ }
+ else
+ {
+ m_FogFailed[index] = true;
+ index = 0;
+ }
+ }
+ }
+ return index;
+}
+
+int GlslGpuProgramGLES30::ApplyGpuProgramES30 (const GpuProgramParameters& params, const UInt8 *buffer)
+{
+ DBG_LOG_GLES30("GlslGpuProgramGLES30::ApplyGpuProgramES30()");
+
+ // m_Programs[0] == 0, is when Unity tries to build a dummy shader with empty source for fragment shaders, do nothing in this case
+ if (m_Programs[0] == 0)
+ return 0;
+
+ GfxDevice& device = GetRealGfxDevice();
+
+ const GfxFogParams& fog = device.GetFogParams();
+ const int index = (int)fog.mode;
+
+ DBG_LOG_GLES30("GLSL: apply program id=%i\n", m_Programs[index]);
+ GLSLUseProgramGLES30 (m_Programs[index]);
+
+ // Apply value parameters
+ const GpuProgramParameters::ValueParameterArray& valueParams = params.GetValueParams();
+ GpuProgramParameters::ValueParameterArray::const_iterator valueParamsEnd = valueParams.end();
+ for( GpuProgramParameters::ValueParameterArray::const_iterator i = valueParams.begin(); i != valueParamsEnd; ++i )
+ {
+ if (i->m_RowCount == 1 && i->m_ArraySize == 1)
+ {
+ UniformCacheGLES30* cache = m_UniformCache+index;
+// const float* val = i->m_ColCount == 1 ? &ShaderLab::shaderprops::GetFloat(props, i->m_Name)
+// : ShaderLab::shaderprops::GetVector(props, i->m_Name).GetPtr();
+ const float * val = reinterpret_cast<const float*>(buffer);
+
+ switch (i->m_ColCount)
+ {
+ case 1: CachedUniform1(cache, i->m_Index, val); break;
+ case 2: CachedUniform2(cache, i->m_Index, val); break;
+ case 3: CachedUniform3(cache, i->m_Index, val); break;
+ case 4: CachedUniform4(cache, i->m_Index, val); break;
+ default: break;
+ }
+
+ #if DEBUG_GLSL_BINDINGS
+ ;;printf_console(" vector %i dim=%i\n", i->m_Index, i->m_Dim );
+ #endif
+
+ buffer += 4*sizeof(float);
+ }
+ else
+ {
+ // Apply matrix parameters
+ DebugAssert (i->m_ArraySize == 1);
+ int size = *reinterpret_cast<const int*>(buffer); buffer += sizeof(int);
+ Assert (size == 16);
+ const Matrix4x4f* mat = reinterpret_cast<const Matrix4x4f*>(buffer);
+ if (i->m_RowCount == 3 && i->m_ColCount == 3)
+ {
+ Matrix3x3f m33 = Matrix3x3f(*mat);
+ GLES_CHK(glUniformMatrix3fv (i->m_Index, 1, GL_FALSE, m33.GetPtr()));
+ }
+ else
+ {
+ const float *ptr = mat->GetPtr ();
+ GLES_CHK(glUniformMatrix4fv (i->m_Index, 1, GL_FALSE, ptr));
+ }
+ #if DEBUG_GLSL_BINDINGS
+ ;;printf_console(" matrix %i (%s)\n", i->m_Index, i->m_Name.GetName() );
+ #endif
+ buffer += size * sizeof(float);
+ }
+ }
+
+ // Apply textures
+ int j = 0;
+ const GpuProgramParameters::TextureParameterList& textureParams = params.GetTextureParams();
+ GpuProgramParameters::TextureParameterList::const_iterator textureParamsEnd = textureParams.end();
+ for( GpuProgramParameters::TextureParameterList::const_iterator i = textureParams.begin(); i != textureParamsEnd; ++i )
+ {
+ const TexEnvData* texdata = reinterpret_cast<const TexEnvData*>(buffer);
+ #if DEBUG_GLSL_BINDINGS
+ ;;printf_console(" sampler %i to unit %i (%s) id=%i dim=%i\n", t.m_Index, j, t.m_Name.GetName(), tex->GetActualTextureID().m_ID, tex->GetTexDim() );
+ #endif
+ ApplyTexEnvData (j, j, *texdata);
+ ++j;
+ buffer += sizeof(TexEnvData);
+ }
+
+ // Fog parameters if needed
+ if (index > 0)
+ {
+ if (m_FogColorIndex[fog.mode] >= 0)
+ CachedUniform4(m_UniformCache+index, m_FogColorIndex[fog.mode], fog.color.GetPtr());
+
+ Vector4f params(
+ fog.density * 1.2011224087f,// density / sqrt(ln(2))
+ fog.density * 1.4426950408f, // density / ln(2)
+ 0.0f,
+ 0.0f
+ );
+
+ if (fog.mode == kFogLinear)
+ {
+ float diff = fog.end - fog.start;
+ float invDiff = Abs(diff) > 0.0001f ? 1.0f/diff : 0.0f;
+ params[2] = -invDiff;
+ params[3] = fog.end * invDiff;
+ }
+
+ if (m_FogParamsIndex[fog.mode] >= 0)
+ CachedUniform4(m_UniformCache+index, m_FogParamsIndex[fog.mode], params.GetPtr());
+ }
+
+ GLESAssert();
+
+ return index;
+}
+
+
+// --------------------------------------------------------------------------
+
+const ShaderLab::FastPropertyName FixedFunctionProgramGLES30::kSLPropTransformBlock = ShaderLab::Property("_ffTransform");
+const ShaderLab::FastPropertyName FixedFunctionProgramGLES30::kSLPropUVTransformBlock = ShaderLab::Property("_ffUVTransform");
+
+
+FixedFunctionProgramGLES30::FixedFunctionProgramGLES30(GLShaderID vertexShader, GLShaderID fragmentShader)
+: m_GLSLProgram(0)
+, m_GLSLVertexShader(vertexShader)
+, m_GLSLFragmentShader(fragmentShader)
+{
+ m_GLSLProgram = Create(m_GLSLVertexShader, m_GLSLFragmentShader);
+}
+
+FixedFunctionProgramGLES30::~FixedFunctionProgramGLES30 ()
+{
+ // NOTE: do not delete vertex/fragment shaders; they can be shared between multiple programs
+ // and are deleted in ClearFixedFunctionPrograms
+ if (m_GLSLProgram != 0 ) // only delete valid programs
+ GLES_CHK(glDeleteProgram(m_GLSLProgram));
+
+ m_UniformCache.Destroy();
+}
+
+GLShaderID FixedFunctionProgramGLES30::Create(GLShaderID vertexShader, GLShaderID fragmentShader)
+{
+ GLESAssert(); // Clear any GL errors
+
+ GLuint program;
+ GLES_CHK(program = glCreateProgram());
+
+ for (int i = 0; i < kAttribLookupTableSize; i++)
+ {
+ if (s_GLESVertexComponents[i] < gGraphicsCaps.gles30.maxAttributes && s_GLESVertexComponents[i] >= 0)
+ GLES_CHK(glBindAttribLocation(program, s_GLESVertexComponents[i], s_GLSLESAttributes[i]));
+ }
+
+ GLES_CHK(glAttachShader(program, m_GLSLVertexShader));
+ GLES_CHK(glAttachShader(program, m_GLSLFragmentShader));
+
+ //We must link only after binding the attributes
+ GLES_CHK(glLinkProgram(program));
+
+ int linked = 0;
+ GLES_CHK(glGetProgramiv(program, GL_LINK_STATUS, &linked));
+ if (linked == 0)
+ {
+ ParseGlslErrors(program, kErrorLinkProgram);
+ GLES_CHK(glDeleteProgram(program));
+ return 0;
+ }
+
+ // Figure out the uniforms
+ int activeUniforms;
+ int activeUniformBlocks;
+
+ char name[1024];
+ GLenum type;
+ int size = 0,
+ length = 0,
+ bufSize = sizeof(name);
+
+ // Fetch texture stage samplers. Bind GLSL uniform to the OpenGL texture unit
+ // just once, cause it never changes after that for this program; and could cause
+ // internal shader recompiles when changing them.
+ DBG_LOG_GLES30("GLSL: apply fixed-function program id=%i\n", program);
+ GLSLUseProgramGLES30 (program);
+
+ for (int i = 0; i < kMaxSupportedTextureUnitsGLES; i++)
+ {
+ std::string samplerName = Format("u_sampler%d", i);
+ GLint uniformNumber;
+ GLES_CHK(uniformNumber = glGetUniformLocation(program, samplerName.c_str()));
+
+ if (uniformNumber != -1)
+ {
+ GLES_CHK(glUniform1i (uniformNumber, i));
+ DBG_GLSL_BINDINGS_GLES30(" FFsampler: %s nr=%d", samplerName.c_str(), uniformNumber);
+ }
+ }
+
+ // uniform blocks
+ GLES_CHK(glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &activeUniformBlocks));
+ m_Params.GetConstantBuffers().clear();
+ m_Params.GetConstantBuffers().reserve(activeUniformBlocks);
+ for (int i = 0; i < activeUniformBlocks; ++i)
+ {
+ GLES_CHK(glGetActiveUniformBlockName(program, i, bufSize, &length, name));
+ GLES_CHK(glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_DATA_SIZE, &size));
+
+ GpuProgramParameters::ConstantBuffer cb;
+ cb.m_Name = ShaderLab::Property(name);
+ cb.m_Size = size;
+ cb.m_BindIndex = i;
+
+ GLES_CHK(glUniformBlockBinding(program, i, i));
+
+ m_Params.GetConstantBuffers().push_back(cb);
+ GetRealGfxDevice().SetConstantBufferInfo(cb.m_Name.index, cb.m_Size);
+ }
+
+ // fetch generic params
+ GLES_CHK(glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &activeUniforms));
+ for(int i=0; i < activeUniforms; i++)
+ {
+ int uniformNumber;
+ int cbIndex = -1;
+
+ GLES_CHK(glGetActiveUniform(program, i, bufSize, &length, &size, &type, name));
+ GLES_CHK(uniformNumber = glGetUniformLocation(program, name));
+
+ const char* glslName = GetGLSLES3PropertyNameRemap(name);
+ const char* unityName = glslName ? glslName : name;
+
+ if (uniformNumber == GL_INVALID_INDEX)
+ {
+ // in a uniform block
+ GLES_CHK(glGetActiveUniformsiv(program, 1, (GLuint*)&i, GL_UNIFORM_OFFSET, &uniformNumber));
+ GLES_CHK(glGetActiveUniformsiv(program, 1, (GLuint*)&i, GL_UNIFORM_BLOCK_INDEX, &cbIndex));
+ }
+
+ if(type == GL_FLOAT) {
+ m_Params.AddVectorParam(uniformNumber, kShaderParamFloat, 1, unityName, cbIndex, NULL);
+ }
+ else if(type == GL_FLOAT_VEC2) {
+ m_Params.AddVectorParam(uniformNumber, kShaderParamFloat, 2, unityName, cbIndex, NULL);
+ }
+ else if(type == GL_FLOAT_VEC3) {
+ m_Params.AddVectorParam(uniformNumber, kShaderParamFloat, 3, unityName, cbIndex, NULL);
+ }
+ else if(type == GL_FLOAT_VEC4) {
+ m_Params.AddVectorParam(uniformNumber, kShaderParamFloat, 4, unityName, cbIndex, NULL);
+ }
+ else if(type == GL_FLOAT_MAT4) {
+ m_Params.AddMatrixParam(uniformNumber, unityName, 4, 4, cbIndex, NULL);
+ }
+ else if(type == GL_FLOAT_MAT3) {
+ m_Params.AddMatrixParam(uniformNumber, unityName, 3, 3, cbIndex, NULL);
+ }
+ else if(type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE) {
+ }
+ else {
+ AssertString( "Unrecognized GLSL uniform type" );
+ }
+ DBG_GLSL_BINDINGS_GLES30(" FFuniform: %s nr=%d type=%d", unityName, uniformNumber, type);
+ }
+
+ m_UniformCache.Create(&m_Params, -1,-1);
+
+ GLESAssert();
+ return program;
+}
+
+void FixedFunctionProgramGLES30::ApplyFFGpuProgram(const BuiltinShaderParamValues& values, ConstantBuffersGLES30& cbs) const
+{
+ DBG_LOG_GLES30("FixedFunctionProgramGLES30::ApplyFFGpuProgram()");
+
+ DBG_LOG_GLES30("GLSL: apply fixed-function program id=%i\n", m_GLSLProgram);
+ GLSLUseProgramGLES30 (m_GLSLProgram);
+
+ cbs.ResetBinds();
+
+ // Apply float/vector parameters
+ const GpuProgramParameters::ValueParameterArray& valueParams = m_Params.GetValueParams();
+ GpuProgramParameters::ValueParameterArray::const_iterator valueParamsEnd = valueParams.end();
+ for( GpuProgramParameters::ValueParameterArray::const_iterator i = valueParams.begin(); i != valueParamsEnd; ++i )
+ {
+ if(i->m_RowCount == 1 && i->m_ArraySize == 1)
+ {
+ UniformCacheGLES30* cache = &m_UniformCache;
+ const float * val = values.GetVectorParam((BuiltinShaderVectorParam)i->m_Name.BuiltinIndex()).GetPtr();
+
+ switch (i->m_ColCount)
+ {
+ case 1: CachedUniform1(cache, i->m_Index, val); break;
+ case 2: CachedUniform2(cache, i->m_Index, val); break;
+ case 3: CachedUniform3(cache, i->m_Index, val); break;
+ case 4: CachedUniform4(cache, i->m_Index, val); break;
+ default: break;
+ }
+ }
+ else
+ {
+ // Apply matrix parameters
+ DebugAssert (i->m_ArraySize == 1);
+ int matSize;
+ const Matrix4x4f& mat = values.GetMatrixParam((BuiltinShaderMatrixParam)i->m_Name.BuiltinIndex());
+ if (i->m_RowCount == 3 && i->m_ColCount == 3)
+ {
+ Matrix3x3f m33 = Matrix3x3f(mat);
+ GLES_CHK(glUniformMatrix3fv (i->m_Index, 1, GL_FALSE, m33.GetPtr()));
+ }
+ else
+ {
+ const float *ptr = mat.GetPtr ();
+ GLES_CHK(glUniformMatrix4fv (i->m_Index, 1, GL_FALSE, ptr));
+ }
+ DBG_GLSL_BINDINGS_GLES30(" FFmatrix %i (%s)", i->m_Index, i->m_Name.GetName() );
+ }
+ }
+
+ GLESAssert();
+}
+
+
+#if UNITY_ANDROID || UNITY_BLACKBERRY
+//-----------------------------------------------------------------------------
+// md5 internals are extracted from External/MurmurHash/md5.cpp
+ struct
+ md5_context
+ {
+ unsigned long total[2];
+ unsigned long state[4];
+ unsigned char buffer[64];
+ unsigned char ipad[64];
+ unsigned char opad[64];
+ };
+
+ extern void md5_starts(md5_context* ctx);
+ extern void md5_update(md5_context* ctx, unsigned char* input, int ilen);
+ extern void md5_finish(md5_context* ctx, unsigned char output[16]);
+#endif
+
+
+static void GetCachedBinaryName(const std::string& vprog, const std::string& fshader, char filename[33])
+{
+// we have caching only on android, so no need to worry (at least for now) about md5
+#if UNITY_ANDROID || UNITY_BLACKBERRY
+
+ unsigned char hash[16] = {0};
+
+ md5_context ctx;
+ md5_starts(&ctx);
+ md5_update(&ctx, (unsigned char*)vprog.c_str(), vprog.length());
+ md5_update(&ctx, (unsigned char*)fshader.c_str(), fshader.length());
+ md5_finish(&ctx, hash);
+
+ BytesToHexString(hash, 16, filename);
+
+#else
+
+ (void)vprog;
+ (void)fshader;
+ ::memset(filename, '1', 33);
+
+#endif
+}
+
+
+#endif // GFX_SUPPORTS_OPENGLES30
diff --git a/Runtime/GfxDevice/opengles30/GpuProgramsGLES30.h b/Runtime/GfxDevice/opengles30/GpuProgramsGLES30.h
new file mode 100644
index 0000000..73bd469
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/GpuProgramsGLES30.h
@@ -0,0 +1,88 @@
+#ifndef GPUPROGRAMSGLES30_H
+#define GPUPROGRAMSGLES30_H
+
+
+#if !GFX_SUPPORTS_OPENGLES30 && !GFX_SUPPORTS_OPENGLES
+#error "Should not include GpuProgramsGLES30 on this platform"
+#endif
+
+
+#include "Runtime/GfxDevice/GpuProgram.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "IncludesGLES30.h"
+#include "GpuProgramsGLES30_UniformCache.h"
+
+class ConstantBuffersGLES30;
+
+class GlslGpuProgramGLES30 : public GpuProgramGL
+{
+public:
+ GlslGpuProgramGLES30 (const std::string& source, CreateGpuProgramOutput& output);
+
+ ~GlslGpuProgramGLES30();
+
+ virtual void ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer) { Assert(!"Should not be used"); }
+
+ // Returns the permutation index used
+ int ApplyGpuProgramES30 (const GpuProgramParameters& params, const UInt8 *buffer);
+
+ static bool InitBinaryShadersSupport();
+
+
+ UniformCacheGLES30 m_UniformCache[kFogModeCount];
+
+ static bool CompileGlslShader(GLShaderID shader, const char* source);
+ int GetGLProgram (FogMode fog, GpuProgramParameters& outParams, ChannelAssigns& channels);
+
+private:
+
+ static void FillParams (unsigned int programID, GpuProgramParameters& params, PropertyNamesSet* outNames);
+
+ bool Create (const std::string& source, ChannelAssigns& channels);
+
+ bool CompileProgram(unsigned index, const std::string& vprog, const std::string& fshader, ChannelAssigns& channels);
+
+private:
+ std::string m_VertexShaderSourceForFog;
+ GLShaderID m_GLSLVertexShader[kFogModeCount];
+ GLShaderID m_GLSLFragmentShader[kFogModeCount];
+ int m_FogColorIndex[kFogModeCount];
+ int m_FogParamsIndex[kFogModeCount];
+ bool m_FogFailed[kFogModeCount];
+
+ static std::string _CachePath;
+ static glGetProgramBinaryOESFunc _glGetProgramBinaryOES;
+ static glProgramBinaryOESFunc _glProgramBinaryOES;
+};
+
+
+class FixedFunctionProgramGLES30
+{
+public:
+ static const ShaderLab::FastPropertyName kSLPropTransformBlock;
+ static const ShaderLab::FastPropertyName kSLPropUVTransformBlock;
+
+public:
+ FixedFunctionProgramGLES30(GLShaderID vertexShader, GLShaderID fragmentShader);
+ ~FixedFunctionProgramGLES30();
+
+ void ApplyFFGpuProgram(const BuiltinShaderParamValues& values, ConstantBuffersGLES30& cbs) const;
+
+ const BuiltinShaderParamIndices& GetBuiltinParams() const { return m_Params.GetBuiltinParams(); }
+ const GpuProgramParameters::ConstantBufferList& GetConstantBuffers() const { return m_Params.GetConstantBuffers(); }
+
+ mutable UniformCacheGLES30 m_UniformCache;
+
+protected:
+ GLShaderID Create(GLShaderID vertexShader, GLShaderID fragmentShader);
+
+private:
+ GLShaderID m_GLSLProgram;
+ GLShaderID m_GLSLVertexShader, m_GLSLFragmentShader;
+
+ GpuProgramParameters m_Params;
+};
+
+
+#endif
diff --git a/Runtime/GfxDevice/opengles30/GpuProgramsGLES30_UniformCache.cpp b/Runtime/GfxDevice/opengles30/GpuProgramsGLES30_UniformCache.cpp
new file mode 100644
index 0000000..235ff7a
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/GpuProgramsGLES30_UniformCache.cpp
@@ -0,0 +1,62 @@
+#include "UnityPrefix.h"
+#if !GFX_SUPPORTS_OPENGLES30
+#error "Should not include GpuProgramsGLES30 on this platform"
+#endif
+
+#include "GpuProgramsGLES30_UniformCache.h"
+
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/GfxDevice/GpuProgram.h"
+#include "IncludesGLES30.h"
+#include "AssertGLES30.h"
+
+
+void UniformCacheGLES30::Create(const GpuProgramParameters* params, int fogParamsIndex, int fogColorIndex)
+{
+ int lastUsedUniform = -1;
+
+ // we will track only float/vector uniforms
+ GpuProgramParameters::ValueParameterArray::const_iterator paramI = params->GetValueParams().begin();
+ GpuProgramParameters::ValueParameterArray::const_iterator paramEnd = params->GetValueParams().end();
+ while(paramI != paramEnd)
+ {
+ if(paramI->m_RowCount == 1 && paramI->m_ArraySize == 1 && paramI->m_Index > lastUsedUniform)
+ lastUsedUniform = paramI->m_Index;
+
+ ++paramI;
+ }
+
+ const BuiltinShaderParamIndices& builtinParam = params->GetBuiltinParams();
+ for(unsigned i = 0 ; i < kShaderInstanceVecCount ; ++i)
+ {
+ if(builtinParam.vec[i].gpuIndex > lastUsedUniform)
+ lastUsedUniform = builtinParam.vec[i].gpuIndex;
+ }
+
+ if(fogParamsIndex > lastUsedUniform) lastUsedUniform = fogParamsIndex;
+ if(fogColorIndex > lastUsedUniform) lastUsedUniform = fogColorIndex;
+
+ count = lastUsedUniform + 1;
+ uniform = (float*)UNITY_MALLOC_ALIGNED(kMemShader, count*4 * sizeof(float), 16);
+ memset(uniform, 0xff /* NaN */, count*4 * sizeof(float));
+}
+
+void UniformCacheGLES30::Destroy()
+{
+ count = 0;
+
+ UNITY_FREE(kMemShader, uniform);
+ uniform = 0;
+}
+
+#define CACHED_UNIFORM_IMPL(Count) \
+void CachedUniform##Count(UniformCacheGLES30* cache, int index, const float* val) \
+{ \
+ if(cache->UpdateUniform(index, val, Count)) \
+ GLES_CHK(glUniform##Count##fv(index, 1, val)); \
+} \
+
+CACHED_UNIFORM_IMPL(1);
+CACHED_UNIFORM_IMPL(2);
+CACHED_UNIFORM_IMPL(3);
+CACHED_UNIFORM_IMPL(4);
diff --git a/Runtime/GfxDevice/opengles30/GpuProgramsGLES30_UniformCache.h b/Runtime/GfxDevice/opengles30/GpuProgramsGLES30_UniformCache.h
new file mode 100644
index 0000000..aa0e74b
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/GpuProgramsGLES30_UniformCache.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#if !GFX_SUPPORTS_OPENGLES30
+#error "Should not include GpuProgramsGLES30 on this platform"
+#endif
+
+#include "Runtime/Utilities/LogAssert.h"
+#include <string.h>
+
+class GpuProgramParameters;
+
+struct
+UniformCacheGLES30
+{
+ // for gles we must set values per-uniform (not per-registers like in dx)
+ // so there is no real need for dirty tracking.
+ // TODO: do unified impl with dirty tracking if/when we do "everything is an array" in gles
+ float* uniform;
+ unsigned count;
+
+ UniformCacheGLES30() : uniform(0), count(0) {}
+
+ // we will pre-alloc memory. Fog params are handled differently (not added to gpu params).
+ // TODO: make it more general, int* perhaps, or some struct
+ void Create(const GpuProgramParameters* params, int fogParamsIndex, int fogColorIndex);
+ void Destroy();
+
+ // returns true if you need to update for real
+ bool UpdateUniform(int index, const float* val, unsigned floatCount);
+};
+
+void CachedUniform1(UniformCacheGLES30* cache, int index, const float* val);
+void CachedUniform2(UniformCacheGLES30* cache, int index, const float* val);
+void CachedUniform3(UniformCacheGLES30* cache, int index, const float* val);
+void CachedUniform4(UniformCacheGLES30* cache, int index, const float* val);
+
+
+inline bool UniformCacheGLES30::UpdateUniform(int index, const float* val, unsigned floatCount)
+{
+ Assert(index < count);
+ const unsigned mem_sz = floatCount*sizeof(float);
+
+ float* target = uniform + 4*index;
+ if(::memcmp(target, val, mem_sz))
+ {
+ ::memcpy(target, val, mem_sz);
+ return true;
+ }
+ return false;
+}
diff --git a/Runtime/GfxDevice/opengles30/GpuPropertiesGLES30.cpp b/Runtime/GfxDevice/opengles30/GpuPropertiesGLES30.cpp
new file mode 100644
index 0000000..ab5c8a2
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/GpuPropertiesGLES30.cpp
@@ -0,0 +1,70 @@
+#include "UnityPrefix.h"
+#include "GpuPropertiesGLES30.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/GfxDevice/BuiltinShaderParams.h"
+#include "Runtime/GfxDevice/BuiltinShaderParamsNames.h"
+
+#if GFX_SUPPORTS_OPENGLES30
+
+struct GLSLESProperty
+{
+ GLSLESProperty(const char* _glName, const char* _glesName) : glName(_glName), unityName(_glesName) { }
+ const char* glName;
+ const char* unityName;
+};
+
+#define DEF_MAT_INTERNAL(name, builtin) GLSLESProperty(name, GetShaderInstanceMatrixParamName(builtin))
+#define DEF_MAT_BUILTIN(name, builtin) GLSLESProperty(name, GetBuiltinMatrixParamName(builtin))
+#define BIND_VEC_BUILTIN(name, builtin) GLSLESProperty(name, GetBuiltinVectorParamName(builtin))
+
+
+static const GLSLESProperty kglslesProperties[] =
+{
+ DEF_MAT_BUILTIN("gl_ProjectionMatrix", kShaderMatProj),
+ DEF_MAT_INTERNAL("gl_NormalMatrix", kShaderInstanceMatNormalMatrix),
+ DEF_MAT_INTERNAL("gl_ModelViewProjectionMatrix", kShaderInstanceMatMVP),
+ DEF_MAT_INTERNAL("gl_ModelViewMatrixTranspose", kShaderInstanceMatTransMV),
+ DEF_MAT_INTERNAL("gl_ModelViewMatrixInverseTranspose", kShaderInstanceMatInvTransMV),
+ DEF_MAT_INTERNAL("gl_ModelViewMatrix", kShaderInstanceMatMV),
+
+ DEF_MAT_INTERNAL("gl_TextureMatrix0", kShaderInstanceMatTexture0),
+ DEF_MAT_INTERNAL("gl_TextureMatrix1", kShaderInstanceMatTexture1),
+ DEF_MAT_INTERNAL("gl_TextureMatrix2", kShaderInstanceMatTexture2),
+ DEF_MAT_INTERNAL("gl_TextureMatrix3", kShaderInstanceMatTexture3),
+ DEF_MAT_INTERNAL("gl_TextureMatrix4", kShaderInstanceMatTexture4),
+ DEF_MAT_INTERNAL("gl_TextureMatrix5", kShaderInstanceMatTexture5),
+ DEF_MAT_INTERNAL("gl_TextureMatrix6", kShaderInstanceMatTexture6),
+ DEF_MAT_INTERNAL("gl_TextureMatrix7", kShaderInstanceMatTexture7),
+
+ BIND_VEC_BUILTIN("_glesLightSource[0].diffuse", kShaderVecLight0Diffuse),
+ BIND_VEC_BUILTIN("_glesLightSource[1].diffuse", kShaderVecLight1Diffuse),
+ BIND_VEC_BUILTIN("_glesLightSource[2].diffuse", kShaderVecLight2Diffuse),
+ BIND_VEC_BUILTIN("_glesLightSource[3].diffuse", kShaderVecLight3Diffuse),
+ BIND_VEC_BUILTIN("_glesLightSource[0].position", kShaderVecLight0Position),
+ BIND_VEC_BUILTIN("_glesLightSource[1].position", kShaderVecLight1Position),
+ BIND_VEC_BUILTIN("_glesLightSource[2].position", kShaderVecLight2Position),
+ BIND_VEC_BUILTIN("_glesLightSource[3].position", kShaderVecLight3Position),
+ BIND_VEC_BUILTIN("_glesLightSource[0].spotDirection", kShaderVecLight0SpotDirection),
+ BIND_VEC_BUILTIN("_glesLightSource[1].spotDirection", kShaderVecLight1SpotDirection),
+ BIND_VEC_BUILTIN("_glesLightSource[2].spotDirection", kShaderVecLight2SpotDirection),
+ BIND_VEC_BUILTIN("_glesLightSource[3].spotDirection", kShaderVecLight3SpotDirection),
+ BIND_VEC_BUILTIN("_glesLightSource[0].atten", kShaderVecLight0Atten),
+ BIND_VEC_BUILTIN("_glesLightSource[1].atten", kShaderVecLight1Atten),
+ BIND_VEC_BUILTIN("_glesLightSource[2].atten", kShaderVecLight2Atten),
+ BIND_VEC_BUILTIN("_glesLightSource[3].atten", kShaderVecLight3Atten),
+ BIND_VEC_BUILTIN("_glesLightModel.ambient", kShaderVecLightModelAmbient),
+};
+
+
+const char* GetGLSLES3PropertyNameRemap (const char* name)
+{
+ for (int i = 0; i < ARRAY_SIZE(kglslesProperties); i++)
+ {
+ const GLSLESProperty& prop = kglslesProperties[i];
+ if (strcmp(name, prop.glName) == 0)
+ return prop.unityName;
+ }
+ return NULL;
+}
+
+#endif // GFX_SUPPORTS_OPENGLES30
diff --git a/Runtime/GfxDevice/opengles30/GpuPropertiesGLES30.h b/Runtime/GfxDevice/opengles30/GpuPropertiesGLES30.h
new file mode 100644
index 0000000..86b980e
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/GpuPropertiesGLES30.h
@@ -0,0 +1,3 @@
+#pragma once
+
+const char* GetGLSLES3PropertyNameRemap (const char* name);
diff --git a/Runtime/GfxDevice/opengles30/IncludesGLES30.h b/Runtime/GfxDevice/opengles30/IncludesGLES30.h
new file mode 100644
index 0000000..260bea5
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/IncludesGLES30.h
@@ -0,0 +1 @@
+#include "Runtime/GfxDevice/opengles/IncludesGLES.h" \ No newline at end of file
diff --git a/Runtime/GfxDevice/opengles30/RenderTextureGLES30.cpp b/Runtime/GfxDevice/opengles30/RenderTextureGLES30.cpp
new file mode 100644
index 0000000..7f5a92f
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/RenderTextureGLES30.cpp
@@ -0,0 +1,551 @@
+#include "UnityPrefix.h"
+#include "RenderTextureGLES30.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "IncludesGLES30.h"
+#include "AssertGLES30.h"
+#include "DebugGLES30.h"
+#include "TextureIdMapGLES30.h"
+#include "UtilsGLES30.h"
+
+#if UNITY_ANDROID
+ #include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+#endif
+
+#if 1
+ #define DBG_LOG_RT_GLES30(...) {}
+#else
+ #define DBG_LOG_RT_GLES30(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+#endif
+
+#if GFX_SUPPORTS_OPENGLES30
+
+namespace
+{
+
+static const UInt32 kCubeFacesES3[] =
+{
+ GL_TEXTURE_CUBE_MAP_POSITIVE_X,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
+ GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
+ GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
+};
+
+inline bool IsDepthStencilFormat (UInt32 format)
+{
+ switch (format)
+ {
+ case GL_DEPTH24_STENCIL8:
+ case GL_DEPTH32F_STENCIL8:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+const char* GetFBOStatusName (UInt32 status)
+{
+ switch (status)
+ {
+ case GL_FRAMEBUFFER_COMPLETE: return "COMPLETE";
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return "INCOMPLETE_ATTACHMENT";
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return "INCOMPLETE_MISSING_ATTACHMENT";
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: return "INCOMPLETE_DIMENSIONS";
+ case GL_FRAMEBUFFER_UNSUPPORTED: return "UNSUPPORTED";
+ default: return "unknown error";
+ }
+}
+
+} // anonymous
+
+// RenderSurfaceGLES30
+RenderSurfaceGLES30::RenderSurfaceGLES30 (Type type, UInt32 format, int w, int h, int numSamples)
+ : m_type (type)
+ , m_format (format)
+ , m_flags (0)
+{
+ RenderSurfaceBase_Init(*this);
+ width = w;
+ height = h;
+ samples = numSamples;
+}
+
+// RenderTexture2DGLES30
+
+RenderTexture2DGLES30::RenderTexture2DGLES30 (TextureID texID, UInt32 format, int w, int h)
+ : RenderSurfaceGLES30 (kTypeTexture2D, format, w, h, 1)
+{
+ textureID = texID;
+
+ TransferFormatGLES30 transferFmt = GetTransferFormatGLES30(format);
+ GLuint glTexID = TextureIdMapGLES30_QueryOrCreate(textureID);
+
+ Assert(glTexID != 0);
+
+ GetRealGfxDevice().SetTexture(kShaderFragment, 0, 0, textureID, kTexDim2D, std::numeric_limits<float>::infinity());
+ GLES_CHK(glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, transferFmt.format, transferFmt.dataType, 0));
+}
+
+RenderTexture2DGLES30::~RenderTexture2DGLES30 (void)
+{
+ // \todo [2013-04-29 pyry] Set texture storage to null?
+}
+
+void RenderTexture2DGLES30::AttachColor (int ndx, CubemapFace face)
+{
+ Assert(face == kCubeFaceUnknown);
+ GLES_CHK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+ndx, GL_TEXTURE_2D, GetGLTextureID(), 0));
+}
+
+void RenderTexture2DGLES30::AttachDepthStencil (CubemapFace face)
+{
+ // \note Using GL_DEPTH_STENCIL_ATTACHMENT would eliminate one more call, but unfortunately
+ // that is broken at least on ARM GLES3 EMU.
+ Assert(face == kCubeFaceUnknown);
+
+ GLES_CHK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, GetGLTextureID(), 0));
+
+ if (IsDepthStencilFormat(m_format))
+ GLES_CHK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, GetGLTextureID(), 0));
+}
+
+// RenderTextureCubeGLES30
+
+RenderTextureCubeGLES30::RenderTextureCubeGLES30 (TextureID texID, UInt32 format, int w, int h)
+ : RenderSurfaceGLES30 (kTypeTextureCube, format, w, h, 1)
+{
+ textureID = texID;
+
+ TransferFormatGLES30 transferFmt = GetTransferFormatGLES30(format);
+ GLuint glTexID = TextureIdMapGLES30_QueryOrCreate(textureID);
+
+ Assert(glTexID != 0);
+
+ GetRealGfxDevice().SetTexture(kShaderFragment, 0, 0, textureID, kTexDimCUBE, std::numeric_limits<float>::infinity());
+
+ for (int ndx = 0; ndx < 6; ndx++)
+ GLES_CHK(glTexImage2D(kCubeFacesES3[ndx], 0, format, width, height, 0, transferFmt.format, transferFmt.dataType, 0));
+}
+
+RenderTextureCubeGLES30::~RenderTextureCubeGLES30 (void)
+{
+ // \todo [2013-04-29 pyry] Set texture storage to null?
+}
+
+void RenderTextureCubeGLES30::AttachColor (int ndx, CubemapFace face)
+{
+ const int faceIndex = clamp<int>(face,0,5); // can be passed -1 when restoring from previous RT
+ GLES_CHK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+ndx, kCubeFacesES3[faceIndex], GetGLTextureID(), 0));
+}
+
+void RenderTextureCubeGLES30::AttachDepthStencil (CubemapFace face)
+{
+ const int faceIndex = clamp<int>(face,0,5); // can be passed -1 when restoring from previous RT
+
+ GLES_CHK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, kCubeFacesES3[faceIndex], GetGLTextureID(), 0));
+
+ if (IsDepthStencilFormat(m_format))
+ GLES_CHK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, kCubeFacesES3[faceIndex], GetGLTextureID(), 0));
+}
+
+// RenderBufferGLES30
+
+RenderBufferGLES30::RenderBufferGLES30 (UInt32 format, int w, int h, int numSamples)
+ : RenderSurfaceGLES30 (kTypeRenderBuffer, format, w, h, numSamples)
+ , m_bufferID (0)
+{
+ glGenRenderbuffers(1, (GLuint*)&m_bufferID);
+ glBindRenderbuffer(GL_RENDERBUFFER, m_bufferID);
+
+ if (numSamples > 1)
+ GLES_CHK(glRenderbufferStorageMultisample(GL_RENDERBUFFER, numSamples, format, width, height));
+ else
+ GLES_CHK(glRenderbufferStorage(GL_RENDERBUFFER, format, width, height));
+}
+
+RenderBufferGLES30::~RenderBufferGLES30 (void)
+{
+ if (m_bufferID)
+ glDeleteRenderbuffers(1, (GLuint*)&m_bufferID);
+}
+
+void RenderBufferGLES30::AttachColor (int ndx, CubemapFace face)
+{
+ Assert(face == kCubeFaceUnknown);
+ GLES_CHK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+ndx, GL_RENDERBUFFER, m_bufferID));
+}
+
+void RenderBufferGLES30::AttachDepthStencil (CubemapFace face)
+{
+ Assert(face == kCubeFaceUnknown);
+
+ GLES_CHK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_bufferID));
+
+ if (IsDepthStencilFormat(m_format))
+ GLES_CHK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_bufferID));
+}
+
+void RenderBufferGLES30::Disown (void)
+{
+ m_bufferID = 0;
+}
+
+// RenderBufferCubeGLES30
+
+RenderBufferCubeGLES30::RenderBufferCubeGLES30 (UInt32 format, int w, int h, int numSamples)
+ : RenderSurfaceGLES30 (kTypeRenderBufferCube, format, w, h, numSamples)
+{
+ memset(&m_buffers[0], 0, sizeof(m_buffers));
+
+ for (int i = 0; i < 6; i++)
+ m_buffers[i] = new RenderBufferGLES30(format, width, height, numSamples);
+}
+
+RenderBufferCubeGLES30::~RenderBufferCubeGLES30 (void)
+{
+ for (int i = 0; i < 6; i++)
+ delete m_buffers[i];
+}
+
+void RenderBufferCubeGLES30::AttachColor (int ndx, CubemapFace face)
+{
+ const int faceIndex = clamp<int>(face,0,5); // can be passed -1 when restoring from previous RT
+ m_buffers[faceIndex]->AttachColor(ndx, kCubeFaceUnknown);
+}
+
+void RenderBufferCubeGLES30::AttachDepthStencil (CubemapFace face)
+{
+ const int faceIndex = clamp<int>(face,0,5); // can be passed -1 when restoring from previous RT
+ m_buffers[faceIndex]->AttachDepthStencil(kCubeFaceUnknown);
+}
+
+// FramebufferAttachmentsGLES30
+
+FramebufferAttachmentsGLES30::FramebufferAttachmentsGLES30 (int numColorAttachments, RenderSurfaceGLES30* colorAttachments, RenderSurfaceGLES30* depthStencilAttachment, CubemapFace face)
+ : numColorAttachments (numColorAttachments)
+ , depthStencil (depthStencilAttachment)
+ , cubemapFace (face)
+{
+ for (int ndx = 0; ndx < numColorAttachments; ndx++)
+ color[ndx] = &colorAttachments[ndx];
+
+ for (int ndx = numColorAttachments; ndx < kMaxColorAttachments; ndx++)
+ color[ndx] = 0;
+}
+
+FramebufferAttachmentsGLES30::FramebufferAttachmentsGLES30 (void)
+ : numColorAttachments (0)
+ , depthStencil (0)
+ , cubemapFace (kCubeFaceUnknown)
+{
+ memset(&color[0], 0, sizeof(color));
+}
+
+// FramebufferObjectGLES30
+
+FramebufferObjectGLES30::FramebufferObjectGLES30 (const FramebufferAttachmentsGLES30& attachments)
+ : m_fboID (0)
+ , m_attachments (attachments)
+{
+ GLuint oldFbo = 0;
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&oldFbo);
+
+ glGenFramebuffers(1, (GLuint*)&m_fboID);
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, m_fboID));
+
+ for (int colorNdx = 0; colorNdx < attachments.numColorAttachments; colorNdx++)
+ {
+ if (m_attachments.color[colorNdx] && m_attachments.color[colorNdx]->GetType() != RenderSurfaceGLES30::kTypeDummy)
+ m_attachments.color[colorNdx]->AttachColor(colorNdx, m_attachments.cubemapFace);
+ }
+
+ if (m_attachments.depthStencil)
+ m_attachments.depthStencil->AttachDepthStencil(m_attachments.cubemapFace);
+
+ UInt32 status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE)
+ {
+ const char* statusName = GetFBOStatusName(status);
+ ErrorStringMsg("Framebuffer is not complete: %s", statusName);
+ Assert(status == GL_FRAMEBUFFER_COMPLETE);
+ }
+
+ // \todo [2013-04-30 pyry] We should really fail object construction if fbo is not complete.
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, oldFbo));
+}
+
+FramebufferObjectGLES30::~FramebufferObjectGLES30 (void)
+{
+ if (m_fboID)
+ glDeleteFramebuffers(1, (GLuint*)&m_fboID);
+}
+
+void FramebufferObjectGLES30::Disown (void)
+{
+ m_fboID = 0;
+}
+
+RenderSurfaceGLES30* FramebufferObjectGLES30::GetColorAttachment (int ndx)
+{
+ Assert(0 <= ndx && ndx <= FramebufferAttachmentsGLES30::kMaxColorAttachments);
+ return m_attachments.color[ndx];
+}
+
+const RenderSurfaceGLES30* FramebufferObjectGLES30::GetColorAttachment (int ndx) const
+{
+ Assert(0 <= ndx && ndx <= FramebufferAttachmentsGLES30::kMaxColorAttachments);
+ return m_attachments.color[ndx];
+}
+
+RenderSurfaceGLES30* FramebufferObjectGLES30::GetDepthStencilAttachment (void)
+{
+ return m_attachments.depthStencil;
+}
+
+const RenderSurfaceGLES30* FramebufferObjectGLES30::GetDepthStencilAttachment (void) const
+{
+ return m_attachments.depthStencil;
+}
+
+// FramebufferObjectManagerGLES30
+
+bool CompareFramebufferAttachmentsGLES30::operator() (const FramebufferAttachmentsGLES30* a, const FramebufferAttachmentsGLES30* b) const
+{
+ if (a->numColorAttachments < b->numColorAttachments) return true;
+ else if (a->numColorAttachments > b->numColorAttachments) return false;
+
+ if (a->depthStencil < b->depthStencil) return true;
+ else if (a->depthStencil > b->depthStencil) return false;
+
+ if (a->cubemapFace < b->cubemapFace) return true;
+ else if (a->cubemapFace > b->cubemapFace) return false;
+
+ for (int ndx = 0; ndx < a->numColorAttachments; ndx++)
+ {
+ if (a->color[ndx] < b->color[ndx]) return true;
+ else if (a->color[ndx] > b->color[ndx]) return false;
+ }
+
+ return false; // Equal.
+}
+
+bool CompareRenderBufferParamsGLES30::operator() (const RenderBufferParamsGLES30& a, const RenderBufferParamsGLES30& b) const
+{
+ if (a.format < b.format) return true;
+ else if (a.format > b.format) return false;
+
+ if (a.width < b.width) return true;
+ else if (a.width > b.width) return false;
+
+ if (a.height < b.height) return true;
+ else if (a.height > b.height) return false;
+
+ return false; // Equal.
+}
+
+FramebufferObjectManagerGLES30::FramebufferObjectManagerGLES30 (void)
+{
+}
+
+FramebufferObjectManagerGLES30::~FramebufferObjectManagerGLES30 (void)
+{
+ Clear();
+}
+
+void FramebufferObjectManagerGLES30::InvalidateObjects (void)
+{
+ // Call disown on all FBOs first.
+ for (FramebufferObjectMapGLES30::iterator iter = m_fboMap.begin(); iter != m_fboMap.end(); ++iter)
+ iter->second->Disown();
+
+ for (RenderBufferMapGLES30::iterator iter = m_rbufMap.begin(); iter != m_rbufMap.end(); ++iter)
+ iter->second->Disown();
+
+ // Clear objects.
+ Clear();
+}
+
+void FramebufferObjectManagerGLES30::Clear (void)
+{
+ // \todo [pyry] This actually invalidates keys as well. Is that okay in clear()?
+ for (FramebufferObjectMapGLES30::iterator iter = m_fboMap.begin(); iter != m_fboMap.end(); ++iter)
+ delete iter->second;
+ m_fboMap.clear();
+
+ for (RenderBufferMapGLES30::iterator iter = m_rbufMap.begin(); iter != m_rbufMap.end(); ++iter)
+ delete iter->second;
+ m_rbufMap.clear();
+}
+
+void FramebufferObjectManagerGLES30::InvalidateSurface (const RenderSurfaceGLES30* surface)
+{
+ std::vector<FramebufferObjectGLES30*> deleteFbos;
+
+ for (FramebufferObjectMapGLES30::iterator iter = m_fboMap.begin(); iter != m_fboMap.end(); ++iter)
+ {
+ if (IsInFramebufferAttachmentsGLES30(*iter->first, surface))
+ deleteFbos.push_back(iter->second);
+ }
+
+ for (std::vector<FramebufferObjectGLES30*>::iterator iter = deleteFbos.begin(); iter != deleteFbos.end(); ++iter)
+ {
+ m_fboMap.erase((*iter)->GetAttachments());
+ delete *iter;
+ }
+}
+
+FramebufferObjectGLES30* FramebufferObjectManagerGLES30::GetFramebufferObject (const FramebufferAttachmentsGLES30& attachments)
+{
+ // Try to fetch from cache.
+ {
+ FramebufferObjectMapGLES30::const_iterator fboPos = m_fboMap.find(&attachments);
+ if (fboPos != m_fboMap.end())
+ return fboPos->second;
+ }
+
+ DBG_LOG_RT_GLES30("FramebufferObjectManagerGLES30::GetFramebufferObject(): cache miss, creating FBO");
+
+ // Not found - create a new one and insert.
+ {
+ FramebufferObjectGLES30* fbo = new FramebufferObjectGLES30(attachments);
+ m_fboMap.insert(std::make_pair(fbo->GetAttachments(), fbo));
+ return fbo;
+ }
+}
+
+RenderBufferGLES30* FramebufferObjectManagerGLES30::GetRenderBuffer (UInt32 format, int width, int height)
+{
+ RenderBufferParamsGLES30 params(format, width, height);
+
+ {
+ RenderBufferMapGLES30::const_iterator pos = m_rbufMap.find(params);
+ if (pos != m_rbufMap.end())
+ return pos->second;
+ }
+
+ DBG_LOG_RT_GLES30("FramebufferObjectManagerGLES30::GetRenderBuffer(): cache miss, creating RBO");
+
+ {
+ RenderBufferGLES30* buf = new RenderBufferGLES30(format, width, height, 1);
+ m_rbufMap.insert(std::make_pair(params, buf));
+ return buf;
+ }
+}
+
+// Utilities
+
+bool IsInFramebufferAttachmentsGLES30 (const FramebufferAttachmentsGLES30& attachments, const RenderSurfaceGLES30* renderSurface)
+{
+ for (int ndx = 0; ndx < attachments.numColorAttachments; ndx++)
+ {
+ if (attachments.color[ndx] == renderSurface)
+ return true;
+ }
+
+ if (attachments.depthStencil == renderSurface)
+ return true;
+
+ return false;
+}
+
+void BindFramebufferObjectGLES30 (FramebufferObjectGLES30* fbo)
+{
+ Assert(fbo != 0);
+
+ GLenum drawBuffers [FramebufferAttachmentsGLES30::kMaxColorAttachments];
+ GLenum discardBuffers [FramebufferAttachmentsGLES30::kMaxColorAttachments+1];
+ int drawBufferNdx = 0;
+ int discardBufferNdx = 0;
+
+ for (int ndx = 0; ndx < fbo->GetNumColorAttachments(); ndx++)
+ {
+ RenderSurfaceGLES30* attachment = fbo->GetColorAttachment(ndx);
+
+ if (attachment)
+ drawBuffers[drawBufferNdx++] = GL_COLOR_ATTACHMENT0+ndx;
+
+ if (attachment && (attachment->GetFlags() & RenderSurfaceGLES30::kDiscardOnBind))
+ {
+ if(fbo->GetFboID() == 0)
+ discardBuffers[discardBufferNdx++] = GL_COLOR;
+ else
+ discardBuffers[discardBufferNdx++] = GL_COLOR_ATTACHMENT0+ndx;
+ attachment->SetFlags(attachment->GetFlags() & ~RenderSurfaceGLES30::kDiscardOnBind);
+ }
+ }
+
+ if (fbo->GetDepthStencilAttachment())
+ {
+ RenderSurfaceGLES30* depthStencilAttachment = fbo->GetDepthStencilAttachment();
+ if (depthStencilAttachment->GetFlags() & RenderSurfaceGLES30::kDiscardOnBind && depthStencilAttachment->GetType() != RenderSurfaceGLES30::kTypeDummy)
+ {
+ if(fbo->GetFboID() == 0)
+ {
+ discardBuffers[discardBufferNdx++] = GL_DEPTH;
+ discardBuffers[discardBufferNdx++] = GL_STENCIL;
+ }
+ else
+ {
+ discardBuffers[discardBufferNdx++] = GL_DEPTH_ATTACHMENT;
+ discardBuffers[discardBufferNdx++] = GL_STENCIL_ATTACHMENT;
+ }
+
+ depthStencilAttachment->SetFlags(depthStencilAttachment->GetFlags() & ~RenderSurfaceGLES30::kDiscardOnBind);
+ }
+ }
+
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, fbo->GetFboID()));
+ GLES_CHK(glDrawBuffers(drawBufferNdx, &drawBuffers[0]));
+
+ if (discardBufferNdx > 0)
+ GLES_CHK(glInvalidateFramebuffer(GL_FRAMEBUFFER, discardBufferNdx, &discardBuffers[0]));
+}
+
+void BindDefaultFramebufferGLES30 (void)
+{
+ GLuint defaultDrawBuffer = GL_BACK;
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
+ GLES_CHK(glDrawBuffers(1, &defaultDrawBuffer));
+}
+
+FramebufferObjectGLES30* GetResolveFramebufferObjectGLES30 (FramebufferObjectManagerGLES30* fboManager, UInt32 colorFormat, UInt32 depthStencilFormat, int width, int height)
+{
+ RenderBufferGLES30* colorBuf = colorFormat != 0 ? fboManager->GetRenderBuffer(colorFormat, width, height) : 0;
+ RenderBufferGLES30* depthBuf = depthStencilFormat != 0 ? fboManager->GetRenderBuffer(depthStencilFormat, width, height) : 0;
+ FramebufferAttachmentsGLES30 attachments;
+
+ attachments.color[0] = colorBuf;
+ attachments.depthStencil = depthBuf;
+ attachments.numColorAttachments = colorBuf ? 1 : 0;
+
+ return fboManager->GetFramebufferObject(attachments);
+}
+
+// back buffer
+RenderSurfaceBase* CreateBackBufferColorSurfaceGLES3()
+{
+ DummyRenderSurfaceGLES30* rs = new DummyRenderSurfaceGLES30(0,0);
+ RenderSurfaceBase_InitColor(*rs);
+ rs->backBuffer = true;
+
+ return rs;
+}
+RenderSurfaceBase* CreateBackBufferDepthSurfaceGLES3()
+{
+ DummyRenderSurfaceGLES30* rs = new DummyRenderSurfaceGLES30(0,0);
+ RenderSurfaceBase_InitDepth(*rs);
+ rs->backBuffer = true;
+
+ return rs;
+}
+
+
+#endif // GFX_SUPPORTS_OPENGLES30
diff --git a/Runtime/GfxDevice/opengles30/RenderTextureGLES30.h b/Runtime/GfxDevice/opengles30/RenderTextureGLES30.h
new file mode 100644
index 0000000..adc1898
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/RenderTextureGLES30.h
@@ -0,0 +1,267 @@
+#pragma once
+
+#if GFX_SUPPORTS_OPENGLES30
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/GfxDevice/GfxDeviceConfigure.h"
+#include "Runtime/GfxDevice/GfxDeviceObjects.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/GfxDevice/TextureIdMap.h"
+#include "Runtime/Graphics/RenderSurface.h"
+
+#include <map>
+
+//! Render surface base class (implementations in RenderTextureGLES30 or RenderBufferGLES30)
+class RenderSurfaceGLES30 : public RenderSurfaceBase
+{
+public:
+ enum Type
+ {
+ kTypeTexture2D = 0,
+ kTypeTextureCube,
+ kTypeRenderBuffer,
+ kTypeRenderBufferCube, //!< RenderSurface that contains one RenderBuffer for each cubemap face. Used for MSAA cubemap rendering.
+ kTypeDummy, //!< Dummy surface (no real storage), used when doing depth only.
+ };
+
+ enum Flags
+ {
+ kDiscardOnBind = (1<<0), //!< Discard this attachment when bound as render target next time.
+ };
+
+ virtual ~RenderSurfaceGLES30 (void) {}
+
+ Type GetType (void) const { return m_type; }
+ UInt32 GetFormat (void) const { return m_format; }
+ int GetWidth (void) const { return width; }
+ int GetHeight (void) const { return height; }
+ int GetNumSamples (void) const { return samples; }
+
+ UInt32 GetFlags (void) const { return m_flags; }
+ void SetFlags (UInt32 flags) { m_flags = flags; }
+
+ virtual void AttachColor (int ndx, CubemapFace face) = 0;
+ virtual void AttachDepthStencil (CubemapFace face) = 0;
+
+protected:
+ RenderSurfaceGLES30 (Type type, UInt32 format, int width, int height, int numSamples);
+
+ const Type m_type; //!< RenderSurface type
+ const UInt32 m_format; //!< Internal format
+
+ UInt32 m_flags; //!< Flags.
+
+private:
+ RenderSurfaceGLES30 (const RenderSurfaceGLES30& other); // Not allowed!
+ RenderSurfaceGLES30& operator= (const RenderSurfaceGLES30& other); // Not allowed!
+};
+
+class DummyRenderSurfaceGLES30 : public RenderSurfaceGLES30
+{
+public:
+ DummyRenderSurfaceGLES30 (int width, int height) : RenderSurfaceGLES30(kTypeDummy, 0, width, height, 1) {}
+ ~DummyRenderSurfaceGLES30 (void) {}
+
+ virtual void AttachColor (int ndx, CubemapFace face) {}
+ virtual void AttachDepthStencil (CubemapFace face) {}
+};
+
+class RenderTexture2DGLES30 : public RenderSurfaceGLES30
+{
+public:
+ RenderTexture2DGLES30 (TextureID textureID, UInt32 format, int width, int height);
+ ~RenderTexture2DGLES30 (void);
+
+ virtual void AttachColor (int ndx, CubemapFace face);
+ virtual void AttachDepthStencil (CubemapFace face);
+
+ TextureID GetTextureID (void) const { return textureID; }
+ UInt32 GetGLTextureID (void) const { return (UInt32)TextureIdMap::QueryNativeTexture(textureID); }
+};
+
+class RenderTextureCubeGLES30 : public RenderSurfaceGLES30
+{
+public:
+ RenderTextureCubeGLES30 (TextureID textureID, UInt32 format, int width, int height);
+ ~RenderTextureCubeGLES30 (void);
+
+ virtual void AttachColor (int ndx, CubemapFace face);
+ virtual void AttachDepthStencil (CubemapFace face);
+
+ TextureID GetTextureID (void) const { return textureID; }
+ UInt32 GetGLTextureID (void) const { return (UInt32)TextureIdMap::QueryNativeTexture(textureID); }
+};
+
+class RenderBufferGLES30 : public RenderSurfaceGLES30
+{
+public:
+ RenderBufferGLES30 (UInt32 format, int width, int height, int numSamples);
+ ~RenderBufferGLES30 (void);
+
+ virtual void AttachColor (int ndx, CubemapFace face);
+ virtual void AttachDepthStencil (CubemapFace face);
+
+ UInt32 GetBufferID (void) const { return m_bufferID; }
+
+ //! RenderBuffer specific: remove current buffer handle and do not attempt to destroy it.
+ void Disown (void);
+
+private:
+ UInt32 m_bufferID;
+};
+
+class RenderBufferCubeGLES30 : public RenderSurfaceGLES30
+{
+public:
+ RenderBufferCubeGLES30 (UInt32 format, int width, int height, int numSamples);
+ ~RenderBufferCubeGLES30 (void);
+
+ virtual void AttachColor (int ndx, CubemapFace face);
+ virtual void AttachDepthStencil (CubemapFace face);
+
+ const RenderBufferGLES30& GetBuffer (CubemapFace face) const;
+
+private:
+ RenderBufferGLES30* m_buffers[6];
+};
+
+//!< FBO attachment container - used as FBO map key and in FramebufferObject
+class FramebufferAttachmentsGLES30
+{
+public:
+ enum
+ {
+ kMaxColorAttachments = 4
+ };
+
+ FramebufferAttachmentsGLES30 (int numColorAttachments, RenderSurfaceGLES30* colorAttachments, RenderSurfaceGLES30* depthStencilAttachment, CubemapFace face);
+ FramebufferAttachmentsGLES30 (void);
+
+ int numColorAttachments; //!< Number of valid attachments in color[] array. Rest are zero-filled.
+ RenderSurfaceGLES30* color[kMaxColorAttachments]; //!< Color attachments.
+ RenderSurfaceGLES30* depthStencil; //!< Depth or depth-stencil attachment, if such is used.
+ CubemapFace cubemapFace; //!< Applies to cubemap attachments only.
+};
+
+class FramebufferObjectGLES30
+{
+public:
+ FramebufferObjectGLES30 (const FramebufferAttachmentsGLES30& attachments);
+ ~FramebufferObjectGLES30 (void);
+
+ UInt32 GetFboID (void) const { return m_fboID; }
+
+ const FramebufferAttachmentsGLES30* GetAttachments (void) const { return &m_attachments; }
+
+ int GetNumColorAttachments (void) const { return m_attachments.numColorAttachments; }
+ CubemapFace GetCubemapFace (void) const { return m_attachments.cubemapFace; }
+
+ RenderSurfaceGLES30* GetColorAttachment (int ndx);
+ const RenderSurfaceGLES30* GetColorAttachment (int ndx) const;
+
+ RenderSurfaceGLES30* GetDepthStencilAttachment (void);
+ const RenderSurfaceGLES30* GetDepthStencilAttachment (void) const;
+
+ //! Disown and remove fbo handle. Used if destructor should not try to delete fbo for some reason (context lost for example).
+ void Disown (void);
+
+private:
+ UInt32 m_fboID;
+ FramebufferAttachmentsGLES30 m_attachments;
+};
+
+class RenderBufferParamsGLES30
+{
+public:
+ UInt32 format;
+ int width;
+ int height;
+ // \note No numSamples since these are currently used for allocating resolve buffers only.
+ // Sample count may be added if temporary buffers are required for something else.
+
+ RenderBufferParamsGLES30 (UInt32 format_, int width_, int height_)
+ : format (format_)
+ , width (width_)
+ , height (height_)
+ {
+ }
+};
+
+// FramebufferObject map implementation.
+//
+// Pointers to actual key data is used, since they are cheaper to move around. When inserting,
+// key pointer is acquired from FramebufferObject. When searching, it is up to user to provide
+// valid pointer (usually from stack).
+struct CompareFramebufferAttachmentsGLES30
+{
+ bool operator() (const FramebufferAttachmentsGLES30* a, const FramebufferAttachmentsGLES30* b) const;
+};
+typedef std::map<const FramebufferAttachmentsGLES30*, FramebufferObjectGLES30*, CompareFramebufferAttachmentsGLES30> FramebufferObjectMapGLES30;
+
+// RenderBuffer map implementation.
+struct CompareRenderBufferParamsGLES30
+{
+ bool operator() (const RenderBufferParamsGLES30& a, const RenderBufferParamsGLES30& b) const;
+};
+typedef std::map<RenderBufferParamsGLES30, RenderBufferGLES30*, CompareRenderBufferParamsGLES30> RenderBufferMapGLES30;
+
+class FramebufferObjectManagerGLES30
+{
+public:
+ FramebufferObjectManagerGLES30 (void);
+ ~FramebufferObjectManagerGLES30 (void);
+
+ //! Mark API objects invalid and clear cache (use on context loss)
+ void InvalidateObjects (void);
+
+ //!< Destroy all internal and API objects (clear cache and free objects).
+ void Clear (void);
+
+ //!< Destroy all FBOs where render surface is attached.
+ void InvalidateSurface (const RenderSurfaceGLES30* surface);
+
+ //! Create (or fetch from cache) framebuffer object to hold given attachment set.
+ FramebufferObjectGLES30* GetFramebufferObject (const FramebufferAttachmentsGLES30& attachments);
+
+ //! Create (or fetch from cache) temporary render buffer.
+ //
+ // Temporary render buffers are currently used as resolve buffers.
+ // Callee should not store returned buffer anywhere since it will be
+ // re-used.
+ RenderBufferGLES30* GetRenderBuffer (UInt32 format, int width, int height);
+
+private:
+ FramebufferObjectMapGLES30 m_fboMap;
+ RenderBufferMapGLES30 m_rbufMap;
+};
+
+//! Check if surface is in given FBO attachment list.
+bool IsInFramebufferAttachmentsGLES30 (const FramebufferAttachmentsGLES30& attachments, const RenderSurfaceGLES30* renderSurface);
+
+//! Bind FBO and setup for drawing
+//
+// This call setups FBO as rendering target:
+// 1) FBO is bound to current context
+// 2) If any of attachments have actions deferred to next bind defined, they are executed
+// + kDiscardOnBind: InvalidateFramebuffer() is called for those attachments
+// 3) DrawBuffers is set up based on attachments
+void BindFramebufferObjectGLES30 (FramebufferObjectGLES30* fbo);
+
+//! Bind default framebuffer (0)
+//
+// Changes FBO binding to 0 and sets GL_BACK as draw buffer.
+void BindDefaultFramebufferGLES30 (void);
+
+//! Get resolve FBO.
+//
+// \param fboManager FBO manager
+// \param colorFormat Color buffer format or 0 if not used.
+// \param depthStencilFormat Depth / depth-stencil format or 0 if not used
+// \param width
+// \param height
+//
+// Creates FBO for doing resolve. Buffers are allocated using fboManager->GetRenderBuffer()
+// and thus will be re-used by subsequent resolve FBOs if formats match.
+FramebufferObjectGLES30* GetResolveFramebufferObjectGLES30 (FramebufferObjectManagerGLES30* fboManager, UInt32 colorFormat, UInt32 depthStencilFormat, int width, int height);
+
+#endif // GFX_SUPPORTS_OPENGLES30
diff --git a/Runtime/GfxDevice/opengles30/ShaderGeneratorGLES30.cpp b/Runtime/GfxDevice/opengles30/ShaderGeneratorGLES30.cpp
new file mode 100644
index 0000000..afb9bdb
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/ShaderGeneratorGLES30.cpp
@@ -0,0 +1,824 @@
+#include "UnityPrefix.h"
+#include "FixedFunctionStateGLES30.h"
+#include "GpuProgramsGLES30.h"
+#include "ShaderGeneratorGLES30.h"
+#include "IncludesGLES30.h"
+#include "DebugGLES30.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/BuiltinShaderParams.h"
+#include "Runtime/GfxDevice/BuiltinShaderParamsNames.h"
+#include "Runtime/Utilities/BitUtility.h"
+
+#include <sstream>
+#include <assert.h>
+
+
+#define CMP_STATE(member) { \
+ if (a.member < b.member) \
+ return true; \
+ else if (b.member < a.member) \
+ return false; \
+}
+
+// builtins support only 4 lights
+const int kMaxEmulatedVertexLights = 4;//kMaxSupportedVertexLights;
+
+bool FullStateCompareGLES30::operator() (FixedFunctionStateGLES30 const& a, const FixedFunctionStateGLES30& b) const
+{
+ CMP_STATE(lightingEnabled)
+ CMP_STATE(specularEnabled)
+ CMP_STATE(lightCount)
+ CMP_STATE(onlyDirectionalLights)
+ CMP_STATE(lightType)
+ CMP_STATE(texUnitMatrix)
+ CMP_STATE(useUniformInsteadOfVertexColor)
+ CMP_STATE(useVertexColorAsAmbientAndDiffuse)
+ CMP_STATE(useVertexColorAsEmission)
+
+ CMP_STATE(fogMode)
+
+ CMP_STATE(texUnitCount);
+ for (int i = 0; i < a.texUnitCount; i++)
+ {
+ CMP_STATE(texUnitCube[i])
+ CMP_STATE(texUnitGen[i])
+ CMP_STATE(texUnitColorCombiner[i])
+ CMP_STATE(texUnitAlphaCombiner[i])
+ }
+
+ CMP_STATE(addSpecularAfterTexturing)
+ CMP_STATE(alphaTest)
+
+ return false; /* All equal, not lesser. */
+}
+
+bool VertexStateCompareGLES30::operator() (FixedFunctionStateGLES30 const& a, FixedFunctionStateGLES30 const& b) const
+{
+ CMP_STATE(lightingEnabled)
+ CMP_STATE(specularEnabled)
+ CMP_STATE(lightCount)
+ CMP_STATE(onlyDirectionalLights)
+ CMP_STATE(lightType)
+ CMP_STATE(texUnitMatrix)
+ CMP_STATE(useUniformInsteadOfVertexColor)
+ CMP_STATE(useVertexColorAsAmbientAndDiffuse)
+ CMP_STATE(useVertexColorAsEmission)
+
+ CMP_STATE(fogMode)
+
+ CMP_STATE(texUnitCount);
+ for (int i = 0; i < a.texUnitCount; i++)
+ {
+ CMP_STATE(texUnitCube[i])
+ CMP_STATE(texUnitGen[i])
+ }
+ return false; /* All equal, not lesser. */
+}
+
+bool FragmentStateCompareGLES30::operator() (FixedFunctionStateGLES30 const& a, FixedFunctionStateGLES30 const& b) const
+{
+ CMP_STATE(fogMode)
+ CMP_STATE(texUnitCount)
+
+ for (int i = 0; i < a.texUnitCount; i++)
+ {
+ CMP_STATE(texUnitCube[i])
+ CMP_STATE(texUnitGen[i])
+ CMP_STATE(texUnitColorCombiner[i])
+ CMP_STATE(texUnitAlphaCombiner[i])
+ }
+
+ CMP_STATE(addSpecularAfterTexturing)
+ CMP_STATE(alphaTest)
+
+ return false; /* All equal, not lesser. */
+}
+
+
+// --- VERTEX program ----------------------------------------------------------------------------
+
+std::string BuildVertexShaderSourceGLES30 (const FixedFunctionStateGLES30& state)
+{
+ DBG_SHADER_VERBOSE_GLES30("ShaderGeneratorGLES30::BuildVertexShaderSource()\n");
+ DBG_SHADER_VERBOSE_GLES30(" state: %s\n", state.ToString().c_str());
+
+ bool eyePositionRequired = (state.lightingEnabled && state.lightCount > 0 && !state.onlyDirectionalLights);
+ for (int i = 0; i < state.texUnitCount; i++)
+ if (state.texUnitGen[i]==kTexGenCubeReflect)
+ eyePositionRequired = true;
+
+ std::ostringstream src;
+
+ src << "#version 300 es\n";
+
+ /* Standard uniforms. */
+ src << "uniform " << FixedFunctionProgramGLES30::kSLPropTransformBlock.GetName() << " {\n";
+ src << " highp mat4 " << GetShaderInstanceMatrixParamName (kShaderInstanceMatMVP) << ";\n";
+ if (eyePositionRequired)
+ {
+ src << " highp mat4 " << GetShaderInstanceMatrixParamName (kShaderInstanceMatMV) << ";\n";
+ }
+ src << "};\n";
+
+ /* Default attributes. */
+ src << "in highp vec4 _glesVertex;\n";
+ src << "in mediump vec3 _glesNormal;\n";
+ if (state.useUniformInsteadOfVertexColor)
+ src << "uniform lowp vec4 _glesFFColor;\n";
+ else
+ src << "in lowp vec4 _glesColor;\n";
+
+ /* Default varyings. */
+ src << "out lowp vec4 v_color;\n";
+
+ if (state.fogMode > kFogDisabled)
+ {
+ src << "uniform highp vec4 _glesFogParams;\n";
+ src << "uniform lowp vec4 _glesFogColor;\n";
+ src << "out lowp vec4 _glesFogColorPreMul;\n";
+ src << "out lowp vec4 _glesFogVar;\n";
+ }
+
+ /* Texture coordinates and transformation matrices. */
+ bool needTexUnitMatrix = false;
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ src << "in " << (state.NeedTexUnitMatrix(i)?"highp":"mediump") << " vec4 _glesMultiTexCoord" << i << ";\n";
+ if (!state.texUnitCube[i])
+ {
+ if (state.texUnitGen[i] == kTexGenObject)
+ src << "out highp vec4 v_texGenObjCoord" << i << ";\n";
+ else
+ src << "out mediump vec2 v_texCoord" << i << ";\n";
+ }
+ else
+ {
+ src << "out highp vec3 v_texCoord" << i << ";\n";
+ }
+
+ if (!needTexUnitMatrix && state.NeedTexUnitMatrix(i))
+ {
+ needTexUnitMatrix = true;
+ }
+ }
+
+ if (needTexUnitMatrix)
+ {
+ src << "uniform " << FixedFunctionProgramGLES30::kSLPropUVTransformBlock.GetName() << " {\n";
+ for (int i = 0; i < state.texUnitCount; ++i)
+ {
+ if(state.NeedTexUnitMatrix(i))
+ src << " highp mat4 " << GetShaderInstanceMatrixParamName (kShaderInstanceMatTexture0 + i) << ";\n";
+ }
+ src << "};\n";
+ }
+
+ /* Handle color -> material mapping. */
+ const char* ambientColor = state.useVertexColorAsAmbientAndDiffuse ? "vertexColor" : "_glesFrontMaterial.ambient";
+ const char* diffuseColor = state.useVertexColorAsAmbientAndDiffuse ? "vertexColor" : "_glesFrontMaterial.diffuse";
+ const char* emissionColor = state.useVertexColorAsEmission ? "vertexColor" : "_glesFrontMaterial.emission";
+
+ /* Light params. */
+ if (state.lightingEnabled)
+ {
+ src << "struct LightModelParameters {\n";
+ src << " vec4 ambient;\n";
+ src << "};\n";
+
+ src << "struct MaterialParameters {\n";
+ src << " vec4 emission;\n";
+ src << " vec4 ambient;\n";
+ src << " vec4 diffuse;\n";
+ src << " vec4 specular;\n";
+ src << " float shininess;\n";
+ src << "};\n";
+
+ src << "uniform LightModelParameters _glesLightModel;\n";
+ src << "uniform MaterialParameters _glesFrontMaterial;\n";
+
+ if (state.lightCount > 0)
+ {
+ src << "struct LightSourceParameters {\n";
+ src << " vec4 diffuse;\n";
+ src << " vec4 position;\n";
+ src << " vec3 spotDirection;\n";
+ src << " vec4 atten;\n";
+ src << "};\n";
+
+ src << "uniform vec4 " << GetBuiltinVectorParamName(kShaderVecLightModelAmbient) << ";\n";
+ for (int q = 0; q < kMaxEmulatedVertexLights; ++q)
+ {
+ src << "uniform vec4 " << GetBuiltinVectorParamName(kShaderVecLight0Diffuse + q) << ";\n";
+ src << "uniform vec4 " << GetBuiltinVectorParamName(kShaderVecLight0Position + q) << ";\n";
+ src << "uniform vec4 " << GetBuiltinVectorParamName(kShaderVecLight0SpotDirection + q) << ";\n";
+ src << "uniform vec4 " << GetBuiltinVectorParamName(kShaderVecLight0Atten + q) << ";\n";
+ }
+
+ src << "uniform mat3 _glesNormalMatrix;\n";
+
+ /* Compute functions. */
+ src << "\nvec3 direction (vec4 from, vec4 to)\n";
+ src << "{\n";
+ src << " return (to.xyz * from.w - from.xyz * to.w);\n";
+ src << "}\n";
+
+ src << "\nvec3 computeLighting(LightSourceParameters light, vec3 dirToLight, vec3 eyeNormal, vec4 vertexColor)\n";
+ src << "{\n";
+ src << " float NdotL = max(dot(eyeNormal, dirToLight), 0.0);\n";
+ // \note in Unity, light ambient is always zero
+ src << " vec3 color = NdotL * " << diffuseColor << ".rgb * light.diffuse.rgb;\n";
+ if (state.specularEnabled)
+ {
+ src << " if (NdotL > 0.0)\n";
+ src << " {\n";
+ src << " vec3 h = normalize(dirToLight + vec3(0.0, 0.0, 1.0));\n";
+ src << " float HdotN = max(dot(eyeNormal, h), 0.0);\n";
+ // \note in Unity, light specular color is always the same as diffuse color
+ src << " color += pow(HdotN, _glesFrontMaterial.shininess) * _glesFrontMaterial.specular.rgb * light.diffuse.rgb;\n";
+ src << " }\n";
+ }
+ src << " return color;\n";
+ src << "}\n";
+
+ src << "\nvec3 computeDirLight(LightSourceParameters light, vec3 eyeNormal, vec4 vertexColor)\n";
+ src << "{\n";
+ src << " vec3 dirToLight = light.position.xyz;\n";
+ // \note D3D backend uses min(val, 1.0). We use clamp(), because Img's wrapper is buggy!
+ src << " return clamp(computeLighting(light, dirToLight, eyeNormal, vertexColor), 0.0, 1.0);\n";
+ src << "}\n";
+
+ src << "\nvec3 computePointLight(LightSourceParameters light, vec4 eyePosition, vec3 eyeNormal, vec4 vertexColor)\n";
+ src << "{\n";
+ src << " vec3 dirToLight = direction(eyePosition, light.position);\n";
+ src << " float distSqr = dot(dirToLight, dirToLight);\n";
+ // \note in Unity, const attenuation=1.0, linear=0.0
+ src << " float att = 1.0 / (1.0 + light.atten.z * distSqr);\n";
+ src << " dirToLight *= inversesqrt(distSqr);\n";
+ // \note D3D backend uses min(val, 1.0). We use clamp(), because Img's wrapper is buggy!
+ src << " return clamp(att * computeLighting(light, dirToLight, eyeNormal, vertexColor), 0.0, 1.0);\n";
+ src << "}\n";
+
+ src << "\nvec3 computeSpotLight(LightSourceParameters light, vec4 eyePosition, vec3 eyeNormal, vec4 vertexColor)\n";
+ src << "{\n";
+ src << " vec3 dirToLight = direction(eyePosition, light.position);\n";
+ src << " float distSqr = dot(dirToLight, dirToLight);\n";
+ // \note in Unity, const atten=1.0, linear=0.0
+ src << " float att = 1.0 / (1.0 + light.atten.z * distSqr);\n";
+ src << " dirToLight *= inversesqrt(distSqr);\n";
+ src << " float rho = max(dot(dirToLight, light.spotDirection), 0.0);\n";
+ src << " float spotAtt = (rho - light.atten.x) * light.atten.y;\n";
+ src << " spotAtt = clamp(spotAtt, 0.0, 1.0);\n";
+ // \note D3D backend uses min(val, 1.0). We use clamp(), because Img's wrapper is buggy!
+ src << " return clamp(att * spotAtt * computeLighting(light, dirToLight, eyeNormal, vertexColor), 0.0, 1.0);\n";
+ //src << " return computeLighting(light, dirToLight, eyeNormal, vertexColor);\n"; // DEBUG DEBUG
+
+ src << "}\n";
+ }
+ }
+
+
+
+ /* Main body. */
+ src << "\nvoid main()\n";
+ src << "{\n";
+
+ /* Vertex transformation. */
+ src << " gl_Position = " << GetShaderInstanceMatrixParamName (kShaderInstanceMatMVP) << " * _glesVertex;\n";
+
+ /* Unpack vertex color if necessary. */
+ if (state.useUniformInsteadOfVertexColor)
+ src << " vec4 vertexColor = _glesFFColor;\n";
+ else
+ src << " vec4 vertexColor = _glesColor;\n";
+
+ if (eyePositionRequired)
+ src << " highp vec4 eyePosition = " << GetShaderInstanceMatrixParamName (kShaderInstanceMatMV) << " * _glesVertex;\n";
+
+ /* Pass and transform texture coordinates. */
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ if (!state.texUnitCube[i])
+ {
+ if (state.texUnitGen[i] == kTexGenObject)
+ {
+ Assert(state.NeedTexUnitMatrix(i));
+ src << " v_texGenObjCoord" << i << " = ";
+ src << "(" << GetShaderInstanceMatrixParamName (kShaderInstanceMatTexture0 + i) << " * _glesMultiTexCoord" << i << ").xyzw;\n";
+ }
+ else
+ {
+ if(state.NeedTexUnitMatrix(i))
+ {
+ src << " vec4 tmpTexCoord" << i << " = (" << GetShaderInstanceMatrixParamName (kShaderInstanceMatTexture0 + i) << " * _glesMultiTexCoord" << i << ").xyzw;\n";
+ if(state.IsTexUnitProjected(i))
+ src << " v_texCoord" << i << " = tmpTexCoord" << i << ".xy / tmpTexCoord" << i << ".w;\n";
+ else
+ src << " v_texCoord" << i << " = tmpTexCoord" << i << ".xy;\n";
+ }
+ else
+ {
+ src << " v_texCoord" << i << " = _glesMultiTexCoord" << i << ".xy;\n";
+ }
+ }
+ }
+ else
+ {
+ src << " v_texCoord" << i << " = ";
+ src << "vec3(" << GetShaderInstanceMatrixParamName (kShaderInstanceMatTexture0 + i) << " * _glesMultiTexCoord" << i << ");\n";
+ if (state.texUnitGen[i] == kTexGenCubeReflect)
+ {
+ Assert(eyePositionRequired);
+ src << "{\n";
+ src << " vec3 n = v_texCoord"<< i <<".xyz;\n";
+ src << " v_texCoord"<< i <<" = reflect(eyePosition.xyz * eyePosition.w, n);\n";
+ src << "}\n";
+ }
+ }
+ }
+
+ switch (state.fogMode)
+ {
+ case kFogLinear:
+ src << " _glesFogVar = vec4(clamp (_glesFogParams.z * gl_Position.z + _glesFogParams.w, 0.0, 1.0)); _glesFogVar.a = 1.0;\n";
+ src << " _glesFogColorPreMul = _glesFogColor * (vec4(1.0)-_glesFogVar);\n";
+ break;
+ case kFogExp:
+ src << " float _patchFog = _glesFogParams.y * gl_Position.z;\n";
+ src << " _glesFogVar = vec4(clamp (exp2(-_patchFog), 0.0, 1.0)); _glesFogVar.a = 1.0;\n";
+ src << " _glesFogColorPreMul = _glesFogColor * (vec4(1.0)-_glesFogVar);\n";
+ break;
+ case kFogExp2:
+ src << " float _patchFog = _glesFogParams.x * gl_Position.z;\n";
+ src << " _patchFog = _patchFog * _patchFog;\n";
+ src << " _glesFogVar = vec4(clamp (exp2(-_patchFog), 0.0, 1.0)); _glesFogVar.a = 1.0;\n";
+ src << " _glesFogColorPreMul = _glesFogColor * (vec4(1.0)-_glesFogVar);\n";
+ break;
+ default:
+ break;
+ }
+
+ /* Vertex color computation. */
+ if (state.lightingEnabled)
+ {
+ src << " vec3 color = " << emissionColor << ".rgb + " << ambientColor << ".rgb * _glesLightModel.ambient.rgb;\n";
+// src << " color = vec3(0.0);\n"; // DEBUG DEBUG
+
+ if (state.lightCount > 0)
+ {
+ src << " vec3 eyeNormal = normalize(_glesNormalMatrix * _glesNormal);\n";
+ for (int i = 0; i < state.lightCount && i < kMaxEmulatedVertexLights; i++)
+ {
+ src << " LightSourceParameters light" << i << ";\n";
+ src << " light" << i << ".diffuse = " << GetBuiltinVectorParamName(kShaderVecLight0Diffuse + i) << ";\n";
+ src << " light" << i << ".position = " << GetBuiltinVectorParamName(kShaderVecLight0Position + i) << ";\n";
+ src << " light" << i << ".spotDirection = " << GetBuiltinVectorParamName(kShaderVecLight0SpotDirection + i) << ".xyz;\n";
+ src << " light" << i << ".atten = " << GetBuiltinVectorParamName(kShaderVecLight0Atten + i) << ";\n";
+
+ if(state.GetLightType(i) == kLightDirectional)
+ src << " color += computeDirLight(light" << i << ", eyeNormal, vertexColor);\n";
+ else if(state.GetLightType(i) == kLightSpot)
+ src << " color += computeSpotLight(light" << i << ", eyePosition, eyeNormal, vertexColor);\n";
+ else
+ src << " color += computePointLight(light" << i << ", eyePosition, eyeNormal, vertexColor);\n";
+
+ Assert(eyePositionRequired || state.GetLightType(i) == kLightDirectional);
+ }
+ }
+
+ src << " float alpha = " << diffuseColor << ".a;\n";
+ src << " v_color = vec4(color, alpha);\n";
+ }
+ else
+ {
+ src << " v_color = vertexColor;\n";
+ }
+
+ src << "}\n";
+
+ DBG_SHADER_VERBOSE_GLES30("Generated VERTEX program:\n%s\n---\n", src.str().c_str());
+
+ return src.str().c_str();
+}
+
+// --- FRAGMENT program ----------------------------------------------------------------------------
+
+
+static void DecodeTextureCombinerDescriptor (unsigned int combinerDesc,
+ combiner::Operation& operation,
+ combiner::Source sources[3],
+ combiner::Operand operands[3],
+ int& scale)
+{
+ int srcBits0 = (combinerDesc >> combiner::kSrcZeroShift) & 0xFF;
+ int srcBits1 = combinerDesc & 0xFF;
+ int cf = COMBINER_GET_FUNC(combinerDesc);
+
+ sources[0] = static_cast<combiner::Source>(srcBits0 & combiner::kSourceMask);
+ operands[0] = static_cast<combiner::Operand>(srcBits0 >> combiner::kOperandShift);
+ sources[1] = static_cast<combiner::Source>(srcBits1 & combiner::kSourceMask);
+ operands[1] = static_cast<combiner::Operand>(srcBits1 >> combiner::kOperandShift);
+ scale = (combinerDesc >> combiner::kScaleShift);
+
+ if (cf & combiner::kBlendFuncMask)
+ {
+ int blendF = COMBINER_GET_BLEND_FUNC_INDEX(cf);
+ int src2 = cf & combiner::kSourceMask;
+ int oper2 = ((cf & combiner::kOperandTwo) >> combiner::kOperandShift) | 1;
+
+ switch( blendF )
+ {
+ case 1:
+ // src0 * src2 alpha + src1
+ sources[2] = static_cast<combiner::Source>(src2);
+ operands[2] = static_cast<combiner::Operand>(oper2);
+ operation = combiner::kOpMulAdd;
+ break;
+ case 3:
+ // src0 * src2 alpha - src1
+ sources[2] = static_cast<combiner::Source>(src2);
+ operands[2] = static_cast<combiner::Operand>(oper2);
+ operation = combiner::kOpMulSub;
+ break;
+ // If not supported or lerp combiner, must go below
+ case 0:
+ // src0 lerp(src2 alpha) src1
+ // handeled by default
+ case 2:
+ // src0 * src2 alpha +- src1
+ // not supported!
+ default:
+ if (blendF != 0) ErrorString("Combiner function not supported by OpenGLES, defaulting to LERP!");
+ sources[2] = static_cast<combiner::Source>(src2);
+ operands[2] = static_cast<combiner::Operand>(oper2);
+ operation = combiner::kOpLerp;
+ break;
+ }
+
+
+ }
+ else
+ operation = static_cast<combiner::Operation>(cf);
+}
+
+static void AddTexOperandSrc (
+ std::ostringstream& src,
+ int unitNdx,
+ combiner::Channels channels,
+ combiner::Operand operand,
+ combiner::Source source)
+{
+ src << "(";
+
+ if (operand == combiner::kOperOneMinusSrcAlpha ||
+ operand == combiner::kOperOneMinusSrcColor)
+ {
+ src << "vec4(1.0) - ";
+ }
+
+ switch (source)
+ {
+ case combiner::kSrcPrimaryColor:
+ src << "v_color";
+ break;
+
+ case combiner::kSrcPrevious:
+ src << "prev";
+ break;
+
+ case combiner::kSrcTexture:
+ src << "texture";
+ break;
+
+ case combiner::kSrcConstant:
+ src << "_glesTextureEnvColor" << unitNdx;
+ break;
+
+ default:
+ printf_console("Error: Unsupported combiner source %d\n", source);
+ src << "vec4(1.0)"; /* Dummy value. */
+ }
+
+ src << ")";
+
+ switch (operand)
+ {
+ case combiner::kOperSrcColor:
+ case combiner::kOperOneMinusSrcColor:
+ if (channels == combiner::kRGBA)
+ src << ".rgba";
+ else if (channels == combiner::kRGB)
+ src << ".rgb";
+ else if (channels == combiner::kAlpha)
+ src << ".a";
+ break;
+
+ case combiner::kOperSrcAlpha:
+ case combiner::kOperOneMinusSrcAlpha:
+ if (channels == combiner::kRGBA)
+ src << ".aaaa";
+ else if (channels == combiner::kRGB)
+ src << ".aaa";
+ else if (channels == combiner::kAlpha)
+ src << ".a";
+ break;
+ }
+}
+
+static void AddTextureCombinerBody (std::ostringstream& src, int unitNdx, UInt32 combinerDesc, combiner::Channels channels)
+{
+ Assert(combiner::kRGBA == 0);
+ Assert(combiner::kRGB == 1);
+ Assert(combiner::kAlpha == 2);
+
+ const std::string channelTypes[] = { "vec4", "vec3", "float" };
+ const std::string channelMask[] = { "", ".rgb", ".a" };
+
+
+ combiner::Source sources[3];
+ combiner::Operand operands[3];
+ combiner::Operation op;
+ int scale;
+
+ DecodeTextureCombinerDescriptor(combinerDesc,
+ op, sources, operands, scale);
+
+ if ((op == combiner::kOpDot3RGBA || op == combiner::kOpDot3RGB))
+ {
+ if (channels == combiner::kAlpha)
+ return;
+ channels = combiner::kRGBA;
+ }
+
+ src << " color" << channelMask[channels] << " = ";
+
+ switch (op)
+ {
+ case combiner::kOpReplace:
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ break;
+
+ case combiner::kOpModulate:
+ {
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << " * ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ break;
+ }
+
+ case combiner::kOpAdd:
+ {
+ src << "(";
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << " + ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ src << ")";
+ break;
+ }
+
+ case combiner::kOpSubtract:
+ {
+ src << "(";
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << " - ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ src << ")";
+ break;
+ }
+
+ case combiner::kOpAddSigned:
+ {
+ src << "(";
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << " + ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ src << " - " << channelTypes[channels] << "(0.5)";
+ src << ")";
+ break;
+ }
+
+ case combiner::kOpLerp:
+ {
+ // NOTE: arguments of Unity LERP combiner are reversed for some reason
+ src << "mix(";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ src << ", ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << ", ";
+ AddTexOperandSrc(src, unitNdx, combiner::kAlpha, operands[2], sources[2]);
+ src << ")";
+ break;
+ }
+
+ case combiner::kOpDot3RGB:
+ {
+ if (channels == combiner::kRGBA)
+ {
+ src << channelTypes[channels] << "(vec3(4.0 * dot(";
+ AddTexOperandSrc(src, unitNdx, combiner::kRGB, operands[0], sources[0]);
+ src << " - vec3(0.5), ";
+ AddTexOperandSrc(src, unitNdx, combiner::kRGB, operands[1], sources[1]);
+ src << " - vec3(0.5))), ";
+ AddTexOperandSrc(src, unitNdx, combiner::kAlpha, operands[0], sources[0]);
+ src << ")";
+ // Note: I am really not sure what goes into alpha channel when dot3_rgb is performed, it's definetly not the from dot
+ // My best guess, that original alpha value is kept
+
+ }
+ else
+ {
+ src << channelTypes[channels] << "(4.0* dot(";
+ AddTexOperandSrc(src, unitNdx, combiner::kRGB, operands[0], sources[0]);
+ src << " - vec3(0.5), ";
+ AddTexOperandSrc(src, unitNdx, combiner::kRGB, operands[1], sources[1]);
+ src << " - vec3(0.5)))";
+ }
+ break;
+ }
+
+ case combiner::kOpDot3RGBA:
+ {
+ src << channelTypes[channels] << "(4.0 * dot(";
+ AddTexOperandSrc(src, unitNdx, combiner::kRGB, operands[0], sources[0]);
+ src << " - vec3(0.5), ";
+ AddTexOperandSrc(src, unitNdx, combiner::kRGB, operands[1], sources[1]);
+ src << " - vec3(0.5)))";
+ break;
+ }
+ case combiner::kOpMulAdd:
+ {
+ src << "(";
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << " * ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[2], sources[2]);
+ src << " + ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ src << ")";
+ break;
+ }
+ case combiner::kOpMulSub:
+ {
+ src << "(";
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << " * ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[2], sources[2]);
+ src << " - ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ src << ")";
+ break;
+ }
+ default:
+ ErrorString(Format("Error: Unsupported combiner operation %d\n", op).c_str());
+
+ /* Dummy value. */
+ src << channelTypes[channels] << "(1.0)";
+ break;
+ }
+
+ if (scale != 1)
+ src << " * float(" << scale << ".0)";
+ src << ";\n";
+}
+
+static void AddTextureCombinerSrc (std::ostringstream& src, int unitNdx, bool isCube, UInt32 colorCombiner, UInt32 alphaCombiner)
+{
+ src << " {\n /* Combiner " << unitNdx << " */\n";
+
+ /* Perform lookup. */
+ src << " lowp vec4 texture = " << "texture(u_sampler" << unitNdx << ", v_texCoord" << unitNdx << ");\n";
+
+ src << " lowp vec4 prev = " << ((unitNdx > 0)? "color": "v_color") << ";\n";
+
+ /* Combine. */
+ if (colorCombiner == alphaCombiner)
+ {
+ // In case of color and alpha combiner being the same
+ // we calc all 4 channels in a single operation
+ // as some GLSL compilers (iPhone) will silently fail on following:
+ // color.rgb = arg0.rgb (op) arg1.rgb
+ // color.a = arg0.a (op) arg1.a
+ // instead we spit:
+ // color = arg0 (op) arg1
+ // plus it is more readable
+ AddTextureCombinerBody(src, unitNdx, colorCombiner, combiner::kRGBA);
+ }
+ else
+ {
+ AddTextureCombinerBody(src, unitNdx, colorCombiner, combiner::kRGB);
+ AddTextureCombinerBody(src, unitNdx, alphaCombiner, combiner::kAlpha);
+ }
+
+ src << " }\n";
+}
+
+
+std::string BuildFragmentShaderSourceGLES30 (const FixedFunctionStateGLES30& state)
+{
+ DBG_SHADER_VERBOSE_GLES30("ShaderGeneratorGLES30::BuildFragmentShaderSource()\n");
+ DBG_SHADER_VERBOSE_GLES30(" state: %s\n", state.ToString().c_str());
+
+ std::ostringstream src;
+
+ src << "#version 300 es\n";
+
+ bool alphaTestEnabled = state.alphaTest != kFuncDisabled &&
+ state.alphaTest != kFuncAlways;
+
+ /* Default varyings. */
+ src << "in lowp vec4 v_color;\n";
+
+ /* Uniforms. */
+ if (alphaTestEnabled)
+ src << "uniform lowp float _glesAlphaTestReference;\n";
+
+ if (state.fogMode > kFogDisabled)
+ {
+ src << "in lowp vec4 _glesFogColorPreMul;\n";
+ src << "in lowp vec4 _glesFogVar;\n";
+ }
+
+ /* Texture units. */
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ if (!state.texUnitCube[i])
+ {
+ if (state.texUnitGen[i] == kTexGenObject)
+ src << "in highp vec4 v_texGenObjCoord" << i << ";\n";
+ else
+ src << "in mediump vec2 v_texCoord" << i << ";\n";
+
+ src << "uniform sampler2D u_sampler" << i << ";\n";
+ }
+ else
+ {
+ src << "in highp vec3 v_texCoord" << i << ";\n";
+ src << "uniform samplerCube u_sampler" << i << ";\n";
+ }
+
+ src << "uniform lowp vec4 _glesTextureEnvColor" << i << ";\n";
+ }
+
+
+ /* Main body. */
+ src << "out lowp vec4 _glesFragColor;\n";
+ src << "\nvoid main()\n";
+ src << "{\n";
+
+ /* Initialize color. */
+ src << " lowp vec4 color = v_color;\n";
+
+ /* Generate correct texCoords if we have texGenObject */
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ if (!state.texUnitCube[i] && state.texUnitGen[i] == kTexGenObject)
+ src << " highp vec2 v_texCoord" << i << " = v_texGenObjCoord" << i << ".xy / v_texGenObjCoord" << i << ".w;\n";
+ }
+
+ /* Texturing. */
+ for (int i = 0; i < state.texUnitCount; i++)
+ AddTextureCombinerSrc(src, i, state.texUnitCube[i], state.texUnitColorCombiner[i], state.texUnitAlphaCombiner[i]);
+
+ if (state.fogMode > kFogDisabled)
+ src << " _glesFragColor = color * _glesFogVar + _glesFogColorPreMul;\n";
+ else
+ src << " _glesFragColor = color;\n";
+
+ /* Alpha test. */
+ if (alphaTestEnabled)
+ {
+ Assert(gGraphicsCaps.gles30.hasAlphaTestQCOM == false);
+
+ if (state.alphaTest == kFuncNever)
+ {
+ // ToDo: Do we just discard everything, or skip drawing itself at vbo level?
+ src << " discard;\n";
+ }
+ else
+ {
+ // Reverse logic because we're using here 'discard'
+ static const char* s_cmpOps[] =
+ {
+ "", // kFuncDisabled
+ "", // kFuncNever
+ ">=", // kFuncLess
+ "!=", // kFuncEqual
+ ">", // kFuncLEqual
+ "<=", // kFuncGreater
+ "==", // kFuncNotEqual
+ "<", // kFuncGEqual
+ "", // kFuncAlways
+ };
+
+ src << " if (color.a " << s_cmpOps[state.alphaTest] << "_glesAlphaTestReference)\n";
+ src << " discard;\n";
+ }
+ }
+// src << " gl_FragColor = vec4(v_color.xyz, 1.0);\n"; // DEBUG DEBUG
+// src << " gl_FragColor = 0.5 * texture2D(u_sampler0, v_texCoord0);\n"; // DEBUG DEBUG
+// src << " gl_FragColor = vec4(_glesTextureEnvColor0.rgb, 1.0);\n"; // DEBUG DEBUG
+ src << "}\n";
+
+ DBG_SHADER_VERBOSE_GLES30("Generated FRAGMENT program:\n%s\n---\n", src.str().c_str());
+
+ return src.str().c_str();
+}
diff --git a/Runtime/GfxDevice/opengles30/ShaderGeneratorGLES30.h b/Runtime/GfxDevice/opengles30/ShaderGeneratorGLES30.h
new file mode 100644
index 0000000..57e0045
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/ShaderGeneratorGLES30.h
@@ -0,0 +1,24 @@
+#ifndef SHADERGENERATOR_GLES30_H
+#define SHADERGENERATOR_GLES30_H
+
+#include <string>
+
+class FixedFunctionStateGLES30;
+
+std::string BuildVertexShaderSourceGLES30 (const FixedFunctionStateGLES30& state);
+std::string BuildFragmentShaderSourceGLES30 (const FixedFunctionStateGLES30& state);
+
+struct FullStateCompareGLES30
+{
+ bool operator() (FixedFunctionStateGLES30 const& a, FixedFunctionStateGLES30 const& b) const;
+};
+struct VertexStateCompareGLES30
+{
+ bool operator() (FixedFunctionStateGLES30 const& a, FixedFunctionStateGLES30 const& b) const;
+};
+struct FragmentStateCompareGLES30
+{
+ bool operator() (FixedFunctionStateGLES30 const& a, FixedFunctionStateGLES30 const& b) const;
+};
+
+#endif /* SHADERGENERATOR_GLES30_H */
diff --git a/Runtime/GfxDevice/opengles30/TextureIdMapGLES30.h b/Runtime/GfxDevice/opengles30/TextureIdMapGLES30.h
new file mode 100644
index 0000000..b0bcd2e
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/TextureIdMapGLES30.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/GfxDevice/TextureIdMap.h"
+#include "IncludesGLES30.h"
+
+inline GLuint TextureIdMapGLES30_QueryOrCreate(TextureID texid)
+{
+ GLuint ret = (GLuint)TextureIdMap::QueryNativeTexture(texid);
+ if(ret == 0)
+ {
+ GLES_CHK(glGenTextures(1, &ret));
+ TextureIdMap::UpdateTexture(texid, ret);
+ }
+
+ return ret;
+}
diff --git a/Runtime/GfxDevice/opengles30/TexturesGLES30.cpp b/Runtime/GfxDevice/opengles30/TexturesGLES30.cpp
new file mode 100644
index 0000000..ba3885c
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/TexturesGLES30.cpp
@@ -0,0 +1,535 @@
+#include "UnityPrefix.h"
+#include "TexturesGLES30.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/GfxDevice/TextureUploadUtils.h"
+#include "Runtime/Graphics/S3Decompression.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "IncludesGLES30.h"
+#include "AssertGLES30.h"
+#include "DebugGLES30.h"
+#include "TextureIdMapGLES30.h"
+
+#include <vector>
+
+#if GFX_SUPPORTS_OPENGLES30
+
+struct TextureFormatInfoGLES30
+{
+ enum Type
+ {
+ kTypeReserved = 0, //!< Reserved format (should not be used).
+ kTypeUncompressed, //!< Uncompressed, natively supported format.
+ kTypeNonNative, //!< Uncompressed, but not supported format. Requires swizzle to nativeFormat.
+ kTypeCompressed //!< Compressed format. If not supported, always decompressed to kTexFormatRGBA32
+ };
+
+ Type type; //!< Texture format type, as interpreted by ES3.
+
+ UInt32 linearInternalFormat; //!< Internal format for linear color space
+ UInt32 sRGBInternalFormat; //!< Internal format for sRGB color space or 0 if not supported
+
+ UInt32 transferFormat; //!< Transfer format, or 0 if compressed texture
+ UInt32 dataType; //!< Transfer data type, or 0 if compressed texture
+
+ TextureFormat nativeFormat; //!< Data must be converted to this format first.
+};
+
+static const TextureFormatInfoGLES30 s_textureFormatInfos[] =
+{
+#define _RES { TextureFormatInfoGLES30::kTypeReserved, 0, 0, 0, 0, 0 }
+#define _NAT(NATIVE) { TextureFormatInfoGLES30::kTypeNonNative, 0, 0, 0, 0, NATIVE }
+#define _UNC(LINEAR, TRANSFERFMT, DATATYPE) { TextureFormatInfoGLES30::kTypeUncompressed, LINEAR, 0, TRANSFERFMT, DATATYPE, 0 }
+#define _SRG(LINEAR, SRGB, TRANSFERFMT, DATATYPE) { TextureFormatInfoGLES30::kTypeUncompressed, LINEAR, SRGB, TRANSFERFMT, DATATYPE, 0 }
+#define _CMP(LINEAR, SRGB) { TextureFormatInfoGLES30::kTypeCompressed, LINEAR, SRGB, 0, 0, 0 }
+
+ /* 0 */ _RES,
+ /* kTexFormatAlpha8 */ _UNC(GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE),
+ /* kTexFormatARGB4444 */ _UNC(GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4), // \todo [2013-05-03 pyry] Should we swizzle this?
+ /* kTexFormatRGB24 */ _SRG(GL_RGB8, GL_SRGB8, GL_RGB, GL_UNSIGNED_BYTE),
+ /* kTexFormatRGBA32 */ _SRG(GL_RGBA8, GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE),
+ /* kTexFormatARGB32 */ _NAT(kTexFormatRGBA32),
+ /* kTexFormatARGBFloat */ _UNC(GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT),
+ /* kTexFormatRGB565 */ _UNC(GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5),
+ /* kTexFormatBGR24 */ _NAT(kTexFormatRGB24),
+ /* kTexFormatAlphaLum16 */ _UNC(GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE),
+ /* kTexFormatDXT1 */ _CMP(GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_COMPRESSED_SRGB_S3TC_DXT1_NV),
+ /* kTexFormatDXT3 */ _CMP(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_NV),
+ /* kTexFormatDXT5 */ _CMP(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_NV),
+ /* kTexFormatRGBA4444 */ _UNC(GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4),
+ /* kTexReserved1 */ _RES,
+ /* kTexReserved2 */ _RES,
+ /* kTexReserved3 */ _RES,
+ /* kTexReserved4 */ _RES,
+ /* kTexReserved5 */ _RES,
+ /* kTexReserved6 */ _RES,
+ /* reserved (wii) 0 */ _RES,
+ /* reserved (wii) 1 */ _RES,
+ /* reserved (wii) 2 */ _RES,
+ /* reserved (wii) 3 */ _RES,
+ /* reserved (wii) 4 */ _RES,
+ /* reserved (wii) 5 */ _RES,
+ /* reserved (wii) 6 */ _RES,
+ /* reserved (wii) 7 */ _RES,
+ /* kTexReserved11 */ _RES,
+ /* kTexReserved12 */ _RES,
+ /* kTexFormatPVRTC_RGB2 */ _CMP(GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG, 0),
+ /* kTexFormatPVRTC_RGBA2 */ _CMP(GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, 0),
+ /* kTexFormatPVRTC_RGB4 */ _CMP(GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 0),
+ /* kTexFormatPVRTC_RGBA4 */ _CMP(GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, 0),
+ /* kTexFormatETC_RGB4 */ _CMP(GL_COMPRESSED_RGB8_ETC2, 0),
+ /* kTexFormatATC_RGB4 */ _CMP(GL_ATC_RGB_AMD, 0),
+ /* kTexFormatATC_RGBA8 */ _CMP(GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD, 0),
+ /* kTexFormatBGRA32 */ _NAT(kTexFormatRGBA32), // \todo [2013-05-03 pyry] Use BGRA extension if possible.
+ /* kTexFormatFlashATF_RGB_DXT1 */ _RES,
+ /* kTexFormatFlashATF_RGBA_JPG */ _RES,
+ /* kTexFormatFlashATF_RGB_JPG */ _RES,
+ /* kTexFormatEAC_R */ _CMP(GL_COMPRESSED_R11_EAC, 0),
+ /* kTexFormatEAC_R_SIGNED */ _CMP(GL_COMPRESSED_SIGNED_R11_EAC, 0),
+ /* kTexFormatEAC_RG */ _CMP(GL_COMPRESSED_RG11_EAC, 0),
+ /* kTexFormatEAC_RG_SIGNED */ _CMP(GL_COMPRESSED_SIGNED_RG11_EAC, 0),
+ /* kTexFormatETC2_RGB */ _CMP(GL_COMPRESSED_RGB8_ETC2, 0),
+ /* kTexFormatETC2_RGBA1 */ _CMP(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, 0),
+ /* kTexFormatETC2_RGBA8 */ _CMP(GL_COMPRESSED_RGBA8_ETC2_EAC, 0),
+ /*kTexFormatASTC_RGB_4x4 */ _CMP(GL_COMPRESSED_RGBA_ASTC_4x4_KHR, 0),
+ /*kTexFormatASTC_RGB_5x5 */ _CMP(GL_COMPRESSED_RGBA_ASTC_5x5_KHR, 0),
+ /*kTexFormatASTC_RGB_6x6 */ _CMP(GL_COMPRESSED_RGBA_ASTC_6x6_KHR, 0),
+ /*kTexFormatASTC_RGB_8x8 */ _CMP(GL_COMPRESSED_RGBA_ASTC_8x8_KHR, 0),
+ /*kTexFormatASTC_RGB_10x10 */ _CMP(GL_COMPRESSED_RGBA_ASTC_10x10_KHR, 0),
+ /*kTexFormatASTC_RGB_12x12 */ _CMP(GL_COMPRESSED_RGBA_ASTC_12x12_KHR, 0),
+
+ /*kTexFormatASTC_RGBA_4x4 */ _CMP(GL_COMPRESSED_RGBA_ASTC_4x4_KHR, 0),
+ /*kTexFormatASTC_RGBA_5x5 */ _CMP(GL_COMPRESSED_RGBA_ASTC_5x5_KHR, 0),
+ /*kTexFormatASTC_RGBA_6x6 */ _CMP(GL_COMPRESSED_RGBA_ASTC_6x6_KHR, 0),
+ /*kTexFormatASTC_RGBA_8x8 */ _CMP(GL_COMPRESSED_RGBA_ASTC_8x8_KHR, 0),
+ /*kTexFormatASTC_RGBA_10x10 */ _CMP(GL_COMPRESSED_RGBA_ASTC_10x10_KHR, 0),
+ /*kTexFormatASTC_RGBA_12x12 */ _CMP(GL_COMPRESSED_RGBA_ASTC_12x12_KHR, 0),
+
+#undef _CMP
+#undef _SRG
+#undef _NAT
+#undef _UNC
+#undef _RES
+};
+
+// Static assert for array size.
+typedef char __gles3TexFormatTableSizeAssert[(int)(sizeof(s_textureFormatInfos) / sizeof(s_textureFormatInfos[0])) == kTexFormatTotalCount ? 1 : - 1];
+
+static const TextureFormatInfoGLES30& GetTextureFormatInfoGLES30 (TextureFormat format)
+{
+ Assert(0 <= format && format < (int)(sizeof(s_textureFormatInfos)/sizeof(s_textureFormatInfos[0])));
+ return s_textureFormatInfos[format];
+}
+
+static inline bool IsTextureFormatSupported (TextureFormat format)
+{
+ return gGraphicsCaps.supportsTextureFormat[format];
+}
+
+static const char* GetCompressedTextureFormatName (TextureFormat format)
+{
+ if (IsCompressedPVRTCTextureFormat(format))
+ return "PVRTC";
+ else if (IsCompressedDXTTextureFormat(format))
+ return "DXT";
+ else if (IsCompressedETCTextureFormat(format))
+ return "ETC1";
+ else if (IsCompressedATCTextureFormat(format))
+ return "ATC";
+ else if (IsCompressedETC2TextureFormat(format))
+ return "ETC2";
+ else if (IsCompressedEACTextureFormat(format))
+ return "EAC";
+ else
+ return "UNKNOWN";
+}
+
+static void GetDecompressBlockSize (TextureFormat format, int& width, int& height)
+{
+ // \todo [2013-05-06 pyry] Is there any format that doesn't use 4x4 blocks?
+ width = 4;
+ height = 4;
+}
+
+static inline int GetDecompressBlockWidth (TextureFormat format)
+{
+ int w, h;
+ GetDecompressBlockSize(format, w, h);
+ return w;
+}
+
+static inline int GetDecompressBlockHeight (TextureFormat format)
+{
+ int w, h;
+ GetDecompressBlockSize(format, w, h);
+ return h;
+}
+
+static inline int AlignToBlockSize (int dim, int blockSize)
+{
+ if (dim % blockSize != 0)
+ return dim + blockSize - (dim % blockSize);
+ else
+ return dim;
+}
+
+static int UploadPyramidCompressed (UInt32 target,
+ TextureFormat format,
+ bool sRGB,
+ int width,
+ int height,
+ int numLevels,
+ const UInt8* data)
+{
+ Assert(IsTextureFormatSupported(format));
+
+ const TextureFormatInfoGLES30& formatInfo = GetTextureFormatInfoGLES30(format);
+ const bool isSRGBUploadOk = formatInfo.sRGBInternalFormat != 0;
+ const UInt32 internalFormat = (sRGB && isSRGBUploadOk) ? formatInfo.sRGBInternalFormat : formatInfo.linearInternalFormat;
+ const UInt8* curDataPtr = data;
+ int totalUploadSize = 0;
+
+ for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
+ {
+ const int levelW = std::max<int>(width>>levelNdx, 1);
+ const int levelH = std::max<int>(height>>levelNdx, 1);
+ const int levelSize = CalculateImageSize(levelW, levelH, format);
+ const int uploadLevel = levelNdx;
+
+ GLES_CHK(glCompressedTexImage2D(target, levelNdx, internalFormat, levelW, levelH, 0, levelSize, curDataPtr));
+
+ curDataPtr += levelSize;
+ totalUploadSize += levelSize;
+ }
+
+ return totalUploadSize;
+}
+
+static int UploadPyramidDecompress (UInt32 target,
+ TextureFormat format,
+ bool sRGB,
+ int width,
+ int height,
+ int numLevels,
+ const UInt8* data)
+{
+ const TextureFormatInfoGLES30& formatInfo = GetTextureFormatInfoGLES30(format);
+ const TextureFormat decompressFormat = kTexFormatRGBA32;
+ const TextureFormatInfoGLES30& uploadFormat = GetTextureFormatInfoGLES30(decompressFormat);
+ const bool isSRGBUploadOk = uploadFormat.sRGBInternalFormat != 0;
+ const UInt32 internalFormat = (sRGB && isSRGBUploadOk) ? uploadFormat.sRGBInternalFormat : uploadFormat.linearInternalFormat;
+ const UInt8* curDataPtr = data;
+ const int blockW = GetDecompressBlockWidth(format);
+ const int blockH = GetDecompressBlockHeight(format);
+ int totalUploadSize = 0;
+ std::vector<UInt8> decompressBuffer (CalculateImageSize(AlignToBlockSize(width, blockW), AlignToBlockSize(height, blockH), decompressFormat));
+
+ for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
+ {
+ const int levelW = std::max<int>(width>>levelNdx, 1);
+ const int levelH = std::max<int>(height>>levelNdx, 1);
+ const int levelSize = CalculateImageSize(levelW, levelH, format);
+ const int decompressW = AlignToBlockSize(levelW, blockW);
+ const int decompressH = AlignToBlockSize(levelH, blockH);
+ const int decompressSize = CalculateImageSize(decompressW, decompressH, decompressFormat);
+
+ Assert(decompressSize <= (int)decompressBuffer.size());
+ if (!DecompressNativeTextureFormat(format, levelW, levelH, (UInt32*)curDataPtr, decompressW, decompressH, (UInt32*)&decompressBuffer[0]))
+ ErrorStringMsg("Decompressing level %d failed!", levelNdx);
+
+ GLES_CHK(glTexImage2D(target, levelNdx, internalFormat, levelW, levelH, 0, uploadFormat.transferFormat, uploadFormat.dataType, &decompressBuffer[0]));
+
+ curDataPtr += levelSize;
+ totalUploadSize += decompressSize;
+ }
+
+ return totalUploadSize;
+}
+
+static int UploadPyramidConvert (UInt32 target,
+ TextureFormat srcFormat,
+ TextureFormat dstFormat,
+ bool sRGB,
+ int width,
+ int height,
+ int numLevels,
+ const UInt8* data)
+{
+ const TextureFormatInfoGLES30& dstFormatInfo = GetTextureFormatInfoGLES30(dstFormat);
+ const bool isSRGBUploadOk = dstFormatInfo.sRGBInternalFormat != 0;
+ const UInt32 internalFormat = (sRGB && isSRGBUploadOk) ? dstFormatInfo.sRGBInternalFormat : dstFormatInfo.linearInternalFormat;
+ const UInt8* curDataPtr = data;
+ int totalUploadSize = 0;
+ std::vector<UInt8> convertBuffer (CalculateImageSize(width, height, dstFormat));
+
+ for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
+ {
+ const int levelW = std::max<int>(width>>levelNdx, 1);
+ const int levelH = std::max<int>(height>>levelNdx, 1);
+ const int levelSize = CalculateImageSize(levelW, levelH, srcFormat);
+
+ ImageReference src (levelW, levelH, GetRowBytesFromWidthAndFormat(levelW, srcFormat), srcFormat, (UInt8*)curDataPtr);
+ ImageReference dst (levelW, levelH, GetRowBytesFromWidthAndFormat(levelW, dstFormat), dstFormat, &convertBuffer[0]);
+
+ dst.BlitImage(src);
+
+ GLES_CHK(glTexImage2D(target, levelNdx, internalFormat, levelW, levelH, 0, dstFormatInfo.transferFormat, dstFormatInfo.dataType, &convertBuffer[0]));
+
+ curDataPtr += levelSize;
+ totalUploadSize += dst.GetRowBytes()*dst.GetHeight();
+ }
+
+ return totalUploadSize;
+}
+
+static int UploadPyramid (UInt32 target,
+ TextureFormat format,
+ bool sRGB,
+ int width,
+ int height,
+ int numLevels,
+ const UInt8* data)
+{
+ const TextureFormatInfoGLES30& formatInfo = GetTextureFormatInfoGLES30(format);
+ const bool isSRGBUploadOk = formatInfo.sRGBInternalFormat != 0;
+ const UInt32 internalFormat = (sRGB && isSRGBUploadOk) ? formatInfo.sRGBInternalFormat : formatInfo.linearInternalFormat;
+ const UInt8* curDataPtr = data;
+ int totalUploadSize = 0;
+
+ for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
+ {
+ const int levelW = std::max<int>(width>>levelNdx, 1);
+ const int levelH = std::max<int>(height>>levelNdx, 1);
+ const int levelSize = CalculateImageSize(levelW, levelH, format);
+
+ GLES_CHK(glTexImage2D(target, levelNdx, internalFormat, levelW, levelH, 0, formatInfo.transferFormat, formatInfo.dataType, curDataPtr));
+
+ curDataPtr += levelSize;
+ totalUploadSize += levelSize;
+ }
+
+ return totalUploadSize;
+}
+
+void UploadTexture2DGLES3 (TextureID texID,
+ TextureDimension dimension,
+ UInt8* srcData,
+ int width,
+ int height,
+ TextureFormat format,
+ int mipCount,
+ UInt32 uploadFlags,
+ int skipMipLevels,
+ TextureColorSpace colorSpace)
+{
+ Assert(dimension == kTexDim2D); // \todo [2013-05-03 pyry] Remove parameter.
+
+ const TextureFormatInfoGLES30& formatInfo = GetTextureFormatInfoGLES30(format);
+ const bool isSRGB = colorSpace == kTexColorSpaceSRGBXenon || colorSpace == kTexColorSpaceSRGB;
+ const bool isCompressed = formatInfo.type == TextureFormatInfoGLES30::kTypeCompressed;
+ const bool decompress = isCompressed && !IsTextureFormatSupported(format);
+ const bool convertToNative = formatInfo.type == TextureFormatInfoGLES30::kTypeNonNative;
+ int totalUploadSize = 0;
+
+ Assert(!decompress || !convertToNative);
+ Assert(skipMipLevels < mipCount);
+
+ if (decompress)
+ printf_console("WARNING: %s compressed texture format not supported, decompressing!\n", GetCompressedTextureFormatName(format));
+ else if (convertToNative)
+ printf_console("WARNING: no native support for texture format %d, converting to %d!\n", format, formatInfo.nativeFormat);
+
+ // Create and bind texture.
+ TextureIdMapGLES30_QueryOrCreate(texID);
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, texID, dimension, std::numeric_limits<float>::infinity());
+
+ // \todo [2013-05-03 pyry] Select unpack alignment based on pixel format
+ GLES_CHK(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+
+ // Upload parameters.
+ const int baseLevelW = std::max<int>(1, width >> skipMipLevels);
+ const int baseLevelH = std::max<int>(1, height >> skipMipLevels);
+ const int numLevelsUpload = mipCount-skipMipLevels;
+ const UInt8* baseLevelPtr = srcData;
+
+ // Adjust data pointer by number of levels skipped.
+ // \todo [2013-045-03 pyry] Skip levels if they are too large for HW.
+ for (int levelNdx = 0; levelNdx < skipMipLevels; levelNdx++)
+ {
+ int levelW = std::max<int>(1, width >> levelNdx);
+ int levelH = std::max<int>(1, height >> levelNdx);
+ int levelSize = CalculateImageSize(levelW, levelH, format);
+
+ baseLevelPtr += levelSize;
+ }
+
+ // Call upload.
+ if (isCompressed)
+ {
+ if (decompress)
+ totalUploadSize = UploadPyramidDecompress(GL_TEXTURE_2D, format, isSRGB, baseLevelW, baseLevelH, numLevelsUpload, baseLevelPtr);
+ else
+ totalUploadSize = UploadPyramidCompressed(GL_TEXTURE_2D, format, isSRGB, baseLevelW, baseLevelH, numLevelsUpload, baseLevelPtr);
+ }
+ else
+ {
+ if (convertToNative)
+ totalUploadSize = UploadPyramidConvert(GL_TEXTURE_2D, format, formatInfo.nativeFormat, isSRGB, baseLevelW, baseLevelH, numLevelsUpload, baseLevelPtr);
+ else
+ totalUploadSize = UploadPyramid(GL_TEXTURE_2D, format, isSRGB, baseLevelW, baseLevelH, numLevelsUpload, baseLevelPtr);
+ }
+
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, numLevelsUpload-1));
+
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(texID.m_ID);
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(texID.m_ID, totalUploadSize, texID.m_ID);
+}
+
+void UploadTextureSubData2DGLES3 (TextureID texID,
+ UInt8* srcData,
+ int mipLevel,
+ int x,
+ int y,
+ int width,
+ int height,
+ TextureFormat format,
+ TextureColorSpace colorSpace)
+{
+ const TextureFormatInfoGLES30& formatInfo = GetTextureFormatInfoGLES30(format);
+ const bool isSRGB = colorSpace == kTexColorSpaceSRGBXenon || colorSpace == kTexColorSpaceSRGB;
+ const bool isCompressed = formatInfo.type == TextureFormatInfoGLES30::kTypeCompressed;
+ const bool decompress = isCompressed && !IsTextureFormatSupported(format);
+ const bool convertToNative = formatInfo.type == TextureFormatInfoGLES30::kTypeNonNative;
+ const TextureFormatInfoGLES30& uploadInfo = convertToNative ? GetTextureFormatInfoGLES30(formatInfo.nativeFormat) : formatInfo;
+ int totalUploadSize = 0;
+ const UInt32 target = GL_TEXTURE_2D;
+
+ Assert(!decompress || !convertToNative);
+
+ if (decompress)
+ printf_console("WARNING: %s compressed texture format not supported, decompressing!\n", GetCompressedTextureFormatName(format));
+ else if (convertToNative)
+ printf_console("WARNING: no native support for texture format %d, converting to %d!\n", format, formatInfo.nativeFormat);
+
+ // Create and bind texture.
+ TextureIdMapGLES30_QueryOrCreate(texID);
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, texID, kTexDim2D, std::numeric_limits<float>::infinity());
+
+ // \todo [2013-05-03 pyry] Select unpack alignment based on pixel format
+ GLES_CHK(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+
+ if (isCompressed)
+ {
+ if (decompress)
+ {
+ const int blockW = GetDecompressBlockWidth(format);
+ const int blockH = GetDecompressBlockHeight(format);
+ const int decompressW = AlignToBlockSize(width, blockW);
+ const int decompressH = AlignToBlockSize(height, blockH);
+ std::vector<UInt8> decompressBuffer (CalculateImageSize(decompressW, decompressH, format));
+
+ if (!DecompressNativeTextureFormat(format, width, height, (const UInt32*)srcData, decompressW, decompressH, (UInt32*)&decompressBuffer[0]))
+ ErrorString("Decompressing texture data failed!");
+
+ GLES_CHK(glTexSubImage2D(target, mipLevel, x, y, width, height, uploadInfo.transferFormat, uploadInfo.dataType, &decompressBuffer[0]));
+ }
+ else
+ {
+ const int dataSize = CalculateImageSize(width, height, format);
+ GLES_CHK(glCompressedTexSubImage2D(target, mipLevel, x, y, width, height, uploadInfo.linearInternalFormat, dataSize, srcData));
+ }
+ }
+ else
+ {
+ if (convertToNative)
+ {
+ std::vector<UInt8> convertBuffer (CalculateImageSize(width, height, formatInfo.nativeFormat));
+ ImageReference src (width, height, GetRowBytesFromWidthAndFormat(width, format), format, srcData);
+ ImageReference dst (width, height, GetRowBytesFromWidthAndFormat(width, formatInfo.nativeFormat), formatInfo.nativeFormat, &convertBuffer[0]);
+
+ dst.BlitImage(src);
+
+ GLES_CHK(glTexSubImage2D(target, mipLevel, x, y, width, height, uploadInfo.transferFormat, uploadInfo.dataType, &convertBuffer[0]));
+ }
+ else
+ GLES_CHK(glTexSubImage2D(target, mipLevel, x, y, width, height, uploadInfo.transferFormat, uploadInfo.dataType, srcData));
+ }
+}
+
+void UploadTextureCubeGLES3 (TextureID texID,
+ UInt8* srcData,
+ int faceDataSize,
+ int size,
+ TextureFormat format,
+ int mipCount,
+ UInt32 uploadFlags,
+ TextureColorSpace colorSpace)
+{
+ const TextureFormatInfoGLES30& formatInfo = GetTextureFormatInfoGLES30(format);
+ const bool isSRGB = colorSpace == kTexColorSpaceSRGBXenon || colorSpace == kTexColorSpaceSRGB;
+ const bool isCompressed = formatInfo.type == TextureFormatInfoGLES30::kTypeCompressed;
+ const bool decompress = isCompressed && !IsTextureFormatSupported(format);
+ const bool convertToNative = formatInfo.type == TextureFormatInfoGLES30::kTypeNonNative;
+ int totalUploadSize = 0;
+
+ Assert(!decompress || !convertToNative);
+
+ if (decompress)
+ printf_console("WARNING: %s compressed texture format not supported, decompressing!\n", GetCompressedTextureFormatName(format));
+ else if (convertToNative)
+ printf_console("WARNING: no native support for texture format %d, converting to %d!\n", format, formatInfo.nativeFormat);
+
+ // Create and bind texture.
+ TextureIdMapGLES30_QueryOrCreate(texID);
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, texID, kTexDimCUBE, std::numeric_limits<float>::infinity());
+
+ // \todo [2013-05-03 pyry] Select unpack alignment based on pixel format
+ GLES_CHK(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+
+ // \todo [2013-045-03 pyry] Skip levels if they are too large for HW.
+ const int baseLevelOffset = 0;
+ const int baseLevelW = size;
+ const int baseLevelH = size;
+ const int numLevelsUpload = mipCount;
+
+ const GLenum faces[6] =
+ {
+ GL_TEXTURE_CUBE_MAP_POSITIVE_X,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
+ GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
+ GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
+ };
+
+ for (int faceNdx = 0; faceNdx < 6; faceNdx++)
+ {
+ const UInt8* facePtr = (srcData + faceNdx*faceDataSize) + baseLevelOffset;
+ const UInt32 target = faces[faceNdx];
+
+ if (isCompressed)
+ {
+ if (decompress)
+ totalUploadSize = UploadPyramidDecompress(target, format, isSRGB, baseLevelW, baseLevelH, numLevelsUpload, facePtr);
+ else
+ totalUploadSize = UploadPyramidCompressed(target, format, isSRGB, baseLevelW, baseLevelH, numLevelsUpload, facePtr);
+ }
+ else
+ {
+ if (convertToNative)
+ totalUploadSize = UploadPyramidConvert(target, format, formatInfo.nativeFormat, isSRGB, baseLevelW, baseLevelH, numLevelsUpload, facePtr);
+ else
+ totalUploadSize = UploadPyramid(target, format, isSRGB, baseLevelW, baseLevelH, numLevelsUpload, facePtr);
+ }
+ }
+
+ GLES_CHK(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, numLevelsUpload-1));
+
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(texID.m_ID);
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(texID.m_ID, totalUploadSize, texID.m_ID);
+}
+
+#endif // GFX_SUPPORTS_OPENGLES30
diff --git a/Runtime/GfxDevice/opengles30/TexturesGLES30.h b/Runtime/GfxDevice/opengles30/TexturesGLES30.h
new file mode 100644
index 0000000..94fbb31
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/TexturesGLES30.h
@@ -0,0 +1,22 @@
+#ifndef UNITY_TEXTURES_GLES_H_
+#define UNITY_TEXTURES_GLES_H_
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Graphics/TextureFormat.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+class ImageReference;
+
+// \todo [2013-05-03 pyry] Clean up this interface.
+
+void UploadTexture2DGLES3(
+ TextureID glname, TextureDimension dimension, UInt8* srcData, int width, int height,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, int masterTextureLimit, TextureColorSpace colorSpace );
+void UploadTextureSubData2DGLES3(
+ TextureID glname, UInt8* srcData,
+ int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace );
+void UploadTextureCubeGLES3(
+ TextureID tid, UInt8* srcData, int faceDataSize, int size,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace );
+
+#endif // UNITY_TEXTURES_GLES_H_
diff --git a/Runtime/GfxDevice/opengles30/TimerQueryGLES30.cpp b/Runtime/GfxDevice/opengles30/TimerQueryGLES30.cpp
new file mode 100644
index 0000000..b59b178
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/TimerQueryGLES30.cpp
@@ -0,0 +1,100 @@
+#include "UnityPrefix.h"
+#if ENABLE_PROFILER && GFX_SUPPORTS_OPENGLES30
+#include "TimerQueryGLES30.h"
+#include "AssertGLES30.h"
+#include "UnityGLES30Ext.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+
+bool TimerQueriesGLES30::Init()
+{
+ Assert(!gGraphicsCaps.hasTimerQuery || QueryExtension("GL_NV_timer_query"));
+
+ if (gGraphicsCaps.hasTimerQuery)
+ GLES_CHK(glGenQueries(kQueriesCount, timestamp_gl));
+
+ nextIndex = 0;
+ return true;
+}
+
+void TimerQueriesGLES30::BeginTimerQueries()
+{
+ GLES_CHK(glFlush());
+ SetTimestamp();
+}
+
+void TimerQueriesGLES30::EndTimerQueries()
+{
+ GLES_CHK(glFlush());
+}
+
+unsigned TimerQueriesGLES30::SetTimestamp()
+{
+ // \todo [2013-04-17 pyry] glQueryCounter is not in ES3
+#if 0
+ GLES_CHK(gGles3ExtFunc.glQueryCounterNV(timestamp_gl[nextIndex], GL_TIMESTAMP_NV));
+#endif
+ unsigned ret = nextIndex;
+
+ ++nextIndex;
+ if(nextIndex == kQueriesCount)
+ nextIndex = 0;
+
+ return ret;
+}
+
+UInt64 TimerQueriesGLES30::GetElapsedTime(unsigned idx, bool wait)
+{
+ GLuint available = 0;
+ GLES_CHK(glGetQueryObjectuiv(timestamp_gl[idx], GL_QUERY_RESULT_AVAILABLE, &available));
+ // sometimes timestamp might be not ready (we still dont know why)
+ // the only workaround would be to add glFlush into SetTimestamp
+ // but then some timings will be a bit off
+ if(wait)
+ {
+ for(unsigned i = 0 ; i < 100 && !available ; ++i)
+ {
+ GLES_CHK(glGetQueryObjectuiv(timestamp_gl[idx], GL_QUERY_RESULT_AVAILABLE, &available));
+ }
+ }
+
+ if(available)
+ {
+ unsigned prev_idx = idx > 0 ? idx-1 : kQueriesCount-1;
+
+ // \todo [2013-04-17 pyry] i64 variant?
+ GLuint time1, time2;
+ GLES_CHK(glGetQueryObjectuiv(timestamp_gl[prev_idx], GL_QUERY_RESULT, &time1));
+ GLES_CHK(glGetQueryObjectuiv(timestamp_gl[idx], GL_QUERY_RESULT, &time2));
+
+ return time2-time1;
+ }
+
+ return kInvalidProfileTime;
+
+}
+
+
+TimerQueryGLES30::TimerQueryGLES30()
+ : m_Index(0),
+ m_Time(kInvalidProfileTime)
+{
+}
+
+void TimerQueryGLES30::Measure()
+{
+ m_Index = g_TimerQueriesGLES30.SetTimestamp();
+ m_Time = kInvalidProfileTime;
+}
+
+ProfileTimeFormat TimerQueryGLES30::GetElapsed(UInt32 flags)
+{
+ if(m_Time == kInvalidProfileTime)
+ m_Time = g_TimerQueriesGLES30.GetElapsedTime(m_Index, (flags & kWaitRenderThread) != 0);
+
+ return m_Time;
+}
+
+TimerQueriesGLES30 g_TimerQueriesGLES30;
+
+
+#endif
diff --git a/Runtime/GfxDevice/opengles30/TimerQueryGLES30.h b/Runtime/GfxDevice/opengles30/TimerQueryGLES30.h
new file mode 100644
index 0000000..9bf7e22
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/TimerQueryGLES30.h
@@ -0,0 +1,47 @@
+#ifndef TIMERQUERYGLES_H
+#define TIMERQUERYGLES_H
+
+#if ENABLE_PROFILER && GFX_SUPPORTS_OPENGLES30
+
+#include "IncludesGLES30.h"
+#include "Runtime/GfxDevice/GfxTimerQuery.h"
+
+struct TimerQueriesGLES30
+{
+ enum
+ {
+ kQueriesCount = 128,
+ };
+
+ GLuint timestamp_gl[kQueriesCount];
+ unsigned nextIndex;
+
+ bool Init();
+
+ void BeginTimerQueries();
+ void EndTimerQueries();
+
+ unsigned SetTimestamp();
+ UInt64 GetElapsedTime(unsigned idx, bool wait);
+};
+extern TimerQueriesGLES30 g_TimerQueriesGLES30;
+
+
+class TimerQueryGLES30
+ : public GfxTimerQuery
+{
+public:
+
+ TimerQueryGLES30();
+
+ virtual void Measure();
+ virtual ProfileTimeFormat GetElapsed(UInt32 flags);
+
+private:
+
+ unsigned m_Index;
+ ProfileTimeFormat m_Time;
+};
+
+#endif
+#endif
diff --git a/Runtime/GfxDevice/opengles30/TransformFeedbackSkinnedMesh.cpp b/Runtime/GfxDevice/opengles30/TransformFeedbackSkinnedMesh.cpp
new file mode 100644
index 0000000..dc22318
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/TransformFeedbackSkinnedMesh.cpp
@@ -0,0 +1,762 @@
+ #include "UnityPrefix.h"
+
+#include "Runtime/GfxDevice/opengles30/TransformFeedbackSkinnedMesh.h"
+
+#include "Runtime/Filters/Mesh/Mesh.h"
+#include "Runtime/Filters/Mesh/MeshSkinning.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/GfxDevice/opengles30/VBOGLES30.h"
+#include "Runtime/GfxDevice/opengles30/AssertGLES30.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+
+// If 1, uses uniform blocks, otherwise fix bone count to 82
+#define USE_UNIFORM_BLOCK_FOR_BONES 0
+
+// 1 to use glVertexAttribIPointer for bone indices, 0 to convert to floats
+#define USE_INT_ATTRIBS 1
+
+//! Attribute array indices.
+enum { TFATTRLOC_POS = 0, TFATTRLOC_NORM = 1, TFATTRLOC_TAN = 2, TFATTRLOC_BONEIDX=3, TFATTRLOC_BONEWEIGHT = 4, TFATTRLOC_SIZE = 5 };
+
+// Shader programs
+enum { TFSHADER_POS = 0, TFSHADER_POSNORM = 1, TFSHADER_POSNORMTAN = 2, TFSHADER_SIZE = 3 };
+
+struct TFShader
+{
+ TFShader() : program(0), vertShader(0), bonesLocation(0) {}
+
+ // Not a dtor, we're storing them in a map, so delete manually in CleanupTransformFeedbackShaders
+ void Release()
+ {
+ if(program)
+ glDeleteProgram(program);
+ if(vertShader)
+ glDeleteShader(vertShader);
+ }
+
+ GLuint program;
+ GLuint vertShader;
+ GLint bonesLocation;
+ GLint attribLocations[TFATTRLOC_SIZE];
+};
+
+// Swap specialization for TFShader
+namespace std
+{
+ template<> void swap(TFShader &a, TFShader &b)
+ {
+ swap(a.program, b.program);
+ swap(a.vertShader, b.vertShader);
+ swap(a.bonesLocation, b.bonesLocation);
+ swap(a.attribLocations, b.attribLocations);
+ }
+}
+
+// Map to store shaders. the key is channelMap + (bonesPerVertex << 16)
+typedef std::map<UInt32, TFShader> TFShaderMap;
+
+static TFShaderMap tfShaders;
+
+const char *tfShaderAttribNames[TFATTRLOC_SIZE] = {"in_vertex", "in_normal", "in_tangent", "in_boneIndices", "in_boneWeights" };
+
+//! Fragment shader, common to all programs.
+static GLuint tfFragShader = 0;
+
+
+#define STRINGIFY(x) #x
+
+#if USE_UNIFORM_BLOCK_FOR_BONES
+ #define MATRIX_DECL "uniform MtxBlock { vec4 bones[max_bone_count*3]; } Matrices; \n"
+ #define BUILD_MATRIX "Matrices.bones[bidx + 0], Matrices.bones[bidx + 1], Matrices.bones[bidx + 2]"
+#else
+ #define MATRIX_DECL "uniform vec4 bones[max_bone_count*3];\n"
+ #define BUILD_MATRIX "bones[bidx + 0], bones[bidx + 1], bones[bidx + 2]"
+#endif
+
+// Macro to build shader source.
+#define BUILD_SHADER_2(bonecount, indecl, outdecl, skincalc, outcalc ) \
+ "#version 300 es\n" \
+ "\n" \
+ "const int max_bone_count = " STRINGIFY(bonecount) ";\n" \
+ "in vec3 in_vertex;\n" \
+ indecl \
+ "out vec3 out_pos;\n" \
+ outdecl \
+ "\n" \
+ MATRIX_DECL \
+ "\n" \
+ "mat4 getMatrix(int idx)\n" \
+ "{\n"\
+ " int bidx = idx*3;\n" \
+ " return mat4(" BUILD_MATRIX ", vec4(0.0, 0.0, 0.0, 1.0));\n" \
+ "}\n"\
+ "void main(void)\n" \
+ "{\n" \
+ " vec4 inpos = vec4(in_vertex.xyz, 1.0);\n" \
+ " mat4 localToWorldMatrix = \n" \
+ skincalc \
+ " out_pos = (inpos * localToWorldMatrix).xyz;\n" \
+ " gl_Position = vec4(out_pos.xyz, 1.0);\n" \
+ outcalc \
+ "}"
+
+#if USE_INT_ATTRIBS
+#define BONEINDEXTYPE1 "int"
+#define BONEINDEXTYPE2 "ivec2"
+#define BONEINDEXTYPE4 "ivec4"
+#else
+#define BONEINDEXTYPE1 "float"
+#define BONEINDEXTYPE2 "vec2"
+#define BONEINDEXTYPE4 "vec4"
+#endif
+
+#if USE_UNIFORM_BLOCK_FOR_BONES
+#define BUILD_SHADER( indecl, outdecl, skincalc, outcalc ) \
+ {\
+ BUILD_SHADER_2(32, indecl, outdecl, skincalc, outcalc), \
+ BUILD_SHADER_2(64, indecl, outdecl, skincalc, outcalc), \
+ BUILD_SHADER_2(128, indecl, outdecl, skincalc, outcalc), \
+ BUILD_SHADER_2(256, indecl, outdecl, skincalc, outcalc), \
+ BUILD_SHADER_2(512, indecl, outdecl, skincalc, outcalc), \
+ BUILD_SHADER_2(1024, indecl, outdecl, skincalc, outcalc) }
+#else
+// Just one bonecount, store it in first element
+#define BUILD_SHADER( indecl, outdecl, skincalc, outcalc ) \
+{\
+ BUILD_SHADER_2(82, indecl, outdecl, skincalc, outcalc), "", "", "", "", ""\
+}
+#endif
+// Shaders for each input type, and for various max bone counts (32, 64, 128, 256, 512 and 1024) and bone-per-vertex counts (1, 2, 4 bones per vertex supported, sparse array so third slot is empty).
+static const char *tfShaderSource[TFSHADER_SIZE][4][6] = {
+ // TFSHADER_POS
+ {
+#define IN_DECL "\n"
+#define OUT_DECL "\n"
+#define OUT_CALC "\n"
+ // 1 bone
+ BUILD_SHADER( "in " BONEINDEXTYPE1 " in_boneIndices;\n" IN_DECL,
+ OUT_DECL,
+ " getMatrix(int(in_boneIndices));\n",
+ OUT_CALC
+ ),
+
+ // 2 bones
+ BUILD_SHADER( "in " BONEINDEXTYPE2 " in_boneIndices;\n in vec2 in_boneWeights;\n" IN_DECL,
+ OUT_DECL,
+ " getMatrix(int(in_boneIndices.x)) * in_boneWeights[0] + \n" \
+ " getMatrix(int(in_boneIndices.y)) * in_boneWeights[1] ;\n ",
+ OUT_CALC
+ ),
+ // 3 bones
+ {"", "", "", "", "", ""},
+
+ // 4 bones
+ BUILD_SHADER( "in " BONEINDEXTYPE4 " in_boneIndices;\n in vec4 in_boneWeights;\n" IN_DECL,
+ OUT_DECL,
+ " getMatrix(int(in_boneIndices.x)) * in_boneWeights[0] + \n" \
+ " getMatrix(int(in_boneIndices.y)) * in_boneWeights[1] + \n" \
+ " getMatrix(int(in_boneIndices.z)) * in_boneWeights[2] + \n" \
+ " getMatrix(int(in_boneIndices.w)) * in_boneWeights[3] ;\n",
+ OUT_CALC
+ )
+ }
+ ,
+ // TFSHADER_POSNORM
+ {
+#undef IN_DECL
+#undef OUT_DECL
+#undef OUT_CALC
+#define IN_DECL "in vec3 in_normal;\n"
+#define OUT_DECL "out vec3 out_normal;\n"
+#define OUT_CALC " out_normal = normalize( (vec4(in_normal.xyz, 0.0) * localToWorldMatrix)).xyz;\n"
+ // 1 bone
+ BUILD_SHADER( "in " BONEINDEXTYPE1 " in_boneIndices;\n" IN_DECL,
+ OUT_DECL,
+ " getMatrix(int(in_boneIndices));\n",
+ OUT_CALC
+ ),
+
+ // 2 bones
+ BUILD_SHADER( "in " BONEINDEXTYPE2 " in_boneIndices;\n in vec2 in_boneWeights;\n" IN_DECL,
+ OUT_DECL,
+ " getMatrix(int(in_boneIndices.x)) * in_boneWeights[0] + \n" \
+ " getMatrix(int(in_boneIndices.y)) * in_boneWeights[1] ;\n ",
+ OUT_CALC
+ ),
+ // 3 bones
+ {"", "", "", "", "", ""},
+
+ // 4 bones
+ BUILD_SHADER( "in " BONEINDEXTYPE4 " in_boneIndices;\n in vec4 in_boneWeights;\n" IN_DECL,
+ OUT_DECL,
+ " getMatrix(int(in_boneIndices.x)) * in_boneWeights[0] + \n" \
+ " getMatrix(int(in_boneIndices.y)) * in_boneWeights[1] + \n" \
+ " getMatrix(int(in_boneIndices.z)) * in_boneWeights[2] + \n" \
+ " getMatrix(int(in_boneIndices.w)) * in_boneWeights[3] ;\n",
+ OUT_CALC
+ )
+ },
+// TFSHADER_POSNORMTAN
+ {
+#undef IN_DECL
+#undef OUT_DECL
+#undef OUT_CALC
+#define IN_DECL "in vec3 in_normal;\n in vec4 in_tangent;\n"
+#define OUT_DECL "out vec3 out_normal;\n out vec4 out_tangent;\n"
+#define OUT_CALC " out_normal = normalize( ( vec4(in_normal.xyz, 0.0) * localToWorldMatrix)).xyz;\n" \
+ " out_tangent = vec4( normalize( ( vec4(in_tangent.xyz, 0.0) * localToWorldMatrix)).xyz, in_tangent.w);\n"
+ // 1 bone
+ BUILD_SHADER( "in " BONEINDEXTYPE1 " in_boneIndices;\n" IN_DECL,
+ OUT_DECL,
+ " getMatrix(int(in_boneIndices));\n",
+ OUT_CALC
+ ),
+
+ // 2 bones
+ BUILD_SHADER( "in " BONEINDEXTYPE2 " in_boneIndices;\n in vec2 in_boneWeights;\n" IN_DECL,
+ OUT_DECL,
+ " getMatrix(int(in_boneIndices.x)) * in_boneWeights[0] + \n" \
+ " getMatrix(int(in_boneIndices.y)) * in_boneWeights[1] ;\n ",
+ OUT_CALC
+ ),
+ // 3 bones
+ {"", "", "", "", "", ""},
+
+ // 4 bones
+ BUILD_SHADER( "in " BONEINDEXTYPE4 " in_boneIndices;\n in vec4 in_boneWeights;\n" IN_DECL,
+ OUT_DECL,
+ " getMatrix(int(in_boneIndices.x)) * in_boneWeights[0] + \n" \
+ " getMatrix(int(in_boneIndices.y)) * in_boneWeights[1] + \n" \
+ " getMatrix(int(in_boneIndices.z)) * in_boneWeights[2] + \n" \
+ " getMatrix(int(in_boneIndices.w)) * in_boneWeights[3] ;\n",
+ OUT_CALC
+ )
+ }
+
+#undef IN_DECL
+#undef OUT_DECL
+#undef OUT_CALC
+
+};
+
+#undef BUILD_SHADER
+#undef BUILD_SHADER_2
+#undef STRINGIFY
+#undef MATRIX_DECL
+#undef BUILD_MATRIX
+
+static const char skinFS[] =
+ "#version 300 es\n"
+ "\n"
+ "precision lowp float;\n"
+ "out vec4 outcol;\n"
+ "void main(void) { outcol = vec4(1.0, 1.0, 1.0, 1.0); }\n";
+
+enum TfSkinShaderChannel
+{
+ kTFC_Position = VERTEX_FORMAT1(Vertex),
+ kTFC_Normal = VERTEX_FORMAT1(Normal),
+ kTFC_Tangent = VERTEX_FORMAT1(Tangent)
+};
+
+static GLuint tfTransformFeedback = 0;
+static GLuint GetTransformFeedbackObject(void)
+{
+ if(!tfTransformFeedback)
+ GLES_CHK(glGenTransformFeedbacks(1, &tfTransformFeedback));
+ return tfTransformFeedback;
+}
+
+// Note: we might not support all formats all the time.
+static bool DoesVertexFormatQualifyForTransformFeedback(UInt32 shaderChannelsMap)
+{
+ // Must have position, and if has tangents, must have normals as well.
+ bool qualify = (shaderChannelsMap & kTFC_Position) != 0;
+ if ((shaderChannelsMap & kTFC_Tangent) != 0)
+ qualify &= (shaderChannelsMap & kTFC_Normal) != 0;
+
+ return qualify;
+
+}
+
+static UInt32 roundUpToNextPowerOf2(UInt32 in)
+{
+ // Round up to nearest power of 2
+ // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
+ in--;
+ in |= in >> 1;
+ in |= in >> 2;
+ in |= in >> 4;
+ in |= in >> 8;
+ in |= in >> 16;
+ in++;
+ return in;
+}
+// Get the bones bit index based on bone count. Assumes bonecount is power of 2
+static int getBonesBits(UInt32 boneCount)
+{
+ // Calculate ln2
+ // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn
+
+ static const int MultiplyDeBruijnBitPosition2[32] =
+ {
+ 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
+ 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
+ };
+ UInt32 res = MultiplyDeBruijnBitPosition2[(UInt32)(boneCount * 0x077CB531U) >> 27];
+
+ if(res < 5) // Minimum size is 32 (= 0)
+ return 0;
+
+ return res-5; // Adjust so that 32 = 0, 64 = 1 etc.
+}
+
+static void print_long_string(std::string in)
+{
+ int offs = 0;
+ int len = in.length();
+ const int split = 200;
+ do
+ {
+ printf_console(in.substr(offs, split).c_str());
+ offs+=split;
+ } while (offs < len);
+
+}
+
+// maxBonesBits == Max bone count: 0 = 32, 1 = 64, etc until 5 = 1024
+static TFShader * GetTransformFeedbackShaderProgram(UInt32 shaderChannelsMap, UInt32 bonesPerVertex, UInt32 maxBonesBits)
+{
+ // Check if already created
+ TFShaderMap::iterator itr = tfShaders.find(shaderChannelsMap + (bonesPerVertex << 16) + (maxBonesBits << 19));
+ if(itr != tfShaders.end())
+ {
+ return &(itr->second);
+ }
+
+ // There are only 3 different combinations, and they are always in order. We'll just cut the array length at the call site.
+ const char *varyings[] = {"out_pos", "out_normal", "out_tangent"};
+ GLuint varyingCount = 0;
+ int shaderIdx = 0;
+ if(shaderChannelsMap & kTFC_Tangent)
+ {
+ shaderIdx = TFSHADER_POSNORMTAN;
+ varyingCount = 3;
+ }
+ else if(shaderChannelsMap & kTFC_Normal)
+ {
+ shaderIdx = TFSHADER_POSNORM;
+ varyingCount = 2;
+ }
+ else
+ {
+ shaderIdx = TFSHADER_POS;
+ varyingCount = 1;
+ }
+
+ TFShader res;
+
+ GLint status = 0;
+ GLint shaderLen = 0;
+ const char *code;
+ int i;
+ // Create the fragment shader if it doesn't exist already
+ if(tfFragShader == 0)
+ {
+ tfFragShader = glCreateShader(GL_FRAGMENT_SHADER);
+ shaderLen = strlen(skinFS);
+ code = &skinFS[0];
+ GLES_CHK(glShaderSource(tfFragShader, 1, &code, &shaderLen));
+ GLES_CHK(glCompileShader(tfFragShader));
+ glGetShaderiv(tfFragShader, GL_COMPILE_STATUS, &status);
+ if(status != GL_TRUE)
+ {
+ char temp[512] = "";
+ GLint len = 512;
+ glGetShaderInfoLog(tfFragShader, 512, &len, temp );
+
+ printf_console("ERROR: Unable to compile Transform Feedback fragment shader!\n Error log:\n%s", temp);
+ return 0;
+ }
+ }
+ res.program = glCreateProgram();
+ res.vertShader = glCreateShader(GL_VERTEX_SHADER);
+ shaderLen = strlen(tfShaderSource[shaderIdx][bonesPerVertex-1][maxBonesBits]);
+ code = &tfShaderSource[shaderIdx][bonesPerVertex-1][maxBonesBits][0];
+ GLES_CHK(glShaderSource(res.vertShader, 1, &code, &shaderLen));
+ GLES_CHK(glCompileShader(res.vertShader));
+ glGetShaderiv(res.vertShader, GL_COMPILE_STATUS, &status);
+ if(status != GL_TRUE)
+ {
+ char temp[512] = "";
+ GLint len = 512;
+ glGetShaderInfoLog(res.vertShader, 512, &len, temp );
+
+ printf_console("ERROR: Unable to compile Transform Feedback vertex shader!\n Error log:\n%s", temp);
+ print_long_string(code);
+ return 0;
+ }
+ GLES_CHK(glAttachShader(res.program, res.vertShader));
+ GLES_CHK(glAttachShader(res.program, tfFragShader));
+
+ GLES_CHK(glTransformFeedbackVaryings(res.program, varyingCount, varyings, GL_INTERLEAVED_ATTRIBS));
+
+ GLES_CHK(glLinkProgram(res.program));
+
+ glGetProgramiv(res.program, GL_LINK_STATUS, &status);
+ if(status != GL_TRUE)
+ {
+ char temp[512] = "";
+ GLint len = 512;
+ glGetProgramInfoLog(res.program, 512, &len, temp );
+ printf_console("ERROR: Unable to link Transform Feedback shader! Error: \n%s", temp);
+ print_long_string(code);
+ return 0;
+ }
+
+#if USE_UNIFORM_BLOCK_FOR_BONES
+ res.bonesLocation = glGetUniformBlockIndex(res.program, "MtxBlock");
+#else
+ res.bonesLocation = glGetUniformLocation(res.program, "bones");
+#endif
+
+ // Get the attribute locations. Some of these may be missing so clear the glerror afterwards
+ for(i = 0; i < TFATTRLOC_SIZE; i++)
+ {
+ res.attribLocations[i] = glGetAttribLocation(res.program, tfShaderAttribNames[i]);
+ }
+ // Clear gl error
+ glGetError();
+
+ // Insert into map and return
+ return &(tfShaders.insert(std::make_pair(shaderChannelsMap + (bonesPerVertex << 16) + (maxBonesBits << 19), res)).first->second);
+
+}
+
+static void ReleaseShader(std::pair<UInt32, TFShader> it)
+{
+ it.second.Release();
+}
+
+void TransformFeedbackSkinningInfo::CleanupTransformFeedbackShaders(void)
+{
+ std::for_each(tfShaders.begin(), tfShaders.end(), ReleaseShader);
+ tfShaders.clear();
+
+ if(tfTransformFeedback)
+ {
+ glDeleteTransformFeedbacks(1, &tfTransformFeedback);
+ tfTransformFeedback = NULL;
+ }
+ if(tfFragShader)
+ {
+ glDeleteShader(tfFragShader);
+ tfFragShader = 0;
+ }
+}
+
+TransformFeedbackSkinningInfo::~TransformFeedbackSkinningInfo()
+{
+#define DEL_BUFFER(x) if(x != 0) { GLES_CHK(glDeleteBuffers(1, &x)); x = 0; }
+ DEL_BUFFER(m_SourceVBO);
+#undef DEL_BUFFER
+ if(m_MatrixBuffer)
+ m_MatrixBuffer->Release();
+}
+
+//! Get Vertex size in floats
+UInt32 TransformFeedbackSkinningInfo::GetVertexSize()
+{
+ // Vertex data size
+ UInt32 res = (GetStride() / 4);
+ // Add skin info size
+ if(GetBonesPerVertex() == 1)
+ return res + 1; // Index
+ else if(GetBonesPerVertex() == 2)
+ return res + 4; // 2 indices, 2 weights
+ else
+ return res + 8; // 4 indices, 4 weights
+}
+
+bool TransformFeedbackSkinningInfo::EnsureBuffer()
+{
+ bool dirty = false;
+ if(m_SourceVBO == 0)
+ {
+ GLES_CHK(glGenBuffers(1, &m_SourceVBO));
+ dirty = true;
+ }
+ GLsizei size = GetVertexSize() * GetVertexCount() * sizeof(float);
+ if(m_SourceVBOSize < size)
+ {
+ GLES_CHK(glBindBuffer(GL_UNIFORM_BUFFER, m_SourceVBO));
+ GLES_CHK(glBufferData(GL_UNIFORM_BUFFER, size, NULL, GL_STATIC_DRAW));
+ m_SourceVBOSize = size;
+ dirty = true;
+ GLES_CHK(glBindBuffer(GL_UNIFORM_BUFFER, 0));
+ }
+ return dirty;
+}
+
+
+void TransformFeedbackSkinningInfo::UpdateSourceData(const void *vertData, const BoneInfluence *skinData, bool dirty)
+{
+ dirty |= EnsureBuffer();
+
+ if(!dirty)
+ return;
+
+ std::vector<float> vboData;
+ vboData.resize(GetVertexSize() * GetVertexCount());
+
+ float *dest = &vboData[0];
+ float *vertsrc = (float *)vertData;
+ int vertsize = GetStride() / sizeof(float);
+ const BoneInfluence *bonesrc4 = skinData;
+ const BoneInfluence2 *bonesrc2 = (BoneInfluence2 *)skinData;
+ const int *bonesrc1 = (int *)skinData;
+
+ for(int i = 0; i < GetVertexCount(); i++)
+ {
+ std::copy(vertsrc, vertsrc+vertsize, dest);
+ dest += vertsize;
+ vertsrc += vertsize;
+ switch(GetBonesPerVertex())
+ {
+ default:
+ case 1:
+#if USE_INT_ATTRIBS
+ memcpy(dest, bonesrc1, sizeof(int));
+ dest++;
+ bonesrc1++;
+#else
+ *(dest++) = (float) *(bonesrc1++);
+#endif
+ break;
+ case 2:
+ // Copy weights
+ std::copy(&bonesrc2->weight[0], (&bonesrc2->weight[0])+2, dest);
+ dest += 2;
+#if USE_INT_ATTRIBS
+ memcpy(dest, &bonesrc2->boneIndex[0], sizeof(int)*2);
+ dest+= 2;
+#else
+ *(dest++) = (float) bonesrc2->boneIndex[0];
+ *(dest++) = (float) bonesrc2->boneIndex[1];
+#endif
+ bonesrc2++;
+
+ break;
+ case 4:
+ // Copy weights
+ std::copy(&bonesrc4->weight[0], (&bonesrc4->weight[0])+4, dest);
+ dest += 4;
+#if USE_INT_ATTRIBS
+ memcpy(dest, &bonesrc4->boneIndex[0], sizeof(int)*4);
+ dest+= 4;
+#else
+ *(dest++) = (float) bonesrc4->boneIndex[0];
+ *(dest++) = (float) bonesrc4->boneIndex[1];
+ *(dest++) = (float) bonesrc4->boneIndex[2];
+ *(dest++) = (float) bonesrc4->boneIndex[3];
+#endif
+ bonesrc4++;
+
+ break;
+ }
+ }
+ GLES_CHK(glBindBuffer(GL_UNIFORM_BUFFER, m_SourceVBO));
+ GLES_CHK(glBufferSubData(GL_UNIFORM_BUFFER, 0, vboData.size() * sizeof(float), &vboData[0]));
+ GLES_CHK(glBindBuffer(GL_UNIFORM_BUFFER, 0));
+
+}
+
+
+void TransformFeedbackSkinningInfo::UpdateSourceBones( const int boneCount, const Matrix4x4f* cachedPose )
+{
+ int i;
+ int inputSize = boneCount * 4 * 3 * sizeof(float);
+
+#if USE_UNIFORM_BLOCK_FOR_BONES
+ m_BoneCount = roundUpToNextPowerOf2(boneCount);
+#else
+ m_BoneCount = 82;
+#endif
+
+ UInt32 realBufSize = m_BoneCount * 4 * 3 * sizeof(float);
+
+ // This basically shouldn't happen but just in case (should be released in SkinMesh)
+ if(m_MatrixBuffer)
+ {
+ m_MatrixBuffer->Release();
+ }
+
+ float *dest = NULL;
+
+#if USE_UNIFORM_BLOCK_FOR_BONES
+ m_MatrixBuffer = GetBufferManagerGLES30()->AcquireBuffer(realBufSize, GL_DYNAMIC_DRAW);
+
+ if(gGraphicsCaps.gles30.useMapBuffer)
+ {
+ m_MatrixBuffer->RecreateStorage(realBufSize, GL_DYNAMIC_DRAW);
+ dest = (float *)m_MatrixBuffer->Map(0, realBufSize, GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_BUFFER_BIT);
+ }
+ else
+#endif
+ {
+ m_CachedPose.resize(realBufSize / sizeof(float));
+ dest = &m_CachedPose[0];
+ }
+
+ int realBoneCount = boneCount;
+ if(boneCount > m_BoneCount)
+ realBoneCount = m_BoneCount;
+
+ for(i = 0; i < realBoneCount; i++)
+ {
+ Matrix4x4f mat = cachedPose[i];
+ mat.Transpose();
+ float *src = mat.GetPtr();
+ std::copy(src, src+12, dest);
+ dest+=12;
+ }
+
+#if USE_UNIFORM_BLOCK_FOR_BONES
+ if(gGraphicsCaps.gles30.useMapBuffer)
+ {
+ m_MatrixBuffer->Unmap();
+ }
+ else
+ {
+ m_MatrixBuffer->RecreateWithData(realBufSize, GL_DYNAMIC_DRAW, (void *)&m_CachedPose[0]);
+ }
+ m_MatrixBuffer->RecordUpdate();
+#endif
+}
+
+// In GfxDeviceGLES30.cpp
+void GLSLUseProgramGLES30(UInt32 programID);
+
+void TransformFeedbackSkinningInfo::SkinMesh( bool last )
+{
+
+ static GLuint s_WorkaroundTFBuf = 0;
+
+ // Qualcomm, srsly?
+ if(s_WorkaroundTFBuf == 0)
+ {
+ glGenBuffers(1, &s_WorkaroundTFBuf);
+ glBindBuffer(GL_COPY_WRITE_BUFFER, s_WorkaroundTFBuf);
+ glBufferData(GL_COPY_WRITE_BUFFER, 1024, NULL, GL_DYNAMIC_DRAW);
+ glBindBuffer(GL_COPY_WRITE_BUFFER, 0);
+ }
+
+#if USE_UNIFORM_BLOCK_FOR_BONES
+ TFShader *shd = GetTransformFeedbackShaderProgram(GetChannelMap(), GetBonesPerVertex(), getBonesBits(m_BoneCount));
+#else
+ TFShader *shd = GetTransformFeedbackShaderProgram(GetChannelMap(), GetBonesPerVertex(), 0);
+#endif
+
+ Assert(shd);
+
+ GLuint tf = GetTransformFeedbackObject();
+ GLES3VBO *vbo = static_cast<GLES3VBO *>(GetDestVBO());
+ GLuint glvbo = vbo->GetSkinningTargetVBO();
+
+ GLES_CHK(glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, glvbo));
+
+
+ GLSLUseProgramGLES30(shd->program);
+
+#if USE_UNIFORM_BLOCK_FOR_BONES
+ GLES_CHK(glUniformBlockBinding(shd->program, shd->bonesLocation, 0));
+
+ if(m_MatrixBuffer)
+ GLES_CHK(glBindBufferBase(GL_UNIFORM_BUFFER, 0, m_MatrixBuffer->GetBuffer()));
+#else
+
+ GLES_CHK(glUniform4fv(shd->bonesLocation, m_CachedPose.size() / 4, &m_CachedPose[0]));
+#endif
+
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, m_SourceVBO));
+ GLuint stride = GetVertexSize() * sizeof(float);
+ GLES_CHK(glVertexAttribPointer(shd->attribLocations[TFATTRLOC_POS], 3, GL_FLOAT, GL_FALSE, stride, 0));
+ GLES_CHK(glEnableVertexAttribArray(shd->attribLocations[TFATTRLOC_POS]));
+
+ GLuint nextoffs = 12;
+
+ if(GetChannelMap() & kTFC_Normal)
+ {
+ GLES_CHK(glVertexAttribPointer(shd->attribLocations[TFATTRLOC_NORM], 3, GL_FLOAT, GL_FALSE, stride, (void *)nextoffs));
+ GLES_CHK(glEnableVertexAttribArray(shd->attribLocations[TFATTRLOC_NORM]));
+ nextoffs += 12;
+ }
+ if(GetChannelMap() & kTFC_Tangent)
+ {
+ GLES_CHK(glVertexAttribPointer(shd->attribLocations[TFATTRLOC_TAN], 4, GL_FLOAT, GL_FALSE, stride, (void *)nextoffs));
+ GLES_CHK(glEnableVertexAttribArray(shd->attribLocations[TFATTRLOC_TAN]));
+ nextoffs += 16;
+ }
+
+ switch(GetBonesPerVertex())
+ {
+ default:
+ case 1:
+#if USE_INT_ATTRIBS
+ GLES_CHK(glVertexAttribIPointer(shd->attribLocations[TFATTRLOC_BONEIDX], 1, GL_INT, stride, (void *)nextoffs));
+#else
+ GLES_CHK(glVertexAttribPointer(shd->attribLocations[TFATTRLOC_BONEIDX], 1, GL_FLOAT, GL_FALSE, stride, (void *)nextoffs));
+#endif
+ GLES_CHK(glEnableVertexAttribArray(shd->attribLocations[TFATTRLOC_BONEIDX]));
+
+ break;
+ case 2:
+ GLES_CHK(glVertexAttribPointer(shd->attribLocations[TFATTRLOC_BONEWEIGHT], 2, GL_FLOAT, GL_FALSE, stride,(void *)nextoffs));
+ GLES_CHK(glEnableVertexAttribArray(shd->attribLocations[TFATTRLOC_BONEWEIGHT]));
+ nextoffs += 8;
+#if USE_INT_ATTRIBS
+ GLES_CHK(glVertexAttribIPointer(shd->attribLocations[TFATTRLOC_BONEIDX], 2, GL_INT, stride, (void *)nextoffs));
+#else
+ GLES_CHK(glVertexAttribPointer(shd->attribLocations[TFATTRLOC_BONEIDX], 2, GL_FLOAT, GL_FALSE, stride, (void *)nextoffs));
+#endif
+ GLES_CHK(glEnableVertexAttribArray(shd->attribLocations[TFATTRLOC_BONEIDX]));
+ break;
+
+ case 4:
+ GLES_CHK(glVertexAttribPointer(shd->attribLocations[TFATTRLOC_BONEWEIGHT], 4, GL_FLOAT, GL_FALSE, stride,(void *)nextoffs));
+ GLES_CHK(glEnableVertexAttribArray(shd->attribLocations[TFATTRLOC_BONEWEIGHT]));
+ nextoffs += 16;
+#if USE_INT_ATTRIBS
+ GLES_CHK(glVertexAttribIPointer(shd->attribLocations[TFATTRLOC_BONEIDX], 4, GL_INT, stride, (void *)nextoffs));
+#else
+ GLES_CHK(glVertexAttribPointer(shd->attribLocations[TFATTRLOC_BONEIDX], 4, GL_FLOAT,GL_FALSE, stride, (void *)nextoffs));
+#endif
+ GLES_CHK(glEnableVertexAttribArray(shd->attribLocations[TFATTRLOC_BONEIDX]));
+
+ break;
+ }
+
+ GLES_CHK(glBeginTransformFeedback(GL_POINTS));
+
+ GLES_CHK(glEnable(GL_RASTERIZER_DISCARD));
+ GLES_CHK(glDrawArrays(GL_POINTS, 0, GetVertexCount()));
+ GLES_CHK(glDisable(GL_RASTERIZER_DISCARD));
+
+ GLES_CHK(glEndTransformFeedback());
+
+
+ GLES_CHK(glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, s_WorkaroundTFBuf));
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, 0));
+
+#if USE_UNIFORM_BLOCK_FOR_BONES
+
+ GLES_CHK(glBindBufferBase(GL_UNIFORM_BUFFER, 0, 0));
+ if(m_MatrixBuffer)
+ {
+ m_MatrixBuffer->RecordRender();
+ m_MatrixBuffer->Release();
+ m_MatrixBuffer = NULL;
+ }
+#endif
+ InvalidateVertexInputCacheGLES30();
+}
diff --git a/Runtime/GfxDevice/opengles30/TransformFeedbackSkinnedMesh.h b/Runtime/GfxDevice/opengles30/TransformFeedbackSkinnedMesh.h
new file mode 100644
index 0000000..3e0c094
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/TransformFeedbackSkinnedMesh.h
@@ -0,0 +1,47 @@
+#ifndef __TRANSFORMFEEDBACKSKINNEDMESH_H__
+#define __TRANSFORMFEEDBACKSKINNEDMESH_H__
+
+#include "Runtime/GfxDevice/GPUSkinningInfo.h"
+#include "Runtime/GfxDevice/opengles30/IncludesGLES30.h"
+#include "Runtime/GfxDevice/opengles30/DataBuffersGLES30.h"
+#include <vector>
+
+class GfxDeviceGLES30;
+
+// Transform Feedback mesh skinning data.
+// Source and destination VBO formats must match.
+class TransformFeedbackSkinningInfo : public GPUSkinningInfo
+{
+ friend class GfxDeviceGLES30;
+private:
+ GLuint m_SourceVBO;
+ GLsizei m_SourceVBOSize;
+
+ std::vector<float> m_CachedPose;
+
+ DataBufferGLES30 *m_MatrixBuffer;
+
+ //! Stores the bone count from the previous call to UpdateSourceBones. Used to select the most suitable shader version.
+ int m_BoneCount;
+
+ UInt32 GetVertexSize();
+
+ bool EnsureBuffer();
+
+ TransformFeedbackSkinningInfo() : GPUSkinningInfo(),
+ m_SourceVBO(0), m_SourceVBOSize(0), m_MatrixBuffer(0), m_BoneCount(0)
+ {}
+
+ virtual ~TransformFeedbackSkinningInfo();
+
+ virtual void UpdateSourceData(const void *vertData, const BoneInfluence *skinData, bool dirty);
+ virtual void UpdateSourceBones(const int boneCount, const Matrix4x4f* cachedPose);
+ virtual void SkinMesh(bool last);
+
+public:
+
+ //! Release shaders that have been created for transform feedback use.
+ static void CleanupTransformFeedbackShaders(void);
+};
+
+#endif
diff --git a/Runtime/GfxDevice/opengles30/UnityGLES30Ext.cpp b/Runtime/GfxDevice/opengles30/UnityGLES30Ext.cpp
new file mode 100644
index 0000000..37fcb09
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/UnityGLES30Ext.cpp
@@ -0,0 +1,14 @@
+#include "UnityPrefix.h"
+#include "IncludesGLES30.h"
+#include "UnityGLES30Ext.h"
+#include "Runtime/GfxDevice/opengles/ExtensionsGLES.h"
+
+void Gles3ExtFunc::InitExtFunc()
+{
+ glPushGroupMarkerEXT = (glPushGroupMarkerEXTFunc)GetGLExtProcAddress("glPushGroupMarkerEXT");
+ glPopGroupMarkerEXT = (glPopGroupMarkerEXTFunc)GetGLExtProcAddress("glPopGroupMarkerEXT");
+
+ glAlphaFuncQCOM = (glAlphaFuncQCOMFunc)GetGLExtProcAddress("glAlphaFuncQCOM");
+}
+
+Gles3ExtFunc gGles3ExtFunc;
diff --git a/Runtime/GfxDevice/opengles30/UnityGLES30Ext.h b/Runtime/GfxDevice/opengles30/UnityGLES30Ext.h
new file mode 100644
index 0000000..0c8bc64
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/UnityGLES30Ext.h
@@ -0,0 +1,276 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/GfxDevice/opengles/ExtensionsGLES.h"
+
+// The reason to have this file is that we use plenty of gles extensions,
+// and not all of them are handled in (all) sdk we build with.
+// Still we do guard all the usages with runtime ifs, so there is no need to intro addidtional preprocessor magic
+
+
+// ----------------------------------------------------------------------------
+// Texture Formats
+//
+
+#ifndef GL_BGRA_EXT
+ #define GL_BGRA_EXT 0x80E1
+#endif
+#ifndef GL_ETC1_RGB8_OES
+ #define GL_ETC1_RGB8_OES 0x8D64
+#endif
+#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT
+ #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
+#endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
+ #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
+#endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
+ #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
+#endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
+ #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
+#endif
+#ifndef GL_COMPRESSED_SRGB_S3TC_DXT1_NV
+ #define GL_COMPRESSED_SRGB_S3TC_DXT1_NV 0x8C4C
+#endif
+#ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_NV
+ #define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_NV 0x8C4D
+#endif
+#ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_NV
+ #define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_NV 0x8C4E
+#endif
+#ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_NV
+ #define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_NV 0x8C4F
+#endif
+#ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG
+ #define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
+#endif
+#ifndef GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG
+ #define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
+#endif
+#ifndef GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG
+ #define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
+#endif
+#ifndef GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG
+ #define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
+#endif
+#ifndef GL_ATC_RGB_AMD
+ #define GL_ATC_RGB_AMD 0x8C92
+#endif
+#ifndef GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD
+ #define GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE
+#endif
+
+#ifndef GL_HALF_FLOAT_OES
+ #define GL_HALF_FLOAT_OES 0x8D61
+#endif
+#ifndef GL_SRGB_EXT
+ #define GL_SRGB_EXT 0x8C40
+#endif
+#ifndef GL_SRGB_ALPHA_EXT
+ #define GL_SRGB_ALPHA_EXT 0x8C42
+#endif
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_blend_minmax
+//
+
+#ifndef GL_MIN_EXT
+ #define GL_MIN_EXT 0x8007
+#endif
+
+#ifndef GL_MAX_EXT
+ #define GL_MAX_EXT 0x8008
+#endif
+
+// ----------------------------------------------------------------------------
+// GL_EXT_debug_marker
+//
+
+typedef void (*glPushGroupMarkerEXTFunc)(int len, const char* name);
+typedef void (*glPopGroupMarkerEXTFunc)();
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_discard_framebuffer
+//
+
+#ifndef GL_COLOR_EXT
+ #define GL_COLOR_EXT 0x1800
+#endif
+#ifndef GL_DEPTH_EXT
+ #define GL_DEPTH_EXT 0x1801
+#endif
+#ifndef GL_STENCIL_EXT
+ #define GL_STENCIL_EXT 0x1802
+#endif
+
+typedef void (*glDiscardFramebufferEXTFunc)(GLenum target, GLsizei numAttachments, const GLenum *attachments);
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_occlusion_query_boolean
+// Note: while we dont use occlusion queries, all queries ext are based on that one
+//
+
+#ifndef GL_QUERY_RESULT_EXT
+ #define GL_QUERY_RESULT_EXT 0x8866
+#endif
+#ifndef GL_QUERY_RESULT_AVAILABLE_EXT
+ #define GL_QUERY_RESULT_AVAILABLE_EXT 0x8867
+#endif
+
+typedef void (*glGenQueriesEXTFunc)(GLuint n, GLuint *ids);
+typedef void (*glDeleteQueriesEXTFunc)(GLuint n, const GLuint *ids);
+typedef void (*glBeginQueryEXTFunc)(GLuint target, GLuint id);
+typedef void (*glEndQueryEXTFunc)(GLuint target);
+typedef void (*glGetQueryObjectuivEXTFunc)(GLuint id, GLuint pname, GLuint *params);
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_shadow_samplers
+//
+
+#ifndef GL_TEXTURE_COMPARE_MODE_EXT
+ #define GL_TEXTURE_COMPARE_MODE_EXT 0x884C
+#endif
+#ifndef GL_TEXTURE_COMPARE_FUNC_EXT
+ #define GL_TEXTURE_COMPARE_FUNC_EXT 0x884D
+#endif
+#ifndef GL_COMPARE_REF_TO_TEXTURE_EXT
+ #define GL_COMPARE_REF_TO_TEXTURE_EXT 0x884E
+#endif
+#ifndef GL_SAMPLER_2D_SHADOW_EXT
+ #define GL_SAMPLER_2D_SHADOW_EXT 0x8B62
+#endif
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_texture_rg
+//
+
+#ifndef GL_RED_EXT
+ #define GL_RED_EXT 0x1903
+#endif
+#ifndef GL_RG_EXT
+ #define GL_RG_EXT 0x8227
+#endif
+
+
+// ----------------------------------------------------------------------------
+// GL_OES_get_program_binary
+//
+
+#ifndef GL_PROGRAM_BINARY_LENGTH_OES
+ #define GL_PROGRAM_BINARY_LENGTH_OES 0x8741
+#endif
+
+#ifndef GL_NUM_PROGRAM_BINARY_FORMATS_OES
+ #define GL_NUM_PROGRAM_BINARY_FORMATS_OES 0x87FE
+#endif
+
+typedef void (*glGetProgramBinaryOESFunc)(GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, GLvoid* binary);
+typedef void (*glProgramBinaryOESFunc)(GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length);
+
+
+// ----------------------------------------------------------------------------
+// GL_OES_mapbuffer
+//
+
+#ifndef GL_WRITE_ONLY_OES
+ #define GL_WRITE_ONLY_OES 0x88B9
+#endif
+
+typedef void* (*glMapBufferOESFunc)(GLenum target, GLenum access);
+typedef GLboolean (*glUnmapBufferOESFunc)(GLenum target);
+
+
+// ----------------------------------------------------------------------------
+// GL_APPLE_framebuffer_multisample
+//
+
+#ifndef GL_MAX_SAMPLES_APPLE
+ #define GL_MAX_SAMPLES_APPLE 0x8D57
+#endif
+#ifndef GL_READ_FRAMEBUFFER_APPLE
+ #define GL_READ_FRAMEBUFFER_APPLE 0x8CA8
+#endif
+#ifndef GL_DRAW_FRAMEBUFFER_APPLE
+ #define GL_DRAW_FRAMEBUFFER_APPLE 0x8CA9
+#endif
+
+
+typedef void (*glRenderbufferStorageMultisampleAPPLEFunc)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
+typedef void (*glResolveMultisampleFramebufferAPPLEFunc)(void);
+
+
+// ----------------------------------------------------------------------------
+// GL_IMG_multisampled_render_to_texture
+//
+
+#ifndef GL_MAX_SAMPLES_IMG
+ #define GL_MAX_SAMPLES_IMG 0x9135
+#endif
+
+typedef void (*glRenderbufferStorageMultisampleIMGFunc)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
+typedef void (*glFramebufferTexture2DMultisampleIMGFunc)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples);
+
+
+// ----------------------------------------------------------------------------
+// GL_NV_draw_buffers
+//
+
+#ifndef GL_MAX_DRAW_BUFFERS_NV
+ #define GL_MAX_DRAW_BUFFERS_NV 0x8824
+#endif
+#ifndef GL_COLOR_ATTACHMENT0_NV
+ #define GL_COLOR_ATTACHMENT0_NV 0x8CE0
+#endif
+
+typedef void (*glDrawBuffersNVFunc)(int len, const GLenum* bufs);
+
+
+// ----------------------------------------------------------------------------
+// GL_NV_timer_query
+//
+
+#ifndef GL_TIME_ELAPSED_NV
+ #define GL_TIME_ELAPSED_NV 0x88BF
+#endif
+#ifndef GL_TIMESTAMP_NV
+ #define GL_TIMESTAMP_NV 0x8E28
+#endif
+
+typedef unsigned long long int EGLuint64NV;
+
+typedef void (*glQueryCounterNVFunc)(GLuint target, GLuint id);
+typedef void (*glGetQueryObjectui64vNVFunc)(GLuint id, GLuint pname, EGLuint64NV *params);
+
+
+// ----------------------------------------------------------------------------
+// GL_QCOM_alpha_test
+//
+
+#ifndef GL_ALPHA_TEST_QCOM
+ #define GL_ALPHA_TEST_QCOM 0x0BC0
+#endif
+
+typedef void (*glAlphaFuncQCOMFunc)(GLenum func, GLfloat ref);
+
+
+// ----------------------------------------------------------------------------
+// common place to get function pointers
+//
+
+struct
+Gles3ExtFunc
+{
+ glPushGroupMarkerEXTFunc glPushGroupMarkerEXT;
+ glPopGroupMarkerEXTFunc glPopGroupMarkerEXT;
+
+ glAlphaFuncQCOMFunc glAlphaFuncQCOM;
+
+ void InitExtFunc();
+};
+extern Gles3ExtFunc gGles3ExtFunc;
+
diff --git a/Runtime/GfxDevice/opengles30/UtilsGLES30.cpp b/Runtime/GfxDevice/opengles30/UtilsGLES30.cpp
new file mode 100644
index 0000000..2c28c12
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/UtilsGLES30.cpp
@@ -0,0 +1,181 @@
+#include "UnityPrefix.h"
+#include "UtilsGLES30.h"
+
+#include "IncludesGLES30.h"
+
+UInt32 GetColorFormatGLES30 (RenderTextureFormat format)
+{
+ switch (format)
+ {
+ case kRTFormatARGB32: return GL_RGBA8;
+ case kRTFormatDepth: return GL_NONE;
+ case kRTFormatARGBHalf: return GL_RGBA16F;
+ case kRTFormatShadowMap: return GL_NONE;
+ case kRTFormatRGB565: return GL_RGB565;
+ case kRTFormatARGB4444: return GL_RGBA4;
+ case kRTFormatARGB1555: return GL_RGB5_A1;
+ case kRTFormatDefault: return GL_RGBA8;
+ case kRTFormatA2R10G10B10: return GL_RGB10_A2;
+ case kRTFormatDefaultHDR: return GL_R11F_G11F_B10F;
+ case kRTFormatARGB64: return GL_RGBA8;
+ case kRTFormatARGBFloat: return GL_RGBA32F;
+ case kRTFormatRGFloat: return GL_RG32F;
+ case kRTFormatRGHalf: return GL_RG16F;
+ case kRTFormatRFloat: return GL_R32F;
+ case kRTFormatRHalf: return GL_R16F;
+ case kRTFormatR8: return GL_R8;
+ case kRTFormatARGBInt: return GL_RGBA32I;
+ case kRTFormatRGInt: return GL_RG32I;
+ case kRTFormatRInt: return GL_R32I;
+ default:
+ Assert("Invalid RenderTextureFormat");
+ return GL_RGBA8;
+ }
+}
+
+UInt32 GetDepthOnlyFormatGLES30 (DepthBufferFormat format)
+{
+ switch (format)
+ {
+ case kDepthFormatNone: return GL_NONE;
+ case kDepthFormat16: return GL_DEPTH_COMPONENT16;
+ case kDepthFormat24: return GL_DEPTH_COMPONENT24;
+ default:
+ Assert("Invalid DepthBufferFormat");
+ return GL_NONE;
+ }
+}
+
+UInt32 GetDepthStencilFormatGLES30 (DepthBufferFormat format)
+{
+ switch (format)
+ {
+ case kDepthFormatNone: return GL_STENCIL_INDEX8;
+ case kDepthFormat16: return GL_DEPTH24_STENCIL8;
+ case kDepthFormat24: return GL_DEPTH24_STENCIL8;
+ default:
+ Assert("Invalid DepthBufferFormat");
+ return GL_NONE;
+ }
+}
+
+TransferFormatGLES30 GetTransferFormatGLES30 (UInt32 internalFormat)
+{
+ switch (internalFormat)
+ {
+ case GL_RGBA32F: return TransferFormatGLES30(GL_RGBA, GL_FLOAT );
+ case GL_RGBA32I: return TransferFormatGLES30(GL_RGBA_INTEGER, GL_INT );
+ case GL_RGBA32UI: return TransferFormatGLES30(GL_RGBA_INTEGER, GL_UNSIGNED_INT );
+ case GL_RGBA16F: return TransferFormatGLES30(GL_RGBA, GL_HALF_FLOAT );
+ case GL_RGBA16I: return TransferFormatGLES30(GL_RGBA_INTEGER, GL_SHORT );
+ case GL_RGBA16UI: return TransferFormatGLES30(GL_RGBA_INTEGER, GL_UNSIGNED_SHORT );
+ case GL_RGBA8: return TransferFormatGLES30(GL_RGBA, GL_UNSIGNED_BYTE );
+ case GL_RGBA8I: return TransferFormatGLES30(GL_RGBA_INTEGER, GL_BYTE );
+ case GL_RGBA8UI: return TransferFormatGLES30(GL_RGBA_INTEGER, GL_UNSIGNED_BYTE );
+ case GL_SRGB8_ALPHA8: return TransferFormatGLES30(GL_RGBA, GL_UNSIGNED_BYTE );
+ case GL_RGB10_A2: return TransferFormatGLES30(GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV );
+ case GL_RGB10_A2UI: return TransferFormatGLES30(GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV );
+ case GL_RGBA4: return TransferFormatGLES30(GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4 );
+ case GL_RGB5_A1: return TransferFormatGLES30(GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 );
+ case GL_RGBA8_SNORM: return TransferFormatGLES30(GL_RGBA, GL_BYTE );
+ case GL_RGB8: return TransferFormatGLES30(GL_RGB, GL_UNSIGNED_BYTE );
+ case GL_RGB565: return TransferFormatGLES30(GL_RGB, GL_UNSIGNED_SHORT_5_6_5 );
+ case GL_R11F_G11F_B10F: return TransferFormatGLES30(GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV );
+ case GL_RGB32F: return TransferFormatGLES30(GL_RGB, GL_FLOAT );
+ case GL_RGB32I: return TransferFormatGLES30(GL_RGB_INTEGER, GL_INT );
+ case GL_RGB32UI: return TransferFormatGLES30(GL_RGB_INTEGER, GL_UNSIGNED_INT );
+ case GL_RGB16F: return TransferFormatGLES30(GL_RGB, GL_HALF_FLOAT );
+ case GL_RGB16I: return TransferFormatGLES30(GL_RGB_INTEGER, GL_SHORT );
+ case GL_RGB16UI: return TransferFormatGLES30(GL_RGB_INTEGER, GL_UNSIGNED_SHORT );
+ case GL_RGB8_SNORM: return TransferFormatGLES30(GL_RGB, GL_BYTE );
+ case GL_RGB8I: return TransferFormatGLES30(GL_RGB_INTEGER, GL_BYTE );
+ case GL_RGB8UI: return TransferFormatGLES30(GL_RGB_INTEGER, GL_UNSIGNED_BYTE );
+ case GL_SRGB8: return TransferFormatGLES30(GL_RGB, GL_UNSIGNED_BYTE );
+ case GL_RGB9_E5: return TransferFormatGLES30(GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV );
+ case GL_RG32F: return TransferFormatGLES30(GL_RG, GL_FLOAT );
+ case GL_RG32I: return TransferFormatGLES30(GL_RG_INTEGER, GL_INT );
+ case GL_RG32UI: return TransferFormatGLES30(GL_RG_INTEGER, GL_UNSIGNED_INT );
+ case GL_RG16F: return TransferFormatGLES30(GL_RG, GL_HALF_FLOAT );
+ case GL_RG16I: return TransferFormatGLES30(GL_RG_INTEGER, GL_SHORT );
+ case GL_RG16UI: return TransferFormatGLES30(GL_RG_INTEGER, GL_UNSIGNED_SHORT );
+ case GL_RG8: return TransferFormatGLES30(GL_RG, GL_UNSIGNED_BYTE );
+ case GL_RG8I: return TransferFormatGLES30(GL_RG_INTEGER, GL_BYTE );
+ case GL_RG8UI: return TransferFormatGLES30(GL_RG_INTEGER, GL_UNSIGNED_BYTE );
+ case GL_RG8_SNORM: return TransferFormatGLES30(GL_RG, GL_BYTE );
+ case GL_R32F: return TransferFormatGLES30(GL_RED, GL_FLOAT );
+ case GL_R32I: return TransferFormatGLES30(GL_RED_INTEGER, GL_INT );
+ case GL_R32UI: return TransferFormatGLES30(GL_RED_INTEGER, GL_UNSIGNED_INT );
+ case GL_R16F: return TransferFormatGLES30(GL_RED, GL_HALF_FLOAT );
+ case GL_R16I: return TransferFormatGLES30(GL_RED_INTEGER, GL_SHORT );
+ case GL_R16UI: return TransferFormatGLES30(GL_RED_INTEGER, GL_UNSIGNED_SHORT );
+ case GL_R8: return TransferFormatGLES30(GL_RED, GL_UNSIGNED_BYTE );
+ case GL_R8I: return TransferFormatGLES30(GL_RED_INTEGER, GL_BYTE );
+ case GL_R8UI: return TransferFormatGLES30(GL_RED_INTEGER, GL_UNSIGNED_BYTE );
+ case GL_R8_SNORM: return TransferFormatGLES30(GL_RED, GL_BYTE );
+ case GL_DEPTH_COMPONENT32F: return TransferFormatGLES30(GL_DEPTH_COMPONENT, GL_FLOAT );
+ case GL_DEPTH_COMPONENT24: return TransferFormatGLES30(GL_DEPTH_COMPONENT, GL_UNSIGNED_INT );
+ case GL_DEPTH_COMPONENT16: return TransferFormatGLES30(GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT );
+ case GL_DEPTH32F_STENCIL8: return TransferFormatGLES30(GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV );
+ case GL_DEPTH24_STENCIL8: return TransferFormatGLES30(GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8 );
+ default: return TransferFormatGLES30(GL_NONE, GL_NONE );
+ }
+}
+
+UInt32 GetDefaultFramebufferColorFormatGLES30 (void)
+{
+ int redBits = 0;
+ int greenBits = 0;
+ int blueBits = 0;
+ int alphaBits = 0;
+
+ glGetIntegerv(GL_RED_BITS, &redBits);
+ glGetIntegerv(GL_GREEN_BITS, &greenBits);
+ glGetIntegerv(GL_BLUE_BITS, &blueBits);
+ glGetIntegerv(GL_ALPHA_BITS, &alphaBits);
+
+#define PACK_FMT(R, G, B, A) (((R) << 24) | ((G) << 16) | ((B) << 8) | (A))
+
+ // \note [pyry] This may not hold true on some implementations - best effort guess only.
+ switch (PACK_FMT(redBits, greenBits, blueBits, alphaBits))
+ {
+ case PACK_FMT(8,8,8,8): return GL_RGBA8;
+ case PACK_FMT(8,8,8,0): return GL_RGB8;
+ case PACK_FMT(4,4,4,4): return GL_RGBA4;
+ case PACK_FMT(5,5,5,1): return GL_RGB5_A1;
+ case PACK_FMT(5,6,5,0): return GL_RGB565;
+ default: return GL_NONE;
+ }
+
+#undef PACK_FMT
+}
+
+UInt32 GetDefaultFramebufferDepthFormatGLES30 (void)
+{
+ int depthBits = 0;
+ int stencilBits = 0;
+ glGetIntegerv(GL_DEPTH_BITS, &depthBits);
+ glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
+
+ if(stencilBits > 0)
+ {
+ switch (depthBits)
+ {
+ case 32: return GL_DEPTH32F_STENCIL8;
+ case 24: return GL_DEPTH24_STENCIL8;
+ case 16: return GL_DEPTH_COMPONENT16; // There's probably no such config?
+ default: return GL_NONE;
+ }
+
+ }
+ else
+ {
+ switch (depthBits)
+ {
+ case 32: return GL_DEPTH_COMPONENT32F;
+ case 24: return GL_DEPTH_COMPONENT24;
+ case 16: return GL_DEPTH_COMPONENT16;
+ default: return GL_NONE;
+ }
+
+ }
+}
diff --git a/Runtime/GfxDevice/opengles30/UtilsGLES30.h b/Runtime/GfxDevice/opengles30/UtilsGLES30.h
new file mode 100644
index 0000000..cde05de
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/UtilsGLES30.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include "UnityPrefix.h"
+#include "Runtime/Graphics/RenderTexture.h"
+
+// Shared GLES3 utilities
+
+class TransferFormatGLES30
+{
+public:
+ UInt32 format;
+ UInt32 dataType;
+
+ TransferFormatGLES30 (UInt32 format_, UInt32 dataType_)
+ : format (format_)
+ , dataType (dataType_)
+ {
+ }
+};
+
+// Map RenderTextureFormat to closest GL sized internal format.
+UInt32 GetColorFormatGLES30 (RenderTextureFormat format);
+
+// Get closest depth internal format.
+UInt32 GetDepthOnlyFormatGLES30 (DepthBufferFormat format);
+
+// Get closest depth&stencil internal format.
+UInt32 GetDepthStencilFormatGLES30 (DepthBufferFormat format);
+
+// Get transfer (upload) format, dataType pair for internal format.
+TransferFormatGLES30 GetTransferFormatGLES30 (UInt32 internalFormat);
+
+// Get default framebuffer (0) internal format (guess based on bits)
+UInt32 GetDefaultFramebufferColorFormatGLES30 (void);
+
+// Get default framebuffer (0) depth format
+UInt32 GetDefaultFramebufferDepthFormatGLES30 (void);
diff --git a/Runtime/GfxDevice/opengles30/VBOGLES30.cpp b/Runtime/GfxDevice/opengles30/VBOGLES30.cpp
new file mode 100644
index 0000000..24d4bba
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/VBOGLES30.cpp
@@ -0,0 +1,1351 @@
+#include "UnityPrefix.h"
+
+#if GFX_SUPPORTS_OPENGLES30
+#include "VBOGLES30.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "Runtime/Utilities/Prefetch.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/GfxDevice/BatchRendering.h"
+#include "IncludesGLES30.h"
+#include "AssertGLES30.h"
+#include "GpuProgramsGLES30.h"
+#include "DebugGLES30.h"
+#include "Runtime/Profiler/TimeHelper.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/GfxDevice/GLESChannels.h"
+#include "Runtime/GfxDevice/GLDataBufferCommon.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+
+#include <algorithm>
+
+#if 1
+ #define DBG_LOG_VBO_GLES30(...) {}
+#else
+ #define DBG_LOG_VBO_GLES30(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+#endif
+
+enum
+{
+ kDefaultBufferAlign = 64
+};
+
+template <typename T>
+inline T Align (T v, size_t alignment)
+{
+ return (v + (alignment-1)) & ~(alignment-1);
+}
+
+template <typename T>
+inline T AlignToDefault (T v)
+{
+ return Align(v, kDefaultBufferAlign);
+}
+
+// Comparison operators for VertexArrayInfoGLES30 (used in caching)
+
+static inline bool operator< (const VertexInputInfoGLES30& a, const VertexInputInfoGLES30& b)
+{
+ return a.componentType < b.componentType &&
+ a.numComponents < b.numComponents &&
+ a.pointer < b.pointer &&
+ a.stride < b.stride;
+}
+
+static inline bool operator!= (const VertexInputInfoGLES30& a, const VertexInputInfoGLES30& b)
+{
+ return a.componentType != b.componentType ||
+ a.numComponents != b.numComponents ||
+ a.pointer != b.pointer ||
+ a.stride != b.stride;
+}
+
+static inline bool operator== (const VertexInputInfoGLES30& a, const VertexInputInfoGLES30& b) { return !(a != b); }
+
+static bool operator< (const VertexArrayInfoGLES30& a, const VertexArrayInfoGLES30& b)
+{
+ if (a.enabledArrays < b.enabledArrays) return true;
+ else if (b.enabledArrays < a.enabledArrays) return false;
+
+ // Compare buffers first as they are more likely to not match.
+ for (int ndx = 0; ndx < kGLES3MaxVertexAttribs; ndx++)
+ {
+ if (a.enabledArrays & (1<<ndx))
+ {
+ if (a.buffers[ndx] < b.buffers[ndx]) return true;
+ else if (b.buffers[ndx] < a.buffers[ndx]) return false;
+ }
+ }
+
+ for (int ndx = 0; ndx < kGLES3MaxVertexAttribs; ndx++)
+ {
+ if (a.enabledArrays & (1<<ndx))
+ {
+ if (a.arrays[ndx] < b.arrays[ndx]) return true;
+ else if (b.arrays[ndx] < a.arrays[ndx]) return false;
+ }
+ }
+
+ return false; // Equal
+}
+
+static bool operator!= (const VertexArrayInfoGLES30& a, const VertexArrayInfoGLES30& b)
+{
+ if (a.enabledArrays != b.enabledArrays)
+ return true;
+
+ // Compare buffers first as they are more likely to not match.
+ for (int ndx = 0; ndx < kGLES3MaxVertexAttribs; ndx++)
+ {
+ if (a.enabledArrays & (1<<ndx))
+ {
+ if (a.buffers[ndx] != b.buffers[ndx])
+ return true;
+ }
+ }
+
+ for (int ndx = 0; ndx < kGLES3MaxVertexAttribs; ndx++)
+ {
+ if (a.enabledArrays & (1<<ndx))
+ {
+ if (a.arrays[ndx] != b.arrays[ndx])
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool operator== (const VertexArrayInfoGLES30& a, const VertexArrayInfoGLES30& b) { return !(a != b); }
+
+struct CompareVertexArrayInfoGLES30
+{
+ bool operator() (const VertexArrayInfoGLES30* a, const VertexArrayInfoGLES30* b) const;
+};
+
+class VertexArrayObjectGLES30
+{
+public:
+ VertexArrayObjectGLES30 (const VertexArrayInfoGLES30& info);
+ ~VertexArrayObjectGLES30 (void);
+
+ UInt32 GetVAO (void) const { return m_vao; }
+ const VertexArrayInfoGLES30* GetInfo (void) const { return &m_info; }
+
+private:
+ VertexArrayObjectGLES30 (const VertexArrayObjectGLES30& other); // Not allowed!
+ VertexArrayObjectGLES30& operator= (const VertexArrayObjectGLES30& other); // Not allowed!
+
+ VertexArrayInfoGLES30 m_info;
+ UInt32 m_vao;
+};
+
+typedef std::map<const VertexArrayInfoGLES30*, VertexArrayObjectGLES30*, CompareVertexArrayInfoGLES30> VertexArrayMapGLES30;
+
+// Utilities
+
+static const GLenum kTopologyGLES3[] =
+{
+ GL_TRIANGLES,
+ GL_TRIANGLE_STRIP,
+ GL_TRIANGLES,
+ GL_LINES,
+ GL_LINE_STRIP,
+ GL_POINTS,
+};
+typedef char kTopologyGLES3SizeAssert[ARRAY_SIZE(kTopologyGLES3) == kPrimitiveTypeCount ? 1 : -1];
+
+static const GLenum kVertexTypeGLES3[] =
+{
+ GL_FLOAT, // kChannelFormatFloat
+ GL_HALF_FLOAT, // kChannelFormatFloat16
+ GL_UNSIGNED_BYTE, // kChannelFormatColor
+ GL_BYTE, // kChannelFormatByte
+};
+typedef char kVertexTypeGLES3Assert[ARRAY_SIZE(kVertexTypeGLES3) == kChannelFormatCount ? 1 : -1];
+
+// Targets by attribute location
+static const VertexComponent kVertexCompTargetsGLES3[] =
+{
+ // \note Indices must match to attribute locations.
+ kVertexCompVertex,
+ kVertexCompColor,
+ kVertexCompNormal,
+ kVertexCompTexCoord0,
+ kVertexCompTexCoord1,
+ kVertexCompTexCoord2,
+ kVertexCompTexCoord3,
+ kVertexCompTexCoord4,
+ kVertexCompTexCoord5,
+ kVertexCompTexCoord6,
+ kVertexCompTexCoord7
+};
+typedef char kVertexCompTargetsGLES3Assert[ARRAY_SIZE(kVertexCompTargetsGLES3) == kGLES3MaxVertexAttribs ? 1 : -1];
+
+// For some reason GfxDevice needs to know whether VBO contains color
+// data in order to set up state properly. So this must be called before
+// doing BeforeDrawCall().
+void VBOContainsColorGLES30 (bool flag); // defined in GfxDeviceGLES30.cpp
+
+static bool IsVertexDataValid (const VertexBufferData& buffer)
+{
+ // Verify streams.
+ {
+ UInt32 shaderChannels = 0;
+ for (int streamNdx = 0; streamNdx < kMaxVertexStreams; streamNdx++)
+ {
+ if ((buffer.streams[streamNdx].channelMask == 0) != (buffer.streams[streamNdx].stride == 0))
+ return false; // No data but enabled channels, or other way around.
+
+ if ((shaderChannels & buffer.streams[streamNdx].channelMask) != 0)
+ return false; // Duplicate channels!
+
+ shaderChannels |= buffer.streams[streamNdx].channelMask;
+ }
+ }
+
+ // Make sure channels point to correct streams.
+ for (int chanNdx = 0; chanNdx < kShaderChannelCount; chanNdx++)
+ {
+ if (buffer.channels[chanNdx].dimension == ChannelInfo::kInvalidDimension)
+ continue;
+
+ int streamNdx = buffer.channels[chanNdx].stream;
+
+ if (streamNdx < 0 || streamNdx >= kMaxVertexStreams)
+ return false;
+
+ if (buffer.streams[streamNdx].channelMask & (1<<chanNdx) == 0)
+ return false; // No such channel in stream.
+ }
+
+ // Verify that streams do not overlap (or otherwise we waste memory).
+ // \todo [pyry] O(n^2), but then again kMaxVertexStreams = 4..
+ for (int streamNdx = 0; streamNdx < kMaxVertexStreams; streamNdx++)
+ {
+ if (buffer.streams[streamNdx].channelMask == 0)
+ continue;
+
+ const int start = buffer.streams[streamNdx].offset;
+ const int end = start + buffer.streams[streamNdx].stride*buffer.vertexCount;
+
+ for (int otherStreamNdx = 0; otherStreamNdx < kMaxVertexStreams; otherStreamNdx++)
+ {
+ if (otherStreamNdx == streamNdx ||
+ buffer.streams[otherStreamNdx].channelMask == 0)
+ continue;
+
+ const int otherStart = buffer.streams[otherStreamNdx].offset;
+ const int otherEnd = otherStart + buffer.streams[otherStreamNdx].stride*buffer.vertexCount;
+
+ if ((start <= otherStart && otherStart < end) || // Start lies inside buffer
+ (start < otherEnd && otherEnd <= end)) // End lies inside buffer
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool IsVertexArrayNormalized (int attribNdx, VertexChannelFormat format)
+{
+ if (attribNdx == 0)
+ return false; // Position is never normalized.
+ else
+ return (format != kChannelFormatFloat && format != kChannelFormatFloat16);
+}
+
+// VAOCacheGLES30
+
+VAOCacheGLES30::VAOCacheGLES30 (void)
+ : m_NextEntryNdx(0)
+{
+}
+
+VAOCacheGLES30::~VAOCacheGLES30 (void)
+{
+ Clear();
+}
+
+void VAOCacheGLES30::Clear (void)
+{
+ for (int ndx = 0; ndx < kCacheSize; ndx++)
+ {
+ delete m_Entries[ndx].vao;
+ m_Entries[ndx].vao = 0;
+ m_Entries[ndx].key = VAOCacheKeyGLES30();
+ }
+
+ m_NextEntryNdx = 0; // Not necessary, but this will place first VAOs in first slots.
+}
+
+inline const VertexArrayObjectGLES30* VAOCacheGLES30::Find (const VAOCacheKeyGLES30& key) const
+{
+ for (int ndx = 0; ndx < kCacheSize; ndx++)
+ {
+ if (m_Entries[ndx].key == key)
+ return m_Entries[ndx].vao;
+ }
+
+ return 0;
+}
+
+void VAOCacheGLES30::Insert (const VAOCacheKeyGLES30& key, VertexArrayObjectGLES30* vao)
+{
+ Assert(!Find(key));
+
+ // Replace last
+ delete m_Entries[m_NextEntryNdx].vao;
+ m_Entries[m_NextEntryNdx].key = key;
+ m_Entries[m_NextEntryNdx].vao = vao;
+ m_NextEntryNdx = (m_NextEntryNdx + 1) % kCacheSize;
+}
+
+bool VAOCacheGLES30::IsFull (void) const
+{
+ return m_Entries[m_NextEntryNdx].vao != 0;
+}
+
+// VertexArrayObjectGLES30
+
+VertexArrayObjectGLES30::VertexArrayObjectGLES30 (const VertexArrayInfoGLES30& info)
+ : m_info(info)
+ , m_vao (0)
+{
+ UInt32 boundBuffer = 0;
+
+ GLES_CHK(glGenVertexArrays(1, (GLuint*)&m_vao));
+ GLES_CHK(glBindVertexArray(m_vao));
+
+ for (int attribNdx = 0; attribNdx < kGLES3MaxVertexAttribs; attribNdx++)
+ {
+ if ((info.enabledArrays & (1<<attribNdx)) == 0)
+ continue;
+
+ const UInt32 buffer = info.buffers[attribNdx];
+ const int numComponents = info.arrays[attribNdx].numComponents;
+ const GLenum compType = kVertexTypeGLES3[info.arrays[attribNdx].componentType];
+ const bool normalized = IsVertexArrayNormalized(attribNdx, (VertexChannelFormat)info.arrays[attribNdx].componentType);
+ const int stride = info.arrays[attribNdx].stride;
+ const void* pointer = info.arrays[attribNdx].pointer;
+
+ if (buffer != boundBuffer)
+ {
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, buffer));
+ boundBuffer = buffer;
+ }
+
+ GLES_CHK(glEnableVertexAttribArray(attribNdx));
+ GLES_CHK(glVertexAttribPointer(attribNdx, numComponents, compType, normalized ? GL_TRUE : GL_FALSE, stride, pointer));
+ }
+
+ GLES_CHK(glBindVertexArray(0));
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, 0));
+}
+
+VertexArrayObjectGLES30::~VertexArrayObjectGLES30 (void)
+{
+ glDeleteVertexArrays(1, (const GLuint*)&m_vao);
+}
+
+// GLES3VBO
+
+GLES3VBO::GLES3VBO (void)
+ : m_IndexBuffer(0)
+{
+}
+
+GLES3VBO::~GLES3VBO (void)
+{
+ Cleanup();
+}
+
+static UInt32 MapStreamModeToBufferUsage (VBO::StreamMode mode)
+{
+ switch (mode)
+ {
+ case VBO::kStreamModeNoAccess: return GL_STATIC_DRAW; // ???
+ case VBO::kStreamModeWritePersist: return GL_STATIC_DRAW;
+ case VBO::kStreamModeDynamic: return GL_STREAM_DRAW;
+ default:
+ return GL_STATIC_DRAW;
+ }
+}
+
+inline DataBufferGLES30* GLES3VBO::GetCurrentBuffer (int streamNdx)
+{
+ return m_StreamBuffers[streamNdx].buffers[m_StreamBuffers[streamNdx].curBufferNdx];
+}
+
+void GLES3VBO::UpdateVertexData (const VertexBufferData& buffer)
+{
+ Assert(IsVertexDataValid(buffer));
+
+ bool streamHasData[kMaxVertexStreams];
+ int streamBufSize[kMaxVertexStreams];
+
+ for (int streamNdx = 0; streamNdx < kMaxVertexStreams; streamNdx++)
+ {
+ const bool hasData = (buffer.streams[streamNdx].channelMask != 0);
+ const int size = buffer.streams[streamNdx].stride * buffer.vertexCount;
+
+ Assert(hasData == (size != 0));
+
+ streamHasData[streamNdx] = hasData;
+ streamBufSize[streamNdx] = size;
+ }
+
+ // Discard all existing buffers.
+ for (int streamNdx = 0; streamNdx < kMaxVertexStreams; streamNdx++)
+ {
+ Stream& stream = m_StreamBuffers[streamNdx];
+
+ for (int bufNdx = 0; bufNdx < kBufferSwapChainSize; bufNdx++)
+ {
+ if (stream.buffers[bufNdx])
+ {
+ stream.buffers[bufNdx]->Release();
+ stream.buffers[bufNdx] = 0;
+ }
+ }
+
+ UNITY_FREE(kMemVertexData, stream.cpuBuf);
+ stream.cpuBuf = 0;
+ }
+
+ m_VAOCache.Clear(); // VAO cache must be cleared
+
+ // Allocate buffers and upload data.
+ for (int streamNdx = 0; streamNdx < kMaxVertexStreams; streamNdx++)
+ {
+ if (!streamHasData[streamNdx])
+ continue;
+
+ const UInt32 usage = MapStreamModeToBufferUsage((StreamMode)m_StreamModes[streamNdx]);
+ const int size = streamBufSize[streamNdx];
+ const UInt8* streamData = buffer.buffer + buffer.streams[streamNdx].offset;
+ Stream& dstStream = m_StreamBuffers[streamNdx];
+
+ Assert(!dstStream.buffers[dstStream.curBufferNdx]);
+
+ dstStream.buffers[dstStream.curBufferNdx] = GetBufferManagerGLES30()->AcquireBuffer(size, usage);
+ dstStream.buffers[dstStream.curBufferNdx]->RecreateWithData(size, usage, streamData);
+
+ dstStream.channelMask = buffer.streams[streamNdx].channelMask;
+ dstStream.stride = buffer.streams[streamNdx].stride;
+
+ // \todo [2013-06-19 pyry] Allocate cpuBuf on-demand once it is not required for Recreate()
+ dstStream.cpuBuf = (UInt8*)UNITY_MALLOC(kMemVertexData, size);
+ ::memcpy(dstStream.cpuBuf, streamData, size);
+ }
+
+ m_VertexCount = buffer.vertexCount;
+ memcpy(&m_Channels[0], &buffer.channels[0], sizeof(ChannelInfoArray));
+}
+
+void GLES3VBO::UpdateIndexData (const IndexBufferData& buffer)
+{
+ const int bufferSize = CalculateIndexBufferSize(buffer);
+ const UInt32 bufferUsage = m_IndicesDynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW;
+
+ Assert(bufferSize > 0);
+
+ if (m_IndexBuffer && BufferUpdateCausesStallGLES30(m_IndexBuffer))
+ {
+ m_IndexBuffer->Release();
+ m_IndexBuffer = 0;
+ }
+
+ if (!m_IndexBuffer)
+ m_IndexBuffer = GetBufferManagerGLES30()->AcquireBuffer(bufferSize, bufferUsage);
+
+ m_IndexBuffer->RecreateWithData(bufferSize, bufferUsage, buffer.indices);
+
+ // Take copy for emulating quads.
+ Assert(kVBOIndexSize == sizeof(UInt16));
+ m_Indices.resize(buffer.count);
+ std::copy((const UInt16*)buffer.indices, (const UInt16*)buffer.indices + buffer.count, m_Indices.begin());
+}
+
+bool GLES3VBO::MapVertexStream (VertexStreamData& outData, unsigned stream)
+{
+ Assert(0 <= stream && stream < kMaxVertexStreams);
+ Assert(!m_IsStreamMapped[stream]); // \note Multiple mappings will screw up buffer swap chain.
+ Assert(m_StreamBuffers[stream].buffers[m_StreamBuffers[stream].curBufferNdx]);
+
+ Stream& mapStream = m_StreamBuffers[stream];
+ const int size = m_VertexCount * mapStream.stride;
+ void* mapPtr = 0;
+
+ if (BufferUpdateCausesStallGLES30(mapStream.buffers[mapStream.curBufferNdx]))
+ {
+ // Advance to next slot.
+ mapStream.curBufferNdx = (mapStream.curBufferNdx + 1) % kBufferSwapChainSize;
+
+ if (!mapStream.buffers[mapStream.curBufferNdx])
+ {
+ const UInt32 usage = MapStreamModeToBufferUsage((StreamMode)m_StreamModes[stream]);
+ DataBufferGLES30* mapBuffer = GetBufferManagerGLES30()->AcquireBuffer(size, usage);
+
+ if (mapBuffer->GetSize() < size)
+ mapBuffer->RecreateStorage(size, usage);
+
+ mapStream.buffers[mapStream.curBufferNdx] = mapBuffer;
+ }
+ }
+
+ if (gGraphicsCaps.gles30.useMapBuffer)
+ {
+ // \note Using write-only mapping always as map is not guaranteed to return old contents anyway.
+ mapPtr = mapStream.buffers[mapStream.curBufferNdx]->Map(0, size, GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_BUFFER_BIT);
+ }
+ else
+ {
+ // \todo [2013-06-19 pyry] Allocate cpuBuf on-demand once it is not required for Recreate()
+ Assert(mapStream.cpuBuf);
+ mapPtr = mapStream.cpuBuf;
+ }
+
+ outData.channelMask = mapStream.channelMask;
+ outData.stride = mapStream.stride;
+ outData.vertexCount = m_VertexCount;
+ outData.buffer = (UInt8*)mapPtr;
+
+ m_IsStreamMapped[stream] = true;
+
+ return true;
+}
+
+void GLES3VBO::UnmapVertexStream (unsigned stream)
+{
+ Assert(0 <= stream && stream < kMaxVertexStreams);
+ Assert(m_IsStreamMapped[stream]);
+
+ if (gGraphicsCaps.gles30.useMapBuffer)
+ {
+ GetCurrentBuffer(stream)->Unmap();
+ }
+ else
+ {
+ const int size = m_StreamBuffers[stream].stride*m_VertexCount;
+ const UInt32 usage = MapStreamModeToBufferUsage((StreamMode)m_StreamModes[stream]);
+
+ // \note Buffer slot was advanced in MapVertexStream()
+ GetCurrentBuffer(stream)->RecreateWithData(size, usage, m_StreamBuffers[stream].cpuBuf);
+ }
+
+ m_IsStreamMapped[stream] = false;
+}
+
+void GLES3VBO::Cleanup (void)
+{
+ if (m_IndexBuffer)
+ {
+ m_IndexBuffer->Release();
+ m_IndexBuffer = 0;
+ }
+
+ for (int streamNdx = 0; streamNdx < kMaxVertexStreams; streamNdx++)
+ {
+ for (int bufNdx = 0; bufNdx < kBufferSwapChainSize; bufNdx++)
+ {
+ if (m_StreamBuffers[streamNdx].buffers[bufNdx])
+ {
+ m_StreamBuffers[streamNdx].buffers[bufNdx]->Release();
+ m_StreamBuffers[streamNdx].buffers[bufNdx] = 0;
+ }
+ }
+
+ UNITY_FREE(kMemVertexData, m_StreamBuffers[streamNdx].cpuBuf);
+ m_StreamBuffers[streamNdx].cpuBuf = 0;
+ }
+
+ m_VAOCache.Clear();
+}
+
+void GLES3VBO::Recreate (void)
+{
+ // \note Called on context loss.
+
+ if (m_IndexBuffer)
+ {
+ const int bufferSize = (int)m_Indices.size() * kVBOIndexSize;
+ const UInt32 bufferUsage = m_IndicesDynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW;
+
+ m_IndexBuffer->Disown();
+ delete m_IndexBuffer;
+
+ m_IndexBuffer = GetBufferManagerGLES30()->AcquireBuffer(bufferSize, bufferUsage);
+ m_IndexBuffer->RecreateWithData(bufferSize, bufferUsage, &m_Indices[0]);
+ }
+
+ for (int streamNdx = 0; streamNdx < kMaxVertexStreams; streamNdx++)
+ {
+ Stream& stream = m_StreamBuffers[streamNdx];
+ const int bufSize = stream.stride*m_VertexCount;
+ const UInt32 usage = MapStreamModeToBufferUsage((StreamMode)m_StreamModes[streamNdx]);
+
+ for (int bufNdx = 0; bufNdx < kBufferSwapChainSize; bufNdx++)
+ {
+ if (stream.buffers[bufNdx])
+ {
+ stream.buffers[bufNdx]->Disown();
+ delete stream.buffers[bufNdx];
+ stream.buffers[bufNdx] = 0;
+ }
+ }
+
+ stream.curBufferNdx = 0;
+
+ if (bufSize > 0)
+ {
+ Assert(stream.cpuBuf);
+ stream.buffers[0] = GetBufferManagerGLES30()->AcquireBuffer(bufSize, usage);
+ stream.buffers[0]->RecreateWithData(bufSize, usage, stream.cpuBuf);
+ }
+ }
+
+ m_VAOCache.Clear();
+}
+
+bool GLES3VBO::IsVertexBufferLost (void) const
+{
+ return false;
+}
+
+bool GLES3VBO::IsUsingSourceVertices (void) const
+{
+ return false;
+}
+
+bool GLES3VBO::IsUsingSourceIndices (void) const
+{
+ return true;
+}
+
+int GLES3VBO::GetRuntimeMemorySize (void) const
+{
+ int totalSize = 0;
+
+ for (int streamNdx = 0; streamNdx < kMaxVertexStreams; streamNdx++)
+ {
+ for (int bufNdx = 0; bufNdx < kBufferSwapChainSize; bufNdx++)
+ {
+ if (m_StreamBuffers[streamNdx].buffers[bufNdx])
+ totalSize += m_StreamBuffers[streamNdx].buffers[bufNdx]->GetSize();
+ }
+ }
+
+ if (m_IndexBuffer)
+ totalSize += m_IndexBuffer->GetSize();
+
+ return totalSize;
+}
+
+void GLES3VBO::ComputeVertexInputState (VertexArrayInfoGLES30& dst, const ChannelAssigns& channelAssigns)
+{
+ UInt32 availableShaderChannels = 0; // Channels that have data in any of streams.
+ UInt32 enabledTargets = channelAssigns.GetTargetMap();
+
+ for (int streamNdx = 0; streamNdx < kMaxVertexStreams; streamNdx++)
+ availableShaderChannels |= m_StreamBuffers[streamNdx].channelMask;
+
+ for (int attribNdx = 0; attribNdx < kGLES3MaxVertexAttribs; attribNdx++)
+ {
+ const VertexComponent target = kVertexCompTargetsGLES3[attribNdx];
+
+ if ((enabledTargets & (1<<target)) == 0)
+ continue; // Not enabled.
+
+ const ShaderChannel sourceChannel = channelAssigns.GetSourceForTarget(target);
+
+ // \todo [pyry] Uh, what? Channel is enabled, but no valid source exists.
+ if (sourceChannel < 0)
+ continue;
+
+ Assert(0 <= sourceChannel && sourceChannel < kShaderChannelCount);
+
+ if ((availableShaderChannels & (1<<sourceChannel)) == 0)
+ continue; // Not available.
+
+ const Stream& stream = m_StreamBuffers[m_Channels[sourceChannel].stream];
+
+ dst.arrays[attribNdx].componentType = m_Channels[sourceChannel].format;
+ dst.arrays[attribNdx].numComponents = m_Channels[sourceChannel].format == kChannelFormatColor ? 4 : m_Channels[sourceChannel].dimension;
+ dst.arrays[attribNdx].pointer = reinterpret_cast<const void*>(m_Channels[sourceChannel].offset);
+ dst.arrays[attribNdx].stride = stream.stride;
+ dst.buffers[attribNdx] = GetCurrentBuffer(m_Channels[sourceChannel].stream)->GetBuffer();
+
+ dst.enabledArrays |= (1<<attribNdx);
+ }
+
+ // Fixed-function texgen stuff.
+ {
+ GfxDevice& device = GetRealGfxDevice();
+ const int texArrayBase = kGLES3AttribLocationTexCoord0;
+ const int maxTexArrays = std::min(gGraphicsCaps.maxTexUnits, kGLES3MaxVertexAttribs-texArrayBase);
+
+ if (device.IsPositionRequiredForTexGen() && (availableShaderChannels & (1 << kShaderChannelVertex)))
+ {
+ const ChannelInfo& posInfo = m_Channels[kShaderChannelVertex];
+ const Stream& posStream = m_StreamBuffers[posInfo.stream];
+
+ for (int texUnit = 0; texUnit < maxTexArrays; ++texUnit)
+ {
+ if (device.IsPositionRequiredForTexGen(texUnit))
+ {
+ const int arrNdx = texArrayBase + texUnit;
+ dst.arrays[arrNdx].componentType = posInfo.format;
+ dst.arrays[arrNdx].numComponents = posInfo.format == kChannelFormatColor ? 4 : posInfo.dimension;
+ dst.arrays[arrNdx].pointer = reinterpret_cast<const void*>(posInfo.offset);
+ dst.arrays[arrNdx].stride = posStream.stride;
+ dst.buffers[arrNdx] = GetCurrentBuffer(posInfo.stream)->GetBuffer();
+
+ dst.enabledArrays |= (1<<arrNdx);
+ }
+ }
+ }
+
+ if (device.IsNormalRequiredForTexGen() && (availableShaderChannels & (1 << kShaderChannelNormal)))
+ {
+ const ChannelInfo& normInfo = m_Channels[kShaderChannelNormal];
+ const Stream& normStream = m_StreamBuffers[normInfo.stream];
+
+ for (int texUnit = 0; texUnit < maxTexArrays; ++texUnit)
+ {
+ if (device.IsNormalRequiredForTexGen(texUnit))
+ {
+ const int arrNdx = texArrayBase + texUnit;
+ dst.arrays[arrNdx].componentType = normInfo.format;
+ dst.arrays[arrNdx].numComponents = normInfo.format == kChannelFormatColor ? 4 : normInfo.dimension;
+ dst.arrays[arrNdx].pointer = reinterpret_cast<const void*>(normInfo.offset);
+ dst.arrays[arrNdx].stride = normStream.stride;
+ dst.buffers[arrNdx] = GetCurrentBuffer(normInfo.stream)->GetBuffer();
+
+ dst.enabledArrays |= (1<<arrNdx);
+ }
+ }
+ }
+ }
+}
+
+void GLES3VBO::MarkBuffersRendered (const ChannelAssigns& channelAssigns)
+{
+ // \todo [pyry] Mark based on channel assignments
+ for (int ndx = 0; ndx < kMaxVertexStreams; ndx++)
+ {
+ DataBufferGLES30* buffer = GetCurrentBuffer(ndx);
+ if (buffer)
+ buffer->RecordRender();
+ }
+}
+
+void GLES3VBO::DrawVBO (const ChannelAssigns& channels,
+ UInt32 firstIndexByte,
+ UInt32 indexCount,
+ GfxPrimitiveType topology,
+ UInt32 firstVertex,
+ UInt32 vertexCount)
+{
+ Assert(0 <= firstVertex && firstVertex+vertexCount <= m_VertexCount);
+
+ if (topology == kPrimitiveQuads)
+ {
+ // Need to emulate - oh well.
+ // \todo [2013-05-24 pyry] Cache this?
+ const int numQuads = indexCount/4;
+ const int emulatedIndexCount = numQuads * 6;
+ const int emulatedBufSize = emulatedIndexCount * sizeof(UInt16);
+ const UInt32 bufUsage = GL_DYNAMIC_DRAW;
+ std::vector<UInt16> quadIndices (emulatedIndexCount);
+ DataBufferGLES30* indexBuffer = GetBufferManagerGLES30()->AcquireBuffer(emulatedBufSize, bufUsage);
+
+ FillIndexBufferForQuads(&quadIndices[0], emulatedBufSize, (const UInt16*)((UInt8*)&m_Indices[0] + firstIndexByte), numQuads);
+ indexBuffer->RecreateWithData(emulatedBufSize, bufUsage, &quadIndices[0]);
+
+ Draw(indexBuffer, channels, kPrimitiveTriangles, emulatedIndexCount, 0, vertexCount);
+
+ indexBuffer->Release();
+ }
+ else
+ Draw(m_IndexBuffer, channels, topology, indexCount, firstIndexByte, vertexCount);
+}
+
+void GLES3VBO::DrawCustomIndexed (const ChannelAssigns& channels,
+ void* indices,
+ UInt32 indexCount,
+ GfxPrimitiveType topology,
+ UInt32 vertexRangeBegin,
+ UInt32 vertexRangeEnd,
+ UInt32 drawVertexCount)
+{
+ Assert(0 <= vertexRangeBegin && vertexRangeBegin <= vertexRangeEnd && vertexRangeEnd <= m_VertexCount);
+
+ // \note Called only in static batching mode which doesn't do quads.
+ Assert(topology != kPrimitiveQuads);
+
+ const int ndxBufSize = indexCount * kVBOIndexSize;
+ const UInt32 ndxBufUsage = GL_DYNAMIC_DRAW;
+ DataBufferGLES30* indexBuffer = GetBufferManagerGLES30()->AcquireBuffer(ndxBufSize, ndxBufUsage);
+
+ indexBuffer->RecreateWithData(ndxBufSize, ndxBufUsage, indices);
+
+ Draw(indexBuffer, channels, topology, indexCount, 0, vertexRangeEnd-vertexRangeBegin);
+
+ indexBuffer->Release();
+}
+
+void GLES3VBO::Draw (DataBufferGLES30* indexBuffer,
+ const ChannelAssigns& channels,
+ GfxPrimitiveType topology,
+ UInt32 indexCount,
+ UInt32 indexOffset,
+ UInt32 vertexCountForStats)
+{
+ const bool useVAO = true; // \todo [pyry] Get from GfxDevice caps
+ const VertexArrayObjectGLES30* vao = useVAO ? TryGetVAO(channels) : 0;
+
+ Assert(topology != kPrimitiveQuads);
+
+ // Setup other render state
+ VBOContainsColorGLES30(channels.GetSourceForTarget (kVertexCompColor) == kShaderChannelColor);
+ GetRealGfxDevice().BeforeDrawCall(false);
+
+ if (vao)
+ {
+ GLES_CHK(glBindVertexArray(vao->GetVAO()));
+ }
+ else
+ {
+ VertexArrayInfoGLES30 vertexState;
+ ComputeVertexInputState(vertexState, channels);
+ SetupDefaultVertexArrayStateGLES30(vertexState);
+ }
+
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer->GetBuffer()));
+
+ {
+ ABSOLUTE_TIME drawTime = START_TIME;
+ const int primCount = GetPrimitiveCount(indexCount, topology, false);
+
+ GLES_CHK(glDrawElements(kTopologyGLES3[topology], indexCount, GL_UNSIGNED_SHORT, (const void*)indexOffset));
+
+ drawTime = ELAPSED_TIME(drawTime);
+ GetRealGfxDevice().GetFrameStats().AddDrawCall(primCount, vertexCountForStats, drawTime);
+ }
+
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+
+ if (vao)
+ {
+ GLES_CHK(glBindVertexArray(0));
+ }
+
+ // Record render event for used buffers
+ MarkBuffersRendered(channels);
+ indexBuffer->RecordRender();
+}
+
+const VertexArrayObjectGLES30* GLES3VBO::TryGetVAO (const ChannelAssigns& channels)
+{
+ // \note [pyry] VAO cache keys don't handle texgen. It is a rare case and we don't want to pay the
+ // cost of extra logic & storage.
+ {
+ GfxDevice& device = GetRealGfxDevice();
+ if (device.IsPositionRequiredForTexGen() || device.IsNormalRequiredForTexGen())
+ return 0;
+ }
+
+ const VAOCacheKeyGLES30 cacheKey(channels, m_StreamBuffers[0].curBufferNdx,
+ m_StreamBuffers[1].curBufferNdx,
+ m_StreamBuffers[2].curBufferNdx,
+ m_StreamBuffers[3].curBufferNdx);
+
+ const VertexArrayObjectGLES30* cachedVAO = m_VAOCache.Find(cacheKey);
+
+ if (cachedVAO)
+ {
+ return cachedVAO;
+ }
+ else if (!m_VAOCache.IsFull())
+ {
+ DBG_LOG_VBO_GLES30("GLES3VBO::GetVAO(): cache miss, creating new VAO");
+
+ // Map channel assigns + current layout to full vertex state
+ VertexArrayInfoGLES30 vertexState;
+ ComputeVertexInputState(vertexState, channels);
+
+ VertexArrayObjectGLES30* vao = new VertexArrayObjectGLES30(vertexState);
+ m_VAOCache.Insert(cacheKey, vao);
+
+ return vao;
+ }
+ else
+ {
+ // If VAO cache gets full, VBO falls back to using default VAO. That
+ // way we avoid constantly creating new objects in extreme cases.
+ return 0;
+ }
+}
+
+UInt32 GLES3VBO::GetSkinningTargetVBO (void)
+{
+ const int skinStreamNdx = 0; // \todo [2013-05-31 pyry] Can this change?
+ Stream& skinStream = m_StreamBuffers[skinStreamNdx];
+
+ Assert(skinStream.buffers[skinStream.curBufferNdx]);
+
+ if (BufferUpdateCausesStallGLES30(skinStream.buffers[skinStream.curBufferNdx]))
+ {
+ // Move to next slot.
+ skinStream.curBufferNdx = (skinStream.curBufferNdx + 1) % kBufferSwapChainSize;
+
+ if (!skinStream.buffers[skinStream.curBufferNdx])
+ {
+ const UInt32 usage = GL_STREAM_DRAW;
+ const int size = m_VertexCount*skinStream.stride;
+ DataBufferGLES30* buffer = GetBufferManagerGLES30()->AcquireBuffer(size, usage);
+
+ if (buffer->GetSize() < size)
+ buffer->RecreateStorage(size, usage);
+
+ skinStream.buffers[skinStream.curBufferNdx] = buffer;
+ }
+ }
+
+ skinStream.buffers[skinStream.curBufferNdx]->RecordUpdate();
+ return skinStream.buffers[skinStream.curBufferNdx]->GetBuffer();
+}
+
+DynamicGLES3VBO::DynamicGLES3VBO (void)
+ : m_CurVertexBuffer (0)
+ , m_CurIndexBuffer (0)
+ , m_CurRenderMode ((RenderMode)0)
+ , m_CurShaderChannelMask (0)
+ , m_CurStride (0)
+ , m_CurVertexCount (0)
+ , m_CurIndexCount (0)
+ , m_QuadArrayIndexBuffer (0)
+{
+}
+
+DynamicGLES3VBO::~DynamicGLES3VBO (void)
+{
+ Cleanup();
+}
+
+static UInt32 GetDynamicChunkStride (UInt32 shaderChannelMask)
+{
+ UInt32 stride = 0;
+ for (int i = 0; i < kShaderChannelCount; ++i)
+ {
+ if (shaderChannelMask & (1<<i))
+ stride += VBO::GetDefaultChannelByteSize(i);
+ }
+ return stride;
+}
+
+bool DynamicGLES3VBO::GetChunk (UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode renderMode, void** outVB, void** outIB)
+{
+ Assert(maxVertices < 65536 && maxIndices < 65536*3);
+ Assert(!((renderMode == kDrawQuads) && (VBO::kMaxQuads*4 < maxVertices)));
+ DebugAssert(outVB != NULL && maxVertices > 0);
+ DebugAssert((renderMode == kDrawIndexedQuads && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedPoints && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedLines && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedTriangles && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedTriangleStrip && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawTriangleStrip && (outIB == NULL && maxIndices == 0)) ||
+ (renderMode == kDrawQuads && (outIB == NULL && maxIndices == 0)));
+ DebugAssert(!m_CurVertexBuffer && !m_CurIndexBuffer);
+
+ const UInt32 stride = GetDynamicChunkStride(shaderChannelMask);
+ const UInt32 usage = GL_STREAM_DRAW;
+ const UInt32 vertexBufferSize = AlignToDefault(stride*maxVertices);
+ const UInt32 indexBufferSize = AlignToDefault(kVBOIndexSize*maxIndices);
+
+ const bool useMapBuffer = gGraphicsCaps.gles30.useMapBuffer;
+ const bool mapVertexBuffer = useMapBuffer && vertexBufferSize >= kDataBufferThreshold;
+ const bool mapIndexBuffer = useMapBuffer && indexBufferSize > 0 && indexBufferSize >= kDataBufferThreshold;
+
+ DataBufferGLES30* vertexBuffer = mapVertexBuffer ? GetBufferManagerGLES30()->AcquireBuffer(maxVertices*stride, usage) : 0;
+ DataBufferGLES30* indexBuffer = mapIndexBuffer ? GetBufferManagerGLES30()->AcquireBuffer(indexBufferSize, usage) : 0;
+
+ // \todo [2013-05-31 pyry] Grow buffers in reasonable steps (align to 1k?)
+ if (vertexBuffer && vertexBuffer->GetSize() < vertexBufferSize)
+ vertexBuffer->RecreateStorage(vertexBufferSize, usage);
+
+ if (indexBuffer && indexBuffer->GetSize() < indexBufferSize)
+ indexBuffer->RecreateStorage(indexBufferSize, usage);
+
+ if (!mapVertexBuffer && m_CurVertexData.size() < vertexBufferSize)
+ m_CurVertexData.resize(vertexBufferSize);
+
+ if (!mapIndexBuffer && m_CurIndexData.size() < indexBufferSize)
+ m_CurIndexData.resize(indexBufferSize);
+
+ if (vertexBuffer)
+ *outVB = vertexBuffer->Map(0, vertexBufferSize, GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_BUFFER_BIT|GL_MAP_FLUSH_EXPLICIT_BIT);
+ else if (!mapVertexBuffer && vertexBufferSize > 0)
+ *outVB = &m_CurVertexData[0];
+
+ if (indexBuffer)
+ *outIB = indexBuffer->Map(0, indexBufferSize, GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_BUFFER_BIT|GL_MAP_FLUSH_EXPLICIT_BIT);
+ else if (!mapIndexBuffer && indexBufferSize > 0)
+ *outIB = &m_CurIndexData[0];
+
+ m_CurVertexBuffer = vertexBuffer;
+ m_CurIndexBuffer = indexBuffer;
+ m_CurRenderMode = renderMode;
+ m_CurShaderChannelMask = shaderChannelMask;
+ m_CurStride = stride;
+
+ return true;
+}
+
+void DynamicGLES3VBO::ReleaseChunk (UInt32 actualVertices, UInt32 actualIndices)
+{
+ Assert(m_CurVertexCount == 0 && m_CurIndexCount == 0);
+
+ if (m_CurVertexBuffer)
+ {
+ m_CurVertexBuffer->FlushMappedRange(0, AlignToDefault(m_CurStride*actualVertices));
+ m_CurVertexBuffer->Unmap();
+ }
+ else if (actualVertices*m_CurStride >= kDataBufferThreshold)
+ {
+ // Migrate to buffer.
+ const int size = AlignToDefault(actualVertices*m_CurStride);
+ const UInt32 usage = GL_STREAM_DRAW;
+
+ m_CurVertexBuffer = GetBufferManagerGLES30()->AcquireBuffer(size, usage);
+ m_CurVertexBuffer->RecreateWithData(size, usage, &m_CurVertexData[0]);
+ }
+
+ if (m_CurIndexBuffer)
+ {
+ m_CurIndexBuffer->FlushMappedRange(0, AlignToDefault(actualIndices*kVBOIndexSize));
+ m_CurIndexBuffer->Unmap();
+ }
+ else if (actualIndices*kVBOIndexSize >= kDataBufferThreshold)
+ {
+ // Migrate to buffer.
+ const int size = AlignToDefault(actualIndices*kVBOIndexSize);
+ const UInt32 usage = GL_STREAM_DRAW;
+
+ m_CurIndexBuffer = GetBufferManagerGLES30()->AcquireBuffer(size, usage);
+ m_CurIndexBuffer->RecreateWithData(size, usage, &m_CurIndexData[0]);
+ }
+
+ m_CurVertexCount = actualVertices;
+ m_CurIndexCount = actualIndices;
+}
+
+DataBufferGLES30* DynamicGLES3VBO::GetQuadArrayIndexBuffer (int vertexCount)
+{
+ const int quadCount = vertexCount/4;
+ const int quadIndexCount = quadCount * 6;
+ const int indexBufferSize = quadIndexCount * sizeof(UInt16);
+ const UInt32 indexBufferUsage = GL_STATIC_DRAW;
+
+ if (!m_QuadArrayIndexBuffer || m_QuadArrayIndexBuffer->GetSize() < indexBufferSize)
+ {
+ // Need to re-specify index buffer since current is too small
+ std::vector<UInt16> quadIndices(quadIndexCount);
+
+ for (int quadNdx = 0; quadNdx < quadCount; ++quadNdx)
+ {
+ const UInt16 srcBaseNdx = quadNdx*4;
+ const int dstBaseNdx = quadNdx*6;
+ quadIndices[dstBaseNdx + 0] = srcBaseNdx + 1;
+ quadIndices[dstBaseNdx + 1] = srcBaseNdx + 2;
+ quadIndices[dstBaseNdx + 2] = srcBaseNdx;
+ quadIndices[dstBaseNdx + 3] = srcBaseNdx + 2;
+ quadIndices[dstBaseNdx + 4] = srcBaseNdx + 3;
+ quadIndices[dstBaseNdx + 5] = srcBaseNdx;
+ }
+
+ if (m_QuadArrayIndexBuffer && BufferUpdateCausesStallGLES30(m_QuadArrayIndexBuffer))
+ {
+ m_QuadArrayIndexBuffer->Release();
+ m_QuadArrayIndexBuffer = 0;
+ }
+
+ if (!m_QuadArrayIndexBuffer)
+ m_QuadArrayIndexBuffer = GetBufferManagerGLES30()->AcquireBuffer(indexBufferSize, indexBufferUsage);
+
+ m_QuadArrayIndexBuffer->RecreateWithData(indexBufferSize, indexBufferUsage, &quadIndices[0]);
+ }
+
+ return m_QuadArrayIndexBuffer;
+}
+
+void DynamicGLES3VBO::DrawChunk (const ChannelAssigns& channels)
+{
+ // Compute input state
+ VertexArrayInfoGLES30 vertexInputState;
+ ComputeVertexInputState(vertexInputState, channels);
+
+ // \todo [2013-05-31 pyry] Do we want to use VAOs here?
+
+ // Setup state
+ VBOContainsColorGLES30(channels.GetSourceForTarget (kVertexCompColor) == kShaderChannelColor);
+ GetRealGfxDevice().BeforeDrawCall(false);
+ SetupDefaultVertexArrayStateGLES30(vertexInputState);
+
+ const void* indexPtr = m_CurIndexBuffer ? 0 : (m_CurIndexData.empty() ? 0 : &m_CurIndexData[0]);
+ int trianglesForStats = 0;
+
+ if (m_CurIndexBuffer)
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_CurIndexBuffer->GetBuffer()));
+
+ switch (m_CurRenderMode)
+ {
+ case kDrawIndexedQuads: // \todo [2013-06-13 pyry] This enum shouldn't even be here.
+ case kDrawIndexedTriangles:
+ GLES_CHK(glDrawElements(GL_TRIANGLES, m_CurIndexCount, GL_UNSIGNED_SHORT, indexPtr));
+ trianglesForStats = m_CurIndexCount/3;
+ break;
+
+ case kDrawIndexedTriangleStrip:
+ GLES_CHK(glDrawElements(GL_TRIANGLE_STRIP, m_CurIndexCount, GL_UNSIGNED_SHORT, indexPtr));
+ trianglesForStats = std::max<int>(0, m_CurIndexCount-2);
+ break;
+ case kDrawIndexedPoints:
+ GLES_CHK(glDrawElements(GL_POINTS, m_CurIndexCount, GL_UNSIGNED_SHORT, indexPtr));
+ trianglesForStats = m_CurIndexCount*2; // Assuming one quad
+ break;
+
+ case kDrawIndexedLines:
+ GLES_CHK(glDrawElements(GL_LINES, m_CurIndexCount, GL_UNSIGNED_SHORT, indexPtr));
+ trianglesForStats = m_CurIndexCount;
+ break;
+
+ case kDrawTriangleStrip:
+ GLES_CHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, m_CurVertexCount));
+ trianglesForStats = m_CurVertexCount-2;
+ break;
+
+ case kDrawQuads:
+ {
+ // Need to emulate with indices.
+ DataBufferGLES30* quadIndexBuf = GetQuadArrayIndexBuffer(m_CurVertexCount);
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadIndexBuf->GetBuffer()));
+ GLES_CHK(glDrawElements(GL_TRIANGLES, m_CurVertexCount/4 * 6, GL_UNSIGNED_SHORT, 0));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ trianglesForStats = m_CurVertexCount/2;
+ quadIndexBuf->RecordRender();
+ break;
+ }
+
+ default:
+ Assert(false);
+ }
+
+ if (m_CurIndexBuffer)
+ {
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ m_CurIndexBuffer->RecordRender();
+ }
+
+ GetRealGfxDevice().GetFrameStats().AddDrawCall(trianglesForStats, m_CurVertexCount);
+
+ // Release buffers and reset state
+ Cleanup();
+}
+
+void DynamicGLES3VBO::Recreate (void)
+{
+ Cleanup();
+}
+
+void DynamicGLES3VBO::Cleanup (void)
+{
+ if (m_CurVertexBuffer)
+ {
+ m_CurVertexBuffer->Release();
+ m_CurVertexBuffer = 0;
+ }
+
+ if (m_CurIndexBuffer)
+ {
+ m_CurIndexBuffer->Release();
+ m_CurIndexBuffer = 0;
+ }
+
+ if (m_QuadArrayIndexBuffer)
+ {
+ m_QuadArrayIndexBuffer->Release();
+ m_QuadArrayIndexBuffer = 0;
+ }
+
+ m_CurRenderMode = (RenderMode)0;
+ m_CurShaderChannelMask = 0;
+ m_CurStride = 0;
+ m_CurVertexCount = 0;
+ m_CurIndexCount = 0;
+
+ m_CurVertexData.clear();
+ m_CurIndexData.clear();
+}
+
+void DynamicGLES3VBO::ComputeVertexInputState (VertexArrayInfoGLES30& dst, const ChannelAssigns& channelAssigns)
+{
+ const UInt32 availableShaderChannels = m_CurShaderChannelMask; // Channels that have data in any of streams.
+ const UInt32 enabledTargets = channelAssigns.GetTargetMap();
+ UInt32 channelOffsets[kShaderChannelCount];
+
+ const UInt8* basePointer = m_CurVertexBuffer ? 0 : &m_CurVertexData[0];
+ const UInt32 buffer = m_CurVertexBuffer ? m_CurVertexBuffer->GetBuffer() : 0;
+
+ // Compute offsets per enabled shader channel.
+ {
+ UInt32 curOffset = 0;
+ for (int ndx = 0; ndx < kShaderChannelCount; ndx++)
+ {
+ if (availableShaderChannels & (1<<ndx))
+ {
+ channelOffsets[ndx] = curOffset;
+ curOffset += VBO::GetDefaultChannelByteSize(ndx);
+ }
+ else
+ channelOffsets[ndx] = 0;
+ }
+ }
+
+ for (int attribNdx = 0; attribNdx < kGLES3MaxVertexAttribs; attribNdx++)
+ {
+ const VertexComponent target = kVertexCompTargetsGLES3[attribNdx];
+
+ if ((enabledTargets & (1<<target)) == 0)
+ continue; // Not enabled.
+
+ const ShaderChannel sourceChannel = channelAssigns.GetSourceForTarget(target);
+
+ // \todo [pyry] Uh, what? Channel is enabled, but no valid source exists.
+ if (sourceChannel < 0)
+ continue;
+
+ Assert(0 <= sourceChannel && sourceChannel < kShaderChannelCount);
+
+ if ((availableShaderChannels & (1<<sourceChannel)) == 0)
+ continue; // Not available.
+
+ dst.arrays[attribNdx].componentType = VBO::GetDefaultChannelFormat(sourceChannel);
+ dst.arrays[attribNdx].numComponents = dst.arrays[attribNdx].componentType == kChannelFormatColor ? 4 : VBO::GetDefaultChannelDimension(sourceChannel);
+ dst.arrays[attribNdx].pointer = basePointer + channelOffsets[sourceChannel];
+ dst.arrays[attribNdx].stride = m_CurStride;
+ dst.buffers[attribNdx] = buffer;
+
+ dst.enabledArrays |= (1<<attribNdx);
+ }
+
+ // Fixed-function texgen stuff. \todo [pyry] Are these even used?
+ {
+ GfxDevice& device = GetRealGfxDevice();
+ const int texArrayBase = 3;
+ const int maxTexArrays = std::min(gGraphicsCaps.maxTexUnits, kGLES3MaxVertexAttribs-texArrayBase);
+
+ if (device.IsPositionRequiredForTexGen() && (availableShaderChannels & (1 << kShaderChannelVertex)))
+ {
+ const ShaderChannel srcChannel = kShaderChannelVertex;
+ const int format = VBO::GetDefaultChannelFormat(srcChannel);
+ const int numComps = VBO::GetDefaultChannelDimension(srcChannel);
+ const UInt32 offset = channelOffsets[srcChannel];
+
+ for (int texUnit = 0; texUnit < maxTexArrays; ++texUnit)
+ {
+ if (device.IsPositionRequiredForTexGen(texUnit))
+ {
+ const int arrNdx = texArrayBase + texUnit;
+ dst.arrays[arrNdx].componentType = format;
+ dst.arrays[arrNdx].numComponents = numComps;
+ dst.arrays[arrNdx].pointer = basePointer + offset;
+ dst.arrays[arrNdx].stride = m_CurStride;
+ dst.buffers[arrNdx] = buffer;
+
+ dst.enabledArrays |= (1<<arrNdx);
+ }
+ }
+ }
+
+ if (device.IsNormalRequiredForTexGen() && (availableShaderChannels & (1 << kShaderChannelNormal)))
+ {
+ const ShaderChannel srcChannel = kShaderChannelNormal;
+ const int format = VBO::GetDefaultChannelFormat(srcChannel);
+ const int numComps = VBO::GetDefaultChannelDimension(srcChannel);
+ const UInt32 offset = channelOffsets[srcChannel];
+
+ for (int texUnit = 0; texUnit < maxTexArrays; ++texUnit)
+ {
+ if (device.IsNormalRequiredForTexGen(texUnit))
+ {
+ const int arrNdx = texArrayBase + texUnit;
+ dst.arrays[arrNdx].componentType = format;
+ dst.arrays[arrNdx].numComponents = numComps;
+ dst.arrays[arrNdx].pointer = basePointer + offset;
+ dst.arrays[arrNdx].stride = m_CurStride;
+ dst.buffers[arrNdx] = buffer;
+
+ dst.enabledArrays |= (1<<arrNdx);
+ }
+ }
+ }
+ }
+}
+
+// \todo [2013-05-31 pyry] Better, more generic state cache
+
+// \note In theory we could use whole VertexArrayInfoGLES30 as state cache, but alas
+// bound buffers can be destroyed, recreated and state cache would be oblivious
+// to the fact that binding is now empty.
+static UInt32 sEnabledArrays = 0;
+
+void InvalidateVertexInputCacheGLES30()
+{
+ sEnabledArrays = 0;
+
+ for (int attribNdx = 0; attribNdx < gGraphicsCaps.gles30.maxAttributes; attribNdx++)
+ GLES_CHK(glDisableVertexAttribArray(attribNdx));
+}
+
+void SetupDefaultVertexArrayStateGLES30 (const VertexArrayInfoGLES30& info)
+{
+ UInt32 curBoundBuffer = 0;
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, 0));
+
+ for (int attribNdx = 0; attribNdx < kGLES3MaxVertexAttribs; attribNdx++)
+ {
+ const UInt32 enableBit = 1<<attribNdx;
+
+ if (info.enabledArrays & enableBit)
+ {
+ if (!(sEnabledArrays & enableBit))
+ GLES_CHK(glEnableVertexAttribArray(attribNdx));
+
+ const UInt32 buffer = info.buffers[attribNdx];
+ const int numComponents = info.arrays[attribNdx].numComponents;
+ const GLenum compType = kVertexTypeGLES3[info.arrays[attribNdx].componentType];
+ const bool normalized = IsVertexArrayNormalized(attribNdx, (VertexChannelFormat)info.arrays[attribNdx].componentType);
+ const int stride = info.arrays[attribNdx].stride;
+ const void* pointer = info.arrays[attribNdx].pointer;
+
+ if (curBoundBuffer != buffer)
+ {
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, buffer));
+ curBoundBuffer = buffer;
+ }
+
+ GLES_CHK(glVertexAttribPointer(attribNdx, numComponents, compType, normalized ? GL_TRUE : GL_FALSE, stride, pointer));
+ }
+ else if (sEnabledArrays & enableBit)
+ GLES_CHK(glDisableVertexAttribArray(attribNdx));
+ }
+
+ sEnabledArrays = info.enabledArrays;
+}
+
+#endif // GFX_SUPPORTS_OPENGLES30
diff --git a/Runtime/GfxDevice/opengles30/VBOGLES30.h b/Runtime/GfxDevice/opengles30/VBOGLES30.h
new file mode 100644
index 0000000..9e7f99a
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/VBOGLES30.h
@@ -0,0 +1,262 @@
+#ifndef VBO_GLES30_H
+#define VBO_GLES30_H
+
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "DataBuffersGLES30.h"
+#include "IncludesGLES30.h"
+
+#include <map>
+
+enum AttribLocationGLES30
+{
+ kGLES3AttribLocationPosition = 0,
+ kGLES3AttribLocationColor = 1,
+ kGLES3AttribLocationNormal = 2,
+ kGLES3AttribLocationTexCoord0 = 3,
+ kGLES3AttribLocationTexCoord1 = 4,
+ kGLES3AttribLocationTexCoord2 = 5,
+ kGLES3AttribLocationTexCoord3 = 6,
+ kGLES3AttribLocationTexCoord4 = 7,
+ kGLES3AttribLocationTexCoord5 = 8,
+ kGLES3AttribLocationTexCoord6 = 9,
+ kGLES3AttribLocationTexCoord7 = 10,
+
+ kGLES3MaxVertexAttribs, //!< Although implementations may support more, this limits VertexArrayInfoGLES30 to a reasonable value.
+};
+
+struct VertexInputInfoGLES30
+{
+ const void* pointer; //!< Pointer or offset.
+ UInt8 componentType; //!< Component type - of type VertexChannelFormat.
+ UInt8 numComponents; //!< Number of components.
+ UInt16 stride; //!< Attribute stride.
+
+ // Following parameters come from outside:
+ // normalize: Deduced based on location and type.
+ // buffer: Comes from VertexArrayInfoGLES30
+
+ VertexInputInfoGLES30 (void)
+ : pointer (0)
+ , componentType (0)
+ , numComponents (0)
+ , stride (0)
+ {
+ }
+
+ VertexInputInfoGLES30 (const void* pointer_, VertexChannelFormat componentType_, int numComponents_, UInt32 stride_)
+ : pointer (pointer_)
+ , componentType ((UInt8)componentType_)
+ , numComponents ((UInt8)numComponents_)
+ , stride ((UInt16)stride_)
+ {
+ // Check overflows.
+ Assert((VertexChannelFormat)componentType == componentType_ &&
+ (int)numComponents == numComponents_ &&
+ (UInt32)stride == stride_);
+ }
+};
+
+struct VertexArrayInfoGLES30
+{
+ UInt32 enabledArrays; //!< Bitmask of enabled arrays.
+ UInt32 buffers[kGLES3MaxVertexAttribs];
+ VertexInputInfoGLES30 arrays[kGLES3MaxVertexAttribs];
+
+ VertexArrayInfoGLES30 (void)
+ : enabledArrays(0)
+ {
+ }
+};
+
+// Setup vertex array state when no VAO is bound.
+void SetupDefaultVertexArrayStateGLES30 (const VertexArrayInfoGLES30& info);
+
+// Invalidate default VA input cache. Call this if you mess up with VA bindings or state gets lost otherwise.
+void InvalidateVertexInputCacheGLES30();
+
+struct VertexArrayInfoGLES30;
+class VertexArrayObjectGLES30;
+
+struct VAOCacheKeyGLES30
+{
+ UInt32 bufferIndices; //!< 4 indices with 8 bits each
+ ChannelAssigns channels;
+
+ inline VAOCacheKeyGLES30 (const ChannelAssigns& channels, UInt32 bufNdx0, UInt32 bufNdx1, UInt32 bufNdx2, UInt32 bufNdx3)
+ : bufferIndices ((bufNdx0 << 24) | (bufNdx1 << 16) | (bufNdx2 << 8) | bufNdx3)
+ , channels (channels)
+ {
+ Assert((bufNdx0 & ~0xff) == 0 &&
+ (bufNdx1 & ~0xff) == 0 &&
+ (bufNdx2 & ~0xff) == 0 &&
+ (bufNdx3 & ~0xff) == 0);
+ }
+
+ inline VAOCacheKeyGLES30 (void)
+ : bufferIndices (~0u)
+ , channels ()
+ {
+ }
+
+ inline bool operator== (const VAOCacheKeyGLES30& other) const
+ {
+ return bufferIndices == other.bufferIndices && channels == other.channels;
+ }
+};
+
+// Cache key must be changed if stream count changes.
+typedef char vaoCacheStreamCountAssert[kMaxVertexStreams == 4 ? 1 : -1];
+
+// VAO cache for single VBO. Can not be shared between VBOs. Cache must be cleared
+// if layout or any buffer in VAO is changed.
+// Linear search is used since VAO cache is very small and most static VBOs should find
+// match in first slot(s) anyway.
+class VAOCacheGLES30
+{
+public:
+ VAOCacheGLES30 (void);
+ ~VAOCacheGLES30 (void);
+
+ const VertexArrayObjectGLES30* Find (const VAOCacheKeyGLES30& key) const;
+ void Insert (const VAOCacheKeyGLES30& key, VertexArrayObjectGLES30* vao);
+ bool IsFull (void) const;
+
+ void Clear (void);
+
+public:
+ VAOCacheGLES30 (const VAOCacheGLES30&); // Not allowed!
+ VAOCacheGLES30& operator= (const VAOCacheGLES30&); // Not allowed!
+
+ enum
+ {
+ kCacheSize = 8
+ };
+
+ struct Entry
+ {
+ VAOCacheKeyGLES30 key;
+ VertexArrayObjectGLES30* vao;
+
+ inline Entry (void) : vao(0) {}
+ };
+
+ Entry m_Entries[kCacheSize];
+ int m_NextEntryNdx;
+};
+
+class GLES3VBO : public VBO
+{
+public:
+ GLES3VBO (void);
+ virtual ~GLES3VBO (void);
+
+ virtual void UpdateVertexData (const VertexBufferData& buffer);
+ virtual void UpdateIndexData (const IndexBufferData& buffer);
+
+ virtual bool MapVertexStream (VertexStreamData& outData, unsigned stream);
+ virtual void UnmapVertexStream (unsigned stream);
+
+ virtual void Cleanup (void);
+ virtual void Recreate (void);
+ virtual bool IsVertexBufferLost (void) const;
+
+ virtual bool IsUsingSourceVertices (void) const;
+ virtual bool IsUsingSourceIndices (void) const;
+
+ virtual int GetRuntimeMemorySize (void) const;
+
+ virtual void DrawVBO (const ChannelAssigns& channels, UInt32 firstIndexByte, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 firstVertex, UInt32 vertexCount);
+ virtual void DrawCustomIndexed (const ChannelAssigns& channels, void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount);
+
+ // This will return VBO for skinned (first) stream
+ UInt32 GetSkinningTargetVBO (void);
+
+
+ virtual void MarkBuffersLost() {}
+
+private:
+ void ComputeVertexInputState (VertexArrayInfoGLES30& dst, const ChannelAssigns& channels);
+ void MarkBuffersRendered (const ChannelAssigns& channels);
+
+ DataBufferGLES30* GetCurrentBuffer (int streamNdx);
+
+ const VertexArrayObjectGLES30* TryGetVAO (const ChannelAssigns& channels);
+
+ void Draw (DataBufferGLES30* indexBuffer,
+ const ChannelAssigns& channels,
+ GfxPrimitiveType topology,
+ UInt32 indexCount,
+ UInt32 indexOffset,
+ UInt32 vertexCountForStats);
+
+ enum
+ {
+ kBufferSwapChainSize = kBufferUpdateMinAgeGLES30+1
+ };
+
+ struct Stream
+ {
+ UInt32 channelMask; //!< Shader channels which this stream contains.
+ UInt32 stride;
+ int curBufferNdx; //!< Current buffer in swap chain
+ DataBufferGLES30* buffers[kBufferSwapChainSize];
+ UInt8* cpuBuf; //!< CPU-side copy, used for Recreate() and emulating mapbuffer.
+
+ Stream (void) : channelMask(0), stride(0), curBufferNdx(0), cpuBuf(0) { memset(&buffers[0], 0, sizeof(buffers)); }
+ };
+
+ // Vertex data
+ Stream m_StreamBuffers[kMaxVertexStreams];
+ ChannelInfoArray m_Channels;
+ int m_VertexCount;
+
+ // Index data
+ std::vector<UInt16> m_Indices; //!< Index data. Copy is kept for emulating quad primitive type.
+ DataBufferGLES30* m_IndexBuffer;
+
+ VAOCacheGLES30 m_VAOCache;
+};
+
+class DynamicGLES3VBO : public DynamicVBO
+{
+public:
+ DynamicGLES3VBO (void);
+ ~DynamicGLES3VBO (void);
+
+ virtual bool GetChunk (UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode renderMode, void** outVB, void** outIB);
+ virtual void ReleaseChunk (UInt32 actualVertices, UInt32 actualIndices);
+ virtual void DrawChunk (const ChannelAssigns& channels);
+
+ virtual void Recreate (void);
+
+private:
+ void ComputeVertexInputState (VertexArrayInfoGLES30& info, const ChannelAssigns& channels);
+
+ void Cleanup (void);
+
+ DataBufferGLES30* GetQuadArrayIndexBuffer (int vertexCount);
+
+ enum
+ {
+ kDataBufferThreshold = 1024
+ };
+
+ std::vector<UInt8> m_CurVertexData;
+ std::vector<UInt16> m_CurIndexData;
+
+ DataBufferGLES30* m_CurVertexBuffer;
+ DataBufferGLES30* m_CurIndexBuffer;
+
+ RenderMode m_CurRenderMode;
+ UInt32 m_CurShaderChannelMask;
+ UInt32 m_CurStride;
+ UInt32 m_CurVertexCount;
+ UInt32 m_CurIndexCount;
+
+ DataBufferGLES30* m_QuadArrayIndexBuffer; //!< Used for kDrawQuads mode.
+};
+
+#endif
diff --git a/Runtime/GfxDevice/threaded/ClientIDMapper.h b/Runtime/GfxDevice/threaded/ClientIDMapper.h
new file mode 100644
index 0000000..4156701
--- /dev/null
+++ b/Runtime/GfxDevice/threaded/ClientIDMapper.h
@@ -0,0 +1,46 @@
+#ifndef CLIENTIDMAPPER_H
+#define CLIENTIDMAPPER_H
+
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+#define ClientIDWrapper(type) ClientIDMapper::ClientID
+#define ClientIDWrapperHandle(type) ClientIDMapper::ClientID
+#else
+#define ClientIDWrapper(type) type*
+#define ClientIDWrapperHandle(type) type
+#endif
+
+class ClientIDMapper {
+public:
+ typedef UInt32 ClientID;
+
+ ClientIDMapper() :
+ m_HighestAllocatedID(0)
+ {
+ }
+
+ ClientID CreateID()
+ {
+ if (m_FreeIDs.empty())
+ return ++m_HighestAllocatedID;
+ else
+ {
+ ClientID retval = m_FreeIDs.back();
+ m_FreeIDs.pop_back();
+ return retval;
+ }
+ }
+
+ void FreeID(ClientID cid)
+ {
+ m_FreeIDs.push_back(cid);
+ }
+
+private:
+ ClientID m_HighestAllocatedID;
+ dynamic_array<ClientID> m_FreeIDs;
+};
+
+#endif \ No newline at end of file
diff --git a/Runtime/GfxDevice/threaded/GfxCommands.h b/Runtime/GfxDevice/threaded/GfxCommands.h
new file mode 100644
index 0000000..e148431
--- /dev/null
+++ b/Runtime/GfxDevice/threaded/GfxCommands.h
@@ -0,0 +1,669 @@
+#pragma once
+
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "Runtime/GfxDevice/threaded/ThreadedDeviceStates.h"
+#include "Runtime/GfxDevice/threaded/ClientIDMapper.h"
+
+struct ClientDeviceVBO;
+
+
+enum GfxCommand
+{
+ kGfxCmd_Unused = 10000,
+ kGfxCmd_InvalidateState,
+ kGfxCmd_VerifyState,
+ kGfxCmd_SetMaxBufferedFrames,
+ kGfxCmd_Clear,
+ kGfxCmd_SetUserBackfaceMode,
+ kGfxCmd_SetInvertProjectionMatrix,
+ kGfxCmd_SetViewportOffset,
+ kGfxCmd_CreateBlendState,
+ kGfxCmd_CreateDepthState,
+ kGfxCmd_CreateStencilState,
+ kGfxCmd_CreateRasterState,
+ kGfxCmd_SetBlendState,
+ kGfxCmd_SetDepthState,
+ kGfxCmd_SetStencilState,
+ kGfxCmd_SetRasterState,
+ kGfxCmd_SetSRGBState,
+ kGfxCmd_SetWorldMatrix,
+ kGfxCmd_SetViewMatrix,
+ kGfxCmd_SetProjectionMatrix,
+ kGfxCmd_SetInverseScale,
+ kGfxCmd_SetNormalizationBackface,
+ kGfxCmd_SetFFLighting,
+ kGfxCmd_SetMaterial,
+ kGfxCmd_SetColor,
+ kGfxCmd_SetViewport,
+ kGfxCmd_SetScissorRect,
+ kGfxCmd_DisableScissor,
+ kGfxCmd_CreateTextureCombiners,
+ kGfxCmd_DeleteTextureCombiners,
+ kGfxCmd_SetTextureCombiners,
+ kGfxCmd_SetTexture,
+ kGfxCmd_SetTextureParams,
+ kGfxCmd_SetTextureTransform,
+ kGfxCmd_SetTextureName,
+ kGfxCmd_SetMaterialProperties,
+ kGfxCmd_CreateGpuProgram,
+ kGfxCmd_SetShaders,
+ kGfxCmd_CreateShaderParameters,
+ kGfxCmd_DestroySubProgram,
+ kGfxCmd_SetConstantBufferInfo,
+ kGfxCmd_DisableLights,
+ kGfxCmd_SetLight,
+ kGfxCmd_SetAmbient,
+ kGfxCmd_EnableFog,
+ kGfxCmd_DisableFog,
+ kGfxCmd_BeginSkinning,
+ kGfxCmd_SkinMesh,
+ kGfxCmd_EndSkinning,
+ kGfxCmd_BeginStaticBatching,
+ kGfxCmd_StaticBatchMesh,
+ kGfxCmd_EndStaticBatching,
+ kGfxCmd_BeginDynamicBatching,
+ kGfxCmd_DynamicBatchMesh,
+#if ENABLE_SPRITES
+ kGfxCmd_DynamicBatchSprite,
+#endif
+ kGfxCmd_EndDynamicBatching,
+ kGfxCmd_AddBatchingStats,
+ kGfxCmd_CreateRenderColorSurface,
+ kGfxCmd_CreateRenderDepthSurface,
+ kGfxCmd_DestroyRenderSurface,
+ kGfxCmd_DiscardContents,
+ kGfxCmd_SetRenderTarget,
+ kGfxCmd_SetRenderTargetWithFlags,
+ kGfxCmd_ResolveColorSurface,
+ kGfxCmd_ResolveDepthIntoTexture,
+ kGfxCmd_SetSurfaceFlags,
+ kGfxCmd_UploadTexture2D,
+ kGfxCmd_UploadTextureSubData2D,
+ kGfxCmd_UploadTextureCube,
+ kGfxCmd_UploadTexture3D,
+ kGfxCmd_DeleteTexture,
+ kGfxCmd_BeginFrame,
+ kGfxCmd_EndFrame,
+ kGfxCmd_PresentFrame,
+ kGfxCmd_HandleInvalidState,
+ kGfxCmd_FinishRendering,
+ kGfxCmd_InsertCPUFence,
+ kGfxCmd_ImmediateVertex,
+ kGfxCmd_ImmediateNormal,
+ kGfxCmd_ImmediateColor,
+ kGfxCmd_ImmediateTexCoordAll,
+ kGfxCmd_ImmediateTexCoord,
+ kGfxCmd_ImmediateBegin,
+ kGfxCmd_ImmediateEnd,
+ kGfxCmd_CaptureScreenshot,
+ kGfxCmd_ReadbackImage,
+ kGfxCmd_GrabIntoRenderTexture,
+ kGfxCmd_SetActiveContext,
+ kGfxCmd_ResetFrameStats,
+ kGfxCmd_BeginFrameStats,
+ kGfxCmd_EndFrameStats,
+ kGfxCmd_SaveDrawStats,
+ kGfxCmd_RestoreDrawStats,
+ kGfxCmd_SynchronizeStats,
+ kGfxCmd_SetAntiAliasFlag,
+ kGfxCmd_SetWireframe,
+ kGfxCmd_DrawUserPrimitives,
+ kGfxCmd_Quit,
+
+ kGfxCmd_VBO_UpdateVertexData,
+ kGfxCmd_VBO_UpdateIndexData,
+ kGfxCmd_VBO_Draw,
+ kGfxCmd_VBO_DrawCustomIndexed,
+ kGfxCmd_VBO_Recreate,
+ kGfxCmd_VBO_MapVertexStream,
+ kGfxCmd_VBO_IsVertexBufferLost,
+ kGfxCmd_VBO_SetVertexStreamMode,
+ kGfxCmd_VBO_SetIndicesDynamic,
+ kGfxCmd_VBO_GetRuntimeMemorySize,
+ kGfxCmd_VBO_GetVertexSize,
+ kGfxCmd_VBO_AddExtraUvChannels,
+ kGfxCmd_VBO_CopyExtraUvChannels,
+ kGfxCmd_VBO_Constructor,
+ kGfxCmd_VBO_Destructor,
+ kGfxCmd_VBO_UseAsStreamOutput,
+
+ kGfxCmd_DynVBO_Chunk,
+ kGfxCmd_DynVBO_DrawChunk,
+
+ kGfxCmd_DisplayList_Call,
+ kGfxCmd_DisplayList_End,
+
+ kGfxCmd_CreateWindow,
+ kGfxCmd_SetActiveWindow,
+ kGfxCmd_WindowReshape,
+ kGfxCmd_WindowDestroy,
+ kGfxCmd_BeginRendering,
+ kGfxCmd_EndRendering,
+
+ kGfxCmd_AcquireThreadOwnership,
+ kGfxCmd_ReleaseThreadOwnership,
+
+ kGfxCmd_BeginProfileEvent,
+ kGfxCmd_EndProfileEvent,
+ kGfxCmd_ProfileControl,
+ kGfxCmd_BeginTimerQueries,
+ kGfxCmd_EndTimerQueries,
+
+ kGfxCmd_TimerQuery_Constructor,
+ kGfxCmd_TimerQuery_Destructor,
+ kGfxCmd_TimerQuery_Measure,
+ kGfxCmd_TimerQuery_GetElapsed,
+
+ kGfxCmd_InsertCustomMarker,
+
+ kGfxCmd_SetComputeBufferData,
+ kGfxCmd_GetComputeBufferData,
+ kGfxCmd_CopyComputeBufferCount,
+ kGfxCmd_SetRandomWriteTargetTexture,
+ kGfxCmd_SetRandomWriteTargetBuffer,
+ kGfxCmd_ClearRandomWriteTargets,
+ kGfxCmd_CreateComputeProgram,
+ kGfxCmd_DestroyComputeProgram,
+ kGfxCmd_CreateComputeConstantBuffers,
+ kGfxCmd_DestroyComputeConstantBuffers,
+ kGfxCmd_CreateComputeBuffer,
+ kGfxCmd_DestroyComputeBuffer,
+ kGfxCmd_UpdateComputeConstantBuffers,
+ kGfxCmd_UpdateComputeResources,
+ kGfxCmd_DispatchComputeProgram,
+ kGfxCmd_DrawNullGeometry,
+ kGfxCmd_DrawNullGeometryIndirect,
+ kGfxCmd_QueryGraphicsCaps,
+ kGfxCmd_SetGpuProgramParameters,
+ kGfxCmd_DeleteGPUSkinningInfo,
+ kGfxCmd_SkinOnGPU,
+ kGfxCmd_UpdateSkinSourceData,
+ kGfxCmd_UpdateSkinBonePoses,
+
+ // Keep platform specific flags last
+#if UNITY_XENON
+ kGfxCmd_RawVBO_Constructor,
+ kGfxCmd_RawVBO_Destructor,
+ kGfxCmd_RawVBO_Next,
+ kGfxCmd_RawVBO_Write,
+ kGfxCmd_RawVBO_InvalidateGpuCache,
+
+ kGfxCmd_EnablePersistDisplayOnQuit,
+ kGfxCmd_OnLastFrameCallback,
+
+ kGfxCmd_RegisterTexture2D,
+ kGfxCmd_PatchTexture2D,
+ kGfxCmd_DeleteTextureEntryOnly,
+ kGfxCmd_UnbindAndDelayReleaseTexture,
+ kGfxCmd_SetTextureWrapModes,
+
+ kGfxCmd_VideoPlayer_Constructor,
+ kGfxCmd_VideoPlayer_Destructor,
+ kGfxCmd_VideoPlayer_Render,
+ kGfxCmd_VideoPlayer_Pause,
+ kGfxCmd_VideoPlayer_Resume,
+ kGfxCmd_VideoPlayer_Stop,
+ kGfxCmd_VideoPlayer_SetPlaySpeed,
+ kGfxCmd_VideoPlayer_Play,
+ kGfxCmd_SetHiStencilState,
+ kGfxCmd_HiStencilFlush,
+ kGfxCmd_SetNullPixelShader,
+ kGfxCmd_EnableHiZ,
+#endif
+
+ kGfxCmd_Count
+};
+
+typedef int GfxCmdBool;
+
+
+struct GfxCmdClear
+{
+ UInt32 clearFlags;
+ Vector4f color;
+ float depth;
+ int stencil;
+};
+
+struct GfxCmdSetNormalizationBackface
+{
+ NormalizationMode mode;
+ bool backface;
+};
+
+struct GfxCmdSetFFLighting
+{
+ bool on;
+ bool separateSpecular;
+ ColorMaterialMode colorMaterial;
+};
+
+struct GfxCmdCreateTextureCombiners
+{
+ int count;
+ bool hasVertexColorOrLighting;
+ bool usesAddSpecular;
+};
+
+struct GfxCmdSetTexture
+{
+ ShaderType shaderType;
+ int unit;
+ int samplerUnit;
+ TextureID texture;
+ TextureDimension dim;
+ float bias;
+};
+
+struct GfxCmdSetTextureParams
+{
+ TextureID texture;
+ TextureDimension texDim;
+ TextureFilterMode filter;
+ TextureWrapMode wrap;
+ int anisoLevel;
+ bool hasMipMap;
+ TextureColorSpace colorSpace;
+};
+
+struct GfxCmdSetTextureName
+{
+ TextureID texture;
+ int nameLength;
+};
+
+struct GfxCmdSetTextureBias
+{
+ int unit;
+ float bias;
+};
+
+struct GfxCmdSetTextureTransform
+{
+ int unit;
+ TextureDimension dim;
+ TexGenMode texGen;
+ bool identity;
+ Matrix4x4f matrix;
+};
+
+struct GfxCmdSetMaterialProperties
+{
+ int propertyCount;
+ int bufferSize;
+};
+
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+struct GfxCmdCreateGpuProgram
+{
+ const char *source;
+ CreateGpuProgramOutput* output;
+ GpuProgram** result;
+};
+#endif
+
+struct GfxCmdSetShaders
+{
+ ClientIDWrapper(GpuProgram) programs[kShaderTypeCount];
+ ClientIDWrapper(const GpuProgramParameters) params[kShaderTypeCount];
+ int paramsBufferSize[kShaderTypeCount];
+};
+
+struct GfxCmdCreateShaderParameters
+{
+ ShaderLab::SubProgram* program;
+ FogMode fogMode;
+};
+
+struct GfxCmdBeginStaticBatching
+{
+ ChannelAssigns channels;
+ GfxPrimitiveType topology;
+};
+
+struct GfxCmdStaticBatchMesh
+{
+ UInt32 firstVertex;
+ UInt32 vertexCount;
+ IndexBufferData indices;
+ UInt32 firstIndexByte;
+ UInt32 indexCount;
+};
+
+struct GfxCmdEndStaticBatching
+{
+ ClientDeviceVBO* vbo;
+ Matrix4x4f matrix;
+ TransformType transformType;
+ int sourceChannels;
+};
+
+struct GfxCmdBeginDynamicBatching
+{
+ ChannelAssigns shaderChannels;
+ UInt32 channelsInVBO;
+ size_t maxVertices;
+ size_t maxIndices;
+ GfxPrimitiveType topology;
+};
+
+struct GfxCmdDynamicBatchMesh
+{
+ Matrix4x4f matrix;
+ VertexBufferData vertices;
+ UInt32 firstVertex;
+ UInt32 vertexCount;
+ IndexBufferData indices;
+ UInt32 firstIndexByte;
+ UInt32 indexCount;
+};
+
+#if ENABLE_SPRITES
+struct GfxCmdDynamicBatchSprite
+{
+ Matrix4x4f matrix;
+ const SpriteRenderData *sprite;
+ ColorRGBA32 color;
+};
+#endif
+struct GfxCmdEndDynamicBatching
+{
+ TransformType transformType;
+};
+
+struct GfxCmdAddBatchingStats
+{
+ int batchedTris;
+ int batchedVerts;
+ int batchedCalls;
+};
+
+struct GfxCmdCreateRenderColorSurface
+{
+ TextureID textureID;
+ int width;
+ int height;
+ int samples;
+ int depth;
+ TextureDimension dim;
+ RenderTextureFormat format;
+ UInt32 createFlags;
+};
+
+struct GfxCmdCreateRenderDepthSurface
+{
+ TextureID textureID;
+ int width;
+ int height;
+ int samples;
+ TextureDimension dim;
+ DepthBufferFormat depthFormat;
+ UInt32 createFlags;
+};
+
+struct GfxCmdSetRenderTarget
+{
+ ClientIDWrapperHandle(RenderSurfaceHandle) colorHandles[kMaxSupportedRenderTargets];
+ ClientIDWrapperHandle(RenderSurfaceHandle) depthHandle;
+ int colorCount;
+ int mipLevel;
+ CubemapFace face;
+};
+
+struct GfxCmdResolveDepthIntoTexture
+{
+ ClientIDWrapperHandle(RenderSurfaceHandle) colorHandle;
+ ClientIDWrapperHandle(RenderSurfaceHandle) depthHandle;
+};
+
+struct GfxCmdSetSurfaceFlags
+{
+ ClientIDWrapperHandle(RenderSurfaceHandle) surf;
+ UInt32 flags;
+ UInt32 keepFlags;
+};
+
+struct GfxCmdUploadTexture2D
+{
+ TextureID texture;
+ TextureDimension dimension;
+ int srcSize;
+ int width;
+ int height;
+ TextureFormat format;
+ int mipCount;
+ UInt32 uploadFlags;
+ int skipMipLevels;
+ TextureUsageMode usageMode;
+ TextureColorSpace colorSpace;
+};
+
+struct GfxCmdUploadTextureSubData2D
+{
+ TextureID texture;
+ int srcSize;
+ int mipLevel;
+ int x;
+ int y;
+ int width;
+ int height;
+ TextureFormat format;
+ TextureColorSpace colorSpace;
+};
+
+struct GfxCmdUploadTextureCube
+{
+ TextureID texture;
+ int srcSize;
+ int faceDataSize;
+ int size;
+ TextureFormat format;
+ int mipCount;
+ UInt32 uploadFlags;
+ TextureColorSpace colorSpace;
+};
+
+struct GfxCmdUploadTexture3D
+{
+ TextureID texture;
+ int srcSize;
+ int width;
+ int height;
+ int depth;
+ TextureFormat format;
+ int mipCount;
+ UInt32 uploadFlags;
+};
+
+struct GfxCmdDrawUserPrimitives
+{
+ GfxPrimitiveType type;
+ int vertexCount;
+ UInt32 vertexChannels;
+ int stride;
+};
+
+struct GfxCmdVBODraw
+{
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceVBO vbo;
+#else
+ ClientDeviceVBO* vbo;
+#endif
+ ChannelAssigns channels;
+ UInt32 firstIndexByte;
+ UInt32 indexCount;
+ GfxPrimitiveType topology;
+ UInt32 firstVertex;
+ UInt32 vertexCount;
+};
+
+struct GfxCmdVBODrawCustomIndexed
+{
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceVBO vbo;
+#else
+ ClientDeviceVBO* vbo;
+#endif
+ ChannelAssigns channels;
+ UInt32 indexCount;
+ GfxPrimitiveType topology;
+ UInt32 vertexRangeBegin;
+ UInt32 vertexRangeEnd;
+ UInt32 drawVertexCount;
+};
+
+struct GfxCmdVBODrawStripWireframe
+{
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceVBO vbo;
+#else
+ ClientDeviceVBO* vbo;
+#endif
+ ChannelAssigns channels;
+ UInt32 indexCount;
+ UInt32 triCount;
+ UInt32 vertexCount;
+};
+
+struct GfxCmdVBOMapVertexStream
+{
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceVBO vbo;
+#else
+ ClientDeviceVBO* vbo;
+#endif
+ unsigned stream;
+ UInt32 size;
+};
+
+struct GfxCmdVBOSetVertexStreamMode
+{
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceVBO vbo;
+#else
+ ClientDeviceVBO* vbo;
+#endif
+ unsigned stream;
+ int mode;
+};
+
+struct GfxCmdVBOSetSetIndicesDynamic
+{
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceVBO vbo;
+#else
+ ClientDeviceVBO* vbo;
+#endif
+ int dynamic;
+};
+
+struct GfxCmdVBOAddExtraUvChannels
+{
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceVBO vbo;
+#else
+ ClientDeviceVBO* vbo;
+#endif
+ UInt32 size;
+ int extraUvCount;
+};
+
+struct GfxCmdVBOCopyExtraUvChannels
+{
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceVBO dest;
+ ClientDeviceVBO source;
+#else
+ ClientDeviceVBO* dest;
+ ClientDeviceVBO* source;
+#endif
+
+};
+
+
+struct GfxCmdVector3
+{
+ float x;
+ float y;
+ float z;
+};
+
+struct GfxCmdVector4
+{
+ float x;
+ float y;
+ float z;
+ float w;
+};
+
+struct GfxCmdImmediateTexCoord
+{
+ int unit;
+ float x;
+ float y;
+ float z;
+};
+
+struct GfxCmdCaptureScreenshot
+{
+ int left;
+ int bottom;
+ int width;
+ int height;
+ UInt8* rgba32;
+ bool* success;
+};
+
+struct GfxCmdReadbackImage
+{
+ ImageReference& image;
+ int left;
+ int bottom;
+ int width;
+ int height;
+ int destX;
+ int destY;
+ bool* success;
+};
+
+struct GfxCmdGrabIntoRenderTexture
+{
+ ClientIDWrapperHandle(RenderSurfaceHandle) rs;
+ ClientIDWrapperHandle(RenderSurfaceHandle) rd;
+ int x;
+ int y;
+ int width;
+ int height;
+};
+
+struct GfxCmdDynVboChunk
+{
+ UInt32 channelMask;
+ UInt32 vertexStride;
+ UInt32 actualVertices;
+ UInt32 actualIndices;
+ UInt32 renderMode;
+};
+
+#if UNITY_WIN && UNITY_EDITOR
+
+struct GfxCmdWindowReshape
+{
+ int width;
+ int height;
+ DepthBufferFormat depthFormat;
+ int antiAlias;
+};
+
+struct GfxCmdCreateWindow
+{
+ HWND window;
+ int width;
+ int height;
+ DepthBufferFormat depthFormat;
+ int antiAlias;
+};
+
+#endif
diff --git a/Runtime/GfxDevice/threaded/GfxDeviceClient.cpp b/Runtime/GfxDevice/threaded/GfxDeviceClient.cpp
new file mode 100644
index 0000000..39295c6
--- /dev/null
+++ b/Runtime/GfxDevice/threaded/GfxDeviceClient.cpp
@@ -0,0 +1,3922 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_MULTITHREADED_CODE
+#if ENABLE_SPRITES
+#include "Runtime/Graphics/SpriteFrame.h"
+#endif
+#include "Runtime/GfxDevice/threaded/GfxDeviceClient.h"
+#include "Runtime/GfxDevice/threaded/GfxDeviceWorker.h"
+#include "Runtime/GfxDevice/threaded/GfxCommands.h"
+#include "Runtime/GfxDevice/threaded/GfxReturnStructs.h"
+#include "Runtime/GfxDevice/GfxDeviceSetup.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/ThreadUtility.h"
+#include "Runtime/GfxDevice/threaded/ThreadedVBO.h"
+#include "Runtime/Shaders/MaterialProperties.h"
+#include "Runtime/GfxDevice/threaded/ThreadedWindow.h"
+#include "Runtime/GfxDevice/threaded/ThreadedDisplayList.h"
+#include "Runtime/GfxDevice/threaded/ThreadedTimerQuery.h"
+#include "External/shaderlab/Library/program.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/shaderlab/Library/pass.h"
+#include "Runtime/Filters/Mesh/MeshSkinning.h"
+#include "Runtime/Graphics/GraphicsHelper.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/GfxDevice/GPUSkinningInfo.h"
+#include "Runtime/Profiler/ProfilerImpl.h"
+#if UNITY_XENON
+#include "PlatformDependent/Xbox360/Source/GfxDevice/GfxXenonVBO.h"
+#include "PlatformDependent/Xbox360/Source/Services/VideoPlayer.h"
+#endif
+
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+#include <ppapi/cpp/instance.h>
+#include "External/NPAPI2NaCl/Common/UnityInterfaces.h"
+#include "PlatformDependent/PepperPlugin/UnityInstance.h"
+
+void *CreateSharedGfxDeviceAndBuffer(size_t size)
+{
+ struct UNITY_GfxDevice_1_0 *gfxInterface = (UNITY_GfxDevice_1_0*)pp::Module::Get()->GetBrowserInterface(UNITY_GFXDEVICE_INTERFACE_1_0);
+ PP_Resource res = gfxInterface->Create(GetUnityInstance().pp_instance(), size);
+ return gfxInterface->GetSharedMemoryBufferAddress(res);
+}
+#endif
+
+PROFILER_INFORMATION(gGfxWaitForPresentProf, "Gfx.WaitForPresent", kProfilerOverhead)
+
+class ClientGPUSkinningInfo : public GPUSkinningInfo
+{
+ friend class GfxDeviceClient;
+private:
+ ClientGPUSkinningInfo(GPUSkinningInfo *realSkinInfo): GPUSkinningInfo(), m_realSkinInfo(realSkinInfo) {}
+ virtual ~ClientGPUSkinningInfo() {}
+
+ GPUSkinningInfo *m_realSkinInfo;
+
+public:
+ virtual UInt32 GetVertexCount() { return m_realSkinInfo->GetVertexCount(); }
+ virtual UInt32 GetChannelMap() { return m_realSkinInfo->GetChannelMap(); }
+ virtual int GetStride() { return m_realSkinInfo->GetStride(); }
+ virtual VBO * GetDestVBO() { return m_DestVBO; }
+ virtual UInt32 GetBonesPerVertex() { return m_realSkinInfo->GetBonesPerVertex(); }
+
+ /** Update vertex count */
+ virtual void SetVertexCount(UInt32 count) { m_realSkinInfo->SetVertexCount(count); }
+
+ /** Update channel map */
+ virtual void SetChannelMap(UInt32 channelmap) { m_realSkinInfo->SetChannelMap(channelmap); }
+
+ /** Update stride of the vertices in bytes (not including skin data). */
+ virtual void SetStride(int stride) { m_realSkinInfo->SetStride(stride); }
+
+ /** Update destination VBO */
+ virtual void SetDestVBO(VBO *vbo)
+ {
+ m_DestVBO = vbo;
+ // realskininfo->m_destVBO will be updated lazily on the actual skinning call
+ }
+
+ virtual void SetBonesPerVertex(UInt32 bones)
+ {
+ m_realSkinInfo->SetBonesPerVertex(bones);
+ }
+
+ /** Threading support: Read from stream. */
+ virtual void Read(ThreadedStreamBuffer& stream)
+ {
+ AssertBreak(false);
+ }
+ /** Threading support: Write to stream. */
+ virtual void Write(ThreadedStreamBuffer& stream)
+ {
+ AssertBreak(false);
+ }
+
+ /** Helper function for thread worker: clear the internal structures without releasing,
+ so the internal resources won't be double-released. */
+ virtual void Clear()
+ {
+ AssertBreak(false);
+ }
+
+};
+
+
+
+
+GfxDevice* CreateClientGfxDevice(GfxDeviceRenderer renderer, UInt32 flags, size_t bufferSize, void *buffer)
+{
+ bool forceRef = (flags & kClientDeviceForceRef) != 0;
+
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+ if (flags & kClientDeviceUseRealDevice)
+ {
+ return CreateRealGfxDevice (renderer, forceRef);
+ }
+#endif
+
+ bool threaded = (flags & kClientDeviceThreaded) != 0;
+ bool clientProcess = (flags & kClientDeviceClientProcess) != 0;
+ bool workerProcess = (flags & kClientDeviceWorkerProcess) != 0;
+
+ printf_console("GfxDevice: creating device client; threaded=%i\n", (int)threaded);
+
+ // Threading mode must be set before creating device (so D3D9 gets correct flags)
+ SetGfxThreadingMode (threaded ? kGfxThreadingModeThreaded : kGfxThreadingModeNonThreaded);
+
+ if (bufferSize == 0)
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+ // The native client uses a larger default size for the ring buffer, since it cannot
+ // flush the buffer at arbitrary times, so it needs to be big enough to hold complete
+ // data for texture upload commands.
+ bufferSize = 64*1024*1024;
+#else
+ bufferSize = 8*1024*1024;
+#endif
+ }
+
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+ if (clientProcess)
+ buffer = CreateSharedGfxDeviceAndBuffer(bufferSize);
+#endif
+
+ GfxDeviceClient* device = UNITY_NEW_AS_ROOT(GfxDeviceClient(threaded, clientProcess, bufferSize, buffer), kMemGfxDevice, "GfxClientDevice", "");
+ if (clientProcess)
+ {
+ device->SetRealGfxDevice (NULL);
+ device->QueryGraphicsCaps();
+ return device;
+ }
+
+ GfxDeviceWorker* worker = device->GetGfxDeviceWorker();
+ if(renderer == kGfxRendererOpenGLES30)
+ SetGfxDevice(device); // Set this on GLES30 as we do stuff in OnDeviceCreated that requires this. Can't set it for all as it breaks NaCL build in teamcity.
+ GfxThreadableDevice* realDevice = worker->Startup(renderer, threaded && !workerProcess, forceRef);
+
+ if (realDevice)
+ {
+ device->SetRealGfxDevice (realDevice);
+ device->AcquireThreadOwnership ();
+ realDevice->OnDeviceCreated (false);
+ device->ReleaseThreadOwnership ();
+ return device;
+ }
+
+ // Failed to create threaded device
+ SetGfxThreadingMode (kGfxThreadingModeDirect);
+
+ // Client device deletes worker
+ UNITY_DELETE(device, kMemGfxDevice);
+ return NULL;
+}
+
+bool GfxDeviceWorkerProcessRunCommand()
+{
+ GfxDeviceClient& device = (GfxDeviceClient&)GetGfxDevice();
+ GfxDeviceWorker* worker = device.GetGfxDeviceWorker();
+ return worker->RunCommandIfDataIsAvailable();
+}
+
+void GfxDeviceClient::WaitForSignal ()
+{
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+ struct UNITY_GfxDevice_1_0 *gfxInterface = (UNITY_GfxDevice_1_0*)pp::Module::Get()->GetBrowserInterface(UNITY_GFXDEVICE_INTERFACE_1_0);
+ return gfxInterface->WaitForSignal(0);
+#else
+ if (m_DeviceWorker)
+ m_DeviceWorker->WaitForSignal();
+#endif
+}
+
+void GfxDeviceClient::QueryGraphicsCaps()
+{
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_QueryGraphicsCaps);
+
+ ReadbackData(m_ReadbackData);
+
+ // no, really, we need to properly serialize this stuff with all the strings. this is just a hack to get running.
+ size_t offset = (char*)&gGraphicsCaps.vendorID - (char*)&gGraphicsCaps;
+ memcpy (&gGraphicsCaps.vendorID, m_ReadbackData.data(), std::min(m_ReadbackData.size(), sizeof(GraphicsCaps) - offset));
+}
+
+
+GfxDeviceClient::GfxDeviceClient(bool threaded, bool clientProcess, size_t bufferSize, void *buffer) :
+ m_RealDevice(NULL),
+ m_Threaded(threaded),
+ m_Serialize(threaded),
+ m_RecordDepth(0),
+ m_MaxCallDepth(1),
+ m_DynamicVBO(NULL),
+ m_sRGBWrite(false)
+{
+ // Create stack of display lists (including "top" level)
+ m_DisplayListStack = new DisplayListContext[m_MaxCallDepth + 1];
+ if (m_Threaded)
+ {
+ if (buffer)
+ m_DisplayListStack[0].commandQueue.CreateFromMemory(ThreadedStreamBuffer::kModeCrossProcess, bufferSize, buffer);
+ else
+ m_DisplayListStack[0].commandQueue.Create(ThreadedStreamBuffer::kModeThreaded, bufferSize);
+ m_DynamicVBO = new ThreadedDynamicVBO(*this);
+ }
+ for (int i = 1; i <= m_MaxCallDepth; i++)
+ m_DisplayListStack[i].commandQueue.Create(ThreadedStreamBuffer::kModeGrowable, 0);
+ m_CurrentContext = &m_DisplayListStack[0];
+ m_CommandQueue = &m_CurrentContext->commandQueue;
+ m_InvertProjectionMatrix = false;
+ #if GFX_USES_VIEWPORT_OFFSET
+ m_ViewportOffset.Set(0.0f, 0.0f);
+ #endif
+ m_TransformState.Invalidate(m_BuiltinParamValues);
+ m_ScissorEnabled = -1;
+ m_PresentPending = false;
+ m_Wireframe = false;
+ m_CurrentTargetWidth = 0;
+ m_CurrentTargetHeight = 0;
+ m_CurrentWindowWidth = 0;
+ m_CurrentWindowHeight = 0;
+ m_ThreadOwnershipCount = 0;
+ m_CurrentCPUFence = 0;
+ m_PresentFrameID = 0;
+ if (clientProcess)
+ m_DeviceWorker = NULL;
+ else
+ m_DeviceWorker = new GfxDeviceWorker(4, m_CommandQueue);
+
+ OnCreate();
+
+ {
+ ClientDeviceRenderSurface* color = new ClientDeviceRenderSurface(0, 0);
+ RenderSurfaceBase_InitColor(*color);
+ color->backBuffer = true;
+ color->internalHandle = m_RealDevice ? m_RealDevice->GetBackBufferColorSurface() : RenderSurfaceHandle();
+ SetBackBufferColorSurface(color);
+ }
+ {
+ ClientDeviceRenderSurface* depth = new ClientDeviceRenderSurface(0, 0);
+ RenderSurfaceBase_InitDepth(*depth);
+ depth->backBuffer = true;
+ depth->internalHandle = m_RealDevice ? m_RealDevice->GetBackBufferDepthSurface() : RenderSurfaceHandle();
+ SetBackBufferDepthSurface(depth);
+ }
+
+#if GFX_DEVICE_CLIENT_TRACK_TEXGEN
+ posForTexGen = 0;
+ nrmForTexGen = 0;
+ CompileTimeAssert(sizeof(posForTexGen) * 8 == kMaxSupportedTextureUnitsGLES, "posForTexGen should have enough bits for tex units");
+ CompileTimeAssert(sizeof(nrmForTexGen) * 8 == kMaxSupportedTextureUnitsGLES, "nrmForTexGen should have enough bits for tex units");
+#endif
+}
+
+GfxDeviceClient::~GfxDeviceClient()
+{
+ CheckMainThread();
+ if (m_Threaded && m_RealDevice)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_Quit);
+ SubmitCommands();
+ WaitForSignal();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ // m_CommandQueue was not allocated
+ m_CommandQueue = NULL;
+ delete[] m_DisplayListStack;
+ if (m_Threaded)
+ delete m_DynamicVBO;
+ delete m_DeviceWorker;
+}
+
+void GfxDeviceClient::InvalidateState()
+{
+ CheckMainThread();
+ DebugAssert(!IsRecording());
+ m_TransformState.Invalidate(m_BuiltinParamValues);
+ m_FogParams.Invalidate();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_InvalidateState);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->InvalidateState();
+}
+
+#if GFX_DEVICE_VERIFY_ENABLE
+void GfxDeviceClient::VerifyState()
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_VerifyState);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->VerifyState();
+}
+#endif
+
+
+void GfxDeviceClient::SetMaxBufferedFrames (int bufferSize)
+{
+ CheckMainThread();
+ GfxDevice::SetMaxBufferedFrames(bufferSize);
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetMaxBufferedFrames);
+ m_CommandQueue->WriteValueType<int>(bufferSize);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetMaxBufferedFrames(bufferSize);
+}
+
+
+void GfxDeviceClient::Clear( UInt32 clearFlags, const float color[4], float depth, int stencil )
+{
+ CheckMainThread();
+
+ // mark cleared surfaces as "no contents" if we're tracking that
+ if (GetFrameStats().m_StatsEnabled)
+ {
+ if (clearFlags & kGfxClearColor)
+ {
+ for (int i = 0; i < kMaxSupportedRenderTargets; ++i)
+ {
+ if (!m_ActiveRenderColorSurfaces[i].IsValid ())
+ continue;
+ ClientDeviceRenderSurface* colorSurf = (ClientDeviceRenderSurface*)m_ActiveRenderColorSurfaces[i].object;
+ colorSurf->state = ClientDeviceRenderSurface::kCleared;
+ }
+ }
+ if ((clearFlags & kGfxClearDepthStencil) && m_ActiveRenderDepthSurface.IsValid ())
+ {
+ ClientDeviceRenderSurface* depthSurf = (ClientDeviceRenderSurface*)m_ActiveRenderDepthSurface.object;
+ if (depthSurf)
+ depthSurf->state = ClientDeviceRenderSurface::kCleared;
+ }
+ }
+
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_Clear);
+ GfxCmdClear clear = { clearFlags, Vector4f(color), depth, stencil };
+ m_CommandQueue->WriteValueType<GfxCmdClear>(clear);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->Clear(clearFlags, color, depth, stencil);
+}
+
+void GfxDeviceClient::SetUserBackfaceMode( bool enable )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetUserBackfaceMode);
+ m_CommandQueue->WriteValueType<GfxCmdBool>(enable);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetUserBackfaceMode(enable);
+}
+
+void GfxDeviceClient::SetWireframe(bool wire)
+{
+ CheckMainThread();
+ m_Wireframe = wire;
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetWireframe);
+ m_CommandQueue->WriteValueType<GfxCmdBool>(wire);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetWireframe(wire);
+}
+
+bool GfxDeviceClient::GetWireframe() const
+{
+ return m_Wireframe;
+}
+
+
+void GfxDeviceClient::SetInvertProjectionMatrix( bool enable )
+{
+ CheckMainThread();
+ m_InvertProjectionMatrix = enable;
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetInvertProjectionMatrix);
+ m_CommandQueue->WriteValueType<GfxCmdBool>(enable);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetInvertProjectionMatrix(enable);
+}
+
+bool GfxDeviceClient::GetInvertProjectionMatrix() const
+{
+ CheckMainThread();
+ return m_InvertProjectionMatrix;
+}
+
+#if GFX_USES_VIEWPORT_OFFSET
+void GfxDeviceClient::SetViewportOffset( float x, float y )
+{
+ CheckMainThread();
+ m_ViewportOffset = Vector2f(x, y);
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetViewportOffset);
+ m_CommandQueue->WriteValueType<Vector2f>(m_ViewportOffset);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetViewportOffset(x, y);
+}
+
+void GfxDeviceClient::GetViewportOffset( float &x, float &y ) const
+{
+ x = m_ViewportOffset.x;
+ y = m_ViewportOffset.y;
+}
+#endif
+
+DeviceBlendState* GfxDeviceClient::CreateBlendState(const GfxBlendState& state)
+{
+ CheckMainThread();
+ SET_ALLOC_OWNER(this);
+ DebugAssert(!IsRecording());
+ std::pair<CachedBlendStates::iterator, bool> added = m_CachedBlendStates.insert(std::make_pair(state, ClientDeviceBlendState(state)));
+ ClientDeviceBlendState* result = &added.first->second;
+ if (!added.second)
+ return result;
+
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_CreateBlendState);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ result->internalState = m_BlendStateMapper.CreateID();
+ m_CommandQueue->WriteValueType<ClientDeviceBlendState>(*result);
+#else
+ m_CommandQueue->WriteValueType<ClientDeviceBlendState*>(result);
+#endif
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ result->internalState = m_RealDevice->CreateBlendState(state);
+#endif
+
+ return result;
+}
+
+DeviceDepthState* GfxDeviceClient::CreateDepthState(const GfxDepthState& state)
+{
+ CheckMainThread();
+ SET_ALLOC_OWNER(this);
+ DebugAssert(!IsRecording());
+ std::pair<CachedDepthStates::iterator, bool> added = m_CachedDepthStates.insert(std::make_pair(state, ClientDeviceDepthState(state)));
+ ClientDeviceDepthState* result = &added.first->second;
+ if (!added.second)
+ return result;
+
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_CreateDepthState);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ result->internalState = m_DepthStateMapper.CreateID();
+ m_CommandQueue->WriteValueType<ClientDeviceDepthState>(*result);
+#else
+ m_CommandQueue->WriteValueType<ClientDeviceDepthState*>(result);
+#endif
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ result->internalState = m_RealDevice->CreateDepthState(state);
+#endif
+ return result;
+}
+
+DeviceStencilState* GfxDeviceClient::CreateStencilState(const GfxStencilState& state)
+{
+ CheckMainThread();
+ DebugAssert(!IsRecording());
+ SET_ALLOC_OWNER(this);
+ std::pair<CachedStencilStates::iterator, bool> added = m_CachedStencilStates.insert(std::make_pair(state, ClientDeviceStencilState(state)));
+ ClientDeviceStencilState* result = &added.first->second;
+ if (!added.second)
+ return result;
+
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_CreateStencilState);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ result->internalState = m_StencilStateMapper.CreateID();
+ m_CommandQueue->WriteValueType<ClientDeviceStencilState>(*result);
+#else
+ m_CommandQueue->WriteValueType<ClientDeviceStencilState*>(result);
+#endif
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ result->internalState = m_RealDevice->CreateStencilState(state);
+#endif
+ return result;
+}
+
+DeviceRasterState* GfxDeviceClient::CreateRasterState(const GfxRasterState& state)
+{
+ CheckMainThread();
+ DebugAssert(!IsRecording());
+ SET_ALLOC_OWNER(this);
+ std::pair<CachedRasterStates::iterator, bool> added = m_CachedRasterStates.insert(std::make_pair(state, ClientDeviceRasterState(state)));
+ ClientDeviceRasterState* result = &added.first->second;
+ if (!added.second)
+ return result;
+
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_CreateRasterState);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ result->internalState = m_RasterStateMapper.CreateID();
+ m_CommandQueue->WriteValueType<ClientDeviceRasterState>(*result);
+#else
+ m_CommandQueue->WriteValueType<ClientDeviceRasterState*>(result);
+#endif
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ result->internalState = m_RealDevice->CreateRasterState(state);
+#endif
+
+ return result;
+}
+
+void GfxDeviceClient::RecordSetBlendState(const DeviceBlendState* state, const ShaderLab::FloatVal& alphaRef, const ShaderLab::PropertySheet* props)
+{
+ CheckMainThread();
+ DebugAssert(IsRecording());
+ const ClientDeviceBlendState* clientState = static_cast<const ClientDeviceBlendState*>(state);
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetBlendState);
+ m_CommandQueue->WriteValueType<const ClientDeviceBlendState*>(clientState);
+ float* dest = m_CommandQueue->GetWritePointer<float>();
+ // Must call GetBuffer() after GetWritePointer() since it might be reallocated!
+ const void* bufferStart = m_CommandQueue->GetBuffer();
+ m_CurrentContext->patchInfo.AddPatchableFloat(alphaRef, *dest, bufferStart, props);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+}
+
+void GfxDeviceClient::SetBlendState(const DeviceBlendState* state, float alphaRef)
+{
+ CheckMainThread();
+ const ClientDeviceBlendState* clientState = static_cast<const ClientDeviceBlendState*>(state);
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetBlendState);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_CommandQueue->WriteValueType<const ClientDeviceBlendState>(*clientState);
+#else
+ m_CommandQueue->WriteValueType<const ClientDeviceBlendState*>(clientState);
+#endif
+ m_CommandQueue->WriteValueType<float>(alphaRef);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ m_RealDevice->SetBlendState(clientState->internalState, alphaRef);
+#endif
+}
+
+void GfxDeviceClient::SetDepthState(const DeviceDepthState* state)
+{
+ CheckMainThread();
+ const ClientDeviceDepthState* clientState = static_cast<const ClientDeviceDepthState*>(state);
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetDepthState);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_CommandQueue->WriteValueType<const ClientDeviceDepthState>(*clientState);
+#else
+ m_CommandQueue->WriteValueType<const ClientDeviceDepthState*>(clientState);
+#endif
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ m_RealDevice->SetDepthState(clientState->internalState);
+#endif
+}
+
+void GfxDeviceClient::SetStencilState(const DeviceStencilState* state, int stencilRef)
+{
+ CheckMainThread();
+ const ClientDeviceStencilState* clientState = static_cast<const ClientDeviceStencilState*>(state);
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetStencilState);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_CommandQueue->WriteValueType<const ClientDeviceStencilState>(*clientState);
+#else
+ m_CommandQueue->WriteValueType<const ClientDeviceStencilState*>(clientState);
+#endif
+ m_CommandQueue->WriteValueType<int>(stencilRef);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ m_RealDevice->SetStencilState(clientState->internalState, stencilRef);
+#endif
+}
+
+#if UNITY_XENON
+void GfxDeviceClient::SetNullPixelShader()
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetNullPixelShader);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetNullPixelShader();
+}
+
+void GfxDeviceClient::SetHiZEnable( const HiZstate hiz )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_EnableHiZ);
+ m_CommandQueue->WriteValueType<HiZstate>( hiz );
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetHiZEnable( hiz );
+}
+
+void GfxDeviceClient::SetHiStencilState( const bool hiStencilEnable, const bool hiStencilWriteEnable, const int hiStencilRef, const CompareFunction cmpFunc )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetHiStencilState);
+ m_CommandQueue->WriteValueType<bool>(hiStencilEnable);
+ m_CommandQueue->WriteValueType<bool>(hiStencilWriteEnable);
+ m_CommandQueue->WriteValueType<int>(hiStencilRef);
+ m_CommandQueue->WriteValueType<CompareFunction>(cmpFunc);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetHiStencilState( hiStencilEnable, hiStencilWriteEnable, hiStencilRef, cmpFunc );
+}
+
+void GfxDeviceClient::HiStencilFlush( const HiSflush flushtype )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_HiStencilFlush);
+ m_CommandQueue->WriteValueType<HiSflush>(flushtype);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->HiStencilFlush(flushtype);
+}
+#endif
+
+void GfxDeviceClient::SetRasterState(const DeviceRasterState* state)
+{
+ CheckMainThread();
+ const ClientDeviceRasterState* clientState = static_cast<const ClientDeviceRasterState*>(state);
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetRasterState);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_CommandQueue->WriteValueType<const ClientDeviceRasterState>(*clientState);
+#else
+ m_CommandQueue->WriteValueType<const ClientDeviceRasterState*>(clientState);
+#endif
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ m_RealDevice->SetRasterState(clientState->internalState);
+#endif
+}
+
+void GfxDeviceClient::SetSRGBWrite (bool enable)
+{
+ CheckMainThread();
+ DebugAssert(!IsRecording());
+ m_sRGBWrite = enable;
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetSRGBState);
+ m_CommandQueue->WriteValueType<GfxCmdBool>(enable);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetSRGBWrite(enable);
+}
+
+bool GfxDeviceClient::GetSRGBWrite ()
+{
+ CheckMainThread();
+ return m_sRGBWrite;
+}
+
+void GfxDeviceClient::SetWorldMatrix( const float matrix[16] )
+{
+ CheckMainThread();
+ DebugAssert(!IsRecording());
+ m_TransformState.dirtyFlags |= TransformState::kWorldDirty;
+ memcpy(m_TransformState.worldMatrix.GetPtr(), matrix, 16 * sizeof(float));
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetWorldMatrix);
+ m_CommandQueue->WriteValueType<Matrix4x4f>(m_TransformState.worldMatrix);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetWorldMatrix(matrix);
+}
+
+void GfxDeviceClient::SetViewMatrix( const float matrix[16] )
+{
+ CheckMainThread();
+ DebugAssert(!IsRecording());
+ m_TransformState.SetViewMatrix (matrix, m_BuiltinParamValues);
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetViewMatrix);
+ m_CommandQueue->WriteValueType<Matrix4x4f>(m_BuiltinParamValues.GetMatrixParam(kShaderMatView));
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetViewMatrix(matrix);
+}
+
+void GfxDeviceClient::SetProjectionMatrix (const Matrix4x4f& matrix)
+{
+ CheckMainThread();
+ DebugAssert(!IsRecording());
+ m_TransformState.dirtyFlags |= TransformState::kProjDirty;
+ Matrix4x4f& m = m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatProj);
+ CopyMatrix (matrix.GetPtr(), m.GetPtr());
+ CopyMatrix (matrix.GetPtr(), m_TransformState.projectionMatrixOriginal.GetPtr());
+ CalculateDeviceProjectionMatrix (m, m_UsesOpenGLTextureCoords, m_InvertProjectionMatrix);
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetProjectionMatrix);
+ m_CommandQueue->WriteValueType<Matrix4x4f>(matrix);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetProjectionMatrix(matrix);
+}
+
+void GfxDeviceClient::GetMatrix( float outMatrix[16] ) const
+{
+ m_TransformState.UpdateWorldViewMatrix(m_BuiltinParamValues);
+ memcpy(outMatrix, m_TransformState.worldViewMatrix.GetPtr(), 16 * sizeof(float));
+}
+
+const float* GfxDeviceClient::GetWorldMatrix() const
+{
+ return m_TransformState.worldMatrix.GetPtr();
+}
+
+const float* GfxDeviceClient::GetViewMatrix() const
+{
+ return m_BuiltinParamValues.GetMatrixParam(kShaderMatView).GetPtr();
+}
+
+const float* GfxDeviceClient::GetProjectionMatrix() const
+{
+ return m_TransformState.projectionMatrixOriginal.GetPtr();
+}
+
+const float* GfxDeviceClient::GetDeviceProjectionMatrix() const
+{
+ return m_BuiltinParamValues.GetMatrixParam(kShaderMatProj).GetPtr();
+}
+
+void GfxDeviceClient::SetInverseScale( float invScale )
+{
+ CheckMainThread();
+ DebugAssert(!IsRecording());
+ GfxDevice::SetInverseScale(invScale);
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetInverseScale);
+ m_CommandQueue->WriteValueType<float>(invScale);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetInverseScale(invScale);
+}
+
+void GfxDeviceClient::SetNormalizationBackface( NormalizationMode mode, bool backface )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetNormalizationBackface);
+ GfxCmdSetNormalizationBackface data = { mode, backface};
+ m_CommandQueue->WriteValueType<GfxCmdSetNormalizationBackface>(data);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetNormalizationBackface(mode, backface);
+}
+
+void GfxDeviceClient::SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetFFLighting);
+ GfxCmdSetFFLighting data = { on, separateSpecular, colorMaterial };
+ m_CommandQueue->WriteValueType<GfxCmdSetFFLighting>(data);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetFFLighting(on, separateSpecular, colorMaterial);
+}
+
+void GfxDeviceClient::RecordSetMaterial( const ShaderLab::VectorVal& ambient, const ShaderLab::VectorVal& diffuse, const ShaderLab::VectorVal& specular, const ShaderLab::VectorVal& emissive, const ShaderLab::FloatVal& shininess, const ShaderLab::PropertySheet* props )
+{
+ CheckMainThread();
+ DebugAssert(IsRecording());
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetMaterial);
+ GfxMaterialParams* dest = m_CommandQueue->GetWritePointer<GfxMaterialParams>();
+ // Must call GetBuffer() after GetWritePointer() since it might be reallocated!
+ const void* bufferStart = m_CommandQueue->GetBuffer();
+ GfxPatchInfo& patchInfo = m_CurrentContext->patchInfo;
+ patchInfo.AddPatchableVector(ambient, dest->ambient, bufferStart, props);
+ patchInfo.AddPatchableVector(diffuse, dest->diffuse, bufferStart, props);
+ patchInfo.AddPatchableVector(specular, dest->specular, bufferStart, props);
+ patchInfo.AddPatchableVector(emissive, dest->emissive, bufferStart, props);
+ patchInfo.AddPatchableFloat(shininess, dest->shininess, bufferStart, props);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+}
+
+void GfxDeviceClient::SetMaterial( const float ambient[4], const float diffuse[4], const float specular[4], const float emissive[4], const float shininess )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetMaterial);
+ GfxMaterialParams mat = { Vector4f(ambient), Vector4f(diffuse), Vector4f(specular), Vector4f(emissive), shininess };
+ m_CommandQueue->WriteValueType<GfxMaterialParams>(mat);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetMaterial(ambient, diffuse, specular, emissive, shininess);
+}
+
+void GfxDeviceClient::RecordSetColor( const ShaderLab::VectorVal& color, const ShaderLab::PropertySheet* props )
+{
+ CheckMainThread();
+ DebugAssert(IsRecording());
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetColor);
+ Vector4f* dest = m_CommandQueue->GetWritePointer<Vector4f>();
+ // Must call GetBuffer() after GetWritePointer() since it might be reallocated!
+ const void* bufferStart = m_CommandQueue->GetBuffer();
+ m_CurrentContext->patchInfo.AddPatchableVector(color, *dest, bufferStart, props);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+}
+
+void GfxDeviceClient::SetColor( const float color[4] )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetColor);
+ m_CommandQueue->WriteValueType<Vector4f>(Vector4f(color));
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetColor(color);
+}
+
+void GfxDeviceClient::SetViewport( int x, int y, int width, int height )
+{
+ CheckMainThread();
+ DebugAssert(!IsRecording());
+ m_Viewport.x = x;
+ m_Viewport.y = y;
+ m_Viewport.width = width;
+ m_Viewport.height = height;
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetViewport);
+ m_CommandQueue->WriteValueType<ClientDeviceRect>(m_Viewport);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetViewport(x, y, width, height);
+}
+
+void GfxDeviceClient::GetViewport( int* values ) const
+{
+ values[0] = m_Viewport.x;
+ values[1] = m_Viewport.y;
+ values[2] = m_Viewport.width;
+ values[3] = m_Viewport.height;
+}
+
+void GfxDeviceClient::SetScissorRect( int x, int y, int width, int height )
+{
+ CheckMainThread();
+ DebugAssert(!IsRecording());
+ m_ScissorRect.x = x;
+ m_ScissorRect.y = y;
+ m_ScissorRect.width = width;
+ m_ScissorRect.height = height;
+ m_ScissorEnabled = 1;
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetScissorRect);
+ m_CommandQueue->WriteValueType<ClientDeviceRect>(m_ScissorRect);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetScissorRect(x, y, width, height);
+}
+
+void GfxDeviceClient::DisableScissor()
+{
+ CheckMainThread();
+ DebugAssert(!IsRecording());
+ m_ScissorEnabled = 0;
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DisableScissor);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->DisableScissor();
+}
+
+bool GfxDeviceClient::IsScissorEnabled() const
+{
+ return m_ScissorEnabled == 1;
+}
+
+void GfxDeviceClient::GetScissorRect( int values[4] ) const
+{
+ values[0] = m_ScissorRect.x;
+ values[1] = m_ScissorRect.y;
+ values[2] = m_ScissorRect.width;
+ values[3] = m_ScissorRect.height;
+}
+
+TextureCombinersHandle GfxDeviceClient::CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular )
+{
+ SET_ALLOC_OWNER(NULL); // Not set to this since these are leaked. Some shaders are created staticly and are not cleaned up.
+ CheckMainThread();
+
+ if (count > gGraphicsCaps.maxTexUnits)
+ return TextureCombinersHandle( NULL );
+
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ for (int i = 0; i < count; i++)
+ if (!m_RealDevice->IsCombineModeSupported( texEnvs[i].m_CombColor ))
+ return TextureCombinersHandle( NULL );
+
+ // check texgen modes & texture dimension are supported
+ for (int i = 0; i < count; i++)
+ {
+ TextureDimension texDim;
+ TexGenMode texGen;
+ ShaderLab::shaderprops::GetTexEnvInfo( props, texEnvs[i].m_TextureName, texDim, texGen );
+ if (!ShaderLab::IsTexEnvSupported( texEnvs[i].m_TextureName, texDim, texGen ))
+ return TextureCombinersHandle( NULL );
+ }
+#endif
+
+ ClientDeviceTextureCombiners* combiners = UNITY_NEW(ClientDeviceTextureCombiners, kMemGfxThread);
+ combiners->bindings = (ShaderLab::TextureBinding*)UNITY_MALLOC(kMemGfxThread,sizeof(ShaderLab::TextureBinding)*count);
+ for(int i = 0; i < count; i++) new ((void*)(&combiners->bindings[i])) ShaderLab::TextureBinding;
+ combiners->count = count;
+ for (int i = 0; i < count; i++)
+ combiners->bindings[i] = texEnvs[i];
+
+ if (m_Serialize)
+ {
+ // Don't want to create texture combiners from a display list
+ m_CurrentContext->recordFailed = true;
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_CreateTextureCombiners);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ combiners->internalHandle = m_TextureCombinerMapper.CreateID();
+ m_CommandQueue->WriteValueType<ClientIDMapper::ClientID>(combiners->internalHandle);
+ m_CommandQueue->WriteValueType<int>(combiners->count);
+ for (int i = 0; i < count; i++)
+ m_CommandQueue->WriteValueType<ShaderLab::TextureBinding>(combiners->bindings[i]);
+#else
+ m_CommandQueue->WriteValueType<ClientDeviceTextureCombiners*>(combiners);
+#endif
+ GfxCmdCreateTextureCombiners texcomb = { count, hasVertexColorOrLighting, usesAddSpecular };
+ m_CommandQueue->WriteValueType<GfxCmdCreateTextureCombiners>(texcomb);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ combiners->internalHandle = m_RealDevice->CreateTextureCombiners(count, texEnvs, props, hasVertexColorOrLighting, usesAddSpecular);
+#endif
+
+ return TextureCombinersHandle( combiners );
+}
+void GfxDeviceClient::DeleteTextureCombiners( TextureCombinersHandle& textureCombiners )
+{
+ CheckMainThread();
+ if (!textureCombiners.IsValid())
+ return;
+
+ ClientDeviceTextureCombiners* combiners = static_cast<ClientDeviceTextureCombiners*>(textureCombiners.object);
+
+ if (m_Serialize)
+ {
+ // Must delete when message received
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DeleteTextureCombiners);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_CommandQueue->WriteValueType<ClientIDMapper::ClientID>(combiners->internalHandle);
+ m_CommandQueue->WriteValueType<int>(combiners->count);
+ delete[] combiners->bindings;
+ delete combiners;
+#else
+ m_CommandQueue->WriteValueType<ClientDeviceTextureCombiners*>(combiners);
+ textureCombiners.Reset();
+#endif
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+
+ for(int i = 0; i < combiners->count; i++) (&combiners->bindings[i])->~TextureBinding();
+ UNITY_FREE(kMemGfxThread, combiners->bindings);
+ UNITY_DELETE(combiners,kMemGfxThread);
+ }
+}
+
+void GfxDeviceClient::SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props )
+{
+ CheckMainThread();
+ if (!textureCombiners.IsValid())
+ return;
+
+ ClientDeviceTextureCombiners* combiners = static_cast<ClientDeviceTextureCombiners*>(textureCombiners.object);
+
+#if GFX_DEVICE_CLIENT_TRACK_TEXGEN
+ for(int i = 0, n = combiners->count ; i < n ; ++i)
+ SetTexGen(i, GetTexEnvForBinding(combiners->bindings[i], props)->GetTexGen());
+ for(int i = combiners->count ; i < kMaxSupportedTextureUnitsGLES ; ++i)
+ DropTexGen(i);
+#endif
+
+ if (m_Serialize)
+ {
+ int count = combiners->count;
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetTextureCombiners);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_CommandQueue->WriteValueType<ClientIDMapper::ClientID>(combiners->internalHandle);
+ m_CommandQueue->WriteValueType<int>(combiners->count);
+#else
+ m_CommandQueue->WriteValueType<ClientDeviceTextureCombiners*>(combiners);
+#endif
+ void* data = m_CommandQueue->GetWriteDataPointer(count * sizeof(TexEnvData), ALIGN_OF(TexEnvData));
+ // Must call GetBuffer() after GetWriteDataPointer() since it might be reallocated!
+ const UInt8* bufferStart = IsRecording() ? static_cast<const UInt8*>(m_CommandQueue->GetBuffer()) : NULL;
+ TexEnvData* dest = static_cast<TexEnvData*>(data);
+ for (int i = 0; i < count; i++)
+ {
+ using namespace ShaderLab::shaderprops;
+ ShaderLab::TextureBinding& binding = combiners->bindings[i];
+ if (IsRecording())
+ {
+ if (!m_CurrentContext->patchInfo.AddPatchableTexEnv(binding.m_TextureName, binding.m_MatrixName,
+ kTexDimAny, &dest[i], bufferStart, props))
+ m_CurrentContext->recordFailed = true;
+ }
+ else
+ {
+ ShaderLab::TexEnv* te = GetTexEnvForBinding(binding, props);
+ Assert(te != NULL);
+ te->PrepareData(binding.m_TextureName.index, binding.m_MatrixName, props, &dest[i]);
+ }
+ }
+ data = m_CommandQueue->GetWriteDataPointer(count * sizeof(Vector4f), ALIGN_OF(Vector4f));
+ Vector4f* texColors = static_cast<Vector4f*>(data);
+ for (int i = 0; i < count; i++)
+ {
+ const ShaderLab::TextureBinding& binding = combiners->bindings[i];
+ texColors[i] = binding.GetTexColor().Get (props);
+ }
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ m_RealDevice->SetTextureCombiners(combiners->internalHandle, props);
+#endif
+}
+
+void GfxDeviceClient::SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias)
+{
+ CheckMainThread();
+ DebugAssert( dim >= kTexDim2D && dim <= kTexDimCUBE );
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetTexture);
+ GfxCmdSetTexture tex = {shaderType, unit, samplerUnit, texture, dim, bias};
+ m_CommandQueue->WriteValueType<GfxCmdSetTexture>(tex);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetTexture (shaderType, unit, samplerUnit, texture, dim, bias);
+}
+
+void GfxDeviceClient::SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetTextureParams);
+ GfxCmdSetTextureParams params = { texture, texDim, filter, wrap, anisoLevel, hasMipMap, colorSpace };
+ m_CommandQueue->WriteValueType<GfxCmdSetTextureParams>(params);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetTextureParams(texture, texDim, filter, wrap, anisoLevel, hasMipMap, colorSpace);
+}
+
+void GfxDeviceClient::SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16])
+{
+ CheckMainThread();
+
+#if GFX_DEVICE_CLIENT_TRACK_TEXGEN
+ SetTexGen(unit, texGen);
+#endif
+
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetTextureTransform);
+ GfxCmdSetTextureTransform trans = { unit, dim, texGen, identity, Matrix4x4f(matrix) };
+ m_CommandQueue->WriteValueType<GfxCmdSetTextureTransform>(trans);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetTextureTransform(unit, dim, texGen, identity, matrix);
+}
+
+void GfxDeviceClient::SetTextureName( TextureID texture, char const* name )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetTextureName);
+ GfxCmdSetTextureName texName = { texture, strlen(name)+1 };
+ m_CommandQueue->WriteValueType<GfxCmdSetTextureName>(texName);
+ m_CommandQueue->WriteArrayType<char>(name, strlen(name)+1);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetTextureName( texture, name );
+}
+
+void GfxDeviceClient::SetMaterialProperties( const MaterialPropertyBlock& block )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetMaterialProperties);
+ int propertyCount = block.GetPropertiesEnd() - block.GetPropertiesBegin();
+ int bufferSize = block.GetBufferEnd() - block.GetBufferBegin();
+ typedef MaterialPropertyBlock::Property Property;
+ GfxCmdSetMaterialProperties matprops = { propertyCount, bufferSize };
+ m_CommandQueue->WriteValueType<GfxCmdSetMaterialProperties>(matprops);
+ m_CommandQueue->WriteArrayType<Property>(block.GetPropertiesBegin(), propertyCount);
+ m_CommandQueue->WriteArrayType<float>(block.GetBufferBegin(), bufferSize);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetMaterialProperties(block);
+}
+
+GpuProgram* GfxDeviceClient::CreateGpuProgram( const std::string& source, CreateGpuProgramOutput& output )
+{
+ CheckMainThread();
+ DebugAssert(!IsRecording());
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_CreateGpuProgram);
+ GpuProgram* result = NULL;
+ //GfxCmdCreateGpuProgram gpuprog = { parent, source.c_str(), outErrors, &result };
+ //m_CommandQueue->WriteValueType<GfxCmdCreateGpuProgram>(gpuprog);
+ //SubmitCommands();
+ //WaitForSignal();
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ GfxCmdCreateGpuProgram gpuprog = { source.c_str(), &output, &result };
+ m_CommandQueue->WriteValueType<GfxCmdCreateGpuProgram>(gpuprog);
+ SubmitCommands();
+ m_DeviceWorker->WaitForSignal();
+#else
+ size_t len = source.length();
+ m_CommandQueue->WriteValueType<UInt32>(len);
+ m_CommandQueue->WriteArrayType<char>(source.c_str(), len + 1);
+ SubmitCommands();
+ ReadbackData(m_ReadbackData);
+ const GfxRet_CreateGpuProgram* returnData = reinterpret_cast<GfxRet_CreateGpuProgram*>(m_ReadbackData.data());
+ returnData->GetOutput(output);
+ result = (GpuProgram*)returnData->gpuProgram;
+#endif
+ UnityMemoryBarrier();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ return result;
+ }
+ else
+ return m_RealDevice->CreateGpuProgram(source, output);
+}
+
+void GfxDeviceClient::SetShadersMainThread( ShaderLab::SubProgram* programs[kShaderTypeCount], const ShaderLab::PropertySheet* props )
+{
+ CheckMainThread();
+
+ FogMode fogMode = m_FogParams.mode;
+ DisplayListContext& context = *m_CurrentContext;
+ for (int pt = 0; pt < kShaderTypeCount; ++pt)
+ context.shadersActive[pt] = false;
+
+ // Fill in arrays of GPU programs, parameters and buffer sizes
+ GfxCmdSetShaders shadersCmd;
+ for (int pt = 0; pt < kShaderTypeCount; ++pt)
+ {
+ if (!programs[pt])
+ {
+ shadersCmd.programs[pt] = NULL;
+ shadersCmd.params[pt] = NULL;
+ shadersCmd.paramsBufferSize[pt] = 0;
+ continue;
+ }
+ GpuProgram& gpuProg = programs[pt]->GetGpuProgram();
+ GpuProgramParameters& params = programs[pt]->GetParams(fogMode);
+ shadersCmd.programs[pt] = (ClientIDWrapper(GpuProgram))&gpuProg;
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+ shadersCmd.params[pt] = &params;
+#else
+ if (!params.m_InternalHandle)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetGpuProgramParameters);
+ params.m_InternalHandle = m_GpuProgramParametersMapper.CreateID();
+ m_CommandQueue->WriteValueType<ClientIDMapper::ClientID>(params.m_InternalHandle);
+ dynamic_array<UInt8> outBuf;
+ dynamic_array<char> strBuf;
+ Gfx_GpuProgramParameters gfxParams (params, outBuf, strBuf);
+ m_CommandQueue->WriteValueType<Gfx_GpuProgramParameters>(gfxParams);
+
+ int outSize = outBuf.size();
+ m_CommandQueue->WriteValueType<UInt32>(outSize);
+ m_CommandQueue->WriteValueType<UInt32>(strBuf.size());
+ outBuf.resize_uninitialized(outSize + strBuf.size());
+ std::copy(strBuf.begin(), strBuf.end(), outBuf.begin()+outSize);
+ void* buffer = m_CommandQueue->GetWriteDataPointer(outBuf.size(), 1);
+ memcpy (buffer, outBuf.begin(), outBuf.size());
+ }
+ shadersCmd.params[pt] = params.m_InternalHandle;
+#endif
+ if (!params.IsReady())
+ {
+ CreateShaderParameters (programs[pt], fogMode);
+ params.MakeReady();
+ }
+ shadersCmd.paramsBufferSize[pt] = params.GetValuesSize();
+ ShaderImplType implType = programs[pt]->GetGpuProgram().GetImplType();
+ context.shadersActive[pt] = implType == pt;
+
+ // GLSL case, where a single vertex shader SubProgram can encompass multiple stages
+ if (implType == kShaderImplBoth)
+ {
+ context.shadersActive[kShaderVertex] |= true;
+ context.shadersActive[kShaderFragment] |= true;
+ }
+ }
+
+ context.hasSetShaders = true;
+
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetShaders);
+ m_CommandQueue->WriteValueType<GfxCmdSetShaders>(shadersCmd);
+ for (int pt = 0; pt < kShaderTypeCount; ++pt)
+ {
+ int bufferSize = shadersCmd.paramsBufferSize[pt];
+ if (bufferSize <= 0)
+ continue;
+
+ void* buffer = m_CommandQueue->GetWriteDataPointer(bufferSize, 1);
+ UInt8* dest = static_cast<UInt8*>(buffer);
+
+ // Must call GetBuffer() after GetWriteDataPointer() since it might be reallocated!
+ const UInt8* bufferStart = IsRecording() ? static_cast<const UInt8*>(m_CommandQueue->GetBuffer()) : NULL;
+ GfxPatchInfo* patchInfo = IsRecording() ? &context.patchInfo : NULL;
+ programs[pt]->GetParams(fogMode).PrepareValues(props, dest, bufferStart, patchInfo, &context.recordFailed);
+ }
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ GraphicsHelper::SetShaders(*m_RealDevice, programs, props);
+}
+
+void GfxDeviceClient::CreateShaderParameters( ShaderLab::SubProgram* program, FogMode fogMode )
+{
+ CheckMainThread();
+ if (m_Threaded)
+ {
+ // Get main command queue even when recording
+ ThreadedStreamBuffer& stream = m_DisplayListStack[0].commandQueue;
+ stream.WriteValueType<GfxCommand>(kGfxCmd_CreateShaderParameters);
+ GfxCmdCreateShaderParameters params = { program, fogMode };
+ stream.WriteValueType<GfxCmdCreateShaderParameters>(params);
+ stream.WriteSubmitData();
+ WaitForSignal();
+#if DEBUG_GFXDEVICE_LOCKSTEP
+ // Lockstep even if recording
+ GetGfxDeviceWorker()->LockstepWait();
+#endif
+ }
+ else
+ {
+ m_RealDevice->CreateShaderParameters(program, fogMode);
+ }
+}
+
+bool GfxDeviceClient::IsShaderActive( ShaderType type ) const
+{
+ return m_CurrentContext->shadersActive[type];
+}
+
+void GfxDeviceClient::DestroySubProgram( ShaderLab::SubProgram* subprogram )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DestroySubProgram);
+ m_CommandQueue->WriteValueType<ShaderLab::SubProgram*>(subprogram);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->DestroySubProgram(subprogram);
+}
+
+void GfxDeviceClient::SetConstantBufferInfo (int id, int size)
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetConstantBufferInfo);
+ m_CommandQueue->WriteValueType<int>(id);
+ m_CommandQueue->WriteValueType<int>(size);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetConstantBufferInfo (id, size);
+}
+
+void GfxDeviceClient::DisableLights( int startLight )
+{
+ CheckMainThread();
+ const Vector4f black(0.0F, 0.0F, 0.0F, 0.0F);
+ for (int i = startLight; i < kMaxSupportedVertexLights; ++i)
+ {
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Diffuse + i), black);
+ }
+
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DisableLights);
+ m_CommandQueue->WriteValueType<int>(startLight);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->DisableLights(startLight);
+}
+
+void GfxDeviceClient::SetLight( int light, const GfxVertexLight& data)
+{
+ CheckMainThread();
+ DebugAssert(!IsRecording());
+
+ if (light >= kMaxSupportedVertexLights)
+ return;
+
+ SetupVertexLightParams (light, data);
+
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetLight);
+ m_CommandQueue->WriteValueType<int>(light);
+ m_CommandQueue->WriteValueType<GfxVertexLight>(data);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetLight(light, data);
+}
+
+void GfxDeviceClient::SetAmbient( const float ambient[4] )
+{
+ CheckMainThread();
+ m_BuiltinParamValues.SetVectorParam(kShaderVecLightModelAmbient, Vector4f(ambient));
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetAmbient);
+ m_CommandQueue->WriteValueType<Vector4f>(Vector4f(ambient));
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetAmbient(ambient);
+}
+
+void GfxDeviceClient::RecordEnableFog( FogMode fogMode, const ShaderLab::FloatVal& fogStart, const ShaderLab::FloatVal& fogEnd, const ShaderLab::FloatVal& fogDensity, const ShaderLab::VectorVal& fogColor, const ShaderLab::PropertySheet* props )
+{
+ CheckMainThread();
+ DebugAssert(IsRecording());
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_EnableFog);
+ m_CurrentContext->fogParamsOffset = m_CommandQueue->GetCurrentSize();
+ GfxFogParams* fog = m_CommandQueue->GetWritePointer<GfxFogParams>();
+ fog->mode = fogMode;
+
+ // Must call GetBuffer() after GetWritePointer() since it might be reallocated!
+ const void* bufferStart = m_CommandQueue->GetBuffer();
+ GfxPatchInfo& patchInfo = m_CurrentContext->patchInfo;
+ patchInfo.AddPatchableVector(fogColor, fog->color, bufferStart, props);
+ patchInfo.AddPatchableFloat(fogStart, fog->start, bufferStart, props);
+ patchInfo.AddPatchableFloat(fogEnd, fog->end, bufferStart, props);
+ patchInfo.AddPatchableFloat(fogDensity, fog->density, bufferStart, props);
+ m_FogParams = *fog;
+ GFXDEVICE_LOCKSTEP_CLIENT();
+}
+
+void GfxDeviceClient::EnableFog (const GfxFogParams& fog)
+{
+ CheckMainThread();
+ m_FogParams = fog;
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_EnableFog);
+ if (IsRecording())
+ m_CurrentContext->fogParamsOffset = m_CommandQueue->GetCurrentSize();
+ m_CommandQueue->WriteValueType<GfxFogParams>(fog);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->EnableFog(fog);
+}
+
+void GfxDeviceClient::DisableFog()
+{
+ CheckMainThread();
+ m_FogParams.mode = kFogDisabled;
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DisableFog);
+ if (IsRecording())
+ m_CurrentContext->fogParamsOffset = DisplayListContext::kFogParamsDisable;
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->DisableFog();
+}
+
+VBO* GfxDeviceClient::CreateVBO()
+{
+ CheckMainThread();
+ Assert(!IsRecording());
+ VBO* vbo = new ThreadedVBO(*this);
+ OnCreateVBO(vbo);
+ return vbo;
+}
+
+void GfxDeviceClient::DeleteVBO( VBO* vbo )
+{
+ CheckMainThread();
+ Assert(!IsRecording());
+ OnDeleteVBO(vbo);
+ delete vbo;
+}
+
+DynamicVBO& GfxDeviceClient::GetDynamicVBO()
+{
+ CheckMainThread();
+ if (!m_DynamicVBO)
+ {
+ Assert(!m_Threaded);
+ return m_RealDevice->GetDynamicVBO();
+ }
+ return *m_DynamicVBO;
+}
+
+void GfxDeviceClient::BeginSkinning( int maxSkinCount )
+{
+ if (m_Threaded)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_BeginSkinning);
+ m_CommandQueue->WriteValueType<int>(maxSkinCount);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ // Call this even when threaded
+ GfxDevice::BeginSkinning(maxSkinCount);
+}
+
+bool GfxDeviceClient::SkinMesh( const SkinMeshInfo& skin, VBO* vbo )
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ // Only skin on render thread if buffer is not read back
+ if (m_Threaded && skin.outVertices == NULL)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SkinMesh);
+ m_CommandQueue->WriteValueType<SkinMeshInfo>(skin);
+ ThreadedVBO* threadedVBO = static_cast<ThreadedVBO*>(vbo);
+ m_CommandQueue->WriteValueType<ClientDeviceVBO*>(threadedVBO->GetClientDeviceVBO());
+ m_CommandQueue->WriteSubmitData();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ return true;
+ }
+ else
+#endif
+ return GfxDevice::SkinMesh(skin, vbo);
+}
+
+void GfxDeviceClient::EndSkinning()
+{
+ if (m_Threaded)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_EndSkinning);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ // Call this even when threaded
+ GfxDevice::EndSkinning();
+}
+
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+void GfxDeviceClient::BeginStaticBatching(const ChannelAssigns& channels, GfxPrimitiveType topology)
+{
+ if (m_Threaded)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_BeginStaticBatching);
+ GfxCmdBeginStaticBatching statbat = { channels, topology };
+ m_CommandQueue->WriteValueType<GfxCmdBeginStaticBatching>(statbat);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->BeginStaticBatching(channels, topology);
+}
+
+void GfxDeviceClient::StaticBatchMesh( UInt32 firstVertex, UInt32 vertexCount, const IndexBufferData& indices, UInt32 firstIndexByte, UInt32 indexCount )
+{
+ if (m_Threaded)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_StaticBatchMesh);
+ GfxCmdStaticBatchMesh batch = { firstVertex, vertexCount, indices, firstIndexByte, indexCount };
+ m_CommandQueue->WriteValueType<GfxCmdStaticBatchMesh>(batch);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->StaticBatchMesh(firstVertex, vertexCount, indices, firstIndexByte, indexCount);
+}
+
+void GfxDeviceClient::EndStaticBatching( VBO& vbo, const Matrix4x4f& matrix, TransformType transformType, int sourceChannels )
+{
+ if (m_Threaded)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_EndStaticBatching);
+ ThreadedVBO& threadedVBO = static_cast<ThreadedVBO&>(vbo);
+ ClientDeviceVBO* clientVBO = threadedVBO.GetClientDeviceVBO();
+ GfxCmdEndStaticBatching endbat = { clientVBO, matrix, transformType, sourceChannels };
+ m_CommandQueue->WriteValueType<GfxCmdEndStaticBatching>(endbat);
+ m_CommandQueue->WriteSubmitData();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->EndStaticBatching(vbo, matrix, transformType, sourceChannels);
+}
+
+void GfxDeviceClient::BeginDynamicBatching( const ChannelAssigns& shaderChannels, UInt32 channelsInVBO, size_t maxVertices, size_t maxIndices, GfxPrimitiveType topology)
+{
+ if (m_Threaded)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_BeginDynamicBatching);
+ GfxCmdBeginDynamicBatching dynbat = { shaderChannels, channelsInVBO, maxVertices, maxIndices, topology };
+ m_CommandQueue->WriteValueType<GfxCmdBeginDynamicBatching>(dynbat);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->BeginDynamicBatching(shaderChannels, channelsInVBO, maxVertices, maxIndices, topology);
+}
+
+void GfxDeviceClient::DynamicBatchMesh( const Matrix4x4f& matrix, const VertexBufferData& vertices, UInt32 firstVertex, UInt32 vertexCount, const IndexBufferData& indices, UInt32 firstIndexByte, UInt32 indexCount )
+{
+ if (m_Threaded)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DynamicBatchMesh);
+ GfxCmdDynamicBatchMesh batch = { matrix, vertices, firstVertex, vertexCount, indices, firstIndexByte, indexCount };
+ m_CommandQueue->WriteValueType<GfxCmdDynamicBatchMesh>(batch);
+ m_CommandQueue->WriteSubmitData();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->DynamicBatchMesh(matrix, vertices, firstVertex, vertexCount, indices, firstIndexByte, indexCount);
+
+}
+#if ENABLE_SPRITES
+void GfxDeviceClient::DynamicBatchSprite(const Matrix4x4f* matrix, const SpriteRenderData* rd, ColorRGBA32 color)
+{
+ if (m_Threaded)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DynamicBatchSprite);
+ GfxCmdDynamicBatchSprite batch = { *matrix, rd, color };
+ m_CommandQueue->WriteValueType<GfxCmdDynamicBatchSprite>(batch);
+ m_CommandQueue->WriteSubmitData();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->DynamicBatchSprite(matrix, rd, color);
+}
+#endif
+void GfxDeviceClient::EndDynamicBatching( TransformType transformType )
+{
+ if (m_Threaded)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_EndDynamicBatching);
+ GfxCmdEndDynamicBatching endbat = { transformType };
+ m_CommandQueue->WriteValueType<GfxCmdEndDynamicBatching>(endbat);
+ m_CommandQueue->WriteSubmitData();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->EndDynamicBatching(transformType);
+}
+#endif
+
+void GfxDeviceClient::AddBatchingStats( int batchedTris, int batchedVerts, int batchedCalls )
+{
+ if (m_Threaded)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_AddBatchingStats);
+ GfxCmdAddBatchingStats stats = { batchedTris, batchedVerts, batchedCalls };
+ m_CommandQueue->WriteValueType<GfxCmdAddBatchingStats>(stats);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->AddBatchingStats(batchedTris, batchedVerts, batchedCalls);
+}
+
+
+GPUSkinningInfo* GfxDeviceClient::CreateGPUSkinningInfo()
+{
+ CheckMainThread();
+ DebugAssert(!IsRecording());
+ SET_ALLOC_OWNER(this);
+
+ // Call create directly. Interface spec says this is safe.
+ GPUSkinningInfo *realInfo = m_RealDevice->CreateGPUSkinningInfo();
+ if(!realInfo)
+ return NULL;
+ return new ClientGPUSkinningInfo(realInfo);
+}
+
+void GfxDeviceClient::DeleteGPUSkinningInfo(GPUSkinningInfo *info)
+{
+ CheckMainThread();
+ if (m_Threaded)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DeleteGPUSkinningInfo);
+ m_CommandQueue->WriteValueType<GPUSkinningInfo *>(((ClientGPUSkinningInfo *)info)->m_realSkinInfo);
+ SubmitCommands();
+ delete (ClientGPUSkinningInfo *)info;
+ GFXDEVICE_LOCKSTEP_CLIENT();
+}
+ else
+ {
+ m_RealDevice->DeleteGPUSkinningInfo(((ClientGPUSkinningInfo *)info)->m_realSkinInfo);
+ delete (ClientGPUSkinningInfo *)info;
+ }
+
+}
+
+void GfxDeviceClient::SkinOnGPU(GPUSkinningInfo * info, bool lastThisFrame)
+{
+ CheckMainThread();
+ if (m_Threaded)
+ {
+ ClientGPUSkinningInfo *ci = (ClientGPUSkinningInfo *)info;
+
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SkinOnGPU);
+ m_CommandQueue->WriteValueType<GPUSkinningInfo *>(ci->m_realSkinInfo);
+ // Add the vbo object pointer to the stream, ci->m_realSkinInfo->m_destVBO will get updated in the worker.
+ ThreadedVBO* vbo = (ThreadedVBO*)ci->m_DestVBO;
+ m_CommandQueue->WriteValueType<ClientDeviceVBO *>(vbo->GetClientDeviceVBO());
+ m_CommandQueue->WriteValueType<bool>(lastThisFrame);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ ClientGPUSkinningInfo *ci = (ClientGPUSkinningInfo *)info;
+ ThreadedVBO* vbo = (ThreadedVBO*)ci->m_DestVBO;
+ ci->m_realSkinInfo->SetDestVBO(vbo->GetNonThreadedVBO());
+ m_RealDevice->SkinOnGPU(ci->m_realSkinInfo, lastThisFrame);
+ }
+}
+
+void GfxDeviceClient::UpdateSkinSourceData(GPUSkinningInfo *info, const void *vertData, const BoneInfluence *skinData, bool dirty)
+{
+ CheckMainThread();
+ if (m_Threaded)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_UpdateSkinSourceData);
+ m_CommandQueue->WriteValueType<GPUSkinningInfo *>(((ClientGPUSkinningInfo *)info)->m_realSkinInfo);
+ m_CommandQueue->WriteValueType<const void *>(vertData);
+ m_CommandQueue->WriteValueType<const BoneInfluence *>(skinData);
+ m_CommandQueue->WriteValueType<bool>(dirty);
+ SubmitCommands();
+ UInt32 fence = InsertCPUFence();
+ WaitOnCPUFence(fence);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ m_RealDevice->UpdateSkinSourceData(((ClientGPUSkinningInfo *)info)->m_realSkinInfo, vertData, skinData, dirty);
+ }
+
+}
+
+void GfxDeviceClient::UpdateSkinBonePoses(GPUSkinningInfo *info, const int boneCount, const Matrix4x4f* poses)
+{
+ CheckMainThread();
+ if (m_Threaded)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_UpdateSkinBonePoses);
+ m_CommandQueue->WriteValueType<GPUSkinningInfo *>(((ClientGPUSkinningInfo *)info)->m_realSkinInfo);
+ m_CommandQueue->WriteValueType<int>(boneCount);
+ m_CommandQueue->WriteArrayType<Matrix4x4f>(poses, boneCount);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ m_RealDevice->UpdateSkinBonePoses(((ClientGPUSkinningInfo *)info)->m_realSkinInfo, boneCount, poses);
+ }
+
+}
+
+
+
+#if UNITY_XENON
+RawVBO* GfxDeviceClient::CreateRawVBO(UInt32 size, UInt32 flags)
+{
+ CheckMainThread();
+ Assert(!IsRecording());
+ RawVBO* vbo = new RawThreadedVBO(*this, size, flags);
+ return vbo;
+}
+
+void GfxDeviceClient::DeleteRawVBO(RawVBO* vbo)
+{
+ CheckMainThread();
+ Assert(!IsRecording());
+ delete vbo;
+}
+
+
+void GfxDeviceClient::EnablePersistDisplayOnQuit(bool enabled)
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_EnablePersistDisplayOnQuit);
+ m_CommandQueue->WriteValueType<bool>(enabled);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->EnablePersistDisplayOnQuit(enabled);
+}
+
+void GfxDeviceClient::RegisterTexture2D( TextureID tid, IDirect3DBaseTexture9* texture )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_RegisterTexture2D);
+ m_CommandQueue->WriteValueType<TextureID>(tid);
+ m_CommandQueue->WriteValueType<IDirect3DBaseTexture9*>(texture);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->RegisterTexture2D(tid, texture);
+}
+
+void GfxDeviceClient::PatchTexture2D( TextureID tid, IDirect3DBaseTexture9* texture )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_PatchTexture2D);
+ m_CommandQueue->WriteValueType<TextureID>(tid);
+ m_CommandQueue->WriteValueType<IDirect3DBaseTexture9*>(texture);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->PatchTexture2D(tid, texture);
+}
+
+void GfxDeviceClient::DeleteTextureEntryOnly( TextureID textureID )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DeleteTextureEntryOnly);
+ m_CommandQueue->WriteValueType<TextureID>(textureID);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->DeleteTextureEntryOnly(textureID);
+}
+
+void GfxDeviceClient::UnbindAndDelayReleaseTexture( IDirect3DBaseTexture9* texture )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_UnbindAndDelayReleaseTexture);
+ m_CommandQueue->WriteValueType<IDirect3DBaseTexture9*>(texture);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->UnbindAndDelayReleaseTexture(texture);
+}
+
+void GfxDeviceClient::SetTextureWrapModes( TextureID textureID, TextureWrapMode wrapU, TextureWrapMode wrapV, TextureWrapMode wrapW )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetTextureWrapModes);
+ m_CommandQueue->WriteValueType<TextureID>(textureID);
+ m_CommandQueue->WriteValueType<TextureWrapMode>(wrapU);
+ m_CommandQueue->WriteValueType<TextureWrapMode>(wrapV);
+ m_CommandQueue->WriteValueType<TextureWrapMode>(wrapW);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetTextureWrapModes(textureID, wrapU, wrapV, wrapW);
+}
+
+void GfxDeviceClient::OnLastFrameCallback()
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_OnLastFrameCallback);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->OnLastFrameCallback();
+}
+
+xenon::IVideoPlayer* GfxDeviceClient::CreateVideoPlayer(bool fullscreen)
+{
+ CheckMainThread();
+ Assert(!IsRecording());
+ xenon::VideoPlayerThreaded* vp = new xenon::VideoPlayerThreaded(*this, fullscreen);
+ return vp;
+}
+
+void GfxDeviceClient::DeleteVideoPlayer(xenon::IVideoPlayer* player)
+{
+ CheckMainThread();
+ Assert(!IsRecording());
+ delete player;
+}
+#endif
+
+RenderSurfaceHandle GfxDeviceClient::CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags)
+{
+ CheckMainThread();
+ DebugAssert(!IsRecording());
+ ClientDeviceRenderSurface* handle = new ClientDeviceRenderSurface(width, height);
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_CreateRenderColorSurface);
+ GfxCmdCreateRenderColorSurface create = { textureID, width, height, samples, depth, dim, format, createFlags };
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_CommandQueue->WriteValueType<ClientDeviceRenderSurface*>(handle);
+#else
+ handle->internalHandle = m_RenderSurfaceMapper.CreateID();
+ m_CommandQueue->WriteValueType<ClientDeviceRenderSurface>(*handle);
+#endif
+ m_CommandQueue->WriteValueType<GfxCmdCreateRenderColorSurface>(create);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ {
+ handle->internalHandle = m_RealDevice->CreateRenderColorSurface(textureID, width, height, samples, depth, dim, format, createFlags);
+ }
+#endif
+ return RenderSurfaceHandle(handle);
+}
+
+RenderSurfaceHandle GfxDeviceClient::CreateRenderDepthSurface (TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags)
+{
+ CheckMainThread();
+ DebugAssert(!IsRecording());
+ ClientDeviceRenderSurface* handle = new ClientDeviceRenderSurface(width, height);
+ handle->zformat = depthFormat;
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_CreateRenderDepthSurface);
+ GfxCmdCreateRenderDepthSurface create = { textureID, width, height, samples, dim, depthFormat, createFlags };
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_CommandQueue->WriteValueType<ClientDeviceRenderSurface*>(handle);
+#else
+ handle->internalHandle = m_RenderSurfaceMapper.CreateID();
+ m_CommandQueue->WriteValueType<ClientDeviceRenderSurface>(*handle);
+#endif
+ m_CommandQueue->WriteValueType<GfxCmdCreateRenderDepthSurface>(create);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ {
+ handle->internalHandle = m_RealDevice->CreateRenderDepthSurface(textureID, width, height, samples, dim, depthFormat, createFlags);
+ }
+#endif
+ return RenderSurfaceHandle(handle);
+}
+
+void GfxDeviceClient::DestroyRenderSurface (RenderSurfaceHandle& rs)
+{
+ CheckMainThread();
+ if( !rs.IsValid() )
+ return;
+
+ ClientDeviceRenderSurface* handle = static_cast<ClientDeviceRenderSurface*>(rs.object);
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DestroyRenderSurface);
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_CommandQueue->WriteValueType<ClientDeviceRenderSurface*>(handle);
+#else
+ m_CommandQueue->WriteValueType<ClientDeviceRenderSurface>(*handle);
+#endif
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ {
+ m_RealDevice->DestroyRenderSurface(handle->internalHandle);
+ delete handle;
+ }
+#endif
+ rs.Reset();
+}
+
+void GfxDeviceClient::DiscardContents (RenderSurfaceHandle& rs)
+{
+ CheckMainThread();
+ if( !rs.IsValid() )
+ return;
+
+ ClientDeviceRenderSurface* handle = (ClientDeviceRenderSurface*)rs.object;
+ handle->state = ClientDeviceRenderSurface::kInitial; // mark as "no contents"
+
+
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DiscardContents);
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_CommandQueue->WriteValueType<ClientDeviceRenderSurface*>(handle);
+#else
+ m_CommandQueue->WriteValueType<ClientDeviceRenderSurface>(*handle);
+#endif
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ {
+ m_RealDevice->DiscardContents(handle->internalHandle);
+ }
+#endif
+}
+
+void GfxDeviceClient::SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face)
+{
+ CheckMainThread();
+ Assert(!IsRecording());
+
+ BeforeRenderTargetChange(count, colorHandles, depthHandle);
+ for (int i = 0; i < count; ++i)
+ m_ActiveRenderColorSurfaces[i] = colorHandles[i];
+ for (int i = count; i < kMaxSupportedRenderTargets; ++i)
+ m_ActiveRenderColorSurfaces[i].Reset();
+ m_ActiveRenderDepthSurface = depthHandle;
+ AfterRenderTargetChange();
+
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetRenderTarget);
+ GfxCmdSetRenderTarget srt;
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ for (int i = 0; i < kMaxSupportedRenderTargets; ++i)
+ srt.colorHandles[i] = m_ActiveRenderColorSurfaces[i];
+ srt.depthHandle = depthHandle;
+#else
+ for (int i = 0; i < kMaxSupportedRenderTargets; ++i)
+ srt.colorHandles[i] = ((ClientDeviceRenderSurface*)(m_ActiveRenderColorSurfaces[i].object))->internalHandle;
+ srt.depthHandle = ((ClientDeviceRenderSurface*)depthHandle.object)->internalHandle;
+#endif
+ srt.colorCount = count;
+ srt.face = face;
+ srt.mipLevel = mipLevel;
+ m_CommandQueue->WriteValueType<GfxCmdSetRenderTarget>(srt);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ {
+ RenderSurfaceHandle realColorHandle[kMaxSupportedRenderTargets];
+ for (int i = 0; i < count; ++i)
+ {
+ ClientDeviceRenderSurface* colorSurf = static_cast<ClientDeviceRenderSurface*>(colorHandles[i].object);
+ realColorHandle[i].object = colorSurf ? colorSurf->internalHandle.object : NULL;
+ if(!realColorHandle[i].IsValid())
+ realColorHandle[i] = m_RealDevice->GetBackBufferColorSurface();
+ }
+ ClientDeviceRenderSurface* depthSurf = static_cast<ClientDeviceRenderSurface*>(depthHandle.object);
+ RenderSurfaceHandle realDepthHandle(depthSurf ? depthSurf->internalHandle.object : NULL);
+ if(!realDepthHandle.IsValid())
+ realDepthHandle = m_RealDevice->GetBackBufferDepthSurface();
+
+ m_RealDevice->SetRenderTargets(count, realColorHandle, realDepthHandle, mipLevel, face);
+ }
+#endif
+}
+
+void GfxDeviceClient::SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face, UInt32 flags)
+{
+ CheckMainThread();
+ Assert(!IsRecording());
+
+ BeforeRenderTargetChange(count, colorHandles, depthHandle);
+ for (int i = 0; i < count; ++i)
+ m_ActiveRenderColorSurfaces[i] = colorHandles[i];
+ for (int i = count; i < kMaxSupportedRenderTargets; ++i)
+ m_ActiveRenderColorSurfaces[i].Reset();
+ m_ActiveRenderDepthSurface = depthHandle;
+ AfterRenderTargetChange();
+
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetRenderTargetWithFlags);
+ GfxCmdSetRenderTarget srt;
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ for (int i = 0; i < kMaxSupportedRenderTargets; ++i)
+ srt.colorHandles[i] = m_ActiveRenderColorSurfaces[i];
+ srt.depthHandle = depthHandle;
+#else
+ for (int i = 0; i < kMaxSupportedRenderTargets; ++i)
+ srt.colorHandles[i] = m_ActiveRenderColorSurfaces[i].object ? ((ClientDeviceRenderSurface*)(m_ActiveRenderColorSurfaces[i].object))->internalHandle : NULL;
+ srt.depthHandle = depthHandle.object ? ((ClientDeviceRenderSurface*)depthHandle.object)->internalHandle: NULL;
+#endif
+ srt.colorCount = count;
+ srt.face = face;
+ srt.mipLevel = mipLevel;
+ m_CommandQueue->WriteValueType<GfxCmdSetRenderTarget>(srt);
+ m_CommandQueue->WriteValueType<UInt32>(flags);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ {
+ RenderSurfaceHandle realColorHandle[kMaxSupportedRenderTargets];
+ for (int i = 0; i < count; ++i)
+ {
+ ClientDeviceRenderSurface* colorSurf = static_cast<ClientDeviceRenderSurface*>(colorHandles[i].object);
+ realColorHandle[i].object = colorSurf ? colorSurf->internalHandle.object : NULL;
+ if(!realColorHandle[i].IsValid())
+ realColorHandle[i] = m_RealDevice->GetBackBufferColorSurface();
+ }
+ ClientDeviceRenderSurface* depthSurf = static_cast<ClientDeviceRenderSurface*>(depthHandle.object);
+ RenderSurfaceHandle realDepthHandle(depthSurf ? depthSurf->internalHandle.object : NULL);
+ if(!realDepthHandle.IsValid())
+ realDepthHandle = m_RealDevice->GetBackBufferDepthSurface();
+
+ m_RealDevice->SetRenderTargets (count, realColorHandle, realDepthHandle, mipLevel, face, flags);
+ }
+#endif
+}
+
+void GfxDeviceClient::ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle)
+{
+ CheckMainThread();
+ ClientDeviceRenderSurface* src = static_cast<ClientDeviceRenderSurface*>(srcHandle.object);
+ ClientDeviceRenderSurface* dst = static_cast<ClientDeviceRenderSurface*>(dstHandle.object);
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_ResolveColorSurface);
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_CommandQueue->WriteValueType<ClientDeviceRenderSurface*>(src);
+ m_CommandQueue->WriteValueType<ClientDeviceRenderSurface*>(dst);
+#else
+// m_CommandQueue->WriteValueType<ClientDeviceRenderSurface*>(src.object ? ((ClientDeviceRenderSurface*)(src.object))->internalHandle : NULL);
+// m_CommandQueue->WriteValueType<ClientDeviceRenderSurface*>(dst.object ? ((ClientDeviceRenderSurface*)(dst.object))->internalHandle : NULL);
+ //todo.
+#endif
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ m_RealDevice->ResolveColorSurface (src->internalHandle, dst->internalHandle);
+#endif
+}
+
+void GfxDeviceClient::ResolveDepthIntoTexture (RenderSurfaceHandle colorHandle, RenderSurfaceHandle depthHandle)
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ GfxCmdResolveDepthIntoTexture resolve = { colorHandle, depthHandle };
+#else
+ GfxCmdResolveDepthIntoTexture resolve = { colorHandle.object ? ((ClientDeviceRenderSurface*)(colorHandle.object))->internalHandle : NULL , depthHandle.object ? ((ClientDeviceRenderSurface*)(depthHandle.object))->internalHandle : NULL};
+#endif
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_ResolveDepthIntoTexture);
+ m_CommandQueue->WriteValueType<GfxCmdResolveDepthIntoTexture>(resolve);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ {
+ ClientDeviceRenderSurface* colorSurf = static_cast<ClientDeviceRenderSurface*>(colorHandle.object);
+ ClientDeviceRenderSurface* depthSurf = static_cast<ClientDeviceRenderSurface*>(depthHandle.object);
+ m_RealDevice->ResolveDepthIntoTexture (colorSurf->internalHandle, depthSurf->internalHandle);
+ }
+#endif
+}
+
+
+RenderSurfaceHandle GfxDeviceClient::GetActiveRenderColorSurface (int index)
+{
+ CheckMainThread();
+ return m_ActiveRenderColorSurfaces[index];
+}
+
+RenderSurfaceHandle GfxDeviceClient::GetActiveRenderDepthSurface ()
+{
+ CheckMainThread();
+ return m_ActiveRenderDepthSurface;
+}
+
+void GfxDeviceClient::BeforeRenderTargetChange(int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle)
+{
+ if (!GetFrameStats().m_StatsEnabled)
+ return;
+
+ // mark any rendered-into render target surfaces as "resolved"
+ for (int i = 0; i < kMaxSupportedRenderTargets; ++i)
+ {
+ if (i >= count || colorHandles[i] != m_ActiveRenderColorSurfaces[i])
+ {
+ ClientDeviceRenderSurface* colorSurf = (ClientDeviceRenderSurface*)m_ActiveRenderColorSurfaces[i].object;
+ if (colorSurf && colorSurf->state != ClientDeviceRenderSurface::kInitial)
+ colorSurf->state = ClientDeviceRenderSurface::kResolved;
+ }
+ }
+
+ if (depthHandle != m_ActiveRenderDepthSurface)
+ {
+ ClientDeviceRenderSurface* depthSurf = (ClientDeviceRenderSurface*)m_ActiveRenderDepthSurface.object;
+ if (depthSurf && depthSurf->state != ClientDeviceRenderSurface::kInitial)
+ depthSurf->state = ClientDeviceRenderSurface::kResolved;
+ }
+}
+
+
+void GfxDeviceClient::AfterRenderTargetChange()
+{
+ if (m_ActiveRenderColorSurfaces[0].IsValid() && !m_ActiveRenderColorSurfaces[0].object->backBuffer)
+ {
+ ClientDeviceRenderSurface* colorSurf = (ClientDeviceRenderSurface*)m_ActiveRenderColorSurfaces[0].object;
+ if (m_ActiveRenderDepthSurface.IsValid())
+ {
+ ClientDeviceRenderSurface* depthSurf = (ClientDeviceRenderSurface*)m_ActiveRenderDepthSurface.object;
+ if (colorSurf->width != depthSurf->width || colorSurf->height != depthSurf->height)
+ {
+ ErrorString("Dimensions of color surface does not match dimensions of depth surface");
+ }
+ }
+ m_CurrentTargetWidth = colorSurf->width;
+ m_CurrentTargetHeight = colorSurf->height;
+ }
+ else
+ {
+#if UNITY_WINRT
+ if (0 == m_CurrentWindowWidth && 0 == m_CurrentWindowHeight && NULL != m_RealDevice)
+ {
+ m_CurrentTargetWidth = m_RealDevice->GetCurrentTargetWidth();
+ m_CurrentTargetHeight = m_RealDevice->GetCurrentTargetHeight();
+ m_CurrentWindowWidth = m_CurrentTargetWidth;
+ m_CurrentWindowHeight = m_CurrentTargetHeight;
+ }
+#endif
+ m_CurrentTargetWidth = m_CurrentWindowWidth;
+ m_CurrentTargetHeight = m_CurrentWindowHeight;
+ }
+}
+
+bool GfxDeviceClient::IsRenderTargetConfigValid(UInt32 width, UInt32 height, RenderTextureFormat colorFormat, DepthBufferFormat depthFormat)
+{
+ CheckMainThread();
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+ return GfxDevice::IsRenderTargetConfigValid(width, height, colorFormat, depthFormat);
+#else
+ return m_RealDevice->IsRenderTargetConfigValid(width, height, colorFormat, depthFormat);
+#endif
+}
+
+void GfxDeviceClient::SetSurfaceFlags(RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags)
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetSurfaceFlags);
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ GfxCmdSetSurfaceFlags sf = { surf, flags, keepFlags };
+#else
+ GfxCmdSetSurfaceFlags sf = { surf.object ? ((ClientDeviceRenderSurface*)(surf.object))->internalHandle : NULL , flags, keepFlags };
+#endif
+ m_CommandQueue->WriteValueType<GfxCmdSetSurfaceFlags>(sf);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ {
+ ClientDeviceRenderSurface* surface = (ClientDeviceRenderSurface*)surf.object;
+ m_RealDevice->SetSurfaceFlags(surface->internalHandle, flags, keepFlags);
+ }
+#endif
+}
+
+void GfxDeviceClient::UploadTexture2D( TextureID texture, TextureDimension dimension, UInt8* srcData, int srcSize, int width, int height, TextureFormat format, int mipCount, UInt32 uploadFlags, int skipMipLevels, TextureUsageMode usageMode, TextureColorSpace colorSpace )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ // Don't want to upload textures from a display list
+ m_CurrentContext->recordFailed = true;
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_UploadTexture2D);
+ Assert(width >= 0 && height >= 0);
+ GfxCmdUploadTexture2D upload = { texture, dimension, srcSize, width, height, format, mipCount, uploadFlags, skipMipLevels, usageMode, colorSpace };
+ m_CommandQueue->WriteValueType<GfxCmdUploadTexture2D>(upload);
+ WriteBufferData(srcData, srcSize);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->UploadTexture2D(texture, dimension, srcData, srcSize, width, height, format, mipCount, uploadFlags, skipMipLevels, usageMode, colorSpace);
+}
+
+void GfxDeviceClient::UploadTextureSubData2D( TextureID texture, UInt8* srcData, int srcSize, int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ // Don't want to upload textures from a display list
+ m_CurrentContext->recordFailed = true;
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_UploadTextureSubData2D);
+ GfxCmdUploadTextureSubData2D upload = { texture, srcSize, mipLevel, x, y, width, height, format, colorSpace };
+ m_CommandQueue->WriteValueType<GfxCmdUploadTextureSubData2D>(upload);
+ WriteBufferData(srcData, srcSize);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->UploadTextureSubData2D(texture, srcData, srcSize, mipLevel, x, y, width, height, format, colorSpace);
+}
+
+void GfxDeviceClient::UploadTextureCube( TextureID texture, UInt8* srcData, int srcSize, int faceDataSize, int size, TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ // Don't want to upload textures from a display list
+ m_CurrentContext->recordFailed = true;
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_UploadTextureCube);
+ GfxCmdUploadTextureCube upload = { texture, srcSize, faceDataSize, size, format, mipCount, uploadFlags, colorSpace };
+ m_CommandQueue->WriteValueType<GfxCmdUploadTextureCube>(upload);
+ WriteBufferData(srcData, srcSize);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->UploadTextureCube(texture, srcData, srcSize, faceDataSize, size, format, mipCount, uploadFlags, colorSpace);
+}
+
+void GfxDeviceClient::UploadTexture3D( TextureID texture, UInt8* srcData, int srcSize, int width, int height, int depth, TextureFormat format, int mipCount, UInt32 uploadFlags )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ // Don't want to upload textures from a display list
+ m_CurrentContext->recordFailed = true;
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_UploadTexture3D);
+ GfxCmdUploadTexture3D upload = { texture, srcSize, width, height, depth, format, mipCount, uploadFlags };
+ m_CommandQueue->WriteValueType<GfxCmdUploadTexture3D>(upload);
+ WriteBufferData(srcData, srcSize);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->UploadTexture3D(texture, srcData, srcSize, width, height, depth, format, mipCount, uploadFlags);
+}
+
+void GfxDeviceClient::DeleteTexture( TextureID texture )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DeleteTexture);
+ m_CommandQueue->WriteValueType<TextureID>(texture);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->DeleteTexture(texture);
+}
+
+GfxDevice::PresentMode GfxDeviceClient::GetPresentMode()
+{
+ if (!m_Threaded)
+ {
+ // If we're not threaded don't change behavior
+ return m_RealDevice->GetPresentMode();
+ }
+ if (!m_RealDevice)
+ return kPresentAfterDraw;
+ GfxDeviceRenderer renderer = m_RealDevice->GetRenderer();
+ switch (renderer)
+ {
+ case kGfxRendererD3D9:
+ {
+ // In D3D9 BeginFrame() waits for the last Present() to finish on the render thread
+ // so we catch lost device state. It's best to present immediately after drawing.
+ return kPresentAfterDraw;
+ }
+ case kGfxRendererD3D11:
+ {
+ // We have to to wait for the last Present() to finish before leaving message loop,
+ // so it's good to present as early as possible to avoid waiting much (case 488862).
+ return kPresentBeforeUpdate;
+ }
+ default:
+ {
+ // By default we synchronize like with D3D9, so the render thread won't fall behind.
+ return kPresentAfterDraw;
+ }
+ }
+}
+
+void GfxDeviceClient::BeginFrame()
+{
+ CheckMainThread();
+ Assert(!m_InsideFrame);
+ m_InsideFrame = true;
+ if (m_Serialize)
+ {
+ WaitForPendingPresent();
+ // Worker thread should check GetNeedsBeginFrame()
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_BeginFrame);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->BeginFrame();
+}
+
+void GfxDeviceClient::EndFrame()
+{
+ CheckMainThread();
+ if (!m_InsideFrame)
+ return;
+ m_InsideFrame = false;
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_EndFrame);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->EndFrame();
+}
+
+void GfxDeviceClient::PresentFrame()
+{
+ CheckMainThread();
+
+ ((ClientDeviceRenderSurface*)m_BackBufferColor.object)->state = ClientDeviceRenderSurface::kInitial;
+ ((ClientDeviceRenderSurface*)m_BackBufferDepth.object)->state = ClientDeviceRenderSurface::kInitial;
+
+ if (m_Serialize)
+ {
+ // Check that we waited on event before issuing a new one
+ bool signalEvent = !m_PresentPending;
+ m_PresentPending = true;
+ m_PresentFrameID++;
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_PresentFrame);
+ m_CommandQueue->WriteValueType<GfxCmdBool>(signalEvent);
+ m_CommandQueue->WriteValueType<UInt32>(m_PresentFrameID);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->PresentFrame();
+}
+
+bool GfxDeviceClient::IsValidState()
+{
+ CheckMainThread();
+ if (!m_RealDevice)
+ return true;
+ return m_RealDevice->IsValidState();
+}
+
+bool GfxDeviceClient::HandleInvalidState()
+{
+ CheckMainThread();
+ if (IsValidState())
+ return true;
+ Assert(!IsRecording());
+
+#if GFX_SUPPORTS_D3D9
+ // Mark threaded dynamic VBOs as lost
+ ResetDynamicVBs();
+#endif
+#if GFX_SUPPORTS_OPENGLES20
+ // Only mark VBOs lost for GLES2.0 renderers (case 570721)
+ if (m_Renderer == kGfxRendererOpenGLES20Desktop || m_Renderer == kGfxRendererOpenGLES20Mobile)
+ MarkAllVBOsLost();
+#endif
+
+ CommonReloadResources(kReleaseRenderTextures);
+
+ bool insideFrame = m_InsideFrame;
+ if (insideFrame)
+ EndFrame();
+ AcquireThreadOwnership();
+ bool success = m_RealDevice->HandleInvalidState();
+ ReleaseThreadOwnership();
+ if (success && insideFrame)
+ BeginFrame();
+ return success;
+}
+
+void GfxDeviceClient::ResetDynamicResources()
+{
+ #if GFX_SUPPORTS_D3D9
+ // Mark threaded dynamic VBOs as lost
+ ResetDynamicVBs();
+ #endif
+
+ // We should have acquired thread ownership here
+ Assert(!m_Serialize);
+ GetRealGfxDevice().ResetDynamicResources();
+}
+
+bool GfxDeviceClient::IsReadyToBeginFrame()
+{
+ if (m_Threaded && m_RealDevice->GetRenderer() == kGfxRendererD3D11)
+ {
+ // DXGI requires us to keep processing events while the render thread calls Present()
+ // We have to wait for that before we begin preparing the next frame (case 488862)
+ return m_DeviceWorker->DidPresentFrame(m_PresentFrameID);
+ }
+ return true;
+}
+
+void GfxDeviceClient::FinishRendering()
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_FinishRendering);
+ SubmitCommands();
+ GetGfxDeviceWorker()->WaitForSignal();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->FinishRendering();
+}
+
+UInt32 GfxDeviceClient::InsertCPUFence()
+{
+ CheckMainThread();
+ if (m_Threaded)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_InsertCPUFence);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ return ++m_CurrentCPUFence;
+ }
+ return 0;
+}
+
+UInt32 GfxDeviceClient::GetNextCPUFence()
+{
+ return m_Threaded ? (m_CurrentCPUFence + 1) : 0;
+}
+
+void GfxDeviceClient::WaitOnCPUFence(UInt32 fence)
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+ CheckMainThread();
+ if (m_Threaded)
+ {
+ // Fence must have been already inserted
+ if (SInt32(fence - m_CurrentCPUFence) <= 0)
+ {
+ m_DeviceWorker->WaitOnCPUFence(fence);
+ }
+ else
+ ErrorString("CPU fence is invalid or very old!");
+ }
+#endif
+}
+
+void GfxDeviceClient::AcquireThreadOwnership()
+{
+ CheckMainThread();
+ if (!m_Threaded)
+ return;
+
+ m_ThreadOwnershipCount++;
+ if (m_ThreadOwnershipCount > 1)
+ return;
+
+ // Worker releases ownership
+ Assert(m_Serialize);
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_ReleaseThreadOwnership);
+ SubmitCommands();
+ WaitForSignal();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+
+ // Caller acquires ownership
+ m_RealDevice->AcquireThreadOwnership();
+
+ SetRealGfxDeviceThreadOwnership();
+
+ // We shouldn't serialize any commands
+ m_Serialize = false;
+}
+
+void GfxDeviceClient::ReleaseThreadOwnership()
+{
+ CheckMainThread();
+ if (!m_Threaded)
+ return;
+
+ Assert(m_ThreadOwnershipCount);
+ m_ThreadOwnershipCount--;
+ if (m_ThreadOwnershipCount > 0)
+ return;
+
+ // Caller releases ownership
+ m_RealDevice->ReleaseThreadOwnership();
+
+ // Worker acquires ownership
+ m_Serialize = true;
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_AcquireThreadOwnership);
+ SubmitCommands();
+ WaitForSignal();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+}
+
+void GfxDeviceClient::ImmediateVertex( float x, float y, float z )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_ImmediateVertex);
+ GfxCmdVector3 data = {x, y, z};
+ m_CommandQueue->WriteValueType<GfxCmdVector3>(data);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->ImmediateVertex(x, y, z);
+}
+
+void GfxDeviceClient::ImmediateNormal( float x, float y, float z )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_ImmediateNormal);
+ GfxCmdVector3 data = {x, y, z};
+ m_CommandQueue->WriteValueType<GfxCmdVector3>(data);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->ImmediateNormal(x, y, z);
+}
+
+void GfxDeviceClient::ImmediateColor( float r, float g, float b, float a )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_ImmediateColor);
+ GfxCmdVector4 data = {r, g, b, a};
+ m_CommandQueue->WriteValueType<GfxCmdVector4>(data);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->ImmediateColor(r, g, b, a);
+}
+
+void GfxDeviceClient::ImmediateTexCoordAll( float x, float y, float z )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_ImmediateTexCoordAll);
+ GfxCmdVector3 data = {x, y, z};
+ m_CommandQueue->WriteValueType<GfxCmdVector3>(data);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->ImmediateTexCoordAll(x, y, z);
+}
+
+void GfxDeviceClient::ImmediateTexCoord( int unit, float x, float y, float z )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_ImmediateTexCoord);
+ GfxCmdImmediateTexCoord data = {unit, x, y, z};
+ m_CommandQueue->WriteValueType<GfxCmdImmediateTexCoord>(data);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->ImmediateTexCoord(unit, x, y, z);
+}
+
+void GfxDeviceClient::ImmediateBegin( GfxPrimitiveType type )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_ImmediateBegin);
+ m_CommandQueue->WriteValueType<GfxPrimitiveType>(type);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->ImmediateBegin(type);
+}
+
+void GfxDeviceClient::ImmediateEnd()
+{
+ BeforeDrawCall(true);
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_ImmediateEnd);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->ImmediateEnd();
+}
+
+bool GfxDeviceClient::BeginRecording()
+{
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ return false;
+#endif
+ Assert(m_RecordDepth < m_MaxCallDepth);
+ DisplayListContext& parentContext = *m_CurrentContext;
+ m_RecordDepth++;
+ m_IsRecording = true;
+ m_CurrentContext = &m_DisplayListStack[m_RecordDepth];
+ memcpy(m_CurrentContext->shadersActive, parentContext.shadersActive, sizeof(parentContext.shadersActive));
+ m_CurrentContext->hasSetShaders = false;
+ m_CommandQueue = &m_CurrentContext->commandQueue;
+ m_Serialize = true;
+ return true;
+}
+
+bool GfxDeviceClient::EndRecording( GfxDisplayList** outDisplayList )
+{
+ Assert(m_RecordDepth > 0);
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DisplayList_End);
+ const void* data = m_CommandQueue->GetBuffer();
+ size_t size = m_CommandQueue->GetCurrentSize();
+ bool failed = m_CurrentContext->recordFailed;
+ ThreadedDisplayList* displayList = new ThreadedDisplayList(data, size, *m_CurrentContext);
+ m_CurrentContext->Reset();
+
+ m_RecordDepth--;
+ m_IsRecording = (m_RecordDepth != 0);
+ m_Serialize = m_Threaded || m_IsRecording;
+ m_CurrentContext = &m_DisplayListStack[m_RecordDepth];
+ m_CommandQueue = &m_CurrentContext->commandQueue;
+
+ // Execute just-recorded display list
+ displayList->Call();
+
+ if (failed)
+ SAFE_RELEASE(displayList);
+
+ Assert(outDisplayList && *outDisplayList==NULL);
+ *outDisplayList = displayList;
+ return !failed;
+}
+
+bool GfxDeviceClient::CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ bool success = false;
+ GfxCmdCaptureScreenshot capture = { left, bottom, width, height, rgba32, &success };
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_CaptureScreenshot);
+ m_CommandQueue->WriteValueType<GfxCmdCaptureScreenshot>(capture);
+ SubmitCommands();
+ GetGfxDeviceWorker()->WaitForSignal();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ return success;
+ }
+ else
+ return m_RealDevice->CaptureScreenshot(left, bottom, width, height, rgba32);
+}
+
+bool GfxDeviceClient::ReadbackImage( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ bool success = false;
+ GfxCmdReadbackImage read = { image, left, bottom, width, height, destX, destY, &success };
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_ReadbackImage);
+ m_CommandQueue->WriteValueType<GfxCmdReadbackImage>(read);
+ SubmitCommands();
+ GetGfxDeviceWorker()->WaitForSignal();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ return success;
+#else
+ // todo.
+ return false;
+#endif
+
+ }
+ else
+ return m_RealDevice->ReadbackImage(image, left, bottom, width, height, destX, destY);
+}
+
+void GfxDeviceClient::GrabIntoRenderTexture (RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height)
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ GfxCmdGrabIntoRenderTexture grab = { rs, rd, x, y, width, height };
+#else
+ GfxCmdGrabIntoRenderTexture grab = { rs.object ? ((ClientDeviceRenderSurface*)(rs.object))->internalHandle : NULL , rd.object ? ((ClientDeviceRenderSurface*)(rd.object))->internalHandle : NULL , x, y, width, height };
+#endif
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_GrabIntoRenderTexture);
+ m_CommandQueue->WriteValueType<GfxCmdGrabIntoRenderTexture>(grab);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ {
+ ClientDeviceRenderSurface* colorSurf = static_cast<ClientDeviceRenderSurface*>(rs.object);
+ ClientDeviceRenderSurface* depthSurf = static_cast<ClientDeviceRenderSurface*>(rd.object);
+ m_RealDevice->GrabIntoRenderTexture(colorSurf->internalHandle, depthSurf->internalHandle, x, y, width, height);
+ }
+#endif
+}
+
+
+#if ENABLE_PROFILER
+// Gets "what is being done" description from profiler sample
+// hierarchy, e.g. "Camera.Render/RenderOpaqueGeometry"
+static void GetContextDescriptionFromProfiler(std::string& outName)
+{
+ UnityProfilerPerThread* profiler = UnityProfilerPerThread::ms_InstanceTLS;
+ if (profiler)
+ {
+ // Do not get the last (most child) level, since that's usually always
+ // the same like DrawVBO.
+ for (int level = 3; level > 0; --level)
+ {
+ const ProfilerSample* sample = profiler->GetActiveSample(level);
+ if (sample && sample->information)
+ {
+ if (!outName.empty())
+ outName += '/';
+ outName += sample->information->name;
+ }
+ }
+ }
+ if (outName.empty())
+ outName = "<run with profiler for info>";
+}
+#endif // #if ENABLE_PROFILER
+
+
+void GfxDeviceClient::IgnoreNextUnresolveOnCurrentRenderTarget()
+{
+ for (int i = 0; i < kMaxSupportedRenderTargets; ++i)
+ {
+ ClientDeviceRenderSurface* colorSurf = (ClientDeviceRenderSurface*)m_ActiveRenderColorSurfaces[i].object;
+ if(colorSurf)
+ colorSurf->state = ClientDeviceRenderSurface::kInitial;
+ }
+
+ ClientDeviceRenderSurface* depthSurf = (ClientDeviceRenderSurface*)m_ActiveRenderDepthSurface.object;
+ depthSurf->state = ClientDeviceRenderSurface::kInitial;
+}
+
+void GfxDeviceClient::IgnoreNextUnresolveOnRS(RenderSurfaceHandle rs)
+{
+ if (!rs.IsValid())
+ return;
+ ((ClientDeviceRenderSurface*)rs.object)->state = ClientDeviceRenderSurface::kInitial;
+}
+
+
+
+void GfxDeviceClient::BeforeDrawCall(bool immediateMode)
+{
+ if (!GetFrameStats().m_StatsEnabled)
+ return;
+
+ ClientDeviceRenderSurface* colorWarn = NULL;
+ ClientDeviceRenderSurface* depthWarn = NULL;
+ bool backColorWarn = false;
+ bool backDepthWarn = false;
+
+ // Check if any of surfaces have been resolved, not cleared/discarded and now
+ // we want to render into them -- warn about this situation if needed.
+ // Then set all surfaces as "rendered into".
+ for (int i = 0; i < kMaxSupportedRenderTargets; ++i)
+ {
+ ClientDeviceRenderSurface* colorSurf = (ClientDeviceRenderSurface*)m_ActiveRenderColorSurfaces[i].object;
+ if(colorSurf)
+ {
+ if (colorSurf->state == ClientDeviceRenderSurface::kResolved)
+ colorWarn = colorSurf;
+ colorSurf->state = ClientDeviceRenderSurface::kRendered;
+ }
+ }
+
+ ClientDeviceRenderSurface* depthSurf = (ClientDeviceRenderSurface*)m_ActiveRenderDepthSurface.object;
+ if(depthSurf)
+ {
+ if (depthSurf->zformat != kDepthFormatNone && depthSurf->state == ClientDeviceRenderSurface::kResolved)
+ depthWarn = depthSurf;
+ depthSurf->state = ClientDeviceRenderSurface::kRendered;
+ }
+
+ #if ENABLE_PROFILER
+ // In development builds, emit warnings if we're emulating
+ // a tiled GPU.
+ if (gGraphicsCaps.warnRenderTargetUnresolves && (colorWarn || depthWarn || backColorWarn || backDepthWarn))
+ {
+ std::string desc;
+ GetContextDescriptionFromProfiler(desc);
+ if (colorWarn)
+ {
+ WarningStringMsg ("Tiled GPU perf. warning: RenderTexture %s (%dx%d) was not cleared/discarded, doing %s", depthWarn ? "" : "color surface ", colorWarn->width, colorWarn->height, desc.c_str());
+ }
+ else if (depthWarn)
+ {
+ WarningStringMsg ("Tiled GPU perf. warning: RenderTexture depth surface (%dx%d) was not cleared/discarded, doing %s", depthWarn->width, depthWarn->height, desc.c_str());
+ }
+ else if (backColorWarn)
+ {
+ WarningStringMsg ("Tiled GPU perf. warning: Backbuffer %s was not cleared/discarded, doing %s", backDepthWarn ? "" : "color surface ", desc.c_str());
+ }
+ else if (backDepthWarn)
+ {
+ WarningStringMsg ("Tiled GPU perf. warning: Backbuffer depth surface was not cleared/discarded, doing %s", desc.c_str());
+ }
+ }
+ #endif
+}
+
+bool GfxDeviceClient::IsPositionRequiredForTexGen (int texStageIndex) const
+{
+#if GFX_DEVICE_CLIENT_TRACK_TEXGEN
+ return m_CurrentContext->shadersActive[kShaderVertex] == 0 && (posForTexGen & (1<<texStageIndex) != 0);
+#else
+ return m_RealDevice->IsPositionRequiredForTexGen(texStageIndex);
+#endif
+}
+
+bool GfxDeviceClient::IsNormalRequiredForTexGen (int texStageIndex) const
+{
+#if GFX_DEVICE_CLIENT_TRACK_TEXGEN
+ return m_CurrentContext->shadersActive[kShaderVertex] == 0 && (nrmForTexGen & (1<<texStageIndex) != 0);
+#else
+ return m_RealDevice->IsNormalRequiredForTexGen(texStageIndex);
+#endif
+}
+
+bool GfxDeviceClient::IsPositionRequiredForTexGen() const
+{
+#if GFX_DEVICE_CLIENT_TRACK_TEXGEN
+ return m_CurrentContext->shadersActive[kShaderVertex] == 0 && posForTexGen != 0;
+#else
+ return m_RealDevice->IsPositionRequiredForTexGen();
+#endif
+}
+
+bool GfxDeviceClient::IsNormalRequiredForTexGen() const
+{
+#if GFX_DEVICE_CLIENT_TRACK_TEXGEN
+ return m_CurrentContext->shadersActive[kShaderVertex] == 0 && nrmForTexGen != 0;
+#else
+ return m_RealDevice->IsNormalRequiredForTexGen();
+#endif
+}
+
+void GfxDeviceClient::SetActiveContext (void* ctx)
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetActiveContext);
+ m_CommandQueue->WriteValueType<void*>(ctx);
+ SubmitCommands();
+ GetGfxDeviceWorker()->WaitForSignal();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetActiveContext (ctx);
+}
+
+
+void GfxDeviceClient::ResetFrameStats()
+{
+ CheckMainThread();
+ m_Stats.ResetClientStats();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_ResetFrameStats);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->ResetFrameStats();
+}
+
+void GfxDeviceClient::BeginFrameStats()
+{
+ ((ClientDeviceRenderSurface*)(m_BackBufferColor.object))->state = ClientDeviceRenderSurface::kInitial;
+ ((ClientDeviceRenderSurface*)(m_BackBufferDepth.object))->state = ClientDeviceRenderSurface::kInitial;
+
+ CheckMainThread();
+ m_Stats.BeginFrameStats();
+ if (m_Serialize)
+ {
+ m_CommandQueue->ResetWriteWaitTime();
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_BeginFrameStats);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->BeginFrameStats();
+}
+
+void GfxDeviceClient::EndFrameStats()
+{
+ CheckMainThread();
+ m_Stats.EndClientFrameStats();
+ if (m_Serialize)
+ {
+ m_Stats.m_ClientFrameTime -= m_CommandQueue->GetWriteWaitTime();
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_EndFrameStats);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->EndFrameStats();
+}
+
+void GfxDeviceClient::SaveDrawStats()
+{
+ CheckMainThread();
+ m_SavedStats.CopyClientStats(m_Stats);
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SaveDrawStats);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SaveDrawStats();
+}
+
+void GfxDeviceClient::RestoreDrawStats()
+{
+ CheckMainThread();
+ m_Stats.CopyClientStats(m_SavedStats);
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_RestoreDrawStats);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->RestoreDrawStats();
+}
+
+void GfxDeviceClient::SynchronizeStats()
+{
+ CheckMainThread();
+ if (m_Threaded)
+ {
+ GetGfxDeviceWorker()->GetLastFrameStats(m_Stats);
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SynchronizeStats);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_Stats.CopyAllDrawStats(m_RealDevice->GetFrameStats());
+}
+
+void* GfxDeviceClient::GetNativeGfxDevice()
+{
+ AcquireThreadOwnership ();
+ void* result = m_RealDevice->GetNativeGfxDevice();
+ ReleaseThreadOwnership ();
+ return result;
+}
+
+void* GfxDeviceClient::GetNativeTexturePointer(TextureID id)
+{
+ AcquireThreadOwnership ();
+ void* result = m_RealDevice->GetNativeTexturePointer(id);
+ ReleaseThreadOwnership ();
+ return result;
+}
+
+UInt32 GfxDeviceClient::GetNativeTextureID(TextureID id)
+{
+#if ENABLE_TEXTUREID_MAP
+ AcquireThreadOwnership ();
+ UInt32 result = m_RealDevice->GetNativeTextureID(id);
+ ReleaseThreadOwnership ();
+ return result;
+#else
+ return id.m_ID;
+#endif
+}
+
+#if ENABLE_TEXTUREID_MAP
+ intptr_t GfxDeviceClient::CreateExternalTextureFromNative(intptr_t nativeTex)
+ {
+ AcquireThreadOwnership ();
+ intptr_t result = m_RealDevice->CreateExternalTextureFromNative(nativeTex);
+ ReleaseThreadOwnership ();
+ return result;
+ }
+ void GfxDeviceClient::UpdateExternalTextureFromNative(TextureID tex, intptr_t nativeTex)
+ {
+ AcquireThreadOwnership ();
+ m_RealDevice->UpdateExternalTextureFromNative(tex, nativeTex);
+ ReleaseThreadOwnership ();
+ }
+#endif
+
+void GfxDeviceClient::InsertCustomMarker (int marker)
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_InsertCustomMarker);
+ m_CommandQueue->WriteValueType<int>(marker);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->InsertCustomMarker (marker);
+}
+
+
+void GfxDeviceClient::SetComputeBufferData (ComputeBufferID bufferHandle, const void* data, size_t size)
+{
+ CheckMainThread();
+ DebugAssert (bufferHandle.IsValid() && data && size);
+ if (m_Serialize)
+ {
+ // Don't want to upload data from a display list
+ m_CurrentContext->recordFailed = true;
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetComputeBufferData);
+ m_CommandQueue->WriteValueType<ComputeBufferID>(bufferHandle);
+ m_CommandQueue->WriteValueType<size_t>(size);
+ m_CommandQueue->WriteStreamingData(data, size);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetComputeBufferData (bufferHandle, data, size);
+}
+
+
+void GfxDeviceClient::GetComputeBufferData (ComputeBufferID bufferHandle, void* dest, size_t destSize)
+{
+ CheckMainThread();
+ DebugAssert (bufferHandle.IsValid() && dest && destSize);
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_GetComputeBufferData);
+ m_CommandQueue->WriteValueType<ComputeBufferID>(bufferHandle);
+ m_CommandQueue->WriteValueType<size_t>(destSize);
+ m_CommandQueue->WriteValueType<void*>(dest);
+ SubmitCommands();
+ GetGfxDeviceWorker()->WaitForSignal();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->GetComputeBufferData (bufferHandle, dest, destSize);
+}
+
+
+void GfxDeviceClient::CopyComputeBufferCount (ComputeBufferID srcBuffer, ComputeBufferID dstBuffer, UInt32 dstOffset)
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_CopyComputeBufferCount);
+ m_CommandQueue->WriteValueType<ComputeBufferID>(srcBuffer);
+ m_CommandQueue->WriteValueType<ComputeBufferID>(dstBuffer);
+ m_CommandQueue->WriteValueType<UInt32>(dstOffset);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->CopyComputeBufferCount (srcBuffer, dstBuffer, dstOffset);
+}
+
+void GfxDeviceClient::SetRandomWriteTargetTexture (int index, TextureID tid)
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetRandomWriteTargetTexture);
+ m_CommandQueue->WriteValueType<int>(index);
+ m_CommandQueue->WriteValueType<TextureID>(tid);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetRandomWriteTargetTexture (index, tid);
+}
+
+void GfxDeviceClient::SetRandomWriteTargetBuffer (int index, ComputeBufferID bufferHandle)
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetRandomWriteTargetBuffer);
+ m_CommandQueue->WriteValueType<int>(index);
+ m_CommandQueue->WriteValueType<ComputeBufferID>(bufferHandle);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetRandomWriteTargetBuffer (index, bufferHandle);
+}
+
+void GfxDeviceClient::ClearRandomWriteTargets ()
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_ClearRandomWriteTargets);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->ClearRandomWriteTargets ();
+}
+
+ComputeProgramHandle GfxDeviceClient::CreateComputeProgram (const UInt8* code, size_t codeSize)
+{
+ CheckMainThread();
+ DebugAssert(!IsRecording());
+ ClientDeviceComputeProgram* handle = UNITY_NEW(ClientDeviceComputeProgram, kMemGfxThread);
+ if (m_Serialize)
+ {
+ // Don't want to upload shaders from a display list
+ m_CurrentContext->recordFailed = true;
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_CreateComputeProgram);
+ m_CommandQueue->WriteValueType<ClientDeviceComputeProgram*>(handle);
+ m_CommandQueue->WriteValueType<size_t>(codeSize);
+ m_CommandQueue->WriteStreamingData(code, codeSize);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ handle->internalHandle = m_RealDevice->CreateComputeProgram (code, codeSize);
+ }
+ return ComputeProgramHandle(handle);
+}
+
+void GfxDeviceClient::DestroyComputeProgram (ComputeProgramHandle& cpHandle)
+{
+ CheckMainThread();
+
+ ClientDeviceComputeProgram* cp = static_cast<ClientDeviceComputeProgram*>(cpHandle.object);
+ if (!cp)
+ return;
+
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DestroyComputeProgram);
+ m_CommandQueue->WriteValueType<ClientDeviceComputeProgram*>(cp);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ m_RealDevice->DestroyComputeProgram (cp->internalHandle);
+ UNITY_DELETE (cp, kMemGfxThread);
+ }
+
+ cpHandle.Reset();
+}
+
+void GfxDeviceClient::CreateComputeConstantBuffers (unsigned count, const UInt32* sizes, ConstantBufferHandle* outCBs)
+{
+ Assert (count <= kMaxSupportedConstantBuffers);
+
+ CheckMainThread();
+
+ for (unsigned i = 0; i < count; ++i)
+ outCBs[i].object = UNITY_NEW(ClientDeviceConstantBuffer(sizes[i]), kMemGfxThread);
+
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_CreateComputeConstantBuffers);
+ m_CommandQueue->WriteValueType<unsigned>(count);
+ for (unsigned i = 0; i < count; ++i)
+ {
+ ClientDeviceConstantBuffer* handle = static_cast<ClientDeviceConstantBuffer*>(outCBs[i].object);
+ m_CommandQueue->WriteValueType<ClientDeviceConstantBuffer*>(handle);
+ }
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ ConstantBufferHandle cbHandles[kMaxSupportedConstantBuffers];
+ m_RealDevice->CreateComputeConstantBuffers (count, sizes, cbHandles);
+ for (unsigned i = 0; i < count; ++i)
+ {
+ ClientDeviceConstantBuffer* handle = static_cast<ClientDeviceConstantBuffer*>(outCBs[i].object);
+ Assert (handle);
+ handle->internalHandle = cbHandles[i];
+ }
+ }
+}
+
+void GfxDeviceClient::DestroyComputeConstantBuffers (unsigned count, ConstantBufferHandle* cbs)
+{
+ Assert (count <= kMaxSupportedConstantBuffers);
+
+ CheckMainThread();
+
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DestroyComputeConstantBuffers);
+ m_CommandQueue->WriteValueType<unsigned>(count);
+ for (unsigned i = 0; i < count; ++i)
+ {
+ ClientDeviceConstantBuffer* handle = static_cast<ClientDeviceConstantBuffer*>(cbs[i].object);
+ m_CommandQueue->WriteValueType<ClientDeviceConstantBuffer*>(handle);
+ }
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ ConstantBufferHandle cbHandles[kMaxSupportedConstantBuffers];
+ for (unsigned i = 0; i < count; ++i)
+ {
+ ClientDeviceConstantBuffer* handle = static_cast<ClientDeviceConstantBuffer*>(cbs[i].object);
+ if (handle)
+ cbHandles[i] = handle->internalHandle;
+
+ UNITY_DELETE (handle, kMemGfxThread);
+ }
+ m_RealDevice->DestroyComputeConstantBuffers (count, cbHandles);
+ }
+
+ for (unsigned i = 0; i < count; ++i)
+ cbs[i].Reset();
+}
+
+void GfxDeviceClient::CreateComputeBuffer (ComputeBufferID id, size_t count, size_t stride, UInt32 flags)
+{
+ CheckMainThread();
+ DebugAssert(!IsRecording());
+ Assert (id.IsValid());
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_CreateComputeBuffer);
+ m_CommandQueue->WriteValueType<ComputeBufferID>(id);
+ m_CommandQueue->WriteValueType<size_t>(count);
+ m_CommandQueue->WriteValueType<size_t>(stride);
+ m_CommandQueue->WriteValueType<UInt32>(flags);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->CreateComputeBuffer (id, count, stride, flags);
+}
+
+void GfxDeviceClient::DestroyComputeBuffer (ComputeBufferID handle)
+{
+ if (!handle.IsValid())
+ return;
+
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DestroyComputeBuffer);
+ m_CommandQueue->WriteValueType<ComputeBufferID>(handle);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->DestroyComputeBuffer (handle);
+}
+
+void GfxDeviceClient::UpdateComputeConstantBuffers (unsigned count, ConstantBufferHandle* cbs, UInt32 cbDirty, size_t dataSize, const UInt8* data, const UInt32* cbSizes, const UInt32* cbOffsets, const int* bindPoints)
+{
+ CheckMainThread();
+
+ if (!count)
+ {
+ DebugAssert (dataSize == 0);
+ return;
+ }
+ DebugAssert (dataSize != 0);
+
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_UpdateComputeConstantBuffers);
+ m_CommandQueue->WriteValueType<unsigned>(count);
+ m_CommandQueue->WriteValueType<UInt32>(cbDirty);
+ for (int i = 0; i < count; ++i)
+ {
+ ClientDeviceConstantBuffer* handle = static_cast<ClientDeviceConstantBuffer*>(cbs[i].object);
+ m_CommandQueue->WriteValueType<ClientDeviceConstantBuffer*>(handle);
+ m_CommandQueue->WriteValueType<UInt32>(cbSizes[i]);
+ m_CommandQueue->WriteValueType<UInt32>(cbOffsets[i]);
+ m_CommandQueue->WriteValueType<int>(bindPoints[i]);
+ }
+ m_CommandQueue->WriteValueType<size_t>(dataSize);
+ m_CommandQueue->WriteStreamingData(data, dataSize);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ ConstantBufferHandle cbHandles[kMaxSupportedConstantBuffers];
+ for (unsigned i = 0; i < count; ++i)
+ {
+ ClientDeviceConstantBuffer* handle = static_cast<ClientDeviceConstantBuffer*>(cbs[i].object);
+ if (handle)
+ cbHandles[i] = handle->internalHandle;
+ }
+ m_RealDevice->UpdateComputeConstantBuffers (count, cbHandles, cbDirty, dataSize, data, cbSizes, cbOffsets, bindPoints);
+ }
+}
+
+void GfxDeviceClient::UpdateComputeResources (
+ unsigned texCount, const TextureID* textures, const int* texBindPoints,
+ unsigned samplerCount, const unsigned* samplers,
+ unsigned inBufferCount, const ComputeBufferID* inBuffers, const int* inBufferBindPoints,
+ unsigned outBufferCount, const ComputeBufferID* outBuffers, const TextureID* outTextures, const UInt32* outBufferBindPoints)
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_UpdateComputeResources);
+ m_CommandQueue->WriteValueType<unsigned>(texCount);
+ for (int i = 0; i < texCount; ++i)
+ {
+ m_CommandQueue->WriteValueType<TextureID>(textures[i]);
+ m_CommandQueue->WriteValueType<int>(texBindPoints[i]);
+ }
+ m_CommandQueue->WriteValueType<unsigned>(samplerCount);
+ for (int i = 0; i < samplerCount; ++i)
+ {
+ m_CommandQueue->WriteValueType<unsigned>(samplers[i]);
+ }
+ m_CommandQueue->WriteValueType<unsigned>(inBufferCount);
+ for (int i = 0; i < inBufferCount; ++i)
+ {
+ m_CommandQueue->WriteValueType<ComputeBufferID>(inBuffers[i]);
+ m_CommandQueue->WriteValueType<int>(inBufferBindPoints[i]);
+ }
+ m_CommandQueue->WriteValueType<unsigned>(outBufferCount);
+ for (int i = 0; i < outBufferCount; ++i)
+ {
+ m_CommandQueue->WriteValueType<ComputeBufferID>(outBuffers[i]);
+ m_CommandQueue->WriteValueType<TextureID>(outTextures[i]);
+ m_CommandQueue->WriteValueType<UInt32>(outBufferBindPoints[i]);
+ }
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->UpdateComputeResources (texCount, textures, texBindPoints, samplerCount, samplers, inBufferCount, inBuffers, inBufferBindPoints, outBufferCount, outBuffers, outTextures, outBufferBindPoints);
+}
+
+void GfxDeviceClient::DispatchComputeProgram (ComputeProgramHandle cpHandle, unsigned threadsX, unsigned threadsY, unsigned threadsZ)
+{
+ CheckMainThread();
+
+ ClientDeviceComputeProgram* cp = static_cast<ClientDeviceComputeProgram*>(cpHandle.object);
+ if (!cp)
+ return;
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DispatchComputeProgram);
+ m_CommandQueue->WriteValueType<ClientDeviceComputeProgram*>(cp);
+ m_CommandQueue->WriteValueType<unsigned>(threadsX);
+ m_CommandQueue->WriteValueType<unsigned>(threadsY);
+ m_CommandQueue->WriteValueType<unsigned>(threadsZ);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ m_RealDevice->DispatchComputeProgram (cp->internalHandle, threadsX, threadsY, threadsZ);
+ }
+}
+
+void GfxDeviceClient::DrawNullGeometry (GfxPrimitiveType topology, int vertexCount, int instanceCount)
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DrawNullGeometry);
+ m_CommandQueue->WriteValueType<GfxPrimitiveType>(topology);
+ m_CommandQueue->WriteValueType<int>(vertexCount);
+ m_CommandQueue->WriteValueType<int>(instanceCount);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->DrawNullGeometry (topology, vertexCount, instanceCount);
+}
+
+void GfxDeviceClient::DrawNullGeometryIndirect (GfxPrimitiveType topology, ComputeBufferID bufferHandle, UInt32 bufferOffset)
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DrawNullGeometryIndirect);
+ m_CommandQueue->WriteValueType<GfxPrimitiveType>(topology);
+ m_CommandQueue->WriteValueType<ComputeBufferID>(bufferHandle);
+ m_CommandQueue->WriteValueType<UInt32>(bufferOffset);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->DrawNullGeometryIndirect (topology, bufferHandle, bufferOffset);
+}
+
+
+
+#if ENABLE_PROFILER
+
+void GfxDeviceClient::BeginProfileEvent (const char* name)
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_BeginProfileEvent);
+ m_CommandQueue->WriteValueType<const char*>(name); // assuming the pointer doesn't possibly go away!
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->BeginProfileEvent (name);
+}
+void GfxDeviceClient::EndProfileEvent ()
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_EndProfileEvent);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->EndProfileEvent ();
+}
+
+void GfxDeviceClient::ProfileControl (GfxProfileControl ctrl, unsigned param)
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_ProfileControl);
+ m_CommandQueue->WriteValueType<GfxProfileControl>(ctrl);
+ m_CommandQueue->WriteValueType<unsigned>(param);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->ProfileControl (ctrl, param);
+}
+
+
+GfxTimerQuery* GfxDeviceClient::CreateTimerQuery()
+{
+ CheckMainThread();
+ Assert(!IsRecording());
+ return new ThreadedTimerQuery(*this);
+}
+
+void GfxDeviceClient::DeleteTimerQuery(GfxTimerQuery* query)
+{
+ CheckMainThread();
+ Assert(!IsRecording());
+ delete query;
+}
+
+void GfxDeviceClient::BeginTimerQueries()
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_BeginTimerQueries);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->BeginTimerQueries();
+}
+
+void GfxDeviceClient::EndTimerQueries()
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_EndTimerQueries);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->EndTimerQueries();
+}
+
+#endif
+
+// Editor-only stuff
+#if UNITY_EDITOR
+
+void GfxDeviceClient::SetAntiAliasFlag( bool aa )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetAntiAliasFlag);
+ m_CommandQueue->WriteValueType<GfxCmdBool>(aa);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->SetAntiAliasFlag(aa);
+}
+
+
+void GfxDeviceClient::DrawUserPrimitives( GfxPrimitiveType type, int vertexCount, UInt32 vertexChannels, const void* data, int stride )
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_DrawUserPrimitives);
+ GfxCmdDrawUserPrimitives user = { type, vertexCount, vertexChannels, stride };
+ m_CommandQueue->WriteValueType<GfxCmdDrawUserPrimitives>(user);
+ m_CommandQueue->WriteStreamingData(data, vertexCount * stride);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_RealDevice->DrawUserPrimitives(type, vertexCount, vertexChannels, data, stride);
+}
+
+int GfxDeviceClient::GetCurrentTargetAA() const
+{
+#if UNITY_WIN
+ return ThreadedWindow::GetCurrentFSAALevel();
+#else
+ return 1; // fix this
+#endif
+}
+
+#endif
+
+#if UNITY_EDITOR && UNITY_WIN
+//ToDo: This is windows specific code, we should replace HWND window with something more abstract
+GfxDeviceWindow* GfxDeviceClient::CreateGfxWindow( HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias )
+{
+ CheckMainThread();
+ ThreadedWindow* result = new ThreadedWindow(window, width, height, depthFormat, antiAlias);
+ ClientDeviceWindow* handle = result->m_ClientWindow;
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_CreateWindow);
+ m_CommandQueue->WriteValueType<ClientDeviceWindow*>(handle);
+ GfxCmdCreateWindow create = { window, width, height, depthFormat, antiAlias };
+ m_CommandQueue->WriteValueType<GfxCmdCreateWindow>(create);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ handle->internalWindow = m_RealDevice->CreateGfxWindow(window, width, height, depthFormat, antiAlias);
+ }
+
+ return result;
+}
+
+void GfxDeviceClient::SetActiveWindow(ClientDeviceWindow* handle)
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_SetActiveWindow);
+ m_CommandQueue->WriteValueType<ClientDeviceWindow*>(handle);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ handle->GetInternal()->SetAsActiveWindow();
+}
+
+void GfxDeviceClient::WindowReshape(ClientDeviceWindow* handle, int width, int height, DepthBufferFormat depthFormat, int antiAlias)
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_WindowReshape);
+ m_CommandQueue->WriteValueType<ClientDeviceWindow*>(handle);
+ GfxCmdWindowReshape reshape = { width, height, depthFormat, antiAlias };
+ m_CommandQueue->WriteValueType<GfxCmdWindowReshape>(reshape);
+ SubmitCommands();
+ WaitForSignal();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ handle->GetInternal()->Reshape(width, height, depthFormat, antiAlias);
+}
+
+void GfxDeviceClient::WindowDestroy(ClientDeviceWindow* handle)
+{
+ CheckMainThread();
+ if (m_Serialize)
+ {
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_WindowDestroy);
+ m_CommandQueue->WriteValueType<ClientDeviceWindow*>(handle);
+ SubmitCommands();
+ WaitForSignal();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ delete handle->GetInternal();
+}
+
+void GfxDeviceClient::BeginRendering(ClientDeviceWindow* handle)
+{
+ CheckMainThread();
+ m_InsideFrame = true;
+ if (m_Serialize)
+ {
+ WaitForPendingPresent();
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_BeginRendering);
+ m_CommandQueue->WriteValueType<ClientDeviceWindow*>(handle);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ handle->GetInternal()->BeginRendering();
+}
+
+void GfxDeviceClient::EndRendering(ClientDeviceWindow* handle, bool presentContent)
+{
+ CheckMainThread();
+ m_InsideFrame = false;
+ if (m_Serialize)
+ {
+ // Check that we waited on event before issuing a new one
+ bool signalEvent = !m_PresentPending;
+ m_PresentPending = true;
+ m_CommandQueue->WriteValueType<GfxCommand>(kGfxCmd_EndRendering);
+ m_CommandQueue->WriteValueType<ClientDeviceWindow*>(handle);
+ m_CommandQueue->WriteValueType<GfxCmdBool>(presentContent);
+ m_CommandQueue->WriteValueType<GfxCmdBool>(signalEvent);
+ SubmitCommands();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ handle->GetInternal()->EndRendering(presentContent);
+}
+
+#endif
+
+#if UNITY_WIN
+int GfxDeviceClient::GetCurrentTargetWidth() const
+{
+ return m_CurrentTargetWidth;
+}
+int GfxDeviceClient::GetCurrentTargetHeight() const
+{
+ return m_CurrentTargetHeight;
+}
+
+void GfxDeviceClient::SetCurrentTargetSize(int width, int height)
+{
+ m_CurrentTargetWidth = width;
+ m_CurrentTargetHeight = height;
+}
+
+void GfxDeviceClient::SetCurrentWindowSize(int width, int height)
+{
+ m_CurrentWindowWidth = m_CurrentTargetWidth = width;
+ m_CurrentWindowHeight = m_CurrentTargetHeight = height;
+}
+#endif
+
+void GfxDeviceClient::SetRealGfxDevice(GfxThreadableDevice* realDevice)
+{
+ if (realDevice)
+ {
+ m_RealDevice = realDevice;
+ m_Renderer = realDevice->GetRenderer();
+ m_UsesOpenGLTextureCoords = realDevice->UsesOpenGLTextureCoords();
+ m_UsesHalfTexelOffset = realDevice->UsesHalfTexelOffset();
+ m_MaxBufferedFrames = realDevice->GetMaxBufferedFrames();
+ m_FramebufferDepthFormat = realDevice->GetFramebufferDepthFormat();
+#if UNITY_WIN
+ m_CurrentTargetWidth = realDevice->GetCurrentTargetWidth();
+ m_CurrentTargetHeight = realDevice->GetCurrentTargetHeight();
+ m_CurrentWindowWidth = m_CurrentTargetWidth;
+ m_CurrentWindowHeight = m_CurrentTargetHeight;
+#endif
+ }
+ else
+ {
+ m_RealDevice = NULL;
+ m_Renderer = kGfxRendererOpenGL;
+ m_UsesOpenGLTextureCoords = true;
+ m_UsesHalfTexelOffset = false;
+ m_MaxBufferedFrames = 1;
+ }
+}
+
+void GfxDeviceClient::UpdateFogDisabled()
+{
+ m_FogParams.mode = kFogDisabled;
+}
+
+void GfxDeviceClient::UpdateFogEnabled(const GfxFogParams& fogParams)
+{
+ m_FogParams = fogParams;
+}
+
+void GfxDeviceClient::UpdateShadersActive(bool shadersActive[kShaderTypeCount])
+{
+ memcpy(m_CurrentContext->shadersActive, shadersActive, sizeof(bool[kShaderTypeCount]));
+}
+
+void GfxDeviceClient::CheckMainThread() const
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+}
+
+void GfxDeviceClient::WriteBufferData(const void* data, int size)
+{
+ int maxNonStreamedSize = m_CommandQueue->GetAllocatedSize() / 2;
+ if (size <= maxNonStreamedSize || IsRecording())
+ {
+ // In the NaCl Web Player, make sure that only complete commands are submitted, as we are not truely
+ // asynchronous.
+ #if !ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+ SubmitCommands();
+ #endif
+ void* dest = m_CommandQueue->GetWriteDataPointer(size, ThreadedStreamBuffer::kDefaultAlignment);
+ memcpy(dest, data, size);
+ SubmitCommands();
+ }
+ else
+ m_CommandQueue->WriteStreamingData(data, size);
+}
+
+void GfxDeviceClient::ReadbackData(dynamic_array<UInt8>& data, const SInt32 chunkSize)
+{
+ data.resize_uninitialized(0);
+ m_CommandQueue->WriteValueType<SInt32>(chunkSize);
+ for (;;)
+ {
+ volatile void* chunkPtr = m_CommandQueue->GetWriteDataPointer(sizeof(SInt32) + chunkSize, ThreadedStreamBuffer::kDefaultAlignment);
+ volatile SInt32* returnedSizePtr = static_cast<volatile SInt32*>(chunkPtr);
+ volatile void* returnedDataPtr = &returnedSizePtr[1];
+ *returnedSizePtr = -1;
+ m_CommandQueue->WriteSubmitData();
+ while (*returnedSizePtr == -1)
+ {
+ WaitForSignal();
+ // Busy wait
+ }
+ SInt32 size = *returnedSizePtr;
+ UnityMemoryBarrier();
+ if (size > 0 && size <= chunkSize)
+ {
+ size_t oldSize = data.size();
+ data.resize_uninitialized(oldSize + size);
+ // Const_cast needed to cast away volatile
+ memcpy(&data[oldSize], const_cast<const void*>(returnedDataPtr), size);
+ }
+ if (size < chunkSize)
+ break;
+ }
+}
+
+void GfxDeviceClient::SubmitCommands()
+{
+ m_CommandQueue->WriteSubmitData();
+}
+
+void GfxDeviceClient::DoLockstep()
+{
+ if (!IsRecording())
+ {
+ SubmitCommands();
+ GetGfxDeviceWorker()->LockstepWait();
+ }
+}
+
+void GfxDeviceClient::WaitForPendingPresent()
+{
+ if (m_PresentPending)
+ {
+ PROFILER_AUTO(gGfxWaitForPresentProf, NULL);
+
+ // We must wait for the last Present() to finish to figure out if device was lost.
+ // Beginning a new frame on a lost device will cause D3D debug runtime to complain.
+ // We may also lose any resources we upload on the render thread after seeing D3DERR_DEVICELOST.
+ m_DeviceWorker->WaitForEvent(GfxDeviceWorker::kEventTypePresent);
+ m_PresentPending = false;
+ }
+}
+
+RenderTextureFormat GfxDeviceClient::GetDefaultRTFormat() const
+{
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ return kRTFormatARGB32;
+#else
+ return m_RealDevice->GetDefaultRTFormat();
+#endif
+}
+
+RenderTextureFormat GfxDeviceClient::GetDefaultHDRRTFormat() const
+{
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ return kRTFormatARGBHalf;
+#else
+ return m_RealDevice->GetDefaultHDRRTFormat();;
+#endif
+}
+
+#if GFX_DEVICE_CLIENT_TRACK_TEXGEN
+ void GfxDeviceClient::SetTexGen(int unit, TexGenMode mode)
+ {
+ bool posNeeded = (mode == kTexGenObject || mode == kTexGenEyeLinear);
+ bool nrmNeeded = (mode == kTexGenSphereMap || mode == kTexGenCubeReflect || mode == kTexGenCubeNormal);
+
+ if(posNeeded) posForTexGen |= (1<<unit);
+ else posForTexGen &= ~(1<<unit);
+ if(nrmNeeded) nrmForTexGen |= (1<<unit);
+ else nrmForTexGen &= ~(1<<unit);
+ }
+ void GfxDeviceClient::DropTexGen(int unit)
+ {
+ posForTexGen &= ~(1<<unit);
+ nrmForTexGen &= ~(1<<unit);
+ }
+#endif
+
+#endif //ENABLE_MULTITHREADED_CODE
diff --git a/Runtime/GfxDevice/threaded/GfxDeviceClient.h b/Runtime/GfxDevice/threaded/GfxDeviceClient.h
new file mode 100644
index 0000000..017fd88
--- /dev/null
+++ b/Runtime/GfxDevice/threaded/GfxDeviceClient.h
@@ -0,0 +1,423 @@
+#pragma once
+#if ENABLE_MULTITHREADED_CODE
+
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/GfxDevice/TransformState.h"
+#include "Runtime/GfxDevice/threaded/ClientIDMapper.h"
+#include "Runtime/GfxDevice/threaded/ThreadedDeviceStates.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+class ThreadedStreamBuffer;
+class GfxDeviceWorker;
+class GfxDeviceWindow;
+class ThreadedWindow;
+class ThreadedDynamicVBO;
+struct DisplayListContext;
+
+enum
+{
+ kClientDeviceThreaded = 1 << 0,
+ kClientDeviceForceRef = 1 << 1,
+ kClientDeviceUseRealDevice = 1 << 2,
+ kClientDeviceClientProcess = 1 << 3,
+ kClientDeviceWorkerProcess = 1 << 4,
+};
+
+GfxDevice* CreateClientGfxDevice(GfxDeviceRenderer renderer, UInt32 flags, size_t bufferSize = 0, void *buffer=0);
+bool GfxDeviceWorkerProcessRunCommand();
+
+#define GFX_DEVICE_CLIENT_TRACK_TEXGEN (GFX_SUPPORTS_OPENGLES20 || GFX_SUPPORTS_OPENGLES30)
+
+
+class GfxDeviceClient : public GfxDevice
+{
+public:
+ GfxDeviceClient(bool threaded, bool clientProcess, size_t bufferSize = 0, void *buffer=0);
+ GFX_API ~GfxDeviceClient();
+
+ GFX_API void InvalidateState();
+ #if GFX_DEVICE_VERIFY_ENABLE
+ GFX_API void VerifyState();
+ #endif
+
+ GFX_API void SetMaxBufferedFrames (int bufferSize);
+
+ GFX_API void Clear (UInt32 clearFlags, const float color[4], float depth, int stencil);
+ GFX_API void SetUserBackfaceMode( bool enable );
+ GFX_API void SetWireframe(bool wire);
+ GFX_API bool GetWireframe() const;
+
+ GFX_API void SetInvertProjectionMatrix( bool enable );
+ GFX_API bool GetInvertProjectionMatrix() const;
+
+ #if GFX_USES_VIEWPORT_OFFSET
+ GFX_API void SetViewportOffset( float x, float y );
+ GFX_API void GetViewportOffset( float &x, float &y ) const;
+ #endif
+
+ GFX_API GPUSkinningInfo *CreateGPUSkinningInfo();
+ GFX_API void DeleteGPUSkinningInfo(GPUSkinningInfo *info);
+ GFX_API void SkinOnGPU( GPUSkinningInfo * info, bool lastThisFrame );
+ GFX_API void UpdateSkinSourceData(GPUSkinningInfo *info, const void *vertData, const BoneInfluence *skinData, bool dirty);
+ GFX_API void UpdateSkinBonePoses(GPUSkinningInfo *info, const int boneCount, const Matrix4x4f* poses);
+
+ GFX_API DeviceBlendState* CreateBlendState(const GfxBlendState& state);
+ GFX_API DeviceDepthState* CreateDepthState(const GfxDepthState& state);
+ GFX_API DeviceStencilState* CreateStencilState(const GfxStencilState& state);
+ GFX_API DeviceRasterState* CreateRasterState(const GfxRasterState& state);
+
+ GFX_API void RecordSetBlendState(const DeviceBlendState* state, const ShaderLab::FloatVal& alphaRef, const ShaderLab::PropertySheet* props);
+ GFX_API void SetBlendState(const DeviceBlendState* state, float alphaRef);
+ GFX_API void SetDepthState(const DeviceDepthState* state);
+ GFX_API void SetStencilState(const DeviceStencilState* state, int stencilRef);
+ GFX_API void SetRasterState(const DeviceRasterState* state);
+ GFX_API void SetSRGBWrite (const bool);
+ GFX_API bool GetSRGBWrite ();
+
+ #if UNITY_XENON
+ GFX_API void SetNullPixelShader();
+ GFX_API void SetHiZEnable( const HiZstate hiz_enable );
+ GFX_API void SetHiStencilState( const bool hiStencilEnable, const bool hiStencilWriteEnable, const int hiStencilRef, const CompareFunction cmpfunc );
+ GFX_API void HiStencilFlush( const HiSflush flushtype );
+ #endif
+
+ GFX_API void SetWorldMatrix( const float matrix[16] );
+ GFX_API void SetViewMatrix( const float matrix[16] );
+ GFX_API void SetProjectionMatrix(const Matrix4x4f& matrix);
+ GFX_API void GetMatrix( float outMatrix[16] ) const;
+
+ GFX_API const float* GetWorldMatrix() const;
+ GFX_API const float* GetViewMatrix() const;
+ GFX_API const float* GetProjectionMatrix() const;
+ GFX_API const float* GetDeviceProjectionMatrix() const;
+
+ GFX_API void SetInverseScale( float invScale );
+
+ GFX_API void SetNormalizationBackface( NormalizationMode mode, bool backface );
+
+ GFX_API void SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial );
+ GFX_API void RecordSetMaterial( const ShaderLab::VectorVal& ambient, const ShaderLab::VectorVal& diffuse, const ShaderLab::VectorVal& specular, const ShaderLab::VectorVal& emissive, const ShaderLab::FloatVal& shininess, const ShaderLab::PropertySheet* props );
+ GFX_API void SetMaterial( const float ambient[4], const float diffuse[4], const float specular[4], const float emissive[4], const float shininess );
+ GFX_API void RecordSetColor( const ShaderLab::VectorVal& color, const ShaderLab::PropertySheet* props );
+ GFX_API void SetColor( const float color[4] );
+ GFX_API void SetViewport( int x, int y, int width, int height );
+ GFX_API void GetViewport( int* values ) const;
+
+ GFX_API void SetScissorRect( int x, int y, int width, int height );
+ GFX_API void DisableScissor();
+ GFX_API bool IsScissorEnabled() const;
+ GFX_API void GetScissorRect( int values[4] ) const;
+
+ GFX_API TextureCombinersHandle CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular );
+ GFX_API void DeleteTextureCombiners( TextureCombinersHandle& textureCombiners );
+ GFX_API void SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props );
+
+ GFX_API void SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias);
+ GFX_API void SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace );
+ GFX_API void SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16]);
+ GFX_API void SetTextureName( TextureID texture, char const* name );
+
+ GFX_API void SetMaterialProperties(const MaterialPropertyBlock& block);
+
+ GFX_API GpuProgram* CreateGpuProgram( const std::string& source, CreateGpuProgramOutput& output );
+ GFX_API void SetShadersMainThread( ShaderLab::SubProgram* programs[kShaderTypeCount], const ShaderLab::PropertySheet* props );
+ GFX_API bool IsShaderActive( ShaderType type ) const;
+ GFX_API void DestroySubProgram( ShaderLab::SubProgram* subprogram );
+ GFX_API void SetConstantBufferInfo (int id, int size);
+
+ GFX_API void DisableLights( int startLight );
+ GFX_API void SetLight( int light, const GfxVertexLight& data);
+ GFX_API void SetAmbient( const float ambient[4] );
+
+ GFX_API void RecordEnableFog( FogMode fogMode, const ShaderLab::FloatVal& fogStart, const ShaderLab::FloatVal& fogEnd, const ShaderLab::FloatVal& fogDensity, const ShaderLab::VectorVal& fogColor, const ShaderLab::PropertySheet* props );
+ GFX_API void EnableFog( const GfxFogParams& fog );
+ GFX_API void DisableFog();
+
+ GFX_API VBO* CreateVBO();
+ GFX_API void DeleteVBO( VBO* vbo );
+ GFX_API DynamicVBO& GetDynamicVBO();
+
+ GFX_API void BeginSkinning( int maxSkinCount );
+ GFX_API bool SkinMesh( const SkinMeshInfo& skin, VBO* vbo );
+ GFX_API void EndSkinning();
+
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+ GFX_API void BeginStaticBatching(const ChannelAssigns& channels, GfxPrimitiveType topology);
+ GFX_API void StaticBatchMesh( UInt32 firstVertex, UInt32 vertexCount, const IndexBufferData& indices, UInt32 firstIndexByte, UInt32 indexCount );
+ GFX_API void EndStaticBatching( VBO& vbo, const Matrix4x4f& matrix, TransformType transformType, int sourceChannels );
+
+ GFX_API void BeginDynamicBatching( const ChannelAssigns& shaderChannel, UInt32 channelsInVBO, size_t maxVertices, size_t maxIndices, GfxPrimitiveType topology);
+ GFX_API void DynamicBatchMesh( const Matrix4x4f& matrix, const VertexBufferData& vertices, UInt32 firstVertex, UInt32 vertexCount, const IndexBufferData& indices, UInt32 firstIndexByte, UInt32 indexCount );
+#if ENABLE_SPRITES
+ GFX_API void DynamicBatchSprite(const Matrix4x4f* matrix, const SpriteRenderData* rd, ColorRGBA32 color);
+#endif
+ GFX_API void EndDynamicBatching( TransformType transformType );
+#endif
+
+ GFX_API void AddBatchingStats( int batchedTris, int batchedVerts, int batchedCalls );
+
+#if UNITY_XENON
+ GFX_API RawVBO* CreateRawVBO( UInt32 size, UInt32 flags );
+ GFX_API void DeleteRawVBO( RawVBO* vbo );
+
+ GFX_API void EnablePersistDisplayOnQuit( bool enabled );
+ GFX_API void OnLastFrameCallback();
+
+ GFX_API void RegisterTexture2D( TextureID tid, IDirect3DBaseTexture9* texture );
+ GFX_API void PatchTexture2D( TextureID tid, IDirect3DBaseTexture9* texture );
+ GFX_API void DeleteTextureEntryOnly( TextureID textureID );
+ GFX_API void UnbindAndDelayReleaseTexture( IDirect3DBaseTexture9* texture );
+ GFX_API void SetTextureWrapModes( TextureID textureID, TextureWrapMode wrapU, TextureWrapMode wrapV, TextureWrapMode wrapW );
+
+ GFX_API xenon::IVideoPlayer* CreateVideoPlayer(bool fullscreen);
+ GFX_API void DeleteVideoPlayer(xenon::IVideoPlayer* player);
+#endif
+
+ GFX_API RenderSurfaceHandle CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags);
+ GFX_API RenderSurfaceHandle CreateRenderDepthSurface (TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags);
+ GFX_API void DestroyRenderSurface (RenderSurfaceHandle& rs);
+ GFX_API void DiscardContents (RenderSurfaceHandle& rs);
+ GFX_API void IgnoreNextUnresolveOnCurrentRenderTarget();
+ GFX_API void IgnoreNextUnresolveOnRS(RenderSurfaceHandle rs);
+ GFX_API void SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face = kCubeFaceUnknown);
+ GFX_API void SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face, UInt32 flags);
+ GFX_API void ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle);
+ GFX_API void ResolveDepthIntoTexture (RenderSurfaceHandle colorHandle, RenderSurfaceHandle depthHandle);
+
+ GFX_API RenderSurfaceHandle GetActiveRenderColorSurface (int index);
+ GFX_API RenderSurfaceHandle GetActiveRenderDepthSurface ();
+
+ GFX_API bool IsRenderTargetConfigValid(UInt32 width, UInt32 height, RenderTextureFormat colorFormat, DepthBufferFormat depthFormat);
+
+ GFX_API void SetSurfaceFlags(RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags);
+
+ GFX_API void UploadTexture2D( TextureID texture, TextureDimension dimension, UInt8* srcData, int srcSize, int width, int height, TextureFormat format, int mipCount, UInt32 uploadFlags, int skipMipLevels, TextureUsageMode usageMode, TextureColorSpace colorSpace );
+ GFX_API void UploadTextureSubData2D( TextureID texture, UInt8* srcData, int srcSize, int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace );
+ GFX_API void UploadTextureCube( TextureID texture, UInt8* srcData, int srcSize, int faceDataSize, int size, TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace );
+ GFX_API void UploadTexture3D( TextureID texture, UInt8* srcData, int srcSize, int width, int height, int depth, TextureFormat format, int mipCount, UInt32 uploadFlags );
+ GFX_API void DeleteTexture( TextureID texture );
+
+ GFX_API PresentMode GetPresentMode();
+
+ GFX_API void BeginFrame();
+ GFX_API void EndFrame();
+ GFX_API void PresentFrame();
+ GFX_API bool IsValidState();
+ GFX_API bool HandleInvalidState();
+ GFX_API void ResetDynamicResources();
+ GFX_API bool IsReadyToBeginFrame();
+ GFX_API void FinishRendering();
+ GFX_API UInt32 InsertCPUFence();
+ GFX_API UInt32 GetNextCPUFence();
+ GFX_API void WaitOnCPUFence(UInt32 fence);
+
+ GFX_API void AcquireThreadOwnership();
+ GFX_API void ReleaseThreadOwnership();
+
+ GFX_API void ImmediateVertex( float x, float y, float z );
+ GFX_API void ImmediateNormal( float x, float y, float z );
+ GFX_API void ImmediateColor( float r, float g, float b, float a );
+ GFX_API void ImmediateTexCoordAll( float x, float y, float z );
+ GFX_API void ImmediateTexCoord( int unit, float x, float y, float z );
+ GFX_API void ImmediateBegin( GfxPrimitiveType type );
+ GFX_API void ImmediateEnd();
+
+ // Recording display lists
+ GFX_API bool BeginRecording();
+ GFX_API bool EndRecording( GfxDisplayList** outDisplayList );
+
+ // Capturing screen shots / blits
+ GFX_API bool CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 );
+ GFX_API bool ReadbackImage( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY );
+ GFX_API void GrabIntoRenderTexture (RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height);
+
+ // Any housekeeping around draw calls
+ GFX_API void BeforeDrawCall( bool immediateMode );
+
+ GFX_API bool IsPositionRequiredForTexGen (int texStageIndex) const;
+ GFX_API bool IsNormalRequiredForTexGen (int texStageIndex) const;
+ GFX_API bool IsPositionRequiredForTexGen() const;
+ GFX_API bool IsNormalRequiredForTexGen() const;
+
+ GFX_API void SetActiveContext (void* ctx);
+
+ GFX_API void ResetFrameStats();
+ GFX_API void BeginFrameStats();
+ GFX_API void EndFrameStats();
+ GFX_API void SaveDrawStats();
+ GFX_API void RestoreDrawStats();
+ GFX_API void SynchronizeStats();
+
+ GFX_API void* GetNativeGfxDevice();
+ GFX_API void* GetNativeTexturePointer(TextureID id);
+ GFX_API UInt32 GetNativeTextureID(TextureID id);
+#if ENABLE_TEXTUREID_MAP
+ GFX_API intptr_t CreateExternalTextureFromNative(intptr_t nativeTex);
+ GFX_API void UpdateExternalTextureFromNative(TextureID tex, intptr_t nativeTex);
+#endif
+
+ GFX_API void InsertCustomMarker (int marker);
+
+ GFX_API void SetComputeBufferData (ComputeBufferID bufferHandle, const void* data, size_t size);
+ GFX_API void GetComputeBufferData (ComputeBufferID bufferHandle, void* dest, size_t destSize);
+ GFX_API void CopyComputeBufferCount (ComputeBufferID srcBuffer, ComputeBufferID dstBuffer, UInt32 dstOffset);
+
+ GFX_API void SetRandomWriteTargetTexture (int index, TextureID tid);
+ GFX_API void SetRandomWriteTargetBuffer (int index, ComputeBufferID bufferHandle);
+ GFX_API void ClearRandomWriteTargets ();
+
+ GFX_API ComputeProgramHandle CreateComputeProgram (const UInt8* code, size_t codeSize);
+ GFX_API void DestroyComputeProgram (ComputeProgramHandle& cpHandle);
+ GFX_API void CreateComputeConstantBuffers (unsigned count, const UInt32* sizes, ConstantBufferHandle* outCBs);
+ GFX_API void DestroyComputeConstantBuffers (unsigned count, ConstantBufferHandle* cbs);
+ GFX_API void CreateComputeBuffer (ComputeBufferID id, size_t count, size_t stride, UInt32 flags);
+ GFX_API void DestroyComputeBuffer (ComputeBufferID handle);
+ GFX_API void UpdateComputeConstantBuffers (unsigned count, ConstantBufferHandle* cbs, UInt32 cbDirty, size_t dataSize, const UInt8* data, const UInt32* cbSizes, const UInt32* cbOffsets, const int* bindPoints);
+ GFX_API void UpdateComputeResources (
+ unsigned texCount, const TextureID* textures, const int* texBindPoints,
+ unsigned samplerCount, const unsigned* samplers,
+ unsigned inBufferCount, const ComputeBufferID* inBuffers, const int* inBufferBindPoints,
+ unsigned outBufferCount, const ComputeBufferID* outBuffers, const TextureID* outTextures, const UInt32* outBufferBindPoints);
+ GFX_API void DispatchComputeProgram (ComputeProgramHandle cpHandle, unsigned threadsX, unsigned threadsY, unsigned threadsZ);
+
+ GFX_API void DrawNullGeometry (GfxPrimitiveType topology, int vertexCount, int instanceCount);
+ GFX_API void DrawNullGeometryIndirect (GfxPrimitiveType topology, ComputeBufferID bufferHandle, UInt32 bufferOffset);
+
+
+#if ENABLE_PROFILER
+ GFX_API void BeginProfileEvent (const char* name);
+ GFX_API void EndProfileEvent ();
+ GFX_API void ProfileControl (GfxProfileControl ctrl, unsigned param);
+ GFX_API GfxTimerQuery* CreateTimerQuery();
+ GFX_API void DeleteTimerQuery(GfxTimerQuery* query);
+ GFX_API void BeginTimerQueries();
+ GFX_API void EndTimerQueries();
+#endif
+
+ // Editor-only stuff
+#if UNITY_EDITOR
+ GFX_API void SetAntiAliasFlag( bool aa );
+ GFX_API void DrawUserPrimitives( GfxPrimitiveType type, int vertexCount, UInt32 vertexChannels, const void* data, int stride );
+ GFX_API int GetCurrentTargetAA() const;
+#endif
+
+#if UNITY_EDITOR && UNITY_WIN
+ //ToDo: This is windows specific code, we should replace HWND window with something more abstract
+ GFX_API GfxDeviceWindow* CreateGfxWindow( HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias );
+
+ void SetActiveWindow(ClientDeviceWindow* handle);
+ void WindowReshape(ClientDeviceWindow* handle, int width, int height, DepthBufferFormat depthFormat, int antiAlias);
+ void WindowDestroy(ClientDeviceWindow* handle);
+ void BeginRendering(ClientDeviceWindow* handle);
+ void EndRendering(ClientDeviceWindow* handle, bool presentContent);
+#endif
+
+#if UNITY_WIN
+ GFX_API int GetCurrentTargetWidth() const;
+ GFX_API int GetCurrentTargetHeight() const;
+ GFX_API void SetCurrentTargetSize(int width, int height);
+ GFX_API void SetCurrentWindowSize(int width, int height);
+#endif
+
+#if GFX_OPENGLESxx_ONLY || GFX_SUPPORTS_MOLEHILL
+ GFX_API void ReloadResources() {};
+#endif
+
+ bool IsThreaded() const { return m_Threaded; }
+ bool IsSerializing() const { return m_Serialize; }
+
+ ThreadedStreamBuffer* GetCommandQueue() const { return m_CommandQueue; }
+ GfxDeviceWorker* GetGfxDeviceWorker() const { return m_DeviceWorker; }
+
+ void SetRealGfxDevice(GfxThreadableDevice* realDevice);
+ void WriteBufferData(const void* data, int size);
+ void ReadbackData(dynamic_array<UInt8>& data, const SInt32 chunkSize = 16384);
+ void SubmitCommands();
+ void DoLockstep();
+
+ void QueryGraphicsCaps ();
+
+ GFX_API RenderTextureFormat GetDefaultRTFormat() const;
+ GFX_API RenderTextureFormat GetDefaultHDRRTFormat() const;
+
+private:
+ friend class ThreadedDisplayList;
+ void UpdateFogDisabled();
+ void UpdateFogEnabled(const GfxFogParams& fogParams);
+ void UpdateShadersActive(bool shadersActive[kShaderTypeCount]);
+
+private:
+ void CreateShaderParameters( ShaderLab::SubProgram* program, FogMode fogMode );
+
+ void CheckMainThread() const;
+ void BeforeRenderTargetChange(int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle);
+ void AfterRenderTargetChange();
+ void WaitForPendingPresent();
+ void WaitForSignal();
+
+ typedef std::map< GfxBlendState, ClientDeviceBlendState, memcmp_less<GfxBlendState> > CachedBlendStates;
+ typedef std::map< GfxDepthState, ClientDeviceDepthState, memcmp_less<GfxDepthState> > CachedDepthStates;
+ typedef std::map< GfxStencilState, ClientDeviceStencilState, memcmp_less<GfxStencilState> > CachedStencilStates;
+ typedef std::map< GfxRasterState, ClientDeviceRasterState, memcmp_less<GfxRasterState> > CachedRasterStates;
+
+ GfxDeviceWorker* m_DeviceWorker;
+ GfxThreadableDevice* m_RealDevice;
+ bool m_Threaded;
+ bool m_Serialize;
+ int m_RecordDepth;
+ int m_MaxCallDepth;
+ ThreadedStreamBuffer* m_CommandQueue;
+ DisplayListContext* m_DisplayListStack;
+ DisplayListContext* m_CurrentContext;
+ DynamicVBO* m_DynamicVBO;
+ bool m_InvertProjectionMatrix;
+ #if GFX_USES_VIEWPORT_OFFSET
+ Vector2f m_ViewportOffset;
+ #endif
+ CachedBlendStates m_CachedBlendStates;
+ CachedDepthStates m_CachedDepthStates;
+ CachedStencilStates m_CachedStencilStates;
+ CachedRasterStates m_CachedRasterStates;
+ TransformState m_TransformState;
+ ClientDeviceRect m_Viewport;
+ ClientDeviceRect m_ScissorRect;
+ int m_ScissorEnabled;
+ RenderSurfaceHandle m_ActiveRenderColorSurfaces[kMaxSupportedRenderTargets];
+ RenderSurfaceHandle m_ActiveRenderDepthSurface;
+ int m_CurrentTargetWidth;
+ int m_CurrentTargetHeight;
+ int m_CurrentWindowWidth;
+ int m_CurrentWindowHeight;
+ int m_ThreadOwnershipCount;
+ UInt32 m_CurrentCPUFence;
+ UInt32 m_PresentFrameID;
+ bool m_PresentPending;
+ bool m_Wireframe;
+ bool m_sRGBWrite;
+ dynamic_array<UInt8> m_ReadbackData;
+
+#if GFX_DEVICE_CLIENT_TRACK_TEXGEN
+ // TEMP: we can actually grab this info from shader, but for now lets just workaround it with minimal impact
+ // anyway, we should kill ffp (pretty please)
+ // TODO: it might be needed on more platforms, but for now i care only about gles
+ UInt8 posForTexGen;
+ UInt8 nrmForTexGen;
+
+ void SetTexGen(int unit, TexGenMode mode);
+ void DropTexGen(int unit);
+#endif
+
+public:
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientIDMapper m_BlendStateMapper;
+ ClientIDMapper m_DepthStateMapper;
+ ClientIDMapper m_StencilStateMapper;
+ ClientIDMapper m_RasterStateMapper;
+ ClientIDMapper m_TextureCombinerMapper;
+ ClientIDMapper m_VBOMapper;
+ ClientIDMapper m_RenderSurfaceMapper;
+ ClientIDMapper m_GpuProgramParametersMapper;
+#endif
+};
+
+#endif
diff --git a/Runtime/GfxDevice/threaded/GfxDeviceWorker.cpp b/Runtime/GfxDevice/threaded/GfxDeviceWorker.cpp
new file mode 100644
index 0000000..3534e7c
--- /dev/null
+++ b/Runtime/GfxDevice/threaded/GfxDeviceWorker.cpp
@@ -0,0 +1,2161 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_MULTITHREADED_CODE
+
+#include "Runtime/GfxDevice/threaded/GfxDeviceWorker.h"
+#include "Runtime/GfxDevice/threaded/GfxCommands.h"
+#include "Runtime/GfxDevice/threaded/GfxReturnStructs.h"
+#include "Runtime/GfxDevice/threaded/ThreadedDeviceStates.h"
+#include "Runtime/Threads/ThreadedStreamBuffer.h"
+#include "Runtime/Shaders/MaterialProperties.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/Semaphore.h"
+#include "Runtime/Threads/ThreadUtility.h"
+#include "Runtime/GfxDevice/GfxTimerQuery.h"
+#include "Runtime/GfxDevice/GfxDeviceSetup.h"
+#include "Runtime/GfxDevice/GPUSkinningInfo.h"
+#include "Runtime/GfxDevice/threaded/ThreadedWindow.h"
+#include "Runtime/GfxDevice/threaded/ThreadedDisplayList.h"
+#include "Runtime/Filters/Mesh/MeshSkinning.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "External/shaderlab/Library/program.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+
+#if GFXDEVICE_USE_CACHED_STATE
+ #define CHECK_CACHED_STATE(x) if (x)
+ #define SET_CACHED_STATE(dst, src) dst = src;
+#else
+ #define CHECK_CACHED_STATE(x)
+ #define SET_CACHED_STATE(dst, src)
+#endif
+
+#if UNITY_XENON
+ #include "PlatformDependent/Xbox360/Source/Services/VideoPlayer.h"
+ #include "PlatformDependent/Xbox360/Source/GfxDevice/GfxXenonVBO.h"
+ #include "PlatformDependent/Xbox360/Source/GfxDevice/GfxDeviceXenon.h"
+ #define GFX_DEVICE_WORKER_PROCESSOR 2
+#else
+ #define GFX_DEVICE_WORKER_PROCESSOR DEFAULT_UNITY_THREAD_PROCESSOR
+#endif
+
+PROFILER_INFORMATION(gMTDrawProf, "Gfx.Draw", kProfilerRender)
+PROFILER_INFORMATION(gMTDrawDynamicProf, "Gfx.DrawDynamic", kProfilerRender)
+PROFILER_INFORMATION(gMTDrawStaticBatch, "Gfx.DrawStaticBatch", kProfilerRender)
+PROFILER_INFORMATION(gMTDrawDynamicBatch, "Gfx.DrawDynamicBatch", kProfilerRender)
+
+PROFILER_INFORMATION(gMTSetRT, "Gfx.SetRenderTarget", kProfilerRender)
+PROFILER_INFORMATION(gMTPresentFrame, "Gfx.PresentFrame", kProfilerRender)
+PROFILER_INFORMATION(gMTBeginQueriesProf, "GPUProfiler.BeginQueries", kProfilerOverhead)
+PROFILER_INFORMATION(gMTEndQueriesProf, "GPUProfiler.EndQueries", kProfilerOverhead)
+
+
+#if GFXDEVICE_USE_CACHED_STATE
+GfxDeviceWorker::CachedState::CachedState()
+{
+ normalization = kNormalizationUnknown;
+ backface = -1;
+ ambient = Vector4f(-1, -1, -1, -1);
+ fogEnabled = -1;
+ fogParams.Invalidate();
+}
+#endif
+
+GfxDeviceWorker::GfxDeviceWorker(int maxCallDepth, ThreadedStreamBuffer* commandQueue) :
+ m_CallDepth(0),
+ m_MaxCallDepth(maxCallDepth),
+ m_WorkerThread(NULL),
+ m_CommandQueue(commandQueue),
+ m_MainCommandQueue(commandQueue),
+ m_CurrentCPUFence(0),
+ m_PresentFrameID(0),
+ m_IsThreadOwner(true),
+ m_Quit(false)
+{
+ m_FrameStats.ResetFrame();
+ m_PlaybackCommandQueues = new ThreadedStreamBuffer[maxCallDepth];
+ m_PlaybackDisplayLists = new ThreadedDisplayList*[maxCallDepth];
+ memset(m_PlaybackDisplayLists, 0, sizeof(m_PlaybackDisplayLists[0]) * maxCallDepth);
+ // Event starts signaled so it doesn't block immediately
+ SignalEvent(kEventTypeTimerQueries);
+}
+
+GfxDeviceWorker::~GfxDeviceWorker()
+{
+ if (m_WorkerThread)
+ {
+ m_WorkerThread->WaitForExit();
+ delete m_WorkerThread;
+ }
+ SetRealGfxDeviceThreadOwnership();
+ DestroyRealGfxDevice();
+ delete[] m_PlaybackCommandQueues;
+ delete[] m_PlaybackDisplayLists;
+}
+
+
+GfxThreadableDevice* GfxDeviceWorker::Startup(GfxDeviceRenderer renderer, bool threaded, bool forceRef)
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+ Assert (IsThreadableGfxDevice(renderer));
+
+ // Create actual device
+ GfxDevice* dev = CreateRealGfxDevice (renderer, forceRef);
+ if (!dev)
+ return NULL;
+
+ m_Device = static_cast<GfxThreadableDevice*>(dev);
+ SetRealGfxDevice(dev);
+
+ // Create worker thread
+ if (threaded)
+ {
+ m_WorkerThread = new Thread();
+ m_WorkerThread->SetName ("UnityGfxDeviceWorker");
+ m_Device->ReleaseThreadOwnership();
+ m_WorkerThread->Run(GfxDeviceWorker::RunGfxDeviceWorker, this, DEFAULT_UNITY_THREAD_STACK_SIZE, GFX_DEVICE_WORKER_PROCESSOR);
+
+ // In D3D11 we don't want to block on Present(), instead we block on IsReadyToBeginFrame()
+ if (renderer == kGfxRendererD3D11)
+ SignalEvent(kEventTypePresent);
+ }
+
+ return m_Device;
+#else
+ return NULL;
+#endif
+}
+
+void GfxDeviceWorker::Signal()
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_WaitSemaphore.Signal();
+#else
+ UnityMemoryBarrier();
+#endif
+}
+
+void GfxDeviceWorker::WaitForSignal()
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_WaitSemaphore.WaitForSignal();
+#endif
+}
+
+void GfxDeviceWorker::LockstepWait()
+{
+ m_LockstepSemaphore.WaitForSignal();
+}
+
+void GfxDeviceWorker::GetLastFrameStats(GfxDeviceStats& stats)
+{
+ Mutex::AutoLock lock(m_StatsMutex);
+ stats.CopyAllDrawStats(m_FrameStats);
+}
+
+void GfxDeviceWorker::CallImmediate(ThreadedDisplayList* dlist)
+{
+ Assert(m_CallDepth == 0);
+ dlist->AddRef();
+ m_PlaybackDisplayLists[m_CallDepth] = dlist;
+ m_CommandQueue = &m_PlaybackCommandQueues[m_CallDepth];
+ m_CommandQueue->CreateReadOnly(dlist->GetData(), dlist->GetSize());
+ m_CallDepth++;
+ while (m_CallDepth > 0)
+ {
+ RunCommand(*m_CommandQueue);
+ }
+}
+
+void GfxDeviceWorker::WaitForEvent(EventType type)
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_EventSemaphores[type].WaitForSignal();
+#endif
+}
+
+void GfxDeviceWorker::SignalEvent(EventType type)
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_EventSemaphores[type].Signal();
+#endif
+}
+
+void GfxDeviceWorker::WaitOnCPUFence(UInt32 fence)
+{
+ while (SInt32(fence - m_CurrentCPUFence) > 0)
+ {
+ Thread::Sleep(0.001);
+ }
+}
+
+bool GfxDeviceWorker::DidPresentFrame(UInt32 frameID) const
+{
+ return m_PresentFrameID == frameID;
+}
+
+void* GfxDeviceWorker::RunGfxDeviceWorker(void* data)
+{
+ #if ENABLE_PROFILER
+ profiler_initialize_thread ("Render Thread", true);
+ #endif
+
+ GfxDeviceWorker* worker = (GfxDeviceWorker*) data;
+ worker->m_Device->AcquireThreadOwnership();
+ SetRealGfxDeviceThreadOwnership();
+ worker->m_Device->OnDeviceCreated (true);
+
+ worker->Run();
+
+ #if ENABLE_PROFILER
+ profiler_cleanup_thread();
+ #endif
+
+ return NULL;
+}
+
+// Soft-toggle for spin-loop
+bool g_enableGfxDeviceWorkerSpinLoop = false;
+
+GfxCommand lastCmd;
+void GfxDeviceWorker::Run()
+{
+
+#define SPIN_WORKER_LOOP UNITY_XENON
+#if SPIN_WORKER_LOOP
+ while (!m_Quit)
+ {
+ if (g_enableGfxDeviceWorkerSpinLoop == false || m_CommandQueue->HasDataToRead())
+ {
+ RunCommand(*m_CommandQueue);
+ }
+ else
+ {
+ const bool presented = IsRealGfxDeviceThreadOwner() ? GetGfxDeviceX360().PresentFrameForTCR022() : false;
+ if (!presented)
+ Thread::Sleep(0.001);
+ }
+ }
+#else
+ while (!m_Quit)
+ {
+ RunCommand(*m_CommandQueue);
+ }
+#endif
+}
+
+bool GfxDeviceWorker::RunCommandIfDataIsAvailable()
+{
+ if (!m_CommandQueue->HasData())
+ return false;
+
+ RunCommand(*m_CommandQueue);
+ return true;
+}
+
+void GfxDeviceWorker::RunCommand(ThreadedStreamBuffer& stream)
+{
+#if DEBUG_GFXDEVICE_LOCKSTEP
+ size_t pos = stream.GetDebugReadPosition();
+#endif
+ GfxCommand cmd = stream.ReadValueType<GfxCommand>();
+ DebugAssert(m_IsThreadOwner || cmd == kGfxCmd_AcquireThreadOwnership);
+ switch (cmd)
+ {
+ case kGfxCmd_InvalidateState:
+ {
+ m_Device->InvalidateState();
+ break;
+ }
+#if GFX_DEVICE_VERIFY_ENABLE
+ case kGfxCmd_VerifyState:
+ {
+ m_Device->VerifyState();
+ break;
+ }
+#endif
+ case kGfxCmd_SetMaxBufferedFrames:
+ {
+ int bufferSize = stream.ReadValueType<int>();
+ m_Device->SetMaxBufferedFrames(bufferSize);
+ break;
+ }
+ case kGfxCmd_Clear:
+ {
+ const GfxCmdClear& clear = stream.ReadValueType<GfxCmdClear>();
+ m_Device->Clear(clear.clearFlags, clear.color.GetPtr(), clear.depth, clear.stencil);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_SetUserBackfaceMode:
+ {
+ bool enable = stream.ReadValueType<GfxCmdBool>();
+ m_Device->SetUserBackfaceMode(enable);
+ break;
+ }
+ case kGfxCmd_SetWireframe:
+ {
+ bool wire = stream.ReadValueType<GfxCmdBool>();
+ m_Device->SetWireframe(wire);
+ break;
+ }
+ case kGfxCmd_SetInvertProjectionMatrix:
+ {
+ bool enable = stream.ReadValueType<GfxCmdBool>();
+ m_Device->SetInvertProjectionMatrix(enable);
+ break;
+ }
+#if GFX_USES_VIEWPORT_OFFSET
+ case kGfxCmd_SetViewportOffset:
+ {
+ Vector2f ofs = stream.ReadValueType<Vector2f>();
+ m_Device->SetViewportOffset(ofs.x, ofs.y);
+ break;
+ }
+#endif
+ case kGfxCmd_CreateBlendState:
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceBlendState result = stream.ReadValueType<ClientDeviceBlendState>();
+ m_BlendStateMapper[result.internalState] = m_Device->CreateBlendState(result.sourceState);
+#else
+ ClientDeviceBlendState* result = stream.ReadValueType<ClientDeviceBlendState*>();
+ result->internalState = m_Device->CreateBlendState(result->sourceState);
+#endif
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_CreateDepthState:
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceDepthState result = stream.ReadValueType<ClientDeviceDepthState>();
+ m_DepthStateMapper[result.internalState] = m_Device->CreateDepthState(result.sourceState);
+#else
+ ClientDeviceDepthState* result = stream.ReadValueType<ClientDeviceDepthState*>();
+ result->internalState = m_Device->CreateDepthState(result->sourceState);
+#endif
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_CreateStencilState:
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceStencilState result = stream.ReadValueType<ClientDeviceStencilState>();
+ m_StencilStateMapper[result.internalState] = m_Device->CreateStencilState(result.sourceState);
+#else
+ ClientDeviceStencilState* result = stream.ReadValueType<ClientDeviceStencilState*>();
+ result->internalState = m_Device->CreateStencilState(result->sourceState);
+#endif
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_CreateRasterState:
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceRasterState result = stream.ReadValueType<ClientDeviceRasterState>();
+ m_RasterStateMapper[result.internalState] = m_Device->CreateRasterState(result.sourceState);
+#else
+ ClientDeviceRasterState* result = stream.ReadValueType<ClientDeviceRasterState*>();
+ result->internalState = m_Device->CreateRasterState(result->sourceState);
+#endif
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_SetBlendState:
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ const ClientDeviceBlendState result = stream.ReadValueType<const ClientDeviceBlendState>();
+ float alphaRef = stream.ReadValueType<float>();
+ m_Device->SetBlendState(m_BlendStateMapper[result.internalState], alphaRef);
+#else
+ const ClientDeviceBlendState* result = stream.ReadValueType<const ClientDeviceBlendState*>();
+ float alphaRef = stream.ReadValueType<float>();
+ m_Device->SetBlendState(result->internalState, alphaRef);
+#endif
+ break;
+ }
+ case kGfxCmd_SetDepthState:
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ const ClientDeviceDepthState result = stream.ReadValueType<const ClientDeviceDepthState>();
+ m_Device->SetDepthState(m_DepthStateMapper[result.internalState]);
+#else
+ const ClientDeviceDepthState* result = stream.ReadValueType<const ClientDeviceDepthState*>();
+ m_Device->SetDepthState(result->internalState);
+#endif
+ break;
+ }
+ case kGfxCmd_SetStencilState:
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ const ClientDeviceStencilState result = stream.ReadValueType<const ClientDeviceStencilState>();
+ int stencilRef = stream.ReadValueType<int>();
+ m_Device->SetStencilState(m_StencilStateMapper[result.internalState], stencilRef);
+#else
+ const ClientDeviceStencilState* result = stream.ReadValueType<const ClientDeviceStencilState*>();
+ int stencilRef = stream.ReadValueType<int>();
+ m_Device->SetStencilState(result->internalState, stencilRef);
+#endif
+ break;
+ }
+ case kGfxCmd_SetRasterState:
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ const ClientDeviceRasterState result = stream.ReadValueType<const ClientDeviceRasterState>();
+ m_Device->SetRasterState(m_RasterStateMapper[result.internalState]);
+#else
+ const ClientDeviceRasterState* result = stream.ReadValueType<const ClientDeviceRasterState*>();
+ m_Device->SetRasterState(result->internalState);
+#endif
+ break;
+ }
+ case kGfxCmd_SetSRGBState:
+ {
+ bool enable = stream.ReadValueType<GfxCmdBool>();
+ m_Device->SetSRGBWrite(enable);
+ break;
+ }
+ case kGfxCmd_SetWorldMatrix:
+ {
+ const Matrix4x4f& matrix = stream.ReadValueType<Matrix4x4f>();
+ m_Device->SetWorldMatrix(matrix.GetPtr());
+ break;
+ }
+ case kGfxCmd_SetViewMatrix:
+ {
+ const Matrix4x4f& matrix = stream.ReadValueType<Matrix4x4f>();
+ m_Device->SetViewMatrix(matrix.GetPtr());
+ break;
+ }
+ case kGfxCmd_SetProjectionMatrix:
+ {
+ const Matrix4x4f& matrix = stream.ReadValueType<Matrix4x4f>();
+ m_Device->SetProjectionMatrix(matrix);
+ break;
+ }
+ case kGfxCmd_SetInverseScale:
+ {
+ float invScale = stream.ReadValueType<float>();
+ m_Device->SetInverseScale(invScale);
+ break;
+ }
+ case kGfxCmd_SetNormalizationBackface:
+ {
+ const GfxCmdSetNormalizationBackface& data = stream.ReadValueType<GfxCmdSetNormalizationBackface>();
+ CHECK_CACHED_STATE(data.mode != m_Cached.normalization || int(data.backface) != m_Cached.backface)
+ {
+ m_Device->SetNormalizationBackface(data.mode, data.backface);
+ SET_CACHED_STATE(m_Cached.normalization, data.mode);
+ SET_CACHED_STATE(m_Cached.backface, data.backface);
+ }
+ break;
+ }
+ case kGfxCmd_SetFFLighting:
+ {
+ const GfxCmdSetFFLighting& data = stream.ReadValueType<GfxCmdSetFFLighting>();
+ m_Device->SetFFLighting(data.on, data.separateSpecular, data.colorMaterial);
+ break;
+ }
+ case kGfxCmd_SetMaterial:
+ {
+ const GfxMaterialParams& mat = stream.ReadValueType<GfxMaterialParams>();
+ m_Device->SetMaterial(mat.ambient.GetPtr(), mat.diffuse.GetPtr(), mat.specular.GetPtr(), mat.emissive.GetPtr(), mat.shininess);
+ break;
+ }
+ case kGfxCmd_SetColor:
+ {
+ const Vector4f& color = stream.ReadValueType<Vector4f>();
+ m_Device->SetColor(color.GetPtr());
+ break;
+ }
+ case kGfxCmd_SetViewport:
+ {
+ const ClientDeviceRect& rect = stream.ReadValueType<ClientDeviceRect>();
+ m_Device->SetViewport(rect.x, rect.y, rect.width, rect.height);
+ break;
+ }
+ case kGfxCmd_SetScissorRect:
+ {
+ const ClientDeviceRect& rect = stream.ReadValueType<ClientDeviceRect>();
+ m_Device->SetScissorRect(rect.x, rect.y, rect.width, rect.height);
+ break;
+ }
+ case kGfxCmd_DisableScissor:
+ {
+ m_Device->DisableScissor();
+ break;
+ }
+ case kGfxCmd_CreateTextureCombiners:
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceTextureCombiners result;
+ result.internalHandle = stream.ReadValueType<ClientIDMapper::ClientID>();
+ result.count = stream.ReadValueType<int>();
+ result.bindings = new ShaderLab::TextureBinding[result.count];
+ for (int i = 0; i < result.count; i++)
+ result.bindings[i] = stream.ReadValueType<ShaderLab::TextureBinding>();
+
+ const GfxCmdCreateTextureCombiners& param = stream.ReadValueType<GfxCmdCreateTextureCombiners>();
+ m_TextureCombinerMapper[result.internalHandle] = m_Device->CreateTextureCombiners(param.count, result.bindings, NULL, param.hasVertexColorOrLighting, param.usesAddSpecular).object;
+#else
+ ClientDeviceTextureCombiners* result = stream.ReadValueType<ClientDeviceTextureCombiners*>();
+ const GfxCmdCreateTextureCombiners& param = stream.ReadValueType<GfxCmdCreateTextureCombiners>();
+ result->internalHandle = m_Device->CreateTextureCombiners(param.count, result->bindings, NULL, param.hasVertexColorOrLighting, param.usesAddSpecular);
+#endif
+ break;
+ }
+ case kGfxCmd_DeleteTextureCombiners:
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceTextureCombiners combiners;
+ combiners.internalHandle = stream.ReadValueType<ClientIDMapper::ClientID>();
+ combiners.count = stream.ReadValueType<int>();
+ TextureCombinersHandle handle(m_TextureCombinerMapper[combiners.internalHandle]);
+ m_Device->DeleteTextureCombiners(handle);
+#else
+ ClientDeviceTextureCombiners* combiners = stream.ReadValueType<ClientDeviceTextureCombiners*>();
+ m_Device->DeleteTextureCombiners(combiners->internalHandle);
+ for(int i = 0; i < combiners->count; i++) combiners->bindings[i].~TextureBinding();
+ UNITY_FREE(kMemGfxThread,combiners->bindings);
+ UNITY_DELETE(combiners, kMemGfxThread);
+#endif
+ break;
+ }
+ case kGfxCmd_SetTextureCombiners:
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceTextureCombiners combiners;
+ combiners.internalHandle = stream.ReadValueType<ClientIDMapper::ClientID>();
+ combiners.count = stream.ReadValueType<int>();
+ int count = combiners.count;
+#else
+ ClientDeviceTextureCombiners* combiners = stream.ReadValueType<ClientDeviceTextureCombiners*>();
+ int count = combiners->count;
+#endif
+ const void* data = m_CommandQueue->GetReadDataPointer(count * sizeof(TexEnvData), ALIGN_OF(TexEnvData));
+ const TexEnvData* texEnvData = static_cast<const TexEnvData*>(data);
+ data = m_CommandQueue->GetReadDataPointer(count * sizeof(Vector4f), ALIGN_OF(Vector4f));
+ const Vector4f* texColors = static_cast<const Vector4f*>(data);
+ // CreateTextureCombiners() might have failed on render thread, unknownst to main thread (case 435703)
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ if (combiners.internalHandle)
+ m_Device->SetTextureCombinersThreadable(TextureCombinersHandle(m_TextureCombinerMapper[combiners.internalHandle]), texEnvData, texColors);
+#else
+ if (combiners->internalHandle.IsValid())
+ m_Device->SetTextureCombinersThreadable(combiners->internalHandle, texEnvData, texColors);
+#endif
+ break;
+ }
+ case kGfxCmd_SetTexture:
+ {
+ const GfxCmdSetTexture& tex = stream.ReadValueType<GfxCmdSetTexture>();
+ m_Device->SetTexture(tex.shaderType, tex.unit, tex.samplerUnit, tex.texture, tex.dim, tex.bias);
+ break;
+ }
+ case kGfxCmd_SetTextureParams:
+ {
+ const GfxCmdSetTextureParams& params = stream.ReadValueType<GfxCmdSetTextureParams>();
+ m_Device->SetTextureParams( params.texture, params.texDim, params.filter, params.wrap, params.anisoLevel, params.hasMipMap, params.colorSpace);
+ m_CommandQueue->ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_SetTextureTransform:
+ {
+ const GfxCmdSetTextureTransform& trans = stream.ReadValueType<GfxCmdSetTextureTransform>();
+ m_Device->SetTextureTransform(trans.unit, trans.dim, trans.texGen, trans.identity, trans.matrix.GetPtr());
+ break;
+ }
+ case kGfxCmd_SetTextureName:
+ {
+ const GfxCmdSetTextureName& texName = stream.ReadValueType<GfxCmdSetTextureName>();
+ char* name = m_CommandQueue->ReadArrayType<char>(texName.nameLength);
+ m_Device->SetTextureName( texName.texture, name );
+ break;
+ }
+ case kGfxCmd_SetMaterialProperties:
+ {
+ typedef MaterialPropertyBlock::Property Property;
+ GfxCmdSetMaterialProperties matprops = m_CommandQueue->ReadValueType<GfxCmdSetMaterialProperties>();
+ Property* props = m_CommandQueue->ReadArrayType<Property>(matprops.propertyCount);
+ float* buffer = m_CommandQueue->ReadArrayType<float>(matprops.bufferSize);
+ MaterialPropertyBlock block(props, matprops.propertyCount, buffer, matprops.bufferSize);
+ m_Device->SetMaterialProperties(block);
+ break;
+ }
+ case kGfxCmd_CreateGpuProgram:
+ {
+ #if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ GfxCmdCreateGpuProgram gpuprog = m_CommandQueue->ReadValueType<GfxCmdCreateGpuProgram>();
+ *gpuprog.result = m_Device->CreateGpuProgram(gpuprog.source, *gpuprog.output);
+ UnityMemoryBarrier();
+ m_WaitSemaphore.Signal();
+ m_CommandQueue->ReadReleaseData();
+ #else
+ size_t len = m_CommandQueue->ReadValueType<UInt32>();
+ const char* source = m_CommandQueue->ReadArrayType<char>(len + 1);
+ CreateGpuProgramOutput output;
+ GpuProgram* program = m_Device->CreateGpuProgram(source, output);
+ Assert(program);
+ // Allocate space for struct, append additional data
+ m_TempBuffer.resize_uninitialized(sizeof(GfxRet_CreateGpuProgram));
+ GfxRet_CreateGpuProgram retStruct(output, m_TempBuffer);
+ retStruct.gpuProgram = m_GpuProgramClientMapper.CreateID();
+ m_GpuProgramMapper[retStruct.gpuProgram] = program;
+ // Copy struct to start of buffer
+ *reinterpret_cast<GfxRet_CreateGpuProgram*>(m_TempBuffer.data()) = retStruct;
+ WritebackData(stream, m_TempBuffer.data(), m_TempBuffer.size());
+ #endif
+ break;
+ }
+ case kGfxCmd_SetShaders:
+ {
+ GfxCmdSetShaders shaders = stream.ReadValueType<GfxCmdSetShaders>();
+ UInt8* paramsBuffer[kShaderTypeCount];
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ const GpuProgramParameters* params[kShaderTypeCount];
+ GpuProgram* programs[kShaderTypeCount];
+#endif
+ for (int pt = 0; pt < kShaderTypeCount; ++pt)
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ params[pt] = m_GpuProgramParametersMapper[shaders.params[pt]];
+ programs[pt] = m_GpuProgramMapper[shaders.programs[pt]];
+#endif
+
+ paramsBuffer[pt] = NULL;
+ if (shaders.paramsBufferSize[pt] > 0)
+ {
+ void* buffer = m_CommandQueue->GetReadDataPointer (shaders.paramsBufferSize[pt], 1);
+ paramsBuffer[pt] = static_cast<UInt8*>(buffer);
+ }
+ }
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_Device->SetShadersThreadable (programs, params, paramsBuffer);
+#else
+ m_Device->SetShadersThreadable (shaders.programs, shaders.params, paramsBuffer);
+#endif
+ break;
+ }
+ case kGfxCmd_CreateShaderParameters:
+ {
+ GfxCmdCreateShaderParameters params = stream.ReadValueType<GfxCmdCreateShaderParameters>();
+ m_Device->CreateShaderParameters(params.program, params.fogMode);
+ Signal();
+ break;
+ }
+ case kGfxCmd_DestroySubProgram:
+ {
+ ShaderLab::SubProgram* subprogram = stream.ReadValueType<ShaderLab::SubProgram*>();
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_Device->DestroySubProgram(subprogram);
+#else
+ printf_console("fix ShaderLab::SubProgram\n");
+#endif
+ break;
+ }
+ case kGfxCmd_SetConstantBufferInfo:
+ {
+ int id = stream.ReadValueType<int>();
+ int size = stream.ReadValueType<int>();
+ m_Device->SetConstantBufferInfo (id, size);
+ break;
+ }
+ case kGfxCmd_DisableLights:
+ {
+ int startLight = stream.ReadValueType<int>();
+ m_Device->DisableLights(startLight);
+ break;
+ }
+ case kGfxCmd_SetLight:
+ {
+ int light = stream.ReadValueType<int>();
+ const GfxVertexLight& lightdata = stream.ReadValueType<GfxVertexLight>();
+ m_Device->SetLight(light, lightdata);
+ break;
+ }
+ case kGfxCmd_SetAmbient:
+ {
+ const Vector4f& ambient = stream.ReadValueType<Vector4f>();
+ CHECK_CACHED_STATE(!CompareMemory(ambient, m_Cached.ambient))
+ {
+ m_Device->SetAmbient(ambient.GetPtr());
+ SET_CACHED_STATE(m_Cached.ambient, ambient);
+ }
+ break;
+ }
+ case kGfxCmd_EnableFog:
+ {
+ const GfxFogParams& fog = stream.ReadValueType<GfxFogParams>();
+ CHECK_CACHED_STATE(m_Cached.fogEnabled != 1 || !CompareMemory(fog, m_Cached.fogParams))
+ {
+ m_Device->EnableFog(fog);
+ SET_CACHED_STATE(m_Cached.fogEnabled, 1);
+ SET_CACHED_STATE(m_Cached.fogParams, fog);
+ }
+ break;
+ }
+ case kGfxCmd_DisableFog:
+ {
+ CHECK_CACHED_STATE(m_Cached.fogEnabled != 0)
+ {
+ m_Device->DisableFog();
+ SET_CACHED_STATE(m_Cached.fogEnabled, 0);
+ }
+ break;
+ }
+ case kGfxCmd_BeginSkinning:
+ {
+ int maxSkinCount = stream.ReadValueType<int>();
+ m_SkinJobGroup = GetJobScheduler().BeginGroup(maxSkinCount);
+ m_ActiveSkins.reserve(maxSkinCount);
+ break;
+ }
+ case kGfxCmd_SkinMesh:
+ {
+ // Array must be preallocated to at least the right size
+ Assert(m_ActiveSkins.size() < m_ActiveSkins.capacity());
+ int skinIndex = m_ActiveSkins.size();
+ m_ActiveSkins.resize_uninitialized(skinIndex + 1);
+ SkinMeshInfo& skin = m_ActiveSkins[skinIndex];
+ skin = stream.ReadValueType<SkinMeshInfo>();
+ ClientDeviceVBO* vbo = stream.ReadValueType<ClientDeviceVBO*>();
+ stream.ReadReleaseData();
+
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ VertexStreamData mappedVSD;
+ if (vbo->GetInternal()->MapVertexStream(mappedVSD, 0))
+ {
+ skin.outVertices = mappedVSD.buffer;
+ GetJobScheduler().SubmitJob(m_SkinJobGroup, DeformSkinnedMeshJob, &skin, NULL);
+ m_MappedSkinVBOs.push_back(vbo->GetInternal());
+ }
+#endif
+ break;
+ }
+ case kGfxCmd_EndSkinning:
+ {
+ GetJobScheduler().WaitForGroup(m_SkinJobGroup);
+ for (int i = 0; i < m_ActiveSkins.size(); i++)
+ {
+ m_ActiveSkins[i].Release();
+ }
+ m_ActiveSkins.resize_uninitialized(0);
+ for (int i = 0; i < m_MappedSkinVBOs.size(); i++)
+ {
+ m_MappedSkinVBOs[i]->UnmapVertexStream(0);
+ }
+ m_MappedSkinVBOs.resize_uninitialized(0);
+ break;
+ }
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+ case kGfxCmd_BeginStaticBatching:
+ {
+ PROFILER_BEGIN(gMTDrawStaticBatch, NULL);
+ const GfxCmdBeginStaticBatching& dynbat = stream.ReadValueType<GfxCmdBeginStaticBatching>();
+ m_Device->BeginStaticBatching(dynbat.channels, dynbat.topology);
+ break;
+ }
+ case kGfxCmd_StaticBatchMesh:
+ {
+ const GfxCmdStaticBatchMesh& batch = stream.ReadValueType<GfxCmdStaticBatchMesh>();
+ m_Device->StaticBatchMesh(batch.firstVertex, batch.vertexCount,
+ batch.indices, batch.firstIndexByte, batch.indexCount);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_EndStaticBatching:
+ {
+ const GfxCmdEndStaticBatching& endbat = stream.ReadValueType<GfxCmdEndStaticBatching>();
+ m_Device->EndStaticBatching(*endbat.vbo->GetInternal(), endbat.matrix, endbat.transformType, endbat.sourceChannels);
+ stream.ReadReleaseData();
+ PROFILER_END;
+ break;
+ }
+ case kGfxCmd_BeginDynamicBatching:
+ {
+ PROFILER_BEGIN(gMTDrawDynamicBatch, NULL);
+ const GfxCmdBeginDynamicBatching& dynbat = stream.ReadValueType<GfxCmdBeginDynamicBatching>();
+ m_Device->BeginDynamicBatching(dynbat.shaderChannels, dynbat.channelsInVBO, dynbat.maxVertices, dynbat.maxIndices, dynbat.topology);
+ break;
+ }
+ case kGfxCmd_DynamicBatchMesh:
+ {
+ const GfxCmdDynamicBatchMesh& batch = stream.ReadValueType<GfxCmdDynamicBatchMesh>();
+ m_Device->DynamicBatchMesh(batch.matrix, batch.vertices, batch.firstVertex, batch.vertexCount,
+ batch.indices, batch.firstIndexByte, batch.indexCount);
+ stream.ReadReleaseData();
+ break;
+ }
+#if ENABLE_SPRITES
+ case kGfxCmd_DynamicBatchSprite:
+ {
+ const GfxCmdDynamicBatchSprite& batch = stream.ReadValueType<GfxCmdDynamicBatchSprite>();
+ m_Device->DynamicBatchSprite(&batch.matrix, batch.sprite, batch.color);
+ stream.ReadReleaseData();
+ break;
+ }
+#endif
+ case kGfxCmd_EndDynamicBatching:
+ {
+ const GfxCmdEndDynamicBatching& endbat = stream.ReadValueType<GfxCmdEndDynamicBatching>();
+ m_Device->EndDynamicBatching(endbat.transformType);
+ stream.ReadReleaseData();
+ PROFILER_END;
+ break;
+ }
+#endif
+ case kGfxCmd_AddBatchingStats:
+ {
+ const GfxCmdAddBatchingStats& stats = stream.ReadValueType<GfxCmdAddBatchingStats>();
+ m_Device->AddBatchingStats(stats.batchedTris, stats.batchedVerts, stats.batchedCalls);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_CreateRenderColorSurface:
+ {
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceRenderSurface* handle = stream.ReadValueType<ClientDeviceRenderSurface*>();
+#else
+ ClientDeviceRenderSurface handle = stream.ReadValueType<ClientDeviceRenderSurface>();
+#endif
+ const GfxCmdCreateRenderColorSurface& create = stream.ReadValueType<GfxCmdCreateRenderColorSurface>();
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ handle->internalHandle = m_Device->CreateRenderColorSurface(create.textureID, create.width, create.height, create.samples, create.depth, create.dim, create.format, create.createFlags);
+#else
+ m_RenderSurfaceMapper[handle.internalHandle] = m_Device->CreateRenderColorSurface(create.textureID, create.width, create.height, create.samples, create.depth, create.dim, create.format, create.createFlags).object;
+#endif
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_CreateRenderDepthSurface:
+ {
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceRenderSurface* handle = stream.ReadValueType<ClientDeviceRenderSurface*>();
+#else
+ ClientDeviceRenderSurface handle = stream.ReadValueType<ClientDeviceRenderSurface>();
+#endif
+ const GfxCmdCreateRenderDepthSurface& create = stream.ReadValueType<GfxCmdCreateRenderDepthSurface>();
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ handle->internalHandle = m_Device->CreateRenderDepthSurface(create.textureID, create.width, create.height, create.samples, create.dim, create.depthFormat, create.createFlags);
+#else
+ m_RenderSurfaceMapper[handle.internalHandle] = m_Device->CreateRenderDepthSurface(create.textureID, create.width, create.height, create.samples, create.dim, create.depthFormat, create.createFlags).object;
+#endif
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_DestroyRenderSurface:
+ {
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceRenderSurface* handle = stream.ReadValueType<ClientDeviceRenderSurface*>();
+ RenderSurfaceHandle rsHandle = handle->internalHandle;
+#else
+ ClientDeviceRenderSurface handle = stream.ReadValueType<ClientDeviceRenderSurface>();
+ RenderSurfaceHandle rsHandle(m_RenderSurfaceMapper[handle.internalHandle]);
+#endif
+ m_Device->DestroyRenderSurface(rsHandle);
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ delete handle;
+#endif
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_DiscardContents:
+ {
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceRenderSurface* handle = stream.ReadValueType<ClientDeviceRenderSurface*>();
+ RenderSurfaceHandle rsHandle = handle->internalHandle;
+#else
+ ClientDeviceRenderSurface handle = stream.ReadValueType<ClientDeviceRenderSurface>();
+ RenderSurfaceHandle rsHandle(m_RenderSurfaceMapper[handle.internalHandle]);
+#endif
+ m_Device->DiscardContents(rsHandle);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_SetRenderTarget:
+ {
+ PROFILER_AUTO(gMTSetRT, NULL);
+ const GfxCmdSetRenderTarget& srt = stream.ReadValueType<GfxCmdSetRenderTarget>();
+ RenderSurfaceHandle colorHandle[kMaxSupportedRenderTargets];
+ for (int i = 0; i < srt.colorCount; ++i)
+ {
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceRenderSurface* colorSurf = static_cast<ClientDeviceRenderSurface*>(srt.colorHandles[i].object);
+ colorHandle[i].object = colorSurf ? colorSurf->internalHandle.object : NULL;
+#else
+ colorHandle[i].object = m_RenderSurfaceMapper[srt.colorHandles[i]];
+#endif
+ // we cant access backbuffer handle in client device, so we write null
+ if(!colorHandle[i].IsValid())
+ colorHandle[i] = m_Device->GetBackBufferColorSurface();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceRenderSurface* depthSurf = (ClientDeviceRenderSurface*)srt.depthHandle.object;
+ RenderSurfaceHandle depthHandle(depthSurf ? depthSurf->internalHandle.object :NULL);
+#else
+ RenderSurfaceHandle depthHandle(m_RenderSurfaceMapper[srt.depthHandle]);
+#endif
+ // we cant access backbuffer handle in client device, so we write null
+ if(!depthHandle.IsValid())
+ depthHandle = m_Device->GetBackBufferDepthSurface();
+
+ m_Device->SetRenderTargets (srt.colorCount, colorHandle, depthHandle, srt.mipLevel, srt.face);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_SetRenderTargetWithFlags:
+ {
+ PROFILER_AUTO(gMTSetRT, NULL);
+ const GfxCmdSetRenderTarget& srt = stream.ReadValueType<GfxCmdSetRenderTarget>();
+ RenderSurfaceHandle colorHandle[kMaxSupportedRenderTargets];
+ for (int i = 0; i < srt.colorCount; ++i)
+ {
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceRenderSurface* colorSurf = static_cast<ClientDeviceRenderSurface*>(srt.colorHandles[i].object);
+ colorHandle[i].object = colorSurf ? colorSurf->internalHandle.object : NULL;
+#else
+ colorHandle[i].object = m_RenderSurfaceMapper[srt.colorHandles[i]];
+#endif
+ // we cant access backbuffer handle in client device, so we write null
+ if(!colorHandle[i].IsValid())
+ colorHandle[i] = m_Device->GetBackBufferColorSurface();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceRenderSurface* depthSurf = (ClientDeviceRenderSurface*)srt.depthHandle.object;
+ RenderSurfaceHandle depthHandle(depthSurf ? depthSurf->internalHandle.object :NULL);
+#else
+ RenderSurfaceHandle depthHandle(m_RenderSurfaceMapper[srt.depthHandle]);
+#endif
+ // we cant access backbuffer handle in client device, so we write null
+ if(!depthHandle.IsValid())
+ depthHandle = m_Device->GetBackBufferDepthSurface();
+
+ UInt32 flags = stream.ReadValueType<UInt32>();
+ m_Device->SetRenderTargets (srt.colorCount, colorHandle, depthHandle, srt.mipLevel, srt.face, flags);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_ResolveColorSurface:
+ {
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceRenderSurface* src = stream.ReadValueType<ClientDeviceRenderSurface*>();
+ ClientDeviceRenderSurface* dst = stream.ReadValueType<ClientDeviceRenderSurface*>();
+ m_Device->ResolveColorSurface (src->internalHandle, dst->internalHandle);
+#else
+ //todo
+#endif
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_ResolveDepthIntoTexture:
+ {
+ const GfxCmdResolveDepthIntoTexture resolve = stream.ReadValueType<GfxCmdResolveDepthIntoTexture>();
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceRenderSurface* colorSurf = (ClientDeviceRenderSurface*)resolve.colorHandle.object;
+ RenderSurfaceHandle colorHandle(colorSurf ? colorSurf->internalHandle.object : NULL);
+
+ ClientDeviceRenderSurface* depthSurf = (ClientDeviceRenderSurface*)resolve.depthHandle.object;
+ RenderSurfaceHandle depthHandle(depthSurf ? depthSurf->internalHandle.object : NULL);
+#else
+ RenderSurfaceHandle colorHandle(m_RenderSurfaceMapper[resolve.colorHandle]);
+ RenderSurfaceHandle depthHandle(m_RenderSurfaceMapper[resolve.depthHandle]);
+#endif
+ m_Device->ResolveDepthIntoTexture (colorHandle, depthHandle);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_SetSurfaceFlags:
+ {
+ const GfxCmdSetSurfaceFlags& sf = stream.ReadValueType<GfxCmdSetSurfaceFlags>();
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceRenderSurface* surface = (ClientDeviceRenderSurface*)sf.surf.object;
+ RenderSurfaceHandle handle = surface->internalHandle;
+#else
+ RenderSurfaceHandle handle(m_RenderSurfaceMapper[sf.surf]);
+#endif
+ m_Device->SetSurfaceFlags(handle, sf.flags, sf.keepFlags);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_UploadTexture2D:
+ {
+ // Must copy since we stream data
+ GfxCmdUploadTexture2D upload = stream.ReadValueType<GfxCmdUploadTexture2D>();
+ UInt8* srcData = ReadBufferData(stream, upload.srcSize);
+ m_Device->UploadTexture2D(upload.texture, upload.dimension, srcData, upload.srcSize,
+ upload.width, upload.height, upload.format, upload.mipCount, upload.uploadFlags,
+ upload.skipMipLevels, upload.usageMode, upload.colorSpace);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_UploadTextureSubData2D:
+ {
+ // Must copy since we stream data
+ GfxCmdUploadTextureSubData2D upload = stream.ReadValueType<GfxCmdUploadTextureSubData2D>();
+ UInt8* srcData = ReadBufferData(stream, upload.srcSize);
+ m_Device->UploadTextureSubData2D(upload.texture, srcData, upload.srcSize,
+ upload.mipLevel, upload.x, upload.y, upload.width, upload.height, upload.format, upload.colorSpace);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_UploadTextureCube:
+ {
+ // Must copy since we stream data
+ GfxCmdUploadTextureCube upload = stream.ReadValueType<GfxCmdUploadTextureCube>();
+ UInt8* srcData = ReadBufferData(stream, upload.srcSize);
+ m_Device->UploadTextureCube(upload.texture, srcData, upload.srcSize,
+ upload.faceDataSize, upload.size, upload.format, upload.mipCount, upload.uploadFlags, upload.colorSpace);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_UploadTexture3D:
+ {
+ // Must copy since we stream data
+ GfxCmdUploadTexture3D upload = stream.ReadValueType<GfxCmdUploadTexture3D>();
+ UInt8* srcData = ReadBufferData(stream, upload.srcSize);
+ m_Device->UploadTexture3D(upload.texture, srcData, upload.srcSize,
+ upload.width, upload.height, upload.depth, upload.format, upload.mipCount, upload.uploadFlags);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_DeleteTexture:
+ {
+ TextureID texture = stream.ReadValueType<TextureID>();
+ m_Device->DeleteTexture(texture);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_BeginFrame:
+ {
+ m_Device->BeginFrame();
+ break;
+ }
+ case kGfxCmd_EndFrame:
+ {
+ m_Device->EndFrame();
+ break;
+ }
+ case kGfxCmd_PresentFrame:
+ {
+ PROFILER_AUTO(gMTPresentFrame, NULL);
+ m_Device->PresentFrame();
+ GfxCmdBool signalEvent = stream.ReadValueType<GfxCmdBool>();
+ if (signalEvent)
+ SignalEvent(kEventTypePresent);
+ UnityMemoryBarrier();
+ m_PresentFrameID = stream.ReadValueType<UInt32>();
+ break;
+ }
+ case kGfxCmd_HandleInvalidState:
+ {
+ bool* success = stream.ReadValueType<bool*>();
+ *success = m_Device->HandleInvalidState();
+ Signal();
+ break;
+ }
+ case kGfxCmd_FinishRendering:
+ {
+ m_Device->FinishRendering();
+ Signal();
+ break;
+ }
+ case kGfxCmd_InsertCPUFence:
+ {
+ stream.ReadReleaseData();
+ m_CurrentCPUFence++;
+ break;
+ }
+ case kGfxCmd_ImmediateVertex:
+ {
+ const GfxCmdVector3& data = stream.ReadValueType<GfxCmdVector3>();
+ m_Device->ImmediateVertex(data.x, data.y, data.z);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_ImmediateNormal:
+ {
+ const GfxCmdVector3& data = stream.ReadValueType<GfxCmdVector3>();
+ m_Device->ImmediateNormal(data.x, data.y, data.z);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_ImmediateColor:
+ {
+ const GfxCmdVector4& data = stream.ReadValueType<GfxCmdVector4>();
+ m_Device->ImmediateColor(data.x, data.y, data.z, data.w);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_ImmediateTexCoordAll:
+ {
+ const GfxCmdVector3& data = stream.ReadValueType<GfxCmdVector3>();
+ m_Device->ImmediateTexCoordAll(data.x, data.y, data.z);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_ImmediateTexCoord:
+ {
+ const GfxCmdImmediateTexCoord& data = stream.ReadValueType<GfxCmdImmediateTexCoord>();
+ m_Device->ImmediateTexCoord(data.unit, data.x, data.y, data.z);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_ImmediateBegin:
+ {
+ const GfxPrimitiveType& data = stream.ReadValueType<GfxPrimitiveType>();
+ m_Device->ImmediateBegin(data);
+ break;
+ }
+ case kGfxCmd_ImmediateEnd:
+ {
+ m_Device->ImmediateEnd();
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_CaptureScreenshot:
+ {
+ const GfxCmdCaptureScreenshot& capture = stream.ReadValueType<GfxCmdCaptureScreenshot>();
+ *capture.success = m_Device->CaptureScreenshot(capture.left, capture.bottom, capture.width, capture.height, capture.rgba32);
+ stream.ReadReleaseData();
+ Signal();
+ break;
+ }
+ case kGfxCmd_ReadbackImage:
+ {
+ const GfxCmdReadbackImage& read = stream.ReadValueType<GfxCmdReadbackImage>();
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ *read.success = m_Device->ReadbackImage(read.image, read.left, read.bottom, read.width, read.height, read.destX, read.destY);
+#else
+ //todo
+#endif
+ stream.ReadReleaseData();
+ Signal();
+ break;
+ }
+ case kGfxCmd_GrabIntoRenderTexture:
+ {
+ const GfxCmdGrabIntoRenderTexture& grab = stream.ReadValueType<GfxCmdGrabIntoRenderTexture>();
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceRenderSurface* surf = (ClientDeviceRenderSurface*)grab.rs.object;
+ RenderSurfaceHandle handle(surf ? surf->internalHandle.object : NULL);
+ ClientDeviceRenderSurface* surfZ = (ClientDeviceRenderSurface*)grab.rd.object;
+ RenderSurfaceHandle handleZ(surfZ ? surfZ->internalHandle.object : NULL);
+#else
+ RenderSurfaceHandle handle(m_RenderSurfaceMapper[grab.rs]);
+ RenderSurfaceHandle handleZ(m_RenderSurfaceMapper[grab.rd]);
+#endif
+ m_Device->GrabIntoRenderTexture(handle, handleZ, grab.x, grab.y, grab.width, grab.height);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_SetActiveContext:
+ {
+ void* ctx = stream.ReadValueType<void*>();
+ m_Device->SetActiveContext (ctx);
+ Signal();
+ break;
+ }
+ case kGfxCmd_ResetFrameStats:
+ {
+ m_Device->ResetFrameStats();
+ break;
+ }
+ case kGfxCmd_BeginFrameStats:
+ {
+ m_Device->GetFrameStats().BeginFrameStats();
+ stream.ResetReadWaitTime();
+ break;
+ }
+ case kGfxCmd_EndFrameStats:
+ {
+ GfxDeviceStats& stats = m_Device->GetFrameStats();
+ stats.EndRenderFrameStats();
+ stats.m_RenderFrameTime -= stream.GetReadWaitTime();
+ break;
+ }
+ case kGfxCmd_SaveDrawStats:
+ {
+ m_Device->SaveDrawStats();
+ break;
+ }
+ case kGfxCmd_RestoreDrawStats:
+ {
+ m_Device->RestoreDrawStats();
+ break;
+ }
+ case kGfxCmd_SynchronizeStats:
+ {
+ Mutex::AutoLock lock(m_StatsMutex);
+ m_Device->GetFrameStats().AccumulateUsedTextureUsage();
+ m_FrameStats = m_Device->GetFrameStats();
+ break;
+ }
+#if UNITY_EDITOR
+ case kGfxCmd_SetAntiAliasFlag:
+ {
+ bool aa = stream.ReadValueType<GfxCmdBool>();
+ m_Device->SetAntiAliasFlag(aa);
+ break;
+ }
+ case kGfxCmd_DrawUserPrimitives:
+ {
+ GfxCmdDrawUserPrimitives user = stream.ReadValueType<GfxCmdDrawUserPrimitives>();
+ int dataSize = user.vertexCount * user.stride;
+ m_TempBuffer.resize_uninitialized(dataSize);
+ stream.ReadStreamingData(m_TempBuffer.data(), dataSize);
+ m_Device->DrawUserPrimitives(user.type, user.vertexCount, user.vertexChannels, m_TempBuffer.data(), user.stride);
+ break;
+ }
+#endif
+ case kGfxCmd_Quit:
+ {
+ m_Quit = true;
+ Signal();
+ break;
+ }
+ case kGfxCmd_InsertCustomMarker:
+ {
+ int marker = stream.ReadValueType<int>();
+ m_Device->InsertCustomMarker (marker);
+ break;
+ }
+ case kGfxCmd_SetComputeBufferData:
+ {
+ ComputeBufferID buffer = stream.ReadValueType<ComputeBufferID>();
+ size_t size = stream.ReadValueType<size_t>();
+ DebugAssert (size);
+ m_TempBuffer.resize_uninitialized(size);
+ stream.ReadStreamingData(&m_TempBuffer[0], size);
+ m_Device->SetComputeBufferData (buffer, m_TempBuffer.data(), size);
+ break;
+ }
+ case kGfxCmd_GetComputeBufferData:
+ {
+ ComputeBufferID buffer = stream.ReadValueType<ComputeBufferID>();
+ size_t size = stream.ReadValueType<size_t>();
+ void* ptr = stream.ReadValueType<void*>();
+ DebugAssert (size && ptr);
+ m_Device->GetComputeBufferData (buffer, ptr, size);
+ stream.ReadReleaseData();
+ Signal();
+ break;
+ }
+ case kGfxCmd_CopyComputeBufferCount:
+ {
+ ComputeBufferID srcBuffer = stream.ReadValueType<ComputeBufferID>();
+ ComputeBufferID dstBuffer = stream.ReadValueType<ComputeBufferID>();
+ UInt32 dstOffset = stream.ReadValueType<UInt32>();
+ m_Device->CopyComputeBufferCount (srcBuffer, dstBuffer, dstOffset);
+ break;
+ }
+ case kGfxCmd_SetRandomWriteTargetTexture:
+ {
+ int index = stream.ReadValueType<int>();
+ TextureID tid = stream.ReadValueType<TextureID>();
+ m_Device->SetRandomWriteTargetTexture (index, tid);
+ break;
+ }
+ case kGfxCmd_SetRandomWriteTargetBuffer:
+ {
+ int index = stream.ReadValueType<int>();
+ ComputeBufferID buffer = stream.ReadValueType<ComputeBufferID>();
+ m_Device->SetRandomWriteTargetBuffer (index, buffer);
+ break;
+ }
+ case kGfxCmd_ClearRandomWriteTargets:
+ {
+ m_Device->ClearRandomWriteTargets ();
+ break;
+ }
+ case kGfxCmd_CreateComputeProgram:
+ {
+ ClientDeviceComputeProgram* handle = stream.ReadValueType<ClientDeviceComputeProgram*>();
+ size_t size = stream.ReadValueType<size_t>();
+ m_TempBuffer.resize_uninitialized(size);
+ stream.ReadStreamingData(&m_TempBuffer[0], size);
+ handle->internalHandle = m_Device->CreateComputeProgram (&m_TempBuffer[0], m_TempBuffer.size());
+ break;
+ }
+ case kGfxCmd_DestroyComputeProgram:
+ {
+ ClientDeviceComputeProgram* handle = stream.ReadValueType<ClientDeviceComputeProgram*>();
+ DebugAssert (handle);
+ m_Device->DestroyComputeProgram (handle->internalHandle);
+ UNITY_DELETE(handle, kMemGfxThread);
+ break;
+ }
+ case kGfxCmd_CreateComputeConstantBuffers:
+ {
+ unsigned count = stream.ReadValueType<unsigned>();
+ Assert (count <= kMaxSupportedConstantBuffers);
+ ClientDeviceConstantBuffer* clientCBs[kMaxSupportedConstantBuffers];
+ UInt32 cbSizes[kMaxSupportedConstantBuffers];
+ for (unsigned i = 0; i < count; ++i)
+ {
+ clientCBs[i] = stream.ReadValueType<ClientDeviceConstantBuffer*>();
+ DebugAssert (clientCBs[i]);
+ cbSizes[i] = clientCBs[i]->size;
+ }
+
+ ConstantBufferHandle cbHandles[kMaxSupportedConstantBuffers];
+ m_Device->CreateComputeConstantBuffers (count, cbSizes, cbHandles);
+ for (unsigned i = 0; i < count; ++i)
+ clientCBs[i]->internalHandle = cbHandles[i];
+
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_DestroyComputeConstantBuffers:
+ {
+ unsigned count = stream.ReadValueType<unsigned>();
+ Assert (count <= kMaxSupportedConstantBuffers);
+ ConstantBufferHandle cbHandles[kMaxSupportedConstantBuffers];
+ for (unsigned i = 0; i < count; ++i)
+ {
+ ClientDeviceConstantBuffer* handle = stream.ReadValueType<ClientDeviceConstantBuffer*>();
+ if (handle)
+ {
+ cbHandles[i] = handle->internalHandle;
+ UNITY_DELETE(handle, kMemGfxThread);
+ }
+ }
+ m_Device->DestroyComputeConstantBuffers (count, cbHandles);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_CreateComputeBuffer:
+ {
+ ComputeBufferID buffer = stream.ReadValueType<ComputeBufferID>();
+ size_t count = stream.ReadValueType<size_t>();
+ size_t stride = stream.ReadValueType<size_t>();
+ UInt32 flags = stream.ReadValueType<UInt32>();
+ m_Device->CreateComputeBuffer (buffer, count, stride, flags);
+ break;
+ }
+ case kGfxCmd_DestroyComputeBuffer:
+ {
+ ComputeBufferID buffer = stream.ReadValueType<ComputeBufferID>();
+ m_Device->DestroyComputeBuffer (buffer);
+ break;
+ }
+ case kGfxCmd_UpdateComputeConstantBuffers:
+ {
+ unsigned count = stream.ReadValueType<unsigned>();
+ Assert (count <= kMaxSupportedConstantBuffers);
+ UInt32 cbDirty = stream.ReadValueType<UInt32>();
+
+ ConstantBufferHandle cbHandles[kMaxSupportedConstantBuffers];
+ UInt32 cbSizes[kMaxSupportedConstantBuffers];
+ UInt32 cbOffsets[kMaxSupportedConstantBuffers];
+ int bindPoints[kMaxSupportedConstantBuffers];
+ for (unsigned i = 0; i < count; ++i)
+ {
+ ClientDeviceConstantBuffer* handle = stream.ReadValueType<ClientDeviceConstantBuffer*>();
+ if (handle)
+ cbHandles[i] = handle->internalHandle;
+ cbSizes[i] = stream.ReadValueType<UInt32>();
+ cbOffsets[i] = stream.ReadValueType<UInt32>();
+ bindPoints[i] = stream.ReadValueType<int>();
+ }
+ size_t dataSize = stream.ReadValueType<size_t>();
+ DebugAssert (dataSize);
+ m_TempBuffer.resize_uninitialized(dataSize);
+ stream.ReadStreamingData(&m_TempBuffer[0], dataSize);
+
+ m_Device->UpdateComputeConstantBuffers (count, cbHandles, cbDirty, dataSize, &m_TempBuffer[0], cbSizes, cbOffsets, bindPoints);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_UpdateComputeResources:
+ {
+ TextureID textures[kMaxSupportedComputeResources];
+ int texBindPoints[kMaxSupportedComputeResources];
+ unsigned samplers[kMaxSupportedComputeResources];
+ ComputeBufferID inBuffers[kMaxSupportedComputeResources];
+ int inBufferBindPoints[kMaxSupportedComputeResources];
+ ComputeBufferID outBuffers[kMaxSupportedComputeResources];
+ TextureID outTextures[kMaxSupportedComputeResources];
+ UInt32 outBufferBindPoints[kMaxSupportedComputeResources];
+
+ unsigned texCount = stream.ReadValueType<unsigned>();
+ for (int i = 0; i < texCount; ++i)
+ {
+ textures[i] = stream.ReadValueType<TextureID>();
+ texBindPoints[i] = stream.ReadValueType<int>();
+ }
+ unsigned samplerCount = stream.ReadValueType<unsigned>();
+ for (int i = 0; i < samplerCount; ++i)
+ {
+ samplers[i] = stream.ReadValueType<unsigned>();
+ }
+ unsigned inBufferCount = stream.ReadValueType<unsigned>();
+ for (int i = 0; i < inBufferCount; ++i)
+ {
+ inBuffers[i] = stream.ReadValueType<ComputeBufferID>();
+ inBufferBindPoints[i] = stream.ReadValueType<int>();
+ }
+ unsigned outBufferCount = stream.ReadValueType<unsigned>();
+ for (int i = 0; i < outBufferCount; ++i)
+ {
+ outBuffers[i] = stream.ReadValueType<ComputeBufferID>();
+ outTextures[i] = stream.ReadValueType<TextureID>();
+ outBufferBindPoints[i] = stream.ReadValueType<UInt32>();
+ }
+ m_Device->UpdateComputeResources (texCount, textures, texBindPoints, samplerCount, samplers, inBufferCount, inBuffers, inBufferBindPoints, outBufferCount, outBuffers, outTextures, outBufferBindPoints);
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_DispatchComputeProgram:
+ {
+ ClientDeviceComputeProgram* cp = stream.ReadValueType<ClientDeviceComputeProgram*>();
+ DebugAssert (cp);
+ unsigned threadsX = stream.ReadValueType<unsigned>();
+ unsigned threadsY = stream.ReadValueType<unsigned>();
+ unsigned threadsZ = stream.ReadValueType<unsigned>();
+ m_Device->DispatchComputeProgram (cp->internalHandle, threadsX, threadsY, threadsZ);
+ break;
+ }
+ case kGfxCmd_DrawNullGeometry:
+ {
+ GfxPrimitiveType topology = stream.ReadValueType<GfxPrimitiveType>();
+ int vertexCount = stream.ReadValueType<int>();
+ int instanceCount = stream.ReadValueType<int>();
+ m_Device->DrawNullGeometry (topology, vertexCount, instanceCount);
+ break;
+ }
+ case kGfxCmd_DrawNullGeometryIndirect:
+ {
+ GfxPrimitiveType topology = stream.ReadValueType<GfxPrimitiveType>();
+ ComputeBufferID bufferHandle = stream.ReadValueType<ComputeBufferID>();
+ UInt32 bufferOffset = stream.ReadValueType<UInt32>();
+ m_Device->DrawNullGeometryIndirect (topology, bufferHandle, bufferOffset);
+ break;
+ }
+ case kGfxCmd_VBO_UpdateVertexData:
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceVBO vbo = stream.ReadValueType<ClientDeviceVBO>();
+ ClientVertexBufferData client;
+ client = stream.ReadValueType<ClientVertexBufferData>();
+ VertexBufferData data;
+ memcpy (data.channels, client.channels, sizeof(ChannelInfoArray));
+ memcpy (data.streams, client.streams, sizeof(StreamInfoArray));
+ data.bufferSize = client.bufferSize;
+ data.vertexCount = client.vertexCount;
+ data.buffer = (UInt8*)client.hasData;
+#else
+ ClientDeviceVBO* vbo = stream.ReadValueType<ClientDeviceVBO*>();
+ VertexBufferData data = stream.ReadValueType<VertexBufferData>();
+#endif
+ // Note! Buffer pointer is no longer valid but we use it to tell if data was written or not
+ if (data.buffer)
+ data.buffer = ReadBufferData(stream, data.bufferSize);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_VBOMapper[vbo.internalVBO]->UpdateVertexData(data);
+#else
+ vbo->GetInternal()->UpdateVertexData(data);
+#endif
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_VBO_UpdateIndexData:
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceVBO vbo = stream.ReadValueType<ClientDeviceVBO>();
+#else
+ ClientDeviceVBO* vbo = stream.ReadValueType<ClientDeviceVBO*>();
+#endif
+ IndexBufferData data;
+ data.count = stream.ReadValueType<int>();
+ data.hasTopologies = stream.ReadValueType<UInt32>();
+ int bufferSize = data.count * kVBOIndexSize;
+ data.indices = ReadBufferData(stream, bufferSize);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_VBOMapper[vbo.internalVBO]->UpdateIndexData(data);
+#else
+ vbo->GetInternal()->UpdateIndexData(data);
+#endif
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_VBO_Draw:
+ {
+ PROFILER_AUTO(gMTDrawProf, NULL);
+ const GfxCmdVBODraw& data = stream.ReadValueType<GfxCmdVBODraw>();
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ Assert(data.vbo.internalVBO);
+ m_VBOMapper[data.vbo.internalVBO]->DrawVBO (data.channels, data.firstIndexByte, data.indexCount,
+ data.topology, data.firstVertex, data.vertexCount);
+#else
+ Assert(data.vbo && data.vbo->GetInternal());
+ data.vbo->GetInternal()->DrawVBO (data.channels, data.firstIndexByte, data.indexCount,
+ data.topology, data.firstVertex, data.vertexCount);
+#endif
+ stream.ReadReleaseData();
+ break;
+ }
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+ case kGfxCmd_VBO_DrawCustomIndexed:
+ {
+ PROFILER_AUTO(gMTDrawProf, NULL);
+ GfxCmdVBODrawCustomIndexed data = stream.ReadValueType<GfxCmdVBODrawCustomIndexed>();
+ int dataSize = data.indexCount * kVBOIndexSize;
+ m_TempBuffer.resize_uninitialized(dataSize);
+
+ stream.ReadStreamingData(m_TempBuffer.data(), dataSize);
+ data.vbo->GetInternal()->DrawCustomIndexed(data.channels, m_TempBuffer.data(), data.indexCount,
+ data.topology, data.vertexRangeBegin, data.vertexRangeEnd, data.drawVertexCount);
+ break;
+ }
+#endif
+#if UNITY_XENON
+ case kGfxCmd_VBO_AddExtraUvChannels:
+ {
+ GfxCmdVBOAddExtraUvChannels adduv = stream.ReadValueType<GfxCmdVBOAddExtraUvChannels>();
+ m_TempBuffer.resize_uninitialized(adduv.size);
+ stream.ReadStreamingData(m_TempBuffer.data(), adduv.size);
+ adduv.vbo->GetInternal()->AddExtraUvChannels(m_TempBuffer.data(), adduv.size, adduv.extraUvCount);
+ break;
+ }
+ case kGfxCmd_VBO_CopyExtraUvChannels:
+ {
+ GfxCmdVBOCopyExtraUvChannels copyuv = stream.ReadValueType<GfxCmdVBOCopyExtraUvChannels>();
+ copyuv.dest->GetInternal()->CopyExtraUvChannels(copyuv.source->GetInternal());
+ break;
+ }
+#endif
+ case kGfxCmd_VBO_Recreate:
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceVBO vbo = stream.ReadValueType<ClientDeviceVBO>();
+ m_VBOMapper[vbo.GetInternal()]->Recreate();
+#else
+ ClientDeviceVBO* vbo = stream.ReadValueType<ClientDeviceVBO*>();
+ vbo->GetInternal()->Recreate();
+#endif
+ break;
+ }
+ case kGfxCmd_VBO_MapVertexStream:
+ {
+ // Release any old data
+ stream.ReadReleaseData();
+ // This must be a reference since struct is updated by client thread
+ const GfxCmdVBOMapVertexStream& map = stream.ReadValueType<GfxCmdVBOMapVertexStream>();
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ const ClientDeviceVBO& vbo = map.vbo;
+#else
+ ClientDeviceVBO* vbo = map.vbo;
+#endif
+ int size = map.size;
+ void* dest = NULL;
+ VertexStreamData mappedVSD;
+ if (m_Device->IsValidState())
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ if (m_VBOMapper[vbo.internalVBO]->MapVertexStream(mappedVSD, map.stream))
+#else
+ if (vbo->GetInternal()->MapVertexStream(mappedVSD, map.stream))
+#endif
+ {
+ Assert(mappedVSD.buffer);
+ dest = mappedVSD.buffer;
+ }
+ else
+ ErrorString("Failed to map vertex stream!");
+ }
+
+ // ReadStreamingData will skip data if dest is NULL
+ stream.ReadStreamingData(dest, size);
+
+ if (dest)
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_VBOMapper[vbo.internalVBO]->UnmapVertexStream(map.stream);
+#else
+ vbo->GetInternal()->UnmapVertexStream(map.stream);
+#endif
+ }
+ break;
+ }
+ case kGfxCmd_VBO_SetVertexStreamMode:
+ {
+ GfxCmdVBOSetVertexStreamMode vsmode = stream.ReadValueType<GfxCmdVBOSetVertexStreamMode>();
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_VBOMapper[vsmode.vbo.GetInternal()]->SetVertexStreamMode(vsmode.stream, VBO::StreamMode(vsmode.mode));
+#else
+ vsmode.vbo->GetInternal()->SetVertexStreamMode(vsmode.stream, VBO::StreamMode(vsmode.mode));
+#endif
+ break;
+ }
+ case kGfxCmd_VBO_SetIndicesDynamic:
+ {
+ GfxCmdVBOSetSetIndicesDynamic vsdyn = stream.ReadValueType<GfxCmdVBOSetSetIndicesDynamic>();
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_VBOMapper[vsdyn.vbo.GetInternal()]->SetIndicesDynamic(vsdyn.dynamic);
+#else
+ vsdyn.vbo->GetInternal()->SetIndicesDynamic(vsdyn.dynamic);
+#endif
+ break;
+ }
+ case kGfxCmd_VBO_UseAsStreamOutput:
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceVBO vbo = stream.ReadValueType<ClientDeviceVBO>();
+ m_VBOMapper[vbo.internalVBO]->UseAsStreamOutput();
+#else
+ ClientDeviceVBO *vbo = stream.ReadValueType<ClientDeviceVBO *>();
+ vbo->GetInternal()->UseAsStreamOutput();
+#endif
+ break;
+ }
+ case kGfxCmd_VBO_Constructor:
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceVBO vbo = stream.ReadValueType<ClientDeviceVBO>();
+ m_VBOMapper[vbo.internalVBO] = m_Device->CreateVBO();
+#else
+ ClientDeviceVBO* vbo = stream.ReadValueType<ClientDeviceVBO*>();
+ vbo->internalVBO = m_Device->CreateVBO();
+#endif
+ break;
+ }
+ case kGfxCmd_VBO_Destructor:
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientDeviceVBO vbo = stream.ReadValueType<ClientDeviceVBO>();
+ m_Device->DeleteVBO(m_VBOMapper[vbo.internalVBO]);
+#else
+ ClientDeviceVBO* vbo = stream.ReadValueType<ClientDeviceVBO*>();
+ m_Device->DeleteVBO(vbo->GetInternal());
+ UNITY_DELETE( vbo, kMemGfxThread);
+#endif
+ break;
+ }
+ case kGfxCmd_DynVBO_Chunk:
+ {
+ GfxCmdDynVboChunk chunk = m_CommandQueue->ReadValueType<GfxCmdDynVboChunk>();
+ DynamicVBO& vbo = m_Device->GetDynamicVBO();
+ void* vertexData;
+ void* indexData;
+ void** indexDataPtr = (chunk.actualIndices > 0) ? &indexData :NULL;
+ bool res = vbo.GetChunk(chunk.channelMask, chunk.actualVertices, chunk.actualIndices, DynamicVBO::RenderMode(chunk.renderMode), &vertexData, indexDataPtr);
+ if (!res) vertexData = indexData = NULL;
+
+ m_CommandQueue->ReadStreamingData(vertexData, chunk.actualVertices * chunk.vertexStride);
+ if (chunk.actualIndices > 0)
+ m_CommandQueue->ReadStreamingData(indexData, chunk.actualIndices * kVBOIndexSize);
+
+ if (res) vbo.ReleaseChunk(chunk.actualVertices, chunk.actualIndices);
+
+ m_CommandQueue->ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_DynVBO_DrawChunk:
+ {
+ PROFILER_AUTO(gMTDrawDynamicProf, NULL);
+ const ChannelAssigns& channels = m_CommandQueue->ReadValueType<ChannelAssigns>();
+ DynamicVBO& vbo = m_Device->GetDynamicVBO();
+ vbo.DrawChunk(channels);
+ m_CommandQueue->ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_DisplayList_Call:
+ {
+ ThreadedDisplayList* dlist = m_CommandQueue->ReadValueType<ThreadedDisplayList*>();
+ Assert(m_CallDepth < m_MaxCallDepth);
+ dlist->Patch(*m_CommandQueue);
+ m_PlaybackDisplayLists[m_CallDepth] = dlist;
+ m_CommandQueue = &m_PlaybackCommandQueues[m_CallDepth];
+ m_CommandQueue->CreateReadOnly(dlist->GetData(), dlist->GetSize());
+ m_CallDepth++;
+ break;
+ }
+ case kGfxCmd_DisplayList_End:
+ {
+ Assert(m_CallDepth > 0);
+ m_CallDepth--;
+ SAFE_RELEASE(m_PlaybackDisplayLists[m_CallDepth]);
+ if (m_CallDepth > 0)
+ m_CommandQueue = &m_PlaybackCommandQueues[m_CallDepth - 1];
+ else
+ m_CommandQueue = m_MainCommandQueue;
+ break;
+ }
+ case kGfxCmd_QueryGraphicsCaps:
+ {
+ // no, really, we need to properly serialize this stuff with all the strings. this is just a hack to get running.
+ size_t offset = (char*)&gGraphicsCaps.vendorID - (char*)&gGraphicsCaps;
+ WritebackData(stream, &gGraphicsCaps.vendorID, sizeof(GraphicsCaps) - offset);
+ break;
+ }
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ case kGfxCmd_SetGpuProgramParameters:
+ {
+ ClientIDMapper::ClientID internalHandle = stream.ReadValueType<ClientIDMapper::ClientID>();
+ GpuProgramParameters* gpu = new GpuProgramParameters;
+ Gfx_GpuProgramParameters gfxParams = stream.ReadValueType<Gfx_GpuProgramParameters>();
+ int outSize = stream.ReadValueType<UInt32>();
+ int strSize = stream.ReadValueType<UInt32>();
+
+ char* buffer = (char*)m_CommandQueue->GetReadDataPointer (outSize+strSize, 1);
+ char* strBuf = buffer + outSize;
+ gfxParams.GetOutput(*gpu, buffer, strBuf, strSize);
+ m_GpuProgramParametersMapper[internalHandle] = gpu;
+ break;
+ }
+#endif
+#if UNITY_EDITOR && UNITY_WIN
+ case kGfxCmd_CreateWindow:
+ {
+ ClientDeviceWindow* handle = stream.ReadValueType<ClientDeviceWindow*>();
+ const GfxCmdCreateWindow& upload = stream.ReadValueType<GfxCmdCreateWindow>();
+ handle->internalWindow = m_Device->CreateGfxWindow(upload.window, upload.width, upload.height, upload.depthFormat, upload.antiAlias);
+ break;
+ }
+ case kGfxCmd_SetActiveWindow:
+ {
+ ClientDeviceWindow* handle = stream.ReadValueType<ClientDeviceWindow*>();
+ handle->GetInternal()->SetAsActiveWindow();
+ break;
+ }
+ case kGfxCmd_WindowReshape:
+ {
+ ClientDeviceWindow* handle = stream.ReadValueType<ClientDeviceWindow*>();
+ const GfxCmdWindowReshape& upload = stream.ReadValueType<GfxCmdWindowReshape>();
+ handle->GetInternal()->Reshape(upload.width, upload.height, upload.depthFormat, upload.antiAlias);
+ Signal();
+ break;
+ }
+ case kGfxCmd_WindowDestroy:
+ {
+ ClientDeviceWindow* handle = stream.ReadValueType<ClientDeviceWindow*>();
+ delete handle->GetInternal();
+ Signal();
+ break;
+ }
+ case kGfxCmd_BeginRendering:
+ {
+ ClientDeviceWindow* handle = stream.ReadValueType<ClientDeviceWindow*>();
+ handle->GetInternal()->BeginRendering();
+ break;
+ }
+ case kGfxCmd_EndRendering:
+ {
+ ClientDeviceWindow* handle = stream.ReadValueType<ClientDeviceWindow*>();
+ bool presentContent = stream.ReadValueType<GfxCmdBool>();
+ handle->GetInternal()->EndRendering(presentContent);
+ GfxCmdBool signalEvent = stream.ReadValueType<GfxCmdBool>();
+ if (signalEvent)
+ SignalEvent(kEventTypePresent);
+ break;
+ }
+#endif
+ case kGfxCmd_AcquireThreadOwnership:
+ {
+ m_Device->AcquireThreadOwnership();
+ SetRealGfxDeviceThreadOwnership();
+ Signal();
+ m_IsThreadOwner = true;
+ break;
+ }
+ case kGfxCmd_ReleaseThreadOwnership:
+ {
+ m_Device->ReleaseThreadOwnership();
+ Signal();
+ m_IsThreadOwner = false;
+ break;
+ }
+
+ #if ENABLE_PROFILER
+ case kGfxCmd_BeginProfileEvent:
+ {
+ const char* name = stream.ReadValueType<const char*>();
+ m_Device->BeginProfileEvent (name);
+ break;
+ }
+ case kGfxCmd_EndProfileEvent:
+ {
+ m_Device->EndProfileEvent ();
+ break;
+ }
+ case kGfxCmd_ProfileControl:
+ {
+ GfxDevice::GfxProfileControl ctrl = stream.ReadValueType<GfxDevice::GfxProfileControl>();
+ unsigned param = stream.ReadValueType<unsigned>();
+
+ #if ENABLE_PROFILER
+ switch (ctrl)
+ {
+ case GfxDevice::kGfxProfBeginFrame: profiler_begin_frame_seperate_thread((ProfilerMode)param); break;
+ case GfxDevice::kGfxProfEndFrame: profiler_end_frame_seperate_thread(param); break;
+ case GfxDevice::kGfxProfDisableSampling: profiler_disable_sampling_seperate_thread(); break;
+ case GfxDevice::kGfxProfSetActive: profiler_set_active_seperate_thread(param!=0); break;
+ }
+ #else
+ AssertString("shouldn't be invoked");
+ #endif
+ break;
+ }
+ case kGfxCmd_BeginTimerQueries:
+ {
+ PROFILER_AUTO(gMTBeginQueriesProf, NULL);
+ m_Device->BeginTimerQueries();
+ // Implicitly poll queries
+ PollTimerQueries();
+ break;
+ }
+ case kGfxCmd_EndTimerQueries:
+ {
+ PROFILER_AUTO(gMTEndQueriesProf, NULL);
+ m_Device->EndTimerQueries();
+ stream.ReadReleaseData();
+ break;
+ }
+ case kGfxCmd_TimerQuery_Constructor:
+ {
+ ClientDeviceTimerQuery* query = stream.ReadValueType<ClientDeviceTimerQuery*>();
+ query->internalQuery = m_Device->CreateTimerQuery();
+ break;
+ }
+ case kGfxCmd_TimerQuery_Destructor:
+ {
+ ClientDeviceTimerQuery* query = stream.ReadValueType<ClientDeviceTimerQuery*>();
+ m_Device->DeleteTimerQuery(query->GetInternal());
+ break;
+ }
+ case kGfxCmd_TimerQuery_Measure:
+ {
+ ClientDeviceTimerQuery* query = stream.ReadValueType<ClientDeviceTimerQuery*>();
+ if (query->GetInternal())
+ {
+ Assert(!query->pending);
+ query->pending = true;
+ query->GetInternal()->Measure();
+ m_PolledTimerQueries.push_back(query);
+ }
+ break;
+ }
+ case kGfxCmd_TimerQuery_GetElapsed:
+ {
+ ClientDeviceTimerQuery* query = stream.ReadValueType<ClientDeviceTimerQuery*>();
+ UInt32 flags = stream.ReadValueType<UInt32>();
+ bool wait = (flags & GfxTimerQuery::kWaitRenderThread) != 0;
+ while (query->pending)
+ PollNextTimerQuery(wait);
+ if (flags & GfxTimerQuery::kWaitClientThread)
+ Signal();
+ break;
+ }
+ #endif
+
+ case kGfxCmd_DeleteGPUSkinningInfo:
+ {
+ GPUSkinningInfo *info = stream.ReadValueType<GPUSkinningInfo *>();
+ m_Device->DeleteGPUSkinningInfo(info);
+ break;
+ }
+ case kGfxCmd_SkinOnGPU:
+ {
+ #if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ // todo.
+ #else
+ GPUSkinningInfo *info = stream.ReadValueType<GPUSkinningInfo *>();
+ ClientDeviceVBO* destVBO = stream.ReadValueType<ClientDeviceVBO*>();
+ bool lastThisFrame = stream.ReadValueType<bool>();
+ info->SetDestVBO(destVBO->GetInternal());
+
+ m_Device->SkinOnGPU(info, lastThisFrame);
+ #endif
+ break;
+ }
+
+ case kGfxCmd_UpdateSkinSourceData:
+ {
+ GPUSkinningInfo *info = stream.ReadValueType<GPUSkinningInfo *>();
+ void *vboData = stream.ReadValueType<void *>();
+ BoneInfluence *bones = stream.ReadValueType<BoneInfluence *>();
+ bool dirty = stream.ReadValueType<bool>();
+
+ m_Device->UpdateSkinSourceData(info, vboData, bones, dirty);
+
+ break;
+ }
+
+ case kGfxCmd_UpdateSkinBonePoses:
+ {
+ GPUSkinningInfo *info = stream.ReadValueType<GPUSkinningInfo *>();
+ int boneSize = stream.ReadValueType<int>();
+ Matrix4x4f *bones = stream.ReadArrayType<Matrix4x4f>(boneSize);
+
+ m_Device->UpdateSkinBonePoses(info, boneSize, bones);
+
+ break;
+ }
+
+#if UNITY_XENON
+ case kGfxCmd_RawVBO_Constructor:
+ {
+ ClientDeviceRawVBO* vbo = stream.ReadValueType<ClientDeviceRawVBO*>();
+ UInt32 size = stream.ReadValueType<UInt32>();
+ UInt32 flags = stream.ReadValueType<UInt32>();
+ vbo->internalVBO = m_Device->CreateRawVBO(size, flags);
+ break;
+ }
+ case kGfxCmd_RawVBO_Destructor:
+ {
+ ClientDeviceRawVBO* vbo = stream.ReadValueType<ClientDeviceRawVBO*>();
+ m_Device->DeleteRawVBO(vbo->GetInternal());
+ delete vbo;
+ break;
+ }
+ case kGfxCmd_RawVBO_Next:
+ {
+ ClientDeviceRawVBO* vbo = stream.ReadValueType<ClientDeviceRawVBO*>();
+ vbo->GetInternal()->Next();
+ break;
+ }
+ case kGfxCmd_RawVBO_Write:
+ {
+ ClientDeviceRawVBO* vbo = stream.ReadValueType<ClientDeviceRawVBO*>();
+ UInt32 offset = stream.ReadValueType<UInt32>();
+ UInt32 size = stream.ReadValueType<UInt32>();
+ void* dest = vbo->GetInternal()->GetMemory(offset, size);
+ stream.ReadStreamingData(dest, size);
+ break;
+ }
+ case kGfxCmd_RawVBO_InvalidateGpuCache:
+ {
+ ClientDeviceRawVBO* vbo = stream.ReadValueType<ClientDeviceRawVBO*>();
+ vbo->GetInternal()->InvalidateGpuCache();
+ break;
+ }
+ case kGfxCmd_EnablePersistDisplayOnQuit:
+ {
+ bool enabled = stream.ReadValueType<bool>();
+ m_Device->EnablePersistDisplayOnQuit(enabled);
+ break;
+ }
+ case kGfxCmd_RegisterTexture2D:
+ {
+ TextureID tid = stream.ReadValueType<TextureID>();
+ IDirect3DBaseTexture9* tex = stream.ReadValueType<IDirect3DBaseTexture9*>();
+ m_Device->RegisterTexture2D(tid, tex);
+ break;
+ }
+ case kGfxCmd_PatchTexture2D:
+ {
+ TextureID tid = stream.ReadValueType<TextureID>();
+ IDirect3DBaseTexture9* tex = stream.ReadValueType<IDirect3DBaseTexture9*>();
+ m_Device->PatchTexture2D(tid, tex);
+ break;
+ }
+ case kGfxCmd_DeleteTextureEntryOnly:
+ {
+ TextureID tid = stream.ReadValueType<TextureID>();
+ m_Device->DeleteTextureEntryOnly(tid);
+ break;
+ }
+ case kGfxCmd_UnbindAndDelayReleaseTexture:
+ {
+ IDirect3DBaseTexture9* tex = stream.ReadValueType<IDirect3DBaseTexture9*>();
+ m_Device->UnbindAndDelayReleaseTexture(tex);
+ break;
+ }
+ case kGfxCmd_SetTextureWrapModes:
+ {
+ TextureID tid = stream.ReadValueType<TextureID>();
+ TextureWrapMode wrapU = stream.ReadValueType<TextureWrapMode>();
+ TextureWrapMode wrapV = stream.ReadValueType<TextureWrapMode>();
+ TextureWrapMode wrapW = stream.ReadValueType<TextureWrapMode>();
+ m_Device->SetTextureWrapModes(tid, wrapU, wrapV, wrapW);
+ break;
+ }
+ case kGfxCmd_OnLastFrameCallback:
+ {
+ m_Device->OnLastFrameCallback();
+ break;
+ }
+ case kGfxCmd_VideoPlayer_Constructor:
+ {
+ ClientDeviceVideoPlayer* vp = stream.ReadValueType<ClientDeviceVideoPlayer*>();
+ bool fullscreen = stream.ReadValueType<bool>();
+ vp->internalVP = m_Device->CreateVideoPlayer(fullscreen);
+ break;
+ }
+ case kGfxCmd_VideoPlayer_Destructor:
+ {
+ ClientDeviceVideoPlayer* vp = stream.ReadValueType<ClientDeviceVideoPlayer*>();
+ m_Device->DeleteVideoPlayer(vp->GetInternal());
+ delete vp;
+ break;
+ }
+ case kGfxCmd_VideoPlayer_Render:
+ {
+ ClientDeviceVideoPlayer* vp = stream.ReadValueType<ClientDeviceVideoPlayer*>();
+ vp->GetInternal()->Render();
+ vp->isDead = vp->GetInternal()->IsDead();
+ break;
+ }
+ case kGfxCmd_VideoPlayer_Pause:
+ {
+ ClientDeviceVideoPlayer* vp = stream.ReadValueType<ClientDeviceVideoPlayer*>();
+ vp->GetInternal()->Pause();
+ break;
+ }
+ case kGfxCmd_VideoPlayer_Resume:
+ {
+ ClientDeviceVideoPlayer* vp = stream.ReadValueType<ClientDeviceVideoPlayer*>();
+ vp->GetInternal()->Resume();
+ break;
+ }
+ case kGfxCmd_VideoPlayer_Stop:
+ {
+ ClientDeviceVideoPlayer* vp = stream.ReadValueType<ClientDeviceVideoPlayer*>();
+ vp->GetInternal()->Stop();
+ break;
+ }
+ case kGfxCmd_VideoPlayer_SetPlaySpeed:
+ {
+ ClientDeviceVideoPlayer* vp = stream.ReadValueType<ClientDeviceVideoPlayer*>();
+ float speed = stream.ReadValueType<float>();
+ vp->GetInternal()->SetPlaySpeed(speed);
+ break;
+ }
+ case kGfxCmd_VideoPlayer_Play:
+ {
+ ClientDeviceVideoPlayer* vp = stream.ReadValueType<ClientDeviceVideoPlayer*>();
+ XMEDIA_XMV_CREATE_PARAMETERS xmvParams = stream.ReadValueType<XMEDIA_XMV_CREATE_PARAMETERS>();
+ bool loop = stream.ReadValueType<bool>();
+ if (xmvParams.createType == XMEDIA_CREATE_FROM_FILE)
+ {
+ size_t len = stream.ReadValueType<size_t>();
+ char* fileName = (char*)_alloca(len);
+ stream.ReadStreamingData(fileName, len);
+ xmvParams.createFromFile.szFileName = fileName;
+ }
+ bool hasRect = stream.ReadValueType<bool>();
+ RECT rect;
+ RECT* rectPtr = 0;
+ if (hasRect)
+ {
+ rect = stream.ReadValueType<RECT>();
+ rectPtr = &rect;
+ }
+ vp->GetInternal()->Play(xmvParams, loop, rectPtr);
+ break;
+ }
+ case kGfxCmd_SetNullPixelShader:
+ {
+ m_Device->SetNullPixelShader();
+ break;
+ }
+ case kGfxCmd_EnableHiZ:
+ {
+ HiZstate hiz_enable = stream.ReadValueType<HiZstate>();
+ m_Device->SetHiZEnable( hiz_enable );
+ break;
+ }
+ case kGfxCmd_SetHiStencilState:
+ {
+ bool hiStencilEnable = stream.ReadValueType<bool>();
+ bool hiStencilWriteEnable = stream.ReadValueType<bool>();
+ int hiStencilRef = stream.ReadValueType<int>();
+ CompareFunction cmpFunc = stream.ReadValueType<CompareFunction>();
+
+ Assert( hiStencilRef < 256 );
+ Assert( cmpFunc == kFuncEqual || cmpFunc == kFuncNotEqual );
+ m_Device->SetHiStencilState( hiStencilEnable, hiStencilWriteEnable, hiStencilRef, cmpFunc );
+ break;
+ }
+ case kGfxCmd_HiStencilFlush:
+ {
+ HiSflush flushtype = stream.ReadValueType<HiSflush>();
+ m_Device->HiStencilFlush( flushtype );
+ break;
+ }
+#endif // UNITY_XENON
+ default:
+ ErrorString(Format("Gfx command not handled: %d (Last command: %d)", cmd, lastCmd));
+ }
+
+ GFXDEVICE_LOCKSTEP_WORKER();
+ lastCmd = cmd;
+}
+
+void GfxDeviceWorker::DoLockstep(int pos, int cmd)
+{
+ if (m_CallDepth == 0)
+ {
+ //printf_console("MT: worker pos %i cmd %i\n", pos, cmd);
+ m_LockstepSemaphore.Signal();
+ }
+}
+
+UInt8* GfxDeviceWorker::ReadBufferData (ThreadedStreamBuffer& stream, int size)
+{
+ int maxNonStreamedSize = stream.GetAllocatedSize() / 2;
+ if (size <= maxNonStreamedSize || m_CallDepth > 0)
+ {
+ stream.ReadReleaseData();
+ void* data = stream.GetReadDataPointer(size, ThreadedStreamBuffer::kDefaultAlignment);
+ return reinterpret_cast<UInt8*>(data);
+ }
+ else
+ {
+ m_TempBuffer.resize_uninitialized(size);
+ stream.ReadStreamingData(m_TempBuffer.data(), size);
+ return m_TempBuffer.data();
+ }
+}
+
+void GfxDeviceWorker::WritebackData (ThreadedStreamBuffer& stream, const void* data, int size)
+{
+ const SInt32 maxSize = m_CommandQueue->ReadValueType<SInt32>();
+ SInt32 writtenSize = 0;
+ do
+ {
+ void* chunkPtr = m_CommandQueue->GetReadDataPointer(sizeof(SInt32) + maxSize, ThreadedStreamBuffer::kDefaultAlignment);
+ SInt32* returnedSizePtr = static_cast<SInt32*>(chunkPtr);
+ void* returnedDataPtr = &returnedSizePtr[1];
+ SInt32 chunkSize = std::min<SInt32>(size, maxSize);
+ if (chunkSize > 0)
+ memcpy(returnedDataPtr, static_cast<const UInt8*>(data) + writtenSize, chunkSize);
+ UnityMemoryBarrier();
+ *returnedSizePtr = chunkSize;
+ stream.ReadReleaseData();
+ writtenSize += size;
+ } while (writtenSize < size);
+}
+
+#if ENABLE_PROFILER
+void GfxDeviceWorker::PollTimerQueries()
+{
+ for (;;)
+ {
+ if (!PollNextTimerQuery(false))
+ break;
+ }
+}
+
+bool GfxDeviceWorker::PollNextTimerQuery(bool wait)
+{
+ if (m_PolledTimerQueries.empty())
+ return false;
+
+ ClientDeviceTimerQuery* query = m_PolledTimerQueries.front();
+ UInt32 flags = wait ? GfxTimerQuery::kWaitAll : 0;
+ ProfileTimeFormat elapsed = query->GetInternal()->GetElapsed(flags);
+ if (elapsed != kInvalidProfileTime)
+ {
+ m_PolledTimerQueries.pop_front();
+ // Make result available to client thread
+ query->elapsed = elapsed;
+ UnityMemoryBarrier();
+ query->pending = false;
+ return true;
+ }
+ return false;
+}
+#endif
+
+#endif
diff --git a/Runtime/GfxDevice/threaded/GfxDeviceWorker.h b/Runtime/GfxDevice/threaded/GfxDeviceWorker.h
new file mode 100644
index 0000000..4129917
--- /dev/null
+++ b/Runtime/GfxDevice/threaded/GfxDeviceWorker.h
@@ -0,0 +1,135 @@
+#pragma once
+#if ENABLE_MULTITHREADED_CODE
+
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/GfxDevice/threaded/WorkerIDMapper.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Threads/Semaphore.h"
+#include "Runtime/Threads/JobScheduler.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "Runtime/Filters/Mesh/MeshSkinning.h"
+#include <deque>
+
+
+#define GFXDEVICE_USE_CACHED_STATE 0
+#define DEBUG_GFXDEVICE_LOCKSTEP 0
+
+#if DEBUG_GFXDEVICE_LOCKSTEP
+ #define GFXDEVICE_LOCKSTEP_CLIENT() { DoLockstep(); }
+ #define GFXDEVICE_LOCKSTEP_WORKER() { DoLockstep(pos, cmd); }
+#else
+ #define GFXDEVICE_LOCKSTEP_CLIENT()
+ #define GFXDEVICE_LOCKSTEP_WORKER()
+#endif
+
+struct ClientDeviceTimerQuery;
+class ThreadedStreamBuffer;
+class ThreadedDisplayList;
+class Thread;
+
+class GfxDeviceWorker : public NonCopyable
+{
+public:
+ GfxDeviceWorker(int maxCallDepth, ThreadedStreamBuffer* commandQueue);
+ ~GfxDeviceWorker();
+
+ GfxThreadableDevice* Startup(GfxDeviceRenderer renderer, bool threaded, bool forceRef);
+
+ void WaitForSignal();
+ void LockstepWait();
+
+ void GetLastFrameStats(GfxDeviceStats& stats);
+
+ void CallImmediate(ThreadedDisplayList* dlist);
+
+ enum EventType
+ {
+ kEventTypePresent,
+ kEventTypeTimerQueries,
+ kEventTypeCount
+ };
+
+ void WaitForEvent(EventType type);
+
+ void WaitOnCPUFence(UInt32 fence);
+
+ bool DidPresentFrame(UInt32 frameID) const;
+
+ bool RunCommandIfDataIsAvailable();
+
+private:
+ void SignalEvent(EventType type);
+
+ static void* RunGfxDeviceWorker(void *data);
+
+ void Run();
+ void RunCommand(ThreadedStreamBuffer& stream);
+ void Signal();
+ void DoLockstep(int pos, int cmd);
+
+ UInt8* ReadBufferData(ThreadedStreamBuffer& stream, int size);
+ void WritebackData(ThreadedStreamBuffer& stream, const void* data, int size);
+
+#if ENABLE_PROFILER
+ void PollTimerQueries();
+ bool PollNextTimerQuery(bool wait);
+#endif
+
+#if GFXDEVICE_USE_CACHED_STATE
+ struct CachedState
+ {
+ CachedState();
+ NormalizationMode normalization;
+ int backface;
+ Vector4f ambient;
+ int fogEnabled;
+ GfxFogParams fogParams;
+ };
+#endif
+
+ int m_CallDepth;
+ int m_MaxCallDepth;
+ Thread* m_WorkerThread;
+ ThreadedStreamBuffer* m_CommandQueue;
+ ThreadedStreamBuffer* m_MainCommandQueue;
+ ThreadedStreamBuffer* m_PlaybackCommandQueues;
+ ThreadedDisplayList** m_PlaybackDisplayLists;
+ dynamic_array<UInt8> m_TempBuffer;
+ dynamic_array<SkinMeshInfo> m_ActiveSkins;
+ dynamic_array<VBO*> m_MappedSkinVBOs;
+ JobScheduler::JobGroupID m_SkinJobGroup;
+ Semaphore m_EventSemaphores[kEventTypeCount];
+ Semaphore m_LockstepSemaphore;
+ Semaphore m_WaitSemaphore;
+ volatile UInt32 m_CurrentCPUFence;
+ volatile UInt32 m_PresentFrameID;
+ Mutex m_StatsMutex;
+ GfxDeviceStats m_FrameStats;
+ GfxThreadableDevice* m_Device;
+ bool m_IsThreadOwner;
+ bool m_Quit;
+#if GFXDEVICE_USE_CACHED_STATE
+ CachedState m_Cached;
+#endif
+#if ENABLE_PROFILER
+ // Timer queries for GPU profiling
+ typedef std::deque<ClientDeviceTimerQuery*> TimerQueryList;
+ TimerQueryList m_PolledTimerQueries;
+#endif
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ WorkerIDMapper<DeviceBlendState> m_BlendStateMapper;
+ WorkerIDMapper<DeviceDepthState> m_DepthStateMapper;
+ WorkerIDMapper<DeviceStencilState> m_StencilStateMapper;
+ WorkerIDMapper<DeviceRasterState> m_RasterStateMapper;
+ WorkerIDMapper<VBO> m_VBOMapper;
+ WorkerIDMapper<void> m_TextureCombinerMapper;
+ WorkerIDMapper<void> m_RenderSurfaceMapper;
+ WorkerIDMapper<GpuProgramParameters> m_GpuProgramParametersMapper;
+ WorkerIDMapper<GpuProgram> m_GpuProgramMapper;
+ ClientIDMapper m_GpuProgramClientMapper;
+#endif
+};
+
+#endif
diff --git a/Runtime/GfxDevice/threaded/GfxReturnStructs.cpp b/Runtime/GfxDevice/threaded/GfxReturnStructs.cpp
new file mode 100644
index 0000000..dd90789
--- /dev/null
+++ b/Runtime/GfxDevice/threaded/GfxReturnStructs.cpp
@@ -0,0 +1,239 @@
+#include "UnityPrefix.h"
+#include "GfxReturnStructs.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+
+static int AppendString(dynamic_array<char>& strBuf, const char* str)
+{
+ int strLen = strlen(str);
+ int strOfs = strBuf.size();
+ strBuf.resize_uninitialized(strOfs + strLen + 1);
+ std::copy(str, str + strLen + 1, &strBuf[strOfs]);
+ return strOfs;
+}
+
+static const char* GetStringAtOffset(int offset, const char* strBuf, int strBufLen)
+{
+ if (offset >= 0 && offset < strBufLen)
+ return &strBuf[offset];
+ ErrorString("Invalid offset in GetStringAtOffset()");
+ return "";
+}
+
+GfxRet_ValueParameter::GfxRet_ValueParameter(const GpuProgramParameters::ValueParameter& src, dynamic_array<char>& strBuf)
+: index(src.m_Index)
+, arraySize(src.m_ArraySize)
+, rowCount(src.m_RowCount)
+, colCount(src.m_ColCount)
+{
+ nameStrOfs = AppendString(strBuf, src.m_Name.GetName());
+}
+
+void GfxRet_ValueParameter::ToValueParameter(GpuProgramParameters::ValueParameter& dest, const char* strBuf, int strBufLen) const
+{
+ dest.m_Name = ShaderLab::Property(GetStringAtOffset(nameStrOfs, strBuf, strBufLen));
+ dest.m_Index = index;
+ dest.m_ArraySize = arraySize;
+ dest.m_RowCount = rowCount;
+ dest.m_ColCount = colCount;
+}
+
+
+GfxRet_TextureParameter::GfxRet_TextureParameter(const GpuProgramParameters::TextureParameter& src, dynamic_array<char>& strBuf)
+: index(src.m_Index)
+, samplerIndex(src.m_SamplerIndex)
+, dim(src.m_Dim)
+{
+ nameStrOfs = AppendString(strBuf, src.m_Name.GetName());
+}
+
+void GfxRet_TextureParameter::ToTextureParameter(GpuProgramParameters::TextureParameter& dest, const char* strBuf, int strBufLen) const
+{
+ dest.m_Name = ShaderLab::Property(GetStringAtOffset(nameStrOfs, strBuf, strBufLen));
+ dest.m_Index = index;
+ dest.m_SamplerIndex = samplerIndex;
+ dest.m_Dim = dim;
+}
+
+
+GfxRet_BufferParameter::GfxRet_BufferParameter(const GpuProgramParameters::BufferParameter& src, dynamic_array<char>& strBuf)
+: index(src.m_Index)
+{
+ nameStrOfs = AppendString(strBuf, src.m_Name.GetName());
+}
+
+void GfxRet_BufferParameter::ToBufferParameter(GpuProgramParameters::BufferParameter& dest, const char* strBuf, int strBufLen) const
+{
+ dest.m_Name = ShaderLab::Property(GetStringAtOffset(nameStrOfs, strBuf, strBufLen));
+ dest.m_Index = index;
+}
+
+GfxRet_ShaderError::GfxRet_ShaderError(const ShaderErrors::ShaderError& src, dynamic_array<char>& strBuf)
+: line(src.line)
+, warning(src.warning)
+, programError(src.programError)
+{
+ messageStrOfs = AppendString(strBuf, src.message.c_str());
+ messageDetailsStrOfs = AppendString(strBuf, src.messageDetails.c_str());
+}
+
+void GfxRet_ShaderError::ToShaderError(ShaderErrors::ShaderError& dest, const char* strBuf, int strBufLen) const
+{
+ dest.message = GetStringAtOffset(messageStrOfs, strBuf, strBufLen);
+ dest.messageDetails = GetStringAtOffset(messageDetailsStrOfs, strBuf, strBufLen);
+ dest.line = line;
+ dest.warning = warning;
+ dest.programError = programError;
+}
+
+
+GfxRet_ChannelAssigns::GfxRet_ChannelAssigns(const ChannelAssigns& src)
+: targetMap(src.m_TargetMap)
+, sourceMap(src.m_SourceMap)
+, directlyWired(src.m_DirectlyWired)
+{
+ std::copy(src.m_Channels, src.m_Channels + kVertexCompCount, channels);
+};
+
+void GfxRet_ChannelAssigns::ToChannelAssigns(ChannelAssigns& dest) const
+{
+ dest.m_TargetMap = targetMap;
+ dest.m_SourceMap = sourceMap;
+ dest.m_DirectlyWired = directlyWired;
+ std::copy(channels, channels + kVertexCompCount, dest.m_Channels);
+}
+
+Gfx_GpuProgramParameters::Gfx_GpuProgramParameters()
+: valueParameterCount(0)
+, valueParameterOffset(0)
+, textureParameterCount(0)
+, textureParameterOffset(0)
+, bufferParameterCount(0)
+, bufferParameterOffset(0)
+{
+}
+
+Gfx_GpuProgramParameters::Gfx_GpuProgramParameters(const GpuProgramParameters& params, dynamic_array<UInt8>& outBuf, dynamic_array<char> &strBuf)
+{
+ const GpuProgramParameters::ValueParameterArray& srcValueParams = params.GetValueParams();
+ valueParameterCount = srcValueParams.size();
+ valueParameterOffset = outBuf.size();
+ outBuf.resize_uninitialized(valueParameterOffset + valueParameterCount * sizeof(GfxRet_ValueParameter));
+ GfxRet_ValueParameter* valueParams = reinterpret_cast<GfxRet_ValueParameter*>(outBuf.data() + valueParameterOffset);
+ for (int i = 0; i < valueParameterCount; ++i)
+ valueParams[i] = GfxRet_ValueParameter(srcValueParams[i], strBuf);
+
+ const GpuProgramParameters::TextureParameterList& srcTextureParams = params.GetTextureParams();
+ textureParameterCount = srcTextureParams.size();
+ textureParameterOffset = outBuf.size();
+ outBuf.resize_uninitialized(textureParameterOffset + textureParameterCount * sizeof(GfxRet_TextureParameter));
+ GfxRet_TextureParameter* textureParams = reinterpret_cast<GfxRet_TextureParameter*>(outBuf.data() + textureParameterOffset);
+ for (int i = 0; i < textureParameterCount; ++i)
+ textureParams[i] = GfxRet_TextureParameter(srcTextureParams[i], strBuf);
+
+ const GpuProgramParameters::BufferParameterArray& srcBufferParams = params.GetBufferParams();
+ bufferParameterCount = srcBufferParams.size();
+ bufferParameterOffset = outBuf.size();
+ outBuf.resize_uninitialized(bufferParameterOffset + bufferParameterCount * sizeof(GfxRet_BufferParameter));
+ GfxRet_BufferParameter* bufferParams = reinterpret_cast<GfxRet_BufferParameter*>(outBuf.data() + bufferParameterOffset);
+ for (int i = 0; i < bufferParameterCount; ++i)
+ bufferParams[i] = GfxRet_BufferParameter(srcBufferParams[i], strBuf);
+}
+
+void Gfx_GpuProgramParameters::GetOutput(GpuProgramParameters& outParams, const char* dataBegin, const char* stringBuffer, int stringBufferLength) const
+{
+ const GfxRet_ValueParameter* valueParams = reinterpret_cast<const GfxRet_ValueParameter*>(dataBegin + valueParameterOffset);
+ for (int i = 0; i < valueParameterCount; ++i)
+ {
+ GpuProgramParameters::ValueParameter param;
+ valueParams[i].ToValueParameter(param, stringBuffer, stringBufferLength);
+ outParams.AddValueParam(param);
+ }
+
+ const GfxRet_TextureParameter* textureParams = reinterpret_cast<const GfxRet_TextureParameter*>(dataBegin + textureParameterOffset);
+ for (int i = 0; i < textureParameterCount; ++i)
+ {
+ GpuProgramParameters::TextureParameter param;
+ textureParams[i].ToTextureParameter(param, stringBuffer, stringBufferLength);
+ outParams.AddTextureParam(param);
+ }
+}
+
+GfxRet_CreateGpuProgram::GfxRet_CreateGpuProgram(const CreateGpuProgramOutput& output, dynamic_array<UInt8>& outBuf)
+: channelAssignsCount(0)
+, channelAssignsOffset(0)
+, shaderErrorCount(0)
+, shaderErrorOffset(0)
+, stringBufferLength(0)
+, stringBufferOffset(0)
+{
+ perFogModeParamsEnabled = output.GetPerFogModeParamsEnabled();
+
+ // Shared buffer for strings
+ dynamic_array<char> strBuf;
+
+ if (output.GetParams())
+ {
+ params = Gfx_GpuProgramParameters(*output.GetParams(), outBuf, strBuf);
+ }
+
+ if (output.GetChannelAssigns())
+ {
+ channelAssignsCount = 1;
+ channelAssignsOffset = outBuf.size();
+ outBuf.resize_uninitialized(channelAssignsOffset + channelAssignsCount * sizeof(GfxRet_ChannelAssigns));
+ GfxRet_ChannelAssigns* channelAssigns = reinterpret_cast<GfxRet_ChannelAssigns*>(outBuf.data() + channelAssignsOffset);
+ channelAssigns[0] = GfxRet_ChannelAssigns(*output.GetChannelAssigns());
+ }
+
+ if (output.GetShaderErrors())
+ {
+ const ShaderErrors::ErrorContainer& srcShaderErrors = output.GetShaderErrors()->GetErrors();
+ shaderErrorCount = srcShaderErrors.size();
+ shaderErrorOffset = outBuf.size();
+ outBuf.resize_uninitialized(shaderErrorOffset + shaderErrorCount * sizeof(GfxRet_ShaderError));
+ GfxRet_ShaderError* shaderErrors = reinterpret_cast<GfxRet_ShaderError*>(outBuf.data() + shaderErrorOffset);
+ ShaderErrors::ErrorContainer::const_iterator srcIt = srcShaderErrors.begin();
+ for (int i = 0; i < shaderErrorCount; ++srcIt, ++i)
+ new (shaderErrors + i) GfxRet_ShaderError(*srcIt, strBuf);
+ Assert(srcIt == srcShaderErrors.end());
+ }
+
+ stringBufferLength = strBuf.size();
+ stringBufferOffset = outBuf.size();
+ outBuf.resize_uninitialized(stringBufferOffset + stringBufferLength);
+ std::copy(strBuf.begin(), strBuf.end(), outBuf.begin() + stringBufferOffset);
+}
+
+void GfxRet_CreateGpuProgram::GetOutput(CreateGpuProgramOutput& output) const
+{
+ output.SetPerFogModeParamsEnabled(perFogModeParamsEnabled);
+
+ const char* dataBegin = reinterpret_cast<const char*>(this);
+ const char* stringBuffer = dataBegin + stringBufferOffset;
+
+ if (params.valueParameterCount > 0 || params.textureParameterCount > 0 || params.bufferParameterCount > 0)
+ {
+ GpuProgramParameters& outParams = output.CreateParams();
+ params.GetOutput(outParams, dataBegin, stringBuffer, stringBufferLength);
+ }
+
+ if (channelAssignsCount > 0)
+ {
+ Assert(channelAssignsCount == 1);
+ const GfxRet_ChannelAssigns* channelAssigns = reinterpret_cast<const GfxRet_ChannelAssigns*>(dataBegin + channelAssignsOffset);
+ channelAssigns[0].ToChannelAssigns(output.CreateChannelAssigns());
+ }
+
+ if (shaderErrorCount > 0)
+ {
+ ShaderErrors& outErrors = output.CreateShaderErrors();
+
+ const GfxRet_ShaderError* shaderErrors = reinterpret_cast<const GfxRet_ShaderError*>(dataBegin + shaderErrorOffset);
+ for (int i = 0; i < shaderErrorCount; ++i)
+ {
+ ShaderErrors::ShaderError err;
+ shaderErrors[i].ToShaderError(err, stringBuffer, stringBufferLength);
+ outErrors.AddShaderError(err.message, err.messageDetails, err.line, err.warning, err.programError);
+ }
+ }
+}
diff --git a/Runtime/GfxDevice/threaded/GfxReturnStructs.h b/Runtime/GfxDevice/threaded/GfxReturnStructs.h
new file mode 100644
index 0000000..f3e7b47
--- /dev/null
+++ b/Runtime/GfxDevice/threaded/GfxReturnStructs.h
@@ -0,0 +1,122 @@
+#pragma once
+
+#include "Runtime/GfxDevice/GpuProgram.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "External/shaderlab/Library/ShaderLabErrors.h"
+class ChannelAssigns;
+namespace ShaderLab { class SubProgram; }
+
+struct GfxRet_ValueParameter
+{
+ GfxRet_ValueParameter(const GpuProgramParameters::ValueParameter& src, dynamic_array<char>& strBuf);
+ void ToValueParameter(GpuProgramParameters::ValueParameter& dest, const char* strBuf, int strBufLen) const;
+
+ int nameStrOfs;
+ int index;
+ int arraySize;
+ UInt8 rowCount;
+ UInt8 colCount;
+};
+
+struct GfxRet_TextureParameter
+{
+ GfxRet_TextureParameter(const GpuProgramParameters::TextureParameter& src, dynamic_array<char>& strBuf);
+ void ToTextureParameter(GpuProgramParameters::TextureParameter& dest, const char* strBuf, int strBufLen) const;
+
+ int nameStrOfs;
+ int index;
+ int samplerIndex;
+ TextureDimension dim;
+};
+
+struct GfxRet_BufferParameter
+{
+ GfxRet_BufferParameter(const GpuProgramParameters::BufferParameter& src, dynamic_array<char>& strBuf);
+ void ToBufferParameter(GpuProgramParameters::BufferParameter& dest, const char* strBuf, int strBufLen) const;
+
+ int nameStrOfs;
+ int index;
+};
+
+struct GfxRet_ChannelAssigns
+{
+ GfxRet_ChannelAssigns(const ChannelAssigns& src);
+ void ToChannelAssigns(ChannelAssigns& dest) const;
+
+ UInt32 targetMap;
+ UInt32 sourceMap;
+ SInt8 channels[kVertexCompCount];
+ bool directlyWired;
+};
+
+struct GfxRet_ShaderError
+{
+ GfxRet_ShaderError(const ShaderErrors::ShaderError& src, dynamic_array<char>& strBuf);
+ void ToShaderError(ShaderErrors::ShaderError& dest, const char* strBuf, int strBufLen) const;
+
+ int messageStrOfs;
+ int messageDetailsStrOfs;
+ int line;
+ bool warning;
+ bool programError;
+};
+
+struct Gfx_GpuProgramParameters
+{
+ Gfx_GpuProgramParameters();
+ Gfx_GpuProgramParameters(const GpuProgramParameters& params, dynamic_array<UInt8>& outBuf, dynamic_array<char> &strBuf);
+
+ //void GetOutput(GpuProgramParameters &output, const char* strBuf, int strBufLen) const;
+ void GetOutput(GpuProgramParameters& outParams, const char* dataBegin, const char* strBuf, int strBufLen) const;
+
+ int valueParameterCount;
+ int valueParameterOffset;
+
+ // Array of GfxRet_TextureParameter
+ int textureParameterCount;
+ int textureParameterOffset;
+
+ // Array of GfxRet_BufferParameter
+ int bufferParameterCount;
+ int bufferParameterOffset;
+};
+
+struct GfxRet_CreateGpuProgram
+{
+ GfxRet_CreateGpuProgram(const CreateGpuProgramOutput& output, dynamic_array<UInt8>& outBuf);
+
+ void GetOutput(CreateGpuProgramOutput& output) const;
+
+ bool perFogModeParamsEnabled;
+
+ Gfx_GpuProgramParameters params;
+ /*
+ // Array of GfxRet_ValueParameter
+ int valueParameterCount;
+ int valueParameterOffset;
+
+ // Array of GfxRet_TextureParameter
+ int textureParameterCount;
+ int textureParameterOffset;
+
+ // Array of GfxRet_BufferParameter
+ int bufferParameterCount;
+ int bufferParameterOffset;
+ */
+ // Array of GfxRet_ChannelAssigns
+ int channelAssignsCount;
+ int channelAssignsOffset;
+
+ // Array of GfxRet_ShaderError
+ int shaderErrorCount;
+ int shaderErrorOffset;
+
+ // Shared buffer of string data
+ int stringBufferLength;
+ int stringBufferOffset;
+
+ ClientIDMapper::ClientID gpuProgram;
+
+ PropertyNamesSet names;
+};
+
diff --git a/Runtime/GfxDevice/threaded/ThreadedDeviceStates.h b/Runtime/GfxDevice/threaded/ThreadedDeviceStates.h
new file mode 100644
index 0000000..a36a2fa
--- /dev/null
+++ b/Runtime/GfxDevice/threaded/ThreadedDeviceStates.h
@@ -0,0 +1,134 @@
+#pragma once
+#if ENABLE_MULTITHREADED_CODE
+
+#include "Runtime/GfxDevice/GfxDeviceObjects.h"
+#include "Runtime/GfxDevice/GfxDeviceResources.h"
+#include "Runtime/GfxDevice/threaded/ClientIDMapper.h"
+#include "Runtime/Filters/Mesh/VertexData.h"
+#include "Runtime/Graphics/RenderSurface.h"
+
+namespace ShaderLab { struct TextureBinding; }
+namespace xenon { class IVideoPlayer; }
+class VBO;
+class RawVBO;
+class GfxTimerQuery;
+class GfxDeviceWindow;
+
+struct ClientDeviceRect
+{
+ ClientDeviceRect() : x(0), y(0), width(0), height(0) {}
+ int x;
+ int y;
+ int width;
+ int height;
+};
+
+struct ClientDeviceBlendState : public DeviceBlendState
+{
+ ClientDeviceBlendState(const GfxBlendState& src) : DeviceBlendState(src), internalState(NULL) {}
+ ClientIDWrapper(DeviceBlendState) internalState;
+};
+
+struct ClientDeviceDepthState : public DeviceDepthState
+{
+ ClientDeviceDepthState(const GfxDepthState& src) : DeviceDepthState(src), internalState(NULL) {}
+ ClientIDWrapper(DeviceDepthState) internalState;
+};
+
+struct ClientDeviceStencilState : public DeviceStencilState
+{
+ ClientDeviceStencilState(const GfxStencilState& src) : DeviceStencilState(src), internalState(NULL) {}
+ ClientIDWrapper(DeviceStencilState) internalState;
+};
+
+struct ClientDeviceRasterState : public DeviceRasterState
+{
+ ClientDeviceRasterState(const GfxRasterState& src) : DeviceRasterState(src), internalState(NULL) {}
+ ClientIDWrapper(DeviceRasterState) internalState;
+};
+
+struct ClientDeviceTextureCombiners
+{
+ ClientIDWrapperHandle(TextureCombinersHandle) internalHandle;
+ ShaderLab::TextureBinding* bindings;
+ int count;
+};
+
+struct ClientDeviceRenderSurface : RenderSurfaceBase
+{
+ enum SurfaceState { kInitial, kCleared, kRendered, kResolved };
+ ClientDeviceRenderSurface(int w, int h) { RenderSurfaceBase_Init(*this); width=w; height=h; zformat=kDepthFormatNone; state=kInitial; }
+ ClientIDWrapperHandle(RenderSurfaceHandle) internalHandle;
+ DepthBufferFormat zformat;
+ SurfaceState state;
+};
+
+struct ClientDeviceVBO
+{
+ ClientDeviceVBO() : internalVBO(NULL) {}
+
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ ClientIDWrapper(VBO) GetInternal() { return internalVBO; }
+#else
+ VBO* GetInternal() { return const_cast<VBO*>(internalVBO); }
+#endif
+ volatile ClientIDWrapper(VBO) internalVBO;
+};
+
+struct ClientVertexBufferData
+{
+ ChannelInfoArray channels;
+ StreamInfoArray streams;
+ int bufferSize;
+ int vertexCount;
+ int hasData;
+};
+
+struct ClientDeviceTimerQuery
+{
+ ClientDeviceTimerQuery() : internalQuery(NULL), elapsed(0), pending(false) {}
+ GfxTimerQuery* GetInternal() { return const_cast<GfxTimerQuery*>(internalQuery); }
+ volatile GfxTimerQuery* internalQuery;
+ volatile UInt64 elapsed;
+ volatile bool pending;
+};
+
+struct ClientDeviceWindow
+{
+ ClientDeviceWindow() : internalWindow(NULL) {}
+ GfxDeviceWindow* GetInternal() { return const_cast<GfxDeviceWindow*>(internalWindow); }
+ volatile GfxDeviceWindow* internalWindow;
+};
+
+struct ClientDeviceConstantBuffer
+{
+ ClientDeviceConstantBuffer(UInt32 sz) : size(sz) {}
+ ConstantBufferHandle internalHandle;
+ UInt32 size;
+};
+
+struct ClientDeviceComputeProgram
+{
+ ClientDeviceComputeProgram() {}
+ ComputeProgramHandle internalHandle;
+};
+
+
+#if UNITY_XENON
+struct ClientDeviceRawVBO
+{
+ ClientDeviceRawVBO() : internalVBO(NULL) {}
+ RawVBO* GetInternal() { return const_cast<RawVBO*>(internalVBO); }
+ volatile RawVBO* internalVBO;
+};
+
+struct ClientDeviceVideoPlayer
+{
+ ClientDeviceVideoPlayer() : internalVP(NULL), isDead(false) {}
+ xenon::IVideoPlayer* GetInternal() { return const_cast<xenon::IVideoPlayer*>(internalVP); }
+ volatile xenon::IVideoPlayer* internalVP;
+ volatile bool isDead;
+};
+#endif
+
+#endif
diff --git a/Runtime/GfxDevice/threaded/ThreadedDisplayList.cpp b/Runtime/GfxDevice/threaded/ThreadedDisplayList.cpp
new file mode 100644
index 0000000..27fa38e
--- /dev/null
+++ b/Runtime/GfxDevice/threaded/ThreadedDisplayList.cpp
@@ -0,0 +1,442 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_MULTITHREADED_CODE
+
+#define DEBUG_GPU_PARAMETER_PATCHING (!UNITY_RELEASE)
+
+#include "Runtime/GfxDevice/threaded/ThreadedDisplayList.h"
+#include "Runtime/GfxDevice/threaded/GfxDeviceClient.h"
+#include "Runtime/GfxDevice/threaded/GfxDeviceWorker.h"
+#include "Runtime/GfxDevice/threaded/GfxCommands.h"
+#include "Runtime/Threads/ThreadedStreamBuffer.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/shaderlab.h"
+
+DisplayListContext::DisplayListContext()
+{
+ ClearState();
+}
+
+void DisplayListContext::ClearState()
+{
+ recordFailed = false;
+ hasSetShaders = false;
+ memset(shadersActive, 0, sizeof(shadersActive));
+ fogParamsOffset = kFogParamsNone;
+}
+
+void DisplayListContext::Reset()
+{
+ ClearState();
+ commandQueue.ResetGrowable();
+ patchInfo.Reset();
+}
+
+
+ThreadedDisplayList::ThreadedDisplayList(const void* data, size_t size, const DisplayListContext& context)
+ : m_ListData(data, size, context.patchInfo)
+{
+ m_HasSetShaders = context.hasSetShaders;
+ memcpy(m_ShadersActive, context.shadersActive, sizeof(m_ShadersActive));
+
+ if (context.fogParamsOffset >= 0)
+ m_FogParamsOffset = CopyClientData(context.fogParamsOffset, sizeof(GfxFogParams));
+ else
+ m_FogParamsOffset = context.fogParamsOffset;
+}
+
+ThreadedDisplayList::~ThreadedDisplayList()
+{
+}
+
+void ThreadedDisplayList::Call()
+{
+ GfxDeviceClient& device = (GfxDeviceClient&)GetGfxDevice();
+ if (device.IsSerializing())
+ {
+ ThreadedStreamBuffer& queue = *device.GetCommandQueue();
+ AddRef(); // Release again when call is finished
+ queue.WriteValueType<GfxCommand>(kGfxCmd_DisplayList_Call);
+ queue.WriteValueType<ThreadedDisplayList*>(this);
+ m_ListData.WriteParameters(queue);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ m_ListData.PatchImmediate();
+ device.GetGfxDeviceWorker()->CallImmediate(this);
+ }
+ UpdateClientDevice(device);
+}
+
+void ThreadedDisplayList::Patch(ThreadedStreamBuffer& queue)
+{
+ m_ListData.Patch(queue);
+}
+
+int ThreadedDisplayList::CopyClientData(int offset, int size)
+{
+ int newOffset = m_ClientData.AppendData(&m_ListData.m_Buffer[offset], size);
+ int relativeOffset = newOffset - offset;
+ for (int pt = 0; pt < GfxPatch::kTypeCount; pt++)
+ {
+ GfxPatch::Type patchType = GfxPatch::Type(pt);
+ int patchCount = m_ListData.m_PatchInfo.GetPatchCount(patchType);
+ for (int i = 0; i < patchCount; i++)
+ {
+ const GfxPatch& srcPatch = m_ListData.m_PatchInfo.GetPatch(patchType, i);
+ int patchOffset = srcPatch.patchOffset;
+ if (patchOffset >= offset && patchOffset < offset + size)
+ {
+ GfxPatch patch = srcPatch;
+ patch.patchOffset += relativeOffset;
+ m_ClientData.m_PatchInfo.AddPatch(patchType, patch);
+ }
+ }
+ }
+ return newOffset;
+}
+
+void ThreadedDisplayList::UpdateClientDevice(GfxDeviceClient& device)
+{
+ if (!m_ClientData.m_Buffer.empty())
+ m_ClientData.PatchImmediate();
+
+ if (m_FogParamsOffset != DisplayListContext::kFogParamsNone)
+ {
+ if (m_FogParamsOffset != DisplayListContext::kFogParamsDisable)
+ {
+ const void* data = &m_ClientData.m_Buffer[m_FogParamsOffset];
+ const GfxFogParams& fogParams = *static_cast<const GfxFogParams*>(data);
+ device.UpdateFogEnabled(fogParams);
+ }
+ else
+ device.UpdateFogDisabled();
+ }
+
+ if (m_HasSetShaders)
+ device.UpdateShadersActive(m_ShadersActive);
+}
+
+void ThreadedDisplayList::DoLockstep()
+{
+ GfxDeviceClient& device = (GfxDeviceClient&)GetGfxDevice();
+ device.DoLockstep();
+}
+
+ThreadedDisplayList::PatchableData::PatchableData(const void* data, size_t size, const GfxPatchInfo& patchInfo)
+ : m_PatchInfo(patchInfo)
+{
+ m_Buffer.resize_uninitialized(size);
+ memcpy(m_Buffer.begin(), data, size);
+}
+
+ThreadedDisplayList::PatchableData::PatchableData()
+{
+}
+
+void ThreadedDisplayList::PatchableData::CheckParametersValid()
+{
+#if DEBUG_GPU_PARAMETER_PATCHING
+ BuiltinShaderParamValues& builtinParams = GetGfxDevice().GetBuiltinParamValues();
+ using namespace ShaderLab;
+
+ FastPropertyName name;
+ size_t floatCount = m_PatchInfo.GetPatchCount(GfxPatch::kTypeFloat);
+ for (size_t i = 0; i < floatCount; i++)
+ {
+ const GfxPatch& patch = m_PatchInfo.GetPatch(GfxPatch::kTypeFloat, i);
+ const float* src = reinterpret_cast<const float*>(patch.source);
+ name.index = patch.nameIndex;
+ if (name.IsBuiltin())
+ {
+ Assert(name.IsBuiltinVector());
+ const float& val = *builtinParams.GetVectorParam(BuiltinShaderVectorParam(name.BuiltinIndex())).GetPtr();
+ Assert(&val == src);
+ }
+ else
+ {
+ const float& val = g_GlobalProperties->GetFloat(name);
+ Assert(src == NULL || &val == src);
+ }
+ }
+
+ size_t vectorCount = m_PatchInfo.GetPatchCount(GfxPatch::kTypeVector);
+ for (size_t i = 0; i < vectorCount; i++)
+ {
+ const GfxPatch& patch = m_PatchInfo.GetPatch(GfxPatch::kTypeVector, i);
+ const Vector4f* src = reinterpret_cast<const Vector4f*>(patch.source);
+ name.index = patch.nameIndex;
+ if (name.IsBuiltin())
+ {
+ Assert(name.IsBuiltinVector());
+ const Vector4f& vec = builtinParams.GetVectorParam(BuiltinShaderVectorParam(name.BuiltinIndex()));
+ Assert(&vec == src);
+ }
+ else
+ {
+ const Vector4f& vec = g_GlobalProperties->GetVector(name);
+ Assert(src == NULL || &vec == src);
+ }
+ }
+
+ size_t matrixCount = m_PatchInfo.GetPatchCount(GfxPatch::kTypeMatrix);
+ for (size_t i = 0; i < matrixCount; i++)
+ {
+ const GfxPatch& patch = m_PatchInfo.GetPatch(GfxPatch::kTypeMatrix, i);
+ const Matrix4x4f* src = reinterpret_cast<const Matrix4x4f*>(patch.source);
+ name.index = patch.nameIndex;
+ if (name.IsBuiltin())
+ {
+ Assert(name.IsBuiltinMatrix());
+ const Matrix4x4f* mat = &builtinParams.GetMatrixParam(BuiltinShaderMatrixParam(name.BuiltinIndex()));
+ Assert(mat == src);
+ }
+ else
+ {
+ int count = 0;
+ const Matrix4x4f* mat = reinterpret_cast<const Matrix4x4f*>(g_GlobalProperties->GetValueProp (name, &count));
+ Assert (src == NULL || mat == src);
+ }
+ }
+
+ const size_t bufferCount = m_PatchInfo.GetPatchCount(GfxPatch::kTypeBuffer);
+ for (size_t i = 0; i < bufferCount; i++)
+ {
+ const GfxPatch& patch = m_PatchInfo.GetPatch(GfxPatch::kTypeBuffer, i);
+ const ComputeBufferID* src = reinterpret_cast<const ComputeBufferID*>(patch.source);
+ name.index = patch.nameIndex;
+ Assert (!name.IsBuiltin());
+ const ComputeBufferID& buf = g_GlobalProperties->GetComputeBuffer(name);
+ Assert(src == NULL || &buf == src);
+ }
+#endif
+}
+
+void ThreadedDisplayList::PatchableData::WriteParameters(ThreadedStreamBuffer& queue)
+{
+ CheckParametersValid();
+
+ size_t floatCount = m_PatchInfo.GetPatchCount(GfxPatch::kTypeFloat);
+ for (size_t i = 0; i < floatCount; i++)
+ {
+ const GfxPatch& patch = m_PatchInfo.GetPatch(GfxPatch::kTypeFloat, i);
+ float* dest = queue.GetWritePointer<float>();
+ PatchFloat(patch, dest);
+ }
+
+ size_t vectorCount = m_PatchInfo.GetPatchCount(GfxPatch::kTypeVector);
+ for (size_t i = 0; i < vectorCount; i++)
+ {
+ const GfxPatch& patch = m_PatchInfo.GetPatch(GfxPatch::kTypeVector, i);
+ Vector4f* dest = queue.GetWritePointer<Vector4f>();
+ PatchVector(patch, dest);
+ }
+
+ size_t matrixCount = m_PatchInfo.GetPatchCount(GfxPatch::kTypeMatrix);
+ for (size_t i = 0; i < matrixCount; i++)
+ {
+ const GfxPatch& patch = m_PatchInfo.GetPatch(GfxPatch::kTypeMatrix, i);
+ Matrix4x4f* dest = queue.GetWritePointer<Matrix4x4f>();
+ PatchMatrix(patch, dest);
+ }
+
+ size_t texEnvCount = m_PatchInfo.GetTexEnvPatchCount();
+ for (size_t i = 0; i < texEnvCount; i++)
+ {
+ const GfxTexEnvPatch& patch = m_PatchInfo.GetTexEnvPatch(i);
+ if (patch.patchFlags & GfxTexEnvPatch::kPatchProperties)
+ {
+ TexEnvProperties* dest = queue.GetWritePointer<TexEnvProperties>();
+ PatchTexEnvProperties(patch, dest);
+ }
+ if (patch.patchFlags & GfxTexEnvPatch::kPatchMatrix)
+ {
+ Matrix4x4f* dest = queue.GetWritePointer<Matrix4x4f>();
+ PatchTexEnvMatrix(patch, dest);
+ }
+ }
+
+ const size_t bufferCount = m_PatchInfo.GetPatchCount(GfxPatch::kTypeBuffer);
+ for (size_t i = 0; i < bufferCount; i++)
+ {
+ const GfxPatch& patch = m_PatchInfo.GetPatch(GfxPatch::kTypeBuffer, i);
+ ComputeBufferID* dest = queue.GetWritePointer<ComputeBufferID>();
+ PatchBuffer(patch, dest);
+ }
+}
+
+void ThreadedDisplayList::PatchableData::PatchImmediate()
+{
+ CheckParametersValid();
+
+ size_t floatCount = m_PatchInfo.GetPatchCount(GfxPatch::kTypeFloat);
+ for (size_t i = 0; i < floatCount; i++)
+ {
+ const GfxPatch& patch = m_PatchInfo.GetPatch(GfxPatch::kTypeFloat, i);
+ float* dest = reinterpret_cast<float*>(&m_Buffer[patch.patchOffset]);
+ PatchFloat(patch, dest);
+ }
+
+ size_t vectorCount = m_PatchInfo.GetPatchCount(GfxPatch::kTypeVector);
+ for (size_t i = 0; i < vectorCount; i++)
+ {
+ const GfxPatch& patch = m_PatchInfo.GetPatch(GfxPatch::kTypeVector, i);
+ Vector4f* dest = reinterpret_cast<Vector4f*>(&m_Buffer[patch.patchOffset]);
+ PatchVector(patch, dest);
+ }
+
+ size_t matrixCount = m_PatchInfo.GetPatchCount(GfxPatch::kTypeMatrix);
+ for (size_t i = 0; i < matrixCount; i++)
+ {
+ const GfxPatch& patch = m_PatchInfo.GetPatch(GfxPatch::kTypeMatrix, i);
+ Matrix4x4f* dest = reinterpret_cast<Matrix4x4f*>(&m_Buffer[patch.patchOffset]);
+ PatchMatrix(patch, dest);
+ }
+
+ size_t texEnvCount = m_PatchInfo.GetTexEnvPatchCount();
+ for (size_t i = 0; i < texEnvCount; i++)
+ {
+ const GfxTexEnvPatch& patch = m_PatchInfo.GetTexEnvPatch(i);
+ TexEnvData* dest = reinterpret_cast<TexEnvData*>(&m_Buffer[patch.patchOffset]);
+
+ if (patch.patchFlags & GfxTexEnvPatch::kPatchProperties)
+ PatchTexEnvProperties(patch, dest);
+
+ if (patch.patchFlags & GfxTexEnvPatch::kPatchMatrix)
+ PatchTexEnvMatrix(patch, &dest->matrix);
+ }
+
+ size_t bufferCount = m_PatchInfo.GetPatchCount(GfxPatch::kTypeBuffer);
+ for (size_t i = 0; i < bufferCount; i++)
+ {
+ const GfxPatch& patch = m_PatchInfo.GetPatch(GfxPatch::kTypeBuffer, i);
+ ComputeBufferID* dest = reinterpret_cast<ComputeBufferID*>(&m_Buffer[patch.patchOffset]);
+ PatchBuffer(patch, dest);
+ }
+}
+
+void ThreadedDisplayList::PatchableData::Patch(ThreadedStreamBuffer& queue)
+{
+ FastPropertyName name;
+
+ size_t floatCount = m_PatchInfo.GetPatchCount(GfxPatch::kTypeFloat);
+ for (size_t i = 0; i < floatCount; i++)
+ {
+ const GfxPatch& patch = m_PatchInfo.GetPatch(GfxPatch::kTypeFloat, i);
+ float val = queue.ReadValueType<float>();
+ *reinterpret_cast<float*>(&m_Buffer[patch.patchOffset]) = val;
+ }
+
+ size_t vectorCount = m_PatchInfo.GetPatchCount(GfxPatch::kTypeVector);
+ for (size_t i = 0; i < vectorCount; i++)
+ {
+ const GfxPatch& patch = m_PatchInfo.GetPatch(GfxPatch::kTypeVector, i);
+ const Vector4f& vec = queue.ReadValueType<Vector4f>();
+ *reinterpret_cast<Vector4f*>(&m_Buffer[patch.patchOffset]) = vec;
+ }
+
+ size_t matrixCount = m_PatchInfo.GetPatchCount(GfxPatch::kTypeMatrix);
+ for (size_t i = 0; i < matrixCount; i++)
+ {
+ const GfxPatch& patch = m_PatchInfo.GetPatch(GfxPatch::kTypeMatrix, i);
+ const Matrix4x4f& mat = queue.ReadValueType<Matrix4x4f>();
+ *reinterpret_cast<Matrix4x4f*>(&m_Buffer[patch.patchOffset]) = mat;
+ }
+
+ size_t texEnvCount = m_PatchInfo.GetTexEnvPatchCount();
+ for (size_t i = 0; i < texEnvCount; i++)
+ {
+ using namespace ShaderLab;
+ const GfxTexEnvPatch& patch = m_PatchInfo.GetTexEnvPatch(i);
+ TexEnvData& data = *reinterpret_cast<TexEnvData*>(&m_Buffer[patch.patchOffset]);
+ if (patch.patchFlags & GfxTexEnvPatch::kPatchProperties)
+ {
+ TexEnvProperties& dest = data;
+ // Patch doesn't know if matrix is identity
+ // Fortunately we can just keep the old value
+ bool savedIdentityMatrix = dest.identityMatrix;
+ dest = queue.ReadValueType<TexEnvProperties>();
+ dest.identityMatrix = savedIdentityMatrix;
+ }
+ if (patch.patchFlags & GfxTexEnvPatch::kPatchMatrix)
+ data.matrix = queue.ReadValueType<Matrix4x4f>();
+ }
+
+ const size_t bufferCount = m_PatchInfo.GetPatchCount(GfxPatch::kTypeBuffer);
+ for (size_t i = 0; i < bufferCount; i++)
+ {
+ const GfxPatch& patch = m_PatchInfo.GetPatch(GfxPatch::kTypeBuffer, i);
+ const ComputeBufferID& buf = queue.ReadValueType<ComputeBufferID>();
+ *reinterpret_cast<ComputeBufferID*>(&m_Buffer[patch.patchOffset]) = buf;
+ }
+}
+
+void ThreadedDisplayList::PatchableData::PatchFloat(const GfxPatch& patch, float* dest)
+{
+ using namespace ShaderLab;
+ FastPropertyName name;
+ name.index = patch.nameIndex;
+ DebugAssert(patch.source || (name.IsValid() && !name.IsBuiltin()));
+ const float* src = static_cast<const float*>(patch.source);
+ *dest = src ? *src : g_GlobalProperties->GetFloat(name);
+}
+
+void ThreadedDisplayList::PatchableData::PatchVector(const GfxPatch& patch, Vector4f* dest)
+{
+ using namespace ShaderLab;
+ FastPropertyName name;
+ name.index = patch.nameIndex;
+ DebugAssert(patch.source || (name.IsValid() && !name.IsBuiltin()));
+ const Vector4f* src = static_cast<const Vector4f*>(patch.source);
+ *dest = src ? *src : g_GlobalProperties->GetVector(name);
+}
+
+void ThreadedDisplayList::PatchableData::PatchMatrix(const GfxPatch& patch, Matrix4x4f* dest)
+{
+ using namespace ShaderLab;
+ FastPropertyName name;
+ name.index = patch.nameIndex;
+ DebugAssert(patch.source || (name.IsValid() && !name.IsBuiltin()));
+ const Matrix4x4f* src = static_cast<const Matrix4x4f*>(patch.source);
+ if (!src)
+ {
+ int count = 0;
+ src = reinterpret_cast<const Matrix4x4f*>(g_GlobalProperties->GetValueProp (name, &count));
+ if (count < 16)
+ src = NULL;
+ }
+ *dest = src ? *src : Matrix4x4f::identity;
+}
+
+void ThreadedDisplayList::PatchableData::PatchBuffer(const GfxPatch& patch, ComputeBufferID* dest)
+{
+ using namespace ShaderLab;
+ FastPropertyName name;
+ name.index = patch.nameIndex;
+ DebugAssert(patch.source || (name.IsValid() && !name.IsBuiltin()));
+ const ComputeBufferID* src = static_cast<const ComputeBufferID*>(patch.source);
+ *dest = src ? *src : g_GlobalProperties->GetComputeBuffer(name);
+}
+
+void ThreadedDisplayList::PatchableData::PatchTexEnvProperties(const GfxTexEnvPatch& patch, TexEnvProperties* dest)
+{
+ patch.texEnv->PrepareProperties(patch.nameIndex, dest);
+}
+
+void ThreadedDisplayList::PatchableData::PatchTexEnvMatrix(const GfxTexEnvPatch& patch, Matrix4x4f* dest)
+{
+ bool identity;
+ patch.texEnv->PrepareMatrix(patch.matrixName, ShaderLab::g_GlobalProperties, dest, identity);
+}
+
+int ThreadedDisplayList::PatchableData::AppendData(const void* data, int size)
+{
+ int offset = m_Buffer.size();
+ m_Buffer.resize_uninitialized(offset + size);
+ memcpy(&m_Buffer[offset], data, size);
+ return offset;
+}
+
+
+#endif
diff --git a/Runtime/GfxDevice/threaded/ThreadedDisplayList.h b/Runtime/GfxDevice/threaded/ThreadedDisplayList.h
new file mode 100644
index 0000000..5711f8e
--- /dev/null
+++ b/Runtime/GfxDevice/threaded/ThreadedDisplayList.h
@@ -0,0 +1,85 @@
+#ifndef THREADED_DISPLAY_LIST_H
+#define THREADED_DISPLAY_LIST_H
+
+#if ENABLE_MULTITHREADED_CODE
+
+#include "Runtime/Threads/ThreadedStreamBuffer.h"
+#include "Runtime/GfxDevice/GfxDisplayList.h"
+#include "Runtime/GfxDevice/GfxPatchInfo.h"
+#include "Runtime/GfxDevice/GpuProgram.h"
+
+class GfxDeviceClient;
+
+struct DisplayListContext
+{
+ DisplayListContext();
+ void ClearState();
+ void Reset();
+
+ enum
+ {
+ kFogParamsNone = -1,
+ kFogParamsDisable = -2
+ };
+
+ ThreadedStreamBuffer commandQueue;
+ GfxPatchInfo patchInfo;
+ bool recordFailed;
+ bool hasSetShaders;
+ bool shadersActive[kShaderTypeCount];
+ int fogParamsOffset;
+};
+
+
+class ThreadedDisplayList : public GfxDisplayList
+{
+public:
+ ThreadedDisplayList(const void* data, size_t size, const DisplayListContext& context);
+ ~ThreadedDisplayList();
+
+ void Call();
+
+ UInt8* GetData() { return m_ListData.m_Buffer.begin(); }
+ const UInt8* GetData() const { return m_ListData.m_Buffer.begin(); }
+ size_t GetSize() const { return m_ListData.m_Buffer.size(); }
+
+ void Patch(ThreadedStreamBuffer& queue);
+
+private:
+ int CopyClientData(int offset, int size);
+ void UpdateClientDevice(GfxDeviceClient& device);
+ void DoLockstep();
+
+ class PatchableData
+ {
+ public:
+ PatchableData(const void* data, size_t size,
+ const GfxPatchInfo& patchInfo);
+ PatchableData();
+
+ void CheckParametersValid();
+ void WriteParameters(ThreadedStreamBuffer& queue);
+ void PatchImmediate();
+ void Patch(ThreadedStreamBuffer& queue);
+ void PatchFloat(const GfxPatch& patch, float* dest);
+ void PatchVector(const GfxPatch& patch, Vector4f* dest);
+ void PatchMatrix(const GfxPatch& patch, Matrix4x4f* dest);
+ void PatchBuffer(const GfxPatch& patch, ComputeBufferID* dest);
+ void PatchTexEnvProperties(const GfxTexEnvPatch& patch, TexEnvProperties* dest);
+ void PatchTexEnvMatrix(const GfxTexEnvPatch& patch, Matrix4x4f* dest);
+ int AppendData(const void* data, int size);
+
+ dynamic_array<UInt8> m_Buffer;
+ GfxPatchInfo m_PatchInfo;
+ };
+
+ PatchableData m_ListData;
+ PatchableData m_ClientData;
+ bool m_HasSetShaders;
+ bool m_ShadersActive[kShaderTypeCount];
+ int m_FogParamsOffset;
+};
+
+
+#endif
+#endif
diff --git a/Runtime/GfxDevice/threaded/ThreadedTimerQuery.cpp b/Runtime/GfxDevice/threaded/ThreadedTimerQuery.cpp
new file mode 100644
index 0000000..2b1731a
--- /dev/null
+++ b/Runtime/GfxDevice/threaded/ThreadedTimerQuery.cpp
@@ -0,0 +1,102 @@
+#include "UnityPrefix.h"
+#if ENABLE_PROFILER
+#include "ThreadedTimerQuery.h"
+#include "GfxDeviceWorker.h"
+#include "Runtime/Threads/ThreadUtility.h"
+#include "Runtime/Threads/ThreadedStreamBuffer.h"
+#include "Runtime/GfxDevice/threaded/GfxDeviceClient.h"
+#include "Runtime/GfxDevice/threaded/GfxCommands.h"
+
+ThreadedTimerQuery::ThreadedTimerQuery(GfxDeviceClient& device)
+: m_ClientDevice(device)
+{
+ m_ClientQuery = new ClientDeviceTimerQuery;
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ if (m_ClientDevice.IsSerializing())
+ {
+ ThreadedStreamBuffer& stream = *m_ClientDevice.GetCommandQueue();
+ stream.WriteValueType<GfxCommand>(kGfxCmd_TimerQuery_Constructor);
+ stream.WriteValueType<ClientDeviceTimerQuery*>(m_ClientQuery);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_ClientQuery->internalQuery = GetRealGfxDevice().CreateTimerQuery();
+}
+
+ThreadedTimerQuery::~ThreadedTimerQuery()
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ if (m_ClientDevice.IsSerializing())
+ {
+ ThreadedStreamBuffer& stream = *m_ClientDevice.GetCommandQueue();
+ stream.WriteValueType<GfxCommand>(kGfxCmd_TimerQuery_Destructor);
+ stream.WriteValueType<ClientDeviceTimerQuery*>(m_ClientQuery);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ Assert(m_ClientQuery);
+ GetRealGfxDevice().DeleteTimerQuery(m_ClientQuery->GetInternal());
+ delete m_ClientQuery;
+ }
+ m_ClientQuery = NULL;
+}
+
+void ThreadedTimerQuery::Measure()
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ if (m_ClientDevice.IsSerializing())
+ {
+ ThreadedStreamBuffer& stream = *m_ClientDevice.GetCommandQueue();
+ stream.WriteValueType<GfxCommand>(kGfxCmd_TimerQuery_Measure);
+ stream.WriteValueType<ClientDeviceTimerQuery*>(m_ClientQuery);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ m_ClientQuery->GetInternal()->Measure();
+}
+
+ProfileTimeFormat ThreadedTimerQuery::GetElapsed(UInt32 flags)
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ if (m_ClientDevice.IsSerializing())
+ {
+ // See if we have the result already
+ ProfileTimeFormat time = GetElapsedIfReady();
+ if (time != kInvalidProfileTime)
+ return time;
+
+ // Request result from worker thread
+ ThreadedStreamBuffer& stream = *m_ClientDevice.GetCommandQueue();
+ stream.WriteValueType<GfxCommand>(kGfxCmd_TimerQuery_GetElapsed);
+ stream.WriteValueType<ClientDeviceTimerQuery*>(m_ClientQuery);
+ stream.WriteValueType<UInt32>(flags);
+ if (flags & GfxTimerQuery::kWaitClientThread)
+ {
+ m_ClientDevice.SubmitCommands();
+ m_ClientDevice.GetGfxDeviceWorker()->WaitForSignal();
+ }
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ return GetElapsedIfReady();
+ }
+ else
+ return m_ClientQuery->GetInternal()->GetElapsed(flags);
+}
+
+ProfileTimeFormat ThreadedTimerQuery::GetElapsedIfReady()
+{
+ if (!m_ClientQuery->pending)
+ {
+ // Be careful since UInt64 isn't guaranteed atomic
+ UnityMemoryBarrier();
+ return m_ClientQuery->elapsed;
+ }
+ return kInvalidProfileTime;
+}
+
+void ThreadedTimerQuery::DoLockstep()
+{
+ m_ClientDevice.DoLockstep();
+}
+
+#endif
diff --git a/Runtime/GfxDevice/threaded/ThreadedTimerQuery.h b/Runtime/GfxDevice/threaded/ThreadedTimerQuery.h
new file mode 100644
index 0000000..b8aecbd
--- /dev/null
+++ b/Runtime/GfxDevice/threaded/ThreadedTimerQuery.h
@@ -0,0 +1,29 @@
+#ifndef THREADEDTIMERQUERY_H
+#define THREADEDTIMERQUERY_H
+
+#if ENABLE_PROFILER
+
+#include "Runtime/GfxDevice/GfxTimerQuery.h"
+
+class GfxDeviceClient;
+struct ClientDeviceTimerQuery;
+
+class ThreadedTimerQuery : public GfxTimerQuery
+{
+public:
+ ThreadedTimerQuery(GfxDeviceClient& device);
+ ~ThreadedTimerQuery();
+
+ virtual void Measure();
+ virtual ProfileTimeFormat GetElapsed(UInt32 flags);
+
+private:
+ ProfileTimeFormat GetElapsedIfReady();
+ void DoLockstep();
+
+ GfxDeviceClient& m_ClientDevice;
+ ClientDeviceTimerQuery* m_ClientQuery;
+};
+
+#endif
+#endif
diff --git a/Runtime/GfxDevice/threaded/ThreadedVBO.cpp b/Runtime/GfxDevice/threaded/ThreadedVBO.cpp
new file mode 100644
index 0000000..09fdd5f
--- /dev/null
+++ b/Runtime/GfxDevice/threaded/ThreadedVBO.cpp
@@ -0,0 +1,511 @@
+#include "UnityPrefix.h"
+#include "ThreadedVBO.h"
+#include "Runtime/Threads/ThreadedStreamBuffer.h"
+#include "Runtime/GfxDevice/threaded/GfxDeviceClient.h"
+#include "Runtime/GfxDevice/threaded/GfxDeviceWorker.h"
+#include "Runtime/GfxDevice/threaded/GfxCommands.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+
+
+ThreadedVBO::ThreadedVBO(GfxDeviceClient& device) :
+ m_ClientDevice(device),
+ m_ClientVBO(NULL),
+ m_NonThreadedVBO(NULL),
+ m_MappedFromRenderThread(false),
+ m_VertexBufferLost(false),
+ m_IndexBufferLost(false),
+ m_VertexBufferSize(0),
+ m_IndexBufferSize(0)
+{
+ SET_ALLOC_OWNER(NULL);
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ m_ClientVBO = UNITY_NEW(ClientDeviceVBO, kMemGfxThread);
+ if (device.IsThreaded())
+ {
+
+ GetCommandQueue().WriteValueType<GfxCommand>(kGfxCmd_VBO_Constructor);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_ClientVBO->internalVBO = m_ClientDevice.m_VBOMapper.CreateID();
+ GetCommandQueue().WriteValueType<ClientDeviceVBO>(*m_ClientVBO);
+#else
+ GetCommandQueue().WriteValueType<ClientDeviceVBO*>(m_ClientVBO);
+#endif
+ GetCommandQueue().WriteSubmitData();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ else
+ {
+ m_NonThreadedVBO = GetRealGfxDevice().CreateVBO();
+ m_ClientVBO->internalVBO = m_NonThreadedVBO;
+ }
+#endif
+}
+
+ThreadedVBO::~ThreadedVBO()
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ DebugAssert(!m_ClientDevice.IsRecording());
+ if (m_ClientDevice.IsThreaded())
+ {
+ DebugAssert(!m_NonThreadedVBO);
+ // m_ClientVBO deleted by server
+ GetCommandQueue().WriteValueType<GfxCommand>(kGfxCmd_VBO_Destructor);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ GetCommandQueue().WriteValueType<ClientDeviceVBO>(*m_ClientVBO);
+#else
+ GetCommandQueue().WriteValueType<ClientDeviceVBO*>(m_ClientVBO);
+#endif
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ DebugAssert(m_NonThreadedVBO);
+ GetRealGfxDevice().DeleteVBO(m_NonThreadedVBO);
+ UNITY_DELETE(m_ClientVBO, kMemGfxThread);
+ }
+ m_NonThreadedVBO = NULL;
+ m_ClientVBO = NULL;
+}
+
+void ThreadedVBO::UpdateVertexData( const VertexBufferData& buffer )
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ if (m_ClientDevice.IsSerializing())
+ {
+ GetCommandQueue().WriteValueType<GfxCommand>(kGfxCmd_VBO_UpdateVertexData);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ GetCommandQueue().WriteValueType<ClientDeviceVBO>(*m_ClientVBO);
+ ClientVertexBufferData client;
+ memcpy (client.channels, buffer.channels, sizeof(ChannelInfoArray));
+ memcpy (client.streams, buffer.streams, sizeof(StreamInfoArray));
+ client.bufferSize = buffer.bufferSize;
+ client.vertexCount = buffer.vertexCount;
+ client.hasData = buffer.buffer != NULL;
+ GetCommandQueue().WriteValueType<ClientVertexBufferData>(client);
+#else
+ GetCommandQueue().WriteValueType<ClientDeviceVBO*>(m_ClientVBO);
+ GetCommandQueue().WriteValueType<VertexBufferData>(buffer);
+#endif
+ if (buffer.buffer)
+ m_ClientDevice.WriteBufferData(buffer.buffer, buffer.bufferSize);
+ if (m_MappedFromRenderThread)
+ UnbufferVertexData();
+ else
+ BufferAccessibleVertexData(buffer);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ DebugAssert(m_NonThreadedVBO);
+ m_NonThreadedVBO->UpdateVertexData(buffer);
+ }
+ m_VertexBufferSize = buffer.bufferSize;
+ m_VertexBufferLost = false;
+}
+
+void ThreadedVBO::UpdateIndexData (const IndexBufferData& buffer)
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ if (m_ClientDevice.IsSerializing())
+ {
+ GetCommandQueue().WriteValueType<GfxCommand>(kGfxCmd_VBO_UpdateIndexData);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ GetCommandQueue().WriteValueType<ClientDeviceVBO>(*m_ClientVBO);
+#else
+ GetCommandQueue().WriteValueType<ClientDeviceVBO*>(m_ClientVBO);
+#endif
+ GetCommandQueue().WriteValueType<int>(buffer.count);
+ GetCommandQueue().WriteValueType<UInt32>(buffer.hasTopologies);
+ m_ClientDevice.WriteBufferData(buffer.indices, CalculateIndexBufferSize(buffer));
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ DebugAssert(m_NonThreadedVBO);
+ m_NonThreadedVBO->UpdateIndexData(buffer);
+ }
+ m_IndexBufferSize = buffer.count * kVBOIndexSize;
+ m_IndexBufferLost = false;
+}
+
+void ThreadedVBO::DrawVBO (const ChannelAssigns& channels, UInt32 firstIndexByte, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 firstVertex, UInt32 vertexCount )
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ m_ClientDevice.BeforeDrawCall(false);
+ if (m_ClientDevice.IsSerializing())
+ {
+ GetCommandQueue().WriteValueType<GfxCommand>(kGfxCmd_VBO_Draw);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ GfxCmdVBODraw data = { *m_ClientVBO, channels, firstIndexByte, indexCount, topology, firstVertex, vertexCount };
+#else
+ GfxCmdVBODraw data = { m_ClientVBO, channels, firstIndexByte, indexCount, topology, firstVertex, vertexCount };
+#endif
+ GetCommandQueue().WriteValueType<GfxCmdVBODraw>(data);
+ GetCommandQueue().WriteSubmitData();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ DebugAssert(m_NonThreadedVBO);
+ m_NonThreadedVBO->DrawVBO (channels, firstIndexByte, indexCount, topology, firstVertex, vertexCount);
+ }
+}
+
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+void ThreadedVBO::DrawCustomIndexed( const ChannelAssigns& channels, void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount )
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ m_ClientDevice.BeforeDrawCall(false);
+ if (m_ClientDevice.IsSerializing())
+ {
+ //Note: Presuming that indices are of size UInt16!
+ GetCommandQueue().WriteValueType<GfxCommand>(kGfxCmd_VBO_DrawCustomIndexed);
+ GfxCmdVBODrawCustomIndexed data = { m_ClientVBO, channels, indexCount, topology, vertexRangeBegin, vertexRangeEnd, drawVertexCount };
+ GetCommandQueue().WriteValueType<GfxCmdVBODrawCustomIndexed>(data);
+ GetCommandQueue().WriteStreamingData(indices, indexCount*kVBOIndexSize);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ DebugAssert(m_NonThreadedVBO);
+ m_NonThreadedVBO->DrawCustomIndexed(channels, indices, indexCount, topology,
+ vertexRangeBegin, vertexRangeEnd, drawVertexCount);
+ }
+}
+#endif
+
+void ThreadedVBO::Recreate()
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ if (m_ClientDevice.IsSerializing())
+ {
+ GetCommandQueue().WriteValueType<GfxCommand>(kGfxCmd_VBO_Recreate);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ GetCommandQueue().WriteValueType<ClientDeviceVBO>(*m_ClientVBO);
+#else
+ GetCommandQueue().WriteValueType<ClientDeviceVBO*>(m_ClientVBO);
+#endif
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ DebugAssert(m_NonThreadedVBO);
+ m_NonThreadedVBO->Recreate();
+ }
+}
+
+bool ThreadedVBO::MapVertexStream( VertexStreamData& outData, unsigned stream )
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ if (m_ClientDevice.IsSerializing())
+ {
+ BufferedVBO::MapVertexStream(outData, stream);
+ }
+ else
+ {
+ DebugAssert(m_NonThreadedVBO);
+ m_IsStreamMapped[stream] = m_NonThreadedVBO->MapVertexStream(outData, stream);
+ }
+ return m_IsStreamMapped[stream];
+}
+
+void ThreadedVBO::UnmapVertexStream( unsigned stream )
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ if (m_ClientDevice.IsSerializing())
+ {
+ BufferedVBO::UnmapVertexStream(stream);
+
+ // Send modified vertices to render thread
+ size_t size = CalculateVertexStreamSize(m_VertexData, stream);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ GfxCmdVBOMapVertexStream map = { *m_ClientVBO, stream, size };
+#else
+ GfxCmdVBOMapVertexStream map = { m_ClientVBO, stream, size };
+#endif
+ GetCommandQueue().WriteValueType<GfxCommand>(kGfxCmd_VBO_MapVertexStream);
+ GetCommandQueue().WriteValueType<GfxCmdVBOMapVertexStream>(map);
+ GetCommandQueue().WriteStreamingData(GetStreamBuffer(stream), size);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ DebugAssert(m_NonThreadedVBO);
+ m_NonThreadedVBO->UnmapVertexStream(stream);
+ }
+ m_IsStreamMapped[0] = false;
+}
+
+bool ThreadedVBO::IsVertexBufferLost() const
+{
+ if (m_NonThreadedVBO)
+ return m_NonThreadedVBO->IsVertexBufferLost();
+ else
+ return m_VertexBufferLost;
+}
+
+bool ThreadedVBO::IsIndexBufferLost() const
+{
+ if (m_NonThreadedVBO)
+ return m_NonThreadedVBO->IsIndexBufferLost();
+ else
+ return m_IndexBufferLost;
+}
+
+void ThreadedVBO::SetMappedFromRenderThread( bool renderThread )
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ m_MappedFromRenderThread = renderThread;
+#endif
+}
+
+void ThreadedVBO::SetVertexStreamMode( unsigned stream, StreamMode mode )
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ if (mode == GetVertexStreamMode(stream))
+ return;
+ VBO::SetVertexStreamMode(stream, mode);
+ if (m_ClientDevice.IsSerializing())
+ {
+ GetCommandQueue().WriteValueType<GfxCommand>(kGfxCmd_VBO_SetVertexStreamMode);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ GfxCmdVBOSetVertexStreamMode vsmode = { *m_ClientVBO, stream, mode };
+#else
+ GfxCmdVBOSetVertexStreamMode vsmode = { m_ClientVBO, stream, mode };
+#endif
+ GetCommandQueue().WriteValueType<GfxCmdVBOSetVertexStreamMode>(vsmode);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ DebugAssert(m_NonThreadedVBO);
+ m_NonThreadedVBO->SetVertexStreamMode(stream, mode);
+ }
+}
+
+void ThreadedVBO::SetIndicesDynamic(bool dynamic)
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ if (dynamic == AreIndicesDynamic())
+ return;
+ VBO::SetIndicesDynamic(dynamic);
+ if (m_ClientDevice.IsSerializing())
+ {
+ GetCommandQueue().WriteValueType<GfxCommand>(kGfxCmd_VBO_SetIndicesDynamic);
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ GfxCmdVBOSetSetIndicesDynamic vsdyn = { *m_ClientVBO, (int)dynamic };
+#else
+ GfxCmdVBOSetSetIndicesDynamic vsdyn = { m_ClientVBO, (int)dynamic };
+#endif
+ GetCommandQueue().WriteValueType<GfxCmdVBOSetSetIndicesDynamic>(vsdyn);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ DebugAssert(m_NonThreadedVBO);
+ m_NonThreadedVBO->SetIndicesDynamic(dynamic);
+ }
+}
+
+void ThreadedVBO::ResetDynamicVB()
+{
+ for (int s = 0; s < kMaxVertexStreams; s++)
+ {
+ if (m_StreamModes[s] == kStreamModeDynamic)
+ m_VertexBufferLost = true;
+ }
+}
+
+void ThreadedVBO::MarkBuffersLost()
+{
+ m_VertexBufferLost = m_IndexBufferLost = true;
+}
+
+
+int ThreadedVBO::GetRuntimeMemorySize() const
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ if (m_ClientDevice.IsSerializing())
+ {
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+ return 0;
+#else
+ return m_ClientVBO->GetInternal()?m_ClientVBO->GetInternal()->GetRuntimeMemorySize():0;
+#endif
+ }
+ else
+ {
+ DebugAssert(m_NonThreadedVBO);
+ return m_NonThreadedVBO->GetRuntimeMemorySize();
+ }
+}
+
+void ThreadedVBO::UseAsStreamOutput()
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ if (m_ClientDevice.IsSerializing())
+ {
+ GetCommandQueue().WriteValueType<GfxCommand>(kGfxCmd_VBO_UseAsStreamOutput);
+ GetCommandQueue().WriteValueType<ClientDeviceVBO *>(m_ClientVBO);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ DebugAssert(m_NonThreadedVBO);
+ m_NonThreadedVBO->UseAsStreamOutput();
+ }
+
+}
+
+#if UNITY_XENON
+void ThreadedVBO::AddExtraUvChannels( const UInt8* data, UInt32 size, int extraUvCount )
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ if (m_ClientDevice.IsSerializing())
+ {
+ GetCommandQueue().WriteValueType<GfxCommand>(kGfxCmd_VBO_AddExtraUvChannels);
+ GfxCmdVBOAddExtraUvChannels adduv = { m_ClientVBO, size, extraUvCount };
+ GetCommandQueue().WriteValueType<GfxCmdVBOAddExtraUvChannels>(adduv);
+ GetCommandQueue().WriteStreamingData(data, size);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ DebugAssert(m_NonThreadedVBO);
+ m_NonThreadedVBO->AddExtraUvChannels(data, size, extraUvCount);
+ }
+}
+
+void ThreadedVBO::CopyExtraUvChannels( VBO* source )
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ DebugAssert(source != NULL);
+ ThreadedVBO* src = static_cast<ThreadedVBO*>(source);
+ if (m_ClientDevice.IsSerializing())
+ {
+ GetCommandQueue().WriteValueType<GfxCommand>(kGfxCmd_VBO_CopyExtraUvChannels);
+ GfxCmdVBOCopyExtraUvChannels copyuv = { m_ClientVBO, src->m_ClientVBO };
+ GetCommandQueue().WriteValueType<GfxCmdVBOCopyExtraUvChannels>(copyuv);
+ GFXDEVICE_LOCKSTEP_CLIENT();
+ }
+ else
+ {
+ DebugAssert(m_NonThreadedVBO);
+ m_NonThreadedVBO->CopyExtraUvChannels(src->m_NonThreadedVBO);
+ }
+}
+#endif
+
+ThreadedStreamBuffer& ThreadedVBO::GetCommandQueue()
+{
+ return *m_ClientDevice.GetCommandQueue();
+}
+
+GfxDeviceWorker* ThreadedVBO::GetGfxDeviceWorker()
+{
+ return m_ClientDevice.GetGfxDeviceWorker();
+}
+
+void ThreadedVBO::SubmitCommands()
+{
+ m_ClientDevice.SubmitCommands();
+}
+
+void ThreadedVBO::DoLockstep()
+{
+ m_ClientDevice.DoLockstep();
+}
+
+
+ThreadedDynamicVBO::ThreadedDynamicVBO(GfxDeviceClient& device) :
+ m_ClientDevice(device),
+ m_ValidChunk(false)
+{
+}
+
+bool ThreadedDynamicVBO::GetChunk( UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode renderMode, void** outVB, void** outIB )
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ Assert( !m_LendedChunk );
+ DebugAssert( outVB != NULL && maxVertices > 0 );
+ DebugAssert(
+ (renderMode == kDrawIndexedTriangles && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedTriangleStrip && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawTriangleStrip && (outIB == NULL && maxIndices == 0)) ||
+ (renderMode == kDrawQuads && (outIB == NULL && maxIndices == 0)));
+
+ m_LendedChunk = true;
+ m_LastChunkShaderChannelMask = shaderChannelMask;
+ m_LastRenderMode = renderMode;
+
+ m_LastChunkStride = 0;
+ for( int i = 0; i < kShaderChannelCount; ++i ) {
+ if( shaderChannelMask & (1<<i) )
+ m_LastChunkStride += VBO::GetDefaultChannelByteSize(i);
+ }
+ m_LastChunkVertices = maxVertices;
+ m_LastChunkIndices = maxIndices;
+ int vertexChunkSize = m_LastChunkStride * maxVertices;
+ m_ChunkVertices.resize_uninitialized(vertexChunkSize);
+ m_ChunkIndices.resize_uninitialized(maxIndices);
+ *outVB = &m_ChunkVertices[0];
+ if (outIB)
+ *outIB = &m_ChunkIndices[0];
+ return true;
+}
+
+void ThreadedDynamicVBO::ReleaseChunk( UInt32 actualVertices, UInt32 actualIndices )
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ Assert( m_LendedChunk );
+ m_LendedChunk = false;
+ m_ValidChunk = (actualVertices > 0) && (m_LastChunkIndices == 0 || actualIndices > 0);
+ if (!m_ValidChunk)
+ return;
+ Assert(actualVertices <= m_LastChunkVertices);
+ Assert(actualIndices <= m_LastChunkIndices);
+ GetCommandQueue().WriteValueType<GfxCommand>(kGfxCmd_DynVBO_Chunk);
+ GfxCmdDynVboChunk chunk = { m_LastChunkShaderChannelMask, m_LastChunkStride, actualVertices, actualIndices, m_LastRenderMode };
+ GetCommandQueue().WriteValueType<GfxCmdDynVboChunk>(chunk);
+ GetCommandQueue().WriteStreamingData(&m_ChunkVertices[0], actualVertices * m_LastChunkStride);
+ if (actualIndices > 0)
+ GetCommandQueue().WriteStreamingData(&m_ChunkIndices[0], actualIndices * kVBOIndexSize);
+ GetCommandQueue().WriteSubmitData();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+}
+
+void ThreadedDynamicVBO::DrawChunk (const ChannelAssigns& channels)
+{
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+ Assert( !m_LendedChunk );
+ if (!m_ValidChunk)
+ return;
+ m_ClientDevice.BeforeDrawCall(false);
+ GetCommandQueue().WriteValueType<GfxCommand>(kGfxCmd_DynVBO_DrawChunk);
+ GetCommandQueue().WriteValueType<ChannelAssigns>(channels);
+ GetCommandQueue().WriteSubmitData();
+ GFXDEVICE_LOCKSTEP_CLIENT();
+}
+
+ThreadedStreamBuffer& ThreadedDynamicVBO::GetCommandQueue()
+{
+ return *m_ClientDevice.GetCommandQueue();
+}
+
+GfxDeviceWorker* ThreadedDynamicVBO::GetGfxDeviceWorker()
+{
+ return m_ClientDevice.GetGfxDeviceWorker();
+}
+
+void ThreadedDynamicVBO::SubmitCommands()
+{
+ m_ClientDevice.SubmitCommands();
+}
+
+void ThreadedDynamicVBO::DoLockstep()
+{
+ m_ClientDevice.DoLockstep();
+}
diff --git a/Runtime/GfxDevice/threaded/ThreadedVBO.h b/Runtime/GfxDevice/threaded/ThreadedVBO.h
new file mode 100644
index 0000000..89dbeff
--- /dev/null
+++ b/Runtime/GfxDevice/threaded/ThreadedVBO.h
@@ -0,0 +1,104 @@
+#ifndef THREADEDVBO_H
+#define THREADEDVBO_H
+
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Shaders/BufferedVBO.h"
+
+class Mutex;
+class GfxDeviceClient;
+struct ClientDeviceVBO;
+class GfxDeviceWorker;
+class ThreadedStreamBuffer;
+
+class ThreadedVBO : public BufferedVBO {
+public:
+ ThreadedVBO(GfxDeviceClient& device);
+ virtual ~ThreadedVBO();
+
+ virtual void UpdateVertexData( const VertexBufferData& buffer );
+ virtual void UpdateIndexData (const IndexBufferData& buffer);
+ virtual void DrawVBO (const ChannelAssigns& channels, UInt32 firstIndexByte, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 firstVertex, UInt32 vertexCount );
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ virtual void DrawCustomIndexed( const ChannelAssigns& channels, void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount );
+ #endif
+
+ virtual void Recreate();
+
+ // For writing directly to VBO. VBO must be filled (UpdateData)
+ // at least once; and vertex layout + topology from the last fill
+ // is used. For example, for skinned meshes you have to call
+ // UpdateData on start and each time layout/topology changes;
+ // then map,write,unmap for each skinning.
+ //
+ // In some situations a vertex buffer might become lost; then you need to do UpdateData
+ // again before using Map.
+ virtual bool MapVertexStream( VertexStreamData& outData, unsigned stream );
+ virtual void UnmapVertexStream( unsigned stream );
+ virtual bool IsVertexBufferLost() const;
+ virtual bool IsIndexBufferLost() const;
+
+ virtual void SetMappedFromRenderThread( bool renderThread );
+ virtual void SetVertexStreamMode( unsigned stream, StreamMode mode );
+ virtual void SetIndicesDynamic(bool dynamic);
+
+ virtual void ResetDynamicVB();
+ virtual void MarkBuffersLost();
+
+ virtual int GetRuntimeMemorySize() const;
+
+ virtual void UseAsStreamOutput();
+
+#if UNITY_XENON
+ virtual void AddExtraUvChannels( const UInt8* data, UInt32 size, int extraUvCount );
+ virtual void CopyExtraUvChannels( VBO* source );
+#endif
+
+ ClientDeviceVBO* GetClientDeviceVBO() { return m_ClientVBO; } //Todo:any nicer way?
+ VBO* GetNonThreadedVBO() { return m_NonThreadedVBO; }
+
+protected:
+ ThreadedStreamBuffer& GetCommandQueue();
+ GfxDeviceWorker* GetGfxDeviceWorker();
+ void SubmitCommands();
+ void DoLockstep();
+
+ GfxDeviceClient& m_ClientDevice;
+ ClientDeviceVBO* m_ClientVBO;
+ VBO* m_NonThreadedVBO;
+ int m_VertexBufferSize;
+ int m_IndexBufferSize;
+ bool m_MappedFromRenderThread;
+ bool m_VertexBufferLost;
+ bool m_IndexBufferLost;
+};
+
+
+class ThreadedDynamicVBO : public DynamicVBO {
+public:
+ ThreadedDynamicVBO(GfxDeviceClient& device);
+ virtual ~ThreadedDynamicVBO() { }
+
+ virtual bool GetChunk( UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode renderMode, void** outVB, void** outIB );
+
+ virtual void ReleaseChunk( UInt32 actualVertices, UInt32 actualIndices );
+
+ virtual void DrawChunk (const ChannelAssigns& channels);
+
+private:
+ ThreadedStreamBuffer& GetCommandQueue();
+ GfxDeviceWorker* GetGfxDeviceWorker();
+ void SubmitCommands();
+ void DoLockstep();
+
+ GfxDeviceClient& m_ClientDevice;
+ ThreadedStreamBuffer* m_CommandQueue;
+ dynamic_array<UInt8> m_ChunkVertices;
+ dynamic_array<UInt16> m_ChunkIndices;
+ bool m_ValidChunk;
+};
+
+
+#endif
diff --git a/Runtime/GfxDevice/threaded/ThreadedWindow.cpp b/Runtime/GfxDevice/threaded/ThreadedWindow.cpp
new file mode 100644
index 0000000..bab324f
--- /dev/null
+++ b/Runtime/GfxDevice/threaded/ThreadedWindow.cpp
@@ -0,0 +1,97 @@
+#include "UnityPrefix.h"
+#include "Runtime/GfxDevice/threaded/ThreadedWindow.h"
+#include "Runtime/GfxDevice/threaded/GfxDeviceClient.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Misc/QualitySettings.h"
+
+
+#if UNITY_WIN && UNITY_EDITOR
+
+int ThreadedWindow::ms_CurrentFSAALevel = 0;
+
+ThreadedWindow::ThreadedWindow(HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias )
+: GfxDeviceWindow(window, width, height, depthFormat, antiAlias)
+{
+ m_ClientWindow = new ClientDeviceWindow;
+ m_FSAALevel = antiAlias;
+ m_Reshaped = false;
+
+ // Creating the actual window calls Reshape on the base class
+ // Threaded window should be kept in the same state
+ GfxDeviceWindow::Reshape(width, height, depthFormat, antiAlias);
+}
+
+ThreadedWindow::~ThreadedWindow()
+{
+ GfxDeviceClient& device = (GfxDeviceClient&)GetGfxDevice();
+ device.WindowDestroy(m_ClientWindow);
+ m_ClientWindow = NULL;
+}
+
+bool ThreadedWindow::Reshape( int width, int height, DepthBufferFormat depthFormat, int antiAlias )
+{
+ if(!GfxDeviceWindow::Reshape(width, height, depthFormat, antiAlias))
+ return false;
+
+ GfxDeviceClient& device = (GfxDeviceClient&)GetGfxDevice();
+ device.WindowReshape(m_ClientWindow, width, height, depthFormat, antiAlias);
+ m_Reshaped = true;
+ return true;
+}
+
+void ThreadedWindow::SetAsActiveWindow ()
+{
+ GfxDeviceClient& device = (GfxDeviceClient&)GetGfxDevice();
+ device.SetActiveWindow(m_ClientWindow);
+ OnActivateWindow();
+}
+
+bool ThreadedWindow::BeginRendering()
+{
+ if (GfxDeviceWindow::BeginRendering())
+ {
+ GfxDeviceClient& device = (GfxDeviceClient&)GetGfxDevice();
+ device.BeginRendering(m_ClientWindow);
+ OnActivateWindow();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool ThreadedWindow::EndRendering( bool presentContent )
+{
+ if(GfxDeviceWindow::EndRendering(presentContent))
+ {
+ GfxDeviceClient& device = (GfxDeviceClient&)GetGfxDevice();
+ device.EndRendering(m_ClientWindow, presentContent);
+ if (m_Reshaped)
+ {
+ GfxDeviceRenderer renderer = device.GetRenderer();
+ // We need to complete rendering on WM_PAINT after window was resized
+ // otherwise contents will look stretched in DirectX mode
+ if (renderer == kGfxRendererD3D9 || renderer == kGfxRendererD3D11)
+ device.FinishRendering();
+ m_Reshaped = false;
+ }
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void ThreadedWindow::OnActivateWindow()
+{
+ GfxDeviceClient& device = (GfxDeviceClient&)GetGfxDevice();
+ device.SetActiveRenderTexture(NULL);
+ device.SetCurrentWindowSize(m_Width, m_Height);
+ device.SetInvertProjectionMatrix(false);
+ ms_CurrentFSAALevel = m_FSAALevel;
+
+}
+
+#endif
diff --git a/Runtime/GfxDevice/threaded/ThreadedWindow.h b/Runtime/GfxDevice/threaded/ThreadedWindow.h
new file mode 100644
index 0000000..60489f4
--- /dev/null
+++ b/Runtime/GfxDevice/threaded/ThreadedWindow.h
@@ -0,0 +1,36 @@
+#ifndef THREADEDWINDOW_H
+#define THREADEDWINDOW_H
+
+#if UNITY_WIN && UNITY_EDITOR
+
+#include "Runtime/GfxDevice/GfxDeviceWindow.h"
+#include "ThreadedDeviceStates.h"
+
+class ThreadedWindow : public GfxDeviceWindow
+{
+public:
+ ThreadedWindow( HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias );
+ ~ThreadedWindow();
+
+ bool Reshape( int width, int height, DepthBufferFormat depthFormat, int antiAlias );
+
+ bool BeginRendering();
+ bool EndRendering( bool presentContent );
+ void SetAsActiveWindow();
+
+ static int GetCurrentFSAALevel() { return ms_CurrentFSAALevel; }
+
+private:
+ void OnActivateWindow();
+
+ friend class GfxDeviceClient;
+ friend class GfxDeviceWorker;
+
+ ClientDeviceWindow* m_ClientWindow;
+ int m_FSAALevel;
+ bool m_Reshaped;
+ static int ms_CurrentFSAALevel;
+};
+
+#endif
+#endif
diff --git a/Runtime/GfxDevice/threaded/WorkerIDMapper.h b/Runtime/GfxDevice/threaded/WorkerIDMapper.h
new file mode 100644
index 0000000..9b563f5
--- /dev/null
+++ b/Runtime/GfxDevice/threaded/WorkerIDMapper.h
@@ -0,0 +1,33 @@
+#ifndef WORKERIDMAPPER_H
+#define WORKERIDMAPPER_H
+
+#include "ClientIDMapper.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+template <class T>
+class WorkerIDMapper {
+public:
+
+ WorkerIDMapper ()
+ {
+ (*this)[0] = NULL;
+ }
+
+ T*& operator [] (ClientIDMapper::ClientID cid)
+ {
+ if (m_IDMapping.size() <= cid)
+ m_IDMapping.resize_uninitialized(cid+1);
+ return m_IDMapping[cid];
+ }
+
+private:
+ dynamic_array<T*> m_IDMapping;
+};
+
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS
+#define WorkerIDWrapper(type,val) m_##type##Mapper[val]
+#else
+#define WorkerIDWrapper(type,val) val
+#endif
+
+#endif \ No newline at end of file
diff --git a/Runtime/Graphics/CubemapProcessor.cpp b/Runtime/Graphics/CubemapProcessor.cpp
new file mode 100644
index 0000000..2cf535e
--- /dev/null
+++ b/Runtime/Graphics/CubemapProcessor.cpp
@@ -0,0 +1,515 @@
+#include "UnityPrefix.h"
+#include "CubemapProcessor.h"
+
+//--------------------------------------------------------------------------------------
+//Based on CCubeMapProcessor from CubeMapGen v1.4
+// Class for filtering and processing cubemaps
+//
+//
+//--------------------------------------------------------------------------------------
+// (C) 2005 ATI Research, Inc., All rights reserved.
+//--------------------------------------------------------------------------------------
+
+//used to index cube faces
+#define CP_FACE_X_POS 0
+#define CP_FACE_X_NEG 1
+#define CP_FACE_Y_POS 2
+#define CP_FACE_Y_NEG 3
+#define CP_FACE_Z_POS 4
+#define CP_FACE_Z_NEG 5
+
+//used to index image edges
+// NOTE.. the actual number corresponding to the edge is important
+// do not change these, or the code will break
+//
+// CP_EDGE_LEFT is u = 0
+// CP_EDGE_RIGHT is u = width-1
+// CP_EDGE_TOP is v = 0
+// CP_EDGE_BOTTOM is v = height-1
+#define CP_EDGE_LEFT 0
+#define CP_EDGE_RIGHT 1
+#define CP_EDGE_TOP 2
+#define CP_EDGE_BOTTOM 3
+
+//corners of CUBE map (P or N specifys if it corresponds to the
+// positive or negative direction each of X, Y, and Z
+#define CP_CORNER_NNN 0
+#define CP_CORNER_NNP 1
+#define CP_CORNER_NPN 2
+#define CP_CORNER_NPP 3
+#define CP_CORNER_PNN 4
+#define CP_CORNER_PNP 5
+#define CP_CORNER_PPN 6
+#define CP_CORNER_PPP 7
+
+
+//information about cube maps neighboring face after traversing
+// across an edge
+struct CPCubeMapNeighbor
+{
+ UInt8 m_Face; //index of neighboring face
+ UInt8 m_Edge; //edge in neighboring face that abuts this face
+};
+
+//------------------------------------------------------------------------------
+// D3D cube map face specification
+// mapping from 3D x,y,z cube map lookup coordinates
+// to 2D within face u,v coordinates
+//
+// --------------------> U direction
+// | (within-face texture space)
+// | _____
+// | | |
+// | | +Y |
+// | _____|_____|_____ _____
+// | | | | | |
+// | | -X | +Z | +X | -Z |
+// | |_____|_____|_____|_____|
+// | | |
+// | | -Y |
+// | |_____|
+// |
+// v V direction
+// (within-face texture space)
+//------------------------------------------------------------------------------
+
+//Information about neighbors and how texture coorrdinates change across faces
+// in ORDER of left, right, top, bottom (e.g. edges corresponding to u=0,
+// u=1, v=0, v=1 in the 2D coordinate system of the particular face.
+//Note this currently assumes the D3D cube face ordering and orientation
+CPCubeMapNeighbor sg_CubeNgh[6][4] =
+{
+ //XPOS face
+ {{CP_FACE_Z_POS, CP_EDGE_RIGHT },
+ {CP_FACE_Z_NEG, CP_EDGE_LEFT },
+ {CP_FACE_Y_POS, CP_EDGE_RIGHT },
+ {CP_FACE_Y_NEG, CP_EDGE_RIGHT }},
+ //XNEG face
+ {{CP_FACE_Z_NEG, CP_EDGE_RIGHT },
+ {CP_FACE_Z_POS, CP_EDGE_LEFT },
+ {CP_FACE_Y_POS, CP_EDGE_LEFT },
+ {CP_FACE_Y_NEG, CP_EDGE_LEFT }},
+ //YPOS face
+ {{CP_FACE_X_NEG, CP_EDGE_TOP },
+ {CP_FACE_X_POS, CP_EDGE_TOP },
+ {CP_FACE_Z_NEG, CP_EDGE_TOP },
+ {CP_FACE_Z_POS, CP_EDGE_TOP }},
+ //YNEG face
+ {{CP_FACE_X_NEG, CP_EDGE_BOTTOM},
+ {CP_FACE_X_POS, CP_EDGE_BOTTOM},
+ {CP_FACE_Z_POS, CP_EDGE_BOTTOM},
+ {CP_FACE_Z_NEG, CP_EDGE_BOTTOM}},
+ //ZPOS face
+ {{CP_FACE_X_NEG, CP_EDGE_RIGHT },
+ {CP_FACE_X_POS, CP_EDGE_LEFT },
+ {CP_FACE_Y_POS, CP_EDGE_BOTTOM },
+ {CP_FACE_Y_NEG, CP_EDGE_TOP }},
+ //ZNEG face
+ {{CP_FACE_X_POS, CP_EDGE_RIGHT },
+ {CP_FACE_X_NEG, CP_EDGE_LEFT },
+ {CP_FACE_Y_POS, CP_EDGE_TOP },
+ {CP_FACE_Y_NEG, CP_EDGE_BOTTOM }}
+};
+
+
+//The 12 edges of the cubemap, (entries are used to index into the neighbor table)
+// this table is used to average over the edges.
+int sg_CubeEdgeList[12][2] = {
+ {CP_FACE_X_POS, CP_EDGE_LEFT},
+ {CP_FACE_X_POS, CP_EDGE_RIGHT},
+ {CP_FACE_X_POS, CP_EDGE_TOP},
+ {CP_FACE_X_POS, CP_EDGE_BOTTOM},
+
+ {CP_FACE_X_NEG, CP_EDGE_LEFT},
+ {CP_FACE_X_NEG, CP_EDGE_RIGHT},
+ {CP_FACE_X_NEG, CP_EDGE_TOP},
+ {CP_FACE_X_NEG, CP_EDGE_BOTTOM},
+
+ {CP_FACE_Z_POS, CP_EDGE_TOP},
+ {CP_FACE_Z_POS, CP_EDGE_BOTTOM},
+ {CP_FACE_Z_NEG, CP_EDGE_TOP},
+ {CP_FACE_Z_NEG, CP_EDGE_BOTTOM}
+};
+
+
+//Information about which of the 8 cube corners are correspond to the
+// the 4 corners in each cube face
+// the order is upper left, upper right, lower left, lower right
+int sg_CubeCornerList[6][4] = {
+ { CP_CORNER_PPP, CP_CORNER_PPN, CP_CORNER_PNP, CP_CORNER_PNN }, // XPOS face
+ { CP_CORNER_NPN, CP_CORNER_NPP, CP_CORNER_NNN, CP_CORNER_NNP }, // XNEG face
+ { CP_CORNER_NPN, CP_CORNER_PPN, CP_CORNER_NPP, CP_CORNER_PPP }, // YPOS face
+ { CP_CORNER_NNP, CP_CORNER_PNP, CP_CORNER_NNN, CP_CORNER_PNN }, // YNEG face
+ { CP_CORNER_NPP, CP_CORNER_PPP, CP_CORNER_NNP, CP_CORNER_PNP }, // ZPOS face
+ { CP_CORNER_PPN, CP_CORNER_NPN, CP_CORNER_PNN, CP_CORNER_NNN } // ZNEG face
+};
+
+
+
+//==========================================================================================================
+//void FixupCubeEdges(CImageSurface *a_CubeMap, int a_FixupType, int a_FixupWidth);
+//
+//Apply edge fixup to a cubemap mip level.
+//
+//a_CubeMap [in/out] Array of 6 images comprising cubemap miplevel to apply edge fixup to.
+//a_FixupType [in] Specifies the technique used for edge fixup. Choose one of the following,
+// CP_FIXUP_NONE, CP_FIXUP_PULL_LINEAR, CP_FIXUP_PULL_HERMITE, CP_FIXUP_AVERAGE_LINEAR,
+// CP_FIXUP_AVERAGE_HERMITE
+//a_FixupWidth [in] Fixup width in texels
+//
+//==========================================================================================================
+
+
+//--------------------------------------------------------------------------------------
+// Fixup cube edges
+//
+// average texels on cube map faces across the edges
+//--------------------------------------------------------------------------------------
+
+void FixupCubeEdges(CImageSurface *a_CubeMap, int a_FixupType, int a_FixupWidth)
+{
+ int i, j, k;
+ int face;
+ int edge;
+ int neighborFace;
+ int neighborEdge;
+
+ int nChannels = a_CubeMap[0].m_NumChannels;
+ int size = a_CubeMap[0].m_Width;
+
+ CPCubeMapNeighbor neighborInfo;
+
+ CP_ITYPE* edgeStartPtr;
+ CP_ITYPE* neighborEdgeStartPtr;
+
+ int edgeWalk;
+ int neighborEdgeWalk;
+
+ //pointer walk to walk one texel away from edge in perpendicular direction
+ int edgePerpWalk;
+ int neighborEdgePerpWalk;
+
+ //number of texels inward towards cubeface center to apply fixup to
+ int fixupDist;
+ int iFixup;
+
+ // note that if functionality to filter across the three texels for each corner, then
+ CP_ITYPE *cornerPtr[8][3]; //indexed by corner and face idx
+ CP_ITYPE *faceCornerPtrs[4]; //corner pointers for face
+ int cornerNumPtrs[8]; //indexed by corner and face idx
+ int iCorner; //corner iterator
+ int iFace; //iterator for faces
+ int corner;
+
+ //if there is no fixup, or fixup width = 0, do nothing
+ if((a_FixupType == CP_FIXUP_NONE) ||
+ (a_FixupWidth == 0) )
+ {
+ return;
+ }
+
+ //special case 1x1 cubemap, average face colors
+ if( a_CubeMap[0].m_Width == 1 )
+ {
+ //iterate over channels
+ for(k=0; k<nChannels; k++)
+ {
+ CP_ITYPE accum = 0.0f;
+
+ //iterate over faces to accumulate face colors
+ for(iFace=0; iFace<6; iFace++)
+ {
+ accum += *(a_CubeMap[iFace].m_ImgData + k);
+ }
+
+ //compute average over 6 face colors
+ accum /= 6.0f;
+
+ //iterate over faces to distribute face colors
+ for(iFace=0; iFace<6; iFace++)
+ {
+ *(a_CubeMap[iFace].m_ImgData + k) = accum;
+ }
+ }
+
+ return;
+ }
+
+
+ //iterate over corners
+ for(iCorner = 0; iCorner < 8; iCorner++ )
+ {
+ cornerNumPtrs[iCorner] = 0;
+ }
+
+ //iterate over faces to collect list of corner texel pointers
+ for(iFace=0; iFace<6; iFace++ )
+ {
+ //the 4 corner pointers for this face
+ faceCornerPtrs[0] = a_CubeMap[iFace].m_ImgData;
+ faceCornerPtrs[1] = a_CubeMap[iFace].m_ImgData + ( (size - 1) * nChannels );
+ faceCornerPtrs[2] = a_CubeMap[iFace].m_ImgData + ( (size) * (size - 1) * nChannels );
+ faceCornerPtrs[3] = a_CubeMap[iFace].m_ImgData + ( (((size) * (size - 1)) + (size - 1)) * nChannels );
+
+ //iterate over face corners to collect cube corner pointers
+ for(i=0; i<4; i++ )
+ {
+ corner = sg_CubeCornerList[iFace][i];
+ cornerPtr[corner][ cornerNumPtrs[corner] ] = faceCornerPtrs[i];
+ cornerNumPtrs[corner]++;
+ }
+ }
+
+
+ //iterate over corners to average across corner tap values
+ for(iCorner = 0; iCorner < 8; iCorner++ )
+ {
+ for(k=0; k<nChannels; k++)
+ {
+ CP_ITYPE cornerTapAccum;
+
+ cornerTapAccum = 0.0f;
+
+ //iterate over corner texels and average results
+ for(i=0; i<3; i++ )
+ {
+ cornerTapAccum += *(cornerPtr[iCorner][i] + k);
+ }
+
+ //divide by 3 to compute average of corner tap values
+ cornerTapAccum *= (1.0f / 3.0f);
+
+ //iterate over corner texels and average results
+ for(i=0; i<3; i++ )
+ {
+ *(cornerPtr[iCorner][i] + k) = cornerTapAccum;
+ }
+ }
+ }
+
+
+ //maximum width of fixup region is one half of the cube face size
+ fixupDist = std::min( a_FixupWidth, size / 2);
+
+ //iterate over the twelve edges of the cube to average across edges
+ for(i=0; i<12; i++)
+ {
+ face = sg_CubeEdgeList[i][0];
+ edge = sg_CubeEdgeList[i][1];
+
+ neighborInfo = sg_CubeNgh[face][edge];
+ neighborFace = neighborInfo.m_Face;
+ neighborEdge = neighborInfo.m_Edge;
+
+ edgeStartPtr = a_CubeMap[face].m_ImgData;
+ neighborEdgeStartPtr = a_CubeMap[neighborFace].m_ImgData;
+ edgeWalk = 0;
+ neighborEdgeWalk = 0;
+
+ //amount to pointer to sample taps away from cube face
+ edgePerpWalk = 0;
+ neighborEdgePerpWalk = 0;
+
+ //Determine walking pointers based on edge type
+ // e.g. CP_EDGE_LEFT, CP_EDGE_RIGHT, CP_EDGE_TOP, CP_EDGE_BOTTOM
+ switch(edge)
+ {
+ case CP_EDGE_LEFT:
+ // no change to faceEdgeStartPtr
+ edgeWalk = nChannels * size;
+ edgePerpWalk = nChannels;
+ break;
+ case CP_EDGE_RIGHT:
+ edgeStartPtr += (size - 1) * nChannels;
+ edgeWalk = nChannels * size;
+ edgePerpWalk = -nChannels;
+ break;
+ case CP_EDGE_TOP:
+ // no change to faceEdgeStartPtr
+ edgeWalk = nChannels;
+ edgePerpWalk = nChannels * size;
+ break;
+ case CP_EDGE_BOTTOM:
+ edgeStartPtr += (size) * (size - 1) * nChannels;
+ edgeWalk = nChannels;
+ edgePerpWalk = -(nChannels * size);
+ break;
+ }
+
+ //For certain types of edge abutments, the neighbor edge walk needs to
+ // be flipped: the cases are
+ // if a left edge mates with a left or bottom edge on the neighbor
+ // if a top edge mates with a top or right edge on the neighbor
+ // if a right edge mates with a right or top edge on the neighbor
+ // if a bottom edge mates with a bottom or left edge on the neighbor
+ //Seeing as the edges are enumerated as follows
+ // left =0
+ // right =1
+ // top =2
+ // bottom =3
+ //
+ //If the edge enums are the same, or the sum of the enums == 3,
+ // the neighbor edge walk needs to be flipped
+ if( (edge == neighborEdge) || ((edge + neighborEdge) == 3) )
+ { //swapped direction neighbor edge walk
+ switch(neighborEdge)
+ {
+ case CP_EDGE_LEFT: //start at lower left and walk up
+ neighborEdgeStartPtr += (size - 1) * (size) * nChannels;
+ neighborEdgeWalk = -(nChannels * size);
+ neighborEdgePerpWalk = nChannels;
+ break;
+ case CP_EDGE_RIGHT: //start at lower right and walk up
+ neighborEdgeStartPtr += ((size - 1)*(size) + (size - 1)) * nChannels;
+ neighborEdgeWalk = -(nChannels * size);
+ neighborEdgePerpWalk = -nChannels;
+ break;
+ case CP_EDGE_TOP: //start at upper right and walk left
+ neighborEdgeStartPtr += (size - 1) * nChannels;
+ neighborEdgeWalk = -nChannels;
+ neighborEdgePerpWalk = (nChannels * size);
+ break;
+ case CP_EDGE_BOTTOM: //start at lower right and walk left
+ neighborEdgeStartPtr += ((size - 1)*(size) + (size - 1)) * nChannels;
+ neighborEdgeWalk = -nChannels;
+ neighborEdgePerpWalk = -(nChannels * size);
+ break;
+ }
+ }
+ else
+ { //swapped direction neighbor edge walk
+ switch(neighborEdge)
+ {
+ case CP_EDGE_LEFT: //start at upper left and walk down
+ //no change to neighborEdgeStartPtr for this case since it points
+ // to the upper left corner already
+ neighborEdgeWalk = nChannels * size;
+ neighborEdgePerpWalk = nChannels;
+ break;
+ case CP_EDGE_RIGHT: //start at upper right and walk down
+ neighborEdgeStartPtr += (size - 1) * nChannels;
+ neighborEdgeWalk = nChannels * size;
+ neighborEdgePerpWalk = -nChannels;
+ break;
+ case CP_EDGE_TOP: //start at upper left and walk left
+ //no change to neighborEdgeStartPtr for this case since it points
+ // to the upper left corner already
+ neighborEdgeWalk = nChannels;
+ neighborEdgePerpWalk = (nChannels * size);
+ break;
+ case CP_EDGE_BOTTOM: //start at lower left and walk left
+ neighborEdgeStartPtr += (size) * (size - 1) * nChannels;
+ neighborEdgeWalk = nChannels;
+ neighborEdgePerpWalk = -(nChannels * size);
+ break;
+ }
+ }
+
+
+ //Perform edge walk, to average across the 12 edges and smoothly propagate change to
+ //nearby neighborhood
+
+ //step ahead one texel on edge
+ edgeStartPtr += edgeWalk;
+ neighborEdgeStartPtr += neighborEdgeWalk;
+
+ // note that this loop does not process the corner texels, since they have already been
+ // averaged across faces across earlier
+ for(j=1; j<(size - 1); j++)
+ {
+ //for each set of taps along edge, average them
+ // and rewrite the results into the edges
+ for(k = 0; k<nChannels; k++)
+ {
+ CP_ITYPE edgeTap, neighborEdgeTap, avgTap; //edge tap, neighborEdgeTap and the average of the two
+ CP_ITYPE edgeTapDev, neighborEdgeTapDev;
+
+ edgeTap = *(edgeStartPtr + k);
+ neighborEdgeTap = *(neighborEdgeStartPtr + k);
+
+ //compute average of tap intensity values
+ avgTap = 0.5f * (edgeTap + neighborEdgeTap);
+
+ //propagate average of taps to edge taps
+ (*(edgeStartPtr + k)) = avgTap;
+ (*(neighborEdgeStartPtr + k)) = avgTap;
+
+ edgeTapDev = edgeTap - avgTap;
+ neighborEdgeTapDev = neighborEdgeTap - avgTap;
+
+ //iterate over taps in direction perpendicular to edge, and
+ // adjust intensity values gradualy to obscure change in intensity values of
+ // edge averaging.
+ for(iFixup = 1; iFixup < fixupDist; iFixup++)
+ {
+ //fractional amount to apply change in tap intensity along edge to taps
+ // in a perpendicular direction to edge
+ CP_ITYPE fixupFrac = (CP_ITYPE)(fixupDist - iFixup) / (CP_ITYPE)(fixupDist);
+ CP_ITYPE fixupWeight;
+
+ switch(a_FixupType )
+ {
+ case CP_FIXUP_PULL_LINEAR:
+ {
+ fixupWeight = fixupFrac;
+ }
+ break;
+ case CP_FIXUP_PULL_HERMITE:
+ {
+ //hermite spline interpolation between 1 and 0 with both pts derivatives = 0
+ // e.g. smooth step
+ // the full formula for hermite interpolation is:
+ //
+ // [ 2 -2 1 1 ][ p0 ]
+ // [t^3 t^2 t 1 ][ -3 3 -2 -1 ][ p1 ]
+ // [ 0 0 1 0 ][ d0 ]
+ // [ 1 0 0 0 ][ d1 ]
+ //
+ // Where p0 and p1 are the point locations and d0, and d1 are their respective derivatives
+ // t is the parameteric coordinate used to specify an interpoltion point on the spline
+ // and ranges from 0 to 1.
+ // if p0 = 0 and p1 = 1, and d0 and d1 = 0, the interpolation reduces to
+ //
+ // p(t) = - 2t^3 + 3t^2
+ fixupWeight = ((-2.0 * fixupFrac + 3.0) * fixupFrac * fixupFrac);
+ }
+ break;
+ case CP_FIXUP_AVERAGE_LINEAR:
+ {
+ fixupWeight = fixupFrac;
+
+ //perform weighted average of edge tap value and current tap
+ // fade off weight linearly as a function of distance from edge
+ edgeTapDev =
+ (*(edgeStartPtr + (iFixup * edgePerpWalk) + k)) - avgTap;
+ neighborEdgeTapDev =
+ (*(neighborEdgeStartPtr + (iFixup * neighborEdgePerpWalk) + k)) - avgTap;
+ }
+ break;
+ case CP_FIXUP_AVERAGE_HERMITE:
+ {
+ fixupWeight = ((-2.0 * fixupFrac + 3.0) * fixupFrac * fixupFrac);
+
+ //perform weighted average of edge tap value and current tap
+ // fade off weight using hermite spline with distance from edge
+ // as parametric coordinate
+ edgeTapDev =
+ (*(edgeStartPtr + (iFixup * edgePerpWalk) + k)) - avgTap;
+ neighborEdgeTapDev =
+ (*(neighborEdgeStartPtr + (iFixup * neighborEdgePerpWalk) + k)) - avgTap;
+ }
+ break;
+ }
+
+ // vary intensity of taps within fixup region toward edge values to hide changes made to edge taps
+ *(edgeStartPtr + (iFixup * edgePerpWalk) + k) -= (fixupWeight * edgeTapDev);
+ *(neighborEdgeStartPtr + (iFixup * neighborEdgePerpWalk) + k) -= (fixupWeight * neighborEdgeTapDev);
+ }
+
+ }
+
+ edgeStartPtr += edgeWalk;
+ neighborEdgeStartPtr += neighborEdgeWalk;
+ }
+ }
+}
diff --git a/Runtime/Graphics/CubemapProcessor.h b/Runtime/Graphics/CubemapProcessor.h
new file mode 100644
index 0000000..b824e8d
--- /dev/null
+++ b/Runtime/Graphics/CubemapProcessor.h
@@ -0,0 +1,35 @@
+#ifndef CUBEMAPPROCESSOR_H
+#define CUBEMAPPROCESSOR_H
+
+//--------------------------------------------------------------------------------------
+// Based on CCubeMapProcessor from CubeMapGen v1.4
+// http://developer.amd.com/archive/gpu/cubemapgen/Pages/default.aspx#download
+// Class for filtering and processing cubemaps
+//
+//
+//--------------------------------------------------------------------------------------
+// (C) 2005 ATI Research, Inc., All rights reserved.
+//--------------------------------------------------------------------------------------
+
+
+#define CP_ITYPE float
+
+struct CImageSurface
+{
+ int m_Width; //image width
+ int m_Height; //image height
+ int m_NumChannels; //number of channels
+ CP_ITYPE *m_ImgData; //cubemap image data
+};
+
+// Edge fixup type (how to perform smoothing near edge region)
+#define CP_FIXUP_NONE 0
+#define CP_FIXUP_PULL_LINEAR 1
+#define CP_FIXUP_PULL_HERMITE 2
+#define CP_FIXUP_AVERAGE_LINEAR 3
+#define CP_FIXUP_AVERAGE_HERMITE 4
+
+
+void FixupCubeEdges (CImageSurface *a_CubeMap, int a_FixupType, int a_FixupWidth);
+
+#endif
diff --git a/Runtime/Graphics/CubemapTexture.cpp b/Runtime/Graphics/CubemapTexture.cpp
new file mode 100644
index 0000000..4fe2d7e
--- /dev/null
+++ b/Runtime/Graphics/CubemapTexture.cpp
@@ -0,0 +1,186 @@
+#include "UnityPrefix.h"
+#include "CubemapTexture.h"
+#include "Runtime/Graphics/CubemapProcessor.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+using namespace std;
+
+Cubemap::Cubemap (MemLabelId label, ObjectCreationMode mode)
+: Super (label, mode)
+{
+ m_SourceTextures.resize (6);
+}
+
+Cubemap::~Cubemap ()
+{
+}
+
+bool Cubemap::InitTexture (int width, int height, TextureFormat format, int flags, int imageCount)
+{
+ Assert (imageCount == 6);
+ Assert (width == height);
+ if( !IsPowerOfTwo (width) || !IsPowerOfTwo (height) )
+ {
+ ErrorStringObject ("Texture has non-power of two size", this);
+ return false;
+ }
+ if( width != height )
+ {
+ ErrorStringObject ("Cubemap faces must be square", this);
+ return false;
+ }
+ SetDirty();
+ return Super::InitTexture (width, width, format, flags, 6);
+}
+
+
+void Cubemap::UploadTexture (bool dontUseSubImage)
+{
+ Assert (GetRawImageData());
+
+ ErrorIf (GetGLWidth() != GetGLHeight() || m_ImageCount != 6);
+ int faceDataSize = GetRawImageData(1)-GetRawImageData(0);
+ int dataSize = faceDataSize * 6;
+ UInt32 uploadFlags = (dontUseSubImage || !m_TextureUploaded) ? GfxDevice::kUploadTextureDontUseSubImage : GfxDevice::kUploadTextureDefault;
+ GetGfxDevice().UploadTextureCube( GetTextureID(), GetRawImageData(), dataSize, faceDataSize, GetGLWidth(), GetTextureFormat(), CountMipmaps(), uploadFlags, GetActiveTextureColorSpace() );
+ Texture::s_TextureIDMap.insert (std::make_pair(GetTextureID(),this));
+
+ m_TextureSettings.m_WrapMode = kTexWrapClamp;
+ ApplySettings();
+ m_TextureUploaded = true;
+
+#if !UNITY_EDITOR
+ if(!m_IsReadable)
+ {
+ DeallocateTextureData (m_TexData.data);
+ m_TexData.data = 0;
+ m_TexData.imageSize = 0;
+ }
+#endif
+}
+
+static void ConvertImageToFloatArray (float* dst, ImageReference const& src)
+{
+ ColorRGBAf* dstRGBA = (ColorRGBAf*)dst;
+ const int width = src.GetWidth ();
+ const int height = src.GetHeight ();
+ for (int y = 0; y < width; ++y)
+ for (int x = 0; x < height; ++x)
+ {
+ *dstRGBA++ = GetImagePixel (src.GetImageData (), width, height, src.GetFormat (), kTexWrapClamp, x, y);
+ }
+}
+
+static void ConvertFloatArrayToImage (ImageReference& dst, float const* src)
+{
+ ColorRGBAf const* srcRGBA = (ColorRGBAf const*)src;
+ const int width = dst.GetWidth ();
+ const int height = dst.GetHeight ();
+ for (int y = 0; y < width; ++y)
+ for (int x = 0; x < height; ++x)
+ {
+ SetImagePixel (dst, x, y, kTexWrapClamp, *srcRGBA++);
+ }
+}
+
+void Cubemap::RebuildMipMap ()
+{
+ TextureRepresentation& rep = m_TexData;
+
+ if (rep.data == NULL || !m_MipMap)
+ return;
+
+ if (IsAnyCompressedTextureFormat(rep.format))
+ {
+ ErrorStringObject ("Rebuilding mipmaps of compressed textures is not supported", this);
+ return;
+ }
+ if (m_ImageCount != 6)
+ {
+ ErrorStringObject ("Cubemap must have 6 faces", this);
+ return;
+ }
+
+ Assert (rep.width == rep.height);
+ const int edge = rep.width;
+ for (int i = 0; i < 6; i++)
+ CreateMipMap (rep.data + rep.imageSize * i, edge, edge, 1, rep.format);
+}
+
+
+void Cubemap::FixupEdges (int fixupWidthInPixels)
+{
+ TextureRepresentation& rep = m_TexData;
+
+ if (rep.data == NULL || !m_MipMap)
+ return;
+
+ const int edge = rep.width;
+ const int channels = 4; CImageSurface cubemapSurfaces[6];
+ for (int q = 0; q < 6; ++q)
+ {
+ cubemapSurfaces[q].m_ImgData = (float*)UNITY_MALLOC (kMemDefault, edge * edge * channels * sizeof(CP_ITYPE));
+ }
+
+ int mipEdge = edge;
+ for (int mip = 0; mip < CountMipmaps (); ++mip)
+ {
+ ImageReference mipImages[6];
+
+ for (int face = 0; face < 6; ++face)
+ {
+ if (!GetWriteImageReference (&mipImages[face], face, mip))
+ {
+ ErrorStringObject ("Can't draw into cubemap", this);
+ return;
+ }
+ ConvertImageToFloatArray (cubemapSurfaces[face].m_ImgData, mipImages[face]);
+ cubemapSurfaces[face].m_Width = cubemapSurfaces[face].m_Height = mipEdge;
+ cubemapSurfaces[face].m_NumChannels = channels;
+ }
+
+ FixupCubeEdges (cubemapSurfaces, CP_FIXUP_AVERAGE_HERMITE, fixupWidthInPixels);
+
+ for (int face = 0; face < 6; ++face)
+ {
+ ConvertFloatArrayToImage (mipImages[face], cubemapSurfaces[face].m_ImgData);
+ }
+
+ mipEdge = max(mipEdge/2, 1);
+ }
+
+ for (int q = 0; q < 6; ++q)
+ {
+ UNITY_FREE (kMemDefault, cubemapSurfaces[q].m_ImgData);
+ }
+}
+
+void Cubemap::SetSourceTexture (CubemapFace face, PPtr<Texture2D> tex)
+{
+ Assert (face < m_SourceTextures.size ());
+
+ m_SourceTextures[face] = tex;
+}
+
+PPtr<Texture2D> Cubemap::GetSourceTexture (CubemapFace face) const
+{
+ Assert (face < m_SourceTextures.size ());
+
+ return m_SourceTextures[face];
+}
+
+template<class TransferFunction>
+void Cubemap::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.Transfer (m_SourceTextures, "m_SourceTextures");
+ transfer.Align ();
+}
+
+IMPLEMENT_CLASS (Cubemap)
+IMPLEMENT_OBJECT_SERIALIZE (Cubemap)
+
diff --git a/Runtime/Graphics/CubemapTexture.h b/Runtime/Graphics/CubemapTexture.h
new file mode 100644
index 0000000..6934926
--- /dev/null
+++ b/Runtime/Graphics/CubemapTexture.h
@@ -0,0 +1,30 @@
+#ifndef CUBEMAPTEXTURE_H
+#define CUBEMAPTEXTURE_H
+
+#include "Texture2D.h"
+
+
+class Cubemap : public Texture2D
+{
+public:
+ REGISTER_DERIVED_CLASS (Cubemap, Texture2D)
+ DECLARE_OBJECT_SERIALIZE (Cubemap)
+
+ Cubemap (MemLabelId label, ObjectCreationMode mode);
+
+ virtual bool InitTexture (int width, int height, TextureFormat format, int flags = kMipmapMask, int imageCount = 1);
+ virtual TextureDimension GetDimension () const { return kTexDimCUBE; }
+ virtual void UploadTexture (bool dontUseSubImage);
+
+ virtual void RebuildMipMap ();
+
+ void FixupEdges (int fixupWidthInPixels = 1);
+
+ void SetSourceTexture (CubemapFace face, PPtr<Texture2D> tex);
+ PPtr<Texture2D> GetSourceTexture (CubemapFace face) const;
+
+private:
+ std::vector<PPtr<Texture2D> > m_SourceTextures;
+};
+
+#endif
diff --git a/Runtime/Graphics/DXTCompression.cpp b/Runtime/Graphics/DXTCompression.cpp
new file mode 100644
index 0000000..b580a32
--- /dev/null
+++ b/Runtime/Graphics/DXTCompression.cpp
@@ -0,0 +1,595 @@
+// Fast DXT compression code adapted from
+// stb_dxt by Sean Barrett: http://nothings.org/stb/stb_dxt.h
+// which is in turn adapted from code by
+// Fabian "ryg" Giesen: http://www.farbrausch.de/~fg/code/dxt/
+
+#include "UnityPrefix.h"
+#include "DXTCompression.h"
+
+// stb_dxt.h - v1.01 - DXT1/DXT5 compressor - public domain
+// original by fabian "ryg" giesen - ported to C by stb
+//
+// version history:
+// v1.xx - fix for big endian archs (Aras)
+// v1.02 - fix alpha encoding bug
+// v1.01 - fix endianness bug that messed up quality, thanks ryg & cbloom
+// v1.00 - first release
+
+#if UNITY_BIG_ENDIAN
+#define STB_BYTESWAP_16(i) ((i<<8)|(i>>8))
+#define STB_BYTESWAP_32(i) ((i>>24) | (i>>8)&0x0000ff00 | (i<<8)&0x00ff0000 | (i<<24))
+#else
+#define STB_BYTESWAP_16(i) (i)
+#define STB_BYTESWAP_32(i) (i)
+#endif
+
+
+static unsigned char stb__Expand5[32];
+static unsigned char stb__Expand6[64];
+static unsigned char stb__OMatch5[256][2];
+static unsigned char stb__OMatch6[256][2];
+static unsigned char stb__QuantRBTab[256+16];
+static unsigned char stb__QuantGTab[256+16];
+
+static int stb__Mul8Bit(int a,int b)
+{
+ int t = a*b + 128;
+ return (t + (t >> 8)) >> 8;
+}
+
+static void stb__From16Bit(unsigned char *out, unsigned short v)
+{
+ int rv = (v & 0xf800) >> 11;
+ int gv = (v & 0x07e0) >> 5;
+ int bv = (v & 0x001f) >> 0;
+
+ out[0] = stb__Expand5[rv];
+ out[1] = stb__Expand6[gv];
+ out[2] = stb__Expand5[bv];
+ out[3] = 0;
+}
+
+static unsigned short stb__As16Bit(int r, int g, int b)
+{
+ return (stb__Mul8Bit(r,31) << 11) + (stb__Mul8Bit(g,63) << 5) + stb__Mul8Bit(b,31);
+}
+
+static void stb__LerpRGB(unsigned char *out, unsigned char *p1, unsigned char *p2, int f)
+{
+ out[0] = p1[0] + stb__Mul8Bit(p2[0] - p1[0],f);
+ out[1] = p1[1] + stb__Mul8Bit(p2[1] - p1[1],f);
+ out[2] = p1[2] + stb__Mul8Bit(p2[2] - p1[2],f);
+}
+
+/****************************************************************************/
+
+// compute table to reproduce constant colors as accurately as possible
+static void stb__PrepareOptTable(unsigned char *Table,const unsigned char *expand,int size)
+{
+ int i,mn,mx;
+ for (i=0;i<256;i++) {
+ int bestErr = 256;
+ for (mn=0;mn<size;mn++) {
+ for (mx=0;mx<size;mx++) {
+ int mine = expand[mn];
+ int maxe = expand[mx];
+ int err = abs(maxe + stb__Mul8Bit(mine-maxe,0x55) - i);
+
+ if(err < bestErr)
+ {
+ Table[i*2+0] = mx;
+ Table[i*2+1] = mn;
+ bestErr = err;
+ }
+ }
+ }
+ }
+}
+
+static void stb__EvalColors(unsigned char *color,unsigned short c0,unsigned short c1)
+{
+ stb__From16Bit(color+ 0, c0);
+ stb__From16Bit(color+ 4, c1);
+ stb__LerpRGB (color+ 8, color+0,color+4, 0x55);
+ stb__LerpRGB (color+12, color+0,color+4, 0xaa);
+}
+
+// Block dithering function. Simply dithers a block to 565 RGB.
+// (Floyd-Steinberg)
+static void stb__DitherBlock(unsigned char *dest, unsigned char *block)
+{
+ int err[8],*ep1 = err,*ep2 = err+4, *et;
+ int ch,y;
+
+ // process channels seperately
+ for (ch=0; ch<3; ++ch) {
+ unsigned char *bp = block+ch, *dp = dest+ch;
+ unsigned char *quant = (ch == 1) ? stb__QuantGTab+8 : stb__QuantRBTab+8;
+ memset(err, 0, sizeof(err));
+ for(y=0; y<4; ++y) {
+ dp[ 0] = quant[bp[ 0] + ((3*ep2[1] + 5*ep2[0]) >> 4)];
+ ep1[0] = bp[ 0] - dp[ 0];
+ dp[ 4] = quant[bp[ 4] + ((7*ep1[0] + 3*ep2[2] + 5*ep2[1] + ep2[0]) >> 4)];
+ ep1[1] = bp[ 4] - dp[ 4];
+ dp[ 8] = quant[bp[ 8] + ((7*ep1[1] + 3*ep2[3] + 5*ep2[2] + ep2[1]) >> 4)];
+ ep1[2] = bp[ 8] - dp[ 8];
+ dp[12] = quant[bp[12] + ((7*ep1[2] + 5*ep2[3] + ep2[2]) >> 4)];
+ ep1[3] = bp[12] - dp[12];
+ bp += 16;
+ dp += 16;
+ et = ep1, ep1 = ep2, ep2 = et; // swap
+ }
+ }
+}
+
+// The color matching function
+static unsigned int stb__MatchColorsBlock(unsigned char *block, unsigned char *color,int dither)
+{
+ unsigned int mask = 0;
+ int dirr = color[0*4+0] - color[1*4+0];
+ int dirg = color[0*4+1] - color[1*4+1];
+ int dirb = color[0*4+2] - color[1*4+2];
+ int dots[16];
+ int stops[4];
+ int i;
+ int c0Point, halfPoint, c3Point;
+
+ for(i=0;i<16;i++)
+ dots[i] = block[i*4+0]*dirr + block[i*4+1]*dirg + block[i*4+2]*dirb;
+
+ for(i=0;i<4;i++)
+ stops[i] = color[i*4+0]*dirr + color[i*4+1]*dirg + color[i*4+2]*dirb;
+
+ c0Point = (stops[1] + stops[3]) >> 1;
+ halfPoint = (stops[3] + stops[2]) >> 1;
+ c3Point = (stops[2] + stops[0]) >> 1;
+
+ if(!dither) {
+ // the version without dithering is straightforward
+ for (i=15;i>=0;i--) {
+ int dot = dots[i];
+ mask <<= 2;
+
+ if(dot < halfPoint)
+ mask |= (dot < c0Point) ? 1 : 3;
+ else
+ mask |= (dot < c3Point) ? 2 : 0;
+ }
+ } else {
+ // with floyd-steinberg dithering
+ int err[8],*ep1 = err,*ep2 = err+4;
+ int *dp = dots, y;
+
+ c0Point <<= 4;
+ halfPoint <<= 4;
+ c3Point <<= 4;
+ for(i=0;i<8;i++)
+ err[i] = 0;
+
+ for(y=0;y<4;y++)
+ {
+ int dot,lmask,step;
+
+ dot = (dp[0] << 4) + (3*ep2[1] + 5*ep2[0]);
+ if(dot < halfPoint)
+ step = (dot < c0Point) ? 1 : 3;
+ else
+ step = (dot < c3Point) ? 2 : 0;
+ ep1[0] = dp[0] - stops[step];
+ lmask = step;
+
+ dot = (dp[1] << 4) + (7*ep1[0] + 3*ep2[2] + 5*ep2[1] + ep2[0]);
+ if(dot < halfPoint)
+ step = (dot < c0Point) ? 1 : 3;
+ else
+ step = (dot < c3Point) ? 2 : 0;
+ ep1[1] = dp[1] - stops[step];
+ lmask |= step<<2;
+
+ dot = (dp[2] << 4) + (7*ep1[1] + 3*ep2[3] + 5*ep2[2] + ep2[1]);
+ if(dot < halfPoint)
+ step = (dot < c0Point) ? 1 : 3;
+ else
+ step = (dot < c3Point) ? 2 : 0;
+ ep1[2] = dp[2] - stops[step];
+ lmask |= step<<4;
+
+ dot = (dp[3] << 4) + (7*ep1[2] + 5*ep2[3] + ep2[2]);
+ if(dot < halfPoint)
+ step = (dot < c0Point) ? 1 : 3;
+ else
+ step = (dot < c3Point) ? 2 : 0;
+ ep1[3] = dp[3] - stops[step];
+ lmask |= step<<6;
+
+ dp += 4;
+ mask |= lmask << (y*8);
+ { int *et = ep1; ep1 = ep2; ep2 = et; } // swap
+ }
+ }
+
+ return mask;
+}
+
+// The color optimization function. (Clever code, part 1)
+static void stb__OptimizeColorsBlock(unsigned char *block, unsigned short *pmax16, unsigned short *pmin16)
+{
+ int mind = 0x7fffffff,maxd = -0x7fffffff;
+ unsigned char *minp = NULL, *maxp = NULL;
+ double magn;
+ int v_r,v_g,v_b;
+ static const int nIterPower = 4;
+ float covf[6],vfr,vfg,vfb;
+
+ // determine color distribution
+ int cov[6];
+ int mu[3],min[3],max[3];
+ int ch,i,iter;
+
+ for(ch=0;ch<3;ch++)
+ {
+ const unsigned char *bp = ((const unsigned char *) block) + ch;
+ int muv,minv,maxv;
+
+ muv = minv = maxv = bp[0];
+ for(i=4;i<64;i+=4)
+ {
+ muv += bp[i];
+ if (bp[i] < minv) minv = bp[i];
+ else if (bp[i] > maxv) maxv = bp[i];
+ }
+
+ mu[ch] = (muv + 8) >> 4;
+ min[ch] = minv;
+ max[ch] = maxv;
+ }
+
+ // determine covariance matrix
+ for (i=0;i<6;i++)
+ cov[i] = 0;
+
+ for (i=0;i<16;i++)
+ {
+ int r = block[i*4+0] - mu[0];
+ int g = block[i*4+1] - mu[1];
+ int b = block[i*4+2] - mu[2];
+
+ cov[0] += r*r;
+ cov[1] += r*g;
+ cov[2] += r*b;
+ cov[3] += g*g;
+ cov[4] += g*b;
+ cov[5] += b*b;
+ }
+
+ // convert covariance matrix to float, find principal axis via power iter
+ for(i=0;i<6;i++)
+ covf[i] = cov[i] / 255.0f;
+
+ vfr = (float) (max[0] - min[0]);
+ vfg = (float) (max[1] - min[1]);
+ vfb = (float) (max[2] - min[2]);
+
+ for(iter=0;iter<nIterPower;iter++)
+ {
+ float r = vfr*covf[0] + vfg*covf[1] + vfb*covf[2];
+ float g = vfr*covf[1] + vfg*covf[3] + vfb*covf[4];
+ float b = vfr*covf[2] + vfg*covf[4] + vfb*covf[5];
+
+ vfr = r;
+ vfg = g;
+ vfb = b;
+ }
+
+ magn = fabs(vfr);
+ if (fabs(vfg) > magn) magn = fabs(vfg);
+ if (fabs(vfb) > magn) magn = fabs(vfb);
+
+ if(magn < 4.0f) { // too small, default to luminance
+ v_r = 148;
+ v_g = 300;
+ v_b = 58;
+ } else {
+ magn = 512.0 / magn;
+ v_r = (int) (vfr * magn);
+ v_g = (int) (vfg * magn);
+ v_b = (int) (vfb * magn);
+ }
+
+ // Pick colors at extreme points
+ for(i=0;i<16;i++)
+ {
+ int dot = block[i*4+0]*v_r + block[i*4+1]*v_g + block[i*4+2]*v_b;
+
+ if (dot < mind) {
+ mind = dot;
+ minp = block+i*4;
+ }
+
+ if (dot > maxd) {
+ maxd = dot;
+ maxp = block+i*4;
+ }
+ }
+
+ *pmax16 = stb__As16Bit(maxp[0],maxp[1],maxp[2]);
+ *pmin16 = stb__As16Bit(minp[0],minp[1],minp[2]);
+}
+
+static int stb__sclamp(float y, int p0, int p1)
+{
+ int x = (int) y;
+ if (x < p0) return p0;
+ if (x > p1) return p1;
+ return x;
+}
+
+// The refinement function. (Clever code, part 2)
+// Tries to optimize colors to suit block contents better.
+// (By solving a least squares system via normal equations+Cramer's rule)
+static int stb__RefineBlock(unsigned char *block, unsigned short *pmax16, unsigned short *pmin16, unsigned int mask)
+{
+ static const int w1Tab[4] = { 3,0,2,1 };
+ static const int prods[4] = { 0x090000,0x000900,0x040102,0x010402 };
+ // ^some magic to save a lot of multiplies in the accumulating loop...
+
+ float frb,fg;
+ unsigned short oldMin, oldMax, min16, max16;
+ int i, akku = 0, xx,xy,yy;
+ int At1_r,At1_g,At1_b;
+ int At2_r,At2_g,At2_b;
+ unsigned int cm = mask;
+
+ oldMin = *pmin16;
+ oldMax = *pmax16;
+
+ if((mask ^ (mask<<2)) < 4) // all pixels have the same index?
+ {
+ // yes, linear system would be singular; solve using optimal
+ // single-color match on average color
+ int r = 8, g = 8, b = 8;
+ for (i=0;i<16;++i) {
+ r += block[i*4+0];
+ g += block[i*4+1];
+ b += block[i*4+2];
+ }
+
+ r >>= 4; g >>= 4; b >>= 4;
+
+ max16 = (stb__OMatch5[r][0]<<11) | (stb__OMatch6[g][0]<<5) | stb__OMatch5[b][0];
+ min16 = (stb__OMatch5[r][1]<<11) | (stb__OMatch6[g][1]<<5) | stb__OMatch5[b][1];
+ } else {
+ At1_r = At1_g = At1_b = 0;
+ At2_r = At2_g = At2_b = 0;
+ for (i=0;i<16;++i,cm>>=2) {
+ int step = cm&3;
+ int w1 = w1Tab[step];
+ int r = block[i*4+0];
+ int g = block[i*4+1];
+ int b = block[i*4+2];
+
+ akku += prods[step];
+ At1_r += w1*r;
+ At1_g += w1*g;
+ At1_b += w1*b;
+ At2_r += r;
+ At2_g += g;
+ At2_b += b;
+ }
+
+ At2_r = 3*At2_r - At1_r;
+ At2_g = 3*At2_g - At1_g;
+ At2_b = 3*At2_b - At1_b;
+
+ // extract solutions and decide solvability
+ xx = akku >> 16;
+ yy = (akku >> 8) & 0xff;
+ xy = (akku >> 0) & 0xff;
+
+ frb = 3.0f * 31.0f / 255.0f / (xx*yy - xy*xy);
+ fg = frb * 63.0f / 31.0f;
+
+ // solve.
+ max16 = stb__sclamp((At1_r*yy - At2_r*xy)*frb+0.5f,0,31) << 11;
+ max16 |= stb__sclamp((At1_g*yy - At2_g*xy)*fg +0.5f,0,63) << 5;
+ max16 |= stb__sclamp((At1_b*yy - At2_b*xy)*frb+0.5f,0,31) << 0;
+
+ min16 = stb__sclamp((At2_r*xx - At1_r*xy)*frb+0.5f,0,31) << 11;
+ min16 |= stb__sclamp((At2_g*xx - At1_g*xy)*fg +0.5f,0,63) << 5;
+ min16 |= stb__sclamp((At2_b*xx - At1_b*xy)*frb+0.5f,0,31) << 0;
+ }
+
+ *pmin16 = min16;
+ *pmax16 = max16;
+ return oldMin != min16 || oldMax != max16;
+}
+
+// Color block compression
+static void stb__CompressColorBlock(unsigned char *dest, unsigned char *block,int quality)
+{
+ unsigned int mask;
+ int i;
+ unsigned short max16, min16;
+ unsigned char dblock[16*4],color[4*4];
+
+ // check if block is constant
+ for (i=1;i<16;i++)
+ if (((unsigned int *) block)[i] != ((unsigned int *) block)[0])
+ break;
+
+ if(i == 16) { // constant color
+ int r = block[0], g = block[1], b = block[2];
+ mask = 0xaaaaaaaa;
+ max16 = (stb__OMatch5[r][0]<<11) | (stb__OMatch6[g][0]<<5) | stb__OMatch5[b][0];
+ min16 = (stb__OMatch5[r][1]<<11) | (stb__OMatch6[g][1]<<5) | stb__OMatch5[b][1];
+ } else {
+ // first step: compute dithered version for PCA if desired
+ if(quality)
+ stb__DitherBlock(dblock,block);
+
+ // second step: pca+map along principal axis
+ stb__OptimizeColorsBlock(quality ? dblock : block,&max16,&min16);
+ if (max16 != min16) {
+ stb__EvalColors(color,max16,min16);
+ mask = stb__MatchColorsBlock(block,color,quality != 0);
+ } else
+ mask = 0;
+
+ // third step: refine
+ if (stb__RefineBlock(quality ? dblock : block,&max16,&min16,mask)) {
+ if (max16 != min16) {
+ stb__EvalColors(color,max16,min16);
+ mask = stb__MatchColorsBlock(block,color,quality != 0);
+ } else
+ mask = 0;
+ }
+ }
+
+ // write the color block
+ if(max16 < min16)
+ {
+ unsigned short t = min16;
+ min16 = max16;
+ max16 = t;
+ mask ^= 0x55555555;
+ }
+
+ ((unsigned short *) dest)[0] = STB_BYTESWAP_16(max16);
+ ((unsigned short *) dest)[1] = STB_BYTESWAP_16(min16);
+ ((unsigned int *) dest)[1] = STB_BYTESWAP_32(mask);
+}
+
+// Alpha block compression (this is easy for a change)
+static void stb__CompressAlphaBlock(unsigned char *dest,unsigned char *src,int quality)
+{
+ int i,dist,bias,dist4,dist2,bits,mask;
+
+ // find min/max color
+ int mn,mx;
+ mn = mx = src[3];
+
+ for (i=1;i<16;i++)
+ {
+ if (src[i*4+3] < mn) mn = src[i*4+3];
+ else if (src[i*4+3] > mx) mx = src[i*4+3];
+ }
+
+ // encode them
+ ((unsigned char *)dest)[0] = mx;
+ ((unsigned char *)dest)[1] = mn;
+ dest += 2;
+
+ // determine bias and emit color indices
+ dist = mx-mn;
+ bias = mn*7 - (dist >> 1);
+ dist4 = dist*4;
+ dist2 = dist*2;
+ bits = 0,mask=0;
+
+ for (i=0;i<16;i++) {
+ int a = src[i*4+3]*7 - bias;
+ int ind,t;
+
+ // select index (hooray for bit magic)
+ t = (dist4 - a) >> 31; ind = t & 4; a -= dist4 & t;
+ t = (dist2 - a) >> 31; ind += t & 2; a -= dist2 & t;
+ t = (dist - a) >> 31; ind += t & 1;
+
+ ind = -ind & 7;
+ ind ^= (2 > ind);
+
+ // write index
+ mask |= ind << bits;
+ if((bits += 3) >= 8) {
+ *dest++ = mask;
+ mask >>= 8;
+ bits -= 8;
+ }
+ }
+}
+
+static void stb__InitDXT()
+{
+ int i;
+ for(i=0;i<32;i++)
+ stb__Expand5[i] = (i<<3)|(i>>2);
+
+ for(i=0;i<64;i++)
+ stb__Expand6[i] = (i<<2)|(i>>4);
+
+ for(i=0;i<256+16;i++)
+ {
+ int v = i-8 < 0 ? 0 : i-8 > 255 ? 255 : i-8;
+ stb__QuantRBTab[i] = stb__Expand5[stb__Mul8Bit(v,31)];
+ stb__QuantGTab[i] = stb__Expand6[stb__Mul8Bit(v,63)];
+ }
+
+ stb__PrepareOptTable(&stb__OMatch5[0][0],stb__Expand5,32);
+ stb__PrepareOptTable(&stb__OMatch6[0][0],stb__Expand6,64);
+}
+
+// input: a 4x4 pixel block, r,g,b,a bytes per pixel
+// alpha: true = DXT5, false = DXT1
+// quality: true = slower, with dither; false = faster, no dither
+static void FastCompressDXTBlock( UInt8* dest, const UInt8* src, bool alpha, bool quality)
+{
+ static int init=0;
+ if (!init) {
+ stb__InitDXT();
+ init=1;
+ }
+
+ if (alpha) {
+ stb__CompressAlphaBlock(dest,(unsigned char*) src,quality);
+ dest += 8;
+ }
+
+ stb__CompressColorBlock(dest,(unsigned char*) src,quality);
+}
+
+void FastCompressImage (int width, int height, const UInt8* src, UInt8* dest, bool dxt5, bool dither )
+{
+ // adapted from stbgl__compress: http://nothings.org/stb/stb_gl.h
+
+ const UInt8* destEnd = dest + ((width+3)/4)*((height+3)/4)*(dxt5?16:8);
+
+ for (int y=0; y < height; y += 4) {
+ for (int x=0; x < width; x += 4) {
+ UInt8 block[16*4];
+ int xleft=4;
+ if (x+3 >= width) xleft = width-x;
+ int yleft;
+ for (yleft=0; yleft < 4; ++yleft) {
+ if (y+yleft >= height) break;
+ memcpy(block+yleft*16, src + width*4*(y+yleft) + x*4, xleft*4);
+ }
+ if (xleft < 4) {
+ switch (xleft) {
+ case 0: AssertString("should not happen");
+ case 1:
+ for (int yy=0; yy < yleft; ++yy) {
+ memcpy(block+yy*16+1*4, block+yy*16+0*4, 4);
+ memcpy(block+yy*16+2*4, block+yy*16+0*4, 8);
+ }
+ break;
+ case 2:
+ for (int yy=0; yy < yleft; ++yy)
+ memcpy(block+yy*16+2*4, block+yy*16+0*4, 8);
+ break;
+ case 3:
+ for (int yy=0; yy < yleft; ++yy)
+ memcpy(block+yy*16+3*4, block+yy*16+1*4, 4);
+ break;
+ }
+ }
+ int yy = 0;
+ for(; yleft<4; ++yleft,++yy)
+ memcpy(block+yleft*16, block+yy*16, 4*4);
+ FastCompressDXTBlock( dest, block, dxt5, dither );
+ dest += dxt5 ? 16 : 8;
+ }
+ }
+
+ AssertIf( dest > destEnd );
+ UNUSED(destEnd);
+}
diff --git a/Runtime/Graphics/DXTCompression.h b/Runtime/Graphics/DXTCompression.h
new file mode 100644
index 0000000..414c23e
--- /dev/null
+++ b/Runtime/Graphics/DXTCompression.h
@@ -0,0 +1,13 @@
+// Fast DXT compression code adapted from
+// stb_dxt by Sean Barrett: http://nothings.org/stb/stb_dxt.h
+// which is in turn adapted from code by
+// Fabian "ryg" Giesen: http://www.farbrausch.de/~fg/code/dxt/
+
+#ifndef __DXT_COMPRESSION__
+#define __DXT_COMPRESSION__
+
+// src: 32 bit/pixel width*height image, R,G,B,A bytes in pixel.
+// dest must be (width+3)/4*(height+3)/4 bytes if dxt5=true, and half of that if dxt5=false (dxt1 is used then).
+void FastCompressImage (int width, int height, const UInt8* src, UInt8* dest, bool dxt5, bool dither );
+
+#endif
diff --git a/Runtime/Graphics/DisplayManager.h b/Runtime/Graphics/DisplayManager.h
new file mode 100644
index 0000000..6de5f08
--- /dev/null
+++ b/Runtime/Graphics/DisplayManager.h
@@ -0,0 +1,54 @@
+#pragma once
+#include "UnityPrefix.h"
+
+// we do c-style interface, because display management is way too platfrom specific
+// these function will be used from script
+// UnityDisplayManager prefix is used because some platforms implements this in trampoline
+
+#define SUPPORT_MULTIPLE_DISPLAYS UNITY_IPHONE
+
+#if !SUPPORT_MULTIPLE_DISPLAYS
+ #include "ScreenManager.h"
+#endif
+
+struct RenderSurfaceBase;
+
+#if SUPPORT_MULTIPLE_DISPLAYS
+
+ extern "C" int UnityDisplayManager_DisplayCount();
+ extern "C" bool UnityDisplayManager_DisplayAvailable(void* nativeDisplay);
+ extern "C" void UnityDisplayManager_DisplaySystemResolution(void* nativeDisplay, int* w, int* h);
+ extern "C" void UnityDisplayManager_DisplayRenderingResolution(void* nativeDisplay, int* w, int* h);
+ extern "C" void UnityDisplayManager_DisplayRenderingBuffers(void* nativeDisplay, RenderSurfaceBase** colorBuffer, RenderSurfaceBase** depthBuffer);
+ extern "C" void UnityDisplayManager_SetRenderingResolution(void* nativeDisplay, int w, int h);
+
+#else
+
+ inline int UnityDisplayManager_DisplayCount()
+ {
+ return 1;
+ }
+ inline bool UnityDisplayManager_DisplayAvailable(void*)
+ {
+ return true;
+ }
+ inline void UnityDisplayManager_DisplaySystemResolution(void*, int* w, int* h)
+ {
+ *w = GetScreenManager().GetWidth();
+ *h = GetScreenManager().GetHeight();
+ }
+ inline void UnityDisplayManager_DisplayRenderingResolution(void*, int* w, int* h)
+ {
+ *w = GetScreenManager().GetWidth();
+ *h = GetScreenManager().GetHeight();
+ }
+ inline void UnityDisplayManager_SetRenderingResolution(void*, int w, int h)
+ {
+ GetScreenManager().RequestResolution(w, h, GetScreenManager ().IsFullScreen (), 0);
+ }
+ inline void UnityDisplayManager_DisplayRenderingBuffers(void*, RenderSurfaceBase** color, RenderSurfaceBase** depth)
+ {
+ *color = *depth = 0;
+ }
+
+#endif
diff --git a/Runtime/Graphics/DrawSplashScreenAndWatermarks.cpp b/Runtime/Graphics/DrawSplashScreenAndWatermarks.cpp
new file mode 100644
index 0000000..6735e4a
--- /dev/null
+++ b/Runtime/Graphics/DrawSplashScreenAndWatermarks.cpp
@@ -0,0 +1,449 @@
+#include "UnityPrefix.h"
+#include "DrawSplashScreenAndWatermarks.h"
+#include "Runtime/Graphics/GraphicsHelper.h"
+#include "Runtime/Misc/ReproductionLog.h"
+#include "Runtime/Misc/PlayerSettings.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Camera/RenderLayers/GUITexture.h"
+#include "Runtime/Misc/SystemInfo.h"
+
+#if WEBPLUG
+double gDisplayFullscreenEscapeTimeout = -1.0F;
+#endif
+
+static bool ConsumeVersionNumber(const char *&version, int &number)
+{
+ number = 0;
+
+ for (int i = 0; i < 8; ++i) // limit to 8 digits
+ {
+ char c = *version;
+
+ if ((c < '0') || (c > '9'))
+ {
+ return (i > 0);
+ }
+
+ ++version;
+
+ number *= 10;
+ number += (c - '0');
+ }
+
+ return false;
+}
+
+static bool ConsumeVersionSeparator(const char *&version)
+{
+ if ('.' == *version)
+ {
+ ++version;
+ return true;
+ }
+
+ return false;
+}
+
+bool IsContentBuiltWithBetaVersion()
+{
+ if (GetBuildSettingsPtr() == NULL)
+ return false;
+
+ const char* version = GetBuildSettings().GetVersion().c_str();
+
+ int temp;
+
+ if (ConsumeVersionNumber(version, temp) && ConsumeVersionSeparator(version)) // major
+ {
+ if (ConsumeVersionNumber(version, temp) && ConsumeVersionSeparator(version)) // minor
+ {
+ if (ConsumeVersionNumber(version, temp)) // fix
+ {
+ char c = *version;
+
+ if (('d' == c) || ('D' == c) || // development
+ ('a' == c) || ('A' == c) || // alpha
+ ('b' == c) || ('B' == c)) // beta
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void DrawWaterMark( bool alwaysDisplay )
+{
+ Texture2D* watermark = GetBuiltinResource<Texture2D>("UnityWaterMark-small.png");
+ if( !watermark )
+ return;
+
+#if !UNITY_EDITOR
+
+ const float kSlideInDelay = 1.5f;
+ const float kSlideInTime = 1.0f;
+ const float kStayTime = 5.0f;
+ const float kSlideOutTime = 1.5f;
+
+ float t = GetTimeManager().GetRealtime();
+ if( !alwaysDisplay && t > kSlideInDelay+kSlideInTime+kStayTime+kSlideOutTime )
+ return;
+
+ float a;
+ if( t < kSlideInDelay+kSlideInTime )
+ a = (t-kSlideInDelay) / kSlideInTime;
+ else if( !alwaysDisplay )
+ a = 1.0f - (t-(kSlideInDelay+kSlideInTime+kStayTime)) / kSlideOutTime;
+ else
+ a = 1.0f;
+
+#else
+ float a = 1.0f;
+#endif
+
+ float pos = SmoothStep( 0.0f, 128.0f, a );
+
+ const Rectf& windowRect = GetRenderManager().GetWindowRect();
+ DeviceMVPMatricesState preserveMVP;
+ SetupPixelCorrectCoordinates();
+
+ DrawGUITexture (Rectf (windowRect.width-pos, 62, 128, -58), watermark, ColorRGBAf(0.5f, 0.5f, 0.5f, 0.5f));
+}
+
+// (0, 0) is at the lower left corner. x increments to the left, y - upwards.
+// specify -(x + 1) to align to the right and -(y + 1) to align to the top
+static int DrawSimpleWatermark(const string &name, float x, float y, const ColorRGBAf& color)
+{
+ Texture2D* texture = GetBuiltinResource<Texture2D>(name);
+ if (!texture)
+ return 0;
+
+ const Rectf& windowRect = GetRenderManager().GetWindowRect();
+ DeviceMVPMatricesState preserveMVP;
+ SetupPixelCorrectCoordinates();
+
+ float width = texture->GetDataWidth();
+ float height = -texture->GetDataHeight();
+
+ if (x < 0)
+ {
+ x = -(x + 1);
+ x = (windowRect.width - width - x);
+ }
+
+ if (y < 0)
+ {
+ y = -(y + 1);
+ y = (windowRect.height + height - y);
+ }
+
+ y -= height;
+
+ DrawGUITexture (Rectf (x, y, width, height), texture, color);
+
+ return texture->GetDataHeight();
+}
+
+static int DrawSimpleWatermark(const string &name, float x, float y)
+{
+ return DrawSimpleWatermark(name, x, y, ColorRGBAf(0.5f, 0.5f, 0.5f, 0.5f));
+}
+
+static void DrawTrialWatermark (int& watermarkoffset)
+{
+ int height = DrawSimpleWatermark("UnityWaterMark-trial.png", -(1 + 1), watermarkoffset);
+ watermarkoffset += height + 3;
+}
+
+static void DrawEducationalWatermark (int& watermarkoffset)
+{
+ int height = DrawSimpleWatermark("UnityWaterMark-edu.png", -(1 + 1), watermarkoffset);
+ watermarkoffset += height + 3;
+}
+
+static void DrawPrototypingWatermark (int& watermarkoffset)
+{
+ int height = DrawSimpleWatermark("UnityWaterMark-proto.png", -(1 + 1), watermarkoffset);
+ watermarkoffset += height + 3;
+}
+
+static void DrawDeveloperWatermark (int& watermarkoffset)
+{
+ int height = DrawSimpleWatermark("UnityWaterMark-dev.png", -(1 + 1), watermarkoffset);
+ watermarkoffset += height + 3;
+}
+
+#if UNITY_FLASH
+static void DrawDebugFlashPlayerWatermark (int& watermarkoffset)
+{
+ int height = DrawSimpleWatermark("UnityWatermark-DebugFlashPlayer.png", -(1 + 1), watermarkoffset);
+ watermarkoffset += height + 3;
+}
+#endif
+
+#if WEBPLUG
+
+
+static void DrawContentBetaWatermark (int& watermarkoffset)
+{
+ int height = DrawSimpleWatermark("UnityWaterMark-beta.png", -(1 + 1), watermarkoffset);
+ watermarkoffset += height + 3;
+}
+
+static void DrawPluginBetaWatermark (int& watermarkoffset)
+{
+ int height = DrawSimpleWatermark("UnityWaterMarkPlugin-beta.png", -(1 + 1), watermarkoffset);
+ watermarkoffset += height + 3;
+}
+
+void ShowFullscreenEscapeWarning ()
+{
+ gDisplayFullscreenEscapeTimeout = GetTimeSinceStartup();
+}
+
+void RenderFullscreenEscapeWarning ()
+{
+ const float kTimeout = 6.0F;
+ if (GetTimeSinceStartup() < gDisplayFullscreenEscapeTimeout + kTimeout && GetScreenManager().IsFullScreen())
+ {
+ float time = GetTimeSinceStartup() - gDisplayFullscreenEscapeTimeout;
+ time = std::max(time, 0.0F);
+
+ Texture2D* background = GetBuiltinResource<Texture2D>("EscToExit_back.png");
+ Texture2D* text = GetBuiltinResource<Texture2D>("EscToExit_text.png");
+ if( background && text )
+ {
+ float a = SmoothStep( 0.5f, 0, (time - 3.5f) * 0.5f );
+ ColorRGBA32 color = ColorRGBAf(0.5f,0.5f,0.5f,a);
+ const float w = 350;
+ const float h = 71;
+ const float yFac = 1.0f - 0.38197f;
+
+ const Rectf& windowRect = GetRenderManager().GetWindowRect();
+ DeviceMVPMatricesState preserveMVP;
+ SetupPixelCorrectCoordinates();
+ DrawGUITexture( Rectf((windowRect.width-w)*0.5f, (windowRect.height-h)*yFac, w, -h), background, 36, 36, 0, 0, color );
+ const float textw = 267;
+ DrawGUITexture( Rectf((windowRect.width-textw)*0.5f, (windowRect.height-h)*yFac-25, textw, -20), text, color );
+ }
+ }
+ else if (!GetScreenManager().HasFullscreenRequested())
+ {
+ gDisplayFullscreenEscapeTimeout = -1000.0;
+ }
+}
+#endif
+
+
+#if UNITY_CAN_SHOW_SPLASH_SCREEN
+
+static double gSplashScreenStartTime;
+static const float kSplashBeforeGameDuration = 4.5f;
+
+void BeginSplashScreenFade()
+{
+ gSplashScreenStartTime = GetTimeSinceStartup();
+}
+
+bool IsSplashScreenFadeComplete()
+{
+ return (GetTimeSinceStartup() >= gSplashScreenStartTime + kSplashBeforeGameDuration);
+}
+
+bool GetShouldShowSplashScreen()
+{
+ const BuildSettings& settings = GetBuildSettings();
+ bool disableWatermark = GetBuildSettings().isNoWatermarkBuild;
+ if ( disableWatermark )
+ return false;
+ bool isTrial = !settings.hasPublishingRights;
+ bool isIndie = !settings.hasPROVersion;
+ return isTrial || isIndie;
+}
+
+#if DEBUGMODE
+ #define DEBUGMSG_ONCE(Message) \
+ {\
+ static bool once = false; \
+ if (!once) { printf_console(Message); once = true; }\
+ }
+#else
+ #define DEBUGMSG_ONCE(Message) {}
+#endif
+
+void DrawSplashScreen (bool fullDraw)
+{
+ AssertIf (!GetShouldShowSplashScreen());
+
+ DEBUGMSG_ONCE("Begin showing splash screen.\n");
+
+ const float kSplashFadeStart = 3.0f;
+ const float kSplashXFadeDuration = 0.4f;
+
+ float timeBegin = fullDraw ? 0.0f : kSplashBeforeGameDuration;
+ float t = GetTimeSinceStartup() - gSplashScreenStartTime - timeBegin;
+ if (!fullDraw && t > kSplashXFadeDuration)
+ {
+ DEBUGMSG_ONCE("End showing splash screen.\n");
+ return;
+ }
+
+ Texture2D* logo = GetBuiltinResource<Texture2D>("UnitySplash.png");
+ if (!logo)
+ return;
+ Texture2D* text = GetBuiltinResource<Texture2D>("UnitySplash2.png");
+ if (!text)
+ return;
+ Texture2D* background = GetBuiltinResource<Texture2D>("UnitySplash3.png");
+ if (!background)
+ return;
+ Texture2D* black = GetBuiltinResource<Texture2D>("UnitySplashBack.png");
+ if (!black)
+ return;
+
+ const Rectf& windowRect = GetRenderManager().GetWindowRect();
+ Vector2f basePos (windowRect.width * .5f - 119, windowRect.height *.5f - 219);
+
+ Rectf (basePos.x, basePos.y, 276,432);
+
+ GfxDevice& device = GetGfxDevice();
+ if (fullDraw)
+ {
+ device.BeginFrame();
+ if (!device.IsValidState())
+ {
+ device.HandleInvalidState();
+ return;
+ }
+ const float kBlack[4] = {0,0,0,0};
+ GraphicsHelper::Clear (kGfxClearAll, kBlack, 1.0f, 0);
+ }
+
+ DeviceMVPMatricesState preserveMVP;
+ SetupPixelCorrectCoordinates();
+
+ if (fullDraw)
+ {
+ float a = SmoothStep( 0.5f, 0.0f, (t - kSplashFadeStart) / (kSplashBeforeGameDuration-kSplashFadeStart));
+ DrawGUITexture( Rectf (basePos.x, basePos.y + 432, 276, -432), background, ColorRGBAf(a,a,a, 0.5f) );
+ DrawGUITexture( Rectf (basePos.x, basePos.y + 432, 276, -432), background, ColorRGBAf(a,a,a, 0.5f) );
+ DrawGUITexture( Rectf (basePos.x + 43, basePos.y + 83, 146, -19), text, ColorRGBAf(a,a,a, 0.5f) );
+ DrawGUITexture( Rectf (basePos.x, basePos.y + 330, 206, -214), logo, ColorRGBAf(a,a,a, 0.5f) );
+ }
+ else
+ {
+ float a = SmoothStep( 0.5f, 0.0f, t / kSplashXFadeDuration);
+ DrawGUITexture(Rectf (0,0,windowRect.width+10, windowRect.height+10), black, ColorRGBAf(0.5f, 0.5f, 0.5f, a));
+ }
+
+ if (fullDraw)
+ {
+ device.EndFrame();
+#if ((UNITY_LINUX && SUPPORT_X11) || UNITY_OSX) && !UNITY_EDITOR && !WEBPLUG
+ GetScreenManager().SetBlitMaterial();
+#endif
+ device.PresentFrame();
+ }
+}
+
+#endif // UNITY_CAN_SHOW_SPLASH_SCREEN
+
+
+#if UNITY_FLASH
+static bool isDebugFlashPlayer;
+static bool haveSetDebugFlashPlayer=false;
+
+static bool IsRunningInDebugFlashPlayer()
+{
+ if (!haveSetDebugFlashPlayer)
+ {
+ __asm("%0 = flash.system.Capabilities.isDebugger ? 1 : 0;" : "=r"(isDebugFlashPlayer));
+ //__asm("trace('Debugger!?!?!');");
+ //__asm("trace(flash.system.Capabilities.isDebugger ? 1 : 0);");
+ haveSetDebugFlashPlayer = true;
+ }
+ return isDebugFlashPlayer;
+}
+#endif
+
+void DrawSplashAndWatermarks()
+{
+#if SUPPORT_REPRODUCE_LOG
+ if (GetReproduceMode() != kNormalPlayback)
+ return;
+#endif
+ // draw splash / watermark
+ RuntimePlatform platform = systeminfo::GetRuntimePlatform();
+ bool disableWatermark = GetBuildSettings().isNoWatermarkBuild;
+ bool isEducational = GetBuildSettings().isEducationalBuild && !UNITY_EDITOR;
+ bool isPrototyping = GetBuildSettings().isPrototypingBuild && !UNITY_EDITOR;
+ bool isTrial = !GetBuildSettings().hasPublishingRights && !UNITY_EDITOR;
+ bool isIndie = !GetBuildSettings().hasPROVersion;
+ bool isIndieAndWebPlayer = isIndie && systeminfo::IsPlatformWebPlayer(platform);
+ bool isDeveloperPlayerBuild = !UNITY_EDITOR && GetBuildSettings().isDebugBuild;
+
+ if (isIndieAndWebPlayer)
+ DrawWaterMark (false);
+
+ int waterMarkOffset = 3;
+
+#if UNITY_FLASH
+ if (IsRunningInDebugFlashPlayer())
+ DrawDebugFlashPlayerWatermark(waterMarkOffset);
+#endif
+
+ const bool isDeveloperBuild = IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_2_a1) && isDeveloperPlayerBuild || (UNITY_DEVELOPER_BUILD && !UNITY_FLASH);
+
+ if (isDeveloperBuild)
+ DrawDeveloperWatermark(waterMarkOffset);
+
+#if !UNITY_FLASH && !UNITY_WP8
+ // Time based licenses will still look like "trial" licenses, so check for educational and prototyping flags first
+ if ( !disableWatermark )
+ {
+ if (isEducational)
+ DrawEducationalWatermark (waterMarkOffset);
+ else if (isPrototyping)
+ DrawPrototypingWatermark (waterMarkOffset);
+ else if (isTrial)
+ DrawTrialWatermark (waterMarkOffset);
+ }
+#endif
+
+#if UNITY_ANDROID || UNITY_BB10
+ // it will be drawn completely transparent, so no need to worry ;-)
+ if( gGraphicsCaps.gles20.needToRenderWatermarkDueToDrawFromMemoryBuggy
+ && GetGfxDevice().GetRenderer() == kGfxRendererOpenGLES20Mobile
+ && !isDeveloperBuild
+ )
+ {
+ DrawSimpleWatermark("UnityWaterMark-dev.png", -(1 + 1), waterMarkOffset, ColorRGBAf(0,0,0,0));
+ }
+#endif
+
+#if WEBPLUG && !UNITY_PEPPER && !UNITY_FLASH
+
+ // Jonas Echterhoff suggested [2012.07.23 18:21:35] to show only one of the messages,
+ // i.e., either plugin beta or content beta, because if you have some unity beta installed
+ // you will always have three watermarks: Made with beta, Running on beta and Development player
+ #if UNITY_IS_BETA
+ DrawPluginBetaWatermark (waterMarkOffset);
+ #else
+ if( IsContentBuiltWithBetaVersion ())
+ DrawContentBetaWatermark (waterMarkOffset);
+ #endif // UNITY_IS_BETA
+#endif
+
+#if UNITY_CAN_SHOW_SPLASH_SCREEN
+ if (GetShouldShowSplashScreen())
+ DrawSplashScreen (false);
+#endif
+}
diff --git a/Runtime/Graphics/DrawSplashScreenAndWatermarks.h b/Runtime/Graphics/DrawSplashScreenAndWatermarks.h
new file mode 100644
index 0000000..b323456
--- /dev/null
+++ b/Runtime/Graphics/DrawSplashScreenAndWatermarks.h
@@ -0,0 +1,18 @@
+#pragma once
+
+void DrawSplashAndWatermarks();
+void DrawWaterMark( bool alwaysDisplay );
+
+#if UNITY_CAN_SHOW_SPLASH_SCREEN
+
+void BeginSplashScreenFade();
+bool IsSplashScreenFadeComplete();
+bool GetShouldShowSplashScreen();
+void DrawSplashScreen (bool fullDraw);
+#endif
+
+#if WEBPLUG
+void ShowFullscreenEscapeWarning ();
+void RenderFullscreenEscapeWarning ();
+extern double gDisplayFullscreenEscapeTimeout;
+#endif
diff --git a/Runtime/Graphics/DrawUtil.cpp b/Runtime/Graphics/DrawUtil.cpp
new file mode 100644
index 0000000..4c17fe8
--- /dev/null
+++ b/Runtime/Graphics/DrawUtil.cpp
@@ -0,0 +1,173 @@
+#include "UnityPrefix.h"
+#include "DrawUtil.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Transform.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/GfxDevice/GfxDeviceStats.h"
+#include "Runtime/GfxDevice/BatchRendering.h"
+#include "Runtime/Camera/Renderqueue.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Shaders/ComputeShader.h"
+#include "Runtime/Graphics/SpriteFrame.h"
+#if UNITY_PS3
+# include "Runtime/GfxDevice/ps3/GfxGCMVBO.h"
+#endif
+
+PROFILER_INFORMATION(gDrawMeshVBOProfile, "Mesh.DrawVBO", kProfilerRender)
+PROFILER_INFORMATION(gDrawMeshNullProfile, "Graphics.DrawProcedural", kProfilerRender)
+
+static void DrawMeshInternal (const ChannelAssigns& channels, Mesh &mesh, const Matrix4x4f &matrix, int subsetIndex, TransformType transformType);
+
+void DrawUtil::DrawVBOMeshRaw (VBO& vbo, Mesh& mesh, const ChannelAssigns& channels, int subsetIndex, UInt32 channelsInVBO)
+{
+ PROFILER_AUTO(gDrawMeshVBOProfile, &mesh)
+
+ subsetIndex = std::min<unsigned int>(subsetIndex, mesh.GetSubMeshCount()? mesh.GetSubMeshCount()-1:0);
+ SubMesh& submesh = mesh.GetSubMeshFast(subsetIndex);
+
+ int firstVertex, vertexCount;
+
+ firstVertex = submesh.firstVertex;
+ vertexCount = submesh.vertexCount;
+#if UNITY_PS3
+ GfxGCMVBO& gcmVBO = (GfxGCMVBO&)vbo;
+ gcmVBO.DrawSubmesh(channels, subsetIndex, &submesh);
+#else
+ vbo.DrawVBO (channels, submesh.firstByte, submesh.indexCount, submesh.topology, firstVertex, vertexCount);
+#endif
+ GPU_TIMESTAMP();
+}
+
+void DrawUtil::DrawMeshRaw (const ChannelAssigns& channels, Mesh& mesh, int subsetIndex)
+{
+ VBO* vbo = mesh.GetSharedVBO (channels.GetSourceMap());
+ if( vbo != NULL )
+ {
+ DrawVBOMeshRaw (*vbo, mesh, channels, subsetIndex);
+ }
+}
+
+void DrawUtil::DrawMesh (const ChannelAssigns& channels, Mesh &mesh, const Vector3f &position, const Quaternionf &rotation, int subsetIndex)
+{
+ Matrix4x4f matrix;
+ QuaternionToMatrix (rotation, matrix);
+ matrix.SetPosition( position );
+
+ TransformType transformType = kNoScaleTransform;
+ DrawMeshInternal (channels, mesh, matrix, subsetIndex, transformType);
+}
+
+void DrawUtil::DrawMesh (const ChannelAssigns& channels, Mesh &mesh, const Matrix4x4f &matrix, int subsetIndex)
+ {
+ // just assume it is a scaled transform, but don't handle negative scales... oh well! (says @aras_p)
+ TransformType transformType = kUniformScaleTransform;
+ DrawMeshInternal (channels, mesh, matrix, subsetIndex, transformType);
+}
+
+static void DrawMeshInternal (const ChannelAssigns& channels, Mesh &mesh, const Matrix4x4f &matrix, int subsetIndex, TransformType transformType)
+{
+ const Camera* camera = GetCurrentCameraPtr();
+
+ GfxDevice& device = GetGfxDevice();
+ float matWorld[16], matView[16];
+
+ CopyMatrix(device.GetViewMatrix(), matView);
+ CopyMatrix(device.GetWorldMatrix(), matWorld);
+ if (camera)
+ device.SetViewMatrix( camera->GetWorldToCameraMatrix().GetPtr() );
+
+ SetupObjectMatrix (matrix, transformType);
+
+ if (subsetIndex != -1)
+ {
+ DrawUtil::DrawMeshRaw (channels, mesh, subsetIndex);
+ }
+ else
+ {
+ int submeshCount = mesh.GetSubMeshCount();
+ for (int i=0;i<submeshCount;i++)
+ DrawUtil::DrawMeshRaw (channels, mesh, i);
+ }
+
+ device.SetViewMatrix(matView);
+ device.SetWorldMatrix(matWorld);
+}
+
+void DrawUtil::DrawProcedural (GfxPrimitiveType topology, int vertexCount, int instanceCount)
+{
+ if (instanceCount > 1 && !gGraphicsCaps.hasInstancing)
+ {
+ ErrorString ("Can't do instanced Graphics.DrawProcedural");
+ return;
+ }
+
+ PROFILER_AUTO(gDrawMeshNullProfile, NULL)
+
+ GfxDevice& device = GetGfxDevice();
+ device.DrawNullGeometry (topology, vertexCount, instanceCount);
+ device.GetFrameStats().AddDrawCall (vertexCount*instanceCount, vertexCount*instanceCount);
+
+ GPU_TIMESTAMP();
+}
+
+void DrawUtil::DrawProceduralIndirect (GfxPrimitiveType topology, ComputeBuffer* bufferWithArgs, UInt32 argsOffset)
+{
+ if (!gGraphicsCaps.hasInstancing || !gGraphicsCaps.hasComputeShader)
+ {
+ ErrorString ("Can't do indirect Graphics.DrawProcedural");
+ return;
+ }
+ if (!bufferWithArgs)
+ {
+ ErrorString ("Graphics.DrawProcedural with invalid buffer");
+ return;
+ }
+ ComputeBufferID bufferHandle = bufferWithArgs->GetBufferHandle();
+ if (!bufferHandle.IsValid())
+ {
+ ErrorString ("Graphics.DrawProcedural with invalid buffer");
+ return;
+ }
+
+ PROFILER_AUTO(gDrawMeshNullProfile, NULL)
+
+ GfxDevice& device = GetGfxDevice();
+ device.DrawNullGeometryIndirect (topology, bufferHandle, argsOffset);
+ device.GetFrameStats().AddDrawCall (1,1); // unknown primitive count
+
+ GPU_TIMESTAMP();
+}
+
+void DrawUtil::DrawSpriteRaw (const ChannelAssigns& channels, Sprite& sprite, const ColorRGBAf& color)
+{
+ GfxDevice& device = GetGfxDevice();
+
+ const SpriteRenderData& rd = sprite.GetRenderData(false); // Use non-atlased RenderData as input.
+ Assert(rd.texture.IsValid());
+
+ // Get VBO chunk for a rectangle or mesh
+ UInt32 numIndices = rd.indices.size();
+ UInt32 numVertices = rd.vertices.size();
+ if (!numIndices)
+ return;
+
+ const UInt32 channelMask = (1<<kShaderChannelVertex) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelColor);
+
+ DynamicVBO& vbo = device.GetDynamicVBO();
+ UInt8* __restrict vbPtr;
+ UInt16* __restrict ibPtr;
+ if ( !vbo.GetChunk(channelMask, numVertices, numIndices, DynamicVBO::kDrawIndexedTriangles, (void**)&vbPtr, (void**)&ibPtr) )
+ return;
+
+ TransformSprite (vbPtr, ibPtr, NULL, &rd, device.ConvertToDeviceVertexColor(color), 0);
+ vbo.ReleaseChunk(numVertices, numIndices);
+ vbo.DrawChunk(channels);
+}
diff --git a/Runtime/Graphics/DrawUtil.h b/Runtime/Graphics/DrawUtil.h
new file mode 100644
index 0000000..52bf156
--- /dev/null
+++ b/Runtime/Graphics/DrawUtil.h
@@ -0,0 +1,51 @@
+#ifndef DRAWUTIL_H
+#define DRAWUTIL_H
+
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/Modules/ExportModules.h"
+
+namespace Unity { class Material; }
+class ChannelAssigns;
+using namespace Unity;
+class Vector2f;
+class Vector3f;
+class ColorRGBAf;
+class Quaternionf;
+class Mesh;
+class VBO;
+class Matrix4x4f;
+class ComputeBuffer;
+class Sprite;
+
+
+enum MeshPrimitiveType
+{
+ kTriangles = 0,
+ kLines = 1,
+};
+
+// Utilities for drawing
+struct EXPORT_COREMODULE DrawUtil
+{
+ static void DrawVBOMeshRaw (VBO& vbo, Mesh& mesh, const ChannelAssigns& channels, int materialIndex, UInt32 channelsInVBO = ~0UL);
+
+ /// Draw a mesh with the current matrix
+ static void DrawMeshRaw (const ChannelAssigns& channels, Mesh &mesh, int materialIndex);
+
+ static void DrawMesh (const ChannelAssigns& channels, Mesh &mesh, const Vector3f &position, const Quaternionf &rotation, int materialIndex = -1);
+ static void DrawMesh (const ChannelAssigns& channels, Mesh &mesh, const Matrix4x4f &matrix, int materialIndex);
+
+ static void DrawProcedural (GfxPrimitiveType topology, int vertexCount, int instanceCount);
+
+ // args in ComputeBuffer at given offset:
+ // uint32 vertexCountPerInstance
+ // uint32 instanceCount
+ // uint32 startVertexLocation
+ // uint32 startInstanceLocation
+ static void DrawProceduralIndirect (GfxPrimitiveType topology, ComputeBuffer* bufferWithArgs, UInt32 argsOffset);
+
+
+ static void DrawSpriteRaw (const ChannelAssigns& channels, Sprite& sprite, const ColorRGBAf& color);
+};
+
+#endif
diff --git a/Runtime/Graphics/ETC2Decompression.cpp b/Runtime/Graphics/ETC2Decompression.cpp
new file mode 100644
index 0000000..d2f8169
--- /dev/null
+++ b/Runtime/Graphics/ETC2Decompression.cpp
@@ -0,0 +1,487 @@
+#include "ETC2Decompression.h"
+
+// Utilities
+
+template<typename T>
+static inline T InRange (T v, T a, T b)
+{
+ return a <= v && v <= b;
+}
+
+template<typename T>
+static inline T Clamp (T v, T a, T b)
+{
+ if (v < a) return a;
+ else if (v > b) return b;
+ else return v;
+}
+
+static inline int DivRoundUp (int a, int b)
+{
+ return a/b + ((a%b) ? 1 : 0);
+}
+
+static inline UInt32 GetSingleBit (UInt64 src, int bit)
+{
+ return (src >> bit) & 1;
+}
+
+static inline UInt32 GetBitRange (UInt64 src, int low, int high)
+{
+ int numBits = (high-low) + 1;
+ return (src >> low) & ((1<<numBits)-1);
+}
+
+static inline UInt8 ExtendTo8 (UInt8 src, UInt32 fromBits)
+{
+ return (src << (8-fromBits)) | (src >> (2*fromBits - 8));
+}
+
+static inline SInt8 Extend3BitSignedDelta (UInt8 src)
+{
+ bool isNeg = (src & (1<<2)) != 0;
+ return (SInt8)((isNeg ? ~((1<<3)-1) : 0) | src);
+}
+
+static inline UInt16 Extend11To16 (UInt16 src)
+{
+ return (src << 5) | (src >> 6);
+}
+
+static inline SInt16 Extend11To16WithSign (SInt16 src)
+{
+ if (src < 0)
+ return -(SInt16)Extend11To16(-src);
+ else
+ return (SInt16)Extend11To16(src);
+}
+
+// Block access utilities
+
+static inline UInt64 Get64BitBlock (const UInt8* src, int blockNdx)
+{
+ UInt64 block = 0;
+ for (int i = 0; i < 8; i++)
+ block = (block << 8ull) | (UInt64)(src[blockNdx*8+i]);
+ return block;
+}
+
+static inline UInt64 Get128BitBlockStart (const UInt8* src, int blockNdx)
+{
+ return Get64BitBlock(src, 2*blockNdx);
+}
+
+static inline UInt64 Get128BitBlockEnd (const UInt8* src, int blockNdx)
+{
+ return Get64BitBlock(src, 2*blockNdx + 1);
+}
+
+// Block decompression routines
+
+static void DecompressETC2Block (UInt64 src, UInt8 dst[kETC2UncompressedBlockSizeRGB8], UInt8 alphaDst[kETC2UncompressedBlockSizeA8], bool alphaMode)
+{
+ typedef enum BlockMode_e
+ {
+ kBlockModeIndividual = 0,
+ kBlockModeDifferential,
+ kBlockModeT,
+ kBlockModeH,
+ kBlockModePlanar,
+ kBlockModeCount
+ } BlockMode;
+
+ int diffOpaqueBit = (int)GetSingleBit(src, 33);
+ SInt8 selBR = (SInt8)GetBitRange(src, 59, 63); // 5 bits.
+ SInt8 selBG = (SInt8)GetBitRange(src, 51, 55);
+ SInt8 selBB = (SInt8)GetBitRange(src, 43, 47);
+ SInt8 selDR = Extend3BitSignedDelta((UInt8)GetBitRange(src, 56, 58)); // 3 bits.
+ SInt8 selDG = Extend3BitSignedDelta((UInt8)GetBitRange(src, 48, 50));
+ SInt8 selDB = Extend3BitSignedDelta((UInt8)GetBitRange(src, 40, 42));
+ BlockMode mode;
+
+ if (!alphaMode && diffOpaqueBit == 0)
+ mode = kBlockModeIndividual;
+ else if (!InRange(selBR + selDR, 0, 31))
+ mode = kBlockModeT;
+ else if (!InRange(selBG + selDG, 0, 31))
+ mode = kBlockModeH;
+ else if (!InRange(selBB + selDB, 0, 31))
+ mode = kBlockModePlanar;
+ else
+ mode = kBlockModeDifferential;
+
+ if (mode == kBlockModeIndividual || mode == kBlockModeDifferential)
+ {
+ // A lot of logic is shared between individual and differential mode
+ static const int modifierLUT[8][4] =
+ {
+ // 00 01 10 11
+ { 2, 8, -2, -8 },
+ { 5, 17, -5, -17 },
+ { 9, 29, -9, -29 },
+ { 13, 42, -13, -42 },
+ { 18, 60, -18, -60 },
+ { 24, 80, -24, -80 },
+ { 33, 106, -33, -106 },
+ { 47, 183, -47, -183 }
+ };
+
+ int flipBit = (int)GetSingleBit(src, 32);
+ UInt32 table[2] = { GetBitRange(src, 37, 39), GetBitRange(src, 34, 36) };
+ UInt8 baseR[2];
+ UInt8 baseG[2];
+ UInt8 baseB[2];
+
+ // Base values per mode
+ if (mode == kBlockModeIndividual)
+ {
+ baseR[0] = ExtendTo8((UInt8)GetBitRange(src, 60, 63), 4);
+ baseR[1] = ExtendTo8((UInt8)GetBitRange(src, 56, 59), 4);
+ baseG[0] = ExtendTo8((UInt8)GetBitRange(src, 52, 55), 4);
+ baseG[1] = ExtendTo8((UInt8)GetBitRange(src, 48, 51), 4);
+ baseB[0] = ExtendTo8((UInt8)GetBitRange(src, 44, 47), 4);
+ baseB[1] = ExtendTo8((UInt8)GetBitRange(src, 40, 43), 4);
+ }
+ else
+ {
+ baseR[0] = ExtendTo8(selBR, 5);
+ baseG[0] = ExtendTo8(selBG, 5);
+ baseB[0] = ExtendTo8(selBB, 5);
+
+ baseR[1] = ExtendTo8((UInt8)(selBR + selDR), 5);
+ baseG[1] = ExtendTo8((UInt8)(selBG + selDG), 5);
+ baseB[1] = ExtendTo8((UInt8)(selBB + selDB), 5);
+ }
+
+ for (int pixelNdx = 0; pixelNdx < kETC2BlockHeight*kETC2BlockWidth; pixelNdx++)
+ {
+ int x = pixelNdx / kETC2BlockHeight;
+ int y = pixelNdx % kETC2BlockHeight;
+ int dstOffset = (y*kETC2BlockWidth + x)*kETC2UncompressedPixelSizeRGB8;
+ int subBlock = ((flipBit ? y : x) >= 2) ? 1 : 0;
+ UInt32 tableNdx = table[subBlock];
+ UInt32 modifierNdx = (GetSingleBit(src, 16+pixelNdx) << 1) | GetSingleBit(src, pixelNdx);
+ int alphaDstOffset = (y*kETC2BlockWidth + x)*kETC2UncompressedPixelSizeA8; // Only needed for PUNCHTHROUGH version.
+
+ if (alphaMode && diffOpaqueBit == 0 && modifierNdx == 2)
+ {
+ // If doing PUNCHTHROUGH version (alphaMode), opaque bit may affect colors.
+ dst[dstOffset+0] = 0;
+ dst[dstOffset+1] = 0;
+ dst[dstOffset+2] = 0;
+ alphaDst[alphaDstOffset] = 0;
+ }
+ else
+ {
+ int modifier;
+
+ // PUNCHTHROUGH version and opaque bit may also affect modifiers.
+ if (alphaMode && diffOpaqueBit == 0 && (modifierNdx == 0 || modifierNdx == 2))
+ modifier = 0;
+ else
+ modifier = modifierLUT[tableNdx][modifierNdx];
+
+ dst[dstOffset+0] = (UInt8)Clamp<int>((int)baseR[subBlock] + modifier, 0, 255);
+ dst[dstOffset+1] = (UInt8)Clamp<int>((int)baseG[subBlock] + modifier, 0, 255);
+ dst[dstOffset+2] = (UInt8)Clamp<int>((int)baseB[subBlock] + modifier, 0, 255);
+
+ if (alphaMode)
+ alphaDst[alphaDstOffset] = 255;
+ }
+ }
+ }
+ else if (mode == kBlockModeT || mode == kBlockModeH)
+ {
+ static const int distTable[8] = { 3, 6, 11, 16, 23, 32, 41, 64 };
+
+ UInt8 paintR[4];
+ UInt8 paintG[4];
+ UInt8 paintB[4];
+
+ if (mode == kBlockModeT)
+ {
+ // T mode, calculate paint values.
+ UInt8 R1a = (UInt8)GetBitRange(src, 59, 60);
+ UInt8 R1b = (UInt8)GetBitRange(src, 56, 57);
+ UInt8 G1 = (UInt8)GetBitRange(src, 52, 55);
+ UInt8 B1 = (UInt8)GetBitRange(src, 48, 51);
+ UInt8 R2 = (UInt8)GetBitRange(src, 44, 47);
+ UInt8 G2 = (UInt8)GetBitRange(src, 40, 43);
+ UInt8 B2 = (UInt8)GetBitRange(src, 36, 39);
+ UInt32 distNdx = (GetBitRange(src, 34, 35) << 1) | GetSingleBit(src, 32);
+ int dist = distTable[distNdx];
+
+ paintR[0] = ExtendTo8(((R1a << 2) | R1b), 4);
+ paintG[0] = ExtendTo8(G1, 4);
+ paintB[0] = ExtendTo8(B1, 4);
+ paintR[2] = ExtendTo8(R2, 4);
+ paintG[2] = ExtendTo8(G2, 4);
+ paintB[2] = ExtendTo8(B2, 4);
+ paintR[1] = (UInt8)Clamp<int>((int)paintR[2] + dist, 0, 255);
+ paintG[1] = (UInt8)Clamp<int>((int)paintG[2] + dist, 0, 255);
+ paintB[1] = (UInt8)Clamp<int>((int)paintB[2] + dist, 0, 255);
+ paintR[3] = (UInt8)Clamp<int>((int)paintR[2] - dist, 0, 255);
+ paintG[3] = (UInt8)Clamp<int>((int)paintG[2] - dist, 0, 255);
+ paintB[3] = (UInt8)Clamp<int>((int)paintB[2] - dist, 0, 255);
+ }
+ else
+ {
+ // H mode, calculate paint values.
+ UInt8 R1 = (UInt8)GetBitRange(src, 59, 62);
+ UInt8 G1a = (UInt8)GetBitRange(src, 56, 58);
+ UInt8 G1b = (UInt8)GetSingleBit(src, 52);
+ UInt8 B1a = (UInt8)GetSingleBit(src, 51);
+ UInt8 B1b = (UInt8)GetBitRange(src, 47, 49);
+ UInt8 R2 = (UInt8)GetBitRange(src, 43, 46);
+ UInt8 G2 = (UInt8)GetBitRange(src, 39, 42);
+ UInt8 B2 = (UInt8)GetBitRange(src, 35, 38);
+ UInt8 baseR[2];
+ UInt8 baseG[2];
+ UInt8 baseB[2];
+ UInt32 baseValue[2];
+ UInt32 distNdx;
+ int dist;
+
+ baseR[0] = ExtendTo8(R1, 4);
+ baseG[0] = ExtendTo8(((G1a << 1) | G1b), 4);
+ baseB[0] = ExtendTo8(((B1a << 3) | B1b), 4);
+ baseR[1] = ExtendTo8(R2, 4);
+ baseG[1] = ExtendTo8(G2, 4);
+ baseB[1] = ExtendTo8(B2, 4);
+ baseValue[0] = (((UInt32)baseR[0]) << 16) | (((UInt32)baseG[0]) << 8) | baseB[0];
+ baseValue[1] = (((UInt32)baseR[1]) << 16) | (((UInt32)baseG[1]) << 8) | baseB[1];
+ distNdx = (GetSingleBit(src, 34) << 2) | (GetSingleBit(src, 32) << 1) | (UInt32)(baseValue[0] >= baseValue[1]);
+ dist = distTable[distNdx];
+
+ paintR[0] = (UInt8)Clamp<int>((int)baseR[0] + dist, 0, 255);
+ paintG[0] = (UInt8)Clamp<int>((int)baseG[0] + dist, 0, 255);
+ paintB[0] = (UInt8)Clamp<int>((int)baseB[0] + dist, 0, 255);
+ paintR[1] = (UInt8)Clamp<int>((int)baseR[0] - dist, 0, 255);
+ paintG[1] = (UInt8)Clamp<int>((int)baseG[0] - dist, 0, 255);
+ paintB[1] = (UInt8)Clamp<int>((int)baseB[0] - dist, 0, 255);
+ paintR[2] = (UInt8)Clamp<int>((int)baseR[1] + dist, 0, 255);
+ paintG[2] = (UInt8)Clamp<int>((int)baseG[1] + dist, 0, 255);
+ paintB[2] = (UInt8)Clamp<int>((int)baseB[1] + dist, 0, 255);
+ paintR[3] = (UInt8)Clamp<int>((int)baseR[1] - dist, 0, 255);
+ paintG[3] = (UInt8)Clamp<int>((int)baseG[1] - dist, 0, 255);
+ paintB[3] = (UInt8)Clamp<int>((int)baseB[1] - dist, 0, 255);
+ }
+
+ for (int pixelNdx = 0; pixelNdx < kETC2BlockHeight*kETC2BlockWidth; pixelNdx++)
+ {
+ int x = pixelNdx / kETC2BlockHeight;
+ int y = pixelNdx % kETC2BlockHeight;
+ int dstOffset = (y*kETC2BlockWidth + x)*kETC2UncompressedPixelSizeRGB8;
+ UInt32 paintNdx = (GetSingleBit(src, 16+pixelNdx) << 1) | GetSingleBit(src, pixelNdx);
+ int alphaDstOffset = (y*kETC2BlockWidth + x)*kETC2UncompressedPixelSizeA8; // Only needed for PUNCHTHROUGH version.
+
+ if (alphaMode && diffOpaqueBit == 0 && paintNdx == 2)
+ {
+ dst[dstOffset+0] = 0;
+ dst[dstOffset+1] = 0;
+ dst[dstOffset+2] = 0;
+ alphaDst[alphaDstOffset] = 0;
+ }
+ else
+ {
+ dst[dstOffset+0] = (UInt8)Clamp<int>((int)paintR[paintNdx], 0, 255);
+ dst[dstOffset+1] = (UInt8)Clamp<int>((int)paintG[paintNdx], 0, 255);
+ dst[dstOffset+2] = (UInt8)Clamp<int>((int)paintB[paintNdx], 0, 255);
+
+ if (alphaMode)
+ alphaDst[alphaDstOffset] = 255;
+ }
+ }
+ }
+ else
+ {
+ // Planar mode.
+ UInt8 GO1 = (UInt8)GetSingleBit(src, 56);
+ UInt8 GO2 = (UInt8)GetBitRange(src, 49, 54);
+ UInt8 BO1 = (UInt8)GetSingleBit(src, 48);
+ UInt8 BO2 = (UInt8)GetBitRange(src, 43, 44);
+ UInt8 BO3 = (UInt8)GetBitRange(src, 39, 41);
+ UInt8 RH1 = (UInt8)GetBitRange(src, 34, 38);
+ UInt8 RH2 = (UInt8)GetSingleBit(src, 32);
+ UInt8 RO = ExtendTo8((UInt8)GetBitRange(src, 57, 62), 6);
+ UInt8 GO = ExtendTo8(((GO1 << 6) | GO2), 7);
+ UInt8 BO = ExtendTo8(((BO1 << 5) | (BO2 << 3) | BO3), 6);
+ UInt8 RH = ExtendTo8(((RH1 << 1) | RH2), 6);
+ UInt8 GH = ExtendTo8((UInt8)GetBitRange(src, 25, 31), 7);
+ UInt8 BH = ExtendTo8((UInt8)GetBitRange(src, 19, 24), 6);
+ UInt8 RV = ExtendTo8((UInt8)GetBitRange(src, 13, 18), 6);
+ UInt8 GV = ExtendTo8((UInt8)GetBitRange(src, 6, 12), 7);
+ UInt8 BV = ExtendTo8((UInt8)GetBitRange(src, 0, 5), 6);
+
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 0; x < 4; x++)
+ {
+ int dstOffset = (y*kETC2BlockWidth + x)*kETC2UncompressedPixelSizeRGB8;
+ int unclampedR = (x * ((int)RH-(int)RO) + y * ((int)RV-(int)RO) + 4*(int)RO + 2) >> 2;
+ int unclampedG = (x * ((int)GH-(int)GO) + y * ((int)GV-(int)GO) + 4*(int)GO + 2) >> 2;
+ int unclampedB = (x * ((int)BH-(int)BO) + y * ((int)BV-(int)BO) + 4*(int)BO + 2) >> 2;
+ int alphaDstOffset = (y*kETC2BlockWidth + x)*kETC2UncompressedPixelSizeA8; // Only needed for PUNCHTHROUGH version.
+
+ dst[dstOffset+0] = (UInt8)Clamp<int>(unclampedR, 0, 255);
+ dst[dstOffset+1] = (UInt8)Clamp<int>(unclampedG, 0, 255);
+ dst[dstOffset+2] = (UInt8)Clamp<int>(unclampedB, 0, 255);
+
+ if (alphaMode)
+ alphaDst[alphaDstOffset] = 255;
+ }
+ }
+ }
+}
+
+static void DecompressEAC8Block (UInt8 dst[kETC2UncompressedBlockSizeA8], UInt64 src)
+{
+ static const int modifierLUT[16][8] =
+ {
+ { -3, -6, -9, -15, 2, 5, 8, 14 },
+ { -3, -7, -10, -13, 2, 6, 9, 12 },
+ { -2, -5, -8, -13, 1, 4, 7, 12 },
+ { -2, -4, -6, -13, 1, 3, 5, 12 },
+ { -3, -6, -8, -12, 2, 5, 7, 11 },
+ { -3, -7, -9, -11, 2, 6, 8, 10 },
+ { -4, -7, -8, -11, 3, 6, 7, 10 },
+ { -3, -5, -8, -11, 2, 4, 7, 10 },
+ { -2, -6, -8, -10, 1, 5, 7, 9 },
+ { -2, -5, -8, -10, 1, 4, 7, 9 },
+ { -2, -4, -8, -10, 1, 3, 7, 9 },
+ { -2, -5, -7, -10, 1, 4, 6, 9 },
+ { -3, -4, -7, -10, 2, 3, 6, 9 },
+ { -1, -2, -3, -10, 0, 1, 2, 9 },
+ { -4, -6, -8, -9, 3, 5, 7, 8 },
+ { -3, -5, -7, -9, 2, 4, 6, 8 }
+ };
+
+ UInt8 baseCodeword = (UInt8)GetBitRange(src, 56, 63);
+ UInt8 multiplier = (UInt8)GetBitRange(src, 52, 55);
+ UInt32 tableNdx = GetBitRange(src, 48, 51);
+
+ for (int pixelNdx = 0; pixelNdx < kETC2BlockHeight*kETC2BlockWidth; pixelNdx++)
+ {
+ int x = pixelNdx / kETC2BlockHeight;
+ int y = pixelNdx % kETC2BlockHeight;
+ int dstOffset = (y*kETC2BlockWidth + x)*kETC2UncompressedPixelSizeA8;
+ int pixelBitNdx = 45 - 3*pixelNdx;
+ UInt32 modifierNdx = (GetSingleBit(src, pixelBitNdx + 2) << 2) | (GetSingleBit(src, pixelBitNdx + 1) << 1) | GetSingleBit(src, pixelBitNdx);
+ int modifier = modifierLUT[tableNdx][modifierNdx];
+
+ dst[dstOffset] = (UInt8)Clamp<int>((int)baseCodeword + (int)multiplier*modifier, 0, 255);
+ }
+}
+
+// Decompression API
+
+void DecompressETC2_RGB8 (UInt8* dst, const UInt8* src, int width, int height)
+{
+ int numBlocksX = DivRoundUp(width, 4);
+ int numBlocksY = DivRoundUp(height, 4);
+ int dstPixelSize = kETC2UncompressedPixelSizeRGBA8;
+ int dstRowPitch = width*dstPixelSize;
+
+ for (int blockY = 0; blockY < numBlocksY; blockY++)
+ {
+ for (int blockX = 0; blockX < numBlocksX; blockX++)
+ {
+ UInt64 compressedBlock = Get64BitBlock(src, blockY*numBlocksX + blockX);
+ UInt8 uncompressedBlock[kETC2UncompressedBlockSizeRGB8];
+
+ DecompressETC2Block(compressedBlock, uncompressedBlock, NULL /* no alpha */, false);
+
+ int baseX = blockX*kETC2BlockWidth;
+ int baseY = blockY*kETC2BlockHeight;
+ for (int y = 0; y < std::min((int)kETC2BlockHeight, height-baseY); y++)
+ {
+ for (int x = 0; x < std::min((int)kETC2BlockWidth, width-baseX); x++)
+ {
+ const UInt8* srcPixel = &uncompressedBlock[(y*kETC2BlockWidth + x)*kETC2UncompressedPixelSizeRGB8];
+ UInt8* dstPixel = dst + (baseY+y)*dstRowPitch + (baseX+x)*dstPixelSize;
+
+ dstPixel[0] = srcPixel[0];
+ dstPixel[1] = srcPixel[1];
+ dstPixel[2] = srcPixel[2];
+ dstPixel[3] = 0xff; // Force alpha to 1
+ }
+ }
+ }
+ }
+}
+
+void DecompressETC2_RGB8_A1 (UInt8* dst, const UInt8* src, int width, int height)
+{
+ int numBlocksX = DivRoundUp(width, 4);
+ int numBlocksY = DivRoundUp(height, 4);
+ int dstPixelSize = kETC2UncompressedPixelSizeRGBA8;
+ int dstRowPitch = width*dstPixelSize;
+
+ for (int blockY = 0; blockY < numBlocksY; blockY++)
+ {
+ for (int blockX = 0; blockX < numBlocksX; blockX++)
+ {
+ UInt64 compressedBlockRGBA = Get64BitBlock(src, blockY*numBlocksX + blockX);
+ UInt8 uncompressedBlockRGB[kETC2UncompressedBlockSizeRGB8];
+ UInt8 uncompressedBlockAlpha[kETC2UncompressedBlockSizeA8];
+
+ DecompressETC2Block(compressedBlockRGBA, uncompressedBlockRGB, uncompressedBlockAlpha, true);
+
+ int baseX = blockX*kETC2BlockWidth;
+ int baseY = blockY*kETC2BlockHeight;
+ for (int y = 0; y < std::min((int)kETC2BlockHeight, height-baseY); y++)
+ {
+ for (int x = 0; x < std::min((int)kETC2BlockWidth, width-baseX); x++)
+ {
+ const UInt8* srcPixel = &uncompressedBlockRGB[(y*kETC2BlockWidth + x)*kETC2UncompressedPixelSizeRGB8];
+ const UInt8* srcPixelAlpha = &uncompressedBlockAlpha[(y*kETC2BlockWidth + x)*kETC2UncompressedPixelSizeA8];
+ UInt8* dstPixel = dst + (baseY+y)*dstRowPitch + (baseX+x)*dstPixelSize;
+
+ dstPixel[0] = srcPixel[0];
+ dstPixel[1] = srcPixel[1];
+ dstPixel[2] = srcPixel[2];
+ dstPixel[3] = srcPixelAlpha[0];
+ }
+ }
+ }
+ }
+}
+
+void DecompressETC2_RGBA8 (UInt8* dst, const UInt8* src, int width, int height)
+{
+ int numBlocksX = DivRoundUp(width, 4);
+ int numBlocksY = DivRoundUp(height, 4);
+ int dstPixelSize = kETC2UncompressedPixelSizeRGBA8;
+ int dstRowPitch = width*dstPixelSize;
+
+ for (int blockY = 0; blockY < numBlocksY; blockY++)
+ {
+ for (int blockX = 0; blockX < numBlocksX; blockX++)
+ {
+ UInt64 compressedBlockAlpha = Get128BitBlockStart(src, blockY*numBlocksX + blockX);
+ UInt64 compressedBlockRGB = Get128BitBlockEnd(src, blockY*numBlocksX + blockX);
+ UInt8 uncompressedBlockAlpha[kETC2UncompressedBlockSizeA8];
+ UInt8 uncompressedBlockRGB[kETC2UncompressedBlockSizeRGB8];
+
+ DecompressETC2Block(compressedBlockRGB, uncompressedBlockRGB, NULL /* no alpha */, false);
+ DecompressEAC8Block(uncompressedBlockAlpha, compressedBlockAlpha);
+
+ int baseX = blockX*kETC2BlockWidth;
+ int baseY = blockY*kETC2BlockHeight;
+ for (int y = 0; y < std::min((int)kETC2BlockHeight, height-baseY); y++)
+ {
+ for (int x = 0; x < std::min((int)kETC2BlockWidth, width-baseX); x++)
+ {
+ const UInt8* srcPixelRGB = &uncompressedBlockRGB[(y*kETC2BlockWidth + x)*kETC2UncompressedPixelSizeRGB8];
+ const UInt8* srcPixelAlpha = &uncompressedBlockAlpha[(y*kETC2BlockWidth + x)*kETC2UncompressedPixelSizeA8];
+ UInt8* dstPixel = dst + (baseY+y)*dstRowPitch + (baseX+x)*dstPixelSize;
+
+ dstPixel[0] = srcPixelRGB[0];
+ dstPixel[1] = srcPixelRGB[1];
+ dstPixel[2] = srcPixelRGB[2];
+ dstPixel[3] = srcPixelAlpha[0];
+ }
+ }
+ }
+ }
+}
diff --git a/Runtime/Graphics/ETC2Decompression.h b/Runtime/Graphics/ETC2Decompression.h
new file mode 100644
index 0000000..5e97047
--- /dev/null
+++ b/Runtime/Graphics/ETC2Decompression.h
@@ -0,0 +1,39 @@
+#ifndef ETC2DECOMPRESSION_H
+#define ETC2DECOMPRESSION_H
+
+#include "UnityPrefix.h"
+
+// ETC-2 decompressor
+//
+// Input is expected to be in standard ETC-2 (+EAC) memory layout. In ETC2 block size is 64b
+// and alpha EAC block adds another 64b.
+//
+// Output must be RGBA with 8 bits for each channel, stored in R, G, B, A order
+// (from offset 0 to 3).
+//
+// Texture sizes that are not exactly divisible by block size are supported. In such
+// case block count is rounded up and extra decoded pixels are just ignored.
+
+//! ETC2 memory layout configuration
+enum
+{
+ kETC2BlockWidth = 4,
+ kETC2BlockHeight = 4,
+ kETC2UncompressedPixelSizeA8 = 1,
+ kETC2UncompressedPixelSizeRGB8 = 3,
+ kETC2UncompressedPixelSizeRGBA8 = 4,
+ kETC2UncompressedBlockSizeA8 = kETC2BlockWidth*kETC2BlockHeight*kETC2UncompressedPixelSizeA8,
+ kETC2UncompressedBlockSizeRGB8 = kETC2BlockWidth*kETC2BlockHeight*kETC2UncompressedPixelSizeRGB8,
+ kETC2UncompressedBlockSizeRGBA8 = kETC2BlockWidth*kETC2BlockHeight*kETC2UncompressedPixelSizeRGBA8
+};
+
+//! Decompress ETC2 to RGBA8 buffer
+void DecompressETC2_RGB8 (UInt8* dst, const UInt8* src, int width, int height);
+
+//! Decompress ETC2 with punchthrough alpha to RGBA8 buffer
+void DecompressETC2_RGB8_A1 (UInt8* dst, const UInt8* src, int width, int height);
+
+//! Decompress ETC2+EAC to RGBA8 buffer
+void DecompressETC2_RGBA8 (UInt8* dst, const UInt8* src, int width, int height);
+
+#endif
diff --git a/Runtime/Graphics/FlashATFDecompression.h b/Runtime/Graphics/FlashATFDecompression.h
new file mode 100644
index 0000000..9819dbe
--- /dev/null
+++ b/Runtime/Graphics/FlashATFDecompression.h
@@ -0,0 +1,7 @@
+
+#if HAS_FLASH_ATF_DECOMPRESSOR
+
+// Flash ATFFormat to kTexFormatRGBA32
+void DecompressFlashATFTexture ( const UInt8* srcData, int dstWidth, int dstHeight, int mipLevel, UInt8* dstData );
+
+#endif \ No newline at end of file
diff --git a/Runtime/Graphics/GeneratedTextures.cpp b/Runtime/Graphics/GeneratedTextures.cpp
new file mode 100644
index 0000000..c27eb2c
--- /dev/null
+++ b/Runtime/Graphics/GeneratedTextures.cpp
@@ -0,0 +1,258 @@
+#include "UnityPrefix.h"
+#include "External/shaderlab/Library/properties.h"
+#include "Texture.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "GeneratedTextures.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Camera/Light.h"
+#include "Runtime/Math/Random/rand.h"
+#include "Runtime/Math/Random/Random.h"
+#include "TextureGenerator.h"
+#include "Runtime/GfxDevice/BuiltinShaderParams.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+#include <limits>
+
+static PPtr<Texture2D> gWhiteTex = NULL;
+static PPtr<Texture2D> gBlackTex = NULL;
+static PPtr<Texture2D> gAttenuationTex = NULL;
+static PPtr<Texture2D> gHaloTex = NULL;
+static PPtr<Texture3D> gDitherMaskTex = NULL;
+static PPtr<Texture2D> s_RandomRotationTex;
+static PPtr<Texture2D> s_NormalMapTex;
+static PPtr<Texture2D> s_RedTex;
+static PPtr<Texture2D> s_GrayTex;
+static PPtr<Texture2D> s_GrayRampTex;
+
+
+static TextureID gDefaultTextures[kTexDimCount];
+
+static Rand gRandomSeed = 0;
+
+using namespace ShaderLab;
+using namespace std;
+
+
+
+inline void EmptyNormalMap (Texture2D *tex, unsigned char *data, int x, int y, int maxX, int maxY) {
+ // make "empty normal map" work in both plain encoding and DXT5nm
+ data[0] = 127; // X=0.5 for plain
+ data[1] = 127; // Y=0.5 for plain & DXT5nm
+ data[2] = 255; // Z=1.0 for plain
+ data[3] = 127; // X=0.5 for DXT5nm
+}
+
+inline void White (Texture2D *tex, unsigned char *data, int x, int y, int maxX, int maxY) {
+ data[0] = 255;
+ data[1] = 255;
+ data[2] = 255;
+ data[3] = 255;
+}
+
+inline void GrayscaleRamp (Texture2D *tex, unsigned char *data, int x, int y, int maxX, int maxY) {
+ data[0] = x;
+ data[1] = x;
+ data[2] = x;
+ data[3] = x;
+}
+
+inline void Black (Texture2D *tex, unsigned char *data, int x, int y, int maxX, int maxY) {
+ data[0] = 0;
+ data[1] = 0;
+ data[2] = 0;
+ data[3] = 0;
+}
+
+inline void Red (Texture2D *tex, unsigned char *data, int x, int y, int maxX, int maxY) {
+ data[0] = 255;
+ data[1] = 0;
+ data[2] = 0;
+ data[3] = 0;
+}
+
+inline void Gray (Texture2D *tex, unsigned char *data, int x, int y, int maxX, int maxY) {
+ data[0] = 127;
+ data[1] = 127;
+ data[2] = 127;
+ data[3] = 127;
+}
+
+inline float ToUnsigned(const float s)
+{
+ return 0.5f * s + 0.5f;
+}
+
+inline void RandomRotation (Texture2D *tex, unsigned char *data, int x, int y, int maxX, int maxY) {
+ float randAngle = 2.0f * kPI * Random01(gRandomSeed);
+ data[0] = NormalizedToByte (ToUnsigned ( Cos (randAngle)));
+ data[1] = NormalizedToByte (ToUnsigned (-Sin (randAngle)));
+ data[2] = NormalizedToByte (ToUnsigned ( Sin (randAngle)));
+ data[3] = NormalizedToByte (ToUnsigned ( Cos (randAngle)));
+}
+
+template <typename T>
+inline void LightAttenuation (Texture2D *tex, T *data, int x, int y, int maxX, int maxY)
+{
+ float sqrRange = (float)x / (float)maxX;
+ float val = Light::AttenuateNormalized(sqrRange);
+ T b = RoundfToInt( val * std::numeric_limits<T>::max() );
+ *data = b;
+}
+
+inline void HaloTex (Texture2D *tex, unsigned char *data, int x, int y, int maxX, int maxY)
+{
+ maxX >>= 1;
+ maxY >>= 1;
+ float xFac = (float)(x - maxX) / (float)(maxX);
+ float yFac = (float)(y - maxY) / (float)(maxY);
+ float sqrRange = xFac * xFac + yFac * yFac;
+ if (sqrRange > 1.0f)
+ sqrRange = 1.0f;
+ *data = RoundfToInt((1.0f - sqrRange) * 255.0f);
+ return;
+}
+
+inline void WhiteTex (Texture2D *tex, unsigned char *data, int x, int y, int maxX, int maxY) {
+ *data = 255;
+}
+
+inline void Empty1D (Texture2D *tex, unsigned char *data, int x, int maxX) {
+ data[0] = data[1] = data[2] = data[3] = 128;
+}
+inline void Empty2D (Texture2D *tex, unsigned char *data, int x, int y, int maxX, int maxY) {
+ data[0] = data[1] = data[2] = data[3] = 128;
+}
+inline void Empty3D( unsigned char *data, int x, int y, int z, int maxX, int maxY, int maxZ ) {
+ data[0] = data[1] = data[2] = data[3] = 128;
+}
+inline void EmptyCube( unsigned char *data, Vector3f normal ) {
+ data[0] = data[1] = data[2] = data[3] = 128;
+}
+
+void GenerateDitherTextures()
+{
+ const int width = 4;
+ const int height = 4;
+ const int numSlices = 16;
+ const int numPixelsPerSlice = width*height;
+
+ const unsigned char mask [numPixelsPerSlice] =
+ {
+ 0,9,3,9,
+ 9,4,9,7,
+ 2,9,1,9,
+ 9,6,9,5,
+ };
+
+ gDitherMaskTex = CreateObjectFromCode<Texture3D>();
+ gDitherMaskTex->SetHideFlags(Object::kHideAndDontSave);
+ gDitherMaskTex->InitTexture (width, height, 16, kTexFormatAlpha8, false);
+ gDitherMaskTex->GetSettings().m_Aniso = 0; // disable aniso
+ gDitherMaskTex->GetSettings().m_FilterMode = 0; // disable aniso
+
+ gDitherMaskTex->ApplySettings ();
+
+ unsigned char* data = (unsigned char*)gDitherMaskTex->GetImageDataPointer();
+ for(int slice = 0; slice < numSlices/2; slice++)
+ {
+ int index = slice;
+ int invIndex = numSlices-1-slice;
+ unsigned char* sliceData0 = data + numPixelsPerSlice * index;
+ unsigned char* sliceData1 = data + numPixelsPerSlice * invIndex;
+ for(int i = 0; i < numPixelsPerSlice; i++)
+ {
+ unsigned char maskValue = mask[i] >= slice ? 0x00 : 0xff;
+ sliceData0[i] = maskValue;
+ sliceData1[i] = ~maskValue;
+ }
+ }
+ gDitherMaskTex->UpdateImageData(false);
+}
+
+
+void builtintex::ReinitBuiltinTextures()
+{
+# define INIT_BUILTIN_TEX(texI, tsI, tex) GetGfxDevice().GetBuiltinParamValues().GetWritableTexEnvParam(texI).InitFromTexture(tex, NULL, tsI)
+
+ INIT_BUILTIN_TEX(kShaderTexEnvWhite, kShaderVecWhiteTexelSize, gWhiteTex);
+ INIT_BUILTIN_TEX(kShaderTexEnvBlack, kShaderVecBlackTexelSize, gBlackTex);
+ INIT_BUILTIN_TEX(kShaderTexEnvRed, kShaderVecRedTexelSize, s_RedTex);
+ INIT_BUILTIN_TEX(kShaderTexEnvGray, kShaderVecGrayTexelSize, s_GrayTex);
+ INIT_BUILTIN_TEX(kShaderTexEnvGrey, kShaderVecGreyTexelSize, s_GrayTex);
+ INIT_BUILTIN_TEX(kShaderTexEnvGrayscaleRamp, kShaderVecGrayscaleRampTexelSize, s_GrayRampTex);
+ INIT_BUILTIN_TEX(kShaderTexEnvGreyscaleRamp, kShaderVecGreyscaleRampTexelSize, s_GrayRampTex);
+ INIT_BUILTIN_TEX(kShaderTexEnvBump, kShaderVecBumpTexelSize, s_NormalMapTex);
+ INIT_BUILTIN_TEX(kShaderTexEnvLightmap, kShaderVecLightmapTexelSize, gBlackTex);
+ INIT_BUILTIN_TEX(kShaderTexEnvUnityLightmap, kShaderVecUnityLightmapTexelSize, gBlackTex);
+ INIT_BUILTIN_TEX(kShaderTexEnvUnityLightmapInd, kShaderVecUnityLightmapIndTexelSize, gBlackTex);
+ INIT_BUILTIN_TEX(kShaderTexEnvUnityLightmapThird, kShaderVecUnityLightmapThirdTexelSize, gBlackTex);
+ INIT_BUILTIN_TEX(kShaderTexEnvDitherMaskLOD, kShaderVecDitherMaskLODSize, gDitherMaskTex);
+ INIT_BUILTIN_TEX(kShaderTexEnvRandomRotation, kShaderVecRandomRotationTexelSize, s_RandomRotationTex);
+
+# undef INIT_BUILTIN_TEX
+}
+
+
+void builtintex::GenerateBuiltinTextures()
+{
+ static bool texturesGenerated = false;
+ if( texturesGenerated )
+ return;
+ texturesGenerated = true;
+
+ s_NormalMapTex = BuildTexture<unsigned char> (4, 4, kTexFormatRGBA32, &EmptyNormalMap);
+
+ gWhiteTex = BuildTexture<unsigned char> (4, 4, kTexFormatRGBA32, &White);
+
+ gBlackTex = BuildTexture<unsigned char> (4, 4, kTexFormatRGBA32, &Black);
+
+ // Random rotation texture for rotating points. Stores unsigned cos(theta) in .r and unsigned sin(theta) in .g
+ s_RandomRotationTex = BuildTexture<unsigned char> (16, 16, kTexFormatRGBA32, &RandomRotation);
+
+ // The red texture will be created with mipmaps. The reason is a driver bug in Nvidia GTX 4xx cards
+ // (Win7, driver 270.61), which makes terrain's FirstPass splat map shader flicker like mad if
+ // the control texture has no mips and is not compressed. This happens on any new terrain
+ // (the red texture is used there by default).
+ s_RedTex = BuildTexture<unsigned char> (4, 4, kTexFormatRGBA32, &Red, true);
+
+ s_GrayTex = BuildTexture<unsigned char> (4, 4, kTexFormatRGBA32, &Gray);
+
+ s_GrayRampTex = BuildTexture<unsigned char> (256, 2, kTexFormatRGBA32, &GrayscaleRamp);
+ s_GrayRampTex->GetSettings().m_WrapMode = kTexWrapClamp;
+ s_GrayRampTex->ApplySettings ();
+
+ gHaloTex = BuildTexture<unsigned char> (64, 64, kTexFormatAlpha8, &HaloTex);
+ gHaloTex->GetSettings ().m_WrapMode = kTexWrapClamp;
+ gHaloTex->ApplySettings ();
+
+ if (gGraphicsCaps.supportsTextureFormat[kTexFormatAlphaLum16])
+ gAttenuationTex = BuildTexture<unsigned short> (1024, 1, kTexFormatAlphaLum16, &LightAttenuation<unsigned short>);
+ else
+ gAttenuationTex = BuildTexture<unsigned char> (1024, 1, kTexFormatAlpha8, &LightAttenuation<unsigned char>);
+ gAttenuationTex->GetSettings ().m_WrapMode = kTexWrapClamp;
+ gAttenuationTex->ApplySettings ();
+
+ // Initialize the textures used when no texture has been bound.
+ gDefaultTextures[kTexDim2D] = BuildTexture<unsigned char> (16,16, kTexFormatRGBA32, &Empty2D)->GetTextureID();
+
+ if (gGraphicsCaps.has3DTexture)
+ gDefaultTextures[kTexDim3D] = Build3DTexture<unsigned char> (1,1,1, kTexFormatRGBA32, &Empty3D)->GetTextureID();
+
+ gDefaultTextures[kTexDimCUBE] = BuildCubeTexture<unsigned char> (1, kTexFormatRGBA32, &EmptyCube)->GetTextureID();
+
+ if (gGraphicsCaps.has3DTexture)
+ GenerateDitherTextures();
+
+ // Make the default for a 'any' be the same as the 2D case
+ gDefaultTextures[kTexDimAny] = gDefaultTextures[kTexDim2D];
+
+ ReinitBuiltinTextures();
+}
+
+Texture2D* builtintex::GetWhiteTexture() { return gWhiteTex; }
+Texture2D* builtintex::GetBlackTexture() { return gBlackTex; }
+Texture3D* builtintex::GetDitherMaskTexture() { return gDitherMaskTex; }
+Texture* builtintex::GetAttenuationTexture() { return gAttenuationTex; }
+Texture* builtintex::GetHaloTexture() { return gHaloTex; }
+TextureID builtintex::GetDefaultTexture( TextureDimension texDim ) { return gDefaultTextures[texDim]; }
+TextureID builtintex::GetBlackTextureID () { return gBlackTex->GetTextureID(); }
diff --git a/Runtime/Graphics/GeneratedTextures.h b/Runtime/Graphics/GeneratedTextures.h
new file mode 100644
index 0000000..bc474c5
--- /dev/null
+++ b/Runtime/Graphics/GeneratedTextures.h
@@ -0,0 +1,28 @@
+#ifndef GENERATEDTEXTURES_H
+#define GENERATEDTEXTURES_H
+
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+class Texture;
+class Texture2D;
+class Texture3D;
+
+namespace builtintex {
+
+void GenerateBuiltinTextures();
+void ReinitBuiltinTextures();
+
+Texture2D* GetWhiteTexture ();
+Texture2D* GetBlackTexture ();
+Texture3D* GetDitherMaskTexture();
+Texture* GetAttenuationTexture ();
+Texture* GetHaloTexture ();
+
+// Get the default texture for a texture dimension
+TextureID GetDefaultTexture( TextureDimension texDim );
+
+TextureID GetBlackTextureID ();
+
+} // namespace
+
+
+#endif
diff --git a/Runtime/Graphics/GraphicsHelper.cpp b/Runtime/Graphics/GraphicsHelper.cpp
new file mode 100644
index 0000000..6a32e72
--- /dev/null
+++ b/Runtime/Graphics/GraphicsHelper.cpp
@@ -0,0 +1,103 @@
+#include "UnityPrefix.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "GraphicsHelper.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Shaders/Shader.h"
+#include "External/shaderlab/Library/intshader.h"
+
+namespace GraphicsHelper {
+
+void Clear( UInt32 clearFlags, const float color[4], float depth, int stencil )
+{
+ GfxDevice &device = GetGfxDevice();
+
+
+#if UNITY_WIN
+ int viewport[4];
+ device.GetViewport(viewport);
+
+ bool canUseNativeClear =
+ gGraphicsCaps.hasNonFullscreenClear ||
+ (viewport[0]==0 && viewport[1]==0 && viewport[2]==device.GetCurrentTargetWidth() && viewport[3]==device.GetCurrentTargetHeight());
+ if (!canUseNativeClear)
+ {
+#if ENABLE_MULTITHREADED_CODE
+ DebugAssert(Thread::CurrentThreadIsMainThread());
+#endif
+
+ // Some devices (e.g. D3D11) can't clear sub-areas of a render target, so must draw a quad instead.
+ Shader* clearShader = Shader::GetScreenClearShader();
+ if (!clearShader || clearShader->GetShaderLabShader()->GetActiveSubShader().GetValidPassCount() != 4)
+ {
+ AssertString ("Valid screen clear shader not found");
+ return;
+ }
+ ShaderLab::SubShader& ss = clearShader->GetShaderLabShader()->GetActiveSubShader();
+ const int clearIndex = clearFlags & 3;
+ ss.GetPass (clearIndex)->ApplyPass(0, NULL);
+
+ bool oldWireframe = device.GetWireframe();
+ device.SetWireframe (false);
+ DeviceMVPMatricesState saveMVPMatrices;
+ LoadFullScreenOrthoMatrix ();
+ device.ImmediateBegin (kPrimitiveQuads);
+ device.ImmediateColor(color[0], color[1], color[2], color[3]);
+ float z = -100.0f - 1e-9f;
+ device.ImmediateVertex (0.0f, 0.0f, z);
+ device.ImmediateVertex (0.0f, 1.0f, z);
+ device.ImmediateVertex (1.0f, 1.0f, z);
+ device.ImmediateVertex (1.0f, 0.0f, z);
+ device.ImmediateEnd ();
+ device.SetWireframe (oldWireframe);
+ return;
+ }
+#endif
+
+ device.Clear(clearFlags, color, depth, stencil);
+}
+
+void SetBlendState( const DeviceBlendState* state, const ShaderLab::FloatVal& alphaRef, const ShaderLab::PropertySheet* props )
+{
+ GfxDevice& device = GetGfxDevice();
+ if (device.IsRecording())
+ device.RecordSetBlendState(state, alphaRef, props);
+ else
+ device.SetBlendState(state, alphaRef.ToFloat(props));
+}
+
+void SetMaterial( const ShaderLab::VectorVal& ambient, const ShaderLab::VectorVal& diffuse, const ShaderLab::VectorVal& specular, const ShaderLab::VectorVal& emissive, const ShaderLab::FloatVal& shininess, const ShaderLab::PropertySheet* props )
+{
+ GfxDevice& device = GetGfxDevice();
+ if (device.IsRecording())
+ device.RecordSetMaterial(ambient, diffuse, specular, emissive, shininess, props);
+ else
+ device.SetMaterial(ambient.Get(props).GetPtr(), diffuse.Get(props).GetPtr(), specular.Get(props).GetPtr(), emissive.Get(props).GetPtr(), shininess.ToFloat(props));
+}
+
+void SetColor( const ShaderLab::VectorVal& color, const ShaderLab::PropertySheet* props )
+{
+ GfxDevice& device = GetGfxDevice();
+ if (device.IsRecording())
+ device.RecordSetColor(color, props);
+ else
+ device.SetColor(color.Get(props).GetPtr());
+}
+
+void EnableFog( FogMode fogMode, const ShaderLab::FloatVal& fogStart, const ShaderLab::FloatVal& fogEnd, const ShaderLab::FloatVal& fogDensity, const ShaderLab::VectorVal& fogColor, const ShaderLab::PropertySheet* props )
+{
+ GfxDevice& device = GetGfxDevice();
+ if (device.IsRecording())
+ device.RecordEnableFog(fogMode, fogStart, fogEnd, fogDensity, fogColor, props);
+ else
+ {
+ GfxFogParams fog;
+ fog.mode = fogMode;
+ fog.color = fogColor.Get(props);
+ fog.start = fogStart.ToFloat(props);
+ fog.end = fogEnd.ToFloat(props);
+ fog.density = fogDensity.ToFloat(props);
+ device.EnableFog(fog);
+ }
+}
+
+} // namespace GfxDeviceHelper \ No newline at end of file
diff --git a/Runtime/Graphics/GraphicsHelper.h b/Runtime/Graphics/GraphicsHelper.h
new file mode 100644
index 0000000..1c23522
--- /dev/null
+++ b/Runtime/Graphics/GraphicsHelper.h
@@ -0,0 +1,98 @@
+#pragma once
+
+// Remove this when SetShaders has been inlined
+#include <Runtime/GfxDevice/GfxDevice.h>
+#include "External/shaderlab/Library/program.h"
+
+namespace ShaderLab {
+ class FloatVal;
+ struct VectorVal;
+ struct TextureBinding;
+ class PropertySheet;
+}
+
+namespace GraphicsHelper
+{
+ void Clear( UInt32 clearFlags, const float color[4], float depth, int stencil );
+
+ void SetBlendState( const DeviceBlendState* state, const ShaderLab::FloatVal& alphaRef, const ShaderLab::PropertySheet* props );
+ void SetMaterial( const ShaderLab::VectorVal& ambient, const ShaderLab::VectorVal& diffuse, const ShaderLab::VectorVal& specular, const ShaderLab::VectorVal& emissive, const ShaderLab::FloatVal& shininess, const ShaderLab::PropertySheet* props );
+ void SetColor( const ShaderLab::VectorVal& color, const ShaderLab::PropertySheet* props );
+ void EnableFog( FogMode fogMode, const ShaderLab::FloatVal& fogStart, const ShaderLab::FloatVal& fogEnd, const ShaderLab::FloatVal& fogDensity, const ShaderLab::VectorVal& fogColor, const ShaderLab::PropertySheet* props );
+
+ //TextureCombinersHandle CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular );
+ //void SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props );
+
+ // This is inlined until GfxDeviceClient has been moved out of GfxDevice module
+ inline void SetShaders (GfxDevice& device, ShaderLab::SubProgram* programs[kShaderTypeCount], const ShaderLab::PropertySheet* props)
+ {
+ // We do this outside of GfxDevice module to avoid dependency on ShaderLab
+
+ if (device.IsThreadable())
+ {
+ // This is a threadable device running in single-threaded mode
+ GfxThreadableDevice& threadableDevice = static_cast<GfxThreadableDevice&>(device);
+
+ // GLSL-like platforms might result in different set of shader parameters
+ // for fog modes.
+ const bool useGLStyleFogParams = (device.GetRenderer() == kGfxRendererOpenGL ||
+ device.GetRenderer() == kGfxRendererOpenGLES20Desktop ||
+ device.GetRenderer() == kGfxRendererOpenGLES20Mobile ||
+ device.GetRenderer() == kGfxRendererOpenGLES30);
+ const FogMode fogMode = useGLStyleFogParams ? device.GetFogParams().mode : kFogDisabled;
+
+ UInt8* paramsBuffer[kShaderTypeCount];
+ for (int pt = 0; pt < kShaderTypeCount; ++pt)
+ {
+ paramsBuffer[pt] = NULL;
+ ShaderLab::SubProgram* prog = programs[pt];
+ if (!prog)
+ continue;
+
+ GpuProgramParameters* params = &prog->GetParams(fogMode);
+ if (params && !params->IsReady())
+ threadableDevice.CreateShaderParameters (prog, fogMode);
+
+ int bufferSize = params->GetValuesSize();
+ if (bufferSize > 0)
+ {
+ paramsBuffer[pt] = ALLOC_TEMP_MANUAL (UInt8, bufferSize);
+ params->PrepareValues (props, paramsBuffer[pt]);
+ }
+ }
+
+ GpuProgram* gpuPrograms[kShaderTypeCount];
+ for (int pt = 0; pt < kShaderTypeCount; ++pt)
+ {
+ if (programs[pt])
+ gpuPrograms[pt] = &programs[pt]->GetGpuProgram();
+ else
+ gpuPrograms[pt] = NULL;
+ }
+
+ const GpuProgramParameters* params[kShaderTypeCount];
+ for (int pt = 0; pt < kShaderTypeCount; ++pt)
+ {
+ if (programs[pt])
+ params[pt] = &programs[pt]->GetParams(fogMode);
+ else
+ params[pt] = NULL;
+ }
+
+ threadableDevice.SetShadersThreadable (gpuPrograms, params, paramsBuffer);
+
+ for (int pt = 0; pt < kShaderTypeCount; ++pt)
+ {
+ if (paramsBuffer[pt])
+ FREE_TEMP_MANUAL(paramsBuffer[pt]);
+ }
+ }
+ else
+ {
+ // Old school device that doesn't support threadable interface,
+ // or GfxDeviceClient that wants to deal with parameters itself
+ device.SetShadersMainThread(programs, props);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/Runtime/Graphics/Image.cpp b/Runtime/Graphics/Image.cpp
new file mode 100644
index 0000000..b4ad02d
--- /dev/null
+++ b/Runtime/Graphics/Image.cpp
@@ -0,0 +1,1657 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Image.h"
+#include "S3Decompression.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Utilities/vector_utility.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "External/ProphecySDK/include/prcore/surface.hpp"
+
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+
+using namespace std;
+
+prcore::PixelFormat GetProphecyPixelFormat (TextureFormat format);
+
+void BlitCopyCompressedImage( TextureFormat format, const UInt8* src, int srcWidth, int srcHeight, UInt8* dst, int dstWidth, int dstHeight, bool fillRest )
+{
+ AssertIf( dstWidth < srcWidth );
+ AssertIf( dstHeight < srcHeight );
+
+ int blockBytes, srcBlocksX, srcBlocksY, dstBlocksX, dstBlocksY;
+
+ {
+ AssertIf( !IsCompressedDXTTextureFormat(format) && !IsCompressedETCTextureFormat(format)
+ && !IsCompressedATCTextureFormat(format) && !IsCompressedETC2TextureFormat(format)
+ && !IsCompressedEACTextureFormat(format));
+
+ blockBytes = (format == kTexFormatDXT1 || format == kTexFormatETC_RGB4 || format == kTexFormatATC_RGB4
+ || format == kTexFormatETC2_RGB || format == kTexFormatETC2_RGBA1
+ || format == kTexFormatEAC_R || format == kTexFormatEAC_R_SIGNED)
+ ? 8 : 16;
+
+ srcBlocksX = (srcWidth + 3) / 4;
+ srcBlocksY = (srcHeight + 3) / 4;
+ dstBlocksX = (dstWidth + 3) / 4;
+ dstBlocksY = (dstHeight + 3) / 4;
+ }
+
+ int srcRowBytes = srcBlocksX * blockBytes;
+ int dstRowBytes = dstBlocksX * blockBytes;
+
+ const UInt8* srcPtr = src;
+ UInt8* dstPtr = dst;
+ int y;
+ for( y = 0; y < srcBlocksY; ++y )
+ {
+ memcpy( dstPtr, srcPtr, srcRowBytes ); // copy DXT blocks
+ if( fillRest )
+ memset( dstPtr + srcRowBytes, 0, dstRowBytes - srcRowBytes ); // fill rest with black/transparent
+ srcPtr += srcRowBytes;
+ dstPtr += dstRowBytes;
+ }
+ if( fillRest )
+ memset( dstPtr, 0, dstRowBytes * (dstBlocksY-srcBlocksY) ); // fill rest with black/transparent
+}
+
+void BlitCopyCompressedDXT1ToDXT5( const UInt8* src, int srcWidth, int srcHeight, UInt8* dst, int dstWidth, int dstHeight )
+{
+ AssertIf( dstWidth < srcWidth );
+ AssertIf( dstHeight < srcHeight );
+
+ int blockBytesSrc = 8;
+ int blockBytesDst = 16;
+
+ int srcBlocksX = (srcWidth + 3) / 4;
+ int srcBlocksY = (srcHeight + 3) / 4;
+ int dstBlocksX = (dstWidth + 3) / 4;
+
+ int srcRowBytes = srcBlocksX * blockBytesSrc;
+ int dstRowBytes = dstBlocksX * blockBytesDst;
+
+ const UInt8* srcPtr = src;
+ UInt8* dstPtr = dst;
+ int y;
+ for( y = 0; y < srcBlocksY; ++y )
+ {
+ for( int x = 0; x < srcBlocksX; ++x )
+ {
+ // set alpha to opaque white
+ memset( dstPtr + x * blockBytesDst, 0xFF, blockBytesDst-blockBytesSrc );
+ // copy block
+ memcpy( dstPtr + x * blockBytesDst + blockBytesSrc, srcPtr + x * blockBytesSrc, blockBytesSrc );
+ }
+ srcPtr += srcRowBytes;
+ dstPtr += dstRowBytes;
+ }
+}
+
+
+void PadImageBorder( ImageReference& image, int sizeX, int sizeY )
+{
+ AssertIf( IsAnyCompressedTextureFormat(image.GetFormat()) );
+
+ int width = image.GetWidth();
+ int height = image.GetHeight();
+ AssertIf( sizeX < 1 || sizeY < 1 || sizeX > width || sizeY > height );
+
+ int y;
+ UInt8* rowptr = image.GetImageData();
+
+ int bpp = GetBytesFromTextureFormat(image.GetFormat());
+ AssertIf(bpp > 4); // we only support up to 4 bpp
+ UInt8 pixel[4];
+
+ // pad rightmost part by repeating last pixel in each row
+ for( y = 0; y < sizeY; ++y )
+ {
+ UInt8* ptr = rowptr + (sizeX-1) * bpp;
+
+ for( int b = 0; b < bpp; ++b )
+ pixel[b] = ptr[b];
+ ptr += bpp;
+ for( int x = sizeX; x < width; ++x )
+ {
+ for( int b = 0; b < bpp; ++b )
+ ptr[b] = pixel[b];
+ ptr += bpp;
+ }
+
+ rowptr += image.GetRowBytes();
+ }
+
+ // pad bottom part by repeating last row
+ UInt8* lastRowPtr = image.GetRowPtr( sizeY - 1 );
+ for( int b = 0; b < bpp; ++b )
+ pixel[b] = lastRowPtr[ (sizeX-1)*bpp + b ];
+
+ for( y = sizeY; y < height; ++y )
+ {
+ UInt8* ptr = rowptr;
+ memcpy( ptr, lastRowPtr, sizeX * bpp ); // copy
+ ptr += sizeX * bpp;
+ for( int x = sizeX; x < width; ++x ) // repeat last pixel
+ {
+ for( int b = 0; b < bpp; ++b )
+ ptr[b] = pixel[b];
+ ptr += bpp;
+ }
+
+ rowptr += image.GetRowBytes();
+ }
+}
+
+
+int CalculateMipMapCount3D (int width, int height, int depth)
+{
+ //AssertIf( !IsPowerOfTwo(width) || !IsPowerOfTwo(height) || !IsPowerOfTwo(depth) );
+
+ // Mip-levels for non-power-of-two textures follow OpenGL's NPOT texture rules: size is divided
+ // by two and floor'ed. This allows just to use same old code I think.
+
+ int minSizeLog2 = HighestBit (width);
+ minSizeLog2 = max (minSizeLog2, HighestBit (height));
+ minSizeLog2 = max (minSizeLog2, HighestBit (depth));
+
+ AssertIf( (width >> minSizeLog2) < 1 && (height >> minSizeLog2) < 1 && (depth >> minSizeLog2) < 1 );
+ AssertIf( (width >> minSizeLog2) > 1 && (height >> minSizeLog2) > 1 && (depth >> minSizeLog2) < 1 );
+
+ return minSizeLog2 + 1;
+}
+
+int CalculateImageSize (int width, int height, TextureFormat format)
+{
+ if (format == kTexFormatDXT1 || format == kTexFormatATC_RGB4)
+ return ((width + 3) / 4) * ((height + 3) / 4) * 8;
+ else if (format == kTexFormatDXT3 || format == kTexFormatDXT5 || format == kTexFormatATC_RGBA8)
+ return ((width + 3) / 4) * ((height + 3) / 4) * 16;
+ else if (format == kTexFormatPVRTC_RGB4 || format == kTexFormatPVRTC_RGBA4)
+ return (max(width, 8) * max(height, 8) * 4 + 7) / 8;
+ else if (format == kTexFormatPVRTC_RGB2 || format == kTexFormatPVRTC_RGBA2)
+ return (max(width, 16) * max(height, 8) * 2 + 7) / 8;
+ else if (format == kTexFormatETC_RGB4 || format == kTexFormatETC2_RGB || format == kTexFormatETC2_RGBA1
+ || format == kTexFormatEAC_R || format == kTexFormatEAC_R_SIGNED)
+ return (max(width, 4) * max(height, 4) * 4 + 7) / 8;
+ else if (format == kTexFormatETC2_RGBA8 || format == kTexFormatEAC_RG || format == kTexFormatEAC_RG_SIGNED)
+ return (max(width, 4) * max(height, 4) * 8 + 7) / 8;
+
+#define STR_(x) #x
+#define STR(x) STR_(x)
+ // The size of the ASTC block is always 128 bits = 16 bytes, width of image in blocks is ceiling(width/blockwidth)
+#define DO_ASTC(bx, by) else if( format == kTexFormatASTC_RGB_##bx##x##by || format == kTexFormatASTC_RGBA_##bx##x##by)\
+ return ( (CeilfToInt(((float)width) / ((float)bx))) * (CeilfToInt(((float)height) / ((float)by))) * 16 )
+
+ DO_ASTC(4, 4);
+ DO_ASTC(5, 5);
+ DO_ASTC(6, 6);
+ DO_ASTC(8, 8);
+ DO_ASTC(10, 10);
+ DO_ASTC(12, 12);
+
+#undef DO_ASTC
+#undef STR_
+#undef STR
+
+ // ATF Format is a container format and has no known size based on width & height
+ else if (format == kTexFormatFlashATF_RGB_DXT1 || format == kTexFormatFlashATF_RGBA_JPG || format == kTexFormatFlashATF_RGB_JPG)
+ return 0;
+ else
+ return GetRowBytesFromWidthAndFormat (width, format) * height;
+}
+
+int CalculateMipMapOffset (int width, int height, TextureFormat format, int miplevel)
+{
+ if (width == 0 || height == 0)
+ return 0;
+
+ // Allow NPOT textures as well.
+ //AssertIf (!IsPowerOfTwo (width) || !IsPowerOfTwo (height));
+ int completeSize = 0;
+ for (int i=0;i < miplevel;i++)
+ completeSize += CalculateImageSize (std::max (width >> i, 1), std::max (height >> i, 1), format);
+ return completeSize;
+}
+
+int CalculateImageMipMapSize (int width, int height, TextureFormat format)
+{
+ return CalculateMipMapOffset( width, height, format, CalculateMipMapCount3D(width, height, 1) );
+}
+
+
+
+void CreateMipMap (UInt8* inData, int width, int height, int depth, TextureFormat format)
+{
+ const int bpp = GetBytesFromTextureFormat (format);
+ int mipCount = CalculateMipMapCount3D (width, height, depth) - 1;
+ UInt8* srcPtr = inData;
+
+ UInt8* tempBuffer = NULL;
+
+ for (int mip = 0; mip < mipCount; ++mip)
+ {
+ int nextWidth = std::max(width/2,1);
+ int nextHeight = std::max(height/2,1);
+ int nextDepth = std::max(depth/2,1);
+
+ UInt8* nextMipPtr = srcPtr + width * height * depth * bpp;
+ UInt8* dstPtr = nextMipPtr;
+ if (depth > 1)
+ {
+ if (!tempBuffer)
+ tempBuffer = ALLOC_TEMP_MANUAL(UInt8, nextWidth*nextHeight*bpp+bpp);
+
+ for (int d = 0; d < nextDepth; ++d)
+ {
+ // blit two slices of this mip level into two 2x2 smaller images
+ ImageReference src1 (width, height, width * bpp, format, srcPtr);
+ srcPtr += width * height * bpp;
+ ImageReference src2 (width, height, width * bpp, format, srcPtr);
+ srcPtr += width * height * bpp;
+
+ ImageReference dst1 (nextWidth, nextHeight, nextWidth * bpp, format, dstPtr); // one directly into dst
+ dst1.BlitImage (src1, ImageReference::BLIT_BILINEAR_SCALE);
+ ImageReference dst2 (nextWidth, nextHeight, nextWidth * bpp, format, tempBuffer); // one into temp buffer
+ dst2.BlitImage (src2, ImageReference::BLIT_BILINEAR_SCALE);
+
+ // now average the two smaller images
+ for (int i = 0; i < nextWidth*nextHeight*bpp; ++i)
+ {
+ unsigned b1 = dstPtr[i];
+ unsigned b2 = tempBuffer[i];
+ dstPtr[i] = (b1 + b2) / 2;
+ }
+
+ dstPtr += nextWidth * nextHeight * bpp;
+ }
+ }
+ else
+ {
+ ImageReference src (width, height, width * bpp, format, srcPtr);
+ ImageReference dst (nextWidth, nextHeight, nextWidth * bpp, format, dstPtr);
+ dst.BlitImage (src, ImageReference::BLIT_BILINEAR_SCALE);
+ }
+
+ srcPtr = nextMipPtr;
+ width = nextWidth;
+ height = nextHeight;
+ depth = nextDepth;
+ }
+
+ FREE_TEMP_MANUAL(tempBuffer);
+}
+
+
+
+ImageReference::ImageReference (int width, int height, int rowbytes, TextureFormat format, void* image)
+{
+ m_Width = width;
+ m_Height = height;
+ m_Format = format;
+ m_RowBytes = rowbytes;
+
+ if (image && CheckImageFormatValid (width, height, format))
+ m_Image = reinterpret_cast<UInt8*> (image);
+ else
+ m_Image = NULL;
+}
+
+ImageReference::ImageReference (int width, int height, TextureFormat format)
+{
+ m_Width = width;
+ m_Height = height;
+ m_Format = format;
+ m_RowBytes = GetRowBytesFromWidthAndFormat (m_Width, format);
+ m_Image = NULL;
+}
+
+void ImageReference::FlipImageY ()
+{
+ if (m_Image)
+ {
+ prcore::Surface srcSurface (GetWidth (), GetHeight (), GetRowBytes (), GetProphecyPixelFormat (GetFormat ()), GetImageData ());
+ srcSurface.FlipImageY ();
+ }
+}
+
+#if UNITY_EDITOR
+static inline void SwapPixels( UInt8* a, UInt8* b, int bpp ) {
+ for( int i = 0; i < bpp; ++i ) {
+ UInt8 t = a[i];
+ a[i] = b[i];
+ b[i] = t;
+ }
+}
+
+void ImageReference::FlipImageX ()
+{
+ if (!m_Image)
+ return;
+
+ const int width = GetWidth();
+ const int height = GetHeight();
+ const TextureFormat format = GetFormat();
+ if( IsAnyCompressedTextureFormat(format) ) {
+ AssertString("FlipImageX does not work on compressed formats");
+ return;
+ }
+ const int bpp = GetBytesFromTextureFormat(format);
+ for( int y = 0; y < height; ++y ) {
+ UInt8* rowFirst = GetRowPtr(y);
+ UInt8* rowLast = rowFirst + (width-1) * bpp;
+ for( int x = 0; x < width/2; ++x ) {
+ SwapPixels( rowFirst, rowLast, bpp );
+ rowFirst += bpp;
+ rowLast -= bpp;
+ }
+ }
+}
+#endif
+
+
+void ImageReference::BlitImage (const ImageReference& source, BlitMode mode)
+{
+ if (m_Image && source.m_Image)
+ {
+ prcore::Surface srcSurface (source.GetWidth (), source.GetHeight (), source.GetRowBytes (), GetProphecyPixelFormat (source.GetFormat ()), source.GetImageData ());
+ prcore::Surface dstSurface (GetWidth (), GetHeight (), GetRowBytes (), GetProphecyPixelFormat (GetFormat ()), GetImageData ());
+ dstSurface.BlitImage (srcSurface, (prcore::Surface::BlitMode)mode);
+ }
+}
+
+void ImageReference::BlitImage (int x, int y, const ImageReference& source)
+{
+ if (source.m_Image && m_Image)
+ {
+ prcore::Surface srcSurface (source.GetWidth (), source.GetHeight (), source.GetRowBytes (), GetProphecyPixelFormat (source.GetFormat ()), source.GetImageData ());
+ prcore::Surface dstSurface (GetWidth (), GetHeight (), GetRowBytes (), GetProphecyPixelFormat (GetFormat ()), GetImageData ());
+ dstSurface.BlitImage (x, y, srcSurface);
+ }
+}
+
+void ImageReference::ClearImage (const ColorRGBA32& color, ClearMode mode)
+{
+ if (m_Image)
+ {
+ prcore::color32 c;
+ c.r = color.r;
+ c.g = color.g;
+ c.b = color.b;
+ c.a = color.a;
+ prcore::Surface dstSurface (GetWidth (), GetHeight (), GetRowBytes (), GetProphecyPixelFormat (GetFormat ()), GetImageData ());
+ dstSurface.ClearImage (c, (prcore::Surface::ClearMode)mode);
+ }
+}
+
+ImageReference ImageReference::ClipImage (int x, int y, int width, int height) const
+{
+ if (m_Image)
+ {
+ x = clamp<int> (x, 0, m_Width);
+ y = clamp<int> (y, 0, m_Height);
+ width = min<int> (m_Width, x + width) - x;
+ height = min<int> (m_Height, y + height) - y;
+ width = max<int> (0, width);
+ height = max<int> (0, height);
+
+ int bytesPerPixel = GetBytesFromTextureFormat( m_Format );
+ return ImageReference( width, height, m_RowBytes, m_Format, m_Image + bytesPerPixel * x + m_RowBytes * y );
+ }
+ else
+ return ImageReference( 0, 0, 0, m_Format, NULL );
+}
+
+bool ImageReference::NeedsReformat(int width, int height, TextureFormat format) const
+{
+ // TODO : it actually doesn't need to reallocate when downsizing!
+ return width != m_Width || height != m_Height || format != m_Format;
+}
+
+Image::Image (int width, int height, TextureFormat format)
+{
+ m_Height = height;
+ m_Width = width;
+ m_Format = format;
+ int bpp = GetBytesFromTextureFormat( m_Format );
+ m_RowBytes = m_Width * bpp;
+ if (CheckImageFormatValid (width, height, format))
+ m_Image = (UInt8*)UNITY_MALLOC_ALIGNED(kMemNewDelete, m_RowBytes * m_Height + GetMaxBytesPerPixel( m_Format ), kImageDataAlignment); // allocate one pixel more for bilinear blits
+ else
+ m_Image = NULL;
+}
+
+Image::Image (int width, int height, int rowbytes, TextureFormat format, void* image)
+{
+ m_Height = height;
+ m_Width = width;
+ m_Format = format;
+ int bpp = GetBytesFromTextureFormat( m_Format );
+ m_RowBytes = m_Width * bpp;
+ if( CheckImageFormatValid (width, height, format) )
+ m_Image = (UInt8*)UNITY_MALLOC_ALIGNED(kMemNewDelete, m_RowBytes * m_Height + GetMaxBytesPerPixel( m_Format ), kImageDataAlignment); // allocate one pixel more for bilinear blits
+ else
+ m_Image = NULL;
+
+ if (image && m_Image)
+ BlitImage (ImageReference (width, height, rowbytes, format, image), BLIT_COPY);
+}
+
+void Image::SetImage(SInt32 width, SInt32 height, UInt32 format, bool shrinkAllowed)
+{
+ // TODO : this size is wrong if it has been shrunk already, but we don't care at the moment,
+ // because we use it for calculation of mipmaps only
+ const int oldSize = m_RowBytes * m_Height + GetBytesFromTextureFormat( m_Format );
+
+ m_Width = width;
+ m_Height = height;
+ m_Format = format;
+ const int bpp = GetBytesFromTextureFormat( m_Format );
+ m_RowBytes = m_Width * bpp;
+
+ const int newSize = m_RowBytes * m_Height + bpp;
+
+ if ((!shrinkAllowed && (oldSize < newSize)) ||
+ (shrinkAllowed && (oldSize != newSize)))
+ {
+ UNITY_FREE(kMemNewDelete, m_Image);
+ m_Image = NULL;
+ if( m_Format != 0 && CheckImageFormatValid (m_Width, m_Height, m_Format) )
+ m_Image = (UInt8*)UNITY_MALLOC_ALIGNED(kMemNewDelete, m_RowBytes * m_Height + GetMaxBytesPerPixel( m_Format ), kImageDataAlignment); // allocate one pixel more for bilinear blits
+ }
+}
+
+void Image::SetImage(const ImageReference& src, bool shrinkAllowed)
+{
+ if (this == &src)
+ return;
+
+ SetImage (src.GetWidth(), src.GetHeight(), src.GetFormat(), shrinkAllowed);
+ BlitImage (src, BLIT_COPY);
+}
+
+void Image::ReformatImage (const ImageReference& image, int width, int height, TextureFormat format, BlitMode mode)
+{
+ AssertIf(!image.NeedsReformat(width, height, format));
+
+ int bpp = GetBytesFromTextureFormat(format);
+ int newRowBytes = width * bpp;
+ UInt8* newImageData = NULL;
+ if (CheckImageFormatValid (width, height, format))
+ newImageData = (UInt8*)UNITY_MALLOC_ALIGNED(kMemNewDelete, height * newRowBytes + GetMaxBytesPerPixel( m_Format ), kImageDataAlignment); // allocate one pixel more for bilinear blits
+
+ ImageReference newImage (width, height, newRowBytes, format, newImageData);
+ newImage.BlitImage (image, mode);
+ UNITY_FREE(kMemNewDelete, m_Image);
+
+ m_Height = height;
+ m_Width = width;
+ m_Format = format;
+ m_RowBytes = newRowBytes;
+ m_Image = newImageData;
+}
+
+void Image::ReformatImage (int width, int height, TextureFormat format, BlitMode mode)
+{
+ if (!NeedsReformat(width, height, format))
+ return;
+
+ ReformatImage (*this, width, height, format, mode);
+}
+
+bool ImageReference::IsValidImage () const
+{
+ return m_Image != NULL && CheckImageFormatValid (GetWidth(), GetHeight(), GetFormat());
+}
+
+bool CheckImageFormatValid (int width, int height, TextureFormat format)
+{
+ if (width > 0 && height > 0 && format > 0 && (format <= kTexFormatBGR24 || format == kTexFormatBGRA32 || format == kTexFormatRGBA4444))
+ return true;
+ else
+ {
+ if (width < 0) {
+ AssertString ("Image invalid width!");
+ }
+ if (height < 0) {
+ AssertString ("Image invalid height!");
+ }
+ if (format > kTexFormatBGR24 && format != kTexFormatRGBA4444 && format != kTexFormatBGRA32) {
+ AssertString ("Image invalid format!");
+ }
+ return false;
+ }
+}
+
+prcore::PixelFormat GetProphecyPixelFormat (TextureFormat format)
+{
+ switch (format)
+ {
+ case kTexFormatAlpha8:
+ return prcore::PixelFormat (8,0,0xff);
+ case kTexFormatARGB4444:
+ return prcore::PixelFormat (16,0x00000f00,0x000000f0,0x0000000f,0x0000f000);
+ case kTexFormatRGBA4444:
+ return prcore::PixelFormat (16,0x0000f000,0x00000f00,0x000000f0,0x0000000f);
+ case kTexFormatRGB24:
+ #if UNITY_BIG_ENDIAN
+ return prcore::PixelFormat (24,0x00ff0000,0x0000ff00,0x000000ff,0x00000000);
+ #else
+ return prcore::PixelFormat (24,0x000000ff,0x0000ff00,0x00ff0000,0x00000000);
+ #endif
+ case kTexFormatRGBA32:
+ #if UNITY_BIG_ENDIAN
+ return prcore::PixelFormat (32,0xff000000,0x00ff0000,0x0000ff00,0x000000ff);
+ #else
+ return prcore::PixelFormat (32,0x000000ff,0x0000ff00,0x00ff0000,0xff000000);
+ #endif
+ case kTexFormatBGRA32:
+ #if UNITY_BIG_ENDIAN
+ return prcore::PixelFormat (32,0x0000ff00,0x00ff0000,0xff000000,0x000000ff);
+ #else
+ return prcore::PixelFormat (32,0x00ff0000,0x0000ff00,0x000000ff,0xff000000);
+ #endif
+ case kTexFormatARGB32:
+ #if UNITY_BIG_ENDIAN
+ return prcore::PixelFormat (32,0x00ff0000,0x0000ff00,0x000000ff,0xff000000);
+ #else
+ return prcore::PixelFormat (32,0x0000ff00,0x00ff0000,0xff000000,0x000000ff);
+ #endif
+ case kTexFormatARGBFloat:
+ return prcore::PixelFormat (128, 2, 4, 8, 1, prcore::PIXELFORMAT_FLOAT32);
+ case kTexFormatRGB565:
+ return prcore::PixelFormat (16,0x0000f800,0x000007e0,0x0000001f,0x00000000);
+ case kTexFormatBGR24:
+#if UNITY_BIG_ENDIAN
+ return prcore::PixelFormat (24,0x00ff0000,0x0000ff00,0x000000ff,0x00000000);
+#else
+ return prcore::PixelFormat (24,0x000000ff,0x0000ff00,0x00ff0000,0x00000000);
+#endif
+ default:
+ DebugAssertIf( true );
+ return prcore::PixelFormat ();
+ }
+}
+
+bool operator == (const ImageReference& lhs, const ImageReference& rhs)
+{
+ if (lhs.GetWidth () != rhs.GetWidth ()) return false;
+ if (lhs.GetHeight () != rhs.GetHeight ()) return false;
+ if (lhs.GetRowBytes () != rhs.GetRowBytes ()) return false;
+ if (lhs.GetFormat () != rhs.GetFormat ()) return false;
+ UInt8* lhsData = lhs.GetImageData ();
+ UInt8* rhsData = rhs.GetImageData ();
+ if (lhsData == NULL || rhsData == NULL)
+ return lhsData == rhsData;
+
+ int size = lhs.GetRowBytes () * lhs.GetHeight ();
+ for (int i=0;i<size / 4;i++)
+ {
+ if (*reinterpret_cast<UInt32*> (lhsData) != *reinterpret_cast<UInt32*> (rhsData))
+ return false;
+ lhsData += 4; rhsData += 4;
+ }
+ for (int i=0;i<size % 4;i++)
+ {
+ if (*lhsData != *rhsData)
+ return false;
+ lhsData++; rhsData++;
+ }
+ return true;
+}
+
+void SwizzleARGB32ToBGRA32 (UInt8* bytes, int imageSize)
+{
+ for (int i=0;i<imageSize;i+=4)
+ {
+ UInt8 swizzle[4];
+ UInt8* dst = bytes + i;
+ memcpy(swizzle, dst, sizeof(swizzle));
+
+ dst[0] = swizzle[3];
+ dst[1] = swizzle[2];
+ dst[2] = swizzle[1];
+ dst[3] = swizzle[0];
+ }
+}
+
+void SwizzleRGBA32ToBGRA32 (UInt8* bytes, int imageSize)
+{
+ for (int i=0;i<imageSize;i+=4)
+ {
+ UInt8 swizzle[4];
+ UInt8* dst = bytes + i;
+ memcpy(swizzle, dst, sizeof(swizzle));
+
+ dst[0] = swizzle[2];
+ dst[1] = swizzle[1];
+ dst[2] = swizzle[0];
+ dst[3] = swizzle[3];
+ }
+}
+
+void SwizzleBGRAToRGBA32 (UInt8* bytes, int imageSize)
+{
+ for (int i=0;i<imageSize;i+=4)
+ {
+ UInt8 swizzle[4];
+ UInt8* dst = bytes + i;
+ memcpy(swizzle, dst, sizeof(swizzle));
+
+ dst[0] = swizzle[2];
+ dst[1] = swizzle[1];
+ dst[2] = swizzle[0];
+ dst[3] = swizzle[3];
+ }
+}
+
+void SwizzleRGB24ToBGR24 (UInt8* bytes, int imageSize)
+{
+ for (int i=0;i<imageSize;i+=3)
+ {
+ UInt8 swizzle[3];
+ UInt8* dst = bytes + i;
+ memcpy(swizzle, dst, sizeof(swizzle));
+
+ dst[0] = swizzle[2];
+ dst[1] = swizzle[1];
+ dst[2] = swizzle[0];
+ }
+}
+
+void Premultiply( ImageReference& image )
+{
+ if (image.GetFormat() == kTexFormatRGBA32)
+ {
+ UInt8* data = image.GetImageData();
+ int size = image.GetRowBytes() * image.GetHeight() / 4;
+ for (int i=0;i<size;i++)
+ {
+ UInt8* rgba = data + 4 * i;
+ rgba[0] = ((int)rgba[0] * (int)rgba[3]) / 255;
+ rgba[1] = ((int)rgba[1] * (int)rgba[3]) / 255;
+ rgba[2] = ((int)rgba[2] * (int)rgba[3]) / 255;
+ }
+ }
+ else if (image.GetFormat() == kTexFormatARGB32)
+ {
+ UInt8* data = image.GetImageData();
+ int size = image.GetRowBytes() * image.GetHeight() / 4;
+ for (int i=0;i<size;i++)
+ {
+ UInt8* rgba = data + 4 * i;
+ rgba[1] = ((int)rgba[1] * (int)rgba[0]) / 255;
+ rgba[2] = ((int)rgba[2] * (int)rgba[0]) / 255;
+ rgba[3] = ((int)rgba[3] * (int)rgba[0]) / 255;
+ }
+ }
+ else
+ {
+ ErrorString("Unsupported");
+ }
+}
+
+inline UInt32 DecodeRGBMChannel(UInt32 val, UInt32 mult, UInt32 mask, UInt8 offset)
+{
+ UInt32 channel = (val & mask) >> offset;
+ channel *= mult;
+ channel /= 255 * 2;
+ channel = channel > 255 ? 255 : channel;
+ channel <<= offset;
+
+ return channel;
+}
+
+// Decodes RGBM encoded lightmaps into doubleLDR in place.
+// Handles kTexFormatRGBA32 and kTexFormatARGB32 data.
+void DecodeRGBM(int width, int height, UInt8* data, int pitch, const prcore::PixelFormat& pf)
+{
+ UInt8* rowData = data;
+ uint32 rmask = pf.GetRedMask();
+ uint32 gmask = pf.GetGreenMask();
+ uint32 bmask = pf.GetBlueMask();
+ uint32 amask = pf.GetAlphaMask();
+ uint8 roffset = (int)pf.GetRedOffset() - (int)pf.GetRedBits() + 1;
+ uint8 goffset = (int)pf.GetGreenOffset() - (int)pf.GetGreenBits() + 1;
+ uint8 boffset = (int)pf.GetBlueOffset() - (int)pf.GetBlueBits() + 1;
+ uint8 aoffset = (int)pf.GetAlphaOffset() - (int)pf.GetAlphaBits() + 1;
+
+ for( int r = 0; r < height; ++r )
+ {
+ uint32* pixel = (uint32*)rowData;
+ for( int c = 0; c < width; ++c )
+ {
+ uint32 val = pixel[c];
+ uint32 alpha = ((val & amask) >> aoffset) * kRGBMMaxRange;
+ pixel[c] = DecodeRGBMChannel(val, alpha, rmask, roffset) +
+ DecodeRGBMChannel(val, alpha, gmask, goffset) +
+ DecodeRGBMChannel(val, alpha, bmask, boffset) +
+ (255 << aoffset);
+ }
+ rowData += pitch;
+ }
+}
+
+
+void SetAlphaChannel(int width, int height, UInt8* data, int pitch, const prcore::PixelFormat& pf, UInt8 alpha)
+{
+ UInt8* rowData = data;
+ uint32 amask = pf.GetAlphaMask();
+ uint8 aoffset = (int)pf.GetAlphaOffset() - (int)pf.GetAlphaBits() + 1;
+ uint32 properAlpha = (alpha << aoffset);
+
+ for( int r = 0; r < height; ++r )
+ {
+ uint32* pixel = (uint32*)rowData;
+ for( int c = 0; c < width; ++c )
+ {
+ uint32 val = pixel[c];
+
+ // merge bits from two values according to a mask,
+ // equivalent to: (val & ~amask) | (properAlpha & amask)
+ pixel[c] = val ^ ((val ^ properAlpha) & amask);
+ }
+ rowData += pitch;
+ }
+}
+
+void SetAlphaToRedChannel (int width, int height, UInt8* data, int pitch, const prcore::PixelFormat& pf)
+{
+ UInt8* rowData = data;
+ uint32 rmask = pf.GetRedMask();
+ uint32 amask = pf.GetAlphaMask();
+ uint8 roffset = (int)pf.GetRedOffset() - (int)pf.GetRedBits() + 1;
+ uint8 aoffset = (int)pf.GetAlphaOffset() - (int)pf.GetAlphaBits() + 1;
+
+ for( int r = 0; r < height; ++r )
+ {
+ uint32* pixel = (uint32*)rowData;
+ for( int c = 0; c < width; ++c )
+ {
+ uint32 val = pixel[c];
+ uint32 red = (val & rmask) >> roffset;
+ pixel[c] = (val & ~amask) | (red << aoffset);
+ }
+ rowData += pitch;
+ }
+}
+
+inline UInt8 XenonToNormalSRGBTexture (UInt32 value)
+{
+ value = UInt32(LinearToGammaSpace(GammaToLinearSpaceXenon (value / 255.0F)) * 255.0F);
+ return std::min<UInt32>(value, 255);
+}
+
+void XenonToNormalSRGBTexture (int width, int height, UInt8* data, int pitch, const prcore::PixelFormat& pf)
+{
+ UInt8* rowData = data;
+ uint32 rmask = pf.GetRedMask();
+ uint32 gmask = pf.GetGreenMask();
+ uint32 bmask = pf.GetBlueMask();
+ uint32 amask = pf.GetAlphaMask();
+ uint8 roffset = (int)pf.GetRedOffset() - (int)pf.GetRedBits() + 1;
+ uint8 goffset = (int)pf.GetGreenOffset() - (int)pf.GetGreenBits() + 1;
+ uint8 boffset = (int)pf.GetBlueOffset() - (int)pf.GetBlueBits() + 1;
+ uint8 aoffset = (int)pf.GetAlphaOffset() - (int)pf.GetAlphaBits() + 1;
+
+ for( int r = 0; r < height; ++r )
+ {
+ uint32* pixel = (uint32*)rowData;
+ for( int c = 0; c < width; ++c )
+ {
+ uint32 val = pixel[c];
+ uint32 r = (val & rmask) >> roffset;
+ uint32 g = (val & gmask) >> goffset;
+ uint32 b = (val & bmask) >> boffset;
+ uint32 a = (val & amask) >> aoffset;
+
+ r = XenonToNormalSRGBTexture (r);
+ g = XenonToNormalSRGBTexture (g);
+ b = XenonToNormalSRGBTexture (b);
+
+ pixel[c] = (r << roffset) | (g << goffset) | (b << boffset) | (a << aoffset);
+ }
+ rowData += pitch;
+ }
+}
+
+
+
+
+// --------------------------------------------------------------------------
+
+
+const char* kUnsupportedGetPixelOpFormatMessage = "Unsupported texture format - needs to be ARGB32, RGBA32, BGRA32, RGB24, Alpha8 or DXT";
+const char* kUnsupportedSetPixelOpFormatMessage = "Unsupported texture format - needs to be ARGB32, RGBA32, RGB24 or Alpha8";
+
+inline int RepeatInt (int x, int width)
+{
+ if (width == 0) return 0;
+ if( x < 0 ) {
+ int times = -(x/width)+1;
+ x += width * times;
+ }
+ x = x % width;
+ return x;
+}
+
+inline int TextureWrap(int x, int max, TextureWrapMode wrapMode)
+{
+ if (wrapMode == kTexWrapRepeat)
+ {
+ return RepeatInt(x, max);
+ }
+ else
+ {
+ if (max <= 0)
+ return 0;
+ return clamp(x, 0, max-1);
+ }
+}
+
+void SetImagePixel (ImageReference& image, int x, int y, TextureWrapMode wrap, const ColorRGBAf& color)
+{
+ int width = image.GetWidth();
+ int height = image.GetHeight();
+ if (x < 0 || x >= width || y < 0 || y >= height)
+ {
+ x = TextureWrap(x, width, wrap);
+ y = TextureWrap(y, height, wrap);
+ }
+
+ if (image.GetFormat() == kTexFormatARGB32)
+ {
+ UInt8* pixel = image.GetRowPtr(y) + x * 4;
+ pixel[1] = RoundfToIntPos(clamp01(color.r) * 255.0);
+ pixel[2] = RoundfToIntPos(clamp01(color.g) * 255.0);
+ pixel[3] = RoundfToIntPos(clamp01(color.b) * 255.0);
+ pixel[0] = RoundfToIntPos(clamp01(color.a) * 255.0);
+ }
+ else if (image.GetFormat() == kTexFormatRGBA32)
+ {
+ UInt8* pixel = image.GetRowPtr(y) + x * 4;
+ pixel[0] = RoundfToIntPos(clamp01(color.r) * 255.0);
+ pixel[1] = RoundfToIntPos(clamp01(color.g) * 255.0);
+ pixel[2] = RoundfToIntPos(clamp01(color.b) * 255.0);
+ pixel[3] = RoundfToIntPos(clamp01(color.a) * 255.0);
+ }
+ else if (image.GetFormat() == kTexFormatRGB24)
+ {
+ UInt8* pixel = image.GetRowPtr(y) + x * 3;
+ pixel[0] = RoundfToIntPos(clamp01(color.r) * 255.0);
+ pixel[1] = RoundfToIntPos(clamp01(color.g) * 255.0);
+ pixel[2] = RoundfToIntPos(clamp01(color.b) * 255.0);
+ }
+ else if (image.GetFormat() == kTexFormatRGB565)
+ {
+ UInt16* pixel = (UInt16 *)(image.GetRowPtr(y) + x * 2);
+ UInt16 r = (UInt16)(RoundfToIntPos( clamp01(color.r) * 31.0f));
+ UInt16 g = (UInt16)(RoundfToIntPos( clamp01(color.g) * 63.0f));
+ UInt16 b = (UInt16)(RoundfToIntPos( clamp01(color.b) * 31.0f));
+ pixel[0] = r<<11 | g<<5 | b;
+ }
+ else if (image.GetFormat() == kTexFormatAlpha8)
+ {
+ UInt8* pixel = image.GetRowPtr(y) + x;
+ pixel[0] = RoundfToIntPos(clamp01(color.a) * 255.0);
+ }
+ else
+ {
+ ErrorString(kUnsupportedSetPixelOpFormatMessage);
+ }
+}
+
+
+ColorRGBA32 GetImagePixel (UInt8* data, int width, int height, TextureFormat format, TextureWrapMode wrap, int x, int y)
+{
+ if (x < 0 || x >= width || y < 0 || y >= height)
+ {
+ x = TextureWrap (x, width, wrap);
+ y = TextureWrap (y, height, wrap);
+ }
+
+ if( IsCompressedDXTTextureFormat(format) )
+ {
+ int texWidth = std::max (width, 4);
+
+ UInt32 uncompressed [16];
+ int blockBytes = (format == kTexFormatDXT1 ? 8 : 16);
+
+ const UInt8 *pos = data + ((x/4) + (y/4)*(texWidth/4)) * blockBytes;
+ DecompressNativeTextureFormat (format, 4, 4, (const UInt32*)pos, 4, 4, uncompressed);
+
+ const UInt8* pixel = (const UInt8*)(uncompressed + x%4 + 4* (y%4));
+ return ColorRGBA32(pixel[0], pixel[1], pixel[2], pixel[3]);
+ }
+ else if( IsAnyCompressedTextureFormat (format) )
+ {
+ ErrorString(kUnsupportedGetPixelOpFormatMessage);
+ }
+ else
+ {
+ ImageReference image (width, height, GetRowBytesFromWidthAndFormat(width, format), format, data);
+ const UInt8* pixel;
+ if (format == kTexFormatARGB32)
+ {
+ pixel = image.GetRowPtr(y) + x * 4;
+ return ColorRGBA32 (pixel[1], pixel[2], pixel[3], pixel[0]);
+ }
+ else if (format == kTexFormatRGBA32)
+ {
+ pixel = image.GetRowPtr(y) + x * 4;
+ return ColorRGBA32 (pixel[0], pixel[1], pixel[2], pixel[3]);
+ }
+ else if (format == kTexFormatBGRA32)
+ {
+ pixel = image.GetRowPtr(y) + x * 4;
+ return ColorRGBA32 (pixel[2], pixel[1], pixel[0], pixel[3]);
+ }
+ else if (format == kTexFormatRGB24)
+ {
+ pixel = image.GetRowPtr(y) + x * 3;
+ return ColorRGBA32 (pixel[0], pixel[1], pixel[2], 255);
+ }
+ else if (format == kTexFormatAlpha8)
+ {
+ pixel = image.GetRowPtr(y) + x;
+ return ColorRGBA32 (255, 255, 255, pixel[0]);
+ }
+ else if (format == kTexFormatRGBA4444)
+ {
+ pixel = image.GetRowPtr(y) + x * 2;
+ return ColorRGBA32(
+ (pixel[1] & 0xF0) << 4 | (pixel[1] & 0xF0),
+ (pixel[1] & 0x0F) << 4 | (pixel[1] & 0x0F),
+ (pixel[0] & 0xF0) << 4 | (pixel[0] & 0xF0),
+ (pixel[0] & 0x0F) << 4 | (pixel[0] & 0x0F)
+ );
+ }
+ else if (format == kTexFormatARGB4444)
+ {
+ pixel = image.GetRowPtr(y) + x * 2;
+ return ColorRGBA32(
+ (pixel[1] & 0x0F) << 4 | (pixel[1] & 0x0F),
+ (pixel[0] & 0xF0) << 4 | (pixel[0] & 0xF0),
+ (pixel[0] & 0x0F) << 4 | (pixel[0] & 0x0F),
+ (pixel[1] & 0xF0) << 4 | (pixel[1] & 0xF0)
+ );
+ }
+ else if (format == kTexFormatRGB565)
+ {
+ pixel = image.GetRowPtr(y) + x * 2;
+ UInt16 c = *reinterpret_cast<const UInt16*>(pixel);
+ UInt8 r = (UInt8)((c >> 11)&0x1F);
+ UInt8 g = (UInt8)((c >> 5)&0x3F);
+ UInt8 b = (UInt8)((c >> 0)&0x1F);
+ return ColorRGBA32 (r<<3|r>>2, g<<2|g>>4, b<<3|b>>2, 255);
+ }
+ else
+ {
+ ErrorString(kUnsupportedGetPixelOpFormatMessage);
+ }
+ }
+ return ColorRGBA32(255,255,255,255);
+}
+
+ColorRGBAf GetImagePixelBilinear (UInt8* data, int width, int height, TextureFormat format, TextureWrapMode wrap, float u, float v)
+{
+ u *= width;
+ v *= height;
+
+ int xBase = FloorfToInt(u);
+ int yBase = FloorfToInt(v);
+
+ float s = u - xBase;
+ float t = v - yBase;
+
+ ColorRGBAf colors[4];
+
+ if (IsCompressedDXTTextureFormat(format))
+ {
+ if (xBase < 0 || xBase+1 >= width || yBase < 0 || yBase+1 >= height)
+ {
+ for (int i=0;i<4;i++)
+ {
+ int x = xBase;
+ int y = yBase;
+ if (i & 1)
+ x++;
+ if (i & 2)
+ y++;
+
+ x = TextureWrap(x, width, wrap);
+ y = TextureWrap(y, height, wrap);
+
+ colors[i] = GetImagePixel (data, width, height, format, wrap, x, y);
+ }
+ }
+ else
+ {
+ GetImagePixelBlock (data, width, height, format, xBase, yBase, 2, 2, colors);
+ }
+ }
+ else if( IsAnyCompressedTextureFormat(format) )
+ {
+ ErrorString(kUnsupportedGetPixelOpFormatMessage);
+ return ColorRGBAf(1.0F,1.0F,1.0F,1.0F);
+ }
+ else
+ {
+ ImageReference image (width, height, GetRowBytesFromWidthAndFormat(width, format), format, data);
+ for (int i=0;i<4;i++)
+ {
+ int x = xBase;
+ int y = yBase;
+ if (i & 1)
+ x++;
+ if (i & 2)
+ y++;
+
+ if (x < 0 || x >= width || y < 0 || y >= height)
+ {
+ x = TextureWrap(x, width, wrap);
+ y = TextureWrap(y, height, wrap);
+ }
+
+ UInt8* pixel;
+ if (format == kTexFormatARGB32)
+ {
+ pixel = image.GetRowPtr(y) + x * 4;
+ colors[i] = ColorRGBA32 (pixel[1], pixel[2], pixel[3], pixel[0]);
+ }
+ else if (format == kTexFormatRGBA32)
+ {
+ pixel = image.GetRowPtr(y) + x * 4;
+ colors[i] = ColorRGBA32 (pixel[0], pixel[1], pixel[2], pixel[3]);
+ }
+ else if (format == kTexFormatBGRA32)
+ {
+ pixel = image.GetRowPtr(y) + x * 4;
+ colors[i] = ColorRGBA32 (pixel[2], pixel[1], pixel[0], pixel[3]);
+ }
+ else if (format == kTexFormatRGB24)
+ {
+ pixel = image.GetRowPtr(y) + x * 3;
+ colors[i] = ColorRGBA32 (pixel[0], pixel[1], pixel[2], 255);
+ }
+ else if (format == kTexFormatAlpha8)
+ {
+ pixel = image.GetRowPtr(y) + x;
+ colors[i] = ColorRGBA32 (255, 255, 255, pixel[0]);
+ }
+ else
+ {
+ ErrorString(kUnsupportedGetPixelOpFormatMessage);
+ return ColorRGBAf(1.0F,1.0F,1.0F,1.0F);
+ }
+ }
+ }
+ ColorRGBAf a = Lerp(colors[0], colors[1], s);
+ ColorRGBAf b = Lerp(colors[2], colors[3], s);
+ return Lerp(a, b, t);
+}
+
+bool GetImagePixelBlock (UInt8* data, int dataWidth, int dataHeight, TextureFormat format, int x, int y, int blockWidth, int blockHeight, ColorRGBAf* outColors)
+{
+ // Checks
+ if (blockWidth <= 0 || blockHeight <= 0)
+ {
+ ErrorString ("Width and height must be positive");
+ return false;
+ }
+
+ // TODO: repeat/crop support
+ if (x < 0 || y < 0 || x + blockWidth < 0 || y + blockHeight < 0 || x + blockWidth > dataWidth || y + blockHeight > dataHeight)
+ {
+ char mad[255];
+
+ if (x < 0)
+ sprintf(mad, "Texture rectangle is out of bounds (%d < 0)", x);
+ if (y < 0)
+ sprintf(mad, "Texture rectangle is out of bounds (%d < 0)", y);
+ if (x + blockWidth > dataWidth)
+ sprintf(mad, "Texture rectangle is out of bounds (%d + %d > %d)", x, blockWidth, dataWidth);
+ if (y + blockHeight > dataHeight)
+ sprintf(mad, "Texture rectangle is out of bounds (%d + %d > %d)", y, blockHeight, dataHeight);
+
+ ErrorString(mad);
+ return false;
+ }
+
+ if (IsCompressedDXTTextureFormat(format))
+ {
+ int texWidth = std::max (dataWidth, 4);
+
+ int paddedWidth = x+blockWidth-(x&~3);
+ if(paddedWidth % 4)
+ paddedWidth = (paddedWidth&~3) + 4;
+ int paddedHeight = y+blockHeight-(y&~3);
+ if(paddedHeight % 4)
+ paddedHeight = (paddedHeight&~3) + 4;
+
+ UInt32* uncompressed;
+ ALLOC_TEMP(uncompressed, UInt32, paddedWidth * paddedHeight);
+
+ int blockBytes = (format == kTexFormatDXT1 ? 8 : 16);
+
+ for(int line = 0; line< paddedHeight; line+=4)
+ {
+ UInt8 *pos = data + ((x/4) + ((y+line)/4)*(texWidth/4)) * blockBytes;
+ DecompressNativeTextureFormat(format, paddedWidth, 4, (UInt32*)pos, paddedWidth, 4, uncompressed+(line*paddedWidth));
+ }
+
+ ColorRGBAf* dest = outColors;
+ const UInt8* pixelRow = (UInt8*)(uncompressed + x%4 + (y%4)*paddedWidth);
+ for( int iy = 0; iy < blockHeight; ++iy )
+ {
+ const UInt8* pixel = pixelRow;
+ for( int ix = 0; ix < blockWidth; ++ix )
+ {
+ *dest = ColorRGBA32(pixel[0], pixel[1], pixel[2], pixel[3]);
+ pixel += 4;
+ ++dest;
+ }
+ pixelRow += paddedWidth * 4;
+ }
+ }
+ else
+ {
+ ImageReference image (dataWidth, dataHeight, GetRowBytesFromWidthAndFormat(dataWidth, format), format, data);
+
+ ColorRGBAf* dest = outColors;
+
+ if (format == kTexFormatARGB32)
+ {
+ const UInt8* pixelRow = image.GetRowPtr(y) + x * 4;
+ for( int iy = 0; iy < blockHeight; ++iy )
+ {
+ const UInt8* pixel = pixelRow;
+ for( int ix = 0; ix < blockWidth; ++ix )
+ {
+ *dest = ColorRGBA32(pixel[1], pixel[2], pixel[3], pixel[0]);
+ pixel += 4;
+ ++dest;
+ }
+ pixelRow += image.GetRowBytes();
+ }
+ }
+ else if (format == kTexFormatRGBA32)
+ {
+ const UInt8* pixelRow = image.GetRowPtr(y) + x * 4;
+ for( int iy = 0; iy < blockHeight; ++iy )
+ {
+ const UInt8* pixel = pixelRow;
+ for( int ix = 0; ix < blockWidth; ++ix )
+ {
+ *dest = ColorRGBA32(pixel[0], pixel[1], pixel[2], pixel[3]);
+ pixel += 4;
+ ++dest;
+ }
+ pixelRow += image.GetRowBytes();
+ }
+ }
+ else if (format == kTexFormatBGRA32)
+ {
+ const UInt8* pixelRow = image.GetRowPtr(y) + x * 4;
+ for( int iy = 0; iy < blockHeight; ++iy )
+ {
+ const UInt8* pixel = pixelRow;
+ for( int ix = 0; ix < blockWidth; ++ix )
+ {
+ *dest = ColorRGBA32(pixel[2], pixel[1], pixel[0], pixel[3]);
+ pixel += 4;
+ ++dest;
+ }
+ pixelRow += image.GetRowBytes();
+ }
+ }
+ else if (format == kTexFormatRGB24)
+ {
+ const UInt8* pixelRow = image.GetRowPtr(y) + x * 3;
+ for( int iy = 0; iy < blockHeight; ++iy )
+ {
+ const UInt8* pixel = pixelRow;
+ for( int ix = 0; ix < blockWidth; ++ix )
+ {
+ *dest = ColorRGBA32(pixel[0], pixel[1], pixel[2], 255);
+ pixel += 3;
+ ++dest;
+ }
+ pixelRow += image.GetRowBytes();
+ }
+ }
+ else if (format == kTexFormatAlpha8)
+ {
+ const UInt8* pixelRow = image.GetRowPtr(y) + x;
+ for( int iy = 0; iy < blockHeight; ++iy )
+ {
+ const UInt8* pixel = pixelRow;
+ for( int ix = 0; ix < blockWidth; ++ix )
+ {
+ *dest = ColorRGBA32 (255, 255, 255, pixel[0]);
+ pixel += 1;
+ ++dest;
+ }
+ pixelRow += image.GetRowBytes();
+ }
+ }
+ else if (format == kTexFormatRGB565)
+ {
+ const UInt8* pixelRow = image.GetRowPtr(y) + x;
+ for( int iy = 0; iy < blockHeight; ++iy )
+ {
+ const UInt8* pixel = pixelRow;
+ for( int ix = 0; ix < blockWidth; ++ix )
+ {
+ UInt16 c = (UInt16)pixel[0] | ((UInt16)pixel[1])<<8;
+ UInt8 r = (UInt8)((c >> 11)&31);
+ UInt8 g = (UInt8)((c >> 5)&63);
+ UInt8 b = (UInt8)((c >> 0)&31);
+
+ *dest = ColorRGBA32 (r<<3|r>>2, g<<2|g>>4, b<<3|b>>2, 255);
+ pixel += 2;
+ ++dest;
+ }
+ pixelRow += image.GetRowBytes();
+ }
+ }
+ else
+ {
+ ErrorString(kUnsupportedGetPixelOpFormatMessage);
+ return false;
+ }
+ }
+ return true;
+}
+
+
+bool SetImagePixelBlock (UInt8* data, int dataWidth, int dataHeight, TextureFormat format, int x, int y, int blockWidth, int blockHeight, int pixelCount, const ColorRGBAf* pixels)
+{
+ if (IsAnyCompressedTextureFormat(format))
+ {
+ ErrorString(kUnsupportedSetPixelOpFormatMessage);
+ return false;
+ }
+ if (blockWidth <= 0 || blockHeight <= 0)
+ {
+ ErrorString ("Width and height must be positive");
+ return false;
+ }
+
+ int tmp = blockWidth * blockHeight;
+ if (blockHeight != tmp / blockWidth || blockWidth * blockHeight > pixelCount ) // check for overflow as well
+ {
+ ErrorString ("Array size must be at least width*height");
+ return false;
+ }
+
+ if (x < 0 || y < 0 || x + blockWidth < 0 || y + blockHeight < 0 || x + blockWidth > dataWidth || y + blockHeight > dataHeight)
+ {
+ ErrorString ("Texture rectangle is out of bounds");
+ return false;
+ }
+
+ ImageReference image (dataWidth, dataHeight, GetRowBytesFromWidthAndFormat(dataWidth, format), format, data);
+
+ if (format == kTexFormatARGB32)
+ {
+ UInt8* pixelRow = image.GetRowPtr(y) + x * 4;
+ for( int iy = 0; iy < blockHeight; ++iy )
+ {
+ UInt8* pixel = pixelRow;
+ for( int ix = 0; ix < blockWidth; ++ix )
+ {
+ pixel[1] = NormalizedToByte( pixels->r);
+ pixel[2] = NormalizedToByte( pixels->g);
+ pixel[3] = NormalizedToByte( pixels->b);
+ pixel[0] = NormalizedToByte( pixels->a);
+ pixel += 4;
+ ++pixels;
+ }
+ pixelRow += image.GetRowBytes();
+ }
+ }
+ else if (format == kTexFormatRGBA32)
+ {
+ UInt8* pixelRow = image.GetRowPtr(y) + x * 4;
+ for( int iy = 0; iy < blockHeight; ++iy )
+ {
+ UInt8* pixel = pixelRow;
+ for( int ix = 0; ix < blockWidth; ++ix )
+ {
+ pixel[0] = NormalizedToByte( pixels->r);
+ pixel[1] = NormalizedToByte( pixels->g);
+ pixel[2] = NormalizedToByte( pixels->b);
+ pixel[3] = NormalizedToByte( pixels->a);
+ pixel += 4;
+ ++pixels;
+ }
+ pixelRow += image.GetRowBytes();
+ }
+ }
+ else if (format == kTexFormatRGB24)
+ {
+ UInt8* pixelRow = image.GetRowPtr(y) + x * 3;
+ for( int iy = 0; iy < blockHeight; ++iy )
+ {
+ UInt8* pixel = pixelRow;
+ for( int ix = 0; ix < blockWidth; ++ix )
+ {
+ pixel[0] = NormalizedToByte(pixels->r);
+ pixel[1] = NormalizedToByte(pixels->g);
+ pixel[2] = NormalizedToByte(pixels->b);
+ pixel += 3;
+ ++pixels;
+ }
+ pixelRow += image.GetRowBytes();
+ }
+ }
+ else if (format == kTexFormatAlpha8)
+ {
+ UInt8* pixelRow = image.GetRowPtr(y) + x;
+ for( int iy = 0; iy < blockHeight; ++iy )
+ {
+ UInt8* pixel = pixelRow;
+ for( int ix = 0; ix < blockWidth; ++ix )
+ {
+ pixel[0] = RoundfToIntPos( clamp01(pixels->a) * 255.0f );
+ pixel += 1;
+ ++pixels;
+ }
+ pixelRow += image.GetRowBytes();
+ }
+ }
+ else if (format == kTexFormatRGB565)
+ {
+ UInt8* pixelRow = image.GetRowPtr(y) + x;
+ for( int iy = 0; iy < blockHeight; ++iy )
+ {
+ UInt16* pixel = (UInt16 *)pixelRow;
+ for( int ix = 0; ix < blockWidth; ++ix )
+ {
+ UInt16 r = (UInt16)(RoundfToIntPos(clamp01(pixels->r) * 31.0f));
+ UInt16 g = (UInt16)(RoundfToIntPos(clamp01(pixels->g) * 63.0f));
+ UInt16 b = (UInt16)(RoundfToIntPos(clamp01(pixels->b) * 31.0f));
+ pixel[0] = r<<11 | g<<5 | b;
+ pixel += 1;
+ ++pixels;
+ }
+ pixelRow += image.GetRowBytes();
+ }
+ }
+ else
+ {
+ ErrorString(kUnsupportedSetPixelOpFormatMessage);
+ return false;
+ }
+
+ return true;
+}
+
+
+
+// --------------------------------------------------------------------------
+
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+
+SUITE (ImageOpsTests)
+{
+
+TEST (RepeatInt)
+{
+ // valdemar: seems like a CW compiler bug, error: (10139) division by 0
+#if !UNITY_WII
+ // handle zero width
+ CHECK_EQUAL (0, RepeatInt (7,0));
+#endif
+ // handle positive args
+ CHECK_EQUAL (7, RepeatInt (7,13));
+ CHECK_EQUAL (0, RepeatInt (13,13));
+ CHECK_EQUAL (1, RepeatInt (170,13));
+ // handle negative args
+ CHECK_EQUAL (12, RepeatInt (-1,13));
+ CHECK_EQUAL (0, RepeatInt (-13,13));
+}
+
+TEST (TextureWrap)
+{
+ // valdemar: seems like a CW compiler bug, error: (10139) division by 0
+#if !UNITY_WII
+ // handle zero width
+ CHECK_EQUAL (0, TextureWrap (7,0,kTexWrapClamp));
+ CHECK_EQUAL (0, TextureWrap (7,0,kTexWrapRepeat));
+#endif
+ // repeat mode
+ CHECK_EQUAL (7, TextureWrap (7,13,kTexWrapRepeat));
+ CHECK_EQUAL (1, TextureWrap (170,13,kTexWrapRepeat));
+ CHECK_EQUAL (12, TextureWrap (-1,13,kTexWrapRepeat));
+ // clamp mode
+ CHECK_EQUAL (7, TextureWrap (7,13,kTexWrapClamp));
+ CHECK_EQUAL (0, TextureWrap (-1,13,kTexWrapClamp));
+ CHECK_EQUAL (12, TextureWrap (13,13,kTexWrapClamp));
+}
+
+TEST (SetGetImagePixelARGB)
+{
+ UInt8 data[4][4];
+ memset (data, 13, sizeof(data));
+ ImageReference image (2, 2, 8, kTexFormatARGB32, data);
+ SetImagePixel (image, 0, 0, kTexWrapRepeat, ColorRGBAf(1.0f,0.5f,0.3f,0.2f)); // sets [0]
+ CHECK (data[0][1]==255 && data[0][2]==128 && data[0][3]==77 && data[0][0]==51);
+ SetImagePixel (image, 3, 8, kTexWrapRepeat, ColorRGBAf(0.1f,0.2f,0.3f,0.4f)); // sets [1] due to repeat
+ CHECK (data[1][1]==26 && data[1][2]==51 && data[1][3]==77 && data[1][0]==102);
+ SetImagePixel (image, -3, 1, kTexWrapClamp, ColorRGBAf(0.3f,0.4f,0.5f,0.6f)); // sets [2] due to clamp
+ CHECK (data[2][1]==77 && data[2][2]==102 && data[2][3]==128 && data[2][0]==153);
+
+ CHECK (data[3][1]==13 && data[3][2]==13 && data[3][3]==13 && data[3][0]==13); // [3] left untouched
+
+ CHECK(ColorRGBA32(ColorRGBAf(1.0f,0.5f,0.3f,0.2f)) == GetImagePixel (&data[0][0], 2, 2, image.GetFormat(), kTexWrapRepeat, 2, 2)); // gets [0] due to repeat
+ CHECK(ColorRGBA32(ColorRGBAf(0.1f,0.2f,0.3f,0.4f)) == GetImagePixel (&data[0][0], 2, 2, image.GetFormat(), kTexWrapRepeat, 5, -2)); // gets [1] due to repeat
+ CHECK(ColorRGBA32(ColorRGBAf(0.3f,0.4f,0.5f,0.6f)) == GetImagePixel (&data[0][0], 2, 2, image.GetFormat(), kTexWrapClamp, -1, 1)); // gets [2] due to clamp
+}
+
+TEST (SetGetImagePixelRGBA)
+{
+ UInt8 data[4][4];
+ memset (data, 13, sizeof(data));
+ ImageReference image (2, 2, 8, kTexFormatRGBA32, data);
+ SetImagePixel (image, 0, 0, kTexWrapRepeat, ColorRGBAf(1.0f,0.5f,0.3f,0.2f)); // sets [0]
+ CHECK (data[0][0]==255 && data[0][1]==128 && data[0][2]==77 && data[0][3]==51);
+ SetImagePixel (image, 3, 8, kTexWrapRepeat, ColorRGBAf(0.1f,0.2f,0.3f,0.4f)); // sets [1] due to repeat
+ CHECK (data[1][0]==26 && data[1][1]==51 && data[1][2]==77 && data[1][3]==102);
+ SetImagePixel (image, -3, 1, kTexWrapClamp, ColorRGBAf(0.3f,0.4f,0.5f,0.6f)); // sets [2] due to clamp
+ CHECK (data[2][0]==77 && data[2][1]==102 && data[2][2]==128 && data[2][3]==153);
+
+ CHECK (data[3][1]==13 && data[3][2]==13 && data[3][3]==13 && data[3][0]==13); // [3] left untouched
+
+ CHECK(ColorRGBA32(ColorRGBAf(1.0f,0.5f,0.3f,0.2f)) == GetImagePixel (&data[0][0], 2, 2, image.GetFormat(), kTexWrapRepeat, 2, 2)); // gets [0] due to repeat
+ CHECK(ColorRGBA32(ColorRGBAf(0.1f,0.2f,0.3f,0.4f)) == GetImagePixel (&data[0][0], 2, 2, image.GetFormat(), kTexWrapRepeat, 5, -2)); // gets [1] due to repeat
+ CHECK(ColorRGBA32(ColorRGBAf(0.3f,0.4f,0.5f,0.6f)) == GetImagePixel (&data[0][0], 2, 2, image.GetFormat(), kTexWrapClamp, -1, 1)); // gets [2] due to clamp
+}
+
+TEST (SetGetImagePixelRGB)
+{
+ UInt8 data[4][3];
+ memset (data, 13, sizeof(data));
+ ImageReference image (2, 2, 6, kTexFormatRGB24, data);
+ SetImagePixel (image, 0, 0, kTexWrapClamp, ColorRGBAf(1.0f,0.5f,0.3f,0.2f)); // sets [0]
+ CHECK (data[0][0]==255 && data[0][1]==128 && data[0][2]==77);
+ SetImagePixel (image, 1, 0, kTexWrapClamp, ColorRGBAf(0.1f,0.2f,0.3f,0.4f)); // sets [1]
+ CHECK (data[1][0]==26 && data[1][1]==51 && data[1][2]==77);
+ SetImagePixel (image, 0, 1, kTexWrapClamp, ColorRGBAf(0.3f,0.4f,0.5f,0.6f)); // sets [2]
+ CHECK (data[2][0]==77 && data[2][1]==102 && data[2][2]==128);
+
+ CHECK (data[3][0]==13 && data[3][1]==13 && data[3][2]==13); // [3] left untouched
+
+ CHECK(ColorRGBA32(ColorRGBAf(1.0f,0.5f,0.3f,1)) == GetImagePixel (&data[0][0], 2, 2, image.GetFormat(), kTexWrapRepeat, 2, 2)); // gets [0] due to repeat
+ CHECK(ColorRGBA32(ColorRGBAf(0.1f,0.2f,0.3f,1)) == GetImagePixel (&data[0][0], 2, 2, image.GetFormat(), kTexWrapRepeat, 5, -2)); // gets [1] due to repeat
+ CHECK(ColorRGBA32(ColorRGBAf(0.3f,0.4f,0.5f,1)) == GetImagePixel (&data[0][0], 2, 2, image.GetFormat(), kTexWrapClamp, -1, 1)); // gets [2] due to clamp
+}
+
+TEST (SetGetImagePixelAlpha)
+{
+ UInt8 data[4];
+ memset (data, 13, sizeof(data));
+ ImageReference image (2, 2, 2, kTexFormatAlpha8, data);
+ SetImagePixel (image, -3, -2, kTexWrapClamp, ColorRGBAf(1.0f,0.5f,0.3f,0.2f)); // sets [0] due to clamp
+ CHECK (data[0]==51);
+ SetImagePixel (image, 1, -4, kTexWrapRepeat, ColorRGBAf(0.1f,0.2f,0.3f,0.4f)); // sets [1] due to repeat
+ CHECK (data[1]==102);
+ SetImagePixel (image, -4, 7, kTexWrapRepeat, ColorRGBAf(0.3f,0.4f,0.5f,0.6f)); // sets [2] due to repeat
+ CHECK (data[2]==153);
+
+ CHECK (data[3]==13); // [3] left untouched
+
+ CHECK(ColorRGBA32(ColorRGBAf(1,1,1,0.2f)) == GetImagePixel (&data[0], 2, 2, image.GetFormat(), kTexWrapRepeat, 2, 2)); // gets [0] due to repeat
+ CHECK(ColorRGBA32(ColorRGBAf(1,1,1,0.4f)) == GetImagePixel (&data[0], 2, 2, image.GetFormat(), kTexWrapRepeat, 5, -2)); // gets [1] due to repeat
+ CHECK(ColorRGBA32(ColorRGBAf(1,1,1,0.6f)) == GetImagePixel (&data[0], 2, 2, image.GetFormat(), kTexWrapClamp, -1, 1)); // gets [2] due to clamp
+}
+
+TEST (SetGetImagePixelRGB565)
+{
+ UInt16 data[4];
+ memset (data, 0xab, sizeof(data));
+ ImageReference image (2, 2, 4, kTexFormatRGB565, data);
+ SetImagePixel (image, 0, 0, kTexWrapClamp, ColorRGBAf(1.0f,0.0f,0.0f,0.2f)); // sets [0]
+ CHECK (data[0]==0xf800);
+ SetImagePixel (image, 1, 0, kTexWrapClamp, ColorRGBAf(0.0f,1.0f,0.0f,0.4f)); // sets [1]
+ CHECK (data[1]==0x07e0);
+ SetImagePixel (image, 0, 1, kTexWrapClamp, ColorRGBAf(0.0f,0.0f,1.0f,0.6f)); // sets [2]
+ CHECK (data[2]==0x001f);
+ CHECK (data[3]==0xabab); // [3] still left untouched
+
+ ColorRGBAf gray(14.0f/31.0f, 31.0f/63.0f, 16.0f/31.0f, 1.0f);
+ SetImagePixel (image, 1, 1, kTexWrapClamp, gray); // sets [3]
+ CHECK (data[3]==0x73f0);
+
+ UInt8* srcData = reinterpret_cast<UInt8*>(&data[0]);
+ CHECK(ColorRGBA32(ColorRGBAf(1.0f,0.0f,0.0f,1)) == GetImagePixel (srcData, 2, 2, image.GetFormat(), kTexWrapRepeat, 2, 2)); // gets [0] due to repeat
+ CHECK(ColorRGBA32(ColorRGBAf(0.0f,1.0f,0.0f,1)) == GetImagePixel (srcData, 2, 2, image.GetFormat(), kTexWrapRepeat, 5, -2)); // gets [1] due to repeat
+ CHECK(ColorRGBA32(ColorRGBAf(0.0f,0.0f,1.0f,1)) == GetImagePixel (srcData, 2, 2, image.GetFormat(), kTexWrapClamp, -1, 1)); // gets [2] due to clamp
+ CHECK(ColorRGBA32(gray) == GetImagePixel (srcData, 2, 2, image.GetFormat(), kTexWrapClamp, 2, 2)); // gets [3] due to clamp
+}
+
+TEST (SetImagePixelBlockARGB)
+{
+ UInt8 data[16][16][4];
+ memset (data, 13, sizeof(data));
+ ImageReference image (16, 16, 16*4, kTexFormatARGB32, data);
+
+ ColorRGBAf color (1,0,1,0);
+ SetImagePixelBlock (&data[0][0][0], 16, 16, kTexFormatARGB32, 15,15,1,1, 1, &color);
+ CHECK (data[15][15][1]==255 && data[15][15][2]==0 && data[15][15][3]==255 && data[15][15][0]==0);
+}
+
+#if UNITY_EDITOR
+TEST (BlitBilinearARGBToFloat)
+{
+ Image src (128, 256, kTexFormatARGB32);
+ Image dst (16, 16, kTexFormatARGB32);
+ // ProphecySDK's bilinear blitter from integer source to floating point destination
+ // when sizes are different allocates temporary intermediate image, and blit there
+ // can result in access violation for the last pixel. This unit test would crash then, especially
+ // on Macs. ProphecySDK modified to allocate one pixel more for temporary images, just like
+ // we do for ours.
+ dst.ReformatImage (src, 128, 128, kTexFormatARGBFloat, Image::BLIT_BILINEAR_SCALE);
+}
+#endif
+
+TEST (CreateMipMap2x2)
+{
+ ColorRGBA32 data[4+1+1]; // 2x2, 1x1, one extra for out-of-bounds check
+ memset (data, 13, sizeof(data));
+ data[0] = ColorRGBA32(255,255,255,255);
+ data[1] = ColorRGBA32(255,255,255,0);
+ data[2] = ColorRGBA32(255,255,0,0);
+ data[3] = ColorRGBA32(255,0,0,0);
+ CreateMipMap ((UInt8*)data, 2, 2, 1, kTexFormatARGB32);
+
+ // next mip level of 1x1 size
+ CHECK(ColorRGBA32(255,191,127,63) == data[4]);
+
+ // data after that should be untouched
+ CHECK(ColorRGBA32(13,13,13,13) == data[5]);
+}
+TEST (CreateMipMap4x1)
+{
+ ColorRGBA32 data[4+2+1+1]; // 4x1, 2x1, 1x1, one extra for out-of-bounds check
+ memset (data, 13, sizeof(data));
+ data[0] = ColorRGBA32(255,255,255,255);
+ data[1] = ColorRGBA32(255,255,255,0);
+ data[2] = ColorRGBA32(255,255,0,0);
+ data[3] = ColorRGBA32(255,0,0,0);
+ CreateMipMap ((UInt8*)data, 4, 1, 1, kTexFormatARGB32);
+
+ // next mip level of 2x1 size
+ CHECK(ColorRGBA32(255,255,255,127) == data[4]);
+ CHECK(ColorRGBA32(255,127,0,0) == data[5]);
+
+ // next mip level of 1x1 size
+ CHECK(ColorRGBA32(255,191,127,63) == data[6]);
+
+ // data after that should be untouched
+ CHECK(ColorRGBA32(13,13,13,13) == data[7]);
+}
+TEST (CreateMipMap4x1x2)
+{
+ ColorRGBA32 data[8+2+1+1]; // 4x1x2, 2x1x1, 1x1x1, one extra for out-of-bounds check
+ memset (data, 13, sizeof(data));
+ data[0] = ColorRGBA32(255,255,255,255);
+ data[1] = ColorRGBA32(255,255,255,0);
+ data[2] = ColorRGBA32(255,255,0,0);
+ data[3] = ColorRGBA32(255,0,0,0);
+ data[4] = ColorRGBA32(128,128,128,128);
+ data[5] = ColorRGBA32(128,128,128,0);
+ data[6] = ColorRGBA32(128,128,0,0);
+ data[7] = ColorRGBA32(128,0,0,0);
+ CreateMipMap ((UInt8*)data, 4, 1, 2, kTexFormatARGB32);
+
+ // next mip level, 2x1x1 size
+ CHECK(ColorRGBA32(191,191,191,95) == data[8]);
+ CHECK(ColorRGBA32(191,95,0,0) == data[9]);
+
+ // next mip level, 1x1x1 size
+ CHECK(ColorRGBA32(191,143,95,47) == data[10]);
+
+ // data after that should be untouched
+ CHECK(ColorRGBA32(13,13,13,13) == data[11]);
+}
+TEST (CreateMipMap4x1x3)
+{
+ ColorRGBA32 data[12+2+1+1]; // 4x1x2, 2x1x1, 1x1x1, one extra for out-of-bounds check
+ memset (data, 13, sizeof(data));
+ data[0] = ColorRGBA32(255,255,255,255);
+ data[1] = ColorRGBA32(255,255,255,0);
+ data[2] = ColorRGBA32(255,255,0,0);
+ data[3] = ColorRGBA32(255,0,0,0);
+ data[4] = ColorRGBA32(128,128,128,128);
+ data[5] = ColorRGBA32(128,128,128,0);
+ data[6] = ColorRGBA32(128,128,0,0);
+ data[7] = ColorRGBA32(128,0,0,0);
+ data[8] = ColorRGBA32(64,64,64,64);
+ data[9] = ColorRGBA32(64,64,64,0);
+ data[10] = ColorRGBA32(64,64,0,0);
+ data[11] = ColorRGBA32(64,0,0,0);
+ CreateMipMap ((UInt8*)data, 4, 1, 3, kTexFormatARGB32);
+
+ // next mip level, 2x1x1 size
+ CHECK(ColorRGBA32(191,191,191,95) == data[12]);
+ CHECK(ColorRGBA32(191,95,0,0) == data[13]);
+
+ // next mip level, 1x1x1 size
+ CHECK(ColorRGBA32(191,143,95,47) == data[14]);
+
+ // data after that should be untouched
+ CHECK(ColorRGBA32(13,13,13,13) == data[15]);
+}
+
+
+} // SUITE
+
+#endif // ENABLE_UNIT_TESTS
diff --git a/Runtime/Graphics/Image.h b/Runtime/Graphics/Image.h
new file mode 100644
index 0000000..ef75c9e
--- /dev/null
+++ b/Runtime/Graphics/Image.h
@@ -0,0 +1,178 @@
+#ifndef IMAGE_H
+#define IMAGE_H
+
+#include "TextureFormat.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+
+class Image;
+class ColorRGBA32;
+class ColorRGBAf;
+class ImageReference;
+namespace prcore { class PixelFormat; }
+
+prcore::PixelFormat GetProphecyPixelFormat (TextureFormat format);
+
+
+// Inside image, leaves sizeX x sizeY portion untouched, fills the rest by
+// repeating border pixels.
+void PadImageBorder( ImageReference& image, int sizeX, int sizeY );
+
+// Copies source compressed image into (possibly larger) destination compressed image.
+void BlitCopyCompressedImage( TextureFormat format, const UInt8* src, int srcWidth, int srcHeight, UInt8* dst, int dstWidth, int dstHeight, bool fillRest );
+void BlitCopyCompressedDXT1ToDXT5( const UInt8* src, int srcWidth, int srcHeight, UInt8* dst, int dstWidth, int dstHeight );
+
+// Generates a mipmap chain
+void CreateMipMap (UInt8* inData, int width, int height, int depth, TextureFormat format);
+
+// Calculate the amount of mipmaps that can be created from an image of given size.
+// (The original image is counted as mipmap also)
+int CalculateMipMapCount3D (int width, int height, int depth);
+
+int CalculateImageSize (int width, int height, TextureFormat format);
+int CalculateImageMipMapSize (int width, int height, TextureFormat format);
+int CalculateMipMapOffset (int width, int height, TextureFormat format, int miplevel);
+
+void Premultiply( ImageReference& image );
+
+// Range of RGBM lightmaps is [0;8]
+enum { kRGBMMaxRange = 8 };
+void DecodeRGBM (int width, int height, UInt8* data, int pitch, const prcore::PixelFormat& pf);
+void SetAlphaChannel (int width, int height, UInt8* data, int pitch, const prcore::PixelFormat& pf, UInt8 alpha);
+void SetAlphaToRedChannel (int width, int height, UInt8* data, int pitch, const prcore::PixelFormat& pf);
+void XenonToNormalSRGBTexture (int width, int height, UInt8* data, int pitch, const prcore::PixelFormat& pf);
+
+#if UNITY_XENON
+enum { kImageDataAlignment = 4096 };
+#else
+enum { kImageDataAlignment = kDefaultMemoryAlignment };
+#endif
+
+class ImageReference
+{
+protected:
+ UInt32 m_Format;
+ SInt32 m_Width;
+ SInt32 m_Height;
+ SInt32 m_RowBytes;
+ UInt8* m_Image;
+
+public:
+
+ enum ClearMode
+ {
+ CLEAR_COLOR = 1,
+ CLEAR_ALPHA = 2,
+ CLEAR_COLOR_ALPHA = CLEAR_COLOR | CLEAR_ALPHA
+ };
+
+ enum BlitMode
+ {
+ BLIT_COPY,
+ BLIT_SCALE,
+ BLIT_BILINEAR_SCALE,
+ };
+
+ ImageReference () { m_Image = NULL; m_Width = 0; m_Height = 0; m_Format = 0; }
+ ImageReference (int width, int height, int rowbytes, TextureFormat format, void* image);
+ ImageReference (int width, int height, TextureFormat format);
+
+ // Returns true if the image is exactly the same by camping width, height, and image data
+ friend bool operator == (const ImageReference& lhs, const ImageReference& rhs);
+
+ // Returns a subpart of the image
+ ImageReference ClipImage (int x, int y, int width, int height) const;
+
+ UInt8* GetImageData () const { return m_Image; }
+ int GetRowBytes () const { return m_RowBytes; }
+ UInt8* GetRowPtr (int y) const { DebugAssertIf(y >= m_Height || y < 0); return m_Image + m_RowBytes * y; }
+ int GetWidth () const { return m_Width; }
+ int GetHeight () const { return m_Height; }
+ TextureFormat GetFormat() const { return (TextureFormat)m_Format; }
+
+ void BlitImage (const ImageReference& source, BlitMode mode = BLIT_COPY);
+ void BlitImage (int x, int y, const ImageReference& source);
+
+ void ClearImage (const ColorRGBA32& color, ClearMode mode = CLEAR_COLOR_ALPHA);
+ void FlipImageY ();
+ #if UNITY_EDITOR
+ void FlipImageX ();
+ #endif
+
+ bool IsValidImage () const;
+
+ bool NeedsReformat(int width, int height, TextureFormat format) const;
+};
+
+class Image : public ImageReference
+{
+public:
+
+ DECLARE_SERIALIZE (Image)
+
+ Image (int width, int height, TextureFormat format);
+ Image (int width, int height, int rowbytes, TextureFormat format, void* image);
+ Image () { }
+
+ // TODO : should be removed, because they implicitly have worse performance - use SetImage instead
+ Image (const Image& image) : ImageReference() { SetImage (image); }
+ Image (const ImageReference& image) { SetImage (image); }
+
+ // TODO : should be removed, because they implicitly have worse performance - use SetImage instead
+ void operator = (const ImageReference& image) { SetImage (image); }
+ void operator = (const Image& image) { SetImage (image); }
+
+ ~Image () { UNITY_FREE(kMemNewDelete, m_Image); }
+
+ // Reformats given image into itself
+ void ReformatImage (const ImageReference& image, int width, int height, TextureFormat format, BlitMode mode = BLIT_COPY);
+ // Reformats itself
+ void ReformatImage (int width, int height, TextureFormat format, BlitMode mode = BLIT_COPY);
+
+ // To initialize an image use: someImage = ImageReference (width, height, format);
+
+ // shrinkAllowed - controls if memory should be reallocated when Image needs less memory,
+ // if shrinkAllowed is set to false it won't reallocate memory if Image needs less memory
+ void SetImage(const ImageReference& src, bool shrinkAllowed = true);
+ void SetImage(SInt32 width, SInt32 height, UInt32 format, bool shrinkAllowed);
+};
+
+bool CheckImageFormatValid (int width, int height, TextureFormat format);
+
+
+extern const char* kUnsupportedGetPixelOpFormatMessage;
+extern const char* kUnsupportedSetPixelOpFormatMessage;
+void SetImagePixel (ImageReference& image, int x, int y, TextureWrapMode wrap, const ColorRGBAf& color);
+ColorRGBA32 GetImagePixel (UInt8* data, int width, int height, TextureFormat format, TextureWrapMode wrap, int x, int y);
+ColorRGBAf GetImagePixelBilinear (UInt8* data, int width, int height, TextureFormat format, TextureWrapMode wrap, float u, float v);
+bool GetImagePixelBlock (UInt8* data, int dataWidth, int dataHeight, TextureFormat format, int x, int y, int blockWidth, int blockHeight, ColorRGBAf* outColors);
+bool SetImagePixelBlock (UInt8* data, int dataWidth, int dataHeight, TextureFormat format, int x, int y, int blockWidth, int blockHeight, int pixelCount, const ColorRGBAf* pixels);
+
+void SwizzleARGB32ToBGRA32 (UInt8* bytes, int imageSize);
+void SwizzleRGBA32ToBGRA32 (UInt8* bytes, int imageSize);
+void SwizzleRGB24ToBGR24 (UInt8* bytes, int imageSize);
+void SwizzleBGRAToRGBA32 (UInt8* bytes, int imageSize);
+
+template<class TransferFunction> inline
+void Image::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (m_Format);
+ TRANSFER (m_Width);
+ TRANSFER (m_Height);
+ TRANSFER (m_RowBytes);
+
+ unsigned completeImageSize = m_RowBytes * std::max<int>(m_Height, 0);
+
+ transfer.TransferTypeless (&completeImageSize, "image data");
+ if (transfer.IsReading ())
+ {
+ UNITY_FREE(kMemNewDelete, m_Image);
+ m_Image = NULL;
+ if (completeImageSize != 0 && CheckImageFormatValid (m_Width, m_Height, m_Format))
+ m_Image = (UInt8*)UNITY_MALLOC_ALIGNED(kMemNewDelete, completeImageSize, kImageDataAlignment);
+ }
+
+ transfer.TransferTypelessData (completeImageSize, m_Image);
+}
+
+#endif
diff --git a/Runtime/Graphics/ImageConversion.cpp b/Runtime/Graphics/ImageConversion.cpp
new file mode 100644
index 0000000..fe0b462
--- /dev/null
+++ b/Runtime/Graphics/ImageConversion.cpp
@@ -0,0 +1,621 @@
+#include "UnityPrefix.h"
+#include "ImageConversion.h"
+#include "Runtime/Utilities/File.h"
+#include "Texture2D.h"
+#include "Image.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Math/Color.h"
+#include "DXTCompression.h"
+#if ENABLE_PNG_JPG
+#include "External/ProphecySDK/src/extlib/pnglib/png.h"
+#include "External/ProphecySDK/src/extlib/jpglib/jpeglib.h"
+#include "Runtime/Export/JPEGMemsrc.h"
+#endif
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDeviceConfigure.h"
+#include <setjmp.h>
+#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h"
+
+using namespace std;
+
+// --------------------------------------------------------------------------
+// PNG
+
+#if ENABLE_PNG_JPG
+static void PngWriteToMemoryFunc( png_structp png, png_bytep data, png_size_t size )
+{
+ MemoryBuffer* buffer = (MemoryBuffer*)png->io_ptr;
+ buffer->insert( buffer->end(), data, data+size );
+}
+static void PngWriteFlushFunc( png_structp png )
+{
+}
+
+
+bool ConvertImageToPNGBuffer( const ImageReference& inputImage, MemoryBuffer& buffer )
+{
+ png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,(png_voidp)NULL, NULL, NULL);
+ if (!png_ptr)
+ return false;
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ return false;
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ return false;
+
+ buffer.reserve( 4096 );
+ png_set_write_fn( png_ptr, &buffer, PngWriteToMemoryFunc, PngWriteFlushFunc );
+
+ png_set_compression_level(png_ptr, Z_BEST_SPEED);
+
+ int format = kTexFormatRGBA32;
+ if (inputImage.GetFormat() == kTexFormatRGB24 || inputImage.GetFormat() == kTexFormatRGB565)
+ format = kTexFormatRGB24;
+
+ Image image( inputImage.GetWidth(), inputImage.GetHeight(), format );
+ image.BlitImage( inputImage );
+
+ png_set_IHDR(png_ptr, info_ptr,
+ inputImage.GetWidth(),
+ inputImage.GetHeight(),
+ 8,
+ format == kTexFormatRGB24 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA,
+ PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+
+ png_write_info(png_ptr, info_ptr);
+ for (int i = 0; i < image.GetHeight(); i++)
+ png_write_row(png_ptr, image.GetRowPtr(image.GetHeight() - i - 1));
+
+ png_write_end(png_ptr, info_ptr);
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+ return !buffer.empty();
+}
+
+
+#if CAPTURE_SCREENSHOT_AVAILABLE
+
+bool ConvertImageToPNGFile (const ImageReference& inputImage, const string& path)
+{
+ MemoryBuffer buffer;
+ if( !ConvertImageToPNGBuffer( inputImage, buffer ) )
+ return false;
+
+ #if UNITY_PEPPER
+ // no file access in pepper. write screenshot to stdout.
+ // we need to write hex, as string reading in mono may perform
+ // string encoding translation on stdout, breaking the binary data.
+ printf_console("SCREENSHOT_MARKER=%sSCREENSHOTSTART_MARKER", path.c_str());
+ for (int i=0; i<buffer.size(); i++)
+ printf_console("%02x", buffer[i]);
+ printf_console("SCREENSHOTEND_MARKER\n");
+ return true;
+ #else
+#if ENABLE_PLAYERCONNECTION
+ TransferFileOverPlayerConnection(path, &buffer[0], buffer.size());
+#endif
+ return WriteBytesToFile (&buffer[0], buffer.size(), path);
+ #endif
+}
+
+#endif
+
+struct PngMemoryReadContext {
+ const unsigned char* inputPointer;
+ size_t inputSizeLeft;
+};
+
+static void PngReadFromMemoryFunc( png_structp png_ptr, png_bytep data, png_size_t len )
+{
+ PngMemoryReadContext* context = (PngMemoryReadContext*)png_ptr->io_ptr;
+ // check for overflow
+ if( len > context->inputSizeLeft )
+ len = context->inputSizeLeft;
+
+ memcpy( data, context->inputPointer, len );
+ context->inputPointer += len;
+ context->inputSizeLeft -= len;
+}
+
+
+static void PngReadWarningFunc( png_struct* png_ptr, png_const_charp warning_msg )
+{
+}
+
+bool ImageSizeBoundCheck(unsigned int width, unsigned int height) {
+ int tmp, tmp2;
+ if (width + 3 < width ||
+ height + 3 < height) {
+ return 0;
+ }
+
+ tmp = width * height;
+ if (width != 0 && height != tmp / width) {
+ return 0;
+ }
+
+ tmp2 = tmp * 16;
+ if (tmp != tmp2/16) {
+ return 0;
+ }
+ return 1;
+}
+
+static bool LoadPngIntoTexture( Texture2D& texture, const void* data, size_t size, bool compressTexture, UInt8** outRGBABaseLevelForDXTMips )
+{
+ PngMemoryReadContext context;
+ context.inputPointer = static_cast<const unsigned char*>( data );
+ context.inputSizeLeft = size;
+
+ if( !data )
+ return false;
+
+ // check png header
+ if( size < 8 || !png_check_sig( const_cast<unsigned char*>(context.inputPointer), 8 ) )
+ return false;
+
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_uint_32 width, height;
+ int bit_depth, color_type, interlace_type;
+
+ double image_gamma = 0.45;
+ int number_passes = 0;
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,&PngReadWarningFunc);
+ if( png_ptr == NULL )
+ return false;
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if( info_ptr == NULL )
+ {
+ png_destroy_read_struct(&png_ptr,(png_infopp)NULL,(png_infopp)NULL);
+ return false;
+ }
+
+ if( setjmp(png_ptr->jmpbuf) )
+ {
+ png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)NULL);
+ return false;
+ }
+
+ png_set_read_fn( png_ptr, &context, &PngReadFromMemoryFunc );
+ png_read_info( png_ptr, info_ptr );
+
+ png_get_IHDR( png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL );
+
+ png_set_strip_16(png_ptr); // strip 16 bit channels to 8 bit
+ png_set_packing(png_ptr); // separate palettized channels
+
+ // palette -> rgb
+ if( color_type == PNG_COLOR_TYPE_PALETTE )
+ {
+ png_set_expand(png_ptr);
+ }
+
+ // grayscale -> 8 bits
+ if( !(color_type & PNG_COLOR_MASK_COLOR) && bit_depth < 8 ) png_set_expand(png_ptr);
+
+ // if exists, expand tRNS to alpha channel
+ if( png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS) ) png_set_expand(png_ptr);
+
+ // expand gray to RGB
+ if( color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
+ png_set_gray_to_rgb(png_ptr);
+
+ // we need to get ARGB format for raw textures, and RGBA if we will compress it
+ if( !compressTexture )
+ png_set_swap_alpha(png_ptr);
+
+ png_set_filler( png_ptr, 0xFF, PNG_FILLER_BEFORE ); // force alpha byte
+
+ // Only apply gamma correction if the image has gamma information. In this case
+ // assume our display gamma is 2.0 (a compromise between Mac and PC...).
+ double screen_gamma = 2.0f;
+ image_gamma = 0.0;
+ if ( png_get_gAMA(png_ptr,info_ptr,&image_gamma) )
+ {
+ png_set_gamma(png_ptr,screen_gamma,image_gamma);
+ }
+
+ number_passes = png_set_interlace_handling(png_ptr);
+ png_read_update_info(png_ptr,info_ptr); // update gamma, etc.
+
+ // If texture size or format differs, reformat the texture.
+ TextureFormat wantedFormat = compressTexture ? kTexFormatDXT5 : kTexFormatARGB32;
+ bool mipMaps = texture.HasMipMap();
+ if( width != texture.GetDataWidth () || height != texture.GetDataHeight () || kTexFormatARGB32 != texture.GetTextureFormat())
+ texture.InitTexture( width, height, wantedFormat, mipMaps ? Texture2D::kMipmapMask : Texture2D::kNoMipmap, 1 );
+
+ ImageReference ref;
+ AssertIf( !outRGBABaseLevelForDXTMips );
+ if( compressTexture ) {
+ int imageByteSize;
+ if (mipMaps) {
+ int miplevel = CalculateMipMapCount3D(width, height, 1);
+ int iter;
+ unsigned int completeSize = 0;
+ unsigned int prevcompleteSize = 0;
+
+ if (!ImageSizeBoundCheck(width, height)) {
+ longjmp(png_ptr->jmpbuf, 1);
+ }
+
+ for (iter = 0; iter < miplevel; iter++) {
+ prevcompleteSize = completeSize;
+ completeSize += CalculateImageSize (std::max (width >> iter, (png_uint_32)1), std::max (height >> iter, (png_uint_32)1), kTexFormatRGBA32);
+ if (completeSize < prevcompleteSize) {
+ longjmp(png_ptr->jmpbuf, 1);
+ }
+ }
+ imageByteSize = CalculateImageMipMapSize( width, height, kTexFormatRGBA32 );
+ } else {
+ if (!ImageSizeBoundCheck(width, height)) {
+ longjmp(png_ptr->jmpbuf, 1);
+ }
+ imageByteSize = CalculateImageSize( width, height, kTexFormatRGBA32 );
+ }
+
+ *outRGBABaseLevelForDXTMips = new UInt8[imageByteSize];
+ ref = ImageReference( width, height, width*4, kTexFormatRGBA32, *outRGBABaseLevelForDXTMips );
+ } else {
+ if( !texture.GetWriteImageReference(&ref, 0, 0) ) {
+ png_destroy_read_struct( &png_ptr,&info_ptr,(png_infopp)NULL );
+ return false;
+ }
+ }
+
+ size_t len = sizeof(png_bytep) * height;
+
+ /* boundcheck for integer overflow */
+ if (height != len / sizeof(png_bytep) ) {
+ png_destroy_read_struct( &png_ptr,&info_ptr,(png_infopp)NULL );
+ return false;
+ }
+
+ png_bytep* row_pointers = new png_bytep[height];
+ for( png_uint_32 row = 0; row<height; ++row )
+ {
+ row_pointers[row] = ref.GetRowPtr(height-1-row);
+ }
+
+ for( int pass = 0; pass < number_passes; pass++ )
+ {
+ png_read_rows( png_ptr,row_pointers,NULL,height );
+ }
+
+ // cleanup
+ png_read_end( png_ptr,info_ptr );
+ png_destroy_read_struct( &png_ptr,&info_ptr,(png_infopp)NULL );
+ delete[] row_pointers;
+
+ return true;
+}
+
+// --------------------------------------------------------------------------
+// JPG
+
+/* Custom error manager. As the default one in libjpeg crashes Unity on error */
+struct unity_jpeg_error_mgr {
+ struct jpeg_error_mgr pub; /* "public" fields */
+ jmp_buf setjmp_buffer; /* for return to caller */
+};
+typedef struct unity_jpeg_error_mgr * unity_jpeg_error_ptr;
+
+/*
+ * Here's the routine that will replace the standard error_exit method:
+ */
+METHODDEF(void)
+unity_jpeg_error_exit (j_common_ptr cinfo)
+{
+ /* cinfo->err really points to a unity_jpeg_error_mgr struct, so coerce pointer */
+ unity_jpeg_error_ptr myerr = (unity_jpeg_error_ptr) cinfo->err;
+ /* Always display the message. */
+ /* We could postpone this until after returning, if we chose. */
+ (*cinfo->err->output_message) (cinfo);
+ /* Return control to the setjmp point */
+ longjmp(myerr->setjmp_buffer, 1);
+}
+
+static void HandleError (struct jpeg_decompress_struct& cinfo, Texture2D* tex)
+{
+ jpeg_destroy_decompress (&cinfo);
+}
+
+static int LoadJpegIntoTexture(Texture2D& tex, const UInt8* jpegData, size_t jpegDataSz, bool compressTexture, UInt8** outRGBABaseLevelForDXTMips)
+{
+#if UNITY_WII
+ AssertIf ("ERROR: LoadJpegIntoTexture is not supported!");
+ return 0;
+#else
+ struct jpeg_decompress_struct cinfo;
+
+ /* We use our private extension JPEG error handler.
+ * Note that this struct must live as long as the main JPEG parameter
+ * struct, to avoid dangling-pointer problems.
+ */
+ struct unity_jpeg_error_mgr jerr;
+
+ JSAMPARRAY in;
+ int row_stride;
+ unsigned char *out;
+
+ // set up the decompression.
+ cinfo.err = jpeg_std_error(&jerr.pub);
+ jerr.pub.error_exit = unity_jpeg_error_exit;
+
+ /* Establish the setjmp return context for my_error_exit to use. */
+ if (setjmp(jerr.setjmp_buffer)) {
+ /* If we get here, the JPEG code has signaled an error.
+ * We need to clean up the JPEG object, close the input file, and return.
+ */
+ HandleError(cinfo, &tex);
+ return 0;
+ }
+
+ jpeg_create_decompress (&cinfo);
+
+ // inititalize the source
+ jpeg_memory_src (&cinfo, (unsigned char*)jpegData, jpegDataSz);
+
+ // initialize decompression
+ (void) jpeg_read_header (&cinfo, TRUE);
+ (void) jpeg_start_decompress (&cinfo);
+
+ // set up the width and height for return
+ int width = cinfo.image_width;
+ int height = cinfo.image_height;
+
+ // initialize the input buffer - we'll use the in-built memory management routines in the
+ // JPEG library because it will automatically free the used memory for us when we destroy
+ // the decompression structure. cool.
+ row_stride = cinfo.output_width * cinfo.output_components;
+ if (cinfo.output_width != 0 && cinfo.output_components != row_stride / cinfo.output_width) {
+ HandleError(cinfo, &tex);
+ return 0;
+ }
+ in = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
+
+ // we only support three channel RGB or one channel grayscale formats
+
+ if (cinfo.output_components != 3 && cinfo.output_components != 1)
+ {
+ HandleError(cinfo, &tex);
+ return 0;
+ }
+
+ // If texture size or format differs, reformat the texture.
+ TextureFormat wantedFormat = compressTexture ? kTexFormatDXT1 : kTexFormatRGB24;
+ bool mipMaps = tex.HasMipMap();
+ if (width != tex.GetDataWidth () || height != tex.GetDataHeight () || wantedFormat != tex.GetTextureFormat())
+ tex.InitTexture (width, height, wantedFormat, mipMaps ? Texture2D::kMipmapMask : Texture2D::kNoMipmap, 1);
+
+ ImageReference ref;
+ AssertIf( !outRGBABaseLevelForDXTMips );
+ if( compressTexture ) {
+ int imageByteSize;
+ if (mipMaps) {
+ int miplevel = CalculateMipMapCount3D(width, height, 1);
+ int iter;
+ unsigned int completeSize = 0;
+ unsigned int prevcompleteSize = 0;
+
+ if (!ImageSizeBoundCheck(width, height)) {
+ HandleError(cinfo, &tex);
+ return 0;
+ }
+
+ for (iter = 0; iter < miplevel; iter++) {
+ prevcompleteSize = completeSize;
+ completeSize += CalculateImageSize (std::max (width >> iter, 1), std::max (height >> iter, 1), kTexFormatRGBA32);
+ if (completeSize < prevcompleteSize) {
+ HandleError(cinfo, &tex);
+ return 0;
+ }
+ }
+ imageByteSize = CalculateImageMipMapSize( width, height, kTexFormatRGBA32 );
+ } else {
+ if (!ImageSizeBoundCheck(width, height)) {
+ HandleError(cinfo, &tex);
+ return 0;
+ }
+ imageByteSize = CalculateImageSize( width, height, kTexFormatRGBA32 );
+ }
+ *outRGBABaseLevelForDXTMips = new UInt8[imageByteSize];
+ ref = ImageReference( width, height, width*4, kTexFormatRGBA32, *outRGBABaseLevelForDXTMips );
+ } else {
+ if( !tex.GetWriteImageReference(&ref, 0, 0) ) {
+ jpeg_destroy_decompress (&cinfo);
+ return 0;
+ }
+ }
+
+ UInt8* dst;
+ int scanline = height-1;
+ int result = 1;
+
+ if (scanline < 0) {
+ HandleError(cinfo, &tex);
+ return 0;
+ }
+
+ // three channel output
+ if( cinfo.output_components == 3 )
+ {
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ jpeg_read_scanlines(&cinfo, in, 1);
+ dst = ref.GetRowPtr(scanline);
+ out = in[0];
+ if( !compressTexture ) {
+ for( int i = 0; i < row_stride; i += 3, dst += 3 )
+ {
+ dst[0] = out[i+2];
+ dst[1] = out[i+1];
+ dst[2] = out[i];
+ }
+ } else {
+ for( int i = 0; i < row_stride; i += 3, dst += 4 )
+ {
+ dst[0] = out[i+2];
+ dst[1] = out[i+1];
+ dst[2] = out[i];
+ dst[3] = 255;
+ }
+ }
+ --scanline;
+ }
+ }
+ // one channel output
+ else if( cinfo.output_components == 1 )
+ {
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ jpeg_read_scanlines(&cinfo, in, 1);
+ dst = ref.GetRowPtr(scanline);
+ out = in[0];
+ if( !compressTexture ) {
+ for( int i = 0; i < row_stride; ++i, dst += 3 )
+ {
+ UInt8 v = out[i];
+ dst[0] = v;
+ dst[1] = v;
+ dst[2] = v;
+ }
+ } else {
+ for( int i = 0; i < row_stride; ++i, dst += 4 )
+ {
+ UInt8 v = out[i];
+ dst[0] = v;
+ dst[1] = v;
+ dst[2] = v;
+ dst[4] = 255;
+ }
+ }
+ --scanline;
+ }
+ }
+ else
+ {
+ AssertString("unsupported number of output components in JPEG");
+ result = 0;
+ }
+
+ // finish decompression and destroy the jpeg
+ (void) jpeg_finish_decompress (&cinfo);
+ jpeg_destroy_decompress (&cinfo);
+
+
+ return result;
+#endif
+}
+
+#endif
+
+// --------------------------------------------------------------------------
+// Generic byte->texture loading
+
+
+// Simple image to load into the texture on error: "?"
+static const char* kDummyErrorImage =
+"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\x00\x00\xff\xff\xff\xff\xff\xff"
+"\xff\xff\xff\xff\x00\x00\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\x00\x00\xff\xff\xff"
+"\xff\xff\xff\xff\x00\x00\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\x00\x00\xff\xff\xff"
+"\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\x00\x00\xff\x00\x00\xff\x00\x00\xff\xff\xff\xff\xff\xff";
+
+#if UNITY_FLASH
+//Leaky temporary workaround to deal with not having setjmp/longjmp on Flash. This is only to be able to return correctly, but doesn't clean up anything...beware!
+
+__attribute__ ((noinline)) static int LoadJpegIntoTextureFlash(Texture2D& tex, const UInt8* jpegData, size_t jpegDataSz, bool compressTexture, UInt8** outRGBABaseLevelForDXTMips)
+{
+ __asm __volatile__("try{");
+ int val = LoadJpegIntoTexture(tex,jpegData,jpegDataSz,compressTexture,outRGBABaseLevelForDXTMips);
+ __asm __volatile__("}catch(e:Error){r_g0 = 0; return;}");
+ return val;
+}
+
+__attribute__ ((noinline)) static bool LoadPngIntoTextureFlash( Texture2D& texture, const void* data, size_t size, bool compressTexture, UInt8** outRGBABaseLevelForDXTMips )
+{
+ __asm __volatile__("try{");
+ bool val = LoadPngIntoTexture(texture,data,size,compressTexture,outRGBABaseLevelForDXTMips);
+ __asm __volatile__("}catch(e:Error){r_g0 = 0; return;}");
+ return val;
+}
+
+#define LoadJpegIntoTexture LoadJpegIntoTextureFlash
+#define LoadPngIntoTexture LoadPngIntoTextureFlash
+#endif
+
+bool LoadMemoryBufferIntoTexture( Texture2D& tex, const UInt8* data, size_t size, LoadImageCompression compression, bool markNonReadable )
+{
+ UInt8* rgbaBaseLevel = NULL;
+ if( !gGraphicsCaps.hasS3TCCompression )
+ compression = kLoadImageUncompressed;
+
+#if ENABLE_PNG_JPG
+ if( !LoadJpegIntoTexture( tex, data, size, compression!=kLoadImageUncompressed, &rgbaBaseLevel ) )
+ {
+ delete[] rgbaBaseLevel; rgbaBaseLevel = NULL;
+ if( !LoadPngIntoTexture( tex, data, size, compression!=kLoadImageUncompressed, &rgbaBaseLevel ) )
+ {
+ // Store a dummy image into the texture
+ tex.InitTexture( 8, 8, kTexFormatRGB24, Texture2D::kNoMipmap, 1 );
+ unsigned char* textureData = (unsigned char*)tex.GetRawImageData();
+ memcpy( textureData, kDummyErrorImage, 8*8*3 );
+ }
+ }
+#endif
+ // Compress the image if needed
+ bool isDXTCompressed = IsCompressedDXTTextureFormat(tex.GetTextureFormat());
+ if( isDXTCompressed ) {
+ #if !GFX_SUPPORTS_DXT_COMPRESSION
+ delete[] rgbaBaseLevel;
+ return false;
+ #else
+
+ AssertIf( !rgbaBaseLevel );
+ // do final image compression
+ int width = tex.GetDataWidth();
+ int height = tex.GetDataHeight();
+ TextureFormat texFormat = tex.GetTextureFormat();
+ AssertIf( texFormat != kTexFormatDXT1 && texFormat != kTexFormatDXT5 );
+ FastCompressImage( width, height, rgbaBaseLevel, tex.GetRawImageData(), texFormat == kTexFormatDXT5, compression==kLoadImageDXTCompressDithered );
+
+ bool mipMaps = tex.HasMipMap();
+ if( mipMaps ) {
+ // compute mip representations from the base level
+ CreateMipMap (rgbaBaseLevel, width, height, 1, kTexFormatRGBA32);
+ // DXT compress them
+ int mipCount = tex.CountDataMipmaps();
+ for( int mip = 1; mip < mipCount; ++mip )
+ {
+ const UInt8* srcMipData = rgbaBaseLevel + CalculateMipMapOffset(width, height, kTexFormatRGBA32, mip);
+ UInt8* dstMipData = tex.GetRawImageData() + CalculateMipMapOffset(width, height, texFormat, mip);
+ FastCompressImage( std::max(width>>mip,1), std::max(height>>mip,1), srcMipData, dstMipData, texFormat == kTexFormatDXT5, compression==kLoadImageDXTCompressDithered );
+ }
+ }
+ #endif // GFX_SUPPORTS_DXT_COMPRESSION
+ }
+ delete[] rgbaBaseLevel;
+
+ if(markNonReadable)
+ {
+ tex.SetIsReadable(false);
+ tex.SetIsUnreloadable(true);
+ }
+
+ if( !IsCompressedDXTTextureFormat(tex.GetTextureFormat()) )
+ tex.UpdateImageData();
+ else
+ tex.UpdateImageDataDontTouchMipmap();
+
+ return true;
+}
diff --git a/Runtime/Graphics/ImageConversion.h b/Runtime/Graphics/ImageConversion.h
new file mode 100644
index 0000000..31ed2e0
--- /dev/null
+++ b/Runtime/Graphics/ImageConversion.h
@@ -0,0 +1,28 @@
+#ifndef _IMAGECONVERSION_H
+#define _IMAGECONVERSION_H
+
+#include "Runtime/Utilities/dynamic_array.h"
+
+class ImageReference;
+class Texture2D;
+typedef dynamic_array<UInt8> MemoryBuffer;
+
+bool ConvertImageToPNGBuffer( const ImageReference& image, MemoryBuffer& buffer );
+
+enum LoadImageCompression {
+ kLoadImageUncompressed = 0,
+ kLoadImageDXTCompress,
+ kLoadImageDXTCompressDithered,
+};
+bool LoadMemoryBufferIntoTexture( Texture2D& tex, const UInt8* data, size_t size, LoadImageCompression compression, bool markNonReadable=false );
+
+
+// hack: this is used only by capture screenshot. Compile code out if it's not
+// available
+#include "Runtime/Misc/CaptureScreenshot.h"
+#if CAPTURE_SCREENSHOT_AVAILABLE
+bool ConvertImageToPNGFile( const ImageReference& image, const std::string& path );
+#endif
+
+
+#endif
diff --git a/Runtime/Graphics/LightProbeGroup.cpp b/Runtime/Graphics/LightProbeGroup.cpp
new file mode 100644
index 0000000..1b19bcb
--- /dev/null
+++ b/Runtime/Graphics/LightProbeGroup.cpp
@@ -0,0 +1,50 @@
+#include "UnityPrefix.h"
+#include "LightProbeGroup.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+using namespace std;
+
+#if UNITY_EDITOR
+static LightProbeGroupList gAllLightProbeGroups;
+#endif
+
+LightProbeGroup::LightProbeGroup (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+#if UNITY_EDITOR
+ , m_LightProbeGroupNode (this)
+#endif
+{
+}
+
+LightProbeGroup::~LightProbeGroup ()
+{
+}
+
+IMPLEMENT_CLASS (LightProbeGroup)
+IMPLEMENT_OBJECT_SERIALIZE (LightProbeGroup)
+
+template<class T> inline
+void LightProbeGroup::Transfer (T& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER_EDITOR_ONLY (m_SourcePositions);
+}
+
+#if UNITY_EDITOR
+void LightProbeGroup::AddToManager ()
+{
+ gAllLightProbeGroups.push_back (m_LightProbeGroupNode);
+}
+
+void LightProbeGroup::RemoveFromManager ()
+{
+ m_LightProbeGroupNode.RemoveFromList ();
+}
+
+LightProbeGroupList& GetLightProbeGroups ()
+{
+ return gAllLightProbeGroups;
+}
+#endif
diff --git a/Runtime/Graphics/LightProbeGroup.h b/Runtime/Graphics/LightProbeGroup.h
new file mode 100644
index 0000000..3ca61a3
--- /dev/null
+++ b/Runtime/Graphics/LightProbeGroup.h
@@ -0,0 +1,41 @@
+#ifndef LIGHTPROBEGROUP_H
+#define LIGHTPROBEGROUP_H
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+class LightProbeGroup : public Behaviour
+{
+public:
+ REGISTER_DERIVED_CLASS (LightProbeGroup, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (LightProbeGroup)
+
+ LightProbeGroup (MemLabelId label, ObjectCreationMode mode);
+
+#if UNITY_EDITOR
+ void SetPositions(Vector3f* data, int size) { m_SourcePositions.assign(data, data + size); SetDirty(); }
+ Vector3f* GetPositions() { return m_SourcePositions.size() > 0 ? &m_SourcePositions[0] : NULL; }
+ int GetPositionsSize() { return m_SourcePositions.size(); }
+
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+#else
+ virtual void AddToManager () {}
+ virtual void RemoveFromManager () {}
+#endif
+
+private:
+#if UNITY_EDITOR
+ dynamic_array<Vector3f> m_SourcePositions;
+ ListNode<LightProbeGroup> m_LightProbeGroupNode;
+#endif
+};
+
+#if UNITY_EDITOR
+typedef List< ListNode<LightProbeGroup> > LightProbeGroupList;
+LightProbeGroupList& GetLightProbeGroups ();
+#endif
+
+#endif
diff --git a/Runtime/Graphics/LightmapSettings.cpp b/Runtime/Graphics/LightmapSettings.cpp
new file mode 100644
index 0000000..a2ff547
--- /dev/null
+++ b/Runtime/Graphics/LightmapSettings.cpp
@@ -0,0 +1,153 @@
+#include "UnityPrefix.h"
+#include "LightmapSettings.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Scripting.h"
+
+const char* const LightmapSettings::kLightmapsModeNames[] =
+{
+ "Single Lightmaps",
+ "Dual Lightmaps",
+ "Directional Lightmaps",
+ "RNM"
+};
+
+template<class T>
+void LightmapData::Transfer (T& transfer)
+{
+ TRANSFER(m_Lightmap);
+ TRANSFER(m_IndirectLightmap);
+}
+
+template<class T>
+void LightmapSettings::Transfer (T& transfer)
+{
+ Super::Transfer(transfer);
+ TRANSFER(m_LightProbes);
+ TRANSFER(m_Lightmaps);
+ TRANSFER(m_LightmapsMode);
+ TRANSFER(m_BakedColorSpace);
+ TRANSFER(m_UseDualLightmapsInForward);
+ transfer.Align ();
+ TRANSFER_EDITOR_ONLY(m_LightmapEditorSettings);
+}
+
+LightmapSettings::LightmapSettings(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_LightmapTextures(NULL)
+, m_LightmapTextureCount(0)
+, m_LightmapsMode(kDualLightmapsMode)
+, m_UseDualLightmapsInForward(false)
+, m_BakedColorSpace(0)
+{
+}
+
+LightmapSettings::~LightmapSettings ()
+{
+ delete[] m_LightmapTextures;
+}
+
+void LightmapSettings::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad(awakeMode);
+ Rebuild ();
+}
+
+void LightmapSettings::Rebuild ()
+{
+ delete[] m_LightmapTextures;
+
+ const size_t lightmapCount = m_Lightmaps.size();
+
+ m_LightmapTextures = new TextureTriple[lightmapCount];
+ m_LightmapTextureCount = lightmapCount;
+
+ for (size_t i = 0; i < lightmapCount; ++i)
+ {
+ Texture2D* tex = m_Lightmaps[i].m_Lightmap;
+ Texture2D* texInd = m_Lightmaps[i].m_IndirectLightmap;
+ Texture2D* texThird = m_Lightmaps[i].m_ThirdLightmap;
+ TextureTriple p;
+ p.first = tex ? tex->GetTextureID() : TextureID();
+ p.second = texInd ? texInd->GetTextureID() : TextureID();
+ p.third = texThird ? texThird->GetTextureID() : TextureID();
+ m_LightmapTextures[i] = p;
+ }
+}
+
+void LightmapSettings::ClearLightmaps()
+{
+ m_Lightmaps.clear();
+ Rebuild();
+ SetDirty();
+}
+
+void LightmapSettings::SetLightmaps (const std::vector<LightmapData>& data)
+{
+ m_Lightmaps = data;
+ Rebuild();
+ SetDirty();
+}
+
+void LightmapSettings::SetLightProbes (LightProbes* lightProbes)
+{
+ if (lightProbes && !GetBuildSettings().hasAdvancedVersion)
+ {
+ ErrorString ("Light probes require Unity Pro.");
+ return;
+ }
+
+ m_LightProbes = lightProbes;
+ SetDirty();
+}
+
+LightProbes* LightmapSettings::GetLightProbes ()
+{
+ return m_LightProbes;
+}
+
+bool LightmapSettings::GetUseDualLightmapsInForward () const
+{
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion3_5_a1))
+ return (m_LightmapsMode == kDualLightmapsMode) && m_UseDualLightmapsInForward;
+ else
+ return m_UseDualLightmapsInForward;
+}
+
+void LightmapSettings::AppendLightmaps (const std::vector<LightmapData>& data)
+{
+ int originalSize = m_Lightmaps.size();
+ int dataSize = data.size();
+
+ if (originalSize + data.size() > LightmapSettings::kMaxLightmaps)
+ {
+ int newDataSize = max(0, LightmapSettings::kMaxLightmaps - originalSize);
+ ErrorString(Format(
+ "Can't append %i lightmaps, since that would exceed the %i lightmaps limit. "
+ "Appending only %i lightmaps. Objects that use lightmaps past that limit won't get proper lightmaps.",
+ dataSize, LightmapSettings::kMaxLightmaps, newDataSize));
+ dataSize = newDataSize;
+ }
+ if ( dataSize <= 0 ) return;
+ m_Lightmaps.resize(originalSize + dataSize);
+ std::copy(data.begin(), data.begin() + dataSize, m_Lightmaps.begin() + originalSize);
+
+ Rebuild();
+ SetDirty();
+}
+
+#if ENABLE_SCRIPTING
+void LightmapDataToMono (const LightmapData &src, LightmapDataMono &dest) {
+ dest.m_Lightmap = Scripting::ScriptingWrapperFor (src.m_Lightmap);
+ dest.m_IndirectLightmap = Scripting::ScriptingWrapperFor (src.m_IndirectLightmap);
+}
+void LightmapDataToCpp (LightmapDataMono &src, LightmapData &dest) {
+ dest.m_Lightmap = ScriptingObjectToObject<Texture2D> (src.m_Lightmap);
+ dest.m_IndirectLightmap = ScriptingObjectToObject<Texture2D> (src.m_IndirectLightmap);
+}
+#endif
+
+IMPLEMENT_CLASS (LightmapSettings)
+IMPLEMENT_OBJECT_SERIALIZE (LightmapSettings)
+GET_MANAGER (LightmapSettings)
diff --git a/Runtime/Graphics/LightmapSettings.h b/Runtime/Graphics/LightmapSettings.h
new file mode 100644
index 0000000..c8e8a65
--- /dev/null
+++ b/Runtime/Graphics/LightmapSettings.h
@@ -0,0 +1,101 @@
+#pragma once
+
+#include "Runtime/Math/Color.h"
+#include "Texture2D.h"
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Utilities/NonCopyable.h"
+#include "Runtime/Utilities/triple.h"
+#if UNITY_EDITOR
+#include "Editor/Src/LightmapEditorSettings.h"
+#endif
+#include "Runtime/Camera/LightProbes.h"
+
+class LightmapData
+{
+public:
+ PPtr<Texture2D> m_Lightmap;
+ PPtr<Texture2D> m_IndirectLightmap;
+ PPtr<Texture2D> m_ThirdLightmap;
+
+ DECLARE_SERIALIZE(LightmapData)
+};
+
+struct LightmapDataMono {
+ ScriptingObjectPtr m_Lightmap;
+ ScriptingObjectPtr m_IndirectLightmap;
+};
+void LightmapDataToMono (const LightmapData &src, LightmapDataMono &dest);
+void LightmapDataToCpp (LightmapDataMono &src, LightmapData &dest);
+
+class LightmapSettings : public LevelGameManager
+{
+public:
+ REGISTER_DERIVED_CLASS (LightmapSettings, LevelGameManager)
+ DECLARE_OBJECT_SERIALIZE(LightmapSettings)
+
+ // Lightmap index is stored as UInt8,
+ // 2 indices are reserved.
+ static const int kMaxLightmaps = 254;
+
+ LightmapSettings(MemLabelId label, ObjectCreationMode mode);
+
+ typedef triple<TextureID> TextureTriple;
+ TextureTriple GetLightmapTexture (UInt32 index) const
+ {
+ if (index < m_LightmapTextureCount)
+ return m_LightmapTextures[index];
+ else
+ return triple<TextureID>(TextureID(),TextureID(), TextureID());
+ }
+
+ const std::vector<LightmapData>& GetLightmaps () { return m_Lightmaps; }
+ void ClearLightmaps();
+ void SetLightmaps (const std::vector<LightmapData>& data);
+ void AppendLightmaps (const std::vector<LightmapData>& data);
+
+ void SetBakedColorSpace(ColorSpace colorSpace) { m_BakedColorSpace = (int)colorSpace; }
+ ColorSpace GetBakedColorSpace() { return (ColorSpace)m_BakedColorSpace; }
+
+ enum LightmapsMode {
+ kSingleLightmapsMode = 0,
+ kDualLightmapsMode = 1,
+ kDirectionalLightmapsMode = 2,
+ kRNMMode = 3,
+ kLightmapsModeCount // keep this last
+ };
+
+ static const char* const kLightmapsModeNames[kLightmapsModeCount];
+
+ GET_SET(int, LightmapsMode, m_LightmapsMode);
+ bool GetUseDualLightmapsInForward () const;
+
+ #if UNITY_EDITOR
+ LightmapEditorSettings& GetLightmapEditorSettings() { return m_LightmapEditorSettings; };
+ #endif
+
+ void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ void SetLightProbes (LightProbes* lightProbes);
+ LightProbes* GetLightProbes ();
+
+private:
+ void Rebuild();
+
+private:
+ TextureTriple* m_LightmapTextures;
+ int m_LightmapTextureCount;
+
+ PPtr<LightProbes> m_LightProbes;
+
+ std::vector<LightmapData> m_Lightmaps;
+ int m_LightmapsMode; // LightmapsMode
+ int m_BakedColorSpace;
+ bool m_UseDualLightmapsInForward;
+
+ #if UNITY_EDITOR
+ LightmapEditorSettings m_LightmapEditorSettings;
+ #endif
+};
+
+LightmapSettings& GetLightmapSettings ();
+
diff --git a/Runtime/Graphics/LowerResBlitTexture.h b/Runtime/Graphics/LowerResBlitTexture.h
new file mode 100644
index 0000000..b96af45
--- /dev/null
+++ b/Runtime/Graphics/LowerResBlitTexture.h
@@ -0,0 +1,61 @@
+#ifndef LowerResBlitTexture_h
+#define LowerResBlitTexture_h
+
+#include "Runtime/Graphics/Texture.h"
+#include "Runtime/GfxDevice/TextureIdMap.h"
+
+// This is used by the OSX/Linux/iOS standalones for scaling up the viewport to the screen resolution.
+class LowerResBlitTexture
+: public Texture
+{
+public:
+
+ LowerResBlitTexture(MemLabelId label, ObjectCreationMode mode) : Texture(label, mode) {}
+
+ unsigned w, h;
+
+ void Create(unsigned tex, unsigned w_, unsigned h_)
+ {
+ #if ENABLE_TEXTUREID_MAP
+ TextureIdMap::UpdateTexture(m_TexID, tex);
+ #else
+ m_TexID = TextureID(tex);
+ #endif
+
+
+
+ w = w_; h = h_;
+ m_TexelSizeX = 1.0f / w_;
+ m_TexelSizeY = 1.0f / h_;
+ }
+
+ virtual TextureDimension GetDimension () const { return kTexDim2D; }
+ virtual bool ExtractImage (ImageReference* /*image*/, int /*imageIndex*/) const { return false; }
+ virtual int GetStorageMemorySize() const { return 0; }
+
+ virtual int GetDataWidth() const { return w; }
+ virtual int GetDataHeight() const { return h; }
+
+ virtual bool HasMipMap () const { return false; }
+ virtual int CountMipmaps () const { return 1; }
+
+ virtual void UnloadFromGfxDevice(bool forceUnloadAll) {}
+ virtual void UploadToGfxDevice() {}
+ virtual void ApplySettings () {}
+};
+
+
+static LowerResBlitTexture* CreateBlitTexture()
+{
+ static LowerResBlitTexture* _BlitTex = 0;
+ if(_BlitTex == 0)
+ {
+ _BlitTex = CreateObjectFromCode<LowerResBlitTexture>();
+ _BlitTex->SetHideFlags(Object::kHideAndDontSave);
+ }
+
+ return _BlitTex;
+}
+
+
+#endif
diff --git a/Runtime/Graphics/MatrixStack.cpp b/Runtime/Graphics/MatrixStack.cpp
new file mode 100644
index 0000000..58e6f12
--- /dev/null
+++ b/Runtime/Graphics/MatrixStack.cpp
@@ -0,0 +1,72 @@
+#include "UnityPrefix.h"
+#include "MatrixStack.h"
+#include "Runtime/Math/Matrix4x4.h"
+
+void MatrixStack::Reset()
+{
+ m_Depth = 1;
+ m_Matrices[0].SetIdentity();
+}
+
+Matrix4x4f& MatrixStack::GetMatrix4x4f( int index )
+{
+ Assert(index >= 0 && index < m_Depth);
+ return m_Matrices[index];
+}
+
+void MatrixStack::Push()
+{
+ if( m_Depth >= kStackDepth )
+ {
+ ErrorString( "Matrix stack full depth reached" );
+ return;
+ }
+ ++m_Depth;
+ CopyMatrix (m_Matrices[m_Depth-2].GetPtr(), m_Matrices[m_Depth-1].GetPtr());
+}
+
+void MatrixStack::Push (const float matrix[16])
+{
+ if( m_Depth >= kStackDepth )
+ {
+ ErrorString( "Matrix stack full depth reached" );
+ return;
+ }
+ ++m_Depth;
+ CopyMatrix (matrix, m_Matrices[m_Depth-2].GetPtr());
+}
+
+void MatrixStack::Pop()
+{
+ if( m_Depth < 2 )
+ {
+ ErrorString( "Matrix stack empty" );
+ return;
+ }
+ --m_Depth;
+}
+
+void MatrixStack::SetMatrix (const float matrix[16])
+{
+ CopyMatrix (matrix, m_Matrices[m_Depth-1].GetPtr());
+}
+
+void MatrixStack::SetCurrentIdentity()
+{
+ m_Matrices[m_Depth-1].SetIdentity();
+}
+
+void MatrixStack::MultMatrix( const float matrix[16] )
+{
+ const Matrix4x4f& a = *reinterpret_cast<const Matrix4x4f*>(matrix);
+ Matrix4x4f& b = GetMatrix4x4f( m_Depth-1 );
+ Matrix4x4f c;
+ MultiplyMatrices4x4 (&b, &a, &c);
+ CopyMatrix (c.GetPtr(), b.GetPtr());
+}
+
+const Matrix4x4f& MatrixStack::GetMatrix() const
+{
+ Assert(m_Depth >= 1 && m_Depth <= kStackDepth);
+ return m_Matrices[m_Depth-1];
+}
diff --git a/Runtime/Graphics/MatrixStack.h b/Runtime/Graphics/MatrixStack.h
new file mode 100644
index 0000000..fe11a18
--- /dev/null
+++ b/Runtime/Graphics/MatrixStack.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "Runtime/Math/Matrix4x4.h"
+
+
+class MatrixStack
+{
+public:
+ enum { kStackDepth = 16 };
+
+public:
+ MatrixStack() { Reset(); }
+
+ void Reset();
+
+ void SetMatrix( const float matrix[16] );
+ void SetCurrentIdentity();
+ void MultMatrix( const float matrix[16] );
+ const Matrix4x4f& GetMatrix() const;
+
+ void Push(const float* matrix);
+ void Push();
+ void Pop();
+
+ int GetCurrentDepth() const { return m_Depth; }
+
+private:
+ Matrix4x4f& GetMatrix4x4f (int index);
+
+ Matrix4x4f m_Matrices[kStackDepth];
+ int m_Depth;
+};
diff --git a/Runtime/Graphics/ParticleSystem/Modules/ClampVelocityModule.cpp b/Runtime/Graphics/ParticleSystem/Modules/ClampVelocityModule.cpp
new file mode 100644
index 0000000..362fbff
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/ClampVelocityModule.cpp
@@ -0,0 +1,102 @@
+#include "UnityPrefix.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "ClampVelocityModule.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemUtils.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+inline float DampenOutsideLimit (float v, float limit, float dampen)
+{
+ float sgn = Sign (v);
+ float abs = Abs (v);
+ if (abs > limit)
+ abs = Lerp(abs, limit, dampen);
+ return abs * sgn;
+}
+
+ClampVelocityModule::ClampVelocityModule () : ParticleSystemModule(false)
+, m_SeparateAxis (false)
+, m_InWorldSpace (false)
+, m_Dampen (1.0f)
+{
+}
+
+template<ParticleSystemCurveEvalMode mode>
+void MagnitudeUpdateTpl(const MinMaxCurve& magnitude, ParticleSystemParticles& ps, size_t fromIndex, size_t toIndex, float dampen)
+{
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ {
+ float limit = Evaluate<mode> (magnitude, NormalizedTime(ps, q), GenerateRandom(ps.randomSeed[q] + kParticleSystemClampVelocityCurveId));
+ Vector3f vel = ps.velocity[q] + ps.animatedVelocity[q];
+ vel = NormalizeSafe (vel) * DampenOutsideLimit (Magnitude (vel), limit, dampen);
+ ps.velocity[q] = vel - ps.animatedVelocity[q];
+ }
+}
+
+void ClampVelocityModule::Update (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex)
+{
+ if (m_SeparateAxis)
+ {
+ Matrix4x4f matrix;
+ Matrix4x4f invMatrix;
+ bool transform;
+ if(IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ transform = GetTransformationMatrices(matrix, invMatrix, !roState.useLocalSpace, m_InWorldSpace, state.localToWorld);
+ else
+ transform = false; // Revert to old broken behavior
+
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ {
+ Vector3f random;
+ GenerateRandom3(random, ps.randomSeed[q] + kParticleSystemClampVelocityCurveId);
+ const float time = NormalizedTime(ps, q);
+
+ Vector3f vel = ps.velocity[q] + ps.animatedVelocity[q];
+ if(transform)
+ vel = matrix.MultiplyVector3 (vel);
+ const Vector3f limit (Evaluate (m_X, time, random.x), Evaluate (m_Y, time, random.y), Evaluate (m_Z, time, random.z));
+ vel.x = DampenOutsideLimit (vel.x, limit.x, m_Dampen);
+ vel.y = DampenOutsideLimit (vel.y, limit.y, m_Dampen);
+ vel.z = DampenOutsideLimit (vel.z, limit.z, m_Dampen);
+ vel = vel - ps.animatedVelocity[q];
+ if(transform)
+ vel = invMatrix.MultiplyVector3 (vel);
+ ps.velocity[q] = vel;
+ }
+ }
+ else
+ {
+ if(m_Magnitude.minMaxState == kMMCScalar)
+ MagnitudeUpdateTpl<kEMScalar> (m_Magnitude, ps, fromIndex, toIndex, m_Dampen);
+ else if(m_Magnitude.IsOptimized() && m_Magnitude.UsesMinMax())
+ MagnitudeUpdateTpl<kEMOptimizedMinMax> (m_Magnitude, ps, fromIndex, toIndex, m_Dampen);
+ else if(m_Magnitude.IsOptimized())
+ MagnitudeUpdateTpl<kEMOptimized> (m_Magnitude, ps, fromIndex, toIndex, m_Dampen);
+ else
+ MagnitudeUpdateTpl<kEMSlow> (m_Magnitude, ps, fromIndex, toIndex, m_Dampen);
+ }
+}
+
+void ClampVelocityModule::CheckConsistency ()
+{
+ m_Dampen = clamp<float> (m_Dampen, 0.0f, 1.0f);
+ m_X.SetScalar(std::max<float> (0.0f, m_X.GetScalar()));
+ m_Y.SetScalar(std::max<float> (0.0f, m_Y.GetScalar()));
+ m_Z.SetScalar(std::max<float> (0.0f, m_Z.GetScalar()));
+ m_Magnitude.SetScalar(std::max<float> (0.0f, m_Magnitude.GetScalar()));
+}
+
+
+template<class TransferFunction>
+void ClampVelocityModule::Transfer (TransferFunction& transfer)
+{
+ ParticleSystemModule::Transfer (transfer);
+ transfer.Transfer (m_X, "x");
+ transfer.Transfer (m_Y, "y");
+ transfer.Transfer (m_Z, "z");
+ transfer.Transfer (m_Magnitude, "magnitude");
+ transfer.Transfer (m_SeparateAxis, "separateAxis");
+ transfer.Transfer (m_InWorldSpace, "inWorldSpace"); transfer.Align ();
+ transfer.Transfer (m_Dampen, "dampen");
+}
+INSTANTIATE_TEMPLATE_TRANSFER(ClampVelocityModule)
diff --git a/Runtime/Graphics/ParticleSystem/Modules/ClampVelocityModule.h b/Runtime/Graphics/ParticleSystem/Modules/ClampVelocityModule.h
new file mode 100644
index 0000000..e315d61
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/ClampVelocityModule.h
@@ -0,0 +1,29 @@
+#ifndef SHURIKENMODULECLAMPVELOCITY_H
+#define SHURIKENMODULECLAMPVELOCITY_H
+
+#include "ParticleSystemModule.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemCurves.h"
+
+class ClampVelocityModule : public ParticleSystemModule
+{
+public:
+ DECLARE_MODULE (ClampVelocityModule)
+ ClampVelocityModule ();
+
+ void Update (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex);
+ void CheckConsistency ();
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+private:
+ MinMaxCurve m_X;
+ MinMaxCurve m_Y;
+ MinMaxCurve m_Z;
+ MinMaxCurve m_Magnitude;
+ bool m_InWorldSpace;
+ bool m_SeparateAxis;
+ float m_Dampen;
+};
+
+#endif // SHURIKENMODULECLAMPVELOCITY_H
diff --git a/Runtime/Graphics/ParticleSystem/Modules/CollisionModule.cpp b/Runtime/Graphics/ParticleSystem/Modules/CollisionModule.cpp
new file mode 100644
index 0000000..9e16bde
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/CollisionModule.cpp
@@ -0,0 +1,603 @@
+#include "UnityPrefix.h"
+#include <float.h>
+#include "CollisionModule.h"
+
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemCurves.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemParticle.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemUtils.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Interfaces/IRaycast.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+#include "Editor/Src/Utility/DebugPrimitives.h"
+
+struct ParticleSystemCollisionParameters
+{
+ float bounceFactor;
+ float energyLossOnCollision;
+ float minKillSpeedSqr;
+ float particleRadius;
+ float dampen;
+
+ PlaneColliderCache* planeColliderCache;
+ IRaycast* raycaster;
+ size_t rayBudget;
+ size_t nextParticleToTrace;
+ float voxelSize;
+};
+
+struct ColliderInfo
+{
+ Plane m_CollisionPlane;
+ bool m_Traced;
+ int m_ColliderInstanceID;
+ int m_RigidBodyOrColliderInstanceID;
+};
+
+struct CollisionInfo
+{
+ CollisionInfo():m_NumWorldCollisions(0),m_NumCachedCollisions(0),m_NumPlaneCollisions(0),m_Colliders(NULL) {}
+
+ size_t m_NumWorldCollisions;
+ size_t m_NumCachedCollisions;
+ size_t m_NumPlaneCollisions;
+ ColliderInfo* m_Colliders;
+
+ size_t AllCollisions() const { return m_NumWorldCollisions+m_NumCachedCollisions+m_NumPlaneCollisions; }
+};
+
+/// @TODO: Why does Vector3f has a constructor? WTF.
+inline void CalculateCollisionResponse(const ParticleSystemReadOnlyState& roState,
+ ParticleSystemState& state,
+ ParticleSystemParticles& ps,
+ const size_t q,
+ const ParticleSystemCollisionParameters& params,
+ const Vector3f& position,
+ const Vector3f& velocity,
+ const HitInfo& hitInfo)
+{
+ // Reflect + dampen
+ Vector3f positionOffset = ReflectVector (position - hitInfo.intersection, hitInfo.normal) * params.dampen;
+ Vector3f newVelocity = ReflectVector(velocity, hitInfo.normal) * params.dampen;
+
+ // Apply bounce
+ positionOffset -= hitInfo.normal * (Dot(positionOffset, hitInfo.normal)) * params.bounceFactor;
+ newVelocity -= hitInfo.normal * Dot(newVelocity, hitInfo.normal) * params.bounceFactor;
+
+ ps.position[q] = hitInfo.intersection + positionOffset;
+ ps.velocity[q] = newVelocity - ps.animatedVelocity[q];
+
+ for(int s = 0; s < state.numCachedSubDataCollision; s++)
+ {
+ ParticleSystemEmissionState emissionState;
+ RecordEmit(emissionState, state.cachedSubDataCollision[s], roState, state, ps, kParticleSystemSubTypeCollision, s, q, 0.0f, 0.0001f, 1.0f);
+ }
+ ps.lifetime[q] -= params.energyLossOnCollision * ps.startLifetime[q];
+
+ if (ps.GetUsesCollisionEvents () && !(hitInfo.colliderInstanceID == 0))
+ {
+ Vector3f wcIntersection = hitInfo.intersection;
+ Vector3f wcNormal = hitInfo.normal;
+ Vector3f wcVelocity = velocity;
+ if ( roState.useLocalSpace )
+ {
+ wcIntersection = state.localToWorld.MultiplyPoint3 (wcIntersection);
+ wcNormal = state.localToWorld.MultiplyVector3 (wcNormal);
+ wcVelocity = state.localToWorld.MultiplyVector3 (wcVelocity);
+ }
+ ps.collisionEvents.AddEvent (ParticleCollisionEvent (wcIntersection, wcNormal, wcVelocity, hitInfo.colliderInstanceID, hitInfo.rigidBodyOrColliderInstanceID));
+ //printf_console (Format ("Intersected '%s' -> id: %d -> go id: %d.\n", hitInfo.collider->GetName (), hitInfo.collider->GetInstanceID (), hitInfo.collider->GetGameObject ().GetInstanceID ()).c_str ());
+}
+}
+
+CollisionModule::CollisionModule () : ParticleSystemModule(false)
+, m_Type(0)
+, m_Dampen(0.0f)
+, m_Bounce(1.0f)
+, m_EnergyLossOnCollision(0.0f)
+, m_MinKillSpeed(0.0f)
+, m_ParticleRadius(0.01f)
+, m_Quality(0)
+, m_VoxelSize(0.5f)
+, m_CollisionMessages(false)
+{
+ m_CollidesWith.m_Bits = 0xFFFFFFFF;
+}
+
+void CollisionModule::AllocateAndCache(const ParticleSystemReadOnlyState& roState, ParticleSystemState& state)
+{
+ Assert(!state.cachedCollisionPlanes);
+
+ Matrix4x4f matrix = Matrix4x4f::identity;
+ if(roState.useLocalSpace)
+ matrix = state.worldToLocal;
+
+ state.numCachedCollisionPlanes = 0;
+ for (unsigned i=0; i<kMaxNumPrimitives; ++i)
+ {
+ if (m_Primitives[i].IsNull ())
+ continue;
+ state.numCachedCollisionPlanes++;
+ }
+
+ state.cachedCollisionPlanes = ALLOC_TEMP_MANUAL(Plane, state.numCachedCollisionPlanes);
+
+ int planeCount = 0;
+
+ for (unsigned i=0; i<kMaxNumPrimitives; ++i)
+ {
+ if (m_Primitives[i].IsNull ())
+ continue;
+
+ const Transform* transform = m_Primitives[i]->QueryComponent(Transform);
+ const Vector3f position = matrix.MultiplyPoint3(transform->GetPosition());
+
+ Assert(planeCount <= state.numCachedCollisionPlanes);
+ const Vector3f normal = matrix.MultiplyVector3(RotateVectorByQuat (transform->GetRotation (), Vector3f::yAxis));
+ state.cachedCollisionPlanes[planeCount].SetNormalAndPosition (normal, position);
+ state.cachedCollisionPlanes[planeCount].NormalizeRobust();
+ planeCount++;
+ }
+}
+
+void CollisionModule::FreeCache(ParticleSystemState& state)
+{
+ if(state.cachedCollisionPlanes)
+ {
+ FREE_TEMP_MANUAL(state.cachedCollisionPlanes);
+ state.cachedCollisionPlanes = 0;
+ state.numCachedCollisionPlanes = 0;
+ }
+}
+
+// read the plane cache for a given range
+size_t ReadCache(const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, const ParticleSystemParticles& ps, const ParticleSystemCollisionParameters& params, CollisionInfo& collision, const size_t fromIndex, const size_t& toIndex, const float dt)
+{
+ size_t numIntersections = 0;
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ {
+ // initialise plane to value that guarantees no intersection
+ collision.m_Colliders[q].m_CollisionPlane.distance = FLT_MAX;
+ collision.m_Colliders[q].m_Traced = false;
+ collision.m_Colliders[q].m_ColliderInstanceID = -1;
+ collision.m_Colliders[q].m_RigidBodyOrColliderInstanceID = -1;
+
+ // build start/end points
+ Vector3f to = ps.position[q];
+ const Vector3f v = ps.velocity[q] + ps.animatedVelocity[q];
+ const Vector3f d = v * dt;
+ Vector3f from = to - d;
+ // convert to WC
+ if ( roState.useLocalSpace )
+ {
+ from = state.localToWorld.MultiplyPoint3(from);
+ to = state.localToWorld.MultiplyPoint3(to);
+ }
+
+ // lookup the cache
+ Plane plane;
+ int colliderInstanceID;
+ int rigidBodyOrColliderInstanceID;
+ if ( params.planeColliderCache->Find (from, to-from, plane, colliderInstanceID, rigidBodyOrColliderInstanceID, params.voxelSize) )
+ {
+ collision.m_Colliders[q].m_CollisionPlane = plane;
+ collision.m_Colliders[q].m_ColliderInstanceID = colliderInstanceID;
+ collision.m_Colliders[q].m_RigidBodyOrColliderInstanceID = rigidBodyOrColliderInstanceID;
+ numIntersections++;
+ }
+ }
+ return numIntersections;
+}
+
+// Perform ray casts. Ray casts are done in WC and results are transformed back into sim space if necessary.
+// There are three sets of indices:
+// [fromIndex ; params.nextParticleToTrace[ for these we do caching if the cache is available
+// [params.nextParticleToTrace ; params.nextParticleToTrace + params.rayBudget[ for these we trace fresh rays
+// [params.nextParticleToTrace + params.rayBudget ; toIndex[ for these we do caching if the cache is available
+CollisionInfo WorldCollision(const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, const ParticleSystemParticles& ps, const ParticleSystemCollisionParameters& params, const size_t fromIndex, const int filter, const float dt)
+{
+ CollisionInfo collisionCounter;
+ const bool approximate = ( params.planeColliderCache ? true : false );
+ size_t numIntersections = 0;
+
+ const size_t toIndex = ps.array_size();
+
+ // pre range that is cached
+ const size_t traceRangeFrom = approximate ? params.nextParticleToTrace : fromIndex;
+ const size_t traceRangeTo = approximate ? std::min(params.nextParticleToTrace+params.rayBudget, toIndex) : toIndex;
+
+ const size_t fromIndex0 = fromIndex;
+ const size_t toIndex0 = traceRangeFrom;
+ // range to actually ray trace
+ const size_t fromIndex1 = traceRangeFrom;
+ const size_t toIndex1 = traceRangeTo;
+ // post range that is cached
+ const size_t fromIndex2 = traceRangeTo;
+ const size_t toIndex2 = toIndex;
+
+ collisionCounter.m_Colliders = ALLOC_TEMP_MANUAL(ColliderInfo, ps.array_size());
+ collisionCounter.m_NumCachedCollisions = (toIndex0-fromIndex0)+(toIndex2-fromIndex2);
+ collisionCounter.m_NumWorldCollisions = toIndex1-fromIndex1;
+
+ if ( toIndex1-fromIndex1 > 0 )
+ {
+ // batch trace selected range
+ dynamic_array< BatchedRaycast > rayBatch(toIndex1-fromIndex1,kMemTempAlloc);
+ dynamic_array< BatchedRaycastResult > rayResults(toIndex1-fromIndex1,kMemTempAlloc);
+ // build request array
+ size_t i = 0;
+ for (size_t q = fromIndex1; q < toIndex1; ++q, ++i)
+ {
+ // build start/end points
+ const Vector3f to = ps.position[q];
+ const Vector3f v = ps.velocity[q] + ps.animatedVelocity[q];
+ const Vector3f d = v * dt;
+ const Vector3f from = to - d;
+
+ if ( approximate )
+ {
+ // extend ray to trace across the entire voxel (and then some)
+ const float voxSize = std::max(params.voxelSize,params.voxelSize*kVoxelHeightMultiplier);
+ const Vector3f displacement = to-from;
+
+ ///@TODO: handle magnitude 0. Should we skip the raycast???
+ const Vector3f direction = NormalizeFast(displacement);
+ const Vector3f extendedTo = from + direction * voxSize;
+
+ // insert into batch
+ rayBatch[i] = BatchedRaycast(q,from,extendedTo);
+ }
+ else
+ rayBatch[i] = BatchedRaycast(q,from,to);
+
+ // initialise plane to value that guarantees no intersection
+ collisionCounter.m_Colliders[q].m_CollisionPlane.distance = FLT_MAX;
+ collisionCounter.m_Colliders[q].m_Traced = (!approximate);
+ collisionCounter.m_Colliders[q].m_ColliderInstanceID = -1;
+ collisionCounter.m_Colliders[q].m_RigidBodyOrColliderInstanceID = -1;
+ }
+ // convert to WC
+ if ( roState.useLocalSpace )
+ {
+ const Matrix4x4f m = state.localToWorld;
+ for (size_t i = 0; i < rayBatch.size(); ++i)
+ {
+ rayBatch[i].from = m.MultiplyPoint3(rayBatch[i].from);
+ rayBatch[i].to = m.MultiplyPoint3(rayBatch[i].to);
+ }
+ }
+ // trace the rays
+ const size_t numIx = params.raycaster->BatchIntersect( rayBatch, rayResults, filter, approximate );
+
+ // convert back to local space
+ if ( roState.useLocalSpace )
+ {
+ const Matrix4x4f m = state.worldToLocal;
+ for (size_t i = 0; i < numIx; ++i)
+ {
+ // the plane intersection was computed in WC, transform intersection to local space.
+ rayResults[i].hitInfo.intersection = m.MultiplyPoint3( rayResults[i].hitInfo.intersection );
+ rayResults[i].hitInfo.normal = m.MultiplyVector3( rayResults[i].hitInfo.normal );
+ }
+ }
+
+ // store planes in the particles that intersected something
+ for (size_t i = 0; i < numIx; ++i)
+ {
+ const size_t q = rayBatch[rayResults[i].index].index;
+ collisionCounter.m_Colliders[q].m_CollisionPlane.normal = rayResults[i].hitInfo.normal;
+ collisionCounter.m_Colliders[q].m_CollisionPlane.distance = -Dot(rayResults[i].hitInfo.intersection, rayResults[i].hitInfo.normal);
+ collisionCounter.m_Colliders[q].m_ColliderInstanceID = rayResults[i].hitInfo.colliderInstanceID;
+ collisionCounter.m_Colliders[q].m_RigidBodyOrColliderInstanceID = rayResults[i].hitInfo.rigidBodyOrColliderInstanceID;
+ }
+
+ // store intersections in cache
+ if (approximate)
+ {
+ for (size_t i = 0; i < numIx; ++i)
+ {
+ const size_t r = rayResults[i].index;
+ const size_t q = rayBatch[rayResults[i].index].index;
+ params.planeColliderCache->Replace (rayBatch[r].from, rayBatch[r].to-rayBatch[r].from, Plane(collisionCounter.m_Colliders[q].m_CollisionPlane), collisionCounter.m_Colliders[q].m_ColliderInstanceID, collisionCounter.m_Colliders[q].m_RigidBodyOrColliderInstanceID, params.voxelSize);
+ }
+ }
+ numIntersections += numIx;
+ }
+
+ // process pre cache range
+ if ( toIndex0-fromIndex0 > 0 )
+ {
+ numIntersections += ReadCache(roState, state, ps, params, collisionCounter, fromIndex0, toIndex0, dt);
+ }
+ // process post cache range
+ if ( toIndex2-fromIndex2 > 0 )
+ {
+ numIntersections += ReadCache(roState, state, ps, params, collisionCounter, fromIndex2, toIndex2, dt);
+ }
+
+ return collisionCounter;
+}
+
+
+// Plane collide all particles in simulation space (remember the cached planes are defined in sim space).
+CollisionInfo PlaneCollision(const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, const ParticleSystemParticles& ps, const ParticleSystemCollisionParameters& params, const int fromIndex, const float dt)
+{
+ CollisionInfo collisionInfo;
+ collisionInfo.m_Colliders = ALLOC_TEMP_MANUAL(ColliderInfo, ps.array_size());
+
+ const bool newBehaviour = IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1);
+
+ size_t toIndex = ps.array_size();
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ {
+ // initialise to value that guarantees no intersection
+ collisionInfo.m_Colliders[q].m_CollisionPlane.distance = FLT_MAX;
+
+ const Vector3f position = ps.position[q];
+ const Vector3f velocity = ps.velocity[q] + ps.animatedVelocity[q];
+ const Vector3f displacement = velocity * dt;
+ const Vector3f origin = position - displacement;
+
+ // walk the planes
+ for (unsigned i=0; i<state.numCachedCollisionPlanes; ++i)
+ {
+ const Plane plane = state.cachedCollisionPlanes[i];
+
+ if ( newBehaviour )
+ {
+ // new behaviour:
+ // plane collisions are single sided only, all particles 'behind' the plane will be forced onto the plane
+ const float dist = plane.GetDistanceToPoint(position);
+ if (dist > params.particleRadius)
+ continue;
+ }
+ else
+ {
+ // old (but fixed) behaviour, particles can collide with both front and back plane
+ const float d0 = plane.GetDistanceToPoint(origin);
+ const float d1 = plane.GetDistanceToPoint(position);
+ const bool sameSide = ( (d0 > 0.0f && d1 > 0.0f) || (d0 <= 0.0f && d1 <= 0.0f) );
+ const float aD0 = Abs(d0);
+ const float aD1 = Abs(d1);
+ if ( sameSide && aD0 > params.particleRadius && aD1 > params.particleRadius )
+ continue;
+ }
+
+ collisionInfo.m_Colliders[q].m_CollisionPlane = plane;
+ collisionInfo.m_Colliders[q].m_ColliderInstanceID = 0;
+ collisionInfo.m_Colliders[q].m_RigidBodyOrColliderInstanceID = 0;
+ collisionInfo.m_NumPlaneCollisions++;
+ break;
+ }
+ }
+
+ return collisionInfo;
+}
+
+// Compute the collision plane to use for each particle
+CollisionInfo UpdateCollisionPlanes(bool worldCollision, UInt32 collisionFilter, const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, const ParticleSystemCollisionParameters& params, ParticleSystemParticles& ps, size_t fromIndex, float dt)
+{
+ if( worldCollision )
+ {
+ // Check if we support raycasting
+ if (params.raycaster == NULL)
+ return CollisionInfo();
+
+ ////@TODO: dtPerParticle
+ return WorldCollision(roState, state, ps, params, fromIndex, collisionFilter, dt);
+ }
+ else
+ {
+ ////@TODO: dtPerParticle
+ return PlaneCollision(roState, state, ps, params, fromIndex, dt);
+ }
+}
+
+// Collide the particles against their selected planes and update collision response - this is done purely in simulation space
+static const float kEpsilon = 0.000001f;
+static const float kRayEpsilon = 0.00001f;
+void PerformPlaneCollisions(bool worldCollision, const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& ps, const ParticleSystemCollisionParameters& params, const CollisionInfo& collisionInfo, const int fromIndex, const float dt)
+{
+ // for world collisions we use and epsilon but for plane intersections we use the particle radius
+ const float radius = worldCollision ? kRayEpsilon : params.particleRadius;
+ const bool newBehaviour = IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1);
+ //const bool approximate = ( params.planeColliderCache ? true : false );
+
+ // collide with plane
+ size_t toIndex = ps.array_size();
+
+ for (size_t q = fromIndex; q < toIndex; q++)
+ {
+ // check if the actual ray was traced in which case we want to disable the ray testing. Note: does not apply to approximate mode as we trace longer rays to improve cache quality
+ const bool tracedParticle = collisionInfo.m_Colliders[q].m_Traced;
+ const Plane plane = collisionInfo.m_Colliders[q].m_CollisionPlane;
+ if ( plane.distance != FLT_MAX )
+ {
+ const Vector3f position = ps.position[q];
+ const Vector3f velocity = ps.velocity[q] + ps.animatedVelocity[q];
+ const Vector3f displacement = velocity * dt;
+ const Vector3f origin = position - displacement;
+
+ HitInfo hit;
+ hit.normal = plane.GetNormal();
+ hit.colliderInstanceID = collisionInfo.m_Colliders[q].m_ColliderInstanceID;
+ hit.rigidBodyOrColliderInstanceID = collisionInfo.m_Colliders[q].m_RigidBodyOrColliderInstanceID;
+
+ if ( worldCollision )
+ {
+ // plane ray dot product
+ const float VdN = Dot( plane.GetNormal(), displacement );
+ if ( !tracedParticle && VdN >= 0 )
+ continue; // only pick up front face intersections
+
+ // Recreate hit point.
+ const float t = -( Dot( origin, plane.GetNormal() ) + plane.distance ) / VdN;
+ if ( !tracedParticle && (t < 0 || t > 1) )
+ continue;
+
+ // build intersection description from t value and ray
+ hit.intersection = origin + displacement * t;
+
+ // Adjust intersection along normal to make sure the particle doesn't fall through geometry when it comes to rest.
+ // This is also an issue when dampen and bounce is zero in which case CalculateCollisionResponse will set the particle
+ // position to *exactly* the intersection point, which will then have issues next time the intersection is executed
+ // where it will try and compare two floating point numbers which are equal and the intersection test will come out
+ // either way depending on fp accuracy
+ //
+ // for world collisions we use and epsilon but for plane intersections we use the particle radius
+ hit.intersection += radius * hit.normal;
+ }
+ else
+ {
+ if (newBehaviour)
+ {
+ const float dist = plane.GetDistanceToPoint(position);
+
+ if (dist > radius)
+ continue;
+
+ const float VdN = Dot( plane.GetNormal(), velocity );
+ if (VdN == 0.0F || VdN == -0.0F)
+ continue;
+
+ const float t = -( Dot( position, plane.GetNormal() ) + plane.distance - radius ) / VdN;
+
+ hit.intersection = position + velocity * t;
+ }
+ else
+ {
+ const float OriginDotPlane = Dot (plane.GetNormal(), origin);
+ const float signedDistanceToOrigin = OriginDotPlane + plane.distance;
+ const float PositionDotPlane = Dot (plane.GetNormal(), position);
+ const float signedDistanceToPosition = PositionDotPlane + plane.distance;
+ const bool originInside = Abs(signedDistanceToOrigin)<radius;
+ const bool positionInside = Abs(signedDistanceToPosition)<=radius;
+ const bool oppositeSide = !( (signedDistanceToOrigin > 0.0f && signedDistanceToPosition > 0.0f) || (signedDistanceToOrigin <= 0.0f && signedDistanceToPosition <= 0.0f) );
+
+ // if the points are both inside or outside the radius we can bail if they're not on opposite sides outside the radius
+ if ( originInside==positionInside && !(oppositeSide && !originInside && !positionInside ) )
+ continue;
+
+ // compute the side of the face we are on - this determines if we are trying to intersect with the front or back face of the plane
+ const float signOrigin = signedDistanceToOrigin < 0.0 ? -1.f : 1.f;
+ const float signedRadius = radius*signOrigin;
+
+ // check the direction of the ray is opposite to the plane normal (the sign flips the normal as appropriate)
+ const float VdN = Dot( plane.GetNormal(), velocity );
+ if (VdN*signOrigin >= 0)
+ continue;
+
+ // calculate intersection point
+ const float t = -( signedDistanceToPosition - signedRadius ) / VdN;
+ hit.intersection = position + velocity * t;
+ }
+ }
+
+ // compute the bounce
+ CalculateCollisionResponse(roState, state, ps, q, params, ps.position[q], ps.velocity[q] + ps.animatedVelocity[q], hit);
+
+ // Kill particle?
+ const float speedSqr = SqrMagnitude(ps.velocity[q] + ps.animatedVelocity[q]);
+ if (ps.lifetime[q] < 0.0f || speedSqr < params.minKillSpeedSqr)
+ {
+ // when killing a particle the last element from the array is pulled into the position of the killed particle and toIndex is updated accordingly
+ // we do a q-- in order to make sure the new particle at q is also collided
+ collisionInfo.m_Colliders[q] = collisionInfo.m_Colliders[toIndex-1];
+ KillParticle(roState, state, ps, q, toIndex);
+ q--;
+ }
+ }
+ }
+
+ // resize array to account for killed particles
+ ps.array_resize(toIndex);
+}
+
+// Update collision for the particle system.
+void CollisionModule::Update (const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& ps, size_t fromIndex, float dt )
+{
+ // any work todo?
+ if (fromIndex == ps.array_size())
+ return;
+
+ ps.SetUsesCollisionEvents (m_CollisionMessages);
+
+ // setup params
+ ParticleSystemCollisionParameters params;
+ params.bounceFactor = 1.0f - m_Bounce;
+ params.energyLossOnCollision = m_EnergyLossOnCollision;
+ params.minKillSpeedSqr = m_MinKillSpeed * m_MinKillSpeed;
+ params.particleRadius = m_ParticleRadius;
+ params.dampen = 1.0f - m_Dampen;
+ params.planeColliderCache = ( IsApproximate() ? &m_ColliderCache : NULL );
+ params.raycaster = GetRaycastInterface();
+ params.rayBudget = state.rayBudget;
+ params.voxelSize = m_VoxelSize;
+ params.nextParticleToTrace = ( state.nextParticleToTrace >= ps.array_size() ? fromIndex : std::max(state.nextParticleToTrace,fromIndex) );
+
+ // update particle collision planes
+ const CollisionInfo collisionCounter = UpdateCollisionPlanes(m_Type != kPlaneCollision, m_CollidesWith.m_Bits, roState, state, params, ps, fromIndex, dt);
+
+ // update collider index
+ state.nextParticleToTrace = params.nextParticleToTrace + params.rayBudget;
+
+ // decrement ray budget
+ state.rayBudget = (state.rayBudget>collisionCounter.m_NumWorldCollisions ? state.rayBudget-collisionCounter.m_NumWorldCollisions : 0);
+
+ // early out if there were no collisions at all
+ if ( collisionCounter.AllCollisions() <= 0 )
+ {
+ FREE_TEMP_MANUAL(collisionCounter.m_Colliders);
+ return;
+ }
+
+ // perform plane collisions
+ PerformPlaneCollisions(m_Type != kPlaneCollision, roState, state, ps, params, collisionCounter, fromIndex, dt);
+
+ FREE_TEMP_MANUAL(collisionCounter.m_Colliders);
+
+ if (ps.GetUsesCollisionEvents ())
+ {
+ ps.collisionEvents.SortCollisionEventThreadArray ();
+ }
+}
+
+void CollisionModule::CheckConsistency ()
+{
+ m_Dampen = clamp<float> (m_Dampen, 0.0f, 1.0f);
+ m_Bounce = clamp<float> (m_Bounce, 0.0f, 2.0f);
+ m_EnergyLossOnCollision = clamp<float> (m_EnergyLossOnCollision, 0.0f, 1.0f);
+ m_ParticleRadius = max<float>(m_ParticleRadius, 0.01f);
+}
+
+template<class TransferFunction>
+void CollisionModule::Transfer (TransferFunction& transfer)
+{
+ ParticleSystemModule::Transfer (transfer);
+ transfer.Transfer (m_Type, "type");
+
+ const char* kPrimitiveNames [kMaxNumPrimitives] = { "plane0", "plane1", "plane2", "plane3", "plane4", "plane5"};
+ for(int i = 0; i < kMaxNumPrimitives; i++)
+ transfer.Transfer (m_Primitives[i], kPrimitiveNames[i]);
+
+ transfer.Transfer (m_Dampen, "dampen");
+ transfer.Transfer (m_Bounce, "bounce");
+ transfer.Transfer (m_EnergyLossOnCollision, "energyLossOnCollision");
+ transfer.Transfer (m_MinKillSpeed, "minKillSpeed");
+ transfer.Transfer (m_ParticleRadius, "particleRadius");
+ transfer.Align();
+ transfer.Transfer (m_CollidesWith, "collidesWith");
+ transfer.Transfer (m_Quality, "quality");
+ transfer.Align();
+ transfer.Transfer (m_VoxelSize, "voxelSize");
+ transfer.Transfer (m_CollisionMessages, "collisionMessages");
+}
+
+INSTANTIATE_TEMPLATE_TRANSFER(CollisionModule)
diff --git a/Runtime/Graphics/ParticleSystem/Modules/CollisionModule.h b/Runtime/Graphics/ParticleSystem/Modules/CollisionModule.h
new file mode 100644
index 0000000..77b5541
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/CollisionModule.h
@@ -0,0 +1,62 @@
+#pragma once
+#include "ParticleSystemModule.h"
+#include "Runtime/BaseClasses/BitField.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/Utilities/SpatialHash.h"
+
+struct ParticleSystemParticles;
+class Transform;
+
+class CollisionModule : public ParticleSystemModule
+{
+public:
+ DECLARE_MODULE (CollisionModule)
+ CollisionModule ();
+
+ void AllocateAndCache(const ParticleSystemReadOnlyState& roState, ParticleSystemState& state);
+ static void FreeCache(ParticleSystemState& state);
+
+#if UNITY_EDITOR
+ float GetEnergyLoss() const { return m_EnergyLossOnCollision; }
+ void SetEnergyLoss(float value) { m_EnergyLossOnCollision = value; };
+ float GetMinKillSpeed() const { return m_MinKillSpeed; }
+ void SetMinKillSpeed(float value) { m_MinKillSpeed = value; };
+#endif
+
+ bool GetUsesCollisionMessages () const {return m_CollisionMessages;}
+ bool IsWorldCollision() const {return m_Type==kWorldCollision;}
+ bool IsApproximate() const {return m_Quality>0;}
+ int GetQuality() const {return m_Quality;}
+
+ void Update (const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& ps, size_t fromIndex, float dt);
+
+ void CheckConsistency ();
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+private:
+ void ClearPrimitives();
+
+ enum { kPlaneCollision, kWorldCollision };
+ enum { kMaxNumPrimitives = 6 };
+
+ PlaneColliderCache m_ColliderCache;
+
+ // Serialized
+ int m_Type;
+ float m_Dampen;
+ float m_Bounce;
+ float m_EnergyLossOnCollision;
+ float m_MinKillSpeed;
+ float m_ParticleRadius;
+ /// Collides the particles with every collider whose layerMask & m_CollidesWith != 0
+ BitField m_CollidesWith;
+ /// Perform approximate world particle collisions
+ int m_Quality; // selected quality, 0 is high (no approximations), 1 is medium (approximate), 2 is low (approximate)
+ float m_VoxelSize;
+ bool m_CollisionMessages;
+ PPtr<Transform> m_Primitives [kMaxNumPrimitives];
+};
+
diff --git a/Runtime/Graphics/ParticleSystem/Modules/ColorByVelocityModule.cpp b/Runtime/Graphics/ParticleSystem/Modules/ColorByVelocityModule.cpp
new file mode 100644
index 0000000..0437322
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/ColorByVelocityModule.cpp
@@ -0,0 +1,60 @@
+#include "UnityPrefix.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "ColorByVelocityModule.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemUtils.h"
+
+template<MinMaxGradientEvalMode mode>
+void UpdateTpl(const ParticleSystemParticles& ps, ColorRGBA32* colorTemp, const MinMaxGradient& gradient, const OptimizedMinMaxGradient& optGradient, const Vector2f offsetScale, size_t fromIndex, size_t toIndex)
+{
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ {
+ Vector3f vel = ps.velocity[q] + ps.animatedVelocity[q];
+ const float time = InverseLerpFast01 (offsetScale, Magnitude(vel));
+ const int random = GenerateRandomByte(ps.randomSeed[q] + kParticleSystemColorByVelocityGradientId);
+
+ ColorRGBA32 value;
+ if(mode == kGEMGradient)
+ value = EvaluateGradient (optGradient, time);
+ else if(mode == kGEMGradientMinMax)
+ value = EvaluateRandomGradient (optGradient, time, random);
+ else
+ value = Evaluate (gradient, time, random);
+
+ colorTemp[q] *= value;
+ }
+}
+
+ColorBySpeedModule::ColorBySpeedModule () : ParticleSystemModule(false)
+, m_Range (0.0f, 1.0f)
+{}
+
+void ColorBySpeedModule::Update (const ParticleSystemParticles& ps, ColorRGBA32* colorTemp, size_t fromIndex, size_t toIndex)
+{
+ DebugAssert(toIndex <= ps.array_size());
+
+ Vector2f offsetScale = CalculateInverseLerpOffsetScale (m_Range);
+ OptimizedMinMaxGradient gradient;
+ m_Gradient.InitializeOptimized(gradient);
+ if(m_Gradient.minMaxState == kMMGGradient)
+ UpdateTpl<kGEMGradient>(ps, colorTemp, m_Gradient, gradient, offsetScale, fromIndex, toIndex);
+ else if(m_Gradient.minMaxState == kMMGRandomBetweenTwoGradients)
+ UpdateTpl<kGEMGradientMinMax>(ps, colorTemp, m_Gradient, gradient, offsetScale, fromIndex, toIndex);
+ else
+ UpdateTpl<kGEMSlow>(ps, colorTemp, m_Gradient, gradient, offsetScale, fromIndex, toIndex);
+}
+
+void ColorBySpeedModule::CheckConsistency ()
+{
+ const float MyEpsilon = 0.001f;
+ m_Range.x = std::min (m_Range.x, m_Range.y - MyEpsilon);
+}
+
+template<class TransferFunction>
+void ColorBySpeedModule::Transfer (TransferFunction& transfer)
+{
+ ParticleSystemModule::Transfer (transfer);
+ transfer.Transfer (m_Gradient, "gradient");
+ transfer.Transfer (m_Range, "range");
+}
+INSTANTIATE_TEMPLATE_TRANSFER(ColorBySpeedModule)
diff --git a/Runtime/Graphics/ParticleSystem/Modules/ColorByVelocityModule.h b/Runtime/Graphics/ParticleSystem/Modules/ColorByVelocityModule.h
new file mode 100644
index 0000000..f140e68
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/ColorByVelocityModule.h
@@ -0,0 +1,27 @@
+#ifndef SHURIKENMODULECOLORBYVELOCITY_H
+#define SHURIKENMODULECOLORBYVELOCITY_H
+
+#include "ParticleSystemModule.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemGradients.h"
+#include "Runtime/Math/Vector2.h"
+
+class ColorBySpeedModule : public ParticleSystemModule
+{
+public:
+ DECLARE_MODULE (ColorBySpeedModule)
+ ColorBySpeedModule ();
+
+ void Update (const ParticleSystemParticles& ps, ColorRGBA32* colorTemp, size_t fromIndex, size_t toIndex);
+ void CheckConsistency ();
+
+ inline MinMaxGradient& GetGradient() { return m_Gradient; };
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+private:
+ MinMaxGradient m_Gradient;
+ Vector2f m_Range;
+};
+
+#endif // SHURIKENMODULECOLORBYVELOCITY_H
diff --git a/Runtime/Graphics/ParticleSystem/Modules/ColorModule.cpp b/Runtime/Graphics/ParticleSystem/Modules/ColorModule.cpp
new file mode 100644
index 0000000..c9e78c0
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/ColorModule.cpp
@@ -0,0 +1,51 @@
+#include "UnityPrefix.h"
+#include "ColorModule.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemUtils.h"
+
+template<MinMaxGradientEvalMode mode>
+void UpdateTpl(const ParticleSystemParticles& ps, ColorRGBA32* colorTemp, const MinMaxGradient& gradient, const OptimizedMinMaxGradient& optGradient, size_t fromIndex, size_t toIndex)
+{
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ {
+ const float time = NormalizedTime(ps, q);
+ const int random = GenerateRandomByte(ps.randomSeed[q] + kParticleSystemColorGradientId);
+
+ ColorRGBA32 value;
+ if(mode == kGEMGradient)
+ value = EvaluateGradient (optGradient, time);
+ else if(mode == kGEMGradientMinMax)
+ value = EvaluateRandomGradient (optGradient, time, random);
+ else
+ value = Evaluate (gradient, time, random);
+
+ colorTemp[q] *= value;
+ }
+}
+
+ColorModule::ColorModule () : ParticleSystemModule(false)
+{}
+
+void ColorModule::Update (const ParticleSystemParticles& ps, ColorRGBA32* colorTemp, size_t fromIndex, size_t toIndex)
+{
+ DebugAssert(toIndex <= ps.array_size());
+
+ OptimizedMinMaxGradient gradient;
+ m_Gradient.InitializeOptimized(gradient);
+ if(m_Gradient.minMaxState == kMMGGradient)
+ UpdateTpl<kGEMGradient>(ps, colorTemp, m_Gradient, gradient, fromIndex, toIndex);
+ else if(m_Gradient.minMaxState == kMMGRandomBetweenTwoGradients)
+ UpdateTpl<kGEMGradientMinMax>(ps, colorTemp, m_Gradient, gradient, fromIndex, toIndex);
+ else
+ UpdateTpl<kGEMSlow>(ps, colorTemp, m_Gradient, gradient, fromIndex, toIndex);
+}
+
+template<class TransferFunction>
+void ColorModule::Transfer (TransferFunction& transfer)
+{
+ ParticleSystemModule::Transfer (transfer);
+ transfer.Transfer (m_Gradient, "gradient");
+}
+INSTANTIATE_TEMPLATE_TRANSFER(ColorModule)
diff --git a/Runtime/Graphics/ParticleSystem/Modules/ColorModule.h b/Runtime/Graphics/ParticleSystem/Modules/ColorModule.h
new file mode 100644
index 0000000..1fe7250
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/ColorModule.h
@@ -0,0 +1,25 @@
+#ifndef SHURIKENMODULECOLOR_H
+#define SHURIKENMODULECOLOR_H
+
+#include "ParticleSystemModule.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemGradients.h"
+
+class ColorModule : public ParticleSystemModule
+{
+public:
+ DECLARE_MODULE (ColorModule)
+ ColorModule ();
+
+ void Update (const ParticleSystemParticles& ps, ColorRGBA32* colorTemp, size_t fromIndex, size_t toIndex);
+ void CheckConsistency() {};
+
+ inline MinMaxGradient& GetGradient() { return m_Gradient; };
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+private:
+ MinMaxGradient m_Gradient;
+};
+
+#endif // SHURIKENMODULECOLOR_H
diff --git a/Runtime/Graphics/ParticleSystem/Modules/EmissionModule.cpp b/Runtime/Graphics/ParticleSystem/Modules/EmissionModule.cpp
new file mode 100644
index 0000000..111b06f
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/EmissionModule.cpp
@@ -0,0 +1,124 @@
+#include "UnityPrefix.h"
+#include "EmissionModule.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Math/Vector2.h"
+
+static int AccumulateBursts (const ParticleSystemEmissionData& emissionData, float t0, float t1)
+{
+ int burstParticles = 0;
+ const size_t count = emissionData.burstCount;
+ for (size_t q = 0; q < count; ++q)
+ {
+ if (emissionData.burstTime[q] >= t0 && emissionData.burstTime[q] < t1)
+ burstParticles += emissionData.burstParticleCount[q];
+ }
+ return burstParticles;
+}
+
+static float AccumulateContinuous(const ParticleSystemEmissionData& emissionData, const float length, const float toT, const float dt)
+{
+ DebugAssert (length > 0.0001f);
+ DebugAssert (toT >= 0.0f);
+ DebugAssert (toT <= length);
+ float normalizedT = toT / length;
+ return std::max<float> (0.0f, Evaluate (emissionData.rate, normalizedT)) * dt;
+};
+
+EmissionModule::EmissionModule () : ParticleSystemModule(true)
+{
+ m_EmissionData.burstCount = 0;
+ m_EmissionData.type = kEmissionTypeTime;
+ for(int i = 0; i < ParticleSystemEmissionData::kMaxNumBursts; i++)
+ {
+ m_EmissionData.burstParticleCount[i] = 30;
+ m_EmissionData.burstTime[i] = 0.0f;
+ }
+}
+
+void EmissionModule::Emit (ParticleSystemEmissionState& emissionState, size_t& amountOfParticlesToEmit, size_t& numContinuous, const ParticleSystemEmissionData& emissionData, const Vector3f velocity, float fromT, float toT, float dt, float length)
+{
+ const float epsilon = 0.0001f;
+ if(kEmissionTypeTime == emissionData.type)
+ {
+ float rate = 0.0f;
+ float t0 = std::max<float> (0.0f, fromT);
+ float t1 = std::max<float> (0.0f, toT);
+ if (t1 < t0) // handle loop
+ {
+ rate += AccumulateContinuous (emissionData, length, t1, t1); // from start to current time
+ t1 = length; // from last time to end
+ }
+ rate += AccumulateContinuous (emissionData, length, t1, t1 - t0); // from start to current time
+
+ const float newParticles = rate;
+ if(newParticles >= epsilon)
+ emissionState.m_ParticleSpacing = 1.0f / newParticles;
+ else
+ emissionState.m_ParticleSpacing = 1.0f;
+
+ emissionState.m_ToEmitAccumulator += newParticles;
+ amountOfParticlesToEmit = (int)emissionState.m_ToEmitAccumulator;
+ emissionState.m_ToEmitAccumulator -= (float)amountOfParticlesToEmit;
+
+ // Continuous emits
+ numContinuous = amountOfParticlesToEmit;
+
+ // Bursts
+ t0 = std::max<float> (0.0f, fromT);
+ t1 = std::max<float> (0.0f, toT);
+ if (t1 < t0) // handle loop
+ {
+ amountOfParticlesToEmit += AccumulateBursts (emissionData, 0.0f, t1); // from start to current time
+ t1 = length + epsilon; // from last time to end
+ }
+ amountOfParticlesToEmit += AccumulateBursts (emissionData, t0, t1); // from start to current time
+ }
+ else
+ {
+ float newParticles = AccumulateContinuous (emissionData, length, toT, dt) * Magnitude (velocity); // from start to current time
+ if(newParticles >= epsilon)
+ emissionState.m_ParticleSpacing = 1.0f / newParticles;
+ else
+ emissionState.m_ParticleSpacing = 1.0f;
+
+ emissionState.m_ToEmitAccumulator += newParticles;
+ amountOfParticlesToEmit = (int)emissionState.m_ToEmitAccumulator;
+ emissionState.m_ToEmitAccumulator -= (float)amountOfParticlesToEmit;
+
+ // Continuous emits
+ numContinuous = amountOfParticlesToEmit;
+ }
+}
+
+void EmissionModule::CheckConsistency ()
+{
+ m_EmissionData.rate.SetScalar(std::max<float> (0.0f, m_EmissionData.rate.GetScalar()));
+
+ const size_t count = m_EmissionData.burstCount;
+ for (size_t q = 0; q < count; ++q)
+ {
+ m_EmissionData.burstTime[q] = std::max<float> (0.0f, m_EmissionData.burstTime[q]);
+ }
+}
+
+template<class TransferFunction>
+void EmissionModule::Transfer (TransferFunction& transfer)
+{
+ ParticleSystemModule::Transfer (transfer);
+
+ transfer.Transfer (m_EmissionData.type, "m_Type");
+ transfer.Transfer (m_EmissionData.rate, "rate");
+
+ const char* kCountNames [ParticleSystemEmissionData::kMaxNumBursts] = { "cnt0", "cnt1", "cnt2", "cnt3", };
+ const char* kTimeNames [ParticleSystemEmissionData::kMaxNumBursts] = { "time0", "time1", "time2", "time3", };
+ for(int i = 0; i < ParticleSystemEmissionData::kMaxNumBursts; i++)
+ transfer.Transfer (m_EmissionData.burstParticleCount[i], kCountNames[i]);
+
+ for(int i = 0; i < ParticleSystemEmissionData::kMaxNumBursts; i++)
+ transfer.Transfer (m_EmissionData.burstTime[i], kTimeNames[i]);
+
+ transfer.Transfer (m_EmissionData.burstCount, "m_BurstCount"); transfer.Align();
+}
+
+INSTANTIATE_TEMPLATE_TRANSFER(EmissionModule)
diff --git a/Runtime/Graphics/ParticleSystem/Modules/EmissionModule.h b/Runtime/Graphics/ParticleSystem/Modules/EmissionModule.h
new file mode 100644
index 0000000..75ad977
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/EmissionModule.h
@@ -0,0 +1,34 @@
+#ifndef SHURIKENMODULEEMISSION_H
+#define SHURIKENMODULEEMISSION_H
+
+#include "ParticleSystemModule.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemCurves.h"
+
+class Vector2f;
+
+class EmissionModule : public ParticleSystemModule
+{
+public:
+ DECLARE_MODULE (EmissionModule)
+ EmissionModule ();
+
+ enum { kEmissionTypeTime, kEmissionTypeDistance };
+
+ static void Emit (ParticleSystemEmissionState& emissionState, size_t& amountOfParticlesToEmit, size_t& numContinuous, const ParticleSystemEmissionData& emissionData, const Vector3f velocity, float fromT, float toT, float dt, float length);
+ void CheckConsistency ();
+
+ int CalculateMaximumEmitCountEstimate(float deltaTime) const;
+
+ const ParticleSystemEmissionData& GetEmissionDataRef() { return m_EmissionData; }
+ const ParticleSystemEmissionData& GetEmissionDataRef() const { return m_EmissionData; }
+ void GetEmissionDataCopy(ParticleSystemEmissionData* emissionData) { *emissionData = m_EmissionData; };
+ ParticleSystemEmissionData& GetEmissionData() { return m_EmissionData; }
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+private:
+ ParticleSystemEmissionData m_EmissionData;
+};
+
+#endif // SHURIKENMODULEEMISSION_H
diff --git a/Runtime/Graphics/ParticleSystem/Modules/ExternalForcesModule.cpp b/Runtime/Graphics/ParticleSystem/Modules/ExternalForcesModule.cpp
new file mode 100644
index 0000000..0b86e79
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/ExternalForcesModule.cpp
@@ -0,0 +1,118 @@
+#include "UnityPrefix.h"
+#include "ExternalForcesModule.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "../ParticleSystemUtils.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "Runtime/Geometry/Sphere.h"
+#include "Runtime/Terrain/Wind.h"
+#include "Runtime/Input/TimeManager.h"
+
+
+void ApplyRadialForce(ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex, const Vector3f position, const float radius, const float force, const float dt)
+{
+ for(int q = fromIndex; q < toIndex; q++)
+ {
+ Vector3f toForce = position - ps.position[q];
+ float distanceToForce = Magnitude(toForce);
+ toForce = NormalizeSafe(toForce);
+ float forceFactor = clamp(distanceToForce/radius, 0.0f, 1.0f);
+ forceFactor = 1.0f - forceFactor * forceFactor;
+ Vector3f delta = toForce * force * forceFactor * dt;
+ ps.velocity[q] += delta;
+ }
+}
+
+void ApplyDirectionalForce(ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex, const Vector3f position, const Vector3f direction, const float radius, const float force, const float dt)
+{
+ for(int q = fromIndex; q < toIndex; q++)
+ {
+ Vector3f delta = direction * force * dt;
+ ps.velocity[q] += delta;
+ }
+}
+
+ExternalForcesModule::ExternalForcesModule () : ParticleSystemModule(false)
+, m_Multiplier (1.0f)
+{
+}
+
+void ExternalForcesModule::Update (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex, float dt)
+{
+ Matrix4x4f matrix = Matrix4x4f::identity;
+ if(roState.useLocalSpace)
+ Matrix4x4f::Invert_General3D(state.localToWorld, matrix);
+
+ AABB aabb = state.minMaxAABB;
+ for(int i = 0; i < state.numCachedForces; i++)
+ {
+ const ParticleSystemExternalCachedForce& cachedForce = state.cachedForces[i];
+ const Vector3f position = matrix.MultiplyPoint3(cachedForce.position);
+ const Vector3f direction = matrix.MultiplyVector3(cachedForce.direction);
+ const float radius = cachedForce.radius;
+ const float force = cachedForce.forceMain * m_Multiplier;
+
+ const WindZone::WindZoneMode forceType = (WindZone::WindZoneMode)cachedForce.forceType;
+ if(WindZone::Spherical == forceType)
+ {
+ Sphere sphere (position, radius);
+ if(!IntersectAABBSphere (aabb, sphere))
+ continue;
+ ApplyRadialForce(ps, fromIndex, toIndex, position, radius, force, dt);
+ }
+ else if(WindZone::Directional == forceType)
+ ApplyDirectionalForce(ps, fromIndex, toIndex, position, direction, radius, force, dt);
+ }
+}
+
+// TODO: Perform culling here, instead of caching all of them
+void ExternalForcesModule::AllocateAndCache(const ParticleSystemReadOnlyState& roState, ParticleSystemState& state)
+{
+ float time = GetTimeManager ().GetTimeSinceLevelLoad ();
+ WindManager::WindZoneList& windZones = WindManager::GetInstance().GetList();
+
+ state.numCachedForces = 0;
+ for (WindManager::WindZoneList::iterator it = windZones.begin (); it != windZones.end (); ++it)
+ state.numCachedForces++;
+
+ // Allocate
+ state.cachedForces = ALLOC_TEMP_MANUAL(ParticleSystemExternalCachedForce, state.numCachedForces);
+
+ // Cache
+ int i = 0;
+ for (WindManager::WindZoneList::iterator it = windZones.begin (); it != windZones.end (); ++it)
+ {
+ const WindZone& zone = **it;
+ ParticleSystemExternalCachedForce& cachedForce = state.cachedForces[i++];
+ cachedForce.position = zone.GetComponent (Transform).GetPosition();
+ cachedForce.direction = zone.GetComponent (Transform).GetLocalToWorldMatrix().GetAxisZ();
+ cachedForce.forceType = zone.GetMode();
+ cachedForce.radius = zone.GetRadius();
+
+ float phase = time * kPI * zone.GetWindPulseFrequency();
+ float pulse = (cos (phase) + cos (phase * 0.375f) + cos (phase * 0.05f)) * 0.333f;
+ pulse = 1.0f + (pulse * zone.GetWindPulseMagnitude());
+ cachedForce.forceMain = zone.GetWindMain() * pulse;
+ // Maybe implement using turbulence and time based phasing?
+ // ForceTurbulenceMultiply (maybe) // @TODO: Figure out what to do about turbulence. Do perlin field? Expensive but maybe cool! If use it: only do it when turbulence force is set to something
+
+ //cachedForce.force = 1.0f;
+ }
+}
+
+void ExternalForcesModule::FreeCache(ParticleSystemState& state)
+{
+ if(state.cachedForces)
+ FREE_TEMP_MANUAL(state.cachedForces);
+ state.cachedForces = NULL;
+ state.numCachedForces = 0;
+}
+
+template<class TransferFunction>
+void ExternalForcesModule::Transfer (TransferFunction& transfer)
+{
+ ParticleSystemModule::Transfer (transfer);
+ transfer.Transfer (m_Multiplier, "multiplier");
+}
+INSTANTIATE_TEMPLATE_TRANSFER(ExternalForcesModule)
diff --git a/Runtime/Graphics/ParticleSystem/Modules/ExternalForcesModule.h b/Runtime/Graphics/ParticleSystem/Modules/ExternalForcesModule.h
new file mode 100644
index 0000000..2abc949
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/ExternalForcesModule.h
@@ -0,0 +1,25 @@
+#ifndef SHURIKENMODULEEXTERNALFORCES_H
+#define SHURIKENMODULEEXTERNALFORCES_H
+
+#include "ParticleSystemModule.h"
+
+class ExternalForcesModule : public ParticleSystemModule
+{
+public:
+ DECLARE_MODULE (ExternalForcesModule)
+ ExternalForcesModule ();
+
+ void Update (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex, float dt);
+ void CheckConsistency() {};
+
+ static void AllocateAndCache(const ParticleSystemReadOnlyState& roState, ParticleSystemState& state);
+ static void FreeCache(ParticleSystemState& state);
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+private:
+ float m_Multiplier;
+};
+
+#endif // SHURIKENMODULEEXTERNALFORCES_H
diff --git a/Runtime/Graphics/ParticleSystem/Modules/ForceModule.cpp b/Runtime/Graphics/ParticleSystem/Modules/ForceModule.cpp
new file mode 100644
index 0000000..d72da93
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/ForceModule.cpp
@@ -0,0 +1,164 @@
+#include "UnityPrefix.h"
+#include "ForceModule.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "../ParticleSystemUtils.h"
+#include "Runtime/Math/Random/Random.h"
+
+template<ParticleSystemCurveEvalMode mode>
+void UpdateTpl(const MinMaxCurve& x, const MinMaxCurve& y, const MinMaxCurve& z, ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex, bool transform, const Matrix4x4f& matrix, float dt)
+{
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ {
+ Vector3f random;
+ GenerateRandom3(random, ps.randomSeed[q] + kParticleSystemForceCurveId);
+ const float time = NormalizedTime(ps, q);
+ Vector3f f = Vector3f (Evaluate<mode> (x, time, random.x), Evaluate<mode> (y, time, random.y), Evaluate<mode> (z, time, random.z));
+ if(transform)
+ f = matrix.MultiplyVector3 (f);
+ ps.velocity[q] += f * dt;
+ }
+}
+
+template<bool isOptimized>
+void UpdateProceduralTpl(const DualMinMax3DPolyCurves& pos, const DualMinMax3DPolyCurves& vel, ParticleSystemParticles& ps, const Matrix4x4f& matrix, bool transform)
+{
+ const size_t count = ps.array_size();
+ for (int q=0; q<count; q++)
+ {
+ Vector3f random;
+ GenerateRandom3(random, ps.randomSeed[q] + kParticleSystemForceCurveId);
+ float time = NormalizedTime(ps, q);
+ float range = ps.startLifetime[q];
+
+ Vector3f delta;
+ Vector3f velocity;
+ if(isOptimized)
+ {
+ delta = Vector3f (EvaluateDoubleIntegrated(pos.optX, time, random.x), EvaluateDoubleIntegrated(pos.optY, time, random.y), EvaluateDoubleIntegrated(pos.optZ, time, random.z));
+ velocity = Vector3f (EvaluateIntegrated(vel.optX, time, random.x), EvaluateIntegrated(vel.optY, time, random.y), EvaluateIntegrated(vel.optZ, time, random.z));
+ }
+ else
+ {
+ delta = Vector3f (EvaluateDoubleIntegrated(pos.x, time, random.x), EvaluateDoubleIntegrated(pos.y, time, random.y), EvaluateDoubleIntegrated(pos.z, time, random.z));
+ velocity = Vector3f (EvaluateIntegrated(vel.x, time, random.x), EvaluateIntegrated(vel.y, time, random.y), EvaluateIntegrated(vel.z, time, random.z));
+ }
+
+ // Sqr range
+ delta *= range * range;
+ velocity *= range;
+
+ if(transform)
+ {
+ delta = matrix.MultiplyVector3 (delta);
+ velocity = matrix.MultiplyVector3 (velocity);
+ }
+
+ ps.position[q] += delta;
+ ps.velocity[q] += velocity;
+ }
+}
+
+ForceModule::ForceModule () : ParticleSystemModule(false)
+, m_RandomizePerFrame (false)
+, m_InWorldSpace(false)
+{}
+
+void ForceModule::Update (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex, float dt)
+{
+ Matrix4x4f matrix;
+ bool transform = GetTransformationMatrix(matrix, !roState.useLocalSpace, m_InWorldSpace, state.localToWorld);
+
+ if (m_RandomizePerFrame)
+ {
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ {
+ const float t = NormalizedTime (ps, q);
+ const float randomX = Random01 (m_Random);
+ const float randomY = Random01 (m_Random);
+ const float randomZ = Random01 (m_Random);
+ Vector3f f (Evaluate (m_X, t, randomX), Evaluate (m_Y, t, randomY), Evaluate (m_Z, t, randomZ));
+ if(transform)
+ f = matrix.MultiplyVector3 (f);
+ ps.velocity[q] += f * dt;
+ }
+ }
+ else
+ {
+ bool usesScalar = (m_X.minMaxState == kMMCScalar) && (m_Y.minMaxState == kMMCScalar) && (m_Z.minMaxState == kMMCScalar);
+ bool isOptimized = m_X.IsOptimized() && m_Y.IsOptimized() && m_Z.IsOptimized();
+ bool usesMinMax = m_X.UsesMinMax() && m_Y.UsesMinMax() && m_Z.UsesMinMax();
+ if(usesScalar)
+ UpdateTpl<kEMScalar>(m_X, m_Y, m_Z, ps, fromIndex, toIndex, transform, matrix, dt);
+ else if(isOptimized && usesMinMax)
+ UpdateTpl<kEMOptimizedMinMax>(m_X, m_Y, m_Z, ps, fromIndex, toIndex, transform, matrix, dt);
+ else if(isOptimized)
+ UpdateTpl<kEMOptimized>(m_X, m_Y, m_Z, ps, fromIndex, toIndex, transform, matrix, dt);
+ else
+ UpdateTpl<kEMSlow>(m_X, m_Y, m_Z, ps, fromIndex, toIndex, transform, matrix, dt);
+ }
+}
+
+void ForceModule::UpdateProcedural (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps)
+{
+ Assert(!m_RandomizePerFrame);
+
+ Matrix4x4f matrix;
+ bool transform = GetTransformationMatrix(matrix, !roState.useLocalSpace, m_InWorldSpace, state.localToWorld);
+
+ DualMinMax3DPolyCurves posCurves;
+ DualMinMax3DPolyCurves velCurves;
+ if(m_X.IsOptimized() && m_Y.IsOptimized() && m_Z.IsOptimized())
+ {
+ posCurves.optX = m_X.polyCurves; posCurves.optX.DoubleIntegrate();
+ posCurves.optY = m_Y.polyCurves; posCurves.optY.DoubleIntegrate();
+ posCurves.optZ = m_Z.polyCurves; posCurves.optZ.DoubleIntegrate();
+ velCurves.optX = m_X.polyCurves; velCurves.optX.Integrate();
+ velCurves.optY = m_Y.polyCurves; velCurves.optY.Integrate();
+ velCurves.optZ = m_Z.polyCurves; velCurves.optZ.Integrate();
+ UpdateProceduralTpl<true>(posCurves, velCurves, ps, matrix, transform);
+ }
+ else
+ {
+ DebugAssert(CurvesSupportProcedural (m_X.editorCurves, m_X.minMaxState));
+ DebugAssert(CurvesSupportProcedural (m_Y.editorCurves, m_Y.minMaxState));
+ DebugAssert(CurvesSupportProcedural (m_Z.editorCurves, m_Z.minMaxState));
+ BuildCurves(posCurves.x, m_X.editorCurves, m_X.GetScalar(), m_X.minMaxState); posCurves.x.DoubleIntegrate();
+ BuildCurves(posCurves.y, m_Y.editorCurves, m_Y.GetScalar(), m_Y.minMaxState); posCurves.y.DoubleIntegrate();
+ BuildCurves(posCurves.z, m_Z.editorCurves, m_Z.GetScalar(), m_Z.minMaxState); posCurves.z.DoubleIntegrate();
+ BuildCurves(velCurves.x, m_X.editorCurves, m_X.GetScalar(), m_X.minMaxState); velCurves.x.Integrate();
+ BuildCurves(velCurves.y, m_Y.editorCurves, m_Y.GetScalar(), m_Y.minMaxState); velCurves.y.Integrate();
+ BuildCurves(velCurves.z, m_Z.editorCurves, m_Z.GetScalar(), m_Z.minMaxState); velCurves.z.Integrate();
+ UpdateProceduralTpl<false>(posCurves, velCurves, ps, matrix, transform);
+ }
+}
+void ForceModule::CalculateProceduralBounds(MinMaxAABB& bounds, const Matrix4x4f& localToWorld, float maxLifeTime)
+{
+ Vector2f xRange = m_X.FindMinMaxDoubleIntegrated();
+ Vector2f yRange = m_Y.FindMinMaxDoubleIntegrated();
+ Vector2f zRange = m_Z.FindMinMaxDoubleIntegrated();
+ bounds.m_Min = Vector3f(xRange.x, yRange.x, zRange.x) * maxLifeTime * maxLifeTime;
+ bounds.m_Max = Vector3f(xRange.y, yRange.y, zRange.y) * maxLifeTime * maxLifeTime;
+
+ if(m_InWorldSpace)
+ {
+ Matrix4x4f matrix;
+ Matrix4x4f::Invert_General3D(localToWorld, matrix);
+ matrix.SetPosition(Vector3f::zero);
+ AABB aabb = bounds;
+ TransformAABBSlow(aabb, matrix, aabb);
+ bounds = aabb;
+ }
+}
+
+template<class TransferFunction>
+void ForceModule::Transfer (TransferFunction& transfer)
+{
+ ParticleSystemModule::Transfer (transfer);
+ transfer.Transfer (m_X, "x");
+ transfer.Transfer (m_Y, "y");
+ transfer.Transfer (m_Z, "z");
+ transfer.Transfer (m_InWorldSpace, "inWorldSpace");
+ transfer.Transfer (m_RandomizePerFrame, "randomizePerFrame"); transfer.Align ();
+}
+INSTANTIATE_TEMPLATE_TRANSFER(ForceModule)
diff --git a/Runtime/Graphics/ParticleSystem/Modules/ForceModule.h b/Runtime/Graphics/ParticleSystem/Modules/ForceModule.h
new file mode 100644
index 0000000..9ad54ec
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/ForceModule.h
@@ -0,0 +1,36 @@
+#ifndef SHURIKENMODULEFORCE_H
+#define SHURIKENMODULEFORCE_H
+
+#include "ParticleSystemModule.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemCurves.h"
+#include "Runtime/Math/Random/rand.h"
+
+class ForceModule : public ParticleSystemModule
+{
+public:
+ DECLARE_MODULE (ForceModule)
+ ForceModule ();
+
+ void Update (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex, float dt);
+ void UpdateProcedural (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps);
+ void CalculateProceduralBounds(MinMaxAABB& bounds, const Matrix4x4f& localToWorld, float maxLifeTime);
+ void CheckConsistency() {};
+
+ inline MinMaxCurve& GetXCurve() { return m_X; }
+ inline MinMaxCurve& GetYCurve() { return m_Y; }
+ inline MinMaxCurve& GetZCurve() { return m_Z; }
+ inline bool GetRandomizePerFrame() { return m_RandomizePerFrame; }
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+private:
+ MinMaxCurve m_X;
+ MinMaxCurve m_Y;
+ MinMaxCurve m_Z;
+ bool m_InWorldSpace;
+ bool m_RandomizePerFrame;
+ Rand m_Random;
+};
+
+#endif // SHURIKENMODULEFORCE_H
diff --git a/Runtime/Graphics/ParticleSystem/Modules/InitialModule.cpp b/Runtime/Graphics/ParticleSystem/Modules/InitialModule.cpp
new file mode 100644
index 0000000..680d175
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/InitialModule.cpp
@@ -0,0 +1,193 @@
+#include "UnityPrefix.h"
+#include "InitialModule.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemUtils.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/Interfaces/IPhysics.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+
+InitialModule::InitialModule () : ParticleSystemModule(true)
+, m_GravityModifier(0.0f)
+, m_InheritVelocity(0.0f)
+, m_MaxNumParticles(1000)
+{
+}
+
+Vector3f InitialModule::GetGravity (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state) const
+{
+#if ENABLE_PHYSICS
+ IPhysics* physicsModule = GetIPhysics();
+ if (!physicsModule)
+ return Vector3f::zero;
+
+ Vector3f gravity = m_GravityModifier * physicsModule->GetGravity ();
+ if(roState.useLocalSpace)
+ {
+ Matrix4x4f worldToLocal;
+ Matrix4x4f::Invert_General3D(state.localToWorld, worldToLocal);
+ gravity = worldToLocal.MultiplyVector3(gravity);
+ }
+ return gravity;
+#else
+ return Vector3f::zero;
+#endif
+}
+
+void InitialModule::Start (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const Matrix4x4f& matrix, size_t fromIndex, float t)
+{
+ DebugAssert(roState.lengthInSec > 0.0001f);
+ const float normalizedT = t / roState.lengthInSec;
+ DebugAssert (normalizedT >= 0.0f);
+ DebugAssert (normalizedT <= 1.0f);
+
+ Rand& random = GetRandom();
+
+ Vector3f origin = matrix.GetPosition ();
+
+ const size_t count = ps.array_size ();
+ for (size_t q = fromIndex; q < count; ++q)
+ {
+ UInt32 randUInt32 = random.Get ();
+ float rand = Rand::GetFloatFromInt (randUInt32);
+ UInt32 randByte = Rand::GetByteFromInt (randUInt32);
+
+ const ColorRGBA32 col = Evaluate (m_Color, normalizedT, randByte);
+ float sz = std::max<float> (0.0f, Evaluate (m_Size, normalizedT, rand));
+ Vector3f vel = matrix.MultiplyVector3 (Vector3f::zAxis);
+ float ttl = std::max<float> (0.0f, Evaluate (m_Lifetime, normalizedT, rand));
+ float rot = Evaluate (m_Rotation, normalizedT, rand);
+
+ ps.position[q] = origin;
+ ps.velocity[q] = vel;
+ ps.animatedVelocity[q] = Vector3f::zero;
+ ps.lifetime[q] = ttl;
+ ps.startLifetime[q] = ttl;
+ ps.size[q] = sz;
+ ps.rotation[q] = rot;
+ if(ps.usesRotationalSpeed)
+ ps.rotationalSpeed[q] = 0.0f;
+ ps.color[q] = col;
+ ps.randomSeed[q] = random.Get(); // One more iteration to avoid visible patterns between random spawned parameters and those used in update
+ if(ps.usesAxisOfRotation)
+ ps.axisOfRotation[q] = Vector3f::zAxis;
+ for(int acc = 0; acc < ps.numEmitAccumulators; acc++)
+ ps.emitAccumulator[acc][q] = 0.0f;
+
+ }
+}
+
+void InitialModule::Update (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex, float dt) const
+{
+ Vector3f gravityDelta = GetGravity(roState, state) * dt;
+ if(!CompareApproximately(gravityDelta, Vector3f::zero, 0.0001f))
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ ps.velocity[q] += gravityDelta;
+
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ ps.animatedVelocity[q] = Vector3f::zero;
+
+ if(ps.usesRotationalSpeed)
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ ps.rotationalSpeed[q] = 0.0f;
+}
+
+void InitialModule::GenerateProcedural (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const ParticleSystemEmitReplay& emit)
+{
+ size_t count = emit.particlesToEmit;
+ float t = emit.t;
+ float alreadyPassedTime = emit.aliveTime;
+
+ DebugAssert(roState.lengthInSec > 0.0001f);
+ const float normalizedT = t / roState.lengthInSec;
+ DebugAssert (normalizedT >= 0.0f);
+ DebugAssert (normalizedT <= 1.0f);
+
+ Rand& random = GetRandom();
+
+ const Matrix4x4f localToWorld = !roState.useLocalSpace ? state.localToWorld : Matrix4x4f::identity;
+ Vector3f origin = localToWorld.GetPosition ();
+ for (size_t i = 0; i < count; ++i)
+ {
+ UInt32 randUInt32 = random.Get ();
+ float rand = Rand::GetFloatFromInt (randUInt32);
+ UInt32 randByte = Rand::GetByteFromInt (randUInt32);
+
+ float frameOffset = (float(i) + emit.emissionOffset) * emit.emissionGap * float(i < emit.numContinuous);
+
+ const ColorRGBA32 col = Evaluate (m_Color, normalizedT, randByte);
+ float sz = std::max<float> (0.0f, Evaluate (m_Size, normalizedT, rand));
+ Vector3f vel = localToWorld.MultiplyVector3 (Vector3f::zAxis);
+ float ttlStart = std::max<float> (0.0f, Evaluate (m_Lifetime, normalizedT, rand));
+ float ttl = ttlStart - alreadyPassedTime - frameOffset;
+ float rot = Evaluate (m_Rotation, normalizedT, rand);
+
+ if (ttl < 0.0F)
+ continue;
+
+ size_t q = ps.array_size();
+ ps.array_resize(ps.array_size() + 1);
+
+ ps.position[q] = origin;
+ ps.velocity[q] = vel;
+ ps.animatedVelocity[q] = Vector3f::zero;
+ ps.lifetime[q] = ttl;
+ ps.startLifetime[q] = ttlStart;
+ ps.size[q] = sz;
+ ps.rotation[q] = rot;
+ if(ps.usesRotationalSpeed)
+ ps.rotationalSpeed[q] = 0.0f;
+ ps.color[q] = col;
+ ps.randomSeed[q] = random.Get(); // One more iteration to avoid visible patterns between random spawned parameters and those used in update
+ if(ps.usesAxisOfRotation)
+ ps.axisOfRotation[q] = Vector3f::zAxis;
+ for(int acc = 0; acc < ps.numEmitAccumulators; acc++)
+ ps.emitAccumulator[acc][q] = 0.0f;
+ }
+}
+
+void InitialModule::CheckConsistency ()
+{
+ m_Lifetime.SetScalar(clamp<float> (m_Lifetime.GetScalar(), 0.05f, 100000.0f));
+ m_Size.SetScalar(std::max<float> (0.0f, m_Size.GetScalar()));
+ m_MaxNumParticles = std::max<int> (0, m_MaxNumParticles);
+}
+
+void InitialModule::AwakeFromLoad (ParticleSystem* system, const ParticleSystemReadOnlyState& roState)
+{
+ ResetSeed(roState);
+}
+
+void InitialModule::ResetSeed(const ParticleSystemReadOnlyState& roState)
+{
+ if(roState.randomSeed == 0)
+ m_Random.SetSeed(GetGlobalRandomSeed ());
+ else
+ m_Random.SetSeed(roState.randomSeed);
+}
+
+Rand& InitialModule::GetRandom()
+{
+#if UNITY_EDITOR
+ if(!IsWorldPlaying())
+ return m_EditorRandom;
+ else
+#endif
+ return m_Random;
+}
+
+template<class TransferFunction>
+void InitialModule::Transfer (TransferFunction& transfer)
+{
+ SetEnabled(true); // always enabled
+ ParticleSystemModule::Transfer (transfer);
+ transfer.Transfer (m_Lifetime, "startLifetime");
+ transfer.Transfer (m_Speed, "startSpeed");
+ transfer.Transfer (m_Color, "startColor");
+ transfer.Transfer (m_Size, "startSize");
+ transfer.Transfer (m_Rotation, "startRotation");
+ transfer.Transfer (m_GravityModifier, "gravityModifier");
+ transfer.Transfer (m_InheritVelocity, "inheritVelocity");
+ transfer.Transfer (m_MaxNumParticles, "maxNumParticles");
+}
+INSTANTIATE_TEMPLATE_TRANSFER(InitialModule)
diff --git a/Runtime/Graphics/ParticleSystem/Modules/InitialModule.h b/Runtime/Graphics/ParticleSystem/Modules/InitialModule.h
new file mode 100644
index 0000000..cd602a9
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/InitialModule.h
@@ -0,0 +1,66 @@
+#ifndef SHURIKENMODULEINITIAL_H
+#define SHURIKENMODULEINITIAL_H
+
+#include "ParticleSystemModule.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemCurves.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemGradients.h"
+#include "Runtime/Math/Random/rand.h"
+
+struct ParticleSystemState;
+
+class InitialModule : public ParticleSystemModule
+{
+public:
+ DECLARE_MODULE (InitialModule)
+ InitialModule ();
+
+ void Start (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const Matrix4x4f& matrix, size_t fromIndex, float t);
+ void Update (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex, float dt) const;
+ void GenerateProcedural (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const ParticleSystemEmitReplay& emit);
+ void CheckConsistency ();
+ void AwakeFromLoad (ParticleSystem* system, const ParticleSystemReadOnlyState& roState);
+ void ResetSeed(const ParticleSystemReadOnlyState& roState);
+
+ inline MinMaxCurve& GetLifeTimeCurve() { return m_Lifetime; }
+ inline const MinMaxCurve& GetLifeTimeCurve() const { return m_Lifetime; }
+ inline MinMaxCurve& GetSpeedCurve() { return m_Speed; }
+ inline const MinMaxCurve& GetSpeedCurve() const { return m_Speed; }
+ inline MinMaxCurve& GetSizeCurve() { return m_Size; }
+ inline const MinMaxCurve& GetSizeCurve() const { return m_Size; }
+ inline MinMaxCurve& GetRotationCurve() { return m_Rotation; }
+ inline const MinMaxCurve& GetRotationCurve() const { return m_Rotation; }
+ inline MinMaxGradient& GetColor() { return m_Color; }
+ inline const MinMaxGradient& GetColor() const { return m_Color; }
+ inline void SetGravityModifier(float value) { m_GravityModifier = value; }
+ inline float GetGravityModifier() const { return m_GravityModifier; }
+
+ inline void SetMaxNumParticles(int value) { m_MaxNumParticles = value; }
+ inline int GetMaxNumParticles() const { return m_MaxNumParticles; }
+ inline float GetInheritVelocity() const { return m_InheritVelocity; }
+ Vector3f GetGravity (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state) const;
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+private:
+ Rand& GetRandom();
+
+ MinMaxCurve m_Lifetime;
+ MinMaxCurve m_Speed;
+ MinMaxGradient m_Color;
+ MinMaxCurve m_Size;
+ MinMaxCurve m_Rotation;
+ float m_GravityModifier;
+ float m_InheritVelocity;
+ int m_MaxNumParticles;
+
+ Rand m_Random;
+
+#if UNITY_EDITOR
+public:
+ Rand m_EditorRandom;
+#endif
+};
+
+
+#endif // SHURIKENMODULEINITIAL_H
diff --git a/Runtime/Graphics/ParticleSystem/Modules/ParticleSystemModule.cpp b/Runtime/Graphics/ParticleSystem/Modules/ParticleSystemModule.cpp
new file mode 100644
index 0000000..dee4872
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/ParticleSystemModule.cpp
@@ -0,0 +1,141 @@
+#include "UnityPrefix.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "ParticleSystemModule.h"
+
+ParticleSystemReadOnlyState::ParticleSystemReadOnlyState()
+: lengthInSec (5.0f)
+, startDelay (0.0f)
+, speed (1.0f)
+, randomSeed (0)
+, looping (true)
+, prewarm (false)
+, playOnAwake (true)
+, useLocalSpace (true)
+{
+}
+
+void ParticleSystemReadOnlyState::CheckConsistency()
+{
+ lengthInSec = std::max(lengthInSec, 0.1f);
+ lengthInSec = std::min(lengthInSec, 100000.0f); // Very large values can lead to editor locking up due to numerical instability.
+ startDelay = std::max(startDelay, 0.0f);
+ speed = std::max(speed, 0.0f);
+}
+
+template<class TransferFunction>
+void ParticleSystemReadOnlyState::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (lengthInSec);
+ TRANSFER (startDelay);
+ TRANSFER (speed);
+ TRANSFER (randomSeed);
+ TRANSFER (looping);
+ TRANSFER (prewarm);
+ TRANSFER (playOnAwake);
+ transfer.Transfer (useLocalSpace, "moveWithTransform");
+}
+INSTANTIATE_TEMPLATE_TRANSFER(ParticleSystemReadOnlyState)
+
+ParticleSystemState::ParticleSystemState ()
+: playing (false)
+, needRestart (true)
+, stopEmitting (false)
+, accumulatedDt (0.0f)
+, delayT (0.0f)
+, t (0.0f)
+, maxSize (0.0f)
+, isSubEmitter (false)
+, recordSubEmits(false)
+, cullTime(0.0)
+, culled(false)
+, numLoops(0)
+, invalidateProcedural(false)
+, supportsProcedural(true)
+, cachedForces(0)
+, numCachedForces(0)
+, cachedSubDataBirth(0)
+, numCachedSubDataBirth(0)
+, cachedSubDataCollision(0)
+, numCachedSubDataCollision(0)
+, cachedSubDataDeath(0)
+, numCachedSubDataDeath(0)
+, cachedCollisionPlanes(0)
+, numCachedCollisionPlanes(0)
+, rayBudget(0)
+, nextParticleToTrace(0)
+{
+ ClearSubEmitterCommandBuffer();
+
+ localToWorld.SetIdentity ();
+ emitterVelocity = Vector3f::zero;
+ emitterScale = Vector3f::one;
+ minMaxAABB = MinMaxAABB (Vector3f::zero, Vector3f::zero);
+}
+
+void ParticleSystemState::Tick (const ParticleSystemReadOnlyState& constState, float dt)
+{
+ t += dt;
+
+ for(int i = 0; i < subEmitterCommandBuffer.commandCount; i++)
+ subEmitterCommandBuffer.commands[i].timeAlive += dt;
+
+ if (!constState.looping)
+ t = std::min<float> (t, constState.lengthInSec);
+ else
+ if(t > constState.lengthInSec)
+ {
+ t -= constState.lengthInSec;
+ numLoops++;
+ }
+}
+
+void ParticleSystemState::ClearSubEmitterCommandBuffer()
+{
+ if(cachedSubDataBirth)
+ {
+ for (int i = 0; i < numCachedSubDataBirth; ++i)
+ {
+ (cachedSubDataBirth+i)->~ParticleSystemSubEmitterData();
+ }
+ FREE_TEMP_MANUAL(cachedSubDataBirth);
+ }
+ if(cachedSubDataCollision)
+ {
+ for (int i = 0; i < numCachedSubDataCollision; ++i)
+ {
+ (cachedSubDataCollision+i)->~ParticleSystemSubEmitterData();
+ }
+ FREE_TEMP_MANUAL(cachedSubDataCollision);
+ }
+ if(cachedSubDataDeath)
+ {
+ for (int i = 0; i < numCachedSubDataDeath; ++i)
+ {
+ (cachedSubDataDeath+i)->~ParticleSystemSubEmitterData();
+ }
+ FREE_TEMP_MANUAL(cachedSubDataDeath);
+ }
+ if(subEmitterCommandBuffer.commands)
+ FREE_TEMP_MANUAL(subEmitterCommandBuffer.commands);
+
+ cachedSubDataBirth = cachedSubDataCollision = cachedSubDataDeath = 0;
+ numCachedSubDataBirth = numCachedSubDataCollision = numCachedSubDataDeath = 0;
+ subEmitterCommandBuffer.commands = 0;
+ subEmitterCommandBuffer.commandCount = subEmitterCommandBuffer.maxCommandCount = 0;
+}
+
+template<class TransferFunction>
+void ParticleSystemState::Transfer (TransferFunction& transfer)
+{
+ TRANSFER_DEBUG (t);
+}
+INSTANTIATE_TEMPLATE_TRANSFER(ParticleSystemState)
+
+
+template<class TransferFunction>
+void ParticleSystemModule::Transfer (TransferFunction& transfer)
+{
+ transfer.Transfer (m_Enabled, "enabled"); transfer.Align ();
+}
+INSTANTIATE_TEMPLATE_TRANSFER(ParticleSystemModule)
diff --git a/Runtime/Graphics/ParticleSystem/Modules/ParticleSystemModule.h b/Runtime/Graphics/ParticleSystem/Modules/ParticleSystemModule.h
new file mode 100644
index 0000000..b4746de
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/ParticleSystemModule.h
@@ -0,0 +1,238 @@
+#ifndef SHURIKENMODULE_H
+#define SHURIKENMODULE_H
+
+#include "../ParticleSystemCommon.h"
+#include "../ParticleSystemCurves.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+#define ENABLE_MULTITHREADED_PARTICLES ENABLE_MULTITHREADED_CODE
+
+#define DECLARE_MODULE(name) const char* GetName () { return #name; } DEFINE_GET_TYPESTRING(name)
+
+class ParticleSystem;
+class Plane;
+
+struct ParticleSystemEmissionState
+{
+ ParticleSystemEmissionState() { Clear(); }
+ inline void Clear()
+ {
+ m_ToEmitAccumulator = 0.0f;
+ m_ParticleSpacing = 0.0f;
+ }
+ float m_ParticleSpacing;
+ float m_ToEmitAccumulator;
+};
+
+struct ParticleSystemEmissionData
+{
+ enum { kMaxNumBursts = 4 };
+
+ int type;
+ MinMaxCurve rate;
+ float burstTime[kMaxNumBursts];
+ UInt16 burstParticleCount[kMaxNumBursts];
+ UInt8 burstCount;
+};
+
+
+struct ParticleSystemSubEmitterData
+{
+ ParticleSystemSubEmitterData()
+ :maxLifetime(0.0f)
+ ,startDelayInSec(0.0f)
+ ,lengthInSec(0.0f)
+ {}
+
+ ParticleSystemEmissionData emissionData;
+ float maxLifetime;
+ float startDelayInSec;
+ float lengthInSec;
+ ParticleSystem* emitter;
+};
+
+// @TODO: Find "pretty" place for shared structs and enums?
+struct ParticleSystemEmitReplay
+{
+ float t;
+ float aliveTime;
+ float emissionOffset;
+ float emissionGap;
+ int particlesToEmit;
+ size_t numContinuous;
+ UInt32 randomSeed;
+
+ ParticleSystemEmitReplay (float inT, int inParticlesToEmit, float inEmissionOffset, float inEmissionGap, size_t inNumContinuous, UInt32 inRandomSeed)
+ : t (inT), particlesToEmit (inParticlesToEmit), aliveTime(0.0F), emissionOffset(inEmissionOffset), emissionGap(inEmissionGap), numContinuous(inNumContinuous), randomSeed(inRandomSeed)
+ {}
+};
+
+struct SubEmitterEmitCommand
+{
+ SubEmitterEmitCommand(ParticleSystemEmissionState inEmissionState, Vector3f inPosition, Vector3f inVelocity, ParticleSystemSubType inSubEmitterType, int inSubEmitterIndex, int inParticlesToEmit, int inParticlesToEmitContinuous, float inParentT, float inDeltaTime)
+ :emissionState(inEmissionState)
+ ,position(inPosition)
+ ,velocity(inVelocity)
+ ,subEmitterType(inSubEmitterType)
+ ,subEmitterIndex(inSubEmitterIndex)
+ ,particlesToEmit(inParticlesToEmit)
+ ,particlesToEmitContinuous(inParticlesToEmitContinuous)
+ ,deltaTime(inDeltaTime)
+ ,parentT(inParentT)
+ ,timeAlive(0.0f)
+ {
+ }
+
+ ParticleSystemEmissionState emissionState;
+ Vector3f position;
+ Vector3f velocity;
+ ParticleSystemSubType subEmitterType;
+ int subEmitterIndex;
+ int particlesToEmit;
+ int particlesToEmitContinuous;
+ float deltaTime;
+ float parentT; // Used for StartModules
+ float timeAlive;
+};
+
+struct ParticleSystemSubEmitCmdBuffer
+{
+ ParticleSystemSubEmitCmdBuffer()
+ :commands(0)
+ ,commandCount(0)
+ ,maxCommandCount(0)
+ {}
+
+ inline void AddCommand(const ParticleSystemEmissionState& emissionState, const Vector3f& initialPosition, const Vector3f& initialVelocity, const ParticleSystemSubType type, const int index, const int particlesToEmit, const int particlesToEmitContinuous, const float parentT, const float dt)
+ {
+ commands[commandCount++] = SubEmitterEmitCommand(emissionState, initialPosition, initialVelocity, type, index, particlesToEmit, particlesToEmitContinuous, parentT, dt);
+ }
+ inline bool IsFull() { return commandCount >= maxCommandCount; }
+
+ SubEmitterEmitCommand* commands;
+ int commandCount;
+ int maxCommandCount; // Mostly to assert/test against trashing memory
+};
+
+struct ParticleSystemExternalCachedForce
+{
+ Vector3f position;
+ Vector3f direction;
+ int forceType;
+ float radius;
+ float forceMain;
+ float forceTurbulence; // not yet implemented
+};
+
+// @TODO: Maybe there's a better name for this? ParticleSystemSerializedState? Some shit like that :)
+struct ParticleSystemReadOnlyState
+{
+ ParticleSystemReadOnlyState();
+
+ void CheckConsistency();
+
+ float lengthInSec;
+ float startDelay;
+ float speed;
+ UInt32 randomSeed;
+ bool looping;
+ bool prewarm;
+ bool playOnAwake;
+ bool useLocalSpace;
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+};
+
+// Some of these aren't actually state, but more like context. Separate it?
+struct ParticleSystemState
+{
+ // state
+ float accumulatedDt;
+ float delayT;
+ bool playing;
+ bool needRestart;
+ bool stopEmitting;
+ size_t rayBudget;
+ size_t nextParticleToTrace;
+
+ bool GetIsSubEmitter() const { return isSubEmitter; }
+private:
+ // When setting this we need to ensure some other things happen as well
+ bool isSubEmitter;
+
+public:
+
+ bool recordSubEmits;
+
+ // Procedural mode / culling
+ bool supportsProcedural; // With the current parameter set, does the emitter support procedural mode?
+ bool invalidateProcedural; // This is set if anything changes from script at some point when running a system
+ bool culled; // Is particle system currently culled?
+ double cullTime; // Absolute time, so we need as double in case it runs for ages
+ int numLoops; // Number of loops executed
+
+ // per-frame
+ Matrix4x4f localToWorld;
+ Matrix4x4f worldToLocal;
+ Vector3f emitterVelocity;
+ Vector3f emitterScale;
+
+ MinMaxAABB minMaxAABB;
+ float maxSize; // Maximum size of particles due to setting from script
+ float t;
+
+ // Temp alloc stuff
+ ParticleSystemSubEmitterData* cachedSubDataBirth;
+ size_t numCachedSubDataBirth;
+ ParticleSystemSubEmitterData* cachedSubDataCollision;
+ size_t numCachedSubDataCollision;
+ ParticleSystemSubEmitterData* cachedSubDataDeath;
+ size_t numCachedSubDataDeath;
+ ParticleSystemExternalCachedForce* cachedForces;
+ size_t numCachedForces;
+ Plane* cachedCollisionPlanes;
+ size_t numCachedCollisionPlanes;
+ ParticleSystemSubEmitCmdBuffer subEmitterCommandBuffer;
+
+ dynamic_array<ParticleSystemEmitReplay> emitReplay;
+ ParticleSystemEmissionState emissionState;
+
+ ParticleSystemState ();
+
+ void Tick (const ParticleSystemReadOnlyState& constState, float dt);
+ void ClearSubEmitterCommandBuffer();
+
+ void SetIsSubEmitter(bool inIsSubEmitter)
+ {
+ if(inIsSubEmitter)
+ {
+ stopEmitting = true;
+ invalidateProcedural = true;
+ }
+ isSubEmitter = inIsSubEmitter;
+ }
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+};
+
+
+class ParticleSystemModule
+{
+public:
+ DECLARE_SERIALIZE (ParticleSystemModule)
+ ParticleSystemModule (bool enabled) : m_Enabled (enabled) {}
+ virtual ~ParticleSystemModule () {}
+
+ inline bool GetEnabled() const { return m_Enabled; }
+ inline void SetEnabled(bool enabled) { m_Enabled = enabled; }
+
+private:
+ // shared data
+ bool m_Enabled;
+};
+
+#endif // SHURIKENMODULE_H
diff --git a/Runtime/Graphics/ParticleSystem/Modules/RotationByVelocityModule.cpp b/Runtime/Graphics/ParticleSystem/Modules/RotationByVelocityModule.cpp
new file mode 100644
index 0000000..c194808
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/RotationByVelocityModule.cpp
@@ -0,0 +1,51 @@
+#include "UnityPrefix.h"
+#include "RotationByVelocityModule.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemUtils.h"
+
+
+RotationBySpeedModule::RotationBySpeedModule () : ParticleSystemModule(false)
+, m_Range (0.0f, 1.0f)
+{}
+
+template<ParticleSystemCurveEvalMode mode>
+void UpdateTpl(const MinMaxCurve& curve, ParticleSystemParticles& ps, size_t fromIndex, size_t toIndex, const Vector2f offsetScale)
+{
+ if (!ps.usesRotationalSpeed) return;
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ {
+ const Vector3f vel = ps.velocity[q] + ps.animatedVelocity[q];
+ const float t = InverseLerpFast01 (offsetScale, Magnitude(vel));
+ const float random = GenerateRandom(ps.randomSeed[q] + kParticleSystemRotationBySpeedCurveId);
+ ps.rotationalSpeed[q] += Evaluate<mode> (curve, t, random);
+ }
+}
+
+void RotationBySpeedModule::Update (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex)
+{
+ Vector2f offsetScale = CalculateInverseLerpOffsetScale (m_Range);
+ if (m_Curve.minMaxState == kMMCScalar)
+ UpdateTpl<kEMScalar>(m_Curve, ps, fromIndex, toIndex, offsetScale);
+ else if(m_Curve.IsOptimized() && m_Curve.UsesMinMax())
+ UpdateTpl<kEMOptimizedMinMax>(m_Curve, ps, fromIndex, toIndex, offsetScale);
+ else if(m_Curve.IsOptimized())
+ UpdateTpl<kEMOptimized>(m_Curve, ps, fromIndex, toIndex, offsetScale);
+ else
+ UpdateTpl<kEMSlow>(m_Curve, ps, fromIndex, toIndex, offsetScale);
+}
+
+void RotationBySpeedModule::CheckConsistency ()
+{
+ const float MyEpsilon = 0.001f;
+ m_Range.y = std::max (m_Range.x + MyEpsilon, m_Range.y);
+}
+
+template<class TransferFunction>
+void RotationBySpeedModule::Transfer (TransferFunction& transfer)
+{
+ ParticleSystemModule::Transfer (transfer);
+ transfer.Transfer (m_Curve, "curve");
+ transfer.Transfer (m_Range, "range");
+}
+INSTANTIATE_TEMPLATE_TRANSFER(RotationBySpeedModule)
diff --git a/Runtime/Graphics/ParticleSystem/Modules/RotationByVelocityModule.h b/Runtime/Graphics/ParticleSystem/Modules/RotationByVelocityModule.h
new file mode 100644
index 0000000..548f9c3
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/RotationByVelocityModule.h
@@ -0,0 +1,27 @@
+#ifndef SHURIKENMODULEROTATIONBYVELOCITY_H
+#define SHURIKENMODULEROTATIONBYVELOCITY_H
+
+#include "ParticleSystemModule.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemCurves.h"
+#include "Runtime/Math/Vector2.h"
+
+class RotationBySpeedModule : public ParticleSystemModule
+{
+public:
+ DECLARE_MODULE (RotationBySpeedModule)
+ RotationBySpeedModule ();
+
+ void Update (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex);
+ void CheckConsistency ();
+
+ inline MinMaxCurve& GetCurve() { return m_Curve; }
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+private:
+ MinMaxCurve m_Curve;
+ Vector2f m_Range;
+};
+
+#endif // SHURIKENMODULEROTATIONBYVELOCITY_H
diff --git a/Runtime/Graphics/ParticleSystem/Modules/RotationModule.cpp b/Runtime/Graphics/ParticleSystem/Modules/RotationModule.cpp
new file mode 100644
index 0000000..7f331d6
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/RotationModule.cpp
@@ -0,0 +1,83 @@
+#include "UnityPrefix.h"
+#include "RotationModule.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemUtils.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/Math/Vector2.h"
+
+struct DualMinMaxPolyCurves
+{
+ MinMaxOptimizedPolyCurves optRot;
+ MinMaxPolyCurves rot;
+};
+
+template<ParticleSystemCurveEvalMode mode>
+void UpdateTpl(const MinMaxCurve& curve, ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex)
+{
+ if ( !ps.usesRotationalSpeed ) return;
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ {
+ const float time = NormalizedTime(ps, q);
+ const float random = GenerateRandom(ps.randomSeed[q] + kParticleSystemRotationCurveId);
+ ps.rotationalSpeed[q] += Evaluate<mode> (curve, time, random);
+ }
+}
+
+template<bool isOptimized>
+void UpdateProceduralTpl(const DualMinMaxPolyCurves& curves, ParticleSystemParticles& ps)
+{
+ const size_t count = ps.array_size ();
+ for (int q=0; q<count; q++)
+ {
+ float time = NormalizedTime(ps, q);
+ float random = GenerateRandom(ps.randomSeed[q] + kParticleSystemRotationCurveId);
+ float range = ps.startLifetime[q];
+ float value;
+ if(isOptimized)
+ value = EvaluateIntegrated (curves.optRot, time, random);
+ else
+ value = EvaluateIntegrated (curves.rot, time, random);
+ ps.rotation[q] += value * range;
+ }
+}
+
+RotationModule::RotationModule() : ParticleSystemModule(false)
+{}
+
+void RotationModule::Update (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex)
+{
+ if (m_Curve.minMaxState == kMMCScalar)
+ UpdateTpl<kEMScalar>(m_Curve, ps, fromIndex, toIndex);
+ else if(m_Curve.IsOptimized() && m_Curve.UsesMinMax())
+ UpdateTpl<kEMOptimizedMinMax>(m_Curve, ps, fromIndex, toIndex);
+ else if(m_Curve.IsOptimized())
+ UpdateTpl<kEMOptimized>(m_Curve, ps, fromIndex, toIndex);
+ else
+ UpdateTpl<kEMSlow>(m_Curve, ps, fromIndex, toIndex);
+
+}
+
+void RotationModule::UpdateProcedural (const ParticleSystemState& state, ParticleSystemParticles& ps)
+{
+ DualMinMaxPolyCurves curves;
+ if(m_Curve.IsOptimized())
+ {
+ curves.optRot = m_Curve.polyCurves; curves.optRot.Integrate();
+ UpdateProceduralTpl<true>(curves, ps);
+ }
+ else
+ {
+ DebugAssert(CurvesSupportProcedural (m_Curve.editorCurves, m_Curve.minMaxState));
+ BuildCurves(curves.rot, m_Curve.editorCurves, m_Curve.GetScalar(), m_Curve.minMaxState); curves.rot.Integrate();
+ UpdateProceduralTpl<false>(curves, ps);
+ }
+}
+
+template<class TransferFunction>
+void RotationModule::Transfer (TransferFunction& transfer)
+{
+ ParticleSystemModule::Transfer (transfer);
+ transfer.Transfer (m_Curve, "curve");
+}
+INSTANTIATE_TEMPLATE_TRANSFER(RotationModule)
diff --git a/Runtime/Graphics/ParticleSystem/Modules/RotationModule.h b/Runtime/Graphics/ParticleSystem/Modules/RotationModule.h
new file mode 100644
index 0000000..494414e
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/RotationModule.h
@@ -0,0 +1,27 @@
+#ifndef SHURIKENMODULEROTATION_H
+#define SHURIKENMODULEROTATION_H
+
+#include "ParticleSystemModule.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemCurves.h"
+
+class RotationModule : public ParticleSystemModule
+{
+public:
+ DECLARE_MODULE (RotationModule)
+
+ RotationModule();
+
+ void Update (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex);
+ void UpdateProcedural (const ParticleSystemState& state, ParticleSystemParticles& ps);
+ void CheckConsistency() {};
+
+ inline MinMaxCurve& GetCurve() { return m_Curve; }
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+private:
+ MinMaxCurve m_Curve;
+};
+
+#endif // SHURIKENMODULEROTATION_H
diff --git a/Runtime/Graphics/ParticleSystem/Modules/ShapeModule.cpp b/Runtime/Graphics/ParticleSystem/Modules/ShapeModule.cpp
new file mode 100644
index 0000000..3cdb007
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/ShapeModule.cpp
@@ -0,0 +1,650 @@
+#include "UnityPrefix.h"
+#include "ShapeModule.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemUtils.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystem.h"
+#include "Runtime/Graphics/TriStripper.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/Geometry/ComputionalGeometry.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Utilities/StrideIterator.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+enum MeshDistributionMode
+{
+ kDistributionVertex,
+ kDistributionTriangle,
+};
+
+
+/// This gives a random barycentric coord (on the edge of triangle)
+// @TODO: Stupid: Make this in a faster way
+inline Vector3f RandomBarycentricCoordEdge (Rand& rand)
+{
+ float u = rand.GetFloat ();
+ float v = rand.GetFloat ();
+ if (u + v > 1.0F)
+ {
+ u = 1.0F - u;
+ v = 1.0F - v;
+ }
+ float w = 1.0F - u - v;
+
+ int edge = RangedRandom(rand, 0, 2);
+ if(0 == edge)
+ {
+ v += 0.5f * u;
+ w += 0.5f * u;
+ u = 0.0f;
+ }
+ else if(1 == edge)
+ {
+ u += 0.5f * v;
+ w += 0.5f * v;
+ v = 0.0f;
+ }
+ else
+ {
+ u += 0.5f * w;
+ v += 0.5f * w;
+ w = 0.0f;
+ }
+
+ return Vector3f (u, v, w);
+}
+
+
+// TODO: It could make sense to initialize in separated loops. i.e. separate position and velcoity vectors
+inline void EmitterStoreData(const Matrix4x4f& localToWorld, const Vector3f& scale, ParticleSystemParticles& ps, size_t q, Vector3f& pos, Vector3f& n, Rand& random, bool randomDirection)
+{
+ if(randomDirection)
+ n = RandomUnitVector (random);
+
+ n = NormalizeSafe(n);
+
+ pos = Scale(pos, scale);
+
+ Vector3f vel = Magnitude (ps.velocity[q]) * n;
+ vel = localToWorld.MultiplyVector3 (vel);
+
+ // @TODO: Sooo... why multiply point and then undo the result of it? Why not just MultiplyVector?
+ pos = localToWorld.MultiplyPoint3 (pos) - localToWorld.GetPosition();
+ ps.position[q] += pos;
+ ps.velocity[q] = vel;
+
+#if 0 // WIP code for converting to spherical
+ Vector3f sp = ps.position[q];
+ ps.position[q].x = Sqrt(sp.x*sp.x + sp.y*sp.y + sp.z*sp.z);
+ ps.position[q].y = acosf(sp.z/ps.position[q].x);
+ ps.position[q].z = acosf(sp.y/ps.position[q].x);
+#endif
+
+ if(ps.usesAxisOfRotation)
+ {
+ Vector3f tan = Cross (-n, Vector3f::zAxis);
+ if (SqrMagnitude (tan) <= 0.01)
+ tan = Cross (-pos, Vector3f::zAxis);
+ if (SqrMagnitude (tan) <= 0.01)
+ tan = Vector3f::yAxis;
+ ps.axisOfRotation[q] = Normalize (tan);
+ }
+}
+
+inline void EmitterStoreData(const Matrix4x4f& localToWorld, const Vector3f& scale, ParticleSystemParticles& ps, size_t q, Vector3f& pos, Vector3f& n, ColorRGBA32& color, Rand& random, bool randomDirection)
+{
+ EmitterStoreData(localToWorld, scale, ps, q, pos, n, random, randomDirection);
+ ps.color[q] *= color;
+}
+
+
+template<MeshDistributionMode distributionMode>
+void GetPositionMesh (Vector3f& pos,
+ Vector3f& n,
+ ColorRGBA32& color,
+ const ParticleSystemEmitterMeshVertex* vertexData,
+ const int vertexCount,
+ const MeshTriangleData* triangleData,
+ const UInt32 numPrimitives,
+ float totalTriangleArea,
+ Rand& random,
+ bool edge)
+{
+ // position/normal of particle is vertex/vertex normal from mesh
+ if(kDistributionVertex == distributionMode)
+ {
+ int vertexIndex = RangedRandom (random, 0, vertexCount);
+ pos = vertexData[vertexIndex].position;
+ n = vertexData[vertexIndex].normal;
+ color = vertexData[vertexIndex].color;
+ }
+ else if(kDistributionTriangle == distributionMode)
+ {
+ float randomArea = RangedRandom(random, 0.0f, totalTriangleArea);
+ float accArea = 0.0f;
+ UInt32 triangleIndex = 0;
+
+ for(UInt32 i = 0; i < numPrimitives; i++)
+ {
+ const MeshTriangleData& data = triangleData[i];
+ accArea += data.area;
+ if(accArea >= randomArea)
+ {
+ triangleIndex = i;
+ break;
+ }
+ }
+
+ const MeshTriangleData& data = triangleData[triangleIndex];
+ UInt16 a = data.indices[0];
+ UInt16 b = data.indices[1];
+ UInt16 c = data.indices[2];
+
+ Vector3f barycenter;
+ if(edge)
+ barycenter = RandomBarycentricCoordEdge (random);
+ else
+ barycenter = RandomBarycentricCoord (random);
+
+ // Interpolate vertex with barycentric coordinate
+ pos = barycenter.x * vertexData[a].position + barycenter.y * vertexData[b].position + barycenter.z * vertexData[c].position;
+ n = barycenter.x * vertexData[a].normal + barycenter.y * vertexData[b].normal + barycenter.z * vertexData[c].normal;
+
+ // TODO: Don't convert to floats!!!
+ ColorRGBAf color1 = vertexData[a].color;
+ ColorRGBAf color2 = vertexData[b].color;
+ ColorRGBAf color3 = vertexData[c].color;
+ color = barycenter.x * color1 + barycenter.y * color2 + barycenter.z * color3;
+ }
+}
+
+static bool CompareMeshTriangleData (const MeshTriangleData& a, const MeshTriangleData& b)
+{
+ return (a.area > b.area);
+}
+
+static float BuildMeshAreaTable(MeshTriangleData* triData, const StrideIterator<Vector3f> vertices, const UInt16* indices, int numTriangles)
+{
+ float result = 0.0f;
+ for(int i = 0; i < numTriangles; i++)
+ {
+ const UInt16 a = indices[i * 3 + 0];
+ const UInt16 b = indices[i * 3 + 1];
+ const UInt16 c = indices[i * 3 + 2];
+ float area = TriangleArea3D (vertices[a], vertices[b], vertices[c]);
+ result += area;
+
+ triData[i].indices[0] = a;
+ triData[i].indices[1] = b;
+ triData[i].indices[2] = c;
+ triData[i].area = area;
+ }
+
+ return result;
+}
+
+// ------------------------------------------------------------------------------------------
+
+ShapeModule::ShapeModule () : ParticleSystemModule(true)
+, m_Type (kCone)
+, m_RandomDirection (false)
+, m_Angle(25.0f)
+, m_Radius(1.0f)
+, m_Length(5.0f)
+, m_BoxX(1.0f)
+, m_BoxY(1.0f)
+, m_BoxZ(1.0f)
+, m_PlacementMode(kVertex)
+, m_CachedMesh(NULL)
+, m_MeshNode (NULL)
+{
+}
+
+void ShapeModule::Start (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const Matrix4x4f& matrix, size_t fromIndex, float t)
+{
+ DebugAssert(roState.lengthInSec > 0.0001f);
+ const float normalizedT = t / roState.lengthInSec;
+ DebugAssert (normalizedT >= 0.0f);
+ DebugAssert (normalizedT <= 1.0f);
+
+ Rand& random = GetRandom();
+
+ if (m_Type == kMesh)
+ {
+ if(!m_CachedMesh)
+ return;
+
+ if(!m_CachedVertexData.size())
+ return;
+
+ if(!m_CachedTriangleData.size())
+ return;
+
+ const ParticleSystemEmitterMeshVertex* vertexData = &m_CachedVertexData[0];
+ const int vertexCount = m_CachedVertexData.size();
+ size_t count = ps.array_size ();
+ switch(m_PlacementMode)
+ {
+ case kVertex:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector3f pos;
+ Vector3f n;
+ ColorRGBA32 color;
+ GetPositionMesh<kDistributionVertex>(pos, n, color, vertexData, vertexCount, NULL, 0, m_CachedTotalTriangleArea, random, false);
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, color, random, m_RandomDirection);
+ }
+ break;
+ }
+ case kEdge:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector3f pos;
+ Vector3f n;
+ ColorRGBA32 color;
+ GetPositionMesh<kDistributionTriangle>(pos, n, color, vertexData, vertexCount, m_CachedTriangleData.begin(), m_CachedTriangleData.size(), m_CachedTotalTriangleArea, random, true);
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, color, random, m_RandomDirection);
+ }
+ break;
+ }
+ case kTriangle:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector3f pos;
+ Vector3f n;
+ ColorRGBA32 color;
+ GetPositionMesh<kDistributionTriangle>(pos, n, color, vertexData, vertexCount, m_CachedTriangleData.begin(), m_CachedTriangleData.size(), m_CachedTotalTriangleArea, random, false);
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, color, random, m_RandomDirection);
+ }
+ break;
+ }
+ default:
+ {
+ DebugAssert(0 && "PlacementMode Not Supported");
+ }
+ }
+ }
+ else
+ {
+ const float r = m_Radius;
+
+ float a = Deg2Rad (m_Angle);
+ float sinA = Sin (a);
+ float cosA = Cos (a);
+ float length = m_Length;
+
+ const size_t count = ps.array_size ();
+ switch(m_Type)
+ {
+ case kSphere:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector3f pos = RandomPointInsideUnitSphere (random) * r;
+ Vector3f n = pos;
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, random, m_RandomDirection);
+ }
+ break;
+ }
+ case kSphereShell:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector3f pos = RandomUnitVector(random) * r;
+ Vector3f n = pos;
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, random, m_RandomDirection);
+ }
+ break;
+ }
+ case kHemiSphere:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector3f pos = RandomPointInsideUnitSphere (random) * r;
+ if (pos.z < 0.0f)
+ pos.z *= -1.0f;
+ Vector3f n = pos;
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, random, m_RandomDirection);
+ }
+ break;
+ }
+ case kHemiSphereShell:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector3f pos = RandomUnitVector (random) * r;
+ if (pos.z < 0.0f)
+ pos.z *= -1.0f;
+ Vector3f n = pos;
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, random, m_RandomDirection);
+ }
+ break;
+ }
+ case kCone:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector2f posXY = RandomPointInsideUnitCircle (random);
+ Vector2f nXY;
+ if(m_RandomDirection)
+ nXY = RandomPointInsideUnitCircle (random) * sinA;
+ else
+ nXY = Vector2f(posXY.x, posXY.y)* sinA;
+ Vector3f n (nXY.x, nXY.y, cosA);
+ Vector3f pos (posXY.x * r, posXY.y * r, 0.0f);
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, random, false);
+ }
+ break;
+ }
+ case kConeShell:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector2f posXY = NormalizeSafe(RandomUnitVector2 (random));
+
+ Vector2f nXY;
+ if(m_RandomDirection)
+ nXY = RandomPointInsideUnitCircle (random) * sinA;
+ else
+ nXY = Vector2f(posXY.x, posXY.y)* sinA;
+ Vector3f n (nXY.x, nXY.y, cosA);
+ Vector3f pos (posXY.x * r, posXY.y * r, 0.0f);
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, random, false);
+ }
+ break;
+ }
+ case kConeVolume:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector2f posXY = RandomPointInsideUnitCircle (random);
+ Vector2f nXY = Vector2f(posXY.x, posXY.y)* sinA;
+ Vector3f n (nXY.x, nXY.y, cosA);
+ Vector3f pos (posXY.x * r, posXY.y * r, 0.0f);
+ pos += length * Random01(random) * NormalizeSafe(n);
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, random, m_RandomDirection);
+ }
+ break;
+ }
+ case kConeVolumeShell:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector2f posXY = NormalizeSafe(RandomUnitVector2 (random));
+ Vector2f nXY = Vector2f(posXY.x, posXY.y)* sinA;
+ Vector3f n (nXY.x, nXY.y, cosA);
+ Vector3f pos = Vector3f(posXY.x * r, posXY.y * r, 0.0f);
+ pos += length * Random01(random) * NormalizeSafe(n);
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, random, m_RandomDirection);
+ }
+ break;
+ }
+ case kBox:
+ {
+ const Vector3f extents (0.5f * m_BoxX, 0.5f * m_BoxY, 0.5f * m_BoxZ);
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector3f pos = RandomPointInsideCube (random, extents);
+ Vector3f n = Vector3f::zAxis;
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, random, m_RandomDirection);
+ }
+ }
+ break;
+ default:
+ {
+ DebugAssert(0 && "Shape not supported");
+ }
+ }
+ }
+}
+
+void ShapeModule::CalculateProceduralBounds(MinMaxAABB& bounds, const Vector3f& emitterScale, Vector2f minMaxBounds) const
+{
+ DebugAssert(minMaxBounds.x <= minMaxBounds.y);
+
+ switch(m_Type)
+ {
+ case kSphere:
+ case kSphereShell:
+ bounds.m_Max = Vector3f(m_Radius, m_Radius, m_Radius);
+ bounds.m_Min = -bounds.m_Max;
+ break;
+ case kHemiSphere:
+ case kHemiSphereShell:
+ bounds.m_Max = Vector3f(m_Radius, m_Radius, m_Radius);
+ bounds.m_Min = Vector3f(-m_Radius, -m_Radius, 0.0f);
+ break;
+ case kCone:
+ case kConeShell:
+ bounds.m_Max = Vector3f(m_Radius, m_Radius, 0.0f);
+ bounds.m_Min = -bounds.m_Max;
+ break;
+ case kConeVolume:
+ case kConeVolumeShell:
+ {
+ const float a = Deg2Rad (m_Angle);
+ const float coneRadius2 = m_Radius + m_Length * Sin (a);
+ const float coneLength = m_Length * Cos (a);
+ bounds.m_Max = Vector3f(coneRadius2, coneRadius2, coneLength);
+ bounds.m_Min = -Vector3f(coneRadius2, coneRadius2, 0.0f);
+ break;
+ }
+ case kBox:
+ bounds.m_Max = Vector3f(m_BoxX, m_BoxY, m_BoxZ) * 0.5f;
+ bounds.m_Min = -bounds.m_Max;
+ break;
+ case kMesh:
+ {
+ if(m_CachedMesh)
+ bounds = m_CachedMesh->GetBounds(0);
+ else
+ bounds = MinMaxAABB(Vector3f::zero, Vector3f::zero);
+ break;
+ }
+ default:
+ {
+ AssertBreak(!"Shape not implemented.");
+ }
+ }
+
+ bounds.m_Min = Scale(bounds.m_Min, emitterScale);
+ bounds.m_Max = Scale(bounds.m_Max, emitterScale);
+
+ MinMaxAABB speedBounds;
+
+ // Cone and cone shell random direction only deviate inside the bound
+ if(m_RandomDirection && (m_Type != kCone) && (m_Type != kConeShell))
+ {
+ speedBounds.m_Max = Vector3f::one;
+ speedBounds.m_Min = -Vector3f::one;
+ minMaxBounds = Abs(minMaxBounds);
+ }
+ else
+ {
+ switch(m_Type)
+ {
+ case kSphere:
+ case kSphereShell:
+ case kMesh:
+ speedBounds.m_Max = Vector3f::one;
+ speedBounds.m_Min = -Vector3f::one;
+ break;
+ case kHemiSphere:
+ case kHemiSphereShell:
+ speedBounds.m_Max = Vector3f::one;
+ speedBounds.m_Min = Vector3f(-1.0f, -1.0f, 0.0f);
+ break;
+ case kCone:
+ case kConeShell:
+ case kConeVolume:
+ case kConeVolumeShell:
+ {
+ const float a = Deg2Rad (m_Angle);
+ const float sinA = Sin (a);
+ speedBounds.m_Max = Vector3f(sinA, sinA, 1.0f);
+ speedBounds.m_Min = Vector3f(-sinA, -sinA, 0.0f);
+ break;
+ }
+ case kBox:
+ speedBounds.m_Max = Vector3f::zAxis;
+ speedBounds.m_Min = Vector3f::zero;
+ break;
+ default:
+ {
+ AssertBreak(!"Shape not implemented.");
+ }
+ }
+ }
+
+ MinMaxAABB speedBound;
+ speedBound.m_Min = bounds.m_Min + speedBounds.m_Min * minMaxBounds.y;
+ speedBound.m_Max = bounds.m_Max + speedBounds.m_Max * minMaxBounds.y;
+ bounds.Encapsulate(speedBound);
+
+ MinMaxAABB negSpeedBound;
+ negSpeedBound.m_Min = speedBounds.m_Min * minMaxBounds.x;
+ negSpeedBound.m_Max = speedBounds.m_Max * minMaxBounds.x;
+ speedBound.m_Min = min(negSpeedBound.m_Min, negSpeedBound.m_Max);
+ speedBound.m_Max = max(negSpeedBound.m_Min, negSpeedBound.m_Max);
+ bounds.Encapsulate(speedBound);
+}
+
+void ShapeModule::CheckConsistency ()
+{
+ m_Type = clamp<int> (m_Type, kSphere, kMax-1);
+ m_PlacementMode = clamp<int> (m_PlacementMode, kVertex, kModeMax-1);
+
+ m_Angle = clamp(m_Angle, 0.0f, 90.0f);
+ m_Radius = max(0.01f, m_Radius);
+ m_Length = max(0.0f, m_Length);
+ m_BoxX = max(0.0f, m_BoxX);
+ m_BoxY = max(0.0f, m_BoxY);
+ m_BoxZ = max(0.0f, m_BoxZ);
+}
+
+void ShapeModule::AwakeFromLoad (ParticleSystem* system, const ParticleSystemReadOnlyState& roState)
+{
+ m_MeshNode.RemoveFromList();
+ m_MeshNode.SetData(system);
+ m_CachedMesh = m_Mesh;
+ if (m_CachedMesh != NULL)
+ m_CachedMesh->AddObjectUser( m_MeshNode );
+ DidModifyMeshData();
+
+ ResetSeed(roState);
+}
+
+void ShapeModule::ResetSeed(const ParticleSystemReadOnlyState& roState)
+{
+ if(roState.randomSeed == 0)
+ m_Random.SetSeed(GetGlobalRandomSeed ());
+ else
+ m_Random.SetSeed(roState.randomSeed);
+}
+
+void ShapeModule::DidDeleteMesh (ParticleSystem* system)
+{
+ m_CachedMesh = NULL;
+}
+
+void ShapeModule::DidModifyMeshData ()
+{
+ if (m_CachedMesh == NULL)
+ {
+ m_CachedTriangleData.resize_uninitialized(0);
+ m_CachedVertexData.resize_uninitialized(0);
+ m_CachedTotalTriangleArea = 0;
+ return;
+ }
+
+
+ const StrideIterator<Vector3f> vertexBuffer = m_CachedMesh->GetVertexBegin();
+ const UInt16* indexBuffer = m_CachedMesh->GetSubMeshBuffer16(0);
+ const SubMesh& submesh = m_CachedMesh->GetSubMeshFast (0);
+ if (submesh.topology == kPrimitiveTriangleStripDeprecated)
+ {
+ const int numTriangles = CountTrianglesInStrip(indexBuffer, submesh.indexCount);
+ const int capacity = numTriangles * 3;
+ UNITY_TEMP_VECTOR(UInt16) tempIndices(capacity);
+ Destripify(indexBuffer, submesh.indexCount, &tempIndices[0], capacity);
+ m_CachedTriangleData.resize_uninitialized(numTriangles);
+ m_CachedTotalTriangleArea = BuildMeshAreaTable(m_CachedTriangleData.begin(), vertexBuffer, &tempIndices[0], numTriangles);
+ }
+ else if (submesh.topology == kPrimitiveTriangles)
+ {
+ const int numTriangles = submesh.indexCount/3;
+ m_CachedTriangleData.resize_uninitialized(numTriangles);
+ m_CachedTotalTriangleArea = BuildMeshAreaTable(m_CachedTriangleData.begin(), vertexBuffer, indexBuffer, numTriangles);
+ }
+ else
+ {
+ m_CachedMesh = NULL;
+ }
+
+ // Optimization: This sorts so big triangles comes before small, which means finding the right triangle is faster
+ std::sort(m_CachedTriangleData.begin(), m_CachedTriangleData.begin() + m_CachedTriangleData.size(), CompareMeshTriangleData);
+
+ // Cache vertices
+ const int vertexCount = m_CachedMesh->GetVertexCount();
+ const StrideIterator<Vector3f> vertices = m_CachedMesh->GetVertexBegin();
+ const StrideIterator<Vector3f> normals = m_CachedMesh->GetNormalBegin();
+ const StrideIterator<ColorRGBA32> colors = m_CachedMesh->GetColorBegin();
+ m_CachedVertexData.resize_uninitialized(vertexCount);
+ for(int i = 0; i < vertexCount; i++)
+ {
+ m_CachedVertexData[i].position = vertices[i];
+
+ if(!normals.IsNull())
+ m_CachedVertexData[i].normal = normals[i];
+ else
+ m_CachedVertexData[i].normal = Vector3f::zero;
+
+ if(!colors.IsNull())
+ m_CachedVertexData[i].color = colors[i];
+ else
+ m_CachedVertexData[i].color = ColorRGBA32(0xffffffff);
+ }
+}
+
+Rand& ShapeModule::GetRandom()
+{
+#if UNITY_EDITOR
+ if(!IsWorldPlaying())
+ return m_EditorRandom;
+ else
+#endif
+ return m_Random;
+}
+
+template<class TransferFunction>
+void ShapeModule::Transfer (TransferFunction& transfer)
+{
+ transfer.SetVersion(2);
+ ParticleSystemModule::Transfer (transfer);
+ transfer.Transfer (m_Type, "type");
+
+ // Primitive
+ transfer.Transfer(m_Radius, "radius");
+ transfer.Transfer(m_Angle, "angle");
+ transfer.Transfer(m_Length, "length");
+ transfer.Transfer(m_BoxX, "boxX");
+ transfer.Transfer(m_BoxY, "boxY");
+ transfer.Transfer(m_BoxZ, "boxZ");
+
+ // Mesh
+ transfer.Transfer (m_PlacementMode, "placementMode");
+ TRANSFER (m_Mesh);
+
+ transfer.Transfer (m_RandomDirection, "randomDirection"); transfer.Align();
+
+ // In Unity 3.5 all cone emitters had random direction set to false, but behaved as if it was true
+ if(transfer.IsOldVersion(1))
+ if(kCone == m_Type)
+ m_RandomDirection = true;
+}
+
+INSTANTIATE_TEMPLATE_TRANSFER(ShapeModule)
+
diff --git a/Runtime/Graphics/ParticleSystem/Modules/ShapeModule.h b/Runtime/Graphics/ParticleSystem/Modules/ShapeModule.h
new file mode 100644
index 0000000..1f95d33
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/ShapeModule.h
@@ -0,0 +1,80 @@
+#ifndef SHURIKENMODULESHAPE_H
+#define SHURIKENMODULESHAPE_H
+
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "ParticleSystemModule.h"
+#include "Runtime/Math/Random/rand.h"
+#include "Runtime/Utilities/LinkedList.h"
+
+struct MeshTriangleData
+{
+ float area;
+ UInt16 indices[3];
+};
+
+struct ParticleSystemEmitterMeshVertex
+{
+ Vector3f position;
+ Vector3f normal;
+ ColorRGBA32 color;
+};
+
+class Mesh;
+
+class ShapeModule : public ParticleSystemModule
+{
+public:
+ DECLARE_MODULE (ShapeModule)
+ ShapeModule ();
+
+ enum MeshPlacementMode { kVertex, kEdge, kTriangle, kModeMax };
+ enum { kSphere, kSphereShell, kHemiSphere, kHemiSphereShell, kCone, kBox, kMesh, kConeShell, kConeVolume, kConeVolumeShell, kMax };
+
+ void AwakeFromLoad (ParticleSystem* system, const ParticleSystemReadOnlyState& roState);
+ void ResetSeed(const ParticleSystemReadOnlyState& roState);
+ void DidModifyMeshData ();
+ void DidDeleteMesh (ParticleSystem* system);
+
+ PPtr<Mesh> GetMeshEmitterShape () { return m_Mesh; }
+
+ void Start (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const Matrix4x4f& matrix, size_t fromIndex, float t);
+ void CalculateProceduralBounds(MinMaxAABB& bounds, const Vector3f& emitterScale, Vector2f minMaxBounds) const;
+ void CheckConsistency ();
+
+ inline void SetShapeType(int type) { m_Type = type; };
+ inline void SetRadius(float radius) { m_Radius = radius; };
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+private:
+ Rand& GetRandom();
+
+ int m_Type;
+
+ // Primitive stuff
+ float m_Radius;
+ float m_Angle;
+ float m_Length;
+ float m_BoxX;
+ float m_BoxY;
+ float m_BoxZ;
+
+ // Mesh stuff
+ int m_PlacementMode;
+ PPtr<Mesh> m_Mesh;
+ Mesh* m_CachedMesh;
+ dynamic_array<ParticleSystemEmitterMeshVertex> m_CachedVertexData;
+ dynamic_array<MeshTriangleData> m_CachedTriangleData;
+ float m_CachedTotalTriangleArea;
+ ListNode<Object> m_MeshNode;
+
+ bool m_RandomDirection;
+ Rand m_Random;
+#if UNITY_EDITOR
+public:
+ Rand m_EditorRandom;
+#endif
+};
+
+#endif // SHURIKENMODULESHAPE_H
diff --git a/Runtime/Graphics/ParticleSystem/Modules/SizeByVelocityModule.cpp b/Runtime/Graphics/ParticleSystem/Modules/SizeByVelocityModule.cpp
new file mode 100644
index 0000000..21cd5df
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/SizeByVelocityModule.cpp
@@ -0,0 +1,50 @@
+#include "UnityPrefix.h"
+#include "SizeByVelocityModule.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemUtils.h"
+
+SizeBySpeedModule::SizeBySpeedModule () : ParticleSystemModule(false)
+, m_Range (0.0f, 1.0f)
+{}
+
+template<ParticleSystemCurveEvalMode mode>
+void UpdateTpl(const MinMaxCurve& curve, const ParticleSystemParticles& ps, float* tempSize, size_t fromIndex, size_t toIndex, const Vector2f offsetScale)
+{
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ {
+ const Vector3f vel = ps.velocity[q] + ps.animatedVelocity[q];
+ const float t = InverseLerpFast01 (offsetScale, Magnitude (vel));
+ const float random = GenerateRandom(ps.randomSeed[q] + kParticleSystemSizeBySpeedCurveId);
+ tempSize[q] *= max<float> (0.0f, Evaluate<mode> (curve, t, random));
+ }
+}
+
+void SizeBySpeedModule::Update (const ParticleSystemParticles& ps, float* tempSize, size_t fromIndex, size_t toIndex)
+{
+ DebugAssert(toIndex <= ps.array_size ());
+ Vector2f offsetScale = CalculateInverseLerpOffsetScale(m_Range);
+ if (m_Curve.minMaxState == kMMCScalar)
+ UpdateTpl<kEMScalar> (m_Curve, ps, tempSize, fromIndex, toIndex, offsetScale);
+ else if (m_Curve.IsOptimized() && m_Curve.UsesMinMax())
+ UpdateTpl<kEMOptimizedMinMax> (m_Curve, ps, tempSize, fromIndex, toIndex, offsetScale);
+ else if(m_Curve.IsOptimized())
+ UpdateTpl<kEMOptimized> (m_Curve, ps, tempSize, fromIndex, toIndex, offsetScale);
+ else
+ UpdateTpl<kEMSlow> (m_Curve, ps, tempSize, fromIndex, toIndex, offsetScale);
+}
+
+void SizeBySpeedModule::CheckConsistency ()
+{
+ const float MyEpsilon = 0.001f;
+ m_Range.x = std::min (m_Range.x, m_Range.y - MyEpsilon);
+}
+
+template<class TransferFunction>
+void SizeBySpeedModule::Transfer (TransferFunction& transfer)
+{
+ ParticleSystemModule::Transfer (transfer);
+ transfer.Transfer (m_Curve, "curve");
+ transfer.Transfer (m_Range, "range");
+}
+INSTANTIATE_TEMPLATE_TRANSFER(SizeBySpeedModule)
diff --git a/Runtime/Graphics/ParticleSystem/Modules/SizeByVelocityModule.h b/Runtime/Graphics/ParticleSystem/Modules/SizeByVelocityModule.h
new file mode 100644
index 0000000..c8ad729
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/SizeByVelocityModule.h
@@ -0,0 +1,27 @@
+#ifndef SHURIKENMODULESIZEBYVELOCITY_H
+#define SHURIKENMODULESIZEBYVELOCITY_H
+
+#include "ParticleSystemModule.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemCurves.h"
+#include "Runtime/Math/Vector2.h"
+
+class SizeBySpeedModule : public ParticleSystemModule
+{
+public:
+ DECLARE_MODULE (SizeBySpeedModule)
+ SizeBySpeedModule ();
+
+ void Update (const ParticleSystemParticles& ps, float* tempSize, size_t fromIndex, size_t toIndex);
+ void CheckConsistency ();
+
+ inline MinMaxCurve& GetCurve() { return m_Curve; }
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+private:
+ MinMaxCurve m_Curve;
+ Vector2f m_Range;
+};
+
+#endif // SHURIKENMODULESIZEBYVELOCITY_H
diff --git a/Runtime/Graphics/ParticleSystem/Modules/SizeModule.cpp b/Runtime/Graphics/ParticleSystem/Modules/SizeModule.cpp
new file mode 100644
index 0000000..ad08a4a
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/SizeModule.cpp
@@ -0,0 +1,41 @@
+#include "UnityPrefix.h"
+#include "SizeModule.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemUtils.h"
+
+template<ParticleSystemCurveEvalMode mode>
+void UpdateTpl(const MinMaxCurve& curve, const ParticleSystemParticles& ps, float* tempSize, size_t fromIndex, size_t toIndex)
+{
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ {
+ const float time = NormalizedTime(ps, q);
+ const float random = GenerateRandom(ps.randomSeed[q] + kParticleSystemSizeCurveId);
+ tempSize[q] *= max<float>(0.0f, Evaluate<mode> (curve, time, random));
+ }
+}
+
+SizeModule::SizeModule() : ParticleSystemModule(false)
+{}
+
+void SizeModule::Update (const ParticleSystemParticles& ps, float* tempSize, size_t fromIndex, size_t toIndex)
+{
+ DebugAssert(toIndex <= ps.array_size ());
+ if(m_Curve.minMaxState == kMMCScalar)
+ UpdateTpl<kEMScalar>(m_Curve, ps, tempSize, fromIndex, toIndex);
+ else if (m_Curve.IsOptimized() && m_Curve.UsesMinMax ())
+ UpdateTpl<kEMOptimizedMinMax>(m_Curve, ps, tempSize, fromIndex, toIndex);
+ else if(m_Curve.IsOptimized())
+ UpdateTpl<kEMOptimized>(m_Curve, ps, tempSize, fromIndex, toIndex);
+ else
+ UpdateTpl<kEMSlow>(m_Curve, ps, tempSize, fromIndex, toIndex);
+}
+
+template<class TransferFunction>
+void SizeModule::Transfer (TransferFunction& transfer)
+{
+ ParticleSystemModule::Transfer (transfer);
+ transfer.Transfer (m_Curve, "curve");
+}
+INSTANTIATE_TEMPLATE_TRANSFER(SizeModule)
diff --git a/Runtime/Graphics/ParticleSystem/Modules/SizeModule.h b/Runtime/Graphics/ParticleSystem/Modules/SizeModule.h
new file mode 100644
index 0000000..3c92634
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/SizeModule.h
@@ -0,0 +1,27 @@
+#ifndef SHURIKENMODULESIZE_H
+#define SHURIKENMODULESIZE_H
+
+#include "ParticleSystemModule.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemCurves.h"
+
+class SizeModule : public ParticleSystemModule
+{
+public:
+ DECLARE_MODULE (SizeModule)
+
+ SizeModule();
+
+ void Update (const ParticleSystemParticles& ps, float* tempSize, size_t fromIndex, size_t toIndex);
+
+ void CheckConsistency () {};
+
+ inline MinMaxCurve& GetCurve() { return m_Curve; }
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+private:
+ MinMaxCurve m_Curve;
+};
+
+#endif // SHURIKENMODULESIZE_H
diff --git a/Runtime/Graphics/ParticleSystem/Modules/SubModule.cpp b/Runtime/Graphics/ParticleSystem/Modules/SubModule.cpp
new file mode 100644
index 0000000..895c444
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/SubModule.cpp
@@ -0,0 +1,148 @@
+#include "UnityPrefix.h"
+#include "SubModule.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "../ParticleSystemUtils.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystem.h" // Only because of PPtr comparison
+
+bool IsUsingSubEmitter(ParticleSystem* emitter)
+{
+ const void* shurikenSelf = NULL;
+ return emitter != shurikenSelf;
+}
+
+SubModule::SubModule () : ParticleSystemModule(false)
+{
+}
+
+void SubModule::Update (const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& ps, size_t fromIndex, size_t toIndex, float dt) const
+{
+ Assert(state.numCachedSubDataBirth <= kParticleSystemMaxNumEmitAccumulators);
+ for(int i = 0; i < state.numCachedSubDataBirth; i++)
+ {
+ const ParticleSystemSubEmitterData& data = state.cachedSubDataBirth[i];
+ //const bool culled = (data.maxLifetime < state.accumulatedDt);
+ const float startDelay = data.startDelayInSec;
+ const float length = data.lengthInSec;
+ for (int q = fromIndex; q < toIndex; ++q)
+ {
+ const float t = std::max(0.0f, ps.startLifetime[q] - ps.lifetime[q] - dt) - startDelay;
+ const bool started = (t >= 0.0f);
+ const bool ended = (t >= length);
+ if(started && !ended)
+ {
+ ParticleSystemEmissionState emissionState;
+ emissionState.m_ToEmitAccumulator = ps.emitAccumulator[i][q];
+ RecordEmit(emissionState, data, roState, state, ps, kParticleSystemSubTypeBirth, i, q, t, dt, length);
+ ps.emitAccumulator[i][q] = emissionState.m_ToEmitAccumulator;
+ }
+ }
+ }
+}
+
+void SubModule::RemoveDuplicatePtrs (ParticleSystem** shurikens)
+{
+ for(int i = 0; i < kParticleSystemMaxSubTotal-1; i++)
+ for(int j = i+1; j < kParticleSystemMaxSubTotal; j++)
+ if(shurikens[i] && (shurikens[i] == shurikens[j]))
+ shurikens[i] = NULL;
+}
+
+// This can not be cached between frames since the referenced particle systems might be deleted at any point.
+int SubModule::GetSubEmitterPtrs (ParticleSystem** subEmitters) const
+{
+ for(int i = 0; i < kParticleSystemMaxSubTotal; i++)
+ subEmitters[i] = NULL;
+
+ int numSubEmitters = 0;
+ for(int i = 0; i < kParticleSystemMaxSubBirth; i++)
+ {
+ ParticleSystem* subParticleSystem = m_SubEmittersBirth[i];
+ if (IsUsingSubEmitter(subParticleSystem))
+ subEmitters[numSubEmitters++] = subParticleSystem;
+ }
+ for(int i = 0; i < kParticleSystemMaxSubCollision; i++)
+ {
+ ParticleSystem* subParticleSystem = m_SubEmittersCollision[i];
+ if (IsUsingSubEmitter(subParticleSystem))
+ subEmitters[numSubEmitters++] = subParticleSystem;
+ }
+ for(int i = 0; i < kParticleSystemMaxSubDeath; i++)
+ {
+ ParticleSystem* subParticleSystem = m_SubEmittersDeath[i];
+ if (IsUsingSubEmitter(subParticleSystem))
+ subEmitters[numSubEmitters++] = subParticleSystem;
+ }
+ return numSubEmitters;
+}
+
+int SubModule::GetSubEmitterPtrsBirth (ParticleSystem** subEmitters) const
+{
+ int numSubEmitters = 0;
+ for(int i = 0; i < kParticleSystemMaxSubBirth; i++)
+ {
+ ParticleSystem* subParticleSystem = m_SubEmittersBirth[i];
+ if (IsUsingSubEmitter(subParticleSystem))
+ subEmitters[numSubEmitters++] = subParticleSystem;
+ }
+ return numSubEmitters;
+}
+
+int SubModule::GetSubEmitterPtrsCollision (ParticleSystem** subEmitters) const
+{
+ int numSubEmitters = 0;
+ for(int i = 0; i < kParticleSystemMaxSubCollision; i++)
+ {
+ ParticleSystem* subParticleSystem = m_SubEmittersCollision[i];
+ if (IsUsingSubEmitter(subParticleSystem))
+ subEmitters[numSubEmitters++] = subParticleSystem;
+ }
+ return numSubEmitters;
+}
+
+int SubModule::GetSubEmitterPtrsDeath(ParticleSystem** subEmitters) const
+{
+ int numSubEmitters = 0;
+ for(int i = 0; i < kParticleSystemMaxSubDeath; i++)
+ {
+ ParticleSystem* subParticleSystem = m_SubEmittersDeath[i];
+ if (IsUsingSubEmitter(subParticleSystem))
+ subEmitters[numSubEmitters++] = subParticleSystem;
+ }
+ return numSubEmitters;
+}
+
+int SubModule::GetSubEmitterTypeCount(ParticleSystemSubType type) const
+{
+ int count = 0;
+ const void* shurikenSelf = NULL;
+ if(type == kParticleSystemSubTypeBirth) {
+ for(int i = 0; i < kParticleSystemMaxSubBirth; i++)
+ if(m_SubEmittersBirth[i] != shurikenSelf)
+ count++;
+ } else if(type == kParticleSystemSubTypeCollision) {
+ for(int i = 0; i < kParticleSystemMaxSubCollision; i++)
+ if(m_SubEmittersCollision[i] != shurikenSelf)
+ count++;
+ } else if(type == kParticleSystemSubTypeDeath) {
+ for(int i = 0; i < kParticleSystemMaxSubDeath; i++)
+ if(m_SubEmittersDeath[i] != shurikenSelf)
+ count++;
+ } else {
+ Assert(!"Sub emitter type not implemented.");
+ }
+ return count;
+}
+
+template<class TransferFunction>
+void SubModule::Transfer (TransferFunction& transfer)
+{
+ ParticleSystemModule::Transfer (transfer);
+ transfer.Transfer (m_SubEmittersBirth[0], "subEmitterBirth");
+ transfer.Transfer (m_SubEmittersBirth[1], "subEmitterBirth1");
+ transfer.Transfer (m_SubEmittersCollision[0], "subEmitterCollision");
+ transfer.Transfer (m_SubEmittersCollision[1], "subEmitterCollision1");
+ transfer.Transfer (m_SubEmittersDeath[0], "subEmitterDeath");
+ transfer.Transfer (m_SubEmittersDeath[1], "subEmitterDeath1");
+}
+INSTANTIATE_TEMPLATE_TRANSFER(SubModule)
diff --git a/Runtime/Graphics/ParticleSystem/Modules/SubModule.h b/Runtime/Graphics/ParticleSystem/Modules/SubModule.h
new file mode 100644
index 0000000..093c8ba
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/SubModule.h
@@ -0,0 +1,38 @@
+#ifndef SHURIKENMODULESUB_H
+#define SHURIKENMODULESUB_H
+
+#include "ParticleSystemModule.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+
+class ParticleSystem;
+struct ParticleSystemParticles;
+
+
+class SubModule : public ParticleSystemModule
+{
+public:
+ DECLARE_MODULE (SubModule)
+ SubModule ();
+
+ void Update (const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& ps, size_t fromIndex, size_t toIndex, float dt) const;
+ void CheckConsistency() {};
+
+ int GetSubEmitterTypeCount(ParticleSystemSubType type) const;
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+ static void RemoveDuplicatePtrs (ParticleSystem** shurikens);
+ int GetSubEmitterPtrs (ParticleSystem** shurikens) const;
+ int GetSubEmitterPtrsBirth (ParticleSystem** shurikens) const;
+ int GetSubEmitterPtrsCollision (ParticleSystem** shurikens) const;
+ int GetSubEmitterPtrsDeath(ParticleSystem** shurikens) const;
+
+
+private:
+ PPtr<ParticleSystem> m_SubEmittersBirth[kParticleSystemMaxSubBirth];
+ PPtr<ParticleSystem> m_SubEmittersCollision[kParticleSystemMaxSubCollision];
+ PPtr<ParticleSystem> m_SubEmittersDeath[kParticleSystemMaxSubDeath];
+};
+
+#endif // SHURIKENMODULESUB_H
diff --git a/Runtime/Graphics/ParticleSystem/Modules/UVModule.cpp b/Runtime/Graphics/ParticleSystem/Modules/UVModule.cpp
new file mode 100644
index 0000000..67d49b3
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/UVModule.cpp
@@ -0,0 +1,105 @@
+#include "UnityPrefix.h"
+#include "UVModule.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemParticle.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemCurves.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemUtils.h"
+#include "Runtime/Math/Random/Random.h"
+
+template<ParticleSystemCurveEvalMode mode>
+void UpdateWholeSheetTpl(float cycles, const MinMaxCurve& curve, const ParticleSystemParticles& ps, float* tempSheetIndex, size_t fromIndex, size_t toIndex)
+{
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ tempSheetIndex[q] = Repeat (cycles * Evaluate(curve, NormalizedTime(ps, q), GenerateRandom(ps.randomSeed[q] + kParticleSystemUVCurveId)), 1.0f);
+}
+
+UVModule::UVModule () : ParticleSystemModule(false)
+, m_TilesX (1), m_TilesY (1)
+, m_AnimationType (kWholeSheet)
+, m_RowIndex (0)
+, m_Cycles (1.0f)
+, m_RandomRow (true)
+{}
+
+void UVModule::Update (const ParticleSystemParticles& ps, float* tempSheetIndex, size_t fromIndex, size_t toIndex)
+{
+ const float cycles = m_Cycles;
+
+ DebugAssert(toIndex <= ps.array_size ());
+ if (m_AnimationType == kSingleRow) // row
+ {
+ int rows = m_TilesY;
+ float animRange = (1.0f / (m_TilesX * rows)) * m_TilesX;
+ if(m_RandomRow)
+ {
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ {
+ const float t = cycles * Evaluate(m_Curve, NormalizedTime(ps, q), GenerateRandom(ps.randomSeed[q] + kParticleSystemUVCurveId));
+ const float x = Repeat (t, 1.0f);
+ const float randomValue = GenerateRandom(ps.randomSeed[q] + kParticleSystemUVRowSelectionId);
+ const float startRow = Floorf (randomValue * rows);
+ float from = startRow * animRange;
+ float to = from + animRange;
+ tempSheetIndex[q] = Lerp (from, to, x);
+ }
+ }
+ else
+ {
+ const float startRow = Floorf(m_RowIndex * animRange * rows);
+ float from = startRow * animRange;
+ float to = from + animRange;
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ {
+ const float t = cycles * Evaluate(m_Curve, NormalizedTime(ps, q), GenerateRandom(ps.randomSeed[q] + kParticleSystemUVCurveId));
+ const float x = Repeat (t, 1.0f);
+ tempSheetIndex[q] = Lerp (from, to, x);
+ }
+ }
+ }
+ else if (m_AnimationType == kWholeSheet) // grid || row
+ {
+ if(m_Curve.minMaxState == kMMCScalar)
+ UpdateWholeSheetTpl<kEMScalar>(m_Cycles, m_Curve, ps, tempSheetIndex, fromIndex, toIndex);
+ else if (m_Curve.IsOptimized() && m_Curve.UsesMinMax ())
+ UpdateWholeSheetTpl<kEMOptimizedMinMax>(m_Cycles, m_Curve, ps, tempSheetIndex, fromIndex, toIndex);
+ else if(m_Curve.IsOptimized())
+ UpdateWholeSheetTpl<kEMOptimized>(m_Cycles, m_Curve, ps, tempSheetIndex, fromIndex, toIndex);
+ else
+ UpdateWholeSheetTpl<kEMSlow>(m_Cycles, m_Curve, ps, tempSheetIndex, fromIndex, toIndex);
+ }
+ else
+ {
+ Assert(!"Animation mode not implemented!");
+ }
+}
+
+void UVModule::CheckConsistency ()
+{
+ m_AnimationType = clamp<int> (m_AnimationType, 0, kNumAnimationTypes-1);
+ m_TilesX = std::max<int> (1, m_TilesX);
+ m_TilesY = std::max<int> (1, m_TilesY);
+ m_Cycles = std::max<int> (1, (int)m_Cycles);
+ m_RowIndex = clamp<int> (m_RowIndex, 0, m_TilesY-1);
+ m_Curve.SetScalar(clamp<float> (m_Curve.GetScalar(), 0.0f, 1.0f));
+}
+
+void UVModule::GetNumTiles(int& uvTilesX, int& uvTilesY) const
+{
+ uvTilesX = m_TilesX;
+ uvTilesY = m_TilesY;
+}
+
+template<class TransferFunction>
+void UVModule::Transfer (TransferFunction& transfer)
+{
+ ParticleSystemModule::Transfer (transfer);
+ transfer.Transfer (m_Curve, "frameOverTime");
+ transfer.Transfer (m_TilesX, "tilesX");
+ transfer.Transfer (m_TilesY, "tilesY");
+ transfer.Transfer (m_AnimationType, "animationType");
+ transfer.Transfer (m_RowIndex, "rowIndex");
+ transfer.Transfer (m_Cycles, "cycles");
+ transfer.Transfer (m_RandomRow, "randomRow"); transfer.Align ();
+}
+INSTANTIATE_TEMPLATE_TRANSFER(UVModule)
diff --git a/Runtime/Graphics/ParticleSystem/Modules/UVModule.h b/Runtime/Graphics/ParticleSystem/Modules/UVModule.h
new file mode 100644
index 0000000..5ed9e7f
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/UVModule.h
@@ -0,0 +1,36 @@
+#ifndef SHURIKENMODULEUV_H
+#define SHURIKENMODULEUV_H
+
+#include "ParticleSystemModule.h"
+#include "Runtime/Graphics/ParticleSystem/PolynomialCurve.h"
+
+struct ParticleSystemParticles;
+
+class UVModule : public ParticleSystemModule
+{
+public:
+ DECLARE_MODULE (UVModule)
+ UVModule ();
+
+ void Update (const ParticleSystemParticles& ps, float* tempSheetIndex, size_t fromIndex, size_t toIndex);
+ void CheckConsistency ();
+
+ inline MinMaxCurve& GetCurve() { return m_Curve; }
+
+ void GetNumTiles(int& uvTilesX, int& uvTilesY) const;
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+private:
+ enum { kWholeSheet, kSingleRow, kNumAnimationTypes };
+
+ MinMaxCurve m_Curve;
+ int m_TilesX, m_TilesY;
+ int m_AnimationType;
+ int m_RowIndex;
+ float m_Cycles;
+ bool m_RandomRow;
+};
+
+#endif // SHURIKENMODULEUV_H
diff --git a/Runtime/Graphics/ParticleSystem/Modules/VelocityModule.cpp b/Runtime/Graphics/ParticleSystem/Modules/VelocityModule.cpp
new file mode 100644
index 0000000..edb7ed9
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/VelocityModule.cpp
@@ -0,0 +1,127 @@
+#include "UnityPrefix.h"
+#include "VelocityModule.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "../ParticleSystemUtils.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/Math/Matrix4x4.h"
+
+template<ParticleSystemCurveEvalMode mode>
+void UpdateTpl(const MinMaxCurve& x, const MinMaxCurve& y, const MinMaxCurve& z, ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex, bool transform, const Matrix4x4f& matrix)
+{
+ for (size_t q = fromIndex; q < toIndex; ++q)
+ {
+ Vector3f random;
+ GenerateRandom3(random, ps.randomSeed[q] + kParticleSystemVelocityCurveId);
+
+ const float normalizedTime = NormalizedTime (ps, q);
+ Vector3f vel = Vector3f (Evaluate<mode> (x, normalizedTime, random.x), Evaluate<mode> (y, normalizedTime, random.y), Evaluate<mode> (z, normalizedTime, random.z));
+ if(transform)
+ vel = matrix.MultiplyVector3 (vel);
+ ps.animatedVelocity[q] += vel;
+ }
+}
+
+template<bool isOptimized>
+void UpdateProceduralTpl(const DualMinMax3DPolyCurves& curves, const MinMaxCurve& x, const MinMaxCurve& y, const MinMaxCurve& z, ParticleSystemParticles& ps, const Matrix4x4f& matrix, bool transform)
+{
+ const size_t count = ps.array_size ();
+ for (int q=0;q<count;q++)
+ {
+ Vector3f random;
+ GenerateRandom3(random, ps.randomSeed[q] + kParticleSystemVelocityCurveId);
+ const float time = NormalizedTime(ps, q);
+
+ Vector3f delta;
+ if(isOptimized)
+ delta = Vector3f(EvaluateIntegrated(curves.optX, time, random.x), EvaluateIntegrated(curves.optY, time, random.y), EvaluateIntegrated(curves.optZ, time, random.z)) * ps.startLifetime[q];
+ else
+ delta = Vector3f(EvaluateIntegrated(curves.x, time, random.x), EvaluateIntegrated(curves.y, time, random.y), EvaluateIntegrated(curves.z, time, random.z)) * ps.startLifetime[q];
+
+ Vector3f velocity (Evaluate(x, time, random.x), Evaluate(y, time, random.y), Evaluate(z, time, random.z));
+ if(transform)
+ {
+ delta = matrix.MultiplyVector3 (delta);
+ velocity = matrix.MultiplyVector3 (velocity);
+ }
+ ps.position[q] += delta;
+ ps.animatedVelocity[q] += velocity;
+ }
+}
+
+VelocityModule::VelocityModule () : ParticleSystemModule(false)
+, m_InWorldSpace (false)
+{}
+
+void VelocityModule::Update (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex)
+{
+ Matrix4x4f matrix;
+ bool transform = GetTransformationMatrix(matrix, !roState.useLocalSpace, m_InWorldSpace, state.localToWorld);
+
+ bool usesScalar = (m_X.minMaxState == kMMCScalar) && (m_Y.minMaxState == kMMCScalar) && (m_Z.minMaxState == kMMCScalar);
+ bool isOptimized = m_X.IsOptimized() && m_Y.IsOptimized() && m_Z.IsOptimized();
+ bool usesMinMax = m_X.UsesMinMax() && m_Y.UsesMinMax() && m_Z.UsesMinMax();
+ if(usesScalar)
+ UpdateTpl<kEMScalar>(m_X, m_Y, m_Z, ps, fromIndex, toIndex, transform, matrix);
+ else if(isOptimized && usesMinMax)
+ UpdateTpl<kEMOptimizedMinMax>(m_X, m_Y, m_Z, ps, fromIndex, toIndex, transform, matrix);
+ else if(isOptimized)
+ UpdateTpl<kEMOptimized>(m_X, m_Y, m_Z, ps, fromIndex, toIndex, transform, matrix);
+ else
+ UpdateTpl<kEMSlow>(m_X, m_Y, m_Z, ps, fromIndex, toIndex, transform, matrix);
+
+}
+
+void VelocityModule::UpdateProcedural (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps)
+{
+ Matrix4x4f matrix;
+ bool transform = GetTransformationMatrix(matrix, !roState.useLocalSpace, m_InWorldSpace, state.localToWorld);
+
+ DualMinMax3DPolyCurves curves;
+ if(m_X.IsOptimized() && m_Y.IsOptimized() && m_Z.IsOptimized())
+ {
+ curves.optX = m_X.polyCurves; curves.optX.Integrate();
+ curves.optY = m_Y.polyCurves; curves.optY.Integrate();
+ curves.optZ = m_Z.polyCurves; curves.optZ.Integrate();
+ UpdateProceduralTpl<true>(curves, m_X, m_Y, m_Z, ps, matrix, transform);
+ }
+ else
+ {
+ DebugAssert(CurvesSupportProcedural (m_X.editorCurves, m_X.minMaxState));
+ DebugAssert(CurvesSupportProcedural (m_Y.editorCurves, m_Y.minMaxState));
+ DebugAssert(CurvesSupportProcedural (m_Z.editorCurves, m_Z.minMaxState));
+ BuildCurves(curves.x, m_X.editorCurves, m_X.GetScalar(), m_X.minMaxState); curves.x.Integrate();
+ BuildCurves(curves.y, m_Y.editorCurves, m_Y.GetScalar(), m_Y.minMaxState); curves.y.Integrate();
+ BuildCurves(curves.z, m_Z.editorCurves, m_Z.GetScalar(), m_Z.minMaxState); curves.z.Integrate();
+ UpdateProceduralTpl<false>(curves, m_X, m_Y, m_Z, ps, matrix, transform);
+ }
+}
+
+void VelocityModule::CalculateProceduralBounds(MinMaxAABB& bounds, const Matrix4x4f& localToWorld, float maxLifeTime)
+{
+ Vector2f xRange = m_X.FindMinMaxIntegrated();
+ Vector2f yRange = m_Y.FindMinMaxIntegrated();
+ Vector2f zRange = m_Z.FindMinMaxIntegrated();
+ bounds.m_Min = Vector3f(xRange.x, yRange.x, zRange.x) * maxLifeTime;
+ bounds.m_Max = Vector3f(xRange.y, yRange.y, zRange.y) * maxLifeTime;
+ if(m_InWorldSpace)
+ {
+ Matrix4x4f matrix;
+ Matrix4x4f::Invert_General3D(localToWorld, matrix);
+ matrix.SetPosition(Vector3f::zero);
+ AABB aabb = bounds;
+ TransformAABBSlow(aabb, matrix, aabb);
+ bounds = aabb;
+ }
+}
+
+template<class TransferFunction>
+void VelocityModule::Transfer (TransferFunction& transfer)
+{
+ ParticleSystemModule::Transfer (transfer);
+ transfer.Transfer (m_X, "x");
+ transfer.Transfer (m_Y, "y");
+ transfer.Transfer (m_Z, "z");
+ transfer.Transfer (m_InWorldSpace, "inWorldSpace"); transfer.Align();
+}
+INSTANTIATE_TEMPLATE_TRANSFER(VelocityModule)
diff --git a/Runtime/Graphics/ParticleSystem/Modules/VelocityModule.h b/Runtime/Graphics/ParticleSystem/Modules/VelocityModule.h
new file mode 100644
index 0000000..bf76f28
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/VelocityModule.h
@@ -0,0 +1,36 @@
+#ifndef SHURIKENMODULEVELOCITY_H
+#define SHURIKENMODULEVELOCITY_H
+
+#include "ParticleSystemModule.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemCurves.h"
+#include "Runtime/Math/Random/rand.h"
+
+class VelocityModule : public ParticleSystemModule
+{
+public:
+ DECLARE_MODULE (VelocityModule)
+ VelocityModule ();
+
+ void Update (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex);
+ void UpdateProcedural (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps);
+ void CalculateProceduralBounds(MinMaxAABB& bounds, const Matrix4x4f& localToWorld, float maxLifeTime);
+ void CheckConsistency() {};
+
+ inline MinMaxCurve& GetXCurve() { return m_X; };
+ inline MinMaxCurve& GetYCurve() { return m_Y; };
+ inline MinMaxCurve& GetZCurve() { return m_Z; };
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+private:
+ MinMaxCurve m_X;
+ MinMaxCurve m_Y;
+ MinMaxCurve m_Z;
+ bool m_InWorldSpace;
+
+ Rand m_Random;
+};
+
+
+#endif // SHURIKENMODULEVELOCITY_H
diff --git a/Runtime/Graphics/ParticleSystem/ParticleCollisionEvents.cpp b/Runtime/Graphics/ParticleSystem/ParticleCollisionEvents.cpp
new file mode 100644
index 0000000..dfefc6a
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/ParticleCollisionEvents.cpp
@@ -0,0 +1,122 @@
+#include "UnityPrefix.h"
+#include "ParticleCollisionEvents.h"
+
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Threads/Thread.h"
+
+CollisionEvents::CollisionEvents (): currentCollisionEventThreadArray(0)
+{
+}
+
+void CollisionEvents::Clear ()
+{
+ collisionEvents[0].clear ();
+ collisionEvents[1].clear ();
+}
+
+bool CollisionEvents::AddEvent (const ParticleCollisionEvent& event)
+{
+ GetCollisionEventThreadArray ().push_back (event);
+ return true;
+}
+
+int CollisionEvents::GetCollisionEventCount () const
+{
+ return GetCollisionEventScriptArray ().size ();
+}
+
+void CollisionEvents::SwapCollisionEventArrays ()
+{
+ Assert (Thread::CurrentThreadIsMainThread ());
+ currentCollisionEventThreadArray = (currentCollisionEventThreadArray+1)%2;
+ collisionEvents[currentCollisionEventThreadArray].clear ();
+}
+
+dynamic_array<ParticleCollisionEvent>& CollisionEvents::GetCollisionEventThreadArray ()
+{
+ return collisionEvents[currentCollisionEventThreadArray];
+}
+
+const dynamic_array<ParticleCollisionEvent>& CollisionEvents::GetCollisionEventScriptArray () const
+{
+ const int currentCollisionEventScriptArray = (currentCollisionEventThreadArray+1)%2;
+ return collisionEvents[currentCollisionEventScriptArray];
+}
+
+struct SortCollisionEventsByGameObject {
+ bool operator()( const ParticleCollisionEvent& ra, const ParticleCollisionEvent& rb ) const;
+};
+
+bool SortCollisionEventsByGameObject::operator()( const ParticleCollisionEvent& ra, const ParticleCollisionEvent& rb ) const
+{
+ return ra.m_RigidBodyOrColliderInstanceID < rb.m_RigidBodyOrColliderInstanceID;
+}
+
+void CollisionEvents::SortCollisionEventThreadArray ()
+{
+ std::sort (GetCollisionEventThreadArray ().begin (), GetCollisionEventThreadArray ().end (), SortCollisionEventsByGameObject ());
+}
+
+static GameObject* GetGameObjectFromInstanceID (int instanceId)
+{
+ Object* temp = Object::IDToPointer (instanceId);
+ if ( temp )
+ {
+ return (reinterpret_cast<Unity::Component*> (temp))->GetGameObjectPtr ();
+ }
+ return NULL;
+}
+
+static int GetGameObjectIDFromInstanceID (int instanceId)
+{
+ Object* temp = Object::IDToPointer (instanceId);
+ if ( temp )
+ {
+ return (reinterpret_cast<Unity::Component*> (temp))->GetGameObjectInstanceID ();
+ }
+ return 0;
+}
+
+void CollisionEvents::SendCollisionEvents (Unity::Component& particleSystem) const
+{
+ const dynamic_array<ParticleCollisionEvent>& scriptEventArray = GetCollisionEventScriptArray ();
+ GameObject* pParticleSystem = &particleSystem.GetGameObject ();
+ GameObject* collideeGO = NULL;
+ int currentId = -1;
+ for (int e = 0; e < scriptEventArray.size (); ++e)
+ {
+ if (currentId != scriptEventArray[e].m_RigidBodyOrColliderInstanceID)
+ {
+ collideeGO = GetGameObjectFromInstanceID (scriptEventArray[e].m_RigidBodyOrColliderInstanceID);
+ if (!collideeGO)
+ continue;
+ currentId = scriptEventArray[e].m_RigidBodyOrColliderInstanceID;
+ pParticleSystem->SendMessage (kParticleCollisionEvent, collideeGO, ClassID (GameObject)); // send message to particle system
+ collideeGO->SendMessage (kParticleCollisionEvent, pParticleSystem, ClassID (GameObject)); // send message to object collided with
+ }
+ }
+}
+
+int CollisionEvents::GetCollisionEvents (int instanceId, MonoParticleCollisionEvent* collisionEvents, int size) const
+{
+ const dynamic_array<ParticleCollisionEvent>& scriptEventArray = GetCollisionEventScriptArray ();
+ dynamic_array<ParticleCollisionEvent>::const_iterator iter = scriptEventArray.begin ();
+ for (; iter != scriptEventArray.end (); ++iter)
+ {
+ if (instanceId == GetGameObjectIDFromInstanceID (iter->m_RigidBodyOrColliderInstanceID))
+ {
+ int count = 0;
+ while (iter != scriptEventArray.end () && GetGameObjectIDFromInstanceID (iter->m_RigidBodyOrColliderInstanceID) == instanceId && count < size)
+ {
+ collisionEvents[count].m_Intersection = iter->m_Intersection;
+ collisionEvents[count].m_Normal = iter->m_Normal;
+ collisionEvents[count].m_Velocity = iter->m_Velocity;
+ collisionEvents[count].m_ColliderInstanceID = iter->m_ColliderInstanceID;
+ count++;
+ iter++;
+ }
+ return count;
+ }
+ }
+ return 0;
+}
diff --git a/Runtime/Graphics/ParticleSystem/ParticleCollisionEvents.h b/Runtime/Graphics/ParticleSystem/ParticleCollisionEvents.h
new file mode 100644
index 0000000..98260ee
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/ParticleCollisionEvents.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+
+struct ParticleCollisionEvent
+{
+ ParticleCollisionEvent (const Vector3f& intersection, const Vector3f& normal, const Vector3f& velocity, int colliderInstanceID, int rigidBodyOrColliderInstanceID);
+ Vector3f m_Intersection;
+ Vector3f m_Normal;
+ Vector3f m_Velocity;
+ int m_ColliderInstanceID;
+ int m_RigidBodyOrColliderInstanceID; // This can be a Collider or a RigidBody component
+};
+
+struct MonoParticleCollisionEvent
+{
+ Vector3f m_Intersection;
+ Vector3f m_Normal;
+ Vector3f m_Velocity;
+ int m_ColliderInstanceID;
+};
+
+struct CollisionEvents
+{
+ CollisionEvents ();
+ dynamic_array<ParticleCollisionEvent> collisionEvents[2];
+ int currentCollisionEventThreadArray;
+
+ void Clear ();
+ bool AddEvent (const ParticleCollisionEvent& event);
+ int GetCollisionEventCount () const;
+ void SwapCollisionEventArrays ();
+ void SortCollisionEventThreadArray ();
+ void SendCollisionEvents (Unity::Component& particleSystem) const;
+ int GetCollisionEvents (int instanceId, MonoParticleCollisionEvent* collisionEvents, int size) const;
+ dynamic_array<ParticleCollisionEvent>& GetCollisionEventThreadArray ();
+ const dynamic_array<ParticleCollisionEvent>& GetCollisionEventScriptArray () const;
+};
diff --git a/Runtime/Graphics/ParticleSystem/ParticleSystem.cpp b/Runtime/Graphics/ParticleSystem/ParticleSystem.cpp
new file mode 100644
index 0000000..47e952f
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/ParticleSystem.cpp
@@ -0,0 +1,2110 @@
+#include "UnityPrefix.h"
+#include "ParticleSystem.h"
+#include "ParticleSystemRenderer.h"
+#include "ParticleSystemCurves.h"
+#include "ParticleSystemUtils.h"
+#include "Modules/ParticleSystemModule.h"
+#include "Modules/InitialModule.h"
+#include "Modules/ShapeModule.h"
+#include "Modules/EmissionModule.h"
+#include "Modules/SizeModule.h"
+#include "Modules/RotationModule.h"
+#include "Modules/ColorModule.h"
+#include "Modules/UVModule.h"
+#include "Modules/VelocityModule.h"
+#include "Modules/ForceModule.h"
+#include "Modules/ExternalForcesModule.h"
+#include "Modules/ClampVelocityModule.h"
+#include "Modules/SizeByVelocityModule.h"
+#include "Modules/RotationByVelocityModule.h"
+#include "Modules/ColorByVelocityModule.h"
+#include "Modules/CollisionModule.h"
+#include "Modules/SubModule.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/FloatConversion.h"
+#include "Runtime/Math/Random/rand.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Math/AnimationCurve.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Threads/JobScheduler.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Misc/QualitySettings.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/ParticleSystem/ParticleSystemEditor.h"
+#include "Runtime/Mono/MonoManager.h"
+#endif
+
+// P1:
+// . Calling ps.Play() in Start(). Does it actually stsrt playing when game starts? No.
+
+// P2:
+// Automatic Culling:
+// . Improve procedural AABB
+// . Gravity: make it work with transforming into local space the same way velocity and force modules work
+// . Get tighter bounds by forces counteracting eachother instead of always expanding
+// . For procedural systems, no gain in calculating AABB every frame. Just do it when something changes.
+// . Solving for roots analytically
+// . http://en.wikipedia.org/wiki/Cubic_function
+// . http://en.wikipedia.org/wiki/Quartic_function
+// . http://en.wikipedia.org/wiki/Quintic_function
+
+// External Forces:
+// . Currently not using turbulence. Start using that?
+
+// Other:
+// . Batching: Make it work with meshes as well
+// . !!! Add runtime tests for playback code. Playing, stopping, simulating etc. Make sure it's 100%.
+
+struct ParticleSystemManager
+{
+ ParticleSystemManager()
+ :needSync(false)
+ {
+ activeEmitters.reserve(32);
+ }
+
+ dynamic_array<ParticleSystem*> activeEmitters;
+
+#if ENABLE_MULTITHREADED_PARTICLES
+ JobScheduler::JobGroupID worldCollisionJobGroup; // group for particle systems performing world collisions, PhysX is currently not threadsafe so deleting objects in LateUpdate could cause crashes as that could overlap with collision testing
+ JobScheduler::JobGroupID jobGroup; // for any other collisions
+#endif
+ bool needSync;
+};
+
+Material* GetDefaultParticleMaterial ()
+{
+ #if WEBPLUG
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion_OldWebResourcesAdded))
+ return GetBuiltinOldWebResource<Material> ("Default-Particle.mat");
+ #endif
+
+ #if UNITY_EDITOR
+ return GetBuiltinExtraResource<Material> ("Default-Particle.mat");
+ #endif
+
+ // If someone happens to create a new particle system component at runtime,
+ // just don't assign any material. The default one might not even be included
+ // into the build.
+ return NULL;
+}
+
+ParticleSystemManager gParticleSystemManager;
+
+PROFILER_INFORMATION(gParticleSystemProfile, "ParticleSystem.Update", kProfilerParticles)
+PROFILER_INFORMATION(gParticleSystemPrewarm, "ParticleSystem.Prewarm", kProfilerParticles)
+PROFILER_INFORMATION(gParticleSystemWait, "ParticleSystem.WaitForUpdateThreads", kProfilerParticles)
+PROFILER_INFORMATION(gParticleSystemJobProfile, "ParticleSystem.UpdateJob", kProfilerParticles)
+
+PROFILER_INFORMATION(gParticleSystemUpdateCollisions, "ParticleSystem.UpdateCollisions", kProfilerParticles)
+
+
+#define MAX_TIME_STEP (0.03f)
+static float GetTimeStep(float dt, bool fixedTimeStep)
+{
+ if(fixedTimeStep)
+ return GetTimeManager().GetFixedDeltaTime();
+ else if(dt > MAX_TIME_STEP)
+ return dt / Ceilf(dt / MAX_TIME_STEP);
+ else
+ return dt;
+}
+
+static void ApplyStartDelay (float& delayT, float& accumulatedDt)
+{
+ if(delayT > 0.0f)
+ {
+ delayT -= accumulatedDt;
+ accumulatedDt = max(-delayT, 0.0f);
+ delayT = max(delayT, 0.0f);
+ }
+ DebugAssert(delayT >= 0.0f);
+}
+
+static inline void CheckParticleConsistency(ParticleSystemState& state, ParticleSystemParticle& particle)
+{
+ particle.lifetime = min(particle.lifetime, particle.startLifetime);
+ state.maxSize = max(state.maxSize, particle.size);
+}
+
+ParticleSystem::ParticleSystem (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_RayBudget(0)
+, m_EmittersIndex (-1)
+#if UNITY_EDITOR
+, m_EditorListNode ( this )
+#endif
+{
+ m_State = new ParticleSystemState ();
+ m_ReadOnlyState = new ParticleSystemReadOnlyState ();
+
+ m_SizeModule = new SizeModule ();
+ m_RotationModule = new RotationModule ();
+ m_ColorModule = new ColorModule ();
+ m_UVModule = new UVModule ();
+ m_VelocityModule = new VelocityModule ();
+ m_ForceModule = new ForceModule ();
+ m_ExternalForcesModule = new ExternalForcesModule ();
+ m_ClampVelocityModule = new ClampVelocityModule ();
+ m_SizeBySpeedModule = new SizeBySpeedModule ();
+ m_RotationBySpeedModule = new RotationBySpeedModule ();
+ m_ColorBySpeedModule = new ColorBySpeedModule ();
+ m_CollisionModule = new CollisionModule ();
+ m_SubModule = new SubModule ();
+
+#if UNITY_EDITOR
+ ParticleSystemEditor::SetupDefaultParticleSystem(*this);
+ m_EditorRandomSeedIndex = 0;
+#endif
+}
+
+ParticleSystem::~ParticleSystem ()
+{
+ delete m_State;
+ delete m_ReadOnlyState;
+
+ delete m_SizeModule;
+ delete m_RotationModule;
+ delete m_ColorModule;
+ delete m_UVModule;
+ delete m_VelocityModule;
+ delete m_ForceModule;
+ delete m_ExternalForcesModule;
+ delete m_ClampVelocityModule;
+ delete m_SizeBySpeedModule;
+ delete m_RotationBySpeedModule;
+ delete m_ColorBySpeedModule;
+ delete m_CollisionModule;
+ delete m_SubModule;
+}
+
+void ParticleSystem::InitializeClass ()
+{
+#if UNITY_EDITOR
+ // This is only necessary to avoid pain during development (Pre 3.5) - can be removed later...
+ RegisterAllowTypeNameConversion ("MinMaxColorCurve", "MinMaxColor");
+ // Only needed due to 3.5 beta content
+ RegisterAllowNameConversion ("MinMaxCurve", "maxScalar", "scalar");
+ RegisterAllowNameConversion ("InitialModule", "lifetime", "startLifetime");
+ RegisterAllowNameConversion ("InitialModule", "speed", "startSpeed");
+ RegisterAllowNameConversion ("InitialModule", "color", "startColor");
+ RegisterAllowNameConversion ("InitialModule", "size", "startSize");
+ RegisterAllowNameConversion ("InitialModule", "rotation", "startRotation");
+ RegisterAllowNameConversion ("ShapeModule", "m_Radius", "radius");
+ RegisterAllowNameConversion ("ShapeModule", "m_Angle", "angle");
+ RegisterAllowNameConversion ("ShapeModule", "m_BoxX", "boxX");
+ RegisterAllowNameConversion ("ShapeModule", "m_BoxY", "boxY");
+ RegisterAllowNameConversion ("ShapeModule", "m_BoxZ", "boxZ");
+
+ REGISTER_MESSAGE_VOID (ParticleSystem, kTransformChanged, TransformChanged);
+#endif
+
+ REGISTER_MESSAGE_VOID(ParticleSystem, kDidDeleteMesh, DidDeleteMesh);
+ REGISTER_MESSAGE_VOID(ParticleSystem, kDidModifyMesh, DidModifyMesh);
+}
+
+void ParticleSystem::SetRayBudget (int rayBudget)
+{
+ m_RayBudget = rayBudget;
+};
+
+int ParticleSystem::GetRayBudget() const
+{
+ return m_RayBudget;
+};
+
+void ParticleSystem::DidModifyMesh ()
+{
+ m_ShapeModule.DidModifyMeshData();
+}
+
+void ParticleSystem::DidDeleteMesh ()
+{
+ m_ShapeModule.DidDeleteMesh(this);
+}
+
+void ParticleSystem::Cull()
+{
+ if(!IsWorldPlaying())
+ return;
+
+ m_State->culled = true;
+
+ Clear(false);
+ m_State->cullTime = GetCurTime ();
+ RemoveFromManager();
+}
+
+void ParticleSystem::RendererBecameVisible()
+{
+ if(m_State->culled)
+ {
+ m_State->culled = false;
+ if(!m_State->stopEmitting && IsPlaying() && IsWorldPlaying() && CheckSupportsProcedural (*this))
+ {
+ double timeDiff = GetCurTime() - m_State->cullTime;
+ Simulate(m_State->numLoops * m_ReadOnlyState->lengthInSec + m_State->t + timeDiff, true);
+ Play();
+ }
+ }
+}
+
+void ParticleSystem::RendererBecameInvisible()
+{
+ if(!m_State->culled && CheckSupportsProcedural(*this))
+ Cull();
+}
+
+void ParticleSystem::AddToManager()
+{
+ if(m_EmittersIndex >= 0)
+ return;
+ size_t index = gParticleSystemManager.activeEmitters.size();
+ gParticleSystemManager.activeEmitters.push_back(this);
+ m_EmittersIndex = index;
+}
+
+void ParticleSystem::RemoveFromManager()
+{
+ if(m_EmittersIndex < 0)
+ return;
+ const int index = m_EmittersIndex;
+ gParticleSystemManager.activeEmitters[index]->m_EmittersIndex = -1;
+ gParticleSystemManager.activeEmitters[index] = gParticleSystemManager.activeEmitters.back();
+ if(gParticleSystemManager.activeEmitters[index] != this) // corner case
+ gParticleSystemManager.activeEmitters[index]->m_EmittersIndex = index;
+ gParticleSystemManager.activeEmitters.resize_uninitialized(gParticleSystemManager.activeEmitters.size() - 1);
+}
+
+void ParticleSystem::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ m_InitialModule.AwakeFromLoad(this, *m_ReadOnlyState);
+ m_ShapeModule.AwakeFromLoad(this, *m_ReadOnlyState);
+
+ if (!IsActive () || (kDefaultAwakeFromLoad == awakeMode))
+ return;
+
+ m_State->localToWorld = GetComponent (Transform).GetLocalToWorldMatrixNoScale();
+ Matrix4x4f::Invert_General3D(m_State->localToWorld, m_State->worldToLocal);
+ m_State->maxSize = 0.0f;
+ m_State->invalidateProcedural = false;
+
+ if (IsWorldPlaying () && m_ReadOnlyState->playOnAwake)
+ Play ();
+
+ // Does this even happen?
+ if(GetParticleCount() || IsPlaying())
+ AddToManager();
+}
+
+void ParticleSystem::Deactivate (DeactivateOperation operation)
+{
+ Super::Deactivate(operation);
+ SyncJobs();
+ RemoveFromManager();
+}
+
+void ParticleSystem::SyncJobs()
+{
+ if(gParticleSystemManager.needSync)
+ {
+#if ENABLE_MULTITHREADED_PARTICLES
+ PROFILER_BEGIN(gParticleSystemWait, NULL);
+ JobScheduler& scheduler = GetJobScheduler();
+ scheduler.WaitForGroup (gParticleSystemManager.jobGroup);
+ PROFILER_END;
+
+#endif
+ const float deltaTimeEpsilon = 0.0001f;
+ float deltaTime = GetDeltaTime();
+ if(deltaTime < deltaTimeEpsilon)
+ return;
+
+ for(int i = 0; i < gParticleSystemManager.activeEmitters.size(); ++i)
+ {
+ ParticleSystem& system = *gParticleSystemManager.activeEmitters[i];
+ ParticleSystemState& state = *system.m_State;
+ system.Update2 (system, *system.m_ReadOnlyState, state, false);
+ }
+
+ gParticleSystemManager.needSync = false;
+ }
+}
+
+#if ENABLE_MULTITHREADED_PARTICLES
+void* ParticleSystem::UpdateFunction (void* rawData)
+{
+ Assert (rawData);
+ ParticleSystem* system = reinterpret_cast<ParticleSystem*>(rawData);
+ ParticleSystem::Update1 (*system, system->GetParticles((int)ParticleSystem::kParticleBuffer0), system->GetThreadScratchPad().deltaTime, false, false, system->m_RayBudget);
+ return NULL;
+}
+#endif
+
+#define MAX_NUM_SUB_EMIT_CMDS (1024)
+void ParticleSystem::Update(ParticleSystem& system, float deltaTime, bool fixedTimeStep, bool useProcedural, int rayBudget)
+{
+ system.m_State->recordSubEmits = false;
+ Update0 (system, *system.m_ReadOnlyState, *system.m_State, deltaTime, fixedTimeStep);
+ Update1 (system, system.GetParticles((int)ParticleSystem::kParticleBuffer0), deltaTime, fixedTimeStep, useProcedural, rayBudget);
+ Update2 (system, *system.m_ReadOnlyState, *system.m_State, fixedTimeStep);
+#if UNITY_EDITOR
+ ParticleSystemEditor::PerformInterpolationStep(&system);
+#endif
+}
+
+size_t ParticleSystem::EmitFromModules (const ParticleSystem& system, const ParticleSystemReadOnlyState& roState, ParticleSystemEmissionState& emissionState, size_t& numContinuous, const Vector3f velocity, float fromT, float toT, float dt)
+{
+ if(system.m_EmissionModule.GetEnabled())
+ return EmitFromData(emissionState, numContinuous, system.m_EmissionModule.GetEmissionDataRef(), velocity, fromT, toT, dt, roState.lengthInSec);
+ return 0;
+}
+
+size_t ParticleSystem::EmitFromData (ParticleSystemEmissionState& emissionState, size_t& numContinuous, const ParticleSystemEmissionData& emissionData, const Vector3f velocity, float fromT, float toT, float dt, float length)
+{
+ size_t amountOfParticlesToEmit = 0;
+ EmissionModule::Emit(emissionState, amountOfParticlesToEmit, numContinuous, emissionData, velocity, fromT, toT, dt, length);
+ return amountOfParticlesToEmit;
+}
+
+size_t ParticleSystem::LimitParticleCount(size_t requestSize) const
+{
+ const size_t maxNumParticles = m_InitialModule.GetMaxNumParticles();
+ return min<size_t>(requestSize, maxNumParticles);
+}
+
+size_t ParticleSystem::AddNewParticles(ParticleSystemParticles& particles, size_t newParticles) const
+{
+ const size_t fromIndex = particles.array_size();
+ const size_t newSize = LimitParticleCount(fromIndex + newParticles);
+ particles.array_resize(newSize);
+ return min(fromIndex, newSize);
+}
+
+void ParticleSystem::SimulateParticles (const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& ps, const size_t fromIndex, float dt)
+{
+ size_t particleCount = ps.array_size();
+ for (size_t q = fromIndex; q < particleCount; )
+ {
+ ps.lifetime[q] -= dt;
+
+#if UNITY_EDITOR
+ if(ParticleSystemEditor::GetIsExtraInterpolationStep())
+ {
+ ps.lifetime[q] = max(ps.lifetime[q], 0.0f);
+ }
+ else
+#endif
+
+ if (ps.lifetime[q] < 0)
+ {
+ KillParticle(roState, state, ps, q, particleCount);
+ continue;
+ }
+ ++q;
+ }
+ ps.array_resize(particleCount);
+
+ for (size_t q = fromIndex; q < particleCount; ++q)
+ ps.position[q] += (ps.velocity[q] + ps.animatedVelocity[q]) * dt;
+
+ if(ps.usesRotationalSpeed)
+ for (size_t q = fromIndex; q < particleCount; ++q)
+ ps.rotation[q] += ps.rotationalSpeed[q] * dt;
+}
+
+// Copy staging particles into real particle buffer
+void ParticleSystem::AddStagingBuffer(ParticleSystem& system)
+{
+ if(0 == system.m_ParticlesStaging.array_size())
+ return;
+
+ bool needsAxisOfRotation = system.m_Particles[kParticleBuffer0].usesAxisOfRotation;
+ bool needsEmitAccumulator = system.m_Particles[kParticleBuffer0].numEmitAccumulators > 0;
+
+ ASSERT_RUNNING_ON_MAIN_THREAD;
+ const int numParticles = system.m_Particles[kParticleBuffer0].array_size();
+ const int numStaging = system.m_ParticlesStaging.array_size();
+ system.m_Particles->array_resize(numParticles + numStaging);
+ system.m_Particles->array_merge_preallocated(system.m_ParticlesStaging, numParticles, needsAxisOfRotation, needsEmitAccumulator);
+ system.m_ParticlesStaging.array_resize(0);
+}
+
+void ParticleSystem::Emit(ParticleSystem& system, const SubEmitterEmitCommand& command, ParticleSystemEmitMode emitMode)
+{
+ int amountOfParticlesToEmit = command.particlesToEmit;
+ if (amountOfParticlesToEmit > 0)
+ {
+ ParticleSystemState& state = *system.m_State;
+ const ParticleSystemReadOnlyState& roState = *system.m_ReadOnlyState;
+
+ const int numContinuous = command.particlesToEmitContinuous;
+ const float deltaTime = command.deltaTime;
+ float parentT = command.parentT;
+ Vector3f position = command.position;
+ Vector3f initialVelocity = command.velocity;
+
+ Matrix3x3f rotMat;
+ Vector3f normalizedVelocity = NormalizeSafe(initialVelocity);
+ float angle = Abs(Dot(normalizedVelocity, Vector3f::zAxis));
+ Vector3f up = Lerp(Vector3f::zAxis, Vector3f::yAxis, angle);
+ if (!LookRotationToMatrix(normalizedVelocity, up, &rotMat))
+ rotMat.SetIdentity();
+
+ Matrix4x4f parentParticleMatrix = rotMat;
+ parentParticleMatrix.SetPosition(position);
+
+ // Transform into local space of sub emitter
+ Matrix4x4f concatMatrix;
+ if(roState.useLocalSpace)
+ MultiplyMatrices3x4(state.worldToLocal, parentParticleMatrix, concatMatrix);
+ else
+ concatMatrix = parentParticleMatrix;
+
+ if(roState.useLocalSpace)
+ initialVelocity = state.worldToLocal.MultiplyVector3(initialVelocity);
+
+ DebugAssert(state.GetIsSubEmitter());
+ DebugAssert(state.stopEmitting);
+
+ const float commandAliveTime = command.timeAlive;
+ const float timeStep = GetTimeStep(commandAliveTime, true);
+
+ // @TODO: Perform culling: if max lifetime < timeAlive, then just skip emit
+
+ // Perform sub emitter loop
+ if(roState.looping)
+ parentT = fmodf(parentT, roState.lengthInSec);
+
+ ParticleSystemParticles& particles = (kParticleSystemEMDirect == emitMode) ? system.m_Particles[kParticleBuffer0] : system.m_ParticlesStaging;
+
+ size_t fromIndex = system.AddNewParticles(particles, amountOfParticlesToEmit);
+ StartModules (system, roState, state, command.emissionState, initialVelocity, concatMatrix, particles, fromIndex, deltaTime, parentT, numContinuous, 0.0f);
+
+ // Make sure particles get updated
+ if(0 == fromIndex)
+ system.KeepUpdating();
+
+ // Update incremental
+ if(timeStep > 0.0001f)
+ {
+ float accumulatedDt = commandAliveTime;
+ while (accumulatedDt >= timeStep)
+ {
+ accumulatedDt -= timeStep;
+ UpdateModulesIncremental(system, roState, state, particles, fromIndex, timeStep);
+ }
+ }
+ }
+}
+
+
+void ParticleSystem::PlaybackSubEmitterCommandBuffer(const ParticleSystem& parent, ParticleSystemState& state, bool fixedTimeStep)
+{
+ ParticleSystem* subEmittersBirth[kParticleSystemMaxSubBirth];
+ ParticleSystem* subEmittersCollision[kParticleSystemMaxSubCollision];
+ ParticleSystem* subEmittersDeath[kParticleSystemMaxSubDeath];
+ int numBirth = parent.m_SubModule->GetSubEmitterPtrsBirth(&subEmittersBirth[0]);
+ int numCollision = parent.m_SubModule->GetSubEmitterPtrsCollision(&subEmittersCollision[0]);
+ int numDeath = parent.m_SubModule->GetSubEmitterPtrsDeath(&subEmittersDeath[0]);
+
+ const int numCommands = state.subEmitterCommandBuffer.commandCount;
+ const SubEmitterEmitCommand* commands = state.subEmitterCommandBuffer.commands;
+ for(int i = 0; i < numCommands; i++)
+ {
+ const SubEmitterEmitCommand& command = commands[i];
+ ParticleSystem* shuriken = NULL;
+ if(command.subEmitterType == kParticleSystemSubTypeBirth)
+ shuriken = (command.subEmitterIndex < numBirth) ? subEmittersBirth[command.subEmitterIndex] : NULL;
+ else if(command.subEmitterType == kParticleSystemSubTypeCollision)
+ shuriken = (command.subEmitterIndex < numCollision) ? subEmittersCollision[command.subEmitterIndex] : NULL;
+ else if(command.subEmitterType == kParticleSystemSubTypeDeath)
+ shuriken = (command.subEmitterIndex < numDeath) ? subEmittersDeath[command.subEmitterIndex] : NULL;
+ else
+ Assert(!"PlaybackSubEmitterCommandBuffer: Sub emitter type not implemented");
+
+ DebugAssert(shuriken && "Y U NO HERE ANYMORE?");
+ if(!shuriken)
+ continue;
+
+ ParticleSystem::Emit(*shuriken, command, kParticleSystemEMDirect);
+ }
+
+ state.subEmitterCommandBuffer.commandCount = 0;
+}
+
+void ParticleSystem::UpdateBounds(const ParticleSystem& system, const ParticleSystemParticles& ps, ParticleSystemState& state)
+{
+ const size_t particleCount = ps.array_size();
+ if (particleCount == 0)
+ {
+ state.minMaxAABB = MinMaxAABB (Vector3f::zero, Vector3f::zero);
+ return;
+ }
+
+ state.minMaxAABB.Init();
+
+ if(CheckSupportsProcedural(system))
+ {
+ const Transform& transform = system.GetComponent (Transform);
+ Matrix4x4f localToWorld = transform.GetLocalToWorldMatrixNoScale ();
+
+ // Lifetime and max speed
+ const Vector2f minMaxLifeTime = system.m_InitialModule.GetLifeTimeCurve().FindMinMax();
+
+ const float maxLifeTime = minMaxLifeTime.y;
+ const Vector2f minMaxStartSpeed = system.m_InitialModule.GetSpeedCurve().FindMinMax() * maxLifeTime;
+
+ state.minMaxAABB = MinMaxAABB(Vector3f::zero, Vector3f::zero);
+ state.minMaxAABB.Encapsulate(Vector3f::zAxis * minMaxStartSpeed.x);
+ state.minMaxAABB.Encapsulate(Vector3f::zAxis * minMaxStartSpeed.y);
+ if(system.m_ShapeModule.GetEnabled())
+ system.m_ShapeModule.CalculateProceduralBounds(state.minMaxAABB, state.emitterScale, minMaxStartSpeed);
+
+ // Gravity
+ // @TODO: Do what we do for force and velocity here, i.e. transform bounds properly
+ const Vector3f gravityBounds = system.m_InitialModule.GetGravity(*system.m_ReadOnlyState, *system.m_State) * maxLifeTime * maxLifeTime * 0.5f;
+
+ state.minMaxAABB.m_Max += max(gravityBounds, Vector3f::zero);
+ state.minMaxAABB.m_Min += min(gravityBounds, Vector3f::zero);
+
+ MinMaxAABB velocityBounds (Vector3f::zero, Vector3f::zero);
+ if(system.m_VelocityModule->GetEnabled())
+ system.m_VelocityModule->CalculateProceduralBounds(velocityBounds, localToWorld, maxLifeTime);
+ state.minMaxAABB.m_Max += velocityBounds.m_Max;
+ state.minMaxAABB.m_Min += velocityBounds.m_Min;
+
+ MinMaxAABB forceBounds (Vector3f::zero, Vector3f::zero);
+ if(system.m_ForceModule->GetEnabled())
+ system.m_ForceModule->CalculateProceduralBounds(forceBounds, localToWorld, maxLifeTime);
+ state.minMaxAABB.m_Max += forceBounds.m_Max;
+ state.minMaxAABB.m_Min += forceBounds.m_Min;
+
+ // Modules that are not supported by procedural
+ DebugAssert(!system.m_RotationBySpeedModule->GetEnabled()); // find out if possible to support it
+ DebugAssert(!system.m_ClampVelocityModule->GetEnabled()); // unsupported: (Need to compute velocity by deriving position curves...), possible to support?
+ DebugAssert(!system.m_CollisionModule->GetEnabled());
+ DebugAssert(!system.m_SubModule->GetEnabled()); // find out if possible to support it
+ DebugAssert(!system.m_ExternalForcesModule->GetEnabled());
+ }
+ else
+ {
+ for (size_t q = 0; q < particleCount; ++q)
+ state.minMaxAABB.Encapsulate (ps.position[q]);
+
+ ParticleSystemRenderer* renderer = system.QueryComponent(ParticleSystemRenderer);
+ if(renderer && (renderer->GetRenderMode() == kSRMStretch3D))
+ {
+ const float velocityScale = renderer->GetVelocityScale();
+ const float lengthScale = renderer->GetLengthScale();
+ for(size_t q = 0; q < particleCount; ++q )
+ {
+ float sqrVelocity = SqrMagnitude (ps.velocity[q]+ps.animatedVelocity[q]);
+ if (sqrVelocity > Vector3f::epsilon)
+ {
+ float scale = velocityScale + FastInvSqrt (sqrVelocity) * lengthScale * ps.size[q];
+ state.minMaxAABB.Encapsulate(ps.position[q] - ps.velocity[q] * scale);
+ }
+ }
+ }
+ }
+
+ // Expand with maximum particle size * sqrt(2) (the length of a diagonal of a particle, if it should happen to be rotated)
+ const float kSqrtOf2 = 1.42f;
+ float maxSize = kSqrtOf2 * 0.5f * system.m_InitialModule.GetSizeCurve().FindMinMax().y;
+ if(system.m_SizeModule->GetEnabled())
+ maxSize *= system.m_SizeModule->GetCurve().FindMinMax().y;
+ if(system.m_SizeBySpeedModule->GetEnabled())
+ maxSize *= system.m_SizeBySpeedModule->GetCurve().FindMinMax().y;
+ state.minMaxAABB.Expand (max(maxSize, state.maxSize));
+
+ Assert (state.minMaxAABB.IsValid ());
+}
+
+IMPLEMENT_CLASS_HAS_INIT (ParticleSystem)
+IMPLEMENT_OBJECT_SERIALIZE (ParticleSystem)
+
+
+template<class TransferFunction>
+void ParticleSystem::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ m_ReadOnlyState->Transfer (transfer); m_ReadOnlyState->CheckConsistency();
+ m_State->Transfer (transfer);
+ transfer.Transfer(m_InitialModule, m_InitialModule.GetName ()); m_InitialModule.CheckConsistency ();
+ transfer.Transfer(m_ShapeModule, m_ShapeModule.GetName ()); m_ShapeModule.CheckConsistency ();
+ transfer.Transfer(m_EmissionModule, m_EmissionModule.GetName ()); m_EmissionModule.CheckConsistency ();
+ transfer.Transfer(*m_SizeModule, m_SizeModule->GetName ()); m_SizeModule->CheckConsistency ();
+ transfer.Transfer(*m_RotationModule, m_RotationModule->GetName ()); m_RotationModule->CheckConsistency ();
+ transfer.Transfer(*m_ColorModule, m_ColorModule->GetName ()); m_ColorModule->CheckConsistency ();
+ transfer.Transfer(*m_UVModule, m_UVModule->GetName ()); m_UVModule->CheckConsistency ();
+ transfer.Transfer(*m_VelocityModule, m_VelocityModule->GetName ()); m_VelocityModule->CheckConsistency ();
+ transfer.Transfer(*m_ForceModule, m_ForceModule->GetName ()); m_ForceModule->CheckConsistency ();
+ transfer.Transfer(*m_ExternalForcesModule, m_ExternalForcesModule->GetName ()); m_ExternalForcesModule->CheckConsistency ();
+ transfer.Transfer(*m_ClampVelocityModule, m_ClampVelocityModule->GetName ()); m_ClampVelocityModule->CheckConsistency ();
+ transfer.Transfer(*m_SizeBySpeedModule, m_SizeBySpeedModule->GetName ()); m_SizeBySpeedModule->CheckConsistency ();
+ transfer.Transfer(*m_RotationBySpeedModule, m_RotationBySpeedModule->GetName ()); m_RotationBySpeedModule->CheckConsistency ();
+ transfer.Transfer(*m_ColorBySpeedModule, m_ColorBySpeedModule->GetName ()); m_ColorBySpeedModule->CheckConsistency ();
+ transfer.Transfer(*m_CollisionModule, m_CollisionModule->GetName ()); m_CollisionModule->CheckConsistency ();
+ transfer.Transfer(*m_SubModule, m_SubModule->GetName ()); m_SubModule->CheckConsistency ();
+ if(transfer.IsReading())
+ {
+ m_State->supportsProcedural = DetermineSupportsProcedural(*this);
+ m_State->invalidateProcedural = true; // Stuff might have changed which we can't support (example: start speed has become smaller)
+ }
+}
+
+bool ParticleSystem::CheckSupportsProcedural(const ParticleSystem& system)
+{
+ ParticleSystemRenderer* renderer = system.QueryComponent(ParticleSystemRenderer);
+ if(renderer && (renderer->GetRenderMode() == kSRMStretch3D))
+ return false;
+ return system.m_State->supportsProcedural && !system.m_State->invalidateProcedural;
+}
+
+// TODO: Needs to match UpdateCullingSupportedString in all xxModule.cs files
+bool ParticleSystem::DetermineSupportsProcedural(const ParticleSystem& system)
+{
+ bool supportsProcedural = true;
+ supportsProcedural = supportsProcedural && system.m_ReadOnlyState->useLocalSpace;
+
+ // Can't be random as we recreate it every frame. TODO: Would be great if we can support this in procedural mode.
+ const short lifeTimeMinMaxState = system.m_InitialModule.GetLifeTimeCurve().minMaxState;
+ supportsProcedural = supportsProcedural && (lifeTimeMinMaxState == kMMCScalar || lifeTimeMinMaxState == kMMCCurve);
+
+ supportsProcedural = supportsProcedural && (EmissionModule::kEmissionTypeDistance != system.m_EmissionModule.GetEmissionDataRef().type);
+
+ supportsProcedural = supportsProcedural && !system.m_ExternalForcesModule->GetEnabled();
+ supportsProcedural = supportsProcedural && !system.m_ClampVelocityModule->GetEnabled();
+ supportsProcedural = supportsProcedural && !system.m_RotationBySpeedModule->GetEnabled();
+ supportsProcedural = supportsProcedural && !system.m_CollisionModule->GetEnabled();
+ supportsProcedural = supportsProcedural && !system.m_SubModule->GetEnabled();
+
+ if(system.m_RotationModule->GetEnabled())
+ supportsProcedural = supportsProcedural && CurvesSupportProcedural (system.m_RotationModule->GetCurve().editorCurves, system.m_RotationModule->GetCurve().minMaxState);
+
+ if(system.m_VelocityModule->GetEnabled())
+ {
+ supportsProcedural = supportsProcedural && CurvesSupportProcedural (system.m_VelocityModule->GetXCurve().editorCurves, system.m_VelocityModule->GetXCurve().minMaxState);
+ supportsProcedural = supportsProcedural && CurvesSupportProcedural (system.m_VelocityModule->GetYCurve().editorCurves, system.m_VelocityModule->GetYCurve().minMaxState);
+ supportsProcedural = supportsProcedural && CurvesSupportProcedural (system.m_VelocityModule->GetZCurve().editorCurves, system.m_VelocityModule->GetZCurve().minMaxState);
+ }
+
+ if(system.m_ForceModule->GetEnabled())
+ {
+ supportsProcedural = supportsProcedural && CurvesSupportProcedural (system.m_ForceModule->GetXCurve().editorCurves, system.m_ForceModule->GetXCurve().minMaxState);
+ supportsProcedural = supportsProcedural && CurvesSupportProcedural (system.m_ForceModule->GetYCurve().editorCurves, system.m_ForceModule->GetYCurve().minMaxState);
+ supportsProcedural = supportsProcedural && CurvesSupportProcedural (system.m_ForceModule->GetZCurve().editorCurves, system.m_ForceModule->GetZCurve().minMaxState);
+ supportsProcedural = supportsProcedural && !system.m_ForceModule->GetRandomizePerFrame();
+ }
+
+ return supportsProcedural;
+}
+
+bool ParticleSystem::ComputePrewarmStartParameters(float& prewarmTime, float t)
+{
+ const float fixedDelta = GetTimeManager().GetFixedDeltaTime();
+ const float maxLifetime = m_InitialModule.GetLifeTimeCurve().FindMinMax().y;
+ const float length = m_ReadOnlyState->lengthInSec;
+ if(!m_ReadOnlyState->looping && ((maxLifetime + length) < t))
+ return false;
+
+ prewarmTime = m_SubModule->GetEnabled() ? CalculateSubEmitterMaximumLifeTime(maxLifetime) : 0.0f;
+ prewarmTime = max(prewarmTime, maxLifetime);
+
+ float frac = fmodf(t, fixedDelta);
+ float startT = t - prewarmTime - frac;
+ prewarmTime += frac;
+
+ // Clamp to start
+ if(!m_ReadOnlyState->prewarm)
+ {
+ startT = max(startT, 0.0f);
+ prewarmTime = min(t, prewarmTime);
+ }
+
+ // This is needed to figure out if emitacc should be inverted or not
+ const float signStartT = Sign(startT);
+ const float absStartT = Abs(startT);
+
+ while(startT < 0.0f)
+ startT += length;
+ float endT = startT + absStartT;
+ m_State->t = fmodf(startT, length);
+
+ ParticleSystemEmissionState emissionState;
+ const Vector3f emitVelocity = Vector3f::zero;
+ const float epsilon = 0.0001f;
+ int seedIndex = 0;
+
+ const float prevStartT = startT;
+ const float nextStartT = startT + fixedDelta;
+ const float prevEndT = endT;
+ const float nextEndT = endT + fixedDelta;
+ if (!(nextStartT > prevStartT && nextEndT > prevEndT))
+ {
+ ErrorStringObject ("Precision issue while prewarming particle system - 'Duration' or 'Start Lifetime' is likely a too large value.", this);
+ return false;
+ }
+
+ while((startT + epsilon) < endT)
+ {
+ const float toT = fmodf(startT + fixedDelta, length);
+ const float fromT = fmodf(startT, length);
+
+ size_t numContinuous;
+ int numParticles = ParticleSystem::EmitFromModules(*this, *m_ReadOnlyState, emissionState, numContinuous, emitVelocity, fromT, toT, fixedDelta);
+ if(numParticles > 0)
+ seedIndex++;
+ startT += fixedDelta;
+ }
+ m_State->emissionState.m_ToEmitAccumulator = ((signStartT > 0.0f ? emissionState.m_ToEmitAccumulator : 1.0f - emissionState.m_ToEmitAccumulator)) + epsilon;
+#if UNITY_EDITOR
+ m_EditorRandomSeedIndex = signStartT > 0.0f ? seedIndex : -seedIndex-1;
+#endif
+
+ return true;
+}
+
+void ParticleSystem::Simulate (float t, bool restart)
+{
+ PROFILER_AUTO(gParticleSystemPrewarm, NULL)
+
+ if(restart)
+ {
+ m_InitialModule.ResetSeed(*m_ReadOnlyState);
+ m_ShapeModule.ResetSeed(*m_ReadOnlyState);
+ Stop();
+ Clear();
+ Play (false);
+
+ ApplyStartDelay (m_State->delayT, t);
+ float prewarmTime;
+ if(ComputePrewarmStartParameters(prewarmTime, t))
+ {
+ Update(*this, prewarmTime, true, CheckSupportsProcedural(*this));
+ Pause ();
+ }
+ else
+ {
+ Stop();
+ Clear();
+ }
+ }
+ else
+ {
+ m_State->playing = true;
+ Update(*this, t, true, false);
+ Pause ();
+ }
+}
+
+void ParticleSystem::Play (bool autoPrewarm)
+{
+ Assert (m_State);
+ if(!IsActive () || m_State->GetIsSubEmitter())
+ return;
+
+ m_State->stopEmitting = false;
+ m_State->playing = true;
+ if (m_State->needRestart)
+ {
+ if(m_ReadOnlyState->prewarm)
+ {
+ if (autoPrewarm)
+ AutoPrewarm();
+ }
+ else
+ {
+ m_State->delayT = m_ReadOnlyState->startDelay;
+ }
+
+ m_State->playing = true;
+ m_State->t = 0.0f;
+ m_State->numLoops = 0;
+ m_State->invalidateProcedural = false;
+ m_State->accumulatedDt = 0.0f;
+#if UNITY_EDITOR
+ m_EditorRandomSeedIndex = 0;
+#endif
+ m_State->emissionState.Clear();
+ }
+
+ if(m_State->culled && CheckSupportsProcedural(*this))
+ Cull();
+ else
+ AddToManager();
+}
+
+void ParticleSystem::AutoPrewarm()
+{
+ if(m_ReadOnlyState->prewarm && m_ReadOnlyState->looping)
+ {
+ DebugAssert(!m_State->GetIsSubEmitter());
+ DebugAssert(m_State->playing); // Only use together with play
+ Simulate (0.0f, true);
+ //DebugAssert(CompareApproximately(m_State->emissionState.m_ToEmitAccumulator, 1.0f, 0.01f) && "Particle emission gaps may occur");
+ }
+}
+
+void ParticleSystem::Stop ()
+{
+ Assert (m_State);
+ m_State->needRestart = true;
+ m_State->stopEmitting = true;
+}
+
+void ParticleSystem::Pause ()
+{
+ Assert (m_State);
+ m_State->playing = false;
+ m_State->needRestart = false;
+ RemoveFromManager();
+}
+
+bool ParticleSystem::IsPaused () const
+{
+ Assert (m_State);
+ return !m_State->playing && !m_State->needRestart;
+}
+
+bool ParticleSystem::IsStopped () const
+{
+ Assert (m_State);
+ return !m_State->playing && m_State->needRestart;
+}
+
+void ParticleSystem::KeepUpdating()
+{
+ if (IsActive())
+ {
+ // Ensure added particles will update, but stop emission
+ m_State->playing = true;
+ m_State->stopEmitting = true;
+ AddToManager();
+ }
+}
+
+void ParticleSystem::Emit (int count)
+{
+ // StartParticles() takes size_t so check for negative value here (case 495098)
+ if (count <= 0)
+ return;
+
+ KeepUpdating();
+ Assert (m_State);
+
+ const Transform& transform = GetComponent (Transform);
+ Matrix4x4f oldLocalToWorld = m_State->localToWorld;
+ Matrix4x4f oldWorldToLocal = m_State->worldToLocal;
+ Vector3f oldEmitterScale = m_State->emitterScale;
+ m_State->localToWorld = transform.GetLocalToWorldMatrixNoScale ();
+ Matrix4x4f::Invert_General3D(m_State->localToWorld, m_State->worldToLocal);
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_2_a1))
+ m_State->emitterScale = transform.GetWorldScaleLossy();
+ else
+ m_State->emitterScale = transform.GetLocalScale();
+ StartParticles(*this, m_Particles[kParticleBuffer0], 0.0f, m_State->t, 0.0f, 0, count, 0.0f);
+ m_State->localToWorld = oldLocalToWorld;
+ m_State->worldToLocal = oldWorldToLocal;
+ m_State->emitterScale = oldEmitterScale;
+}
+
+void ParticleSystem::EmitParticleExternal(ParticleSystemParticle* particle)
+{
+#if UNITY_EDITOR
+ if(!IsWorldPlaying() && IsStopped ())
+ return;
+#endif
+
+ m_State->invalidateProcedural = true;
+
+ CheckParticleConsistency(*m_State, *particle);
+ if(particle->lifetime <= 0.0f)
+ return;
+
+ KeepUpdating();
+
+ m_Particles[kParticleBuffer0].AddParticle(particle);
+
+#if UNITY_EDITOR
+ if(!IsWorldPlaying())
+ m_Particles[kParticleBuffer1].AddParticle(particle);
+#endif
+
+ if(!IsPlaying())
+ UpdateBounds(*this, m_Particles[kParticleBuffer0], *m_State);
+}
+
+void ParticleSystem::Clear (bool updateBounds)
+{
+ for(int i = 0; i < kNumParticleBuffers; i++)
+ m_Particles[i].array_resize(0);
+ m_ParticlesStaging.array_resize(0);
+ m_State->emitReplay.resize_uninitialized(0);
+
+ if (m_State->stopEmitting)
+ {
+ // This triggers sometimes, why? (case 491684)
+ DebugAssert (m_State->needRestart);
+ m_State->playing = false;
+ RemoveFromManager();
+ }
+
+ if(updateBounds)
+ {
+ UpdateBounds(*this, m_Particles[kParticleBuffer0], *m_State);
+ Update2(*this, *m_ReadOnlyState, *m_State, false);
+ }
+}
+
+float ParticleSystem::GetStartDelay() const
+{
+ Assert(m_State);
+ return m_ReadOnlyState->startDelay;
+}
+
+void ParticleSystem::SetStartDelay(float value)
+{
+ Assert(m_State);
+ m_ReadOnlyState->startDelay = value;
+ SetDirty ();
+}
+
+bool ParticleSystem::IsAlive () const
+{
+ Assert(m_State);
+ return (!IsStopped() || (GetParticleCount() > 0));
+}
+
+bool ParticleSystem::IsPlaying () const
+{
+ Assert (m_State);
+ return m_State->playing;
+}
+
+bool ParticleSystem::GetLoop () const
+{
+ Assert (m_State);
+ return m_ReadOnlyState->looping;
+}
+
+void ParticleSystem::SetLoop (bool loop)
+{
+ Assert (m_State);
+ m_ReadOnlyState->looping = loop;
+ SetDirty ();
+}
+
+bool ParticleSystem::GetPlayOnAwake () const
+{
+ Assert (m_State);
+ return m_ReadOnlyState->playOnAwake;
+}
+
+void ParticleSystem::SetPlayOnAwake (bool playOnAwake)
+{
+ Assert (m_State);
+ m_ReadOnlyState->playOnAwake = playOnAwake;
+ SetDirty ();
+}
+
+ParticleSystemSimulationSpace ParticleSystem::GetSimulationSpace () const
+{
+ Assert (m_State);
+ return (m_ReadOnlyState->useLocalSpace ? kSimLocal : kSimWorld);
+}
+
+void ParticleSystem::SetSimulationSpace (ParticleSystemSimulationSpace simulationSpace)
+{
+ Assert (m_State);
+ m_ReadOnlyState->useLocalSpace = (simulationSpace == kSimLocal);
+ SetDirty ();
+}
+
+float ParticleSystem::GetSecPosition () const
+{
+ Assert (m_State);
+ return m_State->t;
+}
+
+void ParticleSystem::SetSecPosition (float pos)
+{
+ Assert (m_State);
+ m_State->t = pos;
+ SetDirty ();
+}
+
+float ParticleSystem::GetPlaybackSpeed () const
+{
+ Assert (m_State);
+ return m_ReadOnlyState->speed;
+}
+
+void ParticleSystem::SetPlaybackSpeed (float speed)
+{
+ Assert (m_State);
+ m_ReadOnlyState->speed = speed;
+ SetDirty ();
+}
+
+float ParticleSystem::GetLengthInSec () const
+{
+ Assert (m_State);
+ return m_ReadOnlyState->lengthInSec;
+}
+
+void ParticleSystem::GetNumTiles(int& uvTilesX, int& uvTilesY) const
+{
+ uvTilesX = uvTilesY = 1;
+ if(m_UVModule->GetEnabled())
+ m_UVModule->GetNumTiles(uvTilesX, uvTilesY);
+}
+
+Matrix4x4f ParticleSystem::GetLocalToWorldMatrix() const
+{
+ return m_State->localToWorld;
+}
+
+bool ParticleSystem::GetEnableEmission() const
+{
+ return m_EmissionModule.GetEnabled();
+}
+
+void ParticleSystem::SetEnableEmission(bool value)
+{
+ m_State->invalidateProcedural = true;
+ m_EmissionModule.SetEnabled(value);
+ SetDirty();
+}
+
+float ParticleSystem::GetEmissionRate() const
+{
+ return m_EmissionModule.GetEmissionDataRef().rate.GetScalar();
+}
+
+void ParticleSystem::SetEmissionRate(float value)
+{
+ m_State->invalidateProcedural = true;
+ m_EmissionModule.GetEmissionData().rate.SetScalar(value);
+ SetDirty();
+}
+
+float ParticleSystem::GetStartSpeed() const
+{
+ return m_InitialModule.GetSpeedCurve().GetScalar();
+}
+
+void ParticleSystem::SetStartSpeed(float value)
+{
+ m_State->invalidateProcedural = true;
+ m_InitialModule.GetSpeedCurve().SetScalar(value);
+ SetDirty();
+}
+
+float ParticleSystem::GetStartSize() const
+{
+ return m_InitialModule.GetSizeCurve().GetScalar();
+}
+
+void ParticleSystem::SetStartSize(float value)
+{
+ m_InitialModule.GetSizeCurve().SetScalar(value);
+ SetDirty();
+}
+
+ColorRGBAf ParticleSystem::GetStartColor() const
+{
+ return m_InitialModule.GetColor().maxColor;
+}
+
+void ParticleSystem::SetStartColor(ColorRGBAf value)
+{
+ m_InitialModule.GetColor().maxColor = value;
+ SetDirty();
+}
+
+float ParticleSystem::GetStartRotation() const
+{
+ return m_InitialModule.GetRotationCurve().GetScalar();
+}
+
+void ParticleSystem::SetStartRotation(float value)
+{
+ m_InitialModule.GetRotationCurve().SetScalar(value);
+ SetDirty();
+}
+
+float ParticleSystem::GetStartLifeTime() const
+{
+ return m_InitialModule.GetLifeTimeCurve().GetScalar();
+}
+
+void ParticleSystem::SetStartLifeTime(float value)
+{
+ m_State->invalidateProcedural = true;
+ m_InitialModule.GetLifeTimeCurve().SetScalar(value);
+ SetDirty();
+}
+
+float ParticleSystem::GetGravityModifier() const
+{
+ return m_InitialModule.GetGravityModifier();
+}
+
+void ParticleSystem::SetGravityModifier(float value)
+{
+ m_State->invalidateProcedural = true;
+ m_InitialModule.SetGravityModifier(value);
+ SetDirty();
+}
+
+UInt32 ParticleSystem::GetRandomSeed() const
+{
+ return m_ReadOnlyState->randomSeed;
+}
+
+void ParticleSystem::SetRandomSeed(UInt32 value)
+{
+ m_ReadOnlyState->randomSeed = value;
+ SetDirty();
+}
+
+int ParticleSystem::GetMaxNumParticles () const
+{
+ return m_InitialModule.GetMaxNumParticles ();
+}
+
+void ParticleSystem::SetMaxNumParticles (int value)
+{
+ m_InitialModule.SetMaxNumParticles (value);
+ SetDirty();
+}
+
+void ParticleSystem::AllocateAllStructuresOfArrays()
+{
+ // Make sure all particle buffers have all elements
+ for(int i = 0; i < kNumParticleBuffers; i++)
+ {
+ if(!m_Particles[i].usesAxisOfRotation)
+ m_Particles[i].SetUsesAxisOfRotation();
+ m_Particles[i].SetUsesEmitAccumulator(kParticleSystemMaxNumEmitAccumulators);
+ }
+}
+
+void ParticleSystem::SetParticlesExternal (ParticleSystemParticle* particles, int size)
+{
+#if UNITY_EDITOR
+ if(!IsWorldPlaying() && IsStopped ())
+ return;
+#endif
+
+ m_State->invalidateProcedural = true;
+
+ // Make sure particles are in the correct ranges etc.
+ for (size_t q = 0; q < size; q++)
+ CheckParticleConsistency(*m_State, particles[q]);
+
+ AllocateAllStructuresOfArrays();
+ ParticleSystemParticles& ps = m_Particles[kParticleBuffer0];
+ ps.array_resize(size);
+ ps.CopyFromArrayAOS(particles, size);
+ size_t particleCount = size;
+ for (size_t q = 0; q < particleCount; )
+ {
+ if (ps.lifetime[q] < 0)
+ {
+ KillParticle(*m_ReadOnlyState, *m_State, ps, q, particleCount);
+ continue;
+ }
+ ++q;
+ }
+ ps.array_resize(particleCount);
+
+ #if UNITY_EDITOR
+ if(!IsWorldPlaying())
+ m_Particles[kParticleBuffer1].array_assign(ps);
+ #endif
+
+ if(!IsPlaying())
+ UpdateBounds(*this, ps, *m_State);
+}
+
+void ParticleSystem::GetParticlesExternal (ParticleSystemParticle* particles, int size)
+{
+ AllocateAllStructuresOfArrays();
+ ParticleSystemParticles& ps = m_Particles[kParticleBuffer0];
+ ps.CopyToArrayAOS(particles, size);
+}
+
+int ParticleSystem::GetSafeCollisionEventSize () const
+{
+ const ParticleSystemParticles& ps = m_Particles[kParticleBuffer0];
+ return ps.collisionEvents.GetCollisionEventCount ();
+}
+
+int ParticleSystem::GetCollisionEventsExternal (int instanceID, MonoParticleCollisionEvent* collisionEvents, int size) const
+{
+ const ParticleSystemParticles& ps = m_Particles[kParticleBuffer0];
+ return ps.collisionEvents.GetCollisionEvents (instanceID, collisionEvents, size);
+}
+
+ParticleSystemParticles& ParticleSystem::GetParticles (int index)
+{
+#if UNITY_EDITOR
+ if(index == -1)
+ if(ParticleSystemEditor::UseInterpolation(*this))
+ index = (int)kParticleBuffer1;
+ else
+ index = (int)kParticleBuffer0;
+ return m_Particles[index];
+#else
+ return m_Particles[kParticleBuffer0];
+#endif
+}
+
+const ParticleSystemParticles& ParticleSystem::GetParticles (int index) const
+{
+#if UNITY_EDITOR
+ if(index == -1)
+ if(ParticleSystemEditor::UseInterpolation(*this))
+ index = (int)kParticleBuffer1;
+ else
+ index = (int)kParticleBuffer0;
+ return m_Particles[index];
+#else
+ return m_Particles[kParticleBuffer0];
+#endif
+}
+
+size_t ParticleSystem::GetParticleCount () const
+{
+ return m_Particles[kParticleBuffer0].array_size ();
+}
+
+int ParticleSystem::SetupSubEmitters(ParticleSystem& shuriken, ParticleSystemState& state)
+{
+ Assert(!state.cachedSubDataBirth && !state.numCachedSubDataBirth);
+ Assert(!state.cachedSubDataCollision && !state.numCachedSubDataCollision);
+ Assert(!state.cachedSubDataDeath && !state.numCachedSubDataDeath);
+ int subEmitterCount = 0;
+
+ if(shuriken.m_SubModule->GetEnabled())
+ {
+ ParticleSystem* subEmittersBirth[kParticleSystemMaxSubBirth];
+ state.numCachedSubDataBirth = shuriken.m_SubModule->GetSubEmitterPtrsBirth(&subEmittersBirth[0]);
+ state.cachedSubDataBirth = ALLOC_TEMP_MANUAL(ParticleSystemSubEmitterData, state.numCachedSubDataBirth);
+ std::uninitialized_fill (state.cachedSubDataBirth, state.cachedSubDataBirth + state.numCachedSubDataBirth, ParticleSystemSubEmitterData());
+ for(int i = 0; i < state.numCachedSubDataBirth; i++)
+ {
+ ParticleSystem* subEmitter = subEmittersBirth[i];
+ ParticleSystemSubEmitterData& subData = state.cachedSubDataBirth[i];
+ subData.startDelayInSec = subEmitter->m_ReadOnlyState->startDelay;
+ subData.lengthInSec = subEmitter->GetLoop() ? numeric_limits<float>::max() : subEmitter->GetLengthInSec();
+ subData.maxLifetime = subEmitter->m_InitialModule.GetLifeTimeCurve().GetScalar();
+ subData.emitter = subEmitter;
+ subEmitter->m_EmissionModule.GetEmissionDataCopy(&subData.emissionData);
+ subEmitter->m_State->SetIsSubEmitter(true);
+ subEmitterCount++;
+ }
+ ParticleSystem* subEmittersCollision[kParticleSystemMaxSubCollision];
+ state.numCachedSubDataCollision = shuriken.m_SubModule->GetSubEmitterPtrsCollision(&subEmittersCollision[0]);
+ state.cachedSubDataCollision = ALLOC_TEMP_MANUAL(ParticleSystemSubEmitterData, state.numCachedSubDataCollision);
+ std::uninitialized_fill (state.cachedSubDataCollision, state.cachedSubDataCollision + state.numCachedSubDataCollision, ParticleSystemSubEmitterData());
+ for(int i = 0; i < state.numCachedSubDataCollision; i++)
+ {
+ ParticleSystem* subEmitter = subEmittersCollision[i];
+ ParticleSystemSubEmitterData& subData = state.cachedSubDataCollision[i];
+ subData.emitter = subEmitter;
+ subEmitter->m_EmissionModule.GetEmissionDataCopy(&subData.emissionData);
+ subEmitter->m_State->SetIsSubEmitter(true);
+ subEmitterCount++;
+ }
+ ParticleSystem* subEmittersDeath[kParticleSystemMaxSubDeath];
+ state.numCachedSubDataDeath = shuriken.m_SubModule->GetSubEmitterPtrsDeath(&subEmittersDeath[0]);
+ state.cachedSubDataDeath = ALLOC_TEMP_MANUAL(ParticleSystemSubEmitterData, state.numCachedSubDataDeath);
+ std::uninitialized_fill (state.cachedSubDataDeath, state.cachedSubDataDeath + state.numCachedSubDataDeath, ParticleSystemSubEmitterData());
+ for(int i = 0; i < state.numCachedSubDataDeath; i++)
+ {
+ ParticleSystem* subEmitter = subEmittersDeath[i];
+ ParticleSystemSubEmitterData& subData = state.cachedSubDataDeath[i];
+ subData.emitter = subEmitter;
+ subEmitter->m_EmissionModule.GetEmissionDataCopy(&subData.emissionData);
+ subEmitter->m_State->SetIsSubEmitter(true);
+ subEmitterCount++;
+ }
+ }
+ return subEmitterCount;
+}
+
+int ParticleSystem::CalculateMaximumSubEmitterEmitCount(ParticleSystem& shuriken, ParticleSystemState& state, float deltaTime, bool fixedTimeStep)
+{
+ // Save emission state
+ const ParticleSystemEmissionState orgEmissionState = state.emissionState;
+ ParticleSystemEmissionState emissionState;
+
+ // Simulate emission
+ float timeStep = GetTimeStep(deltaTime, fixedTimeStep);
+ DebugAssert(timeStep > 0.0001f);
+
+ // @TODO: Maybe we can go back to just picking the conservative solution, since no longer use this for scrubbing/prewarm we shouldn't allocate that much.
+
+
+ int existingParticleCount = shuriken.GetParticleCount();
+ int totalPossibleEmits = 0;
+ float acc = deltaTime;
+ float fromT = state.t;
+ while(acc >= timeStep)
+ {
+ acc -= timeStep;
+ float toT = fromT + (deltaTime - acc);
+ float dt;
+ const float length = shuriken.m_ReadOnlyState->lengthInSec;
+ if(shuriken.m_ReadOnlyState->looping)
+ {
+ toT = fmodf (toT, length);
+ dt = timeStep;
+ }
+ else
+ {
+ toT = min(toT, length);
+ dt = toT - fromT;
+ }
+
+ size_t numContinuous;
+ const Vector3f emitVelocity = state.emitterVelocity;
+ existingParticleCount += EmitFromModules(shuriken, *shuriken.m_ReadOnlyState, emissionState, numContinuous, emitVelocity, fromT, toT, dt);
+ totalPossibleEmits += existingParticleCount;
+ fromT = toT;
+ }
+
+ totalPossibleEmits += existingParticleCount;
+
+ // Restore emission state
+ state.emissionState = orgEmissionState;
+
+ const int kExtra = 5; // Give a few extra to avoid denying due to rounding etc.
+ return totalPossibleEmits + kExtra;
+}
+
+// @TODO: what about chained effects?
+float ParticleSystem::CalculateSubEmitterMaximumLifeTime(float parentLifeTime) const
+{
+ ParticleSystem* subEmitters[kParticleSystemMaxSubTotal];
+ m_SubModule->GetSubEmitterPtrs(subEmitters);
+
+ float maxLifeTime = 0.0f;
+ for(int i = 0; i < kParticleSystemMaxSubTotal; i++)
+ {
+ if (subEmitters[i] && (subEmitters[i] != this))
+ {
+ const float emitterMaximumLifetime = subEmitters[i]->m_InitialModule.GetLifeTimeCurve().FindMinMax().y;
+ maxLifeTime = max(maxLifeTime, parentLifeTime + emitterMaximumLifetime);
+ maxLifeTime = std::max(maxLifeTime, subEmitters[i]->CalculateSubEmitterMaximumLifeTime(parentLifeTime + emitterMaximumLifetime));
+ }
+ }
+ return maxLifeTime;
+}
+
+void ParticleSystem::SmartReset ()
+{
+ Super::SmartReset();
+ AddParticleSystemRenderer ();
+}
+
+void ParticleSystem::AddParticleSystemRenderer ()
+{
+ if (GameObject* go = GetGameObjectPtr ())
+ {
+ ParticleSystemRenderer* renderer = go->QueryComponent (ParticleSystemRenderer);
+ if (renderer == NULL)
+ {
+ string error;
+ AddComponent (*go, ClassID(ParticleSystemRenderer), NULL, &error);
+ if (error.empty ())
+ go->GetComponent (ParticleSystemRenderer).SetMaterial (GetDefaultParticleMaterial(), 0);
+ else
+ LogString (Format("%s", error.c_str()));
+ }
+ }
+}
+
+
+void ParticleSystem::SetUsesRotationalSpeed()
+{
+ ParticleSystemParticles& ps0 = m_Particles[kParticleBuffer0];
+ if(!ps0.usesRotationalSpeed)
+ ps0.SetUsesRotationalSpeed ();
+#if UNITY_EDITOR
+ ParticleSystemParticles& ps1 = m_Particles[kParticleBuffer1];
+ if(!ps1.usesRotationalSpeed)
+ ps1.SetUsesRotationalSpeed ();
+#endif
+ ParticleSystemParticles& pss = m_ParticlesStaging;
+ if(!pss.usesRotationalSpeed)
+ pss.SetUsesRotationalSpeed ();
+}
+
+void ParticleSystem::SetUsesAxisOfRotation()
+{
+ ParticleSystemParticles& ps0 = m_Particles[kParticleBuffer0];
+ if(!ps0.usesAxisOfRotation)
+ ps0.SetUsesAxisOfRotation ();
+#if UNITY_EDITOR
+ ParticleSystemParticles& ps1 = m_Particles[kParticleBuffer1];
+ if(!ps1.usesAxisOfRotation)
+ ps1.SetUsesAxisOfRotation ();
+#endif
+ ParticleSystemParticles& pss = m_ParticlesStaging;
+ if(!pss.usesAxisOfRotation)
+ pss.SetUsesAxisOfRotation ();
+}
+
+void ParticleSystem::SetUsesEmitAccumulator(int numAccumulators)
+{
+ m_Particles[kParticleBuffer0].SetUsesEmitAccumulator (numAccumulators);
+#if UNITY_EDITOR
+ m_Particles[kParticleBuffer1].SetUsesEmitAccumulator (numAccumulators);
+#endif
+ m_ParticlesStaging.SetUsesEmitAccumulator (numAccumulators);
+}
+
+bool ParticleSystem::GetIsDistanceEmitter() const
+{
+ return (EmissionModule::kEmissionTypeDistance == m_EmissionModule.GetEmissionDataRef().type);
+}
+
+// check if the system would like to use any raycasting in this frame
+bool ParticleSystem::SystemWannaRayCast(const ParticleSystem& system)
+{
+ return system.IsActive() && system.m_CollisionModule && system.m_CollisionModule->GetEnabled() && system.m_CollisionModule->IsWorldCollision() && system.m_RayBudgetState.ReceiveRays();
+}
+
+// check if the system will actually use any raycasting in this frame
+bool ParticleSystem::SystemWillRayCast(const ParticleSystem& system)
+{
+ return system.IsActive() && system.m_CollisionModule && system.m_CollisionModule->GetEnabled() && system.m_CollisionModule->IsWorldCollision() && (system.GetRayBudget() > 0);
+}
+
+// dole out ray budgets to each system that will do raycasting
+void ParticleSystem::AssignRayBudgets()
+{
+ int activeCount = gParticleSystemManager.activeEmitters.size();
+
+ // count the jobs and update quality setting
+ int numApproximateWorldCollisionJobs = 0;
+ for(int i = 0; i < activeCount; i++)
+ {
+ ParticleSystem& system = *gParticleSystemManager.activeEmitters[i];
+ system.m_RayBudgetState.SetQuality( system.m_CollisionModule->GetQuality() );
+ system.SetRayBudget( 0 );
+ if ( SystemWannaRayCast( system ) )
+ {
+ if ( system.m_CollisionModule->IsApproximate() )
+ {
+ numApproximateWorldCollisionJobs++;
+ }
+ else
+ {
+ // high quality always get to trace all rays!
+ system.SetRayBudget( (int)system.GetParticleCount() );
+ }
+ }
+ }
+ if (numApproximateWorldCollisionJobs<1) return;
+
+ // assign ray budget to particle systems
+ int totalBudget = GetQualitySettings().GetCurrent().particleRaycastBudget;
+ int raysPerSystem = std::max( 0, totalBudget / numApproximateWorldCollisionJobs );
+ for(int i = 0; i < activeCount; i++)
+ {
+ ParticleSystem& system = *gParticleSystemManager.activeEmitters[i];
+ if ( SystemWannaRayCast( system ) && system.m_CollisionModule->IsApproximate() )
+ {
+ const int rays = std::min((int)system.GetParticleCount(),raysPerSystem);
+ system.SetRayBudget( rays );
+ totalBudget = std::max( totalBudget-rays, 0);
+ }
+ }
+
+ // assign any remaining rays
+ // TODO: possibly better to sort and go through the list incrementally updating the per system budget, than doing this hacky two pass thing
+ for(int i = 0; i < activeCount; i++)
+ {
+ ParticleSystem& system = *gParticleSystemManager.activeEmitters[i];
+ if ( SystemWannaRayCast( system ) && system.m_CollisionModule->IsApproximate() )
+ {
+ const int rays = std::min(totalBudget,(int)system.GetParticleCount()-system.GetRayBudget());
+ system.SetRayBudget( system.GetRayBudget()+rays );
+ totalBudget -= rays;
+ }
+ system.m_RayBudgetState.Update();
+ }
+}
+
+void ParticleSystem::BeginUpdateAll ()
+{
+ const float deltaTimeEpsilon = 0.0001f;
+ float deltaTime = GetDeltaTime();
+ if(deltaTime < deltaTimeEpsilon)
+ return;
+
+ PROFILER_AUTO(gParticleSystemProfile, NULL)
+
+ for(int i = 0; i < gParticleSystemManager.activeEmitters.size(); i++)
+ {
+ ParticleSystem& system = *gParticleSystemManager.activeEmitters[i];
+
+ if (!system.IsActive ())
+ {
+ AssertStringObject( "UpdateParticle system should not happen on disabled vGO", &system);
+ system.RemoveFromManager();
+ continue;
+ }
+
+#if ENABLE_MULTITHREADED_PARTICLES
+ system.m_State->recordSubEmits = true;
+#else
+ system.m_State->recordSubEmits = false;
+#endif
+ Update0 (system, *system.m_ReadOnlyState, *system.m_State, deltaTime, false);
+ }
+
+ gParticleSystemManager.needSync = true;
+
+ // make sure ray budgets are assigned for the frame
+ ParticleSystem::AssignRayBudgets();
+
+#if ENABLE_MULTITHREADED_PARTICLES
+ JobScheduler& scheduler = GetJobScheduler();
+ int activeCount = gParticleSystemManager.activeEmitters.size();
+
+ // count the jobs
+ int numActiveWorldCollisionJobs = 0;
+ for(int i = 0; i < activeCount; i++)
+ {
+ ParticleSystem& system = *gParticleSystemManager.activeEmitters[i];
+ if ( system.GetRayBudget() > 0 )
+ numActiveWorldCollisionJobs++;
+ }
+
+ // add collision jobs
+ gParticleSystemManager.worldCollisionJobGroup = scheduler.BeginGroup(numActiveWorldCollisionJobs);
+ gParticleSystemManager.jobGroup = scheduler.BeginGroup(activeCount-numActiveWorldCollisionJobs);
+ for(int i = 0; i < activeCount; i++)
+ {
+ ParticleSystem& system = *gParticleSystemManager.activeEmitters[i];
+ system.GetThreadScratchPad().deltaTime = deltaTime;
+ if ( system.GetRayBudget() > 0 )
+ scheduler.SubmitJob (gParticleSystemManager.worldCollisionJobGroup, ParticleSystem::UpdateFunction, &system, NULL);
+ else
+ scheduler.SubmitJob (gParticleSystemManager.jobGroup, ParticleSystem::UpdateFunction, &system, NULL);
+ }
+ scheduler.WaitForGroup(gParticleSystemManager.worldCollisionJobGroup);
+#else
+ for(int i = 0; i < gParticleSystemManager.activeEmitters.size(); i++)
+ {
+ //printf_console("BeginUpdateAll [%d]:\n",i);
+ ParticleSystem& system = *gParticleSystemManager.activeEmitters[i];
+ system.Update1 (system, system.GetParticles((int)ParticleSystem::kParticleBuffer0), deltaTime, false, false);
+ }
+#endif
+
+}
+
+void ParticleSystem::EndUpdateAll ()
+{
+ SyncJobs();
+
+ // messages
+ for (int i = 0; i < gParticleSystemManager.activeEmitters.size(); ++i)
+ {
+ ParticleSystem& system = *gParticleSystemManager.activeEmitters[i];
+ if (!system.IsActive ())
+ continue;
+ if (!system.m_CollisionModule->GetUsesCollisionMessages ())
+ continue;
+ ParticleSystemParticles& ps = system.GetParticles((int)ParticleSystem::kParticleBuffer0);
+ ps.collisionEvents.SwapCollisionEventArrays ();
+ ps.collisionEvents.SendCollisionEvents (system);
+ }
+
+ // Remove emitters that are finished (no longer emitting)
+ for(int i = 0; i < gParticleSystemManager.activeEmitters.size();)
+ {
+ ParticleSystem& system = *gParticleSystemManager.activeEmitters[i];
+ ParticleSystemState& state = *system.m_State;
+ const size_t particleCount = system.GetParticleCount();
+ if ((particleCount == 0) && state.playing && state.stopEmitting)
+ {
+ // collision subemitters may not have needRestart==true when being restarted
+ // from a paused state
+ //Assert (state.needRestart);
+ state.playing = false;
+ system.RemoveFromManager();
+ continue;
+ }
+
+ i++;
+ }
+
+ // Interpolate in the editor for very low preview speeds
+#if UNITY_EDITOR
+ for(int i = 0; i < gParticleSystemManager.activeEmitters.size(); i++)
+ ParticleSystemEditor::PerformInterpolationStep(gParticleSystemManager.activeEmitters[i]);
+#endif
+}
+
+void ParticleSystem::StartParticles(ParticleSystem& system, ParticleSystemParticles& ps, const float prevT, const float t, const float dt, const size_t numContinuous, size_t amountOfParticlesToEmit, float frameOffset)
+{
+ if (amountOfParticlesToEmit <= 0)
+ return;
+
+ const ParticleSystemReadOnlyState& roState = *system.m_ReadOnlyState;
+ ParticleSystemState& state = *system.m_State;
+ size_t fromIndex = system.AddNewParticles(ps, amountOfParticlesToEmit);
+ const Matrix4x4f localToWorld = !roState.useLocalSpace ? state.localToWorld : Matrix4x4f::identity;
+ StartModules (system, roState, state, state.emissionState, state.emitterVelocity, localToWorld, ps, fromIndex, dt, t, numContinuous, frameOffset);
+}
+
+void ParticleSystem::StartParticlesProcedural(ParticleSystem& system, ParticleSystemParticles& ps, const float prevT, const float t, const float dt, const size_t numContinuous, size_t amountOfParticlesToEmit, float frameOffset)
+{
+ DebugAssert(CheckSupportsProcedural(system));
+
+ ParticleSystemState& state = *system.m_State;
+
+ int numParticlesRecorded = 0;
+ for (int i=0;i<state.emitReplay.size();i++)
+ numParticlesRecorded += state.emitReplay[i].particlesToEmit;
+
+ float emissionOffset = state.emissionState.m_ToEmitAccumulator;
+ float emissionGap = state.emissionState.m_ParticleSpacing * dt;
+ amountOfParticlesToEmit = system.LimitParticleCount(numParticlesRecorded + amountOfParticlesToEmit) - numParticlesRecorded;
+
+ if (amountOfParticlesToEmit > 0)
+ {
+ UInt32 randomSeed = 0;
+#if UNITY_EDITOR
+ ParticleSystemEditor::UpdateRandomSeed(system);
+ randomSeed = ParticleSystemEditor::GetRandomSeed(system);
+#endif
+ state.emitReplay.push_back(ParticleSystemEmitReplay(t, amountOfParticlesToEmit, emissionOffset, emissionGap, numContinuous, randomSeed));
+ }
+}
+
+void ParticleSystem::StartModules (ParticleSystem& system, const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, const ParticleSystemEmissionState& emissionState, Vector3f initialVelocity, const Matrix4x4f& matrix, ParticleSystemParticles& ps, size_t fromIndex, float dt, float t, size_t numContinuous, float frameOffset)
+{
+#if UNITY_EDITOR
+ ParticleSystemEditor::UpdateRandomSeed(system);
+ ParticleSystemEditor::ApplyRandomSeed(system, ParticleSystemEditor::GetRandomSeed(system));
+#endif
+
+ system.m_InitialModule.Start (roState, state, ps, matrix, fromIndex, t);
+ if(system.m_ShapeModule.GetEnabled())
+ system.m_ShapeModule.Start (roState, state, ps, matrix, fromIndex, t);
+
+ DebugAssert(roState.lengthInSec > 0.0001f);
+ const float normalizedT = t / roState.lengthInSec;
+ DebugAssert (normalizedT >= 0.0f);
+ DebugAssert (normalizedT <= 1.0f);
+
+ size_t count = ps.array_size();
+ const Vector3f velocityOffset = system.m_InitialModule.GetInheritVelocity() * initialVelocity;
+ for(size_t q = fromIndex; q < count; q++)
+ {
+ const float randomValue = GenerateRandom(ps.randomSeed[q] + kParticleSystemStartSpeedCurveId);
+ ps.velocity[q] *= Evaluate (system.m_InitialModule.GetSpeedCurve(), normalizedT, randomValue);
+ ps.velocity[q] += velocityOffset;
+ }
+
+ for(size_t q = fromIndex; q < count; ) // array size changes
+ {
+ // subFrameOffset allows particles to be spawned at increasing times, thus spacing particles within a single frame.
+ // For example if you spawn particles with high velocity you will get a continous streaming instead of a clump of particles.
+ const int particleIndex = q - fromIndex;
+ float subFrameOffset = (particleIndex < numContinuous) ? (float(particleIndex) + emissionState.m_ToEmitAccumulator) * emissionState.m_ParticleSpacing : 0.0f;
+ DebugAssert(subFrameOffset >= -0.01f);
+ DebugAssert(subFrameOffset <= 1.5f); // Not 1 due to possibly really bad precision
+ subFrameOffset = clamp01(subFrameOffset);
+
+ // Update from curves and apply forces etc.
+ UpdateModulesPreSimulationIncremental (system, roState, state, ps, q, q+1, subFrameOffset * dt);
+
+ // Position change due to where the emitter was at time of emission
+ ps.position[q] -= initialVelocity * (frameOffset + subFrameOffset) * dt;
+
+ // Position, rotation and energy change due to how much the particle has travelled since time of emission
+ // @TODO: Call Simulate instead?
+ ps.lifetime[q] -= subFrameOffset * dt;
+ if((ps.lifetime[q] < 0.0f) && (count > 0))
+ {
+ KillParticle(roState, state, ps, q, count);
+ continue;
+ }
+
+ ps.position[q] += (ps.velocity[q] + ps.animatedVelocity[q]) * subFrameOffset * dt;
+
+ if(ps.usesRotationalSpeed)
+ ps.rotation[q] += ps.rotationalSpeed[q] * subFrameOffset * dt;
+
+ if(system.m_SubModule->GetEnabled())
+ system.m_SubModule->Update (roState, state, ps, q, q+1, subFrameOffset * dt);
+
+ ++q;
+ }
+ ps.array_resize(count);
+}
+
+void ParticleSystem::UpdateModulesPreSimulationIncremental (const ParticleSystem& system, const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& particles, const size_t fromIndex, const size_t toIndex, float dt)
+{
+ const size_t count = particles.array_size();
+ system.m_InitialModule.Update (roState, state, particles, fromIndex, toIndex, dt);
+ if(system.m_RotationModule->GetEnabled())
+ system.m_RotationModule->Update (roState, state, particles, fromIndex, toIndex);
+ if(system.m_VelocityModule->GetEnabled())
+ system.m_VelocityModule->Update (roState, state, particles, fromIndex, toIndex);
+ if(system.m_ForceModule->GetEnabled())
+ system.m_ForceModule->Update (roState, state, particles, fromIndex, toIndex, dt);
+ if(system.m_ExternalForcesModule->GetEnabled())
+ system.m_ExternalForcesModule->Update (roState, state, particles, fromIndex, toIndex, dt);
+ if(system.m_ClampVelocityModule->GetEnabled())
+ system.m_ClampVelocityModule->Update (roState, state, particles, fromIndex, toIndex);
+ if(system.m_RotationBySpeedModule->GetEnabled())
+ system.m_RotationBySpeedModule->Update (roState, state, particles, fromIndex, toIndex);
+
+ Assert(count >= toIndex);
+ Assert(particles.array_size() == count);
+}
+
+void ParticleSystem::UpdateModulesPostSimulationIncremental (const ParticleSystem& system, const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& particles, const size_t fromIndex, float dt)
+{
+ const size_t count = particles.array_size();
+ if(system.m_SubModule->GetEnabled())
+ system.m_SubModule->Update (roState, state, particles, fromIndex, particles.array_size(), dt);
+ Assert(count == particles.array_size());
+
+ if(system.m_CollisionModule->GetEnabled())
+ {
+#if !ENABLE_MULTITHREADED_PARTICLES
+ PROFILER_AUTO(gParticleSystemUpdateCollisions, NULL)
+#endif
+ system.m_CollisionModule->Update (roState, state, particles, fromIndex, dt);
+ }
+}
+
+void ParticleSystem::UpdateModulesNonIncremental (const ParticleSystem& system, const ParticleSystemParticles& particles, ParticleSystemParticlesTempData& psTemp, size_t fromIndex, size_t toIndex)
+{
+ Assert(particles.array_size() == psTemp.particleCount);
+
+ for(int i = fromIndex; i < toIndex; i++)
+ psTemp.color[i] = particles.color[i];
+ for(int i = fromIndex; i < toIndex; i++)
+ psTemp.size[i] = particles.size[i];
+
+ if(system.m_ColorModule->GetEnabled())
+ system.m_ColorModule->Update (particles, psTemp.color, fromIndex, toIndex);
+ if(system.m_ColorBySpeedModule->GetEnabled())
+ system.m_ColorBySpeedModule->Update (particles, psTemp.color, fromIndex, toIndex);
+ if(system.m_SizeModule->GetEnabled())
+ system.m_SizeModule->Update (particles, psTemp.size, fromIndex, toIndex);
+ if(system.m_SizeBySpeedModule->GetEnabled())
+ system.m_SizeBySpeedModule->Update (particles, psTemp.size, fromIndex, toIndex);
+
+ if (gGraphicsCaps.needsToSwizzleVertexColors)
+ std::transform(&psTemp.color[fromIndex], &psTemp.color[toIndex], &psTemp.color[fromIndex], SwizzleColorForPlatform);
+
+ if(system.m_UVModule->GetEnabled())
+ {
+ // No other systems used sheet index yet, allocate!
+ if(!psTemp.sheetIndex)
+ {
+ psTemp.sheetIndex = ALLOC_TEMP_MANUAL(float, psTemp.particleCount);
+ for(int i = 0; i < fromIndex; i++)
+ psTemp.sheetIndex[i] = 0.0f;
+ }
+ system.m_UVModule->Update (particles, psTemp.sheetIndex, fromIndex, toIndex);
+ }
+ else if(psTemp.sheetIndex) // if this is present with disabled module, that means we have a combined buffer with one system not using UV module, just initislize to 0.0f
+ for(int i = fromIndex; i < toIndex; i++)
+ psTemp.sheetIndex[i] = 0.0f;
+}
+
+void ParticleSystem::UpdateModulesIncremental (const ParticleSystem& system, const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& particles, size_t fromIndex, float dt)
+{
+ UpdateModulesPreSimulationIncremental (system, roState, state, particles, fromIndex, particles.array_size(), dt);
+ SimulateParticles(roState, state, particles, fromIndex, dt);
+ UpdateModulesPostSimulationIncremental (system, roState, state, particles, fromIndex, dt);
+}
+
+void ParticleSystem::Update0 (ParticleSystem& system, const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, float dt, bool fixedTimeStep)
+{
+ const Transform& transform = system.GetComponent (Transform);
+ Vector3f oldPosition = state.localToWorld.GetPosition();
+ state.localToWorld = transform.GetLocalToWorldMatrixNoScale ();
+ Matrix4x4f::Invert_General3D(state.localToWorld, state.worldToLocal);
+
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_1_a1))
+ state.emitterScale = transform.GetWorldScaleLossy();
+ else
+ state.emitterScale = transform.GetLocalScale();
+
+ if (state.playing && (dt > 0.0001f))
+ {
+ const Vector3f position = transform.GetPosition();
+
+ if (roState.useLocalSpace)
+ state.emitterVelocity = Vector3f::zero;
+ else
+ state.emitterVelocity = (position - oldPosition) / dt;
+ }
+
+ AddStagingBuffer(system);
+
+ ParticleSystemRenderer* renderer = system.QueryComponent(ParticleSystemRenderer);
+ if(renderer && !renderer->GetScreenSpaceRotation())
+ ParticleSystemRenderer::SetUsesAxisOfRotationRec(system, true);
+
+ if(system.m_RotationModule->GetEnabled() || system.m_RotationBySpeedModule->GetEnabled())
+ system.SetUsesRotationalSpeed();
+
+ int subEmitterBirthTypeCount = system.m_SubModule->GetSubEmitterTypeCount(kParticleSystemSubTypeBirth);
+ if(system.m_SubModule->GetEnabled() && subEmitterBirthTypeCount)
+ system.SetUsesEmitAccumulator (subEmitterBirthTypeCount);
+
+ int subEmitterCount = SetupSubEmitters(system, *system.m_State);
+
+#if ENABLE_MULTITHREADED_PARTICLES
+ if(state.recordSubEmits)
+ {
+ const int numCommands = min(ParticleSystem::CalculateMaximumSubEmitterEmitCount(system, *system.m_State, dt, fixedTimeStep) * subEmitterCount, MAX_NUM_SUB_EMIT_CMDS);
+ ParticleSystemSubEmitCmdBuffer& buffer = system.m_State->subEmitterCommandBuffer;
+ Assert(NULL == buffer.commands);
+ buffer.commands = ALLOC_TEMP_MANUAL(SubEmitterEmitCommand, numCommands);
+ buffer.commandCount = 0;
+ buffer.maxCommandCount = numCommands;
+ }
+#endif
+
+ if(system.m_CollisionModule->GetEnabled())
+ system.m_CollisionModule->AllocateAndCache(roState, state);
+ if(system.m_ExternalForcesModule->GetEnabled())
+ system.m_ExternalForcesModule->AllocateAndCache(roState, state);
+}
+
+void ParticleSystem::Update1 (ParticleSystem& system, ParticleSystemParticles& ps, float dt, bool fixedTimeStep, bool useProcedural, int rayBudget)
+{
+ PROFILER_AUTO(gParticleSystemJobProfile, NULL)
+
+ const ParticleSystemReadOnlyState& roState = *system.m_ReadOnlyState;
+ ParticleSystemState& state = *system.m_State;
+ state.rayBudget = rayBudget;
+
+ // Exposed through script
+ dt *= std::max<float> (roState.speed, 0.0f);
+
+ float timeStep = GetTimeStep(dt, fixedTimeStep);
+ if(timeStep < 0.00001f)
+ return;
+
+ if (state.playing)
+ {
+ state.accumulatedDt += dt;
+
+ if(system.GetIsDistanceEmitter())
+ {
+ float t = state.t + state.accumulatedDt;
+ const float length = roState.lengthInSec;
+ t = roState.looping ? fmodf(t, length) : min(t, length);
+ size_t numContinuous = 0;
+ size_t amountOfParticlesToEmit = system.EmitFromModules (system, roState, state.emissionState, numContinuous, state.emitterVelocity, state.t, t, dt);
+ StartParticles(system, ps, state.t, t, dt, numContinuous, amountOfParticlesToEmit, 0.0f);
+ }
+
+ Update1Incremental(system, roState, state, ps, 0, timeStep, useProcedural);
+
+ if (useProcedural)
+ UpdateProcedural(system, roState, state, ps);
+ }
+
+ UpdateBounds(system, ps, state);
+}
+
+void ParticleSystem::Update2 (ParticleSystem& system, const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, bool fixedTimeStep)
+{
+ if(state.subEmitterCommandBuffer.commandCount > 0)
+ PlaybackSubEmitterCommandBuffer(system, state, fixedTimeStep);
+ state.ClearSubEmitterCommandBuffer();
+
+ CollisionModule::FreeCache(state);
+ ExternalForcesModule::FreeCache(state);
+
+ AddStagingBuffer(system);
+
+ //
+ // Update renderer
+ ParticleSystemRenderer* renderer = system.QueryComponent(ParticleSystemRenderer);
+ if (renderer)
+ {
+ MinMaxAABB result;
+ ParticleSystemRenderer::CombineBoundsRec(system, result, true);
+ renderer->Update (result);
+ }
+}
+
+// Returns true if update loop is executed at least once
+void ParticleSystem::Update1Incremental(ParticleSystem& system, const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& ps, size_t fromIndex, float dt, bool useProcedural)
+{
+ ApplyStartDelay (state.delayT, state.accumulatedDt);
+
+ int numTimeSteps = 0;
+ const int numTimeStepsTotal = int(state.accumulatedDt / dt);
+
+ while (state.accumulatedDt >= dt)
+ {
+ const float prevT = state.t;
+ state.Tick (roState, dt);
+ const float t = state.t;
+ const bool timePassedDuration = t >= (roState.lengthInSec);
+ const float frameOffset = float(numTimeStepsTotal - 1 - numTimeSteps);
+
+ if(!roState.looping && timePassedDuration)
+ system.Stop();
+
+ // Update simulation
+ if (!useProcedural)
+ UpdateModulesIncremental(system, roState, state, ps, fromIndex, dt);
+ else
+ for (int i=0;i<state.emitReplay.size();i++)
+ state.emitReplay[i].aliveTime += dt;
+
+ // Emission
+ bool emit = !system.GetIsDistanceEmitter() && !state.stopEmitting;
+ if(emit)
+ {
+ size_t numContinuous = 0;
+ size_t amountOfParticlesToEmit = system.EmitFromModules (system, roState, state.emissionState, numContinuous, state.emitterVelocity, prevT, t, dt);
+ if(useProcedural)
+ StartParticlesProcedural(system, ps, prevT, t, dt, numContinuous, amountOfParticlesToEmit, frameOffset);
+ else
+ StartParticles(system, ps, prevT, t, dt, numContinuous, amountOfParticlesToEmit, frameOffset);
+ }
+
+ state.accumulatedDt -= dt;
+
+ AddStagingBuffer(system);
+
+ // Workaround for external forces being dependent on AABB (need to update it before the next time step)
+ if(!useProcedural && (state.accumulatedDt >= dt) && system.m_ExternalForcesModule->GetEnabled())
+ UpdateBounds(system, ps, state);
+
+ numTimeSteps++;
+ }
+}
+
+void ParticleSystem::UpdateProcedural (ParticleSystem& system, const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& ps)
+{
+ DebugAssert(CheckSupportsProcedural(system));
+
+ // Clear all particles
+ ps.array_resize(0);
+
+ const Matrix4x4f localToWorld = !roState.useLocalSpace ? state.localToWorld : Matrix4x4f::identity;
+
+ // Emit all particles
+ for (int i=0; i<state.emitReplay.size(); i++)
+ {
+ const ParticleSystemEmitReplay& emit = state.emitReplay[i];
+
+#if UNITY_EDITOR
+ ParticleSystemEditor::ApplyRandomSeed(system, emit.randomSeed);
+#endif
+ //@TODO: remove passing m_State since that is very dangerous when making things procedural compatible
+ size_t previousParticleCount = ps.array_size();
+ system.m_InitialModule.GenerateProcedural (roState, state, ps, emit);
+
+ //@TODO: This can be moved out of the emit all particles loop...
+ if (system.m_ShapeModule.GetEnabled())
+ system.m_ShapeModule.Start (roState, state, ps, localToWorld, previousParticleCount, emit.t);
+
+ // Apply gravity & integrated velocity after shape module so that it picks up any changes done in shapemodule (for example rotating the velocity)
+ Vector3f gravity = system.m_InitialModule.GetGravity(roState, state);
+ float particleIndex = 0.0f;
+ const size_t particleCount = ps.array_size();
+ for (int q = previousParticleCount; q < particleCount; q++)
+ {
+ const float normalizedT = emit.t / roState.lengthInSec;
+ ps.velocity[q] *= Evaluate (system.m_InitialModule.GetSpeedCurve(), normalizedT, GenerateRandom(ps.randomSeed[q] + kParticleSystemStartSpeedCurveId));
+ Vector3f velocity = ps.velocity[q];
+ float frameOffset = (particleIndex + emit.emissionOffset) * emit.emissionGap * float(particleIndex < emit.numContinuous);
+ float aliveTime = emit.aliveTime + frameOffset;
+
+ ps.position[q] += velocity * aliveTime + gravity * aliveTime * aliveTime * 0.5F;
+ ps.velocity[q] += gravity * aliveTime;
+
+ particleIndex += 1.0f;
+ }
+
+ // If no particles were emitted we can get rid of the emit replay state...
+ if (previousParticleCount == ps.array_size())
+ {
+ state.emitReplay[i] = state.emitReplay.back();
+ state.emitReplay.pop_back();
+ i--;
+ }
+ }
+
+ if (system.m_RotationModule->GetEnabled())
+ system.m_RotationModule->UpdateProcedural (state, ps);
+
+ if (system.m_VelocityModule->GetEnabled())
+ system.m_VelocityModule->UpdateProcedural (roState, state, ps);
+
+ if (system.m_ForceModule->GetEnabled())
+ system.m_ForceModule->UpdateProcedural (roState, state, ps);
+
+ // Modules that are not supported by procedural
+ DebugAssert(!system.m_RotationBySpeedModule->GetEnabled()); // find out if possible to support it
+ DebugAssert(!system.m_ClampVelocityModule->GetEnabled()); // unsupported: (Need to compute velocity by deriving position curves...), possible to support?
+ DebugAssert(!system.m_CollisionModule->GetEnabled());
+ DebugAssert(!system.m_SubModule->GetEnabled()); // find out if possible to support it
+ DebugAssert(!system.m_ExternalForcesModule->GetEnabled());
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// // Editor only
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if UNITY_EDITOR
+void ParticleSystem::TransformChanged()
+{
+ if(!IsWorldPlaying() && ParticleSystemEditor::GetResimulation())
+ {
+ const Transform& transform = GetComponent (Transform);
+ Matrix4x4f newMatrix = transform.GetLocalToWorldMatrixNoScale ();
+ newMatrix.SetPosition(Vector3f::zero);
+ Matrix4x4f oldMatrix = m_State->localToWorld;
+ oldMatrix.SetPosition(Vector3f::zero);
+ bool rotationChanged = !CompareApproximately(oldMatrix, newMatrix);
+ if(m_ReadOnlyState->useLocalSpace || rotationChanged)
+ ParticleSystemEditor::PerformCompleteResimulation(this);
+ }
+}
+#endif
+
diff --git a/Runtime/Graphics/ParticleSystem/ParticleSystem.h b/Runtime/Graphics/ParticleSystem/ParticleSystem.h
new file mode 100644
index 0000000..10c8f2a
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/ParticleSystem.h
@@ -0,0 +1,298 @@
+#ifndef SHURIKEN_H
+#define SHURIKEN_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "ParticleSystemParticle.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Modules/InitialModule.h"
+#include "Modules/ShapeModule.h"
+#include "Modules/EmissionModule.h"
+
+struct ParticleSystemEmitReplay;
+struct ParticleSystemState;
+class ParticleSystemModule;
+class SizeModule;
+class RotationModule;
+class ColorModule;
+class UVModule;
+class VelocityModule;
+class ForceModule;
+class ExternalForcesModule;
+class ClampVelocityModule;
+class SizeBySpeedModule;
+class RotationBySpeedModule;
+class ColorBySpeedModule;
+class CollisionModule;
+class SubModule;
+
+enum ParticleSystemSimulationSpace {
+ kSimLocal = 0,
+ kSimWorld = 1,
+};
+
+// TODO: rename
+struct ParticleSystemThreadScratchPad
+{
+ ParticleSystemThreadScratchPad ()
+ : deltaTime (1.0f)
+ {}
+
+ float deltaTime;
+};
+
+enum { kGoodQualityDelay = 0, kMediumQualityDelay = 0, kLowQualityDelay = 4 };
+struct RayBudgetState
+{
+ void SetQuality(int quality)
+ {
+ if ( m_Quality != quality )
+ {
+ switch (quality)
+ {
+ case 0:
+ m_QualityFrameDelay = kGoodQualityDelay;
+ break;
+ case 1:
+ m_QualityFrameDelay = kMediumQualityDelay;
+ break;
+ case 2:
+ m_QualityFrameDelay = kLowQualityDelay;
+ break;
+ default:
+ m_QualityFrameDelay = kGoodQualityDelay;
+ }
+ m_FramesRemaining = m_QualityFrameDelay;
+ m_Quality = quality;
+ };
+ }
+ RayBudgetState() { m_Quality=m_QualityFrameDelay=m_FramesRemaining=0; }
+ bool ReceiveRays() const { return m_FramesRemaining==0; }
+ void Update() { m_FramesRemaining = ( m_FramesRemaining ? m_FramesRemaining-1 : m_QualityFrameDelay ); }
+ int m_Quality;
+ int m_QualityFrameDelay;
+ int m_FramesRemaining;
+};
+
+class ParticleSystem : public Unity::Component
+{
+public:
+ REGISTER_DERIVED_CLASS (ParticleSystem, Unity::Component)
+ DECLARE_OBJECT_SERIALIZE (ParticleSystem)
+
+ enum
+ {
+ kParticleBuffer0,
+#if UNITY_EDITOR // Double buffered + interpolation
+ kParticleBuffer1,
+#endif
+ kNumParticleBuffers,
+ };
+
+ ParticleSystem (MemLabelId label, ObjectCreationMode mode);
+
+ // ParticleSystem (); declared-by-macro
+
+ void SmartReset ();
+ void AddParticleSystemRenderer ();
+
+ void Deactivate (DeactivateOperation operation);
+ void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ #if UNITY_EDITOR
+ // ParticleSystemRenderer always goes with ParticleSystem
+ virtual int GetCoupledComponentClassID() const { return ClassID(ParticleSystemRenderer); }
+ #endif
+
+
+ static void* UpdateFunction (void* rawData);
+
+ static void BeginUpdateAll();
+ static void EndUpdateAll();
+ static void Update(ParticleSystem& system, float deltaTime, bool fixedTimeStep, bool useProcedural, int rayBudget = 0);
+
+ static bool SystemWannaRayCast(const ParticleSystem& system);
+ static bool SystemWillRayCast(const ParticleSystem& system);
+ static void AssignRayBudgets();
+
+ static void SyncJobs();
+
+ static void Emit(ParticleSystem& system, const SubEmitterEmitCommand& command, ParticleSystemEmitMode emitMode);
+
+ // Client interface
+ void Simulate (float t, bool restart); // Fastforwards the particle system by simulating particles over given period of time, then pauses it.
+ void Play (bool autoPrewarm = true);
+ void Stop ();
+ void Pause ();
+ void Emit (int count);
+ void EmitParticleExternal(ParticleSystemParticle* particle);
+ void Clear (bool updateBounds = true);
+ void AutoPrewarm();
+
+ bool IsAlive () const;
+ bool IsPlaying () const;
+ bool IsPaused () const;
+ bool IsStopped () const;
+ float GetStartDelay() const;
+ void SetStartDelay(float value);
+ bool GetLoop () const;
+ void SetLoop (bool loop);
+ bool GetPlayOnAwake () const;
+ void SetPlayOnAwake (bool playOnAwake);
+ ParticleSystemSimulationSpace GetSimulationSpace () const;
+ void SetSimulationSpace (ParticleSystemSimulationSpace simulationSpace);
+ float GetSecPosition () const;
+ void SetSecPosition (float pos);
+ float GetLengthInSec () const;
+ void SetPlaybackSpeed (float speed);
+ float GetPlaybackSpeed () const;
+ void SetRayBudget (int rayBudget);
+ int GetRayBudget() const;
+
+ bool GetEnableEmission() const;
+ void SetEnableEmission(bool value);
+ float GetEmissionRate() const;
+ void SetEmissionRate(float value);
+ float GetStartSpeed() const;
+ void SetStartSpeed(float value);
+ float GetStartSize() const;
+ void SetStartSize(float value);
+ ColorRGBAf GetStartColor() const;
+ void SetStartColor(ColorRGBAf value);
+ float GetStartRotation() const;
+ void SetStartRotation(float value);
+ float GetStartLifeTime() const;
+ void SetStartLifeTime(float value);
+ float GetGravityModifier() const;
+ void SetGravityModifier(float value);
+ UInt32 GetRandomSeed() const;
+ void SetRandomSeed(UInt32 value);
+ int GetMaxNumParticles() const;
+ void SetMaxNumParticles(int value);
+
+ ShapeModule& GetShapeModule () { return m_ShapeModule; }
+
+
+ Matrix4x4f GetLocalToWorldMatrix() const;
+
+ void GetNumTiles(int& uvTilesX, int& uvTilesY) const;
+
+ void AllocateAllStructuresOfArrays();
+ void SetParticlesExternal (ParticleSystemParticle* particles, int size);
+ void GetParticlesExternal (ParticleSystemParticle* particles, int size);
+
+ int GetSafeCollisionEventSize () const;
+ int GetCollisionEventsExternal (int instanceID, MonoParticleCollisionEvent* collisionEvents, int size) const;
+
+ ParticleSystemParticles& GetParticles (int index = -1);
+ const ParticleSystemParticles& GetParticles (int index = -1) const;
+ size_t GetParticleCount () const;
+
+ ParticleSystemThreadScratchPad& GetThreadScratchPad () { return m_ThreadScratchpad; }
+
+ static void InitializeClass ();
+ static void CleanupClass () {};
+
+ void DidModifyMesh ();
+ void DidDeleteMesh ();
+
+#if UNITY_EDITOR
+ void TransformChanged();
+#endif
+
+ void RendererBecameVisible();
+ void RendererBecameInvisible();
+
+ static size_t EmitFromData (ParticleSystemEmissionState& emissionState, size_t& numContinuous, const ParticleSystemEmissionData& emissionData, const Vector3f velocity, float fromT, float toT, float dt, float length);
+private:
+ static void Update0 (ParticleSystem& system, const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, float dt, bool fixedTimeStep);
+ static void Update1 (ParticleSystem& system, ParticleSystemParticles& ps, float dt, bool fixedTimeStep, bool useProcedural, int rayBudget = 0);
+ static void Update2 (ParticleSystem& system, const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, bool fixedTimeStep);
+ static void Update1Incremental(ParticleSystem& system, const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& ps, size_t fromIndex, float dt, bool useProcedural);
+
+ static size_t EmitFromModules (const ParticleSystem& system, const ParticleSystemReadOnlyState& roState, ParticleSystemEmissionState& emissionState, size_t& numContinuous, const Vector3f velocity, float fromT, float toT, float dt);
+ static void StartModules (ParticleSystem& system, const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, const ParticleSystemEmissionState& emissionState, Vector3f initialVelocity, const Matrix4x4f& matrix, ParticleSystemParticles& ps, size_t fromIndex, float dt, float t, size_t numContinuous, float frameOffset);
+ static void StartParticles(ParticleSystem& system, ParticleSystemParticles& ps, const float prevT, const float t, const float dt, const size_t numContinuous, size_t amountOfParticlesToEmit, float frameOffset);
+ static void StartParticlesProcedural(ParticleSystem& system, ParticleSystemParticles& ps, const float prevT, const float t, const float dt, const size_t numContinuous, size_t amountOfParticlesToEmit, float frameOffset);
+ static void UpdateProcedural(ParticleSystem& system, const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& ps);
+ static void UpdateModulesPreSimulationIncremental (const ParticleSystem& system, const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const size_t fromIndex, const size_t toIndex, float dt);
+ static void UpdateModulesPostSimulationIncremental (const ParticleSystem& system, const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& ps, const size_t fromIndex, float dt);
+ static void UpdateModulesIncremental (const ParticleSystem& system, const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& ps, size_t fromIndex, float dt);
+ static void UpdateModulesNonIncremental (const ParticleSystem& system, const ParticleSystemParticles& ps, ParticleSystemParticlesTempData& psTemp, size_t fromIndex, size_t toIndex);
+ static void SimulateParticles (const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& ps, const size_t fromIndex, float dt);
+ static void PlaybackSubEmitterCommandBuffer(const ParticleSystem& shuriken, ParticleSystemState& state, bool fixedTimeStep);
+ static void UpdateBounds(const ParticleSystem& system, const ParticleSystemParticles& ps, ParticleSystemState& state);
+
+ static void AddStagingBuffer(ParticleSystem& system);
+ static int CalculateMaximumSubEmitterEmitCount(ParticleSystem& shuriken, ParticleSystemState& state, float deltaTime, bool fixedTimeStep);
+ static int SetupSubEmitters(ParticleSystem& shuriken, ParticleSystemState& state);
+
+ static bool CheckSupportsProcedural(const ParticleSystem& system);
+ static bool DetermineSupportsProcedural(const ParticleSystem& system);
+
+ bool ComputePrewarmStartParameters(float& prewarmTime, float t);
+
+ void SetUsesAxisOfRotation();
+ void SetUsesEmitAccumulator(int numAccumulators);
+ void SetUsesRotationalSpeed();
+ void KeepUpdating();
+ void Cull();
+
+ size_t AddNewParticles(ParticleSystemParticles& particles, size_t newParticles) const;
+ size_t LimitParticleCount(size_t requestSize) const;
+ float CalculateSubEmitterMaximumLifeTime(float parentLifeTime) const;
+
+ bool GetIsDistanceEmitter() const;
+
+ void AddToManager();
+ void RemoveFromManager();
+
+ ParticleSystemParticles m_Particles[kNumParticleBuffers];
+ ParticleSystemParticles m_ParticlesStaging; // staging buffer for emitting into the emitter
+ ParticleSystemReadOnlyState* m_ReadOnlyState;
+ ParticleSystemState* m_State;
+ InitialModule m_InitialModule;
+ ShapeModule m_ShapeModule;
+ EmissionModule m_EmissionModule;
+
+ // Dependent on energy value
+ SizeModule* m_SizeModule;
+ RotationModule* m_RotationModule; // @TODO: Requires outputs angular velocity and thus requires integration (Inconsistent with other modules in this group)
+ ColorModule* m_ColorModule;
+ UVModule* m_UVModule;
+
+ // Dependent on energy value
+ VelocityModule* m_VelocityModule;
+ ForceModule* m_ForceModule;
+ ExternalForcesModule* m_ExternalForcesModule;
+
+ // Depends on velocity and modifies velocity
+ ClampVelocityModule* m_ClampVelocityModule;
+
+ // Dependent on velocity value
+ SizeBySpeedModule* m_SizeBySpeedModule;
+ RotationBySpeedModule* m_RotationBySpeedModule;
+ ColorBySpeedModule* m_ColorBySpeedModule;
+
+ // Dependent on a position and velocity
+ CollisionModule* m_CollisionModule;
+
+ SubModule* m_SubModule;
+
+ ParticleSystemThreadScratchPad m_ThreadScratchpad;
+
+ RayBudgetState m_RayBudgetState;
+ int m_RayBudget;
+
+private:
+ int m_EmittersIndex;
+
+#if UNITY_EDITOR
+public:
+ int m_EditorRandomSeedIndex;
+ ListNode<ParticleSystem> m_EditorListNode;
+ friend class ParticleSystemEditor;
+#endif
+ friend class ParticleSystemRenderer;
+};
+
+#endif // SHURIKEN_H
diff --git a/Runtime/Graphics/ParticleSystem/ParticleSystemCommon.h b/Runtime/Graphics/ParticleSystem/ParticleSystemCommon.h
new file mode 100644
index 0000000..fd59987
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/ParticleSystemCommon.h
@@ -0,0 +1,48 @@
+#ifndef PARTICLESYSTEMCOMMON_H
+#define PARTICLESYSTEMCOMMON_H
+
+enum ParticleSystemSubType
+{
+ kParticleSystemSubTypeBirth,
+ kParticleSystemSubTypeCollision,
+ kParticleSystemSubTypeDeath,
+};
+
+enum ParticleSystemEmitMode
+{
+ kParticleSystemEMDirect,
+ kParticleSystemEMStaging,
+};
+
+enum
+{
+ kParticleSystemMaxSubBirth = 2,
+ kParticleSystemMaxSubCollision = 2,
+ kParticleSystemMaxSubDeath = 2,
+ kParticleSystemMaxSubTotal = kParticleSystemMaxSubBirth + kParticleSystemMaxSubCollision + kParticleSystemMaxSubDeath,
+};
+
+// Curve id's needed to offset randomness for curves, to avoid visible patterns due to only storing 1 random value per particle
+enum ParticleSystemRandomnessIds
+{
+ // Curves
+ kParticleSystemClampVelocityCurveId = 0x13371337,
+ kParticleSystemForceCurveId = 0x12460f3b,
+ kParticleSystemRotationCurveId = 0x6aed452e,
+ kParticleSystemRotationBySpeedCurveId = 0xdec4aea1,
+ kParticleSystemStartSpeedCurveId = 0x96aa4de3,
+ kParticleSystemSizeCurveId = 0x8d2c8431,
+ kParticleSystemSizeBySpeedCurveId = 0xf3857f6f,
+ kParticleSystemVelocityCurveId = 0xe0fbd834,
+ kParticleSystemUVCurveId = 0x13740583,
+
+ // Gradient
+ kParticleSystemColorGradientId = 0x591bc05c,
+ kParticleSystemColorByVelocityGradientId = 0x40eb95e4,
+
+ // Misc
+ kParticleSystemMeshSelectionId = 0xbc524e5f,
+ kParticleSystemUVRowSelectionId = 0xaf502044,
+};
+
+#endif // PARTICLESYSTEMCOMMON_H
diff --git a/Runtime/Graphics/ParticleSystem/ParticleSystemCurves.cpp b/Runtime/Graphics/ParticleSystem/ParticleSystemCurves.cpp
new file mode 100644
index 0000000..3ac445c
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/ParticleSystemCurves.cpp
@@ -0,0 +1,196 @@
+#include "UnityPrefix.h"
+#include "ParticleSystemCurves.h"
+#include "Runtime/Math/Polynomials.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+// Calculates the min max range of an animation curve analytically
+static void CalculateCurveRangesValue(Vector2f& minMaxValue, const AnimationCurve& curve)
+{
+ const int keyCount = curve.GetKeyCount();
+ if (keyCount == 0)
+ return;
+
+ if (keyCount == 1)
+ {
+ CalculateMinMax(minMaxValue, curve.GetKey(0).value);
+ }
+ else
+ {
+ int segmentCount = keyCount - 1;
+ CalculateMinMax(minMaxValue, curve.GetKey(0).value);
+ for (int i = 0;i<segmentCount;i++)
+ {
+ DebugAssert(i+1 < keyCount);
+ AnimationCurve::Cache cache;
+ curve.CalculateCacheData(cache, i, i + 1, 0.0F);
+
+ // Differentiate polynomial
+ float a = 3.0f * cache.coeff[0];
+ float b = 2.0f * cache.coeff[1];
+ float c = 1.0f * cache.coeff[2];
+
+ const float start = curve.GetKey(i).time;
+ const float end = curve.GetKey(i+1).time;
+
+ float roots[2];
+ int numRoots = QuadraticPolynomialRootsGeneric(a, b, c, roots[0], roots[1]);
+ for(int r = 0; r < numRoots; r++)
+ if((roots[r] >= 0.0f) && ((roots[r] + start) < end))
+ CalculateMinMax(minMaxValue, Polynomial::EvalSegment(roots[r], cache.coeff));
+ CalculateMinMax(minMaxValue, Polynomial::EvalSegment(end-start, cache.coeff));
+ }
+ }
+}
+
+bool BuildCurves (MinMaxOptimizedPolyCurves& polyCurves, const MinMaxAnimationCurves& editorCurves, float scalar, short minMaxState)
+{
+ bool isOptimizedCurve = polyCurves.max.BuildOptimizedCurve(editorCurves.max, scalar);
+ if ((minMaxState != kMMCTwoCurves) && (minMaxState != kMMCTwoConstants))
+ isOptimizedCurve = isOptimizedCurve && polyCurves.min.BuildOptimizedCurve(editorCurves.max, scalar);
+ else
+ isOptimizedCurve = isOptimizedCurve && polyCurves.min.BuildOptimizedCurve(editorCurves.min, scalar);
+ return isOptimizedCurve;
+}
+
+void BuildCurves (MinMaxPolyCurves& polyCurves, const MinMaxAnimationCurves& editorCurves, float scalar, short minMaxState)
+{
+ polyCurves.max.BuildCurve(editorCurves.max, scalar);
+ if ((minMaxState != kMMCTwoCurves) && (minMaxState != kMMCTwoConstants))
+ polyCurves.min.BuildCurve(editorCurves.max, scalar);
+ else
+ polyCurves.min.BuildCurve(editorCurves.min, scalar);
+}
+
+bool CurvesSupportProcedural (const MinMaxAnimationCurves& editorCurves, short minMaxState)
+{
+ bool isValid = PolynomialCurve::IsValidCurve(editorCurves.max);
+ if ((minMaxState != kMMCTwoCurves) && (minMaxState != kMMCTwoConstants))
+ return isValid;
+ else
+ return isValid && PolynomialCurve::IsValidCurve(editorCurves.min);
+}
+
+
+void MinMaxOptimizedPolyCurves::Integrate ()
+{
+ max.Integrate ();
+ min.Integrate ();
+}
+
+void MinMaxOptimizedPolyCurves::DoubleIntegrate ()
+{
+ max.DoubleIntegrate ();
+ min.DoubleIntegrate ();
+}
+
+Vector2f MinMaxOptimizedPolyCurves::FindMinMaxIntegrated()
+{
+ Vector2f minRange = min.FindMinMaxIntegrated();
+ Vector2f maxRange = max.FindMinMaxIntegrated();
+ Vector2f result = Vector2f(std::min(minRange.x, maxRange.x), std::max(minRange.y, maxRange.y));
+ return result;
+}
+
+Vector2f MinMaxOptimizedPolyCurves::FindMinMaxDoubleIntegrated()
+{
+ Vector2f minRange = min.FindMinMaxDoubleIntegrated();
+ Vector2f maxRange = max.FindMinMaxDoubleIntegrated();
+ Vector2f result = Vector2f(std::min(minRange.x, maxRange.x), std::max(minRange.y, maxRange.y));
+ return result;
+}
+
+void MinMaxPolyCurves::Integrate ()
+{
+ max.Integrate ();
+ min.Integrate ();
+}
+
+void MinMaxPolyCurves::DoubleIntegrate ()
+{
+ max.DoubleIntegrate ();
+ min.DoubleIntegrate ();
+}
+
+Vector2f MinMaxPolyCurves::FindMinMaxIntegrated()
+{
+ Vector2f minRange = min.FindMinMaxIntegrated();
+ Vector2f maxRange = max.FindMinMaxIntegrated();
+ Vector2f result = Vector2f(std::min(minRange.x, maxRange.x), std::max(minRange.y, maxRange.y));
+ return result;
+}
+
+Vector2f MinMaxPolyCurves::FindMinMaxDoubleIntegrated()
+{
+ Vector2f minRange = min.FindMinMaxDoubleIntegrated();
+ Vector2f maxRange = max.FindMinMaxDoubleIntegrated();
+ Vector2f result = Vector2f(std::min(minRange.x, maxRange.x), std::max(minRange.y, maxRange.y));
+ return result;
+}
+
+MinMaxCurve::MinMaxCurve ()
+: scalar (1.0f)
+, minMaxState (kMMCScalar)
+, isOptimizedCurve(false)
+{
+ SetPolynomialCurveToValue (editorCurves.max, polyCurves.max, 1.0f);
+ SetPolynomialCurveToValue (editorCurves.min, polyCurves.min, 0.0f);
+}
+
+Vector2f MinMaxCurve::FindMinMax() const
+{
+ Vector2f result = Vector2f(std::numeric_limits<float>::infinity (), -std::numeric_limits<float>::infinity ());
+ CalculateCurveRangesValue(result, editorCurves.max);
+ if((minMaxState == kMMCTwoCurves) || (minMaxState == kMMCTwoConstants))
+ CalculateCurveRangesValue(result, editorCurves.min);
+ return result * GetScalar();
+}
+
+Vector2f MinMaxCurve::FindMinMaxIntegrated() const
+{
+ if(IsOptimized())
+ {
+ MinMaxOptimizedPolyCurves integrated = polyCurves;
+ integrated.Integrate();
+ return integrated.FindMinMaxIntegrated();
+ }
+ else
+ {
+ DebugAssert(CurvesSupportProcedural (editorCurves, minMaxState));
+ MinMaxPolyCurves integrated;
+ BuildCurves(integrated, editorCurves, GetScalar(), minMaxState);
+ integrated.Integrate();
+ return integrated.FindMinMaxIntegrated();
+ }
+}
+
+Vector2f MinMaxCurve::FindMinMaxDoubleIntegrated() const
+{
+ if(IsOptimized())
+ {
+ MinMaxOptimizedPolyCurves doubleIntegrated = polyCurves;
+ doubleIntegrated.DoubleIntegrate();
+ return doubleIntegrated.FindMinMaxDoubleIntegrated();
+ }
+ else
+ {
+ DebugAssert(CurvesSupportProcedural (editorCurves, minMaxState));
+ MinMaxPolyCurves doubleIntegrated;
+ BuildCurves(doubleIntegrated, editorCurves, GetScalar(), minMaxState);
+ doubleIntegrated.DoubleIntegrate();
+ return doubleIntegrated.FindMinMaxDoubleIntegrated();
+ }
+}
+
+template<class TransferFunction>
+void MinMaxCurve::Transfer (TransferFunction& transfer)
+{
+ transfer.Transfer (scalar, "scalar");
+ transfer.Transfer (editorCurves.max, "maxCurve");
+ transfer.Transfer (editorCurves.min, "minCurve");
+ TRANSFER (minMaxState); transfer.Align ();
+ if (transfer.IsReading ())
+ isOptimizedCurve = BuildCurves(polyCurves, editorCurves, scalar, minMaxState);
+}
+INSTANTIATE_TEMPLATE_TRANSFER(MinMaxCurve)
diff --git a/Runtime/Graphics/ParticleSystem/ParticleSystemCurves.h b/Runtime/Graphics/ParticleSystem/ParticleSystemCurves.h
new file mode 100644
index 0000000..5d59eb2
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/ParticleSystemCurves.h
@@ -0,0 +1,187 @@
+#ifndef SHURIKENCURVES_H
+#define SHURIKENCURVES_H
+
+#include "ParticleSystemParticle.h"
+#include "PolynomialCurve.h"
+#include "Runtime/Math/AnimationCurve.h"
+
+
+struct MinMaxCurve;
+
+// Some profile numbers from a run with 250,000 particles evaluating 3 velocity properties each on Intel i7-2600 CPU @ 3.4 GHz
+// Scalar: 4.6 ms
+// Optimized curve: 7.2 ms
+// Random between 2 scalars: 9.5 ms
+// Random between 2 curves: 9.5 ms
+// Non-optimized curve: 10.0 ms
+// Random between 2 non-optimized curves: 12.0 ms
+
+enum ParticleSystemCurveEvalMode
+{
+ kEMScalar,
+ kEMOptimized,
+ kEMOptimizedMinMax,
+ kEMSlow,
+};
+
+enum MinMaxCurveState
+{
+ kMMCScalar = 0,
+ kMMCCurve = 1,
+ kMMCTwoCurves = 2,
+ kMMCTwoConstants = 3
+};
+
+struct MinMaxOptimizedPolyCurves
+{
+ void Integrate();
+ void DoubleIntegrate();
+ Vector2f FindMinMaxIntegrated();
+ Vector2f FindMinMaxDoubleIntegrated();
+
+ OptimizedPolynomialCurve max;
+ OptimizedPolynomialCurve min;
+};
+
+inline float EvaluateIntegrated (const MinMaxOptimizedPolyCurves& curves, float t, float factor)
+{
+ const float v0 = curves.min.EvaluateIntegrated (t);
+ const float v1 = curves.max.EvaluateIntegrated (t);
+ return Lerp (v0, v1, factor);
+}
+
+inline float EvaluateDoubleIntegrated (const MinMaxOptimizedPolyCurves& curves, float t, float factor)
+{
+ const float v0 = curves.min.EvaluateDoubleIntegrated (t);
+ const float v1 = curves.max.EvaluateDoubleIntegrated (t);
+ return Lerp (v0, v1, factor);
+}
+
+struct MinMaxPolyCurves
+{
+ void Integrate();
+ void DoubleIntegrate();
+ Vector2f FindMinMaxIntegrated();
+ Vector2f FindMinMaxDoubleIntegrated();
+
+ PolynomialCurve max;
+ PolynomialCurve min;
+};
+
+inline float EvaluateIntegrated (const MinMaxPolyCurves& curves, float t, float factor)
+{
+ const float v0 = curves.min.EvaluateIntegrated (t);
+ const float v1 = curves.max.EvaluateIntegrated (t);
+ return Lerp (v0, v1, factor);
+}
+
+inline float EvaluateDoubleIntegrated (const MinMaxPolyCurves& curves, float t, float factor)
+{
+ const float v0 = curves.min.EvaluateDoubleIntegrated (t);
+ const float v1 = curves.max.EvaluateDoubleIntegrated (t);
+ return Lerp (v0, v1, factor);
+}
+
+struct MinMaxAnimationCurves
+{
+ bool SupportsProcedural ();
+
+ AnimationCurve max;
+ AnimationCurve min;
+};
+
+bool BuildCurves (MinMaxOptimizedPolyCurves& polyCurves, const MinMaxAnimationCurves& editorCurves, float scalar, short minMaxState);
+void BuildCurves (MinMaxPolyCurves& polyCurves, const MinMaxAnimationCurves& editorCurves, float scalar, short minMaxState);
+bool CurvesSupportProcedural (const MinMaxAnimationCurves& editorCurves, short minMaxState);
+
+struct MinMaxCurve
+{
+ MinMaxOptimizedPolyCurves polyCurves;
+private:
+ float scalar; // Since scalar is baked into the optimized curve we use the setter function to modify it.
+
+public:
+ short minMaxState; // see enum MinMaxCurveState
+ bool isOptimizedCurve;
+
+ MinMaxAnimationCurves editorCurves;
+
+ MinMaxCurve ();
+
+ inline float GetScalar() const { return scalar; }
+ inline void SetScalar(float value) { scalar = value; BuildCurves(polyCurves, editorCurves, scalar, minMaxState); }
+
+ bool IsOptimized () const { return isOptimizedCurve; }
+ bool UsesMinMax () const { return (minMaxState == kMMCTwoCurves) || (minMaxState == kMMCTwoConstants); }
+
+ DEFINE_GET_TYPESTRING (MinMaxCurve)
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+ Vector2f FindMinMax() const;
+ Vector2f FindMinMaxIntegrated() const;
+ Vector2f FindMinMaxDoubleIntegrated() const;
+};
+
+inline float EvaluateSlow (const MinMaxCurve& curve, float t, float factor)
+{
+ const float v = curve.editorCurves.max.Evaluate(t) * curve.GetScalar ();
+ if (curve.minMaxState == kMMCTwoCurves)
+ return Lerp (curve.editorCurves.min.Evaluate(t) * curve.GetScalar (), v, factor);
+ else
+ return v;
+}
+
+template<ParticleSystemCurveEvalMode mode>
+inline float Evaluate (const MinMaxCurve& curve, float t, float factor = 1.0F)
+{
+ if(mode == kEMScalar)
+ {
+ return curve.GetScalar();
+ }
+ if(mode == kEMOptimized)
+ {
+ DebugAssert(curve.isOptimizedCurve);
+ return curve.polyCurves.max.Evaluate (t);
+ }
+ else if (mode == kEMOptimizedMinMax)
+ {
+ DebugAssert(curve.isOptimizedCurve);
+ const float v0 = curve.polyCurves.min.Evaluate (t);
+ const float v1 = curve.polyCurves.max.Evaluate (t);
+ return Lerp (v0, v1, factor);
+ }
+ else if (mode == kEMSlow)
+ {
+ return EvaluateSlow (curve, t, factor);
+ }
+}
+
+inline float Evaluate (const MinMaxCurve& curve, float t, float randomValue = 1.0F)
+{
+ if (curve.minMaxState == kMMCScalar)
+ return curve.GetScalar ();
+
+ if (curve.minMaxState == kMMCTwoConstants)
+ return Lerp ( curve.editorCurves.min.GetKey(0).value * curve.GetScalar (),
+ curve.editorCurves.max.GetKey(0).value * curve.GetScalar (), randomValue);
+
+ DebugAssert(t <= 1.0F && t >= 0.0F);
+ if (curve.isOptimizedCurve)
+ return Evaluate<kEMOptimizedMinMax> (curve, t, randomValue);
+ else
+ return Evaluate<kEMSlow> (curve, t, randomValue);
+}
+
+struct DualMinMax3DPolyCurves
+{
+ MinMaxOptimizedPolyCurves optX;
+ MinMaxOptimizedPolyCurves optY;
+ MinMaxOptimizedPolyCurves optZ;
+ MinMaxPolyCurves x;
+ MinMaxPolyCurves y;
+ MinMaxPolyCurves z;
+};
+
+#endif // SHURIKENCURVES_H
diff --git a/Runtime/Graphics/ParticleSystem/ParticleSystemGradients.cpp b/Runtime/Graphics/ParticleSystem/ParticleSystemGradients.cpp
new file mode 100644
index 0000000..efe95c0
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/ParticleSystemGradients.cpp
@@ -0,0 +1,27 @@
+#include "UnityPrefix.h"
+#include "ParticleSystemGradients.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+MinMaxGradient::MinMaxGradient()
+: minMaxState (kMMGColor), minColor (255,255,255,255), maxColor (255,255,255,255)
+{
+}
+
+void MinMaxGradient::InitializeOptimized(OptimizedMinMaxGradient& g)
+{
+ maxGradient.InitializeOptimized(g.max);
+ if(minMaxState == kMMGRandomBetweenTwoGradients)
+ minGradient.InitializeOptimized(g.min);
+}
+
+template<class TransferFunction>
+void MinMaxGradient::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (maxGradient);
+ TRANSFER (minGradient);
+ TRANSFER (minColor);
+ TRANSFER (maxColor);
+ TRANSFER (minMaxState); transfer.Align ();
+}
+INSTANTIATE_TEMPLATE_TRANSFER(MinMaxGradient)
diff --git a/Runtime/Graphics/ParticleSystem/ParticleSystemGradients.h b/Runtime/Graphics/ParticleSystem/ParticleSystemGradients.h
new file mode 100644
index 0000000..bb74cde
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/ParticleSystemGradients.h
@@ -0,0 +1,87 @@
+#ifndef SHURIKENGRADIENTS_H
+#define SHURIKENGRADIENTS_H
+
+#include "Runtime/Math/Gradient.h"
+
+enum MinMaxGradientEvalMode
+{
+ kGEMGradient,
+ kGEMGradientMinMax,
+ kGEMSlow,
+};
+
+enum MinMaxGradientState
+{
+ kMMGColor = 0,
+ kMMGGradient = 1,
+ kMMGRandomBetweenTwoColors = 2,
+ kMMGRandomBetweenTwoGradients = 3
+};
+
+struct OptimizedMinMaxGradient
+{
+ OptimizedGradient max;
+ OptimizedGradient min;
+};
+
+inline ColorRGBA32 EvaluateGradient (const OptimizedMinMaxGradient& g, float t)
+{
+ return g.max.Evaluate(t);
+}
+
+inline ColorRGBA32 EvaluateRandomGradient (const OptimizedMinMaxGradient& g, float t, UInt32 factor)
+{
+ return Lerp (g.min.Evaluate(t), g.max.Evaluate(t), factor);
+}
+
+struct MinMaxGradient
+{
+ GradientNEW maxGradient;
+ GradientNEW minGradient;
+ ColorRGBA32 minColor; // we have the colors separate to prevent destroying the gradients
+ ColorRGBA32 maxColor;
+ short minMaxState; // see enum State
+
+ MinMaxGradient();
+
+ DEFINE_GET_TYPESTRING (MinMaxGradient)
+
+ void InitializeOptimized(OptimizedMinMaxGradient& g);
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+};
+
+inline ColorRGBA32 EvaluateColor (const MinMaxGradient& gradient)
+{
+ return gradient.maxColor;
+}
+
+inline ColorRGBA32 EvaluateGradient (const MinMaxGradient& gradient, float t)
+{
+ return gradient.maxGradient.Evaluate(t);
+}
+
+inline ColorRGBA32 EvaluateRandomColor (const MinMaxGradient& gradient, UInt32 factor)
+{
+ return Lerp (gradient.minColor, gradient.maxColor, factor);
+}
+
+inline ColorRGBA32 EvaluateRandomGradient (const MinMaxGradient& gradient, float t, UInt32 factor)
+{
+ return Lerp (gradient.minGradient.Evaluate(t), gradient.maxGradient.Evaluate(t), factor);
+}
+
+inline ColorRGBA32 Evaluate (const MinMaxGradient& gradient, float t, UInt32 factor = 0xff)
+{
+ if (gradient.minMaxState == kMMGColor)
+ return EvaluateColor(gradient);
+ else if (gradient.minMaxState == kMMGGradient)
+ return EvaluateGradient(gradient, t);
+ else if (gradient.minMaxState == kMMGRandomBetweenTwoColors)
+ return EvaluateRandomColor(gradient, factor);
+ else // gradient.minMaxState == kMMGRandomBetweenTwoGradients
+ return EvaluateRandomGradient(gradient, t, factor);
+}
+
+#endif // SHURIKENGRADIENTS_H
diff --git a/Runtime/Graphics/ParticleSystem/ParticleSystemParticle.cpp b/Runtime/Graphics/ParticleSystem/ParticleSystemParticle.cpp
new file mode 100644
index 0000000..d10bee0
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/ParticleSystemParticle.cpp
@@ -0,0 +1,323 @@
+#include "UnityPrefix.h"
+#include "ParticleSystemParticle.h"
+
+#include "Runtime/Dynamics/Collider.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Threads/Thread.h"
+
+void InitializeEmitAccumulator(dynamic_array<float>& emitAccumulator, const size_t count)
+{
+ emitAccumulator.resize_uninitialized(count);
+ memset(emitAccumulator.begin(), 0, count * sizeof(float));
+}
+
+ParticleCollisionEvent::ParticleCollisionEvent (const Vector3f& intersection, const Vector3f& normal, const Vector3f& velocity, int colliderInstanceID, int rigidBodyOrColliderInstanceID)
+{
+ m_Intersection = intersection;
+ m_Normal = normal;
+ m_Velocity = velocity;
+ m_ColliderInstanceID = colliderInstanceID;
+ m_RigidBodyOrColliderInstanceID = rigidBodyOrColliderInstanceID;
+}
+
+size_t ParticleSystemParticles::GetParticleSize()
+{
+ // @TODO: How do we guard against elements we remove
+ return sizeof(ParticleSystemParticle);
+}
+
+void ParticleSystemParticles::SetUsesAxisOfRotation()
+{
+ usesAxisOfRotation = true;
+ const size_t count = position.size();
+ axisOfRotation.resize_uninitialized(count);
+ for(int i = 0; i < count; i++)
+ axisOfRotation[i] = Vector3f::yAxis;
+}
+
+void ParticleSystemParticles::SetUsesRotationalSpeed()
+{
+ usesRotationalSpeed = true;
+ const size_t count = position.size();
+ rotationalSpeed.resize_uninitialized(count);
+ for(int i = 0; i < count; i++)
+ rotationalSpeed[i] = 0.0f;
+}
+
+void ParticleSystemParticles::SetUsesCollisionEvents (bool wantUsesCollisionEvents)
+{
+ if (usesCollisionEvents == wantUsesCollisionEvents) return;
+ usesCollisionEvents = wantUsesCollisionEvents;
+ if (!usesCollisionEvents)
+ {
+ collisionEvents.Clear ();
+ }
+}
+
+bool ParticleSystemParticles::GetUsesCollisionEvents () const
+{
+ return usesCollisionEvents;
+}
+
+void ParticleSystemParticles::SetUsesEmitAccumulator(int numAccumulators)
+{
+ Assert(numAccumulators <= kParticleSystemMaxNumEmitAccumulators);
+ const size_t count = position.size();
+ for(int i = numEmitAccumulators; i < numAccumulators; i++)
+ InitializeEmitAccumulator(emitAccumulator[i], count);
+
+ numEmitAccumulators = numAccumulators;
+}
+
+void ParticleSystemParticles::CopyFromArrayAOS(ParticleSystemParticle* particles, int size)
+{
+ Assert(usesAxisOfRotation);
+ Assert(numEmitAccumulators == kParticleSystemMaxNumEmitAccumulators);
+ for(int i = 0; i < size; i++)
+ {
+ (*this).position[i] = particles[i].position;
+ (*this).velocity[i] = particles[i].velocity;
+ (*this).animatedVelocity[i] = particles[i].animatedVelocity;
+ (*this).axisOfRotation[i] = particles[i].axisOfRotation;
+ (*this).rotation[i] = particles[i].rotation;
+ if(usesRotationalSpeed)
+ (*this).rotationalSpeed[i] = particles[i].rotationalSpeed;
+ (*this).size[i] = particles[i].size;
+ (*this).color[i] = particles[i].color;
+ (*this).randomSeed[i] = particles[i].randomSeed;
+ (*this).lifetime[i] = particles[i].lifetime;
+ (*this).startLifetime[i] = particles[i].startLifetime;
+ for(int acc = 0; acc < kParticleSystemMaxNumEmitAccumulators; acc++)
+ (*this).emitAccumulator[acc][i] = particles[i].emitAccumulator[acc];
+ }
+}
+
+void ParticleSystemParticles::CopyToArrayAOS(ParticleSystemParticle* particles, int size)
+{
+ Assert(usesAxisOfRotation);
+ Assert(numEmitAccumulators == kParticleSystemMaxNumEmitAccumulators);
+ for(int i = 0; i < size; i++)
+ {
+ particles[i].position = (*this).position[i];
+ particles[i].velocity = (*this).velocity[i];
+ particles[i].animatedVelocity = (*this).animatedVelocity[i];
+ particles[i].axisOfRotation = (*this).axisOfRotation[i];
+ particles[i].rotation = (*this).rotation[i];
+ if(usesRotationalSpeed)
+ particles[i].rotationalSpeed = (*this).rotationalSpeed[i];
+ particles[i].size = (*this).size[i];
+ particles[i].color = (*this).color[i];
+ particles[i].randomSeed = (*this).randomSeed[i];
+ particles[i].lifetime = (*this).lifetime[i];
+ particles[i].startLifetime = (*this).startLifetime[i];
+ for(int acc = 0; acc < kParticleSystemMaxNumEmitAccumulators; acc++)
+ particles[i].emitAccumulator[acc] = (*this).emitAccumulator[acc][i];
+ }
+}
+
+void ParticleSystemParticles::AddParticle(ParticleSystemParticle* particle)
+{
+ const size_t count = array_size();
+ array_resize(count+1);
+ position[count] = particle->position;
+ velocity[count] = particle->velocity;
+ animatedVelocity[count] = Vector3f::zero;
+ lifetime[count] = particle->lifetime;
+ startLifetime[count] = particle->startLifetime;
+ size[count] = particle->size;
+ rotation[count] = particle->rotation;
+ if(usesRotationalSpeed)
+ rotationalSpeed[count] = particle->rotationalSpeed;
+ color[count] = particle->color;
+ randomSeed[count] = particle->randomSeed;
+ if(usesAxisOfRotation)
+ axisOfRotation[count] = particle->axisOfRotation;
+ for(int acc = 0; acc < numEmitAccumulators; acc++)
+ emitAccumulator[acc][count] = 0.0f;
+}
+
+size_t ParticleSystemParticles::array_size () const
+{
+ return position.size();
+}
+
+void ParticleSystemParticles::array_resize (size_t i)
+{
+ position.resize_uninitialized(i);
+ velocity.resize_uninitialized(i);
+ animatedVelocity.resize_uninitialized(i);
+ rotation.resize_uninitialized(i);
+ if(usesRotationalSpeed)
+ rotationalSpeed.resize_uninitialized(i);
+ size.resize_uninitialized(i);
+ color.resize_uninitialized(i);
+ randomSeed.resize_uninitialized(i);
+ lifetime.resize_uninitialized(i);
+ startLifetime.resize_uninitialized(i);
+ if(usesAxisOfRotation)
+ axisOfRotation.resize_uninitialized(i);
+ for(int acc = 0; acc < numEmitAccumulators; acc++)
+ emitAccumulator[acc].resize_uninitialized(i);
+}
+
+void ParticleSystemParticles::element_swap(size_t i, size_t j)
+{
+ std::swap(position[i], position[j]);
+ std::swap(velocity[i], velocity[j]);
+ std::swap(animatedVelocity[i], animatedVelocity[j]);
+ std::swap(rotation[i], rotation[j]);
+ if(usesRotationalSpeed)
+ std::swap(rotationalSpeed[i], rotationalSpeed[j]);
+ std::swap(size[i], size[j]);
+ std::swap(color[i], color[j]);
+ std::swap(randomSeed[i], randomSeed[j]);
+ std::swap(lifetime[i], lifetime[j]);
+ std::swap(startLifetime[i], startLifetime[j]);
+ if(usesAxisOfRotation)
+ std::swap(axisOfRotation[i], axisOfRotation[j]);
+ for(int acc = 0; acc < numEmitAccumulators; acc++)
+ std::swap(emitAccumulator[acc][i], emitAccumulator[acc][j]);
+}
+
+void ParticleSystemParticles::element_assign(size_t i, size_t j)
+{
+ position[i] = position[j];
+ velocity[i] = velocity[j];
+ animatedVelocity[i] = animatedVelocity[j];
+ rotation[i] = rotation[j];
+ if(usesRotationalSpeed)
+ rotationalSpeed[i] = rotationalSpeed[j];
+ size[i] = size[j];
+ color[i] = color[j];
+ randomSeed[i] = randomSeed[j];
+ lifetime[i] = lifetime[j];
+ startLifetime[i] = startLifetime[j];
+ if(usesAxisOfRotation)
+ axisOfRotation[i] = axisOfRotation[j];
+ for(int acc = 0; acc < numEmitAccumulators; acc++)
+ emitAccumulator[acc][i] = emitAccumulator[acc][j];
+}
+
+void ParticleSystemParticles::array_assign_external(void* data, const int numParticles)
+{
+#define SHURIKEN_INCREMENT_ASSIGN_PTRS(element, type) { beginPtr = endPtr; endPtr += numParticles * sizeof(type); (element).assign_external((type*)beginPtr, (type*)endPtr); }
+
+ UInt8* beginPtr = 0;
+ UInt8* endPtr = (UInt8*)data;
+
+ SHURIKEN_INCREMENT_ASSIGN_PTRS(position, Vector3f);
+ SHURIKEN_INCREMENT_ASSIGN_PTRS(velocity, Vector3f);
+ SHURIKEN_INCREMENT_ASSIGN_PTRS(animatedVelocity, Vector3f);
+ SHURIKEN_INCREMENT_ASSIGN_PTRS(rotation, float);
+ if(usesRotationalSpeed)
+ SHURIKEN_INCREMENT_ASSIGN_PTRS(rotationalSpeed, float);
+ SHURIKEN_INCREMENT_ASSIGN_PTRS(size, float);
+ SHURIKEN_INCREMENT_ASSIGN_PTRS(color, ColorRGBA32);
+ SHURIKEN_INCREMENT_ASSIGN_PTRS(randomSeed, UInt32);
+ SHURIKEN_INCREMENT_ASSIGN_PTRS(lifetime, float);
+ SHURIKEN_INCREMENT_ASSIGN_PTRS(startLifetime, float);
+ if(usesAxisOfRotation)
+ SHURIKEN_INCREMENT_ASSIGN_PTRS(axisOfRotation, Vector3f);
+ for(int acc = 0; acc < numEmitAccumulators; acc++)
+ SHURIKEN_INCREMENT_ASSIGN_PTRS(emitAccumulator[acc], float);
+#undef SHURIKEN_INCREMENT_ASSIGN_PTRS
+}
+
+void ParticleSystemParticles::array_merge_preallocated(const ParticleSystemParticles& rhs, const int offset, const bool needAxisOfRotation, const bool needEmitAccumulator)
+{
+#define SHURIKEN_COPY_DATA(element, type) memcpy(&element[offset], &rhs.element[0], count * sizeof(type))
+
+ const size_t count = rhs.array_size();
+ if(0 == count)
+ return;
+
+ Assert((rhs.array_size() + offset) <= array_size());
+ SHURIKEN_COPY_DATA(position, Vector3f);
+ SHURIKEN_COPY_DATA(velocity, Vector3f);
+ SHURIKEN_COPY_DATA(animatedVelocity, Vector3f);
+ SHURIKEN_COPY_DATA(rotation, float);
+ if(usesRotationalSpeed)
+ SHURIKEN_COPY_DATA(rotationalSpeed, float);
+ SHURIKEN_COPY_DATA(size, float);
+ SHURIKEN_COPY_DATA(color, ColorRGBA32);
+ SHURIKEN_COPY_DATA(randomSeed, UInt32);
+ SHURIKEN_COPY_DATA(lifetime, float);
+ SHURIKEN_COPY_DATA(startLifetime, float);
+
+ if(needAxisOfRotation)
+ {
+ Assert(usesAxisOfRotation && rhs.usesAxisOfRotation);
+ SHURIKEN_COPY_DATA(axisOfRotation, Vector3f);
+ }
+ if(needEmitAccumulator)
+ {
+ Assert(numEmitAccumulators && rhs.numEmitAccumulators);
+ Assert(numEmitAccumulators == rhs.numEmitAccumulators);
+ for(int acc = 0; acc < numEmitAccumulators; acc++)
+ SHURIKEN_COPY_DATA(emitAccumulator[acc], float);
+ }
+
+#undef SHURIKEN_COPY_DATA
+}
+
+
+void ParticleSystemParticles::array_assign(const ParticleSystemParticles& rhs)
+{
+ position.assign(rhs.position.begin(), rhs.position.end());
+ velocity.assign(rhs.velocity.begin(), rhs.velocity.end());
+ animatedVelocity.assign(rhs.animatedVelocity.begin(), rhs.animatedVelocity.end());
+ rotation.assign(rhs.rotation.begin(), rhs.rotation.end());
+ if(usesRotationalSpeed)
+ rotationalSpeed.assign(rhs.rotationalSpeed.begin(), rhs.rotationalSpeed.end());
+ size.assign(rhs.size.begin(), rhs.size.end());
+ color.assign(rhs.color.begin(), rhs.color.end());
+ randomSeed.assign(rhs.randomSeed.begin(), rhs.randomSeed.end());
+ lifetime.assign(rhs.lifetime.begin(), rhs.lifetime.end());
+ startLifetime.assign(rhs.startLifetime.begin(), rhs.startLifetime.end());
+ if(usesAxisOfRotation)
+ axisOfRotation.assign(rhs.axisOfRotation.begin(), rhs.axisOfRotation.end());
+ for(int acc = 0; acc < numEmitAccumulators; acc++)
+ emitAccumulator[acc].assign(rhs.emitAccumulator[acc].begin(), rhs.emitAccumulator[acc].end());
+}
+
+void ParticleSystemParticles::array_lerp(ParticleSystemParticles& output, const ParticleSystemParticles& a, const ParticleSystemParticles& b, float factor)
+{
+ DebugAssert(a.array_size() == b.array_size()); // else it doesn't really make sense
+ DebugAssert(a.usesRotationalSpeed == b.usesRotationalSpeed);
+
+ const int count = a.array_size();
+ output.array_resize(count);
+
+ // Note: Not all data is interpolated here intentionally, because it doesn't change anything or because it's incorrect
+
+ #define SHURIKEN_LERP_DATA(element, type) for(int i = 0; i < count; i++) output.element[i] = Lerp(a.element[i], b.element[i], factor)
+ SHURIKEN_LERP_DATA(position, Vector3f);
+ SHURIKEN_LERP_DATA(velocity, Vector3f);
+ SHURIKEN_LERP_DATA(animatedVelocity, Vector3f);
+
+ SHURIKEN_LERP_DATA(rotation, float);
+ if(a.usesRotationalSpeed)
+ SHURIKEN_LERP_DATA(rotationalSpeed, float);
+ SHURIKEN_LERP_DATA(lifetime, float);
+
+ #undef SHURIKEN_LERP_DATA
+}
+
+ParticleSystemParticlesTempData::ParticleSystemParticlesTempData()
+:color(0)
+,size(0)
+,sheetIndex(0)
+,particleCount(0)
+{}
+
+void ParticleSystemParticlesTempData::element_swap(size_t i, size_t j)
+{
+ DebugAssert(i <= particleCount);
+ DebugAssert(j <= particleCount);
+
+ std::swap(color[i], color[j]);
+ std::swap(size[i], size[j]);
+ if(sheetIndex)
+ std::swap(sheetIndex[i], sheetIndex[j]);
+}
+
diff --git a/Runtime/Graphics/ParticleSystem/ParticleSystemParticle.h b/Runtime/Graphics/ParticleSystem/ParticleSystemParticle.h
new file mode 100644
index 0000000..65d6894
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/ParticleSystemParticle.h
@@ -0,0 +1,116 @@
+#ifndef SHURIKENPARTICLE_H
+#define SHURIKENPARTICLE_H
+
+#include "Runtime/Graphics/ParticleSystem/ParticleCollisionEvents.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+class Collider;
+// Keep in sync with struct ParticleSystem.Particle
+enum{ kParticleSystemMaxNumEmitAccumulators = 2 };
+
+// TODO: Optimization:
+// . Store startLifetime as 1.0f/startLifeTime and store lifetime as 1.0f - lifetime.
+// This means that a lot of fdivs turn into muls and mads turns into nothing (NormalizedTime will become cheaper).
+// Remember: script must still convert into legacy format.
+
+// Keep in sync with struct ParticleSystem.Particle
+struct ParticleSystemParticle
+{
+ Vector3f position;
+ Vector3f velocity;
+ Vector3f animatedVelocity;
+ Vector3f axisOfRotation;
+ float rotation;
+ float rotationalSpeed;
+ float size;
+ ColorRGBA32 color;
+ UInt32 randomSeed;
+ float lifetime;
+ float startLifetime;
+ float emitAccumulator[kParticleSystemMaxNumEmitAccumulators];
+};
+
+typedef dynamic_array<Vector3f> ParticleSystemVector3Array;
+typedef dynamic_array<float> ParticleSystemFloatArray;
+typedef dynamic_array<ColorRGBA32> ParticleSystemColor32Array;
+typedef dynamic_array<UInt32> ParticleSystemUInt32Array;
+
+// Keep in sync with struct ParticleSystem.Particle
+struct ParticleSystemParticles
+{
+ ParticleSystemParticles()
+ :numEmitAccumulators(0)
+ ,usesAxisOfRotation(false)
+ ,usesRotationalSpeed(false)
+ ,usesCollisionEvents(false)
+ ,currentCollisionEventThreadArray(0)
+ {}
+
+ ParticleSystemVector3Array position;
+ ParticleSystemVector3Array velocity;
+ ParticleSystemVector3Array animatedVelocity; // Would actually only need this when modules with force and velocity curves are used
+ ParticleSystemVector3Array axisOfRotation;
+ ParticleSystemFloatArray rotation;
+ ParticleSystemFloatArray rotationalSpeed;
+ ParticleSystemFloatArray size;
+ ParticleSystemColor32Array color;
+ ParticleSystemUInt32Array randomSeed;
+ ParticleSystemFloatArray lifetime;
+ ParticleSystemFloatArray startLifetime;
+ ParticleSystemFloatArray emitAccumulator[kParticleSystemMaxNumEmitAccumulators]; // Usage: Only needed if particle system has time sub emitter
+ CollisionEvents collisionEvents;
+
+ bool usesAxisOfRotation;
+ bool usesRotationalSpeed;
+ bool usesCollisionEvents;
+ int currentCollisionEventThreadArray;
+ int numEmitAccumulators;
+
+ void AddParticle(ParticleSystemParticle* particle);
+
+ void SetUsesAxisOfRotation ();
+ void SetUsesRotationalSpeed();
+
+ void SetUsesCollisionEvents(bool usesCollisionEvents);
+ bool GetUsesCollisionEvents() const;
+ void SetUsesEmitAccumulator (int numAccumulators);
+
+ static size_t GetParticleSize();
+
+ size_t array_size () const;
+ void array_resize (size_t i);
+ void element_swap(size_t i, size_t j);
+ void element_assign(size_t i, size_t j);
+ void array_assign_external(void* data, const int numParticles);
+ void array_merge_preallocated(const ParticleSystemParticles& rhs, const int offset, const bool needAxisOfRotation, const bool needEmitAccumulator);
+ void array_assign(const ParticleSystemParticles& rhs);
+ static void array_lerp(ParticleSystemParticles& output, const ParticleSystemParticles& a, const ParticleSystemParticles& b, float factor);
+
+ void CopyFromArrayAOS(ParticleSystemParticle* particles, int size);
+ void CopyToArrayAOS(ParticleSystemParticle* particles, int size);
+};
+
+struct ParticleSystemParticlesTempData
+{
+ ParticleSystemParticlesTempData();
+ void element_swap(size_t i, size_t j);
+
+ ColorRGBA32* color;
+ float* size;
+ float* sheetIndex;
+ size_t particleCount;
+};
+
+inline float NormalizedTime (const ParticleSystemParticles& ps, size_t i)
+{
+ return (ps.startLifetime[i] - ps.lifetime[i]) / ps.startLifetime[i];
+}
+
+inline float NormalizedTime (float wholeTime, float currentTime)
+{
+ return (wholeTime - currentTime) / wholeTime;
+}
+
+#endif // SHURIKENPARTICLE_H
diff --git a/Runtime/Graphics/ParticleSystem/ParticleSystemRenderer.cpp b/Runtime/Graphics/ParticleSystem/ParticleSystemRenderer.cpp
new file mode 100644
index 0000000..7b14e62
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/ParticleSystemRenderer.cpp
@@ -0,0 +1,1241 @@
+#include "UnityPrefix.h"
+#include "ParticleSystem.h"
+#include "ParticleSystemParticle.h"
+#include "ParticleSystemRenderer.h"
+#include "ParticleSystemUtils.h"
+#include "Modules/SubModule.h"
+#include "Modules/UVModule.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Camera/Renderqueue.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Graphics/DrawUtil.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+#include "Runtime/Threads/JobScheduler.h"
+#include "Runtime/Filters/Misc/LineBuilder.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "Runtime/Graphics/TriStripper.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/BaseClasses/GameObject.h"
+
+IMPLEMENT_CLASS_INIT_ONLY (ParticleSystemRenderer)
+IMPLEMENT_OBJECT_SERIALIZE (ParticleSystemRenderer)
+
+PROFILER_INFORMATION(gParticlesSort, "ParticleSystem.Sort", kProfilerParticles)
+PROFILER_INFORMATION(gParticlesSingleProfile, "ParticleSystem.RenderSingle", kProfilerParticles)
+PROFILER_INFORMATION(gParticlesBatchProfile, "ParticleSystem.RenderBatch", kProfilerParticles)
+PROFILER_INFORMATION(gSubmitVBOParticleProfile, "Mesh.SubmitVBO", kProfilerRender)
+
+#define DEBUG_PARTICLE_SORTING (0)
+#if UNITY_WII
+#define kMaxNumParticlesPerBatch (65536/6)
+#else
+#define kMaxNumParticlesPerBatch (min<int>(kDynamicBatchingIndicesThreshold/6, VBO::kMaxQuads))
+#endif
+
+struct ParticleSystemVertex
+{
+ Vector3f vert;
+ Vector3f normal;
+ ColorRGBA32 color;
+ Vector2f uv;
+ Vector4f tangent; // Here, we put 2nd uv + blend factor
+};
+
+struct ParticleSystemGeomConstInputData
+{
+ Matrix4x4f m_ViewMatrix;
+ Vector3f m_CameraVelocity;
+ Object* m_Renderer;
+ UInt16 const* m_MeshIndexBuffer[ParticleSystemRendererData::kMaxNumParticleMeshes];
+ int m_MeshIndexCount[ParticleSystemRendererData::kMaxNumParticleMeshes];
+ int m_NumTilesX;
+ int m_NumTilesY;
+ float maxPlaneScale;
+ float maxOrthoSize;
+ float numUVFrame;
+ float animUScale;
+ float animVScale;
+ Vector3f xSpan;
+ Vector3f ySpan;
+ bool usesSheetIndex;
+ float bentNormalFactor;
+ Vector3f bentNormalVector;
+};
+
+inline void ScaleMatrix(Matrix4x4f& matrix, float scale)
+{
+ matrix.m_Data[0] *= scale;
+ matrix.m_Data[1] *= scale;
+ matrix.m_Data[2] *= scale;
+ matrix.m_Data[4] *= scale;
+ matrix.m_Data[5] *= scale;
+ matrix.m_Data[6] *= scale;
+ matrix.m_Data[8] *= scale;
+ matrix.m_Data[9] *= scale;
+ matrix.m_Data[10] *= scale;
+}
+
+struct ParticleSort
+{
+ inline static void SetValues(ParticleSort& sort, UInt32 inIndex, int inIntValue)
+ {
+ sort.index = inIndex;
+ sort.intValue = inIntValue;
+ }
+
+ inline static bool CompareValue (const ParticleSort& left, const ParticleSort& right)
+ {
+ return (left.intValue < right.intValue);
+ }
+
+ inline static void Swap(ParticleSort* oneOfThem, ParticleSort* theOtherOne)
+ {
+ ParticleSort temp = *oneOfThem;
+ *oneOfThem = *theOtherOne;
+ *theOtherOne = temp;
+ }
+
+ UInt32 index;
+ int intValue;
+};
+
+void GenerateSortIndices(ParticleSort* indices, const Vector3f& distFactor, const ParticleSystemParticles& ps, ParticleSystemSortMode sortMode)
+{
+ const size_t particleCount = ps.array_size();
+ if(IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ {
+ if(sortMode == kSSMByDistance)
+ for(int i = 0; i < particleCount; i++)
+ ParticleSort::SetValues(indices[i], i, (int)(Dot (distFactor, ps.position[i]) * 40000.0f));
+ else if(sortMode == kSSMOldestFirst)
+ for(int i = 0; i < particleCount; i++)
+ ParticleSort::SetValues(indices[i], i, (int)((ps.startLifetime[i]- ps.lifetime[i]) * -40000.0f));
+ else if(sortMode == kSSMYoungestFirst)
+ for(int i = 0; i < particleCount; i++)
+ ParticleSort::SetValues(indices[i], i, (int)((ps.startLifetime[i]- ps.lifetime[i]) * 40000.0f));
+ }
+ else
+ {
+ // 3.5 used lifetime - this is pretty broken if you have random lifetimes, as you get random sorting
+ if(sortMode == kSSMByDistance)
+ for(int i = 0; i < particleCount; i++)
+ ParticleSort::SetValues(indices[i], i, (int)(Dot (distFactor, ps.position[i]) * 40000.0f));
+ else if(sortMode == kSSMOldestFirst)
+ for(int i = 0; i < particleCount; i++)
+ ParticleSort::SetValues(indices[i], i, (int)(ps.lifetime[i] * 40000.0f));
+ else if(sortMode == kSSMYoungestFirst)
+ for(int i = 0; i < particleCount; i++)
+ ParticleSort::SetValues(indices[i], i, (int)(ps.lifetime[i] * -40000.0f));
+ }
+}
+
+template<bool sortTempData>
+void ApplySortRemap(ParticleSort* particleSortIndexBuffer, ParticleSystemParticlesTempData* tempData, ParticleSystemParticles& ps)
+{
+ const size_t count = ps.array_size();
+ for(int i = 0; i < count; i++)
+ {
+ int dst = particleSortIndexBuffer[i].intValue;
+ while(i != dst)
+ {
+ ParticleSort::Swap(&particleSortIndexBuffer[i], &particleSortIndexBuffer[dst]);
+ ps.element_swap(i, dst);
+ if(sortTempData)
+ tempData->element_swap(i, dst);
+
+ dst = particleSortIndexBuffer[i].intValue;
+ }
+ }
+}
+
+void Sort (const Matrix4x4f& matrix, ParticleSystemParticles& ps, ParticleSystemSortMode mode, ParticleSystemParticlesTempData* tempData, bool sortTempData)
+{
+ PROFILER_AUTO_GFX(gParticlesSort, 0);
+
+ DebugAssert(mode != kSSMNone);
+
+ const Vector3f distFactor = Vector3f (matrix.Get (2, 0), matrix.Get (2, 1), + matrix.Get (2, 2));
+ const size_t count = ps.array_size();
+
+ ParticleSort* particleSortIndexBuffer;
+ ALLOC_TEMP(particleSortIndexBuffer, ParticleSort, count);
+ GenerateSortIndices(&particleSortIndexBuffer[0], distFactor, ps, mode);
+
+ // Sort
+ std::sort(&particleSortIndexBuffer[0], &particleSortIndexBuffer[0] + count, ParticleSort::CompareValue);
+
+ // Create inverse mapping
+ for(int i = 0; i < count; i++)
+ particleSortIndexBuffer[particleSortIndexBuffer[i].index].intValue = i;
+
+ if(sortTempData)
+ ApplySortRemap<true>(particleSortIndexBuffer, tempData, ps);
+ else
+ ApplySortRemap<false>(particleSortIndexBuffer, tempData, ps);
+}
+
+struct ParticleMeshData
+{
+ int vertexCount;
+ StrideIterator<Vector3f> positions;
+ StrideIterator<Vector3f> normals;
+ StrideIterator<Vector4f> tangents;
+ StrideIterator<ColorRGBA32> colors;
+ StrideIterator<Vector2f> texCoords;
+ int indexCount;
+ const UInt16* indexBuffer;
+};
+
+template<bool hasNormals, bool hasTangents>
+void TransformParticleMesh(const ParticleMeshData& src, ColorRGBA32 particleColor,
+ const Matrix4x4f& xform, const Matrix4x4f& xformNoScale, UInt8** dest)
+{
+ for(int vertex = 0; vertex < src.vertexCount; vertex++)
+ {
+ // Vertex format is position, color, uv, and optional normals and tangents
+ xform.MultiplyPoint3(src.positions[vertex], *reinterpret_cast<Vector3f*>(*dest));
+ *dest += sizeof(Vector3f);
+ if (hasNormals)
+ {
+ xformNoScale.MultiplyVector3(src.normals[vertex], *reinterpret_cast<Vector3f*>(*dest));
+ *dest += sizeof(Vector3f);
+ }
+ *reinterpret_cast<ColorRGBA32*>(*dest) = particleColor * src.colors[vertex];
+ *dest += sizeof(ColorRGBA32);
+ *reinterpret_cast<Vector2f*>(*dest) = src.texCoords[vertex];
+ *dest += sizeof(Vector2f);
+ // Tangent is last in vertex format
+ if (hasTangents)
+ {
+ Vector3f newTangent = xformNoScale.MultiplyVector3((const Vector3f&)src.tangents[vertex]);
+ *reinterpret_cast<Vector4f*>(*dest) = Vector4f(newTangent, src.tangents[vertex].w);
+ *dest += sizeof(Vector4f);
+ }
+ }
+}
+
+
+ParticleSystemRenderer::ParticleSystemRenderer (MemLabelId label, ObjectCreationMode mode)
+: Super(kRendererParticleSystem, label, mode)
+, m_LocalSpaceAABB (Vector3f::zero, Vector3f::zero)
+{
+ SetVisible (false);
+
+ for (int i = 0; i < ParticleSystemRendererData::kMaxNumParticleMeshes; ++i)
+ m_Data.cachedMeshUserNode[i].SetData (this);
+
+#if UNITY_EDITOR
+ m_EditorEnabled = true;
+#endif
+}
+
+ParticleSystemRenderer::~ParticleSystemRenderer ()
+{
+}
+
+void ParticleSystemRenderer::InitializeClass ()
+{
+ REGISTER_MESSAGE_PTR (ParticleSystemRenderer, kDidDeleteMesh, OnDidDeleteMesh, Mesh);
+}
+
+void ParticleSystemRenderer::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ UpdateCachedMesh ();
+}
+
+void ParticleSystemRenderer::UpdateCachedMesh ()
+{
+ int dst = 0;
+ for(int src = 0; src < ParticleSystemRendererData::kMaxNumParticleMeshes; src++)
+ {
+ m_Data.cachedMesh[src] = NULL;
+ m_Data.cachedMeshUserNode[src].RemoveFromList ();
+
+ Mesh* mesh = m_Mesh[src];
+ if (mesh)
+ {
+ if (mesh->GetSubMeshCount() == 1)
+ {
+ m_Data.cachedMesh[dst] = mesh;
+ const SubMesh& sm = mesh->GetSubMeshFast(0);
+ const UInt16* buffer = mesh->GetSubMeshBuffer16(0);
+
+ if (sm.topology == kPrimitiveTriangleStripDeprecated)
+ {
+ const int capacity = CountTrianglesInStrip(buffer, sm.indexCount) * 3;
+ m_CachedIndexBuffer[dst].resize_uninitialized(capacity);
+ Destripify(buffer, sm.indexCount, m_CachedIndexBuffer[dst].begin(), capacity);
+ }
+ else if (sm.topology == kPrimitiveTriangles)
+ {
+ const int capacity = sm.indexCount;
+ m_CachedIndexBuffer[dst].resize_uninitialized(capacity);
+ memcpy(m_CachedIndexBuffer[dst].begin(), buffer, capacity*kVBOIndexSize);
+ }
+ else
+ {
+ m_CachedIndexBuffer[dst].resize_uninitialized(0);
+ }
+
+ // Hook into mesh's user notifications.
+ mesh->AddObjectUser (m_Data.cachedMeshUserNode[dst]);
+
+ dst++;
+ }
+ else
+ {
+ m_Data.cachedMesh[src] = NULL;
+ m_CachedIndexBuffer[src].resize_uninitialized(0);
+ AssertString ("Particle system meshes will only work with exactly one (1) sub mesh");
+ }
+ }
+ }
+}
+
+void ParticleSystemRenderer::OnDidDeleteMesh (Mesh* mesh)
+{
+ // Clear out cached pointer to mesh.
+ for (int i = 0; i < ParticleSystemRendererData::kMaxNumParticleMeshes; ++i)
+ {
+ if (m_Data.cachedMesh[i] != mesh)
+ continue;
+
+ m_Data.cachedMesh[i] = NULL;
+ m_Data.cachedMeshUserNode[i].RemoveFromList ();
+ }
+}
+
+void ParticleSystemRenderer::GetLocalAABB (AABB& result)
+{
+ result = m_LocalSpaceAABB;
+}
+
+void ParticleSystemRenderer::GetWorldAABB (AABB& result)
+{
+ TransformAABB (m_LocalSpaceAABB, GetTransform ().GetPosition (), GetTransform ().GetRotation (), result);
+}
+
+float ParticleSystemRenderer::GetSortingFudge () const
+{
+ return m_Data.sortingFudge;
+}
+
+void ParticleSystemRenderer::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+ m_Data.maxParticleSize = std::max (0.0F, m_Data.maxParticleSize);
+ m_Data.normalDirection = clamp<float>(m_Data.normalDirection, 0.0f, 1.0f);
+}
+
+void ParticleSystemRenderer::Reset ()
+{
+ Super::Reset ();
+ m_Data.renderMode = kSRMBillboard;
+ m_Data.lengthScale = 2.0F;
+ m_Data.velocityScale = 0.0F;
+ m_Data.cameraVelocityScale = 0.0F;
+ m_Data.maxParticleSize = 0.5F;
+ m_Data.sortingFudge = 0.0F;
+ m_Data.sortMode = kSSMNone;
+ m_Data.normalDirection = 1.0f;
+
+ for(int i = 0; i < ParticleSystemRendererData::kMaxNumParticleMeshes; i++)
+ m_Mesh[i] = NULL;
+ m_LocalSpaceAABB.SetCenterAndExtent (Vector3f::zero, Vector3f::zero);
+
+
+
+#if UNITY_EDITOR
+ m_EditorEnabled = true;
+#endif
+}
+
+void ParticleSystemRenderer::UpdateRenderer ()
+{
+ ParticleSystem* system = QueryComponent(ParticleSystem);
+ if (system)
+ {
+ SetVisible (true);
+ BoundsChanged();
+ }
+ else
+ {
+ UpdateManagerState (false);
+ }
+
+ Super::UpdateRenderer ();
+}
+
+void ParticleSystemRenderer::Update (const AABB& aabb)
+{
+ m_LocalSpaceAABB = aabb;
+ UpdateManagerState (true);
+}
+
+void ParticleSystemRenderer::RendererBecameVisible()
+{
+ Super::RendererBecameVisible();
+
+ ParticleSystem* system = QueryComponent(ParticleSystem);
+ if(system)
+ system->RendererBecameVisible();
+}
+
+void ParticleSystemRenderer::RendererBecameInvisible()
+{
+ Super::RendererBecameInvisible();
+
+ ParticleSystem* system = QueryComponent(ParticleSystem);
+ if(system)
+ system->RendererBecameInvisible();
+}
+
+void ParticleSystemRenderer::UpdateLocalAABB()
+{
+ AABB aabb;
+ GetLocalAABB(aabb);
+ m_TransformInfo.localAABB = aabb;
+}
+
+inline Rectf GetFrameUV (int index, int tilesX, float animUScale, float animVScale)
+{
+ int vIdx = index / tilesX;
+ int uIdx = index - vIdx * tilesX; // slightly faster than index % m_UVAnimation.xTile
+ float uOffset = (float)uIdx * animUScale;
+ float vOffset = 1.0f - animVScale - (float)vIdx * animVScale;
+
+ return Rectf(uOffset, vOffset, animUScale, animVScale);
+}
+
+template<ParticleSystemRenderMode renderMode>
+void GenerateParticleGeometry (ParticleSystemVertex* vbPtr,
+ const ParticleSystemGeomConstInputData& constData,
+ const ParticleSystemRendererData& rendererData,
+ const ParticleSystemParticles& ps,
+ const ParticleSystemParticlesTempData& psTemp,
+ size_t startIndex,
+ size_t endIndex,
+ const Matrix4x4f& worldViewMatrix,
+ const Matrix4x4f& viewToWorldMatrix)
+{
+ float maxPlaneScale = constData.maxPlaneScale;
+ float maxOrthoSize = constData.maxOrthoSize;
+ float numUVFrame = constData.numUVFrame;
+ Vector3f xSpan = constData.xSpan;
+ Vector3f ySpan = constData.ySpan;
+ Vector3f cameraVelocity = constData.m_CameraVelocity * rendererData.cameraVelocityScale;
+ int numTilesX = constData.m_NumTilesX;
+ float animUScale = constData.animUScale;
+ float animVScale = constData.animVScale;
+ bool usesSheetIndex = constData.usesSheetIndex;
+ float lengthScale = rendererData.lengthScale;
+ float velocityScale = rendererData.velocityScale;
+
+ float bentNormalFactor = constData.bentNormalFactor;
+ Vector3f bentNormalVector = constData.bentNormalVector;
+
+ Vector2f uv[4] = { Vector2f(0.0f, 1.0f),
+ Vector2f(1.0f, 1.0f),
+ Vector2f(1.0f, 0.0f),
+ Vector2f(0.0f, 0.0f)};
+ Vector4f uv2[4] = { Vector4f(0.0f, 1.0f, 0.0f, 0.0f),
+ Vector4f(1.0f, 1.0f, 0.0f, 0.0f),
+ Vector4f(1.0f, 0.0f, 0.0f, 0.0f),
+ Vector4f(0.0f, 0.0f, 0.0f, 0.0f)};
+
+ float invAnimVScale = 1.0f - animVScale;
+
+ for( int i = startIndex; i < endIndex; ++i )
+ {
+ Vector3f vert[4];
+ Vector3f n0, n1;
+
+ Vector3f position;
+ worldViewMatrix.MultiplyPoint3 (ps.position[i], position);
+
+ // Constrain the size to be a fraction of the viewport size.
+ // v[0].z * / farPlaneZ * farPlaneWorldSpaceLength * maxLength[0...1]
+ // Also all valid z's are negative so we just negate the whole equation
+ float maxWorldSpaceLength = position.z * maxPlaneScale + maxOrthoSize;
+ float hsize = std::min (psTemp.size[i], maxWorldSpaceLength) * 0.5f;
+ if (renderMode == kSRMBillboard)
+ {
+ float s = Sin (ps.rotation[i]);
+ float c = Cos (ps.rotation[i]);
+ n0 = Vector3f(-c+s, s+c, 0.0f);
+ n1 = Vector3f( c+s, -s+c, 0.0f);
+ vert[0] = position + n0 * hsize;
+ vert[1] = position + n1 * hsize;
+ vert[2] = position - n0 * hsize;
+ vert[3] = position - n1 * hsize;
+ }
+ else if (renderMode == kSRMBillboardFixedHorizontal || renderMode == kSRMBillboardFixedVertical)
+ {
+ float s = Sin (ps.rotation[i]+0.78539816339744830961566084581988f);
+ float c = Cos (ps.rotation[i]+0.78539816339744830961566084581988f);
+ n0 = xSpan*c + ySpan*s;
+ n1 = ySpan*c - xSpan*s;
+ vert[0] = position + n0 * hsize;
+ vert[1] = position + n1 * hsize;
+ vert[2] = position - n0 * hsize;
+ vert[3] = position - n1 * hsize;
+ }
+ else if (renderMode == kSRMStretch3D)
+ {
+ //RH BUG FOR LATER: Here we see the stretching bug as described by case no 434115...this is a Flash VM error, where a writeFloat (or readFloat) fails.
+ Vector3f velocity;
+ worldViewMatrix.MultiplyVector3(ps.velocity[i] + ps.animatedVelocity[i], velocity);
+ velocity -= cameraVelocity;
+ float sqrVelocity = SqrMagnitude (velocity);
+
+ Vector2f delta;
+ Vector3f endProj;
+ bool nonZeroVelocity = sqrVelocity > Vector3f::epsilon;
+ if (nonZeroVelocity)
+ {
+ endProj = position - velocity * (velocityScale + FastInvSqrt (sqrVelocity) * (lengthScale * psTemp.size[i]));
+ delta.x = position.z*endProj.y - position.y*endProj.z;
+ delta.y = position.x*endProj.z - position.z*endProj.x;
+ delta = NormalizeFast(delta);
+ }
+ else
+ {
+ endProj = position;
+ delta = Vector2f::xAxis;
+ }
+ n0 = n1 = Vector3f(delta.x, delta.y, 0.0f);
+ vert[0] = position + n0 * hsize;
+ vert[1] = endProj + n1 * hsize;
+ vert[2] = endProj - n0 * hsize;
+ vert[3] = position - n1 * hsize;
+ }
+
+ // UV animation
+ float sheetIndex;
+ if(usesSheetIndex)
+ {
+ // TODO: Pretty much the perfect candidate for SIMD
+
+ sheetIndex = psTemp.sheetIndex[i] * numUVFrame;
+ Assert (psTemp.sheetIndex[i] >= 0.0f && psTemp.sheetIndex[i] <= 1.0f);
+
+ const int index0 = FloorfToIntPos (sheetIndex);
+ const int index1 = index0 + 1;
+ Vector2f offset0, offset1;
+ const float blend = sheetIndex - (float)index0;
+
+ int vIdx = index0 / numTilesX;
+ int uIdx = index0 - vIdx * numTilesX;
+ offset0.x = (float)uIdx * animUScale;
+ offset0.y = invAnimVScale - (float)vIdx * animVScale;
+
+ vIdx = index1 / numTilesX;
+ uIdx = index1 - vIdx * numTilesX;
+ offset1.x = (float)uIdx * animUScale;
+ offset1.y = invAnimVScale - (float)vIdx * animVScale;
+
+ uv[0].Set(offset0.x, offset0.y + animVScale );
+ uv[1].Set(offset0.x + animUScale, offset0.y + animVScale );
+ uv[2].Set(offset0.x + animUScale, offset0.y );
+ uv[3].Set(offset0.x, offset0.y );
+
+ uv2[0].Set(offset1.x, offset1.y + animVScale, blend, 0.0f );
+ uv2[1].Set(offset1.x + animUScale, offset1.y + animVScale, blend, 0.0f );
+ uv2[2].Set(offset1.x + animUScale, offset1.y, blend, 0.0f );
+ uv2[3].Set(offset1.x, offset1.y, blend, 0.0f );
+ }
+
+ n0 = viewToWorldMatrix.MultiplyVector3(n0 * bentNormalFactor);
+ n1 = viewToWorldMatrix.MultiplyVector3(n1 * bentNormalFactor);
+
+ ColorRGBA32 color = psTemp.color[i];
+
+ vbPtr[0].vert = vert[0];
+ vbPtr[0].normal = bentNormalVector + n0;
+ vbPtr[0].color = color;
+ vbPtr[0].uv = uv[0];
+ vbPtr[0].tangent = uv2[0];
+
+ vbPtr[1].vert = vert[1];
+ vbPtr[1].normal = bentNormalVector + n1;
+ vbPtr[1].color = color;
+ vbPtr[1].uv = uv[1];
+ vbPtr[1].tangent = uv2[1];
+
+ vbPtr[2].vert = vert[2];
+ vbPtr[2].normal = bentNormalVector - n0;
+ vbPtr[2].color = color;
+ vbPtr[2].uv = uv[2];
+ vbPtr[2].tangent = uv2[2];
+
+ vbPtr[3].vert = vert[3];
+ vbPtr[3].normal = bentNormalVector - n1;
+ vbPtr[3].color = color;
+ vbPtr[3].uv = uv[3];
+ vbPtr[3].tangent = uv2[3];
+
+ // Next four vertices
+ vbPtr += 4;
+ }
+}
+
+static void DrawMeshParticles (const ParticleSystemGeomConstInputData& constInput, const ParticleSystemRendererData& rendererData, const Matrix4x4f& worldMatrix, const ParticleSystemParticles& ps, const ParticleSystemParticlesTempData& psTemp, const ChannelAssigns& channels)
+{
+ int numMeshes = 0;
+ ParticleMeshData particleMeshes[ParticleSystemRendererData::kMaxNumParticleMeshes];
+ Vector3f defaultNormal(0, 0, 0);
+ Vector4f defaultTangent(0, 0, 0, 0);
+ ColorRGBA32 defaultColor(255, 255, 255, 255);
+ Vector2f defaultTexCoords(0, 0);
+ for(int i = 0; i < ParticleSystemRendererData::kMaxNumParticleMeshes; i++)
+ {
+ if(constInput.m_MeshIndexCount[i] == 0)
+ break;
+ const Mesh* mesh = rendererData.cachedMesh[i];
+ if(mesh == NULL || !mesh->HasVertexData())
+ break;
+ ParticleMeshData& dest = particleMeshes[i];
+ dest.vertexCount = mesh->GetVertexCount();
+ dest.positions = mesh->GetVertexBegin();
+ dest.normals = mesh->GetNormalBegin();
+ if (dest.normals.IsNull())
+ dest.normals = StrideIterator<Vector3f>(&defaultNormal, 0);
+ dest.tangents = mesh->GetTangentBegin();
+ if (dest.tangents.IsNull())
+ dest.tangents = StrideIterator<Vector4f>(&defaultTangent, 0);
+ dest.texCoords = mesh->GetUvBegin();
+ if (dest.texCoords.IsNull())
+ dest.texCoords = StrideIterator<Vector2f>(&defaultTexCoords, 0);
+ dest.colors = mesh->GetColorBegin();
+ if (dest.colors.IsNull())
+ dest.colors = StrideIterator<ColorRGBA32>(&defaultColor, 0);
+ dest.indexCount = constInput.m_MeshIndexCount[i];
+ dest.indexBuffer = constInput.m_MeshIndexBuffer[i];
+ numMeshes++;
+ }
+
+ if(0 == numMeshes)
+ return;
+
+ GfxDevice& device = GetGfxDevice();
+
+ Matrix4x4f viewMatrix;
+ CopyMatrix (device.GetViewMatrix (), viewMatrix.GetPtr ());
+
+ const size_t particleCount = ps.array_size ();
+
+ float probability = 1.0f / (float)numMeshes;
+
+ // @TODO: We should move all these platform dependent numbers into Gfx specific code and get it from there.
+ const int kMaxVertices = 65536;
+
+#if UNITY_WII
+ const int kMaxIndices = 65536;
+#else
+ const int kMaxIndices = kDynamicBatchingIndicesThreshold;
+#endif
+
+ int particleOffset = 0;
+ while (particleOffset < particleCount)
+ {
+ int numVertices = 0;
+ int numIndices = 0;
+ int particleCountBatch = 0;
+
+ // Figure out batch size
+ for(int i = particleOffset; i < particleCount; i++)
+ {
+ const float randomValue = GenerateRandom(ps.randomSeed[i] + kParticleSystemMeshSelectionId);
+ int lastNumVertices = 0;
+ int lastNumIndices = 0;
+ for(int j = 0; j < numMeshes; j++)
+ {
+ const float lower = probability * j;
+ const float upper = probability * (j + 1);
+ if((randomValue >= lower) && (randomValue <= upper))
+ {
+ lastNumVertices = particleMeshes[j].vertexCount;
+ lastNumIndices = particleMeshes[j].indexCount;
+ break;
+ }
+ }
+ if((numVertices >= kMaxVertices) || (numIndices >= kMaxIndices))
+ {
+ break;
+ }
+ else
+ {
+ numVertices += lastNumVertices;
+ numIndices += lastNumIndices;
+ particleCountBatch++;
+ }
+ }
+
+ const int vertexCount = numVertices;
+ const int indexCount = numIndices;
+
+ // Figure out if normals and tangents are needed by shader
+ UInt32 normalTangentMask = channels.GetSourceMap() & VERTEX_FORMAT2(Normal, Tangent);
+
+ // Tangents requires normals
+ if( normalTangentMask & VERTEX_FORMAT1(Tangent) )
+ normalTangentMask |= VERTEX_FORMAT1(Normal);
+
+ // Get VBO chunk
+ DynamicVBO& vbo = device.GetDynamicVBO();
+ UInt8* vbPtr = NULL;
+ UInt16* ibPtr = NULL;
+ const UInt32 mandatoryChannels = VERTEX_FORMAT3(Vertex, Color, TexCoord0);
+ if( !vbo.GetChunk( mandatoryChannels | normalTangentMask,
+ vertexCount, indexCount,
+ DynamicVBO::kDrawIndexedTriangles,
+ (void**)&vbPtr, (void**)&ibPtr ) )
+ {
+ return;
+ }
+
+ int vertexOffset = 0;
+ int indexOffset = 0;
+ const int startIndex = particleOffset;
+ const int endIndex = particleOffset + particleCountBatch;
+ for( int i = startIndex; i < endIndex; ++i )
+ {
+ const Vector3f position = ps.position[i];
+ const float rotation = ps.rotation[i];
+ const float size = psTemp.size[i];
+ const Vector3f axisOfRotation = NormalizeSafe (ps.axisOfRotation[i], Vector3f::yAxis);
+ const ColorRGBA32 particleColor = psTemp.color[i];
+
+ // Only shared part is actually rotation. xformNoScale doesn't need a translation, so no need to copy that data
+ Matrix4x4f xformNoScale;
+ xformNoScale.SetTR (position, AxisAngleToQuaternion (axisOfRotation, rotation));
+
+ Matrix4x4f xform = xformNoScale;
+ ScaleMatrix(xform, size);
+
+ // Figure out which mesh to use
+ const float randomValue = GenerateRandom(ps.randomSeed[i] + kParticleSystemMeshSelectionId);
+ int meshIndex = 0;
+ for(int j = 0; j < numMeshes; j++)
+ {
+ const float lower = probability * j;
+ const float upper = probability * (j + 1);
+ if((randomValue >= lower) && (randomValue <= upper))
+ {
+ meshIndex = j;
+ break;
+ }
+ }
+
+ const ParticleMeshData& mesh = particleMeshes[meshIndex];
+
+ // Fill up vbo here
+ if( normalTangentMask == VERTEX_FORMAT2(Normal, Tangent) )
+ TransformParticleMesh<true, true>(mesh, particleColor, xform, xformNoScale, &vbPtr);
+ else if( normalTangentMask == VERTEX_FORMAT1(Normal) )
+ TransformParticleMesh<true, false>(mesh, particleColor, xform, xformNoScale, &vbPtr);
+ else if( normalTangentMask == 0 )
+ TransformParticleMesh<false, false>(mesh, particleColor, xform, xformNoScale, &vbPtr);
+ else
+ ErrorString("Invalid normalTangentMask");
+
+ const int meshIndexMax = mesh.indexCount - 2;
+ for(int index = 0; index < meshIndexMax; index+=3)
+ {
+ ibPtr[index+0] = mesh.indexBuffer[index+0] + vertexOffset;
+ ibPtr[index+1] = mesh.indexBuffer[index+1] + vertexOffset;
+ ibPtr[index+2] = mesh.indexBuffer[index+2] + vertexOffset;
+ }
+ ibPtr += mesh.indexCount;
+
+ vertexOffset += mesh.vertexCount;
+ indexOffset += mesh.indexCount;
+ }
+
+ vbo.ReleaseChunk (vertexCount, indexCount);
+ device.SetViewMatrix(viewMatrix.GetPtr());
+ device.SetWorldMatrix(worldMatrix.GetPtr());
+ vbo.DrawChunk (channels);
+ GPU_TIMESTAMP();
+
+ particleOffset += particleCountBatch;
+ }
+}
+
+static void DrawParticlesInternal(const ParticleSystemGeomConstInputData& constData, const ParticleSystemRendererData& rendererData, const Matrix4x4f& worldViewMatrix, const Matrix4x4f& viewToWorldMatrix, const ParticleSystemParticles& ps, const ParticleSystemParticlesTempData& psTemp, ParticleSystemVertex* vbPtr, const size_t particleOffset, const size_t numParticles, int renderMode)
+{
+ const size_t endIndex = particleOffset + numParticles;
+
+ if (renderMode == kSRMBillboard)
+ GenerateParticleGeometry<kSRMBillboard> (vbPtr, constData, rendererData, ps, psTemp, particleOffset, endIndex, worldViewMatrix, viewToWorldMatrix);
+ if (renderMode == kSRMStretch3D)
+ GenerateParticleGeometry<kSRMStretch3D> (vbPtr, constData, rendererData, ps, psTemp, particleOffset, endIndex, worldViewMatrix, viewToWorldMatrix);
+ if (renderMode == kSRMBillboardFixedHorizontal)
+ GenerateParticleGeometry<kSRMBillboardFixedHorizontal> (vbPtr, constData, rendererData, ps, psTemp, particleOffset, endIndex, worldViewMatrix, viewToWorldMatrix);
+ if (renderMode == kSRMBillboardFixedVertical)
+ GenerateParticleGeometry<kSRMBillboardFixedVertical> (vbPtr, constData, rendererData, ps, psTemp, particleOffset, endIndex, worldViewMatrix, viewToWorldMatrix);
+}
+
+static void DrawParticles(const ParticleSystemGeomConstInputData& constData, const ParticleSystemRendererData& rendererData, const Matrix4x4f& worldViewMatrix, const Matrix4x4f& viewToWorldMatrix, const ParticleSystemParticles& ps, const ParticleSystemParticlesTempData& psTemp, const ChannelAssigns& channels, ParticleSystemVertex* vbPtr)
+{
+ GfxDevice& device = GetGfxDevice();
+ const size_t particleCount = ps.array_size();
+
+ if(vbPtr)
+ {
+ DrawParticlesInternal(constData, rendererData, worldViewMatrix, viewToWorldMatrix, ps, psTemp, vbPtr, 0, particleCount, rendererData.renderMode);
+ }
+ else
+ {
+ int particleOffset = 0;
+ while (particleOffset < particleCount)
+ {
+ const int particleCountBatch = min(kMaxNumParticlesPerBatch, (int)particleCount - particleOffset);
+
+ // Get VBO chunk
+ DynamicVBO& vbo = device.GetDynamicVBO();
+ if( !vbo.GetChunk( (1<<kShaderChannelVertex) | (1<<kShaderChannelNormal) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelColor) | (1<<kShaderChannelTangent),
+ particleCountBatch * 4, 0,
+ DynamicVBO::kDrawQuads,
+ (void**)&vbPtr, NULL ) )
+ {
+ continue;
+ }
+
+ DrawParticlesInternal(constData, rendererData, worldViewMatrix, viewToWorldMatrix, ps, psTemp, vbPtr, particleOffset, particleCountBatch, rendererData.renderMode);
+ particleOffset += particleCountBatch;
+
+ vbo.ReleaseChunk (particleCountBatch * 4, 0);
+
+ // Draw
+ device.SetViewMatrix (Matrix4x4f::identity.GetPtr()); // implicitly sets world to identity
+
+ PROFILER_BEGIN(gSubmitVBOParticleProfile, constData.m_Renderer)
+ vbo.DrawChunk (channels);
+ GPU_TIMESTAMP();
+ PROFILER_END
+
+ device.SetViewMatrix(constData.m_ViewMatrix.GetPtr ());
+ }
+ }
+}
+
+void ParticleSystemRenderer::CalculateTotalParticleCount(UInt32& totalNumberOfParticles, ParticleSystem& system, bool first)
+{
+ ParticleSystemRenderer* renderer = system.QueryComponent(ParticleSystemRenderer);
+ if(!renderer || first)
+ {
+ totalNumberOfParticles += system.GetParticleCount();
+
+ Transform* t = system.QueryComponent (Transform);
+ if (t == NULL)
+ return;
+ for (Transform::iterator i=t->begin ();i != t->end ();i++)
+ {
+ ParticleSystem* child = (**i).QueryComponent(ParticleSystem);
+ if (child)
+ CalculateTotalParticleCount(totalNumberOfParticles, *child, false);
+ }
+ }
+}
+
+void ParticleSystemRenderer::CombineParticleBuffersRec(int& offset, ParticleSystemParticles& ps, ParticleSystemParticlesTempData& psTemp, ParticleSystem& system, bool first, bool needsAxisOfRotation)
+{
+ ParticleSystemRenderer* renderer = system.QueryComponent(ParticleSystemRenderer);
+ if(!renderer || first)
+ {
+ int particleCount = system.GetParticleCount();
+ ps.array_merge_preallocated(system.GetParticles(), offset, needsAxisOfRotation, false);
+
+ if(system.m_ReadOnlyState->useLocalSpace)
+ {
+ Matrix4x4f localToWorld = system.GetComponent (Transform).GetLocalToWorldMatrixNoScale ();
+
+ int endIndex = offset + particleCount;
+ for(int i = offset; i < endIndex; i++)
+ ps.position[i] = localToWorld.MultiplyPoint3(ps.position[i]);
+ for(int i = offset; i < endIndex; i++)
+ ps.velocity[i] = localToWorld.MultiplyVector3(ps.velocity[i]);
+ for(int i = offset; i < endIndex; i++)
+ ps.animatedVelocity[i] = localToWorld.MultiplyVector3(ps.animatedVelocity[i]);
+ if(ps.usesAxisOfRotation)
+ for(int i = offset; i < endIndex; i++)
+ ps.axisOfRotation[i] = localToWorld.MultiplyVector3(ps.axisOfRotation[i]);
+ }
+
+ ParticleSystem::UpdateModulesNonIncremental(system, ps, psTemp, offset, offset + particleCount);
+ offset += particleCount;
+
+ Transform* t = system.QueryComponent (Transform);
+ if (t == NULL)
+ return;
+ for (Transform::iterator i=t->begin ();i != t->end ();i++)
+ {
+ ParticleSystem* child = (**i).QueryComponent(ParticleSystem);
+ if (child)
+ CombineParticleBuffersRec(offset, ps, psTemp, *child, false, needsAxisOfRotation);
+ }
+ }
+}
+
+void ParticleSystemRenderer::SetUsesAxisOfRotationRec(ParticleSystem& shuriken, bool first)
+{
+ ParticleSystemRenderer* renderer = shuriken.QueryComponent(ParticleSystemRenderer);
+ if(!renderer || first)
+ {
+ shuriken.SetUsesAxisOfRotation();
+
+ Transform* t = shuriken.QueryComponent (Transform);
+ if (t == NULL)
+ return;
+ for (Transform::iterator i=t->begin ();i != t->end ();i++)
+ {
+ ParticleSystem* shuriken = (**i).QueryComponent(ParticleSystem);
+ if (shuriken)
+ SetUsesAxisOfRotationRec(*shuriken, false);
+ }
+ }
+}
+
+void ParticleSystemRenderer::CombineBoundsRec(ParticleSystem& shuriken, MinMaxAABB& aabb, bool first)
+{
+ ParticleSystemRenderer* renderer = shuriken.QueryComponent(ParticleSystemRenderer);
+ if(!renderer || first)
+ {
+ AABB result = shuriken.m_State->minMaxAABB;
+ if(!shuriken.m_ReadOnlyState->useLocalSpace)
+ InverseTransformAABB (result, renderer->GetTransform().GetPosition (), renderer->GetTransform().GetRotation (), result);
+
+ if(first)
+ aabb = result;
+ else
+ aabb.Encapsulate(result);
+
+ Transform* t = shuriken.QueryComponent (Transform);
+ if (t == NULL)
+ return;
+ for (Transform::iterator i=t->begin ();i != t->end ();i++)
+ {
+ ParticleSystem* shuriken = (**i).QueryComponent(ParticleSystem);
+ if (shuriken)
+ CombineBoundsRec(*shuriken, aabb, false);
+ }
+ }
+}
+
+void ParticleSystemRenderer::Render (int/* materialIndex*/, const ChannelAssigns& channels)
+{
+ ParticleSystem::SyncJobs();
+
+ ParticleSystem* system = QueryComponent(ParticleSystem);
+ if(!system)
+ return;
+
+ // Can't render without an active camera (case 568930)
+ // Can remove check when we finally kill Renderer.Render()
+ if (!GetCurrentCameraPtr())
+ return;
+
+ PROFILER_AUTO_GFX(gParticlesSingleProfile, this);
+
+ UInt32 numParticles = 0;
+ CalculateTotalParticleCount(numParticles, *system, true);
+ if(numParticles)
+ RenderInternal(*system, *this, channels, 0, numParticles);
+}
+
+void ParticleSystemRenderer::RenderMultiple (const BatchInstanceData* instances, size_t count, const ChannelAssigns& channels)
+{
+ ParticleSystem::SyncJobs();
+
+ size_t numParticlesBatch = 0;
+
+ BatchInstanceData const* instancesEnd = instances + count;
+ BatchInstanceData const* iBatchBegin = instances;
+ BatchInstanceData const* iBatchEnd = instances;
+ while(iBatchEnd != instancesEnd)
+ {
+ Assert(iBatchEnd->renderer->GetRendererType() == kRendererParticleSystem);
+ ParticleSystemRenderer* psRenderer = (ParticleSystemRenderer*)iBatchEnd->renderer;
+ Assert(psRenderer->GetRenderMode() != kSRMMesh);
+ ParticleSystem* system = psRenderer->QueryComponent(ParticleSystem);
+ if (!system )
+ {
+ iBatchEnd++;
+ continue;
+ }
+ UInt32 numParticles = 0;
+ psRenderer->CalculateTotalParticleCount(numParticles, *system, true);
+
+ if((numParticlesBatch + numParticles) <= kMaxNumParticlesPerBatch)
+ {
+ numParticlesBatch += numParticles;
+ iBatchEnd++;
+ }
+ else
+ {
+ if(numParticlesBatch)
+ {
+ RenderBatch(iBatchBegin, iBatchEnd - iBatchBegin, numParticlesBatch, channels);
+ numParticlesBatch = 0;
+ iBatchBegin = iBatchEnd;
+ }
+ else // Can't fit in one draw call
+ {
+ RenderBatch(iBatchEnd, 1, numParticles, channels);
+ iBatchEnd++;
+ iBatchBegin = iBatchEnd;
+ }
+ }
+ }
+
+ if((iBatchBegin != iBatchEnd) && numParticlesBatch)
+ RenderBatch(iBatchBegin, iBatchEnd - iBatchBegin, numParticlesBatch, channels);
+}
+
+void ParticleSystemRenderer::RenderBatch (const BatchInstanceData* instances, size_t count, size_t numParticles, const ChannelAssigns& channels)
+{
+ DebugAssert(numParticles);
+
+ GfxDevice& device = GetGfxDevice();
+
+ const MaterialPropertyBlock* customProps = count > 0 ? instances[0].renderer->GetCustomProperties() : NULL;
+ if (customProps)
+ device.SetMaterialProperties (*customProps);
+
+ Matrix4x4f viewMatrix;
+ CopyMatrix (device.GetViewMatrix (), viewMatrix.GetPtr ());
+
+ ParticleSystemVertex* vbPtr = 0;
+ DynamicVBO& vbo = device.GetDynamicVBO();
+ if(numParticles <= kMaxNumParticlesPerBatch)
+ {
+ if( !vbo.GetChunk( (1<<kShaderChannelVertex) | (1<<kShaderChannelNormal) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelColor) | (1<<kShaderChannelTangent),
+ numParticles * 4, 0,
+ DynamicVBO::kDrawQuads,
+ (void**)&vbPtr, NULL ) )
+ {
+ return;
+ }
+ }
+
+ PROFILER_AUTO_GFX(gParticlesBatchProfile, 0);
+
+ // Allocate VBO if count is not above threshold. Else just pass null down
+ BatchInstanceData const* iBatchBegin = instances;
+ BatchInstanceData const* instancesEnd = instances + count;
+ size_t particleOffset = 0;
+ while (iBatchBegin != instancesEnd)
+ {
+ Assert(iBatchBegin->renderer->GetRendererType() == kRendererParticleSystem);
+ ParticleSystemRenderer* psRenderer = (ParticleSystemRenderer*)iBatchBegin->renderer;
+ Assert(psRenderer->GetRenderMode() != kSRMMesh);
+ ParticleSystem* system = psRenderer->QueryComponent(ParticleSystem);
+ UInt32 particleCountTotal = 0;
+ if (system)
+ {
+ // It would be nice to filter out NULL particle systems earlier, but we don't (case 504744)
+ CalculateTotalParticleCount(particleCountTotal, *system, true);
+ if(particleCountTotal)
+ RenderInternal(*system, *psRenderer, channels, vbPtr + particleOffset * 4, particleCountTotal);
+ }
+ iBatchBegin++;
+ particleOffset += particleCountTotal;
+ }
+
+ if(vbPtr)
+ {
+ vbo.ReleaseChunk (numParticles * 4, 0);
+
+ // Draw
+ device.SetViewMatrix (Matrix4x4f::identity.GetPtr()); // implicitly sets world to identity
+
+ PROFILER_BEGIN(gSubmitVBOParticleProfile, 0)
+ vbo.DrawChunk (channels);
+ GPU_TIMESTAMP();
+ PROFILER_END
+
+ if (count > 1)
+ device.AddBatchingStats(numParticles * 2, numParticles * 4, count);
+
+ device.SetViewMatrix(viewMatrix.GetPtr());
+ }
+}
+
+void ParticleSystemRenderer::RenderInternal (ParticleSystem& system, const ParticleSystemRenderer& renderer, const ChannelAssigns& channels, ParticleSystemVertex* vbPtr, UInt32 particleCountTotal)
+{
+ Assert(particleCountTotal);
+
+#if UNITY_EDITOR
+ if (!renderer.m_EditorEnabled)
+ return;
+#endif
+
+ GfxDevice& device = GetGfxDevice();
+
+ // Render matrix
+ Matrix4x4f viewMatrix;
+ CopyMatrix (device.GetViewMatrix (), viewMatrix.GetPtr ());
+
+ ParticleSystemParticles* ps = &system.GetParticles ();
+ size_t particleCount = ps->array_size ();
+ AssertBreak(particleCountTotal >= particleCount);
+
+ UInt8* combineBuffer = 0;
+ ParticleSystemParticles combineParticles;
+ if(particleCountTotal > particleCount)
+ {
+ particleCount = particleCountTotal;
+ combineBuffer = ALLOC_TEMP_MANUAL(UInt8, particleCountTotal * ParticleSystemParticles::GetParticleSize());
+ combineParticles.array_assign_external((void*)&combineBuffer[0], particleCountTotal);
+ ps = &combineParticles;
+ }
+
+ if(!particleCount)
+ return;
+
+ const bool needsAxisOfRotation = !renderer.GetScreenSpaceRotation();
+ if(needsAxisOfRotation)
+ {
+ if ( IS_CONTENT_NEWER_OR_SAME (GetNumericVersion ("4.0.0f7")) && !IS_CONTENT_NEWER_OR_SAME (GetNumericVersion ("4.1.1a1")) )
+ {
+ // this was introduced in 4.0 and is wrong, it will effectively force the rotation axis to be the y-axis for all particles, the function is meant only
+ // to initialize the axis array (once) and should only be called from SetUsesAxisOfRotationRec
+ ps->SetUsesAxisOfRotation ();
+ }
+ else
+ {
+ // this is the intended functionality, but was broken for 4.0 and 4.0.1 see above
+ SetUsesAxisOfRotationRec (system, true);
+ }
+ }
+
+ ParticleSystemSortMode sortMode = (ParticleSystemSortMode)renderer.m_Data.sortMode;
+ bool isInLocalSpace = system.m_ReadOnlyState->useLocalSpace && !combineBuffer;
+ Matrix4x4f worldMatrix = Matrix4x4f::identity;
+ if(isInLocalSpace)
+ worldMatrix = system.GetComponent (Transform).GetLocalToWorldMatrixNoScale ();
+
+ ParticleSystemParticlesTempData psTemp;
+ psTemp.color = ALLOC_TEMP_MANUAL(ColorRGBA32, particleCount);
+ psTemp.size = ALLOC_TEMP_MANUAL(float, particleCount);
+ psTemp.sheetIndex = 0;
+ psTemp.particleCount = particleCount;
+ if(combineBuffer)
+ {
+ int offset = 0;
+ CombineParticleBuffersRec(offset, *ps, psTemp, system, true, needsAxisOfRotation);
+ if (kSSMNone != sortMode)
+ Sort(viewMatrix, *ps, sortMode, &psTemp, true);
+ }
+ else
+ {
+ if(system.m_UVModule->GetEnabled())
+ psTemp.sheetIndex = ALLOC_TEMP_MANUAL(float, particleCount);
+
+ if (kSSMNone != sortMode)
+ {
+ Matrix4x4f objectToViewMatrix;
+ MultiplyMatrices3x4(viewMatrix, worldMatrix, objectToViewMatrix);
+ Sort(objectToViewMatrix, *ps, sortMode, 0, false);
+ }
+ ParticleSystem::UpdateModulesNonIncremental(system, *ps, psTemp, 0, particleCount);
+ }
+
+ // Constrain the size to be a fraction of the viewport size.
+ // In perspective case, max size is (z*factorA). In ortho case, max size is just factorB. To have both
+ // without branches, we do (z*factorA+factorB) and set one of factors to zero.
+ float maxPlaneScale = 0.0f;
+ float maxOrthoSize = 0.0f;
+ // Getting the camera isn't totally free, so do it once.
+ const Camera& camera = GetCurrentCamera();
+ if (!camera.GetOrthographic())
+ maxPlaneScale = -camera.CalculateFarPlaneWorldSpaceLength() * renderer.m_Data.maxParticleSize / camera.GetFar();
+ else
+ maxOrthoSize = camera.CalculateFarPlaneWorldSpaceLength() * renderer.m_Data.maxParticleSize;
+
+ int numMeshes = 0;
+ for(int i = 0; i < ParticleSystemRendererData::kMaxNumParticleMeshes; i++)
+ if(renderer.m_Data.cachedMesh[i])
+ numMeshes++;
+
+ ParticleSystemGeomConstInputData constData;
+ constData.m_ViewMatrix = viewMatrix;
+ constData.m_CameraVelocity = viewMatrix.MultiplyVector3(camera.GetVelocity ());
+ constData.m_Renderer = (Object*)&renderer;
+ for(int i = 0; i < numMeshes; i++)
+ {
+ constData.m_MeshIndexBuffer[i] = renderer.m_CachedIndexBuffer[i].begin();
+ constData.m_MeshIndexCount[i] = renderer.m_CachedIndexBuffer[i].size();
+ AssertBreak((constData.m_MeshIndexCount[i] % 3) == 0);
+ }
+
+ system.GetNumTiles(constData.m_NumTilesX, constData.m_NumTilesY);
+ constData.maxPlaneScale = maxPlaneScale;
+ constData.maxOrthoSize = maxOrthoSize;
+ constData.numUVFrame = constData.m_NumTilesX * constData.m_NumTilesY;
+ constData.animUScale = 1.0f / (float)constData.m_NumTilesX;
+ constData.animVScale = 1.0f / (float)constData.m_NumTilesY;
+ constData.xSpan = Vector3f(-1.0f,0.0f,0.0f);
+ constData.ySpan = Vector3f(0.0f,0.0f,1.0f);
+ if (renderer.m_Data.renderMode == kSRMBillboardFixedVertical)
+ {
+ constData.ySpan = Vector3f(0.0f,1.0f,0.0f);
+ const Vector3f zSpan = viewMatrix.MultiplyVector3 (Vector3f::zAxis);// (RotateVectorByQuat (cameraRotation, Vector3f(0.0f,0.0f,1.0f));
+ constData.xSpan = NormalizeSafe (Cross (constData.ySpan, zSpan));
+ }
+ constData.xSpan = viewMatrix.MultiplyVector3(constData.xSpan);
+ constData.ySpan = viewMatrix.MultiplyVector3(constData.ySpan);
+ constData.usesSheetIndex = psTemp.sheetIndex != NULL;
+
+ const float bentNormalAngle = renderer.m_Data.normalDirection * 90.0f * kDeg2Rad;
+ const float scale = (renderer.m_Data.renderMode == kSRMBillboard) ? 0.707106781f : 1.0f;
+
+ Matrix4x4f viewToWorldMatrix;
+ Matrix4x4f::Invert_General3D(viewMatrix, viewToWorldMatrix);
+
+ Matrix4x4f worldViewMatrix;
+ MultiplyMatrices4x4(&viewMatrix, &worldMatrix, &worldViewMatrix);
+
+ Vector3f billboardNormal = Vector3f::zAxis;
+ if((renderer.m_Data.renderMode == kSRMBillboardFixedHorizontal) || (renderer.m_Data.renderMode == kSRMBillboardFixedVertical))
+ billboardNormal = viewMatrix.MultiplyVector3 (NormalizeSafe (Cross (constData.xSpan, constData.ySpan)));
+ constData.bentNormalVector = viewToWorldMatrix.MultiplyVector3(Sin(bentNormalAngle) * billboardNormal);
+ constData.bentNormalFactor = Cos(bentNormalAngle) * scale;
+
+ if (renderer.m_Data.renderMode == kSRMMesh)
+ DrawMeshParticles (constData, renderer.m_Data, worldMatrix, *ps, psTemp, channels);
+ else
+ DrawParticles(constData, renderer.m_Data, worldViewMatrix, viewToWorldMatrix, *ps, psTemp, channels, vbPtr);
+
+ FREE_TEMP_MANUAL(psTemp.color);
+ FREE_TEMP_MANUAL(psTemp.size);
+ if(psTemp.sheetIndex)
+ FREE_TEMP_MANUAL(psTemp.sheetIndex);
+ if(combineBuffer)
+ FREE_TEMP_MANUAL(combineBuffer);
+}
+
+template<class TransferFunction> inline
+void ParticleSystemRenderer::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.Transfer (m_Data.renderMode, "m_RenderMode");
+ transfer.Transfer (m_Data.maxParticleSize, "m_MaxParticleSize");
+ transfer.Transfer (m_Data.cameraVelocityScale, "m_CameraVelocityScale");
+ transfer.Transfer (m_Data.velocityScale, "m_VelocityScale");
+ transfer.Transfer (m_Data.lengthScale, "m_LengthScale");
+ transfer.Transfer (m_Data.sortingFudge, "m_SortingFudge");
+ transfer.Transfer (m_Data.normalDirection, "m_NormalDirection");
+ transfer.Transfer (m_Data.sortMode, "m_SortMode");
+ transfer.Transfer (m_Mesh[0], "m_Mesh");
+ transfer.Transfer (m_Mesh[1], "m_Mesh1");
+ transfer.Transfer (m_Mesh[2], "m_Mesh2");
+ transfer.Transfer (m_Mesh[3], "m_Mesh3");
+}
diff --git a/Runtime/Graphics/ParticleSystem/ParticleSystemRenderer.h b/Runtime/Graphics/ParticleSystem/ParticleSystemRenderer.h
new file mode 100644
index 0000000..07bcb0d
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/ParticleSystemRenderer.h
@@ -0,0 +1,134 @@
+#ifndef SHURIKENRENDERER_H
+#define SHURIKENRENDERER_H
+
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Vector2.h"
+
+
+
+
+class Mesh;
+class MinMaxAABB;
+struct ParticleSystemVertex;
+
+struct ParticleSystemRendererData
+{
+ // Must match the one in RendererModuleUI.cs
+ enum { kMaxNumParticleMeshes = 4 };
+
+ int renderMode; ///< enum { Billboard = 0, Stretched = 1, Horizontal Billboard = 2, Vertical Billboard = 3, Mesh = 4 }
+ int sortMode; ///< enum { None = 0, By Distance = 1, Youngest First = 2, Oldest First = 3 }
+ float maxParticleSize; ///< How large is a particle allowed to be on screen at most? 1 is entire viewport. 0.5 is half viewport.
+ float cameraVelocityScale; ///< How much the camera motion is factored in when determining particle stretching.
+ float velocityScale; ///< When Stretch Particles is enabled, defines the length of the particle compared to its velocity.
+ float lengthScale; ///< When Stretch Particles is enabled, defines the length of the particle compared to its width.
+ float sortingFudge; ///< Lower the number, most likely that these particles will appear in front of other transparent objects, including other particles.
+ float normalDirection; ///< Value between 0.0 and 1.0. If 1.0 is used, normals will point towards camera. If 0.0 is used, normals will point out in the corner direction of the particle.
+ Mesh* cachedMesh[kMaxNumParticleMeshes];
+
+ /// Node hooked into the mesh user list of cached meshes so we get notified
+ /// when a mesh goes away.
+ ///
+ /// NOTE: Must be initialized properly after construction to point to the
+ /// ParticleSystemRenderer.
+ ListNode<Object> cachedMeshUserNode[kMaxNumParticleMeshes];
+};
+
+
+enum ParticleSystemRenderMode {
+ kSRMBillboard = 0,
+ kSRMStretch3D = 1,
+ kSRMBillboardFixedHorizontal = 2,
+ kSRMBillboardFixedVertical = 3,
+ kSRMMesh = 4,
+};
+
+enum ParticleSystemSortMode
+{
+ kSSMNone,
+ kSSMByDistance,
+ kSSMYoungestFirst,
+ kSSMOldestFirst,
+};
+
+struct ParticleSystemParticles;
+struct ParticleSystemParticlesTempData;
+class ParticleSystem;
+struct ParticleSystemGeomConstInputData;
+class ParticleSystemRenderer : public Renderer {
+public:
+ REGISTER_DERIVED_CLASS (ParticleSystemRenderer, Renderer)
+ DECLARE_OBJECT_SERIALIZE (ParticleSystemRenderer)
+ static void InitializeClass ();
+
+ ParticleSystemRenderer (MemLabelId label, ObjectCreationMode mode);
+ // ParticleSystemRenderer(); declared-by-macro
+
+ virtual void Render (int materialIndex, const ChannelAssigns& channels);
+ static void RenderMultiple (const BatchInstanceData* instances, size_t count, const ChannelAssigns& channels);
+
+ virtual void GetLocalAABB (AABB& result);
+ virtual void GetWorldAABB (AABB& result);
+ virtual float GetSortingFudge () const;
+
+ virtual void CheckConsistency ();
+ virtual void Reset ();
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ void Update (const AABB& aabb);
+ void UpdateLocalAABB();
+
+ virtual void RendererBecameVisible();
+ virtual void RendererBecameInvisible();
+
+ static void SetUsesAxisOfRotationRec(ParticleSystem& system, bool first);
+ static void CombineBoundsRec(ParticleSystem& shuriken, MinMaxAABB& aabb, bool first);
+
+ GET_SET_DIRTY (ParticleSystemRenderMode, RenderMode, m_Data.renderMode) ;
+ GET_SET_DIRTY (ParticleSystemSortMode, SortMode, m_Data.sortMode) ;
+ GET_SET_DIRTY (float, MaxParticleSize, m_Data.maxParticleSize) ;
+ GET_SET_DIRTY (float, CameraVelocityScale, m_Data.cameraVelocityScale) ;
+ GET_SET_DIRTY (float, VelocityScale, m_Data.velocityScale) ;
+ GET_SET_DIRTY (float, LengthScale, m_Data.lengthScale) ;
+ void SetMesh (PPtr<Mesh> mesh) { m_Mesh[0] = mesh; SetDirty(); UpdateCachedMesh (); }
+ PPtr<Mesh> GetMesh () const { return m_Mesh[0]; }
+
+ const PPtr<Mesh>* GetMeshes () const { return m_Mesh; }
+ const ParticleSystemRendererData& GetData() const { return m_Data; }
+
+#if UNITY_EDITOR
+ bool GetEditorEnabled() const { return m_EditorEnabled; }
+ void SetEditorEnabled(bool value) { m_EditorEnabled = value; }
+#endif
+
+ // For mesh we use world space rotation, else screen space
+ bool GetScreenSpaceRotation() const { return m_Data.renderMode != kSRMMesh; };
+private:
+ // from Renderer
+ virtual void UpdateRenderer ();
+ void UpdateCachedMesh ();
+ void OnDidDeleteMesh (Mesh* mesh);
+
+private:
+ static void CalculateTotalParticleCount(UInt32& totalNumberOfParticles, ParticleSystem& shuriken, bool first);
+ static void CombineParticleBuffersRec(int& offset, ParticleSystemParticles& particles, ParticleSystemParticlesTempData& psTemp, ParticleSystem& shuriken, bool first, bool needsAxisOfRotation);
+
+ static void RenderBatch (const BatchInstanceData* instances, size_t count, size_t numParticles, const ChannelAssigns& channels);
+ static void RenderInternal (ParticleSystem& system, const ParticleSystemRenderer& renderer, const ChannelAssigns& channels, ParticleSystemVertex* vbPtr, UInt32 particleCountTotal);
+
+ ParticleSystemRendererData m_Data;
+ dynamic_array<UInt16> m_CachedIndexBuffer[ParticleSystemRendererData::kMaxNumParticleMeshes];
+ PPtr<Mesh> m_Mesh[ParticleSystemRendererData::kMaxNumParticleMeshes];
+
+ AABB m_LocalSpaceAABB;
+
+#if UNITY_EDITOR
+ bool m_EditorEnabled;
+#endif
+};
+
+#endif // SHURIKENRENDERER_H
diff --git a/Runtime/Graphics/ParticleSystem/ParticleSystemRendererTests.cpp b/Runtime/Graphics/ParticleSystem/ParticleSystemRendererTests.cpp
new file mode 100644
index 0000000..47db7ee
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/ParticleSystemRendererTests.cpp
@@ -0,0 +1,29 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemRenderer.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Testing/TestFixtures.h"
+
+
+SUITE (ParticleSystemRendererTests)
+{
+ typedef ObjectTestFixture<ParticleSystemRenderer> Fixture;
+
+ TEST_FIXTURE (Fixture, DeletingMeshClearsOutCachedMeshPointers)
+ {
+ // Arrange.
+ PPtr<Mesh> mesh (NEW_OBJECT_RESET_AND_AWAKE (Mesh));
+ m_ObjectUnderTest->SetMesh (mesh);
+
+ // Act.
+ DestroySingleObject (mesh);
+
+ // Assert.
+ CHECK (m_ObjectUnderTest->GetData().cachedMesh[0] == NULL);
+ }
+}
+
+#endif
diff --git a/Runtime/Graphics/ParticleSystem/ParticleSystemUtils.cpp b/Runtime/Graphics/ParticleSystem/ParticleSystemUtils.cpp
new file mode 100644
index 0000000..032cd19
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/ParticleSystemUtils.cpp
@@ -0,0 +1,113 @@
+#include "UnityPrefix.h"
+#include "ParticleSystemUtils.h"
+#include "ParticleSystem.h"
+#include "ParticleSystemCurves.h"
+#include "Modules/ParticleSystemModule.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/ParticleSystem/ParticleSystemEditor.h"
+#endif
+
+UInt32 randomSeed = 0x1337;
+
+UInt32 GetGlobalRandomSeed ()
+{
+ return ++randomSeed;
+}
+
+void ResetGlobalRandomSeed ()
+{
+ randomSeed = 0x1337;
+}
+
+Vector2f CalculateInverseLerpOffsetScale (const Vector2f& range)
+{
+ Assert (range.x < range.y);
+ float scale = 1.0F / (range.y - range.x);
+ return Vector2f (scale, -range.x * scale);
+}
+
+void CalculatePositionAndVelocity(Vector3f& initialPosition, Vector3f& initialVelocity, const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, const ParticleSystemParticles& ps, const size_t index)
+{
+ initialPosition = ps.position[index];
+ initialVelocity = ps.velocity[index] + ps.animatedVelocity[index];
+ if(roState.useLocalSpace)
+ {
+ // If we are in local space, transform to world space to make independent of this emitters transform
+ initialPosition = state.localToWorld.MultiplyPoint3(initialPosition);
+ initialVelocity = state.localToWorld.MultiplyVector3(initialVelocity);
+ }
+}
+
+void KillParticle(const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& ps, size_t index, size_t& particleCount)
+{
+ Assert(particleCount > 0);
+
+ for(int i = 0; i < state.numCachedSubDataDeath; i++)
+ {
+ ParticleSystemEmissionState emissionState;
+ RecordEmit(emissionState, state.cachedSubDataDeath[i], roState, state, ps, kParticleSystemSubTypeDeath, i, index, 0.0f, 0.0001f, 1.0f);
+ }
+
+ ps.element_assign (index, particleCount - 1);
+ --particleCount;
+
+}
+
+void RecordEmit(ParticleSystemEmissionState& emissionState, const ParticleSystemSubEmitterData& subEmitterData, const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& ps, ParticleSystemSubType type, int subEmitterIndex, size_t particleIndex, float t, float dt, float length)
+{
+ size_t numContinuous = 0;
+ Vector3f initialPosition;
+ Vector3f initialVelocity;
+ CalculatePositionAndVelocity(initialPosition, initialVelocity, roState, state, ps, particleIndex);
+ int amountOfParticlesToEmit = ParticleSystem::EmitFromData (emissionState, numContinuous, subEmitterData.emissionData, initialVelocity, t, std::min(t + dt, length), dt, length);
+ if(amountOfParticlesToEmit)
+ {
+ if(!state.recordSubEmits)
+ ParticleSystem::Emit(*subEmitterData.emitter, SubEmitterEmitCommand(emissionState, initialPosition, initialVelocity, type, subEmitterIndex, amountOfParticlesToEmit, numContinuous, t, dt), kParticleSystemEMStaging);
+ else if(!state.subEmitterCommandBuffer.IsFull())
+ state.subEmitterCommandBuffer.AddCommand(emissionState, initialPosition, initialVelocity, type, subEmitterIndex, amountOfParticlesToEmit, numContinuous, t, dt);
+ }
+}
+
+bool GetTransformationMatrix(Matrix4x4f& output, const bool isSystemInWorld, const bool isCurveInWorld, const Matrix4x4f& localToWorld)
+{
+ if(isCurveInWorld != isSystemInWorld)
+ {
+ if(isSystemInWorld)
+ output = localToWorld;
+ else
+ Matrix4x4f::Invert_General3D(localToWorld, output);
+ return true;
+ }
+ else
+ {
+ output = Matrix4x4f::identity;
+ return false;
+ }
+}
+
+bool GetTransformationMatrices(Matrix4x4f& output, Matrix4x4f& outputInverse, const bool isSystemInWorld, const bool isCurveInWorld, const Matrix4x4f& localToWorld)
+{
+ if(isCurveInWorld != isSystemInWorld)
+ {
+ if(isSystemInWorld)
+ {
+ output = localToWorld;
+ Matrix4x4f::Invert_General3D(localToWorld, outputInverse);
+ }
+ else
+ {
+ Matrix4x4f::Invert_General3D(localToWorld, output);
+ outputInverse = localToWorld;
+ }
+ return true;
+ }
+ else
+ {
+ output = Matrix4x4f::identity;
+ outputInverse = Matrix4x4f::identity;
+ return false;
+ }
+}
diff --git a/Runtime/Graphics/ParticleSystem/ParticleSystemUtils.h b/Runtime/Graphics/ParticleSystem/ParticleSystemUtils.h
new file mode 100644
index 0000000..45ec0a3
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/ParticleSystemUtils.h
@@ -0,0 +1,48 @@
+#ifndef SHURIKENUTILS_H
+#define SHURIKENUTILS_H
+
+#include "ParticleSystemCommon.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Random/Random.h"
+
+class Matrix4x4f;
+struct ParticleSystemParticles;
+struct ParticleSystemReadOnlyState;
+struct ParticleSystemState;
+struct ParticleSystemEmissionState;
+struct ParticleSystemSubEmitterData;
+
+inline float InverseLerpFast01 (const Vector2f& scaleOffset, float v)
+{
+ return clamp01 (v * scaleOffset.x + scaleOffset.y);
+}
+
+inline float GenerateRandom(UInt32 randomIn)
+{
+ Rand rand(randomIn);
+ return Random01(rand);
+}
+
+inline void GenerateRandom3(Vector3f& randomOut, UInt32 randomIn)
+{
+ Rand rand(randomIn);
+ randomOut.x = Random01(rand);
+ randomOut.y = Random01(rand);
+ randomOut.z = Random01(rand);
+}
+
+inline UInt8 GenerateRandomByte (UInt32 seed)
+{
+ Rand rand (seed);
+ return Rand::GetByteFromInt (rand.Get ());
+}
+
+UInt32 GetGlobalRandomSeed ();
+void ResetGlobalRandomSeed ();
+Vector2f CalculateInverseLerpOffsetScale (const Vector2f& range);
+void KillParticle(const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& ps, size_t index, size_t& particleCount);
+void RecordEmit(ParticleSystemEmissionState& emissionState, const ParticleSystemSubEmitterData& subEmitterData, const ParticleSystemReadOnlyState& roState, ParticleSystemState& state, ParticleSystemParticles& ps, ParticleSystemSubType type, int subEmitterIndex, size_t particleIndex, float t, float dt, float length);
+bool GetTransformationMatrix(Matrix4x4f& output, const bool isSystemInWorld, const bool isCurveInWorld, const Matrix4x4f& localToWorld);
+bool GetTransformationMatrices(Matrix4x4f& output, Matrix4x4f& outputInverse, const bool isSystemInWorld, const bool isCurveInWorld, const Matrix4x4f& localToWorld);
+
+#endif // SHURIKENUTILS_H
diff --git a/Runtime/Graphics/ParticleSystem/PolynomialCurve.cpp b/Runtime/Graphics/ParticleSystem/PolynomialCurve.cpp
new file mode 100644
index 0000000..f20b0ac
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/PolynomialCurve.cpp
@@ -0,0 +1,405 @@
+#include "UnityPrefix.h"
+#include "PolynomialCurve.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Polynomials.h"
+#include "Runtime/Math/AnimationCurve.h"
+
+static void DoubleIntegrateSegment (float* coeff)
+{
+ coeff[0] /= 20.0F;
+ coeff[1] /= 12.0F;
+ coeff[2] /= 6.0F;
+ coeff[3] /= 2.0F;
+}
+
+static void IntegrateSegment (float* coeff)
+{
+ coeff[0] /= 4.0F;
+ coeff[1] /= 3.0F;
+ coeff[2] /= 2.0F;
+ coeff[3] /= 1.0F;
+}
+
+void CalculateMinMax(Vector2f& minmax, float value)
+{
+ minmax.x = std::min(minmax.x, value);
+ minmax.y = std::max(minmax.y, value);
+}
+
+void ConstrainToPolynomialCurve (AnimationCurve& curve)
+{
+ const int max = OptimizedPolynomialCurve::kMaxPolynomialKeyframeCount;
+
+ // Maximum 3 keys
+ if (curve.GetKeyCount () > max)
+ curve.RemoveKeys(curve.begin() + max, curve.end());
+
+ // Clamp begin and end to 0...1 range
+ if (curve.GetKeyCount () >= 2)
+ {
+ curve.GetKey(0).time = 0;
+ curve.GetKey(curve.GetKeyCount ()-1).time = 1;
+ }
+}
+
+bool IsValidPolynomialCurve (const AnimationCurve& curve)
+{
+ // Maximum 3 keys
+ if (curve.GetKeyCount () > OptimizedPolynomialCurve::kMaxPolynomialKeyframeCount)
+ return false;
+ // One constant key can always be representated
+ else if (curve.GetKeyCount () <= 1)
+ return true;
+ // First and last keyframe must be at 0 and 1 time
+ else
+ {
+ float beginTime = curve.GetKey(0).time;
+ float endTime = curve.GetKey(curve.GetKeyCount ()-1).time;
+
+ return CompareApproximately(beginTime, 0.0F, 0.0001F) && CompareApproximately(endTime, 1.0F, 0.0001F);
+ }
+}
+
+void SetPolynomialCurveToValue (AnimationCurve& a, OptimizedPolynomialCurve& c, float value)
+{
+ AnimationCurve::Keyframe keys[2] = { AnimationCurve::Keyframe(0.0f, value), AnimationCurve::Keyframe(1.0f, value) };
+ a.Assign(keys, keys + 2);
+ c.BuildOptimizedCurve(a, 1.0f);
+}
+
+void SetPolynomialCurveToLinear (AnimationCurve& a, OptimizedPolynomialCurve& c)
+{
+ AnimationCurve::Keyframe keys[2] = { AnimationCurve::Keyframe(0.0f, 0.0f), AnimationCurve::Keyframe(1.0f, 1.0f) };
+ keys[0].inSlope = 0.0f; keys[0].outSlope = 1.0f;
+ keys[1].inSlope = 1.0f; keys[1].outSlope = 0.0f;
+ a.Assign(keys, keys + 2);
+ c.BuildOptimizedCurve(a, 1.0f);
+}
+
+bool OptimizedPolynomialCurve::BuildOptimizedCurve (const AnimationCurve& editorCurve, float scale)
+{
+ if (!IsValidPolynomialCurve(editorCurve))
+ return false;
+
+ const size_t keyCount = editorCurve.GetKeyCount ();
+
+ timeValue = 1.0F;
+ memset(segments, 0, sizeof(segments));
+
+ // Handle corner case 1 & 0 keyframes
+ if (keyCount == 0)
+ ;
+ else if (keyCount == 1)
+ {
+ // Set constant value coefficient
+ for (int i=0;i<kSegmentCount;i++)
+ segments[i].coeff[3] = editorCurve.GetKey(0).value * scale;
+ }
+ else
+ {
+ float segmentStartTime[kSegmentCount];
+
+ for (int i=0;i<kSegmentCount;i++)
+ {
+ bool hasSegment = i+1 < keyCount;
+ if (hasSegment)
+ {
+ AnimationCurve::Cache cache;
+ editorCurve.CalculateCacheData(cache, i, i + 1, 0.0F);
+
+ memcpy(segments[i].coeff, cache.coeff, sizeof(Polynomial));
+ segmentStartTime[i] = editorCurve.GetKey(i).time;
+ }
+ else
+ {
+ memcpy(segments[i].coeff, segments[i-1].coeff, sizeof(Polynomial));
+ segmentStartTime[i] = 1.0F;//timeValue[i-1];
+ }
+ }
+
+ // scale curve
+ for (int i=0;i<kSegmentCount;i++)
+ {
+ segments[i].coeff[0] *= scale;
+ segments[i].coeff[1] *= scale;
+ segments[i].coeff[2] *= scale;
+ segments[i].coeff[3] *= scale;
+ }
+
+ // Timevalue 0 is always 0.0F. No need to store it.
+ timeValue = segmentStartTime[1];
+
+ #if !UNITY_RELEASE
+ // Check that UI editor curve matches polynomial curve except if the
+ // scale happens to be infinite (trying to subtract infinity values from
+ // each other yields NaN with IEEE floats).
+ if (scale != std::numeric_limits<float>::infinity () &&
+ scale != -std::numeric_limits<float>::infinity())
+ {
+ for (int i=0;i<=50;i++)
+ {
+ // The very last element at 1.0 can be different when using step curves.
+ // The AnimationCurve implementation will sample the position of the last key.
+ // The OptimizedPolynomialCurve will keep value of the previous key (Continuing the trajectory of the segment)
+ // In practice this is probably not a problem, since you don't sample 1.0 because then the particle will be dead.
+ // thus we just don't do the automatic assert when the curves are not in sync in that case.
+ float t = std::min(i / 50.0F, 0.99999F);
+ float dif;
+
+ DebugAssert((dif = Abs(Evaluate(t) - editorCurve.Evaluate(t) * scale)) < 0.01F);
+ }
+ }
+ #endif
+ }
+
+ return true;
+}
+
+void OptimizedPolynomialCurve::Integrate ()
+{
+ for (int i=0;i<kSegmentCount;i++)
+ IntegrateSegment(segments[i].coeff);
+}
+
+void OptimizedPolynomialCurve::DoubleIntegrate ()
+{
+ Polynomial velocity0 = segments[0];
+ IntegrateSegment (velocity0.coeff);
+
+ velocityValue = Polynomial::EvalSegment(timeValue, velocity0.coeff) * timeValue;
+
+ for (int i=0;i<kSegmentCount;i++)
+ DoubleIntegrateSegment(segments[i].coeff);
+}
+
+Vector2f OptimizedPolynomialCurve::FindMinMaxDoubleIntegrated() const
+{
+ // Because of velocityValue * t, this becomes a quartic polynomial (4th order polynomial).
+ // TODO: Find all roots of quartic polynomial
+ Vector2f result = Vector2f::zero;
+ const int numSteps = 20;
+ const float delta = 1.0f / float(numSteps);
+ float acc = delta;
+ for(int i = 0; i < numSteps; i++)
+ {
+ CalculateMinMax(result, EvaluateDoubleIntegrated(acc));
+ acc += delta;
+ }
+ return result;
+}
+
+// Find the maximum of the integrated curve (x: min, y: max)
+Vector2f OptimizedPolynomialCurve::FindMinMaxIntegrated() const
+{
+ Vector2f result = Vector2f::zero;
+
+ float start[kSegmentCount] = {0.0f, timeValue};
+ float end[kSegmentCount] = {timeValue, 1.0f};
+ for(int i = 0; i < kSegmentCount; i++)
+ {
+ // Differentiate coefficients
+ float a = 4.0f*segments[i].coeff[0];
+ float b = 3.0f*segments[i].coeff[1];
+ float c = 2.0f*segments[i].coeff[2];
+ float d = 1.0f*segments[i].coeff[3];
+
+ float roots[3];
+ int numRoots = CubicPolynomialRootsGeneric(roots, a, b, c, d);
+ for(int r = 0; r < numRoots; r++)
+ {
+ float root = roots[r] + start[i];
+ if((root >= start[i]) && (root < end[i]))
+ CalculateMinMax(result, EvaluateIntegrated(root));
+ }
+
+ // TODO: Don't use eval integrated, use eval segment (and integrate in loop)
+ CalculateMinMax(result, EvaluateIntegrated(end[i]));
+ }
+ return result;
+}
+
+// Find the maximum of a double integrated curve (x: min, y: max)
+Vector2f PolynomialCurve::FindMinMaxDoubleIntegrated() const
+{
+ // Because of velocityValue * t, this becomes a quartic polynomial (4th order polynomial).
+ // TODO: Find all roots of quartic polynomial
+ Vector2f result = Vector2f::zero;
+ const int numSteps = 20;
+ const float delta = 1.0f / float(numSteps);
+ float acc = delta;
+ for(int i = 0; i < numSteps; i++)
+ {
+ CalculateMinMax(result, EvaluateDoubleIntegrated(acc));
+ acc += delta;
+ }
+ return result;
+}
+
+Vector2f PolynomialCurve::FindMinMaxIntegrated() const
+{
+ Vector2f result = Vector2f::zero;
+
+ float prevTimeValue = 0.0f;
+ for(int i = 0; i < segmentCount; i++)
+ {
+ // Differentiate coefficients
+ float a = 4.0f*segments[i].coeff[0];
+ float b = 3.0f*segments[i].coeff[1];
+ float c = 2.0f*segments[i].coeff[2];
+ float d = 1.0f*segments[i].coeff[3];
+
+ float roots[3];
+ int numRoots = CubicPolynomialRootsGeneric(roots, a, b, c, d);
+ for(int r = 0; r < numRoots; r++)
+ {
+ float root = roots[r] + prevTimeValue;
+ if((root >= prevTimeValue) && (root < times[i]))
+ CalculateMinMax(result, EvaluateIntegrated(root));
+ }
+
+ // TODO: Don't use eval integrated, use eval segment (and integrate in loop)
+ CalculateMinMax(result, EvaluateIntegrated(times[i]));
+ prevTimeValue = times[i];
+ }
+ return result;
+}
+
+bool PolynomialCurve::IsValidCurve(const AnimationCurve& editorCurve)
+{
+ int keyCount = editorCurve.GetKeyCount();
+ int segmentCount = keyCount - 1;
+ if(editorCurve.GetKey(0).time != 0.0f)
+ segmentCount++;
+ if(editorCurve.GetKey(keyCount-1).time != 1.0f)
+ segmentCount++;
+ return segmentCount <= kMaxNumSegments;
+}
+
+bool PolynomialCurve::BuildCurve(const AnimationCurve& editorCurve, float scale)
+{
+ int keyCount = editorCurve.GetKeyCount();
+ segmentCount = 1;
+
+ const float kMaxTime = 1.01f;
+
+ memset(segments, 0, sizeof(segments));
+ memset(integrationCache, 0, sizeof(integrationCache));
+ memset(doubleIntegrationCache, 0, sizeof(doubleIntegrationCache));
+ memset(times, 0, sizeof(times));
+ times[0] = kMaxTime;
+
+ // Handle corner case 1 & 0 keyframes
+ if (keyCount == 0)
+ ;
+ else if (keyCount == 1)
+ {
+ // Set constant value coefficient
+ segments[0].coeff[3] = editorCurve.GetKey(0).value * scale;
+ }
+ else
+ {
+ segmentCount = keyCount - 1;
+ int segmentOffset = 0;
+
+ // Add extra key to start if it doesn't match up
+ if(editorCurve.GetKey(0).time != 0.0f)
+ {
+ segments[0].coeff[3] = editorCurve.GetKey(0).value;
+ times[0] = editorCurve.GetKey(0).time;
+ segmentOffset = 1;
+ }
+
+ for (int i = 0;i<segmentCount;i++)
+ {
+ DebugAssert(i+1 < keyCount);
+ AnimationCurve::Cache cache;
+ editorCurve.CalculateCacheData(cache, i, i + 1, 0.0F);
+ memcpy(segments[i+segmentOffset].coeff, cache.coeff, 4 * sizeof(float));
+ times[i+segmentOffset] = editorCurve.GetKey(i+1).time;
+ }
+ segmentCount += segmentOffset;
+
+ // Add extra key to start if it doesn't match up
+ if(editorCurve.GetKey(keyCount-1).time != 1.0f)
+ {
+ segments[segmentCount].coeff[3] = editorCurve.GetKey(keyCount-1).value;
+ segmentCount++;
+ }
+
+ // Fixup last key time value
+ times[segmentCount-1] = kMaxTime;
+
+ for (int i = 0;i<segmentCount;i++)
+ {
+ segments[i].coeff[0] *= scale;
+ segments[i].coeff[1] *= scale;
+ segments[i].coeff[2] *= scale;
+ segments[i].coeff[3] *= scale;
+ }
+ }
+
+ DebugAssert(segmentCount <= kMaxNumSegments);
+
+#if !UNITY_RELEASE
+ // Check that UI editor curve matches polynomial curve
+ for (int i=0;i<=10;i++)
+ {
+ // The very last element at 1.0 can be different when using step curves.
+ // The AnimationCurve implementation will sample the position of the last key.
+ // The PolynomialCurve will keep value of the previous key (Continuing the trajectory of the segment)
+ // In practice this is probably not a problem, since you don't sample 1.0 because then the particle will be dead.
+ // thus we just don't do the automatic assert when the curves are not in sync in that case.
+ float t = std::min(i / 50.0F, 0.99999F);
+ float dif;
+ DebugAssert((dif = Abs(Evaluate(t) - editorCurve.Evaluate(t) * scale)) < 0.01F);
+ }
+#endif
+ return true;
+}
+
+void GenerateIntegrationCache(PolynomialCurve& curve)
+{
+ curve.integrationCache[0] = 0.0f;
+ float prevTimeValue0 = curve.times[0];
+ float prevTimeValue1 = 0.0f;
+ for (int i=1;i<curve.segmentCount;i++)
+ {
+ float coeff[4];
+ memcpy(coeff, curve.segments[i-1].coeff, 4*sizeof(float));
+ IntegrateSegment (coeff);
+ float time = prevTimeValue0 - prevTimeValue1;
+ curve.integrationCache[i] = curve.integrationCache[i-1] + Polynomial::EvalSegment(time, coeff) * time;
+ prevTimeValue1 = prevTimeValue0;
+ prevTimeValue0 = curve.times[i];
+ }
+}
+
+// Expects double integrated segments and valid integration cache
+void GenerateDoubleIntegrationCache(PolynomialCurve& curve)
+{
+ float sum = 0.0f;
+ float prevTimeValue = 0.0f;
+ for(int i = 0; i < curve.segmentCount; i++)
+ {
+ curve.doubleIntegrationCache[i] = sum;
+ float time = curve.times[i] - prevTimeValue;
+ time = std::max(time, 0.0f);
+ sum += Polynomial::EvalSegment(time, curve.segments[i].coeff) * time * time + curve.integrationCache[i] * time;
+ prevTimeValue = curve.times[i];
+ }
+}
+
+void PolynomialCurve::Integrate ()
+{
+ GenerateIntegrationCache(*this);
+ for (int i=0;i<segmentCount;i++)
+ IntegrateSegment(segments[i].coeff);
+}
+
+void PolynomialCurve::DoubleIntegrate ()
+{
+ GenerateIntegrationCache(*this);
+ for (int i=0;i<segmentCount;i++)
+ DoubleIntegrateSegment(segments[i].coeff);
+ GenerateDoubleIntegrationCache(*this);
+}
diff --git a/Runtime/Graphics/ParticleSystem/PolynomialCurve.h b/Runtime/Graphics/ParticleSystem/PolynomialCurve.h
new file mode 100644
index 0000000..d9e7270
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/PolynomialCurve.h
@@ -0,0 +1,229 @@
+#ifndef POLYONOMIAL_CURVE_H
+#define POLYONOMIAL_CURVE_H
+
+template<class T>
+class AnimationCurveTpl;
+typedef AnimationCurveTpl<float> AnimationCurve;
+class Vector2f;
+
+struct Polynomial
+{
+ static float EvalSegment (float t, const float* coeff)
+ {
+ return (t * (t * (t * coeff[0] + coeff[1]) + coeff[2])) + coeff[3];
+ }
+
+ float coeff[4];
+};
+
+// Smaller, optimized version
+struct OptimizedPolynomialCurve
+{
+ enum { kMaxPolynomialKeyframeCount = 3, kSegmentCount = kMaxPolynomialKeyframeCount-1, };
+
+ Polynomial segments[kSegmentCount];
+
+ float timeValue;
+ float velocityValue;
+
+ // Evaluate double integrated Polynomial curve.
+ // Example: position = EvaluateDoubleIntegrated (normalizedTime) * startEnergy^2
+ // Use DoubleIntegrate function to for example turn a force curve into a position curve.
+ // Expects that t is in the 0...1 range.
+ float EvaluateDoubleIntegrated (float t) const
+ {
+ DebugAssert(t >= -0.01F && t <= 1.01F);
+ float res0, res1;
+
+ // All segments are added together. At t = 0, the integrated curve is always zero.
+
+ // 0 segment is sampled up to the 1 keyframe
+ // First key is always assumed to be at 0 time
+ float t1 = std::min(t, timeValue);
+
+ // 1 segment is sampled from 1 key to 2 key
+ // Last key is always assumed to be at 1 time
+ float t2 = std::max(0.0F, t - timeValue);
+
+ res0 = Polynomial::EvalSegment(t1, segments[0].coeff) * t1 * t1;
+ res1 = Polynomial::EvalSegment(t2, segments[1].coeff) * t2 * t2;
+
+ float finalResult = res0 + res1;
+
+ // Add velocity of previous segments
+ finalResult += velocityValue * std::max(t - timeValue, 0.0F);
+
+ return finalResult;
+ }
+
+ // Evaluate integrated Polynomial curve.
+ // Example: position = EvaluateIntegrated (normalizedTime) * startEnergy
+ // Use Integrate function to for example turn a velocity curve into a position curve.
+ // Expects that t is in the 0...1 range.
+ float EvaluateIntegrated (float t) const
+ {
+ DebugAssert(t >= -0.01F && t <= 1.01F);
+ float res0, res1;
+
+ // All segments are added together. At t = 0, the integrated curve is always zero.
+
+ // 0 segment is sampled up to the 1 keyframe
+ // First key is always assumed to be at 0 time
+ float t1 = std::min(t, timeValue);
+
+ // 1 segment is sampled from 1 key to 2 key
+ // Last key is always assumed to be at 1 time
+ float t2 = std::max(0.0F, t - timeValue);
+
+ res0 = Polynomial::EvalSegment(t1, segments[0].coeff) * t1;
+ res1 = Polynomial::EvalSegment(t2, segments[1].coeff) * t2;
+
+ return (res0 + res1);
+ }
+
+ // Evaluate the curve
+ // extects that t is in the 0...1 range
+ float Evaluate (float t) const
+ {
+ DebugAssert(t >= -0.01F && t <= 1.01F);
+
+ float res0 = Polynomial::EvalSegment(t, segments[0].coeff);
+ float res1 = Polynomial::EvalSegment(t - timeValue, segments[1].coeff);
+
+ float result;
+ if (t > timeValue)
+ result = res1;
+ else
+ result = res0;
+
+ return result;
+ }
+
+ // Find the maximum of a double integrated curve (x: min, y: max)
+ Vector2f FindMinMaxDoubleIntegrated() const;
+
+ // Find the maximum of the integrated curve (x: min, y: max)
+ Vector2f FindMinMaxIntegrated() const;
+
+ // Precalculates polynomials from the animation curve and a scale factor
+ bool BuildOptimizedCurve (const AnimationCurve& editorCurve, float scale);
+
+ // Integrates a velocity curve to be a position curve.
+ // You have to call EvaluateIntegrated to evaluate the curve
+ void Integrate ();
+
+ // Integrates a velocity curve to be a position curve.
+ // You have to call EvaluateDoubleIntegrated to evaluate the curve
+ void DoubleIntegrate ();
+
+ // Add a constant force to a velocity curve
+ // Assumes that you have already called Integrate on the velocity curve.
+ void AddConstantForceToVelocityCurve (float gravity)
+ {
+ for (int i=0;i<kSegmentCount;i++)
+ segments[i].coeff[1] += 0.5F * gravity;
+ }
+};
+
+// Bigger, not so optimized version
+struct PolynomialCurve
+{
+ enum{ kMaxNumSegments = 8 };
+
+ Polynomial segments[kMaxNumSegments]; // Cached polynomial coefficients
+ float integrationCache[kMaxNumSegments]; // Cache of integrated values up until start of segments
+ float doubleIntegrationCache[kMaxNumSegments]; // Cache of double integrated values up until start of segments
+ float times[kMaxNumSegments]; // Time value for end of segment
+
+ int segmentCount;
+
+ // Find the maximum of a double integrated curve (x: min, y: max)
+ Vector2f FindMinMaxDoubleIntegrated() const;
+
+ // Find the maximum of the integrated curve (x: min, y: max)
+ Vector2f FindMinMaxIntegrated() const;
+
+ // Precalculates polynomials from the animation curve and a scale factor
+ bool BuildCurve(const AnimationCurve& editorCurve, float scale);
+
+ // Integrates a velocity curve to be a position curve.
+ // You have to call EvaluateIntegrated to evaluate the curve
+ void Integrate ();
+
+ // Integrates a velocity curve to be a position curve.
+ // You have to call EvaluateDoubleIntegrated to evaluate the curve
+ void DoubleIntegrate ();
+
+ // Evaluates if it is possible to represent animation curve as PolynomialCurve
+ static bool IsValidCurve(const AnimationCurve& editorCurve);
+
+ // Evaluate double integrated Polynomial curve.
+ // Example: position = EvaluateDoubleIntegrated (normalizedTime) * startEnergy^2
+ // Use DoubleIntegrate function to for example turn a force curve into a position curve.
+ // Expects that t is in the 0...1 range.
+ float EvaluateDoubleIntegrated (float t) const
+ {
+ DebugAssert(t >= -0.01F && t <= 1.01F);
+
+ float prevTimeValue = 0.0f;
+ for(int i = 0; i < segmentCount; i++)
+ {
+ if(t <= times[i])
+ {
+ const float time = t - prevTimeValue;
+ return doubleIntegrationCache[i] + integrationCache[i] * time + Polynomial::EvalSegment(time, segments[i].coeff) * time * time;
+ }
+ prevTimeValue = times[i];
+ }
+ DebugAssert(!"PolyCurve: Outside segment range!");
+ return 1.0f;
+ }
+
+ // Evaluate integrated Polynomial curve.
+ // Example: position = EvaluateIntegrated (normalizedTime) * startEnergy
+ // Use Integrate function to for example turn a velocity curve into a position curve.
+ // Expects that t is in the 0...1 range.
+ float EvaluateIntegrated (float t) const
+ {
+ DebugAssert(t >= -0.01F && t <= 1.01F);
+
+ float prevTimeValue = 0.0f;
+ for(int i = 0; i < segmentCount; i++)
+ {
+ if(t <= times[i])
+ {
+ const float time = t - prevTimeValue;
+ return integrationCache[i] + Polynomial::EvalSegment(time, segments[i].coeff) * time;
+ }
+ prevTimeValue = times[i];
+ }
+ DebugAssert(!"PolyCurve: Outside segment range!");
+ return 1.0f;
+ }
+
+ // Evaluate the curve
+ // extects that t is in the 0...1 range
+ float Evaluate(float t) const
+ {
+ DebugAssert(t >= -0.01F && t <= 1.01F);
+
+ float prevTimeValue = 0.0f;
+ for(int i = 0; i < segmentCount; i++)
+ {
+ if(t <= times[i])
+ return Polynomial::EvalSegment(t - prevTimeValue, segments[i].coeff);
+ prevTimeValue = times[i];
+ }
+ DebugAssert(!"PolyCurve: Outside segment range!");
+ return 1.0f;
+ }
+};
+
+
+void SetPolynomialCurveToValue (AnimationCurve& a, OptimizedPolynomialCurve& c, float value);
+void SetPolynomialCurveToLinear (AnimationCurve& a, OptimizedPolynomialCurve& c);
+void ConstrainToPolynomialCurve (AnimationCurve& curve);
+bool IsValidPolynomialCurve (const AnimationCurve& curve);
+void CalculateMinMax(Vector2f& minmax, float value);
+
+#endif // POLYONOMIAL_CURVE_H
diff --git a/Runtime/Graphics/ParticleSystem/PolynomialCurveTests.cpp b/Runtime/Graphics/ParticleSystem/PolynomialCurveTests.cpp
new file mode 100644
index 0000000..2a74710
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/PolynomialCurveTests.cpp
@@ -0,0 +1,259 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Animation/AnimationCurveUtility.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Graphics/ParticleSystem/PolynomialCurve.h"
+
+static float PhysicsSimulate (float gravity, float velocity, float time)
+{
+ return velocity * time + gravity * time * time * 0.5F;
+}
+
+SUITE (PolynomialCurveTests)
+{
+TEST (PolynomialCurve_ConstantIntegrate)
+{
+ AnimationCurve editorCurve;
+ OptimizedPolynomialCurve curve;
+ PolynomialCurve polyCurve;
+
+ SetPolynomialCurveToValue(editorCurve, curve, 1.0f);
+ curve.Integrate();
+ polyCurve.BuildCurve(editorCurve, 1.0f);
+ polyCurve.Integrate();
+
+ CHECK_CLOSE(0.0F, curve.EvaluateIntegrated(0.0F), 0.0001F);
+ CHECK_CLOSE(0.5F, curve.EvaluateIntegrated(0.5F), 0.0001F);
+ CHECK_CLOSE(1.0F, curve.EvaluateIntegrated(1.0F), 0.0001F);
+
+ CHECK_CLOSE(0.0F, polyCurve.EvaluateIntegrated(0.0F), 0.0001F);
+ CHECK_CLOSE(0.5F, polyCurve.EvaluateIntegrated(0.5F), 0.0001F);
+ CHECK_CLOSE(1.0F, polyCurve.EvaluateIntegrated(1.0F), 0.0001F);
+
+ CHECK_EQUAL(true, CompareApproximately(Vector2f(0.0f, 1.0f), curve.FindMinMaxIntegrated(), 0.0001F));
+ CHECK_EQUAL(true, CompareApproximately(Vector2f(0.0f, 1.01f), polyCurve.FindMinMaxIntegrated(), 0.0001F));
+
+
+ SetPolynomialCurveToLinear(editorCurve, curve);
+ curve.Integrate();
+ polyCurve.BuildCurve(editorCurve, 1.0f);
+ polyCurve.Integrate();
+
+ CHECK_CLOSE(0.0F, curve.EvaluateIntegrated(0.0F), 0.0001F);
+ CHECK_CLOSE(0.125F, curve.EvaluateIntegrated(0.5F), 0.0001F);
+ CHECK_CLOSE(0.5F, curve.EvaluateIntegrated(1.0F), 0.0001F);
+
+ CHECK_CLOSE(0.0F, polyCurve.EvaluateIntegrated(0.0F), 0.0001F);
+ CHECK_CLOSE(0.125F, polyCurve.EvaluateIntegrated(0.5F), 0.0001F);
+ CHECK_CLOSE(0.5F, polyCurve.EvaluateIntegrated(1.0F), 0.0001F);
+
+ CHECK_EQUAL(true, CompareApproximately(Vector2f(0.0f, 0.5f), curve.FindMinMaxIntegrated(), 0.0001F));
+ CHECK_EQUAL(true, CompareApproximately(Vector2f(0.0f, 0.51005f), polyCurve.FindMinMaxIntegrated(), 0.0001F));
+}
+
+// @TODO: Add generic poly
+TEST (PolynomialCurve_ConstantDoubleIntegrate)
+{
+ AnimationCurve editorCurve;
+ OptimizedPolynomialCurve curve;
+ PolynomialCurve polyCurve;
+
+ SetPolynomialCurveToValue(editorCurve, curve, 1.0f);
+ curve.DoubleIntegrate();
+
+ polyCurve.BuildCurve(editorCurve, 1.0f);
+ polyCurve.DoubleIntegrate();
+
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 0.00F), curve.EvaluateDoubleIntegrated(0.00F), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 0.25F), curve.EvaluateDoubleIntegrated(0.25F), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 0.50F), curve.EvaluateDoubleIntegrated(0.50F), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 0.75F), curve.EvaluateDoubleIntegrated(0.75F), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 1.00F), curve.EvaluateDoubleIntegrated(1.00F), 0.0001F);
+
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 0.00F), polyCurve.EvaluateDoubleIntegrated(0.00F), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 0.25F), polyCurve.EvaluateDoubleIntegrated(0.25F), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 0.50F), polyCurve.EvaluateDoubleIntegrated(0.50F), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 0.75F), polyCurve.EvaluateDoubleIntegrated(0.75F), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 1.00F), polyCurve.EvaluateDoubleIntegrated(1.00F), 0.0001F);
+
+ CHECK_EQUAL(true, CompareApproximately(Vector2f(0.0f, 0.5f), curve.FindMinMaxDoubleIntegrated(), 0.0001F));
+ CHECK_EQUAL(true, CompareApproximately(Vector2f(0.0f, 0.5f), polyCurve.FindMinMaxDoubleIntegrated(), 0.0001F));
+
+ AnimationCurve::Keyframe keys[3] = { AnimationCurve::Keyframe(0.0f, 1.0f), AnimationCurve::Keyframe(0.5f, 1.0f), AnimationCurve::Keyframe(1.0f, 1.0f) };
+ editorCurve.Assign(keys, keys + 3);
+ curve.BuildOptimizedCurve(editorCurve, 1);
+ curve.DoubleIntegrate();
+
+ polyCurve.BuildCurve(editorCurve, 1.0f);
+ polyCurve.DoubleIntegrate();
+
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 0.00F), curve.EvaluateDoubleIntegrated(0.00F), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 0.25F), curve.EvaluateDoubleIntegrated(0.25F), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 0.50F), curve.EvaluateDoubleIntegrated(0.50F), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 0.75F), curve.EvaluateDoubleIntegrated(0.75F), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 1.00F), curve.EvaluateDoubleIntegrated(1.00F), 0.0001F);
+
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 0.00F), polyCurve.EvaluateDoubleIntegrated(0.00F), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 0.25F), polyCurve.EvaluateDoubleIntegrated(0.25F), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 0.50F), polyCurve.EvaluateDoubleIntegrated(0.50F), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 0.75F), polyCurve.EvaluateDoubleIntegrated(0.75F), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(1, 0, 1.00F), polyCurve.EvaluateDoubleIntegrated(1.00F), 0.0001F);
+
+ CHECK_EQUAL(true, CompareApproximately(Vector2f(0.0f, 0.5f), curve.FindMinMaxDoubleIntegrated(), 0.0001F));
+ CHECK_EQUAL(true, CompareApproximately(Vector2f(0.0f, 0.5f), polyCurve.FindMinMaxDoubleIntegrated(), 0.0001F));
+}
+
+static float EvaluateGravityIntegrated (OptimizedPolynomialCurve& gravityCurve, float time, float maximumRange)
+{
+ float res = gravityCurve.EvaluateDoubleIntegrated(time / maximumRange) * maximumRange * maximumRange;
+ return res;
+}
+
+TEST (PolynomialCurve_GravityIntegrate)
+{
+ const float kGravity = -9.81f;
+ AnimationCurve editorCurve;
+ OptimizedPolynomialCurve gravityCurve;
+ SetPolynomialCurveToValue(editorCurve, gravityCurve, kGravity);
+ gravityCurve.DoubleIntegrate();
+
+ const float kMaxRange = 5.0F;
+
+ CHECK_CLOSE(PhysicsSimulate(kGravity, 0.0F, 0.0F), EvaluateGravityIntegrated(gravityCurve, 0.0F, kMaxRange), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(kGravity, 0.0F, 0.5F), EvaluateGravityIntegrated(gravityCurve, 0.5F, kMaxRange), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(kGravity, 0.0F, 1.0F), EvaluateGravityIntegrated(gravityCurve, 1.0F, kMaxRange), 0.0001F);
+ CHECK_CLOSE(PhysicsSimulate(kGravity, 0.0F, 5.0F), EvaluateGravityIntegrated(gravityCurve, 5.0F, kMaxRange), 0.0001F);
+}
+
+float TriangleShapeIntegralFirstHalf (float x)
+{
+ return (2.0F / 3.0F) *x*x*x - 0.5F*x*x;
+}
+
+// Test that a simple triangle shape 0,-1 to 0.5,1 to 1,-1
+// Gives expected results during double integration
+TEST (PolynomialCurve_TriangleShapeDoubleIntegrate)
+{
+ OptimizedPolynomialCurve curve;
+ AnimationCurve::Keyframe keys[3] = { AnimationCurve::Keyframe(0.0f, -1.0f), AnimationCurve::Keyframe(0.5f, 1.0f), AnimationCurve::Keyframe(1.0f, -1.0f) };
+ AnimationCurve editorCurve;
+ editorCurve.Assign(keys, keys + 3);
+
+ RecalculateSplineSlopeLinear(editorCurve);
+
+ curve.BuildOptimizedCurve(editorCurve, 1.0F);
+
+ CHECK_CLOSE(-1, curve.Evaluate(0), 0.0001F);
+ CHECK_CLOSE(0, curve.Evaluate(0.25F), 0.0001F);
+ CHECK_CLOSE(1.0F, curve.Evaluate(0.5F), 0.0001F);
+ CHECK_CLOSE(0.0F, curve.Evaluate(0.75F), 0.0001F);
+ CHECK_CLOSE(-1, curve.Evaluate(1), 0.0001F);
+
+ curve.DoubleIntegrate();
+
+ CHECK_CLOSE(TriangleShapeIntegralFirstHalf(0), EvaluateGravityIntegrated(curve, 0.0F , 1.0F), 0.0001F);
+ CHECK_CLOSE(TriangleShapeIntegralFirstHalf(0.25F), EvaluateGravityIntegrated(curve, 0.25F, 1.0F), 0.0001F);
+ CHECK_CLOSE(TriangleShapeIntegralFirstHalf(0.5F), EvaluateGravityIntegrated(curve, 0.5F , 1.0F), 0.0001F);
+ CHECK_CLOSE(-0.0208333, EvaluateGravityIntegrated(curve, 0.75F, 1.0F), 0.0001F);
+ CHECK_CLOSE(0, EvaluateGravityIntegrated(curve, 1.0F , 1.0F), 0.0001F);
+}
+
+void CompareIntegrateCurve (const AnimationCurve& curve, const OptimizedPolynomialCurve& integratedCurve)
+{
+ CHECK_CLOSE(0, integratedCurve.EvaluateIntegrated(0), 0.0001F);
+
+ int intervals = 100;
+
+ float sum = 0.0F;
+ for (int i=1;i<intervals;i++)
+ {
+ float t = (float)i / (float)intervals;
+ float dt = 1.0F / (float)intervals;
+
+ float avgValue = curve.Evaluate(t - dt * 0.5F);
+ sum += avgValue * dt;
+
+ float integratedValue = integratedCurve.EvaluateIntegrated(t);
+ CHECK_CLOSE(sum, integratedValue, 0.0001F);
+ }
+}
+
+void CompareDoubleIntegrateCurve (const AnimationCurve& curve, const OptimizedPolynomialCurve& integratedCurve)
+{
+ CHECK_CLOSE(0, integratedCurve.EvaluateIntegrated(0), 0.0001F);
+
+ int intervals = 1000;
+
+ float integratedSum = 0.0F;
+ float sum = 0.0F;
+ for (int i=1;i<intervals;i++)
+ {
+ float t = (float)i / (float)intervals;
+ float dt = 1.0F / (float)intervals;
+
+ float avgValue = curve.Evaluate(t - dt * 0.5F);
+ sum += avgValue * dt;
+ integratedSum += sum * dt;
+
+ float integratedValue = integratedCurve.EvaluateDoubleIntegrated(t);
+ CHECK_CLOSE(integratedSum, integratedValue, 0.001F);
+ }
+}
+
+void CompareIntegrateCurve (const AnimationCurve::Keyframe* keys, int size)
+{
+ AnimationCurve sourceCurve;
+ sourceCurve.Assign(keys, keys + size);
+
+ OptimizedPolynomialCurve curve;
+ AnimationCurve editorCurve;
+ editorCurve = sourceCurve;
+ curve.BuildOptimizedCurve(editorCurve, 1);
+ curve.Integrate();
+
+ CompareIntegrateCurve(sourceCurve, curve);
+}
+
+void CompareDoubleIntegrateCurve (const AnimationCurve::Keyframe* keys, int size)
+{
+ AnimationCurve sourceCurve;
+ sourceCurve.Assign(keys, keys + size);
+
+ OptimizedPolynomialCurve curve;
+ AnimationCurve editorCurve;
+ editorCurve = sourceCurve;
+ curve.BuildOptimizedCurve(editorCurve, 1);
+ curve.DoubleIntegrate();
+
+ CompareDoubleIntegrateCurve(sourceCurve, curve);
+}
+
+TEST (PolynomialCurve_TriangleCurve)
+{
+ AnimationCurve sourceCurve;
+ AnimationCurve::Keyframe keys[3] = { AnimationCurve::Keyframe(0.0f, 0.0f), AnimationCurve::Keyframe(0.5f, 1.0f), AnimationCurve::Keyframe(1.0f, 0.0f) };
+ sourceCurve.Assign(keys, keys + 3);
+ RecalculateSplineSlopeLinear(sourceCurve);
+
+ CompareIntegrateCurve(&sourceCurve.GetKey(0), sourceCurve.GetKeyCount());
+ CompareDoubleIntegrateCurve(&sourceCurve.GetKey(0), sourceCurve.GetKeyCount());
+}
+
+
+TEST (PolynomialCurve_LineCurve)
+{
+ AnimationCurve sourceCurve;
+ AnimationCurve::Keyframe keys[2] = { AnimationCurve::Keyframe(0.0f, 0.0f), AnimationCurve::Keyframe(1.0f, 1.0f) };
+ sourceCurve.Assign(keys, keys + 2);
+ RecalculateSplineSlopeLinear(sourceCurve);
+
+ CompareIntegrateCurve(&sourceCurve.GetKey(0), sourceCurve.GetKeyCount());
+ CompareDoubleIntegrateCurve(&sourceCurve.GetKey(0), sourceCurve.GetKeyCount());
+}
+}
+
+#endif
diff --git a/Runtime/Graphics/Polygon2D.cpp b/Runtime/Graphics/Polygon2D.cpp
new file mode 100644
index 0000000..9985d65
--- /dev/null
+++ b/Runtime/Graphics/Polygon2D.cpp
@@ -0,0 +1,145 @@
+#include "UnityPrefix.h"
+#include "Polygon2D.h"
+
+#if ENABLE_SPRITES
+#include "Runtime/Geometry/Intersection.h"
+#include "Runtime/Graphics/SpriteFrame.h"
+
+Polygon2D::Polygon2D()
+{
+ Reset();
+}
+
+void Polygon2D::SetPoints (const Vector2f* points, size_t count)
+{
+ m_Paths.resize(1, TPath(kMemPhysics));
+ m_Paths[0].clear();
+ m_Paths[0].assign(points, points + count);
+}
+
+void Polygon2D::SetPathCount (int pathCount)
+{
+ m_Paths.resize(pathCount);
+}
+
+void Polygon2D::SetPath (int index, const TPath& path)
+{
+ if (index == 0 && GetPathCount() == 0)
+ {
+ m_Paths.resize(1);
+ }
+ else if (index >= GetPathCount())
+ {
+ ErrorString("Failed setting path. Index is out of bounds.");
+ return;
+ }
+
+ m_Paths[index] = path;
+}
+
+void Polygon2D::CopyFrom (const Polygon2D& paths)
+{
+ const int pathCount = paths.GetPathCount ();
+ if (pathCount == 0)
+ {
+ m_Paths.clear ();
+ return;
+ }
+
+ // Transfer paths.
+ m_Paths.resize (pathCount);
+ for (int index = 0; index < pathCount; ++index)
+ m_Paths[index] = paths.GetPath (index);
+}
+
+void Polygon2D::GenerateFrom(Sprite* sprite, const Vector2f& offset, float detail, unsigned char alphaTolerance, bool holeDetection)
+{
+ m_Paths.clear();
+ sprite->GenerateOutline(detail, alphaTolerance, holeDetection, m_Paths, 0);
+
+ if (offset.x != 0.0f || offset.y != 0.0f)
+ {
+ for (TPaths::iterator pit = m_Paths.begin(); pit != m_Paths.end(); ++pit)
+ {
+ TPath& path = *pit;
+ for (TPath::iterator it = path.begin(); it != path.end(); ++it)
+ {
+ Vector2f& point = *it;
+ point += offset;
+ }
+ }
+ }
+}
+
+void Polygon2D::Reset()
+{
+ m_Paths.resize(1);
+ m_Paths[0].clear();
+ m_Paths[0].reserve(4);
+ m_Paths[0].push_back(Vector2f(-1, -1));
+ m_Paths[0].push_back(Vector2f(-1, 1));
+ m_Paths[0].push_back(Vector2f( 1, 1));
+ m_Paths[0].push_back(Vector2f( 1, -1));
+}
+
+bool Polygon2D::GetNearestPoint(const Vector2f& point, int& pathIndex, int& pointIndex, float& distance) const
+{
+ bool ret = false;
+ float sqrDist = std::numeric_limits<float>::max();
+
+ const int pathCount = m_Paths.size();
+ for (int i = 0; i < pathCount; ++i)
+ {
+ const Polygon2D::TPath& path = m_Paths[i];
+ const int pointCount = path.size();
+ for (int j = 0; j < pointCount; ++j)
+ {
+ const Vector2f& testPoint = path[j];
+ float d = SqrMagnitude(testPoint - point);
+ if (d < sqrDist)
+ {
+ sqrDist = d;
+ ret = true;
+
+ pathIndex = i;
+ pointIndex = j;
+ distance = SqrtImpl(d);
+ }
+ }
+ }
+
+ return ret;
+}
+
+bool Polygon2D::GetNearestEdge(const Vector2f& point, int& pathIndex, int& pointIndex0, int& pointIndex1, float& distance, bool loop) const
+{
+ bool ret = false;
+ float dist = std::numeric_limits<float>::max();
+
+ const int pathCount = m_Paths.size();
+ for (int i = 0; i < pathCount; ++i)
+ {
+ const Polygon2D::TPath& path = m_Paths[i];
+ const int pointCount = path.size();
+ const int edgeCount = loop ? pointCount : pointCount - 1;
+ for (int p0 = 0; p0 < edgeCount; ++p0)
+ {
+ int p1 = (p0 + 1) % pointCount;
+ float d = DistancePointLine<Vector2f>(point, path[p0], path[p1]);
+ if (d < dist)
+ {
+ dist = d;
+ ret = true;
+
+ pathIndex = i;
+ pointIndex0 = p0;
+ pointIndex1 = p1;
+ distance = d;
+ }
+ }
+ }
+
+ return ret;
+}
+
+#endif //ENABLE_SPRITES
diff --git a/Runtime/Graphics/Polygon2D.h b/Runtime/Graphics/Polygon2D.h
new file mode 100644
index 0000000..76af1cf
--- /dev/null
+++ b/Runtime/Graphics/Polygon2D.h
@@ -0,0 +1,66 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_SPRITES
+
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+
+class Sprite;
+
+class Polygon2D
+{
+public:
+ DECLARE_SERIALIZE_NO_PPTR (Polygon2D)
+
+ typedef dynamic_array<Vector2f> TPath;
+ typedef std::vector<TPath> TPaths;
+
+ Polygon2D();
+
+ bool IsEmpty() const { return (m_Paths.size() == 0 || m_Paths[0].size() < 3); }
+ void Reset();
+ void Clear() { m_Paths.clear(); }
+
+ // Simple API
+ void SetPoints (const Vector2f* points, size_t count);
+ const Vector2f* GetPoints() const { return (m_Paths.size() > 0) ? m_Paths[0].data() : NULL; }
+ size_t GetPointCount() const { return (m_Paths.size() > 0) ? m_Paths[0].size() : 0; }
+
+ // Advanced API
+ void SetPathCount(int pathCount);
+ int GetPathCount() const { return m_Paths.size(); }
+
+ void SetPath(int index, const TPath& path);
+ const TPath& GetPath(int index) const { return m_Paths[index]; }
+ TPath& GetPath(int index) { return m_Paths[index]; }
+
+ void CopyFrom (const Polygon2D& paths);
+
+ size_t GetTotalPointCount() const
+ {
+ size_t count = 0;
+ for (size_t i = 0; i < m_Paths.size(); ++i)
+ count += m_Paths[i].size();
+ return count;
+ }
+
+ // Generation
+ void GenerateFrom(Sprite* sprite, const Vector2f& offset, float detail, unsigned char alphaTolerance, bool holeDetection);
+
+ // Math
+ bool GetNearestPoint(const Vector2f& point, int& pathIndex, int& pointIndex, float& distance) const;
+ bool GetNearestEdge(const Vector2f& point, int& pathIndex, int& pointIndex0, int& pointIndex1, float& distance, bool loop) const;
+
+private:
+ TPaths m_Paths;
+};
+
+template<class TransferFunction>
+void Polygon2D::Transfer(TransferFunction& transfer)
+{
+ TRANSFER(m_Paths);
+}
+
+#endif //ENABLE_SPRITES
diff --git a/Runtime/Graphics/ProceduralCache.cpp b/Runtime/Graphics/ProceduralCache.cpp
new file mode 100644
index 0000000..9ae3176
--- /dev/null
+++ b/Runtime/Graphics/ProceduralCache.cpp
@@ -0,0 +1,154 @@
+#include "UnityPrefix.h"
+#include "ProceduralMaterial.h"
+#include "SubstanceSystem.h"
+#include "Runtime/Misc/CachingManager.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/Graphics/Image.h"
+
+#if ENABLE_SUBSTANCE && ENABLE_CACHING && !UNITY_EDITOR
+bool ProceduralMaterial::PreProcess(std::set<unsigned int>& cachedTextureIDs)
+{
+ if (m_LoadingBehavior!=ProceduralLoadingBehavior_Cache)
+ return false;
+
+ std::string cacheFolder = GetCacheFolder();
+ std::vector<string> fileNames;
+ CachingManager::ReadInfoFile(cacheFolder, NULL, &fileNames);
+ if (fileNames.size()==0)
+ return false;
+
+ bool success = true;
+ std::map<ProceduralTexture*, SubstanceTexture> cachedTextures;
+ for (PingedTextures::iterator it=m_PingedTextures.begin();it!=m_PingedTextures.end() ; ++it)
+ {
+ if (*it!=NULL)
+ {
+ std::string fileName;
+ if (ReadCachedTexture(fileName, cachedTextures, cacheFolder, **it))
+ {
+ cachedTextureIDs.insert((*it)->GetSubstanceBaseTextureUID());
+ }
+ else
+ {
+ success = false;
+ DeleteFileOrDirectory(fileName);
+ std::vector<string>::iterator it = std::find(fileNames.begin(), fileNames.end(), fileName);
+ if (it!=fileNames.end())
+ fileNames.erase(it);
+ }
+ }
+ }
+ GetSubstanceSystem().ForceSubstanceResults(cachedTextures);
+
+ if (fileNames.size()==0)
+ {
+ DeleteFileOrDirectory(cacheFolder);
+ return false;
+ }
+
+ time_t timestamp = CachingManager::GenerateTimeStamp();
+ CachingManager::WriteInfoFile(cacheFolder, fileNames, timestamp);
+ GetCachingManager().GetCurrentCache().UpdateTimestamp(cacheFolder, timestamp);
+
+ if (success)
+ m_LoadingBehavior = ProceduralLoadingBehavior_Generate;
+ return success;
+}
+
+void ProceduralMaterial::PostProcess(const std::map<ProceduralTexture*, SubstanceTexture>& textures, const std::set<unsigned int>& cachedTextureIDs)
+{
+ if (m_LoadingBehavior!=ProceduralLoadingBehavior_Cache)
+ return;
+ m_LoadingBehavior = ProceduralLoadingBehavior_Generate;
+
+ std::string cacheFolder = GetCacheFolder();
+ std::vector<string> fileNames;
+ CachingManager::ReadInfoFile(cacheFolder, NULL, &fileNames);
+ for (std::map<ProceduralTexture*, SubstanceTexture>::const_iterator it=textures.begin() ; it!=textures.end() ; ++it)
+ {
+ if (it->first!=NULL && cachedTextureIDs.find(it->first->GetSubstanceBaseTextureUID())==cachedTextureIDs.end())
+ {
+ std::string fileName;
+ bool result = WriteCachedTexture(fileName, cacheFolder, *it->first, it->second);
+ std::vector<string>::iterator it = std::find(fileNames.begin(), fileNames.end(), fileName);
+ if (result)
+ {
+ if (it==fileNames.end())
+ fileNames.push_back(fileName);
+ }
+ else
+ {
+ DeleteFileOrDirectory(fileName);
+ if (it!=fileNames.end())
+ fileNames.erase(it);
+ }
+ }
+ }
+ if (fileNames.size()==0)
+ {
+ DeleteFileOrDirectory(cacheFolder);
+ }
+ else
+ {
+ GetCachingManager().WriteInfoFile(cacheFolder, fileNames);
+ }
+}
+
+std::string ProceduralMaterial::GetCacheFolder() const
+{
+ std::string hash;
+ char hashValue[6];
+ for (int i=0 ; i<16 ; ++i)
+ {
+ snprintf(hashValue, 6, "%d", (int)m_Hash.hashData.bytes[i]);
+ hash += hashValue;
+ }
+ return GetCachingManager().GetCurrentCache().GetFolder(hash.c_str(), true);
+}
+
+std::string ProceduralMaterial::GetCacheFilename(const ProceduralTexture& texture) const
+{
+ char filename[24];
+ snprintf(filename, 24, "%u.cache", texture.GetSubstanceBaseTextureUID());
+ return filename;
+}
+
+bool ProceduralMaterial::ReadCachedTexture(string& fileName, std::map<ProceduralTexture*, SubstanceTexture>& cachedTextures, const std::string& folder, const ProceduralTexture& texture)
+{
+ fileName = folder+"/"+GetCacheFilename(texture);
+ File file;
+ if (!file.Open(fileName, File::kReadPermission, File::kSilentReturnOnOpenFail))
+ return false;
+ unsigned int infoSize = sizeof(SubstanceTexture);
+ unsigned int textureSize;
+ SubstanceTexture& data = cachedTextures[(ProceduralTexture*)&texture];
+ bool result = file.Read(&data, infoSize)
+ && file.Read(&textureSize, 4)
+ && (data.buffer=SubstanceSystem::OnMalloc(textureSize))!=NULL
+ && file.Read(data.buffer, textureSize);
+ file.Close();
+ return result;
+}
+
+bool ProceduralMaterial::WriteCachedTexture(string& fileName, const std::string& folder, const ProceduralTexture& texture, const SubstanceTexture& data)
+{
+ fileName = folder+"/"+GetCacheFilename(texture);
+ if (IsFileCreated(fileName) && texture.HasBeenUploaded())
+ return true;
+ unsigned int infoSize = sizeof(SubstanceTexture);
+ int mipLevels = data.mipmapCount;
+ if (mipLevels==0)
+ mipLevels = CalculateMipMapCount3D(data.level0Width, data.level0Height, 1);
+ unsigned int textureSize = CalculateMipMapOffset(data.level0Width, data.level0Height, texture.GetSubstanceFormat(), mipLevels+1);
+ if (!GetCachingManager().GetCurrentCache().FreeSpace(infoSize+4+textureSize))
+ return false;
+ File file;
+ if (!file.Open(fileName, File::kWritePermission, File::kSilentReturnOnOpenFail))
+ return false;
+ bool result = file.Write(&data, infoSize)
+ && file.Write(&textureSize, 4)
+ && file.Write(data.buffer, textureSize);
+ file.Close();
+ return result;
+}
+#endif
diff --git a/Runtime/Graphics/ProceduralLinker.cpp b/Runtime/Graphics/ProceduralLinker.cpp
new file mode 100644
index 0000000..402756d
--- /dev/null
+++ b/Runtime/Graphics/ProceduralLinker.cpp
@@ -0,0 +1,521 @@
+#include "UnityPrefix.h"
+#include "ProceduralMaterial.h"
+#include "SubstanceArchive.h"
+#include "SubstanceSystem.h"
+
+#if ENABLE_SUBSTANCE
+bool ProceduralShuffleOutputs (SubstanceLinkerHandle* linkerHandle, ProceduralMaterial::PingedTextures& textures, SubstanceInputs& inputs)
+{
+ for (ProceduralMaterial::PingedTextures::iterator it=textures.begin();it!=textures.end();++it)
+ {
+ unsigned int rI = 0, gI = 1, bI = 2, aI = 3;
+ ProceduralTexture* texture = *it;
+ ProceduralTexture* alpha = NULL;
+ SubstanceLinkerShuffle shuffle;
+ unsigned int baseOutputUid;
+ unsigned int shuffledOutputUid;
+
+ texture->SetSubstancePreShuffleUID(texture->GetSubstanceTextureUID());
+ texture->EnableFlag(ProceduralTexture::Flag_Binded, false);
+
+ if (texture->GetSubstanceFormat()==kTexFormatARGB32)
+ {
+ rI = 1;
+ gI = 2;
+ bI = 3;
+ aI = 0;
+ }
+
+ baseOutputUid = texture->GetSubstanceTextureUID();
+
+ // RGB normal map
+ if (texture->GetUsageMode() == kTexUsageNormalmapPlain)
+ {
+ shuffle.useLevels = 1;
+ shuffle.channels[rI].outputUID = baseOutputUid;
+ shuffle.channels[rI].channelIndex = 0;
+ shuffle.channels[rI].levelMin = 0.0f;
+ shuffle.channels[rI].levelMax = 1.0f;
+ shuffle.channels[gI].outputUID = baseOutputUid;
+ shuffle.channels[gI].channelIndex = 1;
+ shuffle.channels[gI].levelMin = 1.0f;
+ shuffle.channels[gI].levelMax = 0.0f;
+ shuffle.channels[bI].outputUID = baseOutputUid;
+ shuffle.channels[bI].channelIndex = 2;
+ shuffle.channels[bI].levelMin = 0.0f;
+ shuffle.channels[bI].levelMax = 1.0f;
+ shuffle.channels[aI].outputUID = baseOutputUid;
+ shuffle.channels[aI].channelIndex = 3;
+ shuffle.channels[aI].levelMin = 0.0f;
+ shuffle.channels[aI].levelMax = 1.0f;
+ }
+ // platform uses optimized normal map (Z reconstruction)
+ else if (texture->GetUsageMode() == kTexUsageNormalmapDXT5nm)
+ {
+ shuffle.useLevels = 1;
+ shuffle.channels[rI].outputUID = baseOutputUid;
+ shuffle.channels[rI].channelIndex = 1;
+ shuffle.channels[rI].levelMin = 1.0f;
+ shuffle.channels[rI].levelMax = 0.0f;
+ shuffle.channels[gI].outputUID = baseOutputUid;
+ shuffle.channels[gI].channelIndex = 1;
+ shuffle.channels[gI].levelMin = 1.0f;
+ shuffle.channels[gI].levelMax = 0.0f;
+ shuffle.channels[bI].outputUID = baseOutputUid;
+ shuffle.channels[bI].channelIndex = 1;
+ shuffle.channels[bI].levelMin = 1.0f;
+ shuffle.channels[bI].levelMax = 0.0f;
+ shuffle.channels[aI].outputUID = baseOutputUid;
+ shuffle.channels[aI].channelIndex = 0;
+ shuffle.channels[aI].levelMin = 0.0f;
+ shuffle.channels[aI].levelMax = 1.0f;
+ }
+ else
+ {
+ shuffle.useLevels = 0;
+ shuffle.channels[rI].outputUID = baseOutputUid;
+ shuffle.channels[rI].channelIndex = 0;
+ shuffle.channels[gI].outputUID = baseOutputUid;
+ shuffle.channels[gI].channelIndex = 1;
+ shuffle.channels[bI].outputUID = baseOutputUid;
+ shuffle.channels[bI].channelIndex = 2;
+ shuffle.channels[aI].outputUID = baseOutputUid;
+ shuffle.channels[aI].channelIndex = texture->GetType()==Substance_OType_Specular?0:3;
+
+ if (texture->GetAlphaSource()!=Substance_OType_Unknown)
+ {
+ for (ProceduralMaterial::PingedTextures::iterator i=textures.begin();i!=textures.end();++i)
+ {
+ ProceduralTexture* alphaTexture = *i;
+ if (alphaTexture->GetType() == texture->GetAlphaSource())
+ {
+ alpha = alphaTexture;
+ shuffle.channels[aI].outputUID = alphaTexture->GetSubstanceTextureUID();
+ shuffle.channels[aI].channelIndex = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ unsigned int substance_format;
+ switch( texture->GetSubstanceFormat() )
+ {
+ case kTexFormatDXT5:
+ substance_format = Substance_PF_DXT5;
+ break;
+ case kTexFormatETC_RGB4:
+ substance_format = Substance_PF_ETC1;
+ break;
+ case kTexFormatPVRTC_RGBA4:
+ substance_format = Substance_PF_PVRTC4;
+ break;
+ default:
+ substance_format = Substance_PF_RGBA;
+ }
+ if (substanceLinkerHandleCreateOutput(linkerHandle, &shuffledOutputUid,
+ substance_format, 0, Substance_Linker_Flip_Vertical, &shuffle)!=0)
+ {
+ return false;
+ }
+
+ texture->SetSubstanceShuffledUID(shuffledOutputUid);
+
+ for (SubstanceInputs::iterator i=inputs.begin();i!=inputs.end();++i)
+ {
+ // look if main texture is used
+ {
+ std::set<unsigned int>::iterator ai = std::find(
+ i->alteredTexturesUID.begin(), i->alteredTexturesUID.end(), texture->GetSubstanceBaseTextureUID());
+ if (ai!=i->alteredTexturesUID.end())
+ {
+ texture->EnableFlag(ProceduralTexture::Flag_Binded, true);
+ }
+ }
+
+ // look if alpha texture is used
+ if (alpha!=NULL)
+ {
+ std::set<unsigned int>::iterator ai = std::find(
+ i->alteredTexturesUID.begin(), i->alteredTexturesUID.end(), alpha->GetSubstanceBaseTextureUID());
+ if (ai!=i->alteredTexturesUID.end())
+ {
+ i->alteredTexturesUID.insert(texture->GetSubstanceBaseTextureUID());
+ texture->EnableFlag(ProceduralTexture::Flag_Binded, true);
+ }
+ }
+ }
+ }
+ return true;
+}
+
+ProceduralMaterial* ProceduralMaterial::m_PackedSubstance = NULL;
+
+void SUBSTANCE_CALLBACK ProceduralHandleUIDConflict(SubstanceLinkerHandle* handle, SubstanceLinkerUIDCollisionType conflict, unsigned int previousUID, unsigned int newUID)
+{
+ SubstanceInputs& inputs = ProceduralMaterial::m_PackedSubstance->GetSubstanceInputs();
+ ProceduralMaterial::PingedTextures& textures = ProceduralMaterial::m_PackedSubstance->GetPingedTextures();
+ if (conflict==Substance_Linker_UIDCollision_Input)
+ {
+ for (SubstanceInputs::iterator it=inputs.begin() ; it!=inputs.end() ; ++it)
+ {
+ if (previousUID==it->internalIdentifier)
+ {
+ it->shuffledIdentifier = newUID;
+ break;
+ }
+ }
+ }
+ else if (conflict==Substance_Linker_UIDCollision_Output)
+ {
+ for (ProceduralMaterial::PingedTextures::iterator it=textures.begin() ; it!=textures.end() ; ++it)
+ {
+ ProceduralTexture* texture = *it;
+ if (texture!=NULL && previousUID==texture->GetSubstanceBaseTextureUID())
+ {
+ texture->SetSubstanceShuffledUID(newUID);
+ break;
+ }
+ }
+ }
+}
+
+void ProceduralMaterial::PackSubstances(std::vector<ProceduralMaterial*>& materials)
+{
+ if (materials.size()==0)
+ return;
+
+ // Create linker context
+ SubstanceEngineIDEnum currentEngine(GetSubstanceEngineID());
+ SubstanceLinkerContext* linkerContext;
+ SubstanceLinkerHandle* linkerHandle;
+ if (substanceLinkerContextInit(&linkerContext, SUBSTANCE_LINKER_API_VERSION, currentEngine)!=0
+ || substanceLinkerContextSetCallback(linkerContext, Substance_Linker_Callback_UIDCollision, (void*)&ProceduralHandleUIDConflict)!=0
+ || substanceLinkerHandleInit(&linkerHandle, linkerContext)!=0)
+ {
+ ErrorString("Failed to initialize substance linker");
+ for (std::vector<ProceduralMaterial*>::iterator it=materials.begin();it!=materials.end();++it)
+ (*it)->EnableFlag(Flag_Broken);
+ return;
+ }
+
+ // Make the list of materials to pack
+ std::vector<ProceduralMaterial*> packedMaterials;
+ packedMaterials.reserve(materials.size());
+
+ // Try to push all substances assembly
+ for (std::vector<ProceduralMaterial*>::iterator it=materials.begin();it!=materials.end();++it)
+ {
+ if ((*it)->IsFlagEnabled(Flag_Broken))
+ continue;
+
+ SubstanceArchive* package = (*it)->m_PingedPackage;
+ Assert(package!=NULL);
+
+ unsigned size = package->GetBufferSize();
+ UInt8* bufferData = package->GetBufferData();
+
+ m_PackedSubstance = *it;
+
+ // Prepare texture UIDs
+ {
+ for (ProceduralMaterial::PingedTextures::iterator i=m_PackedSubstance->m_PingedTextures.begin();i!=m_PackedSubstance->m_PingedTextures.end();++i)
+ (*i)->SetSubstanceShuffledUID((*i)->GetSubstanceBaseTextureUID());
+ }
+
+ // Prepare input UIDs
+ {
+ // Check if it's the old format
+ if (m_PackedSubstance->m_Inputs.size()>0
+ && m_PackedSubstance->m_Inputs[0].internalIdentifier==0)
+ {
+ SubstanceLinkerContext *context;
+ SubstanceLinkerHandle* linkHandle;
+ size_t substanceDataSize = 0;
+ UInt8* buffer = NULL;
+ if (substanceLinkerContextInit(&context, SUBSTANCE_LINKER_API_VERSION, currentEngine)==0
+ && substanceLinkerHandleInit(&linkHandle, context)==0
+ && substanceLinkerHandlePushAssemblyMemory(linkHandle, (const char*)bufferData, size)==0
+ && substanceLinkerHandleLink(linkHandle, (const unsigned char**)&buffer, &substanceDataSize)==0)
+ {
+ SubstanceHandle* substanceHandle = NULL;
+ if (substanceHandleInit( &substanceHandle, GetSubstanceSystem().GetContext(), buffer, substanceDataSize, NULL )==0)
+ {
+ for (SubstanceInputs::iterator i=m_PackedSubstance->m_Inputs.begin();i!=m_PackedSubstance->m_Inputs.end();++i)
+ {
+ SubstanceInputDesc inputDescription;
+ if (substanceHandleGetInputDesc(substanceHandle, i->internalIndex, &inputDescription)==0)
+ i->internalIdentifier = inputDescription.inputId;
+ }
+ substanceHandleRelease(substanceHandle);
+ }
+
+ substanceLinkerHandleRelease(linkHandle);
+ substanceLinkerContextRelease(context);
+ }
+ else
+ {
+ ErrorStringObject("Failed to initialize substance linker", m_PackedSubstance);
+ m_PackedSubstance->EnableFlag(Flag_Broken);
+ continue;
+ }
+ }
+
+ // Prepare shuffled ID
+ for (SubstanceInputs::iterator i=m_PackedSubstance->m_Inputs.begin();i!=m_PackedSubstance->m_Inputs.end();++i)
+ {
+ i->shuffledIdentifier = i->internalIdentifier;
+ }
+ }
+
+ // Push assembly if it has not already been pushed
+ // If we're creating another instance of a graph that has already been pushed, then we still need to push the SBSASM,
+ // otherwise we'll just get copies of the previous instance's outputs.
+ if ((!package->m_isPushed) || (package->m_generatedGraphs.count(m_PackedSubstance->m_PrototypeName) == 1))
+ {
+ if (substanceLinkerHandlePushAssemblyMemory(linkerHandle, (const char*)bufferData, size)!=0)
+ {
+ ErrorStringObject("Failed to pack substance (substanceLinkerHandlePushAssemblyMemory)", *it);
+ m_PackedSubstance->EnableFlag(Flag_Broken);
+ continue;
+ }
+ package->m_isPushed = true;
+ }
+ package->m_generatedGraphs.insert(m_PackedSubstance->m_PrototypeName);
+
+ // Set output formats & create shuffled outputs if needed
+ if (!ProceduralShuffleOutputs(linkerHandle, (*it)->m_PingedTextures, (*it)->m_Inputs))
+ {
+ ErrorStringObject("Failed to pack substance (ShuffleOutputs)", *it);
+ m_PackedSubstance->EnableFlag(Flag_Broken);
+ continue;
+ }
+
+ // Set inputs constness
+ #if !UNITY_EDITOR
+ if ((*it)->IsFlagEnabled(Flag_ConstSize))
+ {
+ SubstanceInputs& inputs = (*it)->m_Inputs;
+ bool broken=false;
+ for (SubstanceInputs::iterator i=inputs.begin();i!=inputs.end();++i)
+ {
+ if (i->name=="$outputsize" || i->name=="$randomseed")
+ {
+ SubstanceLinkerInputValue value;
+ value.integer[0] = (int)i->value.scalar[0];
+ value.integer[1] = (int)i->value.scalar[1];
+ value.integer[2] = (int)i->value.scalar[2];
+ value.integer[3] = (int)i->value.scalar[3];
+ if (substanceLinkerConstifyInput(linkerHandle, i->shuffledIdentifier, &value)!=0)
+ break;
+ }
+ }
+ if (broken)
+ {
+ ErrorStringObject("Failed to pack substance (substanceLinkerConstifyInput)", *it);
+ m_PackedSubstance->EnableFlag(Flag_Broken);
+ continue;
+ }
+ }
+ #endif
+
+ packedMaterials.push_back(*it);
+ }
+
+ // Select all used outputs
+ if (packedMaterials.size()>0 && substanceLinkerHandleSelectOutputs(linkerHandle, Substance_Linker_Select_UnselectAll, 0)!=0)
+ {
+ ErrorString("Failed to pack substances (substanceLinkerHandleSelectOutputs)");
+ for (int i=packedMaterials.size()-1;i>=0;--i)
+ packedMaterials[i]->EnableFlag(Flag_Broken);
+ return;
+ }
+ for (int i=packedMaterials.size()-1;i>=0;--i)
+ {
+ ProceduralMaterial* material = packedMaterials[i];
+ for (ProceduralMaterial::PingedTextures::iterator it=material->m_PingedTextures.begin();it!=material->m_PingedTextures.end();++it)
+ {
+ ProceduralTexture* texture = *it;
+ if (substanceLinkerHandleSelectOutputs(linkerHandle, Substance_Linker_Select_Select, texture->GetSubstancePreShuffleUID())!=0)
+ {
+ ErrorStringObject("Failed to pack substance (Substance_Linker_Select_Select)", material);
+ material->EnableFlag(Flag_Broken);
+ packedMaterials.erase(packedMaterials.begin()+i);
+ break;
+ }
+ }
+ }
+
+ // Nothing to pack ?
+ if (packedMaterials.size()==0)
+ {
+ substanceLinkerHandleRelease(linkerHandle);
+ substanceLinkerContextRelease(linkerContext);
+ return;
+ }
+
+ // Initialize substance handle
+ SubstanceData* pack = (SubstanceData*) UNITY_MALLOC_ALIGNED_NULL(kMemSubstance, sizeof(SubstanceData), 32);
+ if (!pack)
+ {
+ ErrorString("Could not allocate memory for Substance data (PackSubstances)");
+ for (int i=packedMaterials.size()-1;i>=0;--i)
+ packedMaterials[i]->EnableFlag(Flag_Broken);
+ return;
+ }
+
+ pack->substanceHandle = NULL;
+ pack->instanceCount = 0;
+ pack->memorySleepBudget = ProceduralCacheSize_None;
+ pack->memoryWorkBudget = ProceduralCacheSize_Tiny;
+
+
+ // Do we need to link the SBSASM?
+ // Only case where we DON'T need to link is when the three following conditions are met:
+ // - we are only linking a single substance
+ // - we are linking a clone
+ // - we have linked the same standalone substance clone once and cached the SBSBIN data
+ // If one of the above conditions is not met, we need to link.
+ size_t substanceDataSize = 0;
+ UInt8* buffer = NULL;
+ const ProceduralMaterial* currentMat = packedMaterials[0];
+ const UnityStr& prototypeName = currentMat->m_PrototypeName;
+ if (!((packedMaterials.size() == 1)
+ && (currentMat->IsFlagEnabled(Flag_Clone))
+ && (currentMat->m_PingedPackage->IsCloneDataAvailable(prototypeName))))
+ {
+ if (substanceLinkerHandleLink(linkerHandle, (const unsigned char**)&buffer, &substanceDataSize )!=0)
+ {
+ ErrorStringObject("Failed to pack substances (substanceLinkerHandleLink)", currentMat);
+ for (int i=packedMaterials.size()-1;i>=0;--i)
+ packedMaterials[i]->EnableFlag(Flag_Broken);
+ return;
+ }
+ }
+
+ // Handle the "cloning of a single substance" situation
+ if ((packedMaterials.size() == 1) && (currentMat->IsFlagEnabled(Flag_Clone)))
+ {
+ // In that case, we link and cache the SBSBIN inside the SubstancePackage.
+ // When the same substance is cloned again, this vanilla SBSBIN data is reused to avoid reallocating memory
+ SubstanceArchive* package = currentMat->m_PingedPackage;
+ if (package->IsCloneDataAvailable(prototypeName))
+ {
+ pack->substanceData = package->GetLinkedBinaryData(prototypeName);
+ }
+ else
+ {
+ if (package->SaveLinkedBinaryData(prototypeName, (const UInt8*) buffer, (const int) substanceDataSize))
+ {
+ pack->substanceData = package->GetLinkedBinaryData(prototypeName);
+ }
+ else
+ {
+ WarningStringMsg("Failed to save SBSBIN data for material %s", currentMat->GetName());
+ pack->substanceData = (UInt8*) UNITY_MALLOC_ALIGNED_NULL(kMemSubstance, substanceDataSize, 32);
+ if (pack->substanceData)
+ {
+ memcpy(pack->substanceData, buffer, substanceDataSize);
+ }
+ else
+ {
+ ErrorString("Could not allocate memory for Substance linked data");
+ for (int i=packedMaterials.size()-1;i>=0;--i)
+ packedMaterials[i]->EnableFlag(Flag_Broken);
+ return;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Either we're packing more than one substance, or this is not a clone, in
+ // which case the SBSBIN is not reusable because of constified inputs.
+ pack->substanceData = (UInt8*) UNITY_MALLOC_ALIGNED_NULL(kMemSubstance, substanceDataSize, 32);
+ if (pack->substanceData)
+ {
+ memcpy(pack->substanceData, buffer, substanceDataSize);
+ }
+ else
+ {
+ ErrorString("Could not allocate memory for Substance linked data");
+ for (int i=packedMaterials.size()-1;i>=0;--i)
+ packedMaterials[i]->EnableFlag(Flag_Broken);
+ return;
+ }
+ }
+
+ // Reset the isPushed flags and the list of generated graphs of the packages that have been pushed and linked
+ for (std::vector<ProceduralMaterial*>::iterator it=materials.begin();it!=materials.end();++it)
+ {
+ SubstanceArchive *package = (*it)->m_PingedPackage;
+ package->m_isPushed = false;
+ package->m_generatedGraphs.clear();
+ }
+
+ // Release linker
+ substanceLinkerHandleRelease(linkerHandle);
+ substanceLinkerContextRelease(linkerContext);
+
+ // Create substance handle
+ if (substanceHandleInit(&pack->substanceHandle, GetSubstanceSystem().GetContext(), pack->substanceData, substanceDataSize, NULL)!=0)
+ {
+ ErrorStringObject("Failed to pack substances (substanceHandleInit)", packedMaterials[0]);
+ for (int i=packedMaterials.size()-1;i>=0;--i)
+ packedMaterials[i]->EnableFlag(Flag_Broken);
+ return;
+ }
+
+ // Bind all substances
+ for (std::vector<ProceduralMaterial*>::iterator it=packedMaterials.begin();it!=packedMaterials.end();++it)
+ {
+ bool broken(false);
+
+ // Update input identifiers (todo: improve this if doable)
+ for (SubstanceInputs::iterator i=(*it)->m_Inputs.begin();i!=(*it)->m_Inputs.end();++i)
+ {
+ i->internalIndex = -1;
+ int id=0;
+ SubstanceInputDesc inputDescription;
+ while (substanceHandleGetInputDesc(pack->substanceHandle, id, &inputDescription)==0)
+ {
+ if (i->shuffledIdentifier==inputDescription.inputId)
+ {
+ i->internalIndex = id;
+ break;
+ }
+ ++id;
+ }
+ if (i->internalIndex==-1)
+ {
+#if !UNITY_EDITOR
+ // Don't break since the input may be removed
+ if ((*it)->IsFlagEnabled(Flag_ConstSize) && (i->name=="$outputsize" || i->name=="$randomseed"))
+ {
+ continue;
+ }
+#endif
+ broken = true;
+ }
+ }
+
+ if (broken)
+ {
+ ErrorStringObject("Failed to pack substances (SubstanceInputDesc)", *it);
+ (*it)->EnableFlag(Flag_Broken);
+ continue;
+ }
+
+ (*it)->m_SubstanceData = pack;
+ ++pack->instanceCount;
+ }
+
+ if (pack->instanceCount==0)
+ {
+ substanceHandleRelease(pack->substanceHandle);
+ UNITY_FREE(kMemSubstance, pack->substanceData);
+ UNITY_FREE(kMemSubstance, pack);
+ }
+}
+
+#endif // ENABLE_SUBSTANCE
diff --git a/Runtime/Graphics/ProceduralMaterial.cpp b/Runtime/Graphics/ProceduralMaterial.cpp
new file mode 100644
index 0000000..69b616a
--- /dev/null
+++ b/Runtime/Graphics/ProceduralMaterial.cpp
@@ -0,0 +1,1339 @@
+#include "UnityPrefix.h"
+#include "ProceduralMaterial.h"
+#include "SubstanceArchive.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Input/TimeManager.h"
+#include "SubstanceSystem.h"
+#include "Runtime/Graphics/Image.h"
+
+#if ENABLE_SUBSTANCE
+#include "Runtime/BaseClasses/IsPlaying.h"
+#endif
+
+#if UNITY_EDITOR
+#include "Editor/Src/AssetPipeline/SubstanceImporter.h"
+#include "Editor/Src/EditorUserBuildSettings.h"
+#endif
+
+size_t GetProceduralMemoryBudget(ProceduralCacheSize budget)
+{
+ switch(budget)
+ {
+ case ProceduralCacheSize_NoLimit:
+ return 0;
+ case ProceduralCacheSize_Heavy:
+ return 512 * 1024 * 1024;
+ case ProceduralCacheSize_Medium:
+ return 256 * 1024 * 1024;
+ case ProceduralCacheSize_Tiny:
+ return 128 * 1024 * 1024;
+ case ProceduralCacheSize_None:
+ default:
+ return 1;
+ }
+}
+
+Mutex ProceduralMaterial::m_InputMutex;
+
+IMPLEMENT_CLASS( ProceduralMaterial )
+IMPLEMENT_OBJECT_SERIALIZE( ProceduralMaterial )
+
+ProceduralMaterial::ProceduralMaterial( MemLabelId label, ObjectCreationMode mode ) :
+ Super( label, mode ),
+ m_SubstanceData( NULL ),
+ m_Flags( 0 ),
+ m_LoadingBehavior( ProceduralLoadingBehavior_Generate ),
+ m_Width( 9 ),
+ m_Height( 9 ),
+#if UNITY_EDITOR
+ m_isAlreadyLoadedInCurrentScene( false ),
+#endif
+ m_AnimationUpdateRate( 42 ), // 24fps
+ m_AnimationTime( 0.0f ),
+ m_PingedPackage( NULL ),
+ m_PrototypeName( "" )
+{
+#if ENABLE_SUBSTANCE
+ integrationTimeStamp = GetSubstanceSystem().integrationTimeStamp;
+#endif
+}
+
+ProceduralMaterial::~ProceduralMaterial()
+{
+#if ENABLE_SUBSTANCE
+
+ /////@TODO: This is an incredible hack. Waiting for another process to complete in the destructor is a big no no, especially when it involves a Thread::Sleep ()...
+ UnlockObjectCreation();
+ GetSubstanceSystem().NotifySubstanceDestruction(this);
+ LockObjectCreation();
+
+ Clean();
+
+ // Delete texture inputs (TODO: try to remove std::vector<Image*>)
+ for (std::vector<TextureInput>::iterator it=m_TextureInputs.begin();it!=m_TextureInputs.end();++it)
+ {
+ delete it->inputParameters;
+ delete it->image;
+
+ if (it->buffer)
+ {
+ UNITY_FREE(kMemSubstance, it->buffer);
+ }
+ }
+#endif
+}
+
+void ProceduralMaterial::Clean()
+{
+#if ENABLE_SUBSTANCE
+ if (m_SubstanceData!=NULL)
+ {
+ if (m_SubstanceData->instanceCount==1)
+ {
+ substanceHandleRelease(m_SubstanceData->substanceHandle);
+ // Clones do not delete their SBSBIN data, this is done in the destructor of the SubstanceArchive
+ // the clone was created from.
+ if (!IsFlagEnabled(Flag_Clone))
+ {
+ UNITY_FREE( kMemSubstance, m_SubstanceData->substanceData );
+ }
+ UNITY_FREE( kMemSubstance, m_SubstanceData );
+ }
+ else
+ {
+ --m_SubstanceData->instanceCount;
+ }
+ }
+ m_SubstanceData = NULL;
+ // Awake inputs
+ for (SubstanceInputs::iterator i=m_Inputs.begin();i != m_Inputs.end();i++)
+ {
+ i->EnableFlag(SubstanceInput::Flag_Awake, true);
+ }
+#endif
+}
+
+ProceduralMaterial* ProceduralMaterial::Clone()
+{
+ ProceduralMaterial* clone = CreateObjectFromCode<ProceduralMaterial>();
+ clone->m_SubstancePackage = m_SubstancePackage;
+ clone->m_PrototypeName = m_PrototypeName;
+ clone->m_Width = m_Width;
+ clone->m_Height = m_Height;
+ clone->m_Textures = m_Textures;
+ // Rename cloned textures
+ for (int idx=0 ; idx<m_Textures.size() ; ++idx)
+ {
+ clone->m_Textures[idx]->SetName(m_Textures[idx]->GetName());
+ }
+ clone->m_AnimationUpdateRate = m_AnimationUpdateRate;
+ clone->m_Inputs = m_Inputs;
+ clone->m_Flags = ((m_Flags | Flag_Clone | Flag_AwakeClone) & (~Flag_ConstSize));
+ clone->m_LoadingBehavior = m_LoadingBehavior;
+ clone->AwakeDependencies(false);
+ return clone;
+}
+
+void ProceduralMaterial::RebuildClone()
+{
+ EnableFlag(Flag_AwakeClone, false);
+
+ // Clone textures
+#if ENABLE_SUBSTANCE
+ if (!IsWorldPlaying() || m_LoadingBehavior!=ProceduralLoadingBehavior_BakeAndDiscard)
+ {
+ for (Textures::iterator it=m_Textures.begin();it!=m_Textures.end();++it)
+ {
+ *it = (*it)->Clone(this);
+ }
+
+ // Awake inputs
+ for (SubstanceInputs::iterator i=m_Inputs.begin();i != m_Inputs.end();i++)
+ {
+ i->EnableFlag(SubstanceInput::Flag_Awake, true);
+ }
+
+ AwakeDependencies(false);
+
+ // Force generation if required
+ if (m_LoadingBehavior==ProceduralLoadingBehavior_None)
+ EnableFlag(ProceduralMaterial::Flag_ForceGenerate);
+
+ GetSubstanceSystem().QueueLoading(this);
+ }
+#endif
+}
+
+#if UNITY_EDITOR
+void ProceduralMaterial::Init( SubstanceArchive& substancePackage, const UnityStr& prototypeName, const SubstanceInputs& inputs, const Textures& textures )
+{
+ Assert(m_SubstancePackage.GetInstanceID () == 0);
+
+ m_SubstancePackage = &substancePackage;
+ m_PrototypeName = prototypeName;
+ m_Inputs = inputs;
+ m_Textures = textures;
+
+ EnableFlag(Flag_ConstSize);
+
+ // Update Flag_Animated
+ EnableFlag(Flag_Animated, HasSubstanceProperty("$time"));
+}
+
+const char* ProceduralMaterial::GetSubstancePackageName()
+{
+ if (m_PingedPackage)
+ {
+ return m_PingedPackage->GetName();
+ }
+ else
+ {
+ return m_SubstancePackage->GetName();
+ }
+}
+
+#endif
+
+SubstanceHandle* ProceduralMaterial::GetSubstanceHandle()
+{
+ if (m_SubstanceData!=NULL)
+ return m_SubstanceData->substanceHandle;
+ return NULL;
+}
+
+void ProceduralMaterial::SetSize(int width, int height)
+{
+ m_Width = width;
+ m_Height = height;
+
+ Mutex::AutoLock locker(m_InputMutex);
+ SubstanceInput* input = FindSubstanceInput("$outputsize");
+ if (input!=NULL)
+ {
+ input->value.scalar[0] = (float)m_Width;
+ input->value.scalar[1] = (float)m_Height;
+ }
+}
+
+void ProceduralMaterial::AwakeFromLoadThreaded()
+{
+ Super::AwakeFromLoadThreaded();
+ AwakeDependencies(true);
+#if ENABLE_SUBSTANCE
+#if UNITY_EDITOR
+ EnableFlag(Flag_Awake);
+#endif
+ // Neither Baked (keep or discard) nor DoNothing substances must be generated at this time
+ ProceduralLoadingBehavior behavior = GetLoadingBehavior();
+ if (behavior != ProceduralLoadingBehavior_BakeAndKeep &&
+ behavior != ProceduralLoadingBehavior_BakeAndDiscard &&
+ behavior != ProceduralLoadingBehavior_None)
+ {
+ GetSubstanceSystem().QueueLoading(this);
+ }
+#endif
+}
+
+void ProceduralMaterial::AwakeFromLoad( AwakeFromLoadMode awakeMode )
+{
+ Super::AwakeFromLoad( awakeMode );
+
+ if ((awakeMode & kDidLoadThreaded)==0)
+ {
+ AwakeDependencies(false);
+
+#if ENABLE_SUBSTANCE
+
+#if UNITY_EDITOR
+ EnableFlag(Flag_Awake);
+ if (awakeMode!=kInstantiateOrCreateFromCodeAwakeFromLoad
+ && SubstanceImporter::OnLoadSubstance(*this)
+ && !(IsWorldPlaying() && m_LoadingBehavior==ProceduralLoadingBehavior_None))
+#else
+ // Don't link and render the DoNothing substances when loading them
+ // This is done when calling RebuildTextures instead
+ if (m_LoadingBehavior!=ProceduralLoadingBehavior_None)
+#endif
+ {
+ GetSubstanceSystem().QueueSubstance(this);
+ }
+
+#endif
+ }
+
+#if ENABLE_SUBSTANCE
+ GetSubstanceSystem().NotifySubstanceCreation(this);
+#endif
+}
+
+#if ENABLE_SUBSTANCE
+void ProceduralMaterial::ApplyInputs (bool& it_has_changed, bool asHint, std::set<unsigned int>& modifiedOutputsUID)
+{
+ int textureInputIndex(0);
+
+ // Handle input alteration
+ for (SubstanceInputs::iterator i=m_Inputs.begin();i != m_Inputs.end();i++)
+ {
+ SubstanceInput& input = *i;
+
+ // Skip const inputs at runtime
+#if !UNITY_EDITOR
+ if (IsFlagEnabled(Flag_ConstSize)
+ && (input.name=="$outputsize" || input.name=="$randomseed"))
+ {
+ continue;
+ }
+#endif
+
+ // Check texture instance hasn't changed, in the editor in case of texture changes
+#if UNITY_EDITOR
+ if (!input.IsFlagEnabled(SubstanceInput::Flag_Modified) && (input.internalType==Substance_IType_Image)
+ && textureInputIndex<m_TextureInputs.size())
+ {
+ Texture2D* checkTexture = dynamic_pptr_cast<Texture2D*>(
+ InstanceIDToObjectThreadSafe(input.value.texture.GetInstanceID()));
+ if (m_TextureInputs[textureInputIndex].texture!=checkTexture)
+ m_TextureInputs[textureInputIndex].texture = checkTexture;
+ }
+#endif
+
+ if (asHint)
+ {
+ // Push hint if needed
+ if (input.IsFlagEnabled(SubstanceInput::Flag_Modified) || input.IsFlagEnabled(SubstanceInput::Flag_Cached))
+ {
+ if (!input.IsFlagEnabled(SubstanceInput::Flag_SkipHint))
+ {
+ if (substanceHandlePushSetInput( m_SubstanceData->substanceHandle, Substance_PushOpt_HintOnly, input.internalIndex, input.internalType, 0, 0 )!=0)
+ ErrorStringObject("Failed to apply substance input as hint", this);
+ }
+ it_has_changed = true;
+ input.EnableFlag(SubstanceInput::Flag_Modified, false);
+ }
+
+ if (input.IsFlagEnabled(SubstanceInput::Flag_Awake))
+ {
+ it_has_changed = true;
+ input.EnableFlag(SubstanceInput::Flag_Awake, false);
+ }
+ }
+ else if (input.IsFlagEnabled(SubstanceInput::Flag_Modified) || input.IsFlagEnabled(SubstanceInput::Flag_Awake))
+ {
+ int error = 0;
+
+ // Apply Float values
+ if (IsSubstanceAnyFloatType(input.internalType))
+ {
+ error = substanceHandlePushSetInput( m_SubstanceData->substanceHandle, Substance_PushOpt_NotAHint, input.internalIndex, input.internalType, input.value.scalar, 0 );
+ }
+ // Apply integer values
+ else if (IsSubstanceAnyIntType(input.internalType))
+ {
+ int intValue[4];
+ intValue[0] = (int)input.value.scalar[0];
+ intValue[1] = (int)input.value.scalar[1];
+ intValue[2] = (int)input.value.scalar[2];
+ intValue[3] = (int)input.value.scalar[3];
+ error = substanceHandlePushSetInput( m_SubstanceData->substanceHandle, Substance_PushOpt_NotAHint, input.internalIndex, input.internalType, intValue, 0 );
+ }
+ // Apply image values
+ else if (input.internalType == Substance_IType_Image)
+ {
+ if (textureInputIndex>=m_TextureInputs.size())
+ {
+ ErrorStringObject("failed to apply substance input image", this);
+ }
+ else
+ {
+ error = substanceHandlePushSetInput( m_SubstanceData->substanceHandle, Substance_PushOpt_NotAHint, input.internalIndex, input.internalType, m_TextureInputs[textureInputIndex].inputParameters, 0 );
+ }
+ }
+ else
+ {
+ ErrorStringObject("unsupported substance input type", this);
+ }
+ if (error != 0)
+ ErrorStringObject("Failed to apply substance input", this);
+
+ // Add modified output
+ modifiedOutputsUID.insert(input.alteredTexturesUID.begin(), input.alteredTexturesUID.end());
+ }
+
+ // Keep the texture input index up to date
+ if (input.internalType == Substance_IType_Image)
+ {
+ ++textureInputIndex;
+ }
+ }
+}
+
+void ProceduralMaterial::ApplyOutputs (bool& it_has_changed, bool asHint, std::set<unsigned int>& modifiedOutputsUID, const std::set<unsigned int>& cachedTextureIDs)
+{
+ // Add the invalid outputs
+ if (!asHint)
+ {
+ for (PingedTextures::iterator it=m_PingedTextures.begin();it!=m_PingedTextures.end();++it)
+ {
+ if (*it!=NULL && !(*it)->IsValid())
+ {
+ modifiedOutputsUID.insert((*it)->GetSubstanceBaseTextureUID());
+ it_has_changed = true;
+ }
+ }
+ }
+
+ // Push outputs
+ unsigned int flags = Substance_OutOpt_TextureId | Substance_OutOpt_CopyNeeded | (asHint?Substance_PushOpt_HintOnly:0);
+ GetSubstanceSystem ().processedTextures.clear();
+ std::vector<unsigned int> textureIDs;
+ for (PingedTextures::iterator i=m_PingedTextures.begin();i!=m_PingedTextures.end();++i)
+ {
+ ProceduralTexture* texture = *i;
+ if (texture!=NULL && modifiedOutputsUID.find(texture->GetSubstanceBaseTextureUID())!=modifiedOutputsUID.end()
+ && cachedTextureIDs.find(texture->GetSubstanceBaseTextureUID())==cachedTextureIDs.end())
+ {
+ GetSubstanceSystem ().processedTextures[texture->GetSubstanceTextureUID()] = texture;
+ textureIDs.push_back(texture->GetSubstanceTextureUID());
+ }
+ }
+
+ if (textureIDs.size()==0)
+ {
+ modifiedOutputsUID.clear();
+ it_has_changed = false;
+ }
+
+ if (textureIDs.size()>0 && substanceHandlePushOutputs( m_SubstanceData->substanceHandle, flags, &textureIDs[0], textureIDs.size(), 0 )!=0)
+ {
+ ErrorStringObject("Failed to apply substance texture outputs", this);
+ }
+}
+#endif
+
+void ProceduralMaterial::RebuildTextures()
+{
+ if (IsFlagEnabled(Flag_AwakeClone))
+ {
+ RebuildClone();
+ }
+#if ENABLE_SUBSTANCE
+ else if (!IsWorldPlaying() || m_LoadingBehavior!=ProceduralLoadingBehavior_BakeAndDiscard)
+ {
+ GetSubstanceSystem().QueueSubstance(this);
+ }
+#endif
+}
+
+void ProceduralMaterial::RebuildTexturesImmediately()
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD;
+ RebuildTextures();
+#if ENABLE_SUBSTANCE
+ GetSubstanceSystem().WaitFinished(this);
+#endif
+}
+
+void ProceduralMaterial::ReloadAll (bool unload, bool load)
+{
+#if ENABLE_SUBSTANCE
+ std::vector<SInt32> objects;
+ Object::FindAllDerivedObjects (ClassID (ProceduralMaterial), &objects);
+ std::sort(objects.begin(), objects.end());
+
+ if (objects.empty())
+ return;
+
+ GetSubstanceSystem().WaitFinished();
+ for (int i=0;i<objects.size ();i++)
+ {
+ ProceduralMaterial& mat = *PPtr<ProceduralMaterial> (objects[i]);
+ Textures& textures = mat.GetTextures();
+ for (Textures::iterator it=textures.begin() ; it!=textures.end() ; ++it)
+ {
+#if UNITY_EDITOR
+ (*it)->EnableFlag(ProceduralTexture::Flag_Cached, false);
+#endif
+ (*it)->Invalidate();
+ }
+#if UNITY_EDITOR
+ mat.EnableFlag(ProceduralMaterial::Flag_Awake);
+#endif
+ if (mat.m_LoadingBehavior==ProceduralLoadingBehavior_BakeAndDiscard || (unload && !load))
+ {
+ for (Textures::iterator it=textures.begin() ; it!=textures.end() ; ++it)
+ {
+ if (unload)
+ (*it)->UnloadFromGfxDevice(false);
+ if (load)
+ (*it)->UploadToGfxDevice();
+ }
+ }
+ else
+ {
+ mat.RebuildTextures ();
+ }
+ }
+ SubstanceSystem::Context context(ProceduralProcessorUsage_All);
+ GetSubstanceSystem().WaitFinished();
+#endif
+}
+
+template<class _class_>
+void AwakeProceduralObject(PPtr<_class_>& pptr, _class_*& object, bool awakeThreaded)
+{
+ if (awakeThreaded)
+ {
+ #if SUPPORT_THREADS
+ Assert (!Thread::CurrentThreadIsMainThread());
+ #endif
+
+ // Awake the object
+ object = dynamic_pptr_cast<_class_*> (InstanceIDToObjectThreadSafe(pptr.GetInstanceID()));
+ }
+ else
+ {
+ #if SUPPORT_THREADS
+ Assert (Thread::CurrentThreadIsMainThread());
+ #endif
+
+ // We can safely load it synchroneously
+ object = pptr;
+ }
+
+ // Clear the PPtr if the object no more exist (removed/deprecated)
+ if (object==NULL)
+ pptr.SetInstanceID(0);
+}
+
+void ProceduralMaterial::AwakeDependencies(bool awakeThreaded)
+{
+ // Simplest validity check
+ if (m_Textures.size()==0)
+ {
+ EnableFlag(Flag_Broken);
+ return;
+ }
+
+#if ENABLE_SUBSTANCE
+ // Awake package
+ AwakeProceduralObject(m_SubstancePackage, m_PingedPackage, awakeThreaded);
+ if (m_PingedPackage==NULL && m_LoadingBehavior!=ProceduralLoadingBehavior_BakeAndDiscard)
+ {
+ EnableFlag(Flag_Broken);
+ return;
+ }
+
+ // Awake texture inputs
+ unsigned int input_count(0);
+ for (SubstanceInputs::iterator it=m_Inputs.begin();it!=m_Inputs.end();++it)
+ {
+ if (it->internalType==Substance_IType_Image)
+ {
+ if (input_count>=m_TextureInputs.size())
+ {
+ m_TextureInputs.push_back( TextureInput() );
+ m_TextureInputs.back().inputParameters = new SubstanceTextureInput();
+ memset(m_TextureInputs.back().inputParameters, 0, sizeof(SubstanceTextureInput));
+ m_TextureInputs.back().image = new Image();
+ m_TextureInputs.back().buffer = NULL;
+ }
+
+ AwakeProceduralObject(it->value.texture, m_TextureInputs[input_count].texture, awakeThreaded);
+ ++input_count;
+ }
+ }
+#endif
+
+ // Awake textures
+ if (m_PingedTextures.size()!=m_Textures.size())
+ {
+ int size = m_PingedTextures.size();
+ m_PingedTextures.resize(m_Textures.size());
+ if (size<m_PingedTextures.size())
+ memset(&m_PingedTextures[size], 0, sizeof(ProceduralTexture*)*(m_Textures.size()-size));
+ }
+
+ int texture_index(0);
+ for (ProceduralMaterial::Textures::iterator i=m_Textures.begin();i!=m_Textures.end();++i,++texture_index)
+ {
+ AwakeProceduralObject(*i, m_PingedTextures[texture_index], awakeThreaded);
+ if (m_PingedTextures[texture_index]==NULL)
+ {
+ EnableFlag(Flag_Broken);
+ return;
+ }
+ m_PingedTextures[texture_index]->SetOwner(this);
+ }
+}
+
+#if ENABLE_SUBSTANCE
+
+bool ProceduralMaterial::ProcessTexturesThreaded(const std::map<ProceduralTexture*, SubstanceTexture>& textures)
+{
+ if (IsFlagEnabled(Flag_Broken))
+ return true;
+
+ GetSubstanceSystem().UpdateMemoryBudget();
+
+ std::set<unsigned int> cachedTextureIDs;
+#if ENABLE_CACHING && !UNITY_EDITOR
+ if (PreProcess(cachedTextureIDs))
+ {
+ return true;
+ }
+#else
+ // Force rebuild if it's cached and no input is modified
+ InvalidateIfCachedOrInvalidTextures();
+#endif
+
+ // Apply substance parameters (this is the exact ordering the engine needs)
+ std::set<unsigned int> modifiedOutputsUID;
+ bool it_has_changed(false);
+ // Apply inputs values
+ ApplyInputs (it_has_changed, false, modifiedOutputsUID);
+
+ // For readable textures, force the rebuild by pushing all output UIDs,
+ // even if no input was changed (Bnecessary for a clean workflow for case 538383 / GetPixels32())
+ if (IsFlagEnabled(Flag_Readable))
+ {
+ for (PingedTextures::iterator i=m_PingedTextures.begin() ; i!=m_PingedTextures.end() ; ++i)
+ {
+ ProceduralTexture* texture = *i;
+ modifiedOutputsUID.insert(texture->GetSubstanceBaseTextureUID());
+ }
+ }
+
+ // Apply outputs uid
+ ApplyOutputs(it_has_changed, false, modifiedOutputsUID, cachedTextureIDs);
+ // Apply input hints
+ ApplyInputs (it_has_changed, true, modifiedOutputsUID);
+ // Apply outputs hints
+ ApplyOutputs(it_has_changed, true, modifiedOutputsUID, cachedTextureIDs);
+
+ if (!IsFlagEnabled(Flag_Readable) && !it_has_changed)
+ {
+ // Flush render list
+ substanceHandleFlush( m_SubstanceData->substanceHandle );
+ return false;
+ }
+
+ if (substanceHandleStart( m_SubstanceData->substanceHandle, Substance_Sync_Synchronous )!=0)
+ ErrorStringObject("Failed to start substance computation", this);
+
+ // Flush render list
+ substanceHandleFlush( m_SubstanceData->substanceHandle );
+
+#if ENABLE_CACHING && !UNITY_EDITOR
+ PostProcess(textures, cachedTextureIDs);
+#endif
+ return true;
+}
+
+void ProceduralMaterial::ApplyTextureInput (int substanceInputIndex, const SubstanceTextureInput& requiredTextureInput)
+{
+ // Find which input needs update
+ bool found(false);
+ int textureInputIndex(0);
+ SubstanceInput* input;
+ for (SubstanceInputs::iterator i=m_Inputs.begin();i != m_Inputs.end();i++)
+ {
+ input = &*i;
+ if (input->internalIndex==substanceInputIndex)
+ {
+ found = true;
+ break;
+ }
+ if (input->internalType==Substance_IType_Image)
+ {
+ ++textureInputIndex;
+ }
+ }
+ if (!found || textureInputIndex>=m_TextureInputs.size())
+ {
+ ErrorStringObject("Failed to push Substance texture input", this);
+ return;
+ }
+
+ // Check format
+ TextureFormat format;
+ bool need16bitsConvert(false);
+ if (requiredTextureInput.pixelFormat==Substance_PF_RGBA)
+ format = kTexFormatRGBA32;
+ else if (requiredTextureInput.pixelFormat==Substance_PF_RGB)
+ format = kTexFormatRGB24;
+ else if (requiredTextureInput.pixelFormat==(Substance_PF_16b | Substance_PF_RGBA))
+ {
+ format = kTexFormatRGBA32;
+ need16bitsConvert = true;
+ }
+ else if (requiredTextureInput.pixelFormat==(Substance_PF_16b | Substance_PF_RGB))
+ {
+ format = kTexFormatRGB24;
+ need16bitsConvert = true;
+ }
+ else if (requiredTextureInput.pixelFormat==Substance_PF_DXT1)
+ format = kTexFormatDXT1;
+ else if (requiredTextureInput.pixelFormat==Substance_PF_DXT3)
+ format = kTexFormatDXT3;
+ else if (requiredTextureInput.pixelFormat==Substance_PF_DXT5)
+ format = kTexFormatDXT5;
+ else if (requiredTextureInput.pixelFormat==Substance_PF_L)
+ format = kTexFormatAlpha8;
+ else
+ {
+ ErrorStringObject("Failed to push Substance texture input : unsupported format", this);
+ return;
+ }
+
+ if (m_TextureInputs.size()<=textureInputIndex)
+ {
+ ErrorStringObject("Failed to push Substance texture input : unexpected error", this);
+ return;
+ }
+
+ // Initialize the texture input if required
+ TextureInput& textureInput = m_TextureInputs[textureInputIndex];
+ if (textureInput.inputParameters->level0Width!=requiredTextureInput.level0Width
+ || textureInput.inputParameters->level0Height!=requiredTextureInput.level0Height
+ || textureInput.inputParameters->pixelFormat!=requiredTextureInput.pixelFormat)
+ {
+ // Fill format description
+ memcpy(textureInput.inputParameters, &requiredTextureInput, sizeof(SubstanceTextureInput));
+ textureInput.inputParameters->mTexture.level0Width = textureInput.inputParameters->level0Width;
+ textureInput.inputParameters->mTexture.level0Height = textureInput.inputParameters->level0Height;
+ textureInput.inputParameters->mTexture.mipmapCount = textureInput.inputParameters->mipmapCount;
+ textureInput.inputParameters->mTexture.pixelFormat = textureInput.inputParameters->pixelFormat;
+ textureInput.inputParameters->mTexture.channelsOrder = 0;
+ textureInput.image->SetImage(textureInput.inputParameters->level0Width, textureInput.inputParameters->level0Height, format, true);
+
+ if (textureInput.buffer!=NULL)
+ {
+ UNITY_FREE(kMemSubstance, textureInput.buffer);
+ textureInput.buffer = NULL;
+ }
+
+ if (need16bitsConvert)
+ {
+ size_t pitch = ((requiredTextureInput.pixelFormat | Substance_PF_RGBA)?4:3)*sizeof(unsigned short);
+ textureInput.buffer = UNITY_MALLOC_ALIGNED_NULL(kMemSubstance, pitch*textureInput.inputParameters->level0Width*textureInput.inputParameters->level0Height, 16);
+ if (!textureInput.buffer)
+ {
+ ErrorString("Could not allocate memory for textureInput.buffer (ApplyTextureInput)");
+ return;
+ }
+ textureInput.inputParameters->mTexture.buffer = textureInput.buffer;
+ }
+ else
+ {
+ textureInput.inputParameters->mTexture.buffer = textureInput.image->GetImageData();
+ }
+ }
+
+ Texture2D* texture = textureInput.texture;
+ if (texture==NULL)
+ {
+ // Default to white texture
+ textureInput.image->ClearImage(ColorRGBAf(1.0f, 1.0f, 1.0f, 1.0f));
+ }
+ else
+ {
+ // Try to retrieve current texture
+ if (texture->ExtractImage(textureInput.image))
+ {
+ textureInput.image->FlipImageY();
+ }
+ else
+ {
+ ErrorStringObject("Incorrect ProceduralMaterial input", this);
+ if (texture->GetRawImageData()==NULL)
+ {
+ ErrorStringObject("ProceduralMaterial: Unexpected error (Texture input is not in RAM), try a reimport", texture);
+ }
+ else
+ {
+ ErrorStringObject("ProceduralMaterial: Texture input is compressed in undecompressable format, you should switch it to RAW, then reimport the material", texture);
+ }
+
+ textureInput.image->ClearImage(ColorRGBAf(1.0f, 0.0f, 0.0f, 1.0f));
+ }
+ }
+
+ // Actually we can't force the input texture format using the Substance API,
+ // so here it requires 8bits -> 16bits conversion.
+ if (need16bitsConvert)
+ {
+ size_t pitch = (requiredTextureInput.pixelFormat | Substance_PF_RGBA)?4:3;
+ unsigned short * output = (unsigned short*)textureInput.buffer;
+ unsigned char * input = textureInput.image->GetImageData();
+ unsigned char * inputEnd = input + pitch*textureInput.image->GetWidth()*textureInput.image->GetHeight();
+ while(input!=inputEnd)
+ {
+ *(output++) = (unsigned short)*(input++)*257;
+ }
+ }
+}
+#endif
+
+void ProceduralMaterial::UpdateAnimation(float time)
+{
+ if (m_AnimationUpdateRate>0)
+ {
+ if (time<m_AnimationTime
+ || time>m_AnimationTime+m_AnimationUpdateRate/1000.0f)
+ {
+ m_AnimationTime = time;
+ SetSubstanceFloat("$time", time);
+ RebuildTextures();
+ }
+ }
+}
+
+std::vector<std::string> ProceduralMaterial::GetSubstanceProperties() const
+{
+ std::vector<std::string> properties;
+ for (SubstanceInputs::const_iterator i=m_Inputs.begin();i!=m_Inputs.end();++i)
+ {
+ properties.push_back(i->name);
+ }
+ return properties;
+}
+
+bool ProceduralMaterial::HasSubstanceProperty( const std::string& inputName ) const
+{
+ Mutex::AutoLock locker(m_InputMutex);
+ return FindSubstanceInput(inputName)!=NULL;
+}
+
+bool ProceduralMaterial::GetSubstanceBoolean( const std::string& inputName ) const
+{
+ Mutex::AutoLock locker(m_InputMutex);
+ const SubstanceInput* input = FindSubstanceInput(inputName);
+ if (input!=NULL)
+ {
+ return input->value.scalar[0]>0.5f;
+ }
+ return false;
+}
+
+void ProceduralMaterial::SetSubstanceBoolean( const std::string& inputName, bool value )
+{
+ SetSubstanceFloat( inputName, value?1.0f:0.0f );
+}
+
+float ProceduralMaterial::GetSubstanceFloat( const std::string& inputName ) const
+{
+ Mutex::AutoLock locker(m_InputMutex);
+ const SubstanceInput* input = FindSubstanceInput(inputName);
+ if (input!=NULL)
+ return input->value.scalar[0];
+ return 0.0F;
+}
+
+void ProceduralMaterial::SetSubstanceFloat( const std::string& inputName, float value )
+{
+ SetSubstanceVector(inputName, Vector4f(value, 0.0f, 0.0f, 0.0f));
+}
+
+Vector4f ProceduralMaterial::GetSubstanceVector( const std::string& inputName ) const
+{
+ Mutex::AutoLock locker(m_InputMutex);
+ const SubstanceInput* input = FindSubstanceInput(inputName);
+ if (input!=NULL)
+ {
+ Vector4f value;
+ value.Set(input->value.scalar);
+ return value;
+ }
+ return Vector4f(0.0F, 0.0F, 0.0F, 0.0F);
+}
+
+void ProceduralMaterial::SetSubstanceVector( const std::string& inputName, const Vector4f& value )
+{
+ Mutex::AutoLock locker(m_InputMutex);
+ const SubstanceInput* input = FindSubstanceInput(inputName);
+ if (input!=NULL)
+ {
+ SubstanceValue inputValue;
+ memcpy(inputValue.scalar, value.GetPtr(), sizeof(float)*4);
+ SetDirty();
+#if ENABLE_SUBSTANCE
+ GetSubstanceSystem().QueueInput(this, inputName, inputValue);
+#endif
+ }
+}
+
+ColorRGBAf ProceduralMaterial::GetSubstanceColor( const std::string& inputName ) const
+{
+ Mutex::AutoLock locker(m_InputMutex);
+ const SubstanceInput* input = FindSubstanceInput(inputName);
+ if (input!=NULL)
+ {
+ ColorRGBAf value;
+ memcpy(value.GetPtr(), input->value.scalar, sizeof(float)*4);
+ return value;
+ }
+
+ return ColorRGBAf(0.0F, 0.0F, 0.0F, 0.0F);
+}
+
+void ProceduralMaterial::SetSubstanceColor( const std::string& inputName, const ColorRGBAf& value )
+{
+ SetSubstanceVector(inputName, Vector4f(value.r, value.g, value.b, value.a));
+}
+
+int ProceduralMaterial::GetSubstanceEnum( const string& inputName )
+{
+ Mutex::AutoLock locker(m_InputMutex);
+ SubstanceInput* input = FindSubstanceInput(inputName);
+ if (input!=NULL)
+ {
+ int index(0);
+ for (std::vector<SubstanceEnumItem>::iterator it=input->enumValues.begin();it!=input->enumValues.end();++it)
+ {
+ if ((int)input->value.scalar[0]==it->value)
+ {
+ return index;
+ }
+
+ ++index;
+ }
+ }
+ return -1;
+}
+
+void ProceduralMaterial::SetSubstanceEnum( const string& inputName, int value )
+{
+ Mutex::AutoLock locker(m_InputMutex);
+ SubstanceInput* input = FindSubstanceInput(inputName);
+ if (input!=NULL && value>=0 && value<input->enumValues.size())
+ {
+ SubstanceEnumItem& item = input->enumValues[value];
+ SubstanceValue inputValue;
+ inputValue.scalar[0] = (float)item.value;
+ SetDirty();
+#if ENABLE_SUBSTANCE
+ GetSubstanceSystem().QueueInput(this, inputName, inputValue);
+#endif
+ }
+}
+
+Texture2D* ProceduralMaterial::GetSubstanceTexture( const string& inputName ) const
+{
+ Mutex::AutoLock locker(m_InputMutex);
+ const SubstanceInput* input = FindSubstanceInput(inputName);
+ if (input!=NULL && input->internalType == Substance_IType_Image)
+ return input->value.texture;
+ return NULL;
+}
+
+void ProceduralMaterial::SetSubstanceTexture( const string& inputName, Texture2D* value )
+{
+ Mutex::AutoLock locker(m_InputMutex);
+ const SubstanceInput* input(NULL);
+ int texture_index(0);
+ for (SubstanceInputs::iterator i=m_Inputs.begin();i!=m_Inputs.end();++i)
+ {
+ if (i->name==inputName)
+ {
+ input = &*i;
+ break;
+ }
+ if (i->type==ProceduralPropertyType_Texture)
+ ++texture_index;
+ }
+
+ if (input!=NULL && input->type==ProceduralPropertyType_Texture
+ && texture_index<m_TextureInputs.size())
+ {
+ m_TextureInputs[texture_index].texture = value;
+ SubstanceValue inputValue;
+ inputValue.texture = value;
+ SetDirty();
+#if ENABLE_SUBSTANCE
+ GetSubstanceSystem().QueueInput(this, inputName, inputValue);
+#endif
+ }
+}
+
+#if ENABLE_SUBSTANCE
+void ProceduralMaterial::Callback_SetSubstanceInput( const string& inputName, SubstanceValue& inputValue)
+{
+ Mutex::AutoLock locker(m_InputMutex);
+ SubstanceInput* input = FindSubstanceInput(inputName);
+
+ //AreSubstanceInputValuesEqual(input->internalType, input->value, inputValue))
+ // Its up to the user to set value if it hasn't changed, he may want to really set the value
+ // even if it hasn't changed, to cache it for instance.
+ if (input==NULL)
+ return;
+
+ ClampSubstanceInputValues(*input, inputValue);
+
+ if (input->type==ProceduralPropertyType_Texture)
+ {
+ input->value.texture = inputValue.texture;
+ }
+ else
+ {
+ memcpy(input->value.scalar, inputValue.scalar,
+ sizeof(float)*GetRequiredInputComponentCount(input->internalType));
+ }
+
+ input->EnableFlag(SubstanceInput::Flag_Modified);
+
+#if !UNITY_EDITOR
+ if (IsFlagEnabled(Flag_ConstSize) && (input->name=="$outputsize" || input->name=="$randomseed"))
+ {
+ EnableFlag(Flag_ConstSize, false);
+ Clean();
+
+ // Initialize fresh substance data
+ std::vector<ProceduralMaterial*> materials;
+ materials.push_back(this);
+ PackSubstances(materials);
+ }
+#endif
+}
+#endif
+
+const SubstanceInput* ProceduralMaterial::FindSubstanceInput( const string& inputName ) const
+{
+ for (SubstanceInputs::const_iterator i=m_Inputs.begin();i!=m_Inputs.end();++i)
+ {
+ if (i->name==inputName)
+ return &*i;
+ }
+ return NULL;
+}
+
+SubstanceInput* ProceduralMaterial::FindSubstanceInput( const string& inputName )
+{
+ for (SubstanceInputs::iterator i=m_Inputs.begin();i!=m_Inputs.end();++i)
+ {
+ if (i->name==inputName)
+ return &*i;
+ }
+ return NULL;
+}
+
+bool ProceduralMaterial::IsSubstancePropertyCached( const string& inputName ) const
+{
+ const SubstanceInput* input = FindSubstanceInput(inputName);
+ if (input!=NULL)
+ return input->IsFlagEnabled(SubstanceInput::Flag_Cached);
+ return false;
+}
+
+void ProceduralMaterial::CacheSubstanceProperty( const string& inputName, bool value )
+{
+ SubstanceInput* input = FindSubstanceInput(inputName);
+ if (input!=NULL)
+ input->EnableFlag(SubstanceInput::Flag_Cached, value);
+}
+
+void ProceduralMaterial::ClearCache()
+{
+#if ENABLE_SUBSTANCE
+ for (SubstanceInputs::iterator it=m_Inputs.begin() ; it!=m_Inputs.end() ; ++it)
+ it->EnableFlag(SubstanceInput::Flag_Cached, false);
+ GetSubstanceSystem().QueryClearCache(this);
+#endif
+}
+
+void ProceduralMaterial::SetProceduralMemoryBudget(ProceduralCacheSize budget)
+{
+ SetProceduralMemorySleepBudget(budget);
+
+ switch(budget)
+ {
+ case ProceduralCacheSize_Tiny: SetProceduralMemoryWorkBudget(ProceduralCacheSize_Medium); break;
+ case ProceduralCacheSize_Medium: SetProceduralMemoryWorkBudget(ProceduralCacheSize_Heavy); break;
+ case ProceduralCacheSize_Heavy: SetProceduralMemoryWorkBudget(ProceduralCacheSize_NoLimit); break;
+ case ProceduralCacheSize_NoLimit: SetProceduralMemoryWorkBudget(ProceduralCacheSize_NoLimit); break;
+ default:
+ case ProceduralCacheSize_None: SetProceduralMemoryWorkBudget(ProceduralCacheSize_Tiny); break;
+ }
+}
+
+ProceduralCacheSize ProceduralMaterial::GetProceduralMemoryBudget() const
+{
+ if (m_SubstanceData==NULL)
+ return ProceduralCacheSize_None;
+ return m_SubstanceData->memoryWorkBudget;
+}
+
+void ProceduralMaterial::SetProceduralMemoryWorkBudget(ProceduralCacheSize budget)
+{
+ if (m_SubstanceData!=NULL)
+ m_SubstanceData->memoryWorkBudget = budget;
+}
+
+ProceduralCacheSize ProceduralMaterial::GetProceduralMemoryWorkBudget() const
+{
+ if (m_SubstanceData==NULL)
+ return ProceduralCacheSize_None;
+ return m_SubstanceData->memoryWorkBudget;
+}
+
+void ProceduralMaterial::SetProceduralMemorySleepBudget(ProceduralCacheSize budget)
+{
+ if (m_SubstanceData!=NULL)
+ m_SubstanceData->memorySleepBudget = budget;
+}
+
+ProceduralCacheSize ProceduralMaterial::GetProceduralMemorySleepBudget() const
+{
+ if (m_SubstanceData==NULL)
+ return ProceduralCacheSize_None;
+ return m_SubstanceData->memorySleepBudget;
+}
+
+#if UNITY_EDITOR
+// Strips substance data during serialization when building a player
+struct TemporarilyStripSubstanceData
+{
+ ProceduralMaterial* material;
+ PPtr<SubstanceArchive> substancePackage;
+ SubstanceInputs inputs;
+
+ TemporarilyStripSubstanceData (ProceduralMaterial& mat, bool isBuildingPlayer)
+ {
+ bool shouldDiscardSubstanceData;
+ shouldDiscardSubstanceData = !IsSubstanceSupportedOnPlatform(GetEditorUserBuildSettings().GetActiveBuildTarget());
+ shouldDiscardSubstanceData |= mat.GetLoadingBehavior() == ProceduralLoadingBehavior_BakeAndDiscard;
+
+ // Should we discard the substance data?
+ if (isBuildingPlayer && shouldDiscardSubstanceData)
+ {
+ material = &mat;
+
+ // Clear m_SubstancePackage & m_Inputs and back it up so we can revert them after serialization
+ swap(substancePackage, mat.m_SubstancePackage);
+ swap(inputs, mat.m_Inputs);
+ }
+ else
+ {
+ material = NULL;
+ }
+ }
+
+ ~TemporarilyStripSubstanceData ()
+ {
+ if (material != NULL)
+ {
+ swap(material->m_Inputs, inputs);
+ swap(material->m_SubstancePackage, substancePackage);
+ }
+ }
+};
+#endif
+
+
+
+template<class T> void ProceduralMaterial::Transfer( T& transfer )
+{
+ Super::Transfer( transfer );
+
+ // Serialize maximum sizes
+ if (transfer.IsVersionSmallerOrEqual (2))
+ {
+ int m_MaximumSize;
+ TRANSFER( m_MaximumSize );
+ m_Width = m_MaximumSize;
+ m_Height = m_MaximumSize;
+ }
+ else
+ {
+ TRANSFER( m_Width );
+ TRANSFER( m_Height );
+ }
+
+ TRANSFER( m_Textures );
+ TRANSFER( m_Flags );
+
+ // Serialize load behavior
+ if (transfer.IsReading ())
+ {
+ // Handle deprecated GenerateAtLoad flag, replaced by LoadingBehavior
+ m_LoadingBehavior = IsFlagEnabled(Flag_DeprecatedGenerateAtLoad)?ProceduralLoadingBehavior_Generate:ProceduralLoadingBehavior_None;
+ EnableFlag(Flag_DeprecatedGenerateAtLoad, false);
+ }
+ transfer.Transfer(reinterpret_cast<int&> (m_LoadingBehavior), "m_LoadingBehavior");
+
+ #if UNITY_EDITOR
+ // Strip unused data when building/collecting assets
+ TemporarilyStripSubstanceData stripData (*this, transfer.GetFlags () & kBuildPlayerOnlySerializeBuildProperties);
+ #endif
+
+ TRANSFER( m_SubstancePackage );
+ TRANSFER( m_Inputs );
+
+ TRANSFER( m_PrototypeName );
+ if (m_PrototypeName=="")
+ {
+ m_PrototypeName = GetName();
+ }
+
+ TRANSFER( m_AnimationUpdateRate );
+
+ TRANSFER(m_Hash);
+}
+
+bool ProceduralMaterial::IsProcessing() const
+{
+#if ENABLE_SUBSTANCE
+ return GetSubstanceSystem().IsSubstanceProcessing(this);
+#else
+ return false;
+#endif
+}
+
+void ProceduralMaterial::SetProceduralProcessorUsage(ProceduralProcessorUsage processorUsage)
+{
+#if ENABLE_SUBSTANCE
+ GetSubstanceSystem().SetProcessorUsage(processorUsage);
+#endif
+}
+
+ProceduralProcessorUsage ProceduralMaterial::GetProceduralProcessorUsage()
+{
+#if ENABLE_SUBSTANCE
+ return GetSubstanceSystem().GetProcessorUsage();
+#else
+ return ProceduralProcessorUsage_Unsupported;
+#endif
+}
+
+void ProceduralMaterial::StopProcessing()
+{
+#if ENABLE_SUBSTANCE
+ GetSubstanceSystem().ClearProcessingQueue();
+#endif
+}
+
+#if UNITY_EDITOR
+
+void ProceduralMaterial::InvalidateIfCachedOrInvalidTextures()
+{
+ // Check if some textures are cached
+ bool cachedOrInvalid=false;
+ for (PingedTextures::iterator i=m_PingedTextures.begin();i!=m_PingedTextures.end();++i)
+ {
+ ProceduralTexture* texture = *i;
+ if (texture!=NULL
+ && (texture->IsFlagEnabled(ProceduralTexture::Flag_Cached)
+ || !texture->IsValid()))
+ {
+ cachedOrInvalid = true;
+ break;
+ }
+ }
+
+ if (cachedOrInvalid)
+ {
+ // Check if an input has been modified
+ bool modified = false;
+ for (SubstanceInputs::iterator i=m_Inputs.begin();i != m_Inputs.end();++i)
+ {
+ SubstanceInput& input = *i;
+ if (input.IsFlagEnabled(SubstanceInput::Flag_Modified))
+ {
+ modified = true;
+ break;
+ }
+ }
+ // Force rebuild since no input is modified
+ if (!modified)
+ {
+ for (SubstanceInputs::iterator i=m_Inputs.begin();i != m_Inputs.end();++i)
+ {
+ SubstanceInput& input = *i;
+ input.EnableFlag(SubstanceInput::Flag_Awake);
+ }
+ }
+ }
+}
+
+bool IsSubstanceSupportedOnPlatform(BuildTargetPlatform platform)
+{
+return (platform == kBuildWebPlayerLZMA
+ || platform == kBuildWebPlayerLZMAStreamed
+ || platform == kBuildStandaloneOSXIntel
+ || platform == kBuildStandaloneOSXIntel64
+ || platform == kBuildStandaloneOSXUniversal
+ || platform == kBuildStandaloneWinPlayer
+ || platform == kBuildStandaloneWin64Player
+ || platform == kBuildStandaloneLinux
+ || platform == kBuildStandaloneLinux64
+ || platform == kBuildStandaloneLinuxUniversal
+ || platform == kBuild_Android
+ || platform == kBuild_iPhone
+ || platform == kBuildNaCl
+ );
+}
+#endif
+
+bool IsSubstanceSupported()
+{
+#if UNITY_EDITOR
+ BuildTargetPlatform platform = GetEditorUserBuildSettings().GetActiveBuildTarget();
+ return IsSubstanceSupportedOnPlatform(platform);
+#endif
+
+#if ENABLE_SUBSTANCE
+ return true;
+#else
+ return false;
+#endif
+}
+
+TextureFormat GetSubstanceTextureFormat(SubstanceOutputFormat outputFormat, bool requireCompressed)
+{
+#if ENABLE_SUBSTANCE
+#if UNITY_EDITOR
+ BuildTargetPlatform platform = GetEditorUserBuildSettings().GetActiveBuildTarget();
+ if (!requireCompressed || IsSubstanceSupportedOnPlatform(platform))
+#endif
+ {
+ TextureFormat format(kTexFormatRGBA32);
+
+ if (outputFormat==Substance_OFormat_Compressed)
+ {
+#if UNITY_IPHONE
+ format = kTexFormatPVRTC_RGBA4;
+#elif UNITY_ANDROID
+ if (gGraphicsCaps.supportsTextureFormat[kTexFormatDXT5])
+ {
+ format = kTexFormatDXT5;
+ }
+ else if (gGraphicsCaps.supportsTextureFormat[kTexFormatPVRTC_RGBA4])
+ {
+ format = kTexFormatPVRTC_RGBA4;
+ }
+ else
+ {
+ // Lowest common denominator = ETC
+ // But this will cancel the alpha, need to think of something else for non-DXT non-PVR platforms
+ // format = kTexFormatETC_RGB4;
+ }
+#else
+ format = kTexFormatDXT5;
+#endif
+ }
+
+ return format;
+ }
+#endif
+
+ return kTexFormatRGBA32;
+}
+
+SubstanceEngineIDEnum GetSubstanceEngineID()
+{
+#if defined(__ppc__)
+ return Substance_EngineID_xenos;
+#endif
+ return Substance_EngineID_sse2;
+}
diff --git a/Runtime/Graphics/ProceduralMaterial.h b/Runtime/Graphics/ProceduralMaterial.h
new file mode 100644
index 0000000..e008566
--- /dev/null
+++ b/Runtime/Graphics/ProceduralMaterial.h
@@ -0,0 +1,289 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Utilities/Hash128.h"
+#include "ProceduralTexture.h"
+#include "SubstanceInput.h"
+#include "External/Allegorithmic/builds/Engines/include/substance/handle.h"
+#include "External/Allegorithmic/builds/Engines/include/substance/linker/linker.h"
+
+class SubstanceArchive;
+class Texture2D;
+class Image;
+
+#if UNITY_EDITOR
+bool IsSubstanceSupportedOnPlatform(BuildTargetPlatform platform);
+#endif
+
+bool IsSubstanceSupported(); // return true if Substance is supported on current platform
+TextureFormat GetSubstanceTextureFormat(SubstanceOutputFormat outputFormat, bool requireCompressed=false); // return the required Substance texture format
+SubstanceEngineIDEnum GetSubstanceEngineID(); // return the required Substance engine ID
+
+// Global Substance processor usage
+enum ProceduralProcessorUsage
+{
+ ProceduralProcessorUsage_Unsupported = 0, // Substance isn't supported
+ ProceduralProcessorUsage_One, // uses only one CPU
+ ProceduralProcessorUsage_Half, // uses half of the CPU count (and not the first one if applicable)
+ ProceduralProcessorUsage_All // uses all the CPU
+};
+
+// Memory budget
+enum ProceduralCacheSize
+{
+ ProceduralCacheSize_Tiny = 0, // 128 Mb
+ ProceduralCacheSize_Medium, // 256 Mb
+ ProceduralCacheSize_Heavy, // 512 Mb
+ ProceduralCacheSize_NoLimit, // unlimited
+ ProceduralCacheSize_None // 1 byte
+};
+
+size_t GetProceduralMemoryBudget(ProceduralCacheSize budget);
+
+// Loading behavior
+enum ProceduralLoadingBehavior
+{
+ ProceduralLoadingBehavior_None = 0, // do nothing
+ ProceduralLoadingBehavior_Generate, // generate textures
+ ProceduralLoadingBehavior_BakeAndKeep, // use baked textures, the substance may be generated later on
+ ProceduralLoadingBehavior_BakeAndDiscard, // use baked textures, the substance data is removed from runtime
+ ProceduralLoadingBehavior_Cache // generate textures and cache it, then use it at runtime to speed-up the loading
+};
+
+/* A ProceduralMaterial is a dynamic material that use the Substance engine.
+ */
+
+class ProceduralMaterial : public Material
+{
+public: // NESTED TYPES
+
+ typedef std::vector<PPtr<ProceduralTexture> > Textures;
+ typedef std::vector<ProceduralTexture*> PingedTextures;
+
+public: // METHODS
+
+ REGISTER_DERIVED_CLASS( ProceduralMaterial, Material )
+ DECLARE_OBJECT_SERIALIZE( ProceduralMaterial )
+
+ ProceduralMaterial( MemLabelId label, ObjectCreationMode mode );
+
+ void Clean();
+
+ ProceduralMaterial* Clone();
+private:
+ void RebuildClone();
+
+public:
+#if UNITY_EDITOR
+ // Creates the material (one time call only from the importer)
+ void Init( SubstanceArchive& substancePackage, const UnityStr& prototypeName, const SubstanceInputs& inputs, const Textures& textures );
+ SubstanceArchive* GetSubstancePackage() { return m_PingedPackage; }
+ const char* GetSubstancePackageName();
+#endif
+ const Textures& GetTextures() const { return m_Textures; }
+ Textures& GetTextures() { return m_Textures; }
+ PingedTextures& GetPingedTextures() { return m_PingedTextures; }
+ SubstanceInputs& GetSubstanceInputs () { return m_Inputs; }
+ const SubstanceInputs& GetSubstanceInputs () const { return m_Inputs; }
+ const SubstanceArchive* GetSubstancePackage() const { return m_PingedPackage; }
+ SubstanceHandle* GetSubstanceHandle();
+
+ // Generation sizes accessors
+ void SetSize(int width, int height);
+ int GetWidth() const { return m_Width; }
+ int GetHeight() const { return m_Height; }
+
+ // Threaded loading, launch the generation if all data is available
+ void AwakeFromLoadThreaded();
+
+ // Used in the editor to generate at reimport time
+ void AwakeFromLoad( AwakeFromLoadMode awakeMode );
+
+ // Call the rebuild of all the textures and updates the SBS texture assets
+ // generationType, the type of requested texture generation
+ // forceTextureGeneration, a boolean to force textures generation even though they may be up to date from the engine's point of view
+ void RebuildTextures();
+
+ // Rebuilds all texture immediately in a synchronous manner
+ // When that function returns, all textures should have been generated
+ void RebuildTexturesImmediately();
+
+ // Call the rebuild of all the textures in all of the Procedural Materials
+ static void ReloadAll (bool unload = true, bool load = true);
+
+ // Awake dependent objects
+ void AwakeDependencies(bool awakeThreaded);
+
+ // Process rebuild of textures
+#if ENABLE_SUBSTANCE
+ static ProceduralMaterial* m_PackedSubstance;
+ static void PackSubstances(std::vector<ProceduralMaterial*>& materials);
+ bool ProcessTexturesThreaded(const std::map<ProceduralTexture*, SubstanceTexture>& textures);
+#if !UNITY_EDITOR
+ bool PreProcess(std::set<unsigned int>& cachedTextureIDs);
+ void PostProcess(const std::map<ProceduralTexture*, SubstanceTexture>& textures, const std::set<unsigned int>& cachedTextureIDs);
+#endif
+#endif
+
+ // Scriptable input accessors
+ std::vector<std::string> GetSubstanceProperties() const;
+ bool HasSubstanceProperty( const std::string& inputName ) const;
+ bool GetSubstanceBoolean( const std::string& inputName ) const;
+ void SetSubstanceBoolean( const std::string& inputName, bool value );
+ float GetSubstanceFloat( const std::string& inputName ) const;
+ void SetSubstanceFloat( const std::string& inputName, float value );
+ Vector4f GetSubstanceVector( const std::string& inputName ) const;
+ void SetSubstanceVector( const std::string& inputName, const Vector4f& value );
+ ColorRGBAf GetSubstanceColor( const std::string& inputName ) const;
+ void SetSubstanceColor( const std::string& inputName, const ColorRGBAf& value );
+ int GetSubstanceEnum( const string& inputName );
+ void SetSubstanceEnum( const string& inputName, int value );
+ Texture2D* GetSubstanceTexture( const string& inputName ) const;
+ void SetSubstanceTexture( const string& inputName, Texture2D* value );
+
+#if ENABLE_SUBSTANCE
+ // Called by the SubstanceSystem when he process a "SetInput" command
+ void Callback_SetSubstanceInput( const string& inputName, SubstanceValue& inputValue );
+#endif
+
+ const SubstanceInput* FindSubstanceInput( const string& inputName ) const;
+ SubstanceInput* FindSubstanceInput( const string& inputName );
+
+ // Property caching
+ bool IsSubstancePropertyCached( const string& inputName ) const;
+ void CacheSubstanceProperty( const string& inputName, bool value );
+ void ClearCache();
+
+ // Memory budget
+ void SetProceduralMemoryBudget(ProceduralCacheSize budget);
+ ProceduralCacheSize GetProceduralMemoryBudget() const;
+ void SetProceduralMemoryWorkBudget(ProceduralCacheSize budget);
+ ProceduralCacheSize GetProceduralMemoryWorkBudget() const;
+ void SetProceduralMemorySleepBudget(ProceduralCacheSize budget);
+ ProceduralCacheSize GetProceduralMemorySleepBudget() const;
+
+protected:
+
+#if ENABLE_SUBSTANCE
+ void ApplyInputs (bool& it_has_changed, bool asHint, std::set<unsigned int>& modifiedOutputsUID);
+ void ApplyOutputs (bool& it_has_changed, bool asHint, std::set<unsigned int>& modifiedOutputsUID, const std::set<unsigned int>& cachedTextureIDs);
+#endif
+
+private:
+
+ // Shared substance data
+ struct SubstanceData
+ {
+ UInt8* substanceData; // Platform dependent linked binary content
+ SubstanceHandle* substanceHandle; // Substance engine handle, shared by instances
+ ProceduralCacheSize memoryWorkBudget; // 'Work' cache size, shared by instances
+ ProceduralCacheSize memorySleepBudget; // 'Sleep' cache size, shared by instances
+ int instanceCount; // Count of substances using the same handle
+ };
+
+ PPtr<SubstanceArchive> m_SubstancePackage; // The parent SBS package from which we get generated
+ SubstanceArchive* m_PingedPackage; // The pinged package
+ SubstanceData* m_SubstanceData; // Shared substance data
+ UnityStr m_PrototypeName; // The name of the original graph in the package
+ int m_Width; // Width
+ int m_Height; // Height
+ Textures m_Textures; // The list of persistent output textures for that material
+ PingedTextures m_PingedTextures; // The list of pinged output textures
+ SubstanceInputs m_Inputs; // Substance inputs
+ static Mutex m_InputMutex; // Input accessors mutex
+ Hash128 m_Hash; // Hash used for cache status checking
+
+public:
+ // Texture inputs
+ struct TextureInput
+ {
+ Texture2D* texture;
+ Image* image;
+ SubstanceTextureInput* inputParameters;
+ void* buffer;
+ };
+ std::vector<TextureInput> m_TextureInputs;
+#if UNITY_EDITOR
+ std::vector<TextureInput>& GetTextureInputs() { return m_TextureInputs; }
+#endif
+
+ void ApplyTextureInput (int substanceInputIndex, const SubstanceTextureInput& requiredTextureInput);
+
+ // Animated substances
+private:
+ int m_AnimationUpdateRate;
+ float m_AnimationTime;
+public:
+ void SetAnimationUpdateRate(int rate) { m_AnimationUpdateRate = rate; }
+ int GetAnimationUpdateRate() const { return m_AnimationUpdateRate; }
+ void UpdateAnimation(float time);
+
+ // Flags
+public:
+ enum Flag
+ {
+ Flag_DeprecatedGenerateAtLoad = 1<<0, // deprecated
+ Flag_Animated = 1<<2, // the material has animated textures
+ Flag_AwakeClone = 1<<3, // the material is a clone which require to be awaken
+ Flag_GenerateAll = 1<<4, // we force the generation of all outputs
+ Flag_ConstSize = 1<<5, // the size and seed don't change at runtime
+ Flag_ForceGenerate = 1<<6, // force the generation
+ Flag_Clone = 1<<7, // the material is a clone
+ Flag_Import = 1<<8, // the material is being imported
+ Flag_Awake = 1<<9, // the material is awakening
+ Flag_Uncompressed = 1<<10, // the import is forced uncompressed
+ Flag_Broken = 1<<11, // some dependencies are lacking, the substance can't be generated
+ Flag_Readable = 1<<12 // generated textures are readable, provided it's in RAW format
+ };
+ void EnableFlag(const Flag& flag, bool enabled=true) { if (enabled) m_Flags |= (unsigned int)flag; else m_Flags &= ~(unsigned int)flag; }
+ bool IsFlagEnabled(const Flag& flag) const { return m_Flags & (unsigned int)flag; }
+private:
+ unsigned int m_Flags;
+
+ // Loading mode
+public:
+ void SetLoadingBehavior(ProceduralLoadingBehavior behavior) { m_LoadingBehavior = behavior; }
+ ProceduralLoadingBehavior GetLoadingBehavior() const { return m_LoadingBehavior; }
+private:
+ ProceduralLoadingBehavior m_LoadingBehavior;
+
+// Substance processing
+public:
+ bool IsProcessing() const;
+ static void SetProceduralProcessorUsage(ProceduralProcessorUsage processorUsage);
+ static ProceduralProcessorUsage GetProceduralProcessorUsage();
+ static void StopProcessing();
+ SubstanceData* GetSubstanceData() { return m_SubstanceData; }
+
+// Presets handling
+ bool SetPreset(const std::string& presetContent);
+ std::string GetPreset() const;
+
+// Textures accessors
+ ProceduralTexture* GetGeneratedTexture(const std::string& textureName);
+
+// Integration
+ unsigned int integrationTimeStamp;
+
+#if UNITY_EDITOR
+ UInt8* GetHashPtr() { return m_Hash.hashData.bytes; }
+ void InvalidateIfCachedOrInvalidTextures();
+
+ friend struct TemporarilyStripSubstanceData;
+#endif
+
+// Enter/Leave PlayMode
+#if UNITY_EDITOR
+ bool m_isAlreadyLoadedInCurrentScene;
+#endif
+
+// Caching
+#if ENABLE_SUBSTANCE && !UNITY_EDITOR
+ std::string GetCacheFolder() const;
+ std::string GetCacheFilename(const ProceduralTexture& texture) const;
+ bool ReadCachedTexture(string& fileName, std::map<ProceduralTexture*, SubstanceTexture>& cachedTextures, const std::string& folder, const ProceduralTexture& texture);
+ bool WriteCachedTexture(string& fileName, const std::string& folder, const ProceduralTexture& texture, const SubstanceTexture& data);
+#endif
+};
diff --git a/Runtime/Graphics/ProceduralPreset.cpp b/Runtime/Graphics/ProceduralPreset.cpp
new file mode 100644
index 0000000..154cdd3
--- /dev/null
+++ b/Runtime/Graphics/ProceduralPreset.cpp
@@ -0,0 +1,143 @@
+#include "UnityPrefix.h"
+#include "ProceduralMaterial.h"
+
+int ProceduralPreset_parseValues(std::vector<std::string>& values, std::string name, std::string line)
+{
+ values.clear();
+ int start = line.find(name);
+ if (start==std::string::npos)
+ {
+ return 0;
+ }
+
+ line = line.substr(start+name.size());
+ if (line.size()<3 || line[0]!='=' || line[1]!='\"')
+ {
+ return 0;
+ }
+
+ line = line.substr(2);
+ int p=0;
+ string val;
+ while (p<line.size() && line[p]!='\"')
+ {
+ if (line[p]==',')
+ {
+ values.push_back(val); val = "";
+ }
+ else
+ {
+ val += line[p];
+ }
+ ++p;
+ }
+ if (val.size()>0)
+ {
+ values.push_back(val);
+ }
+ return values.size();
+}
+
+inline std::string ProceduralPreset_getLine(std::string::const_iterator& pos, const std::string& preset)
+{
+ std::string::const_iterator i = pos;
+ std::string::const_iterator it = std::find(i, preset.end(), '\n');
+ pos = it;
+ if (it!=preset.end()) ++pos;
+ return std::string(i, it);
+}
+
+bool ProceduralMaterial::SetPreset(const std::string& preset)
+{
+ std::string::const_iterator pos = preset.begin();
+ std::string line;
+ line = ProceduralPreset_getLine(pos, preset);
+ if (line.find("formatversion=\"1.0\"")==std::string::npos)
+ {
+ return false;
+ }
+
+ while (pos!=preset.end() && line.find("/sbspreset")==std::string::npos)
+ {
+ line = ProceduralPreset_getLine(pos, preset);
+ if (line.find("presetinput")!=std::string::npos)
+ {
+ std::vector<std::string> identifier;
+ if (ProceduralPreset_parseValues(identifier, "identifier", line)!=1)
+ continue;
+
+ SubstanceInput* input = FindSubstanceInput(identifier[0]);
+ if (input==NULL || identifier[0]=="$normalformat" || identifier[0]=="$outputsize")
+ continue;
+
+ std::vector<std::string> type;
+ if (ProceduralPreset_parseValues(type, "type", line)!=1)
+ continue;
+
+ unsigned int internalType = atoi(type[0].c_str());
+ if (internalType!=input->internalType || internalType==Substance_IType_Image)
+ continue;
+
+ std::vector<std::string> value;
+ ProceduralPreset_parseValues(value, "value", line);
+ float f[4];
+ int count = GetRequiredInputComponentCount((SubstanceInputType)internalType);
+ if (value.size()!=count)
+ continue;
+ for (int i=0 ; i<count ; ++i)
+ {
+ f[i] = (float)atof(value[i].c_str());
+ }
+ SetSubstanceVector(identifier[0], Vector4f(f));
+ }
+ }
+ RebuildTextures();
+ return true;
+}
+
+std::string ProceduralMaterial::GetPreset() const
+{
+ std::string preset = "<sbspresets formatversion=\"1.0\" count=\"1\">\n";
+ preset += " <sbspreset pkgurl=\"\" description=\"\" label=\"\">\n";
+ char tmp[256];
+ for (SubstanceInputs::const_iterator i=m_Inputs.begin() ; i!=m_Inputs.end() ; ++i)
+ {
+ preset += " <presetinput identifier=\"";
+ preset += i->name;
+ preset += "\" uid=\"";
+ snprintf(tmp, 256, "%d", i->internalIdentifier);
+ preset += tmp;
+ preset += "\" type=\"";
+ snprintf(tmp, 256, "%d", (int)i->internalType);
+ preset += tmp;
+ preset += "\" value=\"";
+ if (i->internalType!=Substance_IType_Image)
+ {
+ bool isInteger = IsSubstanceAnyIntType(i->internalType);
+ int count = GetRequiredInputComponentCount(i->internalType);
+ for (int j=0 ; j<count ; ++j)
+ {
+ if (j>0) preset += ",";
+ if (isInteger) snprintf(tmp, 256, "%d", (int)i->value.scalar[j]);
+ else snprintf(tmp, 256, "%f", i->value.scalar[j]);
+ preset += tmp;
+ }
+ }
+ preset += "\"/>\n";
+ }
+ preset += " </sbspreset>\n";
+ preset += "</sbspresets>\n";
+ return preset;
+}
+
+ProceduralTexture* ProceduralMaterial::GetGeneratedTexture(const std::string& textureName)
+{
+ for (Textures::iterator it=m_Textures.begin() ; it!=m_Textures.end() ; ++it)
+ {
+ if (it->IsValid() && (*it)->GetName()==textureName)
+ {
+ return &**it;
+ }
+ }
+ return NULL;
+}
diff --git a/Runtime/Graphics/ProceduralTexture.cpp b/Runtime/Graphics/ProceduralTexture.cpp
new file mode 100644
index 0000000..c4ea5ea
--- /dev/null
+++ b/Runtime/Graphics/ProceduralTexture.cpp
@@ -0,0 +1,372 @@
+#include "UnityPrefix.h"
+#include "ProceduralTexture.h"
+#include "SubstanceArchive.h"
+#include "ProceduralMaterial.h"
+#include "Image.h"
+#include "SubstanceSystem.h"
+#include "Texture2D.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Graphics/S3Decompression.h"
+#include "Runtime/File/ApplicationSpecificPersistentDataPath.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/properties.h"
+
+using namespace std;
+
+ProceduralTexture::TextureParameters::TextureParameters() :
+ width (0),
+ height(0),
+ mipLevels(0),
+ textureFormat(kTexFormatTotalCount)
+{
+}
+
+ProceduralTexture::TextureParameters::TextureParameters(int inWidth, int inHeight, int inMipLevels, TextureFormat inFormat) :
+ width (inWidth),
+ height(inHeight),
+ mipLevels(inMipLevels),
+ textureFormat(inFormat)
+{
+}
+
+IMPLEMENT_CLASS( ProceduralTexture )
+IMPLEMENT_OBJECT_SERIALIZE( ProceduralTexture )
+
+ProceduralTexture::ProceduralTexture( MemLabelId label, ObjectCreationMode mode ) :
+ Super( label, mode ),
+ m_SubstanceTextureUID( 0 ),
+ m_Type(Substance_OType_Unknown),
+ m_AlphaSource(Substance_OType_Unknown),
+ m_Flags(0),
+ m_UploadState(UploadState_None),
+ m_Format(Substance_OFormat_Compressed),
+ m_PingedMaterial(NULL)
+{
+}
+
+ProceduralTexture::~ProceduralTexture()
+{
+ // Update pointer
+ if (m_PingedMaterial==NULL)
+ m_PingedMaterial = dynamic_pptr_cast<ProceduralMaterial*>(Object::IDToPointer(m_SubstanceMaterial.GetInstanceID()));
+
+#if ENABLE_SUBSTANCE
+ /////@TODO: This is an incredible hack. Waiting for another process to complete in the destructor is a big no no, especially when it involves a Thread::Sleep ()...
+ UnlockObjectCreation();
+ GetSubstanceSystem ().NotifyTextureDestruction(this);
+ LockObjectCreation();
+#endif
+
+ RemoveTexture ();
+}
+
+#if ENABLE_SUBSTANCE
+ProceduralTexture* ProceduralTexture::Clone(ProceduralMaterial* owner)
+{
+ ProceduralTexture* clone = CreateObjectFromCode<ProceduralTexture>();
+ clone->m_SubstanceMaterial = owner;
+ clone->m_PingedMaterial = owner;
+ clone->m_SubstanceTextureUID = m_SubstanceTextureUID;
+ clone->m_Type = m_Type;
+ clone->m_AlphaSource = m_AlphaSource;
+ clone->m_Format = m_Format;
+ clone->m_SubstanceFormat = m_SubstanceFormat;
+ clone->m_ClonedID = GetTextureID();
+ clone->SetName(GetName());
+ clone->EnableFlag(Flag_AwakeClone, true);
+ clone->SetUsageMode(GetUsageMode());
+ return clone;
+}
+
+void ProceduralTexture::AwakeClone()
+{
+ // Update material texenvs
+ const ShaderLab::PropertySheet::TexEnvs& textureProperties = GetSubstanceMaterial()->GetProperties().GetTexEnvsMap();
+ for (ShaderLab::PropertySheet::TexEnvs::const_iterator i=textureProperties.begin();i!=textureProperties.end();++i)
+ {
+ if (i->second.texEnv->GetAssignedTextureID()==m_ClonedID)
+ {
+ GetSubstanceMaterial()->SetTexture(i->first, this);
+ }
+ }
+ EnableFlag(Flag_AwakeClone, false);
+}
+#endif
+
+#if UNITY_EDITOR
+void ProceduralTexture::Init( ProceduralMaterial& _Parent, int substanceTextureUID, ProceduralOutputType type, SubstanceOutputFormat format, ProceduralOutputType alphaSource, bool requireCompressed )
+{
+ m_SubstanceMaterial = &_Parent;
+ m_SubstanceTextureUID = ((UInt64)substanceTextureUID)<<32;
+ m_Type = type;
+ m_Format = format;
+ m_TextureParameters.textureFormat = GetSubstanceTextureFormat(format, requireCompressed);
+ m_AlphaSource = alphaSource;
+ m_Flags = 0;
+
+ AwakeFromLoad(kDefaultAwakeFromLoad);
+}
+#endif
+
+bool ProceduralTexture::IsBaked() const
+{
+ return m_BakedParameters.IsValid() && m_BakedData.size()>0;
+}
+
+bool ProceduralTexture::GetPixels32(int x, int y, int width, int height, ColorRGBA32* data)
+{
+ if (m_Format != Substance_OFormat_Raw)
+ {
+ WarningStringMsg("Substance %s should be set to RAW in order to use GetPixels32 on its texture outputs.", m_PingedMaterial->GetName());
+ return false;
+ }
+ if (! m_PingedMaterial->IsFlagEnabled(ProceduralMaterial::Flag_Readable))
+ {
+ WarningStringMsg("The isReadable property of Substance %s should be set to true in order to use GetPixels32 on its texture outputs.", m_PingedMaterial->GetName());
+ return false;
+ }
+ const ProceduralTexture::TextureParameters& parameters = GetBakedParameters();
+ if (GetBakedData().size()==0 || (parameters.textureFormat!=kTexFormatRGBA32 && parameters.textureFormat!=kTexFormatARGB32))
+ return false;
+ ImageReference rawTexture(parameters.width, parameters.height, GetRowBytesFromWidthAndFormat(parameters.width, parameters.textureFormat), parameters.textureFormat, &GetBakedData()[0]);
+ ImageReference resultImage(parameters.width, parameters.height, GetRowBytesFromWidthAndFormat(parameters.width, kTexFormatRGBA32), kTexFormatRGBA32, data);
+ resultImage.BlitImage(rawTexture, ImageReference::BLIT_COPY);
+ return true;
+}
+
+void ProceduralTexture::Invalidate()
+{
+ m_TextureParameters.Invalidate();
+}
+
+void ProceduralTexture::UnloadFromGfxDevice (bool forceUnloadAll)
+{
+ RemoveTexture();
+}
+
+void ProceduralTexture::UploadToGfxDevice ()
+{
+ if (!m_BakedParameters.IsValid())
+ return;
+ if (m_BakedData.size()==0)
+ {
+ GetPersistentManager().ReloadFromDisk(this);
+ }
+ else
+ {
+ UploadBakedTexture();
+ }
+}
+
+void ProceduralTexture::RemoveTexture ()
+{
+ if (IsFlagEnabled (Flag_Uploaded))
+ {
+ GetGfxDevice().DeleteTexture (GetTextureID());
+ EnableFlag(Flag_Uploaded, false);
+ m_UploadState = UploadState_None;
+ }
+}
+
+void ProceduralTexture::UploadWaitingTexture ()
+{
+ RemoveTexture ();
+ // Upload blue texture to show the substance is waiting generation
+ UInt8 bluePixel[] = { 255, 0, 0, 255 };
+ GetGfxDevice().UploadTexture2D( GetTextureID(), GetDimension(), bluePixel, sizeof(bluePixel),
+ 1, 1, kTexFormatARGB32, 1, true, 0 /* \note We upload only one level so can't skip any */,
+ GetUsageMode(), kTexColorSpaceLinear);
+ Texture::s_TextureIDMap.insert (std::make_pair(GetTextureID(),this));
+ EnableFlag(Flag_Uploaded, true);
+ m_UploadState = UploadState_Waiting;
+ m_TextureSettings.Apply( GetTextureID(), GetDimension(), false, kTexColorSpaceLinear );
+}
+
+void ProceduralTexture::UploadBakedTexture ()
+{
+ RemoveTexture ();
+ Assert(m_BakedData.size()>0);
+ GetGfxDevice().UploadTexture2D( GetTextureID(), GetDimension(), &m_BakedData[0], m_BakedData.size(),
+ m_BakedParameters.width, m_BakedParameters.height, m_BakedParameters.textureFormat,
+ m_BakedParameters.mipLevels, true, 0 /* \note We upload only one level so can't skip any */,
+ GetUsageMode(), GetActiveTextureColorSpace());
+ Texture::s_TextureIDMap.insert (std::make_pair(GetTextureID(),this));
+ EnableFlag(Flag_Uploaded, true);
+ m_UploadState = UploadState_Baked;
+ m_TextureSettings.Apply( GetTextureID(), GetDimension(), m_BakedParameters.mipLevels != 1, GetActiveTextureColorSpace() );
+ m_TextureParameters = m_BakedParameters;
+
+#if !UNITY_EDITOR
+ m_BakedData.clear();
+#endif
+}
+
+void ProceduralTexture::SetSubstanceShuffledUID(unsigned int textureUID)
+{
+ m_SubstanceTextureUID &= ((UInt64)0xffffffff) << 32;
+ m_SubstanceTextureUID |= (UInt64)textureUID;
+}
+
+void ProceduralTexture::AwakeFromLoadThreaded()
+{
+ Super::AwakeFromLoadThreaded();
+
+ // Update format before it gets packed & generated
+ m_SubstanceFormat = GetSubstanceTextureFormat(m_Format);
+
+#if UNITY_ANDROID || UNITY_IPHONE
+ // It is more practical to do this check at runtime since for Substances we control
+ // the way we generate the normal maps. Doing this here instead of in the editor avoids
+ // the reimports that would be associated with platform switches (non-mobile <-> mobile).
+ if (m_UsageMode == kTexUsageNormalmapDXT5nm)
+ {
+ // Override normal format
+ m_UsageMode = kTexUsageNormalmapPlain;
+ }
+#endif
+}
+
+void ProceduralTexture::AwakeFromLoad( AwakeFromLoadMode awakeMode )
+{
+ Super::AwakeFromLoad( awakeMode );
+
+ // Force loading of material
+ if ((awakeMode & kDidLoadThreaded) == 0)
+ {
+ ProceduralMaterial* material = m_SubstanceMaterial;
+ if (material==NULL)
+ {
+ // This happens
+ }
+ }
+
+ if (IsBaked())
+ {
+ if (m_UploadState<UploadState_Valid)
+ UploadBakedTexture();
+ }
+ else
+ {
+ if (m_UploadState==UploadState_None)
+ UploadWaitingTexture();
+ }
+
+ m_SubstanceFormat = GetSubstanceTextureFormat(m_Format);
+
+#if UNITY_ANDROID || UNITY_IPHONE
+ // It is more practical to do this check at runtime since for Substances we control
+ // the way we generate the normal maps. Doing this here instead of in the editor avoids
+ // the reimports that would be associated with platform switches (non-mobile <-> mobile).
+ if (m_UsageMode == kTexUsageNormalmapDXT5nm)
+ {
+ // Override normal format
+ m_UsageMode = kTexUsageNormalmapPlain;
+ }
+#endif
+}
+
+void ProceduralTexture::UploadSubstanceTexture(SubstanceTexture& outputTexture)
+{
+#if ENABLE_SUBSTANCE
+ // Check if it's full pyramid
+ if (outputTexture.mipmapCount==0)
+ {
+ outputTexture.mipmapCount = CalculateMipMapCount3D(outputTexture.level0Width, outputTexture.level0Height, 1);
+ }
+
+ // Check if we can replace the existing texture data
+ TextureParameters state (outputTexture.level0Width, outputTexture.level0Height, outputTexture.mipmapCount, m_SubstanceFormat);
+
+ bool reuseTextureMemory = (state==m_TextureParameters);
+ if (!reuseTextureMemory)
+ RemoveTexture ();
+
+ int bufferSize = CalculateMipMapOffset(state.width, state.height, state.textureFormat, state.mipLevels+1);
+ GetGfxDevice().UploadTexture2D( GetTextureID(), GetDimension(), reinterpret_cast<UInt8*> (outputTexture.buffer), bufferSize,
+ state.width, state.height, state.textureFormat, state.mipLevels, !reuseTextureMemory, min(state.mipLevels-1, Texture::GetMasterTextureLimit()),
+ GetUsageMode(), GetActiveTextureColorSpace());
+ Texture::s_TextureIDMap.insert (std::make_pair(GetTextureID(),this));
+ EnableFlag(Flag_Uploaded, true);
+ m_TextureParameters = state;
+
+ // Handle readable flag
+ if (GetSubstanceMaterial()!=NULL && GetSubstanceMaterial()->IsFlagEnabled(ProceduralMaterial::Flag_Readable)
+ && (state.textureFormat==kTexFormatRGBA32 || state.textureFormat==kTexFormatARGB32))
+ {
+ size_t size = state.width*state.height*4;
+ m_BakedData.resize(size);
+ memcpy(&m_BakedData[0], outputTexture.buffer, size);
+ m_BakedParameters = state;
+ }
+
+ if (IsFlagEnabled(Flag_AwakeClone))
+ AwakeClone();
+
+ m_TextureSettings.Apply( GetTextureID(), GetDimension(), outputTexture.mipmapCount != 1, GetActiveTextureColorSpace() );
+ m_UploadState = UploadState_Generated;
+#endif
+}
+
+void ProceduralTexture::SetOwner(ProceduralMaterial* material)
+{
+ Assert (material->IsFlagEnabled(ProceduralMaterial::Flag_Clone)
+ || m_PingedMaterial==NULL || m_PingedMaterial==material);
+ if (m_PingedMaterial==NULL)
+ m_PingedMaterial = material;
+}
+
+template<class TransferFunction>
+void ProceduralTexture::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER( m_SubstanceMaterial );
+ TRANSFER( m_SubstanceTextureUID );
+ transfer.Transfer(reinterpret_cast<int&> (m_Type), "Type");
+ transfer.Transfer(reinterpret_cast<int&> (m_AlphaSource), "AlphaSource");
+ transfer.Transfer(reinterpret_cast<int&> (m_Format), "Format");
+
+ if (m_Format<0 || m_Format>1)
+ m_Format = Substance_OFormat_Compressed;
+
+ TRANSFER( m_TextureSettings );
+
+ if (transfer.IsBuildingTargetPlatform(kBuildXBOX360))
+ {
+ const size_t size = m_BakedData.size();
+ std::vector<UInt8> swappedData;
+ swappedData.resize(size);
+ if (m_Format == Substance_OFormat_Compressed)
+ {
+ // Compressed Substance textures are DXT : 16b byte-swap needed
+ for (int i=0 ; i<size/2 ; ++i)
+ {
+ swappedData[2*i+0] = m_BakedData[2*i+1];
+ swappedData[2*i+1] = m_BakedData[2*i+0];
+ }
+ }
+ else
+ {
+ // RAW Substance textures = 4Bpp : 32b byte-swap needed
+ for (int i=0 ; i<size/4 ; ++i)
+ {
+ swappedData[4*i+0] = m_BakedData[4*i+3];
+ swappedData[4*i+1] = m_BakedData[4*i+2];
+ swappedData[4*i+2] = m_BakedData[4*i+1];
+ swappedData[4*i+3] = m_BakedData[4*i+0];
+ }
+ }
+ TRANSFER( swappedData );
+ }
+ else
+ {
+ TRANSFER( m_BakedData );
+ }
+
+ TRANSFER( m_BakedParameters );
+ transfer.Transfer( m_UsageMode, "m_LightmapFormat");
+ transfer.Transfer( m_ColorSpace, "m_ColorSpace");
+}
diff --git a/Runtime/Graphics/ProceduralTexture.h b/Runtime/Graphics/ProceduralTexture.h
new file mode 100644
index 0000000..94740a7
--- /dev/null
+++ b/Runtime/Graphics/ProceduralTexture.h
@@ -0,0 +1,190 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Texture.h"
+#include "External/Allegorithmic/builds/Engines/include/substance/handle.h"
+
+class ColorRGBAf;
+class ColorRGBA32;
+class ProceduralMaterial;
+
+enum ProceduralOutputType
+{
+ Substance_OType_Unknown = 0,
+ Substance_OType_Diffuse,
+ Substance_OType_Normal,
+ Substance_OType_Height,
+ Substance_OType_Emissive,
+ Substance_OType_Specular,
+ Substance_OType_Opacity,
+ ProceduralOutputType_Count
+};
+
+enum SubstanceOutputFormat
+{
+ Substance_OFormat_Compressed = 0,
+ Substance_OFormat_Raw
+};
+
+/* A ProceduralTexture is a dynamic texture generated by the Substance engine.
+ * It's part of a Substance graph hierarchy, compound of ProceduralMaterial, SubstanceArchive.
+ */
+
+
+class ProceduralTexture : public Texture
+{
+public:
+
+ // Uploaded texture parameters
+ struct TextureParameters
+ {
+ DECLARE_SERIALIZE(TextureParameters)
+
+ int width;
+ int height;
+ int mipLevels;
+ TextureFormat textureFormat;
+
+ bool IsValid () const { return width != 0; }
+ friend bool operator == (const TextureParameters& lhs, const TextureParameters& rhs)
+ {
+ return lhs.width == rhs.width && lhs.height == rhs.height && lhs.mipLevels == rhs.mipLevels && lhs.textureFormat == rhs.textureFormat;
+ }
+
+ TextureParameters ();
+ TextureParameters (int inWidth, int inHeight, int inMipLevels, TextureFormat inFormat);
+
+ void Invalidate() { *this = TextureParameters(); }
+ };
+
+ // Flags
+ enum Flag
+ {
+ Flag_Binded = 1<<0, // the output is altered by an input
+ Flag_Uploaded = 1<<1, // the data has been uploaded
+ Flag_AwakeClone = 1<<2, // the texture need awake from clone
+ Flag_Cached = 1<<3 // the texture has been cached, editor flag only
+ };
+
+ // Upload State
+ enum UploadState
+ {
+ UploadState_None, // no data has been uploaded
+ UploadState_Waiting, // blue waiting texture
+ UploadState_Valid, // lower states indicate the current uploaded data isn't valid
+ UploadState_Baked, // baked texture
+ UploadState_Generated // generated texture
+ };
+
+protected: // FIELDS
+
+ PPtr<ProceduralMaterial> m_SubstanceMaterial; // The parent procedural material from which we get generated
+ ProceduralMaterial* m_PingedMaterial; // The pinged procedural material
+ UInt64 m_SubstanceTextureUID; // The index of this texture (i.e. Substance output) WORD[shuffledID,baseID]
+ unsigned int m_SubstancePreShuffleUID; // This ID is required to use substanceLinkerHandleSelectOutputs
+ ProceduralOutputType m_Type;
+ ProceduralOutputType m_AlphaSource;
+ unsigned int m_Flags;
+ UploadState m_UploadState;
+ SubstanceOutputFormat m_Format;
+ TextureFormat m_SubstanceFormat; // Platform dependant format required for substance generation
+ TextureParameters m_TextureParameters;
+ std::vector<UInt8> m_BakedData; // Baked texture if substance isn't supported
+ TextureParameters m_BakedParameters;
+ TextureID m_ClonedID; // Link to the source ID when cloned and not generated
+
+public: // METHODS
+
+ REGISTER_DERIVED_CLASS( ProceduralTexture, Texture )
+ DECLARE_OBJECT_SERIALIZE( ProceduralTexture )
+
+ ProceduralTexture( MemLabelId label, ObjectCreationMode mode );
+
+#if ENABLE_SUBSTANCE
+ ProceduralTexture* Clone(ProceduralMaterial* owner);
+ void AwakeClone();
+#endif
+
+ virtual int GetDataWidth() const { return m_TextureParameters.width; }
+ virtual int GetDataHeight() const { return m_TextureParameters.height; }
+ TextureFormat GetDataFormat() const { return m_TextureParameters.textureFormat; }
+ int GetDataMipLevels() const { return m_TextureParameters.mipLevels; }
+ virtual TextureDimension GetDimension () const { return kTexDim2D; }
+ virtual bool HasMipMap () const { return m_TextureParameters.mipLevels != 1; }
+ virtual int CountMipmaps() const { return m_TextureParameters.mipLevels; }
+
+ const ProceduralOutputType& GetType() const { return m_Type; }
+ const ProceduralOutputType& GetAlphaSource() const { return m_AlphaSource; }
+ bool IsFlagEnabled(const Flag& flag) const { return m_Flags & (unsigned int)flag; }
+ void EnableFlag(const Flag& flag, bool enabled=true) { if (enabled) m_Flags |= (unsigned int)flag; else m_Flags &= ~(unsigned int)flag; }
+
+#if ENABLE_PROFILER
+ virtual int GetStorageMemorySize() const { return m_TextureParameters.width * m_TextureParameters.height; }
+#endif
+ virtual bool ExtractImage (ImageReference* image, int imageIndex = 0) const { return false; }
+
+ virtual void UnloadFromGfxDevice(bool forceUnloadAll);
+ virtual void UploadToGfxDevice();
+ void RemoveTexture ();
+ void UploadWaitingTexture ();
+ void UploadBakedTexture ();
+
+ // Gets the output texture UID (i.e. output UID for the Substance engine)
+ unsigned int GetSubstanceTextureUID() const { return m_SubstanceTextureUID & 0xffffffff; }
+ unsigned int GetSubstanceBaseTextureUID() const { return (m_SubstanceTextureUID >> 32); }
+
+ void SetSubstancePreShuffleUID(unsigned int id) { m_SubstancePreShuffleUID = id; }
+ unsigned int GetSubstancePreShuffleUID() const { return m_SubstancePreShuffleUID; }
+
+ // Set the UIDs
+ // (Don't call externally though !)
+ void SetSubstanceShuffledUID(unsigned int textureUID);
+ void SetSubstanceFormat(TextureFormat format) { m_SubstanceFormat = format; }
+ TextureFormat GetSubstanceFormat() const { return m_SubstanceFormat; }
+
+ virtual void AwakeFromLoadThreaded();
+ virtual void AwakeFromLoad( AwakeFromLoadMode awakeMode );
+
+ virtual bool ShouldIgnoreInGarbageDependencyTracking () { return false; }
+
+ // (Don't call externally though !)
+ void UploadSubstanceTexture(SubstanceTexture& outputTexture);
+
+ bool HasBeenGenerated() const { return m_UploadState==UploadState_Generated; }
+ bool HasBeenUploaded() const { return (m_UploadState==UploadState_Generated) || (m_UploadState==UploadState_Baked); }
+
+#if UNITY_EDITOR
+ // Creates the texture (one time call only by importer)
+ void Init( ProceduralMaterial& _Parent, int _TextureIndex, ProceduralOutputType type, SubstanceOutputFormat format, ProceduralOutputType alphaSource, bool requireCompressed );
+ const TextureParameters& GetTextureParameters() const { return m_TextureParameters; }
+ TextureParameters& GetTextureParameters() { return m_TextureParameters; }
+
+ virtual TextureFormat GetEditorUITextureFormat () const { return m_TextureParameters.textureFormat; }
+#endif
+
+ bool IsBaked() const;
+ std::vector<UInt8>& GetBakedData() { return m_BakedData; }
+ const TextureParameters& GetBakedParameters() const { return m_BakedParameters; }
+ TextureParameters& GetBakedParameters() { return m_BakedParameters; }
+
+ bool GetPixels32(int x, int y, int width, int height, ColorRGBA32* data);
+
+ bool IsValid() { return m_TextureParameters.IsValid(); }
+ void Invalidate();
+
+ void SetOwner(ProceduralMaterial* material);
+ ProceduralMaterial* GetSubstanceMaterial() { return m_PingedMaterial; }
+#if UNITY_EDITOR
+ PPtr<ProceduralMaterial>& GetSubstanceMaterialPtr() { return m_SubstanceMaterial; }
+#endif
+};
+
+template<class T>
+void ProceduralTexture::TextureParameters::Transfer(T& transfer)
+{
+ TRANSFER(width);
+ TRANSFER(height);
+ TRANSFER(mipLevels);
+ transfer.Transfer(reinterpret_cast<int&> (textureFormat), "textureFormat");
+}
diff --git a/Runtime/Graphics/RenderBufferManager.cpp b/Runtime/Graphics/RenderBufferManager.cpp
new file mode 100644
index 0000000..2b0f25a
--- /dev/null
+++ b/Runtime/Graphics/RenderBufferManager.cpp
@@ -0,0 +1,251 @@
+#include "UnityPrefix.h"
+#include "RenderBufferManager.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "RenderTexture.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Profiler/Profiler.h"
+#if UNITY_XENON
+ #include "PlatformDependent/Xbox360/Source/GfxDevice/TexturesXenon.h"
+#elif UNITY_WII
+ #include "Runtime/Graphics/ScreenManager.h"
+#endif
+
+#ifndef DEBUG_RB_MANAGER
+#define DEBUG_RB_MANAGER 0
+#endif
+
+using namespace std;
+
+static RenderBufferManager* gRenderBufferManager = NULL;
+
+void RenderBufferManager::InitRenderBufferManager ()
+{
+ Assert(gRenderBufferManager == NULL);
+ gRenderBufferManager = new RenderBufferManager();
+}
+
+void RenderBufferManager::CleanupRenderBufferManager ()
+{
+ Assert(gRenderBufferManager != NULL);
+ delete gRenderBufferManager;
+ gRenderBufferManager = NULL;
+}
+
+RenderBufferManager& GetRenderBufferManager ()
+{
+ Assert(gRenderBufferManager != NULL);
+ return *gRenderBufferManager;
+}
+
+RenderBufferManager* GetRenderBufferManagerPtr ()
+{
+ return gRenderBufferManager;
+}
+
+
+static int CalcSize( int size, int parentSize )
+{
+ switch (size) {
+ case RenderBufferManager::kFullSize:
+ return parentSize;
+ default:
+ #if DEBUGMODE
+ if( size <= 0 ) {
+ AssertString ("Invalid Temp Buffer size");
+ return 128;
+ }
+ #endif
+ return size;
+ }
+}
+
+
+RenderTexture *RenderBufferManager::GetTempBuffer (int width, int height, DepthBufferFormat depthFormat, RenderTextureFormat colorFormat, UInt32 flags, RenderTextureReadWrite colorSpace, int antiAliasing)
+{
+ if( colorFormat == kRTFormatDefault )
+ colorFormat = GetGfxDevice().GetDefaultRTFormat();
+
+ if( colorFormat == kRTFormatDefaultHDR )
+ colorFormat = GetGfxDevice().GetDefaultHDRRTFormat();
+
+ bool sRGB = colorSpace == kRTReadWriteSRGB;
+ const bool createdFromScript = flags & kRBCreatedFromScript;
+ const bool sampleOnlyDepth = flags & kRBSampleOnlyDepth;
+ const TextureDimension dim = (flags & kRBCubemap) ? kTexDimCUBE : kTexDim2D;
+
+ if (colorSpace == kRTReadWriteDefault)
+ sRGB = GetActiveColorSpace() == kLinearColorSpace;
+
+ // only SRGB where it makes sense
+ sRGB = sRGB && (colorFormat != GetGfxDevice().GetDefaultHDRRTFormat());
+
+ if( width <= 0 || height <= 0 )
+ {
+ if (dim != kTexDim2D) {
+ AssertString( "Trying to get a relatively sized RenderBuffer cubemap" );
+ return NULL;
+ }
+ Camera *cam = GetCurrentCameraPtr();
+ if (cam == NULL) {
+ AssertString ("Trying to get a relatively sized RenderBuffer without an active camera.");
+ return NULL;
+ }
+ Rectf r = cam->GetScreenViewportRect();
+#if UNITY_WII
+ GetScreenManager().ScaleViewportToFrameBuffer(r);
+#endif
+ // Figure out pixel size. Get screen extents as ints so we do the rounding correctly.
+ int viewport[4];
+ RectfToViewport(r, viewport);
+ width = viewport[2];
+ height = viewport[3];
+ }
+
+ if (dim == kTexDimCUBE && (!IsPowerOfTwo(width) || width != height))
+ {
+ AssertString( "Trying to get a non square or non power of two RenderBuffer cubemap" );
+ return NULL;
+ }
+
+ if (antiAliasing < 1 || antiAliasing > 8 || !IsPowerOfTwo(antiAliasing))
+ {
+ AssertString( "Trying to get RenderBuffer with invalid antiAliasing (must be 1, 2, 4 or 8)" );
+ return NULL;
+ }
+
+ // TODO: actually set & check depth
+
+ // Go over free textures & find the one that matches in parameters.
+ // The main point is: If we used a texture of same dims last frame we'll get that.
+ FreeTextures::iterator found = m_FreeTextures.end();
+ for( FreeTextures::iterator i = m_FreeTextures.begin(); i != m_FreeTextures.end(); ++i )
+ {
+ RenderTexture* rt = i->second;
+ if( !rt
+ || rt->GetDepthFormat() != depthFormat
+ || rt->GetColorFormat() != colorFormat
+ || rt->GetDimension() != dim
+ //@TODO: Only matters on OSX as DX can just set sampler state...
+ || rt->GetSRGBReadWrite() != sRGB
+ || rt->GetAntiAliasing() != antiAliasing
+ || rt->GetSampleOnlyDepth() != sampleOnlyDepth )
+ continue;
+ int tw = rt->GetWidth();
+ int th = rt->GetHeight();
+
+ if (tw == width && th == height) { // If the texture is same size
+ found = i;
+ break;
+ }
+ }
+
+ // We didn't find any.
+ if (found == m_FreeTextures.end() || !found->second)
+ {
+ m_TempBuffers++;
+ RenderTexture *tex = NEW_OBJECT (RenderTexture);
+ tex->Reset();
+
+ tex->SetHideFlags(Object::kDontSave);
+ tex->SetName (Format ("TempBuffer %d", m_TempBuffers).c_str());
+ tex->SetWidth(width);
+ tex->SetHeight(height);
+ tex->SetColorFormat( colorFormat );
+ tex->SetDepthFormat( depthFormat );
+ tex->SetDimension (dim);
+ tex->SetSRGBReadWrite (sRGB);
+ tex->SetAntiAliasing (antiAliasing);
+ tex->SetSampleOnlyDepth (sampleOnlyDepth);
+ tex->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+ m_TakenTextures.insert (tex);
+ #if DEBUG_RB_MANAGER
+ printf_console ("RBM: new texture %ix%i fmt=%i\n", width, height, colorFormat);
+ #endif
+ return tex;
+ }
+
+ // We found one. Move it from free to taken
+ RenderTexture *tex = found->second;
+ Assert (tex->GetWidth() == width && tex->GetHeight() == height);
+ m_TakenTextures.insert (tex);
+ m_FreeTextures.erase (found);
+
+ // Set it's parameters (filtering etc.) as if it was newly created
+ tex->GetSettings().Reset();
+ tex->GetSettings().m_WrapMode = kTexWrapClamp;
+ tex->ApplySettings();
+
+ tex->SetCreatedFromScript (createdFromScript);
+
+ // Automatically DiscardContents when createdFromScript - user can't expect any valid content.
+ if (createdFromScript)
+ tex->DiscardContents();
+
+ return tex;
+}
+
+PROFILER_INFORMATION(gRenderBufferCollect, "RenderTexture.GarbageCollectTemporary", kProfilerRender)
+
+void RenderBufferManager::GarbageCollect (int framesDelay)
+{
+ ++m_CurrentRBMFrame;
+
+ for (FreeTextures::iterator i = m_FreeTextures.begin(); i != m_FreeTextures.end();)
+ {
+ // Should only ever compare the difference since frame wraps around
+ int frameDiff = m_CurrentRBMFrame - i->first;
+ if( frameDiff > framesDelay || frameDiff < 0 )
+ {
+ PROFILER_AUTO(gRenderBufferCollect, NULL);
+ #if DEBUG_RB_MANAGER
+ printf_console ("RBM: kill unused texture (currframe=%i usedframe=%i)\n", m_CurrentRBMFrame, i->first);
+ #endif
+
+ FreeTextures::iterator j = i;
+ i++;
+ DestroySingleObject(j->second);
+ m_FreeTextures.erase (j);
+ }
+ else
+ {
+ i++;
+ }
+ }
+}
+
+
+void RenderBufferManager::Cleanup ()
+{
+ for (TakenTextures::iterator i=m_TakenTextures.begin();i != m_TakenTextures.end();i++)
+ {
+ DestroySingleObject(*i);
+ }
+ m_TakenTextures.clear();
+
+ for (FreeTextures::iterator i=m_FreeTextures.begin();i != m_FreeTextures.end();i++)
+ {
+ DestroySingleObject(i->second);
+ }
+ m_FreeTextures.clear();
+ #if DEBUG_RB_MANAGER
+ printf_console( "RBM: destroy all textures\n" );
+ #endif
+}
+
+void RenderBufferManager::ReleaseTempBuffer (RenderTexture *rTex)
+{
+ if (!rTex)
+ return;
+
+ if (!m_TakenTextures.count (PPtr<RenderTexture> (rTex)))
+ {
+ ErrorStringObject ("Attempting to release RenderTexture that were not gotten as a temp buffer", rTex);
+ return;
+ }
+
+ m_TakenTextures.erase (PPtr<RenderTexture> (rTex));
+ m_FreeTextures.push_back (make_pair (m_CurrentRBMFrame, PPtr<RenderTexture> (rTex)));
+}
diff --git a/Runtime/Graphics/RenderBufferManager.h b/Runtime/Graphics/RenderBufferManager.h
new file mode 100644
index 0000000..5496acf
--- /dev/null
+++ b/Runtime/Graphics/RenderBufferManager.h
@@ -0,0 +1,58 @@
+#ifndef RENDERBUFFERMANAGER_H
+#define RENDERBUFFERMANAGER_H
+
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/Utilities/MemoryPool.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+
+class RenderTexture;
+
+
+/* Manager for getting temporary render buffers.
+ * Use this instead of creating RenderTextures if you need a quick buffer.
+ * This is a low-overhead class that recycles render textures.
+ */
+class RenderBufferManager
+{
+ RenderBufferManager () { m_CurrentRBMFrame = m_TempBuffers = 0; }
+
+public:
+
+ enum { kFullSize = -1 };
+ enum {
+ kRBCubemap = (1<<0),
+ kRBCreatedFromScript = (1<<1),
+ kRBSampleOnlyDepth = (1<<2),
+ };
+ enum { kKillFrames = 15 };
+
+ static void InitRenderBufferManager ();
+ static void CleanupRenderBufferManager ();
+
+ // Get a RenderTexture with the specific sizes
+ // If the width & height parameters uses autosizing, viewport size is taken from the current camera.
+ RenderTexture *GetTempBuffer (int width, int height, DepthBufferFormat depthFormat, RenderTextureFormat colorFormat, UInt32 flags, RenderTextureReadWrite colorSpace, int antiAliasing = 1);
+
+ // Release the temporary buffer.
+ void ReleaseTempBuffer (RenderTexture *rTex);
+
+ void GarbageCollect (int framesDelay = kKillFrames);
+
+ void Cleanup ();
+
+private:
+
+ typedef std::set<PPtr<RenderTexture>, std::less< PPtr<RenderTexture> > , memory_pool<PPtr<RenderTexture> > > TakenTextures;
+ typedef std::pair <int, PPtr<RenderTexture> > IntPPtrPair;
+ typedef std::list<IntPPtrPair, memory_pool<IntPPtrPair > > FreeTextures;
+
+ FreeTextures m_FreeTextures;
+ TakenTextures m_TakenTextures;
+ int m_TempBuffers;
+ int m_CurrentRBMFrame;
+};
+
+RenderBufferManager& GetRenderBufferManager ();
+RenderBufferManager* GetRenderBufferManagerPtr ();
+
+#endif
diff --git a/Runtime/Graphics/RenderSurface.h b/Runtime/Graphics/RenderSurface.h
new file mode 100644
index 0000000..c856a39
--- /dev/null
+++ b/Runtime/Graphics/RenderSurface.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+struct
+RenderSurfaceBase
+{
+ TextureID textureID;
+ int width;
+ int height;
+ int samples;
+ UInt32 flags;
+ bool colorSurface;
+ bool backBuffer;
+ bool shouldDiscard;
+ bool shouldClear;
+};
+
+// we dont want to enforce ctor, so lets do it as simple function
+inline void RenderSurfaceBase_Init(RenderSurfaceBase& rs)
+{
+ rs.textureID.m_ID = 0;
+ rs.width = rs.height = 0;
+ rs.samples = 1;
+ rs.flags = 0;
+ rs.shouldDiscard = rs.shouldClear = false;
+ rs.backBuffer = false;
+}
+
+inline void RenderSurfaceBase_InitColor(RenderSurfaceBase& rs)
+{
+ RenderSurfaceBase_Init(rs);
+ rs.colorSurface = true;
+}
+
+inline void RenderSurfaceBase_InitDepth(RenderSurfaceBase& rs)
+{
+ RenderSurfaceBase_Init(rs);
+ rs.colorSurface = false;
+}
diff --git a/Runtime/Graphics/RenderTexture.cpp b/Runtime/Graphics/RenderTexture.cpp
new file mode 100644
index 0000000..b47cf0f
--- /dev/null
+++ b/Runtime/Graphics/RenderTexture.cpp
@@ -0,0 +1,889 @@
+#include "UnityPrefix.h"
+#include "RenderTexture.h"
+#if UNITY_XENON
+#include "PlatformDependent/Xbox360/Source/GfxDevice/TexturesXenon.h"
+#endif
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Graphics/RenderSurface.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Utilities/MemoryPool.h"
+#include "Runtime/Misc/PlayerSettings.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Profiler/Profiler.h"
+
+PROFILER_INFORMATION(gGrabPixels, "RenderTexture.GrabPixels", kProfilerRender);
+PROFILER_INFORMATION(gSetRenderTargets, "RenderTexture.SetActive", kProfilerRender);
+
+static const int kRenderTextureFormatBPP[kRTFormatCount] = {
+ 4, // ARGB32
+ 4, // Depth various amounts on various HW, but we'll assume 4
+ 8, // ARGBHalf
+ 4, // Shadowmap various amounts on various HW, but we'll assume 4
+ 2, // RGB565
+ 2, // ARGB4444
+ 2, // ARGB1555
+ 4, // Default
+ 4, // A2R10G10B10
+ 8, // DefaultHDR
+ 8, // ARGB64
+ 16,// ARGBFloat
+ 8, // RGFloat
+ 4, // RGHalf
+ 4, // RFloat
+ 2, // RHalf
+ 1, // R8
+ 16,// ARGBInt
+ 8, // RGInt
+ 4, // RInt
+ 4, // BGRA32
+};
+
+static int kDepthFormatBPP[kDepthFormatCount] = {
+ 0, // None
+ 2, // 16
+ 4, // 24
+};
+
+
+typedef List< ListNode<RenderTexture> > RenderTextureList;
+RenderTextureList gRenderTextures;
+
+static bool gIsRenderTexEnabled = true;
+int gTemporarilyAllowIndieRenderTextures = 0;
+
+void RenderTexture::SetTemporarilyAllowIndieRenderTexture (bool allow)
+{
+ if (allow)
+ gTemporarilyAllowIndieRenderTextures++;
+ else
+ gTemporarilyAllowIndieRenderTextures--;
+}
+
+void RenderTexture::FindAndSetSRGBWrite( RenderTexture* newActive )
+{
+ bool wantLinear = GetActiveColorSpace() == kLinearColorSpace;
+ GetGfxDevice().SetSRGBWrite(newActive ? newActive->GetSRGBReadWrite() && wantLinear : wantLinear);
+}
+
+void RenderTexture::SetActive (RenderTexture* newActive, int mipLevel, CubemapFace face, UInt32 flags)
+{
+ newActive = EnsureRenderTextureIsCreated(newActive);
+
+ RenderSurfaceHandle newColorSurface = newActive ? newActive->m_ColorHandle : GetGfxDevice().GetBackBufferColorSurface();
+ RenderSurfaceHandle newDepthSurface = newActive ? newActive->m_DepthHandle : GetGfxDevice().GetBackBufferDepthSurface();
+ int mips = newActive && newActive->HasMipMap() ? mipLevel : 0;
+
+ SetActive (1, &newColorSurface, newDepthSurface, newActive, mips, face, flags);
+}
+
+bool RenderTexture::SetActive (int count, RenderSurfaceHandle* newColorSurfaces, RenderSurfaceHandle newDepthSurface, RenderTexture* rt, int mipLevel, CubemapFace face, UInt32 flags)
+{
+ DebugAssert(count > 0);
+ if( !IsEnabled() )
+ {
+ for (int i = 0; i < count; ++i)
+ newColorSurfaces[i].Reset();
+
+ // ??? should we keep them invalid just in case?
+ newColorSurfaces[0] = GetGfxDevice().GetBackBufferColorSurface();
+ newDepthSurface = GetGfxDevice().GetBackBufferDepthSurface();
+ }
+
+ int width = 1, height = 1;
+ if(newColorSurfaces[0].IsValid() && !newColorSurfaces[0].object->backBuffer)
+ {
+ width = newColorSurfaces[0].object->width;
+ height= newColorSurfaces[0].object->height;
+ }
+
+ // Clamp mip level to valid range
+ const int mipCount = CalculateMipMapCount3D(width, height, 1);
+ mipLevel = clamp(mipLevel, 0, mipCount-1);
+
+ GfxDevice& device = GetGfxDevice();
+ RenderSurfaceHandle oldColorSurface = device.GetActiveRenderColorSurface(0);
+ RenderSurfaceHandle oldDepthSurface = device.GetActiveRenderDepthSurface();
+
+ Assert(newColorSurfaces[0].IsValid() == newDepthSurface.IsValid());
+ bool renderTarget = newColorSurfaces[0].IsValid() && !newColorSurfaces[0].object->backBuffer;
+
+ if( oldColorSurface != newColorSurfaces[0] || (flags & kFlagForceResolve) )
+ {
+ // MSAA surface has to be resolved
+ RenderTexture* oldRt = GetActive();
+ if( oldRt && oldRt->IsAntiAliased() )
+ oldRt->ResolveAntiAliasedSurface();
+ }
+
+ UInt32 rtFlags = 0;
+ rtFlags |= (flags & kFlagDontRestoreColor) ? GfxDevice::kFlagDontRestoreColor : 0;
+ rtFlags |= (flags & kFlagDontRestoreDepth) ? GfxDevice::kFlagDontRestoreDepth : 0;
+ rtFlags |= (flags & kFlagForceResolve) ? GfxDevice::kFlagForceResolve : 0;
+
+ PROFILER_AUTO(gSetRenderTargets, rt);
+ device.SetRenderTargets (count, newColorSurfaces, newDepthSurface, mipLevel, face, rtFlags);
+ GPU_TIMESTAMP();
+
+ // we can call RenderTexture::SetActive before device init (d3d11)
+ if( oldColorSurface == newColorSurfaces[0] && oldDepthSurface == newDepthSurface
+ && newColorSurfaces[0].IsValid() && !newColorSurfaces[0].object->backBuffer
+ )
+ {
+ return false;
+ }
+
+
+ // NOTE: important to set ActiveRenderTexture to null/non-null value before setting up viewport (as that might involve
+ // flipping vertically based on whether there is a RT or not).
+ GetGfxDevice().SetActiveRenderTexture(rt);
+
+ // Setup the viewport for the render texture
+ if( !(flags & kFlagDontSetViewport) )
+ {
+ if (renderTarget)
+ {
+ width >>= mipLevel;
+ height >>= mipLevel;
+ device.SetViewport (0, 0, width, height);
+ }
+ else
+ {
+ // When switching to main window, restore the viewport to the one of the current
+ // camera.
+ const int* cameraViewPort = GetRenderManager().GetCurrentViewPort();
+ int viewcoord[4];
+ viewcoord[0] = cameraViewPort[0];
+ viewcoord[1] = cameraViewPort[1];
+ viewcoord[2] = cameraViewPort[2];
+ viewcoord[3] = cameraViewPort[3];
+
+ // Flip viewport just before applying to device, but don't store it flipped in the render manager
+ FlipScreenRectIfNeeded( device, viewcoord );
+ device.SetViewport( viewcoord[0], viewcoord[1], viewcoord[2], viewcoord[3] );
+ }
+ }
+
+ if (renderTarget)
+ {
+ // If texture coordinates go from top to bottom, then we need to
+ // invert projection matrix when rendering into a texture.
+ #if UNITY_WII
+ device.SetInvertProjectionMatrix( true );
+ #else
+ device.SetInvertProjectionMatrix( !device.UsesOpenGLTextureCoords() );
+ #endif
+ }
+ else
+ {
+ device.SetInvertProjectionMatrix( false );
+ }
+
+ return true;
+}
+
+
+bool RenderTexture::GetSupportedMipMapFlag(bool mipMap) const
+{
+ if (!gGraphicsCaps.hasAutoMipMapGeneration)
+ mipMap = false;
+ if (m_Dimension == kTexDimCUBE && gGraphicsCaps.buggyMipmappedCubemaps)
+ mipMap = false;
+ if (m_Dimension == kTexDim3D && gGraphicsCaps.buggyMipmapped3DTextures)
+ mipMap = false;
+ return mipMap;
+}
+
+
+bool RenderTexture::Create ()
+{
+ if( !IsEnabled() )
+ return false;
+
+ DestroySurfaces();
+ GfxDevice& device = GetGfxDevice();
+
+ if (m_Width <= 0 || m_Height <= 0)
+ {
+ ErrorString("RenderTexture.Create failed: width & height must be larger than 0");
+ return false;
+ }
+
+ if (m_Dimension == kTexDimCUBE && (!GetIsPowerOfTwo() || m_Width != m_Height))
+ {
+ ErrorString("RenderTexture.Create failed: cube maps must be power of two and width must match height");
+ return false;
+ }
+
+ if( !device.IsRenderTargetConfigValid(m_Width, m_Height, static_cast<RenderTextureFormat>(m_ColorFormat), static_cast<DepthBufferFormat>(m_DepthFormat)) )
+ {
+ if( GetIsPowerOfTwo() )
+ {
+ if (gGraphicsCaps.maxRenderTextureSize < 4)
+ {
+ ErrorString("RenderTexture.Create failed: maxRenderTextureSize is too small");
+ return false;
+ }
+
+ // Decrease too large render textures while maintaining aspect ratio
+ do
+ {
+ m_Width = std::max( m_Width / 2, 4 );
+ m_Height = std::max( m_Height / 2, 4 );
+ }
+ while ( !device.IsRenderTargetConfigValid(m_Width, m_Height, static_cast<RenderTextureFormat>(m_ColorFormat), static_cast<DepthBufferFormat>(m_DepthFormat)) );
+ }
+ else
+ {
+ ErrorString("RenderTexture.Create failed: requested size is too large.");
+ return false;
+ }
+ }
+
+ if( !gGraphicsCaps.supportsRenderTextureFormat[m_ColorFormat] )
+ {
+ ErrorString("RenderTexture.Create failed: format unsupported.");
+ return false;
+ }
+
+ if (!GetIsPowerOfTwo() && (gGraphicsCaps.npotRT == kNPOTNone))
+ {
+ ErrorString("RenderTexture.Create failed: non-power-of-two sizes not supported.");
+ return false;
+ }
+
+ if (m_Dimension == kTexDimCUBE && (!gGraphicsCaps.hasRenderToCubemap || IsDepthRTFormat((RenderTextureFormat)m_ColorFormat)))
+ {
+ ErrorString("RenderTexture.Create failed: cubemap not supported.");
+ return false;
+ }
+
+ if (m_Dimension == kTexDim3D && (!gGraphicsCaps.has3DTexture || !gGraphicsCaps.hasRenderTo3D))
+ {
+ ErrorString("RenderTexture.Create failed: volume texture not supported.");
+ return false;
+ }
+
+
+ bool mipMap = GetSupportedMipMapFlag(m_MipMap);
+ if (!GetIsPowerOfTwo())
+ mipMap = false;
+
+ int samples = m_AntiAliasing;
+ samples = clamp<int>(samples, 1, 8);
+ if (!gGraphicsCaps.hasMultiSample)
+ samples = 1;
+ if (m_Dimension != kTexDim2D)
+ samples = 1;
+
+ if (samples > 1)
+ mipMap = false;
+
+ // If we have native depth textures, we should use our regular textureID for the depth
+ // surface.
+ TextureID colorTID, resolvedColorTID, depthTID;
+ if ((m_ColorFormat == kRTFormatDepth && gGraphicsCaps.hasNativeDepthTexture) ||
+ (m_ColorFormat == kRTFormatShadowMap && gGraphicsCaps.hasNativeShadowMap))
+ {
+ depthTID = m_TexID;
+ m_SecondaryTexIDUsed = false;
+ }
+ else
+ {
+ // Use resolved surface as texture if MSAA enabled
+ if (samples > 1 && !gGraphicsCaps.hasMultiSampleAutoResolve)
+ resolvedColorTID = m_TexID;
+ else
+ colorTID = m_TexID;
+ // For regular non-MSAA render textures, also provide capability to treat depth buffer as a texture.
+ // Only do this if HW supports native depth textures AND texture is 2D AND
+ // we actually have a depth buffer AND driver is not broken for this case.
+ bool useDepthAsTexture = false;
+ if (m_Dimension == kTexDim2D && m_DepthFormat != kDepthFormatNone && samples <= 1)
+ useDepthAsTexture = gGraphicsCaps.hasStencilInDepthTexture && !gGraphicsCaps.buggyTextureBothColorAndDepth;
+ if (useDepthAsTexture)
+ {
+ depthTID = m_SecondaryTexID;
+ m_SecondaryTexIDUsed = true;
+ }
+ else
+ m_SecondaryTexIDUsed = false;
+ }
+
+ UInt32 colorFlags = 0;
+ if (mipMap) colorFlags |= kSurfaceCreateMipmap;
+ if (m_GenerateMips) colorFlags |= kSurfaceCreateAutoGenMips;
+ if (m_SRGB) colorFlags |= kSurfaceCreateSRGB;
+ if (m_EnableRandomWrite) colorFlags |= kSurfaceCreateRandomWrite;
+ if (colorTID == TextureID() && samples <= 1) colorFlags |= kSurfaceCreateNeverUsed; // never sampled or resolved
+ m_ColorHandle = device.CreateRenderColorSurface (colorTID,
+ m_Width, m_Height, samples, m_VolumeDepth, m_Dimension, static_cast<RenderTextureFormat>(m_ColorFormat), colorFlags);
+
+ if (samples > 1 && !gGraphicsCaps.hasMultiSampleAutoResolve)
+ {
+ m_ResolvedColorHandle = device.CreateRenderColorSurface (resolvedColorTID,
+ m_Width, m_Height, 1, m_VolumeDepth, m_Dimension, static_cast<RenderTextureFormat>(m_ColorFormat), colorFlags);
+ }
+
+ UInt32 depthFlags = 0;
+ if (m_ColorFormat==kRTFormatShadowMap) depthFlags |= kSurfaceCreateShadowmap;
+ if (m_SampleOnlyDepth) depthFlags |= kSurfaceCreateSampleOnly;
+ m_DepthHandle = device.CreateRenderDepthSurface (depthTID,
+ m_Width, m_Height, samples, m_Dimension, static_cast<DepthBufferFormat>(m_DepthFormat), depthFlags);
+
+ if (!(m_ColorHandle.IsValid() || m_DepthHandle.IsValid()))
+ {
+ ErrorString("RenderTexture.Create failed");
+ return false;
+ }
+
+ if (IsCreated())
+ {
+ Assert(m_RegisteredSizeForStats == 0);
+ m_RegisteredSizeForStats = GetRuntimeMemorySize();
+ device.GetFrameStats().ChangeRenderTextureBytes (m_RegisteredSizeForStats);
+ }
+
+ if (m_CreatedFromScript)
+ {
+ // We can't currently read the minds of users, so let's always restore their render targets.
+ // TODO: extend RenderTexture API
+ device.SetSurfaceFlags(m_ColorHandle, GfxDevice::kSurfaceAlwaysRestore, ~GfxDevice::kSurfaceRestoreMask);
+ device.SetSurfaceFlags(m_DepthHandle, GfxDevice::kSurfaceAlwaysRestore, ~GfxDevice::kSurfaceRestoreMask);
+ }
+
+ SetStoredColorSpaceNoDirtyNoApply(m_SRGB ? kTexColorSpaceSRGB : kTexColorSpaceLinear);
+
+ // Set filtering, etc. mode
+ ApplySettings();
+
+ UpdateTexelSize();
+
+ return true;
+}
+
+void RenderTexture::UpdateTexelSize()
+{
+ SetUVScale( 1.0f, 1.0f );
+ if (m_Width != 0 && m_Height != 0)
+ {
+ int width = m_Width;
+ int height = m_Height;
+ #if GFX_EMULATES_NPOT_RENDERTEXTURES
+ width = NextPowerOfTwo (width);
+ height = NextPowerOfTwo (height);
+ #endif
+ SetTexelSize( 1.0f / width, 1.0f / height );
+ }
+}
+
+
+void RenderTexture::ApplySettings()
+{
+ TextureDimension dim = GetDimension();
+ bool mip = HasMipMap();
+ // Don't ever use Aniso on depth textures or textures where we
+ // sample depth buffer.
+ bool depth = IsDepthRTFormat((RenderTextureFormat)m_ColorFormat);
+ if (depth || m_SecondaryTexIDUsed)
+ m_TextureSettings.m_Aniso = 0;
+
+ m_TextureSettings.Apply (GetTextureID(), dim, mip, GetActiveTextureColorSpace());
+ if (m_SecondaryTexIDUsed)
+ m_TextureSettings.Apply (m_SecondaryTexID, dim, mip, GetActiveTextureColorSpace());
+ NotifyMipBiasChanged();
+}
+
+
+void RenderTexture::Release()
+{
+ if (GetGfxDevice().GetActiveRenderTexture() == this)
+ {
+ ErrorString("Releasing render texture that is set to be RenderTexture.active!");
+ GetGfxDevice().SetActiveRenderTexture(NULL);
+ }
+ DestroySurfaces();
+}
+
+void RenderTexture::DestroySurfaces()
+{
+ if(!IsCreated())
+ return;
+
+ GfxDevice& device = GetGfxDevice();
+ // Different graphics caps can change the calculated runtime size (case 555046)
+ // so we store the size we added and make sure to subtract the same amount.
+ // Graphics caps can change during an editor session due to emulation.
+ device.GetFrameStats().ChangeRenderTextureBytes (-m_RegisteredSizeForStats);
+ m_RegisteredSizeForStats = 0;
+ if( m_ColorHandle.IsValid() )
+ device.DestroyRenderSurface (m_ColorHandle);
+ if( m_ResolvedColorHandle.IsValid() )
+ device.DestroyRenderSurface (m_ResolvedColorHandle);
+ if( m_DepthHandle.IsValid() )
+ device.DestroyRenderSurface (m_DepthHandle);
+}
+
+void RenderTexture::DiscardContents()
+{
+ if(IsCreated())
+ RenderTextureDiscardContents(this, true, true);
+
+}
+void RenderTexture::DiscardContents(bool discardColor, bool discardDepth)
+{
+ if(IsCreated())
+ RenderTextureDiscardContents(this, discardColor, discardDepth);
+}
+
+
+void RenderTexture::MarkRestoreExpected()
+{
+ GfxDevice& device = GetGfxDevice();
+ device.IgnoreNextUnresolveOnRS (m_ColorHandle);
+ device.IgnoreNextUnresolveOnRS (m_DepthHandle);
+ device.IgnoreNextUnresolveOnRS (m_ResolvedColorHandle);
+}
+
+
+void RenderTexture::GrabPixels (int left, int bottom, int width, int height)
+{
+ if (!IsCreated())
+ Create();
+
+ const RenderSurfaceHandle& handle = IsAntiAliased() ? m_ResolvedColorHandle : m_ColorHandle;
+
+ if (!handle.IsValid())
+ return;
+
+ if (left < 0) {
+ width += left;
+ left = 0;
+ }
+ if (bottom < 0) {
+ height += bottom;
+ bottom = 0;
+ }
+ if (width > m_Width)
+ width = m_Width;
+ if (height > m_Height)
+ height = m_Height;
+
+ PROFILER_AUTO(gGrabPixels, NULL);
+ GfxDevice& device = GetGfxDevice();
+ device.GrabIntoRenderTexture (handle, m_DepthHandle, left, bottom, width, height);
+ GPU_TIMESTAMP();
+ device.GetFrameStats().AddRenderTextureChange(); // treat resolve as RT change
+}
+void RenderTexture::CorrectVerticalTexelSize(bool shouldBePositive)
+{
+ if (!GetGfxDevice().UsesOpenGLTextureCoords())
+ {
+ if (m_TexelSizeY < 0.0f && shouldBePositive) m_TexelSizeY = -m_TexelSizeY;
+ else if (m_TexelSizeY > 0.0f && !shouldBePositive) m_TexelSizeY = -m_TexelSizeY;
+ }
+}
+RenderTexture::RenderTexture (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_DepthFormat(kDepthFormat24)
+, m_ColorFormat(kRTFormatARGB32)
+, m_Dimension(kTexDim2D)
+, m_MipMap(false)
+, m_GenerateMips(true)
+, m_SRGB(false)
+, m_EnableRandomWrite(false)
+, m_CreatedFromScript(false)
+, m_SampleOnlyDepth(false)
+, m_RegisteredSizeForStats(0)
+, m_RenderTexturesNode(this)
+{
+ m_Width = 256;
+ m_Height = 256;
+ m_VolumeDepth = 1;
+ m_AntiAliasing = 1;
+
+ GetSettings().m_WrapMode = kTexWrapClamp;
+
+ // We use unchecked version since we may not be on the main thread
+ // This means CreateTextureID() implementation must be thread safe!
+ m_SecondaryTexID = GetUncheckedGfxDevice().CreateTextureID();
+ m_SecondaryTexIDUsed = false;
+}
+
+RenderTexture::~RenderTexture ()
+{
+ Release();
+ m_RenderTexturesNode.RemoveFromList();
+ Texture::s_TextureIDMap.erase (m_SecondaryTexID);
+ // FreeTextureID() implementation must be thread safe!
+ GetUncheckedGfxDevice().FreeTextureID(m_SecondaryTexID);
+}
+
+void RenderTexture::SetDimension (TextureDimension dim)
+{
+ if (m_Dimension == dim)
+ return;
+
+ if (!IsCreated ())
+ m_Dimension = dim;
+ else
+ ErrorString ("Setting dimension of already created render texture is not supported!");
+}
+
+void RenderTexture::SetVolumeDepth (int v)
+{
+ if (m_VolumeDepth == v)
+ return;
+
+ if (!IsCreated ()) {
+ m_VolumeDepth = v;
+ } else
+ ErrorString ("Setting volume depth of already created render texture is not supported!");
+}
+
+void RenderTexture::SetAntiAliasing (int aa)
+{
+ if (m_AntiAliasing == aa)
+ return;
+
+ if (aa < 1 || aa > 8 || !IsPowerOfTwo(aa))
+ {
+ ErrorString( "Invalid antiAliasing value (must be 1, 2, 4 or 8)" );
+ return;
+ }
+
+ if (!IsCreated ()) {
+ m_AntiAliasing = aa;
+ } else
+ ErrorString ("Setting anti-aliasing of already created render texture is not supported!");
+}
+
+
+void RenderTexture::SetMipMap( bool mipMap )
+{
+ mipMap = GetSupportedMipMapFlag(mipMap);
+
+ if (mipMap == m_MipMap)
+ return;
+
+ if (!IsCreated ()) {
+ m_MipMap = mipMap;
+ } else {
+ ErrorString ("Setting mipmap mode of render texture that is loaded not supported!");
+ }
+}
+
+void RenderTexture::SetGenerateMips (bool v)
+{
+ if (v == m_GenerateMips)
+ return;
+
+ if (!IsCreated ())
+ {
+ if (m_MipMap && m_DepthFormat != kDepthFormatNone && !v)
+ {
+ WarningStringObject ("Mipmapped RenderTextures with manual mip generation can't have depth buffer", this);
+ v = true;
+ }
+ m_GenerateMips = v;
+ }
+ else
+ ErrorString ("Can't change mipmap generation of already created RenderTexture");
+}
+
+void RenderTexture::SetSRGBReadWrite ( bool sRGB )
+{
+ if (!IsCreated ()) {
+ bool setSRGB = sRGB ? (GetActiveColorSpace() == kLinearColorSpace) : false;
+ setSRGB = setSRGB && (m_ColorFormat != GetGfxDevice().GetDefaultHDRRTFormat());
+ m_SRGB = setSRGB;
+ } else
+ ErrorString ("Changing srgb mode of render texture that is loaded not supported!");
+}
+
+void RenderTexture::SetEnableRandomWrite (bool v)
+{
+ if (v == m_EnableRandomWrite)
+ return;
+
+ if (!IsCreated ()) {
+ m_EnableRandomWrite = v;
+ } else
+ ErrorString ("Can't change random write mode of already created render texture!");
+}
+
+void RenderTexture::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ m_Width = std::max (1, m_Width);
+ m_Height = std::max (1, m_Height);
+ m_VolumeDepth = std::max (1, m_VolumeDepth);
+ m_AntiAliasing = clamp<int> (m_AntiAliasing, 1, 8);
+
+ if( IsDepthRTFormat( (RenderTextureFormat)m_ColorFormat ) )
+ m_MipMap = false;
+ if (m_Dimension==kTexDimCUBE)
+ m_Height = m_Width;
+
+ if( !GetIsPowerOfTwo() && GetSettings().m_WrapMode == kTexWrapRepeat )
+ GetSettings().m_WrapMode = kTexWrapClamp;
+
+ if( IsDepthRTFormat((RenderTextureFormat)m_ColorFormat) ) // always clamp depth textures
+ GetSettings().m_WrapMode = kTexWrapClamp;
+
+#if GFX_OPENGLESxx_ONLY
+ // currently i have no idea about possibility of linear filter for fp rts (seems like gl do support it)
+ if( IsHalfRTFormat((RenderTextureFormat)m_ColorFormat) && !gGraphicsCaps.gles20.hasHalfLinearFilter )
+ GetSettings().m_FilterMode = kTexFilterNearest;
+#endif
+
+ gRenderTextures.push_back(m_RenderTexturesNode);
+
+ UpdateTexelSize();
+
+ Super::AwakeFromLoad (awakeMode);
+}
+
+void RenderTexture::SetWidth (int width)
+{
+ if (!IsCreated ()) {
+ m_Width = width;
+ UpdateTexelSize();
+ SetDirty();
+ } else
+ ErrorString ("Resizing of render texture that is loaded not supported!");
+}
+
+void RenderTexture::SetHeight (int height)
+{
+ if (!IsCreated ()) {
+ m_Height = height;
+ UpdateTexelSize();
+ SetDirty();
+ } else
+ ErrorString ("Resizing of render texture that is loaded not supported!");
+}
+
+void RenderTexture::SetDepthFormat( DepthBufferFormat depth )
+{
+ if (!IsCreated ())
+ m_DepthFormat = depth;
+ else
+ ErrorString ("Setting depth of render texture that is loaded not supported!");
+}
+
+void RenderTexture::SetColorFormat( RenderTextureFormat format )
+{
+ if (!IsCreated ())
+ {
+ if( format == kRTFormatDefault )
+ format = GetGfxDevice().GetDefaultRTFormat();
+
+ m_ColorFormat = format;
+ if (IsDepthRTFormat(format)) m_TextureSettings.m_Aniso = 0; // never use Anisotropic on depth textures
+ } else
+ ErrorString ("Changing format of render texture that is loaded not supported!");
+}
+
+
+RenderTexture* RenderTexture::GetActive ()
+{
+ return GetGfxDevice().GetActiveRenderTexture();
+}
+
+ShaderLab::TexEnv *RenderTexture::SetGlobalProperty (const ShaderLab::FastPropertyName& name)
+{
+ ShaderLab::TexEnv *te = ShaderLab::g_GlobalProperties->SetTexture (name, this);
+ te->ClearMatrix();
+ return te;
+}
+
+bool RenderTexture::IsEnabled ()
+{
+ if (gGraphicsCaps.hasRenderToTexture)
+ return gIsRenderTexEnabled && (GetBuildSettings().hasRenderTexture || gTemporarilyAllowIndieRenderTextures);
+ else
+ return false;
+}
+
+void RenderTexture::SetEnabled (bool enable)
+{
+ if (!enable)
+ ReleaseAll();
+
+ gIsRenderTexEnabled = enable;
+}
+
+void RenderTexture::ReleaseAll ()
+{
+ SetActive (NULL);
+
+ for (RenderTextureList::iterator i=gRenderTextures.begin ();i != gRenderTextures.end ();i++)
+ (**i).Release ();
+}
+
+
+#if ENABLE_PROFILER || UNITY_EDITOR
+
+int RenderTexture::GetCreatedRenderTextureCount ()
+{
+ int count = 0;
+ for (RenderTextureList::iterator i=gRenderTextures.begin ();i != gRenderTextures.end ();i++)
+ {
+ if ((**i).IsCreated ())
+ count++;
+ }
+
+ return count;
+}
+
+int RenderTexture::GetCreatedRenderTextureBytes ()
+{
+ int count = 0;
+ for (RenderTextureList::iterator i=gRenderTextures.begin ();i != gRenderTextures.end ();i++)
+ {
+ if ((**i).IsCreated ())
+ count += (**i).GetRuntimeMemorySize();
+ }
+
+ return count;
+}
+
+#endif
+
+
+
+int EstimateRenderTextureSize (int width, int height, int depth, RenderTextureFormat format, DepthBufferFormat depthFormat, TextureDimension dim, bool mipMap)
+{
+ // color buffer
+ int colorBPP;
+ if (format == kRTFormatDepth && gGraphicsCaps.hasNativeDepthTexture)
+ colorBPP = 0;
+ else if (format == kRTFormatShadowMap && gGraphicsCaps.hasNativeShadowMap)
+ colorBPP = 0;
+ else
+ colorBPP = kRenderTextureFormatBPP[format];
+ int size = width * height * colorBPP;
+
+ if (dim == kTexDim3D)
+ size *= depth;
+ else if (dim == kTexDimCUBE)
+ size *= 6;
+ if (mipMap && gGraphicsCaps.hasAutoMipMapGeneration)
+ size += size / 3;
+
+ // depth buffer
+ size += width * height * kDepthFormatBPP[depthFormat];
+ return size;
+}
+
+
+
+int RenderTexture::GetRuntimeMemorySize() const
+{
+ int bytes = EstimateRenderTextureSize (m_Width, m_Height, m_VolumeDepth, (RenderTextureFormat)m_ColorFormat, (DepthBufferFormat)m_DepthFormat, m_Dimension, m_MipMap);
+ bytes *= m_AntiAliasing;
+ return bytes;// + Super::GetRuntimeMemorySize(); Since the name is assigned after this value is used for gfxstats accumulate, the string will add to the mem usage later - Causes Assert
+}
+
+void RenderTexture::ResolveAntiAliasedSurface()
+{
+ if (!m_ResolvedColorHandle.IsValid())
+ return;
+
+ GfxDevice& device = GetGfxDevice();
+ device.ResolveColorSurface(m_ColorHandle, m_ResolvedColorHandle);
+}
+
+RenderTexture* EnsureRenderTextureIsCreated(RenderTexture* tex)
+{
+ RenderTexture* ret = tex;
+
+ if(!RenderTexture::IsEnabled())
+ ret = 0;
+
+ if(ret && !ret->IsCreated())
+ ret->Create();
+
+ // check if we could create
+ if(ret && !ret->IsCreated())
+ ret = 0;
+
+ return ret;
+}
+
+template<class TransferFunction>
+void RenderTexture::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER (m_Width);
+ TRANSFER (m_Height);
+ TRANSFER (m_AntiAliasing);
+ TRANSFER (m_DepthFormat);
+ TRANSFER (m_ColorFormat);
+ TRANSFER (m_MipMap);
+ TRANSFER (m_GenerateMips);
+ TRANSFER (m_SRGB);
+ transfer.Align();
+ TRANSFER (m_TextureSettings);
+}
+
+IMPLEMENT_CLASS (RenderTexture)
+IMPLEMENT_OBJECT_SERIALIZE (RenderTexture)
+
+bool RenderTextureSupportsStencil (RenderTexture* rt)
+{
+ if(!(gGraphicsCaps.hasStencil && GetBuildSettings ().hasAdvancedVersion))
+ return false;
+
+ if (rt)
+ return rt->GetDepthFormat() >= kDepthFormat24;
+
+ return GetGfxDevice().GetFramebufferDepthFormat() >= kDepthFormat24;
+}
+
+void RenderTextureDiscardContents(RenderTexture* rt, bool discardColor, bool discardDepth)
+{
+ GfxDevice& device = GetGfxDevice();
+
+ RenderSurfaceHandle color = rt ? rt->GetColorSurfaceHandle() : device.GetBackBufferColorSurface();
+ RenderSurfaceHandle rcolor = rt ? rt->GetResolvedColorSurfaceHandle() : RenderSurfaceHandle();
+ RenderSurfaceHandle depth = rt ? rt->GetDepthSurfaceHandle() : device.GetBackBufferDepthSurface();
+
+ if(discardColor && color.IsValid())
+ device.DiscardContents(color);
+ if(discardColor && rcolor.IsValid())
+ device.DiscardContents(rcolor);
+ if(discardDepth && depth.IsValid())
+ device.DiscardContents(depth);
+}
+
+
+// --------------------------------------------------------------------------
+
+
+#if ENABLE_UNIT_TESTS
+#include "External/UnitTest++/src/UnitTest++.h"
+SUITE (RenderTextureTests)
+{
+TEST(RenderTextureTests_BPPTableCorrect)
+{
+ // checks that you did not forget to update BPP counts when you added a new format :)
+ for (int i = 0; i < kRTFormatCount; ++i)
+ {
+ CHECK(kRenderTextureFormatBPP[i] != 0);
+ }
+ for (int i = 1; i < kDepthFormatCount; ++i) // skip DepthFormatNone, since it is expected to have zero
+ {
+ CHECK(kDepthFormatBPP[i] != 0);
+ }
+}
+}
+
+#endif
diff --git a/Runtime/Graphics/RenderTexture.h b/Runtime/Graphics/RenderTexture.h
new file mode 100644
index 0000000..44e7281
--- /dev/null
+++ b/Runtime/Graphics/RenderTexture.h
@@ -0,0 +1,210 @@
+#pragma once
+
+#include "Texture.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/GfxDevice/GfxDeviceObjects.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Modules/ExportModules.h"
+
+namespace ShaderLab {
+ struct FastPropertyName;
+ class TexEnv;
+}
+
+enum {
+ kScreenOffscreen = -1,
+};
+
+
+class EXPORT_COREMODULE RenderTexture : public Texture
+{
+public:
+
+ DECLARE_OBJECT_SERIALIZE (RenderTexture)
+ REGISTER_DERIVED_CLASS (RenderTexture, Texture)
+
+ RenderTexture (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~RenderTexture (); declared-by-macro
+
+ void SetWidth (int width);
+ int GetWidth () const { return m_Width; }
+
+ void SetHeight (int height);
+ int GetHeight () const { return m_Height; }
+
+ void SetDepthFormat( DepthBufferFormat depth );
+ DepthBufferFormat GetDepthFormat() const { return static_cast<DepthBufferFormat>(m_DepthFormat); }
+
+ bool GetIsPowerOfTwo () const { return IsPowerOfTwo(m_Width) && IsPowerOfTwo(m_Height); }
+
+ void SetDimension (TextureDimension dim);
+ virtual TextureDimension GetDimension() const { return m_Dimension; }
+
+ void SetVolumeDepth(int v);
+ int GetVolumeDepth() const { return m_VolumeDepth; }
+
+ void SetAntiAliasing(int aa);
+ int GetAntiAliasing() const { return m_AntiAliasing; }
+ bool IsAntiAliased() const { return m_AntiAliasing > 1; }
+
+ void SetMipMap( bool mipmap );
+ bool GetMipMap() const { return m_MipMap; }
+
+ void SetGenerateMips (bool v);
+ bool GetGenerateMips() const { return m_GenerateMips; }
+
+ void SetSRGBReadWrite(bool sRGB);
+ bool GetSRGBReadWrite() const { return m_SRGB; }
+
+ void SetEnableRandomWrite(bool v);
+ bool GetEnableRandomWrite() const { return m_EnableRandomWrite; }
+
+ void SetCreatedFromScript( bool fromScript ) { m_CreatedFromScript = fromScript; }
+ bool GetCreatedFromScript() const { return m_CreatedFromScript; }
+
+ void SetSampleOnlyDepth( bool sampleOnly ) { m_SampleOnlyDepth = sampleOnly; }
+ bool GetSampleOnlyDepth() const { return m_SampleOnlyDepth; }
+
+ RenderTextureFormat GetColorFormat() const { return static_cast<RenderTextureFormat>(m_ColorFormat); }
+ void SetColorFormat( RenderTextureFormat format );
+
+ virtual bool HasMipMap () const { return m_MipMap; }
+ virtual int CountMipmaps() const { return 1; } //@TODO ?
+
+ enum {
+ kFlagDontSetViewport = (1<<0),
+ kFlagForceResolve = (1<<1), // Force resolve to texture: Used by MSAA targets and Xbox 360
+ kFlagDontRestoreColor = (1<<2), // Xbox 360 specific: do not restore old contents to EDRAM
+ kFlagDontRestoreDepth = (1<<3), // Xbox 360 specific: do not restore old contents to EDRAM
+ kFlagDontRestore = kFlagDontRestoreColor | kFlagDontRestoreDepth,
+ };
+
+ static void FindAndSetSRGBWrite( RenderTexture* newActive );
+
+ // Makes the render texture the current render target. If texture is NULL the back buffer is activated.
+ static void SetActive( RenderTexture* texture, int mipLevel = 0, CubemapFace face = kCubeFaceUnknown, UInt32 flags = 0 );
+ static bool SetActive(int count, RenderSurfaceHandle* colors, RenderSurfaceHandle depth, RenderTexture* rt, int mipLevel = 0, CubemapFace face=kCubeFaceUnknown, UInt32 flags=0);
+
+ // Returns the active render texture.
+ // NULL means the main window is active.
+ static RenderTexture* GetActive();
+
+ // Does card support RTs, and built with Unity Pro, and not manually disabled?
+ static bool IsEnabled ();
+ // this should not be used... it's there only to support RenderTexture.enabled in scripts, in case anyone uses it.
+ static void SetEnabled (bool enable);
+
+ // Destroys all render textures created
+ static void ReleaseAll ();
+
+ // Creates the render texture.
+ // Create is automatically called inside Activate the first time.
+ // Create can fail if RenderTexture::IsEnabled is false
+ bool Create ();
+
+ // Destroys the render texture
+ void Release ();
+
+ // Is the render texture created?
+ bool IsCreated() const { return m_ColorHandle.IsValid() || m_DepthHandle.IsValid(); }
+
+ // Discards the contents of this render texture
+ // Xbox 360: will not restore contents to EDRAM the next time this RenderTexture is set.
+ // GLES: will use combo of glDiscardFramebufferEXT and glClear to avoid both resolve and restore
+ // Other platforms: currently no effect.
+ // NB: we have both because no-arg one had been used directly from scripts, so we keep it for b/c
+ void DiscardContents();
+ void DiscardContents(bool discardColor, bool discardDepth);
+ void MarkRestoreExpected();
+
+ virtual bool ExtractImage (ImageReference* /*image*/, int /*imageIndex*/ = 0) const { return false; }
+ virtual void ApplySettings();
+
+ virtual int GetRuntimeMemorySize() const;
+
+
+ #if UNITY_EDITOR
+ TextureFormat GetEditorUITextureFormat () const { return -1; }
+ #endif
+
+ #if ENABLE_PROFILER || UNITY_EDITOR
+ virtual int GetStorageMemorySize() const { return 0; }
+ static int GetCreatedRenderTextureCount ();
+ static int GetCreatedRenderTextureBytes ();
+ #endif
+
+
+ RenderSurfaceHandle GetColorSurfaceHandle() { return m_ColorHandle; }
+ RenderSurfaceHandle GetResolvedColorSurfaceHandle() { return m_ResolvedColorHandle; }
+ RenderSurfaceHandle GetDepthSurfaceHandle() { return m_DepthHandle; }
+
+
+ virtual int GetDataWidth() const { return m_Width; }
+ virtual int GetDataHeight() const { return m_Height; }
+
+ void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ virtual void UnloadFromGfxDevice(bool /*forceUnloadAll*/) { }
+ virtual void UploadToGfxDevice() { }
+
+ ShaderLab::TexEnv *SetGlobalProperty (const ShaderLab::FastPropertyName& name);
+
+ void GrabPixels (int left, int bottom, int width, int height);
+
+ // Flips vertical texel size to positive or negative if we're not using OpenGL coords
+ void CorrectVerticalTexelSize(bool shouldBePositive);
+
+ const TextureID& GetSecondaryTextureID() { return m_SecondaryTexID; }
+
+ static void SetTemporarilyAllowIndieRenderTexture (bool allow);
+
+private:
+ void DestroySurfaces();
+ void ResolveAntiAliasedSurface();
+ void UpdateTexelSize();
+ bool GetSupportedMipMapFlag(bool mipMap) const;
+
+private:
+ int m_Width; ///< range {1, 20000}
+ int m_Height;///< range {1, 20000}
+ int m_AntiAliasing;///< enum { None = 1, 2xMSAA = 2, 4xMSAA = 4, 8xMSAA = 8 } Anti-aliasing
+ int m_VolumeDepth;///< range {1, 20000}
+ int m_ColorFormat; ///< enum { RGBA32 = 0, Depth texture = 1 } Color buffer format
+ int m_DepthFormat; ///< enum { No depth buffer = 0, 16 bit depth = 1, 24 bit depth = 2 } Depth buffer format
+ TextureDimension m_Dimension;
+ bool m_MipMap;
+ bool m_GenerateMips;
+ bool m_SRGB;
+ bool m_EnableRandomWrite;
+ bool m_CreatedFromScript;
+ bool m_SampleOnlyDepth;
+
+ TextureID m_SecondaryTexID;
+
+ RenderSurfaceHandle m_ColorHandle;
+ RenderSurfaceHandle m_ResolvedColorHandle;
+ RenderSurfaceHandle m_DepthHandle;
+
+ int m_RegisteredSizeForStats;
+
+ ListNode<RenderTexture> m_RenderTexturesNode;
+
+ bool m_SecondaryTexIDUsed;
+};
+
+int EstimateRenderTextureSize (int width, int height, int depth, RenderTextureFormat format, DepthBufferFormat depthFormat, TextureDimension dim, bool mipMap);
+
+// return or passed tex (but created if needed) or NULL if RT cannot be created by any reason
+RenderTexture* EnsureRenderTextureIsCreated(RenderTexture* tex);
+
+bool RenderTextureSupportsStencil(RenderTexture* rt);
+
+void RenderTextureDiscardContents(RenderTexture* rt, bool discardColor, bool discardDepth);
+
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+struct ScriptingRenderBuffer
+{
+ int m_RenderTextureInstanceID;
+ RenderSurfaceBase* m_BufferPtr;
+};
diff --git a/Runtime/Graphics/S3Decompression.cpp b/Runtime/Graphics/S3Decompression.cpp
new file mode 100644
index 0000000..b2f518d
--- /dev/null
+++ b/Runtime/Graphics/S3Decompression.cpp
@@ -0,0 +1,1882 @@
+#include "UnityPrefix.h"
+#include "S3Decompression.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Graphics/FlashATFDecompression.h"
+#include "Runtime/Graphics/ETC2Decompression.h"
+
+#define PRINT_DECOMPRESSION_TIMES 0
+#if PRINT_DECOMPRESSION_TIMES
+#include "Runtime/Input/TimeManager.h"
+#endif
+
+
+// \todo [2010-02-09 petri] Define in some config file?
+#define HAS_ETC_DECOMPRESSOR (UNITY_ANDROID && !i386 || UNITY_EDITOR || UNITY_BB10 || UNITY_TIZEN)
+#define HAS_ATC_DECOMPRESSOR (UNITY_ANDROID && !i386 || UNITY_EDITOR)
+
+#define HAS_ASTC_DECOMPRESSOR (UNITY_ANDROID || UNITY_EDITOR)
+
+#if HAS_ATC_DECOMPRESSOR
+ #include "External/Qualcomm_TextureConverter/TextureConverter.h"
+ #if UNITY_WIN
+ #include <process.h>
+ // On Windows, Qonvert is compiled with MSVCRT, but we link against LIBCMT; filling the gaps here
+ extern "C" int _imp___getpid(void){ return _getpid(); }
+ #elif UNITY_OSX
+ // On OSX, Qonvert is compiled with -fstack-protector, but we don't use it for the MacEditor
+ extern "C"
+ {
+ void* __stack_chk_guard = (void*)0xdeadc0de;
+ void __stack_chk_fail() { ErrorString("stack_chk_fail failed"); }
+ }
+ #elif defined(ARM_ARCH_VFP)
+ // On Android, Qonvert is compiled with armv5 (no vfp), but we don't want to include the softfp emulation
+ extern "C" unsigned __aeabi_f2uiz(float f){return (int)f;}
+ #endif
+#endif
+
+// ------------------------------------------------------------------------
+// DXT
+
+
+#if HAS_DXT_DECOMPRESSOR
+
+struct DXTColBlock
+{
+ UInt16 col0;
+ UInt16 col1;
+ UInt8 row[4];
+};
+
+struct DXTAlphaBlockExplicit
+{
+ UInt16 row[4];
+};
+
+struct DXTAlphaBlock3BitLinear
+{
+ UInt8 alpha0;
+ UInt8 alpha1;
+ UInt8 stuff[6];
+};
+
+#if UNITY_BIG_ENDIAN
+
+struct Color8888
+{
+ UInt8 b;
+ UInt8 g;
+ UInt8 r;
+ UInt8 a;
+};
+
+static inline UInt16 GetByteSwap16( UInt16 i )
+{
+ return static_cast<UInt16>((i << 8) | (i >> 8));
+}
+
+static inline UInt32 GetByteSwap32( UInt32 i )
+{
+ return static_cast<UInt32>((i >> 24) | (i >> 8) & 0x0000ff00 | (i << 8) & 0x00ff0000 | (i << 24));
+}
+
+struct Color565
+{
+ unsigned b : 5;
+ unsigned g : 6;
+ unsigned r : 5;
+};
+
+#else
+
+static inline UInt16 GetByteSwap16( UInt16 i ) { return i; }
+static inline UInt32 GetByteSwap32( UInt32 i ) { return i; }
+
+struct Color8888 // EH? This seems to be wrong!
+{
+ UInt8 r;
+ UInt8 g;
+ UInt8 b;
+ UInt8 a;
+};
+
+struct Color565
+{
+ unsigned b : 5;
+ unsigned g : 6;
+ unsigned r : 5;
+};
+
+#endif
+
+
+inline void GetColorBlockColors( const DXTColBlock* block, Color8888 colors[4] )
+{
+ union
+ {
+ Color565 color565;
+ UInt16 color16;
+ } col0, col1;
+ col0.color16 = GetByteSwap16( block->col0 );
+ col1.color16 = GetByteSwap16( block->col1 );
+
+ const Color565* col;
+
+ // It's not enough just to shift bits to full 8 bit precision - the lower bits
+ // must also be filled to match the way hardware does the rounding.
+ col = &col0.color565;
+ colors[0].r = ( col->r << 3 ) | ( col->r >> 2 );
+ colors[0].g = ( col->g << 2 ) | ( col->g >> 4 );
+ colors[0].b = ( col->b << 3 ) | ( col->b >> 2 );
+ colors[0].a = 0xff;
+
+ col = &col1.color565;
+ colors[1].r = ( col->r << 3 ) | ( col->r >> 2 );
+ colors[1].g = ( col->g << 2 ) | ( col->g >> 4 );
+ colors[1].b = ( col->b << 3 ) | ( col->b >> 2 );
+ colors[1].a = 0xff;
+
+ if( col0.color16 > col1.color16 )
+ {
+ // Four-color block: derive the other two colors.
+ // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
+ // These two bit codes correspond to the 2-bit fields
+ // stored in the 64-bit block.
+
+ colors[2].r = (UInt8)(((UInt16)colors[0].r * 2 + (UInt16)colors[1].r )/3);
+ colors[2].g = (UInt8)(((UInt16)colors[0].g * 2 + (UInt16)colors[1].g )/3);
+ colors[2].b = (UInt8)(((UInt16)colors[0].b * 2 + (UInt16)colors[1].b )/3);
+ colors[2].a = 0xff;
+
+ colors[3].r = (UInt8)(((UInt16)colors[0].r + (UInt16)colors[1].r *2 )/3);
+ colors[3].g = (UInt8)(((UInt16)colors[0].g + (UInt16)colors[1].g *2 )/3);
+ colors[3].b = (UInt8)(((UInt16)colors[0].b + (UInt16)colors[1].b *2 )/3);
+ colors[3].a = 0xff;
+ }
+ else
+ {
+ // Three-color block: derive the other color.
+ // 00 = color_0, 01 = color_1, 10 = color_2, 11 = transparent.
+ // These two bit codes correspond to the 2-bit fields
+ // stored in the 64-bit block.
+
+ colors[2].r = (UInt8)(((UInt16)colors[0].r + (UInt16)colors[1].r )/2);
+ colors[2].g = (UInt8)(((UInt16)colors[0].g + (UInt16)colors[1].g )/2);
+ colors[2].b = (UInt8)(((UInt16)colors[0].b + (UInt16)colors[1].b )/2);
+ colors[2].a = 0xff;
+
+ // set transparent to black to match DXT specs
+ colors[3].r = 0x00;
+ colors[3].g = 0x00;
+ colors[3].b = 0x00;
+ colors[3].a = 0x00;
+ }
+}
+
+// width is width of destination image in pixels
+inline void DecodeColorBlock( UInt32* dest, const DXTColBlock& colorBlock, int width, const UInt32 colors[4] )
+{
+ // r steps through lines in y
+ for( int r=0; r < 4; r++, dest += width-4 )
+ {
+ // width * 4 bytes per pixel per line
+ // each j block row is 4 lines of pixels
+
+ // Do four pixels, step in twos because n is only used for the shift
+ // n steps through pixels
+ for( int n = 0; n < 8; n += 2 )
+ {
+ UInt32 bits = (colorBlock.row[r] >> n) & 3;
+ DebugAssert (bits <= 3);
+ *dest = colors[bits];
+ ++dest;
+ }
+ }
+}
+
+inline void DecodeAlphaExplicit( UInt32* dest, const DXTAlphaBlockExplicit& alphaBlock, int width, UInt32 alphazero )
+{
+ // alphazero is a bit mask that when ANDed with the image color
+ // will zero the alpha bits, so if the image DWORDs are
+ // ARGB then alphazero will be 0x00FFFFFF or if
+ // RGBA then alphazero will be 0xFFFFFF00
+ // alphazero constructed automatically from field order of Color8888 structure
+
+ union
+ {
+ Color8888 col;
+ UInt32 col32;
+ } u;
+ u.col.r = u.col.g = u.col.b = 0;
+
+ for( int row=0; row < 4; row++, dest += width-4 )
+ {
+ UInt16 wrd = GetByteSwap16( alphaBlock.row[ row ] );
+
+ for( int pix = 0; pix < 4; ++pix )
+ {
+ // zero the alpha bits of image pixel
+ *dest &= alphazero;
+
+ u.col.a = wrd & 0x000f; // get lowest 4 bits
+ u.col.a = u.col.a | (u.col.a << 4);
+
+ *dest |= u.col32; // OR into the previously nulled alpha
+
+ wrd >>= 4;
+ ++dest;
+ }
+ }
+}
+
+inline void DecodeAlpha3BitLinear( UInt32* dest, const DXTAlphaBlock3BitLinear& alphaBlock, int width, UInt32 alphazero )
+{
+ union
+ {
+ Color8888 alphaCol[4][4];
+ UInt32 alphaCol32[4][4];
+ } u;
+ UInt32 alphamask = ~alphazero;
+ UInt16 alphas[8];
+
+ alphas[0] = alphaBlock.alpha0;
+ alphas[1] = alphaBlock.alpha1;
+
+ // 8-alpha or 6-alpha block?
+ if( alphas[0] > alphas[1] )
+ {
+ // 8-alpha block: derive the other 6 alphas.
+ // 000 = alpha[0], 001 = alpha[1], others are interpolated
+
+ alphas[2] = ( 6 * alphas[0] + alphas[1] + 3) / 7; // bit code 010
+ alphas[3] = ( 5 * alphas[0] + 2 * alphas[1] + 3) / 7; // Bit code 011
+ alphas[4] = ( 4 * alphas[0] + 3 * alphas[1] + 3) / 7; // Bit code 100
+ alphas[5] = ( 3 * alphas[0] + 4 * alphas[1] + 3) / 7; // Bit code 101
+ alphas[6] = ( 2 * alphas[0] + 5 * alphas[1] + 3) / 7; // Bit code 110
+ alphas[7] = ( alphas[0] + 6 * alphas[1] + 3) / 7; // Bit code 111
+ }
+ else
+ {
+ // 6-alpha block: derive the other alphas.
+ // 000 = alpha[0], 001 = alpha[1], others are interpolated
+
+ alphas[2] = (4 * alphas[0] + alphas[1] + 2) / 5; // Bit code 010
+ alphas[3] = (3 * alphas[0] + 2 * alphas[1] + 2) / 5; // Bit code 011
+ alphas[4] = (2 * alphas[0] + 3 * alphas[1] + 2) / 5; // Bit code 100
+ alphas[5] = ( alphas[0] + 4 * alphas[1] + 2) / 5; // Bit code 101
+ alphas[6] = 0; // Bit code 110
+ alphas[7] = 255; // Bit code 111
+ }
+
+ // Decode 3-bit fields into array of 16 bytes with same value
+ UInt8 blockBits[4][4];
+
+ // first two rows of 4 pixels each:
+ const UInt32 mask = 7; // three bits
+
+ // TBD: Ouch! Unaligned reads there! Poor old PPC!
+
+ UInt32 bits = GetByteSwap32( *(UInt32*)&alphaBlock.stuff[0] ); // first 3 bytes
+
+ blockBits[0][0] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[0][1] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[0][2] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[0][3] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[1][0] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[1][1] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[1][2] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[1][3] = (UInt8)( bits & mask );
+
+ // now last two rows
+ // it's ok to fetch one byte too much because there will always be color block after alpha
+ bits = GetByteSwap32( *(UInt32*)&alphaBlock.stuff[3] ); // last 3 bytes
+
+ blockBits[2][0] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[2][1] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[2][2] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[2][3] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[3][0] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[3][1] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[3][2] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[3][3] = (UInt8)( bits & mask );
+
+ // decode the codes into alpha values
+ int row, pix;
+
+ for( row = 0; row < 4; row++ )
+ {
+ for( pix=0; pix < 4; pix++ )
+ {
+ u.alphaCol[row][pix].a = (UInt8) alphas[ blockBits[row][pix] ];
+ }
+ }
+
+ // Write out alpha values to the image bits
+ for( row=0; row < 4; row++, dest += width-4 )
+ {
+ for( pix = 0; pix < 4; pix++ )
+ {
+ *dest &= alphazero; // zero the alpha bits of image pixel
+ *dest |= u.alphaCol32[row][pix] & alphamask; // or the bits into the prev. nulled alpha
+ dest++;
+ }
+ }
+}
+
+
+void DecompressDXT1( int xblocks, int yblocks, int destWidth, const UInt32* m_pCompBytes, UInt32* decompBytes )
+{
+ union
+ {
+ Color8888 colors8888[4];
+ UInt32 colors32[4];
+ } u;
+
+ for( int j = 0; j < yblocks; ++j )
+ {
+ const DXTColBlock* block = (const DXTColBlock*)( m_pCompBytes + j * xblocks * 2 ); // 8 bytes per block
+
+ for( int i = 0; i < xblocks; ++i, ++block )
+ {
+ GetColorBlockColors( block, u.colors8888 );
+ UInt32* dest = (UInt32*)((UInt8*)decompBytes + i*16 + (j*4) * destWidth * 4 );
+ DecodeColorBlock( dest, *block, destWidth, u.colors32 );
+ }
+ }
+}
+
+
+void DecompressDXT3( int xblocks, int yblocks, int destWidth, const UInt32* m_pCompBytes, UInt32* decompBytes )
+{
+ union
+ {
+ Color8888 colors[4];
+ UInt32 colors32[4];
+ } u;
+
+ // fill alphazero with appropriate value to zero out alpha when
+ // alphazero is ANDed with the image color
+ u.colors[0].a = 0;
+ u.colors[0].r = u.colors[0].g = u.colors[0].b = 0xff;
+ UInt32 alphazero = u.colors32[0];
+
+ for( int j = 0; j < yblocks; ++j )
+ {
+ const DXTColBlock* block = (const DXTColBlock*)( m_pCompBytes + j * xblocks * 4 ); // 16 bytes for alpha+color block
+
+ for( int i = 0; i < xblocks; ++i, ++block )
+ {
+ // Get alpha block
+ const DXTAlphaBlockExplicit* alphaBlock = (const DXTAlphaBlockExplicit*)block;
+ ++block;
+
+ // Get color block & colors
+ GetColorBlockColors( block, u.colors );
+
+ // Decode the color block into the bitmap bits
+ UInt32* dest = (UInt32*)((UInt8*)decompBytes + i*16 + (j*4) * destWidth * 4 );
+ DecodeColorBlock( dest, *block, destWidth, u.colors32 );
+
+ // Overwrite the previous alpha bits with the alpha block info
+ DecodeAlphaExplicit( dest, *alphaBlock, destWidth, alphazero );
+ }
+ }
+}
+
+
+void DecompressDXT5( int xblocks, int yblocks, int destWidth, const UInt32* m_pCompBytes, UInt32* decompBytes )
+{
+ union
+ {
+ Color8888 colors[4];
+ UInt32 colors32[4];
+ } u;
+
+ // fill alphazero with appropriate value to zero out alpha when
+ // alphazero is ANDed with the image color 32 bit
+ u.colors[0].a = 0;
+ u.colors[0].r = u.colors[0].g = u.colors[0].b = 0xff;
+ UInt32 alphazero = u.colors32[0];
+
+ for( int j = 0; j < yblocks; ++j )
+ {
+ const DXTColBlock* block = (const DXTColBlock*) ( m_pCompBytes + j * xblocks * 4 ); // 16 bytes for alpha+color block
+
+ for( int i = 0; i < xblocks; ++i, ++block )
+ {
+ // Get alpha block
+ const DXTAlphaBlock3BitLinear* alphaBlock = (const DXTAlphaBlock3BitLinear*)block;
+ block++;
+
+ // Get color block & colors
+ GetColorBlockColors( block, u.colors );
+
+ // Decode the color block into the bitmap bits
+ UInt32* dest = (UInt32*)((UInt8*)decompBytes + i*16 + (j*4) * destWidth * 4 );
+ DecodeColorBlock( dest, *block, destWidth, u.colors32 );
+
+ // Overwrite the previous alpha bits with the alpha block info
+ DecodeAlpha3BitLinear( dest, *alphaBlock, destWidth, alphazero );
+ }
+ }
+}
+
+#endif
+
+
+
+// ------------------------------------------------------------------------
+// PVRTC
+
+
+
+#if HAS_PVRTC_DECOMPRESSOR
+
+const int kPVRSizeX2 = 8; // block width 8 pixels in 2BPP case
+const int kPVRSizeX4 = 4; // block width 4 pixels in 4BPP case
+const int kPVRBlockSizeY = 4; // block height always 4 pixels
+
+const int kPVRPunchThroughIndex = 2;
+
+#define PVR_WRAP_POT_COORD(Val, Size) ((Val) & ((Size)-1))
+
+#define CLAMP(X, lower, upper) (std::min(std::max((X),(lower)), (upper)))
+
+#define PVR_LIMIT_COORD(Val, Size, AssumeImageTiles) \
+ ((AssumeImageTiles) ? PVR_WRAP_POT_COORD((Val), (Size)) : CLAMP((Val), 0, (Size)-1))
+
+// 64 bits per PVRTC block
+struct PVRTCBlock
+{
+ UInt32 packedData[2];
+};
+
+
+
+// Given a block, extract the color information and convert to 5554 formats
+static void Unpack5554Colour (const PVRTCBlock *pBlock, int ABColours[2][4])
+{
+ UInt32 RawBits[2];
+
+ int i;
+
+ /*
+ // Extract A and B
+ */
+ RawBits[0] = pBlock->packedData[1] & (0xFFFE); /*15 bits (shifted up by one)*/
+ RawBits[1] = pBlock->packedData[1] >> 16; /*16 bits*/
+
+ /*
+ //step through both colours
+ */
+ for(i = 0; i < 2; i++)
+ {
+ /*
+ // if completely opaque
+ */
+ if(RawBits[i] & (1<<15))
+ {
+ /*
+ // Extract R and G (both 5 bit)
+ */
+ ABColours[i][0] = (RawBits[i] >> 10) & 0x1F;
+ ABColours[i][1] = (RawBits[i] >> 5) & 0x1F;
+
+ /*
+ // The precision of Blue depends on A or B. If A then we need to
+ // replicate the top bit to get 5 bits in total
+ */
+ ABColours[i][2] = RawBits[i] & 0x1F;
+ if(i==0)
+ {
+ ABColours[0][2] |= ABColours[0][2] >> 4;
+ }
+
+ /*
+ // set 4bit alpha fully on...
+ */
+ ABColours[i][3] = 0xF;
+ }
+ /*
+ // Else if colour has variable translucency
+ */
+ else
+ {
+ /*
+ // Extract R and G (both 4 bit).
+ // (Leave a space on the end for the replication of bits
+ */
+ ABColours[i][0] = (RawBits[i] >> (8-1)) & 0x1E;
+ ABColours[i][1] = (RawBits[i] >> (4-1)) & 0x1E;
+
+ /*
+ // replicate bits to truly expand to 5 bits
+ */
+ ABColours[i][0] |= ABColours[i][0] >> 4;
+ ABColours[i][1] |= ABColours[i][1] >> 4;
+
+ /*
+ // grab the 3(+padding) or 4 bits of blue and add an extra padding bit
+ */
+ ABColours[i][2] = (RawBits[i] & 0xF) << 1;
+
+ /*
+ // expand from 3 to 5 bits if this is from colour A, or 4 to 5 bits if from
+ // colour B
+ */
+ if(i==0)
+ {
+ ABColours[0][2] |= ABColours[0][2] >> 3;
+ }
+ else
+ {
+ ABColours[0][2] |= ABColours[0][2] >> 4;
+ }
+
+ /*
+ // Set the alpha bits to be 3 + a zero on the end
+ */
+ ABColours[i][3] = (RawBits[i] >> 11) & 0xE;
+ }/*end if variable alpha*/
+ }/*end for i*/
+
+}
+
+
+// Given the block and the texture type and it's relative position in the
+// 2x2 group of blocks, extract the bit patterns for the fully defined pixels.
+template<bool Do2bitMode>
+static void UnpackModulations(const PVRTCBlock *pBlock,
+ int ModulationVals[8][16],
+ int ModulationModes[8][16],
+ int StartX,
+ int StartY)
+{
+ int BlockModMode= pBlock->packedData[1] & 1;
+ UInt32 ModulationBits = pBlock->packedData[0];
+
+ // if it's in an interpolated mode
+ if(Do2bitMode && BlockModMode)
+ {
+ // run through all the pixels in the block. Note we can now treat all the
+ // "stored" values as if they have 2bits (even when they didn't!)
+ for(int y = 0; y < kPVRBlockSizeY; y++)
+ {
+ for(int x = 0; x < kPVRSizeX2; x++)
+ {
+ ModulationModes[y+StartY][x+StartX] = BlockModMode;
+
+ // if this is a stored value...
+ if(((x^y)&1) == 0)
+ {
+ ModulationVals[y+StartY][x+StartX] = ModulationBits & 3;
+ ModulationBits >>= 2;
+ }
+ }
+ }
+ }
+ // else if direct encoded 2bit mode - i.e. 1 mode bit per pixel
+ else if(Do2bitMode)
+ {
+ for(int y = 0; y < kPVRBlockSizeY; y++)
+ {
+ for(int x = 0; x < kPVRSizeX2; x++)
+ {
+ ModulationModes[y+StartY][x+StartX] = BlockModMode;
+
+ // double the bits so 0=> 00, and 1=>11
+ if(ModulationBits & 1)
+ {
+ ModulationVals[y+StartY][x+StartX] = 0x3;
+ }
+ else
+ {
+ ModulationVals[y+StartY][x+StartX] = 0x0;
+ }
+ ModulationBits >>= 1;
+ }
+ }
+ }
+ // else its the 4bpp mode so each value has 2 bits
+ else
+ {
+ for(int y = 0; y < kPVRBlockSizeY; y++)
+ {
+ for(int x = 0; x < kPVRSizeX4; x++)
+ {
+ ModulationModes[y+StartY][x+StartX] = BlockModMode;
+ ModulationVals[y+StartY][x+StartX] = ModulationBits & 3;
+ ModulationBits >>= 2;
+ }
+ }
+ }
+
+ // make sure nothing is left over
+ DebugAssert(ModulationBits==0);
+}
+
+
+template<bool do2bitMode>
+static int GetUCoordPVR (int x)
+{
+ if (do2bitMode)
+ {
+ int u = (x & 0x7) | ((~x & 0x4) << 1);
+ return u - kPVRSizeX2/2;
+ }
+ else
+ {
+ int u = (x & 0x3) | ((~x & 0x2) << 1);
+ return u - kPVRSizeX4/2;
+ }
+}
+
+static int GetVCoordPVR (int y)
+{
+ int v = (y & 0x3) | ((~y & 0x2) << 1);
+ return v - kPVRBlockSizeY/2;
+}
+
+
+// Performs a HW bit accurate interpolation of either the
+// A or B colors for a particular pixel
+//
+// NOTE: It is assumed that the source colors are in ARGB 5554 format -
+// This means that some "preparation" of the values will be necessary.
+// NOTE: QP is Q-P, SR is S-R
+template<bool Do2bitMode>
+static void InterpolateColoursPVRTC(const int* __restrict P,
+ const int* __restrict QP,
+ const int* __restrict R,
+ const int* __restrict SR,
+ const int u,
+ const int v,
+ int* __restrict Result)
+{
+ int k;
+ int tmp1, tmp2;
+
+ int uscale = Do2bitMode ? 8 : 4;
+
+ for(k = 0; k < 4; k++)
+ {
+ tmp1 = P[k] * uscale + u * QP[k];
+ tmp2 = R[k] * uscale + u * SR[k];
+
+ tmp1 = tmp1 * 4 + v * (tmp2 - tmp1);
+
+ Result[k] = tmp1;
+ }
+
+ /*
+ // Lop off the appropriate number of bits to get us to 8 bit precision
+ */
+ if(Do2bitMode)
+ {
+ /*
+ // do RGB
+ */
+ for(k = 0; k < 3; k++)
+ {
+ Result[k] >>= 2;
+ }
+
+ Result[3] >>= 1;
+ }
+ else
+ {
+ /*
+ // do RGB (A is ok)
+ */
+ for(k = 0; k < 3; k++)
+ {
+ Result[k] >>= 1;
+ }
+ }
+
+ /*
+ // sanity check
+ */
+ for(k = 0; k < 4; k++)
+ {
+ DebugAssert(Result[k] < 256);
+ }
+
+
+ /*
+ // Convert from 5554 to 8888
+ //
+ // do RGB 5.3 => 8
+ */
+ for(k = 0; k < 3; k++)
+ {
+ Result[k] += Result[k] >> 5;
+ }
+ Result[3] += Result[3] >> 4;
+
+ /*
+ // 2nd sanity check
+ */
+ for(k = 0; k < 4; k++)
+ {
+ DebugAssert(Result[k] < 256);
+ }
+
+}
+
+
+// Get the modulation value as a numerator of a fraction of 8ths
+template<bool Do2bitMode>
+static void GetModulationValue(int x,
+ int y,
+ const int ModulationVals[8][16],
+ const int ModulationModes[8][16],
+ int *Mod,
+ int *DoPT)
+{
+ static const int RepVals0[4] = {0, 3, 5, 8};
+ static const int RepVals1[4] = {0, 4, 4, 8};
+
+ int ModVal;
+
+ /*
+ // Map X and Y into the local 2x2 block
+ */
+ y = (y & 0x3) | ((~y & 0x2) << 1);
+ if(Do2bitMode)
+ {
+ x = (x & 0x7) | ((~x & 0x4) << 1);
+
+ }
+ else
+ {
+ x = (x & 0x3) | ((~x & 0x2) << 1);
+ }
+
+ /*
+ // assume no PT for now
+ */
+ *DoPT = 0;
+
+ /*
+ // extract the modulation value. If a simple encoding
+ */
+ if(ModulationModes[y][x]==0)
+ {
+ ModVal = RepVals0[ModulationVals[y][x]];
+ }
+ else if(Do2bitMode)
+ {
+ /*
+ // if this is a stored value
+ */
+ if(((x^y)&1)==0)
+ {
+ ModVal = RepVals0[ModulationVals[y][x]];
+ }
+ /*
+ // else average from the neighbours
+ //
+ // if H&V interpolation...
+ */
+ else if(ModulationModes[y][x] == 1)
+ {
+ ModVal = (RepVals0[ModulationVals[y-1][x]] +
+ RepVals0[ModulationVals[y+1][x]] +
+ RepVals0[ModulationVals[y][x-1]] +
+ RepVals0[ModulationVals[y][x+1]] + 2) / 4;
+ }
+ /*
+ // else if H-Only
+ */
+ else if(ModulationModes[y][x] == 2)
+ {
+ ModVal = (RepVals0[ModulationVals[y][x-1]] +
+ RepVals0[ModulationVals[y][x+1]] + 1) / 2;
+ }
+ /*
+ // else it's V-Only
+ */
+ else
+ {
+ ModVal = (RepVals0[ModulationVals[y-1][x]] +
+ RepVals0[ModulationVals[y+1][x]] + 1) / 2;
+
+ }/*end if/then/else*/
+ }
+ /*
+ // else it's 4BPP and PT encoding
+ */
+ else
+ {
+ ModVal = RepVals1[ModulationVals[y][x]];
+
+ *DoPT = ModulationVals[y][x] == kPVRPunchThroughIndex;
+ }
+
+ *Mod =ModVal;
+}
+
+
+
+// PVRTC UV twiddling interleaves bits of Y & X, like: XYXYXYXYXY.
+// If size of one coordinate is larger, those bits are just copied into higher bits;
+// e.g. XXXX,YYYYYYYY = YYYYXYXYXYXY.
+
+static UInt32 TwiddleY_PVRTC(UInt32 YSize, UInt32 XSize, UInt32 YPos)
+{
+ UInt32 Twiddled;
+
+ UInt32 MinDimension;
+
+ UInt32 SrcBitPos;
+ UInt32 DstBitPos;
+
+ int ShiftCount;
+
+ DebugAssert(YPos < YSize);
+ DebugAssert(IsPowerOfTwo(YSize));
+ DebugAssert(IsPowerOfTwo(XSize));
+
+ if (YSize < XSize)
+ MinDimension = YSize;
+ else
+ MinDimension = XSize;
+
+ // Step through all the bits in the "minimum" dimension
+ SrcBitPos = 1;
+ DstBitPos = 1;
+ Twiddled = 0;
+ ShiftCount = 0;
+
+ while (SrcBitPos < MinDimension)
+ {
+ if(YPos & SrcBitPos)
+ Twiddled |= DstBitPos;
+
+ SrcBitPos <<= 1;
+ DstBitPos <<= 2;
+ ShiftCount += 1;
+ }
+
+ // prepend any unused bits, if they were from Y
+ if (YSize >= XSize)
+ {
+ YPos >>= ShiftCount;
+ Twiddled |= (YPos << (2*ShiftCount));
+ }
+
+ return Twiddled;
+}
+
+
+static UInt32 TwiddleX_PVRTC(UInt32 YSize, UInt32 XSize, UInt32 XPos)
+{
+ UInt32 Twiddled;
+
+ UInt32 MinDimension;
+
+ UInt32 SrcBitPos;
+ UInt32 DstBitPos;
+
+ int ShiftCount;
+
+ DebugAssert(XPos < XSize);
+ DebugAssert(IsPowerOfTwo(YSize));
+ DebugAssert(IsPowerOfTwo(XSize));
+
+ if (YSize < XSize)
+ MinDimension = YSize;
+ else
+ MinDimension = XSize;
+
+ // Step through all the bits in the "minimum" dimension
+ SrcBitPos = 1;
+ DstBitPos = 2;
+ Twiddled = 0;
+ ShiftCount = 0;
+
+ while (SrcBitPos < MinDimension)
+ {
+ if (XPos & SrcBitPos)
+ {
+ Twiddled |= DstBitPos;
+ }
+
+ SrcBitPos <<= 1;
+ DstBitPos <<= 2;
+ ShiftCount += 1;
+ }
+
+ // prepend any unused bits, if they were from X
+ if (YSize < XSize)
+ {
+ XPos >>= ShiftCount;
+ Twiddled |= (XPos << (2*ShiftCount));
+ }
+
+ return Twiddled;
+}
+
+
+
+static UInt32 TwiddleUVPVRTC(UInt32 YSize, UInt32 XSize, UInt32 YPos, UInt32 XPos)
+{
+ return TwiddleY_PVRTC (YSize, XSize, YPos) + TwiddleX_PVRTC (YSize, XSize, XPos);
+}
+
+
+
+template<bool Do2bitMode, bool AssumeImageTiles>
+static void DecompressPVRTC(const PVRTCBlock *pCompressedData,
+ const int XDim,
+ const int YDim,
+ unsigned char* pResultImage)
+{
+ int BlkX, BlkY;
+ int BlkXp1, BlkYp1;
+ int BlkXDim, BlkYDim;
+
+ int ModulationVals[8][16];
+ int ModulationModes[8][16];
+
+ int Mod, DoPT;
+
+ // local neighbourhood of blocks
+ const PVRTCBlock *pBlocks[2][2];
+
+ const PVRTCBlock *pPrevious[2][2] = {{NULL, NULL}, {NULL, NULL}};
+
+ // Low precision colors extracted from the blocks.
+ // Rightmost colors have leftmost values subtracted from them.
+ struct PVRBlockColors {
+ int Reps[2][4];
+ };
+ PVRBlockColors Colours5554[2][2];
+
+ // Interpolated A and B colours for the pixel
+ int ASig[4], BSig[4];
+
+ const int XBlockSize = Do2bitMode ? kPVRSizeX2 : kPVRSizeX4;
+
+
+ // For MBX don't allow the sizes to get too small
+ BlkXDim = std::max(2, XDim / XBlockSize);
+ BlkYDim = std::max(2, YDim / kPVRBlockSizeY);
+
+ // Step through the pixels of the image decompressing each one in turn
+ //
+ // Note that this is a hideously inefficient way to do this!
+ for (int y = 0; y < YDim; y++)
+ {
+ BlkY = (y - kPVRBlockSizeY/2);
+ BlkY = PVR_LIMIT_COORD(BlkY, YDim, AssumeImageTiles);
+ BlkY /= kPVRBlockSizeY;
+ //BlkY = PVR_LIMIT_COORD(BlkY, BlkYDim, AssumeImageTiles);
+ BlkYp1 = PVR_LIMIT_COORD(BlkY+1, BlkYDim, AssumeImageTiles);
+
+ // Since Y & X coordinates twiddle into separate bits,
+ // we can compute Y block twiddle mask here for the whole row.
+ const PVRTCBlock* blockPointerY = pCompressedData + TwiddleY_PVRTC (BlkYDim, BlkXDim, BlkY);
+ const PVRTCBlock* blockPointerY1 = pCompressedData + TwiddleY_PVRTC (BlkYDim, BlkXDim, BlkYp1);
+
+ int coordV = GetVCoordPVR(y);
+
+ for (int x = 0; x < XDim; x++)
+ {
+ // map this pixel to the top left neighbourhood of blocks
+ BlkX = (x - XBlockSize/2);
+ BlkX = PVR_LIMIT_COORD(BlkX, XDim, AssumeImageTiles);
+ BlkX /= XBlockSize;
+ //BlkX = PVR_LIMIT_COORD(BlkX, BlkXDim, AssumeImageTiles);
+
+
+ // compute the positions of the other 3 blocks
+ BlkXp1 = PVR_LIMIT_COORD(BlkX+1, BlkXDim, AssumeImageTiles);
+
+ // Map to block memory locations
+
+ // block offsets, X bits
+ UInt32 blockOffsetX = TwiddleX_PVRTC (BlkYDim, BlkXDim, BlkX);
+ UInt32 blockOffsetX1 = TwiddleX_PVRTC (BlkYDim, BlkXDim, BlkXp1);
+
+ pBlocks[0][0] = blockPointerY + blockOffsetX;
+ pBlocks[0][1] = blockPointerY + blockOffsetX1;
+ pBlocks[1][0] = blockPointerY1 + blockOffsetX;
+ pBlocks[1][1] = blockPointerY1 + blockOffsetX1;
+
+
+ // extract the colours and the modulation information IF the previous values
+ // have changed.
+ if (pPrevious[0][0]!=pBlocks[0][0]||pPrevious[0][1]!=pBlocks[0][1]||
+ pPrevious[1][0]!=pBlocks[1][0]||pPrevious[1][1]!=pBlocks[1][1])
+ {
+ int StartY = 0;
+ for (int i = 0; i < 2; i++)
+ {
+ int StartX = 0;
+ for (int j = 0; j < 2; j++)
+ {
+ Unpack5554Colour(pBlocks[i][j], Colours5554[i][j].Reps);
+
+ UnpackModulations<Do2bitMode>(pBlocks[i][j],
+ ModulationVals,
+ ModulationModes,
+ StartX, StartY);
+
+ StartX += XBlockSize;
+ } // end for j
+
+ // for rightmost color block, subtract leftmost colors now
+ for (int ab = 0; ab < 2; ++ab)
+ {
+ for (int c = 0; c < 4; ++c)
+ Colours5554[i][1].Reps[ab][c] -= Colours5554[i][0].Reps[ab][c];
+ }
+
+ StartY += kPVRBlockSizeY;
+ } // end for i
+
+ // make a copy of the new pointers
+ pPrevious[0][0] = pBlocks[0][0];
+ pPrevious[0][1] = pBlocks[0][1];
+ pPrevious[1][0] = pBlocks[1][0];
+ pPrevious[1][1] = pBlocks[1][1];
+ } // end if the blocks have changed
+
+
+ // decompress the pixel. First compute the interpolated A and B signals
+ int coordU = GetUCoordPVR<Do2bitMode>(x);
+ InterpolateColoursPVRTC<Do2bitMode>(Colours5554[0][0].Reps[0],
+ Colours5554[0][1].Reps[0],
+ Colours5554[1][0].Reps[0],
+ Colours5554[1][1].Reps[0],
+ coordU, coordV,
+ ASig);
+
+ InterpolateColoursPVRTC<Do2bitMode>(Colours5554[0][0].Reps[1],
+ Colours5554[0][1].Reps[1],
+ Colours5554[1][0].Reps[1],
+ Colours5554[1][1].Reps[1],
+ coordU, coordV,
+ BSig);
+
+ GetModulationValue<Do2bitMode>(x,y, ModulationVals, ModulationModes,
+ &Mod, &DoPT);
+
+
+ // compute the modulated color and store in output image
+ for (int i = 0; i < 4; i++)
+ {
+ int res = (ASig[i] * 8 + Mod * (BSig[i] - ASig[i])) >> 3;
+ pResultImage[i] = (UInt8)res;
+ }
+ if (DoPT)
+ {
+ pResultImage[3] = 0;
+ }
+ pResultImage += 4;
+
+ } // end for x
+ } // end for y
+}
+#endif
+
+
+
+
+// ------------------------------------------------------------------------
+// ETC
+
+
+
+#if HAS_ETC_DECOMPRESSOR
+
+#define GETBITS(source, size, startpos) (( (source) >> ((startpos)-(size)+1) ) & ((1<<(size)) -1))
+#define GETBITSHIGH(source, size, startpos) (( (source) >> (((startpos)-32)-(size)+1) ) & ((1<<(size)) -1))
+inline int clampUByte(int v) { return (v < 0) ? 0 : ((v > 255) ? 255 : v); }
+
+inline void STORE_RGB(UInt32* img, int width, int x, int y, int r, int g, int b)
+{
+ Assert(r >= 0 && r <= 255);
+ Assert(g >= 0 && g <= 255);
+ Assert(b >= 0 && b <= 255);
+ Assert(x >= 0 && x < width);
+ img[y*width + x] = (0xFF<<24) | (r<<0) | (g<<8) | (b<<16);
+}
+
+void decompressBlockDiffFlip(unsigned int block_part1, unsigned int block_part2, UInt32* dstImg, int width, int startx, int starty)
+{
+ static const int unscramble[4] = {2, 3, 1, 0};
+
+ int compressParams[16][4];
+
+ compressParams[0][0] = -8; compressParams[0][1] = -2; compressParams[0][2] = 2; compressParams[0][3] = 8;
+ compressParams[1][0] = -8; compressParams[1][1] = -2; compressParams[1][2] = 2; compressParams[1][3] = 8;
+ compressParams[2][0] = -17; compressParams[2][1] = -5; compressParams[2][2] = 5; compressParams[2][3] = 17;
+ compressParams[3][0] = -17; compressParams[3][1] = -5; compressParams[3][2] = 5; compressParams[3][3] = 17;
+ compressParams[4][0] = -29; compressParams[4][1] = -9; compressParams[4][2] = 9; compressParams[4][3] = 29;
+ compressParams[5][0] = -29; compressParams[5][1] = -9; compressParams[5][2] = 9; compressParams[5][3] = 29;
+ compressParams[6][0] = -42; compressParams[6][1] = -13; compressParams[6][2] = 13; compressParams[6][3] = 42;
+ compressParams[7][0] = -42; compressParams[7][1] = -13; compressParams[7][2] = 13; compressParams[7][3] = 42;
+ compressParams[8][0] = -60; compressParams[8][1] = -18; compressParams[8][2] = 18; compressParams[8][3] = 60;
+ compressParams[9][0] = -60; compressParams[9][1] = -18; compressParams[9][2] = 18; compressParams[9][3] = 60;
+ compressParams[10][0] = -80; compressParams[10][1] = -24; compressParams[10][2] = 24; compressParams[10][3] = 80;
+ compressParams[11][0] = -80; compressParams[11][1] = -24; compressParams[11][2] = 24; compressParams[11][3] = 80;
+ compressParams[12][0] =-106; compressParams[12][1] = -33; compressParams[12][2] = 33; compressParams[12][3] = 106;
+ compressParams[13][0] =-106; compressParams[13][1] = -33; compressParams[13][2] = 33; compressParams[13][3] = 106;
+ compressParams[14][0] =-183; compressParams[14][1] = -47; compressParams[14][2] = 47; compressParams[14][3] = 183;
+ compressParams[15][0] =-183; compressParams[15][1] = -47; compressParams[15][2] = 47; compressParams[15][3] = 183;
+
+ UInt8 avg_color[3], enc_color1[3], enc_color2[3];
+ signed char diff[3];
+ int table;
+ int index,shift;
+ int r,g,b;
+ int diffbit;
+ int flipbit;
+
+ diffbit = (GETBITSHIGH(block_part1, 1, 33));
+ flipbit = (GETBITSHIGH(block_part1, 1, 32));
+
+ if( !diffbit )
+ {
+
+ // We have diffbit = 0.
+
+ // First decode left part of block.
+ avg_color[0]= GETBITSHIGH(block_part1, 4, 63);
+ avg_color[1]= GETBITSHIGH(block_part1, 4, 55);
+ avg_color[2]= GETBITSHIGH(block_part1, 4, 47);
+
+ // Here, we should really multiply by 17 instead of 16. This can
+ // be done by just copying the four lower bits to the upper ones
+ // while keeping the lower bits.
+ avg_color[0] |= (avg_color[0] <<4);
+ avg_color[1] |= (avg_color[1] <<4);
+ avg_color[2] |= (avg_color[2] <<4);
+
+ table = GETBITSHIGH(block_part1, 3, 39) << 1;
+
+
+ unsigned int pixel_indices_MSB, pixel_indices_LSB;
+
+ pixel_indices_MSB = GETBITS(block_part2, 16, 31);
+ pixel_indices_LSB = GETBITS(block_part2, 16, 15);
+
+ if( (flipbit) == 0 )
+ {
+ // We should not flip
+ shift = 0;
+ for(int x=startx; x<startx+2; x++)
+ {
+ for(int y=starty; y<starty+4; y++)
+ {
+ index = ((pixel_indices_MSB >> shift) & 1) << 1;
+ index |= ((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index=unscramble[index];
+
+ r=clampUByte(avg_color[0]+compressParams[table][index]);
+ g=clampUByte(avg_color[1]+compressParams[table][index]);
+ b=clampUByte(avg_color[2]+compressParams[table][index]);
+ STORE_RGB(dstImg, width, x, y, r, g, b);
+ }
+ }
+ }
+ else
+ {
+ // We should flip
+ shift = 0;
+ for(int x=startx; x<startx+4; x++)
+ {
+ for(int y=starty; y<starty+2; y++)
+ {
+ index = ((pixel_indices_MSB >> shift) & 1) << 1;
+ index |= ((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index=unscramble[index];
+
+ r=clampUByte(avg_color[0]+compressParams[table][index]);
+ g=clampUByte(avg_color[1]+compressParams[table][index]);
+ b=clampUByte(avg_color[2]+compressParams[table][index]);
+ STORE_RGB(dstImg, width, x, y, r, g, b);
+ }
+ shift+=2;
+ }
+ }
+
+ // Now decode other part of block.
+ avg_color[0]= GETBITSHIGH(block_part1, 4, 59);
+ avg_color[1]= GETBITSHIGH(block_part1, 4, 51);
+ avg_color[2]= GETBITSHIGH(block_part1, 4, 43);
+
+ // Here, we should really multiply by 17 instead of 16. This can
+ // be done by just copying the four lower bits to the upper ones
+ // while keeping the lower bits.
+ avg_color[0] |= (avg_color[0] <<4);
+ avg_color[1] |= (avg_color[1] <<4);
+ avg_color[2] |= (avg_color[2] <<4);
+
+ table = GETBITSHIGH(block_part1, 3, 36) << 1;
+ pixel_indices_MSB = GETBITS(block_part2, 16, 31);
+ pixel_indices_LSB = GETBITS(block_part2, 16, 15);
+
+ if( (flipbit) == 0 )
+ {
+ // We should not flip
+ shift=8;
+ for(int x=startx+2; x<startx+4; x++)
+ {
+ for(int y=starty; y<starty+4; y++)
+ {
+ index = ((pixel_indices_MSB >> shift) & 1) << 1;
+ index |= ((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index=unscramble[index];
+
+ r=clampUByte(avg_color[0]+compressParams[table][index]);
+ g=clampUByte(avg_color[1]+compressParams[table][index]);
+ b=clampUByte(avg_color[2]+compressParams[table][index]);
+ STORE_RGB(dstImg, width, x, y, r, g, b);
+ }
+ }
+ }
+ else
+ {
+ // We should flip
+ shift=2;
+ for(int x=startx; x<startx+4; x++)
+ {
+ for(int y=starty+2; y<starty+4; y++)
+ {
+ index = ((pixel_indices_MSB >> shift) & 1) << 1;
+ index |= ((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index=unscramble[index];
+
+ r=clampUByte(avg_color[0]+compressParams[table][index]);
+ g=clampUByte(avg_color[1]+compressParams[table][index]);
+ b=clampUByte(avg_color[2]+compressParams[table][index]);
+ STORE_RGB(dstImg, width, x, y, r, g, b);
+ }
+ shift += 2;
+ }
+ }
+
+ }
+ else
+ {
+ // We have diffbit = 1.
+
+ // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
+ // ---------------------------------------------------------------------------------------------------
+ // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip|
+ // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit |
+ // ---------------------------------------------------------------------------------------------------
+ //
+ //
+ // c) bit layout in bits 31 through 0 (in both cases)
+ //
+ // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+ // --------------------------------------------------------------------------------------------------
+ // | most significant pixel index bits | least significant pixel index bits |
+ // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a |
+ // --------------------------------------------------------------------------------------------------
+
+
+ // First decode left part of block.
+ enc_color1[0]= GETBITSHIGH(block_part1, 5, 63);
+ enc_color1[1]= GETBITSHIGH(block_part1, 5, 55);
+ enc_color1[2]= GETBITSHIGH(block_part1, 5, 47);
+
+
+ // Expand from 5 to 8 bits
+ avg_color[0] = (enc_color1[0] <<3) | (enc_color1[0] >> 2);
+ avg_color[1] = (enc_color1[1] <<3) | (enc_color1[1] >> 2);
+ avg_color[2] = (enc_color1[2] <<3) | (enc_color1[2] >> 2);
+
+
+ table = GETBITSHIGH(block_part1, 3, 39) << 1;
+
+ unsigned int pixel_indices_MSB, pixel_indices_LSB;
+
+ pixel_indices_MSB = GETBITS(block_part2, 16, 31);
+ pixel_indices_LSB = GETBITS(block_part2, 16, 15);
+
+ if( (flipbit) == 0 )
+ {
+ // We should not flip
+ shift = 0;
+ for(int x=startx; x<startx+2; x++)
+ {
+ for(int y=starty; y<starty+4; y++)
+ {
+ index = ((pixel_indices_MSB >> shift) & 1) << 1;
+ index |= ((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index=unscramble[index];
+
+ r=clampUByte(avg_color[0]+compressParams[table][index]);
+ g=clampUByte(avg_color[1]+compressParams[table][index]);
+ b=clampUByte(avg_color[2]+compressParams[table][index]);
+ STORE_RGB(dstImg, width, x, y, r, g, b);
+ }
+ }
+ }
+ else
+ {
+ // We should flip
+ shift = 0;
+ for(int x=startx; x<startx+4; x++)
+ {
+ for(int y=starty; y<starty+2; y++)
+ {
+ index = ((pixel_indices_MSB >> shift) & 1) << 1;
+ index |= ((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index=unscramble[index];
+
+ r=clampUByte(avg_color[0]+compressParams[table][index]);
+ g=clampUByte(avg_color[1]+compressParams[table][index]);
+ b=clampUByte(avg_color[2]+compressParams[table][index]);
+ STORE_RGB(dstImg, width, x, y, r, g, b);
+ }
+ shift+=2;
+ }
+ }
+
+
+ // Now decode right part of block.
+
+
+ diff[0]= GETBITSHIGH(block_part1, 3, 58);
+ diff[1]= GETBITSHIGH(block_part1, 3, 50);
+ diff[2]= GETBITSHIGH(block_part1, 3, 42);
+
+ enc_color2[0]= enc_color1[0] + diff[0];
+ enc_color2[1]= enc_color1[1] + diff[1];
+ enc_color2[2]= enc_color1[2] + diff[2];
+
+ // Extend sign bit to entire byte.
+ diff[0] = (diff[0] << 5);
+ diff[1] = (diff[1] << 5);
+ diff[2] = (diff[2] << 5);
+ diff[0] = diff[0] >> 5;
+ diff[1] = diff[1] >> 5;
+ diff[2] = diff[2] >> 5;
+
+ // Calculale second color
+ enc_color2[0]= enc_color1[0] + diff[0];
+ enc_color2[1]= enc_color1[1] + diff[1];
+ enc_color2[2]= enc_color1[2] + diff[2];
+
+ // Expand from 5 to 8 bits
+ avg_color[0] = (enc_color2[0] <<3) | (enc_color2[0] >> 2);
+ avg_color[1] = (enc_color2[1] <<3) | (enc_color2[1] >> 2);
+ avg_color[2] = (enc_color2[2] <<3) | (enc_color2[2] >> 2);
+
+
+ table = GETBITSHIGH(block_part1, 3, 36) << 1;
+ pixel_indices_MSB = GETBITS(block_part2, 16, 31);
+ pixel_indices_LSB = GETBITS(block_part2, 16, 15);
+
+ if( (flipbit) == 0 )
+ {
+ // We should not flip
+ shift=8;
+ for(int x=startx+2; x<startx+4; x++)
+ {
+ for(int y=starty; y<starty+4; y++)
+ {
+ index = ((pixel_indices_MSB >> shift) & 1) << 1;
+ index |= ((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index=unscramble[index];
+
+ r=clampUByte(avg_color[0]+compressParams[table][index]);
+ g=clampUByte(avg_color[1]+compressParams[table][index]);
+ b=clampUByte(avg_color[2]+compressParams[table][index]);
+ STORE_RGB(dstImg, width, x, y, r, g, b);
+ }
+ }
+ }
+ else
+ {
+ // We should flip
+ shift=2;
+ for(int x=startx; x<startx+4; x++)
+ {
+ for(int y=starty+2; y<starty+4; y++)
+ {
+ index = ((pixel_indices_MSB >> shift) & 1) << 1;
+ index |= ((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index=unscramble[index];
+
+ r=clampUByte(avg_color[0]+compressParams[table][index]);
+ g=clampUByte(avg_color[1]+compressParams[table][index]);
+ b=clampUByte(avg_color[2]+compressParams[table][index]);
+ STORE_RGB(dstImg, width, x, y, r, g, b);
+ }
+ shift += 2;
+ }
+ }
+ }
+}
+
+
+static void DecompressETC_RGB4 ( int xblocks, int yblocks, int destWidth, const UInt32* m_pCompBytes, UInt32* m_pDecompBytes )
+{
+ const UInt8* srcPtr = (const UInt8*)m_pCompBytes;
+ UInt32* dstPtr = m_pDecompBytes;
+
+ for (int y = 0; y < yblocks; y++)
+ {
+ for (int x = 0; x < xblocks; x++)
+ {
+ UInt32 part1 = (srcPtr[0]<<24) | (srcPtr[1]<<16) | (srcPtr[2]<<8) | (srcPtr[3]<<0);
+ UInt32 part2 = (srcPtr[4]<<24) | (srcPtr[5]<<16) | (srcPtr[6]<<8) | (srcPtr[7]<<0);
+ decompressBlockDiffFlip(part1, part2, dstPtr, destWidth, x*4, y*4);
+ srcPtr += 8;
+ }
+ }
+}
+
+#endif // HAS_ETC_DECOMPRESSOR
+
+
+#if HAS_ATC_DECOMPRESSOR
+
+template<int textureFormat>
+static void DecompressATC ( int xblocks, int yblocks, int destWidth, const UInt32* m_pCompBytes, UInt32* m_pDecompBytes )
+{
+ bool doAlpha = (textureFormat == kTexFormatATC_RGBA8);
+
+ TQonvertImage src, dst;
+ src.nWidth = xblocks * 4;
+ src.nHeight = yblocks * 4;
+ src.nFormat = doAlpha ? Q_FORMAT_ATC_RGBA_INTERPOLATED_ALPHA : Q_FORMAT_ATC_RGB;
+ src.pFormatFlags = NULL;
+ src.nDataSize = CalculateImageSize (src.nWidth, src.nHeight, textureFormat);
+ src.pData = (unsigned char*)m_pCompBytes;
+
+ dst.nWidth = destWidth;
+ dst.nHeight = yblocks * 4;
+ dst.nFormat = Q_FORMAT_RGBA_8888;
+ dst.pFormatFlags = NULL;
+ dst.nDataSize = CalculateImageSize (dst.nWidth, dst.nHeight, kTexFormatRGBA32);
+ dst.pData = (unsigned char*)m_pDecompBytes;
+
+ int nRet = Qonvert(&src, &dst);
+ if (nRet != Q_SUCCESS)
+ {
+ ErrorStringMsg("DecompressETC2 failed with nRet = %i", nRet);
+ }
+}
+
+#endif // HAS_ATC_DECOMPRESSOR
+
+#if HAS_ASTC_DECOMPRESSOR
+
+// ASTC decoding
+
+#include "External/astcenc/Source/astc_codec_internals.h"
+
+// Needed by astcenc
+void astc_codec_internal_error(const char *filename, int linenum)
+{
+ printf_console("ASTCenc: Internal error: File=%s Line=%d\n", filename, linenum);
+}
+// Define an unlink() function in terms of the Unity DeleteFile function.
+int astc_codec_unlink(const char *filename)
+{
+/* bool res = DeleteFile(filename);
+ return (res ? 0 : -1);*/
+
+ // Not used in runtime
+ return -1;
+}
+int rgb_force_use_of_hdr = 0;
+int alpha_force_use_of_hdr = 0;
+int perform_srgb_transform = 0;
+int print_tile_errors = 0;
+
+// TODO: Make threadsafe when we have multithreaded renderer. Doesn't really hurt to do multiple inits though.
+static int astcenc_initialized = 0;
+
+static void DecompressASTC(const UInt32 *srcData, int destWidth, int destHeight, UInt32 *destData, int blockWidth, int blockHeight)
+{
+ swizzlepattern swz_decode = { 0, 1, 2, 3 };
+
+ if(!astcenc_initialized)
+ {
+ // initialization routines
+ prepare_angular_tables();
+ build_quantization_mode_table();
+ astcenc_initialized = 1;
+ }
+
+
+ astc_codec_image *img = allocate_image(8, destWidth, destHeight, 1, 0);
+ initialize_image(img);
+
+ const int zblocks = 1;
+ const int yblocks = CeilfToInt((float)destHeight / (float)blockHeight);
+ const int xblocks = CeilfToInt((float)destWidth / (float)blockWidth);
+
+ const int xdim = blockWidth;
+ const int ydim = blockHeight;
+ const int zdim = 1;
+
+ int x, y, z;
+
+ imageblock pb;
+ for (z = 0; z < zblocks; z++)
+ for (y = 0; y < yblocks; y++)
+ for (x = 0; x < xblocks; x++)
+ {
+ int offset = (((z * yblocks + y) * xblocks) + x) * 16;
+ uint8_t *bp = ((uint8_t *)srcData) + offset;
+ physical_compressed_block pcb = *(physical_compressed_block *) bp;
+ symbolic_compressed_block scb;
+ physical_to_symbolic(xdim, ydim, zdim, pcb, &scb);
+ decompress_symbolic_block(DECODE_LDR, xdim, ydim, zdim, x * xdim, y * ydim, z * zdim, &scb, &pb);
+ write_imageblock(img, &pb, xdim, ydim, zdim, x * xdim, y * ydim, z * zdim, swz_decode);
+ }
+
+ for(y = 0; y < destHeight; y++)
+ {
+ memcpy(&destData[y*destWidth], img->imagedata8[0][y], destWidth * 4);
+ }
+ destroy_image(img);
+
+}
+
+#endif // HAS_ASTC_DECOMPRESSOR
+
+// ------------------------------------------------------------------------
+// Common
+
+
+bool DecompressNativeTextureFormatWithMipLevel( TextureFormat srcFormat, int srcWidth, int srcHeight, int mipLevel, const UInt32* sourceData,
+ int destWidth, int destHeight, UInt32* destData )
+{
+
+#if HAS_FLASH_ATF_DECOMPRESSOR
+ // Flash requires that we pass the mipLevel explicitly
+ if (IsCompressedFlashATFTextureFormat(srcFormat))
+ {
+ DecompressFlashATFTexture((const UInt8*)sourceData, destWidth, destHeight, mipLevel, (UInt8*)destData);
+ return true;
+ }
+#endif
+
+ return DecompressNativeTextureFormat (srcFormat, srcWidth, srcHeight, (UInt32*)sourceData, destWidth, destHeight, destData);
+}
+
+
+
+bool DecompressNativeTextureFormat( TextureFormat srcFormat, int srcWidth, int srcHeight, const UInt32* sourceData,
+ int destWidth, int destHeight, UInt32* destData )
+{
+ Assert( IsAnyCompressedTextureFormat(srcFormat) );
+ Assert( destWidth >= srcWidth && destHeight >= srcHeight );
+ Assert( destWidth % 4 == 0 && destHeight % 4 == 0 );
+
+#if UNITY_XENON || HAS_DXT_DECOMPRESSOR || HAS_ETC_DECOMPRESSOR || HAS_ATC_DECOMPRESSOR
+ int xblocks = (srcWidth + 3) / 4;
+ int yblocks = (srcHeight + 3) / 4;
+#endif
+
+ const UInt32* srcData = sourceData;
+
+ #if UNITY_XENON
+ UInt32 dataSize = (xblocks * yblocks * ((kTexFormatDXT1 == srcFormat) ? 8 : 16)) >> 1;
+ UInt16 *s = (UInt16*)srcData;
+ UInt16 *d = new UInt16[dataSize];
+ for(int i=0;i<dataSize;i++)
+ d[i]=GetByteSwap16(s[i]);
+ srcData = (UInt32*)d;
+ #endif
+
+ #if PRINT_DECOMPRESSION_TIMES
+ double t0 = GetTimeSinceStartup();
+ #endif
+
+
+ switch( srcFormat )
+ {
+ #if HAS_DXT_DECOMPRESSOR
+ case kTexFormatDXT1: DecompressDXT1( xblocks, yblocks, destWidth, srcData, destData ); break;
+ case kTexFormatDXT3: DecompressDXT3( xblocks, yblocks, destWidth, srcData, destData ); break;
+ case kTexFormatDXT5: DecompressDXT5( xblocks, yblocks, destWidth, srcData, destData ); break;
+ #endif
+
+ #if HAS_ETC_DECOMPRESSOR
+ case kTexFormatETC_RGB4: DecompressETC_RGB4(xblocks, yblocks, destWidth, srcData, destData ); break;
+ #endif
+
+ #if HAS_ATC_DECOMPRESSOR
+ case kTexFormatATC_RGB4: DecompressATC<kTexFormatATC_RGB4>(xblocks, yblocks, destWidth, srcData, destData); break;
+ case kTexFormatATC_RGBA8: DecompressATC<kTexFormatATC_RGBA8>(xblocks, yblocks, destWidth, srcData, destData); break;
+ #endif
+
+ case kTexFormatETC2_RGB: DecompressETC2_RGB8 ((UInt8*)destData, (const UInt8*)srcData, destWidth, destHeight); break;
+ case kTexFormatETC2_RGBA1: DecompressETC2_RGB8_A1 ((UInt8*)destData, (const UInt8*)srcData, destWidth, destHeight); break;
+ case kTexFormatETC2_RGBA8: DecompressETC2_RGBA8 ((UInt8*)destData, (const UInt8*)srcData, destWidth, destHeight); break;
+
+ #if HAS_PVRTC_DECOMPRESSOR
+ case kTexFormatPVRTC_RGB4:
+ case kTexFormatPVRTC_RGBA4: DecompressPVRTC<false,true>( reinterpret_cast<const PVRTCBlock*>(srcData), srcWidth, srcHeight, reinterpret_cast<unsigned char*> (destData) ); break;
+
+ case kTexFormatPVRTC_RGB2:
+ case kTexFormatPVRTC_RGBA2: DecompressPVRTC<true,true>( reinterpret_cast<const PVRTCBlock*>(srcData), srcWidth, srcHeight, reinterpret_cast<unsigned char*> (destData) ); break;
+ #endif
+
+ #if HAS_FLASH_ATF_DECOMPRESSOR
+ case kTexFormatFlashATF_RGB_DXT1:
+ case kTexFormatFlashATF_RGB_JPG:
+ case kTexFormatFlashATF_RGBA_JPG:
+ DecompressFlashATFTexture ((const UInt8*)srcData, destWidth, destHeight, 0, (UInt8*)destData);
+ break;
+ #endif
+
+#if HAS_ASTC_DECOMPRESSOR
+#define DO_ASTC(bx,by) case kTexFormatASTC_RGB_##bx##x##by : case kTexFormatASTC_RGBA_##bx##x##by : DecompressASTC(srcData, destWidth, destHeight, destData, bx, by); break
+
+ DO_ASTC(4, 4);
+ DO_ASTC(5, 5);
+ DO_ASTC(6, 6);
+ DO_ASTC(8, 8);
+ DO_ASTC(10, 10);
+ DO_ASTC(12, 12);
+
+#undef DO_ASTC
+#endif
+
+ default:
+ AssertString( "Unknown compressed texture format!" );
+ #if UNITY_XENON
+ delete[] srcData;
+ #endif
+ return false;
+ }
+
+#if UNITY_XENON
+ delete[] srcData;
+#endif
+
+ #if PRINT_DECOMPRESSION_TIMES
+ double t1 = GetTimeSinceStartup();
+ if (srcWidth >= 512 && srcHeight >= 512)
+ {
+ printf_console("Decompress %x size %ix%i fmt %i time %.3fs\n", srcData, srcWidth, srcHeight, (int)srcFormat, t1-t0);
+ }
+ #endif
+
+ return true;
+}
+
+
+
+// ------------------------------------------------------------------------
+// Unit Tests
+
+
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (ImageDecompressionTests)
+{
+ #if UNITY_LITTLE_ENDIAN
+ TEST (DecodeDXT5AlphaPalette8a)
+ {
+ UInt32 res[16];
+ memset (res, 0xCC, sizeof(res));
+ UInt8 b[] = { 17, 13, 177, 109, 155, 54, 105, 82, 255 };
+ DecodeAlpha3BitLinear (res, *(DXTAlphaBlock3BitLinear*)b, 4, 0x00FFFFFF);
+ UInt8 r[] = { 13,14,14,14, 14,14,14,15, 14,14,15,15, 14,15,15,16 };
+ for (int i = 0; i < 16; ++i) {
+ CHECK_EQUAL ((int)r[i], (res[i]&0xFF000000)>>24);
+ }
+ }
+ TEST (DecodeDXT5AlphaPalette8b)
+ {
+ UInt32 res[16];
+ memset (res, 0xCD, sizeof(res));
+ UInt8 b[] = { 251, 5, 179, 109, 113, 54, 107, 84, 255 };
+ DecodeAlpha3BitLinear (res, *(DXTAlphaBlock3BitLinear*)b, 4, 0x00FFFFFF);
+ UInt8 r[] = { 181,75,75,75, 75,216,146,181, 75,75,146,110, 75,251,110,216 };
+ for (int i = 0; i < 16; ++i) {
+ CHECK_EQUAL ((int)r[i], (res[i]&0xFF000000)>>24);
+ }
+ }
+ TEST (DecodeDXT5AlphaPalette6)
+ {
+ UInt32 res[16];
+ memset (res, 0xCC, sizeof(res));
+ UInt8 b[] = { 15, 18, 0, 4, 72, 144, 8, 137, 255 };
+ DecodeAlpha3BitLinear (res, *(DXTAlphaBlock3BitLinear*)b, 4, 0x00FFFFFF);
+ UInt8 r[] = { 15,15,15,16, 15,15,16,16, 15,16,16,17, 15,16,16,17 };
+ for (int i = 0; i < 16; ++i) {
+ CHECK_EQUAL ((int)r[i], (res[i]&0xFF000000)>>24);
+ }
+ }
+ #if HAS_PVRTC_DECOMPRESSOR
+ TEST (DecodePVRTC_2_16x16)
+ {
+ const int kSize = 16;
+ UInt32 src[kSize*kSize*2/32] = {
+ 0xeeeeeeee, 0x83eed400, 0xeeeeeeee, 0x8befb006, 0xfefefefe, 0xeed9801e, 0x00fefefe, 0xcf18801e,
+ 0x0eeeeeee, 0x9ff4fc00, 0x00fefefe, 0xaff58000, 0x00ffffff, 0x83add404, 0x00ffffff, 0xb77d8000,
+ };
+ UInt32 expected[kSize*kSize] = {
+ 0xff3f002b,0xffadeb59,0xffa3f24b,0xff99f83b,0xff000056,0xff99f83b,0xffa3f24b,0xffadeb59,0xff3f002b,0xffc0dd77,0xffcbd787,0xffd4d095,0xffdecaa5,0xffd4d095,0xffcbd787,0xffc0dd77,
+ 0xff5f0040,0xffa0e756,0xff96ef40,0xff8cf72b,0xff000081,0xff8cf72b,0xff96ef40,0xffa0e756,0xff5f0040,0xffb6d780,0xffc0cf96,0xffcbc7ac,0xffd6bfc1,0xffcbc7ac,0xffc0cf96,0xffb6d780,
+ 0xff7f0056,0xff95e353,0xff8aed37,0xff7ef61b,0xff0000ad,0xff7ef61b,0xff8aed37,0xff95e353,0xff7f0056,0xffacd18b,0xffb7c8a7,0xffc2bec2,0xffceb5de,0xffc2bec2,0xffb7c8a7,0xffacd18b,
+ 0xff86004d,0xff95e44f,0xff8bee36,0xff7ff61c,0xff0c009a,0xff7ff61c,0xff8bee36,0xff95e44f,0xff86004d,0xffabd381,0xffb6cb9b,0xffc0c1b4,0xffccb9ce,0xffc0c1b4,0xffb6cb9b,0xffabd381,
+ 0xff8c0044,0xff96e74c,0xff8cef35,0xff81f71e,0xff180088,0xff81f71e,0xff8cef35,0xff96e74c,0xff8c0044,0xffabd679,0xffb5ce90,0xffbfc6a7,0xffcabdbd,0xffbfc6a7,0xffb5ce90,0xffabd679,
+ 0xff92003a,0xff96e848,0xff8df034,0xff82f71f,0xff250075,0xff82f71f,0xff8df034,0xff96e848,0xff92003a,0xffaad870,0xffb4d185,0xffbdc998,0xffc8c1ad,0xffbdc998,0xffb4d185,0xffaad870,
+ 0xff980031,0xff97ea45,0xff8ef133,0xff85f822,0xff310063,0xff85f822,0xff8ef133,0xff97ea45,0xff980031,0xffaadb68,0xffb3d479,0xffbccd8b,0xffc6c69c,0xffbccd8b,0xffb3d479,0xffaadb68,
+ 0xff76005a,0xff95ed3c,0xff90f331,0xff8bf926,0xff25008a,0xff8bf926,0xff90f331,0xff95ed3c,0xff76005a,0xff8a004e,0xff9e0043,0xffb30036,0xffc8002b,0xffb30036,0xff9e0043,0xff8a004e,
+ 0xff540084,0xff93f134,0xff92f62f,0xff91fa2a,0xff1800b1,0xff91fa2a,0xff92f62f,0xff93f134,0xff94ed39,0xff95e83e,0xff96e344,0xff97de49,0xff98da4e,0xff97de49,0xff96e344,0xff95e83e,
+ 0xff3200ad,0xff91f52c,0xff94f82d,0xff97fb2e,0xff0c00d8,0xff97fb2e,0xff94f82d,0xff91f52c,0xff8ef22b,0xff8bee2a,0xff88eb29,0xff85e828,0xff81e427,0xff85e828,0xff88eb29,0xff8bee2a,
+ 0xff1000d6,0xff8ff924,0xff96fb2b,0xff9dfd32,0xff0000ff,0xff9dfd32,0xff96fb2b,0xff8ff924,0xff88f71c,0xff80f515,0xff79f30e,0xff72f107,0xff6bef00,0xff72f107,0xff79f30e,0xff80f515,
+ 0xff0c00a0,0xff9cf732,0xff9ffa37,0xffa2fc3c,0xff0000bf,0xff0300b7,0xff0600b0,0xff0900a8,0xff0c00a0,0xff0f0098,0xff120091,0xff150089,0xff180081,0xff150089,0xff120091,0xff0f0098,
+ 0xff08006b,0xffaaf642,0xffaaf945,0xffa9fc47,0xffa9ff4a,0xffa9fc47,0xffaaf945,0xffaaf642,0xffabf33f,0xffabf03c,0xffaced3a,0xffacea37,0xffade735,0xffacea37,0xffaced3a,0xffabf03c,
+ 0xff040035,0xffb7f451,0xffb3f851,0xffaffb51,0xffabff52,0xffaffb51,0xffb3f851,0xffb7f451,0xffbcf151,0xffc0ed50,0xffc4ea50,0xffc9e550,0xffcee250,0xffc9e550,0xffc4ea50,0xffc0ed50,
+ 0xff000000,0xffc6f360,0xffbdf75e,0xffb5fb5c,0xffadff5a,0xffb5fb5c,0xffbdf75e,0xffc6f360,0xffceef63,0xffd6eb65,0xffdee767,0xffe7e269,0xffefde6b,0xffe7e269,0xffdee767,0xffd6eb65,
+ 0xff1f0015,0xff17001a,0xff0f001f,0xff070025,0xff00002b,0xff070025,0xff0f001f,0xff17001a,0xff1f0015,0xff27000f,0xff2f000a,0xff370005,0xff3f0000,0xff370005,0xff2f000a,0xff27000f,
+ };
+ UInt32 res[kSize*kSize];
+ DecompressPVRTC<true,true> (reinterpret_cast<const PVRTCBlock*>(src), kSize, kSize, reinterpret_cast<UInt8*>(res));
+ CHECK_ARRAY_EQUAL (expected, res, kSize*kSize);
+ }
+ TEST (DecodePVRTC_4_16x16)
+ {
+ const int kSize = 16;
+ UInt32 src[kSize*kSize*4/32] = {
+ 0x32323230,0x7faa30e7,0x32323232,0x7fbc40f9,0x03030303,0x050230f6,0x03030303,0x060330f4,
+ 0x32323232,0x7faa40f7,0xa802f232,0xffff30e7,0xff030303,0x0f0040e6,0xaa00ff00,0xff9f40e9,
+ 0x0303035b,0x300f6aca,0xff030303,0x300f68ca,0x409094aa,0x68af5bba,0xff000040,0x200f58ca,
+ 0xff000000,0x2c0140e6,0xaa00ff00,0xffff41db,0xff000000,0x1c0140e8,0xaa00ff00,0xffff40bb,
+ };
+ UInt32 expected[kSize*kSize] = {
+ 0x7f92d62f,0x727ee217,0xf6d2d6ff,0x6a70f100,0xbbb1a5d4,0x727af500,0x777ff700,0x838eef17,0x99c67994,0xa2cca070,0xadd1b56a,0xaed1b971,0xbbe2b688,0xc1e2c193,0xc8e2cc9f,0xc6d4d19f,
+ 0x00b6cb8d,0x747ee223,0xf2bbc1ff,0x686df200,0x998a7dbe,0x6c72f800,0x6e75fb00,0x8187f121,0x66a93c5e,0xa7aadc64,0xbbbbd286,0xb4bbcd88,0xaebbc88a,0xb2d2b58c,0xb5d2be98,0xc2d0c49e,
+ 0x00b1c192,0x777fe22f,0xeea5adff,0x666bf300,0x776356a9,0x666bfb00,0x666bff00,0x7f7ff32b,0x338c0029,0xb2a9da81,0xccbdcead,0xc3bdcab1,0xbbbdc6b5,0xb2bdc1b9,0xb6d2b7aa,0xbdccb79e,
+ 0x00b6b986,0x7d89e72c,0xeeadb1ff,0x6c72f600,0x776958ab,0x6868fc00,0x6663ff00,0x7f79f329,0x338e002b,0xb2a7da7b,0xccbdcea5,0xc3bdcba8,0xbbbdc8ab,0xb2bdc4ae,0xaabdc1b1,0xa7c1b588,
+ 0x00bbb27a,0x8392eb29,0xeeb5b5ff,0x7279f900,0x776f5aad,0x6a65fd00,0x665aff00,0x7f73f327,0x3390002d,0xb2a5da75,0xccbdce9c,0xc3bdcc9e,0xbbbdcaa0,0xb2bdc8a2,0xaabdc6a5,0x9ec4ac78,
+ 0x00c0aa6e,0x8a9bef26,0xeebdb9ff,0x7980fc00,0x77755caf,0x6c61fe00,0x6652ff00,0x7f6df325,0x3392002f,0xb2a2da6f,0xccbdce94,0xc3bdcd95,0xbbbdcc96,0xb2bdcb97,0xaabdca98,0x9fb2d672,
+ 0x00c5a262,0x90a5f323,0xeec6bdff,0x7f88ff00,0x777b5eb1,0x6e5eff00,0x664aff00,0x7f67f323,0x33940031,0xb2a0da69,0xccbdce8c,0xc3bdce8c,0xbbbdce8c,0xb2bdce8c,0xaabdce8c,0xa1b5da69,
+ 0x00afa366,0x8e99f519,0xeebdb9ff,0x8180fe00,0x77715cc4,0x7461fc00,0x6e52fb00,0x8168f219,0x2e71005e,0x46970049,0x5dbd0033,0x55bd0033,0x4cbd0033,0x44bd0033,0x3bbd0033,0x68bd2e66,
+ 0x009aa46b,0x8c8ef711,0xeeb5b5ff,0x8379fd00,0x77675ad8,0x7b65f900,0x775af700,0x8369f111,0x9077eb23,0x9d86e434,0xaa94de46,0xa59ade46,0xa1a0de46,0x9da7de46,0x99adde46,0x94a2e734,
+ 0x0084a56f,0x8a82f908,0xeeadb1ff,0x8572fc00,0x775c58ec,0x8168f600,0x7f63f300,0x856af008,0x8c71ed11,0x9278ea19,0x997fe723,0x9689e723,0x9492e723,0x929be723,0x90a5e723,0x8e99ed19,
+ 0x006fa673,0x8877fb00,0xeea5adff,0x886bfb00,0x775256ff,0x886bf300,0x886bef00,0x886bef00,0x886bef00,0x886bef00,0x886bef00,0x8877ef00,0x8884ef00,0x8890ef00,0x889cef00,0x8890f300,
+ 0x0085b776,0x8178f500,0xf2bbc1ff,0x816df800,0x997d7dff,0x6c5e5bff,0x3f3f39ff,0x4c433af6,0x59463ced,0x66493de3,0x724c3fda,0x6c4c3fda,0x664c3fda,0x5f4c3fda,0x594c3fda,0x7f685fe3,
+ 0x009bc979,0x7b7aef00,0xf6d2d6ff,0x7b70f500,0x7f75f300,0x837af100,0x887fef00,0x8884ed02,0x8888eb04,0x888ce906,0x8890e708,0x8896e206,0x889cde04,0x88a2da02,0x88a9d600,0x8399de00,
+ 0x00b1d97c,0x747be900,0xfae9ebff,0xebdedbff,0xddd4ccff,0xcecabcff,0xbfbfadff,0xc3c0b1fc,0xc7c1b6f9,0xccc2baf6,0xd0c3bff3,0xcec3bff3,0xccc3bff3,0xc9c3bff3,0xc7c3bff3,0xd4cdcaf6,
+ 0x00c7ea7f,0x6e7de200,0x666bef00,0x6e75ef00,0x777fef00,0x7f8aef00,0x8894ef00,0x889ceb04,0x88a5e708,0x88ade20c,0x88b5de10,0x88b5d60c,0x88b5ce08,0x88b5c604,0x88b5bd00,0x7fa2ca00,
+ 0x7b91d617,0x00b5e681,0x00aaed7f,0x00a9e57a,0x00a9de75,0x00a8d76f,0x00a9d06a,0x00b5cf6e,0x00c1ce72,0x00cecd76,0x00dacc7b,0x00dace7e,0x00dad082,0x00dad185,0x00dad488,0x00cdda86,
+ };
+ UInt32 res[kSize*kSize];
+ DecompressPVRTC<false,true> (reinterpret_cast<const PVRTCBlock*>(src), kSize, kSize, reinterpret_cast<UInt8*>(res));
+ CHECK_ARRAY_EQUAL (expected, res, kSize*kSize);
+ }
+ TEST (DecodePVRTC_4_8x8)
+ {
+ const int kSize = 8;
+ UInt32 src[kSize*kSize*4/32] = {
+ 0x4c4c4c4c,0x63fb3494,0x00fc4c4c,0x68fc2352,0xa9fefefe,0x5bac1078,0x00ff5555,0x68dc2072,
+ };
+ UInt32 expected[kSize*kSize] = {
+ 0x444a751c,0xc7bff170,0x55357739,0x7a6fa444,0x9394b95b,0xbfc3d493,0xbbc6c6a5,0xbfc3d493,0x445e7e1e,0xc5bcee61,0x5d3f8a3d,0x7b77ab41,0x909ab858,0xb8c2cb96,0xb2c6b9b1,0xb8c2cb96,
+ 0x44738821,0xc3b9eb54,0x664a9c42,0x7e80b33e,0x8ea1b856,0xb2c1c19a,0xaac6adbd,0xb2c1c19a,0x445e7e1e,0xc5bcee61,0x5d3f8a3d,0x7b77ab41,0x7282a141,0x87a2ab63,0x7faa9e6e,0x87a2ab63,
+ 0x444a751c,0xc7bff170,0x55357739,0x7a6fa444,0x73769d41,0x6c7d983f,0x6685923d,0x6c7d983f,0x44356c1a,0xc9c2f47d,0x4c2b6535,0x78669c47,0x756a9a42,0x716d983d,0x6e719639,0x716d983d,
+ 0x44216318,0xccc6f78c,0xccc6ff8c,0xccc6f78c,0xccc6ef8c,0xccc6e78c,0xccc6de8c,0xccc6e78c,0x44356c1a,0x48306828,0x4c2b6535,0x48306828,0x44356c1a,0x3f3a6f0d,0x3b3f7300,0x3f3a6f0d,
+ };
+ UInt32 res[kSize*kSize];
+ DecompressPVRTC<false,true> (reinterpret_cast<const PVRTCBlock*>(src), kSize, kSize, reinterpret_cast<UInt8*>(res));
+ CHECK_ARRAY_EQUAL (expected, res, kSize*kSize);
+ }
+ TEST (DecodePVRTC_4_8x16)
+ {
+ const int kSizeX = 8;
+ const int kSizeY = 16;
+ UInt32 src[kSizeX*kSizeY*4/32] = {
+ 0x4c4c4c4c,0x63fb3494,0x00fc4c4c,0x68fc2352,0xa9fefefe,0x5bac1078,0x00ff5555,0x68dc2072,
+ 0x32323232,0x7faa40f7,0xa802f232,0xffff30e7,0xff030303,0x0f0040e6,0xaa00ff00,0xff9f40e9,
+ };
+ UInt32 expected[kSizeX*kSizeY] = {
+ 0x5d79bb10,0xe1dcf2aa,0x665ac621,0x9194d24e,0xadb8d47a,0xd8e0d7cd,0xd4e2cade,0xd8e0d7cd,0x5076a118,0xd2cbee7e,0x6652b131,0x878ac246,0x9dadc668,0xc5d1ccb3,0xbfd4bbce,0xc5d1ccb3,
+ 0x44738821,0xc3b9eb54,0x664a9c42,0x7e80b33e,0x8ea1b856,0xb2c1c19a,0xaac6adbd,0xb2c1c19a,0x445e7e1e,0xc5bcee61,0x5d3f8a3d,0x7b77ab41,0x7282a141,0x87a2ab63,0x7faa9e6e,0x87a2ab63,
+ 0x444a751c,0xc7bff170,0x55357739,0x7a6fa444,0x73769d41,0x6c7d983f,0x6685923d,0x6c7d983f,0x44356c1a,0xc9c2f47d,0x4c2b6535,0x78669c47,0x756a9a42,0x716d983d,0x6e719639,0x716d983d,
+ 0x44216318,0xccc6f78c,0xccc6ff8c,0xccc6f78c,0xccc6ef8c,0xccc6e78c,0xccc6de8c,0xccc6e78c,0x55338812,0x5533821b,0x55337d25,0x5533821b,0x55338812,0x55338d09,0x55339200,0x55338d09,
+ 0x0069a769,0x6646ab12,0xddb5d6c6,0x6646ab12,0xa18ca2c6,0x6646af06,0x6646b100,0x6646af06,0x0063a774,0x7758d309,0xe5adc1e2,0x7758d309,0x8c6f7ce2,0x7758d103,0x7758d000,0x7758d103,
+ 0x005ea67f,0x886bfb00,0xeea5adff,0x886bfb00,0x775256ff,0x886bf300,0x886bef00,0x886bf300,0x0076b97f,0x816df800,0xf2bbc1ff,0x816df800,0x997d7dff,0x6c5e5bff,0x3f3f39ff,0x6c5e5bff,
+ 0x008fcc7f,0x7b70f500,0xf6d2d6ff,0x7b70f500,0x7f75f300,0x837af100,0x887fef00,0x837af100,0x00a7de7f,0x7472f200,0xfae9ebff,0xebdedbff,0xddd4ccff,0xcecabcff,0xbfbfadff,0xcecabcff,
+ 0x00bff17f,0x6e75ef00,0x666bef00,0x6e75ef00,0x777fef00,0x7f8aef00,0x8894ef00,0x7f8aef00,0x6a7cd508,0x00aee670,0x00a8ec6e,0x00aee670,0x00b5e072,0x00bcd974,0x00c3d477,0x00bcd974,
+ };
+ UInt32 res[kSizeX*kSizeY];
+ DecompressPVRTC<false,true> (reinterpret_cast<const PVRTCBlock*>(src), kSizeX, kSizeY, reinterpret_cast<UInt8*>(res));
+ CHECK_ARRAY_EQUAL (expected, res, kSizeX*kSizeY);
+ }
+ TEST (DecodePVRTC_4_16x8)
+ {
+ const int kSizeX = 16;
+ const int kSizeY = 8;
+ UInt32 src[kSizeX*kSizeY*4/32] = {
+ 0x4c4c4c4c,0x63fb3494,0x00fc4c4c,0x68fc2352,0xa9fefefe,0x5bac1078,0x00ff5555,0x68dc2072,
+ 0x32323232,0x7faa40f7,0xa802f232,0xffff30e7,0xff030303,0x0f0040e6,0xaa00ff00,0xff9f40e9,
+ };
+ UInt32 expected[kSizeX*kSizeY] = {
+ 0x6e5ab31c,0xb8aedc87,0x55357739,0x7a6fa444,0x9394b95b,0xbfc3d493,0xbbc6c6a5,0xc9c9cabb,0x0098c169,0x6668d600,0xf6d2d6ff,0x7b70f500,0xbba9a5ff,0x837af100,0x887fef00,0x7b6dd10e,
+ 0x725abc1e,0xa89ace75,0x5d3f8a3d,0x7b77ab41,0x909ab858,0xb8c2cb96,0xb2c6b9b1,0xc2c2bbc4,0x009aba6c,0x6a6fd900,0xf2bbc1ff,0x816df800,0x997d7dff,0x8572f200,0x8875ef00,0x7d68d50f,
+ 0x775ac621,0x9988bf65,0x664a9c42,0x7e80b33e,0x8ea1b856,0xb2c1c19a,0xaac6adbd,0xbbbdadce,0x009cb36f,0x6e77dc00,0xeea5adff,0x886bfb00,0x775256ff,0x886bf300,0x886bef00,0x7f63da10,
+ 0x725abc1e,0xa89ace75,0x5d3f8a3d,0x7b77ab41,0x7282a141,0x87a2ab63,0x7faa9e6e,0x90a6ac7a,0x009aba6c,0x6a6fd900,0xf2bbc1ff,0x816df800,0x997d7dff,0x6c5e5bff,0x3f3f39ff,0x625d6bd1,
+ 0x6e5ab31c,0xb8aedc87,0x55357739,0x7a6fa444,0x73769d41,0x6c7d983f,0x6685923d,0x7588a846,0x0098c169,0x6668d600,0xf6d2d6ff,0x7b70f500,0x7f75f300,0x837af100,0x887fef00,0x7b6dd10e,
+ 0x6a5aaa1a,0xc8c0eb97,0x4c2b6535,0x78669c47,0x756a9a42,0x716d983d,0x6e719639,0x7b7bac42,0x0096c866,0x615fd300,0xfae9ebff,0xebdedbff,0xddd4ccff,0xcecabcff,0xbfbfadff,0xc2bfc1dc,
+ 0x665aa018,0xd8d4f9a9,0xccc6ff8c,0xccc6f78c,0xccc6ef8c,0xccc6e78c,0xccc6de8c,0xd8d4e7a9,0x0094d063,0x5d58d000,0x666bef00,0x6e75ef00,0x777fef00,0x7f8aef00,0x8894ef00,0x7777c80c,
+ 0x6a5aaa1a,0x5b438728,0x4c2b6535,0x48306828,0x44356c1a,0x3f3a6f0d,0x3b3f7300,0x484a9300,0x5555b300,0x009fdb72,0x00aaef7f,0x00a8e67f,0x00a7de7f,0x00a5d67f,0x00a4ce7f,0x0098c674,
+ };
+ UInt32 res[kSizeX*kSizeY];
+ DecompressPVRTC<false,true> (reinterpret_cast<const PVRTCBlock*>(src), kSizeX, kSizeY, reinterpret_cast<UInt8*>(res));
+ CHECK_ARRAY_EQUAL (expected, res, kSizeX*kSizeY);
+ }
+ TEST (TwiddleUVPVRTC)
+ {
+ // 00000000, 11111111 = 0101010101010101
+ CHECK_EQUAL (0x5555, TwiddleUVPVRTC (0x100, 0x100, 0xFF, 0x00));
+ // 00011011, 11110000 = 0101011110001010
+ CHECK_EQUAL (0x578A, TwiddleUVPVRTC (0x100, 0x100, 0xF0, 0x1B));
+
+ // 10100000, 1111 = 101001010101
+ CHECK_EQUAL (0xA55, TwiddleUVPVRTC (0x10, 0x100, 0xF, 0xA0));
+ // 0000, 11101111 = 111001010101
+ CHECK_EQUAL (0xE55, TwiddleUVPVRTC (0x100, 0x10, 0xEF, 0x0));
+ }
+ #endif // HAS_PVRTC_DECOMPRESSOR
+ #endif
+
+} // SUITE
+
+#endif // ENABLE_UNIT_TESTS
diff --git a/Runtime/Graphics/S3Decompression.h b/Runtime/Graphics/S3Decompression.h
new file mode 100644
index 0000000..d29a4bc
--- /dev/null
+++ b/Runtime/Graphics/S3Decompression.h
@@ -0,0 +1,18 @@
+#ifndef S3DECOMPRESSION_H
+#define S3DECOMPRESSION_H
+
+#include "TextureFormat.h"
+
+// Decompresses into RGBA32
+bool DecompressNativeTextureFormat (
+ TextureFormat srcFormat, int srcWidth, int srcHeight, const UInt32* srcData,
+ int destWidth, int destHeight, UInt32* destData );
+
+// Some texture formats also need to know the mipLevel
+bool DecompressNativeTextureFormatWithMipLevel( TextureFormat srcFormat, int srcWidth, int srcHeight, int mipLevel, const UInt32* sourceData,
+ int destWidth, int destHeight, UInt32* destData );
+
+
+void DecompressDXT1 (int xblocks, int yblocks, int destWidth, const UInt32* m_pCompBytes, UInt32* m_pDecompBytes);
+
+#endif
diff --git a/Runtime/Graphics/ScreenManager.cpp b/Runtime/Graphics/ScreenManager.cpp
new file mode 100644
index 0000000..4094338
--- /dev/null
+++ b/Runtime/Graphics/ScreenManager.cpp
@@ -0,0 +1,201 @@
+#include "UnityPrefix.h"
+#include "ScreenManager.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/GfxDevice/GfxDeviceSetup.h"
+
+static ScreenManagerPlatform* gScreenManager = NULL;
+
+#if UNITY_IPHONE
+ extern "C" void NotifyAutoOrientationChange();
+#endif
+
+
+void InitScreenManager()
+{
+ Assert(gScreenManager == NULL);
+ gScreenManager = new ScreenManagerPlatform();
+}
+
+void ReleaseScreenManager()
+{
+ Assert(gScreenManager != NULL);
+ delete gScreenManager;
+ gScreenManager = NULL;
+}
+
+ScreenManagerPlatform &GetScreenManager()
+{
+ Assert(gScreenManager != NULL);
+ return *gScreenManager;
+}
+
+ScreenManagerPlatform* GetScreenManagerPtr()
+{
+ return gScreenManager;
+}
+
+ScreenManager::ScreenManager ()
+{
+ m_Width = 0;
+ m_Height = 0;
+ m_SwitchResolutionCallback = NULL;
+ m_CursorInWindow = false;
+ m_IsFullscreen = false;
+ m_AllowCursorHide = true;
+ m_AllowCursorLock = true;
+
+ m_ScreenOrientation = kPortrait;
+ m_RequestedOrientation = kScreenOrientationUnknown;
+
+ // Do not allow to autorotate by default.
+ m_EnabledOrientations = 0;
+}
+
+void ScreenManager::RequestResolution (int width, int height, bool fullscreen, int preferredRefreshRate)
+{
+ m_ResolutionRequest.width = width;
+ m_ResolutionRequest.height = height;
+ m_ResolutionRequest.fullScreen = fullscreen ? 1 : 0;
+ m_ResolutionRequest.refreshRate = preferredRefreshRate;
+}
+
+void ScreenManager::RequestSetFullscreen (bool fullscreen)
+{
+ m_ResolutionRequest.fullScreen = fullscreen ? 1 : 0;
+}
+
+bool ScreenManager::HasFullscreenRequested () const
+{
+ return m_ResolutionRequest.fullScreen == 1;
+}
+
+void ScreenManager::SetCursorInsideWindow (bool insideWindow)
+{
+ m_CursorInWindow = insideWindow;
+ SetShowCursor(GetShowCursor());
+}
+
+int ScreenManager::FindClosestResolution (const ScreenManager::Resolutions& resolutions, int width, int height) const
+{
+ if (resolutions.empty ())
+ return -1;
+
+ int maxDistance = std::numeric_limits<int>::max ();
+ int index = 0;
+ for (int i=0;i<resolutions.size ();i++)
+ {
+ int curWidth = resolutions[i].width;
+ int curHeight = resolutions[i].height;
+ int distance = Abs (width - curWidth) + Abs (height - curHeight);
+ if (distance < maxDistance)
+ {
+ index = i;
+ maxDistance = distance;
+ }
+ }
+ return index;
+}
+
+void ScreenManager::RegisterDidSwitchResolutions (DidSwitchResolutions* resolution)
+{
+ m_SwitchResolutionCallback = resolution;
+}
+
+void ScreenManager::SetRequestedResolution ()
+{
+ if (m_ResolutionRequest.width != -1 && m_ResolutionRequest.height != -1)
+ {
+ SetResolutionImmediate (m_ResolutionRequest.width, m_ResolutionRequest.height, m_ResolutionRequest.IsFullScreen(), m_ResolutionRequest.refreshRate);
+ m_ResolutionRequest.Reset();
+ }
+
+ if (m_ResolutionRequest.fullScreen != -1)
+ {
+ SetIsFullScreenImmediate (m_ResolutionRequest.fullScreen ? true : false);
+ m_ResolutionRequest.fullScreen = -1;
+ }
+}
+
+void ScreenManager::SetIsFullScreenImmediate (bool fullscreen)
+{
+ if (fullscreen != IsFullScreen())
+ SetResolutionImmediate (GetWidth (), GetHeight (), fullscreen, 0);
+}
+
+ScreenManager::Resolution ScreenManager::GetCurrentResolution() const
+{
+ Resolution res;
+ res.width = GetWidth();
+ res.height = GetHeight();
+ res.refreshRate = 0;
+ return res; //@TODO
+}
+
+void ScreenManager::SetupScreenManagerEditor( float w, float h )
+{
+ m_Width = RoundfToIntPos(w);
+ m_Height = RoundfToIntPos(h);
+}
+
+
+void ScreenManager::SetAllowCursorHide (bool allowHide)
+{
+ m_AllowCursorHide = allowHide;
+ if (!m_AllowCursorHide)
+ SetShowCursor(true);
+}
+
+void ScreenManager::SetAllowCursorLock (bool allowLock)
+{
+ m_AllowCursorLock = allowLock;
+ if (!m_AllowCursorLock)
+ SetLockCursor(false);
+}
+
+
+void ScreenManager::SetIsOrientationEnabled(EnabledOrientation orientation, bool enabled)
+{
+#if UNITY_IPHONE
+ NotifyAutoOrientationChange();
+#endif
+
+#if UNITY_WP8
+ // upside down portrait is not available on wp8
+ if (orientation == EnabledOrientation::kAutorotateToPortraitUpsideDown)
+ enabled = false;
+#endif
+
+ if (enabled)
+ m_EnabledOrientations |= orientation;
+ else
+ m_EnabledOrientations &= ~orientation;
+}
+
+
+#include "Runtime/Misc/PlayerSettings.h"
+
+void ScreenManager::EnableOrientationsFromPlayerSettings()
+{
+ SetIsOrientationEnabled(kAutorotateToPortrait, GetPlayerSettings().GetAutoRotationAllowed(0));
+ SetIsOrientationEnabled(kAutorotateToPortraitUpsideDown, GetPlayerSettings().GetAutoRotationAllowed(1));
+ SetIsOrientationEnabled(kAutorotateToLandscapeRight, GetPlayerSettings().GetAutoRotationAllowed(2));
+ SetIsOrientationEnabled(kAutorotateToLandscapeLeft, GetPlayerSettings().GetAutoRotationAllowed(3));
+}
+
+#define INIT_ORIENT_FROM_PLAYER_SETTINGS_IMPL(name, func) \
+void ScreenManager::name(int playerSettingsOrient) \
+{ \
+ switch(playerSettingsOrient) \
+ { \
+ case 0: func(kPortrait); break; \
+ case 1: func(kPortraitUpsideDown); break; \
+ case 2: func(kLandscapeRight); break; \
+ case 3: func(kLandscapeLeft); break; \
+ \
+ default: Assert(false && #name "do not accept autorotation."); \
+ } \
+} \
+
+INIT_ORIENT_FROM_PLAYER_SETTINGS_IMPL(SetConcreteOrientationFromPlayerSettings, SetScreenOrientation);
+INIT_ORIENT_FROM_PLAYER_SETTINGS_IMPL(RequestConcreteOrientationFromPlayerSettings, RequestOrientation);
diff --git a/Runtime/Graphics/ScreenManager.h b/Runtime/Graphics/ScreenManager.h
new file mode 100644
index 0000000..77adf85
--- /dev/null
+++ b/Runtime/Graphics/ScreenManager.h
@@ -0,0 +1,257 @@
+#pragma once
+
+#include <vector>
+
+enum ScreenOrientation
+{
+ kScreenOrientationUnknown,
+
+ kPortrait,
+ kPortraitUpsideDown,
+ kLandscapeLeft,
+ kLandscapeRight,
+
+ kAutoRotation,
+
+ kScreenOrientationCount
+};
+
+inline ScreenOrientation PlayerSettingsToScreenOrientation(int defaultScreenOrientation)
+{
+ switch (defaultScreenOrientation) {
+ case 0 :
+ return kPortrait;
+ case 1 :
+ return kPortraitUpsideDown;
+ case 2 :
+ return kLandscapeRight;
+ case 3 :
+ return kLandscapeLeft;
+ case 4 :
+ return kAutoRotation;
+ default:
+ return kScreenOrientationUnknown;
+ }
+}
+
+inline int ScreenOrientationToPlayerSettings(ScreenOrientation screenOrientation)
+{
+ switch (screenOrientation) {
+ case kPortrait :
+ return 0;
+ case kPortraitUpsideDown :
+ return 1;
+ case kLandscapeRight :
+ return 2;
+ case kLandscapeLeft :
+ return 3;
+ case kAutoRotation :
+ return 4;
+ default:
+ return 5;
+ }
+}
+
+
+enum SleepTimeout
+{
+ kNeverSleep = -1,
+ kSystemSetting = -2,
+};
+
+enum EnabledOrientation
+{
+ kAutorotateToPortrait = 1,
+ kAutorotateToPortraitUpsideDown = 2,
+ kAutorotateToLandscapeLeft = 4,
+ kAutorotateToLandscapeRight = 8
+};
+
+#define kResolutionWidth "Screenmanager Resolution Width"
+#define kResolutionHeight "Screenmanager Resolution Height"
+#define kIsFullScreen "Screenmanager Is Fullscreen mode"
+#define kGraphicsQuality "UnityGraphicsQuality"
+
+class ScreenManager
+{
+public:
+ ScreenManager ();
+
+ void RequestResolution (int width, int height, bool fullscreen, int preferredRefreshRate);
+ void RequestSetFullscreen (bool fullscreen);
+ bool HasFullscreenRequested () const;
+
+ virtual void SetRequestedResolution ();
+ void SetIsFullScreenImmediate (bool fullscreen);
+ virtual bool SetResolutionImmediate (int /*width*/, int /*height*/, bool /*fullscreen*/, int /*preferredRefreshRate*/) { return false; }
+
+ struct Resolution
+ {
+ // Keep in sync with .NET Resolution struct!
+ int width;
+ int height;
+ int refreshRate;
+
+ friend bool operator < (const Resolution& lhs, const Resolution& rhs)
+ {
+ if( lhs.width != rhs.width )
+ return lhs.width < rhs.width;
+ return lhs.height < rhs.height;
+ }
+
+ /**
+ * Returns true if this resolution is >= given width/height.
+ * Used to filter out resolutions that are too big for windowed modes.
+ */
+ bool IsTooBigFor( int w, int h ) const
+ {
+ return width >= w || height >= h;
+ }
+
+ bool IsRotated() const { return height > width; }
+
+ Resolution ()
+ {
+ width = 0;
+ height = 0;
+ refreshRate = 0;
+ }
+ };
+
+ struct ResolutionRequest
+ {
+ ResolutionRequest() { Reset(); }
+
+ int width;
+ int height;
+ int fullScreen;
+ int refreshRate;
+
+ bool IsFullScreen() const { return fullScreen == 1; }
+
+ void Reset()
+ {
+ width = -1;
+ height = -1;
+ fullScreen = -1;
+ refreshRate = -1;
+ }
+ };
+
+ typedef std::vector<Resolution> Resolutions;
+ virtual Resolutions GetResolutions (int /*preferredRefreshRate*/ = 0, bool /*clampToDesktopRes*/ = false) { return Resolutions(); }
+ int FindClosestResolution (const ScreenManager::Resolutions& resolutions, int width, int height) const;
+
+ virtual Resolution GetCurrentResolution() const;
+
+ virtual bool GetShowCursor () const { return true; }
+ virtual void SetShowCursor (bool /*show*/) { }
+
+ virtual bool GetLockCursor () const { return false; }
+ virtual void SetLockCursor (bool /*lock*/) { }
+
+ virtual bool GetAllowLayeredRendering () const { return true; }
+ virtual void SetAllowLayeredRendering (bool /*allow*/) { }
+
+ virtual int GetScreenTimeout () const { return kNeverSleep; }
+ virtual void SetScreenTimeout (int /*value*/) { }
+
+ bool GetAllowCursorHide() const { return m_AllowCursorHide; }
+ void SetAllowCursorHide (bool allowHide);
+
+ bool GetAllowCursorLock() const { return m_AllowCursorLock; }
+ void SetAllowCursorLock (bool allowLock);
+
+ // Named *IsFocus to avoid confusion with windows API
+ virtual bool GetIsFocused() const { return true; }
+ virtual void SetIsFocused (bool /*focus*/) { }
+
+ virtual bool GetCursorInsideWindow () const { return m_CursorInWindow; }
+ virtual void SetCursorInsideWindow (bool insideWindow);
+
+ // Do these need to be virtual?
+ virtual int GetWidth () const { return m_Width; }
+ virtual int GetHeight () const { return m_Height; }
+
+ int GetWidthAsRequested () const { return m_ResolutionRequest.width == -1 ? GetWidth () : m_ResolutionRequest.width; }
+ int GetHeightAsRequested () const { return m_ResolutionRequest.height == -1 ? GetHeight () : m_ResolutionRequest.height; }
+ bool GetFullScreenAsRequested () const { return m_ResolutionRequest.fullScreen == -1 ? IsFullScreen () : m_ResolutionRequest.IsFullScreen (); }
+ int GetRefreshRateAsRequested () const { return m_ResolutionRequest.refreshRate == -1 ? GetCurrentResolution ().refreshRate : m_ResolutionRequest.refreshRate; }
+
+ virtual float GetDPI () const { return 0.f; }
+ virtual bool IsFullScreen () const { return m_IsFullscreen; }
+
+ void RequestOrientation(ScreenOrientation value) { m_RequestedOrientation = value; }
+ ScreenOrientation GetRequestedOrientation() const { return m_RequestedOrientation; }
+
+ virtual ScreenOrientation GetScreenOrientation() const { return m_ScreenOrientation; };
+ virtual void SetScreenOrientation (ScreenOrientation value) { m_ScreenOrientation = value; }
+
+ typedef void DidSwitchResolutions ();
+ void RegisterDidSwitchResolutions (DidSwitchResolutions* resolution);
+
+ virtual void SetupScreenManagerEditor(float w, float h);
+
+ void SetIsOrientationEnabled(EnabledOrientation orientation, bool enabled);
+ bool GetIsOrientationEnabled(EnabledOrientation orientation) const { return m_EnabledOrientations & orientation; }
+
+ // helpers for connecting ScreenManager and PlayerSettings
+ void EnableOrientationsFromPlayerSettings();
+ void SetConcreteOrientationFromPlayerSettings(int playerSettingsOrient);
+ void RequestConcreteOrientationFromPlayerSettings(int playerSettingsOrient);
+
+protected:
+ ResolutionRequest m_ResolutionRequest;
+
+ DidSwitchResolutions* m_SwitchResolutionCallback;
+
+ bool m_AllowCursorHide;
+ bool m_AllowCursorLock;
+ bool m_CursorInWindow;
+ bool m_IsFullscreen;
+
+ int m_Width;
+ int m_Height;
+
+ unsigned int m_EnabledOrientations;
+ ScreenOrientation m_ScreenOrientation;
+ ScreenOrientation m_RequestedOrientation;
+};
+
+#if UNITY_PEPPER
+#include "PlatformDependent/PepperPlugin/ScreenManagerPepper.h"
+#elif UNITY_OSX
+#include "PlatformDependent/OSX/ScreenManagerOSX.h"
+#elif UNITY_WP8
+#include "PlatformDependent/WP8Player/ScreenManagerWP8.h"
+#elif UNITY_METRO
+#include "PlatformDependent/MetroPlayer/ScreenManagerMetro.h"
+#elif UNITY_WIN
+#include "../../PlatformDependent/WinPlayer/ScreenManagerWin.h"
+#elif UNITY_XENON
+#include "../../PlatformDependent/Xbox360/Source/ScreenManagerXenon.h"
+#elif UNITY_PS3
+#include "../../PlatformDependent/PS3Player/ScreenManagerPS3.h"
+#elif UNITY_ANDROID
+#include "PlatformDependent/AndroidPlayer/ScreenManagerAndroid.h"
+#elif UNITY_IPHONE
+#include "PlatformDependent/iPhonePlayer/ScreenManagerIPhone.h"
+#elif UNITY_LINUX && SUPPORT_X11
+#include "PlatformDependent/Linux/ScreenManagerLinux.h"
+#elif UNITY_WII
+#include "PlatformDependent/Wii/WiiScreenManager.h"
+#elif UNITY_FLASH
+#include "PlatformDependent/FlashSupport/cpp/FlashScreenManager.h"
+#elif UNITY_BB10
+#include "PlatformDependent/BB10Player/ScreenManagerBB10.h"
+#elif UNITY_TIZEN
+#include "PlatformDependent/TizenPlayer/ScreenManagerTizen.h"
+#else
+#define ScreenManagerPlatform ScreenManager
+#endif
+
+void InitScreenManager();
+void ReleaseScreenManager();
+ScreenManagerPlatform &GetScreenManager();
+ScreenManagerPlatform* GetScreenManagerPtr();
+
diff --git a/Runtime/Graphics/SpriteFrame.cpp b/Runtime/Graphics/SpriteFrame.cpp
new file mode 100644
index 0000000..80c568f
--- /dev/null
+++ b/Runtime/Graphics/SpriteFrame.cpp
@@ -0,0 +1,272 @@
+#include "UnityPrefix.h"
+#include "SpriteFrame.h"
+
+#if ENABLE_SPRITES
+
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Graphics/SpriteUtility.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Misc/BuildSettings.h"
+#if UNITY_EDITOR
+ #include "Editor/Src/EditorSettings.h"
+ #include "Editor/Src/SpritePacker/SpritePacker.h"
+ #include "Runtime/BaseClasses/IsPlaying.h"
+#endif
+
+static const int kMinSpriteDimensionForMesh = 32;
+static const float kSpriteAABBDepth = 0.1f;
+using namespace Unity;
+
+IMPLEMENT_CLASS(Sprite)
+IMPLEMENT_OBJECT_SERIALIZE(Sprite)
+
+Sprite::Sprite(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_PixelsToUnits(100.0f)
+, m_Extrude(0)
+#if UNITY_EDITOR
+, m_AtlasReady(false)
+#endif
+{
+#if ENABLE_MULTITHREADED_CODE
+ m_CurrentCPUFence = 0;
+ m_WaitOnCPUFence = false;
+#endif
+}
+
+Sprite::~Sprite()
+{
+ WaitOnRenderThreadUse();
+ m_IntermediateUsers.Notify( kImNotifyAssetDeleted );
+}
+
+void Sprite::AwakeFromLoad(AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad(mode);
+
+#if UNITY_EDITOR
+ RefreshAtlasRD();
+#endif
+}
+
+template<class TransferFunction>
+void Sprite::Transfer(TransferFunction& transfer)
+{
+ Super::Transfer(transfer);
+
+ TRANSFER(m_Rect);
+ TRANSFER(m_Offset);
+ TRANSFER(m_PixelsToUnits);
+ TRANSFER(m_Extrude);
+
+ TRANSFER_EDITOR_ONLY_HIDDEN(m_AtlasName);
+ TRANSFER_EDITOR_ONLY_HIDDEN(m_PackingTag);
+
+#if UNITY_EDITOR
+ bool atlasPacked = !m_PackingTag.empty();
+ if (!atlasPacked)
+ {
+ transfer.Transfer(m_RD, "m_RD");
+ transfer.Align();
+ }
+ else
+ {
+ if (transfer.IsSerializingForGameRelease())
+ {
+ const bool packingEnabled = (GetEditorSettings().GetSpritePackerMode() != EditorSettings::kSPOff); // Make sure packing is not disabled
+ const bool serializePacked = (packingEnabled && GetIsPacked());
+
+ // Note: it is not expected that all sprites with atlas hints will be packed.
+ transfer.Transfer(serializePacked ? m_AtlasRD : m_RD, "m_RD");
+ transfer.Align();
+ }
+ else
+ {
+ transfer.Transfer(m_RD, "m_RD");
+ transfer.Transfer(m_AtlasRD, "m_AtlasRD", kHideInEditorMask);
+ }
+ }
+#else
+ transfer.Transfer(m_RD, "m_RD");
+ transfer.Align();
+#endif
+
+#if ENABLE_SPRITECOLLIDER
+ TRANSFER(m_Poly);
+#endif
+}
+
+bool Sprite::GetIsPacked() const
+{
+#if UNITY_EDITOR
+ return (m_AtlasReady != 0);
+#else
+ return m_RD.settings.packed;
+#endif
+}
+
+#if UNITY_EDITOR
+static bool s_packingAllowedInPlayMode = false;
+void Sprite::OnEnterPlaymode()
+{
+ s_packingAllowedInPlayMode = (GetEditorSettings().GetSpritePackerMode() == EditorSettings::kSPOn);
+}
+#endif
+
+const SpriteRenderData& Sprite::GetRenderDataForPlayMode() const
+{
+#if UNITY_EDITOR
+ return GetRenderData(s_packingAllowedInPlayMode && IsWorldPlaying());
+#else
+ return GetRenderData(true);
+#endif
+}
+
+const SpriteRenderData& Sprite::GetRenderData(bool getEditorOnlyAtlasRenderDataIfPacked) const
+{
+#if UNITY_EDITOR
+ if (getEditorOnlyAtlasRenderDataIfPacked && GetIsPacked())
+ return m_AtlasRD;
+#endif
+ return m_RD;
+}
+
+void Sprite::Initialize(Texture2D* texture, const Rectf& rect, const Vector2f& pivot, float pixelsToUnits, unsigned int extrude, SpriteMeshType meshType)
+{
+ const bool hasAdvancedVersion = GetBuildSettings().hasAdvancedVersion;
+
+ Assert(texture);
+ bool generateRenderMesh = (meshType == kSpriteMeshTypeTight);
+ generateRenderMesh &= (rect.width >= kMinSpriteDimensionForMesh);
+ generateRenderMesh &= (rect.height >= kMinSpriteDimensionForMesh);
+ generateRenderMesh &= (texture->GetRawImageData() != NULL); //BUGFIX:569636 - if we can't access pixel data, generate a quad.
+ generateRenderMesh &= hasAdvancedVersion; //Note: tight mesh is Pro only.
+
+ // Common data
+ m_Rect = rect;
+ m_Offset = PivotToOffset(rect, pivot);
+ m_PixelsToUnits = pixelsToUnits;
+ m_Extrude = hasAdvancedVersion ? extrude : 0; //Note: extrude is Pro only.
+
+ // Render data
+ m_RD.texture = texture;
+ if (generateRenderMesh)
+ {
+ Rectf meshRect;
+ m_RD.GenerateFullMesh(m_Rect, m_Offset, pixelsToUnits, m_Extrude, &meshRect);
+ m_RD.textureRect = meshRect;
+ m_RD.textureRect.x += m_Rect.GetPosition().x;
+ m_RD.textureRect.y += m_Rect.GetPosition().y;
+ }
+ else
+ {
+ //Note: we could do alpha-trim here. But do we want to lose the texture space in this case?
+ m_RD.GenerateQuadMesh(m_Rect, m_Offset, pixelsToUnits);
+ m_RD.textureRect = m_Rect;
+ }
+ m_RD.textureRectOffset = m_RD.textureRect.GetPosition() - rect.GetPosition();
+}
+
+void Sprite::WaitOnRenderThreadUse()
+{
+#if ENABLE_MULTITHREADED_CODE
+ if (m_WaitOnCPUFence)
+ {
+ GetGfxDevice().WaitOnCPUFence(m_CurrentCPUFence);
+ m_WaitOnCPUFence = false;
+ }
+#endif
+}
+
+void Sprite::GenerateOutline(float detail, unsigned char alphaTolerance, bool holeDetection, std::vector<dynamic_array<Vector2f> >& outVertices, int extrudeOverride)
+{
+ unsigned int extrude = (extrudeOverride < 0) ? m_Extrude : extrudeOverride;
+ GenerateSpriteOutline(m_RD.texture, m_PixelsToUnits, m_Rect, m_Offset, detail, alphaTolerance, holeDetection, extrude, kPathEmbed, &outVertices);
+}
+
+SpriteRenderData::SpriteRenderData()
+: settingsRaw(0)
+{
+}
+
+void SpriteRenderData::GenerateFullMesh(const Rectf& rect, const Vector2f& rectOffset, float pixelsToUnits, unsigned int extrude, Rectf* meshRect)
+{
+ GenerateSpriteOutline(texture, pixelsToUnits, rect, rectOffset, kSpriteDefaultDetail, kSpriteDefaultAlphaTolerance, true, extrude, kPathEmbed, NULL, &vertices, &indices, meshRect);
+}
+
+void SpriteRenderData::GenerateQuadMesh(const Rectf& rect, const Vector2f& rectOffset, float pixelsToUnits)
+{
+ const float scaler = 1.0f / pixelsToUnits;
+ const int texGlWidth = texture->GetGLWidth();
+ const int texGlHeight = texture->GetGLHeight();
+
+ const float halfW = rect.width * 0.5f * scaler;
+ const float halfH = rect.height * 0.5f * scaler;
+ const Vector2f offset = rectOffset * scaler;
+ const Vector4f uv = Vector4f(rect.x / texGlWidth, rect.y / texGlHeight, rect.GetXMax() / texGlWidth, rect.GetYMax() / texGlHeight);
+
+ vertices.resize(4);
+ vertices[0].pos = Vector3f(-halfW - offset.x, halfH - offset.y, 0.0f);
+ vertices[0].uv = Vector2f(uv[0], uv[3]);
+ vertices[1].pos = Vector3f( halfW - offset.x, halfH - offset.y, 0.0f);
+ vertices[1].uv = Vector2f(uv[2], uv[3]);
+ vertices[2].pos = Vector3f(-halfW - offset.x, -halfH - offset.y, 0.0f);
+ vertices[2].uv = Vector2f(uv[0], uv[1]);
+ vertices[3].pos = Vector3f( halfW - offset.x, -halfH - offset.y, 0.0f);
+ vertices[3].uv = Vector2f(uv[2], uv[1]);
+
+ indices.resize(6);
+ indices[0] = 0;
+ indices[1] = 1;
+ indices[2] = 2;
+ indices[3] = 2;
+ indices[4] = 1;
+ indices[5] = 3;
+}
+
+AABB Sprite::GetBounds(Vector2f extraOffset) const
+{
+ Vector2f halfRect(m_Rect.width / m_PixelsToUnits * 0.5f, m_Rect.height / m_PixelsToUnits * 0.5f);
+ Vector2f offset(m_Offset.x / m_PixelsToUnits, m_Offset.y / m_PixelsToUnits);
+
+ MinMaxAABB minmax;
+ Vector3f minV(-halfRect.x - offset.x + extraOffset.x, halfRect.y - offset.y + extraOffset.y, kSpriteAABBDepth);
+ Vector3f maxV( halfRect.x - offset.x + extraOffset.x, -halfRect.y - offset.y + extraOffset.y, -kSpriteAABBDepth);
+ minmax.Encapsulate(minV);
+ minmax.Encapsulate(maxV);
+ return minmax;
+}
+
+Vector2f Sprite::PivotToOffset(const Rectf& rect, const Vector2f& pivot)
+{
+ const Vector2f alignPos = rect.GetPosition() + rect.GetSize().Scale(pivot);
+ Vector2f offset = alignPos - rect.GetCenterPos();
+ return offset;
+}
+
+#if UNITY_EDITOR
+void Sprite::RefreshAtlasRD ()
+{
+ UnityStr atlasName;
+ const SpriteRenderData* packedRD = (m_PackingTag.empty() ? NULL : SpritePacker::GetPackedSpriteRD(*this, atlasName));
+ if (packedRD)
+ {
+ WaitOnRenderThreadUse();
+ m_AtlasReady = true;
+ m_AtlasRD = *packedRD;
+ m_AtlasName = atlasName;
+ }
+ else
+ ClearAtlasRD ();
+}
+
+void Sprite::ClearAtlasRD ()
+{
+ WaitOnRenderThreadUse();
+ m_AtlasReady = false;
+ m_AtlasRD = SpriteRenderData ();
+ m_AtlasName.clear();
+}
+#endif
+
+#endif //ENABLE_SPRITES
diff --git a/Runtime/Graphics/SpriteFrame.h b/Runtime/Graphics/SpriteFrame.h
new file mode 100644
index 0000000..e319e2c
--- /dev/null
+++ b/Runtime/Graphics/SpriteFrame.h
@@ -0,0 +1,190 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_SPRITES
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Graphics/Polygon2D.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Camera/IntermediateUsers.h"
+
+const float kSpriteDefaultDetail = -1.0f; // if less than zero, automatic lod evaluation will performed
+const UInt8 kSpriteDefaultAlphaTolerance = 0;
+const UInt32 kSpriteDefaultExtrude = 1;
+const UInt8 kSpriteMaxAlphaTolerance = 254;
+
+class AABB;
+
+struct SpriteVertex
+{
+ DECLARE_SERIALIZE_NO_PPTR (SpriteVertex)
+
+ Vector3f pos;
+ Vector2f uv;
+};
+
+enum SpritePackingMode
+{
+ kSPMTight = 0,
+ kSPMRectangle
+};
+
+enum SpritePackingRotation
+{
+ kSPRNone = 0,
+ // Reserved
+ kSPRAny = 15
+};
+
+enum SpriteMeshType
+{
+ kSpriteMeshTypeFullRect = 0,
+ kSpriteMeshTypeTight = 1
+};
+
+struct SpriteRenderData
+{
+ DECLARE_SERIALIZE (SpriteRenderData)
+
+ SpriteRenderData();
+
+ PPtr<Texture2D> texture;
+
+ // Mesh is scaled according to kTexturePixelsPerUnit
+ std::vector<SpriteVertex> vertices;
+ std::vector<UInt16> indices;
+
+ Rectf textureRect; // In pixels (texture space). Invalid if packingMode is Tight.
+ Vector2f textureRectOffset; // In pixels (texture space). Invalid if packingMode is Tight.
+
+ union
+ {
+ struct
+ {
+ UInt32 packed : 1; // bool
+ UInt32 packingMode : 1; // SpritePackingMode
+ UInt32 packingRotation : 4; // SpritePackingRotation
+ UInt32 reserved : 26;
+ } settings;
+ UInt32 settingsRaw;
+ };
+
+ void GenerateQuadMesh(const Rectf& rect, const Vector2f& rectOffset, float pixelsToUnits);
+ void GenerateFullMesh(const Rectf& rect, const Vector2f& rectOffset, float pixelsToUnits, unsigned int extrude, Rectf* meshRect);
+};
+
+class Sprite : public NamedObject
+{
+public:
+ REGISTER_DERIVED_CLASS(Sprite, NamedObject)
+ DECLARE_OBJECT_SERIALIZE(Sprite)
+
+ Sprite(MemLabelId label, ObjectCreationMode mode);
+ // ~Sprite(); declared-by-macro
+
+ void Initialize(Texture2D* texture, const Rectf& rect, const Vector2f& pivot, float pixelsToUnits, unsigned int extrude, SpriteMeshType meshType);
+
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+
+ // detail [0; 1] where 0 is simplified and 1 is max quality.
+ void GenerateOutline(float detail, unsigned char alphaTolerance, bool holeDetection, std::vector<dynamic_array<Vector2f> >& outVertices, int extrudeOverride = -1);
+
+ // Common data
+ Vector2f GetSize() const { return m_Rect.GetSize(); }
+ Vector2f GetSizeInUnits() const { return m_Rect.GetSize()/m_PixelsToUnits; }
+
+ const Rectf& GetRect() const { return m_Rect; }
+ const Vector2f& GetOffset() const { return m_Offset; }
+ float GetPixelsToUnits() const { return m_PixelsToUnits; }
+
+#if ENABLE_SPRITECOLLIDER
+ Polygon2D& GetPoly() { return m_Poly; }
+#endif
+
+ // Rendering data
+ // Note: we want live preview of atlases when in editor, but only for rendering in play mode.
+ // GetRenderData(false) will always return the source texture render data.
+ // GetRenderData(true) will return the atlas texture render data if it is available, otherwise fall back to source texture render data.
+ // GetRenderDataForPlayMode() is a convenience function which tries to get atlas texture render data when world is playing.
+ const SpriteRenderData& GetRenderData(bool getEditorOnlyAtlasRenderDataIfPacked) const;
+ const SpriteRenderData& GetRenderDataForPlayMode() const;
+
+ bool GetIsPacked() const;
+ AABB GetBounds(Vector2f extraOffset = Vector2f(0, 0)) const;
+
+#if UNITY_EDITOR
+ void SetPackingTag (const std::string& packingTag) { m_PackingTag = packingTag; }
+ std::string GetPackingTag() const { return m_PackingTag; }
+
+ std::string GetActiveAtlasName() const { return m_AtlasReady ? m_AtlasName : ""; }
+ Texture2D* GetActiveAtlasTexture() const { return m_AtlasReady ? m_AtlasRD.texture : NULL; }
+
+ // Changing texture import settings will rebuild Sprites and not use atlases until the next atlas rebuild, which will trigger RefreshAtlasRD or ClearAtlasRD.
+ void RefreshAtlasRD ();
+ void ClearAtlasRD ();
+
+ static void OnEnterPlaymode ();
+#endif
+
+#if ENABLE_MULTITHREADED_CODE
+ void SetCurrentCPUFence( UInt32 fence ) { m_CurrentCPUFence = fence; m_WaitOnCPUFence = true; }
+#endif
+ void WaitOnRenderThreadUse();
+
+ static Vector2f PivotToOffset(const Rectf& rect, const Vector2f& pivot); // [0;1] to rect space
+
+ void AddIntermediateUser( ListNode<IntermediateRenderer>& node ) { m_IntermediateUsers.AddUser(node); }
+
+private:
+ // Common data
+ Rectf m_Rect; // In pixels (originating texture, full rect definition)
+ Vector2f m_Offset; // In pixels (from center of final rect, defines pivot point based on alignment)
+
+ // Rendering data
+ SpriteRenderData m_RD;
+ float m_PixelsToUnits;
+ unsigned int m_Extrude;
+
+#if UNITY_EDITOR
+ int m_AtlasReady;
+ SpriteRenderData m_AtlasRD;
+ UnityStr m_AtlasName;
+ UnityStr m_PackingTag;
+#endif
+
+#if ENABLE_SPRITECOLLIDER
+ Polygon2D m_Poly; // Collider
+#endif
+
+#if ENABLE_MULTITHREADED_CODE
+ UInt32 m_CurrentCPUFence;
+ bool m_WaitOnCPUFence;
+#endif
+
+ IntermediateUsers m_IntermediateUsers; // IntermediateRenderer users of this sprite
+};
+
+template<class TransferFunction>
+void SpriteVertex::Transfer(TransferFunction& transfer)
+{
+ transfer.Transfer(pos, "pos");
+ transfer.Transfer(uv, "uv");
+}
+
+template<class TransferFunction>
+void SpriteRenderData::Transfer(TransferFunction& transfer)
+{
+ TRANSFER(texture);
+ TRANSFER(vertices);
+ TRANSFER(indices);
+
+ transfer.Align();
+ TRANSFER(textureRect);
+ TRANSFER(textureRectOffset);
+ TRANSFER(settingsRaw);
+}
+
+#endif //ENABLE_SPRITES
diff --git a/Runtime/Graphics/SpriteUtility.cpp b/Runtime/Graphics/SpriteUtility.cpp
new file mode 100644
index 0000000..5c131c3
--- /dev/null
+++ b/Runtime/Graphics/SpriteUtility.cpp
@@ -0,0 +1,155 @@
+#include "UnityPrefix.h"
+#include "SpriteUtility.h"
+
+#if ENABLE_SPRITES
+
+#include "Runtime/Geometry/SpriteMeshGenerator.h"
+
+static const float kSpriteEdgeBias = 1.0f;
+
+bool GetSpriteMeshRectPixelBounds (const Texture2D& texture, const Rectf& rectInaccurate, int& rectX, int& rectY, int& rectRight, int& rectBottom)
+{
+ rectX = Floorf(rectInaccurate.x);
+ rectY = Floorf(rectInaccurate.y);
+ rectRight = Ceilf(rectInaccurate.GetRight());
+ rectBottom = Ceilf(rectInaccurate.GetBottom());
+
+ // Sanity check to see if out of texture rect or if error is more than 1 pixel for whole-texture sprites.
+ const bool errorX = (rectX < 0);
+ const bool errorY = (rectY < 0);
+ const bool errorR = (rectRight > texture.GetGLWidth() + 1);
+ const bool errorB = (rectBottom > texture.GetGLHeight() + 1);
+ const bool hasError = (errorX || errorY || errorR || errorB);
+ Assert(!hasError);
+
+ // In rare cases when a texture is downscaled to match the max texture size limit it might introduce an odd scaling factor, cause a 1 pixel error and grow beyond the texture size.
+ rectRight = std::min(rectRight, texture.GetGLWidth());
+ rectBottom = std::min(rectBottom, texture.GetGLHeight());
+
+ return hasError;
+}
+
+void GenerateSpriteOutline(PPtr<Texture2D> texture, float pixelsToUnits, const Rectf& rectInaccurate, const Vector2f& rectOffset, float detail, unsigned char alphaTolerance, bool holeDetection, unsigned int extrude, int simplifyMode, std::vector<dynamic_array<Vector2f> >* outLine, std::vector<SpriteVertex>* outVertices, std::vector<UInt16>* outIndices, Rectf* meshRect)
+{
+ if(texture.IsNull())
+ return;
+
+ const int texW = texture->GetGLWidth();
+ const int texH = texture->GetGLHeight();
+
+ // Figure out what pixels to use.
+ // If a platform texture size limit is set, rectInaccurate will not define actual pixels.
+ int rectX, rectY, rectRight, rectBottom;
+ GetSpriteMeshRectPixelBounds(*texture, rectInaccurate, rectX, rectY, rectRight, rectBottom);
+ const int rectWidth = rectRight - rectX;
+ const int rectHeight = rectBottom - rectY;
+ const float rectAdjustX = rectInaccurate.x - rectX;
+ const float rectAdjustY = rectInaccurate.y - rectY;
+
+ // Extract rectangle
+ const int imageSize = rectWidth * rectHeight * sizeof(ColorRGBA32);
+ ColorRGBA32* imageData = (ColorRGBA32*)UNITY_MALLOC(kMemDefault, imageSize);
+ {
+ const int textureSize = texW * texH * sizeof(ColorRGBA32);
+ ColorRGBA32* texData = (ColorRGBA32*)UNITY_MALLOC(kMemDefault, textureSize);
+ texture->GetPixels32(0, texData);
+
+ for (int row = 0; row < rectHeight; ++row)
+ {
+ ColorRGBA32* dest = imageData + row * rectWidth;
+ ColorRGBA32* src = texData + (rectY + row) * texW + rectX;
+ UNITY_MEMCPY(dest, src, sizeof(ColorRGBA32) * rectWidth);
+ }
+
+ UNITY_FREE(kMemDefault, texData);
+ }
+
+ // Detect shape
+ const float hullTolerance = (detail>=0.0f) ? 1.0f - clamp01(detail) : detail;
+ extrude = clamp<unsigned int>(extrude, 0, 32);
+ SpriteMeshGenerator smg;
+ smg.MakeShape(imageData, rectWidth, rectHeight, hullTolerance, alphaTolerance, holeDetection, extrude, kSpriteEdgeBias, simplifyMode);
+
+ // Adjustments for offset and pixel-to-unity scale.
+ const float scale = 1.0 / pixelsToUnits;
+ const float scale_x = 1.0 / texW;
+ const float scale_y = 1.0 / texH;
+ const Vector2f oxy(float(rectWidth) * 0.5f + rectOffset.x - rectAdjustX, float(rectHeight) * 0.5f + rectOffset.y - rectAdjustY);
+
+ // Fill outline
+ if (outLine)
+ {
+ const std::vector<SpriteMeshGenerator::path>& paths = smg.GetPaths();
+ outLine->resize(paths.size());
+
+ int ci = 0;
+ for (std::vector<SpriteMeshGenerator::path>::const_iterator p = paths.begin(); p != paths.end(); ++p)
+ {
+ const SpriteMeshGenerator::path& path = *p;
+ const std::vector<SpriteMeshGenerator::vertex>& poly = path.m_path;
+ dynamic_array<Vector2f> finalPath;
+ finalPath.reserve(poly.size());
+ for (std::vector<SpriteMeshGenerator::vertex>::const_iterator it = poly.begin(); it != poly.end(); ++it)
+ {
+ Vector2f polyPoint = it->p;
+ Vector2f finalPoint((polyPoint.x-oxy.x)*scale, (polyPoint.y-oxy.y)*scale);
+ finalPath.push_back(finalPoint);
+ }
+ (*outLine)[ci++] = finalPath;
+ }
+ }
+
+ // Fill mesh
+ if (outVertices)
+ {
+ Assert(outIndices);
+
+ // Decompose outline
+ std::vector<Vector2f> vertices;
+ std::vector<int> indices;
+ smg.Decompose(&vertices, &indices);
+
+ if (indices.size() > 0)
+ {
+ outVertices->clear();
+ outIndices->clear();
+ // Assign indices
+ std::reverse(indices.begin(), indices.end());
+ outIndices->assign(indices.begin(), indices.end());
+
+ // Assign vertices
+ outVertices->reserve(vertices.size());
+ for (std::vector<SpriteMeshGenerator::vertex>::size_type i = 0; i < vertices.size(); ++i)
+ {
+ SpriteVertex v;
+ v.pos = Vector3f((vertices[i].x-oxy.x)*scale, (vertices[i].y-oxy.y)*scale, 0.0f);
+ v.uv = Vector2f((vertices[i].x+rectInaccurate.x)*scale_x, (vertices[i].y+rectInaccurate.y)*scale_y);
+ outVertices->push_back(v);
+ }
+ }
+ }
+ else
+ {
+ Assert(outIndices == NULL);
+ }
+
+ // Set mesh rect
+ if (meshRect)
+ {
+ if (!smg.FindBounds(*meshRect))
+ *meshRect = Rectf(rectX, rectY, rectWidth, rectHeight);
+ }
+
+ // Clean up
+ UNITY_FREE(kMemDefault, imageData);
+}
+
+void GetAABBVerticesForSprite (const AABB& aabb, Vector3f* outVertices)
+{
+ outVertices[0] = aabb.m_Center + Vector3f (-aabb.m_Extent.x, -aabb.m_Extent.y, 0.0f);
+ outVertices[1] = aabb.m_Center + Vector3f (+aabb.m_Extent.x, -aabb.m_Extent.y, 0.0f);
+ outVertices[2] = aabb.m_Center + Vector3f (-aabb.m_Extent.x, +aabb.m_Extent.y, 0.0f);
+ outVertices[3] = aabb.m_Center + Vector3f (+aabb.m_Extent.x, +aabb.m_Extent.y, 0.0f);
+}
+
+#endif //ENABLE_SPRITES
diff --git a/Runtime/Graphics/SpriteUtility.h b/Runtime/Graphics/SpriteUtility.h
new file mode 100644
index 0000000..aff8491
--- /dev/null
+++ b/Runtime/Graphics/SpriteUtility.h
@@ -0,0 +1,22 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_SPRITES
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Graphics/SpriteFrame.h"
+
+enum PathSimplifyMode
+{
+ kPathReduce=0,
+ kPathEmbed
+};
+
+void GenerateSpriteOutline(PPtr<Texture2D> texture, float pixelsToUnits, const Rectf& rect, const Vector2f& rectOffset, float detail, unsigned char alphaTolerance, bool holeDetection, unsigned int extrude, int simplifyMode, std::vector<dynamic_array<Vector2f> >* outLine = NULL, std::vector<SpriteVertex>* outVertices = NULL, std::vector<UInt16>* outIndices = NULL, Rectf* meshRect = NULL);
+
+// Returns the front face of the AABB.
+// Sprite is on local Z = 0, however the AABB has volume. Flatten it on Z and return 4 vertices only.
+void GetAABBVerticesForSprite (const AABB& aabb, Vector3f* outVertices);
+
+bool GetSpriteMeshRectPixelBounds (const Texture2D& texture, const Rectf& rectInaccurate, int& rectX, int& rectY, int& rectRight, int& rectBottom);
+
+#endif //ENABLE_SPRITES
diff --git a/Runtime/Graphics/SubstanceArchive.cpp b/Runtime/Graphics/SubstanceArchive.cpp
new file mode 100644
index 0000000..33bc5a7
--- /dev/null
+++ b/Runtime/Graphics/SubstanceArchive.cpp
@@ -0,0 +1,121 @@
+#include "UnityPrefix.h"
+#include "SubstanceArchive.h"
+#include "SubstanceSystem.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/AssetPipeline/SubstanceImporter.h"
+#include "Editor/Src/AssetPipeline/SubstanceCache.h"
+#include "Editor/Src/EditorUserBuildSettings.h"
+#endif
+
+using namespace std;
+
+IMPLEMENT_CLASS_HAS_POSTINIT( SubstanceArchive )
+
+IMPLEMENT_OBJECT_SERIALIZE( SubstanceArchive )
+
+SubstanceArchive::SubstanceArchive( MemLabelId label, ObjectCreationMode mode )
+ : Super( label, mode )
+{
+#if ENABLE_SUBSTANCE
+ m_isPushed = false;
+ m_generatedGraphs.clear();
+ m_linkedBinaryData.clear();
+#endif
+}
+
+SubstanceArchive::~SubstanceArchive()
+{
+#if ENABLE_SUBSTANCE
+ GetSubstanceSystem().NotifyPackageDestruction(this);
+ for (std::map< UnityStr, UInt8* >::iterator i=m_linkedBinaryData.begin() ; i != m_linkedBinaryData.end() ; ++i)
+ {
+ UNITY_FREE(kMemSubstance, i->second);
+ }
+ m_linkedBinaryData.clear();
+#endif
+}
+
+void SubstanceArchive::AwakeFromLoad( AwakeFromLoadMode awakeMode )
+{
+ Super::AwakeFromLoad( awakeMode );
+}
+
+UInt8* SubstanceArchive::GetBufferData ()
+{
+ return &m_PackageData[0];
+}
+
+unsigned SubstanceArchive::GetBufferSize ()
+{
+ return m_PackageData.size();
+}
+
+#if ENABLE_SUBSTANCE
+bool SubstanceArchive::SaveLinkedBinaryData( const UnityStr& prototypeName, const UInt8* data, const int size)
+{
+ if (IsCloneDataAvailable(prototypeName))
+ {
+ WarningStringMsg("Trying to save linked substance data to a package that already has it");
+ return false;
+ }
+
+ UInt8* linkedBinaryData = (UInt8*) UNITY_MALLOC_ALIGNED_NULL(kMemSubstance, size, 32);
+ if (!linkedBinaryData)
+ {
+ WarningStringMsg("Could not allocate memory for a Substance package linked data");
+ return false;
+ }
+
+ memcpy( (void *) linkedBinaryData, (const void *) data, size);
+ m_linkedBinaryData[prototypeName] = linkedBinaryData;
+ return true;
+}
+
+UInt8* SubstanceArchive::GetLinkedBinaryData( const UnityStr& prototypeName ) const
+{
+ return (m_linkedBinaryData.find(prototypeName)->second);
+}
+
+bool SubstanceArchive::IsCloneDataAvailable( const UnityStr& prototypeName ) const
+{
+ return (m_linkedBinaryData.count(prototypeName) == 1);
+}
+#endif
+
+#if UNITY_EDITOR
+void SubstanceArchive::Init( const UInt8* _pPackage, unsigned int _PackageLength )
+{
+ // Copy the package content
+ m_PackageData.assign( _pPackage, _pPackage + _PackageLength );
+
+ AwakeFromLoad(kDefaultAwakeFromLoad);
+}
+#endif
+
+template<class T>
+void SubstanceArchive::Transfer( T& transfer )
+{
+ Super::Transfer( transfer );
+
+ transfer.Transfer( m_PackageData, "m_PackageData" );
+ transfer.Align();
+}
+
+void SubstanceArchive::PostInitializeClass ()
+{
+#if ENABLE_SUBSTANCE
+ g_SubstanceSystem = new SubstanceSystem();
+#endif
+}
+
+void SubstanceArchive::CleanupClass ()
+{
+#if ENABLE_SUBSTANCE
+ delete g_SubstanceSystem;
+#endif
+}
+
+#if ENABLE_SUBSTANCE
+SubstanceSystem* SubstanceArchive::g_SubstanceSystem = NULL;
+#endif
diff --git a/Runtime/Graphics/SubstanceArchive.h b/Runtime/Graphics/SubstanceArchive.h
new file mode 100644
index 0000000..ac49912
--- /dev/null
+++ b/Runtime/Graphics/SubstanceArchive.h
@@ -0,0 +1,75 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "External/Allegorithmic/builds/Engines/include/substance/handle.h"
+
+class ColorRGBAf;
+class ColorRGBA32;
+class ProceduralTexture;
+class SubstanceSystem;
+
+/* A SubstanceArchive is a resource representation for an imported Substance package.
+ * It hosts the persisted binary version of the package that is either imported
+ * or reloaded from disk.
+ */
+
+
+class SubstanceArchive : public NamedObject
+{
+protected: // FIELDS
+
+ // The SBSAR package as a binary
+ dynamic_array<UInt8> m_PackageData;
+
+public: // METHODS
+
+ REGISTER_DERIVED_CLASS( SubstanceArchive, NamedObject )
+ DECLARE_OBJECT_SERIALIZE( SubstanceArchive )
+
+ SubstanceArchive( MemLabelId label, ObjectCreationMode mode );
+
+ // Reloads the package from disk
+ void AwakeFromLoad( AwakeFromLoadMode awakeMode );
+
+#if UNITY_EDITOR
+ // Creates the package from a binary SBSBIN file and the XML description file (one time call only when importing the SBSBIN file)
+ void Init( const UInt8* _pPackage, unsigned int _PackageLength );
+#endif
+
+ UInt8* GetBufferData ();
+ unsigned GetBufferSize ();
+
+#if ENABLE_SUBSTANCE
+public:
+ // Flag indicating whether the package's SBSASM has already pushed for linking
+ bool m_isPushed;
+ // Set of graph names that have already been generated from this package
+ std::set<UnityStr> m_generatedGraphs;
+
+ // Cache of single-package no-const-inputs SBSBIN data used for cloning
+public:
+ bool SaveLinkedBinaryData (const UnityStr& prototypeName, const UInt8* data, const int size);
+ UInt8* GetLinkedBinaryData (const UnityStr& prototypeName) const;
+ bool IsCloneDataAvailable (const UnityStr& prototypeName) const;
+private:
+ std::map< UnityStr, UInt8* > m_linkedBinaryData;
+#endif
+
+public:
+
+ // Substance system initialization
+ static void InitializeClass (){}
+ static void PostInitializeClass ();
+ static void CleanupClass ();
+
+#if ENABLE_SUBSTANCE
+ static SubstanceSystem& GetSubstanceSystem() { return *g_SubstanceSystem; }
+ static SubstanceSystem* GetSubstanceSystemPtr() { return g_SubstanceSystem; }
+private:
+ static SubstanceSystem* g_SubstanceSystem;
+#endif
+};
diff --git a/Runtime/Graphics/SubstanceInput.h b/Runtime/Graphics/SubstanceInput.h
new file mode 100644
index 0000000..bb0a661
--- /dev/null
+++ b/Runtime/Graphics/SubstanceInput.h
@@ -0,0 +1,243 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "External/Allegorithmic/builds/Engines/include/substance/inputdesc.h"
+#include "Texture2D.h"
+
+enum ProceduralPropertyType
+{
+ ProceduralPropertyType_Boolean = 0,
+ ProceduralPropertyType_Float,
+ ProceduralPropertyType_Vector2,
+ ProceduralPropertyType_Vector3,
+ ProceduralPropertyType_Vector4,
+ ProceduralPropertyType_Color3,
+ ProceduralPropertyType_Color4,
+ ProceduralPropertyType_Enum,
+ ProceduralPropertyType_Texture
+};
+
+inline bool IsSubstanceAnyFloatType (SubstanceInputType type) { return type >= Substance_IType_Float && type <= Substance_IType_Float4; }
+inline bool IsSubstanceAnyIntType (SubstanceInputType type) { return (type == Substance_IType_Integer) || (type == Substance_IType_Integer2) || (type == Substance_IType_Integer3) || (type == Substance_IType_Integer4); }
+inline int GetRequiredInputComponentCount (SubstanceInputType type)
+{
+ switch (type)
+ {
+ case Substance_IType_Float:
+ case Substance_IType_Integer:
+ return 1;
+ case Substance_IType_Float2:
+ case Substance_IType_Integer2:
+ return 2;
+ case Substance_IType_Float3:
+ case Substance_IType_Integer3:
+ return 3;
+ case Substance_IType_Float4:
+ case Substance_IType_Integer4:
+ return 4;
+ default:
+ AssertString("Not supported");
+ return -1;
+ }
+}
+
+struct SubstanceEnumItem
+{
+ DECLARE_SERIALIZE(SubstanceEnumItem)
+
+ int value;
+ UnityStr text;
+};
+
+struct SubstanceValue
+{
+ DECLARE_SERIALIZE(SubstanceValue)
+
+ float scalar[4];
+ PPtr<Texture2D> texture;
+
+ SubstanceValue() { memset(scalar, 0, sizeof(float)*4); }
+};
+
+struct SubstanceInput
+{
+ DECLARE_SERIALIZE(SubstanceInput)
+
+ // type & names
+ UnityStr name;
+ UnityStr label;
+ UnityStr group;
+ ProceduralPropertyType type;
+ SubstanceValue value;
+ SubstanceInputType internalType;
+ unsigned int internalIndex;
+ unsigned int internalIdentifier;
+ unsigned int shuffledIdentifier;
+
+ // Ranges and step
+ float minimum;
+ float maximum;
+ float step;
+
+ // Enum values
+ std::vector<SubstanceEnumItem> enumValues;
+ std::vector<std::string> GetEnumOptions() const
+ {
+ std::vector<std::string> enumOptions;
+ std::vector<SubstanceEnumItem>::const_iterator it;
+ for (it=enumValues.begin();it!=enumValues.end();++it)
+ {
+ enumOptions.push_back(it->text);
+ }
+ return enumOptions;
+ }
+
+ // Flags
+ enum Flag
+ {
+ Flag_SkipHint = 1<<0, // input is in head of the graph so the cache is not usefull
+ Flag_Modified = 1<<1, // input has been modified
+ Flag_Cached = 1<<2, // input is cached to speed up modifications
+ Flag_Awake = 1<<3, // this flag is set when AwakeFromLoad comes, this to rebuild all without having hints everywhere
+ Flag_Clamp = 1<<4 // needs to clamp value when set
+ };
+ unsigned int flags;
+ bool IsFlagEnabled(const Flag& flag) const { return flags & (unsigned int)flag; }
+ void EnableFlag(const Flag& flag, bool enabled=true) { if (enabled) flags |= (unsigned int)flag; else flags &= ~(unsigned int)flag; }
+
+ // Altered texture links
+ std::set<unsigned int> alteredTexturesUID;
+
+ SubstanceInput ()
+ {
+ type = ProceduralPropertyType_Float;
+ flags = SubstanceInput::Flag_Awake;
+ value.scalar[0] = value.scalar[1] = value.scalar[2] = value.scalar[3] = 0.0F;
+ internalType = Substance_IType_Float;
+ internalIndex = 0;
+ internalIdentifier = 0;
+
+ minimum = -std::numeric_limits<float>::max();
+ maximum = std::numeric_limits<float>::max();
+ }
+};
+
+inline void ClampSubstanceInputValues (const SubstanceInput& input, SubstanceValue& value)
+{
+ // validate combo value
+ if (input.type==ProceduralPropertyType_Enum && input.enumValues.size()>0)
+ {
+ std::vector<SubstanceEnumItem>::const_iterator it;
+ for (it=input.enumValues.begin();it!=input.enumValues.end();++it)
+ {
+ if ((int)value.scalar[0]==it->value)
+ return;
+ }
+ value.scalar[0] = (float)input.enumValues[0].value;
+ return;
+ }
+
+ // clamp scalar values
+ if (input.IsFlagEnabled(SubstanceInput::Flag_Clamp))
+ {
+ int count = GetRequiredInputComponentCount(input.internalType);
+
+ if (IsSubstanceAnyIntType(input.internalType))
+ {
+ for (int index=0;index<count;++index)
+ value.scalar[index] = (float)clamp<int>((int)(value.scalar[index]+0.5f), (int)input.minimum, (int)input.maximum);
+ }
+ else
+ {
+ for (int index=0;index<count;++index)
+ value.scalar[index] = clamp<float>(value.scalar[index], input.minimum, input.maximum);
+ }
+ } // step integers
+ else if (IsSubstanceAnyIntType(input.internalType))
+ {
+ int count = GetRequiredInputComponentCount(input.internalType);
+ for (int index=0;index<count;++index)
+ value.scalar[index] = (float)((int)(value.scalar[index]+0.5f));
+ }
+}
+
+inline bool AreSubstanceInputValuesEqual(SubstanceInputType type, SubstanceValue& first, SubstanceValue& second)
+{
+ if (IsSubstanceAnyFloatType(type))
+ {
+ return memcmp(first.scalar, second.scalar,
+ sizeof(float)*GetRequiredInputComponentCount(type))==0;
+ }
+ else if (IsSubstanceAnyIntType(type))
+ {
+ int count = GetRequiredInputComponentCount(type);
+
+ for (int index=0;index<count;++index)
+ {
+ if ((int)first.scalar[index]!=(int)second.scalar[index])
+ return false;
+ }
+ return true;
+ }
+ else if (type==Substance_IType_Image)
+ {
+ return first.texture==second.texture;
+ }
+ return false;
+}
+
+typedef std::vector<SubstanceInput> SubstanceInputs;
+
+template<class T>
+void SubstanceEnumItem::Transfer(T& transfer)
+{
+ TRANSFER(value);
+ TRANSFER(text);
+}
+
+template<class T>
+void SubstanceValue::Transfer(T& transfer)
+{
+ TRANSFER(scalar[0]);
+ TRANSFER(scalar[1]);
+ TRANSFER(scalar[2]);
+ TRANSFER(scalar[3]);
+ TRANSFER(texture);
+}
+
+template<class T>
+void SubstanceInput::Transfer(T& transfer)
+{
+ // type & names
+ TRANSFER(name);
+ TRANSFER(label);
+ TRANSFER(group);
+ transfer.Transfer(reinterpret_cast<int&> (type), "type");
+ TRANSFER(value);
+ transfer.Transfer(reinterpret_cast<int&> (internalType), "internalType");
+ TRANSFER(internalIndex);
+ TRANSFER(internalIdentifier);
+
+ // Range and step
+ TRANSFER(minimum);
+ TRANSFER(maximum);
+ TRANSFER(step);
+
+ // Flags
+ TRANSFER(flags);
+
+ // Altered texture links
+ TRANSFER(alteredTexturesUID);
+
+ // Enum
+ TRANSFER(enumValues);
+
+ // Update status
+ if (transfer.IsReading())
+ {
+ EnableFlag(Flag_Awake, true);
+ EnableFlag(Flag_Cached, false);
+ }
+}
diff --git a/Runtime/Graphics/SubstanceSystem.cpp b/Runtime/Graphics/SubstanceSystem.cpp
new file mode 100644
index 0000000..2231bbf
--- /dev/null
+++ b/Runtime/Graphics/SubstanceSystem.cpp
@@ -0,0 +1,876 @@
+#include "UnityPrefix.h"
+#include "SubstanceSystem.h"
+
+#if ENABLE_SUBSTANCE
+#include "ProceduralMaterial.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Serialize/LoadProgress.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/BaseClasses/GameManager.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/AssetPipeline/SubstanceImporter.h"
+#include "Editor/Src/Application.h"
+#endif
+
+using namespace std;
+
+int SubstanceSystem::s_maximumSubstancePerFrame = 40;
+
+SubstanceSystem::SubstanceSystem() :
+ processedSubstance(NULL),
+ outputCallback(NULL),
+ lastProcessedSubstance(NULL),
+ queryClearCache(NULL),
+ isIntegrating(false),
+ integrationTimeStamp(0)
+{
+ int Error = substanceContextInit( &m_Context, &m_Device );
+ Assert( !Error && "Failed to initialize blend platform context!" ); // We use the BLEND platform
+
+ // Setup memory callback
+ Error = substanceContextSetCallback( m_Context, Substance_Callback_Malloc, (void*) OnMalloc );
+ Assert( !Error && "Failed to setup malloc callback!" );
+ Error = substanceContextSetCallback( m_Context, Substance_Callback_Free, (void*) OnFree );
+ Assert( !Error && "Failed to setup free callback!" );
+
+ // Setup the callback for image inputs
+ Error = substanceContextSetCallback( m_Context, Substance_Callback_InputImageLock, (void*) OnInputImageLock );
+ Assert( !Error && "Failed to setup image lock callback!" );
+ Error = substanceContextSetCallback( m_Context, Substance_Callback_InputImageUnlock, (void*) OnInputImageUnlock );
+ Assert( !Error && "Failed to setup image unlock callback!" );
+ Error = substanceContextSetCallback( m_Context, Substance_Callback_OutOfMemory, (void*) OnOutOfMemory );
+ Assert( !Error && "Failed to setup out_of_memory callback!" );
+
+ SetOutputCallback();
+ processingThread.SetName ("UnitySubstanceThread");
+ processingThread.Run(ThreadMain, this);
+ SetProcessorUsage(ProceduralProcessorUsage_One);
+}
+
+SubstanceSystem::~SubstanceSystem()
+{
+ processingThread.SignalQuit();
+ threadSemaphore.Signal();
+ processingThread.WaitForExit();
+
+ if ( m_Context == NULL )
+ return; // Already released !
+
+ // Release the context
+ substanceContextRelease( m_Context );
+ m_Context = NULL;
+}
+
+void SubstanceSystem::SetOutputCallback(void* callback)
+{
+ outputCallback = callback==NULL?(void*)OnOutputCompleted:callback;
+ int err = substanceContextSetCallback( m_Context, Substance_Callback_OutputCompleted, outputCallback );
+ AssertMsg(err == 0, "Failed to sertup render callback!");
+}
+
+bool SubstanceSystem::AreQueuesEmpty()
+{
+ return integrationQueue.size()==0
+ && waitingSubstanceQueue.size()==0
+ && processingSubstanceQueue.size()==0
+ && processedSubstance==NULL
+ && updatingSubstanceQueue.size()==0;
+}
+
+bool SubstanceSystem::AreIntegratingQueuesEmpty()
+{
+ {
+ Mutex::AutoLock integrationLocker(integrationMutex);
+ if (integrationQueue.size()>0)
+ for (std::vector<ProceduralMaterial*>::iterator it=integrationQueue.begin() ; it!=integrationQueue.end() ; ++it)
+ if ((*it)->integrationTimeStamp==integrationTimeStamp)
+ return false;
+ }
+
+ {
+ Mutex::AutoLock waitingLocker(waitingMutex);
+ if (waitingSubstanceQueue.size()>0)
+ for (std::list<Substance*>::iterator it=waitingSubstanceQueue.begin() ; it!=waitingSubstanceQueue.end() ; ++it)
+ if ((*it)->material->integrationTimeStamp==integrationTimeStamp)
+ return false;
+ }
+
+ {
+ Mutex::AutoLock processingLocker(processingMutex);
+ if (processingSubstanceQueue.size()>0)
+ for (std::list<Substance*>::iterator it=processingSubstanceQueue.begin() ; it!=processingSubstanceQueue.end() ; ++it)
+ if ((*it)->material->integrationTimeStamp==integrationTimeStamp)
+ return false;
+ }
+
+ {
+ Mutex::AutoLock processedLocker(processedMutex);
+ if (processedSubstance!=NULL && processedSubstance->material->integrationTimeStamp==integrationTimeStamp)
+ return false;
+ }
+
+ {
+ Mutex::AutoLock updatingLocker(updatingMutex);
+ if (updatingSubstanceQueue.size()>0)
+ for (std::list<Substance*>::iterator it=updatingSubstanceQueue.begin() ; it!=updatingSubstanceQueue.end() ; ++it)
+ if ((*it)->material->integrationTimeStamp==integrationTimeStamp)
+ return false;
+ }
+
+ return true;
+}
+
+bool SubstanceSystem::IsSubstanceProcessing(const ProceduralMaterial* substance)
+{
+ // Search into integration queue
+ {
+ Mutex::AutoLock integrationLocker(integrationMutex);
+ std::vector<ProceduralMaterial*>::iterator it = std::find(integrationQueue.begin(), integrationQueue.end(), substance);
+ if (it!=integrationQueue.end())
+ return true;
+ }
+
+ // Search into processing queue
+ {
+ Mutex::AutoLock processingLocker(processingMutex);
+ std::list<Substance*>::iterator it = std::find_if(processingSubstanceQueue.begin(), processingSubstanceQueue.end(), QueueFinder(substance));
+ if (it!=processingSubstanceQueue.end())
+ return true;
+ }
+
+ // Is currently processing ?
+ {
+ Mutex::AutoLock processedLocker(processedMutex);
+ if (processedSubstance!=NULL && processedSubstance->material==substance)
+ return true;
+ }
+
+ // Search into updating queue
+ {
+ Mutex::AutoLock updatingLocker(updatingMutex);
+ std::list<Substance*>::iterator it = std::find_if(updatingSubstanceQueue.begin(), updatingSubstanceQueue.end(), QueueFinder(substance));
+ if (it!=updatingSubstanceQueue.end())
+ return true;
+ }
+
+ return false;
+}
+
+void SubstanceSystem::WaitFinished(LoadProgress* progress)
+{
+ isIntegrating = true;
+ int integrate_count = integrationQueue.size();
+ if (progress!=NULL)
+ {
+ progress->totalItems += integrate_count;
+ }
+
+ while (!AreIntegratingQueuesEmpty())
+ {
+ if (Thread::CurrentThreadIsMainThread())
+ Update();
+
+ if (progress!=NULL)
+ {
+ int new_count = integrationQueue.size();
+ progress->ItemProcessed(integrate_count-new_count);
+ integrate_count = integrationQueue.size();
+ }
+
+ Thread::Sleep(0.001);
+ }
+ isIntegrating = false;
+}
+
+void SubstanceSystem::WaitFinished(const ProceduralMaterial* substance)
+{
+ isIntegrating = true;
+ while (IsSubstanceProcessing(substance))
+ {
+ if (Thread::CurrentThreadIsMainThread())
+ Update();
+
+ Thread::Sleep(0.001);
+ }
+ isIntegrating = false;
+}
+
+void SubstanceSystem::Update(bool isQuitSignaled)
+{
+ if (!AreQueuesEmpty())
+ threadSemaphore.Signal();
+
+ // Clear waiting queue
+ {
+ Mutex::AutoLock waitingLocker(waitingMutex);
+ while (waitingSubstanceQueue.size()>0)
+ {
+ delete waitingSubstanceQueue.back();
+ waitingSubstanceQueue.pop_back();
+ }
+ }
+
+ // Update animated substances
+ if (!isIntegrating && !isQuitSignaled)
+ {
+ Mutex::AutoLock deleteLocker(deletePingMutex);
+ for (std::vector<ProceduralMaterial*>::iterator it=animatedSubstanceArray.begin();it!=animatedSubstanceArray.end();++it)
+ {
+ ProceduralMaterial *substance = *it;
+ substance->UpdateAnimation(GetTimeManager().GetCurTime());
+ }
+ }
+
+ // Upload updated substances
+ int uploadedCount = s_maximumSubstancePerFrame;
+ Substance* updatedSubstance;
+ while (uploadedCount-- && updatingSubstanceQueue.size()>0)
+ {
+ // Pick one if available
+ {
+ Mutex::AutoLock updatingLocker(updatingMutex);
+ std::list<Substance*>::iterator it = updatingSubstanceQueue.begin();
+ if (it==updatingSubstanceQueue.end())
+ break;
+ updatedSubstance = *it;
+ updatingSubstanceQueue.erase(it);
+ }
+
+ // Upload textures
+ {
+ //WarningStringMsg("SUBSTANCE: Uploading %d textures from %s", updatedSubstance->updatedTextures.size(), updatedSubstance->material->GetName());
+ for (std::map< ProceduralTexture*, SubstanceTexture >::iterator it=updatedSubstance->updatedTextures.begin();it!=updatedSubstance->updatedTextures.end();++it)
+ {
+ it->first->UploadSubstanceTexture(it->second);
+#if UNITY_EDITOR
+ SubstanceImporter::OnTextureModified(*updatedSubstance, *it->first, it->second);
+ //WarningStringMsg("SUBSTANCE: Uploading %s from material %s", it->first->GetName(), it->first->GetSubstanceMaterial()->GetName());
+#endif
+ }
+
+#if UNITY_EDITOR
+ SubstanceImporter::OnSubstanceModified(*updatedSubstance);
+#endif
+ for (std::map< ProceduralTexture*, SubstanceTexture >::iterator it=updatedSubstance->updatedTextures.begin();it!=updatedSubstance->updatedTextures.end();++it)
+ {
+ OnFree(it->second.buffer);
+ }
+ delete updatedSubstance;
+ }
+ }
+
+ if (isQuitSignaled)
+ {
+ // Clear processing queue
+ Mutex::AutoLock processingLocker(processingMutex);
+ for (std::list<Substance*>::iterator it=processingSubstanceQueue.begin();it!=processingSubstanceQueue.end();++it)
+ delete *it;
+ processingSubstanceQueue.clear();
+ }
+}
+
+void SubstanceSystem::UpdateThreaded()
+{
+ // Integrate loaded substances, if any
+ Integrate();
+
+ // Retrieve next substance to process
+ {
+ Mutex::AutoLock processingLocker(processingMutex);
+ std::list<Substance*>::iterator it = processingSubstanceQueue.begin();
+ if (it==processingSubstanceQueue.end())
+ return;
+
+ {
+ Mutex::AutoLock processedLocker(processedMutex);
+ processedSubstance = *it;
+ }
+ processingSubstanceQueue.erase(it);
+ }
+
+ // Set input values
+ for (std::map<std::string, SubstanceValue>::iterator i=processedSubstance->inputValues.begin();i!=processedSubstance->inputValues.end();++i)
+ {
+ processedSubstance->material->Callback_SetSubstanceInput(i->first, i->second);
+ }
+
+ // Clear cache if required
+ if (processedSubstance->material==queryClearCache)
+ {
+ ApplyMemoryBudget(processedSubstance->material, false);
+ queryClearCache = NULL;
+ }
+
+ // Generate textures
+ processedTextures.clear();
+ if (!processedSubstance->material->ProcessTexturesThreaded(processedSubstance->updatedTextures))
+ {
+ Mutex::AutoLock processedLocker(processedMutex);
+ delete processedSubstance;
+ processedSubstance = NULL;
+ return;
+ }
+
+ // Push substance into the updating queue
+ {
+ Mutex::AutoLock updatingLocker(updatingMutex);
+ updatingSubstanceQueue.push_back(processedSubstance);
+ }
+ // Clear
+ {
+ Mutex::AutoLock processedLocker(processedMutex);
+ processedSubstance = NULL;
+ }
+}
+
+void SubstanceSystem::QueueInput( ProceduralMaterial* material, std::string inputName, SubstanceValue& inputValue )
+{
+ // Skip if the material is broken
+ if (material->IsFlagEnabled(ProceduralMaterial::Flag_Broken))
+ return;
+
+ material->SetDirty();
+
+#if UNITY_EDITOR
+ SubstanceImporter::OnInputModified(*material, inputName, inputValue);
+#endif
+
+ // Directly apply value if not loaded
+ if (material->GetSubstanceHandle()==NULL)
+ {
+ material->Callback_SetSubstanceInput(inputName, inputValue);
+ return;
+ }
+
+ // Search if the substance is already in the process queue
+ {
+ Mutex::AutoLock locker(processingMutex);
+ std::list<Substance*>::iterator it = std::find_if(processingSubstanceQueue.begin(), processingSubstanceQueue.end(), QueueFinder(material));
+ if (it!=processingSubstanceQueue.end())
+ {
+ (*it)->inputValues[inputName] = inputValue;
+ return;
+ }
+ }
+
+ // Search if is already in the waiting queue
+ {
+ Mutex::AutoLock waitingLocker(waitingMutex);
+ std::list<Substance*>::iterator it = std::find_if(waitingSubstanceQueue.begin(), waitingSubstanceQueue.end(), QueueFinder(material));
+ if (it!=waitingSubstanceQueue.end())
+ {
+ (*it)->inputValues[inputName] = inputValue;
+ return;
+ }
+ }
+
+ // Search if is already in the integration queue
+ {
+ Mutex::AutoLock integrationLocker(integrationMutex);
+ std::vector<ProceduralMaterial*>::iterator i = std::find(integrationQueue.begin(), integrationQueue.end(), material);
+ if (i!=integrationQueue.end())
+ {
+ material->Callback_SetSubstanceInput(inputName, inputValue);
+ return;
+ }
+ }
+
+ // Else add it in the waiting queue
+ Substance* substance = new Substance();
+ substance->material = material;
+ substance->inputValues[inputName] = inputValue;
+ {
+ Mutex::AutoLock waitingLocker(waitingMutex);
+ waitingSubstanceQueue.push_back(substance);
+ }
+}
+
+void SubstanceSystem::QueueSubstance(ProceduralMaterial* material)
+{
+ // Skip if the material is broken
+ if (material->IsFlagEnabled(ProceduralMaterial::Flag_Broken))
+ return;
+
+ if (material->GetSubstanceHandle()==NULL)
+ {
+ if (IsWorldPlaying() && material->GetLoadingBehavior()==ProceduralLoadingBehavior_None)
+ material->EnableFlag(ProceduralMaterial::Flag_ForceGenerate);
+ QueueLoading(material);
+ return;
+ }
+
+ // If it exists in the processing queue, input values are already pushed -> leave it as is
+ if (processingSubstanceQueue.size()>0)
+ {
+ Mutex::AutoLock locker(processingMutex);
+ std::list<Substance*>::iterator it = std::find_if(processingSubstanceQueue.begin(), processingSubstanceQueue.end(), QueueFinder(material));
+ if (it!=processingSubstanceQueue.end())
+ return;
+ }
+
+ // If it exists in the waiting queue, push it into the processing queue
+ if (waitingSubstanceQueue.size()>0)
+ {
+ Mutex::AutoLock waitingLocker(waitingMutex);
+ std::list<Substance*>::iterator it = std::find_if(waitingSubstanceQueue.begin(), waitingSubstanceQueue.end(), QueueFinder(material));
+ if (it!=waitingSubstanceQueue.end())
+ {
+ Mutex::AutoLock locker(processingMutex);
+ processingSubstanceQueue.push_back(*it);
+ waitingSubstanceQueue.erase(it);
+ return;
+ }
+ }
+
+ // Push it into the processing queue
+ Substance* substance = new Substance();
+ substance->material = material;
+ Mutex::AutoLock locker(processingMutex);
+ processingSubstanceQueue.push_back(substance);
+}
+
+void SubstanceSystem::QueueLoading(ProceduralMaterial* material)
+{
+ // Skip if the material is broken
+ if (material->IsFlagEnabled(ProceduralMaterial::Flag_Broken))
+ return;
+
+ // Already loaded ?
+ if (material->GetSubstanceHandle()!=NULL)
+ return;
+
+ // If it exists in the waiting queue, directly assign input values
+ {
+ Mutex::AutoLock waitingLocker(waitingMutex);
+ std::list<Substance*>::iterator it = std::find_if(waitingSubstanceQueue.begin(), waitingSubstanceQueue.end(), QueueFinder(material));
+ if (it!=waitingSubstanceQueue.end())
+ {
+#if UNITY_EDITOR
+ material->EnableFlag(ProceduralMaterial::Flag_Awake, false);
+#endif
+ for (std::map<std::string, SubstanceValue>::iterator i=(*it)->inputValues.begin() ; i!=(*it)->inputValues.end() ; ++i)
+ material->Callback_SetSubstanceInput(i->first, i->second);
+ delete *it;
+ waitingSubstanceQueue.erase(it);
+ }
+ }
+
+ // Push it to the integrationQueue if it isn't
+ Mutex::AutoLock integrationLocker(integrationMutex);
+ std::vector<ProceduralMaterial*>::iterator i = std::find(integrationQueue.begin(), integrationQueue.end(), material);
+ if (i==integrationQueue.end())
+ integrationQueue.push_back(material);
+}
+
+void SubstanceSystem::QueryClearCache(ProceduralMaterial* material)
+{
+ queryClearCache = material;
+ QueueSubstance(material);
+}
+
+void* SubstanceSystem::ThreadMain(void*data)
+{
+ SubstanceSystem* system = static_cast<SubstanceSystem*>(data);
+ while (!system->processingThread.IsQuitSignaled())
+ {
+ system->threadSemaphore.WaitForSignal();
+ system->threadSemaphore.Reset();
+
+ for (int i=0 ; i<s_maximumSubstancePerFrame ; ++i)
+ system->UpdateThreaded();
+ }
+ return NULL;
+}
+
+#define SUBSTANCE_TRACE_MEM_CALLBACKS 0
+
+void* SUBSTANCE_CALLBACK SubstanceSystem::OnMalloc(size_t bytesCount, size_t alignment)
+{
+ void *ptr = UNITY_MALLOC_ALIGNED_NULL(kMemSubstance, bytesCount, alignment);
+ if (!ptr)
+ {
+ ErrorString("Could not allocate memory in OnMalloc (SubstanceSystem)");
+ }
+#if SUBSTANCE_TRACE_MEM_CALLBACKS
+ Substance *substance = GetSubstanceSystem().processedSubstance;
+ WarningStringMsg("\n%08X %d Alloc %s", ptr, bytesCount, (substance != NULL ? substance->material->GetName() : "No Substance"));
+#endif
+ return ptr;
+}
+
+void SUBSTANCE_CALLBACK SubstanceSystem::OnFree(void* bufferPtr)
+{
+#if SUBSTANCE_TRACE_MEM_CALLBACKS
+ Substance *substance = GetSubstanceSystem().processedSubstance;
+ WarningStringMsg("\n%08X Free %s", bufferPtr, (substance != NULL ? substance->material->GetName() : "No Substance"));
+#endif
+ UNITY_FREE(kMemSubstance, bufferPtr);
+}
+
+void SUBSTANCE_CALLBACK SubstanceSystem::OnOutputCompleted( SubstanceHandle* _pHandle, unsigned int _OutputIndex, size_t _JobUserData )
+{
+ SubstanceOutputDesc outputDesc;
+ if (substanceHandleGetOutputDesc(_pHandle, _OutputIndex, &outputDesc)==0)
+ {
+ std::map<unsigned int, ProceduralTexture*>::iterator it;
+ it = GetSubstanceSystem ().processedTextures.find(outputDesc.outputId);
+ if (it!=GetSubstanceSystem ().processedTextures.end())
+ {
+ ProceduralTexture* texture = it->second;
+ Substance* substance = GetSubstanceSystem().processedSubstance;
+ Assert( substance!=NULL && "Failed to update substance!" );
+ //WarningStringMsg("SUBSTANCE: OnOutputCompleted received texture %s from substance %s", it->second->GetName(), substance->material->GetName());
+ if (substanceHandleGetOutputs(substance->material->GetSubstanceHandle(), Substance_OutOpt_TextureId, texture->GetSubstanceTextureUID(), 1, &substance->updatedTextures[texture])!=0)
+ {
+ ErrorStringObject("Failed to retrieve substance texture data", substance->material);
+ return;
+ }
+ }
+ }
+}
+
+void SUBSTANCE_CALLBACK SubstanceSystem::OnInputImageLock( SubstanceHandle* _pHandle, size_t _JobUserData, unsigned int _InputIndex, SubstanceTextureInput** _ppCurrentTextureInputDesc, const SubstanceTextureInput* _pPreferredTextureInputDesc )
+{
+ Substance* substance = GetSubstanceSystem().processedSubstance;
+ Assert( substance!=NULL && "Failed to update substance input texture!" );
+ substance->material->ApplyTextureInput(_InputIndex, *_pPreferredTextureInputDesc);
+}
+
+void SUBSTANCE_CALLBACK SubstanceSystem::OnInputImageUnlock( SubstanceHandle* _pHandle, size_t _JobUserData, unsigned int _InputIndex, SubstanceTextureInput* _ppCurrentTextureInputDesc )
+{
+}
+
+void SUBSTANCE_CALLBACK SubstanceSystem::OnOutOfMemory(SubstanceHandle *handle, int memoryType)
+{
+ Substance* substance = GetSubstanceSystem().processedSubstance;
+ if (substance->material->GetProceduralMemoryWorkBudget()!=ProceduralCacheSize_NoLimit)
+ {
+ GetSubstanceSystem().ApplyMemoryBudget(substance->material, true, true);
+ }
+}
+
+void SubstanceSystem::NotifySubstanceCreation(ProceduralMaterial* substance)
+{
+ // Add it into the animated substance array if needed
+ if (substance->IsFlagEnabled(ProceduralMaterial::Flag_Animated))
+ {
+ if (std::find(animatedSubstanceArray.begin(), animatedSubstanceArray.end(), substance)==animatedSubstanceArray.end())
+ {
+ animatedSubstanceArray.push_back(substance);
+ }
+ }
+}
+
+void SubstanceSystem::NotifySubstanceDestruction(ProceduralMaterial* substance)
+{
+ // Remove it from animated array if in
+ {
+ if (substance->IsFlagEnabled(ProceduralMaterial::Flag_Animated))
+ {
+ Mutex::AutoLock deletePingLocker(deletePingMutex);
+ std::vector<ProceduralMaterial*>::iterator i(std::find(animatedSubstanceArray.begin(), animatedSubstanceArray.end(), substance));
+ if (i!=animatedSubstanceArray.end())
+ animatedSubstanceArray.erase(i);
+ }
+ }
+
+ // Remove it from integration array if in
+ {
+ Mutex::AutoLock deletePingLocker(deletePingMutex);
+ Mutex::AutoLock deleteIntegrationLocker(deleteIntegrationMutex);
+ Mutex::AutoLock integrationLocker(integrationMutex);
+ std::vector<ProceduralMaterial*>::iterator i = std::find(integrationQueue.begin(), integrationQueue.end(), substance);
+ if (i!=integrationQueue.end())
+ integrationQueue.erase(i);
+ }
+
+ // Remove it from processing array if in
+ {
+ Mutex::AutoLock processingLocker(processingMutex);
+ std::list<Substance*>::iterator it;
+ while((it=std::find_if(processingSubstanceQueue.begin(), processingSubstanceQueue.end(), QueueFinder(substance)))!=processingSubstanceQueue.end())
+ {
+ delete *it;
+ processingSubstanceQueue.erase(it);
+ }
+ }
+
+ // Wait if it's processing
+ while(1)
+ {
+ {
+ Mutex::AutoLock processedLocker(processedMutex);
+ if (processedSubstance==NULL || substance->GetSubstanceData()!=processedSubstance->material->GetSubstanceData())
+ break;
+ }
+ Thread::Sleep(0.001);
+ }
+
+ // Remove it from updating array if in
+ {
+ Mutex::AutoLock updatingLocker(updatingMutex);
+ std::list<Substance*>::iterator it = std::find_if(updatingSubstanceQueue.begin(), updatingSubstanceQueue.end(), QueueFinder(substance));
+ if (it!=updatingSubstanceQueue.end())
+ {
+ // Delete the output buffers from the Substance if they have not yet been pushed to the GPU
+ // This can happen when building for example, where very quick cycles of load/generate/discard are performed
+ for (std::map<ProceduralTexture*,SubstanceTexture>::iterator itTex = (*it)->updatedTextures.begin() ;
+ itTex != (*it)->updatedTextures.end() ; ++itTex)
+ {
+ OnFree(itTex->second.buffer);
+ }
+
+ delete *it;
+ updatingSubstanceQueue.erase(it);
+ }
+ }
+
+ // Clear last processed if needed
+ {
+ Mutex::AutoLock lastSubstanceLocker(lastProcessedSubstanceMutex);
+ if (substance==lastProcessedSubstance)
+ lastProcessedSubstance = NULL;
+ }
+}
+
+void SubstanceSystem::NotifyTextureDestruction(ProceduralTexture* texture)
+{
+ if (texture->GetSubstanceMaterial()!=NULL)
+ NotifySubstanceDestruction(texture->GetSubstanceMaterial());
+}
+
+void SubstanceSystem::NotifyPackageDestruction(SubstanceArchive* package)
+{
+ // Remove it from animated array if in
+ {
+ Mutex::AutoLock deletePingLocker(deletePingMutex);
+ std::vector<ProceduralMaterial*>::iterator it;
+ while((it=std::find_if(animatedSubstanceArray.begin(), animatedSubstanceArray.end(), ArrayPackageFinder(package)))!=animatedSubstanceArray.end())
+ {
+ animatedSubstanceArray.erase(it);
+ }
+ }
+
+ // Remove it from integration array if in
+ {
+ Mutex::AutoLock deletePingLocker(deletePingMutex);
+ Mutex::AutoLock deleteIntegrationLocker(deleteIntegrationMutex);
+ Mutex::AutoLock integrationLocker(integrationMutex);
+ std::vector<ProceduralMaterial*>::iterator i;
+ while((i=std::find_if(integrationQueue.begin(), integrationQueue.end(), ArrayPackageFinder(package)))!=integrationQueue.end())
+ integrationQueue.erase(i);
+ }
+
+ // Remove it from processing array if in
+ {
+ Mutex::AutoLock processingLocker(processingMutex);
+ std::list<Substance*>::iterator it;
+ while((it=std::find_if(processingSubstanceQueue.begin(), processingSubstanceQueue.end(), QueuePackageFinder(package)))!=processingSubstanceQueue.end())
+ {
+ delete *it;
+ processingSubstanceQueue.erase(it);
+ }
+ }
+
+ // Wait if it's processing
+ while(1)
+ {
+ {
+ Mutex::AutoLock processedLocker(processedMutex);
+ if (processedSubstance==NULL || package!=processedSubstance->material->GetSubstancePackage())
+ break;
+ }
+ Thread::Sleep(0.001);
+ }
+
+ // Remove it from updating array if in
+ {
+ Mutex::AutoLock updatingLocker(updatingMutex);
+ std::list<Substance*>::iterator it;
+ while((it=std::find_if(updatingSubstanceQueue.begin(), updatingSubstanceQueue.end(), QueuePackageFinder(package)))!=updatingSubstanceQueue.end())
+ {
+ // Delete the output buffers from the Substance if they have not yet been pushed to the GPU
+ // This can happen when building for example, where very quick cycles of load/generate/discard are performed
+ for (std::map<ProceduralTexture*,SubstanceTexture>::iterator itTex = (*it)->updatedTextures.begin() ;
+ itTex != (*it)->updatedTextures.end() ; ++itTex)
+ {
+ OnFree(itTex->second.buffer);
+ }
+
+ delete *it;
+ updatingSubstanceQueue.erase(it);
+ }
+ }
+
+ // Clear last processed if needed
+ {
+ Mutex::AutoLock lastSubstanceLocker(lastProcessedSubstanceMutex);
+ if (lastProcessedSubstance!=NULL && package==lastProcessedSubstance->GetSubstancePackage())
+ lastProcessedSubstance = NULL;
+ }
+}
+
+void SubstanceSystem::SetProcessorUsage(ProceduralProcessorUsage usage)
+{
+ m_ProcessorUsage = usage;
+
+ switch(m_ProcessorUsage)
+ {
+ case ProceduralProcessorUsage_Half:
+ processingThread.SetPriority(kNormalPriority);
+ break;
+ case ProceduralProcessorUsage_All:
+ processingThread.SetPriority(kNormalPriority);
+ break;
+ default:
+ processingThread.SetPriority(kLowPriority);
+ }
+}
+
+void SubstanceSystem::ClearProcessingQueue()
+{
+ Mutex::AutoLock processingLocker(processingMutex);
+ for (std::list<Substance*>::iterator it=processingSubstanceQueue.begin() ; it!=processingSubstanceQueue.end() ; ++it)
+ {
+ delete *it;
+ }
+ processingSubstanceQueue.clear();
+}
+
+SubstanceSystem::Context::Context(ProceduralProcessorUsage usage)
+{
+ m_OldProcessorUsage = GetSubstanceSystem().GetProcessorUsage();
+ GetSubstanceSystem().SetProcessorUsage(usage);
+}
+
+SubstanceSystem::Context::~Context()
+{
+ GetSubstanceSystem().SetProcessorUsage(m_OldProcessorUsage);
+}
+
+void SubstanceSystem::ForceSubstanceResults(std::map<ProceduralTexture*, SubstanceTexture>& results)
+{
+ Assert( processedSubstance!=NULL && "Substance caching incorrect behavior!" );
+ processedSubstance->updatedTextures = results;
+}
+
+void SubstanceSystem::UpdateMemoryBudget()
+{
+ Mutex::AutoLock lastSubstanceLocker(lastProcessedSubstanceMutex);
+ if (lastProcessedSubstance!=NULL &&
+ lastProcessedSubstance->GetSubstanceHandle()!=processedSubstance->material->GetSubstanceHandle())
+ {
+ //WarningStringMsg("Emptying Substance cache for handle %p, for substance %s\n", lastProcessedSubstance->GetSubstanceHandle(), lastProcessedSubstance->GetName());
+ ApplyMemoryBudget(lastProcessedSubstance, false);
+ }
+
+ lastProcessedSubstance = processedSubstance->material;
+ ApplyMemoryBudget(lastProcessedSubstance, true);
+}
+
+void SubstanceSystem::ApplyMemoryBudget(ProceduralMaterial* substance, bool requireMaximum, bool outOfMemory)
+{
+ if (substance->GetSubstanceHandle()==NULL)
+ {
+ // The substance is not loaded now, so skip it.
+ return;
+ }
+
+ // Always use tiny memory budget in the editor if the material is loading, this to lower memory usage.
+ // Only do this when NOT playing (in Play mode, the runtime-set mem. budget must be used)
+#if UNITY_EDITOR
+ if (!IsWorldPlaying() && requireMaximum && substance->IsFlagEnabled(ProceduralMaterial::Flag_Awake))
+ substance->SetProceduralMemoryWorkBudget(ProceduralCacheSize_Tiny);
+#endif
+
+ SubstanceHardResources memoryBudget;
+ memset(&memoryBudget, 0, sizeof(SubstanceHardResources));
+ int processorCount = max(systeminfo::GetProcessorCount(), 1);
+ int mediumLimit = max(processorCount/2, 1);
+
+ for (int index=0;index<SUBSTANCE_CPU_COUNT_MAX;++index)
+ {
+ unsigned char processorUsage(Substance_Resource_FullUse);
+
+ if ((m_ProcessorUsage==ProceduralProcessorUsage_Half && index>=mediumLimit)
+ || (m_ProcessorUsage==ProceduralProcessorUsage_One && index>0))
+ {
+ processorUsage = Substance_Resource_DoNotUse;
+ }
+
+ memoryBudget.cpusUse[index] = processorUsage;
+ }
+
+ if (outOfMemory)
+ {
+ substance->SetProceduralMemoryWorkBudget(
+ (ProceduralCacheSize)((int)substance->GetProceduralMemoryWorkBudget()+1));
+ }
+
+ size_t workSize = GetProceduralMemoryBudget(substance->GetProceduralMemoryWorkBudget());
+ if (workSize==1)
+ workSize = 128 * 1024 * 1024;
+ size_t sleepSize = GetProceduralMemoryBudget(substance->GetProceduralMemorySleepBudget());
+
+ memoryBudget.systemMemoryBudget = requireMaximum?workSize:sleepSize;
+
+ if (substanceHandleSwitchHard(substance->GetSubstanceHandle(), Substance_Sync_Synchronous, &memoryBudget, NULL, 0)!=0)
+ ErrorStringObject("Failed to set substance memory budget", substance);
+
+ if (!outOfMemory)
+ {
+ if (substanceHandleStart(substance->GetSubstanceHandle(), Substance_Sync_Synchronous)!=0)
+ ErrorStringObject("Failed to update substance memory budget", substance);
+ }
+}
+
+void SubstanceSystem::Integrate()
+{
+ if (integrationQueue.size()>0)
+ {
+ Mutex::AutoLock deleteLocker(deleteIntegrationMutex);
+
+ // Retrieve materials to pack
+ std::vector<ProceduralMaterial*> packedMaterials;
+ {
+ Mutex::AutoLock integrationLocker(integrationMutex);
+ packedMaterials = integrationQueue;
+ }
+
+ // Pack all available substances
+ ProceduralMaterial::PackSubstances(packedMaterials);
+
+ for (std::vector<ProceduralMaterial*>::iterator it=packedMaterials.begin() ; it!=packedMaterials.end() ; ++it)
+ {
+ if (!(*it)->IsFlagEnabled(ProceduralMaterial::Flag_Broken)
+ && ((*it)->IsFlagEnabled(ProceduralMaterial::Flag_ForceGenerate)
+ || !IsWorldPlaying() || (*it)->GetLoadingBehavior()!=ProceduralLoadingBehavior_None))
+ {
+ QueueSubstance(*it);
+ queryClearCache = *it;
+ }
+
+ // Remove it from the integrationQueue
+ Mutex::AutoLock integrationLocker(integrationMutex);
+ std::vector<ProceduralMaterial*>::iterator i = std::find(integrationQueue.begin(), integrationQueue.end(), *it);
+ integrationQueue.erase(i);
+ }
+ }
+}
+
+SubstanceSystem& GetSubstanceSystem ()
+{
+ return SubstanceArchive::GetSubstanceSystem();
+}
+
+SubstanceSystem* GetSubstanceSystemPtr ()
+{
+ return SubstanceArchive::GetSubstanceSystemPtr();
+}
+
+
+#endif //ENABLE_SUBSTANCE
diff --git a/Runtime/Graphics/SubstanceSystem.h b/Runtime/Graphics/SubstanceSystem.h
new file mode 100644
index 0000000..e87b49d
--- /dev/null
+++ b/Runtime/Graphics/SubstanceSystem.h
@@ -0,0 +1,160 @@
+#pragma once
+
+#include "ProceduralMaterial.h"
+#include "SubstanceArchive.h"
+#include "Texture2D.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Threads/Semaphore.h"
+#include <queue>
+#include <algorithm>
+
+#if ENABLE_SUBSTANCE
+#include "External/Allegorithmic/builds/Engines/include/substance/handle.h"
+#include "External/Allegorithmic/builds/Engines/include/substance/device.h"
+
+class LoadProgress;
+
+class SubstanceSystem
+{
+ SubstanceDevice m_Device;
+ SubstanceContext* m_Context;
+
+public:
+
+ SubstanceSystem ();
+ ~SubstanceSystem ();
+
+ SubstanceContext* GetContext () { return m_Context; }
+
+ // Set the callback handling generated texture
+ // The default substance system is used when set to NULL
+ void SetOutputCallback(void* callback=NULL);
+
+ // State queries
+ bool AreQueuesEmpty();
+ bool AreIntegratingQueuesEmpty();
+ bool IsSubstanceProcessing(const ProceduralMaterial* substance);
+
+ // State wait
+ void WaitFinished(LoadProgress* progress = NULL);
+ void WaitFinished(const ProceduralMaterial* substance);
+
+ // Update substance system, called from the main thread
+ void Update(bool isQuitSignaled=false);
+
+ // Update substance system, called from the substance thread
+ // . generate queued substances
+ // . integrate substances
+ void UpdateThreaded();
+
+ // Queued updates coming from ProceduralMaterial
+ void QueueInput(ProceduralMaterial* material, std::string inputName, SubstanceValue& inputValue);
+ void QueueSubstance(ProceduralMaterial* material);
+ void QueueLoading(ProceduralMaterial* material);
+
+ void QueryClearCache(ProceduralMaterial* material);
+
+ static void * ThreadMain(void*data);
+
+ // Substance callbacks
+ static void* SUBSTANCE_CALLBACK OnMalloc(size_t bytesCount, size_t alignment=16);
+ static void SUBSTANCE_CALLBACK OnFree(void* bufferPtr);
+ static void SUBSTANCE_CALLBACK OnOutputCompleted(SubstanceHandle* _pHandle, unsigned int _OutputIndex, size_t _JobUserData);
+ static void SUBSTANCE_CALLBACK OnInputImageLock(SubstanceHandle* _pHandle, size_t _JobUserData, unsigned int _InputIndex, SubstanceTextureInput** _ppCurrentTextureInputDesc, const SubstanceTextureInput* _pPreferredTextureInputDesc);
+ static void SUBSTANCE_CALLBACK OnInputImageUnlock(SubstanceHandle* _pHandle, size_t _JobUserData, unsigned int _InputIndex, SubstanceTextureInput* _ppCurrentTextureInputDesc);
+ static void SUBSTANCE_CALLBACK OnOutOfMemory(SubstanceHandle *handle, int memoryType);
+
+ // Substance notifications
+ void NotifySubstanceCreation(ProceduralMaterial* substance);
+ void NotifySubstanceDestruction(ProceduralMaterial* substance);
+ void NotifyTextureDestruction(ProceduralTexture* texture);
+ void NotifyPackageDestruction(SubstanceArchive* package);
+
+ // Priority handling
+ void SetProcessorUsage(ProceduralProcessorUsage usage);
+ ProceduralProcessorUsage GetProcessorUsage() const { return m_ProcessorUsage; }
+ void ClearProcessingQueue();
+
+ // Queued substance data
+ struct Substance
+ {
+ ProceduralMaterial* material;
+ std::map<std::string, SubstanceValue> inputValues;
+ std::map<ProceduralTexture*, SubstanceTexture> updatedTextures;
+ };
+ std::map<unsigned int, ProceduralTexture*> processedTextures;
+ void ForceSubstanceResults(std::map<ProceduralTexture*, SubstanceTexture>& results);
+
+ // Memory budget management
+ void UpdateMemoryBudget();
+
+ // Context of generation which temporary set processor usage
+ class Context
+ {
+ public:
+ Context(ProceduralProcessorUsage usage);
+ ~Context();
+ private:
+ ProceduralProcessorUsage m_OldProcessorUsage;
+ };
+
+ void BeginPreloading() { ++integrationTimeStamp; }
+ unsigned int integrationTimeStamp;
+
+private:
+ void ApplyMemoryBudget(ProceduralMaterial* substance, bool requireMaximum, bool outOfMemory=false);
+
+ // Threaded integration / packing of substances
+ void Integrate();
+
+ // Substance queue helpers
+ struct QueueFinder
+ {
+ const ProceduralMaterial* m_Material;
+ QueueFinder(const ProceduralMaterial* material) : m_Material(material) {}
+ bool operator()(const Substance* substance) { return substance->material == m_Material; }
+ };
+
+ struct QueuePackageFinder
+ {
+ const SubstanceArchive* m_Package;
+ QueuePackageFinder(const SubstanceArchive* package) : m_Package(package) {}
+ bool operator()(const Substance* substance) { return substance->material->GetSubstancePackage()==m_Package; }
+ };
+
+ struct ArrayPackageFinder
+ {
+ const SubstanceArchive* m_Package;
+ ArrayPackageFinder(const SubstanceArchive* package) : m_Package(package) {}
+ bool operator()(const ProceduralMaterial* substance) { return substance->GetSubstancePackage()==m_Package; }
+ };
+
+ std::list<Substance*> waitingSubstanceQueue;
+ std::list<Substance*> processingSubstanceQueue;
+ std::list<Substance*> updatingSubstanceQueue;
+ std::vector<ProceduralMaterial*> integrationQueue;
+ Thread processingThread;
+ Mutex processingMutex;
+ Mutex updatingMutex;
+ Mutex processedMutex;
+ Mutex integrationMutex;
+ Mutex waitingMutex;
+ Substance* processedSubstance;
+ void* outputCallback;
+ ProceduralMaterial* lastProcessedSubstance;
+ Mutex lastProcessedSubstanceMutex;
+ std::vector<ProceduralMaterial*> animatedSubstanceArray;
+ ProceduralProcessorUsage m_ProcessorUsage;
+ ProceduralMaterial* queryClearCache;
+ Mutex deleteIntegrationMutex;
+ Mutex deletePingMutex;
+ bool isIntegrating;
+ Semaphore threadSemaphore;
+ static int s_maximumSubstancePerFrame;
+};
+
+SubstanceSystem& GetSubstanceSystem ();
+SubstanceSystem* GetSubstanceSystemPtr ();
+
+#endif
diff --git a/Runtime/Graphics/Texture.cpp b/Runtime/Graphics/Texture.cpp
new file mode 100644
index 0000000..a343da4
--- /dev/null
+++ b/Runtime/Graphics/Texture.cpp
@@ -0,0 +1,332 @@
+#include "UnityPrefix.h"
+#include "Texture.h"
+#include "Image.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "Runtime/Graphics/ProceduralMaterial.h"
+
+using namespace std;
+
+static int gTextureBaseLevel = 0;
+static int gAnisoSetting = Texture::kEnableAniso;
+
+static const int kForceAnisoMinLevelDefault = 9;
+static const int kAnisoMaxLevelDefault = 16;
+
+static int gForceAnisoMinLevel = kForceAnisoMinLevelDefault;
+static int gAnisoMaxLevel = kAnisoMaxLevelDefault;
+
+
+Texture::TextureIDMap Texture::s_TextureIDMap;
+
+
+///@todo: texture should not allocate memory based on texture base level.
+
+Texture::Texture(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode), m_UVScaleX(1.0f), m_UVScaleY(1.0f), m_TexelSizeX(1.0f), m_TexelSizeY(1.0f)
+{
+ // We use unchecked version since we may not be on the main thread
+ // This means CreateTextureID() implementation must be thread safe!
+ m_TexID = GetUncheckedGfxDevice().CreateTextureID();
+ m_UsageMode = kTexUsageNone;
+ m_ColorSpace = kTexColorSpaceLinear;
+}
+
+Texture::~Texture ()
+{
+ MainThreadCleanup ();
+}
+
+bool Texture::MainThreadCleanup ()
+{
+ Texture::s_TextureIDMap.erase (m_TexID);
+
+ // FreeTextureID() implementation must be thread safe!
+ GetUncheckedGfxDevice().FreeTextureID(m_TexID);
+ m_TexID = TextureID();
+
+ // Notify TexEnvs using this texture
+ for( size_t i = 0; i < m_TexEnvUsers.size(); ++i )
+ {
+ ASSERT_RUNNING_ON_MAIN_THREAD
+ m_TexEnvUsers[i]->SetUsedTexture(NULL, 0);
+ }
+ m_TexEnvUsers.clear();
+ return true;
+}
+
+
+void Texture::CheckConsistency()
+{
+ Super::CheckConsistency();
+ m_TextureSettings.CheckConsistency ();
+}
+
+void Texture::Reset ()
+{
+ Super::Reset ();
+ m_UsageMode = kTexUsageNone;
+ m_ColorSpace = kTexColorSpaceLinear;
+}
+
+void Texture::SetMasterTextureLimit (int i, bool reloadTextures /*= true*/)
+{
+ if (gTextureBaseLevel == i)
+ return;
+
+ gTextureBaseLevel = i;
+ if (!reloadTextures)
+ return;
+
+ vector<SInt32> objects;
+ Object::FindAllDerivedObjects (ClassID (Texture), &objects);
+ for( size_t j = 0; j < objects.size(); ++j )
+ {
+ Texture& tex = *PPtr<Texture> (objects[j]);
+#if UNITY_EDITOR
+ if (tex.IgnoreMasterTextureLimit ())
+ continue;
+#endif
+ tex.UnloadFromGfxDevice(false);
+ tex.UploadToGfxDevice();
+ }
+
+ ProceduralMaterial::ReloadAll();
+}
+
+void Texture::ReloadAll (bool unload, bool load, bool forceUnloadAll)
+{
+ vector<SInt32> objects;
+ Object::FindAllDerivedObjects (ClassID (Texture), &objects, true);
+ for (size_t i=0;i<objects.size ();++i)
+ {
+ Texture& tex = *PPtr<Texture> (objects[i]);
+
+ if (unload)
+ tex.UnloadFromGfxDevice(forceUnloadAll);
+ if (load)
+ tex.UploadToGfxDevice();
+ }
+
+ ProceduralMaterial::ReloadAll(unload, load);
+}
+
+static void SetAnisoLimitEnumImpl(int aniso, bool forced = false)
+{
+ if (aniso == gAnisoSetting && !forced)
+ return;
+
+ gAnisoSetting = aniso;
+ if (gAnisoSetting == Texture::kDisableAniso)
+ TextureSettings::SetAnisoLimits (1, 1);
+ else if (gAnisoSetting == Texture::kForceEnableAniso)
+ TextureSettings::SetAnisoLimits (gForceAnisoMinLevel, gAnisoMaxLevel);
+ else
+ TextureSettings::SetAnisoLimits (1, gAnisoMaxLevel);
+
+ vector<Texture*> objects;
+ Object::FindObjectsOfType (&objects);
+ for (int i=0;i<objects.size ();i++)
+ objects[i]->ApplySettings ();
+}
+
+void Texture::SetAnisoLimit (int aniso)
+{
+ SetAnisoLimitEnumImpl(aniso, false);
+}
+
+
+void Texture::SetGlobalAnisoLimits(int forcedMin, int globalMax)
+{
+ if(forcedMin == -1)
+ forcedMin = kForceAnisoMinLevelDefault;
+ if(globalMax == -1)
+ globalMax = kAnisoMaxLevelDefault;
+
+ if(gForceAnisoMinLevel == forcedMin && gAnisoMaxLevel == globalMax)
+ return;
+
+ gForceAnisoMinLevel = forcedMin;
+ gAnisoMaxLevel = globalMax;
+
+ SetAnisoLimitEnumImpl(gAnisoSetting, true);
+}
+
+int Texture::GetAnisoLimit ()
+{
+ return gAnisoSetting;
+}
+
+bool Texture::HasMipMap () const {return false;}
+
+void Texture::ApplySettings()
+{
+ m_TextureSettings.Apply( GetTextureID(), GetDimension(), HasMipMap(), GetActiveTextureColorSpace() );
+ NotifyMipBiasChanged();
+}
+
+void Texture::AddTexEnvUser(ShaderLab::TexEnv* texenv)
+{
+ SET_ALLOC_OWNER(this);
+ size_t index = m_TexEnvUsers.size();
+ texenv->SetUsedTexture(this, index);
+ m_TexEnvUsers.push_back(texenv);
+}
+
+void Texture::RemoveTexEnvUser(ShaderLab::TexEnv* texenv, size_t index)
+{
+ DebugAssert(m_TexEnvUsers[index] == texenv);
+ // Swap with last element and pop
+ m_TexEnvUsers[index] = m_TexEnvUsers.back();
+ m_TexEnvUsers[index]->SetUsedTexture(this, index);
+ m_TexEnvUsers.pop_back();
+ texenv->SetUsedTexture(NULL, 0);
+}
+
+void Texture::NotifyMipBiasChanged()
+{
+ float mipBias = m_TextureSettings.m_MipBias;
+ for( size_t i = 0; i < m_TexEnvUsers.size(); ++i )
+ {
+ ShaderLab::TexEnv* te = m_TexEnvUsers[i];
+ te->TextureMipBiasChanged( mipBias );
+ }
+}
+
+void Texture::NotifyUVScaleChanged()
+{
+ float x = m_UVScaleX;
+ float y = m_UVScaleY;
+ for( size_t i = 0; i < m_TexEnvUsers.size(); ++i )
+ {
+ ShaderLab::TexEnv* te = m_TexEnvUsers[i];
+ te->TextureUVScaleChanged( x, y );
+ }
+}
+
+
+void Texture::SetFilterMode (int mode)
+{
+ if (m_TextureSettings.m_FilterMode != mode) {
+ m_TextureSettings.m_FilterMode = mode;
+ ApplySettings();
+ SetDirty();
+ }
+}
+
+void Texture::SetUsageMode ( TextureUsageMode mode)
+{
+ if (m_UsageMode != mode) {
+ m_UsageMode = mode;
+ ApplySettings();
+ SetDirty();
+ }
+}
+
+void Texture::SetStoredColorSpace(TextureColorSpace space)
+{
+ if (m_ColorSpace != space) {
+ m_ColorSpace = space;
+ ApplySettings();
+ SetDirty();
+ }
+}
+
+void Texture::SetStoredColorSpaceNoDirtyNoApply(TextureColorSpace space)
+{
+ if (m_ColorSpace != space) {
+ m_ColorSpace = space;
+ }
+}
+
+void Texture::SetWrapMode (int mode)
+{
+ if (m_TextureSettings.m_WrapMode != mode) {
+ m_TextureSettings.m_WrapMode = mode;
+ ApplySettings();
+ SetDirty();
+ }
+}
+
+void Texture::SetAnisoLevel (int level)
+{
+ if (m_TextureSettings.m_Aniso != level) {
+ m_TextureSettings.m_Aniso = level;
+ ApplySettings();
+ SetDirty();
+ }
+}
+
+void Texture::SetMipMapBias (float mipBias)
+{
+ if (m_TextureSettings.m_MipBias != mipBias)
+ {
+ m_TextureSettings.m_MipBias = mipBias;
+ ApplySettings();
+ SetDirty();
+ }
+}
+
+
+#if UNITY_EDITOR
+// Helper function so texture inspector can draw preview wrap mode
+void Texture::SetWrapModeNoDirty (int mode)
+{
+ m_TextureSettings.m_WrapMode = mode;
+ ApplySettings();
+}
+
+// Helper function so texture inspector can draw preview aniso level
+void Texture::SetAnisoLevelNoDirty (int level)
+{
+ m_TextureSettings.m_Aniso = level;
+ ApplySettings();
+}
+
+// Helper function so texture inspector can draw a specific mip of the texture
+void Texture::SetMipMapBiasNoDirty (float mipBias)
+{
+ m_TextureSettings.m_MipBias = mipBias;
+ ApplySettings();
+}
+
+// Helper function for editor so it can draw zoomed textures as point.
+void Texture::SetFilterModeNoDirty (int mode)
+{
+ if (m_TextureSettings.m_FilterMode != mode) {
+ m_TextureSettings.m_FilterMode = mode;
+ ApplySettings();
+ }
+}
+
+bool Texture::IgnoreMasterTextureLimit () const
+{
+ return false;
+}
+
+#endif
+
+int Texture::GetMasterTextureLimit ()
+{
+ return gTextureBaseLevel;
+}
+
+bool Texture::ShouldIgnoreInGarbageDependencyTracking ()
+{
+ return true;
+}
+
+void* Texture::GetNativeTexturePtr()
+{
+ return GetGfxDevice().GetNativeTexturePointer(m_TexID);
+}
+
+UInt32 Texture::GetNativeTextureID()
+{
+ return GetGfxDevice().GetNativeTextureID(m_TexID);
+}
+
+
+IMPLEMENT_CLASS (Texture)
diff --git a/Runtime/Graphics/Texture.h b/Runtime/Graphics/Texture.h
new file mode 100644
index 0000000..5babbbb
--- /dev/null
+++ b/Runtime/Graphics/Texture.h
@@ -0,0 +1,165 @@
+#pragma once
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "TextureFormat.h"
+#include "TextureSettings.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Modules/ExportModules.h"
+
+#define TRACK_TEXTURE_SIZES (UNITY_EDITOR || ENABLE_PROFILER)
+
+class ImageReference;
+namespace ShaderLab { class TexEnv; }
+
+class EXPORT_COREMODULE Texture : public NamedObject
+{
+protected:
+ TextureSettings m_TextureSettings;
+ TextureID m_TexID;
+
+ int m_UsageMode;
+
+ int m_ColorSpace;
+
+ // Used by movie textures so that 0..1 range covers only the
+ // movie portion. For all other textures this is (1,1).
+ float m_UVScaleX, m_UVScaleY;
+
+ // Texel size. This is 1/size for all textures.
+ float m_TexelSizeX, m_TexelSizeY;
+
+ // TexEnvs that use this texture
+ dynamic_array<ShaderLab::TexEnv*> m_TexEnvUsers;
+
+public:
+ REGISTER_DERIVED_ABSTRACT_CLASS (Texture, NamedObject)
+
+ Texture(MemLabelId label, ObjectCreationMode mode);
+
+ virtual bool MainThreadCleanup();
+
+ virtual void Reset ();
+
+ virtual void CheckConsistency();
+
+ virtual TextureDimension GetDimension () const = 0;
+
+ // Blits the textures contents into image.
+ // Will use the closest matching mipmap.
+ virtual bool ExtractImage (ImageReference* image, int imageIndex = 0) const = 0;
+
+ TextureID GetTextureID() const { return m_TexID; }
+
+ TextureUsageMode GetUsageMode() const { return static_cast<TextureUsageMode>(m_UsageMode); }
+ void SetUsageMode(TextureUsageMode mode);
+
+ // The Color space the texture is imported for.
+ void SetStoredColorSpace(TextureColorSpace space);
+ // Needed to avoid an odd corner case for srgb cube render textures.
+ // If a render texture has the apply function called a texture id will be created and
+ // it will cause an error as it will be the wrong dimension when cubemap is set
+ // This function sets the stored color space without applying it. It will be applied
+ // when the create on the cubemap is called.
+ void SetStoredColorSpaceNoDirtyNoApply(TextureColorSpace space);
+ TextureColorSpace GetStoredColorSpace() const { return static_cast<TextureColorSpace>(m_ColorSpace); }
+
+ // The active color space of the texture
+ // Depending on if we are in linear or gamma rendering mode, textures will be tagged as sRGB or non-SRGB.
+ TextureColorSpace GetActiveTextureColorSpace() const { return (GetActiveColorSpace() == kLinearColorSpace) ? GetStoredColorSpace() : kTexColorSpaceLinear; }
+
+ #if ENABLE_PROFILER
+ virtual int GetStorageMemorySize() const = 0;
+ #endif
+
+ virtual TextureID GetUnscaledTextureID() const { return m_TexID; }
+
+ // Raw (original) texture size. For NPOT textures these values are NPOT.
+ virtual int GetDataWidth() const = 0;
+ virtual int GetDataHeight() const = 0;
+
+ // Size as used by GL. In most cases this matches Data width/height, except for NPOT textures
+ // where this is scaled up to next power of two.
+ virtual int GetGLWidth() const { return GetDataWidth(); }
+ virtual int GetGLHeight() const { return GetDataHeight(); }
+
+ virtual bool HasMipMap () const = 0;
+ virtual int CountMipmaps () const = 0;
+
+ static void SetMasterTextureLimit (int i, bool reloadTextures = true);
+ static int GetMasterTextureLimit ();
+
+ static void ReloadAll (bool unload = true, bool load = true, bool forceUnloadAll = false);
+
+ /// this is at the wrong place
+ enum { kDisableAniso = 0, kEnableAniso = 1, kForceEnableAniso = 2 };
+
+ // Get/Set the global texture anisotrophy levels
+ static void SetAnisoLimit (int aniso);
+ static int GetAnisoLimit ();
+
+ static void SetGlobalAnisoLimits(int forcedMin, int globalMax);
+
+ static Texture* FindTextureByID (TextureID tid)
+ {
+ TextureIDMap::iterator it = s_TextureIDMap.find(tid);
+ return it == s_TextureIDMap.end() ? NULL : it->second;
+ }
+
+ // Set the filtering mode for this texture.
+ // TextureFilterMode kTexFilterNearest, kTexFilterBilinear, kTexFilterTrilinear
+ void SetFilterMode( int mode );
+ int GetFilterMode() const { return m_TextureSettings.m_FilterMode; }
+
+ void SetWrapMode (int mode);
+ int GetWrapMode () const { return m_TextureSettings.m_WrapMode; }
+
+ void SetAnisoLevel (int mode);
+ int GetAnisoLevel () const { return m_TextureSettings.m_Aniso; }
+
+ float GetMipMapBias () const { return m_TextureSettings.m_MipBias; }
+ void SetMipMapBias (float bias);
+
+ const TextureSettings& GetSettings() const { return m_TextureSettings; }
+ TextureSettings& GetSettings() { return m_TextureSettings; }
+ virtual void ApplySettings();
+
+ float GetUVScaleX() const { return m_UVScaleX; }
+ float GetUVScaleY() const { return m_UVScaleY; }
+ void SetUVScale( float x, float y ) { m_UVScaleX = x; m_UVScaleY = y; NotifyUVScaleChanged(); }
+ float GetTexelSizeX() const { return m_TexelSizeX; }
+ float GetTexelSizeY() const { return m_TexelSizeY; }
+ void SetTexelSize( float x, float y ) { m_TexelSizeX = x; m_TexelSizeY = y; }
+
+ void AddTexEnvUser(ShaderLab::TexEnv* texenv);
+ void RemoveTexEnvUser(ShaderLab::TexEnv* texenv, size_t index);
+
+ virtual bool ShouldIgnoreInGarbageDependencyTracking ();
+
+
+#if UNITY_EDITOR
+ void SetAnisoLevelNoDirty (int level);
+ void SetWrapModeNoDirty (int mode);
+ void SetMipMapBiasNoDirty (float mipBias);
+ void SetFilterModeNoDirty (int mode);
+ virtual bool IgnoreMasterTextureLimit () const;
+
+ virtual TextureFormat GetEditorUITextureFormat () const = 0;
+#endif
+
+ void* GetNativeTexturePtr();
+ UInt32 GetNativeTextureID();
+
+protected:
+ void NotifyMipBiasChanged();
+ void NotifyUVScaleChanged();
+
+ // Used when changing master texture mip limit or explicitly reloading/unloading
+ // all resources.
+ virtual void UnloadFromGfxDevice(bool forceUnloadAll) = 0;
+ virtual void UploadToGfxDevice() = 0;
+
+ typedef std::map<TextureID, Texture*> TextureIDMap;
+ static TextureIDMap s_TextureIDMap;
+};
diff --git a/Runtime/Graphics/Texture2D.cpp b/Runtime/Graphics/Texture2D.cpp
new file mode 100644
index 0000000..691dfd9
--- /dev/null
+++ b/Runtime/Graphics/Texture2D.cpp
@@ -0,0 +1,1422 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Texture2D.h"
+#include "Image.h"
+#include "RenderTexture.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "S3Decompression.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Serialize/SwapEndianArray.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/GfxDevice/GfxDeviceConfigure.h"
+#include "ImageConversion.h"
+#include "Runtime/Misc/Allocator.h"
+#include "DXTCompression.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+#if UNITY_EDITOR
+#include "CubemapTexture.h"
+#endif
+
+#if UNITY_WII
+#include "PlatformDependent/wii/WiiUtility.h"
+#endif
+
+#if ENABLE_TEXTUREID_MAP
+#include "Runtime/GfxDevice/TextureIdMap.h"
+#endif
+
+
+#define USE_IMMEDIATE_INTEGRATION (UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_PS3 || UNITY_XENON || UNITY_TIZEN)
+
+
+PROFILER_INFORMATION(gIntegrateLoadedImmediately, "Texture.IntegrateLoadedImmediately", kProfilerLoading)
+PROFILER_INFORMATION(gAwakeFromLoadTex2D, "Texture.AwakeFromLoad", kProfilerLoading)
+
+/*
+ Regular and non-power-of-two textures:
+
+ For regular textures, all TextureRepresentation's are exactly the same; and the data is allocated just
+ once (all representations point to the same data).
+
+ For NPOT textures:
+ * m_TexData is what is serialized (non power of two). Mip maps use floor(size/2) convention,
+ DXT compression is actually next multiple of 4.
+ * If no mipmaps are specified and NPOTRestricted is supported - act as regular texture
+ * If mipmaps are specified and NPOTFULL is supported - act as regular texture
+ * If NPOT textures are not supported:
+ * m_Tex is scaled up to POT at load time. DXT source gets decompressed into ARGB32.
+ * m_TexPadded is POT size, but the original is padded by repeating border pixels. GUITexture uses it.
+*/
+
+bool Texture2D::s_ScreenReadAllowed = true;
+
+static inline UInt32 GetBytesForOnePixel( TextureFormat format )
+{
+ return IsAnyCompressedTextureFormat(format) ? 0 : GetBytesFromTextureFormat(format);
+}
+
+bool IsNPOTTextureAllowed(bool hasMipMap)
+{
+ if(IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a1))
+ return hasMipMap ? gGraphicsCaps.npot == kNPOTFull : gGraphicsCaps.npot >= kNPOTRestricted;
+ else
+ return false;
+}
+
+static int GetNextAllowedTextureSize(int size, bool hasMipMap, TextureFormat format)
+{
+ int multipleMask = GetTextureSizeAllowedMultiple(format) - 1;
+ size = (size + multipleMask) & ~multipleMask;
+ if (!IsNPOTTextureAllowed(hasMipMap))
+ size = NextPowerOfTwo(size);
+ return size;
+}
+
+
+Texture2D::TextureRepresentation::TextureRepresentation()
+: data(NULL)
+, width(0)
+, height(0)
+, format(kTexFormatARGB32)
+, imageSize(0)
+{
+}
+
+Texture2D::Texture2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_InitFlags(0)
+{
+ #if UNITY_EDITOR
+ m_IgnoreMasterTextureLimit = false;
+ #endif
+
+ m_MipMap = false;
+ m_TextureDimension = kTexDimNone;
+
+#if UNITY_EDITOR
+ m_EditorDontWriteTextureData = false;
+ m_AlphaIsTransparency = false;
+#endif
+
+ m_PowerOfTwo = true;
+
+ m_ImageCount = 0;
+
+ m_IsReadable = true;
+ m_IsUnreloadable = false;
+ m_ReadAllowed = true;
+ m_TextureUploaded = false;
+ m_UnscaledTextureUploaded = false;
+
+ // We use unchecked version since we may not be on the main thread
+ // This means CreateTextureID() implementation must be thread safe!
+ m_UnscaledTexID = GetUncheckedGfxDevice().CreateTextureID();
+}
+
+void Texture2D::Reset ()
+{
+ Super::Reset();
+
+ m_TextureSettings.Reset ();
+}
+
+//#define LOG_AWAKE(x, arg) printf_console(x, arg);
+#define LOG_AWAKE(x, arg)
+
+typedef std::vector<Texture2D*> TexturesT;
+static TexturesT g_TexturesToUploadOnMainThread;
+static Mutex g_UploadMutex;
+
+void Texture2D::AwakeFromLoadThreaded()
+{
+ Super::AwakeFromLoadThreaded();
+
+#if USE_IMMEDIATE_INTEGRATION
+ LOG_AWAKE("Texture2D: AwakeFromLoadThreaded (%s)\n", GetName());
+ g_UploadMutex.Lock();
+ g_TexturesToUploadOnMainThread.push_back(this);
+ g_UploadMutex.Unlock();
+ // Pause loading for a while
+ // this will allow main thread to start integrating previously loaded textures
+ Thread::Sleep(0.001);
+#endif // USE_IMMEDIATE_INTEGRATION
+}
+
+// Helps to avoid memory peak while loading the level on platforms where graphics driver creates an internal copy of texture data
+//
+// Memory peak arrises in the following scenario:
+// 1) [Load thread] all texture assets are loaded
+// 2) [Main thread] for every asset (OnAwake)
+// a) texture data is uploaded to graphics driver (copy)
+// b) texture data is released (in case of NonReadable textures)
+//
+// Instead we do:
+// 1) [Load thread] load 1 texture asset
+// 2) [Load thread] stall for a moment and allow Main thread to start uploading
+// 3) [Main thread] upload as much texture data as possible
+// 4) [Main thread] if significantly behind Load thread, then block Load thread
+// 5) continue for all assets
+//
+// NOTE: Main thread calls IntegrateLoadedImmediately() from PreloadManager::UpdatePreloadingSingleStep()
+void Texture2D::IntegrateLoadedImmediately()
+{
+#if USE_IMMEDIATE_INTEGRATION
+
+ PROFILER_AUTO(gIntegrateLoadedImmediately, NULL)
+
+ g_UploadMutex.Lock();
+ TexturesT toUpload = g_TexturesToUploadOnMainThread;
+ g_TexturesToUploadOnMainThread.clear();
+
+ size_t totalTextureSize = 0;
+ for (size_t q = 0; q < toUpload.size(); ++q)
+ totalTextureSize += toUpload[q]->m_TexData.imageSize;
+
+ // Evalute texture data ready for immediate integration
+ // If we have too much data to upload, then block the loading thread!
+ // Loading thread will stall until all pending data is uploaded.
+ bool blockLoadingThread = totalTextureSize > (512*1024);
+
+ // Avoid stalling on every texture (if possible) to improve loading speed
+ if (!blockLoadingThread)
+ {
+ g_UploadMutex.Unlock();
+ }
+ else
+ {
+ LOG_AWAKE("Texture2D: IntegrateLoadedImmediately() will block loader thread, pending texture data size: %d bytes\n", totalTextureSize);
+ }
+
+ for (size_t q = 0; q < toUpload.size(); ++q)
+ {
+ Texture2D* tex = toUpload[q];
+ LOG_AWAKE("Texture2D: Immediately upload texture data (%s)\n", tex->GetName());
+ if (tex->m_TexData.data != NULL)
+ tex->UploadTexture (false);
+ }
+
+ if (blockLoadingThread)
+ g_UploadMutex.Unlock();
+
+#endif // USE_IMMEDIATE_INTEGRATION
+}
+
+void Texture2D::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ SET_ALLOC_OWNER(this);
+ Super::AwakeFromLoad (awakeMode);
+
+ if( (awakeMode == kDefaultAwakeFromLoad || awakeMode == kInstantiateOrCreateFromCodeAwakeFromLoad)
+ && m_TexData.data == NULL
+ )
+ {
+ // actually pretty valid
+ return;
+ }
+
+#if USE_IMMEDIATE_INTEGRATION
+ // Check if texture data was already uploaded by IntegrateLoadedImmediately()
+ if ((awakeMode & kDidLoadThreaded) != 0)
+ {
+ bool dynamicTexture = m_TexData.imageSize == 0;
+ Assert(m_TextureUploaded || dynamicTexture);
+
+ if (m_TextureUploaded)
+ return;
+ }
+ LOG_AWAKE("Texture2D: Upload texture data in Awake (%s)\n", GetName());
+#endif // USE_IMMEDIATE_INTEGRATION
+
+ PROFILER_AUTO(gAwakeFromLoadTex2D, this)
+ UploadTexture (false);
+}
+
+Texture2D::~Texture2D ()
+{
+ DestroyTexture ();
+ MainThreadCleanup ();
+}
+
+bool Texture2D::MainThreadCleanup ()
+{
+ DeleteGfxTexture ();
+
+ Texture::s_TextureIDMap.erase (m_UnscaledTexID);
+
+ // FreeTextureID() implementation must be thread safe!
+ GetUncheckedGfxDevice().FreeTextureID(m_UnscaledTexID);
+ m_UnscaledTexID = TextureID();
+
+ return Super::MainThreadCleanup ();
+}
+
+
+static int SourceMipLevelForBlit( int srcWidth, int srcHeight, int dstWidth, int dstHeight )
+{
+ int level = HighestBit( NextPowerOfTwo(srcWidth) ) - HighestBit( NextPowerOfTwo(dstWidth) );
+ level = std::max( level, HighestBit( NextPowerOfTwo(srcHeight) ) - HighestBit( NextPowerOfTwo(dstHeight) ) );
+ level = std::max( 0, level );
+ return level;
+}
+
+// Compressed->compressed extraction. Copies compressed blocks and pads the remainder with
+// transparent black.
+void Texture2D::ExtractCompressedImageInternal( UInt8* dst, int dstWidth, int dstHeight, int imageIndex ) const
+{
+ AssertIf( imageIndex < 0 || imageIndex >= m_ImageCount );
+
+ TextureRepresentation const& rep =m_TexData;
+ AssertIf( !IsAnyCompressedTextureFormat( rep.format ) );
+ if (m_TexData.data == NULL)
+ {
+ ErrorString ("Texture data can not be accessed");
+ return;
+ }
+
+ int mipmapLevel = SourceMipLevelForBlit( rep.width, rep.height, dstWidth, dstHeight );
+ mipmapLevel = std::min( mipmapLevel, CountDataMipmaps() - 1 );
+ int mipmapoffset = CalculateMipMapOffset( rep.width, rep.height, rep.format, mipmapLevel );
+ int width = std::max( rep.width >> mipmapLevel, 1 );
+ int height = std::max( rep.height >> mipmapLevel, 1 );
+
+ // pad-copy compressed->compressed
+ BlitCopyCompressedImage( rep.format, rep.data + imageIndex * rep.imageSize + mipmapoffset,
+ width, height, dst, dstWidth, dstHeight, true );
+}
+
+
+bool Texture2D::ExtractImageInternal( ImageReference* image, bool scaleToSize, int imageIndex ) const
+{
+ AssertIf (imageIndex < 0 || imageIndex >= m_ImageCount);
+
+ TextureRepresentation const& rep = m_TexData;
+ if(rep.data == NULL)
+ {
+ ErrorString ("Texture is not accessible.");
+ return false;
+ }
+
+ int mipmapLevel = SourceMipLevelForBlit( rep.width, rep.height, image->GetWidth(), image->GetHeight() );
+ mipmapLevel = std::min( mipmapLevel, CountDataMipmaps() - 1 );
+ int mipmapoffset = CalculateMipMapOffset( rep.width, rep.height, rep.format, mipmapLevel );
+ int width = std::max( rep.width >> mipmapLevel, 1 );
+ int height = std::max( rep.height >> mipmapLevel, 1 );
+
+ if( IsAnyCompressedTextureFormat(rep.format) )
+ {
+ // Decompress into upper multiple-of-4 size
+ int decompWidth = (width + 3) / 4 * 4;
+ int decompHeight = (height + 3) / 4 * 4;
+
+ Image decompressed( decompWidth, decompHeight, kTexFormatRGBA32 );
+ UInt8* compressed = rep.data + imageIndex * rep.imageSize + mipmapoffset;
+
+ if (!DecompressNativeTextureFormatWithMipLevel( rep.format, width, height, mipmapLevel, (UInt32*)compressed, decompWidth, decompHeight, (UInt32*)decompressed.GetImageData() ))
+ return false;
+
+ // Clip this back to original size
+ ImageReference clipped = decompressed.ClipImage( 0, 0, width, height );
+
+ if( scaleToSize )
+ {
+ image->BlitImage( clipped, ImageReference::BLIT_BILINEAR_SCALE );
+ }
+ else
+ {
+ AssertIf( width > image->GetWidth() || height > image->GetHeight() );
+ image->BlitImage( clipped, ImageReference::BLIT_COPY );
+ PadImageBorder( *image, width, height );
+ }
+ return true;
+ }
+ else
+ {
+ ImageReference source( width, height, width * GetBytesFromTextureFormat(rep.format),
+ rep.format, rep.data + imageIndex * rep.imageSize + mipmapoffset);
+
+ if( scaleToSize )
+ {
+ image->BlitImage( source, ImageReference::BLIT_BILINEAR_SCALE );
+ }
+ else
+ {
+ AssertIf( width > image->GetWidth() || height > image->GetHeight() );
+ image->BlitImage( source, ImageReference::BLIT_COPY );
+ PadImageBorder( *image, width, height );
+ }
+ return true;
+ }
+
+ return false;
+}
+bool Texture2D::ExtractImage (ImageReference* image, int imageIndex) const
+{
+ return ExtractImageInternal( image, true, imageIndex );
+}
+
+bool Texture2D::HasMipMap() const {
+ return m_MipMap;
+}
+
+int Texture2D::CountMipmaps() const
+{
+ if (m_MipMap)
+ return CalculateMipMapCount3D( m_glWidth, m_glHeight, 1 );
+ else
+ return 1;
+}
+int Texture2D::CountDataMipmaps() const
+{
+ if (m_MipMap)
+ return CalculateMipMapCount3D( m_TexData.width, m_TexData.height, 1 );
+ else
+ return 1;
+}
+
+UInt8* Texture2D::AllocateTextureData (int imageSize, TextureFormat format, bool initMemory)
+{
+ // Allocate one more pixel because software bi-linear filtering might require it.
+ int allocSize = imageSize + GetBytesForOnePixel(format);
+ void* buffer = UNITY_MALLOC_ALIGNED (GetTextureDataMemoryLabel(), allocSize, 32);
+
+ // In the past the memory manager cleared textures created from script to 0xcd.
+ // Let's keep doing that even though a color like white makes more sense (case 564961).
+ if (initMemory && buffer)
+ memset(buffer, 0xcd, allocSize);
+
+ return static_cast<UInt8*>(buffer);
+}
+
+void Texture2D::DeallocateTextureData (UInt8* tex)
+{
+ UNITY_FREE (GetTextureDataMemoryLabel(), tex);
+}
+
+void Texture2D::InitTextureInternal (int width, int height, TextureFormat format, int imageSize, UInt8* buffer, int options, int imageCount)
+{
+ // Cleanup old memory
+ if ((options & kThreadedInitialize) == 0)
+ {
+ DestroyTexture ();
+ SetDirty ();
+ }
+ else
+ {
+ AssertIf(m_TexData.data != NULL);
+ }
+
+ m_TextureDimension = kTexDim2D;
+
+ m_TexData.width = width;
+ m_TexData.height = height;
+ m_TexData.format = format;
+ m_TexData.imageSize = imageSize;
+ m_TexData.data = buffer;
+ m_MipMap = options & kMipmapMask;
+ m_InitFlags = options;
+ m_ImageCount = imageCount;
+ UpdatePOTStatus();
+
+ m_glWidth = GetNextAllowedTextureSize(m_TexData.width, m_MipMap, format);
+ m_glHeight = GetNextAllowedTextureSize(m_TexData.height, m_MipMap, format);
+ SetTexelSize( 1.0f/m_glWidth, 1.0f/m_glHeight );
+}
+
+bool Texture2D::InitTexture (int width, int height, TextureFormat format, int options, int imageCount, intptr_t nativeTex)
+{
+ SET_ALLOC_OWNER(this);
+ if (width < 0 || width > 10000 || height < 0 || height > 10000)
+ {
+ ErrorStringObject ("Texture has out of range width / height", this);
+ return false;
+ }
+
+ if (!IsValidTextureFormat(format))
+ {
+ ErrorStringObject ("TextureFormat is invalid!", this);
+ return false;
+ }
+
+ int imageSize;
+ if( options & kMipmapMask)
+ imageSize = CalculateImageMipMapSize( width, height, format );
+ else
+ imageSize = CalculateImageSize( width, height, format );
+
+ unsigned int tlen = imageSize * imageCount;
+ // probably an multiplication overflow
+ if (imageSize != 0 && imageCount != tlen / imageSize)
+ return false;
+ // probably an addition overflow
+ if (tlen + GetBytesForOnePixel(format) < tlen)
+ return false;
+
+ bool allocData = ENABLE_TEXTUREID_MAP == 0 || nativeTex == 0;
+
+#if ENABLE_TEXTUREID_MAP
+ if(nativeTex)
+ TextureIdMap::UpdateTexture(m_TexID, GetGfxDevice().CreateExternalTextureFromNative(nativeTex));
+#endif
+
+ UInt8* buffer = allocData ? AllocateTextureData(imageSize * imageCount, format, true) : 0;
+ InitTextureInternal (width, height, format, imageSize, buffer, options, imageCount);
+
+ return true;
+}
+
+void Texture2D::RebuildMipMap ()
+{
+ TextureRepresentation& rep = m_TexData;
+
+ if( rep.data == NULL || !m_MipMap )
+ return;
+ if( IsAnyCompressedTextureFormat(rep.format) )
+ {
+ ErrorString ("Rebuilding mipmaps of compressed textures is not supported");
+ return;
+ }
+
+ for( int i=0;i<m_ImageCount;i++ )
+ CreateMipMap (rep.data + rep.imageSize * i, rep.width, rep.height, 1, rep.format);
+}
+
+
+#if UNITY_EDITOR
+void Texture2D::SetImage (const ImageReference& image, int flags)
+{
+ SET_ALLOC_OWNER(this);
+ InitTexture( image.GetWidth (), image.GetHeight (), image.GetFormat (), flags );
+
+ int bytesPerPixel = GetBytesFromTextureFormat (m_TexData.format);
+
+ ImageReference dst( m_TexData.width, m_TexData.height, bytesPerPixel * m_TexData.width, m_TexData.format, m_TexData.data );
+ if (image.GetImageData () != NULL)
+ dst.BlitImage (image);
+
+ RebuildMipMap ();
+
+ UploadTexture (true);
+ SetDirty ();
+}
+#endif
+
+
+
+bool Texture2D::GetImageReferenceInternal (ImageReference* image, int frame, int miplevel) const
+{
+ TextureRepresentation const& rep = m_TexData;
+
+ if( rep.data == NULL || IsAnyCompressedTextureFormat(rep.format) )
+ return false;
+
+ AssertIf (frame >= m_ImageCount);
+ AssertIf (miplevel >= CountDataMipmaps ());
+
+ UInt8* base = rep.data + frame * rep.imageSize;
+ base += CalculateMipMapOffset( rep.width, rep.height, rep.format, miplevel );
+ int width = std::max (rep.width >> miplevel, 1);
+ int height = std::max (rep.height >> miplevel, 1);
+
+ *image = ImageReference( width, height, GetRowBytesFromWidthAndFormat(width, rep.format), rep.format, base );
+ return true;
+}
+
+bool Texture2D::GetWriteImageReference (ImageReference* image, int frame, int miplevel)
+{
+ return GetImageReferenceInternal (image, frame, miplevel);
+}
+
+
+void Texture2D::ApplySettings()
+{
+ TextureDimension texdim = GetDimension();
+ m_TextureSettings.Apply( GetTextureID(), texdim, HasMipMap(), GetActiveTextureColorSpace());
+ if( m_UnscaledTextureUploaded )
+ m_TextureSettings.Apply( GetUnscaledTextureID(), texdim, HasMipMap(), GetActiveTextureColorSpace() );
+
+ NotifyMipBiasChanged();
+}
+
+void Texture2D::UpdatePOTStatus()
+{
+ m_PowerOfTwo = IsPowerOfTwo(m_TexData.width) && IsPowerOfTwo(m_TexData.height);
+
+ // force clamp if we will keep it npot
+ if(!m_PowerOfTwo && !m_MipMap && gGraphicsCaps.npot == kNPOTRestricted)
+ m_TextureSettings.m_WrapMode = kTexWrapClamp;
+}
+
+
+void Texture2D::UploadTexture (bool dontUseSubImage)
+{
+ if (m_TexData.data == NULL)
+ {
+ ErrorString("No Texture memory available to upload");
+ return;
+ }
+ if (m_TexData.width == 0 || m_TexData.height == 0)
+ {
+ return;
+ }
+
+ TextureRepresentation scaled = m_TexData;
+ TextureRepresentation padded = m_TexData;
+
+ InitTextureRepresentations(&scaled, &padded);
+
+ int mipCount = CountMipmaps();
+
+ int masterTextureLimit = Texture::GetMasterTextureLimit();
+ #if UNITY_EDITOR
+ if (m_IgnoreMasterTextureLimit)
+ masterTextureLimit = 0;
+ #endif
+
+ // Master texture limit must be clamped to mipCount-1 or otherwise GfxDevice won't upload anything
+ masterTextureLimit = min(mipCount-1, masterTextureLimit);
+
+ TextureUsageMode usageMode = GetUsageMode();
+
+ // upload regular texture
+ {
+ TextureRepresentation& rep = scaled;
+ UInt8* srcData = rep.data;
+ UInt32 uploadFlags = (dontUseSubImage || !m_TextureUploaded) ? GfxDevice::kUploadTextureDontUseSubImage : GfxDevice::kUploadTextureDefault;
+ if (m_InitFlags & kOSDrawingCompatible)
+ uploadFlags |= GfxDevice::kUploadTextureOSDrawingCompatible;
+ GetGfxDevice().UploadTexture2D( GetTextureID(), GetDimension(), srcData, rep.imageSize, rep.width, rep.height, rep.format, mipCount, uploadFlags, masterTextureLimit, usageMode, GetActiveTextureColorSpace() );
+ Texture::s_TextureIDMap.insert (std::make_pair(GetTextureID(),this));
+ m_TextureSettings.Apply( GetTextureID(), GetDimension(), m_MipMap, GetActiveTextureColorSpace());
+ m_TextureUploaded = true;
+ }
+
+ // upload unscaled one if NPOT and not supported
+ if( m_TexData.width != m_glWidth || m_TexData.height != m_glHeight )
+ {
+ TextureRepresentation& rep = padded;
+ UInt8* srcData = rep.data;
+
+ UInt32 uploadFlags = (dontUseSubImage || !m_UnscaledTextureUploaded) ? GfxDevice::kUploadTextureDontUseSubImage : GfxDevice::kUploadTextureDefault;
+ if (m_InitFlags & kOSDrawingCompatible)
+ uploadFlags |= GfxDevice::kUploadTextureOSDrawingCompatible;
+
+ m_UnscaledTextureUploaded = true; // must be before Upload in order for GetUnscaledGLTextureName() to return the right value
+ TextureID tid = GetUnscaledTextureID();
+ GetGfxDevice().UploadTexture2D( tid, GetDimension(), srcData, rep.imageSize, rep.width, rep.height, rep.format, mipCount, uploadFlags, masterTextureLimit, usageMode, GetActiveTextureColorSpace() );
+ Texture::s_TextureIDMap.insert (std::make_pair(tid,this));
+ m_TextureSettings.Apply( tid, GetDimension(), m_MipMap, GetActiveTextureColorSpace() );
+ }
+
+#if UNITY_XENON && !MASTER_BUILD
+ GetGfxDevice().SetTextureName( GetTextureID(), GetName() );
+#endif
+
+#if UNITY_EDITOR
+ DestroyTextureRepresentations(&scaled, &padded, false);
+#elif UNITY_WII
+ // On Wii texture data is being directly referenced from m_TexData.data, so don't delete it after uploading
+ DestroyTextureRepresentations(&scaled, &padded, false);
+#else
+ DestroyTextureRepresentations(&scaled, &padded, !m_IsReadable);
+ if(!m_IsReadable)
+ m_TexData.imageSize = 0;
+#endif
+}
+
+void Texture2D::DestroyTextureRepresentation( TextureRepresentation* rep )
+{
+ if( rep )
+ {
+ if( rep->data == m_TexData.data )
+ rep->data = NULL;
+
+ DeallocateTextureData (rep->data);
+ rep->data = NULL;
+ }
+}
+
+void Texture2D::DestroyTextureRepresentations( TextureRepresentation* scaled, TextureRepresentation* padded, bool freeSourceImage )
+{
+ DestroyTextureRepresentation(padded);
+ DestroyTextureRepresentation(scaled);
+
+ if( freeSourceImage )
+ {
+ DeallocateTextureData (m_TexData.data);
+ m_TexData.data = NULL;
+ }
+}
+
+void Texture2D::DeleteGfxTexture ()
+{
+ if (m_TextureUploaded)
+ {
+ ASSERT_RUNNING_ON_MAIN_THREAD
+ GetGfxDevice().DeleteTexture( GetTextureID() );
+ m_TextureUploaded = false;
+ }
+ if (m_UnscaledTextureUploaded)
+ {
+ ASSERT_RUNNING_ON_MAIN_THREAD
+ GetGfxDevice().DeleteTexture( GetUnscaledTextureID() );
+ m_UnscaledTextureUploaded = false;
+ }
+}
+
+
+void Texture2D::DestroyTexture ()
+{
+ DestroyTextureRepresentations (0,0,true);
+
+ DeleteGfxTexture ();
+}
+
+
+void Texture2D::UnloadFromGfxDevice(bool forceUnloadAll)
+{
+ if (m_IsUnreloadable && !forceUnloadAll)
+ return;
+ DeleteGfxTexture ();
+}
+
+void Texture2D::UploadToGfxDevice()
+{
+ if (m_IsUnreloadable)
+ return;
+
+ GfxDevice& device = GetGfxDevice();
+
+ // We need to load the texture data from disk. Awake FromLoad will be called during ReloadFromDisk.
+ bool needReloadFromDisk = (m_TexData.data == NULL && !m_IsReadable);
+ if (needReloadFromDisk)
+ {
+ TextureSettings settings = m_TextureSettings;
+
+ GetPersistentManager().ReloadFromDisk(this);
+
+ m_TextureSettings = settings;
+ ApplySettings();
+ }
+ // Just upload the texture data we already have in memory
+ else
+ {
+ UploadTexture (true);
+ }
+}
+
+
+#if UNITY_EDITOR
+struct TemporaryTextureSerializationRevert
+{
+ Texture2D* texture;
+ Texture2D::TextureRepresentation texData;
+ int imageCount;
+ bool isReadable;
+
+ TemporaryTextureSerializationRevert (Texture2D& tex2D, bool doRevert)
+ {
+ texture = NULL;
+
+ if (doRevert)
+ {
+ texture = &tex2D;
+
+ texData = texture->m_TexData;
+ imageCount = texture->m_ImageCount;
+ isReadable = texture->m_IsReadable;
+ }
+ }
+ ~TemporaryTextureSerializationRevert ()
+ {
+ if (texture != NULL)
+ {
+ texture->m_TexData = texData;
+ texture->m_ImageCount = imageCount;
+ texture->m_IsReadable = isReadable;
+ }
+ }
+};
+#endif
+
+template<class TransferFunction>
+void Texture2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+#if UNITY_EDITOR
+
+ // Store serialization state temporarliy and revert it when exiting function
+ TemporaryTextureSerializationRevert revert(*this, !transfer.IsReading ());
+
+ // When writing dynamic font textures we don't want to write any texture data to disk
+ if (m_EditorDontWriteTextureData && !transfer.IsReading ())
+ {
+ m_ImageCount = m_TexData.imageSize = m_TexData.height = m_TexData.width = 0;
+ m_TexData.data = NULL;
+ }
+
+ // readable flag gets adjusted by forced readable flag
+ if (transfer.IsBuildingTargetPlatform(kBuildAnyPlayerData))
+ m_IsReadable |= transfer.GetBuildUsage().forceTextureReadable;
+
+ TRANSFER_EDITOR_ONLY_HIDDEN(m_AlphaIsTransparency);
+ transfer.Align();
+#endif
+
+ // In case we're converting the texture data to another format, we also properly write out the format ID.
+ transfer.Transfer( m_TexData.width, "m_Width", kNotEditableMask );
+ transfer.Transfer( m_TexData.height, "m_Height", kNotEditableMask );
+ transfer.Transfer( m_TexData.imageSize, "m_CompleteImageSize", kNotEditableMask );
+ transfer.Transfer( m_TexData.format, "m_TextureFormat", kHideInEditorMask );
+ transfer.Transfer( m_MipMap, "m_MipMap", kNotEditableMask );
+
+ transfer.Transfer( m_IsReadable, "m_IsReadable");
+ transfer.Transfer( m_ReadAllowed, "m_ReadAllowed", kNotEditableMask );
+ transfer.Align();
+ transfer.Transfer( m_ImageCount, "m_ImageCount", kNotEditableMask );
+ transfer.Transfer( m_TextureDimension, "m_TextureDimension", kHideInEditorMask );
+ transfer.Transfer( m_TextureSettings, "m_TextureSettings");
+ transfer.Transfer( m_UsageMode, "m_LightmapFormat");
+ transfer.Transfer( m_ColorSpace, "m_ColorSpace");
+
+ unsigned imageSizeXImageCount = m_ImageCount * m_TexData.imageSize;
+
+ if (!transfer.IsWritingGameReleaseData ())
+ transfer.TransferTypeless (&imageSizeXImageCount, "image data", kHideInEditorMask);
+
+ if (transfer.IsReading ())
+ {
+ DestroyTexture ();
+ Assert(GetMemoryLabel().label != kMemTextureCacheId);
+ m_TexData.data = AllocateTextureData(imageSizeXImageCount, m_TexData.format);
+ m_glWidth = GetNextAllowedTextureSize(m_TexData.width, m_MipMap, TextureFormat(m_TexData.format));
+ m_glHeight = GetNextAllowedTextureSize(m_TexData.height, m_MipMap, TextureFormat(m_TexData.format));
+ SetTexelSize( 1.0f/m_glWidth, 1.0f/m_glHeight );
+ }
+
+ // write tex image
+ if (transfer.IsWriting())
+ {
+ const bool gpuBE = UNITY_EDITOR ? transfer.IsBuildingTargetPlatform(kBuildXBOX360) : false;
+
+ UInt8* convertedData = NULL;
+ if( transfer.ConvertEndianess() )
+ {
+ convertedData = AllocateTextureData(imageSizeXImageCount, m_TexData.format);
+ ConvertTextureEndianessWrite(m_TexData.format, m_TexData.data, convertedData, imageSizeXImageCount, gpuBE);
+ }
+
+ if (transfer.IsWritingGameReleaseData ())
+ transfer.TransferTypeless (&imageSizeXImageCount, "image data", kHideInEditorMask);
+ transfer.TransferTypelessData (imageSizeXImageCount, convertedData ? convertedData : m_TexData.data );
+
+ DeallocateTextureData(convertedData);
+ }
+ else
+ {
+ if( transfer.ConvertEndianess() )
+ {
+ transfer.TransferTypelessData (imageSizeXImageCount, m_TexData.data);
+ ConvertTextureEndianessRead (m_TexData.format, m_TexData.data, imageSizeXImageCount);
+ }
+ else
+ {
+ transfer.TransferTypelessData (imageSizeXImageCount, m_TexData.data);
+ }
+ }
+
+ AssertIf ( m_TexData.imageSize != 0 && m_TexData.data == NULL );
+
+ if( transfer.IsReading() )
+ UpdatePOTStatus();
+}
+
+void ConvertTextureEndianessWrite (int format, UInt8* src, UInt8* dst, int size, bool bBigEndianGPU)
+{
+ memcpy (dst, src, size);
+ if (format == kTexFormatARGBFloat)
+ SwapEndianArray (dst, 4, size / 4);
+ else if (format == kTexFormatRGB565 || format == kTexFormatARGB4444 || format == kTexFormatRGBA4444)
+ SwapEndianArray (dst, 2, size / 2);
+ else if (bBigEndianGPU && (format == kTexFormatDXT1 || format == kTexFormatDXT3 || format == kTexFormatDXT5))
+ SwapEndianArray (dst, 2, size / 2);
+
+}
+
+void ConvertTextureEndianessRead (int format, UInt8* dst, int size)
+{
+ if (format == kTexFormatARGBFloat)
+ SwapEndianArray (dst, 4, size / 4);
+ else if (format == kTexFormatRGB565 || format == kTexFormatARGB4444 || format == kTexFormatRGBA4444)
+ SwapEndianArray (dst, 2, size / 2);
+}
+
+
+void Texture2D::UpdateImageData ()
+{
+ RebuildMipMap ();
+ UploadTexture (false);
+ SetDirty ();
+}
+
+void Texture2D::UpdateImageDataDontTouchMipmap ()
+{
+ UploadTexture (false);
+ SetDirty ();
+}
+
+
+TextureID Texture2D::GetUnscaledTextureID() const
+{
+ return m_UnscaledTextureUploaded ? m_UnscaledTexID : m_TexID;
+}
+int Texture2D::GetDataWidth() const
+{
+ return m_TexData.width;
+}
+int Texture2D::GetDataHeight() const
+{
+ return m_TexData.height;
+}
+
+
+void Texture2D::InitTextureRepresentation( TextureRepresentation* rep, int format, const char* tag )
+{
+ Assert(rep);
+
+ rep->width = GetNextAllowedTextureSize(m_TexData.width, m_MipMap, TextureFormat(format));
+ rep->height = GetNextAllowedTextureSize(m_TexData.height, m_MipMap, TextureFormat(format));
+ rep->format = format;
+
+ if( m_MipMap )
+ rep->imageSize = CalculateImageMipMapSize( rep->width, rep->height, rep->format );
+ else
+ rep->imageSize = CalculateImageSize( rep->width, rep->height, rep->format );
+
+ rep->data = AllocateTextureData(rep->imageSize * m_ImageCount, rep->format);
+}
+
+void Texture2D::ExtractMipLevel( TextureRepresentation* dst, int frame, int mipLevel, bool checkCompression, bool scaleToSize )
+{
+ if (dst->width == 0 || dst->height == 0)
+ return;
+
+ UInt8* data = dst->data + frame * dst->imageSize + CalculateMipMapOffset(dst->width, dst->height, dst->format, mipLevel );
+
+
+ int width = std::max( dst->width >> mipLevel, 1 );
+ int height = std::max( dst->height >> mipLevel, 1 );
+
+ if( checkCompression && IsAnyCompressedTextureFormat(dst->format) )
+ {
+ ExtractCompressedImageInternal( data, width, height, frame );
+ }
+ else
+ {
+ ImageReference ref( width, height, width*GetBytesFromTextureFormat(dst->format), dst->format, data );
+ ExtractImageInternal( &ref, scaleToSize, frame );
+ }
+}
+
+
+void Texture2D::InitTextureRepresentations(Texture2D::TextureRepresentation* scaled, Texture2D::TextureRepresentation* padded)
+{
+ Assert(scaled);
+ Assert(padded);
+
+ if( m_TextureDimension == kTexDimDeprecated1D )
+ m_TextureDimension = kTexDim2D;
+
+ int multipleMask = GetTextureSizeAllowedMultiple(TextureFormat(m_TexData.format)) - 1;
+ bool isAllowedMultiple = (m_TexData.width & multipleMask) == 0 && (m_TexData.height & multipleMask) == 0;
+ if (isAllowedMultiple && (m_PowerOfTwo || IsNPOTTextureAllowed(m_MipMap)))
+ {
+ *scaled = *padded = m_TexData;
+ SetTexelSize( 1.0f / m_TexData.width, 1.0f / m_TexData.height );
+ return;
+ }
+
+ DebugAssertIf( m_PowerOfTwo );
+ DebugAssertIf( !m_TexData.data );
+
+ InitTextureRepresentation(scaled, IsAnyCompressedTextureFormat(m_TexData.format) ? kTexFormatRGBA32 : m_TexData.format, "tex.scaled");
+ InitTextureRepresentation(padded, m_TexData.format, "tex.padded");
+
+ int mipcount = CountMipmaps();
+ for( int frame = 0; frame < m_ImageCount; ++frame )
+ {
+ for( int mip = 0; mip < mipcount; ++mip )
+ {
+ ExtractMipLevel(scaled, frame, mip, false, true);
+ ExtractMipLevel(padded, frame, mip, true, false);
+ }
+ }
+}
+
+int Texture2D::GetRuntimeMemorySize() const
+{
+#if ENABLE_MEM_PROFILER
+ return Super::GetRuntimeMemorySize() +
+ GetMemoryProfiler()->GetRelatedIDMemorySize(m_TexID.m_ID) +
+ (m_UnscaledTextureUploaded?GetMemoryProfiler()->GetRelatedIDMemorySize(GetUnscaledTextureID().m_ID):0);
+#endif
+ return sizeof(Texture2D);
+}
+
+
+IMPLEMENT_CLASS (Texture2D)
+IMPLEMENT_OBJECT_SERIALIZE (Texture2D)
+INSTANTIATE_TEMPLATE_TRANSFER (Texture2D)
+
+bool Texture2D::CheckHasPixelData () const
+{
+ if (m_TexData.data != NULL)
+ return true;
+
+ if (!m_IsReadable)
+ {
+ ErrorString(Format("Texture '%s' is not readable, the texture memory can not be accessed from scripts. You can make the texture readable in the Texture Import Settings.", GetName()));
+ }
+ else
+ {
+ ErrorString(Format("Texture '%s' has no data", GetName()));
+ }
+ return false;
+}
+
+
+void Texture2D::SetPixel (int frame, int x, int y, const ColorRGBAf& c)
+{
+ if (!CheckHasPixelData())
+ return;
+
+ if (frame > m_ImageCount) {
+ ErrorString (Format ("SetPixel called on an undefined image (valid values are 0 - %d", m_ImageCount - 1));
+ return;
+ }
+
+ ImageReference image;
+ if (GetWriteImageReference(&image, frame, 0))
+ {
+ SetImagePixel (image, x, y, static_cast<TextureWrapMode>(GetSettings().m_WrapMode), c);
+ }
+ else
+ {
+ if( IsAnyCompressedTextureFormat(m_TexData.format) )
+ {
+ ErrorString(kUnsupportedSetPixelOpFormatMessage);
+ }
+ else
+ {
+ ErrorString("Unable to retrieve image reference");
+ }
+ }
+}
+
+void Texture2D::SetPixels( int x, int y, int width, int height, int pixelCount, const ColorRGBAf* pixels, int miplevel, int frame )
+{
+ if (width == 0 || height == 0)
+ return; // nothing to do
+
+ if (!CheckHasPixelData())
+ return;
+
+ int mipcount = CountMipmaps();
+ if (miplevel < 0 || miplevel >= mipcount)
+ {
+ ErrorString ("Invalid mip level");
+ return;
+ }
+
+ UInt8* data = m_TexData.data + frame * m_TexData.imageSize;
+ data += CalculateMipMapOffset (m_TexData.width, m_TexData.height, m_TexData.format, miplevel);
+ int dataWidth = std::max (m_TexData.width >> miplevel, 1);
+ int dataHeight = std::max (m_TexData.height >> miplevel, 1);
+
+ SetImagePixelBlock (data, dataWidth, dataHeight, m_TexData.format, x, y, width, height, pixelCount, pixels);
+}
+
+
+
+ColorRGBAf Texture2D::GetPixel (int frame,int x, int y) const
+{
+ if (!CheckHasPixelData())
+ return ColorRGBAf(1.0F,1.0F,1.0F,1.0F);
+
+ if (frame > m_ImageCount) {
+ ErrorString (Format ("GetPixel called on an undefined image (valid values are 0 - %d", m_ImageCount - 1));
+ return ColorRGBAf(1.0F,1.0F,1.0F,1.0F);
+ }
+
+ return GetImagePixel (m_TexData.data + frame*m_TexData.imageSize, m_TexData.width, m_TexData.height, m_TexData.format, static_cast<TextureWrapMode>(GetSettings().m_WrapMode), x, y);
+}
+
+ColorRGBAf Texture2D::GetPixelBilinear (int frame, float u, float v) const
+{
+ if (!CheckHasPixelData())
+ return ColorRGBAf(1.0F,1.0F,1.0F,1.0F);
+ if (frame > m_ImageCount) {
+ ErrorString (Format ("GetPixelBilinear called on an undefined image (valid values are 0 - %d", m_ImageCount - 1));
+ return ColorRGBAf(1.0F,1.0F,1.0F,1.0F);
+ }
+ return GetImagePixelBilinear (m_TexData.data + frame*m_TexData.imageSize, m_TexData.width, m_TexData.height, m_TexData.format, static_cast<TextureWrapMode>(GetSettings().m_WrapMode), u, v);
+}
+
+bool Texture2D::GetPixels( int x, int y, int width, int height, int miplevel, ColorRGBAf* colors, int frame ) const
+{
+ if (width == 0 || height == 0)
+ return true; // nothing to do
+
+ if (!CheckHasPixelData())
+ return false;
+
+ int mipcount = CountMipmaps();
+ if (miplevel < 0 || miplevel >= mipcount)
+ {
+ ErrorString ("Invalid mip level");
+ return false;
+ }
+
+ UInt8* data = m_TexData.data + frame * m_TexData.imageSize;
+ data += CalculateMipMapOffset (m_TexData.width, m_TexData.height, m_TexData.format, miplevel);
+ int dataWidth = std::max (m_TexData.width >> miplevel, 1);
+ int dataHeight = std::max (m_TexData.height >> miplevel, 1);
+
+ return GetImagePixelBlock (data, dataWidth, dataHeight, m_TexData.format, x, y, width, height, colors);
+}
+
+bool Texture2D::GetPixels32( int miplevel, ColorRGBA32* colors ) const
+{
+ AssertIf( miplevel < 0 || miplevel >= CountDataMipmaps() );
+
+ ImageReference texImage;
+ if( !GetImageReferenceInternal(&texImage, 0, miplevel) )
+ {
+ if (m_TexData.data != NULL && IsAnyCompressedTextureFormat(m_TexData.format))
+ {
+ UInt8* data = m_TexData.data + CalculateMipMapOffset( m_TexData.width, m_TexData.height, m_TexData.format, miplevel );
+ const int minSize = GetMinimumTextureMipSizeForFormat(m_TexData.format);
+ // the decompress size is at least minSize x minSize
+ int width = std::max (m_TexData.width >> miplevel, minSize);
+ int height = std::max (m_TexData.height >> miplevel, minSize);
+ // also, it should be a multiple of minSize
+ if (width % minSize == 0 && height % minSize == 0)
+ {
+ DecompressNativeTextureFormatWithMipLevel( m_TexData.format, width, height,miplevel, reinterpret_cast<const UInt32*>(data), width, height, reinterpret_cast<UInt32*>(colors) );
+ return true;
+ }
+ else
+ {
+ // make it an upper multiple of minSize
+ int decompWidth = (width + minSize - 1) / minSize * minSize;
+ int decompHeight = (height + minSize - 1) / minSize * minSize;
+ Image decompressed( decompWidth, decompHeight, kTexFormatRGBA32 );
+ DecompressNativeTextureFormatWithMipLevel( m_TexData.format, width, height,miplevel, reinterpret_cast<const UInt32*>(data), decompWidth, decompHeight, (UInt32*)decompressed.GetImageData() );
+ // clip it back to the original size
+ ImageReference clipped = decompressed.ClipImage( 0, 0, width, height );
+ // blit it over to the colors array
+ ImageReference resultImage( width, height, GetRowBytesFromWidthAndFormat(width,kTexFormatRGBA32), kTexFormatRGBA32, colors );
+ resultImage.BlitImage( clipped, ImageReference::BLIT_COPY );
+ return true;
+ }
+ }
+ AssertString( "Invalid texture" );
+ return false;
+ }
+
+ int texWidth = texImage.GetWidth();
+ int texHeight = texImage.GetHeight();
+
+ ImageReference resultImage( texWidth, texHeight, GetRowBytesFromWidthAndFormat(texWidth,kTexFormatRGBA32), kTexFormatRGBA32, colors );
+ resultImage.BlitImage( texImage, ImageReference::BLIT_COPY );
+
+ return true;
+}
+
+
+void Texture2D::SetPixels32( int miplevel, const ColorRGBA32* pixels, const int pixelCount )
+{
+ AssertIf( miplevel < 0 || miplevel >= CountMipmaps() );
+
+ ImageReference texImage;
+ if( !GetWriteImageReference(&texImage, 0, miplevel) )
+ {
+ AssertString( "Invalid texture format" );
+ return;
+ }
+
+ int texWidth = texImage.GetWidth();
+ int texHeight = texImage.GetHeight();
+
+ if(texWidth * texHeight != pixelCount)
+ {
+ AssertString( "SetPixels32 called with invalid number if pixels in the array" );
+ return;
+ }
+
+ ImageReference inputImage( texWidth, texHeight, GetRowBytesFromWidthAndFormat(texWidth,kTexFormatRGBA32), kTexFormatRGBA32, (void*)pixels );
+ texImage.BlitImage( inputImage, ImageReference::BLIT_COPY );
+}
+
+static const char* kUnsupportedColorPixelFormatMessage = "Unsupported image format - the texture needs to be ARGB32 or RGB24";
+
+static bool IsAllowedToReadPixels ()
+{
+#if UNITY_EDITOR
+ // from Player.h
+ bool IsInsidePlayerLoop ();
+ // Editor needs to read frame buffer outside player loop
+ if( !IsInsidePlayerLoop() )
+ return true;
+#elif WEBPLUG
+ // Allow old web content for compatibility
+ if( !IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1) )
+ return true;
+#endif
+ return GetGfxDevice().IsInsideFrame() || RenderTexture::GetActive();
+}
+
+// TODO: Check dimensions, make everything work.
+void Texture2D::ReadPixels (int frame, int left, int bottom, int width, int height, int destX, int destY, bool flipped, bool computeMipMap)
+{
+ //Prevent out of bounds reads (case 562067)
+ if (destX < 0 || destY < 0 || destX >= GetDataWidth() || destY >= GetDataHeight())
+ {
+ ErrorString("Trying to read pixels out of bounds");
+ return;
+ }
+ if (width < 0 || height < 0)
+ {
+ ErrorString("Negative read pixels rectangle width|height");
+ return;
+ }
+
+ if( !IsAllowedToReadPixels() )
+ {
+ ErrorString ("ReadPixels was called to read pixels from system frame buffer, while not inside drawing frame.");
+ // Our tests rely on calling ReadImage() during Update() so we allow it anyway...
+ }
+
+ if( frame >= m_ImageCount )
+ {
+ ErrorString (Format ("ReadPixels called on undefined image %d (valid values are 0 - %d", frame, m_ImageCount - 1));
+ return;
+ }
+
+ GfxDeviceRenderer renderer = GetGfxDevice().GetRenderer();
+ bool isGLES = (renderer == kGfxRendererOpenGLES20Mobile || renderer == kGfxRendererOpenGLES30);
+ int texFormat = GetTextureFormat();
+ bool validFormat = texFormat == kTexFormatARGB32 ||
+ texFormat == kTexFormatRGB24 ||
+ texFormat == (kTexFormatRGBA32 && isGLES) ||
+ texFormat == (kTexFormatRGB565 && isGLES);
+ if( !validFormat )
+ {
+ ErrorString(kUnsupportedColorPixelFormatMessage);
+ return;
+ }
+
+ ImageReference image;
+ if( !GetWriteImageReference(&image, frame, 0) )
+ {
+ ErrorString("Unable to retrieve image reference");
+ return;
+ }
+
+ if (RenderTexture::GetActive() == NULL)
+ {
+ Rectf rc = GetRenderManager().GetWindowRect();
+ left += rc.x;
+ bottom += rc.y;
+ }
+
+ if (left < 0) {
+ width += left;
+ left = 0;
+ }
+ if (bottom < 0) {
+ height += bottom;
+ bottom = 0;
+ }
+ if (destX + width > GetDataWidth())
+ width = GetDataWidth() - destX;
+ if (destY + height > GetDataHeight())
+ height = GetDataHeight() - destY;
+
+ GetGfxDevice().ReadbackImage( image, left, bottom, width, height, destX, destY );
+
+ if (flipped)
+ {
+ ImageReference subImage = image.ClipImage (destX, destY, width, height);
+ subImage.FlipImageY();
+ }
+
+ if( computeMipMap && m_MipMap )
+ RebuildMipMap();
+}
+
+
+#if ENABLE_PNG_JPG
+bool Texture2D::EncodeToPNG( dynamic_array<UInt8>& outBuffer )
+{
+ if( IsAnyCompressedTextureFormat(GetTextureFormat()) )
+ {
+ ErrorString(kUnsupportedSetPixelOpFormatMessage);
+ return false;
+ }
+ ImageReference image;
+ if( !GetWriteImageReference( &image, 0, 0 ) )
+ {
+ ErrorString( "Unable to retrieve image reference" );
+ return false;
+ }
+ if( !ConvertImageToPNGBuffer( image, outBuffer ) )
+ {
+ ErrorString( "Failed to encode to PNG" );
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+
+
+bool Texture2D::ResizeWithFormat (int width, int height, TextureFormat format, int flags)
+{
+ if (!m_IsReadable)
+ {
+ ErrorString ("Texture is not readable.");
+ return false;
+ }
+
+ if( IsAnyCompressedTextureFormat(format) )
+ {
+ ErrorStringObject ("Can't resize to a compressed texture format", this);
+ return false;
+ }
+
+ return InitTexture(width, height, format, flags);
+}
+
+
+void Texture2D::Compress (bool dither)
+{
+ if (!m_IsReadable)
+ {
+ ErrorString(Format("Texture '%s' is not readable, Compress will not work. You can make the texture readable in the Texture Import Settings.", GetName()));
+ return;
+ }
+
+ SET_ALLOC_OWNER(this);
+ // If hardware does not support DXT, then nothing to do.
+ if( !gGraphicsCaps.hasS3TCCompression )
+ return;
+
+ #if !GFX_SUPPORTS_DXT_COMPRESSION
+ Assert(!"GraphicsCaps.hasS3TCCompression is invalid");
+ return;
+ #else
+
+ TextureFormat format = GetTextureFormat();
+ // If already compressed, then nothing to do.
+ if( IsAnyCompressedTextureFormat(format) )
+ return;
+
+ // Copy out old data into RGBA32 format.
+ bool mipMaps = HasMipMap();
+ int width = GetDataWidth();
+ int height = GetDataHeight();
+ int rgbaByteSize = mipMaps ?
+ CalculateImageMipMapSize( width, height, kTexFormatRGBA32 ) :
+ CalculateImageSize( width, height, kTexFormatRGBA32 );
+ UInt8* rgbaData = new UInt8[rgbaByteSize];
+ int mipCount = CountDataMipmaps();
+ for( int mip = 0; mip < mipCount; ++mip )
+ {
+ UInt8* rgbaMipData = rgbaData + CalculateMipMapOffset(width, height, kTexFormatRGBA32, mip);
+ int mipWidth = std::max( width >> mip, 1 );
+ int mipHeight = std::max( height >> mip, 1 );
+ ImageReference mipDst( mipWidth, mipHeight, mipWidth * 4, kTexFormatRGBA32, rgbaMipData );
+ ExtractImageInternal( &mipDst, false, 0 );
+ }
+
+ // Reformat texture into DXT format
+ bool hasAlpha = HasAlphaTextureFormat(format);
+ TextureFormat compressedFormat = hasAlpha ? kTexFormatDXT5 : kTexFormatDXT1;
+ InitTexture( width, height, compressedFormat, mipMaps ? kMipmapMask : kNoMipmap );
+
+ // DXT compress RGBA data
+ for( int mip = 0; mip < mipCount; ++mip )
+ {
+ const UInt8* srcMipData = rgbaData + CalculateMipMapOffset(width, height, kTexFormatRGBA32, mip);
+ UInt8* dstMipData = GetRawImageData() + CalculateMipMapOffset(width, height, compressedFormat, mip);
+ int mipWidth = std::max( width >> mip, 1 );
+ int mipHeight = std::max( height >> mip, 1 );
+ FastCompressImage( mipWidth, mipHeight, srcMipData, dstMipData, hasAlpha, dither );
+ }
+
+ delete[] rgbaData;
+
+ UpdateImageDataDontTouchMipmap();
+ #endif //GFX_SUPPORTS_DXT_COMPRESSION
+}
+
+#if UNITY_EDITOR
+void Texture2D::WarnInstantiateDisallowed ()
+{
+ if (!m_IsReadable)
+ {
+ ErrorStringObject(Format("Instantiating a non-readable '%s' texture is not allowed! Please mark the texture readable in the inspector or don't instantiate it.", GetName()), this);
+ }
+}
+
+bool Texture2D::IgnoreMasterTextureLimit () const
+{
+ return m_IgnoreMasterTextureLimit;
+}
+
+void Texture2D::SetIgnoreMasterTextureLimit (bool ignore)
+{
+ m_IgnoreMasterTextureLimit = ignore;
+}
+
+bool Texture2D::GetAlphaIsTransparency() const
+{
+ return m_AlphaIsTransparency;
+}
+
+void Texture2D::SetAlphaIsTransparency(bool is)
+{
+ m_AlphaIsTransparency = is;
+}
+#endif // UNITY_EDITOR
+
+
+void Texture2D::Apply(bool updateMipmaps, bool makeNoLongerReadable)
+{
+ if( makeNoLongerReadable )
+ {
+ SetIsReadable(false);
+ SetIsUnreloadable(true);
+ }
+
+ if( IsAnyCompressedTextureFormat(GetTextureFormat()) )
+ updateMipmaps = false;
+
+ if (updateMipmaps) UpdateImageData();
+ else UpdateImageDataDontTouchMipmap();
+}
+
+
diff --git a/Runtime/Graphics/Texture2D.h b/Runtime/Graphics/Texture2D.h
new file mode 100644
index 0000000..e64a035
--- /dev/null
+++ b/Runtime/Graphics/Texture2D.h
@@ -0,0 +1,208 @@
+#pragma once
+
+#include "Texture.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Modules/ExportModules.h"
+
+class ColorRGBAf;
+class ColorRGBA32;
+
+
+class EXPORT_COREMODULE Texture2D: public Texture
+{
+public:
+ // Should be called by MAIN thread in order to upload texture data immediately after texture asset was loaded
+ // On some platforms helps to avoid memory peak during level load
+ static void IntegrateLoadedImmediately();
+
+protected:
+
+ // See comments at Texture2D.cpp
+ struct TextureRepresentation {
+ TextureRepresentation();
+
+ UInt8* data; // data for all image frames
+ int width;
+ int height;
+ int format;
+ int imageSize; // size in bytes of one image frames, including mip levels
+ };
+ TextureRepresentation m_TexData; // original data
+ TextureID m_UnscaledTexID;
+
+ static bool s_ScreenReadAllowed;
+
+protected:
+ int m_ImageCount;
+ int m_TextureDimension;
+
+ int m_glWidth;
+ int m_glHeight;
+
+ int m_InitFlags;
+ bool m_MipMap;
+ bool m_PowerOfTwo;
+ bool m_TextureUploaded;
+ bool m_UnscaledTextureUploaded;
+ bool m_IsReadable;
+ bool m_ReadAllowed;
+ bool m_IsUnreloadable;
+
+ #if UNITY_EDITOR
+ bool m_EditorDontWriteTextureData;
+ bool m_IgnoreMasterTextureLimit;
+ bool m_AlphaIsTransparency;
+ #endif
+
+protected:
+ void DestroyTexture ();
+ void DeleteGfxTexture ();
+
+ virtual void UploadTexture (bool dontUseSubImage);
+ virtual void UnloadFromGfxDevice(bool forceUnloadAll);
+ virtual void UploadToGfxDevice();
+
+
+private:
+
+ void DestroyTextureRepresentation( TextureRepresentation* rep );
+ void DestroyTextureRepresentations( TextureRepresentation* scaled, TextureRepresentation* padded, bool freeSourceImage=true );
+
+ void InitTextureRepresentation( TextureRepresentation* rep, int format, const char* tag );
+ void InitTextureRepresentations( TextureRepresentation* scaled, TextureRepresentation* padded );
+
+ void UpdatePOTStatus();
+
+
+ void ExtractMipLevel( TextureRepresentation* dst, int frame, int mipLevel, bool checkCompression, bool scaleToSize );
+ bool ExtractImageInternal( ImageReference* image, bool scaleToSize, int imageIndex ) const;
+ void ExtractCompressedImageInternal( UInt8* dst, int dstWidth, int dstHeight, int imageIndex ) const;
+
+ bool GetImageReferenceInternal (ImageReference* image, int frame, int miplevel) const;
+
+ bool CheckHasPixelData () const;
+
+ MemLabelId GetTextureDataMemoryLabel() const { return ( GetMemoryLabel().label == kMemTextureCacheId ? GetMemoryLabel() : MemLabelId(kMemTextureId, GetMemoryLabel().GetRootHeader()) ); }
+public:
+ REGISTER_DERIVED_CLASS (Texture2D, Texture)
+ DECLARE_OBJECT_SERIALIZE (Texture2D)
+
+ Texture2D (MemLabelId label, ObjectCreationMode mode);
+ // ~Texture2D (); declared-by-macro
+
+ virtual bool MainThreadCleanup ();
+
+ virtual void Reset ();
+ virtual void AwakeFromLoadThreaded ();
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ virtual TextureDimension GetDimension () const { return static_cast<TextureDimension>(m_TextureDimension); }
+
+ virtual int GetGLWidth() const { return m_glWidth; }
+ virtual int GetGLHeight() const { return m_glHeight; }
+ virtual void ApplySettings();
+
+ virtual int GetDataWidth() const;
+ virtual int GetDataHeight() const;
+
+ virtual int GetRuntimeMemorySize() const;
+ #if ENABLE_PROFILER || UNITY_EDITOR
+ virtual int GetStorageMemorySize() const { return m_TexData.imageSize*m_ImageCount; }
+ #endif
+
+ virtual TextureID GetUnscaledTextureID() const;
+ int CountDataMipmaps () const;
+ bool IsNonPowerOfTwo() const { return !m_PowerOfTwo; }
+
+ enum {
+ kNoMipmap = 0,
+ kMipmapMask = 1 << 0,
+ kThreadedInitialize = 1 << 2,
+ kOSDrawingCompatible = 1 << 3,
+ };
+ virtual bool InitTexture (int width, int height, TextureFormat format, int flags=kMipmapMask, int imageCount=1, intptr_t nativeTex=0);
+
+ void InitTextureInternal (int width, int height, TextureFormat format, int imageSize, UInt8* buffer, int options, int imageCount);
+ UInt8* AllocateTextureData (int imageSize, TextureFormat format, bool initMemory = false);
+ void DeallocateTextureData (UInt8* memory);
+
+ virtual bool HasMipMap () const;
+
+ void SetIsReadable (bool readable) { m_IsReadable = readable; }
+ bool GetIsReadable () const { return m_IsReadable; }
+ void SetIsUnreloadable (bool value) { m_IsUnreloadable = value; }
+ bool GetIsUploaded () const { return m_TextureUploaded; }
+
+ #if UNITY_EDITOR
+ void SetEditorDontWriteTextureData (bool value) { m_EditorDontWriteTextureData = value; }
+ // directly load from an image, used in editor for gizmos/icons
+ void SetImage (const ImageReference& image, int flags = kMipmapMask);
+ virtual void WarnInstantiateDisallowed ();
+ virtual bool IgnoreMasterTextureLimit () const;
+ void SetIgnoreMasterTextureLimit (bool ignore);
+
+ bool GetAlphaIsTransparency() const;
+ void SetAlphaIsTransparency(bool is);
+
+ virtual TextureFormat GetEditorUITextureFormat () const { return GetTextureFormat(); }
+
+ #endif
+
+ virtual void UpdateImageData ();
+ virtual void UpdateImageDataDontTouchMipmap ();
+
+ // Returns the original (may be NPOT) data
+ UInt8 *GetRawImageData (int frame = 0) { return m_TexData.data + frame * m_TexData.imageSize; }
+ int GetRawImageDataSize () const { return m_TexData.imageSize; }
+
+ bool GetWriteImageReference (ImageReference* image, int frame, int miplevel);
+
+ int GetImageCount () const { return m_ImageCount; }
+
+ virtual bool ExtractImage (ImageReference* image, int imageIndex = 0) const;
+
+ bool ResizeWithFormat (int width, int height, TextureFormat format, int flags);
+ bool Resize (int width, int height) { return ResizeWithFormat (width, height, GetTextureFormat(), HasMipMap() ? kMipmapMask : kNoMipmap); }
+
+ int GetTextureFormat () const { return m_TexData.format; }
+
+ void Compress (bool dither);
+
+ virtual void RebuildMipMap ();
+
+ virtual int CountMipmaps () const;
+
+ ColorRGBAf GetPixelBilinear (int image, float u, float v) const;
+ ColorRGBAf GetPixel (int image, int x, int y) const;
+ void SetPixel (int image, int x, int y, const ColorRGBAf& c);
+
+ // Read pixels. Set reversed when reading into a cubemap
+ void ReadPixels (int frame, int left, int bottom, int width, int height, int destX, int destY, bool reversed, bool computeMipMap);
+
+ bool GetPixels (int x, int y, int width, int height, int mipLevel, ColorRGBAf* data, int frame = 0) const;
+ void SetPixels( int x, int y, int width, int height, int pixelCount, const ColorRGBAf* pixels, int miplevel, int frame = 0 );
+
+
+ // always whole mip level, into/from 32 bit RGBA colors
+ // GetPixels32 also supports getting pixels from DXT textures.
+ // For DXT textures the output width/height must have minimum of 4.
+ bool GetPixels32( int mipLevel, ColorRGBA32* data ) const;
+ void SetPixels32( int mipLevel, const ColorRGBA32* pixels, const int pixelCount );
+
+ // Encodes to PNG bytes
+ bool EncodeToPNG( dynamic_array<UInt8>& outBuffer );
+
+ // Is reading the data from this texture allowed by webplayer security
+ void SetReadAllowed (bool allowed) { m_ReadAllowed = allowed; if (!allowed) s_ScreenReadAllowed=false;}
+ bool GetReadAllowed () const { return m_ReadAllowed; }
+
+ void Apply(bool updateMipmaps, bool makeNoLongerReadable);
+
+ static bool GetScreenReadAllowed () { return s_ScreenReadAllowed; }
+
+
+ friend struct TemporaryTextureSerializationRevert;
+};
+
+void ConvertTextureEndianessWrite (int format, UInt8* src, UInt8* dst, int size, bool bBigEndianGPU);
+void ConvertTextureEndianessRead (int format, UInt8* src, int size);
diff --git a/Runtime/Graphics/Texture3D.cpp b/Runtime/Graphics/Texture3D.cpp
new file mode 100644
index 0000000..0c2f6c3
--- /dev/null
+++ b/Runtime/Graphics/Texture3D.cpp
@@ -0,0 +1,322 @@
+#include "UnityPrefix.h"
+#include "Texture3D.h"
+#include "Image.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+
+static UInt32 CalculateMipOffset3D (int mipCount, int width, int height, int depth, TextureFormat format)
+{
+ UInt32 imageSize = 0;
+ const int bpp = GetBytesFromTextureFormat(format);
+ for (int mip = 0; mip < mipCount; ++mip)
+ {
+ int mipWidth = std::max(width >> mip,1);
+ int mipHeight = std::max(height >> mip,1);
+ int mipDepth = std::max(depth >> mip,1);
+ imageSize += bpp * mipWidth * mipHeight * mipDepth;
+ }
+ return imageSize;
+}
+
+
+Texture3D::Texture3D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_Width(0)
+, m_Height(0)
+, m_Depth(0)
+, m_Format(kTexFormatARGB32)
+, m_Data(NULL)
+, m_DataSize(0)
+, m_MipMap(false)
+, m_TextureUploaded(false)
+{
+}
+
+Texture3D::~Texture3D ()
+{
+ DestroyTexture();
+}
+
+bool Texture3D::MainThreadCleanup ()
+{
+ DeleteGfxTexture();
+ return Super::MainThreadCleanup();
+}
+
+
+
+void Texture3D::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ if ((awakeMode == kDefaultAwakeFromLoad || awakeMode == kInstantiateOrCreateFromCodeAwakeFromLoad) && m_Data == NULL)
+ return;
+
+ UploadTexture (false);
+}
+
+
+void Texture3D::Reset ()
+{
+ Super::Reset();
+ m_TextureSettings.Reset();
+}
+
+
+bool Texture3D::InitTexture (int width, int height, int depth, TextureFormat format, bool mipMaps)
+{
+ if (!IsPowerOfTwo(width) || !IsPowerOfTwo(height) || !IsPowerOfTwo(depth))
+ {
+ ErrorStringObject ("Texture3D has non-power of two size", this);
+ return false;
+ }
+ if (format >= kTexFormatDXT1 || !IsValidTextureFormat(format))
+ {
+ ErrorStringObject ("Invalid texture format for Texture3D", this);
+ return false;
+ }
+
+ const int kMax3DTextureSize = 1024;
+ if (width < 0 || width > kMax3DTextureSize || height < 0 || height > kMax3DTextureSize || depth < 0 || depth > kMax3DTextureSize)
+ {
+ ErrorStringObject ("Texture3D has out of range width / height / depth", this);
+ return false;
+ }
+
+ m_Width = width;
+ m_Height = height;
+ m_Depth = depth;
+ m_Format = format;
+ m_MipMap = mipMaps;
+
+ const int mipCount = CountMipmaps();
+ UInt32 imageSize = CalculateMipOffset3D (mipCount, width, height, depth, format);
+
+ UInt8* buffer = AllocateTextureData(imageSize, m_Format, true);
+ if (!buffer)
+ return false;
+
+ // Cleanup any old memory
+ DestroyTexture ();
+
+ m_Data = buffer;
+ m_DataSize = imageSize;
+
+ SetTexelSize (1.0f/m_Width, 1.0f/m_Height);
+
+ SetDirty ();
+ return true;
+}
+
+UInt8* Texture3D::AllocateTextureData (int imageSize, TextureFormat format, bool initMemory)
+{
+ // Allocate one more pixel because software bi-linear filtering might require it.
+ int allocSize = imageSize + GetBytesFromTextureFormat(format);
+ void* buffer = UNITY_MALLOC_ALIGNED (kMemTexture, allocSize, 32);
+
+ // In the past the memory manager cleared textures created from script to 0xcd.
+ // Let's keep doing that even though a color like white makes more sense (case 564961).
+ if (initMemory && buffer)
+ memset(buffer, 0xcd, allocSize);
+
+ return static_cast<UInt8*>(buffer);
+}
+
+void Texture3D::DeleteGfxTexture()
+{
+ if (m_TextureUploaded)
+ {
+ ASSERT_RUNNING_ON_MAIN_THREAD
+ GetGfxDevice().DeleteTexture(GetTextureID());
+ m_TextureUploaded = false;
+ }
+}
+
+
+void Texture3D::DestroyTexture()
+{
+ UNITY_FREE (kMemTexture, m_Data);
+ m_Data = NULL;
+ m_DataSize = 0;
+
+ DeleteGfxTexture();
+}
+
+void Texture3D::UploadTexture (bool dontUseSubImage)
+{
+ if (!gGraphicsCaps.has3DTexture)
+ return;
+
+ const BuildSettings* buildSettings = GetBuildSettingsPtr();
+ if (buildSettings && !buildSettings->hasPROVersion)
+ return;
+
+ Assert (GetImageDataPointer() != NULL);
+
+ UInt32 uploadFlags = (dontUseSubImage || !m_TextureUploaded) ? GfxDevice::kUploadTextureDontUseSubImage : GfxDevice::kUploadTextureDefault;
+ GetGfxDevice().UploadTexture3D (GetTextureID(), GetImageDataPointer(), GetImageDataSize(), m_Width, m_Height, m_Depth, m_Format, CountMipmaps(), uploadFlags);
+ Texture::s_TextureIDMap.insert (std::make_pair(GetTextureID(),this));
+ ApplySettings();
+#if UNITY_XENON && !MASTER_BUILD
+ GetGfxDevice().SetTextureName( GetTextureID(), GetName() );
+#endif
+ m_TextureUploaded = true;
+}
+
+void Texture3D::UpdateImageData (bool rebuildMipMaps)
+{
+ if (rebuildMipMaps)
+ RebuildMipMap ();
+ UploadTexture (false);
+ SetDirty ();
+}
+
+
+void Texture3D::RebuildMipMap ()
+{
+ if (!m_MipMap || m_Data == NULL)
+ return;
+ if (IsAnyCompressedTextureFormat(m_Format))
+ {
+ ErrorStringObject ("Rebuilding mipmaps of compressed textures is not supported", this);
+ return;
+ }
+
+ CreateMipMap (m_Data, m_Width, m_Height, m_Depth, m_Format);
+}
+
+
+bool Texture3D::ExtractImage (ImageReference* image, int imageIndex) const
+{
+ if (!m_Data)
+ return false;
+
+ ImageReference source (m_Width, m_Height, m_Width * GetBytesFromTextureFormat(m_Format), m_Format, m_Data);
+ image->BlitImage (source, ImageReference::BLIT_BILINEAR_SCALE);
+ return true;
+}
+
+
+
+int Texture3D::CountMipmaps () const
+{
+ if (m_MipMap)
+ return CalculateMipMapCount3D (m_Width, m_Height, m_Depth);
+ else
+ return 1;
+}
+
+
+void Texture3D::UnloadFromGfxDevice(bool forceUnloadAll)
+{
+ DeleteGfxTexture ();
+}
+
+void Texture3D::UploadToGfxDevice()
+{
+ //@TODO: once texture3D gets the option to unload system memory copy,
+ // then we need to do needReloadFromDisk dance similar to Texture2D.
+
+ UploadTexture (true);
+}
+
+
+void Texture3D::SetPixels (int pixelCount, const ColorRGBAf* pixels, int miplevel)
+{
+ if (pixelCount == 0 || pixels == NULL)
+ return;
+
+ if (!m_Data)
+ {
+ ErrorStringObject("Texture has no data", this);
+ return;
+ }
+
+ int mipcount = CountMipmaps();
+ if (miplevel < 0 || miplevel >= mipcount)
+ {
+ ErrorStringObject ("Invalid mip level", this);
+ return;
+ }
+
+
+ UInt8* data = m_Data + CalculateMipOffset3D (miplevel, m_Width, m_Height, m_Depth, m_Format);
+ const int mipWidth = std::max(m_Width >> miplevel,1);
+ const int mipHeight = std::max(m_Height >> miplevel,1);
+ const int mipDepth = std::max(m_Depth >> miplevel,1);
+
+ SetImagePixelBlock (data, mipWidth, mipHeight*mipDepth, m_Format, 0, 0, mipWidth, mipHeight*mipDepth, pixelCount, pixels);
+}
+
+
+bool Texture3D::GetPixels (ColorRGBAf* dest, int miplevel) const
+{
+ if (!dest)
+ return true; // nothing to do
+
+ if (!m_Data)
+ {
+ ErrorStringObject("Texture has no data", this);
+ return false;
+ }
+
+ int mipcount = CountMipmaps();
+ if (miplevel < 0 || miplevel >= mipcount)
+ {
+ ErrorStringObject ("Invalid mip level", this);
+ return false;
+ }
+
+ UInt8* data = m_Data + CalculateMipOffset3D (miplevel, m_Width, m_Height, m_Depth, m_Format);
+ const int mipWidth = std::max(m_Width >> miplevel,1);
+ const int mipHeight = std::max(m_Height >> miplevel,1);
+ const int mipDepth = std::max(m_Depth >> miplevel,1);
+
+ return GetImagePixelBlock (data, mipWidth, mipHeight*mipDepth, m_Format, 0, 0, mipWidth, mipHeight*mipDepth, dest);
+}
+
+
+
+template<class TransferFunction>
+void Texture3D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ transfer.Transfer (m_Width, "m_Width", kNotEditableMask);
+ transfer.Transfer (m_Height, "m_Height", kNotEditableMask);
+ transfer.Transfer (m_Depth, "m_Depth", kNotEditableMask);
+ transfer.Transfer (m_Format, "m_Format", kNotEditableMask);
+ transfer.Transfer (m_MipMap, "m_MipMap", kNotEditableMask);
+ transfer.Align();
+ transfer.Transfer (m_DataSize, "m_DataSize", kNotEditableMask);
+ transfer.Transfer (m_TextureSettings, "m_TextureSettings");
+
+ unsigned dataSize = m_DataSize;
+ transfer.TransferTypeless (&dataSize, "image data", kHideInEditorMask);
+
+ if (transfer.IsReading ())
+ {
+ DestroyTexture ();
+ Assert(GetMemoryLabel().label != kMemTextureCacheId);
+
+ m_DataSize = dataSize;
+ m_Data = AllocateTextureData(dataSize, m_Format, false);
+
+ SetTexelSize (1.0f/m_Width, 1.0f/m_Height);
+ }
+
+ // texture data
+ //@TODO: format / endianess conversions
+ transfer.TransferTypelessData (dataSize, m_Data);
+
+ Assert (m_DataSize == 0 || m_Data != NULL);
+}
+
+
+
+IMPLEMENT_CLASS (Texture3D)
+IMPLEMENT_OBJECT_SERIALIZE (Texture3D)
diff --git a/Runtime/Graphics/Texture3D.h b/Runtime/Graphics/Texture3D.h
new file mode 100644
index 0000000..8618e9b
--- /dev/null
+++ b/Runtime/Graphics/Texture3D.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include "Texture.h"
+
+
+class Texture3D : public Texture
+{
+public:
+ REGISTER_DERIVED_CLASS (Texture3D, Texture)
+ DECLARE_OBJECT_SERIALIZE (Texture3D)
+
+ Texture3D (MemLabelId label, ObjectCreationMode mode);
+
+ virtual bool MainThreadCleanup ();
+
+
+ virtual void Reset ();
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ void UploadTexture (bool dontUseSubImage);
+ bool InitTexture (int width, int height, int depth, TextureFormat format, bool mipMaps);
+ void UpdateImageData (bool rebuildMipMaps);
+
+ UInt8* GetImageDataPointer() { return m_Data; }
+ UInt32 GetImageDataSize() const { return m_DataSize; }
+ int GetDepth() const { return m_Depth; }
+ TextureFormat GetTextureFormat() const { return m_Format; }
+
+ bool GetPixels (ColorRGBAf* dest, int miplevel) const;
+ void SetPixels (int pixelCount, const ColorRGBAf* pixels, int miplevel);
+
+ // Texture
+ virtual TextureDimension GetDimension () const { return kTexDim3D; }
+ virtual bool ExtractImage (ImageReference* image, int imageIndex = 0) const;
+ virtual int GetDataWidth() const { return m_Width; }
+ virtual int GetDataHeight() const { return m_Height; }
+ virtual bool HasMipMap () const { return m_MipMap; }
+ virtual int CountMipmaps() const;
+ #if ENABLE_PROFILER
+ virtual int GetStorageMemorySize() const { return m_DataSize; }
+ #endif
+ #if UNITY_EDITOR
+ virtual TextureFormat GetEditorUITextureFormat () const { return GetTextureFormat(); }
+ #endif
+ virtual int GetRuntimeMemorySize() const { return m_DataSize; }
+
+protected:
+ // Texture
+ virtual void UnloadFromGfxDevice(bool forceUnloadAll);
+ virtual void UploadToGfxDevice();
+
+private:
+ UInt8* AllocateTextureData(int imageSize, TextureFormat format, bool initMemory);
+ void DestroyTexture();
+ void RebuildMipMap ();
+ void DeleteGfxTexture();
+
+private:
+ int m_Width;
+ int m_Height;
+ int m_Depth;
+ TextureFormat m_Format;
+ UInt8* m_Data;
+ UInt32 m_DataSize;
+ bool m_MipMap;
+ bool m_TextureUploaded;
+};
diff --git a/Runtime/Graphics/TextureFormat.cpp b/Runtime/Graphics/TextureFormat.cpp
new file mode 100644
index 0000000..27d60fa
--- /dev/null
+++ b/Runtime/Graphics/TextureFormat.cpp
@@ -0,0 +1,248 @@
+#include "UnityPrefix.h"
+#include "TextureFormat.h"
+#include "Runtime/Utilities/LogAssert.h"
+
+// NOTE: match indices in kTexFormat* enum!
+
+const static int kTextureByteTable[kTexFormatTotalCount] =
+{
+ 0,
+ 1, // kTexFormatAlpha8
+ 2, // kTexFormatARGB4444
+ 3, // kTexFormatRGB24
+ 4, // kTexFormatRGBA32
+ 4, // kTexFormatARGB32
+ 16, // kTexFormatARGBFloat
+ 2, // kTexFormatRGB565
+ 3, // kTexFormatBGR24
+ 2, // kTexFormatAlphaLum16
+ 0, // kTexFormatDXT1 (Depends on width, height, depth)
+ 0, // kTexFormatDXT3 (Depends on width, height, depth)
+ 0, // kTexFormatDXT5 (Depends on width, height, depth)
+ 2, // kTexFormatRGBA4444
+};
+
+UInt32 GetBytesFromTextureFormat (TextureFormat inFormat)
+{
+ AssertMsg (inFormat < kTexFormatDXT1 || inFormat == kTexFormatBGRA32 || inFormat == kTexFormatRGBA4444, "Invalid texture format: %d", (int)inFormat);
+ return (inFormat == kTexFormatBGRA32) ? 4 : kTextureByteTable[inFormat];
+}
+
+UInt32 GetMaxBytesPerPixel (TextureFormat inFormat)
+{
+ Assert (GetBytesFromTextureFormat (inFormat) <= kTextureByteTable[kTexFormatARGBFloat]);
+ return kTextureByteTable[kTexFormatARGBFloat];
+}
+
+int GetRowBytesFromWidthAndFormat (int width, TextureFormat inFormat)
+{
+ AssertMsg (inFormat < kTexFormatDXT1 || inFormat == kTexFormatBGRA32 || inFormat == kTexFormatRGBA4444, "Invalid texture format: %d", (int)inFormat);
+ return GetBytesFromTextureFormat (inFormat) * width;
+}
+
+bool IsValidTextureFormat (TextureFormat format)
+{
+ if ((format >= kTexFormatAlpha8 && format <= kTexFormatRGBA4444) ||
+ IsCompressedPVRTCTextureFormat(format) ||
+ IsCompressedETCTextureFormat(format) ||
+ IsCompressedATCTextureFormat(format) ||
+ IsCompressedFlashATFTextureFormat (format) ||
+ IsCompressedETC2TextureFormat(format) ||
+ IsCompressedASTCTextureFormat(format) ||
+ IsCompressedEACTextureFormat(format) ||
+ format == kTexFormatBGRA32)
+ return true;
+ else
+ return false;
+}
+
+int GetTextureSizeAllowedMultiple( TextureFormat format )
+{
+ if (IsCompressedDXTTextureFormat(format) || IsCompressedATCTextureFormat(format) || IsCompressedETCTextureFormat(format)
+ || IsCompressedETC2TextureFormat(format) || IsCompressedEACTextureFormat(format))
+ return 4;
+ else
+ return 1;
+}
+
+int GetMinimumTextureMipSizeForFormat( TextureFormat format )
+{
+ if (format == kTexFormatPVRTC_RGBA2 || format == kTexFormatPVRTC_RGB2)
+ return 16;
+ else if (format == kTexFormatPVRTC_RGBA4 || format == kTexFormatPVRTC_RGB4)
+ return 8;
+ else if (IsCompressedETCTextureFormat(format) || IsCompressedETC2TextureFormat(format) || IsCompressedEACTextureFormat(format))
+ return 4;
+ else if (IsCompressedATCTextureFormat(format))
+ return 4;
+ else if (IsCompressedASTCTextureFormat(format))
+ return 1;
+ else if (IsCompressedFlashATFTextureFormat(format))
+ return 1;
+ else if (IsCompressedDXTTextureFormat(format))
+ return 4;
+ else
+ return 1;
+}
+
+bool IsAlphaOnlyTextureFormat( TextureFormat format )
+{
+ return format == kTexFormatAlpha8;
+}
+
+
+TextureFormat ConvertToAlphaTextureFormat (TextureFormat format)
+{
+ if (format == kTexFormatRGB24 || format == kTexFormatBGR24)
+ return kTexFormatARGB32;
+ else if (format == kTexFormatRGB565)
+ return kTexFormatARGB4444;
+ else if (format == kTexFormatDXT1)
+ return kTexFormatDXT5;
+ else if (format == kTexFormatPVRTC_RGB2)
+ return kTexFormatPVRTC_RGBA2;
+ else if (format == kTexFormatPVRTC_RGB4)
+ return kTexFormatPVRTC_RGBA4;
+ else if (format == kTexFormatETC_RGB4)
+ return kTexFormatARGB4444;
+ else if (format == kTexFormatATC_RGB4)
+ return kTexFormatATC_RGBA8;
+ else if (IsCompressedFlashATFTextureFormat(format))
+ return kTexFormatFlashATF_RGBA_JPG;
+ else if (format == kTexFormatETC2_RGB)
+ return kTexFormatETC2_RGBA8;
+ else
+ return format;
+}
+
+bool HasAlphaTextureFormat( TextureFormat format )
+{
+ return format == kTexFormatAlpha8 || format == kTexFormatARGB4444 || format == kTexFormatRGBA4444 || format == kTexFormatRGBA32 || format == kTexFormatARGB32
+ || format == kTexFormatARGBFloat || format == kTexFormatAlphaLum16 || format == kTexFormatDXT5 || format == kTexFormatDXT3
+ || format == kTexFormatPVRTC_RGBA2 || format == kTexFormatPVRTC_RGBA4 || format == kTexFormatATC_RGBA8 || format == kTexFormatBGRA32 || format == kTexFormatFlashATF_RGBA_JPG
+ || format == kTexFormatETC2_RGBA1 || format == kTexFormatETC2_RGBA8 || (format >= kTexFormatASTC_RGBA_4x4 && format <= kTexFormatASTC_RGBA_12x12);
+}
+
+bool IsDepthRTFormat( RenderTextureFormat format )
+{
+ return format == kRTFormatDepth || format == kRTFormatShadowMap;
+}
+
+bool IsHalfRTFormat( RenderTextureFormat format )
+{
+ return format == kRTFormatARGBHalf || format == kRTFormatRGHalf || format == kRTFormatRHalf || IsDepthRTFormat(format);
+}
+
+const char* GetCompressionTypeString (TextureFormat format)
+{
+ // Shortcut here, no sense in typing all block sizes in switchcase below
+ if(IsCompressedASTCTextureFormat(format))
+ return "ASTC";
+
+ switch (format)
+ {
+ case kTexFormatDXT1: return "DXT1";
+ case kTexFormatDXT3: return "DXT3";
+ case kTexFormatDXT5: return "DXT5";
+ case kTexFormatETC_RGB4: return "ETC1";
+ case kTexFormatETC2_RGB:
+ case kTexFormatETC2_RGBA1:
+ case kTexFormatETC2_RGBA8: return "ETC2";
+ case kTexFormatEAC_R:
+ case kTexFormatEAC_R_SIGNED:
+ case kTexFormatEAC_RG:
+ case kTexFormatEAC_RG_SIGNED: return "EAC";
+ case kTexFormatPVRTC_RGB2:
+ case kTexFormatPVRTC_RGBA2:
+ case kTexFormatPVRTC_RGB4:
+ case kTexFormatPVRTC_RGBA4: return "PVRTC";
+ case kTexFormatATC_RGB4:
+ case kTexFormatATC_RGBA8: return "ATC";
+ case kTexFormatFlashATF_RGB_DXT1: return "DXT1";
+ case kTexFormatFlashATF_RGBA_JPG: return "JPG";
+ case kTexFormatFlashATF_RGB_JPG: return "JPG";
+ default:
+ return "Uncompressed";
+ }
+}
+
+const char* GetTextureFormatString (TextureFormat format)
+{
+ switch (format)
+ {
+ case kTexFormatAlpha8: return "Alpha 8";
+ case kTexFormatARGB4444: return "ARGB 16 bit";
+ case kTexFormatRGBA4444: return "RGBA 16 bit";
+ case kTexFormatRGB24: return "RGB 24 bit";
+ case kTexFormatRGBA32: return "RGBA 32 bit";
+ case kTexFormatARGB32: return "ARGB 32 bit";
+ case kTexFormatARGBFloat: return "ARGB float";
+ case kTexFormatRGB565: return "RGB 16 bit";
+ case kTexFormatBGR24: return "BGR 24 bit";
+ case kTexFormatAlphaLum16: return "Alpha 16 bit";
+ case kTexFormatDXT1: return "RGB Compressed DXT1";
+ case kTexFormatDXT3: return "RGBA Compressed DXT3";
+ case kTexFormatDXT5: return "RGBA Compressed DXT5";
+
+ // gles
+ case kTexFormatPVRTC_RGB2: return "RGB Compressed PVRTC 2 bits";
+ case kTexFormatPVRTC_RGBA2: return "RGBA Compressed PVRTC 2 bits";
+ case kTexFormatPVRTC_RGB4: return "RGB Compressed PVRTC 4 bits";
+ case kTexFormatPVRTC_RGBA4: return "RGBA Compressed PVRTC 4 bits";
+ case kTexFormatETC_RGB4: return "RGB Compressed ETC 4 bits";
+ case kTexFormatATC_RGB4: return "RGB Compressed ATC 4 bits";
+ case kTexFormatATC_RGBA8: return "RGBA Compressed ATC 8 bits";
+
+ case kTexFormatETC2_RGB: return "RGB Compressed ETC2 4 bits";
+ case kTexFormatETC2_RGBA1: return "RGB + 1-bit Alpha Compressed ETC2 4 bits";
+ case kTexFormatETC2_RGBA8: return "RGBA Compressed ETC2 8 bits";
+ case kTexFormatEAC_R: return "11-bit R Compressed EAC 4 bit";
+ case kTexFormatEAC_R_SIGNED: return "11-bit signed R Compressed EAC 4 bit";
+ case kTexFormatEAC_RG: return "11-bit RG Compressed EAC 8 bit";
+ case kTexFormatEAC_RG_SIGNED: return "11-bit signed RG Compressed EAC 8 bit";
+
+#define STR_(x) #x
+#define STR(x) STR_(x)
+#define DO_ASTC(bx,by) case kTexFormatASTC_RGB_##bx##x##by : return "RGB Compressed ASTC " STR(bx) "x" STR(by) " block"; case kTexFormatASTC_RGBA_##bx##x##by : return "RGBA Compressed ASTC " STR(bx) "x" STR(by) " block"
+
+ DO_ASTC(4, 4);
+ DO_ASTC(5, 5);
+ DO_ASTC(6, 6);
+ DO_ASTC(8, 8);
+ DO_ASTC(10, 10);
+ DO_ASTC(12, 12);
+
+#undef DO_ASTC
+#undef STR
+#undef STR_
+
+ // Flash
+ case kTexFormatFlashATF_RGB_DXT1: return "RGB Compressed DXT1";
+ case kTexFormatFlashATF_RGBA_JPG: return "RGBA JPG Compressed";
+ case kTexFormatFlashATF_RGB_JPG: return "RGB JPG Compressed";
+
+ case kTexFormatBGRA32: return "BGRA 32 bit";
+
+ default:
+ return "Unsupported";
+ }
+}
+
+const char* GetTextureColorSpaceString (TextureColorSpace colorSpace)
+{
+ switch (colorSpace)
+ {
+ case kTexColorSpaceLinear: return "Linear";
+ case kTexColorSpaceSRGB: return "sRGB";
+ case kTexColorSpaceSRGBXenon: return "sRGB (Xenon)";
+ default: return "Unsupported";
+ }
+}
+
+TextureColorSpace ColorSpaceToTextureColorSpace(BuildTargetPlatform platform, ColorSpace colorSpace)
+{
+ if (colorSpace == kGammaColorSpace)
+ return (platform == kBuildXBOX360) ? kTexColorSpaceSRGBXenon : kTexColorSpaceSRGB;
+ else
+ return kTexColorSpaceLinear;
+}
diff --git a/Runtime/Graphics/TextureFormat.h b/Runtime/Graphics/TextureFormat.h
new file mode 100644
index 0000000..5d6eed0
--- /dev/null
+++ b/Runtime/Graphics/TextureFormat.h
@@ -0,0 +1,96 @@
+#pragma once
+
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+
+/* Important note about endianess.
+ Endianess needs to be swapped for the following formats:
+ kTexFormatARGBFloat, kTexFormatRGB565, kTexFormatARGB4444, (assuming for this too: kTexFormatRGBA4444)
+*/
+
+UInt32 GetBytesFromTextureFormat( TextureFormat inFormat );
+UInt32 GetMaxBytesPerPixel( TextureFormat inFormat );
+int GetRowBytesFromWidthAndFormat( int width, TextureFormat format );
+bool IsValidTextureFormat (TextureFormat format);
+
+
+inline bool IsCompressedDXTTextureFormat( TextureFormat format )
+{
+ return format >= kTexFormatDXT1 && format <= kTexFormatDXT5;
+}
+
+inline bool IsCompressedPVRTCTextureFormat( TextureFormat format )
+{
+ return format >= kTexFormatPVRTC_RGB2 && format <= kTexFormatPVRTC_RGBA4;
+}
+
+inline bool IsCompressedETCTextureFormat( TextureFormat format )
+{
+ return format == kTexFormatETC_RGB4;
+}
+
+inline bool IsCompressedEACTextureFormat( TextureFormat format )
+{
+ return format >= kTexFormatEAC_R && format <= kTexFormatEAC_RG_SIGNED;
+}
+
+inline bool IsCompressedETC2TextureFormat( TextureFormat format )
+{
+ return format >= kTexFormatETC2_RGB && format <= kTexFormatETC2_RGBA8;
+}
+
+inline bool IsCompressedATCTextureFormat( TextureFormat format )
+{
+ return format == kTexFormatATC_RGB4 || format == kTexFormatATC_RGBA8;
+}
+inline bool IsCompressedFlashATFTextureFormat( TextureFormat format )
+{
+ return format == kTexFormatFlashATF_RGB_DXT1 || format == kTexFormatFlashATF_RGBA_JPG || format == kTexFormatFlashATF_RGB_JPG;
+}
+inline bool Is16BitTextureFormat(TextureFormat format )
+{
+ return format == kTexFormatARGB4444 || format == kTexFormatRGBA4444 || format == kTexFormatRGB565;
+}
+
+inline bool IsCompressedASTCTextureFormat( TextureFormat format)
+{
+ return format >= kTexFormatASTC_RGB_4x4 && format <= kTexFormatASTC_RGBA_12x12;
+}
+
+inline bool IsTextureFormatSupportedOnFlash(TextureFormat format )
+{
+ return IsCompressedFlashATFTextureFormat(format) ||
+ format == kTexFormatARGB32 ||
+ format == kTexFormatRGBA32 ||
+ format == kTexFormatRGB24 ||
+ format == kTexFormatAlpha8;
+}
+
+inline bool IsAnyCompressedTextureFormat( TextureFormat format )
+{
+ return IsCompressedDXTTextureFormat(format) || IsCompressedPVRTCTextureFormat(format)
+ || IsCompressedETCTextureFormat(format) || IsCompressedATCTextureFormat(format)
+ || IsCompressedFlashATFTextureFormat (format) || IsCompressedEACTextureFormat(format)
+ || IsCompressedETC2TextureFormat(format) || IsCompressedASTCTextureFormat(format);
+}
+
+bool IsAlphaOnlyTextureFormat( TextureFormat format );
+
+int GetTextureSizeAllowedMultiple( TextureFormat format );
+int GetMinimumTextureMipSizeForFormat( TextureFormat format );
+bool IsAlphaOnlyTextureFormat( TextureFormat format );
+
+TextureFormat ConvertToAlphaTextureFormat (TextureFormat format);
+
+bool HasAlphaTextureFormat( TextureFormat format );
+
+bool IsDepthRTFormat( RenderTextureFormat format );
+bool IsHalfRTFormat( RenderTextureFormat format );
+
+const char* GetCompressionTypeString (TextureFormat format);
+const char* GetTextureFormatString (TextureFormat format);
+const char* GetTextureColorSpaceString (TextureColorSpace colorSpace);
+
+TextureColorSpace ColorSpaceToTextureColorSpace(BuildTargetPlatform platform, ColorSpace colorSpace);
+
+std::pair<int,int> RoundTextureDimensionsToBlocks (TextureFormat fmt, int w, int h);
diff --git a/Runtime/Graphics/TextureGenerator.h b/Runtime/Graphics/TextureGenerator.h
new file mode 100644
index 0000000..13e9dac
--- /dev/null
+++ b/Runtime/Graphics/TextureGenerator.h
@@ -0,0 +1,114 @@
+#ifndef TEXTUREGENERATOR_H
+#define TEXTUREGENERATOR_H
+
+#include "TextureFormat.h"
+#include "Texture3D.h"
+#include "Texture.h"
+#include "Runtime/Math/Vector3.h"
+#include "CubemapTexture.h"
+
+template<typename T, class ColorFunctor>
+void GenerateTexture (Texture2D *tex, const ColorFunctor &fun) {
+ AssertIf (!tex);
+ int maxX = tex->GetGLWidth (), maxY = tex->GetGLHeight();
+ T *data = (T*)tex->GetRawImageData();
+ int bpp = GetBytesFromTextureFormat (tex->GetTextureFormat()) / sizeof(T);
+ for (int y = 0; y < maxY; y++)
+ for (int x = 0; x < maxX; x++) {
+ fun (tex, data, x, y, maxX, maxY);
+ data+=bpp;
+ }
+}
+
+template<typename T, class ColorFunctor>
+void Generate3DTexture (Texture3D *tex, const ColorFunctor &fun) {
+ AssertIf (!tex);
+ int maxX = tex->GetGLWidth (), maxY = tex->GetGLHeight(), maxZ = tex->GetDepth();
+ T *data = (T*)tex->GetImageDataPointer();
+ int bpp = GetBytesFromTextureFormat (tex->GetTextureFormat()) / sizeof(T);
+ for (int z = 0; z < maxZ; z++) {
+ for (int y = 0; y < maxY; y++) {
+ for (int x = 0; x < maxX; x++) {
+ fun (data, x, y, z, maxX, maxY, maxZ);
+ data += bpp;
+ }
+ }
+ }
+}
+
+template<typename T, class ColorFunctor>
+void GenerateCubeTexture (Cubemap *cubemap, const ColorFunctor &fun) {
+ AssertIf (!cubemap);
+ for (int direction = 0; direction < 6; direction++) {
+ const int kCubeXRemap[6] = { 2, 2, 0, 0, 0, 0 };
+ const int kCubeYRemap[6] = { 1, 1, 2, 2, 1, 1 };
+ const int kCubeZRemap[6] = { 0, 0, 1, 1, 2, 2 };
+ const float kCubeXSign[6] = { -1.0F, 1.0F, 1.0F, 1.0F, 1.0F, -1.0F };
+ const float kCubeYSign[6] = { -1.0F, -1.0F, 1.0F, -1.0F, -1.0F, -1.0F };
+ const float kCubeZSign[6] = { 1.0F, -1.0F, 1.0F, -1.0F, 1.0F, -1.0F };
+
+ int width = cubemap->GetGLWidth ();
+ int height = cubemap->GetGLHeight ();
+
+ // do the sign scale according to the cubemap specs then flip y sign
+ Vector3f signScale = Vector3f (kCubeXSign[direction], kCubeYSign[direction], kCubeZSign[direction]);
+ int byteSize = GetBytesFromTextureFormat (cubemap->GetTextureFormat ());
+
+ Vector2f invSize (1.0F / (float)width, 1.0F / (float)height);
+ UInt8* dest = cubemap->GetRawImageData (direction);
+ for (int y=0;y<height;y++)
+ {
+ for (int x=0;x<width;x++)
+ {
+ Vector2f uv = Scale (Vector2f (x, y), invSize) * 2.0F - Vector2f (1.0F, 1.0F);
+ Vector3f uvDir = Vector3f (uv.x, uv.y, 1.0F);
+
+ uvDir.Scale (signScale);
+ Vector3f worldDir;
+ // Rotate the uv to the world direction using a table lookup
+ worldDir[kCubeXRemap[direction]] = uvDir[0];
+ worldDir[kCubeYRemap[direction]] = uvDir[1];
+ worldDir[kCubeZRemap[direction]] = uvDir[2];
+
+ fun ((T*)dest, worldDir);
+ dest += byteSize;
+ }
+ }
+ }
+}
+
+template<typename T, typename ColorFunctor>
+Texture2D *BuildTexture (int width, int height, TextureFormat format, const ColorFunctor &fun, bool mipmaps = false) {
+ Texture2D* tex = CreateObjectFromCode<Texture2D>();
+ tex->SetHideFlags(Object::kHideAndDontSave);
+ tex->InitTexture (width, height, format, mipmaps ? Texture2D::kMipmapMask : Texture2D::kNoMipmap, 1);
+ tex->GetSettings().m_Aniso = 0; // disable aniso
+ GenerateTexture<T> (tex, fun);
+ mipmaps ? tex->UpdateImageData() : tex->UpdateImageDataDontTouchMipmap();
+ return tex;
+}
+
+template<typename T, class ColorFunctor>
+Texture3D *Build3DTexture (int width, int height, int depth, TextureFormat format, const ColorFunctor &fun) {
+ Texture3D *tex = CreateObjectFromCode<Texture3D>();
+ tex->SetHideFlags(Object::kHideAndDontSave);
+ tex->InitTexture (width, height, depth, format, false);
+ Generate3DTexture<T> (tex, fun);
+ tex->UpdateImageData(false);
+ return tex;
+}
+
+template<typename T, class ColorFunctor>
+Cubemap *BuildCubeTexture (int size, TextureFormat format, const ColorFunctor &fun) {
+ Cubemap *tex = CreateObjectFromCode<Cubemap>();
+ tex->SetHideFlags(Object::kHideAndDontSave);
+ tex->InitTexture (size, size, format, 0, 6);
+ GenerateCubeTexture<T> (tex, fun);
+ tex->UpdateImageDataDontTouchMipmap();
+ tex->GetSettings ().m_WrapMode = kTexWrapClamp;
+ tex->ApplySettings ();
+ return tex;
+}
+
+
+#endif
diff --git a/Runtime/Graphics/TextureSettings.cpp b/Runtime/Graphics/TextureSettings.cpp
new file mode 100644
index 0000000..a11b565
--- /dev/null
+++ b/Runtime/Graphics/TextureSettings.cpp
@@ -0,0 +1,64 @@
+#include "UnityPrefix.h"
+#include "TextureSettings.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+static int gUserMinAniso = 1;
+static int gUserMaxAniso = 16;
+
+
+void TextureSettings::SetAnisoLimits (int minAniso, int maxAniso)
+{
+ gUserMinAniso = minAniso;
+ gUserMaxAniso = maxAniso;
+ ErrorIf (gUserMinAniso < 1);
+ ErrorIf (gUserMaxAniso > 16);
+}
+
+void TextureSettings::GetAnisoLimits (int& minAniso, int& maxAniso)
+{
+ minAniso = gUserMinAniso;
+ maxAniso = gUserMaxAniso;
+}
+
+
+void TextureSettings::Reset ()
+{
+ m_FilterMode = kTexFilterBilinear;
+ m_Aniso = 1;
+ m_MipBias = 0.0f;
+ m_WrapMode = 0;
+}
+
+void TextureSettings::CheckConsistency()
+{
+ m_FilterMode = clamp<int> (m_FilterMode, 0, kTexFilterCount-1);
+ m_WrapMode = clamp<int> (m_WrapMode, 0, kTexWrapCount-1);
+}
+
+
+void TextureSettings::Apply (TextureID texture, TextureDimension texDim, bool hasMipMap, TextureColorSpace colorSpace) const
+{
+ GfxDevice& device = GetGfxDevice();
+
+ int aniso;
+ // Never use anisotropic on textures where we certainly don't want it,
+ // and on Point filtered textures.
+ if (m_Aniso == 0 || m_FilterMode == kTexFilterNearest)
+ aniso = 1;
+ else
+ aniso = clamp (m_Aniso, gUserMinAniso, gUserMaxAniso);
+
+ device.SetTextureParams (texture, texDim, (TextureFilterMode)m_FilterMode,
+ (TextureWrapMode)m_WrapMode, aniso, hasMipMap, colorSpace);
+}
+
+#if UNITY_EDITOR
+void TextureSettings::Invalidate ()
+{
+ m_FilterMode = -1;
+ m_Aniso = -1;
+ m_MipBias = -1.0f;
+ m_WrapMode = -1;
+}
+#endif
diff --git a/Runtime/Graphics/TextureSettings.h b/Runtime/Graphics/TextureSettings.h
new file mode 100644
index 0000000..3082a5a
--- /dev/null
+++ b/Runtime/Graphics/TextureSettings.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+
+// Texture filter, anisotropy, wrap mode settings.
+struct TextureSettings
+{
+ DECLARE_SERIALIZE_NO_PPTR (GLTextureSettings) // keep the name GLTextureSettings here, it's for serialized stuff!
+
+ int m_FilterMode; ///< enum { Nearest, Bilinear, Trilinear } Texture filter mode
+ int m_Aniso; ///< Anisotropy factor (1 = None, 0 = Always disabled)
+ float m_MipBias; ///< Bias used for LOD-selection (0 = none)
+ int m_WrapMode; ///< enum {Repeat, Clamp} Texture wrapping mode.
+
+ TextureSettings () { Reset (); }
+
+ // Set default values
+ void Reset();
+
+ void CheckConsistency();
+
+ #if UNITY_EDITOR
+ // Set all numbers to -1, marking them as invalid
+ void Invalidate();
+ #endif
+
+ void Apply (TextureID texture, TextureDimension texDim, bool hasMipMap, TextureColorSpace colorSpace) const;
+
+ static void SetAnisoLimits (int minAniso, int maxAniso);
+ static void GetAnisoLimits (int& minAniso, int& maxAniso);
+};
+
+template<class TransferFunc>
+void TextureSettings::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (m_FilterMode);
+ TRANSFER (m_Aniso);
+ TRANSFER (m_MipBias);
+ TRANSFER (m_WrapMode);
+}
diff --git a/Runtime/Graphics/Transform.cpp b/Runtime/Graphics/Transform.cpp
new file mode 100644
index 0000000..eaa9bdb
--- /dev/null
+++ b/Runtime/Graphics/Transform.cpp
@@ -0,0 +1,1695 @@
+#include "UnityPrefix.h"
+#include "Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/BaseClasses/SupportedMessageOptimization.h"
+#include "Runtime/Serialize/SerializationMetaFlags.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Utilities/RecursionLimit.h"
+#include "Runtime/Utilities/Prefetch.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/SceneInspector.h"
+static Transform::HierarchyChangedCallback* gHierarchyChangedCallback = NULL;
+static Transform::HierarchyChangedCallbackSetParent* gHierarchyChangedSetParentCallback = NULL;
+#endif
+
+static Transform* FindActiveTransformWithPathImpl (const char* path, GameObject& go, bool needsToBeRoot);
+
+// parentTransform * (translation * rotation * scale)
+
+#if DEBUGMODE
+#define ASSERT_ROTATION if (!CompareApproximately (SqrMagnitude (m_LocalRotation), 1.0F)) { AssertStringObject(Format("transform.rotation of '%s' is no longer valid, due to a bad input value. Input rotation is %f, %f, %f, %f.", GetName(), q.x, q.y, q.z, q.w), this); }
+#else
+#define ASSERT_ROTATION
+#endif
+
+#pragma message ("Move this away")
+inline float InverseSafe (float f)
+{
+ if (Abs (f) > Vector3f::epsilon)
+ return 1.0F / f;
+ else
+ return 0.0F;
+
+}
+
+inline Vector3f InverseSafe (const Vector3f& v)
+{
+ return Vector3f (InverseSafe (v.x), InverseSafe (v.y), InverseSafe (v.z));
+}
+
+static float MakeNiceAbs(float f)
+{
+ union
+ {
+ float f;
+ UInt32 n;
+ } u;
+ u.f = f;
+
+ int exponent = ((u.n & 0x7F800000) >> 23) - 127;
+
+ if(exponent >= 24)
+ return f;
+
+ if(exponent <= -24)
+ return 0;
+
+ UInt32 v = (UInt32)f;
+ float fraction = f - v;
+
+ if(fraction < 0.00001f)
+ return v;
+
+ if(fraction > 0.99999f)
+ return v + 1;
+
+ return f;
+}
+
+static float MakeNice(float f)
+{
+ if(f >= 0)
+ return MakeNiceAbs(f);
+ else
+ return -MakeNiceAbs(-f);
+}
+
+Vector3f MakeNice(const Vector3f& v)
+{
+ return Vector3f(MakeNice(v[0]), MakeNice(v[1]), MakeNice(v[2]));
+}
+
+
+
+
+Transform::Transform (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+//, m_Children (Transform::TransformComList::allocator_type(*baseAllocator))
+#if UNITY_EDITOR
+, m_VisibleRootValid(false)
+#endif
+, m_CachedTransformType(kNoScaleTransform)
+, m_HasCachedTransformMatrix(false)
+, m_HasChanged(false)
+{
+ m_InternalTransformType = kNoScaleTransform;
+ m_SupportsTransformChanged = 0;
+
+#if UNITY_EDITOR
+ // Create hint that will make euler angles at editor time match those in play mode
+ //
+ m_LocalEulerAnglesHint.x = 179.999f;
+ m_LocalEulerAnglesHint.y = 179.999f;
+ m_LocalEulerAnglesHint.z = 179.999f;
+ #endif
+}
+
+void Transform::Reset ()
+{
+ Super::Reset ();
+ m_LocalRotation = Quaternionf::identity ();
+
+ m_LocalPosition = Vector3f::zero;
+ m_LocalScale = Vector3f::one;
+
+#if UNITY_EDITOR
+ m_LocalEulerAnglesHint.x = 0;
+ m_LocalEulerAnglesHint.y = 0;
+ m_LocalEulerAnglesHint.z = 0;
+#endif
+
+ RecalculateTransformType ();
+
+
+ m_HasCachedTransformMatrix = false;
+ m_HasChanged = true;
+
+ if (GetGameObjectPtr())
+ SendTransformChanged (kPositionChanged | kRotationChanged | kScaleChanged | kParentingChanged);
+
+#if ENABLE_EDITOR_HIERARCHY_ORDERING
+ m_Order = 0;
+#endif
+}
+
+Transform::~Transform ()
+{
+#if UNITY_EDITOR
+ if (m_VisibleRootValid)
+ GetSceneTracker().GetVisibleRootTransforms().erase(m_VisibleRootIterator);
+#endif
+}
+
+Transform::iterator Transform::Find( const Transform* child )
+{
+ iterator it, itEnd = end();
+ for( it = begin(); it != itEnd; ++it )
+ {
+ if( *it == child )
+ return it;
+ }
+ return itEnd;
+}
+
+void Transform::RemoveFromParent ()
+{
+// Assert(!IsPersistent());
+
+ // If it has an father, remove this from fathers children
+ Transform* father = GetParent ();
+ if (father != NULL)
+ {
+// Assert(!father->IsPersistent());
+
+ TransformComList& children = father->m_Children;
+ // Fastpath try back of children array
+ if (!children.empty() != 0 && &*children.back() == this)
+ {
+ children.pop_back();
+ }
+ // Find this in fathers children list
+ else
+ {
+ iterator i = father->Find(this);
+ if (i != children.end ())
+ children.erase (i);
+ }
+
+ father->SetDirty ();
+ }
+}
+
+void Transform::ClearChildrenParentPointer ()
+{
+ for (int i=0;i<m_Children.size();i++)
+ {
+ Transform* cur = m_Children[i];
+
+ AssertIf(cur == NULL || cur->GetParent() != this);
+ if (cur && cur->GetParent() == this)
+ cur->m_Father = NULL;
+ }
+}
+
+void Transform::ClearChild (Transform* child)
+{
+ iterator found = Find(child);
+ if (found != m_Children.end())
+ m_Children.erase(found);
+}
+
+Matrix3x3f Transform::GetWorldScale () const
+{
+ Matrix3x3f invRotation;
+ QuaternionToMatrix (Inverse (GetRotation ()), invRotation);
+ Matrix3x3f scaleAndRotation = GetWorldRotationAndScale ();
+ return invRotation * scaleAndRotation;
+}
+
+Vector3f Transform::GetWorldScaleLossy () const
+{
+ Matrix3x3f rot = GetWorldScale ();
+ return Vector3f (rot.Get (0, 0), rot.Get (1, 1), rot.Get (2, 2));
+}
+
+
+Matrix3x3f Transform::GetWorldRotationAndScale () const
+{
+ Matrix3x3f scale;
+ scale.SetScale (m_LocalScale);
+
+ Matrix3x3f rotation;
+ QuaternionToMatrix (m_LocalRotation, rotation);
+
+ Transform* parent = GetParent ();
+ if (parent)
+ {
+ ///@TODO optimize: Special case multiplication
+ Matrix3x3f parentTransform = parent->GetWorldRotationAndScale ();
+ return parentTransform * rotation * scale;
+ }
+ else
+ {
+ return rotation * scale;
+ }
+}
+
+bool Transform::SetParent (Transform* newFather, SetParentOption options)
+{
+ if (GetGameObject().IsDestroying() || (newFather && newFather->GetGameObject().IsDestroying()))
+ return false;
+
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ {
+ if ( (GetParent () && GetParent ()->GetGameObject().IsActivating()) || (newFather && newFather->GetGameObject().IsActivating()))
+ {
+ ErrorStringObject ("Cannot change GameObject hierarchy while activating or deactivating the parent.", this);
+ return false;
+ }
+ }
+
+ // Make sure that the new father is not a child of this transform.
+ Transform* cur;
+ for (cur=newFather;cur != NULL;cur = cur->m_Father)
+ {
+ if (this == cur)
+ return false;
+ }
+
+ if ((options & kAllowParentingFromPrefab) == 0)
+ {
+ if (IsPrefabParent() || (newFather && newFather->IsPrefabParent()))
+ {
+ ErrorString("Setting the parent of a transform which resides in a prefab is disabled to prevent data corruption.");
+ return false;
+ }
+ }
+
+// if (IsPersistent() || (newFather && newFather->IsPersistent()))
+// {
+// ErrorString("Setting the parent of a transform on an asset is not allowed!");
+// return false;
+// }
+
+ // Save the old position in worldspace
+ Vector3f worldPosition = GetPosition ();
+ Quaternionf worldRotation = GetRotation ();
+ Matrix3x3f worldScale = GetWorldRotationAndScale ();
+
+ // If it already has an father, remove this from fathers children
+ Transform* father = GetParent ();
+ if (father != NULL)
+ {
+ // Find this in fathers children list
+ iterator i = father->Find( this );
+ AssertIf (i == father->end ());
+ father->m_Children.erase (i);
+ father->SetDirty ();
+ }
+
+ #if UNITY_EDITOR
+ if (newFather)
+ {
+ ///@TODO: This can use binary search because we should be able to assume that the children array is already sorted
+ iterator i = newFather->begin();
+ iterator end = newFather->end();
+
+ for (;i!=end;i++)
+ {
+ #if ENABLE_EDITOR_HIERARCHY_ORDERING
+ if (CompareDepths(this, (*i)))
+ #else
+ if (SemiNumericCompare(GetName(), (**i).GetName()) < 0)
+ #endif
+ {
+ newFather->m_Children.insert (i, this);
+ break;
+ }
+ }
+ if (i == end)
+ newFather->m_Children.push_back (this);
+
+ newFather->SetDirty ();
+ }
+ #else
+ if (newFather)
+ {
+ newFather->m_Children.push_back (this);
+ newFather->SetDirty ();
+ }
+ #endif
+
+ m_Father = newFather;
+
+ if (!(options & kDisableTransformMessage))
+ {
+ if (options & kWorldPositionStays)
+ {
+ // Restore old position so they stay at the same position in worldspace
+ SetRotationSafe (worldRotation);
+ SetPosition (worldPosition);
+ SetWorldRotationAndScale ( worldScale );
+ SendTransformChanged (kParentingChanged);
+ }
+ else
+ SendTransformChanged (kPositionChanged | kRotationChanged | kScaleChanged | kParentingChanged);
+ }
+
+ #if UNITY_EDITOR
+ if (gHierarchyChangedSetParentCallback)
+ gHierarchyChangedSetParentCallback (this, father, newFather);
+ #endif
+ SetDirty ();
+ SetCacheDirty();
+
+ return true;
+}
+
+
+#if UNITY_EDITOR
+
+#if ENABLE_EDITOR_HIERARCHY_ORDERING
+bool Transform::CompareVisibleRoots::operator() (const VisibleRootKey& lhs, const VisibleRootKey& rhs) const
+{
+ bool orderCompare = lhs.first != rhs.first;
+ if (orderCompare)
+ return lhs.first < rhs.first;
+ else
+ {
+ VisibleRootSecondaryKey lhsSecondaryKey = lhs.second;
+ VisibleRootSecondaryKey rhsSecondaryKey = rhs.second;
+ int compare = SemiNumericCompare(lhsSecondaryKey.first, rhsSecondaryKey.first);
+ if (compare != 0)
+ return compare < 0;
+ return lhsSecondaryKey.second < rhsSecondaryKey.second;
+ }
+}
+#else
+bool Transform::CompareVisibleRoots::operator() (const VisibleRootKey& lhs, const VisibleRootKey& rhs) const
+{
+ int compare = SemiNumericCompare(lhs.first, rhs.first);
+ if (compare != 0)
+ return compare < 0;
+ return lhs.second < rhs.second;
+}
+#endif
+
+// Cannot be called Repeat as there is already another function with that name (which does currently not work for negative numbers)
+inline float RepeatWorking (float t, float length)
+{
+ return (t - (floor (t / length) * length));
+}
+
+void Transform::SyncLocalEulerAnglesHint ()
+{
+ if (IsWorldPlaying())
+ return;
+
+ Vector3f newEuler = QuaternionToEuler(m_LocalRotation) * Rad2Deg(1);
+
+ newEuler.x = RepeatWorking(newEuler.x - m_LocalEulerAnglesHint.x + 180.0F, 360.0F) + m_LocalEulerAnglesHint.x - 180.0F;
+ newEuler.y = RepeatWorking(newEuler.y - m_LocalEulerAnglesHint.y + 180.0F, 360.0F) + m_LocalEulerAnglesHint.y - 180.0F;
+ newEuler.z = RepeatWorking(newEuler.z - m_LocalEulerAnglesHint.z + 180.0F, 360.0F) + m_LocalEulerAnglesHint.z - 180.0F;
+
+ m_LocalEulerAnglesHint = MakeNice(newEuler);
+}
+#endif
+
+void Transform::SetLocalEulerAngles (const Vector3f& eulerAngles)
+{
+ ABORT_INVALID_VECTOR3 (eulerAngles, localEulerAngles, transform)
+
+ SetLocalRotationSafe (EulerToQuaternion (eulerAngles * Deg2Rad (1)));
+
+ #if UNITY_EDITOR
+ if (!IsWorldPlaying())
+ {
+ m_LocalEulerAnglesHint = MakeNice(eulerAngles);
+ }
+ #endif
+}
+
+Vector3f Transform::GetLocalEulerAngles ()
+{
+ #if UNITY_EDITOR
+ if (!IsWorldPlaying())
+ {
+ if ( !CompareApproximately (EulerToQuaternion (m_LocalEulerAnglesHint * Deg2Rad(1)), m_LocalRotation) )
+ SyncLocalEulerAnglesHint ();
+
+ return m_LocalEulerAnglesHint;
+ }
+ else
+ {
+ Quaternionf rotation = NormalizeSafe (m_LocalRotation);
+ return QuaternionToEuler (rotation) * Rad2Deg (1);
+ }
+ #else
+ Quaternionf rotation = NormalizeSafe (m_LocalRotation);
+ return QuaternionToEuler (rotation) * Rad2Deg (1);
+ #endif
+}
+
+
+void Transform::SetLocalPosition (const Vector3f& inTranslation)
+{
+ ABORT_INVALID_VECTOR3 (inTranslation, localPosition, transform);
+ m_LocalPosition = inTranslation;
+ SetDirty ();
+ SendTransformChanged (kPositionChanged);
+}
+
+void Transform::SetLocalRotation (const Quaternionf& q)
+{
+ ABORT_INVALID_QUATERNION (q, localRotation, transform);
+ m_LocalRotation = q;
+
+#if UNITY_EDITOR
+ SyncLocalEulerAnglesHint ();
+#endif
+ ASSERT_ROTATION
+
+ SetDirty ();
+ SendTransformChanged (kRotationChanged);
+}
+
+void Transform::SetLocalRotationSafe (const Quaternionf& q)
+{
+ SetLocalRotation (NormalizeSafe(q));
+}
+
+void Transform::SetRotation (const Quaternionf& q)
+{
+ Transform* father = GetParent ();
+ if (father != NULL)
+ SetLocalRotation (Inverse (father->GetRotation ()) * q);
+ else
+ SetLocalRotation (q);
+}
+
+void Transform::SetRotationSafe(const Quaternionf& q)
+{
+ ABORT_INVALID_QUATERNION (q, rotation, transform);
+ Transform* father = GetParent ();
+ if (father != NULL)
+ SetLocalRotation (NormalizeSafe (Inverse (father->GetRotation ()) * q));
+ else
+ SetLocalRotation (NormalizeSafe (q));
+}
+
+void Transform::SetPosition (const Vector3f& p)
+{
+ ABORT_INVALID_VECTOR3 (p, position, transform);
+
+ Vector3f newPosition = p;
+ Transform* father = GetParent ();
+ if (father != NULL)
+ newPosition = father->InverseTransformPoint (newPosition);
+
+ SetLocalPosition (newPosition);
+}
+
+void Transform::SetPositionWithLocalOffset (const Vector3f& p, const Vector3f& localOffset)
+{
+ ABORT_INVALID_VECTOR3 (p, positionWithLocalOffset, transform);
+ ABORT_INVALID_VECTOR3 (localOffset, positionWithLocalOffset, transform);
+ Vector3f newPosition = p - TransformPoint (localOffset) + GetPosition ();
+ SetPosition (newPosition);
+}
+
+Vector3f Transform::TransformPointWithLocalOffset (const Vector3f& p, const Vector3f& localOffset) const
+{
+ return p - TransformPoint (localOffset) + GetPosition ();
+}
+
+void Transform::SetWorldRotationAndScale (const Matrix3x3f& scale)
+{
+ m_LocalScale = Vector3f::one;
+
+ Matrix3x3f inverseRS = GetWorldRotationAndScale ();
+ inverseRS.Invert ();
+
+ inverseRS = inverseRS * scale;
+
+ m_LocalScale.x = inverseRS.Get (0, 0);
+ m_LocalScale.y = inverseRS.Get (1, 1);
+ m_LocalScale.z = inverseRS.Get (2, 2);
+
+ RecalculateTransformType ();
+ SetDirty ();
+ SendTransformChanged (kScaleChanged | kRotationChanged | kPositionChanged);
+}
+
+void Transform::SetLocalScale (const Vector3f& scale)
+{
+ ABORT_INVALID_VECTOR3 (scale, localScale, transform);
+ m_LocalScale = scale;
+ RecalculateTransformType ();
+ SetDirty ();
+ SendTransformChanged (kScaleChanged | kRotationChanged | kPositionChanged);
+}
+
+template<bool Safe, bool Notify>
+void Transform::SetPositionAndRotationInternal ( const Vector3f& p, const Quaternionf& q )
+{
+ ABORT_INVALID_VECTOR3 (p, position, transform);
+ ABORT_INVALID_QUATERNION (q, rotation, transform);
+ Transform* father = GetParent ();
+ if (father != NULL)
+ {
+ m_LocalPosition = father->InverseTransformPoint (p);
+ if ( Safe )
+ m_LocalRotation = NormalizeSafe (Inverse (father->GetRotation ()) * q);
+ else
+ m_LocalRotation = Inverse (father->GetRotation ()) * q;
+ }
+ else
+ {
+ m_LocalPosition = p;
+ if ( Safe )
+ m_LocalRotation = NormalizeSafe (q);
+ else
+ m_LocalRotation = q;
+ }
+#if UNITY_EDITOR
+ SyncLocalEulerAnglesHint ();
+#endif
+
+ ASSERT_ROTATION
+ if ( Notify )
+ {
+ SetDirty ();
+ SendTransformChanged ( kPositionChanged | kRotationChanged );
+ }
+}
+
+void Transform::SetPositionAndRotationWithoutNotification (const Vector3f& p, const Quaternionf& q)
+{
+ SetPositionAndRotationInternal<false, false>( p, q );
+}
+
+void Transform::SetPositionAndRotationSafeWithoutNotification (const Vector3f& p, const Quaternionf& q)
+{
+ SetPositionAndRotationInternal<true, false>( p, q );
+}
+
+void Transform::SetPositionAndRotation (const Vector3f& p, const Quaternionf& q)
+{
+ SetPositionAndRotationInternal<false, true>( p, q );
+}
+
+void Transform::SetPositionAndRotationSafe (const Vector3f& p, const Quaternionf& q)
+{
+ SetPositionAndRotationInternal<true, true>( p, q );
+}
+
+void Transform::SetPositionWithoutNotification (const Vector3f& p)
+{
+ ABORT_INVALID_VECTOR3 (p, position, transform);
+ Transform* father = GetParent ();
+ if (father != NULL)
+ m_LocalPosition = father->InverseTransformPoint (p);
+ else
+ m_LocalPosition = p;
+}
+
+void Transform::SetRotationWithoutNotification (const Quaternionf& q)
+{
+ Transform* father = GetParent ();
+ if (father != NULL)
+ m_LocalRotation = Inverse (father->GetRotation ()) * q;
+ else
+ m_LocalRotation = q;
+}
+
+void Transform::SetLocalPositionAndRotation (const Vector3f& p, const Quaternionf& q)
+{
+ ABORT_INVALID_VECTOR3 (p, localPosition, transform);
+ ABORT_INVALID_QUATERNION (q, localRotation, transform);
+ m_LocalPosition = p;
+ m_LocalRotation = q;
+
+ #if UNITY_EDITOR
+ SyncLocalEulerAnglesHint ();
+ #endif
+
+ ASSERT_ROTATION
+
+ SetDirty ();
+ SendTransformChanged (kPositionChanged | kRotationChanged);
+}
+#if UNITY_EDITOR
+void Transform::RegisterHierarchyChangedCallback (HierarchyChangedCallback* callback)
+{
+ gHierarchyChangedCallback = callback;
+}
+
+void Transform::RegisterHierarchyChangedSetParentCallback (HierarchyChangedCallbackSetParent* callback)
+{
+ gHierarchyChangedSetParentCallback = callback;
+}
+#endif
+
+UInt32 Transform::CalculateSupportedMessages ()
+{
+ if (GetGameObject().WillHandleMessage(kTransformChanged))
+ return kSupportsTransformChanged;
+ else
+ return 0;
+}
+
+void Transform::MakeEditorValuesLookNice()
+{
+ m_LocalScale = MakeNice(m_LocalScale);
+ m_LocalPosition = MakeNice(m_LocalPosition);
+}
+
+void Transform::SupportedMessagesDidChange (int mask)
+{
+ Super::SupportedMessagesDidChange(mask);
+ m_SupportsTransformChanged = mask & kSupportsTransformChanged;
+}
+
+void Transform::SendTransformChanged (int changeMask)
+{
+ bool parentingChanged = changeMask & kParentingChanged;
+
+ // Fastpath if we don't support any TransformChanged callbacks on this game object
+ if (!m_SupportsTransformChanged && !parentingChanged)
+ {
+ m_HasCachedTransformMatrix = false;
+ m_HasChanged = true;
+
+ TransformComList::iterator i;
+ TransformComList::iterator end = m_Children.end ();
+ for (i = m_Children.begin ();i != end;i++)
+ {
+ Transform* child = *i;
+ child->SendTransformChanged (changeMask | kPositionChanged);
+ }
+
+ return;
+ }
+
+ m_HasCachedTransformMatrix = false;
+ m_HasChanged = true;
+
+ // NOTE: SetCacheDirty() doesn't need to be called here since SendTransformChanged traverses the transform hierarchy anyway
+
+ GameObject& go = GetGameObject ();
+ if (m_SupportsTransformChanged)
+ {
+ MessageData data;
+ data.SetData (changeMask, ClassID (int));
+ go.SendMessageAny (kTransformChanged, data);
+ }
+
+ if (parentingChanged)
+ go.TransformParentHasChanged ();
+
+ TransformComList::iterator i;
+ TransformComList::iterator end = m_Children.end ();
+ for (i = m_Children.begin ();i != end;i++)
+ {
+ Transform* child = *i;
+ child->SendTransformChanged (changeMask | kPositionChanged);
+ }
+}
+
+void Transform::SetCacheDirty()
+{
+ m_HasCachedTransformMatrix = false;
+ m_HasChanged = true;
+
+ TransformComList::iterator end = m_Children.end ();
+ for (TransformComList::iterator i = m_Children.begin (); i != end; ++i)
+ (*i)->SetCacheDirty();
+}
+
+void Transform::BroadcastMessageAny(const MessageIdentifier& messageID, MessageData& data)
+{
+ GameObject* go = GetGameObjectPtr ();
+ if (go)
+ go->SendMessageAny (messageID, data);
+
+ TransformComList::iterator i;
+ for (i = m_Children.begin ();i != m_Children.end ();i++)
+ (**i).BroadcastMessageAny (messageID, data);
+}
+
+void Transform::RotateAroundLocal (const Vector3f& localAxis, float rad)
+{
+ AssertIf (!CompareApproximately (Magnitude (localAxis), 1.0F));
+
+ Quaternionf q = AxisAngleToQuaternion (localAxis, rad);
+ m_LocalRotation = Normalize (q * m_LocalRotation);
+ #if UNITY_EDITOR
+ SyncLocalEulerAnglesHint ();
+ #endif
+
+ SetDirty ();
+ SendTransformChanged (kRotationChanged);
+}
+
+void Transform::RotateAroundLocalSafe (const Vector3f& localAxis, float rad)
+{
+ if (SqrMagnitude (localAxis) > Vector3f::epsilon)
+ RotateAroundLocal (Normalize (localAxis), rad);
+}
+
+void Transform::RotateAround (const Vector3f& worldAxis, float rad)
+{
+ AssertIf (!CompareApproximately (Magnitude (worldAxis), 1.0F));
+
+ Vector3f localAxis = InverseTransformDirection(worldAxis);
+
+ Quaternionf q = AxisAngleToQuaternion (localAxis, rad);
+ m_LocalRotation = Normalize (m_LocalRotation * q);
+ #if UNITY_EDITOR
+ SyncLocalEulerAnglesHint ();
+ #endif
+
+ SetDirty ();
+ SendTransformChanged (kRotationChanged);
+}
+
+void Transform::RotateAroundSafe (const Vector3f& worldAxis, float rad)
+{
+ Vector3f localAxis = InverseTransformDirection(worldAxis);
+ if (SqrMagnitude (localAxis) > Vector3f::epsilon)
+ {
+ localAxis = Normalize (localAxis);
+ Quaternionf q = AxisAngleToQuaternion (localAxis, rad);
+ m_LocalRotation = Normalize (m_LocalRotation * q);
+ #if UNITY_EDITOR
+ SyncLocalEulerAnglesHint ();
+ #endif
+
+ SetDirty ();
+ SendTransformChanged (kRotationChanged);
+ }
+}
+
+Vector3f Transform::GetPosition () const
+{
+ Vector3f worldPos = m_LocalPosition;
+ Transform* cur = GetParent ();
+ while (cur)
+ {
+ worldPos.Scale (cur->m_LocalScale);
+ worldPos = RotateVectorByQuat (cur->m_LocalRotation, worldPos);
+ worldPos += cur->m_LocalPosition;
+
+ cur = cur->GetParent ();
+ }
+
+ return worldPos;
+}
+
+Quaternionf Transform::GetRotation ()const
+{
+ Quaternionf worldRot = m_LocalRotation;
+ Transform* cur = GetParent ();
+ while (cur)
+ {
+ worldRot = cur->m_LocalRotation * worldRot;
+ cur = cur->GetParent ();
+ }
+
+ return worldRot;
+}
+
+
+void Transform::GetPositionAndRotation (Vector3f& pos, Quaternionf& q) const
+{
+ Vector3f worldPos = m_LocalPosition;
+ Quaternionf worldRot = m_LocalRotation;
+ Transform* cur = GetParent ();
+ while (cur)
+ {
+ worldPos.Scale (cur->m_LocalScale);
+ worldPos = RotateVectorByQuat (cur->m_LocalRotation, worldPos);
+ worldPos += cur->m_LocalPosition;
+
+ worldRot = cur->m_LocalRotation * worldRot;
+
+ cur = cur->GetParent ();
+ }
+
+ pos = worldPos;
+ q = worldRot;
+}
+
+static TransformType DetectActualNegativeScale (int type, const Transform* transform)
+{
+ type &= ~kOddNegativeScaleTransform;
+
+ // Calculate if we need to flip the back facing when rendering
+ // We need to use backface rendering if one or three scale axes are negative (odd count)
+ // In this case we enable kOddNegativeScaleTransform flag
+
+ Transform* cur = (Transform *)transform;
+ while (cur)
+ {
+ TransformType parentType = (TransformType)cur->m_InternalTransformType;
+ // kOddNegativeScaleTransform is XOR against parent (odd+odd=even, odd+even=odd, even+even=even), other bits are OR
+ type = ((type | parentType) & ~kOddNegativeScaleTransform) | ((type ^ parentType) & kOddNegativeScaleTransform);
+ cur = cur->GetParent ();
+ }
+
+ return (TransformType)type;
+}
+
+static TransformType UpdateTransformType (TransformType type, const Transform* transform)
+{
+ if ((type & kOddNegativeScaleTransform) != 0)
+ type = DetectActualNegativeScale (type, transform);
+
+ // kNonUniformScaleTransform 'overwrites' kUniformScaleTransform
+ if ((type & kNonUniformScaleTransform) != 0)
+ type &= ~kUniformScaleTransform;
+
+ Assert ((type & (kNonUniformScaleTransform|kUniformScaleTransform)) != (kNonUniformScaleTransform|kUniformScaleTransform));
+ return type;
+}
+
+
+TransformType Transform::GetPositionAndRotationWithTransformType (Vector3f& worldPos, Quaternionf& worldRot) const
+{
+ TransformType type = (TransformType)m_InternalTransformType;
+
+ worldPos = m_LocalPosition;
+ worldRot = m_LocalRotation;
+ Transform* cur = GetParent ();
+ while (cur)
+ {
+ TransformType parentType = (TransformType)cur->m_InternalTransformType;
+ // kOddNegativeScaleTransform is XOR against parent (odd+odd=even, odd+even=odd, even+even=even), other bits are OR
+ type = ((type | parentType) & ~kOddNegativeScaleTransform) | ((type ^ parentType) & kOddNegativeScaleTransform);
+
+ worldPos.Scale (cur->m_LocalScale);
+ worldPos = RotateVectorByQuat (cur->m_LocalRotation, worldPos);
+ worldPos += cur->m_LocalPosition;
+
+ worldRot = cur->m_LocalRotation * worldRot;
+
+ cur = cur->GetParent ();
+ }
+
+ // kNonUniformScaleTransform 'overwrites' kUniformScaleTransform
+ if ((type & kNonUniformScaleTransform) != 0)
+ type &= ~kUniformScaleTransform;
+ Assert ((type & (kNonUniformScaleTransform|kUniformScaleTransform)) != (kNonUniformScaleTransform|kUniformScaleTransform));
+
+ return type;
+}
+
+TransformType Transform::CalculateTransformMatrix (Matrix4x4f& transform) const
+{
+ //@TODO: Does this give any performance gain??
+ Prefetch(m_CachedTransformMatrix.GetPtr());
+ if (m_HasCachedTransformMatrix)
+ {
+ CopyMatrix(m_CachedTransformMatrix.GetPtr(), transform.GetPtr());
+ return (TransformType)m_CachedTransformType;
+ }
+
+ const Transform* transforms[32];
+ int transformCount = 1;
+ TransformType type = (TransformType)0;
+ Matrix4x4f temp;
+
+ {
+ // collect all transform that need CachedTransformMatrix update
+ transforms[0] = this;
+ Transform* parent = NULL;
+ for (parent = GetParent(); parent != NULL && !parent->m_HasCachedTransformMatrix; parent = parent->GetParent())
+ {
+ transforms[transformCount++] = parent;
+ // reached maximum of transforms that we can calculate - fallback to old method
+ if (transformCount == 31)
+ {
+ parent = parent->GetParent();
+ if (parent)
+ {
+ type = parent->CalculateTransformMatrixIterative(temp);
+ Assert(parent->m_HasCachedTransformMatrix);
+ }
+ break;
+ }
+ }
+
+ // storing parent of last transform (can be null), the transform itself won't be updated
+ transforms[transformCount] = parent;
+ Assert(transformCount <= 31);
+ }
+
+ // iterate transforms from lowest parent
+ for (int i = transformCount - 1; i >= 0; --i)
+ {
+ const Transform* t = transforms[i];
+ const Transform* parent = transforms[i + 1];
+ if (parent)
+ {
+ Assert(parent->m_HasCachedTransformMatrix);
+ // Build the local transform into temp
+ type |= t->CalculateLocalTransformMatrix(temp);
+ type |= (TransformType)parent->m_CachedTransformType;
+ MultiplyMatrices4x4(&parent->m_CachedTransformMatrix, &temp, &t->m_CachedTransformMatrix);
+ }
+ else
+ {
+ // Build the local transform into m_CachedTransformMatrix
+ type |= t->CalculateLocalTransformMatrix(t->m_CachedTransformMatrix);
+ }
+ // store cached transform
+ t->m_CachedTransformType = UpdateTransformType(type, t);
+ t->m_HasCachedTransformMatrix = true;
+ }
+
+ Assert(m_HasCachedTransformMatrix);
+ CopyMatrix(m_CachedTransformMatrix.GetPtr(), transform.GetPtr());
+ return (TransformType)m_CachedTransformType;
+}
+
+
+// This method doesn't cache all transforms - just the last one, but can calculate
+// more than 32 transforms. CalculateTransformMatrix caches all results
+TransformType Transform::CalculateTransformMatrixIterative (Matrix4x4f& transform) const
+{
+ if (m_HasCachedTransformMatrix)
+ {
+ CopyMatrix(m_CachedTransformMatrix.GetPtr(), transform.GetPtr());
+ return (TransformType)m_CachedTransformType;
+ }
+
+ // Build the local transform
+ TransformType type = CalculateLocalTransformMatrix(transform);
+
+ Transform* parent = GetParent ();
+ Matrix4x4f temp;
+ while (parent != NULL)
+ {
+ if (parent->m_HasCachedTransformMatrix)
+ {
+ type |= (TransformType)parent->m_CachedTransformType;
+ MultiplyMatrices4x4 (&parent->m_CachedTransformMatrix, &transform, &temp);
+ // no need to iterate further - we got world transform
+ parent = NULL;
+ }
+ else
+ {
+ Matrix4x4f parentTransform;
+ type |= parent->CalculateLocalTransformMatrix(parentTransform);
+ MultiplyMatrices4x4 (&parentTransform, &transform, &temp);
+ parent = parent->GetParent ();
+ }
+ CopyMatrix (temp.GetPtr(), transform.GetPtr());
+ }
+
+ CopyMatrix(transform.GetPtr(), m_CachedTransformMatrix.GetPtr()) ;
+ m_CachedTransformType = UpdateTransformType(type, this);
+ m_HasCachedTransformMatrix = true;
+ return type;
+}
+
+
+TransformType Transform::CalculateLocalTransformMatrix(Matrix4x4f& matrix) const
+{
+ TransformType type = (TransformType)m_InternalTransformType;
+ if (type == kNoScaleTransform)
+ matrix.SetTR(m_LocalPosition, m_LocalRotation);
+ else
+ matrix.SetTRS(m_LocalPosition, m_LocalRotation, m_LocalScale);
+ return type;
+}
+
+
+TransformType Transform::CalculateTransformMatrixDisableNonUniformScale (Matrix4x4f& transform, Matrix4x4f& scaleOnly, float& uniformScale) const
+{
+ // Use CalculateTransformMatrix in order to take advantage of the cached transform.
+ // Only non-uniform scaled meshes need to take the slower code path.
+/* TransformType cachedTransformType = CalculateTransformMatrix (transform);
+ if (!IsNonUniformScaleTransform(cachedTransformType))
+ {
+ uniformScale = Magnitude(transform.GetAxisX());
+ scaleOnly.SetIdentity();
+ return cachedTransformType;
+ }
+*/
+
+ // Build the local transform!
+ TransformType type = (TransformType)m_InternalTransformType;
+ uniformScale = m_LocalScale.x;
+ if (type == kNoScaleTransform)
+ transform.SetTR (m_LocalPosition, m_LocalRotation);
+ else
+ transform.SetTRS (m_LocalPosition, m_LocalRotation, m_LocalScale);
+
+ // @TBD: cache parent transform and reuse it across CalculateTransformXXX funcs
+ Transform* parent = GetParent ();
+ Matrix4x4f temp;
+ while (parent != NULL)
+ {
+ Matrix4x4f parentTransform;
+
+ TransformType parentType = (TransformType)parent->m_InternalTransformType;
+ // kOddNegativeScaleTransform is XOR against parent (odd+odd=even, odd+even=odd, even+even=even), other bits are OR
+ type = ((type | parentType) & ~kOddNegativeScaleTransform) | ((type ^ parentType) & kOddNegativeScaleTransform);
+
+ if (parentType == kNoScaleTransform)
+ parentTransform.SetTR (parent->m_LocalPosition, parent->m_LocalRotation);
+ else
+ parentTransform.SetTRS (parent->m_LocalPosition, parent->m_LocalRotation, parent->m_LocalScale);
+ uniformScale *= parent->m_LocalScale.x;
+
+ MultiplyMatrices4x4 (&parentTransform, &transform, &temp);
+ CopyMatrix (temp.GetPtr(), transform.GetPtr());
+ parent = parent->GetParent ();
+ }
+
+ // kNonUniformScaleTransform 'overwrites' kUniformScaleTransform
+ if ((type & kNonUniformScaleTransform) != 0)
+ type &= ~kUniformScaleTransform;
+ DebugAssert ((type & (kNonUniformScaleTransform|kUniformScaleTransform)) != (kNonUniformScaleTransform|kUniformScaleTransform));
+
+ // uniform or no scale
+ if (!IsNonUniformScaleTransform(type))
+ {
+ scaleOnly.SetIdentity();
+ return type;
+ }
+ // non-uniform scale
+ else
+ {
+ // Calculate scaleOnlyMatrix (In order to scale the mesh with the non-uniform scale)
+ Matrix4x4f worldToLocalMatrixNoScale;
+ GetWorldToLocalMatrixNoScale (worldToLocalMatrixNoScale);
+ MultiplyMatrices4x4(&worldToLocalMatrixNoScale, &transform, &scaleOnly);
+ scaleOnly.Get (0,3) = 0.0F;
+ scaleOnly.Get (1,3) = 0.0F;
+ scaleOnly.Get (2,3) = 0.0F;
+
+ // Calculate the matrix without any scale applied
+ GetLocalToWorldMatrixNoScale (transform);
+ uniformScale = 1.0F;
+
+ return type;
+ }
+}
+
+
+TransformType Transform::CalculateTransformMatrixDisableScale (Matrix4x4f& matrix) const
+{
+ Vector3f worldPos;
+ Quaternionf worldRot;
+ TransformType type = GetPositionAndRotationWithTransformType(worldPos, worldRot);
+
+ matrix.SetTR (worldPos, worldRot);
+ return type;
+}
+
+TransformType Transform::CalculateTransformMatrixScaleDelta (Matrix4x4f& m) const
+{
+ Matrix4x4f scaledMatrix;
+ TransformType type = CalculateTransformMatrix (scaledMatrix);
+ // Affine matrix?
+ if ((type & (kUniformScaleTransform | kNonUniformScaleTransform)) == 0)
+ {
+ m.SetIdentity ();
+ return type;
+ }
+ else
+ {
+ Matrix4x4f tmp;
+ GetWorldToLocalMatrixNoScale (tmp);
+ MultiplyMatrices4x4(&tmp, &scaledMatrix, &m);
+ m.Get (0,3) = 0.0F;
+ m.Get (1,3) = 0.0F;
+ m.Get (2,3) = 0.0F;
+ return type;
+ }
+}
+
+Matrix4x4f Transform::GetWorldToLocalMatrixNoScale () const
+{
+ Matrix4x4f m;
+ GetWorldToLocalMatrixNoScale(m);
+ return m;
+}
+
+const Matrix4x4f& Transform::GetWorldToLocalMatrixNoScale (Matrix4x4f& m) const
+{
+ Vector3f pos;
+ Quaternionf rot;
+ GetPositionAndRotation(pos, rot);
+ m.SetTRInverse (pos, rot);
+ return m;
+}
+
+Matrix4x4f Transform::GetLocalToWorldMatrixNoScale () const
+{
+ Matrix4x4f m;
+ GetLocalToWorldMatrixNoScale(m);
+ return m;
+}
+
+const Matrix4x4f& Transform::GetLocalToWorldMatrixNoScale (Matrix4x4f& m) const
+{
+ Quaternionf rot; Vector3f pos;
+ GetPositionAndRotation(pos, rot);
+ m.SetTR (pos, rot);
+ return m;
+}
+
+Matrix4x4f Transform::GetWorldToLocalMatrix () const
+{
+ Matrix4x4f m, temp;
+ m.SetTRInverse (m_LocalPosition, m_LocalRotation);
+ if (m_InternalTransformType != kNoScaleTransform)
+ {
+ Matrix4x4f scale;
+ scale.SetScale (InverseSafe (m_LocalScale));
+ MultiplyMatrices4x4 (&scale, &m, &temp);
+ CopyMatrix (temp.GetPtr(), m.GetPtr());
+ }
+
+ Transform* father = GetParent ();
+ if (father != NULL)
+ {
+ Matrix4x4f parentMat = father->GetWorldToLocalMatrix();
+ MultiplyMatrices4x4 (&m, &parentMat, &temp);
+ CopyMatrix (temp.GetPtr(), m.GetPtr());
+ }
+
+ return m;
+}
+
+
+Matrix4x4f Transform::GetLocalToWorldMatrix () const
+{
+ Matrix4x4f m;
+ CalculateTransformMatrix (m);
+ return m;
+}
+
+Vector3f Transform::TransformDirection (const Vector3f& inDirection) const
+{
+ return RotateVectorByQuat (GetRotation (), inDirection);
+}
+
+Vector3f Transform::InverseTransformDirection (const Vector3f& inDirection) const
+{
+ return RotateVectorByQuat (Inverse(GetRotation()), inDirection);
+}
+
+Vector3f Transform::InverseTransformPoint (const Vector3f& inPosition) const
+{
+ Vector3f newPosition, localPosition;
+ Transform* father = GetParent ();
+ if (father)
+ localPosition = father->InverseTransformPoint (inPosition);
+ else
+ localPosition = inPosition;
+
+ localPosition -= m_LocalPosition;
+ newPosition = RotateVectorByQuat (Inverse(m_LocalRotation), localPosition);
+ if (m_InternalTransformType != kNoScaleTransform)
+ newPosition.Scale (InverseSafe (m_LocalScale));
+
+ return newPosition;
+}
+
+Vector3f Transform::TransformPoint (const Vector3f& inPoint) const
+{
+ Vector3f worldPos = inPoint;
+
+ const Transform* cur = this;
+ while (cur)
+ {
+ worldPos.Scale (cur->m_LocalScale);
+ worldPos = RotateVectorByQuat (cur->m_LocalRotation, worldPos);
+ worldPos += cur->m_LocalPosition;
+
+ cur = cur->GetParent ();
+ }
+ return worldPos;
+}
+
+template<class TransferFunction> inline
+void Transform::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER_SIMPLE (m_LocalRotation);
+ TRANSFER_SIMPLE (m_LocalPosition);
+ TRANSFER_SIMPLE (m_LocalScale);
+
+ //TRANSFER_EDITOR_ONLY_HIDDEN (m_LocalEulerAnglesHint);
+
+ // This needs to be here since eg. Mesh collider queries the recalculate transform type.
+ // and awakefromload might not have been called already.
+ if (transfer.IsReading())
+ RecalculateTransformType ();
+
+ // When cloning objects for prefabs and instantiate, we don't use serialization to duplicate the hierarchy,
+ // we duplicate the hierarchy directly
+ if (SerializePrefabIgnoreProperties(transfer))
+ {
+ transfer.Transfer (m_Children, "m_Children", kHideInEditorMask | kStrongPPtrMask | kIgnoreWithInspectorUndoMask);
+ transfer.Transfer (m_Father, "m_Father", kHideInEditorMask | kIgnoreWithInspectorUndoMask);
+ }
+
+#if ENABLE_EDITOR_HIERARCHY_ORDERING
+ TRANSFER_EDITOR_ONLY_HIDDEN(m_Order);
+#endif
+}
+
+void Transform::RecalculateTransformType ()
+{
+ // #pragma message ("Compare approximately is bad due to epsilon changing with the size of the value")
+ if (CompareApproximately (m_LocalScale.x, m_LocalScale.y, 0.0001F) && CompareApproximately (m_LocalScale.y, m_LocalScale.z, 0.0001F))
+ {
+ if (CompareApproximately( m_LocalScale.x, 1.0F, 0.0001F ))
+ {
+ m_InternalTransformType = kNoScaleTransform;
+ }
+ else
+ {
+ m_InternalTransformType = kUniformScaleTransform;
+ if (m_LocalScale.x < 0.0F)
+ {
+ m_InternalTransformType = kOddNegativeScaleTransform | kNonUniformScaleTransform;
+ }
+ }
+ }
+ else
+ {
+ m_InternalTransformType = kNonUniformScaleTransform;
+
+ int hasOddNegativeScale = m_LocalScale.x * m_LocalScale.y * m_LocalScale.z < 0.0F ? 1 : 0;
+ m_InternalTransformType |= (TransformType)(hasOddNegativeScale * kOddNegativeScaleTransform);
+ }
+}
+
+void Transform::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ SetCacheDirty();
+
+ // Only call SendTransformChanged if it was really changed eg.
+ // by a propepertyeditor or datatemplate propagation but not if it was loaded from disk
+
+ if ((awakeMode & kDidLoadFromDisk) == 0)
+ {
+ // This is for all kinds of non-serialization Awakes.
+ // eg. animation. Because Transfer already recalculates
+ RecalculateTransformType ();
+
+ SendTransformChanged (kPositionChanged | kRotationChanged | kScaleChanged);
+ }
+
+ #if UNITY_EDITOR
+ if (gHierarchyChangedCallback)
+ gHierarchyChangedCallback (this);
+ #endif
+}
+
+inline void MakeValidFloat (float* f)
+{
+ if (!IsFinite (*f))
+ *f = 0.0F;
+}
+
+void Transform::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+ MakeValidFloat (&m_LocalRotation.x);
+ MakeValidFloat (&m_LocalRotation.y);
+ MakeValidFloat (&m_LocalRotation.z);
+ MakeValidFloat (&m_LocalRotation.w);
+ MakeValidFloat (&m_LocalPosition.x);
+ MakeValidFloat (&m_LocalPosition.y);
+ MakeValidFloat (&m_LocalPosition.z);
+ MakeValidFloat (&m_LocalScale.x);
+ MakeValidFloat (&m_LocalScale.y);
+ MakeValidFloat (&m_LocalScale.z);
+ m_LocalRotation = NormalizeSafe (m_LocalRotation);
+ #if UNITY_EDITOR
+ SyncLocalEulerAnglesHint ();
+ #endif
+
+ // Check if father has this as child
+ Transform* father = m_Father;
+ if (father)
+ {
+ if ( father->Find(this) == father->m_Children.end ())
+ father->m_Children.push_back (this);
+ }
+
+ // Check if all children are available and have this as father. Also make
+ // sure that any of our children is on the list exactly once.
+ for (int i=0;i<m_Children.size ();i++)
+ {
+ Transform* child = m_Children[i];
+ Assert(child != this);
+
+ if (child == NULL)
+ {
+ ErrorStringObject ("CheckConsistency: Transform child can't be loaded", this);
+ iterator it = m_Children.begin () + i;
+ m_Children.erase (it);
+ i--;
+ continue;
+ }
+
+ Transform* parent = child->m_Father;
+
+ #if UNITY_EDITOR
+ // We only try to fix the parent pointer in the Editor as we don't want to risk breaking existing players.
+ if (parent == NULL)
+ {
+ child->m_Father = this;
+ ErrorStringObject ("CheckConsistency: Restored Transform child parent pointer from NULL", child);
+ continue;
+ }
+ #endif
+
+ if (parent != this)
+ {
+ iterator it = m_Children.begin () + i;
+ m_Children.erase (it);
+ i--;
+ ErrorStringObject ("CheckConsistency: Transform child has another parent", child);
+ continue;
+ }
+
+ // Look for and remove multiple occurrences.
+ bool occursMultipleTimes = false;
+ for (int j = i + 1; j < m_Children.size ();)
+ {
+ Transform* otherChild = m_Children[j];
+ if (otherChild == child)
+ {
+ occursMultipleTimes = true;
+ m_Children.erase (m_Children.begin () + j);
+ }
+ else
+ ++j;
+ }
+ if (occursMultipleTimes)
+ {
+ ErrorStringObject ("CheckConsistency: Transform child is linked multiple times to parent; removed extraneous links from parent", child);
+ }
+ }
+}
+#if ENABLE_EDITOR_HIERARCHY_ORDERING
+void Transform::SetOrder(SInt32 order)
+{
+ m_Order = order;
+ SetDirty();
+
+ if (gHierarchyChangedCallback)
+ gHierarchyChangedCallback (this);
+
+ SendTransformChanged (kParentingChanged);
+}
+
+int Transform::CompareDepths(Transform* lhs, Transform* rhs)
+ {
+ bool orderCompare = lhs->GetOrder() != rhs->GetOrder();
+ if (orderCompare)
+ return lhs->GetOrder() < rhs->GetOrder();
+ else
+ {
+ const char* lhsName = lhs ? lhs->GetName() : "";
+ const char* rhsName = rhs ? rhs->GetName() : "";
+ return SemiNumericCompare(lhsName, rhsName) < 0;
+ }
+ }
+
+void Transform::OrderChildrenRecursively()
+{
+ std::sort(m_Children.begin(), m_Children.end(), Transform::CompareDepths);
+
+ for (int i = 0; i < m_Children.size(); ++i)
+ {
+ Transform* childTrans = m_Children[i];
+ childTrans->OrderChildrenRecursively();
+ }
+}
+#endif
+
+IMPLEMENT_OBJECT_SERIALIZE (Transform)
+IMPLEMENT_CLASS (Transform)
+
+static inline int FindSeperator (const char* in)
+{
+ const char* c = in;
+ while (*c != '/' && *c != '\0')
+ c++;
+ return c - in;
+}
+
+Transform* FindRelativeTransformWithPath (Transform& transform, const char* path)
+{
+ LIMIT_RECURSION (2000, NULL);
+
+ if (path[0] == '\0')
+ return &transform;
+
+ int seperator = FindSeperator (path);
+
+ if (path[0] == '/')
+ return FindActiveTransformWithPath (path);
+ else if (path[0] == '.' && path[1] == '.')
+ {
+ Transform* parent = transform.GetParent();
+ if (path[2] == '/')
+ {
+ if (parent)
+ return FindRelativeTransformWithPath (*parent, path + 3);
+ else
+ return NULL;
+ }
+ else if (path[2] == '\0')
+ return parent;
+ }
+
+ for (Transform::iterator i=transform.begin ();i != transform.end ();i++)
+ {
+ Transform& child = **i;
+
+ const char* name = child.GetName();
+
+ // Early out if size is not the same
+ if (strlen(name) != seperator)
+ continue;
+
+ // continue if the name isn't the same
+ const char* n = name;
+ int j;
+ for (j=0;j<seperator;j++,n++)
+ if (path[j] != *n)
+ break;
+ if (j != seperator)
+ continue;
+
+ // We found the transform we were searching for
+ if (path[seperator] == '\0')
+ return &child;
+ // Recursively find in the children
+ else
+ {
+ Transform* result = FindRelativeTransformWithPath (child, path + seperator + 1);
+ if (result != NULL)
+ return result;
+ }
+ }
+ return NULL;
+}
+
+string CalculateTransformPath (const Transform& transform, const Transform* to)
+{
+ string path;
+ const Transform* cur = &transform;
+ while (cur != to && cur != NULL)
+ {
+ if (!path.empty ())
+ path = cur->GetName () + ('/' + path);
+ else
+ path = cur->GetName ();
+ cur = cur->GetParent ();
+ }
+ return path;
+}
+
+void AppendTransformPath (string& path, const char* appendName)
+{
+ if (path.empty())
+ path = appendName;
+ else
+ {
+ path += '/';
+ path += appendName;
+ }
+}
+
+void AppendTransformPath (UnityStr& path, const char* appendName)
+{
+ if (path.empty())
+ path = appendName;
+ else
+ {
+ path += '/';
+ path += appendName;
+ }
+}
+
+
+
+Transform* FindActiveTransformWithPath (const char* path)
+{
+ bool needsToBeRoot = path[0] == '/';
+ if (path[0] == '/')
+ path++;
+
+ if (path[0] == 0)
+ return NULL;
+
+ GameObjectList::iterator i;
+
+ GameObjectList& tagged = GetGameObjectManager().m_TaggedNodes;
+ for (i=tagged.begin();i != tagged.end();i++)
+ {
+ Transform* transform = FindActiveTransformWithPathImpl(path, **i, needsToBeRoot);
+ if (transform)
+ return transform;
+ }
+
+ GameObjectList& active = GetGameObjectManager().m_ActiveNodes;
+ for (i=active.begin();i != active.end();i++)
+ {
+ Transform* transform = FindActiveTransformWithPathImpl(path, **i, needsToBeRoot);
+ if (transform)
+ return transform;
+ }
+
+ return NULL;
+/*
+
+ for (int i=0;i<gos.size ();i++)
+ {
+ if (!gos[i]->IsActive ())
+ continue;
+
+ const string& name = gos[i]->GetName ();
+ if (name.find (path, 0, name.size ()) == 0)
+ {
+ path += name.size ();
+ if (path[0] == '/')
+ path ++;
+
+ Transform* transform = gos[i]->QueryComponent (Transform);
+ if (transform)
+ {
+ if (needsToBeRoot && transform->GetParent())
+ continue;
+
+ if (path[0] == 0)
+ return transform;
+ transform = FindRelativeTransformWithPath (*transform, path);
+ if (transform)
+ return transform;
+ }
+ }
+ }
+
+ return NULL;
+*/
+}
+
+static Transform* FindActiveTransformWithPathImpl (const char* path, GameObject& go, bool needsToBeRoot)
+{
+ AssertIf(!go.IsActive());
+
+ const char* name = go.GetName ();
+ size_t size = strlen(name);
+
+ if (strncmp(name, path, size) == 0)
+ {
+ path += size;
+ if (path[0] == '/')
+ path ++;
+
+ Transform* transform = go.QueryComponent (Transform);
+ if (transform)
+ {
+ if (needsToBeRoot && transform->GetParent())
+ return NULL;
+
+ if (path[0] == 0 && transform->IsActive())
+ return transform;
+
+ transform = FindRelativeTransformWithPath (*transform, path);
+ if (transform && transform->IsActive())
+ return transform;
+ }
+ }
+ return NULL;
+}
+
+Transform& Transform::GetRoot ()
+{
+ Transform* cur = this;
+ Transform* curParent = NULL;
+ while ((curParent = cur->GetParent ()) != NULL)
+ cur = curParent;
+
+ return *cur;
+}
+
+bool IsChildOrSameTransform(Transform& transform, Transform& inParent)
+{
+ Transform* child = &transform;
+ while (child)
+ {
+ if (child == &inParent)
+ return true;
+ child = child->GetParent();
+ }
+ return false;
+}
+
+#if ENABLE_EDITOR_HIERARCHY_ORDERING
+void Transform::GetSortedChildList (Transform::TransformComList& sortedChildren) const
+{
+ sortedChildren.assign(m_Children.begin(), m_Children.end());
+ std::sort(sortedChildren.begin(), sortedChildren.end(), Transform::CompareDepths);
+}
+#endif
+
+void Transform::SetLocalTRS (const Vector3f& pos, const Quaternionf& rot, const Vector3f& scale)
+{
+ ABORT_INVALID_VECTOR3 (pos, localPosition, transform);
+ ABORT_INVALID_QUATERNION (rot, localRotation, transform);
+ m_LocalRotation = NormalizeSafe(rot);
+ m_LocalPosition = pos;
+ m_LocalScale = scale;
+ #if UNITY_EDITOR
+ SyncLocalEulerAnglesHint ();
+ #endif
+ RecalculateTransformType();
+ SendTransformChanged(kPositionChanged | kRotationChanged | kScaleChanged);
+}
+
+int GetTransformDepth(Transform& transform)
+{
+ Transform* parent = transform.GetParent();
+ int depth = 0;
+ while (parent)
+ {
+ depth++;
+ parent = parent->GetParent();
+ }
+ return depth;
+}
+
+Transform* FindTransformWithName(Transform* root, const char* name)
+{
+ if (strcmp(root->GetName(), name) == 0)
+ return root;
+ else
+ {
+ for (int i = 0; i < root->GetChildrenCount(); i++)
+ {
+ Transform* ret = FindTransformWithName(&(root->GetChild(i)), name);
+ if (ret)
+ return ret;
+ }
+ return NULL;
+ }
+}
diff --git a/Runtime/Graphics/Transform.h b/Runtime/Graphics/Transform.h
new file mode 100644
index 0000000..2e1cf66
--- /dev/null
+++ b/Runtime/Graphics/Transform.h
@@ -0,0 +1,327 @@
+#ifndef TRANSFORM_H
+#define TRANSFORM_H
+
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/EnumFlags.h"
+#include "Runtime/Modules/ExportModules.h"
+using std::vector; //TODO:refactor
+
+/** Transformcomponent stores rotation and position of objects
+ * A Transformcomponent can also have child Transformcomponent's,
+ * which then rotate and translate relative to their father.
+
+ * Local space is the space which is relative to its father while world space is an absolute space
+ * Local and Worldspace is the same if the transformcomponent does not have a father.
+ * The localspace functions are faster than the worldspace functions
+ */
+
+class EXPORT_COREMODULE Transform : public Unity::Component
+{
+ public:
+
+ typedef dynamic_array<ImmediatePtr<Transform> > TransformComList;
+ typedef TransformComList::iterator iterator;
+
+ typedef void TransformChangedCallback (Transform* t);
+ typedef void HierarchyChangedCallback (Transform* t);
+ typedef void HierarchyChangedCallbackSetParent (Transform* obj, Transform* oldParent, Transform* newParent);
+
+private:
+ Quaternionf m_LocalRotation;
+ Vector3f m_LocalPosition;
+ Vector3f m_LocalScale;
+
+ mutable Matrix4x4f m_CachedTransformMatrix;
+ mutable UInt8 m_CachedTransformType;
+ mutable UInt8 m_HasCachedTransformMatrix;
+ mutable UInt8 m_HasChanged;
+
+public:
+ // This tracks kNoScaleTransform, kUniformScaleTransform and kNonUniformScaleTransform, kHasNegativeScale
+ UInt8 m_InternalTransformType;
+ UInt8 m_SupportsTransformChanged;
+
+ TransformComList m_Children;
+ ImmediatePtr<Transform> m_Father;
+
+ #if UNITY_EDITOR
+
+ #if ENABLE_EDITOR_HIERARCHY_ORDERING
+ typedef std::pair<std::string, int> VisibleRootSecondaryKey;
+ typedef std::pair<SInt32, VisibleRootSecondaryKey> VisibleRootKey;
+ #else
+ typedef std::pair<std::string, int> VisibleRootKey;
+ #endif
+
+ struct CompareVisibleRoots
+ {
+ bool operator() (const VisibleRootKey& lhs, const VisibleRootKey& rhs) const;
+ };
+ typedef std::map<VisibleRootKey, Transform*, CompareVisibleRoots> VisibleRootMap;
+ VisibleRootMap::iterator m_VisibleRootIterator;
+ bool m_VisibleRootValid;
+ Vector3f m_LocalEulerAnglesHint;
+ #endif
+
+ #if ENABLE_EDITOR_HIERARCHY_ORDERING
+ SInt32 m_Order;
+ #endif
+
+public:
+
+ REGISTER_DERIVED_CLASS (Transform, Component)
+ DECLARE_OBJECT_SERIALIZE (Transform)
+
+ Transform (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~Transform (); declared-by-macro
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+ virtual void CheckConsistency ();
+ virtual void SupportedMessagesDidChange (int mask);
+
+ // Tag class as sealed, this makes QueryComponent faster.
+ static bool IsSealedClass () { return true; }
+
+ /// Returns a ptr to the father transformcomponent (NULL if no father)
+ Transform *GetParent () const { return m_Father; }
+ /// Returns a reference to the root transform (top most transform with no parent)
+ Transform& GetRoot ();
+
+ /// Finds given transform
+ iterator Find( const Transform* child );
+
+ /// access to the children
+ int GetChildrenCount ()const { return m_Children.size (); }
+ Transform &GetChild (int i) const { Assert (i < (int)m_Children.size()); return *m_Children[i]; }
+ iterator begin () { return m_Children.begin (); }
+ iterator end () { return m_Children.end (); }
+
+ /// Sets the father to p(if p is invalid the Transformcomponent will have no father)
+ /// Returns false if the father could not be set
+ /// This happens only if you are trying to set the father to one of its direct/indirect children.
+ enum SetParentOption { kWorldPositionStays = 1 << 0, kLocalPositionStays = 1 << 1, kAllowParentingFromPrefab = 1 << 2, kDisableTransformMessage = 1 << 3 };
+ bool SetParent (Transform * parent, SetParentOption options = kWorldPositionStays);
+
+ /// Sets the rotation in local space
+ void SetLocalRotation (const Quaternionf& rotation);
+ /// Sets the Rotation in world space
+ void SetRotation (const Quaternionf& rotation);
+ /// Sets the local euler angles
+ void SetLocalEulerAngles (const Vector3f& eulerAngles);
+
+ /// Sets the position in local space
+ /// (If the object has no father, localspace is basically the same as world space)
+ void SetLocalPosition (const Vector3f& inTranslation);
+ /// Sets the position in world space
+ void SetPosition (const Vector3f& position);
+
+ /// Sets the position - a local space offset will be scaled, rotated and subtracted from the position.
+ /// Used to set the position For CharacterController and NavMeshAgent that have baseOffset / center.
+ /// For retreiving the position with a local offset use: TransformPoint (localOffset)
+ void SetPositionWithLocalOffset (const Vector3f& p, const Vector3f& localOffset);
+
+ /// Transforms local space position to world space - while applying an offset which is scaled and rotated accordingly.
+ Vector3f TransformPointWithLocalOffset (const Vector3f& p, const Vector3f& localOffset) const;
+
+ /// Same as above but normalizes the quaternion
+ void SetLocalRotationSafe (const Quaternionf& rotation);
+ void SetRotationSafe (const Quaternionf& rotation);
+
+ /// Sets the scale in local space
+ void SetLocalScale (const Vector3f& scale);
+ /// Sets the scale from a rotation * scale
+ /// The transform can not hold the full scale if it contains skew
+ void SetWorldRotationAndScale (const Matrix3x3f& worldRotationAndScale);
+
+ /// Sets the world position and rotation
+ void SetPositionAndRotation (const Vector3f& position, const Quaternionf& rotation);
+ void SetLocalPositionAndRotation (const Vector3f& position, const Quaternionf& rotation);
+
+ /// Return matrix that converts a point from World To Local space
+ Matrix4x4f GetWorldToLocalMatrix () const;
+ /// Return matrix that converts a point from Local To World space
+ Matrix4x4f GetLocalToWorldMatrix () const;
+
+ Matrix4x4f GetWorldToLocalMatrixNoScale () const;
+ const Matrix4x4f& GetWorldToLocalMatrixNoScale (Matrix4x4f& m) const;
+ Matrix4x4f GetLocalToWorldMatrixNoScale () const;
+ const Matrix4x4f& GetLocalToWorldMatrixNoScale (Matrix4x4f& m) const;
+
+ TransformType CalculateTransformMatrix (Matrix4x4f& matrix) const;
+
+ TransformType CalculateTransformMatrixScaleDelta (Matrix4x4f& matrix) const;
+
+ TransformType CalculateTransformMatrixDisableNonUniformScale (Matrix4x4f& matrix, Matrix4x4f& scaleOnly, float& scale) const;
+ TransformType CalculateTransformMatrixDisableScale (Matrix4x4f& matrix) const;
+
+ /// Whether the transform has changed since the last time this flag was set to 'false'.
+ bool GetChangedFlag () { return m_HasChanged; }
+ /// Sets the flag indicating whether the transform has changed. Most commonly used to simply set it to 'false'.
+ void SetChangedFlag (bool val) { m_HasChanged = val; }
+
+ /// Gets the rotation from local to world space
+ Quaternionf GetRotation () const;
+ /// Gets the local rotation
+ Quaternionf GetLocalRotation () const { return m_LocalRotation; }
+ /// Gets the local euler angles (in the editor it is first ensures that they are in sync with the local rotation quaternion)
+ Vector3f GetLocalEulerAngles ();
+
+ /// Gets the local position relative to the father
+ Vector3f GetLocalPosition () const {return m_LocalPosition;}
+ /// Gets the position in world space
+ Vector3f GetPosition () const;
+
+ /// Returns the local scale
+ Vector3f GetLocalScale () const { return m_LocalScale; }
+ /// Returns the world rotation and scale.
+ /// (It is impossible to return a Vector3 because the scale might be skewed)
+ Matrix3x3f GetWorldRotationAndScale () const;
+
+ Matrix3x3f GetWorldScale () const;
+
+ Vector3f GetWorldScaleLossy () const;
+
+ /// Rotates the transform around axis by rad
+ void RotateAroundLocal (const Vector3f& localAxis, float rad);
+ /// Same, but normalizes the axis for you
+ void RotateAroundLocalSafe (const Vector3f& localAxis, float rad);
+ /// Rotates the transform around axis by rad
+ void RotateAround (const Vector3f& worldAxis, float rad);
+ /// Same, but normalizes the axis for you
+ void RotateAroundSafe (const Vector3f& worldAxis, float rad);
+
+
+ /// transforms a point from localspace to worldspace
+ Vector3f TransformPoint (const Vector3f& inPoint) const;
+ /// Transforms a direction from localspace to worldspace
+ /// (Ignores scale)
+ Vector3f TransformDirection (const Vector3f& inDirection) const;
+
+ /// Transforms a point from worldspace to localspace
+ Vector3f InverseTransformPoint (const Vector3f& inDirection) const;
+ /// Transforms a direction from worldspace to localspace
+ /// (Ignores scale)
+ Vector3f InverseTransformDirection (const Vector3f& inDirection) const;
+
+ #if UNITY_EDITOR
+ /// Register a function which is called whenever a transformcomponent is changed
+ static void RegisterHierarchyChangedCallback (HierarchyChangedCallback* callback);
+ static void RegisterHierarchyChangedSetParentCallback (HierarchyChangedCallbackSetParent* callback);
+
+ VisibleRootMap::iterator* GetVisibleRootIterator () { return m_VisibleRootValid ? &m_VisibleRootIterator : NULL; }
+ void SetVisibleRootIterator (const VisibleRootMap::iterator& it) { m_VisibleRootIterator = it; m_VisibleRootValid = true; }
+ void ClearVisibleRootIterator () { m_VisibleRootValid = false; }
+ #endif // End UNITY_EDITOR
+
+ #if ENABLE_EDITOR_HIERARCHY_ORDERING
+ static int CompareDepths(Transform* lhs, Transform* rhs);
+
+ SInt32 GetOrder () const { return m_Order; }
+ void SetOrder (SInt32 order);
+ void OrderChildrenRecursively();
+ void GetSortedChildList (TransformComList& sortedChildren) const;
+ #endif
+
+ TransformComList& GetChildrenInternal () { return m_Children; }
+ const TransformComList& GetChildrenInternal () const { return m_Children; }
+ ImmediatePtr<Transform>& GetParentPtrInternal () { return m_Father; }
+
+ /// Reset position&rotation
+ void Reset ();
+
+ /// Sets the world position and rotation without sending out a TransformChanged message to the gameobject and without setting dirty
+ /// (Not sending TransformChanged will result in the Renderer
+ /// not updating the AABB to reflect the transform change)
+ void SetPositionAndRotationWithoutNotification (const Vector3f& position, const Quaternionf& q);
+ void SetPositionWithoutNotification (const Vector3f& position);
+ void SetRotationWithoutNotification (const Quaternionf& q);
+ void SetPositionAndRotationSafeWithoutNotification (const Vector3f& p, const Quaternionf& q);
+
+ void SetPositionAndRotationSafe (const Vector3f& p, const Quaternionf& q);
+
+ void GetPositionAndRotation (Vector3f& pos, Quaternionf& q)const;
+ TransformType GetPositionAndRotationWithTransformType (Vector3f& worldPos, Quaternionf& worldRot) const;
+
+ /// You seldomly want to call this function yourself.
+ /// Sends the transform changed message to itself and all children.
+ /// A bitmask specifies which components have changed!
+ /// kParentingChanged is also called when SetOrder(SInt32) is called. (Editor Only)
+ enum { kPositionChanged = 1 << 0, kRotationChanged = 1 << 1, kScaleChanged = 1 << 3, kAnimatePhysics = 1 << 4, kParentingChanged = 1 << 5};
+ void SendTransformChanged (int mask);
+
+ /// private but used by datatemplates
+ void RemoveFromParent ();
+ void ClearChildrenParentPointer ();
+ void ClearChild (Transform* child);
+
+ /// Broadcasts a message to this and all child transforms
+ void BroadcastMessageAny(const MessageIdentifier& message, MessageData& data);
+
+ void SetLocalTRS (const Vector3f& pos, const Quaternionf& rot, const Vector3f& scale);
+
+ inline void SetLocalRotationWithoutNotification (const Quaternionf& rotation)
+ {
+ m_LocalRotation = rotation;
+ #if UNITY_EDITOR
+ SyncLocalEulerAnglesHint ();
+ #endif
+ }
+
+ inline void SetLocalPositionWithoutNotification (const Vector3f& inTranslation)
+ {
+ m_LocalPosition = inTranslation;
+ }
+
+ inline void SetLocalScaleWithoutNotification (const Vector3f& scale)
+ {
+ m_LocalScale = scale;
+ RecalculateTransformType ();
+ }
+
+ // For use only by the animation system
+ void RecalculateTransformType ();
+
+ UInt32 CalculateSupportedMessages ();
+
+ void MakeEditorValuesLookNice();
+
+private:
+
+ friend class AnimationBinder;
+ friend void SampleAnimation (GameObject& go, class AnimationClip& clip, float inTime, int wrapMode, float deltaTime);
+
+ TransformType CalculateLocalTransformMatrix(Matrix4x4f& matrix) const;
+ TransformType CalculateTransformMatrixIterative (Matrix4x4f& matrix) const;
+
+ void SetCacheDirty();
+
+ template<bool Safe, bool Notify>
+ void SetPositionAndRotationInternal( const Vector3f& position, const Quaternionf& rotation );
+
+ #if UNITY_EDITOR
+ /// Makes the local euler angles be in sync with the quaternion rotation
+ void SyncLocalEulerAnglesHint ();
+ #endif
+};
+
+ENUM_FLAGS(Transform::SetParentOption);
+
+Transform* FindRelativeTransformWithPath (Transform& transform, const char* path);
+
+Transform* FindActiveTransformWithPath (const char* path);
+
+Transform* FindTransformWithName (Transform* root, const char* name);
+
+std::string CalculateTransformPath (const Transform& transform, const Transform* to);
+void AppendTransformPath (string& path, const char* appendName);
+void AppendTransformPath (UnityStr& path, const char* appendName);
+
+/// Is transform a child of parent? Or is the transform the same.
+bool IsChildOrSameTransform(Transform& transform, Transform& parent);
+
+EXPORT_COREMODULE int GetTransformDepth(Transform& transform);
+
+
+#endif
diff --git a/Runtime/Graphics/TransformTests.cpp b/Runtime/Graphics/TransformTests.cpp
new file mode 100644
index 0000000..7d6e870
--- /dev/null
+++ b/Runtime/Graphics/TransformTests.cpp
@@ -0,0 +1,102 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Transform.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Testing/TestFixtures.h"
+#include "Runtime/Testing/Testing.h"
+#include "Editor/Src/Prefabs/Prefab.h"
+
+
+class Prefab;
+
+SUITE (TransformTests)
+{
+ class TransformFixture : TestFixtureBase
+ {
+ protected:
+ Transform* MakeTransform ()
+ {
+ Transform* tf = NewTestObject<Transform> ();
+ GameObject* go = NewTestObject<GameObject> ();
+ go->AddComponentInternal (tf);
+ return tf;
+ }
+
+ Prefab* MakePrefabParent ()
+ {
+ Prefab* prefab = NewTestObject<Prefab> ();
+ prefab->m_IsPrefabParent = true;
+ return prefab;
+ }
+ };
+
+ TEST_FIXTURE (TransformFixture, Has_Null_Parrent_By_Default)
+ {
+ Transform* transform = MakeTransform ();
+ CHECK_EQUAL ((Transform*)0, transform->GetParent ());
+ }
+
+ TEST_FIXTURE (TransformFixture, SetParent_Returns_True_If_New_Parent_Is_Null)
+ {
+ Transform* transform = MakeTransform ();
+ CHECK_EQUAL (true, transform->SetParent (0));
+ }
+
+ TEST_FIXTURE (TransformFixture, SetParent_Returns_False_When_GameObject_Is_Being_Destroyed)
+ {
+ Transform* transform = MakeTransform ();
+ Transform* parent = MakeTransform ();
+ transform->GetGameObject().WillDestroyGameObject ();
+ CHECK_EQUAL (false, transform->SetParent (parent));
+ }
+
+ TEST_FIXTURE (TransformFixture, SetParent_Returns_False_When_GameObject_Of_New_Parent_Is_Being_Destroyed)
+ {
+ Transform* transform = MakeTransform ();
+ Transform* parent = MakeTransform ();
+ parent->GetGameObject().WillDestroyGameObject ();
+ CHECK_EQUAL (false, transform->SetParent (parent));
+ }
+
+ TEST_FIXTURE (TransformFixture, SetParent_Returns_False_If_New_Parent_Is_A_Child)
+ {
+ Transform* parent = MakeTransform ();
+ Transform* child = MakeTransform ();
+ child->SetParent (parent);
+ CHECK_EQUAL (false, parent->SetParent (child));
+ }
+
+ #if UNITY_EDITOR
+ TEST_FIXTURE (TransformFixture, SetParent_With_Disable_Parenting_From_Prefab_Returns_False_If_Tranform_Resides_In_Prefab_EditorOnly)
+ {
+ Transform* transform = MakeTransform ();
+ transform->m_Prefab = PPtr<Prefab> (MakePrefabParent ());
+
+ EXPECT (Error, "Setting the parent of a transform which resides in a prefab is disabled");
+ CHECK_EQUAL (false, transform->SetParent (MakeTransform(), Transform::kWorldPositionStays));
+ }
+ #endif
+
+ TEST (ChildPosition_Test)
+ {
+ GameObject& go = CreateGameObject ("parent", "Transform", NULL);
+ go.GetComponent (Transform).SetLocalScale (Vector3f (1.0F, 1.0F, 0.1F));
+
+ GameObject& child = CreateGameObject ("child", "Transform", NULL);
+
+ child.GetComponent (Transform).SetParent (&go.GetComponent (Transform), Transform::kLocalPositionStays);
+
+ child.GetComponent (Transform).SetLocalPosition (Vector3f (0.0F, 0.0F, 10.0F));
+ child.GetComponent (Transform).SetLocalScale (Vector3f (1.0F, 1.0F, 1.0F));
+ child.GetComponent (Transform).SetLocalRotation (Quaternionf::identity ());
+
+ CHECK ( CompareApproximately (child.GetComponent (Transform).GetPosition (), Vector3f (0.0F,0.0F,1.0F)) );
+
+ DestroyObjectHighLevel (&go);
+ }
+}
+
+#endif // ENABLE_UNIT_TESTS
diff --git a/Runtime/Graphics/TriStripper.cpp b/Runtime/Graphics/TriStripper.cpp
new file mode 100644
index 0000000..89d6c1b
--- /dev/null
+++ b/Runtime/Graphics/TriStripper.cpp
@@ -0,0 +1,112 @@
+#include "UnityPrefix.h"
+#include "External/Tristripper/Striper.h"
+#include "TriStripper.h"
+#include "Runtime/Filters/Mesh/Mesh.h"
+#include "Runtime/Utilities/vector_utility.h"
+#include "Runtime/Utilities/LogAssert.h"
+
+using namespace std;
+
+
+bool Stripify (const UInt32* faces, int count, UNITY_TEMP_VECTOR(UInt32)& strip)
+{
+ Assert (faces != NULL || !count);
+ strip.clear ();
+
+ // Fail on empty input.
+ if (!count)
+ return false;
+
+ STRIPERCREATE stripinput;
+ stripinput.DFaces = const_cast<UInt32*> (faces);
+ stripinput.NbFaces = count / 3;
+
+ STRIPERRESULT stripresult;
+ Striper striper;
+ if (striper.Init (stripinput) && striper.Compute (stripresult) && stripresult.NbStrips == 1)
+ {
+ int stripSize = stripresult.StripLengths[0];
+ UInt32* stripData = reinterpret_cast<UInt32*> (stripresult.StripRuns);
+ reserve_trimmed (strip, stripSize);
+ strip.assign (stripData, stripData + stripSize);
+ return true;
+ }
+ return false;
+}
+
+template<typename T>
+int CountTrianglesInStrip (const T* strip, int length)
+{
+ int count = 0;
+ // de-stripify :
+ int n = length;
+ for(int i=0;i<(n-2);i++)
+ {
+ T a = strip[i];
+ T b = strip[i+1];
+ T c = strip[i+2];
+
+ // skip degenerates
+ if ( a == b || a == c || b == c )
+ continue;
+
+ count++;
+ }
+ return count;
+}
+
+template int CountTrianglesInStrip<UInt16> (const UInt16* strip, int length);
+template int CountTrianglesInStrip<UInt32> (const UInt32* strip, int length);
+
+// WARNING: You have to make sure you have space for all indices in trilist (you can use CountTrianglesInStrip for that)
+template<typename Tin, typename Tout>
+void Destripify(const Tin* strip, int length, Tout* trilist, int capacity)
+{
+ // de-stripify :
+ int n = length;
+ int triIndex = 0;
+ for(int i=0;i<(n-2);i++)
+ {
+ Tin a = strip[i];
+ Tin b = strip[i+1];
+ Tin c = strip[i+2];
+
+ // skip degenerates
+ if ( a == b || a == c || b == c )
+ continue;
+
+ // do the winding flip-flop of strips :
+ if ( (i&1) == 1 )
+ std::swap (a,b);
+
+ trilist[triIndex++] = a;
+ trilist[triIndex++] = b;
+ trilist[triIndex++] = c;
+ }
+}
+
+
+template void Destripify<UInt32, UInt32> (const UInt32* strip, int length, UInt32* trilist, int capacity);
+template void Destripify<UInt16, UInt32> (const UInt16* strip, int length, UInt32* trilist, int capacity);
+template void Destripify<UInt16, UInt16> (const UInt16* strip, int length, UInt16* trilist, int capacity);
+
+void Destripify (const UInt16* strip, int length, UNITY_TEMP_VECTOR(UInt16)& trilist)
+{
+ int oldSize = trilist.size();
+ trilist.resize (oldSize + CountTrianglesInStrip(strip, length) * 3);
+ Destripify(strip, length, &trilist[oldSize], trilist.size());
+}
+
+void Destripify (const UInt16* strip, int length, UNITY_TEMP_VECTOR(UInt32)& trilist)
+{
+ int oldSize = trilist.size();
+ trilist.resize (oldSize + CountTrianglesInStrip(strip, length) * 3);
+ Destripify(strip, length, &trilist[oldSize], trilist.size());
+}
+
+void Destripify (const UInt32* strip, int length, UNITY_TEMP_VECTOR(UInt32)& trilist)
+{
+ int oldSize = trilist.size();
+ trilist.resize (oldSize + CountTrianglesInStrip(strip, length) * 3);
+ Destripify(strip, length, &trilist[oldSize], trilist.size());
+}
diff --git a/Runtime/Graphics/TriStripper.h b/Runtime/Graphics/TriStripper.h
new file mode 100644
index 0000000..63d02fe
--- /dev/null
+++ b/Runtime/Graphics/TriStripper.h
@@ -0,0 +1,22 @@
+#ifndef TRISTRIPPER_H
+#define TRISTRIPPER_H
+
+#include <vector>
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Modules/ExportModules.h"
+
+bool EXPORT_COREMODULE Stripify (const UInt32* faces, int count, UNITY_TEMP_VECTOR(UInt32)& strip);
+
+/// Destrips a triangle strip into a face list.
+/// Adds the triangles from the strip into the trilist
+void EXPORT_COREMODULE Destripify (const UInt32* strip, int length, UNITY_TEMP_VECTOR(UInt32)& trilist);
+void EXPORT_COREMODULE Destripify (const UInt16* strip, int length, UNITY_TEMP_VECTOR(UInt32)& trilist);
+void EXPORT_COREMODULE Destripify (const UInt16* strip, int length, UNITY_TEMP_VECTOR(UInt16)& trilist);
+
+template<typename Tin, typename Tout>
+void EXPORT_COREMODULE Destripify(const Tin* strip, int length, Tout* trilist, int capacity);
+
+template<typename T>
+int EXPORT_COREMODULE CountTrianglesInStrip (const T* strip, int length);
+
+#endif
diff --git a/Runtime/Graphs/UnityEngine.Graphs/AnimationNodeLibrary.cs b/Runtime/Graphs/UnityEngine.Graphs/AnimationNodeLibrary.cs
new file mode 100644
index 0000000..7ee2299
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/AnimationNodeLibrary.cs
@@ -0,0 +1,22 @@
+#if UNITY_ANIMATION_GRAPH
+
+using UnityEngine.Graphs.LogicGraph;
+using UnityEngine;
+
+namespace UnityEngine.Graphs
+{
+ public class AnimationNodeLibrary
+ {
+ [AnimationLogic(typeof(Animation))]
+ public static void SetSpeed1 (Animation self, string animName, float speed)
+ {
+ // Sanity check
+ if (self)
+ self[animName].speed = speed;
+ else
+ Debug.LogWarning("SetSpeed self parameter is null");
+ }
+ }
+}
+
+#endif
diff --git a/Runtime/Graphs/UnityEngine.Graphs/Attributes.cs b/Runtime/Graphs/UnityEngine.Graphs/Attributes.cs
new file mode 100644
index 0000000..1e6bca9
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/Attributes.cs
@@ -0,0 +1,96 @@
+using System;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
+ public class SettingAttribute : Attribute { }
+
+ // TODO : should be abstract
+ // for almost everything logic graph related (classes, functions, variables, ...)
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Event | AttributeTargets.Field | AttributeTargets.Property)]
+ public class CodeGeneratingLogicAttribute : Attribute
+ {
+ public Type type;
+ public Type inputsType;
+ public Type stateType;
+ }
+
+ // for almost everything logic graph related (classes, functions, variables, ...)
+ public class LogicAttribute : CodeGeneratingLogicAttribute
+ {
+ public LogicAttribute() { type = null; }
+ public LogicAttribute(Type type) { this.type = type; }
+ public LogicAttribute(Type type, Type inputsType)
+ {
+ base.type = type;
+ base.inputsType = inputsType;
+ }
+ public LogicAttribute(Type type, Type inputsType, Type stateType)
+ {
+ base.type = type;
+ base.inputsType = inputsType;
+ base.stateType = stateType;
+ }
+ }
+
+ // for evaluator nodes
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
+ public class LogicEvalAttribute : Attribute
+ {
+ public Type type;
+ public Type stateType;
+
+ public LogicEvalAttribute() { type = null; }
+ public LogicEvalAttribute(Type type) { this.type = type; }
+ public LogicEvalAttribute(Type type, Type stateType)
+ {
+ this.type = type;
+ this.stateType = stateType;
+ }
+ }
+
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field)]
+ public class LogicExpressionAttribute: Attribute
+ {
+ public string name;
+ public LogicExpressionAttribute() { name = string.Empty;}
+ public LogicExpressionAttribute(string name) { this.name = name; }
+ }
+
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+ public class LogicTargetAttribute : Attribute { }
+
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue)]
+ public class TitleAttribute : Attribute
+ {
+ public string title;
+ public TitleAttribute(string title) { this.title = title; }
+ }
+
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Event | AttributeTargets.Field | AttributeTargets.Property)]
+ public class ValidateAttribute : Attribute
+ {
+ public string validateFunction;
+ public ValidateAttribute(string validateFunction) { this.validateFunction = validateFunction; }
+ }
+
+ #if UNITY_ANIMATION_GRAPH
+ // for almost everything logic graph related (classes, functions, variables, ...)
+ public class AnimationLogicAttribute : CodeGeneratingLogicAttribute
+ {
+ public AnimationLogicAttribute() { type = null; }
+ public AnimationLogicAttribute(Type type) { base.type = type; }
+ public AnimationLogicAttribute(Type type, Type inputsType)
+ {
+ base.type = type;
+ base.inputsType = inputsType;
+ }
+ public AnimationLogicAttribute(Type type, Type inputsType, Type stateType)
+ {
+ base.type = type;
+ base.inputsType = inputsType;
+ base.stateType = stateType;
+ }
+ }
+ #endif
+}
diff --git a/Runtime/Graphs/UnityEngine.Graphs/DefaultValueAttribute.cs b/Runtime/Graphs/UnityEngine.Graphs/DefaultValueAttribute.cs
new file mode 100644
index 0000000..2dd334a
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/DefaultValueAttribute.cs
@@ -0,0 +1,33 @@
+using System;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ [AttributeUsage(AttributeTargets.All)]
+ public class DefaultValueAttribute : Attribute
+ {
+ private object m_Value;
+ private Type m_Type;
+
+ public object value
+ {
+ get { return m_Value; }
+ }
+
+ public Type type
+ {
+ get { return m_Type; }
+ }
+
+ public DefaultValueAttribute(object value)
+ {
+ m_Value = value;
+ }
+
+ public DefaultValueAttribute(Type type, string value)
+ {
+ m_Value = value;
+ m_Type = type;
+ }
+ }
+}
+
diff --git a/Runtime/Graphs/UnityEngine.Graphs/GraphBehaviour.cs b/Runtime/Graphs/UnityEngine.Graphs/GraphBehaviour.cs
new file mode 100644
index 0000000..2330c66
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/GraphBehaviour.cs
@@ -0,0 +1,121 @@
+using System;
+using UnityEngine;
+
+#if TESTING
+using Object = UnityEngine.Graphs.Testing.Object;
+#else
+using Object = UnityEngine.Object;
+#endif
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public interface IMonoBehaviourEventCaller
+ {
+ event GraphBehaviour.VoidDelegate OnAwake;
+ event GraphBehaviour.VoidDelegate OnStart;
+ event GraphBehaviour.VoidUpdateDelegate OnUpdate;
+ event GraphBehaviour.VoidUpdateDelegate OnLateUpdate;
+ event GraphBehaviour.VoidUpdateDelegate OnFixedUpdate;
+ }
+
+ //TODO: we can optimize this, now say Update will get called always for each graph even though nothing is using it
+ // we can generate code for those in generated graph instead
+
+ // Component that represents graph in the hierarchy.
+ // When used in editor it stores all LogicGraph state as well, but since those are editor side classes they get stripped out when the player is built.
+ public class GraphBehaviour :
+ #if TESTING
+ Object, IMonoBehaviourEventCaller
+ #else
+ MonoBehaviour, IMonoBehaviourEventCaller
+ #endif
+ {
+ [NonSerialized]
+ private bool m_IsInitialized;
+
+ #region Nodes
+ public delegate void VoidDelegate ();
+ public delegate void VoidUpdateDelegate (float deltaTime);
+
+ [Logic]
+ [Title("Flow Events/On Awake")]
+ public event VoidDelegate OnAwake;
+
+ [Logic]
+ [Title("Flow Events/On Start")]
+ public event VoidDelegate OnStart;
+
+ [Logic]
+ [Title("Flow Events/On Update")]
+ public event VoidUpdateDelegate OnUpdate;
+
+ [Logic]
+ [Title("Flow Events/On Late Update")]
+ public event VoidUpdateDelegate OnLateUpdate;
+
+ [Logic]
+ [Title("Flow Events/On Fixed Update")]
+ public event VoidUpdateDelegate OnFixedUpdate;
+
+ protected virtual void @ΣInit()
+ {
+ }
+
+ private void Initialize()
+ {
+ if (m_IsInitialized)
+ return;
+ m_IsInitialized = true;
+ @ΣInit();
+ }
+
+ public void OnEnable()
+ {
+ Initialize ();
+ }
+
+ public void Awake ()
+ {
+ Initialize ();
+
+ if (OnAwake != null)
+ OnAwake();
+ }
+
+ public void Start ()
+ {
+ if (OnStart != null)
+ OnStart ();
+ }
+
+ public void Update ()
+ {
+ if (OnUpdate != null)
+ OnUpdate (Time.deltaTime);
+ }
+
+ public void LateUpdate ()
+ {
+ if (OnLateUpdate != null)
+ OnLateUpdate (Time.deltaTime);
+ }
+
+ public void FixedUpdate ()
+ {
+ if (OnFixedUpdate != null)
+ OnFixedUpdate (Time.deltaTime);
+ }
+ #endregion
+
+ protected void PullSceneReferenceVariables(string references)
+ {
+ foreach (var asm in System.AppDomain.CurrentDomain.GetAssemblies())
+ if (asm.GetName().Name == "UnityEditor.Graphs.LogicGraph")
+ {
+ var method = asm.GetType("UnityEditor.Graphs.LogicGraph.CompilerUtils").GetMethod("SetEditorModeGeneratedGraphSceneReferences");
+ method.Invoke(null, new object[] {this, references});
+ break;
+ }
+ }
+ }
+}
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Animation/AnimationNodes.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Animation/AnimationNodes.cs
new file mode 100644
index 0000000..28d0efd
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Animation/AnimationNodes.cs
@@ -0,0 +1,76 @@
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public partial class AnimationNodes
+ {
+ [Logic(typeof(Animation))]
+ [return: Title("Animation State")]
+ public static AnimationState GetAnimationState (Animation self, [Setting] string animationStateName)
+ {
+ return self[animationStateName];
+ }
+
+ [Logic(typeof(Animation))]
+ [return: Title("Animation State")]
+ public static AnimationState PlayAnimation (Animation self, [Setting] string animationName, [Setting] bool crossfade, [Setting] float fadeLength, [Setting] PlayMode playMode)
+ {
+ AnimationState animationState = self[animationName == "" ? self.clip.name : animationName];
+
+ if (crossfade)
+ self.CrossFade (animationState.name, fadeLength, playMode);
+ else
+ self.Play (animationState.name, playMode);
+
+ return animationState;
+ }
+
+ [Logic(typeof(Animation))]
+ [return: Title("Animation State")]
+ public static AnimationState PlayQueuedAnimation (Animation self, [Setting] string animationName, [Setting] bool crossfade, [Setting] float fadeLength, [Setting] QueueMode queueMode, [Setting] PlayMode playMode)
+ {
+ if (animationName == "")
+ animationName = self.clip.name;
+
+ var animationState = crossfade ?
+ self.CrossFadeQueued (animationName, fadeLength, queueMode, playMode) :
+ self.PlayQueued (animationName, queueMode, playMode);
+
+ return animationState;
+ }
+
+ [Logic(typeof(Animation))]
+ public static void StopAnimation (Animation self, [Setting] string animationName)
+ {
+ if (animationName == "")
+ self.Stop();
+ else
+ self.Stop(animationName);
+ }
+
+ [Logic (typeof (Animation))]
+ public static void SampleAnimation (Animation self)
+ {
+ self.Sample ();
+ }
+
+ [Logic(typeof(Animation))]
+ public static void StopAnimationState (Animation self, AnimationState animationState)
+ {
+ self.Stop(animationState.name);
+ }
+
+ [Logic(typeof(Animation))]
+ public static void BlendAnimationState (Animation self, AnimationState animationState, float targetWeight, [Setting] float fadeLength)
+ {
+ self.Blend (animationState.name, targetWeight, fadeLength);
+ }
+
+ [Logic(typeof(Animation))]
+ public static void SyncAnimationLayer (Animation self, int layer)
+ {
+ self.SyncLayer (layer);
+ }
+ }
+}
+
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Animation/SimpleAnimationPlayer.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Animation/SimpleAnimationPlayer.cs
new file mode 100644
index 0000000..9b1ee07
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Animation/SimpleAnimationPlayer.cs
@@ -0,0 +1,78 @@
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public partial class AnimationNodes
+ {
+ [Logic(typeof(Animation))]
+ public sealed class SimpleAnimationPlayer
+ {
+ // Logic Target
+ public Animation self;
+
+ // Settings
+ private AnimationState m_AnimationState;
+ [Setting]
+ public string animationName
+ {
+ set
+ {
+ m_AnimationState = self[value != "" ? value : self.clip.name];
+ }
+ }
+ private bool m_Crossfade;
+ [Setting]
+ public bool crossfade { set { m_Crossfade = value; } }
+ private float m_FadeLength;
+ [Setting]
+ public float fadeLength { set { m_FadeLength = value; } }
+
+ private bool m_IsPaused;
+ private float m_ResumeSpeed;
+
+ public SimpleAnimationPlayer () { }
+
+ public SimpleAnimationPlayer (Animation self, string animationName, bool crossfade, float fadeLength)
+ {
+ this.self = self;
+ this.animationName = animationName;
+ m_Crossfade = crossfade;
+ m_FadeLength = fadeLength;
+ }
+
+ public void Play ()
+ {
+ if (m_Crossfade)
+ self.CrossFade (m_AnimationState.name, m_FadeLength);
+ else
+ self.Play (m_AnimationState.name);
+ }
+
+ public void Stop ()
+ {
+ StopAnimationState (m_AnimationState);
+ }
+
+ [Title("Pause/Resume")]
+ public void PauseResume ()
+ {
+ float tmpSpeed = m_AnimationState.speed;
+ m_AnimationState.speed = m_IsPaused ? m_ResumeSpeed : 0.0f;
+
+ m_ResumeSpeed = tmpSpeed;
+ m_IsPaused = !m_IsPaused;
+ }
+
+ public void Rewind ()
+ {
+ m_AnimationState.time = 0.0f;
+ }
+
+ private static void StopAnimationState (AnimationState animationState)
+ {
+ animationState.enabled = false;
+ animationState.time = 0.0f;
+ }
+ }
+ }
+}
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/AudioSource/AudioSourceNodes.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/AudioSource/AudioSourceNodes.cs
new file mode 100644
index 0000000..75b9f90
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/AudioSource/AudioSourceNodes.cs
@@ -0,0 +1,19 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public class AudioSourceNodes
+ {
+ [Logic (typeof(AudioSource))]
+ public static IEnumerator PlayOneShot (AudioSource self, AudioClip clip, float volume)
+ {
+ self.PlayOneShot (clip, volume);
+
+ yield return new WaitForSeconds (clip.length);
+ }
+ }
+}
+
+
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/CharacterController/SimpleCharacterControllerNodes.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/CharacterController/SimpleCharacterControllerNodes.cs
new file mode 100644
index 0000000..89d791a
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/CharacterController/SimpleCharacterControllerNodes.cs
@@ -0,0 +1,24 @@
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public sealed class SimpleCharacterControllerNodes
+ {
+ [Logic(typeof(CharacterController))]
+ public static void SimpleMove (CharacterController self, Vector3 speed, Action grounded, Action airborne)
+ {
+ if (self.SimpleMove (speed))
+ {
+ if (grounded != null) grounded ();
+ }
+ else if (airborne != null) airborne ();
+ }
+
+ [Logic(typeof(CharacterController))]
+ public static CollisionFlags Move (CharacterController self, Vector3 motion)
+ {
+ return self.Move (motion);
+ }
+ }
+}
+
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Collections/CollectionsNodes.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Collections/CollectionsNodes.cs
new file mode 100644
index 0000000..f585bc0
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Collections/CollectionsNodes.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Collections.Generic;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public class CollectionsNodes
+ {
+ public enum IteratorIn { Reset, Next }
+ public delegate void IteratorOut<T> (T element, int index);
+ public delegate void IteratorElementOut<T>(T element);
+
+ [Logic(null, typeof(IteratorIn), typeof(int))]
+ [Title("Collections/Iterator")]
+ public static void Iterator<T> (IteratorIn input, [DefaultValue(-1)] ref int index, List<T> collection, IteratorOut<T> resetOut, IteratorOut<T> iteration, IteratorOut<T> done)
+ {
+ if (input == IteratorIn.Reset)
+ {
+ index = -1;
+ if (resetOut != null) resetOut (default(T), index);
+ return;
+ }
+
+ if (++index >= collection.Count)
+ {
+ if (done != null) done (default (T), index);
+ }
+ else
+ {
+ if (iteration != null) iteration (collection[index], index);
+ if (index + 1 >= collection.Count)
+ if (done != null) done (default (T), index);
+ }
+ }
+
+ [Logic]
+ [Title("Collections/IterateAll")]
+ public static void IterateAll<T> (List<T> collection, IteratorOut<T> iteration, IteratorOut<T> done)
+ {
+ for (int index = 0; index < collection.Count; ++index)
+ if (iteration != null) iteration (collection[index], index);
+
+ if (done != null) done (default (T), collection.Count - 1);
+ }
+
+ [Logic]
+ [Title("Collections/Add")]
+ public static void Add<T> (List<T> collection, T objectToAdd)
+ {
+ collection.Add (objectToAdd);
+ }
+
+ [Logic]
+ [Title("Collections/Insert")]
+ public static void Insert<T> (List<T> collection, T objectToAdd, int index)
+ {
+ collection.Insert (index, objectToAdd);
+ }
+
+ [Logic]
+ [Title("Collections/GetElementAt")]
+ public static void GetElementAt<T>(List<T> collection, int index, IteratorElementOut<T> success, IteratorElementOut<T> notPresent)
+ {
+ if (index < 0 || collection.Count <= index)
+ {
+ if (notPresent != null)
+ notPresent ((T) (typeof (T).IsValueType ? Activator.CreateInstance (typeof (T)) : null));
+ }
+ else
+ if (success != null)
+ success(collection[index]);
+ }
+
+ [Logic]
+ [Title("Collections/SetElementAt")]
+ public static void SetElementAt<T>(List<T> collection, T element, int index, Action success, Action notPresent)
+ {
+ if (index < 0 || collection.Count <= index)
+ {
+ if (notPresent != null)
+ notPresent();
+ }
+ else
+ {
+ collection[index] = element;
+
+ if (success != null)
+ success ();
+ }
+ }
+
+ [Logic]
+ [Title("Collections/Contains")]
+ public static void Contains<T> (List<T> collection, T objectToTest, Action present, Action notPresent)
+ {
+ if (collection.Contains (objectToTest))
+ {
+ if (present != null) present ();
+ }
+ else if (notPresent != null) notPresent ();
+ }
+
+ [Logic]
+ [Title("Collections/Remove")]
+ public static void Remove<T> (List<T> collection, T objectToRemove, Action removed, Action notPresent)
+ {
+ if (collection.Remove (objectToRemove))
+ {
+ if (removed != null) removed ();
+ }
+ else if (notPresent != null) notPresent ();
+ }
+
+ [Logic]
+ [Title("Collections/RemoveAt")]
+ public static void RemoveAt<T> (List<T> collection, int index)
+ {
+ collection.RemoveAt (index);
+ }
+
+ [Logic]
+ [Title("Collections/Clear")]
+ public static void Clear<T> (List<T> collection)
+ {
+ collection.Clear ();
+ }
+ }
+}
+
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Collider/OnCollisionEvent.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Collider/OnCollisionEvent.cs
new file mode 100644
index 0000000..acc732e
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Collider/OnCollisionEvent.cs
@@ -0,0 +1,74 @@
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public partial class ColliderNodes
+ {
+ // This is only used for node declaration. Implementation is in the OnCollisionEventDummy monobehaviour.
+ [Logic(typeof(Collider))]
+ public class OnCollisionEvent
+ {
+ [LogicTarget]
+ public Collider self;
+
+ public Action enter;
+ public Action exit;
+ public Action stay;
+
+ private Vector3 m_RelativeVelocity;
+ public Vector3 relativeVelocity
+ {
+ get { return m_RelativeVelocity; }
+ }
+
+ private Collider m_Other;
+ public Collider other
+ {
+ get { return m_Other; }
+ }
+
+ //TODO: would be nice to have, but no nodes in graphs yet know about how to work with arrays
+ //private ContactPoint[] m_Contacts;
+ //public ContactPoint[] contacts
+ //{
+ // get { return m_Contacts; }
+ //}
+
+ internal void EnterDummy(Collision collision)
+ {
+ if (enter == null)
+ return;
+
+ m_RelativeVelocity = collision.relativeVelocity;
+ m_Other = collision.collider;
+ //m_Contacts = collision.contacts;
+
+ enter ();
+ }
+
+ internal void ExitDummy(Collision collision)
+ {
+ if (exit == null)
+ return;
+
+ m_RelativeVelocity = collision.relativeVelocity;
+ m_Other = collision.collider;
+ //m_Contacts = collision.contacts;
+
+ exit();
+ }
+
+ internal void StayDummy(Collision collision)
+ {
+ if (stay == null)
+ return;
+
+ m_RelativeVelocity = collision.relativeVelocity;
+ m_Other = collision.collider;
+ //m_Contacts = collision.contacts;
+
+ stay();
+ }
+ }
+ }
+}
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Collider/OnMouseEvent.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Collider/OnMouseEvent.cs
new file mode 100644
index 0000000..869fc5e
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Collider/OnMouseEvent.cs
@@ -0,0 +1,22 @@
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public partial class ColliderNodes
+ {
+ // This is only used for node declaration. Implementation is in the OnMouseEventDummy monobehaviour.
+ [Logic(typeof(Collider))]
+ public class OnMouseEvent
+ {
+ [LogicTarget]
+ public Collider self;
+
+ public Action enter;
+ public Action over;
+ public Action exit;
+ public Action down;
+ public Action up;
+ public Action drag;
+ }
+ }
+}
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Collider/OnTriggerEvent.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Collider/OnTriggerEvent.cs
new file mode 100644
index 0000000..4be1fbc
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Collider/OnTriggerEvent.cs
@@ -0,0 +1,48 @@
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public partial class ColliderNodes
+ {
+ // This is only used for node declaration. Implementation is in the OnCollisionEventDummy monobehaviour.
+ [Logic(typeof(Collider))]
+ public class OnTriggerEvent
+ {
+ [LogicTarget]
+ public Collider self;
+
+ public Action enter;
+ public Action exit;
+ public Action stay;
+
+ private Collider m_Other;
+
+ public Collider other
+ {
+ get { return m_Other; }
+ }
+
+ internal void EnterDummy(Collider other)
+ {
+ if (enter == null)
+ return;
+ m_Other = other;
+ enter();
+ }
+ internal void ExitDummy(Collider other)
+ {
+ if (exit == null)
+ return;
+ m_Other = other;
+ exit();
+ }
+ internal void StayDummy(Collider other)
+ {
+ if (stay == null)
+ return;
+ m_Other = other;
+ stay();
+ }
+ }
+ }
+}
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Component/ComponentNodes.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Component/ComponentNodes.cs
new file mode 100644
index 0000000..2467573
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Component/ComponentNodes.cs
@@ -0,0 +1,10 @@
+using UnityEngine;
+using System.Collections;
+using System;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public class ComponentNodes
+ {
+ }
+}
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/InputNodes.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/InputNodes.cs
new file mode 100644
index 0000000..d1687db
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/InputNodes.cs
@@ -0,0 +1,82 @@
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public partial class InputNodes
+ {
+ public delegate void AxisDelegate (float value);
+ public delegate void MouseDelegate (Vector3 mousePosition);
+
+ [Logic]
+ [Title("Input/Get Button")]
+ public static void GetButton(string buttonName, Action onDown, Action onUp, Action down, Action up)
+ {
+ if (onDown != null && Input.GetButtonDown (buttonName))
+ onDown ();
+
+ if (onUp != null && Input.GetButtonUp (buttonName))
+ onUp ();
+
+ if (down != null || up != null)
+ {
+ var stateDelegate = Input.GetButton (buttonName) ? down : up;
+ if (stateDelegate != null)
+ stateDelegate ();
+ }
+ }
+
+ [Logic]
+ [Title("Input/Get Mouse Button")]
+ public static void GetMouseButton (int mouseButton, MouseDelegate onDown, MouseDelegate onUp, MouseDelegate down, MouseDelegate up)
+ {
+ if (onDown != null && Input.GetMouseButtonDown(mouseButton))
+ onDown(Input.mousePosition);
+
+ if (onUp != null && Input.GetMouseButtonUp(mouseButton))
+ onUp(Input.mousePosition);
+
+ if (down != null || up != null)
+ {
+ MouseDelegate stateDelegate = Input.GetMouseButton(mouseButton) ? down : up;
+ if (stateDelegate != null)
+ stateDelegate(Input.mousePosition);
+ }
+ }
+
+ [Logic]
+ [Title("Input/Get Key")]
+ public static void GetKey(KeyCode key, Action onDown, Action onUp, Action down, Action up)
+ {
+ if (onDown != null && Input.GetKeyDown (key))
+ onDown ();
+
+ if (onUp != null && Input.GetKeyUp (key))
+ onUp ();
+
+ if (down != null || up != null)
+ {
+ var stateDelegate = Input.GetKey (key) ? down : up;
+ if (stateDelegate != null)
+ stateDelegate ();
+ }
+ }
+
+ [Logic]
+ [Title("Input/Get Axis")]
+ public static void GetAxis(string axisName, AxisDelegate down, AxisDelegate up)
+ {
+ AxisDelegate stateDelegate = Input.GetButton (axisName) ? down : up;
+ if (stateDelegate != null)
+ stateDelegate (Input.GetAxis (axisName));
+ }
+
+ [LogicEval]
+ [Title("Input/Mouse Position")]
+ [return: Title("Mouse Position")]
+ public static Vector3 MousePosition ()
+ {
+ return Input.mousePosition;
+ }
+ }
+}
+
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnAxis.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnAxis.cs
new file mode 100644
index 0000000..f9bdcb6
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnAxis.cs
@@ -0,0 +1,37 @@
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public partial class InputNodes
+ {
+ [Logic]
+ [Title("Input/On Axis")]
+ public sealed class OnAxis : OnInputNode
+ {
+ private float m_Value;
+ public float value { get { return m_Value; } }
+
+ private string m_AxisName;
+ public string axisName { set { m_AxisName = value; } }
+
+ public OnAxis (GraphBehaviour graphBehaviour) : base (graphBehaviour) { }
+ public OnAxis (IMonoBehaviourEventCaller graphBehaviour, string axisName) : base (graphBehaviour)
+ {
+ m_AxisName = axisName;
+ }
+
+ protected override void OnUpdate ()
+ {
+ if (down == null && up == null)
+ return;
+
+ m_Value = Input.GetAxis (m_AxisName);
+
+ var stateDelegate = Input.GetButton (m_AxisName) ? down : up;
+ if (stateDelegate != null)
+ stateDelegate ();
+ }
+ }
+ }
+}
+
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnButton.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnButton.cs
new file mode 100644
index 0000000..a5d0809
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnButton.cs
@@ -0,0 +1,38 @@
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public partial class InputNodes
+ {
+ [Logic]
+ [Title("Input/On Button")]
+ public sealed class OnButton : OnStateInputNode
+ {
+ private string m_ButtonName;
+ public string buttonName { set { m_ButtonName = value; } }
+
+ public OnButton (GraphBehaviour graphBehaviour) : base (graphBehaviour) { }
+ public OnButton (IMonoBehaviourEventCaller graphBehaviour, string buttonName) : base (graphBehaviour)
+ {
+ m_ButtonName = buttonName;
+ }
+
+ protected override void OnUpdate ()
+ {
+ if (onDown != null && Input.GetButtonDown (m_ButtonName))
+ onDown ();
+
+ if (onUp != null && Input.GetButtonUp (m_ButtonName))
+ onUp ();
+
+ if (down != null || up != null)
+ {
+ var stateDelegate = Input.GetButton (m_ButtonName) ? down : up;
+ if (stateDelegate != null)
+ stateDelegate ();
+ }
+ }
+ }
+ }
+}
+
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnInputNode.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnInputNode.cs
new file mode 100644
index 0000000..70032f2
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnInputNode.cs
@@ -0,0 +1,50 @@
+using System;
+
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public partial class InputNodes
+ {
+ public abstract class OnInputNode
+ {
+ protected IMonoBehaviourEventCaller m_GraphBehaviour;
+
+ protected float m_DeltaTime;
+ public float deltaTime { get { return m_DeltaTime; } }
+
+ public Action down;
+ public Action up;
+
+ protected OnInputNode () { }
+
+ protected OnInputNode (GraphBehaviour graphBehaviour)
+ {
+ m_GraphBehaviour = graphBehaviour;
+ m_GraphBehaviour.OnUpdate += OnBaseUpdate;
+ }
+
+ protected OnInputNode (IMonoBehaviourEventCaller graphBehaviour)
+ {
+ m_GraphBehaviour = graphBehaviour;
+ m_GraphBehaviour.OnUpdate += OnBaseUpdate;
+ }
+
+ private void OnBaseUpdate (float deltaTime)
+ {
+ m_DeltaTime = deltaTime;
+ OnUpdate ();
+ }
+
+ protected abstract void OnUpdate ();
+ }
+
+ public abstract class OnStateInputNode : OnInputNode
+ {
+ public Action onDown;
+ public Action onUp;
+
+ protected OnStateInputNode (GraphBehaviour graphBehaviour) : base (graphBehaviour) { }
+ protected OnStateInputNode (IMonoBehaviourEventCaller graphBehaviour) : base (graphBehaviour) { }
+ }
+ }
+}
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnKey.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnKey.cs
new file mode 100644
index 0000000..64732f7
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnKey.cs
@@ -0,0 +1,38 @@
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public partial class InputNodes
+ {
+ [Logic]
+ [Title("Input/On Key")]
+ public sealed class OnKey : OnStateInputNode
+ {
+ private KeyCode m_Key;
+ public KeyCode key { set { m_Key = value; } }
+
+ public OnKey (GraphBehaviour graphBehaviour) : base (graphBehaviour) { }
+ public OnKey (IMonoBehaviourEventCaller graphBehaviour, KeyCode key) : base (graphBehaviour)
+ {
+ m_Key = key;
+ }
+
+ protected override void OnUpdate ()
+ {
+ if (onDown != null && Input.GetKeyDown (m_Key))
+ onDown ();
+
+ if (onUp != null && Input.GetKeyUp (m_Key))
+ onUp ();
+
+ if (down != null || up != null)
+ {
+ var stateDelegate = Input.GetKey (m_Key) ? down : up;
+ if (stateDelegate != null)
+ stateDelegate ();
+ }
+ }
+ }
+ }
+}
+
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnMouseButton.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnMouseButton.cs
new file mode 100644
index 0000000..544a1e8
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Input/OnMouseButton.cs
@@ -0,0 +1,38 @@
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public partial class InputNodes
+ {
+ [Logic]
+ [Title("Input/On Mouse Button")]
+ public sealed class OnMouseButton : OnStateInputNode
+ {
+ private int m_MouseButton;
+ public int mouseButton { set { m_MouseButton = value; } }
+
+ public OnMouseButton (GraphBehaviour graphBehaviour) : base (graphBehaviour) { }
+ public OnMouseButton (IMonoBehaviourEventCaller graphBehaviour, int mouseButton) : base (graphBehaviour)
+ {
+ m_MouseButton = mouseButton;
+ }
+
+ protected override void OnUpdate ()
+ {
+ if (onDown != null && Input.GetMouseButtonDown (m_MouseButton))
+ onDown ();
+
+ if (onUp != null && Input.GetMouseButtonUp (m_MouseButton))
+ onUp ();
+
+ if (down != null || up != null)
+ {
+ var stateDelegate = Input.GetMouseButton (m_MouseButton) ? down : up;
+ if (stateDelegate != null)
+ stateDelegate ();
+ }
+ }
+ }
+ }
+}
+
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/LogicNodeUtility.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/LogicNodeUtility.cs
new file mode 100644
index 0000000..1caca64
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/LogicNodeUtility.cs
@@ -0,0 +1,5 @@
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public delegate void Action ();
+}
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Material/MaterialNodes.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Material/MaterialNodes.cs
new file mode 100644
index 0000000..734968b
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Material/MaterialNodes.cs
@@ -0,0 +1,113 @@
+using UnityEngine;
+using System.Collections;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public class MaterialNodes
+ {
+ #if REIMPLEMENT_USING_CLASS_NODES
+ [Logic(typeof(Renderer), typeof(NodeLibrary.StartStopEvents))]
+ public static IEnumerator UVScroll (Renderer self, ByRef<NodeLibrary.StartStopEvents> evt, string property, Vector2 magnitude, float frequency, int mode)
+ {
+ Material mat = self.material;
+ if (property == null || property == "")
+ property = "_MainTex";
+
+ if (mode == 0)
+ {
+ while (true)
+ {
+ // scroll uv
+ Vector2 uv = mat.GetTextureOffset(property);
+ uv += magnitude * Time.deltaTime;
+ mat.SetTextureOffset(property, uv);
+
+ // Handle Stop event
+ if (evt.Value == NodeLibrary.StartStopEvents.Stop)
+ break;
+ yield return 0;
+ }
+ }
+ else
+ {
+ // Elapsed time, measured in cycles.
+ float elapsed = 0;
+
+ Vector2 lastUV = mat.GetTextureOffset(property);
+
+ float stopElapsed = 0f;
+ bool exit = false;
+
+ while (true)
+ {
+ // Handle Stop event
+ if (evt.Value == NodeLibrary.StartStopEvents.Stop)
+ {
+ // When stopping, complete the current cycle before really stopping
+ // Update: Actually we only need to complete the current half-cycle
+ // because the cycle always has the same value in the middle as in the beginning and end.
+ if (stopElapsed == 0)
+ stopElapsed = Mathf.Ceil(elapsed * 2) * 0.5f;
+
+ // When we reach the end of the cycle, stop at the exact time
+ else if (elapsed >= stopElapsed)
+ {
+ elapsed = stopElapsed;
+ exit = true;
+ }
+ }
+
+ Vector2 uv = Vector2.zero;
+ // Triangle wave (centered around 0)
+ if (mode == 1)
+ uv += magnitude * (Mathf.PingPong(elapsed * 2f + 0.5f, 1) - 0.5f);
+ // Sine wave (centered around 0)
+ if (mode == 2)
+ uv += magnitude * 0.5f * Mathf.Sin(elapsed * 2f * Mathf.PI);
+
+ mat.SetTextureOffset(property, mat.GetTextureOffset(property) + (uv - lastUV));
+ lastUV = uv;
+
+ if (exit)
+ break;
+
+ elapsed += Time.deltaTime * frequency;
+
+ yield return 0;
+ }
+ }
+
+ }
+
+ [Logic(typeof(Renderer), typeof(NodeLibrary.StartStopEvents))]
+ public static IEnumerator UVCycler (Renderer self, ByRef<NodeLibrary.StartStopEvents> evt, string property, int xTiles, int yTiles, float speed)
+ {
+ Material mat = self.material;
+ if (property == null || property == "")
+ property = "_MainTex";
+
+ // TODO: find out what initial frame is based on uv offset in the beginning?
+
+ float elapsed = 0;
+ while (true)
+ {
+ int frame = Mathf.FloorToInt(elapsed);
+
+ float xOffset = frame % xTiles;
+ float yOffset = yTiles - 1 - (frame / xTiles) % yTiles;
+
+ Vector2 uv = new Vector2(xOffset / xTiles, yOffset / yTiles);
+ mat.SetTextureOffset(property, uv);
+
+ // Handle Stop event
+ if (evt.Value == NodeLibrary.StartStopEvents.Stop)
+ break;
+
+ elapsed += Time.deltaTime * speed;
+
+ yield return 0;
+ }
+ }
+ #endif
+ }
+}
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/NodeLibrary.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/NodeLibrary.cs
new file mode 100644
index 0000000..4723b37
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/NodeLibrary.cs
@@ -0,0 +1,94 @@
+using System.Collections;
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public class NodeLibrary
+ {
+ public enum ToggleEnum { On, Off, Toggle }
+
+ public static string version
+ {
+ get { return "0.3a"; }
+ }
+
+ // this is actually used, leave here when cleaning up this class
+ public static float Iff(bool a, float t, float f)
+ {
+ return a ? t : f;
+ }
+
+ public static int Iffint(bool a, int t, int f)
+ {
+ return a ? t : f;
+ }
+
+ public static bool IsTrigger(Collider target)
+ {
+ return target.isTrigger;
+ }
+
+ public static bool IsNotTrigger(Collider target)
+ {
+ return !target.isTrigger;
+ }
+
+ [Logic]
+ [Title("Logic/Log")]
+ public static void Log (string str)
+ {
+ Debug.Log(str);
+ }
+
+ [Logic]
+ [Title("Logic/Wait")]
+ public static IEnumerator Wait (float waitSeconds)
+ {
+ yield return new WaitForSeconds(waitSeconds);
+ }
+
+ [Logic]
+ [Title("Logic/Timer")]
+ public static IEnumerator Timer (float waitSeconds, int repeatCount, Action tick, Action done)
+ {
+ for (int i = 0; i < repeatCount; i++)
+ {
+ yield return new WaitForSeconds(waitSeconds);
+ if (tick != null)
+ tick();
+ }
+
+ if (done != null)
+ done();
+ }
+
+ [Logic]
+ [Title("Logic/Nop")]
+ public static T Nop<T>(T arg)
+ {
+ return arg;
+ }
+
+ [Logic]
+ [Title("Object/Instantiate")]
+ [return: Title("Instantiated Object")]
+ public static Object Instantiate ([Title("Object")] Object obj, Vector3 position, Quaternion rotation)
+ {
+ return Object.Instantiate(obj, position, rotation);
+ }
+
+ [Logic]
+ [Title("Object/Destroy")]
+ public static void Destroy ([Title("Object")] Object obj)
+ {
+ Object.Destroy(obj);
+ }
+
+ [Logic]
+ [Title("Object/Dont Destroy On Load")]
+ public static void DontDestroyOnLoad([Title("Object")] Object obj)
+ {
+ Object.DontDestroyOnLoad (obj);
+ }
+ }
+}
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/NodeLibraryForTesting.txt b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/NodeLibraryForTesting.txt
new file mode 100644
index 0000000..7a69517
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/NodeLibraryForTesting.txt
@@ -0,0 +1,124 @@
+ [Logic]
+ public static void ExplosionForce (float force, Vector3 position, float radius, float upwardsModifier, float occlusion)
+ {
+ // Get all colliders in radius
+ Collider[] colliders = Physics.OverlapSphere(position, radius);
+
+ // Get all of those that have rigidbodies
+ List<Rigidbody> rigids = new List<Rigidbody> ();
+ foreach (Collider col in colliders)
+ {
+ Rigidbody rigid = col.rigidbody;
+ if (rigid != null)
+ rigids.Add(rigid);
+ }
+
+ if (occlusion <= 0)
+ {
+ // Apply the explosion force
+ for (int i=0; i<rigids.Count; i++)
+ {
+ rigids[i].AddExplosionForce(force, position, radius, upwardsModifier, ForceMode.Impulse);
+ }
+ }
+ else
+ {
+ // Save original layers of all the rigidbodies
+ // Then but them into Ignore Raycast layer
+ int[] origLayers = new int[rigids.Count];
+ for (int i=0; i<rigids.Count; i++)
+ {
+ origLayers[i] = rigids[i].gameObject.layer;
+ rigids[i].gameObject.layer = 2; // Ignore Raycast
+ }
+
+ // Find out which of the rigidbodies are occuded
+ bool[] occluded = new bool[rigids.Count];
+ for (int i=0; i<rigids.Count; i++)
+ {
+ Vector3 pos = rigids[i].transform.position;
+ if (Physics.Raycast(pos, position - pos, (position - pos).magnitude))
+ occluded[i] = true;
+ }
+
+ // Set layers back to the original values
+ for (int i=0; i<rigids.Count; i++)
+ {
+ rigids[i].gameObject.layer = origLayers[i];
+ }
+
+ // Finally apply the explosion force
+ float mult = Mathf.Clamp01(1-occlusion);
+ for (int i=0; i<rigids.Count; i++)
+ {
+ float thisForce = force;
+ if (occluded[i])
+ thisForce *= mult;
+ rigids[i].AddExplosionForce(thisForce, position, radius, upwardsModifier, ForceMode.Impulse);
+ }
+ }
+ }
+ [Logic]
+ public static void FindCollidersInRadius (Vector3 center, float radius, ColliderDelegate affected, ColliderDelegate done)
+ {
+ Collider[] colliders = Physics.OverlapSphere(center, radius);
+ foreach (Collider col in colliders)
+ {
+ affected(col);
+ }
+ if (done == null)
+ Debug.LogWarning("done delegate is null");
+ else
+ done(colliders[0]);
+ }
+
+ [Logic]
+ public static float AssignFloat (float value)
+ {
+ return value;
+ }
+
+ // Eval
+ [LogicEval]
+ public static Vector3 Vector3FromFloats (float x, float y, float z)
+ {
+ return new Vector3(x, y, z);
+ }
+
+ public enum Vector3Element {X, Y, Z}
+ [LogicEval]
+ public static float ElementFromVector3 (Vector3 vector, Vector3Element element)
+ {
+ switch (element)
+ {
+ case Vector3Element.X:
+ return vector.x;
+ case Vector3Element.Y:
+ return vector.y;
+ case Vector3Element.Z:
+ return vector.z;
+ default:
+ return 0f;
+ }
+ }
+
+ [LogicEval]
+ public static Vector3 ScaleVector (Vector3 vector, float scalar)
+ {
+ return vector * scalar;
+ }
+
+ [LogicEval]
+ public static Vector3 AddVectors (Vector3 vector1, Vector3 vector2)
+ {
+ return vector1 + vector2;
+ }
+
+ [LogicEval]
+ public static Vector3 InverseDistVector (Vector3 from, Vector3 to, float multiplier)
+ {
+ float dist = Vector3.Distance(from, to);
+ if (dist == 0)
+ return Vector3.zero;
+ return (to - from) / (dist * dist) * multiplier;
+ }
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Rigidbody/RigidbodyNodes.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Rigidbody/RigidbodyNodes.cs
new file mode 100644
index 0000000..d151a62
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Rigidbody/RigidbodyNodes.cs
@@ -0,0 +1,104 @@
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public class RigidbodyNode
+ {
+ #if REIMPLEMENT_USING_CLASS_NODES
+ [Logic(typeof(Rigidbody), typeof(NodeLibrary.StartStopEvents))]
+ public static IEnumerator Force (Rigidbody self, ByRef<NodeLibrary.StartStopEvents> evt, Vector3 force, bool localSpace, bool ignoreMass)
+ {
+ if (evt.Value == NodeLibrary.StartStopEvents.Stop)
+ yield break;
+ if (self)
+ {
+ ForceMode mode = (ignoreMass ? ForceMode.Acceleration : ForceMode.Force);
+
+ if (localSpace)
+ {
+ while (evt.Value != NodeLibrary.StartStopEvents.Stop)
+ {
+ yield return new WaitForFixedUpdate();
+ self.AddRelativeForce(force, mode);
+ }
+ }
+ else
+ {
+ while (evt.Value != NodeLibrary.StartStopEvents.Stop)
+ {
+ yield return new WaitForFixedUpdate();
+ self.AddForce(force, mode);
+ }
+ }
+ }
+ else
+ Debug.LogWarning("Force self parameter is null");
+ }
+
+ [Logic(typeof(Rigidbody), typeof(NodeLibrary.StartStopEvents))]
+ public static IEnumerator Torque (Rigidbody self, ByRef<NodeLibrary.StartStopEvents> evt, Vector3 torque, bool localSpace, bool ignoreMass)
+ {
+ if (evt.Value == NodeLibrary.StartStopEvents.Stop)
+ yield break;
+ if (self)
+ {
+ ForceMode mode = (ignoreMass ? ForceMode.Acceleration : ForceMode.Force);
+
+ if (localSpace)
+ {
+ while (evt.Value != NodeLibrary.StartStopEvents.Stop)
+ {
+ yield return new WaitForFixedUpdate();
+ self.AddRelativeTorque(torque, mode);
+ }
+ }
+ else
+ {
+ while (evt.Value != NodeLibrary.StartStopEvents.Stop)
+ {
+ yield return new WaitForFixedUpdate();
+ self.AddTorque(torque, mode);
+ }
+ }
+ }
+ else
+ Debug.LogWarning("Torque self parameter is null");
+ }
+ #endif
+
+ [Logic(typeof(Rigidbody))]
+ public static void ApplyForce (Rigidbody self, Vector3 force, Space relativeTo, ForceMode forceMode)
+ {
+ if (relativeTo == Space.Self)
+ self.AddRelativeForce(force, forceMode);
+ else
+ self.AddForce(force, forceMode);
+ }
+
+ [Logic(typeof(Rigidbody))]
+ public static void ApplyTorque (Rigidbody self, Vector3 torque, Space relativeTo, ForceMode forceMode)
+ {
+ if (relativeTo == Space.Self)
+ self.AddRelativeTorque(torque, forceMode);
+ else
+ self.AddTorque(torque, forceMode);
+ }
+
+ [Logic(typeof(Rigidbody))]
+ public static void SetVelocity (Rigidbody self, Vector3 velocity, Space relativeTo)
+ {
+ if (relativeTo == Space.Self)
+ velocity = self.transform.rotation * velocity;
+ self.velocity = velocity;
+ }
+
+ [Logic(typeof(Rigidbody))]
+ public static void SetAngularVelocity (Rigidbody self, Vector3 angularVelocity, Space relativeTo)
+ {
+ if (relativeTo == Space.Self)
+ angularVelocity = self.transform.rotation * angularVelocity;
+ self.angularVelocity = angularVelocity;
+ }
+ }
+
+}
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/LookAt.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/LookAt.cs
new file mode 100644
index 0000000..4ba9bd0
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/LookAt.cs
@@ -0,0 +1,46 @@
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public partial class TransformNodes
+ {
+ [Logic(typeof(Transform))]
+ public sealed class LookAt : YieldedTransformNodeBase
+ {
+ private readonly ILookAtRotationCalculator m_RotationCalculator;
+ private Quaternion m_InitialRotation;
+ private Vector3 m_TargetRelativePosition;
+
+ public override Transform target { set { m_Target = value; } }
+ public Vector3 targetOffset { set { m_TargetRelativePosition = value; } }
+
+
+ public LookAt ()
+ {
+ m_RotationCalculator = StandardLookAtRotationCalculator.s_Instance;
+ }
+
+ public LookAt (Transform self, Transform target, Vector3 targetRelativePosition, float time, ILookAtRotationCalculator rotationCalculator) : base (self, target, time)
+ {
+ m_TargetRelativePosition = targetRelativePosition;
+ m_RotationCalculator = rotationCalculator;
+ }
+
+ protected override void OnStart()
+ {
+ m_InitialRotation = self.rotation;
+ }
+
+ protected override void OnUpdate()
+ {
+ self.rotation = m_RotationCalculator.CalculateRotation(self, m_Target, m_TargetRelativePosition, m_InitialRotation, m_Percentage, m_Curve);
+ }
+
+ protected override void OnDone()
+ {
+ self.rotation = m_RotationCalculator.CalculateRotation(self, m_Target, m_TargetRelativePosition, m_InitialRotation, 1.0f, m_Curve);
+ }
+ }
+ }
+}
+
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/MoveTo.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/MoveTo.cs
new file mode 100644
index 0000000..b60454c
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/MoveTo.cs
@@ -0,0 +1,45 @@
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public partial class TransformNodes
+ {
+ [Logic (typeof (Transform))]
+ public sealed class MoveTo : YieldedTransformNodeBase
+ {
+ private readonly IMoveToPositionCalculator m_PositionCalculator;
+ private Vector3 m_InitialPosition;
+ private Vector3 m_TargetRelativePosition;
+
+ public override Transform target { set { m_Target = value; } }
+ public Vector3 targetOffset { set { m_TargetRelativePosition = value; } }
+
+ public MoveTo()
+ {
+ m_PositionCalculator = StandardMoveToPositionCalculator.s_Instance;
+ }
+
+ public MoveTo(Transform self, Transform target, Vector3 targetRelativePosition, float time, IMoveToPositionCalculator positionCalculator) : base (self, target, time)
+ {
+ m_TargetRelativePosition = targetRelativePosition;
+ m_PositionCalculator = positionCalculator;
+ }
+
+
+ protected override void OnStart()
+ {
+ m_InitialPosition = self.position;
+ }
+
+ protected override void OnUpdate()
+ {
+ self.position = m_PositionCalculator.CalculatePosition(m_Target, m_TargetRelativePosition, m_InitialPosition, m_Percentage, m_Curve);
+ }
+
+ protected override void OnDone()
+ {
+ self.position = m_PositionCalculator.CalculatePosition(m_Target, m_TargetRelativePosition, m_InitialPosition, 1.0f, m_Curve);
+ }
+ }
+ }
+}
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/RotateTo.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/RotateTo.cs
new file mode 100644
index 0000000..8b44479
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/RotateTo.cs
@@ -0,0 +1,45 @@
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public partial class TransformNodes
+ {
+ [Logic (typeof (Transform))]
+ public sealed class RotateTo : YieldedTransformNodeBase
+ {
+ private readonly IRotateToRotationCalculator m_RotationCalculator;
+ private Quaternion m_InitialRotation;
+ private Quaternion m_TargetRelativeRotation;
+
+ public override Transform target { set { m_Target = value; } }
+ public Quaternion targetOffset { set { m_TargetRelativeRotation = value; } }
+
+ public RotateTo ()
+ {
+ m_RotationCalculator = StandardRotateToRotationCalculator.s_Instance;
+ }
+
+ public RotateTo (Transform self, Transform target, Quaternion targetRelativeRotation, float time, IRotateToRotationCalculator rotationCalculator)
+ : base (self, target, time)
+ {
+ m_TargetRelativeRotation = targetRelativeRotation;
+ m_RotationCalculator = rotationCalculator;
+ }
+
+ protected override void OnStart ()
+ {
+ m_InitialRotation = self.rotation;
+ }
+
+ protected override void OnUpdate ()
+ {
+ self.rotation = m_RotationCalculator.CalculateRotation (m_Target, m_TargetRelativeRotation, m_InitialRotation, m_Percentage, m_Curve);
+ }
+
+ protected override void OnDone ()
+ {
+ self.rotation = m_RotationCalculator.CalculateRotation (m_Target, m_TargetRelativeRotation, m_InitialRotation, 1.0f, m_Curve);
+ }
+ }
+ }
+}
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/TransformNodes.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/TransformNodes.cs
new file mode 100644
index 0000000..5e99275
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/TransformNodes.cs
@@ -0,0 +1,122 @@
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public partial class TransformNodes
+ {
+ #region Nodes
+ [Logic(typeof(Transform))]
+ public static void Translate(Transform self, Vector3 translation, Space relativeTo)
+ {
+ self.Translate(translation, relativeTo);
+ }
+
+ [Logic(typeof(Transform))]
+ public static void Rotate(Transform self, Vector3 axis, float angle, Space relativeTo)
+ {
+ self.Rotate(axis, angle, relativeTo);
+ }
+
+ [Logic(typeof(Transform))]
+ public static void Mimic(Transform self, Transform target, bool mimicPosition, bool mimicRotation, bool mimicScale, bool useLocalSpace)
+ {
+ if (mimicPosition)
+ if (useLocalSpace)
+ self.localPosition = target.localPosition;
+ else
+ self.position = target.position;
+
+ if (mimicRotation)
+ if (useLocalSpace)
+ self.localRotation = target.localRotation;
+ else
+ self.rotation = target.rotation;
+
+ if (mimicScale)
+ self.localScale = target.localScale;
+ }
+
+ [LogicEval(typeof(Transform))]
+ [Title("Get Position")]
+ public static Vector3 GetPosition(Transform target)
+ {
+ if (target == null)
+ return Vector3.zero;
+ return target.position;
+ }
+
+ [Logic(typeof(Transform))]
+ [Title("Set Position")]
+ public static void SetPosition(Transform target, Vector3 position)
+ {
+ if (target == null)
+ return;
+ target.position = position;
+ }
+ #endregion
+
+ #region Node Helpers
+ private static Quaternion LookAtLookRotation(Transform self, Transform target, Vector3 targetRelativePosition)
+ {
+ return Quaternion.LookRotation(AbsoluteTargetPosition(target, targetRelativePosition) - self.position);
+ }
+
+ private static Vector3 AbsoluteTargetPosition(Transform target, Vector3 targetRelativePosition)
+ {
+ if (target != null)
+ return target.position + targetRelativePosition;
+ return targetRelativePosition;
+ }
+ #endregion
+
+ #region Transform Calculators
+ public interface IMoveToPositionCalculator
+ {
+ Vector3 CalculatePosition (Transform target, Vector3 targetRelativePosition, Vector3 initialPosition, float percentage, AnimationCurve curve);
+ }
+
+ class StandardMoveToPositionCalculator : IMoveToPositionCalculator
+ {
+ public static readonly IMoveToPositionCalculator s_Instance = new StandardMoveToPositionCalculator ();
+
+ public Vector3 CalculatePosition (Transform target, Vector3 targetRelativePosition, Vector3 initialPosition, float percentage, AnimationCurve curve)
+ {
+ return Vector3.Lerp (initialPosition, AbsoluteTargetPosition (target, targetRelativePosition), curve.Evaluate (percentage));
+ }
+ }
+
+
+ public interface IRotateToRotationCalculator
+ {
+ Quaternion CalculateRotation (Transform target, Quaternion targetRelativeRotation, Quaternion initialRotation, float percentage, AnimationCurve curve);
+ }
+
+ class StandardRotateToRotationCalculator : IRotateToRotationCalculator
+ {
+ public static readonly IRotateToRotationCalculator s_Instance = new StandardRotateToRotationCalculator ();
+
+ public Quaternion CalculateRotation (Transform target, Quaternion targetRelativeRotation, Quaternion initialRotation, float percentage, AnimationCurve curve)
+ {
+ return Quaternion.Lerp (initialRotation, targetRelativeRotation * target.rotation, curve.Evaluate (percentage));
+ }
+ }
+
+
+ public interface ILookAtRotationCalculator
+ {
+ Quaternion CalculateRotation (Transform self, Transform target, Vector3 targetRelativePosition, Quaternion initialRotation, float percentage, AnimationCurve curve);
+ }
+
+ class StandardLookAtRotationCalculator : ILookAtRotationCalculator
+ {
+ public static readonly ILookAtRotationCalculator s_Instance = new StandardLookAtRotationCalculator ();
+
+ public Quaternion CalculateRotation (Transform self, Transform target, Vector3 targetRelativePosition, Quaternion initialRotation, float percentage, AnimationCurve curve)
+ {
+ return Quaternion.Lerp (initialRotation, LookAtLookRotation (self, target, targetRelativePosition), curve.Evaluate (percentage));
+ }
+ }
+ #endregion
+ }
+}
+
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/YieldedTransformNodeBase.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/YieldedTransformNodeBase.cs
new file mode 100644
index 0000000..e95f699
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/Transform/YieldedTransformNodeBase.cs
@@ -0,0 +1,27 @@
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public abstract class YieldedTransformNodeBase : YieldedNodeBase
+ {
+ [LogicTarget]
+ public Transform self;
+
+ protected Transform m_Target;
+ protected AnimationCurve m_Curve;
+
+ public virtual Transform target { set { m_Target = value; } }
+ public virtual AnimationCurve curve { set { m_Curve = value; } }
+
+ protected YieldedTransformNodeBase ()
+ {
+ m_Curve = new AnimationCurve ();
+ }
+
+ protected YieldedTransformNodeBase (Transform self, Transform target, float time) : base (time)
+ {
+ this.self = self;
+ m_Target = target;
+ }
+ }
+}
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/YieldedNodeBase.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/YieldedNodeBase.cs
new file mode 100644
index 0000000..2086d95
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeLibrary/YieldedNodeBase.cs
@@ -0,0 +1,54 @@
+using System.Collections;
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public abstract class YieldedNodeBase
+ {
+ protected float m_Time;
+ protected float m_Percentage;
+
+ public Action done;
+ public Action update;
+
+ public virtual float totalTime { set { m_Time = value; } }
+ public virtual float percentage { get { return m_Percentage; } }
+
+ protected YieldedNodeBase () {}
+
+ protected YieldedNodeBase (float time)
+ {
+ m_Time = time;
+ }
+
+ public IEnumerator Start ()
+ {
+ OnStart ();
+
+ if (m_Time > 0.0f)
+ {
+ float doneTime = Time.time + m_Time;
+ float t = 0;
+ do
+ {
+ t += Time.deltaTime;
+ m_Percentage = t / m_Time;
+
+ OnUpdate();
+ if (update != null)
+ update();
+
+ yield return 0;
+ } while (Time.time < doneTime);
+ }
+
+ OnDone();
+ if (done != null)
+ done();
+ }
+
+ protected abstract void OnStart ();
+ protected abstract void OnUpdate ();
+ protected abstract void OnDone ();
+ }
+}
diff --git a/Runtime/Graphs/UnityEngine.Graphs/LogicNodeTestLibrary.cs b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeTestLibrary.cs
new file mode 100644
index 0000000..b92555a
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/LogicNodeTestLibrary.cs
@@ -0,0 +1,359 @@
+
+#if NOT
+// Nodes for trying-out purposes. Complete mess.
+
+using System.Collections;
+using UnityEngine;
+using EmptyDelegate = UnityEngine.Graphs.LogicGraph.LogicNodeUtility.EmptyDelegate;
+using ColliderDelegate = UnityEngine.Graphs.LogicGraph.NodeLibrary.ColliderDelegate;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ public class LogicNodeTestLibrary
+ {
+ [Logic]
+ public static IEnumerator DoSomethingWithAnimationCurve(AnimationCurve curve)
+ {
+ var go = GameObject.Find("Cube");
+
+ for (float f = 0; f < 2f; f+=0.01f)
+ {
+ go.transform.Rotate(new Vector3(curve.Evaluate(f) * 10f, 0,0));
+ yield return new WaitForFixedUpdate();
+ }
+ }
+
+ [Logic]
+ public class LimitedInvoker
+ {
+ public int invokeTimes = 500;
+ int currentInvokeIndex = 0;
+
+ public delegate void VoidDelegate();
+
+ public VoidDelegate myOut;
+
+ public void In()
+ {
+ if (++currentInvokeIndex > invokeTimes)
+ return;
+
+ if (currentInvokeIndex % 500 == 0)
+ Debug.Log(currentInvokeIndex);
+
+ if (myOut != null)
+ myOut();
+ }
+ }
+
+ [Logic(null, null, typeof(int))]
+ public static void Counter(ref int state, int UpTo, EmptyDelegate done)
+ {
+ state++;
+ if (state >= UpTo)
+ if (done != null)
+ done();
+ }
+
+ [Logic]
+ public static void HasTag(GameObject go, string tag, EmptyDelegate True, EmptyDelegate False)
+ {
+ if (go.tag == tag)
+ {
+ if (True != null)
+ True();
+ }
+ else
+ {
+ if (False != null)
+ False();
+ }
+ }
+
+ [Logic]
+ public static event EmptyDelegate StaticEvent;
+
+ public static void CauseStaticEvent()
+ {
+ if (StaticEvent != null)
+ StaticEvent();
+ }
+
+ // Enums for in slots
+ public enum StartStopEvents { Start, Stop }
+ public enum StartPauseStopEvents { Start, Pause, Stop }
+
+ [LogicEval(typeof(Transform))]
+ public static float GetPositionY(Transform self)
+ {
+ return self.position.y;
+ }
+
+ [Logic]
+ public static string PassString()
+ {
+ return "Some String";
+ }
+
+ [Logic]
+ public static string AddStrings(string a, string b)
+ {
+ return a + " + " + b;
+ }
+
+ [Logic]
+ public static void LogCollidersGO(Collider collider)
+ {
+ Debug.Log("Ouch!.. " + collider.gameObject.name);
+ }
+
+ [Logic(typeof(Collider))]
+ public static void ColliderFunction(Collider self)
+ {
+ Debug.Log("ColliderFunction " + self.gameObject.name);
+ }
+
+ [Logic]
+ public static IEnumerator YieldedFunction(string strParam)
+ {
+ yield return new WaitForSeconds(3);
+ Debug.Log(strParam);
+ }
+
+ public enum MyActions { Start, Stop, Pause }
+
+ [Logic(typeof(AudioSource), typeof(MyActions))]
+ public static void PlayAudio(AudioSource self, MyActions action)
+ {
+ switch (action)
+ {
+ case MyActions.Start:
+ self.Play();
+ break;
+ case MyActions.Stop:
+ self.Stop();
+ break;
+ case MyActions.Pause:
+ self.Pause();
+ break;
+ }
+ }
+
+ public class Expressions
+ {
+ [LogicExpression]
+ public static int CustomFn(int f)
+ {
+ return f*f;
+ }
+
+ [LogicExpression]
+ public static float Prop
+ {
+ get
+ {
+ return 3f;
+ }
+ }
+ }
+
+ [Logic]
+ public static int intVar;
+
+ [Logic]
+ public static string strProperty { get { return "0"; } set { Debug.Log("setting to: " + value); } }
+
+ [Logic]
+ public static int SetInt (int a)
+ {
+ return a;
+ }
+
+ [Logic]
+ public static void EvaluateBool (bool b, EmptyDelegate True, EmptyDelegate False)
+ {
+ if (b)
+ True();
+ else
+ False();
+ }
+
+ [Logic]
+ public static void Destroy (GameObject self)
+ {
+ Object.Destroy(self);
+ }
+
+ [Logic]
+ public static GameObject Instantiate (GameObject obj, Vector3 position, Quaternion rotation)
+ {
+ return (GameObject)GameObject.Instantiate(obj, position, rotation);
+ }
+
+ [Logic]
+ public static void Do() { }
+
+ [Logic]
+ public static void FindCollidersInRadius (Vector3 center, float radius, ColliderDelegate affected, ColliderDelegate done)
+ {
+ Collider[] colliders = Physics.OverlapSphere(center, radius);
+ foreach (Collider col in colliders)
+ {
+ affected(col);
+ }
+ if (done == null)
+ Debug.LogWarning("done delegate is null");
+ else
+ done(colliders[0]);
+ }
+
+ // Eval
+
+ [LogicEval]
+ public static Vector3 Vector3FromFloats (float x, float y, float z)
+ {
+ return new Vector3(x, y, z);
+ }
+
+ [LogicEval]
+ public static int Add (int a, int b)
+ {
+ return a + b;
+ }
+
+ [LogicEval]
+ public static float Random (float min, float max)
+ {
+ return UnityEngine.Random.Range(min, max);
+ }
+
+ [LogicEval]
+ public static float InputAxis (string axisName)
+ {
+ return Input.GetAxis(axisName);
+ }
+
+ [LogicEval]
+ public static GameObject GameObjectVar (GameObject obj)
+ {
+ return obj;
+ }
+
+ [LogicEval]
+ public static Vector3 InverseDistVector (Vector3 from, Vector3 to, float multiplier)
+ {
+ float dist = Vector3.Distance(from, to);
+ if (dist == 0)
+ return Vector3.zero;
+ else
+ return (to - from) / (dist * dist) * multiplier;
+ }
+
+ }
+
+ [Logic]
+ public class NodeInClass
+ {
+ public int simpleVariable;
+ public string onlyGet
+ {
+ get { return string.Empty; }
+ }
+
+ public string onlySet
+ {
+ set { }
+ }
+
+ public delegate void VoidDelegate();
+ public VoidDelegate ExitLink1;
+ public VoidDelegate ExitLink2;
+
+ public void Input1() { Debug.Log("input1");}
+ public IEnumerator YieldedInput() { return null; }
+ }
+
+ [Logic(typeof(Collider))]
+ public class ColliderNodeInClass
+ {
+ public Collider target;
+ public void DoSomething() { Debug.Log(target);}
+ }
+
+ [Logic]
+ public class TitledStuff
+ {
+ public enum TitledEnum { [Title("Crazy")]Start, [Title("Thing")] Stop, [Title("ToDo")] Pause }
+
+ public delegate void TwoStringTitledDelegate([Title("String 1")]string str1, [Title("String 2")]string str2);
+ public delegate void TwoObjectsTitledEvent([Title("GO arg")]Object go, [Title("other arg")]Object other);
+
+ [Logic]
+ [Title("-::Custom Named Fn::-")]
+ [return: Title("My ÀÛT")]
+ public static int FunctionWithCustomTitles([Title("First variable")]string var1, [Title("Second variable")]int var2) { return 0; }
+
+ [Logic]
+ [Title("-::Custom Named delegate Fn::-")]
+ public static void CustomNameFnWithDelegates([Title("Str input")]string string1, [Title("Output 1")]TwoStringTitledDelegate out1, [Title("Output 2")]TwoStringTitledDelegate out2) { }
+
+ [Logic]
+ [Title("@#^@#$")]
+ public static TwoObjectsTitledEvent TitledEvent;
+
+ [Logic]
+ [Title("Ghy")]
+ public static int titledVar;
+ [Logic]
+ [Title("Ghy@$^")]
+ public static int titledProp { get { return 0; } }
+
+ [LogicEval]
+ [Title("Eval 123")]
+ [return: Title("Eval Ret")]
+ public static int TitledEval([Title("eval arg")]string str) { return 0; }
+
+ [Logic(null, typeof(TitledEnum))]
+ [Title("it's really titled")]
+ public static void TitledMultiInputFunction(TitledEnum actions, [Title("Var In 1")]int prm1, [Title("Var In 2")]string prm2) { }
+ }
+
+ [Logic]
+ [Title("-::Custom Named Class::-")]
+ public class CustomTitleNodeInClass
+ {
+ [Title("Var Custom")]
+ public int simpleVariable;
+
+ [Title("Property Custom")]
+ public string onlyGet { get { return string.Empty; } }
+
+ public delegate void VoidDelegate();
+ [Title("Exit link custom")]
+ public VoidDelegate ExitLink1;
+
+ [Title("Input custom")]
+ public void Input1() { }
+ }
+
+ [Logic]
+ public class ValidatingNodes
+ {
+ public delegate void GOEvent(GameObject go);
+ [Logic(typeof(GameObject)), Validate("MyValidate")] public static GOEvent ValidatedDelegate;
+ [Logic(typeof(GameObject)), Validate("MyValidate")] public static event GOEvent ValidatedEvent;
+
+ [Logic(typeof(GameObject)), Validate("MyValidate")]
+ public static void FunctionNodeWithValidate(GameObject target){}
+
+ public static bool MyValidate(GameObject target) { return target.name == "ShowMe"; }
+ }
+
+ [Logic(typeof(GameObject)), Validate("ValidatingNodes.MyValidate")]
+ public class ClassWithValidate
+ {
+ public GameObject target;
+ public void Input(){}
+ }
+}
+#endif \ No newline at end of file
diff --git a/Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/ColliderDummyBase.cs b/Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/ColliderDummyBase.cs
new file mode 100644
index 0000000..2d7efca
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/ColliderDummyBase.cs
@@ -0,0 +1,25 @@
+using System;
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ // For now we do triggers by attaching this MonoBehaviour to needed gameobjects. Class then sends events to logic graph nodes.
+ public abstract class ColliderDummyBase : MonoBehaviour
+ {
+ protected static Component AttachToCollider(Collider self, Type dummyType)
+ {
+ var attached = GetAndAddComponentIfNeeded(self.gameObject, dummyType);
+
+ if (attached == null)
+ throw new ArgumentException("Failed to attach Logic Graph Collider Event handler to a game object of component '" + self + "'.");
+
+ return attached;
+ }
+
+ private static Component GetAndAddComponentIfNeeded(GameObject go, Type type)
+ {
+ return go.GetComponent(type) ?? go.AddComponent(type);
+ }
+ }
+}
+
diff --git a/Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/OnAnimationEventDummy.cs b/Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/OnAnimationEventDummy.cs
new file mode 100644
index 0000000..66bf531
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/OnAnimationEventDummy.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ // This component gets attached to a GameObject with Animation component and handles special LogicGraphEvent.
+ public class OnAnimationEventDummy : MonoBehaviour
+ {
+ private Dictionary<string, Action> m_Events = new Dictionary<string, Action> ();
+
+ public static void AttachToGameObject(Animation component, string eventName, Action delegateToCall)
+ {
+ var animEventDummy = component.gameObject.GetComponent (typeof (OnAnimationEventDummy)) as OnAnimationEventDummy ??
+ component.gameObject.AddComponent(typeof(OnAnimationEventDummy)) as OnAnimationEventDummy;
+
+ if (animEventDummy == null)
+ throw new ArgumentException("Failed to attach Logic Graph Animation Event handler to a game object of component '" + component + "'.");
+
+ animEventDummy.AddNewEvent(eventName, delegateToCall);
+ }
+
+ private void AddNewEvent(string eventName, Action delegateToCall)
+ {
+ if (!m_Events.ContainsKey(eventName))
+ m_Events.Add(eventName, delegateToCall);
+ else
+ m_Events[eventName] += delegateToCall;
+ }
+
+ public void LogicGraphEvent(string eventName)
+ {
+ Action delegateToCall;
+
+ if (!m_Events.TryGetValue(eventName, out delegateToCall))
+ {
+ Debug.LogError("Logic Graph failed to handle Animation Event '" + eventName + "'. Receiver was not found.");
+ return;
+ }
+
+ if (delegateToCall == null)
+ {
+ Debug.LogError("Logic Graph failed to handle Animation Event '" + eventName + "'. Receiver was null.");
+ return;
+ }
+
+ delegateToCall ();
+ }
+ }
+}
+
diff --git a/Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/OnCollisionEventDummy.cs b/Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/OnCollisionEventDummy.cs
new file mode 100644
index 0000000..09ccc8a
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/OnCollisionEventDummy.cs
@@ -0,0 +1,43 @@
+using System;
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ // For now we do triggers by attaching this MonoBehaviour to needed gameobjects. Class then sends events to logic graph nodes.
+ public class OnCollisionEventDummy : ColliderDummyBase
+ {
+ public delegate void CollisionOutDelegate(Collision other);
+
+ private CollisionOutDelegate m_OnEnter;
+ private CollisionOutDelegate m_OnExit;
+ private CollisionOutDelegate m_OnStay;
+
+ public static void AttachToCollider(ColliderNodes.OnCollisionEvent node)
+ {
+ var attached = AttachToCollider(node.self, typeof(OnCollisionEventDummy)) as OnCollisionEventDummy;
+ attached.m_OnEnter += node.EnterDummy;
+ attached.m_OnExit += node.ExitDummy;
+ attached.m_OnStay += node.StayDummy;
+ }
+
+ public void OnCollisionEnter(Collision collision)
+ {
+ if (m_OnEnter == null)
+ return;
+ m_OnEnter (collision);
+ }
+ public void OnCollisionExit(Collision collision)
+ {
+ if (m_OnExit == null)
+ return;
+ m_OnExit(collision);
+ }
+ public void OnCollisionStay(Collision collision)
+ {
+ if (m_OnStay == null)
+ return;
+ m_OnStay(collision);
+ }
+ }
+}
+
diff --git a/Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/OnMouseEventDummy.cs b/Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/OnMouseEventDummy.cs
new file mode 100644
index 0000000..c359edf
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/OnMouseEventDummy.cs
@@ -0,0 +1,41 @@
+using System;
+using UnityEngine;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ // For now we do triggers by attaching this MonoBehaviour to needed gameobjects. Class then sends events to logic graph nodes.
+ public class OnMouseEventDummy : ColliderDummyBase
+ {
+ private Action m_OnEnter;
+ private Action m_OnOver;
+ private Action m_OnExit;
+ private Action m_OnDown;
+ private Action m_OnUp;
+ private Action m_OnDrag;
+
+ public static void AttachToCollider (ColliderNodes.OnMouseEvent node)
+ {
+ var attached = AttachToCollider(node.self, typeof(OnMouseEventDummy)) as OnMouseEventDummy;
+ attached.m_OnEnter += node.enter;
+ attached.m_OnOver += node.over;
+ attached.m_OnExit += node.exit;
+ attached.m_OnDown += node.down;
+ attached.m_OnUp += node.up;
+ attached.m_OnDrag += node.drag;
+ }
+
+ public void OnMouseEnter () { CallEventDelegate (m_OnEnter); }
+ public void OnMouseOver () { CallEventDelegate (m_OnOver); }
+ public void OnMouseExit () { CallEventDelegate (m_OnExit); }
+ public void OnMouseDown () { CallEventDelegate (m_OnDown); }
+ public void OnMouseUp () { CallEventDelegate (m_OnUp); }
+ public void OnMouseDrag () { CallEventDelegate (m_OnDrag); }
+
+ protected static void CallEventDelegate(Action eventDelegate)
+ {
+ if (eventDelegate != null)
+ eventDelegate();
+ }
+ }
+}
+
diff --git a/Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/OnTriggerEventDummy.cs b/Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/OnTriggerEventDummy.cs
new file mode 100644
index 0000000..992f240
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/MonoBehaviourEventDummies/OnTriggerEventDummy.cs
@@ -0,0 +1,40 @@
+using System;
+using UnityEngine;
+using System.Collections.Generic;
+
+namespace UnityEngine.Graphs.LogicGraph
+{
+ // For now we do triggers by attaching this MonoBehaviour to needed gameobjects. Class then sends events to logic graph nodes.
+ public class OnTriggerEventDummy : ColliderDummyBase
+ {
+ public delegate void TriggerOutDelegate (Collider other);
+ private TriggerOutDelegate m_OnEnter;
+ private TriggerOutDelegate m_OnExit;
+ private TriggerOutDelegate m_OnStay;
+
+ public static void AttachToCollider(ColliderNodes.OnTriggerEvent node)
+ {
+ var attached = AttachToCollider(node.self, typeof(OnTriggerEventDummy)) as OnTriggerEventDummy;
+ attached.m_OnEnter += node.EnterDummy;
+ attached.m_OnExit += node.ExitDummy;
+ attached.m_OnStay += node.StayDummy;
+ }
+
+ public void OnTriggerEnter(Collider other)
+ {
+ if (m_OnEnter != null)
+ m_OnEnter (other);
+ }
+ public void OnTriggerExit(Collider other)
+ {
+ if (m_OnExit != null)
+ m_OnExit(other);
+ }
+ public void OnTriggerStay(Collider other)
+ {
+ if (m_OnStay != null)
+ m_OnStay (other);
+ }
+ }
+}
+
diff --git a/Runtime/Graphs/UnityEngine.Graphs/TestHelpers.cs b/Runtime/Graphs/UnityEngine.Graphs/TestHelpers.cs
new file mode 100644
index 0000000..feb438b
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/TestHelpers.cs
@@ -0,0 +1,33 @@
+#if TESTING
+
+using UnityEngine;
+
+namespace UnityEngine.Graphs
+{
+ public class Testing
+ {
+ public class Object
+ {
+ public string name;
+ }
+
+ public class ObjectSub1 : Object { }
+ public class ObjectSub2 : Object { }
+
+ // fake animation curve. can't seem to be able to initialize real one out of Unity
+ public class AnimationCurve
+ {
+ public WrapMode postWrapMode;
+ public WrapMode preWrapMode;
+ public Keyframe[] keys;
+ public AnimationCurve(params Keyframe[] frames) { keys = frames; }
+ }
+
+ public static bool AmIRunningInMono()
+ {
+ // internet says this is a supported way of detecting mono runtime
+ return System.Type.GetType ("Mono.Runtime") != null;
+ }
+ }
+}
+#endif
diff --git a/Runtime/Graphs/UnityEngine.Graphs/UnityEngine.Graphs.csproj b/Runtime/Graphs/UnityEngine.Graphs/UnityEngine.Graphs.csproj
new file mode 100644
index 0000000..7f3a47b
--- /dev/null
+++ b/Runtime/Graphs/UnityEngine.Graphs/UnityEngine.Graphs.csproj
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.30729</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{10E97B21-AEA1-4E95-BC6B-717815F18EBE}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <RootNamespace>UnityEngine.Graphs</RootNamespace>
+ <AssemblyName>UnityEngine.Graphs</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <FileUpgradeFlags>
+ </FileUpgradeFlags>
+ <OldToolsVersion>3.5</OldToolsVersion>
+ <UpgradeBackupLocation />
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>TRACE;DEBUG</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\Projects\CSharp\UnityEngine.csproj">
+ <Project>{F0499708-3EB6-4026-8362-97E6FFC4E7C8}</Project>
+ <Name>UnityEngine</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="AnimationNodeLibrary.cs" />
+ <Compile Include="Attributes.cs" />
+ <Compile Include="DefaultValueAttribute.cs" />
+ <Compile Include="GraphBehaviour.cs" />
+ <Compile Include="LogicNodeLibrary\Animation\AnimationNodes.cs" />
+ <Compile Include="LogicNodeLibrary\Animation\SimpleAnimationPlayer.cs" />
+ <Compile Include="LogicNodeLibrary\AudioSource\AudioSourceNodes.cs" />
+ <Compile Include="LogicNodeLibrary\CharacterController\SimpleCharacterControllerNodes.cs" />
+ <Compile Include="LogicNodeLibrary\Collections\CollectionsNodes.cs" />
+ <Compile Include="LogicNodeLibrary\Collider\OnCollisionEvent.cs" />
+ <Compile Include="LogicNodeLibrary\Collider\OnMouseEvent.cs" />
+ <Compile Include="LogicNodeLibrary\Collider\OnTriggerEvent.cs" />
+ <Compile Include="LogicNodeLibrary\Component\ComponentNodes.cs" />
+ <Compile Include="LogicNodeLibrary\Input\InputNodes.cs" />
+ <Compile Include="LogicNodeLibrary\Input\OnAxis.cs" />
+ <Compile Include="LogicNodeLibrary\Input\OnButton.cs" />
+ <Compile Include="LogicNodeLibrary\Input\OnInputNode.cs" />
+ <Compile Include="LogicNodeLibrary\Input\OnKey.cs" />
+ <Compile Include="LogicNodeLibrary\Input\OnMouseButton.cs" />
+ <Compile Include="LogicNodeLibrary\LogicNodeUtility.cs" />
+ <Compile Include="LogicNodeLibrary\Material\MaterialNodes.cs" />
+ <Compile Include="LogicNodeLibrary\NodeLibrary.cs" />
+ <Compile Include="LogicNodeLibrary\Rigidbody\RigidbodyNodes.cs" />
+ <Compile Include="LogicNodeLibrary\Transform\LookAt.cs" />
+ <Compile Include="LogicNodeLibrary\Transform\MoveTo.cs" />
+ <Compile Include="LogicNodeLibrary\Transform\RotateTo.cs" />
+ <Compile Include="LogicNodeLibrary\Transform\TransformNodes.cs" />
+ <Compile Include="LogicNodeLibrary\Transform\YieldedTransformNodeBase.cs" />
+ <Compile Include="LogicNodeLibrary\YieldedNodeBase.cs" />
+ <Compile Include="LogicNodeTestLibrary.cs" />
+ <Compile Include="MonoBehaviourEventDummies\ColliderDummyBase.cs" />
+ <Compile Include="MonoBehaviourEventDummies\OnAnimationEventDummy.cs" />
+ <Compile Include="MonoBehaviourEventDummies\OnCollisionEventDummy.cs" />
+ <Compile Include="MonoBehaviourEventDummies\OnMouseEventDummy.cs" />
+ <Compile Include="MonoBehaviourEventDummies\OnTriggerEventDummy.cs" />
+ <Compile Include="TestHelpers.cs" />
+ </ItemGroup>
+ <ItemGroup />
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file
diff --git a/Runtime/IMGUI/GUIButton.cpp b/Runtime/IMGUI/GUIButton.cpp
new file mode 100644
index 0000000..d83462b
--- /dev/null
+++ b/Runtime/IMGUI/GUIButton.cpp
@@ -0,0 +1,77 @@
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/GUIButton.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+
+static const int kGUIButtonHash = 2001146706;
+
+namespace IMGUI
+{
+
+bool GUIButton (GUIState &state, const Rectf &position, GUIContent &content, GUIStyle &style, int id)
+{
+ InputEvent &evt (*state.m_CurrentEvent);
+ switch (GetEventTypeForControl (state, evt, id))
+ {
+ case InputEvent::kMouseDown:
+ // If the mouse is inside the button, we say that we're the hot control
+#if ENABLE_NEW_EVENT_SYSTEM
+ if (position.Contains (evt.touch.pos))
+#else
+ if (position.Contains (evt.mousePosition))
+#endif
+ {
+ GrabMouseControl (state, id);
+ evt.Use ();
+ }
+ break;
+ case InputEvent::kMouseUp:
+ if (HasMouseControl (state, id))
+ {
+ ReleaseMouseControl (state);
+
+ // If we got the mousedown, the mouseup is ours as well
+ // (no matter if the click was in the button or not)
+ evt.Use ();
+
+ // toggle the passed-in value if the mouse was over the button & return true
+#if ENABLE_NEW_EVENT_SYSTEM
+ if (position.Contains (evt.touch.pos))
+#else
+ if (position.Contains (evt.mousePosition))
+#endif
+ {
+ state.m_OnGUIState.m_Changed = true;
+ return true;
+ }
+ }
+ break;
+ case InputEvent::kKeyDown:
+ if (evt.character == 32 && state.m_MultiFrameGUIState.m_KeyboardControl == id)
+ {
+ evt.Use ();
+ state.m_OnGUIState.m_Changed = true;
+ return true;
+ }
+ break;
+ case InputEvent::kMouseDrag:
+ if (HasMouseControl (state, id))
+ evt.Use ();
+ break;
+
+ case InputEvent::kRepaint:
+ style.Draw (state, position, content, id, false);
+ break;
+ }
+ return false;
+}
+
+bool GUIButton (GUIState &state, const Rectf &position, GUIContent &content, GUIStyle &style)
+{
+ int id = GetControlID (state, kGUIButtonHash, kNative, position);
+ return GUIButton (state, position, content, style, id);
+}
+
+
+}
diff --git a/Runtime/IMGUI/GUIButton.h b/Runtime/IMGUI/GUIButton.h
new file mode 100644
index 0000000..29b2404
--- /dev/null
+++ b/Runtime/IMGUI/GUIButton.h
@@ -0,0 +1,16 @@
+#ifndef GUIBUTTON_H
+#define GUIBUTTON_H
+
+#include "Runtime/Math/Rect.h"
+
+struct GUIState;
+struct GUIContent;
+class GUIStyle;
+
+namespace IMGUI
+{
+ bool GUIButton (GUIState &state, const Rectf &position, GUIContent &content, GUIStyle &style, int id);
+ bool GUIButton (GUIState &state, const Rectf &position, GUIContent &content, GUIStyle &style);
+}
+
+#endif
diff --git a/Runtime/IMGUI/GUIClip.cpp b/Runtime/IMGUI/GUIClip.cpp
new file mode 100644
index 0000000..203c1e9
--- /dev/null
+++ b/Runtime/IMGUI/GUIClip.cpp
@@ -0,0 +1,419 @@
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/GUIClip.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Math/Quaternion.h"
+
+static const float ko1 = -10000, ko2 = 10000, ko3= 0, ko4 = 0;
+
+GUIClipState::GUIClipState()
+{
+ m_Enabled = 0;
+}
+
+GUIClipState::~GUIClipState ()
+{
+}
+
+
+/// Push a clip rect to the stack with pixel offsets.
+/// This is the low-level function for doing clipping rectangles. Unless you're working with embedded
+/// temporary render buffers, this is most likely not what you want.
+/// /absoluteRect/ is the absolute device coordinates this element will be mapped to.
+/// /scrollOffset/ is a scrolling offset to apply.
+/// /renderOffset/ is the rendering offset of the absoluteRect from source to destination. Used to map from an on-screen rectangle to a
+/// destination inside a render buffer.
+void GUIClipState::Push (InputEvent& event, const Rectf& screenRect, Vector2f scrollOffset, const Vector2f& renderOffset, bool resetOffset)
+{
+ if (m_GUIClips.empty())
+ {
+ ErrorString("GUIClip pushing empty stack not allowed.");
+ return;
+ }
+
+ GUIClip& topmost = m_GUIClips.back ();
+ // build absolute offsets by adding parent's position & scroll to the screenRect's positions
+ float physicalxMin = screenRect.x + topmost.physicalRect.x + topmost.scrollOffset.x;
+ float physicalxMax = screenRect.GetXMax() + topmost.physicalRect.x + topmost.scrollOffset.x;
+ float physicalyMin = screenRect.y + topmost.physicalRect.y + topmost.scrollOffset.y;
+ float physicalyMax = screenRect.GetYMax() + topmost.physicalRect.y + topmost.scrollOffset.y;
+
+ // If the user tries to push a GUIClip with an xMin that goes outside the parent's clipping, we cannot allow that
+ // so we move the xMin in and use scrollOffset to hide this.
+ if (physicalxMin < topmost.physicalRect.x)
+ {
+ scrollOffset.x += physicalxMin - topmost.physicalRect.x;
+ physicalxMin = topmost.physicalRect.x;
+ }
+
+ // Clip top side (yMin) to the parent as well.
+ if (physicalyMin < topmost.physicalRect.y)
+ {
+ scrollOffset.y += physicalyMin - topmost.physicalRect.y;
+ physicalyMin = topmost.physicalRect.y;
+ }
+
+ // Clip right side (xMax) as well.
+ if (physicalxMax > topmost.physicalRect.GetXMax())
+ {
+ physicalxMax = topmost.physicalRect.GetXMax();
+ }
+
+ // Clip bottom side (yMax) as well.
+ if (physicalyMax > topmost.physicalRect.GetYMax())
+ {
+ physicalyMax = topmost.physicalRect.GetYMax();
+ }
+
+ // if the new GUIClip is completely outside parent, sizes can get negative.
+ // We just make them be 0, so no rendering is performed.
+ if (physicalxMax <= physicalxMin)
+ physicalxMax = physicalxMin;
+ if (physicalyMax <= physicalyMin)
+ physicalyMax = physicalyMin;
+
+ // Build the rect straight away
+ Rectf absoluteRect = MinMaxRect (physicalxMin, physicalyMin, physicalxMax, physicalyMax);
+
+ if (!resetOffset)
+ {
+ // Maintian global render offset
+ m_GUIClips.push_back(GUIClip (screenRect, absoluteRect, scrollOffset, topmost.renderOffset + renderOffset, topmost.globalScrollOffset + scrollOffset));
+ }
+ else
+ {
+ // Maintian global scroll offset
+ m_GUIClips.push_back (GUIClip (screenRect, absoluteRect, scrollOffset,
+ Vector2f(absoluteRect.x + scrollOffset.x + renderOffset.x, absoluteRect.y + scrollOffset.y + renderOffset.y),
+ topmost.globalScrollOffset + scrollOffset));
+ }
+
+ Apply(event, m_GUIClips.back());
+}
+
+/// Removes the topmost clipping rectangle, undoing the effect of the latest GUIClip.Push
+void GUIClipState::Pop (InputEvent& event)
+{
+ if (m_GUIClips.size() < 2)
+ {
+ ErrorString("Invalid GUIClip stack popping");
+ return;
+ }
+
+ m_GUIClips.pop_back();
+ Apply(event, m_GUIClips.back());
+}
+
+Vector2f GUIClipState::Unclip (const Vector2f& pos)
+{
+ if (!m_GUIClips.empty())
+ {
+ GUIClip& topmost = m_GUIClips.back();
+ Vector3f res;
+ m_Matrix.PerspectiveMultiplyPoint3 (Vector3f (pos.x, pos.y, 0.0F), res);
+ return Vector2f(res.x, res.y) + topmost.scrollOffset + Vector2f (topmost.physicalRect.x, topmost.physicalRect.y);
+ }
+ else
+ {
+ return Vector2f (0,0);
+ }
+}
+
+Rectf GUIClipState::Unclip (const Rectf& rect)
+{
+ if (!m_GUIClips.empty())
+ {
+ GUIClip& topmost = m_GUIClips.back();
+
+ return Rectf (rect.x + topmost.scrollOffset.x + topmost.physicalRect.x,
+ rect.y + topmost.scrollOffset.y + topmost.physicalRect.y,
+ rect.width, rect.height);
+ }
+ else
+ {
+ return Rectf (0,0,0,0);
+ }
+}
+
+/// Clips /absolutePos/ to drawing coordinates
+/// Used for reconverting values calculated from ::ref::Unclip
+Vector2f GUIClipState::Clip (const Vector2f& absolutePos)
+{
+ if (!m_GUIClips.empty())
+ {
+ GUIClip& topmost = m_GUIClips.back();
+
+ Vector3f transformedPoint;
+ m_InverseMatrix.PerspectiveMultiplyPoint3(Vector3f(absolutePos.x, absolutePos.y, 0.0F), transformedPoint);
+
+ // return (Vector2)s_InverseMatrix.MultiplyPoint (absolutePos) - topmost.globalScrollOffset - new Vector2 (topmost.physicalRect.x, topmost.physicalRect.y);
+ Vector2f res = Vector2f(transformedPoint.x, transformedPoint.y) - topmost.scrollOffset - Vector2f (topmost.physicalRect.x, topmost.physicalRect.y);
+ return Vector2f(res.x, res.y);
+ }
+ else
+ {
+ return Vector2f (0,0);
+ }
+}
+
+/// Convert /absoluteRect/ to drawing coordinates
+/// Used for reconverting values calculated from ::ref::Unclip
+Rectf GUIClipState::Clip (const Rectf& absoluteRect)
+{
+ if (!m_GUIClips.empty())
+ {
+ GUIClip& topmost = m_GUIClips.back();
+
+ return Rectf (absoluteRect.x - topmost.globalScrollOffset.x - topmost.physicalRect.x,
+ absoluteRect.y - topmost.globalScrollOffset.y - topmost.physicalRect.y,
+ absoluteRect.width, absoluteRect.height);
+ }
+ else
+ {
+ return Rectf (0,0,0,0);
+ }
+}
+
+// Return the rect for the topmost clip in screen space
+Rectf GUIClipState::GetTopRect ()
+{
+ if (!m_GUIClips.empty())
+ {
+ GUIClip& topmost = m_GUIClips.back();
+ return topmost.screenRect;
+ }
+ else
+ {
+ return Rectf (0,0,0,0);
+ }
+}
+
+
+/// Reapply the clipping info.
+/// Call this after switching render buffers.
+void GUIClipState::Reapply (InputEvent& event)
+{
+ if (!m_GUIClips.empty())
+ Apply (event, m_GUIClips.back());
+}
+
+
+void GUIClipState::SetMatrix (InputEvent& event, const Matrix4x4f& m)
+{
+ m_Matrix = m;
+
+ Matrix4x4f inverse;
+ bool success = Matrix4x4f::Invert_Full(m, inverse);
+ if (!success)
+ {
+ ErrorString ("Ignoring invalid matrix assinged to GUI.matrix - the matrix needs to be invertible. Did you scale by 0 on Z-axis?");
+ return;
+ }
+
+ m_Matrix = m; // Store the value
+ m_InverseMatrix = inverse;
+ Reapply (event); // Reapply the toplevel cliprect.
+}
+
+/// constructor
+GUIClip::GUIClip (const Rectf& iscreenRect, const Rectf& iphysicalRect, const Vector2f& iscrollOffset, const Vector2f& irenderOffset, const Vector2f& iglobalScrollOffset)
+{
+ // Debug.Log ("GUIClipping: " + physicalRect + scrollOffset + renderOffset + globalScrollOffset);
+
+ screenRect = iscreenRect;
+ physicalRect = iphysicalRect;
+ scrollOffset = iscrollOffset;
+ renderOffset = irenderOffset;
+ globalScrollOffset = iglobalScrollOffset;
+}
+
+// Recalculate the mouse values from the absolute screen position into local GUI coordinates, taking cliprects & all into account.
+void GUIClipState::CalculateMouseValues (InputEvent& event)
+{
+ if (!m_GUIClips.empty())
+ {
+#if ENABLE_NEW_EVENT_SYSTEM
+ event.touch.pos = Clip (m_AbsoluteMousePosition);
+#else
+ event.mousePosition = Clip (m_AbsoluteMousePosition);
+#endif
+
+ // Check if we're outside the cliprect & set the event ignore flag
+ Vector3f res;
+ m_InverseMatrix.PerspectiveMultiplyPoint3 (Vector3f(m_AbsoluteMousePosition.x, m_AbsoluteMousePosition.y, 0.0F), res);
+
+ GUIClip& topmost = m_GUIClips.back();
+ m_Enabled = topmost.physicalRect.Contains (res.x, res.y) ? -1 : 0;
+
+ // scrollwheel is a specialcase
+ if (event.type != InputEvent::kScrollWheel)
+ {
+#if ENABLE_NEW_EVENT_SYSTEM
+ event.touch.deltaPos = event.touch.pos - Clip (m_AbsoluteLastMousePosition);
+#else
+ event.delta = event.mousePosition - Clip (m_AbsoluteLastMousePosition);
+#endif
+ }
+ }
+}
+
+/// Apply the current clip rect to OpenGL's viewport & scissor rects.
+void GUIClipState::Apply (InputEvent& event, GUIClip &topmost)
+{
+ // Warp the current event to the correct place in the new coordinate space
+
+ CalculateMouseValues (event);
+
+ m_VisibleRect = Rectf (-topmost.scrollOffset.x, -topmost.scrollOffset.y, topmost.physicalRect.width, topmost.physicalRect.height);
+
+ // From here on out, we're only setting up OpenGL, so abort if we're not repainting
+ if (event.type != InputEvent::kRepaint)
+ return;
+
+ // Calculate the viewport (where we end up on screen)
+ Rectf r = topmost.physicalRect;
+
+ if (r.width < 0) r.width = 0;
+ if (r.height < 0) r.height = 0;
+
+ r.x -= topmost.renderOffset.x;
+ r.y -= topmost.renderOffset.y;
+
+
+ r.x = RoundfToInt(r.x);
+ r.y = RoundfToInt (r.y);
+ r.width = RoundfToIntPos (r.width);
+ r.height = RoundfToIntPos (r.height);
+
+ Matrix4x4f viewportMatrix;
+ viewportMatrix.SetIdentity();
+
+ float width, height;
+ RenderTexture *rTex = RenderTexture::GetActive ();
+ if (rTex)
+ {
+ width = rTex->GetWidth ();
+ height = rTex->GetHeight ();
+ }
+ else
+ {
+ width = GetScreenManager ().GetWidth ();
+ height = GetScreenManager ().GetHeight ();
+ }
+
+ Vector3f scaleFac = Vector3f (r.width / width, r.height / height,1);
+ Vector3f move;
+ m_Matrix.PerspectiveMultiplyPoint3 (Vector3f (r.x, r.y,0), move);
+
+ ///@TODO: OPTIMIZE non rotation
+ viewportMatrix.SetTRS(Vector3f(move.x * scaleFac.x, move.y * scaleFac.y, 0.0F), Quaternionf::identity(), scaleFac);
+
+ SetGLViewport (Rectf (0,0, width, height));
+
+ // The ortho bounds passed to LoadPixelMatrix gets multiplied by the matrix as well
+ // We need to counter that for the scroll offsets - so stuff like scale doesn't apply to scrolling
+ Vector3f sOffset;
+ m_Matrix.PerspectiveMultiplyPoint3 (Vector3f(-topmost.scrollOffset.x, -topmost.scrollOffset.y, 0.0F), sOffset);
+ sOffset.x *= scaleFac.x;
+ sOffset.y *= scaleFac.y;
+
+ // Scale X & Y
+ // Upload the client coordinate system.
+ // Here we multiply in the scaleFac on the scrollOffsets - its something with Matrix ordering - a case of "iterative debugging": I kept chanigng it (at random) until it worked...
+ Matrix4x4f pixelMatrix;
+ MultiplyMatrices4x4 (&viewportMatrix, &m_Matrix, &pixelMatrix);
+ LoadPixelMatrix(
+ sOffset.x, Roundf (topmost.physicalRect.width) + sOffset.x,
+ Roundf (topmost.physicalRect.height) + sOffset.y, sOffset.y,
+ pixelMatrix
+ );
+ GUIStyle::SetGUIClipRect(m_VisibleRect);
+}
+
+/// Load the pixel matrix and also multiply in a GUIMatrix
+void GUIClipState::LoadPixelMatrix(float left, float right, float bottom, float top, const Matrix4x4f& mat)
+{
+ Rectf rect( left, bottom, right-left, top-bottom );
+ Matrix4x4f matrix;
+ CalcPixelMatrix (rect, matrix);
+
+ // Important: apply half-texel offsets after multiplying the matrix!
+ matrix *= mat;
+ ApplyTexelOffsetsToPixelMatrix( true, matrix );
+
+ GfxDevice& device = GetGfxDevice();
+ device.SetProjectionMatrix(matrix);
+ device.SetViewMatrix (Matrix4x4f::identity.GetPtr()); // implicitly sets world to identity
+}
+
+Rectf GUIClipState::GetTopMostPhysicalRect ()
+{
+ return m_GUIClips.back().physicalRect;
+}
+
+//////@TODO:
+// CSRAW public override string ToString () {
+// return System.String.Format ("GUIClip: physicalRect {0}, scrollOffset {1}, renderOffset {2}, globalScrollOffset{3}", physicalRect, scrollOffset, renderOffset, globalScrollOffset);
+// }
+
+/// Set up the clip rect to contain the entire display.
+/// called by GUI.Begin to initialize the view.
+void GUIClipState::BeginOnGUI (InputEvent& event)
+{
+#if ENABLE_NEW_EVENT_SYSTEM
+ m_AbsoluteMousePosition = event.touch.pos;
+ m_AbsoluteLastMousePosition = m_AbsoluteMousePosition - event.touch.deltaPos;
+#else
+ m_AbsoluteMousePosition = event.mousePosition;
+ m_AbsoluteLastMousePosition = m_AbsoluteMousePosition - event.delta;
+#endif
+ m_Matrix.SetIdentity();
+ m_InverseMatrix.SetIdentity();
+
+ m_GUIClips.resize (0);
+ // Push in a really large screen, so GUI.matrix scales doesn't crop the root level.
+ m_GUIClips.push_back (GUIClip (Rectf (ko1, ko1, 40000,40000), Rectf (ko1, ko1, 40000,40000), Vector2f (ko2, ko2), Vector2f (ko3,ko3), Vector2f (ko4,ko4)));
+
+ Apply (event, m_GUIClips.back());
+}
+
+void GUIClipState::SetAbsoluteMousePosition (const Vector2f& val)
+{
+ m_AbsoluteMousePosition = val;
+}
+
+/// *End the current GUI stuff.
+void GUIClipState::EndOnGUI (InputEvent &event)
+{
+ InputEvent::Type eventType = event.type;
+ if (m_GUIClips.size() != 1 && eventType != InputEvent::kIgnore && eventType != InputEvent::kUsed)
+ {
+ if (m_GUIClips.size() > 1)
+ {
+ ErrorString ("GUI Error: You are pushing more GUIClips than you are popping. Make sure they are balanced)");
+ }
+ else
+ {
+ ErrorString ("GUI Error: You are popping more GUIClips than you are pushing. Make sure they are balanced)");
+ return;
+ }
+ }
+ m_GUIClips.pop_back();
+ // Make sure we restore the mouse values. Otherwise things like GUi.Matrix's modifications to
+ // MouseEvents will seep into the next OnGUI
+#if ENABLE_NEW_EVENT_SYSTEM
+ event.touch.deltaPos = m_AbsoluteMousePosition - m_AbsoluteLastMousePosition;
+ event.touch.pos = m_AbsoluteMousePosition;
+#else
+ event.delta = m_AbsoluteMousePosition - m_AbsoluteLastMousePosition;
+ event.mousePosition = m_AbsoluteMousePosition;
+#endif
+}
+
+void GUIClipState::EndThroughException ()
+{
+ m_GUIClips.resize(0);
+}
diff --git a/Runtime/IMGUI/GUIClip.h b/Runtime/IMGUI/GUIClip.h
new file mode 100644
index 0000000..3ef5e05
--- /dev/null
+++ b/Runtime/IMGUI/GUIClip.h
@@ -0,0 +1,107 @@
+#pragma once
+
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Misc/InputEvent.h"
+
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+class GUIClip
+{
+ public:
+
+ GUIClip () {}
+
+
+ /// The rectangle of this clipping rect.
+ /// This is stored absolute coordinates
+ Rectf physicalRect;
+
+ // The rectangle of the clip in screen space
+ Rectf screenRect;
+
+ /// physical scrolling offset for coordinates - this is relative to parent.
+ Vector2f scrollOffset;
+ /// This is absolute
+ Vector2f globalScrollOffset;
+
+ /// rendering offset. This is the global GUIClip->buffer coordinates
+ Vector2f renderOffset;
+
+ /// constructor
+ GUIClip (const Rectf& iscreenRect, const Rectf& iphysicalRect, const Vector2f& iscrollOffset, const Vector2f& irenderOffset, const Vector2f& iglobalScrollOffset);
+};
+
+// MUST BYTE-MATCH CORRESPONDING STRUCT IN GUISTATEMONO
+struct GUIClipState
+{
+ typedef std::vector<GUIClip> ClipStack;
+ ClipStack m_GUIClips;
+
+ private:
+ Matrix4x4f m_Matrix;
+ Matrix4x4f m_InverseMatrix;
+
+ // Where is the mouse onscreen, and where was it last frame (so we can calculate deltas correctly)
+ Vector2f m_AbsoluteMousePosition;
+ Vector2f m_AbsoluteLastMousePosition;
+
+ Rectf m_VisibleRect;
+
+ // Should we disable events?
+ int m_Enabled;
+
+ public:
+ GUIClipState ();
+ ~GUIClipState ();
+
+ Rectf GetTopMostPhysicalRect ();
+
+ /// Set up the clip rect to contain the entire display.
+ /// called by GUI.Begin to initialize the view.
+ void BeginOnGUI (InputEvent& ievent);
+ void EndOnGUI (InputEvent& ievent);
+ void EndThroughException ();
+
+ /// This is the low-level function for doing clipping rectangles. Unless you're working with embedded temporary render buffers, this is most likely not what you want.
+ /// /absoluteRect/ is the absolute device coordinates this element will be mapped to.
+ /// /scrollOffset/ is a scrolling offset to apply.
+ /// /renderOffset/ is the rendering offset of the absoluteRect from source to destination. Used to map from an on-screen rectangle to a destination inside a render buffer.
+ void Push (InputEvent& ievent, const Rectf& screenRect, Vector2f scrollOffset, const Vector2f& renderOffset, bool resetOffset);
+ /// Removes the topmost clipping rectangle, undoing the effect of the latest GUIClip.Push
+ void Pop (InputEvent& ievent);
+
+ /// Unclips /pos/ to physical device coordinates.
+ Vector2f Unclip (const Vector2f& pos);
+ Rectf Unclip (const Rectf& rect);
+
+ /// Clips /absolutePos/ to drawing coordinates
+ Vector2f Clip (const Vector2f& absolutePos);
+ Rectf Clip (const Rectf& absoluteRect);
+
+ // Return the rect for the topmost clip in screen space
+ Rectf GetTopRect();
+
+
+ /// Reapply the clipping info. Call this after switching render buffers.
+ void Reapply (InputEvent& ievent);
+ // Set the GUIMatrix. This is here as this class handles all coordinate transforms anyways.
+ const Matrix4x4f& GetMatrix () { return m_Matrix; }
+ void SetMatrix (InputEvent& ievent, const Matrix4x4f& m);
+
+ Vector2f GetAbsoluteMousePosition () { return m_AbsoluteMousePosition; }
+ Rectf GetVisibleRect () { return m_VisibleRect; }
+
+ bool GetEnabled () const { return m_Enabled; }
+ private:
+ // Recalculate the mouse values from the absolute screen position into local GUI coordinates, taking cliprects & all into account.
+ void CalculateMouseValues (InputEvent& ievent);
+ static void LoadPixelMatrix(float left, float right, float bottom, float top, const Matrix4x4f& mat);
+ void SetAbsoluteMousePosition (const Vector2f& absoluteMousePosition);
+
+
+ // Apply the current clip rect to event pointer positions & render settings for culling, etc.
+ void Apply (InputEvent& ievent, GUIClip &topmost);
+};
diff --git a/Runtime/IMGUI/GUIContent.cpp b/Runtime/IMGUI/GUIContent.cpp
new file mode 100644
index 0000000..6b811c7
--- /dev/null
+++ b/Runtime/IMGUI/GUIContent.cpp
@@ -0,0 +1,58 @@
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/GUIContent.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+#if ENABLE_SCRIPTING
+static GUIContent s_TempGUIContent;
+#if UNITY_WINRT
+void MonoGUIContentToTempNativeCallback(Platform::String^ text, Platform::String^ tooltip, long long image)
+{
+ s_TempGUIContent.m_Text.BorrowString (text);
+ s_TempGUIContent.m_Tooltip.BorrowString (tooltip);
+ s_TempGUIContent.m_Image = ScriptingObjectToObject<Texture> (ScriptingObjectPtr(image));
+}
+BridgeInterface::ScriptingGUIContentToTempNativeDelegateGC^ GetMonoGUIContentToTempNativeCallback()
+{
+ static BridgeInterface::ScriptingGUIContentToTempNativeDelegateGC^ s_Callback = ref new BridgeInterface::ScriptingGUIContentToTempNativeDelegateGC(MonoGUIContentToTempNativeCallback);
+ return s_Callback;
+}
+
+#endif
+
+GUIContent &MonoGUIContentToTempNative (ScriptingObjectPtr scriptingContent)
+{
+ MonoGUIContentToNative (scriptingContent, s_TempGUIContent);
+ return s_TempGUIContent;
+}
+
+void MonoGUIContentToNative (ScriptingObjectPtr scriptingContent, GUIContent& cppContent)
+{
+ if (scriptingContent == SCRIPTING_NULL)
+ {
+ WarningString("GUIContent is null. Use GUIContent.none.");
+ cppContent.m_Text = (UTF16String) "";
+ cppContent.m_Tooltip = (UTF16String) "";
+ cppContent.m_Image = NULL;
+
+ return;
+ }
+
+#if UNITY_WINRT
+ GetWinRTUtils()->ScriptingGUIContentToTempNativeGC(scriptingContent.GetHandle(), GetMonoGUIContentToTempNativeCallback());
+#else
+ MonoGUIContent nativeContent;
+ MarshallManagedStructIntoNative(scriptingContent, &nativeContent);
+
+# if ENABLE_MONO
+ cppContent.m_Text.BorrowString (nativeContent.m_Text);
+ cppContent.m_Tooltip.BorrowString (nativeContent.m_Tooltip);
+# elif UNITY_FLASH
+ Ext_WriteTextAndToolTipIntoUTF16Strings(scriptingContent,&cppContent.m_Text,&cppContent.m_Tooltip);
+# endif
+
+ cppContent.m_Image = ScriptingObjectToObject<Texture> (nativeContent.m_Image);
+#endif
+ return;
+}
+
+#endif
diff --git a/Runtime/IMGUI/GUIContent.h b/Runtime/IMGUI/GUIContent.h
new file mode 100644
index 0000000..5570bf8
--- /dev/null
+++ b/Runtime/IMGUI/GUIContent.h
@@ -0,0 +1,36 @@
+#ifndef GUIContent_H
+#define GUIContent_H
+
+#include "Runtime/IMGUI/TextUtil.h"
+#include "Runtime/Graphics/Texture.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Misc/UTF8.h"
+
+struct GUIContent
+{
+ UTF16String m_Text;
+ UTF16String m_Tooltip;
+ PPtr<Texture> m_Image;
+
+ void operator = (const GUIContent& other)
+ {
+ m_Text.CopyString(other.m_Text);
+ m_Tooltip.CopyString(other.m_Tooltip);
+ m_Image = other.m_Image;
+ }
+};
+
+#if ENABLE_SCRIPTING
+
+struct MonoGUIContent
+{
+ ScriptingStringPtr m_Text;
+ ScriptingObjectPtr m_Image;
+ ScriptingStringPtr m_Tooltip;
+};
+
+GUIContent &MonoGUIContentToTempNative (ScriptingObjectPtr monoContent);
+void MonoGUIContentToNative (ScriptingObjectPtr monoContent, GUIContent& cppContent);
+#endif
+
+#endif
diff --git a/Runtime/IMGUI/GUIContentTests.cpp b/Runtime/IMGUI/GUIContentTests.cpp
new file mode 100644
index 0000000..4f45a64
--- /dev/null
+++ b/Runtime/IMGUI/GUIContentTests.cpp
@@ -0,0 +1,28 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/IMGUI/GUIContent.h"
+#include "Runtime/Testing/TestFixtures.h"
+#include "Runtime/Testing/Testing.h"
+
+SUITE (GUIContentTests)
+{
+ typedef TestFixtureBase Fixture;
+
+ TEST_FIXTURE (Fixture, NullContentDoesNotCrash)
+ {
+ //Set expectations.
+ EXPECT (Warning, "GUIContent is null. Use GUIContent.none.");
+
+ // Do.
+ GUIContent content = MonoGUIContentToTempNative (SCRIPTING_NULL);
+
+ // Assert.
+ CHECK ((UTF16String)"" == content.m_Text);
+ CHECK ((UTF16String)"" == content.m_Tooltip);
+ CHECK (content.m_Image.IsNull());
+ }
+}
+#endif
diff --git a/Runtime/IMGUI/GUILabel.cpp b/Runtime/IMGUI/GUILabel.cpp
new file mode 100644
index 0000000..a483a27
--- /dev/null
+++ b/Runtime/IMGUI/GUILabel.cpp
@@ -0,0 +1,29 @@
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/GUIButton.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+
+namespace IMGUI
+{
+void GUILabel (GUIState &state, const Rectf &position, GUIContent &content, GUIStyle &style)
+{
+ InputEvent &evt (*state.m_CurrentEvent);
+
+ if (evt.type == InputEvent::kRepaint)
+ {
+ style.Draw (state, position, content, false, false, false, false);
+
+ // Is inside label AND inside guiclip visible rect (prevents tooltips on labels that are clipped)
+#if ENABLE_NEW_EVENT_SYSTEM
+ if (content.m_Tooltip.length != 0 && position.Contains (evt.touch.pos) &&
+ state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect().Contains(evt.touch.pos))
+#else
+ if (content.m_Tooltip.length != 0 && position.Contains (evt.mousePosition) &&
+ state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect().Contains(evt.mousePosition))
+#endif
+ GUIStyle::SetMouseTooltip (state, content.m_Tooltip, position);
+ }
+}
+
+} // namespace
diff --git a/Runtime/IMGUI/GUILabel.h b/Runtime/IMGUI/GUILabel.h
new file mode 100644
index 0000000..61b8d56
--- /dev/null
+++ b/Runtime/IMGUI/GUILabel.h
@@ -0,0 +1,15 @@
+#ifndef GUILABEL_H
+#define GUILABEL_H
+
+#include "Runtime/Math/Rect.h"
+
+struct GUIState;
+struct GUIContent;
+class GUIStyle;
+
+namespace IMGUI
+{
+ void GUILabel (GUIState &state, const Rectf &position, GUIContent &content, GUIStyle &style);
+}
+
+#endif
diff --git a/Runtime/IMGUI/GUIManager.cpp b/Runtime/IMGUI/GUIManager.cpp
new file mode 100644
index 0000000..6270421
--- /dev/null
+++ b/Runtime/IMGUI/GUIManager.cpp
@@ -0,0 +1,571 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "GUIManager.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Input/InputManager.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/GUIWindows.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Utilities/UserAuthorizationManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingInvocation.h"
+
+#if SUPPORT_REPRODUCE_LOG
+#include <fstream>
+#include "Runtime/Misc/ReproductionLog.h"
+#endif
+
+#if ENABLE_UNITYGUI
+static GUIManager* s_GUIManager = NULL;
+
+void InitGUIManager ()
+{
+ AssertIf(s_GUIManager != NULL);
+ s_GUIManager = new GUIManager();
+
+ InitGUIState ();
+
+#if UNITY_HAS_DEVELOPER_CONSOLE
+ InitializeDeveloperConsole ();
+#endif // UNITY_HAS_DEVELOPER_CONSOLE
+}
+
+void CleanupGUIManager ()
+{
+#if UNITY_HAS_DEVELOPER_CONSOLE
+ CleanupDeveloperConsole();
+#endif // UNITY_HAS_DEVELOPER_CONSOLE
+ CleanupGUIState ();
+
+ AssertIf(s_GUIManager == NULL);
+ delete s_GUIManager;
+ s_GUIManager = NULL;
+}
+
+GUIManager &GetGUIManager () {
+ AssertIf(s_GUIManager == NULL);
+ return *s_GUIManager;
+}
+
+#if UNITY_EDITOR
+void GUIManager::Reset ()
+{
+ m_MasterState.Reset ();
+}
+#endif
+
+GUIManager::GUIManager () :
+ m_GUIPixelOffset (0.0f,0.0f)
+{
+ m_LastInputEventTime = 0.0f;
+ m_DidGUIWindowsEatLastEvent = false;
+ m_MouseUsed = false;
+ m_mouseButtonsDown = 0;
+}
+
+void GUIManager::AddGUIScript (MonoBehaviourListNode& beh)
+{
+ m_GUIScripts.push_back(beh);
+}
+
+PROFILER_INFORMATION(gGUIRepaintProfile, "GUI.Repaint", kProfilerGUI)
+PROFILER_INFORMATION(gGUIEventProfile, "GUI.ProcessEvents", kProfilerGUI)
+
+void GUIManager::Repaint () {
+ GetInputManager().SetTextFieldInput(false);
+
+ InputEvent ie;
+ ie = m_LastEvent;
+ ie.type = InputEvent::kRepaint;
+
+ DoGUIEvent(ie, false);
+}
+
+bool GUIManager::AnyMouseButtonsDown()
+{
+ return GetGUIManager().m_mouseButtonsDown != 0;
+}
+
+#if UNITY_EDITOR
+void GUIManager::SetEditorGUIInfo (Vector2f guiPixelOffset) {
+ m_GUIPixelOffset = guiPixelOffset;
+ if (MONO_COMMON.setViewInfo) {
+ ScriptingInvocation invocation(MONO_COMMON.setViewInfo);
+ invocation.AddStruct(&m_GUIPixelOffset);
+ invocation.Invoke();
+ }
+}
+
+Vector2f GUIManager::GetEditorGUIInfo() const
+{
+ return m_GUIPixelOffset;
+}
+#endif
+
+void GUIManager::SendQueuedEvents ()
+{
+ #if UNITY_EDITOR
+ // Inside editor: make the Game GUI behave like it does at runtime.
+ if (MONO_COMMON.setViewInfo) {
+ Vector2f screenPosition (0,0);
+
+ ScriptingInvocation invocation(MONO_COMMON.setViewInfo);
+ invocation.AddStruct(&screenPosition);
+ invocation.Invoke();
+ }
+ #endif
+
+ while (!m_Events.empty())
+ {
+ DoGUIEvent(m_Events.front(), true);
+ m_Events.pop_front();
+ }
+}
+
+bool GUIManager::GetDidGUIWindowsEatLastEvent () {
+ return GetGUIManager().m_DidGUIWindowsEatLastEvent;
+}
+
+void GUIManager::SetDidGUIWindowsEatLastEvent (bool value) {
+ GetGUIManager().m_DidGUIWindowsEatLastEvent = value;
+}
+
+struct OldSortScript : std::binary_function<GUIManager::SortedScript&, GUIManager::SortedScript&, std::size_t>
+{
+ bool operator () (GUIManager::SortedScript& lhs, GUIManager::SortedScript& rhs) const { return lhs.depth < rhs.depth; }
+};
+
+struct NewSortScript : std::binary_function<GUIManager::SortedScript&, GUIManager::SortedScript&, std::size_t>
+{
+ bool operator () (GUIManager::SortedScript& lhs, GUIManager::SortedScript& rhs) const { return lhs.depth > rhs.depth; }
+};
+
+
+
+static bool MonoBehaviourDoGUI(void* beh, MonoBehaviour::GUILayoutType layoutType, int skin)
+{
+ Assert( NULL != beh );
+ return reinterpret_cast<MonoBehaviour *>(beh)->DoGUI(layoutType, skin);
+}
+
+static ObjectGUIState& MonoBehaviourGetObjectGUIState(void* beh)
+{
+ Assert( NULL != beh );
+ return reinterpret_cast<MonoBehaviour *>(beh)->GetObjectGUIState();
+}
+
+GUIManager::GUIObjectWrapper::GUIObjectWrapper(MonoBehaviour* beh)
+ : wrapped_ptr(beh)
+ , do_gui_func(&MonoBehaviourDoGUI)
+ , get_gui_state_func(&MonoBehaviourGetObjectGUIState)
+{}
+
+#if UNITY_HAS_DEVELOPER_CONSOLE
+static bool DeveloperConsoleDoGUI(void* dev, MonoBehaviour::GUILayoutType layoutType, int skin)
+{
+ Assert( NULL != dev );
+ return reinterpret_cast<DeveloperConsole *>(dev)->DoGUI();
+}
+
+static ObjectGUIState& DeveloperConsoleGetObjectGUIState(void* dev)
+{
+ Assert( NULL != dev );
+ return reinterpret_cast<DeveloperConsole *>(dev)->GetObjectGUIState();
+}
+
+GUIManager::GUIObjectWrapper::GUIObjectWrapper(DeveloperConsole* dev)
+ : wrapped_ptr(dev)
+ , do_gui_func(&DeveloperConsoleDoGUI)
+ , get_gui_state_func(&DeveloperConsoleGetObjectGUIState)
+{}
+#endif // UNITY_HAS_DEVELOPER_CONSOLE
+
+
+void GUIManager::DoGUIEvent (InputEvent &eventToSend, bool frontToBack)
+{
+ #if ENABLE_PROFILER
+ ProfilerInformation* information = &gGUIEventProfile;
+ if (eventToSend.type == InputEvent::kRepaint)
+ information = &gGUIRepaintProfile;
+
+ PROFILER_AUTO(*information, NULL)
+ #endif
+
+ GUIState &state = GetGUIState();
+ state.SetEvent (eventToSend);
+ InputEvent &e = *state.m_CurrentEvent;
+
+ m_MasterState.LoadIntoGUIState (state);
+ state.BeginFrame ();
+ MonoBehaviour* authorizationDialog = GetUserAuthorizationManager().GetAuthorizationDialog();
+
+ // Update the lists of which scripts we _actually_ want to execute.
+#if UNITY_HAS_DEVELOPER_CONSOLE
+ if (m_GUIScripts.empty() && authorizationDialog == NULL && !GetDeveloperConsole().IsVisible())
+#else
+ if (m_GUIScripts.empty() && authorizationDialog == NULL)
+#endif // UNITY_HAS_DEVELOPER_CONSOLE
+ {
+ m_MouseUsed = false;
+ return;
+ }
+
+ // Move the event mouse position away if the screen is locked. We don't want the cursor to interact
+ // with the GUI when it is in an arbitrary, fixed position.
+ if (GetScreenManager().GetLockCursor()
+#if UNITY_METRO
+ && e.touchType == InputEvent::kMouseTouch
+#endif
+ )
+ {
+#if ENABLE_NEW_EVENT_SYSTEM
+ e.touch.pos = Vector2f (-10000, -10000);
+#else
+ e.mousePosition = Vector2f (-10000, -10000);
+#endif
+ }
+
+ // ok - first we send them the layout event and find out the layering
+ InputEvent::Type originalType = e.type;
+
+ int handleTab = 0;
+ if (e.type == InputEvent::kKeyDown && (e.character == '\t' || e.character == 25))
+ handleTab = ((e.modifiers & InputEvent::kShift) == 0) ? 1 : -1;
+
+ // Execute all the no-layout scripts
+ bool eventUsed = false;
+ std::vector<GUIObjectWrapper> layoutedScripts;
+ if (authorizationDialog)
+ {
+ layoutedScripts.push_back (GUIObjectWrapper(authorizationDialog));
+ }
+ else
+ {
+ layoutedScripts.reserve(m_GUIScripts.size_slow());
+ SafeIterator<MonoBehaviourList> guiScriptIterator (m_GUIScripts);
+ while (guiScriptIterator.Next())
+ {
+ MonoBehaviour& beh = **guiScriptIterator;
+
+ if (beh.GetUseGUILayout())
+ layoutedScripts.push_back(GUIObjectWrapper(&beh));
+ else
+ {
+ if (!eventUsed)
+ eventUsed |= beh.DoGUI (MonoBehaviour::kNoLayout, 0);
+ }
+ }
+ }
+ // TODO post 3.5: We can't bail here - nolayout scripts need tab handling as well
+#if UNITY_HAS_DEVELOPER_CONSOLE
+ layoutedScripts.push_back(GUIObjectWrapper(&GetDeveloperConsole()));
+#endif // UNITY_HAS_DEVELOPER_CONSOLE
+
+ int current = 1;
+ bool hasModalWindow = state.m_MultiFrameGUIState.m_Windows != NULL && state.m_MultiFrameGUIState.m_Windows->m_ModalWindow != NULL;
+ m_SortedScripts.clear ();
+ if (!layoutedScripts.empty ())
+ {
+ // Send the layout event to all scripts that have that.
+ e.type = InputEvent::kLayout;
+
+ IMGUI::BeginWindows (state, true, !hasModalWindow); // We need to enable clipping as otherwise it hasn't been set up.
+
+ for (std::vector<GUIObjectWrapper>::iterator i = layoutedScripts.begin (); i != layoutedScripts.end (); ++i)
+ {
+ GUIObjectWrapper beh = *i;
+ if (beh)
+ {
+ beh.DoGUI (MonoBehaviour::kGameLayout, 0);
+ m_SortedScripts.push_back (SortedScript (GetGUIState().m_OnGUIState.m_Depth, beh));
+ current++;
+ }
+ }
+ // Remove any unused windows
+ state.m_CanvasGUIState.m_GUIClipState.BeginOnGUI (*state.m_CurrentEvent);
+ IMGUI::EndWindows (state, !hasModalWindow);
+ state.m_CanvasGUIState.m_GUIClipState.EndOnGUI (*state.m_CurrentEvent);
+
+ OldSortScript sort;
+ // Next, we sort by depth
+ m_SortedScripts.sort (sort);
+ e.type = originalType;
+ }
+
+ bool hasSentEndWindows = false;
+
+ current = 1; // reset the count so we can send the DoWindows.
+ if (frontToBack)
+ {
+ // This gets cleared during REPAINT event. For normal event processing, we'll just re-read that.
+ state.m_CanvasGUIState.m_IsMouseUsed = m_MouseUsed;
+
+ for (SortedScripts::iterator i = m_SortedScripts.begin(); i != m_SortedScripts.end(); i++)
+ {
+ // If this script used the event, we terminate the loop
+ if (eventUsed)
+ break;
+
+ // If this is the first script in layer 0, Put BeginWindows / EndWindows around the OnGUI call
+ bool endWindows = false;
+ if ((hasModalWindow || (current == m_SortedScripts.size() || i->depth > 0)) && !hasSentEndWindows)
+ {
+ IMGUI::BeginWindows (state, true, !hasModalWindow);
+ if (eventUsed)
+ break;
+ hasSentEndWindows = true;
+ endWindows = true;
+ }
+ current++;
+ eventUsed = i->beh.DoGUI (MonoBehaviour::kGameLayout, 0);
+ if (endWindows)
+ IMGUI::EndWindows (state, !hasModalWindow);
+ }
+
+ // Remove keyboard focus when clicking on empty GUI area
+ // (so text fields don't take away game view input).
+ if (originalType == InputEvent::kMouseDown && !eventUsed)
+ {
+ GUIState &state = GetGUIState();
+ state.m_MultiFrameGUIState.m_KeyboardControl = 0;
+ }
+
+ // Handle mouse focus: We want the new GUI system to eat any mouse events. This means that we need to check this during repaint or mouseDown/Up
+ // and set a global variable accordingly.
+ if (originalType == InputEvent::kMouseDown || originalType == InputEvent::kMouseUp)
+ state.m_CanvasGUIState.m_IsMouseUsed = (bool)state.m_CanvasGUIState.m_IsMouseUsed | eventUsed;
+ }
+ else
+ {
+ // We clear mouse used at repaint time, then let it run through any event processing for next frame, before reading it back
+ m_MouseUsed = state.m_CanvasGUIState.m_IsMouseUsed = false;
+
+ // I'm iterating backwards here - used by repainting
+ // this time, users can't bail out of the event processing
+ IMGUI::BeginWindows (state, true, !hasModalWindow);
+ for (SortedScripts::iterator i = m_SortedScripts.end(); i != m_SortedScripts.begin();)
+ {
+ i--;
+ bool endWindows = false;
+
+
+ // If this window is the last, OR the next one is above 0 depth, we should do repaint all popup windows NOW
+ if (!hasSentEndWindows) // If we haven't already sent it
+ {
+ if (current == m_SortedScripts.size()) // If this is the last script, we must call it now
+ {
+ hasSentEndWindows = true;
+ endWindows = true;
+ }
+ else
+ {
+ // If this is the last script with a depth > 0, we must call it now.
+ SortedScripts::iterator j = i;
+ j--;
+ if (j->depth <= 0)
+ {
+ hasSentEndWindows = true;
+ endWindows = true;
+ }
+ }
+ }
+ i->beh.DoGUI (MonoBehaviour::kGameLayout, 0);
+ if (endWindows)
+ {
+ state.m_CanvasGUIState.m_GUIClipState.BeginOnGUI (*state.m_CurrentEvent);
+ IMGUI::EndWindows (state, !hasModalWindow); // don't ignore modal windows if we have them
+ state.m_CanvasGUIState.m_GUIClipState.EndOnGUI (*state.m_CurrentEvent);
+ }
+
+ current++;
+ }
+
+ if(hasModalWindow)
+ {
+ // Ensure modal windows are always on top by painting them last.
+ state.m_CanvasGUIState.m_GUIClipState.BeginOnGUI (*state.m_CurrentEvent);
+ IMGUI::RepaintModalWindow (state);
+ state.m_CanvasGUIState.m_GUIClipState.EndOnGUI (*state.m_CurrentEvent);
+ }
+ }
+
+ if (handleTab != 0 && !eventUsed && m_SortedScripts.size() != 0)
+ {
+ // Build the list of IDLists to cycle through
+ std::vector<IDList*> keyIDLists;
+
+ IMGUI::GUIWindow* focusedWindow = IMGUI::GetFocusedWindow (state);
+ if (focusedWindow)
+ keyIDLists.push_back (&focusedWindow->m_ObjectGUIState.m_IDList);
+ else
+ {
+ keyIDLists.reserve (m_SortedScripts.size());
+ for (SortedScripts::iterator i = m_SortedScripts.begin(); i != m_SortedScripts.end(); i++)
+ {
+ keyIDLists.push_back (&i->beh.GetObjectGUIState().m_IDList);
+ }
+ }
+
+ state.CycleKeyboardFocus(keyIDLists, handleTab == 1);
+ }
+
+ m_MasterState.SaveFromGUIState (state);
+ m_MasterState.EndFrame ();
+ m_MouseUsed = state.m_CanvasGUIState.m_IsMouseUsed;
+}
+
+void GUIManager::QueueEvent (InputEvent &ie)
+{
+ QueueEventImmediate(ie);
+}
+
+
+void GUIManager::QueueEventImmediate (InputEvent &ie)
+{
+ // MouseMove events are not sent.
+ // The same info can be obtained from repaint events.
+ if (ie.type == InputEvent::kMouseMove)
+ {
+ // We still use them as last event to update the cursor position.
+ m_LastEvent = ie;
+ return;
+ }
+ if (ie.type == InputEvent::kIgnore)
+ return;
+
+ if ( ie.type == InputEvent::kMouseDown )
+ m_mouseButtonsDown |= (1<<ie.button);
+ else if ( ie.type == InputEvent::kMouseUp )
+ m_mouseButtonsDown &= ~(1<<ie.button);
+
+ switch (ie.type) {
+ case InputEvent::kMouseDown:
+ case InputEvent::kMouseUp:
+ case InputEvent::kKeyDown:
+ ResetCursorFlash ();
+ break;
+ }
+
+ m_LastEvent = ie;
+ m_Events.push_back(ie);
+}
+
+void GUIManager::ResetCursorFlash ()
+{
+ #if SUPPORT_REPRODUCE_LOG
+ if (RunningReproduction())
+ return;
+ #endif
+
+ GetGUIManager().m_LastInputEventTime = GetTimeManager().GetRealtime ();
+}
+
+float GUIManager::GetCursorFlashTime ()
+{
+ return GetGUIManager().m_LastInputEventTime;
+}
+
+
+GUIKeyboardState &GUIManager::GetMasterGUIState ()
+{
+ return GetGUIManager().m_MasterState;
+}
+
+#if SUPPORT_REPRODUCE_LOG
+void WriteInputEvent (InputEvent& event, std::ostream& out)
+{
+ out << (int&)event.type << ' ';
+ WriteFloat(out, event.mousePosition.x); out << ' ';
+ WriteFloat(out, event.mousePosition.y); out << ' ';
+ WriteFloat(out, event.delta.x); out << ' ';
+ WriteFloat(out, event.delta.y); out << ' ';
+
+ out << event.button << ' ' << event.modifiers << ' ';
+ WriteFloat(out, event.pressure); out << ' ';
+ out << event.clickCount << ' ' << event.character << ' ' << event.keycode << ' ';
+
+ // if (event.commandString)
+ // WriteReproductionString(out, event.commandString);
+ // else
+ // WriteReproductionString(out, "");
+}
+
+void ReadInputEvent (InputEvent& event, std::istream& in, int version)
+{
+ event.Init();
+
+ in >> (int&)event.type;
+ ReadFloat(in, event.mousePosition.x);
+ ReadFloat(in, event.mousePosition.y);
+ ReadFloat(in, event.delta.x);
+ ReadFloat(in, event.delta.y);
+ in >> event.button >> event.modifiers;
+ ReadFloat(in, event.pressure);
+ in >> event.clickCount >> event.character >> event.keycode;
+
+ //if (version >= 6)
+ //{
+ // std::string commandString;
+ // ReadReproductionString(in, commandString);
+ // event.commandString = new char[commandString.size() + 1];
+ // memcpy(event.commandString, commandString.c_str(), commandString.size() + 1);
+ //}
+}
+
+void GUIManager::WriteLog (std::ofstream& out)
+{
+ out << "Events" << std::endl;
+
+ out << m_Events.size() << std::endl;
+
+ for (int i=0;i<m_Events.size();i++)
+ {
+ InputEvent& event = m_Events[i];
+ WriteInputEvent(event, out);
+ }
+
+ // Hover events seem to require the last event as well!
+ InputEvent& event = m_LastEvent;
+ WriteInputEvent (event, out);
+
+ if (GetReproduceVersion () >= 6)
+ {
+ WriteReproductionString(out, GetInputManager().GetCompositionString());
+ out << (int)(GetInputManager().GetTextFieldInput()) << ' ';
+ }
+
+ out << std::endl;
+}
+
+void GUIManager::ReadLog (std::ifstream& in)
+{
+ CheckReproduceTagAndExit("Events", in);
+
+ int size;
+ in >> size;
+ m_Events.clear();
+ for (int i=0;i<size;i++)
+ {
+ InputEvent event;
+ ReadInputEvent (event, in, GetReproduceVersion());
+ m_Events.push_back(event);
+ }
+
+ // Hover events seem to require the last event as well!
+ ReadInputEvent (m_LastEvent, in, GetReproduceVersion());
+
+ if (GetReproduceVersion () >= 6)
+ {
+ ReadReproductionString(in, GetInputManager().GetCompositionString());
+ int textFieldInput;
+ in >> textFieldInput;
+ GetInputManager().SetTextFieldInput(textFieldInput);
+ }
+}
+#endif
+#endif
diff --git a/Runtime/IMGUI/GUIManager.h b/Runtime/IMGUI/GUIManager.h
new file mode 100644
index 0000000..eb35c45
--- /dev/null
+++ b/Runtime/IMGUI/GUIManager.h
@@ -0,0 +1,135 @@
+#ifndef GUIMANAGER_H
+#define GUIMANAGER_H
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Misc/InputEvent.h"
+#include "Runtime/Utilities/MemoryPool.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/Misc/DeveloperConsole.h"
+
+#include <deque>
+
+struct InputEvent;
+class GUIManager {
+ public:
+ GUIManager ();
+
+ // Issue a repaint event - used by players...
+ // Mouse position & modifiers are read from the input manager.
+ // TODO: implement Modifier keys
+ void Repaint ();
+
+ // Add/remove - called from MonoBehaviour
+ void AddGUIScript (MonoBehaviourListNode& beh);
+
+ // Send an event to all in-game GUI scripts
+ // e - the event to send.
+ // mask - GOLayer mask to match.
+ // frontToBack Send event to the frontmost GUI script first (true for normal event processing, false for repaint)
+ void DoGUIEvent (InputEvent &e, bool frontToBack);
+
+ // Send an input event (keydown, mousemove, mouseup, etc) to the GUI scripts.
+ void QueueEvent (InputEvent &ie);
+ void QueueEventImmediate (InputEvent &ie);
+
+ void SendQueuedEvents ();
+
+ InputEvent GetLastInputEvent() { return m_LastEvent;}
+
+ static bool AnyMouseButtonsDown();
+
+ // Did GUI code inside BeginWindows make it irrelevant for OnGUI code to be called at all? (e.g. mouseClick inside a GUI.window)
+ // We used to do this by calling event.Use (), but that causes repaints, so instead this function is called that sets a var to track it
+ static void SetDidGUIWindowsEatLastEvent (bool value);
+ static bool GetDidGUIWindowsEatLastEvent ();
+
+ #if UNITY_EDITOR
+ // Clear all setting for entering / exiting playmode
+ void Reset ();
+ #endif
+
+ static void ResetCursorFlash ();
+ static float GetCursorFlashTime ();
+
+ #if UNITY_EDITOR
+ void SetEditorGUIInfo (Vector2f guiPixelOffset);
+ Vector2f GetGUIPixelOffset () { return m_GUIPixelOffset; }
+ Vector2f GetEditorGUIInfo() const;
+ #endif
+
+ #if SUPPORT_REPRODUCE_LOG
+ void WriteLog (std::ofstream& out);
+ void ReadLog (std::ifstream& in);
+ #endif
+
+ struct GUIObjectWrapper
+ {
+ public:
+ explicit GUIObjectWrapper(MonoBehaviour* beh);
+ #if UNITY_HAS_DEVELOPER_CONSOLE
+
+ explicit GUIObjectWrapper(DeveloperConsole* dev);
+
+ #endif // UNITY_HAS_DEVELOPER_CONSOLE
+
+ // Wrapped functions for GUI objects
+ bool DoGUI(MonoBehaviour::GUILayoutType layoutType, int skin) const {
+ return do_gui_func(wrapped_ptr, layoutType, skin);
+ }
+
+ ObjectGUIState& GetObjectGUIState() const {
+ return get_gui_state_func(wrapped_ptr);
+ }
+
+ // This avoids a plethora of pitfalls in situations,
+ // where implicit conversions may happen
+ typedef void * GUIObjectWrapper::*unspecified_bool_type;
+ operator unspecified_bool_type() const { // never throws
+ return wrapped_ptr == 0? 0: &GUIObjectWrapper::wrapped_ptr;
+ }
+
+ private:
+ typedef bool (*dogui_function_type)(void*, MonoBehaviour::GUILayoutType, int);
+ typedef ObjectGUIState& (*get_gui_state_function_type)(void*);
+
+ void* wrapped_ptr;
+
+ dogui_function_type do_gui_func;
+ get_gui_state_function_type get_gui_state_func;
+ };
+
+ struct SortedScript
+ {
+ int depth;
+ GUIObjectWrapper beh;
+ SortedScript (int dep, GUIObjectWrapper b) : depth(dep), beh(b) {}
+ };
+
+ inline std::deque<InputEvent>& GetQueuedEvents() {return m_Events;}
+ bool GetMouseUsed () const { return m_MouseUsed; }
+
+ static GUIKeyboardState &GetMasterGUIState ();
+
+private:
+ typedef List<MonoBehaviourListNode> MonoBehaviourList;
+ MonoBehaviourList m_GUIScripts;
+ std::deque<InputEvent> m_Events;
+
+ bool m_MouseUsed, m_DidGUIWindowsEatLastEvent;
+ int m_mouseButtonsDown;
+ Vector2f m_GUIPixelOffset;
+ typedef std::list<SortedScript, memory_pool<SortedScript> > SortedScripts;
+ SortedScripts m_SortedScripts;
+
+ float m_LastInputEventTime;
+ InputEvent m_LastEvent;
+ GUIKeyboardState m_MasterState;
+};
+
+GUIManager &GetGUIManager ();
+
+void InitGUIManager ();
+void CleanupGUIManager ();
+
+#endif
diff --git a/Runtime/IMGUI/GUIState.cpp b/Runtime/IMGUI/GUIState.cpp
new file mode 100644
index 0000000..90db27a
--- /dev/null
+++ b/Runtime/IMGUI/GUIState.cpp
@@ -0,0 +1,519 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNITYGUI
+
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Misc/InputEvent.h"
+#include "Runtime/IMGUI/IDList.h"
+#include "Runtime/IMGUI/NamedKeyControlList.h"
+#include "Runtime/IMGUI/GUIWindows.h"
+#include "Runtime/IMGUI/TextUtil.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/CommonScriptingClasses.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/Backend/ScriptingArguments.h"
+
+static EternalGUIState* gEternalGUIState = NULL;
+static IDList* gEternalIDList = NULL;
+static GUIState *gGUIState = NULL;
+
+GUIState &GetGUIState ()
+{
+ AssertIf (!gGUIState);
+ return *gGUIState;
+}
+
+
+
+EternalGUIState *GetEternalGUIState ()
+{
+ if (gEternalGUIState == NULL)
+ gEternalGUIState = new EternalGUIState ();
+ return gEternalGUIState;
+}
+
+static IDList* GetEternalIDList ()
+{
+ if (gEternalIDList == NULL)
+ gEternalIDList = new IDList();
+ return gEternalIDList;
+}
+
+
+MultiFrameGUIState::MultiFrameGUIState ()
+{
+ m_KeyboardControl = 0;
+ m_NamedKeyControlList = NULL;
+ m_Windows = NULL;
+}
+
+MultiFrameGUIState::~MultiFrameGUIState ()
+{
+ delete m_NamedKeyControlList;
+ delete m_Windows;
+}
+
+void MultiFrameGUIState::Reset ()
+{
+ delete m_Windows;
+ m_Windows = 0;
+
+ delete m_NamedKeyControlList;
+ m_NamedKeyControlList = 0;
+}
+
+IMGUI::NamedControl *MultiFrameGUIState::GetControlNamed (const std::string &name)
+{
+ if (m_NamedKeyControlList == NULL)
+ return NULL;
+ return m_NamedKeyControlList->GetControlNamed (name);
+}
+
+void MultiFrameGUIState::AddNamedControl (std::string &name, int id, IMGUI::GUIWindow* window)
+{
+ if (m_NamedKeyControlList == NULL)
+ m_NamedKeyControlList = new IMGUI::NamedKeyControlList ();
+ m_NamedKeyControlList->AddNamedControl(name, id, window ? window->m_ID : -1);
+}
+
+void MultiFrameGUIState::ClearNamedControls ()
+{
+ if (m_NamedKeyControlList)
+ m_NamedKeyControlList->Clear ();
+}
+
+
+ObjectGUIState::ObjectGUIState ()
+{
+}
+
+ObjectGUIState::~ObjectGUIState ()
+{
+}
+
+void ObjectGUIState::BeginOnGUI ()
+{
+ m_IDList.BeginOnGUI ();
+}
+
+void OnGUIState::SetNameOfNextKeyboardControl (const std::string &nextName)
+{
+ delete m_NameOfNextKeyboardControl;
+ m_NameOfNextKeyboardControl = new string (nextName);
+}
+
+void OnGUIState::ClearNameOfNextKeyboardControl ()
+{
+ delete m_NameOfNextKeyboardControl;
+ m_NameOfNextKeyboardControl = NULL;
+}
+
+OnGUIState::OnGUIState ()
+{
+ m_NameOfNextKeyboardControl = NULL;
+ m_MouseTooltip = m_KeyTooltip = NULL;
+ m_CaptureBlock = NULL;
+ m_Color = m_BackgroundColor = m_ContentColor = ColorRGBAf (1.0f,1.0f,1.0f,1.0f);
+ m_Enabled = 1;
+ m_Changed = 0;
+ m_Depth = 0;
+ m_ShowKeyboardControl = 1;
+
+}
+
+OnGUIState::~OnGUIState ()
+{
+ delete m_NameOfNextKeyboardControl;
+ delete m_MouseTooltip;
+ delete m_KeyTooltip;
+}
+
+void OnGUIState::BeginOnGUI ()
+{
+ m_Color = m_BackgroundColor = m_ContentColor = ColorRGBAf(1.0f,1.0f,1.0f,1.0f);
+ m_Enabled = true;
+ m_Changed = false;
+ m_Depth = 1;
+}
+
+void OnGUIState::EndOnGUI ()
+{
+ delete m_NameOfNextKeyboardControl;
+ m_NameOfNextKeyboardControl = NULL;
+ delete m_MouseTooltip;
+ m_MouseTooltip = NULL;
+ delete m_KeyTooltip;
+ m_KeyTooltip = NULL;
+}
+
+
+void OnGUIState::SetMouseTooltip (const UTF16String &tooltip)
+{
+ delete m_MouseTooltip;
+ m_MouseTooltip = new UTF16String (tooltip);
+}
+
+void OnGUIState::SetKeyTooltip (const UTF16String &tooltip)
+{
+ delete m_KeyTooltip;
+ m_KeyTooltip = new UTF16String (tooltip);
+}
+
+GUIState::GUIState ()
+{
+ m_CurrentEvent = NULL;
+ m_ObjectGUIState = NULL;
+ m_OnGUIDepth = 0;
+}
+
+GUIState::~GUIState()
+{
+}
+
+
+void GUIState::BeginFrame ()
+{
+
+}
+
+void GUIState::EndFrame ()
+{
+
+}
+
+void GUIState::BeginOnGUI (ObjectGUIState &objectGUIState)
+{
+ m_ObjectGUIState = &objectGUIState;
+
+ m_OnGUIState.BeginOnGUI ();
+ m_ObjectGUIState->BeginOnGUI ();
+ m_OnGUIDepth++;
+}
+
+void GUIState::EndOnGUI ()
+{
+ m_OnGUIState.EndOnGUI();
+ m_ObjectGUIState = NULL;
+
+ AssertIf (m_OnGUIDepth < 1);
+ m_OnGUIDepth--;
+}
+
+static int GetControlID (GUIState& state, int hint, FocusType focusType, const Rectf& rect, bool useRect)
+{
+ int id;
+
+ if (state.m_ObjectGUIState == NULL)
+ {
+ id = state.m_EternalGUIState->GetNextUniqueID ();
+
+#if (UNITY_EDITOR)
+ // Editor GUI needs some additional things to happen when calling GetControlID.
+ // While this will also be called for runtime code running in Play mode in the Editor,
+ // it won't have any effect. EditorGUIUtility.s_LastControlID will be set to the id,
+ // but this is only used inside the handling of a single control.
+ ScriptingInvocation invocation(MONO_COMMON.handleControlID);
+ invocation.AddInt(id);
+ invocation.Invoke();
+#endif
+
+ return id;
+ }
+
+ if (useRect)
+ id = state.m_ObjectGUIState->m_IDList.GetNext (state, hint, focusType, rect);
+ else
+ id = state.m_ObjectGUIState->m_IDList.GetNext (state, hint, focusType);
+
+ // maybe in future this should check for focusType == keyboard
+ // the issue currently is that there is no way to focus buttons via the keyboard
+ // this means that if you have a game with no mouse input you can not 'tab' to the
+ // button using a joystic or keys.
+ //
+ // because of this we need to allow custom named controls of all types (apart from passive)
+ // to be selectable.
+ if (focusType != kPassive && state.m_OnGUIState.GetNameOfNextKeyboardControl () != NULL)
+ {
+ IMGUI::GUIWindow* currentWindow = NULL;
+ if (state.m_MultiFrameGUIState.m_Windows)
+ currentWindow = state.m_MultiFrameGUIState.m_Windows->m_CurrentWindow;
+ state.m_MultiFrameGUIState.AddNamedControl (*state.m_OnGUIState.GetNameOfNextKeyboardControl (), id, currentWindow);
+ state.m_OnGUIState.ClearNameOfNextKeyboardControl ();
+ }
+
+#if (UNITY_EDITOR)
+ // Same as above; see comment there.
+ ScriptingInvocation invocation(MONO_COMMON.handleControlID);
+ invocation.AddInt(id);
+ invocation.Invoke();
+#endif
+
+ return id;
+}
+
+int GUIState::GetControlID (int hint, FocusType focusType)
+{
+ return ::GetControlID (*this, hint, focusType, Rectf(), false);
+}
+
+int GUIState::GetControlID (int hint, FocusType focusType, const Rectf& rect)
+{
+ return ::GetControlID (*this, hint, focusType, rect, true);
+}
+
+int GUIState::GetIDOfNamedControl (const std::string &name)
+{
+ IMGUI::NamedControl *ctrl = m_MultiFrameGUIState.GetControlNamed (name);
+ if (ctrl)
+ return ctrl->ID;
+ return 0;
+}
+
+std::string GUIState::GetNameOfFocusedControl ()
+{
+ if (m_MultiFrameGUIState.m_NamedKeyControlList)
+ return m_MultiFrameGUIState.m_NamedKeyControlList->GetNameOfControl (m_MultiFrameGUIState.m_KeyboardControl);
+ return "";
+}
+
+void GUIState::FocusKeyboardControl (const std::string &name)
+{
+ IMGUI::NamedControl *ctrl = m_MultiFrameGUIState.GetControlNamed (name);
+ if (ctrl)
+ {
+ m_MultiFrameGUIState.m_KeyboardControl = ctrl->ID;
+ IMGUI::FocusWindow (*this, ctrl->windowID);
+ }
+ else
+ {
+ m_MultiFrameGUIState.m_KeyboardControl = 0;
+ IMGUI::FocusWindow (*this, -1);
+ }
+}
+
+void GUIState::SetEvent( const InputEvent& event )
+{
+ ScriptingInvocationNoArgs invocation;
+ invocation.method = MONO_COMMON.makeMasterEventCurrent;
+ invocation.Invoke();
+ *m_CurrentEvent = event;
+}
+
+void GUIState::Internal_SetManagedEvent (void *event)
+{
+ m_CurrentEvent = (InputEvent*)event;
+}
+
+void GUIState::MoveAllDataTo (GUIState &dest, bool saving)
+{
+ dest.m_MultiFrameGUIState = m_MultiFrameGUIState;
+ m_MultiFrameGUIState.m_NamedKeyControlList = NULL;
+ m_MultiFrameGUIState.m_Windows = NULL;
+
+ dest.m_OnGUIState = m_OnGUIState;
+ m_OnGUIState.m_CaptureBlock = NULL;
+ m_OnGUIState.m_NameOfNextKeyboardControl = NULL;
+ m_OnGUIState.m_MouseTooltip = NULL;
+ m_OnGUIState.m_KeyTooltip = NULL;
+
+ dest.m_ObjectGUIState = m_ObjectGUIState;
+
+ // Move over the clipping info.
+ dest.m_CanvasGUIState = m_CanvasGUIState;
+
+ // This one is truly eternal, so we don't want to clear it from the current.
+ dest.m_EternalGUIState = m_EternalGUIState;
+
+ // Move over the event. Since the event is shared in weird ways between Mono and C++, we'll copy it.
+ if (saving)
+ {
+ dest.m_BackupEvent = *m_CurrentEvent;
+ dest.m_CurrentEvent = m_CurrentEvent;
+ }
+ else
+ {
+
+ *(dest.m_CurrentEvent) = m_BackupEvent;
+ }
+}
+
+GUIState* GUIState::GetPushState ()
+{
+ // Set up state
+ GUIState *pushState = new GUIState ();
+ GetGUIState().MoveAllDataTo (*pushState, true);
+ return pushState;
+}
+
+void GUIState::PopAndDelete (GUIState *pushState)
+{
+ pushState->MoveAllDataTo (GetGUIState(), false);
+ delete pushState;
+}
+
+static IDList* FindWhichIDListHasKeyboardControl (std::vector<IDList *> &idListsToSearch)
+{
+ // Find which IDList has the keyboard Control.
+ for (std::vector<IDList *>::iterator i = idListsToSearch.begin(); i != idListsToSearch.end(); i++)
+ {
+ if ((*i)->HasKeyboardControl ())
+ {
+ return *i;
+ }
+ }
+ return NULL;
+}
+
+void GUIState::CycleKeyboardFocus (std::vector<IDList *> &idListsToSearch, bool searchForward)
+{
+ m_MultiFrameGUIState.m_KeyboardControl = GetNextKeyboardControlID(idListsToSearch, searchForward);
+}
+
+int GUIState::GetNextKeyboardControlID (std::vector<IDList *> &idListsToSearch, bool searchForward)
+{
+ IDList *start = FindWhichIDListHasKeyboardControl (idListsToSearch);
+
+ if (searchForward)
+ {
+ if (start && start->GetNextKeyboardControlID() != -1)
+ {
+ return start->GetNextKeyboardControlID();
+ }
+
+ // Which script did we start the keyboard search FROM.
+ int startIdx = -1;
+ // Which script are we scanning for keyboard controls.
+ int listIdx = -1;
+ if (start != NULL)
+ {
+ for (int i = 0; i < idListsToSearch.size(); i++)
+ {
+ if (idListsToSearch[i] == start)
+ {
+ startIdx = listIdx = (i + 1) % idListsToSearch.size();
+ break;
+ }
+ }
+ } else {
+ startIdx = 0;//idListsToSearch.size();
+ listIdx = 0;
+ }
+
+ do {
+ int firstInList = idListsToSearch[listIdx]->GetFirstKeyboardControlID ();
+ if (firstInList != -1)
+ {
+ return firstInList;
+ }
+ listIdx++;
+ listIdx = listIdx % idListsToSearch.size();
+ } while (listIdx != startIdx);
+ return 0;
+ }
+ else
+ {
+ if (start && start->GetPreviousKeyboardControlID() != -1)
+ {
+ return start->GetPreviousKeyboardControlID();
+ }
+
+ int startIdx = 0, listIdx = idListsToSearch.size();
+ if (start != NULL)
+ {
+ for (int i = 0; i < idListsToSearch.size(); i++)
+ {
+ if (idListsToSearch[i] == start)
+ {
+ startIdx = listIdx = i;
+ break;
+ }
+ }
+ }
+
+ do {
+ listIdx = listIdx - 1;
+ if (listIdx == -1)
+ listIdx = idListsToSearch.size() - 1;
+
+ int firstInList = idListsToSearch[listIdx]->GetLastKeyboardControlID ();
+ if (firstInList != -1)
+ {
+ return firstInList;
+ }
+ } while (listIdx != startIdx);
+ return 0;
+ }
+}
+
+GUIKeyboardState::GUIKeyboardState ()
+{
+ m_KeyboardControl = 0;
+ m_FocusedGUIWindow = -1;
+ m_ShowKeyboardControl = true;
+ m_Windows = NULL;
+ m_NamedKeyControlList = NULL;
+}
+
+GUIKeyboardState::~GUIKeyboardState ()
+{
+ delete m_Windows;
+ delete m_NamedKeyControlList;
+}
+
+void GUIKeyboardState::LoadIntoGUIState (GUIState &dest)
+{
+ dest.m_MultiFrameGUIState.m_KeyboardControl = m_KeyboardControl;
+
+ Assert (!dest.m_MultiFrameGUIState.m_NamedKeyControlList);
+ dest.m_MultiFrameGUIState.m_NamedKeyControlList = m_NamedKeyControlList;
+
+ Assert (!dest.m_MultiFrameGUIState.m_Windows);
+ dest.m_MultiFrameGUIState.m_Windows = m_Windows;
+
+ dest.m_OnGUIState.m_ShowKeyboardControl = m_ShowKeyboardControl;
+ m_Windows = NULL;
+}
+void GUIKeyboardState::SaveFromGUIState (GUIState &src)
+{
+ m_KeyboardControl = src.m_MultiFrameGUIState.m_KeyboardControl;
+ m_NamedKeyControlList = src.m_MultiFrameGUIState.m_NamedKeyControlList;
+ src.m_MultiFrameGUIState.m_NamedKeyControlList = NULL;
+ m_Windows = src.m_MultiFrameGUIState.m_Windows;
+ m_ShowKeyboardControl = src.m_OnGUIState.m_ShowKeyboardControl;
+ src.m_MultiFrameGUIState.m_Windows = NULL;
+}
+
+void GUIKeyboardState::Reset ()
+{
+ m_KeyboardControl = m_FocusedGUIWindow = 0;
+ delete m_Windows;
+ m_Windows = NULL;
+ delete m_NamedKeyControlList;
+ m_NamedKeyControlList = NULL;
+}
+
+void GUIKeyboardState::EndFrame ()
+{
+ if (m_Windows)
+ m_Windows->ReleaseScriptingObjects ();
+}
+
+void InitGUIState ()
+{
+ Assert(gGUIState == NULL);
+ gGUIState = new GUIState ();
+ gGUIState->m_EternalGUIState = GetEternalGUIState();
+ gGUIState->m_CurrentEvent = new InputEvent ();
+ gGUIState->m_CurrentEvent->Init();
+}
+
+
+void CleanupGUIState ()
+{
+ Assert(gGUIState != NULL);
+ delete gGUIState;
+ gGUIState = NULL;
+}
+
+#endif
diff --git a/Runtime/IMGUI/GUIState.h b/Runtime/IMGUI/GUIState.h
new file mode 100644
index 0000000..9ab0ad6
--- /dev/null
+++ b/Runtime/IMGUI/GUIState.h
@@ -0,0 +1,231 @@
+#ifndef GUISTATE_H
+#define GUISTATE_H
+
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/IMGUI/IDList.h"
+#include "Runtime/IMGUI/NamedKeyControlList.h"
+#include "Runtime/IMGUI/GUIClip.h"
+
+namespace IMGUI
+{
+ struct GUIWindowState;
+ struct GUIWindow;
+}
+
+
+struct InputEvent;
+struct MonoObject;
+struct GUIGraphicsCacheBlock;
+struct UTF16String;
+
+// Lasts multiple frames.
+// There is ONE for user's games and one for each Editor Window.
+struct MultiFrameGUIState
+{
+ // Which control has keyboard focus
+ int m_KeyboardControl;
+
+ // List of named keyboard controls. NULL if none
+ IMGUI::NamedKeyControlList *m_NamedKeyControlList;
+
+ // List of GUI.Window IDLists
+ IMGUI::GUIWindowState *m_Windows;
+
+
+ IMGUI::NamedControl *GetControlNamed (const std::string &name);
+ void AddNamedControl (std::string &name, int id, IMGUI::GUIWindow* window);
+ void ClearNamedControls ();
+ void Reset ();
+
+ MultiFrameGUIState ();
+ ~MultiFrameGUIState ();
+};
+
+// State that is reset each OnGUI.
+struct OnGUIState
+{
+ OnGUIState ();
+ ~OnGUIState ();
+
+ // Colors for rendering
+ ColorRGBAf m_Color, m_BackgroundColor, m_ContentColor;
+
+ // Is the GUI enabled
+ int m_Enabled;
+
+ // Has the GUI changed
+ int m_Changed;
+
+ // Depth of the current GUIBehaviour's OnGUI - not used by GUI.Window or EditorWindow
+ int m_Depth;
+
+ int m_ShowKeyboardControl;
+
+ // If IMGUI is rendered inside NewGUI, this is a pointer to the batching object. All rendering commands go into this.
+ std::vector<GUIGraphicsCacheBlock>* m_CaptureBlock;
+
+ // Name of the next keyboard control. It's a pointer to maintain size with Mono, but owned by OnGUIState
+ // Don't set this pointer (unless you're implementing nested OnGUI calls) - use the getters and setters
+ std::string *m_NameOfNextKeyboardControl;
+
+ UTF16String *m_MouseTooltip, *m_KeyTooltip;
+
+ void SetNameOfNextKeyboardControl (const std::string &name);
+ std::string *GetNameOfNextKeyboardControl () { return m_NameOfNextKeyboardControl; }
+
+ void SetMouseTooltip (const UTF16String &tooltip);
+ UTF16String *GetMouseTooltip () { return m_MouseTooltip; }
+
+ void SetKeyTooltip (const UTF16String &tooltip);
+ UTF16String *GetKeyTooltip () { return m_KeyTooltip; }
+
+ void ClearNameOfNextKeyboardControl ();
+ // Reset the values to sane defaults for an OnGUI call
+ void BeginOnGUI ();
+ void EndOnGUI ();
+};
+
+// State that is stored in the MonoBehaviour and pulled from it every OnGUI.
+struct ObjectGUIState
+{
+ IDList m_IDList; // per monobehaviour + 1 per GUI.Window
+
+ void BeginOnGUI ();
+
+ ObjectGUIState ();
+ ~ObjectGUIState ();
+};
+
+// State that exists per new-style canvas, and per old-style MonoBehaviour with an OnGUI.
+struct CanvasGUIState
+{
+ GUIClipState m_GUIClipState;
+ int m_IsMouseUsed;
+};
+
+/// This one is always available.
+struct EternalGUIState
+{
+private:
+ int m_UniqueID;
+ // Which control has touch / mouse focus
+public:
+ int m_HotControl; // TODO:should be int[kMaxSupportedPointers]
+ bool m_AllowHover;
+
+ int GetNextUniqueID ()
+ {
+ return m_UniqueID++;
+ }
+
+ EternalGUIState ()
+ {
+ m_UniqueID = 1;
+ m_HotControl = 0;
+ m_AllowHover = true;
+ }
+};
+EternalGUIState *GetEternalGUIState ();
+
+// All state used by the GUI (so be it!)
+struct GUIState
+{
+ GUIState ();
+ ~GUIState();
+
+ MultiFrameGUIState m_MultiFrameGUIState;
+ OnGUIState m_OnGUIState;
+ ObjectGUIState* m_ObjectGUIState; // The state owned by the monobehaviour we're calling on
+ CanvasGUIState m_CanvasGUIState; // The state for the surface we're rendering into
+ EternalGUIState* m_EternalGUIState;
+ InputEvent* m_CurrentEvent; // C++ side. Maps to the memory INSIDE the ManagedCurrentEvent.
+
+ InputEvent m_BackupEvent;
+
+ int m_OnGUIDepth; // How deep we are inside OnGUI calls. 0 = we haven't called at all
+
+ // Whether the current event has been marked as used
+#if ENABLE_NEW_EVENT_SYSTEM
+ bool GetIsEventUsed() const { return (m_CurrentEvent != NULL) && m_CurrentEvent->type == InputEvent::kUsed; }
+
+ // Begin and End simply make it possible to use Event.current from C# outside of OnGUI code
+ void BeginUsingEvents () { ++m_OnGUIDepth; }
+ void EndUsingEvents() { --m_OnGUIDepth; }
+#endif
+
+ // Call this to intialize a game frame or window frame - not for each event we send into the system.
+ void BeginFrame ();
+
+ // Call this when we're done with one OS-level frame.
+ void EndFrame ();
+
+ // Call this to intialize ONE onGUI call. Must be called between e.g. layout and repaint events
+ // Assumes all states have been assigned correctly.
+ void BeginOnGUI (ObjectGUIState &objectGUIState);
+
+ /// Call this to end an OnGUI call. It will call all substates with EndOnGUI, so they can clean up.
+ void EndOnGUI ();
+
+
+ int GetControlID (int hint, FocusType focusType, const Rectf &rect);
+ int GetControlID (int hint, FocusType focusType);
+
+ // Handle Named Controls
+ void SetNameOfNextKeyboardControl (const std::string &name) { m_OnGUIState.SetNameOfNextKeyboardControl (name); }
+ std::string GetNameOfFocusedControl ();
+ int GetIDOfNamedControl (const std::string &name);
+ void FocusKeyboardControl (const std::string &name);
+
+ //Not sure where this should go. It's called by GUIManager & EditorWindows. For now, I'll put it here...
+ void CycleKeyboardFocus (std::vector<IDList *> &IDListsToSearch, bool searchForward);
+ int GetNextKeyboardControlID (std::vector<IDList *> &IDListsToSearch, bool searchForward);
+
+ void SetEvent (const InputEvent& event);
+ void SetObjectGUIState (ObjectGUIState &objectGUIState);
+
+ // Make a copy of the current managed gui state (to implement nesting)
+ // When done with an OnGUI call, call PopAndDelete to restore.
+ static GUIState* GetPushState ();
+ static void PopAndDelete (GUIState *pushState);
+
+ // Move all GUI state into dest, NULLing all pointer fields in this
+ // Used by GetPushState & PopAndDelete
+ void MoveAllDataTo (GUIState &dest, bool saving);
+
+ // Called from C# whenever the user assigns into Event.current.
+ // Don't call this from C++;
+ void Internal_SetManagedEvent (void *event);
+};
+
+GUIState &GetGUIState ();
+
+// Struct for saving GUI State between in-game frames. Each editor window is also a separate "world" in this regard.
+// So E.g. Keyboard control names are shared between all scripts in a game, but each editor window has it's own separate world.
+struct GUIKeyboardState
+{
+ // Which controlID has focus last frame
+ int m_KeyboardControl;
+ // Should we show the keyboard control? false if editor window (or webplayer I guess) doesn't have focus.
+ int m_ShowKeyboardControl;
+ // IDLists for each window. Null if none
+ IMGUI::GUIWindowState* m_Windows;
+
+ IMGUI::NamedKeyControlList *m_NamedKeyControlList;
+
+ int m_FocusedGUIWindow;
+
+ void LoadIntoGUIState (GUIState &dest);
+ void SaveFromGUIState (GUIState &src);
+
+ void EndFrame ();
+
+ void Reset ();
+
+ GUIKeyboardState ();
+ ~GUIKeyboardState ();
+};
+
+void InitGUIState ();
+void CleanupGUIState ();
+#endif
diff --git a/Runtime/IMGUI/GUIStyle.cpp b/Runtime/IMGUI/GUIStyle.cpp
new file mode 100644
index 0000000..ac4e632
--- /dev/null
+++ b/Runtime/IMGUI/GUIStyle.cpp
@@ -0,0 +1,1154 @@
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "Runtime/Camera/RenderLayers/GUITexture.h"
+#include "Runtime/Filters/Misc/Font.h"
+#include "TextMeshGenerator2.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "External/shaderlab/Library/FastPropertyName.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "External/shaderlab/Library/properties.h"
+#include "Runtime/Graphics/GeneratedTextures.h"
+#include "External/shaderlab/Library/FastPropertyName.h"
+#include "Runtime/Graphics/TextureGenerator.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "Runtime/Shaders/Shader.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/GUIContent.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+#include "Runtime/IMGUI/GUIManager.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/AssetBundle.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/AssetPipeline/AssetDatabase.h"
+#include "Editor/Src/OptimizedGUIBlock.h"
+#include "Editor/Src/TooltipManager.h"
+#include "Editor/Src/Highlighter/HighlighterCore.h"
+#include "Editor/Src/EditorResources.h"
+#include "Editor/Src/EditorHelper.h"
+#endif
+
+const float s_TabSize = 16;
+using namespace std;
+static Rectf s_GUIClipRect (0,0,1,1);
+
+namespace GUIStyle_Static
+{
+ PPtr<Font> s_DefaultFont = NULL, s_BuiltinFont = NULL;
+} // namespace GUIStyle_Static
+
+// if these are set to 0 icons scale to fit the text
+float s_GUIStyleIconSizeX = 0;
+float s_GUIStyleIconSizeY = 0;
+
+
+// A minimal (4x4) texture is not enough because of limited subtexel precision on some cards.
+// With large clipping rectangles, one texel in clipping texture can span lots of pixels on screen,
+// and because of limited precision there can be one pixel errors.
+// E.g. Radeon HD 3850 seems to need at least 16x16 texture.
+const float kGUIClipTextureSize = 16.0f;
+
+static Material *kGUITextMaterial = NULL;
+static Material *kBlendMaterial = NULL;
+static Material *kBlitMaterial = NULL;
+
+inline void GUIClipTexture (Texture2D *tex, unsigned char *data, int x, int y, int maxX, int maxY)
+{
+ if (x == 0 || y == 0 || x == maxX - 1 || y == maxY - 1)
+ *data = 0;
+ else
+ *data = 255;
+}
+
+static Texture2D *gGUIClipTexture = NULL;
+
+Texture2D *GUIStyle::GetClipTexture ()
+{
+ return gGUIClipTexture;
+}
+
+void InitializeGUIClipTexture ()
+{
+ if (gGUIClipTexture)
+ return;
+
+ gGUIClipTexture = BuildTexture <unsigned char> (int(kGUIClipTextureSize), int(kGUIClipTextureSize), kTexFormatAlpha8, &GUIClipTexture);
+ gGUIClipTexture->SetFilterMode (kTexFilterNearest);
+ gGUIClipTexture->SetWrapMode(kTexWrapClamp);
+ ShaderLab::PropertySheet *props = ShaderLab::g_GlobalProperties;
+ ShaderLab::TexEnv *te = props->SetTexture (ShaderLab::Property ("_GUIClipTexture"),gGUIClipTexture);
+ te->SetMatrixName (ShaderLab::Property("_GUIClipTextureMatrix"));
+ te->SetTexGen (kTexGenEyeLinear);
+}
+
+Material* GetGUIBlitMaterial ()
+{
+ if (!kBlitMaterial)
+ {
+ Shader* shader = GetBuiltinResource<Shader> ("Internal-GUITextureBlit.shader");
+ kBlitMaterial = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+ InitializeGUIClipTexture();
+ }
+
+ return kBlitMaterial;
+}
+
+Material* GetGUIBlendMaterial ()
+{
+ if (!kBlendMaterial)
+ {
+ Shader* shader = GetBuiltinResource<Shader> ("Internal-GUITextureClip.shader");
+ kBlendMaterial = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+ InitializeGUIClipTexture();
+ }
+
+ return kBlendMaterial;
+}
+
+static Material* GetGUITextMaterial ()
+{
+ if (!kGUITextMaterial)
+ {
+ Shader* shader = GetBuiltinResource<Shader> ("Internal-GUITextureClipText.shader");
+ kGUITextMaterial = Material::CreateMaterial(*shader, Object::kHideAndDontSave);
+ InitializeGUIClipTexture();
+ }
+
+ return kGUITextMaterial;
+}
+
+GUIStyle::GUIStyle()
+{
+ m_Alignment = 0;
+ m_RichText = true;
+ m_WordWrap = 0;
+ m_Clipping = 0;
+ m_ImagePosition = 0;
+ m_ContentOffset = Vector2f(0.0f, 0.0f);
+ m_ClipOffset = Vector2f(0.0f, 0.0f);
+ m_FixedWidth = 0;
+ m_FixedHeight = 0;
+ m_FontSize = 0;
+ m_FontStyle = 0;
+ m_StretchWidth = true;
+ m_StretchHeight = false;
+
+}
+
+GUIStyle::GUIStyle (const GUIStyle &other)
+{
+ m_Name = other.m_Name;
+ m_Normal = other.m_Normal;
+ m_Hover = other.m_Hover;
+ m_Active = other.m_Active;
+ m_Focused = other.m_Focused;
+ m_OnNormal = other.m_OnNormal;
+ m_OnHover = other.m_OnHover;
+ m_OnActive = other.m_OnActive;
+ m_OnFocused = other.m_OnFocused;
+ m_Border = other.m_Border;
+ m_Margin = other.m_Margin;
+ m_Padding = other.m_Padding;
+ m_Overflow = other.m_Overflow;
+ m_Font = other.m_Font;
+ m_Alignment = other.m_Alignment;
+ m_RichText = other.m_RichText;
+ m_WordWrap = other.m_WordWrap;
+ m_Clipping = other.m_Clipping;
+ m_ImagePosition = other.m_ImagePosition;
+ m_ContentOffset = other.m_ContentOffset;
+ m_ClipOffset = other.m_ClipOffset;
+ m_FixedWidth = other.m_FixedWidth;
+ m_FixedHeight = other.m_FixedHeight;
+ m_FontSize = other.m_FontSize;
+ m_FontStyle = other.m_FontStyle;
+ m_StretchWidth = other.m_StretchWidth;
+ m_StretchHeight = other.m_StretchHeight;
+}
+
+
+void GUIStyle::SetDefaultFont (Font *font)
+{
+ using namespace GUIStyle_Static;
+ if (font != NULL)
+ s_DefaultFont = font;
+ else
+ s_DefaultFont = GetBuiltinResource<Font> (kDefaultFontName);
+}
+Font*GUIStyle::GetDefaultFont ()
+{
+ return GUIStyle_Static::s_DefaultFont;
+}
+
+void GUIStyle::Draw (GUIState &state, const Rectf &screenRect, GUIContent &content, int controlID, bool on) const
+{
+ InputEvent &evt (*state.m_CurrentEvent);
+ int hot = IMGUI::GetHotControl (state);
+ bool enabled = state.m_OnGUIState.m_Enabled;
+#if ENABLE_NEW_EVENT_SYSTEM
+ bool mouseOver = screenRect.Contains (evt.touch.pos);
+#else
+ bool mouseOver = screenRect.Contains (evt.mousePosition);
+#endif
+ bool isHover = mouseOver && state.m_CanvasGUIState.m_GUIClipState.GetEnabled();
+ bool isHover2 = isHover && (hot == controlID || hot == 0);
+
+ if (isHover)
+ state.m_CanvasGUIState.m_IsMouseUsed = true;
+
+ bool isActive = (controlID == hot) && enabled && mouseOver;
+ bool hasKey = state.m_MultiFrameGUIState.m_KeyboardControl == controlID && enabled && state.m_OnGUIState.m_ShowKeyboardControl;
+
+ Draw (state, screenRect, content, isHover2, isActive, on, hasKey);
+
+
+ // #if UNITY_GUI_SUPPORT_TOOLTIP
+ if (content.m_Tooltip.text != NULL && content.m_Tooltip.length != 0)
+ {
+ if (isHover || isActive || hot == controlID)
+ {
+ SetMouseTooltip (state, content.m_Tooltip, screenRect);
+ }
+ if (hasKey)
+ state.m_OnGUIState.SetKeyTooltip (content.m_Tooltip);
+ }
+ // #endif
+}
+
+void GUIStyle::SetMouseTooltip (GUIState& state, const UTF16String& tooltip, const Rectf& screenRect)
+{
+ state.m_OnGUIState.SetMouseTooltip (tooltip);
+#if UNITY_EDITOR
+ Vector2f v = GetGUIManager().GetGUIPixelOffset();
+ v += state.m_CanvasGUIState.m_GUIClipState.Unclip (Vector2f (screenRect.x, screenRect.y));
+ GetTooltipManager().SetTooltip (tooltip, Rectf (v.x, v.y, screenRect.width, screenRect.height));
+#endif
+
+}
+
+
+void GUIStyle::Draw (GUIState &state, const Rectf &screenRect, GUIContent &content, bool isHover, bool isActive, bool on, bool hasKeyboardFocus) const
+{
+ #if UNITY_EDITOR
+ if (HighlighterCore::s_SearchMode == kHighlightByContent || HighlighterCore::s_SearchMode == kHighlightAuto)
+ HighlighterCore::Handle (state, screenRect, content.m_Text);
+ #endif
+
+ // always draw of pixel coordinates
+ Rectf rounded = ClampRect (screenRect);
+ float right = Roundf (rounded.x + rounded.width);
+ float bottom = Roundf (rounded.y + rounded.height);
+ rounded.x = Roundf(rounded.x);
+ rounded.y = Roundf(rounded.y);
+ rounded.width = right - rounded.x;
+ rounded.height = bottom - rounded.y;
+
+ Rectf visibleRect = state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect();
+
+ if (bottom < visibleRect.y || rounded.y > visibleRect.GetBottom())
+ return;
+
+ isHover = state.m_EternalGUIState->m_AllowHover && isHover;
+ const GUIStyleState *gss = GetGUIStyleState (state, isHover, isActive, on, hasKeyboardFocus);
+ DrawBackground (state, rounded, gss);
+ DrawContent (state, rounded, content, gss);
+}
+
+void GUIStyle::DrawWithTextSelection (GUIState &state, const Rectf &screenRect, GUIContent &content, bool isHover, bool isActive, bool on, bool hasKeyboardFocus, bool drawSelectionAsComposition, int cursorFirst, int cursorLast, const ColorRGBAf &cursorColor, const ColorRGBAf &selectionColor) const {
+ // always draw of pixel coordinates
+ Rectf rounded = ClampRect (screenRect);
+ rounded.x = Roundf(rounded.x);
+ rounded.y = Roundf(rounded.y);
+ rounded.width = Roundf(rounded.width);
+ rounded.height = Roundf(rounded.height);
+
+ const GUIStyleState *gss = GetGUIStyleState (state, isHover, isActive, on, hasKeyboardFocus);
+ DrawBackground (state, rounded, gss);
+ if (hasKeyboardFocus)
+ {
+ if (drawSelectionAsComposition)
+ {
+ DrawTextUnderline (state, rounded, content, cursorFirst, cursorLast, gss);
+ // draw cursor.
+ DrawTextSelection (state, rounded, content, cursorLast, cursorLast, cursorColor, selectionColor);
+ }
+ else
+ DrawTextSelection (state, rounded, content, cursorFirst, cursorLast, cursorColor, selectionColor);
+ }
+ DrawContent (state, rounded, content, gss);
+}
+
+void GUIStyle::CalcMinMaxWidth (GUIContent &content, float *minWidth, float *maxWidth) const {
+ // If we have a fixed width, that becoms both min and max
+ if (m_FixedWidth != 0) {
+ *minWidth = *maxWidth = m_FixedWidth;
+ return;
+ }
+
+ TextMeshGenerator2 &tmgen = TextMeshGenerator2::Get (content.m_Text, &GetCurrentFont(), (TextAnchor)m_Alignment, kAuto, 0, s_TabSize, 1.0f, m_RichText, true, ColorRGBA32(0xffffffff), m_Font ? m_FontSize : 0, m_Font ? m_FontStyle : 0);
+ Vector2f size = tmgen.GetSize ();
+ // If we're word wrapping, we'll never go below 32 pixels.
+ // Ideally, we should get the size of the largest word, but that is just too painful.
+ *maxWidth = size.x;
+ if (m_WordWrap)
+ *minWidth = min (32.0f, size.x);
+ else
+ *minWidth = size.x;
+
+ if (content.m_Image) {
+ float iWidth = content.m_Image->GetDataWidth();
+ switch (m_ImagePosition) {
+ case kImageLeft:
+ *minWidth += iWidth;
+ *maxWidth += iWidth;
+ break;
+ case kImageOnly:
+ *minWidth = *maxWidth = iWidth;
+ break;
+ case kImageAbove:
+ *minWidth = max (iWidth, *minWidth);
+ *maxWidth = max (iWidth, *maxWidth);
+ break;
+ case kTextOnly:
+ break;
+ }
+ }
+
+ *minWidth += m_Padding.left + m_Padding.right;
+ *maxWidth += m_Padding.left + m_Padding.right;
+}
+
+Vector2f GUIStyle::GetCursorPixelPosition (const Rectf &screenRect, GUIContent &content, int cursorStringIndex) const {
+ TextMeshGenerator2 *tmgen = GetGenerator (screenRect, content);
+ if (tmgen)
+ return tmgen->GetCursorPosition(m_Padding.Remove (screenRect), cursorStringIndex) + (m_ContentOffset + m_ClipOffset);
+ else
+ return Vector2f (0,0);
+}
+
+int GUIStyle::GetCursorStringIndex (const Rectf &screenRect, GUIContent &content, const Vector2f &cursorPixelPosition) const {
+ TextMeshGenerator2 *tmgen = GetGenerator (screenRect, content);
+ if (tmgen)
+ return tmgen->GetCursorIndexAtPosition(m_Padding.Remove (screenRect), cursorPixelPosition - (m_ContentOffset + m_ClipOffset));
+ return 0;
+}
+
+Font& GUIStyle::GetCurrentFont () const
+{
+ using namespace GUIStyle_Static;
+ Font* thisFont = m_Font;
+ if (thisFont != NULL)
+ return *thisFont;
+
+ Font* defaultFont = s_DefaultFont;
+ if (defaultFont != NULL)
+ return *defaultFont;
+
+ return GetBuiltinFont ();
+}
+
+Font &GUIStyle::GetBuiltinFont ()
+{
+ Font* builtinFont = GUIStyle_Static::s_BuiltinFont;
+ if (builtinFont != NULL)
+ return *builtinFont;
+
+ GUIStyle_Static::s_BuiltinFont = builtinFont = GetBuiltinResource<Font> (kDefaultFontName);
+ if (builtinFont == NULL)
+ {
+ LogString ("Couldn't load default font or font material!");
+ }
+
+ return *builtinFont;
+}
+
+float GUIStyle::GetLineHeight () const {
+ Font& f = GetCurrentFont();
+ return f.GetLineSpacing (m_FontSize);
+}
+
+int GUIStyle::GetNumCharactersThatFitWithinWidth (const UTF16String &text, float width) const
+{
+ Font &f = GetCurrentFont();
+
+ // Call before GetCharacterWidth to ensure character data has been setup
+ f.CacheFontForText (text.text, text.length);
+
+ width -= m_Padding.left + m_Padding.right;
+
+ float currentWidth = 0;
+ unsigned stringlen = text.length;
+ int numChars = stringlen;
+ for (int i=0; i<stringlen; i++)
+ {
+ int c = text[i];
+ float characterWidth = f.GetCharacterWidth (c, m_FontSize, m_FontStyle);
+ if (characterWidth == 0.f)
+ {
+ return -1; // failed to get character width
+ }
+
+ currentWidth += characterWidth;
+ if (currentWidth > width)
+ {
+ numChars = i;
+ break;
+ }
+ }
+
+ return numChars;
+}
+
+float GUIStyle::CalcHeight (GUIContent &content, float width) const {
+ if (m_FixedHeight != 0.0f)
+ return m_FixedHeight;
+
+ Vector2f imageSize (0,0), textSize (0,0);
+ Texture *image = content.m_Image;
+ if (image != NULL)
+ imageSize = Vector2f (image->GetDataWidth(), image->GetDataHeight());
+
+ float contentHeight = 0;
+
+ TextMeshGenerator2 *tmgen = GetGenerator (Rectf (0,0,width, 1000), content);
+ if (tmgen)
+ textSize = tmgen->GetSize ();
+ switch (m_ImagePosition) {
+ case kImageLeft: {
+ contentHeight = max (textSize.y, imageSize.y);
+ break;
+ }
+ case kImageAbove: {
+ contentHeight = textSize.y + imageSize.y;
+ break;
+ }
+ case kImageOnly:
+ contentHeight = imageSize.y;
+ break;
+ case kTextOnly: {
+ contentHeight = textSize.y;
+ break;
+ }
+ }
+
+ return contentHeight + m_Padding.top + m_Padding.bottom;
+}
+
+Vector2f GUIStyle::CalcSize (GUIContent &content) const {
+ Texture *image = content.m_Image;
+
+ if (m_FixedHeight != 0.0f && m_FixedWidth != 0.0f)
+ return Vector2f (m_FixedWidth, m_FixedHeight);
+
+ Vector2f textSize (0,0), imageSize (0,0);
+ if (content.m_Text.length != 0 && m_ImagePosition != kImageOnly)
+ textSize = GetGenerator(Rectf (0,0,0,0), content)->GetSize ();
+ if (image != NULL && m_ImagePosition != kTextOnly)
+ imageSize = Vector2f (image->GetDataWidth(), image->GetDataHeight());
+
+ Vector2f size (0,0);
+ switch (m_ImagePosition) {
+ case kImageLeft:
+ if (imageSize.x > 0)
+ {
+ if ((s_GUIStyleIconSizeX != 0) && (s_GUIStyleIconSizeY != 0))
+ {
+ imageSize.x = s_GUIStyleIconSizeX;
+ imageSize.y = s_GUIStyleIconSizeY;
+ }
+ }
+ size = Vector2f (textSize.x + imageSize.x, max (textSize.y, imageSize.y));
+ break;
+ case kImageAbove:
+ size = Vector2f (max (textSize.x, imageSize.x), textSize.y + imageSize.y);
+ break;
+ case kImageOnly:
+ size = imageSize;
+ break;
+ case kTextOnly:
+ size = textSize;
+ break;
+ }
+
+ // If we
+ if (content.m_Text.length == 0 && image == NULL && m_ImagePosition != kImageOnly)
+ {
+ size.y = GetCurrentFont().GetLineSpacing(m_FontSize);
+ }
+ size+= m_Padding.Size ();
+ if (m_FixedWidth != 0.0f)
+ size.x = m_FixedWidth;
+ if (m_FixedHeight != 0.0f)
+ size.y = m_FixedHeight;
+ return size;
+}
+
+const GUIStyleState *GUIStyle::GetGUIStyleState (GUIState &state, bool isHover, bool isActive, bool on, bool hasKeyboardFocus) const
+{
+ const GUIStyleState *t = NULL;
+ if (!on) {
+ // If the GUI is disabled, return the normal style
+ if (isHover)
+ t = m_Hover.background ? &m_Hover : t;
+ if (hasKeyboardFocus)
+ t = m_Focused.background ? &m_Focused : (m_Hover.background ? &m_Hover : t);
+ if (isActive && isHover)
+ t = m_Active.background ? &m_Active : t;
+ if (!state.m_OnGUIState.m_Enabled)
+ t = &m_Normal;
+ } else {
+ // If the GUI is disabled, return the normal style
+ if (isHover)
+ t = m_OnHover.background ? &m_OnHover : t;
+ if (hasKeyboardFocus)
+ t = m_OnFocused.background ? &m_OnFocused : (m_OnHover.background ? &m_OnHover : t);
+ if (isActive && isHover)
+ t = m_OnActive.background ? &m_OnActive : t;
+ if (!state.m_OnGUIState.m_Enabled)
+ t = &m_Normal;
+
+ if (t == NULL || !t->background || !state.m_OnGUIState.m_Enabled)
+ t = &m_OnNormal;
+ }
+
+ if (t == NULL || !t->background)
+ t = &m_Normal;
+ return t;
+}
+
+static void DrawClippedTexture (const Rectf &screenRect, Texture *image, float leftBorder, float rightBorder, float topBorder, float bottomBorder, const ColorRGBAf &color)
+{
+ DrawGUITexture (screenRect, image, int(leftBorder), int(rightBorder), int(topBorder), int(bottomBorder), color, GetGUIBlendMaterial());
+}
+
+
+void GUIStyle::DrawBackground (GUIState &state, const Rectf &screenRect, const GUIStyleState *gss) const {
+#if ENABLE_RETAINEDGUI
+ if (state.m_OnGUIState.m_CaptureBlock)
+ {
+ if (gss->background)
+ {
+ GUIVertexData* data = new GUIVertexData (&GUIVertexDataFormat::vtxPosColorUV0UV1);
+ GUIUtils::BuildImage (m_Overflow.Add (screenRect), m_Border, gss->background, Vector4f(1.0f, 1.0f, 0.0f, 0.0f), *data);
+ GUIClipRegion temp (state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect());
+ GUIUtils::BuildClipCoords (&temp, *data);
+
+ ColorRGBAf backgroundColor = state.m_OnGUIState.m_Color * state.m_OnGUIState.m_BackgroundColor;
+ if (!state.m_OnGUIState.m_Enabled)
+ backgroundColor.a *= .5f;
+ GUIUtils::BuildColor(backgroundColor, *data);
+
+ state.m_OnGUIState.m_CaptureBlock->push_back (GUIGraphicsCacheBlock (data, GetGUIBlendMaterial()));
+ }
+ return;
+ }
+#endif
+
+ Rectf visibleRect = state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect();
+ SetGUIClipRect(visibleRect);
+
+ if (gss->background) {
+ ColorRGBAf backgroundColor = state.m_OnGUIState.m_Color * state.m_OnGUIState.m_BackgroundColor;
+ if (!state.m_OnGUIState.m_Enabled)
+ backgroundColor *= ColorRGBAf (1,1,1,.5f);
+
+#if UNITY_EDITOR
+ OptimizedGUIBlock *block = GetCaptureGUIBlock ();
+ if (block)
+ {
+ block->QueueBackground (m_Overflow.Add (screenRect), gss->background, m_Border, backgroundColor, state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect());
+ return;
+ }
+#endif
+
+ DrawClippedTexture (m_Overflow.Add (screenRect), gss->background, m_Border.left, m_Border.right, m_Border.top, m_Border.bottom, backgroundColor);
+ }
+}
+
+void GUIStyle::RenderText (const Rectf &screenRect, TextMeshGenerator2 &tmgen, ColorRGBAf color) const {
+ // Configure the shaders
+ Font& currentFont = GetCurrentFont();
+
+ Material *material = GetGUITextMaterial ();
+
+ // now we set color using TextMeshGenerator vertices, rather then using the material, so set material color to white.
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1))
+ color = ColorRGBA32(0xffffffff);
+
+ static SHADERPROP (Color);
+ static SHADERPROP (MainTex);
+ ShaderLab::PropertySheet& properties = material->GetWritableProperties();
+
+ properties.SetVector(kSLPropColor, color.GetPtr());
+
+ Texture* fontTexture = currentFont.GetTexture();
+
+ // In the case when font is a custum font, GetTexture() will return NULL, so in this case take the main texture from font's material
+ if (fontTexture == NULL && currentFont.GetMaterial())
+ {
+ fontTexture = currentFont.GetMaterial()->GetTexture(kSLPropMainTex);
+ }
+
+ properties.SetTexture(kSLPropMainTex, fontTexture);
+
+ GfxDevice &device = GetGfxDevice ();
+
+ float matWorld[16], matView[16];
+
+ CopyMatrix(device.GetViewMatrix(), matView);
+ CopyMatrix(device.GetWorldMatrix(), matWorld);
+
+ Matrix4x4f textMatrix;
+
+ Vector2f offset = tmgen.GetTextOffset (screenRect);
+
+ textMatrix.SetTranslate (Vector3f (offset.x, offset.y, 0.0f));
+
+ device.SetViewMatrix (textMatrix.GetPtr());
+
+ int passCount = material->GetPassCount ();
+ for (int i=0;i < passCount ;i++)
+ {
+ const ChannelAssigns* channels = material->SetPass (i);
+ tmgen.RenderRaw (*channels);
+ }
+ device.SetViewMatrix(matView);
+ device.SetWorldMatrix(matWorld);
+}
+
+TextMeshGenerator2* GUIStyle::GetGenerator (const Rectf &screenRect, GUIContent &content, ColorRGBA32 color) const
+{
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1))
+ color = 0xffffffff;
+ return IMGUI::GetGenerator (m_Padding.Remove (screenRect), content, GetCurrentFont(), (TextAnchor)m_Alignment, m_WordWrap, m_RichText, color, m_FontSize, m_FontStyle, (ImagePosition)m_ImagePosition);
+}
+
+TextMeshGenerator2* GUIStyle::GetGenerator (const Rectf &screenRect, GUIContent &content) const
+{
+ ColorRGBA32 color = 0xffffffff;
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1))
+ {
+ GUIState &state = GetGUIState();
+ ColorRGBAf imageColor = state.m_OnGUIState.m_Color * state.m_OnGUIState.m_ContentColor;
+ ColorRGBAf textColor = m_Normal.textColor * imageColor;
+ if (!state.m_OnGUIState.m_Enabled) {
+ textColor.a *= .5f;
+ }
+ color = textColor;
+ }
+ return IMGUI::GetGenerator (m_Padding.Remove (screenRect), content, GetCurrentFont(), (TextAnchor)m_Alignment, m_WordWrap, m_RichText, color, m_FontSize, m_FontStyle, (ImagePosition)m_ImagePosition);
+}
+
+
+void GUIStyle::CalcContentRects (const Rectf &contentRect, Vector2f imageSize, Vector2f textSize, Rectf &imageRect, Rectf &textRect, float &width, float &height, int imagePosition, int alignment, Vector2f contentOffset)
+{
+ // Sum them up depending on contents layout.
+ width = 0;
+ height = 0;
+ switch (imagePosition) {
+ case kImageLeft:
+ // Scale the image down if we need the room
+ if (imageSize.x > 0)
+ {
+ if ((s_GUIStyleIconSizeX == 0) || (s_GUIStyleIconSizeY == 0))
+ {
+ float imageScale = clamp (min ((contentRect.Width() - textSize.x) / imageSize.x, contentRect.Height() / imageSize.y), 0.0f, 1.0f);
+
+ imageSize.x = Roundf (imageSize.x * imageScale);
+ imageSize.y = Roundf (imageSize.y * imageScale);
+ }
+ else
+ {
+ imageSize.x = s_GUIStyleIconSizeX;
+ imageSize.y = s_GUIStyleIconSizeY;
+ }
+ }
+
+ width = imageSize.x + textSize.x;
+ height = max (imageSize.y, textSize.y);
+ break;
+ case kImageAbove:
+ // Scale the image down if we need the room
+ if (imageSize.x > 0)
+ {
+ if ((s_GUIStyleIconSizeX == 0) || (s_GUIStyleIconSizeY == 0))
+ {
+ float imageScale = clamp (min ((contentRect.Height() - textSize.y) / imageSize.y, contentRect.Width() / imageSize.x), 0.0f, 1.0f);
+ imageSize.x = Roundf (imageSize.x * imageScale);
+ imageSize.y = Roundf (imageSize.y * imageScale);
+ }
+ else
+ {
+ imageSize.x = s_GUIStyleIconSizeX;
+ imageSize.y = s_GUIStyleIconSizeY;
+ }
+ }
+
+ width = max (imageSize.x, textSize.x);
+ height = imageSize.y + textSize.y;
+ break;
+ case kImageOnly:
+ // Scale the image down if we need the room
+ if (imageSize.x > 0)
+ {
+ if ((s_GUIStyleIconSizeX == 0) || (s_GUIStyleIconSizeY == 0))
+ {
+ float imageScale = min (min (contentRect.Width() / imageSize.x, contentRect.Height() / imageSize.y), 1.0f);
+ imageSize.x = Roundf (imageSize.x * imageScale);
+ imageSize.y = Roundf (imageSize.y * imageScale);
+ }
+ else
+ {
+ imageSize.x = s_GUIStyleIconSizeX;
+ imageSize.y = s_GUIStyleIconSizeY;
+ }
+ }
+
+ width = imageSize.x;
+ height = imageSize.y;
+ break;
+ case kTextOnly:
+ width = textSize.x;
+ height = textSize.y;
+ break;
+ }
+
+ // We now have the combined sizes - need to know where to put them.
+ float xAlign = 0, yAlign = 0;
+ switch (alignment) {
+ case kUpperLeft:
+ xAlign = 0; yAlign = 0; break;
+ case kUpperCenter:
+ xAlign = .5f; yAlign = 0; break;
+ case kUpperRight:
+ xAlign = 1; yAlign = 0; break;
+ case kMiddleLeft:
+ xAlign = 0; yAlign = .5f; break;
+ case kMiddleCenter:
+ xAlign = .5f; yAlign = .5f; break;
+ case kMiddleRight:
+ xAlign = 1; yAlign = .5f; break;
+ case kLowerLeft:
+ xAlign = 0; yAlign = 1; break;
+ case kLowerCenter:
+ xAlign = .5f; yAlign = 1; break;
+ case kLowerRight:
+ xAlign = 1; yAlign = 1; break;
+ }
+ // We need to round as centering the text can position it on half a pixel
+ float x = Roundf (contentRect.x + (contentRect.width - width) * xAlign + contentOffset.x);
+ float y = Roundf (contentRect.y + (contentRect.height - height) * yAlign + contentOffset.y);
+
+ switch (imagePosition) {
+ case kImageLeft:
+ if (imageSize.x > 0.0f)
+ imageRect = Rectf (x, y + (height - imageSize.y) * .5f, imageSize.x, imageSize.y);
+ if (textSize.x > 0.0f && imageSize.x > 0.0f)
+ textRect = Rectf (x + imageSize.x + 1, y + (height - textSize.y) * .5f, textSize.x, textSize.y);
+ else if (textSize.x > 0.0f)
+ textRect = Rectf (x, y + (height - textSize.y) * .5f, textSize.x, textSize.y);
+ break;
+ case kImageAbove:
+ if (imageSize.x > 0.0f)
+ imageRect = Rectf (Roundf (x + (width - imageSize.x) * .5f), y, imageSize.x, imageSize.y);
+ if (textSize.x > 0.0f)
+ textRect = Rectf (Roundf (x + (width - textSize.x) * .5f), y + imageSize.y, textSize.x, textSize.y);
+
+ break;
+ case kImageOnly:
+ if (imageSize.x > 0.0f)
+ imageRect = Rectf (Roundf (x + (width - imageSize.x) * .5f), y, imageSize.x, imageSize.y);
+ break;
+ case kTextOnly:
+ if (textSize.x > 0.0f)
+ textRect = Rectf (x, y, textSize.x, textSize.y);
+ break;
+ }
+}
+
+void GUIStyle::DrawContent (GUIState &state, const Rectf &screenRect, GUIContent &content, const GUIStyleState *gss) const
+{
+ Vector2f textSize (0,0), imageSize (0,0);
+ TextMeshGenerator2 *tmgen = NULL;
+
+ // we now have the location of the contents in the rects. Now to draw it.
+ // Setup the color
+ ColorRGBAf imageColor = state.m_OnGUIState.m_Color * state.m_OnGUIState.m_ContentColor;
+ ColorRGBAf textColor = gss->textColor * imageColor;
+ if (!state.m_OnGUIState.m_Enabled) {
+ textColor.a *= .5f;
+ imageColor.a *= .5f;
+ }
+
+ if (m_ImagePosition != kImageOnly && content.m_Text.length != 0)
+ {
+ tmgen = GetGenerator (screenRect, content, textColor);
+ textSize = tmgen->GetSize();
+ }
+
+ // TODO: If we use word wrapping and imageLeft we should somehow subtract the width...
+ Texture *image = content.m_Image;
+ if (image != NULL && m_ImagePosition != kTextOnly)
+ imageSize = Vector2f (image->GetDataWidth(), image->GetDataHeight());
+
+ Rectf imageRect (0,0,0,0), textRect (0,0,0,0);
+ float width, height;
+ Rectf contentRect = m_Padding.Remove (screenRect);
+ CalcContentRects (contentRect, imageSize, textSize, imageRect, textRect, width, height, m_ImagePosition, m_Alignment, m_ContentOffset);
+
+#if ENABLE_RETAINEDGUI
+ if (state.m_OnGUIState.m_CaptureBlock)
+ {
+ GUIVertexData* data = new GUIVertexData (&GUIVertexDataFormat::vtxPosColorUV0UV1);
+ GUIUtils::BuildText(textRect, *tmgen, *data);
+
+ GUIClipRegion temp (state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect());
+ GUIUtils::BuildClipCoords (&temp, *data);
+
+ GUIUtils::BuildColor(textColor, *data);
+
+ state.m_OnGUIState.m_CaptureBlock->push_back (GUIGraphicsCacheBlock (data, GetGUITextMaterial()));
+ return;
+ }
+#endif
+ // We know where the various components go, now we draw them (with lots of clipping shit)
+ Rectf visibleRect = state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect();
+
+#if UNITY_EDITOR
+ OptimizedGUIBlock *block = GetCaptureGUIBlock ();
+ if (block)
+ {
+ Rectf newClipRect, clipRect = visibleRect;
+ if (m_Clipping != kOverflow && (width > contentRect.width || height > contentRect.height))
+ {
+ newClipRect = contentRect;
+ newClipRect.x += (m_ContentOffset.x + m_ClipOffset.x);
+ newClipRect.y += (m_ContentOffset.y + m_ClipOffset.y);
+ newClipRect.Clamp (clipRect);
+ } else {
+ newClipRect = clipRect;
+ }
+
+ // Queue the text for later processing
+ if (textRect.width != 0.0f)
+ {
+ block->QueueText (textRect, tmgen, newClipRect);
+ }
+ // Render image
+ if (imageRect.width != 0.0f)
+ block->QueueTexture (imageRect, image, imageColor, clipRect);
+ return;
+ }
+#endif
+
+ // Do clipping of content
+ bool needClipping = false;
+ Rectf newClipRect;
+ if (m_Clipping != kOverflow && (width > contentRect.width || height > contentRect.height)) {
+ needClipping = true;
+ newClipRect = contentRect;
+ newClipRect.x += (m_ContentOffset.x + m_ClipOffset.x);
+ newClipRect.y += (m_ContentOffset.y + m_ClipOffset.y);
+ newClipRect.Clamp (visibleRect);
+ if (newClipRect.width == 0.0f || newClipRect.height == 0.0f)
+ return;
+ SetGUIClipRect (newClipRect);
+ } else {
+ SetGUIClipRect(visibleRect);
+ }
+
+ // Render text
+ if (textRect.width != 0.0f && tmgen != NULL)
+ {
+ RenderText (textRect, *tmgen, textColor);
+ }
+ // Render image
+ if (imageRect.width != 0.0f)
+ DrawClippedTexture (imageRect, image, 0,0,0,0,imageColor);
+
+ // If we used custom clipping, restore it
+ if (needClipping)
+ SetGUIClipRect (visibleRect);
+
+}
+
+Rectf GUIStyle::ClampRect (const Rectf &screenRect) const
+{
+ return Rectf (screenRect.x, screenRect.y, m_FixedWidth ? m_FixedWidth : screenRect.Width(), m_FixedHeight ? m_FixedHeight : screenRect.Height());
+}
+
+void GUIStyle::DrawCursor(GUIState &state, const Rectf &screenRect, GUIContent &content, int position, const ColorRGBAf &cursorColor) const
+{
+ if (!state.m_OnGUIState.m_Enabled)
+ return;
+
+ Texture *tex = builtintex::GetWhiteTexture();
+ Font& currentFont = GetCurrentFont();
+
+ float lineHeight = currentFont.GetLineSpacing (m_FontSize);
+ Material *material = GetGUIBlendMaterial();
+
+ ColorRGBA32 textureColor = cursorColor * state.m_OnGUIState.m_Color;
+ Vector2f cursorPos = GetCursorPixelPosition (screenRect, content, position) - m_ClipOffset;
+ DrawGUITexture(Rectf (cursorPos.x, cursorPos.y, 1, lineHeight),tex,textureColor,material);
+}
+
+void GUIStyle::DrawTextSelection (GUIState &state, const Rectf &screenRect, GUIContent &content, int first, int last, const ColorRGBAf &cursorColor, const ColorRGBAf &selectionColor) const {
+ if (!state.m_OnGUIState.m_Enabled)
+ return;
+ Texture *tex = builtintex::GetWhiteTexture();
+
+ Font& currentFont = GetCurrentFont();
+
+ float lineHeight = currentFont.GetLineSpacing (m_FontSize);
+ Material *material = GetGUIBlendMaterial();
+
+ Rectf visibleRect = state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect();
+ SetGUIClipRect(visibleRect);
+
+ // If the style uses clipping, just apply it - there's only ever one of these anyways
+ Rectf innerRect = m_Padding.Remove (screenRect);
+ Rectf clipRect;
+ if (m_Clipping) {
+ clipRect = visibleRect;
+ innerRect.Clamp (visibleRect);
+ innerRect.x += (m_ContentOffset.x + m_ClipOffset.x);
+ innerRect.y += (m_ContentOffset.y + m_ClipOffset.y);
+
+ SetGUIClipRect (innerRect);
+ }
+
+ // We don't have a selection, only a cursor position
+ if (first == last) {
+ ColorRGBA32 textureColor = cursorColor * state.m_OnGUIState.m_Color;
+ Vector2f cursorPos = GetCursorPixelPosition (screenRect, content, first) - m_ClipOffset;
+
+ // if we auto-size the GUIStyle to the text, and the cursor is after the last character, it always gets clipped.
+ // Move it one pixel to the left, so it does not clip
+ if (first == content.m_Text.length && cursorPos.x >= screenRect.x + screenRect.width)
+ cursorPos.x--;
+
+#if UNITY_EDITOR
+ OptimizedGUIBlock *block = GetCaptureGUIBlock ();
+ if (block)
+ {
+ block->QueueTexture (Rectf (cursorPos.x, cursorPos.y, 1, lineHeight),tex,textureColor,visibleRect);
+ } else {
+ DrawGUITexture(Rectf (cursorPos.x, cursorPos.y, 1, lineHeight),tex,textureColor,material);
+ }
+#else
+ DrawGUITexture(Rectf (cursorPos.x, cursorPos.y, 1, lineHeight),tex,textureColor,material);
+#endif
+ } else {
+ ColorRGBA32 textureColor = selectionColor * state.m_OnGUIState.m_Color;
+
+ int min = first < last ? first : last;
+ int max = first > last ? first : last;
+
+ Vector2f minPos = GetCursorPixelPosition (screenRect, content, min) - m_ClipOffset;
+ Vector2f maxPos = GetCursorPixelPosition (screenRect, content, max) - m_ClipOffset;
+
+ if (minPos.y == maxPos.y)
+ {
+#if UNITY_EDITOR
+ OptimizedGUIBlock *block = GetCaptureGUIBlock ();
+ if (block)
+ {
+ block->QueueTexture (Rectf (minPos.x, minPos.y, maxPos.x - minPos.x + 1, lineHeight), tex, textureColor, visibleRect);
+ } else {
+#endif
+ DrawGUITexture (Rectf (minPos.x, minPos.y, maxPos.x - minPos.x + 1, lineHeight), tex, textureColor, material);
+#if UNITY_EDITOR
+ }
+#endif
+ } else {
+#if UNITY_EDITOR
+ OptimizedGUIBlock *block = GetCaptureGUIBlock ();
+ if (block)
+ {
+ // draw the first line - including end part
+ block->QueueTexture (Rectf (minPos.x, minPos.y, innerRect.GetRight() - minPos.x, lineHeight), tex, textureColor, visibleRect);
+ // Draw the middle part
+ block->QueueTexture (Rectf (innerRect.x, minPos.y + lineHeight, innerRect.Width(), maxPos.y - minPos.y - lineHeight), tex, textureColor, visibleRect);
+ // Draw the bottom line - up to selection
+ if (maxPos.x != innerRect.x) // Don't draw silly 1px line at the left when a newline is selected
+ block->QueueTexture (Rectf (innerRect.x, maxPos.y, maxPos.x - innerRect.x + 1, lineHeight), tex, textureColor, visibleRect);
+ } else {
+#endif
+ // draw the first line - including end part
+ DrawGUITexture (Rectf (minPos.x, minPos.y, innerRect.GetRight() - minPos.x, lineHeight), tex, textureColor, material);
+ // Draw the middle part
+ DrawGUITexture (Rectf (innerRect.x, minPos.y + lineHeight, innerRect.Width(), maxPos.y - minPos.y - lineHeight), tex, textureColor, material);
+ // Draw the bottom line - up to selection
+ if (maxPos.x != innerRect.x) // Don't draw silly 1px line at the left when a newline is selected
+ DrawGUITexture (Rectf (innerRect.x, maxPos.y, maxPos.x - innerRect.x + 1, lineHeight), tex, textureColor, material);
+#if UNITY_EDITOR
+ }
+#endif
+ }
+ }
+ if (m_Clipping)
+ SetGUIClipRect (clipRect);
+
+}
+
+void GUIStyle::DrawTextUnderline (GUIState &state, const Rectf &screenRect, GUIContent &content, int first, int last, const GUIStyleState *gss) const
+{
+ if (!state.m_OnGUIState.m_Enabled)
+ return;
+
+ Rectf visibleRect = state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect();
+
+ SetGUIClipRect(visibleRect);
+
+ Texture *tex = builtintex::GetWhiteTexture();
+ Font& currentFont = GetCurrentFont();
+
+ float lineHeight = currentFont.GetLineSpacing (m_FontSize);
+ Material *material = GetGUIBlendMaterial();
+
+ // If the style uses clipping, just apply it - there's only ever one of these anyways
+ Rectf innerRect = m_Padding.Remove (screenRect);
+ Rectf clipRect;
+
+ if (m_Clipping) {
+ clipRect = visibleRect;
+ innerRect.Clamp (visibleRect);
+ innerRect.x += (m_ContentOffset.x + m_ClipOffset.x);
+ innerRect.y += (m_ContentOffset.y + m_ClipOffset.y);
+
+ SetGUIClipRect (innerRect);
+ }
+
+ {
+ ColorRGBA32 textColor = gss->textColor * state.m_OnGUIState.m_Color * state.m_OnGUIState.m_ContentColor;
+
+ int min = first < last ? first : last;
+ int max = first > last ? first : last;
+
+ Vector2f pos = GetCursorPixelPosition (screenRect, content, min) - m_ClipOffset;
+ Vector2f maxPos = GetCursorPixelPosition (screenRect, content, max) - m_ClipOffset;
+
+ float underlineSize = std::max(1.0f, lineHeight*0.03f);
+ float underlineOffset = lineHeight*0.95f-underlineSize;
+
+ while(pos.y < maxPos.y - 0.01)
+ {
+#if UNITY_EDITOR
+ OptimizedGUIBlock *block = GetCaptureGUIBlock ();
+ if (block)
+ {
+ block->QueueTexture (Rectf (pos.x, pos.y+underlineOffset, innerRect.GetRight() - pos.x + 1, underlineSize), tex, textColor, visibleRect);
+ } else
+#endif
+ DrawGUITexture (Rectf (pos.x, pos.y+underlineOffset, innerRect.GetRight() - pos.x + 1, underlineSize), tex, textColor, material);
+ pos.y += ceilf(lineHeight);
+ pos.x = innerRect.x;
+ }
+#if UNITY_EDITOR
+ OptimizedGUIBlock *block = GetCaptureGUIBlock ();
+ if (block)
+ {
+ block->QueueTexture (Rectf (pos.x, pos.y+underlineOffset, maxPos.x - pos.x + 1, underlineSize), tex, textColor, visibleRect);
+ } else
+#endif
+ DrawGUITexture (Rectf (pos.x, pos.y+underlineOffset, maxPos.x - pos.x + 1, underlineSize), tex, textColor, material);
+ }
+ if (m_Clipping)
+ SetGUIClipRect (clipRect);
+}
+
+void GUIStyle::SetStyleState (int stateIndex, ColorRGBAf textColor, Texture2D *background) {
+ GUIStyleState *gss = &m_Normal;
+ gss += stateIndex;
+ gss->background = background;
+ gss->textColor = textColor;
+}
+
+void GUIStyle::SetGUIClipRect (const Rectf &screenRect)
+{
+ s_GUIClipRect = screenRect;
+ Matrix4x4f clipMatrix;
+ clipMatrix.SetIdentity();
+ // In a divide-by-zero case, set these to infinity, so we don't render anything.
+ if (screenRect.width > 0.0f)
+ clipMatrix.Get (0,0) = (kGUIClipTextureSize-2.0f)/kGUIClipTextureSize / screenRect.width;
+ else
+ clipMatrix.Get (0,0) = numeric_limits<float>::infinity ();
+ if (screenRect.height > 0.0f)
+ clipMatrix.Get (1,1) = (kGUIClipTextureSize-2.0f)/kGUIClipTextureSize / screenRect.height;
+ else
+ clipMatrix.Get (1,1) = numeric_limits<float>::infinity ();
+ clipMatrix.Get (0,3) = -screenRect.x * clipMatrix.Get (0,0) + 1.0f/kGUIClipTextureSize;
+ clipMatrix.Get (1,3) = -screenRect.y * clipMatrix.Get (1,1) + 1.0f/kGUIClipTextureSize;
+ clipMatrix.Get (2,2) = clipMatrix.Get (3,3) = 0; // Kill all perspective/depth: Just put 1 in ZW texcoords
+ clipMatrix.Get (2,3) = clipMatrix.Get (3,3) = 1; // Kill all perspective/depth: Just put 1 in ZW texcoords
+ GetGfxDevice().GetBuiltinParamValues().SetMatrixParam(kShaderMatGUIClip, clipMatrix);
+}
+
+Rectf GUIStyle::GetGUIClipRect ()
+{
+ return s_GUIClipRect;
+}
+
+MonoBehaviour* GetBuiltinSkin (int skin)
+{
+ // 0 == Game, 1 == Editor Light, 2 = Editor Dark
+ static PPtr<MonoBehaviour> skinObject[3] = { NULL, NULL, NULL };
+
+#if UNITY_EDITOR
+ // Load skins (editor version)
+ if (!skinObject[0] || !skinObject[1] || !skinObject[2])
+ {
+ // If this is a devel buildwe want to try and load the skins from the opened project
+ // (super useful when skinning the app).
+ if (IsDeveloperBuild ())
+ {
+ // Try to load the skins from the current project
+ skinObject[0] = dynamic_pptr_cast<MonoBehaviour*> (GetMainAsset ("Assets/DefaultResources/GameSkin/GameSkin.GUISkin"));
+ skinObject[1] = dynamic_pptr_cast<MonoBehaviour*> (GetMainAsset (AppendPathName ("Assets/Editor Default Resources/", EditorResources::kLightSkinPath)));
+ skinObject[2] = dynamic_pptr_cast<MonoBehaviour*> (GetMainAsset (AppendPathName ("Assets/Editor Default Resources/", EditorResources::kDarkSkinPath)));
+ }
+
+ // Load the game skin.
+ // We can not mark this object as dont save, because that will make it not be unloaded when unloading the player.
+ // When that happens in the player the serialized state will be lost. So instead we let it be unloaded and on next
+ // load it will be reloaded from disk.
+ if (!skinObject[0])
+ {
+ Object *obj = GetBuiltinResourceManager ().GetResource (ClassID(MonoBehaviour), "GameSkin/GameSkin.guiskin");
+ skinObject[0] = static_cast<MonoBehaviour*> (obj);
+ }
+
+ // Load the light inspector skin.
+ if (!skinObject[1])
+ skinObject[1] = GetEditorAssetBundle()->Get<MonoBehaviour>(EditorResources::kLightSkinPath);
+
+ // Load the dark inspector skin.
+ if (!skinObject[2])
+ skinObject[2] = GetEditorAssetBundle()->Get<MonoBehaviour>(EditorResources::kDarkSkinPath);
+ }
+#else
+ // Players are much easier: we just load the game skin.
+ if (!skinObject[0])
+ {
+ Object *obj = GetBuiltinResourceManager ().GetResource (ClassID(MonoBehaviour), "GameSkin/GameSkin.guiskin");
+ skinObject[0] = static_cast<MonoBehaviour*> (obj);
+ }
+#endif
+
+ return skinObject[skin];
+}
+
+MonoBehaviour* GetDefaultSkin (int skinMode)
+{
+#if UNITY_EDITOR
+ if (skinMode == 0)
+ return GetBuiltinSkin (0);
+ return GetBuiltinSkin (GetEditorResources().GetSkinIdx () + 1);
+#else
+ return GetBuiltinSkin (0);
+#endif
+}
diff --git a/Runtime/IMGUI/GUIStyle.h b/Runtime/IMGUI/GUIStyle.h
new file mode 100644
index 0000000..d1af3ac
--- /dev/null
+++ b/Runtime/IMGUI/GUIStyle.h
@@ -0,0 +1,297 @@
+#ifndef GUISTYLE_H
+#define GUISTYLE_H
+
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/IMGUI/TextUtil.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+
+class Font;
+struct MonoString;
+struct GUIState;
+struct GUIContent;
+class TextMeshGenerator2;
+struct GUIGraphicsCacheBlock;
+class GUIClipRegion;
+
+struct GUIStyleState {
+ DECLARE_SERIALIZE (GUIStyleState)
+
+ /// Background image used by this style
+ PPtr<Texture2D> background;
+ /// The color of the text
+ ColorRGBAf textColor;
+
+ GUIStyleState () : textColor (0,0,0,1) {}
+ GUIStyleState (const GUIStyleState &other) : background (other.background), textColor (other.textColor) {}
+};
+
+template<class TransferFunc>
+void GUIStyleState::Transfer (TransferFunc& transfer)
+{
+ transfer.Transfer (background, "m_Background");
+ transfer.Transfer (textColor, "m_TextColor");
+}
+
+/// Positioning of the image and the text withing a GUIStyle
+enum ImagePosition {
+ /// Image is to the left of the text.
+ kImageLeft = 0,
+ /// Image is above the text.
+ kImageAbove = 1,
+ /// Only the image is displayed.
+ kImageOnly = 2,
+ /// Only the text is displayed.
+ kTextOnly = 3
+};
+
+
+
+struct RectOffset {
+ DECLARE_SERIALIZE (RectOffset)
+
+ int left, right, top, bottom;
+
+ RectOffset () { left = right = top = bottom = 0; }
+ RectOffset (const RectOffset &other)
+ {
+ left = other.left;
+ right = other.right;
+ top = other.top;
+ bottom = other.bottom;
+ }
+
+ int GetHorizontal () { return left + right; }
+ int GetVertical () { return top + bottom; }
+
+ Rectf Add (const Rectf &r) const
+ {
+ return MinMaxRect (r.x - left, r.y - top, r.GetRight() + right, r.GetBottom() + bottom);
+ }
+
+ Vector2f Size () const {
+ return Vector2f (left + right, top + bottom);
+ }
+ Rectf Remove (const Rectf &r) const
+ {
+ return MinMaxRect (r.x + left, r.y + top, r.GetRight() - right, r.GetBottom() - bottom);
+ }
+};
+
+template<class TransferFunc>
+void RectOffset::Transfer (TransferFunc& transfer)
+{
+ transfer.Transfer (left, "m_Left");
+ transfer.Transfer (right, "m_Right");
+ transfer.Transfer (top, "m_Top");
+ transfer.Transfer (bottom, "m_Bottom");
+}
+
+class GUIStyle {
+ public:
+ DECLARE_SERIALIZE (GUIStyle);
+ GUIStyle ();
+ GUIStyle (const GUIStyle &other);
+ UnityStr m_Name;
+
+ GUIStyleState m_Normal;
+ GUIStyleState m_Hover;
+ GUIStyleState m_Active;
+ GUIStyleState m_Focused;
+ GUIStyleState m_OnNormal;
+ GUIStyleState m_OnHover;
+ GUIStyleState m_OnActive;
+ GUIStyleState m_OnFocused;
+
+ /// Border of the background images
+ RectOffset m_Border;
+
+ /// Spacing between this element and ones next to it
+ RectOffset m_Margin;
+
+ /// Distance from outer edge to contents
+ RectOffset m_Padding;
+
+ /// Extra size to use for the background images.
+ RectOffset m_Overflow;
+
+ /// The font to use. If not set, the font is read from the main GUISkin
+ PPtr<Font> m_Font;
+
+ /// Text alignment.
+ int m_Alignment; ///< enum { Upper Left = 0, Upper Center = 1, Upper Right = 2, Middle Left = 3, Middle Center = 4, Middle Right = 5, Lower Left = 6, Lower Center = 7, Lower Right = 8 } How is the content placed inside the control.
+
+ /// Word wrap the text?
+ bool m_WordWrap;
+
+ /// Use HTML-style markup
+ bool m_RichText;
+
+ /// Clipping mode to use.
+ int m_Clipping; ///< enum { Overflow = 0, Clip = 1 } What happens with content that goes outside the control
+
+ /// How image and text is combined.
+ int m_ImagePosition; ///< enum { Image Left = 0, Image Above = 1, Image Only = 2, Text Only = 3 } How text and image is placed in relation to each other.
+
+ /// Pixel offset to apply to the content of this GUIstyle.
+ Vector2f m_ContentOffset;
+
+ /// Clip offset
+ Vector2f m_ClipOffset;
+
+ float m_FixedWidth; ///< If non-0, that axis is always draw at the specified size.
+ float m_FixedHeight; ///< If non-0, that axis is always draw at the specified size.
+
+ /// The font size to use. Set to 0 to use default font size. Only applicable for dynamic fonts.
+ int m_FontSize;
+
+ /// The font style to use. Only applicable for dynamic fonts.
+ int m_FontStyle; ///< enum { Normal = 0, Bold = 1, Italic = 2, Bold and Italic = 3 } Only applicable for dynamic fonts.
+
+ bool m_StretchWidth, m_StretchHeight;
+
+ /// Draw this GUI style
+ /// screenRect: Rectangle for the border
+ /// content: The text/image to stuff inside
+ /// isHover: Is the mouse over the element
+ /// isActive: Does the element have keyboard focus
+ /// on: Is the element on (as in a togglebutton)
+ void Draw (GUIState &state, const Rectf &screenRect, GUIContent &content, bool isHover, bool isActive, bool on, bool hasKeyboardFocus) const ;
+
+ /// Draw this GUI style
+ /// screenRect: Rectangle for the border
+ /// content: The text/image to stuff inside
+ /// id: The controlID mouse of the element
+ /// isActive: Does the element have keyboard focus
+ /// on: Is the element on (as in a togglebutton)
+ void Draw (GUIState &state, const Rectf &screenRect, GUIContent &content, int controlID, bool on) const ;
+
+
+ /// screenRect: Rectangle for the border
+ /// content: The text/image to stuff inside
+ /// isHover: Is the mouse over the element
+ /// isActive: Does the element have keyboard focus
+ /// on: Is the element on (as in a togglebutton)
+ /// cursorFirst, last Where is the text selection
+ void DrawWithTextSelection (GUIState &state, const Rectf &screenRect, GUIContent &content, bool isHover, bool isActive, bool on, bool hasKeyboardFocus, bool drawSelectionAsComposition, int cursorFirst, int cursorLast, const ColorRGBAf &cursorColor, const ColorRGBAf &selectionColor) const;
+
+ /// screenRect: Rectangle for the border
+ /// text/image: The text/image to stuff inside
+ /// position Where is the text selection
+ void DrawCursor(GUIState &state, const Rectf &screenRect, GUIContent &content, int position, const ColorRGBAf &cursorColor) const;
+
+ /// Calculate the min & max widths of this element to correctly render the content
+ void CalcMinMaxWidth (GUIContent &content, float *minWidth, float *maxWidth) const;
+ /// Calculate the height of a component given a specific width
+ float CalcHeight (GUIContent &content, float width) const;
+ /// Calculate the size
+ Vector2f CalcSize (GUIContent &content) const;
+ /// Get the position of a specific character (used for finding out where to draw the cursor)
+ Vector2f GetCursorPixelPosition (const Rectf &screenRect, GUIContent &content, int cursorStringIndex) const;
+ /// Get the index of a given pixel position (used for finding out where in the string of a textfield the user clicked)
+ int GetCursorStringIndex (const Rectf &screenRect, GUIContent &content, const Vector2f &cursorPixelPosition) const;
+
+ /// Get the height of one line, in pixels...
+ float GetLineHeight () const;
+
+ /// returns number of characters that can fit within width, returns -1 if fails to shorten string
+ int GetNumCharactersThatFitWithinWidth (const UTF16String &text, float width) const;
+
+ /// Set the default font (used by GUISkin)
+ static void SetDefaultFont (Font *font);
+ static Font*GetDefaultFont ();
+
+ static Texture2D* GetClipTexture ();
+
+ Font& GetCurrentFont () const;
+ static Font &GetBuiltinFont ();
+
+ /// Set a specific style state. Used when changing style states from mono
+ void SetStyleState (int stateIndex, ColorRGBAf textColor, Texture2D *background);
+
+ // Calculate where text and image go inside screenRect in order to fit imageSize and textSize
+ static void CalcContentRects (const Rectf &contentRect, Vector2f imageSize, Vector2f textSize, Rectf &imageRect, Rectf &textRect, float &width, float &height, int imagePosition, int alignment, Vector2f contentOffset);
+
+ // Used by OptimizedGUIBlock
+ static Rectf GetGUIClipRect ();
+ static void SetGUIClipRect (const Rectf& rect);
+
+ static void SetMouseTooltip (GUIState& state, const UTF16String& tooltip, const Rectf& screenRect);
+ private:
+
+ /// Figure out which GUIStyle to use
+ const GUIStyleState *GetGUIStyleState (GUIState &state, bool isHover, bool isActive, bool on, bool hasKeyboardFocus) const;
+ /// Draw the background GUIStyle without any contents
+ void DrawBackground (GUIState &state, const Rectf &screenRect, const GUIStyleState *gss) const;
+
+
+ /// Draw the contents
+ void DrawContent (GUIState &state, const Rectf &screenRect, GUIContent &content, const GUIStyleState *gss) const;
+ /// Draw the text selection highlight.
+ void DrawTextSelection (GUIState &state, const Rectf &screenRect, GUIContent &content, int first, int last, const ColorRGBAf &cursorColor, const ColorRGBAf &selectionColor) const;
+ void DrawTextUnderline (GUIState &state, const Rectf &screenRect, GUIContent &content, int first, int last, const GUIStyleState *gss) const;
+
+ // set up all the static text-rendering vars from this settings.
+ void RenderText (const Rectf &screenRect, TextMeshGenerator2 &tmgen, ColorRGBAf color) const;
+
+ TextMeshGenerator2 *GetGenerator (const Rectf &screenRect, GUIContent &content, ColorRGBA32 color) const;
+ TextMeshGenerator2 *GetGenerator (const Rectf &screenRect, GUIContent &content) const;
+
+ // Clamp a screenRect to be set to fixedWidth & height
+ Rectf ClampRect (const Rectf &screenRect) const;
+
+};
+template<class TransferFunc>
+void GUIStyle::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (m_Name);
+ transfer.Align ();
+ TRANSFER (m_Normal);
+ TRANSFER (m_Hover);
+ TRANSFER (m_Active);
+ TRANSFER (m_Focused);
+ TRANSFER (m_OnNormal);
+ TRANSFER (m_OnHover);
+ TRANSFER (m_OnActive);
+ TRANSFER (m_OnFocused);
+
+ TRANSFER (m_Border);
+ TRANSFER (m_Margin);
+ TRANSFER (m_Padding);
+ TRANSFER (m_Overflow);
+ TRANSFER (m_Font);
+ TRANSFER (m_FontSize);
+ TRANSFER (m_FontStyle);
+ TRANSFER (m_Alignment);
+ TRANSFER (m_WordWrap);
+ TRANSFER (m_RichText);
+ transfer.Align();
+ transfer.Transfer (m_Clipping, "m_TextClipping");
+ TRANSFER (m_ImagePosition);
+ TRANSFER (m_ContentOffset);
+ TRANSFER (m_FixedWidth);
+ TRANSFER (m_FixedHeight);
+ TRANSFER (m_StretchWidth);
+ TRANSFER (m_StretchHeight);
+ transfer.Align();
+};
+
+Material* GetGUIBlitMaterial ();
+Material* GetGUIBlendMaterial ();
+
+void InitializeGUIClipTexture();
+
+// skin:
+// Game = 0
+// Light Skin = 1
+// Dark Skin = 2
+MonoBehaviour* GetBuiltinSkin (int skin);
+// skinMode:
+// Game = 0
+// Editor = 1
+MonoBehaviour* GetDefaultSkin (int skinMode);
+
+#endif
diff --git a/Runtime/IMGUI/GUITest.cpp b/Runtime/IMGUI/GUITest.cpp
new file mode 100644
index 0000000..2214097
--- /dev/null
+++ b/Runtime/IMGUI/GUITest.cpp
@@ -0,0 +1,209 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+// Disabled GUITests for lack of acceptable framework for native tests hitting scripting invocations (GetControlID). Rene is on the case!
+#if 0
+//#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/Misc/InputEvent.h"
+#include "Runtime/IMGUI/IDList.h"
+
+static ObjectGUIState* gObjectGUIState;
+
+// Set up a clean GUIState for testing purposes. Delete it with DeleteTestGUIState
+static GUIState &MakeTestGUIState ()
+{
+ GUIState *state = new GUIState ();
+ state->m_EternalGUIState = new EternalGUIState ();
+ state->m_CurrentEvent = new InputEvent();
+ state->m_CurrentEvent->Init();
+ gObjectGUIState = new ObjectGUIState();
+ return *state;
+}
+
+static void DeleteTestGUIState (GUIState &state)
+{
+ delete gObjectGUIState;
+ delete &state;
+}
+
+static void BeginOnGUI (GUIState &state, InputEvent evt)
+{
+ *state.m_CurrentEvent = evt;
+ state.BeginOnGUI (*gObjectGUIState);
+}
+
+static void EndOnGUI (GUIState &state)
+{
+ state.EndOnGUI ();
+}
+
+static InputEvent MakeLayoutEvent ()
+{
+ InputEvent e;
+ e.type = InputEvent::kLayout;
+ return e;
+}
+
+static InputEvent MakeRepaintEvent ()
+{
+ InputEvent e;
+ e.type = InputEvent::kRepaint;
+ return e;
+}
+
+static InputEvent MakeKeyDownEvent (char c)
+{
+ InputEvent e;
+ e.type = InputEvent::kKeyDown;
+ e.character = c;
+ return e;
+}
+
+SUITE ( GUITests )
+{
+TEST (GUITests_IDListGeneration)
+{
+ GUIState &state = MakeTestGUIState ();
+ state.BeginFrame ();
+ BeginOnGUI (state, MakeLayoutEvent ());
+ int v1 = state.GetControlID (1, kPassive);
+ int v2 = state.GetControlID (1, kPassive);
+ int v3 = state.GetControlID (2, kPassive);
+ EndOnGUI (state);
+
+ // Check we get the same IDs next event
+ BeginOnGUI (state, MakeRepaintEvent ());
+ CHECK_EQUAL (v1, state.GetControlID (1, kPassive));
+ CHECK_EQUAL (v2, state.GetControlID (1, kPassive));
+ CHECK_EQUAL (v3, state.GetControlID (2, kPassive));
+ EndOnGUI (state);
+
+ //check we correctly handle something going away.
+ BeginOnGUI (state, MakeRepaintEvent ());
+ CHECK_EQUAL (v1, state.GetControlID (1, kPassive));
+ CHECK_EQUAL (v3, state.GetControlID (2, kPassive));
+ EndOnGUI (state);
+
+ state.EndFrame ();
+ DeleteTestGUIState(state);
+}
+
+TEST (GUITests_IDListTabFinding)
+{
+ GUIState &state = MakeTestGUIState ();
+ state.BeginFrame ();
+
+ // Init & set up keycontrol
+ BeginOnGUI (state, MakeLayoutEvent ());
+ int v1 = state.GetControlID (1, kKeyboard);
+ state.GetControlID (1, kPassive);
+ int v2 = state.GetControlID (1, kKeyboard);
+ int v3 = state.GetControlID (2, kKeyboard);
+ EndOnGUI (state);
+ state.m_MultiFrameGUIState.m_KeyboardControl = v2;
+
+ // Run again to make sure we have the values - they are only recorded on keydown events
+ BeginOnGUI (state, MakeKeyDownEvent ('\t'));
+ state.GetControlID (1, kKeyboard);
+ state.GetControlID (1, kPassive);
+ state.GetControlID (1, kKeyboard);
+ state.GetControlID (2, kKeyboard);
+
+ CHECK (state.m_ObjectGUIState->m_IDList.HasKeyboardControl());
+ CHECK_EQUAL (v1, state.m_ObjectGUIState->m_IDList.GetPreviousKeyboardControlID());
+ CHECK_EQUAL (v3, state.m_ObjectGUIState->m_IDList.GetNextKeyboardControlID());
+ CHECK_EQUAL (v1, state.m_ObjectGUIState->m_IDList.GetFirstKeyboardControlID());
+ CHECK_EQUAL (v3, state.m_ObjectGUIState->m_IDList.GetLastKeyboardControlID());
+
+ EndOnGUI (state);
+ state.EndFrame ();
+ DeleteTestGUIState(state);
+}
+
+TEST (GUITests_IDListNamedKeyControls)
+{
+ GUIState &state = MakeTestGUIState ();
+ state.BeginFrame ();
+
+ BeginOnGUI (state, MakeLayoutEvent ());
+ state.GetControlID (1, kKeyboard);
+ state.SetNameOfNextKeyboardControl ("v1");
+ int v1 = state.GetControlID (1, kKeyboard);
+ state.SetNameOfNextKeyboardControl ("v2");
+ state.GetControlID (1, kPassive);
+ int v2 = state.GetControlID (1, kKeyboard);
+ state.GetControlID (2, kKeyboard);
+ state.SetNameOfNextKeyboardControl ("v3fake");
+ EndOnGUI (state);
+
+ // Run event chain so we can move to past events
+ BeginOnGUI (state, MakeRepaintEvent ());
+ state.GetControlID (1, kKeyboard);
+ state.SetNameOfNextKeyboardControl ("v1");
+ state.GetControlID (1, kKeyboard);
+ state.SetNameOfNextKeyboardControl ("v2");
+ state.GetControlID (1, kPassive);
+ state.GetControlID (1, kKeyboard);
+ state.GetControlID (2, kKeyboard);
+ state.SetNameOfNextKeyboardControl ("v3fake");
+
+ CHECK_EQUAL (v1, state.GetIDOfNamedControl ("v1"));
+ CHECK_EQUAL (v2, state.GetIDOfNamedControl ("v2"));
+ EndOnGUI (state);
+
+ state.EndFrame ();
+ DeleteTestGUIState(state);
+}
+
+TEST (GUITests_IDListNativeGetsKBControl)
+{
+ GUIState &state = MakeTestGUIState ();
+ state.BeginFrame ();
+
+ // First pass: Layout... get all the control ID's
+ BeginOnGUI (state, MakeLayoutEvent ());
+
+ // padding control at start
+ int id1 = state.GetControlID (1, kNative);
+
+ // first named control
+ state.SetNameOfNextKeyboardControl ("named1");
+ int id2 = state.GetControlID (1, kNative);
+
+ // second named control... passive should be ignored!
+ state.SetNameOfNextKeyboardControl ("named2");
+ int id3 = state.GetControlID (1, kPassive);
+ int id4 = state.GetControlID (1, kNative);
+
+ // extra trailing controls
+ int id5 = state.GetControlID (1, kNative);
+
+ // check the named ID's
+ CHECK_EQUAL (id2, state.GetIDOfNamedControl ("named1"));
+ CHECK_EQUAL (id4, state.GetIDOfNamedControl ("named2"));
+ EndOnGUI (state);
+
+ // Now simulate a repaint... the control ID's should be the same as before!
+ BeginOnGUI (state, MakeRepaintEvent ());
+ CHECK_EQUAL (id1, state.GetControlID (1, kNative));
+ state.SetNameOfNextKeyboardControl ("v1");
+ CHECK_EQUAL (id2, state.GetControlID (1, kNative));
+ state.SetNameOfNextKeyboardControl ("v2");
+ CHECK_EQUAL (id3, state.GetControlID (1, kPassive));
+ CHECK_EQUAL (id4, state.GetControlID (1, kNative));
+ CHECK_EQUAL (id5, state.GetControlID (1, kNative));
+
+ // also check that the named events STILL have the same ID's
+ CHECK_EQUAL (id2, state.GetIDOfNamedControl ("named1"));
+ CHECK_EQUAL (id4, state.GetIDOfNamedControl ("named2"));
+ EndOnGUI (state);
+
+ state.EndFrame ();
+ DeleteTestGUIState(state);
+}
+}
+#endif
diff --git a/Runtime/IMGUI/GUIToggle.cpp b/Runtime/IMGUI/GUIToggle.cpp
new file mode 100644
index 0000000..a8fde5a
--- /dev/null
+++ b/Runtime/IMGUI/GUIToggle.cpp
@@ -0,0 +1,76 @@
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/GUIToggle.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+
+namespace IMGUI
+{
+
+static const int kGUIToggleHash = -1784436876;
+
+bool GUIToggle (GUIState &state, const Rectf &position, bool value, GUIContent &content, GUIStyle &style, int id)
+{
+ InputEvent &evt (*state.m_CurrentEvent);
+ switch (GetEventTypeForControl (state, evt, id))
+ {
+ case InputEvent::kMouseDown:
+ // If the mouse is inside the button, we say that we're the hot control
+#if ENABLE_NEW_EVENT_SYSTEM
+ if (position.Contains (evt.touch.pos))
+#else
+ if (position.Contains (evt.mousePosition))
+#endif
+ {
+ GrabMouseControl (state, id);
+ evt.Use ();
+ }
+ break;
+ case InputEvent::kKeyDown:
+ if (evt.character == 32 && state.m_MultiFrameGUIState.m_KeyboardControl == id)
+ {
+ evt.Use ();
+ state.m_OnGUIState.m_Changed = true;
+ return !value;
+ }
+ break;
+ case InputEvent::kMouseUp:
+ if (HasMouseControl (state, id))
+ {
+ ReleaseMouseControl (state);
+
+ // If we got the mousedown, the mouseup is ours as well
+ // (no matter if the click was in the button or not)
+ evt.Use ();
+
+ // toggle the passed-in value if the mouse was over the button & return true
+#if ENABLE_NEW_EVENT_SYSTEM
+ if (position.Contains (evt.touch.pos))
+#else
+ if (position.Contains (evt.mousePosition))
+#endif
+ {
+ state.m_OnGUIState.m_Changed = true;
+ return !value;
+ }
+ }
+ break;
+ case InputEvent::kMouseDrag:
+ if (HasMouseControl (state, id))
+ evt.Use ();
+ break;
+
+ case InputEvent::kRepaint:
+ style.Draw (state, position, content, id, value);
+ break;
+ }
+ return value;
+}
+
+bool GUIToggle (GUIState &state, const Rectf &position, bool value, GUIContent &content, GUIStyle &style)
+{
+ int id = GetControlID (state, kGUIToggleHash, kNative, position);
+ return GUIToggle (state, position, value, content, style, id);
+}
+
+} \ No newline at end of file
diff --git a/Runtime/IMGUI/GUIToggle.h b/Runtime/IMGUI/GUIToggle.h
new file mode 100644
index 0000000..3f9d619
--- /dev/null
+++ b/Runtime/IMGUI/GUIToggle.h
@@ -0,0 +1,16 @@
+#ifndef GUITOGGLE_H
+#define GUITOGGLE_H
+
+#include "Runtime/Math/Rect.h"
+
+struct GUIState;
+struct GUIContent;
+class GUIStyle;
+
+namespace IMGUI
+{
+ bool GUIToggle (GUIState &state, const Rectf &position, bool value, GUIContent &content, GUIStyle &style, int id);
+ bool GUIToggle (GUIState &state, const Rectf &position, bool value, GUIContent &content, GUIStyle &style);
+}
+
+#endif
diff --git a/Runtime/IMGUI/GUIWindows.cpp b/Runtime/IMGUI/GUIWindows.cpp
new file mode 100644
index 0000000..30a3f9b
--- /dev/null
+++ b/Runtime/IMGUI/GUIWindows.cpp
@@ -0,0 +1,748 @@
+#include "UnityPrefix.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+#if ENABLE_UNITYGUI
+#include "Runtime/IMGUI/GUIWindows.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/CommonScriptingClasses.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+
+
+#include <algorithm> // for std::sort
+namespace IMGUI
+{
+ static Vector2f s_DragStartPos (0,0); // Start of the drag (mousePosition)
+ static Vector2f s_DragStartSize (0,0); // Value at start of drag.
+
+ GUIWindow::GUIWindow ()
+ {
+ m_Delegate = m_Skin = 0;
+ m_Moved = m_ForceRect = false;
+ m_ID = 0;
+ m_Style = 0;
+ }
+
+ GUIWindow::~GUIWindow ()
+ {
+ ReleaseScriptingObjects ();
+ }
+
+
+ void GUIWindow::ReleaseScriptingObjects ()
+ {
+ if (m_Delegate)
+ {
+ scripting_gchandle_free (m_Delegate);
+ m_Delegate = 0;
+ }
+ if (m_Skin)
+ {
+ scripting_gchandle_free (m_Skin);
+ m_Skin = 0;
+ }
+ if (m_Style)
+ {
+ scripting_gchandle_free (m_Style);
+ m_Style = 0;
+ }
+ }
+
+ void GUIWindow::OnGUI (GUIState& state)
+ {
+ InputEvent& evt (*state.m_CurrentEvent);
+ // Set up the state that was recorded for this window
+ state.m_OnGUIState.m_Color = m_Color;
+ state.m_OnGUIState.m_BackgroundColor = m_BackgroundColor;
+ state.m_OnGUIState.m_ContentColor = m_ContentColor;
+ state.m_OnGUIState.m_Enabled = m_Enabled;
+ state.m_CanvasGUIState.m_GUIClipState.SetMatrix (evt, m_Matrix);
+ state.m_MultiFrameGUIState.m_Windows->m_CurrentWindow = this;
+
+ // Block OnHover calls into the scene if the window contains the mouse
+#if ENABLE_NEW_EVENT_SYSTEM
+ if (evt.type == InputEvent::kRepaint && m_Position.Contains (evt.touch.pos))
+#else
+ if (evt.type == InputEvent::kRepaint && m_Position.Contains (evt.mousePosition))
+#endif
+ state.m_CanvasGUIState.m_IsMouseUsed = true;
+
+ // Disable drawing keyboard focus if window doesn't have focus.
+ int hadShowKeyboardControl = state.m_OnGUIState.m_ShowKeyboardControl;
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ state.m_OnGUIState.m_ShowKeyboardControl &= winState->m_FocusedWindow == m_ID;
+
+ // If it's a repaint event, draw the background
+ ScriptingObjectPtr style = scripting_gchandle_get_target (m_Style);
+ if (style && evt.type == InputEvent::kRepaint)
+ {
+ GUIStyle* _style = ScriptingObjectWithIntPtrField<GUIStyle> (style).GetPtr();
+#if ENABLE_NEW_EVENT_SYSTEM
+ _style->Draw (state, m_Position, m_Title, m_Position.Contains (evt.touch.pos), false, state.m_MultiFrameGUIState.m_Windows->m_FocusedWindow == m_ID, false);
+#else
+ _style->Draw (state, m_Position, m_Title, m_Position.Contains (evt.mousePosition), false, state.m_MultiFrameGUIState.m_Windows->m_FocusedWindow == m_ID, false);
+#endif
+ }
+
+ state.m_CanvasGUIState.m_GUIClipState.Push (*state.m_CurrentEvent, m_Position, Vector2f::zero, Vector2f::zero, false);
+ ObjectGUIState* old = state.m_ObjectGUIState;
+ state.BeginOnGUI (m_ObjectGUIState);
+
+ // No exception handling here on purpose.
+ ScriptingInvocation invocation(MONO_COMMON.callGUIWindowDelegate);
+ invocation.AddObject(scripting_gchandle_get_target (m_Delegate));
+ invocation.AddInt(m_ID);
+ invocation.AddObject(scripting_gchandle_get_target (m_Skin));
+ invocation.AddInt((int)m_ForceRect);
+ invocation.AddFloat(m_Position.width);
+ invocation.AddFloat(m_Position.height);
+ invocation.AddObject(style);
+
+ state.m_OnGUIState.m_ShowKeyboardControl = winState->m_FocusedWindow == m_ID;
+
+ // we need to catch a log our own exceptions to properly handle ExitGUIException
+ ScriptingExceptionPtr exception = NULL;
+ invocation.logException = false;
+
+ invocation.Invoke (&exception);
+
+ if (exception)
+ {
+ // TODO: Kill GUI all the way down to the MonoBehaviour
+#if ENABLE_MONO
+ void* excparams[] = {exception};
+ MonoObject* res = CallStaticMonoMethod("GUIUtility", "EndGUIFromException", excparams);
+ if (!MonoObjectToBool(res))
+ ::Scripting::LogException(exception, 0);
+#endif
+ }
+
+ state.EndOnGUI ();
+ state.m_ObjectGUIState = old;
+ state.m_CanvasGUIState.m_GUIClipState.Pop (evt);
+ state.m_MultiFrameGUIState.m_Windows->m_CurrentWindow = NULL;
+
+ // make sure that the rest of the script shows keyboard focus
+ state.m_OnGUIState.m_ShowKeyboardControl = hadShowKeyboardControl;
+ }
+
+ Rectf DoWindow (GUIState& state, int id, const Rectf &clientRect, ScriptingObjectPtr delegate, GUIContent& title, ScriptingObjectPtr style, ScriptingObjectPtr guiSkin, bool forceRectOnLayout, bool isModal)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ if (winState == NULL)
+ state.m_MultiFrameGUIState.m_Windows = winState = new GUIWindowState ();
+
+ GUIWindow* win = winState->GetWindow (id);
+ if (!win)
+ {
+ if(isModal && winState->m_ModalWindow != NULL)
+ {
+ DebugStringToFile ("You cannot show two modal windows at once", 0, __FILE__, __LINE__, kError);
+ return clientRect;
+ }
+ win = new GUIWindow();
+ win->m_ID = id;
+ win->m_Depth = -1;
+
+ if(isModal)
+ {
+ winState->m_ModalWindow = win;
+ }
+ else
+ {
+ winState->m_WindowList.push_back(win);
+ winState->m_LayersChanged = true;
+ }
+ }
+
+ if(isModal)
+ {
+ if(winState->m_ModalWindow == NULL)
+ {
+ winState->m_ModalWindow = win;
+
+ // If window is in the window list, remove it.
+ GUIWindowState::WindowList::iterator i = std::find(winState->m_WindowList.begin(),
+ winState->m_WindowList.end(),
+ win);
+ if(i != winState->m_WindowList.end())
+ {
+ winState->m_WindowList.erase(i);
+ winState->m_LayersChanged = true;
+ }
+ }
+ else if(winState->m_ModalWindow != win)
+ {
+ // This can happen if you already have a modal window open, and attempt
+ // to show an already-created window as a modal window.
+ DebugStringToFile ("Attempting to show modal windows at once; the newer windows will not be modal", 0, __FILE__, __LINE__, kError);
+ }
+ }
+
+ if (!win->m_Moved)
+ win->m_Position = clientRect;
+ else
+ win->m_Moved = false;
+
+ win->m_Title = title;
+
+ win->ReleaseScriptingObjects ();
+ win->m_Style = scripting_gchandle_new (style);
+ win->m_Delegate = scripting_gchandle_new (delegate);
+ win->m_Skin = scripting_gchandle_new (guiSkin);
+
+ win->m_Used = true;
+ win->m_Enabled = state.m_OnGUIState.m_Enabled;
+ win->m_Color = state.m_OnGUIState.m_Color;
+ win->m_BackgroundColor = state.m_OnGUIState.m_BackgroundColor;
+ win->m_ContentColor = state.m_OnGUIState.m_ContentColor;
+ win->m_Matrix = state.m_CanvasGUIState.m_GUIClipState.GetMatrix();
+ win->m_ForceRect = forceRectOnLayout;
+
+ #if !GAMERELEASE
+ if (state.m_MultiFrameGUIState.m_Windows->m_CurrentWindow)
+ ErrorString("GUI Error: You called GUI.Window inside a another window's function. Ensure to call it in a OnGUI code path.");
+ #endif
+
+ return win->m_Position;
+
+ }
+
+ void DragWindow (GUIState &state, const Rectf &position)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ GUIWindow* win = winState ? winState->m_CurrentWindow : NULL;
+ if (win == NULL)
+ {
+ ErrorString ("Dragwindow can only be called within a window callback");
+ return;
+ }
+
+ int id = IMGUI::GetControlID (state, 0, kPassive);
+
+ InputEvent& evt (*state.m_CurrentEvent);
+
+ switch (GetEventTypeForControl (state, evt, id)) {
+ case InputEvent::kMouseDown:
+ // If the mouse is inside the button, we say that we're the hot control
+#if ENABLE_NEW_EVENT_SYSTEM
+ if (position.Contains (evt.touch.pos))
+#else
+ if (position.Contains (evt.mousePosition))
+#endif
+ {
+ GrabMouseControl (state, id);
+ evt.Use ();
+ //Matrix4x4f mat = win->m_Matrix;
+ Vector2f mouseAbs = state.m_CanvasGUIState.m_GUIClipState.GetAbsoluteMousePosition();
+ Vector3f windowAbs = win->m_Matrix.MultiplyPoint3 (Vector3f (win->m_Position.x, win->m_Position.y, 0));
+ s_DragStartPos = mouseAbs - Vector2f (windowAbs.x, windowAbs.y);
+ s_DragStartSize = Vector2f (win->m_Position.width, win->m_Position.height);
+ }
+ break;
+ case InputEvent::kMouseUp:
+ if (GetHotControl (state) == id)
+ {
+ ReleaseMouseControl (state);
+ evt.Use ();
+ }
+ break;
+ case InputEvent::kMouseDrag:
+ if (GetHotControl (state) == id)
+ {
+ Matrix4x4f mat;
+ Matrix4x4f::Invert_Full (win->m_Matrix, mat);
+ Vector2f mouseAbs = state.m_CanvasGUIState.m_GUIClipState.GetAbsoluteMousePosition();
+
+ Vector3f deltaPos (mouseAbs.x - s_DragStartPos.x, mouseAbs.y - s_DragStartPos.y, 0);
+ deltaPos = mat.MultiplyPoint3 (deltaPos);
+ win->m_Position = Rectf (deltaPos.x, deltaPos.y, s_DragStartSize.x, s_DragStartSize.y);
+
+ win->m_Moved = true;
+ evt.Use ();
+ }
+ break;
+ }
+
+ }
+
+ struct GUIStatePropertiesCache
+ {
+ Matrix4x4f mat;
+ ColorRGBAf color;
+ ColorRGBAf contentColor;
+ ColorRGBAf backgroundColor;
+ bool enabled;
+ };
+
+ void CacheGUIStateProperties (GUIState &state, GUIStatePropertiesCache &cache)
+ {
+ // Cache some GUIState properties to restore after we're done doing windows
+ cache.mat = state.m_CanvasGUIState.m_GUIClipState.GetMatrix();
+ cache.color = state.m_OnGUIState.m_Color;
+ cache.contentColor = state.m_OnGUIState.m_ContentColor;
+ cache.backgroundColor = state.m_OnGUIState.m_BackgroundColor;
+ cache.enabled = state.m_OnGUIState.m_Enabled;
+ }
+
+ void RestoreGUIStateProperties (GUIState &state, InputEvent &evt, GUIStatePropertiesCache &cache)
+ {
+ // Restore previous GUIState properties
+ state.m_CanvasGUIState.m_GUIClipState.SetMatrix (evt, cache.mat);
+ state.m_OnGUIState.m_Color = cache.color;
+ state.m_OnGUIState.m_ContentColor = cache.contentColor;
+ state.m_OnGUIState.m_BackgroundColor = cache.backgroundColor;
+ state.m_OnGUIState.m_Enabled = cache.enabled;
+ }
+
+ void BeginWindows (GUIState &state, bool setupClipping, bool ignoreModalWindow)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ InputEvent& evt (*state.m_CurrentEvent);
+ if (winState == NULL)
+ return;
+
+ GUIStatePropertiesCache oldProperties;
+ CacheGUIStateProperties (state, oldProperties);
+
+ if (setupClipping)
+ state.m_CanvasGUIState.m_GUIClipState.BeginOnGUI (evt);
+
+ if (winState->m_LayersChanged)
+ winState->SortWindows();
+
+ // The window we want to pass the event on to (mouse & key mainly)
+ GUIWindow* win = NULL;
+
+ switch (evt.type) {
+ case InputEvent::kLayout:
+ // Before we process any events... Mark all windows as unused (we mark them as used when doing the layout event for all scripts)
+ for (GUIWindowState::WindowList::iterator i = winState->m_WindowList.begin(); i != winState->m_WindowList.end(); i++)
+ (*i)->m_Used = false;
+
+ if(!ignoreModalWindow && winState->m_ModalWindow != NULL)
+ winState->m_ModalWindow->m_Used = false;
+ break;
+
+ // Dragging events go to the window UNDER the mouse
+ case InputEvent::kDragUpdated:
+ case InputEvent::kDragPerform:
+ case InputEvent::kDragExited:
+ if (!ignoreModalWindow && winState->m_ModalWindow != NULL)
+ win = winState->m_ModalWindow;
+ else
+ win = winState->FindWindowUnderMouse (state);
+ break;
+
+ // If we have a hot control, we send mouseUp/mouseDrag event to the active window.
+ // If not, we send it to window under mouse.
+ case InputEvent::kMouseUp:
+ case InputEvent::kMouseDrag:
+ case InputEvent::kMouseMove:
+ if (!ignoreModalWindow && winState->m_ModalWindow != NULL)
+ win = winState->m_ModalWindow;
+ else if (GetHotControl (state) == 0)
+ win = winState->FindWindowUnderMouse (state);
+ else
+ win = winState->GetWindow (winState->m_FocusedWindow);
+ break;
+
+ // Scroll wheel goes to window under mouse
+ // TODO: Maybe not the same for windows
+ case InputEvent::kScrollWheel:
+ if (!ignoreModalWindow && winState->m_ModalWindow != NULL)
+ win = winState->m_ModalWindow;
+ else
+ win = winState->FindWindowUnderMouse (state);
+ break;
+ // mouse events should pick a specific window & bring that forwards...
+ case InputEvent::kMouseDown:
+ winState->m_FocusedWindow = -1;
+ if(!ignoreModalWindow && winState->m_ModalWindow != NULL)
+ win = winState->m_ModalWindow;
+ else
+ win = winState->FindWindowUnderMouse (state);
+
+ // If somebody got moved to front - we need to go over all windows and reset the window depth
+ if (win) {
+ win->m_Depth = -1;
+ winState->m_FocusedWindow = win->m_ID;
+ winState->SortWindows ();
+ }
+ break;
+ case InputEvent::kRepaint:
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_1_a1))
+ state.m_EternalGUIState->m_AllowHover = ((ignoreModalWindow || winState->m_ModalWindow == NULL) && winState->FindWindowUnderMouse (state) == NULL);
+ // We handle all repainting in EndWindows (so we can draw in reverse order on top of other stuff)
+ return;
+ default:
+ if(!ignoreModalWindow && winState->m_ModalWindow != NULL)
+ win = winState->m_ModalWindow;
+ else
+ win = winState->GetWindow (winState->m_FocusedWindow);
+ break;
+ }
+
+ // Pass the event on to the window
+ if (win != NULL && win->m_Delegate != 0)
+ {
+ win->OnGUI (state);
+
+ // Some events should not be passed down to the GUI or windows underneath the handled window
+ if(!ignoreModalWindow && winState->m_ModalWindow != NULL)
+ {
+ // If this is a scrollwheel or mousedown event, OR
+ // If this is a mouse move/drag or mouseup event AND we have no active control
+ // Ignore the event.
+ if(evt.type == InputEvent::kScrollWheel || evt.type == InputEvent::kMouseDown)
+ {
+ evt.type = InputEvent::kIgnore;
+ }
+ else if( (evt.type == InputEvent::kMouseMove
+ || evt.type == InputEvent::kMouseDrag
+ || evt.type == InputEvent::kMouseUp)
+ && IMGUI::GetHotControl (state) == 0)
+ {
+ evt.type = InputEvent::kIgnore;
+ }
+ }
+ }
+
+ RestoreGUIStateProperties (state, evt, oldProperties);
+
+ if (setupClipping)
+ state.m_CanvasGUIState.m_GUIClipState.EndOnGUI (*state.m_CurrentEvent);
+ }
+
+ void EndWindows (GUIState &state, bool ignoreModalWindow)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ if (winState == NULL)
+ return;
+
+ GUIStatePropertiesCache oldProperties;
+ CacheGUIStateProperties (state, oldProperties);
+
+ InputEvent& evt (*state.m_CurrentEvent);
+ switch (evt.type) {
+ case InputEvent::kLayout:
+ {
+ // Remove unused windows (they have to be marked as Used during the Layout event.
+ bool clearFocus = true;
+ for (int i = winState->m_WindowList.size(); i--;)
+ {
+ GUIWindow* win = winState->m_WindowList[i];
+ if (!win->m_Used)
+ {
+ delete win;
+ winState->m_WindowList.erase(winState->m_WindowList.begin() + i);
+ winState->m_LayersChanged = true;
+ } else {
+ if (win->m_ID == winState->m_FocusedWindow)
+ clearFocus = false;
+ }
+ }
+
+ if(!ignoreModalWindow && winState->m_ModalWindow != NULL && !winState->m_ModalWindow->m_Used)
+ {
+ delete winState->m_ModalWindow;
+ winState->m_ModalWindow = NULL;
+ }
+
+ if (clearFocus)
+ winState->m_FocusedWindow = -1;
+
+ if (winState->m_LayersChanged)
+ winState->SortWindows ();
+
+ // Always run modal windows first
+ if(!ignoreModalWindow && winState->m_ModalWindow != NULL)
+ winState->m_ModalWindow->OnGUI(state);
+
+ for (GUIWindowState::WindowList::iterator i = winState->m_WindowList.begin(); i != winState->m_WindowList.end(); i++)
+ {
+ // Send the layout event to the user's input code (also does the layouting from the C# delegate wrapper)
+ (*i)->OnGUI (state);
+ }
+ break;
+ }
+ case InputEvent::kRepaint:
+ GUIWindow* windowUnderMouse;
+
+ if(winState->m_ModalWindow != NULL)
+ windowUnderMouse = winState->m_ModalWindow;
+ else
+ windowUnderMouse = winState->FindWindowUnderMouse (state);
+
+ for (int i = winState->m_WindowList.size(); i--;)
+ {
+ GUIWindow* win = winState->m_WindowList[i];
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_1_a1))
+ state.m_EternalGUIState->m_AllowHover = (win == windowUnderMouse && winState->m_ModalWindow == NULL);
+ win->OnGUI (state);
+ }
+
+ if(ignoreModalWindow || winState->m_ModalWindow == NULL)
+ {
+ // Re-enable hovering for always-on-top normal GUIs when there's no modal GUI
+ state.m_EternalGUIState->m_AllowHover = true;
+ }
+ else
+ {
+ // Disable hovering for the non-modal GUI when we have one.
+ // Repainting happens in RepaintModalWindow.
+ state.m_EternalGUIState->m_AllowHover = false;
+ }
+
+ break;
+ }
+
+ RestoreGUIStateProperties (state, evt, oldProperties);
+
+ // Release objects if we don't have a modal window. If we do, this will be done later.
+ if (evt.type != InputEvent::kLayout && (ignoreModalWindow || winState->m_ModalWindow == NULL))
+ winState->ReleaseScriptingObjects();
+ }
+
+ void RepaintModalWindow(GUIState& state)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ if (winState == NULL)
+ return;
+
+ GUIStatePropertiesCache oldProperties;
+ CacheGUIStateProperties (state, oldProperties);
+
+ InputEvent& evt (*state.m_CurrentEvent);
+ if(evt.type == InputEvent::kRepaint)
+ {
+ state.m_EternalGUIState->m_AllowHover = true;
+
+ // Always run modal windows last so they paint on top.
+ if(winState->m_ModalWindow != NULL)
+ winState->m_ModalWindow->OnGUI(state);
+ }
+
+ if(evt.type != InputEvent::kLayout)
+ {
+ winState->ReleaseScriptingObjects();
+ }
+ }
+
+ GUIWindow *GetFocusedWindow (GUIState &state)
+ {
+ if (state.m_MultiFrameGUIState.m_Windows)
+ return state.m_MultiFrameGUIState.m_Windows->GetWindow (state.m_MultiFrameGUIState.m_Windows->m_FocusedWindow);
+ return NULL;
+ }
+
+
+ GUIWindowState::GUIWindowState ()
+ {
+ m_FocusedWindow = -1;
+ m_LayersChanged = false;
+ m_CurrentWindow = NULL;
+ m_ModalWindow = NULL;
+ }
+
+
+ GUIWindowState::~GUIWindowState ()
+ {
+ for (GUIWindowState::WindowList::iterator i = m_WindowList.begin(); i != m_WindowList.end(); i++)
+ delete *i;
+
+ if(m_ModalWindow != NULL)
+ {
+ delete m_ModalWindow;
+ m_ModalWindow = NULL;
+ }
+ }
+
+ GUIWindow* GUIWindowState::GetWindow (int windowId)
+ {
+ for (GUIWindowState::WindowList::iterator i = m_WindowList.begin(); i != m_WindowList.end(); i++)
+ {
+ if ((*i)->m_ID == windowId)
+ return *i;
+ }
+
+ if(m_ModalWindow != NULL && m_ModalWindow->m_ID == windowId)
+ {
+ return m_ModalWindow;
+ }
+
+ return NULL;
+ }
+
+ static bool SortTwoWindows(const GUIWindow* a, const GUIWindow* b)
+ {
+ return a->m_Depth < b->m_Depth;
+ }
+
+ void GUIWindowState::SortWindows ()
+ {
+ std::sort (m_WindowList.begin(), m_WindowList.end(), SortTwoWindows);
+ for (int i = 0; i < m_WindowList.size(); i++)
+ m_WindowList[i]->m_Depth = i;
+ }
+
+
+ GUIWindow* GUIWindowState::FindWindowUnderMouse (GUIState &state)
+ {
+ InputEvent evt (*state.m_CurrentEvent);
+
+#if ENABLE_NEW_EVENT_SYSTEM
+ if(m_ModalWindow != NULL && m_ModalWindow->m_Position.Contains(evt.touch.pos))
+#else
+ if(m_ModalWindow != NULL && m_ModalWindow->m_Position.Contains(evt.mousePosition))
+#endif
+ {
+ return m_ModalWindow;
+ }
+
+ for (GUIWindowState::WindowList::iterator i = m_WindowList.begin(); i != m_WindowList.end(); i++)
+ {
+ state.m_CanvasGUIState.m_GUIClipState.SetMatrix (evt, (*i)->m_Matrix);
+#if ENABLE_NEW_EVENT_SYSTEM
+ if ((*i)->m_Position.Contains (evt.touch.pos))
+#else
+ if ((*i)->m_Position.Contains (evt.mousePosition))
+#endif
+ return *i;
+ }
+ return NULL;
+ }
+
+ void GUIWindowState::ReleaseScriptingObjects ()
+ {
+ for (WindowList::iterator i = m_WindowList.begin(); i != m_WindowList.end(); i++)
+ (*i)->ReleaseScriptingObjects ();
+
+ if(m_ModalWindow != NULL)
+ {
+ m_ModalWindow->ReleaseScriptingObjects();
+ }
+ }
+
+ void MoveWindowFromLayout (GUIState &state, int windowID, const Rectf &rect)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ AssertIf (!winState);
+
+ GUIWindow* win = winState->GetWindow(windowID);
+ if (win && win->m_Position != rect)
+ {
+ win->m_Position = rect;
+ win->m_Moved = true;
+ }
+ }
+
+ Rectf GetWindowRect (GUIState &state, int windowID)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ AssertIf (!winState);
+
+ GUIWindow* win = winState->GetWindow (windowID);
+ if (win)
+ return win->m_Position;
+ return Rectf (0,0,0,0);
+ }
+
+ Rectf GetWindowsBounds (GUIState &state)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ if (!winState)
+ return Rectf(0,0,0,0);
+
+ GUIWindowState::WindowList &windows = winState->m_WindowList;
+
+ Rectf bounds (std::numeric_limits<float>::max (), std::numeric_limits<float>::max (), -std::numeric_limits<float>::max (), -std::numeric_limits<float>::max ());
+ for (GUIWindowState::WindowList::const_iterator i = windows.begin (); i != windows.end (); i++)
+ {
+ Rectf& windowRect = (*i)->m_Position;
+
+ bounds.SetLeft (std::min (bounds.x, windowRect.x));
+ bounds.SetTop (std::min (bounds.y, windowRect.y));
+ bounds.SetRight (std::max (bounds.GetXMax (), windowRect.GetXMax ()));
+ bounds.SetBottom (std::max (bounds.GetYMax (), windowRect.GetYMax ()));
+ }
+
+ if(winState->m_ModalWindow != NULL)
+ {
+ Rectf& windowRect = winState->m_ModalWindow->m_Position;
+ bounds.SetLeft (std::min (bounds.x, windowRect.x));
+ bounds.SetTop (std::min (bounds.y, windowRect.y));
+ bounds.SetRight (std::max (bounds.GetXMax (), windowRect.GetXMax ()));
+ bounds.SetBottom (std::max (bounds.GetYMax (), windowRect.GetYMax ()));
+ }
+
+ return bounds;
+ }
+
+ void BringWindowToFront (GUIState &state, int windowID)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ if (!winState)
+ return;
+
+ // Modal windows are on top by definition.
+ if(winState->m_ModalWindow != NULL && winState->m_ModalWindow->m_ID == windowID)
+ return;
+
+ GUIWindow* win = winState->GetWindow (windowID);
+ if (win)
+ {
+ int minDepth = 0;
+ for (GUIWindowState::WindowList::iterator i = winState->m_WindowList.begin(); i != winState->m_WindowList.end(); i++)
+ {
+ if ((*i)->m_Depth < minDepth)
+ minDepth = (*i)->m_Depth;
+ }
+ win->m_Depth = minDepth - 1;
+ winState->m_LayersChanged = true;
+ }
+ }
+
+ void BringWindowToBack (GUIState &state, int windowID)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ if (!winState)
+ return;
+
+ // Modal windows are on top by definition.
+ if(winState->m_ModalWindow != NULL && winState->m_ModalWindow->m_ID == windowID)
+ return;
+
+ GUIWindow* win = winState->GetWindow (windowID);
+ if (win)
+ {
+ int maxDepth = 0;
+ for (GUIWindowState::WindowList::iterator i = winState->m_WindowList.begin(); i != winState->m_WindowList.end(); i++)
+ {
+ if ((*i)->m_Depth > maxDepth)
+ maxDepth = (*i)->m_Depth;
+ }
+ win->m_Depth = maxDepth + 1;
+ winState->m_LayersChanged = true;
+ }
+ }
+
+ void FocusWindow (GUIState &state, int windowID)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ if (!winState)
+ return;
+
+ // Modal windows must be focused if they exist.
+ if(winState->m_ModalWindow != NULL && winState->m_ModalWindow->m_ID != windowID)
+ return;
+
+ winState->m_FocusedWindow = windowID;
+ }
+}
+#endif
diff --git a/Runtime/IMGUI/GUIWindows.h b/Runtime/IMGUI/GUIWindows.h
new file mode 100644
index 0000000..1519d43
--- /dev/null
+++ b/Runtime/IMGUI/GUIWindows.h
@@ -0,0 +1,95 @@
+#ifndef GUIWINDOWS_H
+#define GUIWINDOWS_H
+
+#include "Runtime/Math/Rect.h"
+#include "Runtime/IMGUI/GUIContent.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+
+struct GUIContent;
+class GUIStyle;
+
+namespace IMGUI
+{
+ struct GUIWindow
+ {
+ int m_ID;
+ // The ID list used by this window
+ ObjectGUIState m_ObjectGUIState;
+ Rectf m_Position;
+ // Sorting depth
+ int m_Depth;
+ // What's the title?
+ GUIContent m_Title;
+ // Was this window referenced this frame? (used to clean up unused windows at end-of-frame)
+ bool m_Used;
+ // Was it moved with a DragWindow? If so, we need to use our internal rect instead of the one passed in to us
+ bool m_Moved;
+ bool m_ForceRect;
+
+ // Mono Object handles
+ int m_Delegate;
+ int m_Skin;
+ int m_Style;
+
+ // GUIState GUI.window time:
+ ColorRGBAf m_Color, m_BackgroundColor, m_ContentColor;
+ Matrix4x4f m_Matrix;
+ bool m_Enabled;
+
+ void LoadFromGUIState (GUIState &state);
+ void SetupGUIValues (GUIState &state);
+ void OnGUI (GUIState &state);
+ void ReleaseScriptingObjects ();
+
+ GUIWindow ();
+ ~GUIWindow ();
+ };
+
+ Rectf DoWindow (GUIState &state, int windowId, const Rectf &clientRect, ScriptingObjectPtr delegate, GUIContent& title, ScriptingObjectPtr style, ScriptingObjectPtr guiSkin, bool forceRectOnLayout, bool isModal = false);
+ void DragWindow (GUIState &state, const Rectf &rect);
+
+ void BeginWindows (GUIState &state, bool setupClipping, bool ignoreModalWindow = true);
+ void EndWindows (GUIState &state, bool ignoreModalWindow = true);
+ void RepaintModalWindow(GUIState &state);
+
+ void MoveWindowFromLayout (GUIState &state, int windowID, const Rectf &rect);
+ Rectf GetWindowRect (GUIState &state, int windowID);
+
+ Rectf GetWindowsBounds (GUIState &state);
+
+ void BringWindowToFront (GUIState &state, int windowID);
+ void BringWindowToBack (GUIState &state, int windowID);
+ void FocusWindow (GUIState &state, int windowID);
+
+ // Get the window that has focus, or NULL
+ GUIWindow *GetFocusedWindow (GUIState &state);
+
+ struct GUIWindowState
+ {
+ GUIWindowState ();
+ ~GUIWindowState ();
+ typedef std::vector<GUIWindow*> WindowList;
+ WindowList m_WindowList;
+ int m_FocusedWindow;
+ bool m_LayersChanged;
+
+ // The window we're currently calling OnGUI on, or NULL
+ GUIWindow* m_CurrentWindow;
+
+ // The current modal window being displayed, or NULL if there are no modal windows this frame
+ GUIWindow* m_ModalWindow;
+
+ GUIWindow* GetWindow (int windowId);
+ void SortWindows ();
+ GUIWindow* FindWindowUnderMouse (GUIState &state);
+
+ // Release all GC handles. We call this at the end of every frame in order to make sure we don't leak anything
+ // (they only need to get remembered WITHIN one layout/event cycle)
+ void ReleaseScriptingObjects ();
+ };
+}
+
+
+
+
+#endif
diff --git a/Runtime/IMGUI/IDList.cpp b/Runtime/IMGUI/IDList.cpp
new file mode 100644
index 0000000..896c498
--- /dev/null
+++ b/Runtime/IMGUI/IDList.cpp
@@ -0,0 +1,164 @@
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/IDList.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/Misc/InputEvent.h"
+
+IDList::IDList ()
+{
+ m_Idx = 0;
+}
+
+int IDList::CalculateNextFromHintList (GUIState &state, int hint, bool isKeyboard)
+{
+ int retval = 0;
+ // Ok - we're searching: start at idx and search to end.
+ for (int searchIdx = m_Idx; searchIdx < m_IDs.size(); searchIdx++)
+ {
+ if (m_IDs[searchIdx].hint == hint)
+ {
+ m_Idx = searchIdx + 1;
+ retval = m_IDs[searchIdx].value;
+ break;
+ }
+ }
+
+ // We still couldn't find it, so we just add to end...
+ if (retval == 0)
+ {
+ retval = state.m_EternalGUIState->GetNextUniqueID();
+ m_IDs.push_back (ID (hint, retval, isKeyboard));
+ m_Idx = m_IDs.size();
+ }
+
+ return retval;
+}
+
+void IDList::BeginOnGUI ()
+{
+ m_Idx = 0;
+ m_FirstKeyControl = -1;
+ m_LastKeyControl = -1;
+ m_PreviousKeyControl = -1;
+ m_NextKeyControl = -1;
+ m_HasKeyboardControl = false;
+ m_TabControlSearchStatus = kLookingForPrevious;
+}
+
+int IDList::GetNext (GUIState &state, int hint, FocusType focusType, const Rectf &rect)
+{
+ int retval = GetNext (state, hint, focusType);
+ if (state.m_CurrentEvent->type != InputEvent::kLayout && state.m_CurrentEvent->type != InputEvent::kUsed && ShouldBeKeyboardControl(focusType))
+ {
+ Assert (m_Idx > 0);
+ m_IDs[m_Idx-1].rect = rect;
+ }
+ return retval;
+}
+
+int IDList::GetNext (GUIState &state, int hint, FocusType focusType)
+{
+ InputEvent::Type type = state.m_CurrentEvent->type;
+
+ bool isKeyboard = ShouldBeKeyboardControl(focusType);
+ int retval = 0;
+ if (type != InputEvent::kUsed)
+ retval = CalculateNextFromHintList(state, hint, isKeyboard);
+ else
+ {
+ return -1;
+ }
+
+ if (type != InputEvent::kLayout)
+ {
+ if (type == InputEvent::kKeyDown && state.m_OnGUIState.m_Enabled == (int)true)
+ {
+ if (isKeyboard)
+ {
+ switch (m_TabControlSearchStatus)
+ {
+ case kNotActive:
+ break;
+ case kLookingForPrevious:
+ if (m_FirstKeyControl == -1)
+ m_FirstKeyControl = retval;
+ if (retval != state.m_MultiFrameGUIState.m_KeyboardControl)
+ m_PreviousKeyControl = retval;
+ else
+ {
+ m_TabControlSearchStatus = kLookingForNext;
+ m_HasKeyboardControl = true;
+ }
+ break;
+ case kLookingForNext:
+ m_NextKeyControl = retval;
+ m_TabControlSearchStatus = kFound;
+ break;
+ default:
+ break;
+ }
+ m_LastKeyControl = retval;
+ }
+ }
+ }
+ return retval;
+}
+
+bool IDList::GetRectOfControl (int id, Rectf &out) const
+{
+ for (dynamic_array<ID>::const_iterator i = m_IDs.begin(); i != m_IDs.end(); i++)
+ {
+ if (i->value == id && i->rect.width != -1.0f)
+ {
+ out = i->rect;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool IDList::CanHaveKeyboardFocus (int id) const
+{
+ for (dynamic_array<ID>::const_iterator i = m_IDs.begin(); i != m_IDs.end(); i++)
+ {
+ if (i->value == id)
+ {
+ return i->isKeyboard;
+ }
+ }
+ return false;
+}
+
+bool IDList::ShouldBeKeyboardControl (FocusType focus) {
+ switch (focus) {
+ case kPassive:
+ return false;
+ case kKeyboard:
+ return true;
+ case kNative:
+ return false;
+ // TODO: Move this back in during 2.x when we accept keyboard input on the various GUI controls.
+ /* PlatformSelection platform = GUI.skin.settings.keyboardFocus;
+ if (platform == PlatformSelection.Native) {
+ if (Application.platform == RuntimePlatform.WindowsPlayer || Application.platform == RuntimePlatform.WindowsWebPlayer || Application.platform == RuntimePlatform.WindowsEditor)
+ platform = PlatformSelection.Windows;
+ else
+ platform = PlatformSelection.Mac;
+ }
+ return platform == PlatformSelection.Windows;
+ */ }
+ return true;
+}
+
+void IDList::SetSearchIndex (int index)
+{
+ if (index >= 0 && index < m_IDs.size())
+ m_Idx = index;
+ else
+ AssertString (Format("Invalid index %d (size is %zd)", index, m_IDs.size()));
+}
+
+int IDList::GetSearchIndex () const
+{
+ return m_Idx;
+}
+
diff --git a/Runtime/IMGUI/IDList.h b/Runtime/IMGUI/IDList.h
new file mode 100644
index 0000000..40c1a3d
--- /dev/null
+++ b/Runtime/IMGUI/IDList.h
@@ -0,0 +1,69 @@
+#ifndef IDLIST_H
+#define IDLIST_H
+
+struct GUIState;
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+/// Used by GUIUtility.GetcontrolID to inform the UnityGUI system if a given control can get keyboard focus.
+/// MUST MATCH FocusType in GUIUtility.txt
+enum FocusType {
+ /// This control can get keyboard focus on Windows, but not on Mac. Used for buttons, checkboxes and other "pressable" things.
+ kNative = 0,
+ /// This is a proper keyboard control. It can have input focus on all platforms. Used for TextField and TextArea controls
+ kKeyboard = 1,
+ /// This control can never recieve keyboard focus.
+ kPassive = 2,
+};
+
+/// Manages the list of IDs that are returned from GUIUtility.GetControlID ();
+class IDList
+{
+public:
+ IDList ();
+ int GetNext (GUIState &state, int hint, FocusType focusType);
+ int GetNext (GUIState &state, int hint, FocusType focusType, const Rectf &rect);
+ void BeginOnGUI ();
+ bool HasKeyboardControl () { return m_HasKeyboardControl; }
+
+ // Get the ID of the keyboard control BEFORE the one that has keyboard focus (used to implement shift-tab) -1 if not found
+ int GetPreviousKeyboardControlID () { return m_PreviousKeyControl; }
+ // Get the ID of the keyboard control AFTER the one that has keyboard focus (used to implement tab) -1 if not found
+ int GetNextKeyboardControlID () { return m_NextKeyControl; }
+ // Get the ID of the first keyboard control - used when tabbing into this script -1 if not found
+ int GetFirstKeyboardControlID () { return m_FirstKeyControl; }
+ // Get the ID of the last keyboard control - used when tabbing out of this script -1 if not found
+ int GetLastKeyboardControlID () { return m_LastKeyControl; }
+
+ bool GetRectOfControl (int id, Rectf &out) const;
+ bool CanHaveKeyboardFocus (int id) const;
+ void SetSearchIndex (int index);
+ int GetSearchIndex () const;
+private:
+ // Enum and vars for handling searching for next and previous fields when tabbing through controls.
+ enum TabControlSearchStatus
+ {
+ kNotActive = 0, kLookingForPrevious = 1, kLookingForNext = 2, kFound = 3
+ };
+ TabControlSearchStatus m_TabControlSearchStatus;
+ int m_FirstKeyControl, m_LastKeyControl, m_PreviousKeyControl, m_NextKeyControl;
+ bool m_HasKeyboardControl;
+ /// Determine if a given focusType should result in keyboard control
+ static bool ShouldBeKeyboardControl (FocusType focus);
+
+ int CalculateNextFromHintList (GUIState &state, int hint, bool isKeyboard);
+ struct ID
+ {
+ int hint, value;
+ bool isKeyboard;
+ Rectf rect;
+ ID (int _hint, int _value, bool _isKeyboard) : hint (_hint), value (_value), isKeyboard (_isKeyboard), rect (-1.0f, -1.0f, -1.0f, -1.0f) {}
+ ID (int _hint, int _value, bool _isKeyboard, Rectf _rect) : hint (_hint), value (_value), isKeyboard (_isKeyboard), rect (_rect) {}
+ };
+
+ dynamic_array<ID> m_IDs;
+ int m_Idx;
+};
+
+
+#endif
diff --git a/Runtime/IMGUI/IMGUIUtils.cpp b/Runtime/IMGUI/IMGUIUtils.cpp
new file mode 100644
index 0000000..2ce0596
--- /dev/null
+++ b/Runtime/IMGUI/IMGUIUtils.cpp
@@ -0,0 +1,101 @@
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+#include "Runtime/IMGUI/TextMeshGenerator2.h"
+
+const float s_TabWidth = 16;
+extern float s_GUIStyleIconSizeX;
+
+namespace IMGUI
+{
+ InputEvent::Type GetEventType (const GUIState &state, const InputEvent &event)
+ {
+ InputEvent::Type type = event.type;
+ if (!state.m_OnGUIState.m_Enabled)
+ {
+ if (type == InputEvent::kRepaint || type == InputEvent::kLayout || type == InputEvent::kUsed)
+ return type;
+ return InputEvent::kIgnore;
+ }
+
+ if (state.m_CanvasGUIState.m_GUIClipState.GetEnabled())
+ return type;
+
+ if (type == InputEvent::kMouseDown || type == InputEvent::kMouseUp || type == InputEvent::kDragPerform || type == InputEvent::kDragUpdated)
+ return InputEvent::kIgnore;
+
+ return type;
+ }
+
+
+ InputEvent::Type GetEventTypeForControl (const GUIState &state, const InputEvent &event, int controlID)
+ {
+ InputEvent::Type m_Type = event.type;
+
+ // if we have no hot control, just return the usual
+ if (GetHotControl(state) == 0)
+ return GetEventType (state, event);
+ switch (m_Type)
+ {
+ // Mouse events follow GUIUtility.hotControl
+ case InputEvent::kMouseDown:
+ case InputEvent::kMouseUp:
+ case InputEvent::kMouseMove:
+ case InputEvent::kMouseDrag:
+ if (!GetEnabled(state))
+ return InputEvent::kIgnore;
+ if (GetGUIClipEnabled(state) || HasMouseControl (state, controlID))
+ return m_Type;
+ return InputEvent::kIgnore;
+
+ // Key events follow keyboard control
+ case InputEvent::kKeyDown:
+ case InputEvent::kKeyUp:
+ case InputEvent::kScrollWheel: // Not sure about scrollwheel. For now we map it to behave like keyboard events.
+
+ if (!GetEnabled(state))
+ return InputEvent::kIgnore;
+ if (GetGUIClipEnabled(state) || HasMouseControl (state, controlID) || GetKeyboardControl(state) == controlID)
+ return m_Type;
+ return InputEvent::kIgnore;
+
+ // Repaint, Layout, Used, DragUpdated, DragPerform, Ignore
+ default:
+ return m_Type;
+ }
+ }
+
+
+ TextMeshGenerator2* GetGenerator (const Rectf &contentRect, const GUIContent &content, Font& font, TextAnchor alignment, bool wordWrap, bool richText, ColorRGBA32 color, int fontSize, int fontStyle, ImagePosition imagePosition)
+ {
+ if (!wordWrap)
+ return &TextMeshGenerator2::Get (content.m_Text, &font, alignment, kAuto, 0.0f, s_TabWidth, 1.0f, richText, true, color, fontSize, fontStyle);
+
+ Texture *image = content.m_Image;
+ float textWidth = contentRect.Width();
+ switch (imagePosition) {
+ case kImageLeft:
+ // Todo: Subtract width of the icon
+ if (image != NULL)
+ {
+ Vector2f imageSize = Vector2f (image->GetDataWidth(), image->GetDataHeight());
+
+ //float imageScale = clamp (min (contentRect.Width() / imageSize.x, contentRect.Height() / imageSize.y), 0.0f, 1.0f);
+ //contentRect.width -= Roundf (imageSize.x * imageScale);
+
+ if (s_GUIStyleIconSizeX == 0)
+ textWidth -= Roundf (imageSize.x * clamp (std::min (contentRect.Width() / imageSize.x, contentRect.Height() / imageSize.y), 0.0f, 1.0f));
+ else
+ textWidth -= s_GUIStyleIconSizeX;
+ }
+ break;
+ case kImageOnly:
+ return NULL;
+ case kImageAbove:
+ case kTextOnly:
+ // These two require no special handling
+ break;
+ }
+
+ return &TextMeshGenerator2::Get (content.m_Text, &font, alignment, kAuto, textWidth, s_TabWidth, 1.0f, richText, true, color, fontSize, fontStyle);
+ }
+} // namespace IMGUI
diff --git a/Runtime/IMGUI/IMGUIUtils.h b/Runtime/IMGUI/IMGUIUtils.h
new file mode 100644
index 0000000..486b2fe
--- /dev/null
+++ b/Runtime/IMGUI/IMGUIUtils.h
@@ -0,0 +1,64 @@
+#ifndef IMGUIUtils_H
+#define IMGUIUtils_H
+
+#include "Runtime/Math/Color.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/GUIContent.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+
+struct InputEvent;
+struct GUIState;
+class TextMeshGenerator2;
+namespace IMGUI
+{
+ inline ColorRGBAf GetColor (const GUIState &state) { return state.m_OnGUIState.m_Color; }
+ inline void SetColor (GUIState &state, const ColorRGBAf &color) { state.m_OnGUIState.m_Color = color; }
+
+ inline ColorRGBAf GetBackgroundColor (const GUIState &state) { return state.m_OnGUIState.m_BackgroundColor; }
+ inline void SetBackgroundColor (GUIState &state, const ColorRGBAf &color) { state.m_OnGUIState.m_BackgroundColor = color; }
+
+ inline ColorRGBAf GetContentColor (const GUIState &state) { return state.m_OnGUIState.m_ContentColor; }
+ inline void SetContentColor (GUIState &state, const ColorRGBAf &color) { state.m_OnGUIState.m_ContentColor = color; }
+
+ inline bool GetEnabled (const GUIState &state) { return state.m_OnGUIState.m_Enabled; }
+ inline void SetEnabled (GUIState &state, bool enab) { state.m_OnGUIState.m_Enabled = enab; }
+
+ inline bool GetChanged (const GUIState &state) { return state.m_OnGUIState.m_Changed; }
+ inline void SetChanged (GUIState &state, bool changed) { state.m_OnGUIState.m_Changed = changed; }
+
+ inline int GetDepth (const GUIState &state) { return state.m_OnGUIState.m_Depth; }
+ inline void SetDepth (GUIState &state, int depth) { state.m_OnGUIState.m_Depth = depth; }
+
+ inline int GetHotControl (const GUIState &state) { return state.m_EternalGUIState->m_HotControl; }
+ inline void SetHotControl (GUIState &state, int hotControl) { state.m_EternalGUIState->m_HotControl = hotControl; }
+
+ inline int GetKeyboardControl (const GUIState &state) { return state.m_MultiFrameGUIState.m_KeyboardControl; }
+ inline void SetKeyboardControl (GUIState &state, int keyControl) { state.m_MultiFrameGUIState.m_KeyboardControl = keyControl; }
+
+ inline InputEvent &GetCurrentEvent (GUIState &state) { return *state.m_CurrentEvent; }
+
+ inline bool GetGUIClipEnabled (const GUIState &state) { return state.m_CanvasGUIState.m_GUIClipState.GetEnabled();}
+
+ /// Get the type of the current event taking clipping and enabled flag into account from GUIState.
+ InputEvent::Type GetEventType (const GUIState &state, const InputEvent &event);
+
+ /// Get the type of the current event considering that a specific control is asking.
+ /// This is more agressive than the old C# one as it will also cull mouse events, assuming that hotControl is being used.
+ InputEvent::Type GetEventTypeForControl (const GUIState &state, const InputEvent &event, int controlID);
+
+ inline int GetControlID (GUIState &state, int hash, FocusType focusType)
+ { return state.GetControlID (hash, focusType); }
+
+ inline int GetControlID (GUIState &state, int hash, FocusType focusType, const Rectf& rect)
+ { return state.GetControlID (hash, focusType, rect); }
+
+
+ inline void GrabMouseControl (GUIState &state, int controlID) { state.m_EternalGUIState->m_HotControl = controlID; }
+ inline void ReleaseMouseControl (GUIState &state) { state.m_EternalGUIState->m_HotControl = 0; }
+ inline bool HasMouseControl (const GUIState &state, int controlID) { return state.m_EternalGUIState->m_HotControl == controlID; }
+
+ inline bool AreWeInOnGUI (const GUIState &state) { return state.m_OnGUIDepth > 0; }
+
+ TextMeshGenerator2* GetGenerator (const Rectf &contentRect, const GUIContent &content, Font& font, TextAnchor alignment, bool wordWrap, bool richText, ColorRGBA32 color, int fontSize, int fontStyle, ImagePosition imagePosition);
+}
+#endif
diff --git a/Runtime/IMGUI/NamedKeyControlList.cpp b/Runtime/IMGUI/NamedKeyControlList.cpp
new file mode 100644
index 0000000..6574a13
--- /dev/null
+++ b/Runtime/IMGUI/NamedKeyControlList.cpp
@@ -0,0 +1,27 @@
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/NamedKeyControlList.h"
+namespace IMGUI
+{
+
+void NamedKeyControlList::AddNamedControl (const std::string &name, int id, int windowID)
+{
+ m_NamedControls[name] = NamedControl (id, windowID);
+}
+
+std::string NamedKeyControlList::GetNameOfControl (int id)
+{
+ for (std::map<std::string, NamedControl>::const_iterator i = m_NamedControls.begin(); i != m_NamedControls.end(); i++)
+ if (i->second.ID == id)
+ return i->first;
+ return std::string ("");
+}
+
+NamedControl* NamedKeyControlList::GetControlNamed (const std::string &name)
+{
+ std::map<std::string, NamedControl>::iterator i = m_NamedControls.find (name);
+ if (i != m_NamedControls.end ())
+ return &i->second;
+ return NULL;
+}
+
+} // namespace \ No newline at end of file
diff --git a/Runtime/IMGUI/NamedKeyControlList.h b/Runtime/IMGUI/NamedKeyControlList.h
new file mode 100644
index 0000000..dc4dff7
--- /dev/null
+++ b/Runtime/IMGUI/NamedKeyControlList.h
@@ -0,0 +1,40 @@
+#ifndef NAMEDKEYCONTROLLIST_H
+#define NAMEDKEYCONTROLLIST_H
+
+namespace IMGUI
+{
+ struct NamedControl
+ {
+ int ID;
+ int windowID;
+ NamedControl ()
+ {
+ ID = 0;
+ windowID = -1;
+ }
+ NamedControl (int _ID, int _windowID)
+ {
+ ID = _ID;
+ windowID = _windowID;
+ }
+ };
+
+ class NamedKeyControlList
+ {
+ public:
+ void AddNamedControl (const std::string &str, int id, int windowID);
+
+ // Return ptr name of a given control, NULL if none.
+ std::string GetNameOfControl (int id);
+
+ // Return the ID of a named control.
+ // Used in GUIState::FocusControl
+ NamedControl* GetControlNamed (const std::string &name);
+
+ void Clear () { m_NamedControls.clear (); }
+ private:
+ std::map<std::string, NamedControl> m_NamedControls;
+ };
+};
+
+#endif \ No newline at end of file
diff --git a/Runtime/IMGUI/TextFormatting.cpp b/Runtime/IMGUI/TextFormatting.cpp
new file mode 100644
index 0000000..7495326
--- /dev/null
+++ b/Runtime/IMGUI/TextFormatting.cpp
@@ -0,0 +1,337 @@
+#include "UnityPrefix.h"
+#include "TextFormatting.h"
+
+enum FormattingTag {
+ kTagBold,
+ kTagItalic,
+ kTagColor,
+ kTagSize,
+ kTagMaterial,
+ kTagImage,
+ kTagX,
+ kTagY,
+ kTagWidth,
+ kTagHeight,
+ kNumTags,
+};
+
+const char* kFormattingTagStrings[] =
+{
+ "b",
+ "i",
+ "color",
+ "size",
+ "material",
+ "quad",
+ "x",
+ "y",
+ "width",
+ "height",
+};
+
+
+const char* kFormattingHTMLColorStrings[] =
+{
+ "red",
+ "cyan",
+ "blue",
+ "darkblue",
+ "lightblue",
+ "purple",
+ "yellow",
+ "lime",
+ "fuchsia",
+ "white",
+ "silver",
+ "grey",
+ "black",
+ "orange",
+ "brown",
+ "maroon",
+ "green",
+ "olive",
+ "navy",
+ "teal",
+ "aqua",
+ "magenta",
+};
+
+#define kNumHTMLColors (sizeof(kFormattingHTMLColorStrings)/sizeof(kFormattingHTMLColorStrings[0]))
+
+const UInt8 kFormattingHTMLColorValues[kNumHTMLColors*4] =
+{
+ 0xff,0x00,0x00,0xff,
+ 0x00,0xff,0xff,0xff,
+ 0x00,0x00,0xff,0xff,
+ 0x00,0x00,0xa0,0xff,
+ 0xad,0xd8,0xe6,0xff,
+ 0x80,0x00,0x80,0xff,
+ 0xff,0xff,0x00,0xff,
+ 0x00,0xff,0x00,0xff,
+ 0xff,0x00,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xc0,0xc0,0xc0,0xff,
+ 0x80,0x80,0x80,0xff,
+ 0x00,0x00,0x00,0xff,
+ 0xff,0xa5,0x00,0xff,
+ 0xa5,0x2a,0x2a,0xff,
+ 0x80,0x00,0x00,0xff,
+ 0x00,0x80,0x00,0xff,
+ 0x80,0x80,0x00,0xff,
+ 0x00,0x00,0x80,0xff,
+ 0x00,0x80,0x80,0xff,
+ 0x00,0xff,0xff,0xff,
+ 0xff,0x00,0xff,0xff,
+};
+
+FormattingTag GetTag(UTF16String& input, int &pos, bool &closing)
+{
+ if (input[pos] == '<')
+ {
+ int outpos = pos + 1;
+ if (outpos == input.length)
+ return (FormattingTag)-1;
+
+ closing = input[outpos] == '/';
+ if (closing)
+ outpos++;
+
+ for (int i=0;i<kNumTags;i++)
+ {
+ bool match = true;
+ for (int j=0;kFormattingTagStrings[i][j] != '\0'; j++)
+ {
+ if (outpos+j == input.length || ToLower((char)input[outpos+j]) != kFormattingTagStrings[i][j])
+ {
+ match = false;
+ break;
+ }
+ }
+ if (match)
+ {
+ int paramPos = outpos + strlen(kFormattingTagStrings[i]);
+ if ((!closing && input[paramPos] == '=') || (input[paramPos] == ' ' && i == kTagImage))
+ {
+ while (input[paramPos] != '>' && paramPos < input.length)
+ paramPos++;
+ }
+ else if (input[paramPos] != '>')
+ continue;
+ outpos += strlen(kFormattingTagStrings[i]);
+ pos = outpos;
+ return (FormattingTag)i;
+ }
+ }
+ }
+ return (FormattingTag)-1;
+}
+
+FormattingTag GetImageTag(UTF16String& input, int &pos)
+{
+ for (int i=0;i<kNumTags;i++)
+ {
+ bool match = true;
+ for (int j=0;kFormattingTagStrings[i][j] != '\0'; j++)
+ {
+ if (pos+j == input.length || ToLower((char)input[pos+j]) != kFormattingTagStrings[i][j])
+ {
+ match = false;
+ break;
+ }
+ }
+ if (match)
+ {
+ int paramPos = pos + strlen(kFormattingTagStrings[i]);
+ if (input[paramPos] == '=')
+ {
+ pos = paramPos;
+ return (FormattingTag)i;
+ }
+ else
+ continue;
+ }
+ }
+ return (FormattingTag)-1;
+}
+
+std::string GetParameter(UTF16String& input, int &pos, bool allowMultipleParamaters = false)
+{
+ std::string parameter;
+ if (input[pos] == '=')
+ {
+ pos++;
+ while (input[pos] != '>' && (input[pos] != ' ' || !allowMultipleParamaters) && pos < input.length)
+ parameter.push_back(input[pos++]);
+ }
+ if (parameter.size() > 2 && parameter[0] == parameter[parameter.size()-1])
+ {
+ if (parameter[0] == '\'' || parameter[0] == '"')
+ parameter = parameter.substr(1,parameter.size()-2);
+ }
+ return parameter;
+}
+
+ColorRGBA32 ParseHTMLColor(std::string colorstring)
+{
+ ColorRGBA32 color = ColorRGBA32(0xffffffff);
+ if (colorstring[0] == '#')
+ {
+ if (colorstring.size() == 4 || colorstring.size() == 5)
+ {
+ std::string longcolorstring = "#";
+ for (int i=1;i<colorstring.size();i++)
+ {
+ longcolorstring += colorstring[i];
+ longcolorstring += colorstring[i];
+ }
+ colorstring = longcolorstring;
+ }
+ if (colorstring.size() == 7 || colorstring.size() == 9)
+ HexStringToBytes (&colorstring[1], colorstring.size()/2, &color);
+ }
+ else for (int i=0; i<kNumHTMLColors; i++)
+ {
+ if (StrICmp(colorstring, kFormattingHTMLColorStrings[i]) == 0)
+ return ColorRGBA32 (*(UInt32*)(kFormattingHTMLColorValues+i*4));
+ }
+ return color;
+}
+
+bool ValidateFormat (std::vector<TextFormatChange> &format)
+{
+ std::vector<int> stack;
+ for (std::vector<TextFormatChange>::iterator i = format.begin(); i != format.end(); i++)
+ {
+ int flags = i->flags;
+ if (flags & kFormatPop)
+ {
+ if (stack.empty())
+ // closing tag without opening tag.
+ return false;
+ flags &= ~kFormatPop;
+ if (stack.back() != flags)
+ // closing does not match opening tag.
+ return false;
+ stack.pop_back();
+ }
+ else
+ stack.push_back(flags);
+ }
+ if (!stack.empty())
+ // unclosed tags.
+ return false;
+ return true;
+}
+
+void ParseImageParameters (UTF16String& input, int &pos, TextFormatChange &change)
+{
+ while (pos < input.length && input[pos] != '>')
+ {
+ FormattingTag tag = GetImageTag(input, pos);
+ if (tag != -1)
+ {
+ switch (tag)
+ {
+ case kTagMaterial:
+ change.flags |= kFormatMaterial;
+ change.format.material = StringToInt(GetParameter(input, pos, true));
+ break;
+ case kTagSize:
+ change.flags |= kFormatSize;
+ change.format.size = StringToInt(GetParameter(input, pos, true));
+ break;
+ case kTagColor:
+ change.flags |= kFormatColor;
+ change.format.color = ParseHTMLColor(GetParameter(input, pos, true));
+ break;
+ case kTagX:
+ sscanf(GetParameter(input, pos, true).c_str(), "%f", &change.format.imageRect.x);
+ break;
+ case kTagY:
+ sscanf(GetParameter(input, pos, true).c_str(), "%f", &change.format.imageRect.y);
+ break;
+ case kTagWidth:
+ sscanf(GetParameter(input, pos, true).c_str(), "%f", &change.format.imageRect.width);
+ break;
+ case kTagHeight:
+ sscanf(GetParameter(input, pos, true).c_str(), "%f", &change.format.imageRect.height);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ pos++;
+ }
+}
+
+void GetFormatString (UTF16String& input, std::vector<TextFormatChange> &format)
+{
+ int pos = 0;
+ while (pos < input.length)
+ {
+ int oldpos = pos;
+ bool closing;
+ FormattingTag tag = GetTag(input, pos, closing);
+ if (tag != -1)
+ {
+ TextFormatChange change;
+ change.flags = kFormatPop;
+ switch (tag)
+ {
+ case kTagBold:
+ change.flags = kFormatBold;
+ break;
+ case kTagItalic:
+ change.flags = kFormatItalic;
+ break;
+ case kTagSize:
+ change.flags = kFormatSize;
+ break;
+ case kTagColor:
+ change.flags = kFormatColor;
+ break;
+ case kTagMaterial:
+ change.flags = kFormatMaterial;
+ break;
+ case kTagImage:
+ change.flags = kFormatImage;
+ break;
+ default:
+ break;
+ }
+ if (closing)
+ change.flags |= kFormatPop;
+ else switch (tag)
+ {
+ case kTagSize:
+ change.format.size = StringToInt(GetParameter(input, pos));
+ break;
+ case kTagColor:
+ change.format.color = ParseHTMLColor(GetParameter(input, pos));
+ break;
+ case kTagMaterial:
+ change.format.material = StringToInt(GetParameter(input, pos));
+ break;
+ case kTagImage:
+ ParseImageParameters(input, pos, change);
+ break;
+ default:
+ break;
+ }
+ change.skipCharacters = pos + 1 - oldpos;
+ change.startPosition = oldpos;
+ format.push_back(change);
+ if (tag == kTagImage)
+ {
+ change.flags |= kFormatPop;
+ change.skipCharacters = 0;
+ format.push_back(change);
+ }
+ }
+ pos ++;
+ }
+ if (!ValidateFormat(format))
+ // Fail silently here rather then spamming the console with errors while typing unfinished markup.
+ format.clear();
+} \ No newline at end of file
diff --git a/Runtime/IMGUI/TextFormatting.h b/Runtime/IMGUI/TextFormatting.h
new file mode 100644
index 0000000..3646692
--- /dev/null
+++ b/Runtime/IMGUI/TextFormatting.h
@@ -0,0 +1,85 @@
+#ifndef TEXTFORMATTING_H
+#define TEXTFORMATTING_H
+
+#include "TextUtil.h"
+#include "Runtime/Math/Rect.h"
+
+enum {
+ kStyleDefault = 0,
+ kStyleFlagBold = 1 << 0,
+ kStyleFlagItalic = 1 << 1,
+};
+
+struct TextFormat
+{
+ int style;
+ ColorRGBA32 color;
+ int size;
+ int material;
+ Rectf imageRect;
+
+ TextFormat () :
+ style(0),
+ color(0xff,0xff,0xff,0xff),
+ size(0),
+ material(0),
+ imageRect(0,0,1,1)
+ {
+ }
+};
+
+enum FormatFlags {
+ kFormatBold = 1 << 0,
+ kFormatItalic = 1 << 1,
+ kFormatColor = 1 << 2,
+ kFormatSize = 1 << 3,
+ kFormatMaterial = 1 << 4,
+ kFormatImage = 1 << 5,
+ kFormatPop = 1 << 15,
+};
+
+struct TextFormatChange
+{
+ int startPosition;
+ int skipCharacters;
+ TextFormat format;
+ int flags;
+};
+
+class FormatStack : std::vector<TextFormat>
+{
+public:
+ FormatStack (ColorRGBA32 _color, int _size, int _style)
+ {
+ push_back (TextFormat());
+ back().color = _color;
+ back().size = _size;
+ back().style = _style;
+ }
+
+ void PushFormat (TextFormatChange &change)
+ {
+ if (change.flags & kFormatPop)
+ pop_back();
+ else
+ {
+ push_back(back());
+ if (change.flags & kFormatBold)
+ back().style |= kStyleFlagBold;
+ if (change.flags & kFormatItalic)
+ back().style |= kStyleFlagItalic;
+ if (change.flags & kFormatColor)
+ back().color = change.format.color;
+ if (change.flags & kFormatSize)
+ back().size = change.format.size;
+ if (change.flags & kFormatMaterial)
+ back().material = change.format.material;
+ }
+ }
+
+ TextFormat& Current() { return back(); }
+};
+
+void GetFormatString (UTF16String& input, std::vector<TextFormatChange> &format);
+
+#endif \ No newline at end of file
diff --git a/Runtime/IMGUI/TextMeshGenerator2.cpp b/Runtime/IMGUI/TextMeshGenerator2.cpp
new file mode 100644
index 0000000..c5fafb0
--- /dev/null
+++ b/Runtime/IMGUI/TextMeshGenerator2.cpp
@@ -0,0 +1,871 @@
+#include "UnityPrefix.h"
+#include "TextMeshGenerator2.h"
+#include "TextFormatting.h"
+#include "Runtime/Filters/Misc/Font.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Graphics/DrawUtil.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Allocator/LinearAllocator.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include <limits>
+
+#define TEXTDEBUG 0
+
+#define kMaxMaterials 8
+
+using std::pair;
+typedef std::vector<TextMeshGenerator2*/*, main_thread_allocator<TextMeshGenerator2*> */> Generators;
+static Generators s_Generators;
+static Font *gDefaultFont = NULL;
+const int kKillFrames = 5;
+
+static const TextAlignment kTextAnchorToAlignment[] = {
+ kLeft, kCenter, kRight, kLeft, kCenter, kRight, kLeft, kCenter, kRight
+};
+
+// The global getter function
+TextMeshGenerator2 &TextMeshGenerator2::Get (const UTF16String &text, Font *font, TextAnchor anchor, TextAlignment alignment, float wordWrapWidth, float tabSize, float lineSpacing, bool richText, bool pixelCorrect, ColorRGBA32 color, int fontSize, int fontStyle) {
+ if (font == NULL) {
+ if (!gDefaultFont)
+ gDefaultFont = GetBuiltinResource<Font> (kDefaultFontName);
+ font = gDefaultFont;
+ }
+
+ // maybe not the best way around, but we want to report error if changing size/style for non-dynamic fonts
+ // but in the same time don't want to spam console because we'll continue to work just fine
+ bool reportNonDynamicFontError = false;
+
+ bool fontDynamic = font->GetConvertCase() == Font::kDynamicFont;
+ if( !fontDynamic )
+ {
+ if( fontSize != 0 || fontStyle != kStyleDefault )
+ reportNonDynamicFontError = true;
+
+ fontSize = 0;
+ fontStyle = kStyleDefault;
+ }
+
+ if (alignment == kAuto)
+ alignment = kTextAnchorToAlignment[anchor];
+ // TODO: Use some sort of hash to quickly find candidate meshes
+ // in practice a lot of the parameters will be the same, with only the string really differing
+ for (Generators::const_iterator i = s_Generators.begin(); i != s_Generators.end(); i++) {
+ TextMeshGenerator2 *gen = *i;
+ if ( gen->m_Font.GetInstanceID() == font->GetInstanceID() &&
+ (anchor == kDontCare || (gen->m_Anchor == anchor && gen->m_Alignment == alignment)) &&
+ gen->m_WordWrapWidth == wordWrapWidth &&
+ gen->m_TabSize == tabSize &&
+ gen->m_LineSpacing == lineSpacing &&
+ gen->m_UTF16Text == text &&
+ gen->m_FontSize == fontSize &&
+ gen->m_FontStyle == fontStyle &&
+ gen->m_RichText == richText &&
+ gen->m_PixelCorrect == pixelCorrect &&
+ gen->m_Color == color
+ ) {
+ // we mark it as dirty the moment we look at it.
+ // Doing it during rendering only makes us regenerate strings only for wordwrap & scrollview size negotiations.
+ gen->m_LastUsedFrame = GetTimeManager().GetRenderFrameCount();
+ return *gen;
+ }
+ }
+
+ // report error only now - it will fire up the single time we create text mesh
+ if(reportNonDynamicFontError)
+ {
+ // TODO: font name?
+ WarningString("Font size and style overrides are only supported for dynamic fonts.");
+ }
+
+ // We don't have one already so we need to generate it.
+ // Sanitize anchor if we don't care. We just pick one for the generation.
+ // If it gets rendered with another, this mesh will get garbage collected later and the real one used instead.
+ if (anchor == kDontCare)
+ anchor = kUpperLeft;
+
+ TextMeshGenerator2 *gen = new TextMeshGenerator2 (text, font, anchor, alignment, wordWrapWidth, tabSize, lineSpacing, richText, pixelCorrect, color, fontSize, fontStyle);
+ gen->Generate ();
+ s_Generators.push_back (gen);
+ return *gen;
+}
+
+float TextMeshGenerator2::Roundf (float x)
+{
+ if (m_PixelCorrect)
+ return ::Roundf(x);
+ else
+ return x;
+}
+/// Get the cursor position
+Vector2f TextMeshGenerator2::GetCursorPosition (const Rectf &screenRect, int textPosition) {
+ // clamp the text position to valid ranges
+ if (textPosition < 0)
+ textPosition = 0;
+ else if (textPosition > m_UTF16Text.length)
+ textPosition = m_UTF16Text.length;
+
+ // make sure we don't access out of bounds position when the string is too large to be processed completely.
+ if(textPosition * 4 + 4 > std::numeric_limits<UInt16>::max())
+ textPosition = (std::numeric_limits<UInt16>::max() / 4) - 1;
+
+ return m_CursorPos[textPosition] + GetTextOffset (screenRect);
+}
+
+
+// Compare 2 vertex positions.
+// this function correctly handles rounding so you can click on the first half of a letter and be taken to the front...
+inline int ComparePositions2 (Vector2f *arr, int maxCount, int idx1, const Vector2f &p2, float lineHeight) {
+ // Find out if we're line(s) above or below
+ Vector2f p = arr[idx1];
+ if (p.y <= p2.y - lineHeight)
+ return -1;
+ if (p.y > p2.y)
+ return 1;
+ // ok, we're on the same line here, so compare x positions...
+
+ // We need to get the middle of letters
+ Vector2f curr = arr[idx1];
+ Vector2f nextPoint = arr[idx1 != maxCount ? idx1+ 1 : maxCount];
+ // If the next letter is on the line after, we cheat by moving the point way to the right, so hit detection will work...
+ float next;
+ if (nextPoint.y == curr.y)
+ next = nextPoint.x;
+ else
+ next = 10000;
+
+
+ float rightBorder = (next + curr.x) * .5f;
+ if (rightBorder < p2.x)
+ return -1;
+
+ Vector2f prevPoint = arr[idx1 != 0 ? idx1 - 1 : 0];
+ float prev;
+ // If the previous letter is on the line before, we cheat by moving the point way to the left, so hit detection will work...
+ if (prevPoint.y == curr.y)
+ prev = prevPoint.x;
+ else
+ prev = -10000;
+ float leftBorder = (prev + curr.x) * .5f;
+ if (leftBorder > p2.x)
+ return 1;
+ return 0;
+}
+
+int TextMeshGenerator2::GetCursorIndexAtPosition (const Rectf &screenRect, const Vector2f &pos) {
+ int start = 0;
+ int maxCount = m_CursorPos.size () - 1;
+ int end = maxCount;
+ Vector2f localPos = pos - GetTextOffset (screenRect);
+ Vector2f *arr = &m_CursorPos[0];
+
+ Font *font = GetFont();
+ float lineSize = Roundf (font->GetLineSpacing(m_FontSize));
+
+ // Binary search to find out where we clicked
+ while (start <= end) {
+ int mid = ((start + end) >> 1);
+ switch (ComparePositions2 (arr, maxCount, mid, localPos, lineSize)) {
+ case 0:
+ return mid;
+ case -1:
+ start = mid + 1;
+ break;
+ case 1:
+ end = mid - 1;
+ break;
+ }
+ }
+ // We didn't find anything, this is the closest match
+ if (end < 0)
+ end = 0; // Sanity.
+ return end;
+}
+
+PROFILER_INFORMATION(gTextMeshGenerator, "TextRendering.Cleanup", kProfilerRender)
+
+ // clear the cache for anything that wasn't rendered in the last frame.
+ void TextMeshGenerator2::GarbageCollect ()
+{
+ int currentFrame = GetTimeManager().GetRenderFrameCount();
+
+#if TEXTDEBUG
+ printf_console ("TextMesh GarbageCollect\n");
+#endif
+
+ // We're removing from the vector, so we need to iterate backwards through it
+ for (int i = s_Generators.size(); i--;)
+ {
+ PROFILER_AUTO(gTextMeshGenerator, NULL)
+ TextMeshGenerator2 *gen = s_Generators[i];
+ if (currentFrame - gen->m_LastUsedFrame > kKillFrames)
+ {
+#if TEXTDEBUG
+ printf_console ("\tTextMesh killed - %d chars\n", gen->m_CursorPos.size() - 1);
+#endif
+ delete gen;
+ s_Generators.erase(s_Generators.begin() + i);
+ }
+ }
+}
+
+void TextMeshGenerator2::Flush ()
+{
+ for (int i = s_Generators.size(); i--;)
+ {
+ TextMeshGenerator2 *gen = s_Generators[i];
+ delete gen;
+ }
+ s_Generators.clear ();
+}
+
+
+TextMeshGenerator2::TextMeshGenerator2 (const UTF16String &text, Font *font, TextAnchor anchor, TextAlignment alignment, float wordWrapWidth, float tabSize, float lineSpacing, bool richText, bool pixelCorrect, ColorRGBA32 color, int fontSize, int fontStyle) :
+m_UTF16Text (text)
+{
+ m_Font = font;
+ m_FontSize = fontSize;
+ m_FontStyle = fontStyle;
+
+ m_Anchor = anchor;
+ m_Alignment = alignment;
+ m_WordWrapWidth = wordWrapWidth;
+ m_TabSize = tabSize;
+ m_LastUsedFrame = 0;
+ m_LineSpacing = lineSpacing;
+ m_Mesh = NULL;
+ m_RichText = richText;
+ m_PixelCorrect = pixelCorrect;
+ m_Color = color;
+
+#if TEXTDEBUG
+ printf_console ("Creating textMesh\n");
+#endif
+}
+
+
+TextMeshGenerator2::~TextMeshGenerator2 ()
+{
+ if (m_Mesh)
+ DestroySingleObject (&*m_Mesh);
+}
+
+Vector2f TextMeshGenerator2::GetTextOffset (const Rectf &rect)
+{
+ Vector2f offset;
+ switch (m_Anchor) {
+ case kUpperLeft:
+ offset.x = Roundf (rect.x);
+ offset.y = Roundf (rect.y);
+ break;
+ case kUpperCenter:
+ offset.x = Roundf (rect.x + rect.width *.5f);
+ offset.y = Roundf (rect.y);
+ break;
+ case kUpperRight:
+ offset.x = Roundf (rect.GetRight());
+ offset.y = Roundf (rect.y);
+ break;
+ case kMiddleLeft:
+ offset.x = Roundf (rect.x);
+ offset.y = Roundf ((rect.GetBottom() + rect.y - m_Rect.Height ()) * .5f);
+ break;
+ case kMiddleCenter:
+ offset.x = Roundf (rect.x + rect.width *.5f);
+ offset.y = Roundf ((rect.GetBottom() + rect.y - m_Rect.Height ()) * .5f);
+ break;
+ case kMiddleRight:
+ offset.x = Roundf (rect.GetRight());
+ offset.y = Roundf ((rect.GetBottom() + rect.y - m_Rect.Height ()) * .5f);
+ break;
+ case kLowerLeft:
+ offset.x = Roundf (rect.x);
+ offset.y = Roundf (rect.GetBottom() - m_Rect.Height ());
+ break;
+ case kLowerCenter:
+ offset.x = Roundf ((rect.x + rect.GetRight()) *.5f);
+ offset.y = Roundf (rect.GetBottom() - m_Rect.Height ());
+ break;
+ case kLowerRight:
+ offset.x = Roundf (rect.GetRight());
+ offset.y = Roundf (rect.GetBottom() - m_Rect.Height ());
+ break;
+ default:
+ offset.x = offset.y = 0.0F;
+ break;
+ }
+ return offset;
+}
+
+// Draw the text mesh
+// TODO: clip
+void TextMeshGenerator2::Render (const Rectf &rect, const ChannelAssigns& channels)
+{
+ GfxDevice &device = GetGfxDevice();
+
+ float matWorld[16], matView[16];
+
+ CopyMatrix(device.GetViewMatrix(), matView);
+ CopyMatrix(device.GetWorldMatrix(), matWorld);
+
+ Matrix4x4f textMatrix;
+
+ Vector2f offset = GetTextOffset (rect);
+ textMatrix.SetTranslate (Vector3f (offset.x, offset.y, 0.0f));
+
+ device.SetViewMatrix (textMatrix.GetPtr());
+
+ DrawUtil::DrawMeshRaw (channels, *m_Mesh, 0);
+
+ device.SetViewMatrix(matView);
+ device.SetWorldMatrix(matWorld);
+
+ // YES, we have actually used this TextMesh
+ m_LastUsedFrame = GetTimeManager().GetRenderFrameCount();
+}
+
+void TextMeshGenerator2::RenderRaw (const ChannelAssigns& channels)
+{
+ DrawUtil::DrawMeshRaw (channels, *m_Mesh, 0);
+ m_LastUsedFrame = GetTimeManager().GetRenderFrameCount();
+}
+
+// Will offset count*4 vertices horizontally - used for doing center & rightaligned
+void OffsetCharacters (Vector2f offset, TextMeshGenerator2::Vertex *firstIndex, Vector2f *cursor, int count) {
+ int vertexCount = count * 4;
+ while (vertexCount--) {
+ firstIndex->vert.x += offset.x;
+ firstIndex->vert.y += offset.y;
+ firstIndex++;
+ }
+ while (count--) {
+ cursor->x += offset.x;
+ cursor->y += offset.y;
+ cursor++;
+ }
+}
+
+void TextMeshGenerator2::FixLineOffset (float lineWidth, TextMeshGenerator2::Vertex *firstChar, Vector2f *firstCursor, int count) {
+ // If we're centre or right-aligned, handle that.
+ // Right now charOffset.x contains the width of the line.
+ switch (m_Alignment) {
+ // if we're right-aligned, move everything backl
+ case kRight:
+ OffsetCharacters (Vector2f (-lineWidth, 0), firstChar, firstCursor, count);
+ break;
+ case kCenter:
+ OffsetCharacters (Vector2f (Roundf (-lineWidth * .5f), 0), firstChar, firstCursor, count);
+ break;
+ default:
+ break;
+ }
+}
+
+class TextMeshGenerationData
+{
+ TextMeshGenerator2 &tmgen;
+ std::vector<TextFormatChange> format;
+ TextMeshGenerator2::Vertex *vertex, *vertexBegin;
+ dynamic_array<UInt16> materialTriangles[kMaxMaterials];
+ Font *font;
+ int formatChange;
+ int characterPos;
+ int lastChar;
+ int startOfWord, startOfLine;
+ int stringlen;
+ int materialCount;
+ float lineSize;
+ float lineLength, wordLength;
+ float endOfLastWord, startOfWordPos;
+ float spacesLength;
+ float maxLineLength;
+ bool wordWrap;
+ Vector3f charOffset;
+ float startY;
+ FormatStack formatStack;
+
+ inline float Roundf (float x)
+ {
+ return tmgen.Roundf(x);
+ }
+
+public:
+ int GetStringLength () const { return stringlen; }
+ dynamic_array<UInt16> *GetTriangles () { return materialTriangles; }
+
+ TextMeshGenerationData(TextMeshGenerator2 &_tmgen)
+ : tmgen (_tmgen),
+ formatStack(tmgen.m_Color, tmgen.m_FontSize, tmgen.m_FontStyle),
+ formatChange (0),
+ characterPos (0),
+ startOfWord (0), // Character index at start of current word (used for wordwrap)
+ startOfLine (0), // character index at start of current line (used for right/center-aligning text).
+ lineLength (0), // Length of current line without trailing spaces (in pixels)
+ wordLength (0), // Length of current word (in pixels)
+ endOfLastWord (0), // position of the last completed word's right bounds
+ startOfWordPos (0), // Pixel position of the current word start.
+ spacesLength (0), // Size of spaces (used to not make trailing spaces affect alignment)
+ maxLineLength (0), // The max line length - used for calculating the rect at the end.
+ lineSize (0),
+ startY (0),
+ lastChar (-1)
+ {
+ font = tmgen.GetFont();
+ wordWrap = tmgen.m_WordWrapWidth != 0.0f;
+ }
+
+ void ParseFormatAndCacheFont ()
+ {
+ if (tmgen.m_RichText && IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ GetFormatString (tmgen.m_UTF16Text, format);
+ int maxFontSize = tmgen.m_FontSize ? tmgen.m_FontSize : font->GetFontSize();
+
+ materialCount = 1;
+ for (std::vector<TextFormatChange>::iterator i = format.begin(); i != format.end(); i++)
+ {
+ if (i->flags & kFormatSize)
+ {
+ float size = i->format.size ? i->format.size : font->GetFontSize();
+ if (size > maxFontSize)
+ maxFontSize = (int)size;
+ }
+ if (i->flags & (kFormatMaterial | kFormatImage))
+ {
+ if (i->format.material >= kMaxMaterials || i->format.material < 0)
+ {
+ WarningStringMsg ("Only %d materials are allowed per TextMesh.", kMaxMaterials);
+ i->format.material = 0;
+ }
+ if (i->format.material+1 > materialCount)
+ materialCount = i->format.material+1;
+ }
+ }
+
+ AssertIf (font == NULL);
+ font->CacheFontForText (tmgen.m_UTF16Text.text, tmgen.m_UTF16Text.length, tmgen.m_FontSize, tmgen.m_FontStyle, format);
+
+ if (font->GetFontSize() != 0 && IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1) || font->GetConvertCase() == Font::kDynamicFont)
+ startY = Roundf(font->GetAscent() * ((float)maxFontSize/font->GetFontSize() - 1.0f));
+
+ lineSize = Roundf (font->GetLineSpacing(maxFontSize) * tmgen.m_LineSpacing);
+
+ charOffset = Vector3f (0, startY, 0);
+ }
+
+ void SetupBuffers ()
+ {
+ stringlen = tmgen.m_UTF16Text.length;
+
+ if(stringlen * 4 + 4 > std::numeric_limits<UInt16>::max())
+ {
+ stringlen = (std::numeric_limits<UInt16>::max() / 4) - 1;
+ Assert("String too long for TextMeshGenerator. Cutting off characters.");
+ }
+
+ Mesh *mesh = tmgen.m_Mesh;
+ if (!mesh)
+ {
+ tmgen.m_Mesh = NEW_OBJECT_MAIN_THREAD (Mesh);
+ mesh = tmgen.m_Mesh;
+ mesh->Reset();
+ mesh->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+
+ mesh->SetHideFlags (Object::kHideAndDontSave);
+ mesh->SetHideFromRuntimeStats(true);
+#if UNITY_EDITOR
+ mesh->SetName("TextMesh");
+#endif
+ }
+ else
+ mesh->Clear (true);
+
+ size_t vCount = stringlen * 4 + 4;
+ mesh->ResizeVertices (vCount, TextMeshGenerator2::Vertex::kFormat);
+ mesh->SetVertexColorsSwizzled (gGraphicsCaps.needsToSwizzleVertexColors);
+
+ vertexBegin = (TextMeshGenerator2::Vertex*)mesh->GetVertexDataPointer ();
+ vertex = vertexBegin;
+
+ // Set up the array & pointer for cursor positions.
+ // This is 1 longer than the string, as asking for a cursor position AFTER the last character is a valid request
+ tmgen.m_CursorPos.resize (stringlen + 1);
+ }
+
+ void InsertSpace ()
+ {
+ vertex[0].vert = vertex[1].vert = vertex[2].vert = vertex[3].vert = charOffset;
+ vertex += 4;
+ float w = font->GetCharacterWidth (' ', formatStack.Current().size, formatStack.Current().style);
+ // If this is the first space following a word, we record the last word position for correct wrapping
+ if (spacesLength == 0)
+ endOfLastWord = charOffset.x;
+
+ spacesLength += w;
+
+ // all the vars that makes this a word delimiter
+ startOfWord = characterPos + 1;
+ wordLength = 0;
+ startOfWordPos = charOffset.x + w;
+ // we never wordwrap on a space (no matter how many spaces you have, if it doesn't fit the cursor just stops at the rightmost position)
+
+ charOffset.x += w;
+ }
+
+ void InsertLineBreak ()
+ {
+ // Move the line down
+ vertex[0].vert = vertex[1].vert = vertex[2].vert = vertex[3].vert = charOffset;
+ vertex += 4;
+
+ // if we're right or center aligned, move all character in this line to the right place.
+ tmgen.FixLineOffset (lineLength, &vertexBegin[startOfLine * 4], &tmgen.m_CursorPos[startOfLine], characterPos - startOfLine + 1);
+
+ charOffset.x = 0;
+ charOffset.y += lineSize;
+ maxLineLength = std::max(maxLineLength, lineLength);
+ lineLength = 0;
+ spacesLength = 0;
+ startOfLine = startOfWord = characterPos + 1;
+ }
+
+ void InsertTab ()
+ {
+ if (spacesLength == 0)
+ endOfLastWord = charOffset.x;
+ spacesLength = 0;
+
+ int tab = FloorfToInt (charOffset.x / tmgen.m_TabSize) + 1;
+ if (wordWrap && tab * tmgen.m_TabSize > tmgen.m_WordWrapWidth) {
+ // if we're right or center aligned, move all character in this line to the right place.
+ tmgen.FixLineOffset (lineLength, &vertexBegin[startOfLine * 4], &tmgen.m_CursorPos[startOfLine], characterPos - startOfLine);
+ charOffset.y += lineSize;
+ charOffset.x = lineLength = tmgen.m_TabSize;
+ startOfLine = startOfWord = characterPos + 1;
+ maxLineLength = std::max(maxLineLength, lineLength);
+ } else {
+ // float newPos = tab * m_TabSize;
+
+ if (wordWrap && tab * tmgen.m_TabSize > tmgen.m_WordWrapWidth) {
+ // if we're right or center aligned, move all character in this line to the right place.
+ tmgen.FixLineOffset (lineLength, &vertexBegin[startOfLine * 4], &tmgen.m_CursorPos[startOfLine], characterPos - startOfLine);
+ charOffset.y -= lineSize;
+ charOffset.x = lineLength = tmgen.m_TabSize;
+ startOfLine = startOfWord = characterPos + 1;
+ maxLineLength = std::max(maxLineLength, lineLength);
+ } else {
+ float newPos = tab * tmgen.m_TabSize;
+
+ lineLength = charOffset.x = newPos;
+ }
+ vertex[0].vert = vertex[1].vert = vertex[2].vert = vertex[3].vert = charOffset;
+ vertex += 4;
+
+ // all the vars that makes this a word delimiter
+ startOfWord = characterPos + 1;
+ wordLength = 0;
+ startOfWordPos = charOffset.x;
+ }
+ }
+
+ void InsertCharacter (int c)
+ {
+ Rectf vert, charUv; // rendering info
+ bool flipped;
+ float w; // character width
+
+ font->GetCharacterRenderInfo( c, formatStack.Current().size, formatStack.Current().style, vert, charUv, flipped );
+ w = Roundf (font->GetCharacterWidth (c, formatStack.Current().size, formatStack.Current().style));
+
+ // TODO: can these be non-floats? If they can't shouldn't we be doing this at load rather than when getting each character?
+ // Possibly during serialization as well to conserve size (assume 100 characters * 4 floats = 1600 bytes/font)
+ vert.y = Roundf (vert.y);
+ vert.x = Roundf (vert.x);
+ vert.width = Roundf (vert.width);
+ vert.height = Roundf (vert.height);
+
+ // Add kerning information if present.
+ if( !font->GetKerningValues().empty() && lastChar != -1 )
+ {
+ pair<UnicodeChar, UnicodeChar> kerningPair = pair<UnicodeChar, UnicodeChar>(lastChar, c);
+ Font::KerningValues::iterator foundKerning = font->GetKerningValues().find(kerningPair);
+
+ if (foundKerning != font->GetKerningValues().end())
+ {
+ float kerning = foundKerning->second;
+ if (tmgen.m_FontSize != 0)
+ kerning = Roundf(kerning * tmgen.m_FontSize/(float)font->GetFontSize());
+ charOffset.x += kerning;
+ }
+ }
+
+ // Add quad vertices and UVs
+ vertex[0].vert = charOffset + Vector3f(vert.x, -vert.y, 0);
+ vertex[flipped?2:0].uv = Vector2f (charUv.x, charUv.GetBottom());
+ vertex[1].vert = charOffset + Vector3f(vert.GetRight(), -vert.y, 0);
+ vertex[1].uv = Vector2f (charUv.GetRight(), charUv.GetBottom());
+ vertex[2].vert = charOffset + Vector3f(vert.GetRight(), -vert.GetBottom(), 0);
+ vertex[flipped?0:2].uv = Vector2f (charUv.GetRight(), charUv.y);
+ vertex[3].vert = charOffset + Vector3f(vert.x, -vert.GetBottom(), 0);
+ vertex[3].uv = Vector2f (charUv.x, charUv.y);
+ ColorRGBA32 deviceColor = GfxDevice::ConvertToDeviceVertexColor(formatStack.Current().color);
+ vertex[0].color = vertex[1].color = vertex[2].color = vertex[3].color = deviceColor;
+ vertex += 4;
+
+ // Add two triangles to indices
+ int vertexIndex = characterPos * 4; // since we always emit vertices we know the triangle index from the character index
+ int material = formatStack.Current().material;
+ materialTriangles[material].push_back(vertexIndex + 1);
+ materialTriangles[material].push_back(vertexIndex + 2);
+ materialTriangles[material].push_back(vertexIndex);
+
+ materialTriangles[material].push_back(vertexIndex + 2);
+ materialTriangles[material].push_back(vertexIndex + 3);
+ materialTriangles[material].push_back(vertexIndex);
+
+ // word-wrapping: If we're too large with this letter, we wrap the word to the next line by moving the verts
+ if (wordWrap && charOffset.x + w > tmgen.m_WordWrapWidth /* && wordLength != 0.0f*/) {
+
+ // Special case: if the word we're wrapping is longer than one line alone, we split the word at the offending
+ // character.
+ if (startOfWord == startOfLine) {
+ startOfWord = characterPos;
+ wordLength = 0;
+ startOfWordPos = charOffset.x;
+ endOfLastWord = charOffset.x;
+ }
+
+ // Horizontally align everything up to the last word (the part before the inserted newline)
+ tmgen.FixLineOffset (endOfLastWord, &vertexBegin[startOfLine * 4], &tmgen.m_CursorPos[startOfLine], startOfWord - startOfLine);
+
+ // Move all letters after the inserted newline one line down and to 0 in Xoffset.
+ OffsetCharacters (Vector2f (-startOfWordPos, +lineSize), &vertexBegin[startOfWord * 4], &tmgen.m_CursorPos[startOfWord], characterPos - startOfWord + 1);
+ maxLineLength = std::max(maxLineLength, lineLength);
+
+ // Move the cursor
+ charOffset.x -= startOfWordPos;
+ // The length of the current line is the length of the word we just wrapped down
+ lineLength = wordLength;
+
+ charOffset.y += lineSize;
+
+ // The new line starts at this word
+ startOfLine = startOfWord;
+ startOfWordPos = 0;
+
+ // If we had any accumulated spaces, they don't apply now (as we just word-wrapped)
+ spacesLength = 0;
+ }
+ wordLength += w;
+ // advance the cursor
+ charOffset.x += w;
+ lineLength += w + spacesLength; // Add this letter + any accumulated spaces to the line length
+ spacesLength = 0; // We have now used the accumulated spaces
+ lastChar = c;
+ }
+
+ void ProcessFormatForPosition ()
+ {
+ while (formatChange < format.size() && characterPos >= format[formatChange].startPosition)
+ {
+ characterPos += format[formatChange].skipCharacters;
+
+ // Make sure we don't overflow if the string is capped.
+ if (characterPos > stringlen)
+ {
+ characterPos = stringlen;
+ return;
+ }
+
+ for (int j=0; j<format[formatChange].skipCharacters; j++)
+ {
+ vertex[0].vert = vertex[1].vert = vertex[2].vert = vertex[3].vert = charOffset;
+ vertex += 4;
+ }
+
+ formatStack.PushFormat(format[formatChange]);
+ if ((format[formatChange].flags & (kFormatImage | kFormatPop)) == kFormatImage)
+ {
+ vertex -= 4;
+ float size = formatStack.Current().size;
+ if (font->GetFontSize() != 0 && IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1) || font->GetConvertCase() == Font::kDynamicFont)
+ {
+ if (size == 0)
+ size = font->GetFontSize();
+ size = size / font->GetFontSize() * font->GetAscent();
+ }
+ else
+ size = font->GetAscent();
+ Vector3f imageBase = charOffset + Vector3f(0, font->GetAscent(), 0);
+ Rectf& r = format[formatChange].format.imageRect;
+ float aspect;
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1))
+ aspect = r.width / r.height;
+ else
+ aspect = 1.0f;
+ vertex[0].vert = imageBase + Vector3f(0, -size, 0);
+ vertex[0].uv = Vector2f (r.x, r.GetYMax());
+ vertex[1].vert = imageBase + Vector3f(size * aspect, -size, 0);
+ vertex[1].uv = Vector2f (r.GetXMax(), r.GetYMax());
+ vertex[2].vert = imageBase + Vector3f(size * aspect, 0, 0);
+ vertex[2].uv = Vector2f (r.GetXMax(), r.y);
+ vertex[3].vert = imageBase + Vector3f(0, 0, 0);
+ vertex[3].uv = Vector2f (r.x, r.y);
+ ColorRGBA32 deviceColor = GfxDevice::ConvertToDeviceVertexColor(formatStack.Current().color);
+ vertex[0].color = vertex[1].color = vertex[2].color = vertex[3].color = deviceColor;
+ charOffset += Vector3f(Roundf(size * aspect), 0, 0);
+
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1))
+ lineLength = charOffset.x;
+
+ vertex += 4;
+
+ int vertexIndex = (characterPos-1) * 4; // since we always emit vertices we know the triangle index from the character index
+ int material = formatStack.Current().material;
+ materialTriangles[material].push_back(vertexIndex + 1);
+ materialTriangles[material].push_back(vertexIndex + 2);
+ materialTriangles[material].push_back(vertexIndex);
+
+ materialTriangles[material].push_back(vertexIndex + 2);
+ materialTriangles[material].push_back(vertexIndex + 3);
+ materialTriangles[material].push_back(vertexIndex);
+ }
+ formatChange++;
+ }
+ }
+
+ void ProcessString ()
+ {
+ for (characterPos=0; characterPos <= stringlen; characterPos++)
+ {
+ ProcessFormatForPosition ();
+
+ // all the code becomes a lot simpler if we pretend we have a newline at the end.
+ // If not, the last line becomes a special-case requiring lots of code duplication.
+ int c;
+ if (characterPos < stringlen)
+ c = tmgen.m_UTF16Text[characterPos];
+ else
+ c = '\n';
+
+#if TEXTDEBUG
+ printf_console ("%c", c);
+#endif
+
+ // store the character offset into the cursor position array
+ tmgen.m_CursorPos[characterPos] = Vector2f (charOffset.x, charOffset.y - startY);
+
+ switch (c) {
+ case '\n':
+ InsertLineBreak();
+ break;
+ case ' ':
+ InsertSpace();
+ break;
+ case '\t':
+ InsertTab();
+ break;
+ default:
+ InsertCharacter(c);
+ break;
+ }
+ }
+#if TEXTDEBUG
+ printf_console ("\n");
+#endif
+ }
+
+ Rectf GetBounds ()
+ {
+ // Get the bounding volume.
+ // Y coordinates are 0 - charOffset.y (which has been moved down one line due to the fake \n we inserted.
+ Rectf r;
+ r.y = 0;
+ r.SetBottom(charOffset.y-startY);
+ switch (tmgen.m_Alignment) {
+ case kLeft:
+ r.x = 0;
+ r.width = Roundf (maxLineLength);
+ break;
+ case kRight:
+ r.x = -Roundf (maxLineLength);
+ r.width = Abs(r.x);
+ break;
+ case kCenter:
+ r.x = -Roundf (maxLineLength * .5f);
+ r.width = Roundf (maxLineLength);
+ break;
+ default:
+ break;
+ }
+ return r;
+ }
+
+ void SetMeshData ()
+ {
+ Mesh* mesh = tmgen.m_Mesh;
+ mesh->SetSubMeshCount(materialCount);
+ for (int i=0; i<materialCount; i++)
+ {
+ if (materialTriangles[i].size())
+ mesh->SetIndicesComplex(&materialTriangles[i][0], materialTriangles[i].size(), i, kPrimitiveTriangles, Mesh::k16BitIndices);
+ }
+
+ AABB bounds;
+ bounds.SetCenterAndExtent(Vector3f(tmgen.m_Rect.x, tmgen.m_Rect.y, 0.0f), Vector3f::zero);
+ bounds.Encapsulate (Vector3f(tmgen.m_Rect.GetXMax(), tmgen.m_Rect.GetYMax(), 0.0f));
+ mesh->SetLocalAABB (bounds);
+
+ mesh->SetChannelsDirty (mesh->GetAvailableChannels (), false );
+ }
+};
+
+void TextMeshGenerator2::Generate ()
+{
+ TextMeshGenerationData data (*this);
+ data.ParseFormatAndCacheFont ();
+ data.SetupBuffers ();
+
+ // Allocate temp memory for the first material (which contains all triangles by default),
+ // to avoid unnecessary allocations.
+ UInt16 *triangles = NULL;
+ int bufferSize = data.GetStringLength() * 6;
+ ALLOC_TEMP(triangles, UInt16, bufferSize);
+ data.GetTriangles()[0].assign_external (triangles, triangles + bufferSize);
+ data.GetTriangles()[0].resize_uninitialized(0);
+
+ data.ProcessString ();
+
+ m_Rect = data.GetBounds ();
+ data.SetMeshData ();
+}
+
+Font *TextMeshGenerator2::GetFont () const {
+ Font *f = m_Font;
+ if (!f) {
+ if (!gDefaultFont)
+ gDefaultFont = GetBuiltinResource<Font> (kDefaultFontName);
+ return gDefaultFont;
+ }
+ else {
+ return f;
+ }
+}
+
+#if UNITY_EDITOR
+void TextMeshGenerator2::CleanCache (const UTF16String &text)
+{
+ for (Generators::iterator i = s_Generators.begin(); i != s_Generators.end();)
+ {
+ TextMeshGenerator2 *gen = *i;
+ if (gen->m_UTF16Text == text)
+ {
+ delete gen;
+ i = s_Generators.erase(i);
+ }
+ else
+ i++;
+ }
+}
+#endif
diff --git a/Runtime/IMGUI/TextMeshGenerator2.h b/Runtime/IMGUI/TextMeshGenerator2.h
new file mode 100644
index 0000000..71b5cc6
--- /dev/null
+++ b/Runtime/IMGUI/TextMeshGenerator2.h
@@ -0,0 +1,96 @@
+#ifndef TEXTMESH
+#define TEXTMESH
+
+#include "TextUtil.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Math/Rect.h"
+
+class ChannelAssigns;
+
+#if UNITY_LINUX
+class Font;
+#endif
+
+//#include "DLAllocator.h"
+
+// Pixel-correct textmesh generator
+class TextMeshGenerator2 {
+ public:
+// DECLARE_FORWARD_MAINTHREAD_ALLOCATOR_MEMBER_NEW_DELETE
+
+ struct Vertex {
+ enum { kFormat = VERTEX_FORMAT3(Vertex, Color, TexCoord0) };
+ Vector3f vert;
+ ColorRGBA32 color;
+ Vector2f uv;
+ };
+
+ // The global getter function
+ static TextMeshGenerator2 &Get (const UTF16String &text, Font *font, TextAnchor anchor, TextAlignment alignment, float wordWrapWidth, float tabSize, float lineSpacing, bool richText, bool pixelcorrect, ColorRGBA32 color, int fontSize = 0, int fontStyle = 0);
+
+ /// Global update function - this is called once per frame & cleans up any meshes not used since then.
+ static void GarbageCollect ();
+
+ /// Delete all text meshes
+ static void Flush ();
+
+ static void CleanCache (const UTF16String &text);
+ // Individual textmesh objects here on down:
+
+ /// Render this text thing inside Rect.
+ // See this code for the correct stup instructions for rendering text with RenderRaw
+ void Render (const Rectf &rect, const ChannelAssigns& channels);
+
+ /// Just call the rendering on the GPU - don't set up a matrix to position the text or any such crap. Just submit the mesh.
+ void RenderRaw (const ChannelAssigns& channels);
+
+ /// Get the modelview offset to position this object's text within rect.
+ Vector2f GetTextOffset (const Rectf &rect);
+ // Get the font to use - this gets the default font if none set
+ Font *GetFont () const;
+ // Get the g
+ Mesh *GetMesh () const { return m_Mesh; }
+
+ /// Get the cursor position
+ Vector2f GetCursorPosition (const Rectf &screenRect, int cursor);
+
+ int GetCursorIndexAtPosition (const Rectf &screenRect, const Vector2f &pos);
+
+ /// Get the size.
+ Vector2f GetSize () const { return Vector2f (m_Rect.width, m_Rect.height); }
+
+ private:
+
+ TextMeshGenerator2 (const UTF16String &text, Font *font, TextAnchor anchor, TextAlignment alignment, float wordWrapWidth, float tabSize, float lineSpacing, bool richText, bool pixelcorrect, ColorRGBA32 color, int fontSize, int fontStyle);
+ ~TextMeshGenerator2 ();
+ void FixLineOffset (float lineWidth, Vertex *firstChar, Vector2f *firstCursor, int count);
+
+ // Generate the mesh stuff from the stored values
+ void Generate ();
+
+ float Roundf (float x);
+
+ // Width to word-wrap against. If 0, no word-wrapping is applied.
+ float m_WordWrapWidth;
+
+ TextAnchor m_Anchor; //< The text anchor.
+ TextAlignment m_Alignment;
+ float m_LineSpacing;
+ float m_TabSize; //< The pixel tab size
+ bool m_TabUsed; //< Is the tab used in the string?
+ bool m_RichText;
+ PPtr<Font> m_Font; //< font to use
+ int m_FontSize;
+ int m_FontStyle;
+ ColorRGBA32 m_Color;
+ bool m_PixelCorrect;
+ Rectf m_Rect;
+ UTF16String m_UTF16Text;
+
+ Mesh* m_Mesh; //< The mesh that contains the generated characters
+ std::vector<Vector2f/*, main_thread_allocator<Vector2f>*/ > m_CursorPos; //< Cursor positions for each character
+ int m_LastUsedFrame; //< Has this textmeshgenerator been used this frame
+
+ friend class TextMeshGenerationData;
+};
+#endif
diff --git a/Runtime/IMGUI/TextUtil.cpp b/Runtime/IMGUI/TextUtil.cpp
new file mode 100644
index 0000000..4d8289a
--- /dev/null
+++ b/Runtime/IMGUI/TextUtil.cpp
@@ -0,0 +1,277 @@
+#include "UnityPrefix.h"
+#include "TextUtil.h"
+#include "Runtime/Filters/Misc/Font.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+using namespace std;
+
+class Font;
+
+namespace TextUtil_Static
+{
+static Font *s_DefaultFont = NULL;
+} // namespace TextUtil_Static
+static PPtr <Font> s_CurrentFont = NULL;
+static PPtr<Material> s_CurrentMaterial = NULL;
+
+void SetTextFont (Font *font) { s_CurrentFont = font; }
+void SetDrawTextMaterial (Material *m) { s_CurrentMaterial = m; }
+Material *GetDrawTextMaterial () { return s_CurrentMaterial; }
+
+extern "C" UInt16* AllocateUTF16Memory(int bytes)
+{
+ return (UInt16*)UNITY_MALLOC(kMemUTF16String,bytes);
+}
+
+#if ENABLE_SCRIPTING
+
+#if ENABLE_MONO
+UTF16String::UTF16String (MonoString *sourceString) {
+ if (sourceString != SCRIPTING_NULL && mono_string_length (sourceString) != 0)
+ {
+ text = mono_string_chars (sourceString);
+ length = mono_string_length (sourceString);
+ owns = false;
+ }
+ else
+ {
+ owns = false;
+ text = NULL;
+ length = 0;
+ }
+}
+#elif UNITY_WINRT
+UTF16String::UTF16String (ScriptingStringPtr sourceString)
+{
+ if (sourceString != SCRIPTING_NULL)
+ {
+ Platform::String^ utf16String = safe_cast<Platform::String^>(sourceString);
+ length = utf16String->Length();
+ int size = length * sizeof(UInt16);
+ text = (UInt16*)UNITY_MALLOC(kMemUTF16String, size);
+ memcpy(text, utf16String->Data(), size);
+ owns = true;
+ }
+ else
+ {
+ owns = false;
+ text = NULL;
+ length = 0;
+ }
+}
+#else
+UTF16String::UTF16String (ScriptingStringPtr sourceString)
+{
+ if (sourceString != SCRIPTING_NULL)
+ {
+ std::string str = scripting_cpp_string_for(sourceString);
+ text = AllocateUTF16Memory(str.size()*sizeof(UInt16));
+ ConvertUTF8toUTF16 (str.c_str(), str.size(), text, length);
+ owns = true;
+ }
+ else
+ {
+ owns = false;
+ text = NULL;
+ length = 0;
+ }
+}
+#endif
+
+#endif
+
+extern "C" void UTF16String_TakeOverPreAllocatedUTF16Bytes(UTF16String* utfString, UInt16* bytes, int length)
+{
+ utfString->TakeOverPreAllocatedUTF16Bytes(bytes,length);
+}
+
+void UTF16String::TakeOverPreAllocatedUTF16Bytes(UInt16* bytes, int size)
+{
+ if (owns)
+ UNITY_FREE(kMemUTF16String,text);
+ text = bytes;
+ length = size;
+ owns = true;
+}
+
+void UTF16String::InitFromCharPointer (const char* str)
+{
+ int slen = strlen (str);
+ if (slen != 0) {
+ text = AllocateUTF16Memory(slen*sizeof(UInt16));
+ ConvertUTF8toUTF16 (str, slen, text, length);
+ owns = true;
+ }
+ else {
+ text = NULL;
+ length = 0;
+ owns = false;
+ }
+}
+
+UTF16String::UTF16String (const char* str) {
+ InitFromCharPointer(str);
+}
+
+UTF16String::~UTF16String () {
+ if (owns)
+ UNITY_FREE(kMemUTF16String,text);
+}
+
+UTF16String::UTF16String (const UTF16String &other) {
+ if (other.length != 0)
+ {
+ length = other.length;
+ text =(UInt16*)UNITY_MALLOC(kMemUTF16String,length*sizeof(UInt16));
+ memcpy(text,other.text,sizeof (UInt16) * length);
+ owns = true;
+ }
+ else
+ {
+ length = 0;
+ text = NULL;
+ owns = false;
+ }
+}
+
+
+void UTF16String::CopyString (const UTF16String &other)
+{
+ if (owns)
+ UNITY_FREE(kMemUTF16String,text);
+ if (other.length != 0)
+ {
+ length = other.length;
+ text =(UInt16*)UNITY_MALLOC(kMemUTF16String,length*sizeof(UInt16));
+ memcpy(text,other.text,sizeof (UInt16) * length);
+ owns = true;
+ }
+ else
+ {
+ length = 0;
+ text = NULL;
+ owns = false;
+ }
+}
+
+#if ENABLE_SCRIPTING
+
+#if ENABLE_MONO
+void UTF16String::BorrowString( ScriptingString *sourceString )
+{
+ if (owns)
+ UNITY_FREE(kMemUTF16String,text);
+
+ if (sourceString != NULL && mono_string_length (sourceString) != 0)
+ {
+ text = mono_string_chars (sourceString);
+ length = mono_string_length (sourceString);
+ owns = false;
+ }
+ else
+ {
+ owns = false;
+ text = NULL;
+ length = 0;
+ }
+}
+#elif UNITY_WINRT
+void UTF16String::BorrowString(ScriptingStringPtr sourceString)
+{
+ if (owns)
+ UNITY_FREE(kMemUTF16String,text);
+
+ if (sourceString != SCRIPTING_NULL)
+ {
+ Platform::String^ utf16String = safe_cast<Platform::String^>(sourceString);
+ length = utf16String->Length();
+ int size = length * sizeof(UInt16);
+ text = (UInt16*)UNITY_MALLOC(kMemUTF16String, size);
+ memcpy(text, utf16String->Data(), size);
+ owns = true;
+ }
+ else
+ {
+ owns = false;
+ text = NULL;
+ length = 0;
+ }
+}
+#endif
+
+void UTF8ToUTF16String (const char* src, UTF16String& dst)
+{
+ UTF16String tmp(src);
+ dst.TakeOverPreAllocatedUTF16Bytes(tmp.text, tmp.length);
+ tmp.owns = false;
+}
+
+ScriptingStringPtr UTF16String::GetScriptingString ()
+{
+ if (!length || !text)
+ return SCRIPTING_NULL;
+
+#if ENABLE_MONO
+ UInt16* buffer = new UInt16[length+1];
+ memcpy(buffer, text, length * 2);
+ buffer[length] = 0;
+ MonoString* retval = MonoStringNewUTF16 ((const wchar_t*) buffer);
+ delete[] buffer;
+ return retval;
+#elif UNITY_FLASH || UNITY_WINRT
+ std::vector<char> tempStr;
+ tempStr.resize(length * 4);
+ int outLength;
+ ConvertUTF16toUTF8 (text, length, &tempStr[0], outLength);
+ tempStr[outLength]=0;
+ return scripting_string_new((char const*)&tempStr[0]);
+#endif
+}
+
+#endif
+
+Font*
+GetTextFont ()
+{
+ using namespace TextUtil_Static;
+ Font* font = s_CurrentFont;
+ if (font == NULL) {
+ // Make sure we have default resource
+ if (s_DefaultFont == NULL) {
+ s_DefaultFont = GetBuiltinResource<Font> (kDefaultFontName);
+ if (!s_DefaultFont || !s_DefaultFont->GetMaterial()) {
+ LogString ("Couldn't load default font or font material!");
+ }
+ }
+ font = s_DefaultFont;
+ }
+ return font;
+}
+
+
+#include "TextMeshGenerator2.h"
+static TextMeshGenerator2 &GetMeshGen (const UTF16String &text, Font *font, TextAnchor align, float width) {
+ const float tabSize = 16;
+ return TextMeshGenerator2::Get (text, font, align, kAuto, width, tabSize, 1.0f, false, true, ColorRGBA32(0xffffffff));
+}
+float CalcTextHeight (const UTF16String &text, float width) {
+ TextMeshGenerator2 &gen = GetMeshGen (text, (Font*)GetTextFont(), kDontCare, width);
+ return gen.GetSize().y;
+}
+Vector2f CalcTextSize (const UTF16String &text) {
+ TextMeshGenerator2 &gen = GetMeshGen (text, (Font*)GetTextFont(), kDontCare, 0);
+ return gen.GetSize ();
+}
+Vector2f GetCursorPosition (const Rectf &screenRect, const UTF16String &text, TextAnchor align, bool wordWrap, int textPosition) {
+ float width = wordWrap ? screenRect.Width() : 0;
+ TextMeshGenerator2 &gen = GetMeshGen (text, (Font*)GetTextFont(), align, width);
+ return gen.GetCursorPosition(screenRect, textPosition);
+}
diff --git a/Runtime/IMGUI/TextUtil.h b/Runtime/IMGUI/TextUtil.h
new file mode 100644
index 0000000..2ddc8be
--- /dev/null
+++ b/Runtime/IMGUI/TextUtil.h
@@ -0,0 +1,110 @@
+#ifndef TEXTUTIL_H
+#define TEXTUTIL_H
+
+namespace Unity { class Material; }
+class Transform;
+class Camera;
+class Vector3f;
+class Matrix4x4f;
+class Object;
+class Texture;
+class Shader;
+class Vector2f;
+class Font;
+
+//#include <string>
+#include <set>
+#include <vector>
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Rect.h"
+
+using namespace Unity;
+
+enum TextAlignment {
+ kLeft,
+ kCenter,
+ kRight,
+ kAuto,
+};
+
+enum TextAnchor {
+ kUpperLeft,
+ kUpperCenter,
+ kUpperRight,
+ kMiddleLeft,
+ kMiddleCenter,
+ kMiddleRight,
+ kLowerLeft,
+ kLowerCenter,
+ kLowerRight,
+ kDontCare ///< Special case for getting text mesh generators: The anchoring used for the text doesn't modify the size of the generated text, so if you just want to query for it you don't care about anchoring
+};
+
+enum TextClipping {
+ /// Text flows freely outside the element.
+ kOverflow = 0,
+ /// Text gets clipped to be inside the element.
+ kClip = 1,
+};
+
+struct MonoString;
+
+// Opaque UTF16 string representation
+struct UTF16String
+{
+#if ENABLE_SCRIPTING
+ explicit UTF16String (ScriptingStringPtr sourceSting);
+#endif
+ UTF16String () {text = NULL; length = 0; owns = false;}
+ explicit UTF16String (const char* str);
+ UTF16String (const UTF16String &other);
+ ~UTF16String ();
+
+ UInt16 operator [] (int index) const { return text[index]; }
+ friend bool operator == (const UTF16String& lhs, const UTF16String& rhs)
+ {
+ using namespace std;
+ if (lhs.length != rhs.length)
+ return false;
+ return (rhs.text == NULL || memcmp(lhs.text, rhs.text,lhs.length * sizeof (UInt16)) == 0);
+ }
+
+ UInt16* text;
+ int length;
+ bool owns;
+
+ ScriptingStringPtr GetScriptingString ();
+ void CopyString (const UTF16String &other);
+#if ENABLE_MONO || UNITY_WINRT
+ void BorrowString (ScriptingStringPtr sourceString);
+#endif
+ void TakeOverPreAllocatedUTF16Bytes(UInt16* bytes, int size);
+private:
+ void InitFromCharPointer(const char* str);
+};
+
+void UTF8ToUTF16String (const char* src, UTF16String& dst);
+
+Vector2f GetCursorPosition (const Rectf &screenRect, const UTF16String &text, TextAnchor align, bool wordWrap, int textPosition);
+
+// Set a custom font to use.
+// If null, it will use the default resource font
+void SetTextFont (Font *font);
+Font *GetTextFont ();
+
+// Calculate size of a piece of text
+Vector2f CalcTextSize (const UTF16String &text);
+
+/// Calculate height of text word-wrapped to width
+float CalcTextHeight (const UTF16String &, float width);
+
+/// Set a custom shader to use for drawing text.
+/// If null, the material set in the font will be used
+void SetDrawTextMaterial (Material *m);
+Material *GetDrawTextMaterial ();
+
+
+
+
+#endif
diff --git a/Runtime/Input/GetInput.h b/Runtime/Input/GetInput.h
new file mode 100644
index 0000000..bccbe2b
--- /dev/null
+++ b/Runtime/Input/GetInput.h
@@ -0,0 +1,288 @@
+#ifndef GETINPUT_H
+#define GETINPUT_H
+
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Math/Color.h"
+#include "PlatformDependent/iPhonePlayer/APN.h"
+#include <string>
+
+/* Mac os x input handling
+
+--- Joystick input:
+* Uses HID
+
+--- Keyboard input:
+* Uses HID for keyboard events
+* Uses normal event passing for Input.inputString. The applications are responsible for passing the events on to GetInput.cpp
+
+--- Mouse input
+
+HID gives the nicest mouse delta, so we always try to use it.
+BUT Some mice don't have mouse delta from HID and USB overdrive snatches it away.
+
+So we must provide fallbacks. At startup hid mouse delta is disabled, as soon any hid mouse delta
+is reported, we switch to using only hid mouse delta.
+
+The fallback look different for all editor/player/webplayer
+
+Some facts:
+- In a cocoa application, Carbon events work only in fullscreen mode. In window mode they get snatched.
+ (In Safari carbon events fail, in Firefox they work)
+- In editor/player we can access all cocoa events directly. In the web player we can't.
+- Cocoa/Carbon mouse delta is non-accelerated and supports screen boundary deltas
+- getmouse based is accelerated and doesnt support screen boundaries
+
+Which leads to the following workarounds:
+
+Editor
+-> NSApp sendEvents forwards all mouse moved NSEvents to InputProcessMouseMove
+-> Mouse down is generated from GameView
+
+Standalone
+-> NSApp sendEvents forwards all mouse moved NSEvents to InputProcessMouseMove
+-> Mouse down forwarded from NSApp
+
+Web plugin windowed
+-> getmouse based mouse delta (Fails at screen borders)
+-> mouse down comes from the plugin events (No right or middle mouse buttons)
+
+Web plugin fullscreen
+-> carbon event based mouse delta
+-> carbon event based mouse down
+*/
+
+void GetJoystickNames (std::vector<std::string> &names);
+std::string GetJoystickAxisName(int joyNum,int axis);
+std::string GetNiceKeyname(int key);
+
+void InputShutdown ();
+
+void InputReadMousePosition ();
+void InputReadMouseState();
+void InputReadKeyboardState();
+void InputReadJoysticks();
+
+void ClearInputEvents ();
+
+// Clears all input axes, keydowns, mouse buttons and sets up how event handlers
+void ResetInput ();
+void ResetInputAfterPause ();
+
+namespace Unity
+{
+ class GameObject;
+};
+
+// touchscreen
+struct Touch
+{
+#if ENABLE_NEW_EVENT_SYSTEM
+ SInt32 id; // -1 = LMB, -2 = RMB, -3 = MMB, 0+ = actual touch IDs.
+ Vector2f pos;
+ Vector2f rawPos;
+ Vector2f oldPos;
+ Vector2f deltaPos;
+ Vector2f deltaScroll;
+ Vector3f worldPos;
+ float deltaTime; // time between two events that came to us during this
+ // touch/drag gesture. 1.0f = 1 second.
+ UInt32 tapCount;
+ UInt32 phase;
+
+ Unity::GameObject* hover; // Object the mouse is hovering over
+ Unity::GameObject* press; // Object that was pressed on via mouse or touch
+
+ //bool eligibleForClick;
+
+ enum TouchPhase
+ {
+ kTouchBegan = 0,
+ kTouchMoved = 1,
+ kTouchStationary = 2,
+ kTouchEnded = 3,
+ kTouchCanceled = 4
+ };
+
+ Touch () : hover(NULL), press(NULL) {}
+#else
+ UInt32 id;
+ Vector2f pos;
+ Vector2f rawPos;
+ Vector2f deltaPos;
+ float deltaTime; // time between two events that came to us during this
+ // touch/drag gesture. 1.0f = 1 second.
+ UInt32 tapCount;
+ UInt32 phase;
+#endif
+};
+
+struct Acceleration
+{
+ Vector3f acc;
+ float deltaTime;
+};
+
+Vector3f GetAcceleration ();
+size_t GetAccelerationCount ();
+void GetAcceleration (size_t index, Acceleration& acceleration);
+size_t GetTouchCount ();
+#if ENABLE_NEW_EVENT_SYSTEM
+Touch* GetTouch (unsigned index);
+#else
+bool GetTouch (unsigned index, Touch& touch);
+#endif
+bool IsMultiTouchEnabled();
+void SetMultiTouchEnabled(bool flag = true);
+
+// Must match DeviceOrientation in UnityEngineInput.txt
+enum DeviceOrientation
+{
+ // The orientation of the device cannot be determined.
+ DEVICE_ORIENTATION_UNKNOWN = 0,
+ // The device is in portrait mode, with the device held upright and the home button at the bottom.
+ DEVICE_ORIENTATION_PORTRAIT = 1,
+ // The device is in portrait mode but upside down, with the device held upright and the home button at the top.
+ DEVICE_ORIENTATION_PORTRAITUPSIDEDOWN = 2,
+ // The device is in landscape mode, with the device held upright and the home button on the right side.
+ DEVICE_ORIENTATION_LANDSCAPELEFT = 3,
+ // The device is in landscape mode, with the device held upright and the home button on the left side.
+ DEVICE_ORIENTATION_LANDSCAPERIGHT = 4,
+ // The device is held parallel to the ground with the screen facing upwards.
+ DEVICE_ORIENTATION_FACEUP = 5,
+ // The device is held parallel to the ground with the screen facing downwards.
+ DEVICE_ORIENTATION_FACEDOWN = 6
+};
+unsigned int GetOrientation (); // device orientation
+
+void Vibrate ();
+bool IsApplicationGenuine ();
+bool IsApplicationGenuineAvailable ();
+void PlayMovie (std::string const& path, ColorRGBA32 const& backgroundColor,
+ UInt32 controlMode, UInt32 scalingMode, bool pathIsUrl);
+
+Vector3f GetAcceleration (int controllerID);
+Quaternionf GetRotation(int controllerID);
+Vector3f GetPosition(int controllerID);
+bool IsCompensatingSensors();
+void SetCompensatingSensors(bool val);
+Vector3f GetGyroRotationRate(int idx);
+bool IsGyroAvailable();
+Vector3f GetGyroRotationRateUnbiased(int idx);
+Vector3f GetGravity(int idx);
+Vector3f GetUserAcceleration(int idx);
+Quaternionf GetAttitude(int idx);
+bool IsGyroEnabled(int idx);
+void SetGyroEnabled(int idx, bool enabled);
+float GetGyroUpdateInterval(int idx);
+void SetGyroUpdateInterval(int idx, float interval);
+int GetGyro();
+size_t GetLocalNotificationCount();
+iPhoneLocalNotification* CopyLocalNotification(unsigned index);
+size_t GetRemoteNotificationCount();
+iPhoneRemoteNotification* CopyRemoteNotification(unsigned index);
+void ClearLocalNotifications();
+void ClearRemoteNotifications();
+
+#if UNITY_OSX
+ //scaling value for mouse deltas
+ #define kHIDMouseDeltaScale 0.5
+ #define kCocoaMouseDeltaScale 0.25
+
+ struct InputEvent;
+
+ void InputInit();
+ void CleanupMouseInputHandlers();
+ int MapCocoaScancodeToSDL (int inKey);
+ void InputProcessMacOSEvent(EventRecord *evt);
+ void InputProcessMouseMove (float x, float y);
+ Vector2f MacOSGlobalPointToLocal (Point p);
+ void ResetUnicodeInput();
+ int ConvertCocoaModifierFlags (UInt32 modFlags);
+ void SanitizeKeyEvent( InputEvent& event);
+ void ProcessCocoaKeyInput(InputEvent &ie, CFStringRef characters, UInt32 keyCode);
+ void GetKeyStateFromFlagsChangedEvent (UInt32 mod);
+ void InputGetKeyboardIMEMode();
+
+ #if WEBPLUG
+ typedef struct _NPCocoaEvent NPCocoaEvent;
+
+ void InputProcessMacOSEventHIDWorkaround(EventRecord *evt);
+ void InputProcessCocoaEvent(NPCocoaEvent *event, bool letBrowserHandleTextComposition);
+ #endif
+#elif UNITY_IPHONE || UNITY_ANDROID
+ void InputInit();
+ // Process any input - call once per main loop
+ void InputProcess();
+#elif UNITY_BB10
+ void InputInit();
+ // Process any input - call once per main loop
+ void InputProcess();
+ void AddKeyEvent(int key, int flag);
+ void HandleEvents();
+ int GetFrameCount();
+#elif UNITY_TIZEN
+ void InputInit();
+ // Process any input - call once per main loop
+ void InputProcess();
+#elif UNITY_LINUX
+ #define kX11MouseDeltaScale 0.25
+ void InputInit();
+ // Process any input - call once per main loop
+ void InputProcess();
+ void processKey( void* event, bool down );
+ void processMouseButton( void* event, bool down );
+ void processMouseMovedEvent( void* event );
+ void processMouseWarp( void* event );
+#elif UNITY_WII
+ #include <revolution/kpad.h>
+
+ void InputInit();
+ // Process any input - call once per main loop
+ void InputProcess();
+
+#elif UNITY_WIN
+
+ // Initialize input with application's main window
+ void InputInit( HWND window );
+
+ void InputInitWindow (HWND window);
+ void InputShutdownWindow ();
+
+ // Process any input - call once per main loop
+ void InputProcess ();
+
+ // Activate input - call when application goes to foreground
+ void InputActivate();
+ // Passivate input - call when application goes to background
+ void InputPassivate();
+ // Window/fullscreen has changed
+ void InputSetWindow(HWND window, bool fullscreen);
+
+ LRESULT ProcessInputMessage(HWND window, UINT message, WPARAM wParam, LPARAM lParam, BOOL &handled);
+
+ int InputKeycodeFromVKEY( int vkey );
+ bool GetMousePresent();
+ void SetMousePresent(const bool mousePresent);
+
+ #if UNITY_EDITOR
+ void SetEditorMouseOffset( int x, int y, HWND window );
+ void GetEditorMouseOffset(int *x, int *y, HWND *window = NULL);
+ #endif
+#elif UNITY_XENON
+
+ void InputInit();
+ void InputProcess ();
+
+#elif UNITY_PS3
+ int InputInit();
+ int InputProcess ();
+#elif UNITY_PEPPER
+ void InputInit ();
+#elif UNITY_FLASH || UNITY_WEBGL
+#else
+#error "Unknown platform"
+#endif
+
+#endif
diff --git a/Runtime/Input/InputAxis.cpp b/Runtime/Input/InputAxis.cpp
new file mode 100644
index 0000000..62cf9d1
--- /dev/null
+++ b/Runtime/Input/InputAxis.cpp
@@ -0,0 +1,226 @@
+#include "UnityPrefix.h"
+#include "InputAxis.h"
+#include "TimeManager.h"
+#include "InputManager.h"
+
+using namespace std;
+
+InputAxis::InputAxis () {
+ positiveButton = 0;
+ negativeButton = 0;
+ altPositiveButton = 0;
+ altNegativeButton = 0;
+ joyNum = 0;
+ type=kAxisButton;
+ dead = 0.001f;
+ gravity = 0.0;
+ sensitivity= .1f;
+ invert = false;
+ snap = false;
+ value = 0.0f;
+ axis = 0;
+ descriptiveName="";
+ descriptiveNegativeName="";
+}
+
+InputAxis::InputAxis (const string &name) {
+ positiveButton = 0;
+ negativeButton = 0;
+ altPositiveButton = 0;
+ altNegativeButton = 0;
+ joyNum = 0;
+ type=kAxisButton;
+ dead = 0.001f;
+ gravity = 0.0;
+ sensitivity= .1f;
+ invert = false;
+ snap = false;
+ value = 0.0f;
+ axis = 0;
+ m_Name = name;
+ descriptiveName="";
+ descriptiveNegativeName="";
+}
+
+void InputAxis::DoGravity (float time) {
+ // Handle gravity
+ if (gravity) {
+ if (value > 0) {
+ value -= gravity * time;
+ if (value < 0)
+ value = 0;
+ } else if (value < 0) {
+ value += gravity * time;
+ if (value > 0)
+ value = 0;
+ }
+ }
+}
+
+void InputAxis::Update () {
+ float time = GetDeltaTime();
+ Vector2f pos;
+#if !ENABLE_NEW_EVENT_SYSTEM
+ Vector3f delta;
+#endif
+
+// Vector2f pos;
+ int posFlag, negFlag;
+
+ if (invert)
+ value = -value;
+
+ switch (type) {
+ case kAxisButton:
+ posFlag = GetInputManager().GetKey (positiveButton)||GetInputManager().GetKey (altPositiveButton);
+ negFlag = GetInputManager().GetKey (negativeButton)||GetInputManager().GetKey (altNegativeButton);
+ rawValue = 0.0F;
+
+ // Lock if both up and down are held
+ if (!(posFlag && negFlag)) {
+ if (posFlag) {
+ if (snap && value < 0.0)
+ value = 0.0;
+ else
+ value += sensitivity * time;
+ if (value < 0.0)
+ value += gravity * time;
+
+ value = min (1.0F, value);
+ rawValue = 1.0F;
+
+ }
+ else if (negFlag) {
+ if (snap && value > 0.0)
+ value = 0.0;
+ else
+ value -= sensitivity * time;
+ if (value > 0.0)
+ value -= gravity * time;
+
+ value = max (-1.0F, value);
+ rawValue = -1.0F;
+
+ } else
+ DoGravity (time);
+ }
+
+ break;
+
+ case kAxisMouse:
+#if ENABLE_NEW_EVENT_SYSTEM
+ if (axis == 0)
+ value = GetInputManager().GetMouseDelta().x;
+ else if (axis == 1)
+ value = GetInputManager().GetMouseDelta().y;
+ else
+ value = GetInputManager().GetMouseScroll().x;
+#else
+ delta = GetInputManager().GetMouseDelta ();
+ if (axis == 0)
+ value = delta.x;
+ else if (axis == 1)
+ value = delta.y;
+ else
+ value = delta.z;
+#endif
+
+ rawValue = value;
+ value *= sensitivity;
+
+ break;
+ case kAxisJoystick:
+ value = GetInputManager().GetJoystickPosition (joyNum, axis);
+ rawValue = value;
+ value *= sensitivity;
+
+
+ if (value > 1.0F)
+ value = 1.0F;
+ else if (value < -1.0F)
+ value = -1.0F;
+ else if (value < dead && value > -dead)
+ value = 0.0F;
+ else if (value > 0.0F)
+ value = Lerp (0.0F, 1.0F, (value - dead) / (1.0F - dead));
+ else
+ value = Lerp (0.0F, -1.0F, (-value - dead) / (1.0F - dead));
+ break;
+ }
+
+ if (invert)
+ {
+ value = -value;
+ rawValue = -rawValue;
+ }
+}
+
+
+void InputAxis::MakeAnalogKey (int pos, int neg, int altpos, int altnegpos) {
+ positiveButton = pos;
+ negativeButton = neg;
+ altPositiveButton = altpos;
+ altNegativeButton = altnegpos;
+ type = kAxisButton;
+ sensitivity = 3;
+ gravity = 3;
+ snap = true;
+}
+
+void InputAxis::MakeButton (int button, int altbutton) {
+ positiveButton = button;
+ negativeButton = 0;
+ altPositiveButton = altbutton;
+ altNegativeButton = 0;
+ type = kAxisButton;
+ sensitivity = 1000;
+ gravity = 1000;
+ snap = false;
+}
+
+void InputAxis::MakeMouse (int a) {
+ type = kAxisMouse;
+ axis = a;
+ dead = 0.0;
+ sensitivity = 0.1f;
+}
+
+void InputAxis::MakeJoystick (int a) {
+ type = kAxisJoystick;
+ axis = a;
+ sensitivity = 1.0F;
+ dead = 0.19F;
+ gravity = 0.0F;
+ snap = false;
+ invert = false;
+}
+
+UnityStr InputAxis::GetDescriptiveName(bool neg) {
+ if(neg)
+ if(descriptiveNegativeName.length())
+ return descriptiveNegativeName;
+ else
+ if(descriptiveName.length())
+ return descriptiveName+" (-)";
+ else
+ return m_Name+" (-)";
+ else
+ if(descriptiveName.length())
+ if(negativeButton&&!descriptiveNegativeName.length())
+ return descriptiveName+" (+)";
+ else
+ return descriptiveName;
+ else
+ if(negativeButton)
+ return m_Name+" (+)";
+ else
+ return m_Name;
+}
+
+float InputAxis::GetValueRaw () const
+{
+ if (type == kAxisButton)
+ return rawValue;
+ else
+ return value;
+}
diff --git a/Runtime/Input/InputAxis.h b/Runtime/Input/InputAxis.h
new file mode 100644
index 0000000..cde499b
--- /dev/null
+++ b/Runtime/Input/InputAxis.h
@@ -0,0 +1,151 @@
+#ifndef INPUTAXIS_H
+#define INPUTAXIS_H
+
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Serialize/SerializationMetaFlags.h"
+#include <string>
+using std::string;
+
+enum AxisType {
+ kAxisButton,
+ kAxisMouse,
+ kAxisJoystick,
+};
+
+/// The class for one input axis
+class InputAxis {
+public:
+ DECLARE_SERIALIZE (InputAxis)
+
+ InputAxis ();
+ InputAxis (const string &name);
+
+ const UnityStr &GetName () { return m_Name; }
+ void SetName (const string &name) { m_Name = name; }
+
+ float GetValue () const { return value; }
+ float GetValueRaw () const;
+
+ void Reset () { value = 0.0F; rawValue = 0.0F; }
+ /// Set the key codes for positiove and negative position.
+ void SetKeys (int pos, int neg) { positiveButton = pos; negativeButton = neg; }
+ void SetPosKey (int pos) { positiveButton = pos; }
+ int GetPosKey () const { return positiveButton; }
+ void SetNegKey (int neg) { negativeButton = neg; }
+ int GetNegKey () const { return negativeButton; }
+
+ void SetAltPosKey (int pos) { altPositiveButton = pos; }
+ int GetAltPosKey () const { return altPositiveButton; }
+ void SetAltNegKey (int neg) { altNegativeButton = neg; }
+ int GetAltNegKey () const { return altNegativeButton; }
+
+ /// Set the mouse or joystick axis for this input.
+ /// 0 = x, 1 = y
+ void SetAxis (int a) { axis = a; }
+ int GetAxis() { return axis; }
+ /// for joysticks: Set the joystick number to read.
+ void SetJoystickNumber (int input) { joyNum = input; }
+ int GetJoystickNumber () const { return joyNum; }
+
+ /// Make this axis return to neutral if left alone
+ /// @param grav time it takes to return to neutral position
+ /// @param pos neutral position
+ void SetGravity (float grav) { gravity = grav; }
+ float GetGravity () const { return gravity; }
+
+ /// How sensitive is this axis.
+ /// In mouse or joystick control, it maps the factor between mouse position and axis position.
+ /// If keyboard control, how much we should move for each update. HINT: Use a value < .5
+ void SetSensitivity (float sens) { sensitivity = sens; }
+ float GetSensitivity () const { return sensitivity; }
+
+ /// Keyboard control only: should the axis snap to neutral position if the opposite key is pressed
+ void SetKeySnap (bool s) { snap = s; }
+ bool GetKeySnap () const { return snap; }
+ /// Set the size of the center.
+ void SetDeadZone (float size) { dead = size; }
+ float GetDeadZone () const { return dead; }
+
+ /// Should this axis be inverted ?
+ void SetInvert (bool invert) { this->invert = invert; }
+ bool GetInvert () const { return invert; }
+
+ /// Helper: Make a sensible analog key controller.
+ void MakeAnalogKey (int keyPos, int keyNeg, int altKeyPos, int altKeyNeg);
+ /// Helper: Make a sensible fire button.
+ void MakeButton (int keyPos, int altKeyPos);
+ void MakeMouse (int axis);
+ void MakeJoystick (int axis);
+
+ int GetType () { return type; }
+
+ virtual void Update ();
+
+ UnityStr GetDescriptiveName(bool neg);
+
+ float& GetValueRawRef () { return rawValue; }
+ float& GetValueRef () { return value; }
+
+ private:
+ UnityStr m_Name;
+ UnityStr descriptiveName; ///< Name presented to the user for setup if present
+ UnityStr descriptiveNegativeName; ///< Name for negative Button presented to the user for setup if present
+ int positiveButton; ///< Button to be pressed for movement in negative direction
+ int negativeButton; ///< Button to be pressed for movement in positive direction
+ int altPositiveButton; ///< alternative Button to be pressed for movement in negative direction
+ int altNegativeButton; ///< alternative Button to be pressed for movement in positive direction
+ int joyNum; ///< Joystick identifier index enum {Get Motion from all Joysticks = 0, Joystick 1,Joystick 2,Joystick 3,Joystick 4,Joystick 5,Joystick 6,Joystick 7,Joystick 8,Joystick 9,Joystick 10,Joystick 11 }
+ int type; ///< enum { Key or Mouse Button = 0, Mouse Movement, Joystick Axis}
+ float value;
+ float rawValue;
+
+ int axis; ///< Axis to use enum { X axis = 0, Y axis = 1, 3rd axis (Joysticks and Scrollwheel) = 2, 4th axis (Joysticks) = 3, 5th axis (Joysticks) = 4, 6th axis (Joysticks) = 5, 7th axis (Joysticks) = 6, 8th axis (Joysticks) = 7, 9th axis (Joysticks) = 8, 10th axis (Joysticks) = 9 }
+ float gravity; ///< Speed (in units/sec) that the output value falls towards neutral when device at rest
+ float dead; ///< Size of the analog dead zone. All analog device values within this range map to neutral
+ float sensitivity; ///< Speed to move towards target value for digital devices (in units per sec)
+ bool snap; ///< If we have input in opposite direction of current, do we jump to neutral and continue from there?
+ bool invert; ///< flip positive and negative?
+ void DoGravity (float time);
+};
+
+std::string ConvertKeyToString (int key);
+int ConvertStringToKey (const std::string& name);
+
+string KeyToString (int key);
+int StringToKey (const string& name);
+
+template<class TransferFunc>
+void InputAxis::Transfer (TransferFunc& transfer)
+{
+ transfer.SetVersion (3);
+
+ TRANSFER(m_Name);
+ TRANSFER(descriptiveName);
+ TRANSFER(descriptiveNegativeName);
+
+ TRANSFER_WITH_CUSTOM_GET_SET (UnityStr, "negativeButton",
+ value = KeyToString (negativeButton),
+ negativeButton = StringToKey (value), kSimpleEditorMask);
+ TRANSFER_WITH_CUSTOM_GET_SET (UnityStr, "positiveButton",
+ value = KeyToString (positiveButton),
+ positiveButton = StringToKey (value), kSimpleEditorMask);
+ TRANSFER_WITH_CUSTOM_GET_SET (UnityStr, "altNegativeButton",
+ value = KeyToString (altNegativeButton),
+ altNegativeButton = StringToKey (value), kSimpleEditorMask);
+ TRANSFER_WITH_CUSTOM_GET_SET (UnityStr, "altPositiveButton",
+ value = KeyToString (altPositiveButton),
+ altPositiveButton = StringToKey (value), kSimpleEditorMask);
+
+ TRANSFER(gravity);
+ TRANSFER(dead);
+ TRANSFER_SIMPLE(sensitivity);
+ TRANSFER(snap);
+ TRANSFER(invert);
+ transfer.Align();
+
+ TRANSFER_SIMPLE(type);
+ TRANSFER(axis);
+ TRANSFER(joyNum);
+}
+
+#endif
diff --git a/Runtime/Input/InputManager.cpp b/Runtime/Input/InputManager.cpp
new file mode 100644
index 0000000..e9d5e05
--- /dev/null
+++ b/Runtime/Input/InputManager.cpp
@@ -0,0 +1,1329 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "InputManager.h"
+#include "LocationService.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Utilities/BitSetSerialization.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Math/Quaternion.h"
+#include "PlatformDependent/iPhonePlayer/APN.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+#if ENABLE_NEW_EVENT_SYSTEM
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Dynamics/PhysicsManager.h"
+#include "Runtime/Dynamics/Collider.h"
+#include "Runtime/Dynamics/Rigidbody.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Graphics/Transform.h"
+#endif
+
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/GUIWindows.h"
+
+#if SUPPORT_REPRODUCE_LOG
+#include <fstream>
+#include "Runtime/Misc/ReproductionLog.h"
+#endif
+
+IMPLEMENT_CLASS_HAS_INIT (InputManager)
+IMPLEMENT_OBJECT_SERIALIZE (InputManager)
+IMPLEMENT_CLUSTER_SERIALIZE (InputManager)
+GET_MANAGER (InputManager)
+GET_MANAGER_PTR (InputManager)
+
+using namespace std;
+
+
+InputManager::InputManager (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ MakeDefault ();
+ m_CurrentKeyState.resize (kKeyAndJoyButtonCount, false);
+ m_ThisFrameKeyDown.resize (kKeyAndJoyButtonCount, false);
+ m_ThisFrameKeyUp.resize (kKeyAndJoyButtonCount, false);
+
+#if ENABLE_NEW_EVENT_SYSTEM
+ m_CurrentTouch = NULL;
+ m_Selection = NULL;
+#else
+ m_MousePos = Vector2f (0.0F,0.0F);
+ m_MouseDelta = Vector3f (0.0F,0.0F,0.0F);
+#endif
+ m_MousePresent = true;
+ m_ShouldQuit = false;
+ m_TextFieldInput = false;
+ m_IMEIsSelected = false;
+ m_SimulateMouseWithTouches = true;
+
+ for(int i=0;i<kMaxJoySticks;i++)
+ {
+ std::vector<float> axes;
+ for(int j=0;j<kMaxJoyStickAxis;j++)
+ axes.push_back(0.0);
+ m_JoystickPos.push_back(axes);
+ }
+
+ m_EatKeyPressOnTextFieldFocus = true;
+ m_IMECompositionMode = kCompositionModeAuto;
+}
+
+InputManager::~InputManager () {
+}
+
+void InputManager::Reset ()
+{
+ Super::Reset();
+
+ m_Axes.clear ();
+ m_CompositionString.clear ();
+ MakeDefault ();
+}
+
+void InputManager::MakeDefault () {
+ m_Axes.push_back (InputAxis ("Horizontal"));
+ m_Axes.push_back (InputAxis ("Vertical"));
+ m_Axes[0].MakeAnalogKey (StringToKey ("right"), StringToKey ("left"), StringToKey ("d"), StringToKey ("a"));
+ m_Axes[1].MakeAnalogKey (StringToKey ("up"), StringToKey ("down"), StringToKey ("w"), StringToKey ("s"));
+
+ m_Axes.push_back (InputAxis ("Fire1"));
+ m_Axes.push_back (InputAxis ("Fire2"));
+ m_Axes.push_back (InputAxis ("Fire3"));
+ m_Axes.push_back (InputAxis ("Jump"));
+ m_Axes[2].MakeButton (StringToKey ("left ctrl"), StringToKey ("mouse 0"));
+ m_Axes[3].MakeButton (StringToKey ("left alt"), StringToKey ("mouse 1"));
+ m_Axes[4].MakeButton (StringToKey ("left cmd"), StringToKey ("mouse 2"));
+ m_Axes[5].MakeButton (StringToKey ("space"), 0);
+
+ m_Axes.push_back (InputAxis ("Mouse X"));
+ m_Axes.push_back (InputAxis ("Mouse Y"));
+ m_Axes.push_back (InputAxis ("Mouse ScrollWheel"));
+ m_Axes[6].MakeMouse (0);
+ m_Axes[7].MakeMouse (1);
+ m_Axes[8].MakeMouse (2);
+
+ m_Axes.push_back (InputAxis ("Horizontal"));
+ m_Axes.push_back (InputAxis ("Vertical"));
+ m_Axes[9].MakeJoystick (0);
+ m_Axes[10].MakeJoystick (1);
+ m_Axes[10].SetInvert(true);
+
+ m_Axes.push_back (InputAxis ("Fire1"));
+ m_Axes.push_back (InputAxis ("Fire2"));
+ m_Axes.push_back (InputAxis ("Fire3"));
+ m_Axes.push_back (InputAxis ("Jump"));
+ m_Axes[11].MakeButton (StringToKey ("joystick button 0"), 0);
+ m_Axes[12].MakeButton (StringToKey ("joystick button 1"), 0);
+ m_Axes[13].MakeButton (StringToKey ("joystick button 2"), 0);
+ m_Axes[14].MakeButton (StringToKey ("joystick button 3"), 0);
+}
+
+bool InputManager::GetButton (const string &name) {
+ bool finalButton = false;
+ for (vector<InputAxis>::iterator i = m_Axes.begin(); i != m_Axes.end();i++) {
+ if (i->GetName() == name)
+ {
+ finalButton |= GetKey(i->GetPosKey ());
+ finalButton |= GetKey(i->GetNegKey ());
+ finalButton |= GetKey(i->GetAltPosKey ());
+ finalButton |= GetKey(i->GetAltNegKey ());
+ }
+ }
+ return finalButton;
+}
+
+bool InputManager::GetButtonDown (const string &name)
+{
+ bool finalButton = false;
+ for (vector<InputAxis>::iterator i = m_Axes.begin(); i != m_Axes.end();i++)
+ {
+ if (i->GetName() == name && i->GetType () == kAxisButton)
+ {
+ finalButton |= m_ThisFrameKeyDown[i->GetPosKey ()];
+ finalButton |= m_ThisFrameKeyDown[i->GetNegKey ()];
+ finalButton |= m_ThisFrameKeyDown[i->GetAltPosKey ()];
+ finalButton |= m_ThisFrameKeyDown[i->GetAltNegKey ()];
+ }
+ }
+ return finalButton;
+}
+
+bool InputManager::GetButtonUp (const string &name)
+{
+ bool finalButton = false;
+ for (vector<InputAxis>::iterator i = m_Axes.begin(); i != m_Axes.end();i++)
+ {
+ if (i->GetName() == name && i->GetType () == kAxisButton)
+ {
+ finalButton |= m_ThisFrameKeyUp[i->GetPosKey ()];
+ finalButton |= m_ThisFrameKeyUp[i->GetNegKey ()];
+ finalButton |= m_ThisFrameKeyUp[i->GetAltPosKey ()];
+ finalButton |= m_ThisFrameKeyUp[i->GetAltNegKey ()];
+ }
+ }
+ return finalButton;
+}
+
+
+float InputManager::GetAxis (const string &name)
+{
+ float finalValue = 0.0F;
+ for (vector<InputAxis>::iterator i = m_Axes.begin(); i != m_Axes.end();i++) {
+ if (i->GetName() == name && Abs (i->GetValue ()) > Abs (finalValue))
+ finalValue = i->GetValue ();
+ }
+ return finalValue;
+}
+
+float InputManager::GetAxisRaw (const string &name) {
+ float finalValue = 0.0F;
+ for (vector<InputAxis>::iterator i = m_Axes.begin(); i != m_Axes.end();i++) {
+ if (i->GetName() == name && Abs (i->GetValueRaw ()) > Abs (finalValue))
+ finalValue = i->GetValueRaw ();
+ }
+ return finalValue;
+}
+
+
+bool InputManager::HasAxisOrButton (const string& name)
+{
+ for (vector<InputAxis>::iterator i = m_Axes.begin(); i != m_Axes.end();i++) {
+ if (i->GetName() == name)
+ return true;
+ }
+ return false;
+}
+
+// Might consider doing this inside InputAxis somehow...
+void InputManager::CheckConsistency () {
+ Super::CheckConsistency ();
+ ResetInputAxes ();
+}
+
+void InputManager::ResetInputAxes ()
+{
+ for (vector<InputAxis>::iterator i = m_Axes.begin(); i != m_Axes.end(); i++)
+ i->Reset();
+
+ m_CurrentKeyState.reset ();
+ m_ThisFrameKeyDown.reset ();
+ m_ThisFrameKeyUp.reset();
+
+ for (int i=0;i<m_JoystickPos.size ();i++)
+ {
+ for (int j=0;j<m_JoystickPos[i].size ();j++)
+ m_JoystickPos[i][j] = 0.0F;
+ }
+
+#if ENABLE_NEW_EVENT_SYSTEM
+ for (int i = 0; i < 3; ++i)
+ {
+ m_Mouse[i].deltaPos = Vector2f::zero;
+ m_Mouse[i].deltaScroll = Vector2f::zero;
+ }
+#else
+ m_MouseDelta = Vector3f (0,0,0);
+#endif
+}
+
+bool InputManager::GetAnyKey ()
+{
+ #if SUPPORT_REPRODUCE_LOG
+ if(GetKeyDown(286) || GetKeyDown(287))
+ return false;
+ #endif
+ return m_CurrentKeyState.any () || m_ThisFrameKeyDown.any ();
+}
+
+bool InputManager::GetAnyKeyThisFrame ()
+{
+ #if SUPPORT_REPRODUCE_LOG
+ if(GetKeyDown(286) || GetKeyDown(287))
+ return false;
+ #endif
+ return m_ThisFrameKeyDown.any ();
+}
+
+template<class TransferFunc>
+void InputManager::Transfer (TransferFunc& transfer) {
+ Super::Transfer (transfer);
+ TRANSFER_SIMPLE (m_Axes);
+}
+
+#if ENABLE_CLUSTER_SYNC
+template<class TransferFunc>
+void InputManager::ClusterTransfer (TransferFunc& transfer) {
+
+ TRANSFER(m_Axes);
+
+ TRANSFER(m_CurrentKeyState);
+ TRANSFER(m_ThisFrameKeyDown);
+ TRANSFER(m_ThisFrameKeyUp);
+
+ TRANSFER(m_MouseDelta.x);
+ TRANSFER(m_MouseDelta.y);
+ TRANSFER(m_MouseDelta.z);
+
+ TRANSFER(m_MousePos.x);
+ TRANSFER(m_MousePos.y);
+}
+#endif
+
+
+
+float InputManager::GetJoystickPosition (int joyNum, int axis) const
+{
+ if (joyNum < m_JoystickPos.size () && axis < m_JoystickPos[joyNum].size ())
+ return m_JoystickPos[joyNum][axis];
+ else
+ return 0;
+}
+
+void InputManager::SetJoystickPosition (int joyNum, int axis, float pos)
+{
+ if (joyNum < m_JoystickPos.size () && axis < m_JoystickPos[joyNum].size ())
+ {
+ m_JoystickPos[joyNum][axis]=pos;
+ }
+}
+
+
+typedef std::map<int, std::string> KeyToName;
+typedef std::map<std::string, int> NameToKey;
+static KeyToName* g_KeyToName = NULL;
+static NameToKey* g_NameToKey = NULL;
+
+void SetupKeyNameMapping ();
+void InputManager::InitializeClass()
+{
+ g_KeyToName = UNITY_NEW(KeyToName, kMemResource);
+ g_NameToKey = UNITY_NEW(NameToKey, kMemResource);
+ SetupKeyNameMapping();
+}
+
+void InputManager::CleanupClass()
+{
+ UNITY_DELETE(g_KeyToName, kMemResource);
+ UNITY_DELETE(g_NameToKey, kMemResource);
+}
+
+void SetupKeyNameMapping ()
+{
+ static bool isInitialized = false;
+ if (isInitialized)
+ return;
+ isInitialized = true;
+
+ SET_ALLOC_OWNER(NULL);
+
+ (*g_KeyToName)[(int)SDLK_BACKSPACE] = "backspace";
+ (*g_KeyToName)[(int)SDLK_TAB] = "tab";
+ (*g_KeyToName)[(int)SDLK_CLEAR] = "clear";
+ (*g_KeyToName)[(int)SDLK_RETURN] = "return";
+ (*g_KeyToName)[(int)SDLK_PAUSE] = "pause";
+ (*g_KeyToName)[(int)SDLK_ESCAPE] = "escape";
+ (*g_KeyToName)[(int)SDLK_SPACE] = "space";
+ (*g_KeyToName)[(int)SDLK_EXCLAIM] = "!";
+ (*g_KeyToName)[(int)SDLK_QUOTEDBL] = "\"";
+ (*g_KeyToName)[(int)SDLK_HASH] = "#";
+ (*g_KeyToName)[(int)SDLK_DOLLAR] = "$";
+ (*g_KeyToName)[(int)SDLK_AMPERSAND] = "&";
+ (*g_KeyToName)[(int)SDLK_QUOTE] = "'";
+ (*g_KeyToName)[(int)SDLK_LEFTPAREN] = "(";
+ (*g_KeyToName)[(int)SDLK_RIGHTPAREN] = ")";
+ (*g_KeyToName)[(int)SDLK_ASTERISK] = "*";
+ (*g_KeyToName)[(int)SDLK_PLUS] = "+";
+ (*g_KeyToName)[(int)SDLK_COMMA] = ",";
+ (*g_KeyToName)[(int)SDLK_MINUS] = "-";
+ (*g_KeyToName)[(int)SDLK_PERIOD] = ".";
+ (*g_KeyToName)[(int)SDLK_SLASH] = "/";
+ (*g_KeyToName)[(int)SDLK_0] = "0";
+ (*g_KeyToName)[(int)SDLK_1] = "1";
+ (*g_KeyToName)[(int)SDLK_2] = "2";
+ (*g_KeyToName)[(int)SDLK_3] = "3";
+ (*g_KeyToName)[(int)SDLK_4] = "4";
+ (*g_KeyToName)[(int)SDLK_5] = "5";
+ (*g_KeyToName)[(int)SDLK_6] = "6";
+ (*g_KeyToName)[(int)SDLK_7] = "7";
+ (*g_KeyToName)[(int)SDLK_8] = "8";
+ (*g_KeyToName)[(int)SDLK_9] = "9";
+ (*g_KeyToName)[(int)SDLK_COLON] = ":";
+ (*g_KeyToName)[(int)SDLK_SEMICOLON] = ";";
+ (*g_KeyToName)[(int)SDLK_LESS] = "<";
+ (*g_KeyToName)[(int)SDLK_EQUALS] = "=";
+ (*g_KeyToName)[(int)SDLK_GREATER] = ">";
+ (*g_KeyToName)[(int)SDLK_QUESTION] = "?";
+ (*g_KeyToName)[(int)SDLK_AT] = "@";
+ (*g_KeyToName)[(int)SDLK_LEFTBRACKET] = "[";
+ (*g_KeyToName)[(int)SDLK_BACKSLASH] = "\\";
+ (*g_KeyToName)[(int)SDLK_RIGHTBRACKET] = "]";
+ (*g_KeyToName)[(int)SDLK_CARET] = "^";
+ (*g_KeyToName)[(int)SDLK_UNDERSCORE] = "_";
+ (*g_KeyToName)[(int)SDLK_BACKQUOTE] = "`";
+ (*g_KeyToName)[(int)SDLK_a] = "a";
+ (*g_KeyToName)[(int)SDLK_b] = "b";
+ (*g_KeyToName)[(int)SDLK_c] = "c";
+ (*g_KeyToName)[(int)SDLK_d] = "d";
+ (*g_KeyToName)[(int)SDLK_e] = "e";
+ (*g_KeyToName)[(int)SDLK_f] = "f";
+ (*g_KeyToName)[(int)SDLK_g] = "g";
+ (*g_KeyToName)[(int)SDLK_h] = "h";
+ (*g_KeyToName)[(int)SDLK_i] = "i";
+ (*g_KeyToName)[(int)SDLK_j] = "j";
+ (*g_KeyToName)[(int)SDLK_k] = "k";
+ (*g_KeyToName)[(int)SDLK_l] = "l";
+ (*g_KeyToName)[(int)SDLK_m] = "m";
+ (*g_KeyToName)[(int)SDLK_n] = "n";
+ (*g_KeyToName)[(int)SDLK_o] = "o";
+ (*g_KeyToName)[(int)SDLK_p] = "p";
+ (*g_KeyToName)[(int)SDLK_q] = "q";
+ (*g_KeyToName)[(int)SDLK_r] = "r";
+ (*g_KeyToName)[(int)SDLK_s] = "s";
+ (*g_KeyToName)[(int)SDLK_t] = "t";
+ (*g_KeyToName)[(int)SDLK_u] = "u";
+ (*g_KeyToName)[(int)SDLK_v] = "v";
+ (*g_KeyToName)[(int)SDLK_w] = "w";
+ (*g_KeyToName)[(int)SDLK_x] = "x";
+ (*g_KeyToName)[(int)SDLK_y] = "y";
+ (*g_KeyToName)[(int)SDLK_z] = "z";
+ (*g_KeyToName)[(int)SDLK_DELETE] = "delete";
+
+ (*g_KeyToName)[(int)SDLK_WORLD_0] = "world 0";
+ (*g_KeyToName)[(int)SDLK_WORLD_1] = "world 1";
+ (*g_KeyToName)[(int)SDLK_WORLD_2] = "world 2";
+ (*g_KeyToName)[(int)SDLK_WORLD_3] = "world 3";
+ (*g_KeyToName)[(int)SDLK_WORLD_4] = "world 4";
+ (*g_KeyToName)[(int)SDLK_WORLD_5] = "world 5";
+ (*g_KeyToName)[(int)SDLK_WORLD_6] = "world 6";
+ (*g_KeyToName)[(int)SDLK_WORLD_7] = "world 7";
+ (*g_KeyToName)[(int)SDLK_WORLD_8] = "world 8";
+ (*g_KeyToName)[(int)SDLK_WORLD_9] = "world 9";
+ (*g_KeyToName)[(int)SDLK_WORLD_10] = "world 10";
+ (*g_KeyToName)[(int)SDLK_WORLD_11] = "world 11";
+ (*g_KeyToName)[(int)SDLK_WORLD_12] = "world 12";
+ (*g_KeyToName)[(int)SDLK_WORLD_13] = "world 13";
+ (*g_KeyToName)[(int)SDLK_WORLD_14] = "world 14";
+ (*g_KeyToName)[(int)SDLK_WORLD_15] = "world 15";
+ (*g_KeyToName)[(int)SDLK_WORLD_16] = "world 16";
+ (*g_KeyToName)[(int)SDLK_WORLD_17] = "world 17";
+ (*g_KeyToName)[(int)SDLK_WORLD_18] = "world 18";
+ (*g_KeyToName)[(int)SDLK_WORLD_19] = "world 19";
+ (*g_KeyToName)[(int)SDLK_WORLD_20] = "world 20";
+ (*g_KeyToName)[(int)SDLK_WORLD_21] = "world 21";
+ (*g_KeyToName)[(int)SDLK_WORLD_22] = "world 22";
+ (*g_KeyToName)[(int)SDLK_WORLD_23] = "world 23";
+ (*g_KeyToName)[(int)SDLK_WORLD_24] = "world 24";
+ (*g_KeyToName)[(int)SDLK_WORLD_25] = "world 25";
+ (*g_KeyToName)[(int)SDLK_WORLD_26] = "world 26";
+ (*g_KeyToName)[(int)SDLK_WORLD_27] = "world 27";
+ (*g_KeyToName)[(int)SDLK_WORLD_28] = "world 28";
+ (*g_KeyToName)[(int)SDLK_WORLD_29] = "world 29";
+ (*g_KeyToName)[(int)SDLK_WORLD_30] = "world 30";
+ (*g_KeyToName)[(int)SDLK_WORLD_31] = "world 31";
+ (*g_KeyToName)[(int)SDLK_WORLD_32] = "world 32";
+ (*g_KeyToName)[(int)SDLK_WORLD_33] = "world 33";
+ (*g_KeyToName)[(int)SDLK_WORLD_34] = "world 34";
+ (*g_KeyToName)[(int)SDLK_WORLD_35] = "world 35";
+ (*g_KeyToName)[(int)SDLK_WORLD_36] = "world 36";
+ (*g_KeyToName)[(int)SDLK_WORLD_37] = "world 37";
+ (*g_KeyToName)[(int)SDLK_WORLD_38] = "world 38";
+ (*g_KeyToName)[(int)SDLK_WORLD_39] = "world 39";
+ (*g_KeyToName)[(int)SDLK_WORLD_40] = "world 40";
+ (*g_KeyToName)[(int)SDLK_WORLD_41] = "world 41";
+ (*g_KeyToName)[(int)SDLK_WORLD_42] = "world 42";
+ (*g_KeyToName)[(int)SDLK_WORLD_43] = "world 43";
+ (*g_KeyToName)[(int)SDLK_WORLD_44] = "world 44";
+ (*g_KeyToName)[(int)SDLK_WORLD_45] = "world 45";
+ (*g_KeyToName)[(int)SDLK_WORLD_46] = "world 46";
+ (*g_KeyToName)[(int)SDLK_WORLD_47] = "world 47";
+ (*g_KeyToName)[(int)SDLK_WORLD_48] = "world 48";
+ (*g_KeyToName)[(int)SDLK_WORLD_49] = "world 49";
+ (*g_KeyToName)[(int)SDLK_WORLD_50] = "world 50";
+ (*g_KeyToName)[(int)SDLK_WORLD_51] = "world 51";
+ (*g_KeyToName)[(int)SDLK_WORLD_52] = "world 52";
+ (*g_KeyToName)[(int)SDLK_WORLD_53] = "world 53";
+ (*g_KeyToName)[(int)SDLK_WORLD_54] = "world 54";
+ (*g_KeyToName)[(int)SDLK_WORLD_55] = "world 55";
+ (*g_KeyToName)[(int)SDLK_WORLD_56] = "world 56";
+ (*g_KeyToName)[(int)SDLK_WORLD_57] = "world 57";
+ (*g_KeyToName)[(int)SDLK_WORLD_58] = "world 58";
+ (*g_KeyToName)[(int)SDLK_WORLD_59] = "world 59";
+ (*g_KeyToName)[(int)SDLK_WORLD_60] = "world 60";
+ (*g_KeyToName)[(int)SDLK_WORLD_61] = "world 61";
+ (*g_KeyToName)[(int)SDLK_WORLD_62] = "world 62";
+ (*g_KeyToName)[(int)SDLK_WORLD_63] = "world 63";
+ (*g_KeyToName)[(int)SDLK_WORLD_64] = "world 64";
+ (*g_KeyToName)[(int)SDLK_WORLD_65] = "world 65";
+ (*g_KeyToName)[(int)SDLK_WORLD_66] = "world 66";
+ (*g_KeyToName)[(int)SDLK_WORLD_67] = "world 67";
+ (*g_KeyToName)[(int)SDLK_WORLD_68] = "world 68";
+ (*g_KeyToName)[(int)SDLK_WORLD_69] = "world 69";
+ (*g_KeyToName)[(int)SDLK_WORLD_70] = "world 70";
+ (*g_KeyToName)[(int)SDLK_WORLD_71] = "world 71";
+ (*g_KeyToName)[(int)SDLK_WORLD_72] = "world 72";
+ (*g_KeyToName)[(int)SDLK_WORLD_73] = "world 73";
+ (*g_KeyToName)[(int)SDLK_WORLD_74] = "world 74";
+ (*g_KeyToName)[(int)SDLK_WORLD_75] = "world 75";
+ (*g_KeyToName)[(int)SDLK_WORLD_76] = "world 76";
+ (*g_KeyToName)[(int)SDLK_WORLD_77] = "world 77";
+ (*g_KeyToName)[(int)SDLK_WORLD_78] = "world 78";
+ (*g_KeyToName)[(int)SDLK_WORLD_79] = "world 79";
+ (*g_KeyToName)[(int)SDLK_WORLD_80] = "world 80";
+ (*g_KeyToName)[(int)SDLK_WORLD_81] = "world 81";
+ (*g_KeyToName)[(int)SDLK_WORLD_82] = "world 82";
+ (*g_KeyToName)[(int)SDLK_WORLD_83] = "world 83";
+ (*g_KeyToName)[(int)SDLK_WORLD_84] = "world 84";
+ (*g_KeyToName)[(int)SDLK_WORLD_85] = "world 85";
+ (*g_KeyToName)[(int)SDLK_WORLD_86] = "world 86";
+ (*g_KeyToName)[(int)SDLK_WORLD_87] = "world 87";
+ (*g_KeyToName)[(int)SDLK_WORLD_88] = "world 88";
+ (*g_KeyToName)[(int)SDLK_WORLD_89] = "world 89";
+ (*g_KeyToName)[(int)SDLK_WORLD_90] = "world 90";
+ (*g_KeyToName)[(int)SDLK_WORLD_91] = "world 91";
+ (*g_KeyToName)[(int)SDLK_WORLD_92] = "world 92";
+ (*g_KeyToName)[(int)SDLK_WORLD_93] = "world 93";
+ (*g_KeyToName)[(int)SDLK_WORLD_94] = "world 94";
+ (*g_KeyToName)[(int)SDLK_WORLD_95] = "world 95";
+
+ (*g_KeyToName)[(int)SDLK_KP0] = "[0]";
+ (*g_KeyToName)[(int)SDLK_KP1] = "[1]";
+ (*g_KeyToName)[(int)SDLK_KP2] = "[2]";
+ (*g_KeyToName)[(int)SDLK_KP3] = "[3]";
+ (*g_KeyToName)[(int)SDLK_KP4] = "[4]";
+ (*g_KeyToName)[(int)SDLK_KP5] = "[5]";
+ (*g_KeyToName)[(int)SDLK_KP6] = "[6]";
+ (*g_KeyToName)[(int)SDLK_KP7] = "[7]";
+ (*g_KeyToName)[(int)SDLK_KP8] = "[8]";
+ (*g_KeyToName)[(int)SDLK_KP9] = "[9]";
+ (*g_KeyToName)[(int)SDLK_KP_PERIOD] = "[.]";
+ (*g_KeyToName)[(int)SDLK_KP_DIVIDE] = "[/]";
+ (*g_KeyToName)[(int)SDLK_KP_MULTIPLY] = "[*]";
+ (*g_KeyToName)[(int)SDLK_KP_MINUS] = "[-]";
+ (*g_KeyToName)[(int)SDLK_KP_PLUS] = "[+]";
+ (*g_KeyToName)[(int)SDLK_KP_ENTER] = "enter";
+ (*g_KeyToName)[(int)SDLK_KP_EQUALS] = "equals";
+
+ (*g_KeyToName)[(int)SDLK_UP] = "up";
+ (*g_KeyToName)[(int)SDLK_DOWN] = "down";
+ (*g_KeyToName)[(int)SDLK_RIGHT] = "right";
+ (*g_KeyToName)[(int)SDLK_LEFT] = "left";
+ (*g_KeyToName)[(int)SDLK_DOWN] = "down";
+ (*g_KeyToName)[(int)SDLK_INSERT] = "insert";
+ (*g_KeyToName)[(int)SDLK_HOME] = "home";
+ (*g_KeyToName)[(int)SDLK_END] = "end";
+ (*g_KeyToName)[(int)SDLK_PAGEUP] = "page up";
+ (*g_KeyToName)[(int)SDLK_PAGEDOWN] = "page down";
+
+ (*g_KeyToName)[(int)SDLK_F1] = "f1";
+ (*g_KeyToName)[(int)SDLK_F2] = "f2";
+ (*g_KeyToName)[(int)SDLK_F3] = "f3";
+ (*g_KeyToName)[(int)SDLK_F4] = "f4";
+ (*g_KeyToName)[(int)SDLK_F5] = "f5";
+ (*g_KeyToName)[(int)SDLK_F6] = "f6";
+ (*g_KeyToName)[(int)SDLK_F7] = "f7";
+ (*g_KeyToName)[(int)SDLK_F8] = "f8";
+ (*g_KeyToName)[(int)SDLK_F9] = "f9";
+ (*g_KeyToName)[(int)SDLK_F10] = "f10";
+ (*g_KeyToName)[(int)SDLK_F11] = "f11";
+ (*g_KeyToName)[(int)SDLK_F12] = "f12";
+ (*g_KeyToName)[(int)SDLK_F13] = "f13";
+ (*g_KeyToName)[(int)SDLK_F14] = "f14";
+ (*g_KeyToName)[(int)SDLK_F15] = "f15";
+
+ (*g_KeyToName)[(int)SDLK_NUMLOCK] = "numlock";
+ (*g_KeyToName)[(int)SDLK_CAPSLOCK] = "caps lock";
+ (*g_KeyToName)[(int)SDLK_SCROLLOCK] = "scroll lock";
+ (*g_KeyToName)[(int)SDLK_RSHIFT] = "right shift";
+ (*g_KeyToName)[(int)SDLK_LSHIFT] = "left shift";
+ (*g_KeyToName)[(int)SDLK_RCTRL] = "right ctrl";
+ (*g_KeyToName)[(int)SDLK_LCTRL] = "left ctrl";
+ (*g_KeyToName)[(int)SDLK_RALT] = "right alt";
+ (*g_KeyToName)[(int)SDLK_LALT] = "left alt";
+ (*g_KeyToName)[(int)SDLK_RMETA] = "right cmd";
+ (*g_KeyToName)[(int)SDLK_LMETA] = "left cmd";
+ (*g_KeyToName)[(int)SDLK_LSUPER] = "left super"; /* "Windows" keys */
+ (*g_KeyToName)[(int)SDLK_RSUPER] = "right super";
+ (*g_KeyToName)[(int)SDLK_MODE] = "alt gr";
+ (*g_KeyToName)[(int)SDLK_COMPOSE] = "compose";
+
+ (*g_KeyToName)[(int)SDLK_HELP] = "help";
+ (*g_KeyToName)[(int)SDLK_PRINT] = "print screen";
+ (*g_KeyToName)[(int)SDLK_SYSREQ] = "sys req";
+ (*g_KeyToName)[(int)SDLK_BREAK] = "break";
+ (*g_KeyToName)[(int)SDLK_MENU] = "menu";
+ (*g_KeyToName)[(int)SDLK_POWER] = "power";
+ (*g_KeyToName)[(int)SDLK_EURO] = "euro";
+ (*g_KeyToName)[(int)SDLK_UNDO] = "undo";
+
+ (*g_KeyToName)[(int)kKeyCount + 0] = "mouse 0";
+ (*g_KeyToName)[(int)kKeyCount + 1] = "mouse 1";
+ (*g_KeyToName)[(int)kKeyCount + 2] = "mouse 2";
+ (*g_KeyToName)[(int)kKeyCount + 3] = "mouse 3";
+ (*g_KeyToName)[(int)kKeyCount + 4] = "mouse 4";
+ (*g_KeyToName)[(int)kKeyCount + 5] = "mouse 5";
+ (*g_KeyToName)[(int)kKeyCount + 6] = "mouse 6";
+
+ for (int joystick=0;joystick<kMaxJoySticks;joystick++) {
+ for (int button=0;button<kMaxJoyStickButtons;button++) {
+ char buffy[100];
+ if(joystick!=0)
+ sprintf (buffy, "joystick %d button %d", joystick, button);
+ else
+ sprintf (buffy, "joystick button %d", button);
+ (*g_KeyToName)[kKeyAndMouseButtonCount + joystick * kMaxJoyStickButtons + button] = buffy;
+ }
+ }
+
+ AssertIf ((int)kKeyCount != (int)SDLK_LAST);
+
+ g_NameToKey->clear ();
+ for (KeyToName::iterator i=g_KeyToName->begin ();i != g_KeyToName->end ();i++)
+ (*g_NameToKey)[i->second] = i->first;
+}
+
+string KeyToString (int key) {
+ KeyToName::iterator found = g_KeyToName->find (key);
+ if (found == g_KeyToName->end ())
+ return string ();
+ else
+ return found->second;
+}
+
+int StringToKey (const string& name) {
+ NameToKey::iterator found = g_NameToKey->find (name);
+ if (found == g_NameToKey->end ())
+ return 0;
+ else
+ return found->second;
+}
+
+void InputManager::InputEndFrame ()
+{
+ m_ThisFrameKeyDown.reset ();
+ m_ThisFrameKeyUp.reset ();
+#if ENABLE_NEW_EVENT_SYSTEM
+ m_Mouse->deltaPos = Vector2f::zero;
+ m_Mouse->deltaScroll = Vector2f::zero;
+#else
+ m_MouseDelta = Vector3f::zero;
+#endif
+ m_InputString.clear ();
+}
+
+void InputManager::ProcessInput()
+{
+ // Update Joystick 0
+
+ if (m_JoystickPos.size() > 0)
+ {
+ vector<float>& joy0 = m_JoystickPos[0];
+
+ for (vector<float>::iterator axis = joy0.begin(); axis != joy0.end(); ++axis)
+ {
+ *axis = 0.0f;
+ }
+
+ for (vector<vector<float> >::const_iterator joy = (m_JoystickPos.begin() + 1); joy != m_JoystickPos.end(); ++joy)
+ {
+ size_t const size = min(joy0.size(), joy->size());
+
+ for (size_t i = 0; i < size; ++i)
+ {
+ if (abs((*joy)[i]) > abs(joy0[i]))
+ {
+ joy0[i] = (*joy)[i];
+ }
+ }
+ }
+ }
+
+ //Update Axes
+ for (vector<InputAxis>::iterator i = m_Axes.begin(); i != m_Axes.end(); i++)
+ i->Update();
+}
+
+#if ENABLE_NEW_EVENT_SYSTEM
+Rigidbody* FindRigidbodyInParents (GameObject& root)
+{
+ Rigidbody* rb = root.QueryComponentT<Rigidbody>(ClassID(Rigidbody));
+
+ if (rb != NULL)
+ return rb;
+
+ Transform* parent = root.GetComponentT<Transform>(ClassID(Transform)).GetParent();
+
+ if (parent == NULL)
+ return NULL;
+
+ return FindRigidbodyInParents(parent->GetGameObject());
+}
+
+void InputManager::SendInputEvents()
+{
+ // Prior to Unity 4.1 events were sent out via C# in MouseEvents.cs, and only for mouse events
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a1))
+ {
+ ProcessMouse();
+ ProcessTouches();
+ m_CurrentTouch = NULL;
+ }
+}
+
+void InputManager::ProcessMouse ()
+{
+ const RenderManager::CameraContainer& cameras = GetRenderManager().GetOnscreenCameras();
+
+ // Mouse events are handled as separate touches for each button
+ for (int b = 0; b < 3; ++b)
+ {
+ // Touch IDs: -1 for LMB, -2 for RMB, -3 for MMB.
+ // Touch IDs for actual touch events are 0+.
+ m_CurrentTouch = &m_Mouse[b];
+ m_CurrentTouch->id = -1 - b;
+
+ bool isPressed = GetMouseButtonDown(b);
+ bool isReleased = GetMouseButtonUp(b);
+ bool isDragging = (m_CurrentTouch->press != NULL);
+
+ if (isReleased) m_CurrentTouch->phase = Touch::kTouchEnded;
+ else if (isPressed) m_CurrentTouch->phase = Touch::kTouchBegan;
+ else m_CurrentTouch->phase = Touch::kTouchMoved;
+
+ // TODO: What order are these in? We need to go from highest depth to lowest depth for events
+ for (RenderManager::CameraContainer::const_iterator i = cameras.begin(); i != cameras.end(); ++i)
+ {
+ RaycastHit hit;
+ const Camera& cam = (**i);
+ Ray ray = cam.ScreenPointToRay(m_Mouse[0].pos);
+ UInt32 mask = cam.GetEventMask() & cam.GetCullingMask();
+
+ GameObject* go = NULL;
+
+ if (GetPhysicsManager().Raycast(ray, 1000.0f, hit, mask))
+ {
+ m_CurrentTouch->worldPos = hit.point;
+ go = &hit.collider->GetGameObject();
+
+ // Raycast hitting a collider should target its rigidbody instead
+ Rigidbody* rb = FindRigidbodyInParents(*go);
+ if (rb != NULL)
+ go = &rb->GetGameObject();
+ }
+
+ if (isPressed)
+ {
+ // Newly pressed -- clear all values
+ m_CurrentTouch->press = NULL;
+ m_CurrentTouch->oldPos = m_CurrentTouch->pos;
+ //m_CurrentTouch->eligibleForClick = true;
+ m_CurrentTouch->deltaPos = Vector2f::zero;
+ m_CurrentTouch->deltaScroll = Vector2f::zero;
+ }
+ else if (b == 0)
+ {
+ // Left mouse button should calculate delta
+ m_CurrentTouch->deltaPos = m_CurrentTouch->pos - m_CurrentTouch->oldPos;
+ m_CurrentTouch->oldPos = m_CurrentTouch->pos;
+ }
+ else
+ {
+ // Other mouse buttons should simply copy the first button's data
+ m_CurrentTouch->pos = m_Mouse[0].pos;
+ m_CurrentTouch->deltaPos = m_Mouse[0].deltaPos;
+ m_CurrentTouch->deltaScroll = m_Mouse[0].deltaScroll;
+ m_CurrentTouch->deltaTime = m_Mouse[0].deltaTime;
+ }
+
+ // Process this mouse event as a touch
+ ProcessTouch(go, isPressed, isReleased, isDragging, b == 0);
+ if (go != NULL) break;
+ }
+ }
+}
+
+void InputManager::ProcessTouches()
+{
+ const RenderManager::CameraContainer& cameras = GetRenderManager().GetOnscreenCameras();
+
+ size_t count = GetTouchCount();
+
+ for (size_t i = 0; i < count; ++i)
+ {
+ m_CurrentTouch = GetTouch(count);
+
+ bool isPressed = (m_CurrentTouch->phase == Touch::kTouchBegan);
+ bool isReleased = (m_CurrentTouch->phase == Touch::kTouchEnded) || (m_CurrentTouch->phase == Touch::kTouchCanceled);
+
+ for (RenderManager::CameraContainer::const_iterator i = cameras.begin(); i != cameras.end(); ++i)
+ {
+ RaycastHit hit;
+ const Camera& cam = (**i);
+ Ray ray = cam.ScreenPointToRay(m_Mouse[0].pos);
+ UInt32 mask = cam.GetEventMask() & cam.GetCullingMask();
+ bool isHit = GetPhysicsManager().Raycast(ray, 1000.0f, hit, mask);
+
+ GameObject* go = NULL;
+
+ if (GetPhysicsManager().Raycast(ray, 1000.0f, hit, mask))
+ {
+ m_CurrentTouch->worldPos = hit.point;
+ go = &hit.collider->GetGameObject();
+
+ // Raycast hitting a collider should target its rigidbody instead
+ Rigidbody* rb = FindRigidbodyInParents(*go);
+ if (rb != NULL)
+ go = &rb->GetGameObject();
+ }
+
+ if (m_CurrentTouch->phase == Touch::kTouchBegan)
+ {
+ m_CurrentTouch->press = NULL;
+ m_CurrentTouch->oldPos = m_CurrentTouch->pos;
+ //m_CurrentTouch->eligibleForClick = true;
+ m_CurrentTouch->deltaPos = Vector2f::zero;
+ m_CurrentTouch->deltaScroll = Vector2f::zero;
+ }
+
+ ProcessTouch(go, isPressed, isReleased, true, true);
+ if (go != NULL) break;
+ }
+ }
+}
+
+void InputManager::ProcessTouch (GameObject* hover, bool isPressed, bool isReleased, bool isDragging, bool moveEvents)
+{
+ GUIState& state = GetGUIState();
+ state.BeginUsingEvents();
+
+ // Save the previous game objects prior to updates
+ GameObject* previousHover = m_CurrentTouch->hover;
+ GameObject* previousPress = m_CurrentTouch->press;
+
+ // Update the current hovered so that Input.current.hover is proper in the callbacks below
+ m_CurrentTouch->hover = hover;
+
+ // Update the pressed object if it has changed
+ if (isPressed) m_CurrentTouch->press = hover;
+
+ MessageData data;
+ InputEvent ev;
+ ev.touch = *m_CurrentTouch;
+ ev.button = -m_CurrentTouch->id;
+
+ // Set the event so that C# can access it via Event.current.touch
+ GetGUIState().SetEvent(ev);
+
+ // Move and Drag notifications
+ if (Magnitude(m_CurrentTouch->deltaPos) > 0.001f)
+ {
+ if (previousPress != NULL)
+ {
+ previousPress->SendMessageAny(kOnDragEvent, data);
+ }
+ else if (previousHover != NULL)
+ {
+ if (moveEvents)
+ previousHover->SendMessageAny(kOnMouseMoveEvent, data);
+ }
+ }
+
+ // Hovering over a different object
+ if (previousHover != hover)
+ {
+ if (moveEvents && hover != NULL)
+ hover->SendMessageAny(isDragging ? kOnDragEnterEvent : kOnMouseEnterEvent, data);
+
+ if (moveEvents && previousHover != NULL)
+ previousHover->SendMessageAny(isDragging ? kOnDragExitEvent : kOnMouseExitEvent, data);
+ }
+
+ // Pressed the mouse button on something
+ if (isPressed)
+ {
+ // Change the selection
+ if (m_Selection != hover)
+ {
+ GetGUIState().SetEvent(ev);
+
+ if (hover != NULL)
+ hover->SendMessageAny(kOnSelectEvent, data);
+
+ // Only send the OnDeselect if the Event.current was marked as 'used'
+ if (GetGUIState().GetIsEventUsed())
+ {
+ if (m_Selection != NULL)
+ m_Selection->SendMessageAny(kOnDeselectEvent, data);
+
+ m_Selection = hover;
+ }
+ }
+
+ // Pressed on the hovered item
+ if (hover != NULL)
+ hover->SendMessageAny(kOnPressEvent, data);
+ }
+
+ // Released the mouse button
+ if (isReleased)
+ {
+ if (hover != NULL)
+ {
+ if (previousPress == hover)
+ hover->SendMessageAny(kOnClickEvent, data);
+ else if (hover != NULL)
+ hover->SendMessageAny(kOnDropEvent, data);
+ }
+
+ if (previousPress != NULL)
+ previousPress->SendMessageAny(kOnReleaseEvent, data);
+ }
+
+ // Now that we are done with events we can clear the pressed object.
+ // Clearing it earlier makes the property pointless for scripts: Null is not very informative.
+ if (isReleased) m_CurrentTouch->press = NULL;
+ state.EndUsingEvents();
+}
+#endif
+
+void InputManager::SetKeyState (int key, bool state)
+{
+ // This ignores keyRepeats (multiple keydown without a keyup event between)
+ if (state && !m_CurrentKeyState[key])
+ m_ThisFrameKeyDown[key] = true;
+ if (!state && m_CurrentKeyState[key])
+ m_ThisFrameKeyUp[key] = true;
+
+ m_CurrentKeyState[key] = state;
+}
+
+bool InputManager::ConfigureButton(int *button)
+{
+ for(int key=0;key<kKeyAndMouseButtonCount;key++)
+ if(m_ThisFrameKeyDown[key])
+ {
+ *button=key;
+ return true;
+ }
+ //skip virtual joystick 0
+ for(int key=kKeyAndMouseButtonCount+kMaxJoyStickButtons;key<kKeyAndJoyButtonCount;key++)
+ if(m_ThisFrameKeyDown[key])
+ {
+ *button=key;
+ return true;
+ }
+ return false;
+}
+
+#if SUPPORT_REPRODUCE_LOG
+
+template<class T>
+void WriteFloat (std::ofstream& out, T& value)
+{
+ int intValue = RoundfToInt(value * 1000.0);
+ out << intValue;
+ value = intValue / 1000.0;
+}
+
+template<class T>
+void ReadFloat (std::ifstream& in, T& value)
+{
+ int intValue = 0;
+ in >> intValue;
+ value = intValue / 1000.0;
+}
+
+void WriteBitSet (std::ofstream& out, dynamic_bitset& value)
+{
+ out << value.count() << ' ';
+ for (int i=0;i<value.size();i++)
+ {
+ if (value[i])
+ out << i << ' ';
+ }
+ out << std::endl;
+}
+
+void ReadBitSet (std::ifstream& in, dynamic_bitset& value)
+{
+ int size = 0;
+ in >> size;
+ value.reset();
+ for (int i=0;i<size;i++)
+ {
+ int index = 0;
+ in >> index;
+ value[index] = true;
+ }
+}
+
+void InputManager::WriteLog (std::ofstream& out)
+{
+ out << "Input" << std::endl;
+
+ for (int i=0;i<m_Axes.size();i++)
+ {
+ WriteFloat(out, m_Axes[i].GetValueRef()); out << ' ';
+ WriteFloat(out, m_Axes[i].GetValueRawRef()); out << ' ';
+ }
+
+ out << std::endl;
+
+ WriteBitSet(out, m_CurrentKeyState);
+ WriteBitSet(out, m_ThisFrameKeyDown);
+ WriteBitSet(out, m_ThisFrameKeyUp);
+
+// to_string(m_CurrentKeyState, keystate); out << keystate << std::endl;
+// to_string(m_ThisFrameKeyDown, keystate); out << keystate << std::endl;
+// to_string(m_ThisFrameKeyUp, keystate); out << keystate << std::endl;
+
+
+
+ WriteFloat(out, m_MouseDelta.x); out << ' ';
+ WriteFloat(out, m_MouseDelta.y); out << ' ';
+ WriteFloat(out, m_MousePos.x); out << ' ';
+ WriteFloat(out, m_MousePos.y); out << ' ';
+ float obsoleteWindowDelta = 0;
+ WriteFloat(out, obsoleteWindowDelta); out << ' ';
+ WriteFloat(out, obsoleteWindowDelta); out << std::endl;
+ WriteReproductionString(out, m_InputString);
+
+ out << GetScreenManager().GetAllowCursorLock() << ' ';
+}
+
+
+void InputManager::ReadLog (std::ifstream& in)
+{
+ CheckReproduceTagAndExit("Input", in);
+
+ for (int i=0;i<m_Axes.size();i++)
+ {
+ ReadFloat(in, m_Axes[i].GetValueRef());
+ ReadFloat(in, m_Axes[i].GetValueRawRef());
+ }
+
+ ReadBitSet(in, m_CurrentKeyState);
+ ReadBitSet(in, m_ThisFrameKeyDown);
+ ReadBitSet(in, m_ThisFrameKeyUp);
+
+ ReadFloat(in, m_MouseDelta.x);
+ ReadFloat(in, m_MouseDelta.y);
+
+ ReadFloat(in, m_MousePos.x);
+ ReadFloat(in, m_MousePos.y);
+
+ float obsoleteWindowDelta = 0;
+ ReadFloat(in, obsoleteWindowDelta);
+ ReadFloat(in, obsoleteWindowDelta);
+
+ if (GetReproduceVersion () == 1 || GetReproduceVersion () == 2)
+ {
+ in.ignore(1);
+ getline(in, m_InputString);
+ if (in.peek() != 'E')
+ in.ignore(1);
+ }
+ else
+ {
+ ReadReproductionString(in, m_InputString);
+ }
+
+ if (GetReproduceVersion () >= 5)
+ {
+ bool cursorLock;
+ in >> cursorLock;
+ GetScreenManager().SetAllowCursorLock(cursorLock);
+ }
+}
+#endif
+
+UInt16 NormalizeInputCharacter (UInt16 input)
+{
+ // standardize input characters as different platforms report different
+ // ascii codes for the same input.
+ if (input == 3)
+ return '\n';
+ if (input == '\r')
+ return '\n';
+ if (input == 8 || input == 127) //backspace
+ return '\b';
+
+ return input;
+}
+
+bool InputManager::GetTextFieldInput()
+{
+ return m_TextFieldInput;
+}
+
+bool InputManager::GetEnableIMEComposition()
+{
+ if (m_IMECompositionMode != InputManager::kCompositionModeAuto)
+ return m_IMECompositionMode == InputManager::kCompositionModeOn;
+ else
+ return m_TextFieldInput;
+}
+
+bool InputManager::GetEatKeyPressOnTextFieldFocus()
+{
+ // The "or" predicate ensures that we force input-eating when modal windows are open
+ if(IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_4_a1))
+ return m_EatKeyPressOnTextFieldFocus || (GetGUIState().m_MultiFrameGUIState.m_Windows != NULL && GetGUIState().m_MultiFrameGUIState.m_Windows->m_ModalWindow != NULL);
+#if UNITY_WIN
+ return false;
+#else
+ return true;
+#endif
+}
+
+
+// These stubs are for all the other platforms that do not (yet?) support this.
+#if !UNITY_IPHONE && !UNITY_ANDROID && !UNITY_EDITOR && !UNITY_WINRT && !UNITY_BB10 && !UNITY_TIZEN
+unsigned GetOrientation () { return 0; }
+size_t GetAccelerationCount () { return 0; }
+void GetAcceleration (size_t, struct Acceleration&) {}
+#endif
+
+#if !UNITY_IPHONE && !UNITY_ANDROID && !UNITY_BB10 && !UNITY_EDITOR && !UNITY_TIZEN && !UNITY_METRO && !UNITY_WP8
+bool IsApplicationGenuine () { return true; }
+bool IsApplicationGenuineAvailable () { return false; }
+LocationInfo LocationService::GetLastLocation () { return LocationInfo (); }
+LocationServiceStatus LocationService::GetLocationStatus ()
+{ return kLocationServiceStopped; }
+bool LocationService::IsServiceEnabledByUser () { return true; }
+void LocationService::StartUpdatingLocation () {}
+void LocationService::StopUpdatingLocation () {}
+void LocationService::SetDesiredAccuracy (float) {}
+void LocationService::SetDistanceFilter (float) {}
+#endif
+
+#if !UNITY_IPHONE && !UNITY_EDITOR && !UNITY_ANDROID && !UNITY_METRO && !UNITY_WP8
+void LocationService::SetHeadingUpdatesEnabled (bool enabled) { }
+bool LocationService::IsHeadingUpdatesEnabled() { return false; }
+LocationServiceStatus LocationService::GetHeadingStatus ()
+{ return kLocationServiceStopped; }
+const HeadingInfo &LocationService::GetLastHeading ()
+{
+ static HeadingInfo dummy = { 0, 0, Vector3f::zero, 0 };
+ return dummy;
+}
+bool LocationService::IsHeadingAvailable () { return false; }
+#endif
+
+#if !UNITY_IPHONE && !UNITY_ANDROID && !UNITY_WINRT
+bool IsCompensatingSensors() { return false; }
+void SetCompensatingSensors(bool val) { }
+#endif
+#if !UNITY_IPHONE && !UNITY_ANDROID && !UNITY_EDITOR && !UNITY_METRO && !UNITY_WP8 && !UNITY_BB10 && !UNITY_TIZEN
+Vector3f GetGyroRotationRate(int idx) { return Vector3f(0.0f, 0.0f, 0.0f); }
+bool IsGyroAvailable() { return false; }
+Vector3f GetGyroRotationRateUnbiased(int idx) { return Vector3f(0.0f, 0.0f, 0.0f); }
+Vector3f GetGravity(int idx) { return Vector3f(0.0f, 0.0f, 0.0f); }
+Vector3f GetUserAcceleration(int idx) { return Vector3f(0.0f, 0.0f, 0.0f); }
+Quaternionf GetAttitude(int idx) { return Quaternionf(0.0f, 0.0f, 0.0f, 1.0f); }
+bool IsGyroEnabled(int idx) { return false; }
+void SetGyroEnabled(int idx, bool enabled) {}
+float GetGyroUpdateInterval(int idx) { return 0.0f; }
+void SetGyroUpdateInterval(int idx, float interval) {}
+int GetGyro() { return 0; }
+#endif
+
+#if !UNITY_PS3
+Vector3f GetAcceleration (int controllerID) { return Vector3f(0,0,0); }
+Quaternionf GetRotation(int controllerID) { return Quaternionf(0,0,0,0); }
+Vector3f GetPosition(int controllerID) { return Vector3f(0,0,0); }
+void SetActuator(int controllerID, UInt32 mode) {}
+#endif
+
+#if !UNITY_IPHONE
+// --- iPhoneLocalNotification ---
+iPhoneLocalNotification::iPhoneLocalNotification()
+ : m_Notification(0)
+{ }
+
+iPhoneLocalNotification::iPhoneLocalNotification(UILocalNotification *notification)
+ : m_Notification(0)
+{ }
+
+iPhoneLocalNotification::iPhoneLocalNotification(const iPhoneLocalNotification &other)
+ : m_Notification(0)
+{ }
+
+iPhoneLocalNotification::~iPhoneLocalNotification()
+{ }
+
+iPhoneLocalNotification &iPhoneLocalNotification::operator=(const iPhoneLocalNotification &other)
+{ return *this; }
+
+double iPhoneLocalNotification::GetFireDate() const
+{
+ return 0;
+}
+
+void iPhoneLocalNotification::SetFireDate(const double fireDate)
+{ }
+
+const char *iPhoneLocalNotification::GetTimeZone() const
+{ return 0; }
+
+void iPhoneLocalNotification::SetTimeZone(const char *timeZone)
+{ }
+
+unsigned int iPhoneLocalNotification::GetRepeatInterval() const
+{ return 0; }
+
+void iPhoneLocalNotification::SetRepeatInterval(unsigned int repeatInterval)
+{ }
+
+CalendarIdentifier iPhoneLocalNotification::GetRepeatCalendar() const
+{ return kGregorianCalendar; }
+
+void iPhoneLocalNotification::SetRepeatCalendar(CalendarIdentifier calendar)
+{ }
+
+const char *iPhoneLocalNotification::GetAlertBody() const
+{ return 0; }
+
+void iPhoneLocalNotification::SetAlertBody(const char *message)
+{ }
+
+const char *iPhoneLocalNotification::GetAlertAction() const
+{ return 0; }
+
+void iPhoneLocalNotification::SetAlertAction(const char *action)
+{ }
+
+bool iPhoneLocalNotification::HasAction() const
+{ return false; }
+
+void iPhoneLocalNotification::HasAction(bool yes)
+{ }
+
+const char *iPhoneLocalNotification::GetAlertLaunchImage() const
+{ return 0; }
+
+void iPhoneLocalNotification::SetAlertLaunchImage(const char *imagePath)
+{ }
+
+int iPhoneLocalNotification::GetApplicationIconBadgeNumber() const
+{ return 0; }
+
+void iPhoneLocalNotification::SetApplicationIconBadgeNumber(int number)
+{ }
+
+const char *iPhoneLocalNotification::GetSoundName() const
+{ return 0; }
+
+void iPhoneLocalNotification::SetSoundName(const char *soundName)
+{ }
+
+const char *iPhoneLocalNotification::GetDefaultSoundName()
+{ return 0; }
+
+MonoObject *iPhoneLocalNotification::GetUserInfo() const
+{ return 0; }
+
+void iPhoneLocalNotification::SetUserInfo(MonoObject *userInfo)
+{ }
+
+void iPhoneLocalNotification::Schedule()
+{ }
+
+void iPhoneLocalNotification::PresentNow()
+{ }
+
+void iPhoneLocalNotification::Cancel()
+{ }
+
+void iPhoneLocalNotification::CancelAll()
+{ }
+
+std::vector<iPhoneLocalNotification*> iPhoneLocalNotification::GetScheduled()
+{ return std::vector<iPhoneLocalNotification*>(); }
+
+// ---
+
+size_t GetLocalNotificationCount()
+{ return 0; }
+
+iPhoneLocalNotification *CopyLocalNotification(unsigned index)
+{ return 0; }
+
+void ClearLocalNotifications()
+{ }
+
+
+// --- iPhoneRemoteNotification ---
+
+iPhoneRemoteNotification::iPhoneRemoteNotification(NSDictionary *notification)
+ : m_HasAction(false),
+ m_Badge(0),
+ m_UserInfo(0)
+{ }
+
+iPhoneRemoteNotification::iPhoneRemoteNotification(const iPhoneRemoteNotification &other)
+: m_HasAction(false),
+ m_Badge(0),
+ m_UserInfo(0)
+{ }
+
+iPhoneRemoteNotification::~iPhoneRemoteNotification()
+{ }
+
+iPhoneRemoteNotification &iPhoneRemoteNotification::operator=(const iPhoneRemoteNotification &other)
+{ return *this; }
+
+const char *iPhoneRemoteNotification::GetAlertBody() const
+{ return 0; }
+
+bool iPhoneRemoteNotification::HasAction() const
+{ return false; }
+
+int iPhoneRemoteNotification::GetApplicationIconBadgeNumber() const
+{ return 0; }
+
+const char *iPhoneRemoteNotification::GetSoundName() const
+{ return 0; }
+
+MonoObject *iPhoneRemoteNotification::GetUserInfo() const
+{ return 0; }
+
+void iPhoneRemoteNotification::Register(int notifTypes)
+{ }
+
+void iPhoneRemoteNotification::Unregister()
+{ }
+
+int iPhoneRemoteNotification::GetEnabledTypes()
+{ return 0; }
+
+int iPhoneRemoteNotification::GetDeviceTokenLength()
+{ return 0; }
+
+const char *iPhoneRemoteNotification::GetDeviceToken()
+{ return 0; }
+
+void iPhoneRemoteNotification::SetDeviceToken(NSData *deviceToken)
+{ }
+
+//void iPhoneRemoteNotification::SendProviderDeviceToken(NSData *deviceToken)
+//{ }
+
+void iPhoneRemoteNotification::SetError(NSError *error)
+{ }
+
+const char *iPhoneRemoteNotification::GetError()
+{ return 0; }
+
+// ---
+
+size_t GetRemoteNotificationCount()
+{ return 0; }
+
+iPhoneRemoteNotification *CopyRemoteNotification(unsigned index)
+{ return 0; }
+
+void ClearRemoteNotifications()
+{ }
+
+#endif
diff --git a/Runtime/Input/InputManager.h b/Runtime/Input/InputManager.h
new file mode 100644
index 0000000..1fe4a70
--- /dev/null
+++ b/Runtime/Input/InputManager.h
@@ -0,0 +1,470 @@
+#ifndef INPUTMANAGER_H
+#define INPUTMANAGER_H
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include <map>
+#include "Runtime/Input/InputAxis.h"
+#include "Runtime/Utilities/dynamic_bitset.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Vector3.h"
+#include "Configuration/UnityConfigure.h"
+#include <iosfwd>
+
+#if ENABLE_NEW_EVENT_SYSTEM
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Input/GetInput.h"
+#endif
+
+#include "Runtime/ClusterRenderer/ClusterRendererDefines.h"
+
+enum {
+ kMaxJoySticks = 12,
+ kMaxJoyStickButtons = 20,
+ kMaxJoyStickAxis = 20
+};
+
+enum {
+ kKeyCount = 323,
+ kMouseButtonCount = 7,
+ kKeyAndMouseButtonCount = kKeyCount + kMouseButtonCount,
+ kKeyAndJoyButtonCount = kKeyAndMouseButtonCount + kMaxJoySticks * kMaxJoyStickButtons
+};
+
+class InputManager : public GlobalGameManager
+{
+ public:
+
+ InputManager (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~InputManager(); declared-by-macro
+
+ REGISTER_DERIVED_CLASS (InputManager, GlobalGameManager)
+ DECLARE_OBJECT_SERIALIZE (InputManager)
+ // for cluster renderer
+ DECLARE_CLUSTER_SERIALIZE (InputManager)
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ /// Make up some sensible controls...
+ void MakeDefault ();
+
+ enum { kCompositionModeAuto, kCompositionModeOn, kCompositionModeOff };
+
+ bool GetButton (const string &name);
+ bool GetButtonDown (const string &name);
+ bool GetButtonUp (const string &name);
+ float GetAxis (const string &name);
+ float GetAxisRaw (const string &name);
+ bool HasAxisOrButton (const string& name);
+
+ void ProcessInput();
+ void SendInputEvents();
+#if ENABLE_NEW_EVENT_SYSTEM
+ void ProcessInput (const GameObject& go);
+
+private:
+ void ProcessMouse ();
+ void ProcessTouches ();
+ void ProcessTouch (GameObject* hover, bool isPressed, bool isReleased, bool isDragging, bool moveEvents);
+#endif
+
+public:
+ void InputEndFrame ();
+
+ // Is the button down or was down during this frame?
+ bool GetMouseButton (int mouseBut) const { return GetKey (kKeyCount + mouseBut); }
+ bool GetMouseButtonState (int mouseBut) const { return m_CurrentKeyState[kKeyCount + mouseBut]; }
+
+ // Is the button down or was down during this frame?
+ bool GetMouseButtonDown (int mouseBut) const { return GetKeyDown (kKeyCount + mouseBut); }
+ bool GetMouseButtonUp (int mouseBut) const { return GetKeyUp (kKeyCount + mouseBut); }
+
+ // Is the key down or was down during this frame?
+ bool GetKey (int keyNum) const { return m_ThisFrameKeyDown[keyNum] | m_CurrentKeyState[keyNum]; }
+ // Is the key down this frame but was up last frame?
+ bool GetKeyDown (int keyNum) const { return m_ThisFrameKeyDown[keyNum]; }
+ bool GetKeyUp (int keyNum) const { return m_ThisFrameKeyUp[keyNum]; }
+
+#if ENABLE_NEW_EVENT_SYSTEM
+ const Touch& GetMouse() { return m_Mouse[0]; }
+ const Vector2f &GetMouseDelta () const { return m_Mouse[0].deltaPos; }
+ const Vector2f &GetMouseScroll () const { return m_Mouse[0].deltaScroll; }
+ const Vector2f &GetMousePosition () const { return m_Mouse[0].pos; }
+
+ // Set prior to OnPress / OnRelease / OnDrag etc callbacks
+ const Touch& GetCurrentTouch() const { return m_CurrentTouch != NULL ? *m_CurrentTouch : m_Mouse[0]; }
+#else
+ const Vector3f &GetMouseDelta () const { return m_MouseDelta; }
+ const Vector2f &GetMousePosition () const { return m_MousePos; }
+#endif
+
+ float GetJoystickPosition (int joyNum, int axis) const;
+ void SetJoystickPosition (int joyNum, int axis, float pos);
+
+ void SetKeyState (int key, bool state);
+#if ENABLE_NEW_EVENT_SYSTEM
+ void SetMouseDelta (const Vector2f &delta) { m_Mouse[0].deltaPos = delta; }
+ void SetMouseScroll (const Vector2f& delta) { m_Mouse[0].deltaScroll = delta; }
+ void SetMousePosition (const Vector2f &pos) { m_Mouse[0].pos = pos; }
+#else
+ void SetMouseDelta (const Vector3f &pos) { m_MouseDelta = pos; }
+ void SetMousePosition (const Vector2f &pos) { m_MousePos = pos; }
+#endif
+ void SetMouseButton (int button, bool enabled) { SetKeyState (kKeyCount + button, enabled); }
+
+ void QuitApplication () { m_ShouldQuit = true; }
+ void CancelQuitApplication () { m_ShouldQuit = false; }
+ bool ShouldQuit () const { return m_ShouldQuit; }
+
+ string& GetInputString () { return m_InputString; }
+ string& GetCompositionString () { return m_CompositionString;}
+
+ bool GetSimulateMouseWithTouches () const { return m_SimulateMouseWithTouches; }
+ void SetSimulateMouseWithTouches (bool value) { m_SimulateMouseWithTouches = value; }
+
+ void SetTextFieldInput(bool value) { m_TextFieldInput = value; }
+ bool GetTextFieldInput();
+
+ Vector2f &GetTextFieldCursorPos() { return m_TextFieldCursorPos; }
+ void SetTextFieldCursorPos(Vector2f &value) { m_TextFieldCursorPos = value; }
+
+ bool GetEatKeyPressOnTextFieldFocus ();
+ void SetEatKeyPressOnTextFieldFocus (bool value) { m_EatKeyPressOnTextFieldFocus = value; }
+
+ int GetIMECompositionMode () { return m_IMECompositionMode; }
+ void SetIMECompositionMode (int value) { m_IMECompositionMode = value; }
+
+ bool GetIMEIsSelected() { return m_IMEIsSelected; }
+ void SetIMEIsSelected(bool value) { m_IMEIsSelected = value; }
+
+ bool GetEnableIMEComposition ();
+
+ /// Needed here because m_Axes need to be preprocessed before use.
+ virtual void CheckConsistency ();
+ void ResetInputAxes ();
+
+ bool GetAnyKey ();
+ bool GetAnyKeyThisFrame ();
+
+ virtual void Reset ();
+
+ bool ConfigureButton(int *button);
+
+ int NumAxes() {return m_Axes.size();}
+ InputAxis *GetIndexedAxis(int i) {if(i<m_Axes.size())return &(m_Axes[i]); else return NULL;}
+
+ #if SUPPORT_REPRODUCE_LOG
+ void WriteLog (std::ofstream& out);
+ void ReadLog (std::ifstream& in);
+ #endif
+
+ const std::vector< std::vector<float> >& GetJoystickPositions() const { return m_JoystickPos; }
+
+ private:
+ std::vector<InputAxis> m_Axes;
+
+ // There are shortclick keys because in one frame the player might hit a key and release it.
+ // We want the key to be down for one frame in this case.
+ dynamic_bitset m_CurrentKeyState;
+ dynamic_bitset m_ThisFrameKeyDown;
+ dynamic_bitset m_ThisFrameKeyUp;
+
+#if ENABLE_NEW_EVENT_SYSTEM
+ Touch m_Mouse[3];
+ Touch* m_CurrentTouch;
+ GameObject* m_Selection;
+#else
+ Vector3f m_MouseDelta; //x and y for delta, z for scrollwheel
+ Vector2f m_MousePos;
+#endif
+ bool m_MousePresent;
+
+ std::vector<std::vector<float> > m_JoystickPos;
+
+ std::string m_InputString;
+ std::string m_CompositionString;
+
+ Vector2f m_TextFieldCursorPos;
+ bool m_TextFieldInput;
+
+ bool m_EatKeyPressOnTextFieldFocus;
+ int m_IMECompositionMode;
+ bool m_IMEIsSelected;
+
+ int m_LastJoyNum,m_LastJoyAxis;
+ bool m_ShouldQuit;
+ bool m_SimulateMouseWithTouches;
+
+ friend bool GetConfigureJoyAxis(std::string name,int *joyNum,int *axis);
+};
+
+InputManager* GetInputManagerPtr ();
+InputManager& GetInputManager ();
+
+string KeyToString (int key);
+int StringToKey (const string& name);
+
+UInt16 NormalizeInputCharacter (UInt16 input);
+
+enum {
+ /* The keyboard syms have been cleverly chosen to map to ASCII */
+ SDLK_UNKNOWN = 0,
+ SDLK_FIRST = 0,
+ SDLK_BACKSPACE = 8,
+ SDLK_TAB = 9,
+ SDLK_CLEAR = 12,
+ SDLK_RETURN = 13,
+ SDLK_PAUSE = 19,
+ SDLK_ESCAPE = 27,
+ SDLK_SPACE = 32,
+ SDLK_EXCLAIM = 33,
+ SDLK_QUOTEDBL = 34,
+ SDLK_HASH = 35,
+ SDLK_DOLLAR = 36,
+ SDLK_AMPERSAND = 38,
+ SDLK_QUOTE = 39,
+ SDLK_LEFTPAREN = 40,
+ SDLK_RIGHTPAREN = 41,
+ SDLK_ASTERISK = 42,
+ SDLK_PLUS = 43,
+ SDLK_COMMA = 44,
+ SDLK_MINUS = 45,
+ SDLK_PERIOD = 46,
+ SDLK_SLASH = 47,
+ SDLK_0 = 48,
+ SDLK_1 = 49,
+ SDLK_2 = 50,
+ SDLK_3 = 51,
+ SDLK_4 = 52,
+ SDLK_5 = 53,
+ SDLK_6 = 54,
+ SDLK_7 = 55,
+ SDLK_8 = 56,
+ SDLK_9 = 57,
+ SDLK_COLON = 58,
+ SDLK_SEMICOLON = 59,
+ SDLK_LESS = 60,
+ SDLK_EQUALS = 61,
+ SDLK_GREATER = 62,
+ SDLK_QUESTION = 63,
+ SDLK_AT = 64,
+ /*
+ Skip uppercase letters
+ */
+ SDLK_LEFTBRACKET = 91,
+ SDLK_BACKSLASH = 92,
+ SDLK_RIGHTBRACKET = 93,
+ SDLK_CARET = 94,
+ SDLK_UNDERSCORE = 95,
+ SDLK_BACKQUOTE = 96,
+ SDLK_a = 97,
+ SDLK_b = 98,
+ SDLK_c = 99,
+ SDLK_d = 100,
+ SDLK_e = 101,
+ SDLK_f = 102,
+ SDLK_g = 103,
+ SDLK_h = 104,
+ SDLK_i = 105,
+ SDLK_j = 106,
+ SDLK_k = 107,
+ SDLK_l = 108,
+ SDLK_m = 109,
+ SDLK_n = 110,
+ SDLK_o = 111,
+ SDLK_p = 112,
+ SDLK_q = 113,
+ SDLK_r = 114,
+ SDLK_s = 115,
+ SDLK_t = 116,
+ SDLK_u = 117,
+ SDLK_v = 118,
+ SDLK_w = 119,
+ SDLK_x = 120,
+ SDLK_y = 121,
+ SDLK_z = 122,
+ SDLK_DELETE = 127,
+ /* End of ASCII mapped keysyms */
+
+ /* International keyboard syms */
+ SDLK_WORLD_0 = 160, /* 0xA0 */
+ SDLK_WORLD_1 = 161,
+ SDLK_WORLD_2 = 162,
+ SDLK_WORLD_3 = 163,
+ SDLK_WORLD_4 = 164,
+ SDLK_WORLD_5 = 165,
+ SDLK_WORLD_6 = 166,
+ SDLK_WORLD_7 = 167,
+ SDLK_WORLD_8 = 168,
+ SDLK_WORLD_9 = 169,
+ SDLK_WORLD_10 = 170,
+ SDLK_WORLD_11 = 171,
+ SDLK_WORLD_12 = 172,
+ SDLK_WORLD_13 = 173,
+ SDLK_WORLD_14 = 174,
+ SDLK_WORLD_15 = 175,
+ SDLK_WORLD_16 = 176,
+ SDLK_WORLD_17 = 177,
+ SDLK_WORLD_18 = 178,
+ SDLK_WORLD_19 = 179,
+ SDLK_WORLD_20 = 180,
+ SDLK_WORLD_21 = 181,
+ SDLK_WORLD_22 = 182,
+ SDLK_WORLD_23 = 183,
+ SDLK_WORLD_24 = 184,
+ SDLK_WORLD_25 = 185,
+ SDLK_WORLD_26 = 186,
+ SDLK_WORLD_27 = 187,
+ SDLK_WORLD_28 = 188,
+ SDLK_WORLD_29 = 189,
+ SDLK_WORLD_30 = 190,
+ SDLK_WORLD_31 = 191,
+ SDLK_WORLD_32 = 192,
+ SDLK_WORLD_33 = 193,
+ SDLK_WORLD_34 = 194,
+ SDLK_WORLD_35 = 195,
+ SDLK_WORLD_36 = 196,
+ SDLK_WORLD_37 = 197,
+ SDLK_WORLD_38 = 198,
+ SDLK_WORLD_39 = 199,
+ SDLK_WORLD_40 = 200,
+ SDLK_WORLD_41 = 201,
+ SDLK_WORLD_42 = 202,
+ SDLK_WORLD_43 = 203,
+ SDLK_WORLD_44 = 204,
+ SDLK_WORLD_45 = 205,
+ SDLK_WORLD_46 = 206,
+ SDLK_WORLD_47 = 207,
+ SDLK_WORLD_48 = 208,
+ SDLK_WORLD_49 = 209,
+ SDLK_WORLD_50 = 210,
+ SDLK_WORLD_51 = 211,
+ SDLK_WORLD_52 = 212,
+ SDLK_WORLD_53 = 213,
+ SDLK_WORLD_54 = 214,
+ SDLK_WORLD_55 = 215,
+ SDLK_WORLD_56 = 216,
+ SDLK_WORLD_57 = 217,
+ SDLK_WORLD_58 = 218,
+ SDLK_WORLD_59 = 219,
+ SDLK_WORLD_60 = 220,
+ SDLK_WORLD_61 = 221,
+ SDLK_WORLD_62 = 222,
+ SDLK_WORLD_63 = 223,
+ SDLK_WORLD_64 = 224,
+ SDLK_WORLD_65 = 225,
+ SDLK_WORLD_66 = 226,
+ SDLK_WORLD_67 = 227,
+ SDLK_WORLD_68 = 228,
+ SDLK_WORLD_69 = 229,
+ SDLK_WORLD_70 = 230,
+ SDLK_WORLD_71 = 231,
+ SDLK_WORLD_72 = 232,
+ SDLK_WORLD_73 = 233,
+ SDLK_WORLD_74 = 234,
+ SDLK_WORLD_75 = 235,
+ SDLK_WORLD_76 = 236,
+ SDLK_WORLD_77 = 237,
+ SDLK_WORLD_78 = 238,
+ SDLK_WORLD_79 = 239,
+ SDLK_WORLD_80 = 240,
+ SDLK_WORLD_81 = 241,
+ SDLK_WORLD_82 = 242,
+ SDLK_WORLD_83 = 243,
+ SDLK_WORLD_84 = 244,
+ SDLK_WORLD_85 = 245,
+ SDLK_WORLD_86 = 246,
+ SDLK_WORLD_87 = 247,
+ SDLK_WORLD_88 = 248,
+ SDLK_WORLD_89 = 249,
+ SDLK_WORLD_90 = 250,
+ SDLK_WORLD_91 = 251,
+ SDLK_WORLD_92 = 252,
+ SDLK_WORLD_93 = 253,
+ SDLK_WORLD_94 = 254,
+ SDLK_WORLD_95 = 255, /* 0xFF */
+
+ /* Numeric keypad */
+ SDLK_KP0 = 256,
+ SDLK_KP1 = 257,
+ SDLK_KP2 = 258,
+ SDLK_KP3 = 259,
+ SDLK_KP4 = 260,
+ SDLK_KP5 = 261,
+ SDLK_KP6 = 262,
+ SDLK_KP7 = 263,
+ SDLK_KP8 = 264,
+ SDLK_KP9 = 265,
+ SDLK_KP_PERIOD = 266,
+ SDLK_KP_DIVIDE = 267,
+ SDLK_KP_MULTIPLY = 268,
+ SDLK_KP_MINUS = 269,
+ SDLK_KP_PLUS = 270,
+ SDLK_KP_ENTER = 271,
+ SDLK_KP_EQUALS = 272,
+
+ /* Arrows + Home/End pad */
+ SDLK_UP = 273,
+ SDLK_DOWN = 274,
+ SDLK_RIGHT = 275,
+ SDLK_LEFT = 276,
+ SDLK_INSERT = 277,
+ SDLK_HOME = 278,
+ SDLK_END = 279,
+ SDLK_PAGEUP = 280,
+ SDLK_PAGEDOWN = 281,
+
+ /* Function keys */
+ SDLK_F1 = 282,
+ SDLK_F2 = 283,
+ SDLK_F3 = 284,
+ SDLK_F4 = 285,
+ SDLK_F5 = 286,
+ SDLK_F6 = 287,
+ SDLK_F7 = 288,
+ SDLK_F8 = 289,
+ SDLK_F9 = 290,
+ SDLK_F10 = 291,
+ SDLK_F11 = 292,
+ SDLK_F12 = 293,
+ SDLK_F13 = 294,
+ SDLK_F14 = 295,
+ SDLK_F15 = 296,
+
+ /* Key state modifier keys */
+ SDLK_NUMLOCK = 300,
+ SDLK_CAPSLOCK = 301,
+ SDLK_SCROLLOCK = 302,
+ SDLK_RSHIFT = 303,
+ SDLK_LSHIFT = 304,
+ SDLK_RCTRL = 305,
+ SDLK_LCTRL = 306,
+ SDLK_RALT = 307,
+ SDLK_LALT = 308,
+ SDLK_RMETA = 309,
+ SDLK_LMETA = 310,
+ SDLK_RGUI = 309,
+ SDLK_LGUI = 310,
+ SDLK_LSUPER = 311, /* Left "Windows" key */
+ SDLK_RSUPER = 312, /* Right "Windows" key */
+ SDLK_MODE = 313, /* "Alt Gr" key */
+ SDLK_COMPOSE = 314, /* Multi-key compose key */
+
+ /* Miscellaneous function keys */
+ SDLK_HELP = 315,
+ SDLK_PRINT = 316,
+ SDLK_SYSREQ = 317,
+ SDLK_BREAK = 318,
+ SDLK_MENU = 319,
+ SDLK_POWER = 320, /* Power Macintosh power key */
+ SDLK_EURO = 321, /* Some european keyboards */
+ SDLK_UNDO = 322, /* Atari keyboard has Undo */
+
+ /* Add any other keys here */
+
+ SDLK_LAST
+};
+
+
+#endif
diff --git a/Runtime/Input/LocationService.h b/Runtime/Input/LocationService.h
new file mode 100644
index 0000000..eecd4d3
--- /dev/null
+++ b/Runtime/Input/LocationService.h
@@ -0,0 +1,51 @@
+#ifndef UNITY_LOCATION_SERVICE_H_
+#define UNITY_LOCATION_SERVICE_H_
+
+#include "Runtime/Math/Vector3.h"
+
+struct LocationInfo
+{
+ double timestamp;
+ float latitude;
+ float longitude;
+ float altitude;
+ float horizontalAccuracy;
+ float verticalAccuracy;
+};
+
+struct HeadingInfo
+{
+ float magneticHeading;
+ float trueHeading;
+ Vector3f raw;
+ double timestamp;
+};
+
+enum LocationServiceStatus
+{
+ kLocationServiceStopped,
+ kLocationServiceInitializing,
+ kLocationServiceRunning,
+ kLocationServiceFailed
+};
+
+class LocationService
+{
+public:
+ static void SetDesiredAccuracy (float val);
+ static float GetDesiredAccuracy ();
+ static void SetDistanceFilter (float val);
+ static float GetDistanceFilter ();
+ static bool IsServiceEnabledByUser ();
+ static void StartUpdatingLocation ();
+ static void StopUpdatingLocation ();
+ static void SetHeadingUpdatesEnabled (bool enabled);
+ static bool IsHeadingUpdatesEnabled();
+ static LocationServiceStatus GetLocationStatus ();
+ static LocationServiceStatus GetHeadingStatus ();
+ static LocationInfo GetLastLocation ();
+ static const HeadingInfo &GetLastHeading ();
+ static bool IsHeadingAvailable ();
+};
+
+#endif // #ifndef UNITY_LOCATION_SERVICE_H_
diff --git a/Runtime/Input/OnScreenKeyboard.h b/Runtime/Input/OnScreenKeyboard.h
new file mode 100644
index 0000000..a828173
--- /dev/null
+++ b/Runtime/Input/OnScreenKeyboard.h
@@ -0,0 +1,47 @@
+#ifndef UNITY_ONSCREEN_KEYBOARD_
+#define UNITY_ONSCREEN_KEYBOARD_
+
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Graphics/ScreenManager.h"
+
+class KeyboardOnScreen
+{
+public:
+
+ static Rectf GetRect();
+ static bool IsVisible();
+
+ static void Hide();
+ static void setInputHidden(bool flag);
+ static bool isInputHidden();
+
+public:
+ KeyboardOnScreen(std::string const& text, UInt32 keyboardType = 0,
+ bool autocorrection = true, bool multiline = false,
+ bool secure = false, bool alert = false,
+ std::string const& textPlaceholder = "");
+ ~KeyboardOnScreen();
+ bool isActive() const;
+ bool isDone() const;
+ bool wasCanceled() const;
+ std::string getText() const;
+ void setText(std::string text);
+ void setActive(bool flag = true);
+
+private:
+ #if UNITY_WP8
+ static BridgeInterface::IWP8Keyboard^ ms_Keyboard;
+ BridgeInterface::IWP8Keyboard^ m_Keyboard;
+ #endif
+ ColorRGBA32 textColor;
+ ColorRGBA32 backgroundColor;
+ UInt32 keyboardType;
+ std::string textPlaceholder;
+ bool autocorrection;
+ bool multiline;
+ bool secure;
+ bool alert;
+};
+
+#endif
diff --git a/Runtime/Input/SimulateInputEvents.cpp b/Runtime/Input/SimulateInputEvents.cpp
new file mode 100644
index 0000000..4802477
--- /dev/null
+++ b/Runtime/Input/SimulateInputEvents.cpp
@@ -0,0 +1,222 @@
+#include "UnityPrefix.h"
+#include "SimulateInputEvents.h"
+#include "GetInput.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "InputManager.h"
+#include "Runtime/IMGUI/GUIManager.h"
+#include <math.h>
+
+#if UNITY_EDITOR && UNITY_OSX
+#include "Editor/Src/RemoteInput/iPhoneRemoteImpl.h"
+#endif
+
+#if UNITY_EDITOR
+#include "Editor/Src/RemoteInput/AndroidRemote.h"
+#endif
+
+#ifndef MAXFLOAT
+ #define MAXFLOAT ((float)3.40282346638528860e+38)
+#endif
+
+void CaptureEventMousePosition (InputEvent& e)
+{
+ e.Init();
+
+ Vector2f p = GetInputManager().GetMousePosition();
+
+#if ENABLE_NEW_EVENT_SYSTEM
+ e.touch.pos = p;
+ e.touch.pos.y = GetScreenManager().GetHeight() - p.y;
+ e.touch.deltaPos = GetInputManager().GetMouseDelta();
+#else
+ e.mousePosition = p;
+ e.mousePosition.y = GetScreenManager().GetHeight() - e.mousePosition.y;
+ Vector3f d = GetInputManager().GetMouseDelta();
+ e.delta = Vector2f(d.x, d.y);
+#endif
+ e.pressure = 1.0f;
+
+ e.clickCount = 1;
+
+ size_t touchCount = GetActiveTouchCount();
+
+ for (int i = 0; i < touchCount; ++i)
+ {
+#if ENABLE_NEW_EVENT_SYSTEM
+ Touch* touch = GetTouch(i);
+
+ if (touch != NULL)
+ {
+ if (touch->tapCount > e.clickCount)
+ {
+ e.clickCount = touch->tapCount;
+ }
+ }
+#else
+ Touch touch;
+
+ if (GetTouch(i, touch))
+ {
+ if (touch.tapCount > e.clickCount)
+ {
+ e.clickCount = touch.tapCount;
+ }
+ }
+#endif
+ }
+}
+
+// send event on button down/up
+void GenerateAndSendInputDownUpEvent( const InputEvent::MouseButton button, const bool isDown )
+{
+ InputEvent ie;
+ CaptureEventMousePosition (ie);
+ ie.button = button;
+ ie.type = isDown ? InputEvent::kMouseDown : InputEvent::kMouseUp;
+#if UNITY_METRO
+ ie.touchType = InputEvent::kFingerTouch;
+#endif
+ GetGUIManager().QueueEvent (ie);
+ // If the touch has ended we need to "disable" the mouse hover state, by moving the mouse pointer "away"
+ if (!isDown)
+ {
+ ie.Init();
+ ie.type = InputEvent::kMouseUp;
+#if ENABLE_NEW_EVENT_SYSTEM
+ ie.touch.pos = Vector2f(MAXFLOAT,MAXFLOAT);
+#else
+ ie.mousePosition = Vector2f(MAXFLOAT,MAXFLOAT);
+#endif
+ GetGUIManager().QueueEvent (ie);
+ }
+}
+
+void RestoreMouseState( const Vector2f& mousePosition )
+{
+ enum { MaxSimulatedMouseButtons = 3 };
+ GetInputManager().SetMousePosition(mousePosition);
+ if (GetActiveTouchCount() > 0)
+ {
+ for (int i = 0; i < MaxSimulatedMouseButtons; ++i)
+ GetInputManager().SetMouseButton(i, false);
+ GetInputManager().InputEndFrame();
+ }
+}
+
+void SimulateMouseInput()
+{
+ enum { MaxSimulatedMouseButtons = 3 };
+ static size_t prevTouchPointCount = 0;
+
+#if UNITY_WINRT
+ Vector2f originalPos = GetInputManager().GetMousePosition();
+#endif
+
+ for (int i = 0; i < MaxSimulatedMouseButtons; ++i)
+ {
+ if (i < GetActiveTouchCount())
+ GetInputManager().SetMouseButton(i, true);
+ else if (i < prevTouchPointCount)
+ GetInputManager().SetMouseButton(i, false);
+ }
+
+ prevTouchPointCount = GetActiveTouchCount();
+
+ size_t touchCount = GetTouchCount();
+ Vector2f pos(0.0f, 0.0f);
+ static Vector2f prevPos(0.0f, 0.0f);
+
+ for (int i = 0; i < GetTouchCount(); ++i)
+ {
+#if ENABLE_NEW_EVENT_SYSTEM
+ Touch* touch = GetTouch(i);
+ if (touch != NULL)
+ pos += touch->pos;
+#else
+ Touch touch;
+ if (GetTouch(i, touch))
+ pos += touch.pos;
+#endif
+ }
+
+ if (touchCount > 0)
+ {
+ float invCount = 1.0f / (float)touchCount;
+ pos.x *= invCount;
+ pos.y *= invCount;
+
+ GetInputManager().SetMousePosition(pos);
+#if ENABLE_NEW_EVENT_SYSTEM
+ GetInputManager().SetMouseDelta(Vector2f(pos.x - prevPos.x, pos.y - prevPos.y));
+#else
+ GetInputManager().SetMouseDelta(Vector3f(pos.x - prevPos.x,
+ pos.y - prevPos.y, 0.0f));
+#endif
+ prevPos = pos;
+ }
+
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_WINRT || UNITY_BB10 || UNITY_TIZEN
+ SimulateInputEvents ();
+
+#elif UNITY_EDITOR
+ if (
+#if UNITY_OSX
+ iPhoneHasRemoteConnected () ||
+#endif
+ AndroidHasRemoteConnected())
+ {
+ SimulateInputEvents();
+ }
+#endif
+
+#if UNITY_WINRT
+ if (!GetInputManager().GetSimulateMouseWithTouches())
+ RestoreMouseState(originalPos);
+#endif
+}
+
+void SimulateInputEvents()
+{
+ InputEvent ie;
+
+ static bool lastMouseB0 = false;
+ static bool lastMouseB1 = false;
+
+ if (SqrMagnitude(GetInputManager().GetMouseDelta()) > 1e-6)
+ {
+ CaptureEventMousePosition (ie);
+ ie.type = InputEvent::kMouseMove;
+ ie.button = 0;
+#if UNITY_METRO
+ ie.touchType = InputEvent::kFingerTouch;
+#endif
+
+ if (GetInputManager().GetMouseButton(0) && lastMouseB0)
+ {
+ ie.type = InputEvent::kMouseDrag;
+ ie.button |= InputEvent::kLeftButton;
+ }
+ if (GetInputManager().GetMouseButton(1) && lastMouseB1)
+ {
+ ie.type = InputEvent::kMouseDrag;
+ ie.button |= InputEvent::kRightButton;
+ }
+
+ GetGUIManager().QueueEvent (ie);
+ }
+
+ bool buttonDown = GetInputManager().GetMouseButton(0);
+ if (buttonDown != lastMouseB0)
+ {
+ GenerateAndSendInputDownUpEvent( InputEvent::kLeftButton, buttonDown );
+ lastMouseB0 = buttonDown;
+ }
+
+ buttonDown = GetInputManager().GetMouseButton(1);
+ if (buttonDown != lastMouseB1)
+ {
+ GenerateAndSendInputDownUpEvent( InputEvent::kRightButton, buttonDown );
+ lastMouseB1 = buttonDown;
+ }
+}
+
diff --git a/Runtime/Input/SimulateInputEvents.h b/Runtime/Input/SimulateInputEvents.h
new file mode 100644
index 0000000..9624e4c
--- /dev/null
+++ b/Runtime/Input/SimulateInputEvents.h
@@ -0,0 +1,10 @@
+#ifndef UNITY_SIMULATE_INPUT_EVENTS_
+#define UNITY_SIMULATE_INPUT_EVENTS_
+
+void SimulateInputEvents();
+void SimulateMouseInput();
+
+// Implemented by various platforms:
+size_t GetActiveTouchCount ();
+
+#endif
diff --git a/Runtime/Input/TimeManager.cpp b/Runtime/Input/TimeManager.cpp
new file mode 100644
index 0000000..faadada
--- /dev/null
+++ b/Runtime/Input/TimeManager.cpp
@@ -0,0 +1,489 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "TimeManager.h"
+#include <limits>
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Math/FloatConversion.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/ReproductionLog.h"
+#include "Runtime/Threads/Thread.h"
+#if SUPPORT_REPRODUCE_LOG
+#include <fstream>
+#endif
+
+using namespace std;
+
+
+#define DEBUG_TIME_MANAGER 0
+
+
+const float kMaximumDeltaTime = 1.0F / 3.0F;
+const float kStartupDeltaTime = 0.02F;
+const float kNewDeltaTimeWeight = 0.2F; // for smoothing
+
+float CalcInvDeltaTime (float dt)
+{
+ if (dt > kMinimumDeltaTime)
+ return 1.0F / dt;
+ else
+ return 1.0F;
+}
+
+TimeManager::TimeHolder::TimeHolder()
+: m_CurFrameTime(0)
+, m_LastFrameTime(0)
+, m_DeltaTime(0)
+, m_SmoothDeltaTime(0)
+, m_SmoothingWeight(0)
+, m_InvDeltaTime(0)
+{
+}
+
+TimeManager::TimeManager (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_CullFrameCount(0)
+{
+ m_SetTimeManually = false;
+ m_UseFixedTimeStep = false;
+
+ m_FixedTime.m_SmoothDeltaTime = m_FixedTime.m_DeltaTime;
+
+ m_TimeScale = 1.0F;
+ m_FixedTime.m_DeltaTime = 0.02F;
+ m_MaximumTimestep = kMaximumDeltaTime;
+ m_LastSyncEnd = 0;
+ ResetTime ();
+}
+
+TimeManager::~TimeManager () {}
+
+inline void CalcSmoothDeltaTime (TimeManager::TimeHolder& time)
+{
+ // If existing weight is zero, don't take existing value into account
+ time.m_SmoothingWeight *= (1.0F - kNewDeltaTimeWeight);
+ time.m_SmoothingWeight += kNewDeltaTimeWeight;
+ // As confidence in smoothed value increases the divisor goes towards 1
+ float normalized = kNewDeltaTimeWeight / time.m_SmoothingWeight;
+ time.m_SmoothDeltaTime = Lerp (time.m_SmoothDeltaTime, time.m_DeltaTime, normalized);
+}
+
+void TimeManager::SetPause(bool pause)
+{
+ m_FirstFrameAfterPause = true;
+}
+
+void TimeManager::Sync (float framerate)
+{
+ double time = GetTimeSinceStartup ();
+ // wait for enough time to pass for the requested framerate
+ if (framerate > 0)
+ {
+ // Wait a bit less (0.1ms), to accomodate for small fluctuations (gives much better result in webplayers)
+ double frameTime = 1.0/(double)framerate - 0.0001;
+ if (!CompareApproximately(time, m_LastSyncEnd) && time - m_LastSyncEnd < frameTime)
+ {
+#if SUPPORT_THREADS
+ Thread::Sleep(frameTime - (time-m_LastSyncEnd));
+#endif
+ int i = 0;
+ double start = GetTimeSinceStartup();
+ // do the last adjustment with a busy wait
+ do
+ {
+ time = GetTimeSinceStartup();
+ if (++i >= 1000)
+ {
+ // When using PerfHUD ES together with the NVIDIA time extension
+ // the time might be stopped, thus causing a diff of 0.
+ if ((time - start) == 0)
+ {
+ m_LastSyncEnd = GetTimeSinceStartup ();
+ return;
+ }
+ // Need to reset "start" here just in case we started to
+ // busy wait and then the time was stopped while busy waiting
+ start = time;
+ i = 0;
+ }
+ }
+ while(time - m_LastSyncEnd < frameTime);
+ m_LastSyncEnd += frameTime;
+ return;
+ }
+ }
+ m_LastSyncEnd = GetTimeSinceStartup ();
+}
+
+void TimeManager::Update ()
+{
+ AssertIf (m_UseFixedTimeStep);
+ m_FrameCount++;
+ m_RenderFrameCount++;
+ if (m_SetTimeManually)
+ return;
+
+ // Capture framerate is always constant
+ if (m_CaptureFramerate > 0)
+ {
+ #if DEBUG_TIME_MANAGER
+ printf_console( "time: setting time using capture framerate of %i, timescale %f\n", m_CaptureFramerate, m_TimeScale );
+ #endif
+ SetTime (m_DynamicTime.m_CurFrameTime + 1.0F / (float)m_CaptureFramerate * m_TimeScale);
+ return;
+ }
+
+ // Don't do anything to delta time the first frame!
+ if (m_FirstFrameAfterReset)
+ {
+ m_FirstFrameAfterReset = false;
+ return;
+ }
+
+ // When coming out of a pause / startup / level load we don't want to have a spike in delta time.
+ // So just default to kStartupDeltaTime.
+ if (m_FirstFrameAfterPause)
+ {
+ m_FirstFrameAfterPause = false;
+ #if DEBUG_TIME_MANAGER
+ printf_console( "time: setting time first frame after pause\n" );
+ #endif
+ SetTime (m_DynamicTime.m_CurFrameTime + kStartupDeltaTime * m_TimeScale);
+ // This is not a real delta time so don't include in smoothed time
+ m_ActiveTime.m_SmoothingWeight = 0.0f;
+ m_DynamicTime.m_SmoothingWeight = 0.0f;
+ return;
+ }
+
+ double time = GetTimeSinceStartup () - m_ZeroTime;
+ m_RealtimeStartOfFrame = time;
+
+ // clamp the delta time in case a frame takes too long.
+ if (time - m_DynamicTime.m_CurFrameTime > m_MaximumTimestep)
+ {
+ #if DEBUG_TIME_MANAGER
+ printf_console( "time: maximum dt (was %f)\n", time - m_DynamicTime.m_CurFrameTime );
+ #endif
+ SetTime (m_DynamicTime.m_CurFrameTime + m_MaximumTimestep * m_TimeScale);
+ return;
+ }
+
+ // clamp the delta time in case a frame goes to fast! (prevent delta time being zero)
+ if (time - m_DynamicTime.m_CurFrameTime < kMinimumDeltaTime)
+ {
+ #if DEBUG_TIME_MANAGER
+ printf_console( "time: minimum dt (was %f)\n", time - m_DynamicTime.m_CurFrameTime );
+ #endif
+ SetTime (m_DynamicTime.m_CurFrameTime + kMinimumDeltaTime * m_TimeScale);
+ return;
+ }
+
+ // Handle time scale
+ if (!CompareApproximately (m_TimeScale, 1.0F))
+ {
+ #if DEBUG_TIME_MANAGER
+ printf_console( "time: time scale path, delta %f\n", time - m_DynamicTime.m_CurFrameTime );
+ #endif
+ float deltaTime = time - m_DynamicTime.m_CurFrameTime;
+ SetTime (m_DynamicTime.m_CurFrameTime + deltaTime * m_TimeScale);
+ return;
+ }
+
+ #if DEBUG_TIME_MANAGER
+ printf_console( "time: set to %f\n", time );
+ #endif
+ m_DynamicTime.m_LastFrameTime = m_DynamicTime.m_CurFrameTime;
+ m_DynamicTime.m_CurFrameTime = time;
+ m_DynamicTime.m_DeltaTime = m_DynamicTime.m_CurFrameTime - m_DynamicTime.m_LastFrameTime;
+ m_DynamicTime.m_InvDeltaTime = CalcInvDeltaTime(m_DynamicTime.m_DeltaTime);
+ CalcSmoothDeltaTime (m_DynamicTime);
+
+ m_ActiveTime = m_DynamicTime;
+}
+
+void TimeManager::SetDeltaTimeHack (float dt)
+{
+ m_ActiveTime.m_DeltaTime = max(dt, kMinimumDeltaTime);
+ m_ActiveTime.m_InvDeltaTime = CalcInvDeltaTime(m_ActiveTime.m_DeltaTime);
+}
+
+void TimeManager::SetTime (double time)
+{
+ AssertIf (m_UseFixedTimeStep);
+// AssertIf (time - m_DynamicTime.m_CurFrameTime < kMinimumDeltaTime * 0.1F);
+
+ m_DynamicTime.m_LastFrameTime = m_DynamicTime.m_CurFrameTime;
+ m_DynamicTime.m_CurFrameTime = time;
+ m_DynamicTime.m_DeltaTime = m_DynamicTime.m_CurFrameTime - m_DynamicTime.m_LastFrameTime;
+
+ m_DynamicTime.m_InvDeltaTime = CalcInvDeltaTime(m_DynamicTime.m_DeltaTime);
+ CalcSmoothDeltaTime (m_DynamicTime);
+
+ m_ActiveTime = m_DynamicTime;
+
+ // Sync m_ZeroTime with timemanager time
+ m_ZeroTime = GetTimeSinceStartup () - m_DynamicTime.m_CurFrameTime;
+ #if DEBUG_TIME_MANAGER
+ printf_console( "time: set to %f, sync zero to %f\n", time, m_ZeroTime );
+ #endif
+}
+
+#if UNITY_EDITOR
+void TimeManager::NextFrameEditor () {
+ m_RenderFrameCount++;
+}
+#endif
+
+bool TimeManager::StepFixedTime ()
+{
+ if (m_FixedTime.m_CurFrameTime + m_FixedTime.m_DeltaTime > m_DynamicTime.m_CurFrameTime && !m_FirstFixedFrameAfterReset)
+ {
+ m_ActiveTime = m_DynamicTime;
+ m_UseFixedTimeStep = false;
+
+ return false;
+ }
+
+ m_FixedTime.m_LastFrameTime = m_FixedTime.m_CurFrameTime;
+ if (!m_FirstFixedFrameAfterReset)
+ m_FixedTime.m_CurFrameTime += m_FixedTime.m_DeltaTime;
+
+ m_ActiveTime = m_FixedTime;
+ m_UseFixedTimeStep = true;
+ m_FirstFixedFrameAfterReset = false;
+
+ return true;
+}
+
+void TimeManager::ResetTime ()
+{
+ AssertIf (m_UseFixedTimeStep);
+ m_DynamicTime.m_CurFrameTime = 0.0F;
+ m_DynamicTime.m_LastFrameTime = 0.0F;
+ if (IsWorldPlaying())
+ {
+ m_DynamicTime.m_DeltaTime = 0.02F;
+ m_DynamicTime.m_InvDeltaTime = 1.0F / m_DynamicTime.m_DeltaTime;
+ }
+ else
+ {
+ m_DynamicTime.m_DeltaTime = 0.0F;
+ m_DynamicTime.m_InvDeltaTime = 0.0F;
+ }
+ m_DynamicTime.m_SmoothDeltaTime = 0.0F;
+ m_DynamicTime.m_SmoothingWeight = 0.0F;
+
+ m_FixedTime.m_CurFrameTime = 0.0F;
+ m_FixedTime.m_LastFrameTime = 0.0F;
+ // Dont erase the fixed delta time
+ m_FixedTime.m_InvDeltaTime = 1.0F / m_FixedTime.m_DeltaTime;
+
+ m_ActiveTime = m_DynamicTime;
+
+ m_FirstFrameAfterReset = true;
+ m_FirstFrameAfterPause = true;
+ m_FirstFixedFrameAfterReset = true;
+
+ m_FrameCount = 0;
+ m_RenderFrameCount = 0;
+ m_ZeroTime = GetTimeSinceStartup ();
+ m_RealZeroTime = m_ZeroTime;
+ #if DEBUG_TIME_MANAGER
+ printf_console( "time: startup, zero time %f\n", m_RealZeroTime );
+ #endif
+ m_LevelLoadOffset = 0.0F;
+ m_CaptureFramerate = 0;
+ m_RealtimeStartOfFrame = 0.0;
+}
+
+template<class TransferFunction>
+void TimeManager::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.Transfer (m_FixedTime.m_DeltaTime, "Fixed Timestep", kSimpleEditorMask);
+ transfer.Transfer (m_MaximumTimestep, "Maximum Allowed Timestep", kSimpleEditorMask);
+ transfer.Transfer (m_TimeScale, "m_TimeScale", kSimpleEditorMask);
+}
+
+void TimeManager::SetFixedDeltaTime (float fixedStep)
+{
+ fixedStep = clamp<float>(fixedStep, 0.0001F, 10.0F);
+ m_FixedTime.m_DeltaTime = fixedStep;
+ m_FixedTime.m_InvDeltaTime = 1.0F / m_FixedTime.m_DeltaTime;
+ m_FixedTime.m_SmoothDeltaTime = m_FixedTime.m_DeltaTime;
+
+ SetMaximumDeltaTime(m_MaximumTimestep);
+}
+
+void TimeManager::SetMaximumDeltaTime (float maxStep)
+{
+ m_MaximumTimestep = max<float>(maxStep, m_FixedTime.m_DeltaTime);
+}
+
+void TimeManager::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad(awakeMode);
+
+ m_FixedTime.m_InvDeltaTime = 1.0F / m_FixedTime.m_DeltaTime;
+ m_FixedTime.m_SmoothDeltaTime = m_FixedTime.m_DeltaTime;
+}
+
+void TimeManager::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_FixedTime.m_DeltaTime = clamp<float>(m_FixedTime.m_DeltaTime, 0.0001F, 10.0F);
+ m_MaximumTimestep = max<float>(m_MaximumTimestep, m_FixedTime.m_DeltaTime);
+}
+
+void TimeManager::DidFinishLoadingLevel ()
+{
+ m_LevelLoadOffset = -m_DynamicTime.m_CurFrameTime;
+ // Trying to reconstruct what was intended here, this seems plausible:
+ m_FirstFrameAfterPause = m_FirstFrameAfterReset = true;
+}
+
+void TimeManager::SetTimeScale (float scale) {
+ bool outOfRange = scale <= 100.0f && scale >= 0.0f;
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1))
+ outOfRange = scale < 100;
+
+ if (outOfRange)
+ {
+ m_TimeScale = scale;
+ SetDirty ();
+ }
+ else
+ {
+ ErrorString ("Time.timeScale is out of range. Needs to be between 0 and 100.");
+ }
+}
+
+#if SUPPORT_REPRODUCE_LOG
+
+void TimeManager::ReadLog (std::ifstream& in)
+{
+ if (!CheckReproduceTag("Time", in))
+ {
+ FailReproduction("Error reading reproduce log");
+ return;
+ }
+
+ ReadFloat(in, m_DynamicTime.m_CurFrameTime);
+ ReadFloat(in, m_DynamicTime.m_LastFrameTime);
+ ReadFloat(in, m_DynamicTime.m_DeltaTime);
+ ReadFloat(in, m_DynamicTime.m_SmoothDeltaTime);
+ ReadFloat(in, m_DynamicTime.m_InvDeltaTime);
+ ReadFloat(in, m_RealtimeStartOfFrame);
+
+ m_ActiveTime = m_DynamicTime;
+}
+
+void TimeManager::WriteLog (std::ofstream& out)
+{
+ // In the web player WebScripting.cpp injects the first new line for the frame
+ #if !WEBPLUG
+ out << std::endl;
+ #endif
+
+ out << "Time" << std::endl;
+ WriteFloat(out, m_DynamicTime.m_CurFrameTime); out << " ";
+ WriteFloat(out, m_DynamicTime.m_LastFrameTime); out << " ";
+ WriteFloat(out, m_DynamicTime.m_DeltaTime); out << " ";
+ WriteFloat(out, m_DynamicTime.m_SmoothDeltaTime); out << " ";
+ WriteFloat(out, m_DynamicTime.m_InvDeltaTime); out << " ";
+ WriteFloat(out, m_RealtimeStartOfFrame); out << std::endl;
+
+ m_ActiveTime = m_DynamicTime;
+}
+
+double TimeManager::GetRealtime()
+{
+ #if SUPPORT_REPRODUCE_LOG
+
+ if (RunningReproduction() && (GetReproduceVersion() == 1 || GetReproduceVersion() == 2))
+ {
+ return m_RealtimeStartOfFrame;
+ }
+
+ if (GetReproduceMode() == kPlaybackReproduceLog)
+ {
+ std::ifstream& in = *GetReproduceInStream();
+
+ if (!CheckReproduceTag("RealTime", in))
+ {
+ ErrorString("Grabbing realtime but there are no realtime calls recorded");
+ return m_RealtimeStartOfFrame;
+ }
+
+ double realtime;
+ ReadBigFloat(in, realtime);
+
+ return realtime;
+ }
+ else if (RunningReproduction())
+ {
+ double realtime = GetTimeSinceStartup() - m_RealZeroTime;
+
+ std::ofstream& out = *GetReproduceOutStream();
+ out << "RealTime ";
+ WriteBigFloat(out, realtime);
+ out << std::endl;
+
+ return realtime;
+ }
+ #endif
+ double realtime = GetTimeSinceStartup() - m_RealZeroTime;
+ return realtime;
+}
+#else
+double TimeManager::GetRealtime()
+{
+ return GetTimeSinceStartup() - m_RealZeroTime;
+}
+#endif
+
+#if ENABLE_CLUSTER_SYNC
+template<class TransferFunc>
+void TimeManager::ClusterTransfer (TransferFunc& transfer)
+{
+ TRANSFER(m_DynamicTime.m_CurFrameTime);
+ TRANSFER(m_DynamicTime.m_LastFrameTime);
+ TRANSFER(m_DynamicTime.m_DeltaTime);
+ TRANSFER(m_DynamicTime.m_SmoothDeltaTime);
+ TRANSFER(m_DynamicTime.m_InvDeltaTime);
+ TRANSFER(m_RealtimeStartOfFrame);
+ TRANSFER(m_TimeScale);
+}
+#endif
+
+
+
+#if UNITY_ANDROID || UNITY_NACL
+#include <sys/time.h>
+
+inline double clock_gettime_to_double ()
+{
+ timespec time;
+ clock_gettime(CLOCK_MONOTONIC, &time);
+ return time.tv_sec + (double)time.tv_nsec * 0.000000001;
+}
+
+double TimeSinceStartupImpl ()
+{
+ static double sStartTime = 0;
+
+ if (sStartTime == 0)
+ sStartTime = clock_gettime_to_double ();
+
+ return clock_gettime_to_double () - sStartTime;
+}
+#endif
+
+
+IMPLEMENT_CLASS (TimeManager)
+IMPLEMENT_OBJECT_SERIALIZE (TimeManager)
+IMPLEMENT_CLUSTER_SERIALIZE(TimeManager)
+GET_MANAGER (TimeManager)
diff --git a/Runtime/Input/TimeManager.h b/Runtime/Input/TimeManager.h
new file mode 100644
index 0000000..17a5da9
--- /dev/null
+++ b/Runtime/Input/TimeManager.h
@@ -0,0 +1,149 @@
+#ifndef TIMEMANAGER_H
+#define TIMEMANAGER_H
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/ClusterRenderer/ClusterRendererDefines.h"
+
+class TimeManager;
+
+EXPORT_COREMODULE TimeManager& GetTimeManager ();
+/// The time since startup
+double GetTimeSinceStartup ();
+double TimeSinceStartupImpl ();
+
+
+
+/*
+Time requirements:
+- Delta time shall never be less than kMinimumDeltaTime (So people dont get nans when dividing by delta time)
+- The first frame when starting up is always at zero and kStartupDeltaTime delta time.
+- delta time is clamped to a maximum of kMaximumDeltaTime
+- adding up delta time always gives you the current time
+
+- after loading a level or pausing, the first frames delta time is kStartupDeltaTime
+- fixed delta time is always smaller or equal to dynamic time
+
+- When starting up there is always one physics frame before the first display!
+*/
+
+class TimeManager : public GlobalGameManager
+{
+ public:
+
+ struct TimeHolder
+ {
+ TimeHolder();
+ double m_CurFrameTime;
+ double m_LastFrameTime;
+ float m_DeltaTime;
+ float m_SmoothDeltaTime;
+ float m_SmoothingWeight;
+ float m_InvDeltaTime;
+ };
+
+ TimeManager (MemLabelId label, ObjectCreationMode mode);
+ // ~TimeManager (); declared-by-macro
+
+ REGISTER_DERIVED_CLASS (TimeManager, GlobalGameManager)
+ DECLARE_OBJECT_SERIALIZE (TimeManager)
+ // for cluster renderer
+ DECLARE_CLUSTER_SERIALIZE(TimeManager)
+
+ void AwakeFromLoad (AwakeFromLoadMode mode);
+ virtual void CheckConsistency ();
+
+ virtual void Update ();
+
+ #if UNITY_EDITOR
+ /// Called from the editor to bump the frameCount. Later, this would also update GraphicsTime,
+ /// So we can get animations from the editor.
+ void NextFrameEditor ();
+ #endif
+
+ void ResetTime ();
+
+ void SetPause(bool pause);
+
+
+// void SetMinimumDeltaTime (float c) { m_MinimumDeltaTime = c; }
+
+ inline double GetCurTime () const { return m_ActiveTime.m_CurFrameTime; }
+ inline double GetTimeSinceLevelLoad () const { return m_ActiveTime.m_CurFrameTime + m_LevelLoadOffset; }
+ inline float GetDeltaTime () const { return m_ActiveTime.m_DeltaTime; }
+ inline float GetSmoothDeltaTime () const { return m_ActiveTime.m_SmoothDeltaTime; }
+
+ inline float GetInvDeltaTime () const { return m_ActiveTime.m_InvDeltaTime; }
+ inline int GetFrameCount () const { return m_FrameCount; }
+
+ inline int GetRenderFrameCount () const { return m_RenderFrameCount; }
+
+ inline float GetFixedDeltaTime () {return m_FixedTime.m_DeltaTime; }
+ void SetFixedDeltaTime (float fixedStep);
+ inline double GetFixedTime () {return m_FixedTime.m_CurFrameTime; }
+
+ inline float GetMaximumDeltaTime () {return m_MaximumTimestep; }
+ void SetMaximumDeltaTime (float maxStep);
+
+
+ /// Steps the fixed time step until the dynamic time is exceeded.
+ /// Returns true if it has to be called again to reach the dynamic time
+ bool StepFixedTime ();
+
+ void SetTimeManually (bool manually) { m_SetTimeManually = manually; }
+ void SetTime (double time);
+ void SetDeltaTimeHack (float dt);
+ void SetTimeScale (float scale);
+ float GetTimeScale () { return m_TimeScale; }
+
+ inline bool IsUsingFixedTimeStep () const { return m_UseFixedTimeStep; }
+
+ void DidFinishLoadingLevel ();
+
+ void SetCaptureFramerate (int rate) { m_CaptureFramerate = rate; }
+ int GetCaptureFramerate () { return m_CaptureFramerate; }
+
+ double GetRealtime();
+
+ void Sync(float framerate);
+
+ #if SUPPORT_REPRODUCE_LOG
+ void WriteLog (std::ofstream& out);
+ void ReadLog (std::ifstream& in);
+ #endif
+
+ private:
+
+ TimeHolder m_FixedTime;
+ TimeHolder m_DynamicTime;
+ TimeHolder m_ActiveTime;
+
+ bool m_FirstFrameAfterReset;
+ bool m_FirstFrameAfterPause;
+ bool m_FirstFixedFrameAfterReset;
+
+ int m_FrameCount;
+ int m_RenderFrameCount;
+ int m_CullFrameCount;
+ int m_CaptureFramerate;
+ double m_ZeroTime;
+ double m_RealZeroTime;
+ double m_LevelLoadOffset;
+ double m_RealtimeStartOfFrame;
+
+ bool m_SetTimeManually;
+ bool m_UseFixedTimeStep;
+ float m_TimeScale;///< How fast compared to the real time does the game time progress (1.0 is realtime, .5 slow motion) range { -infinity, 100 }
+ float m_MaximumTimestep;
+
+ double m_LastSyncEnd;
+};
+
+inline double GetCurTime () { return GetTimeManager ().GetCurTime (); }
+inline float GetDeltaTime () { return GetTimeManager ().GetDeltaTime (); }
+inline float GetInvDeltaTime () { return GetTimeManager ().GetInvDeltaTime (); }
+float CalcInvDeltaTime (float dt);
+
+const float kMinimumDeltaTime = 0.00001F;
+
+#endif
diff --git a/Runtime/Input/TouchPhaseEmulation.cpp b/Runtime/Input/TouchPhaseEmulation.cpp
new file mode 100644
index 0000000..be1ab28
--- /dev/null
+++ b/Runtime/Input/TouchPhaseEmulation.cpp
@@ -0,0 +1,719 @@
+#include "UnityPrefix.h"
+
+/*
+ * TouchPhaseEmulation is layer between an 'event-based' touch OS (Android, Metro etc) and our phase-based script API (similar to iOS).
+ *
+ * The core logic of this layer is in DispatchTouchEvent, which handles mapping and collapsing of events to a frame-discrete phase.
+ */
+
+#define DEBUG_TOUCH_EMU (DEBUGMODE && 0)
+
+#include "TouchPhaseEmulation.h"
+
+#if UNITY_BLACKBERRY
+ static const int touchTimeout = 400;
+#else
+ static const int touchTimeout = 150;
+#endif
+
+// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
+
+class TouchImpl : public Touch
+{
+ enum { kEmptyTouchId = ~0UL };
+
+public:
+ TouchImpl ()
+ {
+ clear ();
+ }
+
+ long long timestamp; // in milliseconds
+ UInt32 pointerId; // matches OS pointerId
+ size_t frameToReport; // frame # for this event to be reported. Should
+ // only be =gFrameCount or =gFrameCount+1
+ size_t frameBegan; // frame # when BEGIN event was received
+ UInt32 endPhaseInQueue; // acts both as a bool to indicate that this event
+ // has already received an END/CANCEL from OS and
+ // will be reported to scripts next frame, and as
+ // a container to hold the actual value: was it
+ // END or CANCEL?
+
+ void setDeltaTime (long long newTimestamp)
+ {
+ if (timestamp == 0)
+ return;
+
+ deltaTime = (newTimestamp - timestamp) / 1000.0f;
+ }
+
+ void setDeltaPos (Vector2f const& newPos)
+ {
+ if (CompareApproximately (pos, Vector2f::zero))
+ return;
+
+ deltaPos = newPos - pos;
+ }
+
+ bool isMultitap (long long newTimestamp, Vector2f const& newPos, float screenDPI)
+ {
+ static const float tapZoneRadiusCM = 0.4f; // 4mm
+ static const float cmToInch = 0.393701f;
+ static const float multitapRadiusPixels = screenDPI * tapZoneRadiusCM * cmToInch;
+ static const float multitapRadiusSqr = multitapRadiusPixels * multitapRadiusPixels;
+
+ return newTimestamp - timestamp < touchTimeout
+ && SqrMagnitude (pos - newPos) < multitapRadiusSqr;
+ }
+
+ void setTapCount (long long newTimestamp, Vector2f const &newPos, float screenDPI)
+ {
+ if (isMultitap (newTimestamp, newPos, screenDPI))
+ ++tapCount;
+ else
+ tapCount = 1;
+ }
+
+ void init(size_t _pointerId, Vector2f _pos, TouchPhaseEmulation::TouchPhase _phase,
+ long long _timestamp, size_t currFrame)
+ {
+ pointerId = _pointerId;
+ pos = _pos;
+ rawPos = _pos;
+ phase = _phase;
+ frameBegan = currFrame;
+ timestamp = _timestamp;
+ frameToReport = currFrame;
+ }
+
+ void clear ()
+ {
+ id = kEmptyTouchId;
+ phase = TouchPhaseEmulation::kTouchCanceled;
+ endPhaseInQueue = 0;
+ deltaPos = Vector2f (0.0f, 0.0f);
+ deltaTime = 0.0f;
+ frameToReport = 0;
+ frameBegan = 0;
+ tapCount = 0;
+ rawPos = pos = Vector2f (0.0f, 0.0f);
+ timestamp = 0;
+ pointerId = kEmptyTouchId;
+ }
+
+ bool isOld (size_t frame) const
+ {
+ return frameToReport < frame;
+ }
+
+ bool isEmpty () const
+ {
+ return id == kEmptyTouchId;
+ }
+
+ bool isFinished () const
+ {
+ return !isEmpty () && IsEnd (phase);
+ }
+
+ bool willBeFinishedNextFrame () const
+ {
+ return !isEmpty () && IsEnd (endPhaseInQueue);
+ }
+
+ bool isNow (size_t frame) const
+ {
+ return frameToReport == frame;
+ }
+
+ static bool IsBegin (size_t phase)
+ {
+ return phase == TouchPhaseEmulation::kTouchBegan;
+ }
+
+ static bool IsTransitional (size_t phase)
+ {
+ return phase == TouchPhaseEmulation::kTouchMoved || phase == TouchPhaseEmulation::kTouchStationary;
+ }
+
+ static bool IsEnd (size_t phase)
+ {
+ return phase == TouchPhaseEmulation::kTouchEnded || phase == TouchPhaseEmulation::kTouchCanceled;
+ }
+
+
+#if DEBUG_TOUCH_EMU
+ void dump (TouchImpl* gAllTouches)
+ {
+ size_t index = this - gAllTouches;
+ char const* phaseNames[] = {
+ "<Began>",
+ "<Moved>",
+ "<Stationary>",
+ "<Ended>",
+ "<Canceled>",
+ };
+
+ char const *fmt =
+ "T[%02d]={fid=%d, pid=%d, phase=%s, p=(%3.1f,%3.1f), dp=(%3.1f,%3.1f),\n"
+ " tm=%lld, dtm=%f, endPhaseQ=%d, fbeg=%d, frep=%d, tcnt=%d}\n";
+
+ printf_console (fmt, index, id, pointerId, phaseNames[phase], pos.x, pos.y,
+ deltaPos.x, deltaPos.y, timestamp, deltaTime,
+ endPhaseInQueue, frameBegan, frameToReport, tapCount);
+ }
+#endif
+};
+
+// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
+
+TouchPhaseEmulation::TouchPhaseEmulation(float screenDPI, bool singleTouchDevice)
+: m_AllocatedFingerIDs(0)
+, m_FrameCount(0)
+, m_ScreenDPI(screenDPI)
+, m_IsMultiTouchEnabled(!singleTouchDevice)
+, m_IsSingleTouchDevice(singleTouchDevice)
+{
+ m_TouchSlots = new TouchImpl[kMaxTouchCount];
+ InitTouches();
+}
+
+TouchPhaseEmulation::~TouchPhaseEmulation()
+{
+ delete [] m_TouchSlots;
+}
+
+void TouchPhaseEmulation::InitTouches ()
+{
+ for (size_t i = 0; i < kMaxTouchCount; ++i)
+ {
+ m_TouchSlots[i].clear();
+ }
+ m_AllocatedFingerIDs = 0;
+
+ m_FrameCount = 1;
+}
+
+void TouchPhaseEmulation::PreprocessTouches ()
+{
+ DiscardRedundantTouches();
+}
+
+void TouchPhaseEmulation::PostprocessTouches ()
+{
+#if DEBUG_TOUCH_EMU
+ DumpAll ();
+#endif
+ ++m_FrameCount;
+ UpdateActiveTouches();
+}
+
+
+bool TouchPhaseEmulation::IsExistingTouch( int pointerId )
+{
+ TouchImpl* matchingSlots[kMaxTouchCount];
+ const size_t slotsFound = FindByPointerId(matchingSlots, pointerId);
+
+ for (size_t i = 0; i < slotsFound; ++i)
+ if (matchingSlots[i])
+ return true;
+
+ return false;
+}
+
+void TouchPhaseEmulation::AddTouchEvent (int pointerId, float x, float y, TouchPhase newPhase, long long timestamp)
+{
+ Vector2f pos = Vector2f (x, y);
+
+#if UNITY_WINRT
+ // [Metro] With one finger touching; that touch pointerId is usually > 0
+ if (!m_IsMultiTouchEnabled && GetTouchCount() > 0 && !IsExistingTouch(pointerId))
+ return;
+#else
+ if (!m_IsMultiTouchEnabled && pointerId > 0)
+ return;
+#endif
+
+ DispatchTouchEvent (pointerId, pos, static_cast<TouchPhase> (newPhase), timestamp, m_FrameCount);
+}
+
+void TouchPhaseEmulation::DispatchTouchEvent (size_t pointerId, Vector2f pos, TouchPhase newPhase, long long timestamp, size_t currFrame)
+{
+ // Brief terminology legend:
+ // action The OS level indication of what happened in a particular touch event; DOWN, MOVE, UP etc.
+ // phase Logical state of a touch, emulated to the script side; Began/Moved/Ended.
+ // Phase is considered constant per frame, and all intra-frame actions are collapsed to a single phase.
+ // If two actions can't be collapsed (like UP/DOWN), then the DOWN action will be delayed one frame.
+ // pointerId The ID given to a touch event by the OS. These IDs can be reused if touches end/start within the same frame.
+ // fingerId The ID we give a touch when presented to the script side. Also known as 'id' in the Touch struct.
+ // touch event OS level touch information
+ // touch slot Script level touch information.
+
+ // Step 1: Determine if already tracking this 'pointerId'
+ // We do this by looping through all touch slots while looking for an active touch with matching 'pointerId'.
+ // If a slot with phase == Ended has been inactive for 150ms => set it to Inactive
+ // Step 2: Determine if an old touch slot should be updated with the new event information, or if a new touch slot should be allocated.
+ // If no touch slot was found in Step 1 => allocate a new touch slot. Skip to step 4.
+ // If only a singular touch slot was found in Step 1 => use that slot. Skip to step 4.
+ // If multiple slots were found in Step 1 => determine which slot to use, or if another slot needs to be allocated.
+ // Step 3: Determine which active slot to use, or allocate a new.
+ // If phase == Began : for each slot with phase == Ended, consider event to be a multi-tap by comparing position and timestamp.
+ // If phase != Began : find slot with phase != Ended (should only be one!)
+ // If no slot matches : allocate a new slot with fingerId = highestFingerIdUsed + 1.
+ // Step 4: Initialize/update the touch slot based on current and old touch phase.
+ // If new phase == Began and old phase == Ended => increase tap count.
+ // If new phase == Began => try to compact finger id
+ // If new phase == Ended and old phase == Began on the same frame => delay new phase (Ended) until next frame.
+ // If new phase == Moved and old phase == Stationary => check distance against threshold for Moved/Stationary.
+ //
+ // After every frame, loop through all active touch slots:
+ // If a delayed phase (Ended) was enabled => update the phase.
+ // If a touch != Ended wasn't updated this frame => set phase = Stationary
+ // If a touch began and ended the this frame, and it resulted in another, currently active, touch having an increased tap count
+ // => clear those extra touches, and compact the finger id
+
+ // NB: for now we do use passed position as both raw position and position
+ // on ios, where we actually implement raw position, different code is used
+
+ FreeExpiredTouches(m_FrameCount, timestamp);
+
+ TouchImpl* matchingSlots[kMaxTouchCount];
+ const size_t slotsFound = FindByPointerId(matchingSlots, pointerId);
+
+ TouchImpl* touch = NULL;
+ int inheritedTapCount = 0;
+
+#if UNITY_WINRT
+ // [Metro] Calculate tapCount manually as pointerIds aren't reused.
+ if (TouchImpl::IsBegin(newPhase))
+ inheritedTapCount += CalculateTapCount(timestamp, pos);
+#endif
+
+ for (size_t i = 0; i < slotsFound; ++i)
+ {
+ TouchImpl* slot = matchingSlots[i];
+
+ bool touchFinished = slot->isFinished() || slot->willBeFinishedNextFrame();
+ if (TouchImpl::IsBegin(newPhase))
+ {
+ if (touchFinished)
+ {
+ if (slot->isOld(m_FrameCount))
+ {
+ touch = slot;
+ }
+
+ if (slot->isMultitap(timestamp, pos, m_ScreenDPI))
+ {
+ inheritedTapCount = slot->tapCount;
+ }
+ }
+ }
+ else
+ {
+ if (!touchFinished)
+ {
+ if (touch)
+ {
+ if (DEBUGMODE) printf_console("Stale/stuck touch released.");
+ ExpireOld(*touch);
+ }
+ touch = slot;
+ }
+ }
+ }
+
+ if (!touch)
+ {
+ if (!TouchImpl::IsBegin(newPhase))
+ {
+ if (DEBUGMODE) printf_console("Dropping touch event part of canceled gesture.");
+ return;
+ }
+ if (!(touch = AllocateNew()))
+ return;
+ }
+
+ if (TouchImpl::IsBegin (newPhase))
+ {
+ touch->tapCount = inheritedTapCount;
+ #if DEBUG_TOUCH_EMU
+ printf_console("Slot before initialized:");
+ touch->dump(m_TouchSlots);
+ #endif
+
+ touch->init( pointerId, pos, newPhase, timestamp, currFrame );
+ touch->setTapCount( timestamp, pos, m_ScreenDPI );
+ touch->id = CompactFingerID(touch->id);
+
+ #if DEBUG_TOUCH_EMU
+ printf_console("Slot initialized:");
+ touch->dump(m_TouchSlots);
+ #endif
+ return;
+ }
+ else if (TouchImpl::IsEnd (newPhase))
+ {
+ // if touch began this frame, we will delay end phase for one frame
+ if (touch->frameBegan == currFrame)
+ touch->endPhaseInQueue = newPhase;
+ else
+ touch->phase = newPhase;
+
+ // Android only sends CANCELED on the first pointer in a gesture - kill all other active touches when this happens.
+ if (newPhase == kTouchCanceled)
+ {
+ for (size_t i = 0; i < kMaxTouchCount; ++i)
+ {
+ TouchImpl* slot = &m_TouchSlots[i];
+ if (slot->isEmpty() || slot->isFinished() || slot->willBeFinishedNextFrame())
+ continue;
+ slot->endPhaseInQueue = newPhase;
+ #if DEBUG_TOUCH_EMU
+ m_TouchSlots[i].dump(m_TouchSlots);
+ #endif
+ }
+ }
+ }
+ else if (newPhase == kTouchMoved
+ && touch->phase == kTouchStationary)
+ {
+ // old event is STATIONARY, the new one is MOVE. Android does not
+ // report STATIONARY Events, so if MOVE's deltaPos is not big enough,
+ // let's keep it STATIONARY. Promote to MOVE otherwise.
+ static const float deltaPosTolerance = 0.5f;
+ if (Magnitude (touch->pos - pos) >= deltaPosTolerance)
+ touch->phase = newPhase;
+ }
+
+ touch->setDeltaPos (pos);
+ touch->pos = pos;
+
+ touch->setDeltaTime (timestamp);
+ touch->timestamp = timestamp;
+ touch->frameToReport = currFrame;
+
+#if DEBUG_TOUCH_EMU
+ printf_console("Slot updated:");
+ touch->dump(m_TouchSlots);
+#endif
+
+}
+
+size_t TouchPhaseEmulation::FindByPointerId(TouchImpl* matchingSlots[kMaxTouchCount], size_t pointerId)
+{
+#if DEBUG_TOUCH_EMU
+ printf_console("%s", __FUNCTION__);
+#endif
+ size_t slotsFound = 0;
+ for (size_t i = 0; i < kMaxTouchCount; ++i)
+ {
+ if (m_TouchSlots[i].pointerId != pointerId)
+ continue;
+#if DEBUG_TOUCH_EMU
+ m_TouchSlots[i].dump(m_TouchSlots);
+#endif
+ matchingSlots[slotsFound++] = &m_TouchSlots[i];
+ }
+ return slotsFound;
+}
+
+TouchImpl* TouchPhaseEmulation::AllocateNew()
+{
+#if DEBUG_TOUCH_EMU
+ printf_console("%s", __FUNCTION__);
+#endif
+ // allocate virtual fingerId
+ int fingerId = 0;
+ const int maxFingerId = sizeof(m_AllocatedFingerIDs) * 8;
+ for (; fingerId < maxFingerId; ++fingerId)
+ {
+ UInt32 bitField = (1 << fingerId);
+ if (m_AllocatedFingerIDs & bitField)
+ continue;
+ m_AllocatedFingerIDs |= bitField;
+ break;
+ }
+ if (fingerId >= maxFingerId)
+ {
+ Assert (!"Out of virtual finger IDs!");
+ return NULL;
+ }
+
+ // find empty slot for touch
+ for (size_t i = 0; i < kMaxTouchCount; ++i)
+ {
+ TouchImpl& t = m_TouchSlots[i];
+
+ if (!t.isEmpty())
+ continue;
+
+ t.id = fingerId;
+ t.deltaPos = Vector2f(0, 0);
+ t.deltaTime = 0.0f;
+ t.endPhaseInQueue = 0;
+
+ return &t;
+ }
+
+ Assert (!"Out of free touches!");
+ return NULL;
+}
+
+void TouchPhaseEmulation::ExpireOld(TouchImpl& touch)
+{
+#if DEBUG_TOUCH_EMU
+ printf_console("%s", __FUNCTION__);
+#endif
+
+ if (touch.isEmpty())
+ {
+ ErrorString("Trying to expire empty touch slot!");
+ return;
+ }
+
+ // deallocate virtual fingerId
+ UInt32 bitField = (1 << touch.id);
+ Assert((m_AllocatedFingerIDs & bitField) && "Touch with stale finger ID killed!");
+ m_AllocatedFingerIDs &= ~bitField;
+
+ Assert(!touch.endPhaseInQueue && "Delayed touch killed prematurely!");
+ touch.clear();
+}
+
+int TouchPhaseEmulation::CompactFingerID(int id)
+{
+ int fingerId = 0;
+ const int maxFingerId = sizeof(m_AllocatedFingerIDs) * 8;
+ for (; fingerId < maxFingerId; ++fingerId)
+ {
+ UInt32 bitField = (1 << fingerId);
+ if (m_AllocatedFingerIDs & bitField)
+ continue;
+
+ if (id < fingerId)
+ return id;
+
+ m_AllocatedFingerIDs |= bitField;
+ bitField = (1 << id);
+ Assert((m_AllocatedFingerIDs & bitField) && "Touch with stale finger ID killed!");
+ m_AllocatedFingerIDs &= ~bitField;
+ id = fingerId;
+ break;
+ }
+ return id;
+}
+
+void TouchPhaseEmulation::FreeExpiredTouches (size_t eventFrame, long long timestamp)
+{
+ for (size_t i = 0; i < kMaxTouchCount; ++i)
+ {
+ TouchImpl& touch = m_TouchSlots[i];
+
+ if (touch.isEmpty())
+ continue;
+
+ long long age = timestamp - touch.timestamp;
+ if (touch.isOld(eventFrame) && touch.isFinished() && age > touchTimeout)
+ {
+ ExpireOld(touch);
+ }
+ }
+}
+
+void TouchPhaseEmulation::DiscardRedundantTouches()
+{
+ for (size_t i = 0; i < kMaxTouchCount; ++i)
+ {
+ TouchImpl* t0 = &m_TouchSlots[i];
+ TouchImpl* t1 = 0;
+
+ if (t0->isEmpty())
+ continue;
+
+ // find Down/Up touches recorded within a single frame
+ bool downUpTouch =
+ t0->frameBegan == m_FrameCount &&
+ t0->frameToReport == m_FrameCount &&
+ t0->willBeFinishedNextFrame() &&
+ !t0->isFinished();
+
+ if (!downUpTouch)
+ continue;
+
+ #if DEBUG_TOUCH_EMU
+ printf_console("Found new touch set to expire next frame");
+ t0->dump(m_TouchSlots);
+ #endif
+
+ bool redundant = false;
+
+ // compare the multitap info
+ for (size_t j = 0; j < kMaxTouchCount; ++j)
+ {
+ t1 = &m_TouchSlots[j];
+
+ if (t1->isEmpty() || i == j)
+ continue;
+
+ bool multitapTouch =
+ t1->frameBegan == m_FrameCount &&
+ t1->frameToReport == m_FrameCount &&
+ t1->pointerId == t0->pointerId &&
+ t1->tapCount > t0->tapCount &&
+ t0->isMultitap(t1->timestamp, t1->pos, m_ScreenDPI) &&
+ !t1->isFinished();
+
+ if (!multitapTouch)
+ continue;
+
+ #if DEBUG_TOUCH_EMU
+ printf_console("Found new touch, with tapCount that matches the one found earlier");
+ t1->dump(m_TouchSlots);
+ #endif
+ // found a match
+ redundant = true;
+ break;
+ }
+
+ if (redundant)
+ {
+ t0->endPhaseInQueue = 0; // this touch is officially gone anyway
+ ExpireOld(*t0);
+ t1->id = CompactFingerID(t1->id);
+ #if DEBUG_TOUCH_EMU
+ printf_console("New touch, with compacted finger id");
+ t1->dump(m_TouchSlots);
+ #endif
+ }
+ else
+ {
+ t0->id = CompactFingerID(t0->id);
+ #if DEBUG_TOUCH_EMU
+ printf_console("Refresh touch, with compacted finger id");
+ t0->dump(m_TouchSlots);
+ #endif
+ }
+ }
+}
+
+void TouchPhaseEmulation::UpdateActiveTouches()
+{
+ for (size_t i = 0; i < kMaxTouchCount; ++i)
+ {
+ TouchImpl& touch = m_TouchSlots[i];
+
+ if (touch.isEmpty())
+ continue;
+
+ // Skip Expired Touches
+ if (touch.isFinished())
+ {
+ continue;
+ }
+
+ // End Delayed Touches
+ if (touch.willBeFinishedNextFrame ())
+ {
+ touch.deltaPos = Vector2f (0, 0);
+ touch.phase = touch.endPhaseInQueue;
+ touch.endPhaseInQueue = 0;
+ touch.frameToReport = m_FrameCount;
+ continue;
+ }
+
+ // Default Stationary Touches
+ touch.phase = kTouchStationary;
+ touch.deltaPos = Vector2f (0, 0);
+ touch.frameToReport = m_FrameCount;
+ }
+
+}
+
+size_t TouchPhaseEmulation::GetTouchCount ()
+{
+ size_t count = 0;
+
+ // TODO: on first call to GetTouchCount() per frame, call PackTouchIds() to
+ // compact virtual touch IDs to stand out less
+
+ for (size_t i = 0; i < kMaxTouchCount; ++i)
+ if ( m_TouchSlots[i].isNow(m_FrameCount) &&
+ !m_TouchSlots[i].isEmpty() )
+ ++count;
+
+ return count;
+}
+
+size_t TouchPhaseEmulation::GetActiveTouchCount ()
+{
+ size_t count = 0;
+
+ for (size_t i = 0; i < kMaxTouchCount; ++i)
+ if (!m_TouchSlots[i].isEmpty() && !m_TouchSlots[i].isFinished())
+ ++count;
+
+ return count;
+}
+
+// @param index Zero-based index of events that are to be reported this
+// frame. Since not all of the events in the container need to
+// be reported this frame, it's used to skip already reported
+// ones.
+bool TouchPhaseEmulation::GetTouch (size_t index, Touch& touch)
+{
+ for (size_t i = 0; i < kMaxTouchCount; ++i)
+ {
+ if ( m_TouchSlots[i].isNow(m_FrameCount) &&
+ !m_TouchSlots[i].isEmpty() &&
+ index-- == 0 )
+ {
+ touch = m_TouchSlots[i];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool TouchPhaseEmulation::IsMultiTouchEnabled ()
+{
+ if (m_IsSingleTouchDevice)
+ return false;
+
+ return m_IsMultiTouchEnabled;
+}
+
+void TouchPhaseEmulation::SetMultiTouchEnabled (bool enabled)
+{
+ if (m_IsSingleTouchDevice)
+ return;
+
+ m_IsMultiTouchEnabled = enabled;
+}
+
+int TouchPhaseEmulation::CalculateTapCount( long long timestamp, Vector2f const &pos ) const
+{
+ int result = 0;
+ for (size_t i = 0; i < kMaxTouchCount; ++i)
+ {
+ TouchImpl& touch = m_TouchSlots[i];
+
+ if (touch.isEmpty())
+ continue;
+
+ if (touch.isMultitap(timestamp, pos, m_ScreenDPI))
+ result += touch.tapCount;
+ }
+
+ return result;
+}
+
+#if DEBUG_TOUCH_EMU
+void TouchPhaseEmulation::DumpAll (bool verbose)
+{
+ for (int i = 0; i < kMaxTouchCount; ++i)
+ if (!m_TouchSlots[i].isEmpty() && !m_TouchSlots[i].isOld(m_FrameCount) || verbose)
+ m_TouchSlots[i].dump( m_TouchSlots );
+}
+#endif
diff --git a/Runtime/Input/TouchPhaseEmulation.h b/Runtime/Input/TouchPhaseEmulation.h
new file mode 100644
index 0000000..e18c1ca
--- /dev/null
+++ b/Runtime/Input/TouchPhaseEmulation.h
@@ -0,0 +1,68 @@
+#ifndef __UNITY_INPUT_TOUCHPHASEEMULATION_H
+#define __UNITY_INPUT_TOUCHPHASEEMULATION_H
+
+#include "Runtime/Math/Vector2.h"
+#include "GetInput.h"
+
+class TouchImpl;
+
+class TouchPhaseEmulation
+{
+public:
+ TouchPhaseEmulation(float screenDPI, bool singleTouchDevice);
+ virtual ~TouchPhaseEmulation();
+
+public:
+
+ void InitTouches();
+ void PreprocessTouches();
+ void PostprocessTouches();
+
+ enum TouchPhase
+ {
+ kTouchBegan = 0,
+ kTouchMoved = 1,
+ kTouchStationary = 2,
+ kTouchEnded = 3,
+ kTouchCanceled = 4
+ };
+
+ void AddTouchEvent (int pointerId, float x, float y, TouchPhase newPhase, long long timestamp);
+ size_t GetTouchCount();
+ size_t GetActiveTouchCount();
+ bool GetTouch(size_t index, Touch& touch);
+
+ bool IsMultiTouchEnabled ();
+ void SetMultiTouchEnabled (bool enabled);
+
+private:
+
+ void DispatchTouchEvent (size_t pointerId, Vector2f pos, TouchPhase newPhase, long long timestamp, size_t currFrame);
+ bool IsExistingTouch( int pointerId );
+
+ enum { kMaxTouchCount = 32 };
+
+ size_t FindByPointerId(TouchImpl* matchingSlots[kMaxTouchCount], size_t pointerId);
+ TouchImpl* AllocateNew();
+ void ExpireOld(TouchImpl& touch);
+ int CompactFingerID(int id);
+ void FreeExpiredTouches (size_t eventFrame, long long timestamp);
+
+ void DiscardRedundantTouches();
+ void UpdateActiveTouches();
+ int CalculateTapCount( long long timestamp, Vector2f const &pos ) const;
+
+#if DEBUG_TOUCH_EMU
+ void DumpAll (bool verbose=false);
+#endif
+
+ TouchImpl* m_TouchSlots; //[kMaxTouchCount];
+ UInt32 m_AllocatedFingerIDs; // holds one bit per finger
+ size_t m_FrameCount;
+ const float m_ScreenDPI;
+ bool m_IsMultiTouchEnabled;
+ const bool m_IsSingleTouchDevice;
+
+};
+
+#endif // __UNITY_INPUT_TOUCHPHASEEMULATION_H
diff --git a/Runtime/Interfaces/IAnimation.cpp b/Runtime/Interfaces/IAnimation.cpp
new file mode 100644
index 0000000..4e63c73
--- /dev/null
+++ b/Runtime/Interfaces/IAnimation.cpp
@@ -0,0 +1,13 @@
+#include "UnityPrefix.h"
+#include "IAnimation.h"
+
+static IAnimation* gAnimation = NULL;
+IAnimation* GetAnimationInterface()
+{
+ return gAnimation;
+}
+
+void SetAnimationInterface(IAnimation* theInterface)
+{
+ gAnimation = theInterface;
+}
diff --git a/Runtime/Interfaces/IAnimation.h b/Runtime/Interfaces/IAnimation.h
new file mode 100644
index 0000000..151526e
--- /dev/null
+++ b/Runtime/Interfaces/IAnimation.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Utilities/NonCopyable.h"
+#include "Runtime/Modules/ExportModules.h"
+#include "Runtime/Math/Matrix4x4.h"
+
+typedef UInt32 BindingHash;
+
+#include "Runtime/Math/Matrix4x4.h"
+
+namespace Unity {
+ class Component;
+}
+
+struct CalculateSkinMatricesTask
+{
+ // input
+ const void* skeletonPose;
+ const UInt16* skeletonIndices;
+ Matrix4x4f rootPose;
+ int bindPoseCount;
+ const Matrix4x4f* bindPose;
+ // output
+ Matrix4x4f* outPose;
+};
+
+typedef void* (*CalculateAnimatorSkinMatricesFunc)(void* userData);
+
+class EXPORT_COREMODULE IAnimation : public NonCopyable
+{
+public:
+ virtual const void* GetGlobalSpaceSkeletonPose(const Unity::Component& animator) = 0;
+
+ virtual bool CalculateWorldSpaceMatricesMainThread(Unity::Component& animator, const UInt16* indices, size_t count, Matrix4x4f* outMatrices) = 0;
+
+ virtual CalculateAnimatorSkinMatricesFunc GetCalculateAnimatorSkinMatricesFunc() = 0;
+
+ virtual bool PathHashesToIndices(Unity::Component& animator, const BindingHash* bonePathHashes, size_t count, UInt16* outIndices) = 0;
+};
+
+EXPORT_COREMODULE IAnimation* GetAnimationInterface();
+EXPORT_COREMODULE void SetAnimationInterface(IAnimation* theInterface);
diff --git a/Runtime/Interfaces/IAnimationBinding.h b/Runtime/Interfaces/IAnimationBinding.h
new file mode 100644
index 0000000..c056a27
--- /dev/null
+++ b/Runtime/Interfaces/IAnimationBinding.h
@@ -0,0 +1,69 @@
+#pragma once
+
+class IAnimationBinding
+{
+public:
+#if UNITY_EDITOR
+ virtual void GetAllAnimatableProperties (Object& component, std::vector<EditorCurveBinding>& outProperties) const = 0;
+#endif
+
+ virtual float GetFloatValue (const UnityEngine::Animation::BoundCurve& bound) const = 0;
+ virtual void SetFloatValue (const UnityEngine::Animation::BoundCurve& bound, float value) const = 0;
+
+ virtual void SetPPtrValue (const UnityEngine::Animation::BoundCurve& bound, SInt32 value) const= 0;
+ virtual SInt32 GetPPtrValue (const UnityEngine::Animation::BoundCurve& bound) const = 0;
+
+ virtual bool GenerateBinding (const UnityStr& attribute, bool pptrCurve, UnityEngine::Animation::GenericBinding& outputBinding) const = 0;
+ virtual ClassIDType BindValue (Object& target, const UnityEngine::Animation::GenericBinding& binding, UnityEngine::Animation::BoundCurve& bound) const = 0;
+
+ virtual std::string SerializedPropertyPathToCurveAttribute (Object& target, const char* propertyPath) const { return std::string(); }
+ virtual std::string CurveAttributeToSerializedPath (const UnityEngine::Animation::BoundCurve& bound) const { return std::string(); }
+};
+
+inline const char* ParsePrefixedName (const char* attribute, const char* prefix)
+{
+ if (BeginsWith(attribute, prefix))
+ return attribute + strlen(prefix);
+ else
+ return NULL;
+}
+
+inline void AddBinding (std::vector<EditorCurveBinding>& attributes, int classID, const std::string& attribute)
+{
+ attributes.push_back(EditorCurveBinding ("", classID, NULL, attribute, false));
+}
+
+inline void AddBindingCheckUnique (std::vector<EditorCurveBinding>& attributes, int startIndex, int classID, const std::string& attribute)
+{
+ for (int i=startIndex;i<attributes.size();i++)
+ {
+ if (attributes[i].classID == classID && attributes[i].attribute == attribute)
+ return;
+ }
+
+ attributes.push_back(EditorCurveBinding ("", classID, NULL, attribute, false));
+}
+
+
+inline void AddPPtrBinding (std::vector<EditorCurveBinding>& attributes, int classID, const std::string& attribute)
+{
+ attributes.push_back(EditorCurveBinding ("", classID, NULL, attribute, true));
+}
+
+inline int ParseIndexAttributeIndex (const UnityStr& attribute, const char* preString)
+{
+ const std::string::size_type pos0 = attribute.find_first_of('[');
+ const std::string::size_type pos1 = attribute.find_first_of(']');
+
+ if (pos0 == std::string::npos || pos1 == std::string::npos ||
+ !BeginsWith(attribute, preString))
+ {
+ return -1;
+ }
+
+ Assert(pos0 < pos1);
+ Assert(pos1 - pos0 > 1);
+
+ return StringToInt(attribute.c_str() + pos0);
+}
+
diff --git a/Runtime/Interfaces/IAnimationStateNetworkProvider.cpp b/Runtime/Interfaces/IAnimationStateNetworkProvider.cpp
new file mode 100644
index 0000000..3198cfa
--- /dev/null
+++ b/Runtime/Interfaces/IAnimationStateNetworkProvider.cpp
@@ -0,0 +1,14 @@
+#include "UnityPrefix.h"
+#include "IAnimationStateNetworkProvider.h"
+
+static IAnimationStateNetworkProvider* gAnimationProvider;
+
+IAnimationStateNetworkProvider* GetIAnimationStateNetworkProvider ()
+{
+ return gAnimationProvider;
+}
+
+void SetIAnimationStateNetworkProvider (IAnimationStateNetworkProvider* manager)
+{
+ gAnimationProvider = manager;
+} \ No newline at end of file
diff --git a/Runtime/Interfaces/IAnimationStateNetworkProvider.h b/Runtime/Interfaces/IAnimationStateNetworkProvider.h
new file mode 100644
index 0000000..797c744
--- /dev/null
+++ b/Runtime/Interfaces/IAnimationStateNetworkProvider.h
@@ -0,0 +1,22 @@
+#pragma once
+
+class Animation;
+
+struct AnimationStateForNetwork
+{
+ bool enabled;
+ float weight;
+ float time;
+};
+
+class IAnimationStateNetworkProvider
+{
+public:
+
+ virtual int GetNetworkAnimationStateCount (Animation& animation) = 0;
+ virtual void GetNetworkAnimationState (Animation& animation, AnimationStateForNetwork* state, int count) = 0;
+ virtual void SetNetworkAnimationState (Animation& animation, const AnimationStateForNetwork* serialize, int count) = 0;
+};
+
+EXPORT_COREMODULE IAnimationStateNetworkProvider* GetIAnimationStateNetworkProvider ();
+EXPORT_COREMODULE void SetIAnimationStateNetworkProvider (IAnimationStateNetworkProvider* manager); \ No newline at end of file
diff --git a/Runtime/Interfaces/IAudio.cpp b/Runtime/Interfaces/IAudio.cpp
new file mode 100644
index 0000000..ca1189c
--- /dev/null
+++ b/Runtime/Interfaces/IAudio.cpp
@@ -0,0 +1,14 @@
+#include "UnityPrefix.h"
+#include "IAudio.h"
+
+static IAudio* gAudio = NULL;
+
+IAudio* GetIAudio()
+{
+ return gAudio;
+}
+
+void SetIAudio(IAudio* audio)
+{
+ gAudio = audio;
+} \ No newline at end of file
diff --git a/Runtime/Interfaces/IAudio.h b/Runtime/Interfaces/IAudio.h
new file mode 100644
index 0000000..f8d1760
--- /dev/null
+++ b/Runtime/Interfaces/IAudio.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Audio/correct_fmod_includer.h"
+
+class Object;
+class MovieTexture;
+class WWW;
+class AudioClip;
+struct AudioStats;
+namespace FMOD { class DSP; }
+class AudioCustomFilter;
+class MonoBehaviour;
+
+class IAudio
+{
+public:
+ virtual void SetPause( bool pause ) = 0;
+ virtual void FixedUpdate() = 0;
+ virtual void Update() = 0;
+
+ virtual void StopVideoTextures() = 0;
+ virtual void UpdateVideoTextures() = 0;
+ virtual void PauseVideoTextures() = 0;
+
+#if ENABLE_WWW
+#if ENABLE_MOVIES
+ virtual MovieTexture* CreateMovieTextureFromWWW(WWW& www) = 0;
+#endif
+ virtual AudioClip* CreateAudioClipFromWWW(WWW& www, bool threeD, bool stream, FMOD_SOUND_TYPE audioType) = 0;
+ virtual AudioClip* CreateAudioClipOGGFromWWW(WWW& www) = 0;
+#endif
+
+ virtual bool IsFormatSupportedByPlatform(const char* type) = 0;
+
+#if ENABLE_AUDIO_FMOD
+ virtual FMOD::DSP* GetOrCreateDSPFromCustomFilter(AudioCustomFilter* filter) = 0;
+ virtual AudioCustomFilter* CreateAudioCustomFilter(MonoBehaviour* mb) = 0;
+ virtual FMOD::DSP* GetDSPFromAudioCustomFilter(AudioCustomFilter* filter) = 0;
+ virtual void SetBypassOnDSP(FMOD::DSP* dsp, bool state) = 0;
+#endif
+
+#if ENABLE_PROFILER
+ virtual void GetProfilerStats(AudioStats& stats) = 0;
+#endif
+ virtual void AudioManagerAwakeFromLoad(AwakeFromLoadMode mode) = 0;
+};
+
+IAudio* CreateAudioModule();
+
+
+IAudio* EXPORT_COREMODULE GetIAudio();
+void EXPORT_COREMODULE SetIAudio(IAudio* physics); \ No newline at end of file
diff --git a/Runtime/Interfaces/IClusterRenderer.cpp b/Runtime/Interfaces/IClusterRenderer.cpp
new file mode 100644
index 0000000..cda6697
--- /dev/null
+++ b/Runtime/Interfaces/IClusterRenderer.cpp
@@ -0,0 +1,14 @@
+#include "IClusterRenderer.h"
+#if ENABLE_CLUSTER_SYNC
+static IClusterRenderer* gIClusterRenderer = NULL;
+
+IClusterRenderer* GetIClusterRenderer()
+{
+ return gIClusterRenderer;
+}
+
+void SetIClusterRenderer(IClusterRenderer* value)
+{
+ gIClusterRenderer = value;
+}
+#endif \ No newline at end of file
diff --git a/Runtime/Interfaces/IClusterRenderer.h b/Runtime/Interfaces/IClusterRenderer.h
new file mode 100644
index 0000000..dfc420b
--- /dev/null
+++ b/Runtime/Interfaces/IClusterRenderer.h
@@ -0,0 +1,16 @@
+#pragma once
+#if ENABLE_CLUSTER_SYNC
+#include "Runtime/Utilities/NonCopyable.h"
+
+class EXPORT_COREMODULE IClusterRenderer : public NonCopyable
+{
+public:
+ virtual void InitCluster() = 0;
+ virtual void SynchronizeCluster() = 0;
+ virtual bool IsMasterOfCluster() = 0;
+ virtual void ShutdownCluster() = 0;
+};
+
+EXPORT_COREMODULE IClusterRenderer* GetIClusterRenderer();
+EXPORT_COREMODULE void SetIClusterRenderer(IClusterRenderer* value);
+#endif \ No newline at end of file
diff --git a/Runtime/Interfaces/IGfxDevice.cpp b/Runtime/Interfaces/IGfxDevice.cpp
new file mode 100644
index 0000000..3e358a0
--- /dev/null
+++ b/Runtime/Interfaces/IGfxDevice.cpp
@@ -0,0 +1,54 @@
+#include "UnityPrefix.h"
+#include "IGfxDevice.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Misc/Plugins.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Shaders/Shader.h"
+
+static IGfxDevice* gGfxDevice;
+
+IGfxDevice* GetIGfxDevice ()
+{
+ return gGfxDevice;
+}
+
+void SetIGfxDevice (IGfxDevice* manager)
+{
+ gGfxDevice = manager;
+}
+
+
+void CommonReloadResources(int flags)
+{
+ if (flags & GfxDevice::kReloadTextures)
+ Texture::ReloadAll();
+
+ if (flags & GfxDevice::kReloadShaders)
+ Shader::ReloadAllShaders();
+
+ if (flags & GfxDevice::kReleaseRenderTextures)
+ RenderTexture::ReleaseAll();
+}
+
+bool InitializeGfxDevice() {
+ if (!GetIGfxDevice()->InitializeGfxDevice())
+ return false;
+
+ GfxDevice& device = GetGfxDevice();
+ device.SetMarkerCallback (PluginsRenderMarker);
+ device.SetSetNativeGfxDeviceCallback (PluginsSetGraphicsDevice);
+ device.SetReloadCallback (CommonReloadResources);
+ return true;
+}
+
+void ParseGfxDeviceArgs () { return GetIGfxDevice()->ParseGfxDeviceArgs(); }
+bool IsGfxDevice() { return GetIGfxDevice()->IsGfxDevice(); }
+GfxDevice& GetGfxDevice() { return GetIGfxDevice()->GetGfxDevice(); }
+GfxDevice& GetUncheckedGfxDevice() { return GetIGfxDevice()->GetUncheckedGfxDevice(); }
+void DestroyGfxDevice() { GetIGfxDevice()->DestroyGfxDevice(); }
+
+GfxDevice& GetRealGfxDevice() { return GetIGfxDevice()->GetRealGfxDevice(); }
+bool IsRealGfxDeviceThreadOwner() { return GetIGfxDevice()->IsRealGfxDeviceThreadOwner(); }
+
+GraphicsCaps& gGraphicsCaps { return GetIGfxDevice()->gGraphicsCaps; }
diff --git a/Runtime/Interfaces/IGfxDevice.h b/Runtime/Interfaces/IGfxDevice.h
new file mode 100644
index 0000000..749c723
--- /dev/null
+++ b/Runtime/Interfaces/IGfxDevice.h
@@ -0,0 +1,28 @@
+#ifndef IGFXDEVICE_H
+#define IGFXDEVICE_H
+
+class GfxDevice;
+class GraphicsCaps;
+class GpuProgramParameters;
+
+class IGfxDevice
+{
+public:
+ virtual bool IsGfxDevice() = 0;
+ virtual bool InitializeGfxDevice() = 0;
+ virtual bool IsRealGfxDeviceThreadOwner() = 0;
+ virtual GfxDevice &GetGfxDevice() = 0;
+ virtual GfxDevice &GetUncheckedGfxDevice() = 0;
+ virtual GfxDevice &GetRealGfxDevice() = 0;
+ virtual void DestroyGfxDevice() = 0;
+ virtual void ParseGfxDeviceArgs() = 0;
+ virtual GpuProgramParameters* CreateGpuProgramParameters() = 0;
+ virtual void DestroyGpuProgramParameters(GpuProgramParameters*) = 0;
+ virtual GraphicsCaps &gGraphicsCaps = 0;
+
+};
+
+EXPORT_COREMODULE IGfxDevice* GetIGfxDevice ();
+EXPORT_COREMODULE void SetIGfxDevice (IGfxDevice* device);
+
+#endif \ No newline at end of file
diff --git a/Runtime/Interfaces/IPhysics.cpp b/Runtime/Interfaces/IPhysics.cpp
new file mode 100644
index 0000000..e7c84b1
--- /dev/null
+++ b/Runtime/Interfaces/IPhysics.cpp
@@ -0,0 +1,14 @@
+#include "UnityPrefix.h"
+#include "IPhysics.h"
+
+static IPhysics* gIPhysics = NULL;
+
+IPhysics* GetIPhysics()
+{
+ return gIPhysics;
+}
+
+void SetIPhysics(IPhysics* phys)
+{
+ gIPhysics = phys;
+} \ No newline at end of file
diff --git a/Runtime/Interfaces/IPhysics.h b/Runtime/Interfaces/IPhysics.h
new file mode 100644
index 0000000..d2497b4
--- /dev/null
+++ b/Runtime/Interfaces/IPhysics.h
@@ -0,0 +1,72 @@
+#pragma once
+
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Utilities/NonCopyable.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+class Rigidbody;
+struct PhysicsStats;
+class NxTriangleMesh;
+class NxConvexMesh;
+class NxStream;
+class CapsuleCollider;
+struct Collision;
+namespace Unity { class SkinnedCloth; }
+class Collider;
+class MeshCollider;
+class NxConvexMeshDesc;
+class NxTriangleMeshDesc;
+class MemoryStream;
+class Mesh;
+class NxHeightField;
+class NxHeightFieldDesc;
+
+class EXPORT_COREMODULE IPhysics : public NonCopyable
+{
+public:
+ // Collision intersection
+ struct RigidBodyState
+ {
+ Vector3f position;
+ Quaternionf rotation;
+ Vector3f velocity;
+ Vector3f avelocity;
+ };
+
+ virtual void SetRigidBodyState( Rigidbody& rigidbody, const RigidBodyState& state ) = 0;
+ virtual void GetRigidBodyState( const Rigidbody& rigidbody, RigidBodyState* result) = 0;
+ virtual Vector3f GetRigidBodyVelocity( const Rigidbody& rigidbody) = 0;
+ virtual Vector3f GetGravity() = 0;
+
+ virtual void* CreateNxMeshFromNxStream(bool convex, const NxStream& stream) = 0;
+
+ virtual void ReleaseNxTriangleMesh(NxTriangleMesh& mesh) = 0;
+ virtual void ReleaseNxConvexMesh(NxConvexMesh& mesh) = 0;
+
+ virtual void CapsuleColliderSetHeight(CapsuleCollider& collider, float height) = 0;
+
+ virtual ScriptingObjectPtr ConvertContactToMono (Collision* input) = 0;
+ virtual int GetColliderMaterialInstanceID(Collider& collider) = 0;
+ virtual int GetColliderSharedMeshInstanceID(MeshCollider& collider) = 0;
+
+ virtual bool CreateNxStreamFromUnityMesh (Mesh* mesh, bool convex, const Matrix4x4f& scalematrix, TransformType transformType, MemoryStream& stream ) = 0;
+ virtual void* CreateNxMeshFromUnityMesh (Mesh* mesh, bool convex, const Matrix4x4f& scalematrix, TransformType transformType ) = 0;
+ virtual MemoryStream* CreateNxStreamFromUnityMesh(Mesh& meshData, bool convex) = 0;
+
+ virtual void DeleteMemoryStream(MemoryStream* memory) = 0;
+
+ virtual void ReleaseHeightField(NxHeightField& heightField) = 0;
+ virtual NxHeightField* CreateNxHeightField(NxHeightFieldDesc& desc) = 0;
+
+#if ENABLE_CLOTH
+ virtual void SetUpSkinnedBuffersOnSkinnedCloth (Unity::SkinnedCloth& cloth, void *vertices, void *normals, void *tangents, size_t bufferStride) = 0;
+#endif
+
+#if ENABLE_PROFILER
+ virtual void GetProfilerStats(PhysicsStats& stats) = 0;
+#endif
+};
+
+EXPORT_COREMODULE IPhysics* GetIPhysics();
+EXPORT_COREMODULE void SetIPhysics(IPhysics* physics); \ No newline at end of file
diff --git a/Runtime/Interfaces/IPhysics2D.cpp b/Runtime/Interfaces/IPhysics2D.cpp
new file mode 100644
index 0000000..f94ead4
--- /dev/null
+++ b/Runtime/Interfaces/IPhysics2D.cpp
@@ -0,0 +1,23 @@
+#include "UnityPrefix.h"
+#include "IPhysics2D.h"
+
+
+// --------------------------------------------------------------------------
+
+
+static IPhysics2D* gIPhysics2D = NULL;
+
+
+// --------------------------------------------------------------------------
+
+
+IPhysics2D* GetIPhysics2D()
+{
+ return gIPhysics2D;
+}
+
+
+void SetIPhysics2D(IPhysics2D* value)
+{
+ gIPhysics2D = value;
+}
diff --git a/Runtime/Interfaces/IPhysics2D.h b/Runtime/Interfaces/IPhysics2D.h
new file mode 100644
index 0000000..8cdc8f8
--- /dev/null
+++ b/Runtime/Interfaces/IPhysics2D.h
@@ -0,0 +1,29 @@
+#pragma once
+
+// --------------------------------------------------------------------------
+
+
+struct Physics2DStats;
+
+
+// --------------------------------------------------------------------------
+
+
+class IPhysics2D
+{
+public:
+ virtual void FixedUpdate () = 0;
+ virtual void DynamicUpdate () = 0;
+ virtual void ResetInterpolations () = 0;
+
+#if ENABLE_PROFILER
+ virtual void GetProfilerStats (Physics2DStats& stats) = 0;
+#endif
+};
+
+
+// --------------------------------------------------------------------------
+
+
+IPhysics2D* GetIPhysics2D ();
+void SetIPhysics2D (IPhysics2D* manager);
diff --git a/Runtime/Interfaces/IRaycast.cpp b/Runtime/Interfaces/IRaycast.cpp
new file mode 100644
index 0000000..b910856
--- /dev/null
+++ b/Runtime/Interfaces/IRaycast.cpp
@@ -0,0 +1,56 @@
+#include "UnityPrefix.h"
+#include "IRaycast.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Dynamics/PhysicsManager.h"
+#include <limits>
+
+// Test if ray intersects at least one of the aabbs
+bool IRaycast::IntersectAny( const Ray& ray, float maxDistance, AABB* shapeBounds, size_t shapeCount )
+{
+ const Vector3f halfDir = ray.GetDirection() * maxDistance * 0.5;
+ const AABB rayBounds(ray.GetOrigin() + halfDir, Abs(halfDir));
+ for (size_t s = 0; s < shapeCount; ++s)
+ {
+ if (IntersectAABBAABBInclusive(rayBounds, shapeBounds[s]))
+ return true;
+ }
+ return false;
+}
+
+// Test if ray intersects at least one of the aabbs
+bool IRaycast::IntersectAny( const BatchedRaycast& ray, AABB* shapeBounds, size_t shapeCount )
+{
+ const Vector3f halfDir = (ray.to-ray.from) * 0.5;
+ const AABB rayBounds(ray.from + halfDir, Abs(halfDir));
+ for (size_t s = 0; s < shapeCount; ++s)
+ {
+ if (IntersectAABBAABBInclusive(rayBounds, shapeBounds[s]))
+ return true;
+ }
+ return false;
+}
+
+// AABB of ray segments expanded with particle radius
+MinMaxAABB IRaycast::ComputeBatchAABB( const dynamic_array<BatchedRaycast>& raycasts )
+{
+ MinMaxAABB aabb;
+ for (size_t q = 0; q < raycasts.size(); ++q)
+ {
+ aabb.Encapsulate(raycasts[q].from);
+ aabb.Encapsulate(raycasts[q].to);
+ }
+ return aabb;
+}
+
+static IRaycast* gRaycaster = NULL;
+
+IRaycast* GetRaycastInterface()
+{
+ return gRaycaster;
+}
+
+void SetRaycastInterface(IRaycast* theInterface)
+{
+ gRaycaster = theInterface;
+}
diff --git a/Runtime/Interfaces/IRaycast.h b/Runtime/Interfaces/IRaycast.h
new file mode 100644
index 0000000..18539ae
--- /dev/null
+++ b/Runtime/Interfaces/IRaycast.h
@@ -0,0 +1,73 @@
+#ifndef IRAYCAST_H
+#define IRAYCAST_H
+
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Geometry/Ray.h"
+#include "Runtime/Utilities/NonCopyable.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Modules/ExportModules.h"
+
+class AABB;
+class MinMaxAABB;
+class Plane;
+class Collider;
+struct RaycastHit;
+
+// Collision intersection
+struct HitInfo
+{
+ Vector3f intersection;
+ Vector3f normal;
+ int colliderInstanceID; // The instanceID of the intersected Collider component.
+ int rigidBodyOrColliderInstanceID; // The instanceID of the RigidBody to which the Collider is attached. If there is none then the ID is the same as colliderInstanceID.
+};
+
+// Batch intersection result
+struct BatchedRaycastResult
+{
+ BatchedRaycastResult(unsigned int i, const HitInfo& h) : index(i), hitInfo(h) {}
+ unsigned int index; // the index of the result into BatchedRaycast array
+ HitInfo hitInfo; // the intersection
+};
+
+// Batch intersection request
+struct BatchedRaycast
+{
+ BatchedRaycast(unsigned int i, const Vector3f& f, const Vector3f& t) : index(i), from(f), to(t) {}
+ unsigned int index; // user provided index
+ Vector3f from; // ray origin
+ Vector3f to; // ray endpoint
+};
+
+/// Abstract ray tracing class
+class EXPORT_COREMODULE IRaycast : public NonCopyable
+{
+public:
+ /// ======================================
+ /// Useful intersection methods
+
+ /// Returns true if any shape is intersected along the ray within maxDist
+ static bool IntersectAny( const Ray& ray, float maxDist, AABB* shapeBounds, size_t shapeCount );
+
+ /// Version of above taking a pair of points
+ static bool IntersectAny( const BatchedRaycast& ray, AABB* shapeBounds, size_t shapeCount );
+
+ /// ======================================
+ /// Virtual interface
+
+ /// Intersects all the rays and returns any intersections in results, the return value specifies how many intersections were found.
+ /// The index entry within BatchedRaycast is user provided while the index in BatchedRaycastResult specifies the BatchedRaycast that it is a result.
+ size_t virtual BatchIntersect( const dynamic_array<BatchedRaycast>& raycasts, dynamic_array<BatchedRaycastResult>& results, const UInt32 filter, bool staticOnly ) const = 0;
+
+ bool virtual Raycast (const Ray& ray, float distance, int mask, HitInfo& hit) = 0;
+
+protected:
+ /// Helper function for computing AABB collection of rays
+ static MinMaxAABB ComputeBatchAABB( const dynamic_array<BatchedRaycast>& raycasts);
+};
+
+/// Singleton access
+EXPORT_COREMODULE IRaycast* GetRaycastInterface();
+EXPORT_COREMODULE void SetRaycastInterface(IRaycast* theInterface); // argh, using interface raises an C2332
+
+#endif
diff --git a/Runtime/Interfaces/ITerrainManager.cpp b/Runtime/Interfaces/ITerrainManager.cpp
new file mode 100644
index 0000000..10f5c87
--- /dev/null
+++ b/Runtime/Interfaces/ITerrainManager.cpp
@@ -0,0 +1,14 @@
+#include "UnityPrefix.h"
+#include "ITerrainManager.h"
+
+static ITerrainManager* gTerrainManager;
+
+ITerrainManager* GetITerrainManager ()
+{
+ return gTerrainManager;
+}
+
+void SetITerrainManager (ITerrainManager* manager)
+{
+ gTerrainManager = manager;
+}
diff --git a/Runtime/Interfaces/ITerrainManager.h b/Runtime/Interfaces/ITerrainManager.h
new file mode 100644
index 0000000..97f930d
--- /dev/null
+++ b/Runtime/Interfaces/ITerrainManager.h
@@ -0,0 +1,46 @@
+#pragma once
+#include <list>
+
+#include "Runtime/Modules/ExportModules.h"
+#include "Runtime/Camera/SceneNode.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+class Light;
+class Object;
+class TerrainInstance;
+class Vector3f;
+class NxHeightField;
+class Heightmap;
+
+typedef UNITY_LIST(kMemRenderer, TerrainInstance*) TerrainList;
+
+class ITerrainManager
+{
+public:
+#if ENABLE_PHYSICS
+ virtual NxHeightField* Heightmap_GetNxHeightField(Heightmap& heightmap) = 0;
+#endif
+ virtual int Heightmap_GetMaterialIndex(Heightmap& heightmap) = 0;
+ virtual Vector3f Heightmap_GetSize(Heightmap& heightmap) = 0;
+
+ /// Extracts the height on the heightmap from a TerrainData
+ /// Returns true if the heightmap was sampled and the position is inside the terrain extents.
+ virtual bool GetInterpolatedHeight (const Object* terrainData, const Vector3f& terrainPosition, const Vector3f& position, float& outputHeight) = 0;
+
+ /// Render all terrains
+ virtual void CullAllTerrains (int cullingMask) = 0;
+ /// Set the lightmap index on all terrains
+ virtual void SetLightmapIndexOnAllTerrains (int lightmapIndex) = 0;
+ virtual void AddTerrainAndSetActive (TerrainInstance* terrain) = 0;
+ virtual void RemoveTerrain (TerrainInstance* terrain) = 0;
+ virtual TerrainInstance* GetActiveTerrain () const = 0;
+ virtual const TerrainList& GetActiveTerrains () const = 0;
+ virtual void UnloadTerrainsFromGfxDevice () = 0;
+ virtual void ReloadTerrainsToGfxDevice () = 0;
+
+ virtual void CollectTreeRenderers(dynamic_array<SceneNode>& sceneNodes, dynamic_array<AABB>& boundingBoxes) const = 0;
+};
+
+EXPORT_COREMODULE ITerrainManager* GetITerrainManager ();
+EXPORT_COREMODULE void SetITerrainManager (ITerrainManager* manager);
diff --git a/Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.csproj b/Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.csproj
new file mode 100644
index 0000000..07b5653
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.csproj
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.30729</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{31C2F345-D887-49DD-A1F6-741CABD74A42}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>CrossDomainPolicyParser</RootNamespace>
+ <AssemblyName>CrossDomainPolicyParser</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="Mono.Forks\AddressFamily.cs" />
+ <Compile Include="Mono.Forks\BaseDomainPolicy.cs" />
+ <Compile Include="Mono.Forks\CrossDomainPolicyManager.cs" />
+ <Compile Include="Mono.Forks\FlashCrossDomainPolicy.cs" />
+ <Compile Include="Mono.Forks\FlashCrossDomainPolicyParser.cs" />
+ <Compile Include="Mono.Forks\ICrossDomainPolicy.cs" />
+ <Compile Include="Mono.Forks\IPAddress.cs" />
+ <Compile Include="Mono.Forks\IPv6Address.cs" />
+ <Compile Include="Mono.Forks\Locale.cs" />
+ <Compile Include="Mono.Forks\MiniParser.cs" />
+ <Compile Include="Mono.Forks\NoAccessPolicy.cs" />
+ <Compile Include="Mono.Forks\PolicyDownloadPolicy.cs" />
+ <Compile Include="Mono.Forks\SiteOfOriginPolicy.cs" />
+ <Compile Include="Mono.Forks\UnityExtra.cs" />
+ <Compile Include="Mono.Forks\Uri.cs" />
+ <Compile Include="Mono.Forks\UriFormatException.cs" />
+ <Compile Include="Mono.Forks\UriHostNameType.cs" />
+ <Compile Include="Mono.Forks\UriKind.cs" />
+ <Compile Include="Mono.Forks\UriPartial.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="UnityCrossDomainHelper.cs" />
+ <Compile Include="UriTools.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Reference Include="UnityEngine">
+ <HintPath>..\..\..\build\ManagedAssemblies\UnityEngine.dll</HintPath>
+ <Private>False</Private>
+ </Reference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
diff --git a/Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.jam b/Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.jam
new file mode 100644
index 0000000..e56959a
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.jam
@@ -0,0 +1,46 @@
+SubDir TOP Runtime Managed CrossDomainPolicyParser ;
+
+ActiveProject CrossDomainPolicyParser ;
+
+NotFile CrossDomainPolicyParser ;
+local csfiles = @($(TOP)/Runtime/Managed/CrossDomainPolicyParser/**/*.cs:W=$(TOP)/:X=Tests) ;
+
+# Globbing hack
+if $(PLATFORM) in linux32 linux64
+{
+ csfiles = [ Split [ Shell "find $(TOP)/Runtime/Managed/CrossDomainPolicyParser -name '*.cs'" ] : ";
+" ] ;
+ csfiles = $(csfiles:W=$(TOP)/) ;
+ csfiles = $(csfiles:X=Tests) ;
+}
+SEARCH on $(csfiles:G=CrossDomainPolicyParser) = $(TOP) ;
+
+local unityenginelocation_crossdomain = $(unityenginelocation_editor) ;
+unityenginelocation_crossdomain ?= $(unityenginelocation_webplayer) ;
+
+local targetdirs ;
+local tempdir ;
+if $(OS) = NT
+{
+ tempdir = $(TOP)/build/temp ;
+ targetdirs += $(TOP)/build/WindowsEditor/Data/Managed ;
+ targetdirs += $(TOP)/build/WindowsWebplayer/Data/lib ;
+ targetdirs += $(TOP)/build/Windows64WebPlayer/Data/lib ;
+ targetdirs += $(TOP)/build/Windows64Editor/Data/Managed ;
+} else
+{
+ tempdir = $(TOP)/build/temp ;
+ targetdirs += $(TOP)/build/MacEditor/Unity.app/Contents/Frameworks/Managed ;
+}
+
+local temp = $(tempdir)/CrossDomainPolicyParser.dll ;
+MkDir $(tempdir) ;
+Depends $(temp) : $(tempdir) ;
+Depends $(temp) : $(unityenginelocation_crossdomain) ;
+
+BuildAssembly CrossDomainPolicyParser : 2.0 : $(temp) : $(csfiles) : : $(unityenginelocation_crossdomain:T) ;
+
+for targetdir in $(targetdirs)
+{
+ CopyFile : $(targetdir)/CrossDomainPolicyParser.dll : $(temp) ;
+}
diff --git a/Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.sln b/Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.sln
new file mode 100644
index 0000000..f8bd348
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/CrossDomainPolicyParser.sln
@@ -0,0 +1,41 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossDomainPolicyParser", "CrossDomainPolicyParser.csproj", "{31C2F345-D887-49DD-A1F6-741CABD74A42}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossDomainPolicyParserTests", "Tests\CrossDomainPolicyParserTests.csproj", "{5C04DB87-3B34-43B4-B164-86CE4361DC7B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityEngine", "..\..\..\Projects\CSharp\UnityEngine.csproj", "{F0499708-3EB6-4026-8362-97E6FFC4E7C8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.TestableAssemblyMaker", "..\..\..\Tools\Unity.TestableAssemblyMaker\Unity.TestableAssemblyMaker.csproj", "{3C2CC5A1-0663-458A-AF4B-52248977DDF3}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {31C2F345-D887-49DD-A1F6-741CABD74A42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {31C2F345-D887-49DD-A1F6-741CABD74A42}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {31C2F345-D887-49DD-A1F6-741CABD74A42}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {31C2F345-D887-49DD-A1F6-741CABD74A42}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3C2CC5A1-0663-458A-AF4B-52248977DDF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3C2CC5A1-0663-458A-AF4B-52248977DDF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3C2CC5A1-0663-458A-AF4B-52248977DDF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3C2CC5A1-0663-458A-AF4B-52248977DDF3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5C04DB87-3B34-43B4-B164-86CE4361DC7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5C04DB87-3B34-43B4-B164-86CE4361DC7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5C04DB87-3B34-43B4-B164-86CE4361DC7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5C04DB87-3B34-43B4-B164-86CE4361DC7B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F0499708-3EB6-4026-8362-97E6FFC4E7C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F0499708-3EB6-4026-8362-97E6FFC4E7C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F0499708-3EB6-4026-8362-97E6FFC4E7C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F0499708-3EB6-4026-8362-97E6FFC4E7C8}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(MonoDevelopProperties) = preSolution
+ StartupItem = CrossDomainPolicyParser.csproj
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/AddressFamily.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/AddressFamily.cs
new file mode 100644
index 0000000..5277509
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/AddressFamily.cs
@@ -0,0 +1,73 @@
+// AddressFamily.cs
+//
+// This code was automatically generated from
+// ECMA CLI XML Library Specification.
+// Generator: libgen.xsl [1.0; (C) Sergey Chaban (serge@wildwestsoftware.com)]
+// Created: Wed, 5 Sep 2001 06:31:59 UTC
+// Source file: AllTypes.xml
+// URL: http://msdn.microsoft.com/net/ecma/AllTypes.xml
+//
+// (C) 2001 Ximian, Inc. http://www.ximian.com
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+namespace MonoForks.System.Net.Sockets {
+
+
+#if ONLY_1_1
+ [Serializable]
+#endif
+ public enum AddressFamily {
+ Unknown = -1,
+ Unspecified = 0,
+ Unix = 1,
+ InterNetwork = 2,
+ ImpLink = 3,
+ Pup = 4,
+ Chaos = 5,
+ NS = 6,
+ Ipx = 6,
+ Iso = 7,
+ Osi = 7,
+ Ecma = 8,
+ DataKit = 9,
+ Ccitt = 10,
+ Sna = 11,
+ DecNet = 12,
+ DataLink = 13,
+ Lat = 14,
+ HyperChannel = 15,
+ AppleTalk = 16,
+ NetBios = 17,
+ VoiceView = 18,
+ FireFox = 19,
+ Banyan = 21,
+ Atm = 22,
+ InterNetworkV6 = 23,
+ Cluster = 24,
+ Ieee12844 = 25,
+ Irda = 26,
+ NetworkDesigners = 28,
+ Max = 29,
+ }
+}
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/BaseDomainPolicy.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/BaseDomainPolicy.cs
new file mode 100644
index 0000000..34fe6fa
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/BaseDomainPolicy.cs
@@ -0,0 +1,147 @@
+//
+// BaseDomainPolicy.cs
+//
+// Authors:
+// Atsushi Enomoto <atsushi@ximian.com>
+// Moonlight List (moonlight-list@lists.ximian.com)
+//
+// Copyright (C) 2009 Novell, Inc. http://www.novell.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#define NET_2_1
+#if NET_2_1
+
+using System;
+using MonoForks.System;
+using System.Collections.Generic;
+using System.IO;
+using MonoForks.System.Net;
+#if !TEST
+using MonoForks.System.Windows.Interop;
+#endif
+
+// Base class for shared stuff between the Silverlight and Flash policies
+// e.g. Headers and Domain comparison
+
+namespace MonoForks.System.Windows.Browser.Net {
+
+ abstract class BaseDomainPolicy : ICrossDomainPolicy {
+#if TEST
+ static public Uri ApplicationUri { get; set; }
+#else
+ static public Uri ApplicationUri {
+ get { return PluginHost.RootUri; }
+ }
+#endif
+ static string root;
+
+ static public string ApplicationRoot {
+ get {
+ if (root == null)
+ root = CrossDomainPolicyManager.GetRoot (ApplicationUri);
+ return root;
+ }
+ }
+
+ public class Headers {
+
+ class PrefixComparer : IEqualityComparer<string> {
+
+ public bool Equals (string x, string y)
+ {
+ int check_length = x.Length - 1;
+ if ((x.Length > 0) && (x [check_length] == '*'))
+ return (String.Compare (x, 0, y, 0, check_length, StringComparison.OrdinalIgnoreCase) == 0);
+
+ return (String.Compare (x, y, StringComparison.OrdinalIgnoreCase) == 0);
+ }
+
+ public int GetHashCode (string obj)
+ {
+ return (obj == null) ? 0 : obj.GetHashCode ();
+ }
+ }
+
+ static PrefixComparer pc = new PrefixComparer ();
+
+ private List<string> list;
+
+ public Headers ()
+ {
+ }
+
+ public bool AllowAllHeaders { get; private set; }
+
+ public bool IsAllowed (string[] headers)
+ {
+ if (AllowAllHeaders)
+ return true;
+
+ if (headers == null || headers.Length == 0)
+ return true;
+
+ foreach(var h in headers)
+ {
+ bool found = false;
+ foreach(var item in list)
+ if (pc.Equals(item,h)) found = true;
+ if (!found) return false;
+ }
+ return true;
+ }
+
+ public void SetHeaders (string raw)
+ {
+ if (raw == "*") {
+ AllowAllHeaders = true;
+ list = null;
+ } else if (raw != null) {
+ string [] headers = raw.Split (',');
+ list = new List<string> (headers.Length + 1);
+ list.Add ("Content-Type");
+ for (int i = 0; i < headers.Length; i++) {
+ string s = headers [i].Trim ();
+ if (!String.IsNullOrEmpty (s))
+ list.Add (s);
+ }
+ } else {
+ // without a specified 'http-request-headers' no header, expect Content-Type, is allowed
+ AllowAllHeaders = false;
+ list = new List<string> (1);
+ list.Add ("Content-Type");
+ }
+ }
+ }
+
+ public bool IsAllowed (WebRequest request)
+ {
+ var keys = request.Headers.Keys;
+ string[] AllKeys = new string[keys.Count];
+ keys.CopyTo(AllKeys,0);
+ return IsAllowed (request.RequestUri, AllKeys);
+ }
+
+ abstract public bool IsAllowed (Uri uri, params string [] headerKeys);
+ }
+}
+
+#endif
+
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/CrossDomainPolicyManager.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/CrossDomainPolicyManager.cs
new file mode 100644
index 0000000..a73eadc
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/CrossDomainPolicyManager.cs
@@ -0,0 +1,245 @@
+//
+// CrossDomainPolicyManager.cs
+//
+// Authors:
+// Atsushi Enomoto <atsushi@ximian.com>
+// Moonlight List (moonlight-list@lists.ximian.com)
+//
+// Copyright (C) 2009 Novell, Inc. http://www.novell.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#define NET_2_1
+#if NET_2_1
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using MonoForks.System.Windows.Interop;
+using System.Security;
+using System.Reflection;
+
+namespace MonoForks.System.Windows.Browser.Net
+{
+ internal static class CrossDomainPolicyManager
+ {
+
+ public static string GetRoot(Uri uri)
+ {
+ if ((uri.Scheme == "http" && uri.Port == 80) || (uri.Scheme == "https" && uri.Port == 443) || (uri.Port == -1))
+ return String.Format("{0}://{1}/", uri.Scheme, uri.DnsSafeHost);
+ else
+ return String.Format("{0}://{1}:{2}/", uri.Scheme, uri.DnsSafeHost, uri.Port);
+ }
+
+ public const string ClientAccessPolicyFile = "/clientaccesspolicy.xml";
+ public const string CrossDomainFile = "/crossdomain.xml";
+
+ const int Timeout = 10000;
+
+ // Web Access Policy
+
+ static Dictionary<string, ICrossDomainPolicy> policies = new Dictionary<string, ICrossDomainPolicy>();
+
+ static internal ICrossDomainPolicy PolicyDownloadPolicy = new PolicyDownloadPolicy();
+ static ICrossDomainPolicy site_of_origin_policy = new SiteOfOriginPolicy();
+ static ICrossDomainPolicy no_access_policy = new NoAccessPolicy();
+
+ static Uri GetRootUri(Uri uri)
+ {
+ return new Uri(GetRoot(uri));
+ }
+
+ public static Uri GetSilverlightPolicyUri(Uri uri)
+ {
+ return new Uri(GetRootUri(uri), CrossDomainPolicyManager.ClientAccessPolicyFile);
+ }
+
+ public static Uri GetFlashPolicyUri(Uri uri)
+ {
+ return new Uri(GetRootUri(uri), CrossDomainPolicyManager.CrossDomainFile);
+ }
+
+ public static ICrossDomainPolicy GetCachedWebPolicy(Uri uri)
+ {
+ //Debug.Log("Got to GetCachedWebPolicy");
+ //Debug.Log("debug1. uri: " + uri.ToString() + " pluginhost.sourceuri: " + PluginHost.SourceUri);
+ //Debug.Log("debug1.1 pluginhost.rooturi: " + PluginHost.RootUri);
+ // if we request an Uri from the same site then we return an "always positive" policy
+ if (SiteOfOriginPolicy.HasSameOrigin(uri, PluginHost.SourceUri))
+ return site_of_origin_policy;
+
+ //Debug.Log("debug2");
+
+ //if this is a request for a crossdomainfile, then we allow it.
+ //TODO: Make this more secure.
+ string postfix = "";
+ if (!uri.IsDefaultPort) postfix = ":" + uri.Port;
+
+ if (uri.ToString() == uri.Scheme + "://" + uri.Host + postfix + "/crossdomain.xml") return PolicyDownloadPolicy;
+
+ //Debug.Log("debug3");
+ // otherwise we search for an already downloaded policy for the web site
+ string root = GetRoot(uri);
+ ICrossDomainPolicy policy = null;
+ policies.TryGetValue(root, out policy);
+ // and we return it (if we have it) or null (if we dont)
+ //Debug.Log("debug4: " + policy);
+ return policy;
+ }
+
+ private static void AddPolicy(Uri responseUri, ICrossDomainPolicy policy)
+ {
+ string root = GetRoot(responseUri);
+ try
+ {
+ policies.Add(root, policy);
+ }
+ catch (ArgumentException)
+ {
+ // it's possible another request already added this root
+ }
+ }
+
+ /*
+ public static ICrossDomainPolicy BuildSilverlightPolicy (HttpWebResponse response)
+ {
+ // return null if no Silverlight policy was found, since we offer a second chance with a flash policy
+ if (response.StatusCode != HttpStatusCode.OK)
+ return null;
+
+ ICrossDomainPolicy policy = null;
+ try {
+ policy = ClientAccessPolicy.FromStream (response.GetResponseStream ());
+ if (policy != null)
+ policies.Add (GetRoot (response.ResponseUri), policy);
+ } catch (Exception ex) {
+ Console.WriteLine (String.Format ("CrossDomainAccessManager caught an exception while reading {0}: {1}",
+ response.ResponseUri, ex.Message));
+ // and ignore.
+ }
+ return policy;
+ }
+
+ public static ICrossDomainPolicy BuildFlashPolicy (HttpWebResponse response)
+ {
+ bool ok = response.StatusCode == HttpStatusCode.OK;
+ return BuildFlashPolicy(ok, response.ResponseUri, response.GetResponseStream(), response.Headers);
+ }
+ */
+ public static ICrossDomainPolicy BuildFlashPolicy(bool statuscodeOK, Uri uri, Stream responsestream, Dictionary<string, string> responseheaders)
+ {
+ ICrossDomainPolicy policy = null;
+ if (statuscodeOK)
+ {
+ try
+ {
+ policy = FlashCrossDomainPolicy.FromStream(responsestream);
+ }
+ catch (Exception ex)
+ {
+ Log.Msg(String.Format("BuildFlashPolicy caught an exception while parsing {0}: {1}",
+ uri, ex.Message));
+ throw;
+ }
+ if (policy != null)
+ {
+ // see DRT# 864 and 865
+ Log.Msg("crossdomain.xml was succesfully parsed");
+ string site_control = null;
+ responseheaders.TryGetValue("X-Permitted-Cross-Domain-Policies", out site_control);
+ if (!String.IsNullOrEmpty(site_control))
+ (policy as FlashCrossDomainPolicy).SiteControl = site_control;
+ }
+ }
+
+ // the flash policy was the last chance, keep a NoAccess into the cache
+ if (policy == null)
+ policy = no_access_policy;
+
+ AddPolicy(uri, policy);
+ return policy;
+ }
+
+ public static void ClearCache()
+ {
+ policies.Clear();
+ }
+
+ // Socket Policy
+ //
+ // - we connect once to a site for the entire application life time
+ // - this returns us a policy file (silverlight format only) or else no access is granted
+ // - this policy file
+ // - can contain multiple policies
+ // - can apply to multiple domains
+ // - can grant access to several resources
+
+ static readonly Dictionary<string, FlashCrossDomainPolicy> SocketPoliciesByIp = new Dictionary<string, FlashCrossDomainPolicy>();
+ const int PolicyPort = 843;
+
+ static public bool CheckSocketEndPoint(string connecting_to_ip, int port)
+ {
+ return CheckSocketEndPoint(connecting_to_ip,port,PolicyPort);
+ }
+
+ static public bool CheckSocketEndPoint(string connecting_to_ip, int port, int policyport)
+ {
+ var policy = FlashCrossDomainPolicyFor(connecting_to_ip, policyport, 3000);
+ return policy.IsSocketConnectionAllowed(port);
+ }
+
+ public static FlashCrossDomainPolicy FlashCrossDomainPolicyFor(string connecting_to_ip, int policyport, int timeout)
+ {
+ FlashCrossDomainPolicy cachedPolicy;
+ if (SocketPoliciesByIp.TryGetValue(connecting_to_ip, out cachedPolicy))
+ {
+ Log.Msg(String.Format("Policy for host {0} found in the cache.", connecting_to_ip));
+ return cachedPolicy;
+ }
+
+ try
+ {
+ FlashCrossDomainPolicy policy = RetrieveFlashCrossDomainPolicyFrom(connecting_to_ip, policyport,timeout);
+ policy.PolicyPort = policyport;
+ SocketPoliciesByIp.Add(connecting_to_ip, policy);
+ return policy;
+ }
+ catch (Exception ex)
+ {
+ Log.Msg(String.Format("{0} caught an exception while checking endpoint {1}: {2}", typeof(CrossDomainPolicyManager).Name, connecting_to_ip, ex));
+ return FlashCrossDomainPolicy.DenyPolicy;
+ }
+ }
+
+ [SecuritySafeCritical]
+ private static FlashCrossDomainPolicy RetrieveFlashCrossDomainPolicyFrom(string host, int port, int timeout)
+ {
+ var type = Type.GetType("System.Net.Sockets.SocketPolicyClient, System");
+ var stream = (Stream)type.InvokeMember("GetPolicyStreamForIP", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic, null, null, new object[] { host, port, timeout });
+
+ if (stream == null) throw new Exception("got back null stream from getpolicystream");
+ return FlashCrossDomainPolicy.FromStream(stream);
+ }
+ }
+}
+
+#endif
+
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/FlashCrossDomainPolicy.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/FlashCrossDomainPolicy.cs
new file mode 100644
index 0000000..cbeb571
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/FlashCrossDomainPolicy.cs
@@ -0,0 +1,213 @@
+//
+// FlashCrossDomainPolicy.cs
+//
+// Author:
+// Atsushi Enomoto <atsushi@ximian.com>
+// Moonlight List (moonlight-list@lists.ximian.com)
+//
+// Copyright (C) 2009 Novell, Inc. http://www.novell.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#define NET_2_1
+#if NET_2_1
+
+using System;
+using MonoForks.System;
+using System.Collections.Generic;
+using System.IO;
+using MonoForks.System.Net;
+using UnityEngine;
+
+namespace MonoForks.System.Windows.Browser.Net {
+
+ partial class FlashCrossDomainPolicy : BaseDomainPolicy {
+
+ private string site_control;
+ public int PolicyPort { get; set; }
+
+ public FlashCrossDomainPolicy ()
+ {
+ AllowedAccesses = new List<AllowAccessFrom> ();
+ AllowedHttpRequestHeaders = new List<AllowHttpRequestHeadersFrom> ();
+ PolicyPort = 843;
+ }
+
+ public static FlashCrossDomainPolicy DenyPolicy = new FlashCrossDomainPolicy();
+
+ public List<AllowAccessFrom> AllowedAccesses { get; private set; }
+ public List<AllowHttpRequestHeadersFrom> AllowedHttpRequestHeaders { get; private set; }
+
+ public string SiteControl {
+ get { return String.IsNullOrEmpty (site_control) ? "all" : site_control; }
+ set { site_control = value; }
+ }
+
+ public bool IsSocketConnectionAllowed(int port)
+ {
+ foreach(var allowed in AllowedAccesses)
+ {
+ if (allowed.IsSocketConnectionAllowed (port, PolicyPort))
+ return true;
+ }
+ return false;
+ }
+
+ public override bool IsAllowed (Uri uri, string [] headerKeys)
+ {
+ switch (SiteControl) {
+ case "all":
+ case "master-only":
+ case "by-ftp-filename":
+ break;
+ default:
+ // others, e.g. 'none', are not supported/accepted
+ Log.Msg("rejected because SiteControl does not have a valid value");
+ return false;
+ }
+ bool any = false;
+ if (AllowedAccesses.Count > 0)
+ {
+ foreach (var a in AllowedAccesses)
+ {
+ if (a.IsAllowed(uri, headerKeys))
+ {
+ any = true;
+ }
+ }
+ }
+ if (!any)
+ {
+ Log.Msg("Rejected because there was no AllowedAcces entry in the crossdomain file allowing this request.");
+ return false;
+ }
+
+ if (AllowedHttpRequestHeaders.Count > 0)
+ foreach(var h in AllowedHttpRequestHeaders)
+ if (h.IsRejected(uri,headerKeys)) return false;
+
+ return true;
+ }
+
+ public class AllowAccessFrom {
+
+ public AllowAccessFrom ()
+ {
+ Secure = true; // true by default
+ }
+
+ public string Domain { get; set; }
+ public bool AllowAnyPort { get; set; }
+ public int [] ToPorts { get; set; }
+ public bool Secure { get; set; }
+
+ public bool IsAllowed (Uri uri, string [] headerKeys)
+ {
+ Log.Msg("Checking if "+uri+" is a valid domain");
+ if (!CheckDomain(uri)) return false;
+
+ if (!AllowAnyPort && ToPorts != null && Array.IndexOf(ToPorts, uri.Port) < 0)
+ {
+ Log.Msg("requested port: "+uri.Port+" is not allowed by specified portrange");
+ return false;
+ }
+
+ // if Secure is false then it allows applications from HTTP to download data from HTTPS servers
+ if (!Secure)
+ return true;
+ // if Secure is true then only application on HTTPS servers can access data on HTTPS servers
+ if (ApplicationUri.Scheme == Uri.UriSchemeHttps)
+ return (uri.Scheme == Uri.UriSchemeHttps);
+ // otherwise FILE/HTTP applications can access HTTP uris
+
+ Log.Msg("All requirements met, the request is approved");
+ return true;
+ }
+
+ public bool IsSocketConnectionAllowed(int port, int policyport)
+ {
+ if (policyport>1024 && port<1024) return false;
+
+ bool portok = false;
+
+ if (AllowAnyPort) portok = true;
+ if (ToPorts != null)
+ {
+ foreach (int allowedport in ToPorts)
+ {
+ if (allowedport == port)
+ portok = true;
+ }
+ if (!portok) return false;
+ }
+ //for now we only support socket policies that say all domains are fine.
+ return (Domain == "*");
+ }
+
+ bool CheckDomain(Uri uri)
+ {
+ Log.Msg("Checking request-host: "+uri.Host+" against valid domain: "+Domain);
+ if (Domain == "*") return true;
+ if (ApplicationUri.Host == Domain) return true;
+
+ if (Domain[0] != '*') return false;
+ string match = Domain.Substring(1, Domain.Length - 1);
+ if (uri.Host.EndsWith(match)) return true;
+
+ return false;
+ }
+ }
+
+ public class AllowHttpRequestHeadersFrom {
+
+ public AllowHttpRequestHeadersFrom ()
+ {
+ Headers = new Headers ();
+ }
+
+ public string Domain { get; set; }
+ public bool AllowAllHeaders { get; set; }
+ public Headers Headers { get; private set; }
+ public bool Secure { get; set; }
+
+ public bool IsRejected (Uri uri, string [] headerKeys)
+ {
+ // "A Flash policy file must allow access to all domains to be used by the Silverlight runtime."
+ // http://msdn.microsoft.com/en-us/library/cc645032(VS.95).aspx
+ //if (Domain != "*")
+ // return false;
+
+ if (Headers.IsAllowed (headerKeys))
+ return false;
+
+ // if Secure is false then it allows applications from HTTP to download data from HTTPS servers
+ if (!Secure)
+ return true;
+ // if Secure is true then only application on HTTPS servers can access data on HTTPS servers
+ if (ApplicationUri.Scheme == Uri.UriSchemeHttps)
+ return (uri.Scheme == Uri.UriSchemeHttps);
+ // otherwise FILE/HTTP applications can access HTTP uris
+ return true;
+ }
+ }
+ }
+}
+
+#endif
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/FlashCrossDomainPolicyParser.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/FlashCrossDomainPolicyParser.cs
new file mode 100644
index 0000000..980089a
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/FlashCrossDomainPolicyParser.cs
@@ -0,0 +1,265 @@
+//
+// FlashCrossDomainPolicyParser.cs
+//
+// Author:
+// Atsushi Enomoto <atsushi@ximian.com>
+// Moonlight List (moonlight-list@lists.ximian.com)
+//
+// Copyright (C) 2009 Novell, Inc. http://www.novell.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#define NET_2_1
+#if NET_2_1
+
+using System;
+using System.IO;
+using System.Collections;
+using System.Text;
+using MonoForks.Mono.Xml;
+
+/*
+
+Specification: http://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html
+
+# This grammar is based on the xsd from Adobe, but the schema is wrong.
+# It should have used interleave (all). Some crossdomain.xml are invalidated.
+# (For example, try mono-xmltool --validate-xsd http://www.adobe.com/xml/schemas/PolicyFile.xsd http://twitter.com/crossdomain.xml)
+
+default namespace = ""
+
+grammar {
+
+start = cross-domain-policy
+
+cross-domain-policy = element cross-domain-policy {
+ element site-control {
+ attribute permitted-cross-domain-policies {
+ "all" | "by-contract-type" | "by-ftp-filename" | "master-only" | "none"
+ }
+ }?,
+ element allow-access-from {
+ attribute domain { text },
+ attribute to-ports { text }?,
+ attribute secure { xs:boolean }?
+ }*,
+ element allow-http-request-headers-from {
+ attribute domain { text },
+ attribute headers { text },
+ attribute secure { xs:boolean }?
+ }*,
+ element allow-access-from-identity {
+ element signatory {
+ element certificate {
+ attribute fingerprint { text },
+ attribute fingerprint-algorithm { text }
+ }
+ }
+ }*
+}
+
+}
+
+*/
+
+namespace MonoForks.System.Windows.Browser.Net
+{
+
+ partial class FlashCrossDomainPolicy {
+
+ static public bool ReadBooleanAttribute(MiniParser.IAttrList attrs, string attribute)
+ {
+ switch (attrs.GetValue(attribute))
+ {
+ case null:
+ case "true":
+ return true;
+ case "false":
+ return false;
+ default:
+ throw new Exception("Invalid boolean attribute: " + attribute);
+ }
+ }
+ class Reader : MiniParser.IReader
+ {
+ public Reader(Stream stream)
+ {
+ this.stream = stream;
+ }
+ public int Read()
+ {
+ return stream.ReadByte();
+ }
+
+ Stream stream;
+ }
+
+ static public FlashCrossDomainPolicy FromStream(Stream originalStream)
+ {
+ Log.Msg("received policy");
+
+ var stream = StripTrailing0(originalStream);
+ if (stream.Length == 0)
+ throw new ArgumentException("Policy can't be constructed from empty stream.");
+
+ FlashCrossDomainPolicy cdp = new FlashCrossDomainPolicy ();
+ Handler handler = new Handler(cdp);
+ Reader r = new Reader(stream);
+ MiniParser p = new MiniParser();
+ p.Parse(r, handler);
+
+ // if none supplied set a default for headers
+ if (cdp.AllowedHttpRequestHeaders.Count == 0)
+ {
+ var h = new AllowHttpRequestHeadersFrom() { Domain = "*", Secure = true };
+ h.Headers.SetHeaders(null); // defaults
+ cdp.AllowedHttpRequestHeaders.Add(h);
+ }
+ Log.Msg("done parsing policy");
+ return cdp;
+ }
+
+ private static MemoryStream StripTrailing0(Stream stream)
+ {
+ //crazy inefficient, but only happens on very small streams.
+ var dupe = new MemoryStream();
+ while(true)
+ {
+ var b = stream.ReadByte();
+ if (b==0 || b==-1) break;
+ dupe.WriteByte((byte)b);
+ }
+ dupe.Seek(0, SeekOrigin.Begin);
+ return dupe;
+ }
+
+ class Handler : MiniParser.IHandler
+ {
+ public Handler(FlashCrossDomainPolicy cdp)
+ {
+ this.cdp = cdp;
+ }
+
+
+ // IContentHandler
+
+ private string current = null;
+ private Stack stack = new Stack();
+ FlashCrossDomainPolicy cdp;
+
+ public void OnStartElement(string name, MiniParser.IAttrList attrs)
+ {
+ Log.Msg("Parsing: "+name);
+ if (current == null)
+ {
+ if (name != "cross-domain-policy") throw new Exception("Expected root element to be <cross-domain-policy>. found "+name+" instead");
+ }
+ else
+ {
+ if (current == "cross-domain-policy")
+ {
+ switch (name)
+ {
+ case "site-control":
+ cdp.SiteControl = attrs.GetValue("permitted-cross-domain-policies");
+ break;
+ case "allow-access-from":
+ var a = new AllowAccessFrom()
+ {
+ Domain = attrs.GetValue("domain"),
+ Secure = ReadBooleanAttribute(attrs, "secure")
+ };
+
+ var p = attrs.GetValue ("to-ports");
+ if (p == "*")
+ a.AllowAnyPort = true;
+ else if (p != null)
+ {
+ var ports = new ArrayList();
+ string[] ports_string = p.Split (',');
+ foreach(string portstring in ports_string)
+ {
+ if (portstring.Contains("-"))
+ {
+ string[] s = portstring.Split('-');
+ if (s.Length != 2) continue;
+ int startport;
+ int endport;
+ if (!Int32.TryParse(s[0], out startport)) continue;
+ if (!Int32.TryParse(s[1], out endport)) continue;
+ for (int i = startport; i != endport; i++)
+ {
+ ports.Add(i);
+ }
+ }
+ else
+ {
+ int port;
+ if (Int32.TryParse(portstring, out port))
+ ports.Add(port);
+ }
+ }
+ a.ToPorts = (int[]) ports.ToArray(typeof(int));
+ }
+ cdp.AllowedAccesses.Add(a);
+ break;
+ case "allow-http-request-headers-from":
+ var h = new AllowHttpRequestHeadersFrom()
+ {
+ Domain = attrs.GetValue("domain"),
+ Secure = ReadBooleanAttribute(attrs, "secure")
+ };
+ h.Headers.SetHeaders(attrs.GetValue("headers"));
+ cdp.AllowedHttpRequestHeaders.Add(h);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ throw new Exception("Invalid element " + name);
+ }
+ }
+ stack.Push(name);
+ current = name;
+ Log.Msg(name);
+ // attributes
+ int n = attrs.Length;
+ for (int i = 0; i < n; i++)
+ Log.Msg(" " + attrs.GetName(i) + ": " + attrs.GetValue(i));
+ }
+
+ public void OnEndElement(string name)
+ {
+ stack.Pop();
+ current = stack.Count>0 ? (string)stack.Peek() : null;
+ }
+
+ public void OnStartParsing(MiniParser parser) { }
+ public void OnChars(string ch) { }
+ public void OnEndParsing(MiniParser parser) { }
+ }
+ }
+}
+
+#endif
+
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/ICrossDomainPolicy.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/ICrossDomainPolicy.cs
new file mode 100644
index 0000000..67c846f
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/ICrossDomainPolicy.cs
@@ -0,0 +1,42 @@
+//
+// System.Windows.Browser.Net.ICrossDomainPolicy interface
+//
+// Contact:
+// Moonlight List (moonlight-list@lists.ximian.com)
+//
+// Copyright (C) 2009 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#define NET_2_1
+#if NET_2_1
+
+using MonoForks.System.Net;
+
+namespace MonoForks.System.Windows.Browser.Net {
+
+ interface ICrossDomainPolicy {
+
+ bool IsAllowed (WebRequest request);
+ }
+}
+
+#endif
+
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/IPAddress.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/IPAddress.cs
new file mode 100644
index 0000000..61b254f
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/IPAddress.cs
@@ -0,0 +1,503 @@
+//
+// System.Net.IPAddress.cs
+//
+// Author:
+// Miguel de Icaza (miguel@ximian.com)
+// Lawrence Pit (loz@cable.a2000.nl)
+//
+// (C) Ximian, Inc. http://www.ximian.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Globalization;
+using MonoForks.System;
+using MonoForks.System.Net;
+using MonoForks.System.Net.Sockets;
+using System.Runtime.InteropServices;
+
+namespace MonoForks.System.Net {
+
+ /// <remarks>
+ /// Encapsulates an IP Address.
+ /// </remarks>
+ [Serializable]
+ public class IPAddress {
+ // Don't change the name of this field without also
+ // changing socket-io.c in the runtime
+ // The IP address is stored in little-endian order inside the int,
+ // meaning the lower order bytes contain the netid
+ private long m_Address;
+ private AddressFamily m_Family;
+ private ushort[] m_Numbers; /// ip6 Stored in network order (as ip4)
+ private long m_ScopeId;
+
+ public static readonly IPAddress Any = new IPAddress(0);
+ public static readonly IPAddress Broadcast = IPAddress.Parse ("255.255.255.255");
+ public static readonly IPAddress Loopback = IPAddress.Parse ("127.0.0.1");
+ public static readonly IPAddress None = IPAddress.Parse ("255.255.255.255");
+ public static readonly IPAddress IPv6Any = IPAddress.ParseIPV6 ("::");
+ public static readonly IPAddress IPv6Loopback = IPAddress.ParseIPV6 ("::1");
+ public static readonly IPAddress IPv6None = IPAddress.ParseIPV6 ("::");
+
+ private static short SwapShort (short number)
+ {
+ return (short) ( ((number >> 8) & 0xFF) | ((number << 8) & 0xFF00) );
+ }
+
+ private static int SwapInt (int number)
+ {
+ return (((number >> 24) & 0xFF)
+ | ((number >> 08) & 0xFF00)
+ | ((number << 08) & 0xFF0000)
+ | ((number << 24)));
+ }
+
+ private static long SwapLong(long number)
+ {
+ return (((number >> 56) & 0xFF)
+ | ((number >> 40) & 0xFF00)
+ | ((number >> 24) & 0xFF0000)
+ | ((number >> 08) & 0xFF000000)
+ | ((number << 08) & 0xFF00000000)
+ | ((number << 24) & 0xFF0000000000)
+ | ((number << 40) & 0xFF000000000000)
+ | ((number << 56)));
+ }
+
+ public static short HostToNetworkOrder(short host) {
+ if (!BitConverter.IsLittleEndian)
+ return(host);
+
+ return SwapShort (host);
+ }
+
+ public static int HostToNetworkOrder(int host) {
+ if (!BitConverter.IsLittleEndian)
+ return(host);
+
+ return SwapInt (host);
+ }
+
+ public static long HostToNetworkOrder(long host) {
+ if (!BitConverter.IsLittleEndian)
+ return(host);
+
+ return SwapLong (host);
+ }
+
+ public static short NetworkToHostOrder(short network) {
+ if (!BitConverter.IsLittleEndian)
+ return(network);
+
+ return SwapShort (network);
+ }
+
+ public static int NetworkToHostOrder(int network) {
+ if (!BitConverter.IsLittleEndian)
+ return(network);
+
+ return SwapInt (network);
+ }
+
+ public static long NetworkToHostOrder(long network) {
+ if (!BitConverter.IsLittleEndian)
+ return(network);
+
+ return SwapLong (network);
+ }
+
+ /// <summary>
+ /// Constructor from a 32-bit constant with the address bytes in
+ /// little-endian order (the lower order bytes contain the netid)
+ /// </summary>
+ public IPAddress (long addr)
+ {
+ m_Address = addr;
+ m_Family = AddressFamily.InterNetwork;
+ }
+
+ public IPAddress (byte[] address)
+ {
+ if (address == null)
+ throw new ArgumentNullException ("address");
+
+ int len = address.Length;
+
+#if NET_2_0
+ if (len != 16 && len != 4)
+ throw new ArgumentException ("An invalid IP address was specified.",
+ "address");
+#else
+ if (len != 16)
+ throw new ArgumentException ("address");
+#endif
+
+ if (len == 16) {
+ m_Numbers = new ushort [8];
+ Buffer.BlockCopy(address, 0, m_Numbers, 0, 16);
+ m_Family = AddressFamily.InterNetworkV6;
+ m_ScopeId = 0;
+ } else {
+ m_Address = ((uint) address [3] << 24) + (address [2] << 16) +
+ (address [1] << 8) + address [0];
+ m_Family = AddressFamily.InterNetwork;
+ }
+ }
+
+ public IPAddress(byte[] address, long scopeId)
+ {
+ if (address == null)
+ throw new ArgumentNullException ("address");
+
+ if (address.Length != 16)
+#if NET_2_0
+ throw new ArgumentException ("An invalid IP address was specified.",
+ "address");
+#else
+ throw new ArgumentException("address");
+#endif
+
+ m_Numbers = new ushort [8];
+ Buffer.BlockCopy(address, 0, m_Numbers, 0, 16);
+ m_Family = AddressFamily.InterNetworkV6;
+ m_ScopeId = scopeId;
+ }
+
+ internal IPAddress(ushort[] address, long scopeId)
+ {
+ m_Numbers = address;
+
+ for(int i=0; i<8; i++)
+ m_Numbers[i] = (ushort)HostToNetworkOrder((short)m_Numbers[i]);
+
+ m_Family = AddressFamily.InterNetworkV6;
+ m_ScopeId = scopeId;
+ }
+
+ public static IPAddress Parse (string ipString)
+ {
+ IPAddress ret;
+ if (TryParse (ipString, out ret))
+ return ret;
+ throw new FormatException ("An invalid IP address was specified.");
+ }
+
+#if NET_2_0
+ public
+#else
+ internal
+#endif
+ static bool TryParse (string ipString, out IPAddress address)
+ {
+ if (ipString == null)
+ throw new ArgumentNullException ("ipString");
+
+ if ((address = ParseIPV4 (ipString)) == null)
+ if ((address = ParseIPV6 (ipString)) == null)
+ return false;
+ return true;
+ }
+
+ private static IPAddress ParseIPV4 (string ip)
+ {
+#if ONLY_1_1
+ if (ip.Length == 0 || ip == " ")
+ return new IPAddress (0);
+#endif
+
+ int pos = ip.IndexOf (' ');
+ if (pos != -1) {
+#if NET_2_0
+ string [] nets = ip.Substring (pos + 1).Split (new char [] {'.'});
+ if (nets.Length > 0) {
+ string lastNet = nets [nets.Length - 1];
+ if (lastNet.Length == 0)
+ return null;
+#if NET_2_1 //workaround for smcs, as it generate code that can't access string.GetEnumerator ()
+ foreach (char c in lastNet.ToCharArray ())
+#else
+ foreach (char c in lastNet)
+#endif
+ if (!Uri.IsHexDigit (c))
+ return null;
+ }
+#endif
+ ip = ip.Substring (0, pos);
+ }
+
+ if (ip.Length == 0 || ip [ip.Length - 1] == '.')
+ return null;
+
+ string [] ips = ip.Split (new char [] {'.'});
+ if (ips.Length > 4)
+ return null;
+
+ // Make the number in network order
+ try {
+ long a = 0;
+ long val = 0;
+ for (int i = 0; i < ips.Length; i++) {
+ string subnet = ips [i];
+ if ((3 <= subnet.Length && subnet.Length <= 4) &&
+ (subnet [0] == '0') && (subnet [1] == 'x' || subnet [1] == 'X')) {
+ if (subnet.Length == 3)
+ val = (byte) Uri.FromHex (subnet [2]);
+ else
+ val = (byte) ((Uri.FromHex (subnet [2]) << 4) | Uri.FromHex (subnet [3]));
+ } else if (subnet.Length == 0)
+ return null;
+ else if (subnet [0] == '0') {
+ // octal
+ val = 0;
+ for (int j = 1; j < subnet.Length; j++) {
+ if ('0' <= subnet [j] && subnet [j] <= '7')
+ val = (val << 3) + subnet [j] - '0';
+ else
+ return null;
+ }
+ }
+ else {
+#if NET_2_0
+ if (!Int64.TryParse (subnet, NumberStyles.None, null, out val))
+ return null;
+#else
+ val = long.Parse (subnet, NumberStyles.None);
+#endif
+ }
+
+ if (i == (ips.Length - 1))
+ i = 3;
+ else if (val > 0xFF)
+ return null; // e.g. 256.0.0.1
+ for (int j = 0; val > 0; j++, val /= 0x100)
+ a |= (val & 0xFF) << ((i - j) << 3);
+ }
+
+ return (new IPAddress (a));
+ } catch (Exception) {
+ return null;
+ }
+ }
+
+ private static IPAddress ParseIPV6 (string ip)
+ {
+ IPv6Address newIPv6Address;
+
+ if (IPv6Address.TryParse(ip, out newIPv6Address))
+ return new IPAddress (newIPv6Address.Address, newIPv6Address.ScopeId);
+ return null;
+ }
+
+ [Obsolete("This property is obsolete. Use GetAddressBytes.")]
+ public long Address
+ {
+ get {
+ if(m_Family != AddressFamily.InterNetwork)
+ throw new Exception("The attempted operation is not supported for the type of object referenced");
+
+ return m_Address;
+ }
+ set {
+ /* no need to do this test, ms.net accepts any value.
+ if (value < 0 || value > 0x00000000FFFFFFFF)
+ throw new ArgumentOutOfRangeException (
+ "the address must be between 0 and 0xFFFFFFFF");
+ */
+
+ if(m_Family != AddressFamily.InterNetwork)
+ throw new Exception("The attempted operation is not supported for the type of object referenced");
+
+ m_Address = value;
+ }
+ }
+
+ internal long InternalIPv4Address {
+ get { return m_Address; }
+ }
+
+#if NET_2_0
+ public bool IsIPv6LinkLocal {
+ get {
+ if (m_Family == AddressFamily.InterNetwork)
+ return false;
+ int v = NetworkToHostOrder ((short) m_Numbers [0]) & 0xFFF0;
+ return 0xFE80 <= v && v < 0xFEC0;
+ }
+ }
+
+ public bool IsIPv6SiteLocal {
+ get {
+ if (m_Family == AddressFamily.InterNetwork)
+ return false;
+ int v = NetworkToHostOrder ((short) m_Numbers [0]) & 0xFFF0;
+ return 0xFEC0 <= v && v < 0xFF00;
+ }
+ }
+
+ public bool IsIPv6Multicast {
+ get {
+ return m_Family != AddressFamily.InterNetwork &&
+ ((ushort) NetworkToHostOrder ((short) m_Numbers [0]) & 0xFF00) == 0xFF00;
+ }
+ }
+#endif
+
+ public long ScopeId {
+ get {
+ if(m_Family != AddressFamily.InterNetworkV6)
+ throw new Exception("The attempted operation is not supported for the type of object referenced");
+
+ return m_ScopeId;
+ }
+ set {
+ if(m_Family != AddressFamily.InterNetworkV6)
+ throw new Exception("The attempted operation is not supported for the type of object referenced");
+
+ m_ScopeId = value;
+ }
+ }
+
+ public byte [] GetAddressBytes ()
+ {
+ if(m_Family == AddressFamily.InterNetworkV6) {
+ byte [] addressBytes = new byte [16];
+ Buffer.BlockCopy (m_Numbers, 0, addressBytes, 0, 16);
+ return addressBytes;
+ } else {
+ return new byte [4] { (byte)(m_Address & 0xFF),
+ (byte)((m_Address >> 8) & 0xFF),
+ (byte)((m_Address >> 16) & 0xFF),
+ (byte)(m_Address >> 24) };
+ }
+ }
+
+ public AddressFamily AddressFamily
+ {
+ get {
+ return m_Family;
+ }
+ }
+
+
+ /// <summary>
+ /// Used to tell whether an address is a loopback.
+ /// All IP addresses of the form 127.X.Y.Z, where X, Y, and Z are in
+ /// the range 0-255, are loopback addresses.
+ /// </summary>
+ /// <param name="addr">Address to compare</param>
+ /// <returns></returns>
+ public static bool IsLoopback (IPAddress addr)
+ {
+ if(addr.m_Family == AddressFamily.InterNetwork)
+ return (addr.m_Address & 0xFF) == 127;
+ else {
+ for(int i=0; i<6; i++) {
+ if(addr.m_Numbers[i] != 0)
+ return false;
+ }
+
+ return NetworkToHostOrder((short)addr.m_Numbers[7]) == 1;
+ }
+ }
+
+ /// <summary>
+ /// Overrides System.Object.ToString to return
+ /// this object rendered in a quad-dotted notation
+ /// </summary>
+ public override string ToString ()
+ {
+ if(m_Family == AddressFamily.InterNetwork)
+ return ToString (m_Address);
+ else
+ {
+ ushort[] numbers = m_Numbers.Clone() as ushort[];
+
+ for(int i=0; i<numbers.Length; i++)
+ numbers[i] = (ushort)NetworkToHostOrder((short)numbers[i]);
+
+ IPv6Address v6 = new IPv6Address(numbers);
+ v6.ScopeId = ScopeId;
+ return v6.ToString();
+ }
+ }
+
+ /// <summary>
+ /// Returns this object rendered in a quad-dotted notation
+ /// </summary>
+ static string ToString (long addr)
+ {
+ // addr is in network order
+ return (addr & 0xff).ToString () + "." +
+ ((addr >> 8) & 0xff).ToString () + "." +
+ ((addr >> 16) & 0xff).ToString () + "." +
+ ((addr >> 24) & 0xff).ToString ();
+ }
+
+ /// <returns>
+ /// Whether both objects are equal.
+ /// </returns>
+ public override bool Equals (object other)
+ {
+ if (other is System.Net.IPAddress){
+ IPAddress otherAddr = other as IPAddress;
+
+ if(AddressFamily != otherAddr.AddressFamily)
+ return false;
+
+ if(AddressFamily == AddressFamily.InterNetwork) {
+ return m_Address == otherAddr.m_Address;
+ } else {
+ ushort[] vals = otherAddr.m_Numbers;
+
+ for(int i=0; i<8; i++)
+ if(m_Numbers[i] != vals[i])
+ return false;
+
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public override int GetHashCode ()
+ {
+ if(m_Family == AddressFamily.InterNetwork)
+ return (int)m_Address;
+ else
+ return Hash (((((int) m_Numbers[0]) << 16) + m_Numbers [1]),
+ ((((int) m_Numbers [2]) << 16) + m_Numbers [3]),
+ ((((int) m_Numbers [4]) << 16) + m_Numbers [5]),
+ ((((int) m_Numbers [6]) << 16) + m_Numbers [7]));
+ }
+
+ private static int Hash (int i, int j, int k, int l)
+ {
+ return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25);
+ }
+
+#pragma warning disable 169
+ // Added for serialization compatibility with MS.NET
+ private int m_HashCode;
+#pragma warning restore
+
+ }
+}
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/IPv6Address.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/IPv6Address.cs
new file mode 100644
index 0000000..4d9e7d3
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/IPv6Address.cs
@@ -0,0 +1,478 @@
+//
+// System.Net.IPv6Address.cs
+//
+// Author:
+// Lawrence Pit (loz@cable.a2000.nl)
+//
+// Note I: This class is not defined in the specs of .Net
+//
+// Note II : The name of this class is perhaps unfortunate as it turns
+// out that in ms.net there's an internal class called
+// IPv6Address in namespace System.
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using System.Globalization;
+using MonoForks.System;
+using MonoForks.System.Net;
+using MonoForks.System.Net.Sockets;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace MonoForks.System.Net {
+
+ /// <remarks>
+ /// Encapsulates an IPv6 Address.
+ /// See RFC 2373 for more info on IPv6 addresses.
+ /// </remarks>
+ [Serializable]
+ internal class IPv6Address {
+ private ushort [] address;
+ private int prefixLength;
+ private long scopeId = 0;
+
+ public static readonly IPv6Address Loopback = IPv6Address.Parse ("::1");
+ public static readonly IPv6Address Unspecified = IPv6Address.Parse ("::");
+
+ public IPv6Address (ushort [] addr)
+ {
+ if (addr == null)
+ throw new ArgumentNullException ("addr");
+ if (addr.Length != 8)
+ throw new ArgumentException ("addr");
+ address = addr;
+ }
+
+ public IPv6Address (ushort [] addr, int prefixLength) : this (addr)
+ {
+ if (prefixLength < 0 || prefixLength > 128)
+ throw new ArgumentException ("prefixLength");
+ this.prefixLength = prefixLength;
+ }
+
+ public IPv6Address (ushort [] addr, int prefixLength, int scopeId) : this (addr, prefixLength)
+ {
+ this.scopeId = scopeId;
+ }
+
+ public static IPv6Address Parse (string ipString)
+ {
+ if (ipString == null)
+ throw new ArgumentNullException ("ipString");
+
+ IPv6Address result;
+ if (TryParse (ipString, out result))
+ return result;
+ throw new FormatException ("Not a valid IPv6 address");
+ }
+
+ static int Fill (ushort [] addr, string ipString)
+ {
+ int p = 0;
+ int slot = 0;
+
+ if (ipString.Length == 0)
+ return 0;
+
+ // Catch double uses of ::
+ if (ipString.IndexOf ("::") != -1)
+ return -1;
+
+ for (int i = 0; i < ipString.Length; i++){
+ char c = ipString [i];
+ int n;
+
+ if (c == ':'){
+ // Trailing : is not allowed.
+ if (i == ipString.Length-1)
+ return -1;
+
+ if (slot == 8)
+ return -1;
+
+ addr [slot++] = (ushort) p;
+ p = 0;
+ continue;
+ } if ('0' <= c && c <= '9')
+ n = (int) (c - '0');
+ else if ('a' <= c && c <= 'f')
+ n = (int) (c - 'a' + 10);
+ else if ('A' <= c && c <= 'F')
+ n = (int) (c - 'A' + 10);
+ else
+ return -1;
+ p = (p << 4) + n;
+ if (p > UInt16.MaxValue)
+ return -1;
+ }
+
+ if (slot == 8)
+ return -1;
+
+ addr [slot++] = (ushort) p;
+
+ return slot;
+ }
+
+ static bool TryParse (string prefix, out int res)
+ {
+#if NET_2_0
+ return Int32.TryParse (prefix, NumberStyles.Integer, CultureInfo.InvariantCulture, out res);
+#else
+ try {
+ res = Int32.Parse (prefix, NumberStyles.Integer, CultureInfo.InvariantCulture);
+ } catch (Exception) {
+ res = -1;
+ return false;
+ }
+
+ return true;
+#endif
+ }
+
+ public static bool TryParse (string ipString, out IPv6Address result)
+ {
+ result = null;
+ if (ipString == null)
+ return false;
+
+ if (ipString.Length > 2 &&
+ ipString [0] == '[' &&
+ ipString [ipString.Length - 1] == ']')
+ ipString = ipString.Substring (1, ipString.Length - 2);
+
+ if (ipString.Length < 2)
+ return false;
+
+ int prefixLen = 0;
+ int scopeId = 0;
+ int pos = ipString.LastIndexOf ('/');
+ if (pos != -1) {
+ string prefix = ipString.Substring (pos + 1);
+ if (!TryParse (prefix , out prefixLen))
+ prefixLen = -1;
+ if (prefixLen < 0 || prefixLen > 128)
+ return false;
+ ipString = ipString.Substring (0, pos);
+ } else {
+ pos = ipString.LastIndexOf ('%');
+ if (pos != -1) {
+ string prefix = ipString.Substring (pos + 1);
+ if (!TryParse (prefix, out scopeId))
+ scopeId = 0;
+ ipString = ipString.Substring (0, pos);
+ }
+ }
+
+ //
+ // At this point the prefix/suffixes have been removed
+ // and we only have to deal with the ipv4 or ipv6 addressed
+ //
+ ushort [] addr = new ushort [8];
+
+ //
+ // Is there an ipv4 address at the end?
+ //
+ bool ipv4 = false;
+ int pos2 = ipString.LastIndexOf (':');
+ if (pos2 == -1)
+ return false;
+
+ int slots = 0;
+ if (pos2 < (ipString.Length - 1)) {
+ string ipv4Str = ipString.Substring (pos2 + 1);
+ if (ipv4Str.IndexOf ('.') != -1) {
+ IPAddress ip;
+
+ if (!IPAddress.TryParse (ipv4Str, out ip))
+ return false;
+
+ long a = ip.InternalIPv4Address;
+ addr [6] = (ushort) (((int) (a & 0xff) << 8) + ((int) ((a >> 8) & 0xff)));
+ addr [7] = (ushort) (((int) ((a >> 16) & 0xff) << 8) + ((int) ((a >> 24) & 0xff)));
+ if (pos2 > 0 && ipString [pos2 - 1] == ':')
+ ipString = ipString.Substring (0, pos2 + 1);
+ else
+ ipString = ipString.Substring (0, pos2);
+ ipv4 = true;
+ slots = 2;
+ }
+ }
+
+ //
+ // Only an ipv6 block remains, either:
+ // "hexnumbers::hexnumbers", "hexnumbers::" or "hexnumbers"
+ //
+ int c = ipString.IndexOf ("::");
+ if (c != -1){
+ int right_slots = Fill (addr, ipString.Substring (c+2));
+ if (right_slots == -1){
+ return false;
+ }
+
+ if (right_slots + slots > 8){
+ return false;
+ }
+
+ int d = 8-slots-right_slots;
+ for (int i = right_slots; i > 0; i--){
+ addr [i+d-1] = addr [i-1];
+ addr [i-1] = 0;
+ }
+
+ int left_slots = Fill (addr, ipString.Substring (0, c));
+ if (left_slots == -1)
+ return false;
+
+ if (left_slots + right_slots + slots > 7)
+ return false;
+ } else {
+ if (Fill (addr, ipString) != 8-slots)
+ return false;
+ }
+
+ // Now check the results in the ipv6-address range only
+ bool ipv6 = false;
+ for (int i = 0; i < slots; i++){
+ if (addr [i] != 0 || i == 5 && addr [i] != 0xffff)
+ ipv6 = true;
+ }
+
+ // check IPv4 validity
+ if (ipv4 && !ipv6) {
+ for (int i = 0; i < 5; i++) {
+ if (addr [i] != 0)
+ return false;
+ }
+
+ if (addr [5] != 0 && addr [5] != 0xffff)
+ return false;
+ }
+
+ result = new IPv6Address (addr, prefixLen, scopeId);
+ return true;
+ }
+
+ public ushort [] Address {
+ get { return address; }
+ }
+
+ public int PrefixLength {
+ get { return this.prefixLength; }
+ }
+
+ public long ScopeId {
+ get {
+ return scopeId;
+ }
+ set {
+ scopeId = value;
+ }
+ }
+
+ public ushort this [int index] {
+ get { return address [index]; }
+ }
+
+ public AddressFamily AddressFamily {
+ get { return AddressFamily.InterNetworkV6; }
+ }
+
+ public static bool IsLoopback (IPv6Address addr)
+ {
+ if (addr.address [7] != 1)
+ return false;
+
+ int x = addr.address [6] >> 8;
+ if (x != 0x7f && x != 0)
+ return false;
+
+ for (int i = 0; i < 4; i++) {
+ if (addr.address [i] != 0)
+ return false;
+ }
+
+ if (addr.address [5] != 0 && addr.address [5] != 0xffff)
+ return false;
+
+ return true;
+ }
+
+ private static ushort SwapUShort (ushort number)
+ {
+ return (ushort) ( ((number >> 8) & 0xFF) + ((number << 8) & 0xFF00) );
+ }
+
+ // Convert the address into a format expected by the IPAddress (long) ctor
+ private int AsIPv4Int ()
+ {
+ return (SwapUShort (address [7]) << 16) + SwapUShort (address [6]);
+ }
+
+ public bool IsIPv4Compatible ()
+ {
+ for (int i = 0; i < 6; i++)
+ if (address [i] != 0)
+ return false;
+ return (AsIPv4Int () > 1);
+ }
+
+ public bool IsIPv4Mapped ()
+ {
+ for (int i = 0; i < 5; i++)
+ if (address [i] != 0)
+ return false;
+ return address [5] == 0xffff;
+ }
+
+ /// <summary>
+ /// Overrides System.Object.ToString to return
+ /// this object rendered in a canonicalized notation
+ /// </summary>
+ public override string ToString ()
+ {
+ StringBuilder s = new StringBuilder ();
+
+
+ if(IsIPv4Compatible() || IsIPv4Mapped())
+ {
+ s.Append("::");
+
+ if(IsIPv4Mapped())
+ s.Append("ffff:");
+
+ s.Append(new IPAddress( AsIPv4Int ()).ToString ());
+
+ return s.ToString ();
+ }
+ else
+ {
+ int bestChStart = -1; // Best chain start
+ int bestChLen = 0; // Best chain length
+ int currChLen = 0; // Current chain length
+
+ // Looks for the longest zero chain
+ for (int i=0; i<8; i++)
+ {
+ if (address[i] != 0)
+ {
+ if ((currChLen > bestChLen)
+ && (currChLen > 1))
+ {
+ bestChLen = currChLen;
+ bestChStart = i - currChLen;
+ }
+ currChLen = 0;
+ }
+ else
+ currChLen++;
+ }
+ if ((currChLen > bestChLen)
+ && (currChLen > 1))
+ {
+ bestChLen = currChLen;
+ bestChStart = 8 - currChLen;
+ }
+
+ // makes the string
+ if (bestChStart == 0)
+ s.Append(":");
+ for (int i=0; i<8; i++)
+ {
+ if (i == bestChStart)
+ {
+ s.Append (":");
+ i += (bestChLen - 1);
+ continue;
+ }
+ s.AppendFormat("{0:x}", address [i]);
+ if (i < 7) s.Append (':');
+ }
+ }
+ if (scopeId != 0)
+ s.Append ('%').Append (scopeId);
+ return s.ToString ();
+ }
+
+ public string ToString (bool fullLength)
+ {
+ if (!fullLength)
+ return ToString ();
+
+ StringBuilder sb = new StringBuilder ();
+ for (int i=0; i < address.Length - 1; i++) {
+ sb.AppendFormat ("{0:X4}:", address [i]);
+ }
+ sb.AppendFormat ("{0:X4}", address [address.Length - 1]);
+ return sb.ToString ();
+ }
+
+ /// <returns>
+ /// Whether both objects are equal.
+ /// </returns>
+ public override bool Equals (object other)
+ {
+ System.Net.IPv6Address ipv6 = other as System.Net.IPv6Address;
+ if (ipv6 != null) {
+ for (int i = 0; i < 8; i++)
+ if (this.address [i] != ipv6.address [i])
+ return false;
+ return true;
+ }
+
+ System.Net.IPAddress ipv4 = other as System.Net.IPAddress;
+ if (ipv4 != null) {
+ for (int i = 0; i < 5; i++)
+ if (address [i] != 0)
+ return false;
+
+ if (address [5] != 0 && address [5] != 0xffff)
+ return false;
+
+ long a = ipv4.InternalIPv4Address;
+ if (address [6] != (ushort) (((int) (a & 0xff) << 8) + ((int) ((a >> 8) & 0xff))) ||
+ address [7] != (ushort) (((int) ((a >> 16) & 0xff) << 8) + ((int) ((a >> 24) & 0xff))))
+ return false;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public override int GetHashCode ()
+ {
+ return Hash (((((int) address [0]) << 16) + address [1]),
+ ((((int) address [2]) << 16) + address [3]),
+ ((((int) address [4]) << 16) + address [5]),
+ ((((int) address [6]) << 16) + address [7]));
+ }
+
+ private static int Hash (int i, int j, int k, int l)
+ {
+ return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25);
+ }
+ }
+}
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/Locale.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/Locale.cs
new file mode 100644
index 0000000..27a4f39
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/Locale.cs
@@ -0,0 +1,14 @@
+internal class MonoTODOAttribute : System.Attribute
+{
+ public MonoTODOAttribute(string s)
+ {
+}
+}
+
+internal static class Locale
+{
+ public static string GetText(string s)
+ {
+ return s;
+ }
+}
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/MiniParser.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/MiniParser.cs
new file mode 100644
index 0000000..8934ffc
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/MiniParser.cs
@@ -0,0 +1,628 @@
+//
+// System.Security.Cryptography.MiniParser: Internal XML parser implementation
+//
+// Authors:
+// Sergey Chaban
+//
+// Copyright (c) 2001, 2002 Wild West Software
+// Copyright (c) 2002 Sergey Chaban
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Text;
+using System.Collections;
+using System.Globalization;
+
+namespace MonoForks.Mono.Xml
+{
+
+#if INSIDE_CORLIB
+ internal
+#else
+// [CLSCompliant(false)]
+ public
+#endif
+class MiniParser {
+
+ public interface IReader {
+ int Read();
+ }
+
+ public interface IAttrList {
+ int Length {get;}
+ bool IsEmpty {get;}
+ string GetName(int i);
+ string GetValue(int i);
+ string GetValue(string name);
+ void ChangeValue(string name, string newValue);
+ string[] Names {get;}
+ string[] Values {get;}
+ }
+
+ public interface IMutableAttrList : IAttrList {
+ void Clear();
+ void Add(string name, string value);
+ void CopyFrom(IAttrList attrs);
+ void Remove(int i);
+ void Remove(string name);
+ }
+
+ public interface IHandler {
+ void OnStartParsing(MiniParser parser);
+ void OnStartElement(string name, IAttrList attrs);
+ void OnEndElement(string name);
+ void OnChars(string ch);
+ void OnEndParsing(MiniParser parser);
+ }
+
+ public class HandlerAdapter : IHandler {
+ public HandlerAdapter() {}
+ public void OnStartParsing(MiniParser parser) {}
+ public void OnStartElement(string name, IAttrList attrs) {}
+ public void OnEndElement(string name) {}
+ public void OnChars(string ch) {}
+ public void OnEndParsing(MiniParser parser) {}
+ }
+
+ private enum CharKind : byte {
+ LEFT_BR = 0,
+ RIGHT_BR = 1,
+ SLASH = 2,
+ PI_MARK = 3,
+ EQ = 4,
+ AMP = 5,
+ SQUOTE = 6,
+ DQUOTE = 7,
+ BANG = 8,
+ LEFT_SQBR = 9,
+ SPACE = 0xA,
+ RIGHT_SQBR = 0xB,
+ TAB = 0xC,
+ CR = 0xD,
+ EOL = 0xE,
+ CHARS = 0xF,
+ UNKNOWN = 0x1F
+ }
+
+ private enum ActionCode : byte {
+ START_ELEM = 0,
+ END_ELEM = 1,
+ END_NAME = 2,
+ SET_ATTR_NAME = 3,
+ SET_ATTR_VAL = 4,
+ SEND_CHARS = 5,
+ START_CDATA = 6,
+ END_CDATA = 7,
+ ERROR = 8,
+ STATE_CHANGE = 9,
+ FLUSH_CHARS_STATE_CHANGE = 0xA,
+ ACC_CHARS_STATE_CHANGE = 0xB,
+ ACC_CDATA = 0xC,
+ PROC_CHAR_REF = 0xD,
+ UNKNOWN = 0xF
+ }
+
+ public class AttrListImpl : IMutableAttrList {
+ protected ArrayList names;
+ protected ArrayList values;
+
+ public AttrListImpl() : this(0) {}
+
+ public AttrListImpl(int initialCapacity) {
+ if (initialCapacity <= 0) {
+ names = new ArrayList();
+ values = new ArrayList();
+ } else {
+ names = new ArrayList(initialCapacity);
+ values = new ArrayList(initialCapacity);
+ }
+ }
+
+ public AttrListImpl(IAttrList attrs)
+ : this(attrs != null ? attrs.Length : 0) {
+ if (attrs != null) this.CopyFrom(attrs);
+ }
+
+ public int Length {
+ get {return names.Count;}
+ }
+
+ public bool IsEmpty {
+ get {return this.Length != 0;}
+ }
+
+ public string GetName(int i) {
+ string res = null;
+ if (i >= 0 && i < this.Length) {
+ res = names[i] as string;
+ }
+ return res;
+ }
+
+ public string GetValue(int i) {
+ string res = null;
+ if (i >= 0 && i < this.Length) {
+ res = values[i] as string;
+ }
+ return res;
+ }
+
+ public string GetValue(string name) {
+ return this.GetValue(names.IndexOf(name));
+ }
+
+ public void ChangeValue(string name, string newValue) {
+ int i = names.IndexOf(name);
+ if (i >= 0 && i < this.Length) {
+ values[i] = newValue;
+ }
+ }
+
+ public string[] Names {
+ get {return names.ToArray(typeof(string)) as string[];}
+ }
+
+ public string[] Values {
+ get {return values.ToArray(typeof(string)) as string[];}
+ }
+
+ public void Clear() {
+ names.Clear();
+ values.Clear();
+ }
+
+ public void Add(string name, string value) {
+ names.Add(name);
+ values.Add(value);
+ }
+
+ public void Remove(int i) {
+ if (i >= 0) {
+ names.RemoveAt(i);
+ values.RemoveAt(i);
+ }
+ }
+
+ public void Remove(string name) {
+ this.Remove(names.IndexOf(name));
+ }
+
+ public void CopyFrom(IAttrList attrs) {
+ if (attrs != null && ((object)this == (object)attrs)) {
+ this.Clear();
+ int n = attrs.Length;
+ for (int i = 0; i < n; i++) {
+ this.Add(attrs.GetName(i), attrs.GetValue(i));
+ }
+ }
+ }
+ }
+
+ public class XMLError : Exception {
+ protected string descr;
+ protected int line, column;
+ public XMLError() : this("Unknown") {}
+ public XMLError(string descr) : this(descr, -1, -1) {}
+ public XMLError(string descr, int line, int column)
+ : base(descr) {
+ this.descr = descr;
+ this.line = line;
+ this.column = column;
+ }
+ public int Line {get {return line;}}
+ public int Column {get {return column;}}
+ public override string ToString() {
+ return (String.Format("{0} @ (line = {1}, col = {2})", descr, line, column));
+ }
+ }
+
+ private static readonly int INPUT_RANGE = 13;
+ private static readonly ushort[] tbl = {
+ (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 1), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 0), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 128),
+ (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 2), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 133), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 16), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.FLUSH_CHARS_STATE_CHANGE << 8) | 4),
+ (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.END_ELEM << 8) | 0), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 2), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 2), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129),
+ (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 5), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3),
+ (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 4), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.END_NAME << 8) | 6), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.END_NAME << 8) | 7), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.END_NAME << 8) | 8), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129),
+ (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 0), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3),
+ (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 0), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ERROR << 8) | 129),
+ (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.FLUSH_CHARS_STATE_CHANGE << 8) | 1), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.PROC_CHAR_REF << 8) | 10), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 7), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10),
+ (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 9), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.START_ELEM << 8) | 6), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.START_ELEM << 8) | 7), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 8), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129),
+ (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 9), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.SET_ATTR_NAME << 8) | 11), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 12), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 130),
+ (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 13), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.PROC_CHAR_REF << 8) | 10), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10),
+ (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 11), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ERROR << 8) | 132),
+ (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.SET_ATTR_NAME << 8) | 11), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 12), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ERROR << 8) | 130),
+ (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.SEND_CHARS << 8) | 2), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 16), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ERROR << 8) | 134),
+ (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.SET_ATTR_VAL << 8) | 17), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.PROC_CHAR_REF << 8) | 14), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14),
+ (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.SET_ATTR_VAL << 8) | 17), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.PROC_CHAR_REF << 8) | 15), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15),
+ (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.START_CDATA << 8) | 18), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 0), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19),
+ (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.START_ELEM << 8) | 6), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.START_ELEM << 8) | 7), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 17), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 9), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129),
+ (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.END_CDATA << 8) | 10), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18),
+ (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19),
+ (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.UNKNOWN << 8) | 255),
+ 0xFFFF
+ };
+
+ protected static string[] errors = {
+ /* 0 */ "Expected element",
+ /* 1 */ "Invalid character in tag",
+ /* 2 */ "No '='",
+ /* 3 */ "Invalid character entity",
+ /* 4 */ "Invalid attr value",
+ /* 5 */ "Empty tag",
+ /* 6 */ "No end tag",
+ /* 7 */ "Bad entity ref"
+ };
+
+ protected int line;
+ protected int col;
+ protected int[] twoCharBuff;
+ protected bool splitCData;
+
+ public MiniParser() {
+ twoCharBuff = new int[2];
+ splitCData = false;
+ Reset();
+ }
+
+ public void Reset() {
+ line = 0;
+ col = 0;
+ }
+
+ protected static bool StrEquals(string str, StringBuilder sb, int sbStart, int len) {
+ if (len != str.Length) return false;
+ for (int i = 0; i < len; i++) {
+ if (str[i] != sb[sbStart + i]) return false;
+ }
+ return true;
+ }
+
+ protected void FatalErr(string descr) {
+ throw new XMLError(descr, this.line, this.col);
+ }
+
+ protected static int Xlat(int charCode, int state) {
+ int p = state * INPUT_RANGE;
+ int n = Math.Min(tbl.Length - p, INPUT_RANGE);
+ for (;--n >= 0;) {
+ ushort code = tbl[p];
+ if (charCode == (code >> 12)) return (code & 0xFFF);
+ p++;
+ }
+ return 0xFFF;
+ }
+
+ public void Parse(IReader reader, IHandler handler) {
+ if (reader == null) throw new ArgumentNullException("reader");
+ if (handler == null) handler = new HandlerAdapter();
+
+ AttrListImpl attrList = new AttrListImpl();
+ string lastAttrName = null;
+ Stack tagStack = new Stack();
+ string elementName = null;
+ line = 1;
+ col = 0;
+ int currCh = 0;
+ int stateCode = 0;
+ StringBuilder sbChars = new StringBuilder();
+ bool seenCData = false;
+ bool isComment = false;
+ bool isDTD = false;
+ int bracketSwitch = 0;
+
+ handler.OnStartParsing(this);
+
+ while (true) {
+ ++this.col;
+
+ currCh = reader.Read();
+
+ if (currCh == -1) {
+ if (stateCode != 0) {
+ FatalErr("Unexpected EOF");
+ }
+ break;
+ }
+
+ int charCode = "<>/?=&'\"![ ]\t\r\n".IndexOf((char)currCh) & 0xF;
+ if (charCode == (int)CharKind.CR) continue; // ignore
+ // whitepace ::= (#x20 | #x9 | #xd | #xa)+
+ if (charCode == (int)CharKind.TAB) charCode = (int)CharKind.SPACE; // tab == space
+ if (charCode == (int)CharKind.EOL) {
+ this.col = 0;
+ this.line++;
+ charCode = (int)CharKind.SPACE;
+ }
+
+ int actionCode = MiniParser.Xlat(charCode, stateCode);
+ stateCode = actionCode & 0xFF;
+ // Ignore newline inside attribute value.
+ if (currCh == '\n' && (stateCode == 0xE || stateCode == 0xF)) continue;
+ actionCode >>= 8;
+
+ if (stateCode >= 0x80) {
+ if (stateCode == 0xFF) {
+ FatalErr("State dispatch error.");
+ } else {
+ FatalErr(errors[stateCode ^ 0x80]);
+ }
+ }
+
+ switch (actionCode) {
+ case (int)ActionCode.START_ELEM:
+ handler.OnStartElement(elementName, attrList);
+ if (currCh != '/') {
+ tagStack.Push(elementName);
+ } else {
+ handler.OnEndElement(elementName);
+ }
+ attrList.Clear();
+ break;
+
+ case (int)ActionCode.END_ELEM:
+ elementName = sbChars.ToString();
+ sbChars = new StringBuilder();
+ string endName = null;
+ if (tagStack.Count == 0 ||
+ elementName != (endName = tagStack.Pop() as string)) {
+ if (endName == null) {
+ FatalErr("Tag stack underflow");
+ } else {
+ FatalErr(String.Format("Expected end tag '{0}' but found '{1}'", elementName, endName));
+ }
+ }
+ handler.OnEndElement(elementName);
+ break;
+
+ case (int)ActionCode.END_NAME:
+ elementName = sbChars.ToString();
+ sbChars = new StringBuilder();
+ if (currCh != '/' && currCh != '>') break;
+ goto case (int)ActionCode.START_ELEM;
+
+ case (int)ActionCode.SET_ATTR_NAME:
+ lastAttrName = sbChars.ToString();
+ sbChars = new StringBuilder();
+ break;
+
+ case (int)ActionCode.SET_ATTR_VAL:
+ if (lastAttrName == null) FatalErr("Internal error.");
+ attrList.Add(lastAttrName, sbChars.ToString());
+ sbChars = new StringBuilder();
+ lastAttrName = null;
+ break;
+
+ case (int)ActionCode.SEND_CHARS:
+ handler.OnChars(sbChars.ToString());
+ sbChars = new StringBuilder();
+ break;
+
+ case (int)ActionCode.START_CDATA:
+ string cdata = "CDATA[";
+ isComment = false;
+ isDTD = false;
+
+ if (currCh == '-') {
+ currCh = reader.Read();
+
+ if (currCh != '-') FatalErr("Invalid comment");
+
+ this.col++;
+ isComment = true;
+ twoCharBuff[0] = -1;
+ twoCharBuff[1] = -1;
+ } else {
+ if (currCh != '[') {
+ isDTD = true;
+ bracketSwitch = 0;
+ break;
+ }
+
+ for (int i = 0; i < cdata.Length; i++) {
+ if (reader.Read() != cdata[i]) {
+ this.col += i+1;
+ break;
+ }
+ }
+ this.col += cdata.Length;
+ seenCData = true;
+ }
+ break;
+
+ case (int)ActionCode.END_CDATA:
+ int n = 0;
+ currCh = ']';
+
+ while (currCh == ']') {
+ currCh = reader.Read();
+ n++;
+ }
+
+ if (currCh != '>') {
+ for (int i = 0; i < n; i++) sbChars.Append(']');
+ sbChars.Append((char)currCh);
+ stateCode = 0x12;
+ } else {
+ for (int i = 0; i < n-2; i++) sbChars.Append(']');
+ seenCData = false;
+ }
+
+ this.col += n;
+ break;
+
+ case (int)ActionCode.ERROR:
+ FatalErr(String.Format("Error {0}", stateCode));
+ break;
+
+ case (int)ActionCode.STATE_CHANGE:
+ break;
+
+ case (int)ActionCode.FLUSH_CHARS_STATE_CHANGE:
+ sbChars = new StringBuilder();
+ if (currCh != '<') goto case (int)ActionCode.ACC_CHARS_STATE_CHANGE;
+ break;
+
+ case (int)ActionCode.ACC_CHARS_STATE_CHANGE:
+ sbChars.Append((char)currCh);
+ break;
+
+ case (int)ActionCode.ACC_CDATA:
+ if (isComment) {
+ if (currCh == '>'
+ && twoCharBuff[0] == '-'
+ && twoCharBuff[1] == '-') {
+ isComment = false;
+ stateCode = 0;
+ } else {
+ twoCharBuff[0] = twoCharBuff[1];
+ twoCharBuff[1] = currCh;
+ }
+ } else if (isDTD) {
+ if (currCh == '<' || currCh == '>') bracketSwitch ^= 1;
+ if (currCh == '>' && bracketSwitch != 0) {
+ isDTD = false;
+ stateCode = 0;
+ }
+ } else {
+ if (this.splitCData
+ && sbChars.Length > 0
+ && seenCData) {
+ handler.OnChars(sbChars.ToString());
+ sbChars = new StringBuilder();
+ }
+ seenCData = false;
+ sbChars.Append((char)currCh);
+ }
+ break;
+
+ case (int)ActionCode.PROC_CHAR_REF:
+ currCh = reader.Read();
+ int cl = this.col + 1;
+ if (currCh == '#') { // character reference
+ int r = 10;
+ int chCode = 0;
+ int nDigits = 0;
+ currCh = reader.Read();
+ cl++;
+
+ if (currCh == 'x') {
+ currCh = reader.Read();
+ cl++;
+ r=16;
+ }
+
+ NumberStyles style = r == 16 ? NumberStyles.HexNumber : NumberStyles.Integer;
+
+ while (true) {
+ int x = -1;
+ if (Char.IsNumber((char)currCh) || "abcdef".IndexOf(Char.ToLower((char)currCh)) != -1) {
+ try {
+ x = Int32.Parse(new string((char)currCh, 1), style);
+ } catch (FormatException) {x = -1;}
+ }
+ if (x == -1) break;
+ chCode *= r;
+ chCode += x;
+ nDigits++;
+ currCh = reader.Read();
+ cl++;
+ }
+
+ if (currCh == ';' && nDigits > 0) {
+ sbChars.Append((char)chCode);
+ } else {
+ FatalErr("Bad char ref");
+ }
+ } else {
+ // entity reference
+ string entityRefChars = "aglmopqstu"; // amp | apos | quot | gt | lt
+ string entities = "&'\"><";
+
+ int pos = 0;
+ int entIdx = 0xF;
+ int predShift = 0;
+
+ int sbLen = sbChars.Length;
+
+ while (true) {
+ if (pos != 0xF) pos = entityRefChars.IndexOf((char)currCh) & 0xF;
+ if (pos == 0xF) FatalErr(errors[7]);
+ sbChars.Append((char)currCh);
+
+ int path = "\uFF35\u3F8F\u4F8F\u0F5F\uFF78\uE1F4\u2299\uEEFF\uEEFF\uFF4F"[pos];
+ int lBr = (path >> 4) & 0xF;
+ int rBr = path & 0xF;
+ int lPred = path >> 12;
+ int rPred = (path >> 8) & 0xF;
+ currCh = reader.Read();
+ cl++;
+ pos = 0xF;
+ if (lBr != 0xF && currCh == entityRefChars[lBr]) {
+ if (lPred < 0xE) entIdx = lPred;
+// pred = lPred;
+ predShift = 12; // left
+ } else if (rBr != 0xF && currCh == entityRefChars[rBr]) {
+ if (rPred < 0xE) entIdx = rPred;
+// pred = rPred;
+ predShift = 8; // right
+ } else if (currCh == ';') {
+ if (entIdx != 0xF
+ && predShift != 0
+ && ((path >> predShift) & 0xF) == 0xE) break;
+ continue; // pos == 0xF
+ }
+
+ pos=0;
+
+ }
+
+ int l = cl - this.col - 1;
+
+ if ((l > 0 && l < 5)
+ &&(StrEquals("amp", sbChars, sbLen, l)
+ || StrEquals("apos", sbChars, sbLen, l)
+ || StrEquals("quot", sbChars, sbLen, l)
+ || StrEquals("lt", sbChars, sbLen, l)
+ || StrEquals("gt", sbChars, sbLen, l))
+ ) {
+ sbChars.Length = sbLen;
+ sbChars.Append(entities[entIdx]);
+ } else FatalErr(errors[7]);
+ }
+
+ this.col = cl;
+ break;
+
+ default:
+ FatalErr(String.Format("Unexpected action code - {0}.", actionCode));
+ break;
+ }
+ } // while (true)
+
+ handler.OnEndParsing(this);
+
+ } // Parse
+
+}
+
+}
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/NoAccessPolicy.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/NoAccessPolicy.cs
new file mode 100644
index 0000000..166a1d4
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/NoAccessPolicy.cs
@@ -0,0 +1,46 @@
+//
+// System.Windows.Browser.Net.PolicyDownloadPolicy class
+//
+// Contact:
+// Moonlight List (moonlight-list@lists.ximian.com)
+//
+// Copyright (C) 2009 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#define NET_2_1
+#if NET_2_1
+
+using MonoForks.System;
+using MonoForks.System.Net;
+
+namespace MonoForks.System.Windows.Browser.Net {
+
+ sealed class NoAccessPolicy : ICrossDomainPolicy {
+
+ public bool IsAllowed (WebRequest request)
+ {
+ return false;
+ }
+ }
+}
+
+#endif
+
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/PolicyDownloadPolicy.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/PolicyDownloadPolicy.cs
new file mode 100644
index 0000000..4ea405e
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/PolicyDownloadPolicy.cs
@@ -0,0 +1,59 @@
+//
+// System.Windows.Browser.Net.PolicyDownloadPolicy class
+//
+// Contact:
+// Moonlight List (moonlight-list@lists.ximian.com)
+//
+// Copyright (C) 2009 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#define NET_2_1
+#if NET_2_1
+
+using System;
+using MonoForks.System;
+using MonoForks.System.Net;
+
+namespace MonoForks.System.Windows.Browser.Net {
+
+ sealed class PolicyDownloadPolicy : ICrossDomainPolicy {
+
+ public bool IsAllowed (WebRequest request)
+ {
+ return IsLocalPathPolicy (request.RequestUri);
+ }
+
+ static public bool IsLocalPathPolicy (Uri uri)
+ {
+ string local = uri.LocalPath;
+ if (String.CompareOrdinal (local, CrossDomainPolicyManager.ClientAccessPolicyFile) == 0)
+ return true;
+ if (String.CompareOrdinal (local, CrossDomainPolicyManager.CrossDomainFile) == 0)
+ return true;
+
+ return false;
+ }
+ }
+}
+
+#endif
+
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/SiteOfOriginPolicy.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/SiteOfOriginPolicy.cs
new file mode 100644
index 0000000..d84fbf4
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/SiteOfOriginPolicy.cs
@@ -0,0 +1,55 @@
+//
+// System.Windows.Browser.Net.SiteOfOriginPolicy class
+//
+// Contact:
+// Moonlight List (moonlight-list@lists.ximian.com)
+//
+// Copyright (C) 2009 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#define NET_2_1
+#if NET_2_1
+
+using MonoForks.System;
+using MonoForks.System.Net;
+
+namespace MonoForks.System.Windows.Browser.Net {
+
+ sealed class SiteOfOriginPolicy : ICrossDomainPolicy {
+
+ public bool IsAllowed (WebRequest request)
+ {
+ // a WebRequest to the site of origin (SOO) is always granted
+ return true;
+ }
+
+ // helper to determine if two Uri share the same origin (policy wise)
+ static public bool HasSameOrigin (Uri a, Uri b)
+ {
+ return ((a.Scheme == b.Scheme) && (a.DnsSafeHost == b.DnsSafeHost) &&
+ ((a.Port == -1) || (b.Port == -1) || (a.Port == b.Port)));
+ }
+ }
+}
+
+#endif
+
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UnityExtra.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UnityExtra.cs
new file mode 100644
index 0000000..f085b16
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UnityExtra.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using MonoForks.System.Net;
+using MonoForks.System;
+
+namespace MonoForks.System.Windows.Interop
+{
+ public class PluginHost
+ {
+ static public Uri SourceUri
+ {
+ get
+ {
+ return new Uri(UnityEngine.UnityCrossDomainHelper.GetWebSecurityHostUri());
+ }
+ }
+ static public Uri RootUri
+ {
+ get
+ {
+ return new Uri(GetRoot(SourceUri));
+ }
+ }
+
+ private static string GetRoot(Uri uri)
+ {
+ if ((uri.Scheme == "http" && uri.Port == 80) || (uri.Scheme == "https" && uri.Port == 443) || (uri.Port == -1))
+ return String.Format("{0}://{1}", uri.Scheme, uri.DnsSafeHost);
+ else
+ return String.Format("{0}://{1}:{2}", uri.Scheme, uri.DnsSafeHost, uri.Port);
+ }
+
+ }
+}
+
+namespace MonoForks.System.Net
+{
+ internal class WebRequest
+ {
+ public WebRequest(MonoForks.System.Uri requesturi, Dictionary<string,string> headers)
+ {
+ this.RequestUri = requesturi;
+ this.Headers = headers;
+ }
+ public MonoForks.System.Uri RequestUri { get; set; }
+ public Dictionary<string,string> Headers { get; set; }
+ }
+}
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/Uri.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/Uri.cs
new file mode 100644
index 0000000..0bf4011
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/Uri.cs
@@ -0,0 +1,2225 @@
+//
+// System.Uri
+//
+// Authors:
+// Lawrence Pit (loz@cable.a2000.nl)
+// Garrett Rooney (rooneg@electricjellyfish.net)
+// Ian MacLean (ianm@activestate.com)
+// Ben Maurer (bmaurer@users.sourceforge.net)
+// Atsushi Enomoto (atsushi@ximian.com)
+// Sebastien Pouliot <sebastien@ximian.com>
+// Stephane Delcroix <stephane@delcroix.org>
+//
+// (C) 2001 Garrett Rooney
+// (C) 2003 Ian MacLean
+// (C) 2003 Ben Maurer
+// Copyright (C) 2003,2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// See RFC 2396 for more info on URI's.
+//
+// TODO: optimize by parsing host string only once
+//
+//using System.ComponentModel;
+using System.IO;
+using MonoForks.System.Net;
+using System;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Collections;
+using System.Globalization;
+
+//
+// Disable warnings on Obsolete methods being used
+//
+#pragma warning disable 612
+
+namespace MonoForks.System {
+
+ [Serializable]
+#if NET_2_0
+ [TypeConverter (typeof (UriTypeConverter))]
+ public class Uri : ISerializable {
+#else
+ public class Uri : MarshalByRefObject, ISerializable {
+#endif
+ // NOTES:
+ // o scheme excludes the scheme delimiter
+ // o port is -1 to indicate no port is defined
+ // o path is empty or starts with / when scheme delimiter == "://"
+ // o query is empty or starts with ? char, escaped.
+ // o fragment is empty or starts with # char, unescaped.
+ // o all class variables are in escaped format when they are escapable,
+ // except cachedToString.
+ // o UNC is supported, as starts with "\\" for windows,
+ // or "//" with unix.
+
+ private bool isUnixFilePath;
+ private string source;
+ private string scheme = String.Empty;
+ private string host = String.Empty;
+ private int port = -1;
+ private string path = String.Empty;
+ private string query = String.Empty;
+ private string fragment = String.Empty;
+ private string userinfo = String.Empty;
+ private bool isUnc;
+ private bool isOpaquePart;
+ private bool isAbsoluteUri = true;
+
+ private string [] segments;
+
+ private bool userEscaped;
+ private string cachedAbsoluteUri;
+ private string cachedToString;
+ private string cachedLocalPath;
+ private int cachedHashCode;
+
+ private static readonly string hexUpperChars = "0123456789ABCDEF";
+
+ // Fields
+
+ public static readonly string SchemeDelimiter = "://";
+ public static readonly string UriSchemeFile = "file";
+ public static readonly string UriSchemeFtp = "ftp";
+ public static readonly string UriSchemeGopher = "gopher";
+ public static readonly string UriSchemeHttp = "http";
+ public static readonly string UriSchemeHttps = "https";
+ public static readonly string UriSchemeMailto = "mailto";
+ public static readonly string UriSchemeNews = "news";
+ public static readonly string UriSchemeNntp = "nntp";
+#if NET_2_0
+ public static readonly string UriSchemeNetPipe = "net.pipe";
+ public static readonly string UriSchemeNetTcp = "net.tcp";
+#endif
+
+ // Constructors
+
+#if NET_2_1
+ public Uri (string uriString) : this (uriString, UriKind.Absolute)
+ {
+ }
+#else
+ public Uri (string uriString) : this (uriString, false)
+ {
+ }
+#endif
+ protected Uri (SerializationInfo serializationInfo,
+ StreamingContext streamingContext) :
+ this (serializationInfo.GetString ("AbsoluteUri"), true)
+ {
+ }
+
+#if NET_2_0
+ public Uri (string uriString, UriKind uriKind)
+ {
+ source = uriString;
+ ParseUri (uriKind);
+
+ switch (uriKind) {
+ case UriKind.Absolute:
+ if (!IsAbsoluteUri)
+ throw new UriFormatException("Invalid URI: The format of the URI could not be "
+ + "determined.");
+ break;
+ case UriKind.Relative:
+ if (IsAbsoluteUri)
+ throw new UriFormatException("Invalid URI: The format of the URI could not be "
+ + "determined because the parameter 'uriString' represents an absolute URI.");
+ break;
+ case UriKind.RelativeOrAbsolute:
+ break;
+ default:
+ string msg = Locale.GetText ("Invalid UriKind value '{0}'.", uriKind);
+ throw new ArgumentException (msg);
+ }
+ }
+
+ //
+ // An exception-less constructor, returns success
+ // condition on the out parameter `success'.
+ //
+ Uri (string uriString, UriKind uriKind, out bool success)
+ {
+ if (uriString == null) {
+ success = false;
+ return;
+ }
+
+ if (uriKind != UriKind.RelativeOrAbsolute &&
+ uriKind != UriKind.Absolute &&
+ uriKind != UriKind.Relative) {
+ string msg = Locale.GetText ("Invalid UriKind value '{0}'.", uriKind);
+ throw new ArgumentException (msg);
+ }
+
+ source = uriString;
+ if (ParseNoExceptions (uriKind, uriString) != null)
+ success = false;
+ else {
+ success = true;
+
+ switch (uriKind) {
+ case UriKind.Absolute:
+ if (!IsAbsoluteUri)
+ success = false;
+ break;
+ case UriKind.Relative:
+ if (IsAbsoluteUri)
+ success = false;
+ break;
+ case UriKind.RelativeOrAbsolute:
+ break;
+ default:
+ success = false;
+ break;
+ }
+ }
+ }
+
+ public Uri (Uri baseUri, Uri relativeUri)
+ {
+ Merge (baseUri, relativeUri == null ? String.Empty : relativeUri.OriginalString);
+ // FIXME: this should call UriParser.Resolve
+ }
+
+ // note: doc says that dontEscape is always false but tests show otherwise
+ [Obsolete]
+ public Uri (string uriString, bool dontEscape)
+ {
+ userEscaped = dontEscape;
+ source = uriString;
+ ParseUri (UriKind.Absolute);
+ if (!isAbsoluteUri)
+ throw new UriFormatException("Invalid URI: The format of the URI could not be "
+ + "determined: " + uriString);
+ }
+#else
+ public Uri (string uriString, bool dontEscape)
+ {
+ userEscaped = dontEscape;
+ source = uriString;
+ Parse ();
+ if (!isAbsoluteUri)
+ throw new UriFormatException("Invalid URI: The format of the URI could not be "
+ + "determined.");
+ }
+#endif
+
+ public Uri (Uri baseUri, string relativeUri)
+ {
+ Merge (baseUri, relativeUri);
+ // FIXME: this should call UriParser.Resolve
+ }
+
+#if NET_2_0
+ [Obsolete ("dontEscape is always false")]
+#endif
+ public Uri (Uri baseUri, string relativeUri, bool dontEscape)
+ {
+ userEscaped = dontEscape;
+ Merge (baseUri, relativeUri);
+ }
+
+ private void Merge (Uri baseUri, string relativeUri)
+ {
+#if NET_2_0
+ if (baseUri == null)
+ throw new ArgumentNullException ("baseUri");
+ if (!baseUri.IsAbsoluteUri)
+ throw new ArgumentOutOfRangeException ("baseUri");
+ if (relativeUri == null)
+ relativeUri = String.Empty;
+#else
+ if (baseUri == null)
+ throw new NullReferenceException ("baseUri");
+#endif
+ // See RFC 2396 Par 5.2 and Appendix C
+
+ // Check Windows UNC (for // it is scheme/host separator)
+ if (relativeUri.Length >= 2 && relativeUri [0] == '\\' && relativeUri [1] == '\\') {
+ source = relativeUri;
+#if NET_2_0
+ ParseUri (UriKind.Absolute);
+#else
+ Parse ();
+#endif
+ return;
+ }
+
+ int pos = relativeUri.IndexOf (':');
+ if (pos != -1) {
+
+ int pos2 = relativeUri.IndexOfAny (new char [] {'/', '\\', '?'});
+
+ // pos2 < 0 ... e.g. mailto
+ // pos2 > pos ... to block ':' in query part
+ if (pos2 > pos || pos2 < 0) {
+ // in some cases, it is equivanent to new Uri (relativeUri, dontEscape):
+ // 1) when the URI scheme in the
+ // relative path is different from that
+ // of the baseUri, or
+ // 2) the URI scheme is non-standard
+ // ones (non-standard URIs are always
+ // treated as absolute here), or
+ // 3) the relative URI path is absolute.
+ if (String.CompareOrdinal (baseUri.Scheme, 0, relativeUri, 0, pos) != 0 ||
+ !IsPredefinedScheme (baseUri.Scheme) ||
+ relativeUri.Length > pos + 1 &&
+ relativeUri [pos + 1] == '/') {
+ source = relativeUri;
+#if NET_2_0
+ ParseUri (UriKind.Absolute);
+#else
+ Parse ();
+#endif
+ return;
+ }
+ else
+ relativeUri = relativeUri.Substring (pos + 1);
+ }
+ }
+
+ this.scheme = baseUri.scheme;
+ this.host = baseUri.host;
+ this.port = baseUri.port;
+ this.userinfo = baseUri.userinfo;
+ this.isUnc = baseUri.isUnc;
+ this.isUnixFilePath = baseUri.isUnixFilePath;
+ this.isOpaquePart = baseUri.isOpaquePart;
+
+ if (relativeUri == String.Empty) {
+ this.path = baseUri.path;
+ this.query = baseUri.query;
+ this.fragment = baseUri.fragment;
+ return;
+ }
+
+ // 8 fragment
+ // Note that in relative constructor, file URI cannot handle '#' as a filename character, but just regarded as a fragment identifier.
+ pos = relativeUri.IndexOf ('#');
+ if (pos != -1) {
+ if (userEscaped)
+ fragment = relativeUri.Substring (pos);
+ else
+ fragment = "#" + EscapeString (relativeUri.Substring (pos+1));
+ relativeUri = relativeUri.Substring (0, pos);
+ }
+
+ // 6 query
+ pos = relativeUri.IndexOf ('?');
+ if (pos != -1) {
+ query = relativeUri.Substring (pos);
+ if (!userEscaped)
+ query = EscapeString (query);
+ relativeUri = relativeUri.Substring (0, pos);
+ }
+
+ if (relativeUri.Length > 0 && relativeUri [0] == '/') {
+ if (relativeUri.Length > 1 && relativeUri [1] == '/') {
+ source = scheme + ':' + relativeUri;
+#if NET_2_0
+ ParseUri (UriKind.Absolute);
+#else
+ Parse ();
+#endif
+ return;
+ } else {
+ path = relativeUri;
+ if (!userEscaped)
+ path = EscapeString (path);
+ return;
+ }
+ }
+
+ // par 5.2 step 6 a)
+ path = baseUri.path;
+ if (relativeUri.Length > 0 || query.Length > 0) {
+ pos = path.LastIndexOf ('/');
+ if (pos >= 0)
+ path = path.Substring (0, pos + 1);
+ }
+
+ if(relativeUri.Length == 0)
+ return;
+
+ // 6 b)
+ path += relativeUri;
+
+ // 6 c)
+ int startIndex = 0;
+ while (true) {
+ pos = path.IndexOf ("./", startIndex);
+ if (pos == -1)
+ break;
+ if (pos == 0)
+ path = path.Remove (0, 2);
+ else if (path [pos - 1] != '.')
+ path = path.Remove (pos, 2);
+ else
+ startIndex = pos + 1;
+ }
+
+ // 6 d)
+ if (path.Length > 1 &&
+ path [path.Length - 1] == '.' &&
+ path [path.Length - 2] == '/')
+ path = path.Remove (path.Length - 1, 1);
+
+ // 6 e)
+ startIndex = 0;
+ while (true) {
+ pos = path.IndexOf ("/../", startIndex);
+ if (pos == -1)
+ break;
+ if (pos == 0) {
+ startIndex = 3;
+ continue;
+ }
+ int pos2 = path.LastIndexOf ('/', pos - 1);
+ if (pos2 == -1) {
+ startIndex = pos + 1;
+ } else {
+ if (path.Substring (pos2 + 1, pos - pos2 - 1) != "..")
+ path = path.Remove (pos2 + 1, pos - pos2 + 3);
+ else
+ startIndex = pos + 1;
+ }
+ }
+
+ // 6 f)
+ if (path.Length > 3 && path.EndsWith ("/..")) {
+ pos = path.LastIndexOf ('/', path.Length - 4);
+ if (pos != -1)
+ if (path.Substring (pos + 1, path.Length - pos - 4) != "..")
+ path = path.Remove (pos + 1, path.Length - pos - 1);
+ }
+
+ if (!userEscaped)
+ path = EscapeString (path);
+ }
+
+ // Properties
+
+ public string AbsolutePath {
+ get {
+#if NET_2_0
+ EnsureAbsoluteUri ();
+ switch (Scheme) {
+ case "mailto":
+ case "file":
+ // faster (mailto) and special (file) cases
+ return path;
+ default:
+ if (path.Length == 0) {
+ string start = Scheme + SchemeDelimiter;
+ if (path.StartsWith (start))
+ return "/";
+ else
+ return String.Empty;
+ }
+ return path;
+ }
+#else
+ return path;
+#endif
+ }
+ }
+
+ public string AbsoluteUri {
+ get {
+ EnsureAbsoluteUri ();
+ if (cachedAbsoluteUri == null) {
+ cachedAbsoluteUri = GetLeftPart (UriPartial.Path);
+ if (query.Length > 0)
+ cachedAbsoluteUri += query;
+ if (fragment.Length > 0)
+ cachedAbsoluteUri += fragment;
+ }
+ return cachedAbsoluteUri;
+ }
+ }
+
+ public string Authority {
+ get {
+ EnsureAbsoluteUri ();
+ return (GetDefaultPort (Scheme) == port)
+ ? host : host + ":" + port;
+ }
+ }
+
+ public string Fragment {
+ get {
+ EnsureAbsoluteUri ();
+ return fragment;
+ }
+ }
+
+ public string Host {
+ get {
+ EnsureAbsoluteUri ();
+ return host;
+ }
+ }
+#if !NET_2_1
+ public UriHostNameType HostNameType {
+ get {
+ EnsureAbsoluteUri ();
+ UriHostNameType ret = CheckHostName (Host);
+ if (ret != UriHostNameType.Unknown)
+ return ret;
+#if NET_2_0
+ switch (Scheme) {
+ case "mailto":
+ return UriHostNameType.Basic;
+ default:
+ return (IsFile) ? UriHostNameType.Basic : ret;
+ }
+#else
+ // looks it always returns Basic...
+ return UriHostNameType.Basic; //.Unknown;
+#endif
+ }
+ }
+
+#endif // NET_2_1
+
+ public bool IsDefaultPort {
+ get {
+ EnsureAbsoluteUri ();
+ return GetDefaultPort (Scheme) == port;
+ }
+ }
+
+ public bool IsFile {
+ get {
+ EnsureAbsoluteUri ();
+ return (Scheme == UriSchemeFile);
+ }
+ }
+
+#if !NET_2_1
+ public bool IsLoopback {
+ get {
+ EnsureAbsoluteUri ();
+
+ if (Host.Length == 0) {
+#if NET_2_0
+ return IsFile;
+#else
+ return false;
+#endif
+ }
+
+ if (host == "loopback" || host == "localhost")
+ return true;
+
+ IPAddress result;
+ if (IPAddress.TryParse (host, out result))
+ if (IPAddress.Loopback.Equals (result))
+ return true;
+
+ IPv6Address result6;
+ if (IPv6Address.TryParse (host, out result6)){
+ if (IPv6Address.IsLoopback (result6))
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+#endif // NET_2_1
+
+ public bool IsUnc {
+ // rule: This should be true only if
+ // - uri string starts from "\\", or
+ // - uri string starts from "//" (Samba way)
+ get {
+ EnsureAbsoluteUri ();
+ return isUnc;
+ }
+ }
+
+ public string LocalPath {
+ get {
+ EnsureAbsoluteUri ();
+ if (cachedLocalPath != null)
+ return cachedLocalPath;
+ if (!IsFile)
+ return AbsolutePath;
+
+ bool windows = (path.Length > 3 && path [1] == ':' &&
+ (path [2] == '\\' || path [2] == '/'));
+
+ if (!IsUnc) {
+ string p = Unescape (path);
+ bool replace = windows;
+#if ONLY_1_1
+ replace |= (System.IO.Path.DirectorySeparatorChar == '\\');
+#endif
+ if (replace)
+ cachedLocalPath = p.Replace ('/', '\\');
+ else
+ cachedLocalPath = p;
+ } else {
+ // support *nix and W32 styles
+ if (path.Length > 1 && path [1] == ':')
+ cachedLocalPath = Unescape (path.Replace (Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar));
+
+ // LAMESPEC: ok, now we cannot determine
+ // if such URI like "file://foo/bar" is
+ // Windows UNC or unix file path, so
+ // they should be handled differently.
+ else if (global::System.IO.Path.DirectorySeparatorChar == '\\') {
+ string h = host;
+ if (path.Length > 0) {
+#if NET_2_0
+ if ((path.Length > 1) || (path[0] != '/')) {
+ h += path.Replace ('/', '\\');
+ }
+#else
+ h += path.Replace ('/', '\\');
+#endif
+ }
+ cachedLocalPath = "\\\\" + Unescape (h);
+ } else
+ cachedLocalPath = Unescape (path);
+ }
+ if (cachedLocalPath.Length == 0)
+ cachedLocalPath = Path.DirectorySeparatorChar.ToString ();
+ return cachedLocalPath;
+ }
+ }
+
+ public string PathAndQuery {
+ get {
+ EnsureAbsoluteUri ();
+ return path + Query;
+ }
+ }
+
+ public int Port {
+ get {
+ EnsureAbsoluteUri ();
+ return port;
+ }
+ }
+
+ public string Query {
+ get {
+ EnsureAbsoluteUri ();
+ return query;
+ }
+ }
+
+ public string Scheme {
+ get {
+ EnsureAbsoluteUri ();
+ return scheme;
+ }
+ }
+
+ public string [] Segments {
+ get {
+ EnsureAbsoluteUri ();
+ if (segments != null)
+ return segments;
+
+ if (path.Length == 0) {
+ segments = new string [0];
+ return segments;
+ }
+
+ string [] parts = path.Split ('/');
+ segments = parts;
+ bool endSlash = path.EndsWith ("/");
+ if (parts.Length > 0 && endSlash) {
+ string [] newParts = new string [parts.Length - 1];
+ Array.Copy (parts, 0, newParts, 0, parts.Length - 1);
+ parts = newParts;
+ }
+
+ int i = 0;
+ if (IsFile && path.Length > 1 && path [1] == ':') {
+ string [] newParts = new string [parts.Length + 1];
+ Array.Copy (parts, 1, newParts, 2, parts.Length - 1);
+ parts = newParts;
+ parts [0] = path.Substring (0, 2);
+ parts [1] = String.Empty;
+ i++;
+ }
+
+ int end = parts.Length;
+ for (; i < end; i++)
+ if (i != end - 1 || endSlash)
+ parts [i] += '/';
+
+ segments = parts;
+ return segments;
+ }
+ }
+
+ public bool UserEscaped {
+ get { return userEscaped; }
+ }
+
+ public string UserInfo {
+ get {
+ EnsureAbsoluteUri ();
+ return userinfo;
+ }
+ }
+
+//#if NET_2_0
+ [MonoTODO ("add support for IPv6 address")]
+ public string DnsSafeHost {
+ get {
+ EnsureAbsoluteUri ();
+ return Unescape (Host);
+ }
+ }
+
+ public bool IsAbsoluteUri {
+ get { return isAbsoluteUri; }
+ }
+//#endif
+ // LAMESPEC: source field is supplied in such case that this
+ // property makes sense. For such case that source field is
+ // not supplied (i.e. .ctor(Uri, string), this property
+ // makes no sense. To avoid silly regression it just returns
+ // ToString() value now. See bug #78374.
+#if NET_2_0
+ public
+#else
+ internal
+#endif
+ string OriginalString {
+ get { return source != null ? source : ToString (); }
+ }
+
+ // Methods
+
+ public static UriHostNameType CheckHostName (string name)
+ {
+ if (name == null || name.Length == 0)
+ return UriHostNameType.Unknown;
+
+ if (IsIPv4Address (name))
+ return UriHostNameType.IPv4;
+
+ if (IsDomainAddress (name))
+ return UriHostNameType.Dns;
+
+ IPv6Address addr;
+ if (IPv6Address.TryParse (name, out addr))
+ return UriHostNameType.IPv6;
+
+ return UriHostNameType.Unknown;
+ }
+
+ internal static bool IsIPv4Address (string name)
+ {
+ string [] captures = name.Split (new char [] {'.'});
+ if (captures.Length != 4)
+ return false;
+
+ for (int i = 0; i < 4; i++) {
+ int length;
+
+ length = captures [i].Length;
+ if (length == 0)
+ return false;
+ uint number;
+#if NET_2_0
+ if (!UInt32.TryParse (captures [i], out number))
+ return false;
+#else
+ try {
+ number = UInt32.Parse (captures [i]);
+ } catch (Exception) {
+ return false;
+ }
+#endif
+ if (number > 255)
+ return false;
+ }
+ return true;
+ }
+
+ internal static bool IsDomainAddress (string name)
+ {
+ int len = name.Length;
+
+ int count = 0;
+ for (int i = 0; i < len; i++) {
+ char c = name [i];
+ if (count == 0) {
+ if (!Char.IsLetterOrDigit (c))
+ return false;
+ } else if (c == '.') {
+ count = 0;
+ } else if (!Char.IsLetterOrDigit (c) && c != '-' && c != '_') {
+ return false;
+ }
+ if (++count == 64)
+ return false;
+ }
+
+ return true;
+ }
+#if !NET_2_1
+
+#if NET_2_0
+ [Obsolete("This method does nothing, it has been obsoleted")]
+#endif
+ protected virtual void Canonicalize ()
+ {
+ //
+ // This is flagged in the Microsoft documentation as used
+ // internally, no longer in use, and Obsolete.
+ //
+ }
+
+ [MonoTODO ("Find out what this should do")]
+#if NET_2_0
+ [Obsolete]
+#endif
+ protected virtual void CheckSecurity ()
+ {
+ }
+
+#endif // NET_2_1
+
+ // defined in RFC3986 as = ALPHA *( ALPHA / DIGIT / "+" / "-" / ".")
+ public static bool CheckSchemeName (string schemeName)
+ {
+ if (schemeName == null || schemeName.Length == 0)
+ return false;
+
+ if (!IsAlpha (schemeName [0]))
+ return false;
+
+ int len = schemeName.Length;
+ for (int i = 1; i < len; i++) {
+ char c = schemeName [i];
+ if (!Char.IsDigit (c) && !IsAlpha (c) && c != '.' && c != '+' && c != '-')
+ return false;
+ }
+
+ return true;
+ }
+
+ private static bool IsAlpha (char c)
+ {
+#if NET_2_0
+ // as defined in rfc2234
+ // %x41-5A / %x61-7A (A-Z / a-z)
+ int i = (int) c;
+ return (((i >= 0x41) && (i <= 0x5A)) || ((i >= 0x61) && (i <= 0x7A)));
+#else
+ // Fx 1.x got this too large
+ return Char.IsLetter (c);
+#endif
+ }
+
+ public override bool Equals (object comparant)
+ {
+ if (comparant == null)
+ return false;
+
+ Uri uri = comparant as Uri;
+ if ((object) uri == null) {
+ string s = comparant as String;
+ if (s == null)
+ return false;
+ uri = new Uri (s);
+ }
+
+ return InternalEquals (uri);
+ }
+
+ // Assumes: uri != null
+ bool InternalEquals (Uri uri)
+ {
+#if NET_2_0
+ if (this.isAbsoluteUri != uri.isAbsoluteUri)
+ return false;
+ if (!this.isAbsoluteUri)
+ return this.source == uri.source;
+#endif
+
+ CultureInfo inv = CultureInfo.InvariantCulture;
+ return this.scheme.ToLower (inv) == uri.scheme.ToLower (inv)
+ && this.host.ToLower (inv) == uri.host.ToLower (inv)
+ && this.port == uri.port
+#if NET_2_0
+ && this.query == uri.query
+#else
+ // Note: MS.NET 1.x has bug - ignores query check altogether
+ && this.query.ToLower (inv) == uri.query.ToLower (inv)
+#endif
+ && this.path == uri.path;
+ }
+
+#if NET_2_0
+ public static bool operator == (Uri u1, Uri u2)
+ {
+ return object.Equals(u1, u2);
+ }
+
+ public static bool operator != (Uri u1, Uri u2)
+ {
+ return !(u1 == u2);
+ }
+#endif
+
+ public override int GetHashCode ()
+ {
+ if (cachedHashCode == 0) {
+ CultureInfo inv = CultureInfo.InvariantCulture;
+ if (isAbsoluteUri) {
+ cachedHashCode = scheme.ToLower (inv).GetHashCode ()
+ ^ host.ToLower (inv).GetHashCode ()
+ ^ port
+#if NET_2_0
+ ^ query.GetHashCode ()
+#else
+ ^ query.ToLower (inv).GetHashCode ()
+#endif
+ ^ path.GetHashCode ();
+ }
+ else {
+ cachedHashCode = source.GetHashCode ();
+ }
+ }
+ return cachedHashCode;
+ }
+
+ public string GetLeftPart (UriPartial part)
+ {
+ EnsureAbsoluteUri ();
+ int defaultPort;
+ switch (part) {
+ case UriPartial.Scheme :
+ return scheme + GetOpaqueWiseSchemeDelimiter ();
+ case UriPartial.Authority :
+ if ((scheme == Uri.UriSchemeMailto) || (scheme == Uri.UriSchemeNews))
+ return String.Empty;
+
+ StringBuilder s = new StringBuilder ();
+ s.Append (scheme);
+ s.Append (GetOpaqueWiseSchemeDelimiter ());
+ if (path.Length > 1 && path [1] == ':' && (Uri.UriSchemeFile == scheme))
+ s.Append ('/'); // win32 file
+ if (userinfo.Length > 0)
+ s.Append (userinfo).Append ('@');
+ s.Append (host);
+ defaultPort = GetDefaultPort (scheme);
+ if ((port != -1) && (port != defaultPort))
+ s.Append (':').Append (port);
+ return s.ToString ();
+ case UriPartial.Path :
+ StringBuilder sb = new StringBuilder ();
+ sb.Append (scheme);
+ sb.Append (GetOpaqueWiseSchemeDelimiter ());
+ if (path.Length > 1 && path [1] == ':' && (Uri.UriSchemeFile == scheme))
+ sb.Append ('/'); // win32 file
+ if (userinfo.Length > 0)
+ sb.Append (userinfo).Append ('@');
+ sb.Append (host);
+ defaultPort = GetDefaultPort (scheme);
+ if ((port != -1) && (port != defaultPort))
+ sb.Append (':').Append (port);
+
+ if (path.Length > 0) {
+#if NET_2_0
+ switch (Scheme) {
+ case "mailto":
+ case "news":
+ sb.Append (path);
+ break;
+ default:
+ sb.Append (Reduce (path));
+ break;
+ }
+#else
+ sb.Append (path);
+#endif
+ }
+ return sb.ToString ();
+ }
+ return null;
+ }
+
+ public static int FromHex (char digit)
+ {
+ if ('0' <= digit && digit <= '9') {
+ return (int) (digit - '0');
+ }
+
+ if ('a' <= digit && digit <= 'f')
+ return (int) (digit - 'a' + 10);
+
+ if ('A' <= digit && digit <= 'F')
+ return (int) (digit - 'A' + 10);
+
+ throw new ArgumentException ("digit");
+ }
+
+ public static string HexEscape (char character)
+ {
+ if (character > 255) {
+ throw new ArgumentOutOfRangeException ("character");
+ }
+
+ return "%" + hexUpperChars [((character & 0xf0) >> 4)]
+ + hexUpperChars [((character & 0x0f))];
+ }
+
+ public static char HexUnescape (string pattern, ref int index)
+ {
+ if (pattern == null)
+ throw new ArgumentException ("pattern");
+
+ if (index < 0 || index >= pattern.Length)
+ throw new ArgumentOutOfRangeException ("index");
+
+ if (!IsHexEncoding (pattern, index))
+ return pattern [index++];
+
+ index++;
+ int msb = FromHex (pattern [index++]);
+ int lsb = FromHex (pattern [index++]);
+ return (char) ((msb << 4) | lsb);
+ }
+
+ public static bool IsHexDigit (char digit)
+ {
+ return (('0' <= digit && digit <= '9') ||
+ ('a' <= digit && digit <= 'f') ||
+ ('A' <= digit && digit <= 'F'));
+ }
+
+ public static bool IsHexEncoding (string pattern, int index)
+ {
+ if ((index + 3) > pattern.Length)
+ return false;
+
+ return ((pattern [index++] == '%') &&
+ IsHexDigit (pattern [index++]) &&
+ IsHexDigit (pattern [index]));
+ }
+
+#if NET_2_0
+ //
+ // Implemented by copying most of the MakeRelative code
+ //
+ public Uri MakeRelativeUri (Uri uri)
+ {
+ if (uri == null)
+ throw new ArgumentNullException ("uri");
+
+ if (Host != uri.Host || Scheme != uri.Scheme)
+ return uri;
+
+ string result = String.Empty;
+ if (this.path != uri.path){
+ string [] segments = this.Segments;
+ string [] segments2 = uri.Segments;
+
+ int k = 0;
+ int max = Math.Min (segments.Length, segments2.Length);
+ for (; k < max; k++)
+ if (segments [k] != segments2 [k])
+ break;
+
+ for (int i = k + 1; i < segments.Length; i++)
+ result += "../";
+ for (int i = k; i < segments2.Length; i++)
+ result += segments2 [i];
+
+ }
+ uri.AppendQueryAndFragment (ref result);
+
+ return new Uri (result, UriKind.Relative);
+ }
+
+ [Obsolete ("Use MakeRelativeUri(Uri uri) instead.")]
+#endif
+ public string MakeRelative (Uri toUri)
+ {
+ if ((this.Scheme != toUri.Scheme) ||
+ (this.Authority != toUri.Authority))
+ return toUri.ToString ();
+
+ string result = String.Empty;
+ if (this.path != toUri.path){
+ string [] segments = this.Segments;
+ string [] segments2 = toUri.Segments;
+ int k = 0;
+ int max = Math.Min (segments.Length, segments2.Length);
+ for (; k < max; k++)
+ if (segments [k] != segments2 [k])
+ break;
+
+ for (int i = k + 1; i < segments.Length; i++)
+ result += "../";
+ for (int i = k; i < segments2.Length; i++)
+ result += segments2 [i];
+ }
+
+ // Important: MakeRelative does not append fragment or query.
+
+ return result;
+ }
+
+ void AppendQueryAndFragment (ref string result)
+ {
+ if (query.Length > 0) {
+ string q = query [0] == '?' ? '?' + Unescape (query.Substring (1), false) : Unescape (query, false);
+ result += q;
+ }
+ if (fragment.Length > 0)
+ result += fragment;
+ }
+
+ public override string ToString ()
+ {
+ if (cachedToString != null)
+ return cachedToString;
+
+ if (isAbsoluteUri)
+ cachedToString = Unescape (GetLeftPart (UriPartial.Path), true);
+ else {
+ // Everything is contained in path in this case.
+ cachedToString = Unescape (path);
+ }
+
+ AppendQueryAndFragment (ref cachedToString);
+ return cachedToString;
+ }
+
+#if NET_2_0
+ protected void GetObjectData (SerializationInfo info, StreamingContext context)
+ {
+ info.AddValue ("AbsoluteUri", this.AbsoluteUri);
+ }
+#endif
+
+ void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
+ {
+ info.AddValue ("AbsoluteUri", this.AbsoluteUri);
+ }
+
+
+ // Internal Methods
+
+#if NET_2_0
+ [Obsolete]
+#endif
+ protected virtual void Escape ()
+ {
+ path = EscapeString (path);
+ }
+
+#if NET_2_0
+ [Obsolete]
+#endif
+ protected static string EscapeString (string str)
+ {
+ return EscapeString (str, false, true, true);
+ }
+
+ internal static string EscapeString (string str, bool escapeReserved, bool escapeHex, bool escapeBrackets)
+ {
+ if (str == null)
+ return String.Empty;
+
+ StringBuilder s = new StringBuilder ();
+ int len = str.Length;
+ for (int i = 0; i < len; i++) {
+ // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
+ // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
+ // control = <US-ASCII coded characters 00-1F and 7F hexadecimal>
+ // space = <US-ASCII coded character 20 hexadecimal>
+ // delims = "<" | ">" | "#" | "%" | <">
+ // unwise = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`"
+
+ // check for escape code already placed in str,
+ // i.e. for encoding that follows the pattern
+ // "%hexhex" in a string, where "hex" is a digit from 0-9
+ // or a letter from A-F (case-insensitive).
+ if (IsHexEncoding (str,i)) {
+ // if ,yes , copy it as is
+ s.Append(str.Substring (i, 3));
+ i += 2;
+ continue;
+ }
+
+ byte [] data = Encoding.UTF8.GetBytes (new char[] {str[i]});
+ int length = data.Length;
+ for (int j = 0; j < length; j++) {
+ char c = (char) data [j];
+ if ((c <= 0x20) || (c >= 0x7f) ||
+ ("<>%\"{}|\\^`".IndexOf (c) != -1) ||
+ (escapeHex && (c == '#')) ||
+ (escapeBrackets && (c == '[' || c == ']')) ||
+ (escapeReserved && (";/?:@&=+$,".IndexOf (c) != -1))) {
+ s.Append (HexEscape (c));
+ continue;
+ }
+ s.Append (c);
+ }
+ }
+
+ return s.ToString ();
+ }
+
+ // On .NET 1.x, this method is called from .ctor(). When overriden, we
+ // can avoid the "absolute uri" constraints of the .ctor() by
+ // overriding with custom code.
+#if NET_2_0
+ [Obsolete("The method has been deprecated. It is not used by the system.")]
+#endif
+ protected virtual void Parse ()
+ {
+#if !NET_2_0
+ ParseUri (UriKind.Absolute);
+#endif
+ }
+
+ private void ParseUri (UriKind kind)
+ {
+ Parse (kind, source);
+
+ if (userEscaped)
+ return;
+
+ host = EscapeString (host, false, true, false);
+ if (host.Length > 1 && host [0] != '[' && host [host.Length - 1] != ']') {
+ // host name present (but not an IPv6 address)
+ host = host.ToLower (CultureInfo.InvariantCulture);
+ }
+
+ if (path.Length > 0) {
+ path = EscapeString (path);
+ }
+ }
+
+#if NET_2_0
+ [Obsolete]
+#endif
+ protected virtual string Unescape (string str)
+ {
+ return Unescape (str, false);
+ }
+
+ internal static string Unescape (string str, bool excludeSpecial)
+ {
+ if (str == null)
+ return String.Empty;
+ StringBuilder s = new StringBuilder ();
+ int len = str.Length;
+ for (int i = 0; i < len; i++) {
+ char c = str [i];
+ if (c == '%') {
+ char surrogate;
+ char x = HexUnescapeMultiByte (str, ref i, out surrogate);
+ if (excludeSpecial && x == '#')
+ s.Append ("%23");
+ else if (excludeSpecial && x == '%')
+ s.Append ("%25");
+ else if (excludeSpecial && x == '?')
+ s.Append ("%3F");
+ else {
+ s.Append (x);
+ if (surrogate != char.MinValue)
+ s.Append (surrogate);
+ }
+ i--;
+ } else
+ s.Append (c);
+ }
+ return s.ToString ();
+ }
+
+
+ // Private Methods
+
+ private void ParseAsWindowsUNC (string uriString)
+ {
+ scheme = UriSchemeFile;
+ port = -1;
+ fragment = String.Empty;
+ query = String.Empty;
+ isUnc = true;
+
+ uriString = uriString.TrimStart (new char [] {'\\'});
+ int pos = uriString.IndexOf ('\\');
+ if (pos > 0) {
+ path = uriString.Substring (pos);
+ host = uriString.Substring (0, pos);
+ } else { // "\\\\server"
+ host = uriString;
+ path = String.Empty;
+ }
+ path = path.Replace ("\\", "/");
+ }
+
+ //
+ // Returns null on success, string with error on failure
+ //
+ private string ParseAsWindowsAbsoluteFilePath (string uriString)
+ {
+ if (uriString.Length > 2 && uriString [2] != '\\' && uriString [2] != '/')
+ return "Relative file path is not allowed.";
+ scheme = UriSchemeFile;
+ host = String.Empty;
+ port = -1;
+ path = uriString.Replace ("\\", "/");
+ fragment = String.Empty;
+ query = String.Empty;
+
+ return null;
+ }
+
+ private void ParseAsUnixAbsoluteFilePath (string uriString)
+ {
+ isUnixFilePath = true;
+ scheme = UriSchemeFile;
+ port = -1;
+ fragment = String.Empty;
+ query = String.Empty;
+ host = String.Empty;
+ path = null;
+
+ if (uriString.Length >= 2 && uriString [0] == '/' && uriString [1] == '/') {
+ uriString = uriString.TrimStart (new char [] {'/'});
+ // Now we don't regard //foo/bar as "foo" host.
+ /*
+ int pos = uriString.IndexOf ('/');
+ if (pos > 0) {
+ path = '/' + uriString.Substring (pos + 1);
+ host = uriString.Substring (0, pos);
+ } else { // "///server"
+ host = uriString;
+ path = String.Empty;
+ }
+ */
+ path = '/' + uriString;
+ }
+ if (path == null)
+ path = uriString;
+ }
+
+ //
+ // This parse method will throw exceptions on failure
+ //
+ private void Parse (UriKind kind, string uriString)
+ {
+ if (uriString == null)
+ throw new ArgumentNullException ("uriString");
+
+ string s = ParseNoExceptions (kind, uriString);
+ if (s != null)
+ throw new UriFormatException (s);
+ }
+
+ //
+ // This parse method will not throw exceptions on failure
+ //
+ // Returns null on success, or a description of the error in the parsing
+ //
+ private string ParseNoExceptions (UriKind kind, string uriString)
+ {
+ //
+ // From RFC 2396 :
+ //
+ // ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
+ // 12 3 4 5 6 7 8 9
+ //
+
+ uriString = uriString.Trim();
+ int len = uriString.Length;
+
+ if (len == 0){
+ if (kind == UriKind.Relative || kind == UriKind.RelativeOrAbsolute){
+ isAbsoluteUri = false;
+ return null;
+ }
+ }
+
+ if (len <= 1 && (kind != UriKind.Relative))
+ return "Absolute URI is too short";
+
+ int pos = 0;
+
+ // 1, 2
+ // Identify Windows path, unix path, or standard URI.
+ pos = uriString.IndexOf (':');
+ if (pos == 0) {
+ return "Invalid URI: The format of the URI could not be determined.";
+ } else if (pos < 0) {
+ // It must be Unix file path or Windows UNC
+ if (uriString [0] == '/' && Path.DirectorySeparatorChar == '/'){
+ ParseAsUnixAbsoluteFilePath (uriString);
+#if NET_2_1
+ isAbsoluteUri = false;
+#else
+ if (kind == UriKind.Relative)
+ isAbsoluteUri = false;
+#endif
+
+ } else if (uriString.Length >= 2 && uriString [0] == '\\' && uriString [1] == '\\')
+ ParseAsWindowsUNC (uriString);
+ else {
+ /* Relative path */
+ isAbsoluteUri = false;
+ path = uriString;
+ }
+ return null;
+ } else if (pos == 1) {
+ if (!IsAlpha (uriString [0]))
+ return "URI scheme must start with a letter.";
+ // This means 'a:' == windows full path.
+ string msg = ParseAsWindowsAbsoluteFilePath (uriString);
+ if (msg != null)
+ return msg;
+ return null;
+ }
+
+ // scheme
+ scheme = uriString.Substring (0, pos).ToLower (CultureInfo.InvariantCulture);
+
+ // Check scheme name characters as specified in RFC2396.
+ // Note: different checks in 1.x and 2.0
+ if (!CheckSchemeName (scheme))
+ return Locale.GetText ("URI scheme must start with a letter and must consist of one of alphabet, digits, '+', '-' or '.' character.");
+
+ // from here we're practically working on uriString.Substring(startpos,endpos-startpos)
+ int startpos = pos + 1;
+ int endpos = uriString.Length;
+
+ // 8 fragment
+ pos = uriString.IndexOf ('#', startpos);
+ if (!IsUnc && pos != -1) {
+ if (userEscaped)
+ fragment = uriString.Substring (pos);
+ else
+ fragment = "#" + EscapeString (uriString.Substring (pos+1));
+
+ endpos = pos;
+ }
+
+ // 6 query
+ pos = uriString.IndexOf ('?', startpos, endpos-startpos);
+ if (pos != -1) {
+ query = uriString.Substring (pos, endpos-pos);
+ endpos = pos;
+ if (!userEscaped)
+ query = EscapeString (query);
+ }
+
+ // 3
+ if (IsPredefinedScheme (scheme) && scheme != UriSchemeMailto && scheme != UriSchemeNews && (
+ (endpos-startpos < 2) ||
+ (endpos-startpos >= 2 && uriString [startpos] == '/' && uriString [startpos+1] != '/')))
+ return "Invalid URI: The Authority/Host could not be parsed.";
+
+
+ bool startsWithSlashSlash = endpos-startpos >= 2 && uriString [startpos] == '/' && uriString [startpos+1] == '/';
+ bool unixAbsPath = scheme == UriSchemeFile && startsWithSlashSlash && (endpos-startpos == 2 || uriString [startpos+2] == '/');
+ bool windowsFilePath = false;
+ if (startsWithSlashSlash) {
+ if (kind == UriKind.Relative)
+ return "Absolute URI when we expected a relative one";
+
+ if (scheme != UriSchemeMailto && scheme != UriSchemeNews)
+ startpos += 2;
+
+ if (scheme == UriSchemeFile) {
+ int num_leading_slash = 2;
+ for (int i = startpos; i < endpos; i++) {
+ if (uriString [i] != '/')
+ break;
+ num_leading_slash++;
+ }
+ if (num_leading_slash >= 4) {
+ unixAbsPath = false;
+ while (startpos < endpos && uriString[startpos] == '/') {
+ startpos++;
+ }
+ } else if (num_leading_slash >= 3) {
+ startpos += 1;
+ }
+ }
+
+ if (endpos - startpos > 1 && uriString [startpos + 1] == ':') {
+ unixAbsPath = false;
+ windowsFilePath = true;
+ }
+
+ } else if (!IsPredefinedScheme (scheme)) {
+ path = uriString.Substring(startpos, endpos-startpos);
+ isOpaquePart = true;
+ return null;
+ }
+
+ // 5 path
+ if (unixAbsPath) {
+ pos = -1;
+ } else {
+ pos = uriString.IndexOf ('/', startpos, endpos-startpos);
+ if (pos == -1 && windowsFilePath)
+ pos = uriString.IndexOf ('\\', startpos, endpos-startpos);
+ }
+
+ if (pos == -1) {
+ if ((scheme != Uri.UriSchemeMailto) &&
+#if ONLY_1_1
+ (scheme != Uri.UriSchemeFile) &&
+#endif
+ (scheme != Uri.UriSchemeNews))
+ path = "/";
+ } else {
+ path = uriString.Substring (pos, endpos-pos);
+ endpos = pos;
+ }
+
+ // 4.a user info
+ pos = uriString.IndexOf ('@', startpos, endpos-startpos);
+ if (pos != -1) {
+ userinfo = uriString.Substring (startpos, pos-startpos);
+ startpos = pos + 1;
+ }
+
+ // 4.b port
+ port = -1;
+ if (unixAbsPath)
+ pos = -1;
+ else
+ pos = uriString.LastIndexOf (':', endpos-1, endpos-startpos);
+ if (pos != -1 && pos != endpos - 1) {
+ string portStr = uriString.Substring(pos + 1, endpos - (pos + 1));
+ if (portStr.Length > 0 && portStr[portStr.Length - 1] != ']') {
+#if NET_2_0
+ if (!Int32.TryParse (portStr, NumberStyles.Integer, CultureInfo.InvariantCulture, out port) ||
+ port < 0 || port > UInt16.MaxValue)
+ return "Invalid URI: Invalid port number";
+ endpos = pos;
+#else
+ try {
+ port = (int) UInt32.Parse (portStr, CultureInfo.InvariantCulture);
+ endpos = pos;
+ } catch (Exception) {
+ return "Invalid URI: Invalid port number";
+ }
+#endif
+ } else {
+ if (port == -1) {
+ port = GetDefaultPort (scheme);
+ }
+ }
+ } else {
+ if (port == -1) {
+ port = GetDefaultPort (scheme);
+ }
+ }
+
+ // 4 authority
+ uriString = uriString.Substring(startpos, endpos-startpos);
+ host = uriString;
+
+ if (unixAbsPath) {
+ path = Reduce ('/' + uriString);
+ host = String.Empty;
+ } else if (host.Length == 2 && host [1] == ':') {
+ // windows filepath
+ path = host + path;
+ host = String.Empty;
+ } else if (isUnixFilePath) {
+ uriString = "//" + uriString;
+ host = String.Empty;
+ } else if (scheme == UriSchemeFile) {
+ isUnc = true;
+ } else if (scheme == UriSchemeNews) {
+ // no host for 'news', misinterpreted path
+ if (host.Length > 0) {
+ path = host;
+ host = String.Empty;
+ }
+ } else if (host.Length == 0 &&
+ (scheme == UriSchemeHttp || scheme == UriSchemeGopher || scheme == UriSchemeNntp ||
+ scheme == UriSchemeHttps || scheme == UriSchemeFtp)) {
+ return "Invalid URI: The hostname could not be parsed";
+ }
+
+ bool badhost = ((host.Length > 0) && (CheckHostName (host) == UriHostNameType.Unknown));
+ if (!badhost && (host.Length > 1) && (host[0] == '[') && (host[host.Length - 1] == ']')) {
+ IPv6Address ipv6addr;
+
+ if (IPv6Address.TryParse (host, out ipv6addr))
+ host = "[" + ipv6addr.ToString (true) + "]";
+ else
+ badhost = true;
+ }
+#if NET_2_0
+ if (badhost && (Parser is DefaultUriParser || Parser == null))
+ return Locale.GetText ("Invalid URI: The hostname could not be parsed. (" + host + ")");
+
+ UriFormatException ex = null;
+ if (Parser != null)
+ Parser.InitializeAndValidate (this, out ex);
+ if (ex != null)
+ return ex.Message;
+#else
+ if (badhost)
+ return Locale.GetText ("Invalid URI: The hostname could not be parsed. (" + host + ")");
+#endif
+
+ if ((scheme != Uri.UriSchemeMailto) &&
+ (scheme != Uri.UriSchemeNews) &&
+ (scheme != Uri.UriSchemeFile)) {
+ path = Reduce (path);
+ }
+
+ return null;
+ }
+
+ private static string Reduce (string path)
+ {
+ // quick out, allocation-free, for a common case
+ if (path == "/")
+ return path;
+
+ // replace '\', %5C ('\') and %2f ('/') into '/'
+ // other escaped values seems to survive this step
+ StringBuilder res = new StringBuilder();
+ for (int i=0; i < path.Length; i++) {
+ char c = path [i];
+ switch (c) {
+ case '\\':
+ res.Append ('/');
+ break;
+ case '%':
+ if (i < path.Length - 2) {
+ char c1 = path [i + 1];
+ char c2 = Char.ToUpper (path [i + 2]);
+ if (((c1 == '2') && (c2 == 'F')) || ((c1 == '5') && (c2 == 'C'))) {
+ res.Append ('/');
+ i += 2;
+ } else {
+ res.Append (c);
+ }
+ } else {
+ res.Append (c);
+ }
+ break;
+ default:
+ res.Append (c);
+ break;
+ }
+ }
+ path = res.ToString ();
+ ArrayList result = new ArrayList ();
+
+ for (int startpos = 0; startpos < path.Length; ) {
+ int endpos = path.IndexOf('/', startpos);
+ if (endpos == -1) endpos = path.Length;
+ string current = path.Substring (startpos, endpos-startpos);
+ startpos = endpos + 1;
+ if (current.Length == 0 || current == "." )
+ continue;
+
+ if (current == "..") {
+ int resultCount = result.Count;
+#if NET_2_0
+ // in 2.0 profile, skip leading ".." parts
+ if (resultCount == 0) {
+ continue;
+ }
+
+ result.RemoveAt (resultCount - 1);
+ continue;
+#else
+ // in 1.x profile, retain leading ".." parts, and only reduce
+ // URI is previous part is not ".."
+ if (resultCount > 0) {
+ if ((string) result[resultCount - 1] != "..") {
+ result.RemoveAt (resultCount - 1);
+ continue;
+ }
+ }
+#endif
+ }
+
+ result.Add (current);
+ }
+
+ if (result.Count == 0)
+ return "/";
+
+ res.Length = 0;
+ if (path [0] == '/')
+ res.Append ('/');
+
+ bool first = true;
+ foreach (string part in result) {
+ if (first) {
+ first = false;
+ } else {
+ res.Append ('/');
+ }
+ res.Append(part);
+ }
+
+ if (path.EndsWith ("/"))
+ res.Append ('/');
+
+ return res.ToString();
+ }
+
+ // A variant of HexUnescape() which can decode multi-byte escaped
+ // sequences such as (e.g.) %E3%81%8B into a single character
+ private static char HexUnescapeMultiByte (string pattern, ref int index, out char surrogate)
+ {
+ surrogate = char.MinValue;
+
+ if (pattern == null)
+ throw new ArgumentException ("pattern");
+
+ if (index < 0 || index >= pattern.Length)
+ throw new ArgumentOutOfRangeException ("index");
+
+ if (!IsHexEncoding (pattern, index))
+ return pattern [index++];
+
+ int orig_index = index++;
+ int msb = FromHex (pattern [index++]);
+ int lsb = FromHex (pattern [index++]);
+
+ // We might be dealing with a multi-byte character:
+ // The number of ones at the top-end of the first byte will tell us
+ // how many bytes will make up this character.
+ int msb_copy = msb;
+ int num_bytes = 0;
+ while ((msb_copy & 0x8) == 0x8) {
+ num_bytes++;
+ msb_copy <<= 1;
+ }
+
+ // We might be dealing with a single-byte character:
+ // If there was only 0 or 1 leading ones then we're not dealing
+ // with a multi-byte character.
+ if (num_bytes <= 1)
+ return (char) ((msb << 4) | lsb);
+
+ // Now that we know how many bytes *should* follow, we'll check them
+ // to ensure we are dealing with a valid multi-byte character.
+ byte [] chars = new byte [num_bytes];
+ bool all_invalid = false;
+ chars[0] = (byte) ((msb << 4) | lsb);
+
+ for (int i = 1; i < num_bytes; i++) {
+ if (!IsHexEncoding (pattern, index++)) {
+ all_invalid = true;
+ break;
+ }
+
+ // All following bytes must be in the form 10xxxxxx
+ int cur_msb = FromHex (pattern [index++]);
+ if ((cur_msb & 0xc) != 0x8) {
+ all_invalid = true;
+ break;
+ }
+
+ int cur_lsb = FromHex (pattern [index++]);
+ chars[i] = (byte) ((cur_msb << 4) | cur_lsb);
+ }
+
+ // If what looked like a multi-byte character is invalid, then we'll
+ // just return the first byte as a single byte character.
+ if (all_invalid) {
+ index = orig_index + 3;
+ return (char) chars[0];
+ }
+
+ // Otherwise, we're dealing with a valid multi-byte character.
+ // We need to ignore the leading ones from the first byte:
+ byte mask = (byte) 0xFF;
+ mask >>= (num_bytes + 1);
+ int result = chars[0] & mask;
+
+ // The result will now be built up from the following bytes.
+ for (int i = 1; i < num_bytes; i++) {
+ // Ignore upper two bits
+ result <<= 6;
+ result |= (chars[i] & 0x3F);
+ }
+
+ if (result <= 0xFFFF) {
+ return (char) result;
+ } else {
+ // We need to handle this as a UTF16 surrogate (i.e. return
+ // two characters)
+ result -= 0x10000;
+ surrogate = (char) ((result & 0x3FF) | 0xDC00);
+ return (char) ((result >> 10) | 0xD800);
+ }
+ }
+
+ private struct UriScheme
+ {
+ public string scheme;
+ public string delimiter;
+ public int defaultPort;
+
+ public UriScheme (string s, string d, int p)
+ {
+ scheme = s;
+ delimiter = d;
+ defaultPort = p;
+ }
+ };
+
+ static UriScheme [] schemes = new UriScheme [] {
+ new UriScheme (UriSchemeHttp, SchemeDelimiter, 80),
+ new UriScheme (UriSchemeHttps, SchemeDelimiter, 443),
+ new UriScheme (UriSchemeFtp, SchemeDelimiter, 21),
+ new UriScheme (UriSchemeFile, SchemeDelimiter, -1),
+ new UriScheme (UriSchemeMailto, ":", 25),
+ new UriScheme (UriSchemeNews, ":", 119),
+ new UriScheme (UriSchemeNntp, SchemeDelimiter, 119),
+ new UriScheme (UriSchemeGopher, SchemeDelimiter, 70),
+ };
+
+ internal static string GetSchemeDelimiter (string scheme)
+ {
+ for (int i = 0; i < schemes.Length; i++)
+ if (schemes [i].scheme == scheme)
+ return schemes [i].delimiter;
+ return Uri.SchemeDelimiter;
+ }
+
+ internal static int GetDefaultPort (string scheme)
+ {
+#if NET_2_0
+ UriParser parser = UriParser.GetParser (scheme);
+ if (parser == null)
+ return -1;
+ return parser.DefaultPort;
+#else
+ for (int i = 0; i < schemes.Length; i++)
+ if (schemes [i].scheme == scheme)
+ return schemes [i].defaultPort;
+ return -1;
+#endif
+ }
+
+ private string GetOpaqueWiseSchemeDelimiter ()
+ {
+ if (isOpaquePart)
+ return ":";
+ else
+ return GetSchemeDelimiter (scheme);
+ }
+
+#if NET_2_0
+ [Obsolete]
+#endif
+ protected virtual bool IsBadFileSystemCharacter (char ch)
+ {
+ // It does not always overlap with InvalidPathChars.
+ int chInt = (int) ch;
+ if (chInt < 32 || (chInt < 64 && chInt > 57))
+ return true;
+ switch (chInt) {
+ case 0:
+ case 34: // "
+ case 38: // &
+ case 42: // *
+ case 44: // ,
+ case 47: // /
+ case 92: // \
+ case 94: // ^
+ case 124: // |
+ return true;
+ }
+
+ return false;
+ }
+
+#if NET_2_0
+ [Obsolete]
+#endif
+ protected static bool IsExcludedCharacter (char ch)
+ {
+ if (ch <= 32 || ch >= 127)
+ return true;
+
+ if (ch == '"' || ch == '#' || ch == '%' || ch == '<' ||
+ ch == '>' || ch == '[' || ch == '\\' || ch == ']' ||
+ ch == '^' || ch == '`' || ch == '{' || ch == '|' ||
+ ch == '}')
+ return true;
+ return false;
+ }
+
+ internal static bool MaybeUri (string s)
+ {
+ int p = s.IndexOf (':');
+ if (p == -1)
+ return false;
+
+ if (p >= 10)
+ return false;
+
+ return IsPredefinedScheme (s.Substring (0, p));
+ }
+
+ private static bool IsPredefinedScheme (string scheme)
+ {
+ switch (scheme) {
+ case "http":
+ case "https":
+ case "file":
+ case "ftp":
+ case "nntp":
+ case "gopher":
+ case "mailto":
+ case "news":
+#if NET_2_0
+ case "net.pipe":
+ case "net.tcp":
+#endif
+ return true;
+ default:
+ return false;
+ }
+ }
+
+#if NET_2_0
+ [Obsolete]
+#endif
+ protected virtual bool IsReservedCharacter (char ch)
+ {
+ if (ch == '$' || ch == '&' || ch == '+' || ch == ',' ||
+ ch == '/' || ch == ':' || ch == ';' || ch == '=' ||
+ ch == '@')
+ return true;
+ return false;
+ }
+#if NET_2_0
+ [NonSerialized]
+ private UriParser parser;
+
+ private UriParser Parser {
+ get {
+ if (parser == null) {
+ parser = UriParser.GetParser (Scheme);
+ // no specific parser ? then use a default one
+ if (parser == null)
+ parser = new DefaultUriParser ("*");
+ }
+ return parser;
+ }
+ set { parser = value; }
+ }
+
+ public string GetComponents (UriComponents components, UriFormat format)
+ {
+ return Parser.GetComponents (this, components, format);
+ }
+
+ public bool IsBaseOf (Uri uri)
+ {
+ return Parser.IsBaseOf (this, uri);
+ }
+
+ public bool IsWellFormedOriginalString ()
+ {
+ // funny, but it does not use the Parser's IsWellFormedOriginalString().
+ return EscapeString (OriginalString) == OriginalString;
+ }
+
+ // static methods
+
+ private const int MaxUriLength = 32766;
+
+ public static int Compare (Uri uri1, Uri uri2, UriComponents partsToCompare, UriFormat compareFormat, StringComparison comparisonType)
+ {
+ if ((comparisonType < StringComparison.CurrentCulture) || (comparisonType > StringComparison.OrdinalIgnoreCase)) {
+ string msg = Locale.GetText ("Invalid StringComparison value '{0}'", comparisonType);
+ throw new ArgumentException ("comparisonType", msg);
+ }
+
+ if ((uri1 == null) && (uri2 == null))
+ return 0;
+
+ string s1 = uri1.GetComponents (partsToCompare, compareFormat);
+ string s2 = uri2.GetComponents (partsToCompare, compareFormat);
+ return String.Compare (s1, s2, comparisonType);
+ }
+
+ //
+ // The rules for EscapeDataString
+ //
+ static bool NeedToEscapeDataChar (char b)
+ {
+ return !((b >= 'A' && b <= 'Z') ||
+ (b >= 'a' && b <= 'z') ||
+ (b >= '0' && b <= '9') ||
+ b == '_' || b == '~' || b == '!' || b == '\'' ||
+ b == '(' || b == ')' || b == '*' || b == '-' || b == '.');
+ }
+
+ public static string EscapeDataString (string stringToEscape)
+ {
+ if (stringToEscape == null)
+ throw new ArgumentNullException ("stringToEscape");
+
+ if (stringToEscape.Length > MaxUriLength) {
+ string msg = Locale.GetText ("Uri is longer than the maximum {0} characters.");
+ throw new UriFormatException (msg);
+ }
+ bool escape = false;
+ foreach (char c in stringToEscape){
+ if (NeedToEscapeDataChar (c)){
+ escape = true;
+ break;
+ }
+ }
+ if (!escape){
+ return stringToEscape;
+ }
+
+ StringBuilder sb = new StringBuilder ();
+ byte [] bytes = Encoding.UTF8.GetBytes (stringToEscape);
+ foreach (byte b in bytes){
+ if (NeedToEscapeDataChar ((char) b))
+ sb.Append (HexEscape ((char) b));
+ else
+ sb.Append ((char) b);
+ }
+ return sb.ToString ();
+ }
+
+ //
+ // The rules for EscapeUriString
+ //
+ static bool NeedToEscapeUriChar (char b)
+ {
+ return !((b >= 'A' && b <= 'Z') ||
+ (b >= 'a' && b <= 'z') ||
+ (b >= '&' && b <= ';') ||
+ b == '!' || b == '#' || b == '$' || b == '=' ||
+ b == '?' || b == '@' || b == '_' || b == '~');
+ }
+
+ public static string EscapeUriString (string stringToEscape)
+ {
+ if (stringToEscape == null)
+ throw new ArgumentNullException ("stringToEscape");
+
+ if (stringToEscape.Length > MaxUriLength) {
+ string msg = Locale.GetText ("Uri is longer than the maximum {0} characters.");
+ throw new UriFormatException (msg);
+ }
+
+ bool escape = false;
+ foreach (char c in stringToEscape){
+ if (NeedToEscapeUriChar (c)){
+ escape = true;
+ break;
+ }
+ }
+ if (!escape)
+ return stringToEscape;
+
+ StringBuilder sb = new StringBuilder ();
+ byte [] bytes = Encoding.UTF8.GetBytes (stringToEscape);
+ foreach (byte b in bytes){
+ if (NeedToEscapeUriChar ((char) b))
+ sb.Append (HexEscape ((char) b));
+ else
+ sb.Append ((char) b);
+ }
+ return sb.ToString ();
+ }
+
+ public static bool IsWellFormedUriString (string uriString, UriKind uriKind)
+ {
+ if (uriString == null)
+ return false;
+
+ Uri uri;
+ if (Uri.TryCreate (uriString, uriKind, out uri))
+ return uri.IsWellFormedOriginalString ();
+ return false;
+ }
+
+ public static bool TryCreate (string uriString, UriKind uriKind, out Uri result)
+ {
+ bool success;
+
+ Uri r = new Uri (uriString, uriKind, out success);
+ if (success) {
+ result = r;
+ return true;
+ }
+ result = null;
+ return false;
+ }
+
+ // [MonoTODO ("rework code to avoid exception catching")]
+ public static bool TryCreate (Uri baseUri, string relativeUri, out Uri result)
+ {
+ try {
+ // FIXME: this should call UriParser.Resolve
+ result = new Uri (baseUri, relativeUri);
+ return true;
+ } catch (UriFormatException) {
+ result = null;
+ return false;
+ }
+ }
+
+ //[MonoTODO ("rework code to avoid exception catching")]
+ public static bool TryCreate (Uri baseUri, Uri relativeUri, out Uri result)
+ {
+ try {
+ // FIXME: this should call UriParser.Resolve
+ result = new Uri (baseUri, relativeUri.OriginalString);
+ return true;
+ } catch (UriFormatException) {
+ result = null;
+ return false;
+ }
+ }
+
+ public static string UnescapeDataString (string stringToUnescape)
+ {
+ if (stringToUnescape == null)
+ throw new ArgumentNullException ("stringToUnescape");
+
+ if (stringToUnescape.IndexOf ('%') == -1 && stringToUnescape.IndexOf ('+') == -1)
+ return stringToUnescape;
+
+ StringBuilder output = new StringBuilder ();
+ long len = stringToUnescape.Length;
+ MemoryStream bytes = new MemoryStream ();
+ int xchar;
+
+ for (int i = 0; i < len; i++) {
+ if (stringToUnescape [i] == '%' && i + 2 < len && stringToUnescape [i + 1] != '%') {
+ if (stringToUnescape [i + 1] == 'u' && i + 5 < len) {
+ if (bytes.Length > 0) {
+ output.Append (GetChars (bytes, Encoding.UTF8));
+ bytes.SetLength (0);
+ }
+
+ xchar = GetChar (stringToUnescape, i + 2, 4);
+ if (xchar != -1) {
+ output.Append ((char) xchar);
+ i += 5;
+ }
+ else {
+ output.Append ('%');
+ }
+ }
+ else if ((xchar = GetChar (stringToUnescape, i + 1, 2)) != -1) {
+ bytes.WriteByte ((byte) xchar);
+ i += 2;
+ }
+ else {
+ output.Append ('%');
+ }
+ continue;
+ }
+
+ if (bytes.Length > 0) {
+ output.Append (GetChars (bytes, Encoding.UTF8));
+ bytes.SetLength (0);
+ }
+
+ output.Append (stringToUnescape [i]);
+ }
+
+ if (bytes.Length > 0) {
+ output.Append (GetChars (bytes, Encoding.UTF8));
+ }
+
+ bytes = null;
+ return output.ToString ();
+ }
+
+ private static int GetInt (byte b)
+ {
+ char c = (char) b;
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+
+ return -1;
+ }
+
+ private static int GetChar (string str, int offset, int length)
+ {
+ int val = 0;
+ int end = length + offset;
+ for (int i = offset; i < end; i++) {
+ char c = str [i];
+ if (c > 127)
+ return -1;
+
+ int current = GetInt ((byte) c);
+ if (current == -1)
+ return -1;
+ val = (val << 4) + current;
+ }
+
+ return val;
+ }
+
+ private static char [] GetChars (MemoryStream b, Encoding e)
+ {
+ return e.GetChars (b.GetBuffer (), 0, (int) b.Length);
+ }
+
+
+ private void EnsureAbsoluteUri ()
+ {
+ if (!IsAbsoluteUri)
+ throw new InvalidOperationException ("This operation is not supported for a relative URI.");
+ }
+#else
+ private void EnsureAbsoluteUri ()
+ {
+ }
+#endif
+ }
+}
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriFormatException.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriFormatException.cs
new file mode 100644
index 0000000..8506b93
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriFormatException.cs
@@ -0,0 +1,72 @@
+//
+// System.UriFormatException.cs
+//
+// Author:
+// Scott Sanders (scott@stonecobra.com)
+// Duncan Mak (duncan@ximian.com)
+//
+// (C) 2001 Scott Sanders
+// (C) 2002 Ximian, Inc.
+// Copyright (C) 2005, 2008 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Globalization;
+using System.Runtime.Serialization;
+
+namespace MonoForks.System {
+
+ [Serializable]
+ public class UriFormatException : FormatException, ISerializable
+ {
+
+ // Constructors
+ public UriFormatException ()
+ : base (Locale.GetText("Invalid URI format"))
+ {
+ }
+
+ public UriFormatException (string message)
+ : base (message)
+ {
+ }
+#if NET_2_1
+ public UriFormatException (string message, Exception exception)
+ : base (message, exception)
+ {
+ }
+#endif
+ protected UriFormatException (SerializationInfo info, StreamingContext context)
+ : base (info, context)
+ {
+ }
+
+ // Methods
+
+ // This effectively kills the LinkDemand from Exception.GetObjectData (if someone
+ // use the ISerializable interface to serialize the object). See unit tests.
+ void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
+ {
+ base.GetObjectData (info, context);
+ }
+ }
+}
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriHostNameType.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriHostNameType.cs
new file mode 100644
index 0000000..0ae13ef
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriHostNameType.cs
@@ -0,0 +1,62 @@
+// UriHostNameType.cs
+//
+// This code was automatically generated from
+// ECMA CLI XML Library Specification.
+// Generator: libgen.xsl [1.0; (C) Sergey Chaban (serge@wildwestsoftware.com)]
+// Created: Wed, 5 Sep 2001 06:33:14 UTC
+// Source file: AllTypes.xml
+// URL: http://msdn.microsoft.com/net/ecma/AllTypes.xml
+//
+// (C) 2001 Ximian, Inc. http://www.ximian.com
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+namespace MonoForks.System {
+
+
+ /// <summary>
+ /// </summary>
+ public enum UriHostNameType {
+
+ /// <summary>
+ /// </summary>
+ Unknown = 0,
+
+ /// <summary>
+ /// </summary>
+ Basic = 1,
+
+ /// <summary>
+ /// </summary>
+ Dns = 2,
+
+ /// <summary>
+ /// </summary>
+ IPv4 = 3,
+
+ /// <summary>
+ /// </summary>
+ IPv6 = 4,
+ } // UriHostNameType
+
+} // System
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriKind.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriKind.cs
new file mode 100644
index 0000000..2437c56
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriKind.cs
@@ -0,0 +1,42 @@
+//
+// System.UriKind enumeration
+//
+// Author:
+// Sebastien Pouliot <sebastien@ximian.com>
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MonoForks.System {
+
+#if NET_2_0
+ public
+#else
+ internal
+#endif
+ enum UriKind {
+
+ RelativeOrAbsolute,
+ Absolute,
+ Relative,
+ }
+}
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriPartial.cs b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriPartial.cs
new file mode 100644
index 0000000..f85bc23
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Mono.Forks/UriPartial.cs
@@ -0,0 +1,44 @@
+// UriPartial.cs
+//
+// This code was automatically generated from
+// ECMA CLI XML Library Specification.
+// Generator: libgen.xsl [1.0; (C) Sergey Chaban (serge@wildwestsoftware.com)]
+// Created: Wed, 5 Sep 2001 06:33:21 UTC
+// Source file: AllTypes.xml
+// URL: http://msdn.microsoft.com/net/ecma/AllTypes.xml
+//
+// (C) 2001 Ximian, Inc. http://www.ximian.com
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MonoForks.System {
+
+ public enum UriPartial {
+
+ Scheme = 0,
+ Authority = 1,
+ Path = 2,
+#if NET_2_0
+ Query
+#endif
+ }
+}
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Properties/AssemblyInfo.cs b/Runtime/Managed/CrossDomainPolicyParser/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..85cb64b
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Properties/AssemblyInfo.cs
@@ -0,0 +1,37 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("CrossDomainPolicyParser")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("CrossDomainPolicyParser")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2010")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("f17522fc-5e6b-43ea-baf9-8af61d9740f0")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: InternalsVisibleTo("CrossDomainPolicyParserTests")]
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Tests/CrossDomainPolicyParserTests.csproj b/Runtime/Managed/CrossDomainPolicyParser/Tests/CrossDomainPolicyParserTests.csproj
new file mode 100644
index 0000000..0e27635
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Tests/CrossDomainPolicyParserTests.csproj
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.30729</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{5C04DB87-3B34-43B4-B164-86CE4361DC7B}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>CrossDomainPolicyParserTests</RootNamespace>
+ <AssemblyName>CrossDomainPolicyParserTests</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data.DataSetExtensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ <Reference Include="nunit.framework, Version=2.5.3.9345, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\..\..\Tools\NUnit\bin\nunit.framework.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="FlashPolicyParserSocketTests.cs" />
+ <Compile Include="FlashPolicyParserTests.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="UriToolsTests.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\CrossDomainPolicyParser.csproj">
+ <Project>{31C2F345-D887-49DD-A1F6-741CABD74A42}</Project>
+ <Name>CrossDomainPolicyParser</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Tests/FlashPolicyParserSocketTests.cs b/Runtime/Managed/CrossDomainPolicyParser/Tests/FlashPolicyParserSocketTests.cs
new file mode 100644
index 0000000..8e0f62b
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Tests/FlashPolicyParserSocketTests.cs
@@ -0,0 +1,124 @@
+using System.IO;
+using System.Text;
+using MonoForks.System.Windows.Browser.Net;
+using NUnit.Framework;
+
+namespace CrossDomainPolicyParserTests
+{
+ [TestFixture]
+ public class FlashPolicyParserSocketTests
+ {
+ [Test]
+ public void AllDomains_AllPorts_IsAllowed()
+ {
+ string policy = @"<?xml version='1.0'?>
+<cross-domain-policy>
+ <allow-access-from domain=""*"" to-ports=""*"" />
+</cross-domain-policy>";
+ Assert.IsTrue(RequestAllowed(policy, 123));
+ }
+
+ [Test]
+ public void AllDomains_AllPorts_Trailing0_IsAllowed()
+ {
+ string policy = @"<?xml version='1.0'?>
+<cross-domain-policy>
+ <allow-access-from domain=""*"" to-ports=""*"" />
+</cross-domain-policy>" + "\0";
+ Assert.IsTrue(RequestAllowed(policy, 123));
+ }
+
+ [Test]
+ public void AllDomains_UsingSpecificPorts_IsAllowed()
+ {
+ string policy = @"<?xml version='1.0'?>
+<cross-domain-policy>
+ <allow-access-from domain=""*"" to-ports=""1010,1020"" />
+</cross-domain-policy>";
+ Assert.IsTrue(RequestAllowed(policy, 1020));
+ }
+
+ [Test]
+ public void AllDomains_OutsideSpecificPorts_IsDisAllowed()
+ {
+ string policy = @"<?xml version='1.0'?>
+<cross-domain-policy>
+ <allow-access-from domain=""*"" to-ports=""1010,1030"" />
+</cross-domain-policy>";
+ Assert.IsFalse(RequestAllowed(policy, 1020));
+ }
+
+ [Test]
+ public void AllDomains_OutsidePortRange_IsDisAllowed()
+ {
+ string policy = @"<?xml version='1.0'?>
+<cross-domain-policy>
+ <allow-access-from domain=""*"" to-ports=""1030-1040"" />
+</cross-domain-policy>";
+ Assert.IsFalse(RequestAllowed(policy, 1020));
+ }
+
+ [Test]
+ public void AllDomains_InsidePortRange_IsAllowed()
+ {
+ string policy = @"<?xml version='1.0'?>
+<cross-domain-policy>
+ <allow-access-from domain=""*"" to-ports=""1030-1040"" />
+</cross-domain-policy>";
+ Assert.IsTrue(RequestAllowed(policy, 1035));
+ }
+
+ [Test]
+ public void PolicyReceivedFromHigherThan1024_DisallowsAccessToBelow1024Ports()
+ {
+ string policy = @"<?xml version='1.0'?>
+<cross-domain-policy>
+ <allow-access-from domain=""*"" to-ports=""1000-1040"" />
+</cross-domain-policy>";
+ Assert.IsFalse(RequestAllowed(policy, 1010, 1300));
+ }
+
+ [Test]
+ public void PolicyReceivedFromHigherThan1024_AllowsAccessToAbove1024Ports()
+ {
+ string policy = @"<?xml version='1.0'?>
+<cross-domain-policy>
+ <allow-access-from domain=""*"" to-ports=""1000-1040"" />
+</cross-domain-policy>";
+ Assert.IsTrue(RequestAllowed(policy, 1035, 1300));
+ }
+
+ [Test]
+ public void PolicyReceivedFromLowerThan1024_AllowsAccessToBelow1024Ports()
+ {
+ string policy = @"<?xml version='1.0'?>
+<cross-domain-policy>
+ <allow-access-from domain=""*"" to-ports=""1000-1040"" />
+</cross-domain-policy>";
+ Assert.IsTrue(RequestAllowed(policy, 1010, 1000));
+ }
+ [Test]
+ public void PolicyReceivedFromLowerThan1024_AllowsAccessToAbove1024Ports()
+ {
+ string policy = @"<?xml version='1.0'?>
+<cross-domain-policy>
+ <allow-access-from domain=""*"" to-ports=""1000-1040"" />
+</cross-domain-policy>";
+ Assert.IsTrue(RequestAllowed(policy, 1030, 1000));
+ }
+
+ private bool RequestAllowed(string xdomain, int port)
+ {
+ return RequestAllowed(xdomain, port, 843);
+ }
+
+ private bool RequestAllowed(string xdomain, int port, int policyport)
+ {
+ var ms = new MemoryStream(Encoding.UTF8.GetBytes(xdomain));
+ var policy = FlashCrossDomainPolicy.FromStream(ms);
+ policy.PolicyPort = policyport;
+ return policy.IsSocketConnectionAllowed(port);
+ }
+
+ }
+} \ No newline at end of file
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Tests/FlashPolicyParserTests.cs b/Runtime/Managed/CrossDomainPolicyParser/Tests/FlashPolicyParserTests.cs
new file mode 100644
index 0000000..976ba92
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Tests/FlashPolicyParserTests.cs
@@ -0,0 +1,209 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using MonoForks.Mono.Xml;
+using MonoForks.System.Net;
+using NUnit.Framework;
+using MonoForks.System.Windows.Browser.Net;
+using UnityEngine;
+using Uri = MonoForks.System.Uri;
+
+namespace CrossDomainPolicyParserTests
+{
+ [TestFixture]
+ public class FlashPolicyParserTests
+ {
+ static string XDomainGlobal =
+@"<?xml version=""1.0""?>
+<!DOCTYPE cross-domain-policy SYSTEM ""http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd"">
+<cross-domain-policy>
+ <allow-access-from domain=""*"" />
+</cross-domain-policy>";
+
+ string http_hosted = "http://www.host.com/coolgame.unity3d";
+ string https_hosted = "https://secure.host.net/coolgame.unity3d";
+ string file_hosted = "file:///coolgame.unity3";
+
+ [Test]
+ public void GlobalXDomainAcceptsRequestOnSameDomain()
+ {
+ string requesturl = "http://www.mach8.nl/index.html";
+
+ Assert.IsTrue(RequestAllowed(XDomainGlobal, requesturl, http_hosted));
+ }
+ [Test]
+ public void GlobalXDomainAcceptsRequestOnSubDomain()
+ {
+ string requesturl = "http://subdomain.mach8.nl/index.html";
+
+ Assert.IsTrue(RequestAllowed(XDomainGlobal, requesturl, http_hosted));
+ }
+
+ [Test]
+ public void GlobalXDomainAllowsSecureRequestWhenHostedNonSecure()
+ {
+ string requesturl = "https://www.mach8.nl/index.html";
+
+ Assert.IsTrue(RequestAllowed(XDomainGlobal, requesturl, http_hosted));
+ }
+ [Test]
+ public void GlobalXDomainAcceptsSecureRequestWhenHostedSecure()
+ {
+ string requesturl = "https://www.mach8.nl/index.html";
+
+ Assert.IsTrue(RequestAllowed(XDomainGlobal, requesturl, https_hosted));
+ }
+ [Test]
+ public void GlobalXDomainDeniesNonSecureRequestWhenHostedSecure()
+ {
+ string requesturl = "http://www.mach8.nl/index.html";
+ Assert.IsFalse(RequestAllowed(XDomainGlobal, requesturl, https_hosted));
+ }
+
+ [Test]
+ public void AllDomain_Secure()
+ {
+ string policy = @"<?xml version=""1.0""?>
+<!DOCTYPE cross-domain-policy SYSTEM ""http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd"">
+<cross-domain-policy>
+ <allow-access-from domain=""*"" secure=""true""/>
+</cross-domain-policy>";
+
+ Assert.IsTrue(RequestAllowed(policy, "http://www.host.com", http_hosted));
+ }
+
+ [Test]
+ public void WhenRequestURLMatchesWildCardAccessIsAllowed()
+ {
+ string policy = @"<?xml version=""1.0""?>
+<cross-domain-policy>
+ <allow-access-from domain=""*.mydomain.nl"" />
+</cross-domain-policy>";
+
+ Assert.IsTrue(RequestAllowed(policy, "http://subdomain.mydomain.nl", http_hosted));
+ }
+
+ [Test]
+ public void WhenRequestURLDoesNotMatchWildCardAccessIsDisallowed()
+ {
+ string policy = @"<?xml version=""1.0""?>
+<cross-domain-policy>
+ <allow-access-from domain=""*.mydomain.nl"" />
+</cross-domain-policy>";
+
+ Assert.IsFalse(RequestAllowed(policy, "http://subdomain.myotherdomain.nl", http_hosted));
+ }
+
+
+ [Test]
+ public void AllDomains_NoDTD()
+ {
+ string policy = @"<?xml version='1.0'?><cross-domain-policy><allow-access-from domain='*'/></cross-domain-policy>";
+
+ Assert.IsTrue(RequestAllowed(policy, "http://www.host.com", http_hosted));
+ }
+
+ [Test]
+ public void AllDomains_NoXmlHeader()
+ {
+ string policy = @"<cross-domain-policy>
+ <allow-access-from domain=""*"" to-ports=""*""/>
+</cross-domain-policy> ";
+ Assert.IsTrue(RequestAllowed(policy, "http://www.host.com", http_hosted));
+ }
+
+ [Test]
+ public void AllDomains_PermittedCrossDomainPolicies_All()
+ {
+ // 'all' is the default value
+ // http://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html#site-control-permitted-cross-domain-policies
+ string policy = @"<?xml version='1.0'?>
+<!DOCTYPE cross-domain-policy SYSTEM 'http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd'>
+<cross-domain-policy>
+ <site-control permitted-cross-domain-policies='all' />
+ <allow-access-from domain='*' />
+</cross-domain-policy>";
+
+ Assert.IsTrue(RequestAllowed(policy, "http://www.host.com", http_hosted));
+ }
+
+ [Test]
+ public void AllDomains_PermittedCrossDomainPolicies_MasterOnly()
+ {
+ string policy = @"<?xml version='1.0'?>
+<!DOCTYPE cross-domain-policy SYSTEM 'http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd'>
+<cross-domain-policy>
+ <site-control permitted-cross-domain-policies='master-only' />
+ <allow-access-from domain='*' />
+</cross-domain-policy>";
+
+ Assert.IsTrue(RequestAllowed(policy, "http://www.host.com", http_hosted));
+ }
+
+ [Test]
+ public void AllDomains_PermittedCrossDomainPolicies_None()
+ {
+ string policy = @"<?xml version='1.0'?>
+<!DOCTYPE cross-domain-policy SYSTEM 'http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd'>
+<cross-domain-policy>
+ <site-control permitted-cross-domain-policies='none' />
+ <allow-access-from domain='*' />
+</cross-domain-policy>";
+ Assert.IsFalse(RequestAllowed(policy, "http://www.host.com", http_hosted));
+ }
+
+ [Test]
+ public void AllDomains_PermittedCrossDomainPolicies_ByContentType()
+ {
+ string policy = @"<?xml version='1.0'?>
+<!DOCTYPE cross-domain-policy SYSTEM 'http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd'>
+<cross-domain-policy>
+ <site-control permitted-cross-domain-policies='by-content-type' />
+ <allow-access-from domain='*' />
+</cross-domain-policy>";
+ Assert.IsFalse(RequestAllowed(policy, "http://www.host.com", http_hosted));
+ }
+
+ [Test]
+ public void AllDomains_PermittedCrossDomainPolicies_ByFtpFilename()
+ {
+ string policy = @"<?xml version='1.0'?>
+<!DOCTYPE cross-domain-policy SYSTEM 'http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd'>
+<cross-domain-policy>
+ <site-control permitted-cross-domain-policies='by-ftp-filename' />
+ <allow-access-from domain='*' />
+</cross-domain-policy>";
+ Assert.IsTrue(RequestAllowed(policy, "http://www.host.com", http_hosted));
+ }
+
+ [Test]
+ [ExpectedException(typeof(MiniParser.XMLError))]
+ public void IllformedPolicyIsRejected()
+ {
+ FlashCrossDomainPolicyFromString("bogus", "http://www.host.com");
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void EmptyPolicyStringIsRejected()
+ {
+ FlashCrossDomainPolicyFromString("", "http://www.host.com");
+ }
+
+ private bool RequestAllowed(string xdomain, string requesturl, string hosturl)
+ {
+ FlashCrossDomainPolicy policy = FlashCrossDomainPolicyFromString(xdomain, hosturl);
+ var wr = new WebRequest(new Uri(requesturl), new Dictionary<string, string>());
+ return policy.IsAllowed(wr);
+ }
+
+ private FlashCrossDomainPolicy FlashCrossDomainPolicyFromString(string xdomain, string hosturl)
+ {
+ UnityCrossDomainHelper.SetWebSecurityHostUriDelegate(() => hosturl);
+
+ var ms = new MemoryStream(Encoding.UTF8.GetBytes(xdomain));
+ return FlashCrossDomainPolicy.FromStream(ms);
+ }
+ }
+}
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Tests/Properties/AssemblyInfo.cs b/Runtime/Managed/CrossDomainPolicyParser/Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..5fd4c99
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("CrossDomainPolicyParserTests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("CrossDomainPolicyParserTests")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2010")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c40d4596-1c43-4f37-b7fa-545a543577f3")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Runtime/Managed/CrossDomainPolicyParser/Tests/UriToolsTests.cs b/Runtime/Managed/CrossDomainPolicyParser/Tests/UriToolsTests.cs
new file mode 100644
index 0000000..7a29b00
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/Tests/UriToolsTests.cs
@@ -0,0 +1,23 @@
+using CrossDomainPolicyParser;
+using MonoForks.System;
+using NUnit.Framework;
+
+namespace CrossDomainPolicyParserTests
+{
+ [TestFixture]
+ public class UriToolsTests
+ {
+ [Test]
+ public void MakeUriWorksForRelativeUri()
+ {
+ Uri uri = UriTools.MakeUri("http://mydomain.com/mygame.unity3d", "test.png");
+ Assert.AreEqual("mydomain.com",uri.Host);
+ }
+ [Test]
+ public void MakeUriWorksForAbsoluteUri()
+ {
+ Uri uri = UriTools.MakeUri("http://mydomain.com/mygame.unity3d", "http://www.google.com/test.png");
+ Assert.AreEqual("www.google.com", uri.Host);
+ }
+ }
+}
diff --git a/Runtime/Managed/CrossDomainPolicyParser/UnityCrossDomainHelper.cs b/Runtime/Managed/CrossDomainPolicyParser/UnityCrossDomainHelper.cs
new file mode 100644
index 0000000..e43ba59
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/UnityCrossDomainHelper.cs
@@ -0,0 +1,213 @@
+using System;
+using System.Reflection;
+using System.Security;
+using System.Text;
+using CrossDomainPolicyParser;
+using System.IO;
+using System.Collections.Generic;
+using MonoForks.System.Windows.Browser.Net;
+
+internal class Log
+{
+ public delegate void LogDelegate(string msg);
+
+ static LogDelegate logger;
+
+ static public void SetLog(LogDelegate ld)
+ {
+ logger = ld;
+ }
+
+ static public void Msg(string msg)
+ {
+ if (logger != null) logger(msg);
+ }
+}
+
+namespace UnityEngine
+{
+ public class UnityCrossDomainHelper
+ {
+ public enum SecurityPolicy
+ {
+ DontKnowYet = 0,
+ AllowAccess = 1,
+ DenyAccess = 2
+ }
+
+ public delegate string GetWebSecurityHostUriDelegate();
+ private static GetWebSecurityHostUriDelegate getWebSecurityHostUriDelegate = DefaultGetWebSecurityHostUri;
+ static WWWPolicyProvider wwwPolicyProvider = new WWWPolicyProvider();
+
+ static public string GetWebSecurityHostUri()
+ {
+ return getWebSecurityHostUriDelegate();
+ }
+
+ static internal void SetWebSecurityHostUriDelegate(GetWebSecurityHostUriDelegate d)
+ {
+ getWebSecurityHostUriDelegate = d;
+ }
+
+ static string DefaultGetWebSecurityHostUri()
+ {
+ return Application.webSecurityHostUrl;
+ }
+
+ public static void ClearCache()
+ {
+ wwwPolicyProvider.ClearCache();
+ CrossDomainPolicyManager.ClearCache();
+ }
+
+ interface IPolicyProvider
+ {
+ Stream GetPolicy(string url);
+ }
+
+ class WWWPolicyProvider : IPolicyProvider
+ {
+ Dictionary<string,WWW> policyDownloads = new Dictionary<string, WWW>();
+
+ public void ClearCache()
+ {
+ policyDownloads.Clear();
+ }
+
+ public Stream GetPolicy(string policyurl)
+ {
+ WWW downloadInProgress = null;
+ policyDownloads.TryGetValue(policyurl, out downloadInProgress);
+
+ if (downloadInProgress != null)
+ {
+ if (!downloadInProgress.isDone) return null;
+
+ bool statuscodeOK = downloadInProgress.error == null;
+
+ if (!statuscodeOK) throw new InvalidOperationException("Unable to download policy");
+ if (statuscodeOK)
+ {
+ Log.Msg("Download had OK statuscode");
+ Log.Msg("Received the following crossdomain.xml");
+ Log.Msg("----------");
+ Log.Msg(downloadInProgress.text);
+ Log.Msg("----------");
+ return new MemoryStream(downloadInProgress.bytes);
+ }
+
+ }
+
+ //okay, we hadn't started downloading the policy, lets start the policy download.
+ var www = new WWW(policyurl);
+ policyDownloads.Add(policyurl, www);
+ return null;
+ }
+ }
+
+ class WebRequestPolicyProvider : IPolicyProvider
+ {
+ private MethodInfo methodinfo;
+
+ public WebRequestPolicyProvider(MethodInfo mi)
+ {
+ methodinfo = mi;
+ }
+
+ [System.Security.SecuritySafeCritical]
+ public Stream GetPolicy(string policy_url)
+ {
+ var proxy = System.Environment.GetEnvironmentVariable("UNITY_PROXYSERVER");
+ if (string.IsNullOrEmpty(proxy))
+ proxy = null;
+ object result = methodinfo.Invoke(null, new object[] {policy_url, proxy});
+ return (Stream) result;
+ }
+ }
+
+ public static SecurityPolicy GetSecurityPolicy(string requesturi_string)
+ {
+ return GetSecurityPolicy(requesturi_string, wwwPolicyProvider);
+ }
+
+ public static bool GetSecurityPolicyForDotNetWebRequest(string requesturi_string, MethodInfo policyProvidingMethod)
+ {
+ var provider = new WebRequestPolicyProvider(policyProvidingMethod);
+
+ return GetSecurityPolicy(requesturi_string, provider) == SecurityPolicy.AllowAccess;
+ }
+
+ static SecurityPolicy GetSecurityPolicy(string requesturi_string, IPolicyProvider policyProvider)
+ {
+ var requesturi = UriTools.MakeUri(Application.webSecurityHostUrl, requesturi_string);
+ if (requesturi.Scheme=="file")
+ {
+ //Editor-In-WebMode is allowed to read files from file://
+ if (Application.isEditor) return SecurityPolicy.AllowAccess;
+
+ //Webplayer itself is allowed to read files from file:// as long as it is hosted on file:// itself as well.
+ var hostedat = new MonoForks.System.Uri(Application.webSecurityHostUrl);
+ if (Application.isWebPlayer && hostedat.Scheme == "file") return SecurityPolicy.AllowAccess;
+
+ //other scenarios of accessing file:// are not allowed
+ return SecurityPolicy.DenyAccess;
+ }
+
+ //todo: force absolute
+ ICrossDomainPolicy policy = CrossDomainPolicyManager.GetCachedWebPolicy(requesturi);
+ if (policy != null)
+ {
+ var request = new MonoForks.System.Net.WebRequest(requesturi, new Dictionary<string, string>());
+ SecurityPolicy allowed = policy.IsAllowed(request) ? SecurityPolicy.AllowAccess : SecurityPolicy.DenyAccess;
+ return allowed;
+ }
+
+ if (ShouldEnableLogging())
+ Log.SetLog(Console.WriteLine);
+
+ Log.Msg("Determining crossdomain.xml location for request: " + requesturi);
+ var policyURI = CrossDomainPolicyManager.GetFlashPolicyUri(requesturi);
+
+ Stream s;
+ try
+ {
+ s = policyProvider.GetPolicy(policyURI.ToString());
+ if (s == null) return SecurityPolicy.DontKnowYet;
+ CrossDomainPolicyManager.BuildFlashPolicy(true, policyURI, s, new Dictionary<string, string>());
+ } catch (InvalidOperationException)
+ {
+ return SecurityPolicy.DenyAccess;
+ }
+ catch (MonoForks.Mono.Xml.MiniParser.XMLError xe)
+ {
+ Debug.Log (string.Format ("Error reading crossdomain policy: {0}", xe.Message));
+ return SecurityPolicy.DenyAccess;
+ }
+ return GetSecurityPolicy(requesturi_string, policyProvider);
+ }
+
+ [SecuritySafeCritical]
+ private static bool ShouldEnableLogging()
+ {
+ return Environment.GetEnvironmentVariable("ENABLE_CROSSDOMAIN_LOGGING")=="1";
+ }
+
+ static public bool CheckSocketEndPoint(string connecting_to_ip, int port)
+ {
+ Log.Msg("CheckSocketEndpoint called for "+connecting_to_ip+" with port: "+port);
+
+ if (!Application.webSecurityEnabled) return true;
+
+ bool result = CrossDomainPolicyManager.CheckSocketEndPoint(connecting_to_ip,port);
+ Log.Msg("CheckSocketENdpoint returns :"+result);
+ return result;
+ }
+ static public bool PrefetchSocketPolicy(string ip, int policyport, int timeout)
+ {
+ if (!Application.webSecurityEnabled) return false;
+ var policy = CrossDomainPolicyManager.FlashCrossDomainPolicyFor(ip, policyport, timeout);
+ return policy != FlashCrossDomainPolicy.DenyPolicy;
+ }
+
+ }
+}
diff --git a/Runtime/Managed/CrossDomainPolicyParser/UriTools.cs b/Runtime/Managed/CrossDomainPolicyParser/UriTools.cs
new file mode 100644
index 0000000..6fafc86
--- /dev/null
+++ b/Runtime/Managed/CrossDomainPolicyParser/UriTools.cs
@@ -0,0 +1,20 @@
+using System;
+using Uri = MonoForks.System.Uri;
+
+namespace CrossDomainPolicyParser
+{
+ class UriTools
+ {
+ public static Uri MakeUri(string gameurl, string url)
+ {
+ if ((!url.ToLower().StartsWith("http://")) && (!url.ToLower().StartsWith("https://")) && (!url.ToLower().StartsWith("file://")))
+ url = GetBaseUrl(gameurl) + "/" +url;
+ Log.Msg("About to parse url: " + url);
+ return new Uri(url);
+ }
+ static string GetBaseUrl(string url)
+ {
+ return url.Substring(0, url.LastIndexOf('/'));
+ }
+ }
+}
diff --git a/Runtime/Math/AnimationCurve.cpp b/Runtime/Math/AnimationCurve.cpp
new file mode 100644
index 0000000..07b976c
--- /dev/null
+++ b/Runtime/Math/AnimationCurve.cpp
@@ -0,0 +1,964 @@
+#include "UnityPrefix.h"
+#include "AnimationCurve.h"
+#include "Runtime/Utilities/LogAssert.h"
+using namespace std;
+
+#define kOneThird (1.0F / 3.0F)
+#define kMaxTan 5729577.9485111479F
+
+int ToInternalInfinity (int pre);
+int FromInternalInfinity (int pre);
+
+int ToInternalInfinity (int pre)
+{
+ if (pre == kRepeat)
+ return AnimationCurve::kInternalRepeat;
+ else if (pre == kPingPong)
+ return AnimationCurve::kInternalPingPong;
+ else
+ return AnimationCurve::kInternalClamp;
+}
+
+int FromInternalInfinity (int pre)
+{
+ if (pre == AnimationCurve::kInternalRepeat)
+ return kRepeat;
+ else if (pre == AnimationCurve::kInternalPingPong)
+ return kPingPong;
+ else
+ return kClampForever;
+}
+
+template<class T>
+KeyframeTpl<T>::KeyframeTpl (float t, const T& v)
+{
+ time = t;
+ value = v;
+ inSlope = Zero<T>();
+ outSlope = Zero<T>();
+
+#if UNITY_EDITOR
+ tangentMode = 0;
+#endif
+}
+
+
+template<class T>
+void AnimationCurveTpl<T>::InvalidateCache ()
+{
+ m_Cache.time = std::numeric_limits<float>::infinity ();
+ m_Cache.index = 0;
+ m_ClampCache.time = std::numeric_limits<float>::infinity ();
+ m_ClampCache.index = 0;
+}
+
+template<class T>
+pair<float, float> AnimationCurveTpl<T>::GetRange () const
+{
+ if (!m_Curve.empty ())
+ return make_pair (m_Curve[0].time, m_Curve.back ().time);
+ else
+ return make_pair (std::numeric_limits<float>::infinity (), -std::numeric_limits<float>::infinity ());
+}
+
+
+
+///@TODO: Handle step curves correctly
+template<class T>
+void AnimationCurveTpl<T>::EvaluateWithoutCache (float curveT, T& output)const
+{
+ DebugAssertIf (!IsValid ());
+ curveT = WrapTime (curveT);
+
+ int lhsIndex, rhsIndex;
+ FindIndexForSampling (m_Cache, curveT, lhsIndex, rhsIndex);
+ const Keyframe& lhs = m_Curve[lhsIndex];
+ const Keyframe& rhs = m_Curve[rhsIndex];
+
+ float dx = rhs.time - lhs.time;
+ T m1;
+ T m2;
+ float t;
+ if (dx != 0.0F)
+ {
+ t = (curveT - lhs.time) / dx;
+ m1 = lhs.outSlope * dx;
+ m2 = rhs.inSlope * dx;
+ }
+ else
+ {
+ t = 0.0F;
+ m1 = Zero<T>();
+ m2 = Zero<T>();
+ }
+
+ output = HermiteInterpolate (t, lhs.value, m1, m2, rhs.value);
+ HandleSteppedCurve(lhs, rhs, output);
+ DebugAssertIf(!IsFinite(output));
+}
+
+template<class T>
+inline void EvaluateCache (const typename AnimationCurveTpl<T>::Cache& cache, float curveT, T& output)
+{
+// DebugAssertIf (curveT < cache.time - kCurveTimeEpsilon || curveT > cache.timeEnd + kCurveTimeEpsilon);
+ float t = curveT - cache.time;
+ output = (t * (t * (t * cache.coeff[0] + cache.coeff[1]) + cache.coeff[2])) + cache.coeff[3];
+ DebugAssertIf (!IsFinite(output));
+}
+
+void SetupStepped (float* coeff, const KeyframeTpl<float>& lhs, const KeyframeTpl<float>& rhs)
+{
+ // If either of the tangents in the segment are set to stepped, make the constant value equal the value of the left key
+ if (lhs.outSlope == std::numeric_limits<float>::infinity() || rhs.inSlope == std::numeric_limits<float>::infinity())
+ {
+ coeff[0] = 0.0F;
+ coeff[1] = 0.0F;
+ coeff[2] = 0.0F;
+ coeff[3] = lhs.value;
+ }
+}
+
+void HandleSteppedCurve (const KeyframeTpl<float>& lhs, const KeyframeTpl<float>& rhs, float& value)
+{
+ if (lhs.outSlope == std::numeric_limits<float>::infinity() || rhs.inSlope == std::numeric_limits<float>::infinity())
+ value = lhs.value;
+}
+
+void HandleSteppedTangent (const KeyframeTpl<float>& lhs, const KeyframeTpl<float>& rhs, float& tangent)
+{
+ if (lhs.outSlope == std::numeric_limits<float>::infinity() || rhs.inSlope == std::numeric_limits<float>::infinity())
+ tangent = std::numeric_limits<float>::infinity();
+}
+
+
+void SetupStepped (Vector3f* coeff, const KeyframeTpl<Vector3f>& lhs, const KeyframeTpl<Vector3f>& rhs)
+{
+ for (int i=0;i<3;i++)
+ {
+ // If either of the tangents in the segment are set to stepped, make the constant value equal the value of the left key
+ if (lhs.outSlope[i] == std::numeric_limits<float>::infinity() || rhs.inSlope[i] == std::numeric_limits<float>::infinity())
+ {
+ coeff[0][i] = 0.0F;
+ coeff[1][i] = 0.0F;
+ coeff[2][i] = 0.0F;
+ coeff[3][i] = lhs.value[i];
+ }
+ }
+}
+
+void HandleSteppedCurve (const KeyframeTpl<Vector3f>& lhs, const KeyframeTpl<Vector3f>& rhs, Vector3f& value)
+{
+ for (int i=0;i<3;i++)
+ {
+ if (lhs.outSlope[i] == std::numeric_limits<float>::infinity() || rhs.inSlope[i] == std::numeric_limits<float>::infinity())
+ value[i] = lhs.value[i];
+ }
+}
+
+void HandleSteppedTangent (const KeyframeTpl<Vector3f>& lhs, const KeyframeTpl<Vector3f>& rhs, Vector3f& value)
+{
+ for (int i=0;i<3;i++)
+ {
+ if (lhs.outSlope[i] == std::numeric_limits<float>::infinity() || rhs.inSlope[i] == std::numeric_limits<float>::infinity())
+ value[i] = std::numeric_limits<float>::infinity();
+ }
+}
+
+void SetupStepped (Quaternionf* coeff, const KeyframeTpl<Quaternionf>& lhs, const KeyframeTpl<Quaternionf>& rhs)
+{
+ // If either of the tangents in the segment are set to stepped, make the constant value equal the value of the left key
+ if (lhs.outSlope[0] == std::numeric_limits<float>::infinity() || rhs.inSlope[0] == std::numeric_limits<float>::infinity() ||
+ lhs.outSlope[1] == std::numeric_limits<float>::infinity() || rhs.inSlope[1] == std::numeric_limits<float>::infinity() ||
+ lhs.outSlope[2] == std::numeric_limits<float>::infinity() || rhs.inSlope[2] == std::numeric_limits<float>::infinity() ||
+ lhs.outSlope[3] == std::numeric_limits<float>::infinity() || rhs.inSlope[3] == std::numeric_limits<float>::infinity() )
+ {
+ for (int i=0;i<4;i++)
+ {
+ coeff[0][i] = 0.0F;
+ coeff[1][i] = 0.0F;
+ coeff[2][i] = 0.0F;
+ coeff[3][i] = lhs.value[i];
+ }
+ }
+}
+
+void HandleSteppedCurve (const KeyframeTpl<Quaternionf>& lhs, const KeyframeTpl<Quaternionf>& rhs, Quaternionf& value)
+{
+ if (lhs.outSlope[0] == std::numeric_limits<float>::infinity() || rhs.inSlope[0] == std::numeric_limits<float>::infinity() ||
+ lhs.outSlope[1] == std::numeric_limits<float>::infinity() || rhs.inSlope[1] == std::numeric_limits<float>::infinity() ||
+ lhs.outSlope[2] == std::numeric_limits<float>::infinity() || rhs.inSlope[2] == std::numeric_limits<float>::infinity() ||
+ lhs.outSlope[3] == std::numeric_limits<float>::infinity() || rhs.inSlope[3] == std::numeric_limits<float>::infinity() )
+ {
+ value = lhs.value;
+ }
+}
+
+void HandleSteppedTangent (const KeyframeTpl<Quaternionf>& lhs, const KeyframeTpl<Quaternionf>& rhs, Quaternionf& tangent)
+{
+ for (int i=0;i<4;i++)
+ {
+ if (lhs.outSlope[i] == std::numeric_limits<float>::infinity() || rhs.inSlope[i] == std::numeric_limits<float>::infinity())
+ tangent[i] = std::numeric_limits<float>::infinity();
+ }
+}
+
+template<class T>
+void AnimationCurveTpl<T>::CalculateCacheData (Cache& cache, int lhsIndex, int rhsIndex, float timeOffset) const
+{
+ const Keyframe& lhs = m_Curve[lhsIndex];
+ const Keyframe& rhs = m_Curve[rhsIndex];
+// DebugAssertIf (timeOffset < -0.001F || timeOffset - 0.001F > rhs.time - lhs.time);
+ cache.index = lhsIndex;
+ cache.time = lhs.time + timeOffset;
+ cache.timeEnd = rhs.time + timeOffset;
+ cache.index = lhsIndex;
+
+ float dx, length;
+ T dy;
+ T m1, m2, d1, d2;
+
+ dx = rhs.time - lhs.time;
+ dx = max(dx, 0.0001F);
+ dy = rhs.value - lhs.value;
+ length = 1.0F / (dx * dx);
+
+ m1 = lhs.outSlope;
+ m2 = rhs.inSlope;
+ d1 = m1 * dx;
+ d2 = m2 * dx;
+
+ cache.coeff[0] = (d1 + d2 - dy - dy) * length / dx;
+ cache.coeff[1] = (dy + dy + dy - d1 - d1 - d2) * length;
+ cache.coeff[2] = m1;
+ cache.coeff[3] = lhs.value;
+ SetupStepped(cache.coeff, lhs, rhs);
+
+ DebugAssertIf(!IsFinite(cache.coeff[0]));
+ DebugAssertIf(!IsFinite(cache.coeff[1]));
+ DebugAssertIf(!IsFinite(cache.coeff[2]));
+ DebugAssertIf(!IsFinite(cache.coeff[3]));
+}
+
+// When we look for the next index, how many keyframes do we just loop ahead instead of binary searching?
+#define SEARCH_AHEAD 3
+///@TODO: Cleanup old code to completely get rid of this
+template<class T>
+int AnimationCurveTpl<T>::FindIndex (const Cache& cache, float curveT) const
+{
+ #if SEARCH_AHEAD >= 0
+ int cacheIndex = cache.index;
+ if (cacheIndex != -1)
+ {
+ // We can not use the cache time or time end since that is in unwrapped time space!
+ float time = m_Curve[cacheIndex].time;
+
+ if (curveT > time)
+ {
+ if (cacheIndex + SEARCH_AHEAD < static_cast<int>(m_Curve.size()))
+ {
+ for (int i=0;i<SEARCH_AHEAD;i++)
+ {
+ if (curveT < m_Curve[cacheIndex + i + 1].time)
+ return cacheIndex + i;
+ }
+ }
+ }
+ else
+ {
+ if (cacheIndex - SEARCH_AHEAD >= 0)
+ {
+ for (int i=0;i<SEARCH_AHEAD;i++)
+ {
+ if (curveT > m_Curve[cacheIndex - i - 1].time)
+ return cacheIndex - i - 1;
+ }
+ }
+ }
+ }
+
+ #endif
+
+ ///@ use cache to index into next if not possible use binary search
+ const_iterator i = std::lower_bound (m_Curve.begin (), m_Curve.end (), curveT, KeyframeCompare());
+ int index = distance (m_Curve.begin (), i);
+ index--;
+ index = min<int> (m_Curve.size () - 2, index);
+ index = max<int> (0, index);
+
+ return index;
+}
+
+///@TODO: Cleanup old code to completely get rid of this
+template<class T>
+int AnimationCurveTpl<T>::FindIndex (float curveT) const
+{
+ pair<float, float> range = GetRange ();
+ if (curveT <= range.first || curveT >= range.second)
+ return -1;
+
+ const_iterator i = std::lower_bound (m_Curve.begin (), m_Curve.end (), curveT, KeyframeCompare());
+ AssertIf (i == m_Curve.end ());
+ int index = distance (m_Curve.begin (), i);
+ index--;
+ index = min<int> (m_Curve.size () - 2, index);
+ index = max<int> (0, index);
+
+ AssertIf (curveT < m_Curve[index].time || curveT > m_Curve[index+1].time);
+ return index;
+}
+
+template<class T>
+void AnimationCurveTpl<T>::FindIndexForSampling (const Cache& cache, float curveT, int& lhs, int& rhs) const
+{
+ AssertIf (curveT < GetRange ().first || curveT > GetRange ().second);
+ int actualSize = m_Curve.size();
+ const Keyframe* frames = &m_Curve[0];
+
+ // Reference implementation:
+ // (index is the last value that is equal to or smaller than curveT)
+ #if 0
+ int foundIndex = 0;
+ for (int i=0;i<actualSize;i++)
+ {
+ if (frames[i].time <= curveT)
+ foundIndex = i;
+ }
+
+ lhs = foundIndex;
+ rhs = min<int>(lhs + 1, actualSize - 1);
+ AssertIf (curveT < m_Curve[lhs].time || curveT > m_Curve[rhs].time);
+ AssertIf(frames[rhs].time == curveT && frames[lhs].time != curveT)
+ return;
+ #endif
+
+
+ #if SEARCH_AHEAD > 0
+ int cacheIndex = cache.index;
+ if (cacheIndex != -1)
+ {
+ // We can not use the cache time or time end since that is in unwrapped time space!
+ float time = m_Curve[cacheIndex].time;
+
+ if (curveT > time)
+ {
+ for (int i=0;i<SEARCH_AHEAD;i++)
+ {
+ int index = cacheIndex + i;
+ if (index + 1 < actualSize && frames[index + 1].time > curveT)
+ {
+ lhs = index;
+
+ rhs = min<int>(lhs + 1, actualSize - 1);
+ AssertIf (curveT < frames[lhs].time || curveT > frames[rhs].time);
+ AssertIf(frames[rhs].time == curveT && frames[lhs].time != curveT);
+ return;
+ }
+ }
+ }
+ else
+ {
+ for (int i=0;i<SEARCH_AHEAD;i++)
+ {
+ int index = cacheIndex - i;
+ if (index >= 0 && curveT >= frames[index].time)
+ {
+ lhs = index;
+ rhs = min<int>(lhs + 1, actualSize - 1);
+ AssertIf (curveT < frames[lhs].time || curveT > m_Curve[rhs].time);
+ AssertIf(frames[rhs].time == curveT && frames[lhs].time != curveT);
+ return;
+ }
+ }
+ }
+ }
+
+ #endif
+
+ // Fall back to using binary search
+ // upper bound (first value larger than curveT)
+ int __len = actualSize;
+ int __half;
+ int __middle;
+ int __first = 0;
+ while (__len > 0)
+ {
+ __half = __len >> 1;
+ __middle = __first + __half;
+
+ if (curveT < frames[__middle].time)
+ __len = __half;
+ else
+ {
+ __first = __middle;
+ ++__first;
+ __len = __len - __half - 1;
+ }
+ }
+
+ // If not within range, we pick the last element twice
+ lhs = __first - 1;
+ rhs = min(actualSize - 1, __first);
+
+ AssertIf(lhs < 0 || lhs >= actualSize);
+ AssertIf(rhs < 0 || rhs >= actualSize);
+
+ AssertIf (curveT < m_Curve[lhs].time || curveT > m_Curve[rhs].time);
+ AssertIf(frames[rhs].time == curveT && frames[lhs].time != curveT);
+}
+
+template<class T>
+void AnimationCurveTpl<T>::SetPreInfinity (int pre)
+{
+ m_PreInfinity = ToInternalInfinity(pre);
+ InvalidateCache ();
+}
+
+template<class T>
+void AnimationCurveTpl<T>::SetPostInfinity (int post)
+{
+ m_PostInfinity = ToInternalInfinity(post);
+ InvalidateCache ();
+}
+
+template<class T>
+int AnimationCurveTpl<T>::GetPreInfinity () const
+{
+ return FromInternalInfinity(m_PreInfinity);
+}
+
+template<class T>
+int AnimationCurveTpl<T>::GetPostInfinity () const
+{
+ return FromInternalInfinity(m_PostInfinity);
+}
+
+template<class T>
+T AnimationCurveTpl<T>::EvaluateClamp (float curveT) const
+{
+ T output;
+ if (curveT >= m_ClampCache.time && curveT < m_ClampCache.timeEnd)
+ {
+// AssertIf (!CompareApproximately (EvaluateCache (m_Cache, curveT), EvaluateWithoutCache (curveT), 0.001F));
+ EvaluateCache<T> (m_ClampCache, curveT, output);
+ return output;
+ }
+ else
+ {
+ DebugAssertIf (!IsValid ());
+
+ float begTime = m_Curve[0].time;
+ float endTime = m_Curve.back().time;
+
+ if (curveT > endTime)
+ {
+ m_ClampCache.time = endTime;
+ m_ClampCache.timeEnd = std::numeric_limits<float>::infinity ();
+ m_ClampCache.coeff[0] = m_ClampCache.coeff[1] = m_ClampCache.coeff[2] = Zero<T>();
+ m_ClampCache.coeff[3] = m_Curve[m_Curve.size()-1].value;
+ }
+ else if (curveT < begTime)
+ {
+ m_ClampCache.time = curveT - 1000.0F;
+ m_ClampCache.timeEnd = begTime;
+ m_ClampCache.coeff[0] = m_ClampCache.coeff[1] = m_ClampCache.coeff[2] = Zero<T>();
+ m_ClampCache.coeff[3] = m_Curve[0].value;
+ }
+ else
+ {
+ int lhs, rhs;
+ FindIndexForSampling (m_ClampCache, curveT, lhs, rhs);
+ CalculateCacheData (m_ClampCache, lhs, rhs, 0.0F);
+ }
+
+// AssertIf (!CompareApproximately (EvaluateCache (m_Cache, curveT), EvaluateWithoutCache (curveT), 0.001F));
+ EvaluateCache<T> (m_ClampCache, curveT, output);
+ return output;
+ }
+}
+
+template<class T>
+T AnimationCurveTpl<T>::Evaluate (float curveT) const
+{
+ int lhs, rhs;
+ T output;
+ if (curveT >= m_Cache.time && curveT < m_Cache.timeEnd)
+ {
+// AssertIf (!CompareApproximately (EvaluateCache (m_Cache, curveT), EvaluateWithoutCache (curveT), 0.001F));
+ EvaluateCache<T> (m_Cache, curveT, output);
+ return output;
+ }
+ // @TODO: Optimize IsValid () away if by making the non-valid case always use the m_Cache codepath
+ else if (IsValid ())
+ {
+ float begTime = m_Curve[0].time;
+ float endTime = m_Curve.back().time;
+ float wrappedTime;
+
+ if (curveT >= endTime)
+ {
+ if (m_PostInfinity == kInternalClamp)
+ {
+ m_Cache.time = endTime;
+ m_Cache.timeEnd = std::numeric_limits<float>::infinity ();
+ m_Cache.coeff[0] = m_Cache.coeff[1] = m_Cache.coeff[2] = Zero<T>();
+ m_Cache.coeff[3] = m_Curve[m_Curve.size()-1].value;
+ }
+ else if (m_PostInfinity == kInternalRepeat)
+ {
+ wrappedTime = Repeat (curveT, begTime, endTime);
+
+ FindIndexForSampling (m_Cache, wrappedTime, lhs, rhs);
+ CalculateCacheData (m_Cache, lhs, rhs, curveT - wrappedTime);
+ }
+ ///@todo optimize pingpong by making it generate a cache too
+ else
+ {
+ EvaluateWithoutCache (curveT, output);
+ return output;
+ }
+ }
+ else if (curveT < begTime)
+ {
+ if (m_PreInfinity == kInternalClamp)
+ {
+ m_Cache.time = curveT - 1000.0F;
+ m_Cache.timeEnd = begTime;
+ m_Cache.coeff[0] = m_Cache.coeff[1] = m_Cache.coeff[2] = Zero<T>();
+ m_Cache.coeff[3] = m_Curve[0].value;
+ }
+ else if (m_PreInfinity == kInternalRepeat)
+ {
+ wrappedTime = Repeat (curveT, begTime, endTime);
+ FindIndexForSampling (m_Cache, wrappedTime, lhs, rhs);
+ CalculateCacheData (m_Cache, lhs, rhs, curveT - wrappedTime);
+ }
+ ///@todo optimize pingpong by making it generate a cache too
+ else
+ {
+ EvaluateWithoutCache (curveT, output);
+ return output;
+ }
+ }
+ else
+ {
+ FindIndexForSampling (m_Cache, curveT, lhs, rhs);
+ CalculateCacheData (m_Cache, lhs, rhs, 0.0F);
+ }
+
+ // AssertIf (!CompareApproximately (EvaluateCache (m_Cache, curveT), EvaluateWithoutCache (curveT), 0.001F));
+ EvaluateCache<T> (m_Cache, curveT, output);
+ return output;
+ }
+ else
+ {
+ if (m_Curve.size () == 1)
+ return m_Curve.begin()->value;
+ else
+ return Zero<T> ();
+ }
+}
+
+template<class T>
+float AnimationCurveTpl<T>::WrapTime (float curveT) const
+{
+ DebugAssertIf (!IsValid ());
+
+ float begTime = m_Curve[0].time;
+ float endTime = m_Curve.back().time;
+
+ if (curveT < begTime)
+ {
+ if (m_PreInfinity == kInternalClamp)
+ curveT = begTime;
+ else if (m_PreInfinity == kInternalPingPong)
+ curveT = PingPong (curveT, begTime, endTime);
+ else
+ curveT = Repeat (curveT, begTime, endTime);
+ }
+ else if (curveT > endTime)
+ {
+ if (m_PostInfinity == kInternalClamp)
+ curveT = endTime;
+ else if (m_PostInfinity == kInternalPingPong)
+ curveT = PingPong (curveT, begTime, endTime);
+ else
+ curveT = Repeat (curveT, begTime, endTime);
+ }
+ return curveT;
+}
+
+template<class T>
+int AnimationCurveTpl<T>::AddKey (const Keyframe& key)
+{
+ InvalidateCache ();
+
+ iterator i = std::lower_bound (m_Curve.begin (), m_Curve.end (), key);
+
+ // is not included in container and value is not a duplicate
+ if (i == end () || key < *i)
+ {
+ iterator ii = m_Curve.insert (i, key);
+ return std::distance (m_Curve.begin (), ii);
+ }
+ else
+ return -1;
+}
+
+template<class T>
+void AnimationCurveTpl<T>::RemoveKeys (iterator begin, iterator end)
+{
+ InvalidateCache ();
+ m_Curve.erase (begin, end);
+}
+
+void ScaleCurveValue (AnimationCurve& curve, float scale)
+{
+ for (int i=0;i<curve.GetKeyCount ();i++)
+ {
+ curve.GetKey (i).value *= scale;
+ curve.GetKey (i).inSlope *= scale;
+ curve.GetKey (i).outSlope *= scale;
+ }
+
+ curve.InvalidateCache();
+}
+
+void OffsetCurveValue (AnimationCurve& curve, float offset)
+{
+ for (int i=0;i<curve.GetKeyCount ();i++)
+ curve.GetKey (i).value += offset;
+
+ curve.InvalidateCache();
+}
+
+void ScaleCurveTime (AnimationCurve& curve, float scale)
+{
+ for (int i=0;i<curve.GetKeyCount ();i++)
+ {
+ curve.GetKey (i).time *= scale;
+ curve.GetKey (i).inSlope /= scale;
+ curve.GetKey (i).outSlope /= scale;
+ }
+ curve.InvalidateCache();
+}
+
+void OffsetCurveTime (AnimationCurve& curve, float offset)
+{
+ for (int i=0;i<curve.GetKeyCount ();i++)
+ curve.GetKey (i).time += offset;
+ curve.InvalidateCache();
+}
+
+
+/*
+
+Calculating tangents from a hermite spline () Realtime rendering page 56
+
+
+
+> On this first pass we're stuck with linear keyframing, because we just
+> don't have the cycles in game to go to splines. I know this would help a
+> lot, but it isn't an option.
+
+In Granny I do successive least-squares approximations do the data. I
+take the array of samples for a given channel (ie., position) which is 2x
+oversampled or better from the art tool. I then start by doing a
+least-square solve for a spline of arbitrary degree with knots at either
+end. I compute the error over the spline from the original samples, and
+add knots in the areas of highest error. Repeat and salt to taste.
+
+Since it's fairly easy to write a single solver that solves for any degree
+of spline, I use the same solver for linear keyframes as I do for
+quadratic keyframes, cubic keyframes, or even "0th order keframes", which
+is to say if you don't want to interpolate _at all_, the solver can still
+place the "stop-motion" key frames in the best locations. But hopefully
+no one is still doing that kind of animation.
+
+If I were doing this over again (which I probably will at some point), I
+would probably use some kind of weird waveletty scheme now. I decided not
+to do that originally, because I had about a month to do the entire
+spline/solver/reduction thing the first time, and it had to be highly
+optimized. So I didn't want to have to learn wavelets and spline solvers
+at the same time. Next time I'll spend some time on wavelets and probably
+use some sort of hierachical reduction scheme instead, primarily so you
+can change how densely your keyframes are placed at run-time, and so you
+can get hard edges easier (motions which are intended to have sharp
+discontinuities aren't handled well at all by my current scheme).
+
+- Casey
+
+
+
+--
+
+> Is anybody looking at the way errors add up? eg. if you do some
+> reduction on the hip, and the thigh, and the ankle bone channels, the
+> result is a foot that moves quite a bit differently than it should.
+
+This has been on my list for a long time. I don't think it's a simple
+case of error analysis though. I think it's more a case for using
+discrete skeletal changes or integrated IK, because hey, if what you care
+about is having a particular thing stay in one place, then it seems to me
+that the best way to compress that is by just saying "this stays in one
+place", rather than trying to spend a lot of data on the joint curves
+necessary to make that so.
+
+> Also, is anybody doing things like using IK in the reducer, to make sure
+> that even in the reduced version the feet stay in exactly the same spot?
+
+That doesn't work with splines, unfortunately, because the splines are
+continuous, and you will always have the feet slipping as a result (if not
+at the keyframes, then in between for sure). So you end up with the
+problem that the IK reducer would need to shove a metric assload of keys
+into the streams, which defeats the compression.
+
+From Ian:
+
+> You said you are doing this on linear data as well. I can see that this
+> would work, but do you find you get a fairly minimal result? I can
+> envisage cases where you'd get many unneeded keyframes from initial
+> 'breaks' at points of large error.
+
+I'm not sure what you mean by this. The error is controllable, so you say
+how much you are willing to accept. You get a minimal spline for the
+error that you ask for, but no more - obviously in the areas of high
+error, it has to add more keys, but that's what you want. The objective
+of compression, at least in my opinion, is not to remove detail, but
+rather to more efficiently store places where there is less detail.
+
+> I think I may be missing the point. Do you do least squares on the
+> already fitted cures (ie, a chi squared test) then curve fit separately,
+> or do you use least squares to fit your actual spline to the data?
+
+The latter. The incremental knot addition sets up the t_n's of an
+arbitrary degree spline. You have a matrix A that has sample-count rows
+and knot-count columns. The vector you're looking for is x, the spline
+vector, which has knot-count rows. You're producing b, the vector of
+samples, which has sample-count rows. So it's Ax = b, but A is
+rectangular. You solve via A^T Ax = A^Tb, a simple least squares problem.
+You can solve it any way you like. I chose a straightforward
+implementation, because there's no numerical difficulties with these
+things.
+
+The version that comes with Granny is a highly optimized A^T Ax = A^Tb
+solver, which constructs A^T A and A^T b directly and sparsely (so it is
+O(n) in the number of samples, instead of O(n^3)). The A^T A is band
+diagonal, because of the sparsity pattern of A, so I use a sparse banded
+cholesky solver on the back end. It's _extremely_ fast. In fact, the
+stupid part of Granny's solver is actually the other part (the error
+analysis + knot addition), because it adds very few knots per cycle, so on
+a really long animation it can call the least-squares Ax = b solver many
+hundreds of times for a single animation, and it _still_ never takes more
+than 10 seconds or so even on animations with many hundred frames. So
+really, the right place to fix currently is the stupid knot addition
+alogirithm, which really should be improved quite a bit.
+
+> I have not used least squared before and from the reading I have done
+> (numerical recipes, last night), it seem that the equation of the
+> line/spline are inherent in the method and it needs to be reformulated
+> to use a different line/spline. (The version I read was for fitting a
+> straight line to a data-set, which I know won't work for quaternions
+> slerps anyway)
+
+Quaternion lerps are great, and work great with splines. I use them
+throughout all of Granny - there is no slerping anywhere. Slerping is not
+a very useful thing in a run-time engine, in my opinion, unless you are
+dealing with orientations that are greater the 90 degrees apart. It
+allows me to use the same solver for position, rotation, AND scale/shear,
+with no modifications.
+
+- Casey
+
+
+-----
+> When is it best to use quaternions and when to use normal matrix
+> rotations?
+
+In Granny, I use quaternions for everything except the final composite
+phase where rotations (and everything else) are multiplied by their
+parents to produce the final world-space results. Since we support
+scale/shear, orientation, and position, it tends to be fastest at the
+composition stage to convert the orientation from quaternion to matrix,
+and then do all the matrix concatenation together. I'm not sure I would
+do the same if I didn't support scale/shear. It might be a bit more fun
+and efficient to go ahead and do the whole pipe in quaternion and only
+convert to matrices at the very end (maybe somebody else has played with
+that and can comment?)
+
+> And for character animation with moving joints quaternions is usually
+> used, but why?
+
+There's lots of nice things about quaternions:
+
+1) You can treat the linearly if you want to (so you can blend animations
+quickly and easily)
+
+2) You can treat them non-linearly if you want to (so you can do exact
+geodesic interpolation at a constant speed)
+
+3) You can convert them to a matrix with no transcendentals
+
+4) They are compact, and can be easily converted to a 3-element
+representation when necessary (ie., any one of the four values can be
+easily re-generated from the other 3)
+
+5) They cover 720 degrees of rotation, not 360 (if you do a lot of work
+with character animation, you will appreciate this!), and the
+neighborhooding operator is extremely simple and fast (inner product and a
+negation)
+
+6) They are easy to visualize (they're not much more complicated than
+angle/axis) and manipulate (ie., the axis of rotation is obvious, you can
+transform the axis of rotation without changing the amount of rotation,
+and even just use a regular 3-vector transform, etc.)
+
+7) You can easily transform vectors with them without converting to a
+matrix if you want to
+
+8) They are easy to renormalize, and they can never become "unorthogonal"
+
+9) No gimbal lock
+
+10) There is a simple, fast, and relevant distance metric (the inner
+product)
+
+> Is it just by trial and error that we decide when we have to use
+> quarternions?
+
+Well, I can't speak for everyone, but I have been extremely careful in my
+selection of quaternions. With Granny 1 I did quaternions to get some
+practice with them, but when I did 2 I did a lot of research and wrote
+code to visualize the actions of various rotational representations and
+operations, and I can very confidently say there's no better choice for
+general character animation than quaternions, at least among the
+representations that I know (euler, angle/axis, matrix, quaternion, exp
+map). There are some other mappings that I've come across since I worked
+everything out (like the Rational Map), that I have not experimented with,
+because so far quaternions have been working superbly so I haven't had
+much cause to go hunting.
+
+> "Quarternions have the ability to have smooth interpollated rotations",
+> I've made smooth interpollated rotations with normal matrix rotations
+> though.
+
+Well, it's not so much what you can do as how easy it is to do it.
+Quaternions can be interpolated directly as a smooth geodesic (via slerp)
+or via a nonlinear geodesic (lerp). The latter is particularly powerful
+because it distributes - it's a linear operator. So you can literally
+plug it in to anything that would work, like splines, multi-point blends,
+etc., and it will "just work" (well, that's a bit of an exaggeration,
+because there is a neighborhooding concern, but it's always easy to do).
+
+> "They take less room, 4 elements versus 9 and some operations
+> are cheaper in terms of CPU cycles". I accept this reason except someone
+> said that quarternions use more CPU cycles, so I'm not sure who to
+> believe.
+
+It depends what you're doing. Quaternions take more CPU cycles to
+transform a vector than a matrix does. But quaternions are MUCH less
+expensive for lots of other operations, like, for example, splining.
+This is why it's very useful to use quaternions for everything except the
+final stage of your pipe, where matrices are more appropriate.
+
+> "Quarternions are not susceptible to gimbal lock. Gimbal lock shows its
+> face when two axes point in the same direction." So if no axes face in
+> the same direction then this is not a reason to use quarternions.
+> ...
+> Am I right in saying that if no axes face the same direction it is
+> possible to represent all rotations with normal matrix rotations?
+
+Matrix rotations are not subject to gimbal lock. That's Euler angles.
+
+- Casey
+
+
+Least squares is very simple. Given a set of data points (2x oversampled in caseys case) and a knot vector, compute the coefficients for the control points of the spline that minimize squared error. This involves solving a linear system where each row corresponds to a data point (di - they are uniformly space so each point has a corresponding ti which is in the domain of the curve) and each column corresponds to a control point for the curve (cj the unkowns). So matrix element Aij is Bj(ti) where Bj is the basis function for the jth control point. The right hand side is just a vector of the di and the x's are the unkown control points. Sine you are compressing things there will be many more rows then columns in this linear system so you don't have to worry about over fitting problems (which you would if there were more DOF...)
+
+-Peter-Pike
+
+ -----Original Message-----
+ From: Ian Elsley [mailto:ielsley@kushgames.com]
+ Sent: Tue 12/17/2002 10:35 AM
+ To: gdalgorithms-list@lists.sourceforge.net
+ Cc:
+ Subject: RE: [Algorithms] Re: Keyframe reduction
+
+
+
+ Casey
+
+ I think I may be missing the point. Do you do least squares on the
+ already fitted cures (ie, a chi squared test) then curve fit separately,
+ or do you use least squares to fit your actual spline to the data?
+
+ I have not used least squared before and from the reading I have done
+ (numerical recipes, last night), it seem that the equation of the
+ line/spline are inherent in the method and it needs to be reformulated
+ to use a different line/spline. (The version I read was for fitting a
+ straight line to a data-set, which I know won't work for quaternions
+ slerps anyway)
+
+ I see the elegance of this method and would love to work it out, but
+ I've got a few blanks here.
+
+ Any pointers?
+
+ Thanks in advance,
+
+ Ian
+
+
+
+
+
+
+ q
+ q' = -----
+ |q|
+
+For renormalizing, since you are very close to 1, I usually use the
+tangent-line approximation, which was suggested by Checker a long long
+time ago for 3D vectors and works just peachy for 4D ones as well:
+
+inline void NormalizeCloseToOne4(float *Dest)
+{
+ float const Sum = (Dest[0] * Dest[0] +
+ Dest[1] * Dest[1] +
+ Dest[2] * Dest[2] +
+ Dest[3] * Dest[3]);
+
+ float const ApproximateOneOverRoot = (3.0f - Sum) * 0.5f;
+
+ Dest[0] *= ApproximateOneOverRoot;
+ Dest[1] *= ApproximateOneOverRoot;
+ Dest[2] *= ApproximateOneOverRoot;
+ Dest[3] *= ApproximateOneOverRoot;
+}
+
+
+*/
+
+
+
+#define INSTANTIATE(T) \
+template EXPORT_COREMODULE std::pair<float, float> AnimationCurveTpl<T>::GetRange() const; \
+template EXPORT_COREMODULE T AnimationCurveTpl<T>::Evaluate (float curveT) const; \
+template EXPORT_COREMODULE void AnimationCurveTpl<T>::RemoveKeys (iterator begin, iterator end);\
+template EXPORT_COREMODULE int AnimationCurveTpl<T>::AddKey (const Keyframe& key);\
+template EXPORT_COREMODULE int AnimationCurveTpl<T>::FindIndex (float time) const; \
+template EXPORT_COREMODULE int AnimationCurveTpl<T>::FindIndex (const Cache& cache, float time) const; \
+template EXPORT_COREMODULE void AnimationCurveTpl<T>::InvalidateCache (); \
+template EXPORT_COREMODULE void AnimationCurveTpl<T>::SetPreInfinity (int mode); \
+template EXPORT_COREMODULE void AnimationCurveTpl<T>::SetPostInfinity (int mode); \
+template EXPORT_COREMODULE int AnimationCurveTpl<T>::GetPreInfinity () const; \
+template EXPORT_COREMODULE int AnimationCurveTpl<T>::GetPostInfinity () const; \
+template EXPORT_COREMODULE KeyframeTpl<T>::KeyframeTpl (float time, const T& value);
+
+INSTANTIATE(float)
+INSTANTIATE(Vector3f)
+INSTANTIATE(Quaternionf)
+
+template EXPORT_COREMODULE void AnimationCurveTpl<float>::CalculateCacheData (Cache& cache, int lhs, int rhs, float timeOffset) const;
+template EXPORT_COREMODULE float AnimationCurveTpl<float>::EvaluateClamp (float curveT) const;
+template EXPORT_COREMODULE Quaternionf AnimationCurveTpl<Quaternionf>::EvaluateClamp (float curveT) const;
+template EXPORT_COREMODULE Vector3f AnimationCurveTpl<Vector3f>::EvaluateClamp (float curveT) const;
diff --git a/Runtime/Math/AnimationCurve.h b/Runtime/Math/AnimationCurve.h
new file mode 100644
index 0000000..e88629a
--- /dev/null
+++ b/Runtime/Math/AnimationCurve.h
@@ -0,0 +1,324 @@
+#pragma once
+
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Math/FloatConversion.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Modules/ExportModules.h"
+
+enum { kDefaultWrapMode = 0, kClamp = 1 << 0, kRepeat = 1 << 1, kPingPong = 1 << 2, kClampForever = 1 << 3 };
+#define kCurveTimeEpsilon 0.00001F
+
+/*
+ AnimationCurves in Maya are represented as time/value keys with 2D tangents which are always of normalized length.
+ From the tangents a slope is calculated (tangent.y / tangent.x) -> (thus length of the tangent doesn't matter)
+
+ When the slope is multiplied by the time range of the curve (rhs.time - lhs.time) it can be evaluated using
+ a standard hermite interpolator.
+
+ In the Unity AnimationCurve the slopes are directly stored in the keyframe instead of the 2D tangent vectors.
+*/
+
+///@TODO: Curve templates suck.
+/// Lets make some implementation where they share the same data structure and only Evaluate is specialized.
+
+template<class T>
+struct KeyframeTpl
+{
+ // DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (Keyframe)
+ inline static const char* GetTypeString () { return "Keyframe"; }
+ inline static bool IsAnimationChannel () { return false; }
+ inline static bool MightContainPPtr () { return false; }
+ // Disable transfer optimization in Editor because tangentMode optimized serialization when reading AssetBundles will corrupt data
+ inline static bool AllowTransferOptimization ()
+ {
+ #if UNITY_EDITOR
+ return false;
+ #else
+ return true;
+ #endif
+ }
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+ float time;
+ T value;
+ T inSlope;
+ T outSlope;
+ #if UNITY_EDITOR
+ int tangentMode;
+ #endif
+
+ #if UNITY_EDITOR
+ KeyframeTpl ()
+ {
+ tangentMode = 0;
+ }
+ #else
+ KeyframeTpl () {}
+ #endif
+ KeyframeTpl (float t, const T& v);
+
+
+ friend bool operator < (const KeyframeTpl& lhs, const KeyframeTpl& rhs) { return lhs.time < rhs.time; }
+};
+
+enum AnimationCurveType {
+ kFloatCurve = 0,
+ kVector3Curve = 1,
+ kQuaternionCurve = 2
+};
+
+template<class T>
+class EXPORT_COREMODULE AnimationCurveTpl
+{
+ public:
+
+ DECLARE_SERIALIZE_NO_PPTR (AnimationCurve)
+
+ /// Stores the curve as a pure cubic function with 4 coefficients
+ struct Cache
+ {
+ int index;
+ float time;
+ float timeEnd;
+ T coeff[4];
+
+ Cache () { time = std::numeric_limits<float>::infinity (); index=0; timeEnd = 0.0f; memset(&coeff, 0, sizeof(coeff)); }
+ void Invalidate () { time = std::numeric_limits<float>::infinity (); index=0; }
+ };
+
+ typedef KeyframeTpl<T> Keyframe;
+
+ typedef dynamic_array<Keyframe> KeyframeContainer;
+ typedef typename KeyframeContainer::iterator iterator;
+ typedef typename KeyframeContainer::const_iterator const_iterator;
+
+public:
+ AnimationCurveTpl ()
+ {
+ m_PreInfinity = m_PostInfinity = kInternalClamp;
+ }
+
+ /// Evaluates the AnimationCurve caching the segment.
+ T Evaluate (float curveT) const;
+ T EvaluateClamp (float curveT) const;
+
+ bool IsValid () const { return m_Curve.size () >= 2; }
+
+ int AddKey (const Keyframe& key);
+
+ /// Performs no error checking. And doesn't invalidate the cache!
+ void AddKeyBackFast (const Keyframe& key) { m_Curve.push_back (key); }
+
+ const Keyframe& GetKey (int index) const { AssertMsg(index >= 0 && index < m_Curve.size(), "Index (%d) is out of range [0, %i)", index, (int)m_Curve.size()); return m_Curve[index]; }
+
+ /// When changing the keyframe using GetKey you are not allowed to change the time!
+ /// After modifying a key you have to call InvalidateCache
+ Keyframe& GetKey (int index) { AssertMsg(index >= 0 && index < m_Curve.size(), "Index (%d) is out of range [0, %i)", index, (int)m_Curve.size()); return const_cast<Keyframe&> (m_Curve[index]); }
+
+ iterator begin () { return m_Curve.begin (); }
+ iterator end () { return m_Curve.end (); }
+ const_iterator begin () const { return m_Curve.begin (); }
+ const_iterator end () const { return m_Curve.end (); }
+
+ void InvalidateCache ();
+
+ int GetKeyCount () const { return m_Curve.size (); }
+
+ void RemoveKeys (iterator begin, iterator end);
+
+ /// Returns the first and last keyframe time
+ std::pair<float, float> GetRange () const;
+
+ enum { kInternalPingPong = 0, kInternalRepeat = 1, kInternalClamp = 2 };
+
+ // How does the curve before the first keyframe
+ void SetPreInfinity (int pre);
+ int GetPreInfinity () const;
+ // How does the curve behave after the last keyframe
+ void SetPostInfinity (int post);
+ int GetPostInfinity () const;
+
+ // How does the curve before the first keyframe
+ void SetPreInfinityInternal (int pre) { m_PreInfinity = pre; InvalidateCache (); }
+ int GetPreInfinityInternal () const { return m_PreInfinity; }
+ // How does the curve behave after the last keyframe
+ void SetPostInfinityInternal (int post) { m_PostInfinity = post; InvalidateCache (); }
+ int GetPostInfinityInternal () const { return m_PostInfinity; }
+
+ void Assign (const Keyframe* begin, const Keyframe* end) { m_Curve.assign (begin, end); InvalidateCache(); }
+ void Swap (KeyframeContainer& newArray) { m_Curve.swap(newArray); InvalidateCache(); }
+ void Sort () { std::sort(m_Curve.begin(), m_Curve.end()); InvalidateCache(); }
+
+ void ResizeUninitialized (int size) { m_Curve.resize_uninitialized(size); }
+
+ ///@TODO: Cleanup old code to completely get rid of this
+ int FindIndex (const Cache& cache, float curveT) const;
+
+ ///@TODO: Cleanup old code to completely get rid of this
+ /// Returns the closest keyframe index that is less than time.
+ /// Returns -1 if time is outside the range of the curve
+ int FindIndex (float time) const;
+
+ void CalculateCacheData (Cache& cache, int lhs, int rhs, float timeOffset) const;
+
+ private:
+
+ void FindIndexForSampling (const Cache& cache, float curveT, int& lhs, int& rhs) const;
+
+ /// Evaluates the AnimationCurve directly.
+ void EvaluateWithoutCache (float curveT, T& output)const;
+
+ float WrapTime (float curveT) const;
+
+ mutable Cache m_Cache;
+ mutable Cache m_ClampCache;
+
+ KeyframeContainer m_Curve;
+ int m_PreInfinity;
+ int m_PostInfinity;
+};
+
+typedef AnimationCurveTpl<float> AnimationCurveBase;
+typedef AnimationCurveTpl<float> AnimationCurve;
+typedef AnimationCurveTpl<Quaternionf> AnimationCurveQuat;
+typedef AnimationCurveTpl<Vector3f> AnimationCurveVec3;
+
+template<class T>
+template<class TransferFunction>
+inline void KeyframeTpl<T>::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (time);
+ TRANSFER (value);
+ TRANSFER (inSlope);
+ TRANSFER (outSlope);
+ #if UNITY_EDITOR
+ if (!transfer.IsSerializingForGameRelease())
+ TRANSFER (tangentMode);
+ #endif
+}
+
+template<class T>
+template<class TransferFunction>
+inline void AnimationCurveTpl<T>::Transfer (TransferFunction& transfer)
+{
+ transfer.SetVersion(2);
+
+ transfer.Transfer (m_Curve, "m_Curve", kHideInEditorMask);
+ transfer.Transfer (m_PreInfinity, "m_PreInfinity", kHideInEditorMask);
+ transfer.Transfer (m_PostInfinity, "m_PostInfinity", kHideInEditorMask);
+
+ if (transfer.IsReading ())
+ InvalidateCache ();
+}
+
+inline int TimeToFrame (float time, float sampleRate)
+{
+ return RoundfToInt(time * sampleRate);
+}
+
+inline float FrameToTime (int frame, float sampleRate)
+{
+ return (float)frame / sampleRate;
+}
+
+inline float FloatFrameToTime (float frame, float sampleRate)
+{
+ return frame / sampleRate;
+}
+
+
+void HandleSteppedCurve (const KeyframeTpl<float>& lhs, const KeyframeTpl<float>& rhs, float& value);
+void HandleSteppedTangent (const KeyframeTpl<float>& lhs, const KeyframeTpl<float>& rhs, float& value);
+
+void HandleSteppedCurve (const KeyframeTpl<Vector3f>& lhs, const KeyframeTpl<Vector3f>& rhs, Vector3f& value);
+void HandleSteppedTangent (const KeyframeTpl<Vector3f>& lhs, const KeyframeTpl<Vector3f>& rhs, Vector3f& tangent);
+
+void HandleSteppedCurve (const KeyframeTpl<Quaternionf>& lhs, const KeyframeTpl<Quaternionf>& rhs, Quaternionf& tangent);
+void HandleSteppedTangent (const KeyframeTpl<Quaternionf>& lhs, const KeyframeTpl<Quaternionf>& rhs, Quaternionf& tangent);
+
+inline float PingPong (float t, float length)
+{
+ t = Repeat (t, length * 2.0F);
+ t = length - Abs (t - length);
+ return t;
+}
+
+
+inline float Repeat (float t, float begin, float end)
+{
+ return Repeat (t - begin, end - begin) + begin;
+}
+
+inline double RepeatD (double t, double begin, double end)
+{
+ return RepeatD (t - begin, end - begin) + begin;
+}
+
+inline float PingPong (float t, float begin, float end)
+{
+ return PingPong (t - begin, end - begin) + begin;
+}
+
+#if (defined(__GNUC__) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 3)) || defined(__clang__)
+ // in GCC 4.3 and above the explicit template specialization cannot have a storage class
+ #define SPEC_STORAGE_CLASS inline
+#else
+ #define SPEC_STORAGE_CLASS static
+#endif
+
+#define kMaxTan 5729577.9485111479F
+
+template<class T>
+static T MaxTan () { return kMaxTan; }
+
+template<>
+SPEC_STORAGE_CLASS Quaternionf MaxTan<Quaternionf> () { return Quaternionf(kMaxTan, kMaxTan, kMaxTan, kMaxTan); }
+
+template<>
+SPEC_STORAGE_CLASS Vector3f MaxTan<Vector3f> () { return Vector3f(kMaxTan, kMaxTan, kMaxTan); }
+
+#undef kMaxTan
+
+template<class T>
+static T Zero () { return T (); }
+
+template<>
+SPEC_STORAGE_CLASS Quaternionf Zero<Quaternionf> () { return Quaternionf(0.0F, 0.0F, 0.0F, 0.0F); }
+
+template<>
+SPEC_STORAGE_CLASS Vector3f Zero<Vector3f> () { return Vector3f(0.0F, 0.0F, 0.0F); }
+
+#undef SPEC_STORAGE_CLASS
+
+void ScaleCurveValue (AnimationCurve& curve, float scale);
+void OffsetCurveValue (AnimationCurve& curve, float offset);
+void ScaleCurveTime (AnimationCurve& curve, float scale);
+void OffsetCurveTime (AnimationCurve& curve, float offset);
+
+template<class T>
+inline T HermiteInterpolate (float t, T p0, T m0, T m1, T p1)
+{
+ float t2 = t * t;
+ float t3 = t2 * t;
+
+ float a = 2.0F * t3 - 3.0F * t2 + 1.0F;
+ float b = t3 - 2.0F * t2 + t;
+ float c = t3 - t2;
+ float d = -2.0F * t3 + 3.0F * t2;
+
+ return a * p0 + b * m0 + c * m1 + d * p1;
+}
+
+struct KeyframeCompare
+{
+ template<class T>
+ bool operator ()(KeyframeTpl<T> const& k, float t) { return k.time < t; }
+ // These are necessary for debug STL (validation of predicates)
+ template<class T>
+ bool operator ()(KeyframeTpl<T> const& k1, KeyframeTpl<T> const& k2) { return k1.time < k2.time; }
+ template<class T>
+ bool operator ()(float t, KeyframeTpl<T> const& k) { return !operator() (k, t); }
+};
diff --git a/Runtime/Math/Color.h b/Runtime/Math/Color.h
new file mode 100644
index 0000000..594598b
--- /dev/null
+++ b/Runtime/Math/Color.h
@@ -0,0 +1,293 @@
+#ifndef COLOR_H
+#define COLOR_H
+
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Serialize/SerializationMetaFlags.h"
+#include <algorithm>
+#include "Runtime/Utilities/Utility.h"
+#include "FloatConversion.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+
+class ColorRGBAf
+{
+ public:
+ float r, g, b, a;
+
+ DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL (ColorRGBA)
+
+ ColorRGBAf () {}
+
+ ColorRGBAf (float inR, float inG, float inB, float inA = 1.0F) : r(inR), g(inG), b(inB), a(inA) {}
+ explicit ColorRGBAf (const float* c) : r(c[0]), g(c[1]), b(c[2]), a(c[3]) {}
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer);
+
+ void Set (float inR, float inG, float inB, float inA) {r = inR; g = inG; b = inB; a = inA;}
+
+ void SetHex (UInt32 hex)
+ {
+ Set(float (hex >> 24) / 255.0f,
+ float ((hex >> 16) & 255) / 255.0f,
+ float ((hex >> 8) & 255) / 255.0f,
+ float (hex & 255) / 255.0f);
+ }
+
+ UInt32 GetHex () const
+ {
+ UInt32 hex = (NormalizedToByte(r) << 24) | (NormalizedToByte(g) << 16) | (NormalizedToByte(b) << 8) | NormalizedToByte(a);
+ return hex;
+ }
+
+ float AverageRGB () const {return (r+g+b)*(1.0F / 3.0F);}
+ float GreyScaleValue () const { return r * 0.30f + g * 0.59f + b * 0.11f; }
+
+ ColorRGBAf& operator = (const ColorRGBAf& in) { Set (in.r, in.g, in.b, in.a); return *this; }
+
+ bool Equals(const ColorRGBAf& inRGB) const
+ {
+ return (r == inRGB.r && g == inRGB.g && b == inRGB.b && a == inRGB.a);
+ }
+
+ bool NotEquals(const ColorRGBAf& inRGB) const
+ {
+ return (r != inRGB.r || g != inRGB.g || b != inRGB.b || a != inRGB.a);
+ }
+
+ float* GetPtr () {return &r;}
+ const float* GetPtr () const {return &r;}
+
+ ColorRGBAf& operator += (const ColorRGBAf &inRGBA)
+ {
+ r += inRGBA.r; g += inRGBA.g; b += inRGBA.b; a += inRGBA.a;
+ return *this;
+ }
+
+ ColorRGBAf& operator *= (const ColorRGBAf &inRGBA)
+ {
+ r *= inRGBA.r; g *= inRGBA.g; b *= inRGBA.b; a *= inRGBA.a;
+ return *this;
+ }
+
+private:
+ // intentionally undefined
+ bool operator == (const ColorRGBAf& inRGB) const;
+ bool operator != (const ColorRGBAf& inRGB) const;
+};
+
+
+inline ColorRGBAf operator + (const ColorRGBAf& inC0, const ColorRGBAf& inC1)
+{
+ return ColorRGBAf (inC0.r + inC1.r, inC0.g + inC1.g, inC0.b + inC1.b, inC0.a + inC1.a);
+}
+
+inline ColorRGBAf operator * (const ColorRGBAf& inC0, const ColorRGBAf& inC1)
+{
+ return ColorRGBAf (inC0.r * inC1.r, inC0.g * inC1.g, inC0.b * inC1.b, inC0.a * inC1.a);
+}
+
+inline ColorRGBAf operator * (float inScale, const ColorRGBAf& inC0)
+{
+ return ColorRGBAf (inC0.r * inScale, inC0.g * inScale, inC0.b * inScale, inC0.a * inScale);
+}
+
+inline ColorRGBAf operator * (const ColorRGBAf& inC0, float inScale)
+{
+ return ColorRGBAf (inC0.r * inScale, inC0.g * inScale, inC0.b * inScale, inC0.a * inScale);
+}
+
+inline ColorRGBAf Lerp (const ColorRGBAf& c0, const ColorRGBAf& c1, float t)
+{
+ return (1.0f - t) * c0 + t * c1;
+}
+
+
+
+class ColorRGBA32
+{
+ public:
+
+ UInt8 r, g, b, a;
+
+ DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL (ColorRGBA)
+
+ ColorRGBA32 () {}
+
+ ColorRGBA32 (UInt8 inR, UInt8 inG, UInt8 inB, UInt8 inA) { r = inR; g = inG; b = inB; a = inA; }
+ ColorRGBA32 (UInt32 c) { *(UInt32*)this = c; }
+ void Set (UInt8 inR, UInt8 inG, UInt8 inB, UInt8 inA) { r = inR; g = inG; b = inB; a = inA; }
+
+ ColorRGBA32 operator = (const ColorRGBA32& c) { *(UInt32*)this = *((UInt32*)&c); return *this;}
+
+ ColorRGBA32 (const ColorRGBAf& c) { Set (c); }
+
+ operator ColorRGBAf() const
+ {
+ return ColorRGBAf (ByteToNormalized(r), ByteToNormalized(g), ByteToNormalized(b), ByteToNormalized(a));
+ }
+
+ UInt32 AsUInt32 () const { return *(UInt32*)this; }
+
+ void operator = (const ColorRGBAf& c)
+ {
+ Set (c);
+ }
+
+ UInt32 GetUInt32 () { return *(UInt32*)this; }
+
+ void Set (const ColorRGBAf& c)
+ {
+ r = NormalizedToByte(c.r);
+ g = NormalizedToByte(c.g);
+ b = NormalizedToByte(c.b);
+ a = NormalizedToByte(c.a);
+ }
+
+ template<class TransferFunction>
+ void Transfer (TransferFunction& transfer)
+ {
+ transfer.SetVersion (2);
+ UInt32* c = reinterpret_cast<UInt32*> (this);
+ // When transferring colors we shouldn't swap bytes.
+ // UInt32 already convert endianess by default so we convert it two times to keep it the same :)
+ if (transfer.ConvertEndianess ())
+ {
+ if (transfer.IsReading())
+ {
+ transfer.Transfer (*c, "rgba", kHideInEditorMask);
+ SwapEndianBytes (*c);
+ }
+ else
+ {
+ UInt32 temp = *c;
+ SwapEndianBytes (temp);
+ transfer.Transfer (temp, "rgba", kHideInEditorMask);
+ }
+ }
+ else
+ {
+ transfer.Transfer (*c, "rgba", kHideInEditorMask);
+ }
+ }
+
+ UInt8& operator [] (long i) {return GetPtr () [i];}
+ const UInt8& operator [] (long i)const {return GetPtr () [i];}
+
+ bool operator == (const ColorRGBA32& inRGB) const
+ {
+ return (r == inRGB.r && g == inRGB.g && b == inRGB.b && a == inRGB.a) ? true : false;
+ }
+
+ bool operator != (const ColorRGBA32& inRGB) const
+ {
+ return (r != inRGB.r || g != inRGB.g || b != inRGB.b || a != inRGB.a) ? true : false;
+ }
+
+ UInt8* GetPtr () {return &r;}
+ const UInt8* GetPtr ()const {return &r;}
+
+ inline ColorRGBA32 operator * (int scale) const
+ {
+ //AssertIf (scale < 0 || scale > 255);
+ scale += 1;
+ const UInt32& u = reinterpret_cast<const UInt32&> (*this);
+ UInt32 lsb = (((u & 0x00ff00ff) * scale) >> 8) & 0x00ff00ff;
+ UInt32 msb = (((u & 0xff00ff00) >> 8) * scale) & 0xff00ff00;
+ lsb |= msb;
+ return ColorRGBA32 (lsb);
+ }
+
+ inline void operator *= (const ColorRGBA32& inC1)
+ {
+#if 0
+ r = (r * inC1.r) / 255;
+ g = (g * inC1.g) / 255;
+ b = (b * inC1.b) / 255;
+ a = (a * inC1.a) / 255;
+#else // This is much faster, but doesn't guarantee 100% matching result (basically color values van vary 1/255 but not at ends, check out unit test in cpp file).
+ UInt32& u = reinterpret_cast<UInt32&> (*this);
+ const UInt32& v = reinterpret_cast<const UInt32&> (inC1);
+ UInt32 result = (((u & 0x000000ff) * ((v & 0x000000ff) + 1)) >> 8) & 0x000000ff;
+ result |= (((u & 0x0000ff00) >> 8) * (((v & 0x0000ff00) >> 8) + 1)) & 0x0000ff00;
+ result |= (((u & 0x00ff0000) * (((v & 0x00ff0000) >> 16) + 1)) >> 8) & 0x00ff0000;
+ result |= (((u & 0xff000000) >> 8) * (((v & 0xff000000) >> 24) + 1)) & 0xff000000;
+ u = result;
+#endif
+}
+
+ inline ColorRGBA32 SwizzleToBGRA() const { return ColorRGBA32(b, g, r, a); }
+ inline ColorRGBA32 SwizzleToBGR() const { return ColorRGBA32(b, g, r, 255); }
+ inline ColorRGBA32 SwizzleToARGB() const { return ColorRGBA32(a, r, g, b); }
+ inline ColorRGBA32 UnswizzleBGRA() const { return ColorRGBA32(b, g, r, a); }
+ inline ColorRGBA32 UnswizzleARGB() const { return ColorRGBA32(g, b, a, r); }
+};
+
+#if GFX_OPENGLESxx_ONLY
+ inline ColorRGBA32 SwizzleColorForPlatform(const ColorRGBA32& col) { return col; }
+ inline ColorRGBA32 UnswizzleColorForPlatform(const ColorRGBA32& col) { return col; }
+#elif UNITY_XENON || UNITY_PS3 || UNITY_WII
+ inline ColorRGBA32 SwizzleColorForPlatform(const ColorRGBA32& col) { return col.SwizzleToARGB(); }
+ inline ColorRGBA32 UnswizzleColorForPlatform(const ColorRGBA32& col) { return col.UnswizzleARGB(); }
+#else
+ inline ColorRGBA32 SwizzleColorForPlatform(const ColorRGBA32& col) { return col.SwizzleToBGRA(); }
+ inline ColorRGBA32 UnswizzleColorForPlatform(const ColorRGBA32& col) { return col.UnswizzleBGRA(); }
+#endif
+
+struct OpColorRGBA32ToUInt32
+{
+ typedef UInt32 result_type;
+ UInt32 operator() (ColorRGBA32 const& arg) const { return arg.AsUInt32(); }
+};
+
+inline ColorRGBA32 operator + (const ColorRGBA32& inC0, const ColorRGBA32& inC1)
+{
+ return ColorRGBA32 (std::min<int> (inC0.r + inC1.r, 255),
+ std::min<int> (inC0.g + inC1.g, 255),
+ std::min<int> (inC0.b + inC1.b, 255),
+ std::min<int> (inC0.a + inC1.a, 255));
+}
+
+inline ColorRGBA32 operator * (const ColorRGBA32& inC0, const ColorRGBA32& inC1)
+{
+#if 0
+ return ColorRGBA32 ((inC0.r * inC1.r) / 255,
+ (inC0.g * inC1.g) / 255,
+ (inC0.b * inC1.b) / 255,
+ (inC0.a * inC1.a) / 255);
+#else
+ // This is much faster, but doesn't guarantee 100% matching result (basically color values van vary 1/255 but not at ends, check out unit test in cpp file).
+ const UInt32& u = reinterpret_cast<const UInt32&> (inC0);
+ const UInt32& v = reinterpret_cast<const UInt32&> (inC1);
+ UInt32 result = (((u & 0x000000ff) * ((v & 0x000000ff) + 1)) >> 8) & 0x000000ff;
+ result |= (((u & 0x0000ff00) >> 8) * (((v & 0x0000ff00) >> 8) + 1)) & 0x0000ff00;
+ result |= (((u & 0x00ff0000) * (((v & 0x00ff0000) >> 16) + 1)) >> 8) & 0x00ff0000;
+ result |= (((u & 0xff000000) >> 8) * (((v & 0xff000000) >> 24) + 1)) & 0xff000000;
+ return ColorRGBA32 (result);
+#endif
+}
+
+inline ColorRGBA32 Lerp(const ColorRGBA32& c0, const ColorRGBA32& c1, int scale)
+{
+ //AssertIf (scale < 0 || scale > 255);
+ const UInt32& u0 = reinterpret_cast<const UInt32&> (c0);
+ const UInt32& u1 = reinterpret_cast<const UInt32&> (c1);
+ UInt32 vx = u0 & 0x00ff00ff;
+ UInt32 rb = vx + ((((u1 & 0x00ff00ff) - vx) * scale) >> 8) & 0x00ff00ff;
+ vx = u0 & 0xff00ff00;
+ return ColorRGBA32( rb | (vx + ((((u1 >> 8) & 0x00ff00ff) - (vx >> 8)) * scale) & 0xff00ff00) );
+}
+
+
+template<class TransferFunction>
+void ColorRGBAf::Transfer (TransferFunction& transfer)
+{
+ transfer.AddMetaFlag (kTransferUsingFlowMappingStyle);
+ transfer.Transfer (r, "r", kHideInEditorMask);
+ transfer.Transfer (g, "g", kHideInEditorMask);
+ transfer.Transfer (b, "b", kHideInEditorMask);
+ transfer.Transfer (a, "a", kHideInEditorMask);
+}
+
+
+#endif
+
diff --git a/Runtime/Math/ColorSpaceConversion.cpp b/Runtime/Math/ColorSpaceConversion.cpp
new file mode 100644
index 0000000..08d58f5
--- /dev/null
+++ b/Runtime/Math/ColorSpaceConversion.cpp
@@ -0,0 +1,17 @@
+#include "UnityPrefix.h"
+#include "ColorSpaceConversion.h"
+#include "Runtime/Misc/PlayerSettings.h"
+
+
+ColorSpace GetActiveColorSpace ()
+{
+ if (GetPlayerSettingsPtr())
+ return GetPlayerSettings().GetValidatedColorSpace();
+ else
+ return kUninitializedColorSpace;
+}
+
+/*
+ TODO:
+ * Fog colors in fixed function pipeline are not adjusted. Ask aras how the fog color gets put into the shader. ApplyFog does something with builtin shader params but it's never passed to shaderstate??? WTF.
+*/ \ No newline at end of file
diff --git a/Runtime/Math/ColorSpaceConversion.h b/Runtime/Math/ColorSpaceConversion.h
new file mode 100644
index 0000000..4eb11da
--- /dev/null
+++ b/Runtime/Math/ColorSpaceConversion.h
@@ -0,0 +1,123 @@
+#pragma once
+
+#include "Color.h"
+
+enum ColorSpace { kUninitializedColorSpace = -1, kGammaColorSpace = 0, kLinearColorSpace, kMaxColorSpace };
+
+ColorSpace GetActiveColorSpace ();
+
+
+// http://www.opengl.org/registry/specs/EXT/framebuffer_sRGB.txt
+// http://www.opengl.org/registry/specs/EXT/texture_sRGB_decode.txt
+// { cs / 12.92, cs <= 0.04045 }
+// { ((cs + 0.055)/1.055)^2.4, cs > 0.04045 }
+
+inline float GammaToLinearSpace (float value)
+{
+ if (value <= 0.04045F)
+ return value / 12.92F;
+ else if (value < 1.0F)
+ return pow((value + 0.055F)/1.055F, 2.4F);
+ else
+ return pow(value, 2.4F);
+}
+
+// http://www.opengl.org/registry/specs/EXT/framebuffer_sRGB.txt
+// http://www.opengl.org/registry/specs/EXT/texture_sRGB_decode.txt
+// { 0.0, 0 <= cl
+// { 12.92 * c, 0 < cl < 0.0031308
+// { 1.055 * cl^0.41666 - 0.055, 0.0031308 <= cl < 1
+// { 1.0, cl >= 1 <- This has been adjusted since we want to maintain HDR colors
+
+inline float LinearToGammaSpace (float value)
+{
+ if (value <= 0.0F)
+ return 0.0F;
+ else if (value <= 0.0031308F)
+ return 12.92F * value;
+ else if (value <= 1.0F)
+ return 1.055F * powf(value, 0.41666F) - 0.055F;
+ else
+ return powf(value, 0.41666F);
+}
+
+inline float GammaToLinearSpaceXenon(float val)
+{
+ float ret;
+ if (val < 0)
+ ret = 0;
+ else if (val < 0.25f)
+ ret = 0.25f * val;
+ else if (val < 0.375f)
+ ret = (1.0f/16.0f) + 0.5f*(val-0.25f);
+ else if (val < 0.75f)
+ ret = 0.125f + 1.0f*(val-0.375f);
+ else if (val < 1.0f)
+ ret = 0.5f + 2.0f*(val-0.75f);
+ else
+ ret = 1.0f;
+ return ret;
+}
+
+inline float LinearToGammaSpaceXenon(float val)
+{
+ float ret;
+ if (val < 0)
+ ret = 0;
+ else if (val < (1.0f/16.0f))
+ ret = 4.0f * val;
+ else if (val < (1.0f/8.0f))
+ ret = (1.0f/4.0f) + 2.0f*(val-(1.0f/16.0f));
+ else if (val < 0.5f)
+ ret = 0.375f + 1.0f*(val-0.125f);
+ else if (val < 1.0f)
+ ret = 0.75f + 0.5f*(val-0.50f);
+ else
+ ret = 1.0f;
+
+ return ret;
+}
+
+inline ColorRGBAf GammaToLinearSpace (const ColorRGBAf& value)
+{
+ return ColorRGBAf(GammaToLinearSpace(value.r), GammaToLinearSpace(value.g), GammaToLinearSpace(value.b), value.a);
+}
+
+inline ColorRGBAf LinearToGammaSpace (const ColorRGBAf& value)
+{
+ return ColorRGBAf(LinearToGammaSpace(value.r), LinearToGammaSpace(value.g), LinearToGammaSpace(value.b), value.a);
+}
+
+inline ColorRGBAf GammaToLinearSpaceXenon (const ColorRGBAf& value)
+{
+ return ColorRGBAf(GammaToLinearSpaceXenon(value.r), GammaToLinearSpaceXenon(value.g), GammaToLinearSpaceXenon(value.b), value.a);
+}
+
+inline ColorRGBAf LinearToGammaSpaceXenon (const ColorRGBAf& value)
+{
+ return ColorRGBAf(LinearToGammaSpaceXenon(value.r), LinearToGammaSpaceXenon(value.g), LinearToGammaSpaceXenon(value.b), value.a);
+}
+
+inline float GammaToActiveColorSpace (float value)
+{
+ if (GetActiveColorSpace () == kLinearColorSpace)
+ return GammaToLinearSpace(value);
+ else
+ return value;
+}
+
+inline ColorRGBAf GammaToActiveColorSpace (const ColorRGBAf& value)
+{
+ if (GetActiveColorSpace () == kLinearColorSpace)
+ return GammaToLinearSpace(value);
+ else
+ return value;
+}
+
+inline ColorRGBAf ActiveToGammaColorSpace (const ColorRGBAf& value)
+{
+ if (GetActiveColorSpace () == kLinearColorSpace)
+ return LinearToGammaSpace(value);
+ else
+ return value;
+} \ No newline at end of file
diff --git a/Runtime/Math/FloatConversion.cpp b/Runtime/Math/FloatConversion.cpp
new file mode 100644
index 0000000..a76126d
--- /dev/null
+++ b/Runtime/Math/FloatConversion.cpp
@@ -0,0 +1,98 @@
+#include "UnityPrefix.h"
+#include "FloatConversion.h"
+
+#if UNITY_EDITOR
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+FloatToHalfConverter::FloatToHalfConverter()
+{
+ for (int i = 0; i < 256; i++)
+ {
+ int e = i - 127;
+ if (e < -24)
+ {
+ // Too small to represent becomes zero
+ m_ExponentTable[i] = 0x0000;
+ m_MantissaShift[i] = 24;
+ }
+ else if (e < -14)
+ {
+ // Small numbers become denormals
+ m_ExponentTable[i] = 0x0400 >> (-14 - e);
+ m_MantissaShift[i] = -1 - e;
+ }
+ else if (e < 16)
+ {
+ // Handle normalized numbers
+ m_ExponentTable[i] = (15 + e) << 10;
+ m_MantissaShift[i] = 13;
+ }
+ else if (e < 128)
+ {
+ // Large numbers become infinity
+ m_ExponentTable[i] = 0x7C00;
+ m_MantissaShift[i] = 24;
+ }
+ else
+ {
+ // Handle infinity and NaN
+ m_ExponentTable[i] = 0x7C00;
+ m_MantissaShift[i] = 13;
+ }
+ }
+}
+
+FloatToHalfConverter g_FloatToHalf;
+
+SUITE (FloatConversionTests)
+{
+TEST(FloatConversionTests_FloatToHalf)
+{
+ // 1 bit sign
+ for (int s = 0; s <= 1; s++)
+ {
+ // 5 bits exponent
+ for (int ebits = 0; ebits < (1 << 5); ebits++)
+ {
+ // 10 bits mantissa
+ for (int m = 0; m < (1 << 10); m++)
+ {
+ int orig = (s << 15) | (ebits << 10) | m;
+ float val;
+ HalfToFloat(orig, val);
+ UInt16 conv;
+ g_FloatToHalf.Convert(val, conv);
+ CHECK_EQUAL(orig, conv);
+ }
+ }
+ }
+}
+
+TEST(FloatConversionTests_IsFinite)
+{
+ float infF = std::numeric_limits<float>::infinity();
+ CHECK(IsFinite(0.0f));
+ CHECK(IsFinite(1.0f));
+ CHECK(IsFinite(FLT_MIN));
+ CHECK(IsFinite(FLT_MAX));
+ CHECK(IsFinite(-FLT_MIN));
+ CHECK(IsFinite(-FLT_MAX));
+ CHECK(!IsFinite(infF));
+ CHECK(!IsFinite(-infF));
+ CHECK(!IsFinite(infF-infF));
+
+ double infD = std::numeric_limits<double>::infinity();
+ CHECK(IsFinite(0.0));
+ CHECK(IsFinite(1.0));
+ CHECK(IsFinite(DBL_MIN));
+ CHECK(IsFinite(DBL_MAX));
+ CHECK(IsFinite(-DBL_MIN));
+ CHECK(IsFinite(-DBL_MAX));
+ CHECK(!IsFinite(infD));
+ CHECK(!IsFinite(-infD));
+ CHECK(!IsFinite(infD-infD));
+}
+}
+
+#endif // UNITY_EDITOR
diff --git a/Runtime/Math/FloatConversion.h b/Runtime/Math/FloatConversion.h
new file mode 100644
index 0000000..eb6b49c
--- /dev/null
+++ b/Runtime/Math/FloatConversion.h
@@ -0,0 +1,696 @@
+#ifndef FLOATCONVERSION_H
+#define FLOATCONVERSION_H
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+#include <math.h>
+
+#if !UNITY_EXTERNAL_TOOL
+#include "Runtime/Utilities/LogAssert.h"
+#endif
+
+#if defined(SN_TARGET_PS3)
+# include <ppu_intrinsics.h>
+#elif defined(__GNUC__) && defined(__ppc__)
+# include <ppc_intrinsics.h>
+#endif
+
+#ifndef kPI
+ #define kPI 3.14159265358979323846264338327950288419716939937510F
+#endif
+
+const float kBiggestFloatSmallerThanOne = 0.99999994f;
+const double kBiggestDoubleSmallerThanOne = 0.99999999999999989;
+
+#if defined(_XBOX)
+#define __FSELF __fself
+#elif defined(SN_TARGET_PS3)
+#define __FSELF __fsels
+#endif
+
+inline float FloatMin(float a, float b)
+{
+#if defined(_XBOX) || defined(SN_TARGET_PS3)
+ return __FSELF((a)-(b), b, a);
+#else
+ return std::min(a, b);
+#endif
+}
+
+inline float FloatMax(float a, float b)
+{
+#if defined(_XBOX) || defined(SN_TARGET_PS3)
+ return __FSELF((a)-(b), a, b);
+#else
+ return std::max(a, b);
+#endif
+}
+
+inline float Abs (float v)
+{
+#if defined(__ppc__) && (defined(__MWERKS__) || defined(SN_TARGET_PS3))
+ return __fabsf(v);
+#elif defined(_XBOX)
+ return __fabs(v);
+#else
+ return v < 0.0F ? -v : v;
+#endif
+}
+
+inline double Abs (double v)
+{
+ return v < 0.0 ? -v : v;
+}
+
+inline int Abs (int v)
+{
+ return v < 0 ? -v : v;
+}
+
+// Floor, ceil and round functions.
+//
+// When changing or implementing these functions, make sure the tests in MathTest.cpp
+// still pass.
+//
+// Floor: rounds to the largest integer smaller than or equal to the input parameter.
+// Ceil: rounds to the smallest integer larger than or equal to the input parameter.
+// Round: rounds to the nearest integer. Ties (0.5) are rounded up to the smallest integer
+// larger than or equal to the input parameter.
+// Chop/truncate: use a normal integer cast.
+//
+// Windows:
+// Casts are as fast as a straight fistp on an SSE equipped CPU. This is by far the most common
+// scenario and will result in the best code for most users. fistp will use the rounding mode set
+// in the control register (round to nearest by default), and needs fiddling to work properly.
+// This actually makes code that attempt to use fistp slower than a cast.
+// Unless we want round to nearest, in which case fistp should be the best choice, right? But
+// it is not. The default rounding mode is round to nearest, but in case of a tie (0.5), round to
+// nearest even is used. Thus 0.5 is rounded down to 0, 1.5 is rounded up to 2.
+// Conclusion - fistp is useless without stupid fiddling around that actually makes is slower than
+// an SSE cast.
+//
+// OS X Intel:
+// Needs investigating
+//
+// OS X PowerPC:
+// Needs investigating
+//
+// Xbox 360:
+// Needs investigating
+//
+// PS3:
+// Needs investigating
+//
+// iPhone:
+// Needs investigating
+//
+// Android:
+// Needs investigating
+
+
+inline int FloorfToInt (float f)
+{
+ DebugAssertIf (f < INT_MIN || f > INT_MAX);
+ return f >= 0 ? (int)f : (int)(f - kBiggestFloatSmallerThanOne);
+}
+
+inline UInt32 FloorfToIntPos (float f)
+{
+ DebugAssertIf (f < 0 || f > UINT_MAX);
+ return (UInt32)f;
+}
+
+inline float Floorf (float f)
+{
+ // Use std::floor().
+ // We are interested in reliable functions that do not lose precision.
+ // Casting to int and back to float would not be helpful.
+ return floor (f);
+}
+
+inline double Floord (double f)
+{
+ // Use std::floor().
+ // We are interested in reliable functions that do not lose precision.
+ // Casting to int and back to float would not be helpful.
+ return floor (f);
+}
+
+
+inline int CeilfToInt (float f)
+{
+ DebugAssertIf (f < INT_MIN || f > INT_MAX);
+ return f >= 0 ? (int)(f + kBiggestFloatSmallerThanOne) : (int)(f);
+}
+
+inline UInt32 CeilfToIntPos (float f)
+{
+ DebugAssertIf (f < 0 || f > UINT_MAX);
+ return (UInt32)(f + kBiggestFloatSmallerThanOne);
+}
+
+inline float Ceilf (float f)
+{
+ // Use std::ceil().
+ // We are interested in reliable functions that do not lose precision.
+ // Casting to int and back to float would not be helpful.
+ return ceil (f);
+}
+
+inline double Ceild (double f)
+{
+ // Use std::ceil().
+ // We are interested in reliable functions that do not lose precision.
+ // Casting to int and back to float would not be helpful.
+ return ceil (f);
+}
+
+
+inline int RoundfToInt (float f)
+{
+ return FloorfToInt (f + 0.5F);
+}
+
+inline UInt32 RoundfToIntPos (float f)
+{
+ return FloorfToIntPos (f + 0.5F);
+}
+
+inline float Roundf (float f)
+{
+ return Floorf (f + 0.5F);
+}
+
+inline double Roundf (double f)
+{
+ return Floord (f + 0.5);
+}
+
+
+/// Fast conversion of float [0...1] to 0 ... 65535
+inline int NormalizedToWord (float f)
+{
+ f = FloatMax (f, 0.0F);
+ f = FloatMin (f, 1.0F);
+ return RoundfToIntPos (f * 65535.0f);
+}
+
+/// Fast conversion of float [0...1] to 0 ... 65535
+inline float WordToNormalized (int p)
+{
+ AssertIf(p < 0 || p > 65535);
+ return (float)p / 65535.0F;
+}
+
+/// Fast conversion of float [0...1] to 0 ... 255
+inline int NormalizedToByte (float f)
+{
+ f = FloatMax (f, 0.0F);
+ f = FloatMin (f, 1.0F);
+ return RoundfToIntPos (f * 255.0f);
+}
+
+/// Fast conversion of float [0...1] to 0 ... 255
+inline float ByteToNormalized (int p)
+{
+ AssertIf(p < 0 || p > 255);
+ return (float)p / 255.0F;
+}
+
+
+// Returns float remainder for t / length
+inline float Repeat (float t, float length)
+{
+ return t - Floorf (t / length) * length;
+}
+
+// Returns double remainder for t / length
+inline double RepeatD (double t, double length)
+{
+ return t - floor (t / length) * length;
+}
+
+// Returns relative angle on the interval (-pi, pi]
+inline float DeltaAngleRad (float current, float target)
+{
+ float delta = Repeat ((target - current), 2 * kPI);
+ if (delta > kPI)
+ delta -= 2 * kPI;
+ return delta;
+}
+
+// Returns true if the distance between f0 and f1 is smaller than epsilon
+inline bool CompareApproximately (float f0, float f1, float epsilon = 0.000001F)
+{
+ float dist = (f0 - f1);
+ dist = Abs (dist);
+ return dist < epsilon;
+}
+
+/// CopySignf () returns x with its sign changed to y's.
+inline float CopySignf (float x, float y)
+{
+ union
+ {
+ float f;
+ UInt32 i;
+ } u, u0, u1;
+ u0.f = x; u1.f = y;
+ UInt32 a = u0.i;
+ UInt32 b = u1.i;
+ SInt32 mask = 1 << 31;
+ UInt32 sign = b & mask;
+ a &= ~mask;
+ a |= sign;
+
+ u.i = a;
+ return u.f;
+}
+
+inline int CompareFloatRobustSignUtility (float A)
+{
+ // The sign bit of a number is the high bit.
+ union
+ {
+ float f;
+ int i;
+ } u;
+ u.f = A;
+ return (u.i) & 0x80000000;
+}
+
+inline bool CompareFloatRobust (float f0, float f1, int maxUlps = 10)
+{
+ // After adjusting floats so their representations are lexicographically
+ // ordered as twos-complement integers a very small positive number
+ // will compare as 'close' to a very small negative number. If this is
+ // not desireable, and if you are on a platform that supports
+ // subnormals (which is the only place the problem can show up) then
+ // you need this check.
+ // The check for A == B is because zero and negative zero have different
+ // signs but are equal to each other.
+ if (CompareFloatRobustSignUtility(f0) != CompareFloatRobustSignUtility(f1))
+ return f0 == f1;
+
+ union
+ {
+ float f;
+ int i;
+ } u0, u1;
+ u0.f = f0;
+ u1.f = f1;
+ int aInt = u0.i;
+ // Make aInt lexicographically ordered as a twos-complement int
+ if (aInt < 0)
+ aInt = 0x80000000 - aInt;
+ // Make bInt lexicographically ordered as a twos-complement int
+ int bInt = u1.i;
+ if (bInt < 0)
+ bInt = 0x80000000 - bInt;
+
+ // Now we can compare aInt and bInt to find out how far apart A and B
+ // are.
+ int intDiff = Abs (aInt - bInt);
+ if (intDiff <= maxUlps)
+ return true;
+ return false;
+}
+
+// Returns the t^2
+template<class T>
+T Sqr (const T& t)
+{
+ return t * t;
+}
+
+#define kDeg2Rad (2.0F * kPI / 360.0F)
+#define kRad2Deg (1.F / kDeg2Rad)
+
+inline float Deg2Rad (float deg)
+{
+ // TODO : should be deg * kDeg2Rad, but can't be changed,
+ // because it changes the order of operations and that affects a replay in some RegressionTests
+ return deg / 360.0F * 2.0F * kPI;
+}
+
+inline float Rad2Deg (float rad)
+{
+ // TODO : should be rad * kRad2Deg, but can't be changed,
+ // because it changes the order of operations and that affects a replay in some RegressionTests
+ return rad / 2.0F / kPI * 360.0F;
+}
+
+inline float Lerp (float from, float to, float t)
+{
+ return to * t + from * (1.0F - t);
+}
+
+inline bool IsNAN (float value)
+{
+ #if defined __APPLE_CC__
+ return value != value;
+ #elif _MSC_VER
+ return _isnan(value) != 0;
+ #else
+ return isnan (value);
+ #endif
+}
+
+inline bool IsNAN (double value)
+{
+ #if defined __APPLE_CC__
+ return value != value;
+ #elif _MSC_VER
+ return _isnan(value) != 0;
+ #else
+ return isnan (value);
+ #endif
+}
+
+inline bool IsPlusInf(float value) { return value == std::numeric_limits<float>::infinity (); }
+inline bool IsMinusInf(float value) { return value == -std::numeric_limits<float>::infinity (); }
+
+inline bool IsFinite(const float& value)
+{
+ // Returns false if value is NaN or +/- infinity
+ UInt32 intval = *reinterpret_cast<const UInt32*>(&value);
+ return (intval & 0x7f800000) != 0x7f800000;
+}
+
+inline bool IsFinite(const double& value)
+{
+ // Returns false if value is NaN or +/- infinity
+ UInt64 intval = *reinterpret_cast<const UInt64*>(&value);
+ return (intval & 0x7ff0000000000000LL) != 0x7ff0000000000000LL;
+}
+
+inline float InvSqrt (float p) { return 1.0F / sqrt (p); }
+inline float Sqrt (float p) { return sqrt (p); }
+
+/// - Almost highest precision sqrt
+/// - Returns 0 if value is 0 or -1
+/// inline float FastSqrt (float value)
+
+/// - Almost highest precision inv sqrt
+/// - if value == 0 or -0 it returns 0.
+/// inline float FastInvSqrt (float value)
+
+/// - Low precision inv sqrt approximately
+/// - if value == 0 or -0 it returns nan or undefined
+/// inline float FastestInvSqrt (float value)
+
+#if defined(__ppc__) || defined(SN_TARGET_PS3)
+
+#if UNITY_WII
+// Copied from <CodeWarrior>\PowerPC_EABI_Support\MSL\MSL_C\PPC_EABI\Include\math_ppc_inlines.h
+// Requires hardware floating to be enabled
+// P.S I've also profiled with function below which uses fabs(x) == 0.0F, it's two times slower than this one
+inline float FastSqrt (float x)
+{
+ static const double _half=.5f;
+ static const double _three=3.0f;
+
+ if(x > 0.0f)
+ {
+ double xd = (double)x;
+ double guess = __frsqrte(xd); /* returns an approximation to */
+ guess = _half*guess*(_three - guess*guess*xd); /* now have 12 sig bits */
+ guess = _half*guess*(_three - guess*guess*xd); /* now have 24 sig bits */
+ return (float)(xd * guess);
+ }
+ else if (x < 0.0)
+ return NAN;
+ else
+ return x;
+}
+#else
+/// - Accurate to 1 bit precision
+/// - returns zero if x is zero
+inline float FastSqrt (float x)
+{
+ const float half = 0.5;
+ const float one = 1.0;
+ float B, y0, y1;
+
+ // This'll NaN if it hits frsqrte. Handle both +0.0 and -0.0
+ if (fabs(x) == 0.0F)
+ return x;
+
+ B = x;
+
+#if defined(__GNUC__) && !defined(SN_TARGET_PS3)
+ y0 = __frsqrtes(B);
+#else
+ y0 = __frsqrte(B);
+#endif
+ // First refinement step
+
+ y1 = y0 + half*y0*(one - B*y0*y0);
+
+ // Second refinement step -- copy the output of the last step to the input of this step
+
+ y0 = y1;
+ y1 = y0 + half*y0*(one - B*y0*y0);
+
+ // Get sqrt(x) from x * 1/sqrt(x)
+ return x * y1;
+}
+#endif
+
+/// - Accurate to 1 bit precision
+/// - returns zero if f is zero
+inline float FastInvSqrt( float f )
+{
+ float result;
+ float estimate, estimate2;
+ float oneHalf = 0.5f;
+ float one = oneHalf + oneHalf;
+ //Calculate a 5 bit starting estimate for the reciprocal sqrt
+#if defined(__GNUC__) && !defined(SN_TARGET_PS3)
+ estimate = estimate2 = __frsqrtes ( f );
+#else
+ estimate = estimate2 = __frsqrte ( f );
+#endif
+
+ //if you require less precision, you may reduce the number of loop iterations
+ estimate = estimate + oneHalf * estimate * ( one - f * estimate * estimate );
+ estimate = estimate + oneHalf * estimate * ( one - f * estimate * estimate );
+
+#if defined(__GNUC__) && !defined(SN_TARGET_PS3)
+ result = __fsels( -f, estimate2, estimate );
+#else
+ result = __fsel( -f, estimate2, estimate );
+#endif
+ return result;
+}
+
+/// Fast inverse sqrt function
+inline float FastestInvSqrt (float value)
+{
+ #if defined (__ppc__) && (defined (__MWERKS__) || defined(SN_TARGET_PS3))
+ return (float)__frsqrte (value);
+ #elif defined (__ppc__)
+ return (float)__frsqrtes(value);
+ #else
+ return 1.0F / sqrtf (value);
+ #endif
+}
+
+#else
+
+inline float FastSqrt (float value)
+{
+ return sqrtf (value);
+}
+
+inline float FastInvSqrt( float f )
+{
+ // The Newton iteration trick used in FastestInvSqrt is a bit faster on
+ // Pentium4 / Windows, but lower precision. Doing two iterations is precise enough,
+ // but actually a bit slower.
+ if (fabs(f) == 0.0F)
+ return f;
+ return 1.0F / sqrtf (f);
+}
+
+inline float FastestInvSqrt( float f )
+{
+ union
+ {
+ float f;
+ int i;
+ } u;
+ float fhalf = 0.5f*f;
+ u.f = f;
+ int i = u.i;
+ i = 0x5f3759df - (i>>1);
+ u.i = i;
+ f = u.f;
+ f = f*(1.5f - fhalf*f*f);
+ // f = f*(1.5f - fhalf*f*f); // uncommenting this would be two iterations
+ return f;
+}
+
+#endif
+
+inline float SqrtImpl (float f)
+{
+ #if UNITY_WII || UNITY_FLASH
+ return FastSqrt (f);
+ #else
+ return sqrt (f);
+ #endif
+}
+inline float Sin (float f)
+{
+ return sinf (f);
+}
+
+inline float Pow (float f, float f2)
+{
+ return powf (f, f2);
+}
+
+inline float Cos (float f)
+{
+ return cosf (f);
+}
+
+inline float Sign (float f)
+{
+#if defined(_XBOX)
+ return __fsel(f, 1.0f, -1.0f);
+#else
+ if (f < 0.0F)
+ return -1.0F;
+ else
+ return 1.0;
+#endif
+}
+
+#if UNITY_EDITOR
+
+class FloatToHalfConverter
+{
+public:
+ FloatToHalfConverter();
+
+ void Convert(const float& src, UInt16& dest)
+ {
+ UInt32 bits = *reinterpret_cast<const UInt32*>(&src);
+ UInt8 index = UInt8(bits >> 23);
+ UInt32 sign = bits & 0x80000000;
+ UInt32 mantissa = bits & 0x007fffff;
+ dest = (sign >> 16) | m_ExponentTable[index] | (mantissa >> m_MantissaShift[index]);
+ }
+
+private:
+ UInt16 m_ExponentTable[256];
+ UInt8 m_MantissaShift[256];
+};
+
+extern FloatToHalfConverter g_FloatToHalf;
+
+#endif // UNITY_EDITOR
+
+#if UNITY_SUPPORTS_SSE
+#include "Runtime/Math/Simd/SimdMath.h"
+
+#define SSE_CONST4(name, val) static const ALIGN16 UInt32 name[4] = { (val), (val), (val), (val) }
+#define CONST_M128I(name) *(const __m128i *)&name
+
+static ALIGN16 UInt16 source[] = {0,0,0,0,0,0,0,0};
+static ALIGN16 float destination[] = {0.0,0.0,0.0,0.0};
+
+static void HalfToFloat(UInt16 src, float& dest)
+{
+ SSE_CONST4(mask_nosign, 0x7fff);
+ SSE_CONST4(smallest_normal, 0x0400);
+ SSE_CONST4(infinity, 0x7c00);
+ SSE_CONST4(expadjust_normal, (127 - 15) << 23);
+ SSE_CONST4(magic_denorm, 113 << 23);
+
+ source[0] = src;
+ __m128i in = _mm_loadu_si128(reinterpret_cast<const __m128i*>(source));
+ __m128i mnosign = CONST_M128I(mask_nosign);
+ __m128i eadjust = CONST_M128I(expadjust_normal);
+ __m128i smallest = CONST_M128I(smallest_normal);
+ __m128i infty = CONST_M128I(infinity);
+ __m128i expmant = _mm_and_si128(mnosign, in);
+ __m128i justsign = _mm_xor_si128(in, expmant);
+ __m128i b_notinfnan = _mm_cmpgt_epi32(infty, expmant);
+ __m128i b_isdenorm = _mm_cmpgt_epi32(smallest, expmant);
+ __m128i shifted = _mm_slli_epi32(expmant, 13);
+ __m128i adj_infnan = _mm_andnot_si128(b_notinfnan, eadjust);
+ __m128i adjusted = _mm_add_epi32(eadjust, shifted);
+ __m128i den1 = _mm_add_epi32(shifted, CONST_M128I(magic_denorm));
+ __m128i adjusted2 = _mm_add_epi32(adjusted, adj_infnan);
+ __m128 den2 = _mm_sub_ps(_mm_castsi128_ps(den1), *(const __m128 *)&magic_denorm);
+ __m128 adjusted3 = _mm_and_ps(den2, _mm_castsi128_ps(b_isdenorm));
+ __m128 adjusted4 = _mm_andnot_ps(_mm_castsi128_ps(b_isdenorm), _mm_castsi128_ps(adjusted2));
+ __m128 adjusted5 = _mm_or_ps(adjusted3, adjusted4);
+ __m128i sign = _mm_slli_epi32(justsign, 16);
+ __m128 out = _mm_or_ps(adjusted5, _mm_castsi128_ps(sign));
+ _mm_storeu_ps(destination, out);
+ dest = destination[0];
+#undef SSE_CONST4
+#undef CONST_M128I
+}
+
+#else
+
+static void HalfToFloat(UInt16 src, float& dest)
+{
+ // Integer alias
+ UInt32& bits = *reinterpret_cast<UInt32*>(&dest);
+
+ // Based on Fabian Giesen's public domain half_to_float_fast3
+ static const UInt32 magic = { 113 << 23 };
+ const float& magicFloat = *reinterpret_cast<const float*>(&magic);
+ static const UInt32 shiftedExp = 0x7c00 << 13; // exponent mask after shift
+
+ // Mask out sign bit
+ bits = src & 0x7fff;
+ if (bits)
+ {
+ // Move exponent + mantissa to correct bits
+ bits <<= 13;
+ UInt32 exponent = bits & shiftedExp;
+ if (exponent == 0)
+ {
+ // Handle denormal
+ bits += magic;
+ dest -= magicFloat;
+ }
+ else if (exponent == shiftedExp) // Inf/NaN
+ bits += (255 - 31) << 23;
+ else
+ bits += (127 - 15) << 23;
+ }
+
+ // Copy sign bit
+ bits |= (src & 0x8000) << 16;
+}
+
+#endif
+
+using std::cos;
+using std::pow;
+using std::atan2;
+using std::acos;
+using std::sin;
+using std::sqrt;
+using std::log;
+using std::exp;
+
+// On non-C99 platforms log2 is not available, so approximate it.
+#if UNITY_WIN || UNITY_XENON || UNITY_ANDROID || UNITY_FLASH || UNITY_WEBGL
+#define kNaturalLogarithm2 0.693147180559945309417
+#define Log2(x) (logf(x) / kNaturalLogarithm2)
+#else
+#define Log2(x) log2f(x)
+#endif
+
+
+#endif
diff --git a/Runtime/Math/FloatExceptions.cpp b/Runtime/Math/FloatExceptions.cpp
new file mode 100644
index 0000000..d8c9563
--- /dev/null
+++ b/Runtime/Math/FloatExceptions.cpp
@@ -0,0 +1,18 @@
+#include "FloatExceptions.h"
+
+
+#if 0 && DEBUGMODE && defined(__SSE__)
+
+void InitFloatExceptions ()
+{
+ _MM_SET_EXCEPTION_MASK (_MM_GET_EXCEPTION_MASK () & ~_MM_MASK_INVALID);
+}
+
+#else
+
+void InitFloatExceptions ()
+{
+}
+
+#endif
+
diff --git a/Runtime/Math/FloatExceptions.h b/Runtime/Math/FloatExceptions.h
new file mode 100644
index 0000000..d2002e9
--- /dev/null
+++ b/Runtime/Math/FloatExceptions.h
@@ -0,0 +1,6 @@
+#ifndef FLOATEXCEPTIONS_H
+#define FLOATEXCEPTIONS_H
+
+void InitFloatExceptions ();
+
+#endif
diff --git a/Runtime/Math/Gradient.cpp b/Runtime/Math/Gradient.cpp
new file mode 100644
index 0000000..9544a2b
--- /dev/null
+++ b/Runtime/Math/Gradient.cpp
@@ -0,0 +1,310 @@
+#include "UnityPrefix.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Math/Gradient.h"
+
+GradientNEW::GradientNEW()
+: m_NumColorKeys(2)
+, m_NumAlphaKeys(2)
+{
+ m_Keys[0] = m_Keys[1] = ColorRGBA32(0xffffffff);
+ m_ColorTime[0] = m_AlphaTime[0] = NormalizedToWord(0.0f);
+ m_ColorTime[1] = m_AlphaTime[1] = NormalizedToWord(1.0f);
+
+ for(UInt32 i = 2; i < kGradientMaxNumKeys; i++)
+ {
+ m_Keys[i] = ColorRGBA32(0);
+ m_ColorTime[i] = NormalizedToWord(0.0f);
+ m_AlphaTime[i] = NormalizedToWord(0.0f);
+ }
+}
+
+GradientNEW::~GradientNEW()
+{
+}
+
+void GradientNEW::SetKeys (ColorKey* colorKeys, unsigned numColorKeys, AlphaKey* alphaKeys, unsigned numAlphaKeys)
+{
+ SetColorKeys (colorKeys, numColorKeys);
+ SetAlphaKeys (alphaKeys, numAlphaKeys);
+}
+
+void GradientNEW::SwapColorKeys(int i, int j)
+{
+ ColorRGBA32 tmpCol = m_Keys[i];
+ UInt16 tmpTime = m_ColorTime[i];
+ m_Keys[i].r = m_Keys[j].r;
+ m_Keys[i].g = m_Keys[j].g;
+ m_Keys[i].b = m_Keys[j].b;
+ m_ColorTime[i] = m_ColorTime[j];
+ m_Keys[j].r = tmpCol.r;
+ m_Keys[j].g = tmpCol.g;
+ m_Keys[j].b = tmpCol.b;
+ m_ColorTime[j] = tmpTime;
+}
+
+void GradientNEW::SwapAlphaKeys(int i, int j)
+{
+ ColorRGBA32 tmpCol = m_Keys[i];
+ UInt16 tmpTime = m_AlphaTime[i];
+ m_Keys[i].a = m_Keys[j].a;
+ m_AlphaTime[i] = m_AlphaTime[j];
+ m_Keys[j].a = tmpCol.a;
+ m_AlphaTime[j] = tmpTime;
+}
+
+void GradientNEW::SetColorKeys (ColorKey* colorKeys, unsigned numKeys)
+{
+ DebugAssert (numKeys <= kGradientMaxNumKeys);
+ if (numKeys > kGradientMaxNumKeys)
+ numKeys = kGradientMaxNumKeys;
+
+ for (int i=0; i<numKeys; ++i)
+ {
+ const ColorRGBAf& color = colorKeys[i].m_Color;
+ m_Keys[i].r = NormalizedToByte(color.r);
+ m_Keys[i].g = NormalizedToByte(color.g);
+ m_Keys[i].b = NormalizedToByte(color.b);
+ m_ColorTime[i] = NormalizedToWord(colorKeys[i].m_Time);
+ }
+ m_NumColorKeys = numKeys;
+
+ // Ensure sorted!
+ int i = 0;
+ const int keyCount = m_NumColorKeys;
+ while ((i + 1) < keyCount)
+ {
+ if (m_ColorTime[i] > m_ColorTime[i+1])
+ {
+ SwapColorKeys(i, i + 1);
+ if (i > 0)
+ i -= 2;
+ }
+ i++;
+ }
+
+ ValidateColorKeys();
+}
+
+void GradientNEW::SetAlphaKeys (AlphaKey* alphaKeys, unsigned numKeys)
+{
+ DebugAssert (numKeys <= kGradientMaxNumKeys);
+ if (numKeys > kGradientMaxNumKeys)
+ numKeys = kGradientMaxNumKeys;
+
+ for (int i=0; i<numKeys; ++i)
+ {
+ float alpha = alphaKeys[i].m_Alpha;
+ m_Keys[i].a = NormalizedToByte(alpha);
+ m_AlphaTime[i] = NormalizedToWord(alphaKeys[i].m_Time);
+ }
+ m_NumAlphaKeys = numKeys;
+
+ // Ensure sorted!
+ int i = 0;
+ const int keyCount = m_NumAlphaKeys;
+ while ((i + 1) < keyCount)
+ {
+ if (m_AlphaTime[i] > m_AlphaTime[i+1])
+ {
+ SwapAlphaKeys(i, i + 1);
+ if (i > 0)
+ i -= 2;
+ }
+ i++;
+ }
+
+ ValidateAlphaKeys();
+}
+
+ColorRGBA32 GradientNEW::GetConstantColor () const
+{
+ return m_Keys[0];
+}
+
+void GradientNEW::SetConstantColor (ColorRGBA32 color)
+{
+ m_Keys[0] = color;
+ m_NumAlphaKeys = 1;
+ m_NumColorKeys = 1;
+}
+
+void GradientNEW::ValidateColorKeys()
+{
+ // Make sure there is a minimum of 2 keys
+ if(m_NumColorKeys < 2)
+ {
+ m_NumColorKeys = 2;
+ for(int rgb = 0; rgb < 3; rgb++)
+ m_Keys[1][rgb] = m_Keys[0][rgb];
+ m_ColorTime[0] = NormalizedToWord(0.0f);
+ m_ColorTime[1] = NormalizedToWord(1.0f);
+ }
+}
+
+void GradientNEW::ValidateAlphaKeys()
+{
+ // Make sure there is a minimum of 2 keys
+ if(m_NumAlphaKeys < 2)
+ {
+ m_NumAlphaKeys = 2;
+ m_Keys[1].a = m_Keys[0].a;
+ m_AlphaTime[0] = NormalizedToWord(0.0f);
+ m_AlphaTime[1] = NormalizedToWord(1.0f);
+ }
+}
+
+void GradientNEW::InitializeOptimized(OptimizedGradient& gradient)
+{
+ // Copy all time values
+ for(int i = 0; i < m_NumColorKeys; ++i)
+ gradient.times[i] = m_ColorTime[i];
+
+ for(int i = 0, i2 = m_NumColorKeys; i < m_NumAlphaKeys; ++i, ++i2)
+ gradient.times[i2] = m_AlphaTime[i];
+
+ // Remove duplicates
+ int keyCount = m_NumColorKeys + m_NumAlphaKeys;
+ for(int i = 0; i < keyCount-1; ++i)
+ {
+ for(int j = i+1; j < keyCount; )
+ {
+ if(gradient.times[i] == gradient.times[j])
+ {
+ std::swap(gradient.times[j], gradient.times[keyCount-1]);
+ keyCount--;
+ continue;
+ }
+ ++j;
+ }
+ }
+
+ // Sort
+ int i = 0;
+ while ((i + 1) < keyCount)
+ {
+ if (gradient.times[i] > gradient.times[i+1])
+ {
+ std::swap(gradient.times[i], gradient.times[i+1]);
+ if (i > 0)
+ i -= 2;
+ }
+ i++;
+ }
+
+ for(int i = 0; i < keyCount; ++i)
+ gradient.colors[i] = Evaluate(WordToNormalized(gradient.times[i]));
+ gradient.keyCount = keyCount;
+
+ for(int i = 1; i < keyCount; ++i)
+ gradient.rcp[i] = ((((1<<24)) / std::max<UInt32>(gradient.times[i] - gradient.times[i-1], 1)))+1;
+}
+
+template<class TransferFunction>
+void GradientNEW::Transfer (TransferFunction& transfer)
+{
+ AssertIf (kGradientMaxNumKeys > 9);
+
+ const char* kKeyNames [kGradientMaxNumKeys] = { "key0", "key1", "key2", "key3", "key4", "key5", "key6", "key7", };
+ for(UInt32 i = 0; i < kGradientMaxNumKeys; i++)
+ transfer.Transfer(m_Keys[i], kKeyNames[i], kHideInEditorMask);
+
+ const char* kColorTimeNames [kGradientMaxNumKeys] = { "ctime0", "ctime1", "ctime2", "ctime3", "ctime4", "ctime5", "ctime6", "ctime7", };
+ for(UInt32 i = 0; i < kGradientMaxNumKeys; i++)
+ transfer.Transfer(m_ColorTime[i], kColorTimeNames[i], kHideInEditorMask);
+
+ const char* kAlphaTimeNames [kGradientMaxNumKeys] = { "atime0", "atime1", "atime2", "atime3", "atime4", "atime5", "atime6", "atime7", };
+ for(UInt32 i = 0; i < kGradientMaxNumKeys; i++)
+ transfer.Transfer(m_AlphaTime[i], kAlphaTimeNames[i], kHideInEditorMask);
+
+ transfer.Transfer (m_NumColorKeys, "m_NumColorKeys", kHideInEditorMask);
+ transfer.Transfer (m_NumAlphaKeys, "m_NumAlphaKeys", kHideInEditorMask);
+ transfer.Align();
+
+ if(transfer.IsReading())
+ {
+ ValidateColorKeys();
+ ValidateAlphaKeys();
+ }
+}
+INSTANTIATE_TEMPLATE_TRANSFER(GradientNEW)
+
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+bool CompareColors(ColorRGBA32 c0, ColorRGBA32 c1, int tolerance)
+{
+ if(Abs(c0.r-c1.r) > tolerance)
+ return false;
+ if(Abs(c0.g-c1.g) > tolerance)
+ return false;
+ if(Abs(c0.b-c1.b) > tolerance)
+ return false;
+ if(Abs(c0.a-c1.a) > tolerance)
+ return false;
+ return true;
+}
+
+SUITE (GradientTests)
+{
+TEST (GradientTests_GradientEvaluate)
+{
+ // Set up rainbow gradient
+ GradientNEW gradient;
+ gradient.SetNumColorKeys(5);
+ gradient.SetNumAlphaKeys(3);
+ gradient.GetKey(0) = ColorRGBA32(0xff, 0x00, 0x00, 0xff);
+ gradient.GetKey(1) = ColorRGBA32(0xf8, 0xff, 0x00, 0x00);
+ gradient.GetKey(2) = ColorRGBA32(0x00, 0xff, 0x49, 0xff);
+ gradient.GetKey(3) = ColorRGBA32(0x22, 0x00, 0xff, 0x00);
+ gradient.GetKey(4) = ColorRGBA32(0xff, 0x00, 0xe6, 0x00);
+ gradient.GetColorTime(0) = 0x0000;
+ gradient.GetColorTime(1) = 0x40c1;
+ gradient.GetColorTime(2) = 0x9212;
+ gradient.GetColorTime(3) = 0xce4e;
+ gradient.GetColorTime(4) = 0xffff;
+ gradient.GetAlphaTime(0) = 0x0000;
+ gradient.GetAlphaTime(1) = 0x8000;
+ gradient.GetAlphaTime(2) = 0xffff;
+
+ CHECK_EQUAL(ColorRGBA32(0xff, 0x00, 0x00, 0xff) == gradient.Evaluate(0.0f), true);
+ CHECK_EQUAL(ColorRGBA32(0xfd, 0x31, 0x00, 0xe6) == gradient.Evaluate(0.05f), true);
+ CHECK_EQUAL(ColorRGBA32(0xfa, 0x96, 0x00, 0xb3) == gradient.Evaluate(0.15f), true);
+ CHECK_EQUAL(ColorRGBA32(0xf8, 0xfc, 0x00, 0x7f) == gradient.Evaluate(0.25f), true);
+ CHECK_EQUAL(ColorRGBA32(0xac, 0xff, 0x16, 0x4c) == gradient.Evaluate(0.35f), true);
+ CHECK_EQUAL(ColorRGBA32(0x5e, 0xff, 0x2d, 0x19) == gradient.Evaluate(0.45f), true);
+ CHECK_EQUAL(ColorRGBA32(0x10, 0xff, 0x44, 0x18) == gradient.Evaluate(0.55f), true);
+ CHECK_EQUAL(ColorRGBA32(0x0b, 0xa9, 0x86, 0x4b) == gradient.Evaluate(0.65f), true);
+ CHECK_EQUAL(ColorRGBA32(0x19, 0x3c, 0xd3, 0x7e) == gradient.Evaluate(0.75f), true);
+ CHECK_EQUAL(ColorRGBA32(0x54, 0x00, 0xf9, 0xb2) == gradient.Evaluate(0.85f), true);
+ CHECK_EQUAL(ColorRGBA32(0xc6, 0x00, 0xec, 0xe5) == gradient.Evaluate(0.95f), true);
+ CHECK_EQUAL(ColorRGBA32(0xff, 0x00, 0xe6, 0xff) == gradient.Evaluate(1.0f), true);
+
+ OptimizedGradient optGradient;
+ gradient.InitializeOptimized(optGradient);
+
+#if UNITY_LINUX
+#warning Investigate/fix GradientEvaluateTest!
+#else
+ // Being off by 1LSB is okay... (due to rounding)
+ for(float time = 0.0f; time <= 1.0f; time += 0.02f)
+ CHECK_EQUAL(CompareColors(optGradient.Evaluate(time), gradient.Evaluate(time), 1), true);
+
+ // ... but require exactness precicely at key times
+ for(int i = 0; i < 5; i++)
+ {
+ float time = WordToNormalized(gradient.GetColorTime(i));
+ CHECK_EQUAL(CompareColors(optGradient.Evaluate(time), gradient.Evaluate(time), 0), true);
+ }
+ for(int i = 0; i < 3; i++)
+ {
+ float time = WordToNormalized(gradient.GetAlphaTime(i));
+ CHECK_EQUAL(CompareColors(optGradient.Evaluate(time), gradient.Evaluate(time), 0), true);
+ }
+#endif
+}
+}
+
+#endif
diff --git a/Runtime/Math/Gradient.h b/Runtime/Math/Gradient.h
new file mode 100644
index 0000000..8469033
--- /dev/null
+++ b/Runtime/Math/Gradient.h
@@ -0,0 +1,219 @@
+#ifndef GRADIENT_H
+#define GRADIENT_H
+
+#include "Color.h"
+#include "Runtime/Utilities/LogAssert.h"
+
+enum
+{
+ kGradientMaxNumKeys = 8,
+ kOptimizedGradientMaxNumKeys = kGradientMaxNumKeys + kGradientMaxNumKeys, // color keys + alpha keys
+};
+
+// Optimized version of gradient
+struct OptimizedGradient
+{
+ static inline UInt32 InverseLerpWordOptimized (UInt32 from, UInt32 rcp, UInt32 v)
+ {
+ DebugAssert((from & 0xffff) == from);
+ DebugAssert((v & 0xffff) == v);
+ return ((v - from) * rcp)>>16;
+ }
+
+ inline ColorRGBA32 Evaluate(float normalizedTime) const
+ {
+ DebugAssert((normalizedTime >= 0.0f) && (normalizedTime <= 1.0f));
+ DebugAssert(keyCount >= 2);
+
+ UInt32 time = NormalizedToWord(normalizedTime);
+
+ // Color blend
+ const UInt32 numKeys = keyCount;
+ time = std::min(std::max((UInt32)times[0], time), (UInt32)times[keyCount-1]); // TODO: Is this necessary?
+ for (int i = 1; i < numKeys; i++)
+ {
+ const UInt32 currTime = times[i];
+ if(time <= currTime)
+ {
+ const UInt32 prevTime = times[i-1];
+ const UInt32 frac = InverseLerpWordOptimized(prevTime, rcp[i], time);
+ return Lerp (colors[i-1], colors[i], frac);
+ }
+ }
+ return ColorRGBA32 (0xff,0xff,0xff,0xff);
+ }
+
+ ColorRGBA32 colors[kOptimizedGradientMaxNumKeys];
+ UInt32 times[kOptimizedGradientMaxNumKeys];
+ UInt32 rcp[kOptimizedGradientMaxNumKeys]; // precomputed reciprocals
+ UInt32 keyCount;
+};
+
+// Work in progress (Rename NEW to something else when found..)
+class GradientNEW
+{
+public:
+ GradientNEW ();
+ ~GradientNEW ();
+
+ DECLARE_SERIALIZE_NO_PPTR (GradientNEW)
+
+ ColorRGBA32 Evaluate(float time) const;
+
+ struct ColorKey
+ {
+ DEFINE_GET_TYPESTRING (GradientColorKey)
+
+ ColorKey () {}
+ ColorKey (ColorRGBAf color, float time) {m_Color = color; m_Time = time;}
+ ColorRGBAf m_Color;
+ float m_Time;
+ };
+
+ struct AlphaKey
+ {
+ DEFINE_GET_TYPESTRING (GradientAlphaKey)
+
+ AlphaKey () {}
+ AlphaKey (float alpha, float time) {m_Alpha = alpha; m_Time = time;}
+ float m_Alpha;
+ float m_Time;
+ };
+
+ void SetKeys (ColorKey* colorKeys, unsigned numColorKeys, AlphaKey* alphaKeys, unsigned numAlphaKeys);
+
+ void SetColorKeys (ColorKey* colorKeys, unsigned numKeys);
+ void SetAlphaKeys (AlphaKey* alphaKeys, unsigned numKeys);
+
+ void SetNumColorKeys (int numColorKeys) { m_NumColorKeys = numColorKeys;};
+ void SetNumAlphaKeys (int numAlphaKeys) { m_NumAlphaKeys = numAlphaKeys; };
+
+ int GetNumColorKeys () const { return m_NumColorKeys; }
+ int GetNumAlphaKeys () const { return m_NumAlphaKeys; }
+
+ ColorRGBA32& GetKey (unsigned index) { return m_Keys[index]; }
+ const ColorRGBA32& GetKey (unsigned index) const { return m_Keys[index]; }
+
+ UInt16& GetColorTime (unsigned index) { return m_ColorTime[index]; }
+ const UInt16& GetColorTime (unsigned index) const { return m_ColorTime[index]; }
+
+ UInt16& GetAlphaTime(unsigned index) { return m_AlphaTime[index]; }
+ const UInt16& GetAlphaTime(unsigned index) const { return m_AlphaTime[index]; }
+
+ ColorRGBA32 GetConstantColor () const;
+ void SetConstantColor (ColorRGBA32 color);
+
+ void SwapColorKeys (int i, int j);
+ void SwapAlphaKeys (int i, int j);
+
+ void InitializeOptimized(OptimizedGradient& g);
+
+private:
+ static inline UInt32 InverseLerpWord (UInt32 from, UInt32 to, UInt32 v)
+ {
+ DebugAssert((from & 0xffff) == from);
+ DebugAssert((to & 0xffff) == to);
+ DebugAssert((v & 0xffff) == v);
+ DebugAssert (from <= to);
+ UInt32 nom = (v - from) << 16;
+ UInt32 den = std::max<UInt32>(to - from, 1);
+ UInt32 res = nom / den;
+ return res;
+ }
+
+ static inline UInt32 LerpByte(UInt32 u0, UInt32 u1, UInt32 scale)
+ {
+ DebugAssert((u0 & 0xff) == u0);
+ DebugAssert((u1 & 0xff) == u1);
+ //DebugAssert((scale & 0xff) == scale);
+ return u0 + (((u1 - u0) * scale) >> 8) & 0xff;
+ }
+
+ void ValidateColorKeys();
+ void ValidateAlphaKeys();
+
+ ColorRGBA32 m_Keys[kGradientMaxNumKeys];
+ UInt16 m_ColorTime[kGradientMaxNumKeys];
+ UInt16 m_AlphaTime[kGradientMaxNumKeys];
+ UInt8 m_NumColorKeys;
+ UInt8 m_NumAlphaKeys;
+};
+
+inline ColorRGBA32 GradientNEW::Evaluate(float normalizedTime) const
+{
+ DebugAssert((normalizedTime >= 0.0f) && (normalizedTime <= 1.0f));
+ DebugAssert(m_NumColorKeys >= 2);
+ DebugAssert(m_NumAlphaKeys >= 2);
+
+ ColorRGBA32 color = ColorRGBA32 (0xff,0xff,0xff,0xff);
+ const UInt32 time = NormalizedToWord(normalizedTime);
+
+ // Color blend
+ const UInt32 numColorKeys = m_NumColorKeys;
+ const UInt32 timeColor = std::min(std::max((UInt32)m_ColorTime[0], time), (UInt32)m_ColorTime[numColorKeys-1]);
+ for (int i = 1; i < numColorKeys; i++)
+ {
+ const UInt32 currTime = m_ColorTime[i];
+ if(timeColor <= currTime)
+ {
+ const UInt32 prevTime = m_ColorTime[i-1];
+ const UInt32 frac = InverseLerpWord(prevTime, currTime, timeColor) >> 8; // frac is byte
+ color = Lerp (m_Keys[i-1], m_Keys[i], frac);
+ break;
+ }
+ }
+
+ // Alpha blend
+ const UInt32 numAlphaKeys = m_NumAlphaKeys;
+ const UInt32 timeAlpha = std::min(std::max((UInt32)m_AlphaTime[0], time), (UInt32)m_AlphaTime[numAlphaKeys-1]);
+ for (int i = 1; i < numAlphaKeys; i++)
+ {
+ const UInt32 currTime = m_AlphaTime[i];
+ if(timeAlpha <= currTime)
+ {
+ const UInt32 prevTime = m_AlphaTime[i-1];
+ const UInt32 frac = InverseLerpWord(prevTime, currTime, timeAlpha) >> 8; // frac is byte
+ color.a = LerpByte(m_Keys[i-1].a, m_Keys[i].a, frac);
+ break;
+ }
+ }
+
+ return color;
+}
+
+/// Simple class to interpolate between colors.
+template<int size>
+class GradientDeprecated
+ {
+ public:
+ DEFINE_GET_TYPESTRING (Gradient)
+
+ template<class TransferFunc>
+ void Transfer (TransferFunc& transfer) {
+ AssertIf (size > 9);
+ char name[] = "m_Color[ ]";
+ for (int i=0;i<size;i++)
+ {
+ name[8] = '0' + i;
+ transfer.Transfer (m_Colors[i], name);
+ }
+ }
+
+ /// Get a color
+ ColorRGBA32 &operator[] (int i) { AssertIf (i < 0 || i >= size); return m_Colors[i]; }
+ /// Get a color
+ const ColorRGBA32 &operator[] (int i) const { AssertIf (i < 0 || i >= size); return m_Colors[i]; }
+
+ /// Get the color value at a given position
+ /// @param position a position in unnormalized 16.16 bit fixed
+ ColorRGBA32 GetFixed (UInt32 position) const {
+ AssertIf ((position >> 16) >= size - 1);
+ return Lerp (m_Colors[position >> 16], m_Colors[(position >> 16) + 1], (position >> 8) & 255);
+ }
+
+ private:
+ /// The array of colors this interpolator works through
+ ColorRGBA32 m_Colors[size];
+};
+
+#endif
diff --git a/Runtime/Math/MathTests.cpp b/Runtime/Math/MathTests.cpp
new file mode 100644
index 0000000..229406a
--- /dev/null
+++ b/Runtime/Math/MathTests.cpp
@@ -0,0 +1,726 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Math/FloatConversion.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/Math/SphericalHarmonics.h"
+#if UNITY_WIN && !UNITY_WINRT
+#include "External/DirectX/builds/dx9include/d3dx9.h"
+#endif
+#include <vector>
+
+
+SUITE (MathTests)
+{
+
+#if UNITY_WIN
+#pragma warning( disable : 4723 ) //required for the divide by 0 that's happening in this test.
+#endif
+
+TEST (Math_Nan)
+{
+ struct
+ {
+ inline bool IsNANNew (float A)
+ {
+ // A NAN has an exponent of 255 (shifted left 23 positions) and a non-zero mantissa.
+ int exp = *(int*)&A & 0x7F800000;
+ int mantissa = *(int*)&A & 0x007FFFFF;
+ return exp == 0x7F800000 && mantissa != 0;
+ }
+
+ inline void operator () (bool expect, float A)
+ {
+ CHECK_EQUAL (IsNANNew (A), IsNAN (A));
+ CHECK_EQUAL (expect, IsNAN (A));
+ }
+ } CheckNAN;
+
+ float f = 0.0F;
+ float f0 = 0.0F;
+
+ f = f / f0;
+
+ CheckNAN (true, f);
+
+ CheckNAN (true, std::numeric_limits<float>::quiet_NaN ());
+ CheckNAN (true, std::numeric_limits<float>::signaling_NaN ());
+ CheckNAN (false, 1.0F);
+ CheckNAN (false, 0.0F);
+}
+
+#if UNITY_WIN
+#pragma warning( default : 4723 )
+#endif
+
+TEST (Math_Matrix)
+{
+ Matrix4x4f m0, m1, m2, m6;
+
+ for (int i = 0; i < 16; ++i)
+ {
+ m0.m_Data[i] = (float)i;
+ m1.m_Data[15 - i] = (float)i;
+ }
+
+ MultiplyMatrices4x4REF (&m0, &m1, &m2);
+ MultiplyMatrices4x4 (&m0, &m1, &m6);
+ CHECK_EQUAL (0, memcmp (m2.m_Data, m6.m_Data, sizeof(Matrix4x4f)));
+
+ TransposeMatrix4x4REF(&m0, &m2);
+ TransposeMatrix4x4(&m0, &m6);
+ CHECK_EQUAL (0, memcmp (m2.m_Data, m6.m_Data, sizeof(Matrix4x4f)));
+
+ Vector3f v (2.0F, 5.0F, 2.0F);
+ Vector3f res (2.0F, 5.0F, -2.0F);
+
+ Quaternionf q;
+ Quaternionf backConvertedQ;
+ Matrix3x3f m3;
+ Vector3f tempResult;
+
+ q = AxisAngleToQuaternion (Vector3f::yAxis, kPI / 2.0F);
+ QuaternionToMatrix (q, m3);
+
+ CHECK_EQUAL (true, CompareApproximately (RotateVectorByQuat(q, v), res));
+ CHECK_EQUAL (true, CompareApproximately (m3.MultiplyPoint3 (v), res));
+
+ MatrixToQuaternion (m3, backConvertedQ);
+ CHECK_EQUAL (true, CompareApproximately (backConvertedQ, q));
+
+ Vector3f axis;
+ float roll;
+
+ QuaternionToAxisAngle (backConvertedQ, &axis, &roll);
+ CHECK_EQUAL (true, CompareApproximately (axis, Vector3f::yAxis));
+ CHECK_CLOSE (kPI / 2.0F, roll, 0.000001F);
+
+ q = Inverse (q);
+ m3.Invert ();
+ MatrixToQuaternion (m3, backConvertedQ);
+ CHECK_EQUAL (true, CompareApproximately (backConvertedQ, q));
+
+ tempResult = RotateVectorByQuat (q, res);
+ CHECK_EQUAL (true, CompareApproximately (tempResult, v));
+ tempResult = RotateVectorByQuat (backConvertedQ, res);
+ CHECK_EQUAL (true, CompareApproximately (tempResult, v));
+ tempResult = m3.MultiplyPoint3 (res);
+ CHECK_EQUAL (true, CompareApproximately (tempResult, v));
+}
+
+
+
+TEST (Math_NormalizeFastTest)
+{
+ Vector3f input[] = { Vector3f (0.0f, 0.1f, 0.0f), Vector3f (0.0f, 0.0f, 0.0f), Vector3f (-0.0f, -0.0f, -0.0f) };
+ Vector3f output[] = { Vector3f (0.0f, 1.f, 0.0f), Vector3f (0.0f, 0.0f, 0.0f), Vector3f (-0.0f, -0.0f, -0.0f) };
+
+ for (int i=0;i<3;i++)
+ {
+ Vector3f normalized = NormalizeFast(input[i]);
+ CHECK (CompareApproximately (output[i] , normalized, 0.0001f));
+ }
+}
+
+
+
+TEST (Math_MatrixQuaternionConversion)
+{
+ Rand rand (GetTimeSinceStartup ());
+ for (int i = 0; i < 500; ++i)
+ {
+ Quaternionf rot = RandomQuaternion (rand);
+ Quaternionf outq, outq2;
+ Matrix3x3f m, outm;
+ QuaternionToMatrix (rot, m);
+ Vector3f angle;
+
+ MatrixToEuler (m, angle);
+ EulerToMatrix (angle, outm);
+ outq2 = EulerToQuaternion (angle);
+
+ MatrixToQuaternion (outm, outq);
+ CHECK (CompareApproximately (m , outm, 0.1f));
+ CHECK_CLOSE (1, Abs (Dot (outq, rot)), 0.01f);
+ CHECK_CLOSE (1, Abs (Dot (outq2, rot)), 0.01f);
+ }
+}
+
+
+TEST (Math_EulerAngles)
+{
+ struct
+ {
+ void operator() (float x, float y, float z)
+ {
+ Quaternionf quat = EulerToQuaternion (Vector3f (Deg2Rad (x), Deg2Rad (y), Deg2Rad (z)));
+ Matrix3x3f quatM;
+ QuaternionToMatrix (quat, quatM);
+
+ Vector3f euler = QuaternionToEuler (quat);
+ Vector3f eulerDeg (Rad2Deg (euler.x), Rad2Deg (euler.y), Rad2Deg (euler.z));
+ Quaternionf newquat = EulerToQuaternion (euler);
+
+ CHECK_CLOSE (Abs (Dot (newquat, quat)), 1, 0.01f);
+ }
+ } TestEulerAngles;
+
+ TestEulerAngles ( 90.0f, 45.0f, 0.0f);
+ TestEulerAngles ( 90.0f, 90.0f, 0.0f);
+ TestEulerAngles (270.0f, 0.0f, 0.0f);
+ TestEulerAngles (270.0f, 40.0f, 0.0f);
+}
+
+TEST (Math_EulerAnglesMatchAxisAngle)
+{
+ Quaternionf quat = AxisAngleToQuaternion(Vector3f::yAxis, Deg2Rad(20.0F));
+ Vector3f euler = QuaternionToEuler(quat);
+ CHECK_EQUAL (true, CompareApproximately (0, euler.x));
+ CHECK_EQUAL (true, CompareApproximately (Deg2Rad(20.0F), euler.y));
+ CHECK_EQUAL (true, CompareApproximately (0, euler.z));
+}
+
+// This test fails with the current version of QuaternionToEuler. The angles
+// being close to gimbal lock will snap to 90 degree increments.
+#if 0
+
+TEST (Math_QuaternionToEulerHandlesGimbalLock)
+{
+ Quaternionf quat = EulerToQuaternion (Vector3f (Deg2Rad (269.5f), 0.f, 0.f));
+// printf( "%f, %f, %f, %f\n", quat.x, quat.y, quat.z, quat.w);
+ Vector3f euler = QuaternionToEuler (quat);
+// printf( "%f, %f, %f\n", Rad2Deg(euler.x), Rad2Deg(euler.y), Rad2Deg(euler.z));
+ Quaternionf quat1 = EulerToQuaternion (euler);
+// printf( "%f, %f, %f, %f\n", quat1.x, quat1.y, quat1.z, quat1.w);
+
+ CHECK_CLOSE (269.5f, Rad2Deg (euler.x), 0.01f);
+ CHECK_CLOSE (0.f, euler.y, 0.01f);
+ CHECK_CLOSE (0.f, euler.z, 0.01f);
+
+ quat = EulerToQuaternion (Vector3f (Deg2Rad (89.5f), 0.f, 0.f));
+ euler = QuaternionToEuler (quat);
+
+ CHECK_CLOSE (89.5f, Rad2Deg (euler.x), 0.01f);
+ CHECK_CLOSE (0.f, euler.y, 0.01f);
+ CHECK_CLOSE (0.f, euler.z, 0.01f);
+
+ quat = EulerToQuaternion (Vector3f (Deg2Rad (89.0f), 0.f, 0.f));
+ euler = QuaternionToEuler (quat);
+
+ CHECK_CLOSE (89.0f, Rad2Deg (euler.x), 0.01f);
+ CHECK_CLOSE (0.f, euler.y, 0.01f);
+ CHECK_CLOSE (0.f, euler.z, 0.01f);
+
+ quat = EulerToQuaternion (Vector3f (Deg2Rad (88.5f), 0.f, 0.f));
+ euler = QuaternionToEuler (quat);
+
+// printf( "%f, %f, %f\n", Rad2Deg(euler.x), Rad2Deg(euler.y), Rad2Deg(euler.z));
+
+ CHECK_CLOSE (88.5f, Rad2Deg (euler.x), 0.01f);
+ CHECK_CLOSE (0.f, euler.y, 0.01f);
+ CHECK_CLOSE (0.f, euler.z, 0.01f);
+}
+
+#endif
+
+TEST (Math_ColorRGBA32Lerp)
+{
+#if UNITY_LINUX
+#warning Investigate/fix ColorRGBA32 Tests!
+#else
+ ColorRGBA32 c0, c1, res;
+
+ c0 = ColorRGBA32 (100, 150, 255, 0);
+ c1 = ColorRGBA32 (200, 100, 0, 255);
+
+ res = Lerp (c0, c1, 0);
+ CHECK (ColorRGBA32 (100, 150, 255, 0) == res);
+
+ res = Lerp (c0, c1, 90);
+ CHECK (ColorRGBA32 (135, 132,165,89) == res);
+
+ res = Lerp (c0, c1, 200);
+ CHECK (ColorRGBA32 (178, 110,55,199) == res);
+
+ res = Lerp (c0, c1, 255);
+ CHECK (ColorRGBA32 (199, 100, 0, 254) == res);
+#endif
+}
+
+
+TEST (Math_ColorRGBA32Scale)
+{
+#if UNITY_LINUX
+#warning Investigate/fix ColorRGBA32 Tests!
+#else
+ ColorRGBA32 c0, res;
+
+ c0 = ColorRGBA32 (100, 150, 255, 150);
+
+ res = c0 * 0;
+ CHECK (ColorRGBA32 (0, 0, 0, 0) == res);
+
+ res = c0 * 20;
+ CHECK (ColorRGBA32 (8, 12, 20, 12) == res);
+
+ res = c0 * 150;
+ CHECK (ColorRGBA32 (58, 88, 150, 88) == res);
+
+ res = c0 * 255;
+ CHECK (ColorRGBA32 (100, 150, 255, 150) == res);
+#endif
+}
+
+void TestMultiplyColorRGBA32(const ColorRGBA32 input0, const ColorRGBA32 input1, int tolerance)
+{
+ ColorRGBA32 expected;
+ ColorRGBA32 actual;
+ expected.r = (input0.r * input1.r) / 255;
+ expected.g = (input0.g * input1.g) / 255;
+ expected.b = (input0.b * input1.b) / 255;
+ expected.a = (input0.a * input1.a) / 255;
+ actual = input0*input1;
+
+ CHECK_CLOSE((int)expected.r, (int)actual.r, tolerance);
+ CHECK_CLOSE((int)expected.g, (int)actual.g, tolerance);
+ CHECK_CLOSE((int)expected.b, (int)actual.b, tolerance);
+ CHECK_CLOSE((int)expected.a, (int)actual.a, tolerance);
+}
+
+TEST (Math_ColorRGBA32Muliply)
+{
+ for(int i = 0; i < 256; i+=4)
+ {
+ TestMultiplyColorRGBA32(ColorRGBA32(0,0,0,0), ColorRGBA32(i+0,i+1,i+2,i+3), 0);
+ TestMultiplyColorRGBA32(ColorRGBA32(i+0,i+1,i+2,i+3), ColorRGBA32(0,0,0,0), 0);
+ TestMultiplyColorRGBA32(ColorRGBA32(i+0,i+1,i+2,i+3), ColorRGBA32(0xff,0xff,0xff,0xff), 0);
+ TestMultiplyColorRGBA32(ColorRGBA32(0xff,0xff,0xff,0xff), ColorRGBA32(i+0,i+1,i+2,i+3), 0);
+ }
+
+ for(int i = 0; i < 256; i+=4)
+ for(int j = i; j < 256; j+=4)
+ TestMultiplyColorRGBA32(ColorRGBA32(j+0,j+1,j+2,j+3), ColorRGBA32(i+0,i+1,i+2,i+3), 1);
+}
+
+// Reference Implementation: D3DX; thus only test on Windows
+#if UNITY_WIN && !UNITY_WINRT
+TEST (Math_SphericalHarmonics)
+{
+ Rand r;
+
+ for (int i = 0; i < 10000; ++i)
+ {
+ float x = r.GetFloat () * 2.0f - 1.0f;
+ float y = r.GetFloat () * 2.0f - 1.0f;
+ float z = r.GetFloat () * 2.0f - 1.0f;
+ float sh[9], d3dxsh[9];
+
+ SHEvalDirection9 (x, y, z, sh);
+ D3DXSHEvalDirection (d3dxsh, 3, &D3DXVECTOR3 (x, y, z));
+ for (int j = 0; j < 9; ++j)
+ {
+ CHECK_CLOSE (sh[j], d3dxsh[j], 0.000001f);
+ }
+
+ float shR[9], shG[9], shB[9];
+ float d3dxshR[9], d3dxshG[9], d3dxshB[9];
+ SHEvalDirectionalLight9 (x, y, z, 0.1f, 0.2f, 0.3f, shR, shG, shB);
+ D3DXSHEvalDirectionalLight (3, &D3DXVECTOR3(x,y,z), 0.1f, 0.2f, 0.3f, d3dxshR, d3dxshG, d3dxshB);
+ for (int j = 0; j < 9; ++j)
+ {
+ CHECK_CLOSE (shR[j], d3dxshR[j], 0.000001f);
+ CHECK_CLOSE (shG[j], d3dxshG[j], 0.000001f);
+ CHECK_CLOSE (shB[j], d3dxshB[j], 0.000001f);
+ }
+ }
+}
+#endif
+
+
+void FabsPerformance ();
+
+TEST (Math_Repeat)
+{
+ CHECK_EQUAL (15.0F, Repeat (-5.0F, 20.0F));
+ CHECK_EQUAL ( 5.0F, Repeat (5.0F, 20.0F));
+ CHECK_EQUAL ( 5.0F, Repeat (25.0F, 20.0F));
+ CHECK_EQUAL ( 0.0F, Repeat (20.0F, 20.0F));
+ CHECK_EQUAL ( 0.0F, Repeat (0.0F, 20.0F));
+ CHECK_EQUAL (19.9F, Repeat (-0.1F, 20.0F));
+ CHECK_EQUAL (10.0F, Repeat (-10.0F, 20.0F));
+ CHECK_CLOSE (0.139999F, Repeat (0.699999F, 0.14F), 1e-5f);
+ //CHECK (Repeat (0.69999999F, 0.14F) >= 0.0f) // This fails! Revisit for the next breaking version.
+
+ // Our Repeat inverts when in negative space
+ CHECK_CLOSE ( 3.0F, Repeat ( 3.0F, 5.0F), 1e-5f);
+ CHECK_CLOSE (-2.0F, Repeat ( 3.0F, -5.0F), 1e-5f);
+ CHECK_CLOSE (-3.0F, Repeat (-3.0F, -5.0F), 1e-5f);
+ CHECK_CLOSE ( 2.0F, Repeat (-3.0F, 5.0F), 1e-5f);
+ CHECK_CLOSE ( 0.0F, Repeat ( 0.0F, -1.0F), 1e-5f);
+ CHECK_CLOSE ( 0.0F, Repeat ( 0.0F, 1.0F), 1e-5f);
+
+ CHECK_CLOSE ( 1.0F, Repeat (-59.0F, 30.0F), 1e-5f);
+ CHECK_CLOSE ( 0.0F, Repeat (-60.0F, 30.0F), 1e-5f);
+ CHECK_CLOSE (29.0F, Repeat (-61.0F, 30.0F), 1e-5f);
+}
+
+TEST (Math_DeltaAngleRad)
+{
+ CHECK_EQUAL (0, DeltaAngleRad (12345.67890F, 12345.67890F));
+
+ CHECK_EQUAL (kPI, DeltaAngleRad (0, -kPI));
+ CHECK_EQUAL (kPI, DeltaAngleRad (0, kPI));
+ CHECK_EQUAL (kPI, DeltaAngleRad (kPI, 0));
+
+ CHECK_EQUAL (0, DeltaAngleRad (1.0F, 1.0F+2*kPI));
+ CHECK_EQUAL (0, DeltaAngleRad (1.0F+2*kPI, 1.0F));
+
+ CHECK_CLOSE ( kPI/2, DeltaAngleRad (0, 5*kPI/2), 1e-5f);
+ CHECK_CLOSE (-kPI/2, DeltaAngleRad (0, 7*kPI/2), 1e-5f);
+}
+
+/*
+TEST (Math_RoundFunctions)
+{
+ struct
+ {
+ void operator() (float t, int floor, int ceil, int round)
+ {
+ CHECK_EQUAL (floor, std::floor(t));
+ CHECK_EQUAL (ceil, std::ceil(t));
+
+ CHECK_EQUAL (round, RoundfToInt(t));
+ CHECK_EQUAL (round, Roundf(t));
+
+ CHECK_EQUAL (floor, Floorf(t));
+ CHECK_EQUAL (floor, FloorfToInt(t));
+
+ CHECK_EQUAL (ceil, Ceilf(t));
+ CHECK_EQUAL (ceil, CeilfToInt(t));
+
+ if (t >= 0.0F)
+ {
+ CHECK_EQUAL (RoundfToIntPos(t), round);
+ CHECK_EQUAL (FloorfToIntPos(t), floor);
+ CHECK_EQUAL (CeilfToIntPos(t), ceil);
+ }
+ }
+ } TestRoundFunctions;
+
+ CHECK_EQUAL (64, NextPowerOfTwo (33));
+ CHECK_EQUAL (32, NextPowerOfTwo (32));
+ CHECK_EQUAL (32, NextPowerOfTwo (31));
+
+ TestRoundFunctions (15.1F, 15, 16, 15);
+ TestRoundFunctions (0.9F, 0, 1, 1);
+
+ TestRoundFunctions (1.0F, 1, 1, 1);
+ TestRoundFunctions (2.0F, 2, 2, 2);
+
+ TestRoundFunctions (5.9F, 5, 6, 6);
+ TestRoundFunctions (7.1F, 7, 8, 7);
+ TestRoundFunctions (7.6F, 7, 8, 8);
+ TestRoundFunctions (0.49F, 0, 1, 0);
+ TestRoundFunctions (120000.51F, 120000, 120001, 120001);
+
+ TestRoundFunctions (-19.7F, -20, -19, -20);
+ TestRoundFunctions (-16.01F, -17, -16, -16);
+ TestRoundFunctions (-25.0F, -25, -25, -25);
+ TestRoundFunctions (-25.501F, -26, -25, -26);
+ TestRoundFunctions (-5.9F, -6, -5, -6);
+ TestRoundFunctions (-7.1F, -8, -7, -7);
+ TestRoundFunctions (-7.6F, -8, -7, -8);
+ TestRoundFunctions (-0.1F, -1, 0, 0);
+ TestRoundFunctions (-0.0000011F, -1, 0, 0);
+ TestRoundFunctions (-0.25F, -1, 0, 0);
+ TestRoundFunctions (-0.49F, -1, 0, 0);
+ TestRoundFunctions (-0.51F, -1, 0, -1);
+ TestRoundFunctions (-0.6F, -1, 0, -1);
+ TestRoundFunctions (-1.0F, -1, -1, -1);
+ TestRoundFunctions (-2.0F, -2, -2, -2);
+ TestRoundFunctions (-1.01F, -2, -1, -1);
+ TestRoundFunctions (-100000.49F, -100001, -100000, -100000);
+
+ CHECK_EQUAL (1, RoundfToInt(0.5F));
+ CHECK_EQUAL (2, RoundfToInt(1.5F));
+ CHECK_EQUAL (0, RoundfToInt(-0.5F));
+ CHECK_EQUAL (-1, RoundfToInt(-1.5F));
+
+ // Rounding up or down, doesn't have to match floor / ceil function. Pick fastest
+ //ErrorIf (TestFloor (120000.51F, 120000, 120000, 120000));
+
+ CHECK_EQUAL (15, FloorfToIntPos (15.1F));
+ CHECK_EQUAL (0, FloorfToIntPos (0.9F));
+
+ CHECK_EQUAL (16, CeilfToIntPos (15.1F));
+ CHECK_EQUAL (1, CeilfToIntPos (0.9F));
+
+ CHECK_EQUAL (15, RoundfToIntPos (15.1F));
+ CHECK_EQUAL (1, RoundfToIntPos (0.9F));
+}
+*/
+
+TEST (Math_TransformPoints)
+{
+ Vector3f v (1, 0, 0);
+ Matrix4x4f tr;
+ tr.SetTR (Vector3f(10,0,0), AxisAngleToQuaternion (Vector3f::zAxis, Deg2Rad (90)));
+
+ //Must ignore the translation, and work when input and output are the same.
+ TransformPoints3x3 (tr, &v, &v, 1);
+
+ CHECK (CompareApproximately (v, Vector3f(0, 1, 0)));
+}
+
+
+TEST (TypeSizes)
+{
+ CHECK_EQUAL (4, sizeof(SInt32));
+ CHECK_EQUAL (4, sizeof(UInt32));
+
+ CHECK_EQUAL (2, sizeof(SInt16));
+ CHECK_EQUAL (2, sizeof(UInt16));
+
+ CHECK_EQUAL (1, sizeof(UInt8));
+ CHECK_EQUAL (1, sizeof(SInt8));
+
+ CHECK_EQUAL (8, sizeof(UInt64));
+ CHECK_EQUAL (8, sizeof(SInt64));
+}
+
+
+TEST (Math_QuaternionMatrixEquivalence)
+{
+ Matrix3x3f m;
+ EulerToMatrix (Vector3f (Deg2Rad (15.0F), Deg2Rad (20.0F), Deg2Rad (64.0F)), m);
+
+ Quaternionf q;
+ MatrixToQuaternion (m, q);
+
+ Vector3f v (25.3F, 27.14F, 34.2F);
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+ Vector3f quatRes = RotateVectorByQuat (q, v);
+
+ CHECK (CompareApproximately (matrixRes, quatRes));
+ }
+
+ {
+ Matrix3x3f m2 (m);
+ m2.Scale (Vector3f (1.0F, 1.0F, -1.0F));
+
+ CHECK (CompareApproximately (m2.GetDeterminant (), -1.0F));
+ }
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+ Vector3f quatRes = RotateVectorByQuat (q, Vector3f (v.x, v.y, v.z));
+
+ CHECK (CompareApproximately (matrixRes, quatRes));
+ }
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+
+ Quaternionf modQ = q;
+ modQ.x *= -1.0F;
+ modQ.y *= -1.0F;
+ modQ.z *= -1.0F;
+ modQ.w *= -1.0F;
+ Vector3f quatRes = RotateVectorByQuat(modQ, v);
+
+ CHECK (CompareApproximately (matrixRes, quatRes));
+ }
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+
+ Quaternionf modQ = q;
+ modQ.x *= 1.0F;
+ modQ.y *= -1.0F;
+ modQ.z *= -1.0F;
+ modQ.w *= -1.0F;
+
+ Vector3f quatRes = RotateVectorByQuat(modQ, v);
+
+ CHECK (!CompareApproximately (matrixRes, quatRes));
+ }
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+
+ Quaternionf modQ = q;
+ modQ.x *= 1.0F;
+ modQ.y *= 1.0F;
+ modQ.z *= -1.0F;
+ modQ.w *= -1.0F;
+
+ Vector3f quatRes = RotateVectorByQuat(modQ, v);
+
+ CHECK (!CompareApproximately (matrixRes, quatRes));
+ }
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+
+ Quaternionf modQ = q;
+ modQ.x *= 1.0F;
+ modQ.y *= 1.0F;
+ modQ.z *= 1.0F;
+ modQ.w *= -1.0F;
+
+ Vector3f quatRes = RotateVectorByQuat(modQ, v);
+
+ CHECK (!CompareApproximately (matrixRes, quatRes));
+ }
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+
+ Quaternionf modQ = q;
+ modQ.x *= -1.0F;
+ modQ.y *= -1.0F;
+ modQ.z *= 1.0F;
+ modQ.w *= -1.0F;
+
+ Vector3f quatRes = RotateVectorByQuat(modQ, v);
+
+ CHECK (!CompareApproximately (matrixRes, quatRes));
+ }
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+
+ Quaternionf modQ = q;
+ modQ.x *= -1.0F;
+ modQ.y *= -1.0F;
+ modQ.z *= 1.0F;
+ modQ.w *= 1.0F;
+
+ Vector3f quatRes = RotateVectorByQuat(modQ, v);
+
+ CHECK (!CompareApproximately (matrixRes, quatRes));
+ }
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+
+ Quaternionf modQ = q;
+ modQ.x *= 1.0F;
+ modQ.y *= 1.0F;
+ modQ.z *= -1.0F;
+ modQ.w *= 1.0F;
+
+ Vector3f quatRes = RotateVectorByQuat(modQ, v);
+
+ CHECK (!CompareApproximately (matrixRes, quatRes));
+ }
+
+ {
+ Vector3f matrixRes (m.MultiplyPoint3 (v));
+
+ Quaternionf modQ = q;
+ modQ.x *= -1.0F;
+ modQ.y *= 1.0F;
+ modQ.z *= -1.0F;
+ modQ.w *= 1.0F;
+
+ Vector3f quatRes = RotateVectorByQuat(modQ, v);
+
+ CHECK (!CompareApproximately (matrixRes, quatRes));
+ }
+}
+
+
+TEST (Math_ColorMisc)
+{
+ {
+ ColorRGBA32 c0 (100, 150, 100, 0);
+ ColorRGBA32 c1 (200, 100, 0, 200);
+ ColorRGBA32 res = c0 + c1;
+ CHECK (res == ColorRGBA32 (255, 250, 100, 200));
+ }
+
+ {
+ ColorRGBA32 res = ColorRGBA32 (150, 150, 150, 150) + ColorRGBA32 (150, 150, 150, 150);
+ CHECK (res == ColorRGBA32 (255, 255, 255, 255));
+ }
+}
+
+
+TEST (Math_TransformAABB)
+{
+ Matrix4x4f m;
+
+ for (int i = 0; i < 16; ++i)
+ m.m_Data[i] = (float)(7-i);
+
+ AABB aabb(Vector3f(1,2,3), Vector3f(4,5,6));
+
+ AABB aabbSlow;
+ TransformAABBSlow(aabb, m, aabbSlow);
+
+ AABB aabbRef;
+ TransformAABB(aabb, m, aabbRef);
+
+ CHECK (CompareApproximately (aabbSlow.m_Center, aabbRef.m_Center));
+ CHECK (CompareApproximately (aabbSlow.m_Extent, aabbRef.m_Extent));
+}
+
+TEST (Math_BitsInMask)
+{
+ CHECK_EQUAL (0, BitsInMask(0x0));
+ CHECK_EQUAL (32, BitsInMask(0xFFFFFFFF));
+ CHECK_EQUAL (1, BitsInMask(0x1));
+ CHECK_EQUAL (1, BitsInMask(0x80000000));
+ CHECK_EQUAL (2, BitsInMask(0x5));
+ CHECK_EQUAL (3, BitsInMask(0x7));
+ CHECK_EQUAL (24, BitsInMask(0xDEADBEEF));
+ CHECK_EQUAL (19, BitsInMask(0xCAFE1337));
+}
+
+TEST (Math_BitsInMask64)
+{
+ CHECK_EQUAL (0, BitsInMask64(0x0000000000000000ULL));
+ CHECK_EQUAL (64, BitsInMask64(0xFFFFFFFFFFFFFFFFULL));
+ CHECK_EQUAL (1, BitsInMask64(0x0000000000000001ULL));
+ CHECK_EQUAL (2, BitsInMask64(0x8000000080000000ULL));
+ CHECK_EQUAL (2, BitsInMask64(0x0000000000000005ULL));
+ CHECK_EQUAL (3, BitsInMask64(0x0000000000000007ULL));
+ CHECK_EQUAL (24, BitsInMask64(0x00000000DEADBEEFULL));
+ CHECK_EQUAL (19, BitsInMask64(0x00000000CAFE1337ULL));
+ CHECK_EQUAL (43, BitsInMask64(0xCAFE1337DEADBEEFULL));
+}
+
+TEST (Math_Normalize)
+{
+ Plane p;
+ p.SetABCD(0,0,0,1);
+ p.NormalizeRobust();
+ Vector3f n = p.GetNormal();
+ CHECK (IsNormalized(n));
+
+ p.SetABCD(2.5e-5f, 3.1e-5f, 1.2e-5f, 1.f);
+ p.NormalizeRobust();
+ n = p.GetNormal();
+ CHECK (IsNormalized(n));
+
+ Vector3f normal(2.3e-5f, 2.1e-5f, 3.2e-5f);
+ float invOriginalLength;
+ normal = NormalizeRobust(normal, invOriginalLength);
+ CHECK (CompareApproximately (22394.295f, invOriginalLength));
+}
+
+}
+
+
+#endif
diff --git a/Runtime/Math/Matrix3x3.cpp b/Runtime/Math/Matrix3x3.cpp
new file mode 100644
index 0000000..ac92b70
--- /dev/null
+++ b/Runtime/Math/Matrix3x3.cpp
@@ -0,0 +1,596 @@
+#include "UnityPrefix.h"
+#include "Matrix3x3.h"
+#include "Matrix4x4.h"
+using namespace std;
+
+namespace
+{
+ Matrix3x3f CreateIdentityMatrix3x3f ()
+ {
+ Matrix3x3f temp;
+ temp.SetIdentity ();
+ return temp;
+ }
+
+ Matrix3x3f CreateZeroMatrix3x3f ()
+ {
+ Matrix3x3f temp;
+ temp.SetZero ();
+ return temp;
+ }
+}
+
+const Matrix3x3f Matrix3x3f::identity = CreateIdentityMatrix3x3f ();
+const Matrix3x3f Matrix3x3f::zero = CreateZeroMatrix3x3f ();
+
+void GetRotMatrixNormVec (float* out, const float* inVec, float radians);
+
+Matrix3x3f& Matrix3x3f::operator = (const Matrix4x4f& other)
+{
+ m_Data[0] = other.m_Data[0];
+ m_Data[1] = other.m_Data[1];
+ m_Data[2] = other.m_Data[2];
+
+ m_Data[3] = other.m_Data[4];
+ m_Data[4] = other.m_Data[5];
+ m_Data[5] = other.m_Data[6];
+
+ m_Data[6] = other.m_Data[8];
+ m_Data[7] = other.m_Data[9];
+ m_Data[8] = other.m_Data[10];
+ return *this;
+}
+
+Matrix3x3f::Matrix3x3f (const Matrix4x4f& other)
+{
+ m_Data[0] = other.m_Data[0];
+ m_Data[1] = other.m_Data[1];
+ m_Data[2] = other.m_Data[2];
+
+ m_Data[3] = other.m_Data[4];
+ m_Data[4] = other.m_Data[5];
+ m_Data[5] = other.m_Data[6];
+
+ m_Data[6] = other.m_Data[8];
+ m_Data[7] = other.m_Data[9];
+ m_Data[8] = other.m_Data[10];
+}
+
+Matrix3x3f& Matrix3x3f::SetIdentity ()
+{
+ Get (0, 0) = 1.0F; Get (0, 1) = 0.0F; Get (0, 2) = 0.0F;
+ Get (1, 0) = 0.0F; Get (1, 1) = 1.0F; Get (1, 2) = 0.0F;
+ Get (2, 0) = 0.0F; Get (2, 1) = 0.0F; Get (2, 2) = 1.0F;
+ return *this;
+}
+
+Matrix3x3f& Matrix3x3f::SetZero ()
+{
+ Get (0, 0) = 0.0F; Get (0, 1) = 0.0F; Get (0, 2) = 0.0F;
+ Get (1, 0) = 0.0F; Get (1, 1) = 0.0F; Get (1, 2) = 0.0F;
+ Get (2, 0) = 0.0F; Get (2, 1) = 0.0F; Get (2, 2) = 0.0F;
+ return *this;
+}
+
+Matrix3x3f& Matrix3x3f::SetOrthoNormalBasis (const Vector3f& inX, const Vector3f& inY, const Vector3f& inZ)
+{
+ Get (0, 0) = inX[0]; Get (0, 1) = inY[0]; Get (0, 2) = inZ[0];
+ Get (1, 0) = inX[1]; Get (1, 1) = inY[1]; Get (1, 2) = inZ[1];
+ Get (2, 0) = inX[2]; Get (2, 1) = inY[2]; Get (2, 2) = inZ[2];
+ return *this;
+}
+
+Matrix3x3f& Matrix3x3f::SetOrthoNormalBasisInverse (const Vector3f& inX, const Vector3f& inY, const Vector3f& inZ)
+{
+ Get (0, 0) = inX[0]; Get (1, 0) = inY[0]; Get (2, 0) = inZ[0];
+ Get (0, 1) = inX[1]; Get (1, 1) = inY[1]; Get (2, 1) = inZ[1];
+ Get (0, 2) = inX[2]; Get (1, 2) = inY[2]; Get (2, 2) = inZ[2];
+ return *this;
+}
+
+Matrix3x3f& Matrix3x3f::SetScale (const Vector3f& inScale)
+{
+ Get (0, 0) = inScale[0]; Get (0, 1) = 0.0F; Get (0, 2) = 0.0F;
+ Get (1, 0) = 0.0F; Get (1, 1) = inScale[1]; Get (1, 2) = 0.0F;
+ Get (2, 0) = 0.0F; Get (2, 1) = 0.0F; Get (2, 2) = inScale[2];
+ return *this;
+}
+
+bool Matrix3x3f::IsIdentity (float threshold) {
+ if (CompareApproximately (Get (0,0),1.0f, threshold) && CompareApproximately (Get (0,1),0.0f, threshold) && CompareApproximately (Get (0,2),0.0f, threshold) &&
+ CompareApproximately (Get (1,0),0.0f, threshold) && CompareApproximately (Get (1,1),1.0f, threshold) && CompareApproximately (Get (1,2),0.0f, threshold) &&
+ CompareApproximately (Get (2,0),0.0f, threshold) && CompareApproximately (Get (2,1),0.0f, threshold) && CompareApproximately (Get (2,2),1.0f, threshold))
+ return true;
+ return false;
+}
+
+
+Matrix3x3f& Matrix3x3f::Scale (const Vector3f& inScale)
+{
+ Get (0, 0) *= inScale[0];
+ Get (1, 0) *= inScale[0];
+ Get (2, 0) *= inScale[0];
+
+ Get (0, 1) *= inScale[1];
+ Get (1, 1) *= inScale[1];
+ Get (2, 1) *= inScale[1];
+
+ Get (0, 2) *= inScale[2];
+ Get (1, 2) *= inScale[2];
+ Get (2, 2) *= inScale[2];
+ return *this;
+}
+
+float Matrix3x3f::GetDeterminant () const
+{
+ float fCofactor0 = Get (0, 0) * Get (1, 1) * Get (2, 2);
+ float fCofactor1 = Get (0, 1) * Get (1, 2) * Get (2, 0);
+ float fCofactor2 = Get (0, 2) * Get (1, 0) * Get (2, 1);
+
+ float fCofactor3 = Get (0, 2) * Get (1, 1) * Get (2, 0);
+ float fCofactor4 = Get (0, 1) * Get (1, 0) * Get (2, 2);
+ float fCofactor5 = Get (0, 0) * Get (1, 2) * Get (2, 1);
+
+ return fCofactor0 + fCofactor1 + fCofactor2 - fCofactor3 - fCofactor4 - fCofactor5;
+}
+
+Matrix3x3f& Matrix3x3f::Transpose ()
+{
+ swap (Get (0, 1), Get (1, 0));
+ swap (Get (0, 2), Get (2, 0));
+ swap (Get (2, 1), Get (1, 2));
+ return *this;
+}
+/*
+Matrix3x3f& Matrix3x3f::Transpose (const Matrix3x3f& inMat)
+{
+ int i;
+ for (i=0;i<3;i++)
+ {
+ Get (i, 0) = inMat.Get (0, i);
+ Get (i, 1) = inMat.Get (1, i);
+ Get (i, 2) = inMat.Get (2, i);
+ }
+ return *this;
+}
+*/
+
+bool Matrix3x3f::Invert ()
+{
+ ///@TODO make a fast but robust inverse matrix 3x3
+ Matrix4x4f m = *this;
+ bool success = InvertMatrix4x4_Full( m.GetPtr(), m.GetPtr() );
+ *this = m;
+ return success;
+
+#if 0
+ ////// THIS METHOD IS NUMERICALLY LESS ROBUST
+ // Invert a 3x3 using cofactors. This is faster than using a generic
+ // Gaussian elimination because of the loop overhead of such a method.
+
+ Matrix3x3f kInverse;
+
+ kInverse.Get (0, 0) = Get (1, 1) * Get (2, 2) - Get (1, 2) * Get (2, 1);
+ kInverse.Get (0, 1) = Get (0, 2) * Get (2, 1) - Get (0, 1) * Get (2, 2);
+ kInverse.Get (0, 2) = Get (0, 1) * Get (1, 2) - Get (0, 2) * Get (1, 1);
+ kInverse.Get (1, 0) = Get (1, 2) * Get (2, 0) - Get (1, 0) * Get (2, 2);
+ kInverse.Get (1, 1) = Get (0, 0) * Get (2, 2) - Get (0, 2) * Get (2, 0);
+ kInverse.Get (1, 2) = Get (0, 2) * Get (1, 0) - Get (0, 0) * Get (1, 2);
+ kInverse.Get (2, 0) = Get (1, 0) * Get (2, 1) - Get (1, 1) * Get (2, 0);
+ kInverse.Get (2, 1) = Get (0, 1) * Get (2, 0) - Get (0, 0) * Get (2, 1);
+ kInverse.Get (2, 2) = Get (0, 0) * Get (1, 1) - Get (0, 1) * Get (1, 0);
+
+ float fDet = Get (0, 0) * kInverse.Get (0, 0) + Get (0, 1) * kInverse.Get (1, 0) + Get (0, 2) * kInverse.Get (2, 0);
+
+ if (Abs (fDet) > Vector3f::epsilon)
+ {
+ kInverse /= fDet;
+ *this = kInverse;
+ return true;
+ }
+ else
+ {
+ SetZero ();
+ return false;
+ }
+ #endif
+}
+
+void Matrix3x3f::InvertTranspose ()
+{
+ Invert ();
+ Transpose ();
+}
+
+Matrix3x3f& Matrix3x3f::operator *= (float f)
+{
+ for (int i=0;i<9;i++)
+ m_Data[i] *= f;
+ return *this;
+}
+
+Matrix3x3f& Matrix3x3f::operator *= (const Matrix3x3f& inM)
+{
+ int i;
+ for (i=0;i<3;i++)
+ {
+ float v[3] = {Get (i, 0), Get (i, 1), Get (i, 2)};
+ Get (i, 0) = v[0] * inM.Get (0, 0) + v[1] * inM.Get (1, 0) + v[2] * inM.Get (2, 0);
+ Get (i, 1) = v[0] * inM.Get (0, 1) + v[1] * inM.Get (1, 1) + v[2] * inM.Get (2, 1);
+ Get (i, 2) = v[0] * inM.Get (0, 2) + v[1] * inM.Get (1, 2) + v[2] * inM.Get (2, 2);
+ }
+ return *this;
+}
+
+Matrix3x3f& Matrix3x3f::operator *= (const Matrix4x4f& inM)
+{
+ int i;
+ for (i=0;i<3;i++)
+ {
+ float v[3] = {Get (i, 0), Get (i, 1), Get (i, 2)};
+ Get (i, 0) = v[0] * inM.Get (0, 0) + v[1] * inM.Get (1, 0) + v[2] * inM.Get (2, 0);
+ Get (i, 1) = v[0] * inM.Get (0, 1) + v[1] * inM.Get (1, 1) + v[2] * inM.Get (2, 1);
+ Get (i, 2) = v[0] * inM.Get (0, 2) + v[1] * inM.Get (1, 2) + v[2] * inM.Get (2, 2);
+ }
+ return *this;
+}
+
+Matrix3x3f& Matrix3x3f::SetAxisAngle (const Vector3f& rotationAxis, float radians)
+{
+ GetRotMatrixNormVec (m_Data, rotationAxis.GetPtr (), radians);
+ return *this;
+}
+
+void fromToRotation(const float from[3], const float to[3],float mtx[3][3]);
+
+Matrix3x3f& Matrix3x3f::SetFromToRotation (const Vector3f& from, const Vector3f& to)
+{
+ float mtx[3][3];
+ fromToRotation (from.GetPtr (), to.GetPtr (), mtx);
+ Get (0, 0) = mtx[0][0]; Get (0, 1) = mtx[0][1]; Get (0, 2) = mtx[0][2];
+ Get (1, 0) = mtx[1][0]; Get (1, 1) = mtx[1][1]; Get (1, 2) = mtx[1][2];
+ Get (2, 0) = mtx[2][0]; Get (2, 1) = mtx[2][1]; Get (2, 2) = mtx[2][2];
+ return *this;
+}
+
+inline void MakePositive (Vector3f& euler)
+{
+ const float negativeFlip = -0.0001F;
+ const float positiveFlip = (kPI * 2.0F) - 0.0001F;
+
+ if (euler.x < negativeFlip)
+ euler.x += 2.0 * kPI;
+ else if (euler.x > positiveFlip)
+ euler.x -= 2.0 * kPI;
+
+ if (euler.y < negativeFlip)
+ euler.y += 2.0 * kPI;
+ else if (euler.y > positiveFlip)
+ euler.y -= 2.0 * kPI;
+
+ if (euler.z < negativeFlip)
+ euler.z += 2.0 * kPI;
+ else if (euler.z > positiveFlip)
+ euler.z -= 2.0 * kPI;
+}
+
+inline void SanitizeEuler (Vector3f& euler)
+{
+ MakePositive (euler);
+ /*
+ Vector3f option0 = euler;
+ option0.x = kPI - option0.x;
+ option0.y = kPI - option0.y;
+ option0.z = kPI - option0.z;
+
+ MakePositive (euler);
+ MakePositive (option0);
+ if (option0.x+option0.y+option0.z < euler.x+euler.y+euler.z)
+ euler = option0;
+ */
+}
+
+void EulerToMatrix (const Vector3f& v, Matrix3x3f& matrix)
+{
+ float cx = cos (v.x);
+ float sx = sin (v.x);
+ float cy = cos (v.y);
+ float sy = sin (v.y);
+ float cz = cos (v.z);
+ float sz = sin (v.z);
+
+ matrix.Get(0,0) = cy*cz + sx*sy*sz;
+ matrix.Get(0,1) = cz*sx*sy - cy*sz;
+ matrix.Get(0,2) = cx*sy;
+
+ matrix.Get(1,0) = cx*sz;
+ matrix.Get(1,1) = cx*cz;
+ matrix.Get(1,2) = -sx;
+
+ matrix.Get(2,0) = -cz*sy + cy*sx*sz;
+ matrix.Get(2,1) = cy*cz*sx + sy*sz;
+ matrix.Get(2,2) = cx*cy;
+}
+
+/// This is YXZ euler conversion
+bool MatrixToEuler (const Matrix3x3f& matrix, Vector3f& v)
+{
+ // from http://www.geometrictools.com/Documentation/EulerAngles.pdf
+ // YXZ order
+ if ( matrix.Get(1,2) < 0.999F ) // some fudge for imprecision
+ {
+ if ( matrix.Get(1,2) > -0.999F ) // some fudge for imprecision
+ {
+ v.x = asin(-matrix.Get(1,2));
+ v.y = atan2(matrix.Get(0,2), matrix.Get(2,2));
+ v.z = atan2(matrix.Get(1,0), matrix.Get(1,1));
+ SanitizeEuler (v);
+ return true;
+ }
+ else
+ {
+ // WARNING. Not unique. YA - ZA = atan2(r01,r00)
+ v.x = kPI * 0.5F;
+ v.y = atan2(matrix.Get (0,1), matrix.Get(0,0));
+ v.z = 0.0F;
+ SanitizeEuler (v);
+
+ return false;
+ }
+ }
+ else
+ {
+ // WARNING. Not unique. YA + ZA = atan2(-r01,r00)
+ v.x = -kPI * 0.5F;
+ v.y = atan2(-matrix.Get(0,1),matrix.Get(0,0));
+ v.z = 0.0F;
+ SanitizeEuler (v);
+ return false;
+ }
+}
+
+
+#define EPSILON 0.000001
+
+#define CROSS(dest,v1,v2){ \
+ dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \
+ dest[1]=v1[2]*v2[0]-v1[0]*v2[2]; \
+ dest[2]=v1[0]*v2[1]-v1[1]*v2[0];}
+
+#define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2])
+
+#define SUB(dest,v1,v2){ \
+ dest[0]=v1[0]-v2[0]; \
+ dest[1]=v1[1]-v2[1]; \
+ dest[2]=v1[2]-v2[2];}
+
+/*
+ * A function for creating a rotation matrix that rotates a vector called
+ * "from" into another vector called "to".
+ * Input : from[3], to[3] which both must be *normalized* non-zero vectors
+ * Output: mtx[3][3] -- a 3x3 matrix in colum-major form
+ * Author: Tomas Möller, 1999
+ */
+void fromToRotation(const float* from, const float* to,float mtx[3][3])
+{
+ float v[3];
+ float e,h;
+ CROSS(v,from,to);
+ e=DOT(from,to);
+ if(e>1.0-EPSILON) /* "from" almost or equal to "to"-vector? */
+ {
+ /* return identity */
+ mtx[0][0]=1.0; mtx[0][1]=0.0; mtx[0][2]=0.0;
+ mtx[1][0]=0.0; mtx[1][1]=1.0; mtx[1][2]=0.0;
+ mtx[2][0]=0.0; mtx[2][1]=0.0; mtx[2][2]=1.0;
+ }
+ else if(e<-1.0+EPSILON) /* "from" almost or equal to negated "to"? */
+ {
+ float up[3],left[3];
+ float invlen;
+ float fxx,fyy,fzz,fxy,fxz,fyz;
+ float uxx,uyy,uzz,uxy,uxz,uyz;
+ float lxx,lyy,lzz,lxy,lxz,lyz;
+ /* left=CROSS(from, (1,0,0)) */
+ left[0]=0.0; left[1]=from[2]; left[2]=-from[1];
+ if(DOT(left,left)<EPSILON) /* was left=CROSS(from,(1,0,0)) a good choice? */
+ {
+ /* here we now that left = CROSS(from, (1,0,0)) will be a good choice */
+ left[0]=-from[2]; left[1]=0.0; left[2]=from[0];
+ }
+ /* normalize "left" */
+ invlen=1.0f/sqrt(DOT(left,left));
+ left[0]*=invlen;
+ left[1]*=invlen;
+ left[2]*=invlen;
+ CROSS(up,left,from);
+ /* now we have a coordinate system, i.e., a basis; */
+ /* M=(from, up, left), and we want to rotate to: */
+ /* N=(-from, up, -left). This is done with the matrix:*/
+ /* N*M^T where M^T is the transpose of M */
+ fxx=-from[0]*from[0]; fyy=-from[1]*from[1]; fzz=-from[2]*from[2];
+ fxy=-from[0]*from[1]; fxz=-from[0]*from[2]; fyz=-from[1]*from[2];
+
+ uxx=up[0]*up[0]; uyy=up[1]*up[1]; uzz=up[2]*up[2];
+ uxy=up[0]*up[1]; uxz=up[0]*up[2]; uyz=up[1]*up[2];
+
+ lxx=-left[0]*left[0]; lyy=-left[1]*left[1]; lzz=-left[2]*left[2];
+ lxy=-left[0]*left[1]; lxz=-left[0]*left[2]; lyz=-left[1]*left[2];
+ /* symmetric matrix */
+ mtx[0][0]=fxx+uxx+lxx; mtx[0][1]=fxy+uxy+lxy; mtx[0][2]=fxz+uxz+lxz;
+ mtx[1][0]=mtx[0][1]; mtx[1][1]=fyy+uyy+lyy; mtx[1][2]=fyz+uyz+lyz;
+ mtx[2][0]=mtx[0][2]; mtx[2][1]=mtx[1][2]; mtx[2][2]=fzz+uzz+lzz;
+ }
+ else /* the most common case, unless "from"="to", or "from"=-"to" */
+ {
+ /* ...otherwise use this hand optimized version (9 mults less) */
+ float hvx,hvz,hvxy,hvxz,hvyz;
+ h=(1.0f-e)/DOT(v,v);
+ hvx=h*v[0];
+ hvz=h*v[2];
+ hvxy=hvx*v[1];
+ hvxz=hvx*v[2];
+ hvyz=hvz*v[1];
+ mtx[0][0]=e+hvx*v[0]; mtx[0][1]=hvxy-v[2]; mtx[0][2]=hvxz+v[1];
+ mtx[1][0]=hvxy+v[2]; mtx[1][1]=e+h*v[1]*v[1]; mtx[1][2]=hvyz-v[0];
+ mtx[2][0]=hvxz-v[1]; mtx[2][1]=hvyz+v[0]; mtx[2][2]=e+hvz*v[2];
+ }
+}
+
+// Right handed
+bool LookRotationToMatrix (const Vector3f& viewVec, const Vector3f& upVec, Matrix3x3f* m)
+{
+ Vector3f z = viewVec;
+ // compute u0
+ float mag = Magnitude (z);
+ if (mag < Vector3f::epsilon)
+ {
+ m->SetIdentity();
+ return false;
+ }
+ z /= mag;
+
+ Vector3f x = Cross (upVec, z);
+ mag = Magnitude (x);
+ if (mag < Vector3f::epsilon)
+ {
+ m->SetIdentity();
+ return false;
+ }
+ x /= mag;
+
+ Vector3f y (Cross (z, x));
+ if (!CompareApproximately (SqrMagnitude (y), 1.0F))
+ return false;
+
+ m->SetOrthoNormalBasis (x, y, z);
+ return true;
+}
+/*
+//Left handed
+bool LookRotationToMatrixLeftHanded (const Vector3f& viewVec, const Vector3f& upVec, Matrix3x3f* m)
+{
+ Vector3f z = viewVec;
+ // compute u0
+ float mag = Magnitude (z);
+ if (mag < Vector3f::epsilon)
+ return false;
+ z /= mag;
+
+ Vector3f x = Cross (z, upVec);
+ mag = Magnitude (x);
+ if (mag < Vector3f::epsilon)
+ return false;
+ x /= mag;
+
+ Vector3f y (Cross (x, z));
+ if (!CompareApproximately (SqrMagnitude (y), 1.0F))
+ return false;
+
+ m->SetOrthoNormalBasis (x, y, z);
+ return true;
+}
+*/
+
+void GetRotMatrixNormVec (float* out, const float* inVec, float radians)
+{
+ /* This function contributed by Erich Boleyn (erich@uruk.org) */
+ /* This function used from the Mesa OpenGL code (matrix.c) */
+ float s, c;
+ float vx, vy, vz, xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c;
+
+ s = sin (radians);
+ c = cos (radians);
+
+ vx = inVec[0];
+ vy = inVec[1];
+ vz = inVec[2];
+
+#define M(row,col) out[row*3 + col]
+ /*
+ * Arbitrary axis rotation matrix.
+ *
+ * This is composed of 5 matrices, Rz, Ry, T, Ry', Rz', multiplied
+ * like so: Rz * Ry * T * Ry' * Rz'. T is the final rotation
+ * (which is about the X-axis), and the two composite transforms
+ * Ry' * Rz' and Rz * Ry are (respectively) the rotations necessary
+ * from the arbitrary axis to the X-axis then back. They are
+ * all elementary rotations.
+ *
+ * Rz' is a rotation about the Z-axis, to bring the axis vector
+ * into the x-z plane. Then Ry' is applied, rotating about the
+ * Y-axis to bring the axis vector parallel with the X-axis. The
+ * rotation about the X-axis is then performed. Ry and Rz are
+ * simply the respective inverse transforms to bring the arbitrary
+ * axis back to its original orientation. The first transforms
+ * Rz' and Ry' are considered inverses, since the data from the
+ * arbitrary axis gives you info on how to get to it, not how
+ * to get away from it, and an inverse must be applied.
+ *
+ * The basic calculation used is to recognize that the arbitrary
+ * axis vector (x, y, z), since it is of unit length, actually
+ * represents the sines and cosines of the angles to rotate the
+ * X-axis to the same orientation, with theta being the angle about
+ * Z and phi the angle about Y (in the order described above)
+ * as follows:
+ *
+ * cos ( theta ) = x / sqrt ( 1 - z^2 )
+ * sin ( theta ) = y / sqrt ( 1 - z^2 )
+ *
+ * cos ( phi ) = sqrt ( 1 - z^2 )
+ * sin ( phi ) = z
+ *
+ * Note that cos ( phi ) can further be inserted to the above
+ * formulas:
+ *
+ * cos ( theta ) = x / cos ( phi )
+ * sin ( theta ) = y / cos ( phi )
+ *
+ * ...etc. Because of those relations and the standard trigonometric
+ * relations, it is pssible to reduce the transforms down to what
+ * is used below. It may be that any primary axis chosen will give the
+ * same results (modulo a sign convention) using thie method.
+ *
+ * Particularly nice is to notice that all divisions that might
+ * have caused trouble when parallel to certain planes or
+ * axis go away with care paid to reducing the expressions.
+ * After checking, it does perform correctly under all cases, since
+ * in all the cases of division where the denominator would have
+ * been zero, the numerator would have been zero as well, giving
+ * the expected result.
+ */
+
+ xx = vx * vx;
+ yy = vy * vy;
+ zz = vz * vz;
+ xy = vx * vy;
+ yz = vy * vz;
+ zx = vz * vx;
+ xs = vx * s;
+ ys = vy * s;
+ zs = vz * s;
+ one_c = 1.0F - c;
+
+ M(0,0) = (one_c * xx) + c;
+ M(1,0) = (one_c * xy) - zs;
+ M(2,0) = (one_c * zx) + ys;
+
+ M(0,1) = (one_c * xy) + zs;
+ M(1,1) = (one_c * yy) + c;
+ M(2,1) = (one_c * yz) - xs;
+
+ M(0,2) = (one_c * zx) - ys;
+ M(1,2) = (one_c * yz) + xs;
+ M(2,2) = (one_c * zz) + c;
+
+#undef M
+}
+
+
+void OrthoNormalize (Matrix3x3f& matrix)
+{
+ Vector3f* c0 = (Vector3f*)matrix.GetPtr () + 0;
+ Vector3f* c1 = (Vector3f*)matrix.GetPtr () + 3;
+ Vector3f* c2 = (Vector3f*)matrix.GetPtr () + 6;
+ OrthoNormalize (c0, c1, c2);
+}
diff --git a/Runtime/Math/Matrix3x3.h b/Runtime/Math/Matrix3x3.h
new file mode 100644
index 0000000..f884349
--- /dev/null
+++ b/Runtime/Math/Matrix3x3.h
@@ -0,0 +1,119 @@
+#pragma once
+
+#include "Vector3.h"
+#include "Runtime/Modules/ExportModules.h"
+
+class EXPORT_COREMODULE Matrix3x3f
+{
+ public:
+
+ float m_Data[9];
+
+ ///@todo: Can't be Transfer optimized because Transfer doesn't write the same as memory layout
+ DECLARE_SERIALIZE_NO_PPTR (Matrix3x3f)
+
+ Matrix3x3f () {}
+ Matrix3x3f (float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22) { Get (0,0) = m00; Get (1,0) = m10; Get (2,0) = m20; Get (0,1) = m01; Get (1,1) =m11; Get (2,1) = m21; Get (0,2) = m02; Get (1,2) = m12; Get (2,2) = m22; }
+ explicit Matrix3x3f (const class Matrix4x4f& m);
+ // The Get function accesses the matrix in std math convention
+ // m0,0 m0,1 m0,2
+ // m1,0 m1,1 m1,2
+ // m2,0 m2,1 m2,2
+
+ // The floats are laid out:
+ // m0 m3 m6
+ // m1 m4 m7
+ // m2 m5 m8
+
+
+ float& Get (int row, int column) { return m_Data[row + (column * 3)]; }
+ const float& Get (int row, int column)const { return m_Data[row + (column * 3)]; }
+
+ float& operator [] (int row) { return m_Data[row]; }
+ float operator [] (int row) const { return m_Data[row]; }
+
+ float* GetPtr () { return m_Data; }
+ const float* GetPtr () const { return m_Data; }
+
+ Vector3f GetColumn (int col) const { return Vector3f (Get (0, col), Get (1, col), Get (2, col)); }
+ Matrix3x3f& operator = (const class Matrix4x4f& m);
+
+ Matrix3x3f& operator *= (const Matrix3x3f& inM);
+ Matrix3x3f& operator *= (const class Matrix4x4f& inM);
+ friend Matrix3x3f operator * (const Matrix3x3f& lhs, const Matrix3x3f& rhs) { Matrix3x3f temp (lhs); temp *= rhs; return temp; }
+ Vector3f MultiplyVector3 (const Vector3f& inV) const;
+ void MultiplyVector3 (const Vector3f& inV, Vector3f& output) const;
+
+ Vector3f MultiplyPoint3 (const Vector3f& inV) const { return MultiplyVector3 (inV); }
+ Vector3f MultiplyVector3Transpose (const Vector3f& inV) const;
+ Vector3f MultiplyPoint3Transpose (const Vector3f& inV) const { return MultiplyVector3Transpose (inV); }
+
+ Matrix3x3f& operator *= (float f);
+ Matrix3x3f& operator /= (float f) { return *this *= (1.0F / f); }
+
+ float GetDeterminant () const;
+
+// Matrix3x3f& Transpose (const Matrix3x3f& inM);
+ Matrix3x3f& Transpose ();
+// Matrix3x3f& Invert (const Matrix3x3f& inM) { return Transpose (inM); }
+ bool Invert ();
+ void InvertTranspose ();
+
+ Matrix3x3f& SetIdentity ();
+ Matrix3x3f& SetZero ();
+ Matrix3x3f& SetFromToRotation (const Vector3f& from, const Vector3f& to);
+ Matrix3x3f& SetAxisAngle (const Vector3f& rotationAxis, float radians);
+ Matrix3x3f& SetOrthoNormalBasis (const Vector3f& inX, const Vector3f& inY, const Vector3f& inZ);
+ Matrix3x3f& SetOrthoNormalBasisInverse (const Vector3f& inX, const Vector3f& inY, const Vector3f& inZ);
+ Matrix3x3f& SetScale (const Vector3f& inScale);
+ Matrix3x3f& Scale (const Vector3f& inScale);
+
+ bool IsIdentity (float threshold = Vector3f::epsilon);
+
+ static const Matrix3x3f zero;
+ static const Matrix3x3f identity;
+};
+
+// Generates an orthornormal basis from a look at rotation, returns if it was successful
+// (Righthanded)
+bool LookRotationToMatrix (const Vector3f& viewVec, const Vector3f& upVec, Matrix3x3f* m);
+
+bool MatrixToEuler (const Matrix3x3f& matrix, Vector3f& v);
+void EulerToMatrix (const Vector3f& v, Matrix3x3f& matrix);
+
+inline Vector3f Matrix3x3f::MultiplyVector3 (const Vector3f& v) const
+{
+ Vector3f res;
+ res.x = m_Data[0] * v.x + m_Data[3] * v.y + m_Data[6] * v.z;
+ res.y = m_Data[1] * v.x + m_Data[4] * v.y + m_Data[7] * v.z;
+ res.z = m_Data[2] * v.x + m_Data[5] * v.y + m_Data[8] * v.z;
+ return res;
+}
+
+inline void Matrix3x3f::MultiplyVector3 (const Vector3f& v, Vector3f& output) const
+{
+ output.x = m_Data[0] * v.x + m_Data[3] * v.y + m_Data[6] * v.z;
+ output.y = m_Data[1] * v.x + m_Data[4] * v.y + m_Data[7] * v.z;
+ output.z = m_Data[2] * v.x + m_Data[5] * v.y + m_Data[8] * v.z;
+}
+
+
+inline Vector3f Matrix3x3f::MultiplyVector3Transpose (const Vector3f& v) const
+{
+ Vector3f res;
+ res.x = Get (0, 0) * v.x + Get (1, 0) * v.y + Get (2, 0) * v.z;
+ res.y = Get (0, 1) * v.x + Get (1, 1) * v.y + Get (2, 1) * v.z;
+ res.z = Get (0, 2) * v.x + Get (1, 2) * v.y + Get (2, 2) * v.z;
+ return res;
+}
+
+
+template<class TransferFunction>
+inline void Matrix3x3f::Transfer (TransferFunction& t)
+{
+ t.Transfer (Get (0, 0), "e00"); t.Transfer (Get (0, 1), "e01"); t.Transfer (Get (0, 2), "e02");
+ t.Transfer (Get (1, 0), "e10"); t.Transfer (Get (1, 1), "e11"); t.Transfer (Get (1, 2), "e12");
+ t.Transfer (Get (2, 0), "e20"); t.Transfer (Get (2, 1), "e21"); t.Transfer (Get (2, 2), "e22");
+}
+
+void EXPORT_COREMODULE OrthoNormalize (Matrix3x3f& matrix);
diff --git a/Runtime/Math/Matrix4x4.cpp b/Runtime/Math/Matrix4x4.cpp
new file mode 100644
index 0000000..660a6bf
--- /dev/null
+++ b/Runtime/Math/Matrix4x4.cpp
@@ -0,0 +1,805 @@
+#include "UnityPrefix.h"
+#include "Matrix4x4.h"
+#include "Matrix3x3.h"
+#include "Quaternion.h"
+#include "Runtime/Utilities/Utility.h"
+
+using namespace std;
+
+namespace
+{
+ Matrix4x4f CreateIdentityMatrix4x4f ()
+ {
+ Matrix4x4f temp;
+ temp.SetIdentity ();
+ return temp;
+ }
+}
+
+const Matrix4x4f Matrix4x4f::identity = CreateIdentityMatrix4x4f ();
+
+Matrix4x4f::Matrix4x4f (const float data[16])
+{
+ for (int i=0; i<16; i++)
+ m_Data[i] = data[i];
+}
+
+Matrix4x4f::Matrix4x4f (const Matrix3x3f &other)
+{
+ m_Data[0] = other.m_Data[0];
+ m_Data[1] = other.m_Data[1];
+ m_Data[2] = other.m_Data[2];
+ m_Data[3] = 0.0F;
+
+ m_Data[4] = other.m_Data[3];
+ m_Data[5] = other.m_Data[4];
+ m_Data[6] = other.m_Data[5];
+ m_Data[7] = 0.0F;
+
+ m_Data[8] = other.m_Data[6];
+ m_Data[9] = other.m_Data[7];
+ m_Data[10] = other.m_Data[8];
+ m_Data[11] = 0.0F;
+
+ m_Data[12] = 0.0F;
+ m_Data[13] = 0.0F;
+ m_Data[14] = 0.0F;
+ m_Data[15] = 1.0F;
+}
+
+Matrix4x4f& Matrix4x4f::operator = (const Matrix3x3f& other)
+{
+ m_Data[0] = other.m_Data[0];
+ m_Data[1] = other.m_Data[1];
+ m_Data[2] = other.m_Data[2];
+ m_Data[3] = 0.0F;
+
+ m_Data[4] = other.m_Data[3];
+ m_Data[5] = other.m_Data[4];
+ m_Data[6] = other.m_Data[5];
+ m_Data[7] = 0.0F;
+
+ m_Data[8] = other.m_Data[6];
+ m_Data[9] = other.m_Data[7];
+ m_Data[10] = other.m_Data[8];
+ m_Data[11] = 0.0F;
+
+ m_Data[12] = 0.0F;
+ m_Data[13] = 0.0F;
+ m_Data[14] = 0.0F;
+ m_Data[15] = 1.0F;
+ return *this;
+}
+
+bool Matrix4x4f::IsIdentity (float threshold) const
+{
+ if (CompareApproximately (Get (0,0),1.0f, threshold) && CompareApproximately (Get (0,1),0.0f, threshold) && CompareApproximately (Get (0,2),0.0f, threshold) && CompareApproximately (Get (0,3),0.0f, threshold) &&
+ CompareApproximately (Get (1,0),0.0f, threshold) && CompareApproximately (Get (1,1),1.0f, threshold) && CompareApproximately (Get (1,2),0.0f, threshold) && CompareApproximately (Get (1,3),0.0f, threshold) &&
+ CompareApproximately (Get (2,0),0.0f, threshold) && CompareApproximately (Get (2,1),0.0f, threshold) && CompareApproximately (Get (2,2),1.0f, threshold) && CompareApproximately (Get (2,3),0.0f, threshold) &&
+ CompareApproximately (Get (3,0),0.0f, threshold) && CompareApproximately (Get (3,1),0.0f, threshold) && CompareApproximately (Get (3,2),0.0f, threshold) && CompareApproximately (Get (3,3),1.0f, threshold))
+ return true;
+ return false;
+}
+
+double Matrix4x4f::GetDeterminant () const
+{
+ double m00 = Get(0, 0); double m01 = Get(0, 1); double m02 = Get(0, 2); double m03 = Get(0, 3);
+ double m10 = Get(1, 0); double m11 = Get(1, 1); double m12 = Get(1, 2); double m13 = Get(1, 3);
+ double m20 = Get(2, 0); double m21 = Get(2, 1); double m22 = Get(2, 2); double m23 = Get(2, 3);
+ double m30 = Get(3, 0); double m31 = Get(3, 1); double m32 = Get(3, 2); double m33 = Get(3, 3);
+
+ double result =
+ m03 * m12 * m21 * m30 - m02 * m13 * m21 * m30 - m03 * m11 * m22 * m30 + m01 * m13 * m22 * m30 +
+ m02 * m11 * m23 * m30 - m01 * m12 * m23 * m30 - m03 * m12 * m20 * m31 + m02 * m13 * m20 * m31 +
+ m03 * m10 * m22 * m31 - m00 * m13 * m22 * m31 - m02 * m10 * m23 * m31 + m00 * m12 * m23 * m31 +
+ m03 * m11 * m20 * m32 - m01 * m13 * m20 * m32 - m03 * m10 * m21 * m32 + m00 * m13 * m21 * m32 +
+ m01 * m10 * m23 * m32 - m00 * m11 * m23 * m32 - m02 * m11 * m20 * m33 + m01 * m12 * m20 * m33 +
+ m02 * m10 * m21 * m33 - m00 * m12 * m21 * m33 - m01 * m10 * m22 * m33 + m00 * m11 * m22 * m33;
+ return result;
+}
+
+Matrix4x4f& Matrix4x4f::operator *= (const Matrix4x4f& inM1)
+{
+ Assert(&inM1 != this);
+ Matrix4x4f tmp;
+ MultiplyMatrices4x4(this, &inM1, &tmp);
+ *this = tmp;
+ return *this;
+}
+
+void MultiplyMatrices3x4( const Matrix4x4f& lhs, const Matrix4x4f& rhs, Matrix4x4f& res)
+{
+ for (int i=0;i<3;i++)
+ {
+ res.m_Data[i] = lhs.m_Data[i] * rhs.m_Data[0] + lhs.m_Data[i+4] * rhs.m_Data[1] + lhs.m_Data[i+8] * rhs.m_Data[2];// + lhs.m_Data[i+12] * rhs.m_Data[3];
+ res.m_Data[i+4] = lhs.m_Data[i] * rhs.m_Data[4] + lhs.m_Data[i+4] * rhs.m_Data[5] + lhs.m_Data[i+8] * rhs.m_Data[6];// + lhs.m_Data[i+12] * rhs.m_Data[7];
+ res.m_Data[i+8] = lhs.m_Data[i] * rhs.m_Data[8] + lhs.m_Data[i+4] * rhs.m_Data[9] + lhs.m_Data[i+8] * rhs.m_Data[10];// + lhs.m_Data[i+12] * rhs.m_Data[11];
+ res.m_Data[i+12] = lhs.m_Data[i] * rhs.m_Data[12] + lhs.m_Data[i+4] * rhs.m_Data[13] + lhs.m_Data[i+8] * rhs.m_Data[14] + lhs.m_Data[i+12];// * rhs.m_Data[15];
+ }
+
+ res.m_Data[3] = 0.0f;
+ res.m_Data[7] = 0.0f;
+ res.m_Data[11] = 0.0f;
+ res.m_Data[15] = 1.0f;
+}
+
+
+Matrix4x4f& Matrix4x4f::SetIdentity ()
+{
+ Get (0, 0) = 1.0; Get (0, 1) = 0.0; Get (0, 2) = 0.0; Get (0, 3) = 0.0;
+ Get (1, 0) = 0.0; Get (1, 1) = 1.0; Get (1, 2) = 0.0; Get (1, 3) = 0.0;
+ Get (2, 0) = 0.0; Get (2, 1) = 0.0; Get (2, 2) = 1.0; Get (2, 3) = 0.0;
+ Get (3, 0) = 0.0; Get (3, 1) = 0.0; Get (3, 2) = 0.0; Get (3, 3) = 1.0;
+ return *this;
+}
+
+Matrix4x4f& Matrix4x4f::SetOrthoNormalBasis (const Vector3f& inX, const Vector3f& inY, const Vector3f& inZ)
+{
+ Get (0, 0) = inX[0]; Get (0, 1) = inY[0]; Get (0, 2) = inZ[0]; Get (0, 3) = 0.0;
+ Get (1, 0) = inX[1]; Get (1, 1) = inY[1]; Get (1, 2) = inZ[1]; Get (1, 3) = 0.0;
+ Get (2, 0) = inX[2]; Get (2, 1) = inY[2]; Get (2, 2) = inZ[2]; Get (2, 3) = 0.0;
+ Get (3, 0) = 0.0; Get (3, 1) = 0.0; Get (3, 2) = 0.0; Get (3, 3) = 1.0;
+ return *this;
+}
+
+Matrix4x4f& Matrix4x4f::SetOrthoNormalBasisInverse (const Vector3f& inX, const Vector3f& inY, const Vector3f& inZ)
+{
+ Get (0, 0) = inX[0]; Get (1, 0) = inY[0]; Get (2, 0) = inZ[0]; Get (3, 0) = 0.0;
+ Get (0, 1) = inX[1]; Get (1, 1) = inY[1]; Get (2, 1) = inZ[1]; Get (3, 1) = 0.0;
+ Get (0, 2) = inX[2]; Get (1, 2) = inY[2]; Get (2, 2) = inZ[2]; Get (3, 2) = 0.0;
+ Get (0, 3) = 0.0; Get (1, 3) = 0.0; Get (2, 3) = 0.0; Get (3, 3) = 1.0;
+ return *this;
+}
+
+Matrix4x4f& Matrix4x4f::SetPositionAndOrthoNormalBasis (const Vector3f& inPosition, const Vector3f& inX, const Vector3f& inY, const Vector3f& inZ)
+{
+ Get (0, 0) = inX[0]; Get (0, 1) = inY[0]; Get (0, 2) = inZ[0]; Get (0, 3) = inPosition[0];
+ Get (1, 0) = inX[1]; Get (1, 1) = inY[1]; Get (1, 2) = inZ[1]; Get (1, 3) = inPosition[1];
+ Get (2, 0) = inX[2]; Get (2, 1) = inY[2]; Get (2, 2) = inZ[2]; Get (2, 3) = inPosition[2];
+ Get (3, 0) = 0.0; Get (3, 1) = 0.0; Get (3, 2) = 0.0; Get (3, 3) = 1.0;
+ return *this;
+}
+
+Matrix4x4f& Matrix4x4f::SetScale (const Vector3f& inScale)
+{
+ Get (0, 0) = inScale[0]; Get (0, 1) = 0.0; Get (0, 2) = 0.0; Get (0, 3) = 0.0;
+ Get (1, 0) = 0.0; Get (1, 1) = inScale[1]; Get (1, 2) = 0.0; Get (1, 3) = 0.0;
+ Get (2, 0) = 0.0; Get (2, 1) = 0.0; Get (2, 2) = inScale[2]; Get (2, 3) = 0.0;
+ Get (3, 0) = 0.0; Get (3, 1) = 0.0; Get (3, 2) = 0.0; Get (3, 3) = 1.0;
+ return *this;
+}
+
+Matrix4x4f& Matrix4x4f::Scale (const Vector3f& inScale)
+{
+ Get (0, 0) *= inScale[0];
+ Get (1, 0) *= inScale[0];
+ Get (2, 0) *= inScale[0];
+ Get (3, 0) *= inScale[0];
+
+ Get (0, 1) *= inScale[1];
+ Get (1, 1) *= inScale[1];
+ Get (2, 1) *= inScale[1];
+ Get (3, 1) *= inScale[1];
+
+ Get (0, 2) *= inScale[2];
+ Get (1, 2) *= inScale[2];
+ Get (2, 2) *= inScale[2];
+ Get (3, 2) *= inScale[2];
+ return *this;
+}
+
+Matrix4x4f& Matrix4x4f::Translate (const Vector3f& inTrans)
+{
+ Get (0, 3) = Get (0, 0) * inTrans[0] + Get (0, 1) * inTrans[1] + Get (0, 2) * inTrans[2] + Get (0, 3);
+ Get (1, 3) = Get (1, 0) * inTrans[0] + Get (1, 1) * inTrans[1] + Get (1, 2) * inTrans[2] + Get (1, 3);
+ Get (2, 3) = Get (2, 0) * inTrans[0] + Get (2, 1) * inTrans[1] + Get (2, 2) * inTrans[2] + Get (2, 3);
+ Get (3, 3) = Get (3, 0) * inTrans[0] + Get (3, 1) * inTrans[1] + Get (3, 2) * inTrans[2] + Get (3, 3);
+ return *this;
+}
+
+Matrix4x4f& Matrix4x4f::SetTranslate (const Vector3f& inTrans)
+{
+ Get (0, 0) = 1.0; Get (0, 1) = 0.0; Get (0, 2) = 0.0; Get (0, 3) = inTrans[0];
+ Get (1, 0) = 0.0; Get (1, 1) = 1.0; Get (1, 2) = 0.0; Get (1, 3) = inTrans[1];
+ Get (2, 0) = 0.0; Get (2, 1) = 0.0; Get (2, 2) = 1.0; Get (2, 3) = inTrans[2];
+ Get (3, 0) = 0.0; Get (3, 1) = 0.0; Get (3, 2) = 0.0; Get (3, 3) = 1.0;
+ return *this;
+}
+
+Matrix4x4f& Matrix4x4f::SetPerspective(
+ float fovy,
+ float aspect,
+ float zNear,
+ float zFar )
+{
+ float cotangent, deltaZ;
+ float radians = Deg2Rad (fovy / 2.0f);
+ cotangent = cos (radians) / sin (radians);
+ deltaZ = zNear - zFar;
+
+ Get (0,0) = cotangent / aspect; Get (0,1) = 0.0F; Get (0,2) = 0.0F; Get (0,3) = 0.0F;
+ Get (1,0) = 0.0F; Get (1,1) = cotangent; Get (1,2) = 0.0F; Get (1,3) = 0.0F;
+ Get (2,0) = 0.0F; Get (2,1) = 0.0F; Get (2,2) = (zFar + zNear) / deltaZ; Get (2,3) = 2.0F * zNear * zFar / deltaZ;
+ Get (3,0) = 0.0F; Get (3,1) = 0.0F; Get (3,2) = -1.0F; Get (3,3) = 0.0F;
+
+ return *this;
+}
+
+Matrix4x4f& Matrix4x4f::SetPerspectiveCotan(
+ float cotangent,
+ float zNear,
+ float zFar )
+{
+ float deltaZ = zNear - zFar;
+
+ Get (0,0) = cotangent; Get (0,1) = 0.0F; Get (0,2) = 0.0F; Get (0,3) = 0.0F;
+ Get (1,0) = 0.0F; Get (1,1) = cotangent; Get (1,2) = 0.0F; Get (1,3) = 0.0F;
+ Get (2,0) = 0.0F; Get (2,1) = 0.0F; Get (2,2) = (zFar + zNear) / deltaZ; Get (2,3) = 2.0F * zNear * zFar / deltaZ;
+ Get (3,0) = 0.0F; Get (3,1) = 0.0F; Get (3,2) = -1.0F; Get (3,3) = 0.0F;
+
+ return *this;
+}
+
+Matrix4x4f& Matrix4x4f::SetOrtho (
+ float left,
+ float right,
+ float bottom,
+ float top,
+ float zNear,
+ float zFar )
+{
+ SetIdentity ();
+
+ float deltax = right - left;
+ float deltay = top - bottom;
+ float deltaz = zFar - zNear;
+
+ Get(0,0) = 2.0F / deltax;
+ Get(0,3) = -(right + left) / deltax;
+ Get(1,1) = 2.0F / deltay;
+ Get(1,3) = -(top + bottom) / deltay;
+ Get(2,2) = -2.0F / deltaz;
+ Get(2,3) = -(zFar + zNear) / deltaz;
+ return *this;
+}
+
+Matrix4x4f& Matrix4x4f::SetFrustum (
+ float left,
+ float right,
+ float bottom,
+ float top,
+ float nearval,
+ float farval )
+{
+ float x, y, a, b, c, d, e;
+
+ x = (2.0F * nearval) / (right - left);
+ y = (2.0F * nearval) / (top - bottom);
+ a = (right + left) / (right - left);
+ b = (top + bottom) / (top - bottom);
+ c = -(farval + nearval) / (farval - nearval);
+ d = -(2.0f * farval * nearval) / (farval - nearval);
+ e = -1.0f;
+
+ Get (0,0) = x; Get (0,1) = 0.0; Get (0,2) = a; Get (0,3) = 0.0;
+ Get (1,0) = 0.0; Get (1,1) = y; Get (1,2) = b; Get (1,3) = 0.0;
+ Get (2,0) = 0.0; Get (2,1) = 0.0; Get (2,2) = c; Get (2,3) = d;
+ Get (3,0) = 0.0; Get (3,1) = 0.0; Get (3,2) = e; Get (3,3) = 0.0;
+ return *this;
+}
+
+
+
+TransformType ComputeTransformType (const Matrix4x4f& matrix, float& outUniformScale, float epsilon)
+{
+ float lengthX = Magnitude(matrix.GetAxisX());
+ float lengthY = Magnitude(matrix.GetAxisY());
+ float lengthZ = Magnitude(matrix.GetAxisZ());
+ float minAxis = std::min(std::min(lengthX, lengthY), lengthZ);
+ float maxAxis = std::max(std::max(lengthX, lengthY), lengthZ);
+ TransformType transType = kNoScaleTransform;
+ outUniformScale = 1.0f;
+ if (minAxis < 1.0 - epsilon || maxAxis > 1.0 + epsilon)
+ {
+ if (minAxis != 0.0f && maxAxis / minAxis < 1.0 + epsilon)
+ {
+ transType = kUniformScaleTransform;
+ outUniformScale = minAxis;
+ }
+ else
+ transType = kNonUniformScaleTransform;
+ }
+ return transType;
+}
+
+
+#define MAT(m,r,c) (m)[(c)*4+(r)]
+
+#define RETURN_ZERO \
+{ \
+ for (int i=0;i<16;i++) \
+ out[i] = 0.0F; \
+ return false; \
+}
+
+// 4x4 matrix inversion by Gaussian reduction with partial pivoting followed by back/substitution;
+// with loops manually unrolled.
+
+#define SWAP_ROWS(a, b) { float *_tmp = a; (a)=(b); (b)=_tmp; }
+bool InvertMatrix4x4_Full(const float* m, float* out)
+{
+ float wtmp[4][8];
+ float m0, m1, m2, m3, s;
+ float *r0, *r1, *r2, *r3;
+
+ r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3];
+
+ r0[0] = MAT(m,0,0), r0[1] = MAT(m,0,1),
+ r0[2] = MAT(m,0,2), r0[3] = MAT(m,0,3),
+ r0[4] = 1.0, r0[5] = r0[6] = r0[7] = 0.0,
+
+ r1[0] = MAT(m,1,0), r1[1] = MAT(m,1,1),
+ r1[2] = MAT(m,1,2), r1[3] = MAT(m,1,3),
+ r1[5] = 1.0, r1[4] = r1[6] = r1[7] = 0.0,
+
+ r2[0] = MAT(m,2,0), r2[1] = MAT(m,2,1),
+ r2[2] = MAT(m,2,2), r2[3] = MAT(m,2,3),
+ r2[6] = 1.0, r2[4] = r2[5] = r2[7] = 0.0,
+
+ r3[0] = MAT(m,3,0), r3[1] = MAT(m,3,1),
+ r3[2] = MAT(m,3,2), r3[3] = MAT(m,3,3),
+ r3[7] = 1.0, r3[4] = r3[5] = r3[6] = 0.0;
+
+ /* choose pivot - or die */
+ if (Abs(r3[0])>Abs(r2[0])) SWAP_ROWS(r3, r2);
+ if (Abs(r2[0])>Abs(r1[0])) SWAP_ROWS(r2, r1);
+ if (Abs(r1[0])>Abs(r0[0])) SWAP_ROWS(r1, r0);
+ if (0.0F == r0[0]) RETURN_ZERO
+
+ /* eliminate first variable */
+ m1 = r1[0]/r0[0]; m2 = r2[0]/r0[0]; m3 = r3[0]/r0[0];
+ s = r0[1]; r1[1] -= m1 * s; r2[1] -= m2 * s; r3[1] -= m3 * s;
+ s = r0[2]; r1[2] -= m1 * s; r2[2] -= m2 * s; r3[2] -= m3 * s;
+ s = r0[3]; r1[3] -= m1 * s; r2[3] -= m2 * s; r3[3] -= m3 * s;
+ s = r0[4];
+ if (s != 0.0F) { r1[4] -= m1 * s; r2[4] -= m2 * s; r3[4] -= m3 * s; }
+ s = r0[5];
+ if (s != 0.0F) { r1[5] -= m1 * s; r2[5] -= m2 * s; r3[5] -= m3 * s; }
+ s = r0[6];
+ if (s != 0.0F) { r1[6] -= m1 * s; r2[6] -= m2 * s; r3[6] -= m3 * s; }
+ s = r0[7];
+ if (s != 0.0F) { r1[7] -= m1 * s; r2[7] -= m2 * s; r3[7] -= m3 * s; }
+
+ /* choose pivot - or die */
+ if (Abs(r3[1])>Abs(r2[1])) SWAP_ROWS(r3, r2);
+ if (Abs(r2[1])>Abs(r1[1])) SWAP_ROWS(r2, r1);
+ if (0.0F == r1[1]) RETURN_ZERO;
+
+ /* eliminate second variable */
+ m2 = r2[1]/r1[1]; m3 = r3[1]/r1[1];
+ r2[2] -= m2 * r1[2]; r3[2] -= m3 * r1[2];
+ r2[3] -= m2 * r1[3]; r3[3] -= m3 * r1[3];
+ s = r1[4]; if (0.0F != s) { r2[4] -= m2 * s; r3[4] -= m3 * s; }
+ s = r1[5]; if (0.0F != s) { r2[5] -= m2 * s; r3[5] -= m3 * s; }
+ s = r1[6]; if (0.0F != s) { r2[6] -= m2 * s; r3[6] -= m3 * s; }
+ s = r1[7]; if (0.0F != s) { r2[7] -= m2 * s; r3[7] -= m3 * s; }
+
+ /* choose pivot - or die */
+ if (Abs(r3[2])>Abs(r2[2])) SWAP_ROWS(r3, r2);
+ if (0.0F == r2[2]) RETURN_ZERO;
+
+ /* eliminate third variable */
+ m3 = r3[2]/r2[2];
+ r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4],
+ r3[5] -= m3 * r2[5], r3[6] -= m3 * r2[6],
+ r3[7] -= m3 * r2[7];
+
+ /* last check */
+ if (0.0F == r3[3]) RETURN_ZERO;
+
+ s = 1.0F/r3[3]; /* now back substitute row 3 */
+ r3[4] *= s; r3[5] *= s; r3[6] *= s; r3[7] *= s;
+
+ m2 = r2[3]; /* now back substitute row 2 */
+ s = 1.0F/r2[2];
+ r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2),
+ r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2);
+ m1 = r1[3];
+ r1[4] -= r3[4] * m1, r1[5] -= r3[5] * m1,
+ r1[6] -= r3[6] * m1, r1[7] -= r3[7] * m1;
+ m0 = r0[3];
+ r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0,
+ r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0;
+
+ m1 = r1[2]; /* now back substitute row 1 */
+ s = 1.0F/r1[1];
+ r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1),
+ r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1);
+ m0 = r0[2];
+ r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0,
+ r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0;
+
+ m0 = r0[1]; /* now back substitute row 0 */
+ s = 1.0F/r0[0];
+ r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0),
+ r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0);
+
+ MAT(out,0,0) = r0[4]; MAT(out,0,1) = r0[5], MAT(out,0,2) = r0[6]; MAT(out,0,3) = r0[7],
+ MAT(out,1,0) = r1[4]; MAT(out,1,1) = r1[5], MAT(out,1,2) = r1[6]; MAT(out,1,3) = r1[7],
+ MAT(out,2,0) = r2[4]; MAT(out,2,1) = r2[5], MAT(out,2,2) = r2[6]; MAT(out,2,3) = r2[7],
+ MAT(out,3,0) = r3[4]; MAT(out,3,1) = r3[5], MAT(out,3,2) = r3[6]; MAT(out,3,3) = r3[7];
+
+ return true;
+}
+
+#undef SWAP_ROWS
+
+// Invert 3D transformation matrix (not perspective). Adapted from graphics gems 2.
+// Inverts upper left by calculating its determinant and multiplying it to the symmetric
+// adjust matrix of each element. Finally deals with the translation by transforming the
+// original translation using by the calculated inverse.
+bool InvertMatrix4x4_General3D( const float* in, float* out )
+{
+ float pos, neg, t;
+ float det;
+
+ // Calculate the determinant of upper left 3x3 sub-matrix and
+ // determine if the matrix is singular.
+ pos = neg = 0.0;
+ t = MAT(in,0,0) * MAT(in,1,1) * MAT(in,2,2);
+ if (t >= 0.0) pos += t; else neg += t;
+
+ t = MAT(in,1,0) * MAT(in,2,1) * MAT(in,0,2);
+ if (t >= 0.0) pos += t; else neg += t;
+
+ t = MAT(in,2,0) * MAT(in,0,1) * MAT(in,1,2);
+ if (t >= 0.0) pos += t; else neg += t;
+
+ t = -MAT(in,2,0) * MAT(in,1,1) * MAT(in,0,2);
+ if (t >= 0.0) pos += t; else neg += t;
+
+ t = -MAT(in,1,0) * MAT(in,0,1) * MAT(in,2,2);
+ if (t >= 0.0) pos += t; else neg += t;
+
+ t = -MAT(in,0,0) * MAT(in,2,1) * MAT(in,1,2);
+ if (t >= 0.0) pos += t; else neg += t;
+
+ det = pos + neg;
+
+ if (det*det < 1e-25)
+ RETURN_ZERO;
+
+ det = 1.0F / det;
+ MAT(out,0,0) = ( (MAT(in,1,1)*MAT(in,2,2) - MAT(in,2,1)*MAT(in,1,2) )*det);
+ MAT(out,0,1) = (- (MAT(in,0,1)*MAT(in,2,2) - MAT(in,2,1)*MAT(in,0,2) )*det);
+ MAT(out,0,2) = ( (MAT(in,0,1)*MAT(in,1,2) - MAT(in,1,1)*MAT(in,0,2) )*det);
+ MAT(out,1,0) = (- (MAT(in,1,0)*MAT(in,2,2) - MAT(in,2,0)*MAT(in,1,2) )*det);
+ MAT(out,1,1) = ( (MAT(in,0,0)*MAT(in,2,2) - MAT(in,2,0)*MAT(in,0,2) )*det);
+ MAT(out,1,2) = (- (MAT(in,0,0)*MAT(in,1,2) - MAT(in,1,0)*MAT(in,0,2) )*det);
+ MAT(out,2,0) = ( (MAT(in,1,0)*MAT(in,2,1) - MAT(in,2,0)*MAT(in,1,1) )*det);
+ MAT(out,2,1) = (- (MAT(in,0,0)*MAT(in,2,1) - MAT(in,2,0)*MAT(in,0,1) )*det);
+ MAT(out,2,2) = ( (MAT(in,0,0)*MAT(in,1,1) - MAT(in,1,0)*MAT(in,0,1) )*det);
+
+ // Do the translation part
+ MAT(out,0,3) = - (MAT(in,0,3) * MAT(out,0,0) +
+ MAT(in,1,3) * MAT(out,0,1) +
+ MAT(in,2,3) * MAT(out,0,2) );
+ MAT(out,1,3) = - (MAT(in,0,3) * MAT(out,1,0) +
+ MAT(in,1,3) * MAT(out,1,1) +
+ MAT(in,2,3) * MAT(out,1,2) );
+ MAT(out,2,3) = - (MAT(in,0,3) * MAT(out,2,0) +
+ MAT(in,1,3) * MAT(out,2,1) +
+ MAT(in,2,3) * MAT(out,2,2) );
+
+ MAT(out,3,0) = 0.0f;
+ MAT(out,3,1) = 0.0f;
+ MAT(out,3,2) = 0.0f;
+ MAT(out,3,3) = 1.0f;
+
+ return true;
+}
+
+#undef MAT
+#undef RETURN_ZERO
+
+
+/*
+4x4 matrix inverse based on Cramer's rule. From Intel's "Streaming SIMD Extensions - Inverse of 4x4 Matrix" paper.
+Seems to be about the same speed as our current one, maybe slightly faster. Less numerically robust though,
+at very small numbers.
+
+bool InvertMatrix4x4_Cramer (const float* mat, float* dst)
+{
+ float tmp[12]; // temp array for pairs
+ float src[16]; // array of transpose source matrix
+ float det; // determinant
+ // transpose matrix
+ for (int i = 0; i < 4; i++) {
+ src[i] = mat[i*4];
+ src[i + 4] = mat[i*4 + 1];
+ src[i + 8] = mat[i*4 + 2];
+ src[i + 12] = mat[i*4 + 3];
+ }
+ // calculate pairs for first 8 elements (cofactors)
+ tmp[0] = src[10] * src[15];
+ tmp[1] = src[11] * src[14];
+ tmp[2] = src[9] * src[15];
+ tmp[3] = src[11] * src[13];
+ tmp[4] = src[9] * src[14];
+ tmp[5] = src[10] * src[13];
+ tmp[6] = src[8] * src[15];
+ tmp[7] = src[11] * src[12];
+ tmp[8] = src[8] * src[14];
+ tmp[9] = src[10] * src[12];
+ tmp[10] = src[8] * src[13];
+ tmp[11] = src[9] * src[12];
+ // calculate first 8 elements (cofactors)
+ dst[0] = tmp[0]*src[5] + tmp[3]*src[6] + tmp[4]*src[7];
+ dst[0] -= tmp[1]*src[5] + tmp[2]*src[6] + tmp[5]*src[7];
+ dst[1] = tmp[1]*src[4] + tmp[6]*src[6] + tmp[9]*src[7];
+ dst[1] -= tmp[0]*src[4] + tmp[7]*src[6] + tmp[8]*src[7];
+ dst[2] = tmp[2]*src[4] + tmp[7]*src[5] + tmp[10]*src[7];
+ dst[2] -= tmp[3]*src[4] + tmp[6]*src[5] + tmp[11]*src[7];
+ dst[3] = tmp[5]*src[4] + tmp[8]*src[5] + tmp[11]*src[6];
+ dst[3] -= tmp[4]*src[4] + tmp[9]*src[5] + tmp[10]*src[6];
+ dst[4] = tmp[1]*src[1] + tmp[2]*src[2] + tmp[5]*src[3];
+ dst[4] -= tmp[0]*src[1] + tmp[3]*src[2] + tmp[4]*src[3];
+ dst[5] = tmp[0]*src[0] + tmp[7]*src[2] + tmp[8]*src[3];
+ dst[5] -= tmp[1]*src[0] + tmp[6]*src[2] + tmp[9]*src[3];
+ dst[6] = tmp[3]*src[0] + tmp[6]*src[1] + tmp[11]*src[3];
+ dst[6] -= tmp[2]*src[0] + tmp[7]*src[1] + tmp[10]*src[3];
+ dst[7] = tmp[4]*src[0] + tmp[9]*src[1] + tmp[10]*src[2];
+ dst[7] -= tmp[5]*src[0] + tmp[8]*src[1] + tmp[11]*src[2];
+ // calculate pairs for second 8 elements (cofactors)
+ tmp[0] = src[2]*src[7];
+ tmp[1] = src[3]*src[6];
+ tmp[2] = src[1]*src[7];
+ tmp[3] = src[3]*src[5];
+ tmp[4] = src[1]*src[6];
+ tmp[5] = src[2]*src[5];
+ tmp[6] = src[0]*src[7];
+ tmp[7] = src[3]*src[4];
+ tmp[8] = src[0]*src[6];
+ tmp[9] = src[2]*src[4];
+ tmp[10] = src[0]*src[5];
+ tmp[11] = src[1]*src[4];
+ // calculate second 8 elements (cofactors)
+ dst[8] = tmp[0]*src[13] + tmp[3]*src[14] + tmp[4]*src[15];
+ dst[8] -= tmp[1]*src[13] + tmp[2]*src[14] + tmp[5]*src[15];
+ dst[9] = tmp[1]*src[12] + tmp[6]*src[14] + tmp[9]*src[15];
+ dst[9] -= tmp[0]*src[12] + tmp[7]*src[14] + tmp[8]*src[15];
+ dst[10] = tmp[2]*src[12] + tmp[7]*src[13] + tmp[10]*src[15];
+ dst[10]-= tmp[3]*src[12] + tmp[6]*src[13] + tmp[11]*src[15];
+ dst[11] = tmp[5]*src[12] + tmp[8]*src[13] + tmp[11]*src[14];
+ dst[11]-= tmp[4]*src[12] + tmp[9]*src[13] + tmp[10]*src[14];
+ dst[12] = tmp[2]*src[10] + tmp[5]*src[11] + tmp[1]*src[9];
+ dst[12]-= tmp[4]*src[11] + tmp[0]*src[9] + tmp[3]*src[10];
+ dst[13] = tmp[8]*src[11] + tmp[0]*src[8] + tmp[7]*src[10];
+ dst[13]-= tmp[6]*src[10] + tmp[9]*src[11] + tmp[1]*src[8];
+ dst[14] = tmp[6]*src[9] + tmp[11]*src[11] + tmp[3]*src[8];
+ dst[14]-= tmp[10]*src[11] + tmp[2]*src[8] + tmp[7]*src[9];
+ dst[15] = tmp[10]*src[10] + tmp[4]*src[8] + tmp[9]*src[9];
+ dst[15]-= tmp[8]*src[9] + tmp[11]*src[10] + tmp[5]*src[8];
+ // calculate determinant
+ det=src[0]*dst[0]+src[1]*dst[1]+src[2]*dst[2]+src[3]*dst[3];
+ // calculate matrix inverse
+ if( CompareApproximately(det,0.0f) )
+ {
+ for (int i=0;i<16;i++)
+ dst[i] = 0.0F;
+ return false;
+ }
+
+ det = 1.0f/det;
+ for (int j = 0; j < 16; j++)
+ dst[j] *= det;
+
+ return true;
+}
+*/
+
+
+/*
+// SSE based matrix inverse from Intel's "Streaming SIMD Extensions - Inverse of 4x4 Matrix" paper.
+// Does not seem to be much faster on Core 2 Duo. Keeping it here just in case.
+
+#include <emmintrin.h>
+
+bool InvertMatrix4x4( const float* src, float* dst )
+{
+ __m128 minor0, minor1, minor2, minor3;
+ __m128 row0, row1, row2, row3;
+ __m128 det, tmp1;
+ tmp1 = _mm_loadh_pi(_mm_loadl_pi(tmp1, (__m64*)(src)), (__m64*)(src+ 4));
+ row1 = _mm_loadh_pi(_mm_loadl_pi(row1, (__m64*)(src+8)), (__m64*)(src+12));
+ row0 = _mm_shuffle_ps(tmp1, row1, 0x88);
+ row1 = _mm_shuffle_ps(row1, tmp1, 0xDD);
+ tmp1 = _mm_loadh_pi(_mm_loadl_pi(tmp1, (__m64*)(src+ 2)), (__m64*)(src+ 6));
+ row3 = _mm_loadh_pi(_mm_loadl_pi(row3, (__m64*)(src+10)), (__m64*)(src+14));
+ row2 = _mm_shuffle_ps(tmp1, row3, 0x88);
+ row3 = _mm_shuffle_ps(row3, tmp1, 0xDD);
+ // -----------------------------------------------
+ tmp1 = _mm_mul_ps(row2, row3);
+ tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1);
+ minor0 = _mm_mul_ps(row1, tmp1);
+ minor1 = _mm_mul_ps(row0, tmp1);
+ tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E);
+ minor0 = _mm_sub_ps(_mm_mul_ps(row1, tmp1), minor0);
+ minor1 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor1);
+ minor1 = _mm_shuffle_ps(minor1, minor1, 0x4E);
+ // -----------------------------------------------
+ tmp1 = _mm_mul_ps(row1, row2);
+ tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1);
+ minor0 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor0);
+ minor3 = _mm_mul_ps(row0, tmp1);
+ tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E);
+ minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row3, tmp1));
+ minor3 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor3);
+ minor3 = _mm_shuffle_ps(minor3, minor3, 0x4E);
+ // -----------------------------------------------
+ tmp1 = _mm_mul_ps(_mm_shuffle_ps(row1, row1, 0x4E), row3);
+ tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1);
+ row2 = _mm_shuffle_ps(row2, row2, 0x4E);
+ minor0 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor0);
+ minor2 = _mm_mul_ps(row0, tmp1);
+ tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E);
+ minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row2, tmp1));
+ minor2 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor2);
+ minor2 = _mm_shuffle_ps(minor2, minor2, 0x4E);
+ // -----------------------------------------------
+ tmp1 = _mm_mul_ps(row0, row1);
+ tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1);
+ minor2 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor2);
+ minor3 = _mm_sub_ps(_mm_mul_ps(row2, tmp1), minor3);
+ tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E);
+ minor2 = _mm_sub_ps(_mm_mul_ps(row3, tmp1), minor2);
+ minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row2, tmp1));
+ // -----------------------------------------------
+ tmp1 = _mm_mul_ps(row0, row3);
+ tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1);
+ minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row2, tmp1));
+ minor2 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor2);
+ tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E);
+ minor1 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor1);
+ minor2 = _mm_sub_ps(minor2, _mm_mul_ps(row1, tmp1));
+ // -----------------------------------------------
+ tmp1 = _mm_mul_ps(row0, row2);
+ tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1);
+ minor1 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor1);
+ minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row1, tmp1));
+ tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E);
+ minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row3, tmp1));
+ minor3 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor3);
+ // -----------------------------------------------
+ det = _mm_mul_ps(row0, minor0);
+ det = _mm_add_ps(_mm_shuffle_ps(det, det, 0x4E), det);
+ det = _mm_add_ss(_mm_shuffle_ps(det, det, 0xB1), det);
+ // TODO: detect zero determinant
+ tmp1 = _mm_rcp_ss(det);
+ det = _mm_sub_ss(_mm_add_ss(tmp1, tmp1), _mm_mul_ss(det, _mm_mul_ss(tmp1, tmp1)));
+ det = _mm_shuffle_ps(det, det, 0x00);
+ minor0 = _mm_mul_ps(det, minor0);
+ _mm_storel_pi((__m64*)(dst), minor0);
+ _mm_storeh_pi((__m64*)(dst+2), minor0);
+ minor1 = _mm_mul_ps(det, minor1);
+ _mm_storel_pi((__m64*)(dst+4), minor1);
+ _mm_storeh_pi((__m64*)(dst+6), minor1);
+ minor2 = _mm_mul_ps(det, minor2);
+ _mm_storel_pi((__m64*)(dst+ 8), minor2);
+ _mm_storeh_pi((__m64*)(dst+10), minor2);
+ minor3 = _mm_mul_ps(det, minor3);
+ _mm_storel_pi((__m64*)(dst+12), minor3);
+ _mm_storeh_pi((__m64*)(dst+14), minor3);
+
+ return true;
+}
+*/
+
+
+Matrix4x4f& Matrix4x4f::Transpose ()
+{
+ swap(Get (0,1),Get (1,0));
+ swap(Get (0,2),Get (2,0));
+ swap(Get (0,3),Get (3,0));
+ swap(Get (1,2),Get (2,1));
+ swap(Get (1,3),Get (3,1));
+ swap(Get (2,3),Get (3,2));
+ return *this;
+}
+
+Matrix4x4f& Matrix4x4f::Copy (const Matrix4x4f& inM)
+{
+ CopyMatrix(inM.m_Data, m_Data);
+ return *this;
+}
+
+void fromToRotation(const float from[3], const float to[3],float mtx[3][3]);
+
+Matrix4x4f& Matrix4x4f::SetFromToRotation (const Vector3f& from, const Vector3f& to)
+{
+ float mtx[3][3];
+ fromToRotation (from.GetPtr (), to.GetPtr (), mtx);
+ Get (0, 0) = mtx[0][0]; Get (0, 1) = mtx[0][1]; Get (0, 2) = mtx[0][2]; Get (0, 3) = 0.0;
+ Get (1, 0) = mtx[1][0]; Get (1, 1) = mtx[1][1]; Get (1, 2) = mtx[1][2]; Get (1, 3) = 0.0;
+ Get (2, 0) = mtx[2][0]; Get (2, 1) = mtx[2][1]; Get (2, 2) = mtx[2][2]; Get (2, 3) = 0.0;
+ Get (3, 0) = 0.0; Get (3, 1) = 0.0; Get (3, 2) = 0.0; Get (3, 3) = 1.0;
+ return *this;
+}
+
+bool CompareApproximately (const Matrix4x4f& lhs, const Matrix4x4f& rhs, float dist)
+{
+ for (int i=0;i<16;i++)
+ {
+ if (!CompareApproximately (lhs[i], rhs[i], dist))
+ return false;
+ }
+ return true;
+}
+
+void Matrix4x4f::SetTR (const Vector3f& pos, const Quaternionf& q)
+{
+ QuaternionToMatrix (q, *this);
+ m_Data[12] = pos[0];
+ m_Data[13] = pos[1];
+ m_Data[14] = pos[2];
+}
+
+void Matrix4x4f::SetTRS (const Vector3f& pos, const Quaternionf& q, const Vector3f& s)
+{
+ QuaternionToMatrix (q, *this);
+
+ m_Data[0] *= s[0];
+ m_Data[1] *= s[0];
+ m_Data[2] *= s[0];
+
+ m_Data[4] *= s[1];
+ m_Data[5] *= s[1];
+ m_Data[6] *= s[1];
+
+ m_Data[8] *= s[2];
+ m_Data[9] *= s[2];
+ m_Data[10] *= s[2];
+
+ m_Data[12] = pos[0];
+ m_Data[13] = pos[1];
+ m_Data[14] = pos[2];
+}
+
+void Matrix4x4f::SetTRInverse (const Vector3f& pos, const Quaternionf& q)
+{
+ QuaternionToMatrix (::Inverse (q), *this);
+ Translate (Vector3f (-pos[0], -pos[1], -pos[2]));
+}
+
+void TransformPoints3x3 (const Matrix4x4f& matrix, const Vector3f* in, Vector3f* out, int count)
+{
+ Matrix3x3f m = Matrix3x3f(matrix);
+ for (int i=0;i<count;i++)
+ out[i] = m.MultiplyPoint3 (in[i]);
+}
+
+void TransformPoints3x4 (const Matrix4x4f& matrix, const Vector3f* in, Vector3f* out, int count)
+{
+ for (int i=0;i<count;i++)
+ out[i] = matrix.MultiplyPoint3 (in[i]);
+}
+
+void TransformPoints3x3 (const Matrix4x4f& matrix, const Vector3f* in, size_t inStride, Vector3f* out, size_t outStride, int count)
+{
+ Matrix3x3f m = Matrix3x3f(matrix);
+ for (int i=0;i<count; ++i, in = Stride (in, inStride), out = Stride (out, outStride))
+ {
+ *out = m.MultiplyPoint3 (*in);
+ }
+}
+
+void TransformPoints3x4 (const Matrix4x4f& matrix, const Vector3f* in, size_t inStride, Vector3f* out, size_t outStride, int count)
+{
+ for (int i=0;i<count; ++i, in = Stride (in, inStride), out = Stride (out, outStride))
+ {
+ *out = matrix.MultiplyPoint3 (*in);
+ }
+}
+
+
+#include "Matrix4x4_REF.cpp"
diff --git a/Runtime/Math/Matrix4x4.h b/Runtime/Math/Matrix4x4.h
new file mode 100644
index 0000000..2e14091
--- /dev/null
+++ b/Runtime/Math/Matrix4x4.h
@@ -0,0 +1,410 @@
+#ifndef MATRIX4X4_H
+#define MATRIX4X4_H
+
+#include "Vector3.h"
+#include "Vector4.h"
+#include "Runtime/Utilities/Prefetch.h"
+#include "Runtime/Misc/CPUInfo.h"
+#include "Runtime/Modules/ExportModules.h"
+
+#if (UNITY_SUPPORTS_SSE || UNITY_SUPPORTS_VMX)
+# include "Simd/SimdMath.h"
+#endif
+
+
+class Matrix3x3f;
+class Matrix4x4f;
+class Quaternionf;
+
+/// Uniform transform scales x, y, z in the same amount,
+/// NonUniform transform scales x, y, z differently and might contain skew.
+/// kOddNegativeScaleTransform means that FrontFace(CCW) should be used (An odd number of scale axes is negative)
+enum TransformType
+{
+ kNoScaleTransform = 0,
+ kUniformScaleTransform = 1 << 0,
+ kNonUniformScaleTransform = 1 << 1,
+ kOddNegativeScaleTransform = 1 << 2
+};
+ENUM_FLAGS(TransformType);
+
+inline bool IsNoScaleTransform (TransformType type) { return type == kNoScaleTransform; }
+inline bool IsNonUniformScaleTransform (TransformType type) { return (type & kNonUniformScaleTransform) != 0; }
+
+TransformType ComputeTransformType (const Matrix4x4f& matrix, float& outUniformScale, float epsilon = Vector3f::epsilon);
+
+bool InvertMatrix4x4_Full( const float* m, float* out );
+bool InvertMatrix4x4_General3D( const float* m, float* out );
+
+
+/// Matrices in unity are column major.
+class EXPORT_COREMODULE Matrix4x4f
+{
+ public:
+ float m_Data[16];
+
+
+ ///@todo: Can't be Transfer optimized because Transfer doesn't write the same as memory layout
+ DECLARE_SERIALIZE_NO_PPTR (Matrix4x4f)
+
+ Matrix4x4f () {}
+ Matrix4x4f (const Matrix3x3f &other);
+ explicit Matrix4x4f (const float data[16]);
+
+ float& Get (int row, int column) { return m_Data[row + (column*4)]; }
+ const float& Get (int row, int column)const { return m_Data[row + (column*4)]; }
+ float* GetPtr () { return m_Data; }
+ const float* GetPtr ()const { return m_Data; }
+
+ float operator [] (int index) const { return m_Data[index]; }
+ float& operator [] (int index) { return m_Data[index]; }
+
+ Matrix4x4f& operator *= (const Matrix4x4f& inM);
+
+ Matrix4x4f& operator = (const Matrix3x3f& m);
+
+ Vector3f MultiplyVector3 (const Vector3f& inV) const;
+ void MultiplyVector3 (const Vector3f& inV, Vector3f& output) const;
+ bool PerspectiveMultiplyVector3( const Vector3f& inV, Vector3f& output ) const;
+ Vector3f MultiplyPoint3 (const Vector3f& inV) const;
+ void MultiplyPoint3 (const Vector3f& inV, Vector3f& output) const;
+ bool PerspectiveMultiplyPoint3( const Vector3f& inV, Vector3f& output ) const;
+ Vector3f InverseMultiplyPoint3Affine (const Vector3f& inV) const;
+ Vector3f InverseMultiplyVector3Affine (const Vector3f& inV) const;
+
+ bool IsIdentity (float epsilon = Vector3f::epsilon) const;
+
+ double GetDeterminant() const;
+
+ Matrix4x4f& Invert_Full() {
+ InvertMatrix4x4_Full( m_Data, m_Data );
+ return *this;
+ }
+ static bool Invert_Full( const Matrix4x4f &inM, Matrix4x4f &outM ) {
+ return InvertMatrix4x4_Full( inM.m_Data, outM.m_Data );
+ }
+ static bool Invert_General3D( const Matrix4x4f &inM, Matrix4x4f &outM ) {
+ return InvertMatrix4x4_General3D( inM.m_Data, outM.m_Data );
+ }
+
+ Matrix4x4f& Transpose ();
+
+ Matrix4x4f& Copy (const Matrix4x4f& inM);
+
+ Matrix4x4f& SetIdentity ();
+ Matrix4x4f& SetPerspective( float fovy, float aspect, float zNear, float zFar );
+ // rad = Deg2Rad(fovy/2), contanHalfFOV = cos(rad)/sin(rad)
+ Matrix4x4f& SetPerspectiveCotan( float cotanHalfFOV, float zNear, float zFar );
+ Matrix4x4f& SetOrtho( float left, float right, float bottom, float top, float zNear, float zFar );
+ Matrix4x4f& SetFrustum( float left, float right, float bottom, float top, float nearval, float farval );
+
+ Vector3f GetAxisX() const;
+ Vector3f GetAxisY() const;
+ Vector3f GetAxisZ() const;
+ Vector3f GetPosition() const;
+ Vector4f GetRow(int row) const;
+ Vector4f GetColumn(int col) const;
+ // these set only these components of the matrix, everything else is untouched!
+ void SetAxisX( const Vector3f& v );
+ void SetAxisY( const Vector3f& v );
+ void SetAxisZ( const Vector3f& v );
+ void SetPosition( const Vector3f& v );
+ void SetRow( int row, const Vector4f& v );
+ void SetColumn( int col, const Vector4f& v );
+
+ Matrix4x4f& SetTranslate (const Vector3f& inTrans);
+ Matrix4x4f& SetOrthoNormalBasis (const Vector3f& inX, const Vector3f& inY, const Vector3f& inZ);
+ Matrix4x4f& SetOrthoNormalBasisInverse (const Vector3f& inX, const Vector3f& inY, const Vector3f& inZ);
+ Matrix4x4f& SetScale (const Vector3f& inScale);
+ Matrix4x4f& SetPositionAndOrthoNormalBasis (const Vector3f& inPosition, const Vector3f& inX, const Vector3f& inY, const Vector3f& inZ);
+
+ Matrix4x4f& Translate (const Vector3f& inTrans);
+ Matrix4x4f& Scale (const Vector3f& inScale);
+
+ Matrix4x4f& SetFromToRotation (const Vector3f& from, const Vector3f& to);
+
+ void SetTR (const Vector3f& pos, const Quaternionf& q);
+ void SetTRS (const Vector3f& pos, const Quaternionf& q, const Vector3f& s);
+ void SetTRInverse (const Vector3f& pos, const Quaternionf& q);
+
+ static const Matrix4x4f identity;
+};
+
+bool CompareApproximately (const Matrix4x4f& lhs, const Matrix4x4f& rhs, float dist = Vector3f::epsilon);
+
+/// Transforms an array of vertices. input may be the same as output.
+void EXPORT_COREMODULE TransformPoints3x3 (const Matrix4x4f &matrix, const Vector3f* input, Vector3f* ouput, int count);
+void EXPORT_COREMODULE TransformPoints3x4 (const Matrix4x4f &matrix, const Vector3f* input, Vector3f* ouput, int count);
+void EXPORT_COREMODULE TransformPoints3x3 (const Matrix4x4f &matrix, const Vector3f* input, size_t inStride, Vector3f* ouput, size_t outStride, int count);
+void EXPORT_COREMODULE TransformPoints3x4 (const Matrix4x4f &matrix, const Vector3f* input, size_t inStride, Vector3f* ouput, size_t outStride, int count);
+
+void MultiplyMatrices3x4( const Matrix4x4f& lhs, const Matrix4x4f& rhs, Matrix4x4f& res);
+
+void MultiplyMatrices4x4REF(const Matrix4x4f* __restrict lhs, const Matrix4x4f* __restrict rhs, Matrix4x4f* __restrict res);
+void CopyMatrixREF( const float* __restrict lhs, float* __restrict res);
+void TransposeMatrix4x4REF (const Matrix4x4f* __restrict lhs, Matrix4x4f* __restrict res);
+
+// foreach R[i] = A[i] * B[i]
+void MultiplyMatrixArray4x4REF(const Matrix4x4f* __restrict arrayA, const Matrix4x4f* __restrict arrayB,
+ Matrix4x4f* __restrict arrayRes, size_t count);
+// foreach R[i] = BASE * A[i] * B[i]
+void MultiplyMatrixArrayWithBase4x4REF (const Matrix4x4f* __restrict base,
+ const Matrix4x4f* __restrict arrayA, const Matrix4x4f* __restrict arrayB,
+ Matrix4x4f* __restrict arrayRes, size_t count);
+
+#if (UNITY_AUTO_DETECT_VECTOR_UNIT && UNITY_SUPPORTS_SSE)
+# define DECLARE_SIMD_FUNC(f) f##Simd
+#else
+# define DECLARE_SIMD_FUNC(f) f
+#endif
+
+#if (UNITY_SUPPORTS_SSE || UNITY_SUPPORTS_VMX)
+# include "Simd/Matrix4x4Simd.h"
+#elif UNITY_SUPPORTS_NEON
+
+#if UNITY_ANDROID || UNITY_WINRT || UNITY_BB10 || UNITY_TIZEN
+ #define MultiplyMatrices4x4_NEON _MultiplyMatrices4x4_NEON
+ #define CopyMatrix_NEON _CopyMatrix_NEON
+ #define TransposeMatrix4x4_NEON _TransposeMatrix4x4_NEON
+
+ #define MultiplyMatrixArray4x4_NEON _MultiplyMatrixArray4x4_NEON
+ #define MultiplyMatrixArrayWithBase4x4_NEON _MultiplyMatrixArrayWithBase4x4_NEON
+#endif
+
+extern "C"
+{
+ void CopyMatrix_NEON(const float* __restrict lhs, float* __restrict res);
+ void TransposeMatrix4x4_NEON(const Matrix4x4f* __restrict lhs, Matrix4x4f* __restrict res);
+
+ void MultiplyMatrices4x4_NEON(const Matrix4x4f* __restrict lhs, const Matrix4x4f* __restrict rhs, Matrix4x4f* __restrict res);
+ void MultiplyMatrixArray4x4_NEON(const Matrix4x4f* __restrict arrayA, const Matrix4x4f* __restrict arrayB,
+ Matrix4x4f* __restrict arrayRes, size_t count);
+ void MultiplyMatrixArrayWithBase4x4_NEON(const Matrix4x4f* __restrict base,
+ const Matrix4x4f* __restrict arrayA, const Matrix4x4f* __restrict arrayB,
+ Matrix4x4f* __restrict arrayRes, size_t count);
+}
+
+#if UNITY_ANDROID && UNITY_SUPPORTS_NEON && UNITY_SUPPORTS_VFP
+
+ #define MultiplyMatrices4x4_VFP _MultiplyMatrices4x4_VFP
+ #define MultiplyMatrixArray4x4_VFP _MultiplyMatrixArray4x4_VFP
+
+ extern "C"
+ {
+ void MultiplyMatrices4x4_VFP(const Matrix4x4f* __restrict lhs, const Matrix4x4f* __restrict rhs, Matrix4x4f* __restrict res);
+ void MultiplyMatrixArray4x4_VFP(const Matrix4x4f* __restrict arrayA, const Matrix4x4f* __restrict arrayB,
+ Matrix4x4f* __restrict arrayRes, size_t count);
+ }
+
+ #define CopyMatrix(a,b) CPUInfo::HasNEONSupport() ? CopyMatrix_NEON(a,b) : CopyMatrixREF(a,b)
+ #define TransposeMatrix4x4(a,b) CPUInfo::HasNEONSupport() ? TransposeMatrix4x4_NEON(a,b) : TransposeMatrix4x4REF(a,b)
+
+ #define MultiplyMatrices4x4(a,b,c) CPUInfo::HasNEONSupport() ? MultiplyMatrices4x4_NEON(a,b,c) : MultiplyMatrices4x4_VFP(a,b,c)
+ #define MultiplyMatrixArray4x4(a,b,c,d) CPUInfo::HasNEONSupport() ? MultiplyMatrixArray4x4_NEON(a,b,c,d) : MultiplyMatrixArray4x4_VFP(a,b,c,d)
+ #define MultiplyMatrixArrayWithBase4x4(a,b,c,d,e) CPUInfo::HasNEONSupport() ? MultiplyMatrixArrayWithBase4x4_NEON(a,b,c,d,e) : MultiplyMatrixArrayWithBase4x4REF(a,b,c,d,e)
+
+#else
+
+ #define CopyMatrix CopyMatrix_NEON
+ #define TransposeMatrix4x4 TransposeMatrix4x4_NEON
+
+ #define MultiplyMatrices4x4 MultiplyMatrices4x4_NEON
+ #define MultiplyMatrixArray4x4 MultiplyMatrixArray4x4_NEON
+ #define MultiplyMatrixArrayWithBase4x4 MultiplyMatrixArrayWithBase4x4_NEON
+
+#endif
+
+#elif UNITY_SUPPORTS_VFP
+
+#if UNITY_ANDROID
+ #define MultiplyMatrices4x4_VFP _MultiplyMatrices4x4_VFP
+ #define MultiplyMatrixArray4x4_VFP _MultiplyMatrixArray4x4_VFP
+#endif
+
+extern "C"
+{
+ void MultiplyMatrices4x4_VFP(const Matrix4x4f* __restrict lhs, const Matrix4x4f* __restrict rhs, Matrix4x4f* __restrict res);
+ void MultiplyMatrixArray4x4_VFP(const Matrix4x4f* __restrict arrayA, const Matrix4x4f* __restrict arrayB,
+ Matrix4x4f* __restrict arrayRes, size_t count);
+}
+
+ #define CopyMatrix CopyMatrixREF
+ #define TransposeMatrix4x4 TransposeMatrix4x4REF
+
+ #define MultiplyMatrices4x4 MultiplyMatrices4x4_VFP
+ #define MultiplyMatrixArray4x4 MultiplyMatrixArray4x4_VFP
+ #define MultiplyMatrixArrayWithBase4x4 MultiplyMatrixArrayWithBase4x4REF
+
+
+#else
+
+ #define CopyMatrix CopyMatrixREF
+ #define TransposeMatrix4x4 TransposeMatrix4x4REF
+
+ #define MultiplyMatrices4x4 MultiplyMatrices4x4REF
+ #define MultiplyMatrixArray4x4 MultiplyMatrixArray4x4REF
+ #define MultiplyMatrixArrayWithBase4x4 MultiplyMatrixArrayWithBase4x4REF
+
+#endif
+
+
+inline Vector3f Matrix4x4f::GetAxisX() const {
+ return Vector3f( Get(0,0), Get(1,0), Get(2,0) );
+}
+inline Vector3f Matrix4x4f::GetAxisY() const {
+ return Vector3f( Get(0,1), Get(1,1), Get(2,1) );
+}
+inline Vector3f Matrix4x4f::GetAxisZ() const {
+ return Vector3f( Get(0,2), Get(1,2), Get(2,2) );
+}
+inline Vector3f Matrix4x4f::GetPosition() const {
+ return Vector3f( Get(0,3), Get(1,3), Get(2,3) );
+}
+inline Vector4f Matrix4x4f::GetRow(int row) const {
+ return Vector4f( Get(row,0), Get(row,1), Get(row,2), Get(row,3) );
+}
+inline Vector4f Matrix4x4f::GetColumn(int col) const {
+ return Vector4f( Get(0,col), Get(1,col), Get(2,col), Get(3,col) );
+}
+inline void Matrix4x4f::SetAxisX( const Vector3f& v ) {
+ Get(0,0) = v.x; Get(1,0) = v.y; Get(2,0) = v.z;
+}
+inline void Matrix4x4f::SetAxisY( const Vector3f& v ) {
+ Get(0,1) = v.x; Get(1,1) = v.y; Get(2,1) = v.z;
+}
+inline void Matrix4x4f::SetAxisZ( const Vector3f& v ) {
+ Get(0,2) = v.x; Get(1,2) = v.y; Get(2,2) = v.z;
+}
+inline void Matrix4x4f::SetPosition( const Vector3f& v ) {
+ Get(0,3) = v.x; Get(1,3) = v.y; Get(2,3) = v.z;
+}
+inline void Matrix4x4f::SetRow( int row, const Vector4f& v ) {
+ Get(row,0) = v.x; Get(row,1) = v.y; Get(row,2) = v.z; Get(row,3) = v.w;
+}
+inline void Matrix4x4f::SetColumn( int col, const Vector4f& v ) {
+ Get(0,col) = v.x; Get(1,col) = v.y; Get(2,col) = v.z; Get(3,col) = v.w;
+}
+
+
+inline Vector3f Matrix4x4f::MultiplyPoint3 (const Vector3f& v) const
+{
+ Vector3f res;
+ res.x = m_Data[0] * v.x + m_Data[4] * v.y + m_Data[ 8] * v.z + m_Data[12];
+ res.y = m_Data[1] * v.x + m_Data[5] * v.y + m_Data[ 9] * v.z + m_Data[13];
+ res.z = m_Data[2] * v.x + m_Data[6] * v.y + m_Data[10] * v.z + m_Data[14];
+ return res;
+}
+
+inline void Matrix4x4f::MultiplyPoint3 (const Vector3f& v, Vector3f& output) const
+{
+ output.x = m_Data[0] * v.x + m_Data[4] * v.y + m_Data[ 8] * v.z + m_Data[12];
+ output.y = m_Data[1] * v.x + m_Data[5] * v.y + m_Data[ 9] * v.z + m_Data[13];
+ output.z = m_Data[2] * v.x + m_Data[6] * v.y + m_Data[10] * v.z + m_Data[14];
+}
+
+
+inline Vector3f Matrix4x4f::MultiplyVector3 (const Vector3f& v) const
+{
+ Vector3f res;
+ res.x = m_Data[0] * v.x + m_Data[4] * v.y + m_Data[ 8] * v.z;
+ res.y = m_Data[1] * v.x + m_Data[5] * v.y + m_Data[ 9] * v.z;
+ res.z = m_Data[2] * v.x + m_Data[6] * v.y + m_Data[10] * v.z;
+ return res;
+}
+
+inline void Matrix4x4f::MultiplyVector3 (const Vector3f& v, Vector3f& output) const
+{
+ output.x = m_Data[0] * v.x + m_Data[4] * v.y + m_Data[ 8] * v.z;
+ output.y = m_Data[1] * v.x + m_Data[5] * v.y + m_Data[ 9] * v.z;
+ output.z = m_Data[2] * v.x + m_Data[6] * v.y + m_Data[10] * v.z;
+}
+
+
+inline bool Matrix4x4f::PerspectiveMultiplyPoint3( const Vector3f& v, Vector3f& output ) const
+{
+ Vector3f res;
+ float w;
+ res.x = Get (0, 0) * v.x + Get (0, 1) * v.y + Get (0, 2) * v.z + Get (0, 3);
+ res.y = Get (1, 0) * v.x + Get (1, 1) * v.y + Get (1, 2) * v.z + Get (1, 3);
+ res.z = Get (2, 0) * v.x + Get (2, 1) * v.y + Get (2, 2) * v.z + Get (2, 3);
+ w = Get (3, 0) * v.x + Get (3, 1) * v.y + Get (3, 2) * v.z + Get (3, 3);
+ if( Abs(w) > 1.0e-7f )
+ {
+ float invW = 1.0f / w;
+ output.x = res.x * invW;
+ output.y = res.y * invW;
+ output.z = res.z * invW;
+ return true;
+ }
+ else
+ {
+ output.x = 0.0f;
+ output.y = 0.0f;
+ output.z = 0.0f;
+ return false;
+ }
+}
+
+inline bool Matrix4x4f::PerspectiveMultiplyVector3( const Vector3f& v, Vector3f& output ) const
+{
+ Vector3f res;
+ float w;
+ res.x = Get (0, 0) * v.x + Get (0, 1) * v.y + Get (0, 2) * v.z;
+ res.y = Get (1, 0) * v.x + Get (1, 1) * v.y + Get (1, 2) * v.z;
+ res.z = Get (2, 0) * v.x + Get (2, 1) * v.y + Get (2, 2) * v.z;
+ w = Get (3, 0) * v.x + Get (3, 1) * v.y + Get (3, 2) * v.z;
+ if( Abs(w) > 1.0e-7f )
+ {
+ float invW = 1.0f / w;
+ output.x = res.x * invW;
+ output.y = res.y * invW;
+ output.z = res.z * invW;
+ return true;
+ }
+ else
+ {
+ output.x = 0.0f;
+ output.y = 0.0f;
+ output.z = 0.0f;
+ return false;
+ }
+}
+
+inline Vector3f Matrix4x4f::InverseMultiplyPoint3Affine (const Vector3f& inV) const
+{
+ Vector3f v (inV.x - Get (0, 3), inV.y - Get (1, 3), inV.z - Get (2, 3));
+ Vector3f res;
+ res.x = Get (0, 0) * v.x + Get (1, 0) * v.y + Get (2, 0) * v.z;
+ res.y = Get (0, 1) * v.x + Get (1, 1) * v.y + Get (2, 1) * v.z;
+ res.z = Get (0, 2) * v.x + Get (1, 2) * v.y + Get (2, 2) * v.z;
+ return res;
+}
+
+inline Vector3f Matrix4x4f::InverseMultiplyVector3Affine (const Vector3f& v) const
+{
+ Vector3f res;
+ res.x = Get (0, 0) * v.x + Get (1, 0) * v.y + Get (2, 0) * v.z;
+ res.y = Get (0, 1) * v.x + Get (1, 1) * v.y + Get (2, 1) * v.z;
+ res.z = Get (0, 2) * v.x + Get (1, 2) * v.y + Get (2, 2) * v.z;
+ return res;
+}
+
+template<class TransferFunction> inline
+void Matrix4x4f::Transfer (TransferFunction& t)
+{
+ t.Transfer (Get (0, 0), "e00"); t.Transfer (Get (0, 1), "e01"); t.Transfer (Get (0, 2), "e02"); t.Transfer (Get (0, 3), "e03");
+ t.Transfer (Get (1, 0), "e10"); t.Transfer (Get (1, 1), "e11"); t.Transfer (Get (1, 2), "e12"); t.Transfer (Get (1, 3), "e13");
+ t.Transfer (Get (2, 0), "e20"); t.Transfer (Get (2, 1), "e21"); t.Transfer (Get (2, 2), "e22"); t.Transfer (Get (2, 3), "e23");
+ t.Transfer (Get (3, 0), "e30"); t.Transfer (Get (3, 1), "e31"); t.Transfer (Get (3, 2), "e32"); t.Transfer (Get (3, 3), "e33");
+}
+
+inline bool IsFinite (const Matrix4x4f& f)
+{
+ return
+ IsFinite(f.m_Data[0]) & IsFinite(f.m_Data[1]) & IsFinite(f.m_Data[2]) &
+ IsFinite(f.m_Data[4]) & IsFinite(f.m_Data[5]) & IsFinite(f.m_Data[6]) &
+ IsFinite(f.m_Data[8]) & IsFinite(f.m_Data[9]) & IsFinite(f.m_Data[10]) &
+ IsFinite(f.m_Data[12]) & IsFinite(f.m_Data[13]) & IsFinite(f.m_Data[14]) & IsFinite(f.m_Data[15]);
+}
+
+#endif
diff --git a/Runtime/Math/Matrix4x4_NEON.asm b/Runtime/Math/Matrix4x4_NEON.asm
new file mode 100644
index 0000000..59d6bce
--- /dev/null
+++ b/Runtime/Math/Matrix4x4_NEON.asm
@@ -0,0 +1,197 @@
+ AREA .text, CODE
+
+ EXPORT _CopyMatrix_NEON
+ EXPORT _TransposeMatrix4x4_NEON
+ EXPORT _MultiplyMatrices4x4_NEON
+ EXPORT _MultiplyMatrixArray4x4_NEON
+ EXPORT _MultiplyMatrixArrayWithBase4x4_NEON
+
+|_CopyMatrix_NEON| PROC
+ vld1.32 {d0-d3}, [r0]!
+ vld1.32 {d4-d7}, [r0]
+ vst1.32 {d0-d3}, [r1]!
+ vst1.32 {d4-d7}, [r1]
+ bx lr
+ ENDP
+
+
+|_TransposeMatrix4x4_NEON| PROC
+ vld4.32 {d0,d2,d4,d6}, [r0]!
+ vld4.32 {d1,d3,d5,d7}, [r0]
+ vst1.32 {d0-d3}, [r1]!
+ vst1.32 {d4-d7}, [r1]
+ bx lr
+ ENDP
+
+
+|_MultiplyMatrices4x4_NEON| PROC
+ vld1.32 {d0-d3}, [r1]!
+ vld1.32 {d16-d17}, [r0]!
+ vmul.f32 q12, q8, d0[0]
+ vld1.32 {d4-d5}, [r1]!
+ vmul.f32 q13, q8, d2[0]
+ vld1.32 {d6-d7}, [r1]!
+ vmul.f32 q14, q8, d4[0]
+ vld1.32 {d18-d19}, [r0]!
+ vmul.f32 q15, q8, d6[0]
+ vld1.32 {d20-d21}, [r0]!
+ vmla.f32 q12, q9, d0[1]
+ vld1.32 {d22-d23}, [r0]!
+ vmla.f32 q13, q9, d2[1]
+ vmla.f32 q14, q9, d4[1]
+ vmla.f32 q15, q9, d6[1]
+ vmla.f32 q12, q10, d1[0]
+ vmla.f32 q13, q10, d3[0]
+ vmla.f32 q14, q10, d5[0]
+ vmla.f32 q15, q10, d7[0]
+ vmla.f32 q12, q11, d1[1]
+ vmla.f32 q13, q11, d3[1]
+ vmla.f32 q14, q11, d5[1]
+ vmla.f32 q15, q11, d7[1]
+ vst1.32 {d24-d27}, [r2]!
+ vst1.32 {d28-d31}, [r2]!
+ bx lr
+ ENDP
+
+
+|_MultiplyMatrixArray4x4_NEON| PROC
+ vpush {d8-d15}
+ add.w r3, r0, r3, lsl #6
+ vld1.32 {d0-d3}, [r1]!
+ vld1.32 {d4-d7}, [r1]!
+ vld1.32 {d16-d17}, [r0]!
+ nop
+
+|_MultiplyMatrixArray4x4_NEON_loop|
+ vmul.f32 q12, q8, d0[0]
+ vld1.32 {d18-d19}, [r0]!
+ vmul.f32 q13, q8, d2[0]
+ vmul.f32 q14, q8, d4[0]
+ vmul.f32 q15, q8, d6[0]
+ vmla.f32 q12, q9, d0[1]
+ vld1.32 {d20-d21}, [r0]!
+ vmla.f32 q13, q9, d2[1]
+ vld1.32 {d8-d11}, [r1]!
+ vmla.f32 q14, q9, d4[1]
+ vld1.32 {d12-d15}, [r1]!
+ vmla.f32 q15, q9, d6[1]
+ vmla.f32 q12, q10, d1[0]
+ vld1.32 {d22-d23}, [r0]!
+ vmla.f32 q13, q10, d3[0]
+ vmla.f32 q14, q10, d5[0]
+ vmla.f32 q15, q10, d7[0]
+ vmla.f32 q12, q11, d1[1]
+ vld1.32 {d16-d17}, [r0]!
+ vmla.f32 q13, q11, d3[1]
+ vmla.f32 q14, q11, d5[1]
+ vmla.f32 q15, q11, d7[1]
+ vst1.32 {d24-d27}, [r2]!
+ vst1.32 {d28-d31}, [r2]!
+ cmp r0, r3
+ bcs.w |_MultiplyMatrixArray4x4_out|
+ vmul.f32 q12, q8, d8[0]
+ vld1.32 {d18-d19}, [r0]!
+ vmul.f32 q13, q8, d10[0]
+ vmul.f32 q14, q8, d12[0]
+ vmul.f32 q15, q8, d14[0]
+ vmla.f32 q12, q9, d8[1]
+ vld1.32 {d20-d21}, [r0]!
+ vmla.f32 q13, q9, d10[1]
+ vld1.32 {d0-d3}, [r1]!
+ vmla.f32 q14, q9, d12[1]
+ vld1.32 {d4-d7}, [r1]!
+ vmla.f32 q15, q9, d14[1]
+ vmla.f32 q12, q10, d9[0]
+ vld1.32 {d22-d23}, [r0]!
+ vmla.f32 q13, q10, d11[0]
+ vmla.f32 q14, q10, d13[0]
+ vmla.f32 q15, q10, d15[0]
+ vmla.f32 q12, q11, d9[1]
+ vld1.32 {d16-d17}, [r0]!
+ vmla.f32 q13, q11, d11[1]
+ vmla.f32 q14, q11, d13[1]
+ vmla.f32 q15, q11, d15[1]
+ vst1.32 {d24-d27}, [r2]!
+ vst1.32 {d28-d31}, [r2]!
+ cmp r0, r3
+ bcc.w |_MultiplyMatrixArray4x4_NEON_loop|
+ nop.w
+
+|_MultiplyMatrixArray4x4_out|
+ vpop {d8-d15}
+ bx lr
+ ENDP
+
+
+|_MultiplyMatrixArrayWithBase4x4_NEON| PROC
+ mov ip, sp
+ vpush {d8-d15}
+ stmdb sp!, {r4, r5}
+ ldr.w r4, [ip]
+ add.w r4, r1, r4, lsl #6
+ vld1.32 {d16-d17}, [r1]!
+ vld1.32 {d0-d3}, [r2]!
+ vld1.32 {d4-d7}, [r2]!
+ vld1.32 {d20-d23}, [r0]!
+ add.w r5, r0, #16
+ vmul.f32 q4, q8, d0[0]
+ vmul.f32 q5, q8, d2[0]
+ nop.w
+ nop.w
+ nop.w
+
+|_MultiplyMatrixArrayWithBase4x4_NEON_loop|
+ vld1.32 {d18-d19}, [r1]!
+ vmul.f32 q6, q8, d4[0]
+ vmul.f32 q7, q8, d6[0]
+ vmla.f32 q4, q9, d0[1]
+ vmla.f32 q5, q9, d2[1]
+ vld1.32 {d16-d17}, [r1]!
+ vmla.f32 q6, q9, d4[1]
+ vmla.f32 q7, q9, d6[1]
+ vmla.f32 q4, q8, d1[0]
+ vmla.f32 q5, q8, d3[0]
+ vld1.32 {d18-d19}, [r1]!
+ vmla.f32 q6, q8, d5[0]
+ vmla.f32 q7, q8, d7[0]
+ cmp r1, r4
+ vmla.f32 q4, q9, d1[1]
+ vmla.f32 q5, q9, d3[1]
+ vld1.32 {d16-d17}, [r0]
+ vmla.f32 q6, q9, d5[1]
+ vmla.f32 q7, q9, d7[1]
+ vld1.32 {d18-d19}, [r5]
+ vmul.f32 q12, q10, d8[0]
+ vmul.f32 q13, q10, d10[0]
+ vld1.32 {d0-d1}, [r2]!
+ vmul.f32 q14, q10, d12[0]
+ vmul.f32 q15, q10, d14[0]
+ vmla.f32 q12, q11, d8[1]
+ vmla.f32 q13, q11, d10[1]
+ vld1.32 {d2-d3}, [r2]!
+ vmla.f32 q14, q11, d12[1]
+ vmla.f32 q15, q11, d14[1]
+ vmla.f32 q12, q8, d9[0]
+ vmla.f32 q13, q8, d11[0]
+ vld1.32 {d4-d5}, [r2]!
+ vmla.f32 q14, q8, d13[0]
+ vmla.f32 q12, q9, d9[1]
+ vmla.f32 q13, q9, d11[1]
+ vmla.f32 q15, q8, d15[0]
+ vld1.32 {d6-d7}, [r2]!
+ vmla.f32 q14, q9, d13[1]
+ vld1.32 {d16-d17}, [r1]!
+ vmla.f32 q15, q9, d15[1]
+ vst1.32 {d24-d27}, [r3]!
+ vmul.f32 q4, q8, d0[0]
+ vmul.f32 q5, q8, d2[0]
+ vst1.32 {d28-d31}, [r3]!
+ bcc.w |_MultiplyMatrixArrayWithBase4x4_NEON_loop|
+ pop {r4, r5}
+ vpop {d8-d15}
+ bx lr
+ nop
+ ENDP
+
+
+ END
diff --git a/Runtime/Math/Matrix4x4_NEON.s b/Runtime/Math/Matrix4x4_NEON.s
new file mode 100644
index 0000000..12f2ffd
--- /dev/null
+++ b/Runtime/Math/Matrix4x4_NEON.s
@@ -0,0 +1,375 @@
+#define UNITY_ASSEMBLER
+#include "Configuration/PrefixConfigure.h"
+
+#if UNITY_SUPPORTS_NEON
+
+.set device,0
+.set device,__arm__
+
+.if device
+
+//.code32
+
+.globl _CopyMatrix_NEON
+.globl _TransposeMatrix4x4_NEON
+.globl _MultiplyMatrices4x4_NEON
+.globl _MultiplyMatrixArray4x4_NEON
+.globl _MultiplyMatrixArrayWithBase4x4_NEON
+
+#if UNITY_ANDROID
+.hidden _CopyMatrix_NEON
+.hidden _TransposeMatrix4x4_NEON
+.hidden _MultiplyMatrices4x4_NEON
+.hidden _MultiplyMatrixArray4x4_NEON
+.hidden _MultiplyMatrixArrayWithBase4x4_NEON
+#endif
+
+
+//===========================================================================================================================================
+
+// void CopyMatrix_NEON(const float* __restrict lhs, float* __restrict res)
+_CopyMatrix_NEON:
+// r0: src
+// r1: dst
+
+vld1.32 {q0,q1}, [r0]!
+vld1.32 {q2,q3}, [r0]
+vst1.32 {q0,q1}, [r1]!
+vst1.32 {q2,q3}, [r1]
+
+bx lr
+
+
+//===========================================================================================================================================
+
+// void TransposeMatrix4x4_NEON(const Matrix4x4f* __restrict lhs, Matrix4x4f* __restrict res)
+_TransposeMatrix4x4_NEON:
+// r0: src
+// r1: dst
+
+vld4.32 {d0,d2,d4,d6}, [r0]!
+vld4.32 {d1,d3,d5,d7}, [r0]
+vst1.32 {d0,d1,d2,d3}, [r1]!
+vst1.32 {d4,d5,d6,d7}, [r1]
+
+bx lr
+
+
+//===========================================================================================================================================
+
+// void MultiplyMatrices4x4_NEON(const Matrix4x4f* __restrict lhs, const Matrix4x4f* __restrict rhs, Matrix4x4f* __restrict res)
+_MultiplyMatrices4x4_NEON:
+// r0: A
+// r1: B
+// r2: dst
+
+vld1.32 {q0,q1}, [r1]! // load Brow1-2
+vld1.32 {q8}, [r0]! // load Arow1
+
+// R = Arow1 * Bcol1
+vmul.f32 q12, q8, d0[0]
+vld1.32 {q2}, [r1]! // load Brow3
+
+vmul.f32 q13, q8, d2[0]
+vld1.32 {q3}, [r1]! // load Brow4
+
+vmul.f32 q14, q8, d4[0]
+vld1.32 {q9}, [r0]! // load Arow2
+
+vmul.f32 q15, q8, d6[0]
+vld1.32 {q10}, [r0]! // load Arow3
+
+// R += Arow2 * Bcolumn2
+vmla.f32 q12, q9, d0[1]
+vld1.32 {q11}, [r0]! // load Arow4
+
+vmla.f32 q13, q9, d2[1]
+vmla.f32 q14, q9, d4[1]
+vmla.f32 q15, q9, d6[1]
+
+// R += Arow3 * Bcolumn3
+vmla.f32 q12, q10, d1[0]
+vmla.f32 q13, q10, d3[0]
+vmla.f32 q14, q10, d5[0]
+vmla.f32 q15, q10, d7[0]
+
+// R += Arow4 * Bcolumn4
+vmla.f32 q12, q11, d1[1]
+vmla.f32 q13, q11, d3[1]
+vmla.f32 q14, q11, d5[1]
+vmla.f32 q15, q11, d7[1]
+
+vst1.32 {q12,q13}, [r2]!
+vst1.32 {q14,q15}, [r2]!
+
+bx lr
+
+
+//===========================================================================================================================================
+
+// void MultiplyMatrixArray4x4_NEON(const Matrix4x4f* arrayA, const Matrix4x4f* arrayB, Matrix4x4f* arrayRes, size_t count)
+_MultiplyMatrixArray4x4_NEON:
+// r0: A
+// r1: B
+// r2: dst
+// r3: A end
+
+vpush {d8-d15}
+add r3, r0, r3, lsl #6
+
+vld1.32 {q0,q1}, [r1]!
+vld1.32 {q2,q3}, [r1]!
+vld1.32 {q8}, [r0]!
+
+
+.align 4
+_MultiplyMatrixArray4x4_NEON_loop:
+
+vmul.f32 q12, q8, d0[0]
+vld1.32 {q9}, [r0]! // load Arow2
+
+vmul.f32 q13, q8, d2[0]
+vmul.f32 q14, q8, d4[0]
+vmul.f32 q15, q8, d6[0]
+
+
+vmla.f32 q12, q9, d0[1]
+
+vld1.32 {q10}, [r0]! // load Arow3
+vmla.f32 q13, q9, d2[1]
+
+vld1.32 {q4,q5}, [r1]! // load B[i+1]
+vmla.f32 q14, q9, d4[1]
+
+vld1.32 {q6,q7}, [r1]! // load B[i+1]
+vmla.f32 q15, q9, d6[1]
+
+vmla.f32 q12, q10, d1[0]
+vld1.32 {q11}, [r0]! // load Arow3
+
+vmla.f32 q13, q10, d3[0]
+vmla.f32 q14, q10, d5[0]
+vmla.f32 q15, q10, d7[0]
+
+vmla.f32 q12, q11, d1[1]
+vld1.32 {q8}, [r0]! // load A[i+1]row1
+
+vmla.f32 q13, q11, d3[1]
+vmla.f32 q14, q11, d5[1]
+vmla.f32 q15, q11, d7[1]
+
+vst1.32 {q12,q13}, [r2]!
+vst1.32 {q14,q15}, [r2]!
+
+cmp r0, r3
+bcs _MultiplyMatrixArray4x4_out
+
+
+vmul.f32 q12, q8, d8[0]
+vld1.32 {q9}, [r0]! // load A[i+1]row2
+
+vmul.f32 q13, q8, d10[0]
+vmul.f32 q14, q8, d12[0]
+vmul.f32 q15, q8, d14[0]
+
+vmla.f32 q12, q9, d8[1]
+
+vld1.32 {q10}, [r0]! // load A[i+1]row3
+vmla.f32 q13, q9, d10[1]
+
+vld1.32 {q0,q1}, [r1]! // load B[i+2]
+vmla.f32 q14, q9, d12[1]
+
+vld1.32 {q2,q3}, [r1]! // load B[i+2]
+vmla.f32 q15, q9, d14[1]
+
+vmla.f32 q12, q10, d9[0]
+vld1.32 {q11}, [r0]! // load A[i+1]row4
+
+vmla.f32 q13, q10, d11[0]
+vmla.f32 q14, q10, d13[0]
+vmla.f32 q15, q10, d15[0]
+
+vmla.f32 q12, q11, d9[1]
+vld1.32 {q8}, [r0]! // load A[i+2]row1
+
+vmla.f32 q13, q11, d11[1]
+vmla.f32 q14, q11, d13[1]
+vmla.f32 q15, q11, d15[1]
+
+vst1.32 {q12,q13}, [r2]!
+vst1.32 {q14,q15}, [r2]!
+
+cmp r0, r3
+bcc _MultiplyMatrixArray4x4_NEON_loop
+
+
+.align 4
+_MultiplyMatrixArray4x4_out:
+
+vpop {d8-d15}
+bx lr
+
+
+
+//===========================================================================================================================================
+
+#define MT_11_1 \
+ vmul.f32 q12, q10, d8[0] ; \
+ vmul.f32 q13, q10, d10[0] ;
+
+#define MT_11_2 \
+ vmul.f32 q14, q10, d12[0] ; \
+ vmul.f32 q15, q10, d14[0] ;
+
+#define MT_22_1 \
+ vmla.f32 q12, q11, d8[1] ; \
+ vmla.f32 q13, q11, d10[1] ;
+
+#define MT_22_2 \
+ vmla.f32 q14, q11, d12[1] ; \
+ vmla.f32 q15, q11, d14[1] ;
+
+#define MT_33_1 \
+ vmla.f32 q12, q8, d9[0] ; \
+ vmla.f32 q13, q8, d11[0] ;
+
+#define MT_33_2_44_1 \
+ vmla.f32 q14, q8, d13[0] ; \
+ vmla.f32 q12, q9, d9[1] ; \
+ vmla.f32 q13, q9, d11[1] ; \
+ vmla.f32 q15, q8, d15[0] ;
+
+
+
+// void MultiplyMatrixArrayWithBase4x4_NEON( const Matrix4x4f* base, const Matrix4x4f* arrayA, const Matrix4x4f* arrayB, Matrix4x4f* arrayRes, size_t count )
+_MultiplyMatrixArrayWithBase4x4_NEON:
+// r0: base
+// r1: A
+// r2: B
+// r3: dst
+// r4: A end
+
+mov ip, sp
+
+vpush {d8-d15}
+stmfd sp!, {r4-r5}
+
+
+ldr r4, [ip, #0]
+add r4, r1, r4, lsl #6
+
+
+vld1.32 {q8}, [r1]! // load Arow1
+vld1.32 {q0,q1}, [r2]! // load Brow1-2
+vld1.32 {q2,q3}, [r2]! // load Brow3-4
+vld1.32 {q10,q11}, [r0]! // load Mrow1-2
+
+add r5, r0, #16
+
+// T = Arow1 * Bcol1
+
+vmul.f32 q4, q8, d0[0]
+vmul.f32 q5, q8, d2[0]
+
+
+.align 4
+_MultiplyMatrixArrayWithBase4x4_NEON_loop:
+
+// T = Arow1 * Bcol1
+
+vld1.32 {q9}, [r1]! // load Arow2
+vmul.f32 q6, q8, d4[0]
+vmul.f32 q7, q8, d6[0]
+
+// T += Arow2 * Bcol2
+
+vmla.f32 q4, q9, d0[1]
+vmla.f32 q5, q9, d2[1]
+
+ vld1.32 {q8}, [r1]! // load Arow3
+vmla.f32 q6, q9, d4[1]
+vmla.f32 q7, q9, d6[1]
+
+// T += Arow3 * Bcol3
+
+vmla.f32 q4, q8, d1[0]
+vmla.f32 q5, q8, d3[0]
+
+vld1.32 {q9}, [r1]! // load Arow4
+
+vmla.f32 q6, q8, d5[0]
+vmla.f32 q7, q8, d7[0]
+
+cmp r1, r4
+
+// T += Arow4 * Bcol4
+
+vmla.f32 q4, q9, d1[1]
+vmla.f32 q5, q9, d3[1]
+
+vld1.32 {q8}, [r0] // load Mrow3
+vmla.f32 q6, q9, d5[1]
+vmla.f32 q7, q9, d7[1]
+
+// R = M * T
+
+vld1.32 {q9}, [r5] // load Mrow4
+MT_11_1
+
+bge _MultiplyMatrixArrayWithBase4x4_NEON_epilogue
+
+vld1.32 {q0}, [r2]! // load B[i+1]row1
+MT_11_2
+MT_22_1
+
+vld1.32 {q1}, [r2]! // load B[i+1]row2
+MT_22_2
+
+MT_33_1
+vld1.32 {q2}, [r2]! // load B[i+1]row3
+
+MT_33_2_44_1
+vld1.32 {q3}, [r2]! // load B[i+1]row4
+
+vmla.f32 q14, q9, d13[1]
+vld1.32 {q8}, [r1]! // load A[i+1]row1
+
+vmla.f32 q15, q9, d15[1]
+vst1.32 {q12,q13}, [r3]!
+
+// interleave T = Arow1 * Bcol1
+vmul.f32 q4, q8, d0[0]
+vmul.f32 q5, q8, d2[0]
+vst1.32 {q14,q15}, [r3]!
+
+bcc _MultiplyMatrixArrayWithBase4x4_NEON_loop
+
+.align 4
+_MultiplyMatrixArrayWithBase4x4_NEON_epilogue:
+
+MT_11_2
+MT_22_1
+MT_22_2
+MT_33_1
+MT_33_2_44_1
+vmla.f32 q14, q9, d13[1]
+vmla.f32 q15, q9, d15[1]
+vst1.32 {q12,q13}, [r3]!
+vst1.32 {q14,q15}, [r3]!
+
+
+ldmfd sp!, {r4-r5}
+vpop {d8-d15}
+bx lr
+
+.endif
+
+#undef MT_11_1
+#undef MT_11_2
+#undef MT_22_1
+#undef MT_22_2
+#undef MT_33_1
+#undef MT_33_2_44_1
+
+#endif
diff --git a/Runtime/Math/Matrix4x4_REF.cpp b/Runtime/Math/Matrix4x4_REF.cpp
new file mode 100644
index 0000000..290e0d8
--- /dev/null
+++ b/Runtime/Math/Matrix4x4_REF.cpp
@@ -0,0 +1,60 @@
+#include "UnityPrefix.h"
+#include "Matrix4x4.h"
+#include <algorithm>
+
+void CopyMatrixREF ( const float* __restrict lhs, float* __restrict res)
+{
+ ::memcpy( res, lhs, sizeof(Matrix4x4f) );
+}
+
+void TransposeMatrix4x4REF (const Matrix4x4f* __restrict lhs, Matrix4x4f* __restrict res)
+{
+ CopyMatrix(lhs->m_Data, res->m_Data);
+ std::swap( res->Get(0,1), res->Get(1,0) );
+ std::swap( res->Get(0,2), res->Get(2,0) );
+ std::swap( res->Get(0,3), res->Get(3,0) );
+ std::swap( res->Get(1,2), res->Get(2,1) );
+ std::swap( res->Get(1,3), res->Get(3,1) );
+ std::swap( res->Get(2,3), res->Get(3,2) );
+}
+
+void MultiplyMatrices4x4REF (const Matrix4x4f* __restrict lhs, const Matrix4x4f* __restrict rhs, Matrix4x4f* __restrict res)
+{
+ Assert (lhs != rhs && lhs != res && rhs != res);
+ for (int i=0;i<4;i++)
+ {
+ res->m_Data[i] = lhs->m_Data[i] * rhs->m_Data[0] + lhs->m_Data[i+4] * rhs->m_Data[1] + lhs->m_Data[i+8] * rhs->m_Data[2] + lhs->m_Data[i+12] * rhs->m_Data[3];
+ res->m_Data[i+4] = lhs->m_Data[i] * rhs->m_Data[4] + lhs->m_Data[i+4] * rhs->m_Data[5] + lhs->m_Data[i+8] * rhs->m_Data[6] + lhs->m_Data[i+12] * rhs->m_Data[7];
+ res->m_Data[i+8] = lhs->m_Data[i] * rhs->m_Data[8] + lhs->m_Data[i+4] * rhs->m_Data[9] + lhs->m_Data[i+8] * rhs->m_Data[10] + lhs->m_Data[i+12] * rhs->m_Data[11];
+ res->m_Data[i+12] = lhs->m_Data[i] * rhs->m_Data[12] + lhs->m_Data[i+4] * rhs->m_Data[13] + lhs->m_Data[i+8] * rhs->m_Data[14] + lhs->m_Data[i+12] * rhs->m_Data[15];
+ }
+}
+
+void MultiplyMatrixArray4x4REF (const Matrix4x4f* __restrict a, const Matrix4x4f* __restrict b, Matrix4x4f* __restrict res, size_t count)
+{
+ Assert(a);
+ Assert(b);
+ Assert(res);
+
+ for (size_t i = 0; i < count; ++i)
+ {
+ MultiplyMatrices4x4(a+i, b+i, res+i);
+ }
+}
+
+void MultiplyMatrixArrayWithBase4x4REF (const Matrix4x4f* __restrict base,
+ const Matrix4x4f* __restrict a, const Matrix4x4f* __restrict b, Matrix4x4f* __restrict res, size_t count)
+{
+ Assert(base);
+ Assert(a);
+ Assert(b);
+ Assert(res);
+
+ Matrix4x4f tmp;
+ for (size_t i = 0; i < count; ++i)
+ {
+ MultiplyMatrices4x4(base, a+i, &tmp);
+ MultiplyMatrices4x4(&tmp, b+i, res+i);
+ }
+}
+
diff --git a/Runtime/Math/Matrix4x4_VFP.s b/Runtime/Math/Matrix4x4_VFP.s
new file mode 100644
index 0000000..1745cc3
--- /dev/null
+++ b/Runtime/Math/Matrix4x4_VFP.s
@@ -0,0 +1,149 @@
+#define UNITY_ASSEMBLER
+#include "Configuration/PrefixConfigure.h"
+#include "Runtime/Utilities/VFPUtility.h"
+
+#if UNITY_SUPPORTS_VFP
+
+.syntax unified
+
+.set device,0
+.set device,__arm__
+
+.if device
+
+//.code32
+
+.globl _MultiplyMatrices4x4_VFP
+.globl _MultiplyMatrixArray4x4_VFP
+
+#if UNITY_ANDROID
+
+.hidden _MultiplyMatrices4x4_VFP
+.hidden _MultiplyMatrixArray4x4_VFP
+
+#endif
+
+
+//===========================================================================================================================================
+
+
+// void MultiplyMatrices4x4_VFP(const Matrix4x4f* __restrict lhs, const Matrix4x4f* __restrict rhs, Matrix4x4f* __restrict res)
+_MultiplyMatrices4x4_VFP:
+// r0: A
+// r1: B
+// r2: dst
+
+vpush {d8-d15}
+
+mov ip, r0
+
+// VFP_VECTOR_LENGTH(3)
+
+mov r0, ip
+
+vldmia.32 r0, {s8-s23}
+vldmia.32 r1!, {s0-s7}
+
+FMULS4 (24,25,26,27, 8,9,10,11, 0,0,0,0)
+FMULS4 (28,29,30,31, 8,9,10,11, 4,4,4,4)
+
+FMACS4 (24,25,26,27, 12,13,14,15, 1,1,1,1)
+FMACS4 (28,29,30,31, 12,13,14,15, 5,5,5,5)
+
+FMACS4 (24,25,26,27, 16,17,18,19, 2,2,2,2)
+FMACS4 (28,29,30,31, 16,17,18,19, 6,6,6,6)
+
+FMACS4 (24,25,26,27, 20,21,22,23, 3,3,3,3)
+FMACS4 (28,29,30,31, 20,21,22,23, 7,7,7,7)
+
+
+vstmia.32 r2!, {s24-s31}
+vldmia.32 r1, {s0-s7}
+
+FMULS4 (24,25,26,27, 8,9,10,11, 0,0,0,0)
+FMULS4 (28,29,30,31, 8,9,10,11, 4,4,4,4)
+
+FMACS4 (24,25,26,27, 12,13,14,15, 1,1,1,1)
+FMACS4 (28,29,30,31, 12,13,14,15, 5,5,5,5)
+
+FMACS4 (24,25,26,27, 16,17,18,19, 2,2,2,2)
+FMACS4 (28,29,30,31, 16,17,18,19, 6,6,6,6)
+
+FMACS4 (24,25,26,27, 20,21,22,23, 3,3,3,3)
+FMACS4 (28,29,30,31, 20,21,22,23, 7,7,7,7)
+
+vstmia.32 r2, {s24-s31}
+
+// VFP_VECTOR_LENGTH_ZERO
+
+vpop {d8-d15}
+bx lr
+
+
+//===========================================================================================================================================
+
+// void MultiplyMatrixArray4x4_VFP(const Matrix4x4f* arrayA, const Matrix4x4f* arrayB, Matrix4x4f* arrayRes, size_t count)
+_MultiplyMatrixArray4x4_VFP:
+// r0: A
+// r1: B
+// r2: dst
+// r3: A end
+
+vpush {d8-d15}
+
+mov ip, r0
+
+// VFP_VECTOR_LENGTH(3)
+
+mov r0, ip
+add r3, r0, r3, lsl #6
+
+
+.align 4
+_MultiplyMatrixArray4x4_VFP_loop:
+
+vldmia.32 r0!, {s16-s31}
+vldmia.32 r1!, {s0-s7}
+
+FMULS4 (8,9,10,11, 16,17,18,19, 0,0,0,0)
+FMULS4 (12,13,14,15, 16,17,18,19, 4,4,4,4)
+
+FMACS4 (8,9,10,11, 20,21,22,23, 1,1,1,1)
+FMACS4 (12,13,14,15, 20,21,22,23, 5,5,5,5)
+
+FMACS4 (8,9,10,11, 24,25,26,27, 2,2,2,2)
+FMACS4 (12,13,14,15, 24,25,26,27, 6,6,6,6)
+
+FMACS4 (8,9,10,11, 28,29,30,31, 3,3,3,3)
+FMACS4 (12,13,14,15, 28,29,30,31, 7,7,7,7)
+
+
+vldmia.32 r1!, {s0-s7}
+vstmia.32 r2!, {s8-s15}
+
+FMULS4 (8,9,10,11, 16,17,18,19, 0,0,0,0)
+FMULS4 (12,13,14,15, 16,17,18,19, 4,4,4,4)
+
+FMACS4 (8,9,10,11, 20,21,22,23, 1,1,1,1)
+FMACS4 (12,13,14,15, 20,21,22,23, 5,5,5,5)
+
+FMACS4 (8,9,10,11, 24,25,26,27, 2,2,2,2)
+FMACS4 (12,13,14,15, 24,25,26,27, 6,6,6,6)
+
+FMACS4 (8,9,10,11, 28,29,30,31, 3,3,3,3)
+FMACS4 (12,13,14,15, 28,29,30,31, 7,7,7,7)
+
+vstmia.32 r2!, {s8-s15}
+
+cmp r0, r3
+bcc _MultiplyMatrixArray4x4_VFP_loop
+
+// VFP_VECTOR_LENGTH_ZERO
+
+vpop {d8-d15}
+bx lr
+
+
+.endif
+
+#endif \ No newline at end of file
diff --git a/Runtime/Math/PodMathTypes.h b/Runtime/Math/PodMathTypes.h
new file mode 100644
index 0000000..6ac55a5
--- /dev/null
+++ b/Runtime/Math/PodMathTypes.h
@@ -0,0 +1,24 @@
+#ifndef POD_MATH_TYPES_H_
+#define POD_MATH_TYPES_H_
+
+namespace pod
+{
+
+struct v2f
+{
+ float x, y;
+};
+
+struct v3f
+{
+ float x, y, z;
+};
+
+struct m44f
+{
+ float m[16];
+};
+
+}
+
+#endif
diff --git a/Runtime/Math/Polynomials.h b/Runtime/Math/Polynomials.h
new file mode 100644
index 0000000..b32b172
--- /dev/null
+++ b/Runtime/Math/Polynomials.h
@@ -0,0 +1,93 @@
+#ifndef POLYNOMIALS_H
+#define POLYNOMIALS_H
+
+
+// Returns the highest root for the cubic x^3 + px^2 + qx + r
+inline double CubicPolynomialRoot(const double p, const double q, const double r)
+{
+ double rcp3 = 1.0/3.0;
+ double half = 0.5;
+ double po3 = p*rcp3;
+ double po3_2 = po3*po3;
+ double po3_3 = po3_2*po3;
+ double b = po3_3 - po3*q*half + r*half;
+ double a = -po3_2 + q*rcp3;
+ double a3 = a*a*a;
+ double det = a3 + b*b;
+
+ if (det >= 0)
+ {
+ double r0 = sqrt(det) - b;
+ r0 = r0 > 0 ? pow(r0, rcp3) : -pow(-r0, rcp3);
+
+ return - po3 - a/r0 + r0;
+ }
+
+ double abs = sqrt(-a3);
+ double arg = acos(-b/abs);
+ abs = pow(abs, rcp3);
+ abs = abs - a/abs;
+ arg = -po3 + abs*cos(arg*rcp3);
+ return arg;
+}
+
+// Calculates all real roots of polynomial ax^2 + bx + c (and returns how many)
+inline int QuadraticPolynomialRootsGeneric(const float a, const float b, const float c, float& r0, float& r1)
+{
+ const float eps = 0.00001f;
+ if (Abs(a) < eps)
+ {
+ if (Abs(b) > eps)
+ {
+ r0 = -c/b;
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ float disc = b*b - 4*a*c;
+ if (disc < 0.0f)
+ return 0;
+
+ const float halfRcpA = 0.5f/a;
+ const float sqrtDisc = sqrt(disc);
+ r0 = (sqrtDisc-b)*halfRcpA;
+ r1 = (-sqrtDisc-b)*halfRcpA;
+ return 2;
+}
+
+// Calculates all the roots for the cubic ax^3 + bx^2 + cx + d. Max num roots is 3.
+inline int CubicPolynomialRootsGeneric(float* roots, const double a, const double b, const double c, const double d)
+{
+ int numRoots = 0;
+ if(Abs(a) >= 0.0001f)
+ {
+ const double p = b / a;
+ const double q = c / a;
+ const double r = d / a;
+ roots[0] = CubicPolynomialRoot(p, q, r);
+ numRoots++;
+
+ double la = a;
+ double lb = b + a * roots[0];
+ double lc = c + b*roots[0] + a*roots[0]*roots[0];
+ numRoots += QuadraticPolynomialRootsGeneric(la, lb, lc, roots[1], roots[2]);
+ }
+ else
+ {
+ numRoots += QuadraticPolynomialRootsGeneric(b, c, d, roots[0], roots[1]);
+ }
+
+ return numRoots;
+}
+
+// Specialized version of QuadraticPolynomialRootsGeneric that returns the largest root
+inline float QuadraticPolynomialRoot(const float a, const float b, const float c)
+{
+ float r0, r1;
+ QuadraticPolynomialRootsGeneric(a, b, c, r0, r1);
+ return r0;
+}
+
+#endif
diff --git a/Runtime/Math/Quaternion.cpp b/Runtime/Math/Quaternion.cpp
new file mode 100644
index 0000000..605cea2
--- /dev/null
+++ b/Runtime/Math/Quaternion.cpp
@@ -0,0 +1,449 @@
+#include "UnityPrefix.h"
+#include "Quaternion.h"
+#include <limits>
+/*
+Quaternionf Slerp(const Quaternionf& a, const Quaternionf& b, float time)
+{
+ #if DEBUGMODE
+ float debugLengthA = Magnitude (a);
+ float debugLengthB = Magnitude (b);
+ #endif
+ // ====================================================
+ // AART - Advanced Animation and Rendering Techniques
+ // ====================================================
+
+ float cosom = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w;
+
+ if ( (1 + cosom) > std::numeric_limits<float>::epsilon() )
+ {
+ float sp;
+ float sq;
+
+ if ( (1 - cosom) > std::numeric_limits<float>::epsilon() )
+ {
+ double omega = acos(cosom);
+ double sinom = 1.0 / sin(omega);
+
+ sp = (sin((1 - time) * omega) * sinom);
+ sq = (sin(time * omega) * sinom);
+ }
+ else
+ {
+ sp = 1 - time;
+ sq = time;
+ }
+
+ Quaternionf res = Quaternionf (
+ a.x*sp + b.x*sq,
+ a.y*sp + b.y*sq,
+ a.z*sp + b.z*sq,
+ a.w*sp + b.w*sq);
+ AssertIf (!CompareApproximately (SqrMagnitude (res), 1.0F) &&
+ CompareApproximately (SqrMagnitude (b), 1.0) &&
+ CompareApproximately (SqrMagnitude (a), 1.0));
+ return res;
+ }
+ else
+ {
+ float halfpi = pi / 2;
+ float sp = sin((1 - time) * halfpi);
+ float sq = sin(time * halfpi);
+
+ Quaternionf res = Quaternionf (
+ a.x*sp - a.y*sq,
+ a.y*sp + a.x*sq,
+ a.z*sp - a.w*sq,
+ a.z);
+
+ AssertIf (!CompareApproximately (SqrMagnitude (res), 1.0F) &&
+ CompareApproximately (SqrMagnitude (b), 1.0) &&
+ CompareApproximately (SqrMagnitude (a), 1.0));
+ return res;
+ }
+}
+*/
+
+Quaternionf Slerp( const Quaternionf& q1, const Quaternionf& q2, float t )
+{
+ // Quaternionf q3 = new Quaternionf();
+ float dot = Dot( q1, q2 );
+
+ // dot = cos(theta)
+ // if (dot < 0), q1 and q2 are more than 90 degrees apart,
+ // so we can invert one to reduce spinning
+ Quaternionf tmpQuat;
+ if (dot < 0.0f )
+ {
+ dot = -dot;
+ tmpQuat.Set( -q2.x,
+ -q2.y,
+ -q2.z,
+ -q2.w );
+ }
+ else
+ tmpQuat = q2;
+
+
+ if (dot < 0.95f )
+ {
+ float angle = acos(dot);
+ float sinadiv, sinat, sinaomt;
+ sinadiv = 1.0f/sin(angle);
+ sinat = sin(angle*t);
+ sinaomt = sin(angle*(1.0f-t));
+ tmpQuat.Set( (q1.x*sinaomt+tmpQuat.x*sinat)*sinadiv,
+ (q1.y*sinaomt+tmpQuat.y*sinat)*sinadiv,
+ (q1.z*sinaomt+tmpQuat.z*sinat)*sinadiv,
+ (q1.w*sinaomt+tmpQuat.w*sinat)*sinadiv );
+// AssertIf (!CompareApproximately (SqrMagnitude (tmpQuat), 1.0F));
+ return tmpQuat;
+
+ }
+
+ // if the angle is small, use linear interpolation
+
+ else
+ {
+ return Lerp(q1,tmpQuat,t);
+ }
+
+}
+
+float AngularDistance (const Quaternionf& lhs, const Quaternionf& rhs)
+{
+ float dot = Dot (lhs, rhs);
+ if (dot < 0.0f )
+ dot = -dot;
+ return acos (std::min (1.0F, dot)) * 2.0F;
+}
+/*
+Quaternionf EulerXYZToQuaternion (const Vector3f& someEulerAngles)
+{
+ float cX (cos (someEulerAngles.x / 2.0f));
+ float sX (sin (someEulerAngles.x / 2.0f));
+
+ float cY (cos (someEulerAngles.y / 2.0f));
+ float sY (sin (someEulerAngles.y / 2.0f));
+
+ float cZ (cos (someEulerAngles.z / 2.0f));
+ float sZ (sin (someEulerAngles.z / 2.0f));
+
+ Quaternionf qX (sX, 0.0F, 0.0F, cX);
+ Quaternionf qY (0.0F, sY, 0.0F, cY);
+ Quaternionf qZ (0.0F, 0.0F, sZ, cZ);
+
+ Quaternionf q = (qZ * qY) * qX;
+ AssertIf (!CompareApproximately (SqrMagnitude (q), 1.0F));
+ return q;
+}
+*/
+
+Quaternionf EulerToQuaternion (const Vector3f& someEulerAngles)
+{
+ float cX (cos (someEulerAngles.x / 2.0f));
+ float sX (sin (someEulerAngles.x / 2.0f));
+
+ float cY (cos (someEulerAngles.y / 2.0f));
+ float sY (sin (someEulerAngles.y / 2.0f));
+
+ float cZ (cos (someEulerAngles.z / 2.0f));
+ float sZ (sin (someEulerAngles.z / 2.0f));
+
+ Quaternionf qX (sX, 0.0F, 0.0F, cX);
+ Quaternionf qY (0.0F, sY, 0.0F, cY);
+ Quaternionf qZ (0.0F, 0.0F, sZ, cZ);
+
+ Quaternionf q = (qY * qX) * qZ;
+ AssertIf (!CompareApproximately (SqrMagnitude (q), 1.0F));
+ return q;
+}
+
+#if 1
+
+Vector3f QuaternionToEuler (const Quaternionf& quat)
+{
+ Matrix3x3f m;
+ Vector3f rot;
+ QuaternionToMatrix (quat, m);
+ MatrixToEuler (m, rot);
+ return rot;
+}
+
+#else
+
+// Version of QuaternionToEuler that prevents "snapping" on X when getting
+// close to gimbal lock. Noticeably changes behavior compared to version
+// above, so deactivated for now.
+
+Vector3f QuaternionToEuler(const Quaternionf& q)
+{
+ const float sqw = q.w * q.w;
+ const float sqx = q.x * q.x;
+ const float sqy = q.y * q.y;
+ const float sqz = q.z * q.z;
+
+ const float unit = sqx + sqy + sqz + sqw;
+ const float test = q.x * q.y + q.z * q.w;
+
+ float yaw = 0.0f;
+ float pitch = 0.0f;
+ float roll = 0.0f;
+
+ // North pole singularity
+ if (test > 0.499f * unit)
+ {
+ yaw = 2.0f * atan2 (q.x, q.w);
+ pitch = kPI * 0.5f;
+ roll = 0.0f;
+ }
+
+ // South pole singularity
+ else if (test < -0.499f * unit)
+ {
+ yaw = -2.0f * atan2 (q.x, q.w);
+ pitch = -kPI * 0.5f;
+ roll = 0.0f;
+ }
+
+ else
+ {
+ yaw = atan2 (2.0f * q.y * q.w - 2.0f * q.x * q.z , sqx - sqy - sqz + sqw);
+ pitch = asin (2.0f * test/unit);
+ roll = atan2 (2.0f * q.x * q.w - 2.0f * q.y * q.z , -sqx + sqy - sqz + sqw);
+ }
+
+ // Keep angles [0..360].
+ if (Sign (yaw) < 0.f)
+ yaw = Deg2Rad (360.f) + yaw;
+ if (Sign (pitch) < 0.f)
+ pitch = Deg2Rad (360.f) + pitch;
+ if (Sign (roll) < 0.f)
+ roll = Deg2Rad (360.f) + roll;
+
+ return Vector3f(roll, yaw, pitch);
+}
+
+#endif
+
+std::vector<Vector3f> GetEquivalentEulerAngles (const Quaternionf& quat)
+{
+ Matrix3x3f m;
+ Vector3f rot;
+
+ std::vector<Vector3f> euler_triples;
+
+ QuaternionToMatrix (quat, m);
+ MatrixToEuler (m, rot);
+
+ euler_triples.push_back(rot);
+
+ euler_triples.push_back(Vector3f(rot.x + 180.0f, -rot.y, rot.z + 180.0f));
+ euler_triples.push_back(Vector3f(rot.x - 180.0f, -rot.y, rot.z - 180.0f));
+ euler_triples.push_back(Vector3f(-rot.x, rot.y + 180.0f, -rot.z));
+ euler_triples.push_back(Vector3f(-rot.x, rot.y - 180.0f, -rot.z));
+
+ return euler_triples;
+}
+
+void QuaternionToMatrix (const Quaternionf& q, Matrix3x3f& m)
+{
+ // If q is guaranteed to be a unit quaternion, s will always
+ // be 1. In that case, this calculation can be optimized out.
+ #if DEBUGMODE
+ if (!CompareApproximately (SqrMagnitude (q), 1.0F, Vector3f::epsilon))
+ {
+ AssertString(Format("Quaternion To Matrix conversion failed because input Quaternion is invalid {%f, %f, %f, %f} l=%f", q.x, q.y, q.z, q.w, SqrMagnitude(q)));
+ }
+ #endif
+ //float norm = GetNorm (q);
+ //float s = (norm > 0.0) ? 2.0/norm : 0;
+
+ // Precalculate coordinate products
+ float x = q.x * 2.0F;
+ float y = q.y * 2.0F;
+ float z = q.z * 2.0F;
+ float xx = q.x * x;
+ float yy = q.y * y;
+ float zz = q.z * z;
+ float xy = q.x * y;
+ float xz = q.x * z;
+ float yz = q.y * z;
+ float wx = q.w * x;
+ float wy = q.w * y;
+ float wz = q.w * z;
+
+ // Calculate 3x3 matrix from orthonormal basis
+ m.m_Data[0] = 1.0f - (yy + zz);
+ m.m_Data[1] = xy + wz;
+ m.m_Data[2] = xz - wy;
+
+ m.m_Data[3] = xy - wz;
+ m.m_Data[4] = 1.0f - (xx + zz);
+ m.m_Data[5] = yz + wx;
+
+ m.m_Data[6] = xz + wy;
+ m.m_Data[7] = yz - wx;
+ m.m_Data[8] = 1.0f - (xx + yy);
+}
+
+
+void QuaternionToMatrix (const Quaternionf& q, Matrix4x4f& m)
+{
+ // If q is guaranteed to be a unit quaternion, s will always
+ // be 1. In that case, this calculation can be optimized out.
+ #if DEBUGMODE
+ if (!CompareApproximately (SqrMagnitude (q), 1.0F, Vector3f::epsilon))
+ {
+ AssertString(Format("Quaternion To Matrix conversion failed because input Quaternion is invalid {%f, %f, %f, %f} l=%f", q.x, q.y, q.z, q.w, SqrMagnitude(q)));
+ }
+ #endif
+
+ //float norm = GetNorm (q);
+ //float s = (norm > 0.0) ? 2.0/norm : 0;
+
+ // Precalculate coordinate products
+ float x = q.x * 2.0F;
+ float y = q.y * 2.0F;
+ float z = q.z * 2.0F;
+ float xx = q.x * x;
+ float yy = q.y * y;
+ float zz = q.z * z;
+ float xy = q.x * y;
+ float xz = q.x * z;
+ float yz = q.y * z;
+ float wx = q.w * x;
+ float wy = q.w * y;
+ float wz = q.w * z;
+
+ // Calculate 3x3 matrix from orthonormal basis
+ m.m_Data[0] = 1.0f - (yy + zz);
+ m.m_Data[1] = xy + wz;
+ m.m_Data[2] = xz - wy;
+ m.m_Data[3] = 0.0F;
+
+ m.m_Data[4] = xy - wz;
+ m.m_Data[5] = 1.0f - (xx + zz);
+ m.m_Data[6] = yz + wx;
+ m.m_Data[7] = 0.0F;
+
+ m.m_Data[8] = xz + wy;
+ m.m_Data[9] = yz - wx;
+ m.m_Data[10] = 1.0f - (xx + yy);
+ m.m_Data[11] = 0.0F;
+
+ m.m_Data[12] = 0.0F;
+ m.m_Data[13] = 0.0F;
+ m.m_Data[14] = 0.0F;
+ m.m_Data[15] = 1.0F;
+}
+
+void MatrixToQuaternion (const Matrix4x4f& m, Quaternionf& q) {
+ Matrix3x3f mat (
+ m.Get(0,0), m.Get(0,1), m.Get(0,2),
+ m.Get(1,0), m.Get(1,1), m.Get(1,2),
+ m.Get(2,0), m.Get(2,1), m.Get(2,2));
+
+ MatrixToQuaternion (mat, q);
+// mat.Get(0,0) = m.Get(0,0); mat.Get(0,1) = m.Get(0,1); mat.Get(0,2) = m.Get(0,2);
+// mat.Get(1,0) = m.Get(1,0); mat.Get(1,1) = m.Get(1,1); mat.Get(1,2) = m.Get(1,2);
+// mat.Get(2,0) = m.Get(2,0); mat.Get(2,1) = m.Get(2,1); mat.Get(2,2) = m.Get(2,2);
+}
+
+void MatrixToQuaternion (const Matrix3x3f& kRot, Quaternionf& q)
+{
+ // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
+ // article "Quaternionf Calculus and Fast Animation".
+ #if DEBUGMODE
+ float det = kRot.GetDeterminant ();
+ AssertIf (!CompareApproximately (det, 1.0F, .005f));
+ #endif
+ float fTrace = kRot.Get (0, 0) + kRot.Get (1, 1) + kRot.Get (2, 2);
+ float fRoot;
+
+ if ( fTrace > 0.0f )
+ {
+ // |w| > 1/2, may as well choose w > 1/2
+ fRoot = sqrt (fTrace + 1.0f); // 2w
+ q.w = 0.5f*fRoot;
+ fRoot = 0.5f/fRoot; // 1/(4w)
+ q.x = (kRot.Get (2, 1) - kRot.Get (1, 2))*fRoot;
+ q.y = (kRot.Get (0, 2) - kRot.Get (2, 0))*fRoot;
+ q.z = (kRot.Get (1, 0) - kRot.Get (0, 1))*fRoot;
+ }
+ else
+ {
+ // |w| <= 1/2
+ int s_iNext[3] = { 1, 2, 0 };
+ int i = 0;
+ if ( kRot.Get (1, 1) > kRot.Get (0, 0) )
+ i = 1;
+ if ( kRot.Get (2, 2) > kRot.Get (i, i) )
+ i = 2;
+ int j = s_iNext[i];
+ int k = s_iNext[j];
+
+ fRoot = sqrt (kRot.Get (i, i) - kRot.Get (j, j) - kRot.Get (k, k) + 1.0f);
+ float* apkQuat[3] = { &q.x, &q.y, &q.z };
+ AssertIf (fRoot < Vector3f::epsilon);
+ *apkQuat[i] = 0.5f*fRoot;
+ fRoot = 0.5f / fRoot;
+ q.w = (kRot.Get (k, j) - kRot.Get (j, k)) * fRoot;
+ *apkQuat[j] = (kRot.Get (j, i) + kRot.Get (i, j))*fRoot;
+ *apkQuat[k] = (kRot.Get (k, i) + kRot.Get (i, k))*fRoot;
+ }
+ q = Normalize (q);
+}
+
+bool LookRotationToQuaternion (const Vector3f& viewVec, const Vector3f& upVec, Quaternionf* res)
+{
+ Matrix3x3f m;
+ if (!LookRotationToMatrix (viewVec, upVec, &m))
+ return false;
+ MatrixToQuaternion (m, *res);
+ return true;
+}
+
+Quaternionf FromToQuaternionSafe (const Vector3f& lhs, const Vector3f& rhs)
+{
+ float lhsMag = Magnitude (lhs);
+ float rhsMag = Magnitude (rhs);
+ if (lhsMag < Vector3f::epsilon || rhsMag < Vector3f::epsilon)
+ return Quaternionf::identity ();
+ else
+ return FromToQuaternion (lhs / lhsMag, rhs / rhsMag);
+}
+
+Quaternionf FromToQuaternion (const Vector3f& from, const Vector3f& to)
+{
+ Matrix3x3f m;
+ m.SetFromToRotation (from, to);
+ Quaternionf q;
+ MatrixToQuaternion (m, q);
+ return q;
+/*
+ AssertIf (!CompareApproximately (SqrMagnitude (from), 1.0F));
+ AssertIf (!CompareApproximately (SqrMagnitude (to), 1.0F));
+ float dot = Dot (from, to);
+ // almost the same
+ if (dot > 1.0F - Vector3f::epsilon)
+ {
+ return Quaternionf::identity ();
+ }
+ else if (dot < -1.0F + Vector3f::epsilon)
+ {
+ Vector3f axis = OrthoNormalVector (from);
+ Quaternionf q;
+ AxisAngleToQuaternion (axis, pi, &q);
+ return q;
+ }
+ // normal case
+ else
+ {
+ Vector3f axis = Normalize (Cross (from, to));
+ Quaternionf q;
+ float angle = acos (dot);
+ AxisAngleToQuaternion (axis, angle, &q);
+ return q;
+ }
+*/
+}
diff --git a/Runtime/Math/Quaternion.h b/Runtime/Math/Quaternion.h
new file mode 100644
index 0000000..2cabe9c
--- /dev/null
+++ b/Runtime/Math/Quaternion.h
@@ -0,0 +1,405 @@
+#ifndef QUATERNION_H
+#define QUATERNION_H
+
+#include "Matrix3x3.h"
+#include "Matrix4x4.h"
+#include "Vector3.h"
+#include "FloatConversion.h"
+#include <algorithm>
+#include <vector>
+#include "Runtime/Modules/ExportModules.h"
+
+class Quaternionf
+{
+ public:
+
+ float x, y, z, w;
+
+ DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL (Quaternionf)
+ template<class TransferFunction> void Transfer (TransferFunction& transfer);
+
+ Quaternionf () {}
+ Quaternionf (float inX, float inY, float inZ, float inW);
+ explicit Quaternionf (const float* array) { x = array[0]; y = array[1]; z = array[2]; w = array[3]; }
+
+ // methods
+
+ const float* GetPtr ()const { return &x; }
+ float* GetPtr () { return &x; }
+
+ const float& operator [] (int i)const { return GetPtr ()[i]; }
+ float& operator [] (int i) { return GetPtr ()[i]; }
+
+ void Set (float inX, float inY, float inZ, float inW);
+ void Set (const Quaternionf& aQuat);
+ void Set (const float* array) { x = array[0]; y = array[1]; z = array[2]; w = array[3]; }
+
+ friend Quaternionf Normalize(const Quaternionf& q) { return q / Magnitude (q); }
+ friend Quaternionf NormalizeSafe(const Quaternionf& q);
+
+ friend Quaternionf Conjugate(const Quaternionf& q);
+ friend Quaternionf Inverse (const Quaternionf& q);
+
+ friend float SqrMagnitude (const Quaternionf& q);
+ friend float Magnitude (const Quaternionf& q);
+
+ bool operator == (const Quaternionf& q)const { return x == q.x && y == q.y && z == q.z && w == q.w; }
+ bool operator != (const Quaternionf& q)const { return x != q.x || y != q.y || z != q.z || w != q.w; }
+
+ Quaternionf& operator += (const Quaternionf& aQuat);
+ Quaternionf& operator -= (const Quaternionf& aQuat);
+ Quaternionf& operator *= (const float aScalar);
+ Quaternionf& operator *= (const Quaternionf& aQuat);
+ Quaternionf& operator /= (const float aScalar);
+
+ friend Quaternionf operator + (const Quaternionf& lhs, const Quaternionf& rhs)
+ {
+ Quaternionf q (lhs);
+ return q += rhs;
+ }
+
+ friend Quaternionf operator - (const Quaternionf& lhs, const Quaternionf& rhs)
+ {
+ Quaternionf t (lhs);
+ return t -= rhs;
+ }
+
+ Quaternionf operator - () const
+ {
+ return Quaternionf(-x, -y, -z, -w);
+ }
+
+ Quaternionf operator * (const float s) const
+ {
+ return Quaternionf (x*s, y*s, z*s, w*s);
+ }
+
+ friend Quaternionf operator * (const float s, const Quaternionf& q)
+ {
+ Quaternionf t (q);
+ return t *= s;
+ }
+
+ friend Quaternionf operator / (const Quaternionf& q, const float s)
+ {
+ Quaternionf t (q);
+ return t /= s;
+ }
+
+ inline friend Quaternionf operator * (const Quaternionf& lhs, const Quaternionf& rhs)
+ {
+ return Quaternionf (
+ lhs.w*rhs.x + lhs.x*rhs.w + lhs.y*rhs.z - lhs.z*rhs.y,
+ lhs.w*rhs.y + lhs.y*rhs.w + lhs.z*rhs.x - lhs.x*rhs.z,
+ lhs.w*rhs.z + lhs.z*rhs.w + lhs.x*rhs.y - lhs.y*rhs.x,
+ lhs.w*rhs.w - lhs.x*rhs.x - lhs.y*rhs.y - lhs.z*rhs.z);
+ }
+
+ static Quaternionf identity () { return Quaternionf (0.0F, 0.0F, 0.0F, 1.0F); }
+};
+
+bool CompareApproximately (const Quaternionf& q1, const Quaternionf& q2, float epsilon = Vector3f::epsilon);
+
+Quaternionf Lerp( const Quaternionf& q1, const Quaternionf& q2, float t );
+
+Quaternionf EXPORT_COREMODULE Slerp( const Quaternionf& q1, const Quaternionf& q2, float t );
+
+float Dot( const Quaternionf& q1, const Quaternionf& q2 );
+
+Vector3f EXPORT_COREMODULE QuaternionToEuler (const Quaternionf& quat);
+
+std::vector<Vector3f> GetEquivalentEulerAngles (const Quaternionf& quat);
+
+Quaternionf EulerToQuaternion (const Vector3f& euler);
+
+void EXPORT_COREMODULE QuaternionToMatrix (const Quaternionf& q, Matrix3x3f& m);
+
+void EXPORT_COREMODULE MatrixToQuaternion (const Matrix3x3f& m, Quaternionf& q);
+void EXPORT_COREMODULE MatrixToQuaternion (const Matrix4x4f& m, Quaternionf& q);
+
+void QuaternionToMatrix (const Quaternionf& q, Matrix4x4f& m);
+
+void QuaternionToAxisAngle (const Quaternionf& q, Vector3f* axis, float* targetAngle);
+
+Quaternionf AxisAngleToQuaternion (const Vector3f& axis, float angle);
+
+/// Generates a Right handed Quat from a look rotation. Returns if conversion was successful.
+bool LookRotationToQuaternion (const Vector3f& viewVec, const Vector3f& upVec, Quaternionf* res);
+
+
+inline Vector3f RotateVectorByQuat (const Quaternionf& lhs, const Vector3f& rhs)
+{
+// Matrix3x3f m;
+// QuaternionToMatrix (lhs, &m);
+// Vector3f restest = m.MultiplyVector3 (rhs);
+ float x = lhs.x * 2.0F;
+ float y = lhs.y * 2.0F;
+ float z = lhs.z * 2.0F;
+ float xx = lhs.x * x;
+ float yy = lhs.y * y;
+ float zz = lhs.z * z;
+ float xy = lhs.x * y;
+ float xz = lhs.x * z;
+ float yz = lhs.y * z;
+ float wx = lhs.w * x;
+ float wy = lhs.w * y;
+ float wz = lhs.w * z;
+
+ Vector3f res;
+ res.x = (1.0f - (yy + zz)) * rhs.x + (xy - wz) * rhs.y + (xz + wy) * rhs.z;
+ res.y = (xy + wz) * rhs.x + (1.0f - (xx + zz)) * rhs.y + (yz - wx) * rhs.z;
+ res.z = (xz - wy) * rhs.x + (yz + wx) * rhs.y + (1.0f - (xx + yy)) * rhs.z;
+
+// AssertIf (!CompareApproximately (restest, res));
+ return res;
+}
+
+// operator overloads
+// inlines
+
+inline Quaternionf::Quaternionf(float inX, float inY, float inZ, float inW)
+{
+ x = inX;
+ y = inY;
+ z = inZ;
+ w = inW;
+}
+
+template<class TransferFunction> inline
+void Quaternionf::Transfer (TransferFunction& transfer)
+{
+ transfer.AddMetaFlag (kTransferUsingFlowMappingStyle);
+ TRANSFER (x);
+ TRANSFER (y);
+ TRANSFER (z);
+ TRANSFER (w);
+}
+
+inline void Quaternionf::Set (float inX, float inY, float inZ, float inW)
+{
+ x = inX;
+ y = inY;
+ z = inZ;
+ w = inW;
+}
+
+inline void Quaternionf::Set (const Quaternionf& aQuat )
+{
+ x = aQuat.x;
+ y = aQuat.y;
+ z = aQuat.z;
+ w = aQuat.w;
+}
+
+inline Quaternionf Conjugate (const Quaternionf& q)
+{
+ return Quaternionf (-q.x, -q.y, -q.z, q.w);
+}
+
+inline Quaternionf Inverse (const Quaternionf& q)
+{
+ // Is it necessary to divide by SqrMagnitude???
+ Quaternionf res = Conjugate (q);
+ return res;
+}
+
+inline float Magnitude(const Quaternionf& q)
+{
+ return SqrtImpl (SqrMagnitude (q));
+}
+
+inline float SqrMagnitude(const Quaternionf& q)
+{
+ return Dot (q, q);
+}
+
+inline Quaternionf& Quaternionf::operator+= (const Quaternionf& aQuat)
+{
+ x += aQuat.x;
+ y += aQuat.y;
+ z += aQuat.z;
+ w += aQuat.w;
+ return *this;
+}
+
+inline Quaternionf& Quaternionf::operator-= (const Quaternionf& aQuat)
+{
+ x -= aQuat.x;
+ y -= aQuat.y;
+ z -= aQuat.z;
+ w -= aQuat.w;
+ return *this;
+}
+
+inline Quaternionf& Quaternionf::operator *= (float aScalar)
+{
+ x *= aScalar;
+ y *= aScalar;
+ z *= aScalar;
+ w *= aScalar;
+ return *this;
+}
+
+inline Quaternionf& Quaternionf::operator /= (const float aScalar)
+{
+ AssertIf (CompareApproximately (aScalar, 0.0F));
+ x /= aScalar;
+ y /= aScalar;
+ z /= aScalar;
+ w /= aScalar;
+ return *this;
+}
+
+inline Quaternionf& Quaternionf::operator *= (const Quaternionf& rhs)
+{
+ float tempx = w*rhs.x + x*rhs.w + y*rhs.z - z*rhs.y;
+ float tempy = w*rhs.y + y*rhs.w + z*rhs.x - x*rhs.z;
+ float tempz = w*rhs.z + z*rhs.w + x*rhs.y - y*rhs.x;
+ float tempw = w*rhs.w - x*rhs.x - y*rhs.y - z*rhs.z;
+ x = tempx; y = tempy; z = tempz; w = tempw;
+ return *this;
+}
+
+inline Quaternionf Lerp( const Quaternionf& q1, const Quaternionf& q2, float t )
+{
+ Quaternionf tmpQuat;
+ // if (dot < 0), q1 and q2 are more than 360 deg apart.
+ // The problem is that quaternions are 720deg of freedom.
+ // so we - all components when lerping
+ if (Dot (q1, q2) < 0.0F)
+ {
+ tmpQuat.Set(q1.x + t * (-q2.x - q1.x),
+ q1.y + t * (-q2.y - q1.y),
+ q1.z + t * (-q2.z - q1.z),
+ q1.w + t * (-q2.w - q1.w));
+ }
+ else
+ {
+ tmpQuat.Set(q1.x + t * (q2.x - q1.x),
+ q1.y + t * (q2.y - q1.y),
+ q1.z + t * (q2.z - q1.z),
+ q1.w + t * (q2.w - q1.w));
+ }
+ return Normalize (tmpQuat);
+}
+
+inline float Dot( const Quaternionf& q1, const Quaternionf& q2 )
+{
+ return (q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w);
+}
+
+float AngularDistance (const Quaternionf& lhs, const Quaternionf& rhs);
+
+
+inline void QuaternionToAxisAngle (const Quaternionf& q, Vector3f* axis, float* targetAngle)
+{
+ AssertIf (! CompareApproximately(SqrMagnitude (q), 1.0F));
+ *targetAngle = 2.0f* acos(q.w);
+ if (CompareApproximately (*targetAngle, 0.0F))
+ {
+ *axis = Vector3f::xAxis;
+ return;
+ }
+
+ float div = 1.0f / sqrt(1.0f - Sqr (q.w));
+ axis->Set( q.x*div, q.y*div, q.z*div );
+}
+
+inline Quaternionf AxisAngleToQuaternion (const Vector3f& axis, float angle)
+{
+ Quaternionf q;
+ AssertIf (!CompareApproximately (SqrMagnitude (axis), 1.0F));
+ float halfAngle = angle * 0.5F;
+ float s = sin (halfAngle);
+
+ q.w = cos (halfAngle);
+ q.x = s * axis.x;
+ q.y = s * axis.y;
+ q.z = s * axis.z;
+ return q;
+}
+
+inline Quaternionf AngularVelocityToQuaternion (const Vector3f& axis, float deltaTime)
+{
+ float w = Magnitude(axis);
+ if (w > Vector3f::epsilon)
+ {
+ float v = deltaTime * w * 0.5f;
+ float q = cos(v);
+ float s = sin(v) / w;
+
+ Quaternionf integrated;
+ integrated.w = q;
+ integrated.x = s * axis.x;
+ integrated.y = s * axis.y;
+ integrated.z = s * axis.z;
+
+ return NormalizeSafe(integrated);
+ }
+ else
+ {
+ return Quaternionf::identity();
+ }
+}
+
+inline Quaternionf AxisAngleToQuaternionSafe (const Vector3f& axis, float angle)
+{
+ Quaternionf q;
+ float mag = Magnitude (axis);
+ if (mag > 0.000001F)
+ {
+ float halfAngle = angle * 0.5F;
+
+ q.w = cos (halfAngle);
+
+ float s = sin (halfAngle) / mag;
+ q.x = s * axis.x;
+ q.y = s * axis.y;
+ q.z = s * axis.z;
+ return q;
+ }
+ else
+ {
+ return Quaternionf::identity ();
+ }
+}
+
+// Generates a quaternion that rotates lhs into rhs.
+Quaternionf FromToQuaternionSafe (const Vector3f& lhs, const Vector3f& rhs);
+// from and to are assumed to be normalized
+Quaternionf FromToQuaternion (const Vector3f& from, const Vector3f& to);
+
+
+inline bool CompareApproximately (const Quaternionf& q1, const Quaternionf& q2, float epsilon)
+{
+ //return SqrMagnitude (q1 - q2) < epsilon * epsilon;
+ return (SqrMagnitude (q1 - q2) < epsilon * epsilon) || (SqrMagnitude (q1 + q2) < epsilon * epsilon);
+ //return Abs (Dot (q1, q2)) > (1 - epsilon * epsilon);
+}
+
+inline Quaternionf NormalizeSafe (const Quaternionf& q)
+{
+ float mag = Magnitude (q);
+ if (mag < Vector3f::epsilon)
+ return Quaternionf::identity ();
+ else
+ return q / mag;
+}
+
+inline Quaternionf NormalizeFastEpsilonZero (const Quaternionf& q)
+{
+ float m = SqrMagnitude (q);
+ if (m < Vector3f::epsilon)
+ return Quaternionf(0.0F, 0.0F, 0.0F, 0.0F);
+ else
+ return q * FastInvSqrt(m);
+}
+
+
+inline bool IsFinite (const Quaternionf& f)
+{
+ return IsFinite(f.x) & IsFinite(f.y) & IsFinite(f.z) & IsFinite(f.w);
+}
+
+
+#endif
diff --git a/Runtime/Math/Random/Random.h b/Runtime/Math/Random/Random.h
new file mode 100644
index 0000000..ccd8c62
--- /dev/null
+++ b/Runtime/Math/Random/Random.h
@@ -0,0 +1,184 @@
+#ifndef RANDOM_H
+#define RANDOM_H
+
+#include "rand.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Math/FloatConversion.h"
+
+inline float RangedRandom (Rand& r, float min, float max)
+{
+ float t = r.GetFloat ();
+ t = min * t + (1.0F - t) * max;
+ return t;
+}
+
+inline float Random01 (Rand& r)
+{
+ return r.GetFloat ();
+}
+
+inline int RangedRandom (Rand& r, int min, int max)
+{
+ int dif;
+ if (min < max)
+ {
+ dif = max - min;
+ int t = r.Get () % dif;
+ t += min;
+ return t;
+ }
+ else if (min > max)
+ {
+ dif = min - max;
+ int t = r.Get () % dif;
+ t = min - t;
+ return t;
+ }
+ else
+ {
+ return min;
+ }
+}
+
+inline Vector3f RandomUnitVector (Rand& rand)
+{
+ float z = RangedRandom (rand, -1.0f, 1.0f);
+ float a = RangedRandom (rand, 0.0f, 2.0F * kPI);
+
+ float r = sqrt (1.0f - z*z);
+
+ float x = r * cos (a);
+ float y = r * sin (a);
+
+ return Vector3f (x, y, z);
+}
+
+inline Vector2f RandomUnitVector2 (Rand& rand)
+{
+ float a = RangedRandom (rand, 0.0f, 2.0F * kPI);
+
+ float x = cos (a);
+ float y = sin (a);
+
+ return Vector2f (x, y);
+}
+
+
+inline Quaternionf RandomQuaternion (Rand& rand)
+{
+ Quaternionf q;
+ q.x = RangedRandom (rand, -1.0f, 1.0f);
+ q.y = RangedRandom (rand, -1.0f, 1.0f);
+ q.z = RangedRandom (rand, -1.0f, 1.0f);
+ q.w = RangedRandom (rand, -1.0f, 1.0f);
+ q = NormalizeSafe (q);
+ if (Dot (q, Quaternionf::identity ()) < 0.0f)
+ return -q;
+ else
+ return q;
+}
+
+inline Quaternionf RandomQuaternionUniformDistribution (Rand& rand)
+{
+ const float two_pi = 2.0F * kPI;
+
+ // Employs Hopf fibration to uniformly distribute quaternions
+ float u1 = RangedRandom( rand, 0.0f, 1.0f );
+ float theta = RangedRandom( rand, 0.0f, two_pi );
+ float rho = RangedRandom( rand, 0.0f, two_pi );
+
+ float i = sqrt( 1.0f - u1 );
+ float j = sqrt( u1 );
+
+ // We do not need to normalize the generated quaternion, because the probability density corresponds to the Haar measure.
+ // This means that a random rotation is obtained by picking a point at random on S^3, and forming the unit quaternion.
+ Quaternionf q( i * sin(theta), i * cos(theta), j * sin(rho), j * cos(rho) );
+
+ if (Dot (q, Quaternionf::identity ()) < 0.0f)
+ return -q;
+ else
+ return q;
+}
+
+
+inline Vector3f RandomPointInsideCube (Rand& r, const Vector3f& extents)
+{
+ return Vector3f ( RangedRandom (r, -extents.x, extents.x),
+ RangedRandom (r, -extents.y, extents.y),
+ RangedRandom (r, -extents.z, extents.z));
+}
+
+inline Vector3f RandomPointBetweenCubes (Rand& r, const Vector3f& min, const Vector3f& max)
+{
+ Vector3f v;
+ int i;
+ for (i=0;i<3;i++)
+ {
+ float x = r.GetFloat () * 2.0F - 1.0F;
+ if (x > 0.0f)
+ v[i] = min[i] + x * (max[i] - min[i]);
+ else
+ v[i] = -min[i] + x * (max[i] - min[i]);
+ }
+ return v;
+}
+
+inline Vector3f RandomPointInsideUnitSphere (Rand& r)
+{
+ Vector3f v = RandomUnitVector (r);
+ v *= pow (Random01 (r), 1.0F / 3.0F);
+ return v;
+}
+
+inline Vector3f RandomPointInsideEllipsoid (Rand& r, const Vector3f& extents)
+{
+ return Scale (RandomPointInsideUnitSphere (r), extents);
+}
+
+inline Vector3f RandomPointBetweenSphere (Rand& r, float minRadius, float maxRadius)
+{
+ Vector3f v = RandomUnitVector (r);
+ // As the volume of the sphere increases (x^3) over an interval we have to increase range as well with x^(1/3)
+ float range = pow (RangedRandom (r, 0.0F, 1.0F), 1.0F / 3.0F);
+ return v * (minRadius + (maxRadius - minRadius) * range);
+}
+
+inline Vector2f RandomPointInsideUnitCircle (Rand& r)
+{
+ Vector2f v = RandomUnitVector2 (r);
+ // As the volume of the sphere increases (x^3) over an interval we have to increase range as well with x^(1/3)
+ v *= pow (RangedRandom (r, 0.0F, 1.0F), 1.0F / 2.0F);
+ return v;
+}
+
+inline Vector3f RandomPointBetweenEllipsoid (Rand& r, const Vector3f& maxExtents, float minRange)
+{
+ Vector3f v = Scale (RandomUnitVector (r), maxExtents);
+ // As the volume of the sphere increases (x^3) over an interval we have to increase range as well with x^(1/3)
+ float range = pow (RangedRandom (r, minRange, 1.0F), 1.0F / 3.0F);
+ return v * range;
+}
+
+/// Builds a random Barycentric coordinate which can be used to generate random points on a triangle:
+/// Vector3f point = v0 * barycentric.x + v1 * barycentric.y + v2 * barycentric.z;
+inline Vector3f RandomBarycentricCoord (Rand& rand)
+{
+// Was told that this leads to bad distribution because of the 1.0F - s
+// float s = gRand.GetFloat ();
+// float t = RangedRandom (gRand, 0.0F, 1.0F - s);
+// float r = (1.0F - s - t);
+// Vector3f positionOnMesh = r * vertices[face.v1] + s * vertices[face.v2] + t * vertices[face.v3];
+// return positionOnMesh;
+ float u = rand.GetFloat ();
+ float v = rand.GetFloat ();
+ if (u + v > 1.0F)
+ {
+ u = 1.0F - u;
+ v = 1.0F - v;
+ }
+ float w = 1.0F - u - v;
+ return Vector3f (u, v, w);
+}
+
+#endif
diff --git a/Runtime/Math/Random/rand.h b/Runtime/Math/Random/rand.h
new file mode 100644
index 0000000..1971ab4
--- /dev/null
+++ b/Runtime/Math/Random/rand.h
@@ -0,0 +1,81 @@
+#ifndef RAND_H
+#define RAND_H
+
+/*
+Some random generator timings:
+MacBook Pro w/ Core 2 Duo 2.4GHz. Times are for gcc 4.0.1 (OS X 10.6.2) / VS2008 SP1 (Win XP SP3),
+in milliseconds for this loop (4915200 calls):
+
+ for (int j = 0; j < 100; ++j)
+ for (int i = 0; i < 128*128*3; ++i)
+ data[i] = (rnd.get() & 0x3) << 6;
+
+ gcc vs2008 Size
+C's rand(): 57.0 109.3 ms 1
+Mersenne Twister: 56.0 37.4 ms 2500
+Unity 2.x LCG: 11.1 9.2 ms 4
+Xorshift 128: 15.0 17.8 ms 16
+Xorshift 32: 20.6 10.7 ms 4
+WELL 512: 43.6 55.1 ms 68
+*/
+
+
+// Xorshift 128 implementation
+// Xorshift paper: http://www.jstatsoft.org/v08/i14/paper
+// Wikipedia: http://en.wikipedia.org/wiki/Xorshift
+class Rand {
+public:
+
+ Rand (UInt32 seed = 0)
+ {
+ SetSeed (seed);
+ }
+
+ UInt32 Get ()
+ {
+ UInt32 t;
+ t = x ^ (x << 11);
+ x = y; y = z; z = w;
+ return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
+ }
+
+ inline static float GetFloatFromInt (UInt32 value)
+ {
+ // take 23 bits of integer, and divide by 2^23-1
+ return float(value & 0x007FFFFF) * (1.0f / 8388607.0f);
+ }
+
+ inline static UInt8 GetByteFromInt (UInt32 value)
+ {
+ // take the most significant byte from the 23-bit value
+ return UInt8(value >> (23 - 8));
+ }
+
+ // random number between 0.0 and 1.0
+ float GetFloat ()
+ {
+ return GetFloatFromInt (Get ());
+ }
+
+ // random number between -1.0 and 1.0
+ float GetSignedFloat ()
+ {
+ return GetFloat() * 2.0f - 1.0f;
+ }
+
+ void SetSeed (UInt32 seed)
+ {
+ x = seed;
+ y = x * 1812433253U + 1;
+ z = y * 1812433253U + 1;
+ w = z * 1812433253U + 1;
+ }
+
+ UInt32 GetSeed () const { return x; }
+
+private:
+ UInt32 x, y, z, w;
+};
+
+
+#endif
diff --git a/Runtime/Math/Rect.h b/Runtime/Math/Rect.h
new file mode 100644
index 0000000..4801988
--- /dev/null
+++ b/Runtime/Math/Rect.h
@@ -0,0 +1,184 @@
+#ifndef RECT_H
+#define RECT_H
+
+#include "Vector2.h"
+#include "Runtime/Modules/ExportModules.h"
+
+/// A rectangle.
+template <typename T>
+class EXPORT_COREMODULE RectT
+{
+public:
+ typedef RectT<T> RectType;
+ typedef float BaseType;
+
+ T x; ///< Rectangle x coordinate.
+ T y; ///< Rectangle y coordinate.
+ T width; ///< Rectangle width.
+ T height; ///< Rectangle height.
+
+ //DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (Rectf)
+ inline static const char* GetTypeString ();
+ inline static bool IsAnimationChannel () { return false; }
+ inline static bool MightContainPPtr () { return false; }
+ inline static bool AllowTransferOptimization () { return true; }
+ template <class TransferFunction>
+ void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER (x);
+ TRANSFER (y);
+ TRANSFER (width);
+ TRANSFER (height);
+ }
+
+ /// Create a empty rectangle.
+ RectT ()
+ {
+ Reset ();
+ }
+
+ /// Create a new rectangle.
+ RectT (T inX, T inY, T iWidth, T iHeight)
+ {
+ x = inX; width = iWidth;
+ y = inY; height = iHeight;
+ }
+
+ T GetRight() const { return x + width; }
+ T GetBottom() const { return y + height; }
+ void SetLeft(T l) { T oldXMax = GetXMax(); x = l; width = oldXMax - x; }
+ void SetTop(T t) { T oldYMax = GetYMax(); y = t; height = oldYMax - y; }
+ void SetRight(T r) { width = r - x; }
+ void SetBottom(T b) { height = b - y; }
+
+
+ T GetXMax() const { return x + width; }
+ T GetYMax() const { return y + height; }
+
+ /// Return true if rectangle is empty.
+ inline bool IsEmpty () const { return width <= 0 || height <= 0; }
+
+ inline void SetPosition(const Vector2f& position) { x = position.x; y = position.y; }
+ inline Vector2f GetPosition() const { return Vector2f(x, y); }
+
+ inline void SetSize(const Vector2f& size) { width = size.x; height = size.y; }
+ inline Vector2f GetSize() const { return Vector2f(width, height); }
+ /// Resets the rectangle
+ inline void Reset() { x = y = width = height = 0; }
+
+ /// Sets the rectangle
+ inline void Set(T inX, T inY, T iWidth, T iHeight)
+ {
+ x = inX; width = iWidth;
+ y = inY; height = iHeight;
+ }
+
+ inline void Scale (T dx, T dy) { x *= dx; width *= dx; y *= dy; height *= dy;}
+
+ /// Set Center position of rectangle (size stays the same)
+ void SetCenterPos (T cx, T cy) { x = cx - width / 2; y = cy - height / 2; }
+ Vector2f GetCenterPos() const { return Vector2f(x + (BaseType)width / 2, y + (BaseType)height / 2); }
+
+ /// Ensure this is inside the rect r.
+ void Clamp (const RectType &r)
+ {
+ T x2 = x + width;
+ T y2 = y + height;
+ T rx2 = r.x + r.width;
+ T ry2 = r.y + r.height;
+
+ if (x < r.x) x = r.x;
+ if (x2 > rx2) x2 = rx2;
+ if (y < r.y) y = r.y;
+ if (y2 > ry2) y2 = ry2;
+
+ width = x2 - x;
+ if (width < 0) width = 0;
+
+ height = y2 - y;
+ if (height < 0) height = 0;
+ }
+
+ /// Move rectangle by deltaX, deltaY.
+ inline void Move (T dX, T dY) { x += dX; y += dY; }
+
+ /// Return the width of rectangle.
+ inline T Width () const { return width; }
+
+ /// Return the height of rectangle.
+ inline T Height () const { return height; }
+
+ /// Return true if a point lies within rectangle bounds.
+ inline bool Contains (T px, T py) const { return (px >= x) && (px < x + width) && (py >= y) && (py < y + height); }
+ inline bool Contains (const Vector2f& p) const { return Contains(p.x, p.y); }
+ /// Return true if a relative point lies within rectangle bounds.
+ inline bool ContainsRel (T x, T y) const
+ { return (x >= 0) && (x < Width ()) && (y >= 0) && (y < Height ()); }
+
+ inline bool Intersects(const RectType& r) const
+ {
+ // Rects are disjoint if there's at least one separating axis
+ bool disjoint = x + width < r.x;
+ disjoint |= r.x + r.width < x;
+ disjoint |= y + height < r.y;
+ disjoint |= r.y + r.height < y;
+ return !disjoint;
+ }
+
+ /// Normalize a rectangle such that xmin <= xmax and ymin <= ymax.
+ inline void Normalize ()
+ {
+ width = std::max<T>(width, 0);
+ height = std::max<T>(height, 0);
+ }
+
+ bool operator == (const RectType& r)const { return x == r.x && y == r.y && width == r.width && height == r.height; }
+ bool operator != (const RectType& r)const { return x != r.x || y != r.y || width != r.width || height != r.height; }
+};
+
+typedef RectT<float> Rectf;
+typedef RectT<int> RectInt;
+
+template<> inline const char* Rectf::GetTypeString () { return "Rectf"; }
+template<> inline const char* RectInt::GetTypeString () { return "RectInt"; }
+
+inline bool CompareApproximately (const Rectf& lhs, const Rectf& rhs)
+{
+ return CompareApproximately (lhs.x, rhs.x) && CompareApproximately (lhs.y, rhs.y) &&
+ CompareApproximately (lhs.width, rhs.width) && CompareApproximately (lhs.height, rhs.height);
+}
+
+/// Make a rect with width & height
+template<typename T>
+inline RectT<T> MinMaxRect (T minx, T miny, T maxx, T maxy) { return RectT<T> (minx, miny, maxx - minx, maxy - miny); }
+
+// RectT<float> specialization
+template<>
+inline bool Rectf::IsEmpty () const { return width <= 0.00001F || height <= 0.00001F; }
+
+template<>
+template<class TransferFunction> inline
+void Rectf::Transfer (TransferFunction& transfer)
+{
+ transfer.SetVersion(2);
+
+ TRANSFER (x);
+ TRANSFER (y);
+ TRANSFER (width);
+ TRANSFER (height);
+
+ #if UNITY_EDITOR
+ if (transfer.IsOldVersion(1))
+ {
+ float xmax=0.0F, ymax=0.0F, ymin=0.0F, xmin=0.0F;
+ TRANSFER (xmin);
+ TRANSFER (ymin);
+ TRANSFER (xmax);
+ TRANSFER (ymax);
+
+ *this = MinMaxRect(xmin, ymin, xmax, ymax);
+ }
+ #endif
+}
+
+#endif
diff --git a/Runtime/Math/Simd/Matrix4x4Simd.h b/Runtime/Math/Simd/Matrix4x4Simd.h
new file mode 100644
index 0000000..b38890d
--- /dev/null
+++ b/Runtime/Math/Simd/Matrix4x4Simd.h
@@ -0,0 +1,175 @@
+#ifndef MATRIX4X4SIMD_H
+#define MATRIX4X4SIMD_H
+
+static void MultiplyMatrices4x4NATIVE (const Simd128& m10, const Simd128& m11, const Simd128& m12, const Simd128& m13, const Simd128& m20, const Simd128& m21, const Simd128& m22, const Simd128& m23, Simd128& rm0, Simd128& rm1, Simd128& rm2, Simd128& rm3)
+{
+ const Simd128 m20_X = V4Splat( m20, 0 );
+ const Simd128 m21_X = V4Splat( m21, 0 );
+ const Simd128 m22_X = V4Splat( m22, 0 );
+ const Simd128 m23_X = V4Splat( m23, 0 );
+ const Simd128 rm0_0 = V4Mul( m20_X, m10 );
+ const Simd128 rm1_0 = V4Mul( m21_X, m10 );
+ const Simd128 rm2_0 = V4Mul( m22_X, m10 );
+ const Simd128 rm3_0 = V4Mul( m23_X, m10 );
+ const Simd128 m20_Y = V4Splat(m20, 1 );
+ const Simd128 m21_Y = V4Splat(m21, 1 );
+ const Simd128 m22_Y = V4Splat(m22, 1 );
+ const Simd128 m23_Y = V4Splat(m23, 1 );
+ const Simd128 rm0_1 = V4MulAdd( m20_Y, m11, rm0_0 );
+ const Simd128 rm1_1 = V4MulAdd( m21_Y, m11, rm1_0 );
+ const Simd128 rm2_1 = V4MulAdd( m22_Y, m11, rm2_0 );
+ const Simd128 rm3_1 = V4MulAdd( m23_Y, m11, rm3_0 );
+ const Simd128 m20_Z = V4Splat(m20, 2 );
+ const Simd128 m21_Z = V4Splat(m21, 2 );
+ const Simd128 m22_Z = V4Splat(m22, 2 );
+ const Simd128 m23_Z = V4Splat(m23, 2 );
+ const Simd128 rm0_2 = V4MulAdd( m20_Z, m12, rm0_1 );
+ const Simd128 rm1_2 = V4MulAdd( m21_Z, m12, rm1_1 );
+ const Simd128 rm2_2 = V4MulAdd( m22_Z, m12, rm2_1 );
+ const Simd128 rm3_2 = V4MulAdd( m23_Z, m12, rm3_1 );
+ const Simd128 m20_W = V4Splat(m20, 3 );
+ const Simd128 m21_W = V4Splat(m21, 3 );
+ const Simd128 m22_W = V4Splat(m22, 3 );
+ const Simd128 m23_W = V4Splat(m23, 3 );
+ rm0 = V4MulAdd( m20_W, m13 , rm0_2 );
+ rm1 = V4MulAdd( m21_W, m13 , rm1_2 );
+ rm2 = V4MulAdd( m22_W, m13 , rm2_2 );
+ rm3 = V4MulAdd( m23_W, m13 , rm3_2 );
+}
+
+static void TransformPoint3NATIVE(const Simd128& m0, const Simd128& m1, const Simd128& m2, const Simd128& m3, const Simd128& vin, Simd128& vout)
+{
+ const Simd128 v0 = V4Splat(vin, 0);
+ const Simd128 v1 = V4Splat(vin, 1);
+ const Simd128 v2 = V4Splat(vin, 2);
+ Simd128 vtemp = V4MulAdd(m0, v0, m3);
+ vtemp = V4MulAdd(m1, v1, vtemp);
+ vout = V4MulAdd(m2, v2, vtemp);
+}
+
+static void TransformVector3NATIVE(const Simd128& m0, const Simd128& m1, const Simd128& m2, const Simd128& m3, const Simd128& vin, Simd128& vout)
+{
+ const Simd128 v0 = V4Splat(vin, 0);
+ const Simd128 v1 = V4Splat(vin, 1);
+ const Simd128 v2 = V4Splat(vin, 2);
+ Simd128 vtemp = V4Mul(m0, v0);
+ vtemp = V4MulAdd(m1, v1, vtemp);
+ vout = V4MulAdd(m2, v2, vtemp);
+}
+
+static void DECLARE_SIMD_FUNC(MultiplyMatrices4x4) (const Matrix4x4f* __restrict lhs, const Matrix4x4f* __restrict rhs, Matrix4x4f* __restrict res)
+{
+ Assert (lhs != rhs && lhs != res && rhs != res);
+ float* m = res->m_Data;
+ const float* m1 = lhs->m_Data;
+ const float* m2 = rhs->m_Data;
+ Simd128 rm0, rm1, rm2, rm3;
+
+ Prefetch((const char*)m1);
+ Prefetch((const char*)m2);
+
+ const Simd128 m10 = V4LoadUnaligned( m1, 0x0 );
+ const Simd128 m11 = V4LoadUnaligned( m1, 0x4 );
+ const Simd128 m12 = V4LoadUnaligned( m1, 0x8 );
+ const Simd128 m13 = V4LoadUnaligned( m1, 0xC );
+
+ const Simd128 m20 = V4LoadUnaligned( m2, 0x0 );
+ const Simd128 m21 = V4LoadUnaligned( m2, 0x4 );
+ const Simd128 m22 = V4LoadUnaligned( m2, 0x8 );
+ const Simd128 m23 = V4LoadUnaligned( m2, 0xC );
+
+ MultiplyMatrices4x4NATIVE(m10, m11, m12, m13, m20, m21, m22, m23, rm0, rm1, rm2, rm3);
+
+ V4StoreUnaligned(rm0, m, 0x0 );
+ V4StoreUnaligned(rm1, m, 0x4 );
+ V4StoreUnaligned(rm2, m, 0x8 );
+ V4StoreUnaligned(rm3, m, 0xC );
+}
+
+static void DECLARE_SIMD_FUNC(CopyMatrix) ( const float* __restrict lhs, float* __restrict res)
+{
+ Simd128 r0 = V4LoadUnaligned(lhs, 0x0);
+ Simd128 r1 = V4LoadUnaligned(lhs, 0x4);
+ Simd128 r2 = V4LoadUnaligned(lhs, 0x8);
+ Simd128 r3 = V4LoadUnaligned(lhs, 0xC);
+ V4StoreUnaligned(r0, res, 0x0);
+ V4StoreUnaligned(r1, res, 0x4);
+ V4StoreUnaligned(r2, res, 0x8);
+ V4StoreUnaligned(r3, res, 0xC);
+}
+
+
+static void DECLARE_SIMD_FUNC(TransposeMatrix4x4) (const Matrix4x4f* __restrict lhs, Matrix4x4f* __restrict res)
+{
+ const float* m0 = lhs->m_Data;
+ float* m = res->m_Data;
+
+ const Simd128 m00 = V4LoadUnaligned(m0, 0x0);
+ const Simd128 m01 = V4LoadUnaligned(m0, 0x4);
+ const Simd128 m02 = V4LoadUnaligned(m0, 0x8);
+ const Simd128 m03 = V4LoadUnaligned(m0, 0xC);
+
+ const Simd128 xxyy1 = V4MergeH(m00, m02);
+ const Simd128 zzww1 = V4MergeL(m00, m02);
+ const Simd128 xxyy2 = V4MergeH(m01, m03);
+ const Simd128 zzww2 = V4MergeL(m01, m03);
+ const Simd128 t00 = V4MergeH(xxyy1,xxyy2);
+ const Simd128 t01 = V4MergeL(xxyy1,xxyy2);
+ const Simd128 t02 = V4MergeH(zzww1,zzww2);
+ const Simd128 t03 = V4MergeL(zzww1,zzww2);
+
+ V4StoreUnaligned(t00, m, 0x0);
+ V4StoreUnaligned(t01, m, 0x4);
+ V4StoreUnaligned(t02, m, 0x8);
+ V4StoreUnaligned(t03, m, 0xC);
+}
+
+static void DECLARE_SIMD_FUNC(MultiplyMatrixArrayWithBase4x4) (const Matrix4x4f* __restrict base,
+ const Matrix4x4f* __restrict a, const Matrix4x4f* __restrict b, Matrix4x4f* __restrict res, size_t count)
+{
+ const float* mbase = base->m_Data;
+ Prefetch((const char*)mbase);
+
+ const Simd128 base0 = V4LoadUnaligned( mbase, 0x0 );
+ const Simd128 base1 = V4LoadUnaligned( mbase, 0x4 );
+ const Simd128 base2 = V4LoadUnaligned( mbase, 0x8 );
+ const Simd128 base3 = V4LoadUnaligned( mbase, 0xC );
+
+ for (size_t i = 0; i < count; ++i)
+ {
+ float* m = res[i].m_Data;
+ const float* m1 = a[i].m_Data;
+ const float* m2 = b[i].m_Data;
+ Prefetch((const char*)m1);
+ Prefetch((const char*)m2);
+ const Simd128 m10 = V4LoadUnaligned( m1, 0x0 );
+ const Simd128 m11 = V4LoadUnaligned( m1, 0x4 );
+ const Simd128 m12 = V4LoadUnaligned( m1, 0x8 );
+ const Simd128 m13 = V4LoadUnaligned( m1, 0xC );
+ const Simd128 m20 = V4LoadUnaligned( m2, 0x0 );
+ const Simd128 m21 = V4LoadUnaligned( m2, 0x4 );
+ const Simd128 m22 = V4LoadUnaligned( m2, 0x8 );
+ const Simd128 m23 = V4LoadUnaligned( m2, 0xC );
+
+ Simd128 b20, b21, b22, b23, rb0, rb1, rb2, rb3;
+ MultiplyMatrices4x4NATIVE(m10, m11, m12, m13, m20, m21, m22, m23, b20, b21, b22, b23);
+ MultiplyMatrices4x4NATIVE(base0, base1, base2, base3, b20, b21, b22, b23, rb0, rb1, rb2, rb3);
+
+ V4StoreUnaligned(rb0, m, 0x0 );
+ V4StoreUnaligned(rb1, m, 0x4 );
+ V4StoreUnaligned(rb2, m, 0x8 );
+ V4StoreUnaligned(rb3, m, 0xC );
+ }
+}
+
+
+#if UNITY_AUTO_DETECT_VECTOR_UNIT && UNITY_SUPPORTS_SSE
+# define MultiplyMatrices4x4(a,b,c) CPUInfo::HasSSESupport() ? MultiplyMatrices4x4Simd(a,b,c) : MultiplyMatrices4x4REF(a,b,c)
+# define CopyMatrix(a,b) CPUInfo::HasSSESupport() ? CopyMatrixSimd(a,b) : CopyMatrixREF(a,b)
+# define TransposeMatrix4x4(a,b) CPUInfo::HasSSESupport() ? TransposeMatrix4x4Simd(a,b) : TransposeMatrix4x4REF(a,b)
+# define MultiplyMatrixArrayWithBase4x4(base,a,b,res,count) CPUInfo::HasSSESupport() ? MultiplyMatrixArrayWithBase4x4Simd(base,a,b,res,count) : MultiplyMatrixArrayWithBase4x4REF(base,a,b,res,count)
+#endif
+
+#define MultiplyMatrixArray4x4 MultiplyMatrixArray4x4REF
+
+#endif //MATRIX4X4SIMD_H \ No newline at end of file
diff --git a/Runtime/Math/Simd/SimdMath.h b/Runtime/Math/Simd/SimdMath.h
new file mode 100644
index 0000000..99fd118
--- /dev/null
+++ b/Runtime/Math/Simd/SimdMath.h
@@ -0,0 +1,240 @@
+#pragma once
+
+#include "Runtime/Utilities/Prefetch.h"
+#include "Runtime/Misc/CPUInfo.h"
+
+#if UNITY_SUPPORTS_VMX
+
+# define VMX_0X 0
+# define VMX_0Y 1
+# define VMX_0Z 2
+# define VMX_0W 3
+
+# define VMX_1X 4
+# define VMX_1Y 5
+# define VMX_1Z 6
+# define VMX_1W 7
+
+# define V4BuildPermuteMask(a,b,c,d) { \
+ (((a)<<2)|((a)<<10)|((a)<<18)|((a)<<26))+0x00010203,\
+ (((b)<<2)|((b)<<10)|((b)<<18)|((b)<<26))+0x00010203,\
+ (((c)<<2)|((c)<<10)|((c)<<18)|((c)<<26))+0x00010203,\
+ (((d)<<2)|((d)<<10)|((d)<<18)|((d)<<26))+0x00010203}
+
+
+# if UNITY_XENON
+# include <Xtl.h>
+# define ALIGN16 __declspec(align(16))
+ typedef __vector4 Simd128;
+
+# define vec_splat __vspltw
+# define vec_ste(vec, off, addr) __stvebx(vec, addr, off)
+
+# define V4Splat(v0, i) __vspltw((v0), (i))
+
+# elif UNITY_PS3
+# include <ppu_intrinsics.h>
+# define ALIGN16 __attribute__((aligned(16)))
+# define __forceinline __attribute__((always_inline))
+ typedef vec_float4 Simd128;
+ static const vec_float4 __vsignedzero = {-0.f,-0.f,-0.f,-0.f};
+
+
+# define __vzero() ((vec_float4)vec_splat_u32(0))
+
+# define __lvx(base, offset) vec_lvx(offset, base)
+# define __lvlx(base, offset) vec_lvlx(offset, base)
+# define __lvrx(base, offset) vec_lvrx(offset, base)
+
+# define __stvx(value, base, offset) vec_stvx((value), (offset), (float*)(base))
+# define __stvlx(value, base, offset) vec_stvlx((value), (offset), (float*)(base))
+# define __stvrx(value, base, offset) vec_stvrx((value), (offset), (float*)(base))
+
+# define __vmrglw(v0, v1) vec_mergel((vec_float4)(v0), (vec_float4)(v1))
+# define __vmrghw(v0, v1) vec_mergeh((vec_float4)(v0), (vec_float4)(v1))
+
+# define __vmulfp(a, b) vec_madd( a, b, __vsignedzero)
+
+# define __vand vec_and
+# define __vandc vec_andc
+# define __vor vec_or
+# define __vnor vec_nor
+# define __vxor vec_xor
+# define __vspltw vec_splat
+# define __vmaddfp vec_madd
+# define __vaddfp vec_add
+# define __vsubfp vec_sub
+# define __vperm vec_perm
+# define __vnmsubfp vec_nmsub
+# define __vminfp vec_min
+# define __vmaxfp vec_max
+# define __vrsqrtefp vec_rsqrte
+# define __vsel vec_sel
+# define __vrefp vec_re
+
+# define __vcmpeqfp vec_vcmpeqfp
+# define __vcmpgtfp vec_vcmpgtfp
+# define __vcmpgefp vec_vcmpgefp
+
+ __forceinline static Simd128 __vmsum3fp(Simd128 v0, Simd128 v1)
+ {
+ const Simd128 m0 = vec_madd(v0, v1, __vsignedzero);
+ const Simd128 m1 = vec_splat(m0, 0);
+ const Simd128 m2 = vec_splat(m0, 1);
+ const Simd128 m3 = vec_splat(m0, 2);
+ return vec_add(vec_add(m1, m2), m3);
+ }
+ __forceinline static Simd128 __vmsum4fp(Simd128 v0, Simd128 v1)
+ {
+ const Simd128 m0 = vec_madd(v0, v1, __vsignedzero);
+ const Simd128 m1 = vec_sld(m0, m0, 8);
+ const Simd128 m2 = vec_add(m0, m1);
+ const Simd128 m3 = vec_sld(m2, m2, 4);
+ return vec_add(m2, m3);
+ }
+
+# endif
+
+
+# if UNITY_PS3
+ typedef vec_uchar16 Simd128Mask;
+# else
+ typedef Simd128 Simd128Mask;
+# endif
+
+ typedef ALIGN16 struct Simd128i { union { int i[4]; Simd128 v; };} Simd128i;
+
+ // Load / Save
+# define V4Load(base, offset) __lvx((base), sizeof(base)*(offset))
+# define V4LoadUnaligned(base, offset) __vor(__lvlx((base), sizeof(base)*(offset)), __lvrx((base), (sizeof(base)*(offset)) + 16))
+# define V4Store(value, base, offset) __stvx(value, (base), sizeof(base)*(offset))
+# define V4StoreUnaligned(value, base, offset) __stvlx(value, (float*)(base), sizeof(base)*(offset)); __stvrx(value, (float*)(base), (sizeof(base)*(offset)) + 16 )
+
+ // Math functions
+# define V4Zero() __vzero()
+# define V4Add(v0, v1) __vaddfp((v0), (v1))
+# define V4Sub(v0, v1) __vsubfp((v0), (v1))
+# define V4Mul(v0, v1) __vmulfp((v0), (v1))
+# define V4MulAdd(v0, v1, v2) __vmaddfp((v0), (v1), (v2))
+# define V4Min(v0, v1) __vminfp((v0), (v1))
+# define V4Max(v0, v1) __vmaxfp((v0), (v1))
+# define V4Rcp(v0) __vrefp((v0))
+# define V4Rsqrt(v0) __vrsqrtefp((v0))
+# define V3Dot(v0, v1) __vmsum3fp((v0), (v1))
+# define V4Dot(v0, v1) __vmsum4fp((v0), (v1))
+
+ // Shuffling / Permuting / Splatting / Merging
+# define V4Splat(v0, i) __vspltw((v0), (i))
+# define V4MergeL(v0, v1) __vmrglw((v0), (v1))
+# define V4MergeH(v0, v1) __vmrghw((v0), (v1))
+
+ __forceinline static Simd128 V3Cross(Simd128 v0, Simd128 v1)
+ {
+ const static Simd128i maskYZXW = V4BuildPermuteMask(VMX_0Y, VMX_0Z, VMX_0X, VMX_0W);
+ const Simd128Mask p = (Simd128Mask)maskYZXW.v;
+ const Simd128 m0 = __vperm(v1, v1, p);
+ const Simd128 m1 = __vperm(v0, v0, p);
+ const Simd128 m2 = __vmulfp(v0, m0);
+ const Simd128 m3 = __vnmsubfp(m1, v1, m2);
+ return __vperm(m3, m3, p);
+ }
+
+
+
+#elif UNITY_SUPPORTS_SSE
+
+# if UNITY_WIN
+# include <intrin.h>
+# define ALIGN16 __declspec(align(16))
+# else
+# include <xmmintrin.h>
+# define ALIGN16 __attribute__((aligned(16)))
+# define __forceinline inline __attribute__((always_inline))
+# endif
+
+ typedef __m128 Simd128;
+
+ // Load / Save
+# define V4Load(base, offset) _mm_load_ps((base)+(offset))
+# define V4LoadUnaligned(base, offset) _mm_loadu_ps((base)+(offset))
+# define V4Store(value, base, offset) _mm_store_ps((base)+(offset), value)
+# define V4StoreUnaligned(value, base, offset) _mm_storeu_ps((base)+(offset), value)
+
+ // Math functions
+# define V4Zero() _mm_setzero_ps()
+# define V4Add(v0, v1) _mm_add_ps((v0), (v1))
+# define V4Sub(v0, v1) _mm_sub_ps((v0), (v1))
+# define V4Mul(v0, v1) _mm_mul_ps((v0), (v1))
+# define V4MulAdd(v0, v1, v2) _mm_add_ps(_mm_mul_ps((v0), (v1)), (v2))
+# define V4Min(v0, v1) _mm_min_ps((v0), (v1))
+# define V4Max(v0, v1) _mm_max_ps((v0), (v1))
+# define V4Rcp(v0) _mm_rcp_ps((v0))
+# define V4Rsqrt(v0) _mm_rsqrt_ps((v0))
+
+ __forceinline static Simd128 V3Dot(Simd128 v0, Simd128 v1)
+ {
+ const Simd128 m0 = _mm_mul_ps(v0, v1);
+ const Simd128 m1 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0,0,0,0));
+ const Simd128 m2 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(1,1,1,1));
+ const Simd128 m3 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(2,2,2,2));
+ return _mm_add_ps(_mm_add_ps(m1, m2), m3);
+ }
+ __forceinline static Simd128 V4Dot(Simd128 v0, Simd128 v1)
+ {
+ const Simd128 m0 = _mm_mul_ps(v0, v1);
+ const Simd128 m1 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(2,3,0,1));
+ const Simd128 m2 = _mm_add_ps(m0, m1);
+ const Simd128 m3 = _mm_shuffle_ps(m2, m2, _MM_SHUFFLE(1,0,3,2));
+ return _mm_add_ps(m2, m3);
+ }
+ __forceinline static Simd128 V3Cross(Simd128 v0, Simd128 v1)
+ {
+ const Simd128 m0 = _mm_shuffle_ps(v1, v1, _MM_SHUFFLE(3,0,2,1));
+ const Simd128 m1 = _mm_shuffle_ps(v0, v0, _MM_SHUFFLE(3,0,2,1));
+ const Simd128 m2 = _mm_mul_ps(v1, m1);
+ const Simd128 m3 = _mm_sub_ps(_mm_mul_ps(m0, v0), m2);
+ return _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(3,0,2,1));
+ }
+
+ // Shuffling / Permuting / Splatting / Merging
+# define V4Splat(v0, i) _mm_shuffle_ps((v0), (v0), _MM_SHUFFLE(i,i,i,i))
+
+ // Attention! : these are done after PPC big-endian specs.
+# define V4MergeL(v0, v1) _mm_unpackhi_ps((v0), (v1))
+# define V4MergeH(v0, v1) _mm_unpacklo_ps((v0), (v1))
+#endif
+
+// Matrix & Quaternion types
+struct ALIGN16 SimdMatrix3x4 { Simd128 m00, m10, m20; };
+struct ALIGN16 SimdMatrix4x4 { Simd128 m00, m10, m20, m30;};
+typedef Simd128 SimdQuaternion;
+
+#if UNITY_SUPPORTS_VMX
+
+__forceinline static void V3StoreUnaligned(Simd128 value, float* volatile base, const UInt32 offset)
+{
+ const Simd128 X = vec_splat(value, 0);
+ const Simd128 Y = vec_splat(value, 1);
+ const Simd128 Z = vec_splat(value, 2);
+ vec_ste(X, 0, base+offset);
+ vec_ste(Y, 4, base+offset);
+ vec_ste(Z, 8, base+offset);
+}
+
+#else
+
+__forceinline static void V3StoreUnaligned(Simd128 value, float* base, const UInt32 offset)
+{
+ typedef union {
+ UInt32 u[4];
+ __m128i v;
+ } m128u;
+
+ static const m128u store_mask={{0xffffffff,0xffffffff,0xffffffff,0}};
+ _mm_maskmoveu_si128(*(__m128i *)&value, store_mask.v, (char*)(base+offset));
+}
+
+#endif
+
+
+
diff --git a/Runtime/Math/Simd/SimdTest.cpp b/Runtime/Math/Simd/SimdTest.cpp
new file mode 100644
index 0000000..24f76b8
--- /dev/null
+++ b/Runtime/Math/Simd/SimdTest.cpp
@@ -0,0 +1,814 @@
+#include "UnityPrefix.h"
+#if ENABLE_UNIT_TESTS
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Profiler/TimeHelper.h"
+
+#include "Runtime/Math/Simd/float1.h"
+#include "Runtime/Math/Simd/float4.h"
+#include "Runtime/Math/Simd/math.h"
+#include "Runtime/Math/Simd/quaternion.h"
+#include "Runtime/mecanim/math/axes.h"
+
+using namespace math;
+
+const float epsilon = 1e-5f;
+
+SUITE (SimdTests)
+{
+ struct SimdFixture
+ {
+ SimdFixture()
+ {
+ }
+ ~SimdFixture()
+ {
+ }
+ };
+
+ TEST_FIXTURE(SimdFixture, swizzle)
+ {
+ constant_float4(value, 1,2,3,4);
+
+ float4 a = float4(0,0,0,0);
+
+ a.x() = 3.f;
+ CHECK_CLOSE(3, a.x().tofloat(), epsilon);
+ CHECK_CLOSE(0, a.y().tofloat(), epsilon);
+ CHECK_CLOSE(0, a.z().tofloat(), epsilon);
+ CHECK_CLOSE(0, a.w().tofloat(), epsilon);
+
+ a.x() = value.x();
+ CHECK_CLOSE(1, a.x().tofloat(), epsilon);
+ CHECK_CLOSE(0, a.y().tofloat(), epsilon);
+ CHECK_CLOSE(0, a.z().tofloat(), epsilon);
+ CHECK_CLOSE(0, a.w().tofloat(), epsilon);
+
+
+
+ a.y() = value.y();
+ CHECK_CLOSE(1, a.x().tofloat(), epsilon);
+ CHECK_CLOSE(2, a.y().tofloat(), epsilon);
+ CHECK_CLOSE(0, a.z().tofloat(), epsilon);
+ CHECK_CLOSE(0, a.w().tofloat(), epsilon);
+
+ a.z() = value.z();
+ CHECK_CLOSE(1, a.x().tofloat(), epsilon);
+ CHECK_CLOSE(2, a.y().tofloat(), epsilon);
+ CHECK_CLOSE(3, a.z().tofloat(), epsilon);
+ CHECK_CLOSE(0, a.w().tofloat(), epsilon);
+
+ a.w() = value.w();
+ CHECK_CLOSE(1, a.x().tofloat(), epsilon);
+ CHECK_CLOSE(2, a.y().tofloat(), epsilon);
+ CHECK_CLOSE(3, a.z().tofloat(), epsilon);
+ CHECK_CLOSE(4, a.w().tofloat(), epsilon);
+
+ a.z() = value.y();
+ CHECK_CLOSE(1, a.x().tofloat(), epsilon);
+ CHECK_CLOSE(2, a.y().tofloat(), epsilon);
+ CHECK_CLOSE(2, a.z().tofloat(), epsilon);
+ CHECK_CLOSE(4, a.w().tofloat(), epsilon);
+
+ float1 f = value.w();
+ CHECK_CLOSE(4, f.tofloat(), epsilon);
+
+ a = value.wxzy();
+ CHECK( all(a == float4(4,1,3,2) ) );
+
+ float4 g(value.wxzy());
+ CHECK( all(g == float4(4,1,3,2) ) );
+
+
+ a = value.xzwy();
+ CHECK( all(a == float4(1,3,4,2) ) );
+
+ a = value.xwyz();
+ CHECK( all(a == float4(1,4,2,3) ) );
+
+ a = value.wyxz();
+ CHECK( all(a == float4(4,2,1,3) ) );
+
+ a = value.zywx();
+ CHECK( all(a == float4(3,2,4,1) ) );
+
+ a = value.ywzx();
+ CHECK( all(a == float4(2,4,3,1) ) );
+
+ a = value.yzxw();
+ CHECK( all(a == float4(2,3,1,4) ) );
+
+ a = value.zxyw();
+ CHECK( all(a == float4(3,1,2,4) ) );
+
+ a = value.zwxy();
+ CHECK( all(a == float4(3,4,1,2) ) );
+
+ a = value.wwwz();
+ CHECK( all(a == float4(4,4,4,3) ) );
+
+ a = value.wwzz();
+ CHECK( all(a == float4(4,4,3,3) ) );
+
+ a = value.wzyx();
+ CHECK( all(a == float4(4,3,2,1) ) );
+
+ a = value.yxwz();
+ CHECK( all(a == float4(2,1,4,3) ) );
+
+ }
+
+ TEST_FIXTURE(SimdFixture, float1_op)
+ {
+ float ATTRIBUTE_ALIGN(ALIGN4F) v[4];
+
+ float1 b(3.f);
+ cvec4f(two, 2.f, 2.f, 2.f, 2.f);
+ float1 c(two);
+ float4 cx;
+ float1 e;
+
+ {
+ Vstorepf(b.eval(), v, 0);
+ CHECK_CLOSE(3.f, v[0], epsilon);
+ CHECK_CLOSE(3.f, v[1], epsilon);
+ CHECK_CLOSE(3.f, v[2], epsilon);
+ CHECK_CLOSE(3.f, v[3], epsilon);
+
+ Vstorepf(c.eval(), v, 0);
+ CHECK_CLOSE(2.f, v[0], epsilon);
+ CHECK_CLOSE(2.f, v[1], epsilon);
+ CHECK_CLOSE(2.f, v[2], epsilon);
+ CHECK_CLOSE(2.f, v[3], epsilon);
+
+ cx = float4(10.f,2.f,3.f,4.f);
+
+ float1 d(cx.x());
+
+ Vstorepf(d.eval(), v, 0);
+ CHECK_CLOSE(10.f, v[0], epsilon);
+ CHECK_CLOSE(10.f, v[1], epsilon);
+ CHECK_CLOSE(10.f, v[2], epsilon);
+ CHECK_CLOSE(10.f, v[3], epsilon);
+
+ e = cx.y();
+
+ Vstorepf(e.eval(), v, 0);
+ CHECK_CLOSE(2.f, v[0], epsilon);
+ CHECK_CLOSE(2.f, v[1], epsilon);
+ CHECK_CLOSE(2.f, v[2], epsilon);
+ CHECK_CLOSE(2.f, v[3], epsilon);
+
+ e = float1(4.f);
+ Vstorepf(e.eval(), v, 0);
+ CHECK_CLOSE(4.f, v[0], epsilon);
+ CHECK_CLOSE(4.f, v[1], epsilon);
+ CHECK_CLOSE(4.f, v[2], epsilon);
+ CHECK_CLOSE(4.f, v[3], epsilon);
+
+ e = float1(cx.x());
+ Vstorepf(e.eval(), v, 0);
+ CHECK_CLOSE(10.f, v[0], epsilon);
+ CHECK_CLOSE(10.f, v[1], epsilon);
+ CHECK_CLOSE(10.f, v[2], epsilon);
+ CHECK_CLOSE(10.f, v[3], epsilon);
+
+ e = cx.z();
+ Vstorepf(e.eval(), v, 0);
+ CHECK_CLOSE(3.f, v[0], epsilon);
+ CHECK_CLOSE(3.f, v[1], epsilon);
+ CHECK_CLOSE(3.f, v[2], epsilon);
+ CHECK_CLOSE(3.f, v[3], epsilon);
+
+ e += cx.w();
+ Vstorepf(e.eval(), v, 0);
+ CHECK_CLOSE(7.f, v[0], epsilon);
+ CHECK_CLOSE(7.f, v[1], epsilon);
+ CHECK_CLOSE(7.f, v[2], epsilon);
+ CHECK_CLOSE(7.f, v[3], epsilon);
+
+ e -= cx.x();
+ Vstorepf(e.eval(), v, 0);
+ CHECK_CLOSE(-3.f, v[0], epsilon);
+ CHECK_CLOSE(-3.f, v[1], epsilon);
+ CHECK_CLOSE(-3.f, v[2], epsilon);
+ CHECK_CLOSE(-3.f, v[3], epsilon);
+
+ e *= cx.y();
+ Vstorepf(e.eval(), v, 0);
+ CHECK_CLOSE(-6.f, v[0], epsilon);
+ CHECK_CLOSE(-6.f, v[1], epsilon);
+ CHECK_CLOSE(-6.f, v[2], epsilon);
+ CHECK_CLOSE(-6.f, v[3], epsilon);
+
+ e /= cx.z();
+ Vstorepf(e.eval(), v, 0);
+ CHECK_CLOSE(-2.f, v[0], epsilon);
+ CHECK_CLOSE(-2.f, v[1], epsilon);
+ CHECK_CLOSE(-2.f, v[2], epsilon);
+ CHECK_CLOSE(-2.f, v[3], epsilon);
+ }
+ {
+ float1 f = e++;
+ Vstorepf(f.eval(), v, 0);
+ CHECK_CLOSE(-2.f, v[0], epsilon);
+ CHECK_CLOSE(-2.f, v[1], epsilon);
+ CHECK_CLOSE(-2.f, v[2], epsilon);
+ CHECK_CLOSE(-2.f, v[3], epsilon);
+
+ Vstorepf(e.eval(), v, 0);
+ CHECK_CLOSE(-1.f, v[0], epsilon);
+ CHECK_CLOSE(-1.f, v[1], epsilon);
+ CHECK_CLOSE(-1.f, v[2], epsilon);
+ CHECK_CLOSE(-1.f, v[3], epsilon);
+
+ float1 g = ++e;
+ Vstorepf(g.eval(), v, 0);
+ CHECK_CLOSE(0.f, v[0], epsilon);
+ CHECK_CLOSE(0.f, v[1], epsilon);
+ CHECK_CLOSE(0.f, v[2], epsilon);
+ CHECK_CLOSE(0.f, v[3], epsilon);
+
+ Vstorepf(e.eval(), v, 0);
+ CHECK_CLOSE(0.f, v[0], epsilon);
+ CHECK_CLOSE(0.f, v[1], epsilon);
+ CHECK_CLOSE(0.f, v[2], epsilon);
+ CHECK_CLOSE(0.f, v[3], epsilon);
+
+
+ float1 h(float1::zero());
+ Vstorepf(h.eval(), v, 0);
+ CHECK_CLOSE(0.f, v[0], epsilon);
+ CHECK_CLOSE(0.f, v[1], epsilon);
+ CHECK_CLOSE(0.f, v[2], epsilon);
+ CHECK_CLOSE(0.f, v[3], epsilon);
+
+ float1 i(float1::one());
+ Vstorepf(i.eval(), v, 0);
+ CHECK_CLOSE(1.f, v[0], epsilon);
+ CHECK_CLOSE(1.f, v[1], epsilon);
+ CHECK_CLOSE(1.f, v[2], epsilon);
+ CHECK_CLOSE(1.f, v[3], epsilon);
+
+ float1 j(4.f);
+ float1 l(3.f);
+
+ float1 m = j + l;
+ Vstorepf(m.eval(), v, 0);
+ CHECK_CLOSE(7.f, v[0], epsilon);
+ CHECK_CLOSE(7.f, v[1], epsilon);
+ CHECK_CLOSE(7.f, v[2], epsilon);
+ CHECK_CLOSE(7.f, v[3], epsilon);
+
+ float1 n = j - l;
+ Vstorepf(n.eval(), v, 0);
+ CHECK_CLOSE(1.f, v[0], epsilon);
+ CHECK_CLOSE(1.f, v[1], epsilon);
+ CHECK_CLOSE(1.f, v[2], epsilon);
+ CHECK_CLOSE(1.f, v[3], epsilon);
+
+ float1 o = j * l;
+ Vstorepf(o.eval(), v, 0);
+ CHECK_CLOSE(12.f, v[0], epsilon);
+ CHECK_CLOSE(12.f, v[1], epsilon);
+ CHECK_CLOSE(12.f, v[2], epsilon);
+ CHECK_CLOSE(12.f, v[3], epsilon);
+
+ float1 p = j / l;
+ Vstorepf(p.eval(), v, 0);
+ CHECK_CLOSE(4.f/3.f, v[0], epsilon);
+ CHECK_CLOSE(4.f/3.f, v[1], epsilon);
+ CHECK_CLOSE(4.f/3.f, v[2], epsilon);
+ CHECK_CLOSE(4.f/3.f, v[3], epsilon);
+ }
+
+ bool1 bvalue = float1(4.f) < float1(3.f);
+ CHECK( (bool)!bvalue );
+
+ bvalue = float1(4.f) < float1(4.f);
+ CHECK( (bool)!bvalue );
+
+ bvalue = float1(4.f) < float1(5.f);
+ CHECK( (bool)bvalue );
+
+ bvalue = float1(4.f) <= float1(3.f);
+ CHECK( (bool)!bvalue );
+
+ bvalue = float1(4.f) <= float1(4.f);
+ CHECK( (bool)bvalue );
+
+ bvalue = float1(4.f) <= float1(5.f);
+ CHECK( (bool)bvalue );
+
+ bvalue = float1(4.f) > float1(3.f);
+ CHECK( (bool)bvalue );
+
+ bvalue = float1(4.f) > float1(4.f);
+ CHECK( (bool)!bvalue );
+
+ bvalue = float1(4.f) > float1(5.f);
+ CHECK( (bool)!bvalue );
+
+ bvalue = float1(4.f) >= float1(3.f);
+ CHECK( (bool)bvalue );
+
+ bvalue = float1(4.f) >= float1(4.f);
+ CHECK( (bool)bvalue );
+
+ bvalue = float1(4.f) >= float1(5.f);
+ CHECK( (bool)!bvalue );
+
+ bvalue = float1(10.f) == float1(5.f);
+ CHECK( (bool)!bvalue );
+
+ bvalue = float1(10.f) == float1(10.f);
+ CHECK( (bool)bvalue );
+
+ bvalue = float1(10.f) != float1(5.f);
+ CHECK( (bool)bvalue );
+
+ bvalue = float1(10.f) != float1(10.f);
+ CHECK( (bool)!bvalue );
+
+ }
+
+ TEST_FIXTURE(SimdFixture, Operator1)
+ {
+ float ATTRIBUTE_ALIGN(ALIGN4F) v[4];
+
+ float4 a(1,2,3,4);
+ float4 b(4,3,2,1);
+ float4 e(54,3,42,2);
+
+ float4 c = a+b;
+ CHECK_CLOSE( 5.f, c.x().tofloat(), epsilon);
+ CHECK_CLOSE( 5.f, c.y().tofloat(), epsilon);
+ CHECK_CLOSE( 5.f, c.z().tofloat(), epsilon);
+ CHECK_CLOSE( 5.f, c.w().tofloat(), epsilon);
+
+ c = a+b.wwwz();
+ CHECK_CLOSE( 2.f, c.x().tofloat(), epsilon);
+ CHECK_CLOSE( 3.f, c.y().tofloat(), epsilon);
+ CHECK_CLOSE( 4.f, c.z().tofloat(), epsilon);
+ CHECK_CLOSE( 6.f, c.w().tofloat(), epsilon);
+
+ c = a+b.z();
+ CHECK_CLOSE( 3.f, c.x().tofloat(), epsilon);
+ CHECK_CLOSE( 4.f, c.y().tofloat(), epsilon);
+ CHECK_CLOSE( 5.f, c.z().tofloat(), epsilon);
+ CHECK_CLOSE( 6.f, c.w().tofloat(), epsilon);
+
+ c = a+b.wwwz()+e.y();
+ CHECK_CLOSE( 5.f, c.x().tofloat(), epsilon);
+ CHECK_CLOSE( 6.f, c.y().tofloat(), epsilon);
+ CHECK_CLOSE( 7.f, c.z().tofloat(), epsilon);
+ CHECK_CLOSE( 9.f, c.w().tofloat(), epsilon);
+
+ float4 d = a;
+ CHECK_CLOSE( 1.f, d.x().tofloat(), epsilon);
+ CHECK_CLOSE( 2.f, d.y().tofloat(), epsilon);
+ CHECK_CLOSE( 3.f, d.z().tofloat(), epsilon);
+ CHECK_CLOSE( 4.f, d.w().tofloat(), epsilon);
+
+ float1 a1 = float1(10.f);
+
+ d = a+a1;
+ CHECK_CLOSE( 11.f, d.x().tofloat(), epsilon);
+ CHECK_CLOSE( 12.f, d.y().tofloat(), epsilon);
+ CHECK_CLOSE( 13.f, d.z().tofloat(), epsilon);
+ CHECK_CLOSE( 14.f, d.w().tofloat(), epsilon);
+
+ a.x() = 0;
+ CHECK( all(a == float4(0,2,3,4)) );
+
+ a.y() = float1(12);
+ CHECK( all(a == float4(0,12,3,4)) );
+
+ a = float4(1,2,3,4);
+
+ c = a+b;
+ CHECK( all(c == float4(5,5,5,5)) );
+
+ c = a*b;
+ CHECK( all(c == float4(4.f,6.f,6.f,4.f)) );
+
+ c = a/b;
+ CHECK( all(c == float4(1.f/4.f,2.f/3.f,3.f/2.f,4.f/1.f)) );
+
+ c = ++a;
+ CHECK( all(c == float4(2.f,3.f,4.f,5.f)) );
+ CHECK( all(a == float4(2.f,3.f,4.f,5.f)) );
+
+ c = a++;
+ CHECK( all(c == float4(2.f,3.f,4.f,5.f)) );
+ CHECK( all(a == float4(3.f,4.f,5.f,6.f)) );
+
+ c += b;
+ CHECK( all(c == float4(6.f,6.f,6.f,6.f)) );
+
+ c -= a;
+ CHECK( all(c == float4(3.f,2.f,1.f,0.f)) );
+
+ c += 5.f;
+ CHECK( all(c == float4(8.f,7.f,6.f,5.f)) );
+
+ c *= b;
+ CHECK( all(c == float4(32.f,21.f,12.f,5.f)) );
+
+ c /= b;
+ CHECK( all(c == float4(8.f,7.f,6.f,5.f)) );
+
+ c = -c;
+ CHECK( all(c == float4(-8.f,-7.f,-6.f,-5.f)) );
+
+ c -= .5f;
+ CHECK( all(c == float4(-8.5f,-7.5f,-6.5f,-5.5f)) );
+
+ c *= 2.f;
+ CHECK( all(c == float4(-17.f,-15.f,-13.f,-11.f)) );
+
+ c /= 3.f;
+ Vstorepf(c.eval(), v, 0);
+ CHECK_CLOSE(-17.f/3.f, v[0], epsilon);
+ CHECK_CLOSE(-15.f/3.f, v[1], epsilon);
+ CHECK_CLOSE(-13.f/3.f, v[2], epsilon);
+ CHECK_CLOSE(-11.f/3.f, v[3], epsilon);
+ }
+
+ TEST_FIXTURE( SimdFixture, vecexpr1_operator )
+ {
+ float ATTRIBUTE_ALIGN(ALIGN4F) v[4];
+
+ constant_float4(c,-1,2,-3,4);
+ float4 t(5,6,7,8);
+
+ t.x() *= float1(-1.f);
+ CHECK( all(t == float4(-5.f, 6.f, 7.f, 8.f)));
+
+ t.y() += float1(4.f);
+ CHECK( all(t == float4(-5.f, 10.f, 7.f, 8.f)));
+
+ t.z() -= float1(-2.f);
+ CHECK( all(t == float4(-5.f, 10.f, 9.f, 8.f)));
+
+ t.w() /= float1(-2.f);
+ CHECK( all(t == float4(-5.f, 10.f, 9.f, -4.f)));
+
+ t.x() *= c.w();
+ CHECK( all(t == float4(-20.f, 10.f, 9.f, -4.f)));
+
+ t.y() /= c.z();
+ Vstorepf(t.eval(), v, 0);
+ CHECK_CLOSE(-20.f, v[0], epsilon);
+ CHECK_CLOSE(10.f/-3.f, v[1], epsilon);
+ CHECK_CLOSE(9.f, v[2], epsilon);
+ CHECK_CLOSE(-4.f, v[3], epsilon);
+
+ t.w() += c.y();
+ Vstorepf(t.eval(), v, 0);
+ CHECK_CLOSE(-20.f, v[0], epsilon);
+ CHECK_CLOSE(10.f/-3.f, v[1], epsilon);
+ CHECK_CLOSE(9.f, v[2], epsilon);
+ CHECK_CLOSE(-2.f, v[3], epsilon);
+
+ t.z() -= c.x();
+ Vstorepf(t.eval(), v, 0);
+ CHECK_CLOSE(-20.f, v[0], epsilon);
+ CHECK_CLOSE(10.f/-3.f, v[1], epsilon);
+ CHECK_CLOSE(10.f, v[2], epsilon);
+ CHECK_CLOSE(-2.f, v[3], epsilon);
+
+ float x = -c.x().tofloat();
+ CHECK( x == 1.f );
+ }
+
+
+ TEST_FIXTURE( SimdFixture, generic )
+ {
+ float ATTRIBUTE_ALIGN(ALIGN4F) v[4];
+
+ float4 a(-1.f, -.263f, 345.f, 0.f);
+ float4 b(5.f, 2.34f, -12.76f, 54.f);
+ float4 c;
+
+ float1 s;
+
+ c = abs(a);
+ CHECK( all(c == float4(1.f, .263f, 345.f, 0.f)));
+
+ c = math::clamp(c, float4(0.f, 1.f, 100.f, -2.f), float4(2.f, 3.f, 200.f, -10.f));
+ CHECK( all(c == float4(1.f, 1.f, 200.f, -10.f)));
+
+ c = cond(bool4(true), a, b);
+ CHECK( all(c == a));
+
+ c = cond(bool4(false), a, b);
+ CHECK( all(c == b));
+
+ c = cond(a<b, a, b);
+ CHECK( all(c == float4(-1.f, -.263f, -12.76f, 0.f)));
+
+
+ a = float4(-1.f, 0.f, 0.f, 0.f);
+ b = float4(0.f, 1.f, 0.f, 0.f);
+ c = cross(a, b);
+ CHECK( all(c == float4(0.f, 0.f, -1.f, 0.f)));
+
+ a = float4(-1.f, 2.f, -4.f, 1.f);
+ b = float4(4.f, 1.f, -3.f, 1.f);
+ c = cross(a, b);
+ CHECK( all(c == float4(-2.f, -19.f, -9.f, 0.f)));
+
+ c = degrees(float4( float(M_PIf), float(M_PI_2f), float(M_PI_4f), 0.f));
+ CHECK( all(c == float4(180.f, 90.f, 45.f, 0.f)));
+
+ c = radians(float4(180.f, 90.f, 45.f, 0.f));
+ CHECK( all(c == float4( float(M_PIf), float(M_PI_2f), float(M_PI_4f), 0.f)));
+
+ float1 teta = dot(float4(1,0,0,0), float4(0,1,0,0));
+ CHECK_CLOSE( 0.f, teta.tofloat(), epsilon);
+
+ teta = dot(float4(1,0,0,0), float4(1,0,0,0));
+ CHECK_CLOSE( 1.f, teta.tofloat(), epsilon);
+
+ teta = dot(float4(1,0,0,0), normalize(float4(1,1,0,0)));
+ CHECK_CLOSE( 0.70710f, teta.tofloat(), epsilon);
+
+ s = dot(float4( 10.f, 5.f, 2.f, 0.f));
+ CHECK_CLOSE( 129.0f, s.tofloat(), epsilon);
+
+ s = length( float4( 1.f, 0.f, 0.f, 0.f));
+ CHECK_CLOSE( 1.f, s.tofloat(), epsilon );
+
+ s = length( float4( 10.f, 5.f, 2.f, 0.f));
+ CHECK_CLOSE( 11.357816f, s.tofloat(), epsilon);
+
+ s = length( float4( 0.f, 0.f, 0.f, 0.f));
+ CHECK_CLOSE( 0.f, s.tofloat(), epsilon );
+
+ s = lerp(float1(3), float1(6), float1(.3333333f));
+ CHECK_CLOSE( 4.f, s.tofloat(), epsilon);
+
+ c = lerp(float4(1,2,3,4), float4(3,4,5,6), float1(.5f));
+ CHECK_CLOSE( 2.f, c.x().tofloat(), epsilon);
+ CHECK_CLOSE( 3.f, c.y().tofloat(), epsilon);
+ CHECK_CLOSE( 4.f, c.z().tofloat(), epsilon);
+ CHECK_CLOSE( 5.f, c.w().tofloat(), epsilon);
+
+ c = lerp(float4(1,2,3,4), float4(3,4,5,6), float4(-.5f,0,1.0,1.5f));
+ CHECK_CLOSE( 0.f, c.x().tofloat(), epsilon);
+ CHECK_CLOSE( 2.f, c.y().tofloat(), epsilon);
+ CHECK_CLOSE( 5.f, c.z().tofloat(), epsilon);
+ CHECK_CLOSE( 7.f, c.w().tofloat(), epsilon);
+
+ s = maximum(float4(-1.f, -.263f, 345.f, 0.f));
+ CHECK_CLOSE( 345.f, s.tofloat(), epsilon);
+
+ s = minimum(float4(-1.f, -.263f, 345.f, 0.f));
+ CHECK_CLOSE( -1.f, s.tofloat(), epsilon);
+
+ c = normalize(float4( 0.f, 0.f, 0.f, 1.f));
+ CHECK_CLOSE( 0.f, c.x().tofloat(), epsilon);
+ CHECK_CLOSE( 0.f, c.y().tofloat(), epsilon);
+ CHECK_CLOSE( 0.f, c.z().tofloat(), epsilon);
+ CHECK_CLOSE( 1.f, c.w().tofloat(), epsilon);
+
+ c = normalize(float4( 10.f, 5.f, 2.f, 0.f));
+ CHECK_CLOSE( 0.880451f, c.x().tofloat(), epsilon);
+ CHECK_CLOSE( 0.440225f, c.y().tofloat(), epsilon);
+ CHECK_CLOSE( 0.176090f, c.z().tofloat(), epsilon);
+ CHECK_CLOSE( 0.f, c.w().tofloat(), epsilon);
+
+ c = rcp(float4( -25.f, 45.f, .5f, 1.f));
+ CHECK_CLOSE( 1.f/-25.f, c.x().tofloat(), epsilon);
+ CHECK_CLOSE( 1.f/45.f, c.y().tofloat(), epsilon);
+ CHECK_CLOSE( 1.f/.5f, c.z().tofloat(), epsilon);
+ CHECK_CLOSE( 1.f, c.w().tofloat(), epsilon);
+
+ //float4 rsqrt(float4 const& r);
+
+ float f = saturate(-2.f);
+ CHECK_CLOSE( 0.f, f, epsilon);
+
+ f = saturate(.5f);
+ CHECK_CLOSE( .5f, f, epsilon);
+
+ f = saturate(1.5f);
+ CHECK_CLOSE( 1.f, f, epsilon);
+
+ c = saturate(float4( -25.f, 0.f, .5f, 1.5f));
+ CHECK( all(c == float4( 0.f, 0.f, .5f, 1.f)));
+
+ f = sgn(-25.f);
+ CHECK_CLOSE( -1.f, f, epsilon);
+
+ f = sgn(0.f);
+ CHECK_CLOSE( 1.f, f, epsilon);
+
+ f = sgn(3.f);
+ CHECK_CLOSE( 1.f, f, epsilon);
+
+ c = sgn(float4( -25.f, 0.f, .5f, 1.5f));
+ CHECK( all(c == float4( -1.f, 1.f, 1.f, 1.f)));
+
+ c = sgn(float4( 25.f, 0.f, -.5f, -1.5f));
+ CHECK( all(c == float4( 1.f, 1.f, -1.f, -1.f)));
+
+ // inconsistant how sgn of -0 is interpreted. should not matter in any real world scenarios
+/* c = sgn(float4( -25.f, -0.f, .5f, -1.5f));
+ CHECK( all(c == float4( -1.f, -1.f, 1.f, -1.f)));
+*/
+ f = sign(-25.f);
+ CHECK_CLOSE( -1.f, f, epsilon);
+
+ f = sign(0.f);
+ CHECK_CLOSE( 0.f, f, epsilon);
+
+ f = sign(3.f);
+ CHECK_CLOSE( 1.f, f, epsilon);
+
+ c = sign(float4( -25.f, 0.f, .5f, 1.5f));
+ CHECK( all(c == float4( -1.f, 0.f, 1.f, 1.f)));
+
+ c = sign(float4( 25.f, -0.f, .5f, -1.5f));
+ CHECK( all(c == float4( 1.f, 0.f, 1.f, -1.f)));
+
+ c = sqrt(float4( 9.f, 81.f, 49.f, 74.f));
+ Vstorepf(c.eval(), v, 0);
+ CHECK_CLOSE(3.f, v[0], epsilon);
+ CHECK_CLOSE(9.f, v[1], epsilon);
+ CHECK_CLOSE(7.f, v[2], epsilon);
+ CHECK_CLOSE(8.602325f, v[3], epsilon);
+
+ s = sum(float4( 9.f, 81.f, 49.f, 74.f));
+ CHECK_CLOSE( 213.f, s.tofloat(), epsilon);
+
+ c = math::vector(float4( -25.f, 0.f, .5f, 1.5f));
+ CHECK( all(c == float4( -25.f, 0.f, .5f, 0.f)));
+
+ a = float4(-1.f, -4.f, 8.f, 0.f);
+ b = float4(5.f, 2.f, -2.f, 54.f);
+ c = float4( -25.f, 0.f, .5f, 1.5f);
+ float4 d = float4(Vmadd(a.eval(),b.eval(),c.eval()));
+ CHECK(all(d == float4(-30.f,-8.f,-15.5f,1.5f)));
+
+ d = float4(Vmsub(a.eval(),b.eval(),c.eval()));
+ CHECK(all(d == float4(20.f,-8.f,-16.5f,-1.5f)));
+
+ bool4 bv = bool4(0, 0, 0, 0);
+ CHECK( any(bv) == false );
+
+ bv = bool4(0, 1, 0, 0);
+ CHECK( any(bv) == true );
+
+ bv = bool4(1, 1, 1, 1);
+ CHECK( any(bv) == true );
+ }
+
+ TEST_FIXTURE( SimdFixture, quaternion )
+ {
+ float epsilon = 1e-4f;
+
+ float4 qx(1,0,0,0);
+ float4 qy(0,1,0,0);
+ float4 qz(0,0,1,0);
+
+ float4 vz(0,0,1,0);
+
+ float4 v;
+
+ v = quatMulVec(qy, vz);
+ CHECK( all(v == float4(0.f, 0.f, -1.f, 0.f)));
+
+ v = quatMulVec(qz, vz);
+ CHECK( all(v == float4(0.f, 0.f, 1.f, 0.f)));
+
+ v = quatMulVec(qx, vz);
+ CHECK( all(v == float4(0.f, 0.f, -1.f, 0.f)));
+
+ float4 euler(radians(-38.22f), radians(16.16f), radians(-45.96f), 0.f );
+ float4 q = quatEulerToQuat(euler);
+ v = quatMulVec(q, float4(32.21f, 61.03f, -11.19f, 0.f) );
+ CHECK_CLOSE( 41.990852f, v.x().tofloat(), epsilon);
+ CHECK_CLOSE( 15.592499f, v.y().tofloat(), epsilon);
+ CHECK_CLOSE( -53.674984f, v.z().tofloat(), epsilon);
+ CHECK_CLOSE( 0.f, v.w().tofloat(), epsilon);
+
+ float4 q1(radians(-38.22f), radians(16.16f), radians(-45.96f), 0.f );
+ float4 q2(radians(79.24f), radians(-1.61f), radians(-33.15f), 0.f );
+ float4 q3(radians(38.40f), radians(-6.50f), radians(-70.45f), 0.f);
+ q3 = quatEulerToQuat(q3);
+ q = quatMul(quatEulerToQuat(q1), quatEulerToQuat(q2));
+ CHECK_CLOSE( q.x().tofloat(), q3.x().tofloat(), epsilon);
+ CHECK_CLOSE( q.y().tofloat(), q3.y().tofloat(), epsilon);
+ CHECK_CLOSE( q.z().tofloat(), q3.z().tofloat(), epsilon);
+ CHECK_CLOSE( q.w().tofloat(), q3.w().tofloat(), epsilon);
+
+ q3 = quatConj(q3);
+ CHECK_CLOSE( -q.x().tofloat(), q3.x().tofloat(), epsilon);
+ CHECK_CLOSE( -q.y().tofloat(), q3.y().tofloat(), epsilon);
+ CHECK_CLOSE( -q.z().tofloat(), q3.z().tofloat(), epsilon);
+ CHECK_CLOSE( q.w().tofloat(), q3.w().tofloat(), epsilon);
+
+ Axes cAxes;
+ q3 = ToAxes(cAxes,q2);
+ CHECK_CLOSE( 3.121f, q3.x().tofloat(), epsilon);
+ CHECK_CLOSE( 0.792092f, q3.y().tofloat(), epsilon);
+ CHECK_CLOSE( -0.0492416f, q3.z().tofloat(), epsilon);
+ CHECK_CLOSE( 0.0f, q3.w().tofloat(), epsilon);
+
+ q3 = FromAxes(cAxes,q3);
+ CHECK_CLOSE( 0.922363f, q3.x().tofloat(), epsilon);
+ CHECK_CLOSE( -0.0187406f, q3.y().tofloat(), epsilon);
+ CHECK_CLOSE( -0.38587f, q3.z().tofloat(), epsilon);
+ CHECK_CLOSE( 0.0f, q3.w().tofloat(), epsilon);
+
+ Axes aAxiz (float4(0,-0.268f,-0.364f,1),float4(0,-2,1,0),float4(-1,-1,1,1),17,math::kZYRoll);
+ q3 = ToAxes(aAxiz,q2);
+ CHECK_CLOSE( 1.28582f, q3.x().tofloat(), epsilon);
+ CHECK_CLOSE( 1.69701f, q3.y().tofloat(), epsilon);
+ CHECK_CLOSE( -0.652772f, q3.z().tofloat(), epsilon);
+ CHECK_CLOSE( 0.0f, q3.w().tofloat(), epsilon);
+
+ q3 = FromAxes(aAxiz,q3);
+ CHECK_CLOSE( 0.922363f, q3.x().tofloat(), epsilon);
+ CHECK_CLOSE( -0.0187405f, q3.y().tofloat(), epsilon);
+ CHECK_CLOSE( -0.38587f, q3.z().tofloat(), epsilon);
+ CHECK_CLOSE( -0.0f, q3.w().tofloat(), epsilon);
+
+
+
+
+ /* float4 left(0.999886f, 0.011893f, -0.006366f, 0);
+ float4 up(-0.012257f, 0.998085f, -0.060634f, 0);
+ float4 front(0.005632f, 0.060705f, 0.998116f, 0);
+ float4 rootX( -0.068884f, -0.000000f, -0.000000f, 0.997625f);
+
+ math::float4 ret = math::normalize(math::quatMul(math::quatMatrixToQuat(left,up,front),math::quatConj(rootX)));
+ CHECK_CLOSE( 0.278572f, ret.x().tofloat(), epsilon);
+ CHECK_CLOSE( 0.247044f, ret.y().tofloat(), epsilon);
+ CHECK_CLOSE( 0.216015f, ret.z().tofloat(), epsilon);
+ CHECK_CLOSE( 0.902610f, ret.w().tofloat(), epsilon);
+ */
+ }
+
+ TEST_FIXTURE( SimdFixture, trigonometric )
+ {
+
+ int degree;
+ for(degree=-90;degree<90;degree++)
+ {
+ float rad = radians( static_cast<float>(degree) );
+
+ float sin_stl = math::sin(rad);
+ math::float4 sin_unity = math::sin_est( math::float4(rad) );
+
+ CHECK_CLOSE( sin_stl, sin_unity.x().tofloat(), 9.2e-5f);
+ }
+
+ for(degree=-90;degree<90;degree++)
+ {
+ float rad = radians( static_cast<float>(degree) );
+
+ float cos_stl = math::cos(rad);
+ math::float4 cos_unity = math::cos_est( math::float4(rad) );
+
+ CHECK_CLOSE( cos_stl, cos_unity.x().tofloat(), 9.0e-4);
+ }
+
+ /*
+ float sin_stl = 0;
+ ABSOLUTE_TIME time_stl = START_TIME;
+
+ for(int i=0;i<1000;i++)
+ {
+
+ for(degree=-90;degree<90;degree++)
+ {
+ float rad = radians( static_cast<float>(degree) );
+ sin_stl += math::sin(rad);
+ }
+ }
+
+ time_stl = ELAPSED_TIME(time_stl);
+
+ math::float4 sin_unity = math::float4::zero();
+ ABSOLUTE_TIME time_unity = START_TIME;
+
+ for(int i=0;i<1000;i++)
+ {
+ for(degree=-90;degree<90;degree++)
+ {
+ float rad = radians( static_cast<float>(degree) );
+ sin_unity += math::sin_est( math::float4(rad) );
+ }
+ }
+
+ time_unity = ELAPSED_TIME(time_unity);
+
+ CHECK_CLOSE( sin_stl, sin_unity.x().tofloat(), 9.0e-4);
+ CHECK_CLOSE( time_stl, time_unity, 1);
+ */
+ }
+}
+
+#endif
diff --git a/Runtime/Math/Simd/bool1.h b/Runtime/Math/Simd/bool1.h
new file mode 100644
index 0000000..d195a4c
--- /dev/null
+++ b/Runtime/Math/Simd/bool1.h
@@ -0,0 +1,42 @@
+#ifndef SIMD_BOOL1_H
+#define SIMD_BOOL1_H
+
+#include "Runtime/Math/Simd/intrinsic.h"
+
+namespace math
+{
+
+struct ATTRIBUTE_ALIGN(ALIGN4F) bool1
+{
+ enum {
+ RHS_SWZ = kXYZW
+ };
+
+ typedef bool scalar_type;
+ typedef vec4bs packed_type;
+
+ packed_type s;
+
+ MECANIM_FORCE_INLINE bool1()
+ {}
+
+ MECANIM_FORCE_INLINE explicit bool1(packed_type x) : s(x)
+ {}
+
+ MECANIM_FORCE_INLINE explicit bool1(scalar_type x) : s(Vloadsb(x))
+ {}
+
+ MECANIM_FORCE_INLINE operator scalar_type() const
+ { return Vstoresb(s); }
+
+ MECANIM_FORCE_INLINE bool1 &operator=(const bool1 &r)
+ { s = r.s; return *this; }
+
+ // unary operators
+ MECANIM_FORCE_INLINE bool1 operator!() const
+ { bool1 r = bool1(Vnot(s)); return r; }
+};
+
+}
+
+#endif
diff --git a/Runtime/Math/Simd/bool4.h b/Runtime/Math/Simd/bool4.h
new file mode 100644
index 0000000..05afc1d
--- /dev/null
+++ b/Runtime/Math/Simd/bool4.h
@@ -0,0 +1,61 @@
+#ifndef SIMD_BOOL4_H
+#define SIMD_BOOL4_H
+
+
+#include "Runtime/Math/Simd/intrinsic.h"
+#include "Runtime/Math/Simd/bool1.h"
+
+namespace math
+{
+
+template<typename T> struct vecexp4;
+
+struct ATTRIBUTE_ALIGN(ALIGN4F) bool4
+{
+ typedef bool scalar_type;
+ typedef vec4b packed_type;
+
+ packed_type v;
+
+ MECANIM_FORCE_INLINE bool4() {}
+
+ MECANIM_FORCE_INLINE bool4(const bool4 &r):v(r.v) { }
+
+ MECANIM_FORCE_INLINE bool4(const packed_type &r):v(r) { }
+
+ explicit MECANIM_FORCE_INLINE bool4(bool s):v(Vloadsb(s)) { }
+
+ explicit MECANIM_FORCE_INLINE bool4(bool1 const& r):v(r.s) { }
+
+ MECANIM_FORCE_INLINE bool4(bool x, bool y, bool z, bool w):v(Vload4sb(x,y,z,w)) { }
+
+ MECANIM_FORCE_INLINE bool4 &operator=(const bool4 &r) { v = r.v; return *this; }
+
+ MECANIM_FORCE_INLINE bool4 &operator=(bool s) { v = Vloadsb(s); return *this; }
+
+ MECANIM_FORCE_INLINE bool4 operator!() const { bool4 r = Vnot(v); return r; }
+};
+
+static MECANIM_FORCE_INLINE bool4 operator==(bool4 const& l, bool4 const& r)
+{
+ return bool4(Vxnor(l.v, r.v) );
+}
+
+static MECANIM_FORCE_INLINE bool4 operator!=(bool4 const& l, bool4 const& r)
+{
+ return bool4(Vxor(l.v, r.v) );
+}
+
+static MECANIM_FORCE_INLINE bool4 operator&&(bool4 const& l, bool4 const& r)
+{
+ return bool4(Vand(l.v, r.v) );
+}
+
+static MECANIM_FORCE_INLINE bool4 operator||(bool4 const& l, bool4 const& r)
+{
+ return bool4(Vor(l.v, r.v) );
+}
+
+}
+
+#endif
diff --git a/Runtime/Math/Simd/float1.h b/Runtime/Math/Simd/float1.h
new file mode 100644
index 0000000..90c17ae
--- /dev/null
+++ b/Runtime/Math/Simd/float1.h
@@ -0,0 +1,232 @@
+#ifndef SIMD_FLOAT1_H
+#define SIMD_FLOAT1_H
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+#include "Runtime/Math/Simd/intrinsic.h"
+
+#include "Runtime/Math/Simd/bool1.h"
+
+namespace math
+{
+
+struct float1;
+
+template<typename T> struct ATTRIBUTE_ALIGN(ALIGN4F) vecexp1 : T
+{
+ typedef T value_type;
+ typedef typename T::scalar_type scalar_type;
+ typedef typename T::packed_type packed_type;
+
+ MECANIM_FORCE_INLINE vecexp1() {}
+
+ MECANIM_FORCE_INLINE vecexp1(vecexp1 const& e):value_type(e) { }
+
+ explicit MECANIM_FORCE_INLINE vecexp1(scalar_type s):value_type(s){ }
+
+ explicit MECANIM_FORCE_INLINE vecexp1(packed_type vector):value_type(vector) { }
+
+ MECANIM_FORCE_INLINE vecexp1(value_type const& r):value_type(r) { }
+
+ MECANIM_FORCE_INLINE vecexp1(scalar_type x, scalar_type y, scalar_type z, scalar_type w):value_type(x,y,z,w) { }
+
+ template<typename R> MECANIM_FORCE_INLINE vecexp1(const vecexp1<R> &e){
+ value_type::v = Vswizzle<value_type::LHS_SWZ>::lhs(value_type::v, e.eval() );
+ }
+
+ inline bool IsFinite ()const
+ {
+ return ::IsFinite(Vstoresf(value_type::v));
+ }
+
+ MECANIM_FORCE_INLINE vecexp1 &operator=(const vecexp1 &e) {
+ SIMD_ASSERT_IF(!e.IsFinite());
+ value_type::v = Vswizzle<value_type::LHS_SWZ>::lhs(value_type::v, e.eval() );
+ return *this;
+ }
+
+ template<typename R> MECANIM_FORCE_INLINE vecexp1 &operator=(const vecexp1<R> &e){
+ SIMD_ASSERT_IF(!e.IsFinite());
+ value_type::v = Vswizzle<value_type::LHS_SWZ>::lhs(value_type::v, e.eval() );
+ return *this;
+ }
+
+ MECANIM_FORCE_INLINE vecexp1 &operator=(scalar_type s) {
+ SIMD_ASSERT_IF(!::IsFinite(s));
+ value_type::operator =(s);
+ return *this; }
+
+ MECANIM_FORCE_INLINE const vecexp1 &operator+() const { return *this; }
+ MECANIM_FORCE_INLINE vecexp1 operator-() const { return vecexp1(Vneg( value_type::eval() ) ); }
+
+ template<typename R>MECANIM_FORCE_INLINE vecexp1 &operator+=(const vecexp1<R> &r) {
+ value_type::v = Vswizzle<value_type::LHS_SWZ>::lhs(value_type::v, Vadd( value_type::eval(), r.eval() )); return *this;
+ }
+
+ template<typename R> MECANIM_FORCE_INLINE vecexp1 &operator-=(const vecexp1<R> &r) {
+ value_type::v = Vswizzle<value_type::LHS_SWZ>::lhs(value_type::v, Vsub( value_type::eval(), r.eval() )); return *this;
+ }
+
+ template<typename R> MECANIM_FORCE_INLINE vecexp1 &operator*=(const vecexp1<R> &r) {
+ value_type::v = Vswizzle<value_type::LHS_SWZ>::lhs(value_type::v, Vmul(value_type::eval(), r.eval())); return *this;
+ }
+
+ template<typename R> MECANIM_FORCE_INLINE vecexp1 &operator/=(const vecexp1<R> &r) {
+ value_type::v = Vswizzle<value_type::LHS_SWZ>::lhs(value_type::v, Vdiv(value_type::eval(), r.eval())); return *this;
+ }
+};
+
+struct ATTRIBUTE_ALIGN(ALIGN4F) vec1
+{
+ typedef float scalar_type;
+ typedef vec4f packed_type;
+ typedef vec4f const& const_reference_packed_type;
+ typedef vec4f& reference_packed_type;
+
+ enum{
+ RHS_SWZ = kXYZW,
+ LHS_SWZ = kXYZW
+ };
+
+ MECANIM_FORCE_INLINE vec1() {}
+
+ MECANIM_FORCE_INLINE vec1(scalar_type scalar):v(Vloadsf(scalar)) { }
+
+ MECANIM_FORCE_INLINE vec1(packed_type vector):v(vector){ }
+
+ template<typename T> MECANIM_FORCE_INLINE vec1(const T& vector) { v = vector.eval(); }
+
+ MECANIM_FORCE_INLINE scalar_type tofloat() const { return Vstoresf( v ); }
+
+ MECANIM_FORCE_INLINE vec1 &operator=(const vec1 &l) { v = l.v; return *this; }
+
+ MECANIM_FORCE_INLINE packed_type eval()const{ return v; }
+
+protected:
+ packed_type v;
+};
+
+struct ATTRIBUTE_ALIGN(ALIGN4F) float1 : public vecexp1<vec1>
+{
+ DEFINE_GET_TYPESTRING(float1)
+
+ typedef vec1 value_type;
+ typedef vec1::scalar_type scalar_type;
+ typedef vec1::packed_type packed_type;
+
+ MECANIM_FORCE_INLINE float1() {}
+
+ MECANIM_FORCE_INLINE explicit float1(scalar_type s):vecexp1<value_type>(s) {}
+
+ MECANIM_FORCE_INLINE explicit float1(packed_type const& v):vecexp1<value_type>(v) { }
+
+ MECANIM_FORCE_INLINE float1(const float1 &f):vecexp1<value_type>(f) { }
+
+ template<typename R> MECANIM_FORCE_INLINE float1(const vecexp1<R> &e){ value_type::v = e.eval(); }
+
+ MECANIM_FORCE_INLINE float1 &operator=(const float1 &f) { value_type::v = f.eval(); return *this; }
+
+ template<typename R> MECANIM_FORCE_INLINE float1 &operator=(const vecexp1<R> &e) { value_type::v = e.eval(); return *this; }
+
+ template<typename R>MECANIM_FORCE_INLINE float1 &operator+=(const vecexp1<R> &r) {
+ value_type::v = Vadd( value_type::eval(), r.eval() ); return *this;
+ }
+
+ template<typename R> MECANIM_FORCE_INLINE float1 &operator-=(const vecexp1<R> &r) {
+ value_type::v = Vsub( value_type::eval(), r.eval() ); return *this;
+ }
+
+ template<typename R> MECANIM_FORCE_INLINE float1 &operator*=(const vecexp1<R> &r) {
+ value_type::v = Vmul(value_type::eval(), r.eval() ); return *this;
+ }
+
+ template<typename R> MECANIM_FORCE_INLINE float1 &operator/=(const vecexp1<R> &r) {
+ value_type::v = Vdiv(value_type::eval(), r.eval() ); return *this;
+ }
+
+
+ MECANIM_FORCE_INLINE float1 &operator++() { value_type::v = Vinc(value_type::v); return *this; }
+ MECANIM_FORCE_INLINE float1 operator++(int) { float1 r = *this; value_type::v = Vinc(value_type::v); return r; }
+
+ MECANIM_FORCE_INLINE float1 &operator--() { value_type::v = Vdec(value_type::v); return *this; }
+ MECANIM_FORCE_INLINE float1 operator--(int) { float1 r = *this; value_type::v = Vdec(value_type::v); return r; }
+
+ static float1 zero() {return float1(Vzero()); } // 0
+ static float1 one() {return float1(Vone());} // 1
+
+ template<class TransferFunction>
+ MECANIM_FORCE_INLINE void Transfer (TransferFunction& transfer)
+ {
+ /////@TODO: This is wrong. It will not work for SafeBinaryRead
+ /////// Probably other places in the code too!
+
+ float x;
+ if(transfer.IsReading())
+ {
+ transfer.Transfer(x, "x");
+ *this = float1(x);
+ }
+ else if(transfer.IsWriting())
+ {
+ x = tofloat();
+
+ transfer.Transfer(x, "x");
+ }
+ else
+ {
+ transfer.Transfer(x, "x");
+ }
+ }
+};
+
+// vecexp1 Arithemtic
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE vecexp1<vec1> operator+(const vecexp1<LHS> &l, const vecexp1<RHS> &r)
+{
+ return vecexp1<vec1>( Vadd( l.eval(), r.eval() ));
+}
+
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE vecexp1<vec1> operator-(const vecexp1<LHS> &l, const vecexp1<RHS> &r)
+{
+ return vecexp1<vec1>( Vsub( l.eval(), r.eval() ));
+}
+
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE vecexp1<vec1> operator*(const vecexp1<LHS> &l, const vecexp1<RHS> &r)
+{
+ return vecexp1<vec1>( Vmul( l.eval(), r.eval() ));
+}
+
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE vecexp1<vec1> operator/(const vecexp1<LHS> &l, const vecexp1<RHS> &r)
+{
+ return vecexp1<vec1>( Vdiv( l.eval(), r.eval() ));
+}
+
+// vecexp1 logic
+template <typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool1 operator<(const vecexp1<LHS> &l, const vecexp1<RHS> &r)
+{
+ return bool1( Vcmplt( l.eval(), r.eval() ) );
+}
+template <typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool1 operator<=(const vecexp1<LHS> &l, const vecexp1<RHS> &r)
+{
+ return bool1( Vcmple( l.eval(), r.eval() ) );
+}
+template <typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool1 operator==(const vecexp1<LHS> &l, const vecexp1<RHS> &r)
+{
+ return bool1( Vcmpeq( l.eval(), r.eval() ) );
+}
+template <typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool1 operator!=(const vecexp1<LHS> &l, const vecexp1<RHS> &r)
+{
+ return bool1( Vcmpneq( l.eval(), r.eval() ) );
+}
+template <typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool1 operator>=(const vecexp1<LHS> &l, const vecexp1<RHS> &r)
+{
+ return bool1( Vcmpge( l.eval(), r.eval() ));
+}
+template <typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool1 operator>(const vecexp1<LHS> &l, const vecexp1<RHS> &r)
+{
+ return bool1( Vcmpgt( l.eval(), r.eval() ) );
+}
+
+}
+
+
+#endif
diff --git a/Runtime/Math/Simd/float4.h b/Runtime/Math/Simd/float4.h
new file mode 100644
index 0000000..7b653e6
--- /dev/null
+++ b/Runtime/Math/Simd/float4.h
@@ -0,0 +1,397 @@
+#ifndef SIMD_FLOAT4_H
+#define SIMD_FLOAT4_H
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+#include "Runtime/Math/Simd/intrinsic.h"
+
+#include "Runtime/Math/Simd/bool4.h"
+#include "Runtime/Math/Simd/float1.h"
+
+namespace math
+{
+
+template<typename T> struct ATTRIBUTE_ALIGN(ALIGN4F) vecexp4 : T
+{
+ typedef T value_type;
+ typedef typename T::scalar_type scalar_type;
+ typedef typename T::packed_type packed_type;
+
+ MECANIM_FORCE_INLINE vecexp4() {}
+
+ MECANIM_FORCE_INLINE vecexp4(vecexp4 const& e):value_type(e) { }
+
+ template<typename R> MECANIM_FORCE_INLINE vecexp4(const vecexp4<R> &e):value_type(e){ }
+
+ MECANIM_FORCE_INLINE vecexp4(scalar_type const& x, scalar_type const& y, scalar_type const& z, scalar_type const& w):value_type(x,y,z,w) { }
+
+ MECANIM_FORCE_INLINE vecexp4(value_type const& r):value_type(r) { }
+
+ //template<typename R1, typename R2, typename R3, typename R4> MECANIM_FORCE_INLINE vecexp4(const vecexp1<R1> &x, const vecexp1<R2> &y, const vecexp1<R3> &z, const vecexp1<R4> &w) {
+ // value_type::v = Vcombine( x.eval(), y.eval(), z.eval(), w.eval() );
+ //}
+ MECANIM_FORCE_INLINE vecexp4(const float1 &x, const float1 &y, const float1 &z, const float1 &w) {
+ value_type::v = Vcombine( x.eval(), y.eval(), z.eval(), w.eval() );
+ }
+
+ MECANIM_FORCE_INLINE vecexp4 &operator=(const vecexp4 &l) { value_type::operator =(l); return *this; }
+
+ template<typename R> MECANIM_FORCE_INLINE vecexp4 &operator=(const vecexp4<R> &e) {
+ value_type::v = e.eval(); return *this;
+ }
+
+ template<typename R> MECANIM_FORCE_INLINE vecexp4 &operator=(const vecexp1<R> &e) {
+ value_type::v = e.eval(); return *this;
+ }
+
+ MECANIM_FORCE_INLINE const vecexp4 &operator+() const { return *this; }
+ MECANIM_FORCE_INLINE vecexp4 operator-() const { return vecexp4(Vneg( value_type::eval() )); }
+};
+
+template<typename SCALAR, typename RHS_VECTOR, typename LHS_VECTOR, int RHS_MASK, int LHS_MASK> struct ATTRIBUTE_ALIGN(ALIGN4F) swizzle1
+{
+ typedef SCALAR scalar_type;
+ typedef LHS_VECTOR packed_type;
+ typedef RHS_VECTOR rhs_packed_type;
+
+ enum{
+ RHS_SWZ = RHS_MASK,
+ LHS_SWZ = LHS_MASK
+ };
+
+
+ packed_type v;
+
+ MECANIM_FORCE_INLINE swizzle1(packed_type vector):v(vector) {}
+
+ MECANIM_FORCE_INLINE swizzle1 &operator=(const scalar_type &s) { v = Vswizzle<LHS_SWZ>::lhs(v, Vloadsf(s)); return *this; }
+
+ MECANIM_FORCE_INLINE scalar_type tofloat() { return Vstoresf( Vswizzle<RHS_SWZ>::rhs(v) ); }
+
+ MECANIM_FORCE_INLINE rhs_packed_type eval()const{ return Vswizzle<RHS_SWZ>::rhs(v); }
+private:
+
+
+ MECANIM_FORCE_INLINE swizzle1 &operator=(const swizzle1 &s) {return *this;}
+};
+
+template<typename SCALAR, typename VECTOR, int MASK> struct ATTRIBUTE_ALIGN(ALIGN4F) swizzle
+{
+ typedef SCALAR scalar_type;
+ typedef VECTOR packed_type;
+
+ enum{
+ RHS_SWZ = MASK
+ };
+
+ MECANIM_FORCE_INLINE swizzle(packed_type const& vector):v(vector) {}
+
+ MECANIM_FORCE_INLINE packed_type eval()const{ return Vswizzle<RHS_SWZ>::rhs(v); }
+
+protected:
+ packed_type v;
+};
+
+struct ATTRIBUTE_ALIGN(ALIGN4F) vec4
+{
+ typedef float scalar_type;
+ typedef vec4f packed_type;
+ typedef vec4f const& const_reference_packed_type;
+ typedef vec4f& reference_packed_type;
+
+ enum{
+ RHS_SWZ = kXYZW
+ };
+
+ MECANIM_FORCE_INLINE vec4() {}
+
+ MECANIM_FORCE_INLINE vec4(scalar_type x, scalar_type y, scalar_type z, scalar_type w):v(Vload4sf(x,y,z,w)) { }
+
+ MECANIM_FORCE_INLINE vec4(scalar_type s):v(Vloadsf(s)) { }
+
+ MECANIM_FORCE_INLINE vec4(packed_type vector):v(vector){ }
+
+ template<typename T> MECANIM_FORCE_INLINE vec4(const T& vector):v(vector.eval()){ }
+
+ MECANIM_FORCE_INLINE vec4 &operator=(const vec4 &l) {
+ SIMD_ASSERT_IF(!l.IsFinite());
+ v = l.v;
+ return *this;
+ }
+
+ MECANIM_FORCE_INLINE packed_type eval()const{ return v; }
+
+ inline bool IsFinite ()const
+ {
+ return ::IsFinite( x().tofloat() ) & ::IsFinite( y().tofloat() ) & ::IsFinite( z().tofloat() ) & ::IsFinite( w().tofloat() );
+ }
+
+ MECANIM_FORCE_INLINE vecexp1< swizzle1<scalar_type, packed_type, packed_type, kXXXX, kXYZW> > x()const { return vecexp1< swizzle1<scalar_type, packed_type, packed_type, kXXXX, kXYZW> >(v); }
+ MECANIM_FORCE_INLINE vecexp1< swizzle1<scalar_type, packed_type, packed_type, kYYYY, kYXZW> > y()const { return vecexp1< swizzle1<scalar_type, packed_type, packed_type, kYYYY, kYXZW> >(v); }
+ MECANIM_FORCE_INLINE vecexp1< swizzle1<scalar_type, packed_type, packed_type, kZZZZ, kZYXW> > z()const { return vecexp1< swizzle1<scalar_type, packed_type, packed_type, kZZZZ, kZYXW> >(v); }
+ MECANIM_FORCE_INLINE vecexp1< swizzle1<scalar_type, packed_type, packed_type, kWWWW, kWYZX> > w()const { return vecexp1< swizzle1<scalar_type, packed_type, packed_type, kWWWW, kWYZX> >(v); }
+
+ MECANIM_FORCE_INLINE vecexp1< swizzle1<scalar_type, packed_type, reference_packed_type, kXXXX, kXYZW> > x() { return vecexp1< swizzle1<scalar_type, packed_type, reference_packed_type, kXXXX, kXYZW> >(v); }
+ MECANIM_FORCE_INLINE vecexp1< swizzle1<scalar_type, packed_type, reference_packed_type, kYYYY, kYXZW> > y() { return vecexp1< swizzle1<scalar_type, packed_type, reference_packed_type, kYYYY, kYXZW> >(v); }
+ MECANIM_FORCE_INLINE vecexp1< swizzle1<scalar_type, packed_type, reference_packed_type, kZZZZ, kZYXW> > z() { return vecexp1< swizzle1<scalar_type, packed_type, reference_packed_type, kZZZZ, kZYXW> >(v); }
+ MECANIM_FORCE_INLINE vecexp1< swizzle1<scalar_type, packed_type, reference_packed_type, kWWWW, kWYZX> > w() { return vecexp1< swizzle1<scalar_type, packed_type, reference_packed_type, kWWWW, kWYZX> >(v); }
+
+ MECANIM_FORCE_INLINE vecexp4< swizzle<scalar_type, packed_type, kWXZY> > wxzy()const { return vecexp4< swizzle<scalar_type, packed_type, kWXZY> >(v); }
+ MECANIM_FORCE_INLINE vecexp4< swizzle<scalar_type, packed_type, kXZWY> > xzwy()const { return vecexp4< swizzle<scalar_type, packed_type, kXZWY> >(v); }
+ MECANIM_FORCE_INLINE vecexp4< swizzle<scalar_type, packed_type, kXWYZ> > xwyz()const { return vecexp4< swizzle<scalar_type, packed_type, kXWYZ> >(v); }
+ MECANIM_FORCE_INLINE vecexp4< swizzle<scalar_type, packed_type, kWYXZ> > wyxz()const { return vecexp4< swizzle<scalar_type, packed_type, kWYXZ> >(v); }
+ MECANIM_FORCE_INLINE vecexp4< swizzle<scalar_type, packed_type, kZYWX> > zywx()const { return vecexp4< swizzle<scalar_type, packed_type, kZYWX> >(v); }
+ MECANIM_FORCE_INLINE vecexp4< swizzle<scalar_type, packed_type, kYWZX> > ywzx()const { return vecexp4< swizzle<scalar_type, packed_type, kYWZX> >(v); }
+ MECANIM_FORCE_INLINE vecexp4< swizzle<scalar_type, packed_type, kYZXW> > yzxw()const { return vecexp4< swizzle<scalar_type, packed_type, kYZXW> >(v); }
+ MECANIM_FORCE_INLINE vecexp4< swizzle<scalar_type, packed_type, kZXYW> > zxyw()const { return vecexp4< swizzle<scalar_type, packed_type, kZXYW> >(v); }
+ MECANIM_FORCE_INLINE vecexp4< swizzle<scalar_type, packed_type, kZWXY> > zwxy()const { return vecexp4< swizzle<scalar_type, packed_type, kZWXY> >(v); }
+ MECANIM_FORCE_INLINE vecexp4< swizzle<scalar_type, packed_type, kWWWZ> > wwwz()const { return vecexp4< swizzle<scalar_type, packed_type, kWWWZ> >(v); }
+ MECANIM_FORCE_INLINE vecexp4< swizzle<scalar_type, packed_type, kWWZZ> > wwzz()const { return vecexp4< swizzle<scalar_type, packed_type, kWWZZ> >(v); }
+ MECANIM_FORCE_INLINE vecexp4< swizzle<scalar_type, packed_type, kWZYX> > wzyx()const { return vecexp4< swizzle<scalar_type, packed_type, kWZYX> >(v); }
+ MECANIM_FORCE_INLINE vecexp4< swizzle<scalar_type, packed_type, kYXWZ> > yxwz()const { return vecexp4< swizzle<scalar_type, packed_type, kYXWZ> >(v); }
+
+protected:
+ packed_type v;
+};
+
+struct ATTRIBUTE_ALIGN(ALIGN4F) float4 : vecexp4<vec4>
+{
+ DEFINE_GET_TYPESTRING(float4)
+
+ typedef vec4 value_type;
+ typedef vec4::scalar_type scalar_type;
+ typedef vec4::packed_type packed_type;
+
+ MECANIM_FORCE_INLINE float4() {}
+
+ MECANIM_FORCE_INLINE float4(float4 const& vector):vecexp4<value_type>(vector.v) { }
+
+ explicit MECANIM_FORCE_INLINE float4(scalar_type s):vecexp4<value_type>(s) { }
+
+ explicit MECANIM_FORCE_INLINE float4(packed_type const& vector):vecexp4<value_type>(vector) { }
+
+ template<typename R> MECANIM_FORCE_INLINE float4(const vecexp4<R> &r):vecexp4<value_type>(r) { }
+
+ MECANIM_FORCE_INLINE float4(scalar_type x, scalar_type y, scalar_type z, scalar_type w):vecexp4<value_type>(x,y,z,w) { }
+
+ MECANIM_FORCE_INLINE float4(const float1 &x, const float1 &y, const float1 &z, const float1 &w):vecexp4<value_type>(x,y,z,w) { }
+
+ MECANIM_FORCE_INLINE float4 &operator=(const float4 &r) { vecexp4<vec4>::operator =(r); return *this; }
+
+ template<typename R> MECANIM_FORCE_INLINE float4 &operator=(const vecexp4<R> &r) { vecexp4<vec4>::operator =(r); return *this; }
+ template<typename R> MECANIM_FORCE_INLINE float4 &operator=(const vecexp1<R> &r) { vecexp4<vec4>::operator =(r); return *this; }
+
+ template<typename R> MECANIM_FORCE_INLINE float4 &operator+=(const vecexp4<R> &r) { value_type::v = Vadd(value_type::v, r.eval()); return *this; }
+ template<typename R> MECANIM_FORCE_INLINE float4 &operator+=(const vecexp1<R> &r) { value_type::v = Vadd(value_type::v, r.eval()); return *this; }
+ MECANIM_FORCE_INLINE float4 &operator+=(float r) { value_type::v = Vadd(value_type::v, Vloadsf(r)); return *this; }
+
+ template<typename R> MECANIM_FORCE_INLINE float4 &operator-=(const vecexp4<R> &r) { value_type::v = Vsub(value_type::v, r.eval()); return *this; }
+ template<typename R> MECANIM_FORCE_INLINE float4 &operator-=(const vecexp1<R> &r) { value_type::v = Vsub(value_type::v, r.eval()); return *this; }
+ MECANIM_FORCE_INLINE float4 &operator-=(float r) { value_type::v = Vsub(value_type::v, Vloadsf(r)); return *this; }
+
+ template<typename R> MECANIM_FORCE_INLINE float4 &operator*=(const vecexp4<R> &r) { value_type::v = Vmul(value_type::v, r.eval()); return *this; }
+ template<typename R> MECANIM_FORCE_INLINE float4 &operator*=(const vecexp1<R> &r) { value_type::v = Vmul(value_type::v, r.eval()); return *this; }
+ MECANIM_FORCE_INLINE float4 &operator*=(float r) { value_type::v = Vmul(value_type::v, Vloadsf(r)); return *this; }
+
+
+ template<typename R> MECANIM_FORCE_INLINE float4 &operator/=(const vecexp4<R> &r) { value_type::v = Vdiv(value_type::v, r.eval()); return *this; }
+ template<typename R> MECANIM_FORCE_INLINE float4 &operator/=(const vecexp1<R> &r) { value_type::v = Vdiv(value_type::v, r.eval()); return *this; }
+ MECANIM_FORCE_INLINE float4 &operator/=(float r) { value_type::v = Vdiv(value_type::v, Vloadsf(r)); return *this; }
+
+ // prefix decrement
+ MECANIM_FORCE_INLINE float4 &operator++() { value_type::v = Vinc(value_type::v); return *this; }
+ // postfix increment
+ MECANIM_FORCE_INLINE float4 operator++(int) { float4 r = *this; value_type::v = Vinc(value_type::eval() ); return r; }
+
+ // prefix decrement
+ MECANIM_FORCE_INLINE float4 &operator--() { value_type::v = Vdec(value_type::v); return *this; }
+ // postfix decrement
+ MECANIM_FORCE_INLINE float4 operator--(int) { float4 r = *this; value_type::v = Vdec(value_type::eval() ); return r; }
+
+
+ static float4 zero() {return float4(Vzero()); } // 0
+ static float4 one() {return float4(Vone());} // 1
+
+
+ template<class TransferFunction>
+ MECANIM_FORCE_INLINE void Transfer (TransferFunction& transfer)
+ {
+ /////@TODO: This is wrong. It will not work for SafeBinaryRead
+ /////// Probably other places in the code too!
+
+ float ATTRIBUTE_ALIGN(ALIGN4F) buf[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ if(transfer.IsReading())
+ {
+ transfer.Transfer(buf[0], "x");
+ transfer.Transfer(buf[1], "y");
+ transfer.Transfer(buf[2], "z");
+ transfer.Transfer(buf[3], "w");
+
+ v = Vloadpf(buf, 0);
+ }
+ else if(transfer.IsWriting())
+ {
+ Vstorepf(v, buf, 0);
+
+ transfer.Transfer(buf[0], "x");
+ transfer.Transfer(buf[1], "y");
+ transfer.Transfer(buf[2], "z");
+ transfer.Transfer(buf[3], "w");
+ }
+ else
+ {
+ transfer.Transfer(buf[0], "x");
+ transfer.Transfer(buf[1], "y");
+ transfer.Transfer(buf[2], "z");
+ transfer.Transfer(buf[3], "w");
+ }
+ }
+};
+
+// vecexp4 Arithemtic
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE vecexp4<vec4> operator+(const vecexp4<LHS> &l, const vecexp4<RHS> &r)
+{
+ return vecexp4<vec4>( Vadd( l.eval(), r.eval() ));
+}
+
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE vecexp4<vec4> operator+(const vecexp4<LHS> &l, const vecexp1<RHS> &r)
+{
+ return vecexp4<vec4>( Vadd( l.eval(), r.eval() ));
+}
+
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE vecexp4<vec4> operator+(const vecexp1<LHS> &l, const vecexp4<RHS> &r)
+{
+ return vecexp4<vec4>( Vadd( l.eval(), r.eval() ));
+}
+
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE vecexp4<vec4> operator-(const vecexp4<LHS> &l, const vecexp4<RHS> &r)
+{
+ return vecexp4<vec4>( Vsub( l.eval(), r.eval() ));
+}
+
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE vecexp4<vec4> operator-(const vecexp4<LHS> &l, const vecexp1<RHS> &r)
+{
+ return vecexp4<vec4>( Vsub( l.eval(), r.eval() ));
+}
+
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE vecexp4<vec4> operator-(const vecexp1<LHS> &l, const vecexp4<RHS> &r)
+{
+ return vecexp4<vec4>( Vsub( l.eval(), r.eval() ));
+}
+
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE vecexp4<vec4> operator*(const vecexp4<LHS> &l, const vecexp4<RHS> &r)
+{
+ return vecexp4<vec4>( Vmul( l.eval(), r.eval() ));
+}
+
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE vecexp4<vec4> operator*(const vecexp4<LHS> &l,const vecexp1<RHS> &r)
+{
+ return vecexp4<vec4>( Vmul( l.eval(), r.eval() ));
+}
+
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE vecexp4<vec4> operator*(const vecexp1<LHS> &l, const vecexp4<RHS> &r)
+{
+ return vecexp4<vec4>( Vmul( l.eval(), r.eval() ));
+}
+
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE vecexp4<vec4> operator/(const vecexp4<LHS> &l, const vecexp4<RHS> &r)
+{
+ return vecexp4<vec4>( Vdiv( l.eval(), r.eval() ));
+}
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE vecexp4<vec4> operator/(const vecexp4<LHS> &l, const vecexp1<RHS> &r)
+{
+ return vecexp4<vec4>( Vdiv( l.eval(), r.eval() ));
+}
+
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE vecexp4<vec4> operator/(const vecexp1<LHS> &l, const vecexp4<RHS> &r)
+{
+ return vecexp4<vec4>( Vdiv( l.eval(), r.eval() ));
+}
+
+
+// vecexp4 logic
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool4 operator<(const vecexp4<LHS> &l, const vecexp4<RHS> &r)
+{
+ return bool4(Vcmplt(l.eval(), r.eval()));
+}
+template<typename LHS> static MECANIM_FORCE_INLINE bool4 operator<(const vecexp4<LHS> &l, const float1 &r)
+{
+ return bool4(Vcmplt(l.eval(), r.eval()));
+}
+template<typename RHS> static MECANIM_FORCE_INLINE bool4 operator<(const float1 &l, const vecexp4<RHS> &r)
+{
+ return bool4(Vcmplt(l.eval(), r.eval()));
+}
+
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool4 operator<=(const vecexp4<LHS> &l, const vecexp4<RHS> &r)
+{
+ return bool4(Vcmple(l.eval(), r.eval()));
+}
+template<typename LHS> static MECANIM_FORCE_INLINE bool4 operator<=(const vecexp4<LHS> &l, const float1 &r)
+{
+ return bool4(Vcmple(l.eval(), r.eval()));
+}
+template<typename RHS> static MECANIM_FORCE_INLINE bool4 operator<=(const float1 &l, const vecexp4<RHS> &r)
+{
+ return bool4(Vcmple(l.eval(), r.eval()));
+}
+
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool4 operator==(const vecexp4<LHS> &l, const vecexp4<RHS> &r)
+{
+ return bool4( Vcmpeq(l.eval(), r.eval()) );
+}
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool4 operator==(const vecexp4<LHS> &l, const vecexp1<RHS> &r)
+{
+ return bool4( Vcmpeq(l.eval(), r.eval()) );
+}
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool4 operator==(const vecexp1<LHS> &l, const vecexp4<RHS> &r)
+{
+ return bool4( Vcmpeq(l.eval(), r.eval()) );
+}
+
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool4 operator!=(const vecexp4<LHS> &l, const vecexp4<RHS> &r)
+{
+ return bool4( Vcmpneq(l.eval(), r.eval()) );
+}
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool4 operator!=(const vecexp1<LHS> &l, const vecexp4<RHS> &r)
+{
+ return bool4( Vcmpneq(l.eval(), r.eval()) );
+}
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool4 operator!=(const vecexp4<LHS> &l, const vecexp1<RHS> &r)
+{
+ return bool4( Vcmpneq(l.eval(), r.eval()) );
+}
+
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool4 operator>=(const vecexp4<LHS> &l, const vecexp4<RHS> &r)
+{
+ return bool4( Vcmpge(l.eval(), r.eval()) );
+}
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool4 operator>=(const vecexp1<LHS> &l, const vecexp4<RHS> &r)
+{
+ return bool4( Vcmpge(l.eval(), r.eval()) );
+}
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool4 operator>=(const vecexp4<LHS> &l, const vecexp1<RHS> &r)
+{
+ return bool4( Vcmpge(l.eval(), r.eval()) );
+}
+
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool4 operator>(const vecexp4<LHS> &l, const vecexp4<RHS> &r)
+{
+ return bool4( Vcmpgt(l.eval(), r.eval()) );
+}
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool4 operator>(const vecexp4<LHS> &l, const vecexp1<RHS> &r)
+{
+ return bool4( Vcmpgt(l.eval(), r.eval()) );
+}
+template<typename LHS, typename RHS> static MECANIM_FORCE_INLINE bool4 operator>(const vecexp1<LHS> &l, const vecexp4<RHS> &r)
+{
+ return bool4( Vcmpgt(l.eval(), r.eval()) );
+}
+
+#define constant_float4(name, x,y,z,w) \
+ cvec4f(c##name, x,y,z,w); \
+ math::float4 const name(c##name); \
+
+}
+
+#endif
diff --git a/Runtime/Math/Simd/fpu.h b/Runtime/Math/Simd/fpu.h
new file mode 100644
index 0000000..20bd800
--- /dev/null
+++ b/Runtime/Math/Simd/fpu.h
@@ -0,0 +1,245 @@
+#ifndef SIMD_FPU_H
+#define SIMD_FPU_H
+
+// vector 4 packed
+struct vec4f
+{
+ vec4f(float s = 0.0f){m128_f32[0] = m128_f32[1] = m128_f32[2] = m128_f32[3]= s;}
+ vec4f(float x, float y, float z, float w){m128_f32[0] = x; m128_f32[1] = y; m128_f32[2] = z; m128_f32[3]= w;}
+
+ float m128_f32[4];
+};
+typedef vec4f vec4fs; // vector 4 scalar
+
+// vector 4 bool packed
+struct vec4b
+{
+ vec4b(bool s = false){m128_f32[0] = m128_f32[1] = m128_f32[2] = m128_f32[3]= s;}
+ vec4b(bool x, bool y, bool z, bool w){m128_f32[0] = x; m128_f32[1] = y; m128_f32[2] = z; m128_f32[3]= w;}
+
+ bool m128_f32[4];
+};
+
+typedef vec4b vec4bs; // vector 4 bool scalar
+
+#define cvec4f(name, x,y,z,w) static const vec4f name((x),(y),(z),(w))
+#define cvec4b(name, x,y,z,w) static const vec4b name((x),(y),(z),(w))
+#define cvec4fs(name, s) static const vec4fs name(s)
+
+
+#define SWZ_MASK(x, y, z, w) (((w) << 6) | ((z) << 4) | ((y) << 2) | ((x)))
+#define SWZ_X(MASK) (((MASK) >> 0) & 3)
+#define SWZ_Y(MASK) (((MASK) >> 2) & 3)
+#define SWZ_Z(MASK) (((MASK) >> 4) & 3)
+#define SWZ_W(MASK) (((MASK) >> 6) & 3)
+#define SWZ_I(MASK, I) (((MASK) >> (I*2)) & 3)
+
+enum simd_mask
+{
+ kXYZW = SWZ_MASK(0,1,2,3),
+ kXXXX = SWZ_MASK(0,0,0,0),
+ kYYYY = SWZ_MASK(1,1,1,1),
+ kZZZZ = SWZ_MASK(2,2,2,2),
+ kWWWW = SWZ_MASK(3,3,3,3),
+
+ kXWYZ = SWZ_MASK(0,3,1,2),
+ kXZWY = SWZ_MASK(0,2,3,1),
+
+ kYZWX = SWZ_MASK(1,2,3,0),
+ kYXZW = SWZ_MASK(1,0,2,3),
+ kYWZX = SWZ_MASK(1,3,2,0),
+ kYZXW = SWZ_MASK(1,2,0,3),
+ kYXWZ = SWZ_MASK(1,0,3,2),
+
+ kZWXY = SWZ_MASK(2,3,0,1),
+ kZYXW = SWZ_MASK(2,1,0,3),
+ kZYWX = SWZ_MASK(2,1,3,0),
+ kZXYW = SWZ_MASK(2,0,1,3),
+
+ kWYZX = SWZ_MASK(3,1,2,0),
+ kWXZY = SWZ_MASK(3,0,2,1),
+ kWYXZ = SWZ_MASK(3,1,0,2),
+ kWWWZ = SWZ_MASK(3,3,3,2),
+ kWWZZ = SWZ_MASK(3,3,2,2),
+ kWZYX = SWZ_MASK(3,2,1,0),
+};
+
+#define Vzero() vec4f(0.f)
+#define Vone() vec4f(1.f)
+
+static MECANIM_FORCE_INLINE vec4f Vpermute(vec4f v, int mask)
+{
+ return vec4f(v.m128_f32[SWZ_X(mask)], v.m128_f32[SWZ_Y(mask)], v.m128_f32[SWZ_Z(mask)], v.m128_f32[SWZ_W(mask)]);
+}
+
+#define Vmove(l, r) vec4f(r.m128_f32[0], l.m128_f32[1], l.m128_f32[2], l.m128_f32[3])
+
+// This template is part of the back-end because some instruction set support some swizzle operation that could be specialized, like xbox vmx rotate instruction that is use in dot product
+template<int SWZ> struct Vswizzle
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f r)
+ {
+ return Vpermute(r, SWZ);
+ }
+
+ static MECANIM_FORCE_INLINE vec4f lhs(vec4f l, vec4f r)
+ {
+ return Vswizzle<SWZ>::rhs(Vmove(Vswizzle<SWZ>::rhs(l), r));
+ }
+};
+
+template<> struct Vswizzle<kXYZW>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f r)
+ {
+ return r;
+ }
+ static MECANIM_FORCE_INLINE vec4f lhs(vec4f l, vec4f r)
+ {
+ return Vmove(l, r);
+ }
+};
+
+
+// Aligned store, store vector at address base as 4 float
+#define Vstorepf(v, base, offset) memcpy( (base)+(offset), &v.m128_f32[0], sizeof(v))
+
+// Return component x as a float
+#define Vstoresf(r) r.m128_f32[0]
+
+// Return component x as a bool
+#define Vstoresb(r) r.m128_f32[0]
+
+// Aligned store, store vector at address base as 4 bool
+static MECANIM_FORCE_INLINE void Vstorepb(vec4b v, bool* r)
+{
+ r[0] = v.m128_f32[0];
+ r[1] = v.m128_f32[1];
+ r[2] = v.m128_f32[2];
+ r[3] = v.m128_f32[3];
+}
+
+// Aligned load, load 4 float at address v in vector register
+static MECANIM_FORCE_INLINE vec4f Vloadpf(float const* v, int offset)
+{
+ float const* p = v+offset;
+ return vec4f( p[0], p[1], p[2], p[3] );
+}
+
+// Load float value in vector register and replicate value in all component
+#define Vloadsf(s) vec4f(s)
+
+// Load bool value in vector register and replicate value in all component
+#define Vloadsb(s) vec4bs(s)
+
+// Load 4 float value in vector register
+#define Vload4sf(x, y, z, w) vec4f(x,y,z,w)
+
+// Load 4 bool value in vector register
+#define Vload4sb(x, y, z, w) vec4b(x,y,z,w)
+
+static MECANIM_FORCE_INLINE vec4f Vadd(vec4f l, vec4f r)
+{
+ return vec4f(l.m128_f32[0]+r.m128_f32[0], l.m128_f32[1]+r.m128_f32[1], l.m128_f32[2]+r.m128_f32[2], l.m128_f32[3]+r.m128_f32[3]);
+}
+
+static MECANIM_FORCE_INLINE vec4f Vsub(vec4f l, vec4f r)
+{
+ return vec4f(l.m128_f32[0]-r.m128_f32[0], l.m128_f32[1]-r.m128_f32[1], l.m128_f32[2]-r.m128_f32[2], l.m128_f32[3]-r.m128_f32[3]);
+}
+
+static MECANIM_FORCE_INLINE vec4f Vmul( vec4f l, vec4f r)
+{
+ return vec4f(l.m128_f32[0]*r.m128_f32[0], l.m128_f32[1]*r.m128_f32[1], l.m128_f32[2]*r.m128_f32[2], l.m128_f32[3]*r.m128_f32[3]);
+}
+
+static MECANIM_FORCE_INLINE vec4f Vdiv( vec4f l, vec4f r)
+{
+ return vec4f(l.m128_f32[0]/r.m128_f32[0], l.m128_f32[1]/r.m128_f32[1], l.m128_f32[2]/r.m128_f32[2], l.m128_f32[3]/r.m128_f32[3]);
+}
+
+#define Vmadd( a, b, c) vec4f(a.m128_f32[0]*b.m128_f32[0] + c.m128_f32[0], a.m128_f32[1]*b.m128_f32[1] + c.m128_f32[1], a.m128_f32[2]*b.m128_f32[2] + c.m128_f32[2], a.m128_f32[3]*b.m128_f32[3] + c.m128_f32[3])
+#define Vmsub( a, b, c) vec4f(a.m128_f32[0]*b.m128_f32[0] - c.m128_f32[0], a.m128_f32[1]*b.m128_f32[1] - c.m128_f32[1], a.m128_f32[2]*b.m128_f32[2] - c.m128_f32[2], a.m128_f32[3]*b.m128_f32[3] - c.m128_f32[3])
+#define Vneg(r) vec4f(-r.m128_f32[0], -r.m128_f32[1], -r.m128_f32[2], -r.m128_f32[3])
+
+// Vector sgn: return -1, 1
+#define Vsgn(r) vec4f( r.m128_f32[0] < 0 ? -1.f : 1.f, r.m128_f32[1] < 0 ? -1.f : 1.f, r.m128_f32[2] < 0 ? -1.f : 1.f, r.m128_f32[3] < 0 ? -1.f : 1.f)
+
+// Vector sgn: return -1, 0, 1
+static MECANIM_FORCE_INLINE vec4f Vsign(vec4f r)
+{
+ return vec4f( r.m128_f32[0] < 0 ? -1.f : r.m128_f32[0] > 0 ? 1.f : 0.f,
+ r.m128_f32[1] < 0 ? -1.f : r.m128_f32[1] > 0 ? 1.f : 0.f,
+ r.m128_f32[2] < 0 ? -1.f : r.m128_f32[2] > 0 ? 1.f : 0.f,
+ r.m128_f32[3] < 0 ? -1.f : r.m128_f32[3] > 0 ? 1.f : 0.f);
+}
+
+#define Vinc(r) Vadd( (r), Vone())
+#define Vdec(r) Vsub( (r), Vone())
+#define Vabs(r) vec4f( abs(r.m128_f32[0]), abs(r.m128_f32[1]), abs(r.m128_f32[2]), abs(r.m128_f32[3]))
+#define Vmax( l, r) vec4f( l.m128_f32[0] > r.m128_f32[0] ? l.m128_f32[0] : r.m128_f32[0], l.m128_f32[1] > r.m128_f32[1] ? l.m128_f32[1] : r.m128_f32[1], l.m128_f32[2] > r.m128_f32[2] ? l.m128_f32[2] : r.m128_f32[2], l.m128_f32[3] > r.m128_f32[3] ? l.m128_f32[3] : r.m128_f32[3])
+#define Vmin( l, r) vec4f( l.m128_f32[0] < r.m128_f32[0] ? l.m128_f32[0] : r.m128_f32[0], l.m128_f32[1] < r.m128_f32[1] ? l.m128_f32[1] : r.m128_f32[1], l.m128_f32[2] < r.m128_f32[2] ? l.m128_f32[2] : r.m128_f32[2], l.m128_f32[3] < r.m128_f32[3] ? l.m128_f32[3] : r.m128_f32[3])
+
+// Return the largest of the 4 component
+static MECANIM_FORCE_INLINE vec4fs Vlargest(vec4f r)
+{
+ r = Vmax(r, Vswizzle<kYZWX>::rhs(r));
+ r = Vmax(r, Vswizzle<kZWXY>::rhs(r));
+ return r.m128_f32[0];
+}
+
+// Return the smallest of the 4 component
+static MECANIM_FORCE_INLINE vec4fs Vsmallest(vec4f r)
+{
+ r = Vmin(r, Vswizzle<kYZWX>::rhs(r));
+ r = Vmin(r, Vswizzle<kZWXY>::rhs(r));
+ return r.m128_f32[0];;
+}
+
+static MECANIM_FORCE_INLINE vec4fs Vsum(vec4f r)
+{
+ r = Vadd(r, Vswizzle<kYZWX>::rhs(r) );
+ r = Vadd(r, Vswizzle<kZWXY>::rhs(r) );
+ r = Vswizzle<kXXXX>::rhs(r);
+ return r.m128_f32[0];
+}
+
+#define Vdot( l, r) Vsum( Vmul((l), (r)) )
+
+#define Vsqrt(r) vec4f( sqrt(r.m128_f32[0]), sqrt(r.m128_f32[1]), sqrt(r.m128_f32[2]), sqrt(r.m128_f32[3]))
+
+static MECANIM_FORCE_INLINE vec4f Vrsqrt(vec4f r)
+{
+ vec4f const e = Vdiv(vec4f(1.f), Vsqrt(r));
+ return Vmul(Vmul(e, Vsub(vec4f(3.0f), Vmul(Vmul(e,e),r))), vec4f(.5f));
+}
+
+static MECANIM_FORCE_INLINE vec4f Vrcp(vec4f r)
+{
+ return Vdiv(vec4f(1.f), r );
+}
+
+
+// Merge 4 vector low bytes
+#define Vcombine(x,y,z,w) vec4f(x.m128_f32[0], y.m128_f32[0], z.m128_f32[0], w.m128_f32[0])
+
+// Vector comparison
+#define Vcmpeq( a, b) vec4b(a.m128_f32[0] == b.m128_f32[0], a.m128_f32[1] == b.m128_f32[1], a.m128_f32[2] == b.m128_f32[2], a.m128_f32[3] == b.m128_f32[3])
+#define Vcmpneq( a, b) vec4b(a.m128_f32[0] != b.m128_f32[0], a.m128_f32[1] != b.m128_f32[1], a.m128_f32[2] != b.m128_f32[2], a.m128_f32[3] != b.m128_f32[3])
+#define Vcmpgt( a, b) vec4b(a.m128_f32[0] > b.m128_f32[0], a.m128_f32[1] > b.m128_f32[1], a.m128_f32[2] > b.m128_f32[2], a.m128_f32[3] > b.m128_f32[3])
+#define Vcmpge( a, b) vec4b(a.m128_f32[0] >= b.m128_f32[0], a.m128_f32[1] >= b.m128_f32[1], a.m128_f32[2] >= b.m128_f32[2], a.m128_f32[3] >= b.m128_f32[3])
+#define Vcmplt( a, b) vec4b(a.m128_f32[0] < b.m128_f32[0], a.m128_f32[1] < b.m128_f32[1], a.m128_f32[2] < b.m128_f32[2], a.m128_f32[3] < b.m128_f32[3])
+#define Vcmple( a, b) vec4b(a.m128_f32[0] <= b.m128_f32[0], a.m128_f32[1] <= b.m128_f32[1], a.m128_f32[2] <= b.m128_f32[2], a.m128_f32[3] <= b.m128_f32[3])
+
+#define Vsel( c, a, b) vec4f(c.m128_f32[0] ? a.m128_f32[0] : b.m128_f32[0], c.m128_f32[1] ? a.m128_f32[1] : b.m128_f32[1], c.m128_f32[2] ? a.m128_f32[2] : b.m128_f32[2], c.m128_f32[3] ? a.m128_f32[3] : b.m128_f32[3])
+
+// vector logics
+#define Vnot(r) vec4b(!r.m128_f32[0], !r.m128_f32[1], !r.m128_f32[2], !r.m128_f32[3])
+#define Vxnor( a, b) vec4b(!(a.m128_f32[0] ^ b.m128_f32[0]), !(a.m128_f32[1] ^ b.m128_f32[1]), !(a.m128_f32[2] ^ b.m128_f32[2]), !(a.m128_f32[3] ^ b.m128_f32[3]))
+#define Vxor( a, b) vec4b(a.m128_f32[0] ^ b.m128_f32[0], a.m128_f32[1] ^ b.m128_f32[1], a.m128_f32[2] ^ b.m128_f32[2], a.m128_f32[3] ^ b.m128_f32[3])
+#define Vand( a, b) vec4b(a.m128_f32[0] && b.m128_f32[0], a.m128_f32[1] && b.m128_f32[1], a.m128_f32[2] && b.m128_f32[2], a.m128_f32[3] && b.m128_f32[3])
+#define Vor( a, b) vec4b(a.m128_f32[0] || b.m128_f32[0], a.m128_f32[1] || b.m128_f32[1], a.m128_f32[2] || b.m128_f32[2], a.m128_f32[3] || b.m128_f32[3])
+#define Vall(a) (a.m128_f32[0] && a.m128_f32[1] && a.m128_f32[2] && a.m128_f32[3])
+#define Vany(a) (a.m128_f32[0] || a.m128_f32[1] || a.m128_f32[2] || a.m128_f32[3])
+
+#endif // SIMD_FPU_H \ No newline at end of file
diff --git a/Runtime/Math/Simd/intrinsic.h b/Runtime/Math/Simd/intrinsic.h
new file mode 100644
index 0000000..eb58c2e
--- /dev/null
+++ b/Runtime/Math/Simd/intrinsic.h
@@ -0,0 +1,184 @@
+#ifndef SIMD_INTRINSIC_H
+#define SIMD_INTRINSIC_H
+
+/* Here the Math library back-end interface
+ When you declare a function always returns results by values, you want to be sure that simd register stay in register, otherwise you may get poor performance if the CPU need to push back the register into memory
+ Vector data is declared purely, ex: typedef __m128 vec4f. most compile won't recognize encapsulated vector type in class and thus generate more temporary and push back vector in memory.
+
+ to support a new platform you need at least to support this function set
+
+ typedef __m128 vec4f; // vector 4 float packed
+ typedef __m128 vec4fs; // vector 4 float scalar
+ typedef __m128 vec4b; // vector 4 bool packed
+ typedef __m128 vec4bs; // vector 4 bool scalar
+
+ #define Vzero()
+ #define Vone()
+ #define Vpermute(v, mask)
+ #define Vmove(l, r)
+
+ // This template is part of the back-end because some instruction set support some swizzle operation that could be specialized, like xbox vmx rotate instruction that is use in dot product
+ template<int SWZ> struct Vswizzle
+ {
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f r)
+ {
+ return Vpermute(r, SWZ);
+ }
+
+ static MECANIM_FORCE_INLINE vec4f lhs(vec4f l, vec4f r)
+ {
+ return Vswizzle<SWZ>::rhs(Vmove(Vswizzle<SWZ>::rhs(l), r));
+ }
+ };
+
+
+ // Aligned store, store vector at adress base as 4 float
+ #define Vstorepf(v, base, offset)
+
+ // Return component x as a float
+ #define Vstoresf(r)
+
+ // Return component x as a bool
+ #define Vstoresb(r)
+
+ // Aligned store, store vector at adress base as 4 bool
+ #define Vstorepb(vec4f v, bool* r)
+
+ // Aligned load, load 4 float at adress v in vector register
+ #define Vloadpf(v, offset)
+
+ // Load float value in vector register and replicate value in all component
+ #define Vloadsf(s)
+
+ // Load bool value in vector register and replicate value in all component
+ #define Vloadsb(s)
+
+ // Load 4 float value in vector register
+ #define Vload4sf(x, y, z, w)
+
+ // Load 4 bool value in vector register
+ #define Vload4sb( x, y, z, w)
+
+ #define Vadd(l, r)
+ #define Vsub( l, r)
+ #define Vmul( l, r)
+ #define Vdiv( l, r)
+ #define Vmadd( a, b, c)
+ #define Vmsub( a, b, c)
+ #define Vneg(r)
+
+ // Vector sgn: return -1, 1
+ #define Vsgn(r)
+
+ // Vector sgn: return -1, 0, 1
+ #define Vsign(r)
+
+ #define Vinc(r)
+ #define Vdec(r)
+ #define Vabs(r)
+ #define Vmax( l, r)
+ #define Vmin( l, r)
+
+ // Return the largest of the 4 component
+ #define Vlargest(r)
+
+ // Return the smallest of the 4 component
+ #define Vsmallest(r)
+ #define Vsum(r)
+ #define Vdot( l, r)
+ #define Vsqrt(r)
+
+ #define Vrsqrt(r)
+ #define Vrcp(r)
+
+ // Merge 4 vector low bytes
+ #define Vcombine(x,y,z,w)
+
+ // Vector comparison
+ #define Vcmpeq( a, b)
+ #define Vcmpneq( a, b)
+ #define Vcmpgt( a, b)
+ #define Vcmpge( a, b)
+ #define Vcmplt( a, b)
+ #define Vcmple( a, b)
+
+ #define Vsel( c, a, b)
+
+ // vector logics
+ #define Vnot(r)
+ #define Vxnor( a, b)
+ #define Vxor( a, b)
+ #define Vand( a, b)
+ #define Vor( a, b)
+ #define Vall(a)
+ #define Vany( a)
+
+*/
+#if defined(__INTEL_COMPILER) || defined(__ICL) || defined(_MSC_VER)
+ #include <cstddef>
+ #define ATTRIBUTE_ALIGN(a) __declspec(align(a))
+ #define ALIGN4F 16
+ #define MECANIM_FORCE_INLINE __forceinline
+#elif defined(__GNUC__) || defined(__clang__)
+ #include <cstddef>
+
+ #ifndef __has_attribute
+ #define __has_attribute(x) 0
+ #endif
+
+ #if ((__GNUC__ >= 3) && (__GNUC_MINOR__ >= 1)) || (__GNUC__ >= 4) || __has_attribute(always_inline)
+ #ifdef _DEBUG
+ #ifndef MECANIM_FORCE_INLINE
+ #define MECANIM_FORCE_INLINE inline
+ #endif
+ #else
+ #ifndef MECANIM_FORCE_INLINE
+ #define MECANIM_FORCE_INLINE inline __attribute__((always_inline))
+ #endif
+ #endif
+ #endif
+
+ #if defined(__GNUC__) || __has_attribute(aligned)
+ #define ATTRIBUTE_ALIGN(a) __attribute__ ((aligned(a)))
+ #endif
+
+ #define ALIGN4F 16
+#endif
+
+#ifndef MECANIM_FORCE_INLINE
+ #define MECANIM_FORCE_INLINE inline
+#endif
+
+#ifndef ATTRIBUTE_ALIGN
+ #define ATTRIBUTE_ALIGN(a)
+#endif
+
+#ifndef ALIGN4F
+ #define ALIGN4F 16
+#endif
+
+#if UNITY_FORCE_FPU
+ #include "Runtime/Math/Simd/fpu.h"
+#elif UNITY_XENON
+ #include "Runtime/Math/Simd/xenon.h"
+#elif UNITY_PS3
+ #include "Runtime/Math/Simd/ppu.h"
+#elif UNITY_WIN && UNITY_SUPPORTS_SSE
+ #include "Runtime/Math/Simd/sse.h"
+#elif UNITY_OSX
+ #include "Runtime/Math/Simd/sse.h"
+#elif UNITY_SUPPORTS_NEON && (!UNITY_ANDROID)
+ #include "Runtime/Math/Simd/neon.h"
+#else
+ #include "Runtime/Math/Simd/fpu.h"
+#endif
+
+//#define DEBUG_SIMD_ASSERT_IF 1
+#if DEBUG_SIMD_ASSERT_IF
+ #define SIMD_ASSERT_IF(x) AssertIf(x)
+#else
+ #define SIMD_ASSERT_IF(x)
+#endif
+
+#endif
+
diff --git a/Runtime/Math/Simd/math.h b/Runtime/Math/Simd/math.h
new file mode 100644
index 0000000..43a8837
--- /dev/null
+++ b/Runtime/Math/Simd/math.h
@@ -0,0 +1,678 @@
+#ifndef SIMD_MATH_H
+#define SIMD_MATH_H
+
+#include <cmath>
+
+// Standard macro define in cmath
+#ifndef M_EPSF
+#define M_EPSF 1e-6f
+#endif
+#ifndef M_PIf
+#define M_PIf 3.14159265358979323846f
+#endif
+#ifndef M_PI_2f
+#define M_PI_2f 1.57079632679489661923f
+#endif
+#ifndef M_PI_4f
+#define M_PI_4f 0.785398163397448309616f
+#endif
+#ifndef M_1_PIf
+#define M_1_PIf 0.318309886183790671538f
+#endif
+#ifndef M_2_PIf
+#define M_2_PIf 0.636619772367581343076f
+#endif
+#ifndef M_DEG_2_RADf
+#define M_DEG_2_RADf 0.0174532925f
+#endif
+#ifndef M_RAD_2_DEGf
+#define M_RAD_2_DEGf 57.295779513f
+#endif
+
+#include "Runtime/Math/Simd/float4.h"
+#include "Runtime/Math/Simd/bool4.h"
+
+namespace math
+{
+
+static inline bool all(bool4 const& r);
+static inline bool any(bool4 const& r);
+template <typename T> static inline T clamp(T const& v, T const& a, T const& b);
+static inline float cond(bool c, float const& a, float const& b);
+static inline int cond(bool c, int const& a, int const& b);
+static inline float cubic(float const& a, float const& b, float const& c, float const& d, float const& u);
+static inline float4 cubic(float4 const& a, float4 const& b, float4 const& c, float4 const& d, float4 const& u);
+static inline float4 cross(float4 const& a, float4 const& b);
+static inline float degrees(float const& deg);
+static inline float4 degrees(float4 const& deg);
+static inline float1 dot(float4 const& l, float4 const& r);
+static inline float1 dot(float4 const& r);
+static inline float1 length(float4 const& r);
+static inline float lerp(float const& a, float const& b, float x);
+static inline float1 lerp(float1 const& a, float1 const& b, float1 const& x);
+static inline float4 lerp(float4 const& a, float4 const& b, float1 const& x);
+static inline float4 lerp(float4 const& a, float4 const& b, float4 const& x);
+template <typename T> static inline T maximum(T const& a, T const& b);
+template <typename T> static inline T minimum(T const& a, T const& b);
+static inline float1 maximum(float4 const& r);
+static inline float1 minimum(float4 const& r);
+static inline float4 normalize(float4 const& r);
+static inline float pow(float const& r, float const& e);
+static inline float4 pow(float4 const& r, float1 const& e);
+static inline float radians(float const& deg);
+static inline float4 radians(float4 const& deg);
+static inline float4 rcp(float4 const& r);
+static inline float1 rcp(float1 const& r);
+static inline float4 rsqrt(float4 const& r );
+static inline float saturate(float const& r);
+static inline float1 saturate(float1 const& r);
+static inline float4 saturate(float4 const& r);
+static inline float4 scaleIdentity();
+static inline float4 scaleWeight(float4 const& s, float1 const& w);
+static inline void sincos(float4 const& u, float4& s, float4& c);
+static inline void sincos(float1 const& u, float1& s, float1& c);
+static inline void sincose(float4 const& u, float4& s, float4& c);
+static inline void sincose(float1 const& u, float1& s, float1& c);
+static inline float sgn(float const& r);
+static inline float1 sgn(float1 const& r);
+template <typename T> static inline vecexp4<vec4> sgn(vecexp4<T> const& x);
+template <typename T> static inline vecexp1<vec4> sgn(vecexp1<T> const& x);
+static inline float sign(float const& r);
+static inline float4 sign(float4 const& r);
+static inline float smoothstep( float min, float max, float x);
+static inline float smoothpulse( float minmin, float minmax, float maxmin, float maxmax, float x);
+static inline float1 sqrt(float1 const& r);
+static inline float4 sqrt(float4 const& r);
+static inline float1 sum(float4 const& r);
+static inline float4 vector(float4 const& v);
+static inline float unrollangle(float angleRef, float angle);
+static inline float4 load(float const* v);
+static inline void store(float4 const& v, float* r);
+static inline void store(bool4 const& v, bool* r);
+
+
+static inline float abs(const float &r)
+{
+ return std::abs(r);
+}
+
+static inline float cos(float const& theta)
+{
+ return std::cos(theta);
+}
+
+static inline float rcp(const float &r)
+{
+ return 1.f/r;
+}
+
+static inline float rsqrt(const float& r)
+{
+ return 1.f/std::sqrt(r);
+}
+
+static inline float sin(float const& theta)
+{
+ return std::sin(theta);
+}
+
+static inline void sincos(float const& u, float& s, float& c)
+{
+ s = sin(u);
+ c = cos(u);
+}
+
+static inline float tan(float const& theta)
+{
+ return std::tan(theta);
+}
+
+static inline float atan(float const& t)
+{
+ return std::atan(t);
+}
+
+static inline float sqrt(const float& r)
+{
+ return std::sqrt(r);
+}
+
+static inline float modf(float x, float &ip)
+{
+#if UNITY_FLASH
+ float intPart;
+ __asm __volatile__("%[RES] = (%[FARG] < 0 ? Math.ceil(%[FARG]) : Math.floor(%[FARG]));//modf" : [RES] "=f" (intPart) : [FARG] "f" (x));
+ ip = intPart;
+ return x-intPart;
+#else
+ return std::modf(x, &ip);
+#endif
+}
+
+static inline float fmod(float x, float y)
+{
+ return std::fmod(x,y);
+}
+
+static inline float pow(const float& x,const float& y)
+{
+ return std::pow(x,y);
+}
+
+template <typename T> static inline vecexp4<vec4> abs(vecexp4<T> const& x)
+{
+ return vecexp4<vec4>( Vabs( x.eval() ) );
+}
+
+template <typename T> static inline vecexp1<T> abs(vecexp1<T> const& x)
+{
+ return vecexp1<T>( Vabs( x.eval() ) );
+}
+
+static inline float1 abs(float1 const& x)
+{
+ return float1( Vabs( x.eval() ) );
+}
+
+static inline bool all(bool4 const& r)
+{
+ return Vall(r.v);
+}
+static inline bool any(bool4 const& r)
+{
+ return Vany(r.v);
+}
+
+// x clamped to the range [a, b] as follows:
+// Returns a if x is less than a.
+// Returns b if x is greater than b.
+// Returns x otherwise.
+template <typename T> static inline T clamp(T const& v, T const& a, T const& b)
+{
+ return minimum(b, maximum(a, v));
+}
+
+template <typename L, typename R> static inline vecexp4<vec4> cond(bool4 const& c, vecexp4<L> const& l, vecexp4<R> const& r)
+{
+ return vecexp4<vec4>( Vsel(c.v, l.eval(), r.eval()) );
+}
+
+template <typename L, typename R> static inline vecexp4<vec4> cond(bool1 const& c, vecexp4<L> const& l, vecexp4<R> const& r)
+{
+ return vecexp4<vec4>( Vsel(c.s, l.eval(), r.eval()) );
+}
+
+template <typename L, typename R> static inline vecexp1<vec4> cond(bool1 const& c, vecexp1<L> const& l, vecexp1<R> const& r)
+{
+ return vecexp4<vec4>( Vsel(c.s, l.eval(), r.eval()) );
+}
+
+static inline float cond(bool c, float const& a, float const& b)
+{
+ return c ? a : b;
+}
+
+static inline int cond(bool c, int const& a, int const& b)
+{
+ return int(b + (-int(c) & (a - b)));
+}
+
+static inline int cond(bool c, long int const& a, long int const& b)
+{
+ typedef long int long_int;
+ return long_int(b + (-long_int(c) & (a - b)));
+}
+
+static inline unsigned long cond(bool c, unsigned long const& a, unsigned long const& b)
+{
+ return b + (- long(c) & (a - b));
+}
+
+static inline unsigned int cond(bool c, unsigned int const& a, unsigned int const& b)
+{
+ return b + (- int(c) & (a - b));
+}
+
+// De Casteljau construction of bezier
+static inline float cubic(float const& a, float const& b, float const& c, float const& d, float const& u)
+{
+ const float ab = lerp(a,b,u);
+ const float bc = lerp(b,c,u);
+ const float cd = lerp(c,d,u);
+ const float abc = lerp(ab,bc,u);
+ const float bcd = lerp(bc,cd,u);
+ return lerp(abc, bcd, u);
+}
+
+static inline float4 cubic(float4 const& a, float4 const& b, float4 const& c, float4 const& d, float4 const& u)
+{
+ const float4 ab = lerp(a,b,u);
+ const float4 bc = lerp(b,c,u);
+ const float4 cd = lerp(c,d,u);
+ const float4 abc = lerp(ab,bc,u);
+ const float4 bcd = lerp(bc,cd,u);
+ return lerp(abc, bcd, u);
+}
+
+static inline float4 cross(float4 const& a, float4 const& b)
+{
+ return float4(a.yzxw()*b.zxyw() - a.zxyw()*b.yzxw());
+}
+
+static inline float degrees(float const& rad)
+{
+ return M_RAD_2_DEGf*rad;
+}
+
+static inline float4 degrees(float4 const& rad)
+{
+ return float1(M_RAD_2_DEGf)*rad;
+}
+
+static inline float1 degrees(float1 const& rad)
+{
+ return float1(M_RAD_2_DEGf)*rad;
+}
+
+static inline float1 dot(float4 const& l, float4 const& r)
+{
+ return float1( Vdot(l.eval(), r.eval()) );
+}
+
+static inline float1 dot(float4 const& r)
+{
+ return float1( Vdot(r.eval(), r.eval()) );
+}
+
+static inline float1 length(float4 const& r)
+{
+ return float1(Vsqrt( Vdot(r.eval(), r.eval()) ));
+}
+
+static inline float lerp(float const& a, float const& b, float x)
+{
+ return a + x*(b - a);
+}
+
+static inline float1 lerp(float1 const& a, float1 const& b, float1 const& x)
+{
+ return a + x*(b - a);
+}
+
+static inline float4 lerp(float4 const& a, float4 const& b, float1 const& x)
+{
+ return a + x*(b - a);
+}
+
+static inline float4 lerp(float4 const& a, float4 const& b, float4 const& x)
+{
+ return a + x*(b - a);
+}
+
+template <typename T> static inline T maximum(T const& a, T const& b)
+{
+ return cond(a > b, a, b);
+}
+
+static inline float1 maximum(float4 const& r)
+{
+ return float1( Vlargest(r.eval()) );
+}
+
+template <typename T> static inline T minimum(T const& a, T const& b)
+{
+ return cond(a < b, a, b);
+}
+
+static inline float1 minimum(float4 const& r)
+{
+ return float1( Vsmallest(r.eval()) );
+}
+
+static inline float4 normalize(float4 const& r)
+{
+ return float4( Vmul(r.eval(), Vrsqrt(Vdot(r.eval(), r.eval()) ) ));
+}
+
+static inline float4 pow(float4 const& r, float1 const& e)
+{
+ float e1 = e.tofloat();
+
+ return float4( std::pow( r.x().tofloat(), e1), std::pow( r.y().tofloat(), e1), std::pow( r.z().tofloat(), e1), std::pow( r.w().tofloat(), e1));
+}
+
+static inline float radians(float const& deg)
+{
+ return M_DEG_2_RADf*deg;
+}
+
+static inline float4 radians(float4 const& deg)
+{
+ return float1(M_DEG_2_RADf)*deg;
+}
+
+static inline float4 rcp(float4 const& r)
+{
+ return float4(Vrcp(r.eval()));
+}
+
+static inline float1 rcp(float1 const& r)
+{
+ return float1(Vrcp(r.eval()));
+}
+
+static inline float4 rsqrt(float4 const& r)
+{
+ return float4(Vrsqrt(r.eval()));
+}
+
+static inline float saturate(float const& r)
+{
+ return clamp(r, 0.f, 1.f);
+}
+
+static inline float1 saturate(float1 const& r)
+{
+ return float1(Vmin( Vmax(r.eval(), Vzero()), Vone()));
+}
+
+static inline float4 saturate(float4 const& r)
+{
+ return float4(Vmin( Vmax(r.eval(), Vzero()), Vone()));
+}
+
+static inline float4 scaleIdentity()
+{
+ return float4::one();
+}
+
+static inline float4 scaleWeight(float4 const& s, float1 const& w)
+{
+ float4 s_abs = math::abs(s);
+ float4 s_sng = math::sgn(s);
+
+ return s_sng * pow( s_abs, w);
+}
+
+static inline float4 scaleBlend(float4 const& sa, float4 const& sb,float1 const& w)
+{
+ const float4 saw = scaleWeight(sa, float1::one() - w);
+ const float4 sbw = scaleWeight(sb, w);
+ const float4 s_sng = math::sgn( cond( w > float1(.5), sb, sa) );
+ return s_sng * math::abs(saw * sbw);
+}
+
+// return -1 if r < 0
+// return 1 if r >= 0
+static inline float sgn(float const& r)
+{
+ return cond(r >= 0.f, 1.f, -1.f);
+}
+
+// return -1 if r < 0
+// return 1 if r >= 0
+static inline float1 sgn(float1 const& r)
+{
+ return float1(Vsgn(r.eval()));
+}
+
+// return -1 if r < 0
+// return 1 if r >= 0
+template <typename T> static inline vecexp4<vec4> sgn(vecexp4<T> const& x)
+{
+ return vecexp4<vec4>( Vsgn( x.eval()) );
+}
+
+// return -1 if r < 0
+// return 1 if r >= 0
+template <typename T> static inline vecexp1<vec4> sgn(vecexp1<T> const& x)
+{
+ return vecexp1<vec4>( Vsgn( x.eval() ) );
+}
+
+// return -1 if r < 0
+// return 0 if r == 0
+// return 1 if r > 0
+static inline float sign(float const& r)
+{
+ return cond( r > 0, 1.f, cond( r < 0, -1.f, 0.f));
+}
+
+// return -1 if r < 0
+// return 0 if r == 0
+// return 1 if r > 0
+static inline float4 sign(float4 const& r)
+{
+ return float4(Vsign(r.eval()));
+}
+
+static inline float4 smoothClamp(float4 const& v, float4 const& m, float1 const& r)
+{
+ return cond(v-m>float1::zero(),m+r*((v-m)/(v-m+r)),v);
+}
+
+static inline float smoothstep( float min, float max, float x)
+{
+ x = math::clamp(x, min, max);
+ return -2.f * math::pow((x-min)/(max-min), 3.f) + 3.f * math::pow((x-min)/(max-min), 2.f);
+}
+
+static inline float smoothpulse( float minmin, float minmax, float maxmin, float maxmax, float x)
+{
+ return smoothstep(minmin,minmax,x) - smoothstep(maxmin,maxmax,x);
+}
+
+static inline float1 sqrt(float1 const& r)
+{
+ return float1(Vsqrt(r.eval()));
+}
+
+static inline float4 sqrt(float4 const& r)
+{
+ return float4(Vsqrt(r.eval()));
+}
+
+static inline float1 sum(float4 const& r)
+{
+ return float1(Vsum(r.eval()));
+}
+
+static inline float1 triangleAngle(math::float1 const& aLen, math::float1 const& aLen1, math::float1 const& aLen2)
+{
+ math::float1 c = clamp<float1>((aLen1*aLen1 + aLen2*aLen2 - aLen*aLen) / (aLen1*aLen2) / float1(2.f), -float1::one() , float1::one());
+ return math::float1(acos(c.tofloat()));
+}
+
+static inline float4 vector(float4 const& v)
+{
+ constant_float4( mask, 1.f,1.f,1.f,0.f);
+ return v*mask;
+}
+
+static inline float4 vector(float const& x, float const& y, float const& z)
+{
+ return float4(x, y, z, 0);
+}
+
+static inline float unrollangle(float angleRef, float angle)
+{
+ float i;
+ float f = math::modf( (angleRef-angle)/(2.f*M_PIf), i);
+ return angle + ( (i+(math::abs(f) > 0.5f ? math::sgn(f) * 1 : 0)) * 2.f * M_PIf);
+}
+
+static inline float4 doubleAtan(float4 const& v)
+{
+ float ATTRIBUTE_ALIGN(ALIGN4F) av[4];
+
+ store(v, av);
+
+ return float4(2.0f*atan(av[0]),2.0f*atan(av[1]),2.0f*atan(av[2]),2.0f*atan(av[3]));
+}
+
+
+// between range [-pi/2, pi/2] the maximum error is 8.186e-4
+static inline vec4f cos_estimate(vec4f x)
+{
+ // cos(x) = 1 - (c2*x^2) + (c4*x^4) - (c6*x^6)
+ // cos(x) = 1 + (-c2*x^2) + (c4*x^4) + (-c6*x^6) // 3 mul and 3 mul add
+ // let's bake sign into constant to remove some complexity
+ cvec4fs(c2, -0.5f);
+ cvec4fs(c4, 4.166666666667e-2f);
+ cvec4fs(c6, -1.38888889e-3f);
+
+ // Use horner form to reduce the polynomial instruction count
+ // cos(x) = 1 + x^2*(c2 + x^2*(c4 + x^2*(c6))) // 1 mul and 3 mul add
+ vec4f x2 = Vmul(x,x);
+ return Vmadd(Vmadd(Vmadd(c6, x2, c4), x2, c2), x2, Vone());
+}
+
+
+// between range [-pi/2, pi/2] the maximum error is 9.1e-5
+static inline vec4f sin_estimate(vec4f x)
+{
+ // sin(x) = x - (c3*x^3) + (c5*x^5) - (c7*x^7)
+ // sin(x) = x + (-c3*x^3) + (c5*x^5) + (-c7*x^7) // 4 mul and 3 mul add
+ // let's bake sign into constant to remove some complexity
+ cvec4fs(c3, -0.166666567325592041015625f);
+ cvec4fs(c5, 8.33220803e-3f);
+ cvec4fs(c7, -1.95168955e-4f);
+
+ // Use horner form to reduce the polynomial instruction count
+ // sin(x) = x * ( 1 + x^2*(c3 + x^2*(c5 + x^2*c7))) // 2 mul and 3 mul add
+ vec4f x2 = Vmul(x,x);
+ return Vmul(x, Vmadd(Vmadd(Vmadd(c7, x2, c5), x2, c3), x2, Vone()));
+}
+
+static inline float4 sin_est(float4 const& x)
+{
+ return float4( sin_estimate( x.eval() ) );
+}
+
+static inline float4 cos_est(float4 const& x)
+{
+ return float4( cos_estimate( x.eval() ) );
+}
+
+static inline void sincos(float4 const& u, float4& s, float4& c)
+{
+ float ATTRIBUTE_ALIGN(ALIGN4F) sv[4];
+ float ATTRIBUTE_ALIGN(ALIGN4F) cv[4];
+ float ATTRIBUTE_ALIGN(ALIGN4F) uv[4];
+
+ store(u, uv);
+
+ sincos(uv[0], sv[0], cv[0]);
+ sincos(uv[1], sv[1], cv[1]);
+ sincos(uv[2], sv[2], cv[2]);
+ sincos(uv[3], sv[3], cv[3]);
+
+ s = load(sv);
+ c = load(cv);
+}
+
+static inline void sincos(float1 const& u, float1& s, float1& c)
+{
+ float sv;
+ float cv;
+
+ sincos(u.tofloat(), sv, cv);
+
+ s = float1(sv);
+ c = float1(cv);
+}
+
+static inline void sincos_est(float4 const& u, float4& s, float4& c)
+{
+ s = sin_est(u);
+ c = cos_est(u);
+}
+
+static inline void sincos_est(float1 const& u, float1& s, float1& c)
+{
+ s = float1( sin_estimate( u.eval() ) );
+ c = float1( cos_estimate( u.eval() ) );
+}
+
+static inline float4 tan(float4 const& x)
+{
+ vec4f x2,x3;
+
+ // Compute x^2 and x^3
+ //
+ x2 = Vmul(x.eval(),x.eval());
+ x3 = Vmul(x2,x.eval());
+
+ // Compute both the sin and cos of the angles
+ // using a polynomial expression:
+ // cx = 1.0f + x2 * (C0 * x2 + C1), and
+ // sx = xl + x3 * S0
+ //
+ cvec4fs(c0, 0.0097099364f);
+ cvec4fs(c1, -0.4291161787f);
+ cvec4fs(s0, -0.0957822992f);
+
+ vec4f ct2 = Vmadd(c0,x2,c1);
+
+ vec4f cx = Vmadd(ct2,x2, Vone());
+ vec4f sx = Vmadd(s0,x3, x.eval());
+
+ return float4(Vdiv(sx,cx));
+}
+
+static inline float4 atan(float4 const& x)
+{
+ //x - (x^3)/3 + (x^5)/5 - (x^7)/7 + ...
+
+ cvec4fs(c3, 3.f);
+ cvec4fs(c5, 5.f);
+ cvec4fs(c7, 7.f);
+ vec4f x2 = Vmul(x.eval(),x.eval());
+ vec4f x3 = Vmul(x2,x.eval());
+ vec4f x5 = Vmul(x3,x2);
+ vec4f x7 = Vmul(x5,x2);
+
+ return float4(Vsub( x.eval(), Vadd(Vdiv( x3, c3), Vsub(Vdiv(x5,c5), Vdiv(x7,c7)))));
+}
+
+static inline float halfTan(float a)
+{
+ //float x = math::fmod(0.5f*abs(a)+M_PI_2,float(M_PI));
+ float x1 = (0.5f*abs(a)+M_PI_2f);
+ return tan(clamp(sign(a)*(x1-M_PI_2f),-M_PI_2f+M_EPSF,M_PI_2f-M_EPSF));
+}
+
+static inline float4 halfTan(float4 const& a)
+{
+ static const float4 nM_PI_2(-M_PI_2f+M_EPSF);
+ static const float4 pM_PI_2( M_PI_2f+M_EPSF);
+
+ float4 x = float1(0.5f) * abs(a) + float1(M_PI_2f);
+ return tan( math::clamp<float4>( sign(a) * (x-float4(M_PI_2f)), nM_PI_2, pM_PI_2 ));
+}
+
+static inline float4 mirror(float4 const& t)
+{
+ constant_float4(mirrorT,-1,1,1,1);
+ return t * mirrorT;
+}
+
+static inline float4 load(float const* v)
+{
+ return float4(Vloadpf(v, 0));
+}
+
+static inline void store(float4 const& v, float* r)
+{
+ Vstorepf(v.eval(), r, 0);
+}
+
+static inline void store(bool4 const& v, bool* r)
+{
+ Vstorepb(v.v, r);
+}
+
+}
+
+#endif
+
diff --git a/Runtime/Math/Simd/neon.h b/Runtime/Math/Simd/neon.h
new file mode 100644
index 0000000..08196f9
--- /dev/null
+++ b/Runtime/Math/Simd/neon.h
@@ -0,0 +1,548 @@
+#ifndef SIMD_NEON_H
+#define SIMD_NEON_H
+
+#include <arm_neon.h>
+
+typedef float32x4_t vec4f;
+typedef float32x4_t vec4fs;
+typedef uint32x4_t vec4b;
+typedef uint32x4_t vec4bs;
+
+#define SWZ_MASK(x, y, z, w) (((w) << 6) | ((z) << 4) | ((y) << 2) | ((x)))
+#define SWZ_X(MASK) (((MASK) >> 0) & 3)
+#define SWZ_Y(MASK) (((MASK) >> 2) & 3)
+#define SWZ_Z(MASK) (((MASK) >> 4) & 3)
+#define SWZ_W(MASK) (((MASK) >> 6) & 3)
+
+//VPERMWI_CONST(x, y, z, w)
+#if UNITY_WINRT
+#define cvec4f(name, x,y,z,w) static const vec4f name = Vload4sf(x, y, z, w)
+#define cvec4b(name, x,y,z,w) static const vec4b name = Vload4sb(x, y, z, w)
+#define cvec4fs(name, s) static const vec4fs name = Vloadsf(s)
+#else
+#define cvec4f(name, x,y,z,w) static const vec4f name = {(x),(y),(z),(w)}
+#define cvec4b(name, x,y,z,w) static const vec4b name = {(x),(y),(z),(w)}
+#define cvec4fs(name, s) static const vec4fs name = {(s),(s),(s),(s)}
+#endif
+
+enum simd_mask
+{
+ kXYZW = SWZ_MASK(0,1,2,3),
+ kXXXX = SWZ_MASK(0,0,0,0),
+ kYYYY = SWZ_MASK(1,1,1,1),
+ kZZZZ = SWZ_MASK(2,2,2,2),
+ kWWWW = SWZ_MASK(3,3,3,3),
+
+ kXWYZ = SWZ_MASK(0,3,1,2),
+ kXZWY = SWZ_MASK(0,2,3,1),
+
+ kYZWX = SWZ_MASK(1,2,3,0),
+ kYXZW = SWZ_MASK(1,0,2,3),
+ kYWZX = SWZ_MASK(1,3,2,0),
+ kYZXW = SWZ_MASK(1,2,0,3),
+ kYXWZ = SWZ_MASK(1,0,3,2),
+
+ kZWXY = SWZ_MASK(2,3,0,1),
+ kZYXW = SWZ_MASK(2,1,0,3),
+ kZYWX = SWZ_MASK(2,1,3,0),
+ kZXYW = SWZ_MASK(2,0,1,3),
+
+ kWYZX = SWZ_MASK(3,1,2,0),
+ kWXZY = SWZ_MASK(3,0,2,1),
+ kWYXZ = SWZ_MASK(3,1,0,2),
+ kWWWZ = SWZ_MASK(3,3,3,2),
+ kWWZZ = SWZ_MASK(3,3,2,2),
+ kWZYX = SWZ_MASK(3,2,1,0),
+};
+
+#define Vzero() vdupq_n_f32(0.0f)
+#define Vone() vdupq_n_f32(1.0f)
+
+#define Vfalse() vdupq_n_u32(0)
+#define Vtrue() vdupq_n_f32(0xFFFFFFFF)
+
+union U { float32x2x2_t f2x2; float32x4_t f4; uint8x8x2_t b8x2; float32_t f[4]; };
+
+#define LHS_FUNTION() \
+ static MECANIM_FORCE_INLINE vec4f lhs(vec4f l, vec4f r)\
+ {\
+ vec4f m = Vmove(rhs(l), r);\
+ return rhs(m);\
+ }
+
+//#define Vpermute(v, mask) v
+//#define Vmove(l, r) vextq_f32(l, r, 0)
+MECANIM_FORCE_INLINE vec4f Vmove(vec4f l, vec4f r)
+{
+ uint32x4_t sel = Vfalse();
+ sel = vsetq_lane_u32(0xFFFFFFFF,sel,0);
+ return vbslq_f32(sel, r, l);
+}
+template<int SWZ> struct Vswizzle;
+/*
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f r)
+ {
+ ::uint32_t lanes[4];
+ uint32x4_t u = vreinterpretq_u32_f32(r);
+ uint32x4_t result;
+
+ lanes[0] = vgetq_lane_u32(u, 0);
+ lanes[1] = vgetq_lane_u32(u, 1);
+ lanes[2] = vgetq_lane_u32(u, 2);
+ lanes[3] = vgetq_lane_u32(u, 3);
+
+ result = vdupq_n_u32(lanes[SWZ_X(SWZ)]);
+ result = vsetq_lane_u32(lanes[SWZ_Y(SWZ)], result, 1);
+ result = vsetq_lane_u32(lanes[SWZ_Z(SWZ)], result, 2);
+ result = vsetq_lane_u32(lanes[SWZ_W(SWZ)], result, 3);
+
+ return vreinterpretq_f32_u32(result);
+ }
+
+ static MECANIM_FORCE_INLINE vec4f lhs(vec4f l, vec4f r)
+ {
+ vec4f m = Vmove(Vswizzle<SWZ>::rhs(l), r);
+ return Vswizzle<SWZ>::rhs(m);
+ }
+};
+*/
+
+template<> struct Vswizzle<kXYZW>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f r)
+ {
+ return r;
+ }
+ static MECANIM_FORCE_INLINE vec4f lhs(vec4f l, vec4f r)
+ {
+ return Vmove(l, r);
+ }
+};
+template<> struct Vswizzle<kXXXX>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f r)
+ {
+ return vdupq_lane_f32(vget_low_f32(r),0);
+ }
+
+ LHS_FUNTION()
+};
+template<> struct Vswizzle<kYYYY>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f r)
+ {
+ return vdupq_lane_f32(vget_low_f32(r),1);
+ }
+
+ LHS_FUNTION()
+};
+template<> struct Vswizzle<kZZZZ>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f r)
+ {
+ return vdupq_lane_f32(vget_high_f32(r),0);
+ }
+ LHS_FUNTION()
+};
+template<> struct Vswizzle<kWWWW>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f r)
+ {
+ return vdupq_lane_f32(vget_high_f32(r),1);
+ }
+ LHS_FUNTION()
+};
+
+template<> struct Vswizzle<kXWYZ>
+ {
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f p)
+ {
+ U u; u.f2x2 = vtrn_f32(vget_low_f32(p), vrev64_f32(vget_high_f32(p))); return u.f4;
+ }
+ LHS_FUNTION()
+};
+
+template<> struct Vswizzle<kXZWY>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f p)
+ {
+ return vcombine_f32(vtrn_f32(vget_low_f32(p), vget_high_f32(p)).val[0], vrev64_f32(vtrn_f32(vget_low_f32(p), vget_high_f32(p)).val[1]));
+ }
+ LHS_FUNTION()
+};
+
+template<> struct Vswizzle<kYZWX>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f p)
+ {
+ return vreinterpretq_f32_u32(vextq_u32(vreinterpretq_u32_f32(p), vreinterpretq_u32_f32(p), 1));
+ }
+ LHS_FUNTION()
+};
+
+template<> struct Vswizzle<kYXZW>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f p)
+ {
+ return vcombine_f32(vrev64_f32(vget_low_f32(p)), vget_high_f32(p));
+ }
+ LHS_FUNTION()
+};
+
+template<> struct Vswizzle<kYWZX>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f p)
+ {
+ return vcombine_f32(vtrn_f32(vget_low_f32(p), vget_high_f32(p)).val[1], vrev64_f32(vtrn_f32(vget_low_f32(p), vget_high_f32(p)).val[0]));
+ }
+ LHS_FUNTION()
+};
+
+template<> struct Vswizzle<kYZXW>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f p)
+ {
+ U u;
+ u.f2x2 = vtrn_f32(vrev64_f32(vget_low_f32(p)), vget_high_f32(p));
+ return u.f4;
+ }
+ LHS_FUNTION()
+};
+
+template<> struct Vswizzle<kYXWZ>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f p)
+ {
+ return vrev64q_f32(p);
+ }
+ LHS_FUNTION()
+};
+
+template<> struct Vswizzle<kZWXY>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f p)
+ {
+ return vcombine_f32(vget_high_f32(p), vget_low_f32(p));
+ }
+ LHS_FUNTION()
+};
+
+template<> struct Vswizzle<kZYXW>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f p)
+ {
+ return vcombine_f32(vrev64_f32(vreinterpret_f32_u32(vext_u32(vreinterpret_u32_f32(vget_low_f32(p)), vreinterpret_u32_f32(vget_high_f32(p)), 1))), vrev64_f32(vreinterpret_f32_u32(vext_u32(vreinterpret_u32_f32(vget_high_f32(p)), vreinterpret_u32_f32(vget_low_f32(p)), 1))));
+ }
+ LHS_FUNTION()
+};
+
+template<> struct Vswizzle<kZYWX>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f p)
+ {
+ return vcombine_f32(vrev64_f32(vreinterpret_f32_u32(vext_u32(vreinterpret_u32_f32(vget_low_f32(p)), vreinterpret_u32_f32(vget_high_f32(p)), 1))), vreinterpret_f32_u32(vext_u32(vreinterpret_u32_f32(vget_high_f32(p)), vreinterpret_u32_f32(vget_low_f32(p)), 1)));
+ }
+ LHS_FUNTION()
+};
+
+template<> struct Vswizzle<kZXYW>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f p)
+ {
+ return vcombine_f32(vrev64_f32(vtrn_f32(vget_low_f32(p), vget_high_f32(p)).val[0]), vtrn_f32(vget_low_f32(p), vget_high_f32(p)).val[1]);
+ }
+ LHS_FUNTION()
+};
+
+template<> struct Vswizzle<kWYZX>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f p)
+ {
+ U u;
+ u.f4 = vrev64q_f32(p);
+ u.f2x2 = vtrn_f32(u.f2x2.val[1], u.f2x2.val[0]);
+ return u.f4;
+ }
+ LHS_FUNTION()
+};
+
+template<> struct Vswizzle<kWXZY>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f p)
+ {
+ U u; u.f2x2 = vtrn_f32(vrev64_f32(vget_high_f32(p)), vget_low_f32(p)); return u.f4;
+ }
+ LHS_FUNTION()
+};
+
+template<> struct Vswizzle<kWYXZ>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f p)
+ {
+ return vcombine_f32(vrev64_f32(vtrn_f32(vget_low_f32(p), vget_high_f32(p)).val[1]), vtrn_f32(vget_low_f32(p), vget_high_f32(p)).val[0]);
+ }
+ LHS_FUNTION()
+};
+
+template<> struct Vswizzle<kWWWZ>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f p)
+ {
+ return vcombine_f32(vdup_lane_f32(vget_high_f32(p), 1), vrev64_f32(vget_high_f32(p)));
+ }
+ LHS_FUNTION()
+};
+
+template<> struct Vswizzle<kWWZZ>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f p)
+ {
+ U u; u.f2x2 = vtrn_f32(vget_high_f32(p), vget_high_f32(p));
+ return vreinterpretq_f32_u32(vextq_u32(vreinterpretq_u32_f32(u.f4), vreinterpretq_u32_f32(u.f4), 2));
+ }
+ LHS_FUNTION()
+};
+
+template<> struct Vswizzle<kWZYX>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f p)
+ {
+ return vcombine_f32(vrev64_f32(vget_high_f32(p)), vrev64_f32(vget_low_f32(p)));
+ }
+ LHS_FUNTION()
+};
+
+static MECANIM_FORCE_INLINE float Vstoresf(vec4f r)
+{
+ return vgetq_lane_f32(r, 0);
+}
+
+static MECANIM_FORCE_INLINE bool Vstoresb(vec4b r)
+{
+ return (vgetq_lane_u32(r, 0) > 0) ? true : false;
+}
+
+// Aligned store
+#define Vstorepf(v, base, offset) vst1q_f32((float32_t*)((base)+(offset)),v);
+
+static MECANIM_FORCE_INLINE void Vstorepb(const vec4b v, bool* r)
+{
+ ::uint32_t u;
+ vst1q_lane_u32(&u, v, 0);
+ r[0] = (u > 0) ? true : false;
+ vst1q_lane_u32(&u, v, 1);
+ r[1] = (u > 0) ? true : false;
+ vst1q_lane_u32(&u, v, 2);
+ r[2] = (u > 0) ? true : false;
+ vst1q_lane_u32(&u, v, 3);
+ r[3] = (u > 0) ? true : false;
+}
+
+static MECANIM_FORCE_INLINE vec4f Vloadsf(float s)
+{
+ return vmovq_n_f32(s);
+}
+
+static MECANIM_FORCE_INLINE vec4b Vloadsb(bool s)
+{
+ const ::uint32_t false_true[2] = { 0, 0xFFFFFFFF };
+ return vdupq_n_u32(false_true[s ? 1 : 0]);
+}
+
+static MECANIM_FORCE_INLINE vec4f Vload4sf(float x, float y, float z, float w)
+{
+ float32x4_t result;
+ result = vdupq_n_f32(x);
+ result = vsetq_lane_f32(y, result, 1);
+ result = vsetq_lane_f32(z, result, 2);
+ result = vsetq_lane_f32(w, result, 3);
+ return result;
+}
+
+static MECANIM_FORCE_INLINE vec4b Vload4sb(bool x, bool y, bool z, bool w)
+{
+ const ::uint32_t val[4] =
+ {
+ x ? 0xffffffff : 0x00,
+ y ? 0xffffffff : 0x00,
+ z ? 0xffffffff : 0x00,
+ w ? 0xffffffff : 0x00
+ };
+
+ return vld1q_u32(&val[0]);
+}
+
+static MECANIM_FORCE_INLINE vec4f Vloadpf(float const* buf, int offset)
+{
+ return vld1q_f32((float32_t const*)buf + offset);
+}
+
+#define Vadd(l, r) vaddq_f32(l, r)
+#define Vsub(l, r) vsubq_f32(l, r)
+#define Vmul(l, r) vmulq_f32(l, r)
+
+
+// return a*b+c : be aware that vmlaq does a+b*c
+#define Vmadd(a, b, c) vmlaq_f32(c, a, b)
+// return a*b-c : be aware that vmlaq does a-b*c
+#define Vmsub(a, b, c) Vneg(vmlsq_f32(c, a, b))
+
+static MECANIM_FORCE_INLINE vec4f Vneg(vec4f r)
+{
+ uint32x4_t sign_constant = vdupq_n_u32(0x80000000);
+ uint32x4_t negated = veorq_u32(vreinterpretq_u32_f32(r), sign_constant);
+ return vreinterpretq_f32_u32(negated);
+}
+
+// vector sgn: return -1, 1
+static MECANIM_FORCE_INLINE vec4f Vsgn(vec4f r)
+{
+ uint32x4_t sign_constant = vdupq_n_u32(0x80000000);
+ uint32x4_t signs = vandq_u32(vreinterpretq_u32_f32(r), sign_constant);
+ uint32x4_t ones = vdupq_n_u32 (0x3f800000);
+
+ return vreinterpretq_f32_u32(vorrq_u32(signs,ones));
+/* float32x4_t ones = Vone();
+ float32x4_t nones = Vneg(ones);
+ uint32x4_t cmp = vcltq_f32(r,Vzero());
+ return vbslq_f32(cmp,nones,ones);*/
+}
+
+// vector sgn: return -1, 0, 1
+static MECANIM_FORCE_INLINE vec4f Vsign(vec4f r)
+{
+ uint32x4_t sign_constant = vdupq_n_u32(0x80000000);
+ uint32x4_t signs = vandq_u32(vreinterpretq_u32_f32(r), sign_constant);
+ uint32x4_t ones = vdupq_n_u32 (0x3f800000);
+
+ return vreinterpretq_f32_u32(vorrq_u32( signs, vandq_u32( vmvnq_u32( vceqq_f32( r, Vzero())), ones)));
+}
+
+#define Vinc(r) Vadd( (r), Vone())
+#define Vdec(r) Vsub( (r), Vone())
+
+static MECANIM_FORCE_INLINE vec4f Vabs(vec4f r)
+{
+ return vabsq_f32(r);
+}
+
+#define Vmax( l, r) vmaxq_f32(l, r)
+#define Vmin( l, r) vminq_f32(l, r)
+
+static MECANIM_FORCE_INLINE vec4fs Vlargest(vec4f r)
+{
+ float32x2_t temp = vpmax_f32 ( vget_high_f32(r), vget_low_f32(r) );
+ temp = vpmax_f32(temp, temp);
+ return vcombine_f32(temp,temp);
+}
+
+static MECANIM_FORCE_INLINE vec4fs Vsmallest(vec4f r)
+{
+ float32x2_t temp = vpmin_f32 ( vget_high_f32(r), vget_low_f32(r) );
+ temp = vpmin_f32(temp, temp);
+ return vcombine_f32(temp,temp);
+}
+
+static MECANIM_FORCE_INLINE vec4fs Vsum(vec4f r)
+{
+ float32x2_t temp = vpadd_f32 ( vget_high_f32(r), vget_low_f32(r) );
+ temp = vpadd_f32(temp, temp);
+ return vcombine_f32(temp,temp);
+}
+
+#define Vdot( l, r) Vsum( Vmul((l), (r)) )
+
+static MECANIM_FORCE_INLINE vec4f Vrsqrt(vec4f r)
+{
+ float32x4_t e = vrsqrteq_f32(r);
+ float32x4_t s = vmulq_f32(e, r);
+ float32x4_t v = vrsqrtsq_f32(s, e);
+
+ e = vmulq_f32(e,v);
+ s = vmulq_f32(e, r);
+ v = vrsqrtsq_f32(s, e);
+
+ return vmulq_f32(e,v);
+}
+
+static MECANIM_FORCE_INLINE vec4f Vrcp(vec4f r)
+{
+ cvec4fs(C0,-3.402823466e+38f);
+ cvec4fs(C1, 3.402823466e+38f);
+
+ float32x4_t R0 = vrecpeq_f32(r);
+ R0 = vmaxq_f32(R0, C0);
+ R0 = vminq_f32(R0, C1);
+
+ float32x4_t R1 = vrecpsq_f32(r, R0);
+ R0 = vmulq_f32(R0, R1);
+ R0 = vmaxq_f32(R0, C0);
+ R0 = vminq_f32(R0, C1);
+ R1 = vrecpsq_f32(r, R0);
+ return vmulq_f32(R0, R1);
+
+ //float32x4_t inv = vrecpeq_f32(r);
+ //float32x4_t step = vrecpsq_f32(r, inv);
+ //return vmulq_f32(step, inv);
+}
+
+static MECANIM_FORCE_INLINE vec4f Vdiv(const vec4f l, const vec4f r)
+{
+ return Vmul(l, Vrcp(r));
+}
+
+static MECANIM_FORCE_INLINE vec4f Vcombine(vec4f x, vec4f y, vec4f z, vec4f w)
+{
+ float32x2x2_t temp1 = vtrn_f32(vget_high_f32(x), vget_high_f32(y));
+ float32x2x2_t temp2 = vtrn_f32(vget_high_f32(z), vget_high_f32(w));
+ return vcombine_f32(temp1.val[0], temp2.val[0]);
+}
+
+// Vector comparison
+#define Vcmpeq( a, b) vceqq_f32(a, b)
+#define Vcmpneq( a, b) Vnot(vceqq_f32(a, b))
+#define Vcmpgt( a, b) vcgtq_f32(a, b)
+#define Vcmpge( a, b) vcgeq_f32(a, b)
+#define Vcmplt( a, b) vcltq_f32(a, b)
+#define Vcmple( a, b) vcleq_f32(a, b)
+
+static MECANIM_FORCE_INLINE vec4f Vsel(vec4b c, vec4f a, vec4f b)
+{
+ return vbslq_f32(c, a, b);
+}
+
+#define Vsqrt(r) Vsel( Vcmpeq(r, Vzero()), Vzero(), Vmul(r,Vrsqrt(r)))
+
+// vector logics
+#define Vnot(r) vmvnq_u32(r)
+#define Vxnor(a, b) Vnot(veorq_u32(a, b))
+#define Vxor(a, b) veorq_u32(a, b)
+#define Vand(a, b) vandq_u32(a, b)
+#define Vor(a, b) vorrq_u32(a, b)
+
+static MECANIM_FORCE_INLINE bool Vall(const vec4b a)
+{
+ ::uint32_t u[4];
+
+ vst1q_lane_u32(&u[0], a, 0);
+ vst1q_lane_u32(&u[1], a, 1);
+ vst1q_lane_u32(&u[2], a, 2);
+ vst1q_lane_u32(&u[3], a, 3);
+
+ return (u[0] & u[1] & u[2] & u[3]);
+};
+
+static MECANIM_FORCE_INLINE bool Vany(const vec4b a)
+{
+ ::uint32_t u[4];
+
+ vst1q_lane_u32(&u[0], a, 0);
+ vst1q_lane_u32(&u[1], a, 1);
+ vst1q_lane_u32(&u[2], a, 2);
+ vst1q_lane_u32(&u[3], a, 3);
+
+ return (u[0] | u[1] | u[2] | u[3]);
+};
+
+#endif
diff --git a/Runtime/Math/Simd/ppu.h b/Runtime/Math/Simd/ppu.h
new file mode 100644
index 0000000..bfa8832
--- /dev/null
+++ b/Runtime/Math/Simd/ppu.h
@@ -0,0 +1,1944 @@
+#ifndef SIMD_PPU
+#define SIMD_PPU
+
+#include "Runtime/Math/Simd/SimdMath.h"
+#define USE_WPERMWI_EQUIVALENT 1
+typedef vec_float4 vec4f;
+typedef vec_bint4 vec4b;
+typedef vec_bint4 vec4bs;
+
+
+#if USE_WPERMWI_EQUIVALENT
+# define SWZ_MASK(x, y, z, w) (((x&3)<<6) | ((y&3)<<4) | ((z&3)<<2) | (w&3))
+#else
+# define SWZ_MASK(x, y, z, w) (((x)<<24) | ((y)<<16) | ((z)<<8) | (w))
+#endif
+
+#define cvec4f(name, x,y,z,w) static const vec4f name = {(x),(y),(z),(w)}
+#define cvec4b(name, x,y,z,w) static const vec4b name = {(x),(y),(z),(w)}
+#define cvec4fs(name, s) static const vec4f name = {(s),(s),(s),(s)}
+
+enum simd_mask
+{
+ kXYZW = SWZ_MASK(0,1,2,3),
+ kXXXX = SWZ_MASK(0,0,0,0),
+ kYYYY = SWZ_MASK(1,1,1,1),
+ kZZZZ = SWZ_MASK(2,2,2,2),
+ kWWWW = SWZ_MASK(3,3,3,3),
+
+ kXWYZ = SWZ_MASK(0,3,1,2),
+ kXZWY = SWZ_MASK(0,2,3,1),
+
+ kYZWX = SWZ_MASK(1,2,3,0),
+ kYXZW = SWZ_MASK(1,0,2,3),
+ kYWZX = SWZ_MASK(1,3,2,0),
+ kYZXW = SWZ_MASK(1,2,0,3),
+ kYXWZ = SWZ_MASK(1,0,3,2),
+
+ kZWXY = SWZ_MASK(2,3,0,1),
+ kZYXW = SWZ_MASK(2,1,0,3),
+ kZYWX = SWZ_MASK(2,1,3,0),
+ kZXYW = SWZ_MASK(2,0,1,3),
+
+ kWYZX = SWZ_MASK(3,1,2,0),
+ kWXZY = SWZ_MASK(3,0,2,1),
+ kWYXZ = SWZ_MASK(3,1,0,2),
+ kWWWZ = SWZ_MASK(3,3,3,2),
+ kWWZZ = SWZ_MASK(3,3,2,2),
+ kWZYX = SWZ_MASK(3,2,1,0),
+};
+
+#define Vzero() __vzero()
+#define Vone() vec_ctf(vec_splat_u32(1), 0)
+
+
+#if USE_WPERMWI_EQUIVALENT
+# define Vpermute(v, mask) __vpermwi2<mask>( (v) )
+#else
+# define Vpermute(v, mask) __vpermwi3( (v), (mask) )
+#endif
+
+#if USE_WPERMWI_EQUIVALENT
+
+template <const int i>
+vec_float4 __vpermwi2(vec_float4 v0a)
+{
+#if 1
+ if (i == SWZ_MASK(0,0,0,0))
+ {
+ return vec_splat( v0a, 0 );
+ }
+ else if (i == SWZ_MASK(1,0,0,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_splat( v0a, 0 );
+ return vec_sld( v1a, v2a, 12 );
+ }
+ else if (i == SWZ_MASK(2,0,0,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_splat( v0a, 0 );
+ return vec_sld( v1a, v2a, 12 );
+ }
+ else if (i == SWZ_MASK(3,0,0,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 0 );
+ return vec_sld( v0a, v1a, 12 );
+ }
+ else if (i == SWZ_MASK(0,1,0,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_splat( v0a, 0 );
+ return vec_sld( v1a, v2a, 8 );
+ }
+ else if (i == SWZ_MASK(1,1,0,0))
+ {
+ vec_float4 v1a = vec_mergeh( v0a, v0a );
+ return vec_sld( v1a, v1a, 8 );
+ }
+ else if (i == SWZ_MASK(2,1,0,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_splat( v0a, 0 );
+ vec_float4 v3a = vec_mergeh( v1a, v0a );
+ return vec_sld( v3a, v2a, 8 );
+ }
+ else if (i == SWZ_MASK(3,1,0,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergeh( v0a, v1a );
+ return vec_sld( v2a, v0a, 4 );
+ }
+ else if (i == SWZ_MASK(0,2,0,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 0 );
+ vec_float4 v2a = vec_mergel( v0a, v1a );
+ return vec_mergeh( v1a, v2a );
+ }
+ else if (i == SWZ_MASK(1,2,0,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_splat( v0a, 0 );
+ return vec_sld( v1a, v2a, 8 );
+ }
+ else if (i == SWZ_MASK(2,2,0,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_splat( v0a, 0 );
+ return vec_sld( v1a, v2a, 8 );
+ }
+ else if (i == SWZ_MASK(3,2,0,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_sld( v1a, v0a, 12 );
+ return vec_mergeh( v1a, v2a );
+ }
+ else if (i == SWZ_MASK(0,3,0,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 0 );
+ vec_float4 v2a = vec_sld( v0a, v0a, 4 );
+ return vec_mergel( v1a, v2a );
+ }
+ else if (i == SWZ_MASK(1,3,0,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergeh( v1a, v0a );
+ return vec_sld( v2a, v2a, 12 );
+ }
+ else if (i == SWZ_MASK(2,3,0,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 0 );
+ return vec_sld( v0a, v1a, 8 );
+ }
+ else if (i == SWZ_MASK(3,3,0,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ return vec_mergel( v1a, v1a );
+ }
+ else if (i == SWZ_MASK(0,0,1,0))
+ {
+ vec_float4 v1a = vec_mergeh( v0a, v0a );
+ return vec_mergeh( v0a, v1a );
+ }
+ else if (i == SWZ_MASK(1,0,1,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 0 );
+ vec_float4 v2a = vec_splat( v0a, 1 );
+ return vec_mergel( v2a, v1a );
+ }
+ else if (i == SWZ_MASK(2,0,1,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_sld( v1a, v0a, 8 );
+ return vec_sld( v2a, v0a, 4 );
+ }
+ else if (i == SWZ_MASK(3,0,1,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ return vec_sld( v1a, v0a, 4 );
+ }
+ else if (i == SWZ_MASK(0,1,1,0))
+ {
+ vec_float4 v1a = vec_mergeh( v0a, v0a );
+ return vec_sld( v1a, v0a, 4 );
+ }
+ else if (i == SWZ_MASK(1,1,1,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ return vec_sld( v1a, v0a, 4 );
+ }
+ else if (i == SWZ_MASK(2,1,1,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergeh( v1a, v0a );
+ return vec_sld( v2a, v2a, 8 );
+ }
+ else if (i == SWZ_MASK(3,1,1,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_sld( v1a, v0a, 8 );
+ return vec_sld( v0a, v2a, 12 );
+ }
+ else if (i == SWZ_MASK(0,2,1,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_sld( v1a, v0a, 12 );
+ return vec_mergeh( v0a, v2a );
+ }
+ else if (i == SWZ_MASK(1,2,1,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_sld( v1a, v0a, 12 );
+ return vec_sld( v2a, v2a, 8 );
+ }
+ else if (i == SWZ_MASK(2,2,1,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_splat( v0a, 2 );
+ vec_float4 v3a = vec_sld( v1a, v0a, 12 );
+ return vec_sld( v2a, v3a, 8 );
+ }
+ else if (i == SWZ_MASK(3,2,1,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_sld( v1a, v0a, 12 );
+ vec_float4 v3a = vec_mergel( v0a, v2a );
+ return vec_sld( v3a, v2a, 8 );
+ }
+ else if (i == SWZ_MASK(0,3,1,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ return vec_mergeh( v0a, v1a );
+ }
+ else if (i == SWZ_MASK(1,3,1,0))
+ {
+ vec_float4 v1a = vec_mergeh( v0a, v0a );
+ vec_float4 v2a = vec_sld( v0a, v0a, 4 );
+ return vec_mergel( v1a, v2a );
+ }
+ else if (i == SWZ_MASK(2,3,1,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergeh( v1a, v0a );
+ return vec_sld( v0a, v2a, 8 );
+ }
+ else if (i == SWZ_MASK(3,3,1,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_sld( v0a, v1a, 4 );
+ return vec_mergel( v2a, v1a );
+ }
+ else if (i == SWZ_MASK(0,0,2,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 0 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ return vec_sld( v1a, v2a, 12 );
+ }
+ else if (i == SWZ_MASK(1,0,2,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 0 );
+ vec_float4 v2a = vec_sld( v0a, v0a, 4 );
+ return vec_mergeh( v2a, v1a );
+ }
+ else if (i == SWZ_MASK(2,0,2,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_splat( v0a, 0 );
+ return vec_mergel( v1a, v2a );
+ }
+ else if (i == SWZ_MASK(3,0,2,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 0 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ return vec_sld( v0a, v2a, 12 );
+ }
+ else if (i == SWZ_MASK(0,1,2,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ return vec_sld( v1a, v0a, 4 );
+ }
+ else if (i == SWZ_MASK(1,1,2,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergeh( v0a, v1a );
+ return vec_sld( v2a, v0a, 4 );
+ }
+ else if (i == SWZ_MASK(2,1,2,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_mergeh( v1a, v0a );
+ return vec_sld( v2a, v2a, 8 );
+ }
+ else if (i == SWZ_MASK(3,1,2,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_mergel( v0a, v1a );
+ return vec_sld( v2a, v2a, 8 );
+ }
+ else if (i == SWZ_MASK(0,2,2,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_splat( v0a, 0 );
+ vec_float4 v3a = vec_sld( v1a, v0a, 8 );
+ return vec_sld( v2a, v3a, 12 );
+ }
+ else if (i == SWZ_MASK(1,2,2,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergeh( v1a, v1a );
+ return vec_sld( v2a, v0a, 4 );
+ }
+ else if (i == SWZ_MASK(2,2,2,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ return vec_sld( v1a, v0a, 4 );
+ }
+ else if (i == SWZ_MASK(3,2,2,0))
+ {
+ vec_float4 v1a = vec_mergel( v0a, v0a );
+ vec_float4 v2a = vec_sld( v0a, v1a, 8 );
+ return vec_sld( v2a, v0a, 4 );
+ }
+ else if (i == SWZ_MASK(0,3,2,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ return vec_sld( v1a, v2a, 12 );
+ }
+ else if (i == SWZ_MASK(1,3,2,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_sld( v0a, v0a, 12 );
+ return vec_mergel( v2a, v1a );
+ }
+ else if (i == SWZ_MASK(2,3,2,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_sld( v0a, v0a, 4 );
+ return vec_mergel( v1a, v2a );
+ }
+ else if (i == SWZ_MASK(3,3,2,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ return vec_sld( v0a, v2a, 12 );
+ }
+ else if (i == SWZ_MASK(0,0,3,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_splat( v0a, 0 );
+ return vec_sld( v2a, v1a, 8 );
+ }
+ else if (i == SWZ_MASK(1,0,3,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergeh( v0a, v1a );
+ return vec_sld( v2a, v1a, 8 );
+ }
+ else if (i == SWZ_MASK(2,0,3,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 0 );
+ return vec_mergel( v0a, v1a );
+ }
+ else if (i == SWZ_MASK(3,0,3,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 0 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ return vec_mergel( v2a, v1a );
+ }
+ else if (i == SWZ_MASK(0,1,3,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_sld( v0a, v0a, 12 );
+ return vec_sld( v1a, v2a, 8 );
+ }
+ else if (i == SWZ_MASK(1,1,3,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_splat( v0a, 1 );
+ return vec_sld( v2a, v1a, 8 );
+ }
+ else if (i == SWZ_MASK(2,1,3,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_sld( v1a, v0a, 4 );
+ return vec_mergel( v0a, v2a );
+ }
+ else if (i == SWZ_MASK(3,1,3,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 3 );
+ vec_float4 v2a = vec_mergeh( v1a, v0a );
+ return vec_sld( v2a, v2a, 8 );
+ }
+ else if (i == SWZ_MASK(0,2,3,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_splat( v0a, 0 );
+ return vec_sld( v2a, v1a, 12 );
+ }
+ else if (i == SWZ_MASK(1,2,3,0))
+ {
+ return vec_sld( v0a, v0a, 4 );
+ }
+ else if (i == SWZ_MASK(2,2,3,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_splat( v0a, 2 );
+ return vec_sld( v2a, v1a, 12 );
+ }
+ else if (i == SWZ_MASK(3,2,3,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ return vec_sld( v0a, v1a, 12 );
+ }
+ else if (i == SWZ_MASK(0,3,3,0))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergel( v1a, v1a );
+ return vec_sld( v1a, v2a, 12 );
+ }
+ else if (i == SWZ_MASK(1,3,3,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 3 );
+ vec_float4 v2a = vec_sld( v1a, v0a, 8 );
+ return vec_sld( v2a, v2a, 12 );
+ }
+ else if (i == SWZ_MASK(2,3,3,0))
+ {
+ vec_float4 v1a = vec_mergel( v0a, v0a );
+ return vec_sld( v1a, v0a, 4 );
+ }
+ else if (i == SWZ_MASK(3,3,3,0))
+ {
+ vec_float4 v1a = vec_splat( v0a, 3 );
+ return vec_sld( v1a, v0a, 4 );
+ }
+ else if (i == SWZ_MASK(0,0,0,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 0 );
+ return vec_sld( v1a, v0a, 8 );
+ }
+ else if (i == SWZ_MASK(1,0,0,1))
+ {
+ vec_float4 v1a = vec_mergeh( v0a, v0a );
+ return vec_sld( v1a, v1a, 12 );
+ }
+ else if (i == SWZ_MASK(2,0,0,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergeh( v0a, v0a );
+ return vec_sld( v1a, v2a, 12 );
+ }
+ else if (i == SWZ_MASK(3,0,0,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ return vec_mergeh( v1a, v0a );
+ }
+ else if (i == SWZ_MASK(0,1,0,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ return vec_sld( v1a, v0a, 8 );
+ }
+ else if (i == SWZ_MASK(1,1,0,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ return vec_sld( v1a, v0a, 8 );
+ }
+ else if (i == SWZ_MASK(2,1,0,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergeh( v1a, v0a );
+ return vec_sld( v2a, v0a, 8 );
+ }
+ else if (i == SWZ_MASK(3,1,0,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_sld( v0a, v0a, 4 );
+ return vec_mergel( v2a, v1a );
+ }
+ else if (i == SWZ_MASK(0,2,0,1))
+ {
+ vec_float4 v1a = vec_mergeh( v0a, v0a );
+ vec_float4 v2a = vec_mergel( v0a, v1a );
+ return vec_mergeh( v1a, v2a );
+ }
+ else if (i == SWZ_MASK(1,2,0,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ return vec_sld( v1a, v0a, 8 );
+ }
+ else if (i == SWZ_MASK(2,2,0,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ return vec_sld( v1a, v0a, 8 );
+ }
+ else if (i == SWZ_MASK(3,2,0,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergel( v0a, v1a );
+ return vec_sld( v2a, v0a, 8 );
+ }
+ else if (i == SWZ_MASK(0,3,0,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_splat( v0a, 0 );
+ return vec_sld( v2a, v1a, 12 );
+ }
+ else if (i == SWZ_MASK(1,3,0,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_splat( v0a, 1 );
+ return vec_sld( v2a, v1a, 12 );
+ }
+ else if (i == SWZ_MASK(2,3,0,1))
+ {
+ return vec_sld( v0a, v0a, 8 );
+ }
+ else if (i == SWZ_MASK(3,3,0,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 3 );
+ return vec_sld( v1a, v0a, 8 );
+ }
+ else if (i == SWZ_MASK(0,0,1,1))
+ {
+ return vec_mergeh( v0a, v0a );
+ }
+ else if (i == SWZ_MASK(1,0,1,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ return vec_mergeh( v1a, v0a );
+ }
+ else if (i == SWZ_MASK(2,0,1,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergeh( v0a, v1a );
+ return vec_sld( v2a, v2a, 12 );
+ }
+ else if (i == SWZ_MASK(3,0,1,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_splat( v0a, 1 );
+ return vec_sld( v1a, v2a, 4 );
+ }
+ else if (i == SWZ_MASK(0,1,1,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ return vec_mergeh( v0a, v1a );
+ }
+ else if (i == SWZ_MASK(1,1,1,1))
+ {
+ return vec_splat( v0a, 1 );
+ }
+ else if (i == SWZ_MASK(2,1,1,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_splat( v0a, 1 );
+ return vec_sld( v1a, v2a, 12 );
+ }
+ else if (i == SWZ_MASK(3,1,1,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ return vec_sld( v0a, v1a, 12 );
+ }
+ else if (i == SWZ_MASK(0,2,1,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergeh( v1a, v0a );
+ return vec_sld( v2a, v1a, 4 );
+ }
+ else if (i == SWZ_MASK(1,2,1,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_splat( v0a, 1 );
+ return vec_sld( v1a, v2a, 8 );
+ }
+ else if (i == SWZ_MASK(2,2,1,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_splat( v0a, 2 );
+ return vec_sld( v2a, v1a, 8 );
+ }
+ else if (i == SWZ_MASK(3,2,1,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_splat( v0a, 1 );
+ vec_float4 v3a = vec_mergel( v0a, v1a );
+ return vec_sld( v3a, v2a, 8 );
+ }
+ else if (i == SWZ_MASK(0,3,1,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_sld( v0a, v1a, 12 );
+ return vec_mergeh( v0a, v2a );
+ }
+ else if (i == SWZ_MASK(1,3,1,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_sld( v0a, v1a, 4 );
+ return vec_mergel( v1a, v2a );
+ }
+ else if (i == SWZ_MASK(2,3,1,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ return vec_sld( v0a, v1a, 8 );
+ }
+ else if (i == SWZ_MASK(3,3,1,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 3 );
+ vec_float4 v2a = vec_splat( v0a, 1 );
+ return vec_sld( v1a, v2a, 8 );
+ }
+ else if (i == SWZ_MASK(0,0,2,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ return vec_mergeh( v2a, v0a );
+ }
+ else if (i == SWZ_MASK(1,0,2,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ return vec_mergeh( v1a, v0a );
+ }
+ else if (i == SWZ_MASK(2,0,2,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ return vec_mergeh( v1a, v0a );
+ }
+ else if (i == SWZ_MASK(3,0,2,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_sld( v0a, v1a, 12 );
+ return vec_mergeh( v2a, v0a );
+ }
+ else if (i == SWZ_MASK(0,1,2,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_splat( v0a, 1 );
+ return vec_sld( v1a, v2a, 4 );
+ }
+
+ else if (i == SWZ_MASK(1,1,2,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_sld( v0a, v0a, 4 );
+ return vec_mergeh( v2a, v1a );
+ }
+
+ else if (i == SWZ_MASK(2,1,2,1))
+ {
+ vec_float4 v1a = vec_mergeh( v0a, v0a );
+ vec_float4 v2a = vec_splat( v0a, 2 );
+ return vec_mergel( v2a, v1a );
+ }
+
+ else if (i == SWZ_MASK(3,1,2,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ return vec_sld( v0a, v2a, 12 );
+ }
+
+ else if (i == SWZ_MASK(0,2,2,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_splat( v0a, 2 );
+ vec_float4 v3a = vec_sld( v2a, v1a, 8 );
+ return vec_sld( v1a, v3a, 12 );
+ }
+
+ else if (i == SWZ_MASK(1,2,2,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergeh( v1a, v1a );
+ return vec_sld( v2a, v1a, 4 );
+ }
+
+ else if (i == SWZ_MASK(2,2,2,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_splat( v0a, 2 );
+ return vec_sld( v2a, v1a, 4 );
+ }
+
+ else if (i == SWZ_MASK(3,2,2,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergel( v0a, v1a );
+ return vec_sld( v2a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(0,3,2,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ vec_float4 v3a = vec_sld( v1a, v2a, 8 );
+ return vec_sld( v3a, v1a, 4 );
+ }
+
+ else if (i == SWZ_MASK(1,3,2,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_sld( v0a, v1a, 12 );
+ return vec_mergeh( v1a, v2a );
+ }
+
+ else if (i == SWZ_MASK(2,3,2,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergel( v0a, v1a );
+ return vec_sld( v0a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(3,3,2,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ vec_float4 v3a = vec_mergel( v0a, v1a );
+ return vec_sld( v2a, v3a, 8 );
+ }
+
+ else if (i == SWZ_MASK(0,0,3,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergeh( v0a, v1a );
+ return vec_sld( v2a, v2a, 12 );
+ }
+
+ else if (i == SWZ_MASK(1,0,3,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ return vec_mergel( v2a, v1a );
+ }
+
+ else if (i == SWZ_MASK(2,0,3,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ return vec_mergel( v0a, v1a );
+ }
+
+ else if (i == SWZ_MASK(3,0,3,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 3 );
+ return vec_mergeh( v1a, v0a );
+ }
+
+ else if (i == SWZ_MASK(0,1,3,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v3a = vec_mergel( v2a, v1a );
+ return vec_sld( v3a, v3a, 8 );
+ }
+
+ else if (i == SWZ_MASK(1,1,3,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_sld( v0a, v1a, 12 );
+ return vec_sld( v1a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(2,1,3,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ return vec_mergel( v0a, v1a );
+ }
+
+ else if (i == SWZ_MASK(3,1,3,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 3 );
+ vec_float4 v2a = vec_splat( v0a, 1 );
+ return vec_mergel( v1a, v2a );
+ }
+
+ else if (i == SWZ_MASK(0,2,3,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_sld( v0a, v1a, 8 );
+ return vec_sld( v1a, v2a, 12 );
+ }
+
+ else if (i == SWZ_MASK(1,2,3,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ return vec_sld( v0a, v1a, 4 );
+ }
+
+ else if (i == SWZ_MASK(2,2,3,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ return vec_sld( v2a, v2a, 4 );
+ }
+
+ else if (i == SWZ_MASK(3,2,3,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_sld( v0a, v1a, 8 );
+ return vec_sld( v0a, v2a, 12 );
+ }
+
+ else if (i == SWZ_MASK(0,3,3,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ vec_float4 v3a = vec_sld( v0a, v1a, 4 );
+ return vec_mergel( v2a, v3a );
+ }
+
+ else if (i == SWZ_MASK(1,3,3,1))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_sld( v0a, v1a, 12 );
+ vec_float4 v3a = vec_mergel( v1a, v0a );
+ return vec_sld( v3a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(2,3,3,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergel( v0a, v0a );
+ return vec_sld( v2a, v1a, 4 );
+ }
+
+ else if (i == SWZ_MASK(3,3,3,1))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ return vec_sld( v2a, v1a, 4 );
+ }
+
+ else if (i == SWZ_MASK(0,0,0,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_splat( v0a, 0 );
+ return vec_sld( v2a, v1a, 4 );
+ }
+
+ else if (i == SWZ_MASK(1,0,0,2))
+ {
+ vec_float4 v1a = vec_mergeh( v0a, v0a );
+ vec_float4 v2a = vec_splat( v0a, 2 );
+ vec_float4 v3a = vec_sld( v1a, v1a, 8 );
+ return vec_sld( v3a, v2a, 4 );
+ }
+
+ else if (i == SWZ_MASK(2,0,0,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_sld( v1a, v0a, 4 );
+ vec_float4 v3a = vec_mergeh( v0a, v1a );
+ return vec_sld( v2a, v3a, 8 );
+ }
+
+ else if (i == SWZ_MASK(3,0,0,2))
+ {
+ vec_float4 v1a = vec_mergeh( v0a, v0a );
+ vec_float4 v2a = vec_sld( v0a, v1a, 8 );
+ return vec_sld( v2a, v2a, 4 );
+ }
+
+ else if (i == SWZ_MASK(0,1,0,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 0 );
+ vec_float4 v2a = vec_sld( v0a, v0a, 4 );
+ return vec_mergeh( v1a, v2a );
+ }
+
+ else if (i == SWZ_MASK(1,1,0,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergeh( v1a, v0a );
+ return vec_sld( v2a, v2a, 12 );
+ }
+
+ else if (i == SWZ_MASK(2,1,0,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_sld( v1a, v0a, 4 );
+ return vec_mergel( v2a, v1a );
+ }
+
+ else if (i == SWZ_MASK(3,1,0,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_sld( v0a, v0a, 12 );
+ return vec_mergel( v1a, v2a );
+ }
+
+ else if (i == SWZ_MASK(0,2,0,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_splat( v0a, 0 );
+ return vec_mergel( v2a, v1a );
+ }
+
+ else if (i == SWZ_MASK(1,2,0,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_mergeh( v1a, v0a );
+ return vec_sld( v2a, v2a, 12 );
+ }
+
+ else if (i == SWZ_MASK(2,2,0,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_sld( v1a, v0a, 4 );
+ return vec_mergel( v2a, v1a );
+ }
+
+ else if (i == SWZ_MASK(3,2,0,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_sld( v0a, v0a, 4 );
+ return vec_mergel( v2a, v1a );
+ }
+
+ else if (i == SWZ_MASK(0,3,0,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 0 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ return vec_sld( v2a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(1,3,0,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ return vec_sld( v2a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(2,3,0,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_splat( v0a, 2 );
+ return vec_sld( v1a, v2a, 4 );
+ }
+
+ else if (i == SWZ_MASK(3,3,0,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergel( v0a, v1a );
+ return vec_sld( v2a, v2a, 4 );
+ }
+
+ else if (i == SWZ_MASK(0,0,1,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 0 );
+ return vec_sld( v1a, v0a, 12 );
+ }
+
+ else if (i == SWZ_MASK(1,0,1,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ return vec_sld( v1a, v0a, 12 );
+ }
+
+ else if (i == SWZ_MASK(2,0,1,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ return vec_sld( v1a, v0a, 12 );
+ }
+
+ else if (i == SWZ_MASK(3,0,1,2))
+ {
+ return vec_sld( v0a, v0a, 12 );
+ }
+
+ else if (i == SWZ_MASK(0,1,1,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ return vec_mergeh( v0a, v1a );
+ }
+
+ else if (i == SWZ_MASK(1,1,1,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_sld( v0a, v0a, 4 );
+ return vec_mergeh( v1a, v2a );
+ }
+
+ else if (i == SWZ_MASK(2,1,1,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergel( v1a, v1a );
+ return vec_sld( v1a, v2a, 12 );
+ }
+
+ else if (i == SWZ_MASK(3,1,1,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergeh( v1a, v1a );
+ return vec_sld( v0a, v2a, 12 );
+ }
+
+ else if (i == SWZ_MASK(0,2,1,2))
+ {
+ vec_float4 v1a = vec_mergel( v0a, v0a );
+ return vec_mergeh( v0a, v1a );
+ }
+
+ else if (i == SWZ_MASK(1,2,1,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_splat( v0a, 2 );
+ return vec_mergel( v1a, v2a );
+ }
+
+ else if (i == SWZ_MASK(2,2,1,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_splat( v0a, 2 );
+ return vec_sld( v2a, v1a, 8 );
+ }
+
+ else if (i == SWZ_MASK(3,2,1,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_sld( v0a, v1a, 4 );
+ return vec_sld( v2a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(0,3,1,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_sld( v0a, v1a, 12 );
+ return vec_mergeh( v0a, v2a );
+ }
+
+ else if (i == SWZ_MASK(1,3,1,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ return vec_sld( v2a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(2,3,1,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ return vec_sld( v0a, v1a, 8 );
+ }
+
+ else if (i == SWZ_MASK(3,3,1,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ return vec_sld( v2a, v1a, 8 );
+ }
+
+ else if (i == SWZ_MASK(0,0,2,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 0 );
+ vec_float4 v2a = vec_splat( v0a, 2 );
+ return vec_sld( v1a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(1,0,2,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 0 );
+ vec_float4 v2a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v3a = vec_mergel( v1a, v0a );
+ return vec_mergeh( v2a, v3a );
+ }
+
+ else if (i == SWZ_MASK(2,0,2,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_sld( v1a, v0a, 4 );
+ return vec_sld( v2a, v1a, 8 );
+ }
+
+ else if (i == SWZ_MASK(3,0,2,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_splat( v0a, 2 );
+ return vec_sld( v1a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(0,1,2,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_splat( v0a, 2 );
+ return vec_sld( v1a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(1,1,2,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ return vec_mergel( v1a, v1a );
+ }
+
+ else if (i == SWZ_MASK(2,1,2,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_sld( v0a, v0a, 4 );
+ return vec_mergeh( v1a, v2a );
+ }
+
+ else if (i == SWZ_MASK(3,1,2,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ return vec_sld( v0a, v2a, 12 );
+ }
+
+ else if (i == SWZ_MASK(0,2,2,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_splat( v0a, 2 );
+ return vec_sld( v1a, v2a, 12 );
+ }
+
+ else if (i == SWZ_MASK(1,2,2,2))
+ {
+ vec_float4 v1a = vec_mergel( v0a, v0a );
+ vec_float4 v2a = vec_sld( v0a, v0a, 4 );
+ return vec_mergeh( v2a, v1a );
+ }
+
+ else if (i == SWZ_MASK(2,2,2,2))
+ {
+ return vec_splat( v0a, 2 );
+ }
+
+ else if (i == SWZ_MASK(3,2,2,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ return vec_sld( v0a, v1a, 12 );
+ }
+
+ else if (i == SWZ_MASK(0,3,2,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_splat( v0a, 2 );
+ vec_float4 v3a = vec_mergel( v1a, v0a );
+ return vec_sld( v3a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(1,3,2,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergel( v0a, v1a );
+ return vec_sld( v2a, v2a, 4 );
+ }
+
+ else if (i == SWZ_MASK(2,3,2,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ return vec_sld( v0a, v1a, 8 );
+ }
+
+ else if (i == SWZ_MASK(3,3,2,2))
+ {
+ vec_float4 v1a = vec_mergel( v0a, v0a );
+ return vec_sld( v1a, v1a, 8 );
+ }
+
+ else if (i == SWZ_MASK(0,0,3,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_splat( v0a, 0 );
+ vec_float4 v3a = vec_sld( v0a, v1a, 12 );
+ return vec_sld( v2a, v3a, 8 );
+ }
+
+ else if (i == SWZ_MASK(1,0,3,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ vec_float4 v3a = vec_sld( v2a, v0a, 8 );
+ return vec_mergeh( v3a, v2a );
+ }
+
+ else if (i == SWZ_MASK(2,0,3,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ return vec_mergeh( v1a, v2a );
+ }
+
+ else if (i == SWZ_MASK(3,0,3,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ return vec_sld( v1a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(0,1,3,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_sld( v0a, v1a, 12 );
+ return vec_sld( v1a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(1,1,3,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v3a = vec_mergel( v1a, v0a );
+ return vec_mergel( v3a, v2a );
+ }
+
+ else if (i == SWZ_MASK(2,1,3,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ return vec_mergel( v0a, v1a );
+ }
+
+ else if (i == SWZ_MASK(3,1,3,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 3 );
+ vec_float4 v2a = vec_sld( v0a, v0a, 4 );
+ return vec_mergeh( v1a, v2a );
+ }
+
+ else if (i == SWZ_MASK(0,2,3,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_splat( v0a, 0 );
+ vec_float4 v3a = vec_sld( v0a, v1a, 8 );
+ return vec_sld( v2a, v3a, 12 );
+ }
+
+ else if (i == SWZ_MASK(1,2,3,2))
+ {
+ vec_float4 v1a = vec_mergel( v0a, v0a );
+ return vec_sld( v0a, v1a, 4 );
+ }
+
+ else if (i == SWZ_MASK(2,2,3,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ return vec_mergel( v0a, v1a );
+ }
+
+ else if (i == SWZ_MASK(3,2,3,2))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ return vec_mergel( v2a, v1a );
+ }
+
+ else if (i == SWZ_MASK(0,3,3,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ return vec_sld( v2a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(1,3,3,2))
+ {
+ vec_float4 v1a = vec_mergel( v0a, v0a );
+ vec_float4 v2a = vec_splat( v0a, 1 );
+ vec_float4 v3a = vec_sld( v1a, v1a, 8 );
+ return vec_sld( v2a, v3a, 12 );
+ }
+
+ else if (i == SWZ_MASK(2,3,3,2))
+ {
+ vec_float4 v1a = vec_mergel( v0a, v0a );
+ return vec_sld( v1a, v1a, 4 );
+ }
+
+ else if (i == SWZ_MASK(3,3,3,2))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ return vec_sld( v2a, v1a, 4 );
+ }
+
+ else if (i == SWZ_MASK(0,0,0,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_splat( v0a, 0 );
+ return vec_sld( v2a, v1a, 4 );
+ }
+
+ else if (i == SWZ_MASK(1,0,0,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergeh( v0a, v1a );
+ return vec_sld( v2a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(2,0,0,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergeh( v0a, v1a );
+ vec_float4 v3a = vec_sld( v1a, v0a, 12 );
+ return vec_mergeh( v3a, v2a );
+ }
+
+ else if (i == SWZ_MASK(3,0,0,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergel( v1a, v1a );
+ return vec_sld( v2a, v2a, 4 );
+ }
+
+ else if (i == SWZ_MASK(0,1,0,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_sld( v1a, v0a, 4 );
+ return vec_sld( v2a, v2a, 4 );
+ }
+
+ else if (i == SWZ_MASK(1,1,0,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_splat( v0a, 1 );
+ vec_float4 v3a = vec_mergeh( v0a, v1a );
+ return vec_sld( v2a, v3a, 8 );
+ }
+
+ else if (i == SWZ_MASK(2,1,0,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergeh( v0a, v1a );
+ vec_float4 v3a = vec_sld( v2a, v1a, 8 );
+ return vec_sld( v1a, v3a, 12 );
+ }
+
+ else if (i == SWZ_MASK(3,1,0,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergeh( v0a, v1a );
+ return vec_sld( v2a, v1a, 4 );
+ }
+
+ else if (i == SWZ_MASK(0,2,0,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 0 );
+ return vec_mergel( v1a, v0a );
+ }
+
+ else if (i == SWZ_MASK(1,2,0,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_sld( v1a, v0a, 4 );
+ return vec_mergel( v2a, v0a );
+ }
+
+ else if (i == SWZ_MASK(2,2,0,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_sld( v1a, v0a, 4 );
+ return vec_mergel( v2a, v0a );
+ }
+
+ else if (i == SWZ_MASK(3,2,0,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ return vec_mergel( v1a, v0a );
+ }
+
+ else if (i == SWZ_MASK(0,3,0,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 3 );
+ vec_float4 v2a = vec_splat( v0a, 0 );
+ return vec_mergel( v2a, v1a );
+ }
+
+ else if (i == SWZ_MASK(1,3,0,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 3 );
+ vec_float4 v2a = vec_mergeh( v1a, v0a );
+ return vec_sld( v2a, v2a, 12 );
+ }
+
+ else if (i == SWZ_MASK(2,3,0,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ return vec_sld( v1a, v2a, 4 );
+ }
+
+ else if (i == SWZ_MASK(3,3,0,3))
+ {
+ vec_float4 v1a = vec_mergel( v0a, v0a );
+ vec_float4 v2a = vec_sld( v0a, v0a, 4 );
+ return vec_mergel( v2a, v1a );
+ }
+
+ else if (i == SWZ_MASK(0,0,1,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergeh( v1a, v0a );
+ return vec_sld( v2a, v1a, 4 );
+ }
+
+ else if (i == SWZ_MASK(1,0,1,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ vec_float4 v3a = vec_sld( v1a, v0a, 8 );
+ return vec_sld( v3a, v2a, 4 );
+ }
+
+ else if (i == SWZ_MASK(2,0,1,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_sld( v1a, v0a, 8 );
+ return vec_sld( v2a, v1a, 4 );
+ }
+
+ else if (i == SWZ_MASK(3,0,1,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ return vec_sld( v1a, v2a, 4 );
+ }
+
+ else if (i == SWZ_MASK(0,1,1,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergeh( v0a, v0a );
+ return vec_sld( v2a, v1a, 4 );
+ }
+
+ else if (i == SWZ_MASK(1,1,1,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_splat( v0a, 1 );
+ return vec_sld( v2a, v1a, 4 );
+ }
+
+ else if (i == SWZ_MASK(2,1,1,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_splat( v0a, 1 );
+ vec_float4 v3a = vec_sld( v2a, v1a, 8 );
+ return vec_sld( v1a, v3a, 12 );
+ }
+
+ else if (i == SWZ_MASK(3,1,1,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ vec_float4 v3a = vec_sld( v0a, v1a, 8 );
+ return vec_sld( v3a, v2a, 4 );
+ }
+
+ else if (i == SWZ_MASK(0,2,1,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ return vec_mergel( v1a, v0a );
+ }
+
+ else if (i == SWZ_MASK(1,2,1,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ return vec_mergel( v1a, v0a );
+ }
+
+ else if (i == SWZ_MASK(2,2,1,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergel( v0a, v1a );
+ return vec_sld( v1a, v2a, 12 );
+ }
+
+ else if (i == SWZ_MASK(3,2,1,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ vec_float4 v2a = vec_mergel( v0a, v1a );
+ return vec_sld( v0a, v2a, 12 );
+ }
+
+ else if (i == SWZ_MASK(0,3,1,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 3 );
+ return vec_mergeh( v0a, v1a );
+ }
+
+ else if (i == SWZ_MASK(1,3,1,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ return vec_mergel( v1a, v2a );
+ }
+
+ else if (i == SWZ_MASK(2,3,1,3))
+ {
+ vec_float4 v1a = vec_mergel( v0a, v0a );
+ vec_float4 v2a = vec_mergeh( v1a, v0a );
+ return vec_mergel( v2a, v1a );
+ }
+
+ else if (i == SWZ_MASK(3,3,1,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 3 );
+ vec_float4 v2a = vec_mergeh( v1a, v0a );
+ return vec_mergel( v2a, v1a );
+ }
+
+ else if (i == SWZ_MASK(0,0,2,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_splat( v0a, 0 );
+ return vec_sld( v2a, v1a, 8 );
+ }
+
+ else if (i == SWZ_MASK(1,0,2,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_sld( v1a, v0a, 4 );
+ return vec_sld( v2a, v1a, 8 );
+ }
+
+ else if (i == SWZ_MASK(2,0,2,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ vec_float4 v2a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v3a = vec_mergel( v2a, v1a );
+ return vec_sld( v3a, v3a, 4 );
+ }
+
+ else if (i == SWZ_MASK(3,0,2,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergel( v0a, v1a );
+ return vec_sld( v1a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(1,1,2,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_splat( v0a, 1 );
+ return vec_sld( v2a, v1a, 12 );
+ }
+
+ else if (i == SWZ_MASK(2,1,2,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_splat( v0a, 2 );
+ return vec_sld( v2a, v1a, 12 );
+ }
+
+ else if (i == SWZ_MASK(3,1,2,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ return vec_sld( v0a, v1a, 12 );
+ }
+
+ else if (i == SWZ_MASK(0,2,2,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergel( v0a, v0a );
+ return vec_sld( v1a, v2a, 12 );
+ }
+
+ else if (i == SWZ_MASK(1,2,2,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 12 );
+ return vec_mergel( v1a, v0a );
+ }
+
+ else if (i == SWZ_MASK(2,2,2,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 2 );
+ return vec_mergel( v1a, v0a );
+ }
+
+ else if (i == SWZ_MASK(3,2,2,3))
+ {
+ vec_float4 v1a = vec_mergel( v0a, v0a );
+ return vec_sld( v0a, v1a, 12 );
+ }
+
+ else if (i == SWZ_MASK(0,3,2,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_sld( v0a, v1a, 12 );
+ return vec_sld( v2a, v2a, 12 );
+ }
+
+ else if (i == SWZ_MASK(1,3,2,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 3 );
+ vec_float4 v2a = vec_sld( v0a, v0a, 4 );
+ return vec_mergeh( v2a, v1a );
+ }
+
+ else if (i == SWZ_MASK(2,3,2,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ return vec_sld( v0a, v1a, 8 );
+ }
+
+ else if (i == SWZ_MASK(3,3,2,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ return vec_sld( v2a, v1a, 8 );
+ }
+
+ else if (i == SWZ_MASK(0,0,3,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 0 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ return vec_sld( v1a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(1,0,3,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ vec_float4 v3a = vec_sld( v1a, v0a, 4 );
+ return vec_sld( v3a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(2,0,3,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ return vec_sld( v2a, v2a, 4 );
+ }
+
+ else if (i == SWZ_MASK(3,0,3,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ return vec_sld( v1a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(0,1,3,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ return vec_sld( v1a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(1,1,3,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 1 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ return vec_sld( v1a, v2a, 8 );
+ }
+
+ else if (i == SWZ_MASK(2,1,3,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_mergel( v1a, v0a );
+ return vec_mergel( v0a, v2a );
+ }
+
+ else if (i == SWZ_MASK(3,1,3,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 3 );
+ vec_float4 v2a = vec_mergeh( v1a, v0a );
+ return vec_sld( v2a, v1a, 8 );
+ }
+
+ else if (i == SWZ_MASK(0,2,3,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_mergel( v0a, v1a );
+ return vec_sld( v1a, v2a, 12 );
+ }
+
+ else if (i == SWZ_MASK(1,2,3,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 3 );
+ return vec_sld( v0a, v1a, 4 );
+ }
+
+ else if (i == SWZ_MASK(2,2,3,3))
+ {
+ return vec_mergel( v0a, v0a );
+ }
+
+ else if (i == SWZ_MASK(3,2,3,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 3 );
+ return vec_mergel( v1a, v0a );
+ }
+
+ else if (i == SWZ_MASK(0,3,3,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 4 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ return vec_sld( v1a, v2a, 12 );
+ }
+
+ else if (i == SWZ_MASK(1,3,3,3))
+ {
+ vec_float4 v1a = vec_sld( v0a, v0a, 8 );
+ vec_float4 v2a = vec_splat( v0a, 3 );
+ return vec_sld( v1a, v2a, 12 );
+ }
+ else if (i == SWZ_MASK(2,3,3,3))
+ {
+ vec_float4 v1a = vec_splat( v0a, 3 );
+ return vec_sld( v0a, v1a, 8 );
+ }
+ else if (i == SWZ_MASK(3,3,3,3))
+ {
+ return vec_splat( v0a, 3 );
+ }
+ else if (i == SWZ_MASK(0,1,2,3))
+ {
+ return v0a;
+ }
+ return v0a;
+#endif
+}
+
+#else
+
+MECANIM_FORCE_INLINE vec4f __vpermwi3(vec4f v0a, const unsigned int mask)
+{
+
+ unsigned int w = (mask >> 0 ) & 0xff;
+ unsigned int z = (mask >> 8 ) & 0xff;
+ unsigned int y = (mask >> 16 ) & 0xff;
+ unsigned int x = (mask >> 24 ) & 0xff;
+
+ vec_uint4 wmask = V4BuildPermuteMask(x,y,z,w);
+ vec_float4 v = vec_perm(v0a, v0a, (vec_uchar16)wmask);
+ return v;
+
+}
+#endif
+
+
+
+MECANIM_FORCE_INLINE vec4f Vmove(vec4f l, vec4f r)
+ {
+ static const vec_uint4 vu32 = {0xFFFFFFFF,0,0,0};
+ vec_float4 v = __vsel(l, r, vu32);
+ return v;
+ }
+
+template<int SWZ> struct Vswizzle
+{
+ static inline vec4f rhs(vec4f r)
+ {
+ return Vpermute(r, SWZ);
+ }
+
+ static inline vec4f lhs(vec4f l, vec4f r)
+ {
+ vec4f m = Vmove(Vswizzle<SWZ>::rhs(l), r);
+ vec4f v = Vswizzle<SWZ>::rhs(m);
+ return v;
+ }
+};
+template<> struct Vswizzle<kXYZW>
+{
+ static inline vec4f rhs(vec4f r)
+ {
+ return r;
+ }
+ static inline vec4f lhs(vec4f l, vec4f r)
+ {
+ return Vmove(l, r);
+ }
+};
+
+MECANIM_FORCE_INLINE float Vstoresf(vec4f r)
+{
+ float f;
+ vec_ste(__vspltw(r, 0), 0, &f);
+ return f;
+}
+
+MECANIM_FORCE_INLINE bool Vstoresb(vec4b r)
+{
+ r=__vspltw(r, 0);
+ return !vec_all_eq((vec4f)r, Vzero());
+}
+
+// Aligned store
+#define Vstorepf(v, base, offset) __stvx((v), (base), (offset))
+
+MECANIM_FORCE_INLINE void Vstorepb(vec4b v, bool* r)
+{
+ union {
+ vec4b v;
+ int i[4];
+ } a; a.v = v;
+ r[0] = a.i[0] != 0;
+ r[1] = a.i[1] != 0;
+ r[2] = a.i[2] != 0;
+ r[3] = a.i[3] != 0;
+}
+
+MECANIM_FORCE_INLINE vec4f Vloadsf(float s)
+{
+ vec4f v = {s,s,s,s};
+ return v;
+}
+
+MECANIM_FORCE_INLINE vec4b Vloadsb(bool s)
+{
+ vec4b vTrue = (vec4b)vec_splat_u32(0xffffffff);
+
+ return s ? vTrue : (vec4b)Vzero();
+}
+
+MECANIM_FORCE_INLINE vec4f Vload4sf(float x, float y, float z, float w)
+{
+ vec4f v = {x,y,z,w};
+ return v;
+}
+
+static MECANIM_FORCE_INLINE vec4b Vload4sb(bool x, bool y, bool z, bool w)
+{
+ static const unsigned int false_true[2] = {0,~0};
+
+ vec4b v = (vec4b)(vec_uint4) { false_true[x], false_true[y], false_true[z], false_true[w] };
+ return v;
+}
+
+#define Vloadpf(v, offset) __lvx((v), (offset))
+
+#define Vadd(l, r) __vaddfp((l), (r))
+
+#define Vsub( l, r) __vsubfp((l), (r))
+
+#define Vmul( l, r) __vmulfp((l), (r))
+
+MECANIM_FORCE_INLINE vec4f Vrcp(vec4f r)
+{
+ // This function does two iterations of Newton's method! (taken from XMVector)
+ vec_float4 Reciprocal = vec_re(r);
+
+ // First refinement iteration (Newton-Raphson) for 1.0 / x
+ // y0 = reciprocal_estimate(x)
+ // y1 = y0 + y0 * (1.0 - x * y0)
+
+ vec_float4 vone = Vone();
+ vec_float4 Scale = vec_nmsub(r, Reciprocal, vone);
+ vec_float4 Result = vec_madd(Reciprocal, Scale, Reciprocal);
+
+ // Second refinement iteration
+ // y2 = y1 + y1 * (1.0 - x * y1)
+
+ Scale = vec_nmsub(r, Result, vone);
+ vec_bint4 Refine = vec_cmpeq(Result, Result);
+ Result = vec_madd(Result, Scale, Result);
+ return (vec_sel(Reciprocal, Result, Refine));
+}
+
+MECANIM_FORCE_INLINE vec4f Vdiv(vec4f l, vec4f r)
+{
+ // This function does two iterations of Newton's method!
+ return Vmul(l, Vrcp(r));
+}
+
+#define Vmadd( a, b, c) __vmaddfp((a), (b), (c))
+
+#define Vmsub( a, b, c) Vneg(__vnmsubfp((a), (b), (c)))
+
+#define Vneg(r) __vxor( (r), __vsignedzero)
+
+
+// vector sgn: return -1, 1
+#define Vsgn(r) __vor(Vone(), __vand(__vsignedzero, (r) ))
+
+// vector sgn: return -1, 0, 1
+static MECANIM_FORCE_INLINE vec4f Vsign(vec4f r)
+{
+ vec4f c = (vec4f)__vcmpeqfp(r, Vzero());
+ return __vor( __vand(vec_nor(c,c), Vone()), __vand(__vsignedzero, r ));
+}
+
+#define Vinc(r) Vadd( (r), Vone())
+#define Vdec(r) Vsub( (r), Vone())
+#define Vabs(r) __vandc((r), __vsignedzero)
+#define Vmax( l, r) __vmaxfp((l), (r))
+#define Vmin( l, r) __vminfp((l), (r))
+
+MECANIM_FORCE_INLINE vec4f Vlargest(vec4f r)
+{
+ r = Vmax(r, Vswizzle<kYZWX>::rhs(r));
+ r = Vmax(r, Vswizzle<kZWXY>::rhs(r));
+ return r;
+}
+
+MECANIM_FORCE_INLINE vec4f Vsmallest(vec4f r)
+{
+ r = Vmin(r, Vswizzle<kYZWX>::rhs(r));
+ r = Vmin(r, Vswizzle<kZWXY>::rhs(r));
+ return r;
+}
+
+MECANIM_FORCE_INLINE vec4f Vsum(vec4f r)
+{
+ r = Vadd(r, Vswizzle<kYZWX>::rhs(r) );
+ r = Vadd(r, Vswizzle<kZWXY>::rhs(r) );
+ return Vswizzle<kXXXX>::rhs(r);
+}
+
+#define Vdot( l, r) __vmsum4fp( (l), (r) )
+
+MECANIM_FORCE_INLINE vec4f Vrsqrt(vec4f r)
+{
+ static const vec4f three = {3.f,3.f,3.f,3.f};
+ static const vec4f a = {0.5f,0.5f,0.5f,0.5f};
+
+ vec4f const e = __vrsqrtefp(r);
+ return Vmul( Vmul(e, Vsub(three, Vmul( Vmul(e,e),r))), a);
+}
+
+
+#define Vsqrt(r) __vsel( Vmul(r, Vrsqrt(r)), Vzero(), __vcmpeqfp(r, Vzero()))
+
+#define Vcombine(x,y,z,w) __vmrghw(__vmrghw((x), (z)), __vmrghw((y), (w)))
+
+// Vector comparison
+#define Vcmpeq( a, b) (vec_bint4)__vcmpeqfp((a), (b))
+
+MECANIM_FORCE_INLINE vec4b Vcmpneq( vec4f a, vec4f b)
+{
+ vec4f c = (vec4f)Vcmpeq(a, b);
+ return (vec4b)__vnor(c, c);
+}
+
+#define Vcmpgt( a, b) (vec_bint4)__vcmpgtfp((a), (b))
+#define Vcmpge( a, b) (vec_bint4)__vcmpgefp((a), (b))
+#define Vcmplt( a, b) (vec_bint4)__vcmpgtfp((b), (a))
+#define Vcmple( a, b) (vec_bint4)__vcmpgefp((b), (a))
+
+#define Vsel( c, a, b) __vxor(b, __vand(__vxor(a, b), c))
+
+// vector logics
+#define Vnot(r) __vnor( (r), (r) )
+#define Vxnor( a, b) Vnot(__vxor((a), (b)))
+#define Vxor( a, b) __vxor((a), (b))
+#define Vand( a, b) __vand((a), (b))
+#define Vor( a, b) __vor((a), (b))
+
+MECANIM_FORCE_INLINE bool Vall(vec4b a)
+{
+ return vec_all_ne((vec4f)a, Vzero());
+}
+
+MECANIM_FORCE_INLINE bool Vany(vec4b a)
+{
+ // Not all words equal to 0
+ return vec_any_ne((vec4f)a, Vzero());
+}
+
+#endif
diff --git a/Runtime/Math/Simd/quaternion.h b/Runtime/Math/Simd/quaternion.h
new file mode 100644
index 0000000..ccb5077
--- /dev/null
+++ b/Runtime/Math/Simd/quaternion.h
@@ -0,0 +1,253 @@
+#ifndef SIMD_QUATERNION_H
+#define SIMD_QUATERNION_H
+
+#include "Runtime/Math/Simd/math.h"
+
+namespace math
+{
+
+static inline float4 quatIdentity()
+{
+ cvec4f(id, 0,0,0,1);
+ return float4(id);
+}
+
+static inline float4 quatConj(float4 const& q)
+{
+ cvec4f(conj, -1,-1,-1,1);
+ return float4(Vmul(q.eval(), conj));
+}
+
+static inline float4 quatMul(float4 const& a, float4 const& b)
+{
+ return quatConj(a.zxyw()*b.yzxw() - a.ywzx()*b.zywx() - a.wyxz()*b.xwyz() - a.xzwy()*b.wxzy());
+}
+
+static inline float4 quatMulVec(float4 const& q, float4 const& v1)
+{
+ const float4 v = math::vector(v1 + v1);
+ const float4 qv = q*v;
+ return q.w()*(cross(q, v) + q.w()*v) + q*(qv + qv.yzxw() + qv.zxyw()) + quatConj(v1);
+}
+
+static inline float4 quatLerp(float4 const& p, float4 const& q, float1 const& blend)
+{
+ return normalize(p + blend*(q*sgn(dot(p, q)) - p));
+}
+
+static inline float4 quatArcRotate(float4 const& a, float4 const& b)
+{
+ float4 q = cross(a, b);
+ q.w() = dot(a, b) + math::sqrt( float1(dot(a)*dot(b)) );
+ return q;
+}
+
+static inline float4 quatArcRotateX(float4 const& n)
+{
+ return float4(float1::zero(),-n.z(),n.y(),n.x()+float1::one());
+}
+
+static inline float4 quatXcos(float4 const &qn)
+{
+ const float4 qw = qn.w()*qn - float4(0, 0, 0, .5f);
+ const float4 u = qn.x()*qn + float4(1, 1, -1, -1) * qw.wzyx();
+ return u + u;
+}
+
+static inline float4 quatYcos(float4 const &qn)
+{
+ const float4 qw = qn.w()*qn - float4(0, 0, 0, .5f);
+ const float4 v = qn.y()*qn + float4(-1, 1, 1, -1)*qw.zwxy();
+ return v + v;
+}
+
+static inline float4 quatZcos(float4 const &qn)
+{
+ const float4 qw = qn.w()*qn - float4(0, 0, 0, .5f);
+ const float4 w = qn.z()*qn + float4(1, -1, 1, -1)*qw.yxwz();
+
+ return w + w;
+}
+
+static inline float4 quatEulerToQuat(float4 const& euler)
+{
+ float4 s, c; sincos( float1(0.5f)*euler, s, c);
+
+ const float4 t = float4(s.x()*c.z(), s.x()*s.z(), c.x()*s.z(), c.x()*c.z());
+
+ constant_float4( mask, -1.f, 1.f, -1.f, 1.f);
+ return c.y()*t + s.y()*mask*t.zwxy();
+}
+
+static inline float4 quatQuatToEuler(float4 const& q)
+{
+ float4 euler;
+
+ const float4 x = q.x()*q;
+ const float4 y = q.y()*q;
+ const float1 discr = x.z() - y.w();
+
+ if(discr >= float1(.5f - M_EPSF))
+ {
+ float1 _y = x.w() - y.z();
+ float1 _x = -x.z() - y.w();
+
+ euler = float4( atan2( _y.tofloat(), _x.tofloat() ), -M_PI_2f, 0.f, 0.f);
+ }
+ else
+ {
+ const float4 w = q.wwwz()*q.wwzz() - float4(.5f, 0.f, 0.f, 0.f);
+ if(discr <= float1(M_EPSF - .5f))
+ {
+ float1 _y = x.y() - w.z();
+ float1 _x = y.y() + w.x();
+ euler = float4( atan2( _y.tofloat(), _x.tofloat() ), M_PI_2f, 0.f, 0.f);
+ }
+ else
+ {
+ float1 _yX = x.w() + y.z();
+ float1 _xX = w.w() + w.x();
+ float1 discr2 = discr + discr;
+ float1 _yZ = x.y() + w.z();
+ float1 _xZ = x.x() + w.x();
+
+ euler = float4( atan2( _yX.tofloat(), _xX.tofloat() ), -asin( discr2.tofloat() ), atan2( _yZ.tofloat(), _xZ.tofloat() ), 0.f);
+ }
+ }
+ return euler;
+}
+
+// get unit quaternion from rotation matrix
+static inline float4 quatMatrixToQuat(float4 const& u, float4 const& v, float4 const& w)
+{
+ float4 q;
+ if(u.x() >= float1::zero())
+ {
+ const float1 t = v.y() + w.z();
+ if(t >= float1::zero())
+ {
+ float1 x(v.z() - w.y());
+ float1 y(w.x() - u.z());
+ float1 z(u.y() - v.x());
+ float1 ww(float1::one() + u.x() + t);
+ q = float4(x, y, z, ww);
+ // Android doesn't like this expression, it does generate the wrong assembly
+ //q = float4(v.z() - w.y(), w.x() - u.z(), u.y() - v.x(), float1::one() + u.x() + t);
+ }
+ else
+ {
+ float1 x(float1::one() + u.x() - t);
+ float1 y(u.y() + v.x());
+ float1 z(w.x() + u.z());
+ float1 ww(v.z() - w.y());
+ q = float4(x, y, z, ww);
+ // Android doesn't like this expression, it does generate the wrong assembly
+ //q = float4(float1::one() + u.x() - t, u.y() + v.x(), w.x() + u.z(), v.z() - w.y());
+ }
+ }
+ else
+ {
+ const float1 t = v.y() - w.z();
+ if(t >= float1::zero())
+ {
+ float1 x(u.y() + v.x());
+ float1 y(float1::one() - u.x() + t);
+ float1 z(v.z() + w.y());
+ float1 ww(w.x() - u.z());
+ q = float4(x, y, z, ww);
+ // Android doesn't like this expression, it does generate the wrong assembly
+ //q = float4(u.y() + v.x(), float1::one() - u.x() + t, v.z() + w.y(), w.x() - u.z());
+ }
+ else
+ {
+ float1 x(w.x() + u.z());
+ float1 y(v.z() + w.y());
+ float1 z(float1::one() - u.x() - t);
+ float1 ww(u.y() - v.x());
+ q = float4(x, y, z, ww);
+ // Android doesn't like this expression, it does generate the wrong assembly
+ //q = float4(w.x() + u.z(), v.z() + w.y(), float1::one() - u.x() - t, u.y() - v.x());
+ }
+ }
+ return normalize(q);
+}
+
+static inline float4 quatProjOnYPlane(float4 const& q)
+{
+ constant_float4(yMask, 0,1,0,0);
+ constant_float4(ywMask, 0,1,0,1);
+
+ const float4 lQAlignUp = quatArcRotate(quatYcos(q), yMask );
+
+ return normalize( quatMul(lQAlignUp,q) * ywMask);
+}
+
+static inline float4 quatClamp(const float4 &q,float maxAngle)
+{
+ float4 ret = q;
+
+ float1 halfCosMaxAnlge = float1(math::cos(0.5f*maxAngle));
+
+ float4 qn = normalize(q);
+ qn = cond(qn.w() < float1::zero(), -qn, qn);
+
+ if(qn.w() < halfCosMaxAnlge)
+ {
+ float1 fact = (halfCosMaxAnlge - qn.w()) / halfCosMaxAnlge;
+
+ ret = qn * (float1::one() - fact);
+ ret.w() = lerp(halfCosMaxAnlge, float1::one(), fact);
+ }
+
+ return ret;
+}
+
+static inline float4 quatWeight(float4 const& q, float1 const& w)
+{
+ return normalize(float4(q.x()*w,q.y()*w,q.z()*w,q.w()));
+}
+
+static inline float4 quat2Qtan(float4 const& q)
+{
+ float1 w = q.w();
+ w = cond(w == float1::zero(), float1(M_EPSF), w);
+ return math::vector(q/w);
+}
+
+static inline float4 qtan2Quat(float4 const& q)
+{
+ float4 qn = q;
+ qn.w() = float1::one();
+ return normalize(qn);
+}
+
+static inline float4 ZYRoll2Quat(float4 const& zyroll)
+{
+ return normalize(float4(zyroll.x(),zyroll.y()+zyroll.x()*zyroll.z(),zyroll.z()-zyroll.x()*zyroll.y(), float1::one()));
+}
+
+static inline float4 quat2ZYRoll(float4 const& q)
+{
+ const float4 qtan = quat2Qtan(q);
+ const float1 qtanx = qtan.x();
+ const float1 x2p1 = float1::one()+qtanx*qtanx;
+ return float4(qtanx,(qtan.y()-qtanx*qtan.z())/x2p1,(qtan.z()+qtanx*qtan.y())/x2p1,float1::zero());
+}
+
+static inline float4 RollZY2Quat(float4 const& zyroll)
+{
+ return normalize(float4(zyroll.x(),zyroll.y()-zyroll.x()*zyroll.z(),zyroll.z()+zyroll.x()*zyroll.y(),float1::one()));
+}
+
+static inline float4 quat2RollZY(float4 const& q)
+{
+ const float4 qtan = quat2Qtan(q);
+ const float1 qtanx = qtan.x();
+ const float1 x2p1 = float1::one()+qtanx*qtanx;
+ return float4(qtanx,(qtan.y()+qtanx*qtan.z())/x2p1,(qtan.z()-qtanx*qtan.y())/x2p1,float1::zero());
+}
+
+}
+
+#endif
+
diff --git a/Runtime/Math/Simd/sse.h b/Runtime/Math/Simd/sse.h
new file mode 100644
index 0000000..59796d7
--- /dev/null
+++ b/Runtime/Math/Simd/sse.h
@@ -0,0 +1,237 @@
+#ifndef SIMD_SSE_H
+#define SIMD_SSE_H
+
+#include <xmmintrin.h>
+#include <emmintrin.h>
+
+typedef __m128 vec4f; // vector 4 packed
+typedef __m128 vec4fs; // vector 4 scalar
+typedef __m128 vec4b; // vector 4 bool packed
+typedef __m128 vec4bs; // vector 4 bool scalar
+
+#define SWZ_MASK(x, y, z, w) _MM_SHUFFLE(w,z,y,x)
+
+#define cvec4f(name, x,y,z,w) static const vec4f name = {x,y,z,w}
+#define cvec4b(name, x,y,z,w) static const vec4b name = {x,y,z,w}
+#define cvec4fs(name, s) static const vec4f name = {s,s,s,s}
+
+enum simd_mask
+{
+ kXYZW = SWZ_MASK(0,1,2,3),
+ kXXXX = SWZ_MASK(0,0,0,0),
+ kYYYY = SWZ_MASK(1,1,1,1),
+ kZZZZ = SWZ_MASK(2,2,2,2),
+ kWWWW = SWZ_MASK(3,3,3,3),
+
+ kXWYZ = SWZ_MASK(0,3,1,2),
+ kXZWY = SWZ_MASK(0,2,3,1),
+
+ kYZWX = SWZ_MASK(1,2,3,0),
+ kYXZW = SWZ_MASK(1,0,2,3),
+ kYWZX = SWZ_MASK(1,3,2,0),
+ kYZXW = SWZ_MASK(1,2,0,3),
+ kYXWZ = SWZ_MASK(1,0,3,2),
+
+ kZWXY = SWZ_MASK(2,3,0,1),
+ kZYXW = SWZ_MASK(2,1,0,3),
+ kZYWX = SWZ_MASK(2,1,3,0),
+ kZXYW = SWZ_MASK(2,0,1,3),
+
+ kWYZX = SWZ_MASK(3,1,2,0),
+ kWXZY = SWZ_MASK(3,0,2,1),
+ kWYXZ = SWZ_MASK(3,1,0,2),
+ kWWWZ = SWZ_MASK(3,3,3,2),
+ kWWZZ = SWZ_MASK(3,3,2,2),
+ kWZYX = SWZ_MASK(3,2,1,0),
+};
+
+#define Vzero() _mm_setzero_ps()
+
+#define Vone() _mm_set1_ps(1.f)
+
+#define Vpermute(v, mask) _mm_shuffle_ps( (v), (v), (mask) )
+
+#define Vmove(l, r) _mm_move_ss( (l), (r) )
+
+
+template<int SWZ> struct Vswizzle
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f r)
+ {
+ return Vpermute(r, SWZ);
+ }
+
+ static MECANIM_FORCE_INLINE vec4f lhs(vec4f l, vec4f r)
+ {
+ vec4f m = Vmove(Vswizzle<SWZ>::rhs(l), r);
+ return Vswizzle<SWZ>::rhs(m);
+ }
+};
+
+template<> struct Vswizzle<kXYZW>
+{
+ static MECANIM_FORCE_INLINE vec4f rhs(vec4f r)
+ {
+ return r;
+ }
+ static MECANIM_FORCE_INLINE vec4f lhs(vec4f l, vec4f r)
+ {
+ return Vmove(l, r);
+ }
+};
+
+static MECANIM_FORCE_INLINE float Vstoresf(vec4f r)
+{
+ float f; _mm_store_ss(&f, r); return f;
+}
+
+#define Vstoresb(r) ( _mm_movemask_ps((r)) & 0x1<<0) != 0
+
+// Aligned store
+#define Vstorepf(v, base, offset) _mm_store_ps((base)+(offset), (v))
+
+static MECANIM_FORCE_INLINE void Vstorepb(vec4f v, bool* r)
+{
+ r[0] = ( _mm_movemask_ps(v) & 0x1<<0) != 0;
+ r[1] = ( _mm_movemask_ps(v) & 0x1<<1) != 0;
+ r[2] = ( _mm_movemask_ps(v) & 0x1<<2) != 0;
+ r[3] = ( _mm_movemask_ps(v) & 0x1<<3) != 0;
+}
+
+#define Vloadsf(s) _mm_set1_ps(s)
+
+static MECANIM_FORCE_INLINE vec4f Vloadsb(bool s)
+{
+ union {
+ int b[4];
+ vec4f v;
+ } static const false_true[2] = {
+ {0,0,0,0},
+ {~0,~0,~0,~0}
+ };
+
+ return false_true[s].v;
+}
+
+#define Vload4sf(x, y, z, w) _mm_set_ps(w, z, y, x)
+
+static MECANIM_FORCE_INLINE vec4f Vload4sb(bool x, bool y, bool z, bool w)
+{
+ union {
+ int b;
+ float f;
+ } static const false_true[2] = {
+ 0,~0
+ };
+
+ return _mm_set_ps(false_true[w].f, false_true[z].f, false_true[y].f, false_true[x].f);
+}
+
+#define Vloadpf(v, offset) _mm_load_ps( (v)+(offset))
+
+#define Vadd(l, r) _mm_add_ps((l), (r))
+
+#define Vsub( l, r) _mm_sub_ps((l), (r))
+
+#define Vmul( l, r) _mm_mul_ps((l), (r))
+
+#define Vdiv( l, r) _mm_div_ps((l), (r))
+
+#define Vmadd( a, b, c) _mm_add_ps(_mm_mul_ps((a), (b)), (c))
+
+#define Vmsub( a, b, c) _mm_sub_ps(_mm_mul_ps((a), (b)), (c))
+
+
+static MECANIM_FORCE_INLINE vec4f Vneg(vec4f r)
+{
+ static const vec4f sign_constant = {-0.f,-0.f,-0.f,-0.f};
+ return _mm_xor_ps( (r), sign_constant);
+}
+
+// vector sgn: return -1, 1
+static MECANIM_FORCE_INLINE vec4f Vsgn(vec4f r)
+{
+ static const vec4f sign_constant = {-0.f,-0.f,-0.f,-0.f};
+ return _mm_or_ps(Vone(), _mm_and_ps(sign_constant, (r) ));
+}
+
+// vector sgn: return -1, 0, 1
+static MECANIM_FORCE_INLINE vec4f Vsign(vec4f r)
+{
+ static const vec4f sign_constant = {-0.f,-0.f,-0.f,-0.f};
+ return _mm_or_ps( _mm_and_ps( _mm_cmpneq_ps(r, Vzero()), Vone()), _mm_and_ps(sign_constant, r ));
+}
+
+#define Vinc(r) Vadd( (r), Vone())
+#define Vdec(r) Vsub( (r), Vone())
+
+static MECANIM_FORCE_INLINE vec4f Vabs(vec4f r)
+{
+ static const vec4f sign_constant = {-0.f,-0.f,-0.f,-0.f};
+ return _mm_andnot_ps(sign_constant, (r));
+}
+
+#define Vmax( l, r) _mm_max_ps((l), (r))
+#define Vmin( l, r) _mm_min_ps((l), (r))
+
+static MECANIM_FORCE_INLINE vec4fs Vlargest(vec4f r)
+{
+ r = Vmax(r, Vswizzle<kYZWX>::rhs(r));
+ r = Vmax(r, Vswizzle<kZWXY>::rhs(r));
+ return r;
+}
+
+static MECANIM_FORCE_INLINE vec4fs Vsmallest(vec4f r)
+{
+ r = Vmin(r, Vswizzle<kYZWX>::rhs(r));
+ r = Vmin(r, Vswizzle<kZWXY>::rhs(r));
+ return r;
+}
+
+static MECANIM_FORCE_INLINE vec4fs Vsum(vec4f r)
+{
+ r = Vadd(r, Vswizzle<kYZWX>::rhs(r) );
+ r = Vadd(r, Vswizzle<kZWXY>::rhs(r) );
+ return Vswizzle<kXXXX>::rhs(r);
+}
+
+#define Vdot( l, r) Vsum( Vmul((l), (r)) )
+#define Vsqrt(r) _mm_sqrt_ps((r))
+
+static MECANIM_FORCE_INLINE vec4f Vrsqrt(vec4f r)
+{
+ vec4f const e = _mm_rsqrt_ps(r);
+ return Vmul(Vmul(e, Vsub(_mm_set1_ps(3.0f), Vmul(Vmul(e,e),r))), _mm_set1_ps(.5f));
+}
+
+static MECANIM_FORCE_INLINE vec4f Vrcp(vec4f r)
+{
+ vec4f e = _mm_rcp_ps( r );
+ return Vsub( Vadd(e, e), Vmul(r, Vmul(e, e)));
+}
+
+#define Vcombine(x,y,z,w) _mm_movelh_ps(_mm_unpacklo_ps( (x), (y) ), _mm_unpacklo_ps((z), (w)))
+
+// Vector comparison
+#define Vcmpeq( a, b) _mm_cmpeq_ps((a), (b))
+#define Vcmpneq( a, b) _mm_cmpneq_ps((a), (b))
+#define Vcmpgt( a, b) _mm_cmpgt_ps((a), (b))
+#define Vcmpge( a, b) _mm_cmpge_ps((a), (b))
+#define Vcmplt( a, b) _mm_cmplt_ps((a), (b))
+#define Vcmple( a, b) _mm_cmple_ps((a), (b))
+
+static MECANIM_FORCE_INLINE vec4f Vsel( vec4f c, vec4f a, vec4f b)
+{
+ return _mm_xor_ps(b, _mm_and_ps(_mm_xor_ps(a, b), c));
+}
+
+// vector logics
+#define Vnot(r) _mm_cmpeq_ps( (r), Vzero() )
+#define Vxnor( a, b) Vnot(_mm_xor_ps((a), (b)))
+#define Vxor( a, b) _mm_xor_ps((a), (b))
+#define Vand( a, b) _mm_and_ps((a), (b))
+#define Vor( a, b) _mm_or_ps((a), (b))
+#define Vall(a) (_mm_movemask_ps((a)) & 0xf) == 0xf
+#define Vany( a) _mm_movemask_ps((a)) != 0
+
+#endif
diff --git a/Runtime/Math/Simd/xenon.h b/Runtime/Math/Simd/xenon.h
new file mode 100644
index 0000000..5ddfc68
--- /dev/null
+++ b/Runtime/Math/Simd/xenon.h
@@ -0,0 +1,275 @@
+#ifndef SIMD_XENON
+#define SIMD_XENON
+
+#include <vectorintrinsics.h>
+#include <xnamath.h>
+
+typedef __vector4 vec4f; // vector 4 packed
+typedef __vector4 vec4fs; // vector 4 scalar
+typedef __vector4 vec4b; // vector 4 bool packed
+typedef __vector4 vec4bs; // vector 4 bool scalar
+
+#define SWZ_MASK(x, y, z, w) VPERMWI_CONST(x, y, z, w)
+
+#define cvec4f(name, x,y,z,w) static const vec4f name = {(x),(y),(z),(w)}
+#define cvec4b(name, x,y,z,w) static const vec4b name = {(x),(y),(z),(w)}
+#define cvec4fs(name, s) static const vec4fs name = {(s),(s),(s),(s)}
+
+enum simd_mask
+{
+ kXYZW = SWZ_MASK(0,1,2,3),
+ kXXXX = SWZ_MASK(0,0,0,0),
+ kYYYY = SWZ_MASK(1,1,1,1),
+ kZZZZ = SWZ_MASK(2,2,2,2),
+ kWWWW = SWZ_MASK(3,3,3,3),
+
+ kXWYZ = SWZ_MASK(0,3,1,2),
+ kXZWY = SWZ_MASK(0,2,3,1),
+
+ kYZWX = SWZ_MASK(1,2,3,0),
+ kYXZW = SWZ_MASK(1,0,2,3),
+ kYWZX = SWZ_MASK(1,3,2,0),
+ kYZXW = SWZ_MASK(1,2,0,3),
+ kYXWZ = SWZ_MASK(1,0,3,2),
+
+ kZWXY = SWZ_MASK(2,3,0,1),
+ kZYXW = SWZ_MASK(2,1,0,3),
+ kZYWX = SWZ_MASK(2,1,3,0),
+ kZXYW = SWZ_MASK(2,0,1,3),
+
+ kWYZX = SWZ_MASK(3,1,2,0),
+ kWXZY = SWZ_MASK(3,0,2,1),
+ kWYXZ = SWZ_MASK(3,1,0,2),
+ kWWWZ = SWZ_MASK(3,3,3,2),
+ kWWZZ = SWZ_MASK(3,3,2,2),
+ kWZYX = SWZ_MASK(3,2,1,0),
+};
+
+#define Vzero() __vzero()
+#define Vone() __vupkd3d(__vspltisw(0), VPACK_D3DCOLOR)
+
+#define Vpermute(v, mask) __vpermwi( (v), (mask) )
+
+ MECANIM_FORCE_INLINE vec4f Vmove(vec4f l, vec4f r)
+ {
+ static const XMVECTORU32 vu32 = {0xFFFFFFFF,0,0,0};
+ return __vsel(l, r, vu32.v);
+ }
+
+template<int SWZ> struct Vswizzle
+{
+ static inline vec4f rhs(vec4f r)
+ {
+ return Vpermute(r, SWZ);
+ }
+
+ static inline vec4f lhs(vec4f l, vec4f r)
+ {
+ vec4f m = Vmove(Vswizzle<SWZ>::rhs(l), r);
+ return Vswizzle<SWZ>::rhs(m);
+ }
+};
+template<> struct Vswizzle<kXYZW>
+{
+ static inline vec4f rhs(vec4f r)
+ {
+ return r;
+ }
+ static inline vec4f lhs(vec4f l, vec4f r)
+ {
+ return Vmove(l, r);
+ }
+};
+
+MECANIM_FORCE_INLINE float Vstoresf(vec4f r)
+{
+ float f; __stvewx(__vspltw(r, 0), &f, 0); return f;
+}
+
+MECANIM_FORCE_INLINE bool Vstoresb(vec4f r)
+{
+ union {
+ vec4f v;
+ int i[4];
+ } a; a.v = r;
+ return a.i[0] != 0;
+}
+
+// Aligned store
+#define Vstorepf(v, base, offset) __stvx((v), (base), (offset))
+
+MECANIM_FORCE_INLINE void Vstorepb(vec4f v, bool* r)
+{
+ union {
+ vec4f v;
+ int i[4];
+ } a; a.v = v;
+ r[0] = a.i[0] != 0;
+ r[1] = a.i[1] != 0;
+ r[2] = a.i[2] != 0;
+ r[3] = a.i[3] != 0;
+}
+
+MECANIM_FORCE_INLINE vec4f Vloadsf(float s)
+{
+ vec4f v = {s,s,s,s};
+ return v;
+}
+
+MECANIM_FORCE_INLINE vec4f Vloadsb(bool s)
+{
+ union {
+ int b[4];
+ vec4f v;
+ } static const false_true[2] = {
+ {0,0,0,0},
+ {~0,~0,~0,~0}
+ };
+
+ return false_true[s].v;
+}
+
+MECANIM_FORCE_INLINE vec4f Vload4sf(float x, float y, float z, float w)
+{
+ vec4f v = {x,y,z,w};
+ return v;
+}
+
+static MECANIM_FORCE_INLINE vec4f Vload4sb(bool x, bool y, bool z, bool w)
+{
+ union {
+ int b;
+ float f;
+ } static const false_true[2] = {
+ 0,~0
+ };
+
+ vec4f v = {false_true[x].f,false_true[y].f,false_true[z].f,false_true[w].f};
+ return v;
+}
+
+#define Vloadpf(v, offset) __lvx( (v), (offset))
+
+#define Vadd(l, r) __vaddfp((l), (r))
+
+#define Vsub( l, r) __vsubfp((l), (r))
+
+#define Vmul( l, r) __vmulfp((l), (r))
+
+MECANIM_FORCE_INLINE vec4f Vrcp(vec4f r)
+{
+ // This function does two iterations of Newton's method!
+ return XMVectorReciprocal(r);
+}
+
+MECANIM_FORCE_INLINE vec4f Vdiv(vec4f l, vec4f r)
+{
+ // This function does two iterations of Newton's method!
+ return XMVectorDivide(l, r);
+}
+
+#define Vmadd( a, b, c) __vmaddfp((a), (b), (c))
+
+#define Vmsub( a, b, c) Vneg(__vnmsubfp((a), (b), (c)))
+
+static const vec4f sign_constant = {-0.f,-0.f,-0.f,-0.f};
+
+#define Vneg(r) __vxor( (r), sign_constant)
+
+
+// vector sgn: return -1, 1
+#define Vsgn(r) __vor(Vone(), __vand(sign_constant, (r) ))
+
+// vector sgn: return -1, 0, 1
+static MECANIM_FORCE_INLINE vec4f Vsign(vec4f r)
+{
+ vec4f c = __vcmpeqfp(r, Vzero());
+ return __vor( __vand(__vnor(c,c), Vone()), __vand(sign_constant, r ));
+}
+
+#define Vinc(r) Vadd( (r), Vone())
+#define Vdec(r) Vsub( (r), Vone())
+#define Vabs(r) __vandc((r), sign_constant)
+#define Vmax( l, r) __vmaxfp((l), (r))
+#define Vmin( l, r) __vminfp((l), (r))
+
+MECANIM_FORCE_INLINE vec4fs Vlargest(vec4f r)
+{
+ r = Vmax(r, Vswizzle<kYZWX>::rhs(r));
+ r = Vmax(r, Vswizzle<kZWXY>::rhs(r));
+ return r;
+}
+
+MECANIM_FORCE_INLINE vec4fs Vsmallest(vec4f r)
+{
+ r = Vmin(r, Vswizzle<kYZWX>::rhs(r));
+ r = Vmin(r, Vswizzle<kZWXY>::rhs(r));
+ return r;
+}
+
+MECANIM_FORCE_INLINE vec4fs Vsum(vec4f r)
+{
+ r = Vadd(r, Vswizzle<kYZWX>::rhs(r) );
+ r = Vadd(r, Vswizzle<kZWXY>::rhs(r) );
+ return Vswizzle<kXXXX>::rhs(r);
+}
+
+#define Vdot( l, r) __vmsum4fp( (l), (r) )
+
+MECANIM_FORCE_INLINE vec4f Vrsqrt(vec4f r)
+{
+ static const vec4f three = {3.f,3.f,3.f,3.f};
+ static const vec4f a = {0.5f,0.5f,0.5f,0.5f};
+
+ vec4f const e = __vrsqrtefp(r);
+ return Vmul( Vmul(e, Vsub(three, Vmul( Vmul(e,e),r))), a);
+}
+
+
+#define Vsqrt(r) __vsel( Vmul(r, Vrsqrt(r)), Vzero(), __vcmpeqfp(r, Vzero()))
+
+#define Vcombine(x,y,z,w) __vmrghw(__vmrghw((x), (z)), __vmrghw((y), (w)))
+
+// Vector comparison
+#define Vcmpeq( a, b) __vcmpeqfp((a), (b))
+
+MECANIM_FORCE_INLINE vec4f Vcmpneq( vec4f a, vec4f b)
+{
+ vec4f c = __vcmpeqfp(a, b);
+ return __vnor(c, c);
+}
+
+#define Vcmpgt( a, b) __vcmpgtfp((a), (b))
+#define Vcmpge( a, b) __vcmpgefp((a), (b))
+#define Vcmplt( a, b) __vcmpgtfp((b), (a))
+#define Vcmple( a, b) __vcmpgefp((b), (a))
+
+#define Vsel( c, a, b) __vxor(b, __vand(__vxor(a, b), c))
+
+// vector logics
+#define Vnot(r) __vnor( (r), (r) )
+#define Vxnor( a, b) Vnot(__vxor((a), (b)))
+#define Vxor( a, b) __vxor((a), (b))
+#define Vand( a, b) __vand((a), (b))
+#define Vor( a, b) __vor((a), (b))
+
+MECANIM_FORCE_INLINE bool Vall(vec4b a)
+{
+ // All words equal
+ static const XMVECTORU32 vu32 = {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF};
+ const int All = 0x00000080;
+ unsigned int compareResult;
+ __vcmpequwR(a, vu32.v, &compareResult);
+ return (compareResult & All) == All;
+}
+
+MECANIM_FORCE_INLINE bool Vany(vec4b a)
+{
+ // Not all words equal to 0
+ const int All = 0x00000080;
+ unsigned int compareResult;
+ __vcmpequwR(a, Vzero(), &compareResult);
+ return (compareResult & All) == 0;
+}
+
+#endif
diff --git a/Runtime/Math/Simd/xform.h b/Runtime/Math/Simd/xform.h
new file mode 100644
index 0000000..0bfbc13
--- /dev/null
+++ b/Runtime/Math/Simd/xform.h
@@ -0,0 +1,153 @@
+#ifndef SIMD_XFORM_H
+#define SIMD_XFORM_H
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+#include "Runtime/Math/Simd/float4.h"
+#include "Runtime/Math/Simd/quaternion.h"
+
+namespace math
+{
+
+struct ATTRIBUTE_ALIGN(ALIGN4F) xform
+{
+ DEFINE_GET_TYPESTRING(xform)
+
+ inline xform():t(float4::zero()),q(quatIdentity()),s(float4::one()){}
+
+ inline xform( xform const& x)
+ { t = x.t; q = x.q; s = x.s; }
+
+ inline xform &operator=(xform const& x)
+ { t = x.t; q = x.q; s = x.s; return *this; }
+
+ inline xform(float4 const& t, float4 const& q, float4 const& s)
+ { this->t = t; this->q = q; this->s = s; }
+
+ float4 t;
+ float4 q;
+ float4 s;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(t);
+ TRANSFER(q);
+ TRANSFER(s);
+ }
+};
+
+static inline xform xformIdentity()
+{
+ return xform( float4::zero(), quatIdentity(), float4::one() );
+}
+
+static inline float4 xformMulVec(xform const& x, float4 const& v)
+{
+ return x.t + quatMulVec(x.q, v * x.s );
+}
+
+static inline float4 xformInvMulVec(xform const& x, float4 const& v)
+{
+ return quatMulVec(quatConj(x.q), v - x.t) / x.s;
+}
+
+static inline float4 xformInvMulVecNS(xform const& x, float4 const& v)
+{
+ return quatMulVec(quatConj(x.q), v - x.t);
+}
+
+static inline xform xformMul(xform const& a, xform const& b)
+{
+ return xform( xformMulVec(a, b.t), normalize(quatMul(a.q, b.q)), a.s * b.s);
+}
+
+static inline xform xformInvMul(xform const& a, xform const& b)
+{
+ return xform(xformInvMulVec(a, b.t), normalize( quatMul( quatConj(a.q), b.q)), b.s / a.s);
+}
+
+static inline xform xformInvMulNS(xform const& a, xform const& b)
+{
+ return xform(xformInvMulVecNS(a, b.t), normalize( quatMul( quatConj(a.q), b.q)), float4::one());
+}
+
+static inline xform xformMulInv(xform const& a, xform const& b)
+{
+ const float4 qinv = quatConj(b.q);
+ const float4 sinv = rcp(b.s);
+
+ return xform(xformMulVec(a, quatMulVec( qinv, -b.t) * sinv),normalize( quatMul( a.q, qinv)), a.s * sinv);
+}
+
+static inline xform xformBlend(xform const &a, xform const &b, float1 const& w)
+{
+ return xform(lerp(a.t, b.t, w),quatLerp(a.q, b.q, w),scaleBlend(a.s, b.s, w));
+}
+
+static inline bool operator==(xform const& l, xform const& r)
+{
+ return all(l.t == r.t) && all(l.q == r.q) && all(l.s == r.s);
+}
+
+static inline xform xformWeight(xform const& x, float1 const& w)
+{
+ return xform(x.t*w,quatWeight(x.q,w),scaleWeight(x.s,w));
+}
+
+static inline xform xformAdd(xform const& a, xform const& b)
+{
+ return xform(a.t+b.t,qtan2Quat(quat2Qtan(a.q)+quat2Qtan(b.q)),a.s*b.s);
+}
+
+static inline xform xformSub(xform const& a, xform const& b)
+{
+ return xform(a.t-b.t,qtan2Quat(quat2Qtan(a.q)-quat2Qtan(b.q)),a.s/b.s);
+}
+
+static inline xform xformBlend(xform const* apXFormArray, float const* apWeightArray, unsigned int aCount)
+{
+ xform ret;
+
+ ret.t = float4::zero();
+ ret.q = float4::zero();
+ ret.s = float4::one();
+
+ float sumW = 0;
+
+ unsigned int i;
+ for(i = 0; i < aCount ; i++)
+ {
+ float w = apWeightArray[i];
+ math::float1 w1(w);
+ sumW += w;
+
+ ret.t += apXFormArray[i].t*w1;
+ ret.q += cond(dot(ret.q,apXFormArray[i].q) < float1::zero(),apXFormArray[i].q * -w1,apXFormArray[i].q * w1);
+ ret.s *= scaleWeight(apXFormArray[i].s,w1);
+ }
+
+ float4 q(0,0,0,saturate(1.0f-sumW));
+ ret.q = normalize(ret.q+q);
+
+ return ret;
+}
+
+static inline xform mirror(xform const& x)
+{
+ constant_float4(mirrorQ,1,-1,-1,1);
+
+ xform ret = x;
+ ret.t = mirror(ret.t);
+ ret.q *= mirrorQ;
+ return ret;
+}
+
+static inline xform cond(bool c, xform const& a, xform const& b)
+{
+ return c?a:b;
+}
+
+}
+
+#endif
diff --git a/Runtime/Math/SphericalHarmonics.h b/Runtime/Math/SphericalHarmonics.h
new file mode 100644
index 0000000..539d316
--- /dev/null
+++ b/Runtime/Math/SphericalHarmonics.h
@@ -0,0 +1,83 @@
+#pragma once
+
+
+static inline void SHEvalDirection9 (float x, float y, float z, float outsh[9])
+{
+ // Core i7 920, VS2008 Release, FPU code:
+
+ // 114 clocks
+ //D3DXSHEvalDirection (outsh, 3, &D3DXVECTOR3(x,y,z));
+
+ /*
+ // 314 clocks
+ // Reference implementation from Stupid Spherical Harmonics Tricks
+ // http://www.ppsloan.org/publications/StupidSH36.pdf
+ const float kSqrtPI = sqrtf(kPI);
+ const float kSqrt3 = sqrtf(3.0f);
+ const float kSqrt15 = sqrtf(15.0f);
+ outsh[0] = 1.0f / (2.0f * kSqrtPI);
+ outsh[1] = -(kSqrt3 * y) / (2.0f * kSqrtPI);
+ outsh[2] = (kSqrt3 * z) / (2.0f * kSqrtPI);
+ outsh[3] = -(kSqrt3 * x) / (2.0f * kSqrtPI);
+ outsh[4] = (kSqrt15 * x * y) / (2.0f * kSqrtPI);
+ outsh[5] = -(kSqrt15 * y * z) / (2.0f * kSqrtPI);
+ outsh[6] = (sqrtf(5.0f) * (3.0f*z*z-1.0f)) / (4.0f * kSqrtPI);
+ outsh[7] = -(kSqrt15 * x * z) / (2.0f * kSqrtPI);
+ outsh[8] = (kSqrt15 * (x*x - y*y)) / (4.0f * kSqrtPI);
+ */
+
+ // 86 clocks
+ // Make sure all constants are never computed at runtime
+ const float kInv2SqrtPI = 0.28209479177387814347403972578039f; // 1 / (2*sqrt(kPI))
+ const float kSqrt3Div2SqrtPI = 0.48860251190291992158638462283835f; // sqrt(3) / (2*sqrt(kPI))
+ const float kSqrt15Div2SqrtPI = 1.0925484305920790705433857058027f; // sqrt(15) / (2*sqrt(kPI))
+ const float k3Sqrt5Div4SqrtPI = 0.94617469575756001809268107088713f; // 3 * sqrtf(5) / (4*sqrt(kPI))
+ const float kSqrt15Div4SqrtPI = 0.54627421529603953527169285290135f; // sqrt(15) / (4*sqrt(kPI))
+ const float kOneThird = 0.3333333333333333333333f; // 1.0/3.0
+ outsh[0] = kInv2SqrtPI;
+ outsh[1] = - y * kSqrt3Div2SqrtPI;
+ outsh[2] = z * kSqrt3Div2SqrtPI;
+ outsh[3] = - x * kSqrt3Div2SqrtPI;
+ outsh[4] = x * y * kSqrt15Div2SqrtPI;
+ outsh[5] = - y * z * kSqrt15Div2SqrtPI;
+ outsh[6] = (z*z-kOneThird) * k3Sqrt5Div4SqrtPI;
+ outsh[7] = - x * z * kSqrt15Div2SqrtPI;
+ outsh[8] = (x*x-y*y) * kSqrt15Div4SqrtPI;
+}
+
+
+static inline void SHEvalDirectionalLight9 (
+ float x, float y, float z,
+ float colorR, float colorG, float colorB,
+ float outR[9], float outG[9], float outB[9])
+{
+ // Core i7 920, VS2008 Release, FPU code:
+
+ // 397 clocks
+ //D3DXSHEvalDirectionalLight (3, &D3DXVECTOR3(x,y,z), colorR, colorG, colorB, outR, outG, outB);
+
+ // 300 clocks
+ float sh[9];
+ SHEvalDirection9 (x, y, z, sh);
+ // Normalization factor from http://www.ppsloan.org/publications/StupidSH36.pdf
+ const float kNormalization = 2.9567930857315701067858823529412f; // 16*kPI/17
+ float rscale = colorR * kNormalization;
+ float gscale = colorG * kNormalization;
+ float bscale = colorB * kNormalization;
+ for (int i = 0; i < 9; ++i)
+ {
+ float c = sh[i];
+ outR[i] = c * rscale;
+ outG[i] = c * gscale;
+ outB[i] = c * bscale;
+ }
+}
+
+// Add 'out' to the 0th SH coefficients
+static inline void SHEvalAmbientLight(const ColorRGBAf& ambient, float out[3])
+{
+ const float k2SqrtPI = 3.54490770181103205459633496668229f; // 2*sqrt(kPI)
+ out[0] = ambient.r * k2SqrtPI;
+ out[1] = ambient.g * k2SqrtPI;
+ out[2] = ambient.b * k2SqrtPI;
+}
diff --git a/Runtime/Math/Vector2.cpp b/Runtime/Math/Vector2.cpp
new file mode 100644
index 0000000..3c73465
--- /dev/null
+++ b/Runtime/Math/Vector2.cpp
@@ -0,0 +1,13 @@
+#include "UnityPrefix.h"
+#include "Vector2.h"
+#include <limits>
+
+using namespace std;
+
+const float Vector2f::epsilon = 0.00001F;
+const float Vector2f::infinity = numeric_limits<float>::infinity ();
+const Vector2f Vector2f::infinityVec = Vector2f (numeric_limits<float>::infinity (), numeric_limits<float>::infinity ());
+
+const Vector2f Vector2f::zero = Vector2f (0, 0);
+const Vector2f Vector2f::xAxis = Vector2f (1, 0);
+const Vector2f Vector2f::yAxis = Vector2f (0, 1);
diff --git a/Runtime/Math/Vector2.h b/Runtime/Math/Vector2.h
new file mode 100644
index 0000000..7a5a813
--- /dev/null
+++ b/Runtime/Math/Vector2.h
@@ -0,0 +1,126 @@
+#ifndef VECTOR2_H
+#define VECTOR2_H
+
+#include <algorithm>
+#include "FloatConversion.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Serialize/SerializationMetaFlags.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Modules/ExportModules.h"
+
+class Vector2f
+{
+ public:
+ float x, y;
+
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (Vector2f)
+
+ Vector2f () : x(0.f) , y(0.f) {}
+ Vector2f (float inX, float inY) { x = inX; y = inY; }
+ explicit Vector2f (const float* p) { x = p[0]; y = p[1]; }
+
+ void Set (float inX, float inY) { x = inX; y = inY; }
+
+ float* GetPtr () { return &x; }
+ const float* GetPtr ()const { return &x; }
+ float& operator[] (int i) { DebugAssertIf (i < 0 || i > 1); return (&x)[i]; }
+ const float& operator[] (int i)const { DebugAssertIf (i < 0 || i > 1); return (&x)[i]; }
+
+ Vector2f& operator += (const Vector2f& inV) { x += inV.x; y += inV.y; return *this; }
+ Vector2f& operator -= (const Vector2f& inV) { x -= inV.x; y -= inV.y; return *this; }
+ Vector2f& operator *= (const float s) { x *= s; y *= s; return *this; }
+ Vector2f& operator /= (const float s) { DebugAssertIf (CompareApproximately (s, 0.0F)); x /= s; y /= s; return *this; }
+ bool operator == (const Vector2f& v)const { return x == v.x && y == v.y; }
+ bool operator != (const Vector2f& v)const { return x != v.x || y != v.y; }
+
+
+ Vector2f operator - () const { return Vector2f (-x, -y); }
+
+ Vector2f& Scale (const Vector2f& inV) { x *= inV.x; y *= inV.y; return *this;}
+
+ static const float epsilon;
+ static const float infinity;
+ static const Vector2f infinityVec;
+ static EXPORT_COREMODULE const Vector2f zero;
+ static const Vector2f xAxis;
+ static const Vector2f yAxis;
+};
+
+inline Vector2f Scale (const Vector2f& lhs, const Vector2f& rhs) { return Vector2f (lhs.x * rhs.x, lhs.y * rhs.y); }
+
+inline Vector2f operator + (const Vector2f& lhs, const Vector2f& rhs) { return Vector2f (lhs.x + rhs.x, lhs.y + rhs.y); }
+inline Vector2f operator - (const Vector2f& lhs, const Vector2f& rhs) { return Vector2f (lhs.x - rhs.x, lhs.y - rhs.y); }
+inline float Dot (const Vector2f& lhs, const Vector2f& rhs) { return lhs.x * rhs.x + lhs.y * rhs.y; }
+
+inline float SqrMagnitude (const Vector2f& inV) { return Dot (inV, inV); }
+inline float Magnitude (const Vector2f& inV) { return SqrtImpl(Dot (inV, inV)); }
+
+inline float Angle (const Vector2f& lhs, const Vector2f& rhs) { return acos (std::min (1.0f, std::max (-1.0f, Dot (lhs, rhs) / (Magnitude (lhs) * Magnitude (rhs))))); }
+
+inline Vector2f operator * (const Vector2f& inV, float s) { return Vector2f (inV.x * s, inV.y * s); }
+inline Vector2f operator * (const float s, const Vector2f& inV) { return Vector2f (inV.x * s, inV.y * s); }
+inline Vector2f operator / (const Vector2f& inV, float s) { Vector2f temp (inV); temp /= s; return temp; }
+inline Vector2f Inverse (const Vector2f& inVec) { return Vector2f (1.0F / inVec.x, 1.0F / inVec.y); }
+
+// Normalizes a vector, asserts if the vector can be normalized
+inline Vector2f Normalize (const Vector2f& inV) { return inV / Magnitude (inV); }
+// Normalizes a vector, returns default vector if it can't be normalized
+inline Vector2f NormalizeSafe (const Vector2f& inV, const Vector2f& defaultV = Vector2f::zero);
+
+inline Vector2f Lerp (const Vector2f& from, const Vector2f& to, float t) { return to * t + from * (1.0f - t); }
+
+// Returns a vector with the smaller of every component from v0 and v1
+inline Vector2f min (const Vector2f& lhs, const Vector2f& rhs) { return Vector2f (std::min (lhs.x, rhs.x), std::min (lhs.y, rhs.y)); }
+// Returns a vector with the larger of every component from v0 and v1
+inline Vector2f max (const Vector2f& lhs, const Vector2f& rhs) { return Vector2f (std::max (lhs.x, rhs.x), std::max (lhs.y, rhs.y)); }
+
+bool CompareApproximately (const Vector2f& inV0, const Vector2f& inV1, float inMaxDist = Vector2f::epsilon);
+
+inline bool CompareApproximately (const Vector2f& inV0, const Vector2f& inV1, float inMaxDist)
+{
+ return SqrMagnitude (inV1 - inV0) < inMaxDist * inMaxDist;
+}
+
+inline bool IsNormalized (const Vector2f& vec, float epsilon = Vector2f::epsilon)
+{
+ return CompareApproximately (SqrMagnitude (vec), 1.0F, epsilon);
+}
+
+/// Returns the abs of every component of the vector
+inline Vector2f Abs (const Vector2f& v) { return Vector2f (Abs (v.x), Abs (v.y)); }
+
+inline bool IsFinite (const Vector2f& f)
+{
+ return IsFinite(f.x) & IsFinite(f.y);
+}
+
+template<class TransferFunction> inline
+void Vector2f::Transfer (TransferFunction& t)
+{
+ t.AddMetaFlag (kTransferUsingFlowMappingStyle);
+ t.Transfer (x, "x");
+ t.Transfer (y, "y");
+}
+
+inline Vector2f NormalizeFast (const Vector2f& inV)
+{
+ float m = SqrMagnitude (inV);
+ // GCC version of __frsqrte:
+ // static inline double __frsqrte (double x) {
+ // double y;
+ // asm ( "frsqrte %0, %1" : /*OUT*/ "=f" (y) : /*IN*/ "f" (x) );
+ // return y;
+ // }
+ return inV * FastInvSqrt (m);
+}
+
+inline Vector2f NormalizeSafe (const Vector2f& inV, const Vector2f& defaultV)
+{
+ float mag = Magnitude (inV);
+ if (mag > Vector2f::epsilon)
+ return inV / Magnitude (inV);
+ else
+ return defaultV;
+}
+
+#endif
diff --git a/Runtime/Math/Vector3.cpp b/Runtime/Math/Vector3.cpp
new file mode 100644
index 0000000..3195ef2
--- /dev/null
+++ b/Runtime/Math/Vector3.cpp
@@ -0,0 +1,361 @@
+#include "UnityPrefix.h"
+#include "Vector3.h"
+#include "Matrix3x3.h"
+#include <limits>
+
+#define FPFIXES 1
+
+using namespace std;
+
+const float Vector3f::epsilon = 0.00001F;
+const float Vector3f::infinity = numeric_limits<float>::infinity ();
+const Vector3f Vector3f::infinityVec = Vector3f (numeric_limits<float>::infinity (), numeric_limits<float>::infinity (), numeric_limits<float>::infinity ());
+
+const Vector3f Vector3f::zero = Vector3f (0, 0, 0);
+const Vector3f Vector3f::one = Vector3f (1.0F, 1.0F, 1.0F);
+const Vector3f Vector3f::xAxis = Vector3f (1, 0, 0);
+const Vector3f Vector3f::yAxis = Vector3f (0, 1, 0);
+const Vector3f Vector3f::zAxis = Vector3f (0, 0, 1);
+
+
+void OrthoNormalizeFast (Vector3f* inU, Vector3f* inV, Vector3f* inW)
+{
+ // compute u0
+ *inU = Normalize (*inU);
+
+ // compute u1
+ float dot0 = Dot (*inU, *inV);
+ *inV -= dot0 * *inU;
+ *inV = Normalize (*inV);
+
+ // compute u2
+ float dot1 = Dot (*inV, *inW);
+ dot0 = Dot (*inU, *inW);
+ *inW -= dot0 * *inU + dot1 * *inV;
+ *inW = Normalize (*inW);
+}
+
+void OrthoNormalize (Vector3f* inU, Vector3f* inV)
+{
+ // compute u0
+ float mag = Magnitude (*inU);
+ if (mag > Vector3f::epsilon)
+ *inU /= mag;
+ else
+ *inU = Vector3f (1.0F, 0.0F, 0.0F);
+
+ // compute u1
+ float dot0 = Dot (*inU, *inV);
+ *inV -= dot0 * *inU;
+ mag = Magnitude (*inV);
+ if (mag < Vector3f::epsilon)
+ *inV = OrthoNormalVectorFast (*inU);
+ else
+ *inV /= mag;
+}
+
+void OrthoNormalize (Vector3f* inU, Vector3f* inV, Vector3f* inW)
+{
+ // compute u0
+ float mag = Magnitude (*inU);
+ if (mag > Vector3f::epsilon)
+ *inU /= mag;
+ else
+ *inU = Vector3f (1.0F, 0.0F, 0.0F);
+
+ // compute u1
+ float dot0 = Dot (*inU, *inV);
+ *inV -= dot0 * *inU;
+ mag = Magnitude (*inV);
+ if (mag > Vector3f::epsilon)
+ *inV /= mag;
+ else
+ *inV = OrthoNormalVectorFast (*inU);
+
+ // compute u2
+ float dot1 = Dot (*inV, *inW);
+ dot0 = Dot (*inU, *inW);
+ *inW -= dot0 * *inU + dot1 * *inV;
+ mag = Magnitude (*inW);
+ if (mag > Vector3f::epsilon)
+ *inW /= mag;
+ else
+ *inW = Cross (*inU, *inV);
+}
+
+#define k1OverSqrt2 float(0.7071067811865475244008443621048490)
+
+Vector3f OrthoNormalVectorFast (const Vector3f& n)
+{
+ Vector3f res;
+ if (Abs (n.z) > k1OverSqrt2)
+ {
+ // choose p in y-z plane
+ float a = n.y*n.y + n.z*n.z;
+ float k = 1.0F / sqrt (a);
+ res.x = 0;
+ res.y = -n.z*k;
+ res.z = n.y*k;
+ }
+ else
+ {
+ // choose p in x-y plane
+ float a = n.x*n.x + n.y*n.y;
+ float k = 1.0F / sqrt (a);
+ res.x = -n.y*k;
+ res.y = n.x*k;
+ res.z = 0;
+ }
+ return res;
+}
+
+/* from chris hecker (Generates Orthonormal basis)
+void
+DextralBases(real32 const *XAxis, real32 *YAxis, real32 *ZAxis)
+{
+ real32 CrossVector[3] = {1.0f, 1.0f, 1.0f};
+
+ real32 MaximumElement = 0.0f;
+
+ int MaximumElementIndex = 0;
+ {for(int ElementIndex = 0;
+ ElementIndex < 3;
+ ++ElementIndex)
+ {
+ real32 ElementValue = AbsoluteValue(XAxis[ElementIndex]);
+ if(ElementValue > MaximumElement)
+ {
+ MaximumElement = ElementValue;
+ MaximumElementIndex = ElementIndex;
+ }
+ }}
+
+ CrossVector[MaximumElementIndex] = 0.0f;
+
+ VectorCrossProduct3(YAxis, CrossVector, XAxis);
+ Normalize3(YAxis);
+
+ VectorCrossProduct3(ZAxis, XAxis, YAxis);
+ Normalize3(ZAxis);
+}
+
+*/
+
+/// Returns a Vector3 that moves lhs towards rhs by a maximum of clampedDistance
+Vector3f MoveTowards (const Vector3f& lhs, const Vector3f& rhs, float clampedDistance)
+{
+ Vector3f delta = rhs - lhs;
+ float sqrDelta = SqrMagnitude (delta);
+ float sqrClampedDistance = clampedDistance * clampedDistance;
+ if (sqrDelta > sqrClampedDistance)
+ {
+ float deltaMag = sqrt (sqrDelta);
+ if (deltaMag > Vector3f::epsilon)
+ return lhs + delta / deltaMag * clampedDistance;
+ else
+ return lhs;
+ }
+ else
+ return rhs;
+}
+
+static inline float ClampedMove (float lhs, float rhs, float clampedDelta)
+{
+ float delta = rhs - lhs;
+ if (delta > 0.0F)
+ return lhs + min (delta, clampedDelta);
+ else
+ return lhs - min (-delta, clampedDelta);
+}
+
+Vector3f RotateTowards (const Vector3f& lhs, const Vector3f& rhs, float angleMove, float magnitudeMove)
+{
+ float lhsMag = Magnitude (lhs);
+ float rhsMag = Magnitude (rhs);
+
+ // both vectors are non-zero
+ if (lhsMag > Vector3f::epsilon && rhsMag > Vector3f::epsilon)
+ {
+ Vector3f lhsNorm = lhs / lhsMag;
+ Vector3f rhsNorm = rhs / rhsMag;
+
+ float dot = Dot (lhsNorm, rhsNorm);
+ // direction is almost the same
+ if (dot > 1.0F - Vector3f::epsilon)
+ {
+ return MoveTowards (lhs, rhs, magnitudeMove);
+ }
+ // directions are almost opposite
+ else if (dot < -1.0F + Vector3f::epsilon)
+ {
+ Vector3f axis = OrthoNormalVectorFast (lhsNorm);
+ Matrix3x3f m;
+ m.SetAxisAngle (axis, angleMove);
+ Vector3f rotated = m.MultiplyPoint3 (lhsNorm);
+ rotated *= ClampedMove (lhsMag, rhsMag, magnitudeMove);
+ return rotated;
+ }
+ // normal case
+ else
+ {
+ float angle = acos (dot);
+ Vector3f axis = Normalize (Cross (lhsNorm, rhsNorm));
+ Matrix3x3f m;
+ m.SetAxisAngle (axis, min (angleMove, angle));
+ Vector3f rotated = m.MultiplyPoint3 (lhsNorm);
+ rotated *= ClampedMove (lhsMag, rhsMag, magnitudeMove);
+ return rotated;
+ }
+ }
+ // at least one of the vectors is almost zero
+ else
+ {
+ return MoveTowards (lhs, rhs, magnitudeMove);
+ }
+}
+
+
+Vector3f Slerp (const Vector3f& lhs, const Vector3f& rhs, float t) {
+
+ float lhsMag = Magnitude (lhs);
+ float rhsMag = Magnitude (rhs);
+
+ if (lhsMag < Vector3f::epsilon || rhsMag < Vector3f::epsilon)
+ return Lerp (lhs, rhs, t);
+
+ float lerpedMagnitude = Lerp (lhsMag, rhsMag, t);
+
+ float dot = Dot (lhs, rhs) / (lhsMag * rhsMag);
+ // direction is almost the same
+ if (dot > 1.0F - Vector3f::epsilon)
+ {
+ return Lerp (lhs, rhs, t);
+ }
+ // directions are almost opposite
+ else if (dot < -1.0F + Vector3f::epsilon)
+ {
+ Vector3f lhsNorm = lhs / lhsMag;
+ Vector3f axis = OrthoNormalVectorFast (lhsNorm);
+ Matrix3x3f m;
+ m.SetAxisAngle (axis, kPI * t);
+ Vector3f slerped = m.MultiplyPoint3 (lhsNorm);
+ slerped *= lerpedMagnitude;
+ return slerped;
+ }
+ // normal case
+ else
+ {
+ Vector3f axis = Cross (lhs, rhs);
+ Vector3f lhsNorm = lhs / lhsMag;
+ axis = Normalize (axis);
+ float angle = acos (dot) * t;
+
+ Matrix3x3f m;
+ m.SetAxisAngle (axis, angle);
+ Vector3f slerped = m.MultiplyPoint3 (lhsNorm);
+ slerped *= lerpedMagnitude;
+ return slerped;
+ }
+}
+
+inline static Vector3f NormalizeRobust (const Vector3f& a, float &l, float &div)
+{
+ float a0,a1,a2,aa0,aa1,aa2;
+ a0 = a[0];
+ a1 = a[1];
+ a2 = a[2];
+
+#if FPFIXES
+ if ( CompareApproximately( a0, 0.0F, 0.00001F ) )
+ a0 = aa0 = 0;
+ else
+#endif
+ {
+ aa0 = Abs (a0);
+ }
+
+#if FPFIXES
+ if ( CompareApproximately( a1, 0.0F, 0.00001F ) )
+ a1 = aa1 =0;
+ else
+#endif
+ {
+ aa1 = Abs (a1);
+ }
+
+#if FPFIXES
+ if ( CompareApproximately( a2, 0.0F, 0.00001F ) )
+ a2 = aa2 = 0;
+ else
+#endif
+ {
+ aa2 = Abs (a2);
+ }
+
+ if (aa1 > aa0)
+ {
+ if (aa2 > aa1)
+ {
+ a0 /= aa2;
+ a1 /= aa2;
+ l = InvSqrt (a0*a0 + a1*a1 + 1.0F);
+ div = aa2;
+ return Vector3f (a0*l, a1*l, CopySignf (l,a2));
+ }
+ else
+ {
+ // aa1 is largest
+ a0 /= aa1;
+ a2 /= aa1;
+ l = InvSqrt (a0*a0 + a2*a2 + 1.0F);
+ div = aa1;
+ return Vector3f (a0*l, CopySignf (l, a1), a2*l);
+ }
+ }
+ else
+ {
+ if (aa2 > aa0)
+ {
+ // aa2 is largest
+ a0 /= aa2;
+ a1 /= aa2;
+ l = InvSqrt (a0*a0 + a1*a1 + 1.0F);
+ div = aa2;
+ return Vector3f (a0*l, a1*l, CopySignf (l,a2));
+ }
+ else
+ {
+ // aa0 is largest
+ if (aa0 <= 0)
+ {
+ l = 0;
+ div = 1;
+ return Vector3f (0.0F, 1.0F, 0.0F);
+ }
+
+ a1 /= aa0;
+ a2 /= aa0;
+ l = InvSqrt (a1*a1 + a2*a2 + 1.0F);
+ div = aa0;
+ return Vector3f (CopySignf (l,a0), a1*l, a2*l);
+ }
+ }
+}
+
+Vector3f NormalizeRobust (const Vector3f& a)
+{
+ float l, div;
+ return NormalizeRobust(a, l, div);
+}
+
+Vector3f NormalizeRobust (const Vector3f& a, float &invOriginalLength)
+{
+ float l, div;
+ const Vector3f &n = NormalizeRobust(a, l, div);
+ invOriginalLength = l/div;
+ // guard for NaNs
+ Assert (n == n);
+ Assert (invOriginalLength == invOriginalLength);
+ Assert (IsNormalized(n));
+ return n;
+} \ No newline at end of file
diff --git a/Runtime/Math/Vector3.h b/Runtime/Math/Vector3.h
new file mode 100644
index 0000000..ea4bbc4
--- /dev/null
+++ b/Runtime/Math/Vector3.h
@@ -0,0 +1,205 @@
+#ifndef VECTOR3_H
+#define VECTOR3_H
+
+#include <algorithm>
+#include "FloatConversion.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Serialize/SerializationMetaFlags.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Modules/ExportModules.h"
+
+class Vector3f
+{
+ public:
+
+ float x, y, z;
+
+ DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL (Vector3f)
+ template<class TransferFunction> void Transfer (TransferFunction& transfer);
+
+ Vector3f () : x(0.f), y(0.f), z(0.f) {}
+ Vector3f (float inX, float inY, float inZ) { x = inX; y = inY; z = inZ; }
+ explicit Vector3f (const float* array) { x = array[0]; y = array[1]; z = array[2]; }
+ void Set (float inX, float inY, float inZ) { x = inX; y = inY; z = inZ; }
+ void Set (const float* array) { x = array[0]; y = array[1]; z = array[2]; }
+
+ float* GetPtr () { return &x; }
+ const float* GetPtr ()const { return &x; }
+ float& operator[] (int i) { DebugAssertIf (i < 0 || i > 2); return (&x)[i]; }
+ const float& operator[] (int i)const { DebugAssertIf (i < 0 || i > 2); return (&x)[i]; }
+
+ bool operator == (const Vector3f& v)const { return x == v.x && y == v.y && z == v.z; }
+ bool operator != (const Vector3f& v)const { return x != v.x || y != v.y || z != v.z; }
+
+ Vector3f& operator += (const Vector3f& inV) { x += inV.x; y += inV.y; z += inV.z; return *this; }
+ Vector3f& operator -= (const Vector3f& inV) { x -= inV.x; y -= inV.y; z -= inV.z; return *this; }
+ Vector3f& operator *= (float s) { x *= s; y *= s; z *= s; return *this; }
+ Vector3f& operator /= (float s);
+
+ Vector3f operator - () const { return Vector3f (-x, -y, -z); }
+
+ Vector3f& Scale (const Vector3f& inV) { x *= inV.x; y *= inV.y; z *= inV.z; return *this;}
+
+
+ EXPORT_COREMODULE static const float epsilon;
+ EXPORT_COREMODULE static const float infinity;
+ EXPORT_COREMODULE static const Vector3f infinityVec;
+ EXPORT_COREMODULE static const Vector3f zero;
+ EXPORT_COREMODULE static const Vector3f one;
+ EXPORT_COREMODULE static const Vector3f xAxis;
+ EXPORT_COREMODULE static const Vector3f yAxis;
+ EXPORT_COREMODULE static const Vector3f zAxis;
+};
+
+inline Vector3f Scale (const Vector3f& lhs, const Vector3f& rhs) { return Vector3f (lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z); }
+
+inline Vector3f operator + (const Vector3f& lhs, const Vector3f& rhs) { return Vector3f (lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z); }
+inline Vector3f operator - (const Vector3f& lhs, const Vector3f& rhs) { return Vector3f (lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z); }
+inline Vector3f Cross (const Vector3f& lhs, const Vector3f& rhs);
+inline float Dot (const Vector3f& lhs, const Vector3f& rhs) { return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z; }
+
+inline Vector3f operator * (const Vector3f& inV, const float s) { return Vector3f (inV.x * s, inV.y * s, inV.z * s); }
+inline Vector3f operator * (const float s, const Vector3f& inV) { return Vector3f (inV.x * s, inV.y * s, inV.z * s); }
+inline Vector3f operator / (const Vector3f& inV, const float s) { Vector3f temp (inV); temp /= s; return temp; }
+inline Vector3f Inverse (const Vector3f& inVec) { return Vector3f (1.0F / inVec.x, 1.0F / inVec.y, 1.0F / inVec.z); }
+
+inline float SqrMagnitude (const Vector3f& inV) { return Dot (inV, inV); }
+inline float Magnitude (const Vector3f& inV) {return SqrtImpl(Dot (inV, inV));}
+
+// Normalizes a vector, asserts if the vector can be normalized
+inline Vector3f Normalize (const Vector3f& inV) { return inV / Magnitude (inV); }
+// Normalizes a vector, returns default vector if it can't be normalized
+inline Vector3f NormalizeSafe (const Vector3f& inV, const Vector3f& defaultV = Vector3f::zero);
+
+inline Vector3f ReflectVector (const Vector3f& inDirection, const Vector3f& inNormal) { return -2.0F * Dot (inNormal, inDirection) * inNormal + inDirection; }
+
+inline Vector3f Lerp (const Vector3f& from, const Vector3f& to, float t) { return to * t + from * (1.0F - t); }
+Vector3f Slerp (const Vector3f& from, const Vector3f& to, float t);
+
+// Returns a vector with the smaller of every component from v0 and v1
+inline Vector3f min (const Vector3f& lhs, const Vector3f& rhs) { return Vector3f (FloatMin (lhs.x, rhs.x), FloatMin (lhs.y, rhs.y), FloatMin (lhs.z, rhs.z)); }
+// Returns a vector with the larger of every component from v0 and v1
+inline Vector3f max (const Vector3f& lhs, const Vector3f& rhs) { return Vector3f (FloatMax (lhs.x, rhs.x), FloatMax (lhs.y, rhs.y), FloatMax (lhs.z, rhs.z)); }
+
+/// Project one vector onto another.
+inline Vector3f Project (const Vector3f& v1, const Vector3f& v2) { return v2* Dot (v1, v2)/ Dot (v2, v2); }
+
+
+/// Returns the abs of every component of the vector
+inline Vector3f Abs (const Vector3f& v) { return Vector3f (Abs (v.x), Abs (v.y), Abs (v.z)); }
+
+bool CompareApproximately (const Vector3f& inV0, const Vector3f& inV1, const float inMaxDist = Vector3f::epsilon);
+// Orthonormalizes the three vectors, assuming that a orthonormal basis can be formed
+void OrthoNormalizeFast (Vector3f* inU, Vector3f* inV, Vector3f* inW);
+// Orthonormalizes the three vectors, returns false if no orthonormal basis could be formed.
+EXPORT_COREMODULE void OrthoNormalize (Vector3f* inU, Vector3f* inV, Vector3f* inW);
+// Orthonormalizes the two vectors. inV is taken as a hint and will try to be as close as possible to inV.
+EXPORT_COREMODULE void OrthoNormalize (Vector3f* inU, Vector3f* inV);
+
+// Calculates a vector that is orthonormal to n.
+// Assumes that n is normalized
+Vector3f OrthoNormalVectorFast (const Vector3f& n);
+
+// Rotates lhs towards rhs by no more than max Angle
+// Moves the magnitude of lhs towards rhs by no more than maxMagnitude
+Vector3f RotateTowards (const Vector3f& lhs, const Vector3f& rhs, float maxAngle, float maxMagnitude);
+
+// Spherically interpolates the direction of two vectors
+// and interpolates the magnitude of the two vectors
+Vector3f Slerp (const Vector3f& lhs, const Vector3f& rhs, float t);
+
+/// Returns a Vector3 that moves lhs towards rhs by a maximum of clampedDistance
+Vector3f MoveTowards (const Vector3f& lhs, const Vector3f& rhs, float clampedDistance);
+
+inline bool IsNormalized (const Vector3f& vec, float epsilon = Vector3f::epsilon)
+{
+ return CompareApproximately (SqrMagnitude (vec), 1.0F, epsilon);
+}
+
+inline Vector3f Cross (const Vector3f& lhs, const Vector3f& rhs)
+{
+ return Vector3f (
+ lhs.y * rhs.z - lhs.z * rhs.y,
+ lhs.z * rhs.x - lhs.x * rhs.z,
+ lhs.x * rhs.y - lhs.y * rhs.x);
+}
+
+inline Vector3f NormalizeSafe (const Vector3f& inV, const Vector3f& defaultV)
+{
+ float mag = Magnitude (inV);
+ if (mag > Vector3f::epsilon)
+ return inV / Magnitude (inV);
+ else
+ return defaultV;
+}
+
+/// - Handles zero vector correclty
+inline Vector3f NormalizeFast (const Vector3f& inV)
+{
+ float m = SqrMagnitude (inV);
+ // GCC version of __frsqrte:
+ // static inline double __frsqrte (double x) {
+ // double y;
+ // asm ( "frsqrte %0, %1" : /*OUT*/ "=f" (y) : /*IN*/ "f" (x) );
+ // return y;
+ // }
+ return inV * FastInvSqrt (m);
+}
+
+/// - low precision normalize
+/// - nan for zero vector
+inline Vector3f NormalizeFastest (const Vector3f& inV)
+{
+ float m = SqrMagnitude (inV);
+ // GCC version of __frsqrte:
+ // static inline double __frsqrte (double x) {
+ // double y;
+ // asm ( "frsqrte %0, %1" : /*OUT*/ "=f" (y) : /*IN*/ "f" (x) );
+ // return y;
+ // }
+ return inV * FastestInvSqrt (m);
+}
+
+inline bool IsFinite (const Vector3f& f)
+{
+ return IsFinite(f.x) & IsFinite(f.y) & IsFinite(f.z);
+}
+
+
+
+inline bool CompareApproximately (const Vector3f& inV0, const Vector3f& inV1, const float inMaxDist)
+{
+ return SqrMagnitude (inV1 - inV0) < inMaxDist * inMaxDist;
+}
+
+inline Vector3f& Vector3f::operator /= (float s)
+{
+ DebugAssertIf (CompareApproximately (s, 0.0F));
+ x /= s;
+ y /= s;
+ z /= s;
+ return *this;
+}
+
+template<class TransferFunction>
+inline void Vector3f::Transfer (TransferFunction& t)
+{
+ t.AddMetaFlag (kTransferUsingFlowMappingStyle);
+ t.Transfer (x, "x");
+ t.Transfer (y, "y");
+ t.Transfer (z, "z");
+}
+
+// this may be called for vectors `a' with extremely small magnitude, for
+// example the result of a cross product on two nearly perpendicular vectors.
+// we must be robust to these small vectors. to prevent numerical error,
+// first find the component a[i] with the largest magnitude and then scale
+// all the components by 1/a[i]. then we can compute the length of `a' and
+// scale the components by 1/l. this has been verified to work with vectors
+// containing the smallest representable numbers.
+Vector3f NormalizeRobust (const Vector3f& a);
+// This also returns vector's inverse original length, to avoid duplicate
+// invSqrt calculations when needed. If a is a zero vector, invOriginalLength will be 0.
+Vector3f NormalizeRobust (const Vector3f& a, float &invOriginalLength);
+
+#endif
diff --git a/Runtime/Math/Vector4.h b/Runtime/Math/Vector4.h
new file mode 100644
index 0000000..97f2ec0
--- /dev/null
+++ b/Runtime/Math/Vector4.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include "Vector3.h"
+
+class Vector4f
+{
+public:
+ Vector4f() {}
+ Vector4f( float inX, float inY, float inZ, float inW ) : x(inX), y(inY), z(inZ), w(inW) {}
+ explicit Vector4f( const Vector3f& v, float inW ) : x(v.x), y(v.y), z(v.z), w(inW) {}
+ explicit Vector4f( const float* v ) : x(v[0]), y(v[1]), z(v[2]), w(v[3]) {}
+
+ void Set( float inX, float inY, float inZ, float inW ) { x = inX; y = inY; z = inZ; w = inW; }
+ void Set( const float* array ) { x = array[0]; y = array[1]; z = array[2]; w = array[3]; }
+
+ float* GetPtr() { return &x; }
+ const float* GetPtr() const { return &x; }
+
+ float& operator[] (int i) { DebugAssertIf (i < 0 || i > 3); return (&x)[i]; }
+ const float& operator[] (int i)const { DebugAssertIf (i < 0 || i > 3); return (&x)[i]; }
+
+ bool operator == (const Vector4f& v) const { return x == v.x && y == v.y && z == v.z && w == v.w; }
+ bool operator != (const Vector4f& v) const { return x != v.x || y != v.y || z != v.z || w != v.w; }
+ bool operator == (const float v[4]) const { return x == v[0] && y == v[1] && z == v[2] && w == v[3]; }
+ bool operator != (const float v[4]) const { return x != v[0] || y != v[1] || z != v[2] || w != v[3]; }
+
+ Vector4f operator - () const { return Vector4f (-x, -y, -z, -w); }
+
+ DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL (Vector4f)
+ template<class TransferFunction> void Transfer (TransferFunction& transfer);
+
+ float x;
+ float y;
+ float z;
+ float w;
+};
+
+
+inline Vector4f operator * (const Vector4f& lhs, const Vector4f& rhs) { return Vector4f (lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); }
+inline Vector4f operator * (const Vector4f& inV, const float s) { return Vector4f (inV.x * s, inV.y * s, inV.z * s, inV.w * s); }
+inline Vector4f operator + (const Vector4f& lhs, const Vector4f& rhs) { return Vector4f (lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); }
+inline Vector4f operator - (const Vector4f& lhs, const Vector4f& rhs) { return Vector4f (lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w); }
+inline float Dot (const Vector4f& lhs, const Vector4f& rhs) { return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z + lhs.w * rhs.w; }
+
+inline Vector4f Lerp (const Vector4f& from, const Vector4f& to, float t) { return to * t + from * (1.0F - t); }
+
+template<class TransferFunction>
+inline void Vector4f::Transfer (TransferFunction& t)
+{
+ t.AddMetaFlag (kTransferUsingFlowMappingStyle);
+ t.Transfer (x, "x");
+ t.Transfer (y, "y");
+ t.Transfer (z, "z");
+ t.Transfer (w, "w");
+}
diff --git a/Runtime/Misc/Allocator.cpp b/Runtime/Misc/Allocator.cpp
new file mode 100644
index 0000000..0c1f3c0
--- /dev/null
+++ b/Runtime/Misc/Allocator.cpp
@@ -0,0 +1,18 @@
+#include "UnityPrefix.h"
+#if defined(UNIT_TEST)
+#include <cassert>
+#define AssertIf(x) assert(!(x))
+#else
+#include "Configuration/UnityConfigure.h"
+#endif
+
+#include <map>
+
+#if UNITY_WIN || UNITY_ANDROID
+#include <stdlib.h>
+#endif
+
+#include "Allocator.h"
+#if UNITY_WII
+#include <rvlaux/clib.h>
+#endif
diff --git a/Runtime/Misc/Allocator.h b/Runtime/Misc/Allocator.h
new file mode 100644
index 0000000..88480ca
--- /dev/null
+++ b/Runtime/Misc/Allocator.h
@@ -0,0 +1,17 @@
+#ifndef UNITY_CUSTOM_ALLOCATOR_H_
+#define UNITY_CUSTOM_ALLOCATOR_H_
+
+
+
+#if UNITY_WII
+# define MemPool1 0
+# define MemPool2 1
+#elif UNITY_XENON
+# define MemPool1 0
+# define MemPool2 0
+#else
+# define MemPool1 0
+# define MemPool2 1
+#endif
+
+#endif // UNITY_CUSTOM_ALLOCATOR_H_
diff --git a/Runtime/Misc/AllocatorLabelNames.h b/Runtime/Misc/AllocatorLabelNames.h
new file mode 100644
index 0000000..6f95101
--- /dev/null
+++ b/Runtime/Misc/AllocatorLabelNames.h
@@ -0,0 +1,107 @@
+
+////////////////////////////////////////////////////////////////////////////////
+////// This has to be in sync with ENUM WiiMemory in playerSettings.txt ////////
+////// There is also a Wii area map in AllocatorLabels.cpp ////////
+////////////////////////////////////////////////////////////////////////////////
+
+DO_LABEL(Default)
+DO_LABEL(Permanent)
+DO_LABEL(NewDelete)
+DO_LABEL(MallocFree)
+DO_LABEL(Thread)
+DO_LABEL(PVS)
+DO_LABEL(Manager)
+DO_LABEL(DynamicGeometry)
+DO_LABEL(VertexData)
+DO_LABEL(ImmediateGeometry)
+DO_LABEL(Geometry)
+DO_LABEL(BatchedGeometry)
+DO_LABEL(Particles)
+DO_LABEL(Texture)
+DO_LABEL(Shader)
+DO_LABEL(TextureCache)
+DO_LABEL(GfxDevice)
+DO_LABEL(GfxThread)
+DO_LABEL(Animation)
+DO_LABEL(Audio)
+DO_LABEL(AudioData)
+DO_LABEL(AudioProcessing)
+DO_LABEL(FMOD)
+DO_LABEL(Font)
+DO_LABEL(Physics)
+DO_LABEL(Serialization)
+DO_LABEL(IO)
+DO_LABEL(IO2)
+DO_LABEL(ThreadStack)
+DO_LABEL(TextAsset)
+DO_LABEL(GarbageCollector)
+DO_LABEL(GLib)
+DO_LABEL(GLibImage)
+DO_LABEL(Mono)
+DO_LABEL(MonoCode)
+DO_LABEL(BaseObject)
+DO_LABEL(Resource)
+DO_LABEL(Renderer)
+DO_LABEL(Transform)
+DO_LABEL(File)
+DO_LABEL(TempOverflow)
+DO_LABEL(Network)
+DO_LABEL(WebCam)
+DO_LABEL(Profiler)
+DO_LABEL(MemoryProfiler)
+DO_LABEL(MemoryProfilerString)
+DO_LABEL(Culling)
+DO_LABEL(Skinning)
+DO_LABEL(Terrain)
+DO_LABEL(Shadow)
+DO_LABEL(STL)
+DO_LABEL(String)
+DO_LABEL(StaticString)
+DO_LABEL(DynamicArray)
+DO_LABEL(UTF16String)
+DO_LABEL(Utility)
+DO_LABEL(Curl)
+DO_LABEL(PoolAlloc)
+DO_LABEL(Navigation)
+DO_LABEL(AssetServerCache)
+DO_LABEL(TypeTree)
+DO_LABEL(ScriptManager)
+DO_LABEL(Substance)
+DO_LABEL(Sprites)
+DO_LABEL(ClusterRenderer)
+
+// Editor Specific
+DO_LABEL(EditorGui)
+DO_LABEL(EditorUtility)
+DO_LABEL(VersionControl)
+DO_LABEL(UndoBuffer)
+DO_LABEL(Undo)
+DO_LABEL(AssetDatabase)
+DO_LABEL(PreviewImage)
+DO_LABEL(AssetImporter)
+DO_LABEL(FBXImporter)
+DO_LABEL(TempAlloc)
+
+// Wii Specific
+DO_LABEL(WiiDefault1)
+DO_LABEL(WiiDefault2)
+DO_LABEL(WiiRVLAux1)
+DO_LABEL(WiiRVLAux2)
+DO_LABEL(WiiRenderTexture)
+DO_LABEL(WiiStrapReminder)
+DO_LABEL(WiiHBM)
+DO_LABEL(WiiMovie)
+DO_LABEL(WiiInput)
+DO_LABEL(WiiNand)
+DO_LABEL(WiiVI)
+DO_LABEL(WiiSkinning)
+DO_LABEL(WiiPThreads)
+
+// PS3 Specific
+DO_LABEL(PS3VideoMemory)
+DO_LABEL(PS3RingBuffers)
+DO_LABEL(PS3RSXBuffers)
+DO_LABEL(PS3DelayedRelease)
+
+// Metro Specific
+DO_LABEL(WinRTTLS)
diff --git a/Runtime/Misc/AllocatorLabels.cpp b/Runtime/Misc/AllocatorLabels.cpp
new file mode 100644
index 0000000..c778296
--- /dev/null
+++ b/Runtime/Misc/AllocatorLabels.cpp
@@ -0,0 +1,63 @@
+#include "UnityPrefix.h"
+#include "AllocatorLabels.h"
+#include "Allocator.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+
+void InitializeMemoryLabels()
+{
+#define INITIALIZE_LABEL_STRUCT(Name) Name.Initialize(Name##Id);
+#define DO_LABEL(Name) INITIALIZE_LABEL_STRUCT(kMem##Name)
+#include "AllocatorLabelNames.h"
+#undef DO_LABEL
+#undef INITIALIZE_LABEL_STRUCT
+}
+
+const char* const MemLabelName[] =
+{
+#define DO_LABEL(Name) #Name ,
+ #include "AllocatorLabelNames.h"
+#undef DO_LABEL
+};
+
+#define DO_LABEL_STRUCT(Name) EXPORT_COREMODULE Name##Struct Name;
+#define DO_LABEL(Name) DO_LABEL_STRUCT(kMem##Name)
+#include "AllocatorLabelNames.h"
+#undef DO_LABEL
+#undef DO_LABEL_STRUCT
+
+#if ENABLE_MEM_PROFILER
+
+MemLabelId::MemLabelId( const MemLabelId& other ) : label(other.label), rootReference(NULL)
+{
+ rootReference = copy_root_reference(other.rootReference);
+}
+
+void MemLabelId::SetRootHeader( ProfilerAllocationHeader* rootHeader )
+{
+ if (rootReference)
+ ReleaseReference();
+ rootReference = get_root_reference_from_header(rootHeader);
+}
+
+ProfilerAllocationHeader* MemLabelId::GetRootHeader() const
+{
+ return rootReference ? get_root_header_from_reference(rootReference) : NULL;
+}
+
+void MemLabelId::ReleaseReference()
+{
+ release_root_reference(rootReference);
+ rootReference = NULL;
+}
+
+const MemLabelId& MemLabelId::operator=( const MemLabelId& other )
+{
+ label = other.label;
+ if (rootReference)
+ ReleaseReference();
+ rootReference = copy_root_reference(other.rootReference);
+ return *this;
+}
+
+#endif
+
diff --git a/Runtime/Misc/AllocatorLabels.h b/Runtime/Misc/AllocatorLabels.h
new file mode 100644
index 0000000..1c89d28
--- /dev/null
+++ b/Runtime/Misc/AllocatorLabels.h
@@ -0,0 +1,72 @@
+#ifndef ALLOCATORLABELS_H
+#define ALLOCATORLABELS_H
+
+#include "Runtime/Modules/ExportModules.h"
+
+#define ENABLE_MEM_PROFILER (ENABLE_MEMORY_MANAGER && ENABLE_PROFILER)
+#if ENABLE_MEM_PROFILER
+#define IF_MEMORY_PROFILER_ENABLED(x) x
+#else
+#define IF_MEMORY_PROFILER_ENABLED(x)
+#endif
+
+// Must be in Sync with ENUM WiiMemory in PlayerSettings.txt
+enum MemLabelIdentifier
+{
+#define DO_LABEL(Name) kMem##Name##Id ,
+#include "AllocatorLabelNames.h"
+#undef DO_LABEL
+ kMemLabelCount
+};
+
+struct AllocationRootReference;
+struct ProfilerAllocationHeader;
+
+
+struct EXPORT_COREMODULE MemLabelId {
+ MemLabelId() { IF_MEMORY_PROFILER_ENABLED( rootReference = NULL ); }
+ explicit MemLabelId ( MemLabelIdentifier id, ProfilerAllocationHeader* root ) : label(id) { IF_MEMORY_PROFILER_ENABLED( rootReference = NULL; SetRootHeader(root) ); }
+ void Initialize ( MemLabelIdentifier id ) { label = id; IF_MEMORY_PROFILER_ENABLED( rootReference = NULL ); }
+
+ MemLabelIdentifier label;
+
+#if ENABLE_MEM_PROFILER
+ MemLabelId ( const MemLabelId& other );
+ ~MemLabelId () { if(rootReference) ReleaseReference(); }
+
+ const MemLabelId& operator= (const MemLabelId& other);
+ void ReleaseReference();
+ ProfilerAllocationHeader* GetRootHeader () const;
+ void SetRootHeader ( ProfilerAllocationHeader* rootHeader );
+ bool UseAutoRoot () const { return rootReference == NULL; };
+private:
+ // root reference = NULL: use stack to get root
+ AllocationRootReference* rootReference;
+#else
+ ProfilerAllocationHeader* GetRootHeader () const {return NULL;}
+ void SetRootHeader ( ProfilerAllocationHeader* rootHeader ) {}
+#endif
+};
+
+#if ENABLE_MEM_PROFILER
+typedef const MemLabelId& MemLabelRef;
+#else
+typedef MemLabelId MemLabelRef;
+#endif
+
+#define DO_LABEL_STRUCT(Name) struct EXPORT_COREMODULE Name##Struct : public MemLabelId { }; extern EXPORT_COREMODULE Name##Struct Name;
+
+#define DO_LABEL(Name) DO_LABEL_STRUCT(kMem##Name)
+#include "AllocatorLabelNames.h"
+#undef DO_LABEL
+
+#undef DO_LABEL_STRUCT
+
+void InitializeMemoryLabels();
+
+//#if UNITY_ENABLE_ACCOUNTING_ALLOCATORS
+extern const char* const MemLabelName[];
+//#endif
+
+
+#endif
diff --git a/Runtime/Misc/AssetBundle.cpp b/Runtime/Misc/AssetBundle.cpp
new file mode 100644
index 0000000..ce65283
--- /dev/null
+++ b/Runtime/Misc/AssetBundle.cpp
@@ -0,0 +1,183 @@
+#include "UnityPrefix.h"
+#include "AssetBundle.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Misc/ResourceManager.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/Utility/RuntimeClassHashing.h"
+#endif
+
+#if ENABLE_WWW
+#include "PlatformDependent/CommonWebPlugin/UnityWebStream.h"
+#endif
+
+#if ENABLE_CACHING
+#include "Runtime/Misc/CachingManager.h"
+#endif
+
+using namespace std;
+
+IMPLEMENT_CLASS (AssetBundle)
+IMPLEMENT_OBJECT_SERIALIZE (AssetBundle)
+
+AssetBundle::AssetBundle (MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode)
+{
+#if ENABLE_WWW
+ m_UnityWebStream = NULL;
+#endif
+#if ENABLE_CACHING
+ m_CachedUnityWebStream = NULL;
+#endif
+
+ m_UncompressedFileInfo = NULL;
+ m_RuntimeCompatibility = CURRENT_RUNTIME_COMPATIBILITY_VERSION;
+
+ // Mark as kDontSave, so that AssetBundles are not unloaded on level loads
+ SetHideFlags(Object::kDontSave);
+}
+
+template<class TransferFunc>
+void AssetBundle::AssetInfo::Transfer (TransferFunc& transfer)
+{
+ TRANSFER(preloadIndex);
+ TRANSFER(preloadSize);
+ TRANSFER(asset);
+}
+
+template<class TransferFunc>
+void AssetBundleScriptInfo::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (className);
+ TRANSFER (nameSpace);
+ TRANSFER (assemblyName);
+ TRANSFER (hash);
+}
+
+template<class TransferFunc>
+void AssetBundle::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion (3);
+ AssertIf (transfer.GetFlags() & kPerformUnloadDependencyTracking);
+
+ if (transfer.IsReading ())
+ m_RuntimeCompatibility = 0;
+
+ if (transfer.IsOldVersion(1))
+ {
+ multimap<UnityStr, PPtr<Object> > oldContainer;
+ transfer.Transfer(oldContainer, "m_Container");
+ PPtr<Object> mainAsset;
+ transfer.Transfer(mainAsset, "m_MainAsset");
+
+ m_Container.clear();
+ for (multimap<UnityStr, PPtr<Object> >::iterator i=oldContainer.begin();i != oldContainer.end();i++)
+ {
+ AssetInfo info;
+ info.preloadIndex = 0;
+ info.preloadSize = 0;
+ info.asset = i->second;
+ m_Container.insert(make_pair(i->first, info));
+ }
+
+ m_MainAsset.preloadIndex = 0;
+ m_MainAsset.preloadSize = 0;
+ m_MainAsset.asset = mainAsset;
+ }
+ else
+ {
+ transfer.Transfer(m_PreloadTable, "m_PreloadTable");
+ transfer.Transfer(m_Container, "m_Container");
+ transfer.Transfer(m_MainAsset, "m_MainAsset");
+ transfer.Transfer(m_ScriptCompatibility, "m_ScriptCompatibility");
+ transfer.Transfer(m_ClassCompatibility, "m_ClassCompatibility");
+
+ if (!transfer.IsOldVersion (2))
+ transfer.Transfer (m_RuntimeCompatibility, "m_RuntimeCompatibility");
+ }
+}
+
+void AssetBundle::DebugPrintContents ()
+{
+ for (iterator i=m_Container.begin();i != m_Container.end();i++)
+ {
+ printf_console("- %s\n", i->first.c_str());
+ }
+}
+
+AssetBundle::range AssetBundle::GetPathRange (const string& path)
+{
+// if (m_Container.equal_range(ToLower(path)).first == m_Container.equal_range(ToLower(path)).second)
+// {
+// printf_console(("Failed loading " + path + "\n***********\n").c_str());
+// DebugPrintContents();
+// }
+
+ return m_Container.equal_range(ToLower(path));
+}
+
+AssetBundle::range AssetBundle::GetAll ()
+{
+ return make_pair (m_Container.begin(), m_Container.end());
+}
+
+Object* AssetBundle::GetImpl (int classID, const string& path)
+{
+ range r = GetPathRange(path);
+ for (iterator i=r.first;i != r.second;i++)
+ {
+ Object* obj = i->second.asset;
+ if (obj && obj->IsDerivedFrom(classID))
+ return obj;
+ }
+
+ //printf_console(("Failed loading " + path + "\n***********\n").c_str());
+ //DebugPrintContents();
+
+ return NULL;
+}
+
+AssetBundle::~AssetBundle ()
+{
+#if ENABLE_WWW
+ if (m_UnityWebStream)
+ m_UnityWebStream->Release();
+#endif
+#if ENABLE_CACHING
+ UNITY_DELETE(m_CachedUnityWebStream, kMemFile);
+#endif
+ // Don't kill of m_UncompressedFileInfo here due to weird
+ // logic in UnloadAssetBundle() that will access the UncompressedFileInfo
+ // even after the AssetBundle has been deleted through DestroyAllAtPath().
+}
+
+bool AssetBundle::ShouldIgnoreInGarbageDependencyTracking ()
+{
+ return true;
+}
+
+#if UNITY_EDITOR
+
+void AssetBundle::AddScriptCompatibilityInfo (std::string const& className, std::string const& nameSpace, std::string const& assembly, UInt32 hash)
+{
+ m_ScriptCompatibility.push_back (AssetBundleScriptInfo (className, nameSpace, assembly, hash));
+}
+
+void AssetBundle::FillHashTableForRuntimeClasses (std::vector<SInt32> const& classIds, TransferInstructionFlags transferFlags)
+{
+ vector_map<int, UInt32> hashes;
+ CalculateHashForClasses (hashes, classIds, transferFlags);
+ m_ClassCompatibility.resize (hashes.size ());
+ std::copy (hashes.begin (), hashes.end (), m_ClassCompatibility.begin ());
+}
+
+AssetBundle* GetEditorAssetBundle ()
+{
+ int instanceID = GetPersistentManager().GetInstanceIDFromPathAndFileID(kEditorResourcePath, 1);
+ PPtr<AssetBundle> res (instanceID);
+ return res;
+}
+
+#endif
diff --git a/Runtime/Misc/AssetBundle.h b/Runtime/Misc/AssetBundle.h
new file mode 100644
index 0000000..803c8b7
--- /dev/null
+++ b/Runtime/Misc/AssetBundle.h
@@ -0,0 +1,127 @@
+#pragma once
+
+#include "Runtime/BaseClasses/NamedObject.h"
+
+#include <map>
+#include <utility>
+
+class UnityWebStream;
+class CachedUnityWebStream;
+
+class AssetBundleScriptInfo
+{
+public:
+ DECLARE_SERIALIZE(AssetBundleScriptInfo)
+
+ AssetBundleScriptInfo () {}
+ AssetBundleScriptInfo (const UnityStr& name, const UnityStr& ns, const UnityStr& assembly, UInt32 h) : className (name), nameSpace (ns), hash (h), assemblyName(assembly) {}
+ UnityStr className;
+ UnityStr nameSpace;
+ UnityStr assemblyName;
+ UInt32 hash;
+};
+
+
+class AssetBundle : public NamedObject
+{
+public:
+
+ typedef std::vector<AssetBundleScriptInfo> ScriptCompatibility;
+ typedef std::vector<std::pair<int, UInt32> > ClassCompatibility;
+
+ enum
+ {
+ /// A simple integer version count to keep track of changes to the
+ /// runtime that cause asset bundles to no longer work as intended.
+ /// Increase this version whenever you need to break backwards-compatibility.
+ /// An example of this is when we started to no longer package all default
+ /// resources with every player and thus broke asset bundles that were
+ /// referencing these resources.
+ ///
+ /// @note Runtime compatibility checks are bypassed for the webplayer where
+ /// breaking backwards-compatibility is not allowed at this point.
+ ///
+ /// @see TestAssetBundleCompatiblity
+ CURRENT_RUNTIME_COMPATIBILITY_VERSION = 1
+ };
+
+ struct AssetInfo
+ {
+ DECLARE_SERIALIZE(AssetInfo)
+
+ int preloadIndex;
+ int preloadSize;
+
+ PPtr<Object> asset;
+
+ AssetInfo () { preloadIndex = 0; preloadSize = 0; }
+ };
+ struct UncompressedFileInfo
+ {
+ std::string fileName;
+ UInt32 offset, size;
+
+ bool operator < (UncompressedFileInfo const& a) const { return fileName < a.fileName; }
+ };
+ typedef std::vector<UncompressedFileInfo> UncompressedFileInfoContainer;
+
+ DECLARE_OBJECT_SERIALIZE (AssetBundle)
+ REGISTER_DERIVED_CLASS (AssetBundle, NamedObject)
+
+ AssetBundle (MemLabelId label, ObjectCreationMode mode);
+ // ~AssetBundle (); declared-by-macro
+
+ typedef std::multimap<UnityStr, AssetInfo> AssetMap;
+ typedef AssetMap::iterator iterator;
+ typedef std::pair<iterator, iterator> range;
+
+ range GetAll ();
+ range GetPathRange (const string& path);
+
+ virtual bool ShouldIgnoreInGarbageDependencyTracking ();
+
+ void DebugPrintContents ();
+
+ // AssetBundle* file = GetEditorAssetBundle();
+ // if (file) {
+ // // Path MUST omit the extension
+ // MonoBehaviour* be = file->Get<MonoBehaviour>(MySkin);
+ // }
+ template<class T>
+ T* Get (const string& path)
+ {
+ Object* res = GetImpl (T::GetClassIDStatic (), path);
+ return static_cast<T*> (res);
+ }
+
+ Object* GetImpl (int classID, const string& path);
+
+ void AddScriptCompatibilityInfo (std::string const& className, std::string const& nameSpace, std::string const& assembly, UInt32 hash);
+ void FillHashTableForRuntimeClasses (std::vector<SInt32> const& classIds, TransferInstructionFlags transferFlags);
+
+ UInt32 m_RuntimeCompatibility;
+ ScriptCompatibility m_ScriptCompatibility;
+ ClassCompatibility m_ClassCompatibility;
+
+ /// AssetInfo for the main asset. Has no associated name.
+ AssetInfo m_MainAsset;
+
+ /// Table of objects that need to be pulled from the bundle by the preload
+ /// manager when a specific asset is loaded from the bundle. Each AssetInfo
+ /// entry has an associated range of entries in the preload table.
+ std::vector<PPtr<Object> > m_PreloadTable;
+
+ /// Map of named assets contained in the bundle. Multiple objects may
+ /// have the same name.
+ AssetMap m_Container;
+
+#if ENABLE_WWW
+ UnityWebStream* m_UnityWebStream;
+#endif
+#if ENABLE_CACHING
+ CachedUnityWebStream* m_CachedUnityWebStream;
+#endif
+ UncompressedFileInfoContainer* m_UncompressedFileInfo;
+};
+
+AssetBundle* GetEditorAssetBundle ();
diff --git a/Runtime/Misc/AssetBundleUtility.cpp b/Runtime/Misc/AssetBundleUtility.cpp
new file mode 100644
index 0000000..4c5b851
--- /dev/null
+++ b/Runtime/Misc/AssetBundleUtility.cpp
@@ -0,0 +1,692 @@
+#include "UnityPrefix.h"
+#include "AssetBundleUtility.h"
+#include "Configuration/UnityConfigureVersion.h"
+#include "Configuration/UnityConfigureOther.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+
+#if ENABLE_WWW
+#include "Runtime/Serialize/TypeTree.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+#include "PlatformDependent/CommonWebPlugin/FileStream.h"
+#include "PlatformDependent/CommonWebPlugin/UnityWebStream.h"
+#include "Runtime/Export/WWW.h"
+#include "Runtime/Misc/WWWCached.h"
+#endif
+
+#include "Runtime/Misc/Player.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/AssetBundle.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/Mono/MonoScriptManager.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Misc/SaveAndLoadHelper.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/GetComponent.h"
+#include "Runtime/Threads/ThreadUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/EditorUserBuildSettings.h"
+#include "Runtime/Serialize/BuildTargetVerification.h"
+#endif
+
+static char const* kIncompatibleScriptsMsg = "The asset bundle '%s' could not be loaded because it references scripts that are not compatible with the currently loaded ones. Rebuild the AssetBundle to fix this error.";
+static char const* kIncompatibleRuntimeClassMsg = "The asset bundle '%s' could not be loaded because it contains run-time classes of incompatible version. Rebuild the AssetBundle to fix this error.";
+static char const* kIncompatibleRuntimeMsg = "The asset bundle '%s' could not be loaded because it is not compatible with this newer version of the Unity runtime. Rebuild the AssetBundle to fix this error.";
+
+static bool TestScriptCompatibility (std::vector<AssetBundleScriptInfo> const& scripts)
+{
+ bool result = true;
+
+ #if ENABLE_SCRIPTING
+ MonoScriptManager& sm = GetMonoScriptManager();
+
+#if 0 // debug
+ {
+ MonoScriptManager::AllScripts existing = sm.GetAllRuntimeScripts ();
+ printf_console ("LOADED SCRIPTS: %d\n", existing.size ());
+ for (MonoScriptManager::AllScripts::iterator it = existing.begin (), end = existing.end (); it != end; ++it)
+ {
+ printf_console (" %s (%s) hash: %08x\n", (*it)->GetName(), (*it)->GetClassName().c_str(), (*it)->GetPropertiesHash());
+ }
+ }
+
+ {
+ printf_console ("TESTING SCRIPTS: %d\n", scripts.size ());
+ for (size_t i=0, size = scripts.size (); i<size; ++i)
+ printf_console (" [%s.]%s hash: %08x\n", scripts[i].nameSpace.c_str(), scripts[i].className.c_str(), scripts[i].hash);
+ }
+#endif
+
+ for (std::vector<AssetBundleScriptInfo>::const_iterator it = scripts.begin (), end = scripts.end (); it != end; ++it)
+ {
+ if (MonoScript* script = sm.FindRuntimeScript (it->className, it->nameSpace, it->assemblyName))
+ {
+ UInt32 supported = script->GetPropertiesHash ();
+ UInt32 loading = it->hash;
+
+ if (supported != loading)
+ {
+ WarningStringMsg ("AssetBundle loading failed because the %s script serialization hash does not match. Supported: %08x, loading: %08x\n", script->GetScriptFullClassName ().c_str(), (unsigned int)supported, (unsigned int)loading);
+ result = false;
+ }
+ }
+ }
+ #endif
+
+ return result;
+}
+
+static bool TestRuntimeClassCompatibility (std::vector<std::pair<int, UInt32> > const& classes)
+{
+ bool result = true;
+ for (size_t i=0, size = classes.size (); i<size; ++i)
+ {
+ int classID = classes[i].first;
+ UInt32 loading = classes[i].second;
+
+ UInt32 supported = GetBuildSettings ().GetHashOfClass (classID);
+ if (supported != 0 && loading != supported)
+ {
+ WarningStringMsg ("AssetBundle loading failed because the %s class serialization hash does not match. Supported: %08x, loading: %08x\n", Object::ClassIDToString(classID).c_str (), (unsigned int)supported, (unsigned int)loading);
+ result = false;
+ }
+ }
+
+ return result;
+}
+
+bool TestAssetBundleCompatibility (AssetBundle& bundle, const std::string& bundleName, std::string& error)
+{
+ error = string();
+
+ // We only report a single type of incompatibility here even when there are multiple
+ // incompatibilities present in the bundle. However, since the solution to each
+ // incompatibility is rebuilding the asset bundle which will also get rid of
+ // whatever other incompatibilities are present in the bundle, we don't need to
+ // worry about that.
+
+ // Test script class compatibility.
+ if (!TestScriptCompatibility (bundle.m_ScriptCompatibility))
+ {
+ error = Format (kIncompatibleScriptsMsg, bundleName.c_str ());
+ return false;
+ }
+
+ // Test runtime class compatibility.
+ if (!TestRuntimeClassCompatibility (bundle.m_ClassCompatibility))
+ {
+ error = Format (kIncompatibleRuntimeClassMsg, bundleName.c_str ());
+ return false;
+ }
+
+#if !UNITY_WEBPLAYER // We do not allow breaking backwards-compatibility in the webplayer at this point.
+
+ bool isEditorTargetingWebPlayer = false;
+ #if UNITY_EDITOR
+ isEditorTargetingWebPlayer =
+ GetEditorUserBuildSettingsPtr ()
+ && IsWebPlayerTargetPlatform (GetEditorUserBuildSettings ().GetActiveBuildTarget ());
+ #endif
+
+ // Check against our runtime compatibility version to see if there's
+ // been some more profound changes to the runtime that prevent old
+ // asset bundles from working.
+ //
+ // In the case of being in the editor and targeting the webplayer, we
+ // suppress the check given that the webplayer will load the bundle
+ // correctly.
+ if (!isEditorTargetingWebPlayer && bundle.m_RuntimeCompatibility < AssetBundle::CURRENT_RUNTIME_COMPATIBILITY_VERSION)
+ {
+ error = Format (kIncompatibleRuntimeMsg, bundleName.c_str ());
+ return false;
+ }
+
+#endif
+
+ return true;
+}
+
+static AssetBundle* FindAssetBundleObject (std::string const& assetBundlePath)
+{
+ PersistentManager& pm = GetPersistentManager();
+
+ // An AssetBundle can be the first (AssetBundle) or the second object (StreamedLevel assets file)
+ const int fileID1 = 1, fileID2 = 2;
+
+ int fileID = 0;
+ if (pm.GetClassIDFromPathAndFileID(assetBundlePath, fileID1) == ClassID(AssetBundle))
+ fileID = fileID1;
+ else if (pm.GetClassIDFromPathAndFileID(assetBundlePath, fileID2) == ClassID(AssetBundle))
+ fileID = fileID2;
+ else
+ {
+ // Old streamed scene asset bundle that has no AssetBundle object.
+ return NULL;
+ }
+
+ int instanceID = pm.GetInstanceIDFromPathAndFileID (assetBundlePath, fileID);
+ return dynamic_instanceID_cast<AssetBundle*> (instanceID);
+}
+
+static AssetBundle* InitializeAssetBundle (const std::string& assetBundleName,
+ const std::string& assetBundlePath,
+ AssetBundle::UncompressedFileInfoContainer* uncompressedFiles,
+ UnityWebStream* webStream,
+ bool performCompatibilityChecks = true)
+{
+ PersistentManager& pm = GetPersistentManager();
+ UNUSED(pm);
+
+ // Locate AssetBundle object.
+ AssetBundle* assetBundle = FindAssetBundleObject (assetBundlePath);
+ if (!assetBundle)
+ {
+ // We used to not include AssetBundle objects in streamed scene
+ // asset bundles that had type trees. This is no longer the case
+ // but to handle this case, we create an AssetBundle object on
+ // demand (like before).
+
+ assetBundle = NEW_OBJECT (AssetBundle);
+ assetBundle->Reset ();
+ assetBundle->AwakeFromLoad (kInstantiateOrCreateFromCodeAwakeFromLoad);
+
+ // Set to incompatible runtime version. Will only load on
+ // webplayer.
+ assetBundle->m_RuntimeCompatibility = 0;
+ }
+
+ if (assetBundle->m_UncompressedFileInfo)
+ UNITY_DELETE (assetBundle->m_UncompressedFileInfo, kMemFile);
+
+ // Associate uncompressed files with asset bundle, if we have them
+ // (will only be the case for local asset bundles).
+ if (uncompressedFiles)
+ {
+ UNITY_TRANSFER_OWNERSHIP (uncompressedFiles, kMemFile, assetBundle);
+ assetBundle->m_UncompressedFileInfo = uncompressedFiles;
+ }
+
+ // Associate webstream with asset bundle, if we have one
+ // (will only be the case for downloaded asset bundles).
+#if ENABLE_WWW
+ if (webStream)
+ {
+ assetBundle->m_UnityWebStream = webStream;
+ assetBundle->m_UnityWebStream->Retain ();
+ webStream->SetCachedAssetBundle (assetBundle->GetInstanceID());
+ }
+#endif
+
+ // Make sure the asset bundle can be loaded into this
+ // build of the player.
+ if (performCompatibilityChecks)
+ {
+ string errorMessage;
+ bool isCompatible = TestAssetBundleCompatibility (*assetBundle, assetBundleName, errorMessage);
+ if (!isCompatible)
+ {
+ ErrorString (errorMessage);
+ UnloadAssetBundle (*assetBundle, true);
+ return NULL;
+ }
+ }
+
+ return assetBundle;
+}
+
+#if ENABLE_WWW
+static bool LooksLikeStreamedSceneBundle (FileStream* data)
+{
+ if (data->m_Files.size () == 0)
+ return false;
+
+ // If first or second file name starts with "BuildPlayer-", it's
+ // probably a streamed scene bundle. In case it has extra resources,
+ // that will be the first file.
+ const bool firstOrSecondFileIsSceneData =
+ data->m_Files[0].name.find ("BuildPlayer-") == 0 ||
+ (data->m_Files.size () > 1 && data->m_Files[1].name.find ("BuildPlayer-") == 0);
+
+ return firstOrSecondFileIsSceneData;
+}
+
+static bool LooksLikeCustomAssetBundle (FileStream* data)
+{
+ if (data->m_Files.size () == 0)
+ return false;
+
+ // If the first file name starts with "CustomAssetBundle" or "CAB",
+ // it's probably a custom asset bundle.
+ const bool isCustomAssetBundle =
+ data->m_Files[0].name.find ("CustomAssetBundle") == 0 ||
+ data->m_Files[0].name.find ("CAB" ) == 0;
+
+ return isCustomAssetBundle;
+}
+
+static bool LooksLikeValidAssetBundle (FileStream* data)
+{
+ return LooksLikeStreamedSceneBundle (data)
+ || LooksLikeCustomAssetBundle( data );
+}
+
+static AssetBundle* ExtractAssetBundle(UnityWebStream* unityweb, const char* url, bool performCompatibilityChecks = true)
+{
+ if (!unityweb || !unityweb->GetFileStream())
+ return NULL;
+
+ FileStream* data = (FileStream*)unityweb->GetFileStream();
+
+ // Return last asset bundle instance if already accessed.
+ if (data->m_Files.size() >= 1 && dynamic_instanceID_cast<AssetBundle*> (unityweb->GetCachedAssetBundle()))
+ return dynamic_instanceID_cast<AssetBundle*> (unityweb->GetCachedAssetBundle());
+
+ ///@TODO: the UnityWebStream should contain information on if the file is an asset bundle instead
+ if (!LooksLikeValidAssetBundle (data))
+ {
+ ErrorString("The unity3d file is not a valid asset bundle.");
+ return NULL;
+ }
+
+ PersistentManager& pm = GetPersistentManager();
+ pm.Lock();
+
+ for (FileStream::iterator i=data->begin();i != data->end();i++)
+ {
+ if (pm.IsStreamLoaded(i->name))
+ {
+ pm.Unlock();
+ ErrorString(Format("The asset bundle '%s' can't be loaded because another asset bundle with the same files are already loaded", url));
+ return NULL;
+ }
+ }
+
+ // Load all memory streams
+ for (FileStream::iterator i=data->begin();i != data->end();i++)
+ {
+ if (!pm.LoadMemoryBlockStream(i->name, i->blocks, i->offset, i->end, url))
+ {
+ pm.Unlock();
+ ErrorString(Format("The asset bundle '%s' can't be loaded because it was not built with the right version or build target.", url));
+ return NULL;
+ }
+ }
+
+ pm.Unlock();
+
+ ////WORKAROUND!
+ //// There was a bug in 4.2 where script compatibility information was *always* written
+ //// out into streamed scene asset bundles which in turn makes us always check these
+ //// hashes on loading and reject bundles if they don't match. As we *always* build
+ //// streamed scene bundles with type trees when targeting the webplayer, this makes us
+ //// not load bundles we can actually load. Work around this issue here by disabling
+ //// compatibility checks altogether when loading a streamed scene bundle into the webplayer.
+ #if UNITY_WEBPLAYER
+ if (LooksLikeStreamedSceneBundle (data))
+ performCompatibilityChecks = false;
+ #endif
+
+ const string &path = data->begin ()->name;
+ return InitializeAssetBundle (url, path, NULL, unityweb, performCompatibilityChecks);
+}
+
+AssetBundle *ExtractAssetBundle(WWW &www)
+{
+ if (!www.HasDownloadedOrMayBlock ())
+ return NULL;
+ www.BlockUntilDone();
+
+ #if ENABLE_CACHING
+ if (www.GetType() == kWWWTypeCached)
+ return static_cast<WWWCached&>(www).GetAssetBundle();
+ #endif
+
+ return ExtractAssetBundle(www.GetUnityWebStream(), www.GetUrl());
+}
+
+AssetBundleCreateRequest::AssetBundleCreateRequest( const UInt8* dataPtr, int dataSize )
+ : m_UnityWebStream(0)
+ , m_AssetBundle(0)
+ , m_EnableCompatibilityChecks (true)
+{
+ UnityWebStreamHeader header;
+
+ int result = ParseStreamHeader (header, dataPtr, dataPtr + dataSize);
+ if (result)
+ {
+ if (result == 1)
+ {
+ ErrorString("Asset bundle is incomplete");
+ }
+ else if(result == 2)
+ {
+ ErrorString("Error parsing asset bundle");
+ }
+
+ m_Progress = 1.f;
+ UnityMemoryBarrier();
+ m_Complete = true;
+ return;
+ }
+
+ m_UnityWebStream = UNITY_NEW_AS_ROOT(UnityWebStream(NULL, 0, 0), kMemFile, "WebStream", "");
+#if SUPPORT_THREADS
+ m_UnityWebStream->SetDecompressionPriority(GetPreloadManager().GetThreadPriority());
+#endif
+ m_UnityWebStream->Retain();
+
+ m_UnityWebStream->FeedDownloadData(dataPtr, dataSize, true);
+
+ GetPreloadManager().AddToQueue(this);
+}
+
+AssetBundleCreateRequest::~AssetBundleCreateRequest()
+{
+ if (m_UnityWebStream != NULL)
+ m_UnityWebStream->Release();
+}
+
+void AssetBundleCreateRequest::Perform ()
+{
+ if (m_UnityWebStream == NULL)
+ return;
+ m_UnityWebStream->WaitForThreadDecompression();
+}
+void AssetBundleCreateRequest::IntegrateMainThread ()
+{
+ m_AssetBundle = ExtractAssetBundle(m_UnityWebStream, "<unknown>", m_EnableCompatibilityChecks);
+ UnityMemoryBarrier();
+ m_Complete = true;
+}
+
+float AssetBundleCreateRequest::GetProgress ()
+{
+ if (m_UnityWebStream == NULL)
+ return 1.f;
+ m_UnityWebStream->UpdateProgress();
+ m_Progress = m_UnityWebStream->GetProgressUntilLoadable();
+ return m_Progress;
+}
+
+#endif
+
+static void ForcePreload (AssetBundle& bundle, const AssetBundle::AssetInfo& info)
+{
+ for (int i=0;i<info.preloadSize;i++)
+ {
+ PPtr<Object> preload = bundle.m_PreloadTable[i + info.preloadIndex];
+ preload.IsValid();
+ }
+}
+
+///@TODO: For 4.0 we should remove this function and make AssetBundle.Load use the LoadAsync code path and wait for completion.
+
+static void ProcessAssetBundleEntries(AssetBundle& bundle, AssetBundle::range entries, ScriptingObjectPtr systemTypeInstance, vector<Object*>& output, bool stopAfterOne)
+{
+#if ENABLE_SCRIPTING
+ ScriptingClassPtr klass = GetScriptingTypeRegistry().GetType(systemTypeInstance);
+
+ for (AssetBundle::iterator i=entries.first;i != entries.second;i++)
+ {
+ Object* obj = i->second.asset;
+ if (obj == NULL)
+ continue;
+
+ ScriptingObjectPtr o = Scripting::ScriptingWrapperFor(obj);
+ if (o && scripting_class_is_subclass_of(scripting_object_get_class(o, GetScriptingTypeRegistry()), klass))
+ {
+ ForcePreload(bundle, i->second);
+ output.push_back(obj);
+ if (stopAfterOne) return;
+ }
+
+ Unity::GameObject* go = dynamic_pptr_cast<GameObject*> (obj);
+ if (go != NULL)
+ {
+ o = ScriptingGetComponentOfType(*go, systemTypeInstance, false);
+ if (o != SCRIPTING_NULL)
+ {
+ ForcePreload(bundle, i->second);
+ output.push_back(ScriptingObjectToObject<Object>(o));
+ if (stopAfterOne) return;
+ }
+ }
+ }
+#endif
+}
+
+Object* LoadNamedObjectFromAssetBundle (AssetBundle& bundle, const std::string& name, ScriptingObjectPtr type)
+{
+
+ string lowerName = ToLower(name);
+ AssetBundle::range found = bundle.GetPathRange(lowerName);
+
+ vector<Object*> result;
+ ProcessAssetBundleEntries(bundle,found,type,result,true);
+ if (result.empty())
+ return NULL;
+
+ return result[0];
+}
+
+Object* LoadMainObjectFromAssetBundle (AssetBundle& bundle)
+{
+ PreloadLevelOperation::PreloadBundleSync (bundle, "");
+ return bundle.m_MainAsset.asset;
+}
+
+void LoadAllFromAssetBundle (AssetBundle& assetBundle, ScriptingObjectPtr type, vector<Object* >& output)
+{
+ AssetBundle::range found = assetBundle.GetAll();
+ ProcessAssetBundleEntries(assetBundle,found,type,output,false);
+}
+
+namespace UnityConsoleStream_Static
+{
+
+ static bool ReadBigEndian (const UInt8*& cur, const UInt8* dataEnd, UInt32& data)
+ {
+ if (cur + sizeof(SInt32) > dataEnd)
+ return false;
+ data = *reinterpret_cast<const SInt32*> (cur);
+#if UNITY_LITTLE_ENDIAN
+ SwapEndianBytes(data);
+#endif
+ cur += sizeof(SInt32);
+ return true;
+ }
+
+ static bool ReadString (const UInt8*& cur, const UInt8* dataEnd, std::string& data)
+ {
+ int length = 0;
+ while (true)
+ {
+ if (cur + length >= dataEnd)
+ return false;
+
+ if (cur[length] == '\0')
+ break;
+
+ length++;
+ }
+
+ data.assign (cur, cur + length);
+ cur += length + 1;
+ return true;
+ }
+
+} // namespace UnityWebStream_Static
+
+
+bool ParseUncompressedFileHeader(AssetBundle::UncompressedFileInfoContainer* files, const UInt8* cur, const UInt8* headerEnd, int offset)
+{
+ using namespace UnityConsoleStream_Static;
+ UInt32 fileCount;
+ if (!ReadBigEndian(cur, headerEnd, fileCount))
+ return false;
+
+ files->reserve (fileCount);
+
+ for (int i = 0; i < fileCount; i++)
+ {
+ AssetBundle::UncompressedFileInfo fileInfo;
+
+ if (!ReadString(cur, headerEnd, fileInfo.fileName)) return false;
+ if (!ReadBigEndian(cur, headerEnd, fileInfo.offset)) return false;
+ else
+ {
+ fileInfo.offset += offset;
+ }
+ if (!ReadBigEndian(cur, headerEnd, fileInfo.size)) return false;
+
+ files->push_back(fileInfo);
+ }
+
+ return true;
+}
+
+#if SUPPORT_SERIALIZATION_FROM_DISK
+AssetBundle* ExtractAssetBundle(std::string const& assetBundlePathName)
+{
+ using namespace UnityConsoleStream_Static;
+ File file;
+ if (!file.Open (assetBundlePathName, File::kReadPermission, File::kSilentReturnOnOpenFail))
+ return NULL;
+
+#define ExitAndCloseFile() {file.Close(); return NULL;}
+
+ const int kBundleHeaderSize = sizeof("UnityRaw") +
+ sizeof(SInt32) +
+ sizeof(UNITY_WEB_BUNDLE_VERSION) +
+ sizeof(UNITY_WEB_MINIMUM_REVISION) +
+ sizeof(SInt32) * 4;
+
+ UInt8 prefix[kBundleHeaderSize];
+ int bytesRead = file.Read(0, prefix, kBundleHeaderSize);
+ if (bytesRead != kBundleHeaderSize)
+ {
+ ErrorString("Error while reading asset bundle header!");
+ ExitAndCloseFile();
+ }
+
+ // read prefix
+ std::string streamId, unityVersion, minimumRevision;
+ UInt32 streamVersion;
+
+ UInt8 const* it = prefix, *end = prefix + kBundleHeaderSize;
+ if (!ReadString(it, end, streamId))
+ ExitAndCloseFile()
+ if (streamId != "UnityRaw")
+ {
+ ErrorStringMsg("This asset bundle was not created with UncompressedAssetBundle flag, expected id 'UnityRaw', got '%s'", streamId.c_str());
+ ExitAndCloseFile();
+ }
+ if (!ReadBigEndian(it, end, streamVersion))
+ ExitAndCloseFile();
+ if (!ReadString(it, end, unityVersion))
+ ExitAndCloseFile();
+ if (!ReadString(it, end, minimumRevision))
+ ExitAndCloseFile();
+
+ UInt32 byteStart, headerSize, numberOfLevelsToDownloadBeforeStreaming, levelCount, completeFileSize, fileInfoHeaderSize;
+
+ if (!ReadBigEndian(it, end, byteStart))
+ ExitAndCloseFile();
+ if (!ReadBigEndian(it, end, headerSize))
+ ExitAndCloseFile();
+ if (!ReadBigEndian(it, end, numberOfLevelsToDownloadBeforeStreaming))
+ ExitAndCloseFile();
+ if (!ReadBigEndian(it, end, levelCount))
+ ExitAndCloseFile();
+
+ UInt8* fullHeader = (UInt8*)alloca (headerSize);
+ file.Read(0, fullHeader, headerSize);
+
+ it = fullHeader + kBundleHeaderSize;
+ end = fullHeader + headerSize;
+
+ UInt32 dummyCompressed, dummyUncompressed;
+ for (UInt32 i = 0; i < levelCount; i++)
+ {
+ if (!ReadBigEndian(it, end, dummyCompressed))
+ ExitAndCloseFile();
+ if (!ReadBigEndian(it, end, dummyUncompressed))
+ ExitAndCloseFile();
+ }
+ if (!ReadBigEndian(it, end, completeFileSize))
+ ExitAndCloseFile();
+ if (!ReadBigEndian(it, end, fileInfoHeaderSize))
+ ExitAndCloseFile();
+
+ void* fileContainerRoot = UNITY_NEW_AS_ROOT(int,kMemFile,"Temp","");
+ AssetBundle::UncompressedFileInfoContainer* files;
+ {
+ SET_ALLOC_OWNER(fileContainerRoot);
+ files = UNITY_NEW(AssetBundle::UncompressedFileInfoContainer, kMemFile);
+ }
+ UInt8* fileInfoHeader = (UInt8*)alloca (fileInfoHeaderSize);
+ file.Read (headerSize, fileInfoHeader, fileInfoHeaderSize);
+ file.Close();
+
+#undef ExitAndCloseFile
+
+ if (ParseUncompressedFileHeader(files, fileInfoHeader, fileInfoHeader + fileInfoHeaderSize, headerSize) == false)
+ {
+ ErrorString("Failed to parsed asset bundle header");
+ UNITY_DELETE(files, kMemFile);
+ return NULL;
+ }
+
+ if (files->size() == 0 || (files->begin()->fileName.find("BuildPlayer-") != 0 && files->begin()->fileName.find("CustomAssetBundle") != 0 && files->begin()->fileName.find("CAB") != 0))
+ {
+ ErrorString("The unity3d file is not a valid asset bundle.");
+ UNITY_DELETE(files, kMemFile);
+ return NULL;
+ }
+
+ PersistentManager& pm = GetPersistentManager();
+ pm.Lock();
+
+ for (AssetBundle::UncompressedFileInfoContainer::iterator i = files->begin(); i != files->end(); ++i)
+ {
+ if (pm.IsStreamLoaded(i->fileName))
+ {
+ pm.Unlock();
+ ErrorString(Format("The asset bundle '%s' can't be loaded because another asset bundle with the same files are already loaded.", assetBundlePathName.c_str()));
+ UNITY_DELETE(files, kMemFile);
+ return NULL;
+ }
+ }
+
+ // Load all memory streams
+ for (AssetBundle::UncompressedFileInfoContainer::iterator i=files->begin(); i != files->end(); ++i)
+ {
+ // TODO: check flags
+ if (!pm.LoadExternalStream(i->fileName, assetBundlePathName, kSerializeGameRelease, i->offset))
+ {
+ pm.Unlock();
+ ErrorString(Format("The asset bundle '%s' can't be loaded because it was not built with the right version or build target.", assetBundlePathName.c_str()));
+ UNITY_DELETE(files, kMemFile);
+ return NULL;
+ }
+ }
+
+ pm.Unlock();
+
+ const string &path = files->begin ()->fileName;
+ return InitializeAssetBundle (assetBundlePathName, path, files, NULL);
+}
+#else
+AssetBundle* ExtractAssetBundle(std::string const& assetBundlePathName)
+{
+ ErrorString("Failed to load asset bundle (not supported).");
+ return NULL;
+}
+#endif
diff --git a/Runtime/Misc/AssetBundleUtility.h b/Runtime/Misc/AssetBundleUtility.h
new file mode 100644
index 0000000..593db95
--- /dev/null
+++ b/Runtime/Misc/AssetBundleUtility.h
@@ -0,0 +1,76 @@
+#ifndef ASSETBUNDLEUTILITY_H
+#define ASSETBUNDLEUTILITY_H
+
+#include "Runtime/Misc/PreloadManager.h"
+#include "Runtime/Misc/AssetBundle.h"
+
+#if ENABLE_WWW
+class WWW;
+
+AssetBundle* ExtractAssetBundle(WWW &www);
+
+
+class AssetBundleCreateRequest : public PreloadManagerOperation
+{
+public:
+ AssetBundleCreateRequest( const UInt8* dataPtr, int dataSize );
+ virtual ~AssetBundleCreateRequest();
+
+ virtual void Perform ();
+ virtual bool HasIntegrateMainThread () { return true; }
+ virtual void IntegrateMainThread ();
+
+ virtual float GetProgress ();
+
+#if ENABLE_PROFILER
+ virtual std::string GetDebugName ()
+ {
+ static std::string debugName("AssetBundleCreateRequest");
+ return debugName;
+ }
+#endif
+
+ AssetBundle* GetAssetBundle()
+ {
+ return m_AssetBundle;
+ }
+
+ /// Set whether to perform runtime compatibility checks or not. Unfortunately, in the
+ /// editor we get lots of old asset bundles handed to us from the asset store to deliver
+ /// previews. Given that in the editor we can actually still run that content (unlike in
+ /// players), we allow disabling checks for those bundles.
+ ///
+ /// This is an internal feature only.
+ void SetEnableCompatibilityChecks (bool value)
+ {
+ m_EnableCompatibilityChecks = value;
+ }
+
+protected:
+ UnityWebStream* m_UnityWebStream;
+ PPtr<AssetBundle> m_AssetBundle;
+ bool m_EnableCompatibilityChecks;
+};
+#endif
+
+AssetBundle* ExtractAssetBundle(std::string const& assetBundlePathName);
+
+/// Return true if the given asset bundle can be used with the current player.
+/// If not, "error" will be set to a description of why the bundle cannot be used.
+/// The given "bundleName" is only used for printing more informative error
+/// messages.
+bool TestAssetBundleCompatibility (AssetBundle& bundle, const std::string& bundleName, std::string& error);
+
+Object* LoadNamedObjectFromAssetBundle (AssetBundle& bundle, const std::string& name, ScriptingObjectPtr type);
+Object* LoadMainObjectFromAssetBundle (AssetBundle& bundle);
+void LoadAllFromAssetBundle (AssetBundle& assetBundle, ScriptingObjectPtr type, std::vector<Object* >& output);
+
+struct AssetBundleRequestMono
+{
+ AsyncOperation* m_Result;
+ ScriptingObjectPtr m_AssetBundle;
+ ScriptingStringPtr m_Path;
+ ScriptingObjectPtr m_Type;
+};
+
+#endif // ASSETBUNDLEUTILITY_H
diff --git a/Runtime/Misc/AsyncOperation.cpp b/Runtime/Misc/AsyncOperation.cpp
new file mode 100644
index 0000000..5b05c0d
--- /dev/null
+++ b/Runtime/Misc/AsyncOperation.cpp
@@ -0,0 +1,47 @@
+#include "UnityPrefix.h"
+#include "AsyncOperation.h"
+
+void AsyncOperation::SetCoroutineCallback ( DelayedCall* func, Object* coroutineBehaviour, void* userData, CleanupUserData* cleanup)
+{
+ m_CoroutineBehaviour = coroutineBehaviour;
+ m_CoroutineDone = func;
+ m_CoroutineCleanup = cleanup;
+ m_CoroutineData = userData;
+}
+
+void AsyncOperation::InvokeCoroutine ()
+{
+ if (m_CoroutineDone != NULL)
+ {
+ Object* target = m_CoroutineBehaviour;
+ if (target)
+ m_CoroutineDone(target, m_CoroutineData);
+ m_CoroutineCleanup(m_CoroutineData);
+ m_CoroutineDone = NULL;
+ }
+}
+
+void AsyncOperation::CleanupCoroutine ()
+{
+ if (m_CoroutineDone != NULL)
+ {
+ m_CoroutineCleanup(m_CoroutineData);
+ m_CoroutineDone = NULL;
+ }
+}
+
+AsyncOperation::~AsyncOperation ()
+{
+ AssertIf(m_CoroutineDone != NULL);
+}
+
+void AsyncOperation::Retain ()
+{
+ m_RefCount.Retain();
+}
+
+void AsyncOperation::Release ()
+{
+ if (m_RefCount.Release())
+ delete this;
+}
diff --git a/Runtime/Misc/AsyncOperation.h b/Runtime/Misc/AsyncOperation.h
new file mode 100644
index 0000000..2ccb3e0
--- /dev/null
+++ b/Runtime/Misc/AsyncOperation.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Threads/AtomicRefCounter.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+
+class AsyncOperation
+{
+ typedef void DelayedCall(Object* o, void* userData);
+ typedef void CleanupUserData (void* userData);
+
+ AtomicRefCounter m_RefCount;
+ DelayedCall* m_CoroutineDone;
+ CleanupUserData* m_CoroutineCleanup;
+ void* m_CoroutineData;
+ PPtr<Object> m_CoroutineBehaviour;
+public:
+
+ AsyncOperation () { m_CoroutineDone = NULL; }
+ virtual ~AsyncOperation ();
+ virtual bool IsDone () = 0;
+ virtual float GetProgress () = 0;
+
+ virtual int GetPriority () { return 0; }
+ virtual void SetPriority (int priority) { }
+
+ virtual bool GetAllowSceneActivation () { return true; }
+ virtual void SetAllowSceneActivation (bool allow) { }
+
+ void Retain ();
+ void Release ();
+
+ bool HasCoroutineCallback () { return m_CoroutineDone != NULL; }
+ void SetCoroutineCallback ( DelayedCall* func, Object* coroutineBehaviour, void* userData, CleanupUserData* cleanup);
+
+ void InvokeCoroutine ();
+ void CleanupCoroutine ();
+};
diff --git a/Runtime/Misc/BatchDeleteObjects.cpp b/Runtime/Misc/BatchDeleteObjects.cpp
new file mode 100644
index 0000000..07ab974
--- /dev/null
+++ b/Runtime/Misc/BatchDeleteObjects.cpp
@@ -0,0 +1,221 @@
+#include "UnityPrefix.h"
+#include "BatchDeleteObjects.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Mono/MonoIncludes.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Threads/ThreadedStreamBuffer.h"
+
+PROFILER_INFORMATION(gBatchDeleteObjects, "BatchDeleteObjects", kProfilerLoading)
+PROFILER_INFORMATION(gBatchDeleteObjectsThread, "BatchDeleteObjectsThread", kProfilerLoading)
+
+// @TODO: On Angrybots on OS X. We spent 4ms deleting objects in singlethreaded mode on the main thread and 1ms in multithreaded mode on the main thread.
+#define MULTI_THREADED_DELETE 0
+
+// Some classes do not support being deallocated on the main thread.
+bool DoesClassRequireMainThreadDeallocation (int classID)
+{
+ if (classID == ClassID(Shader) || classID == ClassID(PhysicMaterial) || classID == ClassID(Mesh) ||
+ classID == ClassID(Texture) || classID == ClassID(Texture2D) || classID == ClassID(Texture3D) || classID == ClassID(Cubemap) )
+ return true;
+ else
+ return false;
+}
+
+#if MULTI_THREADED_DELETE
+
+#define kTerminateInstruction reinterpret_cast<Object*> (0x1)
+
+struct BatchDeleteManager;
+static BatchDeleteManager* gBatchDeleteManager = NULL;
+
+static void* BatchDeleteStep2Threaded (void* userData);
+
+struct BatchDeleteManager
+{
+ Thread thread;
+ ThreadedStreamBuffer streamBuffer;
+
+ ///////@TODO: Handle when we are out of ring buffer memory.
+
+ BatchDeleteManager ()
+ : streamBuffer (ThreadedStreamBuffer::kModeThreaded, 1024 * 256 * sizeof(Object*) )
+ {
+
+ }
+};
+
+void InitializeBatchDelete ()
+{
+ gBatchDeleteManager = UNITY_NEW(BatchDeleteManager, kMemGarbageCollector);
+ gBatchDeleteManager->thread.SetName ("BatchDeleteObjects");
+ gBatchDeleteManager->thread.Run(BatchDeleteStep2Threaded, mono_domain_get());
+}
+
+void CleanupBatchDelete ()
+{
+ // Send terminate instruction & wait for thread to complete
+ gBatchDeleteManager->streamBuffer.WriteValueType<Object*> (kTerminateInstruction);
+ gBatchDeleteManager->streamBuffer.WriteSubmitData ();
+
+ gBatchDeleteManager->thread.WaitForExit();
+
+ UNITY_DELETE(gBatchDeleteManager, kMemGarbageCollector);
+}
+
+BatchDelete CreateBatchDelete (size_t size)
+{
+ size_t allocationSize = sizeof(Object*)*size;
+ void* objectArray = gBatchDeleteManager->streamBuffer.GetWriteDataPointer (allocationSize, sizeof(Object*));
+
+ BatchDelete batchInfo;
+ batchInfo.reservedObjectCount = size;
+ batchInfo.objectCount = 0;
+ batchInfo.objects = reinterpret_cast<Object**> (objectArray);
+
+ return batchInfo;
+}
+
+static void* BatchDeleteStep2Threaded (void* userData)
+{
+ // Attach mono thread
+ MonoThread* thread = mono_thread_attach((MonoDomain*)userData);
+
+ ThreadedStreamBuffer& threadedStreamBuffer = gBatchDeleteManager->streamBuffer;
+
+ while (true)
+ {
+ Object* object = threadedStreamBuffer.ReadValueType<Object*> ();
+
+ // Terminate instruction. Stop the thread.
+ if (object == kTerminateInstruction)
+ return NULL;
+
+ // Delete the object
+ if (object != NULL)
+ delete_object_internal_step2 (object);
+
+ threadedStreamBuffer.ReadReleaseData ();
+ }
+
+ // Detach mono thread
+ mono_thread_detach(thread);
+
+ return NULL;
+}
+
+void SharkBeginRemoteProfiling ();
+void SharkEndRemoteProfiling ();
+
+/// Callbacks like ScriptableObject.OnDestroy etc must be called before invoking this function.
+void BatchDeleteObjectInternal (const SInt32* unloadObjects, int size)
+{
+ if (size == 0)
+ return;
+
+ PROFILER_AUTO(gBatchDeleteObjects, NULL)
+
+ BatchDelete batch = CreateBatchDelete (size);
+ int destroyObjectIndex = 0;
+ for (int i=0;i<size;i++)
+ {
+ Object* object = Object::IDToPointer(unloadObjects[i]);
+ batch.objects[destroyObjectIndex] = object;
+ }
+ batch.objectCount = destroyObjectIndex;
+
+ CommitBatchDelete(batch);
+}
+
+void CommitBatchDelete (BatchDelete& batchDelete)
+{
+ Assert(batchDelete.reservedObjectCount >= batchDelete.objectCount);
+
+ LockObjectCreation();
+
+ for (int i=0;i<batchDelete.objectCount;i++)
+ {
+ Object* object = batchDelete.objects[i];
+
+ if (object == NULL)
+ continue;
+
+ delete_object_internal_step1 (object);
+
+ int classID = object->GetClassID();
+ if (DoesClassRequireMainThreadDeallocation (classID))
+ {
+ bool requiresThreadCleanup = object->MainThreadCleanup ();
+ /// DoesClassRequireMainThreadDeallocation does not agree with MainThreadCleanup
+ /// Fix DoesClassRequireMainThreadDeallocation or MainThreadCleanup for that class.
+ Assert(requiresThreadCleanup);
+ }
+ }
+
+ for (int i=batchDelete.objectCount;i<batchDelete.reservedObjectCount;i++)
+ batchDelete.objects[i] = NULL;
+
+ UnlockObjectCreation();
+
+ gBatchDeleteManager->streamBuffer.WriteSubmitData ();
+}
+
+
+#else
+
+BatchDelete CreateBatchDelete (size_t size)
+{
+ BatchDelete batch;
+
+ batch.objects = (Object**)UNITY_MALLOC(kMemGarbageCollector, sizeof(Object*)*size);
+ batch.reservedObjectCount = size;
+ batch.objectCount = 0;
+
+ return batch;
+}
+
+void CommitBatchDelete (BatchDelete& batchDelete)
+{
+ PROFILER_AUTO(gBatchDeleteObjectsThread, NULL)
+
+ Assert(batchDelete.reservedObjectCount >= batchDelete.objectCount);
+
+ LockObjectCreation();
+
+ for (int i=0;i<batchDelete.objectCount;i++)
+ {
+ Object* object = batchDelete.objects[i];
+ if (object != NULL)
+ {
+ delete_object_internal_step1 (object);
+ delete_object_internal_step2 (object);
+ }
+ }
+
+ UnlockObjectCreation();
+
+ // Cleanup temp storage
+ UNITY_FREE(kMemGarbageCollector, batchDelete.objects);
+}
+
+void BatchDeleteObjectInternal (const SInt32* unloadObjects, int size)
+{
+ PROFILER_AUTO(gBatchDeleteObjectsThread, NULL)
+
+ BatchDelete batchInfo = CreateBatchDelete (size);
+ batchInfo.objectCount = size;
+ for (int i=0;i<size;i++)
+ batchInfo.objects[i] = Object::IDToPointer(unloadObjects[i]);
+
+ CommitBatchDelete (batchInfo);
+}
+
+void InitializeBatchDelete ()
+{
+}
+
+void CleanupBatchDelete ()
+{
+}
+
+#endif
diff --git a/Runtime/Misc/BatchDeleteObjects.h b/Runtime/Misc/BatchDeleteObjects.h
new file mode 100644
index 0000000..096e9a1
--- /dev/null
+++ b/Runtime/Misc/BatchDeleteObjects.h
@@ -0,0 +1,31 @@
+#pragma once
+
+class Object;
+
+struct BatchDelete
+{
+ size_t reservedObjectCount;
+ size_t objectCount;
+ Object** objects;
+};
+
+// Creates a batch delete object. When the object array has been filled (All Object* must be set. The function does not set them to null)
+// You can call CommitBatchDelete which will make the deletion thread delete the objects.
+BatchDelete CreateBatchDelete (size_t size);
+
+// Makes the batch delete
+void CommitBatchDelete (BatchDelete& batchDelete);
+
+
+/// Deletes an array of objects identified by instanceID. Deallocation is done on another thread.
+/// Callbacks like ScriptableObject.OnDestroy etc must be called before invoking this function.
+void BatchDeleteObjectInternal (const SInt32* unloadObjects, int size);
+
+
+// Used by the batch deletion to figure out if a specific class must be deallocated on the main thread. Eg. Textures need to be deallocate on the main thread
+// Object::MainThreadCleanup will be called if this per class check returns true.
+bool DoesClassRequireMainThreadDeallocation (int classID);
+
+
+void InitializeBatchDelete ();
+void CleanupBatchDelete (); \ No newline at end of file
diff --git a/Runtime/Misc/BuildSettings.cpp b/Runtime/Misc/BuildSettings.cpp
new file mode 100644
index 0000000..2c4ad74
--- /dev/null
+++ b/Runtime/Misc/BuildSettings.cpp
@@ -0,0 +1,188 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigureVersion.h"
+#include "BuildSettings.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Utilities/ErrorExit.h"
+
+int kUnityVersion3_0_0a1 = GetNumericVersion("3.0.0.a1");
+int kUnityVersion3_2_a1 = GetNumericVersion("3.2.0a1");
+int kUnityVersion3_3_a1 = GetNumericVersion("3.3.0a1");
+int kUnityVersion3_4_a1 = GetNumericVersion("3.4.0a1");
+int kUnityVersion3_5_a1 = GetNumericVersion("3.5.0a1");
+int kUnityVersion3_5_3_a1 = GetNumericVersion("3.5.3a1");
+int kUnityVersion4_0_a1 = GetNumericVersion("4.0.0a1");
+int kUnityVersion4_1_a1 = GetNumericVersion("4.1.0a1");
+int kUnityVersion4_1_1_a1 = GetNumericVersion("4.1.1a1");
+int kUnityVersion4_1_a3 = GetNumericVersion("4.1.0a3");
+int kUnityVersion4_2_a1 = GetNumericVersion("4.2.0a1");
+int kUnityVersion4_3_a1 = GetNumericVersion("4.3.0a1");
+int kUnityVersion_OldWebResourcesAdded = GetNumericVersion("4.2.0a1");
+
+BuildSettings::BuildSettings (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ hasRenderTexture = true;
+ hasPROVersion = true;
+ hasAdvancedVersion = true;
+ hasShadows = true;
+ hasSoftShadows = true;
+ hasLocalLightShadows = true;
+ isNoWatermarkBuild = false;
+ isPrototypingBuild = false;
+ isEducationalBuild = false;
+ isEmbedded = false;
+ hasPublishingRights = true;
+ isDebugBuild = true;
+ usesOnMouseEvents = true;
+ enableDynamicBatching = true;
+ #if UNITY_EDITOR
+ m_Version = UNITY_VERSION;
+ #else
+ m_Version = "1.6.0";
+ #endif
+ m_IntVersion = GetNumericVersion(m_Version);
+}
+
+BuildSettings::~BuildSettings ()
+{
+}
+
+string BuildSettings::GetLevelPathName (int index)
+{
+ if (index < remappedLevels.size () && index >= 0)
+ return remappedLevels[index];
+ else
+ return string ();
+}
+
+string BuildSettings::GetLevelPathName (const string& name)
+{
+ return GetLevelPathName (GetLevelIndex (name));
+}
+
+string BuildSettings::GetLevelName (int index)
+{
+ if (index < levels.size () && index >= 0)
+ return DeletePathNameExtension (GetLastPathNameComponent(levels[index]));
+ else
+ return string ();
+}
+
+int BuildSettings::GetLevelIndex (const string& name)
+{
+ for (int i=0;i<levels.size ();i++)
+ {
+ string curName = levels[i];
+ curName = DeletePathNameExtension (GetLastPathNameComponent(curName));
+ if (StrICmp (name, curName) == 0)
+ return i;
+ }
+ return -1;
+}
+
+int BuildSettings::GetLevelIndexChecked (const string& name)
+{
+ int index = GetLevelIndex (name);
+ if (index == -1)
+ {
+ ErrorString (Format ("Level %s couldn't be found because it has not been added to the build settings.\nTo add a level to the build settings use the menu File->Build Settings...", name.c_str()));
+ }
+ return index;
+}
+
+UInt32 BuildSettings::GetHashOfClass (int classID) const
+{
+ ClassHashCont::const_iterator it = runtimeClassHashes.find (classID);
+ if (it == runtimeClassHashes.end ())
+ return 0;
+
+ return it->second;
+}
+
+template<class TransferFunction>
+void BuildSettings::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion (2);
+ TRANSFER (levels);
+ TRANSFER (hasRenderTexture);
+ TRANSFER (hasPROVersion);
+ TRANSFER (isNoWatermarkBuild);
+ TRANSFER (isPrototypingBuild);
+ TRANSFER (isEducationalBuild);
+ TRANSFER (isEmbedded);
+ TRANSFER (hasPublishingRights);
+ TRANSFER (hasShadows);
+ TRANSFER (hasSoftShadows);
+ TRANSFER (hasLocalLightShadows);
+ TRANSFER (hasAdvancedVersion);
+ TRANSFER (enableDynamicBatching);
+ TRANSFER (isDebugBuild);
+ TRANSFER (usesOnMouseEvents);
+ transfer.Align();
+
+ if (transfer.IsOldVersion(1))
+ {
+ hasPROVersion = hasRenderTexture;
+ }
+
+ // Only in game build mode we transfer the version,
+ // in the editor it is always the latest one
+ if (transfer.IsSerializingForGameRelease())
+ {
+ TRANSFER (m_Version);
+ TRANSFER (m_AuthToken);
+
+ if(transfer.IsReading()) {
+ if(GetNumericVersion(m_Version) < kUnityVersion3_0_0a1) {
+ // We're attempting to load a version of unity that we don't have
+ // backwards compatibility for.
+ ExitWithErrorCode(kErrorIncompatibleRuntimeVersion);
+ }
+ }
+ }
+
+ // We need hashed type trees only for the player
+ if (transfer.IsSerializingForGameRelease())
+ {
+ TRANSFER (runtimeClassHashes);
+ }
+}
+
+void BuildSettings::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ remappedLevels = levels;
+ #if GAMERELEASE
+ if (remappedLevels.empty ())
+ {
+ remappedLevels.push_back ("mainData");
+ levels.push_back ("");
+ }
+ else
+ {
+ remappedLevels[0] = "mainData";
+ for (int i=1;i<remappedLevels.size ();i++)
+ remappedLevels[i] = Format ("level%d", i - 1);
+ }
+ #endif
+
+#if UNITY_WINRT
+ // On Metro you switch between debug/release in VS solution,
+ // so we need isDebugBuild to dependant on the player, not on the settings
+# if UNITY_DEVELOPER_BUILD
+ isDebugBuild = true;
+# else
+ isDebugBuild = false;
+# endif
+#endif
+ m_IntVersion = GetNumericVersion(m_Version);
+}
+
+GET_MANAGER(BuildSettings)
+GET_MANAGER_PTR(BuildSettings)
+IMPLEMENT_CLASS (BuildSettings)
+IMPLEMENT_OBJECT_SERIALIZE (BuildSettings)
diff --git a/Runtime/Misc/BuildSettings.h b/Runtime/Misc/BuildSettings.h
new file mode 100644
index 0000000..ed8f116
--- /dev/null
+++ b/Runtime/Misc/BuildSettings.h
@@ -0,0 +1,80 @@
+#pragma once
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Utilities/vector_map.h"
+
+class BuildSettings : public GlobalGameManager
+{
+ public:
+
+ REGISTER_DERIVED_CLASS (BuildSettings, GlobalGameManager)
+ DECLARE_OBJECT_SERIALIZE (BuildSettings)
+
+ BuildSettings (MemLabelId label, ObjectCreationMode mode);
+
+ std::vector<UnityStr> levels;
+ std::vector<UnityStr> remappedLevels;
+ typedef vector_map<int, UInt32> ClassHashCont;
+ ClassHashCont runtimeClassHashes;
+
+ bool hasRenderTexture;
+ bool hasPROVersion;
+ bool hasAdvancedVersion; // PRO version for the active platform.
+ bool enableDynamicBatching;
+
+ bool isNoWatermarkBuild;
+ bool isPrototypingBuild;
+ bool isEducationalBuild;
+ bool isEmbedded;
+ bool hasPublishingRights;
+ bool hasShadows;
+ bool hasSoftShadows;
+ bool hasLocalLightShadows;
+ bool isDebugBuild;
+ bool usesOnMouseEvents;
+
+ UnityStr m_AuthToken;
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ std::string GetLevelPathName (int index);
+ std::string GetLevelPathName (const std::string& name);
+ string GetLevelName (int index);
+
+ int GetLevelIndex (const std::string& name);
+ int GetLevelIndexChecked (const string& name);
+
+ int GetIntVersion () { return m_IntVersion; }
+ const UnityStr& GetVersion () { return m_Version; }
+
+ UInt32 GetHashOfClass (int classID) const;
+
+ private:
+
+ int m_IntVersion;
+ UnityStr m_Version;
+};
+
+BuildSettings& GetBuildSettings();
+BuildSettings* GetBuildSettingsPtr();
+
+extern int kUnityVersion3_2_a1;
+extern int kUnityVersion3_3_a1;
+extern int kUnityVersion3_4_a1;
+extern int kUnityVersion3_5_a1;
+extern int kUnityVersion3_5_3_a1;
+extern int kUnityVersion4_0_a1;
+extern int kUnityVersion4_1_a1;
+extern int kUnityVersion4_1_a3;
+extern int kUnityVersion4_1_1_a1;
+extern int kUnityVersion4_2_a1;
+extern int kUnityVersion4_3_a1;
+extern int kUnityVersion_OldWebResourcesAdded;
+
+// The NaCl runtime will always be included with the player files,
+// so in practice the version will always match.
+#if !WEBPLUG || (UNITY_NACL && !UNITY_NACL_WEBPLAYER)
+#define IS_CONTENT_NEWER_OR_SAME(val) true
+#else
+#define IS_CONTENT_NEWER_OR_SAME(val) (GetBuildSettingsPtr() && GetBuildSettings().GetIntVersion() >= val)
+#endif
diff --git a/Runtime/Misc/CPUInfo.cpp b/Runtime/Misc/CPUInfo.cpp
new file mode 100644
index 0000000..0d67baf
--- /dev/null
+++ b/Runtime/Misc/CPUInfo.cpp
@@ -0,0 +1,120 @@
+#include "UnityPrefix.h"
+#include "CPUInfo.h"
+#if UNITY_ANDROID
+ #include <cpu-features.h>
+#endif
+
+bool CPUInfo::m_Initialized = false;
+bool CPUInfo::m_IsSSESupported = false;
+bool CPUInfo::m_IsSSE2Supported = false;
+bool CPUInfo::m_IsSSE3Supported = false;
+bool CPUInfo::m_IsSupplementalSSE3Supported = false;
+bool CPUInfo::m_IsSSE41Supported = false;
+bool CPUInfo::m_IsSSE42Supported = false;
+bool CPUInfo::m_IsAVXSupported = false;
+bool CPUInfo::m_IsAVX2Supported = false;
+bool CPUInfo::m_IsAVX512Supported = false;
+bool CPUInfo::m_IsFP16CSupported = false;
+bool CPUInfo::m_IsFMASupported = false;
+bool CPUInfo::m_IsNEONSupported = false;
+
+unsigned int CPUInfo::m_CPUIDFeatures = 0;
+
+
+#if UNITY_SUPPORTS_SSE
+
+static inline UInt64 xgetbv_impl()
+{
+# if defined(__GNUC__) || defined(__clang__)
+ UInt32 eax, edx;
+
+ __asm __volatile (
+ ".byte 0x0f, 0x01, 0xd0" // xgetbv instruction isn't supported by some older assemblers, so just emit it raw
+ : "=a"(eax), "=d"(edx) : "c"(0)
+ );
+
+ return ((UInt64)edx << 32) | eax;
+# elif defined(_MSC_VER) && !UNITY_XENON
+ return _xgetbv(_XCR_XFEATURE_ENABLED_MASK);
+# else
+ return 0;
+# endif
+}
+
+static void cpuid_ex_impl(UInt32 eax, UInt32 ecx, UInt32* abcd)
+{
+ #if defined(_MSC_VER)
+
+ __cpuidex((int*)abcd, eax, ecx);
+
+ #else
+ UInt32 ebx, edx;
+ #if defined(__i386__) && defined (__PIC__)
+ // for PIC under 32 bit: EBX can't be modified
+ __asm__ ("movl %%ebx, %%edi \n\t cpuid \n\t xchgl %%ebx, %%edi" : "=D" (ebx), "+a" (eax), "+c" (ecx), "=d" (edx));
+ #else
+ __asm__ ("cpuid" : "+b" (ebx), "+a" (eax), "+c" (ecx), "=d" (edx));
+ #endif
+ abcd[0] = eax; abcd[1] = ebx; abcd[2] = ecx; abcd[3] = edx;
+ #endif
+}
+
+#endif // UNITY_SUPPORTS_SSE
+
+
+CPUInfo::CPUInfo()
+{
+#if UNITY_SUPPORTS_SSE
+ int data[4] = {0};
+
+ // Add more code here to extract vendor string or what ever is needed
+ __cpuid (data, 0);
+ unsigned int cpuData0 = data[0];
+ unsigned int cpuInfo2 = 0;
+ if (cpuData0 >= 1) {
+ __cpuid(data, 1);
+ cpuInfo2 = data[2];
+ m_CPUIDFeatures = data[3];
+ }
+
+ // SSE/2 support
+ m_IsSSESupported = (m_CPUIDFeatures & CPUID_FEATURES_SSE) != 0;
+ m_IsSSE2Supported = (m_CPUIDFeatures & CPUID_FEATURES_SSE2) != 0;
+
+ // SSE 3.x
+ m_IsSSE3Supported = ((cpuInfo2 & (1<<0)) != 0);
+ m_IsSupplementalSSE3Supported = ((cpuInfo2 & (1<<9)) != 0);
+
+ // SSE 4.x support
+ m_IsSSE41Supported = ((cpuInfo2 & (1<<19)) != 0);
+ m_IsSSE42Supported = ((cpuInfo2 & (1<<20)) != 0);
+
+ // AVX support
+ m_IsAVXSupported =
+ ((cpuInfo2 & (1<<28)) != 0) && // AVX support in CPU
+ ((cpuInfo2 & (1<<27)) != 0) && // OS support for AVX (XSAVE/XRESTORE on context switches)
+ ((xgetbv_impl() & 6) == 6); // XMM & YMM registers will be preserved on context switches
+
+ if (m_IsAVXSupported)
+ {
+ if (cpuData0 >= 7)
+ {
+ UInt32 regs7[4] = {0};
+ cpuid_ex_impl(0x7, 0, regs7);
+ m_IsAVX2Supported = ((regs7[1] & (1<<5)) != 0);
+ m_IsAVX512Supported = ((regs7[1] & (1<<16)) != 0);
+ }
+ }
+
+ m_IsFP16CSupported = ((cpuInfo2 & (1<<29)) != 0);
+ m_IsFMASupported = ((cpuInfo2 & (1<<12)) != 0);
+
+#elif UNITY_ANDROID && UNITY_SUPPORTS_NEON
+ m_CPUIDFeatures = android_getCpuFeatures();
+ m_IsNEONSupported = (m_CPUIDFeatures & ANDROID_CPU_ARM_FEATURE_NEON) != 0;
+#endif
+
+ m_Initialized = true;
+}
+
+CPUInfo g_cpuInfo;
diff --git a/Runtime/Misc/CPUInfo.h b/Runtime/Misc/CPUInfo.h
new file mode 100644
index 0000000..840465a
--- /dev/null
+++ b/Runtime/Misc/CPUInfo.h
@@ -0,0 +1,117 @@
+#ifndef CPUINFO_H
+#define CPUINFO_H
+
+#define CPUID_FEATURES_SSE (1 << 25)
+#define CPUID_FEATURES_SSE2 (1 << 26)
+
+#if defined(_MSC_VER) && !UNITY_XENON
+
+// define __cpuid intrinsic
+#include <intrin.h>
+
+#elif defined(__GNUC__) && UNITY_OSX && UNITY_SUPPORTS_SSE
+
+// This has to be implemented in a PIC friendly way or osx web players will not compile
+// http://sam.zoy.org/blog/2007-04-13-shlib-with-non-pic-code-have-inlin
+
+#define __cpuid(array, func) \
+{ \
+ __asm__ __volatile__("pushl %%ebx \n\t" /* save %ebx */ \
+ "cpuid \n\t" \
+ "movl %%ebx, %1 \n\t" /* save what cpuid just put in %ebx */ \
+ "popl %%ebx \n\t" /* restore the old %ebx */ \
+ : "=a"(array[0]), "=r"(array[1]), "=c"(array[2]), "=d"(array[3]) \
+ : "a"(func) \
+ : "cc");\
+}
+
+#else
+#define __cpuid(a, b)
+#endif
+
+
+class CPUInfo
+{
+public:
+ CPUInfo();
+
+ inline unsigned int GetCPUIDFeatures() {return m_CPUIDFeatures;}
+
+#ifdef UNITY_SUPPORTS_SSE
+# if UNITY_AUTO_DETECT_VECTOR_UNIT
+ static __forceinline bool HasSSESupport()
+ {
+ AssertMsg(m_Initialized, "CPUInfo has not been initialized");
+ return m_IsSSESupported;
+ }
+
+ static __forceinline bool HasSSE2Support()
+ {
+ AssertMsg(m_Initialized, "CPUInfo has not been initialized");
+ return m_IsSSE2Supported;
+ }
+
+# else
+ static inline bool HasSSESupport()
+ {
+ return true;
+ }
+
+ static inline bool HasSSE2Support()
+ {
+ return true;
+ }
+# endif
+#endif
+
+ static inline bool HasSSE3Support() { return m_IsSSE3Supported; }
+ static inline bool HasSupplementalSSE3Support() { return m_IsSupplementalSSE3Supported; }
+ static inline bool HasSSE41Support() { return m_IsSSE41Supported; }
+ static inline bool HasSSE42Support() { return m_IsSSE42Supported; }
+ static inline bool HasAVXSupport() { return m_IsAVXSupported; }
+ static inline bool HasAVX2Support() { return m_IsAVX2Supported; }
+ static inline bool HasAVX512Support() { return m_IsAVX512Supported; }
+ static inline bool HasFP16CSupport() { return m_IsFP16CSupported; }
+ static inline bool HasFMASupport() { return m_IsFMASupported; }
+
+#if UNITY_SUPPORTS_NEON
+ #if UNITY_ANDROID
+ static inline bool HasNEONSupport()
+ {
+ AssertMsg(m_Initialized, "CPUInfo has not been initialized");
+ return m_IsNEONSupported;
+ }
+ #else
+ static inline bool HasNEONSupport()
+ {
+ return true;
+ }
+ #endif
+#else
+ static inline bool HasNEONSupport()
+ {
+ return false;
+ }
+#endif
+
+private:
+ static bool m_Initialized;
+
+ static bool m_IsSSESupported;
+ static bool m_IsSSE2Supported;
+ static bool m_IsSSE3Supported;
+ static bool m_IsSupplementalSSE3Supported;
+ static bool m_IsSSE41Supported; // SSE 4.1
+ static bool m_IsSSE42Supported; // SSE 4.2
+ static bool m_IsAVXSupported;
+ static bool m_IsAVX2Supported;
+ static bool m_IsAVX512Supported;
+ static bool m_IsFP16CSupported; // FP16 conversions supported
+ static bool m_IsFMASupported; // fused multiply-add instructions
+
+ static bool m_IsNEONSupported;
+
+ static unsigned int m_CPUIDFeatures;
+
+};
+#endif // CPUINFO_H
diff --git a/Runtime/Misc/CachingManager.cpp b/Runtime/Misc/CachingManager.cpp
new file mode 100644
index 0000000..9945942
--- /dev/null
+++ b/Runtime/Misc/CachingManager.cpp
@@ -0,0 +1,1207 @@
+#include "UnityPrefix.h"
+#include "CachingManager.h"
+
+#if ENABLE_CACHING
+
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "PlatformDependent/CommonWebPlugin/Verification.h"
+#include "PlayerSettings.h"
+#include "Runtime/Utilities/GUID.h"
+#include "Runtime/Misc/AssetBundle.h"
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/File/ApplicationSpecificPersistentDataPath.h"
+#include "Runtime/Threads/ThreadUtility.h"
+
+#include <time.h>
+
+static const char *kInfoFileName = "__info";
+
+#if WEBPLUG && !UNITY_PEPPER
+#include "Runtime/Utilities/GlobalPreferences.h"
+static const char *kCachingEnabledKey = "CachingEnabled";
+#endif
+
+#if UNITY_PS3
+#include <cell/cell_fs.h>
+extern "C" const char* GetBasePath();
+#endif
+
+#if UNITY_XENON
+#include "XenonPaths.h"
+#endif
+
+#if UNITY_OSX || UNITY_LINUX
+#include <sys/stat.h>
+#elif UNITY_IPHONE
+#include "PlatformDependent/iPhonePlayer/iPhoneMisc.h"
+namespace iphone {
+ std::string GetUserDirectory();
+}
+#elif UNITY_WINRT
+#include "PlatformDependent/MetroPlayer/MetroUtils.h"
+#include "PlatformDependent/Win/PathUnicodeConversion.h"
+#elif UNITY_WIN && !UNITY_WINRT
+#include "PlatformDependent/Win/PathUnicodeConversion.h"
+#include "Shlobj.h"
+
+#define kDefaultPathBufferSize MAX_PATH*4
+
+#define UNITY_DEFINE_KNOWN_FOLDER(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
+ EXTERN_C const GUID DECLSPEC_SELECTANY name \
+ = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
+UNITY_DEFINE_KNOWN_FOLDER(FOLDERID_LocalAppDataLow, 0xA520A1A4, 0x1780, 0x4FF6, 0xBD, 0x18, 0x16, 0x73, 0x43, 0xC5, 0xAF, 0x16);
+
+static std::string gWinCacheBasePath;
+
+string GetWindowsCacheBasePath()
+{
+ if (gWinCacheBasePath.empty())
+ {
+ wchar_t *widePath = NULL;
+
+ HRESULT gotPath;
+
+ typedef HRESULT WINAPI SHGetKnownFolderPathFn(REFGUID rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath);
+ HMODULE hShell32 = LoadLibrary(TEXT("shell32.dll"));
+ if(hShell32)
+ {
+ SHGetKnownFolderPathFn* pfnSHGetKnownFolderPath = reinterpret_cast<SHGetKnownFolderPathFn*>(GetProcAddress(hShell32, "SHGetKnownFolderPath"));
+ if(pfnSHGetKnownFolderPath)
+ {
+ gotPath = pfnSHGetKnownFolderPath(FOLDERID_LocalAppDataLow, NULL, NULL, &widePath);
+ }
+ else
+ {
+ widePath = (wchar_t*)CoTaskMemAlloc(MAX_PATH);
+ gotPath = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, widePath);
+ }
+ if (!FreeLibrary(hShell32))
+ printf_console("Error while freeing shell32 library");;
+ }
+
+ if (SUCCEEDED(gotPath))
+ {
+ ConvertWindowsPathName( widePath, gWinCacheBasePath);
+ }
+
+ if (widePath)
+ CoTaskMemFree(widePath);
+ }
+ else
+ {
+ }
+ return gWinCacheBasePath;
+}
+
+void CreateDirectoryHelper( const std::string& path )
+{
+ std::wstring widePath;
+ ConvertUnityPathName( path, widePath );
+ CreateDirectoryW( widePath.c_str(), NULL );
+}
+#endif
+
+int GetFolderSizeRecursive (const string& path)
+{
+ if (!IsDirectoryCreated (path))
+ return 0;
+
+ set<string> paths;
+ if (!GetFolderContentsAtPath (path, paths))
+ return 0;
+
+ int size = 0;
+ for (set<string>::iterator i = paths.begin(); i != paths.end(); i++)
+ {
+ if (IsDirectoryCreated(*i))
+ size += GetFolderSizeRecursive(*i);
+ else
+ size += GetFileLength (*i);
+ }
+
+ return size;
+}
+
+string Cache::URLToPath (const string& url, int version)
+{
+ string source = GetLastPathNameComponent(url);
+
+ // strip url parameters
+ source = source.substr(0, source.find("?"));
+
+ if (m_IncludePlayerURL)
+ source += GetPlayerSettings().absoluteURL;
+
+ if (version != 0)
+ source += Format("@%d", version);
+
+ return GenerateHash ((UInt8*)&(source[0]), source.size());
+}
+
+
+
+string GetPlatformCachePath()
+{
+#if UNITY_OSX
+ string result = getenv ("HOME");
+ if (result.empty())
+ return string();
+
+#if WEBPLUG || UNITY_EDITOR
+ return AppendPathName( result, "Library/Caches/Unity");
+#else
+ // In Standalones use Library/Caches/BundleID to match App Store Requirements
+ return AppendPathName( result, "Library/Caches");
+#endif
+
+#elif UNITY_LINUX
+ string result = getenv ("HOME");
+ if (!result.empty())
+ return string();
+
+ return AppendPathName( result, ".unity/cache");
+#elif UNITY_WINRT
+ Platform::String^ path = Windows::Storage::ApplicationData::Current->LocalFolder->Path;
+ return AppendPathName(ConvertToUnityPath(path), "UnityCache");
+#elif UNITY_WIN && !UNITY_WINRT
+ std::string result = GetWindowsCacheBasePath();
+ if (result.empty())
+ return result;
+
+ result = AppendPathName( result, "Unity" );
+
+ // "Web Player" is wrong, the directory should be "WebPlayer", for consistency with other Unity usage.
+ // but if there exits an old "Web Player" directory, use that. If both, a new and old one exists,
+ // use the new one, as the old one has probably been created by a FusionFall build after running content with
+ // the fixed web player.
+ string oldCachePath = AppendPathName( result, "Web Player/Cache" );
+ string newCachePath = AppendPathName( result, "WebPlayer/Cache" );
+ if (IsDirectoryCreated (newCachePath))
+ return newCachePath;
+ else if (IsDirectoryCreated (oldCachePath))
+ return oldCachePath;
+ else
+ return newCachePath;
+
+#elif UNITY_IPHONE
+
+ // under ios5 guidelines we should use Library folder for downloaded content
+ // we don't want to use caches as they might be purged anyway
+ // check apple's Technical Q&A QA1719
+ std::string result = iphone::GetLibraryDirectory();
+ return result.empty() ? result : AppendPathName( result, "UnityCache" );
+
+#elif UNITY_PS3
+
+ return AppendPathName( GetBasePath(), "/Cache/" );
+
+#else
+ string result = systeminfo::GetPersistentDataPath();
+ if (result.empty())
+ return result;
+
+ return AppendPathName( result, "UnityCache" );
+#endif
+}
+
+std::string GetDefaultApplicationIdentifierForCache(bool broken)
+{
+ // * Webplayer defaults to the Shared cache
+ // * iOS / Android have dedicated cache folders per app managed by the OS -> thus end up in "Shared" as well.
+ if (WEBPLUG || !UNITY_EMULATE_PERSISTENT_DATAPATH)
+ return "Shared";
+ else
+ {
+ std::string companyName = GetPlayerSettings().companyName;
+ std::string productName = GetPlayerSettings().productName;
+ if (broken)
+ {
+ ConvertToLegalPathNameBroken(companyName);
+ ConvertToLegalPathNameBroken(productName);
+#if UNITY_OSX && !UNITY_EDITOR
+ // In the standalone, we changed the base path to Library/Caches without /Unity,
+ // to match app store requirements. Still find old caches in Unity folder.
+ return AppendPathName ("Unity",companyName + "_" + productName);
+#endif
+ }
+ else
+ {
+#if UNITY_OSX && !UNITY_EDITOR
+ // In the OS X standalone, return the bundle ID to match App Store requirements.
+ return CFStringToString(CFBundleGetIdentifier(CFBundleGetMainBundle()));
+#endif
+ ConvertToLegalPathNameCorrectly(companyName);
+ ConvertToLegalPathNameCorrectly(productName);
+ }
+ return companyName + "_" + productName;
+ }
+}
+
+string GetCachingManagerPath(const std::string& path, bool createPath)
+{
+ std::string cachePath = GetPlatformCachePath();
+ if (path.empty() && !createPath)
+ return cachePath;
+
+ string fullPath = AppendPathName(cachePath, path);
+ if (!createPath)
+ return fullPath;
+
+ if (CreateDirectoryRecursive(fullPath))
+ return fullPath;
+ else
+ return string();
+}
+
+bool CachingManager::MoveTempFolder (const string& from, const string& to)
+{
+#if UNITY_OSX || UNITY_IPHONE || UNITY_ANDROID || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+ if (rename(from.c_str(), to.c_str()))
+#elif UNITY_WIN
+ std::wstring wideFrom, wideTo;
+ ConvertUnityPathName(from, wideFrom);
+ ConvertUnityPathName(to, wideTo);
+ if (!MoveFileExW(wideFrom.c_str(), wideTo.c_str(), MOVEFILE_WRITE_THROUGH))
+#elif UNITY_PS3
+ if (cellFsRename(from.c_str(), to.c_str()) != CELL_FS_SUCCEEDED)
+#elif UNITY_XENON
+ char charFrom[kDefaultPathBufferSize];
+ char charTo[kDefaultPathBufferSize];
+
+ DeleteFileOrDirectory( to.c_str() );
+ ConvertUnityPathName( from.c_str(), charFrom, kDefaultPathBufferSize );
+ ConvertUnityPathName( to.c_str(), charTo, kDefaultPathBufferSize );
+ if (!MoveFile(charFrom, charTo))
+#elif UNITY_PEPPER
+ //todo.
+#else
+#error
+#endif
+ {
+ return false;
+ }
+ SetFileFlags(to, kFileFlagTemporary, 0);
+ return true;
+}
+
+time_t CachingManager::GenerateTimeStamp ()
+{
+ return time(NULL);
+}
+
+void AsyncCachedUnityWebStream::LoadCachedUnityWebStream ()
+{
+ string subpath = GetCachingManager().GetCurrentCache().URLToPath (m_Url, m_Version);
+ string path = GetCachingManager().GetCurrentCache().GetFolder(subpath, false);
+ if (path.empty())
+ {
+ m_Error = "Cannot find cache folder for "+ m_Url;
+ return;
+ }
+
+ vector<string> fileNames;
+ if (!CachingManager::ReadInfoFile (path, NULL, &fileNames))
+ {
+ m_Error = "Could not read __info file.";
+ return;
+ }
+
+ // update timestamp.
+ time_t timestamp = CachingManager::GenerateTimeStamp ();
+ CachingManager::WriteInfoFile (path, fileNames, timestamp);
+ GetCachingManager().GetCurrentCache().UpdateTimestamp(path, timestamp);
+
+ if (m_RequestedCRC)
+ {
+ UInt32 crc = CRCBegin();
+ for (vector<string>::iterator i = fileNames.begin(); i != fileNames.end(); i++)
+ {
+ File fd;
+ string filepath = AppendPathName(path, *i);
+ if (fd.Open (filepath, File::kReadPermission))
+ {
+ const size_t kHashReadBufferSize = 64*1024;
+ UInt8* buffer = (UInt8*)UNITY_MALLOC(kMemTempAlloc,kHashReadBufferSize*sizeof(UInt8));
+ size_t len = GetFileLength(filepath);
+ size_t offset = 0;
+ do
+ {
+ size_t readSize = fd.Read (buffer, kHashReadBufferSize);
+ offset += readSize;
+ crc = CRCFeed (crc, buffer, readSize);
+ }
+ while (offset < len);
+ UNITY_FREE(kMemTempAlloc,buffer);
+ fd.Close ();
+ }
+ }
+ crc = CRCDone(crc);
+ if (crc != m_RequestedCRC)
+ {
+ DeleteFileOrDirectory(path);
+ m_Error = Format("CRC Mismatch. Expected %lx, got %lx. Will not load cached AssetBundle", m_RequestedCRC, crc);
+ return;
+ }
+ }
+
+ PersistentManager &pm = GetPersistentManager();
+ pm.Lock();
+ for (vector<string>::iterator i = fileNames.begin(); i != fileNames.end(); i++)
+ {
+ if (pm.IsStreamLoaded(*i))
+ {
+ pm.Unlock();
+ m_Error = "Cannot load cached AssetBundle. A file of the same name is already loaded from another AssetBundle.";
+ m_AssetBundleWithSameNameIsAlreadyLoaded = true;
+ return;
+ }
+
+ if (!pm.LoadCachedFile(*i, AppendPathName(path, *i)))
+ {
+ pm.Unlock();
+ m_Error = "Cannot load cached AssetBundle.";
+ return;
+ }
+ }
+ pm.Unlock();
+
+ // Load resource file from cached path
+ const string &firstPath = fileNames.front();
+
+ int fileID = 1;
+ Assert(m_LoadingAssetBundle == NULL);
+
+ // Check if the first object is PreloadData.
+ // If it's true, the stream is a scene assetBundle, just load the AssetBundle from the 2nd object.
+ if (pm.GetClassIDFromPathAndFileID(firstPath, fileID) == ClassID(PreloadData))
+ fileID = 2;
+
+ // Try loading the asset bundle from disk.
+ if (pm.GetClassIDFromPathAndFileID(firstPath, fileID) == ClassID(AssetBundle))
+ {
+ int instanceID = pm.GetInstanceIDFromPathAndFileID(firstPath, fileID);
+ m_LoadingAssetBundle = dynamic_pptr_cast<AssetBundle*> (InstanceIDToObjectThreadSafe(instanceID));
+ }
+
+ // Otherwise create one
+ // This is for the old scene assetBundle which doesn't contain an AssetBundle object, just for backward compatibility.
+ if (m_LoadingAssetBundle == NULL)
+ {
+ m_LoadingAssetBundle = NEW_OBJECT_FULL (AssetBundle, kCreateObjectFromNonMainThread);
+ m_LoadingAssetBundle->Reset();
+ m_LoadingAssetBundle->AwakeFromLoadThreaded();
+ }
+
+ // Create cached web stream
+ SET_ALLOC_OWNER(m_LoadingAssetBundle);
+ CachedUnityWebStream* cachedStream = UNITY_NEW(CachedUnityWebStream, kMemFile);
+ cachedStream->m_Version = m_Version;
+ cachedStream->m_Files = fileNames;
+
+ m_LoadingAssetBundle->m_CachedUnityWebStream = cachedStream;
+}
+
+void AsyncCachedUnityWebStream::IntegrateMainThread ()
+{
+ GetPersistentManager().IntegrateAllThreadedObjects();
+
+ if (m_LoadingAssetBundle != NULL)
+ {
+ if (!m_LoadingAssetBundle->IsInstanceIDCreated())
+ {
+ Object::AllocateAndAssignInstanceID(m_LoadingAssetBundle);
+ m_LoadingAssetBundle->AwakeFromLoad(kDidLoadThreaded);
+ }
+
+ m_InstanceID = m_LoadingAssetBundle->GetInstanceID();
+ m_LoadingAssetBundle = NULL;
+ }
+
+ UnityMemoryBarrier();
+ m_Complete = true;
+}
+
+void AsyncCachedUnityWebStream::Perform ()
+{
+ LoadCachedUnityWebStream();
+ m_Progress = 1.0F;
+}
+
+AsyncCachedUnityWebStream::~AsyncCachedUnityWebStream ()
+{
+ AssertIf(!m_Complete);
+}
+
+AsyncCachedUnityWebStream::AsyncCachedUnityWebStream ()
+{
+ m_InstanceID = 0;
+ m_Version = 0;
+ m_LoadingAssetBundle = NULL;
+ m_AssetBundleWithSameNameIsAlreadyLoaded = false;
+}
+
+string Cache::GetFolder (const string& path, bool create)
+{
+ return GetCachingManagerPath(AppendPathName(m_Name, path), create);
+}
+
+bool Cache::CleanCache()
+{
+ if (!m_Ready)
+ {
+ // If we are still scanning the cache folder size, we might have open
+ // file handles, preventing us from deleting the cache folder. As we are deleting the folder,
+ // we don't care about it's size any longer anyways.
+ m_IndexThread.SignalQuit();
+ m_IndexThread.WaitForExit();
+ }
+
+ // Cache clean operation needs to make sure things like cache
+ // authorization are over:
+ Mutex::AutoLock lock (m_Mutex);
+ bool cleaned = GetCachingManager().CleanCache(m_Name);
+
+ if ( cleaned )
+ {
+ m_BytesUsed = 0;
+ m_CachedFiles.clear();
+ }
+
+ return cleaned;
+}
+
+void Cache::SetExpirationDelay (int expiration)
+{
+ m_CacheExpirationDelay = expiration;
+ if (m_CacheExpirationDelay > CachingManager::kMaxCacheExpiration)
+ {
+ ErrorString(Format("Cache expiration may not be higher then %d", CachingManager::kMaxCacheExpiration));
+ m_CacheExpirationDelay = CachingManager::kMaxCacheExpiration;
+ }
+ WriteCacheInfoFile (true);
+}
+
+void Cache::AddToCache (const string &path, int bytes)
+{
+ time_t lastUsedTime = 0;
+ CachingManager::ReadInfoFile (path, &lastUsedTime, NULL);
+
+ if (lastUsedTime > 0 && lastUsedTime < time(NULL) - m_CacheExpirationDelay)
+ {
+ // the file has expired. Don't add it, kill it.
+ DeleteFileOrDirectory (path);
+ return;
+ }
+
+ Mutex::AutoLock lock (m_Mutex);
+ m_BytesUsed += bytes;
+
+ string name = GetLastPathNameComponent (path);
+ size_t seperator = name.find_last_of ('@');
+ int version = 0;
+ if (seperator != string::npos)
+ {
+ version = StringToInt(name.substr(seperator + 1));
+ name = name.substr (0, seperator);
+ }
+
+ m_CachedFiles.insert(CachedFile (path, bytes, version, lastUsedTime));
+}
+
+void Cache::UpdateTimestamp (const string &path, time_t lastAccessed)
+{
+ Mutex::AutoLock lock (m_Mutex);
+
+ for (CachedFiles::iterator i=m_CachedFiles.begin();i!=m_CachedFiles.end();i++)
+ {
+ if (i->path == path)
+ {
+ CachedFile replacement = *i;
+ replacement.lastAccessed = lastAccessed;
+ m_CachedFiles.erase(i);
+ m_CachedFiles.insert(replacement);
+ return;
+ }
+ }
+}
+
+Cache::Cache()
+{
+ m_BytesUsed = 0;
+ m_Expires = 0x7fffffff;
+ m_Ready = false;
+ m_IncludePlayerURL = true;
+ m_CacheExpirationDelay = CachingManager::kMaxCacheExpiration;
+}
+
+Cache::~Cache()
+{
+ m_Ready = true;
+ m_IndexThread.WaitForExit();
+}
+
+void *Cache::ReadCacheIndexThreaded (void *data)
+{
+ Cache *cache = (Cache*)data;
+
+ string cachePath = cache->GetFolder ("", false);
+
+ set<string> paths;
+ if (GetFolderContentsAtPath (cachePath, paths))
+ {
+ for (set<string>::iterator i = paths.begin(); i != paths.end() && !cache->m_Ready; i++)
+ {
+ if (IsDirectoryCreated(*i))
+ cache->AddToCache (*i, GetFolderSizeRecursive(*i));
+ if (cache->m_IndexThread.IsQuitSignaled())
+ break;
+ }
+ }
+
+ // Write cache index to update last used time.
+ // Set a lock on it while we write things.
+ Mutex::AutoLock lock (cache->m_Mutex);
+ cache->WriteCacheInfoFile (false);
+ cache->m_Ready = true;
+ return NULL;
+}
+
+bool Cache::FreeSpace (size_t bytes)
+{
+ Mutex::AutoLock lock (m_Mutex);
+ CachedFiles::iterator i = m_CachedFiles.begin();
+ while (m_BytesAvailable - m_BytesUsed < bytes && i != m_CachedFiles.end())
+ {
+ CachedFiles::iterator cur = i;
+ i++;
+
+ const std::string &path = cur->path;
+ if (!IsDirectoryCreated (path) || (!IsFileOrDirectoryInUse (path) && DeleteFileOrDirectory (path)))
+ {
+ m_BytesUsed -= cur->size;
+ m_CachedFiles.erase (cur);
+ }
+ };
+
+ return m_BytesAvailable - m_BytesUsed >= bytes;
+}
+
+#define kCacheInfoVersion 1
+void Cache::WriteCacheInfoFile (bool updateExpiration)
+{
+ string folderPath = GetFolder ("", false);
+ if (!IsDirectoryCreated(folderPath))
+ return;
+
+ if (updateExpiration)
+ m_Expires = time(NULL) + m_CacheExpirationDelay;
+
+ time_t oldestLastUsedTime = 0;
+ if (m_CachedFiles.begin() != m_CachedFiles.end())
+ oldestLastUsedTime = m_CachedFiles.begin()->lastAccessed;
+
+ string indexFile = Format("%llu\n%d\n%llu\n", (UInt64)m_Expires, kCacheInfoVersion, (UInt64)oldestLastUsedTime);
+ string path = AppendPathName( folderPath, kInfoFileName);
+
+ File f;
+ if (!f.Open(path, File::kWritePermission, File::kSilentReturnOnOpenFail|File::kRetryOnOpenFail))
+ {
+ return;
+ }
+
+ SetFileFlags(path, kFileFlagDontIndex, kFileFlagDontIndex);
+
+ if (!f.Write(&indexFile[0], indexFile.size()))
+ {
+ f.Close();
+ return;
+ }
+ f.Close();
+}
+
+#define NEXT_HEADER_FIELD(iterator,file,expectEOF) { (iterator)++; if ((iterator) == (file).end()) return (expectEOF); }
+bool Cache::ReadCacheInfoFile (const string& path, time_t *expires, time_t *oldestLastUsedTime)
+{
+ InputString headerData;
+ if (!ReadStringFromFile(&headerData, AppendPathName(path, kInfoFileName)))
+ return false;
+
+ vector<string> seperatedHeader = FindSeparatedPathComponents(headerData.c_str (), headerData.size (), '\n');
+ vector<string>::iterator read = seperatedHeader.begin();
+
+ if (read == seperatedHeader.end())
+ return false;
+
+ //expires
+ if (expires)
+ *expires = StringToInt(*read);
+
+ NEXT_HEADER_FIELD (read, seperatedHeader, true);
+
+ //version
+ int version = StringToInt(*read);
+ if (version < 1)
+ return false;
+ NEXT_HEADER_FIELD (read, seperatedHeader, false);
+
+ //lastUsedTime
+ if (oldestLastUsedTime)
+ *oldestLastUsedTime = StringToInt(*read);
+ NEXT_HEADER_FIELD (read, seperatedHeader, true);
+ return true;
+}
+
+bool Cache::ReadCacheIndex (const string& name, bool getSize)
+{
+ m_Name = name;
+
+ string cachePath = GetFolder ( "", false);
+ time_t curTime = time(NULL);
+ time_t oldestLastUsedTime = curTime;
+ m_Expires = curTime + m_CacheExpirationDelay;
+
+ ReadCacheInfoFile (cachePath, &m_Expires, &oldestLastUsedTime);
+
+ if (getSize)
+ {
+ m_Ready = false;
+ m_Mutex.Lock();
+ m_BytesUsed = 0;
+ m_CachedFiles.clear();
+ m_Mutex.Unlock();
+ m_IndexThread.Run (ReadCacheIndexThreaded, (void*)this);
+ }
+ else
+ m_Ready = true;
+
+ return true;
+}
+
+
+void Cache::SetMaximumDiskSpaceAvailable (long long maxUsage)
+{
+ if (maxUsage > m_MaxLicenseBytesAvailable)
+ {
+ ErrorString("Maximum disk space used exceeds what is allowed by the license");
+ return;
+ }
+
+ m_BytesAvailable = maxUsage;
+}
+
+
+string CachingManager::GetTempFolder ()
+{
+ UnityGUID guid;
+ guid.Init();
+ string guidStr = GUIDToString(guid);
+ string temp = "Temp";
+ return GetCachingManagerPath (AppendPathName(temp, guidStr), true);
+}
+
+bool CachingManager::CleanCache(string name)
+{
+ string path = GetCachingManagerPath(name, false);
+
+ if ( IsFileOrDirectoryInUse( path ) )
+ {
+ return false;
+ }
+
+ GetGlobalCachingManager().RebuildAllCaches ();
+
+ return DeleteFileOrDirectory(path);
+}
+
+#define kInfoVersion 1
+
+int CachingManager::WriteInfoFile (const string &path, const vector<string> &fileNames)
+{
+ return WriteInfoFile (path, fileNames, GenerateTimeStamp ());
+}
+
+int CachingManager::WriteInfoFile (const string &path, const vector<string> &fileNames, time_t lastAccessed)
+{
+ string info;
+ // write negative info version (positive numbers had been used by previous, unversioned cache functions)
+ info += IntToString(-kInfoVersion) + "\n";
+
+ info += IntToString(lastAccessed) + "\n";
+
+ info += IntToString(fileNames.size()) + "\n";
+
+ for (vector<string>::const_iterator i=fileNames.begin();i != fileNames.end();i++)
+ info += *i + "\n";
+
+ File headerFile;
+ string filename = AppendPathName(path, kInfoFileName);
+ if (!headerFile.Open(filename, File::kWritePermission, File::kSilentReturnOnOpenFail|File::kRetryOnOpenFail))
+ return 0;
+
+ SetFileFlags(filename, kFileFlagDontIndex, kFileFlagDontIndex);
+
+ if (!headerFile.Write(&info[0], info.size()))
+ {
+ headerFile.Close();
+ return 0;
+ }
+
+ headerFile.Close();
+ return info.size();
+}
+
+bool CachingManager::ReadInfoFile (const string &path, time_t *lastUsedTime, vector<string> *fileNames)
+{
+ InputString headerData;
+ if (!ReadStringFromFile(&headerData, AppendPathName(path, kInfoFileName)))
+ return false;
+
+ vector<string> seperatedHeader = FindSeparatedPathComponents(headerData.c_str (), headerData.size (), '\n');
+ vector<string>::iterator read = seperatedHeader.begin();
+
+ if (read == seperatedHeader.end())
+ return false;
+
+ //version
+ int version = StringToInt(*read);
+ if (version >= 0)
+ return false;
+
+ NEXT_HEADER_FIELD (read, seperatedHeader, false);
+
+ //lastUsedTime
+ if (lastUsedTime)
+ *lastUsedTime = StringToInt(*read);
+ NEXT_HEADER_FIELD (read, seperatedHeader, false);
+
+ if (fileNames != NULL)
+ {
+ //numFiles
+ int numFiles = StringToInt(*read);
+ fileNames->resize (numFiles);
+ NEXT_HEADER_FIELD (read, seperatedHeader, false);
+
+ //files
+ for (int i=0;i<numFiles;i++)
+ {
+ (*fileNames)[i] = *read;
+ NEXT_HEADER_FIELD (read, seperatedHeader, i == numFiles - 1);
+ }
+ }
+ return true;
+}
+
+AsyncCachedUnityWebStream* CachingManager::LoadCached (const std::string& url, int version, UInt32 crc)
+{
+ AsyncCachedUnityWebStream* stream = new AsyncCachedUnityWebStream ();
+ stream->m_Url = url;
+ stream->m_Version = version;
+ stream->m_RequestedCRC = crc;
+
+ GetPreloadManager().AddToQueue(stream);
+
+ return stream;
+}
+
+bool CachingManager::IsCached (const string &url, int version)
+{
+ string subpath = GetCachingManager().GetCurrentCache().URLToPath (url, version);
+ string path = GetCachingManager().GetCurrentCache().GetFolder(subpath, false);
+ if (path.empty())
+ return false;
+
+ vector<string> fileNames;
+ if (!CachingManager::ReadInfoFile (path, NULL, &fileNames))
+ return false;
+
+ return true;
+}
+
+bool CachingManager::MarkAsUsed (const string &url, int version)
+{
+ string subpath = GetCachingManager().GetCurrentCache().URLToPath (url, version);
+ string path = GetCachingManager().GetCurrentCache().GetFolder(subpath, false);
+ if (path.empty())
+ return false;
+
+ vector<string> fileNames;
+ if (!CachingManager::ReadInfoFile (path, NULL, &fileNames))
+ return false;
+
+ // update timestamp.
+ time_t timestamp = GenerateTimeStamp ();
+ CachingManager::WriteInfoFile (path, fileNames, timestamp);
+ GetCachingManager().GetCurrentCache().UpdateTimestamp(path, timestamp);
+
+ return true;
+}
+
+#if UNITY_IPHONE
+// TODO: copy paste
+void CachingManager::SetNoBackupFlag(const std::string &url, int version)
+{
+ string subpath = GetCachingManager().GetCurrentCache().URLToPath (url, version);
+ string path = GetCachingManager().GetCurrentCache().GetFolder(subpath, false);
+
+ if (path.empty())
+ return;
+
+ vector<string> fileName;
+ if (!CachingManager::ReadInfoFile (path, NULL, &fileName))
+ return;
+
+ for (vector<string>::iterator i = fileName.begin(); i != fileName.end(); ++i)
+ iphone::SetNoBackupFlag(i->c_str());
+}
+
+void CachingManager::ResetNoBackupFlag(const std::string &url, int version)
+{
+ string subpath = GetCachingManager().GetCurrentCache().URLToPath (url, version);
+ string path = GetCachingManager().GetCurrentCache().GetFolder(subpath, false);
+
+ if (path.empty())
+ return;
+
+ vector<string> fileName;
+ if (!CachingManager::ReadInfoFile (path, NULL, &fileName))
+ return;
+
+ for (vector<string>::iterator i = fileName.begin(); i != fileName.end(); ++i)
+ iphone::ResetNoBackupFlag(i->c_str());
+}
+#endif
+
+
+void CachingManager::ClearTempFolder ()
+{
+ string path = GetCachingManagerPath("Temp", false);
+ if (!IsDirectoryCreated (path))
+ return;
+
+ set<string> tempFolders;
+ if (!GetFolderContentsAtPath (path, tempFolders))
+ return;
+
+ for (set<string>::iterator i = tempFolders.begin(); i != tempFolders.end(); i++)
+ {
+ string lockPath = AppendPathName( *i, "__lock" );
+ if ( !IsFileCreated( lockPath ) || !IsFileOrDirectoryInUse( *i ) )
+ {
+ // No lock file exists or the directory was not in use. Delete temp folder.
+ DeleteFileOrDirectory(*i);
+ continue;
+ }
+ }
+}
+
+void CachingManager::Reset ()
+{
+ Dispose();
+
+ m_Authorization = kAuthorizationNone;
+
+ string defaultCacheName = GetDefaultApplicationIdentifierForCache(true);
+ if (!IsDirectoryCreated (defaultCacheName))
+ defaultCacheName = GetDefaultApplicationIdentifierForCache(false);
+
+ #if WEBPLUG
+ // Webplugin has a 50mb shared cache and includes the hashed unity3d file as the name
+ SetCurrentCache (defaultCacheName, 50 * 1024 * 1024, true);
+ #else
+ // Other platforms has a 4GB shared cache
+ m_Authorization = kAuthorizationUser;
+ SetCurrentCache (defaultCacheName, std::numeric_limits<long long>::max(), false);
+ #endif
+
+ // - On webplayer and PC we are resonposible for cleaning up the cache automatically
+ // - On mobile platforms the OS is responsible for cleaning up left over cache data.
+ if (UNITY_EMULATE_PERSISTENT_DATAPATH || WEBPLUG)
+ GetGlobalCachingManager().ClearAllExpiredCaches();
+
+ ClearTempFolder ();
+#if UNITY_PEPPER
+ m_Enabled = false;
+#elif WEBPLUG
+ m_Enabled = GetGlobalBoolPreference(kCachingEnabledKey, true);
+#else
+ m_Enabled = true;
+#endif
+}
+
+CachingManager::CachingManager ()
+{
+ m_Cache = NULL;
+ Reset ();
+}
+
+void CachingManager::Dispose ()
+{
+ GetGlobalCachingManager().Dispose();
+ if (m_Cache != NULL)
+ {
+ delete m_Cache;
+ m_Cache = NULL;
+ }
+}
+
+Cache& CachingManager::GetCurrentCache()
+{
+ return *m_Cache;
+}
+
+CachingManager::~CachingManager ()
+{
+ Dispose ();
+}
+
+bool CachingManager::VerifyValidDomain (const std::string &domain)
+{
+#if WEBPLUG && !UNITY_PEPPER
+ string currentDomain = GetPlayerSettings().absoluteURL;
+ if (currentDomain.find("http://") == 0 || currentDomain.find("https://") == 0)
+ {
+ currentDomain = GetStrippedPlayerDomain();
+
+ //remove http://
+ string domainStripped = domain;
+ domainStripped.erase(0, 7);
+
+ //remove path
+ std::string::size_type pos = domainStripped.find("/", 0);
+ if (pos != std::string::npos)
+ domainStripped.erase(domainStripped.begin() + pos, domainStripped.end());
+
+ if (currentDomain == "localhost")
+ return true;
+
+ //remove subdomain
+ string currentDomainStripped = currentDomain;
+ currentDomainStripped.erase(0, currentDomainStripped.length()-domainStripped.length());
+
+ if (domainStripped == currentDomainStripped)
+ return true;
+
+ ErrorString ("You need to run this player from the "+domain+" domain to be authorized to use the caching API. (currently running from "+currentDomain+" )");
+ return false;
+ }
+ // file:// domain is always valid for testing purposes
+ else if (currentDomain.find("file://") == 0)
+ return true;
+ // absoluteUrl is not a valid url???
+ else
+ {
+ ErrorString ("You need to run this player from the "+domain+" domain to be authorized to use the caching API.");
+ return false;
+ }
+#else
+ return true;
+#endif
+}
+
+void CachingManager::SetCurrentCache (const string &name, long long size, bool includePlayerURL)
+{
+ if (m_Cache != NULL)
+ delete m_Cache;
+ m_Cache = new Cache();
+
+ if (IsDirectoryCreated (GetCachingManagerPath(name, false)))
+ {
+ //update expiration time
+ m_Cache->WriteCacheInfoFile (true);
+ }
+
+ m_Cache->m_BytesAvailable = size;
+ m_Cache->m_MaxLicenseBytesAvailable = size;
+
+ m_Cache->m_IncludePlayerURL = includePlayerURL;
+ m_Cache->ReadCacheIndex (name, true);
+}
+
+bool CachingManager::Authorize (const string &name, const string &domain, long long size, int expiration, const string &signature)
+{
+ if (!VerifyValidDomain(domain))
+ return false;
+
+ m_Authorization = kAuthorizationNone;
+
+ if (::VerifySignature ("Cache="+name+";Domain="+domain+";Admin", signature))
+ {
+ m_Authorization = kAuthorizationAdmin;
+ }
+ else
+ {
+ string authoriziationString = "Cache="+name+";Domain="+domain+Format(";Size=%lld",size);
+ if (::VerifySignature (authoriziationString, signature))
+ m_Authorization = kAuthorizationUser;
+ else if (::VerifySignature (authoriziationString+Format(";Expiration=%d",expiration), signature))
+ m_Authorization = kAuthorizationUser;
+ else
+ {
+ WarningString ("Authorization to use the caching API failed.\n" + authoriziationString);
+ return false;
+ }
+ }
+
+ Assert(m_Authorization != kAuthorizationNone);
+
+ if ( expiration > 0 )
+ {
+ // expiration in seconds from Jan 1, 1970 UTC (use `date -j mmddYYYY +%s` or `date -j -v +30d +%s` to generate)
+ if ( time(NULL) > expiration)
+ {
+ m_Authorization = kAuthorizationNone;
+ WarningString(Format("Authorization for this cache has expired. Please renew your caching authorization. It will still work in the editor, but will fail in any players you build. (%d)", expiration));
+ return false;
+ }
+ }
+
+ SetCurrentCache (name, size, false);
+
+ return true;
+}
+
+CachingManager::AuthorizationLevel CachingManager::GetAuthorizationLevel ()
+{
+ if (!m_Enabled)
+ return (m_Authorization == kAuthorizationAdmin) ? kAuthorizationAdmin : kAuthorizationNone;
+
+ return m_Authorization;
+}
+
+void CachingManager::SetEnabled (bool enabled)
+{
+ m_Enabled = enabled;
+#if WEBPLUG && !UNITY_PEPPER
+ SetGlobalBoolPreference(kCachingEnabledKey, m_Enabled);
+#endif
+}
+
+long long CachingManager::GetCachingDiskSpaceUsed ()
+{
+ return GetCurrentCache().GetCachingDiskSpaceUsed();
+}
+
+long long CachingManager::GetCachingDiskSpaceFree ()
+{
+ return GetCurrentCache().GetCachingDiskSpaceFree();
+}
+
+void CachingManager::SetMaximumDiskSpaceAvailable (long long maximumAvailable)
+{
+ return GetCurrentCache().SetMaximumDiskSpaceAvailable(maximumAvailable);
+}
+
+long long CachingManager::GetMaximumDiskSpaceAvailable ()
+{
+ return GetCurrentCache().GetMaximumDiskSpaceAvailable();
+}
+
+int CachingManager::GetExpirationDelay ()
+{
+ return GetCurrentCache().GetExpirationDelay();
+}
+
+bool CachingManager::GetIsReady ()
+{
+ return GetCurrentCache().GetIsReady();
+}
+
+CachingManager* gCachingManager = NULL;
+CachingManager& GetCachingManager()
+{
+ if (gCachingManager == NULL)
+ gCachingManager = new CachingManager();
+
+ return *gCachingManager;
+}
+
+
+GlobalCachingManager* gGlobalCachingManager = NULL;
+GlobalCachingManager &GetGlobalCachingManager()
+{
+ if (gGlobalCachingManager == NULL)
+ gGlobalCachingManager = new GlobalCachingManager();
+
+ return *gGlobalCachingManager;
+}
+
+
+void GlobalCachingManager::RebuildAllCaches ()
+{
+ // delete the cached vector of caches. Will rebuild on next call to get_index
+ if (m_CacheIndices != NULL)
+ {
+ for (vector<Cache*>::iterator i = m_CacheIndices->begin(); i!=m_CacheIndices->end(); i++)
+ delete *i;
+ delete m_CacheIndices;
+ m_CacheIndices = NULL;
+ }
+}
+
+void GlobalCachingManager::Dispose ()
+{
+ if (m_CacheIndices != NULL)
+ {
+ for (vector<Cache*>::iterator i = m_CacheIndices->begin(); i!=m_CacheIndices->end(); i++)
+ delete *i;
+ delete m_CacheIndices;
+ m_CacheIndices = NULL;
+ }
+}
+
+std::vector<Cache*>& GlobalCachingManager::GetCacheIndices ()
+{
+ if (m_CacheIndices == NULL)
+ {
+ m_CacheIndices = new std::vector<Cache*>();
+ ReadCacheIndices (*m_CacheIndices, true);
+ }
+ return *m_CacheIndices;
+}
+
+void GlobalCachingManager::ReadCacheIndices (vector<Cache*> &indices, bool getSize)
+{
+ indices.clear();
+ string path = GetCachingManagerPath("", false);
+ if (!IsDirectoryCreated (path))
+ return;
+
+ set<string> caches;
+ if (!GetFolderContentsAtPath (path, caches))
+ return;
+
+ for (set<string>::iterator i = caches.begin(); i != caches.end(); i++)
+ {
+ if (!IsDirectoryCreated (*i))
+ continue;
+
+ string name = GetLastPathNameComponent(*i);
+ if (name == "Temp")
+ continue;
+
+ Cache *cache = new Cache();
+ bool success = cache->ReadCacheIndex (name, getSize);
+
+ // Remove expired caches
+ if (cache->m_Expires < time(NULL) || !success)
+ {
+ DeleteFileOrDirectory(*i);
+ delete cache;
+ }
+ else
+ indices.push_back(cache);
+ }
+}
+
+void GlobalCachingManager::ClearAllExpiredCaches ()
+{
+ vector<Cache*> indices;
+ ReadCacheIndices (indices, false);
+ for (vector<Cache*>::iterator i = indices.begin(); i!=indices.end(); i++)
+ delete *i;
+}
+
+#endif
diff --git a/Runtime/Misc/CachingManager.h b/Runtime/Misc/CachingManager.h
new file mode 100644
index 0000000..4d78221
--- /dev/null
+++ b/Runtime/Misc/CachingManager.h
@@ -0,0 +1,215 @@
+#ifndef CACHINGMANAGER_H
+#define CACHINGMANAGER_H
+
+#include "Runtime/Misc/AssetBundle.h"
+
+class Cache;
+
+#if ENABLE_CACHING
+
+#include "PreloadManager.h"
+
+class CachedUnityWebStream
+{
+ public:
+ SInt32 m_Version;
+ std::vector<std::string> m_Files;
+};
+
+class AsyncCachedUnityWebStream : public PreloadManagerOperation
+{
+ int m_InstanceID;
+ AssetBundle* m_LoadingAssetBundle;
+ std::string m_Url;
+ std::string m_Error;
+ int m_Version;
+ UInt32 m_RequestedCRC;
+ bool m_AssetBundleWithSameNameIsAlreadyLoaded;
+
+ friend class CachingManager;
+
+ void LoadCachedUnityWebStream();
+public:
+
+ AsyncCachedUnityWebStream ();
+ ~AsyncCachedUnityWebStream ();
+
+ AssetBundle* GetAssetBundle () { return PPtr<AssetBundle> (m_InstanceID); }
+ bool DidSucceed() { return IsDone() && m_InstanceID != 0;}
+ bool AssetBundleWithSameNameIsAlreadyLoaded() { return m_AssetBundleWithSameNameIsAlreadyLoaded; }
+ virtual void Perform ();
+
+ virtual void IntegrateMainThread ();
+ virtual bool HasIntegrateMainThread () { return true; }
+ std::string &GetError() { return m_Error; }
+
+#if ENABLE_PROFILER
+ virtual std::string GetDebugName () { return "AsyncCachedUnityWebStream"; }
+#endif
+};
+
+class Cache : public NonCopyable
+{
+public:
+ bool m_IncludePlayerURL;
+ string m_Name;
+ time_t m_Expires;
+ long long m_BytesUsed;
+ long long m_BytesAvailable; // bytes allowed
+ long long m_MaxLicenseBytesAvailable; // Bytes allowed by the license
+
+ int GetExpirationDelay () { return m_CacheExpirationDelay; }
+ void SetExpirationDelay (int expiration);
+
+ void WriteCacheInfoFile (bool updateExpiration);
+ string GetFolder (const string& path, bool create);
+
+ void AddToCache (const string &path, int bytes);
+ void UpdateTimestamp (const std::string &path, time_t lastAccessed);
+
+ bool CleanCache ();
+
+ bool FreeSpace (size_t bytes);
+
+ void SetMaximumDiskSpaceAvailable (long long maxUsage);
+ long long GetMaximumDiskSpaceAvailable () { return m_BytesAvailable; }
+
+ long long GetCachingDiskSpaceUsed () { return m_BytesUsed; }
+ long long GetCachingDiskSpaceFree () { return m_BytesAvailable - m_BytesUsed; }
+ bool GetIsReady () { return m_Ready; }
+
+ bool ReadCacheIndex (const string& name, bool getSize);
+ string URLToPath (const string& url, int version);
+
+ Cache();
+ ~Cache();
+
+private:
+ struct CachedFile
+ {
+ string path;
+ size_t size;
+ int version;
+ time_t lastAccessed;
+
+ CachedFile()
+ {
+ size = version = lastAccessed = 0;
+ }
+ CachedFile(string _path, size_t _size, int _version, time_t _lastAccessed)
+ {
+ path = _path;
+ size = _size;
+ version = _version;
+ lastAccessed = _lastAccessed;
+ }
+ bool operator < (const CachedFile& other) const
+ {
+ return lastAccessed < other.lastAccessed;
+ }
+ };
+
+ typedef std::multiset<CachedFile> CachedFiles;
+
+ int m_CacheExpirationDelay;
+ Thread m_IndexThread;
+ Mutex m_Mutex;
+ bool m_Ready;
+ CachedFiles m_CachedFiles;
+
+ bool ReadCacheInfoFile (const string& path, time_t *expires, time_t *oldestLastUsedTime);
+ static void *ReadCacheIndexThreaded (void *data);
+};
+
+class CachingManager
+{
+public:
+ enum AuthorizationLevel
+ {
+ kAuthorizationNone = 0,
+ kAuthorizationUser,
+ kAuthorizationAdmin
+ };
+ enum
+ {
+ kMaxCacheExpiration = (150 * 86400) //150 days
+ };
+
+ CachingManager ();
+ ~CachingManager ();
+ void Reset ();
+
+ std::string GetTempFolder ();
+ static bool MoveTempFolder (const string& from, const string& to);
+ static time_t GenerateTimeStamp ();
+ static int WriteInfoFile (const std::string &path, const std::vector<std::string> &fileNames);
+ static int WriteInfoFile (const std::string &path, const std::vector<std::string> &fileNames, time_t lastAccessed);
+ static bool ReadInfoFile (const std::string &path, time_t *lastUsedTime, std::vector<std::string> *fileNames);
+
+ std::vector<Cache*> &GetCacheIndices ();
+
+ bool CleanCache (std::string name);
+
+ bool Authorize (const string &name, const string &domain, long long size, int expiration, const string &signature);
+ AuthorizationLevel GetAuthorizationLevel ();
+
+ AsyncCachedUnityWebStream* LoadCached (const std::string& url, int version, UInt32 crc);
+ bool IsCached (const std::string &url, int version);
+ bool MarkAsUsed (const std::string& url, int version);
+
+ bool GetEnabled () { return m_Enabled; }
+ void SetEnabled (bool enabled);
+
+
+ Cache& GetCurrentCache();
+
+ long long GetCachingDiskSpaceUsed ();
+ long long GetCachingDiskSpaceFree ();
+
+ void SetMaximumDiskSpaceAvailable (long long maximumAvailable);
+ long long GetMaximumDiskSpaceAvailable ();
+
+ int GetExpirationDelay ();
+ bool GetIsReady ();
+
+#if UNITY_IPHONE
+ void SetNoBackupFlag(const std::string &url, int version);
+ void ResetNoBackupFlag(const std::string &url, int version);
+#endif
+
+private:
+ AuthorizationLevel m_Authorization;
+ Cache* m_Cache;
+ bool m_Enabled;
+
+ void Dispose ();
+ void ClearTempFolder();
+ bool VerifyValidDomain (const std::string &domain);
+ void SetCurrentCache (const string &name, long long size, bool includePlayerURL);
+ void ReadCacheIndices (std::vector<Cache*> &indices, bool getSize);
+};
+
+/// Handles the cache for multiple games
+class GlobalCachingManager
+{
+ std::vector<Cache*>* m_CacheIndices;
+ void ReadCacheIndices (std::vector<Cache*> &indices, bool getSize);
+
+public:
+ GlobalCachingManager ()
+ {
+ m_CacheIndices = NULL;
+ }
+
+ std::vector<Cache*>& GetCacheIndices ();
+ void ClearAllExpiredCaches ();
+ void RebuildAllCaches ();
+ void Dispose ();
+};
+
+
+CachingManager &GetCachingManager();
+GlobalCachingManager &GetGlobalCachingManager();
+
+#endif // ENABLE_CACHING
+#endif
diff --git a/Runtime/Misc/CaptureScreenshot.cpp b/Runtime/Misc/CaptureScreenshot.cpp
new file mode 100644
index 0000000..440f1ac
--- /dev/null
+++ b/Runtime/Misc/CaptureScreenshot.cpp
@@ -0,0 +1,605 @@
+#include "UnityPrefix.h"
+#include "CaptureScreenshot.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/IMGUI/GUIManager.h"
+#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h"
+#if ENABLE_RETAINEDGUI
+#include "Runtime/ExGUI/GUITracker.h"
+#endif
+
+#if UNITY_WII
+#define STB_IMAGE_WRITE_IMPLEMENTATION
+#include "External/stb/stb_image_write.h"
+#include "PlatformDependent/Wii/WiiHio2.h"
+#endif
+
+#if UNITY_FLASH
+#include "PlatformDependent/FlashSupport/cpp/FlashUtils.h"
+#include "Runtime/GfxDevice/molehill/MolehillUtils.h"
+#endif
+
+#if UNITY_WP8
+#include "Runtime/Graphics/ScreenManager.h"
+#endif
+
+
+#if UNITY_PS3
+#include <cell/codec.h>
+
+ bool ConvertImageToPNGFilePS3 (const ImageReference& inputImage, const char* path)
+ {
+ int ret;
+ static bool hasCreatedEncoder = false;
+ static CellPngEncHandle encoderHandle;
+
+ const UInt32 w = inputImage.GetWidth();
+ const UInt32 h = inputImage.GetHeight();
+ const UInt32 p = inputImage.GetRowBytes();
+ const UInt32 bpp = p / w;
+
+ if (!hasCreatedEncoder)
+ {
+ CELLCALL(cellSysmoduleLoadModule(CELL_SYSMODULE_PNGENC));
+
+ CellPngEncConfig config;
+ CellPngEncAttr attr;
+ CellPngEncResource resource;
+
+ config.maxWidth = 1920;
+ config.maxHeight = 1080;
+ config.maxBitDepth = 8;
+ config.addMemSize = 0;
+ config.enableSpu = true;
+ config.exParamList = NULL;
+ config.exParamNum = 0;
+
+ CELLCALL(cellPngEncQueryAttr(&config, &attr));
+
+ resource.memAddr = malloc(attr.memSize);
+ resource.memSize = attr.memSize;
+ resource.ppuThreadPriority = 1000;
+ resource.spuThreadPriority = 200;
+
+ CELLCALL(cellPngEncOpen(&config, &resource, &encoderHandle));
+
+ hasCreatedEncoder = true;
+ }
+
+ CellPngEncPicture picture;
+ CellPngEncEncodeParam encodeParam;
+ CellPngEncOutputParam outputParam;
+ CellPngEncStreamInfo streamInfo;
+
+ picture.width = w;
+ picture.height = h;
+ picture.pitchWidth = bpp * w;
+ picture.colorSpace = bpp == 4 ? CELL_PNGENC_COLOR_SPACE_ARGB : CELL_PNGENC_COLOR_SPACE_RGB;
+ picture.bitDepth = 8;
+ picture.packedPixel = false;
+ picture.pictureAddr = inputImage.GetImageData();
+
+ encodeParam.enableSpu = true;
+ encodeParam.encodeColorSpace = CELL_PNGENC_COLOR_SPACE_RGB;
+ encodeParam.compressionLevel = CELL_PNGENC_COMPR_LEVEL_1;
+ encodeParam.filterType = CELL_PNGENC_FILTER_TYPE_SUB;
+ encodeParam.ancillaryChunkList = NULL;
+ encodeParam.ancillaryChunkNum = 0;
+
+ outputParam.location = CELL_PNGENC_LOCATION_BUFFER;
+ outputParam.streamFileName = NULL;
+ outputParam.limitSize = w * h * 4;
+ outputParam.streamAddr = malloc(outputParam.limitSize);
+
+ CELLCALL(cellPngEncEncodePicture(encoderHandle, &picture, &encodeParam, &outputParam));
+
+ // wait for encoding to finish
+
+ uint32_t streamInfoNum;
+
+ CELLCALL(cellPngEncWaitForOutput(encoderHandle, &streamInfoNum, true));
+ CELLCALL(cellPngEncGetStreamInfo(encoderHandle, &streamInfo));
+
+ string finalPath = path;
+ ConvertSeparatorsToPlatform(finalPath);
+
+ File file;
+ if (!file.Open(finalPath.c_str(), File::kWritePermission))
+ return false;
+
+ file.Write(streamInfo.streamAddr, streamInfo.streamSize);
+ file.Close();
+
+#if ENABLE_PLAYERCONNECTION
+ TransferFileOverPlayerConnection(finalPath, streamInfo.streamAddr, streamInfo.streamSize);
+#endif
+ free(outputParam.streamAddr);
+ return true;
+ }
+#endif
+
+
+#if UNITY_XENON
+bool ConvertImageToPNGFileXbox360(const ImageReference& inputImage, const char* path)
+{
+ const UInt32 w = inputImage.GetWidth();
+ const UInt32 h = inputImage.GetHeight();
+ const UInt32 p = inputImage.GetRowBytes();
+ const UInt32 s = h * p;
+ const UInt32 bpp = p / w;
+ if (bpp != 4)
+ {
+ UNITY_TRACE("Can only save 32bpp screenshots.\n");
+ return false;
+ }
+
+ DWORD size = XGSetTextureHeader(inputImage.GetWidth(), inputImage.GetHeight(), 1, D3DUSAGE_CPU_CACHED_MEMORY, D3DFMT_LIN_A8R8G8B8, 0, 0, 0, p, NULL, NULL, NULL);
+ if (size != s)
+ {
+ UNITY_TRACE("Invalid screenshot size.\n");
+ return false;
+ }
+
+ bool success = true;
+
+ IDirect3DTexture9* tex = new IDirect3DTexture9();
+ XGSetTextureHeader(inputImage.GetWidth(), inputImage.GetHeight(), 1, D3DUSAGE_CPU_CACHED_MEMORY, D3DFMT_LIN_A8R8G8B8, 0, 0, 0, p, tex, NULL, NULL);
+ XGOffsetResourceAddress(tex, inputImage.GetImageData());
+
+ string finalPath = path;
+ ConvertSeparatorsToPlatform(finalPath);
+
+#if ENABLE_PLAYERCONNECTION
+ ID3DXBuffer* buffer = 0;
+ HRESULT hr = D3DXSaveTextureToFileInMemory(&buffer, D3DXIFF_PNG, tex, NULL);
+ success &= SUCCEEDED(hr);
+
+ if (buffer)
+ {
+ File file;
+ if (file.Open(finalPath.c_str(), File::kWritePermission))
+ {
+ success &= file.Write(buffer->GetBufferPointer(), buffer->GetBufferSize());
+ file.Close();
+ }
+ else
+ success = false;
+
+ TransferFileOverPlayerConnection(finalPath, buffer->GetBufferPointer(), buffer->GetBufferSize());
+ buffer->Release();
+ }
+#else
+ HRESULT hr = D3DXSaveTextureToFile(finalPath.c_str(), D3DXIFF_PNG, tex, NULL);
+ success &= SUCCEEDED(hr);
+#endif
+
+ delete tex;
+
+ return success;
+}
+#endif
+
+#if UNITY_XENON || UNITY_PS3
+//#define ENABLE_MULTITHREADED 1
+ bool ConvertImageToTGAFile (const ImageReference& inputImage, const char* path)
+ {
+ bool success = false;
+ const UInt32 w = inputImage.GetWidth();
+ const UInt32 h = inputImage.GetHeight();
+ const UInt32 p = inputImage.GetRowBytes();
+ const UInt32 bpp = p / w;
+
+ UInt8 tgaHeader[18] = {
+ 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ (w>>0)&0xff, (w>>8)&0xff,
+ (h>>0)&0xff, (h>>8)&0xff,
+ bpp<<3, (bpp==4)?8:0
+ };
+
+ string finalPath = path;
+ ConvertSeparatorsToPlatform(finalPath);
+
+ File file;
+ if (!file.Open(finalPath.c_str(), File::kWritePermission))
+ return false;
+
+ success |= file.Write(tgaHeader, 18);
+ success |= file.Write(inputImage.GetImageData(), h*p);
+
+ file.Close();
+
+#if ENABLE_PLAYERCONNECTION
+ TransferFileOverPlayerConnection(finalPath, inputImage.GetImageData(), h*p, tgaHeader, sizeof(tgaHeader));
+#endif
+
+ return success;
+ }
+#endif
+
+// don't even compile the code in web player
+#if CAPTURE_SCREENSHOT_AVAILABLE
+
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Graphics/ImageConversion.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Misc/SystemInfo.h"
+
+using namespace std;
+
+static const char* gCaptureScreenshotPath = NULL;
+static int s_CaptureSuperSize;
+
+#if CAPTURE_SCREENSHOT_THREAD
+Thread gCaptureScreenshotThread;
+#endif
+
+void QueueScreenshot (const string& path, int superSize)
+{
+#if UNITY_IPHONE || UNITY_ANDROID
+ gCaptureScreenshotPath = strdup (AppendPathName (systeminfo::GetPersistentDataPath(), path).c_str());
+#else
+ gCaptureScreenshotPath = strdup(PathToAbsolutePath(path).c_str());
+#endif
+ s_CaptureSuperSize = clamp (superSize, 0, 16);
+}
+
+void FinishAllCaptureScreenshot ()
+{
+#if CAPTURE_SCREENSHOT_THREAD
+ gCaptureScreenshotThread.WaitForExit();
+#endif
+}
+
+struct WriteImageAsync
+{
+ std::string path;
+ Image* image;
+};
+
+void* WriteImageAsyncThread (void* data)
+{
+ WriteImageAsync* async = static_cast<WriteImageAsync*> (data);
+
+#if UNITY_XENON
+ async->image->ReformatImage( async->image->GetWidth(), async->image->GetHeight(), kTexFormatRGBA32 );
+ if (!ConvertImageToPNGFileXbox360 (*async->image, async->path.c_str()))
+ {
+ ErrorString( "Failed to store screen shot" );
+ }
+#elif UNITY_PS3
+ async->image->ReformatImage( async->image->GetWidth(), async->image->GetHeight(), kTexFormatRGBA32 );
+ if (!ConvertImageToPNGFilePS3 (*async->image, async->path.c_str()))
+ {
+ ErrorString( "Failed to store screen shot" );
+ }
+#elif UNITY_WII
+ async->image->ReformatImage( async->image->GetWidth(), async->image->GetHeight(), kTexFormatRGBA32 );
+
+ int len;
+ UInt8* pngData = stbi_write_png_to_mem(
+ async->image->GetImageData(),
+ async->image->GetRowBytes(),
+ async->image->GetWidth(),
+ async->image->GetHeight(),
+ async->image->GetRowBytes() / async->image->GetWidth(), &len);
+ bool success = pngData != NULL;
+ if (success)
+ {
+ #if ENABLE_HIO2
+ success = wii::hio2::SendFile(async->path.c_str(), pngData, len);
+ #else
+ success = false;
+ #endif
+ free(pngData);
+ }
+
+ if (!success)
+ {
+ ErrorString(Format("Failed to write screenshot to path '%s', (remember this path is relative to *.mcp file)", async->path.c_str()).c_str());
+ }
+#elif ENABLE_PNG_JPG
+ async->image->ReformatImage( async->image->GetWidth(), async->image->GetHeight(), kTexFormatRGB24 );
+ if (!ConvertImageToPNGFile (*async->image, async->path))
+ {
+ ErrorString( "Failed to store screen shot" );
+ }
+#else
+ ErrorString("Cannot create pngs");
+#endif
+ delete async->image;
+ delete async;
+
+ return NULL;
+}
+
+
+// --------------------------------------------------------------------------
+
+
+struct SavedCameraData {
+ PPtr<Camera> camera;
+ Rectf viewportRect;
+};
+typedef dynamic_array<SavedCameraData> SavedCameras;
+
+struct SavedTextureData {
+ PPtr<Texture> texture;
+ float mipBias;
+};
+typedef dynamic_array<SavedTextureData> SavedTextures;
+
+
+static void SaveCameraParams (RenderManager::CameraContainer& cameras, SavedCameras& outCameras)
+{
+ outCameras.clear();
+ for (RenderManager::CameraContainer::iterator camit = cameras.begin(); camit != cameras.end(); ++camit)
+ {
+ Camera* cam = *camit;
+ if (!cam)
+ continue;
+ SavedCameraData data;
+ data.camera = cam;
+ data.viewportRect = cam->GetNormalizedViewportRect();
+ outCameras.push_back (data);
+ }
+}
+
+static void RestoreCameraParams (SavedCameras& cameras)
+{
+ for (SavedCameras::iterator camit = cameras.begin(); camit != cameras.end(); ++camit)
+ {
+ SavedCameraData& data = *camit;
+ Camera* cam = data.camera;
+ if (!cam)
+ continue;
+ cam->SetNormalizedViewportRect (data.viewportRect);
+ cam->ResetProjectionMatrix (); ///@TODO
+ }
+}
+
+
+static void SaveTextureParams (float mipBias, SavedTextures& outTextures, int& minAniso, int& maxAniso)
+{
+ vector<Texture*> objects;
+ Object::FindObjectsOfType (&objects);
+
+ outTextures.clear();
+ outTextures.reserve (objects.size());
+
+ TextureSettings::GetAnisoLimits (minAniso, maxAniso);
+ TextureSettings::SetAnisoLimits (16, 16);
+
+ for (size_t i=0;i<objects.size ();i++)
+ {
+ Texture* t = objects[i];
+ SavedTextureData data;
+ data.texture = t;
+ data.mipBias = t->GetSettings().m_MipBias;
+ outTextures.push_back (data);
+
+ if (t->HasMipMap())
+ t->GetSettings().m_MipBias += mipBias;
+ t->ApplySettings ();
+ }
+}
+
+static void RestoreTextureParams (SavedTextures& textures, int minAniso, int maxAniso)
+{
+ TextureSettings::SetAnisoLimits (minAniso, maxAniso);
+
+ for (SavedTextures::iterator it = textures.begin(); it != textures.end(); ++it)
+ {
+ SavedTextureData& data = *it;
+ Texture* t = data.texture;
+ if (!t)
+ continue;
+ t->GetSettings().m_MipBias = data.mipBias;
+ t->ApplySettings ();
+ }
+}
+
+
+static void ShiftedCameraProjections (SavedCameras& cameras, int superSize, int x, int y, int screenWidth, int screenHeight)
+{
+ for (SavedCameras::iterator camit = cameras.begin(); camit != cameras.end(); ++camit)
+ {
+ SavedCameraData& data = *camit;
+ Camera* cam = data.camera;
+ if (!cam)
+ continue;
+ cam->ResetProjectionMatrix();
+ Rectf camRect = cam->GetScreenViewportRect();
+ float dx = (float(x)/float(superSize)-0.5f) / (camRect.width*0.5f);
+ float dy = (float(y)/float(superSize)-0.5f) / (camRect.height*0.5f);
+ Matrix4x4f proj = cam->GetProjectionMatrix ();
+ if (cam->GetOrthographic())
+ {
+ proj.Get(0,3) -= dx;
+ proj.Get(1,3) -= dy;
+ }
+ else
+ {
+ proj.Get(0,2) += dx;
+ proj.Get(1,2) += dy;
+ }
+ cam->SetProjectionMatrix (proj);
+ }
+}
+
+static void InterleaveImage (const Image& screenImage, Image& finalImage, int xo, int yo, int superSize)
+{
+ Assert (screenImage.GetWidth() * superSize == finalImage.GetWidth());
+ Assert (screenImage.GetHeight() * superSize == finalImage.GetHeight());
+ Assert (xo >= 0 && xo < superSize);
+ Assert (yo >= 0 && yo < superSize);
+
+ const UInt32* srcData = reinterpret_cast<const UInt32*>(screenImage.GetImageData());
+ UInt32* dstData = reinterpret_cast<UInt32*>(finalImage.GetImageData());
+ const int width = screenImage.GetWidth();
+ const int height = screenImage.GetHeight();
+ dstData += (width*superSize) * yo + xo;
+ for (int y = 0; y < height; ++y)
+ {
+ for (int x = 0; x < width; ++x)
+ {
+ dstData[x*superSize] = srcData[x];
+ }
+ dstData += width*superSize*superSize;
+ srcData += width;
+ }
+}
+
+
+// --------------------------------------------------------------------------
+
+
+static Image* DoCaptureScreenshot (int superSize)
+{
+ GfxDevice& device = GetGfxDevice();
+ RenderManager& renderMgr = GetRenderManager();
+
+#if UNITY_WP8
+ /* In WP8, D3D11 seems to always be in portrait orientation, despite program orientation,
+ * so horizontal and vertical axis swap is needed */
+ Rectf rc = renderMgr.GetWindowRect();
+ if (GetScreenManager().GetScreenOrientation() == ScreenOrientation::kLandscapeLeft || GetScreenManager().GetScreenOrientation() == ScreenOrientation::kLandscapeRight)
+ {
+ std::swap(rc.x, rc.y);
+ std::swap(rc.width, rc.height);
+ }
+#else
+ const Rectf rc = renderMgr.GetWindowRect();
+#endif
+
+ const int screenWidth = int(rc.Width ());
+ const int screenHeight = int(rc.Height ());
+
+ Image* finalImage = NULL;
+
+ if (superSize > 1)
+ {
+ finalImage = new Image (screenWidth*superSize, screenHeight*superSize, kTexFormatRGBA32);
+ if (!finalImage)
+ return NULL;
+
+ Image screenImage (screenWidth, screenHeight, kTexFormatRGBA32);
+
+ RenderManager::CameraContainer& cameras = renderMgr.GetOnscreenCameras ();
+ SavedCameras cameraData;
+ SaveCameraParams (cameras, cameraData);
+
+ float mipBias = -Log2(superSize) - 1.0f;
+ SavedTextures textureData;
+ int savedMinAniso, savedMaxAniso;
+ SaveTextureParams (mipBias, textureData, savedMinAniso, savedMaxAniso);
+
+ for (int y = 0; y < superSize; ++y)
+ {
+ for (int x = 0; x < superSize; ++x)
+ {
+ ShiftedCameraProjections (cameraData, superSize, x, y, screenWidth, screenHeight);
+ renderMgr.RenderCameras ();
+ #if ENABLE_UNITYGUI
+ GetGUIManager().Repaint ();
+ #endif
+ device.CaptureScreenshot (int(rc.x), int(rc.y), screenWidth, screenHeight, screenImage.GetImageData());
+ InterleaveImage (screenImage, *finalImage, x, y, superSize);
+ }
+ }
+
+ RestoreTextureParams (textureData, savedMinAniso, savedMaxAniso);
+ RestoreCameraParams (cameraData);
+ }
+ else
+ {
+ finalImage = new Image (screenWidth, screenHeight, kTexFormatRGBA32);
+ if (!finalImage)
+ return NULL;
+ bool ok = device.CaptureScreenshot (int(rc.x), int(rc.y), screenWidth, screenHeight, finalImage->GetImageData());
+ if (!ok)
+ {
+ delete finalImage;
+ return NULL;
+ }
+ }
+ return finalImage;
+}
+
+bool IsScreenshotQueued()
+{
+ return (gCaptureScreenshotPath!= NULL);
+}
+
+void CaptureScreenshotImmediate(string filePath, int x, int y, int width, int height)
+{
+ Rectf rc = GetRenderManager().GetWindowRect();
+ Image* buffer = new Image (width, height, kTexFormatRGBA32);
+ bool ok = GetGfxDevice().CaptureScreenshot( rc.x + x, rc.y + y, width, height, buffer->GetImageData() );
+ if( ok )
+ {
+ WriteImageAsync* asyncData = new WriteImageAsync();
+ asyncData->path = filePath;
+ asyncData->image = buffer;
+ WriteImageAsyncThread(asyncData);
+ }
+ else
+ {
+ delete buffer;
+ ErrorString( "Failed to capture screen shot" );
+ }
+}
+
+void UpdateCaptureScreenshot ()
+{
+ if (!gCaptureScreenshotPath)
+ return;
+
+ #if !UNITY_FLASH
+ Image* buffer = DoCaptureScreenshot (s_CaptureSuperSize);
+ if (buffer)
+ {
+ WriteImageAsync* asyncData = new WriteImageAsync();
+ asyncData->path = gCaptureScreenshotPath;
+ asyncData->image = buffer;
+
+ #if CAPTURE_SCREENSHOT_THREAD
+ gCaptureScreenshotThread.WaitForExit();
+ gCaptureScreenshotThread.Run(WriteImageAsyncThread, asyncData);
+ #else
+ WriteImageAsyncThread(asyncData);
+ #endif
+ }
+ else
+ {
+ delete buffer;
+ ErrorString( "Failed to capture screen shot" );
+ }
+
+ #else
+ Rectf rc = GetRenderManager().GetWindowRect();
+ DBG_LOG_MOLEHILL("\nMH: capture screenshot\n\n");
+ GetGfxDevice().CaptureScreenshot (0,0,0,0,NULL); // arguments don't matter here, just needs to ensure clear is done
+ AS3Handle ba = Ext_Stage3D_CaptureScreenshot(rc.Width(),rc.Height());
+
+ #if ENABLE_PLAYERCONNECTION
+ int baLength = Ext_Flash_GetByteArrayLength(ba);
+ void* buffer = malloc(baLength);
+ Ext_Flash_ReadByteArray(ba,buffer,baLength);
+ TransferFileOverPlayerConnection (gCaptureScreenshotPath, buffer, baLength);
+ free (buffer);
+ #endif
+
+ //Let go of bytearray.
+ //Ext_MarshalMap_Release(ba);//TODO RH : this byterarray shouldn't be in the marshalmap anyway.
+ #endif
+
+ free((void*)gCaptureScreenshotPath);
+ gCaptureScreenshotPath = NULL;
+}
+
+#endif
diff --git a/Runtime/Misc/CaptureScreenshot.h b/Runtime/Misc/CaptureScreenshot.h
new file mode 100644
index 0000000..acf318c
--- /dev/null
+++ b/Runtime/Misc/CaptureScreenshot.h
@@ -0,0 +1,30 @@
+#ifndef _CAPTURESCREENSHOT_H
+#define _CAPTURESCREENSHOT_H
+
+#include "Configuration/UnityConfigure.h"
+
+#undef CAPTURE_SCREENSHOT_AVAILABLE
+
+// In web player we never capture screenshots. Don't even compile the code in (saves whole pnglib!)
+#if (WEBPLUG && !SUPPORT_REPRODUCE_LOG && !UNITY_PEPPER)
+#define CAPTURE_SCREENSHOT_AVAILABLE 0
+#else
+#define CAPTURE_SCREENSHOT_AVAILABLE 1
+// Player connection might be used to transfer screenshots and async is a pain there
+#define CAPTURE_SCREENSHOT_THREAD (SUPPORT_THREADS && !UNITY_WII && !ENABLE_PLAYERCONNECTION)
+#endif
+
+
+#if CAPTURE_SCREENSHOT_AVAILABLE
+
+#include <string>
+
+void QueueScreenshot (const std::string& path, int superSize);
+bool IsScreenshotQueued ();
+void UpdateCaptureScreenshot ();
+void FinishAllCaptureScreenshot ();
+
+#endif
+
+
+#endif
diff --git a/Runtime/Misc/ComponentRequirement.cpp b/Runtime/Misc/ComponentRequirement.cpp
new file mode 100644
index 0000000..70ac2f8
--- /dev/null
+++ b/Runtime/Misc/ComponentRequirement.cpp
@@ -0,0 +1,451 @@
+#include "UnityPrefix.h"
+#include "ComponentRequirement.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+#include <algorithm>
+
+#if UNITY_EDITOR
+#include "Editor/Src/LicenseInfo.h"
+#endif
+
+#if !UNITY_WIN && !UNITY_XENON && !UNITY_PS3 && !UNITY_ANDROID
+#pragma optimization_level 0
+#endif
+
+typedef std::map<int, vector_set<int> > ClassIDToClassIDsMap;
+static ClassIDToClassIDsMap* gRequiredClasses;
+static ClassIDToClassIDsMap* gConflictingClasses;
+typedef std::set<int> ClassIDSet;
+static ClassIDSet* gAllowsMultipleInclusion;
+static ClassIDSet* gDoesComponentAllowReplacement;
+#if UNITY_EDITOR
+static ComponentsHierarchy* gComponentHierarchy;
+#endif
+
+namespace ComponentRequirements
+{
+ void StaticInitialize()
+ {
+ gRequiredClasses = UNITY_NEW(ClassIDToClassIDsMap,kMemResource);
+ gConflictingClasses = UNITY_NEW(ClassIDToClassIDsMap,kMemResource);
+ gAllowsMultipleInclusion = UNITY_NEW(ClassIDSet,kMemResource);
+ gDoesComponentAllowReplacement = UNITY_NEW(ClassIDSet,kMemResource);
+#if UNITY_EDITOR
+ gComponentHierarchy = UNITY_NEW(ComponentsHierarchy,kMemResource);
+#endif
+ }
+ void StaticDestroy()
+ {
+ UNITY_DELETE(gRequiredClasses,kMemResource);
+ UNITY_DELETE(gConflictingClasses,kMemResource);
+ UNITY_DELETE(gAllowsMultipleInclusion,kMemResource);
+ UNITY_DELETE(gDoesComponentAllowReplacement,kMemResource);
+#if UNITY_EDITOR
+ UNITY_DELETE(gComponentHierarchy,kMemResource);
+#endif
+ }
+}
+static RegisterRuntimeInitializeAndCleanup s_ComponentRequirementsCallbacks(ComponentRequirements::StaticInitialize, ComponentRequirements::StaticDestroy);
+
+const vector_set<int>& FindRequiredComponentsForComponent (int componentClassID)
+{
+ InitComponentRequirements ();
+ return (*gRequiredClasses)[componentClassID];
+}
+
+void FindAllRequiredComponentsRecursive (int componentClassID, vector_set<int>& results)
+{
+ // Already added
+ if (!results.insert(componentClassID).second)
+ return;
+
+ const vector_set<int>& thisClassResult = FindRequiredComponentsForComponent (componentClassID);
+ for (int i=0;i<thisClassResult.size();i++)
+ FindAllRequiredComponentsRecursive(thisClassResult[i], results);
+}
+
+const vector_set<int>& FindConflictingComponents (int classID)
+{
+ InitComponentRequirements ();
+ return (*gConflictingClasses)[classID];
+}
+
+
+int GetAllowComponentReplacementClass (int classID)
+{
+ while (classID != ClassID (Object))
+ {
+ if (gDoesComponentAllowReplacement->count (classID))
+ return classID;
+
+ classID = Object::GetSuperClassID (classID);
+ }
+ return -1;
+}
+
+bool DoesComponentAllowMultipleInclusion (int componentClassID)
+{
+ InitComponentRequirements ();
+ return gAllowsMultipleInclusion->find(componentClassID) != gAllowsMultipleInclusion->end();
+}
+#if UNITY_EDITOR
+const ComponentsHierarchy& GetComponentsHierarchy ()
+{
+ InitComponentRequirements ();
+ return (*gComponentHierarchy);
+}
+#endif
+
+static void AddRequiredClassIMPL (const string& aClass, const string& requiredClass)
+{
+ int classID = Object::StringToClassID (aClass);
+ int requiredClassID = Object::StringToClassID (requiredClass);
+#if !SUPPORTS_NATIVE_CODE_STRIPPING
+ Assert (classID != -1 && requiredClassID != -1);
+#endif
+
+ std::vector<SInt32> derivedClasses;
+ Object::FindAllDerivedClasses (classID, &derivedClasses, false);
+
+ for (std::vector<SInt32>::const_iterator i=derivedClasses.begin ();i!=derivedClasses.end ();++i)
+ (*gRequiredClasses)[*i].insert (requiredClassID);
+
+ (*gRequiredClasses)[classID].insert (requiredClassID);
+}
+
+static void AddConflictingClassIMPL (const string& aClass, const string& otherClass)
+{
+ int classID = Object::StringToClassID (aClass);
+ int otherClassID = Object::StringToClassID (otherClass);
+ if (classID == -1 || otherClassID == -1) // might have been stripped out
+ return;
+
+ std::vector<SInt32> derivedClasses;
+ Object::FindAllDerivedClasses (classID, &derivedClasses, false);
+
+ for (std::vector<SInt32>::const_iterator i=derivedClasses.begin ();i!=derivedClasses.end ();++i)
+ (*gConflictingClasses)[*i].insert (otherClassID);
+ (*gConflictingClasses)[classID].insert (otherClassID);
+}
+
+
+#define AddRequiredClass(c, r) AddRequiredClassIMPL(#c, #r)
+#define AddConflictingClass(c, r) AddConflictingClassIMPL(#c, #r)
+
+#define AddToComponentHierarchy(className) \
+{ \
+ int classID = Object::StringToClassID (#className); \
+ AssertIf (classID == 0 || classID == -1 || gComponentHierarchy->empty ()); \
+ gComponentHierarchy->back ().second.push_back (GOComponentDescription (#className, classID)); \
+ components.erase (classID); \
+}
+
+#define AddGroup(s) gComponentHierarchy->push_back (std::make_pair (s, std::vector<GOComponentDescription> ()));
+#define AddSeparator() { gComponentHierarchy->back ().second.push_back (GOComponentDescription ("", 0)); }
+#define AddAllowsMultipleInclusion(c) { int classID = Object::StringToClassID(#c); gAllowsMultipleInclusion->insert(classID); }
+#define AddAllowComponentReplacement(c) { int classID = Object::StringToClassID(#c); AssertIf (classID == -1); gDoesComponentAllowReplacement->insert (classID); }
+
+
+
+void InitComponentRequirements ()
+{
+ static bool gIsInitialized = false;
+ if (gIsInitialized)
+ return;
+ gIsInitialized = true;
+
+ gRequiredClasses->clear ();
+ gConflictingClasses->clear ();
+ gAllowsMultipleInclusion->clear ();
+ std::vector<SInt32> componentsVec;
+ Object::FindAllDerivedClasses (Object::StringToClassID ("Component"), &componentsVec, true);
+ ClassIDSet components (componentsVec.begin (), componentsVec.end ());
+
+ ////////////////// Setup component requirements
+ AddRequiredClass (Renderer, Transform);
+ AddRequiredClass (MeshFilter, Transform);
+ AddRequiredClass (ParticleAnimator, Transform);
+ AddRequiredClass (EllipsoidParticleEmitter, Transform);
+ AddRequiredClass (WorldParticleCollider, Transform);
+ AddRequiredClass (ParticleSystem, Transform);
+ AddRequiredClass (ParticleSystemRenderer, Transform);
+ AddRequiredClass (Camera, Transform);
+ AddRequiredClass (Light, Transform);
+
+#if ENABLE_SPRITES
+ AddRequiredClass (SpriteRenderer, Transform);
+ AddConflictingClass (MeshFilter, SpriteRenderer);
+ AddConflictingClass (MeshRenderer, SpriteRenderer);
+ AddConflictingClass (SpriteRenderer, MeshFilter);
+ AddConflictingClass (SpriteRenderer, MeshRenderer);
+#endif // ENABLE_SPRITES
+
+#if ENABLE_PHYSICS
+ AddRequiredClass (Rigidbody, Transform);
+ // Do not want 2D physics components on physics objects
+ #if ENABLE_2D_PHYSICS
+ AddConflictingClass (Rigidbody, Rigidbody2D);
+ AddConflictingClass (Rigidbody, Collider2D);
+ AddConflictingClass (Rigidbody, Joint2D);
+ AddConflictingClass (Collider, Rigidbody2D);
+ AddConflictingClass (Collider, Collider2D);
+ AddConflictingClass (Collider, Joint2D);
+ AddConflictingClass (Joint, Rigidbody2D);
+ AddConflictingClass (Joint, Collider2D);
+ AddConflictingClass (Joint, Joint2D);
+ AddConflictingClass (ConstantForce, Rigidbody2D);
+ AddConflictingClass (ConstantForce, Collider2D);
+ AddConflictingClass (ConstantForce, Joint2D);
+
+ #endif // #if ENABLE_2D_PHYSICS
+#endif // #if ENABLE_PHYSICS
+
+#if ENABLE_2D_PHYSICS
+ AddRequiredClass (Rigidbody2D, Transform);
+ AddRequiredClass (Collider2D, Transform);
+ AddRequiredClass (Joint2D, Transform);
+ AddAllowsMultipleInclusion (SpringJoint2D);
+ AddAllowsMultipleInclusion (DistanceJoint2D);
+ AddAllowsMultipleInclusion (HingeJoint2D);
+ AddRequiredClass (Joint2D, Rigidbody2D);
+
+ // Do not want physics components on 2D physics objects
+ #if ENABLE_PHYSICS
+ AddConflictingClass (Rigidbody2D, Rigidbody);
+ AddConflictingClass (Rigidbody2D, Collider);
+ AddConflictingClass (Rigidbody2D, Joint);
+ AddConflictingClass (Rigidbody2D, ConstantForce);
+ AddConflictingClass (Collider2D, Rigidbody);
+ AddConflictingClass (Collider2D, Collider);
+ AddConflictingClass (Collider2D, Joint);
+ AddConflictingClass (Collider2D, ConstantForce);
+ AddConflictingClass (Joint2D, Rigidbody);
+ AddConflictingClass (Joint2D, Collider);
+ AddConflictingClass (Joint2D, Joint);
+ AddConflictingClass (Joint2D, ConstantForce);
+
+ #endif // #if ENABLE_PHYSICS
+#endif // #if ENABLE_2D_PHYSICS
+
+ AddRequiredClass (GUIElement, Transform);
+#if ENABLE_AUDIO
+ AddRequiredClass (AudioSource, Transform);
+ AddRequiredClass (AudioListener, Transform);
+#endif
+#if ENABLE_AUDIO_FMOD
+ AddRequiredClass (AudioReverbZone, Transform);
+ AddRequiredClass (AudioLowPassFilter, AudioBehaviour);
+ AddRequiredClass (AudioEchoFilter, AudioBehaviour);
+ AddRequiredClass (AudioDistortionFilter, AudioBehaviour);
+ AddRequiredClass (AudioReverbFilter, AudioBehaviour);
+ AddRequiredClass (AudioHighPassFilter, AudioBehaviour);
+ AddRequiredClass (AudioChorusFilter, AudioBehaviour);
+#endif
+ AddRequiredClass (TextMesh, Transform);
+
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_2_a1))
+ AddRequiredClass (TextMesh, MeshRenderer);
+
+#if ENABLE_PHYSICS
+ AddRequiredClass (Collider, Transform);
+ AddRequiredClass (Joint, Rigidbody);
+ AddRequiredClass (ConstantForce, Rigidbody);
+#endif
+
+ AddRequiredClass (FlareLayer, Camera);
+ AddRequiredClass (GUILayer, Camera);
+ AddRequiredClass (Halo, Transform);
+
+#if ENABLE_CLOTH
+ AddRequiredClass (Cloth, Transform);
+ AddRequiredClass (ClothRenderer, InteractiveCloth);
+ AddRequiredClass (SkinnedCloth, SkinnedMeshRenderer);
+#endif
+
+ //////////////////// Setup allows multiple inclusion
+ AddAllowsMultipleInclusion (HingeJoint);
+ AddAllowsMultipleInclusion (FixedJoint);
+ AddAllowsMultipleInclusion (CharacterJoint);
+ AddAllowsMultipleInclusion (ConfigurableJoint);
+ AddAllowsMultipleInclusion (SpringJoint);
+
+ AddAllowsMultipleInclusion (AudioSource);
+ AddAllowsMultipleInclusion (OffMeshLink);
+
+ AddAllowsMultipleInclusion (Skybox)
+ AddAllowsMultipleInclusion (MonoBehaviour)
+
+ AddAllowsMultipleInclusion (NetworkView)
+
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1))
+ {
+ AddAllowsMultipleInclusion (BoxCollider);
+ AddAllowsMultipleInclusion (SphereCollider);
+ AddAllowsMultipleInclusion (CapsuleCollider);
+ AddAllowsMultipleInclusion (MeshCollider);
+ }
+
+#if ENABLE_2D_PHYSICS
+ AddAllowsMultipleInclusion (CircleCollider2D);
+ AddAllowsMultipleInclusion (BoxCollider2D);
+ AddAllowsMultipleInclusion (EdgeCollider2D);
+ AddAllowsMultipleInclusion (PolygonCollider2D);
+ #if ENABLE_SPRITECOLLIDER
+ AddAllowsMultipleInclusion (SpriteCollider2D);
+ #endif
+#endif // #if ENABLE_2D_PHYSICS
+
+ #if UNITY_EDITOR
+ gComponentHierarchy->clear ();
+
+
+ ////////////////// Setup component hierarchy / Component menu
+ AddGroup ("Mesh")
+ AddToComponentHierarchy (MeshFilter);
+ AddToComponentHierarchy (TextMesh);
+ AddSeparator ();
+ AddToComponentHierarchy (MeshRenderer);
+
+ AddGroup ("Effects")
+ AddToComponentHierarchy (ParticleSystem);
+ AddToComponentHierarchy (TrailRenderer);
+ AddToComponentHierarchy (LineRenderer);
+ AddToComponentHierarchy (LensFlare);
+ AddToComponentHierarchy (Halo);
+ AddToComponentHierarchy (Projector);
+ AddSeparator ();
+ AddGroup ("Effects/Legacy Particles")
+ AddToComponentHierarchy (EllipsoidParticleEmitter);
+ AddToComponentHierarchy (MeshParticleEmitter);
+ AddSeparator ();
+ AddToComponentHierarchy (ParticleAnimator);
+ AddToComponentHierarchy (WorldParticleCollider);
+ AddSeparator ();
+ AddToComponentHierarchy (ParticleRenderer);
+
+ AddGroup ("Physics")
+ AddToComponentHierarchy (Rigidbody);
+ AddToComponentHierarchy (CharacterController);
+ AddSeparator ();
+ AddToComponentHierarchy (BoxCollider);
+ AddToComponentHierarchy (SphereCollider);
+ AddToComponentHierarchy (CapsuleCollider);
+ AddToComponentHierarchy (MeshCollider);
+ AddToComponentHierarchy (WheelCollider);
+ AddToComponentHierarchy (TerrainCollider);
+ AddSeparator ();
+ AddToComponentHierarchy (InteractiveCloth);
+ AddToComponentHierarchy (SkinnedCloth);
+ AddToComponentHierarchy (ClothRenderer);
+ AddSeparator ();
+ AddToComponentHierarchy (HingeJoint);
+ AddToComponentHierarchy (FixedJoint);
+ AddToComponentHierarchy (SpringJoint);
+ AddToComponentHierarchy (CharacterJoint);
+ AddToComponentHierarchy (ConfigurableJoint);
+ AddSeparator ();
+ AddToComponentHierarchy (ConstantForce);
+
+ #if ENABLE_2D_PHYSICS
+ AddGroup ("Physics 2D");
+ AddToComponentHierarchy (Rigidbody2D);
+ AddSeparator ();
+ AddToComponentHierarchy (CircleCollider2D);
+ AddToComponentHierarchy (BoxCollider2D);
+ AddToComponentHierarchy (EdgeCollider2D);
+ AddToComponentHierarchy (PolygonCollider2D);
+ #if ENABLE_SPRITECOLLIDER
+ AddToComponentHierarchy (SpriteCollider2D);
+ #endif
+ AddSeparator ();
+ AddToComponentHierarchy (SpringJoint2D);
+ AddToComponentHierarchy (DistanceJoint2D);
+ AddToComponentHierarchy (HingeJoint2D);
+ AddToComponentHierarchy (SliderJoint2D);
+ #endif // #if ENABLE_2D_PHYSICS
+
+ AddGroup ("Navigation")
+ AddToComponentHierarchy (NavMeshAgent);
+ AddToComponentHierarchy (OffMeshLink);
+ AddToComponentHierarchy (NavMeshObstacle);
+
+ AddGroup ("Audio")
+ AddToComponentHierarchy (AudioListener);
+ AddToComponentHierarchy (AudioSource);
+ AddToComponentHierarchy (AudioReverbZone);
+
+ if (LicenseInfo::Flag (lf_pro_version)
+ || LicenseInfo::Flag (lf_iphone_pro)
+ || LicenseInfo::Flag (lf_android_pro)
+ || LicenseInfo::Flag (lf_bb10_pro)
+ || LicenseInfo::Flag (lf_tizen_pro))
+ {
+ AddSeparator ();
+ AddToComponentHierarchy (AudioLowPassFilter);
+ AddToComponentHierarchy (AudioHighPassFilter);
+ AddToComponentHierarchy (AudioEchoFilter);
+ AddToComponentHierarchy (AudioDistortionFilter);
+ AddToComponentHierarchy (AudioReverbFilter);
+ AddToComponentHierarchy (AudioChorusFilter);
+ }
+
+ AddGroup ("Rendering")
+ // Camera and components that need camera
+ AddToComponentHierarchy (Camera);
+ AddToComponentHierarchy (Skybox);
+ AddToComponentHierarchy (FlareLayer);
+ AddToComponentHierarchy (GUILayer);
+ AddSeparator ();
+ // Lights related
+ AddToComponentHierarchy (Light);
+ AddToComponentHierarchy (LightProbeGroup);
+ AddSeparator ();
+ // Optimization
+ AddToComponentHierarchy (OcclusionArea);
+ AddToComponentHierarchy (OcclusionPortal);
+ AddToComponentHierarchy (LODGroup);
+ AddSeparator ();
+ // 2D related
+#if ENABLE_SPRITES
+ AddToComponentHierarchy (SpriteRenderer);
+ AddSeparator ();
+#endif
+ // Old old GUI
+ AddToComponentHierarchy (GUITexture);
+ AddToComponentHierarchy (GUIText);
+
+ #define RemoveComponentFromMisc(x) { int removeComponentClassID = Object::StringToClassID (#x); AssertIf (removeComponentClassID == -1); components.erase (removeComponentClassID); }
+ RemoveComponentFromMisc (Transform);
+ RemoveComponentFromMisc (MonoBehaviour);
+ RemoveComponentFromMisc (Component);
+ RemoveComponentFromMisc (Pipeline);
+ RemoveComponentFromMisc (HaloLayer);
+
+ // Although added to a menu above, these would still appear under
+ // misc for non-pro licenses if not explicitly removed here.
+ RemoveComponentFromMisc (AudioLowPassFilter);
+ RemoveComponentFromMisc (AudioHighPassFilter);
+ RemoveComponentFromMisc (AudioEchoFilter);
+ RemoveComponentFromMisc (AudioDistortionFilter);
+ RemoveComponentFromMisc (AudioReverbFilter);
+ RemoveComponentFromMisc (AudioChorusFilter);
+
+ RemoveComponentFromMisc (RaycastCollider); // Deprecated since 3.0
+ RemoveComponentFromMisc (SkinnedMeshRenderer);
+ RemoveComponentFromMisc (Tree);
+ RemoveComponentFromMisc (ParticleSystemRenderer);
+
+
+ // Add others to Miscellaneous
+ const string misc = "Miscellaneous";
+ gComponentHierarchy->push_back (std::make_pair (misc, std::vector<GOComponentDescription> ()));
+ for (ClassIDSet::const_iterator i = components.begin ();i != components.end ();i++)
+ gComponentHierarchy->back ().second.push_back (GOComponentDescription (Object::ClassIDToString (*i), *i));
+ #endif
+
+}
diff --git a/Runtime/Misc/ComponentRequirement.h b/Runtime/Misc/ComponentRequirement.h
new file mode 100644
index 0000000..50f3dde
--- /dev/null
+++ b/Runtime/Misc/ComponentRequirement.h
@@ -0,0 +1,33 @@
+#ifndef COMPONENTREQUIREMENT_H
+#define COMPONENTREQUIREMENT_H
+#include "Runtime/Utilities/vector_set.h"
+#include <string>
+#include <map>
+#include <list>
+#include <vector>
+
+// Returns all components that are required for a component
+// of componentClassID to run.
+const vector_set<int>& FindRequiredComponentsForComponent (int componentClassID);
+void FindAllRequiredComponentsRecursive (int componentClassID, vector_set<int>& results);
+const vector_set<int>& FindConflictingComponents (int classID);
+
+// Can the component with componentClassID, be more than once in a gameobject?
+bool DoesComponentAllowMultipleInclusion (int componentClassID);
+
+struct GOComponentDescription
+{
+ int classID;
+ std::string name;
+ GOComponentDescription (const std::string& inName, int inClassID) { name = inName; classID = inClassID; }
+};
+
+typedef std::list<std::pair<std::string, std::vector<GOComponentDescription> > > ComponentsHierarchy;
+// Returns a sorted hierarchy of components
+const ComponentsHierarchy& GetComponentsHierarchy ();
+
+int GetAllowComponentReplacementClass (int classID);
+
+void InitComponentRequirements ();
+
+#endif
diff --git a/Runtime/Misc/DebugUtility.cpp b/Runtime/Misc/DebugUtility.cpp
new file mode 100644
index 0000000..881c271
--- /dev/null
+++ b/Runtime/Misc/DebugUtility.cpp
@@ -0,0 +1,156 @@
+#include "UnityPrefix.h"
+
+#if UNITY_EDITOR
+
+#include "DebugUtility.h"
+#include "Editor/Src/Gizmos/GizmoUtil.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Editor/Src/Gizmos/GizmoManager.h"
+#include "Editor/Src/Gizmos/GizmoRenderer.h"
+#include <vector>
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+
+using namespace std;
+
+class DebugLineRenderer
+{
+public:
+
+ void AddLine (const Vector3f& v0, const Vector3f& v1, const ColorRGBAf& color, double durationSeconds, bool depthTest)
+ {
+ // We do not support timed debug lines in edit mode
+ if (!IsWorldPlaying ())
+ durationSeconds = 0.0;
+
+ Line l;
+ l.start = v0;
+ l.end = v1;
+ l.color = color;
+ l.depthTest = depthTest;
+ if (durationSeconds > 0.0)
+ {
+ // We use game time (CurTime) to be able to pause game and investigate debug lines.
+ l.removeTime = GetTimeManager ().GetCurTime () + durationSeconds;
+ m_TimedLines.push_back (l);
+ }
+ else
+ {
+ l.removeTime = -1.0;
+ if (GetTimeManager ().IsUsingFixedTimeStep ())
+ m_FixedStepLines.push_back (l);
+ else
+ m_DynamicStepLines.push_back (l);
+ }
+ }
+
+ void Clear ()
+ {
+ if (GetTimeManager ().IsUsingFixedTimeStep ())
+ {
+ m_FixedStepLines.clear ();
+ }
+ else
+ {
+ m_DynamicStepLines.clear ();
+
+ if (!m_TimedLines.empty())
+ {
+ float curTime = GetTimeManager ().GetCurTime ();
+ list<Line>::iterator it = m_TimedLines.begin();
+ while (it != m_TimedLines.end())
+ {
+ if (curTime >= it->removeTime)
+ it = m_TimedLines.erase (it);
+ else
+ it++;
+ }
+ }
+ }
+ }
+
+ void ClearAll ()
+ {
+ m_DynamicStepLines.clear ();
+ m_FixedStepLines.clear ();
+ m_TimedLines.clear ();
+ }
+
+ void Draw () const
+ {
+ for (unsigned i=0; i<m_FixedStepLines.size(); ++i)
+ {
+ const Line& line = m_FixedStepLines[i];
+ gizmos::g_GizmoColor = line.color;
+ DrawLine(line.start, line.end, line.depthTest);
+ }
+ for (unsigned i=0; i<m_DynamicStepLines.size(); ++i)
+ {
+ const Line& line = m_DynamicStepLines[i];
+ gizmos::g_GizmoColor = line.color;
+ DrawLine(line.start, line.end, line.depthTest);
+ }
+
+ for (list<Line>::const_iterator it = m_TimedLines.begin(); it != m_TimedLines.end(); it++)
+ {
+ const Line& line = *it;
+ gizmos::g_GizmoColor = line.color;
+ DrawLine(line.start, line.end, line.depthTest);
+ }
+ }
+ static DebugLineRenderer& Get ();
+ static void StaticDestroy();
+private:
+ struct Line
+ {
+ Vector3f start;
+ Vector3f end;
+ ColorRGBAf color;
+ double removeTime;
+ bool depthTest;
+ };
+ list<Line> m_TimedLines;
+ vector<Line> m_FixedStepLines;
+ vector<Line> m_DynamicStepLines;
+
+};
+
+static RegisterRuntimeInitializeAndCleanup gDebugLineRendererInstance(NULL, DebugLineRenderer::StaticDestroy);
+static DebugLineRenderer* g_DebugLineRenderer = NULL;
+
+void DebugLineRenderer::StaticDestroy()
+{
+ if(g_DebugLineRenderer)
+ UNITY_DELETE(g_DebugLineRenderer, kMemEditorUtility);
+}
+
+DebugLineRenderer& DebugLineRenderer::Get ()
+{
+ if(g_DebugLineRenderer == NULL)
+ g_DebugLineRenderer = UNITY_NEW_AS_ROOT(DebugLineRenderer,kMemEditorUtility, "Manager", "DebugLineRenderer");
+ return *g_DebugLineRenderer;
+}
+
+void ClearLines ()
+{
+ DebugLineRenderer::Get ().Clear ();
+}
+
+void ClearAllLines ()
+{
+ DebugLineRenderer::Get ().ClearAll ();
+}
+
+void DrawDebugLinesGizmo ()
+{
+ DebugLineRenderer::Get ().Draw ();
+}
+
+void DebugDrawLine (const Vector3f& p0, const Vector3f& p1, const ColorRGBAf& color, double durationSeconds /*=0.0*/, bool depthTest /*=true*/)
+{
+ DebugLineRenderer::Get ().AddLine (p0, p1, color, durationSeconds, depthTest);
+}
+
+#endif
diff --git a/Runtime/Misc/DebugUtility.h b/Runtime/Misc/DebugUtility.h
new file mode 100644
index 0000000..90d355c
--- /dev/null
+++ b/Runtime/Misc/DebugUtility.h
@@ -0,0 +1,30 @@
+#ifndef DEBUGUTILITY_H
+#define DEBUGUTILITY_H
+
+class Vector3f;
+class ColorRGBAf;
+class EditorExtension;
+
+#if GAMERELEASE
+
+//#define DebugDrawLine (a,b,c,d) ;
+//#define ClearLines () ;
+//#define PauseEditor () ;
+inline void DebugDrawLine (const Vector3f& p0, const Vector3f& p1, const ColorRGBAf& color, double durationSeconds, bool depthTest){}
+inline void ClearLines (){}
+inline void ClearAllLines (){}
+inline void PauseEditor (){}
+
+#else
+
+void DebugDrawLine (const Vector3f& p0, const Vector3f& p1, const ColorRGBAf& color, double durationSeconds, bool depthTest);
+void ClearLines ();
+void ClearAllLines ();
+void PauseEditor ();
+
+void DrawDebugLinesGizmo ();
+
+#endif
+
+
+#endif
diff --git a/Runtime/Misc/DeveloperConsole.cpp b/Runtime/Misc/DeveloperConsole.cpp
new file mode 100644
index 0000000..13def26
--- /dev/null
+++ b/Runtime/Misc/DeveloperConsole.cpp
@@ -0,0 +1,475 @@
+#include "UnityPrefix.h"
+#include "Runtime/Misc/DeveloperConsole.h"
+
+#if UNITY_HAS_DEVELOPER_CONSOLE
+
+#include "Runtime/IMGUI/GUILabel.h"
+#include "Runtime/IMGUI/GUIButton.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Graphics/ScreenManager.h"
+
+#include "Runtime/Threads/ThreadUtility.h"
+
+#include "Runtime/Misc/ReproductionLog.h"
+
+
+static DeveloperConsole* s_DeveloperConsole = 0;
+
+// This function performs the normal loggin operation with the initialized developer console
+void DeveloperConsole_HandleLogFunction (const std::string& condition, const std::string& stackTrace, int type)
+{
+ // Bas Smit and Elvis Alistar suggested to disable the developer console when we are in reproduction mode.
+ // [17:00:40] Bas Smit: I dont see the dev console adding value to the rig
+ // [17:00:57] Elvis Alistar: it actual makes the results very difficult to interpret
+ // [17:01:07] Elvis Alistar: as most of the runs will be different
+#if SUPPORT_REPRODUCE_LOG
+ if (GetReproduceMode() != kPlaybackReproduceLog)
+#endif
+ {
+ UnityMemoryBarrier();
+ if (s_DeveloperConsole)
+ s_DeveloperConsole->HandleLog(condition, stackTrace, static_cast<LogType>(type));
+ }
+}
+
+
+// If nothing is created, then CleanupDeveloperConsole () is a no-op
+void CleanupDeveloperConsole ()
+{
+ RegisterLogCallback(NULL, false);
+
+ DeveloperConsole* tmp = 0;
+ std::swap(s_DeveloperConsole, tmp); // "non-locking destruction"; it is carried out a little bit later
+
+ UnityMemoryBarrier();
+
+ delete tmp;
+}
+
+void InitializeDeveloperConsole ()
+{
+ Assert( NULL == s_DeveloperConsole);
+ s_DeveloperConsole = new DeveloperConsole();
+ RegisterLogCallback(DeveloperConsole_HandleLogFunction, true);
+}
+
+DeveloperConsole& GetDeveloperConsole()
+{
+ Assert( NULL != s_DeveloperConsole);
+ return *s_DeveloperConsole;
+}
+DeveloperConsole* GetDeveloperConsolePtr()
+{
+ return s_DeveloperConsole;
+}
+
+
+DeveloperConsole::DeveloperConsole()
+ : m_ConsoleClosed(false)
+ , m_LogBuffer()
+
+ // State
+ , m_GUIState()
+ , m_Content()
+
+ // Used styles
+ , m_LabelStyle(0)
+ , m_ButtonStyle(0)
+ , m_BoxStyle(0)
+
+ // Titles for GUI elements
+ , m_DevelopmentConsoleBoxTitle("Development Console")
+#if UNITY_WEBPLAYER_AND_STANDALONE
+ , m_OpenLogFileButtonTitle("Open Log File")
+#endif
+ , m_ClearButtonTitle("Clear")
+ , m_CloseButtonTitle("Close")
+{}
+
+DeveloperConsole::~DeveloperConsole()
+{
+ delete m_LabelStyle;
+ delete m_ButtonStyle;
+ delete m_BoxStyle;
+}
+
+LogBufferEntry::LogBufferEntry(const std::string& condition, const std::string& stripped_stacktrace, LogType log)
+ : type(log)
+{
+ // Concatenate condition and the stacktrace;
+ // the only difference between expanded and non-expanded console entry is,
+ // actually, just the length of the rendered string
+ std::string strace(condition);
+ cond_len = strace.length(); // Preserve the length of the condition
+
+ strace += "\n> ";
+ strace += stripped_stacktrace;
+
+ // Initialize the debug string
+ UTF8ToUTF16String(strace.c_str(), debug_text);
+}
+
+void DeveloperConsole::HandleLog(const std::string& condition, const std::string& strippedStacktrace, LogType type)
+{
+ if (type > kLogLevel && type != LogType_Exception)
+ return;
+
+#if SUPPORT_THREADS
+ Mutex::AutoLock lock(m_NewEntriesMutex);
+#endif
+
+ m_NewEntries.push_back( LogBufferEntry(condition, strippedStacktrace, type));
+ if (m_NewEntries.size() > kMaxNumberOfLogMessages)
+ {
+ m_NewEntries.pop_front(); // Forget the first message
+ }
+}
+
+ObjectGUIState& DeveloperConsole::GetObjectGUIState()
+{
+ return m_GUIState;
+}
+
+bool DeveloperConsole::IsVisible () const
+{
+#if SUPPORT_THREADS
+ Mutex::AutoLock consoleLock(m_ConsoleMutex); // Required for m_LogBuffer
+ Mutex::AutoLock newEntriesLock(m_NewEntriesMutex); // Required for m_NewEntries
+#endif
+ if (m_ConsoleClosed)
+ return false;
+ return !m_LogBuffer.empty() || !m_NewEntries.empty();
+}
+
+void DeveloperConsole::SetOpen(bool opened)
+{
+ m_ConsoleClosed = !opened;
+}
+void DeveloperConsole::Clear()
+{
+#if SUPPORT_THREADS
+ Mutex::AutoLock lock(m_ConsoleMutex);
+#endif
+ m_LogBuffer.clear();
+}
+bool DeveloperConsole::HasLogMessages()
+{
+#if SUPPORT_THREADS
+ Mutex::AutoLock lock(m_ConsoleMutex);
+#endif
+ return !m_LogBuffer.empty();
+}
+
+// Initializes an existing instance of a UTF16String in a fast way (avoiding double-copy pattern)
+inline static void FastUTF16StringInit(const UTF16String& src, UTF16String& dst)
+{
+ dst.TakeOverPreAllocatedUTF16Bytes(src.text, src.length);
+ dst.owns = false;
+}
+
+inline static void SetupLogEntryGUIContent(GUIContent& content, const LogBufferEntry& entry)
+{
+ FastUTF16StringInit(entry.debug_text, content.m_Text);
+ if (entry.cond_len > 0)
+ {
+ // if the entry is not expanded, we want to show only the part
+ // that does not include the stacktrace
+ content.m_Text.length = entry.cond_len;
+ }
+}
+
+bool DeveloperConsole::DoGUI()
+{
+#if SUPPORT_THREADS
+ Mutex::AutoLock lock(m_ConsoleMutex);
+#endif
+
+ AppendNewEntries();
+
+ // New entries were copied into into m_LogBuffer, so just check that
+ if (m_ConsoleClosed || m_LogBuffer.empty())
+ return false;
+
+ if (0 == m_LabelStyle) // if this is not initialized,
+ { // then all styles need to be fetched
+ InitGUIStyles();
+ }
+
+ Assert( NULL != m_LabelStyle);
+ Assert( NULL != m_ButtonStyle);
+ Assert( NULL != m_BoxStyle);
+
+ GUIState &guiState = GetGUIState ();
+ guiState.m_CanvasGUIState.m_GUIClipState.BeginOnGUI (*guiState.m_CurrentEvent);
+ guiState.BeginOnGUI(GetObjectGUIState());
+
+ // GUI code must go here
+ const float screen_width = GetScreenManager().GetWidth();
+ const float screen_height = GetScreenManager().GetHeight();
+
+ const float half_screen_width = 0.5f * screen_width;
+
+ float box_height = DrawBox(guiState, screen_height, half_screen_width);
+
+ Rectf position;
+ float label_pos = 0.f;
+ GUIStyle& labelStyle = *m_LabelStyle;
+
+ // We output messages in this order: the newest at the bottom and the oldest at the top
+ for( std::list<LogBufferEntry>::reverse_iterator rit = m_LogBuffer.rbegin();
+ rit != m_LogBuffer.rend(); ++rit)
+ {
+ SetupLogEntryGUIContent(m_Content, *rit);
+
+ switch (rit->type)
+ {
+ case LogType_Error:
+ case LogType_Assert:
+ case LogType_Exception:
+ labelStyle.m_Normal.textColor = ColorRGBAf (1.0f, 0.0f, 0.0f, 1.0f); // Color.red;
+ break;
+
+ case LogType_Warning:
+ labelStyle.m_Normal.textColor = ColorRGBAf (1.0f, 0.0f, 0.0f, 1.0f); // Color.red;
+ break;
+
+ default:
+ labelStyle.m_Normal.textColor = ColorRGBAf (1.0f, 1.0f, 1.0f, 1.0f); // Color.white;
+ break;
+ }
+
+ label_pos += rit->label_height; // Label height has been previously computed in DrawBox
+
+ position.Set(0, screen_height - label_pos, half_screen_width, box_height);
+ if (IMGUI::GUIButton(guiState, position, m_Content, labelStyle))
+ {
+ rit->cond_len = -rit->cond_len;
+ }
+ }
+
+#if UNITY_WEBPLAYER_AND_STANDALONE
+ position.Set(half_screen_width, screen_height - 75, 70, 20);
+ if (DrawButton(guiState, position, m_OpenLogFileButtonTitle))
+ {
+ DeveloperConsole_OpenConsoleFile();
+ }
+#endif // UNITY_WEBPLAYER_AND_STANDALONE
+
+ position.Set(half_screen_width, screen_height - 50, 70, 20);
+ if (DrawButton(guiState, position, m_ClearButtonTitle))
+ {
+ m_LogBuffer.clear();
+ }
+
+ position.Set(half_screen_width, screen_height - 25, 70, 20);
+ if (DrawButton(guiState, position, m_CloseButtonTitle))
+ {
+ m_ConsoleClosed = true;
+ }
+
+ guiState.EndOnGUI ();
+ guiState.m_CanvasGUIState.m_GUIClipState.EndOnGUI (*guiState.m_CurrentEvent);
+
+ return guiState.m_CurrentEvent->type == InputEvent::kUsed;
+}
+
+void DeveloperConsole::AppendNewEntries()
+{
+#if SUPPORT_THREADS
+ // We already have a lock on m_ConsoleMutex for writing to m_LogBuffer
+ // We need a lock on new entries potentially added from other threads
+ Mutex::AutoLock lock(m_NewEntriesMutex);
+#endif
+
+ while (!m_NewEntries.empty())
+ {
+ m_LogBuffer.push_back(m_NewEntries.front());
+ m_NewEntries.pop_front();
+ if (m_LogBuffer.size() > kMaxNumberOfLogMessages)
+ {
+ m_LogBuffer.pop_front(); // Forget the first message
+ }
+ m_ConsoleClosed = false; // New log message, the console pops back
+ }
+}
+
+bool DeveloperConsole::DrawButton(GUIState& guiState, const Rectf& position, const UTF16String& label)
+{
+ FastUTF16StringInit(label, m_Content.m_Text);
+ return IMGUI::GUIButton(guiState, position, m_Content, *m_ButtonStyle);
+}
+
+float DeveloperConsole::DrawBox(GUIState& guiState, float height, float width)
+{
+ const GUIStyle& labelStyle = *m_LabelStyle;
+
+ // Compute how much space log messages themselves would take
+ float box_body_height = 0.f;
+ for( std::list<LogBufferEntry>::iterator it = m_LogBuffer.begin();
+ it != m_LogBuffer.end(); ++it)
+ {
+ SetupLogEntryGUIContent(m_Content, *it);
+ it->label_height = labelStyle.CalcHeight(m_Content, width);
+ box_body_height += it->label_height;
+ }
+
+ // Accomodate the box title
+ GUIStyle& boxStyle = *m_BoxStyle;
+
+ FastUTF16StringInit(m_DevelopmentConsoleBoxTitle, m_Content.m_Text);
+ float box_title_height = boxStyle.CalcHeight(m_Content, width);
+
+ // Summa summarum: the height of the developer console is its body plus its title
+ float dev_console_height = box_body_height + box_title_height;
+
+ Rectf position(0, height - dev_console_height, width, dev_console_height);
+ IMGUI::GUILabel(guiState, position, m_Content, boxStyle);
+ return box_body_height;
+}
+
+
+// Hardcoded styles (the values copied from the Editor)
+
+static GUIStyle* CreateLabelIconGUIStyle ()
+{
+ GUIStyle* style = new GUIStyle ();
+
+ style->m_Normal.textColor = ColorRGBAf (1.0f, 1.0f, 1.0f, 1.0f);//ColorRGBAf (0.0f, 0.0f, 0.0f, 1.0f);
+ style->m_Hover.textColor = ColorRGBAf (0.0f, 0.0f, 0.0f, 1.0f);
+ style->m_OnNormal.textColor = ColorRGBAf (0.0f, 0.0f, 0.0f, 1.0f);
+
+ style->m_Padding.left = 3; // so that the messages do not look glued to the left of the screen
+ style->m_Padding.right = 0;
+ style->m_Padding.top = 3;
+ style->m_Padding.bottom = 3;
+
+ style->m_RichText = true;
+ style->m_Alignment = kUpperLeft;
+ style->m_Clipping = kOverflow;
+
+ style->m_ImagePosition = kTextOnly;
+ style->m_WordWrap = true;
+
+ return style;
+}
+
+static Texture2D* GetResourceTexture(const char* texture_name)
+{
+ return reinterpret_cast<Texture2D*>(
+ GetBuiltinResourceManager ().GetResource (ClassID(Texture2D), texture_name));
+}
+
+static GUIStyle* CreateButtonGUIStyle ()
+{
+ GUIStyle* style = new GUIStyle ();
+
+ const float normal_color = 230.0f / 255.0f;
+ style->m_Normal.textColor = ColorRGBAf (normal_color, normal_color, normal_color, 1.0f);
+ style->m_Normal.background = GetResourceTexture("GameSkin/button.png");
+
+ style->m_Hover.textColor = ColorRGBAf (1.0f, 1.0f, 1.0f, 1.0f);
+ style->m_Hover.background = GetResourceTexture("GameSkin/button hover.png");
+
+ style->m_Active.textColor = style->m_Normal.textColor;
+ style->m_Active.background = GetResourceTexture("GameSkin/button active.png");
+
+ style->m_Focused.textColor = style->m_Hover.textColor;
+
+ style->m_OnNormal.textColor = style->m_Normal.textColor;
+ style->m_OnNormal.background = GetResourceTexture("GameSkin/button on.png");
+
+ style->m_OnHover.textColor = style->m_Hover.textColor;
+ style->m_OnHover.background = GetResourceTexture("GameSkin/button on hover.png");
+
+ style->m_OnActive.textColor = style->m_Normal.textColor;
+ style->m_OnActive.background = GetResourceTexture("GameSkin/button active.png");
+
+ style->m_Border.left = 6;
+ style->m_Border.right = 6;
+ style->m_Border.top = 6;
+ style->m_Border.bottom = 4;
+
+ style->m_Margin.left = 4;
+ style->m_Margin.right = 4;
+ style->m_Margin.top = 4;
+ style->m_Margin.bottom = 4;
+
+ style->m_Padding.left = 6;
+ style->m_Padding.right = 6;
+ style->m_Padding.top = 3;
+ style->m_Padding.bottom = 3;
+
+ style->m_RichText = false;
+ style->m_FontSize = 10; // make button captions a little bit smaller
+ style->m_Alignment = kMiddleCenter;
+
+ style->m_StretchWidth = true;
+ style->m_StretchHeight = false;
+
+ return style;
+}
+
+static GUIStyle* CreateBoxGUIStyle ()
+{
+ GUIStyle* style = new GUIStyle ();
+
+ const float normal_color = 204.0f / 255.0f;
+ style->m_Normal.textColor = ColorRGBAf (normal_color, normal_color, normal_color, 1.0f);
+ style->m_Normal.background = GetResourceTexture("GameSkin/box.png");
+
+ style->m_Border.left = 6;
+ style->m_Border.right = 6;
+ style->m_Border.top = 6;
+ style->m_Border.bottom = 6;
+
+ style->m_Margin.left = 4;
+ style->m_Margin.right = 4;
+ style->m_Margin.top = 4;
+ style->m_Margin.bottom = 4;
+
+ style->m_Padding.left = 4;
+ style->m_Padding.right = 4;
+ style->m_Padding.top = 4;
+ style->m_Padding.bottom = 4;
+
+ style->m_RichText = false;
+ style->m_FontStyle = 1; // The box title is bold
+ style->m_Alignment = kUpperCenter;
+
+ style->m_StretchWidth = true;
+ style->m_StretchHeight = false;
+
+ return style;
+}
+
+void DeveloperConsole::InitGUIStyles()
+{
+ m_LabelStyle = CreateLabelIconGUIStyle();
+ m_ButtonStyle = CreateButtonGUIStyle();
+ m_BoxStyle = CreateBoxGUIStyle();
+}
+
+#endif // UNITY_HAS_DEVELOPER_CONSOLE
+
+
+#if UNITY_WIN
+#include <ShellAPI.h>
+#include "PlatformDependent/Win/PathUnicodeConversion.h"
+#endif
+
+void DeveloperConsole_OpenConsoleFile ()
+{
+ std::string path = GetConsoleLogPath();
+#if UNITY_OSX
+ system (("open \""+path+"\"").c_str());
+#elif UNITY_WIN && !UNITY_WINRT
+ std::wstring widePath;
+ ConvertUnityPathName(path, widePath);
+
+ int res = (int)::ShellExecuteW( NULL, L"open", widePath.c_str(), NULL, NULL, SW_SHOWNORMAL);
+ if (res <= 32)
+ res = (int)::ShellExecuteW( NULL, L"edit", widePath.c_str(), NULL, NULL, SW_SHOWNORMAL);
+#else
+ ErrorString ("Opening Console File is not supported on this platform.");
+#endif
+}
diff --git a/Runtime/Misc/DeveloperConsole.h b/Runtime/Misc/DeveloperConsole.h
new file mode 100644
index 0000000..59c103b
--- /dev/null
+++ b/Runtime/Misc/DeveloperConsole.h
@@ -0,0 +1,154 @@
+#pragma once
+
+#include "Runtime/Utilities/LogAssert.h" // for LogType
+
+// Can't get the DeveloperConsole to work on iOS, Android or XBox
+// (fails with Can't add script behaviour DeveloperConsole. The scripts file name does not match the name of the class defined in the script!)
+// No time to look into, need to merge core. Fix & Enabled later.
+#define UNITY_HAS_DEVELOPER_CONSOLE \
+ !(UNITY_FLASH || UNITY_WEBGL) && GAMERELEASE && UNITY_DEVELOPER_BUILD && !(UNITY_IPHONE || UNITY_XENON || UNITY_ANDROID || UNITY_PS3)
+
+
+#if UNITY_HAS_DEVELOPER_CONSOLE
+
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "Runtime/IMGUI/GUIContent.h"
+
+#if SUPPORT_THREADS
+#include "Runtime/Threads/Mutex.h"
+#endif
+
+struct LogBufferEntry
+{
+ // The sign bit is used to see whether a log entry is expanded or not
+ int cond_len;
+
+ LogType type;
+
+ // This is only used as cache, so that label height
+ // computation would not be done twice
+ float label_height;
+
+ UTF16String debug_text;
+
+ // Constructs the object and initializes UTF16String members
+ LogBufferEntry(const std::string& condition, const std::string& stripped_stacktrace, LogType log);
+
+ // Moves the ownership of UTF16String memory to this object
+ LogBufferEntry(const LogBufferEntry& rhs)
+ : cond_len(rhs.cond_len)
+ , type(rhs.type)
+ {
+ fast_string_init(rhs);
+ }
+
+ // Moves the ownership of UTF16String memory to this object
+ LogBufferEntry& operator=(const LogBufferEntry& rhs)
+ {
+ fast_string_init(rhs);
+ cond_len = rhs.cond_len;
+ type = rhs.type;
+ return *this;
+ }
+
+private:
+ static void change_memory_ownership_impl(UTF16String& src, UTF16String& dst)
+ {
+ dst.TakeOverPreAllocatedUTF16Bytes(src.text, src.length);
+ dst.owns = src.owns; // it is possible that src DOES NOT own the string
+ src.owns = false; // if dst owns, then src must not own anymore;
+ // if dst does not own, then src does not own either, so this assignment is a no-op.
+ }
+
+ void fast_string_init(const LogBufferEntry& rhs)
+ {
+ change_memory_ownership_impl(*const_cast<UTF16String*>(&rhs.debug_text), debug_text);
+ }
+};
+
+
+class DeveloperConsole : private NonCopyable
+{
+public:
+ static const LogType kLogLevel = LogType_Assert;
+
+ // The number of log messages that are displayed
+ static const unsigned int kMaxNumberOfLogMessages = 10u;
+
+ // Interface that is needed by the GUIManager in order to render the console, etc.
+ bool IsVisible() const;
+
+ void SetOpen(bool opened);
+
+ void Clear();
+
+ bool HasLogMessages();
+
+ bool DoGUI();
+
+ ObjectGUIState& GetObjectGUIState();
+
+private:
+ // Developer console should be instantiated only and only from InitializeDeveloperConsole,
+ // thus its constructor is made private. For all other functions, for example,
+ // functions in LogAssert.h, the only accessible member functions are the static ones.
+ friend void InitializeDeveloperConsole ();
+ friend void CleanupDeveloperConsole ();
+
+ DeveloperConsole();
+ ~DeveloperConsole();
+
+private:
+ friend void DeveloperConsole_HandleLogFunction (const std::string& condition, const std::string &stackTrace, int type);
+
+ void HandleLog(const std::string& condition, const std::string& strippedStacktrace, LogType logType);
+
+private:
+ // Helper member functions
+ void InitGUIStyles();
+
+ void AppendNewEntries();
+
+ bool DrawButton(GUIState& guiState, const Rectf& position, const UTF16String& label);
+ float DrawBox(GUIState& guiState, float height, float width);
+
+private:
+ bool m_ConsoleClosed;
+ std::list<LogBufferEntry> m_LogBuffer;
+ std::list<LogBufferEntry> m_NewEntries;
+ ObjectGUIState m_GUIState;
+ GUIContent m_Content;
+
+ // GUI styles that are used in DeveloperConsole.
+ // Initial values are currently copied from the default skin and hardcoded.
+ GUIStyle* m_LabelStyle;
+ GUIStyle* m_ButtonStyle;
+ GUIStyle* m_BoxStyle;
+
+ // Labels are cached, so that they are not allocated and copied on each DoGUI invocation
+ const UTF16String m_DevelopmentConsoleBoxTitle;
+#if UNITY_WEBPLAYER_AND_STANDALONE
+ const UTF16String m_OpenLogFileButtonTitle;
+#endif
+ const UTF16String m_ClearButtonTitle;
+ const UTF16String m_CloseButtonTitle;
+
+#if SUPPORT_THREADS
+ mutable Mutex m_ConsoleMutex;
+ mutable Mutex m_NewEntriesMutex;
+#endif
+};
+
+
+DeveloperConsole& GetDeveloperConsole();
+DeveloperConsole* GetDeveloperConsolePtr();
+void CleanupDeveloperConsole ();
+void InitializeDeveloperConsole ();
+
+void DeveloperConsole_HandleLogFunction (const std::string& condition, const std::string &stackTrace, int type);
+
+#endif // UNITY_HAS_DEVELOPER_CONSOLE
+
+// This function is used in UnityEngineDebug.cpp
+void DeveloperConsole_OpenConsoleFile();
diff --git a/Runtime/Misc/GOCreation.cpp b/Runtime/Misc/GOCreation.cpp
new file mode 100644
index 0000000..d6277b8
--- /dev/null
+++ b/Runtime/Misc/GOCreation.cpp
@@ -0,0 +1,95 @@
+#include "UnityPrefix.h"
+#include "GOCreation.h"
+#include "GameObjectUtility.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Dynamics/CapsuleCollider.h"
+#include "Runtime/Dynamics/Collider.h"
+#include "ResourceManager.h"
+#include "Runtime/Filters/Mesh/LodMeshFilter.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Interfaces/IPhysics.h"
+
+GameObject* CreatePrimitive (int type)
+{
+ GameObject* go = NULL;
+ if (type == kPrimitiveSphere)
+ {
+ go = &CreateGameObject ("Sphere", "MeshFilter", "SphereCollider", "MeshRenderer", NULL);
+ go->GetComponent (MeshFilter).SetSharedMesh (GetBuiltinResource<Mesh> ("New-Sphere.fbx"));
+
+ Collider* collider = go->QueryComponent (Collider);
+ if (collider)
+ {
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ SmartResetObject(*collider);
+ }
+
+ go->GetComponent (Renderer).SetMaterial (Material::GetDefaultDiffuseMaterial(), 0);
+ }
+ else if (type == kPrimitiveCapsule)
+ {
+ go = &CreateGameObject ("Capsule", "MeshFilter", "CapsuleCollider", "MeshRenderer", NULL);
+ go->GetComponent (MeshFilter).SetSharedMesh (GetBuiltinResource<Mesh> ("New-Capsule.fbx"));
+
+ CapsuleCollider* collider = go->QueryComponent (CapsuleCollider);
+ if (collider != NULL)
+ {
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ SmartResetObject(*collider);
+ else
+ GetIPhysics()->CapsuleColliderSetHeight(*collider, 2.0f);
+ }
+
+ go->GetComponent (Renderer).SetMaterial (Material::GetDefaultDiffuseMaterial(), 0);
+ }
+ else if (type == kPrimitiveCylinder)
+ {
+ go = &CreateGameObject ("Cylinder", "MeshFilter", "CapsuleCollider", "MeshRenderer", NULL);
+ go->GetComponent (MeshFilter).SetSharedMesh (GetBuiltinResource<Mesh> ("New-Cylinder.fbx"));
+
+ CapsuleCollider* collider = go->QueryComponent (CapsuleCollider);
+ if (collider != NULL)
+ {
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ SmartResetObject(*collider);
+ else
+ GetIPhysics()->CapsuleColliderSetHeight(*collider, 2.0f);
+ }
+
+ go->GetComponent (Renderer).SetMaterial (Material::GetDefaultDiffuseMaterial(), 0);
+ }
+ else if (type == kPrimitiveCube)
+ {
+ go = &CreateGameObject ("Cube",
+ "MeshFilter",
+#if ENABLE_PHYSICS
+ "BoxCollider",
+#endif
+ "MeshRenderer",
+ NULL);
+
+ go->GetComponent (MeshFilter).SetSharedMesh (GetBuiltinResource<Mesh> ("Cube.fbx"));
+#if ENABLE_PHYSICS
+ SmartResetObject(go->GetComponent (Collider));
+#endif
+ go->GetComponent (Renderer).SetMaterial (Material::GetDefaultDiffuseMaterial(), 0);
+ }
+ else if (type == kPrimitivePlane)
+ {
+ go = &CreateGameObject ("Plane", "MeshFilter", "MeshCollider", "MeshRenderer", NULL);
+ go->GetComponent (MeshFilter).SetSharedMesh (GetBuiltinResource<Mesh> ("New-Plane.fbx"));
+ SmartResetObject(go->GetComponent (Collider));
+ go->GetComponent (Renderer).SetMaterial (Material::GetDefaultDiffuseMaterial(), 0);
+ }
+ else if (type == kPrimitiveQuad)
+ {
+ go = &CreateGameObject ("Quad", "MeshFilter", "MeshCollider", "MeshRenderer", NULL);
+ go->GetComponent (MeshFilter).SetSharedMesh (GetBuiltinResource<Mesh> ("Quad.fbx"));
+ SmartResetObject(go->GetComponent (Collider));
+ go->GetComponent (Renderer).SetMaterial (Material::GetDefaultDiffuseMaterial(), 0);
+ }
+
+ return go;
+}
diff --git a/Runtime/Misc/GOCreation.h b/Runtime/Misc/GOCreation.h
new file mode 100644
index 0000000..67d04b4
--- /dev/null
+++ b/Runtime/Misc/GOCreation.h
@@ -0,0 +1,18 @@
+#ifndef GO_CREATION_CPP_H
+#define GO_CREATION_CPP_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+
+enum
+{
+ kPrimitiveSphere = 0,
+ kPrimitiveCapsule = 1,
+ kPrimitiveCylinder = 2,
+ kPrimitiveCube = 3,
+ kPrimitivePlane = 4,
+ kPrimitiveQuad = 5
+};
+
+Unity::GameObject* CreatePrimitive (int type);
+
+#endif
diff --git a/Runtime/Misc/GOCreationTests.cpp b/Runtime/Misc/GOCreationTests.cpp
new file mode 100644
index 0000000..5d60e70
--- /dev/null
+++ b/Runtime/Misc/GOCreationTests.cpp
@@ -0,0 +1,54 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Misc/GOCreation.h"
+#include "Runtime/Filters/Mesh/LodMeshFilter.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Dynamics/CapsuleCollider.h"
+
+SUITE (GameObjectCreationTests)
+{
+ TEST (CreateSphereTest)
+ {
+ GameObject* go = CreatePrimitive(kPrimitiveSphere);
+
+ CHECK_EQUAL(go->GetComponentCount(), 4);
+ CHECK_EQUAL(go->GetName(), "Sphere");
+ CHECK(!go->GetComponent(MeshFilter).GetSharedMesh().IsNull());
+ CHECK_EQUAL(go->GetComponent(Renderer).GetMaterialCount(), 1);
+ }
+
+ TEST (CreateCubeTest)
+ {
+ GameObject* go = CreatePrimitive(kPrimitiveCube);
+
+#if ENABLE_PHYSICS
+ int count = 4;
+#else
+ int count = 3;
+#endif
+
+ CHECK_EQUAL(go->GetComponentCount(), count);
+ CHECK_EQUAL(go->GetName(), "Cube");
+ CHECK(!go->GetComponent(MeshFilter).GetSharedMesh().IsNull());
+ CHECK_EQUAL(go->GetComponent(Renderer).GetMaterialCount(), 1);
+ }
+
+ TEST (CreateCylinderTest)
+ {
+ GameObject* go = CreatePrimitive(kPrimitiveCylinder);
+
+ CHECK_EQUAL(go->GetComponentCount(), 4);
+ CHECK_EQUAL(go->GetName(), "Cylinder");
+ CHECK(!go->GetComponent(MeshFilter).GetSharedMesh().IsNull());
+ CHECK_EQUAL(go->GetComponent(Renderer).GetMaterialCount(), 1);
+
+#if ENABLE_PHYSICS
+ CHECK_EQUAL(go->GetComponent (CapsuleCollider).GetHeight(), 2.0f);
+#endif
+ }
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/Misc/GameObjectUtility.cpp b/Runtime/Misc/GameObjectUtility.cpp
new file mode 100644
index 0000000..b2ee5b4
--- /dev/null
+++ b/Runtime/Misc/GameObjectUtility.cpp
@@ -0,0 +1,1276 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "ComponentRequirement.h"
+#include "GameObjectUtility.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "BatchDeleteObjects.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Mono/MonoScriptCache.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Utilities/dynamic_bitset.h"
+#include "Runtime/Serialize/TransferUtility.h"
+#include "Runtime/Serialize/IterateTypeTree.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "BuildSettings.h"
+#include "Runtime/Camera/Camera.h" //// @TODO: Only used by FindMainCamera, should be moved to a different file?
+#include "Runtime/BaseClasses/Tags.h"
+#include "Player.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/Prefabs/Prefab.h"
+#endif
+
+PROFILER_INFORMATION(gDestroyProfile, "Destroy", kProfilerOther)
+
+using namespace std;
+const char* kUnityEngine = "UnityEngine";
+
+#if UNITY_EDITOR
+
+static std::vector<AddComponentCallbackFunction*> gGOAddComponentCallbacks;
+
+void RegisterAddComponentCallback (AddComponentCallbackFunction* callback)
+{
+ gGOAddComponentCallbacks.push_back(callback);
+}
+
+static void InvokeAddComponentCallback (Unity::Component& com)
+{
+ for (int i=0;i<gGOAddComponentCallbacks.size();i++)
+ gGOAddComponentCallbacks[i] (com);
+}
+#else
+inline void InvokeAddComponentCallback (Unity::Component& com) { }
+#endif
+
+
+
+bool IsComponentSubclassOfMonoClass (Unity::Component& com, MonoClass* requiredClass);
+bool IsComponentSubclassOfMonoClass (Unity::Component& com, MonoClass* requiredClass)
+{
+#if ENABLE_SCRIPTING
+ int curComponentClassID = com.GetClassID();
+
+ ScriptingClassPtr curKlass = GetMonoManager().ClassIDToScriptingClass (curComponentClassID);
+
+ MonoBehaviour* monoBehaviour = dynamic_pptr_cast<MonoBehaviour*> (&com);
+ if (monoBehaviour)
+ curKlass = monoBehaviour->GetClass();
+
+ // Check classID against behaviour
+ if (curKlass && requiredClass)
+ {
+ if (requiredClass == curKlass || scripting_class_is_subclass_of (curKlass, requiredClass))
+ return true;
+ }
+#endif
+ return false;
+}
+
+Unity::Component* AddComponentUnchecked (GameObject& go, int classID, MonoScriptPtr script, string* error)
+{
+ // Produce object
+ Unity::Component* o = static_cast<Unity::Component*>(Object::Produce (classID));
+ if (o == NULL)
+ {
+ if (error)
+ *error = Format ("Can't add component because the component '%s' can't be produced.", Object::ClassIDToString (classID).c_str());
+ return NULL;
+ }
+
+ AssertIf (!o->IsDerivedFrom (ClassID (Component)));
+
+ o->Reset ();
+ go.AddComponentInternal (static_cast<Unity::Component*> (o));
+
+ MonoBehaviour* monoBehaviour = dynamic_pptr_cast<MonoBehaviour*> (o);
+ if (monoBehaviour)
+ {
+#if ENABLE_SCRIPTING
+ int instanceID = o->GetInstanceID();
+ monoBehaviour->SetScript (script);
+ // Check if the object has destroyed itself in Awake.
+ if (!PPtr<Object> (instanceID).IsValid())
+ return NULL;
+#else
+ AssertIf( script == 0 && "Mono's disabled" );
+#endif
+
+#if UNITY_EDITOR
+ if (!IsInsidePlayerLoop())
+ ApplyDefaultReferences(*monoBehaviour, script->GetDefaultReferences());
+#endif
+ }
+
+ o->Reset();
+ o->SmartReset();
+ o->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+ o->SetDirty ();
+
+ InvokeAddComponentCallback (*o);
+
+ return static_cast<Unity::Component*> (o);
+}
+
+bool CheckForAbstractClass(GameObject& go, int classID, string* error)
+{
+ if (Object::ClassIDToRTTI(classID)->isAbstract)
+ {
+ // list all derived components
+ string objectList;
+ vector<SInt32> derivedObjects;
+ Object::FindAllDerivedClasses (classID, &derivedObjects);
+ for (vector<SInt32>::const_iterator it=derivedObjects.begin();it!=derivedObjects.end();++it)
+ {
+ SInt32 id = (*it);
+ objectList += Format("'%s'", Object::ClassIDToString (id).c_str());
+ if (it != derivedObjects.end()-1)
+ objectList += " or ";
+ }
+ *error = Format ("Adding component failed. Add required component of type %s to the game object '%s' first.", objectList.c_str(), go.GetName ());
+ return false;
+ }
+ return true;
+}
+
+static void AddRequiredScriptComponents (GameObject& go, MonoScript* script, std::set<ScriptingClassPtr> &processed)
+{
+#if ENABLE_MONO || UNITY_WINRT
+ ScriptingArrayPtr array = RequiredComponentsOf(script->GetClass());
+
+ if (array)
+ {
+ for (int j = 0; j < GetScriptingArraySize(array); j++)
+ {
+ ScriptingObjectPtr requiredClassObject = Scripting::GetScriptingArrayElementNoRef<ScriptingObjectPtr>(array, j);
+ if (requiredClassObject == SCRIPTING_NULL)
+ continue;
+
+ ScriptingClassPtr requiredClass = GetScriptingTypeRegistry().GetType (requiredClassObject);
+
+ if (requiredClass != NULL && processed.find(requiredClass) != processed.end())
+ continue;
+
+ // Is that script/component already added to the game obejct?
+ bool needToAdd = true;
+ for (int c=0;c<go.GetComponentCount();c++)
+ {
+ if (IsComponentSubclassOfMonoClass (go.GetComponentAtIndex(c), requiredClass))
+ {
+ needToAdd = false;
+ break;
+ }
+ }
+
+ if (needToAdd)
+ {
+ int clsID = -1;
+ MonoScript* requiredScript = NULL;
+ if (StrICmp (scripting_class_get_namespace (requiredClass), kUnityEngine) == 0)
+ clsID = Object::StringToClassID (scripting_class_get_name (requiredClass));
+
+ if (clsID == -1 || !Object::IsDerivedFromClassID (clsID, ClassID (Component)))
+ {
+ // We can't find the script.
+ // Not very good but we will just let it slip through
+ requiredScript = GetMonoScriptManager().FindRuntimeScript(requiredClass);
+ if (requiredScript == NULL )
+ continue;
+ clsID = ClassID (MonoBehaviour);
+ }
+
+ string internalError;
+ if (!CheckForAbstractClass(go, clsID, &internalError) || AddComponentInternal (go, clsID, requiredScript, processed, &internalError) == NULL)
+ ErrorString (internalError);
+ }
+ }
+ }
+#endif
+}
+
+
+static bool ValidateScriptComponent (MonoScript* script, std::string* error)
+{
+ // Check if the script is instantiatable
+ if (script == NULL)
+ {
+ if (error)
+ *error = Format ("Can't add script behaviour because the script couldn't be found.");
+ return false;
+ }
+
+ MonoScriptType type = (MonoScriptType)script->GetScriptType();
+
+ if (type == kScriptTypeMonoBehaviourDerived)
+ return true;
+
+
+ // Check if the script is instantiatable
+ if (type == kScriptTypeClassNotFound)
+ {
+ if (error)
+ {
+#if UNITY_EDITOR
+ if (GetMonoManager().HasCompileErrors())
+ *error = Format("Can't add script behaviour %s. You need to fix all compile errors in all scripts first!", script->GetName());
+ else
+#endif
+ *error = Format("Can't add script behaviour %s. The scripts file name does not match the name of the class defined in the script!", script->GetName());
+ }
+
+ return false;
+ }
+ // Check if the script is instantiatable
+ else
+ {
+ if (error)
+ {
+ if (script->IsEditorScript())
+ *error = Format ("Can't add script behaviour %s because it is an editor script. To attach a script it needs to be outside the 'Editor' folder.", script->GetName());
+ else if (type == kScriptTypeNotInitialized)
+ *error = Format("Script %s has not finished compilation yet. Please wait until compilation of the script has finished and try again.", script->GetName());
+ else if (type == kScriptTypeClassIsAbstract)
+ *error = Format("Can't add script behaviour %s. The script class can't be abstract!", script->GetName());
+ else if (type == kScriptTypeClassIsInterface)
+ *error = Format("Can't add script behaviour %s. The script can't be an interface!", script->GetName());
+ else if (type == kScriptTypeClassIsGeneric)
+ *error = Format("Can't add script behaviour %s. Generic MonoBehaviours are not supported!", script->GetName());
+ else
+ *error = Format("Can't add script behaviour %s. The script needs to derive from MonoBehaviour!", script->GetName());
+ }
+ return false;
+ }
+}
+
+bool CanAddComponent (Unity::GameObject& go, int classID)
+{
+ if (go.CountDerivedComponents (classID) && !DoesComponentAllowMultipleInclusion (classID))
+ return false;
+ if (go.HasConflictingComponents(classID))
+ return false;
+ return true;
+}
+
+Unity::Component* AddComponent (GameObject& go, int classID, MonoScriptPtr script, string* error)
+{
+ std::set<ScriptingClassPtr> processed;
+ return AddComponentInternal (go, classID, script, processed, error);
+}
+
+Unity::Component* AddComponentInternal (GameObject& go, int classID, MonoScriptPtr script, std::set<ScriptingClassPtr> &processed, std::string* error)
+{
+ // Are we actually derived from go component?
+ if (!Object::IsDerivedFromClassID (classID, ClassID (Component)))
+ {
+ if (error)
+ *error = Format ("Can't add component because '%s' is not derived from Component.", Object::ClassIDToString (classID).c_str ());
+ return NULL;
+ }
+
+ // We can't add a component if it has conflicting components. This is included when checking if we can add a component but we want to give a specific reason here i.e. a component conflict.
+ Unity::Component* conflictingComponent = go.FindConflictingComponentPtr (classID);
+ if (conflictingComponent)
+ {
+ if (error)
+ *error = Format ("Can't add component '%s' to %s because it conflicts with the existing '%s' derived component!",
+ Object::ClassIDToString (classID).c_str (), go.GetName (), Object::ClassIDToString (conflictingComponent->GetClassID ()).c_str ());
+ return NULL;
+ }
+
+ // We can't add a component if we are already inserted and the component doesn't allow multiple insertion!
+ if (!CanAddComponent (go, classID))
+ {
+ if (error)
+ *error = Format ("Can't add component '%s' to %s because such a component is already added to the game object!", Object::ClassIDToString (classID).c_str (), go.GetName ());
+ return NULL;
+ }
+
+ // Don't allow adding a component to an imported model
+ // @TODO: go.TestHideFlag(Object::kNotEditable) && go.IsPersistent()
+ // -> Is weird. We should cleanup the hide flags to be sensible design
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_5_a1) && go.TestHideFlag(Object::kNotEditable) && go.IsPersistent())
+ {
+ if (error)
+ *error = Format ("Can't add component '%s' to %s because the game object is a generated prefab and can only be modified through an AssetPostprocessor.", Object::ClassIDToString (classID).c_str (), go.GetName ());
+ return NULL;
+ }
+
+ // Find all required components and add them before!
+ const vector_set<int>& requiredCom = FindRequiredComponentsForComponent (classID);
+ vector_set<int>::const_iterator i;
+ for (i=requiredCom.begin ();i!=requiredCom.end ();i++)
+ {
+ if (go.CountDerivedComponents (*i))
+ continue;
+
+ //TODO: check
+ string internalError;
+ if (!CheckForAbstractClass(go, *i, &internalError) || AddComponentInternal (go, *i, NULL, processed, &internalError) == NULL)
+ {
+ ErrorString (internalError);
+ return NULL;
+ }
+ }
+
+ // Find all required components for a script and add them before!
+ if (classID == ClassID (MonoBehaviour))
+ {
+ #if ENABLE_SCRIPTING
+
+ if (!ValidateScriptComponent (script, error))
+ return NULL;
+
+ Assert(script->GetClass () != SCRIPTING_NULL);
+
+ processed.insert(script->GetClass());
+ AddRequiredScriptComponents (go, script, processed);
+
+ #else
+ if( error )
+ *error = Format ("Can't add script behaviour because the Mono's disabled.");
+ return NULL;
+ #endif // ENABLE_SCRIPTING
+ }
+
+ // this means user is trying to add abstract class directly (AddComponent("Collider"))
+ if (Object::ClassIDToRTTI(classID)->isAbstract)
+ {
+ *error = Format ("Cannot add component of type '%s' because it is abstract. Add component of type that is derived from '%s' instead.", Object::ClassIDToString (classID).c_str (), Object::ClassIDToString (classID).c_str ());
+ return NULL;
+ }
+
+ return AddComponentUnchecked (go, classID, script, error);
+}
+
+
+
+Unity::Component* AddComponent (GameObject& go, const char* name, string* error)
+{
+ if (BeginsWith(name, "UnityEngine."))
+ name += strlen("UnityEngine.");
+
+ int classID = Object::StringToClassID (name);
+ if (classID != -1 && Object::IsDerivedFromClassID(classID, ClassID(Component)))
+ {
+ return AddComponent (go, classID, NULL, error);
+ }
+#if ENABLE_SCRIPTING
+ else
+ {
+ MonoScriptPtr script = GetMonoScriptManager().FindRuntimeScript (name);
+
+ if (script)
+ return AddComponent (go, ClassID (MonoBehaviour), script, error);
+
+ if (error)
+ {
+ if (classID == -1)
+ *error = Format ("Can't add component because class '%s' doesn't exist!", name);
+ else
+ *error = Format("Can't add component because '%s' is not derived from Component.", name);
+ }
+
+ return NULL;
+ }
+#else
+ return NULL;
+#endif
+}
+
+// varargs can only be passed around by passing the va_list, so caller is responsible for calling va_start/va_end
+void AddComponentsFromVAList (GameObject& go, const char* componentName, va_list componentList)
+{
+ if (componentName == NULL)
+ return;
+
+ string error;
+ if (AddComponent (go, componentName, &error) == NULL)
+ ErrorString (error);
+
+ while (true)
+ {
+ const char* cur = va_arg (componentList, const char*);
+ if (cur == NULL)
+ break;
+ if (AddComponent (go, cur, &error) == NULL)
+ ErrorString (error);
+ }
+}
+
+void AddComponents (GameObject& go, const char* componentName, ...)
+{
+ va_list ap;
+ va_start (ap, componentName);
+ AddComponentsFromVAList (go, componentName, ap);
+ va_end (ap);
+}
+
+void ActivateGameObject (GameObject& go, const string& name)
+{
+ go.Reset ();
+ go.SetName (name.c_str ());
+ go.AwakeFromLoad (kInstantiateOrCreateFromCodeAwakeFromLoad);
+ go.Activate ();
+}
+
+void SetNameAndResetGameObject (GameObject& go, const string& name)
+{
+ go.Reset ();
+ go.SetName (name.c_str ());
+ go.AwakeFromLoad (kInstantiateOrCreateFromCodeAwakeFromLoad);
+}
+
+GameObject& CreateGameObject (const string& name, const char* componentName, ...)
+{
+ // Create game object with name!
+ GameObject &go = *NEW_OBJECT (GameObject);
+
+ ActivateGameObject (go, name);
+
+ // Add components with class names!
+ va_list ap;
+ va_start (ap, componentName);
+ AddComponentsFromVAList (go, componentName, ap);
+ va_end (ap);
+
+ return go;
+}
+
+
+Unity::GameObject& CreateGameObjectWithVAList (const std::string& name, const char* componentName, va_list list)
+{
+ va_list componentList;
+ va_copy (componentList, list);
+ GameObject &go = *NEW_OBJECT (GameObject);
+
+ ActivateGameObject (go, name);
+ AddComponentsFromVAList (go, componentName, componentList);
+ va_end (componentList);
+
+ return go;
+}
+
+GameObject& CreateGameObjectWithHideFlags (const string& name, bool isActive, int flags, const char* componentName, ...)
+{
+ // Create game object with name!
+ GameObject &go = *NEW_OBJECT (GameObject);
+
+ // HideFlags need to be set before object activation because of a bug where
+ // Unity will not immediately update visible root game object list when
+ // changing hide flags after creation Case: 382530
+ go.SetHideFlags (flags);
+
+ if (isActive)
+ ActivateGameObject (go, name);
+ else
+ SetNameAndResetGameObject (go, name);
+
+ // Add components with class names!
+ va_list ap;
+ va_start (ap, componentName);
+ AddComponentsFromVAList(go, componentName, ap);
+ va_end (ap);
+
+ return go;
+}
+
+inline string GetComponentOrScriptName (Unity::Component& com)
+{
+#if ENABLE_SCRIPTING
+ MonoBehaviour* monoBehaviour = dynamic_pptr_cast<MonoBehaviour*> (&com);
+ if (monoBehaviour)
+ {
+ MonoScript* script = monoBehaviour->GetScript();
+ if (script)
+ return Append (script->GetName(), " (Script)");
+ }
+#endif
+ return com.GetClassName ();
+}
+
+bool CanRemoveComponent(Unity::Component& component, std::string* error)
+{
+ return CanReplaceComponent(component, -1, error);
+}
+
+bool CanReplaceComponent(Unity::Component& component, int replacementClassID, std::string* error)
+{
+ GameObject* go = component.GetGameObjectPtr ();
+ if (go == NULL)
+ return false;
+
+ int componentIndex = go->GetComponentIndex(&component);
+ if (componentIndex == -1)
+ return false;
+
+ // Starting with Unity 3.2, a transform component has to be attached to every game object!
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_2_a1))
+ {
+ if (component.GetClassID () == ClassID (Transform))
+ {
+ if (error)
+ {
+ const char* goName = go->GetName ();
+ const char* message = "Can't destroy Transform component of '%s'. "
+ "If you want to destroy the game object, please call 'Destroy' on the game object instead. "
+ "Destroying the transform component is not allowed.";
+
+ *error = Format (message, goName);
+ }
+
+ return false;
+ }
+ }
+
+ int componentClassID = component.GetClassID ();
+ ScriptingClassPtr removeKlass = SCRIPTING_NULL;
+ MonoBehaviour* removeMonoBehaviour = dynamic_pptr_cast<MonoBehaviour*> (&component);
+ if (removeMonoBehaviour)
+ removeKlass = removeMonoBehaviour->GetClass();
+
+ int countSameComponentType = 0;
+ bool mayRemove = true;
+ for (int i=0;i<go->GetComponentCount ();i++)
+ {
+ int curClassID = go->GetComponentClassIDAtIndex (i);
+ const vector_set<int>& requiredCom = FindRequiredComponentsForComponent (go->GetComponentClassIDAtIndex (i));
+
+ // if the component we are releasing is needed by another component
+ if (requiredCom.count (componentClassID))
+ {
+ if (error)
+ {
+ if (!mayRemove)
+ *error += ", ";
+ *error += Object::ClassIDToString (curClassID);
+ mayRemove = false;
+ }
+ }
+
+ // Check component requirement for scripts.
+ // - Fetch all component requirement attributes
+ // - Check if the class we remove is derived from the requirement
+ if (curClassID == ClassID (MonoBehaviour))
+ {
+ MonoBehaviour* behaviour = (MonoBehaviour*)&go->GetComponentAtIndex(i);
+ ScriptingClassPtr scriptClass = behaviour->GetClass();
+ if (scriptClass)
+ {
+#if ENABLE_MONO
+
+ MonoArray* array = RequiredComponentsOf(behaviour);
+
+ if (array)
+ {
+ for (int j=0;j<mono_array_length(array);j++)
+ {
+ MonoObject* requiredClassObject = GetMonoArrayElement<MonoObject*>(array, j);
+ if (requiredClassObject == NULL)
+ continue;
+ MonoClass* requiredClass = GetScriptingTypeRegistry().GetType(requiredClassObject);
+ if (IsComponentSubclassOfMonoClass (component, requiredClass))
+ {
+ // Find if the new replacement meets the requirements
+ bool replacementMeetRequirements = false;
+ if (replacementClassID != -1)
+ {
+ MonoClass* replacementClass = GetMonoManager().ClassIDToScriptingClass(replacementClassID);
+ // This check doesn't work with MonoBehaviour type system.
+ // But currently component replacement only happens for Collider family.
+ // Assert it is a subclass of Collider
+ Assert(scripting_class_is_subclass_of(replacementClass, GetMonoManager().ClassIDToScriptingClass(ClassID(Collider))));
+ if (replacementClass == requiredClass
+ || scripting_class_is_subclass_of(replacementClass, requiredClass))
+ {
+ replacementMeetRequirements = true;
+ }
+ }
+
+ // Find if other components of the same GO meets the requirements
+ bool otherComponentMeetRequirements = false;
+ for (int k = 0; k < go->GetComponentCount(); ++k)
+ {
+ Unity::Component& otherComponent = go->GetComponentAtIndex(k);
+ if (&otherComponent == &component // not the component being removed...
+ || &otherComponent == &go->GetComponentAtIndex(i)) // not the component asking requirements...
+ {
+ continue;
+ }
+ if (IsComponentSubclassOfMonoClass(otherComponent, requiredClass))
+ {
+ otherComponentMeetRequirements = true;
+ break;
+ }
+ }
+
+ if (!replacementMeetRequirements && !otherComponentMeetRequirements)
+ {
+ if (error)
+ {
+ if (!mayRemove)
+ *error += ", ";
+ *error += mono_class_get_name(scriptClass);
+ *error += " (Script)";
+ mayRemove = false;
+ }
+ }
+ }
+ }
+ }
+#endif // ENABLE_MONO
+
+ if (removeKlass == scriptClass)
+ countSameComponentType ++;
+ }
+ }
+ else
+ {
+ if (curClassID == componentClassID)
+ countSameComponentType ++;
+ }
+ }
+
+ if (mayRemove || countSameComponentType > 1)
+ {
+ if (error)
+ *error = "";
+ return true;
+ }
+ else
+ {
+ if (error)
+ *error = Format ("Can't remove %s because %s depends on it", GetComponentOrScriptName (component).c_str (), error->c_str ());
+ return false;
+ }
+}
+
+#if UNITY_EDITOR
+
+int GetMonoBehaviourEngineTypeTreeVariableCount ()
+{
+ static int gCount = -1;
+ if (gCount == -1)
+ {
+ MonoBehaviour* temp = NEW_OBJECT(MonoBehaviour);
+ temp->HackSetResetWasCalled();
+ temp->HackSetAwakeWasCalled();
+
+ TypeTree tree;
+ if (temp)
+ {
+ GenerateTypeTree(*temp, &tree);
+ DestroySingleObject(temp);
+ }
+ gCount = CountTypeTreeVariables(tree);
+ }
+
+ return gCount;
+}
+
+struct DisableMonoBehaviourPPtrSerialize
+{
+ dynamic_bitset override;
+ bool operator () (const TypeTree& typeTree, dynamic_array<UInt8>& data, int bytePosition)
+ {
+ if (IsTypeTreePPtr (typeTree))
+ override[typeTree.m_Index] = false;
+ else if (IsTypeTreePPtrArray (typeTree))
+ override[typeTree.m_Father->m_Index] = false;
+ else if (typeTree.m_Index < GetMonoBehaviourEngineTypeTreeVariableCount())
+ override[typeTree.m_Index] = false;
+
+ return true;
+ }
+};
+
+// Resets all non-pptr values.
+// PPtr values that are already set to a value stay, otherwise they are set to the value of the default properties
+// Does not call AwakeFromLoad so you need to do that yourself
+static void ResetMonoBehaviourToScriptDefaults (MonoBehaviour& behaviour)
+{
+ MonoScript* script = behaviour.GetScript();
+ if (script == NULL)
+ return;
+
+ if (script->GetScriptType() != kScriptTypeMonoBehaviourDerived && script->GetScriptType() != kScriptTypeScriptableObjectDerived && script->GetScriptType() != kScriptTypeEditorScriptableObjectDerived)
+ return;
+
+ // When there is a script class available
+ if (script->GetClass())
+ {
+ // Create a clone and write out the pristine state
+ MonoBehaviour* clone = NEW_OBJECT (MonoBehaviour);
+ clone->HackSetResetWasCalled();
+ clone->HackSetAwakeWasCalled();
+
+ clone->SetScript(behaviour.GetScript());
+ dynamic_array<UInt8> data(kMemTempAlloc);
+ TypeTree typeTree;
+ WriteObjectToVector (*clone, &data, kSerializeForPrefabSystem);
+ GenerateTypeTree (*clone, &typeTree, kSerializeForPrefabSystem);
+
+ DestroySingleObject (clone);
+
+ // Read back replacing everything except pptrs
+ ReadObjectFromVector (&behaviour, data, typeTree, kSerializeForPrefabSystem);
+ }
+
+ ApplyDefaultReferences(behaviour, script->GetDefaultReferences());
+}
+
+#endif
+
+
+void UnloadGameObjectAndComponents (GameObject& go)
+{
+ LockObjectCreation();
+ Assert(go.IsPersistent());
+ Assert(!go.IsActive());
+
+ for (int i=0;i<go.GetComponentCount();i++)
+ {
+ if (go.GetComponentAtIndexIsLoaded(i))
+ {
+ Unity::Component& com = go.GetComponentAtIndex(i);
+ delete_object_internal (&com);
+ }
+ }
+
+ delete_object_internal (&go);
+ UnlockObjectCreation();
+}
+
+bool UnloadGameObjectHierarchy (GameObject& go)
+{
+ LockObjectCreation();
+ Assert(!go.IsActive());
+ Transform* transform = go.QueryComponent(Transform);
+ // AssertIf(transform && !transform->IsPersistent());
+ for (int i=0;i<transform->GetChildrenCount();i++)
+ {
+ Transform& child = transform->GetChild(i);
+ UnloadGameObjectHierarchy (child.GetGameObject());
+ }
+
+ ///@TODO: This should probably be removed and fixed in the specific components that are misbehaving.
+
+ // Run this once to make sure all components are actually loaded.
+ // Otherwise, components of incompletely loaded GameObjects may be loaded in the process,
+ // and when the reference other, previous Components of the same GO, those will be loaded as well
+ // even though they should be unloaded already, causing crashes later on.
+ for (int i=0;i<go.GetComponentCount();i++)
+ go.GetComponentAtIndex(i);
+
+ for (int i=0;i<go.GetComponentCount();i++)
+ {
+ Unity::Component& com = go.GetComponentAtIndex(i);
+ delete_object_internal (&com);
+ }
+
+ delete_object_internal (&go);
+
+ UnlockObjectCreation();
+ return true;
+}
+
+void SendMessageToEveryone(MessageIdentifier message, MessageData msgData)
+{
+ // First, collect all GameObjects
+ vector<GameObject*> gameObjects;
+ Object::FindObjectsOfType (&gameObjects);
+
+ // Next, keep a list of all instance IDs, since it is possible for any GameObject
+ // to be destroyed when a message is handled.
+ set<SInt32> ids;
+ for (int i=0;i<gameObjects.size ();i++)
+ {
+
+ GameObject* go = gameObjects[i];
+ if (go->IsActive ())
+ ids.insert(go->GetInstanceID());
+ }
+
+ // Send all active gameobjects a message
+ for (set<SInt32>::iterator i=ids.begin ();i != ids.end ();i++)
+ {
+ GameObject* go = static_cast<GameObject*>(Object::IDToPointer(*i));
+ if (go && go->IsActive())
+ go->SendMessageAny (message, msgData);
+ }
+}
+
+GameObject* FindGameObjectWithTag (UInt32 tag)
+{
+ GameObjectList& tagged = GetGameObjectManager().m_TaggedNodes;
+ for (GameObjectList::iterator i=tagged.begin();i != tagged.end();i++)
+ {
+ GameObject& go = **i;
+ Assert(go.IsActive() && go.GetTag() != 0);
+ if (go.GetTag() == tag)
+ return & go;
+ }
+ return NULL;
+}
+
+void FindGameObjectsWithTag (UInt32 tag, std::vector<Unity::GameObject*>& gos)
+{
+ GameObjectList& tagged = GetGameObjectManager().m_TaggedNodes;
+ for (GameObjectList::iterator i=tagged.begin();i != tagged.end();i++)
+ {
+ GameObject& go = **i;
+ Assert(go.IsActive() && go.GetTag() != 0);
+
+ if (go.GetTag() == tag)
+ gos.push_back(&go);
+ }
+}
+
+Camera* FindMainCamera ()
+{
+ std::vector<GameObject*> gos;
+ FindGameObjectsWithTag(kMainCameraTag, gos);
+ for (int i=0;i<gos.size();i++)
+ {
+ GameObject* go = gos[i];
+ Camera* cam = go->QueryComponent(Camera);
+ if (cam != NULL && cam->GetEnabled())
+ return cam;
+ }
+ return NULL;
+}
+
+void SmartResetObject (Object& object)
+{
+ MonoBehaviour* behaviour = dynamic_pptr_cast<MonoBehaviour*> (&object);
+ if (behaviour)
+ {
+ #if UNITY_EDITOR
+ if (!IsWorldPlaying())
+ ResetMonoBehaviourToScriptDefaults(*behaviour);
+ #endif
+
+ behaviour->Reset();
+ behaviour->SmartReset();
+ behaviour->AwakeFromLoad(kDefaultAwakeFromLoad);
+ behaviour->SetDirty();
+ }
+ else
+ {
+ object.Reset();
+ object.SmartReset();
+ object.AwakeFromLoad(kDefaultAwakeFromLoad);
+ object.SetDirty();
+ }
+}
+
+Unity::Component* GetComponentWithScript (GameObject& go, int classID, MonoScriptPtr script)
+{
+
+ if (classID != ClassID(MonoBehaviour))
+ return go.QueryComponentT<Unity::Component>(classID);
+#if ENABLE_SCRIPTING
+ if (script == NULL)
+ return NULL;
+
+ ScriptingClassPtr compareKlass = script->GetClass();
+ if (compareKlass == NULL)
+ return NULL;
+
+ int count = go.GetComponentCount ();
+ for (int i=0;i<count;i++)
+ {
+ // We are looking only for MonoBehaviours
+ int clsID = go.GetComponentClassIDAtIndex (i);
+ if (!Object::IsDerivedFromClassID (clsID, ClassID (MonoBehaviour)))
+ continue;
+
+ MonoBehaviour& behaviour = static_cast<MonoBehaviour&> (go.GetComponentAtIndex (i));
+ ScriptingObjectPtr object = behaviour.GetInstance ();
+ if (object)
+ {
+ ScriptingClassPtr klass = scripting_object_get_class(object, GetScriptingTypeRegistry());
+ if (scripting_class_is_subclass_of (klass, compareKlass))
+ return &behaviour;
+ }
+ }
+#endif
+ return NULL;
+}
+
+void GetComponentsInChildren (const GameObject& gameObject, bool includeInactive, int classID, dynamic_array<Unity::Component*>& outComponents)
+{
+ // Find components on this game object
+ if (includeInactive || gameObject.IsActive())
+ {
+ for (int i=0;i<gameObject.GetComponentCount();i++)
+ {
+ if (Object::IsDerivedFromClassID(gameObject.GetComponentClassIDAtIndex(i), classID))
+ outComponents.push_back(&gameObject.GetComponentAtIndex(i));
+ }
+ }
+
+ // Recurse children
+ Transform* transform = gameObject.QueryComponent(Transform);
+ if (transform != NULL)
+ {
+ for (Transform::iterator i=transform->begin();i != transform->end();++i)
+ {
+ GameObject& child = (**i).GetGameObject ();
+ GetComponentsInChildren(child, includeInactive, classID, outComponents);
+ }
+ }
+}
+
+static void AddToBatchDeleteAndMakeUnpersistent (Object& object, BatchDelete& batchDelete)
+{
+ if (object.IsPersistent())
+ GetPersistentManager ().MakeObjectUnpersistent (object.GetInstanceID (), kDestroyFromFile);
+
+ batchDelete.objects[batchDelete.objectCount++] = &object;
+}
+
+static void DestroyGameObjectRecursive (GameObject& gameObject, BatchDelete& batchDelete)
+{
+ Assert(!gameObject.IsActive());
+ Assert(gameObject.IsDestroying());
+
+ Transform* transform = gameObject.QueryComponent (Transform);
+ if (transform)
+ {
+ for (Transform::iterator i=transform->begin();i!=transform->end();i++)
+ {
+ Transform& transform = **i;
+ DestroyGameObjectRecursive(*transform.GetGameObjectPtr(), batchDelete);
+ }
+ }
+
+ if (gameObject.IsActivating())
+ {
+ if (transform)
+ transform->RemoveFromParent();
+ ErrorStringObject("Cannot destroy GameObject while it is being activated or deactivated.", &gameObject);
+ return;
+ }
+
+ for (int i=0;i<gameObject.GetComponentCount();i++)
+ {
+ Unity::Component& com = gameObject.GetComponentAtIndex(i);
+ AddToBatchDeleteAndMakeUnpersistent (com, batchDelete);
+ }
+
+ AddToBatchDeleteAndMakeUnpersistent (gameObject, batchDelete);
+}
+
+static void PreDestroyRecursive (GameObject& gameObject, size_t* destroyedObjectCount)
+{
+ if (gameObject.IsActivating())
+ {
+ ErrorStringObject("Cannot destroy GameObject while it is being activated or deactivated.", &gameObject);
+ return;
+ }
+
+
+ // the callback is only called if the GameObject is
+ // really destroyed (not only removed from memory)
+ GameObject::InvokeDestroyedCallback(&gameObject);
+
+ if (!IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ gameObject.Deactivate(kWillDestroyGameObjectDeactivate);
+
+ gameObject.WillDestroyGameObject();
+ *destroyedObjectCount += 1 + gameObject.GetComponentCount();
+
+ Transform* transform = gameObject.QueryComponent (Transform);
+ if (transform)
+ {
+ for (Transform::iterator i=transform->begin();i!=transform->end();i++)
+ {
+ Transform& child = **i;
+ PreDestroyRecursive(child.GetGameObject(), destroyedObjectCount);
+ }
+ }
+}
+
+#if UNITY_EDITOR
+static void DisconnectPrefabInstanceRecursive (GameObject& gameObject)
+{
+ PrefabDestroyObjectCallback(gameObject);
+
+ Transform* transform = gameObject.QueryComponent (Transform);
+ if (transform == NULL)
+ return;
+
+ for (Transform::iterator i=transform->begin();i!=transform->end();i++)
+ {
+ Transform& child = **i;
+ DisconnectPrefabInstanceRecursive(child.GetGameObject());
+ }
+}
+#endif
+
+void DestroyTransformComponentAndChildHierarchy (Transform& transform)
+{
+ size_t objectCount = 0;
+ for (Transform::iterator i=transform.begin();i!=transform.end();i++)
+ {
+ Transform& child = **i;
+ child.GetGameObject().Deactivate(kWillDestroyGameObjectDeactivate);
+ PreDestroyRecursive(child.GetGameObject(), &objectCount);
+ }
+
+ // Remove transform from transform hierarchy
+ transform.RemoveFromParent();
+
+ BatchDelete batchDelete = CreateBatchDelete (objectCount);
+
+ for (Transform::iterator i=transform.begin();i!=transform.end();i++)
+ {
+ Transform& child = **i;
+ DestroyGameObjectRecursive(child.GetGameObject(), batchDelete);
+ }
+
+ CommitBatchDelete (batchDelete);
+}
+
+void DestroyGameObjectHierarchy (GameObject& gameObject)
+{
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ {
+ // Deactivate and mark is being destroyed recursively
+ // Send all necessary callbacks etc.
+ gameObject.Deactivate(kWillDestroyGameObjectDeactivate);
+ }
+
+ size_t objectCount = 0;
+ PreDestroyRecursive(gameObject, &objectCount);
+
+ // Remove transform from transform hierarchy
+ Transform* transform = gameObject.QueryComponent(Transform);
+ if (transform)
+ transform->RemoveFromParent();
+
+ BatchDelete batchDelete = CreateBatchDelete (objectCount);
+
+ // Destroy the objects (There should be no callbacks happening at this stage anymore)
+ DestroyGameObjectRecursive(gameObject, batchDelete);
+
+ CommitBatchDelete (batchDelete);
+}
+
+void DestroyObjectHighLevel (Object* object, bool forceDestroy)
+{
+ PROFILER_AUTO (gDestroyProfile, NULL)
+
+ if (object)
+ {
+ if (object->IsDerivedFrom (ClassID (Component)))
+ {
+ Unity::Component& component = *static_cast<Unity::Component*> (object);
+ MonoBehaviour* monoBehaviour = dynamic_pptr_cast<MonoBehaviour*> (object);
+
+ // MonoBehaviour needs per
+ if (monoBehaviour && monoBehaviour->IsDestroying())
+ {
+ ErrorString("Destroying object multiple times. Don't use DestroyImmediate on the same object in OnDisable or OnDestroy.");
+ return;
+ }
+
+
+ GameObject* gameObject = component.GetGameObjectPtr();
+ if (gameObject)
+ {
+ if (GetDisableImmediateDestruction())
+ {
+ ErrorStringObject ("Destroying components immediately is not permitted during physics trigger/contact, animation event callbacks or OnValidate. You must use Destroy instead.", object);
+ return;
+ }
+
+ if (gameObject->IsDestroying())
+ {
+ ErrorString("Destroying object multiple times. Don't use DestroyImmediate on the same object in OnDisable or OnDestroy.");
+ return;
+ }
+
+
+ if (gameObject->IsActivating())
+ {
+ ErrorStringObject("Cannot destroy Component while GameObject is being activated or deactivated.", gameObject);
+ return;
+ }
+
+ string error;
+ if (!forceDestroy && !CanRemoveComponent(component, &error))
+ {
+ ErrorStringObject (error, &component);
+ return;
+ }
+
+ PPtr<Unity::Component> componentPPtr = &component;
+
+ if (gameObject->IsActive ())
+ {
+ component.Deactivate (kWillDestroySingleComponentDeactivate);
+
+ // The game object might get destroyed during the OnDisable / OnDestroy callbacks, so don't rely on it.
+ if ((Unity::Component*)componentPPtr != &component)
+ return;
+ }
+
+ // Deleting a transform component is a special case, we have to destroy
+ // the entire child transform hierarchy.
+ // This is necessary to keep behaviour consistent for pre 3.2 content
+ // Starting with 3.2 we prevent this in CanRemoveComponent
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_2_a1) && component.GetClassID() == ClassID (Transform))
+ {
+ DestroyTransformComponentAndChildHierarchy (static_cast<Transform&> (component));
+
+ if ((Unity::Component*)componentPPtr != &component)
+ return;
+ }
+
+ component.WillDestroyComponent();
+
+ // The game object might get destroyed during the OnDisable / OnDestroy callbacks, so don't rely on it.
+ if ((Unity::Component*)componentPPtr != &component)
+ return;
+
+ int componentIndex = gameObject->GetComponentIndex(&component);
+ if (componentIndex != -1)
+ component.GetGameObject().RemoveComponentAtIndex (componentIndex);
+ else
+ {
+ ErrorString("Component Removing internal failure");
+ }
+ }
+ else
+ {
+ component.WillDestroyComponent();
+ }
+
+ #if UNITY_EDITOR
+ PrefabDestroyObjectCallback(component);
+ #endif
+
+ DestroySingleObject (&component);
+ }
+ else if (object->IsDerivedFrom (ClassID (GameObject)))
+ {
+ if (GetDisableImmediateDestruction())
+ {
+ ErrorStringObject ("Destroying GameObjects immediately is not permitted during physics trigger/contact, animation event callbacks or OnValidate. You must use Destroy instead.", object);
+ return;
+ }
+
+ GameObject& gameObject = *static_cast<GameObject*>(object);
+ if (gameObject.IsDestroying())
+ {
+ ErrorString("Destroying object multiple times. Don't use DestroyImmediate on the same object in OnDisable or OnDestroy.");
+ return;
+ }
+
+ if (gameObject.IsActivating())
+ {
+ ErrorStringObject("Cannot destroy GameObject while it is being activated or deactivated.", &gameObject);
+ return;
+ }
+
+ Transform* parent = gameObject.QueryComponent(Transform);
+ if (parent)
+ {
+ parent = parent->GetParent();
+ if (parent && parent->GetGameObject().IsActivating())
+ {
+ ErrorStringObject("Cannot destroy GameObject while it is being activated or deactivated.", &gameObject);
+ return;
+ }
+ }
+ #if UNITY_EDITOR
+ DisconnectPrefabInstanceRecursive (gameObject);
+ #endif
+
+ DestroyGameObjectHierarchy(gameObject);
+ }
+ else if (object->IsDerivedFrom (ClassID(AssetBundle)))
+ {
+ ErrorStringObject ("Destroying AssetBundle directly is not permitted.\nUse AssetBundle.UnloadBundle to destroy an asset bundle.", object);
+ return;
+ }
+ else
+ {
+ DestroySingleObject(object);
+ }
+ }
+}
+
+std::string UnityObjectToString (Object *object)
+{
+ #if ENABLE_SCRIPTING
+ std::string type;
+
+ if (object == NULL) return "null";
+
+ if (object->GetClassID() == ClassID(MonoBehaviour))
+ type = dynamic_pptr_cast<MonoBehaviour*>(object)->GetScriptFullClassName();
+ else
+ type = "UnityEngine."+object->GetClassName();
+
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_2_a1))
+ return Format("%s (%s)", object->GetName(), type.c_str());
+ else
+ return type;
+ #else
+ return "UnityObject";
+ #endif
+}
+
+Unity::Component* FindAncestorComponentExactTypeImpl (Unity::GameObject& gameObject, int classId)
+{
+ Transform* parent = gameObject.QueryComponent (Transform);
+ while (parent != NULL)
+ {
+ Unity::Component* component = parent->GetGameObject().QueryComponentExactTypeImplementation (classId);
+ if (component != NULL)
+ return component;
+
+ parent = parent->GetParent ();
+ }
+ return NULL;
+}
+
+Unity::Component* FindAncestorComponentImpl (Unity::GameObject& gameObject, int classId)
+{
+ Transform* parent = gameObject.QueryComponent (Transform);
+ while (parent != NULL)
+ {
+ Unity::Component* component = parent->GetGameObject().QueryComponentImplementation (classId);
+ if (component != NULL)
+ return component;
+
+ parent = parent->GetParent ();
+ }
+ return NULL;
+}
+
+#if ENABLE_SCRIPTING
+int ExtractTagThrowing(ICallString& name)
+{
+ string tagString = name;
+ int tag = StringToTag (tagString);
+ if (tag != kUndefinedTag)
+ return tag;
+ else
+ {
+ Scripting::RaiseMonoException ("Tag: %s is not defined!", tagString.c_str());
+ return tag;
+ }
+}
+#endif
diff --git a/Runtime/Misc/GameObjectUtility.h b/Runtime/Misc/GameObjectUtility.h
new file mode 100644
index 0000000..91ed672
--- /dev/null
+++ b/Runtime/Misc/GameObjectUtility.h
@@ -0,0 +1,91 @@
+#ifndef GAMEOBJECTUTILITY_H
+#define GAMEOBJECTUTILITY_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Mono/MonoScript.h"
+
+struct ICallString;
+
+class MonoBehaviour;
+class Camera;
+
+/// Adds a component by classID or className to the game object.
+/// This method does several checks and returns null if any of them fail.
+/// - Class has to be derived from Component
+/// - Class has to be not already added to the game object or be allowed to be added multiple times (ComponentRequirement.cpp)
+/// On failure this method returns NULL and if error != null an error string.
+/// This method automatically orders filters by their sort priority.
+/// * Default properties are only setup in edit mode
+Unity::Component* AddComponentInternal (GameObject& go, int classID, MonoScriptPtr script, std::set<ScriptingClassPtr> &processed, std::string* error = NULL);
+Unity::Component* AddComponent (GameObject& go, int classID, MonoScriptPtr script, std::string* error = NULL);
+Unity::Component* AddComponent (GameObject& go, const char* className, std::string* error = NULL);
+Unity::Component* AddComponentUnchecked (GameObject& go, int classID, MonoScriptPtr script, std::string* error);
+
+/// Creates a game object with name. Add's a null terminated list of components by className.
+/// Errors when a component can't be added!
+Unity::GameObject& CreateGameObject (const std::string& name, const char* componentName, ...);
+Unity::GameObject& CreateGameObjectWithVAList (const std::string& name, const char* componentName, va_list componentList);
+Unity::GameObject& CreateGameObjectWithHideFlags (const string& name, bool isActive, int flags, const char* componentName, ...);
+
+/// Adds a null terminated list of components by className.
+/// Errors when a component can't be added!
+void AddComponents (Unity::GameObject& go, const char* componentName, ...);
+
+/// Checks if a component can be removed from its game object.
+/// Does error checking so that
+/// we don't remove a component which is required by another component!
+bool CanRemoveComponent(Unity::Component& component, std::string* error);
+/// See whether a component can be replaced by another
+/// Note: currently it only specifically handles requirements from RequireComponent attributes
+bool CanReplaceComponent(Unity::Component& component, int replacementClassID, std::string* error);
+
+bool CanAddComponent (Unity::GameObject& go, int classID);
+
+EXPORT_COREMODULE void DestroyObjectHighLevel (Object* object, bool forceDestroy = false);
+void DestroyTransformComponentAndChildHierarchy (Transform& transform);
+
+/// On return all GameObject's with the specified tag are added to the array.
+/// Only active game objects are returned
+void FindGameObjectsWithTag (UInt32 tag, std::vector<Unity::GameObject*>& gos);
+Camera* FindMainCamera ();
+
+/// Returns the first game object with the specified tag found!
+/// Only active game objects are returned
+Unity::GameObject* FindGameObjectWithTag (UInt32 tag);
+
+///
+Unity::Component* FindAncestorComponentExactTypeImpl (Unity::GameObject& gameObject, int classId);
+Unity::Component* FindAncestorComponentImpl (Unity::GameObject& gameObject, int classId);
+
+template<class T>
+T* FindAncestorComponent (Unity::GameObject& gameObject)
+{
+ if (T::IsSealedClass())
+ return static_cast<T*> (FindAncestorComponentExactTypeImpl (gameObject, T::GetClassIDStatic()));
+ else
+ return static_cast<T*> (FindAncestorComponentImpl (gameObject, T::GetClassIDStatic()));
+}
+
+/// Sends the message to all active game objects
+void SendMessageToEveryone(MessageIdentifier message, MessageData msgData);
+bool UnloadGameObjectHierarchy (Unity::GameObject& go);
+void UnloadGameObjectAndComponents (Unity::GameObject& go);
+
+EXPORT_COREMODULE void SmartResetObject (Object& com);
+
+Unity::Component* GetComponentWithScript (Unity::GameObject& go, int classID, MonoScriptPtr script);
+
+std::string UnityObjectToString (Object *object);
+
+/// Returns all components in this and any child game objects.
+/// If includeInactive, in active components will be returned, otherwise only active components will be returned.
+void GetComponentsInChildren (const GameObject& gameObject, bool includeInactive, int classID, dynamic_array<Unity::Component*>& outComponents);
+
+
+typedef void AddComponentCallbackFunction (Unity::Component& com);
+void RegisterAddComponentCallback (AddComponentCallbackFunction* callback);
+
+int ExtractTagThrowing (ICallString& name);
+
+#endif
diff --git a/Runtime/Misc/GameObjectUtilityTests.cpp b/Runtime/Misc/GameObjectUtilityTests.cpp
new file mode 100644
index 0000000..a8f3e3c
--- /dev/null
+++ b/Runtime/Misc/GameObjectUtilityTests.cpp
@@ -0,0 +1,118 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Testing/Testing.h"
+
+class GameObjectFixture
+{
+protected:
+ GameObject* NewGameObject()
+ {
+ return NEW_OBJECT_RESET_AND_AWAKE(GameObject);
+ }
+};
+
+SUITE (GameObjectUtilityTests)
+{
+ TEST (CreateGameObjectTest)
+ {
+ const char* name = "TestGameObject";
+ GameObject& go = CreateGameObject (name, "Transform", "MeshRenderer", NULL);
+
+ CHECK_EQUAL (go.GetName(), name);
+ CHECK_EQUAL (go.GetComponentCount(), 2);
+ CHECK (go.IsActive());
+
+ DestroyObjectHighLevel(&go);
+ }
+
+ TEST (CreateGameObjectWithFlagsTest)
+ {
+ int flags = 2;
+ GameObject& go = CreateGameObjectWithHideFlags ("TestGameObject", true, flags, NULL);
+
+ CHECK (go.IsActive());
+ CHECK_EQUAL (go.GetHideFlags(), flags);
+
+ DestroyObjectHighLevel(&go);
+
+ GameObject& go1 = CreateGameObjectWithHideFlags ("TestGameObject", false, flags, NULL);
+
+ CHECK (!go1.IsActive());
+ CHECK_EQUAL (go1.GetHideFlags(), flags);
+
+ DestroyObjectHighLevel(&go1);
+ }
+
+ TEST_FIXTURE (GameObjectFixture, AddComponentsTest)
+ {
+ GameObject* go = NewGameObject();
+
+ AddComponent(*go, "Transform", NULL);
+ CHECK_EQUAL (go->GetComponentCount(), 1);
+
+ AddComponent(*go, ClassID(MeshRenderer), NULL, NULL);
+ CHECK_EQUAL (go->GetComponentCount(), 2);
+
+ // Transform and MeshRenderer don't support multiple inclusion.
+ EXPECT (Error, "Can't add component 'Transform'");
+ EXPECT (Error, "Can't add component 'MeshRenderer'");
+ AddComponents(*go, "Transform", "MeshRenderer", "Skybox", NULL);
+ CHECK_EQUAL (go->GetComponentCount(), 3);
+
+#if ENABLE_SPRITES
+ // SpriteRenderer can't be added to a GO with a MeshRenderer - conflicting classes.
+ EXPECT (Error, "Can't add component 'SpriteRenderer'");
+ AddComponents(*go, "SpriteRenderer", NULL);
+ CHECK_EQUAL (go->GetComponentCount(), 3);
+#endif
+
+ // Skybox supports multiple inclusion.
+ AddComponent(*go, ClassID(Skybox), NULL);
+ CHECK_EQUAL (go->GetComponentCount(), 4);
+
+ DestroyObjectHighLevel(go);
+ }
+
+ TEST_FIXTURE (GameObjectFixture, CanAddorRemoveComponentTest)
+ {
+ GameObject* go = NewGameObject();
+
+ AddComponents(*go, "Transform", "MeshFilter", "Skybox", NULL);
+
+ CHECK (!CanAddComponent(*go, ClassID(Transform)));
+ CHECK (CanAddComponent(*go, ClassID(Skybox)));
+
+ // Refer to the InitComponentRequirements() function for details.
+ CHECK ( !CanRemoveComponent(go->GetComponentT<Transform>(ClassID(Transform)), NULL) );
+ DestroyObjectHighLevel(go);
+ }
+
+ TEST_FIXTURE (GameObjectFixture, FindWithTagTest)
+ {
+ GameObject* go = NewGameObject();
+ int tag = 2;
+
+ CHECK (FindGameObjectWithTag(tag) == NULL);
+
+ go->SetTag(tag);
+ CHECK (FindGameObjectWithTag(tag) == NULL);
+
+ go->Activate();
+ CHECK (FindGameObjectWithTag(tag) != NULL);
+
+ GameObject* go1 = NewGameObject();
+ go1->Activate();
+ go1->SetTag(tag);
+
+ std::vector<Unity::GameObject*> gos;
+ FindGameObjectsWithTag(tag, gos);
+ CHECK_EQUAL(gos.size(), 2);
+
+ DestroyObjectHighLevel(go);
+ }
+}
+
+#endif
diff --git a/Runtime/Misc/GarbageCollectSharedAssets.cpp b/Runtime/Misc/GarbageCollectSharedAssets.cpp
new file mode 100644
index 0000000..9b75d14
--- /dev/null
+++ b/Runtime/Misc/GarbageCollectSharedAssets.cpp
@@ -0,0 +1,1188 @@
+#include "UnityPrefix.h"
+#if !ENABLE_OLD_GARBAGE_COLLECT_SHARED_ASSETS
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Serialize/TransferFunctions/RemapPPtrTransfer.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Mono/Coroutine.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "GameObjectUtility.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Filters/Mesh/MeshRenderer.h"
+#include "Runtime/Dynamics/MeshCollider.h"
+#include "Runtime/Filters/Mesh/LodMeshFilter.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/CollectProfilerStats.h"
+#include "Runtime/Profiler/TimeHelper.h"
+#include "BatchDeleteObjects.h"
+#include "Runtime/Interfaces/IPhysics.h"
+
+/// All these includes are used by AddManagerRoots
+#if UNITY_EDITOR
+#include "Editor/Src/AssetPipeline/AssetDatabase.h"
+#include "Editor/Src/AssetPipeline/AssetImporter.h"
+#include "Editor/Src/GUIDPersistentManager.h"
+#include "Editor/Src/AssetServer/ASCache.h"
+#include "Editor/Src/EditorBuildSettings.h"
+#include "Editor/Src/EditorUserBuildSettings.h"
+#include "Editor/Src/EditorSettings.h"
+#include "Editor/Src/EditorUserSettings.h"
+#include "Editor/Src/AssetPipeline/AssetInterface.h"
+#include "Editor/Src/GUIDPersistentManager.h"
+#include "Editor/Src/AssetPipeline/AssetPathUtilities.h"
+#include "Editor/Src/Application.h"
+#include "Editor/Src/HierarchyState.h"
+#include "Editor/Src/InspectorExpandedState.h"
+#include "Editor/Src/AnnotationManager.h"
+#include "Editor/Src/EditorExtensionImpl.h"
+#include "Runtime/Serialize/SerializedFile.h"
+#include "Editor/Src/EditorAssetGarbageCollectManager.h"
+#endif
+#include "Runtime/Allocator/MemoryManager.h"
+
+#include "Runtime/Scripting/Scripting.h"
+
+PROFILER_INFORMATION(gGarbageCollectSharedAssetsProfile, "GarbageCollectAssetsProfile", kProfilerLoading)
+PROFILER_INFORMATION(gGCFindLiveObjects, "GC.FindLiveObjects", kProfilerLoading)
+PROFILER_INFORMATION(gGCBuildLiveObjectMaps, "GC.BuildLiveObjectMaps", kProfilerLoading)
+PROFILER_INFORMATION(gGCMarkDependencies, "GC.MarkDependencies", kProfilerLoading)
+PROFILER_INFORMATION(gGCDeletedUnusedAssets, "GC.DeleteUnusedAssets", kProfilerLoading)
+PROFILER_INFORMATION(gGCOnDestroyCallback, "ScriptableObject.OnDestroy", kProfilerLoading)
+
+#if UNITY_EDITOR
+static int gPreventGarbageCollectionOfInstanceID = 0;
+#endif
+
+struct ObjectState
+{
+ Object* object;
+ UInt32 classID : 29;
+ UInt32 marked : 1;
+ UInt32 isPersistent : 1;
+};
+
+
+struct ObjectHashFunctor
+{
+ inline size_t operator()(const Object* x) const
+ {
+ return (size_t)x / 32;
+ }
+};
+
+
+#define ASSET_REMAP_TABLE 1
+#define USE_MONO_LIVENESS ENABLE_MONO
+
+// Cap on how much memory we can use for the dense remap table for assets
+enum { kMaximumAssetRemapTableSize = 250 * 1024 };
+
+struct GarbageCollectorState;
+
+struct GenericSlowGarbageCollector : public GenerateIDFunctor
+{
+ GarbageCollectorState* gcState;
+
+ GenericSlowGarbageCollector () { }
+ virtual ~GenericSlowGarbageCollector () {}
+
+ inline void ProcessReference (SInt32 oldInstanceID);
+
+ // General purpose GarbageCollector callback
+ virtual SInt32 GenerateInstanceID (SInt32 oldInstanceID, TransferMetaFlags metaFlag);
+};
+
+struct GarbageCollectorState
+{
+ typedef pair<const int, int> InstanceIDToIndexPair;
+
+#if ASSET_REMAP_TABLE
+ typedef dense_hash_map<int, int, InstanceIDHashFunctor, std::equal_to<int>, STL_ALLOCATOR( kMemTempAlloc, InstanceIDToIndexPair )> InstanceIDToIndex;
+#else
+ typedef map<int, int> InstanceIDToIndex;
+#endif
+ InstanceIDToIndex instanceIDToIndex;
+ dynamic_array<UInt32> assetRemapTable;
+
+ dynamic_array<ObjectState> liveObjects;
+ dynamic_array<UInt32> needsProcessing;
+
+ int originalObjectCount;
+
+ void* livenessState;
+
+ bool followMonoReferences;
+
+ #if ENABLE_MEM_PROFILER
+ bool alwaysAddToNeedsProcessing;
+ #endif
+
+ RemapPPtrTransfer genericSlowTransfer;
+ GenericSlowGarbageCollector genericCollector;
+
+ GarbageCollectorState ()
+ : assetRemapTable (kMemTempAlloc),
+ liveObjects (kMemTempAlloc),
+ needsProcessing (kMemTempAlloc),
+ genericSlowTransfer (kDontRequireAllMetaFlags | kPerformUnloadDependencyTracking, false)
+ {
+ genericCollector.gcState = this;
+#if ENABLE_MEM_PROFILER
+ alwaysAddToNeedsProcessing = false;
+#endif
+ genericSlowTransfer.SetGenerateIDFunctor (&genericCollector);
+ }
+};
+
+
+///@TODO: Make a function that can let us find all classes that have references
+static bool DoesClassIDHaveReferences (int classID)
+{
+#if UNITY_EDITOR
+
+ bool classHasNoReferences =
+ classID == ClassID(AssetDatabase) ||
+ classID == ClassID(HierarchyState) ||
+ classID == ClassID(GUIDSerializer) ||
+ classID == ClassID(AssetServerCache);
+
+ if(classHasNoReferences)
+ return false;
+
+#endif
+
+ return
+ classID != ClassID (ScriptMapper) &&
+ classID != ClassID (MonoScript) &&
+ classID != ClassID (NetworkManager) &&
+ classID != ClassID (ResourceManager) &&
+ classID != ClassID (PreloadData) &&
+ classID != ClassID (Texture) &&
+ classID != ClassID (Texture2D) &&
+ classID != ClassID (Texture3D) &&
+ classID != ClassID (Cubemap) &&
+ classID != ClassID (WebCamTexture) &&
+ classID != ClassID (RenderTexture) &&
+ classID != ClassID (AssetBundle) &&
+ classID != ClassID (Mesh) &&
+ classID != ClassID (TagManager);
+}
+
+static void MarkManagedStaticVariableRoots (GarbageCollectorState& gcState);
+static void FindAllLiveObjects (GarbageCollectorState& gcState);
+static void MarkSceneRootsAndReduceLiveObjects (GarbageCollectorState& gcState);
+static void MarkManagerRoots (GarbageCollectorState& gcState);
+static void MarkSelectedObjectsAsRoots (GarbageCollectorState& gcState);
+static void MarkAllDependencies (GarbageCollectorState& gcState);
+static void CleanupUnusedObjects (GarbageCollectorState& gcState);
+static void ValidateNoObjectsWereLoaded (GarbageCollectorState& state);
+static void CreateObjectToIndexMappingFromNonRootObjects (GarbageCollectorState& gcState);
+static void CleanupOtherUnusedMemory (GarbageCollectorState& gcState);
+static void BeginLivenessChecking( GarbageCollectorState& gcState);
+static void EndLivenessChecking( GarbageCollectorState& gcState);
+
+
+// Rename
+void GarbageCollectSharedAssets (bool monoReferences)
+{
+ PROFILER_AUTO(gGarbageCollectSharedAssetsProfile, NULL);
+
+ ABSOLUTE_TIME totalTime;
+ ABSOLUTE_TIME findAllLiveObjectsTime;
+ ABSOLUTE_TIME buildLiveObjectMapTime;
+ ABSOLUTE_TIME markTime;
+ ABSOLUTE_TIME unloadTime;
+
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+ // Gab: ManagedLivenessAnalysis needs to be reset every time before executing the liveness check.
+ static ScriptingInvocation resetManagedAnalysis(GetScriptingMethodRegistry().GetMethod("UnityEngine.Serialization","ManagedLivenessAnalysis","ResetState"));
+ resetManagedAnalysis.Invoke();
+#endif
+
+ totalTime = START_TIME;
+ int originalObjectCount = Object::GetLoadedObjectCount();
+#if ENABLE_PROFILER
+ printf_console("System memory in use before: %s.\n", FormatBytes(GetUsedHeapSize()).c_str());
+#endif
+ // This is essentially a Mark & Sweep Garbage Collector.
+
+ GarbageCollectorState state;
+
+ state.followMonoReferences = monoReferences;
+ state.originalObjectCount = originalObjectCount;
+
+ findAllLiveObjectsTime = START_TIME;
+
+ //@TODO: This stage can easily be jobified
+ // Fills all live objects and extracts their classID's for use by the marking algorithm
+ FindAllLiveObjects(state);
+ MarkSceneRootsAndReduceLiveObjects (state);
+
+ findAllLiveObjectsTime = ELAPSED_TIME(findAllLiveObjectsTime);
+
+ buildLiveObjectMapTime = START_TIME;
+ // Create mapping to quickly look up all object references
+ CreateObjectToIndexMappingFromNonRootObjects(state);
+ buildLiveObjectMapTime = ELAPSED_TIME(buildLiveObjectMapTime);
+
+ markTime = START_TIME;
+
+ MarkManagerRoots (state);
+ MarkSelectedObjectsAsRoots (state);
+
+ ValidateNoObjectsWereLoaded (state);
+
+ {
+ // Once we call BeginLivenessChecking we can not use the profiler because it might allocate memory.
+ PROFILER_AUTO(gGCMarkDependencies, NULL);
+
+ BeginLivenessChecking(state);
+
+ // Mark static variables
+ MarkManagedStaticVariableRoots (state);
+ // Process roots to find all referenced objects
+ MarkAllDependencies (state);
+
+ EndLivenessChecking(state);
+ }
+ markTime = ELAPSED_TIME(markTime);
+
+ ValidateNoObjectsWereLoaded (state);
+
+ if (state.originalObjectCount != Object::GetLoadedObjectCount())
+ {
+ ErrorString("UnloadUnusedAssets incorrect caused some assets to load. This can easily cause deadlocks or crashes.");
+ }
+
+ unloadTime = START_TIME;
+
+ // Cleanup all objects not marked as dependencies or roots
+ CleanupUnusedObjects (state);
+
+ unloadTime = ELAPSED_TIME(unloadTime);
+
+ int unloadedObjects = originalObjectCount - Object::GetLoadedObjectCount();
+
+ // Unload PersistentManager memory that is not needed
+ CleanupOtherUnusedMemory (state);
+
+ totalTime = ELAPSED_TIME(totalTime);
+
+#if ENABLE_PROFILER
+ printf_console("System memory in use after: %s.\n", FormatBytes(GetUsedHeapSize()).c_str());
+#endif
+ printf_console("\nUnloading %d unused Assets to reduce memory usage. Loaded Objects now: %d.\n", unloadedObjects, (int)Object::GetLoadedObjectCount());
+ printf_console("Total: %f ms (FindLiveObjects: %f ms CreateObjectMapping: %f ms MarkObjects: %f ms DeleteObjects: %f ms)\n\n",
+ AbsoluteTimeToMilliseconds(totalTime), AbsoluteTimeToMilliseconds(findAllLiveObjectsTime), AbsoluteTimeToMilliseconds(buildLiveObjectMapTime), AbsoluteTimeToMilliseconds(markTime), AbsoluteTimeToMilliseconds(unloadTime) );
+
+ #if UNITY_EDITOR
+ EditorAssetGarbageCollectManager::Get()->SetPostCollectMemoryUsage();
+ #endif
+}
+
+
+#if ASSET_REMAP_TABLE
+static void CreateObjectToIndexMappingFromNonRootObjects (GarbageCollectorState& gcState)
+{
+ PROFILER_AUTO(gGCBuildLiveObjectMaps, NULL);
+
+ gcState.instanceIDToIndex.set_empty_key (-1);
+ gcState.instanceIDToIndex.set_deleted_key (-2);
+
+ int largestInstanceID = 0;
+
+ for (int i=0;i<gcState.liveObjects.size();++i)
+ {
+ ObjectState& state = gcState.liveObjects[i];
+ if (state.marked == 0)
+ {
+ int instanceID = state.object->GetInstanceID();
+
+ if (instanceID > 0)
+ {
+ largestInstanceID = std::max<SInt32>(largestInstanceID, instanceID);
+ }
+ else
+ {
+ gcState.instanceIDToIndex.insert(std::make_pair(instanceID, i));
+ }
+ }
+ }
+
+ // Cap the memory of dense assetRemapTable
+ if (largestInstanceID < (kMaximumAssetRemapTableSize / sizeof(UInt32)))
+ {
+ gcState.assetRemapTable.resize_initialized(largestInstanceID + 1, -1);
+ for (int i=0;i<gcState.liveObjects.size();++i)
+ {
+ ObjectState& state = gcState.liveObjects[i];
+ if (state.marked == 0)
+ {
+ int instanceID = state.object->GetInstanceID();
+
+ if (instanceID > 0)
+ gcState.assetRemapTable[instanceID] = i;
+ }
+ }
+ }
+ // Use hashtable for all instance ids as a fallback
+ else
+ {
+ for (int i=0;i<gcState.liveObjects.size();++i)
+ {
+ ObjectState& state = gcState.liveObjects[i];
+ if (state.marked == 0)
+ {
+ int instanceID = state.object->GetInstanceID();
+
+ if (instanceID > 0)
+ gcState.instanceIDToIndex.insert(std::make_pair(instanceID, i));
+ }
+ }
+ }
+}
+
+#else
+static void CreateObjectToIndexMappingFromNonRootObjects (GarbageCollectorState& gcState)
+{
+ ///@TODO: Should we recreate gcState.liveObjects here? There is no reason to ever visit objects that are already marked at this point...
+ //// Try it...
+
+ for (int i=0;i<gcState.liveObjects.size();++i)
+ {
+ ObjectState& state = gcState.liveObjects[i];
+ if (state.marked == 0)
+ {
+ int instanceID = state.object->GetInstanceID();
+
+ gcState.instanceIDToIndex.insert(make_pair(instanceID, i));
+ }
+ }
+}
+#endif
+
+
+static inline int LookupInstanceIDIndex (int instanceID, GarbageCollectorState& gcState)
+{
+ if (instanceID == 0)
+ return -1;
+
+#if ASSET_REMAP_TABLE
+
+ size_t size = gcState.assetRemapTable.size();
+ if (instanceID > 0 && size != 0)
+ {
+ if (instanceID < size)
+ return gcState.assetRemapTable[instanceID];
+ else
+ return -1;
+ }
+#endif
+ else
+ {
+ GarbageCollectorState::InstanceIDToIndex::const_iterator found = gcState.instanceIDToIndex.find(instanceID);
+ if (found == gcState.instanceIDToIndex.end())
+ return -1;
+ else
+ return found->second;
+ }
+}
+
+static inline int LookupObjectIndex (const Object& object, GarbageCollectorState& gcState)
+{
+ return LookupInstanceIDIndex (object.GetInstanceID(), gcState);
+}
+
+
+static void MarkIndexAsRoot (int index, GarbageCollectorState& gcState)
+{
+ ObjectState& state = gcState.liveObjects[index];
+
+ Assert(state.marked == 0);
+ state.marked = 1;
+
+ // If we have references to other objects in the marked object -> then we need to process it
+ // No processing is needed if the class has no references
+ // (Eg. a texture has no references to other objects, thus it does not have to be processed)
+ bool addToNeedsProcessing = DoesClassIDHaveReferences (state.classID);
+ Assert(state.object->ShouldIgnoreInGarbageDependencyTracking() == !addToNeedsProcessing);
+
+ #if ENABLE_MEM_PROFILER
+ // When extracting references for the memory profiler we use the needsProcessing array as the object which have been marked.
+ // Thus we can not use the fastpath for objects that have no references to other objects
+ addToNeedsProcessing |= gcState.alwaysAddToNeedsProcessing;
+ #endif
+
+ if (addToNeedsProcessing)
+ {
+ gcState.needsProcessing.push_back(index);
+ }
+}
+
+static void MarkObjectAsRoot (const Object& object, GarbageCollectorState& gcState)
+{
+ int index = LookupObjectIndex (object, gcState);
+ if (index != -1)
+ MarkIndexAsRoot(index, gcState);
+}
+
+static void MarkInstanceIDAsRoot (int instanceID, GarbageCollectorState& gcState)
+{
+ int index = LookupInstanceIDIndex (instanceID, gcState);
+ if (index != -1)
+ {
+ if (gcState.liveObjects[index].marked == 0)
+ MarkIndexAsRoot(index, gcState);
+ }
+}
+
+static void MarkObjectAsRootUnknownMarkState (const Object& object, GarbageCollectorState& gcState)
+{
+ int index = LookupObjectIndex (object, gcState);
+ if (index != -1 && gcState.liveObjects[index].marked == 0)
+ MarkIndexAsRoot(index, gcState);
+}
+
+static void MarkObjectAsRootCheckNull (const Object* object, GarbageCollectorState& gcState)
+{
+ if (object != NULL)
+ MarkObjectAsRootUnknownMarkState (*object, gcState);
+}
+
+#if USE_MONO_LIVENESS
+static void RegisterFilteredObjectCallback(gpointer* arr, int count, void* userdata)
+{
+ GarbageCollectorState* gcStatePtr = (GarbageCollectorState*)userdata;
+ for (int i = 0; i < count ;i++)
+ {
+ SInt32 instanceID = Scripting::GetInstanceIDFromScriptingWrapper((MonoObject*)arr[i]);
+ MarkInstanceIDAsRoot(instanceID, *gcStatePtr);
+ }
+}
+#endif
+
+static void BeginLivenessChecking( GarbageCollectorState& gcState)
+{
+ if(!gcState.followMonoReferences)
+ return;
+#if USE_MONO_LIVENESS
+ #if ENABLE_MEMORY_MANAGER
+ GetMemoryManager().DisallowAllocationsOnThisThread();
+ #endif
+ gcState.livenessState = mono_unity_liveness_calculation_begin(ScriptingClassFor(Object), gcState.liveObjects.size(), RegisterFilteredObjectCallback, (void*)&gcState);
+#endif
+}
+
+static void EndLivenessChecking( GarbageCollectorState& gcState)
+{
+ if(!gcState.followMonoReferences)
+ return;
+#if USE_MONO_LIVENESS
+ mono_unity_liveness_calculation_end(gcState.livenessState);
+ #if ENABLE_MEMORY_MANAGER
+ GetMemoryManager().ReallowAllocationsOnThisThread();
+ #endif
+#endif
+}
+
+static void MarkManagedStaticVariableRoots (GarbageCollectorState& gcState)
+{
+ if(!gcState.followMonoReferences)
+ return;
+
+#if USE_MONO_LIVENESS
+ ValidateNoObjectsWereLoaded (gcState);
+ mono_unity_liveness_calculation_from_statics (gcState.livenessState);
+#endif
+}
+
+#if UNITY_EDITOR
+void SetPreventGarbageCollectionOfAsset (int instanceID)
+{
+ Assert(gPreventGarbageCollectionOfInstanceID == 0);
+ gPreventGarbageCollectionOfInstanceID = instanceID;
+}
+
+void ClearPreventGarbageCollectionOfAsset (int instanceID)
+{
+ Assert(gPreventGarbageCollectionOfInstanceID == instanceID);
+ gPreventGarbageCollectionOfInstanceID = 0;
+}
+#endif
+
+
+static void MarkSelectedObjectsAsRoots (GarbageCollectorState& gcState)
+{
+#if UNITY_EDITOR
+ // Add all selected objects as GC Roots because they are being shown in the inspector.
+ set<int> selection = GetSceneTracker().GetSelectionID ();
+ for (set<int>::iterator i=selection.begin();i!=selection.end();i++)
+ MarkInstanceIDAsRoot(*i, gcState);
+#endif
+}
+
+
+static void MarkManagerRoots (GarbageCollectorState& gcState)
+{
+ // All managers are roots (except script mapper)
+ for (int i=0;i<ManagerContext::kManagerCount;i++)
+ {
+ if (GetManagerPtrFromContext(i) != NULL)
+ MarkObjectAsRootUnknownMarkState (*GetManagerPtrFromContext(i), gcState);
+ }
+
+ #if UNITY_EDITOR
+ MarkObjectAsRootUnknownMarkState (AssetDatabase::Get(), gcState);
+ MarkObjectAsRootUnknownMarkState (AssetServerCache::Get(), gcState);
+ MarkObjectAsRootUnknownMarkState (GetEditorBuildSettings(), gcState);
+ MarkObjectAsRootUnknownMarkState (GetEditorUserBuildSettings(), gcState);
+ MarkObjectAsRootUnknownMarkState (GetEditorSettings(), gcState);
+ MarkObjectAsRootUnknownMarkState (GetEditorUserSettings(), gcState);
+ MarkObjectAsRootCheckNull (AssetInterface::Get().GetGUIDSerializer(), gcState);
+ MarkObjectAsRootCheckNull (GetProjectWindowHierarchyStateIfLoaded (), gcState);
+ MarkObjectAsRootUnknownMarkState (GetInspectorExpandedState (), gcState);
+ MarkObjectAsRootUnknownMarkState (GetAnnotationManager (), gcState);
+
+ if (gPreventGarbageCollectionOfInstanceID != 0)
+ MarkInstanceIDAsRoot(gPreventGarbageCollectionOfInstanceID, gcState);
+
+ #endif
+
+ ValidateNoObjectsWereLoaded (gcState);
+}
+
+#if UNITY_EDITOR
+bool ShouldPersistentDirtyObjectBeKeptAlive (int instanceID)
+{
+ // The Object is not mapped to disk
+ SerializedObjectIdentifier identifier;
+ if (!GetPersistentManager().InstanceIDToSerializedObjectIdentifier(instanceID, identifier))
+ {
+ // #if !UNITY_RELEASE
+ // WarningString("Persistent object is known in persistent manager will unload Might happen due to Assetbundle.Unload(false)");
+ // #endif
+ return false;
+ }
+
+ // Cached asset file assets should just be unloaded. They contain nothing that can not be reloaded.
+ // Eg a texture in an imported .psd file, while scripts might call SetDirty on it, it doesn't actually ever get serialized back.
+ // Because the only thing that save an texture is the asset importer. Any modifications done in memory will never be saved.
+ // -> Thus a texture that is marked dirty can be unloaded
+ // -> But a MetaData object or AssetImporter settings object (eg. class TextureImporter) should not be unloaded if it is marked dirty
+ FileIdentifier file = GetGUIDPersistentManager().PathIDToFileIdentifierInternal(identifier.serializedFileIndex);
+ if (file.type == FileIdentifier::kMetaAssetType)
+ {
+ if (identifier.localIdentifierInFile == kAssetMetaDataFileID)
+ {
+ // We can't have this warning since changing an import setting and then starting a long import operation will trigger it.
+ //WarningString(Format("ImportSettings '%s' has been modified but AssetDatabase.ImportAsset has not been called. Please fix the scripts code or Import the asset manually.", GetAssetPathFromInstanceID(instanceID).c_str()));
+ return true;
+ }
+ else if (identifier.localIdentifierInFile == kAssetImporterFileID)
+ {
+ // We can't have this warning since changing an import setting and then starting a long import operation will trigger it.
+ //WarningString(Format("ImportSettings '%s' has been modified but AssetDatabase.ImportAsset has not been called. Please fix the scripts code or Import the asset manually.", GetAssetPathFromInstanceID(instanceID).c_str()));
+ return true;
+ }
+
+ return false;
+ }
+ // Any other assets are will not be unloaded when dirtied.
+ // Eg. scriptmapper, guidmapper, builtin resources...
+ else
+ {
+ return true;
+ }
+}
+#endif
+
+enum GCRootType
+{
+ // The object is not a GC root (might be referenced during the marking stage)
+ kNoGCRoot = 0,
+ // The object is a GC root and is guaranteed to not have any references to non-root objects (Things that are already guaranteed to be marked)
+ // For example, game objects and transforms in a scene don't need to be processed, because they can only reference components that are already in the scene and those are already marked as roots.
+ kGCRootReferencesOnly = 1,
+
+ // a root for the GC marking, must be processed and it's dependencies must be found
+ kGCRootWithReferences = 2
+};
+
+#if UNITY_EDITOR
+#define DoesNotHavePrefabAttached(liveObject) (static_cast<EditorExtension*> (liveObject.object)->GetPrefab().GetInstanceID() == 0)
+#else
+#define DoesNotHavePrefabAttached(liveObject) true
+#endif
+
+inline bool HasValidGameObject (const ObjectState& liveObject)
+{
+ const Unity::Component* component = static_cast<const Unity::Component*> (liveObject.object);
+ return component->GetGameObjectPtr() != NULL;
+}
+
+inline bool IsScriptableObject (const ObjectState& liveObject)
+{
+ const MonoBehaviour* monoBehaviour = static_cast<const MonoBehaviour*> (liveObject.object);
+ return monoBehaviour->GetGameObjectPtr() == NULL;
+}
+
+static bool IsSceneObject (const ObjectState& liveObject)
+{
+ if (liveObject.isPersistent)
+ return false;
+
+ if (liveObject.classID == ClassID(MonoBehaviour) && IsScriptableObject(liveObject))
+ return false;
+
+ return liveObject.classID == ClassID(GameObject) || Object::IsDerivedFromClassID(liveObject.classID, ClassID(Component));
+}
+
+static bool IsAssetNotYetSavedToDisk (const ObjectState& liveObject)
+{
+#if UNITY_EDITOR
+ if (liveObject.isPersistent && liveObject.object->IsPersistentDirty())
+ {
+ if (ShouldPersistentDirtyObjectBeKeptAlive (liveObject.object->GetInstanceID()))
+ return true;
+ }
+#endif
+ return false;
+}
+
+
+static GCRootType IsObjectAGCRoot (const ObjectState& liveObject)
+{
+ int classID = liveObject.classID;
+ if (!liveObject.isPersistent)
+ {
+ // Game Objects & Transforms in the scene are roots and have no references beyond other objects that are also in the scene
+ // Except for prefabs which we specifically check for in the editor
+ if (classID == ClassID(GameObject) && DoesNotHavePrefabAttached(liveObject))
+ return kGCRootReferencesOnly;
+ else if (classID == ClassID(Transform) && DoesNotHavePrefabAttached(liveObject))
+ return kGCRootReferencesOnly;
+ // Only MonoBehaviour not ScriptableObject are treated as roots
+ else if (classID == ClassID(MonoBehaviour))
+ {
+ if (!IsScriptableObject(liveObject))
+ return kGCRootWithReferences;
+ }
+ else if (Object::IsDerivedFromClassID(classID, ClassID(Component)))
+ {
+ Assert(HasValidGameObject (liveObject));
+ return kGCRootWithReferences;
+ }
+ }
+
+ // Asset bundles are always explicitly unloaded
+ if (classID == ClassID(AssetBundle))
+ return kGCRootWithReferences;
+ // Objects marked as hide and dont save are treated as roots
+ // else if (liveObject.object->TestHideFlag (Object::kDontSave))
+ // return kGCRootWithReferences;
+ else if (liveObject.object->TestHideFlag (Object::kDontSave))
+ return kGCRootWithReferences;
+#if UNITY_EDITOR
+ else if (liveObject.isPersistent && liveObject.object->IsPersistentDirty())
+ {
+ if (ShouldPersistentDirtyObjectBeKeptAlive (liveObject.object->GetInstanceID()))
+ return kGCRootWithReferences;
+ }
+#endif
+
+ return kNoGCRoot;
+}
+
+static void FindAllLiveObjects (GarbageCollectorState& gcState)
+{
+ PROFILER_AUTO(gGCFindLiveObjects, NULL);
+ gcState.originalObjectCount = Object::GetLoadedObjectCount();
+
+ Object::IDToPointerMap& pointerMap = Object::GetIDToPointerMapInternal ();
+ gcState.liveObjects.resize_uninitialized(pointerMap.size());
+ gcState.needsProcessing.reserve(pointerMap.size());
+
+ Object::IDToPointerMap::const_iterator end = pointerMap.end();
+ Object::IDToPointerMap::const_iterator i=pointerMap.begin();
+
+ int index = 0;
+
+ ObjectState* liveObjects = gcState.liveObjects.begin();
+
+ // Extract All live objects
+ while (i != end)
+ {
+ Object& object = *i->second;
+
+ ObjectState& state = liveObjects[index];
+
+ state.object = &object;
+ state.classID = object.GetClassID();
+ state.marked = 0;
+ state.isPersistent = object.IsPersistent();
+
+ index++;
+ i++;
+ }
+
+ ValidateNoObjectsWereLoaded (gcState);
+}
+
+static void MarkSceneRootsAndReduceLiveObjects (GarbageCollectorState& gcState)
+{
+ int size = gcState.liveObjects.size();
+ ObjectState* liveObjects = gcState.liveObjects.begin();
+
+ // Mark scene roots
+ for (int index=0;index<size;)
+ {
+ ObjectState& state = liveObjects[index];
+
+ GCRootType type = IsObjectAGCRoot (state);
+
+ // It is a root and is guaranteed to have no references to non-roots
+ // We dont need it in any maps or even the liveObjects list.
+ if (type == kGCRootReferencesOnly)
+ {
+ // Reducing the size of this array early on saved around 3% on angrybots so not a significant optimization
+ size--;
+ liveObjects[index] = liveObjects[size];
+ }
+ // Root that might have references to other objects, thus must be processed
+ else if (type == kGCRootWithReferences )
+ {
+ MarkIndexAsRoot (index, gcState);
+ index++;
+ }
+ else
+ {
+ index++;
+ }
+ }
+
+ gcState.liveObjects.resize_uninitialized(size);
+
+ ValidateNoObjectsWereLoaded (gcState);
+}
+
+#if UNITY_EDITOR
+static void ProcessEditorExtension (GarbageCollectorState& gcState, EditorExtension& object)
+{
+ MarkInstanceIDAsRoot (object.GetPrefab().GetInstanceID(), gcState);
+}
+#else
+#define ProcessEditorExtension(x,y)
+#endif
+
+
+static void ProcessGameObject (GarbageCollectorState& gcState, ObjectState& state)
+{
+ GameObject& go = *static_cast<Unity::GameObject*> (state.object);
+
+ ProcessEditorExtension(gcState, go);
+
+ GameObject::Container& container = go.GetComponentContainerInternal ();
+ GameObject::Container::iterator end = container.end();
+ for (GameObject::Container::iterator i = container.begin();i != end;++i)
+ {
+ MarkObjectAsRootUnknownMarkState (*i->second, gcState);
+ }
+}
+
+static void ProcessValidComponent (GarbageCollectorState& gcState, ObjectState& state)
+{
+ Unity::Component& com = *static_cast<Unity::Component*> (state.object);
+
+ ProcessEditorExtension(gcState, com);
+
+ GameObject& go = com.GetGameObject ();
+ MarkObjectAsRootUnknownMarkState (go, gcState);
+}
+
+
+static void ProcessTransform (GarbageCollectorState& gcState, ObjectState& state)
+{
+ ProcessValidComponent(gcState, state);
+
+ Transform& transform = *static_cast<Transform*> (state.object);
+
+ const Transform::TransformComList& children = transform.GetChildrenInternal ();
+ Transform::TransformComList::const_iterator end = children.end();
+ for (Transform::TransformComList::const_iterator i = children.begin();i != end;++i)
+ {
+ MarkObjectAsRootUnknownMarkState (**i, gcState);
+ }
+
+ MarkObjectAsRootCheckNull (transform.GetParentPtrInternal (), gcState);
+}
+
+static void ProcessMeshFilter (GarbageCollectorState& gcState, ObjectState& state)
+{
+ ProcessValidComponent (gcState, state);
+
+ MeshFilter& filter = *static_cast<MeshFilter*> (state.object);
+ MarkInstanceIDAsRoot (filter.GetSharedMesh().GetInstanceID(), gcState);
+}
+
+static void ProcessMeshRenderer (GarbageCollectorState& gcState, ObjectState& state)
+{
+ ProcessValidComponent (gcState, state);
+
+ Renderer& renderer = *static_cast<Renderer*> (state.object);
+
+ const Renderer::MaterialArray& materials = renderer.GetMaterialArray ();
+ Renderer::MaterialArray::const_iterator end = materials.end();
+ for (Renderer::MaterialArray::const_iterator i=materials.begin();i != end;++i)
+ MarkInstanceIDAsRoot(i->GetInstanceID(), gcState);
+
+ // Make sure that the static batching root has a game object...
+// MarkInstanceIDAsRoot (renderer.GetStaticBatchRoot ().GetInstanceID(), gcState);
+
+
+ MarkInstanceIDAsRoot (renderer.GetLightProbeAnchor ().GetInstanceID(), gcState);
+}
+
+#if ENABLE_PHYSICS
+static void ProcessCollider (GarbageCollectorState& gcState, ObjectState& state)
+{
+ ProcessValidComponent (gcState, state);
+
+ Collider& collider = *static_cast<Collider*> (state.object);
+ MarkInstanceIDAsRoot(collider.GetMaterial().GetInstanceID(), gcState);
+}
+
+static void ProcessPrimitiveCollider (GarbageCollectorState& gcState, ObjectState& state)
+{
+ ProcessCollider (gcState, state);
+}
+
+static void ProcessMeshCollider (GarbageCollectorState& gcState, ObjectState& state)
+{
+ ProcessCollider (gcState, state);
+
+ MeshCollider& collider = *static_cast<MeshCollider*> (state.object);
+ MarkInstanceIDAsRoot(collider.GetSharedMesh().GetInstanceID(), gcState);
+}
+#else
+
+static void ProcessPrimitiveCollider (GarbageCollectorState& gcState, ObjectState& state)
+{
+ AssertString("Not supported");
+}
+static void ProcessMeshCollider (GarbageCollectorState& gcState, ObjectState& state)
+{
+ AssertString("Not supported");
+}
+#endif
+
+#if USE_MONO_LIVENESS
+
+static void ProcessLivenessFromScriptingObject (GarbageCollectorState& gcState, ScriptingObjectPtr object)
+{
+ mono_unity_liveness_calculation_from_root(object, gcState.livenessState);
+}
+
+static void ProcessMonoBehaviour (GarbageCollectorState& gcState, ObjectState& state)
+{
+ MonoBehaviour& behaviour = *static_cast<MonoBehaviour*> (state.object);
+
+ ProcessEditorExtension(gcState, behaviour);
+
+ // Not all MonoBehaviours are attached to a game object.
+ // Handle that situation specifically here.
+ GameObject* go = behaviour.GetGameObjectPtr ();
+ if (go != NULL)
+ MarkObjectAsRootUnknownMarkState (*go, gcState);
+
+ MarkInstanceIDAsRoot (behaviour.GetScript().GetInstanceID(), gcState);
+
+ if(!gcState.followMonoReferences)
+ return;
+
+ // MonoBehaviour managed instance
+ MonoObject* instance = Scripting::ScriptingWrapperFor(state.object);
+ if (instance != NULL)
+ ProcessLivenessFromScriptingObject(gcState, instance);
+
+ // Coroutine managed instances
+ List<Coroutine>& coroutine = behaviour.GetActiveCoroutines();
+ ListIterator<Coroutine> end = coroutine.end();
+ for (ListIterator<Coroutine> i=coroutine.begin();i != end;++i)
+ {
+ Coroutine& coroutine = *i;
+ ProcessLivenessFromScriptingObject(gcState, coroutine.m_CoroutineEnumerator);
+ }
+}
+
+#else
+
+static void ProcessMonoBehaviour (GarbageCollectorState& gcState, ObjectState& state)
+{
+ AssertString("Not supported");
+}
+#endif
+
+inline void GenericSlowGarbageCollector::ProcessReference (SInt32 oldInstanceID)
+{
+ int index = LookupInstanceIDIndex (oldInstanceID, *gcState);
+ if (index == -1)
+ return;
+
+ ObjectState& referencedState = gcState->liveObjects[index];
+ if (referencedState.marked == 0)
+ MarkIndexAsRoot(index, *gcState);
+ }
+
+ // General purpose GarbageCollector callback
+SInt32 GenericSlowGarbageCollector::GenerateInstanceID (SInt32 oldInstanceID, TransferMetaFlags metaFlag)
+ {
+ ProcessReference(oldInstanceID);
+ return oldInstanceID;
+ }
+
+void MarkDependencies (GarbageCollectorState& gcState, UInt32 index)
+{
+ ObjectState& state = gcState.liveObjects[index];
+
+ #if ENABLE_MEM_PROFILER
+ Assert(!state.object->ShouldIgnoreInGarbageDependencyTracking() || gcState.alwaysAddToNeedsProcessing);
+ #else
+ Assert(!state.object->ShouldIgnoreInGarbageDependencyTracking());
+ #endif
+
+ Assert((bool)state.marked);
+
+ if (state.classID == ClassID (GameObject))
+ ProcessGameObject(gcState, state);
+ else if (state.classID == ClassID (Transform))
+ ProcessTransform(gcState, state);
+ else if (state.classID == ClassID (MeshRenderer))
+ ProcessMeshRenderer(gcState, state);
+ else if (state.classID == ClassID (MeshFilter))
+ ProcessMeshFilter(gcState, state);
+ // Specialized MonoBehaviour code path for mono based platforms, otherwise falls back to the generic RemapPPtrTransfer
+ else if ((USE_MONO_LIVENESS) && gcState.followMonoReferences && state.classID == ClassID (MonoBehaviour))
+ ProcessMonoBehaviour(gcState, state);
+ else if ((ENABLE_PHYSICS) && state.classID == ClassID (MeshCollider))
+ ProcessMeshCollider(gcState, state);
+ else if ((ENABLE_PHYSICS) && state.classID == ClassID (BoxCollider))
+ ProcessPrimitiveCollider(gcState, state);
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+ else if (state.classID == ClassID (MonoBehaviour))
+ state.object->DoLivenessCheck(gcState.genericSlowTransfer);
+#endif
+ else
+ state.object->VirtualRedirectTransfer (gcState.genericSlowTransfer);
+}
+
+static void MarkAllDependencies (GarbageCollectorState& gcState)
+{
+ //@TODO: This stage can easily be multi-threaded
+ /// The out of this stage is gcState.liveObjects[i].marked = 1;
+ // Find some efficient way multithread that without expensive thread synchronization
+
+ dynamic_array<UInt32>& needsProcessing = gcState.needsProcessing;
+ while (!needsProcessing.empty ())
+ {
+ int index = needsProcessing.back();
+ needsProcessing.pop_back();
+
+ MarkDependencies (gcState, index);
+ }
+}
+
+#if ENABLE_MEM_PROFILER
+static void ExtractObjectArray (GarbageCollectorState& gcState, dynamic_array<Object*>& objects)
+{
+ objects.resize_uninitialized(gcState.liveObjects.size());
+ for (int i=0;i<objects.size();i++)
+ objects[i] = gcState.liveObjects[i].object;
+}
+
+static void ResetMarkedAndNeedsProcessing (GarbageCollectorState& state, dynamic_array<UInt32>& referencedObjectCount, dynamic_array<UInt32>& referencedObjectIndices)
+{
+ referencedObjectCount.push_back(state.needsProcessing.size());
+ referencedObjectIndices.insert(referencedObjectIndices.end(), state.needsProcessing.begin(), state.needsProcessing.end());
+ for (int i=0;i<state.needsProcessing.size();i++)
+ state.liveObjects[state.needsProcessing[i]].marked = 0;
+ state.needsProcessing.resize_uninitialized(0);
+}
+
+static void ClassifyRootObjects (GarbageCollectorState& state, dynamic_array<const char*>& additionalCategories, dynamic_array<UInt32>& referencedObjectCount, dynamic_array<UInt32>& referencedObjectIndices)
+{
+ // Calculate references for all scene roots
+ dynamic_array<UInt32> sceneRootIndices;
+ dynamic_array<UInt32> otherRootIndices;
+ dynamic_array<UInt32> dirtyAssetIndices;
+
+ for (int i=0;i<state.liveObjects.size();i++)
+ {
+ ObjectState& liveObject = state.liveObjects[i];
+
+ if (IsSceneObject (liveObject))
+ sceneRootIndices.push_back(i);
+ else if (IsAssetNotYetSavedToDisk (liveObject))
+ dirtyAssetIndices.push_back(i);
+ else if (IsObjectAGCRoot (liveObject) != kNoGCRoot)
+ otherRootIndices.push_back(i);
+ }
+
+ additionalCategories.push_back("Scene Object");
+ referencedObjectCount.push_back(sceneRootIndices.size());
+ referencedObjectIndices.insert(referencedObjectIndices.end(), sceneRootIndices.begin(), sceneRootIndices.end());
+
+ additionalCategories.push_back("HideAndDontSave, Manager or AssetBundle");
+ referencedObjectCount.push_back(otherRootIndices.size());
+ referencedObjectIndices.insert(referencedObjectIndices.end(), otherRootIndices.begin(), otherRootIndices.end());
+
+ additionalCategories.push_back("Asset has been edited and not yet saved to disk");
+ referencedObjectCount.push_back(dirtyAssetIndices.size());
+ referencedObjectIndices.insert(referencedObjectIndices.end(), dirtyAssetIndices.begin(), dirtyAssetIndices.end());
+}
+
+void CalculateAllObjectReferences (dynamic_array<Object*>& loadedObjects, dynamic_array<const char*>& additionalCategories, dynamic_array<UInt32>& referencedObjectCount, dynamic_array<UInt32>& referencedObjectIndices)
+{
+ enum { kNbAdditionalCategoriesReserve = 20 };
+
+ GarbageCollectorState state;
+ state.alwaysAddToNeedsProcessing = true;
+ state.followMonoReferences = true;
+
+ // Setup live object mapping
+ FindAllLiveObjects(state);
+ CreateObjectToIndexMappingFromNonRootObjects (state);
+
+ // Build loadedObjects output
+ ExtractObjectArray (state, loadedObjects);
+
+ referencedObjectIndices.reserve (loadedObjects.size() * 2);
+ referencedObjectCount.reserve(loadedObjects.size() + kNbAdditionalCategoriesReserve);
+
+ // Calculate references for all loaded objects
+ for (int i=0;i<loadedObjects.size();i++)
+ {
+ ObjectState& liveObject = state.liveObjects[i];
+ if (liveObject.classID == ClassID(MonoBehaviour))
+ BeginLivenessChecking(state);
+
+ Assert(!liveObject.marked);
+ if (DoesClassIDHaveReferences (liveObject.classID))
+ {
+ liveObject.marked = 1;
+ MarkDependencies (state, i);
+ liveObject.marked = 0;
+ }
+
+ if (liveObject.classID == ClassID(MonoBehaviour))
+ EndLivenessChecking(state);
+
+ ResetMarkedAndNeedsProcessing (state, referencedObjectCount, referencedObjectIndices);
+ }
+
+ // Calculate references for managed static references
+ additionalCategories.push_back("ManagedStaticReferences");
+ BeginLivenessChecking(state);
+ MarkManagedStaticVariableRoots (state);
+ EndLivenessChecking(state);
+ ResetMarkedAndNeedsProcessing (state, referencedObjectCount, referencedObjectIndices);
+
+ // Calculate references from any managers
+ additionalCategories.push_back("Managers");
+ MarkManagerRoots(state);
+ ResetMarkedAndNeedsProcessing (state, referencedObjectCount, referencedObjectIndices);
+
+ // Calculate references from any managers
+ additionalCategories.push_back("Selection");
+ MarkSelectedObjectsAsRoots(state);
+ ResetMarkedAndNeedsProcessing (state, referencedObjectCount, referencedObjectIndices);
+
+ // Classify root objects (Scene objects, hide and dontsave etc)
+ ClassifyRootObjects (state, additionalCategories, referencedObjectCount, referencedObjectIndices);
+}
+#endif
+
+static void InvokeScriptableObjectUnloadCallbacks (const SInt32* scriptableObjectCallbacks, int size)
+{
+ PROFILER_AUTO(gGCOnDestroyCallback, NULL);
+
+#if ENABLE_SCRIPTING
+ for (int i=0;i<size;i++)
+ {
+ MonoBehaviour* behaviour = reinterpret_cast<MonoBehaviour*>(Object::IDToPointer(scriptableObjectCallbacks[i]));
+ if (behaviour)
+ {
+ behaviour->WillUnloadScriptableObject();
+ }
+ }
+#endif
+}
+
+static void CleanupUnusedObjects (GarbageCollectorState& gcState)
+{
+ PROFILER_AUTO(gGCDeletedUnusedAssets, NULL);
+
+ dynamic_array<SInt32> unloadObjects;
+ dynamic_array<SInt32> scriptableObjectUnloadCallbacks;
+ unloadObjects.reserve(gcState.liveObjects.size());
+ scriptableObjectUnloadCallbacks.reserve(gcState.liveObjects.size());
+
+ // Classify
+ for (int i=0;i<gcState.liveObjects.size();i++)
+ {
+ const ObjectState& objectState = gcState.liveObjects[i];
+
+ if (objectState.marked == 0)
+ {
+ SInt32 instanceID = objectState.object->GetInstanceID();
+ unloadObjects.push_back(instanceID);
+
+ if (objectState.classID == ClassID(MonoBehaviour))
+ scriptableObjectUnloadCallbacks.push_back(instanceID);
+ }
+ }
+
+ // ScriptableObject.OnDestroy
+ InvokeScriptableObjectUnloadCallbacks (scriptableObjectUnloadCallbacks.begin(), scriptableObjectUnloadCallbacks.size());
+
+ // Delete objects
+ BatchDeleteObjectInternal (unloadObjects.begin(), unloadObjects.size());
+}
+
+static void CleanupOtherUnusedMemory (GarbageCollectorState& gcState)
+{
+ // Unload streams that are not dirty
+ GetPersistentManager().UnloadNonDirtyStreams ();
+}
+
+
+static void ValidateNoObjectsWereLoaded (GarbageCollectorState& state)
+{
+ Assert(state.originalObjectCount == Object::GetLoadedObjectCount());
+}
+
+
+/*
+
+TODO:
+
+Tests:
+ - ScriptableObject referenced from static variable and no longer referenced from static variable
+ - Dirty assets are not unloaded
+ - Get rid of Validation of bound animation targets on every frame again. Profiler new event system for performance of deleting characters in that case.
+
+Important:
+-- When unloading make sure we dont unload other objects during destruction. Maybe we can use it to get rid of Object* lookups this way???
+ @TODO: Try if we can enable this safely.
+ #if DEBUGMODE
+ int count = Object::GetLoadedObjectCount ();
+#endif
+ Do deletion
+ #if DEBUGMODE
+ if (count-1 != Object::GetLoadedObjectCount ())
+ {
+ AssertString("Unloading this object deleted more than the object alone. This is not allowed.");
+ }
+#endif
+*/
+#endif
diff --git a/Runtime/Misc/GarbageCollectSharedAssets.h b/Runtime/Misc/GarbageCollectSharedAssets.h
new file mode 100644
index 0000000..a9a990d
--- /dev/null
+++ b/Runtime/Misc/GarbageCollectSharedAssets.h
@@ -0,0 +1,31 @@
+#ifndef GARBAGE_COLLECT_SHARED_ASSETS_H
+#define GARBAGE_COLLECT_SHARED_ASSETS_H
+
+/// Unloads all assets that are not referenced using dependency tracking and mono GC.
+/// You must call GetPreloadManager().LockPreloading(); GetPreloadManager().UnlockPreloading(); around GarbageCollectSharedAssets.
+void GarbageCollectSharedAssets (bool includeMonoReferencesAsRoots);
+
+/// Calculates all direct references of all loaded objects and additional categories
+/// * loadedObjects is an array of all loaded objects
+/// * additionalCategories is an array of all custom categories (eg. "SceneObject", "HideAndDontSave" etc)
+/// * referencedObjectCount is the number of referenced objects (referencedObjectIndices holds the indices)
+/// * referencedObjectIndices is a combined array of all indices (referencedObjectCount)
+/// This is a stream layout so to make sense of the data you have to walk referencedObjectCount in linear order
+#if ENABLE_MEM_PROFILER
+void CalculateAllObjectReferences (dynamic_array<Object*>& loadedObjects, dynamic_array<const char*>& additionalCategories, dynamic_array<UInt32>& referencedObjectCount, dynamic_array<UInt32>& referencedObjectIndices);
+#endif
+
+#if UNITY_EDITOR
+bool ShouldPersistentDirtyObjectBeKeptAlive (int instanceID);
+
+/// Prevents an Object from being destroyed by GarbageCollectSharedAssets().
+/// This is useful in cases where an Object is internally created by code without ties
+/// to the scene and needs to be kept alive across operations that may trigger an
+/// asset GC run.
+void SetPreventGarbageCollectionOfAsset (int instanceID);
+/// Reverts previous call to SetPreventGarbageCollectionOfAsset().
+void ClearPreventGarbageCollectionOfAsset (int instanceID);
+
+#endif
+
+#endif
diff --git a/Runtime/Misc/GraphicsDevicesDB.cpp b/Runtime/Misc/GraphicsDevicesDB.cpp
new file mode 100644
index 0000000..9a00a84
--- /dev/null
+++ b/Runtime/Misc/GraphicsDevicesDB.cpp
@@ -0,0 +1,1322 @@
+#include "UnityPrefix.h"
+#include "GraphicsDevicesDB.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+
+
+#if GRAPHICS_DEVICES_DB_AVAILABLE
+
+
+// Device ID lists based on:
+// General everything: http://pciids.sourceforge.net/pci.ids
+// ATI:
+// May 2011, http://developer.amd.com/gpu_assets/ATI_Device_ID_List_May_2011.txt
+// July 2010, http://developer.amd.com/gpu_assets/ATI_Device_ID_List_July_2010.txt
+// Jan 2009, http://developer.amd.com/gpu_assets/ATI_Device_IDs_Jan_09.txt
+// NVIDIA:
+// Home: http://developer.nvidia.com/content/device-id-list
+// March 2010, http://www.nvidia.com/object/device_ids.html
+// GeForce/ION/Quadro 257.41 WHQL driver INF list
+// Mesa: http://cgit.freedesktop.org/mesa/mesa/tree/include/pci_ids
+// Intel: http://software.intel.com/en-us/articles/intel-gma-3000-and-x3000-developers-guide/
+// Linux kernel driver database, CONFIG_DRM_* stuff: http://cateee.net/lkddb/web-lkddb/index_d.html
+
+// Performance tables can be found on Wikipedia, for better or worse:
+// http://en.wikipedia.org/wiki/Comparison_of_AMD_graphics_processing_units
+// http://en.wikipedia.org/wiki/Comparison_of_Nvidia_graphics_processing_units
+// http://en.wikipedia.org/wiki/Comparison_of_Intel_graphics_processing_units
+// also here: http://www.techarp.com/showarticle.aspx?artno=88
+
+
+
+struct GraphicsDeviceDesc {
+ int deviceID;
+ const char* name;
+ int pixelFillrate;
+};
+
+
+static const GraphicsDeviceDesc kATIDevices[] = {
+{ 0x3150, "Mobility Radeon X600 M24", 1600 },
+{ 0x3151, "FireMV 2400 RV380", 1600 },
+{ 0x3152, "Mobility Radeon X300 M24", 1600 },
+{ 0x3154, "Mobility FireGL V3200 M24GL", 1600 },
+{ 0x3171, "FireMV 2400 RV380", 1600 },
+{ 0x3E50, "Radeon X600/X550 Series RV380", 1600 },
+{ 0x3E54, "FireGL V3200 RV380GL", 1600 },
+{ 0x3E70, "Radeon X600/X550 Series RV380", 1600 },
+{ 0x3E74, "FireGL V3200 RV380GL", 1600 },
+{ 0x4144, "Radeon 9500 R300", 1100 },
+{ 0x4146, "Radeon 9600TX R300", 1100 },
+{ 0x4147, "FireGL Z1 R300GL", 1100 },
+{ 0x4148, "Radeon 9800 SE R350", 1300 },
+{ 0x4149, "Radeon 9500 R350", 1300 },
+{ 0x4150, "Radeon 9600 Series RV350", 1300 },
+{ 0x4151, "Radeon 9600 Series RV350", 1300 },
+{ 0x4152, "Radeon 9600 Series RV360", 1600 },
+{ 0x4153, "Radeon 9550/X1050 Series RV350", 1000 },
+{ 0x4154, "FireGL T2 RV350GL", 1300 },
+{ 0x4155, "Radeon 9600 Series RV350", 1300 },
+{ 0x4164, "Radeon 9500 R300", 1100 },
+{ 0x4166, "Radeon 9600TX R300", 1100 },
+{ 0x4167, "FireGL Z1 R300GL", 1100 },
+{ 0x4168, "Radeon 9800 SE R350", 1300 },
+{ 0x4169, "Radeon 9500 R350", 1300 },
+{ 0x4170, "Radeon 9600 Series RV350", 1300 },
+{ 0x4171, "Radeon 9600 Series RV350", 1300 },
+{ 0x4172, "Radeon 9600 Series RV360", 1600 },
+{ 0x4173, "Radeon 9550/X1050 Series RV350", 1000 },
+{ 0x4174, "FireGL T2 RV350GL", 1300 },
+{ 0x4175, "Radeon 9600 Series RV350", 1000 },
+{ 0x4966, "Radeon 9000 Series", 1000 },
+{ 0x4A48, "Radeon X800 Series R420", 3800 },
+{ 0x4A49, "Radeon X800 PRO R420", 5700 },
+{ 0x4A4A, "Radeon X800 Series R420", 3800 },
+{ 0x4A4B, "Radeon X800 XT R420", 8000 },
+{ 0x4A4C, "Radeon X800 Series R420", 3800 },
+{ 0x4A4D, "FireGL X3-256 R420GL", 3800 },
+{ 0x4A4E, "Mobility Radeon 9800 M18", 2800 },
+{ 0x4A4F, "Radeon X800 SE R420", 3400 },
+{ 0x4A50, "Radeon X800 XT Platinum Edition R420", 8320 },
+{ 0x4A54, "Radeon X800 VE R420", 1700 },
+{ 0x4A68, "Radeon X800 Series R420", 3800 },
+{ 0x4A69, "Radeon X800 PRO R420", 5700 },
+{ 0x4A6A, "Radeon X800 Series R420", 3800 },
+{ 0x4A6B, "Radeon X800 XT R420", 8000 },
+{ 0x4A6C, "Radeon X800 Series R420", 3800 },
+{ 0x4A6D, "FireGL X3-256 R420GL", 3800 },
+{ 0x4A6F, "Radeon X800 SE R420", 3400 },
+{ 0x4A70, "Radeon X800 XT Platinum Edition R420", 8320 },
+{ 0x4A74, "Radeon X800 VE R420", 1700 },
+{ 0x4B49, "Radeon X850 XT R481", 8320 },
+{ 0x4B4A, "Radeon X850 SE R481", 3400 },
+{ 0x4B4B, "Radeon X850 PRO R481", 6240 },
+{ 0x4B4C, "Radeon X850 XT Platinum Edition R481", 8640 },
+{ 0x4B69, "Radeon X850 XT R481", 8320 },
+{ 0x4B6A, "Radeon X850 SE R481", 3400 },
+{ 0x4B6B, "Radeon X850 PRO R481", 6240 },
+{ 0x4B6C, "Radeon X850 XT Platinum Edition R481", 8640 },
+{ 0x4C66, "Mobility Radeon 9000", 1000 },
+{ 0x4E44, "Radeon 9700 PRO R300", 2600 },
+{ 0x4E45, "Radeon 9500 PRO / 9700 R300", 2200 },
+{ 0x4E46, "Radeon 9600 TX R300", 1100 },
+{ 0x4E47, "FireGL X1 R300GL", 1100 },
+{ 0x4E48, "Radeon 9800 PRO R350", 3040 },
+{ 0x4E49, "Radeon 9800 R350", 2600 },
+{ 0x4E4A, "Radeon 9800 XT R360", 3296 },
+{ 0x4E4B, "FireGL X2-256/X2-256t R350GL", 2600 },
+{ 0x4E50, "Mobility Radeon 9600/9700 Series M10", 1200 },
+{ 0x4E51, "Radeon 9600 Series RV350", 1300 },
+{ 0x4E52, "Mobility Radeon 9500 M10", 840 },
+{ 0x4E54, "Mobility FIRE GL T2/T2e M10GL", 840 },
+{ 0x4E56, "Mobility Radeon 9550 M12", 840 },
+{ 0x4E64, "Radeon 9700 PRO R300", 2600 },
+{ 0x4E65, "Radeon 9500 PRO / 9700 R300", 2200 },
+{ 0x4E66, "Radeon 9600 TX R300", 1100 },
+{ 0x4E67, "FireGL X1 R300GL", 1100 },
+{ 0x4E68, "Radeon 9800 PRO R350", 3040 },
+{ 0x4E69, "Radeon 9800 R350", 2600 },
+{ 0x4E6A, "Radeon 9800 XT R360", 3296 },
+{ 0x4E6B, "FireGL X2-256/X2-256t R350GL", 2600 },
+{ 0x4E71, "Radeon 9600 Series RV350", 1300 },
+{ 0x5460, "Mobility Radeon X300 M22", 1400 },
+{ 0x5461, "Mobility Radeon X300 M22", 1400 },
+{ 0x5462, "Mobility Radeon X600 SE M24C", 1600 },
+{ 0x5464, "Mobility FireGL V3100 M22GL", 1400 },
+{ 0x5548, "Radeon X800 Series R423", 4800 },
+{ 0x5549, "Radeon X800 GTO R423", 6400 },
+{ 0x554A, "Radeon X800 XT Platinum Edition R423", 8320 },
+{ 0x554B, "Radeon X800 GT R423", 3600 },
+{ 0x554D, "Radeon X800 XL R430", 6400 },
+{ 0x554E, "Radeon X800 GT R430", 3800 },
+{ 0x554F, "Radeon X800 GTO R430", 3800 },
+{ 0x5550, "FireGL V7100 R423GL", 4800 },
+{ 0x5551, "FireGL V5100 R423GL", 4800 },
+{ 0x5568, "Radeon X800 Series R423", 4800 },
+{ 0x5569, "Radeon X800 GTO R423", 6400 },
+{ 0x556A, "Radeon X800 XT Platinum Edition R423", 8320 },
+{ 0x556B, "Radeon X800 GT R423", 3600 },
+{ 0x556D, "Radeon X800 XL R430", 6400 },
+{ 0x556E, "Radeon X800 GT R430", 3800 },
+{ 0x556F, "Radeon X800 GTO R430", 3800 },
+{ 0x5570, "FireGL V7100 R423GL", 4800 },
+{ 0x5571, "FireGL V5100 R423GL", 4800 },
+{ 0x564A, "Mobility FireGL V5000 M26GL", 1400 },
+{ 0x564B, "Mobility FireGL V5000 M26GL", 1400 },
+{ 0x564F, "Mobility Radeon X700 XL M26", 1400 },
+{ 0x5652, "Mobility Radeon X700 M26", 1400 },
+{ 0x5653, "Mobility Radeon X700 M26", 140 },
+{ 0x5657, "Radeon X550/X700 Series RV410", 3200 },
+{ 0x5673, "Mobility Radeon X700 M26", 1400 },
+{ 0x5677, "Radeon X550/X700 Series RV410", 3200 },
+{ 0x5835, "Mobility Radeon 9000 IGP", 1000 },
+{ 0x5854, "Radeon Xpress Series RS480", 600 },
+{ 0x5874, "Radeon Xpress Series RS482", 600 },
+{ 0x5954, "Radeon Xpress Series RS480", 600 },
+{ 0x5960, "Radeon 9250", 960 },
+{ 0x5961, "Radeon 9000 Series", 1000 },
+{ 0x5964, "Radeon 9200 Series", 1000 },
+{ 0x5955, "Radeon Xpress Series RS480M", 600 },
+{ 0x5974, "Radeon Xpress Series RS482", 600 },
+{ 0x5975, "Radeon Xpress Series RS482M", 600 },
+{ 0x5A41, "Radeon Xpress Series RS400", 600 },
+{ 0x5A42, "Radeon Xpress Series RS400M", 600 },
+{ 0x5A43, "Radeon Xpress Series RS400", 600 },
+{ 0x5A61, "Radeon Xpress Series RC410", 800 },
+{ 0x5A62, "Radeon Xpress Series RC410M", 800 },
+{ 0x5A63, "Radeon Xpress Series RC410", 800 },
+{ 0x5B60, "Radeon X300/X550/X1050 Series RV370", 1300 },
+{ 0x5B62, "Radeon X600 Series RV380x", 1600 },
+{ 0x5B63, "Radeon X300/X550/X1050 Series RV370", 1300 },
+{ 0x5B64, "FireGL V3100 RV370GL", 1300 },
+{ 0x5B65, "FireMV 2200 RV370", 1300 },
+{ 0x5B70, "Radeon X300/X550/X1050 Series RV370", 1300 },
+{ 0x5B72, "Radeon X600 Series RV380x", 1600 },
+{ 0x5B73, "Radeon X300/X550/X1050 Series RV370", 1300 },
+{ 0x5B74, "FireGL V3100 RV370GL", 1300 },
+{ 0x5B75, "FireMV 2200 RV370", 1300 },
+{ 0x5C61, "Mobility Radeon 9200", 1000 },
+{ 0x5D48, "Mobility/Radeon X800 XT M28", 4800 },
+{ 0x5D49, "Mobility FireGL V5100 M28GL", 4800 },
+{ 0x5D4A, "Mobility Radeon X800 M28", 4800 },
+{ 0x5D4D, "Radeon X850 XT Platinum Edition R480", 8640 },
+{ 0x5D4F, "Radeon X800 GTO R480", 3800 },
+{ 0x5D50, "FireGL V7200 R480GL", 3800 },
+{ 0x5D52, "Radeon X850 XT R480", 8320 },
+{ 0x5D57, "Radeon X800 XT R423", 8000 },
+{ 0x5D6D, "Radeon X850 XT Platinum Edition R480", 8640 },
+{ 0x5D6F, "Radeon X800 GTO R480", 3800 },
+{ 0x5D70, "FireGL V7200 R480GL", 3800 },
+{ 0x5D72, "Radeon X850 XT R480", 8320 },
+{ 0x5D77, "Radeon X800 XT R423", 8000 },
+{ 0x5E48, "FireGL V5000 RV410GL", 3200 },
+{ 0x5E4A, "Radeon X700 XT RV410", 3800 },
+{ 0x5E4B, "Radeon X700 PRO RV410", 3400 },
+{ 0x5E4C, "Radeon X700 SE RV410", 3200 },
+{ 0x5E4D, "Radeon X700 RV410", 3200 },
+{ 0x5E4F, "Radeon X700/X550 Series RV410", 3200 },
+{ 0x5E68, "FireGL V5000 RV410GL", 3200 },
+{ 0x5E6A, "Radeon X700 XT RV410", 3800 },
+{ 0x5E6B, "Radeon X700 PRO RV410", 3400 },
+{ 0x5E6C, "Radeon X700 SE RV410", 3200 },
+{ 0x5E6D, "Radeon X700 RV410", 3200 },
+{ 0x5E6F, "Radeon X700/X550 Series RV410", 3200 },
+{ 0x6704, "FirePro V7900", 23200 },
+{ 0x6707, "FirePro V5900", 19200 },
+{ 0x6718, "Radeon HD 6900 Series", 25600 },
+{ 0x6719, "Radeon HD 6900 Series", 28200 },
+{ 0x671D, "Radeon HD 6990", 39750 }, // 2xGPU, assume 1.5x perf
+{ 0x6720, "Radeon HD 6970M", 21760 },
+{ 0x6738, "Radeon HD 6800 Series", 25600 },
+{ 0x6739, "Radeon HD 6800 Series", 25600 },
+{ 0x673E, "Radeon HD 6700 Series", 12000 },
+{ 0x6740, "Radeon HD 6770M", 5800 },
+{ 0x6741, "Radeon HD 6750M", 4800 },
+{ 0x6749, "FirePro V4900", 6400 },
+{ 0x6758, "Radeon HD 6600 Series", 6400 },
+{ 0x6759, "Radeon HD 6500 Series", 5200 },
+{ 0x675B, "Radeon HD 7670", 6400 },
+{ 0x675D, "Radeon HD 7570", 5200 },
+{ 0x675F, "Radeon HD 5500 Series", 4400 },
+{ 0x6760, "Radeon HD 6490M", 3200 },
+{ 0x6770, "Radeon HD 6400 Series", 2750 },
+{ 0x6778, "Radeon HD 7470", 2750 },
+{ 0x6779, "Radeon HD 6450", 2750 },
+{ 0x677B, "Radeon HD 7400 Series", 3000 },
+{ 0x6798, "Radeon HD 7970", 29600 },
+{ 0x6799, "Radeon HD 7990", 40000 }, // 2xGPU, assume 1.5x perf
+{ 0x679A, "Radeon HD 7950", 25600 },
+{ 0x679E, "Radeon HD 7800 Series", 27500 },
+{ 0x6800, "Radeon HD 7970M", 27200 },
+{ 0x6818, "Radeon HD 7800 Series", 27500 },
+{ 0x6819, "Radeon HD 7800 Series", 27500 },
+{ 0x683D, "Radeon HD 7700 Series", 15000 },
+{ 0x683F, "Radeon HD 7700 Series", 15000 },
+{ 0x6840, "Radeon HD 7600M Series", 4000 },
+{ 0x6841, "Radeon HD 7500/7600M Series", 4000 },
+{ 0x6843, "Radeon HD 7670M", 4800 },
+{ 0x6850, "Radeon HD 7570", 5200 },
+{ 0x6858, "Radeon HD 7400 Series", 3000 },
+{ 0x6888, "FirePro V8800", 26400 },
+{ 0x6889, "FirePro V7800", 22400 },
+{ 0x688C, "FireStream 9370", 26400 },
+{ 0x688D, "FireStream 9350", 22400 },
+{ 0x6898, "Radeon HD 5800 Series", 23200 },
+{ 0x6899, "Radeon HD 5800 Series", 23200 },
+{ 0x689C, "Radeon HD 5900 Series", 32000 }, // 2xGPU, assume 1.5x perf
+{ 0x689E, "Radeon HD 5800 Series", 23200 },
+{ 0x68A0, "Mobility Radeon HD 5870", 11200 },
+{ 0x68A1, "Radeon HD 5750", 11200 },
+{ 0x68A8, "Radeon HD 6800M", 9200 },
+{ 0x68A9, "FirePro V5800", 11200 },
+{ 0x68B8, "Radeon HD 5700 Series", 11200 },
+{ 0x68B9, "Radeon HD 5600 Series", 6200 },
+{ 0x68BA, "Radeon HD 6700 Series", 12000 },
+{ 0x68BE, "Radeon HD 5700 Series", 11200 },
+{ 0x68BF, "Radeon HD 6700 Series", 12000 },
+{ 0x68C0, "Radeon HD 5670", 6200 },
+{ 0x68C1, "Mobility Radeon HD 5650", 4400 },
+{ 0x68C8, "FirePro/FireGL V4800", 6200 },
+{ 0x68C9, "FirePro V3800", 5200 },
+{ 0x68D8, "Radeon HD 5600 Series", 6200 },
+{ 0x68D9, "Radeon HD 5500 Series", 4400 },
+{ 0x68DA, "Radeon HD 5500 Series", 4400 },
+{ 0x68E0, "Mobility Radeon HD 5470", 3000 },
+{ 0x68E1, "Mobility Radeon HD 5430", 2200 },
+{ 0x68E4, "Mobility Radeon HD 6370", 3000 },
+{ 0x68E5, "Radeon HD 6330M", 2000 },
+{ 0x68FA, "Radeon HD 7350", 2000 },
+{ 0x68F9, "Radeon HD 5400 Series", 2600 },
+{ 0x7100, "Radeon X1800 Series R520", 8000 },
+{ 0x7101, "Mobility Radeon X1800 XT M58", 8800 },
+{ 0x7102, "Mobility Radeon X1800 M58", 5400 },
+{ 0x7103, "Mobility FireGL V7200 M58GL", 5400 },
+{ 0x7104, "FireGL V7200 R520GL", 9600 },
+{ 0x7105, "FireGL V5300 R520GL", 2800 },
+{ 0x7106, "Mobility FireGL V7100 M58GL", 5400 },
+{ 0x7108, "Radeon X1800 Series R520", 8000 },
+{ 0x7109, "Radeon X1800 Series R520", 8000 },
+{ 0x710A, "Radeon X1800 Series R520", 8000 },
+{ 0x710B, "Radeon X1800 Series R520", 8000 },
+{ 0x710C, "Radeon X1800 Series R520", 8000 },
+{ 0x710E, "FireGL V7300 R520GL", 9600 },
+{ 0x710F, "FireGL V7350 R520GL", 9600 },
+{ 0x7120, "Radeon X1800 Series R520", 8000 },
+{ 0x7124, "FireGL V7200 R520GL", 9600 },
+{ 0x7125, "FireGL V5300 R520GL", 2800 },
+{ 0x7128, "Radeon X1800 Series R520", 8000 },
+{ 0x7129, "Radeon X1800 Series R520", 8000 },
+{ 0x712A, "Radeon X1800 Series R520", 8000 },
+{ 0x712B, "Radeon X1800 Series R520", 8000 },
+{ 0x712C, "Radeon X1800 Series R520", 8000 },
+{ 0x712E, "FireGL V7300 R520GL", 9600 },
+{ 0x712F, "FireGL V7350 R520GL", 9600 },
+{ 0x7140, "Radeon X1600 Series RV515", 1800 },
+{ 0x7142, "Radeon X1300/X1550 Series RV515", 1800 },
+{ 0x7143, "Radeon X1550 Series RV515", 1800 },
+{ 0x7145, "Mobility Radeon X1400 M54", 1780 },
+{ 0x7146, "Radeon X1300 / X1550 Series RV515", 1800 },
+{ 0x7147, "Radeon X1550 64-bit RV515", 1800 },
+{ 0x7149, "Mobility Radeon X1300 M52", 1400 },
+{ 0x714A, "Mobility Radeon X1300 M52", 1400 },
+{ 0x714B, "Mobility Radeon X1300 M52", 1400 },
+{ 0x714C, "Mobility Radeon X1300 M52", 1400 },
+{ 0x714D, "Radeon X1300 Series RV515", 1800 },
+{ 0x714E, "Radeon X1300 Series RV515PCI", 1800 },
+{ 0x7152, "FireGL V3300 RV515GL", 1800 },
+{ 0x7153, "FireGL V3350 RV515GL", 1800 },
+{ 0x715E, "Radeon X1300 Series RV515", 1800 },
+{ 0x715F, "Radeon X1550 64-bit RV515", 1800 },
+{ 0x7160, "Radeon X1600 Series RV515", 1800 },
+{ 0x7162, "Radeon X1300/X1550 Series RV515", 1800 },
+{ 0x7163, "Radeon X1550 Series RV515", 1800 },
+{ 0x7166, "Radeon X1300 / X1550 Series RV515", 1800 },
+{ 0x7167, "Radeon X1550 64-bit RV515", 1800 },
+{ 0x716D, "Radeon X1300 Series RV515", 1800 },
+{ 0x716E, "Radeon X1300 Series RV515PCI", 1800 },
+{ 0x7172, "FireGL V3300 RV515GL", 1800 },
+{ 0x7173, "FireGL V3350 RV515GL", 1800 },
+{ 0x717E, "Radeon X1300 Series RV515", 1800 },
+{ 0x717F, "Radeon X1550 64-bit RV515", 1800 },
+{ 0x7180, "Radeon X1300/X1550 Series RV515", 1800 },
+{ 0x7181, "Radeon X1600 Series RV515", 1800 },
+{ 0x7183, "Radeon X1300/X1550 Series RV515", 1800 },
+{ 0x7186, "Mobility Radeon X1450 M54", 1800 },
+{ 0x7187, "Radeon X1300/X1550 Series RV515", 1800 },
+{ 0x7188, "Mobility Radeon X2300 M54", 1920 },
+{ 0x718A, "Mobility Radeon X2300 M54", 1920 },
+{ 0x718B, "Mobility Radeon X1350 M52", 1880 },
+{ 0x718C, "Mobility Radeon X1350 M52", 1880 },
+{ 0x718D, "Mobility Radeon X1450 M54", 2200 },
+{ 0x718F, "Radeon X1300 Series RV515PCI", 1800 },
+{ 0x7193, "Radeon X1550 Series RV515", 1800 },
+{ 0x7196, "Mobility Radeon X1350 M52", 1800 },
+{ 0x719B, "FireMV 2250 RV515", 1800 },
+{ 0x719F, "Radeon X1550 64-bit RV515", 1800 },
+{ 0x71A0, "Radeon X1300/X1550 Series RV515", 1800 },
+{ 0x71A1, "Radeon X1600 Series RV515", 1800 },
+{ 0x71A3, "Radeon X1300/X1550 Series RV515", 1800 },
+{ 0x71A7, "Radeon X1300/X1550 Series RV515", 1800 },
+{ 0x71AF, "Radeon X1300 Series RV515PCI", 1800 },
+{ 0x71B3, "Radeon X1550 Series RV515", 1800 },
+{ 0x71BB, "FireMV 2250 RV515", 1800 },
+{ 0x71C0, "Radeon X1600 Series RV530", 2000 },
+{ 0x71C1, "Radeon X1650 Series RV535", 2000 },
+{ 0x71C2, "Radeon X1600 Series RV530", 2000 },
+{ 0x71C3, "Radeon X1300 Series RV535", 2000 },
+{ 0x71C4, "Mobility FireGL V5200 M56GL", 1800 },
+{ 0x71C5, "Mobility Radeon X1600 M56", 1800 },
+{ 0x71C6, "Radeon X1650 Series RV530", 2000 },
+{ 0x71C7, "Radeon X1650 Series RV535", 2000 },
+{ 0x71CD, "Radeon X1600 Series RV530", 2000 },
+{ 0x71CE, "Radeon X1600 Pro / Radeon X1300 XT RV530", 2000 },
+{ 0x71D2, "FireGL V3400 RV530GL", 2000 },
+{ 0x71D4, "Mobility FireGL V5250 M56GL", 1800 },
+{ 0x71D5, "Mobility Radeon X1700 M56", 1800 },
+{ 0x71D6, "Mobility Radeon X1700 XT M56", 1800 },
+{ 0x71DA, "FireGL V5200 RV530GL", 2000 },
+{ 0x71DE, "Mobility Radeon X1700 M56", 1800 },
+{ 0x71E0, "Radeon X1600 Series RV530", 2000 },
+{ 0x71E1, "Radeon X1650 Series RV535", 2000 },
+{ 0x71E2, "Radeon X1600 Series RV530", 2000 },
+{ 0x71E3, "Radeon X1300 Series RV535", 2000 },
+{ 0x71E6, "Radeon X1600 Series RV530", 2000 },
+{ 0x71E7, "Radeon X1650 Series RV535", 2000 },
+{ 0x71ED, "Radeon X1600 Series RV530", 2000 },
+{ 0x71EE, "Radeon X1600 Pro / Radeon X1300 XT RV530", 2000 },
+{ 0x71F2, "FireGL V3400 RV530GL", 2000 },
+{ 0x71FA, "FireGL V5200 RV530GL", 2000 },
+{ 0x7210, "Mobility Radeon HD 2300 M71", 1920 },
+{ 0x7211, "Mobility Radeon HD 2300 M71", 1920 },
+{ 0x7240, "Radeon X1950 Series R580", 6900 },
+{ 0x7243, "Radeon X1900 Series R580", 6900 },
+{ 0x7244, "Radeon X1950 Series R580", 6900 },
+{ 0x7245, "Radeon X1900 Series R580", 6900 },
+{ 0x7246, "Radeon X1900 Series R580", 6900 },
+{ 0x7247, "Radeon X1900 Series R580", 6900 },
+{ 0x7248, "Radeon X1900 Series R580", 6900 },
+{ 0x7249, "Radeon X1900 Series R580", 6900 },
+{ 0x724A, "Radeon X1900 Series R580", 6900 },
+{ 0x724B, "Radeon X1900 Series R580", 6900 },
+{ 0x724C, "Radeon X1900 Series R580", 6900 },
+{ 0x724D, "Radeon X1900 Series R580", 6900 },
+{ 0x724E, "FireStream 2U R580", 6900 },
+{ 0x724F, "Radeon X1900 Series R580", 6900 },
+{ 0x7260, "Radeon X1950 Series R580", 6900 },
+{ 0x7263, "Radeon X1900 Series R580", 6900 },
+{ 0x7264, "Radeon X1950 Series R580", 6900 },
+{ 0x7265, "Radeon X1900 Series R580", 6900 },
+{ 0x7266, "Radeon X1900 Series R580", 6900 },
+{ 0x7267, "Radeon X1900 Series R580", 6900 },
+{ 0x7268, "Radeon X1900 Series R580", 6900 },
+{ 0x7269, "Radeon X1900 Series R580", 6900 },
+{ 0x726A, "Radeon X1900 Series R580", 6900 },
+{ 0x726B, "Radeon X1900 Series R580", 6900 },
+{ 0x726C, "Radeon X1900 Series R580", 6900 },
+{ 0x726D, "Radeon X1900 Series R580", 6900 },
+{ 0x726E, "FireStream 2U R580", 6900 },
+{ 0x726F, "Radeon X1900 Series R580", 6900 },
+{ 0x7280, "Radeon X1950 Series R580", 6900 },
+{ 0x7284, "Mobility Radeon X1900 M58", 5400 },
+{ 0x7288, "Radeon X1950 GT R580", 6000 },
+{ 0x7291, "Radeon X1650 Series R580", 2000 },
+{ 0x7293, "Radeon X1650 Series R580", 2000 },
+{ 0x72A0, "Radeon X1950 Series R580", 6900 },
+{ 0x72A8, "Radeon X1950 GT R580", 6000 },
+{ 0x72B1, "Radeon X1650 Series R580", 2000 },
+{ 0x72B3, "Radeon X1650 Series R580", 2000 },
+{ 0x7835, "Mobility Radeon 9000 IGP", 1000 },
+{ 0x791E, "Radeon X1200 Series RS690", 700 },
+{ 0x791F, "Radeon X1200 Series RS690M", 700 },
+{ 0x793F, "Radeon Xpress 1200 Series RS600", 1000 },
+{ 0x7941, "Radeon Xpress 1200 Series RS600", 1000 },
+{ 0x7942, "Radeon Xpress 1200 Series RS600M", 1000 },
+{ 0x796E, "Radeon 2100 RS690", 1000 },
+{ 0x9400, "Radeon HD 2900 XT R600", 11900 },
+{ 0x9401, "Radeon HD 2900 XT R600", 11900 },
+{ 0x9402, "Radeon HD 2900 XT R600", 11900 },
+{ 0x9403, "Radeon HD 2900 PRO R600", 9600 },
+{ 0x9405, "Radeon HD 2900 GT R600", 7200 },
+{ 0x940A, "FireGL V8650 R600GL", 11008 },
+{ 0x940B, "FireGL V8600 R600GL", 11008 },
+{ 0x940F, "FireGL V7600 R600GL", 9600 },
+{ 0x9440, "Radeon HD 4870 RV770", 12000 },
+{ 0x9441, "Radeon HD 4870 X2 RV700", 16000 }, // 2xGPU, assume 1.5x perf
+{ 0x9442, "Radeon HD 4850 RV770", 10000 },
+{ 0x9443, "Radeon HD 4850 X2 RV700", 15000 }, // 2xGPU, assume 1.5x perf
+{ 0x9444, "FirePro V8750", 12000 },
+{ 0x944A, "Radeon HD 4800 Series M98", 8000 },
+{ 0x944B, "Radeon HD 4850 X2 M98", 1200 }, // 2xGPU, assume 1.5x perf
+{ 0x944C, "Radeon HD 4830 RV770", 9200 },
+{ 0x944E, "Radeon HD 4700 Series RV770", 6000 },
+{ 0x9450, "AMD FireStream 9270 RV770", 6000 },
+{ 0x9452, "AMD FireStream 9250 RV770", 6000 },
+{ 0x9456, "FirePro V8700", 12000 },
+{ 0x945A, "Radeon HD 4870 M98", 8800 },
+{ 0x9460, "Radeon HD 4800 Series RV790", 11200 },
+{ 0x9462, "Radeon HD 4800 Series RV790", 11200 },
+{ 0x9490, "Radeon HD 4600 Series RV730", 5200 },
+{ 0x9495, "Radeon HD 4600 Series RV730", 5200 },
+{ 0x9480, "Radeon HD 4650 M96", 4000 },
+{ 0x9488, "Radeon HD 4670 M96", 5400 },
+{ 0x9498, "Radeon HD 4600 Series RV730", 5200 },
+{ 0x949C, "FirePro V7750", 6400 },
+{ 0x949E, "FirePro V5700", 2400 },
+{ 0x949F, "FirePro V3750", 4400 },
+{ 0x94A0, "Radeon Graphics Processor M97", 6400 },
+{ 0x94A1, "Radeon Graphics Processor M97", 6400 },
+{ 0x94A3, "FirePro M7740", 10400 },
+{ 0x94B3, "Radeon HD 4770 RV740", 12000 },
+{ 0x94B4, "Radeon HD 4700 Series RV740", 12000 },
+{ 0x94B5, "Radeon HD 4770 RV740", 12000 },
+{ 0x94C1, "Radeon HD 2400 XT RV610", 2800 },
+{ 0x94C3, "Radeon HD 2400 Series RV610", 2100 },
+{ 0x94C4, "Radeon HD 2400 PRO AGP RV610", 2400 },
+{ 0x94C5, "Radeon HD 2400 LE RV610", 2100 },
+{ 0x94C7, "Radeon HD 2350 RV610", 2100 },
+{ 0x94C8, "Mobility Radeon HD 2400 XT M72", 2400 },
+{ 0x94C9, "Mobility Radeon HD 2400 M72", 1800 },
+{ 0x94CB, "Radeon E2400 M72", 1800 },
+{ 0x94CC, "Radeon HD 2400 Series RV610", 2100 },
+{ 0x9501, "Radeon HD 3690/3870/4750 RV630", 10700 },
+{ 0x9504, "Mobility Radeon HD 3850 M76", 9280 },
+{ 0x9505, "Radeon HD 3850/4750 RV630", 10700 },
+{ 0x9506, "Mobility Radeon HD 3850 X2 M76", 13000 }, // 2xGPU, assume 1.5x perf
+{ 0x9507, "Radeon HD 3830 RV630", 10700 },
+{ 0x9508, "Mobility Radeon HD 3870 M76", 10560 },
+{ 0x9509, "Mobility Radeon HD 3870 X2 M76", 15000 }, // 2xGPU, assume 1.5x perf
+{ 0x950F, "Radeon HD 3870 X2 RV630", 18000 }, // 2xGPU, assume 1.5x perf
+{ 0x9511, "FireGL V7700 RV630GL", 10700 },
+{ 0x9513, "Radeon HD 3850 X2 RV630", 15000 }, // 2xGPU, assume 1.5x perf
+{ 0x9515, "Radeon HD 3850", 10000 },
+{ 0x9519, "AMD FireStream 9170 RV630", 10700 },
+{ 0x9540, "Radeon HD 4590 R730", 4600 },
+{ 0x954F, "Radeon HD 4300/4500 Series R710", 2300 },
+{ 0x9552, "Radeon HD 4300 Series M92", 2000 },
+{ 0x9553, "Radeon HD 4500 Series M92", 2200 },
+{ 0x9555, "Radeon Graphics Processor M93", 2300 },
+{ 0x9581, "Mobility Radeon HD 2600 M76", 2000 },
+{ 0x9583, "Mobility Radeon HD 2600 XT M76", 2720 },
+{ 0x9586, "Radeon HD 2600 XT AGP RV630", 3200 },
+{ 0x9587, "Radeon HD 2600 Pro AGP RV630", 2400 },
+{ 0x9588, "Radeon HD 2600 XT RV630", 3200 },
+{ 0x9589, "Radeon HD 2600 PRO / 3610 RV630", 2400 },
+{ 0x958A, "Radeon HD 2600 X2 Series RV630", 3200 }, // 2xGPU, assume 1.5x perf
+{ 0x9593, "Mobility Radeon HD 3670", 2720 },
+{ 0x958B, "Mobility Radeon HD 2600 XT Gemini M76", 2720 },
+{ 0x958C, "FireGL V5600 RV630GL", 2000 },
+{ 0x958D, "FireGL V3600 RV630GL", 2000 },
+{ 0x958E, "Radeon HD 2600 LE RV630", 2100 },
+{ 0x9590, "Radeon HD 3600 Series RV630", 2900 },
+{ 0x9591, "Radeon HD 3600 Series M76", 2900 },
+{ 0x9593, "Mobility Radeon HD 3670", 2720 },
+{ 0x9596, "Radeon HD 3600 Series RV630", 2900 },
+{ 0x9597, "Radeon HD 3600 Series RV630", 2900 },
+{ 0x9598, "Radeon HD 3600/3730/4570/4610 RV630", 2900 },
+{ 0x9599, "Radeon HD 3600 Series RV630", 2900 },
+{ 0x95C0, "Radeon HD 3400/3570/4250 RV610", 2400 },
+{ 0x95C2, "Mobility Radeon HD 3430 M72", 1800 },
+{ 0x95C4, "Mobility Radeon HD 3400 Series M72", 1800 },
+{ 0x95C5, "Radeon HD 3400/3550/4250 RV610", 2400 },
+{ 0x95C6, "Radeon HD 3450", 2400 },
+{ 0x95C7, "Radeon HD 3430 RV610", 1600 },
+{ 0x95CC, "FirePro V3700", 3200 },
+{ 0x95CD, "FireMV 2450 RV610", 2000 },
+{ 0x95CE, "FireMV 2260 RV610", 1600 },
+{ 0x95CF, "FireMV 2260 / FirePro 2260", 1600 },
+{ 0x9610, "Radeon HD 3200 Graphics RS780", 2000 },
+{ 0x9611, "Radeon 3100 Graphics RS780", 1400 },
+{ 0x9612, "Radeon HD 3200 Graphics RS780M", 2000 },
+{ 0x9613, "Radeon 3100 Graphics RS780M", 1200 },
+{ 0x9614, "Radeon HD 3300 Graphics RS780", 2000 },
+{ 0x9616, "AMD 760G RS780", 1600 },
+{ 0x9640, "Radeon 6550D", 4800 },
+{ 0x9641, "Radeon 6620G", 3550 },
+{ 0x9642, "Radeon 6370D", 1770 },
+{ 0x9643, "Radeon 6380G", 1600 },
+{ 0x9644, "Radeon 6410D", 2400 },
+{ 0x9645, "Radeon 6410D", 2400 },
+{ 0x9647, "Radeon 6520G", 3200 },
+{ 0x9648, "Radeon 6480G", 1770 },
+{ 0x9649, "Radeon 6480G", 1770 },
+{ 0x964A, "Radeon 6530D", 3540 },
+{ 0x9710, "Radeon HD 4200 RS880", 2000 },
+{ 0x9712, "Mobility Radeon HD 4200 M880G", 2000 },
+{ 0x9713, "Mobility Radeon HD 4100 M860G", 1000 },
+{ 0x9714, "Radeon HD 4290 RS880", 2800 },
+{ 0x9715, "Radeon HD 4250 RS880", 2240 },
+{ 0x9802, "Radeon HD 6300 Series", 2000 },
+{ 0x9803, "Radeon HD 6300 Series", 2000 },
+{ 0x9804, "Radeon HD 6200 Series", 1120 },
+{ 0x9805, "Radeon HD 6200 Series", 1120 },
+{ 0x9806, "Radeon HD 6320", 2000 },
+{ 0x9807, "Radeon HD 6290", 1120 },
+{ 0x9809, "Radeon HD 7310", 2000 },
+{ 0x9900, "Radeon HD 7660G", 4800 },
+{ 0x9901, "Radeon HD 7660D", 6000 },
+{ 0x9903, "Radeon HD 7640G", 4000 },
+{ 0x9904, "Radeon HD 7560D", 6000 },
+{ 0x9990, "Radeon HD 7520G", 3800 },
+{ 0, 0, 0 },
+};
+
+
+static const GraphicsDeviceDesc kNVIDIADevices[] = {
+{ 0x0020, "Riva TNT", 180 },
+{ 0x0028, "Riva TNT2/TNT2 Pro", 286 },
+{ 0x0029, "Riva TNT2 Ultra", 300 },
+{ 0x002C, "Vanta/Vanta LT", 160 },
+{ 0x002D, "Riva TNT2 Model 64/Model 64 Pro", 250 },
+{ 0x0040, "GeForce 6800 Ultra", 6400 },
+{ 0x0041, "GeForce 6800", 3200 },
+{ 0x0042, "GeForce 6800 LE", 2600 },
+{ 0x0043, "GeForce 6800 XE", 2600 },
+{ 0x0044, "GeForce 6800 XT", 2600 },
+{ 0x0045, "GeForce 6800 GT", 5600 },
+{ 0x0046, "GeForce 6800 GT", 5600 },
+{ 0x0047, "GeForce 6800 GS", 3600 },
+{ 0x0048, "GeForce 6800 XT", 2600 },
+{ 0x0049, "NV40GL", 3000 },
+{ 0x004D, "Quadro FX 3400", 5600 },
+{ 0x004E, "Quadro FX 4000", 3000 },
+{ 0x0090, "GeForce 7800 GTX", 6880 },
+{ 0x0091, "GeForce 7800 GTX", 6880 },
+{ 0x0092, "GeForce 7800 GT", 6400 },
+{ 0x0093, "GeForce 7800 GS", 3000 },
+{ 0x0095, "GeForce 7800 SLI", 7200 }, // 2xGPU, assume 1.5x perf
+{ 0x0098, "GeForce Go 7800", 3200 },
+{ 0x0099, "GeForce Go 7800 GTX", 6400 },
+{ 0x009D, "Quadro FX 4500", 7520 },
+{ 0x00A0, "Aladdin TNT2", 286 },
+{ 0x00C0, "GeForce 6800 GS", 3600 },
+{ 0x00C1, "GeForce 6800", 3200 },
+{ 0x00C2, "GeForce 6800 LE", 2600 },
+{ 0x00C3, "GeForce 6800 XT", 2600 },
+{ 0x00C8, "GeForce Go 6800", 3600 },
+{ 0x00C9, "GeForce Go 6800 Ultra", 5400 },
+{ 0x00CC, "Quadro FX Go1400", 2000 },
+{ 0x00CD, "Quadro FX 3450/4000 SDI", 3400 },
+{ 0x00CE, "Quadro FX 1400", 2800 },
+{ 0x00F0, "GeForce 6800", 3200 },
+{ 0x00F1, "GeForce 6600 GT", 2000 },
+{ 0x00F2, "GeForce 6600", 1200 },
+{ 0x00F3, "GeForce 6200", 600 },
+{ 0x00F4, "GeForce 6600 LE", 1200 },
+{ 0x00F5, "GeForce 7800 GS", 3000 },
+{ 0x00F6, "GeForce 6800 GS/XT", 2800 },
+{ 0x00F8, "Quadro FX 3400/4400", 5600 },
+{ 0x00F9, "GeForce 6800 Series GPU", 3200 },
+{ 0x00FA, "GeForce PCX 5750", 1700 },
+{ 0x00FB, "GeForce PCX 5900", 3400 },
+{ 0x00FC, "GeForce PCX 5300", 1000 },
+{ 0x00FD, "Quadro NVS 280 PCI-E", -1 },
+{ 0x00FE, "Quadro FX 1300", 2800 },
+{ 0x00FF, "GeForce PCX 4300", -1 },
+{ 0x0100, "GeForce 256", 480 },
+{ 0x0101, "GeForce DDR", 480 },
+{ 0x0103, "Quadro", -1 },
+{ 0x0110, "GeForce2 MX/MX 400", 400 },
+{ 0x0111, "GeForce2 MX 100/200", 300 },
+{ 0x0112, "GeForce2 Go", -1 },
+{ 0x0140, "GeForce 6600 GT", 2000 },
+{ 0x0141, "GeForce 6600", 1200 },
+{ 0x0142, "GeForce 6600 LE", 1200 },
+{ 0x0143, "GeForce 6600 VE", 1200 },
+{ 0x0144, "GeForce Go 6600", 3000 },
+{ 0x0145, "GeForce 6610 XL", 2100 },
+{ 0x0146, "GeForce Go 6600 TE/6200 TE", 1600 },
+{ 0x0147, "GeForce 6700 XL", 2100 },
+{ 0x0148, "GeForce Go 6600", 3000 },
+{ 0x0149, "GeForce Go 6600 GT", 3000 },
+{ 0x014A, "Quadro NVS 440", -1 },
+{ 0x014B, "NV43", -1 },
+{ 0x014C, "Quadro FX 540M", -1 },
+{ 0x014D, "Quadro FX 550", 2880 },
+{ 0x014E, "Quadro FX 540", 2400 },
+{ 0x014F, "GeForce 6200", 600 },
+{ 0x0150, "GeForce2 GTS/GeForce2 Pro", 800 },
+{ 0x0151, "GeForce2 Ti", 1000 },
+{ 0x0152, "GeForce2 Ultra", 1000 },
+{ 0x0153, "Quadro2 Pro", -1 },
+{ 0x0160, "GeForce 6500", 1400 },
+{ 0x0161, "GeForce 6200 TurboCache", 700 },
+{ 0x0162, "GeForce 6200SE TurboCache", 700 },
+{ 0x0163, "GeForce 6200 LE", 350 },
+{ 0x0164, "GeForce Go 6200", 1200 },
+{ 0x0165, "Quadro NVS 285", 700 },
+{ 0x0166, "GeForce Go 6400", 1600 },
+{ 0x0167, "GeForce Go 6200", 1200 },
+{ 0x0168, "GeForce Go 6400", 1600 },
+{ 0x0169, "GeForce 6250", 700 },
+{ 0x016A, "GeForce 7100 GS", 700 },
+{ 0x016B, "NV44GLM", -1 },
+{ 0x016C, "NV44GLM", -1 },
+{ 0x016D, "NV44GLM", -1 },
+{ 0x016E, "NV44GL", -1 },
+{ 0x0170, "GeForce4 MX 460", 600 },
+{ 0x0171, "GeForce4 MX 440", 550 },
+{ 0x0172, "GeForce4 MX 420", 500 },
+{ 0x0173, "GeForce4 MX 440-SE", 550 },
+{ 0x0174, "GeForce4 440 Go", -1 },
+{ 0x0175, "GeForce4 420 Go", -1 },
+{ 0x0176, "GeForce4 420 Go 32M", -1 },
+{ 0x0177, "GeForce4 460 Go", -1 },
+{ 0x0178, "Quadro4 550 XGL", -1 },
+{ 0x0179, "GeForce4 440 Go 64M", -1 },
+{ 0x017A, "Quadro NVS", -1 },
+{ 0x017C, "Quadro4 500 GoGL", -1 },
+{ 0x017D, "GeForce4 410 Go 16M", -1 },
+{ 0x0181, "GeForce4 MX 440 with AGP8X", 550 },
+{ 0x0182, "GeForce4 MX 440SE with AGP8X", 550 },
+{ 0x0183, "GeForce4 MX 420 with AGP8X", 500 },
+{ 0x0185, "GeForce4 MX 4000", 550 },
+{ 0x0186, "GeForce4 448 Go", -1 },
+{ 0x0187, "GeForce4 488 Go", -1 },
+{ 0x0188, "Quadro4 580 XGL", -1 },
+{ 0x018A, "Quadro NVS with AGP8X", -1 },
+{ 0x018B, "Quadro4 380 XGL", -1 },
+{ 0x018C, "Quadro NVS 50 PCI", -1 },
+{ 0x018D, "GeForce4 448 Go", -1 },
+{ 0x0191, "GeForce 8800 GTX", 13800 },
+{ 0x0193, "GeForce 8800 GTS", 10400 },
+{ 0x0194, "GeForce 8800 Ultra", 14688 },
+{ 0x0197, "Tesla C870", -1 },
+{ 0x019D, "Quadro FX 5600", 14400 },
+{ 0x019E, "Quadro FX 4600", 12000 },
+{ 0x01A0, "GeForce2 Integrated GPU", -1 },
+{ 0x01D0, "GeForce 7350 LE", 1100 },
+{ 0x01D1, "GeForce 7300 LE", 900 },
+{ 0x01D2, "GeForce 7550 LE", 1200 },
+{ 0x01D3, "GeForce 7300 SE", 900 },
+{ 0x01D4, "GeForce Go 7350", 800 },
+{ 0x01D6, "GeForce Go 7200", 450 },
+{ 0x01D7, "GeForce Go 7300", 700 },
+{ 0x01D8, "GeForce Go 7400", 900 },
+{ 0x01D9, "GeForce Go 7450", 1000 },
+{ 0x01DA, "Quadro NVS 110M", 600 },
+{ 0x01DB, "Quadro NVS 120M", 900 },
+{ 0x01DC, "Quadro FX 350M", 900 },
+{ 0x01DD, "GeForce 7500 LE", 1100 },
+{ 0x01DE, "Quadro FX 350", -1 },
+{ 0x01DF, "GeForce 7300 GS", 1100 },
+{ 0x01F0, "GeForce4 MX Integrated GPU", -1 },
+{ 0x0200, "GeForce3", 800 },
+{ 0x0201, "GeForce3 Ti 200", 700 },
+{ 0x0202, "GeForce3 Ti 500", 960 },
+{ 0x0203, "Quadro DCC", -1 },
+{ 0x0211, "GeForce 6800", 3200 },
+{ 0x0212, "GeForce 6800 LE", 2600 },
+{ 0x0215, "GeForce 6800 GT", 5600 },
+{ 0x0218, "GeForce 6800 XT", 2600 },
+{ 0x0220, "NV44", -1 },
+{ 0x0221, "GeForce 6200", 600 },
+{ 0x0222, "GeForce 6200 A-LE", 350 },
+{ 0x0228, "NV44M", -1 },
+{ 0x0240, "GeForce 6150", 475 },
+{ 0x0241, "GeForce 6150 LE", 425 },
+{ 0x0242, "GeForce 6100", 425 },
+{ 0x0244, "GeForce Go 6150", 425 },
+{ 0x0245, "Quadro NVS 210S / GeForce 6150LE", 425 },
+{ 0x0247, "GeForce Go 6100", 425 },
+{ 0x0250, "GeForce4 Ti 4600", 1200 },
+{ 0x0251, "GeForce4 Ti 4400", 1100 },
+{ 0x0252, "NV25", 2200 },
+{ 0x0253, "GeForce4 Ti 4200", 1000 },
+{ 0x0258, "Quadro4 900 XGL", 2400 },
+{ 0x0259, "Quadro4 750 XGL", 2200 },
+{ 0x025B, "Quadro4 700 XGL", 2200 },
+{ 0x0280, "GeForce4 Ti 4800", 1200 },
+{ 0x0281, "GeForce4 Ti 4200 with AGP8X", 1000 },
+{ 0x0282, "GeForce4 Ti 4800 SE", 1100 },
+{ 0x0286, "GeForce4 4200 Go", -1 },
+{ 0x0288, "Quadro4 980 XGL", 2400 },
+{ 0x0289, "Quadro4 780 XGL", -1 },
+{ 0x028C, "Quadro4 700 GoGL", -1 },
+{ 0x0290, "GeForce 7900 GTX", 10400 },
+{ 0x0291, "GeForce 7900 GT/GTO", 7200 },
+{ 0x0292, "GeForce 7900 GS", 7200 },
+{ 0x0293, "GeForce 7950 GX2", 16000 },
+{ 0x0294, "GeForce 7950 GX2", 16000 },
+{ 0x0295, "GeForce 7950 GT", 8800 },
+{ 0x0297, "GeForce Go 7950 GTX", 9200 },
+{ 0x0298, "GeForce Go 7900 GS", 6000 },
+{ 0x0299, "GeForce Go 7900 GTX", 8000 },
+{ 0x029A, "Quadro FX 2500M", 8000 },
+{ 0x029B, "Quadro FX 1500M", 6000 },
+{ 0x029C, "Quadro FX 5500", 11200 },
+{ 0x029D, "Quadro FX 3500", 7520 },
+{ 0x029E, "Quadro FX 1500", 6000 },
+{ 0x029F, "Quadro FX 4500 X2", 15040 },
+{ 0x02E0, "GeForce 7600 GT", 4480 },
+{ 0x02E1, "GeForce 7600 GS", 3200 },
+{ 0x02E2, "GeForce 7300 GT", 2800 },
+{ 0x02E3, "GeForce 7900 GS", 7200 },
+{ 0x02E4, "GeForce 7950 GT", 8800 },
+{ 0x0301, "GeForce FX 5800 Ultra", 4000 },
+{ 0x0302, "GeForce FX 5800", 3200 },
+{ 0x0308, "Quadro FX 2000", -1 },
+{ 0x0309, "Quadro FX 1000", -1 },
+{ 0x0311, "GeForce FX 5600 Ultra", 1400 },
+{ 0x0312, "GeForce FX 5600", 1300 },
+{ 0x0313, "NV31", -1 },
+{ 0x0314, "GeForce FX 5600XT", 940 },
+{ 0x0316, "NV31M", 1400 },
+{ 0x0317, "NV31M Pro", 1400 },
+{ 0x031A, "GeForce FX Go5600", 1400 },
+{ 0x031B, "GeForce FX Go5650", 1400 },
+{ 0x031C, "Quadro FX Go700", -1 },
+{ 0x031D, "NV31GLM", -1 },
+{ 0x031E, "NV31GLM Pro", -1 },
+{ 0x031F, "NV31GLM Pro", -1 },
+{ 0x0320, "GeForce FX 5200", 1000 },
+{ 0x0321, "GeForce FX 5200 Ultra", 1300 },
+{ 0x0322, "GeForce FX 5200", 1000 },
+{ 0x0323, "GeForce FX 5200LE", 800 },
+{ 0x0324, "GeForce FX Go5200", 1200 },
+{ 0x0325, "GeForce FX Go5250", 1200 },
+{ 0x0326, "GeForce FX 5500", 1080 },
+{ 0x0327, "GeForce FX 5100", 1000 },
+{ 0x0328, "GeForce FX Go5200 32M/64M", 1000 },
+{ 0x0329, "NV34MAP", 1800 },
+{ 0x032A, "Quadro NVS 280 PCI", -1 },
+{ 0x032B, "Quadro FX 500/FX 600", 1000 },
+{ 0x032C, "GeForce FX Go53xx", 1200 },
+{ 0x032D, "GeForce FX Go5100", 1200 },
+{ 0x032F, "NV34GL", 1000 },
+{ 0x0330, "GeForce FX 5900 Ultra", 3600 },
+{ 0x0331, "GeForce FX 5900", 3200 },
+{ 0x0332, "GeForce FX 5900XT", 3200 },
+{ 0x0333, "GeForce FX 5950 Ultra", 3800 },
+{ 0x0334, "GeForce FX 5900ZT", -1 },
+{ 0x0338, "Quadro FX 3000", -1 },
+{ 0x033F, "Quadro FX 700", 1100 },
+{ 0x0341, "GeForce FX 5700 Ultra", 1900 },
+{ 0x0342, "GeForce FX 5700", 1700 },
+{ 0x0343, "GeForce FX 5700LE", 1000 },
+{ 0x0344, "GeForce FX 5700VE", 1200 },
+{ 0x0345, "NV36", -1 },
+{ 0x0347, "GeForce FX Go5700", 1800 },
+{ 0x0348, "GeForce FX Go5700", 1800 },
+{ 0x0349, "NV36M Pro", 1800 },
+{ 0x034B, "NV36MAP", 1800 },
+{ 0x034C, "Quadro FX Go1000", -1 },
+{ 0x034E, "Quadro FX 1100", 1700 },
+{ 0x034F, "NV36GL", 1700 },
+{ 0x038B, "GeForce 7650 GS", 3600 },
+{ 0x0390, "GeForce 7650 GS", 3600 },
+{ 0x0391, "GeForce 7600 GT", 4480 },
+{ 0x0392, "GeForce 7600 GS", 3200 },
+{ 0x0393, "GeForce 7300 GT", 2800 },
+{ 0x0394, "GeForce 7600 LE", 2800 },
+{ 0x0395, "GeForce 7300 GT", 2800 },
+{ 0x0397, "GeForce Go 7700", 3600 },
+{ 0x0398, "GeForce Go 7600", 3600 },
+{ 0x0399, "GeForce Go 7600 GT", 4000 },
+{ 0x039A, "Quadro NVS 300M", 900 },
+{ 0x039F, "GeForce GTX 260", 16128 },
+{ 0x039B, "GeForce Go 7900 SE", 3600 },
+{ 0x039C, "Quadro FX 560M", -1 },
+{ 0x039E, "Quadro FX 560", 2800 },
+{ 0x03D0, "GeForce 6150SE nForce 430", 425 },
+{ 0x03D1, "GeForce 6100 nForce 405", 425 },
+{ 0x03D2, "GeForce 6100 nForce 400", 425 },
+{ 0x03D5, "GeForce 6100 nForce 420", 425 },
+{ 0x03D6, "GeForce 7025 / nForce 630a", 850 },
+{ 0x0400, "GeForce 8600 GTS", 5400 },
+{ 0x0401, "GeForce 8600 GT", 4320 },
+{ 0x0402, "GeForce 8600 GT", 4320 },
+{ 0x0403, "GeForce 8600 GS", 4320 },
+{ 0x0404, "GeForce 8400 GS", 1800 },
+{ 0x0405, "GeForce 9500M GS", 3800 },
+{ 0x0406, "GeForce 8300 GS", 1800 },
+{ 0x0407, "GeForce 8600M GT", 3800 },
+{ 0x0408, "GeForce 8600M GTS", 3800 },
+{ 0x0409, "GeForce 8700M GT", 5000 },
+{ 0x040A, "Quadro FX 370", 1440 },
+{ 0x040B, "Quadro NVS 320M", 4600 },
+{ 0x040C, "Quadro FX 570M", 3800 },
+{ 0x040D, "Quadro FX 1600M", 5000 },
+{ 0x040E, "Quadro FX 570", 3680 },
+{ 0x040F, "Quadro FX 1700", 3680 },
+{ 0x0410, "GeForce GT 330", 4320 },
+{ 0x0420, "GeForce 8400 SE", 1400 },
+{ 0x0421, "GeForce 8500 GT", 3600 },
+{ 0x0422, "GeForce 8400 GS", 1800 },
+{ 0x0423, "GeForce 8300 GS", 1800 },
+{ 0x0424, "GeForce 8400 GS", 1800 },
+{ 0x0425, "GeForce 8600M GS", 2400 },
+{ 0x0426, "GeForce 8400M GT", 1800 },
+{ 0x0427, "GeForce 8400M GS", 1600 },
+{ 0x0428, "GeForce 8400M G", 1600 },
+{ 0x0429, "Quadro NVS 140M", 1600 },
+{ 0x042A, "Quadro NVS 130M", 1400 },
+{ 0x042B, "Quadro NVS 135M", 1600 },
+{ 0x042C, "GeForce 9400 GT", 2200 },
+{ 0x042D, "Quadro FX 360M", 1600 },
+{ 0x042E, "GeForce 9300M G", 1600 },
+{ 0x042F, "Quadro NVS 290", 1840 },
+{ 0x0530, "GeForce 7190M / nForce 650M", 900 },
+{ 0x0531, "GeForce 7150M / nForce 630M", 850 },
+{ 0x0532, "MCP67M", 750 },
+{ 0x0533, "GeForce 7000M / nForce 610M", 700 },
+{ 0x053A, "GeForce 7050 PV / nForce 630a", 850 },
+{ 0x053B, "GeForce 7050 PV / nForce 630a", 850 },
+{ 0x053E, "GeForce 7025 / nForce 630a", 850 },
+{ 0x053F, "MCP67M", 750 },
+{ 0x05E0, "GeForce GTX 295", 24000 }, // 2xGPU, assume 1.5x perf
+{ 0x05E1, "GeForce GTX 280", 19264 },
+{ 0x05E2, "GeForce GTX 260", 16128 },
+{ 0x05E3, "GeForce GTX 285", 20736 },
+{ 0x05E6, "GeForce GTX 275", 17724 },
+{ 0x05E7, "Tesla C1060/T10", -1 },
+{ 0x05EA, "GeForce GTX 260", 16128 },
+{ 0x05EB, "GeForce GTX 295", 24000 }, // 2xGPU, assume 1.5x perf
+{ 0x05ED, "Quadroplex 2200 D2", -1 },
+{ 0x05F8, "Quadroplex 2200 S4", -1 },
+{ 0x05F9, "Quadro CX", 14448 },
+{ 0x05FD, "Quadro FX 5800", 20736 },
+{ 0x05FE, "Quadro FX 4800", 14448 },
+{ 0x05FF, "Quadro FX 3800", 9632 },
+{ 0x0600, "GeForce 8800 GTS 512", 10400 },
+{ 0x0601, "GeForce 9800 GT", 9600 },
+{ 0x0602, "GeForce 8800 GT", 9600 },
+{ 0x0603, "GeForce GT 230", 8000 },
+{ 0x0604, "GeForce 9800 GX2", 14000 }, // 2xGPU, assume 1.5x of one card
+{ 0x0605, "GeForce 9800 GT", 9600 },
+{ 0x0606, "GeForce 8800 GS", 6600 },
+{ 0x0607, "GeForce GTS 240", 10800 },
+{ 0x0608, "GeForce 9800M GTX", 8000 },
+{ 0x0609, "GeForce 8800M GTS", 8000 },
+{ 0x060A, "GeForce GTX 280M", 9360 },
+{ 0x060B, "GeForce 9800M GT", 8000 },
+{ 0x060C, "GeForce 8800M GTX", 8000 },
+{ 0x060D, "GeForce 8800 GS", 6600 },
+{ 0x060F, "GeForce GTX 285M", 9600 },
+{ 0x0610, "GeForce 9600 GSO", 6600 },
+{ 0x0611, "GeForce 8800 GT", 9600 },
+{ 0x0612, "GeForce 9800 GTX/9800 GTX+", 10800 },
+{ 0x0613, "GeForce 9800 GTX+", 11808 },
+{ 0x0614, "GeForce 9800 GT", 9600 },
+{ 0x0615, "GeForce GTS 250", 11808 },
+{ 0x0617, "GeForce 9800M GTX", 8000 },
+{ 0x0618, "GeForce GTX 260M", 8800 },
+{ 0x0619, "Quadro FX 4700 X2", 12000 }, // 2xGPU, assume 1.5x perf
+{ 0x061A, "Quadro FX 3700", 12720 },
+{ 0x061B, "Quadro VX 200", 12720 },
+{ 0x061C, "Quadro FX 3600M", 8000 },
+{ 0x061D, "Quadro FX 2800M", 8000 },
+{ 0x061E, "Quadro FX 3700M", 8800 },
+{ 0x061F, "Quadro FX 3800M", 10800 },
+{ 0x0621, "GeForce GT 230", 8000 },
+{ 0x0622, "GeForce 9600 GT", 10400 },
+{ 0x0623, "GeForce 9600 GS", 6000 },
+{ 0x0624, "G94", -1 },
+{ 0x0625, "GeForce 9600 GSO 512", 10400 },
+{ 0x0626, "GeForce GT 130", 6000 },
+{ 0x0627, "GeForce GT 140", 10400 },
+{ 0x0628, "GeForce 9800M GTS", 9600 },
+{ 0x062A, "GeForce 9700M GTS", 8480 },
+{ 0x062B, "GeForce 9800M GT", 8000 },
+{ 0x062C, "GeForce 9800M GTS", 8000 },
+{ 0x062D, "GeForce 9600 GT", 10400 },
+{ 0x062E, "GeForce 9600 GT", 10400 },
+{ 0x062F, "GeForce 9800 S", 9600 },
+{ 0x0630, "GeForce 9700 S", 9600 },
+{ 0x0631, "GeForce GTS 160M", 9600 },
+{ 0x0632, "GeForce GTS 150M", 6400 },
+{ 0x0635, "GeForce 9600 GSO", 6600 },
+{ 0x0637, "GeForce 9600 GT", 10400 },
+{ 0x0638, "Quadro FX 1800", 6600 },
+{ 0x063A, "Quadro FX 2700M", 8480 },
+{ 0x0638, "Quadro FX 1800", 6600 },
+{ 0x0640, "GeForce 9500 GT", 4400 },
+{ 0x0641, "GeForce 9400 GT", 2200 },
+{ 0x0643, "GeForce 9500 GT", 4400 },
+{ 0x0644, "GeForce 9500 GS", 4000 },
+{ 0x0645, "GeForce 9500 GS", 4000 },
+{ 0x0646, "GeForce GT 120", 4400 },
+{ 0x0647, "GeForce 9600M GT", 4000 },
+{ 0x0648, "GeForce 9600M GS", 3440 },
+{ 0x0649, "GeForce 9600M GT", 4000 },
+{ 0x064A, "GeForce 9700M GT", 5000 },
+{ 0x064B, "GeForce 9500M G", 4000 },
+{ 0x064C, "GeForce 9650M GT", 4400 },
+{ 0x064E, "GeForce 9800 GT", 9600 },
+{ 0x0650, "G96-825", -1 },
+{ 0x0651, "GeForce G 110M", 1600 },
+{ 0x0652, "GeForce GT 130M", 4800 },
+{ 0x0653, "GeForce GT 120M", 4000 },
+{ 0x0654, "GeForce GT 220M", 4000 },
+{ 0x0655, "GeForce 9600 S / GT 120", 4400 },
+{ 0x0658, "Quadro FX 380", 3600 },
+{ 0x0659, "Quadro FX 580", 3600 },
+{ 0x065A, "Quadro FX 1700M", 5000 },
+{ 0x065B, "GeForce 9400 GT", 2200 },
+{ 0x065C, "Quadro FX 770M", 4000 },
+{ 0x065F, "GeForce G210", 2356 },
+{ 0x06C0, "GeForce GTX 480", 33600 },
+{ 0x06C4, "GeForce GTX 465", 19420 },
+{ 0x06CA, "GeForce GTX 480M", 13600 },
+{ 0x06CD, "GeForce GTX 470", 24280 },
+{ 0x06D1, "Tesla C2050", -1 },
+{ 0x06D2, "Tesla M2070", -1 },
+{ 0x06D8, "Quadro 6000", 27552 },
+{ 0x06D9, "Quadro 5000", 20530 },
+{ 0x06DD, "Quadro 4000", 15200 },
+{ 0x06DE, "Tesla S2050", -1 },
+{ 0x06E0, "GeForce 9300 GE", 2160 },
+{ 0x06E1, "GeForce 9300 GS", 2268 },
+{ 0x06E2, "GeForce 8400", 1800 },
+{ 0x06E3, "GeForce 8400 SE", 1800 },
+{ 0x06E4, "GeForce 8400 GS", 2268 },
+{ 0x06E5, "GeForce 9300M GS", 2200 },
+{ 0x06E6, "GeForce G100", 2150 },
+{ 0x06E7, "GeForce 9300 SE", 1800 },
+{ 0x06E8, "GeForce 9200M GS", 2200 },
+{ 0x06E9, "GeForce 9300M GS", 2200 },
+{ 0x06EA, "Quadro NVS 150M", 2120 },
+{ 0x06EB, "Quadro NVS 160M", 2320 },
+{ 0x06EC, "GeForce G 105M", 2560 },
+{ 0x06ED, "G98-GL", 2200 },
+{ 0x06EF, "GeForce G 103M", 2560 },
+{ 0x06F1, "GeForce G105M", 2560 },
+{ 0x06F8, "Quadro NVS 420", 3300 }, // 2xGPU, 1.5x perf
+{ 0x06F9, "Quadro FX 370 LP", 2160 },
+{ 0x06FA, "Quadro NVS 450", 3300 }, // 2xGPU, 1.5x perf
+{ 0x06FB, "Quadro FX 370M", 2200 },
+{ 0x06FD, "Quadro NVS 295", 2200 },
+{ 0x06FF, "HICx8/16 + Graphics", -1 },
+{ 0x07E0, "GeForce 7150 / nForce 630i", 1260 },
+{ 0x07E1, "GeForce 7100 / nForce 630i", 1200 },
+{ 0x07E2, "GeForce 7050 / nForce 630i", 1000 },
+{ 0x07E3, "GeForce 7050 / nForce 610i", 1000 },
+{ 0x07E5, "GeForce 7050 / nForce 620i", 1000 },
+{ 0x0844, "GeForce 9100M G", 1800 },
+{ 0x0845, "GeForce 8200M G", 1600 },
+{ 0x0846, "GeForce 9200", 2200 },
+{ 0x0847, "GeForce 9100", 2000 },
+{ 0x0848, "GeForce 8300", 2000 },
+{ 0x0849, "GeForce 8200", 2000 },
+{ 0x084A, "nForce 730a", -1 },
+{ 0x084B, "GeForce 9200", 2200 },
+{ 0x084C, "nForce 980a/780a SLI", -1 },
+{ 0x084D, "nForce 750a SLI", -1 },
+{ 0x084F, "GeForce 8100 / nForce 720a", 2000 },
+{ 0x0860, "GeForce 9400", 2320 },
+{ 0x0861, "GeForce 9400", 2320 },
+{ 0x0862, "GeForce 9400M G", 1800 },
+{ 0x0863, "GeForce 9400M", 1800 },
+{ 0x0864, "GeForce 9300", 1800 },
+{ 0x0865, "ION", 1800 },
+{ 0x0866, "GeForce 9400M", 1800 },
+{ 0x0867, "GeForce 9400", 2320 },
+{ 0x0868, "nForce 760i SLI", 2320 },
+{ 0x0869, "GeForce 9400", 2320 },
+{ 0x086A, "GeForce 9400", 2320 },
+{ 0x086C, "GeForce 9300 / nForce 730i", 1800 },
+{ 0x086D, "GeForce 9200", 2200 },
+{ 0x086E, "GeForce 9100M G", 1800 },
+{ 0x086F, "GeForce 8200M G", 1600 },
+{ 0x0870, "GeForce 9400M", 1800 },
+{ 0x0871, "GeForce 9200", 2200 },
+{ 0x0872, "GeForce G102M", 1800 },
+{ 0x0873, "GeForce G102M", 1800 },
+{ 0x0874, "ION", 1800 },
+{ 0x0876, "ION", 1800 },
+{ 0x087A, "GeForce 9400", 2320 },
+{ 0x087D, "ION", 1800 },
+{ 0x087E, "ION LE", 1600 },
+{ 0x087F, "ION LE", 1600 },
+{ 0x08A0, "GeForce 320M", 3600 },
+{ 0x08A1, "MCP89-MZT", -1 },
+{ 0x08A2, "GeForce 320M", 3600 },
+{ 0x08A3, "GeForce 320M", 3600 },
+{ 0x08A4, "GeForce 320M", 3600 },
+{ 0x08B1, "GeForce 300M", 2000 },
+{ 0x08B2, "GeForce 300M", 2000 },
+{ 0x08B3, "MCP89 MM9", -1 },
+{ 0x0A20, "GeForce GT 220", 5000 },
+{ 0x0A21, "D10M2-20", -1 },
+{ 0x0A22, "GeForce 315", 3800 },
+{ 0x0A23, "GeForce 210", 2356 },
+{ 0x0A26, "GeForce 405", 2356 },
+{ 0x0A27, "GeForce 405", 2356 },
+{ 0x0A28, "GeForce GT 230M", 4000 },
+{ 0x0A29, "GeForce GT 330M", 4600 },
+{ 0x0A2A, "GeForce GT 230M", 4000 },
+{ 0x0A2B, "GeForce GT 330M", 4600 },
+{ 0x0A2C, "NVS 5100M", 4400 },
+{ 0x0A2D, "GeForce GT 320M", 4000 },
+{ 0x0A30, "GeForce GT 330M", 4600 },
+{ 0x0A34, "GeForce GT 240M", 4400 },
+{ 0x0A35, "GeForce GT 325M", 3600 },
+{ 0x0A38, "Quadro 400", 3600 },
+{ 0x0A3C, "Quadro FX 880M", 4400 },
+{ 0x0A60, "GeForce G210", 2356 },
+{ 0x0A61, "D10M1-20", -1 },
+{ 0x0A62, "GeForce 205", 2356 },
+{ 0x0A63, "GeForce 310", 2356 },
+{ 0x0A64, "ION", 1800 },
+{ 0x0A65, "GeForce 210", 2356 },
+{ 0x0A66, "GeForce 310", 2356 },
+{ 0x0A67, "GeForce 315", 3800 },
+{ 0x0A68, "GeForce G105M", 2560 },
+{ 0x0A69, "GeForce G105M", 2560 },
+{ 0x0A6A, "NVS 2100M", 2140 },
+{ 0x0A6C, "NVS 3100M", 2400 },
+{ 0x0A6E, "GeForce 305M", 2100 },
+{ 0x0A6F, "ION", 1800 },
+{ 0x0A70, "GeForce 310M", 2500 },
+{ 0x0A71, "GeForce 305M", 2100 },
+{ 0x0A72, "GeForce 310M", 2500 },
+{ 0x0A73, "GeForce 305M", 2100 },
+{ 0x0A74, "GeForce G210M", 2500 },
+{ 0x0A75, "GeForce 310M", 2500 },
+{ 0x0A76, "ION", 1800 },
+{ 0x0A78, "Quadro FX 380 LP", 2356 },
+{ 0x0A7A, "GeForce 315M", 2420 },
+{ 0x0A7C, "Quadro FX 380M", 2500 },
+{ 0x0CA0, "GeForce GT 330", 4320 },
+{ 0x0CA2, "GeForce GT 320", 4320 },
+{ 0x0CA3, "GeForce GT 240", 4400 },
+{ 0x0CA4, "GeForce GT 340", 4400 },
+{ 0x0CA5, "GT 220", 5000 },
+{ 0x0CA7, "GeForce GT 330", 4320 },
+{ 0x0CA8, "GeForce GTS 260M", 4400 },
+{ 0x0CA9, "GeForce GTS 250M", 4000 },
+{ 0x0CAC, "GeForce 315", 3800 },
+{ 0x0CAF, "GeForce GT 335M", 3600 },
+{ 0x0CB0, "GeForce GTS 350M", 4000 },
+{ 0x0CB1, "GeForce GTS 360M", 4400 },
+{ 0x0CBC, "Quadro FX 1800M", 3600 },
+{ 0x0DC0, "GeForce GT 440", 4000 },
+{ 0x0DC4, "GeForce GTS 450", 12530 },
+{ 0x0DC5, "GeForce GTS 450", 12530 },
+{ 0x0DC6, "GeForce GTS 450", 12530 },
+{ 0x0DCD, "GeForce GTS 555M", 10400 },
+{ 0x0DCE, "GeForce GT 555M", 10400 },
+{ 0x0DD1, "GeForce GTX 460M", 16200 },
+{ 0x0DD2, "GeForce GT 445M", 10000 },
+{ 0x0DD3, "GeForce GT 435M", 2600 },
+{ 0x0DD6, "GeForce GT 550M", 4000 },
+{ 0x0DD8, "Quadro 2000", 10000 },
+{ 0x0DDA, "Quadro 2000M", 8800 },
+{ 0x0DE0, "GeForce GT 440", 4000 },
+{ 0x0DE1, "GeForce GT 430", 2800 },
+{ 0x0DE2, "GeForce GT 420", 2800 },
+{ 0x0DE4, "GeForce GT 520", 3240 },
+{ 0x0DE5, "GeForce GT 530", 2800 },
+{ 0x0DE9, "GeForce GT 630M", 3200 },
+{ 0x0DEA, "GeForce 610M", 3600 },
+{ 0x0DEB, "GeForce GT 555M", 10400 },
+{ 0x0DEC, "GeForce GT 525M", 2400 },
+{ 0x0DEE, "GeForce GT 415M", 2000 },
+{ 0x0DF0, "GeForce GT 425M", 2240 },
+{ 0x0DF1, "GeForce GT 420M", 2000 },
+{ 0x0DF2, "GeForce GT 435M", 2600 },
+{ 0x0DF4, "GeForce GT 540M", 2688 },
+{ 0x0DF5, "GeForce GT 525M", 2400 },
+{ 0x0DF6, "GeForce GT 550M", 4000 },
+{ 0x0DF7, "GeForce GT 520M", 2960 },
+{ 0x0DF8, "Quadro 600", 2560 },
+{ 0x0DFA, "Quadro 1000M", 2800 },
+{ 0x0DFC, "NVS 5200M", 2500 },
+{ 0x0E22, "GeForce GTX 460", 16200 },
+{ 0x0E23, "GeForce GTX 460 SE", 20800 },
+{ 0x0E24, "GeForce GTX 460", 21600 },
+{ 0x0E31, "GeForce GTX 485M", 18400 },
+{ 0x0E3A, "Quadro 3000M", 14400 },
+{ 0x0E3B, "Quadro 4000M", 15200 },
+{ 0x0F00, "GeForce GT 630", 7000 },
+{ 0x0F01, "GeForce GT 620", 3240 },
+{ 0x0FC0, "GeForce GT 640", 12800 },
+{ 0x0FC1, "GeForce GT 640", 12800 },
+{ 0x0FC2, "GeForce GT 630", 7000 },
+{ 0x0FC6, "GeForce GTX 650", 16900 },
+{ 0x0FD1, "GeForce GT 650M", 11800 },
+{ 0x0FD2, "GeForce GT 640M", 10000 },
+{ 0x0FD4, "GeForce GTX 660M", 13400 },
+{ 0x0FD5, "GeForce GT 650M", 11800 },
+{ 0x0FD8, "GeForce GT 640M", 10000 },
+{ 0x0FE0, "GeForce GTX 660M", 13400 },
+{ 0x0FE4, "GeForce GT 750M", 15500 },
+{ 0x0FFC, "Quadro K1000M", 13600 },
+{ 0x1004, "GeForce GTX 780", 41400 },
+{ 0x1040, "GeForce GT 520", 3240 },
+{ 0x1042, "GeForce 510", 2100 },
+{ 0x1049, "GeForce GT 620", 3240 },
+{ 0x104A, "GeForce GT 610", 2800 },
+{ 0x104B, "GeForce 9600 GT", 10400 },
+{ 0x1050, "GeForce GT 520M", 2960 },
+{ 0x1051, "GeForce GT 520MX", 3600 },
+{ 0x1054, "GeForce 410M", 2300 },
+{ 0x1055, "GeForce 410M", 2300 },
+{ 0x1056, "NVS 4200M", 3240 },
+{ 0x1057, "NVS 4200M", 3240 },
+{ 0x1058, "GeForce 610M", 3600 },
+{ 0x1080, "GeForce GTX 580", 37060 },
+{ 0x1081, "GeForce GTX 570", 29280 },
+{ 0x1082, "GeForce GTX 560 Ti", 26300 },
+{ 0x1084, "GeForce GTX 560", 25900 },
+{ 0x1086, "GeForce GTX 570", 29280 },
+{ 0x1087, "GeForce GTX 560 Ti", 26300 },
+{ 0x1088, "GeForce GTX 590", 43710 }, // 2xGPU, assume 1.5x perf
+{ 0x1089, "GeForce GTX 580", 37060 },
+{ 0x109A, "Quadro 5010M", 14400 },
+{ 0x10C0, "GeForce 9300 GS", 2268 },
+{ 0x10C3, "GeForce 8400GS", 2268 },
+{ 0x10C4, "ION", 1600 },
+{ 0x10C5, "GeForce 405", 1400 },
+{ 0x10D8, "NVS 300", 2356 },
+{ 0x1180, "GeForce GTX 680", 32200 },
+{ 0x1183, "GeForce GTX 660 Ti", 22000 },
+{ 0x1184, "GeForce GTX 770", 33500 },
+{ 0x1185, "GeForce GTX 660", 19800 },
+{ 0x1187, "GeForce GTX 760", 31360 },
+{ 0x1188, "GeForce GTX 690", 45000 }, // 2xGPU, assume 1.5x perf
+{ 0x1189, "GeForce GTX 670", 25600 },
+{ 0x11A0, "GeForce GTX 680M", 23000 },
+{ 0x11A2, "GeForce GTX 675MX", 19200 },
+{ 0x11A3, "GeForce GTX 680MX", 23000 },
+{ 0x11C0, "GeForce GTX 660", 19800 },
+{ 0x11C2, "GeForce GTX 650 TiBoost", 23500 },
+{ 0x11C6, "GeForce GTX 650 Ti", 14800 },
+{ 0x11E2, "GeForce GTX 765M", 13600 },
+{ 0x1200, "GeForce GTX 560 Ti", 26300 },
+{ 0x1201, "GeForce GTX 560", 25900 },
+{ 0x1205, "GeForce GTX 460 v2", 20800 },
+{ 0x1206, "GeForce GTX 555", 20800 },
+{ 0x1208, "GeForce GTX 560 SE", 17700 },
+{ 0x1210, "GeForce GTX 570M", 13800 },
+{ 0x1211, "GeForce GTX 580M", 19800 },
+{ 0x1212, "GeForce GTX 675M", 19800 },
+{ 0x1213, "GeForce GTX 670M", 14600 },
+{ 0x1241, "GeForce GT 545", 14000 },
+{ 0x1243, "GeForce GT 545", 14000 },
+{ 0x1244, "GeForce GTX 550 Ti", 21600 },
+{ 0x1245, "GeForce GTS 450", 12530 },
+{ 0x1246, "GeForce GT 550M", 2960 },
+{ 0x1247, "GeForce GT 555M", 10400 },
+{ 0x124B, "GeForce GT 640", 12800 },
+{ 0x124D, "GeForce GT 555M", 10400 },
+{ 0x1251, "GeForce GTX 560M", 18600 },
+{ 0, 0, 0 },
+};
+
+
+static const GraphicsDeviceDesc kIntelDevices[] = {
+{ 0x0042, "GMA HD", 1466 },
+{ 0x0046, "GMA HD", 1800 },
+{ 0x004A, "GMA HD", 1466 },
+{ 0x0102, "SandyBridge GT1", 2000 },
+{ 0x0106, "SandyBridge M GT1", 2000 },
+{ 0x010A, "SandyBridge Server", 2000 },
+{ 0x0112, "SandyBridge GT2", 2400 },
+{ 0x0116, "SandyBridge M GT2", 2400 },
+{ 0x0122, "SandyBridge GT2+", 2400 },
+{ 0x0126, "SandyBridge M GT2+", 2400 },
+{ 0x0152, "IvyBridge", 3000 }, // all guesswork, 1.5x SB
+{ 0x0156, "IvyBridge", 3000 },
+{ 0x015A, "IvyBridge", 3000 },
+{ 0x0162, "IvyBridge", 3600 },
+{ 0x0166, "IvyBridge", 3600 },
+{ 0x016A, "IvyBridge", 3600 },
+{ 0x0172, "IvyBridge", 3600 },
+{ 0x0176, "IvyBridge", 3600 },
+{ 0x0412, "HD Graphics 4600", 5400 },
+{ 0x0416, "HD Graphics 4600M", 4800 },
+{ 0x0A16, "HD Graphics 4600M", 4800 },
+{ 0x0A26, "HD Graphics 5000M", 6400 },
+{ 0x1132, "I815", 150 },
+{ 0x2562, "82845G", 200 },
+{ 0x2572, "82865G", 266 },
+{ 0x2582, "915G", 1332 },
+{ 0x2592, "915GM", 800 },
+{ 0x2772, "945G", 1600 },
+{ 0x2776, "945G", 1600 },
+{ 0x2782, "915G", 1332 },
+{ 0x2792, "915GM", 800 },
+{ 0x27A2, "945GM / GMA950", 1000 },
+{ 0x27A6, "945GM / GMA950", 1000 },
+{ 0x27AE, "945GME / GMA950", 1000 },
+{ 0x2972, "946GZ", 1600 },
+{ 0x2973, "946GZ", 1600 },
+{ 0x2982, "82G35", 1067 },
+{ 0x2992, "Q965/Q963", 1600 },
+{ 0x2993, "Q965/Q963", 1600 },
+{ 0x29A2, "G965", 1067 },
+{ 0x29A3, "G965", 1067 },
+{ 0x29B2, "Q35", 1600 },
+{ 0x29B3, "Q35", 1600 },
+{ 0x29C2, "G33/G31", 1600 },
+{ 0x29C3, "G33/G31", 1600 },
+{ 0x29D2, "Q33", 1600 },
+{ 0x29D3, "Q33", 1600 },
+{ 0x2A02, "GM965", 1067 },
+{ 0x2A03, "GM965", 1067 },
+{ 0x2A12, "GME965", 1067 },
+{ 0x2A42, "Mobile 4 Series", 1280 },
+{ 0x2E02, "4 Series", 1280 },
+{ 0x2E12, "4 Series", 1280 },
+{ 0x2E22, "G45/G43", 1280 },
+{ 0x2E32, "4 Series", 1280 },
+{ 0x3577, "I830", 200 },
+{ 0x3582, "I855", 233 },
+{ 0x358E, "I854", 233 },
+{ 0x7121, "I810", 150 },
+{ 0x7123, "I810 DC100", 150 },
+{ 0x7125, "I810 E", 150 },
+{ 0x8108, "GMA 500", 200 },
+{ 0x8109, "GMA 500", 400 },
+{ 0xA001, "GMA 3150", 1067 },
+{ 0xA011, "GMA 3150", 1067 },
+{ 0, 0, 0 },
+};
+
+
+static const GraphicsDeviceDesc kSISDevices[] = {
+{ 0x6330, "Mirage", 268 },
+{ 0x6351, "Mirage 3", 600 },
+{ 0, 0, 0 },
+};
+
+static const GraphicsDeviceDesc kVIADevices[] = {
+{ 0x3371, "Chrome9", 502 },
+{ 0, 0, 0 },
+};
+
+static const GraphicsDeviceDesc kSoftwareDevices[] = {
+{ 0x0405, "Software", 1000 }, // shared ID between Apple Software Renderer and VMWare SVGA
+{ 0, 0, 0 },
+};
+
+struct GraphicsVendorDesc {
+ int vendorID;
+ const GraphicsDeviceDesc* devices;
+};
+
+static const GraphicsVendorDesc kGraphicsVendors[] = {
+ { 0x1002, kATIDevices },
+ { 0x10DE, kNVIDIADevices },
+ { 0x8086, kIntelDevices },
+ { 0x1039, kSISDevices },
+ { 0x1106, kVIADevices },
+ { 0x15AD, kSoftwareDevices },
+ // 3D3D,3DLabs
+ // 102B,Matrox
+ // 5333,S3
+ // 18CA,XGI
+ // 1023,Trident
+ // 104A,ImgTech
+ // 121A,3dfx
+ // 1AB8,Parallels
+};
+
+
+
+
+
+static const GraphicsDeviceDesc* FindGraphicsDeviceDesc (int vendorID, int deviceID)
+{
+ for (size_t iv = 0; iv < ARRAY_SIZE(kGraphicsVendors); ++iv)
+ {
+ if (kGraphicsVendors[iv].vendorID != vendorID)
+ continue;
+ const GraphicsDeviceDesc* dev = kGraphicsVendors[iv].devices;
+ while (dev->deviceID)
+ {
+ if (dev->deviceID == deviceID)
+ return dev;
+ ++dev;
+ }
+ }
+ return NULL;
+}
+
+int GetGraphicsPixelFillrate (int vendorID, int deviceID)
+{
+ const GraphicsDeviceDesc* desc = FindGraphicsDeviceDesc (vendorID, deviceID);
+ return desc ? desc->pixelFillrate : -1;
+}
+
+#endif // GRAPHICS_DEVICES_DB_AVAILABLE
+
diff --git a/Runtime/Misc/GraphicsDevicesDB.h b/Runtime/Misc/GraphicsDevicesDB.h
new file mode 100644
index 0000000..e702aaa
--- /dev/null
+++ b/Runtime/Misc/GraphicsDevicesDB.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#define GRAPHICS_DEVICES_DB_AVAILABLE (UNITY_WIN || UNITY_OSX || UNITY_LINUX)
+
+#if GRAPHICS_DEVICES_DB_AVAILABLE
+int GetGraphicsPixelFillrate (int vendorID, int deviceID);
+#else
+inline int GetGraphicsPixelFillrate (int vendorID, int deviceID)
+{
+ #if (UNITY_XENON || UNITY_PS3)
+ return 4000;
+ #elif (UNITY_WII)
+ return 972;
+ #else
+ return -1;
+ #endif
+}
+#endif
diff --git a/Runtime/Misc/GraphicsScriptingUtility.cpp b/Runtime/Misc/GraphicsScriptingUtility.cpp
new file mode 100644
index 0000000..64c209f
--- /dev/null
+++ b/Runtime/Misc/GraphicsScriptingUtility.cpp
@@ -0,0 +1,36 @@
+#include "GraphicsScriptingUtility.h"
+#include "Runtime/Scripting/ICallString.h"
+
+#if ENABLE_MONO
+# include "Runtime/Mono/MonoIncludes.h"
+# include "Runtime/Scripting/ScriptingUtility.h"
+#endif
+
+ShaderLab::FastPropertyName ScriptingStringToProperty(ICallString& iCallString)
+{
+ if(iCallString.IsNull())
+ return ShaderLab::FastPropertyName();
+
+#if ENABLE_MONO
+ MonoString* msname = iCallString.str;
+ int const kShortString = 255;
+ char namebuf [kShortString+1];
+
+ if( msname->length <= kShortString && FastTestAndConvertUtf16ToAscii(namebuf, mono_string_chars(msname), mono_string_length(msname)))
+ {
+ namebuf [mono_string_length(msname)] = '\0';
+ return ShaderLab::Property(namebuf);
+ }
+
+ char* name = mono_string_to_utf8(msname);
+ ShaderLab::FastPropertyName propertyName(ShaderLab::Property(name));
+ g_free(name);
+
+ return propertyName;
+
+#else
+
+ return ShaderLab::Property(iCallString.AsUTF8().c_str());
+
+#endif
+} \ No newline at end of file
diff --git a/Runtime/Misc/GraphicsScriptingUtility.h b/Runtime/Misc/GraphicsScriptingUtility.h
new file mode 100644
index 0000000..7998df4
--- /dev/null
+++ b/Runtime/Misc/GraphicsScriptingUtility.h
@@ -0,0 +1,12 @@
+#ifndef GRAPHICSSCRIPTINGUTILITY_H_
+#define GRAPHICSSCRIPTINGUTILITY_H_
+
+#include "UnityPrefix.h"
+
+#include "External/shaderlab/Library/FastPropertyName.h"
+
+struct ICallString;
+
+ShaderLab::FastPropertyName ScriptingStringToProperty(ICallString& msname);
+
+#endif \ No newline at end of file
diff --git a/Runtime/Misc/GuiManager.cpp b/Runtime/Misc/GuiManager.cpp
new file mode 100644
index 0000000..ac5816e
--- /dev/null
+++ b/Runtime/Misc/GuiManager.cpp
@@ -0,0 +1,605 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "GuiManager.h"
+#include "DeveloperConsole.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Input/InputManager.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Mono/MonoUtility.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/GUI/GuiState.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Utilities/UserAuthorizationManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+#if SUPPORT_REPRODUCE_LOG
+#include <fstream>
+#include "ReproductionLog.h"
+#endif
+
+#if ENABLE_UNITYGUI
+static GUIManager* s_GUIManager = NULL;
+
+void InitGUIManager ()
+{
+ AssertIf(s_GUIManager != NULL);
+ s_GUIManager = new GUIManager();
+}
+
+void CleanupGUIManager ()
+{
+ AssertIf(s_GUIManager == NULL);
+ delete s_GUIManager;
+ s_GUIManager = NULL;
+}
+
+GUIManager &GetGUIManager () {
+ AssertIf(s_GUIManager == NULL);
+ return *s_GUIManager;
+}
+
+
+GUIManager::GUIManager () {
+ m_CurrentDepth = 1;
+ m_MouseUsed = false;
+ m_RenderingUserGUI = false;
+ m_HasKeyboardControl = false;
+ m_KeyboardDirection = 0;
+ m_CurrentKeyboardBehaviour = 0;
+ m_CurrentBehaviour = NULL;
+ m_LastInputEventTime = 0.0f;
+ m_DidGUIWindowsEatLastEvent = false;
+ #if UNITY_EDITOR
+ m_HasKeyboardOverride = false;
+ m_KeyboardControl = 0;
+ #endif
+ m_mouseButtonsDown = 0;
+}
+
+void GUIManager::AddGUIScript (ListNode_& beh)
+{
+ m_GUIScripts.push_back(beh);
+}
+
+PROFILER_INFORMATION(gGUIRepaintProfile, "GUI.Repaint", kProfilerGUI)
+PROFILER_INFORMATION(gGUIEventProfile, "GUI.ProcessEvents", kProfilerGUI)
+
+void GUIManager::Repaint () {
+ GetInputManager().SetTextFieldInput(false);
+
+ InputEvent ie;
+ ie = m_LastEvent;
+ ie.type = InputEvent::kRepaint;
+
+ DoGUIEvent(ie, false);
+}
+
+bool GUIManager::AnyMouseButtonsDown()
+{
+ return GetGUIManager().m_mouseButtonsDown != 0;
+}
+
+void GUIManager::SetHasKeyboardControl (bool hasKeyboard) {
+ GetGUIManager().m_HasKeyboardControl = true;
+}
+
+void GUIManager::SetKeyboardDirection (int direction) {
+ GetGUIManager().m_KeyboardDirection = direction;
+}
+int GUIManager::GetKeyboardDirection () {
+ return GetGUIManager().m_KeyboardDirection;
+}
+
+#if UNITY_EDITOR
+// function EditorWindow can call to tell UnityGUI if this window has OS-level keyboard focus
+void GUIManager::SetHasKeyboardOverride (int mode) {
+ GetGUIManager().m_HasKeyboardOverride = mode;
+}
+#endif
+
+
+bool GUIManager::CurrentScriptHasKeyboardFocus () {
+ GUIManager &gm = GetGUIManager();
+#if UNITY_EDITOR
+ if (gm.m_HasKeyboardOverride == 1)
+ return true;
+ else if (gm.m_HasKeyboardOverride == 2)
+ return false;
+#endif
+
+ return gm.m_CurrentBehaviour != NULL && gm.m_CurrentKeyboardBehaviour.GetInstanceID() == gm.m_CurrentBehaviour->GetInstanceID();
+}
+void GUIManager::SetKeyboardScriptInstanceID (int instanceID) {
+ GetGUIManager().m_CurrentKeyboardBehaviour.SetInstanceID(instanceID);
+}
+
+// Small wrapper around DoGUI, that handles getting keyboard between the various controls.
+bool GUIManager::CallGUI (GUIManager::SortedScripts::iterator i, InputEvent &e, bool doWindows) {
+ MonoBehaviour *beh = i->beh;
+ m_CurrentBehaviour = beh;
+ bool retval = beh->DoGUI (e, true, true, doWindows, 0);
+ return retval;
+}
+
+void GUIManager::SetCurrentDepth (int depth) { GetGUIManager().m_CurrentDepth = depth; }
+int GUIManager::GetCurrentDepth () { return GetGUIManager().m_CurrentDepth; }
+
+#if UNITY_EDITOR
+void GUIManager::SetEditorGUIInfo (bool hasKeyboardFocus, Vector2f guiPixelOffset) {
+ if (MONO_COMMON.setViewInfo) {
+ void* params[] = { &hasKeyboardFocus, &guiPixelOffset };
+ CallStaticMonoMethod (MONO_COMMON.setViewInfo, params);
+ }
+}
+#endif
+
+void GUIManager::SendQueuedEvents ()
+{
+ #if UNITY_EDITOR
+ if (MONO_COMMON.setViewInfo) {
+ bool hasKeyboardFocus = false;
+ Vector2f screenPosition (0,0);
+ void* params[] = { &hasKeyboardFocus, &screenPosition };
+ CallStaticMonoMethod (MONO_COMMON.setViewInfo, params);
+ }
+ #endif
+
+ while (!m_Events.empty())
+ {
+ DoGUIEvent(m_Events.front(), true);
+ m_Events.pop_front();
+ }
+}
+
+//implemented in monobehaviour.cpp
+void CallGuiUtilityBeginGUI(ScriptingObjectPtr monoEvent,int skin,int instanceID,bool allowGUILayout,ScriptingObjectPtr idList);
+
+bool GUIManager::BeginWindows (InputEvent &event, int skin, int editorWindowID)
+{
+
+ ScriptingObjectPtr monoEvent = CreateMonoInputEvent(event);
+ m_DidGUIWindowsEatLastEvent = false;
+
+ ScriptingObjectPtr idList = ScriptingGetGCHandleTarget(GetGlobalGUIState().m_State.m_IDListHandle);
+
+ CallGuiUtilityBeginGUI(monoEvent, skin, 0, true, idList);
+#if ENABLE_MONO
+ void* params[] = { &skin, idList, &editorWindowID };
+ CallStaticMonoMethod(MONO_COMMON.beginGuiWindows, params);
+#elif UNITY_FLASH
+ FLASH_ASM_WITH_NEWSP("GUI.BeginWindows(%0, marshallmap.getObjectWithId(%1) as UnityEngine.IDList, %2);" : : "r"(skin), "r"(idList), "r"(editorWindowID));
+#endif
+
+ InputEvent ie;
+ MarshallManagedStructIntoNative(monoEvent,&ie);
+ return ie.type == InputEvent::kUsed;
+}
+
+bool GUIManager::GetDidGUIWindowsEatLastEvent () {
+ return GetGUIManager().m_DidGUIWindowsEatLastEvent;
+}
+
+void GUIManager::SetDidGUIWindowsEatLastEvent (bool value) {
+ GetGUIManager().m_DidGUIWindowsEatLastEvent = value;
+}
+
+void GUIManager::EndWindows () {
+ ScriptingObjectPtr idList = ScriptingGetGCHandleTarget(GetGlobalGUIState().m_State.m_IDListHandle);
+#if ENABLE_MONO
+ void* params[] = { idList };
+ CallStaticMonoMethod(MONO_COMMON.endGuiWindows, params);
+#elif UNITY_FLASH
+ FLASH_ASM_WITH_NEWSP("GUI.EndWindows(marshallmap.getObjectWithId(%0));" : : "r"(idList));
+#endif
+}
+
+// A note on how the popup windows work:
+// It's a bit of a hack: The gui system maintains a list of all popup windows that gets rebuilt every frame. Then we have some static functions we need to call to process that list.
+// It's a LOT simpler if we do the EndWindows call from inside EndGUI, hence we have to call it DURING executing a script (even if its static).
+
+// Here's how it goes:
+// We call BeginWindows with a layout event before anything else. This makes them init.
+// after the last layout event, we call EndWindows. Now the GUI system has an up-to-date list of windows.
+
+// During event processing, we call beginWindows just before the first script that lies on a depth > 0, so it can eat mouse clicks, etc....
+// EndWindows can be called at any time here, so we do it on the same script
+// During repaint, we can call BeginWindows at any point, but EndWindows on the LAST script that is in layer > 0. We call them at the same time.
+
+// I _think_ this can be cleaned up a bit, but I'm not yet ready to do so before I have it working in scene views, etc...
+
+struct OldSortScript : std::binary_function<GUIManager::SortedScript&, GUIManager::SortedScript&, std::size_t>
+{
+ bool operator () (GUIManager::SortedScript& lhs, GUIManager::SortedScript& rhs) const { return lhs.depth < rhs.depth; }
+};
+
+struct NewSortScript : std::binary_function<GUIManager::SortedScript&, GUIManager::SortedScript&, std::size_t>
+{
+ bool operator () (GUIManager::SortedScript& lhs, GUIManager::SortedScript& rhs) const { return lhs.depth > rhs.depth; }
+};
+
+void GUIManager::DoGUIEvent (InputEvent &e, bool frontToBack)
+{
+ #if ENABLE_PROFILER
+ ProfilerInformation* information = &gGUIEventProfile;
+ if (e.type == InputEvent::kRepaint)
+ information = &gGUIRepaintProfile;
+
+ PROFILER_AUTO(*information, NULL)
+ #endif
+
+#if UNITY_EDITOR
+ if (MONO_COMMON.setKeyboardControl)
+ {
+ void* args[] = { &m_KeyboardControl };
+ CallStaticMonoMethod(MONO_COMMON.setKeyboardControl, args);
+ }
+#endif
+
+ MonoBehaviour* authorizationDialog = GetUserAuthorizationManager().GetAuthorizationDialog();
+ MonoBehaviour* developerConsole = DeveloperConsole::Get();
+
+
+ // Update the lists of which sripts we _actually_ want to execute.
+ if (m_GUIScripts.empty() && authorizationDialog == NULL && !DeveloperConsole::IsVisible())
+ {
+ m_MouseUsed = false;
+ return;
+ }
+
+ // Move the event mouse position away if the screen is locked. We don't want the cursor to interact
+ // with the GUI when it is in an arbitrary, fixed position.
+ if (GetScreenManager().GetLockCursor())
+ e.mousePos = Vector2f (-10000, -10000);
+
+ // ok - first we send them the layout event and find out the layering
+ InputEvent::Type originalType = e.type;
+
+ int handleTab = 0;
+ if (e.type == InputEvent::kKeyDown && (e.character == '\t' || e.character == 25))
+ {
+ handleTab = ((e.modifiers & InputEvent::kShift) == 0) ? 1 : -1;
+#if ENABLE_MONO
+ if (MONO_COMMON.beginTabControlSearch)
+ CallStaticMonoMethod (MONO_COMMON.beginTabControlSearch, NULL);
+#endif
+ }
+
+
+
+ std::vector<PPtr<MonoBehaviour> > layoutedScripts;
+ if (authorizationDialog)
+ layoutedScripts.push_back (authorizationDialog);
+ else
+ {
+ layoutedScripts.reserve(m_GUIScripts.size_slow());
+ SafeListIterator<MonoBehaviour*> guiScriptIterator (m_GUIScripts);
+ while (guiScriptIterator.Next())
+ {
+ MonoBehaviour& beh = **guiScriptIterator;
+
+ if (beh.GetUseGUILayout())
+ layoutedScripts.push_back(&beh);
+ else
+ {
+ m_CurrentDepth = 1;
+ m_HasKeyboardControl = false;
+ beh.DoGUI (e, false, false, false, 0);
+ }
+ }
+ }
+ if (developerConsole)
+ layoutedScripts.push_back (developerConsole);
+ if (layoutedScripts.empty())
+ return;
+
+ e.type = InputEvent::kLayout;
+ BeginWindows (e, 0, 0);
+
+ m_SortedScripts.clear ();
+ int current = 1;
+ for (std::vector<PPtr<MonoBehaviour> >::iterator i = layoutedScripts.begin (); i != layoutedScripts.end ();i ++)
+ {
+ MonoBehaviour *beh = *i;
+ if (beh)
+ {
+ m_CurrentDepth = 1;
+ m_HasKeyboardControl = false;
+ bool doWindows = current == layoutedScripts.size();
+ beh->DoGUI (e, true, true, doWindows, 0);
+ m_SortedScripts.push_back (SortedScript (m_CurrentDepth, beh, m_HasKeyboardControl));
+ current++;
+ }
+ }
+
+// @TODO: Fix sort order for 3.0 by introducing new property
+// if( IsUnity2_6OrHigher() )
+// {
+// NewSortScript sort;
+// // Next, we sort by depth
+// m_SortedScripts.sort (sort);
+// }
+ OldSortScript sort;
+ // Next, we sort by depth
+ m_SortedScripts.sort (sort);
+
+ // If we don't have a keyboard-focused script (or the script has been disabled), we set it to be the topmost script;
+ MonoBehaviour* currentKey = m_CurrentKeyboardBehaviour;
+ bool found = false;
+ if (currentKey != NULL) {
+ for (SortedScripts::iterator i = m_SortedScripts.begin(); i != m_SortedScripts.end(); i++) {
+ if (i->beh == currentKey) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found)
+ m_CurrentKeyboardBehaviour = m_SortedScripts.begin()->beh;
+
+ e.type = originalType;
+
+ bool hasSentEndWindows = false;
+ current = 1; // reset the count so we can send the DoWindows.
+ bool eventUsed = false;
+ if (frontToBack)
+ {
+ for (SortedScripts::iterator i = m_SortedScripts.begin(); i != m_SortedScripts.end(); i++)
+ {
+ // If this is the first script in layer 0,
+ bool endWindows = false;
+ if ((current == m_SortedScripts.size() || i->depth > 0) && !hasSentEndWindows)
+ {
+ eventUsed = BeginWindows (e, 0, 0);
+ if (eventUsed)
+ break;
+ hasSentEndWindows = true;
+ endWindows = true;
+ }
+ current++;
+
+ eventUsed = CallGUI (i, e, endWindows); // Tell the last one to do windows as well
+
+ // If this script used the event, we terminate the loop
+ if (eventUsed)
+ break;
+ }
+
+ // Remove keyboard focus when clicking on empty GUI area
+ // (so text fields don't take away game view input).
+ if (originalType == InputEvent::kMouseDown && !eventUsed)
+ {
+ int noKeyboardControl = 0;
+ #if ENABLE_MONO
+ void* args[] = { &noKeyboardControl };
+ CallStaticMonoMethod(MONO_COMMON.setKeyboardControl, args);
+ #endif
+ }
+
+ // Handle mouse focus: We want the new GUI system to eat any mouse events. This means that we need to check this during repaint or mouseDown/Up
+ // and set a global variable accordingly.
+ if (originalType == InputEvent::kMouseDown || originalType == InputEvent::kMouseUp)
+ m_MouseUsed |= eventUsed;
+ }
+ else
+ {
+ // Flag the mouse as beign unused. During repainting, we will detect if any controls are draw under the mouse and mark
+ // it as used.
+ m_MouseUsed = false;
+ m_RenderingUserGUI = true;
+
+ // It's on purpose I'm iterating backwrds here - used by repainting
+ // this time, you can't bail out of the event processing
+ BeginWindows (e,0,0);
+ for (SortedScripts::iterator i = m_SortedScripts.end(); i != m_SortedScripts.begin();)
+ {
+ i--;
+ bool endWindows = false;
+
+
+ // If this window is the last, OR the next one is above 0 depth, we should do repaint all popup windows NOW
+ if (!hasSentEndWindows) // If we haven't already sent it
+ {
+ if (current == m_SortedScripts.size()) // If this is the last script, we must call it now
+ {
+ hasSentEndWindows = true;
+ endWindows = true;
+ }
+ else
+ {
+ // If this is the last script with a depth > 0, we must call it now.
+ SortedScripts::iterator j = i;
+ j--;
+ if (j->depth <= 0)
+ {
+ hasSentEndWindows = true;
+ endWindows = true;
+ }
+ }
+ }
+ CallGUI (i, e, endWindows);
+ current++;
+ }
+
+ m_RenderingUserGUI = false;
+ }
+
+ if (handleTab != 0 && !eventUsed)
+ {
+#if ENABLE_MONO
+ if (MONO_COMMON.endTabControlSearch)
+ {
+ void* params[] = { &handleTab };
+ CallStaticMonoMethod (MONO_COMMON.endTabControlSearch, params);
+
+ }
+#endif
+ }
+
+#if UNITY_EDITOR
+ if (MONO_COMMON.getKeyboardControl)
+ m_KeyboardControl = ExtractMonoObjectData<int> (CallStaticMonoMethod(MONO_COMMON.getKeyboardControl, NULL));
+#endif
+}
+
+void GUIManager::QueueEvent (InputEvent &ie)
+{
+ QueueEventImmediate(ie);
+}
+
+
+void GUIManager::QueueEventImmediate (InputEvent &ie)
+{
+ // MouseMove events are not sent.
+ // The same info can be obtained from repaint events.
+ if (ie.type == InputEvent::kMouseMove)
+ {
+ // We still use them as last event to update the cursor position.
+ m_LastEvent = ie;
+ return;
+ }
+ if (ie.type == InputEvent::kIgnore)
+ return;
+
+ if ( ie.type == InputEvent::kMouseDown )
+ m_mouseButtonsDown |= (1<<ie.button);
+ else if ( ie.type == InputEvent::kMouseUp )
+ m_mouseButtonsDown &= ~(1<<ie.button);
+
+ switch (ie.type) {
+ case InputEvent::kMouseDown:
+ case InputEvent::kMouseUp:
+ case InputEvent::kKeyDown:
+ ResetCursorFlash ();
+ break;
+ }
+
+ m_LastEvent = ie;
+ m_Events.push_back(ie);
+}
+
+void GUIManager::ResetCursorFlash ()
+{
+ #if SUPPORT_REPRODUCE_LOG
+ if (RunningReproduction())
+ return;
+ #endif
+
+ GetGUIManager().m_LastInputEventTime = GetTimeManager().GetRealtime ();
+}
+
+float GUIManager::GetCursorFlashTime ()
+{
+ return GetGUIManager().m_LastInputEventTime;
+}
+
+bool GUIManager::GetMouseUsed ()
+{
+ return GetGUIManager().m_MouseUsed;
+}
+
+void GUIManager::SetMouseUsed (bool used)
+{
+ // We only allow changing m_MouseUsed during rendering of User GUI (fix for case 387913)
+ if (GetGUIManager().m_RenderingUserGUI)
+ {
+ GetGUIManager().m_MouseUsed = used;
+ }
+}
+
+#if SUPPORT_REPRODUCE_LOG
+
+void WriteInputEvent (InputEvent& event, std::ostream& out)
+{
+ out << (int&)event.type << ' ';
+ WriteFloat(out, event.mousePos.x); out << ' ';
+ WriteFloat(out, event.mousePos.y); out << ' ';
+ WriteFloat(out, event.delta.x); out << ' ';
+ WriteFloat(out, event.delta.y); out << ' ';
+
+ out << event.button << ' ' << event.modifiers << ' ';
+ WriteFloat(out, event.pressure); out << ' ';
+ out << event.clickCount << ' ' << event.character << ' ' << event.keycode << ' ';
+
+ // if (event.commandString)
+ // WriteReproductionString(out, event.commandString);
+ // else
+ // WriteReproductionString(out, "");
+}
+
+void ReadInputEvent (InputEvent& event, std::istream& in, int version)
+{
+ event.Init();
+
+ in >> (int&)event.type;
+ ReadFloat(in, event.mousePos.x);
+ ReadFloat(in, event.mousePos.y);
+ ReadFloat(in, event.delta.x);
+ ReadFloat(in, event.delta.y);
+ in >> event.button >> event.modifiers;
+ ReadFloat(in, event.pressure);
+ in >> event.clickCount >> event.character >> event.keycode;
+
+ //if (version >= 6)
+ //{
+ // std::string commandString;
+ // ReadReproductionString(in, commandString);
+ // event.commandString = new char[commandString.size() + 1];
+ // memcpy(event.commandString, commandString.c_str(), commandString.size() + 1);
+ //}
+}
+
+void GUIManager::WriteLog (std::ofstream& out)
+{
+ out << "Events" << std::endl;
+
+ out << m_Events.size() << std::endl;
+
+ for (int i=0;i<m_Events.size();i++)
+ {
+ InputEvent& event = m_Events[i];
+ WriteInputEvent(event, out);
+ }
+
+ // Hover events seem to require the last event as well!
+ InputEvent& event = m_LastEvent;
+ WriteInputEvent (event, out);
+
+ if (GetReproduceVersion () >= 6)
+ {
+ WriteReproductionString(out, GetInputManager().GetCompositionString());
+ out << (int)(GetInputManager().GetTextFieldInput()) << ' ';
+ }
+
+ out << std::endl;
+}
+
+void GUIManager::ReadLog (std::ifstream& in)
+{
+ CheckReproduceTagAndExit("Events", in);
+
+ int size;
+ in >> size;
+ m_Events.clear();
+ for (int i=0;i<size;i++)
+ {
+ InputEvent event;
+ ReadInputEvent (event, in, GetReproduceVersion());
+ m_Events.push_back(event);
+ }
+
+ // Hover events seem to require the last event as well!
+ ReadInputEvent (m_LastEvent, in, GetReproduceVersion());
+
+ if (GetReproduceVersion () >= 6)
+ {
+ ReadReproductionString(in, GetInputManager().GetCompositionString());
+ int textFieldInput;
+ in >> textFieldInput;
+ GetInputManager().SetTextFieldInput(textFieldInput);
+ }
+}
+#endif
+#endif \ No newline at end of file
diff --git a/Runtime/Misc/InputEvent.cpp b/Runtime/Misc/InputEvent.cpp
new file mode 100644
index 0000000..b27eb17
--- /dev/null
+++ b/Runtime/Misc/InputEvent.cpp
@@ -0,0 +1,162 @@
+#include "UnityPrefix.h"
+#include "InputEvent.h"
+
+InputInterface::~InputInterface () {
+}
+bool InputInterface::PerformDrag (InputEvent &event) {
+ return false;
+}
+int InputInterface::UpdateDrag (InputEvent &event) {
+ return 0;
+}
+
+InputEvent::~InputEvent( )
+{
+ delete []commandString;
+}
+
+void InputEvent::Debug () const
+{
+#if ENABLE_NEW_EVENT_SYSTEM
+ Vector2f mousePosition = touch.pos;
+ Vector2f delta = touch.deltaPos;
+#endif
+ std::string buffer;
+ const char *EventNames[] = {
+ "kMouseDown", "kMouseUp", "kMouseMove", "kMouseDrag", "kKeyDown", "kKeyUp",
+ "kScrollWheel", "kRepaint", "kLayout", "kDragUpdated", "kDragPerform", "kIgnore", "kUsed"
+ };
+ bool isMouse = false;
+ bool isKeyb = false;
+ switch (type) {
+ case kKeyDown:
+ case kKeyUp:
+ case kScrollWheel:
+ isKeyb = true;
+ break;
+ case kMouseDown:
+ case kMouseUp:
+ case kMouseMove:
+ case kMouseDrag:
+ isMouse = true;
+ }
+ buffer += Format("Event: %s\n", EventNames[type]);
+ if (isMouse) {
+ buffer += Format(" Pos (%f, %f) \t Delta: (%f, %f)\n", mousePosition.x, mousePosition.y, delta.x, delta.y);
+ buffer += Format(" Button: %d", button);
+ buffer += Format(" Click count: %d", clickCount);
+ } else if (isKeyb) {
+ AssertIf(clickCount != 0);
+ if( character >= 32 && character <= 127 )
+ buffer += Format(" character: '%c'\n", character);
+ else
+ buffer += Format(" character: code %d\n", character);
+ buffer += Format(" keycode: '%d'\n", keycode);
+ }
+ buffer += " Mod: ";
+ if (modifiers & kShift) buffer += "shift ";
+ if (modifiers & kControl) buffer += "ctrl ";
+ if (modifiers & kAlt) buffer += "alt ";
+ if (modifiers & kCommand) buffer += "cmd ";
+ if (modifiers & kNumeric) buffer += "num ";
+ if (modifiers & kCapsLock) buffer += "capslock ";
+ if (modifiers & kFunctionKey) buffer += "fkey ";
+ if (modifiers == 0) buffer += "none ";
+ buffer += "\n";
+ printf_console( "%s", buffer.c_str() );
+}
+
+void InputEvent::Init( )
+{
+#if !ENABLE_NEW_EVENT_SYSTEM
+ mousePosition = Vector2f(0.0F, 0.0F);
+ delta = Vector2f(0.0F, 0.0F);
+ #if UNITY_METRO
+ touchType = kMouseTouch;
+ #endif
+#endif
+ type = InputEvent::kIgnore;
+ keycode = 0;
+ character = 0;
+ button = 0;
+ clickCount = 0;
+ pressure = 0;
+ modifiers = 0;
+ commandString = NULL;
+}
+
+InputEvent::InputEvent( const InputEvent& evt )
+{
+#if ENABLE_NEW_EVENT_SYSTEM
+ touch = evt.touch;
+#else
+ mousePosition = evt.mousePosition;
+ delta = evt.delta;
+ #if UNITY_METRO
+ touchType = evt.touchType;
+ #endif
+#endif
+ type = evt.type;
+ button = evt.button;
+ modifiers = evt.modifiers;
+ pressure = evt.pressure;
+ clickCount = evt.clickCount;
+ character = evt.character;
+ keycode = evt.keycode;
+
+ if (evt.commandString)
+ {
+ commandString = new char [strlen(evt.commandString) + 1];
+ memcpy(commandString, evt.commandString, strlen(evt.commandString) + 1);
+ }
+ else
+ {
+ commandString = NULL;
+ }
+}
+
+
+void InputEvent::operator = (const InputEvent& evt)
+{
+#if ENABLE_NEW_EVENT_SYSTEM
+ touch = evt.touch;
+#else
+ mousePosition = evt.mousePosition;
+ delta = evt.delta;
+ #if UNITY_METRO
+ touchType = evt.touchType;
+ #endif
+#endif
+ type = evt.type;
+ button = evt.button;
+ modifiers = evt.modifiers;
+ pressure = evt.pressure;
+ clickCount = evt.clickCount;
+ character = evt.character;
+ keycode = evt.keycode;
+
+ if (commandString)
+ {
+ delete[] commandString;
+ commandString = NULL;
+ }
+
+ if (evt.commandString)
+ {
+ commandString = new char [strlen(evt.commandString) + 1];
+ memcpy(commandString, evt.commandString, strlen(evt.commandString) + 1);
+ }
+}
+
+InputEvent InputEvent::CommandStringEvent (const std::string& editorCommand, bool execute)
+{
+ InputEvent event;
+ event.Init();
+ if (execute)
+ event.type = InputEvent::kExecuteCommand;
+ else
+ event.type = InputEvent::kValidateCommand;
+ event.commandString = new char[editorCommand.size() + 1];
+ memcpy(event.commandString, editorCommand.c_str(), editorCommand.size() + 1);
+ return event;
+}
diff --git a/Runtime/Misc/InputEvent.h b/Runtime/Misc/InputEvent.h
new file mode 100644
index 0000000..31112ee
--- /dev/null
+++ b/Runtime/Misc/InputEvent.h
@@ -0,0 +1,146 @@
+#ifndef INPUTINTERFACE_H
+#define INPUTINTERFACE_H
+
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Input/GetInput.h"
+#if UNITY_LINUX
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#endif
+#include <iostream>
+
+#ifdef __OBJC__
+ @class NSEvent;
+ @class NSView;
+ @class NSDraggingInfo;
+#endif
+
+/// An input event
+struct InputEvent {
+ InputEvent () { commandString = NULL; }
+#if UNITY_OSX && defined(__OBJC__)
+ InputEvent (NSEvent *src);
+ InputEvent (NSEvent *src, NSView *view, bool cullOffscreenMouseDownEvents = true);
+ InputEvent (id<NSDraggingInfo> draggingInfo, int inType, NSView* view);
+
+ static InputEvent RepaintEvent (NSView *source);
+#endif
+#if UNITY_WIN
+ InputEvent( UINT message, WPARAM wParam, LPARAM lParam, HWND window );
+ #if UNITY_EDITOR
+ InputEvent( int x, int y, DWORD keyFlags, int typ, HWND window );
+ static InputEvent RepaintEvent (HWND window);
+ static InputEvent CommandStringEvent (const std::string& editorCommand, bool execute, HWND window);
+ static InputEvent SimulateKeyPressEvent(HWND window, int keyCode, int modifiers);
+ #endif
+#endif
+#if UNITY_LINUX
+ static InputEvent RepaintEvent (NativeWindow window);
+ InputEvent ( int type, Vector2f location, int code, int state, unsigned long timeStamp );
+ InputEvent ( int type, unsigned int key, unsigned int keycode, unsigned state, Vector2f location );
+#elif UNITY_FLASH || UNITY_WEBGL
+ InputEvent( int type );
+ InputEvent(int eventType, int key, int code, int state);
+#endif
+
+ ~InputEvent( );
+
+ InputEvent( const InputEvent& evt );
+ void operator = (const InputEvent& evt);
+
+ static InputEvent CommandStringEvent (const std::string& editorCommand, bool execute);
+
+ void Debug () const;
+
+ enum Modifiers {
+ kShift = 1 << 0,
+ kControl = 1 << 1,
+ kAlt = 1 << 2,
+ kCommand = 1 << 3,
+ kNumeric = 1 << 4,
+ kCapsLock = 1 << 5,
+ kFunctionKey = 1 << 6
+ };
+ enum TypeEnum {
+ kMouseDown = 0, kMouseUp = 1, kMouseMove = 2, kMouseDrag = 3, kKeyDown=4, kKeyUp=5,
+ kScrollWheel=6, kRepaint=7, kLayout=8, kDragUpdated=9, kDragPerform=10,kDragExited=15, kIgnore=11,kUsed=12,kValidateCommand=13,kExecuteCommand=14,kContextClick=16,
+ kMagnifyGesture=1000, kSwipeGesture=1001, kRotateGesture=1002
+ };
+ enum MouseButton {
+ kLeftButton = 0, kRightButton = 1, kMiddleButton = 2
+ };
+#if UNITY_METRO
+ enum TouchType {
+ kMouseTouch = 0, kFingerTouch = 1
+ };
+#endif
+
+#if UNITY_WINRT
+ // Putted ctor here so I can use TypeEnum...
+ InputEvent (TypeEnum inEventType, Vector2f mouseLocation, int modifiers);
+ InputEvent (TypeEnum inEventType, UInt32 inKey, UInt32 inCharacter, UInt32 state, Vector2f mouseLocation);
+#endif
+ // needs to be size_t, as mono enums are 32-bit or 64-bit depending on architecture
+ typedef size_t Type;
+ Type type; ///< Which type of event.
+#if ENABLE_NEW_EVENT_SYSTEM
+ Touch touch; ///< Touch containing position and delta information
+#else
+ Vector2f mousePosition; ///< Position of mouse events.
+ Vector2f delta; ///< Delta of mouse events.
+ #if UNITY_METRO
+ TouchType touchType;
+ #endif
+#endif
+ int button; ///< mouse button number. (bitfield of MouseButton enum)
+ int modifiers; ///< keyboard modifier flags. (bitfield of Modifiers enum)
+ float pressure; ///< Stylus pressure.
+ int clickCount;
+ UInt16 character; ///< unicode keyboard character (with modifiers).
+ UInt16 keycode; ///< The keyboard scancode of the event.
+ char* commandString;
+
+ // Initialize to ignore event with all values cleared.
+ void Init();
+
+ void Use () { type = kUsed; }
+
+ #ifdef __OBJC__
+ static InputEvent CommandStringEvent (const std::string& editorCommand, bool execute, NSView* view);
+ #endif
+
+private:
+ #if UNITY_OSX && defined (__OBJC__)
+ void GetImmediateMousePosition (NSEvent *event, NSView *view);
+ void Init (NSEvent *src, NSView *view, bool cullOffscreenMouseDownEvents);
+ #endif
+ #if UNITY_WIN && UNITY_EDITOR
+ void DoMouseJumpingTroughScreenEdges(HWND window, Vector2f mousePos, Vector2f& lastMousePos);
+ #endif
+};
+
+/// Semi-Abstract superclass for all things input-related.
+/// Override these functions and return true if you ate the event.
+/// The default implementations just return false.
+class InputInterface {
+ public:
+ virtual ~InputInterface ();
+
+ virtual bool OnInputEvent (InputEvent &event) = 0;
+
+ virtual bool PerformDrag (InputEvent &event);
+ virtual int UpdateDrag (InputEvent &event);
+};
+
+std::string InputEventToString (InputEvent& event);
+void StringToInputEvent (const std::string& inputEvt, InputEvent& evt);
+
+#if UNITY_OSX && GAMERELEASE
+typedef struct _NPCocoaEvent NPCocoaEvent;
+
+void InitWebplayerInputEvent (InputEvent& event, EventRecord* rec);
+void InitEventFromEventRef ( InputEvent& event, EventRef eventRef );
+void InitEventFromEventRecord( InputEvent& event, EventRecord* evt );
+void InitWebplayerInputEventCocoa (InputEvent& event, NPCocoaEvent* cocoaEvent, bool textInputHandledByBrowser);
+#endif
+
+#endif
diff --git a/Runtime/Misc/MeshWelding.cpp b/Runtime/Misc/MeshWelding.cpp
new file mode 100644
index 0000000..2fee400
--- /dev/null
+++ b/Runtime/Misc/MeshWelding.cpp
@@ -0,0 +1,249 @@
+#include "UnityPrefix.h"
+#include "Runtime/Misc/MeshWelding.h"
+#include "Editor/Src/AssetPipeline/ImportMesh.h"
+
+
+inline UInt32 GetVector3HashValue (const Vector3f& value)
+{
+ const UInt32* h = (const UInt32*)(&value);
+ UInt32 f = (h[0]+h[1]*11-(h[2]*17))&0x7fffffff; // avoid problems with +-0
+ return (f>>22)^(f>>12)^(f);
+}
+
+/*
+ In 12 operations, this code computes the next highest power of 2 for a 32-bit integer. The result may be expressed by the formula 1U << (lg(v - 1) + 1).
+ It would be faster by 2 operations to use the formula and the log base 2 methed that uses a lookup table, but in some situations,
+ lookup tables are not suitable, so the above code may be best.
+ */
+
+inline int nextPowerOfTwo (UInt32 v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+ return v + (v==0);
+}
+
+inline bool CompareBone(const BoneInfluence& lhs, const BoneInfluence& rhs)
+{
+ for (int i=0;i<4;i++)
+ {
+ if (!CompareApproximately(lhs.weight[0], rhs.weight[0]) || lhs.boneIndex[i] != rhs.boneIndex[i])
+ return false;
+ }
+ return true;
+}
+
+
+#if UNITY_EDITOR
+
+/*-----------------------------------------------------------------------//*
+ !
+ * \brief an array of vertex positions
+ * \param p Array of vertex positions (will be modified!)
+ * \param N Number of vertices in array
+ * \return Number of vertices after the welding operation
+ * \note The unique vertices are stored into the beginning of array
+ * 'p'.
+ * \note This welder is "bit-exact", i.e. only duplicate vertices are
+ * removed. For distance-based welding a somewhat more complicated
+ * algorithm needs to be used.
+
+ *-------------------------------------------------------------------------*/
+
+int weld (std::vector<Vector3f>& vertices, dynamic_array<BoneInfluence>& skin, std::vector<ImportBlendShape>& shapes, std::vector<int>& remap)
+{
+ const int NIL = -1; // linked list terminator symbol
+ int outputCount = 0; // # of output vertices
+ int hashSize = nextPowerOfTwo(vertices.size()); // size of the hash table
+ int* hashTable = new int[hashSize + vertices.size()]; // hash table + linked list
+ int* next = hashTable + hashSize; // use bottom part as linked list
+
+ remap.resize(vertices.size());
+
+ memset (hashTable, NIL, (hashSize) * sizeof(int)); // init hash table (NIL = 0xFFFFFFFF so memset works)
+
+ for (int i = 0; i < vertices.size(); i++)
+ {
+ const Vector3f& v = vertices[i];
+ UInt32 hashValue = GetVector3HashValue(v) & (hashSize-1);
+ int offset = hashTable[hashValue];
+ while (offset != NIL)
+ {
+ Assert (offset < i);
+ bool euqals = (vertices[offset] == v);
+
+ if (euqals && !skin.empty() && !CompareBone(skin[i], skin[offset]))
+ euqals = false;
+
+ if (euqals && !shapes.empty())
+ {
+ for (int j = 0; j < shapes.size(); ++j)
+ {
+ if (shapes[j].vertices[i] != shapes[j].vertices[offset])
+ {
+ euqals = false;
+ break;
+ }
+ }
+ }
+
+ if (euqals)
+ break;
+
+ offset = next[offset];
+ }
+
+ if (offset == NIL) // no match found - copy vertex & add to hash
+ {
+ remap[i] = outputCount;
+ vertices[outputCount] = v; // copy vertex
+ if (!skin.empty())
+ skin[outputCount] = skin[i];
+ for (int j = 0; j < shapes.size(); ++j)
+ shapes[j].vertices[outputCount] = shapes[j].vertices[i];
+
+ next[outputCount] = hashTable[hashValue]; // link to hash table
+ hashTable[hashValue] = outputCount++; // update hash heads and increase output counter
+ }
+ else
+ {
+ Assert (offset < i);
+ remap[i] = offset;
+ }
+ }
+
+ delete[] hashTable; // cleanup
+ if (outputCount < vertices.size())
+ {
+ vertices.resize(outputCount);
+ if (!skin.empty())
+ skin.resize_initialized(outputCount);
+ for (int j = 0; j < shapes.size(); ++j)
+ shapes[j].vertices.resize(outputCount);
+ return true;
+ }
+ else
+ return false;
+}
+
+void WeldVertices (ImportMesh& mesh)
+{
+ std::vector<int> remap;
+ if (weld (mesh.vertices, mesh.skin, mesh.shapes, remap))
+ {
+ UInt32* indices = &mesh.polygons[0];
+ for (int i=0;i<mesh.polygons.size();i++)
+ indices[i] = remap[indices[i]];
+ }
+}
+
+#endif
+
+bool WeldVertexArray(dynamic_array<Vector3f>& vertices, dynamic_array<UInt16>& triangles, dynamic_array<UInt16>& remap)
+{
+ Mesh::BoneInfluenceContainer skin;
+ return WeldVertexArray(vertices, skin, triangles, remap);
+}
+
+bool WeldVertexArray(dynamic_array<Vector3f>& vertices, Mesh::BoneInfluenceContainer& skin, dynamic_array<UInt16>& triangles, dynamic_array<UInt16>& remap)
+{
+ const int NIL = -1; // linked list terminator symbol
+ int outputCount = 0; // # of output vertices
+ int hashSize = nextPowerOfTwo(vertices.size()); // size of the hash table
+ int* hashTable = new int[hashSize + vertices.size()]; // hash table + linked list
+ int* next = hashTable + hashSize; // use bottom part as linked list
+
+ remap.resize_uninitialized(vertices.size());
+
+ memset (hashTable, NIL, (hashSize) * sizeof(int)); // init hash table (NIL = 0xFFFFFFFF so memset works)
+
+ for (int i = 0; i < vertices.size(); i++)
+ {
+ const Vector3f& v = vertices[i];
+ UInt32 hashValue = GetVector3HashValue(v) & (hashSize-1);
+ int offset = hashTable[hashValue];
+ while (offset != NIL)
+ {
+ Assert (offset < i);
+ if (vertices[offset] == v)
+ {
+ if (skin.empty() || CompareBone(skin[i], skin[offset]))
+ break;
+ }
+ offset = next[offset];
+ }
+
+ if (offset == NIL) // no match found - copy vertex & add to hash
+ {
+ remap[i] = outputCount;
+ vertices[outputCount] = v; // copy vertex
+
+ if (!skin.empty())
+ skin[outputCount] = skin[i];
+
+ next[outputCount] = hashTable[hashValue]; // link to hash table
+ hashTable[hashValue] = outputCount++; // update hash heads and increase output counter
+ }
+ else
+ {
+ Assert (offset < i);
+ remap[i] = offset;
+ }
+ }
+
+ delete[] hashTable; // cleanup
+
+ if (outputCount < vertices.size())
+ {
+ vertices.resize_uninitialized(outputCount);
+ if (!skin.empty())
+ skin.resize_uninitialized(outputCount);
+ for (int i=0;i<triangles.size();i++)
+ triangles[i] = remap[triangles[i]];
+ return true;
+ }
+ return false;
+}
+
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+SUITE (VertexWeldingTests)
+{
+
+ TEST (TestVertexWelding)
+ {
+ Vector3f vertices[] = { Vector3f (0,0,0), Vector3f (1,0,0), Vector3f (1,0,0), Vector3f (0,0,0) };
+ dynamic_array<Vector3f> dVertices; dVertices.assign(vertices, vertices + ARRAY_SIZE(vertices));
+
+ UInt16 indices[] = { 0, 1, 2, 3 };
+ dynamic_array<UInt16> dIndices; dIndices.assign(indices, indices + ARRAY_SIZE(indices));
+
+ dynamic_array<UInt16> remap;
+
+ WeldVertexArray(dVertices, dIndices, remap);
+
+ CHECK_EQUAL(2, dVertices.size());
+ CHECK(Vector3f(0,0,0) == dVertices[0]);
+ CHECK(Vector3f(1,0,0) == dVertices[1]);
+
+ CHECK(0 == dIndices[0]);
+ CHECK(1 == dIndices[1]);
+ CHECK(1 == dIndices[2]);
+ CHECK(0 == dIndices[3]);
+
+ CHECK_EQUAL(4, remap.size());
+ CHECK_EQUAL(0, remap[0]);
+ CHECK_EQUAL(1, remap[1]);
+ CHECK_EQUAL(1, remap[2]);
+ CHECK_EQUAL(0, remap[3]);
+ }
+}
+#endif
diff --git a/Runtime/Misc/MeshWelding.h b/Runtime/Misc/MeshWelding.h
new file mode 100644
index 0000000..10539c2
--- /dev/null
+++ b/Runtime/Misc/MeshWelding.h
@@ -0,0 +1,17 @@
+#ifndef MESH_WELDING_H_
+#define MESH_WELDING_H_
+
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Modules/ExportModules.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/AssetPipeline/ImportMesh.h"
+
+void WeldVertices (ImportMesh& mesh);
+#endif
+
+bool EXPORT_COREMODULE WeldVertexArray(dynamic_array<Vector3f>& vertices, Mesh::BoneInfluenceContainer& skin, dynamic_array<UInt16>& triangles, dynamic_array<UInt16>& remap);
+bool EXPORT_COREMODULE WeldVertexArray(dynamic_array<Vector3f>& vertices, dynamic_array<UInt16>& triangles, dynamic_array<UInt16>& remap);
+
+
+#endif // UNITY_CUSTOM_ALLOCATOR_H_
diff --git a/Runtime/Misc/MessageParameters.h b/Runtime/Misc/MessageParameters.h
new file mode 100644
index 0000000..97a4669
--- /dev/null
+++ b/Runtime/Misc/MessageParameters.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "Runtime/Math/Vector3.h"
+#include <list>
+
+class Collider;
+class Rigidbody;
+struct MonoObject;
+
+struct ContactPoint
+{
+ Collider* collider[2];
+ Vector3f point;
+ Vector3f normal;
+};
+
+struct Collision
+{
+ int status;
+
+ bool flipped;
+
+ Rigidbody* thisRigidbody;
+ Rigidbody* otherRigidbody;
+ Collider* thisCollider;
+ Collider* otherCollider;
+
+ Vector3f impactForceSum;
+ Vector3f frictionForceSum;
+ Vector3f relativeVelocity;
+ typedef std::list<ContactPoint> Contacts;
+ Contacts contacts;
+};
+
+ScriptingObjectPtr ConvertContactToMono (Collision* input);
diff --git a/Runtime/Misc/Player.cpp b/Runtime/Misc/Player.cpp
new file mode 100644
index 0000000..cf4d66b
--- /dev/null
+++ b/Runtime/Misc/Player.cpp
@@ -0,0 +1,2228 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Configuration/UnityConfigureVersion.h"
+#include "Player.h"
+#include "QualitySettings.h"
+#include "PreloadManager.h"
+#if SUPPORT_REPRODUCE_LOG
+#include "ReproductionLog.h"
+#include "Runtime/GfxDevice/GfxDeviceSetup.h"
+#endif
+#include "SaveAndLoadHelper.h"
+#include "Runtime/BaseClasses/ManagerContextLoading.h"
+#include "SceneUnloading.h"
+#include "Runtime/Utilities/PlayerPrefs.h"
+#include "PreloadManager.h"
+#include "Runtime/Input/InputManager.h"
+#include "PlayerSettings.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Filters/Deformation/SkinnedMeshFilter.h"
+#include "Runtime/Serialize/PathNamePersistentManager.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Serialize/TransferUtility.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Camera/UnityScene.h"
+#include "GameObjectUtility.h"
+#include "Runtime/IMGUI/TextMeshGenerator2.h"
+#include "Runtime/Utilities/UserAuthorizationManager.h"
+#if ENABLE_UNITYGUI
+#include "Runtime/IMGUI/GUIManager.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#endif
+#if ENABLE_WWW
+#include "PlatformDependent/CommonWebPlugin/UnityWebStream.h"
+#include "PlatformDependent/CommonWebPlugin/CompressedFileStreamMemory.h"
+#include "Runtime/Utilities/ReportHardware.h"
+#endif
+#if WEBPLUG
+#include "PlatformDependent/CommonWebPlugin/WebScripting.h"
+#include "PlatformDependent/CommonWebPlugin/CompressedFileStreamMemory.h"
+#endif
+#if UNITY_EDITOR
+#include "Editor/Src/Application.h"
+#endif // UNITY_EDITOR
+#include "Runtime/Graphics/ScreenManager.h"
+#include "BuildSettings.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Input/InputManager.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/GameCode/CallDelayed.h"
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Camera/LODGroupManager.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "DebugUtility.h"
+#include "Runtime/Filters/Mesh/MeshRenderer.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Graphics/RenderBufferManager.h"
+#include "Player.h"
+#include "CaptureScreenshot.h"
+#include "Runtime/Input/GetInput.h"
+#include "ResourceManager.h"
+#include "Runtime/Graphics/Texture.h"
+#include "Runtime/Camera/RenderLayers/GUITexture.h"
+#include "Runtime/Profiler/ProfilerHistory.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/GPUProfiler.h"
+#include "Runtime/Profiler/MemoryProfilerStats.h"
+#include "Runtime/Profiler/ProfilerConnection.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/BaseClasses/Cursor.h"
+#include "Runtime/Allocator/MemoryManager.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+
+
+#if ENABLE_CACHING
+#include "CachingManager.h"
+#endif
+#include "Runtime/Utilities/RecursionLimit.h"
+#if ENABLE_MOVIES || ENABLE_WEBCAM
+#include "Runtime/Video/BaseVideoTexture.h"
+#endif
+#include "Runtime/IMGUI/TextMeshGenerator2.h"
+#include "Runtime/Camera/IntermediateRenderer.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystem.h" // ParticleSystem::UpdateAll ()
+#include "Runtime/Filters/Particles/ParticleEmitter.h"
+#include "InputEvent.h"
+#include "SystemInfo.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Graphics/SubstanceSystem.h"
+#include "Runtime/Filters/Misc/Font.h"
+#include "Runtime/Profiler/DeprecatedFrameStatsProfiler.cpp" // hack until jamplus is finished
+#include "Runtime/Graphics/DrawSplashScreenAndWatermarks.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+#include "Runtime/Core/Callbacks/PlayerLoopCallbacks.h"
+
+#if UNITY_WII
+#include "PlatformDependent/Wii/WiiMoviePlayer.h"
+#include "PlatformDependent/Wii/WiiLoadingScreen.h"
+#elif UNITY_XENON
+#include "PlatformDependent/Xbox360/Source/Kinect/Kinect.h"
+#elif UNITY_WIN
+#include "PlatformDependent/Win/WinUtils.h"
+#elif UNITY_ANDROID
+#include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+#elif UNITY_IPHONE
+#include "PlatformDependent/iPhonePlayer/iphoneNativeEvents.h"
+#endif
+
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+#include "PlatformDependent/CommonWebPlugin/WebScripting.h"
+#endif
+
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN
+#include "Runtime/Input/OnScreenKeyboard.h"
+#endif
+
+#if ENABLE_CLUSTER_SYNC
+#include "Runtime/Interfaces/IClusterRenderer.h"
+#endif
+
+#include <ctype.h>
+#include "Runtime/Interfaces/IAudio.h"
+
+
+static bool PlayerInitEngineLoadData ();
+static bool LoadQueuedWebPlayerData ();
+static int gPluginVersion=0;
+static bool s_InsidePlayerLoop = false;
+
+void (*g_PresentCallback)(bool before);
+
+using namespace std;
+
+static const char* kMainDataSharedAssets = "sharedassets0.assets";
+static const char* kExtraResourcesPath = "Resources/unity_builtin_extra";
+
+#if UNITY_WIN && WEBPLUG
+ static const char* kDefaultResourcePath = "Data/unity default resources";
+#elif UNITY_WIN || UNITY_XENON || UNITY_PS3
+ static const char* kDefaultResourcePath = "Resources/unity default resources";
+#elif UNITY_OSX
+ static const char* kDefaultResourcePath = "Resources/unity default resources";
+#elif UNITY_WII
+ static const char* kDefaultResourcePath = "Resources/unity default resources";
+#elif UNITY_IPHONE
+ static const char* kDefaultResourcePath = "Data/unity default resources";
+#elif UNITY_ANDROID
+ static const char* kDefaultResourcePath = "Data/unity default resources";
+#elif UNITY_PEPPER
+ static const char* kDefaultResourcePath = "Resources/unity default resources";
+#elif UNITY_BB10
+ static const char* kDefaultResourcePath = "unity default resources";
+#elif UNITY_TIZEN
+ static const char* kDefaultResourcePath = "unity default resources";
+#elif UNITY_LINUX
+ static const char* kDefaultResourcePath = "Resources/unity default resources";
+#elif UNITY_FLASH
+ static const char* kDefaultResourcePath = "Resources/unity default resources";
+#elif UNITY_WEBGL
+ static const char* kDefaultResourcePath = "Resources/unity_default_resources";
+#else
+ static const char* kDefaultResourcePath = "Resources/unity default resources";
+ #error "Unknown platform"
+#endif
+
+#if WEBPLUG
+#if UNITY_WIN
+static const char* kDefaultOldResourcePath = "Data/unity_web_old";
+#else
+static const char* kDefaultOldResourcePath = "Resources/unity_web_old";
+#endif
+#endif // #if WEBPLUG
+
+#if ENABLE_HARDWARE_INFO_REPORTER
+static HardwareInfoReporter s_HardwareInfoReport;
+#endif
+
+#if ENABLE_GAMECENTER
+namespace GameCenter
+{
+ void ExecuteGameCenterCallbacks();
+}
+#endif
+
+#if ENABLE_MEMORY_TRACKING
+std::auto_ptr<ScopedMemLeakDetector> m_MemLeakDetector = new ScopedMemLeakDetector();
+#endif
+
+struct LevelLoading
+{
+ int m_ActiveLoadedLevel;
+
+ typedef dynamic_array<PPtr<Object> > DontDestroyOnLoadSet;
+ DontDestroyOnLoadSet m_DontDestroyOnLoad;
+
+ public:
+
+ void LoadLevel (int index, const std::string& path, AwakeFromLoadQueue& awakeFromLoadQueue);
+
+ LevelLoading () { ResetLoadLevel (-1); }
+
+ void ResetLoadLevel (int activeLoadedLevel);
+
+ int GetLoadedLevelIndex () { return m_ActiveLoadedLevel; }
+
+ void DontDestroyOnLoad (Object& object)
+ {
+ m_DontDestroyOnLoad.push_back (&object);
+ }
+
+ bool m_hasLateBoundLevel;
+ UnityStr m_lateBoundLevelName;
+ UnityStr m_tempLateBoundLevelName;
+};
+
+
+void LevelLoading::ResetLoadLevel (int activeLoadedLevel)
+{
+ m_DontDestroyOnLoad.clear();
+ m_ActiveLoadedLevel = activeLoadedLevel;
+ m_hasLateBoundLevel = false;
+ m_lateBoundLevelName.clear();
+ m_tempLateBoundLevelName.clear();
+}
+
+static LevelLoading gLoadLevel;
+
+bool GetHasLateBoundLevelFromAssetBundle (const string& name)
+{
+ string levelPath = Format("BuildPlayer-%s", name.c_str());
+ bool exists = GetPersistentManager().HasMemoryOrCachedSerializedFile(levelPath);
+ return exists;
+}
+
+bool GetLevelAndAssetPath (const std::string& levelName, int levelIndex, std::string* outLevelPath, std::string* outAssetPath, int* outIndex)
+{
+ *outLevelPath = "";
+ *outAssetPath = "";
+ *outIndex = -1;
+
+ string levelPath;
+ if (levelIndex != -1)
+ {
+ levelPath = GetBuildSettings().GetLevelPathName (levelIndex);
+ }
+ else
+ {
+ // Late bound level (via asset bundle)
+ if (GetHasLateBoundLevelFromAssetBundle(levelName))
+ {
+ *outLevelPath = Format("BuildPlayer-%s", levelName.c_str());
+ *outAssetPath = Format("BuildPlayer-%s.sharedAssets", levelName.c_str());
+ *outIndex = -1;
+
+ gLoadLevel.m_tempLateBoundLevelName = levelName;
+
+ return true;
+ }
+ // Level included via BuildSettings
+ else
+ {
+ levelIndex = GetBuildSettings().GetLevelIndex(levelName);
+ levelPath = GetBuildSettings().GetLevelPathName (levelIndex);
+ }
+ }
+
+ const char* cantLoadLevelErrorMessage = "Level '%s' (%d) couldn't be loaded because it has not been added to the build settings.\nTo add a level to the build settings use the menu File->Build Settings...";
+ // Verify that the level is accessable
+ #if WEBPLUG
+ const char* cantStreamLoadLevelErrorMessage = "Level '%s' (%d) couldn't be loaded because it has not been streamed in yet. You need to ensure that levels which have not been streamed in yet are not loaded.";
+ if (CompressedFileStream::Get().Find(levelPath) == NULL && !IsFileCreated (levelPath))
+ {
+ if (levelPath.empty ())
+ {
+ ErrorString (Format (cantLoadLevelErrorMessage, levelName.c_str(), levelIndex));
+ }
+ else
+ {
+ ErrorString (Format (cantStreamLoadLevelErrorMessage, levelName.c_str(), levelIndex));
+ }
+ return false;
+ }
+ #else
+ if (!IsFileCreated (levelPath))
+ {
+ ErrorString (Format (cantLoadLevelErrorMessage, levelName.c_str(), levelIndex));
+ return false;
+ }
+ #endif
+
+ // Extract
+ *outLevelPath = levelPath;
+ *outIndex = levelIndex;
+ #if UNITY_EDITOR
+ *outAssetPath = "";
+ #else
+ *outAssetPath = Format("sharedassets%d.assets", levelIndex);
+ #endif
+
+ return true;
+}
+
+int GetPluginVersion()
+{
+ return gPluginVersion;
+}
+
+static int gTargetFrameRate = -1;
+
+static PlayerPause gPlayerPause = kPlayerRunning;
+
+#if !UNITY_EDITOR
+static bool gIsFirstFrame = true;
+static bool gHasFrameToPresent = false;
+#endif
+
+#if WEBPLUG
+static UnityWebStream* gLoadUnityWebData = NULL;
+#endif
+
+#if WEBPLUG || UNITY_EDITOR
+void ResetPlayerInWebPlayer (int level)
+{
+ gLoadLevel.ResetLoadLevel(level);
+ if (GetLODGroupManagerPtr())
+ GetLODGroupManager().ResetLODBias();
+
+
+ StopPreloadManager();
+
+ GetUserAuthorizationManager ().Reset();
+
+ #if WEBPLUG
+ gDisplayFullscreenEscapeTimeout = -1000.0;
+ gTargetFrameRate = -1;
+ AssertIf(gLoadUnityWebData != NULL);
+ #endif
+}
+#endif
+
+#if UNITY_EDITOR
+void ResetPlayerInEditor (int level)
+{
+ ResetPlayerInWebPlayer(level);
+
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ audioModule->AudioManagerAwakeFromLoad(kDefaultAwakeFromLoad);
+
+ #if ENABLE_CACHING
+ GetCachingManager().Reset();
+ #endif
+ StopPreloadManager();
+}
+#endif
+
+void DontDestroyOnLoad (Object& object)
+{
+ gLoadLevel.DontDestroyOnLoad(object);
+}
+
+///// @TODO CLEANUP THIS SHIT
+void PlayerLoadLevelFromThread (int levelIndex, const std::string& name, AwakeFromLoadQueue& loadQueue)
+{
+ gLoadLevel.LoadLevel(levelIndex, name, loadQueue);
+}
+
+bool IsLoadingLevel ()
+{
+ return GetPreloadManager().IsLoadingOrQueued();
+}
+
+void SetPlayerPause (PlayerPause pause)
+{
+ if (gPlayerPause == pause)
+ return;
+ if ((kPlayerPaused == gPlayerPause) && (kPlayerPausing == pause))
+ return;
+
+ // Pausing is sometimes called when unity is not yet initialized
+ // Happens on windows gles20 emulator. It seems like the WM_ACTIVATE function can be called
+ // Prior to the context having been created.
+ if (GetBuildSettingsPtr() == NULL)
+ return;
+
+#if ENABLE_AUDIO
+ bool pauseAudio = ( pause != kPlayerRunning );
+#if UNITY_EDITOR
+ pauseAudio = pauseAudio || GetApplication().IsPaused();
+#endif // UNITY_EDITOR
+
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ audioModule->SetPause( pauseAudio );
+#endif // ENABLE_AUDIO
+
+ GetTimeManager().SetPause( pause == kPlayerPaused );
+ if (kPlayerPaused == pause)
+ {
+ GetScreenManager().SetCursorInsideWindow(false);
+ GetScreenManager().SetAllowCursorLock(false);
+ }
+
+ if (kPlayerRunning == pause)
+ ResetInputAfterPause();
+
+ gPlayerPause = pause;
+
+ if ((kPlayerRunning == pause) || (kPlayerPaused == pause))
+ {
+ MessageData data;
+ data.SetData((kPlayerPaused == pause), ClassID (bool));
+ SendMessageToEveryone (kPlayerPause, data);
+ }
+}
+
+PlayerPause GetPlayerPause(void)
+{
+ return gPlayerPause;
+}
+
+void SetPlayerFocus(bool focus)
+{
+ if (GetBuildSettingsPtr() == NULL)
+ return;
+
+#if !WEBPLUG
+ // Reset input state if focus is being lost as we don't want such state if we resume focus.
+ if (!focus)
+ ResetInput();
+#endif
+
+ static bool focusEventSupported = (GetBuildSettings().GetIntVersion() > GetNumericVersion("2.6.1f3"));
+
+ if (focusEventSupported)
+ {
+ MessageData data;
+ data.SetData(focus, ClassID(bool));
+ SendMessageToEveryone(kPlayerFocus, data);
+ }
+}
+
+bool NotifyPlayerQuit(bool forceQuit)
+{
+ // We unloaded the data file in the web player already. Dont use any managers at this point
+ if (GetManagerPtrFromContext (ManagerContext::kPlayerSettings) == NULL)
+ return true;
+
+ GetInputManager().QuitApplication();
+
+ SendMessageToEveryone (kPlayerQuit, MessageData());
+
+ // During SendMessageToEveryone (kPlayerQuit, MessageData()); scripts can set m_ShouldQuit to false.
+ // This cancels the quit operation
+ Assert(forceQuit || (!WEBPLUG && !UNITY_EDITOR && !UNITY_IPHONE && !UNITY_WINRT));
+ if (!forceQuit && !GetInputManager().ShouldQuit())
+ return false;
+
+ #if ENABLE_MOVIES || ENABLE_WEBCAM
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ audioModule->StopVideoTextures ();
+ #endif
+
+ #if WEBPLUG
+ // This will unfortunately kill all external calls issued from OnApplicationQuit.
+ // Not doing this would make them execute on the next player launch though.
+ WebScripting::Get().ClearExternalCalls();
+ #endif
+
+ #if ENABLE_NETWORK
+ CALL_MANAGER_IF_EXISTS(ManagerContext::kNetworkManager, NetworkOnApplicationQuit());
+ CALL_MANAGER_IF_EXISTS(ManagerContext::kMasterServerInterface, NetworkOnApplicationQuit());
+ #endif
+
+ // When exiting player. Reset show cursor
+ GetScreenManager().SetShowCursor(true);
+ GetScreenManager().SetCursorInsideWindow(false);
+ GetScreenManager().SetLockCursor(false);
+ GetScreenManager().SetAllowCursorLock(false);
+
+ // Stop any tasks on the preload manager
+ StopPreloadManager();
+
+ #if WEBPLUG
+ if (gLoadUnityWebData)
+ {
+ gLoadUnityWebData->Release();
+ gLoadUnityWebData = NULL;
+ }
+ #endif
+
+ return true;
+}
+
+void ProcessMouseInWindow()
+{
+ // In Core Animation plugin, rely on MouseEntered/MouseExited events instead.
+ // Otherwise we hide the cursor when our window is not in front, as we cannot find out when that is the case.
+ #if WEBPLUG && UNITY_OSX && !UNITY_PEPPER
+ if (GetScreenManager().IsUsingCoreAnimation())
+ return;
+ #endif
+
+ #if UNITY_WIN && !UNITY_WINRT
+
+ bool inside = false;
+ POINT cursorPosition;
+
+ const BOOL getCursorPositionResult = GetCursorPos(&cursorPosition);
+ //Assert(FALSE != getCursorPositionResult); // happens when windows is locked
+
+ if (FALSE != getCursorPositionResult)
+ {
+ const HWND window = WindowFromPoint(cursorPosition);
+
+ if (NULL != window)
+ {
+ ScreenManagerPlatform& screen = GetScreenManager();
+ if (screen.GetWindow() == window)
+ {
+ POINT pt = cursorPosition;
+ ScreenToClient (window, &pt);
+ inside = (pt.x >= 0 && pt.y >= 0 && pt.x < screen.GetWidth() && pt.y < screen.GetHeight());
+ }
+ }
+ }
+
+ #else
+
+ bool inside = true;
+ Vector2f pos = GetInputManager().GetMousePosition();
+ if (pos.x < 0.0F || pos.x > GetScreenManager().GetWidth())
+ inside = false;
+ if (pos.y < 0.0F || pos.y > GetScreenManager().GetHeight())
+ inside = false;
+
+ #endif
+
+ GetScreenManager().SetCursorInsideWindow(inside);
+}
+
+bool GetPlayerRunInBackground()
+{
+ if(GetPlayerSettingsPtr() == NULL)
+ return false;
+ #if SUPPORT_REPRODUCE_LOG
+ return GetPlayerSettings().runInBackground || RunningReproduction();
+ #endif
+ return GetPlayerSettings().runInBackground;
+}
+
+void SetPlayerRunInBackground(bool runInBackground)
+{
+ if (runInBackground != GetPlayerSettings().runInBackground)
+ {
+ GetPlayerSettings().runInBackground = runInBackground;
+ GetPlayerSettings().SetDirty();
+
+ if (runInBackground)
+ SetPlayerPause(kPlayerRunning);
+ }
+}
+
+#if !WEBPLUG && !UNITY_EDITOR
+static string CalculateStandaloneDataFolder ()
+{
+ #if UNITY_OSX
+
+ // OS X:
+ // - Try AppPath/Contents/Data folder
+ // - Try AppPath/../Data folder
+ string appContentsFolder = AppendPathName (GetApplicationPath (), "Contents/Data");
+ string appDataFolder = AppendPathName (GetApplicationFolder (), "Data");
+ if (IsFileCreated (AppendPathName (appContentsFolder, kMainData)))
+ return appContentsFolder;
+ else if (IsFileCreated (AppendPathName (appDataFolder, kMainData)))
+ return appDataFolder;
+
+ #elif UNITY_WINRT
+
+ return AppendPathName(GetApplicationFolder(), "Data");
+
+ #elif UNITY_WIN || UNITY_LINUX
+
+ // - Try ExeName_Data
+ // - Try Data folder
+ string appPath = GetApplicationPath();
+ appPath = DeletePathNameExtension(appPath);
+ string path = appPath + "_Data";
+ if (IsFileCreated (AppendPathName (path, kMainData)))
+ return path;
+ string appDataFolder = AppendPathName (GetApplicationFolder (), "Data");
+ if (IsFileCreated (AppendPathName (appDataFolder, kMainData)))
+ return appDataFolder;
+
+ #elif UNITY_XENON
+
+ return "game:\\Media";
+
+ #elif UNITY_PS3
+
+ return AppendPathName (GetApplicationFolder (), "Media");
+
+ #elif UNITY_IPHONE
+
+ return AppendPathName(GetApplicationPath(), "Data");
+ #elif UNITY_ANDROID
+ return AppendPathName(GetApplicationPath(), "assets/bin/Data");
+ #elif UNITY_BB10
+ return AppendPathName(GetApplicationFolder(), "app/native/Data");
+ #elif UNITY_TIZEN
+ return AppendPathName(GetApplicationPath(), "data");
+ #elif UNITY_WII
+ return "unity/Data";
+ #elif UNITY_FLASH || UNITY_WEBGL
+ return "";
+ #else
+ #error "Unknown platform"
+ #endif
+
+ return "";
+}
+
+string SelectDataFolder ()
+{
+ static bool s_DataFolderDone = false;
+ static StaticString s_DataFolder;
+ if (!s_DataFolderDone)
+ {
+ s_DataFolder = CalculateStandaloneDataFolder ().c_str ();
+ s_DataFolderDone = true;
+ }
+ return s_DataFolder.c_str ();
+}
+#endif
+
+#if UNITY_STANDALONE
+string GetApplicationNativeLibsPath()
+{
+# if UNITY_OSX
+ return AppendPathName (GetApplicationPath (), "Contents/Frameworks/MonoEmbedRuntime/osx");
+# else
+ string monoBaseFolder = AppendPathName (SelectDataFolder (), "Mono");
+ string x86_64Folder = AppendPathName (monoBaseFolder, "x86_64");
+ string x86Folder = AppendPathName (monoBaseFolder, "x86");
+
+ if (IsDirectoryCreated (x86_64Folder))
+ return x86_64Folder;
+ if (IsDirectoryCreated (x86Folder))
+ return x86Folder;
+ return monoBaseFolder;
+# endif
+}
+#endif
+
+#if UNITY_EDITOR
+string GetApplicationNativeLibsPath()
+{
+# if UNITY_OSX
+ return AppendPathName (GetApplicationPath (), "Contents/Frameworks/Mono/lib");
+# else
+ return AppendPathName (GetApplicationContentsPath (), "Mono/lib");
+# endif
+}
+#endif
+
+static void AddPathRemapsForBuiltinResources (const string& applicationContentsFolderPath)
+{
+ GetPersistentManager ().SetPathRemap ("library/unity default resources", AppendPathName (applicationContentsFolderPath, kDefaultResourcePath));
+
+#if !WEBPLUG
+ // Remap "resources/unity_builtin_extra" to "Resources/unity_builtin_extra" to deal
+ // with case-sensitive file systems.
+ //
+ // In the webplayer, the file is contained in the webstream rather than being bundled
+ // with the player, so no remapping necessary there.
+ std::string extraResourcesLower = kExtraResourcesPath;
+ ToLowerInplace(extraResourcesLower);
+ GetPersistentManager ().SetPathRemap (extraResourcesLower, kExtraResourcesPath);
+#endif
+
+#if WEBPLUG
+ GetPersistentManager ().SetPathRemap ("library/unity_web_old", AppendPathName (applicationContentsFolderPath, kDefaultOldResourcePath));
+#endif
+}
+
+bool PlayerInitEngineNoGraphics (const string& dataFolder, const string& applicationContentsFolderPath)
+{
+/*
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ g_ForwardFrameAllocator.SetThreadIDs (Thread::GetCurrentThreadID (), Thread::GetCurrentThreadID ());
+#endif
+*/
+ /// Beta running out!
+ #ifdef TIME_LIMIT
+ if (CheckDate (TIME_LIMIT, "Unity Player beta has run out!"))
+ {
+ printf_console ("Unity Player beta has run out!\n");
+ return false;
+ }
+ #endif
+
+ #if SUPPORT_REPRODUCE_LOG
+ BatchInitializeReproductionLog();
+ #endif
+
+ if (!IsFileCreated (AppendPathName (dataFolder, kMainData)))
+ {
+ printf_console ("No mainData file was found, quitting player!\n");
+ return false;
+ }
+
+ InitPathNamePersistentManager();
+
+ File::SetCurrentDirectory (dataFolder);
+
+ AddPathRemapsForBuiltinResources (applicationContentsFolderPath);
+
+ // Initialize Engine
+ if (!InitializeEngineNoGraphics ())
+ {
+ printf_console( "PlayerInitEngineNoGraphics: InitializeEngine failed\n" );
+ return false;
+ }
+
+ string error = PlayerLoadSettingsAndInput(kMainData);
+ if (!error.empty())
+ {
+ #if UNITY_WIN
+ winutils::AddErrorMessage( error.c_str() );
+ #endif
+ printf_console( "PlayerInitEngineNoGraphics settings: %s\n", error.c_str() );
+ return false;
+ }
+
+ return true;
+}
+
+bool PlayerInitEngineGraphics (bool batchmode)
+{
+ // Initialize Engine (eg. Mastercontext, Shaderlab, Setup RTTI, Call Static Initialize functions, Setup messages and Tags)
+ if (!InitializeEngineGraphics (batchmode))
+ {
+ #if UNITY_WIN
+ winutils::AddErrorMessage( "InitializeEngineGraphics failed" );
+ #endif
+ printf_console( "PlayerInitEngineGraphics: InitializeEngineGraphics failed\n" );
+ return false;
+ }
+
+ // Check if GPU is supported
+ std::string gpuMsg = gGraphicsCaps.CheckGPUSupported();
+ if (!gpuMsg.empty())
+ {
+ #if UNITY_WIN
+ winutils::AddErrorMessage (gpuMsg.c_str());
+ #endif
+ printf_console ("PlayerInitEngineGraphics: GPU not supported; %s\n", gpuMsg.c_str());
+ return false;
+ }
+
+ #if ENABLE_HARDWARE_INFO_REPORTER
+ s_HardwareInfoReport.ReportHardwareInfo();
+ #endif
+
+ // Load all game managers! All global game managers are coming first in the main data
+ // (Global game managers have to be loaded before selecting the screen resolution.
+ // ProjectSettings i used by screen selector and RenderManager, InputManager by screen switching)
+ string error = PlayerLoadGlobalManagers(kMainData);
+ if (!error.empty())
+ {
+ printf_console( "PlayerInitEngineGraphics: %s\n", error.c_str() );
+ return false;
+ }
+
+#if !UNITY_EDITOR
+ // We can't do this in the editor, because this will get the default assets.
+ // When the editor is running, the default assets are often being built, and that
+ // will cause exceptions.
+
+ // By Ian's request moving this and : IAN FIXME YESTERDAY.
+ GUIStyle::SetDefaultFont(NULL);
+#endif
+
+ return true;
+}
+
+bool PlayerInitEngineWebNoGraphics (const string& applicationContentsFolderPath, int pluginversion)
+{
+
+
+ #if SUPPORT_REPRODUCE_LOG && UNITY_OSX
+ BatchInitializeReproductionLog();
+ #endif
+ gPluginVersion = pluginversion;
+
+ InitPathNamePersistentManager();
+
+#if !UNITY_FLASH
+ File::SetCurrentDirectory (applicationContentsFolderPath);
+#endif
+
+ AddPathRemapsForBuiltinResources (applicationContentsFolderPath);
+
+ // Initialize base classes (things that are not dependent on graphics)
+ if (!InitializeEngineNoGraphics ())
+ {
+ printf_console( "PlayerInitEngineNoGraphics: InitializeEngine failed\n" );
+ return false;
+ }
+ return true;
+}
+
+bool PlayerInitEngineWebGraphics ()
+{
+ // Initialize ShaderLab, default resources etc. (things that are dependent on graphics)
+ if (!InitializeEngineGraphics (false))
+ {
+ printf_console( "PlayerInitEngineGraphics: InitializeEngineGraphics failed\n" );
+ return false;
+ }
+
+ // Check if GPU is supported
+ std::string gpuMsg = gGraphicsCaps.CheckGPUSupported();
+ if (!gpuMsg.empty())
+ {
+ printf_console ("PlayerInitEngineGraphics: GPU not supported; %s\n", gpuMsg.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+
+bool PlayerInitEngineWeb (const string& applicationContentsFolderPath, int pluginversion)
+{
+ if (!PlayerInitEngineWebNoGraphics(applicationContentsFolderPath, pluginversion))
+ return false;
+ if (!PlayerInitEngineWebGraphics())
+ return false;
+ return true;
+}
+
+
+static bool PlayerLoadNewSettings ()
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ std::string error = PlayerLoadSettingsAndInput(kMainData);
+ if (!error.empty())
+ {
+ printf_console( "PlayerInitEngineReload settings: %s\n", error.c_str() );
+ return false;
+ }
+
+ // Print version number that our content was built with.
+ printf_console( "Loading webdata version: %s\n", GetBuildSettings().GetVersion().c_str() );
+
+ return true;
+}
+#if WEBPLUG
+
+static bool PlayerInitEngineLoadData ()
+{
+ #if UNITY_WIN
+ // Windows web player only calls PlayerInitEngineWebNoGraphics at initialization time;
+ // graphisc part is deferred until here, when we have player settings loaded (to be
+ // able to choose between DX9 & DX11).
+ if (!PlayerInitEngineWebGraphics ())
+ {
+ printf_console("web: failed to initialize graphics\n");
+ return false;
+ }
+
+ #endif
+
+
+ #if ENABLE_HARDWARE_INFO_REPORTER
+ // In web player, report hardware info after loading BuildSettings
+ s_HardwareInfoReport.ReportHardwareInfo();
+ #endif
+
+
+ // Load all game managers! All global game managers are coming first in the main data
+ // (Global game managers have to be loaded before selecting the screen resolution.
+ // ProjectSettings is used by screen selector and RenderManager, InputManager by screen switching)
+ std::string error = PlayerLoadGlobalManagers(kMainData);
+ if (!error.empty())
+ {
+ printf_console( "PlayerInitEngineReload: %s\n", error.c_str() );
+ return false;
+ }
+
+ // Some graphics caps need to be adjusted based on content build version,
+ // which is only available now.
+ gGraphicsCaps.InitWithBuildVersion();
+
+ return true;
+}
+
+bool PlayerResetPreferences(UnityWebStream& stream, const std::string absoluteURL, bool clearPlayerPrefs /*= true*/)
+{
+ ResetPlayerInWebPlayer(0);
+ gPlayerPause = kPlayerRunning;
+ if (clearPlayerPrefs)
+ PlayerPrefs::Init(absoluteURL);
+
+ #if SUPPORT_REPRODUCE_LOG
+ if (RunningReproduction())
+ PlayerPrefs::DeleteAll();
+ #endif
+
+ FileStream::SetDataFile(stream.GetFileStream());
+ stream.SetAddStreamedFilesToPersistentManager(true);
+
+ return PlayerLoadNewSettings();
+}
+
+bool PlayerLoadEngineData (UnityWebStream& stream, const std::string srcValue, const std::string absoluteURL)
+{
+ // Load data
+ if (!PlayerInitEngineLoadData())
+ {
+ return false;
+ }
+
+ GetPlayerSettings().srcValue = srcValue;
+ GetPlayerSettings().absoluteURL = absoluteURL;
+ GetUserAuthorizationManager ().Reset();
+ #if SUPPORT_REPRODUCE_LOG
+ ReadWriteAbsoluteUrl(GetPlayerSettings().srcValue, GetPlayerSettings().absoluteURL);
+
+ // Extract dll's to disk when creating reproduction log
+ if (GetReproduceMode() == kGenerateReproduceLog || GetReproduceMode() == kGenerateReproduceLogAndRemapWWW || GetReproduceMode() == kPlaybackReproduceLog)
+ {
+ AssertIf(stream.GetFileStream()->GetType() != kCompressedFileStreamMemoryType);
+
+ for (CompressedFileStreamMemory::iterator i=stream.GetFileStream()->begin();i != stream.GetFileStream()->end();i++)
+ {
+ if (!StrICmp(GetPathNameExtension(i->name), "dll"))
+ {
+ CompressedFileStream::Data& data = *i;
+
+ string path = AppendPathName(GetReproductionDirectory(), data.name);
+
+ MemoryCacherReadBlocks cacher (data.blocks, data.end, kCacheBlockSize);
+ UInt8* dataCpy = (UInt8*)UNITY_MALLOC(kMemTempAlloc, data.GetSize()*sizeof(UInt8));
+ cacher.DirectRead(dataCpy, data.offset, data.GetSize());
+ WriteBytesToFile(dataCpy, data.GetSize(), path);
+ UNITY_FREE(kMemTempAlloc, dataCpy);
+ }
+ }
+ }
+
+ #endif
+
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ audioModule->SetPause(kPlayerRunning);
+
+ return true;
+}
+
+bool PlayerLoadWebData (UnityWebStream& data, const std::string srcValue, const std::string absoluteURL, bool clearPlayerPrefs /*= true*/)
+{
+ return PlayerResetPreferences(data, absoluteURL, clearPlayerPrefs) &&
+ PlayerLoadEngineData(data, srcValue, absoluteURL);
+}
+
+
+bool QueuePlayerLoadWebData (UnityWebStream* stream)
+{
+ if (stream != NULL)
+ {
+ gLoadUnityWebData = stream;
+ gLoadUnityWebData->Retain();
+ return true;
+ }
+ else
+ {
+ ErrorString("No Valid Unity Web Stream was found in the download.");
+ return false;
+ }
+}
+
+static bool LoadQueuedWebPlayerData ()
+{
+ if (gLoadUnityWebData)
+ {
+ UnityWebStream* stream = gLoadUnityWebData;
+ gLoadUnityWebData = NULL;
+ if (!stream->IsReadyToPlay())
+ {
+ stream->Release();
+ return false;
+ }
+
+ string srcValue = GetPlayerSettings().srcValue;
+ string absoluteURL = GetPlayerSettings().absoluteURL;
+
+ if (!PlayerWebUnloadReloadable ())
+ {
+ stream->Release();
+ return false;
+ }
+
+ SetUnityWebStream(stream);
+
+ if (!PlayerLoadWebData (*stream, srcValue, absoluteURL, false))
+ {
+ return false;
+ }
+
+ PlayerLoadFirstLevel();
+
+ GetScreenManager().ReapplyWindowRect();
+ }
+
+ return true;
+}
+
+bool PlayerWebUnloadReloadable ()
+{
+ PlayerPrefs::Sync();
+
+
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ {
+ // Pause audio (Handle the case where no data file has been loaded yet)
+ if (GetManagerPtrFromContext (ManagerContext::kPlayerSettings))
+ audioModule->SetPause ( true );
+ }
+
+ gPlayerPause = kPlayerPaused;///WHY????
+
+ NotifyPlayerQuit (true);
+
+ ResetInput();
+
+ StopPreloadManager();
+
+ CleanupAllObjects(true);
+
+ #if ENABLE_MONO && !UNITY_PEPPER
+ if (!CleanupMonoReloadable())
+ return false;
+ #endif
+
+ GetPersistentManager().UnloadMemoryStreams();
+
+ GetUnityWebStream ().Release();
+
+ #if ENABLE_HARDWARE_INFO_REPORTER
+ GetCachingManager ().Reset();
+ s_HardwareInfoReport.Shutdown();
+ #endif
+
+ SetUnityWebStream(NULL);
+ CompressedFileStream::SetDataFile(NULL);
+
+ return true;
+}
+
+#endif
+
+
+void PlayerLoadFirstLevel ()
+{
+#if UNITY_WII
+ wii::StartLoadingScreen();
+#elif UNITY_ANDROID
+ StartActivityIndicator();
+#endif
+
+ gLoadLevel.ResetLoadLevel(0);
+
+ /////@TODO: Clear preloadmanager queue
+ // Create an empty set of level managers
+
+#if DEBUGMODE
+ // (GlobalGameManagers are already loaded at this point)
+ for (int i=0;i<ManagerContext::kGlobalManagerCount;i++)
+ {
+ if (GetManagerPtrFromContext(i) == NULL)
+ printf_console ("GlobalManager '%s' is not included.\n", GetManagerContext().m_ManagerNames[i]);
+ }
+# endif
+
+ AwakeFromLoadQueue emptyQueue (kMemTempAlloc);
+ LoadManagers(emptyQueue);
+
+ // Load level async
+ PreloadLevelOperation* op = PreloadLevelOperation::LoadLevel (kMainData, kMainDataSharedAssets, 0, PreloadLevelOperation::kLoadMainData, true);
+ GetPreloadManager().WaitForAllAsyncOperationsToComplete();
+ op->Release();
+
+ GetTimeManager().ResetTime ();
+
+#if UNITY_WII
+ wii::EndLoadingScreen();
+#elif UNITY_ANDROID
+ StopActivityIndicator();
+#endif
+ //?@TODO MAKE WORK
+}
+
+void PlayerInitState(void)
+{
+ MessageData pauseData;
+ pauseData.SetData((kPlayerPaused == gPlayerPause), ClassID(bool));
+ SendMessageToEveryone(kPlayerPause, pauseData);
+
+ SetPlayerFocus(GetScreenManager().GetIsFocused());
+
+#if ENABLE_CLUSTER_SYNC
+ if(GetIClusterRenderer())
+ GetIClusterRenderer()->InitCluster();
+#endif
+}
+
+// Terminate player app.
+bool PlayerCleanup(bool cleanupEverything, bool forceQuit)
+{
+ gPlayerPause = kPlayerPaused;///WHY????
+
+ if (!NotifyPlayerQuit (forceQuit))
+ {
+ Assert(!forceQuit);
+ gPlayerPause = kPlayerRunning;
+ return false;
+ }
+
+#if ENABLE_CLUSTER_SYNC
+ if(GetIClusterRenderer())
+ GetIClusterRenderer()->ShutdownCluster();
+#endif
+
+ ReleasePreloadManager();
+
+ PlayerPrefs::Sync();
+
+#if ENABLE_PROFILER
+ UnityProfiler::CleanupGfx();
+#endif
+
+ InputShutdown();
+
+ if (cleanupEverything)
+ {
+ CleanupEngine();
+ #if ENABLE_MONO
+ CleanupMono();
+ #endif
+ }
+
+ CleanupPersistentManager();
+
+ #if ENABLE_PROFILER
+
+ #if UNITY_EDITOR
+ ProfilerHistory::Cleanup();
+ #endif
+
+ ProfilerConnection::Cleanup();
+ UnityProfiler::Cleanup();
+ #endif
+
+ #if ENABLE_PLAYERCONNECTION && !UNITY_EDITOR
+ // Send last bits (required for automated tests)
+ PlayerConnection::Get().SendMessage(ANY_PLAYERCONNECTION, GeneralConnection::kApplicationQuitMessage, NULL, 0);
+ PlayerConnection::Get().WaitForFinish();
+ PlayerConnection::Get().DisconnectAll();
+ PlayerConnection::Cleanup();
+ #endif
+
+ #if ENABLE_HARDWARE_INFO_REPORTER
+ s_HardwareInfoReport.Shutdown();
+ #endif
+
+ return true;
+}
+
+bool g_RuntimeInitialized = false;
+void RuntimeInitialize()
+{
+ if(g_RuntimeInitialized)
+ return;
+#if SUPPORT_THREADS
+ Thread::mainThreadId = Thread::GetCurrentThreadID ();
+#endif
+ g_RuntimeInitialized = true;
+ #if ENABLE_MEMORY_MANAGER
+ MemoryManager::StaticInitialize();
+ #endif
+
+ RegisterRuntimeInitializeAndCleanup::ExecuteInitializations();
+
+#if !UNITY_ANDROID
+ // atexit functions are called at process termination - _not_ when dlclose is called and the dynamic library is unloaded.
+ // Also, it seems plenty of platforms call RuntimeCleanup in a controlled fashion, instead of relying on the libc cleanup...
+ atexit(RuntimeCleanup);
+#endif
+}
+
+void RuntimeCleanup()
+{
+ if(!g_RuntimeInitialized)
+ return;
+ g_RuntimeInitialized = false;
+
+ RegisterRuntimeInitializeAndCleanup::ExecuteCleanup();
+
+ File::CleanupClass();
+
+#if ENABLE_MEMORY_MANAGER
+#if !UNITY_WP8 // don't touch memory managers because managed finalizers can try to release memory after cleanup. todo: check if metro relates to this issue
+ GetMemoryManager().FrameMaintenance(true);
+// size_t remainingMemory = GetMemoryManager().GetTotalAllocatedMemory() - GetMemoryManager().GetTotalProfilerMemory();
+// Assert(remainingMemory < 100*1024);
+#if ENABLE_STACKS_ON_ALL_ALLOCS
+ ProfilerString leaks = GetMemoryProfiler()->GetOverview();
+ printf("%s",leaks.c_str());
+#endif
+ MemoryManager::StaticDestroy();
+#endif
+
+#endif // #if ENABLE_MEMORY_MANAGER
+}
+
+static void ResetRandomsAfterLevelLoad ()
+{
+ // reset all internal Random number generators after level load.
+ GlobalCallbacks::Get().resetRandomAfterLevelLoad.Invoke ();
+}
+
+void LevelLoading::LoadLevel (int index, const string& path, AwakeFromLoadQueue& loadQueue)
+{
+#if UNITY_WII
+ wii::StartLoadingScreen();
+#endif
+ set<SInt32> temporaryObjects;
+
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN
+ KeyboardOnScreen::Hide();
+#endif
+
+ // Collect dont destroy on loading objects!
+ DontDestroyOnLoadSet::iterator it=m_DontDestroyOnLoad.begin ();
+ while (it != m_DontDestroyOnLoad.end ())
+ {
+ // This is a better way of doing it.
+ // Object* object = Object::IDToPoint(i->GetInstanceID());
+ Object* object = *it;
+ if (object)
+ {
+ CollectPPtrs (*object, &temporaryObjects);
+ ++it;
+ }
+ else
+ {
+ (*it) = m_DontDestroyOnLoad.back();
+ m_DontDestroyOnLoad.pop_back();
+ }
+ }
+
+ // - Deactivate all game objects that are marked not to be destroyed in scene load
+ /// They are deactivate with kDeactivateToggleForLevelLoad. Some components handle this,
+ /// eg. audio will ignore the deactivate completely.
+ // - Make them all temp so they dont get killed
+ typedef set<PPtr<GameObject> > NeedActivation;
+ NeedActivation needActivation;
+ typedef set<PPtr<Object> > NeedsIsTemporaryReset;
+ NeedsIsTemporaryReset needsIsTemporaryReset;
+
+ for (set<SInt32>::iterator i=temporaryObjects.begin ();i != temporaryObjects.end ();i++)
+ {
+ Object* object = PPtr<Object> (*i);
+ if (object)
+ {
+ #if UNITY_EDITOR
+ // In the editor we never unload assets anyway. Thus we shouldn't mess with assets in that case
+ bool needsDontSaveFlag = !object->TestHideFlag (Object::kDontSave) && !object->IsPersistent();
+ #else
+ // Only mark objects that are set to DontSave
+ bool needsDontSaveFlag = !object->TestHideFlag (Object::kDontSave);
+ #endif
+
+ if (needsDontSaveFlag)
+ {
+ needsIsTemporaryReset.insert (object);
+ // game objects don't apply the same to all components
+ object->SetHideFlagsObjectOnly (object->GetHideFlags() | Object::kDontSave);
+ }
+
+ // We used to Deactivate / Activate game objects between level reloads but now we instead keep all managers that track objects always around.
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ {
+ // Deactivate the game!
+ GameObject* go = dynamic_pptr_cast<GameObject*> (object);
+ if (go && go->IsActive ())
+ {
+ needActivation.insert (go);
+ go->Deactivate (kDeprecatedDeactivateToggleForLevelLoad);
+ }
+ }
+ }
+ }
+
+ // Movie textures are assets thus might not get unloaded after a level load.
+ // However since the playback code is on the asset, we should just unpause all videos after loading as a convenience to the user.
+ // (Optimally a movie playback component would be what drives the movie playback code instead, but we are not doing that...)
+ #if ENABLE_MOVIES || ENABLE_WEBCAM
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ audioModule->PauseVideoTextures ();
+ #endif
+
+ // Load level
+ UnloadGameScene ();
+
+ // Reset the temporary state
+ for (NeedsIsTemporaryReset::iterator i=needsIsTemporaryReset.begin ();i != needsIsTemporaryReset.end ();i++)
+ {
+ Object* object = *i;
+ if (object)
+ {
+ int flags = object->GetHideFlags();
+ flags &= ~Object::kDontSave;
+ // Don't apply the same to all components
+ object->SetHideFlagsObjectOnly (flags);
+ }
+ }
+
+ m_ActiveLoadedLevel = index;
+ m_hasLateBoundLevel = false;
+
+ if (m_ActiveLoadedLevel < 0)
+ {
+ // If the m_ActiveLoadedLevel is -1, this means we're loading from an asset bundle.
+ m_hasLateBoundLevel = true;
+
+ m_lateBoundLevelName = m_tempLateBoundLevelName;
+ m_tempLateBoundLevelName.clear();
+ }
+
+ CompletePreloadManagerLoadLevel (path, loadQueue);
+
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ {
+ // Activate the game objects that were marked not to be destroyed on scene load
+ for (NeedActivation::iterator i=needActivation.begin ();i != needActivation.end ();i++)
+ {
+ GameObject* go = *i;
+ if (go != NULL && !go->IsActive ())
+ {
+ go->Activate ();
+ }
+ else
+ {
+ #if UNITY_EDITOR
+ ErrorString("Some objects were marked as DontDestroyOnLoad, but since their parent game objects were not marked as DontDestroyOnLoad they have been destroyed anyway.\nIf you want to keep them around, please ensure their parent is marked as DontDestroyOnLoad or make sure they are unparented before loading a new level.");
+ #endif
+ }
+ }
+ }
+
+ // Send all active gameobjects a kLevelWasLoaded message
+ MessageData msgData;
+ msgData.SetData (index, ClassID (int));
+ SendMessageToEveryone (kLevelWasLoaded, msgData);
+
+ ResetRandomsAfterLevelLoad ();
+ GetTimeManager ().DidFinishLoadingLevel ();
+
+#if UNITY_WII
+ wii::EndLoadingScreen();
+#endif
+}
+
+int PlayerGetLoadedLevel ()
+{
+ return gLoadLevel.GetLoadedLevelIndex();
+}
+
+string PlayerGetLoadedLevelName ()
+{
+ int loadedLevel = PlayerGetLoadedLevel ();
+ // If loadedLevel is invalid, try to check the late bound level.
+ if (loadedLevel >= 0)
+ return GetBuildSettings ().GetLevelName(loadedLevel);
+ else if (gLoadLevel.m_hasLateBoundLevel)
+ return gLoadLevel.m_lateBoundLevelName;
+
+ return string ();
+}
+
+int PlayerGetLevelCount ()
+{
+ return GetBuildSettings ().levels.size ();
+}
+
+void PlayerSendFrameComplete()
+{
+ GetDelayedCallManager ().Update (DelayedCallManager::kEndOfFrame);
+}
+
+void PlayerTakeScreenShots()
+{
+// for now only on iphone we have explicit msaa resolve so we have a place where we can get proper screenshot
+// TODO: proper define?
+#if GAMERELEASE && CAPTURE_SCREENSHOT_AVAILABLE && !UNITY_IPHONE
+ UpdateCaptureScreenshot ();
+ #if SUPPORT_REPRODUCE_LOG
+ CaptureScreenshotReproduction(false);
+ #endif
+#endif
+}
+
+PROFILER_INFORMATION(gRemoteInputProfile, "ProcessRemoteInput", kProfilerOther);
+static void ProcessRemoteInput ()
+{
+ PROFILER_AUTO(gRemoteInputProfile, NULL);
+
+
+#if SUPPORT_IPHONE_REMOTE && UNITY_EDITOR
+ void RemoteInputUpdate();
+ RemoteInputUpdate();
+#endif
+
+}
+
+bool IsInsidePlayerLoop ()
+{
+ return s_InsidePlayerLoop;
+}
+
+PROFILER_INFORMATION(gGraphicsPresent, "Device.Present", kProfilerRender);
+PROFILER_INFORMATION(gFramerateSync, "WaitForTargetFPS", kProfilerVSync);
+PROFILER_INFORMATION(gSubstanceUpdate, "Substance.Update", kProfilerRender)
+PROFILER_INFORMATION(gPlayerLoopPresent, "Graphics.PresentAndSync", kProfilerRender)
+PROFILER_INFORMATION(gPlayerLoopBeginFrame, "Graphics.BeginFrame", kProfilerRender)
+PROFILER_INFORMATION(gUpdatePreloading, "Loading.UpdatePreloading", kProfilerPlayerLoop)
+PROFILER_INFORMATION(gUpdateWebStream, "Loading.UpdateWebStream", kProfilerPlayerLoop)
+PROFILER_INFORMATION(gPlayerCleanupCachedData, "Cleanup Unused Cached Data", kProfilerPlayerLoop)
+
+#if !UNITY_EDITOR
+
+void PresentFrame()
+{
+ PROFILER_AUTO(gGraphicsPresent, NULL);
+#if ((UNITY_LINUX && SUPPORT_X11) || UNITY_OSX) && !UNITY_EDITOR && !WEBPLUG
+ GetScreenManager().SetBlitMaterial();
+#endif
+ GfxDevice& device = GetGfxDevice();
+ device.PresentFrame();
+ GPU_TIMESTAMP();
+ gHasFrameToPresent = false;
+}
+
+void PresentAndSync()
+{
+#if GAMERELEASE
+ #if UNITY_WIN && !UNITY_WINRT
+ if (!IsWindowVisible(GetScreenManager().GetWindow()))
+ return;
+ #endif
+
+ PROFILER_AUTO (gPlayerLoopPresent, NULL)
+
+ if (g_PresentCallback) g_PresentCallback(true);
+
+ PresentFrame();
+
+ if (g_PresentCallback) g_PresentCallback(false);
+#endif
+ ;;gFrameStats.reset();
+}
+
+void PresentBeforeUpdate(GfxDevice::PresentMode presentMode)
+{
+ if (presentMode != GfxDevice::kPresentBeforeUpdate)
+ return;
+
+ // NOTE: updating TimeManager after VBlank potentially leads to stable frame delta times
+ // since there is no OS event pump between them in such case
+ if (gHasFrameToPresent)
+ {
+ #if UNITY_XENON
+ xenon::Kinect::Update(xenon::Kinect::kUT_BeforePresent);
+ #endif
+ PresentAndSync();
+ }
+}
+
+void PresentAfterDraw(GfxDevice::PresentMode presentMode)
+{
+ switch (presentMode)
+ {
+ case GfxDevice::kPresentAfterDraw:
+#if UNITY_XENON
+ xenon::Kinect::Update(xenon::Kinect::kUT_BeforePresent);
+#endif
+ PresentAndSync();
+ break;
+
+ case GfxDevice::kPresentBeforeUpdate:
+ // Present first frame immediately
+ if (gIsFirstFrame)
+ PresentAndSync();
+ break;
+ }
+ gIsFirstFrame = false;
+};
+
+static bool PlayerBeginFrame()
+{
+ GfxDevice& device = GetGfxDevice();
+ for (;;)
+ {
+ // We might already be inside a frame if something was rendered from script during update
+ if (!device.IsInsideFrame())
+ device.BeginFrame();
+
+ // Check if GfxDevice is ready to render
+ if (device.IsValidState())
+ return true;
+
+ // Handle Direct3D lost device
+ if (!device.HandleInvalidState())
+ {
+ LogString ("Skipped rendering frame because GfxDevice is in invalid state (device lost)");
+ return false;
+ }
+ // Loop back to BeginFrame() since we might be outside a frame if the device was lost
+ }
+}
+
+static void PlayerEndFrame(bool present)
+{
+ gHasFrameToPresent = true;
+ if (present)
+ {
+ GetGfxDevice().EndFrame();
+ PresentFrame();
+ }
+}
+
+void PlayerRender(bool present)
+{
+ if (!PlayerBeginFrame())
+ return;
+
+ GetRenderManager().RenderOffscreenCameras();
+ GetRenderManager().RenderCameras();
+
+#if ENABLE_UNITYGUI
+ GetGUIManager().Repaint ();
+#endif
+#if UNITY_WII
+ wii::DrawMovieFrame();
+#endif
+
+ DrawSplashAndWatermarks();
+
+#if RENDER_SOFTWARE_CURSOR
+ Cursors::RenderSoftwareCursor();
+#endif
+
+#if WEBPLUG
+ RenderFullscreenEscapeWarning();
+#endif
+
+ PlayerEndFrame(present);
+}
+
+#endif // !UNITY_EDITOR
+
+
+int GetWantedVSyncCount()
+{
+ // What the user has requested, no clamping to actual caps
+ QualitySettings* qualitySettings = GetQualitySettingsPtr();
+ if (qualitySettings)
+ return qualitySettings->GetCurrent().vSyncCount;
+ return 0;
+}
+
+int GetMaxSupportedVSyncCount()
+{
+ int vSync = gGraphicsCaps.maxVSyncInterval;
+#if UNITY_WIN
+ if (!GetScreenManager().IsFullScreen() &&
+ GetGfxDevice().GetRenderer() == kGfxRendererD3D9)
+ {
+ // D3D9 does not support intervals >1 in windowed mode (case 497116)
+ vSync = std::min(vSync, 1);
+ }
+#endif
+ return vSync;
+}
+
+void PlayerUpdateTime()
+{
+ // Xbox is bound to VBlank and does its own delicate Kinect synchronization.
+ // iOS uses its own VBlank sync
+#if !UNITY_XENON && !UNITY_FLASH && !UNITY_IPHONE && !UNITY_PS3 && !UNITY_WEBGL
+ {
+ int vSyncWanted = GetWantedVSyncCount();
+ int vSyncSupported = GetMaxSupportedVSyncCount();
+ // If interval is non-zero and supported by GfxDevice, don't sync on CPU
+ if (UNITY_EDITOR || vSyncWanted == 0 || vSyncWanted > vSyncSupported)
+ {
+ PROFILER_AUTO(gFramerateSync, NULL);
+ float actualTargetFPS = GetActualTargetFrameRate(vSyncWanted);
+ GetTimeManager().Sync(actualTargetFPS);
+ }
+ }
+#endif
+
+ // Update time, input
+ GetTimeManager().Update();
+}
+
+PROFILER_INFORMATION(gOnMouseHandlers, "Monobehaviour.OnMouse_", kProfilerScripts);
+
+bool PlayerLoop (bool batchMode, bool performRendering, IHookEvent* pHookEvt)
+{
+#if !UNITY_FLASH
+ ReentrancyChecker checker( &s_InsidePlayerLoop );
+ if (!checker.IsOK())
+ {
+ ErrorString ("PlayerLoop called recursively!");
+ return true;
+ }
+#endif
+
+ #if SUPPORT_LOG_ORDER_TRACE
+ if (RunningReproduction())
+ {
+ LogString(Format("Frame %d", GetTimeManager().GetFrameCount()));
+ }
+ #endif
+
+#if ENABLE_CLUSTER_SYNC
+ if(GetIClusterRenderer())
+ GetIClusterRenderer()->SynchronizeCluster();
+#endif
+
+ #if ENABLE_PLAYERCONNECTION && !UNITY_EDITOR
+ PlayerConnection::Get().Poll();
+ #endif
+
+ #if ENABLE_PROFILER && GAMERELEASE
+
+ UnityProfiler::RecordPreviousFrame(kProfilerGame);
+ bool profilerEnabled = UnityProfiler::StartNewFrame(kProfilerGame);
+
+ if (profilerEnabled)
+ {
+ GfxDevice& device = GetGfxDevice();
+ device.BeginFrameStats();
+ }
+
+ #endif // ENABLE_PROFILER && GAMERELEASE
+
+ GPU_TIMESTAMP(); // Initial GPU time
+
+ #if ENABLE_SUBSTANCE
+ {
+ SubstanceSystem* system = GetSubstanceSystemPtr ();
+ if (system != NULL)
+ {
+ PROFILER_AUTO(gSubstanceUpdate, NULL)
+ system->Update();
+ }
+ }
+ #endif
+
+ ;;FrameStats::Timestamp ts = 0;
+
+ #if SUPPORT_REPRODUCE_LOG
+ static bool didSetWebplayerSize = false;
+ if (!didSetWebplayerSize)
+ {
+ didSetWebplayerSize = true;
+ WriteWebplayerSize(GetScreenManager().GetWidth(), GetScreenManager().GetHeight());
+ }
+ #endif // SUPPORT_REPRODUCE_LOG
+
+ if (!batchMode)
+ ProcessMouseInWindow();
+
+ // In the editor, clear intermediate renderers before loop. So that in paused state or when resizing windows,
+ // we can still draw the previous ones.
+ #if UNITY_EDITOR
+ GetScene().ClearIntermediateRenderers();
+ #endif
+
+ ClearLines();
+
+ GfxDevice& device = GetGfxDevice();
+ GfxDevice::PresentMode presentMode = device.GetPresentMode();
+ bool playmode = IsWorldPlaying();
+
+ #if !UNITY_EDITOR
+ if (!batchMode)
+ PresentBeforeUpdate(presentMode);
+ #endif
+
+ // Reset frame stats after present (case 496221)
+ if (presentMode == GfxDevice::kPresentBeforeUpdate)
+ device.ResetFrameStats();
+
+ #if SUPPORT_REPRODUCE_LOG
+ RepeatReproductionScreenshot();
+ ReadWriteReproductionTime();
+ #endif
+#if ENABLE_CLUSTER_SYNC
+ if(!GetIClusterRenderer() || GetIClusterRenderer()->IsMasterOfCluster())
+#endif
+ {
+ PlayerUpdateTime();
+ }
+
+ #if ENABLE_WWW
+ {
+ PROFILER_AUTO(gUpdateWebStream, NULL);
+ UnityWebStream::UpdateAllUnityWebStreams();
+ }
+ #endif
+
+
+ ////@TODO: CLeanup code where input is processed after level loading
+ /// All input should be processed prior to level loading
+
+ // In reproduce mode make sure to handle input postprocessor prior to loading a level.
+ // This fixes potential issues where scripts use input in awake from load
+ #if SUPPORT_REPRODUCE_LOG
+ if (RunningReproduction())
+ {
+ if (playmode)
+ GetInputManager ().ProcessInput ();
+ ReadWriteReproductionInput();
+ }
+ #endif
+
+ #if SUPPORT_REPRODUCE_LOG
+ if (RunningReproduction())
+ GetPreloadManager().WaitForAllAsyncOperationsToComplete();
+ #endif
+
+ {
+ PROFILER_AUTO(gUpdatePreloading, NULL)
+ GetPreloadManager().UpdatePreloading ();
+ }
+
+ if (performRendering)
+ GetScene().NotifyInvisible();
+
+ {
+ PROFILER_AUTO(gPlayerCleanupCachedData, NULL)
+ GetRenderBufferManager().GarbageCollect();
+ TextMeshGenerator2::GarbageCollect();
+ }
+
+ ////@TODO: CLeanup code where input is processed after level loading
+ /// All input should be processed prior to level loading
+ #if SUPPORT_REPRODUCE_LOG
+ if (!RunningReproduction())
+ #endif
+ {
+ if (playmode)
+ GetInputManager ().ProcessInput ();
+ }
+
+ ProcessRemoteInput ();
+
+ ;;ts = GetTimestamp();
+ GetDelayedCallManager ().Update (DelayedCallManager::kRunStartupFrame);
+ ;;gFrameStats.coroutineDt += GetTimestamp() - ts;
+
+ /* This is what fixed time stepping is doing
+ float time = GetCurrentTime ();
+ while (fixedTime < time)
+ {
+ fixedTime += fixedDeltaTime;
+ UpdateFixedBehaviours ();
+ UdateDynamicsManager ();
+ }
+ Which means:
+ - fixed timestep is always larger than dynamic timestep
+ - fixed delta time is always the same
+ */
+
+ int stepCounter = 0;
+
+ // Make sure collider positions update when time scale is zero, so raycasts will still work as expected.
+ if (CompareApproximately (GetTimeManager().GetTimeScale(), 0.0f))
+ {
+ CALL_UPDATE_MODULAR(PhysicsRefreshWhenPaused)
+ }
+
+ #if UNITY_XENON
+ xenon::Kinect::Update(xenon::Kinect::kUT_BeforeUserScripts);
+ #endif
+
+#if UNITY_IPHONE
+ iphone::DeliverPlatformEvents();
+#endif
+
+ // Fixed framerate loop (fixed behaviours, dynamics, delayed calling)
+ while (GetTimeManager ().StepFixedTime ())
+ {
+ // Placed here so we ensure it is also called in edit-mode (fix for case 379024: pressing stop did not properly clear fixedStepLines)
+ ClearLines ();
+
+ if (playmode)
+ {
+ if (stepCounter == 0)
+ {
+ CALL_UPDATE_MODULAR(PhysicsResetInterpolatedTransformPosition)
+ CALL_UPDATE_MODULAR(Physics2DResetInterpolatedTransformPosition)
+ }
+
+ #if ENABLE_AUDIO
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ audioModule->FixedUpdate ();
+ #endif
+
+ // Script.FixedUpdate
+ ;;ts = GetTimestamp();
+ GetFixedBehaviourManager ().Update ();
+ ;;gFrameStats.fixedBehaviourManagerDt += (GetTimestamp() - ts);
+
+ // Animation (Root motion)
+ ;;ts = GetTimestamp();
+ CALL_UPDATE_MODULAR(AnimatorFixedUpdateFKMove)
+ CALL_UPDATE_MODULAR(LegacyAnimationUpdate)
+ ;;gFrameStats.animationUpdateDt += (GetTimestamp() - ts);
+
+ // Physics
+ ;;ts = GetTimestamp();
+ CALL_UPDATE_MODULAR(PhysicsFixedUpdate)
+ CALL_UPDATE_MODULAR(Physics2DFixedUpdate)
+ ;;gFrameStats.fixedPhysicsManagerDt += GetTimestamp() - ts;
+
+ // Animation IK and write bones
+ CALL_UPDATE_MODULAR(AnimatorFixedUpdateRetargetIKWrite)
+
+ // Script Coroutines
+ ;;ts = GetTimestamp();
+ GetDelayedCallManager ().Update (DelayedCallManager::kRunFixedFrameRate);
+ ;;gFrameStats.coroutineDt += GetTimestamp() - ts;
+ }
+ stepCounter++;
+ }
+ gFrameStats.fixedUpdateCount = stepCounter;
+
+ // Dynamics, animation, behaviours
+ if (playmode)
+ {
+ CALL_UPDATE_MODULAR(PhysicsUpdate)
+ CALL_UPDATE_MODULAR(Physics2DUpdate)
+ }
+ bool oldTextFocus = GetInputManager().GetTextFieldInput();
+
+#if ENABLE_UNITYGUI
+ GetGUIManager().SendQueuedEvents ();
+#endif
+
+#if ENABLE_NEW_EVENT_SYSTEM
+#if ENABLE_UNITYGUI
+ if (playmode && !GetGUIManager().GetMouseUsed())
+#else
+ if (playmode)
+#endif
+ GetInputManager().SendInputEvents();
+#endif
+
+#if ENABLE_RETAINEDGUI
+ GetGUITracker().SendInputEvents(GetGUIManager().GetQueuedEvents());
+#endif
+
+#if ENABLE_SCRIPTING && SUPPORT_MOUSE
+#if ENABLE_NEW_EVENT_SYSTEM
+ // Deprecated in Unity 4.1. Mouse and touch events will now be sent via C++. Look inside InputManager.cpp.
+ if (playmode && !IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a1))
+#else
+ if (playmode)
+#endif
+ {
+ if (GetBuildSettings().usesOnMouseEvents)
+ {
+ PROFILER_AUTO(gOnMouseHandlers, NULL)
+ int mouseUsed = GetGUIManager().GetMouseUsed () ? 1 : 0;
+ int skipRTCameras = IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1);
+
+ ScriptingInvocation invocation(MONO_COMMON.doSendMouseEvents);
+ invocation.AddInt(mouseUsed);
+ invocation.AddInt(skipRTCameras);
+ invocation.Invoke();
+ }
+ }
+#endif
+
+ CALL_UPDATE_MODULAR(NavMeshUpdate)
+
+ ;;ts = GetTimestamp();
+ GetBehaviourManager ().Update ();
+ ;;gFrameStats.dynamicBehaviourManagerDt += GetTimestamp() - ts;
+
+ ;;ts = GetTimestamp();
+ GetDelayedCallManager ().Update (DelayedCallManager::kRunDynamicFrameRate);
+ ;;gFrameStats.coroutineDt += GetTimestamp() - ts;
+
+ // Dynamic Step Animation Update
+ if (playmode)
+ {
+ ;;ts = GetTimestamp();
+
+ CALL_UPDATE_MODULAR(AnimatorUpdateFKMove);
+ CALL_UPDATE_MODULAR(LegacyAnimationUpdate);
+ CALL_UPDATE_MODULAR(AnimatorUpdateRetargetIKWrite);
+
+ ;;gFrameStats.animationUpdateDt += (GetTimestamp() - ts);
+ }
+
+ #if ENABLE_NETWORK
+ if (playmode)
+ {
+ CALL_MANAGER_IF_EXISTS(ManagerContext::kNetworkManager, NetworkUpdate());
+ CALL_MANAGER_IF_EXISTS(ManagerContext::kMasterServerInterface, NetworkUpdate());
+ }
+ #endif
+
+ ParticleSystem::BeginUpdateAll ();
+
+ ;;ts = GetTimestamp();
+ GetLateBehaviourManager ().Update ();
+ ;;gFrameStats.dynamicBehaviourManagerDt += GetTimestamp() - ts;
+
+ ;;ts = GetTimestamp();
+ GetDelayedCallManager ().Update (DelayedCallManager::kRunDynamicFrameRate);
+ ;;gFrameStats.coroutineDt += GetTimestamp() - ts;
+
+ #if ENABLE_AUDIO
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ audioModule->Update();
+ #endif
+
+ ParticleEmitter::UpdateAllParticleSystems();
+
+ #if ENABLE_PHYSICS
+ if (playmode && performRendering && device.IsValidState())
+ {
+ CALL_UPDATE_MODULAR(PhysicsSkinnedClothUpdate);
+ }
+ #endif
+
+ // We need to sync particle systems here to make sure they update their renderers properly
+ ParticleSystem::EndUpdateAll ();
+
+ RenderManager::UpdateAllRenderers();
+
+ if(pHookEvt)
+ pHookEvt->Execute();
+
+ ;;ts = GetTimestamp();
+ if (performRendering && device.IsValidState()) // skinning touches graphics resources; only do that when device is not lost
+ {
+ SkinnedMeshRenderer::UpdateAllSkinnedMeshes(SkinnedMeshRenderer::kUpdateNonCloth);
+ }
+ ;;gFrameStats.skinMeshUpdateDt += GetTimestamp() - ts;
+
+ #if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+ WebScripting::Get().ProcessSendMessages();
+ #endif
+ GetUpdateManager ().Update ();
+
+ if(pHookEvt)
+ pHookEvt->Execute();
+
+ #if ENABLE_MOVIES || ENABLE_WEBCAM
+ if (performRendering && device.IsValidState()) // movies touch graphics resources; only do that when device is not lost
+ {
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ audioModule->UpdateVideoTextures ();
+ }
+ #endif
+
+
+#if UNITY_WII && !WIIWARE
+ void ProcessDVDErrors();
+ ProcessDVDErrors();
+#endif
+
+ // In d3d ref mode only render when taking screenshots
+ #if SUPPORT_REPRODUCE_LOG && GFX_SUPPORTS_D3D9 && ENABLE_FORCE_GFX_RENDERER
+ if (::g_ForceD3D9RefDevice && RunningReproduction() && !GetInputManager().GetKey(SDLK_F5))
+ performRendering = false;
+ #endif
+
+ #if !UNITY_EDITOR
+ if (performRendering)
+ {
+ ;;ts = GetTimestamp();
+
+ // Perform rendering
+ if (!batchMode)
+ PlayerRender(false);
+
+ // We want to send 'end-of-frame' events even if running in batch mode
+ PlayerSendFrameComplete();
+
+ PlayerTakeScreenShots();
+
+ if (!batchMode)
+ {
+ device.EndFrame();
+ PresentAfterDraw(presentMode);
+ }
+
+ ;;gFrameStats.renderDt += GetTimestamp() - ts;
+ } // End rendering
+
+ // In the player, clear intermediate renderers after loop.
+ GetScene().ClearIntermediateRenderers();
+
+ #endif // !UNITY_EDITOR
+
+ #if SUPPORT_REPRODUCE_LOG
+ ReadWriteReproductionEnd();
+ #endif
+
+ GetScreenManager().SetRequestedResolution();
+
+ // Clear the input string and the keydown events at the end of the Loop.
+ // This makes sure all input string is cleared
+ GetInputManager ().InputEndFrame ();
+
+ // Let Fonts uncache characters if they are not used in the next frame.
+ Font::FrameComplete();
+
+ // We entered Text Field input this frame, Game mode input is disable.
+ // Reset axes, so they don't stick.
+ if(GetInputManager().GetTextFieldInput() && !oldTextFocus)
+ GetInputManager ().ResetInputAxes ();
+
+ #if WEBPLUG
+ if (!LoadQueuedWebPlayerData())
+ return false;
+ #endif
+
+ #if THREADED_LOADING_DEBUG
+ static double endFrameTime = 0.0F;
+ float delta = GetTimeSinceStartup() - endFrameTime;
+ endFrameTime = GetTimeSinceStartup();
+ printf_console("FPS %f\n", 1.0F / delta);
+ #endif
+
+ #if ENABLE_PROFILER && GAMERELEASE
+ if (profilerEnabled)
+ {
+ device.EndFrameStats();
+ device.SynchronizeStats();
+ }
+ #endif
+
+ #if ENABLE_MEMORY_MANAGER
+ GetMemoryManager().FrameMaintenance();
+ #endif
+
+ #if ENABLE_GAMECENTER
+ GameCenter::ExecuteGameCenterCallbacks();
+ #endif
+
+ // Reset frame stats after present and UnityProfiler EndFrame() (case 494732)
+ if (presentMode == GfxDevice::kPresentAfterDraw)
+ device.ResetFrameStats();
+
+ return true;
+}
+
+void LevelLoadingLoop()
+{
+#if UNITY_ANDROID
+ // make sure input is being processed to keep those ANR dialogs at bay..
+ InputProcess();
+#endif
+#if UNITY_METRO
+ void UpdateMetroEvents();
+ UpdateMetroEvents();
+#endif
+ Thread::Sleep(0.020f);
+}
+
+
+#if !UNITY_EDITOR && !WEBPLUG
+bool SwitchToStandaloneDefaultSettings()
+{
+ // Reload quality settings (Preferences might have changed)
+ GetQualitySettings().AwakeFromLoad(kDefaultAwakeFromLoad);
+
+#if UNITY_IPHONE
+ // on ios we do not use player prefs for resolution
+ bool success = true;
+#else
+ const PlayerSettings& settings = GetPlayerSettings();
+ int width = PlayerPrefs::GetInt(kResolutionWidth, settings.defaultScreenWidth);
+ int height = PlayerPrefs::GetInt(kResolutionHeight, settings.defaultScreenHeight);
+ bool fullscreen = PlayerPrefs::GetInt(kIsFullScreen, settings.defaultIsFullScreen);
+
+ if (fullscreen &&
+ !(PlayerPrefs::HasKey (kResolutionWidth) && PlayerPrefs::HasKey (kResolutionHeight)) &&
+ settings.defaultIsNativeResolution) {
+ width = GetScreenManager ().GetCurrentResolution ().width;
+ height = GetScreenManager ().GetCurrentResolution ().height;
+ }
+
+ bool success = GetScreenManager ().SetResolutionImmediate (width, height, fullscreen, 0);
+#endif
+ GetScreenManager().SetShowCursor (true);
+ return success;
+}
+#endif
+
+#if UNITY_OSX && !UNITY_EDITOR && !UNITY_PEPPER
+bool SwitchToBatchmode()
+{
+ // Reload quality settings (Preferences might have changed)
+ GetQualitySettings().AwakeFromLoad(kDefaultAwakeFromLoad);
+
+ bool success = GetScreenManager().SwitchToBatchmode();
+ return success;
+}
+#endif
+
+int GetTargetFrameRateFromScripting()
+{
+ return gTargetFrameRate;
+}
+
+int GetTargetFrameRate()
+{
+#if SUPPORT_REPRODUCE_LOG
+ if (HasNormalPlaybackSpeed() || GetReproduceMode() == kGenerateReproduceLog || GetReproduceMode() == kGenerateReproduceLogAndRemapWWW)
+ return 20;
+ else if (GetReproduceMode() == kPlaybackReproduceLog)
+ return 1000;
+#endif
+
+ return gTargetFrameRate;
+}
+
+#if UNITY_IPHONE
+ extern "C" void NotifyFramerateChange(int);
+#endif
+
+void SetTargetFrameRate(int target)
+{
+ gTargetFrameRate = target;
+#if UNITY_FLASH
+ __asm __volatile__("stage.frameRate = %0" : : "r"(target));
+#endif
+
+#if UNITY_IPHONE
+ NotifyFramerateChange(gTargetFrameRate);
+#endif
+
+#if UNITY_WP8
+ extern BridgeInterface::IBridge^ s_WinRTBridge;
+ Assert(s_WinRTBridge);
+ s_WinRTBridge->WP8Utility->MaxFrameRate = gTargetFrameRate;
+#endif
+}
+
+float GetActualTargetFrameRate()
+{
+ return GetActualTargetFrameRate(GetWantedVSyncCount());
+}
+
+float GetActualTargetFrameRate(int vSyncCount)
+{
+#if UNITY_FLASH
+ int fr;
+ __asm __volatile__("%0 = stage.frameRate;" : "=r"(fr));
+ return fr;
+#endif
+
+#if UNITY_WIN && WEBPLUG
+ const int kFullscreenTargetFPS = 200;
+#else
+ const int kFullscreenTargetFPS = 1000;
+#endif
+
+ float framerate = GetTargetFrameRate();
+
+ // Can be a fraction of an odd refresh rate like 59 so we can't use int
+ if (vSyncCount > 0)
+ {
+ int refreshRate = GetScreenManager().GetCurrentResolution().refreshRate;
+ framerate = float(refreshRate > 0 ? refreshRate : 60) / vSyncCount;
+ }
+
+ if( framerate <= 0 )
+ {
+#if WEBPLUG && !UNITY_PEPPER
+ return (GetScreenManagerPtr() && GetScreenManager().IsFullScreen()) ? kFullscreenTargetFPS : 60;
+#else
+ return kFullscreenTargetFPS;
+#endif
+ }
+ else
+ {
+ return std::min<float>(framerate,kFullscreenTargetFPS);
+ }
+}
diff --git a/Runtime/Misc/Player.h b/Runtime/Misc/Player.h
new file mode 100644
index 0000000..2bd6e62
--- /dev/null
+++ b/Runtime/Misc/Player.h
@@ -0,0 +1,153 @@
+#ifndef _PLAYER_H
+#define _PLAYER_H
+
+#include <string>
+#include "Runtime/Utilities/File.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Misc/PlayerSettings.h"
+
+// To avoid frozen window because of GPU workload
+class IHookEvent
+{
+public:
+ virtual void Execute() = 0;
+};
+
+class Object;
+class DecompressedDataFile;
+class UnityWebStream;
+class AwakeFromLoadQueue;
+
+// ** Initializing unity
+// 1) InitializeMonoFromMain ("Mono dll path");
+// 2) Optional: SelectDataFolder ();
+// 2) PlayerInitEngine ("Data folder", "app path/Contents");
+// 3) Optional: Display screen selector.
+// The engine is initialized so you can access the input manager at this point.
+// 4) PlayerInitEngineGraphics ();
+// 5) if (!SwitchToStandaloneDefaultSettings())
+// couldnt switch resolution
+// 6) PlayerLoadFirstLevel ();
+bool PlayerInitEngineNoGraphics (const std::string& dataFolder, const std::string& applicationContentsFolderPath);
+bool PlayerInitEngineGraphics (bool batchmode = false);
+std::string SelectDataFolder ();
+void PlayerLoadFirstLevel ();
+void PlayerInitState(void);
+
+/// For reloading a unity player from the web player
+bool PlayerInitEngineWeb (const std::string& applicationContentsFolderPath, int pluginversion);
+bool PlayerInitEngineWebNoGraphics (const string& applicationContentsFolderPath, int pluginversion);
+bool PlayerInitEngineWebGraphics ();
+
+bool PlayerLoadWebData (UnityWebStream& data, const std::string srcValue, const std::string absoluteURL, bool resetPrefs = true);
+bool PlayerResetPreferences(UnityWebStream& stream, const std::string absoluteURL, bool clearPlayerPrefs = true);
+bool PlayerLoadEngineData (UnityWebStream& data, const std::string srcValue, const std::string absoluteURL);
+bool PlayerWebUnloadReloadable ();
+
+/// Force loading a new unityweb data file. Used for streaming unityweb files with the WWW class.
+bool QueuePlayerLoadWebData (UnityWebStream* stream);
+
+/// Render scene without doing anything else
+void PlayerRender(bool present);
+
+/// Running unity every frame
+/// 1) Read input in platform specific way.
+/// GetInput.h on os x. This sets appropriate variables inside InputManager.
+/// 2) PlayerLoop ();
+/// Don't call this is if the player is paused (kPlayerPaused == GetPlayerPause)
+bool PlayerLoop (bool batchmode = false, bool performRendering = true, IHookEvent* pHookEvt=NULL);
+
+/// Cleanup the player (Calls NotifyPlayerQuit)
+/// 1) optional PlayerPrefs::Sync ();
+/// 2) PlayerCleanup ();
+/// 3) ExitToShell();
+/// Returns true if cleanup succeeded.
+/// If forceQuit is true it will always cleanup. (Force quit should be enabled in the web player)
+/// If cleanupEverything is true, it will also delete all objects. This should be enabled
+bool PlayerCleanup(bool cleanupEverything, bool forceQuit);
+
+/// Initialize and destroy the runtime. This has to be called as the first thing on player/engine startup
+/// and as the very last thing before exiting.
+/// All globals and statics should be initialized and cleaned up from these functions
+void RuntimeInitialize();
+void RuntimeCleanup();
+
+struct AutoInitializeAndCleanupRuntime
+{
+ AutoInitializeAndCleanupRuntime(){RuntimeInitialize();}
+ ~AutoInitializeAndCleanupRuntime(){RuntimeCleanup();}
+};
+
+int GetPluginVersion();
+
+/// Sends a script message that the player is about to quit
+bool NotifyPlayerQuit(bool forceQuit);
+
+void ProcessMouseInWindow();
+
+enum PlayerPause
+{
+ kPlayerRunning,
+ kPlayerPausing,
+ kPlayerPaused,
+};
+
+// To pause unity: Call PlayerPause. It notifies all appropriate managers to pause or unpause
+// Also you shouldn't call PlayerLoop until the player is not paused anymoyre
+void SetPlayerPause (PlayerPause pause);
+// Is the player currently paused?
+PlayerPause GetPlayerPause ();
+
+// Notify scripts about lost or received focus
+void SetPlayerFocus(bool focus);
+
+// Sends "frame complete" to scripts
+void PlayerSendFrameComplete();
+
+// Reloads quality settings from preferences
+// Resets resolution
+#if !UNITY_EDITOR && !WEBPLUG
+bool SwitchToStandaloneDefaultSettings();
+#endif
+
+#if UNITY_OSX
+bool SwitchToBatchmode();
+#endif
+
+/// Should the player run in the background?
+bool GetPlayerRunInBackground();
+void SetPlayerRunInBackground(bool runInBackground);
+
+
+// For editor. Are we currently inside of the PlayerLoop?
+// Can be used to detect if we are currently executing game code or editor code in the editor..
+bool IsInsidePlayerLoop ();
+
+/// Makes an object survive when loading a new scene.
+void DontDestroyOnLoad (Object& object);
+
+int PlayerGetLoadedLevel ();
+std::string PlayerGetLoadedLevelName ();
+int PlayerGetLevelCount ();
+bool GetLevelAndAssetPath (const std::string& levelName, int levelIndex, std::string* levelPath, std::string* assetPath, int* index);
+bool GetHasLateBoundLevelFromAssetBundle (const std::string& name);
+
+void ResetPlayerInEditor (int level);
+bool IfWillLoadLevel ();
+bool IsLoadingLevel ();
+
+/// Called from PreloadManager::WaitForAllAsyncOperationsToComplete
+/// while the loading queue is being processed, and the main thread is stalled.
+void LevelLoadingLoop();
+
+float GetActualTargetFrameRate();
+float GetActualTargetFrameRate(int vSyncCount);
+int GetTargetFrameRate();
+int GetTargetFrameRateFromScripting();
+void SetTargetFrameRate(int target);
+
+//// HACK
+void PlayerLoadLevelFromThread (int levelIndex, const std::string& name, AwakeFromLoadQueue& awakeFromLoadQueue);
+
+#endif
diff --git a/Runtime/Misc/PlayerSettings.cpp b/Runtime/Misc/PlayerSettings.cpp
new file mode 100644
index 0000000..0c7b422
--- /dev/null
+++ b/Runtime/Misc/PlayerSettings.cpp
@@ -0,0 +1,436 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "PlayerSettings.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/BaseClasses/Cursor.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/ColorSpaceLiveSwitch.h"
+#endif
+
+
+// ----------------------------------------------------------------------
+
+
+// ----------------------------------------------------------------------
+
+static const char* kAspectRatioSerializeNames[kAspectCount] = {
+ "Others",
+ "4:3",
+ "5:4",
+ "16:10",
+ "16:9",
+};
+
+static float kAspectRatioValues[kAspectCount] = {
+ 0.0f,
+ 4.0f/3.0f,
+ 5.0f/4.0f,
+ 16.0f/10.0f,
+ 16.0f/9.0f,
+};
+
+static bool DoesMatchAspectRatio (int width, int height, ResolutionAspect aspect)
+{
+ AssertIf (aspect == kAspectOthers);
+ if (width == 0 || height == 0)
+ return false;
+ float val = float(width) / float(height);
+ return (Abs (val - kAspectRatioValues[aspect]) < 0.05f);
+}
+
+
+template<class TransferFunc>
+void AspectRatios::Transfer (TransferFunc& transfer)
+{
+ // Others is at index 0 (so we can easily add more ratios later),
+ // but we want to display it last in the UI. So serialize all first, then
+ // "Others" one
+ for (int i = 1; i < kAspectCount; ++i)
+ {
+ transfer.Transfer (m_Ratios[i], kAspectRatioSerializeNames[i]);
+ }
+ transfer.Transfer (m_Ratios[0], kAspectRatioSerializeNames[0]);
+ transfer.Align ();
+}
+
+
+bool PlayerSettings::DoesSupportResolution (int width, int height) const
+{
+ // check if matches any enabled ratio except "Others"
+ bool didSupportAnyRatio = false;
+ for (int i = 1; i < kAspectCount; ++i)
+ {
+ if (DoesMatchAspectRatio (width, height, static_cast<ResolutionAspect>(i)))
+ {
+ didSupportAnyRatio = true;
+ if (m_SupportedAspectRatios.m_Ratios[i])
+ return true;
+ }
+ }
+ // if "Others" is enabled and we did not support any of standard aspects -
+ // then we support this weird aspect
+ if (!didSupportAnyRatio && m_SupportedAspectRatios.m_Ratios[kAspectOthers])
+ return true;
+
+ return false;
+}
+
+
+// ----------------------------------------------------------------------
+
+PlayerSettings::PlayerSettings (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_RenderingPath(kRenderPathForward)
+, m_MobileRenderingPath(kRenderPathForward)
+, m_ActiveColorSpace(kGammaColorSpace)
+, m_MTRendering(true)
+, m_MobileMTRendering(false)
+, m_UseDX11(false)
+{
+ #if UNITY_EDITOR
+ companyName = GetDefaultCompanyName ();
+ productName = GetDefaultProductName ();
+ unityRebuildLibraryVersion = 0;
+ unityForwardCompatibleVersion = UNITY_FORWARD_COMPATIBLE_VERSION;
+ unityStandardAssetsVersion = 0;
+ m_EditorOnlyNotPersistent.CurrentColorSpace = (ColorSpace) m_ActiveColorSpace;
+ #endif
+
+ iPhoneBundleIdentifier = "com.Company.ProductName";
+
+ defaultIsFullScreen = true;
+ defaultIsNativeResolution = true;
+ displayResolutionDialog = kShowResolutionDialog;
+ defaultScreenWidth = 1024;
+ defaultScreenHeight = 768;
+ defaultWebScreenWidth = 960;
+ defaultWebScreenHeight = 600;
+ firstStreamedLevelWithResources = 0;
+ androidProfiler = false;
+
+ defaultScreenOrientation = 0;
+ uiAutoRotateToPortrait = uiAutoRotateToPortraitUpsideDown = uiAutoRotateToLandscapeLeft = uiAutoRotateToLandscapeRight = true;
+ uiUseAnimatedAutoRotation = true;
+ uiUse32BitDisplayBuffer = true;
+ uiUse24BitDepthBuffer = true;
+ iosShowActivityIndicatorOnLoading = -1;
+ androidShowActivityIndicatorOnLoading = -1;
+
+ runInBackground = false;
+ captureSingleScreen = false;
+ targetDevice = 2;
+ targetGlesGraphics = 1;
+ targetResolution = 0;
+ accelerometerFrequency = 60;
+ overrideIPodMusic = false;
+ prepareIOSForRecording = false;
+ enableHWStatistics = true;
+ usePlayerLog = true;
+ stripPhysics = false;
+ forceSingleInstance = false;
+ resizableWindow = false;
+ useMacAppStoreValidation = false;
+ macFullscreenMode = kFullscreenWindowWithMenuBarAndDock;
+ gpuSkinning = false;
+ xboxPIXTextureCapture = false;
+ xboxEnableAvatar = false;
+ xboxEnableKinect = false;
+ xboxEnableKinectAutoTracking = false;
+ xboxSpeechDB = 0;
+ xboxEnableFitness = false;
+ xboxEnableHeadOrientation = false;
+ xboxEnableGuest = false;
+
+ wiiHio2Usage = -1;
+ wiiLoadingScreenRectPlacement = 0;
+ wiiLoadingScreenBackground = ColorRGBAf(1.0f, 1.0f, 1.0f, 1.0f);
+ wiiLoadingScreenPeriod = 1000;
+ wiiLoadingScreenFileName = "";
+ wiiLoadingScreenRect = Rectf(0.0f, 0.0f, 0.0f, 0.0f);
+}
+
+void PlayerSettings::InitializeClass()
+{
+ // 2.x -> 3.0 compatibility
+ RegisterAllowNameConversion("PlayerSettings", "defaultWebScreenWidth", "defaultScreenWidthWeb");
+ RegisterAllowNameConversion("PlayerSettings", "defaultWebScreenHeight", "defaultScreenHeightWeb");
+}
+
+void PlayerSettings::PostInitializeClass ()
+{
+ // This will help reset the cursors during a dynamic reload.
+ // For most platforms, this will happen only during initialization,
+ // the manager pointer will be null and this will no-op.
+ if (GetPlayerSettingsPtr ())
+ GetPlayerSettings ().InitDefaultCursors();
+}
+
+
+PlayerSettings::~PlayerSettings ()
+{
+}
+
+void PlayerSettings::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_RenderingPath = clamp (m_RenderingPath, 0, kRenderPathCount-1);
+ m_ActiveColorSpace = clamp (m_ActiveColorSpace, 0, kMaxColorSpace-1);
+}
+
+
+bool PlayerSettings::GetAutoRotationAllowed(int orientation) const
+{
+ switch(orientation)
+ {
+ case 0: return uiAutoRotateToPortrait;
+ case 1: return uiAutoRotateToPortraitUpsideDown;
+ case 2: return uiAutoRotateToLandscapeRight;
+ case 3: return uiAutoRotateToLandscapeLeft;
+
+ default:
+ ErrorString("orientation out of range");
+ return false;
+ }
+}
+
+void PlayerSettings::SetAutoRotationAllowed(int orientation, bool enabled)
+{
+ switch(orientation)
+ {
+ case 0: uiAutoRotateToPortrait = enabled; break;
+ case 1: uiAutoRotateToPortraitUpsideDown = enabled; break;
+ case 2: uiAutoRotateToLandscapeRight = enabled; break;
+ case 3: uiAutoRotateToLandscapeLeft = enabled; break;
+
+ default:
+ ErrorString("orientation out of range, ignoring");
+ break;
+ }
+
+ bool somethingAllowed = uiAutoRotateToPortrait
+ || uiAutoRotateToPortraitUpsideDown
+ || uiAutoRotateToLandscapeRight
+ || uiAutoRotateToLandscapeLeft;
+
+ if( defaultScreenOrientation == 4 && !somethingAllowed )
+ {
+ ErrorString("all orientations are disabled for auto-rotation. Enabling Portrait");
+ uiAutoRotateToPortrait = true;
+ }
+}
+
+void PlayerSettings::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ #if UNITY_EDITOR
+ // product name and identifier can't be empty strings
+ if (companyName.empty ())
+ companyName = GetDefaultCompanyName ();
+ if (productName.empty () || productName == "UnityPlayer")
+ productName = GetDefaultProductName ();
+
+ // Perform color space conversion / cursor init only when applying a setting in the inspector.
+ if (awakeMode == kDefaultAwakeFromLoad && GetPlayerSettingsPtr() == this)
+ {
+ //only perform color space conversion if it was actually changed.
+ if ( GetEditorOnlyNotPersistent().CurrentColorSpace != GetValidatedColorSpace() ) {
+ ColorSpaceLiveSwitch();
+ GetEditorOnlyNotPersistent().CurrentColorSpace = GetValidatedColorSpace();
+ }
+ InitDefaultCursors ();
+ }
+ #else
+ // Try to set the default cursor. We can only do so if we actually have a GFX device
+ // which is not the case when we are currently still starting up (the player settings
+ // are read before the graphics system is initialized).
+ if((awakeMode == kDidLoadThreaded || awakeMode == kDidLoadFromDisk) && IsGfxDevice ())
+ {
+ InitDefaultCursors();
+ }
+ #endif
+}
+
+void PlayerSettings::InitDefaultCursors ()
+{
+ Assert (IsGfxDevice ());
+ Cursors::InitializeCursors (defaultCursor, cursorHotspot);
+}
+
+template<class TransferFunction>
+void PlayerSettings::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion (2);
+
+ transfer.Transfer (androidProfiler, "AndroidProfiler");
+ transfer.Align();
+
+ transfer.Transfer (defaultScreenOrientation, "defaultScreenOrientation");
+
+ transfer.Transfer (targetDevice, "targetDevice");
+ transfer.Transfer (targetGlesGraphics, "targetGlesGraphics");
+
+ transfer.Transfer (targetResolution, "targetResolution");
+ if(targetResolution == 1)
+ targetResolution = 6; // SD -> x0.5
+ else if(targetResolution == 2)
+ targetResolution = 0; // HD -> Native
+
+ transfer.Transfer (accelerometerFrequency, "accelerometerFrequency");
+
+ transfer.Align();
+
+ transfer.Transfer (companyName, "companyName");
+ transfer.Transfer (productName, "productName");
+
+ transfer.Transfer (defaultCursor, "defaultCursor");
+ transfer.Transfer (cursorHotspot, "cursorHotspot");
+
+ transfer.Transfer (defaultScreenWidth, "defaultScreenWidth");
+ transfer.Transfer (defaultScreenHeight, "defaultScreenHeight");
+ transfer.Transfer (defaultWebScreenWidth, "defaultScreenWidthWeb");
+ transfer.Transfer (defaultWebScreenHeight, "defaultScreenHeightWeb");
+ TRANSFER (m_RenderingPath);
+ TRANSFER (m_MobileRenderingPath);
+ TRANSFER (m_ActiveColorSpace);
+ TRANSFER (m_MTRendering);
+ TRANSFER (m_MobileMTRendering);
+ TRANSFER (m_UseDX11);
+
+ transfer.Align();
+
+ transfer.Transfer (iosShowActivityIndicatorOnLoading, "iosShowActivityIndicatorOnLoading");
+ transfer.Transfer (androidShowActivityIndicatorOnLoading, "androidShowActivityIndicatorOnLoading");
+
+ transfer.Transfer (displayResolutionDialog, "displayResolutionDialog");
+
+ transfer.Transfer (uiAutoRotateToPortrait, "allowedAutorotateToPortrait");
+ transfer.Transfer (uiAutoRotateToPortraitUpsideDown, "allowedAutorotateToPortraitUpsideDown");
+ transfer.Transfer (uiAutoRotateToLandscapeRight, "allowedAutorotateToLandscapeRight");
+ transfer.Transfer (uiAutoRotateToLandscapeLeft, "allowedAutorotateToLandscapeLeft");
+ transfer.Transfer (uiUseAnimatedAutoRotation, "useOSAutorotation");
+ transfer.Transfer (uiUse32BitDisplayBuffer, "use32BitDisplayBuffer");
+ transfer.Transfer (uiUse24BitDepthBuffer, "use24BitDepthBuffer");
+ transfer.Align();
+
+ transfer.Transfer (defaultIsFullScreen, "defaultIsFullScreen");
+ transfer.Transfer (defaultIsNativeResolution, "defaultIsNativeResolution");
+ transfer.Transfer (runInBackground, "runInBackground");
+ transfer.Transfer (captureSingleScreen, "captureSingleScreen");
+
+ transfer.Transfer (overrideIPodMusic, "Override IPod Music");
+ transfer.Transfer (prepareIOSForRecording, "Prepare IOS For Recording");
+ transfer.Transfer (enableHWStatistics, "enableHWStatistics");
+ TRANSFER (usePlayerLog);
+ TRANSFER (stripPhysics);
+#if ENABLE_SINGLE_INSTANCE_BUILD_SETTING
+ TRANSFER (forceSingleInstance);
+#endif
+ TRANSFER (resizableWindow);
+ TRANSFER (useMacAppStoreValidation);
+ TRANSFER (gpuSkinning);
+ TRANSFER (xboxPIXTextureCapture);
+ TRANSFER (xboxEnableAvatar);
+ TRANSFER (xboxEnableKinect);
+ TRANSFER (xboxEnableKinectAutoTracking);
+ TRANSFER (xboxEnableFitness);
+ transfer.Align();
+ TRANSFER (macFullscreenMode);
+ TRANSFER (xboxSpeechDB);
+ TRANSFER (xboxEnableHeadOrientation);
+ TRANSFER (xboxEnableGuest);
+ transfer.Align();
+
+ TRANSFER (wiiHio2Usage);
+ TRANSFER (wiiLoadingScreenRectPlacement);
+ TRANSFER (wiiLoadingScreenBackground);
+ TRANSFER (wiiLoadingScreenPeriod);
+ TRANSFER (wiiLoadingScreenFileName);
+ TRANSFER (wiiLoadingScreenRect);
+
+
+ TRANSFER (m_SupportedAspectRatios);
+
+ transfer.Transfer (iPhoneBundleIdentifier, "iPhoneBundleIdentifier");
+
+
+#if UNITY_EDITOR
+ if (!transfer.IsSerializingForGameRelease())
+ {
+ m_EditorOnly.Transfer(transfer);
+
+ /////@TODO: GET RID OF THIS TOO
+
+ transfer.Transfer (firstStreamedLevelWithResources, "firstStreamedLevelWithResources");
+ transfer.Transfer (unityRebuildLibraryVersion, "unityRebuildLibraryVersion", kHideInEditorMask);
+ transfer.Transfer (unityForwardCompatibleVersion, "unityForwardCompatibleVersion", kHideInEditorMask);
+ transfer.Transfer (unityStandardAssetsVersion, "unityStandardAssetsVersion", kHideInEditorMask);
+ }
+#endif
+}
+
+ColorSpace PlayerSettings::GetValidatedColorSpace () const
+{
+ // On xBox, don't check GraphicsCaps, because we need to know the color space when setting up
+ // front/back buffers, at which point GraphicsCaps are not initialized. We know that Xenon supports
+ // sRGB.
+ if (gGraphicsCaps.hasSRGBReadWrite || UNITY_XENON)
+ return static_cast<ColorSpace>(m_ActiveColorSpace);
+ else
+ return kGammaColorSpace;
+}
+
+RenderingPath PlayerSettings::GetRenderingPathRuntime()
+{
+#if UNITY_ANDROID || UNITY_BB10 || UNITY_IPHONE || UNITY_TIZEN
+ return GetMobileRenderingPath();
+#else
+ return GetRenderingPath();
+#endif
+}
+
+void PlayerSettings::SetRenderingPathRuntime(RenderingPath rp)
+{
+#if UNITY_ANDROID || UNITY_BB10 || UNITY_IPHONE || UNITY_TIZEN
+ SetMobileRenderingPath(rp);
+#else
+ SetRenderingPath(rp);
+#endif
+}
+
+bool PlayerSettings::GetMTRenderingRuntime()
+{
+#if UNITY_ANDROID || UNITY_BB10 || UNITY_IPHONE || UNITY_TIZEN
+ return GetMobileMTRendering();
+#else
+ return GetMTRendering();
+#endif
+}
+void PlayerSettings::SetMTRenderingRuntime(bool mtRendering)
+{
+#if UNITY_ANDROID || UNITY_BB10 || UNITY_IPHONE || UNITY_TIZEN
+ SetMobileMTRendering(mtRendering);
+#else
+ SetMTRendering(mtRendering);
+#endif
+}
+
+IMPLEMENT_CLASS_HAS_POSTINIT (PlayerSettings)
+IMPLEMENT_OBJECT_SERIALIZE (PlayerSettings)
+
+GET_MANAGER (PlayerSettings)
+GET_MANAGER_PTR (PlayerSettings)
+
diff --git a/Runtime/Misc/PlayerSettings.h b/Runtime/Misc/PlayerSettings.h
new file mode 100644
index 0000000..5e25d53
--- /dev/null
+++ b/Runtime/Misc/PlayerSettings.h
@@ -0,0 +1,458 @@
+#ifndef PROJECTSETTINGS_H
+#define PROJECTSETTINGS_H
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Camera/RenderLoops/RenderLoopEnums.h"
+#include "Runtime/Math/Vector2.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/EditorOnlyPlayerSettings.h"
+#endif
+
+class Texture2D;
+
+
+
+enum { kDisableResolutionDialog = 0, kShowResolutionDialog = 1, kShowResolutionDialogDefaultNone = 2 };
+
+enum ResolutionAspect
+{
+ kAspectOthers = 0,
+ kAspect4by3,
+ kAspect5by4,
+ kAspect16by10,
+ kAspect16by9,
+ kAspectCount
+};
+
+enum MacFullscreenMode
+{
+ kCaptureDisplay,
+ kFullscreenWindow,
+ kFullscreenWindowWithMenuBarAndDock,
+};
+
+struct AspectRatios
+{
+ DECLARE_SERIALIZE_NO_PPTR (AspectRatios)
+
+ bool m_Ratios[kAspectCount];
+
+ AspectRatios () { Reset (); }
+
+ void Reset()
+ {
+ for (int i = 0; i < kAspectCount; ++i)
+ m_Ratios[i] = true;
+ }
+};
+
+class PlayerSettings : public GlobalGameManager
+{
+public:
+
+ DECLARE_OBJECT_SERIALIZE (PlayerSettings)
+ REGISTER_DERIVED_CLASS (PlayerSettings, GlobalGameManager)
+ PlayerSettings (MemLabelId label, ObjectCreationMode mode);
+ // ~PlayerSettings (); declared-by-macro
+
+ virtual void CheckConsistency ();
+ void AwakeFromLoad (AwakeFromLoadMode mode);
+
+ bool DoesSupportResolution (int width, int height) const;
+ void InitDefaultCursors ();
+
+ static void InitializeClass ();
+ static void PostInitializeClass ();
+ static void CleanupClass () { }
+
+public:
+
+ UnityStr companyName;
+ UnityStr productName;
+ UnityStr version;
+
+ PPtr<Texture2D> defaultCursor;
+ Vector2f cursorHotspot;
+
+ bool androidProfiler;
+ int defaultScreenOrientation; ///< enum { Portrait = 0, Portrait Upside Down = 1, Landscape Right = 2, Landscape Left = 3, Auto Rotation = 4 }
+ int targetDevice; ///< enum { iPhone Only = 0, iPad Only = 1, iPhone + iPad = 2 }
+ int targetGlesGraphics; ///< enum { OpenGL ES 2.0 = 1, OpenGL ES 3.0 = 2 }
+ int targetResolution; ///< enum { Native (default device resolution) = 0, Auto (Best Performance) = 3, Auto (Best Quality) = 4, 320p (iPhone) = 5, 640p (iPhone Retina Display) = 6, 768p (iPad) = 7 }
+ int accelerometerFrequency; ///< enum { Disabled = 0, 15 Hz = 15, 30 Hz = 30, 60 Hz = 60, 100 Hz = 100 }
+
+ // General
+ int defaultScreenWidth;
+ int defaultScreenHeight;
+
+ int defaultWebScreenWidth;
+ int defaultWebScreenHeight;
+
+ int displayResolutionDialog;///< enum { Disabled = 0, Enabled = 1, Hidden By Default = 2 }
+ AspectRatios m_SupportedAspectRatios;
+
+ int m_RenderingPath; ///< enum { Vertex Lit=0, Forward=1, Deferred Lighting=2 } Rendering path to use.
+ int m_MobileRenderingPath; ///< enum { Vertex Lit=0, Forward=1, Deferred Lighting=2 } Rendering path to use on Mobiles.
+ int m_ActiveColorSpace; ///< enum { Gamma, Linear }
+ bool m_MTRendering;
+ bool m_MobileMTRendering;
+ bool m_UseDX11;
+
+ // TODO: unify probably with ios, though we will ne handle it specifically with EnumPopup or something on editor side
+ // TODO: if someone from editor team wants it - go ahead
+ // values are taken from android.R.attr docs
+ int androidShowActivityIndicatorOnLoading; ///< enum { Don't Show=-1, Large=0, Inversed Large=1, Small=2, Inversed Small=3 }
+ int iosShowActivityIndicatorOnLoading; ///< enum { Don't Show=-1, White Large=0, White=1, Gray=2 }
+
+ // auto-rotation handling. Expose bools on editor side
+ bool uiAutoRotateToPortrait;
+ bool uiAutoRotateToPortraitUpsideDown;
+ bool uiAutoRotateToLandscapeRight;
+ bool uiAutoRotateToLandscapeLeft;
+ bool uiUseAnimatedAutoRotation;
+
+ bool uiUse32BitDisplayBuffer;
+ bool uiUse24BitDepthBuffer;
+
+ bool defaultIsFullScreen;
+ bool defaultIsNativeResolution;
+ bool runInBackground;
+ bool captureSingleScreen;
+
+ bool overrideIPodMusic; ///< Force mixing behavior allowing iPod audio to play with FMOD even if the audio session doesn't usually permit this.
+ bool prepareIOSForRecording; ///< If enabled, the AudioSession of iOS will be initialized in recording mode, thus avoiding delays when initializing the Microphone object.
+
+ bool enableHWStatistics;
+
+ bool usePlayerLog;
+ bool stripPhysics;
+ bool useMacAppStoreValidation;
+ int macFullscreenMode; ///< enum {CaptureDisplay=0, FullscreenWindow=1, FullscreenWindowWithMenuBarAndDock=2}
+ bool forceSingleInstance;
+ bool resizableWindow;
+ bool gpuSkinning;
+ bool xboxPIXTextureCapture; // Xbox360
+ bool xboxEnableAvatar; // Xbox360
+ bool xboxEnableKinect; // Xbox360 Kinect
+ bool xboxEnableKinectAutoTracking; // Xbox360 Kinect
+ UInt32 xboxSpeechDB;
+ bool xboxEnableFitness;
+ bool xboxEnableHeadOrientation;
+ bool xboxEnableGuest;
+
+ int wiiHio2Usage; ///< enum { None=-1, Profiler=0, Automation=1 } Wii Hio2 Usage.
+ int wiiLoadingScreenRectPlacement; ///< enum {TopLeft = 0, TopCenter = 1, TopRight = 2, MiddleLeft = 3, MiddleCenter = 4, MiddlerRight = 5, BottomLeft = 6, BottomCenter = 7, BottomRight = 8} Placement.
+ ColorRGBAf wiiLoadingScreenBackground;
+ int wiiLoadingScreenPeriod;
+ UnityStr wiiLoadingScreenFileName;
+ Rectf wiiLoadingScreenRect;
+ int firstStreamedLevelWithResources;
+
+ UnityStr absoluteURL;// Web player
+ UnityStr srcValue;// Web player
+
+ UnityStr iPhoneBundleIdentifier;
+
+ UnityStr AndroidLicensePublicKey;
+
+ #if UNITY_EDITOR
+ EditorOnlyPlayerSettings m_EditorOnly;
+ EditorOnlyPlayerSettingsNotPersistent m_EditorOnlyNotPersistent;
+
+ ////////@TODO: Move this into a seperate manager!
+ int unityRebuildLibraryVersion;
+ int unityForwardCompatibleVersion;
+ int unityStandardAssetsVersion;
+ #endif
+
+public:
+
+ std::string GetCompanyName () const { return companyName; }
+ void SetCompanyName (std::string value) { companyName = value; SetDirty(); }
+
+ std::string GetProductName () const { return productName; }
+ void SetProductName (std::string value) { productName = value; SetDirty();}
+
+ int GetTargetDevice () const { return targetDevice; }
+ void SetTargetDevice (int value) { targetDevice = value; SetDirty();}
+
+ PPtr<Texture2D> GetDefaultCursor () const { return defaultCursor; }
+ void SetDefaultCursor (PPtr<Texture2D> value) { defaultCursor = value; SetDirty();}
+
+ Vector2f GetCursorHotspot () const { return cursorHotspot; }
+ void SetCursorHotspot (Vector2f value) { cursorHotspot = value; SetDirty();}
+
+ int GetTargetGlesGraphics () const { return targetGlesGraphics; }
+ void SetTargetGlesGraphics (int value) { targetGlesGraphics = value; SetDirty(); }
+
+ int GetTargetResolution () const { return targetResolution; }
+ void SetTargetResolution (int value) { targetResolution = value; SetDirty();}
+
+ int GetAccelerometerFrequency () const { return accelerometerFrequency; }
+ void SetAccelerometerFrequency (int value) { accelerometerFrequency = value; SetDirty();}
+
+ int GetDefaultScreenOrientation () const { return defaultScreenOrientation; }
+ void SetDefaultScreenOrientation (int value) { defaultScreenOrientation = value; SetDirty();}
+
+ bool GetUseAnimatedAutoRotation () const { return uiUseAnimatedAutoRotation; }
+ void SetUseAnimatedAutoRotation (bool value) { uiUseAnimatedAutoRotation = value; SetDirty();}
+
+ bool GetAutoRotationAllowed(int orientation) const;
+ void SetAutoRotationAllowed(int orientation, bool enabled);
+
+ bool GetUse32BitDisplayBuffer() const { return uiUse32BitDisplayBuffer; }
+ void SetUse32BitDisplayBuffer(bool use) { uiUse32BitDisplayBuffer = use; }
+
+ bool GetUse24BitDepthBuffer() const { return uiUse24BitDepthBuffer; }
+ void SetUse24BitDepthBuffer(bool use) { uiUse24BitDepthBuffer = use; }
+
+ int GetIOSShowActivityIndicatorOnLoading() const { return iosShowActivityIndicatorOnLoading; }
+ void SetIOSShowActivityIndicatorOnLoading(int mode) { iosShowActivityIndicatorOnLoading = mode; SetDirty();}
+ int GetAndroidShowActivityIndicatorOnLoading() const { return androidShowActivityIndicatorOnLoading; }
+ void SetAndroidShowActivityIndicatorOnLoading(int mode) { androidShowActivityIndicatorOnLoading = mode; SetDirty();}
+
+ int GetDefaultScreenWidth () const { return defaultScreenWidth; }
+ void SetDefaultScreenWidth (int value) { defaultScreenWidth = value; SetDirty();}
+
+ int GetDefaultScreenHeight () const { return defaultScreenHeight; }
+ void SetDefaultScreenHeight (int value) { defaultScreenHeight = value; SetDirty();}
+
+ int GetDefaultWebScreenWidth () const { return defaultWebScreenWidth; }
+ void SetDefaultWebScreenWidth (int value) { defaultWebScreenWidth = value; SetDirty();}
+
+ int GetDefaultWebScreenHeight () const { return defaultWebScreenHeight; }
+ void SetDefaultWebScreenHeight (int value) { this->defaultWebScreenHeight = value; SetDirty();}
+
+ int GetDisplayResolutionDialog () const { return displayResolutionDialog; }
+ void SetDisplayResolutionDialog (int value) { this->displayResolutionDialog = value; SetDirty();}
+
+ bool AspectRatioEnabled (int aspectRatio) const { return m_SupportedAspectRatios.m_Ratios [aspectRatio]; }
+ void SetAspectRatio (int aspectRatio, bool enabled) { m_SupportedAspectRatios.m_Ratios[aspectRatio] = enabled; SetDirty();}
+
+ bool GetDefaultIsFullScreen () const { return defaultIsFullScreen; }
+ void SetDefaultIsFullScreen (bool value) { defaultIsFullScreen = value; SetDirty(); }
+
+ bool GetDefaultIsNativeResolution () const { return defaultIsNativeResolution; }
+ void SetDefaultIsNativeResolution (bool value) { defaultIsNativeResolution = value; SetDirty(); }
+
+ bool GetRunInBackground () const { return runInBackground; }
+ void SetRunInBackground (bool value) { runInBackground = value; SetDirty(); }
+
+ bool GetStripPhysics () const { return stripPhysics; }
+ void SetStripPhysics (bool value) { stripPhysics = value; SetDirty(); }
+
+ bool GetUsePlayerLog () const { return usePlayerLog; }
+ void SetUsePlayerLog (bool value) { usePlayerLog = value; SetDirty(); }
+
+ bool GetEnableHWStatistics () const { return enableHWStatistics; }
+ void SetEnableHWStatistics (bool value) { enableHWStatistics = value; SetDirty(); }
+
+ bool GetUseMacAppStoreValidation () const { return useMacAppStoreValidation; }
+ void SetUseMacAppStoreValidation (bool value) { useMacAppStoreValidation = value; SetDirty(); }
+
+ int GetMacFullscreenMode () const { return macFullscreenMode; }
+ void SetMacFullscreenMode (int value) { macFullscreenMode = value; SetDirty(); }
+
+ bool GetForceSingleInstance () const { return forceSingleInstance; }
+ void SetForceSingleInstance (bool value) { forceSingleInstance = value; SetDirty(); }
+
+ bool GetResizableWindow () const { return resizableWindow; }
+ void SetResizableWindow (bool value) { resizableWindow = value; SetDirty(); }
+
+ bool GetGPUSkinning() const { return gpuSkinning; }
+ void SetGPUSkinning(bool value) { gpuSkinning = value; SetDirty(); }
+
+ bool GetXboxPIXTextureCapture() const { return xboxPIXTextureCapture; }
+ void SetXboxPIXTextureCapture(bool value) { xboxPIXTextureCapture = value; SetDirty(); }
+
+ bool GetXboxEnableAvatar() const { return xboxEnableAvatar; }
+ void SetXboxEnableAvatar(bool value) { xboxEnableAvatar = value; SetDirty(); }
+ bool GetXboxEnableGuest() const { return xboxEnableGuest; }
+ void SetXboxEnableGuest(bool value) { xboxEnableGuest = value; SetDirty(); }
+
+ bool GetXboxEnableKinect() const { return xboxEnableKinect; }
+ void SetXboxEnableKinect(bool value) { xboxEnableKinect = value; SetDirty(); }
+ bool GetXboxEnableKinectAutoTracking() const { return xboxEnableKinectAutoTracking; }
+ void SetXboxEnableKinectAutoTracking(bool value) { xboxEnableKinectAutoTracking = value; SetDirty(); }
+ UInt32 GetXboxSpeechDB() const { return xboxSpeechDB; }
+ void SetXboxSpeechDB(UInt32 value) { xboxSpeechDB = value; SetDirty(); }
+ bool GetXboxEnableFitness() const { return xboxEnableFitness; }
+ void SetXboxEnableFitness(bool value) { xboxEnableFitness = value; SetDirty(); }
+ bool GetXboxHeadOrientation() const { return xboxEnableHeadOrientation; }
+ void SetXboxHeadOrientation(bool value) { xboxEnableHeadOrientation = value; SetDirty(); }
+
+ bool GetCaptureSingleScreen () const { return captureSingleScreen; }
+ void SetCaptureSingleScreen (bool value) { captureSingleScreen = value; SetDirty(); }
+
+ int GetFirstStreamedLevelWithResources () const { return firstStreamedLevelWithResources; }
+ void SetFirstStreamedLevelWithResources (int value) { this->firstStreamedLevelWithResources = value; SetDirty(); }
+
+ void SetRenderingPath (RenderingPath rp) { m_RenderingPath = rp; SetDirty(); }
+ RenderingPath GetRenderingPath() const { return static_cast<RenderingPath>(m_RenderingPath); }
+
+ void SetMobileRenderingPath (RenderingPath rp) { m_MobileRenderingPath = rp; SetDirty(); }
+ RenderingPath GetMobileRenderingPath() const { return static_cast<RenderingPath>(m_MobileRenderingPath); }
+
+ RenderingPath GetRenderingPathRuntime();
+ void SetRenderingPathRuntime(RenderingPath rp);
+
+ void SetDesiredColorSpace (ColorSpace colorSpace) { m_ActiveColorSpace = colorSpace; SetDirty();}
+ ColorSpace GetDesiredColorSpace () const { return static_cast<ColorSpace> (m_ActiveColorSpace); }
+ ColorSpace GetValidatedColorSpace () const ;
+
+ void SetMTRendering (bool mtRendering) { m_MTRendering = mtRendering; SetDirty(); }
+ bool GetMTRendering () const { return m_MTRendering; }
+
+ void SetMobileMTRendering (bool mtRendering) { m_MobileMTRendering = mtRendering; SetDirty(); }
+ bool GetMobileMTRendering () const { return m_MobileMTRendering; }
+
+ bool GetMTRenderingRuntime();
+ void SetMTRenderingRuntime(bool mtRendering);
+
+
+ void SetUseDX11 (bool v) { m_UseDX11 = v; SetDirty(); }
+ bool GetUseDX11 () const { return m_UseDX11; }
+
+ std::string GetiPhoneBundleIdentifier () const { return iPhoneBundleIdentifier; }
+ void SetiPhoneBundleIdentifier (const std::string& value) { iPhoneBundleIdentifier = value; SetDirty();}
+
+ #if UNITY_EDITOR
+
+ int GetAPICompatibilityLevel () const { return m_EditorOnly.apiCompatibilityLevel; }
+ void SetAPICompatibilityLevel (int value) { m_EditorOnly.apiCompatibilityLevel = value; SetDirty();}
+
+ bool GetStripUnusedMeshComponents() const { return m_EditorOnly.stripUnusedMeshComponents; }
+ void SetStripUnusedMeshComponents (bool v) { m_EditorOnly.stripUnusedMeshComponents = v; SetDirty(); }
+
+ std::string GetAotOptions () const { return m_EditorOnly.aotOptions; }
+ void SetAotOptions (std::string value) { m_EditorOnly.aotOptions = value; SetDirty();}
+
+ std::string GetAndroidKeystoreName () const { return m_EditorOnly.AndroidKeystoreName; }
+ void SetAndroidKeystoreName (std::string value) { m_EditorOnly.AndroidKeystoreName = value; SetDirty();}
+
+ std::string GetAndroidKeystorePass () const { return m_EditorOnlyNotPersistent.AndroidKeystorePass; }
+ void SetAndroidKeystorePass (std::string value) { m_EditorOnlyNotPersistent.AndroidKeystorePass = value; SetDirty();}
+
+ std::string GetAndroidKeyaliasName () const { return m_EditorOnly.AndroidKeyaliasName; }
+ void SetAndroidKeyaliasName (std::string value) { m_EditorOnly.AndroidKeyaliasName = value; SetDirty();}
+
+ std::string GetAndroidKeyaliasPass () const { return m_EditorOnlyNotPersistent.AndroidKeyaliasPass; }
+ void SetAndroidKeyaliasPass (std::string value) { m_EditorOnlyNotPersistent.AndroidKeyaliasPass = value; SetDirty();}
+
+ bool GetAndroidLicenseVerification () const { return !AndroidLicensePublicKey.empty(); }
+
+ int GetUseAPKExpansionFiles () const { return m_EditorOnly.enableAndroidExpansionFiles; }
+ void SetUseAPKExpansionFiles (int value) { m_EditorOnly.enableAndroidExpansionFiles = value; SetDirty();}
+
+ std::string GetiPhoneBundleVersion () const { return m_EditorOnly.iPhoneBundleVersion; }
+ void SetiPhoneBundleVersion (std::string value) { m_EditorOnly.iPhoneBundleVersion = value; SetDirty();}
+
+ int GetiPhoneStrippingLevel () const { return m_EditorOnly.iPhoneStrippingLevel; }
+ void SetiPhoneStrippingLevel (int value) { m_EditorOnly.iPhoneStrippingLevel = value; SetDirty();}
+
+ int GetAndroidBundleVersionCode () const { return m_EditorOnly.AndroidBundleVersionCode; }
+ void SetAndroidBundleVersionCode (int value) { m_EditorOnly.AndroidBundleVersionCode = value; SetDirty();}
+
+ int GetAndroidMinSdkVersion () const { return m_EditorOnly.AndroidMinSdkVersion; }
+ void SetAndroidMinSdkVersion (int value) { m_EditorOnly.AndroidMinSdkVersion = value; SetDirty();}
+
+ int GetAndroidPreferredInstallLocation () const { return m_EditorOnly.AndroidPreferredInstallLocation; }
+ void SetAndroidPreferredInstallLocation (int value) { m_EditorOnly.AndroidPreferredInstallLocation = value; SetDirty();}
+
+ int GetForceAndroidInternetPermission () const { return m_EditorOnly.forceAndroidInternetPermission; }
+ void SetForceAndroidInternetPermission (int value) { m_EditorOnly.forceAndroidInternetPermission = value; SetDirty();}
+
+ int GetForceAndroidSDCardPermission () const { return m_EditorOnly.forceAndroidSDCardPermission; }
+ void SetForceAndroidSDCardPermission (int value) { m_EditorOnly.forceAndroidSDCardPermission = value; SetDirty();}
+
+ int GetCreateAndroidWallpaper () const { return m_EditorOnly.createAndroidWallpaper; }
+ void SetCreateAndroidWallpaper (int value) { m_EditorOnly.createAndroidWallpaper = value; SetDirty();}
+
+ int GetAndroidTargetDevice () const { return m_EditorOnly.AndroidTargetDevice; }
+ void SetAndroidTargetDevice (int value) { m_EditorOnly.AndroidTargetDevice = value; SetDirty();}
+
+ int GetAndroidSplashScreenScale () const { return m_EditorOnly.AndroidSplashScreenScale; }
+ void SetAndroidSplashScreenScale (int value) { m_EditorOnly.AndroidSplashScreenScale = value; SetDirty();}
+
+ std::string GetBlackBerryDeviceAddress () { return m_EditorOnly.blackberryDeviceAddress; }
+ void SetBlackBerryDeviceAddress (std::string value) { m_EditorOnly.blackberryDeviceAddress = value; SetDirty();}
+
+ std::string GetBlackBerryDevicePassword () { return m_EditorOnly.blackberryDevicePassword; }
+ void SetBlackBerryDevicePassword (std::string value) { m_EditorOnly.blackberryDevicePassword = value; SetDirty();}
+
+ std::string GetTizenProductDescription () { return m_EditorOnly.tizenProductDescription; }
+ void SetTizenProductDescription (std::string value) { m_EditorOnly.tizenProductDescription = value; SetDirty();}
+
+ std::string GetTizenProductURL () { return m_EditorOnly.tizenProductURL; }
+ void SetTizenProductURL (std::string value) { m_EditorOnly.tizenProductURL = value; SetDirty();}
+
+ std::string GetTizenCertificatePath () { return m_EditorOnly.tizenCertificatePath; }
+ void SetTizenCertificatePath (std::string value) { m_EditorOnly.tizenCertificatePath = value; SetDirty();}
+
+ std::string GetTizenCertificatePassword () { return m_EditorOnly.tizenCertificatePassword; }
+ void SetTizenCertificatePassword (std::string value) { m_EditorOnly.tizenCertificatePassword = value; SetDirty();}
+
+ int GetiPhoneScriptCallOptimization () const { return m_EditorOnly.iPhoneScriptCallOptimization; }
+ void SetiPhoneScriptCallOptimization (int value) { m_EditorOnly.iPhoneScriptCallOptimization = value; SetDirty();}
+
+ int GetiPhoneSdkVersion () const { return m_EditorOnly.iPhoneSdkVersion; }
+ void SetiPhoneSdkVersion (int value) { m_EditorOnly.iPhoneSdkVersion = value; SetDirty();}
+
+ int GetiPhoneTargetOSVersion () const { return m_EditorOnly.iPhoneTargetOSVersion; }
+ void SetiPhoneTargetOSVersion (int value) { m_EditorOnly.iPhoneTargetOSVersion = value; SetDirty();}
+
+ int GetUIPrerenderedIcon () const { return m_EditorOnly.uIPrerenderedIcon; }
+ void SetUIPrerenderedIcon (int value) { m_EditorOnly.uIPrerenderedIcon = value; SetDirty();}
+
+ int GetUIRequiresPersistentWiFi () const { return m_EditorOnly.uIRequiresPersistentWiFi; }
+ void SetUIRequiresPersistentWiFi (int value) { m_EditorOnly.uIRequiresPersistentWiFi = value; SetDirty();}
+
+ int GetUIStatusBarHidden () const { return m_EditorOnly.uIStatusBarHidden; }
+ void SetUIStatusBarHidden (int value) { m_EditorOnly.uIStatusBarHidden = value; SetDirty();}
+
+ int GetUIStatusBarStyle () const { return m_EditorOnly.uIStatusBarStyle; }
+ void SetUIStatusBarStyle (int value) { m_EditorOnly.uIStatusBarStyle = value; SetDirty();}
+
+ int GetUIExitOnSuspend () const { return m_EditorOnly.uIExitOnSuspend; }
+ void SetUIExitOnSuspend (int value) { m_EditorOnly.uIExitOnSuspend = value; SetDirty();}
+
+ PPtr <Texture2D> GetResolutionDialogBanner () const { return m_EditorOnly.resolutionDialogBanner; }
+ void SetResolutionDialogBanner (PPtr <Texture2D> value) { m_EditorOnly.resolutionDialogBanner = value; SetDirty(); }
+
+ std::vector<int> GetPlatformIconSizes (const std::string& platform) { return m_EditorOnly.GetPlatformIconSizes(platform); }
+ std::vector<PPtr<Texture2D> > GetPlatformIcons (const std::string& platform) { return m_EditorOnly.GetPlatformIcons(platform); }
+ void SetPlatformIcons(const std::string& platform, std::vector<PPtr<Texture2D> > icons) { if (m_EditorOnly.SetPlatformIcons(platform, icons)) SetDirty(); }
+
+ Texture2D* GetPlatformIconForSize (const std::string& platform, int size) { return m_EditorOnly.GetPlatformIconForSize(platform, size); }
+
+ void GetPlatformBatching (BuildTargetPlatform platform, bool* outStaticBatching, bool* outDynamicBatching) const { m_EditorOnly.GetPlatformBatching(platform, outStaticBatching, outDynamicBatching); }
+ void SetPlatformBatching (BuildTargetPlatform platform, bool staticBatching, bool dynamicBatching) { m_EditorOnly.SetPlatformBatching(platform, staticBatching, dynamicBatching); SetDirty(); }
+
+ void SetWebPlayerTemplate (UnityStr value) { if (!m_EditorOnly.m_WebPlayerTemplate.compare(value)) { m_EditorOnly.m_WebPlayerTemplate = value; SetDirty (); } };
+ void SetTemplateCustomKeys (std::vector<std::string> keys) { m_EditorOnly.SetTemplateCustomKeys(keys); SetDirty(); }
+ void SetTemplateCustomValue (std::string key, std::string value) { if (m_EditorOnly.SetTemplateCustomValue(key, value)) SetDirty(); }
+
+ std::vector<std::string> GetTemplateCustomKeys () { return m_EditorOnly.GetTemplateCustomKeys(); }
+ std::string GetWebPlayerTemplate () const { return m_EditorOnly.m_WebPlayerTemplate; }
+ std::string GetTemplateCustomValue (std::string key) { return m_EditorOnly.GetTemplateCustomValue(key); }
+
+ const EditorOnlyPlayerSettings& GetEditorOnly () const { return m_EditorOnly; }
+
+ ///Always marks as dirty
+ EditorOnlyPlayerSettings& GetEditorOnlyForUpdate () { SetDirty(); return m_EditorOnly; }
+ EditorOnlyPlayerSettingsNotPersistent& GetEditorOnlyNotPersistent () { return m_EditorOnlyNotPersistent; }
+
+private:
+ std::vector<EditorOnlyPlayerSettings::IconWithSize> GetBestIconWithSizes (const std::string& platform);
+
+ #endif
+};
+
+PlayerSettings& GetPlayerSettings ();
+PlayerSettings* GetPlayerSettingsPtr ();
+
+#endif
diff --git a/Runtime/Misc/Plugins.cpp b/Runtime/Misc/Plugins.cpp
new file mode 100644
index 0000000..1271c31
--- /dev/null
+++ b/Runtime/Misc/Plugins.cpp
@@ -0,0 +1,231 @@
+#include "UnityPrefix.h"
+#include "Plugins.h"
+
+#if UNITY_PLUGINS_AVAILABLE
+
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#if UNITY_WIN
+#include "PlatformDependent/Win/PathUnicodeConversion.h"
+#endif
+#if UNITY_OSX || UNITY_LINUX
+#include <dlfcn.h>
+#endif
+#if UNITY_XENON
+#include "Runtime/Mono/MonoIncludes.h"
+#endif
+
+
+const char* FindPluginExecutable (const char* pluginName); // implemented in platform dependent
+
+
+
+// --------------------------------------------------------------------------
+
+static void* LoadPluginExecutable (const char* pluginPath)
+{
+ #if UNITY_WINRT
+ std::wstring widePath;
+ ConvertUnityPathName(pluginPath, widePath);
+ return (void*)LoadPackagedLibrary (widePath.c_str(), 0);
+ #elif (UNITY_WIN && !UNITY_WINRT)
+ std::wstring widePath;
+ ConvertUnityPathName(pluginPath, widePath);
+ return (void*)LoadLibraryW (widePath.c_str());
+
+ #elif UNITY_OSX || UNITY_LINUX
+ return dlopen (pluginPath, RTLD_NOW);
+
+ #elif UNITY_XENON
+ return cached_module_load(pluginPath, MONO_DL_LAZY, 0);
+
+ #else
+ return NULL;
+
+ #endif
+}
+
+static void UnloadPluginExecutable (void* pluginHandle)
+{
+#if (UNITY_WIN || UNITY_WINRT)
+ FreeLibrary ((HMODULE) pluginHandle);
+
+#elif UNITY_OSX || UNITY_LINUX
+ dlclose (pluginHandle);
+
+#else
+ // We don't unload on other platforms. Only really relevant to editor
+ // anyway.
+#endif
+}
+
+static void* LoadPluginFunction (void* pluginHandle, const char* name)
+{
+ #if (UNITY_WIN || UNITY_WINRT)
+ return GetProcAddress ((HMODULE)pluginHandle, name);
+
+ #elif UNITY_OSX || UNITY_LINUX
+ return dlsym (pluginHandle, name);
+
+ #elif UNITY_XENON
+ void* symbol = 0;
+ char* err = mono_dl_symbol((MonoDl*)pluginHandle, name, &symbol);
+ g_free(err);
+ return symbol;
+
+ #else
+ return NULL;
+
+ #endif
+}
+
+
+// --------------------------------------------------------------------------
+
+
+typedef void PluginSetGraphicsDeviceFunc (void* device, int deviceType, int eventType);
+typedef void PluginRenderMarkerFunc (int marker);
+
+struct UnityPlugin {
+ void* pluginHandle;
+ PluginSetGraphicsDeviceFunc* setGraphicsDeviceFunc;
+ PluginRenderMarkerFunc* renderMarkerFunc;
+};
+
+typedef dynamic_array<UnityPlugin> UnityPluginArray;
+static UnityPluginArray g_Plugins;
+
+
+
+static void InitializePlugin (void* pluginHandle)
+{
+ // do nothing if we've already loaded this plugin
+ for (size_t i = 0; i < g_Plugins.size(); ++i)
+ {
+ if (g_Plugins[i].pluginHandle == pluginHandle)
+ return;
+ }
+
+ UnityPlugin plugin;
+ plugin.pluginHandle = pluginHandle;
+ plugin.setGraphicsDeviceFunc = (PluginSetGraphicsDeviceFunc*)LoadPluginFunction (pluginHandle, "UnitySetGraphicsDevice");
+ plugin.renderMarkerFunc = (PluginRenderMarkerFunc*)LoadPluginFunction (pluginHandle, "UnityRenderEvent");
+ g_Plugins.push_back (plugin);
+
+ if (IsGfxDevice() && plugin.setGraphicsDeviceFunc)
+ {
+ GfxDevice& device = GetGfxDevice();
+ plugin.setGraphicsDeviceFunc (device.GetNativeGfxDevice(), device.GetRenderer(), kGfxDeviceEventInitialize);
+ }
+}
+
+
+void PluginsSetGraphicsDevice (void* device, int deviceType, GfxDeviceEventType eventType)
+{
+ for (size_t i = 0; i < g_Plugins.size(); ++i)
+ {
+ if (g_Plugins[i].setGraphicsDeviceFunc)
+ {
+ g_Plugins[i].setGraphicsDeviceFunc (device, deviceType, eventType);
+ }
+ }
+}
+
+
+void PluginsRenderMarker (int marker)
+{
+ if (!IsGfxDevice())
+ return;
+ GfxDevice& device = GetRealGfxDevice();
+
+ for (size_t i = 0; i < g_Plugins.size(); ++i)
+ {
+ if (g_Plugins[i].renderMarkerFunc)
+ {
+ device.InvalidateState();
+ g_Plugins[i].renderMarkerFunc (marker);
+ device.InvalidateState();
+ }
+ }
+}
+
+#if UNITY_EDITOR
+static bool gAllowPlugins = true;
+void SetAllowPlugins (bool allow)
+{
+ gAllowPlugins = allow;
+}
+
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingInvocation.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Editor/Src/Keys/PublicKeys.h"
+
+static bool TryVerifySignature (const char *pluginPath, const unsigned char publicKey[])
+{
+ ScriptingInvocation verifySignature ("UnityEngine", "Security", "VerifySignature");
+ verifySignature.AddString (pluginPath);
+ verifySignature.AddArray (CreateScriptingArray (publicKey, PUBLIC_KEY_SIZE, GetScriptingTypeRegistry ().GetType ("System", "Byte")));
+ if (!MonoObjectToBool (verifySignature.Invoke ()))
+ return false;
+
+ // Additionally enable render to texture
+ GetBuildSettings ().hasRenderTexture = true;
+ return true;
+}
+
+// Explicitly allow plugins signed by approved parties
+static bool VerifySignature (const char *pluginPath)
+{
+ // Qualcomm
+ if (TryVerifySignature (pluginPath, kqualcomm))
+ return true;
+
+ return false;
+}
+#endif
+
+const char* FindAndLoadUnityPlugin (const char* pluginName)
+{
+ const char* pluginPath = FindPluginExecutable (pluginName);
+
+ if (pluginPath == NULL
+#if !UNITY_WINRT
+ || !strcmp(pluginPath, pluginName)
+#endif
+ )
+ return pluginPath; // not found
+
+#if UNITY_EDITOR
+ if (!gAllowPlugins)
+ {
+ if (!VerifySignature (pluginPath))
+ {
+ ErrorString ("License error. This plugin is only supported in Unity Pro!\n");
+ return pluginName;
+ }
+ }
+#endif
+
+ // plugin found, try loading & initializing it
+ void* pluginHandle = LoadPluginExecutable (pluginPath);
+ if (pluginHandle)
+ InitializePlugin (pluginHandle);
+
+ return pluginPath;
+}
+
+
+void UnloadAllPlugins ()
+{
+ for (size_t i = 0; i < g_Plugins.size(); ++i)
+ UnloadPluginExecutable (g_Plugins[i].pluginHandle);
+
+ g_Plugins.clear ();
+}
+
+
+#endif // UNITY_PLUGINS_AVAILABLE
diff --git a/Runtime/Misc/Plugins.h b/Runtime/Misc/Plugins.h
new file mode 100644
index 0000000..1230835
--- /dev/null
+++ b/Runtime/Misc/Plugins.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+void SetAllowPlugins (bool allow);
+
+// Never change the enum values!
+// They are used in low level native plugin interface.
+enum GfxDeviceEventType {
+ kGfxDeviceEventInitialize = 0,
+ kGfxDeviceEventShutdown = 1,
+ kGfxDeviceEventBeforeReset = 2,
+ kGfxDeviceEventAfterReset = 3,
+};
+
+#if UNITY_PLUGINS_AVAILABLE
+const char* FindAndLoadUnityPlugin (const char* pluginName);
+// Used by GfxDevice
+void PluginsSetGraphicsDevice (void* device, int deviceType, GfxDeviceEventType eventType);
+void PluginsRenderMarker (int marker);
+void UnloadAllPlugins ();
+#else
+inline void PluginsSetGraphicsDevice (void* device, int deviceType, GfxDeviceEventType eventType) { }
+inline void PluginsRenderMarker (int marker) { }
+#endif
diff --git a/Runtime/Misc/PreloadManager.cpp b/Runtime/Misc/PreloadManager.cpp
new file mode 100644
index 0000000..a5e1fc6
--- /dev/null
+++ b/Runtime/Misc/PreloadManager.cpp
@@ -0,0 +1,1013 @@
+#include "UnityPrefix.h"
+#include "PreloadManager.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+///////// FUSIONFALL todo: Fix this include by moving it to player i guess
+#include "Runtime/Misc/SaveAndLoadHelper.h"
+#include "Runtime/Misc/Player.h"
+#include "Runtime/Misc/AssetBundle.h"
+#include "Runtime/Threads/ThreadUtility.h"
+#include "Runtime/Serialize/SerializedFile.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Graphics/LightmapSettings.h"
+#include "Runtime/Misc/ReproductionLog.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Mono/MonoIncludes.h"
+#include "Runtime/Graphics/SubstanceSystem.h"
+#include "Runtime/Misc/AssetBundleUtility.h"
+
+#if UNITY_ANDROID
+ #include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+#endif
+#if UNITY_WII
+#include "PlatformDependent/Wii/WiiLoadingScreen.h"
+#endif
+
+
+PROFILER_INFORMATION (gPreloadLevel, "Application.LoadLevelAsync Integrate", kProfilerLoading);
+PROFILER_INFORMATION (gPreloadBundle, "AssetBundle.LoadAsync Integrate", kProfilerLoading);
+PROFILER_INFORMATION (gIntegrateAssetsInBackground, "Application.Integrate Assets in Background", kProfilerLoading);
+PROFILER_INFORMATION (gAsyncOperationComplete, "Application.WaitForAsyncOperationToComplete", kProfilerLoading);
+
+#if THREADED_LOADING
+ #define THREADED_LOADING_MUTEX_AUTOLOCK(x) Mutex::AutoLock lock (x)
+#else
+ #define THREADED_LOADING_MUTEX_AUTOLOCK(x) {}
+#endif
+
+#define PROFILE_PRELOAD_MANAGER 0
+#if PROFILE_PRELOAD_MANAGER
+#include "Runtime/Input/TimeManager.h"
+#endif
+
+static void GetFileIDsForLoadingScene (const string& pathName, PreloadLevelOperation::LoadingMode loadingMode, vector<LocalIdentifierInFileType>& fileIDs, vector<int>& managerIndices);
+PreloadData::~PreloadData ()
+{}
+
+template<class TransferFunction>
+void PreloadData::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.Transfer (m_Assets, "m_Assets");
+}
+
+IMPLEMENT_OBJECT_SERIALIZE(PreloadData)
+IMPLEMENT_CLASS(PreloadData)
+
+PreloadManager* gPreloadManager = NULL;
+
+PreloadManager& GetPreloadManager()
+{
+ if (gPreloadManager != NULL)
+ return *gPreloadManager;
+ else
+ {
+ gPreloadManager = new PreloadManager();
+ return *gPreloadManager;
+ }
+}
+
+void ReleasePreloadManager()
+{
+ delete gPreloadManager;
+ gPreloadManager = NULL;
+}
+
+void StopPreloadManager()
+{
+ if (gPreloadManager)
+ gPreloadManager->Stop();
+}
+
+PreloadManager::PreloadManager()
+: m_ProcessingOperation (NULL)
+{
+ m_IntegrationOperation = NULL;
+#if ENABLE_MONO
+ m_InitDomain = NULL;
+#endif
+#if THREADED_LOADING
+ m_Thread.SetName ("UnityPreload");
+#endif
+}
+
+PreloadManager::~PreloadManager()
+{
+ Stop();
+}
+
+void PreloadManager::SetThreadPriority(ThreadPriority p)
+{
+#if THREADED_LOADING
+ m_Thread.SetPriority(p);
+#endif
+}
+
+ThreadPriority PreloadManager::GetThreadPriority ()
+{
+#if THREADED_LOADING
+ return m_Thread.GetPriority();
+#else
+ return kNormalPriority;
+#endif
+}
+
+#if UNITY_EDITOR
+void PreloadManager::RemoveStopPlaymodeOperations ()
+{
+ THREADED_LOADING_MUTEX_AUTOLOCK(m_QueueMutex);
+ for (int i=0;i<m_PreloadQueue.size();i++)
+ {
+ PreloadManagerOperation* op = m_PreloadQueue[i];
+ if (op && op->IsPlaymodeLoadLevel())
+ {
+ op->CleanupCoroutine();
+ op->Release();
+ m_PreloadQueue.erase(m_PreloadQueue.begin() + i);
+ i--;
+ }
+ }
+}
+#endif
+
+void PreloadManager::Stop ()
+{
+#if THREADED_LOADING
+ m_Thread.SignalQuit();
+
+ {
+ PROFILER_AUTO(gIntegrateAssetsInBackground, NULL)
+ // Wait until loading is complete, to make sure everything is integrated so that we don't leak anything.
+ while (m_Thread.IsRunning())
+ {
+ UpdatePreloadingSingleStep(true);
+ Thread::Sleep(0.01F);
+ }
+ }
+
+ m_Thread.WaitForExit();
+#endif
+
+ InvokeCoroutineCallbacks ();
+
+ AssertIf(!m_CallCoroutineCallbackQueue.empty());
+
+ {
+ THREADED_LOADING_MUTEX_AUTOLOCK(m_QueueMutex);
+
+ for (int i=0;i<m_PreloadQueue.size();i++)
+ {
+ m_PreloadQueue[i]->CleanupCoroutine();
+ m_PreloadQueue[i]->Release();
+ }
+ m_PreloadQueue.clear();
+ m_CallCoroutineCallbackQueue.clear();
+ }
+
+ Assert(m_IntegrationOperation == NULL);
+ Assert(m_ProcessingOperation == NULL);
+ #if ENABLE_MONO
+ m_InitDomain = NULL;
+ #endif
+}
+
+#if THREADED_LOADING
+void* PreloadManager::Run (void* managerPtr)
+{
+#if ENABLE_PROFILER
+ profiler_initialize_thread ("PreloadManager Thread", true);
+#endif
+
+ PreloadManager& manager = *static_cast<PreloadManager*> (managerPtr);
+ manager.Run();
+
+#if ENABLE_PROFILER
+ profiler_cleanup_thread();
+#endif
+
+ return NULL;
+}
+
+void PreloadManager::Run ()
+{
+ #if ENABLE_MONO
+ mono_thread_attach(m_InitDomain);
+ m_InitDomain = NULL;
+ #endif
+
+#if PROFILE_PRELOAD_MANAGER
+ float startTime = GetTimeSinceStartup();
+ #define LOG_PROFILER(x) printf_console(x, m_ProcessingOperation->GetDebugName().c_str(), (GetTimeSinceStartup() - startTime) * 1000.0F); startTime = GetTimeSinceStartup();
+#else
+ #define LOG_PROFILER(x)
+#endif
+
+ while (!m_Thread.IsQuitSignaled())
+ {
+ m_QueueMutex.Lock();
+
+ if (!m_PreloadQueue.empty())
+ {
+ Assert(m_IntegrationOperation == NULL);
+
+ // Find highest priority queued item
+ int index = PreloadManager::FindTopPriorityOperation (m_PreloadQueue);
+
+ // Grab item and remove it from queue
+ Assert(m_ProcessingOperation == NULL);
+ m_ProcessingOperation = m_PreloadQueue[index];
+
+ LOG_PROFILER("Begin Background load: %s [idle time: %f]\n");
+
+ m_PreloadQueue.erase(m_PreloadQueue.begin() + index);
+
+ m_QueueMutex.Unlock();
+
+ m_LoadingMutex.Lock();
+
+ // Process it
+ m_ProcessingOperation->Perform();
+
+ LOG_PROFILER("Async background load complete: %s - %f ms\n");
+
+ bool hasIntegrate = m_ProcessingOperation->HasIntegrateMainThread();
+
+ // Integrate any work into main thread, wait for completion,
+ // so that the persistentmanager is kept clear until the integration thread is done
+ if (hasIntegrate)
+ {
+ AssertIf(m_IntegrationOperation != NULL);
+
+ m_IntegrationOperation = m_ProcessingOperation;
+
+ // This is needed in order to make sure that m_IntegrationOperation is properly synced when
+ // the signaled thread reads the value (otherwise we get an assert - so this is actually to prevent the assert.
+ UnityMemoryBarrier();
+
+ // Temporarily release the loading mutex so that the main thread can do
+ // LockPreloading() without deadlocking.
+ m_LoadingMutex.Unlock ();
+
+ m_IntegrationSemaphore.WaitForSignal();
+ Assert (NULL == m_IntegrationOperation);
+
+ // Re-acquire the loading mutex while we finish the current operation.
+ m_LoadingMutex.Lock ();
+ }
+
+ m_QueueMutex.Lock();
+ // Pass operation over into m_CallCoroutineCallbackQueue, which will finally release it
+ m_CallCoroutineCallbackQueue.push_back(m_ProcessingOperation);
+ //m_ProcessingOperation->Release();
+ LOG_PROFILER("Completed Integration step: %s - %f ms\n");
+ m_ProcessingOperation = NULL;
+
+ m_QueueMutex.Unlock();
+
+ m_LoadingMutex.Unlock();
+ }
+ else
+ {
+ m_QueueMutex.Unlock();
+ Thread::Sleep(0.1F);
+ }
+ }
+ #if ENABLE_MONO
+ mono_thread_detach(mono_thread_current ());
+ #endif
+
+#undef PROFILE_PRELOAD_MANAGER
+}
+
+#endif
+
+bool PreloadManager::IsLoading()
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+#if THREADED_LOADING
+ return m_LoadingMutex.IsLocked();
+#else
+ return false;
+#endif
+}
+
+bool PreloadManager::IsLoadingOrQueued()
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ if (IsLoading())
+ return true;
+
+ THREADED_LOADING_MUTEX_AUTOLOCK(m_QueueMutex);
+ if (m_ProcessingOperation)
+ return true;
+ return !m_PreloadQueue.empty();
+}
+
+size_t PreloadManager::FindTopPriorityOperation (std::vector<PreloadManagerOperation*>& ops)
+{
+ Assert (!ops.empty ());
+ int index = 0;
+ int highestPriority = ops[0]->GetPriority();
+ for (int i=1;i<ops.size();i++)
+ {
+ if (ops[i]->GetPriority() > highestPriority)
+ {
+ index = i;
+ highestPriority = ops[i]->GetPriority();
+ }
+ }
+
+ return index;
+}
+
+#if !THREADED_LOADING
+void PreloadManager::ProcessPreloadOperation ()
+{
+
+ #if PROFILE_PRELOAD_MANAGER
+ float startTime = GetTimeSinceStartup();
+ #define LOG_PROFILER(x) printf_console(x, m_ProcessingOperation->GetDebugName().c_str(), (GetTimeSinceStartup() - startTime) * 1000.0F); startTime = GetTimeSinceStartup();
+ #else
+ #define LOG_PROFILER(x)
+ #endif
+
+ if (!m_PreloadQueue.empty())
+ {
+ Assert (m_IntegrationOperation == NULL);
+
+ int index = PreloadManager::FindTopPriorityOperation (m_PreloadQueue);
+
+ // Grab item and remove it from queue
+ Assert(m_ProcessingOperation == NULL);
+ m_ProcessingOperation = m_PreloadQueue[index];
+
+ LOG_PROFILER("Begin background load (FAKE): %s [idle time: %f]\n");
+
+ m_PreloadQueue.erase(m_PreloadQueue.begin() + index);
+ m_ProcessingOperation->Perform();
+
+ LOG_PROFILER("Async background load complete (FAKE): %s - %f ms\n");
+
+ // Integrate any work into main thread, wait for completion,
+ // so that the persistentmanager is kept clear until the integration thread is done
+ if (m_ProcessingOperation->HasIntegrateMainThread())
+ {
+ Assert (m_IntegrationOperation == NULL);
+ m_IntegrationOperation = m_ProcessingOperation;
+ }
+
+ m_CallCoroutineCallbackQueue.push_back(m_ProcessingOperation);
+
+ LOG_PROFILER("Completed Integration step: %s - %f ms\n");
+
+ m_ProcessingOperation = NULL;
+ }
+
+ #undef PROFILE_PRELOAD_MANAGER
+}
+
+void PreloadManager::UpdatePreloadingSingleStep (bool stopPreloading)
+{
+ // Do loading
+ ProcessPreloadOperation ();
+
+ // Upload texture data immediately after texture asset was loaded
+ // On some platforms helps to avoid memory peak during level load
+ Texture2D::IntegrateLoadedImmediately();
+
+ // Integrate threaded objects for some time
+ GetPersistentManager().IntegrateThreadedObjects (20.0F);
+
+ // Perform final main thread integration step
+ if (m_IntegrationOperation != NULL && (m_IntegrationOperation->GetAllowSceneActivation() || stopPreloading))
+ {
+ PreloadManagerOperation* operation = m_IntegrationOperation;
+ m_IntegrationOperation = NULL;
+ operation->IntegrateMainThread();
+ }
+
+ // Potentially call out coroutines
+ InvokeCoroutineCallbacks();
+}
+
+#else
+
+void PreloadManager::UpdatePreloadingSingleStep (bool stopPreloading)
+{
+ // Should we start up the preload manager thread? Do it as soon as something needs to be processed
+ if (!m_Thread.IsRunning())
+ {
+ ////@TODO: Also use stopPreloading here...
+ if (!m_PreloadQueue.empty())
+ {
+ AssertIf(m_IntegrationOperation);
+ #if ENABLE_MONO
+ Assert(m_InitDomain == NULL);
+ m_InitDomain = mono_domain_get();
+ #endif
+#if UNITY_WII
+ // On Wii if we set thread priority below normal and don't call Sleep on MainThread, the child thread will never run!
+ // Update : Threaded loading actually works now on Wii, but it requires that allocators should be thread safe, and
+ // it's not the case on Wii, so if there will be plans to make threaded loading work, allocators should be made thread-safe
+ m_Thread.SetPriority(kNormalPriority);
+#else
+ m_Thread.SetPriority(kBelowNormalPriority);
+#endif
+
+ ////@TODO: This is currently a very high value because MonoBehaviours might do funky recursion!
+ // Reduce stacksize sensibly when we stop the madness.
+
+ unsigned stackSize = 512 * 1024;
+ if (UNITY_EDITOR)
+ stackSize = 2 * 1024 * 1024;
+
+#if UNITY_XENON
+ const int kProcessor = 4;
+#else
+ const int kProcessor = 1;
+#endif
+ m_Thread.Run(&PreloadManager::Run, this, stackSize, kProcessor);
+ }
+ }
+
+ // Upload texture data immediately after texture asset was loaded
+ // On some platforms helps to avoid memory peak during level load
+ Texture2D::IntegrateLoadedImmediately();
+
+ // Update substance integration, since we are not in the gameloop here
+ #if ENABLE_SUBSTANCE
+ SubstanceSystem* system = GetSubstanceSystemPtr ();
+ if (system != NULL && !system->AreIntegratingQueuesEmpty())
+ system->Update(stopPreloading);
+ #endif
+
+ // Figure out sensible time for how long we are allowed to integrate assets on the main thread each frame
+ float ms = 4.0F;
+ ThreadPriority p = GetThreadPriority();
+ if (p == kLowPriority)
+ ms = 2.0F;
+ else if (p == kBelowNormalPriority)
+ ms = 4.0F;
+ else if (p == kNormalPriority)
+ ms = 10.0F;
+ else if (p == kHighPriority)
+ ms = 50.0F;
+
+ // Integrate threaded objects for some time
+ GetPersistentManager().IntegrateThreadedObjects(ms / 1000.0F);
+
+ // Perform final main thread integration step
+ if (m_IntegrationOperation != NULL && (m_IntegrationOperation->GetAllowSceneActivation() || stopPreloading))
+ {
+ PreloadManagerOperation* operation = m_IntegrationOperation;
+ m_IntegrationOperation = NULL;
+ operation->IntegrateMainThread();
+
+ // This is needed in order to make sure that m_IntegrationOperation is properly synced when
+ // the signaled thread reads the value
+ UnityMemoryBarrier();
+ m_IntegrationSemaphore.Signal();
+ }
+
+ // Potentially call out coroutines
+ InvokeCoroutineCallbacks();
+}
+#endif
+
+void PreloadManager::InvokeCoroutineCallbacks ()
+{
+ // Grab coroutines that need to be invoked this frame
+ // And invoke them
+ std::vector<PreloadManagerOperation*> callbacks;
+
+ {
+ THREADED_LOADING_MUTEX_AUTOLOCK(m_QueueMutex);
+ callbacks.swap(m_CallCoroutineCallbackQueue);
+ }
+
+ for (int i=0;i<callbacks.size();i++)
+ {
+ callbacks[i]->InvokeCoroutine();
+ callbacks[i]->Release();
+ }
+}
+
+
+void PreloadManager::AddToQueue (PreloadManagerOperation* operation)
+{
+ THREADED_LOADING_MUTEX_AUTOLOCK(m_QueueMutex);
+ operation->Retain();
+ m_PreloadQueue.push_back(operation);
+}
+
+void PreloadManager::LockPreloading ()
+{
+ #if THREADED_LOADING
+ PROFILER_AUTO(gIntegrateAssetsInBackground, NULL)
+ while (!m_LoadingMutex.TryLock())
+ {
+ UpdatePreloadingSingleStep(false);
+ Thread::Sleep(0.004F);
+ }
+ #endif
+}
+
+void PreloadManager::UnlockPreloading ()
+{
+#if THREADED_LOADING
+ m_LoadingMutex.Unlock();
+#endif
+}
+
+void PreloadManager::WaitForAllAsyncOperationsToComplete()
+{
+ PROFILER_AUTO(gAsyncOperationComplete, NULL)
+
+ while(IsLoadingOrQueued())
+ {
+ UpdatePreloadingSingleStep(false);
+
+ #if THREADED_LOADING
+ if (!GetPersistentManager ().HasThreadedObjectsToIntegrate ())
+ LevelLoadingLoop();
+ #endif
+ }
+}
+
+void PreloadManager::UpdatePreloading()
+{
+ // Check if there are any operation that must be completed right away
+ bool mustCompleteNow = false;
+ {
+ THREADED_LOADING_MUTEX_AUTOLOCK(m_QueueMutex);
+ for (int i=0;i<m_PreloadQueue.size();i++)
+ mustCompleteNow |= m_PreloadQueue[i]->MustCompleteNextFrame();
+ if (m_ProcessingOperation != NULL)
+ mustCompleteNow |= m_ProcessingOperation->MustCompleteNextFrame();
+ }
+
+ if (mustCompleteNow)
+ {
+ WaitForAllAsyncOperationsToComplete ();
+#if UNITY_ANDROID
+ StopActivityIndicator();
+#endif
+ }
+ else
+ {
+ PROFILER_AUTO(gIntegrateAssetsInBackground, NULL)
+ UpdatePreloadingSingleStep (false);
+ }
+}
+
+
+float PreloadManagerOperation::GetProgress ()
+{
+ return m_Progress;
+}
+
+bool PreloadManagerOperation::IsDone ()
+{
+ return m_Complete;
+}
+
+void PreloadLevelOperation::Perform ()
+{
+ // Should indicate threaded loading
+ //GetGlobalAllocators().ActivateLoadingAllocator();
+
+ PersistentManager& pm = GetPersistentManager();
+
+#if UNITY_WII
+ wii::StartLoadingScreen();
+#endif
+
+#if ENABLE_SUBSTANCE
+ // Notify for preload operation
+ SubstanceSystem* system = GetSubstanceSystemPtr ();
+ if (system != NULL)
+ system->BeginPreloading();
+#endif
+
+ // When loading a level, grab the PreloadData and extract PPtrs of all assets not contained in the primary assets file
+ if (!m_LevelAssetDataPath.empty())
+ {
+ int preloadInstanceID = pm.GetInstanceIDFromPathAndFileID(m_LevelAssetDataPath, 1);
+ // @TODO: This should be moved to main thread or the preload data should always be killed immediately
+ Object* obj = Object::IDToPointerThreadSafe (preloadInstanceID);
+ if (obj == NULL)
+ obj = pm.ReadObjectThreaded(preloadInstanceID);
+
+ PreloadData* preload = dynamic_pptr_cast<PreloadData*> (obj);
+ if (preload)
+ {
+ int size = preload->m_Assets.size();
+ m_PreloadAssets.resize_uninitialized(size);
+ if (size > 0)
+ {
+ SInt32* target = &m_PreloadAssets[0];
+ PPtr<Object>* source = &preload->m_Assets[0];
+ for (int i=0;i<size;i++)
+ target[i] = source[i].GetInstanceID();
+ }
+ }
+ else
+ {
+ // In The Editor we load scenes that contain no preload data
+ // In the web player Unity 3.3 had a bug where preloaddata was not included in the build.
+ if (!WEBPLUG && !UNITY_EDITOR && m_LoadMode != kLoadAssetBundle)
+ {
+ AssertString("PreloadData is missing. It should always be there.");
+ }
+ }
+ }
+
+// printf_console("Number of Preload assets %d\n", m_PreloadAssets.size());
+// for (int i=0;i<m_PreloadAssets.size();i++)
+// {
+// printf_console("Will load Preload assets %d %s\n", m_PreloadAssets[i], GetPersistentManager().GetPathName(m_PreloadAssets[i]).c_str());
+// }
+
+ // Figure out exactly which objects in the file we need to load
+ // We want to know what we're loading before preloading to indicate the progress properly
+ vector<LocalIdentifierInFileType> fileIDs;
+ vector<int> managerIndices;
+ if (!m_LevelPath.empty())
+ GetFileIDsForLoadingScene(m_LevelPath, m_LoadMode, fileIDs, managerIndices);
+
+ LoadProgress loadProgress (fileIDs.size () + m_PreloadAssets.size (), 0.9f, &m_Progress);
+ // Preload bundle assets & preload external level assets
+ GetPersistentManager().LoadObjectsThreaded(m_PreloadAssets.begin(), m_PreloadAssets.size(), &loadProgress);
+
+ // Load Level
+ if (!m_LevelPath.empty())
+ {
+ /// Load all assets
+ pm.LoadFileCompletelyThreaded(m_LevelAssetDataPath, NULL, NULL, -1, false, &loadProgress);
+
+ // Wait for all assets to be integrated using timeslicing from the main thread
+ pm.AllowIntegrationWithTimeoutAndWait ();
+
+
+ ///@TODO: these two are not needed anymore...
+ std::vector<SInt32> instanceIDs;
+ instanceIDs.resize(fileIDs.size());
+
+ // Lock up to extracting all objects into the AwakeFromLoadQueue
+ // This prevents the main thread from loading and integrating random objects during the level load
+ pm.Lock();
+
+ Assert(!pm.HasThreadedObjectsToIntegrate());
+
+ // Load level
+ pm.LoadFileCompletelyThreaded(m_LevelPath, &fileIDs[0], &instanceIDs[0], fileIDs.size(), true, &loadProgress);
+
+ pm.PrepareAllThreadedObjectsStep1 (m_AwakeFromLoadQueue);
+
+ // Unlock - See above
+ pm.Unlock();
+
+
+ #if ENABLE_SUBSTANCE
+ // Wait Substance finish to integrate
+ SubstanceSystem* system = GetSubstanceSystemPtr ();
+ if (system != NULL)
+ system->WaitFinished(&loadProgress);
+ #endif
+
+ m_Progress = 0.9F;
+ }
+ else
+ {
+ // Wait for all assets to be integrated using timeslicing from the main thread
+ pm.AllowIntegrationWithTimeoutAndWait ();
+
+ #if ENABLE_SUBSTANCE
+ // Wait Substance finish to integrate
+ SubstanceSystem* system = GetSubstanceSystemPtr ();
+ if (system != NULL)
+ system->WaitFinished(&loadProgress);
+ #endif
+
+ m_Progress = 1.0F;
+ UnityMemoryBarrier();
+ m_Complete = true;
+ }
+#if UNITY_WII
+ wii::EndLoadingScreen();
+#endif
+}
+
+void PreloadLevelOperation::PreloadBundleSync (AssetBundle& bundle, const std::string& name)
+{
+ // Calculate preload index & size into preload table of asset bundle
+ int index = 0, size = 0;
+ if (name.empty())
+ {
+ index = bundle.m_MainAsset.preloadIndex;
+ size = bundle.m_MainAsset.preloadSize;
+ if (Object::IDToPointer(bundle.m_MainAsset.asset.GetInstanceID()))
+ return;
+ }
+ else
+ {
+ AssetBundle::range range = bundle.GetPathRange(name);
+ if (range.first != range.second)
+ {
+ index = range.first->second.preloadIndex;
+ size = range.first->second.preloadSize;
+ if (Object::IDToPointer(range.first->second.asset.GetInstanceID()))
+ return;
+ }
+ }
+
+ if (size == 0)
+ return;
+
+ PPtr<Object>* source = &bundle.m_PreloadTable[index];
+ for (int i=0;i<size;i++)
+ {
+ Object* forceLoad = source[i];
+ UNUSED(forceLoad);
+ }
+}
+
+bool PreloadLevelOperation::GetAllowSceneActivation ()
+{
+ return m_AllowSceneActivation;
+}
+
+void PreloadLevelOperation::SetAllowSceneActivation (bool allow)
+{
+ m_AllowSceneActivation = allow;
+}
+
+void PreloadLevelOperation::CleanupMemory()
+{
+ m_PreloadAssets.clear();
+ m_AwakeFromLoadQueue.Clear();
+}
+
+
+bool PreloadLevelOperation::HasIntegrateMainThread ()
+{
+ return !m_LevelPath.empty();
+}
+
+void PreloadLevelOperation::IntegrateMainThread ()
+{
+ Texture2D::IntegrateLoadedImmediately();
+
+ if (!m_LevelPath.empty())
+ {
+ PROFILER_AUTO(gPreloadLevel, NULL)
+ if (m_LoadMode == kLoadAdditiveLevel)
+ {
+ PostLoadLevelAdditive (m_LevelPath, m_AwakeFromLoadQueue);
+ }
+ else if (m_LoadMode == kLoadEditorAdditiveLevel)
+ {
+ PostEditorLoadLevelAdditive (m_LevelPath, m_AwakeFromLoadQueue);
+ }
+ else if (m_LoadMode == kLoadLevel)
+ {
+ PROFILER_AUTO(gPreloadLevel, NULL)
+
+ PlayerLoadLevelFromThread(m_LoadLevelIndex, m_LevelPath, m_AwakeFromLoadQueue);
+ }
+ else if (m_LoadMode == kLoadMainData)
+ {
+ PROFILER_AUTO(gPreloadLevel, NULL)
+ CompletePreloadMainData (m_AwakeFromLoadQueue);
+ }
+ #if UNITY_EDITOR
+ else if (m_LoadMode == kOpenSceneEditor || m_LoadMode == kOpenSceneEditorPlaymode)
+ {
+ PROFILER_AUTO(gPreloadLevel, NULL)
+ CompletePreloadManagerLoadLevelEditor (m_LevelPath, m_AwakeFromLoadQueue, m_LoadMode);
+ }
+ #endif
+ else
+ {
+ AssertString("Cant reach");
+ }
+
+ VerifyNothingIsPersistentInLoadedScene(m_LevelPath);
+ }
+ else
+ {
+ PROFILER_AUTO(gPreloadBundle, NULL)
+ GetPersistentManager().IntegrateAllThreadedObjects();
+ }
+
+ CleanupMemory();
+
+ m_Progress = 1.0F;
+ UnityMemoryBarrier();
+ m_Complete = true;
+
+ if (RunningReproduction())
+ {
+ LogString(Format("Completed loading level: '%s' (time: %f, frame: %d)", m_LevelPath.c_str(), GetTimeManager().GetCurTime(), GetTimeManager().GetFrameCount()));
+ }
+}
+
+PreloadLevelOperation* PreloadLevelOperation::LoadLevel (const std::string& levelPath, const std::string& levelAssetPath, int levelIndex, LoadingMode mode, bool mustCompleteNextFrame)
+{
+ if (!mustCompleteNextFrame && !GetBuildSettings().hasPROVersion)
+ {
+ ErrorString("Asynchronous Background loading is only supported in Unity Pro.\nPlease use Application.LoadLevel or Application.LoadLevelAdditive instead.");
+ mustCompleteNextFrame = true;
+ }
+
+ PreloadLevelOperation* operation = new PreloadLevelOperation ();
+ operation->m_LevelPath = levelPath;
+ operation->m_LevelAssetDataPath = levelAssetPath;
+ operation->m_LoadMode = mode;
+ operation->m_LoadLevelIndex = levelIndex;
+ operation->m_MustCompleteNextFrame = mustCompleteNextFrame;
+ #if ENABLE_PROFILER || PROFILE_PRELOAD_MANAGER
+ operation->m_DebugName = "Loading " + levelPath;
+ #endif
+ GetPreloadManager().AddToQueue(operation);
+
+ return operation;
+}
+
+UnloadUnusedAssetsOperation* UnloadUnusedAssetsOperation::UnloadUnusedAssets ()
+{
+ UnloadUnusedAssetsOperation* operation = new UnloadUnusedAssetsOperation ();
+ GetPreloadManager().AddToQueue(operation);
+
+ return operation;
+}
+
+void UnloadUnusedAssetsOperation::IntegrateMainThread ()
+{
+ GarbageCollectSharedAssets(true);
+ m_Progress = 1.0F;
+ UnityMemoryBarrier();
+ m_Complete = true;
+}
+
+PreloadLevelOperation* PreloadLevelOperation::CreateDummy ()
+{
+ PreloadLevelOperation* operation = new PreloadLevelOperation ();
+ operation->m_Progress = 1.0F;
+ UnityMemoryBarrier();
+ operation->m_Complete = true;
+ return operation;
+}
+
+/// @TODO: one string can identify multiple different resources.
+/// We must somehow preload all or make preload tables be shared by name
+PreloadLevelOperation* PreloadLevelOperation::LoadAssetBundle (AssetBundle& bundle, const std::string& name)
+{
+ if (!GetBuildSettings().hasPROVersion)
+ {
+ ErrorString("Asynchronous Background loading is only supported in Unity Pro.\nPlease use AssetBundle.Load instead");
+ return CreateDummy ();
+ }
+
+ PreloadLevelOperation* operation = NULL;
+
+ // Calculate preload index & size into preload table of asset bundle
+ int index = 0, size = 0;
+ if (name.empty())
+ {
+ index = bundle.m_MainAsset.preloadIndex;
+ size = bundle.m_MainAsset.preloadSize;
+ if (Object::IDToPointer(bundle.m_MainAsset.asset.GetInstanceID()))
+ return CreateDummy ();
+ }
+ else
+ {
+ AssetBundle::range range = bundle.GetPathRange(name);
+ if (range.first != range.second)
+ {
+ index = range.first->second.preloadIndex;
+ size = range.first->second.preloadSize;
+ if (Object::IDToPointer(range.first->second.asset.GetInstanceID()))
+ return CreateDummy ();
+ }
+ }
+
+ if (size == 0)
+ return CreateDummy ();
+
+ operation = new PreloadLevelOperation ();
+ #if ENABLE_PROFILER || PROFILE_PRELOAD_MANAGER
+ operation->m_DebugName = "Loading asset bundle asset: " + name;
+ #endif
+
+ operation->m_PreloadAssets.resize_uninitialized(size);
+ operation->m_LoadMode = kLoadAssetBundle;
+
+ SInt32* target = &operation->m_PreloadAssets[0];
+ PPtr<Object>* source = &bundle.m_PreloadTable[index];
+ for (int i=0;i<size;i++)
+ target[i] = source[i].GetInstanceID();
+
+ GetPreloadManager().AddToQueue(operation);
+
+ return operation;
+}
+
+PreloadLevelOperation::PreloadLevelOperation ()
+ : PreloadManagerOperation (),
+ m_AwakeFromLoadQueue(kMemSerialization)
+{
+ m_LoadLevelIndex = -1;
+ m_MustCompleteNextFrame = false;
+ m_AllowSceneActivation = true;
+}
+
+
+PreloadLevelOperation::~PreloadLevelOperation ()
+{
+}
+
+static void GetFileIDsForLoadingScene (const string& pathName, PreloadLevelOperation::LoadingMode operation, vector<LocalIdentifierInFileType>& fileIDs, vector<int>& managerIndices)
+{
+ GetPersistentManager ().Lock();
+ SerializedFile* stream = GetPersistentManager ().GetSerializedFileInternal(pathName);
+ if (stream == NULL)
+ {
+ GetPersistentManager ().Unlock();
+ return;
+ }
+
+ vector<LocalIdentifierInFileType> sourceFileIDs;
+ stream->GetAllFileIDs(&sourceFileIDs);
+ fileIDs.reserve(sourceFileIDs.size());
+
+ #if UNITY_EDITOR
+ int editorExtensionImplClassID = Object::StringToClassID("EditorExtensionImpl");
+ #endif
+
+
+ // GameManager no longer references EditorExtensionImpl. We need to not load EditorExtensionImpl related to game managers.
+ // But we do need to load EditorExtensionImpl of game objects & components in order to grab deprecated data describing prefabs from it.
+ // If we dont they might in turn load the manager, which we need very tight control over when we want them to be loaded.
+ // Sometime in the past EditorExtensionImpl were referenced by GameManager.
+ // So we skip over loading any editor extension impl objects before any scene objects are being loaded.
+ // Loading the editorextension impl of a manager is dangerous because it will load it by dereferencing it's m_Object reference.
+ // Don't load any editor extension impl for any managers
+ // All editor extension impl's in scenes are always serialized directly after their related object.
+ int baseSceneDataIndex = 0;
+ for (int i=0;i<sourceFileIDs.size ();i++)
+ {
+ LocalIdentifierInFileType fileID = sourceFileIDs[i];
+ int classID = stream->GetClassID (fileID);
+
+ if (Object::IsDerivedFromClassID (classID, ClassID (GameManager)))
+ baseSceneDataIndex = i + 1;
+ #if UNITY_EDITOR
+ else if (classID == editorExtensionImplClassID)
+ continue;
+ #endif
+ else
+ break;
+ }
+
+ for (int i=0;i<sourceFileIDs.size ();i++)
+ {
+ LocalIdentifierInFileType fileID = sourceFileIDs[i];
+ int classID = stream->GetClassID (fileID);
+
+ if (Object::IsDerivedFromClassID (classID, ClassID (GlobalGameManager)))
+ continue;
+
+ if (Object::IsDerivedFromClassID (classID, ClassID (LevelGameManager)))
+ {
+ // Additive loaded levels need lightmap settings from the scene file. It will be merged into the active scene.
+ bool loadAdditive = operation == PreloadLevelOperation::kLoadEditorAdditiveLevel || operation == PreloadLevelOperation::kLoadAdditiveLevel;
+ if (loadAdditive && classID != ClassID(LightmapSettings))
+ continue;
+
+ managerIndices.push_back(fileIDs.size());
+ }
+
+ #if UNITY_EDITOR
+ if (classID == editorExtensionImplClassID && i < baseSceneDataIndex)
+ continue;
+ #endif
+
+ fileIDs.push_back(fileID);
+ }
+
+ GetPersistentManager ().Unlock();
+}
+
+
+#undef THREADED_LOADING_MUTEX_AUTOLOCK // Mutex::AutoLock lock (x) / {}
+
+void UnloadUnusedAssetsImmediate (bool includeMonoReferencesAsRoots)
+{
+ GetPreloadManager().LockPreloading();
+ GarbageCollectSharedAssets(includeMonoReferencesAsRoots);
+ GetPreloadManager().UnlockPreloading();
+}
diff --git a/Runtime/Misc/PreloadManager.h b/Runtime/Misc/PreloadManager.h
new file mode 100644
index 0000000..e8aa38f
--- /dev/null
+++ b/Runtime/Misc/PreloadManager.h
@@ -0,0 +1,209 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/Mutex.h"
+#include "ResourceManager.h"
+#include "Runtime/Threads/Semaphore.h"
+#include "AsyncOperation.h"
+#include "Runtime/Serialize/AwakeFromLoadQueue.h"
+
+struct MonoDomain;
+class PreloadManagerOperation;
+class AssetBundle;
+
+
+class PreloadData : public NamedObject
+{
+ public:
+
+ DECLARE_OBJECT_SERIALIZE (PreloadData)
+ REGISTER_DERIVED_CLASS (PreloadData, NamedObject)
+
+ PreloadData (MemLabelId label, ObjectCreationMode mode) : Super(label, mode) { }
+ // ~PreloadData (); declared-by-macro
+
+ virtual bool ShouldIgnoreInGarbageDependencyTracking () { return true; }
+
+ std::vector<PPtr<Object> > m_Assets;
+};
+
+class PreloadManager
+{
+ #if THREADED_LOADING
+ Thread m_Thread;
+ Semaphore m_IntegrationSemaphore;
+ Mutex m_QueueMutex;
+ Mutex m_LoadingMutex;
+ #endif
+
+ std::vector<PreloadManagerOperation*> m_PreloadQueue;
+ std::vector<PreloadManagerOperation*> m_CallCoroutineCallbackQueue;
+ PreloadManagerOperation* m_ProcessingOperation;
+
+ PreloadManagerOperation* volatile m_IntegrationOperation;
+ #if ENABLE_MONO
+ MonoDomain* m_InitDomain;
+ #endif
+
+ static void* Run (void* managerPtr);
+ void Run ();
+ void ProcessPreloadOperation ();
+ void InvokeCoroutineCallbacks ();
+
+ static size_t FindTopPriorityOperation (std::vector<PreloadManagerOperation*>& ops);
+
+ public:
+
+ PreloadManager();
+ ~PreloadManager();
+
+ bool IsLoading();
+ bool IsLoadingOrQueued();
+ bool IsLoadingOrQueuedLevel();
+
+ void AddToQueue (PreloadManagerOperation* operation);
+
+ void LockPreloading ();
+ void UnlockPreloading ();
+
+ void UpdatePreloading ();
+
+ void SetThreadPriority(ThreadPriority p);
+ ThreadPriority GetThreadPriority ();
+
+#if UNITY_EDITOR
+ void RemoveStopPlaymodeOperations ();
+#endif
+
+ void WaitForAllAsyncOperationsToComplete();
+
+ void Stop();
+
+private:
+
+ void UpdatePreloadingSingleStep (bool stopPreloadManager);
+};
+
+PreloadManager& GetPreloadManager();
+void ReleasePreloadManager();
+void StopPreloadManager();
+
+class PreloadManagerOperation : public AsyncOperation
+{
+ protected:
+ int m_Priority;
+ bool m_Complete;
+ float m_Progress;
+
+ PreloadManagerOperation () { m_Priority = 0; m_Complete = false; m_Progress = 0.0F; }
+
+ public:
+
+ virtual int GetPriority () { return m_Priority; }
+ virtual void SetPriority (int priority) { m_Priority = priority; }
+
+ virtual float GetProgress ();
+
+ /// Returns true when the operation has completed.
+ /// Subclasses must set m_Complete to true from either Perform or IntegrateMainThread.
+ virtual bool IsDone ();
+
+ /// Performs the actual work on the preload manager thread
+ virtual void Perform () = 0;
+
+ /// When complete gives the operation a chance to integrate some work on the main thread,
+ /// without any other preload operation running at the same time
+ virtual void IntegrateMainThread () { }
+
+ /// Override this and return true if you want IntegrateMainThread to be called.
+ virtual bool HasIntegrateMainThread () { return false; }
+
+ /// Do we have to complete the preload operation by the next frame?
+ virtual bool MustCompleteNextFrame () { return false; }
+
+#if UNITY_EDITOR
+ virtual bool IsPlaymodeLoadLevel () { return false; }
+#endif
+
+#if ENABLE_PROFILER
+ virtual std::string GetDebugName () = 0;
+#endif
+};
+
+///@TODO: We should seperate SceneLoading from assetbundle loading code here. Make it into two AsyncOperation classes.
+
+
+class PreloadLevelOperation : public PreloadManagerOperation
+{
+ public:
+
+ static void PreloadBundleSync (AssetBundle& bundle, const std::string& name);
+
+ static PreloadLevelOperation* LoadAssetBundle (AssetBundle& bundle, const std::string& name);
+
+ enum LoadingMode { kLoadLevel = 0, kLoadAdditiveLevel = 1, kLoadMainData = 2, kLoadAssetBundle = 3, kOpenSceneEditor = 3, kOpenSceneEditorPlaymode = 4, kLoadEditorAdditiveLevel = 5 };
+ static PreloadLevelOperation* LoadLevel (const std::string& levelPath, const std::string& levelAssetPath, int levelIndex, LoadingMode mode, bool mustCompleteNextFrame);
+
+
+ virtual void IntegrateMainThread ();
+ virtual bool HasIntegrateMainThread ();
+
+ virtual bool GetAllowSceneActivation ();
+ virtual void SetAllowSceneActivation (bool allow);
+
+ virtual void Perform ();
+
+ virtual bool MustCompleteNextFrame () { return m_MustCompleteNextFrame; }
+
+#if ENABLE_PROFILER
+ virtual std::string GetDebugName () { return m_DebugName; }
+#endif
+
+#if UNITY_EDITOR
+ virtual bool IsPlaymodeLoadLevel () { return m_LoadMode == kLoadLevel || m_LoadMode == kLoadAdditiveLevel; }
+#endif
+
+private:
+ bool m_AllowSceneActivation;
+ dynamic_array<SInt32> m_PreloadAssets;
+ int m_LoadLevelIndex;
+ std::string m_LevelPath;
+ std::string m_LevelAssetDataPath;
+ LoadingMode m_LoadMode;
+ bool m_MustCompleteNextFrame;
+
+ AwakeFromLoadQueue m_AwakeFromLoadQueue;
+
+#if ENABLE_PROFILER
+ std::string m_DebugName;
+#endif
+
+ void CleanupMemory();
+
+ PreloadLevelOperation ();
+ virtual ~PreloadLevelOperation ();
+
+ static PreloadLevelOperation* CreateDummy ();
+};
+
+class UnloadUnusedAssetsOperation : public PreloadManagerOperation
+{
+ UnloadUnusedAssetsOperation () : PreloadManagerOperation () { }
+
+ public:
+
+ static UnloadUnusedAssetsOperation* UnloadUnusedAssets ();
+
+ virtual void IntegrateMainThread ();
+ virtual bool HasIntegrateMainThread () {return true;}
+
+ virtual void Perform () {}
+
+#if ENABLE_PROFILER
+ virtual std::string GetDebugName () { return "Garbage Collect Assets"; }
+#endif
+};
+
+void UnloadUnusedAssetsImmediate (bool managedObjects);
diff --git a/Runtime/Misc/QualitySettings.cpp b/Runtime/Misc/QualitySettings.cpp
new file mode 100644
index 0000000..11916f6
--- /dev/null
+++ b/Runtime/Misc/QualitySettings.cpp
@@ -0,0 +1,644 @@
+#include "UnityPrefix.h"
+#include "QualitySettings.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Utilities/PlayerPrefs.h"
+#include "Runtime/Graphics/Texture.h"
+#include "Runtime/Camera/LightManager.h"
+#include "Runtime/Camera/LODGroupManager.h"
+#include "Runtime/Camera/Shadows.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Utilities/Utility.h"
+#if UNITY_EDITOR
+#include "Editor/Src/BuildPipeline/BuildTargetPlatformSpecific.h"
+#include "Editor/Src/LightmapVisualization.h"
+#include "Editor/Platform/Interface/EditorWindows.h"
+#endif
+
+enum { kDefaultQualitySettingCount = 6 };
+
+QualitySettings::QualitySettings(MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode)
+{
+ #if UNITY_EDITOR
+ m_PreviousAA = -1;
+ m_PreviousVSync = -1;
+ #endif
+ m_StrippedMaximumLODLevel = 0;
+}
+
+QualitySettings::~QualitySettings()
+{
+}
+
+// defaults to Fastest to Fantastic quality settings
+QualitySettings::QualitySetting::QualitySetting()
+: name("Fastest")
+, pixelLightCount(0)
+, shadows(QualitySettings::kShadowsDisable)
+, shadowResolution(0)
+, shadowProjection(kShadowProjStableFit)
+, shadowCascades(1)
+, shadowDistance(15.0f)
+, blendWeights(1)
+, textureQuality(1)
+, anisotropicTextures(0)
+, antiAliasing(0)
+, vSyncCount(0)
+, softParticles(false)
+, softVegetation(false)
+, lodBias(0.3F)
+, maximumLODLevel(0)
+, particleRaycastBudget(4)
+{
+}
+
+IMPLEMENT_CLASS_HAS_INIT (QualitySettings)
+IMPLEMENT_OBJECT_SERIALIZE (QualitySettings)
+GET_MANAGER (QualitySettings)
+GET_MANAGER_PTR (QualitySettings)
+
+static void InitializeDefaultQualitySettings(QualitySettings::QualitySetting* m_Quality)
+{
+ QualitySettings::QualitySetting qualitySetting;
+
+ // Fastest to Fantastic quality settings
+ m_Quality[0] = m_Quality[1] = m_Quality[2] = m_Quality[3] = m_Quality[4] = m_Quality[5] = qualitySetting;
+
+ m_Quality[1].name = "Fast";
+ m_Quality[1].shadowDistance = 20.0f;
+ m_Quality[1].blendWeights = 2;
+ m_Quality[1].textureQuality = 0;
+ m_Quality[1].lodBias = 0.4F;
+ m_Quality[1].particleRaycastBudget = 16;
+
+ m_Quality[2].name = "Simple";
+ m_Quality[2].pixelLightCount = 1;
+ m_Quality[2].shadows = QualitySettings::kShadowsHardOnly;
+ m_Quality[2].shadowDistance = 20.0f;
+ m_Quality[2].blendWeights = 2;
+ m_Quality[2].textureQuality = 0;
+ m_Quality[2].anisotropicTextures = 1;
+ m_Quality[2].lodBias = 0.70F;
+ m_Quality[2].particleRaycastBudget = 64;
+
+ m_Quality[3].name = "Good";
+ m_Quality[3].pixelLightCount = 2;
+ m_Quality[3].shadows = QualitySettings::kShadowsAll;
+ m_Quality[3].shadowResolution = 1;
+ m_Quality[3].shadowCascades = 2;
+ m_Quality[3].shadowDistance = 40.0f;
+ m_Quality[3].blendWeights = 2;
+ m_Quality[3].textureQuality = 0;
+ m_Quality[3].anisotropicTextures = 1;
+ m_Quality[3].vSyncCount = 1;
+ m_Quality[3].softVegetation = true;
+ m_Quality[3].lodBias = 1.0F;
+ m_Quality[3].particleRaycastBudget = 256;
+
+ m_Quality[4].name = "Beautiful";
+ m_Quality[4].pixelLightCount = 3;
+ m_Quality[4].shadows = QualitySettings::kShadowsAll;
+ m_Quality[4].shadowResolution = 2;
+ m_Quality[4].shadowCascades = 2;
+ m_Quality[4].shadowDistance = 70.0f;
+ m_Quality[4].blendWeights = 4;
+ m_Quality[4].textureQuality = 0;
+ m_Quality[4].anisotropicTextures = 2;
+ m_Quality[4].antiAliasing = 2;
+ m_Quality[4].vSyncCount = 1;
+ m_Quality[4].softParticles = true;
+ m_Quality[4].softVegetation = true;
+ m_Quality[4].lodBias = 1.5F;
+ m_Quality[4].particleRaycastBudget = 1024;
+
+ m_Quality[5].name = "Fantastic";
+ m_Quality[5].pixelLightCount = 4;
+ m_Quality[5].shadows = QualitySettings::kShadowsAll;
+ m_Quality[5].shadowResolution = 2;
+ m_Quality[5].shadowCascades = 4;
+ m_Quality[5].shadowDistance = 150.0f;
+ m_Quality[5].blendWeights = 4;
+ m_Quality[5].textureQuality = 0;
+ m_Quality[5].anisotropicTextures = 2;
+ m_Quality[5].antiAliasing = 2;
+ m_Quality[5].vSyncCount = 1;
+ m_Quality[5].softParticles = true;
+ m_Quality[5].softVegetation = true;
+ m_Quality[5].lodBias = 2.0F;
+ m_Quality[5].particleRaycastBudget = 4096;
+}
+
+#if UNITY_EDITOR
+static void InitializePerPlatformDefaults(QualitySettings::PerPlatformDefaultQuality* platformSettings)
+{
+ platformSettings->clear ();
+ for (int i=1; i<kBuildPlayerTypeCount; i++)
+ {
+ string target = GetBuildTargetGroupName((BuildTargetPlatform)i, false);
+
+ if (target == "")
+ continue;
+
+ QualitySettings::PerPlatformDefaultQuality::iterator found = platformSettings->find(target);
+ if (found == platformSettings->end())
+ {
+ if (target == "iPhone" || target == "Android" || target == "BlackBerry" || target == "Tizen")
+ platformSettings->insert (pair<string,int>(target, 2));
+ else
+ platformSettings->insert (pair<string,int>(target, 3));
+ }
+ }
+}
+#endif
+
+void QualitySettings::Reset()
+{
+ SET_ALLOC_OWNER(this);
+ Super::Reset();
+
+ QualitySetting tempQuality[kDefaultQualitySettingCount];
+ InitializeDefaultQualitySettings(tempQuality);
+#if UNITY_EDITOR
+ InitializePerPlatformDefaults (&m_PerPlatformDefaultQuality);
+#endif
+ m_QualitySettings.assign(tempQuality, tempQuality + kDefaultQualitySettingCount);
+
+ m_CurrentQuality = 3;
+}
+
+void QualitySettings::CheckConsistency ()
+{
+ // Ensure that there is always at least one quality setting.
+ if (m_QualitySettings.empty())
+ {
+ QualitySetting tempQuality[kDefaultQualitySettingCount];
+ InitializeDefaultQualitySettings(tempQuality);
+#if UNITY_EDITOR
+ InitializePerPlatformDefaults (&m_PerPlatformDefaultQuality);
+#endif
+ m_QualitySettings.push_back(tempQuality[3]);
+ }
+
+ for( int i = 0; i < m_QualitySettings.size(); ++i )
+ {
+ QualitySetting &q (m_QualitySettings[i]);
+ q.pixelLightCount = std::max (q.pixelLightCount, 0);
+ q.shadows = clamp<int>( q.shadows, 0, kShadowQualityCount-1 );
+ q.shadowResolution = clamp<int>( q.shadowResolution, 0, kShadowResolutionCount-1 );
+ q.shadowProjection = clamp<int>( q.shadowProjection, 0, kShadowProjectionCount-1 );
+ q.shadowCascades = clamp<int>( q.shadowCascades, 1, 4 );
+ if (q.shadowCascades == 3)
+ q.shadowCascades = 2;
+ if (q.antiAliasing < 2)
+ q.antiAliasing = 0;
+ else if (q.antiAliasing < 4)
+ q.antiAliasing = 2;
+ else if (q.antiAliasing < 8)
+ q.antiAliasing = 4;
+ else
+ q.antiAliasing = 8;
+ q.shadowDistance = std::max ( q.shadowDistance, 0.0f );
+ q.blendWeights = std::min (std::max (q.blendWeights, 1), 4);
+ if( q.blendWeights == 3 )
+ q.blendWeights = 2;
+
+ // in editor inspector we allow set [0..3], but during play we allow any level you want ;-)
+ // set 10 as max in that case as it is lowest mip for 1024 texture - seems enough 8)
+ {
+ #if UNITY_EDITOR
+ if(!IsWorldPlaying())
+ q.textureQuality = std::min (std::max (q.textureQuality, 0), 3);
+ else
+ #endif
+ q.textureQuality = std::min (std::max (q.textureQuality, 0), 10);
+ }
+
+ q.anisotropicTextures = std::min (std::max (q.anisotropicTextures, 0), 3);
+ q.vSyncCount = std::min (std::max (q.vSyncCount, 0), 2);
+ q.lodBias = max(0.0F, q.lodBias);
+ q.maximumLODLevel = clamp(q.maximumLODLevel, 0, 7);
+ }
+
+ m_CurrentQuality = clamp<int>( m_CurrentQuality, 0, m_QualitySettings.size()-1 );
+}
+
+void QualitySettings::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ #if !UNITY_EDITOR
+ // player
+ // careful to not change current quality setting if player prefs entry does not exist
+ m_CurrentQuality = clamp<int>( PlayerPrefs::GetInt("UnityGraphicsQuality", m_CurrentQuality), 0, m_QualitySettings.size()-1 );
+ ApplySettings ();
+ #else
+ // editor
+ PlayerPrefs::SetInt("UnityGraphicsQuality", m_CurrentQuality);
+ ApplySettings (-1, (awakeMode & kDidLoadFromDisk)==0);
+ #endif
+
+}
+
+std::vector<std::string> QualitySettings::GetQualitySettingsNames ()
+{
+ std::vector<std::string> settingNames;
+ for( int i = 0; i < m_QualitySettings.size(); ++i )
+ {
+ settingNames.push_back (m_QualitySettings[i].name);
+ }
+ return settingNames;
+}
+
+void QualitySettings::SetCurrentIndex (int setting, bool applyExpensiveChanges)
+{
+ setting = clamp<int>( setting, 0, m_QualitySettings.size()-1 );
+
+ SetDirty ();
+ int oldIndex = m_CurrentQuality;
+ m_CurrentQuality = setting;
+ PlayerPrefs::SetInt("UnityGraphicsQuality", m_CurrentQuality);
+
+ ApplySettings( oldIndex, applyExpensiveChanges );
+}
+
+bool QualitySettings::SetCurrent (string setting, bool applyExpensiveChanges)
+{
+ for( int i = 0; i < m_QualitySettings.size(); ++i )
+ {
+ if (m_QualitySettings[i].name == setting)
+ {
+ SetCurrentIndex (i, applyExpensiveChanges);
+ return true;
+ }
+ }
+ return false;
+}
+
+void QualitySettings::ApplySettings( int previousIndex, bool applyExpensiveChanges )
+{
+ const QualitySetting &q = GetCurrent();
+
+ Texture::SetAnisoLimit (q.anisotropicTextures);
+ Texture::SetMasterTextureLimit (q.textureQuality);
+ if (GetLODGroupManagerPtr())
+ GetLODGroupManager().SetLODBias (q.lodBias);
+
+ // When playing we have to clamp to the stripped maximum level
+ int lodLevel = q.maximumLODLevel;
+ if (IsWorldPlaying())
+ lodLevel = max(m_StrippedMaximumLODLevel, lodLevel);
+ if (GetLODGroupManagerPtr())
+ GetLODGroupManager().SetMaximumLODLevel (lodLevel);
+
+ bool hasChanged = false;
+ if (applyExpensiveChanges)
+ {
+ #if UNITY_EDITOR
+ if( m_PreviousAA != q.antiAliasing || m_PreviousVSync != q.vSyncCount )
+ hasChanged = true;
+ #else
+ const QualitySetting& old = GetQualityForIndex(previousIndex);
+ if( old.antiAliasing != q.antiAliasing || old.vSyncCount != q.vSyncCount )
+ hasChanged = true;
+ #endif
+ }
+
+ if (hasChanged)
+ {
+ ApplyExpensiveSettings();
+ }
+}
+
+void QualitySettings::ApplyExpensiveSettings()
+{
+#if UNITY_EDITOR
+ GUIView::RecreateAllOnAAChange();
+ m_PreviousAA = GetCurrent().antiAliasing;
+ m_PreviousVSync = GetCurrent().vSyncCount;
+#else
+ // Make sure to use "as requested" values for everything
+ // Otherwise we might lose pending changes (case 559015)
+ ScreenManager& screen = GetScreenManager();
+ screen.RequestResolution(
+ screen.GetWidthAsRequested(), screen.GetHeightAsRequested(),
+ screen.GetFullScreenAsRequested(), screen.GetRefreshRateAsRequested());
+#endif
+}
+
+template<class TransferFunc>
+void QualitySettings::QualitySetting::Transfer (TransferFunc& transfer)
+{
+ transfer.SetVersion(2);
+ TRANSFER_SIMPLE (name);
+ TRANSFER_SIMPLE (pixelLightCount);
+ TRANSFER_SIMPLE (shadows);
+ TRANSFER_SIMPLE (shadowResolution);
+ TRANSFER_SIMPLE (shadowProjection);
+ TRANSFER_SIMPLE (shadowCascades);
+ TRANSFER_SIMPLE (shadowDistance);
+ TRANSFER_SIMPLE (blendWeights);
+ TRANSFER_SIMPLE (textureQuality);
+ TRANSFER_SIMPLE (anisotropicTextures);
+ TRANSFER_SIMPLE (antiAliasing);
+ TRANSFER_SIMPLE (softParticles);
+ transfer.Transfer (softVegetation, "softVegetation", kSimpleEditorMask | kHideInEditorMask);
+ transfer.Align();
+ TRANSFER_SIMPLE (vSyncCount);
+ TRANSFER_SIMPLE (lodBias);
+ TRANSFER_SIMPLE (maximumLODLevel);
+ TRANSFER_SIMPLE (particleRaycastBudget);
+
+ // Unity 3.4 introduced vSyncCount instead of syncToVBL.
+ if(transfer.IsVersionSmallerOrEqual(1))
+ {
+ bool syncToVBL;
+ transfer.Transfer (syncToVBL, "syncToVBL", kSimpleEditorMask | kHideInEditorMask);
+ vSyncCount = (syncToVBL?1:0);
+ }
+ transfer.Align();
+
+ TRANSFER_EDITOR_ONLY (excludedTargetPlatforms);
+}
+
+template<class TransferFunction>
+void QualitySettings::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion( 5 );
+
+ transfer.Transfer (m_CurrentQuality, "m_CurrentQuality", kSimpleEditorMask);
+
+ TRANSFER_SIMPLE (m_QualitySettings);
+ TRANSFER_EDITOR_ONLY(m_PerPlatformDefaultQuality);
+
+ // Read old default selected default quality settings
+ if (transfer.IsVersionSmallerOrEqual(4))
+ {
+ int m_DefaultStandaloneQuality = 3;
+ int m_DefaultWebPlayerQuality = 3;
+ int m_DefaultMobileQuality = 2;
+
+ TRANSFER_SIMPLE (m_DefaultStandaloneQuality);
+ TRANSFER_SIMPLE (m_DefaultWebPlayerQuality);
+ TRANSFER_SIMPLE (m_DefaultMobileQuality);
+ transfer.Transfer (m_CurrentQuality, "m_EditorQuality", kSimpleEditorMask);
+
+ #if UNITY_EDITOR
+ for (int i=1; i<kBuildPlayerTypeCount; i++)
+ {
+ string target = GetBuildTargetGroupName((BuildTargetPlatform)i, false);
+
+ if (target == "")
+ continue;
+
+ PerPlatformDefaultQuality::iterator found = m_PerPlatformDefaultQuality.find(target);
+ if (found == m_PerPlatformDefaultQuality.end())
+ {
+ if (target == "Web")
+ m_PerPlatformDefaultQuality.insert (pair<string,int>(target, m_DefaultWebPlayerQuality));
+ else if (target == "Standalone")
+ m_PerPlatformDefaultQuality.insert (pair<string,int>(target, m_DefaultStandaloneQuality));
+ else if (target == "iPhone" || target == "Android" || target == "BlackBerry" || target == "Tizen")
+ m_PerPlatformDefaultQuality.insert (pair<string,int>(target, m_DefaultMobileQuality));
+ else
+ m_PerPlatformDefaultQuality.insert (pair<string,int>(target, 3));
+ }
+ }
+ #else
+ #if WEBPLUG
+ m_CurrentQuality = m_DefaultWebPlayerQuality;
+ #elif ( (UNITY_ANDROID) || (UNITY_IPHONE) || (UNITY_BB10) || (UNITY_TIZEN) )
+ m_CurrentQuality = m_DefaultMobileQuality;
+ #else
+ m_CurrentQuality = m_DefaultStandaloneQuality;
+ #endif
+ #endif
+
+ if (m_QualitySettings.size () == 6)
+ {
+ m_QualitySettings[0].name = "Fastest";
+ m_QualitySettings[1].name = "Fast";
+ m_QualitySettings[2].name = "Simple";
+ m_QualitySettings[3].name = "Good";
+ m_QualitySettings[4].name = "Beautiful";
+ m_QualitySettings[5].name = "Fantastic";
+ }
+
+ }
+
+ if (transfer.IsVersionSmallerOrEqual(3))
+ {
+ QualitySetting tempQuality[kDefaultQualitySettingCount];
+ InitializeDefaultQualitySettings(tempQuality);
+#if UNITY_EDITOR
+ InitializePerPlatformDefaults (&m_PerPlatformDefaultQuality);
+#endif
+ transfer.Transfer (tempQuality[0], "Fastest", kSimpleEditorMask);
+ transfer.Transfer (tempQuality[1], "Fast", kSimpleEditorMask);
+ transfer.Transfer (tempQuality[2], "Simple", kSimpleEditorMask);
+ transfer.Transfer (tempQuality[3], "Good", kSimpleEditorMask);
+ transfer.Transfer (tempQuality[4], "Beautiful", kSimpleEditorMask);
+ transfer.Transfer (tempQuality[5], "Fantastic", kSimpleEditorMask);
+
+ // Unity 3.3 only supported Close Fit shadow projection
+ // In Unity 3.4 the default value is Stable Fit shadows.
+ if (transfer.IsVersionSmallerOrEqual(2))
+ {
+ for (int i = 0; i < kDefaultQualitySettingCount; i++)
+ tempQuality[i].shadowProjection = kShadowProjCloseFit;
+ }
+
+ m_QualitySettings.assign(tempQuality, tempQuality + kDefaultQualitySettingCount);
+ }
+
+ if (transfer.IsSerializingForGameRelease ())
+ {
+ TRANSFER(m_StrippedMaximumLODLevel);
+ }
+}
+
+void QualitySettings::SetPixelLightCount(int light)
+{
+ m_QualitySettings[m_CurrentQuality].pixelLightCount = light;
+
+ SetDirty();
+}
+
+void QualitySettings::SetShadowProjection(ShadowProjection shadowProjection)
+{
+ int projection = clamp<int>(shadowProjection, 0, kShadowProjectionCount-1);
+ m_QualitySettings[m_CurrentQuality].shadowProjection = projection;
+ SetDirty();
+}
+
+void QualitySettings::SetShadowCascades(int cascades)
+{
+ // valid cascade counts are 1, 2, 4
+ cascades = clamp(cascades,1,4);
+ if (cascades == 3)
+ cascades = 2;
+
+ m_QualitySettings[m_CurrentQuality].shadowCascades = cascades;
+ SetDirty();
+}
+
+// Used by the SceneView in order to temporarily override the shadow distance when rendering in ortho mode
+void QualitySettings::SetShadowDistanceTemporarily(float shadowDistance)
+{
+ shadowDistance = std::max ( shadowDistance, 0.0f );
+ m_QualitySettings[m_CurrentQuality].shadowDistance = shadowDistance;
+}
+
+void QualitySettings::SetShadowDistance(float shadowDistance)
+{
+ shadowDistance = std::max ( shadowDistance, 0.0f );
+ m_QualitySettings[m_CurrentQuality].shadowDistance = shadowDistance;
+ SetDirty();
+}
+
+void QualitySettings::SetSoftVegetation (bool softVegetation)
+{
+ m_QualitySettings[m_CurrentQuality].softVegetation = softVegetation;
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty ();
+}
+
+void QualitySettings::SetSoftParticles (bool soft)
+{
+ m_QualitySettings[m_CurrentQuality].softParticles = soft;
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty ();
+}
+
+void QualitySettings::SetVSyncCount (int count)
+{
+ if (m_QualitySettings[m_CurrentQuality].vSyncCount == count)
+ return;
+ m_QualitySettings[m_CurrentQuality].vSyncCount = count;
+ CheckConsistency();
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty ();
+ ApplyExpensiveSettings();
+}
+
+void QualitySettings::SetAntiAliasing (int aaCount)
+{
+ if (m_QualitySettings[m_CurrentQuality].antiAliasing == aaCount)
+ return;
+ m_QualitySettings[m_CurrentQuality].antiAliasing = aaCount;
+ CheckConsistency();
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty ();
+ ApplyExpensiveSettings();
+}
+
+void QualitySettings::SetAnisotropicTextures (int aniso)
+{
+ if (m_QualitySettings[m_CurrentQuality].anisotropicTextures == aniso)
+ return;
+ m_QualitySettings[m_CurrentQuality].anisotropicTextures = aniso;
+ CheckConsistency();
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty ();
+ ApplySettings();
+}
+
+void QualitySettings::SetLODBias (float lodBias)
+{
+ if (m_QualitySettings[m_CurrentQuality].lodBias == lodBias)
+ return;
+ m_QualitySettings[m_CurrentQuality].lodBias = lodBias;
+ CheckConsistency();
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty ();
+ ApplySettings();
+}
+
+void QualitySettings::SetMaximumLODLevel (int maximumLODLevel)
+{
+ if (m_QualitySettings[m_CurrentQuality].maximumLODLevel == maximumLODLevel)
+ return;
+ m_QualitySettings[m_CurrentQuality].maximumLODLevel = maximumLODLevel;
+ CheckConsistency();
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty ();
+ ApplySettings();
+}
+
+void QualitySettings::SetParticleRaycastBudget (int particleRaycastBudget)
+{
+ if (m_QualitySettings[m_CurrentQuality].particleRaycastBudget == particleRaycastBudget)
+ return;
+ m_QualitySettings[m_CurrentQuality].particleRaycastBudget = particleRaycastBudget;
+ CheckConsistency();
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty ();
+ ApplySettings();
+}
+
+void QualitySettings::SetBlendWeights(int weights)
+{
+ if (m_QualitySettings[m_CurrentQuality].blendWeights == weights)
+ return;
+ m_QualitySettings[m_CurrentQuality].blendWeights = weights;
+ CheckConsistency();
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty();
+ ApplySettings();
+}
+
+void QualitySettings::SetMasterTextureLimit(int limit)
+{
+ if (m_QualitySettings[m_CurrentQuality].textureQuality == limit)
+ return;
+ m_QualitySettings[m_CurrentQuality].textureQuality = limit;
+ CheckConsistency();
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty();
+ ApplySettings();
+}
+
+void QualitySettings::InitializeClass()
+{
+}
+
+float QualitySettings::GetShadowDistanceForRendering()
+{
+#if UNITY_EDITOR
+ if (GetLightmapVisualization().GetEnabled())
+ return GetLightmapVisualization().GetShadowDistance();
+ else
+ return GetQualitySettings().GetCurrent().shadowDistance;
+#else
+ return GetQualitySettings().GetCurrent().shadowDistance;
+#endif
+}
+
+#if UNITY_EDITOR
+
+void QualitySettings::SetQualitySettings(const QualitySetting* settings, int size, int currentQuality)
+{
+ m_QualitySettings.assign(settings, settings + size);
+ m_CurrentQuality = currentQuality;
+ CheckConsistency();
+ AwakeFromLoad(kDefaultAwakeFromLoad);
+ SetDirty();
+}
+
+void QualitySettings::DeletePerPlatformDefaultQuality()
+{
+ m_PerPlatformDefaultQuality.clear();
+}
+
+int QualitySettings::GetDefaultQualityForPlatform (const std::string& platformName)
+{
+ PerPlatformDefaultQuality::iterator found = m_PerPlatformDefaultQuality.find(platformName);
+ if (found == m_PerPlatformDefaultQuality.end())
+ return -1;
+ else
+ return found->second;
+}
+
+#endif
diff --git a/Runtime/Misc/QualitySettings.h b/Runtime/Misc/QualitySettings.h
new file mode 100644
index 0000000..e983771
--- /dev/null
+++ b/Runtime/Misc/QualitySettings.h
@@ -0,0 +1,133 @@
+#ifndef QUALITYSETTINGS_H
+#define QUALITYSETTINGS_H
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+enum ShadowProjection
+{
+ kShadowProjCloseFit = 0,
+ kShadowProjStableFit,
+ kShadowProjectionCount
+};
+
+
+class QualitySettings : public GlobalGameManager {
+public:
+ REGISTER_DERIVED_CLASS (QualitySettings, GlobalGameManager)
+ DECLARE_OBJECT_SERIALIZE (QualitySettings)
+
+ QualitySettings(MemLabelId label, ObjectCreationMode mode);
+
+ virtual void Reset ();
+
+ int GetCurrentIndex () const { return m_CurrentQuality;}
+ bool SetCurrent (string setting, bool applyExpensiveChanges);
+ void SetCurrentIndex (int setting, bool applyExpensiveChanges);
+
+ std::vector<std::string> GetQualitySettingsNames ();
+
+ virtual void CheckConsistency ();
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ void ApplySettings( int previousIndex = -1, bool applyExpensiveChanges = false );
+
+ struct QualitySetting
+ {
+ DECLARE_SERIALIZE (QualitySetting)
+ QualitySetting();
+
+ UnityStr name;
+
+ int pixelLightCount; ///< Number of pixel lights to use
+ int shadows; ///< enum { Disable Shadows = 0, Hard Shadows Only = 1, Hard and Soft Shadows = 2 } Shadow quality
+ int shadowResolution; ///< enum { Low Resolution = 0, Medium Resolution = 1, High Resolution = 2, Very High Resolution = 3 } Shadow resolution
+ int shadowProjection; ///< enum { Close Fit = 0, Stable Fit = 1 } Shadow projection
+ int shadowCascades; ///< enum { No Cascades = 1, Two Cascades = 2, Four Cascades = 4 } Number of cascades for directional light shadows
+ float shadowDistance; ///< Shadow drawing distance
+ int blendWeights; ///< enum { 1 Bone=1, 2 Bones=2, 4 Bones = 4 } Bone count for mesh skinning
+ int textureQuality; ///< enum { Full Res = 0, Half Res = 1, Quarter Res = 2, Eighth Res = 3} Base texture level
+ int anisotropicTextures; ///< enum { Disabled = 0, Per Texture = 1, Forced On = 2 } When to enable anisotropic texturing
+ int antiAliasing; ///< enum { Disabled = 0, 2x Multi Sampling = 2, 4x Multi Sampling = 4, 8x Multi Sampling = 8 } Screen anti aliasing
+ int vSyncCount; ///< enum { Don't Sync = 0, Every VBlank = 1, Every Second VBlank = 2 } Limit refresh rate to avoid tearing
+ bool softParticles; ///< Use soft blending for particles?
+ bool softVegetation; ///< Use soft shading for terrain vegetation?
+ float lodBias;
+ int maximumLODLevel;
+ int particleRaycastBudget; ///< Number of rays to cast for approximate world collisions
+
+ #if UNITY_EDITOR
+ std::vector<UnityStr> excludedTargetPlatforms;
+ #endif
+ };
+
+ enum ShadowQuality
+ {
+ kShadowsDisable = 0,
+ kShadowsHardOnly,
+ kShadowsAll,
+ kShadowQualityCount
+ };
+ enum
+ {
+ kShadowResolutionCount = 4,
+ };
+
+ const QualitySetting& GetCurrent() const { return m_QualitySettings[m_CurrentQuality]; }
+ const QualitySetting& GetQualityForIndex(int index) const { Assert ( index < m_QualitySettings.size() ); return m_QualitySettings[index]; }
+ int GetQualitySettingsCount() const { return m_QualitySettings.size(); }
+
+ void SetPixelLightCount(int light);
+ void SetShadowProjection(ShadowProjection shadowProjection);
+ void SetShadowCascades(int cascades);
+ void SetShadowDistance(float shadowDistance);
+ void SetShadowDistanceTemporarily(float shadowDistance);
+ void SetSoftVegetation (bool softVegetation);
+ void SetSoftParticles (bool soft);
+ void SetVSyncCount (int count);
+ void SetAntiAliasing (int aaSettings);
+ void SetAnisotropicTextures (int aniso);
+ void SetLODBias (float lodBias);
+ void SetMaximumLODLevel (int maximumLODLevel);
+ void SetParticleRaycastBudget (int particleRaycastBudget);
+ void SetBlendWeights(int weights);
+ void SetMasterTextureLimit(int limit);
+
+ /// The stripped maximum LOD level is calculated from the supported quality settings for this platform when entering playmode & building a player.
+ /// It always acts as the absolute minimum for LOD's
+ void SetStrippedMaximumLODLevel (int maximumLODLevel) { m_StrippedMaximumLODLevel = maximumLODLevel; }
+ int GetStrippedMaximumLODLevel () { return m_StrippedMaximumLODLevel; }
+
+ #if UNITY_EDITOR
+ typedef std::map<UnityStr, int> PerPlatformDefaultQuality;
+ void SetQualitySettings(const QualitySetting* settings, int size, int defaultQuality);
+ void DeletePerPlatformDefaultQuality ();
+ int GetDefaultQualityForPlatform (const std::string& platformName);
+ #endif
+
+ static float GetShadowDistanceForRendering();
+
+ static void InitializeClass();
+ static void CleanupClass() {}
+
+private:
+
+ std::vector<QualitySetting> m_QualitySettings;
+
+ int m_StrippedMaximumLODLevel;
+ int m_CurrentQuality;
+
+ void ApplyExpensiveSettings();
+
+ #if UNITY_EDITOR
+ PerPlatformDefaultQuality m_PerPlatformDefaultQuality;
+ int m_PreviousAA;
+
+ int m_PreviousVSync;
+ #endif
+};
+QualitySettings& GetQualitySettings ();
+QualitySettings* GetQualitySettingsPtr ();
+
+
+#endif
diff --git a/Runtime/Misc/RegisterAllClasses.h b/Runtime/Misc/RegisterAllClasses.h
new file mode 100644
index 0000000..102289f
--- /dev/null
+++ b/Runtime/Misc/RegisterAllClasses.h
@@ -0,0 +1,6 @@
+#ifndef REGISTER_ALL_CLASSES_H
+#define REGISTER_ALL_CLASSES_H
+
+void RegisterAllClasses();
+
+#endif
diff --git a/Runtime/Misc/ReproductionLog.cpp b/Runtime/Misc/ReproductionLog.cpp
new file mode 100644
index 0000000..9a84ada
--- /dev/null
+++ b/Runtime/Misc/ReproductionLog.cpp
@@ -0,0 +1,706 @@
+#include "UnityPrefix.h"
+#include "ReproductionLog.h"
+
+#if SUPPORT_REPRODUCE_LOG
+#include "Runtime/Input/InputManager.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/IMGUI/GUIManager.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Misc/CaptureScreenshot.h"
+#include "Runtime/Misc/Player.h"
+#include "Runtime/Export/WWW.h"
+#include "PlatformDependent/CommonWebPlugin/UnityWebStream.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/GfxDevice/GfxDeviceSetup.h"
+#include <fstream>
+#include "Runtime/Math/Random/Random.h"
+
+#if UNITY_OSX || UNITY_LINUX
+#include <sys/stat.h>
+#endif
+
+
+using namespace std;
+struct RemappedWWWStreamData;
+
+static std::ifstream* gReproduceInStream = NULL;
+static std::ofstream* gReproduceOutStream = NULL;
+
+static std::string gReproductionPath;
+#if SUPPORT_REPRODUCE_LOG_GFX_TRACE
+static FILE* gReproduceGfxLogFile = NULL;
+#endif
+int gScreenshotCount = 0;
+bool gDisableScreenShots = false;
+bool gNormalPlaybackSpeed = false;
+bool gRepeatScreenshots = true;
+int gLastScreenshotTime = 0;
+int gReproduceVersion = REPRODUCE_VERSION;
+ReproduceMode gReproMode = kPlaybackUninitialized;
+RemappedWWWStreamData* gRemappedWWWStreamData = NULL;
+Mutex gWaitForCompletionDownloadsMutex;
+set<WWW*> gWaitForCompletionDownloads;
+
+extern Rand gScriptingRand;
+
+ReproduceMode GetReproduceMode()
+{
+ return gReproMode;
+}
+
+void ReproductionWriteExitMessage(int result)
+{
+ if (result == 0)
+ {
+ LogString("Successfully reached end of reproduction log");
+ }
+ else
+ {
+ LogString("Aborting Reproduction playback");
+ }
+}
+
+void ReproductionExitPlayer (int result, bool writeExitMessage)
+{
+ if (writeExitMessage)
+ ReproductionWriteExitMessage(result);
+
+#if UNITY_OSX
+ FinishAllCaptureScreenshot ();
+
+ if (result == 0)
+ {
+ if (!PlayerCleanup(true,true))
+ ErrorString("Failed to clean up player");
+ }
+
+ // On Safari 64-bit, the browser is a seperate process. Take that down as well.
+ CFStringRef pluginHostID = CFSTR("com.apple.WebKit.PluginHost");
+ CFStringRef appID = CFBundleGetIdentifier(CFBundleGetMainBundle());
+ if (CFStringCompare(pluginHostID, appID, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+ {
+ printf_console("killing Safari\n");
+ system("killall Safari");
+ }
+#endif
+ exit(result);
+}
+
+void FailReproduction (const std::string& err)
+{
+ ErrorString(Format("%s\nFrame: %d", err.c_str(), GetTimeManager().GetFrameCount()));
+ ReproductionExitPlayer(1);
+}
+
+struct RemappedWWWStreamData
+{
+ string absoluteUrl;
+ string srcValue;
+ int width;
+ int height;
+ vector<pair<string, string> > downloads;
+ int remapCount;
+
+ RemappedWWWStreamData () : remapCount(0) {}
+
+ void Write ()
+ {
+ std::ofstream stream (AppendPathName(gReproductionPath, "WWWRemap.log").c_str());
+ stream.setf(std::ios::fixed,std::ios::floatfield);
+ stream.precision(15);
+
+ stream << "absoluteUrl= " << absoluteUrl << endl;
+ stream << "srcValue= " << srcValue << endl;
+ stream << "width= " << width << endl;
+ stream << "height= " << height << endl;
+ for(int i=0;i<downloads.size();i++)
+ {
+ stream << "WWW= " << downloads[i].first << endl;
+ stream << "Remap= " << downloads[i].second << endl;
+ }
+ }
+
+ void Parse (std::ifstream& stream)
+ {
+ string temp;
+ stream >> temp;
+ if (temp == "absoluteUrl=")
+ stream >> absoluteUrl;
+ else
+ FailReproduction("Failed parsing absolute url" + temp);
+
+ stream >> temp;
+ if (temp == "srcValue=")
+ stream >> srcValue;
+ else
+ FailReproduction("Failed parsing src value" + temp);
+
+ stream >> temp; if (temp != "width=") FailReproduction("Failed parsing width"); stream >> width;
+ stream >> temp; if (temp != "height=") FailReproduction("Failed parsing height"); stream >> height;
+ while (true)
+ {
+ stream >> temp;
+ if (stream.eof())
+ break;
+
+ if (!BeginsWith(temp, "WWW="))
+ FailReproduction ("Failed parsing WWW GOT:" + temp);
+ string url;
+ stream >> url;
+
+ stream >> temp;
+ if (!BeginsWith(temp, "Remap="))
+ FailReproduction ("Failed parsing Remap");
+ string remap;
+ stream >> remap;
+
+ downloads.push_back(make_pair(url, remap));
+ }
+ }
+};
+
+void SetReproduceMode (string reproductionLogFolder)
+{
+ string reproductionLogPath = AppendPathName(reproductionLogFolder, "Reproduction.log");
+ string remapLogPath = AppendPathName(reproductionLogFolder, "WWWRemap.log");
+
+ if (gReproMode == kGenerateReproduceLog || gReproMode == kGenerateReproduceLogAndRemapWWW)
+ {
+ gReproduceOutStream = new std::ofstream (reproductionLogPath.c_str());
+ gReproduceOutStream->setf(std::ios::fixed,std::ios::floatfield);
+ gReproduceOutStream->precision(15);
+
+ if (gReproMode == kGenerateReproduceLogAndRemapWWW)
+ {
+ gRemappedWWWStreamData = new RemappedWWWStreamData();
+ }
+
+ gScriptingRand.SetSeed(0);
+ }
+ else if (gReproMode == kPlaybackReproduceLog)
+ {
+ gReproduceInStream = new std::ifstream (reproductionLogPath.c_str());
+ gReproduceInStream->setf(std::ios::fixed,std::ios::floatfield);
+ gReproduceInStream->precision(15);
+
+ std::ifstream stream (remapLogPath.c_str());
+ if (stream.is_open())
+ {
+ stream.setf(std::ios::fixed,std::ios::floatfield);
+ stream.precision(15);
+
+ gRemappedWWWStreamData = new RemappedWWWStreamData();
+ gRemappedWWWStreamData->Parse(stream);
+ }
+
+ gScriptingRand.SetSeed(0);
+ }
+}
+
+void ReproduceVersion()
+{
+ if (gReproMode == kGenerateReproduceLog || gReproMode == kGenerateReproduceLogAndRemapWWW)
+ {
+ //set version
+ *gReproduceOutStream << "Version" << " " << REPRODUCE_VERSION;
+ }
+ else if (gReproMode == kPlaybackReproduceLog)
+ {
+ //get version
+
+ if (gReproduceInStream->peek() == 'V')
+ {
+ std::string testVersion;
+ *gReproduceInStream >> testVersion;
+ *gReproduceInStream >> gReproduceVersion;
+ }
+ else
+ {
+ gReproduceVersion = 1;
+ }
+ }
+}
+
+int GetReproduceVersion()
+{
+ return gReproduceVersion;
+}
+
+string GetScreenshotPath()
+{
+ string reproductionPathTemp = AppendPathName( gReproductionPath, "/Images/" );
+ string imgOut = Format("%d.png", gScreenshotCount);
+ reproductionPathTemp = AppendPathName( reproductionPathTemp, imgOut );
+ gScreenshotCount++;
+
+ return reproductionPathTemp;
+}
+
+void CaptureScreenshotReproduction(bool manual)
+{
+ if(manual)
+ QueueScreenshot (GetScreenshotPath(), 0);
+
+ if(gDisableScreenShots)
+ return;
+
+ if(GetInputManager().GetKeyDown(SDLK_F6) && gReproduceOutStream)
+ gRepeatScreenshots = !gRepeatScreenshots;
+
+ if(gReproduceInStream || gReproduceOutStream)
+ {
+ if(GetInputManager().GetKey(SDLK_F5))
+ {
+ QueueScreenshot (GetScreenshotPath(), 0);
+ #if SUPPORT_REPRODUCE_LOG_GFX_TRACE
+ fclose(gReproduceGfxLogFile);
+ gReproduceGfxLogFile = NULL;
+ #endif
+ }
+ }
+}
+
+bool HasNormalPlaybackSpeed()
+{
+ return gNormalPlaybackSpeed;
+}
+
+void BatchInitializeReproductionLog ()
+{
+ if (gReproMode != kPlaybackUninitialized)
+ return;
+ gReproMode = kNormalPlayback;
+
+ std::string instructionsFilePath;
+#if UNITY_OSX
+ string result = getenv ("HOME");
+ if (!result.empty())
+ {
+ instructionsFilePath = AppendPathName( result, "Library/Logs/Unity/GlobalPlaybackInstructions.log");
+ }
+#elif UNITY_WINRT
+ // ?!-
+#elif UNITY_WIN
+ const char* tempPath = ::getenv("TEMP");
+ if (!tempPath)
+ tempPath = "C:";
+ instructionsFilePath = AppendPathName (tempPath, "UnityGlobalPlaybackInstructions.log");
+#elif UNITY_LINUX
+ string result = getenv ("HOME");
+ if (!result.empty())
+ {
+ instructionsFilePath = AppendPathName( result, ".unity/GlobalPlaybackInstructions.log");
+ }
+#else
+#error "Unknown platform"
+#endif
+
+ if (!instructionsFilePath.empty())
+ {
+ //@TODO: this won't work on Windows when user's name has non-ASCII characters!
+ std::ifstream instructionsFile (instructionsFilePath.c_str());
+
+ if (instructionsFile.is_open())
+ {
+ std::string line;
+ std::vector<std::string> lines;
+
+ while (!instructionsFile.eof())
+ {
+ std::getline (instructionsFile,line);
+ lines.push_back (line);
+ }
+
+ gReproductionPath = lines[0];
+
+ // The real instructionsfile is read to determine reproduction mode and playback.log path, other variables should be added here
+ string reproductionPlayerLogPathComplete = AppendPathName(gReproductionPath, "PlayerComplete.log");
+ LogOutputToSpecificFile(reproductionPlayerLogPathComplete.c_str());
+
+ for (size_t i = 0; i < lines.size(); i++)
+ {
+ if(strcmp(lines[i].c_str(),"-reproduceInput") == 0)
+ gReproMode = kPlaybackReproduceLog;
+ else if(strcmp(lines[i].c_str(),"-recordInput") == 0)
+ gReproMode = kGenerateReproduceLog;
+ else if(strcmp(lines[i].c_str(),"-recordInputAndRemapWWW") == 0)
+ gReproMode = kGenerateReproduceLogAndRemapWWW;
+
+ if(!strcmp(lines[i].c_str(),"-disableScreenshots"))
+ gDisableScreenShots = true;
+ if(!strcmp(lines[i].c_str(),"-normalPlaybackSpeed"))
+ gNormalPlaybackSpeed = true;
+
+ // Do we want REF D3D9 device?
+ #if GFX_SUPPORTS_D3D9 && ENABLE_FORCE_GFX_RENDERER
+ if(!strcmp(lines[i].c_str(),"-force-d3d9-ref"))
+ ::g_ForceD3D9RefDevice = true;
+ #endif
+ }
+
+ if (gReproMode == kNormalPlayback)
+ {
+ ErrorString("Failed setting Reproduction mode from reproduction log file");
+ }
+
+ SetReproduceMode (gReproductionPath);
+ ReproduceVersion();
+
+ // Kill the instructions file, so that it wont get reused next time you run Unity
+ instructionsFile.close();
+ DeleteFileOrDirectory(instructionsFilePath);
+ }
+ }
+}
+
+void CheckReproduceTagAndExit(const std::string& tag, std::ifstream& stream)
+{
+ if (!CheckReproduceTag(tag, stream))
+ FailReproduction("Failed to read: " + tag);
+}
+
+bool CheckReproduceTag(const std::string& tag, std::ifstream& stream)
+{
+ int position = stream.tellg();
+ string temp;
+ stream >> temp;
+ if (temp == tag)
+ return true;
+
+ stream.seekg(position, ios::beg);
+ return false;
+}
+
+
+
+void ReadWriteAbsoluteUrl(UnityStr& srcValue, UnityStr& absoluteUrl)
+{
+ if (gRemappedWWWStreamData)
+ {
+ if (GetReproduceMode() == kPlaybackReproduceLog)
+ {
+ absoluteUrl = gRemappedWWWStreamData->absoluteUrl;
+ srcValue = gRemappedWWWStreamData->srcValue;
+ }
+ else
+ {
+ gRemappedWWWStreamData->absoluteUrl = absoluteUrl;
+ gRemappedWWWStreamData->srcValue = srcValue;
+ }
+ }
+}
+
+void WriteWebplayerSize(int width, int height)
+{
+ if (GetReproduceMode() == kGenerateReproduceLogAndRemapWWW)
+ {
+ gRemappedWWWStreamData->width = width;
+ gRemappedWWWStreamData->height = height;
+ }
+}
+
+#if ENABLE_WWW
+
+void CleanupWWW (WWW* www)
+{
+ gWaitForCompletionDownloadsMutex.Lock();
+ gWaitForCompletionDownloads.erase(www);
+ gWaitForCompletionDownloadsMutex.Unlock();
+}
+
+
+
+bool ShouldWaitForCompletedDownloads ()
+{
+ if (!RunningReproduction())
+ return false;
+
+ gWaitForCompletionDownloadsMutex.Lock();
+ set<WWW*>& oldSet = gWaitForCompletionDownloads;
+ set<WWW*> newSet;
+ bool wait = false;
+ for (set<WWW*>::iterator i=oldSet.begin();i!=oldSet.end();++i)
+ {
+ WWW& www = **i;
+
+ #if WWW_USE_BROWSER
+ WWWBrowser* browser = static_cast<WWWBrowser*> (&www);
+ if (browser)
+ browser->ForceProgressDownload();
+ #endif
+
+ if(!www.IsDone() && ( www.GetUnityWebStream() == NULL || www.GetUnityWebStream()->GetProgressUntilLoadable() < 1 ))
+ {
+ newSet.insert(&www);
+ wait = true;
+ }
+ }
+ oldSet = newSet;
+ gWaitForCompletionDownloadsMutex.Unlock();
+
+ return wait;
+}
+
+void CreateWWWReproduce(WWW* www, const std::string& url, std::string& remappedUrl, int& postLength)
+{
+ remappedUrl = url;
+
+ if (RunningReproduction())
+ {
+ gWaitForCompletionDownloadsMutex.Lock();
+ gWaitForCompletionDownloads.insert(www);
+ gWaitForCompletionDownloadsMutex.Unlock();
+ }
+
+
+ // Play back from remap data
+ if (gRemappedWWWStreamData && GetReproduceMode() == kPlaybackReproduceLog)
+ {
+ int downloadNumber = gRemappedWWWStreamData->remapCount;
+ if (downloadNumber >= gRemappedWWWStreamData->downloads.size())
+ {
+ FailReproduction("Failed remapping download because the content is trying to downloading more WWW than the original reproduction log did:" + url);
+ return;
+ }
+
+ remappedUrl = Format("WWW-%d.bin", downloadNumber);
+ postLength = 0;
+
+ if (gRemappedWWWStreamData->downloads[downloadNumber].first != url)
+ FailReproduction("Failed remapping download because the content is trying to download a different url than it originally did:" + url + "\nexpected: " + gRemappedWWWStreamData->downloads[downloadNumber].first);
+ if (gRemappedWWWStreamData->downloads[downloadNumber].second != remappedUrl && gRemappedWWWStreamData->downloads[downloadNumber].second != string("INCOMPLETE"))
+ FailReproduction("Failed remapping download because the remapped name is not matching correctly:" + url);
+ gRemappedWWWStreamData->remapCount++;
+ }
+ // Setup remap data from downloaded content
+ else if (gRemappedWWWStreamData && GetReproduceMode() == kGenerateReproduceLogAndRemapWWW)
+ {
+ *www->GetReproRemapCount() = gRemappedWWWStreamData->remapCount;
+ gRemappedWWWStreamData->downloads.push_back(make_pair(url, string("INCOMPLETE")));
+ gRemappedWWWStreamData->remapCount++;
+ }
+
+}
+
+void CompleteWWWReproduce(WWW* www, const std::string& url, const UInt8* buffer, int size)
+{
+ // Store downloaded www data
+ if (GetReproduceMode() == kGenerateReproduceLogAndRemapWWW)
+ {
+ int downloadNumber = *www->GetReproRemapCount();
+ string fileName = Format("WWW-%d.bin", downloadNumber);
+ if (!WriteBytesToFile(buffer, size, AppendPathName(gReproductionPath, fileName)))
+ FailReproduction("Failed writing completed WWW");
+
+ gRemappedWWWStreamData->downloads[downloadNumber].second = fileName;
+ }
+}
+
+#endif
+
+void ReproduceWriteMainDataFile(const UInt8* buffer, int size)
+{
+ if (GetReproduceMode() == kGenerateReproduceLogAndRemapWWW)
+ {
+ if (!WriteBytesToFile(buffer, size, AppendPathName(gReproductionPath, "main.unity3d")))
+ FailReproduction("Failed writing completed WWW");
+ }
+}
+
+void ReadWriteReproductionInput()
+{
+ if (gReproduceInStream)
+ {
+ GetInputManager().ReadLog(*gReproduceInStream);
+ GetGUIManager().ReadLog(*gReproduceInStream);
+ }
+ else if (gReproduceOutStream)
+ {
+ GetInputManager().WriteLog(*gReproduceOutStream);
+ GetGUIManager().WriteLog(*gReproduceOutStream);
+ }
+
+#if SUPPORT_REPRODUCE_LOG_GFX_TRACE
+ if(GetInputManager().GetKey(SDLK_F5))
+ {
+ string reproductionPathTemp = AppendPathName( gReproductionPath, "/Images/" );
+ string imgOut = Format("%d.png", gScreenshotCount);
+ reproductionPathTemp = AppendPathName( reproductionPathTemp, imgOut );
+ gReproduceGfxLogFile = fopen(reproductionPathTemp.c_str(), "w");
+ }
+#endif
+}
+
+void ReadWriteReproductionTime()
+{
+ if (gReproduceInStream)
+ GetTimeManager().ReadLog(*gReproduceInStream);
+ else if (gReproduceOutStream)
+ {
+ GetTimeManager().WriteLog(*gReproduceOutStream);
+ }
+}
+
+std::ifstream* GetReproduceInStream ()
+{
+ return gReproduceInStream;
+}
+
+std::ofstream* GetReproduceOutStream ()
+{
+ return gReproduceOutStream;
+}
+
+void RepeatReproductionScreenshot()
+{
+ //making repeating screenshots
+ //adjust modulus to make it repeat screenshotting often
+ if (gRepeatScreenshots && gReproduceOutStream)
+ {
+ bool takeScreenshot = false;
+ int ms = RoundfToIntPos(GetTimeSinceStartup() * 1000);
+ if (ms - gLastScreenshotTime > 500)
+ {
+ gLastScreenshotTime = ms;
+ takeScreenshot = true;
+ }
+ GetInputManager().SetKeyState(SDLK_F5, takeScreenshot);
+ }
+}
+
+void PlayerCleanupReproduction()
+{
+ if (GetReproduceMode() == kGenerateReproduceLogAndRemapWWW)
+ gRemappedWWWStreamData->Write();
+
+ if (gReproduceOutStream)
+ gReproduceOutStream->flush();
+}
+
+bool gShouldExit = false;
+
+bool ShouldExitReproduction()
+{
+ return gShouldExit;
+}
+
+void ReadWriteReproductionEnd()
+{
+ if (gReproduceInStream)
+ {
+ string test;
+ *gReproduceInStream >> test;
+ if (test != "EndOfFrame")
+ {
+ ErrorString("Error reading reproduce EndOfFrame tag in Reproduction log. Forwarding to next EndOfFrame.");
+
+ while (!gReproduceInStream->eof() && test != "EndOfFrame")
+ {
+ *gReproduceInStream >> test;
+ }
+ }
+
+ if (gReproduceInStream->eof())
+ {
+ delete gReproduceInStream;
+ gReproduceInStream = NULL;
+ gShouldExit = true;
+ }
+ }
+ else if (gReproduceOutStream)
+ {
+ *gReproduceOutStream << "EndOfFrame";
+ }
+}
+
+void WriteReproductionString (std::ostream& out, const std::string& value)
+{
+ int size = value.size();
+ out << size;
+ for (int i=0;i<size;i++)
+ out << ' ' << (int)value[i];
+ out << std::endl;
+}
+
+void ReadReproductionString (std::istream& in, string& value)
+{
+ int size = 0;
+ in >> size;
+ value.resize(size);
+ for (int i=0;i<value.size();i++)
+ {
+ int val = 0;
+ in >> val;
+ value[i] = val;
+ }
+}
+
+#if SUPPORT_REPRODUCE_LOG_GFX_TRACE
+void LogToScreenshotLog(string str)
+{
+ if (gReproduceGfxLogFile != NULL)
+ {
+ fprintf(gReproduceGfxLogFile, str.c_str());
+ fprintf(gReproduceGfxLogFile, "\n");
+ }
+}
+#endif
+
+bool RunningReproduction()
+{
+ if (gReproduceInStream != NULL || gReproduceOutStream != NULL)
+ return true;
+ else
+ return false;
+}
+
+std::string GetReproductionDirectory()
+{
+ return gReproductionPath;
+}
+
+
+void WriteFloat (std::ostream& out, float& value)
+{
+ int intValue = RoundfToInt(value * 1000.0);
+ out << intValue;
+ value = intValue / 1000.0;
+}
+
+void ReadFloat (std::istream& in, float& value)
+{
+ int intValue = 0;
+ in >> intValue;
+ value = intValue / 1000.0;
+}
+
+void WriteFloat (std::ostream& out, double& value)
+{
+ int intValue = RoundfToInt(value * 1000.0);
+ out << intValue;
+ value = intValue / 1000.0;
+}
+
+void ReadFloat (std::istream& in, double& value)
+{
+ int intValue = 0;
+ in >> intValue;
+ value = intValue / 1000.0;
+}
+
+void WriteBigFloat (std::ostream& out, double& value)
+{
+ SInt64 intValue = RoundfToInt(value * 1000000.0);
+ out << intValue;
+ value = intValue / 1000000.0;
+}
+
+void ReadBigFloat (std::istream& in, double& value)
+{
+ SInt64 intValue = 0;
+ in >> intValue;
+ value = intValue / 1000000.0;
+}
+
+#endif
diff --git a/Runtime/Misc/ReproductionLog.h b/Runtime/Misc/ReproductionLog.h
new file mode 100644
index 0000000..b729572
--- /dev/null
+++ b/Runtime/Misc/ReproductionLog.h
@@ -0,0 +1,80 @@
+#ifndef REPRODUCTIONLOG_H
+#define REPRODUCTIONLOG_H
+
+#include "Configuration/UnityConfigure.h"
+
+#if SUPPORT_REPRODUCE_LOG
+
+#define REPRODUCE_VERSION 6
+
+enum ReproduceMode { kPlaybackUninitialized = -1,
+ kNormalPlayback = 0,
+ kGenerateReproduceLog = 1,
+ kPlaybackReproduceLog = 2,
+ kGenerateReproduceLogAndRemapWWW = 3 };
+
+class WWW;
+
+ReproduceMode GetReproduceMode();
+int GetReproduceVersion();
+void CaptureScreenshotReproduction(bool manual);
+bool HasNormalPlaybackSpeed();
+void BatchInitializeReproductionLog();
+void ReadWriteAbsoluteUrl(UnityStr& srcValue, UnityStr& absoluteUrl);
+void WriteWebplayerSize(int width, int height);
+void ReadWriteReproductionInput();
+void ReadWriteReproductionTime();
+
+void CreateWWWReproduce(WWW* www, const std::string& url, std::string& remappedUrl, int &postSize);
+void CompleteWWWReproduce(WWW* www, const std::string& url, const UInt8* buffer, int size);
+
+void ReproductionWriteExitMessage(int result);
+void ReproductionExitPlayer (int result, bool writeExitMessage=true);
+bool ShouldExitReproduction();
+
+std::string GetReproductionDirectory();
+void RepeatReproductionScreenshot();
+void ReadWriteReproductionEnd();
+bool RunningReproduction();
+FILE* GetReproduceOutputLogFile();
+void PlayerCleanupReproduction();
+void ReproduceWriteMainDataFile(const UInt8* buffer, int size);
+
+void WriteReproductionString (std::ostream& out, const std::string& value);
+void ReadReproductionString (std::istream& in, std::string& value);
+
+bool ShouldWaitForCompletedDownloads ();
+void CleanupWWW (WWW* www);
+
+
+std::ifstream* GetReproduceInStream ();
+std::ofstream* GetReproduceOutStream ();
+
+int GetReproduceVersion ();
+bool CheckReproduceTag(const std::string& tag, std::ifstream& stream);
+void CheckReproduceTagAndExit(const std::string& tag, std::ifstream& stream);
+
+void FailReproduction (const std::string& err);
+
+void WriteFloat (std::ostream& out, float& value);
+void ReadFloat (std::istream& in, float& value);
+
+void WriteFloat (std::ostream& out, double& value);
+void ReadFloat (std::istream& in, double& value);
+
+void WriteBigFloat (std::ostream& out, double& value);
+void ReadBigFloat (std::istream& in, double& value);
+
+#else
+
+inline bool RunningReproduction() { return false; }
+
+#endif
+
+#if SUPPORT_REPRODUCE_LOG && SUPPORT_REPRODUCE_LOG_GFX_TRACE
+void LogToScreenshotLog(std::string str);
+#else
+#define LogToScreenshotLog(x)
+#endif
+
+#endif //REPRODUCTIONLOG_H
diff --git a/Runtime/Misc/ResourceManager.cpp b/Runtime/Misc/ResourceManager.cpp
new file mode 100644
index 0000000..c30dc38
--- /dev/null
+++ b/Runtime/Misc/ResourceManager.cpp
@@ -0,0 +1,1292 @@
+#include "UnityPrefix.h"
+#include "ResourceManager.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Utilities/Argv.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+#include "Configuration/UnityConfigureOther.h"
+#include "Runtime/Misc/BuildSettings.h"
+#if ENABLE_MONO
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Mono/MonoManager.h"
+#endif
+#if UNITY_EDITOR
+#include "Editor/Src/GUIDPersistentManager.h"
+#include "Editor/Src/AssetPipeline/AssetDatabase.h"
+#include "Editor/Src/AssetPipeline/MonoCompilationPipeline.h"
+#include <vector>
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Editor/Src/Graphs/GraphUtils.h"
+#include "Editor/Src/EditorModules.h"
+#include "ResourceManagerGUIDs.h"
+#endif
+
+using namespace std;
+
+const char* kResourcePath = "Library/unity default resources";
+const char* kOldWebResourcePath = "Library/unity_web_old";
+
+static bool s_AreResourcesInitialized;
+
+#if UNITY_EDITOR
+static const char* kDefaultResourcesSource = "Resources/unity default resources";
+
+static const char* kEditorDefaultResourcesSource = "Resources/unity editor resources";
+const char* kEditorResourcePath = "Library/unity editor resources";
+const char* kDefaultExtraResourcesPath = "Resources/unity_builtin_extra";
+
+static BuiltinResourceManager* gBuiltinExtraResourceManager;
+
+static void RegisterBuiltinEditorScript (int instanceID, const char* className);
+static void RegisterBuiltinEditorInternalScript (int instanceID, const char* className);
+static void DoRegisterBuiltinEditorScript (int instanceID, const char* className, const char* _namespace, const char* assemblyName, bool isEditorScript);
+
+void BuiltinResourceManager::InitializeExtraResources ()
+{
+ m_ResourcePath = kDefaultExtraResourcesPath;
+ m_RequiredHideFlags = Object::kNotEditable;
+ m_AllowResourceManagerAccess = true;
+
+ // Issue RegisterResource shader calls in the expected loading order:
+ // if any shader references another (e.g. UsePass), the referenced must come first!
+
+ // Regular opaque
+ RegisterShader (6, "Normal-VertexLit.shader", "VertexLit");
+ RegisterShader (1, "Normal-DiffuseFast.shader", "Legacy Shaders/Diffuse Fast");
+ RegisterShader (7, "Normal-Diffuse.shader", "Diffuse");
+ RegisterShader (2, "Normal-Bumped.shader", "Bumped Diffuse");
+ RegisterShader (3, "Normal-Glossy.shader", "Specular");
+ RegisterShader (4, "Normal-BumpSpec.shader", "Bumped Specular");
+ RegisterShader (5, "Normal-DiffuseDetail.shader", "Diffuse Detail");
+ // Self-Illumin
+ RegisterShader (14, "Illumin-VertexLit.shader", "Self-Illumin/VertexLit");
+ RegisterShader (10, "Illumin-Diffuse.shader", "Self-Illumin/Diffuse");
+ RegisterShader (11, "Illumin-Bumped.shader", "Self-Illumin/Bumped Diffuse");
+ RegisterShader (12, "Illumin-Glossy.shader", "Self-Illumin/Specular");
+ RegisterShader (13, "Illumin-BumpSpec.shader", "Self-Illumin/Bumped Specular");
+ // Reflective
+ RegisterShader (24, "Reflect-VertexLit.shader", "Reflective/VertexLit");
+ RegisterShader (25, "Reflect-BumpNolight.shader", "Reflective/Bumped Unlit");
+ RegisterShader (20, "Reflect-Diffuse.shader", "Reflective/Diffuse");
+ RegisterShader (21, "Reflect-Bumped.shader", "Reflective/Bumped Diffuse");
+ RegisterShader (26, "Reflect-BumpVertexLit.shader", "Reflective/Bumped VertexLit");
+ RegisterShader (22, "Reflect-Glossy.shader", "Reflective/Specular");
+ RegisterShader (23, "Reflect-BumpSpec.shader", "Reflective/Bumped Specular");
+ // Transparent
+ RegisterShader (34, "Alpha-VertexLit.shader", "Transparent/VertexLit");
+ RegisterShader (30, "Alpha-Diffuse.shader", "Transparent/Diffuse");
+ RegisterShader (31, "Alpha-Bumped.shader", "Transparent/Bumped Diffuse");
+ RegisterShader (32, "Alpha-Glossy.shader", "Transparent/Specular");
+ RegisterShader (33, "Alpha-BumpSpec.shader", "Transparent/Bumped Specular");
+ // Transparent/Cutout
+ RegisterShader (50, "AlphaTest-VertexLit.shader", "Transparent/Cutout/VertexLit");
+ RegisterShader (51, "AlphaTest-Diffuse.shader", "Transparent/Cutout/Diffuse");
+ RegisterShader (52, "AlphaTest-Bumped.shader", "Transparent/Cutout/Bumped Diffuse");
+ RegisterShader (53, "AlphaTest-Glossy.shader", "Transparent/Cutout/Specular");
+ RegisterShader (54, "AlphaTest-BumpSpec.shader", "Transparent/Cutout/Bumped Specular");
+ // Parallax
+ RegisterShader (8, "Normal-Parallax.shader", "Parallax Diffuse");
+ RegisterShader (9, "Normal-ParallaxSpec.shader", "Parallax Specular");
+ RegisterShader (15, "Illumin-Parallax.shader", "Self-Illumin/Parallax Diffuse");
+ RegisterShader (16, "Illumin-ParallaxSpec.shader", "Self-Illumin/Parallax Specular");
+ RegisterShader (27, "Reflect-Parallax.shader", "Reflective/Parallax Diffuse");
+ RegisterShader (28, "Reflect-ParallaxSpec.shader", "Reflective/Parallax Specular");
+ RegisterShader (35, "Alpha-Parallax.shader", "Transparent/Parallax Diffuse");
+ RegisterShader (36, "Alpha-ParallaxSpec.shader", "Transparent/Parallax Specular");
+
+ // Misc
+ RegisterShader (100, "Decal.shader", "Decal");
+ RegisterShader (101, "Flare.shader", "FX/Flare");
+ RegisterShader (103, "skybox cubed.shader", "RenderFX/Skybox Cubed");
+ RegisterShader (104, "skybox.shader", "RenderFX/Skybox");
+
+ // Particle shaders
+ RegisterShader (200, "Particle Add.shader", "Particles/Additive");
+ RegisterShader (201, "Particle AddMultiply.shader", "Particles/~Additive-Multiply");
+ RegisterShader (202, "Particle AddSmooth.shader", "Particles/Additive (Soft)");
+ RegisterShader (203, "Particle Alpha Blend.shader", "Particles/Alpha Blended");
+ //RegisterResource (204, "Particle Alpha Blend Deprecated.shader", "Shader"); // obsolete
+ RegisterShader (205, "Particle Multiply.shader", "Particles/Multiply");
+ RegisterShader (206, "Particle MultiplyDouble.shader", "Particles/Multiply (Double)");
+ RegisterShader (207, "Particle Premultiply Blend.shader", "Particles/Alpha Blended Premultiply");
+ RegisterShader (208, "Particle VertexLit Blended.shader", "Particles/VertexLit Blended");
+
+#if ENABLE_SPRITES
+ // Sprite shaders
+ RegisterShader (10800, "Sprites-Diffuse.shader", "Sprites/Diffuse");
+#endif
+
+ // Various
+ RegisterShader (10512, "AlphaTest-SoftEdgeUnlit.shader", "Transparent/Cutout/Soft Edge Unlit");
+
+ // Terrain engine
+ RegisterShader (10500, "TerrainShaders/Details/VertexLit.shader", "Hidden/TerrainEngine/Details/Vertexlit", false);
+ RegisterShader (10501, "TerrainShaders/Details/WavingGrass.shader", "Hidden/TerrainEngine/Details/WavingDoublePass", false);
+ RegisterShader (10502, "TerrainShaders/Details/WavingGrassBillboard.shader", "Hidden/TerrainEngine/Details/BillboardWavingDoublePass", false);
+ RegisterShader (10505, "TerrainShaders/Splats/FirstPass.shader", "Nature/Terrain/Diffuse");
+ RegisterShader (10503, "TerrainShaders/Splats/AddPass.shader", "Hidden/TerrainEngine/Splatmap/Lightmap-AddPass", false);
+ RegisterShader (10507, "TerrainShaders/Trees/BillboardTree.shader", "Hidden/TerrainEngine/BillboardTree", false);
+ //RegisterResource (10513, "TerrainShaders/Trees/SH-Tree Leaves customLit.shader", "Shader"); // not used anymore
+ //RegisterResource (10514, "TerrainShaders/Trees/SH-Tree Bark customLit.shader", "Shader"); // not used anymore
+
+ // 2.x lightmap shaders
+ RegisterShader (40, "Lightmap-VertexLit.shader", "Legacy Shaders/Lightmapped/VertexLit");
+ RegisterShader (41, "Lightmap-Diffuse.shader", "Legacy Shaders/Lightmapped/Diffuse");
+ RegisterShader (42, "Lightmap-Bumped.shader", "Legacy Shaders/Lightmapped/Bumped Diffuse");
+ RegisterShader (43, "Lightmap-Glossy.shader", "Legacy Shaders/Lightmapped/Specular");
+ RegisterShader (44, "Lightmap-BumpSpec.shader", "Legacy Shaders/Lightmapped/Bumped Specular");
+
+ // 2.x tree shaders
+ RegisterShader (10508, "Nature/SoftOcclusion/TreeSoftOcclusionBarkRendertex.shader", "Hidden/Nature/Tree Soft Occlusion Bark Rendertex", false);
+ RegisterShader (10509, "Nature/SoftOcclusion/TreeSoftOcclusionBark.shader", "Nature/Tree Soft Occlusion Bark");
+ RegisterShader (10510, "Nature/SoftOcclusion/TreeSoftOcclusionLeavesRendertex.shader", "Hidden/Nature/Tree Soft Occlusion Leaves Rendertex", false);
+ RegisterShader (10511, "Nature/SoftOcclusion/TreeSoftOcclusionLeaves.shader", "Nature/Tree Soft Occlusion Leaves");
+
+ // Tree Creator shaders
+ RegisterShader (10600, "Nature/TreeCreator/TreeCreatorBark.shader", "Nature/Tree Creator Bark");
+ RegisterShader (10601, "Nature/TreeCreator/TreeCreatorLeaves.shader", "Nature/Tree Creator Leaves");
+ RegisterShader (10602, "Nature/TreeCreator/TreeCreatorBarkRendertex.shader", "Hidden/Nature/Tree Creator Bark Rendertex", false);
+ RegisterShader (10603, "Nature/TreeCreator/TreeCreatorLeavesRendertex.shader", "Hidden/Nature/Tree Creator Leaves Rendertex", false);
+ RegisterShader (10604, "Nature/TreeCreator/TreeCreatorBarkOptimized.shader", "Hidden/Nature/Tree Creator Bark Optimized", false);
+ RegisterShader (10605, "Nature/TreeCreator/TreeCreatorLeavesOptimized.shader", "Hidden/Nature/Tree Creator Leaves Optimized", false);
+
+ RegisterShader (10606, "Nature/TreeCreator/TreeCreatorLeavesFast.shader", "Nature/Tree Creator Leaves Fast");
+ RegisterShader (10607, "Nature/TreeCreator/TreeCreatorLeavesFastOptimized.shader", "Hidden/Nature/Tree Creator Leaves Fast Optimized", false);
+
+ // Terrain shaders
+ RegisterShader (10620, "Nature/Terrain/TerrBumpFirstPass.shader", "Nature/Terrain/Bumped Specular");
+ RegisterShader (10621, "Nature/Terrain/TerrBumpAddPass.shader", "Hidden/Nature/Terrain/Bumped Specular AddPass", false);
+
+ // Mobile optimized shaders
+ RegisterShader (10700, "Mobile/Mobile-Skybox.shader", "Mobile/Skybox");
+ RegisterShader (10701, "Mobile/Mobile-VertexLit.shader", "Mobile/VertexLit");
+ RegisterShader (10703, "Mobile/Mobile-Diffuse.shader", "Mobile/Diffuse");
+ RegisterShader (10704, "Mobile/Mobile-Bumped.shader", "Mobile/Bumped Diffuse");
+ RegisterShader (10705, "Mobile/Mobile-BumpSpec.shader", "Mobile/Bumped Specular");
+ RegisterShader (10706, "Mobile/Mobile-BumpSpec-1DirectionalLight.shader", "Mobile/Bumped Specular (1 Directional Light)");
+ RegisterShader (10707, "Mobile/Mobile-VertexLit-OnlyDirectionalLights.shader", "Mobile/VertexLit (Only Directional Lights)");
+ RegisterShader (10708, "Mobile/Mobile-Lightmap-Unlit.shader", "Mobile/Unlit (Supports Lightmap)");
+
+ RegisterShader (10720, "Mobile/Mobile-Particle-Add.shader", "Mobile/Particles/Additive");
+ RegisterShader (10721, "Mobile/Mobile-Particle-Alpha.shader", "Mobile/Particles/Alpha Blended");
+ RegisterShader (10722, "Mobile/Mobile-Particle-Alpha-VertexLit.shader", "Mobile/Particles/VertexLit Blended");
+ RegisterShader (10723, "Mobile/Mobile-Particle-Multiply.shader", "Mobile/Particles/Multiply");
+
+ // Unlit shaders
+ RegisterShader (10750, "Unlit/Unlit-Alpha.shader", "Unlit/Transparent");
+ RegisterShader (10751, "Unlit/Unlit-AlphaTest.shader", "Unlit/Transparent Cutout");
+ RegisterShader (10752, "Unlit/Unlit-Normal.shader", "Unlit/Texture");
+
+ // Materials & textures
+ RegisterResource (10300, "Default-Particle.psd", "Texture2D");
+ RegisterResource (10301, "Default-Particle.mat", "Material");
+ RegisterResource (10302, "Default-Diffuse.mat", "Material");
+
+ m_Resources.sort();
+}
+
+static void RemapShadersToDefaultExtraResources ()
+{
+ // We have moved some shaders from builtin to default resources extra,
+ // make sure we don't break any backwards compatibility by remapping all references to them on load
+ // in the editor.
+
+ const int ids[] = {
+ 6, 1, 7, 2, 3, 4, 5, // regular opaque
+ 14, 10, 11, 12, 13, // self-illumin
+ 24, 25, 20, 21, 26, 22, 23, // reflective
+ 34, 30, 31, 32, 33, // transparent
+ 50, 51, 52, 53, 54, // transparent/cutout
+ 8, 9, 15, 16, 27, 28, 35, 36, // parallax
+ 100, 101, 103, 104, // misc
+ 200, 201, 202, 203, 205, 206, 207, 208, // particles
+ 10512, // various
+ 10500, 10501, 10502, 10505, 10503, 10507, // terrain engine
+ 40, 41, 42, 43, 44, // 2.x lightmap shaders
+ 10508, 10509, 10510, 10511, // 2.x tree shaders
+ 10600, 10601, 10602, 10603, 10604, 10605, // Tree Creator shaders
+ 10300, 10301, 10302, // default textures & materials
+ };
+
+ PersistentManager& pm = GetPersistentManager();
+ for (int i = 0; i < ARRAY_SIZE(ids); ++i)
+ {
+ pm.RemapInstanceIDOnLoad(kResourcePath, ids[i], kDefaultExtraResourcesPath, ids[i]);
+ }
+}
+
+
+void BuiltinResourceManager::GetResourcesOfClass (int classID, std::vector< std::pair<std::string,int> >& outResources)
+{
+ for (Resources::iterator i = m_Resources.begin(); i != m_Resources.end(); ++i)
+ {
+ if (i->classID != classID || !i->userVisible)
+ continue;
+ // Cache and store the name, so we don't have to load the actual object
+ // each time we do GetResourcesOfClass
+ if (i->cachedDisplayName.empty())
+ {
+ NamedObject* no = dynamic_instanceID_cast<NamedObject*>(i->cachedInstanceID);
+ i->cachedDisplayName = no ? no->GetName() : "<no name>";
+ }
+ outResources.push_back (std::make_pair(i->cachedDisplayName, i->cachedInstanceID));
+ }
+}
+
+void BuiltinResourceManager::GetBuiltinResourcesOfClass (int classID, std::vector< std::pair<std::string,int> >& outResources)
+{
+ GetBuiltinResourceManager().GetResourcesOfClass (classID, outResources);
+ GetBuiltinExtraResourceManager().GetResourcesOfClass (classID, outResources);
+}
+
+#endif // #if UNITY_EDITOR
+
+
+#if WEBPLUG
+static void RemapShadersToOldWebResources ()
+{
+ const int ids[] = {
+ 6, 1, 7, 2, 3, 4, 5, // regular opaque
+ 14, 10, 11, 12, 13, // self-illumin
+ 24, 25, 20, 21, 26, 22, 23, // reflective
+ 34, 30, 31, 32, 33, // transparent
+ 50, 51, 52, 53, 54, // transparent/cutout
+ 8, 9, 15, 16, 27, 28, 35, 36, // parallax
+ 100, 101, 103, 104, // misc
+ 200, 201, 202, 203, 205, 206, 207, 208, // particles
+ 10512, // various
+ 10500, 10501, 10502, 10505, 10503, 10507, // terrain engine
+ 10300, 10301, 10302, // default textures & materials
+ };
+
+ PersistentManager& pm = GetPersistentManager();
+ for (int i = 0; i < ARRAY_SIZE(ids); ++i)
+{
+ pm.RemapInstanceIDOnLoad(kResourcePath, ids[i], kOldWebResourcePath, ids[i]);
+ }
+}
+#endif // #if WEBPLUG
+
+
+#if WEBPLUG || UNITY_EDITOR
+void BuiltinResourceManager::InitializeOldWebResources ()
+{
+ Assert (m_Resources.empty ());
+
+ m_ResourcePath = kOldWebResourcePath;
+ m_RequiredHideFlags = Object::kHideAndDontSave | Object::kHideInspector;
+ m_AllowResourceManagerAccess = true;
+
+ RegisterShader (6, "Normal-VertexLit.shader", "VertexLit");
+ RegisterShader (1, "Normal-DiffuseFast.shader", "Legacy Shaders/Diffuse Fast");
+ RegisterShader (7, "Normal-Diffuse.shader", "Diffuse");
+ RegisterShader (2, "Normal-Bumped.shader", "Bumped Diffuse");
+ RegisterShader (3, "Normal-Glossy.shader", "Specular");
+ RegisterShader (4, "Normal-BumpSpec.shader", "Bumped Specular");
+ RegisterShader (5, "Normal-DiffuseDetail.shader", "Diffuse Detail");
+
+ RegisterShader (14, "Illumin-VertexLit.shader", "Self-Illumin/VertexLit");
+ RegisterShader (10, "Illumin-Diffuse.shader", "Self-Illumin/Diffuse");
+ RegisterShader (11, "Illumin-Bumped.shader", "Self-Illumin/Bumped Diffuse");
+ RegisterShader (12, "Illumin-Glossy.shader", "Self-Illumin/Specular");
+ RegisterShader (13, "Illumin-BumpSpec.shader", "Self-Illumin/Bumped Specular");
+
+ RegisterShader (24, "Reflect-VertexLit.shader", "Reflective/VertexLit");
+ RegisterShader (25, "Reflect-BumpNolight.shader", "Reflective/Bumped Unlit");
+ RegisterShader (20, "Reflect-Diffuse.shader", "Reflective/Diffuse");
+ RegisterShader (21, "Reflect-Bumped.shader", "Reflective/Bumped Diffuse");
+ RegisterShader (26, "Reflect-BumpVertexLit.shader", "Reflective/Bumped VertexLit");
+ RegisterShader (22, "Reflect-Glossy.shader", "Reflective/Specular");
+ RegisterShader (23, "Reflect-BumpSpec.shader", "Reflective/Bumped Specular");
+
+ RegisterShader (34, "Alpha-VertexLit.shader", "Transparent/VertexLit");
+ RegisterShader (30, "Alpha-Diffuse.shader", "Transparent/Diffuse");
+ RegisterShader (31, "Alpha-Bumped.shader", "Transparent/Bumped Diffuse");
+ RegisterShader (32, "Alpha-Glossy.shader", "Transparent/Specular");
+ RegisterShader (33, "Alpha-BumpSpec.shader", "Transparent/Bumped Specular");
+
+ RegisterShader (50, "AlphaTest-VertexLit.shader", "Transparent/Cutout/VertexLit");
+ RegisterShader (51, "AlphaTest-Diffuse.shader", "Transparent/Cutout/Diffuse");
+ RegisterShader (52, "AlphaTest-Bumped.shader", "Transparent/Cutout/Bumped Diffuse");
+ RegisterShader (53, "AlphaTest-Glossy.shader", "Transparent/Cutout/Specular");
+ RegisterShader (54, "AlphaTest-BumpSpec.shader", "Transparent/Cutout/Bumped Specular");
+
+ RegisterShader (8, "Normal-Parallax.shader", "Parallax Diffuse");
+ RegisterShader (9, "Normal-ParallaxSpec.shader", "Parallax Specular");
+ RegisterShader (15, "Illumin-Parallax.shader", "Self-Illumin/Parallax Diffuse");
+ RegisterShader (16, "Illumin-ParallaxSpec.shader", "Self-Illumin/Parallax Specular");
+ RegisterShader (27, "Reflect-Parallax.shader", "Reflective/Parallax Diffuse");
+ RegisterShader (28, "Reflect-ParallaxSpec.shader", "Reflective/Parallax Specular");
+ RegisterShader (35, "Alpha-Parallax.shader", "Transparent/Parallax Diffuse");
+ RegisterShader (36, "Alpha-ParallaxSpec.shader", "Transparent/Parallax Specular");
+
+ RegisterShader (100, "Decal.shader", "Decal");
+ RegisterShader (101, "Flare.shader", "FX/Flare");
+ RegisterShader (103, "skybox cubed.shader", "RenderFX/Skybox Cubed");
+ RegisterShader (104, "skybox.shader", "RenderFX/Skybox");
+
+ RegisterShader (200, "Particle Add.shader", "Particles/Additive");
+ RegisterShader (201, "Particle AddMultiply.shader", "Particles/~Additive-Multiply");
+ RegisterShader (202, "Particle AddSmooth.shader", "Particles/Additive (Soft)");
+ RegisterShader (203, "Particle Alpha Blend.shader", "Particles/Alpha Blended");
+ RegisterShader (205, "Particle Multiply.shader", "Particles/Multiply");
+ RegisterShader (206, "Particle MultiplyDouble.shader", "Particles/Multiply (Double)");
+ RegisterShader (207, "Particle Premultiply Blend.shader", "Particles/Alpha Blended Premultiply");
+ RegisterShader (208, "Particle VertexLit Blended.shader", "Particles/VertexLit Blended");
+
+ RegisterShader (10512, "AlphaTest-SoftEdgeUnlit.shader", "Transparent/Cutout/Soft Edge Unlit");
+
+ RegisterShader (10500, "TerrainShaders/Details/VertexLit.shader", "Hidden/TerrainEngine/Details/Vertexlit");
+ RegisterShader (10501, "TerrainShaders/Details/WavingGrass.shader", "Hidden/TerrainEngine/Details/WavingDoublePass");
+ RegisterShader (10502, "TerrainShaders/Details/WavingGrassBillboard.shader", "Hidden/TerrainEngine/Details/BillboardWavingDoublePass");
+ RegisterShader (10505, "TerrainShaders/Splats/FirstPass.shader", "Nature/Terrain/Diffuse");
+ RegisterShader (10503, "TerrainShaders/Splats/AddPass.shader", "Hidden/TerrainEngine/Splatmap/Lightmap-AddPass");
+ RegisterShader (10507, "TerrainShaders/Trees/BillboardTree.shader", "Hidden/TerrainEngine/BillboardTree");
+
+ // Materials & textures
+ RegisterResource (10300, "Default-Particle.psd", "Texture2D");
+ RegisterResource (10301, "Default-Particle.mat", "Material");
+ RegisterResource (10302, "Default-Diffuse.mat", "Material");
+
+ m_Resources.sort();
+}
+#endif // #if WEBPLUG || UNITY_EDITOR
+
+
+
+template<class TransferFunction>
+void ResourceManager::Dependency::Transfer (TransferFunction& transfer)
+{
+ transfer.Transfer (object, "m_Object");
+ transfer.Transfer (dependencies, "m_Dependencies");
+}
+
+void BuiltinResourceManager::InitializeResources ()
+{
+ m_ResourcePath = kResourcePath;
+ m_RequiredHideFlags = Object::kHideAndDontSave | Object::kHideInspector;
+ m_AllowResourceManagerAccess = true;
+
+ AssertIf (!m_Resources.empty ());
+
+
+ // Issue RegisterResource shader calls in the expected loading order:
+ // if any shader references another (e.g. UsePass), the referenced must come first!
+
+ RegisterShader (60, "Shadow-ScreenBlur.shader", "Hidden/Shadow-ScreenBlur", false);
+ RegisterShader (61, "Camera-DepthTexture.shader", "Hidden/Camera-DepthTexture", false);
+ RegisterShader (62, "Camera-DepthNormalTexture.shader", "Hidden/Camera-DepthNormalTexture", false);
+ RegisterShader (63, "Internal-PrePassLighting.shader", "Hidden/Internal-PrePassLighting", false);
+ RegisterShader (64, "Internal-PrePassCollectShadows.shader", "Hidden/Internal-PrePassCollectShadows", false);
+ RegisterShader (65, "Internal-CombineDepthNormals.shader", "Hidden/Internal-CombineDepthNormals", false);
+ RegisterShader (66, "Internal-BlitCopy.shader", "Hidden/BlitCopy", false);
+ RegisterShader (67, "Shadow-ScreenBlurRotated.shader", "Hidden/Shadow-ScreenBlurRotated", false);
+ RegisterShader (68, "Internal-Clear.shader", "Hidden/InternalClear", false);
+
+ RegisterShader (102, "Internal-Flare.shader", "Hidden/Internal-Flare", false);
+ RegisterShader (105, "Internal-Halo.shader", "Hidden/Internal-Halo", false);
+
+ //RegisterResource (300, "FX-Water.shader", "Shader"); // obsolete
+ //RegisterResource (500, "r7000bumpmap.cgvp", "CGProgram"); // obsolete
+ //RegisterResource (501, "r7000bumpmap2.cgvp", "CGProgram"); // obsolete
+
+ // Light stuff
+ //RegisterResource (10000, "Med.psd", "Cubemap"); // obsolete
+ RegisterResource (10001, "Soft.psd", "Texture2D", false);
+
+ // internal GUI stuff
+ RegisterShader (9000, "Internal-GUITextureClip.shader", "Hidden/Internal-GUITextureClip", false);
+ RegisterShader (9001, "Internal-GUITextureClipText.shader", "Hidden/Internal-GUITextureClipText", false);
+ RegisterShader (9002, "Internal-GUITexture.shader", "Hidden/Internal-GUITexture", false);
+ RegisterShader (9003, "Internal-GUITextureBlit.shader", "Hidden/Internal-GUITextureBlit", false);
+
+ // Default font
+ RegisterResource (10100, kDefaultFontName, "Material", false);
+ RegisterShader (10101, "Font.shader", "GUI/Text Shader");
+ RegisterResource (10102, kDefaultFontName, "Font");
+ RegisterResource (10103, kDefaultFontName, "Texture2D", false);
+
+ // Default meshes
+ RegisterResource (10202, "Cube.fbx", "Mesh");
+
+ RegisterResource (10206, "New-Cylinder.fbx", "Mesh");
+ RegisterResource (10207, "New-Sphere.fbx", "Mesh");
+ RegisterResource (10208, "New-Capsule.fbx", "Mesh");
+ RegisterResource (10209, "New-Plane.fbx", "Mesh");
+ RegisterResource (10210, "Quad.fbx", "Mesh");
+ // These 3 have zero normals, because pre-4.3 light prepass shader expects it
+ RegisterResource (10211, "icosphere.fbx", "Mesh");
+ RegisterResource (10212, "icosahedron.fbx", "Mesh");
+ RegisterResource (10213, "pyramid.fbx", "Mesh");
+
+ // DEPRECATED builtin meshes (Deprecated in version 1.5 (Adjust scale (eg. capsule is 2 meters high instead of 4)))
+ RegisterResource (10200, "Sphere.fbx", "Mesh", false);
+ RegisterResource (10203, "Cylinder.fbx", "Mesh", false);
+ RegisterResource (10204, "Plane.fbx", "Mesh", false);
+ RegisterResource (10205, "Capsule.fbx", "Mesh", false);
+
+ // Watermark, splash etc.
+ RegisterResource (10400, "UnityWaterMark-small.png", "Texture2D", false);
+ RegisterResource (10401, "EscToExit_back.png", "Texture2D", false);
+ RegisterResource (10402, "EscToExit_text.png", "Texture2D", false);
+ RegisterResource (10407, "UnityWaterMark-trial.png", "Texture2D", false);
+ RegisterResource (10408, "UnityWaterMark-beta.png", "Texture2D", false);
+ RegisterResource (10409, "UnityWaterMark-edu.png", "Texture2D", false);
+ RegisterResource (10410, "UnityWaterMark-dev.png", "Texture2D", false);
+ RegisterResource (10411, "WarningSign.psd", "Texture2D", false);
+ RegisterResource (10412, "UnityWatermark-DebugFlashPlayer.png", "Texture2D", false);
+ RegisterResource (10413, "UnityWaterMark-proto.png", "Texture2D", false);
+ RegisterResource (10414, "UnityWaterMarkPlugin-beta.png", "Texture2D", false);
+
+#if !UNITY_IPHONE
+ // Standalone
+ RegisterResource (10403, "UnitySplash.png", "Texture2D", false);
+ RegisterResource (10404, "UnitySplash2.png", "Texture2D", false);
+ RegisterResource (10405, "UnitySplash3.png", "Texture2D", false);
+ RegisterResource (10406, "UnitySplashBack.png", "Texture2D", false);
+#endif
+
+#if ENABLE_SPRITES
+ RegisterShader (10753, "Sprites-Default.shader", "Sprites/Default");
+ RegisterResource (10754, "Sprites-Default.mat", "Material");
+#endif
+
+ // Built-in GUISkin
+ RegisterResource (11000, "GameSkin/GameSkin.guiskin", "MonoBehaviour", false);
+ RegisterResource (11001, "GameSkin/box.png", "Texture2D", false);
+ RegisterResource (11002, "GameSkin/button active.png", "Texture2D", false);
+ RegisterResource (11003, "GameSkin/button hover.png", "Texture2D", false);
+ RegisterResource (11004, "GameSkin/button on hover.png", "Texture2D", false);
+ RegisterResource (11005, "GameSkin/button on.png", "Texture2D", false);
+ RegisterResource (11006, "GameSkin/button.png", "Texture2D", false);
+ RegisterResource (11007, "GameSkin/horizontal scrollbar thumb.png", "Texture2D", false);
+ RegisterResource (11008, "GameSkin/horizontal scrollbar.png", "Texture2D", false);
+ RegisterResource (11009, "GameSkin/horizontalslider.png", "Texture2D", false);
+ RegisterResource (11010, "GameSkin/slider thumb active.png", "Texture2D", false);
+ RegisterResource (11011, "GameSkin/slider thumb.png", "Texture2D", false);
+ RegisterResource (11012, "GameSkin/slidert humb hover.png", "Texture2D", false);
+ RegisterResource (11013, "GameSkin/toggle active.png", "Texture2D", false);
+ RegisterResource (11014, "GameSkin/toggle hover.png", "Texture2D", false);
+ RegisterResource (11015, "GameSkin/toggle on hover.png", "Texture2D", false);
+ RegisterResource (11016, "GameSkin/toggle on.png", "Texture2D", false);
+ RegisterResource (11017, "GameSkin/toggle on active.png", "Texture2D", false);
+ RegisterResource (11018, "GameSkin/toggle.png", "Texture2D", false);
+ RegisterResource (11019, "GameSkin/vertical scrollbar thumb.png", "Texture2D", false);
+ RegisterResource (11020, "GameSkin/vertical scrollbar.png", "Texture2D", false);
+ RegisterResource (11021, "GameSkin/verticalslider.png", "Texture2D", false);
+ RegisterResource (11022, "GameSkin/window on.png", "Texture2D", false);
+ RegisterResource (11023, "GameSkin/window.png", "Texture2D", false);
+ RegisterResource (11024, "GameSkin/textfield.png", "Texture2D", false);
+ RegisterResource (11025, "GameSkin/textfield on.png", "Texture2D", false);
+ RegisterResource (11026, "GameSkin/textfield hover.png", "Texture2D", false);
+
+ // 12000 range is used by scripts in RegisterBuiltinScripts
+ RegisterResource(11998, "DeveloperConsole", "MonoScript", false);
+ RegisterResource(11999, "UserAuthorizationDialog", "MonoScript", false);
+ RegisterResource(12000, "Terrain", "MonoScript", false);
+ RegisterResource(12001, "GUISkin", "MonoScript", false);
+
+ #if UNITY_LOGIC_GRAPH
+ // 130xx are for logic graph engine side scripts
+ RegisterResource(13003, "GraphBehaviour", "MonoScript", false);
+ RegisterResource(13006, "OnAnimationEventDummy", "MonoScript", false);
+ RegisterResource(13007, "OnMouseEventDummy", "MonoScript", false);
+ RegisterResource(13008, "OnTriggerEventDummy", "MonoScript", false);
+ RegisterResource(13009, "OnCollisionEventDummy", "MonoScript", false);
+ #endif
+
+ m_Resources.sort();
+
+}
+
+void BuiltinResourceManager::InitializeAllResources ()
+{
+#if WEBPLUG
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion_OldWebResourcesAdded))
+ {
+ GetBuiltinOldWebResourceManager().InitializeOldWebResources();
+ RemapShadersToOldWebResources();
+ }
+#endif
+
+#if UNITY_EDITOR
+
+ // Setup up path remapping for builtin resources
+ GetPersistentManager().SetPathRemap(kResourcePath, AppendPathName (GetApplicationContentsPath (), kDefaultResourcesSource));
+ GetPersistentManager().SetPathRemap(kEditorResourcePath, AppendPathName (GetApplicationContentsPath (), kEditorDefaultResourcesSource));
+ GetPersistentManager().SetPathRemap(kDefaultExtraResourcesPath, AppendPathName (GetApplicationContentsPath (), kDefaultExtraResourcesPath));
+
+ // Initialize resources
+ GetBuiltinResourceManager().InitializeResources();
+ GetBuiltinExtraResourceManager().InitializeExtraResources();
+ RemapShadersToDefaultExtraResources();
+#else
+ GetBuiltinResourceManager().InitializeResources();
+#endif
+
+ s_AreResourcesInitialized = true;
+}
+
+bool BuiltinResourceManager::AreResourcesInitialized()
+{
+ return s_AreResourcesInitialized;
+}
+
+
+#if UNITY_EDITOR
+
+// Disable builtin resource loading. This is useful to ensure there are no dependencies on builtin resources when building them.
+void BuiltinResourceManager::SetAllowLoadingBuiltinResources (bool allowLoadingBuiltinResources)
+{
+ GetBuiltinResourceManager().m_AllowResourceManagerAccess = allowLoadingBuiltinResources;
+ GetBuiltinOldWebResourceManager().m_AllowResourceManagerAccess = allowLoadingBuiltinResources;
+ GetBuiltinExtraResourceManager().m_AllowResourceManagerAccess = allowLoadingBuiltinResources;
+}
+
+
+void BuiltinResourceManager::RegisterBuiltinScriptsForBuilding ()
+{
+ // Regenerate scripts from scratch and serialize them
+ for (Resources::iterator i=m_Resources.begin ();i != m_Resources.end ();i++)
+ {
+ if (i->classID == ClassID (MonoScript))
+ {
+ string path = AppendPathName("GeneratedBuiltinScripts", i->name);
+ int oldID = GetPersistentManager().GetInstanceIDFromPathAndFileID(path, 1);
+ if (dynamic_instanceID_cast<MonoScript*>(oldID) == NULL)
+ {
+ MonoScript* script = NEW_OBJECT(MonoScript);
+ script->Reset();
+ script->Init("", i->name, "UnityEngine", "UnityEngine.dll", false);
+ script->SetName(i->name);
+ script->SetPathName(i->name);
+ GetPersistentManager().MakeObjectPersistentAtFileID(script->GetInstanceID(), 1, path);
+ script->AwakeFromLoad(kDefaultAwakeFromLoad);
+ }
+ }
+ }
+}
+
+static void RegisterGraphScript (int id, const char* className, const char* _namspace)
+{
+ DoRegisterBuiltinEditorScript(id, className, _namspace, "UnityEditor.Graphs.dll", true);
+}
+
+void GenerateEditorScripts ()
+{
+ RegisterBuiltinEditorScript(12002, "TerrainInspector");
+
+ RegisterBuiltinEditorScript(12003, "ConsoleWindow");
+ RegisterBuiltinEditorScript(12004, "ContainerWindow");
+ RegisterBuiltinEditorScript(12005, "GUIView");
+ RegisterBuiltinEditorScript(12006, "DockArea");
+ RegisterBuiltinEditorScript(12007, "EditorWindow");
+ RegisterBuiltinEditorScript(12008, "MainWindow");
+ RegisterBuiltinEditorScript(12009, "PaneDragTab");
+ RegisterBuiltinEditorScript(12010, "SplitView");
+ RegisterBuiltinEditorScript(12011, "Toolbar");
+ RegisterBuiltinEditorScript(12012, "Tools");
+ RegisterBuiltinEditorScript(12013, "SceneView");
+ RegisterBuiltinEditorScript(12014, "ProjectBrowser");
+ RegisterBuiltinEditorScript(12015, "GameView");
+ RegisterBuiltinEditorScript(12016, "PopupEditor");
+ RegisterBuiltinEditorScript(12017, "ColorPicker");
+ RegisterBuiltinEditorScript(12018, "AboutWindow");
+ RegisterBuiltinEditorScript(12019, "InspectorWindow");
+ RegisterBuiltinEditorScript(12020, "ShaderInspector");
+ //RegisterBuiltinEditorScript(12021, "ServerDemo"); // was used for testing only
+// RegisterBuiltinEditorScript(12022, "GUIDebugHelper");
+ //RegisterBuiltinEditorScript(12023, "ButtonsDemo"); // was used for testing only
+ RegisterBuiltinEditorScript(12024, "AssetImporterInspector");
+ RegisterBuiltinEditorScript(12025, "TextureImporterInspector");
+ RegisterBuiltinEditorScript(12026, "MovieImporterInspector");
+ RegisterBuiltinEditorScript(12027, "TransformInspector");
+ RegisterBuiltinEditorScript(12028, "ModelImporterEditor");
+ RegisterBuiltinEditorScript(12029, "AudioImporterInspector");
+ RegisterBuiltinEditorScript(12030, "TrueTypeFontImporterInspector");
+ RegisterBuiltinEditorScript(12031, "TextureInspector");
+ RegisterBuiltinEditorScript(12032, "GameObjectInspector");
+ RegisterBuiltinEditorScript(12033, "MaterialEditor");
+ RegisterBuiltinEditorScript(12034, "SaveWindowLayout");
+ RegisterBuiltinEditorScript(12035, "DeleteWindowLayout");
+ RegisterBuiltinEditorScript(12036, "RenderTextureInspector");
+ RegisterBuiltinEditorScript(12037, "ASMainWindow");
+ RegisterBuiltinEditorScript(12038, "CubemapTextureInspector");
+ RegisterBuiltinEditorScript(12039, "RagdollBuilder");
+ RegisterBuiltinEditorScript(12040, "GenericInspector");
+ RegisterBuiltinEditorScript(12041, "ProfilerIPWindow");
+ RegisterBuiltinEditorScript(12042, "AppStatusBar");
+ RegisterBuiltinEditorScript(12043, "BuildPlayerWindow");
+ RegisterBuiltinEditorScript(12044, "PreferencesWindow");
+ RegisterBuiltinEditorScript(12045, "Settings");
+ RegisterBuiltinEditorScript(12046, "SnapSettings");
+ RegisterBuiltinEditorScript(12047, "HostView");
+
+ RegisterBuiltinEditorScript(12048, "TerrainWizard");
+ RegisterBuiltinEditorScript(12049, "LightmapWizard");
+ RegisterBuiltinEditorScript(12050, "ImportRawHeightmap");
+ RegisterBuiltinEditorScript(12051, "ExportRawHeightmap");
+ RegisterBuiltinEditorScript(12052, "SetResolutionWizard");
+ RegisterBuiltinEditorScript(12053, "TreeWizard");
+ RegisterBuiltinEditorScript(12054, "SplatWizard");
+ RegisterBuiltinEditorScript(12055, "DetailMeshWizard");
+ RegisterBuiltinEditorScript(12056, "DetailTextureWizard");
+ RegisterBuiltinEditorScript(12057, "PlaceTreeWizard");
+ RegisterBuiltinEditorScript(12058, "FlattenHeightmap");
+
+ RegisterBuiltinEditorScript(12059, "FallbackEditorWindow");
+ RegisterBuiltinEditorScript(12060, "MaximizedHostView");
+ RegisterBuiltinEditorScript(12061, "HierarchyWindow");
+ RegisterBuiltinEditorScript(12062, "PackageExport");
+ RegisterBuiltinEditorScript(12063, "PackageImport");
+ RegisterBuiltinEditorScript(12064, "EyeDropper");
+ RegisterBuiltinEditorScript(12065, "WelcomeScreen");
+ RegisterBuiltinEditorScript(12066, "MonoScriptInspector");
+ RegisterBuiltinEditorScript(12067, "AudioClipInspector");
+ RegisterBuiltinEditorScript(12068, "ModelInspector");
+ RegisterBuiltinEditorScript(12069, "TooltipView");
+ RegisterBuiltinEditorScript(12070, "ProfilerWindow");
+ RegisterBuiltinEditorScript(12071, "AnimationWindow");
+ RegisterBuiltinEditorScript(12072, "ASHistoryWindow");
+ RegisterBuiltinEditorScript(12073, "AnimationEventPopup");
+ RegisterBuiltinEditorScript(12074, "EditorSettingsInspector");
+ RegisterBuiltinEditorScript(12075, "AssetInspector");
+ RegisterBuiltinEditorScript(12076, "AnimationCleanupPopup");
+ RegisterBuiltinEditorScript(12077, "ScriptReloadProperties");
+ RegisterBuiltinEditorInternalScript(12078, "MacroWindow");
+ RegisterBuiltinEditorScript(12079, "LightmappingWindow");
+ RegisterBuiltinEditorScript(12080, "AuxWindow");
+ RegisterBuiltinEditorScript(12081, "ObjectSelector");
+ RegisterBuiltinEditorScript(12082, "LabelCompletion");
+ RegisterBuiltinEditorScript(12083, "CurveEditorWindow");
+ RegisterBuiltinEditorScript(12084, "MonoScriptImporterInspector");
+ RegisterBuiltinEditorScript(12085, "MovieTextureInspector");
+ RegisterBuiltinEditorScript(12086, "TextAssetInspector");
+ RegisterBuiltinEditorScript(12087, "LightEditor");
+ RegisterBuiltinEditorScript(12088, "AudioSourceInspector");
+ RegisterBuiltinEditorScript(12089, "AudioLowPassFilterInspector");
+ RegisterBuiltinEditorScript(12090, "OcclusionCullingWindow");
+ RegisterBuiltinEditorScript(12091, "WebCamTextureInspector");
+ RegisterBuiltinEditorScript(12092, "ShaderImporterInspector");
+
+ RegisterBuiltinEditorScript(12099, "AudioReverbZoneEditor");
+ RegisterBuiltinEditorScript(12100, "PhysicsManagerInspector");
+
+ RegisterBuiltinEditorScript(12101, "ClothInspector");
+ RegisterBuiltinEditorScript(12102, "BumpMapSettingsFixingWindow");
+ RegisterBuiltinEditorScript(12103, "PragmaFixingWindow");
+ RegisterBuiltinEditorScript(12104, "WindInspector");
+
+ DoRegisterBuiltinEditorScript (12105, "TreeEditor", "TreeEditor", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript (12106, "TreeData", "TreeEditor", "UnityEditor.dll", true);
+
+ RegisterBuiltinEditorScript(12107, "PlayerSettingsEditor");
+ RegisterBuiltinEditorScript(12108, "EditorUpdateWindow");
+
+ RegisterBuiltinEditorScript(12109, "AudioReverbFilterEditor");
+
+ RegisterBuiltinEditorScript(12110, "WebView");
+ RegisterBuiltinEditorScript(12111, "AssetStoreWindow");
+ RegisterBuiltinEditorScript(12112, "AssetStoreContext");
+
+ RegisterBuiltinEditorScript(12113, "AssembleEditorSkin");
+ RegisterBuiltinEditorScript(12114, "OcclusionAreaEditor");
+ RegisterBuiltinEditorScript(12115, "CameraEditor");
+
+ RegisterBuiltinEditorScript(12116, "WindowFocusState");
+
+ RegisterBuiltinEditorScript(12117, "AndroidKeystoreWindow");
+
+ RegisterBuiltinEditorScript(12118, "AudioDistortionFilterEditor");
+ RegisterBuiltinEditorScript(12119, "AudioEchoFilterEditor");
+ RegisterBuiltinEditorScript(12120, "AudioHighPassFilterEditor");
+ RegisterBuiltinEditorScript(12121, "AudioChorusFilterEditor");
+
+ RegisterBuiltinEditorScript(12122, "BoxColliderEditor");
+ RegisterBuiltinEditorScript(12123, "SphereColliderEditor");
+ RegisterBuiltinEditorScript(12124, "CapsuleColliderEditor");
+ RegisterBuiltinEditorScript(12125, "MeshColliderEditor");
+ RegisterBuiltinEditorScript(12126, "WheelColliderEditor");
+ RegisterBuiltinEditorScript(12127, "RigidbodyEditor");
+
+ RegisterBuiltinEditorScript(12128, "AssetSaveDialog");
+
+ RegisterBuiltinEditorScript(12129, "AnnotationWindow");
+ RegisterBuiltinEditorScript(12130, "IconSelector");
+ RegisterBuiltinEditorScript(12131, "ScriptExecutionOrderInspector");
+
+ RegisterBuiltinEditorScript(12132, "AnimationEditor");
+ RegisterBuiltinEditorScript(12133, "SkinnedMeshRendererEditor");
+ RegisterBuiltinEditorScript(12134, "PreviewWindow");
+ RegisterBuiltinEditorScript(12135, "LODGroupEditor");
+
+ RegisterBuiltinEditorScript(12136, "OffMeshLinkInspector");
+ RegisterBuiltinEditorScript(12137, "ParticleSystemWindow");
+ RegisterBuiltinEditorScript(12138, "ParticleSystemInspector");
+ RegisterBuiltinEditorScript(12139, "MeshRendererEditor");
+
+ RegisterBuiltinEditorScript(12140, "GradientPicker");
+
+ RegisterBuiltinEditorScript(12141, "NavMeshEditorWindow");
+ RegisterBuiltinEditorScript(12142, "FontInspector");
+
+ RegisterBuiltinEditorScript(12143, "BuildUploadCompletedWindow");
+ RegisterBuiltinEditorScript(12144, "UploadingBuildsMonitor");
+
+ RegisterBuiltinEditorScript(12145, "TerrainSplatEditor");
+
+ #if ENABLE_SPRITES
+ RegisterBuiltinEditorScript(12150, "SpriteRendererInspector");
+ RegisterBuiltinEditorScript(12151, "SpriteInspector");
+ #endif
+
+ RegisterBuiltinEditorScript(12153, "TagManagerInspector");
+
+ RegisterBuiltinEditorScript(12200, "SubstanceImporterInspector");
+ // RegisterBuiltinEditorScript(12201, "SubstanceArchiveInspector");
+ RegisterBuiltinEditorScript(12202, "ProceduralMaterialInspector");
+ RegisterBuiltinEditorScript(12203, "ProceduralTextureInspector");
+ RegisterBuiltinEditorScript(12204, "NavMeshAgentInspector");
+ RegisterBuiltinEditorScript(12205, "LightProbeGroupInspector");
+ RegisterBuiltinEditorScript(12206, "LightProbeGroupSelection");
+ RegisterBuiltinEditorScript(12207, "View");
+ RegisterBuiltinEditorScript(12208, "OcclusionPortalInspector");
+ RegisterBuiltinEditorScript(12209, "NavMeshObstacleInspector");
+
+ RegisterBuiltinEditorScript(12210, "PopupList");
+
+ RegisterBuiltinEditorScript(12211, "AssetStoreAssetInspector");
+ RegisterBuiltinEditorScript(12212, "AssetStoreInstaBuyWindow");
+ RegisterBuiltinEditorScript(12213, "AssetStoreLoginWindow");
+
+ DoRegisterBuiltinEditorScript(12216, "EndNameEditAction", "UnityEditor.ProjectWindowCallback", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript(12217, "DoCreateNoneAsset", "UnityEditor.ProjectWindowCallback", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript(12218, "DoCreateNewAsset", "UnityEditor.ProjectWindowCallback", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript(12219, "DoCreateFolder", "UnityEditor.ProjectWindowCallback", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript(12220, "DoCreatePrefab", "UnityEditor.ProjectWindowCallback", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript(12221, "DoCreateScriptAsset", "UnityEditor.ProjectWindowCallback", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript(12223, "DoCreateAnimatorController", "UnityEditor.ProjectWindowCallback", "UnityEditor.dll", true);
+
+ RegisterBuiltinEditorScript(12214, "LightProbesInspector");
+
+ RegisterBuiltinEditorScript(12215, "AddComponentWindow");
+
+ RegisterBuiltinEditorScript(12222, "LicenseManagementWindow");
+
+ DoRegisterBuiltinEditorScript(12300, "WindowResolve", "UnityEditor.VersionControl", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript(12301, "WindowChange", "UnityEditor.VersionControl", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript(12303, "WindowPending", "UnityEditor.VersionControl", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript(12304, "WindowRevert", "UnityEditor.VersionControl", "UnityEditor.dll", true);
+
+ RegisterBuiltinEditorScript(12306, "CharacterControllerEditor");
+
+ RegisterBuiltinEditorScript(12307, "QualitySettingsEditor");
+ RegisterBuiltinEditorScript(12308, "SavedSearchFilters");
+ // RegisterBuiltinEditorScript(12309, ""); // Unused
+ RegisterBuiltinEditorScript(12310, "TextMeshInspector");
+ RegisterBuiltinEditorScript(12311, "Texture3DInspector");
+
+ //RegisterBuiltinEditorScript(12312, "MetroCreateTestCertificateWindow");
+ //RegisterBuiltinEditorScript(12313, "MetroCertificatePasswordWindow");
+
+ RegisterBuiltinEditorScript(12314, "ModelImporterRigEditor");
+ RegisterBuiltinEditorScript(12315, "ModelImporterModelEditor");
+ RegisterBuiltinEditorScript(12316, "ModelImporterClipEditor");
+
+ RegisterBuiltinEditorScript(12318, "AvatarPreviewSelection");
+ RegisterBuiltinEditorScript(12319, "PopupWindow");
+
+ RegisterBuiltinEditorScript(12320, "PresetLibraryManager");
+ RegisterBuiltinEditorScript(12321, "GradientPresetLibrary");
+ RegisterBuiltinEditorScript(12322, "CurvePresetLibrary");
+ RegisterBuiltinEditorScript(12323, "ColorPresetLibrary");
+ RegisterBuiltinEditorScript(12324, "DoubleCurvePresetLibrary");
+
+ #if ENABLE_SPRITES
+ RegisterBuiltinEditorScript(12325, "AtlasEditorWindow");
+ RegisterBuiltinEditorScript(12326, "SpritePackerWindow");
+ #endif
+
+ #if ENABLE_2D_PHYSICS
+ RegisterBuiltinEditorScript(12327, "PolygonColliderEditor");
+ RegisterBuiltinEditorScript(12329, "Physics2DSettingsInspector");
+ #endif
+
+ RegisterBuiltinEditorScript(12330, "GameViewSizes");
+
+ RegisterBuiltinEditorScript(12331, "ColorPresetLibraryEditor");
+ RegisterBuiltinEditorScript(12332, "GradientPresetLibraryEditor");
+ RegisterBuiltinEditorScript(12333, "CurvePresetLibraryEditor");
+ RegisterBuiltinEditorScript(12334, "DoubleCurvePresetLibraryEditor");
+
+ RegisterBuiltinEditorScript(12340, "BB10SigningAuthorityWindow");
+ RegisterBuiltinEditorScript(12341, "BB10UnregisterWarningWindow");
+
+ RegisterBuiltinEditorScript(12342, "TizenSigningAuthorityWindow");
+ RegisterBuiltinEditorScript(12343, "TizenUnregisterWarningWindow");
+
+ #if ENABLE_2D_PHYSICS
+ RegisterBuiltinEditorScript(12350, "BoxCollider2DEditor");
+ RegisterBuiltinEditorScript(12351, "CircleCollider2DEditor");
+ RegisterBuiltinEditorScript(12352, "EdgeCollider2DEditor");
+ #endif
+
+
+ // 12410-12449 are for Toolbar & GUI Editor
+
+ #if UNITY_LOGIC_GRAPH
+ // 131xx are for logic graph editor side scripts
+ RegisterGraphScript(13100, "FlowWindow", kGraphsEditorBaseNamespace);
+ RegisterGraphScript(13101, "LogicGraph", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13102, "LogicGraphGUI", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13103, "FunctionNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13104, "VariableNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13105, "FunctionNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13106, "VariableNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13107, "ClassNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13108, "ExpressionNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13109, "ExpressionNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13110, "CompareNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13111, "CompareNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13112, "IfNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13113, "IfNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13114, "RandomNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13115, "RandomNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13116, "ClassNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13117, "EventNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13118, "EventNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13119, "SetPropertyNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13120, "SetPropertyNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13121, "GetPropertyNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13122, "GetPropertyNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13123, "OnAnimationEventNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13124, "OnAnimationEventNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13125, "ExternalCallReceiver", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13126, "ExternalCallReceiverEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13127, "ExternalCallSender", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13128, "ExternalCallSenderEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13129, "EvalNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13130, "EvalNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13131, "StructExpressionNode", kLogicGraphEditorNamespace);
+
+ RegisterGraphScript(13132, "StructExpressionNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13133, "ToggleNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13134, "ToggleNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13135, "GetComponentNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13136, "GetComponentNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13137, "GraphBehaviourInspector", kLogicGraphEditorNamespace);
+ #endif
+
+ RegisterGraphScript(13138, "Graph", kGraphsEditorBaseNamespace);
+ RegisterGraphScript(13139, "GraphInspector", kGraphsEditorBaseNamespace);
+ RegisterGraphScript(13140, "GraphGUI", kGraphsEditorBaseNamespace);
+ RegisterGraphScript(13141, "Node", kGraphsEditorBaseNamespace);
+ RegisterBuiltinEditorScript(12901, "AnimatorInspector");
+ RegisterBuiltinEditorScript(12902, "AnimatorOverrideControllerInspector");
+ RegisterBuiltinEditorScript(12905, "AnimationClipEditor");
+ RegisterBuiltinEditorScript(12906, "AvatarEditor");
+ RegisterGraphScript(12907, "TransitionInspector", kAnimationStateMachineNamespace);
+ RegisterGraphScript(12914, "AnimatorControllerTool", kGraphsEditorBaseNamespace);
+ RegisterBuiltinEditorScript(12915, "AvatarMaskInspector");
+ RegisterBuiltinEditorScript(12916, "BlendTreeInspector");
+
+ RegisterBuiltinEditorScript(12917, "AvatarMappingEditor");
+ RegisterBuiltinEditorScript(12918, "AvatarMuscleEditor");
+
+ RegisterGraphScript(12919, "GraphGUI", kAnimationBlendTreeNamespace);
+ RegisterGraphScript(12920, "Graph", kAnimationBlendTreeNamespace);
+ RegisterGraphScript(12921, "Node", kAnimationBlendTreeNamespace);
+
+ RegisterGraphScript(12930, "GraphGUI", kAnimationStateMachineNamespace);
+ RegisterGraphScript(12931, "Graph", kAnimationStateMachineNamespace);
+ RegisterGraphScript(12932, "StateNode", kAnimationStateMachineNamespace);
+ RegisterGraphScript(12933, "AnyStateNode", kAnimationStateMachineNamespace);
+ RegisterGraphScript(12934, "StateMachineNode", kAnimationStateMachineNamespace);
+ RegisterGraphScript(12935, "StateEditor", kAnimationStateMachineNamespace);
+ RegisterGraphScript(12936, "StateMachineInspector", kAnimationStateMachineNamespace);
+ RegisterGraphScript(12937, "AnyStateNodeInspector", kAnimationStateMachineNamespace);
+
+ RegisterBuiltinEditorScript(13201, "HeapshotWindow");
+}
+
+static void DoRegisterBuiltinEditorScript (int fileID, const char* className, const char* _namespace, const char* assemblyName, bool isEditorScript)
+{
+#if ENABLE_MONO
+ MonoScript* script = NEW_OBJECT(MonoScript);
+ script->Reset();
+ script->Init("", className, _namespace, assemblyName, isEditorScript);
+ script->SetName(className);
+ script->SetPathName(className);
+
+ SInt32 oldInstanceID = GetPersistentManager().GetInstanceIDFromPathAndFileID(kResourcePath, fileID);
+ if (Object::IDToPointer(oldInstanceID) != NULL)
+ {
+ ErrorString("Duplicate editor builtin script " + string(className));
+ }
+
+ GetPersistentManager().MakeObjectPersistentAtFileID(script->GetInstanceID(), fileID, kResourcePath);
+ script->SetHideFlags(Object::kDontSave);
+
+ if (isEditorScript)
+ GetMonoScriptManager().RegisterEditorScript(*script);
+ else
+ GetMonoScriptManager().RegisterRuntimeScript(*script);
+ script->AwakeFromLoad(kDefaultAwakeFromLoad);
+
+#endif
+}
+
+static void RegisterBuiltinEditorScript (int instanceID, const char* className)
+{
+ DoRegisterBuiltinEditorScript (instanceID, className, "UnityEditor", "UnityEditor.dll", true);
+}
+
+static void RegisterBuiltinEditorInternalScript (int instanceID, const char* className)
+{
+ DoRegisterBuiltinEditorScript (instanceID, className, "UnityEditorInternal", "UnityEditor.dll", true);
+}
+
+#if ENABLE_UNIT_TESTS
+void BuiltinResourceManager::RegisterShadersWithRegistry (ScriptMapper* registry)
+{
+ for (OrderedShaderStartup::iterator i = m_OrderedShaderStartup.begin ();
+ i != m_OrderedShaderStartup.end (); i++)
+ {
+ const ShaderInitializationData& data = *i;
+ registry->AddBuiltinShader (data.shaderClassName, PPtr<Shader> (data.cachedInstanceID));
+ }
+}
+#endif
+
+void BuiltinResourceManager::LoadDefaultResourcesFromEditor ()
+{
+ if (!m_AllowResourceManagerAccess)
+ return;
+
+ PersistentManager& pm = GetPersistentManager ();
+
+ // They need to show up in the script mapper
+ for (OrderedShaderStartup::iterator i=m_OrderedShaderStartup.begin ();i != m_OrderedShaderStartup.end ();i++)
+ {
+ const ShaderInitializationData& data = *i;
+ GetScriptMapper ().AddBuiltinShader (data.shaderClassName, PPtr<Shader>(data.cachedInstanceID));
+
+ void AddBuiltinShaderInfoForMenu (const std::string& name, SInt32 id);
+ AddBuiltinShaderInfoForMenu (data.shaderClassName, data.cachedInstanceID);
+ }
+
+ // In non-release builds, verify that the setup of all shaders is correct and no shaders have errors.
+ #if !UNITY_RELEASE
+ for (OrderedShaderStartup::iterator i=m_OrderedShaderStartup.begin ();i != m_OrderedShaderStartup.end ();i++)
+ {
+ Shader* shader = dynamic_pptr_cast<Shader*> (GetResource (ClassID (Shader), i->resourceName));
+ Assert(shader != NULL);
+ if (shader)
+ {
+ if (shader->GetScriptClassName() != i->shaderClassName)
+ {
+ AssertString("Shadername is: " + shader->GetScriptClassName() + " but hardcoded name was: " + i->shaderClassName);
+ }
+
+ Assert(shader->GetHideFlags() == m_RequiredHideFlags);
+ if (shader->GetErrors().HasErrorsOrWarnings())
+ shader->GetErrors().LogErrors(shader->GetScriptClassName().c_str(), "BUILTIN SHADER", shader, shader->GetInstanceID());
+ }
+ }
+ #endif
+
+ /////@TODO: Verify that all assets in the builtin resource file are actually being registered!
+
+ // Scripts need to register with mono manager
+ for (Resources::iterator i=m_Resources.begin ();i != m_Resources.end ();i++)
+ {
+ if (i->classID == ClassID (MonoScript))
+ {
+ int instanceID = pm.GetInstanceIDFromPathAndFileID (m_ResourcePath, i->fileID);
+ MonoScript* script = dynamic_instanceID_cast<MonoScript*> (instanceID);
+ if (script)
+ GetMonoScriptManager().RegisterRuntimeScript(*script);
+ else
+ Assert("Script can't be found");
+ }
+ }
+}
+
+
+void BuiltinResourceManager::LoadAllDefaultResourcesFromEditor ()
+{
+ GetBuiltinResourceManager().LoadDefaultResourcesFromEditor();
+ GetBuiltinExtraResourceManager().LoadDefaultResourcesFromEditor();
+
+ // Editor scripts need to be generated and registered
+ GenerateEditorScripts ();
+}
+#endif
+
+
+vector<Object*> BuiltinResourceManager::GetAllResources ()
+{
+ vector<Object*> result;
+ result.reserve (m_Resources.size ());
+
+ for (Resources::const_iterator iter = m_Resources.begin (); iter != m_Resources.end (); ++iter)
+ result.push_back (GetResource (iter->classID, iter->name));
+
+ return result;
+}
+
+// Returns the first found resource with name that is derived from classID
+Object* BuiltinResourceManager::GetResource (int classID, const string& name)
+{
+ if (!m_AllowResourceManagerAccess)
+ {
+ bool mustLoadBuiltinShader =
+ name == "Internal-Halo.shader" ||
+ name == "Internal-GUITexture.shader" ||
+ name == "Normal-Diffuse.shader";
+ // Allow for some specific exceptions!
+ if (!mustLoadBuiltinShader)
+ {
+ AssertString("Loading builtin resources is forbidden when building them: " + name + "\nIf it is really necessary you can add a exception case at this assert.");
+ return NULL;
+ }
+ }
+
+ Resource proxy;
+ proxy.classID = classID;
+ proxy.name = name.c_str();
+
+ Resources::iterator found = m_Resources.find (proxy);
+ if (found == m_Resources.end())
+ {
+ AssertString("Failed to find " + name);
+ return NULL;
+ }
+
+ AssertIf (found->cachedInstanceID == 0);
+ Object* ptr = PPtr<Object> (found->cachedInstanceID);
+ if (ptr == NULL || !ptr->IsDerivedFrom (classID))
+ {
+ ErrorString ("The resource " + name + " could not be loaded from the resource file!");
+ return NULL;
+ }
+ else
+ {
+ AssertIf(ptr->GetHideFlags() != m_RequiredHideFlags);
+ return ptr;
+ }
+}
+
+void BuiltinResourceManager::RegisterShader (LocalIdentifierInFileType fileID, const char* name, const char* shaderClassName, bool userVisible)
+{
+#if UNITY_EDITOR
+ int cachedInstanceID =
+#endif
+ RegisterResourceInternal(fileID, name, "Shader", shaderClassName, userVisible);
+
+#if UNITY_EDITOR
+ // Register shader initialization
+ ShaderInitializationData data;
+ data.shaderClassName = shaderClassName;
+ data.resourceName = name;
+ data.cachedInstanceID = cachedInstanceID;
+ m_OrderedShaderStartup.push_back(data);
+#endif
+}
+
+int BuiltinResourceManager::RegisterResourceInternal (LocalIdentifierInFileType fileID, const char* name, const char* className, const char* displayName, bool userVisible)
+{
+ RegisterDebugUsedFileID (fileID, name);
+
+ Resource r;
+ r.name = name;
+ r.classID = Object::StringToClassID (className);
+ r.fileID = fileID;
+ r.cachedInstanceID = GetPersistentManager().GetInstanceIDFromPathAndFileID(m_ResourcePath, fileID);
+ r.userVisible = userVisible;
+ #if UNITY_EDITOR
+ if (displayName != NULL)
+ r.cachedDisplayName = displayName;
+ #endif
+ m_Resources.push_unsorted (r);
+
+ return r.cachedInstanceID;
+}
+
+void BuiltinResourceManager::RegisterResource (LocalIdentifierInFileType fileID, const char* name, const char* className, bool userVisible)
+{
+ // RegisterResource can not be used on shaders, use RegisterShader
+ Assert(strcmp(className, "Shader") != 0);
+
+ RegisterResourceInternal(fileID, name, className, NULL, userVisible);
+}
+
+static BuiltinResourceManager* gBuiltinResourceManager;
+static BuiltinResourceManager* gBuiltinOldWebResourceManager;
+
+
+void BuiltinResourceManager::StaticInitialize()
+{
+ gBuiltinResourceManager = UNITY_NEW(BuiltinResourceManager,kMemResource);
+ gBuiltinOldWebResourceManager = UNITY_NEW(BuiltinResourceManager,kMemResource);
+
+#if UNITY_EDITOR
+ gBuiltinExtraResourceManager = UNITY_NEW(BuiltinResourceManager,kMemResource);
+#endif
+
+ s_AreResourcesInitialized = false;
+}
+
+void BuiltinResourceManager::StaticDestroy()
+{
+ s_AreResourcesInitialized = false;
+
+ UNITY_DELETE(gBuiltinResourceManager,kMemResource);
+ UNITY_DELETE(gBuiltinOldWebResourceManager,kMemResource);
+#if UNITY_EDITOR
+ UNITY_DELETE(gBuiltinExtraResourceManager,kMemResource);
+#endif
+}
+
+static RegisterRuntimeInitializeAndCleanup s_BuiltinResourceManagerCallbacks(BuiltinResourceManager::StaticInitialize, BuiltinResourceManager::StaticDestroy);
+
+BuiltinResourceManager& GetBuiltinResourceManager ()
+{
+ return *gBuiltinResourceManager;
+}
+
+BuiltinResourceManager& GetBuiltinOldWebResourceManager ()
+{
+ return *gBuiltinOldWebResourceManager;
+}
+
+
+void BuiltinResourceManager::DestroyAllResources()
+{
+ for (Resources::iterator i=m_Resources.begin ();i != m_Resources.end ();i++)
+ {
+ Object* obj = Object::IDToPointer (i->cachedInstanceID);
+ if(obj)
+ {
+ DestroySingleObject(obj);
+ }
+ }
+ m_Resources.clear();
+}
+
+#if DEBUGMODE
+void BuiltinResourceManager::RegisterDebugUsedFileID (LocalIdentifierInFileType fileID, const char* name)
+{
+ if (!m_UsedFileIDs.insert (fileID).second)
+ ErrorString (Format ("File ID %d is registered twice (%s)", (int)fileID, name));
+}
+#endif
+
+#if UNITY_EDITOR
+
+BuiltinResourceManager& GetBuiltinExtraResourceManager ()
+{
+ return *gBuiltinExtraResourceManager;
+}
+
+#endif // #if UNITY_EDITOR
+
+ResourceManager::ResourceManager (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_NeedsReload = true;
+}
+
+ResourceManager::~ResourceManager ()
+{
+}
+
+ResourceManager::range ResourceManager::GetAll ()
+{
+ #if UNITY_EDITOR
+ if (m_NeedsReload)
+ RebuildResources();
+ #endif
+
+ return make_pair (m_Container.begin(), m_Container.end());
+}
+
+ResourceManager::range ResourceManager::GetPathRange (const string& path)
+{
+ #if UNITY_EDITOR
+ if (m_NeedsReload)
+ RebuildResources();
+ #endif
+
+ return m_Container.equal_range(ToLower(path));
+}
+
+#if UNITY_EDITOR
+void ResourceManager::RebuildResources ()
+{
+ m_NeedsReload = !AssetDatabase::Get().GetAllResources(m_Container);
+}
+#endif
+
+void ResourceManager::ClearDependencyInfo ()
+{
+ m_DependentAssets.clear ();
+}
+
+void ResourceManager::PreloadDependencies (SInt32 instanceId)
+{
+ std::set<SInt32> parents;
+ PreloadDependencies (instanceId, parents);
+}
+
+void ResourceManager::PreloadDependencies (SInt32 instanceId, std::set<SInt32>& visited)
+{
+ if (visited.find (instanceId) != visited.end ())
+ return;
+
+ DependencyContainer::iterator it = std::lower_bound (m_DependentAssets.begin (), m_DependentAssets.end (), instanceId, ResourceManager::Dependency::Sorter ());
+ // Check if we actually found info for the object we're looking for. As we're using lower_bound, we might get back
+ // another object that is closest smallest.
+ if (it == m_DependentAssets.end () || it->object.GetInstanceID () != instanceId)
+ return;
+
+ visited.insert (it->object.GetInstanceID ());
+
+ for (size_t i = 0; i<it->dependencies.size (); ++i)
+ {
+ PPtr<Object> pptr = it->dependencies[i];
+ *pptr; // fetch
+ PreloadDependencies (pptr.GetInstanceID (), visited);
+ }
+}
+
+template<class TransferFunc>
+void ResourceManager::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+ AssertIf (transfer.GetFlags() & kPerformUnloadDependencyTracking);
+
+ transfer.Transfer(m_Container, "m_Container");
+
+ // Wee need dependent-assets only in the player
+ if (transfer.IsSerializingForGameRelease ())
+ transfer.Transfer(m_DependentAssets, "m_DependentAssets");
+
+ if (transfer.IsReading ())
+ std::sort (m_DependentAssets.begin (), m_DependentAssets.end (), ResourceManager::Dependency::Sorter ());
+}
+
+bool ResourceManager::ShouldIgnoreInGarbageDependencyTracking ()
+{
+ return true;
+}
+
+IMPLEMENT_CLASS (ResourceManager)
+IMPLEMENT_OBJECT_SERIALIZE (ResourceManager)
+GET_MANAGER (ResourceManager)
+
diff --git a/Runtime/Misc/ResourceManager.h b/Runtime/Misc/ResourceManager.h
new file mode 100644
index 0000000..75b3c01
--- /dev/null
+++ b/Runtime/Misc/ResourceManager.h
@@ -0,0 +1,224 @@
+#ifndef RESOURCEMANAGER_H
+#define RESOURCEMANAGER_H
+
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Misc/Allocator.h"
+#include <list>
+#include "Runtime/Utilities/vector_set.h"
+#include <string>
+#include "Configuration/UnityConfigure.h"
+
+#define kDefaultFontName "Arial.ttf"
+
+class UnityWebStream;
+class CachedUnityWebStream;
+class ScriptMapper;
+
+/*
+ The resource manager can lookup assets by name and classID.
+
+ An resource file is generated with the editor. And is shipped with unity.
+ It provides the most basic assets necessary to run unity.
+ Eg. some shaders, some textures for lighting
+
+ If you want to add a new resource to the ResourceManager:
+ - Call RegisterResource inside InitializeResources (Use a unique id and never reuse an id)
+ - Open a project with the resources you want to add
+ All resources have to be placed inside a "Assets/DefaultResources"
+ - When you have assembled all the resources. Use BuildResources from the main menu.
+ This will Install them in the right folder and also
+ invoke installframeworks so it is installed in all versions of the editor
+*/
+
+class BuiltinResourceManager
+{
+ public:
+
+ static void StaticInitialize();
+ static void StaticDestroy();
+
+ static void InitializeAllResources ();
+ static bool AreResourcesInitialized();
+ void DestroyAllResources ();
+
+ void InitializeOldWebResources ();
+
+ #if UNITY_EDITOR
+ static void SetAllowLoadingBuiltinResources (bool allowLoadingBuiltinResources);
+ void RegisterBuiltinScriptsForBuilding ();
+ static void LoadAllDefaultResourcesFromEditor ();
+ static void GetBuiltinResourcesOfClass (int classID, std::vector< std::pair<std::string,int> >& outResources);
+ #endif
+
+ // Returns the first found resource with name that is derived from classID
+ Object* GetResource (int classID, const std::string& name);
+
+ std::vector<Object*> GetAllResources ();
+
+ int GetRequiredHideFlags() const { return m_RequiredHideFlags; }
+
+ #if ENABLE_UNIT_TESTS
+ void RegisterShadersWithRegistry (ScriptMapper* registry);
+ #endif
+
+private:
+
+ void RegisterResource (LocalIdentifierInFileType fileID, const char* name, const char* className, bool userVisible = true);
+ int RegisterResourceInternal (LocalIdentifierInFileType fileID, const char* name, const char* className, const char* displayName, bool userVisible = true);
+
+ void RegisterShader (LocalIdentifierInFileType fileID, const char* name, const char* shaderClassName, bool userVisible = true);
+
+ void RegisterBuiltinScript (int instanceID, const char* className, bool buildResourceFiles);
+
+ void InitializeResources ();
+ void InitializeExtraResources ();
+ void LoadDefaultResourcesFromEditor ();
+
+ struct Resource
+ {
+ const char* name;
+ int classID;
+
+ LocalIdentifierInFileType fileID;
+ int cachedInstanceID;
+ bool userVisible;
+ #if UNITY_EDITOR
+ std::string cachedDisplayName;
+ #endif
+
+ friend bool operator < (const Resource& lhs, const Resource& rhs)
+ {
+ int res = strcmp(lhs.name, rhs.name);
+ if (res != 0)
+ return res < 0;
+
+ return lhs.classID < rhs.classID;
+ }
+ };
+
+ typedef vector_set<Resource> Resources;
+ Resources m_Resources;
+ std::string m_ResourcePath;
+ int m_RequiredHideFlags;
+ bool m_AllowResourceManagerAccess;
+
+ #if UNITY_EDITOR
+
+ struct ShaderInitializationData
+ {
+ const char* shaderClassName;
+ const char* resourceName;
+ int cachedInstanceID;
+ };
+
+ typedef std::vector<ShaderInitializationData> OrderedShaderStartup;
+ OrderedShaderStartup m_OrderedShaderStartup;
+
+ friend bool BuildBuiltinAssetBundle (BuiltinResourceManager& resourceManager, const std::string& targetFile, const std::string& resourceFolder, InstanceIDResolveCallback* resolveCallback, const std::set<std::string>& ignoreList, BuildTargetSelection platform, int options);
+ friend bool GenerateBuiltinAssetPreviews ();
+ void GetResourcesOfClass (int classID, std::vector< std::pair<std::string,int> >& outResources);
+ #endif
+
+ #if DEBUGMODE
+ void RegisterDebugUsedFileID (LocalIdentifierInFileType fileID, const char* name);
+ std::set<LocalIdentifierInFileType, std::less<LocalIdentifierInFileType>, STL_ALLOCATOR(kMemPermanent, LocalIdentifierInFileType) > m_UsedFileIDs;
+ #else
+ void RegisterDebugUsedFileID (LocalIdentifierInFileType fileID, const char* name) {}
+ #endif
+};
+
+BuiltinResourceManager& GetBuiltinResourceManager ();
+BuiltinResourceManager& GetBuiltinExtraResourceManager ();
+BuiltinResourceManager& GetBuiltinOldWebResourceManager ();
+
+template<class T>
+T* GetBuiltinResource (const std::string& name)
+{
+ Object* res = GetBuiltinResourceManager ().GetResource (T::GetClassIDStatic (), name);
+ return static_cast<T*> (res);
+}
+
+#if WEBPLUG
+template<class T>
+T* GetBuiltinOldWebResource (const std::string& name)
+{
+ Object* res = GetBuiltinOldWebResourceManager ().GetResource (T::GetClassIDStatic (), name);
+ return static_cast<T*> (res);
+}
+#endif // #if WEBPLUG
+
+#if UNITY_EDITOR
+template<class T>
+T* GetBuiltinExtraResource (const std::string& name)
+{
+ Object* res = GetBuiltinExtraResourceManager ().GetResource (T::GetClassIDStatic (), name);
+ return static_cast<T*> (res);
+}
+#endif // #if UNITY_EDITOR
+
+
+class ResourceManager : public GlobalGameManager
+{
+ public:
+
+ struct Dependency
+ {
+ typedef std::vector<PPtr<Object> > ChildCont;
+ DECLARE_SERIALIZE (ResourceManager_Dependency)
+
+ struct Sorter
+ {
+ bool operator() (Dependency const& a, Dependency const& b) const { return a.object.GetInstanceID () < b.object.GetInstanceID (); }
+ bool operator() (Dependency const& a, SInt32 b) const { return a.object.GetInstanceID () < b; }
+ bool operator() (SInt32 a, Dependency const& b) const { return a < b.object.GetInstanceID (); }
+ };
+
+ Dependency () {}
+ explicit Dependency (SInt32 thisInstanceId) : object (thisInstanceId) {}
+
+ PPtr<Object> object;
+ ChildCont dependencies;
+ };
+
+ typedef std::vector<Dependency> DependencyContainer;
+
+ DECLARE_OBJECT_SERIALIZE (ResourceManager)
+ REGISTER_DERIVED_CLASS (ResourceManager, GlobalGameManager)
+
+ ResourceManager (MemLabelId label, ObjectCreationMode mode);
+
+ typedef std::multimap<UnityStr, PPtr<Object> > container;
+ typedef std::multimap<UnityStr, PPtr<Object> >::iterator iterator;
+ typedef std::pair<iterator, iterator> range;
+
+ void RegisterResource (std::string& path, PPtr<Object> resource);
+ std::string GetResourcePath (const std::string& path);
+
+ range GetAll ();
+ range GetPathRange (const string& path);
+
+ void SetResourcesNeedRebuild() { m_NeedsReload = true; }
+ bool ShouldIgnoreInGarbageDependencyTracking ();
+
+ void RebuildResources ();
+
+ void ClearDependencyInfo ();
+ void PreloadDependencies (SInt32 instanceId);
+
+ DependencyContainer m_DependentAssets;
+ container m_Container;
+ bool m_NeedsReload;
+
+private:
+ void PreloadDependencies (SInt32 instanceId, std::set<SInt32>& visited);
+};
+
+ResourceManager& GetResourceManager ();
+
+extern const char* kResourcePath;
+extern const char* kOldWebResourcePath;
+extern const char* kEditorResourcePath;
+extern const char* kDefaultExtraResourcesPath;
+
+#endif
diff --git a/Runtime/Misc/ResourceManagerGUIDs.h b/Runtime/Misc/ResourceManagerGUIDs.h
new file mode 100644
index 0000000..5dba07f
--- /dev/null
+++ b/Runtime/Misc/ResourceManagerGUIDs.h
@@ -0,0 +1,5 @@
+#include "Runtime/Utilities/GUID.h"
+
+inline UnityGUID GetEditorResourcesGUID () { return UnityGUID (0, 0, 13, 0); }
+inline UnityGUID GetBuiltinResourcesGUID () { return UnityGUID (0, 0, 14, 0); }
+inline UnityGUID GetBuiltinExtraResourcesGUID () { return UnityGUID (0, 0, 15, 0); }
diff --git a/Runtime/Misc/ResourceManagerUtility.cpp b/Runtime/Misc/ResourceManagerUtility.cpp
new file mode 100644
index 0000000..0f8ad5d
--- /dev/null
+++ b/Runtime/Misc/ResourceManagerUtility.cpp
@@ -0,0 +1,55 @@
+#include "ResourceManagerUtility.h"
+
+#if ENABLE_SCRIPTING
+
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+
+ScriptingObjectPtr GetScriptingBuiltinResourceFromManager(BuiltinResourceManager& resources, ScriptingObjectPtr type, const std::string& path)
+{
+
+#if ENABLE_MONO || UNITY_WINRT
+ if(path.size() == 0) Scripting::RaiseArgumentException("Invalid path");
+#endif
+
+ ScriptingTypePtr requiredclass = GetScriptingTypeRegistry().GetType(type);
+ int classID = Scripting::GetClassIDFromScriptingClass(GetScriptingTypeRegistry().GetType(type));
+
+ Object* o = resources.GetResource (classID, path);
+
+ ScriptingObjectPtr mono = Scripting::ScriptingWrapperFor(o);
+
+#if ENABLE_MONO
+ // The third parameter 'false' doesn't match in scripting_class_is_subclass_of, need to check
+ if (mono != SCRIPTING_NULL && mono_class_is_subclass_of(mono_object_get_class(mono), requiredclass, false))
+ return mono;
+ else
+ return SCRIPTING_NULL;
+#else
+ if (mono != SCRIPTING_NULL && scripting_class_is_subclass_of(scripting_object_get_class(mono, GetScriptingTypeRegistry()), requiredclass))
+ return mono;
+ else
+ return SCRIPTING_NULL;
+#endif
+}
+
+ScriptingObjectPtr GetScriptingBuiltinResource(ScriptingObjectPtr type, const std::string& path)
+{
+ return GetScriptingBuiltinResourceFromManager(GetBuiltinResourceManager(), type, path);
+}
+
+#endif
+
+
+#if UNITY_EDITOR
+
+ScriptingObjectPtr GetMonoBuiltinExtraResource(ScriptingObjectPtr type, ScriptingStringPtr path)
+{
+ return GetScriptingBuiltinResourceFromManager(GetBuiltinExtraResourceManager(), type, scripting_cpp_string_for(path));
+}
+
+#endif
diff --git a/Runtime/Misc/ResourceManagerUtility.h b/Runtime/Misc/ResourceManagerUtility.h
new file mode 100644
index 0000000..fbec342
--- /dev/null
+++ b/Runtime/Misc/ResourceManagerUtility.h
@@ -0,0 +1,21 @@
+#ifndef RESOURCEMANAGERUTILITY_H
+#define RESOURCEMANAGERUTILITY_H
+
+#include "UnityPrefix.h"
+
+#include <string>
+
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+
+class BuiltinResourceManager;
+
+ScriptingObjectPtr GetScriptingBuiltinResourceFromManager(BuiltinResourceManager& resources, ScriptingObjectPtr type, const std::string& path);
+ScriptingObjectPtr GetScriptingBuiltinResource(ScriptingObjectPtr type, const std::string& path);
+
+#if UNITY_EDITOR
+
+ScriptingObjectPtr GetMonoBuiltinExtraResource(ScriptingObjectPtr type, ScriptingStringPtr path);
+
+#endif
+
+#endif
diff --git a/Runtime/Misc/SaveAndLoadHelper.cpp b/Runtime/Misc/SaveAndLoadHelper.cpp
new file mode 100644
index 0000000..7bc0c2d
--- /dev/null
+++ b/Runtime/Misc/SaveAndLoadHelper.cpp
@@ -0,0 +1,1106 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "SaveAndLoadHelper.h"
+#include "RegisterAllClasses.h"
+
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/BaseClasses/ManagerContextLoading.h"
+#include "Configuration/UnityConfigureRevision.h"
+#include "Runtime/Misc/BuildSettings.h"
+#if ENABLE_UNITYGUI
+#include "Runtime/IMGUI/GUIManager.h"
+#endif
+#include "Runtime/Terrain/TerrainData.h"
+#include "Runtime/Misc/PreloadManager.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Utilities/GUID.h"
+#include "Runtime/Animation/Animation.h"
+#include "Runtime/Utilities/vector_set.h"
+#include "Runtime/Input/InputManager.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Animation/AnimationManager.h"
+#include "QualitySettings.h"
+#include "Runtime/Animation/AnimationClip.h"
+#if ENABLE_AUDIO
+#include "Runtime/Audio/AudioClip.h"
+#endif
+#include "Runtime/Serialize/SerializedFile.h"
+#include "Runtime/Profiler/ProfilerHistory.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/MemoryProfilerStats.h"
+#include "Runtime/Profiler/ProfilerConnection.h"
+#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h"
+#include "CaptureScreenshot.h"
+#include "Runtime/Graphics/Transform.h"
+#include "GameObjectUtility.h"
+#include "Runtime/Animation/Animation.h"
+#include "Runtime/BaseClasses/Cursor.h"
+#include "BatchDeleteObjects.h"
+
+#include "Runtime/Testing/Testing.h"
+#include "Runtime/Camera/UnityScene.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/BaseClasses/Tags.h"
+#include "Runtime/Serialize/TransferFunctions/RemapPPtrTransfer.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#if ENABLE_MONO
+#include "Runtime/Mono/MonoIncludes.h"
+#endif
+#include "Runtime/GfxDevice/GfxDeviceSetup.h"
+#include "Runtime/Misc/AssetBundle.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Input/GetInput.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Mono/MonoAttributeHelpers.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Graphics/RenderBufferManager.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/GameCode/CallDelayed.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystem.h" // ParticleSystem::UpdateAll ()
+#include "Runtime/Filters/Particles/ParticleEmitter.h"
+#include "Runtime/IMGUI/TextMeshGenerator2.h"
+#include "Runtime/IMGUI/GUIClip.h"
+#include "Runtime/Threads/JobScheduler.h"
+#include "External/shaderlab/Library/pass.h"
+#include "Runtime/Graphics/LightmapSettings.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+
+#include "Runtime/GameCode/CallDelayed.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h"
+#include "Runtime/Serialize/AwakeFromLoadQueue.h"
+#include "Runtime/Math/FloatExceptions.h"
+#include "Runtime/Audio/AudioManager.h"
+
+#if UNITY_EDITOR
+#include "Runtime/BaseClasses/CleanupManager.h"
+#include "Editor/Src/HierarchyState.h"
+#include "Editor/Src/EditorSettings.h"
+#include "Editor/Src/Application.h"
+#include "Editor/Src/AssetServer/ASCache.h"
+#include "Editor/Src/EditorBuildSettings.h"
+#include "Editor/Src/EditorUserBuildSettings.h"
+#include "Editor/Src/SceneInspector.h"
+#include "Editor/Src/Prefabs/Prefab.h"
+#include "Editor/Src/AssetPipeline/AssetDatabase.h"
+#include "Editor/Src/AssetPipeline/AssetInterface.h"
+ #if ENABLE_SPRITES
+ #include "Editor/Src/SpritePacker/SpritePacker.h"
+ #endif
+#include "Editor/Src/BuildPipeline/LODGroupStripping.h"
+#include "Editor/Src/GUIDPersistentManager.h"
+#include "Editor/Src/EditorAssetGarbageCollectManager.h"
+#include "Editor/Src/Prefabs/GenerateCachedTypeTree.h"
+#include "Editor/Src/AssetPipeline/MonoCompilationPipeline.h"
+#include "Player.h"
+#include "PlayerSettings.h"
+#endif
+
+#if ENABLE_WWW || ENABLE_CACHING
+#include "PlatformDependent/CommonWebPlugin/UnityWebStream.h"
+#include "CachingManager.h"
+#include "PlatformDependent/CommonWebPlugin/CompressedFileStreamMemory.h"
+#endif
+
+
+#if SUPPORT_REPRODUCE_LOG
+#include "ReproductionLog.h"
+#endif
+
+#include "Runtime/Network/PlayerCommunicator/EditorConnection.h"
+
+const char* kMainData = "mainData";
+
+using namespace std;
+
+std::string SelectDataFolder ();
+
+static void EditorBeforeLoadingCleanup ();
+static void ResetManagersAfterLoadEditor ();
+static void CollectAllSceneObjects (InstanceIDArray& instanceIDs);
+static void CompleteAwakeSequence (const std::string& path, AwakeFromLoadQueue& awakeQueue);
+
+void DestroyWorld (bool destroySceneAssets)
+{
+ InstanceIDArray objects;
+
+ #if UNITY_EDITOR
+ if (destroySceneAssets)
+ CollectAllSceneObjects (objects);
+ else
+ CollectSceneGameObjects (objects);
+ #else
+ Assert(!destroySceneAssets);
+ CollectSceneGameObjects (objects);
+ #endif
+
+ Object* o;
+ // GameObjects first
+ for (InstanceIDArray::iterator i=objects.begin ();i != objects.end ();++i)
+ {
+ o = Object::IDToPointer (*i);
+ AssertIf (o && o->IsPersistent ());
+ GameObject* go = dynamic_pptr_cast<GameObject*> (o);
+ // Only Destroy root level GameObjects. The children will be destroyed
+ // as part of them. That way, we ensure that the hierarchy is walked correctly,
+ // and all objects in the hieararchy will be marked as deactivated when destruction happens.
+ if (go != NULL && go->GetComponent(Transform).GetParent() == NULL)
+ DestroyObjectHighLevel (o);
+ }
+
+ // normal objects whatever they might be after that
+ for (InstanceIDArray::iterator i=objects.begin ();i != objects.end ();++i)
+ {
+ o = Object::IDToPointer (*i);
+ AssertIf (o && o->IsPersistent ());
+ DestroyObjectHighLevel (o);
+ }
+
+ objects.clear ();
+ CollectLevelGameManagers (objects);
+
+ // Gamemanagers & Scene last
+ for (InstanceIDArray::iterator i=objects.begin ();i != objects.end ();++i)
+ {
+ o = Object::IDToPointer (*i);
+ AssertIf (o && o->IsPersistent ());
+ DestroyObjectHighLevel (o);
+ }
+ objects.clear ();
+
+ GlobalCallbacks::Get().didUnloadScene.Invoke();
+
+ #if DEBUGMODE
+ ValidateNoSceneObjectsAreLoaded (destroySceneAssets);
+ #endif
+}
+
+#if WEBPLUG
+#define GET_DATA_FOLDER ""
+#else
+#define GET_DATA_FOLDER SelectDataFolder()
+#endif
+
+bool InitializeEngineNoGraphics ()
+{
+ static bool isInitialized = false;
+ if (isInitialized == false)
+ {
+#if UNITY_EDITOR
+ EditorAssetGarbageCollectManager::StaticInitialize();
+#endif
+ #if SUPPORT_THREADS
+ Thread::mainThreadId = Thread::GetCurrentThreadID ();
+ #endif
+ #if ENABLE_PLAYERCONNECTION
+ #if UNITY_EDITOR
+ EditorConnection::Initialize();
+ #else
+ PlayerConnection::Initialize(GET_DATA_FOLDER);
+ InstallPlayerConnectionLogging(true);
+ #endif
+ #endif
+
+ #if ENABLE_PROFILER
+ InitializeMemoryProfilerStats();
+ UnityProfiler::Initialize();
+ ProfilerConnection::Initialize();
+ #if UNITY_EDITOR
+ ProfilerHistory::Initialize();
+ #endif
+ #endif
+
+#if ENABLE_PLAYERCONNECTION
+#if UNITY_EDITOR
+ EditorConnection::Get().PollWithCustomMessage();
+#else
+ // Bug: Calls GetBuildSettings() inside, but it's not yet initialized !!! At least on windows standalone player
+#if !UNITY_WIN && !UNITY_OSX && !UNITY_IPHONE
+ PlayerConnection::Get().Poll();
+#endif
+#endif
+#endif
+
+ InitializeBatchDelete();
+ RegisterAllClasses ();
+ Object::InitializeAllClasses ();
+ GameObject::InitializeMessageIdentifiers ();
+ ManagerContextInitializeClasses ();
+ RenderBufferManager::InitRenderBufferManager ();
+
+// On Linux Editor we initialize ScreenManager much earlier. Avoid doing that
+// again here so we won't zero out the members.
+#if !(UNITY_EDITOR && UNITY_LINUX)
+ InitScreenManager ();
+#endif
+
+ InitFloatExceptions();
+ #if ENABLE_UNITYGUI
+ InitGUIManager ();
+ #endif
+
+ // Init platform specific input support.
+ // Aras: on windows input must be initialized later, after the
+ // actual window is set up.
+ #if !UNITY_WIN && !UNITY_WII && !UNITY_IPHONE && !UNITY_ANDROID && !UNITY_FLASH && !UNITY_WEBGL && !UNITY_BB10 && !UNITY_TIZEN
+ InputInit();
+ #endif
+ Object::CallInitializeClass ();
+ }
+ return true;
+}
+
+#if (UNITY_OSX || (UNITY_LINUX && SUPPORT_X11)) && !UNITY_PEPPER
+void SetGraphicsBatchMode( bool batch );
+#endif
+
+bool InitializeEngineGraphics (bool batch)
+{
+ static bool isInitialized = false;
+ if (isInitialized == false)
+ {
+ printf_console( "Initialize engine version: %s\n", UNITY_VERSION_FULL_NICE );
+
+#if (UNITY_OSX || (UNITY_LINUX && SUPPORT_X11)) && !UNITY_PEPPER
+ SetGraphicsBatchMode( batch );
+#endif
+#if UNITY_OSX || UNITY_FLASH || (UNITY_LINUX && WEBPLUG) || UNITY_WEBGL || (UNITY_WIN && !UNITY_WINRT) && !UNITY_PEPPER
+ if( !InitializeGfxDevice() )
+ return false;
+#endif
+
+#if ENABLE_PERFORMANCE_TESTS
+ RUN_PERFORMANCE_TESTS();
+#endif
+
+ #if ENABLE_MULTITHREADED_CODE || ENABLE_MULTITHREADED_SKINNING
+ CreateJobScheduler();
+ #endif
+
+ ShaderLab::InitShaderLab ();
+
+ Object::CallPostInitializeClass ();
+ GameObject::InitializeMessageHandlers ();
+ BuiltinResourceManager::InitializeAllResources ();
+
+ // Make sure the default shaders are always preloaded to avoid loading them during
+ // threaded background loading
+ Shader::LoadDefaultShaders ();
+
+ isInitialized = true;
+ GlobalCallbacks::Get().initializedEngineGraphics.Invoke();
+ }
+ return true;
+}
+
+void CleanupAllObjects (bool reloadable)
+{
+ vector<SInt32> objects;
+
+ // Before each loop dealing with Objects here, we refetch all Objects. This is because
+ // deleting some game object might actually create new objects (i.e. components are
+ // dereferenced in GO destructor).
+
+ // Delete all non-temporary game objects first
+ Object::FindAllDerivedObjects (ClassID (Object), &objects);
+ for (int i=0;i<objects.size();i++)
+ {
+ Object* o = Object::IDToPointer(objects[i]);
+ if (o && o->IsDerivedFrom(ClassID(GameObject)) && !o->IsPersistent() && !o->TestHideFlag(Object::kDontSave))
+ {
+ DestroyObjectHighLevel (o);
+ }
+ }
+
+ // Delete all game objects first
+ objects.clear();
+ Object::FindAllDerivedObjects (ClassID (Object), &objects);
+ for (int i=0;i<objects.size();i++)
+ {
+ Object* o = Object::IDToPointer(objects[i]);
+#if WEBPLUG
+ Transform* t = GetIdentityTransform();
+#endif
+ if (o && !o->IsPersistent() && o->IsDerivedFrom(ClassID(GameObject)))
+ {
+#if WEBPLUG
+ if(reloadable && o == t->GetGameObjectPtr()) {
+ // THIS IS A MASSIVE HACK
+ // We do this because, when the Unity player is reloading, it frees all
+ // the gameobjects in memory.
+ // The Renderer object keeps a global static singleton Identity Transform,
+ // attached to an invisible game object that is created on first startup.
+ // Because the startup code is tied deeply into the initialization of
+ // the graphics engine, it's less hacky to Not Destroy that one game object.
+ // We seriously need to eliminate these global statics or place them under
+ // the management of a game manager object, which can refresh them as needed.
+ continue;
+ }
+#endif
+ DestroyObjectHighLevel (o);
+ }
+ }
+
+ LockObjectCreation();
+
+ // Do cleanup after all game objects are killed thus
+ // terrains will be deleted, and RenderTexture which are allocated by the render buffer manager will not be deleted yet.
+ TextMeshGenerator2::Flush();
+ if (GetRenderBufferManagerPtr())
+ GetRenderBufferManager().Cleanup();
+ ShaderLab::Pass::DidClearAllTempRenderTextures ();
+
+ // First of all we need to delete all non-temporary objects
+ // This is because usually there are dependencies from non-temporary objects
+ // to temporary objects
+ objects.clear();
+ Object::FindAllDerivedObjects (ClassID (Object), &objects);
+ for (int i=0;i<objects.size();i++)
+ {
+ Object* o = Object::IDToPointer(objects[i]);
+ if (o && !o->IsDerivedFrom(ClassID(GameManager)))
+ {
+ if (o != NULL && o->TestHideFlag(Object::kDontSave))
+ continue;
+ delete_object_internal (o);
+ }
+ }
+
+ // Finally also cleanup all temporary objects
+ objects.clear();
+ Object::FindAllDerivedObjects (ClassID (Object), &objects);
+ for (int i=0;i<objects.size();i++)
+ {
+ Object* o = Object::IDToPointer(objects[i]);
+ if (o && !o->IsDerivedFrom(ClassID(GameManager)))
+ {
+ if (reloadable)
+ {
+ if (o != NULL && o->TestHideFlag(Object::kDontSave))
+ {
+ bool forceUnload = false;
+ // Make sure AssetBundles are unloaded.
+ if (o->GetClassID() == ClassID(AssetBundle))
+ forceUnload = true;
+#if ENABLE_SCRIPTING
+ if (o->GetClassID() == ClassID(MonoBehaviour))
+ {
+ forceUnload = true;
+ if (strcmp(o->GetName(), "GameSkin") != 0)
+ AssertString(o->GetName());
+ }
+
+ if (o->GetClassID() == ClassID(MonoScript))
+ {
+ forceUnload = true;
+ }
+#endif
+ if (!forceUnload)
+ continue;
+ }
+ }
+ delete_object_internal (o);
+ }
+ }
+
+ for (int i=ManagerContext::kManagerCount-1;i != 0;i--)
+ {
+ if (GetManagerContext().m_Managers[i])
+ {
+ GetPersistentManager().MakeObjectUnpersistent(GetManagerContext().m_Managers[i]->GetInstanceID(), kDontDestroyFromFile);
+ delete_object_internal (GetManagerContext().m_Managers[i]);
+ SetManagerPtrInContext (i, NULL);
+ }
+ }
+
+ objects.clear();
+ Object::FindAllDerivedObjects (ClassID (Object), &objects);
+ for (int i=0;i<objects.size();i++)
+ {
+ Object* o = Object::IDToPointer(objects[i]);
+ if (reloadable)
+ {
+ // Don't cleanup temporary objects (Except if its a gui skin from the builtin resources file)
+ if (o != NULL && o->TestHideFlag(Object::kDontSave))
+ continue;
+ }
+
+ delete_object_internal (o);
+ }
+
+ // Clear cached properties on all materials
+ // This is because reloading the web player might delete some referenced textures (gui style default materials)
+ // but not the material. The cache then gets out of sync and In that case, OpenGL will give an invalid error and text becomes black.
+ // Thus we force reloading the textures from scratch.
+ vector<Material*> materials;
+ Object::FindObjectsOfType (&materials);
+ for (int i=0;i<materials.size();i++)
+ {
+ materials[i]->ClearProperties();
+ }
+
+ #if CAPTURE_SCREENSHOT_AVAILABLE
+ FinishAllCaptureScreenshot ();
+ #endif
+ #if SUPPORT_REPRODUCE_LOG
+ PlayerCleanupReproduction();
+ #endif
+
+#if ENABLE_PROFILER && UNITY_EDITOR
+ ProfilerHistory::Cleanup();
+#endif
+
+ UnlockObjectCreation();
+
+ CleanupBatchDelete ();
+}
+
+void CleanupEngine ()
+{
+ if (IsGfxDevice())
+ GetGfxDevice().FinishRendering();
+ TextMeshGenerator2::Flush();
+ CleanupAllObjects(false);
+ MessageIdentifier::Cleanup ();
+ Object::CleanupAllClasses ();
+ CleanupShaders ();
+ RenderBufferManager::CleanupRenderBufferManager ();
+ #if ENABLE_UNITYGUI
+ CleanupGUIManager ();
+ #endif
+ #if ENABLE_MULTITHREADED_CODE || ENABLE_MULTITHREADED_SKINNING
+ DestroyJobScheduler();
+ #endif
+ // on windows, device cleanup is done from separately (web player: called from separate thread)
+ #if UNITY_OSX
+ DestroyGfxDevice();
+ #endif
+
+ #if !UNITY_FLASH
+ Cursors::CleanupCursors ();
+ #endif
+
+ ReleaseScreenManager();
+
+ #if ENABLE_PLAYERCONNECTION && !UNITY_EDITOR
+ InstallPlayerConnectionLogging(false);
+ #endif
+#if UNITY_EDITOR
+ EditorAssetGarbageCollectManager::StaticDestroy();
+ CleanupTypeTreeCache();
+ CleanupMonoCompilationPipeline();
+#endif
+ ReleaseLogHandlers();
+#if ENABLE_PROFILER
+ CleanupMemoryProfilerStats();
+#endif
+}
+
+void PostprocessSceneGenerateGUITextureAtlas ()
+{
+#if UNITY_EDITOR && ENABLE_RETAINEDGUI
+ std::vector<Canvas*> canvas;
+ Object::FindObjectsOfType(&canvas);
+
+ for( unsigned i = 0 ; i < canvas.size() ; ++ i)
+ BuildTextureAtlasForCanvas(canvas[i]);
+#endif
+}
+
+#if ENABLE_EDITOR_HIERARCHY_ORDERING
+void PostprocessSceneSortTransforms()
+{
+ const Transform::VisibleRootMap& rootMap = GetSceneTracker().GetVisibleRootTransforms();
+
+ for (Transform::VisibleRootMap::const_iterator it = rootMap.begin(); it != rootMap.end(); ++it)
+ {
+ Transform* rootTrans = (*it).second;
+ rootTrans->OrderChildrenRecursively();
+ }
+}
+#endif
+
+void PostprocessScene ()
+{
+#if UNITY_EDITOR
+ // Disconnect all prefab instances.
+ // This ensures that no prefab recording will happen which can be quite expensive.
+ DisconnectAllPrefabInstances ();
+
+ DestroyRenderersFromLODGroupInOpenScene(GetQualitySettings().GetStrippedMaximumLODLevel());
+ ScriptingArguments no_arguments;
+ CallMethodsWithAttribute(MONO_COMMON.postProcessSceneAttribute, no_arguments, NULL);
+ PostprocessSceneGenerateGUITextureAtlas();
+#endif
+
+#if ENABLE_EDITOR_HIERARCHY_ORDERING
+ PostprocessSceneSortTransforms();
+#endif
+}
+
+void CleanupAfterLoad ()
+{
+ #if UNITY_EDITOR
+ GetCleanupManager ().Flush ();
+ RemoveDuplicateGameManagers ();
+ #endif
+
+ GarbageCollectSharedAssets (true);
+
+ TextMeshGenerator2::Flush();
+ GetRenderBufferManager().GarbageCollect(0);
+ GetGfxDevice().InvalidateState();
+
+ #if ENABLE_MONO
+ mono_gc_collect (mono_gc_max_generation ());
+ #endif
+
+ ParticleSystem::BeginUpdateAll ();
+ ParticleSystem::EndUpdateAll ();
+ ParticleEmitter::UpdateAllParticleSystems();
+ RenderManager::UpdateAllRenderers();
+
+ // FIXME: Do this because we can't fully initialize the physicsmanager at startup.
+ CALL_MANAGER_IF_EXISTS(ManagerContext::kPhysicsManager, AwakeFromLoad (kDefaultAwakeFromLoad))
+
+ GetDelayedCallManager().Update(DelayedCallManager::kAfterLoadingCompleted);
+
+
+ ///@TODO: I am not sure why we do this...
+ GetQualitySettings().ApplySettings();
+}
+
+void PatchRendererLightmapIndices (AwakeFromLoadQueue& awakeQueue)
+{
+ // offset the lightmap index in the newly loaded Renderers and Terrains by the current number of lightmaps
+ int lightmapIndexOffset = GetLightmapSettings().GetLightmaps().size();
+ if (lightmapIndexOffset == 0)
+ return;
+
+ AwakeFromLoadQueue::ItemArray& rendererItems = awakeQueue.GetItemArray(kGameObjectAndComponentQueue);
+ for (int i = 0; i < rendererItems.size(); i++)
+ {
+ Object* object = Object::IDToPointer(rendererItems[i].objectPPtr.GetInstanceID());
+
+ // renderers
+ Renderer* r = dynamic_pptr_cast<Renderer*>(object);
+ if (r != NULL)
+ {
+ if (r->IsLightmappedForRendering())
+ r->SetLightmapIndexInt(r->GetLightmapIndexInt() + lightmapIndexOffset);
+ }
+ }
+
+ #if ENABLE_TERRAIN
+ PPtr<MonoScript> terrainScript = GetBuiltinResource<MonoScript>("Terrain");
+ AwakeFromLoadQueue::ItemArray& monoBehaviourItems = awakeQueue.GetItemArray(kMonoBehaviourQueue);
+ for (int i = 0; i < monoBehaviourItems.size(); i++)
+ {
+ Object* object = Object::IDToPointer(monoBehaviourItems[i].objectPPtr.GetInstanceID());
+
+ // terrains
+ MonoBehaviour* behaviour = dynamic_pptr_cast<MonoBehaviour*>(object);
+ if (behaviour && behaviour->GetScript() == terrainScript)
+ {
+ MessageData data;
+ data.SetData (lightmapIndexOffset, ClassID (int));
+ SendMessageDirect(*behaviour, kShiftLightmapIndex, data);
+ }
+ }
+ #endif
+
+}
+
+void MergeLightmapData(AwakeFromLoadQueue& awakeQueue)
+{
+ // We made sure the LightmapSettings object will get loaded along with the other stuff
+ // in PreloadLevelOperation::Perform(). Now let's update the lightmap indices of the
+ // loaded Renderers, append loaded lightmaps to the original lightmaps and destroy
+ // the loaded LightmapSettings object.
+ LightmapSettings* loadedLightmapSettings = NULL;
+
+ AwakeFromLoadQueue::ItemArray& managerItems = awakeQueue.GetItemArray(kManagersQueue);
+ for (int i = 0; i < managerItems.size(); i++)
+ {
+ loadedLightmapSettings = dynamic_instanceID_cast<LightmapSettings*> (managerItems[i].objectPPtr.GetInstanceID());
+ if (loadedLightmapSettings)
+ break;
+ }
+ if (loadedLightmapSettings == NULL)
+ return;
+
+ const std::vector<LightmapData>& loadedLightmapData = loadedLightmapSettings->GetLightmaps();
+
+ // loaded level contains lightmaps, so merge them.
+ if (loadedLightmapData.size() != 0)
+ {
+ int lightmapsMode = GetLightmapSettings ().GetLightmapsMode ();
+ int loadedLightmapsMode = loadedLightmapSettings->GetLightmapsMode ();
+
+ if (loadedLightmapsMode != lightmapsMode)
+ {
+ WarningString (Format("The loaded level has a different lightmaps mode than the current one. Current: %s. Loaded: %s. Will use: %s.",
+ LightmapSettings::kLightmapsModeNames[lightmapsMode],
+ LightmapSettings::kLightmapsModeNames[loadedLightmapsMode],
+ LightmapSettings::kLightmapsModeNames[lightmapsMode]));
+ }
+
+ // first offset lightmap indices of the loaded renderers by the current number of lightmaps
+ PatchRendererLightmapIndices(awakeQueue);
+ // then add the new lightmaps at the end of the current lightmaps array
+ GetLightmapSettings().AppendLightmaps(loadedLightmapData);
+ }
+
+ DestroyObjectHighLevel(loadedLightmapSettings);
+}
+
+void PostLoadLevelAdditive (const std::string& pathName, AwakeFromLoadQueue& awakeQueue)
+{
+ PostEditorLoadLevelAdditive(pathName, awakeQueue);
+
+ PostprocessScene();
+}
+
+void PostEditorLoadLevelAdditive (const std::string& pathName, AwakeFromLoadQueue& awakeQueue)
+{
+ awakeQueue.RegisterObjectInstanceIDs();
+
+ CompleteAwakeSequence(pathName, awakeQueue);
+
+ MergeLightmapData(awakeQueue);
+}
+
+void VerifyNothingIsPersistentInLoadedScene (const std::string& pathName)
+{
+#if DEBUGMODE
+ set<SInt32> persistentObjectsAtPath;
+ GetPersistentManager().GetPersistentInstanceIDsAtPath(pathName, &persistentObjectsAtPath);
+ for (set<SInt32>::iterator i=persistentObjectsAtPath.begin();i!=persistentObjectsAtPath.end();i++)
+ {
+ Object* target = Object::IDToPointer(*i);
+ // Error on everything but global game managers. this handles the case where mainData contains both scene data and global game managers.
+ if (target == NULL || !Object::IsDerivedFromClassID(target->GetClassID(), ClassID(GlobalGameManager)) || UNITY_EDITOR)
+ {
+ string className = target ? target->GetClassName() : "Not loaded";
+ ErrorString("Failed to unpersist: " + className + " ID: " + IntToString(*i) + " FileID: " + IntToString(GetPersistentManager().GetLocalFileID(*i)));
+ }
+ }
+ AssertIf(GetPersistentManager().IsStreamLoaded(pathName) && !GetPersistentManager().HasMemoryOrCachedSerializedFile(pathName));
+#endif
+}
+
+void ValidateNoSceneObjectsAreLoaded (bool includeAllEditorExtensions)
+{
+#if (UNITY_EDITOR && DEBUGMODE) || !UNITY_RELEASE
+ // This happens when objects are accidentally loaded from disk again because someone still had a pointer to them.
+ InstanceIDArray objects;
+#if UNITY_EDITOR
+ if (includeAllEditorExtensions)
+ CollectAllSceneObjects (objects);
+ else
+#endif
+ CollectSceneGameObjects (objects);
+ if (!objects.empty ())
+ {
+ ErrorString ("Some objects were not cleaned up when closing the scene. (Did you spawn new GameObjects from OnDestroy?)");
+ }
+
+ vector<Object*> levelManagers;
+ Object::FindObjectsOfType (ClassID (LevelGameManager), &levelManagers);
+ for (int i=0;i<levelManagers.size();i++)
+ {
+ ErrorString (Format("Manager %s is still loading after clearing scene", levelManagers[i]->GetClassName().c_str()));
+ }
+#endif
+}
+
+void CompletePreloadMainData (AwakeFromLoadQueue& awakeQueue)
+{
+ ResetInput();
+
+ // Cleanup exisiting level maangers
+ DestroyLevelManagers ();
+
+ awakeQueue.RegisterObjectInstanceIDs();
+
+ // Load LevelManagers
+ LoadManagers(awakeQueue);
+
+ // Load everything else
+ CompleteAwakeSequence(kMainData, awakeQueue);
+
+ // FIXME: Do this because we can't fully initialize the physicsmanager at startup.
+ CALL_MANAGER_IF_EXISTS(ManagerContext::kPhysicsManager, AwakeFromLoad (kDefaultAwakeFromLoad))
+
+ GetDelayedCallManager().Update(DelayedCallManager::kAfterLoadingCompleted);
+
+ GetQualitySettings().ApplySettings();
+}
+
+void CompletePreloadManagerLoadLevel (const std::string& path, AwakeFromLoadQueue& awakeQueue)
+{
+ ResetInput();
+
+ awakeQueue.RegisterObjectInstanceIDs();
+
+ LoadManagers(awakeQueue);
+
+ CompleteAwakeSequence(path, awakeQueue);
+
+ PostprocessScene();
+
+ CleanupAfterLoad ();
+}
+
+static void CompleteAwakeSequence (const std::string& path, AwakeFromLoadQueue& awakeQueue)
+{
+#if UNITY_EDITOR
+ // Merge all prefab instances (Requires that prefab backwards compatilibyt has been applied - AwakeFromLoad has not yet been called)
+ MergeAllPrefabInstances(&awakeQueue);
+#endif
+
+ // Unload stream
+ // - Don't unload if the stream came from an AssetBundle
+ if ( !GetPersistentManager().HasMemoryOrCachedSerializedFile( path ) )
+ GetPersistentManager().UnloadStream(path);
+
+ // Invoke AwakeFromLoad and friends.
+ GetPersistentManager().IntegrateAllThreadedObjectsStep2(awakeQueue);
+
+
+ //@TODO: write a check that all objects in the AwakeQueue are !IsPersistent()
+
+#if UNITY_EDITOR
+ GetSceneTracker().ReloadTransformHierarchyRoots();
+#endif
+
+}
+
+#if UNITY_EDITOR
+
+void LoadLevelAdditiveEditor (const std::string& level)
+{
+ PreloadLevelOperation* op = PreloadLevelOperation::LoadLevel (level, "", -1, PreloadLevelOperation::kLoadEditorAdditiveLevel, true);
+
+ GetPreloadManager().WaitForAllAsyncOperationsToComplete();
+
+ op->Release();
+}
+
+void CompletePreloadManagerLoadLevelEditor (const std::string& path, AwakeFromLoadQueue& awakeQueue, int preloadOperationMode)
+{
+ Assert (!path.empty ());
+
+ awakeQueue.RegisterObjectInstanceIDs();
+
+ LoadManagers(awakeQueue);
+
+ CompleteAwakeSequence(path, awakeQueue);
+
+ if (preloadOperationMode == PreloadLevelOperation::kOpenSceneEditorPlaymode)
+ PostprocessScene ();
+
+ CleanupAfterLoad ();
+
+ ResetManagersAfterLoadEditor ();
+
+ if (preloadOperationMode == PreloadLevelOperation::kOpenSceneEditorPlaymode)
+ PlayerInitState();
+}
+
+bool LoadSceneEditor (const string& pathName, std::map<LocalIdentifierInFileType, SInt32>* hintFileIDToHeapID, int options)
+{
+ AssertIf (pathName.empty ());
+
+ bool enterPlaymode = options & kEditorPlayMode;
+ PreloadLevelOperation::LoadingMode preloadOperationMode = enterPlaymode ? PreloadLevelOperation::kOpenSceneEditorPlaymode : PreloadLevelOperation::kOpenSceneEditor;
+
+ // Make sure nothing is in the queue (We are calling DestroyWorld, then doing AsyncLoad)
+ GetPreloadManager().WaitForAllAsyncOperationsToComplete();
+
+ // Destroy world must be called before we start loading.
+ // Otherwise objects will receive different instanceIDs in Playmode then they have in edit mode (hintFileIDToHeapID)
+ DestroyWorld(true);
+ EditorBeforeLoadingCleanup ();
+ SetIsWorldPlaying (enterPlaymode);
+
+ // Any left-over playmode load operations need to be cleared here.
+ GetPreloadManager().RemoveStopPlaymodeOperations ();
+
+ // Now load
+ if (hintFileIDToHeapID)
+ GetPersistentManager().SuggestFileIDToHeapIDs (pathName, *hintFileIDToHeapID);
+
+ PreloadLevelOperation* op = PreloadLevelOperation::LoadLevel (pathName, "", -1, preloadOperationMode, true);
+
+ GetPreloadManager().WaitForAllAsyncOperationsToComplete();
+
+ op->Release();
+
+ return true;
+}
+#endif
+
+
+static void DestroyAllAtPath (const std::string& path)
+{
+#if DEBUGMODE
+ GetPersistentManager().SetDebugAssertLoadingFromFile(path);
+#endif
+ PersistentManager::ObjectIDs ids;
+ GetPersistentManager().GetLoadedInstanceIDsAtPath(path, &ids);
+
+ LockObjectCreation();
+ for (PersistentManager::ObjectIDs::iterator i=ids.begin();i!=ids.end();i++)
+ {
+ Object* o = Object::IDToPointer (*i);
+ AssertIf(o != NULL && !o->IsPersistent());
+ delete_object_internal (o);
+ }
+ UnlockObjectCreation();
+
+#if DEBUGMODE
+ ids.clear();
+ GetPersistentManager().GetLoadedInstanceIDsAtPath(path, &ids);
+ if (!ids.empty())
+ {
+ ErrorString("UnloadAssetBundle failed");
+ }
+ GetPersistentManager().SetDebugAssertLoadingFromFile("");
+#endif
+
+ GetPersistentManager().RemoveObjectsFromPath(path);
+}
+
+static bool DoesGfxDeviceRequireAssetsToBeReloadableFromDisk ()
+{
+// For Windows Phone 8 reloading may be required at any point in time (when user pauses - resumes app, for instance)
+#if UNITY_WP8
+ return true;
+#else
+ return false;
+#endif
+}
+
+void UnloadAssetBundle (AssetBundle& file, bool unloadAllLoadedObjects)
+{
+ if (DoesGfxDeviceRequireAssetsToBeReloadableFromDisk () && !unloadAllLoadedObjects)
+ {
+ ErrorString("AssetBundle.Unload(false) shouldn't be called because used assets might have to be reloaded at any point in time. If no assets are used, call AssetBundle.Unload(true).");
+ return;
+ }
+
+ GetPreloadManager().LockPreloading();
+
+ PPtr<AssetBundle> resourceFilePPtr = &file;
+
+ // Destroy all objects loaded from the AssetBundle path (or unassociate
+ // them from the path if unloadAllLoadedObjects==false) and unload
+ // all streams associated with the bundle.
+ //
+ // Note that DestroyAllAtPath() will actually delete the AssetBundle
+ // instance itself so be careful not to reference any data from it
+ // after the call.
+
+ #if ENABLE_WWW
+ UnityWebStream* stream = file.m_UnityWebStream;
+ if (stream && stream->GetFileStream() &&
+ (stream->GetFileStream()->GetType() == kCompressedFileStreamMemoryType ||
+ stream->GetFileStream()->GetType() == kUncompressedFileStreamMemoryType))
+ {
+ FileStream* compressedFile = stream->GetFileStream();
+ FileStream::Decompressed files = compressedFile->m_Files;
+
+ for (FileStream::iterator f=files.begin();f != files.end();f++)
+ {
+ if (unloadAllLoadedObjects)
+ DestroyAllAtPath(f->name);
+ else
+ GetPersistentManager().RemoveObjectsFromPath(f->name);
+ }
+
+ DestroyWithoutLoadingButDontDestroyFromFile(resourceFilePPtr.GetInstanceID());
+
+ for (FileStream::iterator i=files.begin();i != files.end();i++)
+ {
+ GetPersistentManager().UnloadStream(i->name);
+ }
+ }
+ else
+ #endif // ENABLE_WWW
+ #if ENABLE_CACHING
+ if (file.m_CachedUnityWebStream)
+ {
+ // Don't const reference files, as the changed is being changed in the loop
+ vector<string> files = file.m_CachedUnityWebStream->m_Files;
+
+ for (int i=0;i<files.size();i++)
+ {
+ const string& path = files[i];
+ if (unloadAllLoadedObjects)
+ DestroyAllAtPath(path);
+ else
+ GetPersistentManager().RemoveObjectsFromPath(path);
+ }
+
+ DestroyWithoutLoadingButDontDestroyFromFile(resourceFilePPtr.GetInstanceID());
+
+ for (int i=0;i<files.size();i++)
+ {
+ GetPersistentManager().UnloadStream(files[i]);
+ }
+ }
+ else
+ #endif // ENABLE_CACHING
+ if (file.m_UncompressedFileInfo)
+ {
+ AssetBundle::UncompressedFileInfoContainer* uncompFileInfo = file.m_UncompressedFileInfo;
+
+ for (AssetBundle::UncompressedFileInfoContainer::iterator f = uncompFileInfo->begin();
+ f != uncompFileInfo->end();
+ f++)
+ {
+ const string& path = f->fileName;
+ if (unloadAllLoadedObjects)
+ DestroyAllAtPath(path);
+ else
+ GetPersistentManager().RemoveObjectsFromPath(path);
+ }
+
+ DestroyWithoutLoadingButDontDestroyFromFile(resourceFilePPtr.GetInstanceID());
+
+ for (AssetBundle::UncompressedFileInfoContainer::iterator f = uncompFileInfo->begin();
+ f != uncompFileInfo->end();
+ f++)
+ {
+ GetPersistentManager().UnloadStream(f->fileName);
+ }
+ UNITY_DELETE(uncompFileInfo, kMemFile);
+ }
+ else
+ {
+ ErrorString("Resource file has already been unloaded.");
+ }
+
+ GetPreloadManager().UnlockPreloading();
+}
+
+
+#if UNITY_EDITOR
+
+
+static void EditorBeforeLoadingCleanup ()
+{
+ GetDelayedCallManager ().ClearAll ();
+}
+
+static void ResetManagersAfterLoadEditor ()
+{
+ ResetInput();
+ GetTimeManager ().ResetTime ();
+ GetQualitySettings().ApplySettings();
+}
+
+void CreateWorldEditor ()
+{
+ // Create any managers that are not created yet and setup manager context
+ AwakeFromLoadQueue emptyQueue (kMemTempAlloc);
+ LoadManagers(emptyQueue);
+
+ EditorBeforeLoadingCleanup ();
+
+ GetSceneTracker().ReloadTransformHierarchyRoots();
+
+ ResetManagersAfterLoadEditor ();
+}
+#endif
+
+
+PROFILER_INFORMATION(gCollectSceneGameObjects, "CollectGameObjects", kProfilerLoading)
+
+void CollectSceneGameObjects (InstanceIDArray& outputObjects)
+{
+ PROFILER_AUTO(gCollectSceneGameObjects,NULL);
+ vector<GameObject*> gameObjects;
+ Object::FindObjectsOfType (&gameObjects);
+ for (vector<GameObject*>::iterator i= gameObjects.begin ();i != gameObjects.end ();++i)
+ {
+ GameObject& object = **i;
+
+ // Verify that game objects dont accidentally end up being active and persistent
+ #if DEBUGMODE
+ if (object.IsActive() && object.IsPersistent())
+ {
+ ErrorStringObject("Persistent object inconsistency", &object);
+ }
+ #endif
+
+ if (object.IsPersistent ())
+ continue;
+ if (object.TestHideFlag (Object::kDontSave))
+ continue;
+
+ #if UNITY_EDITOR
+ if (object.IsPrefabParent ())
+ {
+ ErrorStringObject ("A prefab somehow lost its way out of its asset file. Ignoring it. Save your scene and next time you launch the editor it will be gone.", &object);
+ continue;
+ }
+ #endif
+
+ outputObjects.push_back(object.GetInstanceID ());
+ }
+}
+#if UNITY_EDITOR
+static void CollectAllSceneObjects (InstanceIDArray& instanceIDs)
+{
+ vector<SInt32> objects;
+ set<Prefab*> prefabInstances;
+ Object::FindAllDerivedObjects (ClassID (EditorExtension), &objects);
+ for (vector<SInt32>::iterator i= objects.begin ();i != objects.end ();++i)
+ {
+ EditorExtension& object = *PPtr<EditorExtension> (*i);
+
+#if DEBUGMODE
+ GameObject* go = dynamic_pptr_cast<GameObject*> (&object);
+ if (go)
+ {
+ if (go->IsActive() && go->IsPersistent())
+ {
+ ErrorStringObject("Persistent object inconsistency", go);
+ }
+ }
+#endif
+
+ if (object.IsPersistent ())
+ continue;
+
+ if (object.IsPrefabParent())
+ {
+ ErrorStringObject ("A prefab somehow lost its way out of its asset file. Ignoring it. Save your scene and next time you launch the editor it will be gone.", &object);
+ continue;
+ }
+
+ if (object.TestHideFlag (Object::kDontSave))
+ continue;
+ if (object.IsDerivedFrom (ClassID (GameManager)))
+ continue;
+ if (object.GetClassID () >= ClassID (SmallestEditorClassID))
+ continue;
+
+ instanceIDs.push_back(object.GetInstanceID ());
+ }
+}
+
+#endif
+
diff --git a/Runtime/Misc/SaveAndLoadHelper.h b/Runtime/Misc/SaveAndLoadHelper.h
new file mode 100644
index 0000000..f9bd799
--- /dev/null
+++ b/Runtime/Misc/SaveAndLoadHelper.h
@@ -0,0 +1,58 @@
+#ifndef SAVEANDLOADHELPER_H
+#define SAVEANDLOADHELPER_H
+
+#include <map>
+#include <string>
+#include <set>
+#include <vector>
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Utilities/GUID.h"
+#include "Runtime/Misc/GarbageCollectSharedAssets.h"
+
+class AssetBundle;
+class AwakeFromLoadQueue;
+
+typedef dynamic_array<int> InstanceIDArray;
+
+void DestroyWorld (bool destroySceneAssets);
+
+bool InitializeEngineNoGraphics ();
+bool InitializeEngineGraphics (bool batch = false);
+void CleanupEngine ();
+void CleanupAllObjects (bool reloadable);
+
+void CreateWorldEditor ();
+void CleanupAfterLoad ();
+
+/// Loads a new world from a pathname, playmode or editmode
+/// Returns true if any datatemplates were merged while loading.
+bool LoadSceneEditor (const std::string& pathName, std::map<LocalIdentifierInFileType, SInt32>* hintFileIDToHeapID, int mode);
+
+void PostLoadLevelAdditive (const std::string& pathName, AwakeFromLoadQueue& awakeQueue);
+void PostEditorLoadLevelAdditive (const std::string& pathName, AwakeFromLoadQueue& awakeQueue);
+void CompletePreloadManagerLoadLevel (const std::string& path, AwakeFromLoadQueue& awakeQueue);
+void CompletePreloadMainData (AwakeFromLoadQueue& awakeQueue);
+void CompletePreloadManagerLoadLevelEditor (const std::string& path, AwakeFromLoadQueue& awakeQueue, int preloadOperationMode);
+
+void LoadLevelAdditiveEditor (const std::string& level);
+
+void CollectSceneGameObjects (InstanceIDArray& instanceIDs);
+
+void CheckAllGOConsistency ();
+
+void PostprocessScene ();
+
+void UnloadAssetBundle (AssetBundle& file, bool unloadAllLoadedObjects);
+
+void VerifyNothingIsPersistentInLoadedScene (const std::string& pathName);
+
+/// Make there are any game objects and/or level managers still present, print an error message.
+///
+/// @param includeAllEditorExtensions If true, not only check for GameObjects but also for
+/// any object derived from EditorExtension.
+void ValidateNoSceneObjectsAreLoaded (bool includeAllEditorExtensions = false);
+
+extern const char* kMainData;
+
+
+#endif
diff --git a/Runtime/Misc/SceneUnloading.cpp b/Runtime/Misc/SceneUnloading.cpp
new file mode 100644
index 0000000..00c30c2
--- /dev/null
+++ b/Runtime/Misc/SceneUnloading.cpp
@@ -0,0 +1,69 @@
+#include "UnityPrefix.h"
+#include "SceneUnloading.h"
+#include "SaveAndLoadHelper.h"
+#include "GameObjectUtility.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/BaseClasses/ManagerContextLoading.h"
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/TimeHelper.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+
+
+PROFILER_INFORMATION (gUnloadScene, "UnloadScene", kProfilerLoading);
+
+void SharkBeginRemoteProfiling ();
+void SharkEndRemoteProfiling ();
+
+void UnloadGameScene ()
+{
+ ABSOLUTE_TIME begin = START_TIME;
+
+// SharkBeginRemoteProfiling ();
+ PROFILER_AUTO(gUnloadScene, NULL)
+
+ InstanceIDArray objects;
+
+ CollectSceneGameObjects (objects);
+
+ Object* o;
+ // GameObjects first
+ for (InstanceIDArray::iterator i=objects.begin ();i != objects.end ();++i)
+ {
+ o = Object::IDToPointer (*i);
+ AssertIf (o && o->IsPersistent ());
+ GameObject* go = dynamic_pptr_cast<GameObject*> (o);
+ // Only Destroy root level GameObjects. The children will be destroyed
+ // as part of them. That way, we ensure that the hierarchy is walked correctly,
+ // and all objects in the hieararchy will be marked as deactivated when destruction happens.
+ if (go != NULL && go->GetComponent(Transform).GetParent() == NULL)
+ DestroyObjectHighLevel (o);
+ }
+
+ // normal objects whatever they might be after that
+ for (InstanceIDArray::iterator i=objects.begin ();i != objects.end ();++i)
+ {
+ o = Object::IDToPointer (*i);
+ AssertIf (o && o->IsPersistent ());
+ DestroyObjectHighLevel (o);
+ }
+
+ objects.clear ();
+ CollectLevelGameManagers (objects);
+
+ // Gamemanagers & Scene last
+ for (InstanceIDArray::iterator i=objects.begin ();i != objects.end ();++i)
+ {
+ o = Object::IDToPointer (*i);
+ AssertIf (o && o->IsPersistent ());
+ DestroyObjectHighLevel (o);
+ }
+
+ GlobalCallbacks::Get().didUnloadScene.Invoke();
+
+ ValidateNoSceneObjectsAreLoaded ();
+
+ printf_console("UnloadTime: %f ms\n", AbsoluteTimeToMilliseconds(ELAPSED_TIME(begin)));
+
+// SharkEndRemoteProfiling ();
+}
diff --git a/Runtime/Misc/SceneUnloading.h b/Runtime/Misc/SceneUnloading.h
new file mode 100644
index 0000000..3657f73
--- /dev/null
+++ b/Runtime/Misc/SceneUnloading.h
@@ -0,0 +1 @@
+void UnloadGameScene (); \ No newline at end of file
diff --git a/Runtime/Misc/SystemInfo.h b/Runtime/Misc/SystemInfo.h
new file mode 100644
index 0000000..7e25cf6
--- /dev/null
+++ b/Runtime/Misc/SystemInfo.h
@@ -0,0 +1,320 @@
+#ifndef SYSTEM_INFO_H
+#define SYSTEM_INFO_H
+
+#include "Runtime/Modules/ExportModules.h"
+#include <string>
+
+enum RuntimePlatform
+{
+ // NEVER change constants of existing platforms!
+ OSXEditor = 0,
+ OSXPlayer = 1,
+ WindowsPlayer = 2,
+ OSXWebPlayer = 3,
+ OSXDashboardPlayer = 4,
+ WindowsWebPlayer = 5,
+ WiiPlayer = 6,
+ WindowsEditor = 7,
+ iPhonePlayer = 8,
+ PS3Player = 9,
+ XenonPlayer = 10,
+ AndroidPlayer = 11,
+ NaClWebPlayer = 12,
+ LinuxPlayer = 13,
+ LinuxWebPlayer = 14,
+ FlashPlayer = 15,
+ LinuxEditor = 16,
+ WebGLPlayer = 17,
+ MetroPlayerX86 = 18,
+ MetroPlayerX64 = 19,
+ MetroPlayerARM = 20,
+ WP8Player = 21,
+ BB10Player = 22,
+ TizenPlayer = 23,
+
+ RuntimePlatformCount // keep this last
+};
+
+enum DeviceType
+{
+ kDeviceTypeUnknown = 0,
+ kDeviceTypeHandheld,
+ kDeviceTypeConsole,
+ kDeviceTypeDesktop,
+ kDeviceTypeCount // keep this last
+};
+
+enum SystemLanguage {
+ SystemLanguageAfrikaans,
+ SystemLanguageArabic,
+ SystemLanguageBasque,
+ SystemLanguageBelarusian,
+ SystemLanguageBulgarian,
+ SystemLanguageCatalan,
+ SystemLanguageChinese,
+ SystemLanguageCzech,
+ SystemLanguageDanish,
+ SystemLanguageDutch,
+ SystemLanguageEnglish,
+ SystemLanguageEstonian,
+ SystemLanguageFaroese,
+ SystemLanguageFinnish,
+ SystemLanguageFrench,
+ SystemLanguageGerman,
+ SystemLanguageGreek,
+ SystemLanguageHebrew,
+ SystemLanguageHugarian,
+ SystemLanguageIcelandic,
+ SystemLanguageIndonesian,
+ SystemLanguageItalian,
+ SystemLanguageJapanese,
+ SystemLanguageKorean,
+ SystemLanguageLatvian,
+ SystemLanguageLithuanian,
+ SystemLanguageNorwegian,
+ SystemLanguagePolish,
+ SystemLanguagePortuguese,
+ SystemLanguageRomanian,
+ SystemLanguageRussian,
+ SystemLanguageSerboCroatian,
+ SystemLanguageSlovak,
+ SystemLanguageSlovenian,
+ SystemLanguageSpanish,
+ SystemLanguageSwedish,
+ SystemLanguageThai,
+ SystemLanguageTurkish,
+ SystemLanguageUkrainian,
+ SystemLanguageVietnamese,
+ SystemLanguageUnknown
+};
+
+namespace systeminfo {
+
+ std::string GetOperatingSystem();
+ std::string GetProcessorType();
+ int EXPORT_COREMODULE GetProcessorCount();
+ int GetNumberOfCores();
+ int GetPhysicalMemoryMB();
+ int GetUsedVirtualMemoryMB();
+ int GetExecutableSizeMB();
+ int GetSystemLanguage();
+ std::string GetSystemLanguageISO();
+
+#if UNITY_WIN
+ ULONG_PTR GetCoreAffinityMask(DWORD core);
+ std::string GetBIOSIdentifier();
+#endif
+
+#if UNITY_XENON
+ void SetExecutableSizeMB(UInt32 sizeMB);
+#endif
+
+#if UNITY_WIN || UNITY_OSX || UNITY_LINUX
+// Windows: 500=2000, 510=XP, 520=2003, 600=Vista
+// Mac: 1006=Snow Leopard
+// Linux: kernel version 206 = 2.6
+int GetOperatingSystemNumeric();
+
+int GetProcessorSpeed();
+std::string GetMacAddress();
+#endif
+
+#if UNITY_EDITOR && (UNITY_WIN || UNITY_OSX)
+unsigned char* GetMacAddressForBeast ();
+#endif
+
+#if WEBPLUG && UNITY_OSX
+bool IsRunningInDashboardWidget();
+#endif
+
+#if UNITY_WP8
+int GetCommitedMemoryLimitMB();
+int GetCommitedMemoryMB();
+#endif
+
+inline RuntimePlatform GetRuntimePlatform()
+{
+
+ #if UNITY_EDITOR
+ #if UNITY_OSX
+ return OSXEditor;
+ #elif UNITY_WIN
+ return WindowsEditor;
+ #elif UNITY_LINUX
+ return LinuxEditor;
+ #else
+ #error Unknown platform
+ #endif
+ #else
+ #if UNITY_OSX
+ #if WEBPLUG
+ #if !UNITY_PEPPER
+ if (systeminfo::IsRunningInDashboardWidget ())
+ return OSXDashboardPlayer;
+ else
+ #endif
+ return OSXWebPlayer;
+ #else
+ return OSXPlayer;
+ #endif
+ #elif UNITY_WIN && !UNITY_WINRT
+ #if WEBPLUG
+ return WindowsWebPlayer;
+ #else
+ return WindowsPlayer;
+ #endif
+ #elif UNITY_WP8
+ return WP8Player;
+ #elif UNITY_METRO
+ #if __arm__
+ return MetroPlayerARM;
+ #else
+ return MetroPlayerX86;
+ #endif
+ #elif UNITY_WII
+ return WiiPlayer;
+ #elif UNITY_XENON
+ return XenonPlayer;
+ #elif UNITY_PS3
+ return PS3Player;
+ #elif UNITY_IPHONE
+ return iPhonePlayer;
+ #elif UNITY_ANDROID
+ return AndroidPlayer;
+ #elif UNITY_BB10
+ return BB10Player;
+ #elif UNITY_TIZEN
+ return TizenPlayer;
+ #elif UNITY_PEPPER
+ #if UNITY_NACL_WEBPLAYER
+ // Since we want to be fully compatible with existing content
+ // we need to pretend we are a normal web player.
+ return WindowsWebPlayer;
+ #else
+ return NaClWebPlayer;
+ #endif
+ #elif UNITY_LINUX
+ #if WEBPLUG
+ return LinuxWebPlayer;
+ #else
+ return LinuxPlayer;
+ #endif
+ #elif UNITY_FLASH
+ return FlashPlayer;
+ #elif UNITY_WEBGL
+ return WebGLPlayer;
+ #else
+ #error Unknown platform
+ #endif
+ #endif
+}
+
+inline bool IsPlatformStandalone( RuntimePlatform p ) {
+ return p==WindowsPlayer || p==OSXPlayer || p == LinuxPlayer;
+}
+inline bool IsPlatformWebPlayer( RuntimePlatform p ) {
+ return p==WindowsWebPlayer || p==OSXWebPlayer || p==OSXDashboardPlayer;
+}
+
+inline std::string GetRuntimePlatformString(RuntimePlatform p)
+{
+ switch (p)
+ {
+ case OSXEditor: return "OSXEditor";
+ case OSXPlayer: return "OSXPlayer";
+ case WindowsPlayer: return "WindowsPlayer";
+ case OSXWebPlayer: return "OSXWebPlayer";
+ case OSXDashboardPlayer: return "OSXDashboardPlayer";
+ case WindowsWebPlayer: return "WindowsWebPlayer";
+ case WiiPlayer: return "WiiPlayer";
+ case WindowsEditor: return "WindowsEditor";
+ case iPhonePlayer: return "iPhonePlayer";
+ case PS3Player: return "PS3Player";
+ case XenonPlayer: return "XenonPlayer";
+ case AndroidPlayer: return "AndroidPlayer";
+ case NaClWebPlayer: return "NaClWebPlayer";
+ case LinuxPlayer: return "LinuxPlayer";
+ case LinuxWebPlayer: return "LinuxWebPlayer";
+ case FlashPlayer: return "FlashPlayer";
+ case LinuxEditor: return "LinuxEditor";
+ case WebGLPlayer: return "WebGL";
+ case MetroPlayerX86: return "MetroPlayerX86";
+ case MetroPlayerX64: return "MetroPlayerX64";
+ case MetroPlayerARM: return "MetroPlayerARM";
+ case WP8Player: return "WP8Player";
+ case BB10Player: return "BB10Player";
+ case TizenPlayer: return "TizenPlayer";
+ default:
+ #if !UNITY_EXTERNAL_TOOL
+ AssertString("Unknown platform.");
+ #endif
+ return "Unknown";
+ }
+}
+
+inline std::string GetRuntimePlatformString()
+{
+ return GetRuntimePlatformString(GetRuntimePlatform());
+}
+
+ std::string GetPersistentDataPath(); /// A path for data that can be considered "long-lived", possibly to time of un-installation.
+ std::string GetTemporaryCachePath(); /// A path for "short-term" data, that may out-live the running session.
+#if !UNITY_FLASH
+ char const* GetDeviceUniqueIdentifier ();
+ char const* GetDeviceName ();
+ char const* GetDeviceModel ();
+ char const* GetDeviceSystemName ();
+ char const* GetDeviceSystemVersion ();
+#endif
+
+ inline bool IsHandheldPlatform ()
+ {
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN
+ return true;
+#else
+ return false;
+#endif
+ }
+
+#if UNITY_WINRT
+ bool SupportsAccelerometer ();
+#else
+ inline bool SupportsAccelerometer ()
+ {
+ return IsHandheldPlatform ();
+ }
+#endif
+
+ inline bool SupportsLocationService ()
+ {
+ return IsHandheldPlatform ();
+ }
+
+
+#if UNITY_ANDROID || UNITY_IPHONE
+ bool SupportsVibration ();
+#else
+ inline bool SupportsVibration ()
+ {
+ return IsHandheldPlatform ();
+ }
+#endif
+
+
+ inline DeviceType DeviceType ()
+ {
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN
+ return kDeviceTypeHandheld;
+#elif UNITY_EDITOR || UNITY_PEPPER || UNITY_FLASH \
+ || UNITY_OSX || (UNITY_WIN && !UNITY_WP8) || UNITY_LINUX || UNITY_WEBGL
+ return kDeviceTypeDesktop;
+#elif UNITY_WII || UNITY_XENON || UNITY_PS3
+ return kDeviceTypeConsole;
+#else
+ #error Should never get here. Add your platform.
+ return kDeviceTypeUknown;
+#endif
+ }
+} // namespace
+
+#endif
diff --git a/Runtime/Misc/UTF8.cpp b/Runtime/Misc/UTF8.cpp
new file mode 100644
index 0000000..defdfea
--- /dev/null
+++ b/Runtime/Misc/UTF8.cpp
@@ -0,0 +1,376 @@
+#include "UnityPrefix.h"
+
+typedef unsigned long UTF32; /* at least 32 bits */
+typedef unsigned short UTF16; /* at least 16 bits */
+typedef unsigned char UTF8; /* typically 8 bits */
+typedef unsigned char Boolean; /* 0 or 1 */
+
+
+
+typedef enum {
+ strictConversion = 0,
+ lenientConversion
+} ConversionFlags;
+
+typedef enum {
+ conversionOK, /* conversion successful */
+ sourceExhausted, /* partial character in source, but hit end */
+ targetExhausted, /* insuff. room in target for conversion */
+ sourceIllegal /* source sequence is illegal/malformed */
+} ConversionResult;
+
+/* Some fundamental constants */
+#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
+#define UNI_MAX_BMP (UTF32)0x0000FFFF
+#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
+#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
+#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
+
+ConversionResult ConvertUTF8toUTF16 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
+
+
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/* ---------------------------------------------------------------------
+
+ Conversions between UTF32, UTF-16, and UTF-8. Source code file.
+ Author: Mark E. Davis, 1994.
+ Rev History: Rick McGowan, fixes & updates May 2001.
+ Sept 2001: fixed const & error conditions per
+ mods suggested by S. Parent & A. Lillich.
+ June 2002: Tim Dodd added detection and handling of incomplete
+ source sequences, enhanced error detection, added casts
+ to eliminate compiler warnings.
+ July 2003: slight mods to back out aggressive FFFE detection.
+ Jan 2004: updated switches in from-UTF8 conversions.
+ Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
+
+ See the header file "ConvertUTF.h" for complete documentation.
+
+------------------------------------------------------------------------ */
+
+
+#include "UTF8.h"
+#ifdef CVTUTF_DEBUG
+#include <stdio.h>
+#endif
+
+static const int halfShift = 10; /* used for shifting by 10 bits */
+
+static const UTF32 halfBase = 0x0010000UL;
+static const UTF32 halfMask = 0x3FFUL;
+
+#define UNI_SUR_HIGH_START (UTF32)0xD800
+#define UNI_SUR_HIGH_END (UTF32)0xDBFF
+#define UNI_SUR_LOW_START (UTF32)0xDC00
+#define UNI_SUR_LOW_END (UTF32)0xDFFF
+
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
+ * left as-is for anyone who may want to do such conversion, which was
+ * allowed in earlier algorithms.
+ */
+static const char trailingBytesForUTF8[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+/*
+ * Magic values subtracted from a buffer value during UTF8 conversion.
+ * This table contains as many values as there might be trailing bytes
+ * in a UTF-8 sequence.
+ */
+static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
+ 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
+
+/*
+ * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
+ * into the first byte, depending on how many bytes follow. There are
+ * as many entries in this table as there are UTF-8 sequence types.
+ * (I.e., one byte sequence, two byte... etc.). Remember that sequencs
+ * for *legal* UTF-8 will be 4 or fewer bytes total.
+ */
+static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * If not calling this from ConvertUTF8to*, then the length can be set by:
+ * length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns false. The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+
+static Boolean isLegalUTF8(const UTF8 *source, int length) {
+ UTF8 a;
+ const UTF8 *srcptr = source+length;
+ switch (length) {
+ default: return false;
+ /* Everything else falls through when "true"... */
+ case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 2: if ((a = (*--srcptr)) > 0xBF) return false;
+
+ switch (*source) {
+ /* no fall-through in this inner switch */
+ case 0xE0: if (a < 0xA0) return false; break;
+ case 0xED: if (a > 0x9F) return false; break;
+ case 0xF0: if (a < 0x90) return false; break;
+ case 0xF4: if (a > 0x8F) return false; break;
+ default: if (a < 0x80) return false;
+ }
+
+ case 1: if (*source >= 0x80 && *source < 0xC2) return false;
+ }
+ if (*source > 0xF4) return false;
+ return true;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF8toUTF16 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF8* source = *sourceStart;
+ UTF16* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+ if (source + extraBytesToRead >= sourceEnd) {
+ result = sourceExhausted; break;
+ }
+ /* Do this check whether lenient or strict */
+ if (! isLegalUTF8(source, extraBytesToRead+1)) {
+ result = sourceIllegal;
+ break;
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extraBytesToRead) {
+ case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
+ case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
+ case 3: ch += *source++; ch <<= 6;
+ case 2: ch += *source++; ch <<= 6;
+ case 1: ch += *source++; ch <<= 6;
+ case 0: ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ DebugAssertIf (target >= targetEnd);
+
+ if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ source -= (extraBytesToRead+1); /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = (UTF16)ch; /* normal case */
+ }
+ } else if (ch > UNI_MAX_UTF16) {
+ if (flags == strictConversion) {
+ result = sourceIllegal;
+ source -= (extraBytesToRead+1); /* return to the start */
+ break; /* Bail out; shouldn't continue */
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ if (target + 1 >= targetEnd) {
+ source -= (extraBytesToRead+1); /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ ch -= halfBase;
+ *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
+ *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* ---------------------------------------------------------------------
+
+ Note A.
+ The fall-through switches in UTF-8 reading code save a
+ temp variable, some decrements & conditionals. The switches
+ are equivalent to the following loop:
+ {
+ int tmpBytesToRead = extraBytesToRead+1;
+ do {
+ ch += *source++;
+ --tmpBytesToRead;
+ if (tmpBytesToRead) ch <<= 6;
+ } while (tmpBytesToRead > 0);
+ }
+ In UTF-8 writing code, the switches on "bytesToWrite" are
+ similarly unrolled loops.
+
+ --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF16toUTF8 (
+ const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF16* source = *sourceStart;
+ UTF8* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ unsigned short bytesToWrite = 0;
+ const UTF32 byteMask = 0xBF;
+ const UTF32 byteMark = 0x80;
+ const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
+ ch = *source++;
+ /* If we have a surrogate pair, convert to UTF32 first. */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END && source < sourceEnd) {
+ UTF32 ch2 = *source;
+ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+ ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ + (ch2 - UNI_SUR_LOW_START) + halfBase;
+ ++source;
+ } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ } else if ((flags == strictConversion) && (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END)) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ /* Figure out how many bytes the result will require */
+ if (ch < (UTF32)0x80) { bytesToWrite = 1;
+ } else if (ch < (UTF32)0x800) { bytesToWrite = 2;
+ } else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
+ } else if (ch < (UTF32)0x200000) { bytesToWrite = 4;
+ } else { bytesToWrite = 2;
+ ch = UNI_REPLACEMENT_CHAR;
+ }
+
+ target += bytesToWrite;
+ if (target > targetEnd) {
+ source = oldSource; /* Back up source pointer! */
+ target -= bytesToWrite; result = targetExhausted; break;
+ }
+ switch (bytesToWrite) { /* note: everything falls through. */
+ case 4: *--target = (ch | byteMark) & byteMask; ch >>= 6;
+ case 3: *--target = (ch | byteMark) & byteMask; ch >>= 6;
+ case 2: *--target = (ch | byteMark) & byteMask; ch >>= 6;
+ case 1: *--target = ch | firstByteMark[bytesToWrite];
+ }
+ target += bytesToWrite;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+
+bool ConvertUTF8toUTF16 (const char* source, int srcLength, UInt16* output, int& outlength)
+{
+ UInt16* newoutput = output;
+ const UTF8* src = (UTF8*)source;
+ if (ConvertUTF8toUTF16(&src, src + srcLength, &newoutput, newoutput + srcLength, lenientConversion) != sourceIllegal)
+ {
+ outlength = newoutput - output;
+ return true;
+ }
+ else
+ {
+ outlength = 0;
+ return false;
+ }
+}
+
+bool ConvertUTF8toUTF16 (const std::string& source, dynamic_array<UInt16>& utf16)
+{
+ utf16.resize_uninitialized(source.size());
+ int length = 0;
+ bool success = ConvertUTF8toUTF16(source.data(), (int) source.size(), utf16.begin(), length);
+ utf16.resize_uninitialized(length);
+ return success;
+}
+
+bool ConvertUTF16toUTF8 (const UInt16* source, int srcLength, char* output, int& outlength)
+{
+ UTF8* newoutput = (UTF8*)output;
+ const UTF16* src = (UTF16*)source;
+ if (ConvertUTF16toUTF8(&src, src + srcLength, &newoutput, newoutput + (srcLength*4), lenientConversion) != sourceIllegal)
+ {
+ outlength = newoutput - (UTF8*)output;
+ return true;
+ }
+ else
+ {
+ outlength = 0;
+ return false;
+ }
+}
+
+
+bool ConvertUTF16toUTF8 (const dynamic_array<UnicodeChar>& source, std::string& utf8)
+{
+ utf8.resize(source.size()*4);
+ int length = 0;
+ bool success = ConvertUTF16toUTF8(source.data(), (int) source.size(), &utf8[0], length);
+ utf8.resize(length);
+ return success;
+}
+
+bool ConvertUTF16toUTF8 (const UInt16 utf16character, std::string& utf8)
+{
+ int len;
+ char character[5];
+ if (!ConvertUTF16toUTF8 (&utf16character, 1, character, len))
+ return false;
+ character[len] = 0;
+ utf8 = std::string(character);
+ return true;
+}
diff --git a/Runtime/Misc/UTF8.h b/Runtime/Misc/UTF8.h
new file mode 100644
index 0000000..c21a367
--- /dev/null
+++ b/Runtime/Misc/UTF8.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "Runtime/Utilities/dynamic_array.h"
+
+typedef UInt16 UnicodeChar;
+
+bool ConvertUTF8toUTF16 (const char* source, int srcLength, UnicodeChar* output, int& outlength);
+bool ConvertUTF8toUTF16 (const std::string& source, dynamic_array<UnicodeChar>& utf16);
+bool ConvertUTF16toUTF8 (const UInt16* source, int srcLength, char* output, int& outlength);
+bool ConvertUTF16toUTF8 (const dynamic_array<UnicodeChar>& source, std::string& utf8);
+bool ConvertUTF16toUTF8 (const UInt16* source, int srcLength, char* output, int& outlength);
+bool ConvertUTF16toUTF8 (const UInt16 utf16character, std::string& utf8);
diff --git a/Runtime/Misc/UserList.cpp b/Runtime/Misc/UserList.cpp
new file mode 100644
index 0000000..ca2783e
--- /dev/null
+++ b/Runtime/Misc/UserList.cpp
@@ -0,0 +1,195 @@
+#include "UnityPrefix.h"
+#include "UserList.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/BaseClasses/MessageIdentifier.h"
+#include "Runtime/BaseClasses/GameObject.h"
+
+void UserListNode::Clear ()
+{
+ if (IsConnected())
+ {
+ UserList* other = static_cast<UserList*>(m_Entry.other);
+ other->RemoveIndex(m_Entry.indexInOther);
+ m_Entry = Entry();
+ }
+}
+
+void UserList::Clear ()
+{
+ for (int index=0; index<m_Entries.size(); index++)
+ {
+ Entry& user = m_Entries[index];
+ if (user.indexInOther == -1)
+ {
+ // Other is a node
+ UserListNode* other = static_cast<UserListNode*>(user.other);
+ DebugAssert(other->m_Entry.other == this);
+ DebugAssert(other->m_Entry.indexInOther == index);
+ other->m_Entry = Entry();
+ }
+ else
+ {
+ // Other is a another list
+ UserList* other = static_cast<UserList*>(user.other);
+ DebugAssert(other->m_Entries[user.indexInOther].other == this);
+ DebugAssert(other->m_Entries[user.indexInOther].indexInOther == index);
+ other->RemoveIndex(user.indexInOther);
+ }
+ }
+ m_Entries.clear();
+}
+
+void UserList::Reserve (size_t size)
+{
+ m_Entries.reserve(size);
+}
+
+void UserList::AddUser (UserListNode& other)
+{
+ other.Clear();
+ other.m_Entry.other = this;
+ other.m_Entry.indexInOther = m_Entries.size();
+ Entry& user = m_Entries.push_back();
+ user.other = &other;
+ user.indexInOther = -1;
+}
+
+void UserList::AddUser (UserList& other)
+{
+ DebugAssert(&other != this);
+ int index = m_Entries.size();
+ int indexInOther = other.m_Entries.size();
+ Entry& user = m_Entries.push_back();
+ user.other = &other;
+ user.indexInOther = indexInOther;
+ Entry& otherUser = other.m_Entries.push_back();
+ otherUser.other = this;
+ otherUser.indexInOther = index;
+}
+
+void UserList::SendMessage (const MessageIdentifier& msg)
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ MessageData data;
+ // Traverse list backwards, makes it easier if an element removes itself
+ int index = (int)m_Entries.size() - 1;
+ while (index >= 0)
+ {
+ UserListBase* other = m_Entries[index].other;
+#if DEBUGMODE
+ // Verify that the link back is correct
+ GetEntryInOther(index);
+#endif
+ SendMessageDirect(*other->GetTarget(), msg, data);
+ // Make sure index is within range
+ index = std::min(index, (int)m_Entries.size());
+ index--;
+ }
+}
+
+UserList::Entry& UserList::GetEntryInOther (int index)
+{
+ Entry& user = m_Entries[index];
+ Entry* entryInOther;
+ if (user.indexInOther == -1)
+ {
+ // Other is a node
+ UserListNode* other = static_cast<UserListNode*>(user.other);
+ entryInOther = &other->m_Entry;
+ }
+ else
+ {
+ // Other is a another list
+ UserList* other = static_cast<UserList*>(user.other);
+ entryInOther = &other->m_Entries[user.indexInOther];
+ }
+ DebugAssert(entryInOther->other == this);
+ DebugAssert(entryInOther->indexInOther == index);
+ return *entryInOther;
+}
+
+void UserList::RemoveIndex (int index)
+{
+ int lastIndex = m_Entries.size() - 1;
+ if (index != lastIndex)
+ {
+ // Move last entry to index
+ Entry& lastUser = m_Entries[lastIndex];
+ m_Entries[index] = lastUser;
+ Entry& entryInOther = GetEntryInOther(lastIndex);
+ entryInOther.indexInOther = index;
+ }
+ m_Entries.pop_back();
+}
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+class TestUserList : public UserList
+{
+public:
+ TestUserList() : UserList(NULL) {}
+};
+
+class TestUserListNode : public UserListNode
+{
+public:
+ TestUserListNode() : UserListNode(NULL) {}
+};
+
+SUITE (UserListTests)
+{
+TEST(UserList_BasicUserList)
+{
+ const int kNumA = 10;
+ const int kNumB = 20;
+ const int kNumC = 30;
+ TestUserList listsA[kNumA];
+ TestUserList listsB[kNumB];
+ TestUserList listsC[kNumC];
+ TestUserListNode nodesD[kNumC];
+ for (int c = 0; c < kNumC; c++)
+ if ((c % 3) == 1)
+ listsC[c].AddUser(nodesD[c]);
+ for (int a = 0; a < kNumA; a++)
+ for (int b = 0; b < kNumB; b++)
+ if (((a + b) % 3) == 0)
+ listsA[a].AddUser(listsB[b]);
+ for (int b = 0; b < kNumB; b++)
+ for (int c = 0; c < kNumC; c++)
+ if (((b + c) % 5) == 0)
+ listsB[b].AddUser(listsC[c]);
+ for (int c = 0; c < kNumC; c++)
+ if ((c % 3) == 2)
+ listsC[c].AddUser(nodesD[c]);
+ for (int a = kNumA - 1; a >= 0; a--)
+ if ((a % 2) == 0)
+ listsA[a].Clear();
+ for (int c = 0; c < kNumC; c++)
+ if ((c % 4) == 0)
+ nodesD[c].Clear();
+ for (int b = 0; b < kNumB; b++)
+ listsB[b].Clear();
+ for (int a = 0; a < kNumA; a++)
+ {
+ CHECK_EQUAL (0, listsA[a].GetSize());
+ }
+ for (int c = 0; c < kNumC; c++)
+ {
+ if ((c % 3) != 0 && (c % 4) != 0)
+ {
+ CHECK_EQUAL (1, listsC[c].GetSize());
+ CHECK_EQUAL (true, nodesD[c].IsConnected());
+ }
+ else
+ {
+ CHECK_EQUAL (0, listsC[c].GetSize());
+ CHECK_EQUAL (false, nodesD[c].IsConnected());
+ }
+ }
+}
+}
+
+#endif
diff --git a/Runtime/Misc/UserList.h b/Runtime/Misc/UserList.h
new file mode 100644
index 0000000..7999ba9
--- /dev/null
+++ b/Runtime/Misc/UserList.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/NonCopyable.h"
+#include "Runtime/Modules/ExportModules.h"
+
+class MessageIdentifier;
+
+// A UserList can be connected to other UserLists or UserListNodes.
+// A UserListNode is simply the optimized case of a single-element list.
+// The connected lists are symmetrical and can send messages in both directions.
+// Deleting a node or list will disconnect it from the graph.
+
+class EXPORT_COREMODULE UserListBase : public NonCopyable
+{
+public:
+ Object* GetTarget () { return m_Target; }
+
+protected:
+ UserListBase (Object* target) : m_Target(target) {}
+ struct Entry
+ {
+ Entry() : other(NULL), indexInOther(-1) {}
+ UserListBase* other;
+ int indexInOther;
+ };
+ Object* m_Target;
+};
+
+class UserListNode : public UserListBase
+{
+public:
+ UserListNode (Object* target) : UserListBase(target) {}
+ ~UserListNode () { Clear(); }
+
+ void Clear ();
+ bool IsConnected () const { return m_Entry.other != NULL; }
+
+private:
+ friend class UserList;
+ Entry m_Entry;
+};
+
+class EXPORT_COREMODULE UserList : public UserListBase
+{
+public:
+ UserList (Object* target) : UserListBase(target) {}
+ ~UserList () { Clear(); }
+
+ void Clear ();
+ void Reserve (size_t size);
+ void AddUser (UserListNode& other);
+ void AddUser (UserList& other);
+ void SendMessage (const MessageIdentifier& msg);
+ size_t GetSize () const { return m_Entries.size(); }
+
+private:
+ friend class UserListNode;
+ Entry& GetEntryInOther (int index);
+ void RemoveIndex (int index);
+
+ dynamic_array<Entry> m_Entries;
+};
diff --git a/Runtime/Misc/WWWCached.cpp b/Runtime/Misc/WWWCached.cpp
new file mode 100644
index 0000000..17b7e2a
--- /dev/null
+++ b/Runtime/Misc/WWWCached.cpp
@@ -0,0 +1,319 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_CACHING && ENABLE_WWW
+#include "WWWCached.h"
+#include "Runtime/Misc/CachingManager.h"
+#include "PlatformDependent/CommonWebPlugin/UnityWebStream.h"
+#include "Runtime/Misc/AssetBundleUtility.h"
+#include "Runtime/Misc/SaveAndLoadHelper.h"
+#include "Runtime/Misc/AssetBundle.h"
+
+#if UNITY_EDITOR
+#include "Runtime/BaseClasses/IsPlaying.h"
+#endif
+
+void WWWCached::StartDownload(bool cached)
+{
+ if (m_WWW != NULL)
+ {
+ m_WWW->Release();
+ m_WWW = NULL;
+ }
+
+ printf_console("starting www download: %s\n", m_URL);
+ WWWHeaders headers;
+ m_WWW = WWW::Create(m_URL, NULL, 0, headers, true, cached, m_CacheVersion, m_RequestedCRC);
+ m_WWW->SetThreadPriority(m_ThreadPriority);
+}
+
+bool WWWCached::IsDownloadingDone () const
+{
+ // Actually, IsDone() ought to be simplified, but this temporary hack
+ // alows the class to compile properly.
+ return const_cast<WWWCached*>(this)->IsDoneImpl();
+}
+
+bool WWWCached::IsDoneImpl()
+{
+ if (m_Abort)
+ return true;
+ if (m_WWW != NULL)
+ {
+ if (m_WWW->IsDone ())
+ {
+ if (!m_WWW->IsCached())
+ {
+ // downloaded to memory.
+ if (m_WWW->GetError() != NULL)
+ {
+ m_Error = m_WWW->GetError();
+ m_Abort = true;
+ }
+ // no error: success. we are done.
+ else return true;
+ }
+ else
+ {
+ // downloaded to cache
+ if (m_WWW->GetError() != NULL)
+ {
+ UnityWebStream* stream = m_WWW->GetUnityWebStream();
+
+ if (stream && !stream->GetError().empty())
+ {
+ // we might have had an error caching to disk.
+ // try memory download instead.
+ StartDownload (false);
+ return false;
+ }
+ else
+ {
+ m_Error = m_WWW->GetError();
+ m_Abort = true;
+ }
+ }
+ else
+ {
+ m_DidDownload = true;
+ printf_console("loading from cache: %s\n", m_URL);
+ m_CacheRequest = GetCachingManager().LoadCached(m_URL, m_CacheVersion, m_RequestedCRC);
+ if (m_CacheRequest == NULL)
+ {
+ // error: try memory download instead.
+ StartDownload (false);
+ return false;
+ }
+ }
+ }
+ m_WWW->Release();
+ m_WWW = NULL;
+ }
+ }
+ else if (m_CacheRequest != NULL)
+ {
+ if (m_CacheRequest->IsDone ())
+ {
+ if (m_CacheRequest->DidSucceed ())
+ {
+ AssetBundle* bundle = m_CacheRequest->GetAssetBundle ();
+
+ ////WORKAROUND!
+ //// Same workaround as in ExtractAssetBundle (unfortunately, we have two different
+ //// places where we load bundles from WWW).
+ bool performCompatibilityChecks = true;
+ #if UNITY_WEBPLAYER
+ const bool isStreamedSceneBundle =
+ bundle->m_CachedUnityWebStream->m_Files[0].find ("BuildPlayer-") == 0 ||
+ (bundle->m_CachedUnityWebStream->m_Files.size () > 1 && bundle->m_CachedUnityWebStream->m_Files[1].find ("BuildPlayer-") == 0);
+ performCompatibilityChecks = !isStreamedSceneBundle;
+ #endif
+
+ // Verify that we can actually use the bundle and
+ // abort if we don't.
+ if (performCompatibilityChecks && bundle && !TestAssetBundleCompatibility (*bundle, m_URL, m_Error))
+ {
+ m_Abort = true;
+ return false;
+ }
+
+ return true;
+ }
+ else
+ {
+ if (!m_CacheRequest->AssetBundleWithSameNameIsAlreadyLoaded ())
+ {
+ // File not in cache. Try downloading it to the cache
+ if (!m_DidDownload)
+ StartDownload (GetCachingManager().GetCurrentCache().GetIsReady());
+ else
+ // We already tried downloading to the cache without success. Try memory download instead.
+ StartDownload (false);
+ }
+ else
+ {
+ m_Error = m_CacheRequest->GetError();
+ m_Abort = true;
+ }
+ m_CacheRequest->Release();
+ m_CacheRequest = NULL;
+ }
+ }
+ }
+ return false;
+}
+
+float WWWCached::GetProgress () const
+{
+ if (m_DidDownload)
+ return 1.0f;
+ else if (m_CacheRequest != NULL && m_CacheRequest->DidSucceed ())
+ return 1.0f;
+ else if (m_WWW != NULL)
+ return m_WWW->GetProgress();
+ else
+ return 0.0f;
+}
+
+double WWWCached::GetETA () const
+{
+ if (m_DidDownload)
+ return 0.0;
+ else if (m_CacheRequest != NULL && m_CacheRequest->DidSucceed ())
+ return 0.0;
+ else if (m_WWW != NULL)
+ return m_WWW->GetETA();
+ else
+ return 0.0;
+}
+
+WWWCached::WWWCached (const char* url, int version, UInt32 crc)
+ : WWW (false, 0, crc)
+{
+ m_URL = (char*)malloc(strlen(url)+1);
+ AssertIf(!m_URL);
+ strcpy(m_URL, url);
+ m_Abort = false;
+ m_DidDownload = false;
+ m_WWW = NULL;
+ m_CacheRequest = NULL;
+ m_CacheVersion = version;
+ m_AssetBundleRetrieved = false;
+
+#if UNITY_EDITOR
+ if (!IsWorldPlaying())
+ {
+ m_Error = "WWW.LoadFromCacheOrDownload is only available in play mode.";
+ m_Abort = true;
+ return;
+ }
+#endif
+
+ if (!GetCachingManager().GetEnabled())
+ {
+ StartDownload(false);
+ return;
+ }
+ m_CacheRequest = GetCachingManager().LoadCached(m_URL, m_CacheVersion, crc);
+ if (m_CacheRequest == NULL)
+ {
+ // error: try memory download instead.
+ StartDownload (false);
+ return;
+ }
+}
+
+WWWCached::~WWWCached ()
+{
+ free (m_URL);
+ if (m_CacheRequest != NULL)
+ m_CacheRequest->Release();
+
+ if (m_WWW != NULL)
+ m_WWW->Release();
+}
+
+const char* WWWCached::GetError()
+{
+ if(!m_Error.empty())
+ return m_Error.c_str();
+ else if (m_WWW != NULL)
+ return m_WWW->GetError();
+ else
+ return NULL;
+}
+
+void WWWCached::Cancel()
+{
+ if (m_WWW != NULL)
+ m_WWW->Cancel();
+
+ if ( m_CacheRequest != NULL && !m_AssetBundleRetrieved )
+ {
+ // If a cached request was canceled early, then we have to wait until
+ // the request is complete on the other thread. Otherwise, AssetBundle
+ // collisions may occur if we don't unload the asset bundle.
+
+ while (!m_CacheRequest->IsDone ())
+ {
+ GetPreloadManager().UpdatePreloading();
+ Thread::Sleep(0.05f);
+ }
+
+ if (m_CacheRequest->DidSucceed ())
+ {
+ AssetBundle* bundle = GetAssetBundle();
+ if ( bundle )
+ UnloadAssetBundle( *bundle, true );
+ }
+ }
+ m_Abort = true;
+}
+
+void WWWCached::SetThreadPriority( ThreadPriority priority )
+{
+ if (m_WWW != NULL)
+ m_WWW->SetThreadPriority(priority);
+}
+
+void WWWCached::BlockUntilDone ()
+{
+ while (!IsDone())
+ {
+ if (m_WWW != NULL)
+ m_WWW->BlockUntilDone();
+
+ if ( m_CacheRequest != NULL )
+ GetPreloadManager().UpdatePreloading();
+
+ Thread::Sleep(0.05f);
+ }
+}
+
+bool WWWCached::HasDownloadedOrMayBlock ()
+{
+ if (GetError () != NULL)
+ {
+ ErrorString(Format("You are trying to load data from a www stream which had the following error when downloading.\n%s", GetError()));
+ return false;
+ }
+
+ if (m_WWW != NULL)
+ return m_WWW->HasDownloadedOrMayBlock();
+ return true;
+}
+
+AssetBundle* WWWCached::GetAssetBundle ()
+{
+ BlockUntilDone ();
+ if (m_WWW && !m_WWW->IsCached())
+ return ExtractAssetBundle (*m_WWW);
+ else if (m_CacheRequest != NULL)
+ {
+ m_AssetBundleRetrieved = true;
+ return m_CacheRequest->GetAssetBundle();
+ }
+ return NULL;
+}
+
+const UInt8* WWWCached::GetData()
+{
+ ErrorString(kWWWCachedAccessError);
+ return NULL;
+}
+const UInt8* WWWCached::GetPartialData() const
+{
+ ErrorString(kWWWCachedAccessError);
+ return NULL;
+}
+size_t WWWCached::GetSize()
+{
+ ErrorString(kWWWCachedAccessError);
+ return 0;
+}
+size_t WWWCached::GetPartialSize() const
+{
+ ErrorString(kWWWCachedAccessError);
+ return 0;
+}
+
+#endif //ENABLE_CACHING && ENABLE_WWW
diff --git a/Runtime/Misc/WWWCached.h b/Runtime/Misc/WWWCached.h
new file mode 100644
index 0000000..f0146c3
--- /dev/null
+++ b/Runtime/Misc/WWWCached.h
@@ -0,0 +1,59 @@
+#ifndef WWWCACHED_H
+#define WWWCACHED_H
+
+#define kWWWCachedAccessError "WWWCached data can only be accessed using the assetBundle property!"
+
+#if ENABLE_CACHING && ENABLE_WWW
+
+#include "Runtime/Export/WWW.h"
+
+
+class WWWCached : public WWW
+{
+ char* m_URL;
+ bool m_DidDownload;
+ bool m_Abort;
+ WWW* m_WWW;
+ string m_Error;
+ AsyncCachedUnityWebStream* m_CacheRequest;
+ bool m_AssetBundleRetrieved;
+
+ void StartDownload(bool cached);
+
+public:
+ WWWCached (const char* url, int version, UInt32 crc);
+ ~WWWCached ();
+
+ AssetBundle* GetAssetBundle ();
+
+ virtual const UInt8* GetData();
+ virtual const UInt8* GetPartialData() const;
+ virtual size_t GetSize();
+ virtual size_t GetPartialSize() const;
+
+ virtual double GetETA() const;
+
+ virtual void LockPartialData() {}
+ virtual void UnlockPartialData() {}
+
+ // Returns true when the download is complete or failed.
+ virtual void Cancel();
+ virtual bool IsDownloadingDone() const;
+ virtual float GetProgress() const;
+ virtual float GetUploadProgress() const { return 0.0f; }
+ virtual const char* GetError();
+ virtual const char* GetUrl() const { return m_URL; }
+
+ virtual bool HasDownloadedOrMayBlock ();
+ virtual void BlockUntilDone ();
+
+ virtual void SetThreadPriority( ThreadPriority priority );
+
+ virtual WWWType GetType () const { return kWWWTypeCached; }
+
+private:
+ bool IsDoneImpl();
+};
+
+#endif //ENABLE_CACHING
+#endif
diff --git a/Runtime/Modules/ExportModules.h b/Runtime/Modules/ExportModules.h
new file mode 100644
index 0000000..47f6b02
--- /dev/null
+++ b/Runtime/Modules/ExportModules.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#ifndef BUILDING_COREMODULE
+#define BUILDING_COREMODULE 1
+#endif
+
+#if UNITY_WIN && UNITY_STANDALONE && !UNITY_WINRT && 0
+
+#if BUILDING_COREMODULE
+#define EXPORT_COREMODULE __declspec(dllexport)
+#else
+#define EXPORT_COREMODULE __declspec(dllimport)
+#endif
+
+#define EXPORT_MODULE __declspec(dllexport)
+
+#elif UNITY_OSX && UNITY_STANDALONE && 0
+
+#if BUILDING_COREMODULE
+#define EXPORT_COREMODULE __attribute__((visibility("default")))
+#else
+#define EXPORT_COREMODULE
+#endif
+
+#define EXPORT_MODULE __attribute__((visibility("default")))
+
+#else
+
+#define EXPORT_COREMODULE
+#define EXPORT_MODULE
+
+#endif
diff --git a/Runtime/Modules/LoadDylib.cpp b/Runtime/Modules/LoadDylib.cpp
new file mode 100644
index 0000000..581a770
--- /dev/null
+++ b/Runtime/Modules/LoadDylib.cpp
@@ -0,0 +1,230 @@
+#include "UnityPrefix.h"
+#include "LoadDylib.h"
+#include <map>
+
+#if UNITY_OSX || UNITY_LINUX
+#include "dlfcn.h"
+#endif
+
+#if UNITY_WIN
+#include "PlatformDependent/Win/PathUnicodeConversion.h"
+#endif
+
+#if UNITY_WINRT
+#include "PlatformDependent/MetroPlayer/MetroUtils.h"
+#endif
+
+using namespace std;
+
+map<string, void*> gLoaded;
+
+
+std::string GetPathWithPlatformSpecificDllExtension(const std::string& path)
+{
+ string extended = path;
+
+ #if UNITY_OSX
+ extended += ".dylib";
+ #elif UNITY_WIN
+ extended += ".dll";
+ #elif UNITY_LINUX
+ extended += ".so";
+ #else
+ ErrorString ("Dynamic module loading not implemented");
+ #endif
+
+ return extended;
+}
+
+
+void* LoadDynamicLibrary (const std::string& absolutePath)
+{
+ void* library = NULL;
+
+ // load library if needed
+ if (gLoaded.find(absolutePath) != gLoaded.end())
+ {
+ library = gLoaded[absolutePath];
+ }
+ else
+ {
+ #if UNITY_OSX || UNITY_LINUX
+ /// RTLD_LOCAL is supposedly default and also completely fails on 10.3.9
+ library = dlopen (absolutePath.c_str (), RTLD_NOW);
+ if ( !library )
+ ErrorStringMsg( "ERROR: LoadDynamicLibrary(): %s\n", dlerror() );
+
+ #elif UNITY_WIN
+
+ #if UNITY_WINRT
+ HMODULE module = LoadPackagedLibrary (ConvertToWindowsPath(absolutePath.c_str())->Data(), 0);
+ #else
+ std::wstring widePath;
+ ConvertUnityPathName( absolutePath, widePath );
+ HINSTANCE module = LoadLibraryW( widePath.c_str() );
+ #endif
+ library = module;
+
+ #endif
+
+ if (library)
+ {
+ gLoaded[absolutePath] = library;
+ }
+ }
+
+ return library;
+}
+
+
+void* LoadAndLookupSymbol (const std::string& absolutePath, const std::string& name)
+{
+ void* library = LoadDynamicLibrary(absolutePath);
+ void* symbol = LookupSymbol(library, name);
+ return symbol;
+}
+
+
+void UnloadDynamicLibrary (void* libraryReference)
+{
+ map<string, void*>::iterator it;
+ for (it = gLoaded.begin(); it != gLoaded.end(); ++it)
+ {
+ if (it->second == libraryReference)
+ {
+ #if UNITY_OSX || UNITY_LINUX
+ dlclose (it->second);
+ #elif UNITY_WIN
+ FreeLibrary( (HMODULE)it->second );
+ #endif
+
+ gLoaded.erase(it);
+ break;
+ }
+ }
+}
+
+
+void UnloadDynamicLibrary (const std::string& absolutePath)
+{
+ if (gLoaded.count (absolutePath) && gLoaded[absolutePath])
+ {
+ #if UNITY_OSX || UNITY_LINUX
+ dlclose (gLoaded[absolutePath]);
+ #elif UNITY_WIN
+ FreeLibrary( (HMODULE)gLoaded[absolutePath] );
+ #endif
+ }
+ gLoaded.erase (absolutePath);
+}
+
+
+bool LoadAndLookupSymbols (const char* path, ...)
+{
+ va_list ap;
+ va_start (ap, path);
+ while (true)
+ {
+ const char* symbolName = va_arg (ap, const char*);
+ if (symbolName == NULL)
+ return true;
+
+ void** functionHandle = va_arg (ap, void**);
+ AssertIf(functionHandle == NULL);
+
+ *functionHandle = LoadAndLookupSymbol (path, symbolName);
+ if (*functionHandle == NULL)
+ return false;
+ }
+
+ return false;
+}
+
+void* LookupSymbol(void* libraryReference, const std::string& symbolName)
+{
+#if UNITY_OSX || UNITY_LINUX
+
+ void *sym = dlsym (libraryReference, symbolName.c_str ());
+ if (!sym)
+ ErrorStringMsg("Could not load symbol %s : %s\n", symbolName.c_str(), dlerror());
+ return sym;
+
+#elif UNITY_WIN
+
+ if( !libraryReference )
+ return NULL;
+ return GetProcAddress( (HMODULE)libraryReference, symbolName.c_str() );
+
+#else
+ ErrorStringMsg("LoadAndLookupSymbol is not supported on this platform.\n");
+ return NULL;
+#endif
+}
+
+/*
+
+#include <Carbon/Carbon.h>
+#import <mach-o/dyld.h>
+#import <stdlib.h>
+#import <string.h>
+#include "FileUtilities.h"
+
+using namespace std;
+
+CFBundleRef LoadDynamicLibrary (const string& absolutePath)
+{
+ CFStringRef cfstring = CFStringCreateWithCString (NULL, ResolveSymlinks(absolutePath).c_str (), kCFStringEncodingUTF8);
+
+ CFURLRef url = CFURLCreateWithFileSystemPath(
+ kCFAllocatorDefault,
+ cfstring,
+ kCFURLPOSIXPathStyle,
+ false);
+// CFURLRef url = CFURLCreateFromFileSystemRepresentation (kCFAllocatorDefault, (const UInt8*)absolutePath.c_str (), absolutePath.size (), false);
+
+ CFBundleRef bundle = CFBundleCreate (NULL, url);
+ CFRelease (url);
+ CFRelease (cfstring);
+ if (!bundle)
+ return false;
+
+ // Check if already loaded
+ if (CFBundleIsExecutableLoaded (bundle))
+ return bundle;
+
+ // Load the bundle
+ if (!CFBundleLoadExecutable (bundle))
+ return NULL;
+
+ return bundle;
+}
+
+void UnloadDynamicLibrary (const string& absolutePath)
+{
+// CFURLRef url = CFURLCreateFromFileSystemRepresentation (kCFAllocatorDefault, (const UInt8*)absolutePath.c_str (), absolutePath.size (), false);
+ CFStringRef cfstring = CFStringCreateWithCString (kCFAllocatorDefault, absolutePath.c_str (), kCFStringEncodingUTF8);
+
+ CFURLRef url = CFURLCreateWithFileSystemPath(
+ kCFAllocatorDefault,
+ cfstring,
+ kCFURLPOSIXPathStyle,
+ true);
+
+ CFBundleRef bundle = CFBundleCreate (kCFAllocatorDefault, url);
+ CFRelease (url);
+ CFRelease (cfstring);
+ if (!bundle)
+ return;
+
+ CFBundleUnloadExecutable (bundle);
+}
+
+void* LoadAndLookupSymbol (const std::string& absolutePath, const std::string& name)
+{
+ CFBundleRef bundle = LoadDynamicLibrary (absolutePath);
+ CFStringRef cfstring = CFStringCreateWithCString (kCFAllocatorDefault, name.c_str (), kCFStringEncodingUTF8);
+ if (bundle == NULL)
+ return NULL;
+
+ return CFBundleGetFunctionPointerForName(bundle, cfstring);
+}*/
diff --git a/Runtime/Modules/LoadDylib.h b/Runtime/Modules/LoadDylib.h
new file mode 100644
index 0000000..7e019d6
--- /dev/null
+++ b/Runtime/Modules/LoadDylib.h
@@ -0,0 +1,13 @@
+#ifndef LOADDYLIB_H
+#define LOADDYLIB_H
+#include <string>
+
+std::string GetPathWithPlatformSpecificDllExtension(const std::string& path);
+void* LoadDynamicLibrary (const std::string& absolutePath);
+void* LoadAndLookupSymbol (const std::string& absolutePath, const std::string& name);
+void UnloadDynamicLibrary (void* libraryReference);
+void UnloadDynamicLibrary (const std::string& absolutePath);
+bool LoadAndLookupSymbols (const char* path, ...);
+void* LookupSymbol(void* libraryReference, const std::string& symbolName);
+
+#endif
diff --git a/Runtime/Modules/ModuleRegistration.cpp b/Runtime/Modules/ModuleRegistration.cpp
new file mode 100644
index 0000000..960ae96
--- /dev/null
+++ b/Runtime/Modules/ModuleRegistration.cpp
@@ -0,0 +1,93 @@
+#include "UnityPrefix.h"
+#include "ModuleRegistration.h"
+#include "LoadDylib.h"
+#include "RegisterStaticallyLinkedModules.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+
+#if UNITY_WINRT
+#include "PlatformDependent/MetroPlayer/MetroUtils.h"
+#endif
+
+enum { kMaxModuleCount = 10 };
+
+static int gAvailableModuleCount = 0;
+static ModuleRegistrationInfo gModuleRegistrationInfos[kMaxModuleCount];
+
+void RegisterAllAvailableModuleClasses (ClassRegistrationContext& context)
+{
+ RegisterAvailableModules ();
+
+ for(int i=0;i<gAvailableModuleCount;i++)
+ {
+ if (gModuleRegistrationInfos[i].registerClassesCallback != NULL)
+ gModuleRegistrationInfos[i].registerClassesCallback (context);
+ }
+}
+
+void RegisterAllAvailableModuleICalls ()
+{
+ RegisterAvailableModules ();
+
+ for(int i=0;i<gAvailableModuleCount;i++)
+ {
+ if (gModuleRegistrationInfos[i].registerIcallsCallback != NULL)
+ gModuleRegistrationInfos[i].registerIcallsCallback ();
+ }
+}
+
+void RegisterModuleInfo(ModuleRegistrationInfo& info)
+{
+ gModuleRegistrationInfos[gAvailableModuleCount] = info;
+ gAvailableModuleCount++;
+
+ // Bump up kMaxModuleCount if we have too many modules.
+ Assert(gAvailableModuleCount < kMaxModuleCount);
+}
+
+#if !UNITY_WP8
+
+void RegisterModuleInDynamicLibrary (const char* dllName, const char* modulename)
+{
+ std::string dllFullName = dllName;
+#if WEBPLUG
+ ErrorString ("Dynamic module loading not implemented for web plug");
+#elif UNITY_OSX
+ dllFullName = AppendPathName (GetApplicationPath (), "Contents/Frameworks/MacStandalonePlayer_"+dllFullName+".dylib");
+#elif UNITY_WIN
+ dllFullName = "StandalonePlayer_"+dllFullName+".dll";
+#elif UNITY_LINUX
+ dllFullName += "LinuxStandalonePlayer_"+dllFullName+".so";
+#endif
+
+ std::string funcName("RegisterModule_");
+ funcName.append(modulename);
+ void (*registerModuleFunction)() = (void(*)())LoadAndLookupSymbol(dllFullName, funcName);
+ if (registerModuleFunction == NULL)
+ return;
+
+ registerModuleFunction();
+}
+
+#endif
+
+static void RegisterDynamicallyLinkedModules()
+{
+ //Work in progress:
+// RegisterModuleInDynamicLibrary("GfxDeviceModule_Dynamic","GfxDevice");
+// RegisterModuleInDynamicLibrary("TerrainModule_Dynamic","Terrain");
+// RegisterModuleInDynamicLibrary("NavMeshModule_Dynamic","Navigation");
+// RegisterModuleInDynamicLibrary("AnimationModule_Dynamic","Animation");
+// RegisterModuleInDynamicLibrary("Dynamics2DModule_Dynamic","Physics2D");
+// RegisterModuleInDynamicLibrary("PhysicsModule_Dynamic","Physics");
+// RegisterModuleInDynamicLibrary("PhysicsEditorModule_Dynamic","PhysicsEditor");
+}
+
+void RegisterAvailableModules ()
+{
+ if (gAvailableModuleCount != 0)
+ return;
+
+ RegisterStaticallyLinkedModules();
+ RegisterDynamicallyLinkedModules();
+}
diff --git a/Runtime/Modules/ModuleRegistration.h b/Runtime/Modules/ModuleRegistration.h
new file mode 100644
index 0000000..8493f79
--- /dev/null
+++ b/Runtime/Modules/ModuleRegistration.h
@@ -0,0 +1,21 @@
+#pragma once
+
+/// Modules are not guaranteed to exist. You must always check if the module getter function returns non-null.
+
+struct ClassRegistrationContext;
+
+
+void RegisterAllAvailableModuleClasses (ClassRegistrationContext& context);
+void RegisterAllAvailableModuleICalls ();
+void RegisterAvailableModules ();
+
+typedef void RegisterClassesCallback (ClassRegistrationContext& context);
+typedef void RegisterIcallsCallback ();
+
+struct ModuleRegistrationInfo
+{
+ RegisterClassesCallback* registerClassesCallback;
+ RegisterIcallsCallback* registerIcallsCallback;
+};
+
+EXPORT_COREMODULE void RegisterModuleInfo(ModuleRegistrationInfo& info); \ No newline at end of file
diff --git a/Runtime/Modules/RegisterStaticallyLinkedModules.cpp b/Runtime/Modules/RegisterStaticallyLinkedModules.cpp
new file mode 100644
index 0000000..8a1c284
--- /dev/null
+++ b/Runtime/Modules/RegisterStaticallyLinkedModules.cpp
@@ -0,0 +1,73 @@
+#include "UnityPrefix.h"
+
+extern "C" void RegisterModule_Navigation();
+extern "C" void RegisterModule_Physics2D();
+extern "C" void RegisterModule_Physics();
+extern "C" void RegisterModule_PhysicsEditor();
+extern "C" void RegisterModule_Terrain();
+extern "C" void RegisterModule_Audio();
+extern "C" void RegisterModule_Animation();
+extern "C" void RegisterModule_ClusterRenderer();
+
+//we diverge from our usual convention that all defines have to be set to either 0 or 1.
+//Jam will set the *_IS_DYNAMICALLY_LINKED defines on this single cpp file only, based on if
+//jam is planning to link a certain module statically or dynamically. by doing the define check
+//with ifndef like this, it avoids having to add all these defines to all targets that do not yet
+//support modularization. this method also creates support for having some modules statically linked
+//and some modules dynamically, which is incredibly helpful during development of the modularization
+
+void RegisterStaticallyLinkedModules()
+{
+#ifndef NAVMESH_IS_DYNAMICALLY_LINKED
+ #if !UNITY_WEBGL
+ RegisterModule_Navigation();
+ #endif
+#endif
+
+#ifndef ANIMATION_IS_DYNAMICALLY_LINKED
+ RegisterModule_Animation();
+#endif
+
+#ifndef PHYSICS_IS_DYNAMICALLY_LINKED
+ #if ENABLE_PHYSICS
+ RegisterModule_Physics();
+ #endif
+#endif
+
+#ifndef TERRAIN_IS_DYNAMICALLY_LINKED
+ #if ENABLE_TERRAIN
+ RegisterModule_Terrain();
+ #endif
+#endif
+
+#ifndef DYNAMICS2D_IS_DYNAMICALLY_LINKED
+ #if ENABLE_2D_PHYSICS
+ RegisterModule_Physics2D();
+ #endif
+#endif
+
+#ifndef AUDIO_IS_DYNAMICALLY_LINKED
+ #if ENABLE_AUDIO
+ RegisterModule_Audio();
+ #endif
+#endif
+
+#ifndef CLUSTER_SYNC_IS_DYNAMICALLY_LINKED
+ #if ENABLE_CLUSTER_SYNC
+ RegisterModule_ClusterRenderer();
+ #endif
+#endif
+
+#if UNITY_EDITOR
+#ifndef PHYSICSEDITOR_IS_DYNAMICALLY_LINKED
+ RegisterModule_PhysicsEditor();
+#endif
+#endif
+
+#if ENABLE_CLUSTER_SYNC
+#ifndef CLUSTERRENDERER_IS_DYNAMICALLY_LINKED
+ RegisterModule_ClusterRenderer();
+#endif
+#endif
+
+} \ No newline at end of file
diff --git a/Runtime/Modules/RegisterStaticallyLinkedModules.h b/Runtime/Modules/RegisterStaticallyLinkedModules.h
new file mode 100644
index 0000000..9445630
--- /dev/null
+++ b/Runtime/Modules/RegisterStaticallyLinkedModules.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void RegisterStaticallyLinkedModules();
diff --git a/Runtime/Mono/Coroutine.cpp b/Runtime/Mono/Coroutine.cpp
new file mode 100644
index 0000000..887eafc
--- /dev/null
+++ b/Runtime/Mono/Coroutine.cpp
@@ -0,0 +1,379 @@
+#include "UnityPrefix.h"
+
+#include "Coroutine.h"
+
+#if ENABLE_SCRIPTING
+
+#include "Runtime/Misc/AsyncOperation.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Mono/MonoIncludes.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/GameCode/CallDelayed.h"
+#include "Runtime/Export/WWW.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+
+#if UNITY_IPHONE_API
+# include "Runtime/Input/OnScreenKeyboard.h"
+#endif
+
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+
+// copied from MonoBehaviour.cpp
+// if they should be synced - blame the author not me ;-)
+#define DEBUG_COROUTINE 0
+#define DEBUG_COROUTINE_LEAK 0
+
+Coroutine::Coroutine()
+ : m_DoneRunning(false)
+{
+ #if DEBUG_COROUTINE
+ static int coroutineCounter = 0;
+ coroutineCounter++;
+ printf_console("Allocating coroutine %d --- %d : 0x%x\n", this, coroutineCounter, &m_RefCount);
+ #endif
+}
+
+Coroutine::~Coroutine()
+{
+ Assert(m_CoroutineEnumeratorGCHandle == 0);
+
+ #if DEBUG_COROUTINE
+ printf_console("Deconstructor coroutine %d\n", this);
+ #endif
+}
+
+void Coroutine::SetMoveNextMethod(ScriptingMethodPtr method)
+{
+ m_MoveNext = method;
+}
+
+void Coroutine::SetCurrentMethod(ScriptingMethodPtr method)
+{
+ m_Current = method;
+}
+
+void Coroutine::ContinueCoroutine (Object* o, void* userData)
+{
+ Coroutine* coroutine = (Coroutine*)userData;
+ Assert(coroutine->m_RefCount > 0 && coroutine->m_RefCount < 1000000);
+
+ if ((Object*)coroutine->m_Behaviour != o)
+ {
+ ErrorString("Coroutine continue failure");
+ #if DEBUG_COROUTINE
+ if ((Object*)coroutine->m_Behaviour != o)
+ {
+ printf_console ("continue Coroutine fuckup %d refcount: %d behaviour: %d \n", coroutine, coroutine->m_RefCount, coroutine->m_Behaviour);
+ printf_console ("continue Coroutine fuckup name: %s methodname\n", ((MonoBehaviour*)(o))->GetScript()->GetName());
+ if (coroutine->m_CoroutineMethod)
+ printf_console ("continue Coroutine methodname: %s\n", scripting_method_get_name(coroutine->m_CoroutineMethod));
+ }
+ #endif
+ return;
+ }
+
+ coroutine->Run ();
+}
+
+void Coroutine::CleanupCoroutine (void* userData)
+{
+ Coroutine* coroutine = (Coroutine*)userData;
+ AssertIf(coroutine->m_RefCount <= 0);
+ AssertIf(coroutine->m_RefCount > 1000000);
+ coroutine->m_RefCount--;
+
+ #if DEBUG_COROUTINE
+ printf_console("decrease refcount %d - active: %d \n", coroutine, coroutine->m_RefCount);
+ #endif
+
+ if (coroutine->m_RefCount > 0)
+ return;
+
+ coroutine->m_DoneRunning = true;
+
+ #if DEBUG_COROUTINE
+ printf_console ("CleanupCoroutine %d\n", coroutine);
+ if (coroutine->m_Behaviour && GetDelayedCallManager().HasDelayedCall(coroutine->m_Behaviour, Coroutine::ContinueCoroutine, CompareCoroutineMethodName, coroutine))
+ {
+ printf_console ("FUCKUP is still in delayed call manager%d!\n", coroutine->m_Behaviour);
+ }
+ #endif
+
+ if (coroutine->m_ContinueWhenFinished)
+ {
+ CleanupCoroutine (coroutine->m_ContinueWhenFinished);
+ coroutine->m_ContinueWhenFinished = NULL;
+ }
+
+ if (coroutine->m_WaitingFor)
+ coroutine->m_WaitingFor->m_ContinueWhenFinished = NULL;
+
+ coroutine->RemoveFromList();
+
+ if (coroutine->m_AsyncOperation)
+ {
+ coroutine->m_AsyncOperation->SetCoroutineCallback(NULL, NULL, NULL, NULL);
+ coroutine->m_AsyncOperation->Release();
+ coroutine->m_AsyncOperation = NULL;
+ }
+
+ Assert(coroutine->m_CoroutineEnumeratorGCHandle != 0);
+
+ scripting_gchandle_free (coroutine->m_CoroutineEnumeratorGCHandle);
+
+ coroutine->m_CoroutineEnumeratorGCHandle = 0;
+
+ if (!coroutine->m_IsReferencedByMono)
+ {
+ delete coroutine;
+
+ #if DEBUG_COROUTINE_LEAK
+ gCoroutineCounter--;
+ #endif
+ }
+}
+
+void Coroutine::CleanupCoroutineGC (void* userData)
+{
+ Coroutine* coroutine = (Coroutine*)userData;
+ if (!coroutine->m_IsReferencedByMono)
+ return;
+
+ if (coroutine->m_RefCount != 0)
+ {
+ coroutine->m_IsReferencedByMono = false;
+ return;
+ }
+
+ ErrorIf(coroutine->IsInList());
+
+ delete coroutine;
+
+ #if DEBUG_COROUTINE
+ printf_console ("GC free coroutine: %d\n", coroutine);
+ #endif
+
+ #if DEBUG_COROUTINE_LEAK
+ gCoroutineCounter--;
+ #endif
+}
+
+
+bool Coroutine::CompareCoroutineMethodName (void* callBackUserData, void* cancelUserdata)
+{
+ Coroutine* coroutine = (Coroutine*)callBackUserData;
+ if (!coroutine->m_CoroutineMethod)
+ return false;
+
+ return strcmp(scripting_method_get_name(coroutine->m_CoroutineMethod), (const char*)cancelUserdata) == 0;
+}
+
+bool Coroutine::InvokeMoveNext(ScriptingExceptionPtr* exception)
+{
+ ScriptingInvocation invocation(m_MoveNext);
+ invocation.object = m_CoroutineEnumerator;
+ invocation.classContextForProfiler = m_Behaviour->GetClass();
+ invocation.objectInstanceIDContextForException = m_Behaviour->GetInstanceID();
+ bool result = invocation.Invoke<bool>(exception);
+ return result && *exception==NULL;
+}
+
+
+void Coroutine::Run ()
+{
+ Assert(m_RefCount != 0);
+ Assert(m_Behaviour != NULL);
+
+ #if DEBUG_COROUTINE
+ AssertIf(GetDelayedCallManager().HasDelayedCall(m_Behaviour, Coroutine::ContinueCoroutine, CompareCoroutineMethodName, this));
+ if (m_Behaviour == NULL)
+ {
+ printf_console ("Coroutine fuckup %d refcount: %d behaviour%d\n", this, m_RefCount, m_Behaviour);
+ }
+ #endif
+
+ // - Call MoveNext (This processes the function until the next yield!)
+ // - Call Current (This returns condition when to continue the coroutine next.)
+ // -> Queue it based on the continue condition
+
+ // Temporarily increase refcount so the object will not get destroyed during the m_MoveNext call
+ m_RefCount++;
+ ScriptingExceptionPtr exception = NULL;
+ bool keepLooping = InvokeMoveNext(&exception);
+ AssertIf(m_RefCount <= 0 || m_RefCount > 10000000);
+
+ bool coroutineWasDestroyedDuringMoveNext = m_RefCount == 1;
+ // Decrease temporary refcount so the object will not get destroyed during the m_MoveNext call
+ CleanupCoroutine(this);
+
+ // The coroutine has been destroyed in the mean time, probably due to a call to StopAllCoroutines, stop executing further
+ if (coroutineWasDestroyedDuringMoveNext)
+ {
+ Assert(m_ContinueWhenFinished == NULL);
+ return;
+ }
+
+ if (exception != NULL)
+ return;
+
+ // Are we done with this coroutine?
+ if (!keepLooping)
+ {
+ // If there is a coroutine waiting for this one to finish Run it!
+ if (m_ContinueWhenFinished)
+ {
+ AssertIf (this != m_ContinueWhenFinished->m_WaitingFor);
+ Coroutine* continueWhenFinished = m_ContinueWhenFinished;
+ m_ContinueWhenFinished->m_WaitingFor = NULL;
+ m_ContinueWhenFinished = NULL;
+ // The coroutine might have been stopped inside of the last coroutine invokation
+ if (continueWhenFinished->m_Behaviour)
+ continueWhenFinished->Run ();
+ CleanupCoroutine (continueWhenFinished);
+ }
+
+ return;
+ }
+
+ if (m_Behaviour == NULL)
+ return;
+
+ ProcessCoroutineCurrent();
+}
+
+void Coroutine::ProcessCoroutineCurrent()
+{
+ ScriptingExceptionPtr exception = NULL;
+#if !UNITY_FLASH
+ ScriptingInvocation invocation(m_Current);
+ invocation.object = m_CoroutineEnumerator;
+ invocation.objectInstanceIDContextForException = m_Behaviour->GetInstanceID();
+ invocation.classContextForProfiler = m_Behaviour->GetClass();
+ ScriptingObjectPtr monoWait = invocation.Invoke(&exception);
+#else
+ ScriptingObjectPtr monoWait = Ext_Flash_getProperty(m_CoroutineEnumerator,"IEnumerator_Current");
+#endif
+
+ AssertIf(m_RefCount <= 0 || m_RefCount > 10000000);
+
+ if (exception != NULL)
+ return;
+
+ if (monoWait == SCRIPTING_NULL)
+ {
+ m_RefCount++;
+ CallDelayed (ContinueCoroutine, m_Behaviour, 0.0F, this, 0.0F, CleanupCoroutine, DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kWaitForNextFrame);
+ return;
+ }
+
+ HandleIEnumerableCurrentReturnValue(monoWait);
+}
+
+void Coroutine::HandleIEnumerableCurrentReturnValue(ScriptingObjectPtr monoWait)
+{
+ AsyncOperation* async = NULL;
+ ScriptingClassPtr waitClass = scripting_object_get_class (monoWait, GetScriptingTypeRegistry());
+ const CommonScriptingClasses& classes = GetMonoManager ().GetCommonClasses ();
+
+ // Continue the coroutine in 'wait' seconds
+ if (scripting_class_is_subclass_of (waitClass, classes.waitForSeconds))
+ {
+ m_RefCount++;
+
+ float wait;
+ MarshallManagedStructIntoNative(monoWait,&wait);
+ CallDelayed (ContinueCoroutine, m_Behaviour, wait, this, 0.0F, CleanupCoroutine, DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kWaitForNextFrame);
+ return;
+ }
+
+ // Continue the coroutine on the next fixed update
+ if (scripting_class_is_subclass_of (waitClass, classes.waitForFixedUpdate))
+ {
+ m_RefCount++;
+ CallDelayed (ContinueCoroutine, m_Behaviour, 0.0F, this, 0.0F, CleanupCoroutine, DelayedCallManager::kRunFixedFrameRate);
+ return;
+ }
+
+ // Continue the coroutine at the end of frame
+ if (scripting_class_is_subclass_of (waitClass, classes.waitForEndOfFrame))
+ {
+ m_RefCount++;
+ CallDelayed (ContinueCoroutine, m_Behaviour, 0.0F, this, 0.0F, CleanupCoroutine, DelayedCallManager::kEndOfFrame);
+ return;
+ }
+
+ // Continue after another coroutine is finished
+ if (scripting_class_is_subclass_of (waitClass, classes.coroutine))
+ {
+ Coroutine* waitForCoroutine;
+ MarshallManagedStructIntoNative(monoWait,&waitForCoroutine);
+ if (waitForCoroutine->m_DoneRunning)
+ {
+ // continue executing.
+ ContinueCoroutine(m_Behaviour, this);
+ return;
+ }
+
+ if (waitForCoroutine->m_ContinueWhenFinished != NULL)
+ {
+ LogStringObject ("Another coroutine is already waiting for this coroutine!\nCurrently only one coroutine can wait for another coroutine!", m_Behaviour);
+ return;
+ }
+
+ m_RefCount++;
+ waitForCoroutine->m_ContinueWhenFinished = this;
+ m_WaitingFor = waitForCoroutine;
+ return;
+ }
+
+#if ENABLE_WWW
+ // Continue after fetching an www object is done
+
+ if (classes.www && scripting_class_is_subclass_of (waitClass, classes.www ))
+ {
+ WWW* wwwptr;
+ MarshallManagedStructIntoNative(monoWait,&wwwptr);
+ if(wwwptr != NULL)
+ {
+ m_RefCount++;
+ wwwptr->CallWhenDone (ContinueCoroutine, m_Behaviour, this, CleanupCoroutine);
+ }
+ return;
+ }
+#endif
+ // Continue after fetching an www object is done
+ if ((scripting_class_is_subclass_of (waitClass, classes.asyncOperation)) && (async = ScriptingObjectWithIntPtrField<AsyncOperation> (monoWait).GetPtr()) != NULL)
+ {
+ m_RefCount++;
+
+ if (async->IsDone())
+ {
+ CallDelayed (ContinueCoroutine, m_Behaviour, 0.0F, this, 0.0F, CleanupCoroutine, DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kWaitForNextFrame);
+ return;
+ }
+
+ // Use AysncOperation ContinueCoroutine - default path
+ if (async->HasCoroutineCallback ())
+ {
+ ////@TODO: Throw exception?
+ ErrorString("This asynchronous operation is already being yielded from another coroutine. An asynchronous operation can only be yielded once.");
+ CallDelayed (ContinueCoroutine, m_Behaviour, 0.0F, this, 0.0F, CleanupCoroutine, DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kWaitForNextFrame);
+ return;
+ }
+
+ async->SetCoroutineCallback(ContinueCoroutine, m_Behaviour, this, CleanupCoroutine);
+ m_AsyncOperation = async;
+ m_AsyncOperation->Retain();
+
+ return;
+ }
+
+ // Continue the coroutine on the next dynamic frame update
+ m_RefCount++;
+ CallDelayed (ContinueCoroutine, m_Behaviour, 0.0F, this, 0.0F, CleanupCoroutine, DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kWaitForNextFrame);
+ //Ext_MarshalMap_Release_ScriptingObject(monoWait);//RH TODO : RELEASE THE MONOWAIT OBJECTS SOMEWHERE
+}
+#endif
diff --git a/Runtime/Mono/Coroutine.h b/Runtime/Mono/Coroutine.h
new file mode 100644
index 0000000..f82a13d
--- /dev/null
+++ b/Runtime/Mono/Coroutine.h
@@ -0,0 +1,45 @@
+#ifndef _COROUTINE_H_
+#define _COROUTINE_H_
+#if ENABLE_SCRIPTING
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Utilities/LinkedList.h"
+
+class AsyncOperation;
+class MonoBehaviour;
+class Object;
+
+
+struct Coroutine : public ListElement
+{
+ ScriptingObjectPtr m_CoroutineEnumerator;
+ int m_CoroutineEnumeratorGCHandle;
+ ScriptingMethodPtr m_CoroutineMethod;
+ ScriptingMethodPtr m_MoveNext;
+ ScriptingMethodPtr m_Current;
+ MonoBehaviour* m_Behaviour;
+ int m_RefCount;
+ int m_IsReferencedByMono;
+ bool m_DoneRunning;
+ Coroutine* m_ContinueWhenFinished;
+ Coroutine* m_WaitingFor;
+ AsyncOperation* m_AsyncOperation;
+ Coroutine ();
+ ~Coroutine();
+
+ void Run ();
+
+ void SetMoveNextMethod(ScriptingMethodPtr method);
+ void SetCurrentMethod(ScriptingMethodPtr method);
+ static void ContinueCoroutine (Object* o, void* userData);
+ static void CleanupCoroutine (void* userData);
+ static void CleanupCoroutineGC (void* userData);
+ static bool CompareCoroutineMethodName (void* callBackUserData, void* cancelUserdata);
+
+private:
+ bool InvokeMoveNext(ScriptingExceptionPtr* exception);
+ void ProcessCoroutineCurrent();
+ void HandleIEnumerableCurrentReturnValue(ScriptingObjectPtr);
+};
+#endif //ENABLE_SCRIPTING
+#endif
diff --git a/Runtime/Mono/MonoAttributeHelpers.cpp b/Runtime/Mono/MonoAttributeHelpers.cpp
new file mode 100644
index 0000000..cde1054
--- /dev/null
+++ b/Runtime/Mono/MonoAttributeHelpers.cpp
@@ -0,0 +1,191 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h" // include before anything, to get Prof_ENABLED if that is defined
+#if UNITY_EDITOR && ENABLE_MONO
+#include "MonoManager.h"
+#include "MonoAttributeHelpers.h"
+#include <stdlib.h>
+
+using namespace std;
+
+
+static string RemoveStringFromString (string str, string remove)
+{
+ while (true)
+ {
+ int pos = str.find (remove);
+ if (pos == string::npos)
+ break;
+ str.erase (pos, remove.size());
+ }
+ return str;
+}
+
+static std::string SignatureToString (MonoMethodSignature* signature)
+{
+ MonoType* methodType;
+ void* iter = NULL;
+ std::string parameters;
+ while ((methodType = mono_signature_get_params (signature, &iter)))
+ {
+ if (!parameters.empty())
+ parameters += ", ";
+ parameters += mono_type_get_name(methodType);
+ }
+
+ string returnType = mono_type_get_name( mono_signature_get_return_type (signature) );
+ string result = Format ("%s Func(%s)", returnType.c_str(), parameters.c_str());
+ return RemoveStringFromString (result, "System.");
+}
+
+
+
+void GetMethodsWithAttribute (ScriptingClass* attributeClass, MonoMethod* comparedParams, std::vector<MonoMethod*>& resultList)
+{
+ MonoMethodSignature* pCompareSig = NULL;
+ if (comparedParams)
+ pCompareSig = mono_method_signature( comparedParams );
+
+ for (int i = MonoManager::kEngineAssembly; i < GetMonoManager().GetAssemblyCount(); i++)
+ {
+ MonoAssembly* assembly = GetMonoManager().GetAssembly(i);
+ if (!assembly)
+ continue;
+
+ MonoImage* pMonoImage = mono_assembly_get_image( assembly );
+ if (!pMonoImage)
+ continue;
+
+ // stolen from #include...
+ const int MONO_TABLE_TYPEDEF = 2; // mono/metadata/blob.h
+ const int MONO_TOKEN_TYPE_DEF = 0x02000000; // mono/metadata/tokentype.h
+
+ int numFields = mono_image_get_table_rows( pMonoImage, MONO_TABLE_TYPEDEF );
+ for (int typeIdx = 1; typeIdx < numFields; ++typeIdx)
+ {
+ guint32 token = MONO_TOKEN_TYPE_DEF | (typeIdx + 1);
+ MonoClass* klass = mono_class_get (pMonoImage, token);
+ gpointer iter = NULL;
+ MonoMethod* method;
+
+ if (klass == NULL) {
+ // We need to call these two methods to clear the thread local
+ // loader error status in mono. If not we'll randomly process the error
+ // the next time it's checked.
+ void* last_error = mono_loader_get_last_error ();
+ mono_loader_error_prepare_exception (last_error);
+ continue;
+ }
+
+ while ((method = mono_class_get_methods(klass, &iter)))
+ {
+ MonoMethodSignature* sig = mono_method_signature (method);
+
+ if (!sig)
+ continue;
+
+ MonoCustomAttrInfo* attribInfo = mono_custom_attrs_from_method(method);
+ if (!attribInfo)
+ continue;
+
+ if (!mono_custom_attrs_has_attr (attribInfo, attributeClass))
+ {
+ mono_custom_attrs_free(attribInfo);
+ continue;
+ }
+
+ // Get static methods only
+ bool isInstanceMethod = mono_signature_is_instance (sig);
+ if (isInstanceMethod)
+ {
+ mono_custom_attrs_free(attribInfo);
+ ErrorString (Format("UnityException: An [%s] attributed method is not static", mono_class_get_name(attributeClass)));
+ continue;
+ }
+
+ if (pCompareSig)
+ {
+ if (!mono_metadata_signature_equal(sig, pCompareSig))
+ {
+ mono_custom_attrs_free(attribInfo);
+ ErrorString (Format("UnityException: An [%s] attributed method has incorrect signature: %s. Correct signature: %s", mono_class_get_name(attributeClass), SignatureToString(sig).c_str(), SignatureToString(pCompareSig).c_str()));
+ continue;
+ }
+ }
+ else
+ {
+ if (mono_signature_get_param_count (sig) > 0)
+ {
+ mono_custom_attrs_free(attribInfo);
+ ErrorString (Format("UnityException: An [%s] attributed method parameter count should be 0 but signature is: %s", mono_class_get_name(attributeClass), SignatureToString(sig).c_str()));
+ continue;
+ }
+ }
+
+ resultList.push_back(method);
+ mono_custom_attrs_free(attribInfo);
+ }
+ }
+ }
+}
+
+bool AttributeSorter( MonoMethod* methodA, MonoMethod* methodB )
+{
+#if UNITY_EDITOR
+ MonoCustomAttrInfo* attribAInfo = mono_custom_attrs_from_method(methodA);
+ MonoCustomAttrInfo* attribBInfo = mono_custom_attrs_from_method(methodB);
+ MonoObject* objA = mono_custom_attrs_get_attr( attribAInfo, MONO_COMMON.callbackOrderAttribute );
+ MonoObject* objB = mono_custom_attrs_get_attr( attribBInfo, MONO_COMMON.callbackOrderAttribute );
+ MonoClass* klassA = mono_object_get_class(objA);
+ MonoClass* klassB = mono_object_get_class(objB);
+ MonoProperty* propertyA = mono_class_get_property_from_name(klassA, "callbackOrder");
+ MonoProperty* propertyB = mono_class_get_property_from_name(klassB, "callbackOrder");
+ MonoMethod* getterA = mono_property_get_get_method(propertyA);
+ MonoMethod* getterB = mono_property_get_get_method(propertyB);
+ MonoException* monoException = NULL;
+ MonoObject* resA = mono_runtime_invoke(getterA, objA, NULL, &monoException);
+ MonoObject* resB = mono_runtime_invoke(getterB, objB, NULL, &monoException);
+ int x = ExtractMonoObjectData<signed int>(resA);
+ int y = ExtractMonoObjectData<signed int>(resB);
+
+ return x < y;
+#else
+ return false;
+#endif
+}
+
+void CallMethodsWithAttribute (ScriptingClass* attributeClass, ScriptingArguments& arguments, MonoMethod* comparedParams)
+{
+ vector<MonoMethod*> resultList;
+
+ GetMethodsWithAttribute(attributeClass, comparedParams, resultList);
+ sort (resultList.begin(), resultList.end(), AttributeSorter);
+
+ for (vector<MonoMethod*>::iterator iter = resultList.begin(); iter != resultList.end(); ++iter)
+ {
+ ScriptingInvocation invocation(*iter);
+ invocation.Arguments() = arguments;
+ invocation.Invoke();
+ }
+}
+
+bool CallMethodsWithAttributeAndReturnTrueIfUsed (ScriptingClass* attributeClass, ScriptingArguments& arguments, MonoMethod* comparedParams)
+{
+ vector<MonoMethod*> resultList;
+
+ GetMethodsWithAttribute(attributeClass, comparedParams, resultList);
+ sort (resultList.begin(), resultList.end(), AttributeSorter);
+
+ for (vector<MonoMethod*>::iterator iter = resultList.begin(); iter != resultList.end(); ++iter)
+ {
+ ScriptingInvocation invocation(*iter);
+ invocation.Arguments() = arguments;
+ ScriptingObjectPtr returnValueObj = invocation.Invoke();
+ if (ExtractMonoObjectData<bool>(returnValueObj))
+ return true;
+ }
+
+ return false;
+}
+
+
+#endif //UNITY_EDITOR && ENABLE_MONO
diff --git a/Runtime/Mono/MonoAttributeHelpers.h b/Runtime/Mono/MonoAttributeHelpers.h
new file mode 100644
index 0000000..cc2b8c8
--- /dev/null
+++ b/Runtime/Mono/MonoAttributeHelpers.h
@@ -0,0 +1,19 @@
+#ifndef MONOATTRIBUTEHELPERS_H
+#define MONOATTRIBUTEHELPERS_H
+
+#include "MonoIncludes.h"
+#include "Configuration/UnityConfigure.h"
+#if UNITY_EDITOR && ENABLE_MONO
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include <vector>
+
+struct ScriptingArguments;
+
+//void GetMethodsWithAttribute (ScriptingClass* attributeClass, void** requiredParameters, MonoMethod* comparedParams, std::vector<MonoMethod*>& resultList);
+void CallMethodsWithAttribute (ScriptingClass* attributeClass, ScriptingArguments& arguments, MonoMethod* comparedParams);
+bool CallMethodsWithAttributeAndReturnTrueIfUsed (ScriptingClass* attributeClass, ScriptingArguments& arguments, MonoMethod* comparedParams);
+
+#endif //UNITY_EDITOR && ENABLE_MONO
+
+#endif //MONOATTRIBUTEHELPERS_H
diff --git a/Runtime/Mono/MonoBehaviour.cpp b/Runtime/Mono/MonoBehaviour.cpp
new file mode 100644
index 0000000..3c79901
--- /dev/null
+++ b/Runtime/Mono/MonoBehaviour.cpp
@@ -0,0 +1,1894 @@
+#include "UnityPrefix.h"
+#if ENABLE_SCRIPTING
+#include "MonoBehaviour.h"
+#include "MonoBehaviourAnimationBinding.h"
+#include "Runtime/Mono/Coroutine.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "Runtime/Serialize/FileCache.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+#include "MonoScript.h"
+#include "MonoScriptCache.h"
+#include "MonoTypeSignatures.h"
+#include "MonoManager.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/BaseClasses/MessageIdentifiers.h"
+#include "Runtime/GameCode/CallDelayed.h"
+#include "Runtime/BaseClasses/MessageHandler.h"
+#include "tabledefs.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/Serialize/IterateTypeTree.h"
+#include "Runtime/BaseClasses/SupportedMessageOptimization.h"
+#include "Runtime/Misc/MessageParameters.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Camera/Renderable.h"
+#include "Runtime/Math/AnimationCurve.h"
+#include "Runtime/Misc/AsyncOperation.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Threads/ThreadSpecificValue.h"
+#include "Runtime/Audio/AudioCustomFilter.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Scripting/Backend/ScriptingArguments.h"
+#include "Runtime/Scripting/Backend/ScriptingMethodRegistry.h"
+#include "Runtime/Physics2D/CollisionListener2D.h"
+#include "Runtime/Interfaces/IPhysics.h"
+
+#if ENABLE_WWW
+#include "Runtime/Export/WWW.h"
+#endif
+#include "Runtime/Serialize/TransferUtility.h"
+#include "Runtime/BaseClasses/RefCounted.h"
+#include "Runtime/Misc/InputEvent.h"
+#include "Runtime/IMGUI/GUIManager.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+#if UNITY_EDITOR
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Editor/Src/Gizmos/GizmoRenderer.h"
+#include "Editor/Src/Gizmos/GizmoUtil.h"
+#include "Editor/Src/Application.h"
+#include "Editor/Src/AssetPipeline/LogicGraphCompilationPipeline.h"
+#endif
+
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Scripting/Scripting.h"
+
+#include "Runtime/Interfaces/IAudio.h"
+
+IMPLEMENT_CLASS_HAS_INIT (MonoBehaviour)
+
+using namespace std;
+#define DEBUG_COROUTINE 0
+#define DEBUG_COROUTINE_LEAK 0
+#if DEBUG_COROUTINE_LEAK
+int gCoroutineCounter = 0;
+#endif
+
+
+PROFILER_INFORMATION(gMonoImageFxProfile, "Camera.ImageEffect", kProfilerRender);
+
+
+bool IsInstanceValid (ScriptingObjectPtr target);
+
+#if UNITY_EDITOR
+BackupState::BackupState ()
+: yamlState(NULL)
+, inYamlFormat (false)
+, loadedFromDisk (false)
+{}
+
+BackupState::~BackupState ()
+{
+ delete yamlState;
+}
+
+#endif
+
+MonoBehaviour::MonoBehaviour (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_UpdateNode(this)
+, m_FixedUpdateNode(this)
+, m_LateUpdateNode(this)
+, m_GUINode(this)
+, m_OnRenderObjectNode(this)
+{
+ m_Methods = NULL;
+ m_ScriptCache = NULL;
+ m_DidAwake = m_DidStart = m_IsDestroying = false;
+ m_UseGUILayout = true;
+ m_GUIState = NULL;
+ #if UNITY_EDITOR
+ m_EditorHideFlags = 0;
+ m_Backup = NULL;
+ #endif
+ #if ENABLE_AUDIO_FMOD
+ m_AudioCustomFilter = NULL;
+ #endif
+}
+
+MonoBehaviour::~MonoBehaviour()
+{
+ Assert(m_ActiveCoroutines.empty());
+
+ LockObjectCreation();
+ ReleaseMonoInstance ();
+ StopAllCoroutines ();
+ UnlockObjectCreation();
+
+#if UNITY_EDITOR
+ delete m_Backup;
+#endif
+#if ENABLE_UNITYGUI
+ delete m_GUIState;
+#endif
+
+#if ENABLE_AUDIO_FMOD
+ delete m_AudioCustomFilter;
+#endif
+}
+
+bool MonoBehaviour::IsScriptableObject()
+{
+ if (m_ScriptCache != NULL)
+ return m_ScriptCache->scriptType == kScriptTypeScriptableObjectDerived || m_ScriptCache->scriptType == kScriptTypeEditorScriptableObjectDerived;
+ else
+ return false;
+}
+
+#if ENABLE_UNITYGUI
+ObjectGUIState& MonoBehaviour::GetObjectGUIState()
+{
+ if (m_GUIState)
+ return *m_GUIState;
+ else
+ {
+ m_GUIState = new ObjectGUIState ();
+ return *m_GUIState;
+ }
+}
+#endif
+
+bool MonoBehaviour::IsPlayingOrAllowExecuteInEditMode() const
+{
+ #if UNITY_EDITOR
+ return IsWorldPlaying () || GetRunInEditMode();
+ #else
+ return true;
+ #endif
+}
+
+bool MonoBehaviour::WillUnloadScriptableObject ()
+{
+ MonoScript* script = m_Script;
+ if (GetInstance() == SCRIPTING_NULL || script == NULL)
+ return true;
+
+ ///@TODO: SHould this check for m_DidAwake? Can this happen in any way?
+
+ ScriptingObjectPtr instance = GetInstance();
+ if (IsScriptableObject())
+ {
+ ScriptingMethodPtr method = m_Methods[MonoScriptCache::kRemoveFromManager];
+ if (method != SCRIPTING_NULL)
+ CallMethodInactive(method);
+
+ if (IsInstanceValid(instance))
+ {
+ method = m_Methods[MonoScriptCache::kRemoveFromManagerInternal];
+ if (method != SCRIPTING_NULL)
+ CallMethodInactive(method);
+ }
+ }
+ return IsInstanceValid(instance);
+}
+
+
+// IsInstanceValid is used to determine if the C# side destroyed the object during the script callback using DestroyImmediate.
+// For example Awake and OnEnable are called from the same C++ code. If the object is destroyed by Awake, we must not call OnEnable and not touch MonoBehaviour member variables.
+bool IsInstanceValid (ScriptingObjectPtr target)
+{
+ if (target == SCRIPTING_NULL)
+ return false;
+ ScriptingObjectOfType<Object> wrapper(target);
+ return wrapper.GetCachedPtr() != NULL;
+}
+
+void MonoBehaviour::DeprecatedAddToManager ()
+{
+ ScriptingObjectPtr instance = GetInstance();
+ if (instance && IsPlayingOrAllowExecuteInEditMode ())
+ {
+ MonoScript* script = m_Script;
+ int executionOrder = script ? script->GetExecutionOrder() : 0;
+ ///Try removing this
+ if ((IsInstanceValid (instance) && m_Methods[MonoScriptCache::kCoroutineStart]) || m_Methods[MonoScriptCache::kCoroutineMain])
+ CallDelayed (DelayedStartCall, this, -10, NULL, 0.0F, NULL, DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kRunFixedFrameRate | DelayedCallManager::kRunStartupFrame);
+ if (IsInstanceValid (instance) && m_Methods[MonoScriptCache::kUpdate])
+ GetBehaviourManager().AddBehaviour (m_UpdateNode, executionOrder);
+ if (IsInstanceValid (instance) && m_Methods[MonoScriptCache::kFixedUpdate])
+ GetFixedBehaviourManager().AddBehaviour (m_FixedUpdateNode, executionOrder);
+ if (IsInstanceValid (instance) && m_Methods[MonoScriptCache::kLateUpdate])
+ GetLateBehaviourManager().AddBehaviour (m_LateUpdateNode, executionOrder);
+
+ if (IsInstanceValid (instance) && m_Methods[MonoScriptCache::kRenderObject])
+ GetRenderManager().AddOnRenderObject (m_OnRenderObjectNode);
+
+ if (IsInstanceValid (instance) && m_Methods[MonoScriptCache::kAddToManager])
+ {
+ // @TODO: This is only for backwards compatiblity
+ SetupAwake ();
+
+ // If we got disabled or destroyed from a call to Awake, do not proceed calling OnEnable and so on.
+ // Just quit, we are removed from all managers by a call to RemoveFromManager already.
+ if( !IsInstanceValid (instance) || !GetEnabled() )
+ return;
+ CallMethodIfAvailable (MonoScriptCache::kAddToManager);
+ }
+
+#if ENABLE_IMAGEEFFECTS
+ if (IsInstanceValid (instance) && m_Methods[MonoScriptCache::kRenderImageFilter])
+ {
+ Camera *camera = QueryComponent (Camera);
+ if (camera)
+ {
+ bool afterOpaque = scripting_method_has_attribute(m_Methods[MonoScriptCache::kRenderImageFilter], MONO_COMMON.imageEffectOpaque);
+ bool transformsToLDR = scripting_method_has_attribute(m_Methods[MonoScriptCache::kRenderImageFilter], MONO_COMMON.imageEffectTransformsToLDR);
+
+ ImageFilter filter (this, &RenderImageFilter, transformsToLDR, afterOpaque);
+ camera->AddImageFilter (filter);
+ }
+ }
+#endif
+
+#if ENABLE_UNITYGUI
+ if (IsInstanceValid (instance) && m_Methods[MonoScriptCache::kGUI])
+ GetGUIManager().AddGUIScript (m_GUINode);
+#endif
+
+ if ( IsInstanceValid (instance) )
+ SetByPassOnDSP(false);
+ }
+}
+
+void MonoBehaviour::WillDestroyComponent ()
+{
+ Super::WillDestroyComponent();
+
+ if (m_IsDestroying)
+ {
+ ErrorString("DestroyImmediate should not be called on the same game object when destroying a MonoBehaviour");
+ return;
+ }
+
+ m_IsDestroying = true;
+ ScriptingMethodPtr method;
+
+ ScriptingObjectPtr instance = GetInstance();
+ if (instance && m_DidAwake)
+ {
+ // OnDisable must be called for scriptable objects since we never get a Deactivate call there.
+ if (IsScriptableObject())
+ {
+ method = m_Methods[MonoScriptCache::kRemoveFromManager];
+ if (method)
+ CallMethodInactive (method);
+
+ if (IsInstanceValid(instance))
+ {
+ method = m_Methods[MonoScriptCache::kRemoveFromManagerInternal];
+ if (method)
+ CallMethodInactive (method);
+ }
+ }
+
+ // OnDestroy
+ if (IsInstanceValid(instance))
+ {
+ method = m_Methods[MonoScriptCache::kOnDestroy];
+ if (method != SCRIPTING_NULL)
+ CallMethodInactive(method);
+ }
+ }
+}
+
+
+void MonoBehaviour::RemoveFromManager ()
+{
+ m_UpdateNode.RemoveFromList();
+ m_FixedUpdateNode.RemoveFromList();
+ m_LateUpdateNode.RemoveFromList();
+ m_GUINode.RemoveFromList();
+ m_OnRenderObjectNode.RemoveFromList();
+
+#if ENABLE_IMAGEEFFECTS
+ if (GetInstance() && m_Methods[MonoScriptCache::kRenderImageFilter])
+ {
+ Camera *camera = QueryComponent (Camera);
+ if (camera)
+ {
+ ImageFilter filter (this, &RenderImageFilter, false, false);
+ camera->RemoveImageFilter (filter);
+ }
+ }
+#endif
+
+ if (IsPlayingOrAllowExecuteInEditMode() && GetCachedScriptingObject())
+ {
+ ScriptingObjectPtr instance = GetInstance();
+
+ if (IsInstanceValid(instance) && m_Methods[MonoScriptCache::kRemoveFromManager] && m_DidAwake)
+ CallMethodInactive(m_Methods[MonoScriptCache::kRemoveFromManager]);
+ if (IsInstanceValid(instance) && m_Methods[MonoScriptCache::kRemoveFromManagerInternal] && m_DidAwake)
+ CallMethodInactive(m_Methods[MonoScriptCache::kRemoveFromManagerInternal]);
+ //@TODO: Try removing this
+ if (IsInstanceValid(instance) && (m_Methods[MonoScriptCache::kCoroutineStart] || m_Methods[MonoScriptCache::kCoroutineMain]))
+ GetDelayedCallManager().CancelCallDelayed (this, DelayedStartCall, NULL, NULL);
+
+ if ( IsInstanceValid(instance) )
+ SetByPassOnDSP(true);
+ }
+}
+
+bool MonoBehaviour::CallMethodInactive (ScriptingMethodPtr method)
+{
+ AssertIf (GetInstance() == SCRIPTING_NULL);
+ AssertIf (method == SCRIPTING_NULL);
+
+ ScriptingObjectPtr instance = GetInstance();
+ ScriptingInvocation invocation(method);
+ invocation.object = instance;
+ invocation.logException = true;
+ invocation.objectInstanceIDContextForException = Scripting::GetInstanceIDFromScriptingWrapper(instance);
+ invocation.AdjustArgumentsToMatchMethod();
+ invocation.InvokeChecked();
+ return (NULL == invocation.exception);
+}
+
+bool MonoBehaviour::CallMethodInactive (const char* methodName)
+{
+ ScriptingMethodPtr method = GetScriptingMethodRegistry().GetMethod(GetClass(),methodName,ScriptingMethodRegistry::kWithoutArguments);
+
+ if (!method)
+ return false;
+
+ CallMethodInactive (method);
+
+ return true;
+}
+
+bool MonoBehaviour::DoGUI (MonoBehaviour::GUILayoutType layoutType, int skin)
+{
+ if (GetInstance () == SCRIPTING_NULL)
+ return false;
+ ScriptingMethodPtr method = GetMethod (MonoScriptCache::kGUI);
+ if (method == SCRIPTING_NULL)
+ return false;
+ Start ();
+
+ PPtr<MonoBehaviour> behaviourPPtr = this;
+ ScriptingObjectPtr monoObject = GetInstance ();
+ int instanceID = GetInstanceID();
+ GUIState &guiState = GetGUIState ();
+ guiState.m_CanvasGUIState.m_GUIClipState.BeginOnGUI (*guiState.m_CurrentEvent);
+
+ guiState.BeginOnGUI(GetObjectGUIState());
+
+ int allowGUILayoutParam = layoutType;
+
+ ScriptingInvocation invocation;
+ invocation.AddInt(skin);
+ invocation.AddInt(instanceID);
+ invocation.AddInt(allowGUILayoutParam);
+ invocation.method = MONO_COMMON.beginGUI;
+ invocation.Invoke();
+
+#if UNITY_EDITOR
+ ScriptingInvocation invocation2;
+ invocation2.method = MONO_COMMON.clearUndoSnapshotTarget;
+ invocation2.Invoke();
+#endif
+
+ ScriptingExceptionPtr exception = NULL;
+ ScriptingInvocationNoArgs userOnGUIInvocation;
+ userOnGUIInvocation.method = method;
+ userOnGUIInvocation.object = monoObject;
+ userOnGUIInvocation.logException = false;
+ userOnGUIInvocation.Invoke(&exception);
+
+ // Display exception (except for ExitGUIException which exists only to successfully exit out of a GUI loop)
+ if (exception)
+ {
+#if ENABLE_MONO
+ void* excparams[] = {exception};
+ MonoObject* res = CallStaticMonoMethod("GUIUtility", "EndGUIFromException", excparams);
+ guiState.m_CanvasGUIState.m_GUIClipState.EndThroughException();
+ if (MonoObjectToBool(res))
+ {
+ guiState.EndOnGUI ();
+ return guiState.m_CurrentEvent->type == InputEvent::kUsed;
+ }
+ else
+ {
+ guiState.EndOnGUI ();
+ ::Scripting::LogException(exception, behaviourPPtr.GetInstanceID());
+ return false;
+ }
+#endif
+ }
+ else
+ {
+ // Mono is not happy about receiving bools from internal calls,
+ // especially since bools are not exactly specified on how many bytes they contain on the C++ side
+
+ ScriptingInvocation endGUIInvocation;
+ endGUIInvocation.method = MONO_COMMON.endGUI;
+ endGUIInvocation.AddInt((int)layoutType);
+ endGUIInvocation.Invoke();
+
+ guiState.EndOnGUI ();
+ guiState.m_CanvasGUIState.m_GUIClipState.EndOnGUI (*guiState.m_CurrentEvent);
+ }
+ return guiState.m_CurrentEvent->type == InputEvent::kUsed;
+}
+
+#if 0
+// IM GUI in retained GUI callbacks. not used right now.
+bool MonoBehaviour::DoCustomGUI (Rectf &position, const ColorRGBAf &guiColor)
+{
+ MonoObject *instance = GetInstance ();
+ MonoMethod* method = GetMethod(MonoScriptCache::kCustomGUI);
+ if (instance == NULL || method == NULL)
+ return false;
+ Start ();
+
+ GUIState &guiState = GetGUIState ();
+ guiState.SetObjectGUIState (GetObjectGUIState());
+ guiState.BeginOnGUI();
+ guiState.m_OnGUIState.m_Color = guiColor;
+
+ MonoException* exception;
+
+ void* params[] = { &position };
+ bool retval = mono_runtime_invoke_profiled(method, instance, params, &exception);
+
+ // TODO: Handle GUI exceptions
+ AssertIf (exception);
+
+ guiState.EndOnGUI ();
+ return retval;
+}
+#endif
+
+void MonoBehaviour::SetUseGUILayout (bool use)
+{
+ m_UseGUILayout = use;
+}
+
+bool MonoBehaviour::GetUseGUILayout ()
+{
+ return m_UseGUILayout;
+}
+
+#if ENABLE_AUDIO_FMOD
+
+FMOD::DSP* MonoBehaviour::GetOrCreateDSP()
+{
+ IAudio* audio = GetIAudio();
+ if (!audio)
+ return NULL;
+
+ if (!m_AudioCustomFilter)
+ {
+ if (m_Methods && m_Methods[MonoScriptCache::kAudioFilterRead] && IsActive())
+ m_AudioCustomFilter = audio->CreateAudioCustomFilter(this);
+ else
+ return NULL;
+}
+
+ return audio->GetOrCreateDSPFromCustomFilter(m_AudioCustomFilter);
+}
+
+FMOD::DSP* MonoBehaviour::GetDSP() const
+{
+ IAudio* audio = GetIAudio();
+ if (!audio)
+ return NULL;
+
+ if (m_AudioCustomFilter)
+ return audio->GetDSPFromAudioCustomFilter(m_AudioCustomFilter);
+ else
+ return NULL;
+}
+#endif
+
+bool MonoBehaviour::HaveAudioCallback() const
+{
+ return m_Methods ? m_Methods[MonoScriptCache::kAudioFilterRead] != SCRIPTING_NULL : false;
+}
+
+inline void MonoBehaviour::CallMethodIfAvailable (int methodIndex)
+{
+ AssertIf (methodIndex < 0 || methodIndex >= MonoScriptCache::kMethodCount);
+ ScriptingMethodPtr method = m_Methods[methodIndex];
+ if (method == SCRIPTING_NULL)
+ {
+ return;
+ }
+
+ AssertIf (GetInstance() == SCRIPTING_NULL);
+ AssertIf (!m_DidAwake);
+
+ if (!IsActive ())
+ return;
+
+ ScriptingInvocationNoArgs invocation(method);
+ invocation.objectInstanceIDContextForException = GetInstanceID();
+ invocation.object = GetInstance();
+ invocation.Invoke();
+}
+
+inline void MonoBehaviour::Start ()
+{
+ DebugAssertIf (!m_DidAwake);
+
+
+ #if UNITY_EDITOR
+ AssertIf (!IsActive () && !GetRunInEditMode());
+ #else
+ AssertIf (!IsActive ());
+ #endif
+
+ if (m_DidStart)
+ return;
+
+ AssertIf (GetInstance() == SCRIPTING_NULL);
+
+ m_DidStart = true;
+
+ ScriptingMethodPtr method;
+ method = m_Methods[MonoScriptCache::kCoroutineMain];
+
+ if (method)
+ InvokeMethodOrCoroutineChecked (method, SCRIPTING_NULL);
+
+ method = m_Methods[MonoScriptCache::kCoroutineStart];
+ if (method)
+ InvokeMethodOrCoroutineChecked (method, SCRIPTING_NULL);
+}
+
+
+#if UNITY_EDITOR
+void MonoBehaviour::RestartExecuteInEditModeScripts ()
+{
+ std::vector<SInt32> behaviours;
+ Object::FindAllDerivedObjects (ClassID(MonoBehaviour), &behaviours);
+
+ for (std::vector<SInt32>::iterator it = behaviours.begin(), itEnd = behaviours.end(); it != itEnd; ++it)
+ {
+ MonoBehaviour* beh = dynamic_pptr_cast<MonoBehaviour*> (Object::IDToPointer(*it));
+ if (beh == NULL || !beh->GetRunInEditMode() || !beh->IsActive() || !beh->GetEnabled())
+ continue;
+
+ // disable and enable the script (careful to not call SetDirty)
+ beh->SetEnabledNoDirty (false);
+ beh->SetEnabledNoDirty (true);
+ // and Start it again
+ beh->m_DidStart = false;
+ beh->Start ();
+ }
+}
+
+bool MonoBehaviour::GetRunInEditMode () const
+{
+ if (m_ScriptCache)
+ return m_ScriptCache->runInEditMode;
+ else
+ return false;
+}
+
+#endif
+
+
+void MonoBehaviour::CallUpdateMethod(int methodIndex)
+{
+ AssertIf (!IsPlayingOrAllowExecuteInEditMode ());
+ AssertIf (!IsActive ());
+ AssertIf (!GetEnabled ());
+ ScriptingObjectPtr instance = GetInstance();
+
+ if (instance == SCRIPTING_NULL)
+ return;
+ // Ensure Start has been called
+ Start ();
+
+ // We might be getting destroyed in Start
+ if (!IsInstanceValid(instance))
+ return;
+
+ // Call Update
+ CallMethodIfAvailable (methodIndex);
+}
+
+void MonoBehaviour::Update ()
+{
+ CallUpdateMethod (MonoScriptCache::kUpdate);
+}
+
+void MonoBehaviour::LateUpdate ()
+{
+ CallUpdateMethod (MonoScriptCache::kLateUpdate);
+}
+
+void MonoBehaviour::FixedUpdate ()
+{
+ CallUpdateMethod (MonoScriptCache::kFixedUpdate);
+}
+
+ScriptingMethodPtr MonoBehaviour::FindMethod (const char* name)
+{
+ if (GetInstance() == SCRIPTING_NULL)
+ return SCRIPTING_NULL;
+
+ const MethodCache* cache = GetMethodCache();
+
+ MethodCache::const_iterator i = cache->find (name);
+ if (i != cache->end ())
+ return i->second;
+ else
+ return SCRIPTING_NULL;
+}
+
+
+#if ENABLE_MONO || UNITY_WINRT
+void MonoBehaviour::InvokeOnRenderObject ()
+{
+ DebugAssertIf (!IsPlayingOrAllowExecuteInEditMode());
+ AssertIf (!GetEnabled ());
+ AssertIf (!IsActive ());
+ if (GetInstance())
+ {
+ Start ();
+ CallMethodIfAvailable (MonoScriptCache::kRenderObject);
+ }
+}
+#endif //ENABLE_MONO || UNITY_WINRT
+
+#if ENABLE_IMAGEEFFECTS
+void MonoBehaviour::RenderImageFilter (Unity::Component* component, RenderTexture *source, RenderTexture *destination)
+{
+ MonoBehaviour* self = static_cast<MonoBehaviour*>(component);
+
+ AssertIf (!self->GetEnabled ());
+ AssertIf (!self->IsActive ());
+ if (self->IsPlayingOrAllowExecuteInEditMode () && self->GetInstance())
+ {
+ self->Start ();
+
+ ScriptingMethodPtr method = self->m_Methods[MonoScriptCache::kRenderImageFilter];
+ // Early out message not supported
+ if (method == SCRIPTING_NULL)
+ return;
+
+ PROFILER_AUTO_GFX(gMonoImageFxProfile, self)
+
+ ScriptingObjectPtr instance = self->GetInstance();
+ ScriptingInvocation invocation(method);
+ invocation.object = instance;
+ invocation.AddObject(Scripting::ScriptingWrapperFor (source));
+ invocation.AddObject(Scripting::ScriptingWrapperFor (destination));
+ invocation.objectInstanceIDContextForException = self->GetInstanceID();
+ invocation.Invoke();
+ }
+}
+#endif // ENABLE_IMAGEEFFECTS
+
+
+
+#if ENABLE_MONO
+static MonoObject* ConvertBuiltinMonoValue( MonoObject* value, int wantedType )
+{
+ #define SUPPORT_CONVERSION(srctype,dsttype,srccode,dstclass) \
+ case srccode: \
+ res = mono_object_new( domain, classes.dstclass ); \
+ ExtractMonoObjectData<dsttype>(res) = static_cast<dsttype>( ExtractMonoObjectData<srctype>(value) ); \
+ return res
+
+ MonoClass* valueClass = mono_object_get_class (value);
+ int type = mono_type_get_type( mono_class_get_type (valueClass) );
+ // types match exactly - return input object
+ if( type == wantedType )
+ return value;
+
+ MonoDomain* domain = mono_domain_get();
+ const CommonScriptingClasses& classes = GetMonoManager().GetCommonClasses();
+ MonoObject* res;
+
+ if( wantedType == MONO_TYPE_I4 )
+ {
+ switch( type ) {
+ SUPPORT_CONVERSION(float, int,MONO_TYPE_R4,int_32);
+ SUPPORT_CONVERSION(double,int,MONO_TYPE_R8,int_32);
+ }
+ }
+ else if( wantedType == MONO_TYPE_R4 )
+ {
+ switch( type ) {
+ SUPPORT_CONVERSION(int, float,MONO_TYPE_I4,floatSingle);
+ SUPPORT_CONVERSION(double,float,MONO_TYPE_R8,floatSingle);
+ }
+ }
+ else if( wantedType == MONO_TYPE_R8 )
+ {
+ switch( type ) {
+ SUPPORT_CONVERSION(int, double,MONO_TYPE_I4,floatDouble);
+ SUPPORT_CONVERSION(float,double,MONO_TYPE_R4,floatDouble);
+ }
+ }
+
+ #undef SUPPORT_CONVERSION
+
+ // can't convert
+ return NULL;
+}
+#endif //ENABLE_MONO
+
+
+static bool CompareCoroutine (void* callBackUserData, void* cancelUserdata)
+{
+ return callBackUserData == cancelUserdata;
+}
+
+Coroutine* MonoBehaviour::CreateCoroutine(ScriptingObjectPtr userCoroutine, ScriptingMethodPtr method)
+{
+ ScriptingMethodPtr moveNext = scripting_object_get_virtual_method(userCoroutine, MONO_COMMON.IEnumerator_MoveNext, GetScriptingMethodRegistry());
+
+#if !UNITY_FLASH
+ ScriptingMethodPtr current = scripting_object_get_virtual_method(userCoroutine, MONO_COMMON.IEnumerator_Current, GetScriptingMethodRegistry());
+#else
+ //todo: make flash use generic path. set a bogus value for flash here right now so it passes current != NULL check, flash path will never use this value for now.
+ ScriptingMethodPtr current = (ScriptingMethodPtr)1;
+#endif
+
+ if (current == SCRIPTING_NULL || moveNext == SCRIPTING_NULL)
+ {
+ std::string message = (method != SCRIPTING_NULL) ? Format ("Coroutine '%s' couldn't be started!", scripting_method_get_name(method)) : "Coroutine couldn't be started!";
+ LogStringObject (message, this);
+ return NULL;
+ }
+
+ Coroutine* coroutine = new Coroutine ();
+
+ coroutine->m_CoroutineEnumeratorGCHandle = scripting_gchandle_new (userCoroutine);
+ coroutine->m_CoroutineEnumerator = userCoroutine;
+ coroutine->m_CoroutineMethod = method;
+ coroutine->SetMoveNextMethod(moveNext);
+ coroutine->SetCurrentMethod(current);
+ coroutine->m_Behaviour = this;
+ coroutine->m_ContinueWhenFinished = NULL;
+ coroutine->m_WaitingFor = NULL;
+ coroutine->m_AsyncOperation = NULL;
+ coroutine->m_RefCount = 1;
+ coroutine->m_IsReferencedByMono = 0;
+ #if DEBUG_COROUTINE
+ printf_console ("Allocate coroutine %d\n", coroutine);
+ AssertIf(GetDelayedCallManager().HasDelayedCall(coroutine->m_Behaviour, Coroutine::ContinueCoroutine, CompareCoroutine, coroutine));
+ #endif
+
+ #if DEBUG_COROUTINE_LEAK
+ printf_console ("Active coroutines %d\n", gCoroutineCounter);
+ gCoroutineCounter++;
+ #endif
+
+ m_ActiveCoroutines.push_back (*coroutine);
+ AssertIf(&m_ActiveCoroutines.back() != coroutine);
+ m_ActiveCoroutines.back ().Run ();
+
+ AssertIf(coroutine->m_RefCount == 0);
+ if (coroutine->m_RefCount <= 1)
+ {
+ Coroutine::CleanupCoroutine(coroutine);
+ return NULL;
+ }
+
+ Coroutine::CleanupCoroutine(coroutine);
+ return coroutine;
+}
+
+
+static ScriptingObjectPtr CreateManagedWrapperForCoroutine(Coroutine* coroutine)
+{
+ if (coroutine == NULL) return SCRIPTING_NULL;
+ Assert(!coroutine->m_IsReferencedByMono);
+ coroutine->m_IsReferencedByMono = true;
+ ScriptingObjectWithIntPtrField<Coroutine> wrapper = scripting_object_new(GetMonoManager ().GetCommonClasses ().coroutine);
+ wrapper.SetPtr(coroutine, Coroutine::CleanupCoroutineGC);
+ return wrapper.object;
+}
+
+ScriptingObjectPtr MonoBehaviour::StartCoroutineManaged (const char* name, ScriptingObjectPtr value)
+{
+ Coroutine* coroutine = StartCoroutine (name, value);
+ return CreateManagedWrapperForCoroutine(coroutine);
+}
+
+ScriptingObjectPtr MonoBehaviour::StartCoroutineManaged2 (ScriptingObjectPtr enumerator)
+{
+ if (!IsActive ())
+ {
+ ErrorStringObject (Format ("Coroutine couldn't be started because the the game object '%s' is inactive!", GetName()), this);
+ return SCRIPTING_NULL;
+ }
+
+ Coroutine* coroutine = CreateCoroutine(enumerator, SCRIPTING_NULL);
+ return CreateManagedWrapperForCoroutine(coroutine);
+}
+
+static bool DoesMethodHaveIEnumeratorReturnType(ScriptingMethodPtr method)
+{
+ ScriptingClassPtr iEnumerator = GetMonoManager ().GetCommonClasses ().iEnumerator;
+ ScriptingClassPtr returnType = scripting_method_get_returntype(method, GetScriptingTypeRegistry());
+ return returnType == iEnumerator;
+}
+
+Coroutine* MonoBehaviour::HandleCoroutineReturnValue (ScriptingMethodPtr method, ScriptingObjectPtr returnValue)
+{
+ if (!DoesMethodHaveIEnumeratorReturnType(method))
+ return NULL;
+
+ return CreateCoroutine(returnValue, method);
+}
+
+ScriptingObjectPtr MonoBehaviour::InvokeMethodOrCoroutineChecked(ScriptingMethodPtr scriptingMethod, ScriptingObjectPtr value, ScriptingExceptionPtr* exception)
+{
+ #define PARAMETER_ERROR(x) \
+ { \
+ string error = Format ("Failed to call function %s of class %s\n", scripting_method_get_name (scriptingMethod), GetScript ()->GetScriptClassName ().c_str ()); \
+ error += x; \
+ ErrorStringObject (error, this); \
+ return SCRIPTING_NULL; \
+ }
+#if ENABLE_MONO
+
+
+ MonoObject* instance = GetInstance();
+ int argCount = scripting_method_get_argument_count (scriptingMethod, GetScriptingTypeRegistry());
+
+ // Fast path - method takes no arguments
+ if (argCount == 0)
+ return mono_runtime_invoke_profiled_fast(scriptingMethod, instance, exception, NULL);
+
+ // One argument, one value
+ if (argCount != 1 || value == SCRIPTING_NULL)
+ {
+ if (value == NULL)
+ {
+ PARAMETER_ERROR (Format ("Calling function %s with no parameters but the function requires %d.", scripting_method_get_name (scriptingMethod), argCount));
+ }
+ else
+ {
+ PARAMETER_ERROR (Format ("Calling function %s with 1 parameter but the function requires %d.", scripting_method_get_name (scriptingMethod), argCount));
+ }
+
+ return NULL;
+ }
+
+ MonoClass* inParamClass = mono_object_get_class (value);
+
+ void* iterator = NULL;
+
+ MonoMethodSignature* sig = mono_method_signature (scriptingMethod->monoMethod);
+ MonoType* methodType = mono_signature_get_params (sig, &iterator);
+ MonoClass* methodClass = mono_class_from_mono_type (methodType);
+ int methodTypeType = mono_type_get_type (methodType);
+
+ void* argumentList[1] = { NULL };
+
+ // Pass builtin type
+ if (IsMonoBuiltinType (methodTypeType))
+ {
+ MonoObject* converted = ConvertBuiltinMonoValue(value, methodTypeType);
+ if( converted )
+ {
+ argumentList[0] = ExtractMonoObjectDataPtr<void> (converted);
+ }
+ }
+ // Pass by value
+ else if (methodTypeType == MONO_TYPE_VALUETYPE)
+ {
+ if (inParamClass == methodClass)
+ {
+ argumentList[0] = ExtractMonoObjectDataPtr<void> (value);
+ }
+ }
+ // Pass by class
+ else if (methodTypeType == MONO_TYPE_CLASS)
+ {
+ if (mono_class_is_subclass_of (inParamClass, methodClass, false))
+ {
+ argumentList[0] = value;
+ }
+ }
+ // Passing string
+ else if (methodTypeType == MONO_TYPE_STRING && methodTypeType == mono_type_get_type (mono_class_get_type (inParamClass)))
+ {
+ argumentList[0] = value;
+ }
+ else if (methodTypeType == MONO_TYPE_OBJECT)
+ {
+ argumentList[0] = value;
+ }
+
+ if (argumentList[0])
+ return mono_runtime_invoke_profiled (scriptingMethod->monoMethod, instance, argumentList, exception);
+
+ void* invokeargs[3] = { instance, mono_string_new_wrapper (scripting_method_get_name (scriptingMethod)), value };
+ return mono_runtime_invoke_profiled (GetMonoManager().GetCommonClasses().invokeMember->monoMethod, NULL, invokeargs, exception);
+
+#else
+ // ToDo: make full params type check, like we do it with mono
+ int argCount = scripting_method_get_argument_count(scriptingMethod, GetScriptingTypeRegistry());
+ ScriptingInvocation invocation(scriptingMethod);
+ invocation.object = GetInstance();
+
+ if (argCount == 0)
+ return invocation.Invoke(exception);
+
+ if (argCount != 1 || value == SCRIPTING_NULL)
+ {
+ if (value == SCRIPTING_NULL)
+ {
+ PARAMETER_ERROR (Format ("Calling function %s with no parameters but the function requires %d.", scripting_method_get_name (scriptingMethod), argCount));
+ }
+ else
+ {
+ PARAMETER_ERROR (Format ("Calling function %s with 1 parameter but the function requires %d.", scripting_method_get_name (scriptingMethod), argCount));
+ }
+ return SCRIPTING_NULL;
+ }
+
+#if UNITY_WP8
+ ScriptingInvocation invokeMember(MONO_COMMON.invokeMember);
+ invokeMember.AddObject(GetInstance());
+ invokeMember.AddString(scripting_method_get_name(scriptingMethod));
+ invokeMember.AddObject(value);
+ return invokeMember.Invoke(exception);
+#else
+ invocation.AddObject(value);
+ return invocation.Invoke(exception, true);
+#endif
+
+#endif
+ #undef PARAMETER_ERROR
+}
+
+Coroutine* MonoBehaviour::InvokeMethodOrCoroutineChecked (ScriptingMethodPtr method, ScriptingObjectPtr value)
+{
+
+ AssertIf (!IsPlayingOrAllowExecuteInEditMode ());
+
+ ScriptingObjectPtr instance = GetInstance();
+ AssertIf (instance == SCRIPTING_NULL);
+
+ ScriptingExceptionPtr exception = NULL;
+ ScriptingObjectPtr returnValue = InvokeMethodOrCoroutineChecked(method,value,&exception);
+
+ if (returnValue != SCRIPTING_NULL && exception == NULL)
+ return HandleCoroutineReturnValue (method, returnValue);
+
+ if (exception != NULL)
+ Scripting::LogException(exception, Scripting::GetInstanceIDFromScriptingWrapper(instance));
+
+ return NULL;
+}
+
+Coroutine* MonoBehaviour::StartCoroutine (const char* name, ScriptingObjectPtr value)
+{
+ AssertIf (!IsPlayingOrAllowExecuteInEditMode ());
+ AssertIf (GetInstance() == SCRIPTING_NULL);
+
+ if (!IsActive ())
+ {
+ ErrorStringObject (Format ("Coroutine '%s' couldn't be started because the the game object '%s' is inactive!", name, GetName()), this);
+ return NULL;
+ }
+
+ ScriptingMethodPtr method = FindMethod (name);
+ if (method == NULL)
+ {
+ ErrorStringObject (Format ("Coroutine '%s' couldn't be started!", name), this);
+ return NULL;
+ }
+
+ return InvokeMethodOrCoroutineChecked (method, value);
+}
+
+static void DoStopCoroutine (Coroutine *coroutine)
+{
+ coroutine->RemoveFromList ();
+
+ // We clear the behaviour reference prior to cleaning up the coroutine because otherwise CleanupCoroutine might remove
+ // it from underneath m_ActiveCoroutines
+ coroutine->m_Behaviour = NULL;
+
+ if (coroutine->m_WaitingFor)
+ {
+ AssertIf (coroutine->m_WaitingFor->m_ContinueWhenFinished != coroutine);
+ coroutine->m_WaitingFor->m_ContinueWhenFinished = NULL;
+ coroutine->m_WaitingFor = NULL;
+ Coroutine::CleanupCoroutine (coroutine);
+ }
+ else if (coroutine->m_AsyncOperation)
+ Coroutine::CleanupCoroutine (coroutine);
+}
+
+void MonoBehaviour::StopCoroutine (const char* name)
+{
+ #if DEBUG_COROUTINE
+ printf_console("StopCoroutine %s. %d - %s", name, this, GetName());
+ #endif
+ GetDelayedCallManager ().CancelCallDelayed (this, Coroutine::ContinueCoroutine, Coroutine::CompareCoroutineMethodName, (void*)name);
+ for (List<Coroutine>::iterator i = m_ActiveCoroutines.begin ();
+ i != m_ActiveCoroutines.end (); ++i) {
+ if (i->m_CoroutineMethod != NULL && !strcmp (name, scripting_method_get_name (i->m_CoroutineMethod))) {
+ DoStopCoroutine (&(*i));
+ break;
+ }
+ }
+}
+
+void MonoBehaviour::StopAllCoroutines ()
+{
+ if(m_ActiveCoroutines.empty ())
+ {
+ return;
+ }
+
+ #if DEBUG_COROUTINE
+ printf_console("StopAllCoroutines! %d - %s", this, GetName());
+ #endif
+
+ DelayedCall* wwwCallback = NULL;
+ #if ENABLE_WWW
+ wwwCallback = WWWDelayCall::Callback;
+ #endif
+
+ // Remove all coroutines in this behaviour from delayed call/
+ // This will call CleanupCoroutine and decrease the retain count.
+ // In turn this might remove coroutines from the active coroutine list, if no one else is referencing them.
+ GetDelayedCallManager ().CancelCallDelayed2 (this, Coroutine::ContinueCoroutine, wwwCallback);
+
+ /// 1. We need to go through the remaining coroutines (Those that are still referenced from mono or other coroutines are yielding to it from another game object)
+
+ while (!m_ActiveCoroutines.empty())
+ {
+ Coroutine *coroutine = &m_ActiveCoroutines.front();
+ DoStopCoroutine (coroutine);
+ #if DEBUG_COROUTINE
+ printf_console ("Cleaning up Stop ALL %d\n", coroutine);
+ #endif
+ }
+
+ // This assert is useful when you think we might leak coroutines.
+ Assert (m_ActiveCoroutines.empty ());
+}
+
+#if UNITY_EDITOR
+void MonoBehaviour::CheckConsistency ()
+{
+ Super::CheckConsistency();
+
+ if (!GetInstance())
+ return;
+
+ bool canDestroy = GetDisableImmediateDestruction ();
+ SetDisableImmediateDestruction (true);
+
+ // Validate properties
+
+ ScriptingMethodPtr method = m_Methods[MonoScriptCache::kValidateProperties];
+ if (method && !(m_ScriptCache->scriptType == kScriptTypeMonoBehaviourDerived && GetGameObjectPtr() == NULL))
+ {
+ CallMethodInactive (method);
+ }
+
+ SetDisableImmediateDestruction (canDestroy);
+}
+#endif
+
+void MonoBehaviour::SmartReset ()
+{
+ Super::SmartReset ();
+ // Only in edit mode for more speed
+ // and to be nice to scripters we call Reset only if we are active
+ if (GetInstance() && !IsWorldPlaying () )
+ CallMethodInactive("Reset");
+}
+
+void MonoBehaviour::InitializeClass ()
+{
+ GameObject::RegisterAllMessagesHandler (ClassID (MonoBehaviour), &MonoBehaviour::HandleNotifications, &MonoBehaviour::CanHandleNotifications);
+
+ RegisterAllowNameConversion ("GUISkin", "customStyles", "m_CustomStyles");
+
+ InitializeMonoBehaviourAnimationBindingInterface ();
+}
+
+void MonoBehaviour::CleanupClass ()
+{
+ CleanupMonoBehaviourAnimationBindingInterface ();
+}
+
+UInt32 MonoBehaviour::CalculateSupportedMessages ()
+{
+ if (!GetInstance())
+ return 0;
+
+ int mask = 0;
+ if (m_Methods[MonoScriptCache::kMethodCount + kEnterContact.messageID])
+ mask |= kHasCollisionEnterExit;
+ if (m_Methods[MonoScriptCache::kMethodCount + kExitContact.messageID])
+ mask |= kHasCollisionEnterExit;
+ if (m_Methods[MonoScriptCache::kMethodCount + kStayContact.messageID])
+ mask |= kHasCollisionStay;
+ if (m_Methods[MonoScriptCache::kMethodCount + kOnWillRenderObject.messageID])
+ mask |= kHasOnWillRenderObject;
+ if (m_Methods[MonoScriptCache::kMethodCount + kAnimatorMove.messageID])
+ mask |= kHasOnAnimatorMove;
+ if (m_Methods[MonoScriptCache::kMethodCount + kAnimatorIK.messageID])
+ mask |= kHasOnAnimatorIK;
+ if (m_Methods[MonoScriptCache::kMethodCount + kCollisionEnter2D.messageID])
+ mask |= kHasCollision2D;
+ if (m_Methods[MonoScriptCache::kMethodCount + kCollisionExit2D.messageID])
+ mask |= kHasCollision2D;
+ if (m_Methods[MonoScriptCache::kMethodCount + kCollisionStay2D.messageID])
+ mask |= kHasCollision2D;
+ return mask;
+}
+
+bool MonoBehaviour::CanHandleNotifications (void* receiver, int messageIndex, MessageData& data) {
+ DebugAssertIf (dynamic_pptr_cast<MonoBehaviour*> (static_cast<MonoBehaviour*> (receiver)) == NULL);
+ MonoBehaviour* behaviour = static_cast<MonoBehaviour*> (receiver);
+ return behaviour->GetInstance() && behaviour->m_Methods[MonoScriptCache::kMethodCount + messageIndex];
+}
+
+static bool SetupArgumentsForMessageInvocation(ScriptingArguments& arguments, MessageData& data, ScriptingMethodPtr receivingMethod, MonoBehaviour& behaviour)
+{
+ switch (data.type)
+ {
+ case 0:
+ return true;
+ case ClassID(int):
+ arguments.AddInt((int)data.GetGenericDataRef());
+ return true;
+ case ClassID(float):
+ arguments.AddFloat((float)data.GetGenericDataRef());
+ return true;
+ case ClassID(bool):
+ arguments.AddBoolean((int)data.GetGenericDataRef() != 0);
+ return true;
+ case ClassID(Collision):
+ arguments.AddObject(GetIPhysics()->ConvertContactToMono (data.GetData<Collision*> ()));
+ return true;
+ #if ENABLE_2D_PHYSICS
+ case ClassID(Collision2D):
+ arguments.AddObject(ConvertCollision2DToScripting (data.GetData<Collision2D*> ()));
+ return true;
+ #endif
+ case ClassID(MonoObject):
+ {
+ ScriptingObjectPtr parameter = data.GetScriptingObjectData ();
+ arguments.AddObject(parameter);
+
+ if (!parameter)
+ return true;
+
+ ScriptingClassPtr klass_of_receivingmethodargument = scripting_method_get_nth_argumenttype(receivingMethod,0,GetScriptingTypeRegistry());
+ if (klass_of_receivingmethodargument == NULL)
+ return true;
+ ScriptingClassPtr klass_of_payload = scripting_object_get_class(parameter,GetScriptingTypeRegistry());
+
+ if (!scripting_class_is_subclass_of(klass_of_payload, klass_of_receivingmethodargument))
+ {
+ std::string message = Format("%s couldn't be called because the expected parameter %s doesn't match %s.", scripting_method_get_name(receivingMethod), scripting_class_get_name(klass_of_receivingmethodargument),scripting_class_get_name(klass_of_payload));
+ ErrorStringObject(message, &behaviour);
+ return false;
+ }
+
+ return true;
+ }
+ default:
+ // Otherwise its a Object derived class (The heavy typechecking is done in MonoScript)
+ arguments.AddObject(Scripting::ScriptingWrapperFor (data.GetData<Object*> ()));
+ return true;
+ }
+}
+
+static bool ShouldMessageBeSentToDisabledGameObjects(int messageIndex)
+{
+ if( !IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1) && (messageIndex == kAnimatorMove.messageID || messageIndex == kAnimatorIK.messageID))
+ return true;
+
+ MessageIdentifier msg = GameObject::GetMessageHandler ().MessageIDToMessageIdentifier (messageIndex);
+
+ return !(msg.options & MessageIdentifier::kDontSendToDisabled);
+}
+
+void MonoBehaviour::HandleNotifications (void* receiver, int messageIndex, MessageData& data) {
+ DebugAssertIf (dynamic_pptr_cast<MonoBehaviour*> (static_cast<MonoBehaviour*> (receiver)) == NULL);
+ MonoBehaviour* behaviour = static_cast<MonoBehaviour*> (receiver);
+
+ #if UNITY_FLASH
+ if(behaviour->m_Methods == NULL)
+ return;
+ #endif
+
+ if (!behaviour->IsPlayingOrAllowExecuteInEditMode ())
+ return;
+
+ if (!behaviour->GetInstance())
+ return;
+
+ ScriptingMethodPtr method = behaviour->m_Methods[MonoScriptCache::kMethodCount + messageIndex];
+
+ if (method == SCRIPTING_NULL)
+ return;
+
+ if (!behaviour->GetEnabled () && !ShouldMessageBeSentToDisabledGameObjects(messageIndex))
+ return;
+
+ ScriptingInvocation invocation(method);
+ invocation.object = behaviour->GetInstance();
+ invocation.objectInstanceIDContextForException = behaviour->GetInstanceID();
+
+ if (!SetupArgumentsForMessageInvocation(invocation.Arguments(),data,method,*behaviour))
+ return;
+
+ ScriptingExceptionPtr exception = NULL;
+ ScriptingObjectPtr returnValue = invocation.Invoke(&exception);
+
+ if (exception == NULL && returnValue)
+ behaviour->HandleCoroutineReturnValue (method, returnValue);
+}
+
+void MonoBehaviour::SetupAwake ()
+{
+ if (IsPlayingOrAllowExecuteInEditMode () && !m_DidAwake && GetInstance() && IsActive ())
+ {
+ m_DidAwake = true;
+ CallMethodIfAvailable (MonoScriptCache::kAwake);
+ }
+}
+
+void MonoBehaviour::DeprecatedDelayedAwakeCall (Object* o, void* userData)
+{
+ static_cast<MonoBehaviour*> (o)->SetupAwake ();
+}
+
+void MonoBehaviour::DelayedStartCall (Object* o, void* userData)
+{
+ static_cast<MonoBehaviour*> (o)->Start ();
+}
+
+void MonoBehaviour::DeprecatedAwakeFromLoadCodePath (AwakeFromLoadMode awakeMode)
+{
+ // We must cache the reference to other objects here
+ // since Behaviour::AwakeFromLoad might destroy the object itself
+ ScriptingObjectPtr instance = GetInstance();
+
+ // Potential Call to AddToManager / RemoveFromManager with various C# callbacks
+ Super::AwakeFromLoad (awakeMode);
+
+ if (IsPlayingOrAllowExecuteInEditMode () && IsInstanceValid(instance) && !m_DidAwake && IsActive ())
+ {
+ if (awakeMode & (kInstantiateOrCreateFromCodeAwakeFromLoad | kActivateAwakeFromLoad))
+ SetupAwake();
+ else
+ // @TODO: Do we really need this? Build this into Proper AwakeFromLoadQueue or whatever
+ CallDelayed (DeprecatedDelayedAwakeCall, this, -1000, NULL, 0.0F, NULL, DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kRunFixedFrameRate | DelayedCallManager::kRunStartupFrame);
+ }
+ else if (!m_DidAwake && IsInstanceValid(instance) && IsScriptableObject())
+ {
+ m_DidAwake = true;
+
+ ScriptingMethodPtr method = m_Methods[MonoScriptCache::kAddToManager];
+ if (method)
+ CallMethodInactive (method);
+
+ method = m_Methods[MonoScriptCache::kAwake];
+ if (method)
+ CallMethodInactive (method);
+ }
+
+ if ((awakeMode & kDidLoadFromDisk) == 0 && IsInstanceValid(instance))
+ {
+ ScriptingMethodPtr method = m_Methods[MonoScriptCache::kValidateProperties];
+ if (method && !(m_ScriptCache->scriptType == kScriptTypeMonoBehaviourDerived && GetGameObjectPtr() == NULL))
+ CallMethodInactive (method);
+ }
+}
+
+
+#define USE_DEPRECATED_AWAKE !IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1)
+//#define USE_DEPRECATED_AWAKE 1
+
+
+
+// * Awake
+// * OnEnable
+// * ----- For all scripts
+// * Start for all scripts
+void MonoBehaviour::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+#if UNITY_EDITOR && UNITY_LOGIC_GRAPH
+ LoadLogicGraphInEditor ();
+#endif
+
+ if (GetGameObjectPtr())
+ GetGameObjectPtr()->SetSupportedMessagesDirty ();
+
+ // Deprecated 3.x codepath
+ if (USE_DEPRECATED_AWAKE)
+ {
+ DeprecatedAwakeFromLoadCodePath (awakeMode);
+ return;
+ }
+
+ // We must cache the reference to other objects here
+ // since Behaviour::AwakeFromLoad might destroy the object itself
+ // and we need a way to check that this happened.
+ ScriptingObjectPtr instance = GetInstance();
+
+ // The managed MonoBehaviour could not be created. Give control to the Behaviour and move on.
+ if (instance == SCRIPTING_NULL)
+ {
+ Super::AwakeFromLoad (awakeMode);
+ return;
+ }
+
+ // CallDelayed->Start must be called before Awake otherwise objects created inside of Awake will get their Start call
+ // before the Start of the Behaviour that created them.
+ bool willCallAddToManager = IsPlayingOrAllowExecuteInEditMode() && GetEnabled() && IsActive();
+ if (willCallAddToManager)
+ {
+ Super::AwakeFromLoad (awakeMode);
+ return;
+ }
+
+ #define RETURN_IF_INSTANCE_DESTROYED if (!IsInstanceValid(instance)) return;
+
+ bool isMonoBehaviourRequiringAwake = IsPlayingOrAllowExecuteInEditMode () && !m_DidAwake && IsActive ();
+ bool isScriptableObjectRequiringAwakeAndEnable = !m_DidAwake && IsScriptableObject();
+
+ // Awake for monobehaviours and scriptable objects
+ if (isMonoBehaviourRequiringAwake || isScriptableObjectRequiringAwakeAndEnable)
+ {
+ CallAwake();
+ RETURN_IF_INSTANCE_DESTROYED
+ }
+
+ // OnEnable for scriptable object
+ if (isScriptableObjectRequiringAwakeAndEnable)
+ {
+ ScriptingMethodPtr enableMethod = m_Methods[MonoScriptCache::kAddToManager];
+ if (enableMethod)
+ {
+ CallMethodInactive (enableMethod);
+ RETURN_IF_INSTANCE_DESTROYED
+ }
+ }
+
+ // Potential Call to AddToManager / RemoveFromManager with various C# callbacks
+ Super::AwakeFromLoad (awakeMode);
+
+ #undef RETURN_IF_INSTANCE_DESTROYED
+}
+
+void MonoBehaviour::CallAwake ()
+{
+ m_DidAwake = true;
+ ScriptingMethodPtr awakeMethod = m_Methods[MonoScriptCache::kAwake];
+ if (awakeMethod)
+ if (!CallMethodInactive (awakeMethod))
+ SetEnabled (false);
+}
+
+
+
+void MonoBehaviour::AddExternalDependencyCallbacksToManagers ()
+{
+#if ENABLE_IMAGEEFFECTS
+ ///@TODO: It doesn't look like the order of image effects is determined by the component order.
+ // Instead by the rather random from the users perspective Awake order.
+ if (m_Methods[MonoScriptCache::kRenderImageFilter])
+ {
+ Camera *camera = QueryComponent (Camera);
+ if (camera)
+ {
+ bool afterOpaque = scripting_method_has_attribute(m_Methods[MonoScriptCache::kRenderImageFilter], MONO_COMMON.imageEffectOpaque);
+ bool transformsToLDR = scripting_method_has_attribute(m_Methods[MonoScriptCache::kRenderImageFilter], MONO_COMMON.imageEffectTransformsToLDR);
+
+ ImageFilter filter (this, &RenderImageFilter, transformsToLDR, afterOpaque);
+ camera->AddImageFilter (filter);
+ }
+ }
+#endif
+
+ SetByPassOnDSP(false);
+}
+
+void MonoBehaviour::SetByPassOnDSP(bool state)
+{
+#if ENABLE_AUDIO_FMOD
+ IAudio* audio = GetIAudio();
+ if (!audio) return;
+
+ FMOD::DSP* dsp = GetOrCreateDSP();
+ if (dsp)
+ audio->SetBypassOnDSP(dsp,state);
+#endif
+}
+
+void MonoBehaviour::AddBehaviourCallbacksToManagers ()
+{
+ MonoScript* script = m_Script;
+ int executionOrder = script ? script->GetExecutionOrder() : 0;
+
+ if (m_Methods[MonoScriptCache::kUpdate])
+ GetBehaviourManager().AddBehaviour (m_UpdateNode, executionOrder);
+ if (m_Methods[MonoScriptCache::kFixedUpdate])
+ GetFixedBehaviourManager().AddBehaviour (m_FixedUpdateNode, executionOrder);
+ if (m_Methods[MonoScriptCache::kLateUpdate])
+ GetLateBehaviourManager().AddBehaviour (m_LateUpdateNode, executionOrder);
+
+ if (m_Methods[MonoScriptCache::kRenderObject])
+ GetRenderManager().AddOnRenderObject (m_OnRenderObjectNode);
+
+#if ENABLE_UNITYGUI
+ if (m_Methods[MonoScriptCache::kGUI])
+ GetGUIManager().AddGUIScript (m_GUINode);
+#endif
+}
+
+
+/* TODO: Tests
+ * Destruction during OnEnable / Awake / Start (Maybe integration tests cover this already??)
+ * Ensure that image effects & image filters are setup if their camera is created in OnEnable...
+*/
+
+#define RETURN_IF_DESTROYED_OR_DISABLED if (!IsInstanceValid(instance) || !GetEnabled()) return;
+
+void MonoBehaviour::AddToManager ()
+{
+ if (USE_DEPRECATED_AWAKE)
+ {
+ DeprecatedAddToManager ();
+ return;
+ }
+
+
+ ScriptingObjectPtr instance = GetInstance();
+ if (instance == SCRIPTING_NULL || !IsPlayingOrAllowExecuteInEditMode ())
+ return;
+
+ if (m_Methods[MonoScriptCache::kCoroutineStart] || m_Methods[MonoScriptCache::kCoroutineMain])
+ CallDelayed (DelayedStartCall, this, -10, NULL, 0.0F, NULL, DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kRunFixedFrameRate | DelayedCallManager::kRunStartupFrame);
+
+
+ // Behaviour callbacks are registered before OnEnable.
+ // If an object is created in OnEnable, the order will be this script, then the created script.
+ AddBehaviourCallbacksToManagers ();
+
+ // We must call Awake here.
+ // CallDelayed->Start must be called before Awake otherwise objects created inside of Awake will get their Start call
+ // before the Start of the Behaviour that created them.
+ if (!m_DidAwake)
+ {
+ CallAwake ();
+ RETURN_IF_DESTROYED_OR_DISABLED
+ }
+
+ if (m_Methods[MonoScriptCache::kAddToManager])
+ {
+ CallMethodIfAvailable (MonoScriptCache::kAddToManager);
+ RETURN_IF_DESTROYED_OR_DISABLED
+ }
+
+ // External dependencies might get created by OnEnable.
+ // Thus we hook them up after OnEnable
+ AddExternalDependencyCallbacksToManagers ();
+}
+
+void MonoBehaviour::Deactivate (DeactivateOperation operation)
+{
+ // When loading a new level we don't want coroutines to stop running, so just ignore it.
+ if (operation != kDeprecatedDeactivateToggleForLevelLoad)
+ StopAllCoroutines ();
+
+ Super::Deactivate (operation);
+}
+
+
+void MonoBehaviour::ReleaseMonoInstance ()
+{
+ Assert(m_ActiveCoroutines.empty());
+
+ ScriptingObjectPtr instance = GetCachedScriptingObject();
+ if (instance)
+ {
+ // In the player we protect against null reference exceptions by setting the instance id of the mono representation to 0.
+ // This is necessary because mono classes might stick around for a bit longer until the GC cleans it up.
+ // By setting the id to 0 it is impossible for another mono behaviour instance to load it from disk or other weird things.
+
+ // In the editor we don't want to do this as it will cause references to scripts to
+ // get lost when importing packages containing the referenced script.
+ // ReleaseMonoInstance is called in that case because the script is getting deleted.
+
+
+ #if !UNITY_EDITOR
+ ScriptingObjectOfType<Object> wrapper(instance);
+ wrapper.SetInstanceID(0);
+ wrapper.SetCachedPtr(NULL);
+ #endif
+
+ SetCachedScriptingObject(SCRIPTING_NULL);
+ }
+
+ Assert(GetCachedScriptingObject() == SCRIPTING_NULL);
+
+ m_Methods = NULL;
+ if (m_ScriptCache != NULL)
+ {
+ const_cast<MonoScriptCache*> (m_ScriptCache)->Release();
+ m_ScriptCache = NULL;
+ }
+}
+
+#if UNITY_EDITOR
+#define LogScriptError(x,script) DebugStringToFile (x, 0, __FILE__, __LINE__, kLog | kScriptCompileError, script, GetMonoManager().GetInstanceID ());
+#endif
+
+static UNITY_TLS_VALUE(int) s_MonoBehaviourInConstructorCounter;
+int GetMonoBehaviourInConstructor()
+{
+ return s_MonoBehaviourInConstructorCounter;
+}
+
+static std::string SafeGetScriptFileName(MonoScript* script, ScriptingClassPtr klass)
+{
+ if (script)
+ return script->GetName();
+
+ if (klass)
+ return scripting_class_get_name(klass);
+
+ return "";
+}
+
+/////@TODO: THIS IS NOT REALLY THREAD SAFE. ALL MONO SCRIPT FUNCTIONS ARE NOT THREAD SAFE???
+//// MONO SCRIPT LOADING SHOULD NOT BE DONE IN A DIFFERENT THREAD
+/// ALL FUNCTIONS MODIFYING SCRIPT CONTENT MUST BE THREAD SAFE
+void MonoBehaviour::RebuildMonoInstance (ScriptingObjectPtr instance)
+{
+ ReleaseMonoInstance ();
+
+ MonoScript* script = 0;
+ MonoScriptType type = kScriptTypeNotInitialized;
+
+#if UNITY_EDITOR
+ if (m_ScriptCache == NULL && !m_EditorClassIdentifier.empty())
+ {
+ std::string assembly;
+ std::string ns;
+ std::string klass;
+ GetScriptClassIdComponents(m_EditorClassIdentifier, assembly, ns, klass);
+
+ ScriptingClass* sc = GetMonoManager().GetMonoClassWithAssemblyName(klass, ns, assembly);
+ m_ScriptCache = CreateMonoScriptCache(sc, true, this);
+ if (m_ScriptCache != NULL)
+ {
+ m_ScriptCache->Retain();
+ type = m_ScriptCache->scriptType;
+ }
+ }
+#endif
+
+ if (m_ScriptCache == NULL)
+ {
+ script = dynamic_pptr_cast<MonoScript*> (InstanceIDToObjectThreadSafe(m_Script.GetInstanceID()));
+ if (script)
+ {
+ m_ScriptCache = script->GetScriptCache ();
+ if (m_ScriptCache != NULL)
+ {
+ m_ScriptCache->Retain();
+ type = m_ScriptCache->scriptType;
+ }
+ }
+ else
+ type = kScriptTypeScriptMissing;
+ }
+
+ // We want a warning to be printed only once, that is when entering play mode and not when returning back
+ if (IsWorldPlaying() && !IsValidScriptType(type))
+ WarningStringObject (FormatScriptTypeError(type, SafeGetScriptFileName(script, GetClass())), this);
+
+ if (!IsValidScriptType(type))
+ return;
+
+ AssertIf(GetCachedScriptingObject() != SCRIPTING_NULL);
+ Assert(m_ScriptCache != NULL && m_ScriptCache->klass != NULL);
+
+ if (instance == SCRIPTING_NULL)
+ {
+ ScriptingObjectPtr newInstance;
+#if ENABLE_MONO
+ SET_ALLOC_OWNER(s_MonoDomainContainer);
+#endif
+ // Instantiate Mono class, handle exception
+ newInstance = scripting_object_new (m_ScriptCache->klass);
+ if (newInstance == SCRIPTING_NULL)
+ {
+ if (IsWorldPlaying())
+ WarningStringObject (Format("The script behaviour '%s' could not be instantiated!", script->GetScriptClassName().c_str()), this);
+ return;
+ }
+
+ /// Setup Mono object pointers
+ Scripting::ConnectScriptingWrapperToObject(newInstance, this);
+
+ // Prevent certain functions inside constructor
+ int constructorCount = s_MonoBehaviourInConstructorCounter;
+ constructorCount++;
+ s_MonoBehaviourInConstructorCounter = constructorCount;
+
+ ScriptingExceptionPtr exception = NULL;
+
+ scripting_object_invoke_default_constructor(GetInstance(), &exception);
+
+ DebugAssertIf(constructorCount != s_MonoBehaviourInConstructorCounter);
+ constructorCount--;
+ s_MonoBehaviourInConstructorCounter = constructorCount;
+
+ if (exception)
+ Scripting::LogException(exception, Scripting::GetInstanceIDFromScriptingWrapper(newInstance));
+ }
+ else
+ {
+ Scripting::ConnectScriptingWrapperToObject (instance, this);
+ }
+
+ // Get methods array from script
+ m_Methods = m_ScriptCache->methods.begin();
+
+ #if UNITY_EDITOR
+ m_EditorHideFlags |= (script && script->IsBuiltinScript()) ? kHideScriptPPtr : 0;
+ #endif
+}
+
+void MonoBehaviour::RebuildMonoInstanceFromScriptChange (ScriptingObjectPtr instance)
+{
+ if (IsAddedToManager ())
+ RemoveFromManager ();
+
+ RebuildMonoInstance (instance);
+#if UNITY_EDITOR
+ if (GetInstance() != NULL)
+ SetBackup(NULL);
+#endif
+
+ if (IsAddedToManager ())
+ AddToManager ();
+}
+
+void MonoBehaviour::SetScript (const PPtr<MonoScript>& newScript, ScriptingObjectPtr instance)
+{
+ if (m_Script != newScript)
+ {
+ //LockPlayerLoop();
+
+ m_Script = newScript;
+
+ RebuildMonoInstanceFromScriptChange(instance);
+
+ //UnlockPlayerLoop();
+ }
+ else if (IsWorldPlaying () && newScript == PPtr<MonoScript> (0))
+ {
+ WarningStringObject ("The referenced script on this Behaviour is missing!", this);
+ }
+}
+
+#if UNITY_EDITOR
+
+void MonoBehaviour::SetClassIdentifier (const std::string& id)
+{
+ if (m_EditorClassIdentifier != id)
+ {
+ //LockPlayerLoop();
+
+ m_EditorClassIdentifier = id;
+
+ RebuildMonoInstanceFromScriptChange(NULL);
+
+ //UnlockPlayerLoop();
+ }
+ //m_EditorClassIdentifier = id;
+}
+
+bool MonoBehaviour::CanAssignMonoVariable (const char* propertyType, Object* object)
+{
+ return CanAssignMonoVariableStatic(propertyType, object);
+}
+
+bool MonoBehaviour::CanAssignMonoVariableStatic (const char* propertyType, Object* object)
+{
+ MonoObject* instance = Scripting::ScriptingWrapperFor(object);
+ if (!instance)
+ return false;
+
+ MonoClass* assignmentClass = mono_object_get_class(instance);
+ while (assignmentClass != NULL)
+ {
+ if (strcmp(mono_class_get_name(assignmentClass), propertyType) == 0)
+ return true;
+
+ assignmentClass = mono_class_get_parent(assignmentClass);
+ }
+
+ return false;
+}
+
+#if UNITY_LOGIC_GRAPH
+void MonoBehaviour::LoadLogicGraphInEditor ()
+{
+ // At the moment we compile LogicGraphs just in time when loading scenes.
+ // MonoBehaviour instances are currently created on the loading thread -> Before logic graphs are compiled
+ // Thus we need this hack in the editor to ensure that the logic graph is active using the latest graph data
+ MonoScript* script = m_Script;
+ if (script != NULL && script->GetEditorGraphData() != NULL && IsWorldPlaying())
+ {
+ script->Rebuild(CompileAndLoadLogicGraph(*script, false));
+ RebuildMonoInstance(NULL);
+ }
+}
+#endif
+
+
+void MonoBehaviour::DidReloadDomain ()
+{
+#if ENABLE_AUDIO_FMOD
+ // release custom filter
+ delete m_AudioCustomFilter;
+ m_AudioCustomFilter = NULL;
+#endif
+
+ if (IsAddedToManager())
+ AddToManager();
+ else
+ {
+ MonoScript* script = m_Script;
+ if (GetInstance() == NULL || script == NULL)
+ return;
+
+ if (!IsScriptableObject())
+ return;
+
+ ScriptingMethodPtr method = m_Methods[MonoScriptCache::kAddToManager];
+ if (method != NULL)
+ CallMethodInactive(method);
+ }
+}
+
+bool MonoBehaviour::ShouldDisplayEnabled ()
+{
+ if (m_EditorHideFlags & kHideEnabled)
+ return false;
+ if (GetInstance())
+ {
+ return m_Methods[MonoScriptCache::kUpdate] || m_Methods[MonoScriptCache::kFixedUpdate] ||
+ m_Methods[MonoScriptCache::kLateUpdate] ||
+ m_Methods[MonoScriptCache::kRenderImageFilter] ||
+ m_Methods[MonoScriptCache::kCoroutineStart] || m_Methods[MonoScriptCache::kCoroutineMain] ||
+ m_Methods[MonoScriptCache::kGUI] ||
+ m_Methods[MonoScriptCache::kAddToManager] || m_Methods[MonoScriptCache::kRemoveFromManager] ||
+ m_Methods[MonoScriptCache::kAudioFilterRead];
+ }
+ else
+ return true;
+}
+
+
+
+#endif // UNITY_EDITOR
+
+char const* MonoBehaviour::GetName () const
+{
+ const GameObject* go = GetGameObjectPtr();
+ if (go)
+ return go->GetName();
+ else
+ return m_Name.c_str ();
+}
+
+void MonoBehaviour::SetName (char const* name)
+{
+ GameObject* go = GetGameObjectPtr();
+ if (go)
+ return go->SetName(name);
+ else
+ {
+ m_Name = name;
+ SetDirty();
+ }
+}
+
+ScriptingClassPtr MonoBehaviour::GetClass ()
+{
+ if (m_ScriptCache != NULL)
+ return m_ScriptCache->klass;
+ else
+ return NULL;
+}
+
+const MonoBehaviour::MethodCache* MonoBehaviour::GetMethodCache ()
+{
+ if (m_ScriptCache != NULL)
+ return &m_ScriptCache->methodCache;
+ else
+ return NULL;
+}
+
+
+std::string MonoBehaviour::GetScriptClassName ()
+{
+ MonoScript* script = m_Script;
+ if (script)
+ return script->GetScriptClassName();
+ else
+ return string();
+}
+
+std::string MonoBehaviour::GetScriptFullClassName ()
+{
+ MonoScript* script = m_Script;
+ if (script)
+ return script->GetScriptFullClassName();
+ else
+ return string();
+}
+
+#if ENABLE_MONO || UNITY_WINRT
+ScriptingArrayPtr RequiredComponentsOf(ScriptingClassPtr klass)
+{
+ // Extract component requirements
+ ScriptingObjectPtr typeObject = scripting_class_get_object(klass);
+ ScriptingInvocation invocation(GetMonoManager().GetCommonClasses().extractRequiredComponents);
+ invocation.AddObject(typeObject);
+ return scripting_cast_object_to_array(invocation.Invoke());
+}
+
+ScriptingArrayPtr RequiredComponentsOf(MonoBehaviour* script)
+{
+ return RequiredComponentsOf(script->GetClass());
+}
+#endif
+
+void ResetAndApplyDefaultReferencesOnNewMonoBehaviour(MonoBehaviour& behaviour)
+{
+
+#if UNITY_EDITOR
+ MonoScript* script = behaviour.GetScript();
+ if (script && (script->GetScriptType() == kScriptTypeEditorScriptableObjectDerived || !IsWorldPlaying()))
+ ApplyDefaultReferences(behaviour, script->GetDefaultReferences());
+#endif
+
+ behaviour.Reset();
+ behaviour.SmartReset();
+
+ behaviour.AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+}
+
+#undef LogScriptError
+#endif
diff --git a/Runtime/Mono/MonoBehaviour.h b/Runtime/Mono/MonoBehaviour.h
new file mode 100644
index 0000000..9972c51
--- /dev/null
+++ b/Runtime/Mono/MonoBehaviour.h
@@ -0,0 +1,373 @@
+#ifndef MONOBEHAVIOUR_H
+#define MONOBEHAVIOUR_H
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "MonoIncludes.h"
+#include "Runtime/Serialize/TypeTree.h"
+#include "Runtime/Utilities/CStringHash.h"
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+
+
+class MonoScript;
+class MonoBehaviour;
+typedef ListNode<MonoBehaviour> MonoBehaviourListNode;
+class MonoManager;
+struct ObjectGUIState;
+struct Coroutine;
+class RenderTexture;
+struct MonoScriptCache;
+
+#if ENABLE_AUDIO_FMOD
+class AudioCustomFilter;
+namespace FMOD {
+ class DSP;
+}
+#endif
+
+class TerrainInstance;
+
+
+#if UNITY_EDITOR
+#include "Editor/Src/Utility/YAMLNode.h"
+
+struct BackupState
+{
+ BackupState ();
+ ~BackupState ();
+
+ TypeTree typeTree;
+ dynamic_array<UInt8> state;
+ bool inYamlFormat;
+ bool loadedFromDisk;
+ YAMLNode* yamlState;
+
+
+ void SetYamlBackup (YAMLNode* root) { delete yamlState; inYamlFormat = true; yamlState = root; }
+ bool IsYaml () const { return inYamlFormat; }
+ bool HasYamlData () const { return yamlState != NULL; }
+
+ private:
+
+ // Preventy copy constructor
+ BackupState (const BackupState& preventCopy);
+ void operator = (const BackupState& preventCopy);
+};
+#endif
+
+
+class MonoBehaviour : public Behaviour
+{
+public:
+
+ REGISTER_DERIVED_CLASS (MonoBehaviour, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (MonoBehaviour)
+
+ MonoBehaviour (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~MonoBehaviour (); declared-by-macro
+
+ // Tag class as sealed, this makes QueryComponent faster.
+ static bool IsSealedClass () { return true; }
+
+ /// Returns the MonoObject representing this MonoBehaviour
+ ScriptingObjectPtr GetInstance ()
+ {
+ #if ENABLE_SCRIPTING
+ return GetCachedScriptingObject();
+ #else
+ return SCRIPTING_NULL;
+ #endif
+ }
+ ScriptingClassPtr GetClass ();
+
+ void RebuildMonoInstanceFromScriptChange (ScriptingObjectPtr instance);
+ /// Changes the used script to newScript
+ /// It is critical you call Awake
+ void SetScript (const PPtr<MonoScript>& newScript, ScriptingObjectPtr instance = SCRIPTING_NULL);
+ PPtr<MonoScript> GetScript () const { return m_Script; }
+
+ Coroutine* InvokeMethodOrCoroutineChecked (ScriptingMethodPtr method, ScriptingObjectPtr value = SCRIPTING_NULL);
+
+ /// Starts a coroutine
+ /// If value is not null the value will be passed into the object.
+ /// If the method is not a coroutine, it will just be invoked
+ Coroutine* CreateCoroutine(ScriptingObjectPtr enumerator, ScriptingMethodPtr method);
+ Coroutine* StartCoroutine (const char* name, ScriptingObjectPtr value = SCRIPTING_NULL);
+ ScriptingObjectPtr StartCoroutineManaged (const char* name, ScriptingObjectPtr value = SCRIPTING_NULL);
+ ScriptingObjectPtr StartCoroutineManaged2 (ScriptingObjectPtr enumerator);
+
+ std::string GetScriptClassName ();
+ std::string GetScriptFullClassName ();
+
+ /// Calls a method with methodName if the method is implemented by the class
+ bool CallMethodInactive (const char* methodName);
+
+ /// Calls a method
+ bool CallMethodInactive (ScriptingMethodPtr method);
+
+#if UNITY_EDITOR
+ // Check the consistency of the mono behavior... (validate fields ect in mono land)
+ virtual void CheckConsistency ();
+#endif
+
+ /// Forwards Reset to the MonoClass in edit mode only.
+ virtual void SmartReset ();
+
+ enum GUILayoutType {kNoLayout = 0, kGameLayout = 1, kEditorWindowLayout = 2 };
+#if ENABLE_UNITYGUI
+ /// Caller for the GUI functions.
+ virtual bool DoGUI (GUILayoutType layoutType, int skin);
+#endif
+
+ // ImageFilter
+ #if ENABLE_IMAGEEFFECTS
+ static void RenderImageFilter (Unity::Component* component, RenderTexture *source, RenderTexture *destination);
+ #endif
+
+ virtual void Deactivate (DeactivateOperation operation);
+ virtual void AwakeFromLoad (AwakeFromLoadMode mode);
+
+ virtual UInt32 CalculateSupportedMessages ();
+
+ ScriptingMethodPtr FindMethod (const char* name);
+
+ void InvokeOnRenderObject ();
+
+ #if UNITY_EDITOR
+ enum { kHideScriptPPtr = 1 << 0, kHideEnabled = 1 << 2 };
+ void SetEditorHideFlags(int flags) { m_EditorHideFlags = flags; }
+
+ void SetClassIdentifier(const std::string& id);
+ std::string GetClassIdentifier() const { return m_EditorClassIdentifier; }
+ #endif
+
+ void StopCoroutine (const char* name);
+ void StopAllCoroutines ();
+
+ virtual char const* GetName () const;
+ virtual void SetName (char const* name);
+
+ typedef std::map<const char*, ScriptingMethodPtr, compare_cstring> MethodCache;
+ const MethodCache* GetMethodCache ();
+
+ // SetupAwake is called immediately by Activate.
+ // It is delayed to make sure that all objects in the scene are setup
+ // when Awake gets called on the MonoObject
+ void SetupAwake ();
+
+ bool GetUseGUILayout ();
+ void SetUseGUILayout (bool use);
+
+ bool WillUnloadScriptableObject ();
+
+private:
+
+ static void DeprecatedDelayedAwakeCall (Object* o, void* userData);
+ static void DelayedStartCall (Object* o, void* userData);
+
+ /// Calls Awake on the MonoObject if it implements the Awake method
+ static void DelayedAwakeMonoBehaviour (Object* o, void* userData);
+
+ // Registers the MonoBehaviour for the Update/FixedUpdate/LateUpdate method.
+ // Update/FixedUpdate/LateUpdate is called if:
+ // - We are in play mode
+ // - Any of the Update, FixedUpdate, LateUpdate is implemented in the MonoClass.
+ // Before invoking the Update method,
+ // the Start method is invoked if it hasnt been invoked yet.
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+ virtual void Update ();
+ virtual void FixedUpdate ();
+ virtual void LateUpdate ();
+ inline void Start ();
+
+ void DeprecatedAwakeFromLoadCodePath (AwakeFromLoadMode awakeMode);
+
+ #if UNITY_EDITOR
+ friend void DrawMonoGizmo (Object& object, int options, void*);
+ friend bool CanDrawMonoGizmo (Object& object, int options, void*);
+ #endif
+
+ inline void CallMethodIfAvailable (int methodIndex);
+ void CallUpdateMethod(int methodIndex);
+
+ ScriptingObjectPtr InvokeMethodOrCoroutineChecked(ScriptingMethodPtr method, ScriptingObjectPtr value, ScriptingExceptionPtr* exception);
+
+ // Depending on which script we use the serialized typetree changes
+ // thus the typetree might change while the editor is running
+ virtual bool GetNeedsPerObjectTypeTree () const { return true; }
+
+ static void HandleNotifications (void* receiver, int messageIndex, MessageData& data);
+ static bool CanHandleNotifications (void* receiver, int messageIndex, MessageData& data);
+
+ #if UNITY_EDITOR
+ bool CanAssignMonoVariable (const char* property, Object* object);
+ void DidReloadDomain ();
+ #endif
+
+ virtual void WillDestroyComponent ();
+
+ template<bool kSwap>
+ void VirtualRedirectTransferStreamedBinaryRead(StreamedBinaryRead<kSwap>& transfer);
+public:
+
+ // Don't call this if GetInstance() == NULL
+ ScriptingMethodPtr GetMethod (int index) const { return m_Methods[index]; }
+
+ #if UNITY_EDITOR
+ static bool CanAssignMonoVariableStatic (const char* property, Object* object);
+ #endif
+
+ // RebuildMonoInstance should only be called by MonoScript and MonoManager
+ // Anulls the old instance
+ // And creates a new Mono representation if the script class is availible
+ // Resets m_Methods to the new class
+ void RebuildMonoInstance (ScriptingObjectPtr instance);
+ void ReleaseMonoInstance ();
+
+ #if UNITY_EDITOR
+ void SetBackup (BackupState* state) { delete m_Backup; m_Backup = state; }
+ BackupState* GetBackup () { return m_Backup; }
+ static void ExtractBackupFromInstance (MonoObject* instance, MonoClass* scriptClass, BackupState& backup, int flags);
+ static void ExtractYAMLBackupFromInstance (MonoObject* instance, MonoClass* scriptClass, BackupState& backup, int flags);
+ void RestoreInstanceStateFromBackup (BackupState& backup, int flags);
+ template<class TransferFunctor>
+ void ProcessBackupStateWhileReading (TransferFunctor& transfer);
+ #endif
+
+ void SetInstanceNULLAndCreateBackup ();
+ Coroutine* HandleCoroutineReturnValue (ScriptingMethodPtr method, ScriptingObjectPtr returnValue);
+
+ // Registers the notification receiver function
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ #if UNITY_EDITOR
+ virtual bool ShouldDisplayEnabled ();
+ static void RestartExecuteInEditModeScripts ();
+ #endif
+
+ void TransferSafeBinaryInstanceOnly (dynamic_array<UInt8>& data, const TypeTree& typeTree, int options);
+
+ ObjectGUIState& GetObjectGUIState();
+
+ bool IsScriptableObject();
+ bool IsDestroying() { return m_IsDestroying; }
+
+ List<Coroutine>& GetActiveCoroutines () { return m_ActiveCoroutines; }
+
+public:
+ bool HaveAudioCallback() const;
+ void SetByPassOnDSP(bool state);
+#if ENABLE_AUDIO_FMOD
+ FMOD::DSP* GetDSP() const;
+ FMOD::DSP* GetOrCreateDSP();
+ AudioCustomFilter* GetAudioCustomFilter() { return m_AudioCustomFilter; }
+#endif
+
+private:
+
+ void AddBehaviourCallbacksToManagers ();
+ void AddExternalDependencyCallbacksToManagers ();
+ void CallAwake ();
+ void DeprecatedAddToManager ();
+
+ bool GetRunInEditMode ()const;
+ bool IsPlayingOrAllowExecuteInEditMode() const;
+ std::string GetDebugDescription();
+
+ template<class TransferFunction>
+ PPtr<MonoScript> TransferEngineData (TransferFunction& transfer);
+
+ template<class TransferFunction>
+ void TransferMonoData (TransferFunction& transfer);
+
+ template<class TransferFunction>
+ static void TransferWithInstance (TransferFunction& transfer, ScriptingObjectPtr instance, ScriptingClassPtr klass);
+ template<class TransferFunction>
+ void TransferWithInstance (TransferFunction& transfer);
+
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+ void DoLivenessCheck(RemapPPtrTransfer& transfer);
+#endif
+
+ template<class TransferFunction>
+ void TransferEngineAndInstance (TransferFunction& transfer);
+
+#if UNITY_EDITOR
+ static void ExtractBackup (class SafeBinaryRead& transfer, struct BackupState& backup);
+ static void ExtractBackup (YAMLRead& transfer, BackupState& backup);
+#if UNITY_LOGIC_GRAPH
+ void LoadLogicGraphInEditor ();
+#endif
+#endif
+
+ PPtr<MonoScript> m_Script;
+
+ UnityStr m_Name;
+
+ const MonoScriptCache* m_ScriptCache;
+ const ScriptingMethodPtr* m_Methods;
+
+ List<Coroutine> m_ActiveCoroutines;
+
+ BehaviourListNode m_UpdateNode;
+ BehaviourListNode m_FixedUpdateNode;
+ BehaviourListNode m_LateUpdateNode;
+ MonoBehaviourListNode m_GUINode;
+ MonoBehaviourListNode m_OnRenderObjectNode;
+
+ // Per-monobehaviour GUI State.
+ ObjectGUIState *m_GUIState;
+
+ bool m_DidAwake;
+ bool m_DidStart;
+ bool m_UseGUILayout;
+ bool m_IsDestroying;
+
+private:
+#if ENABLE_AUDIO_FMOD
+ AudioCustomFilter* m_AudioCustomFilter;
+#endif
+
+ #if UNITY_EDITOR
+ BackupState* m_Backup;
+ UInt32 m_EditorHideFlags;
+ UnityStr m_EditorClassIdentifier;
+ #endif
+
+ friend struct Coroutine;
+ friend class MonoManager;
+};
+
+#if UNITY_EDITOR
+void BuildScriptPopupMenus (MonoBehaviour& behaviour, std::map<std::string, std::map<int, std::string> >& popups);
+void ApplyDefaultReferences (MonoBehaviour& behaviour, const std::map<UnityStr, PPtr<Object> >& data);
+#endif
+
+void ResetAndApplyDefaultReferencesOnNewMonoBehaviour(MonoBehaviour& behaviour);
+
+EXPORT_COREMODULE int GetMonoBehaviourInConstructor();
+
+#if ENABLE_MONO || UNITY_WINRT
+ScriptingArrayPtr RequiredComponentsOf(ScriptingClassPtr klass);
+ScriptingArrayPtr RequiredComponentsOf(MonoBehaviour* script);
+#endif
+
+////@TODO: THIS SHOULD BE REMOVED AND DONE WITH THREAD_SAFE / CONSTRUCTOR_SAFE tags instead!
+
+/// DISALLOW_IN_CONSTRUCTOR Raises an exception when executed from inside a MonoBehaviour constructor.
+/// eg. GetComponent uses this to make sure no one uses it in a constructor
+#if UNITY_EDITOR
+#define DISALLOW_IN_CONSTRUCTOR { \
+ if (GetMonoBehaviourInConstructor() == 0) ; else { \
+ Scripting::RaiseMonoException("You are not allowed to call this function when declaring a variable.\nMove it to the line after without a variable declaration.\nIf you are using C# don't use this function in the constructor or field initializers, Instead move initialization to the Awake or Start function."); } \
+ }
+#else
+#define DISALLOW_IN_CONSTRUCTOR { }
+#endif
+
+#endif
diff --git a/Runtime/Mono/MonoBehaviourAnimationBinding.cpp b/Runtime/Mono/MonoBehaviourAnimationBinding.cpp
new file mode 100644
index 0000000..d077acd
--- /dev/null
+++ b/Runtime/Mono/MonoBehaviourAnimationBinding.cpp
@@ -0,0 +1,66 @@
+#include "UnityPrefix.h"
+#include "Runtime/Animation/GenericAnimationBindingCache.h"
+#include "Runtime/Animation/AnimationClipBindings.h"
+#include "Runtime/Animation/BoundCurve.h"
+#include "MonoBehaviour.h"
+#include "Runtime/Interfaces/IAnimationBinding.h"
+#include "MonoScript.h"
+
+static const char* kEnabledStr = "m_Enabled";
+
+class MonoBehaviourPropertyBinding : public IAnimationBinding
+{
+public:
+
+#if UNITY_EDITOR
+ virtual void GetAllAnimatableProperties (Object& targetObject, std::vector<EditorCurveBinding>& outProperties) const
+ {
+ MonoBehaviour* beh = reinterpret_cast<MonoBehaviour *> (&targetObject);
+ MonoScript& script = *beh->GetScript ();
+
+ outProperties.push_back(EditorCurveBinding ("", ClassID(MonoBehaviour), &script, kEnabledStr, false));
+ }
+#endif
+
+ virtual float GetFloatValue (const UnityEngine::Animation::BoundCurve& bind) const
+ {
+ MonoBehaviour* mono = reinterpret_cast<MonoBehaviour*>(bind.targetObject);
+
+ return UnityEngine::Animation::AnimationBoolToFloat(mono->GetEnabled());
+ }
+
+ virtual void SetFloatValue (const UnityEngine::Animation::BoundCurve& bind, float value) const
+ {
+ MonoBehaviour* mono = reinterpret_cast<MonoBehaviour*>(bind.targetObject);
+
+ mono->SetEnabled(UnityEngine::Animation::AnimationFloatToBool(value));
+ }
+
+ virtual void SetPPtrValue (const UnityEngine::Animation::BoundCurve& bound, SInt32 value) const { }
+
+ virtual SInt32 GetPPtrValue (const UnityEngine::Animation::BoundCurve& bound) const { return 0;}
+
+ virtual bool GenerateBinding (const UnityStr& attribute, bool pptrCurve, UnityEngine::Animation::GenericBinding& outputBinding) const
+ {
+ return attribute == kEnabledStr && !pptrCurve;
+ }
+
+ virtual ClassIDType BindValue (Object& target, const UnityEngine::Animation::GenericBinding& inputBinding, UnityEngine::Animation::BoundCurve& bound) const
+ {
+ return ClassID(bool);
+ }
+};
+
+static MonoBehaviourPropertyBinding* gBinding = NULL;
+
+void InitializeMonoBehaviourAnimationBindingInterface ()
+{
+ gBinding = UNITY_NEW (MonoBehaviourPropertyBinding, kMemAnimation);
+ UnityEngine::Animation::GetGenericAnimationBindingCache ().RegisterIAnimationBinding (ClassID(MonoBehaviour), UnityEngine::Animation::kMonoBehaviourPropertyBinding, gBinding);
+}
+
+void CleanupMonoBehaviourAnimationBindingInterface ()
+{
+ UNITY_DELETE (gBinding, kMemAnimation);
+}
+
diff --git a/Runtime/Mono/MonoBehaviourAnimationBinding.h b/Runtime/Mono/MonoBehaviourAnimationBinding.h
new file mode 100644
index 0000000..e417cbe
--- /dev/null
+++ b/Runtime/Mono/MonoBehaviourAnimationBinding.h
@@ -0,0 +1,4 @@
+#pragma once
+
+void InitializeMonoBehaviourAnimationBindingInterface ();
+void CleanupMonoBehaviourAnimationBindingInterface ();
diff --git a/Runtime/Mono/MonoBehaviourSerialization.cpp b/Runtime/Mono/MonoBehaviourSerialization.cpp
new file mode 100644
index 0000000..3c21123
--- /dev/null
+++ b/Runtime/Mono/MonoBehaviourSerialization.cpp
@@ -0,0 +1,1745 @@
+#include "UnityPrefix.h"
+#if ENABLE_SCRIPTING
+#include "MonoBehaviour.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/FileCache.h"
+#include "Runtime/Serialize/IterateTypeTree.h"
+#include "Runtime/Serialize/SerializedFile.h"
+#include "MonoScript.h"
+#include "MonoTypeSignatures.h"
+#include "MonoManager.h"
+#include "Runtime/Filters/Misc/Font.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Vector4.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Math/AnimationCurve.h"
+#include "Runtime/Math/Gradient.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "tabledefs.h"
+#include "Runtime/Mono/MonoBehaviourSerialization.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Scripting/CommonScriptingClasses.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+
+using namespace std;
+
+//Exotic inclusion of .h file that contains a lot of function bodies.
+//Only way I could figure out how to split up this 2500 line file into multiple parts.
+//splitting it up normally doesn't work, because all invocations of a template function
+//need to be in the same compilation unit, and this serializationcode is all template functions
+//calling other template functions.
+#include "Runtime/Mono/MonoBehaviourSerialization_Array.h"
+
+// Shoudl not be used!!!
+#include "Runtime/GameCode/CallDelayed.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/GUIDPersistentManager.h"
+#include "Editor/Src/Application.h"
+#include "Editor/Src/Utility/RuntimeClassHashing.h"
+#endif
+
+struct TransferScriptInstance;
+
+template<class TransferFunction>
+void TransferScriptData (TransferScriptInstance& info, TransferFunction& transfer);
+
+#if ENABLE_MONO
+const char* CalculateMonoPPtrTypeString (char* buffer, MonoClass* klass)
+{
+ AssertIf(buffer == NULL);
+
+ // Potential buffer overflow
+ char* c = buffer;
+ *c++ = 'P';
+ *c++ = 'P';
+ *c++ = 't';
+ *c++ = 'r';
+ *c++ = '<';
+ *c++ = '$';
+
+ const char* className = mono_class_get_name(klass);
+ while (*className)
+ {
+ *c = *className;
+ c++;
+ className++;
+ }
+
+ *c++ = '>';
+ *c++ = '\0';
+ return buffer;
+}
+
+#if SUPPORT_TEXT_SERIALIZATION
+YAMLNode* ConvertBackupToYAML (BackupState& binary);
+
+template<>
+class YAMLSerializeTraits<MonoPPtr> : public YAMLSerializeTraits<PPtr<Object> >
+{
+};
+#endif
+
+template<>
+class SerializeTraits<MonoPPtr> : public SerializeTraitsBase<MonoPPtr>
+{
+public:
+
+ typedef MonoPPtr value_type;
+
+ inline static const char* GetTypeString (void* data)
+ {
+ MonoPPtr* ptr = reinterpret_cast<MonoPPtr*> (data);
+ AssertIf(ptr == NULL);
+ // Needed for arrays (Could write custom Array & Traits class but thats a lot of work)
+ if (ptr->m_Buffer == NULL)
+ return "PPtr<$>";
+
+ return CalculateMonoPPtrTypeString(ptr->m_Buffer, ptr->m_Class);
+ }
+
+ inline static bool IsAnimationChannel () { return false; }
+ inline static bool MightContainPPtr () { return true; }
+ inline static bool AllowTransferOptimization () { return false; }
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ data.Transfer (transfer);
+ }
+};
+
+template<>
+struct RemapPPtrTraits<MonoPPtr>
+{
+ static bool IsPPtr () { return true; }
+ static int GetInstanceIDFromPPtr (const MonoPPtr& data) { return data.GetInstanceID (); }
+ static void SetInstanceIDOfPPtr (MonoPPtr& data, SInt32 instanceID) { data.SetInstanceID (instanceID); }
+};
+
+template<class TransferFunction> inline
+void TransferPPtr (MonoObject* instance, MonoClassField* field, const char* name, MonoClass* klass, int classID, TransferFunction& transfer, TransferMetaFlags metaFlags)
+{
+ MonoPPtr pptr;
+ if (transfer.IsWritingPPtr ())
+ {
+ MonoObject* referencedObject = NULL;
+ mono_field_get_value (instance, field, &referencedObject);
+ if (referencedObject != NULL)
+ pptr.SetInstanceID (Scripting::GetInstanceIDFromScriptingWrapper (referencedObject));
+ }
+
+ // Potential buffer overflow
+ char buffer[128];
+ pptr.m_Buffer = buffer;
+ pptr.m_Class = klass;
+
+ transfer.Transfer (pptr, name, metaFlags);
+
+ if (transfer.DidReadLastPPtrProperty ())
+ {
+ MonoObject* newValue = NULL;
+ newValue = TransferPPtrToMonoObject (pptr.GetInstanceID (), klass, classID, field, instance, transfer.GetFlags() & kThreadedSerialization);
+ mono_field_set_value (instance, field, newValue);
+ }
+}
+
+template<class T>
+void TransferWithProxyElement (T& t, vector<MonoPPtr>& arr, MonoPPtr&proxy, const char* name, TransferMetaFlags metaflags)
+{
+ t.Transfer(arr, name, metaflags);
+}
+
+template<>
+void TransferWithProxyElement<ProxyTransfer> (ProxyTransfer& t, vector<MonoPPtr>& arr, MonoPPtr& proxy, const char* name, TransferMetaFlags metaflags)
+{
+ t.BeginTransfer (name, SerializeTraits<vector<MonoPPtr> >::GetTypeString (&arr), (char*)&arr, metaflags);
+ t.TransferSTLStyleArrayWithElement(proxy, kNoTransferFlags);
+ t.EndTransfer ();
+}
+
+template<class T, class TransferFunction> inline
+void TransferBuiltins (MonoObject* instance, MonoClassField* field, const char* name, TransferFunction& transfer, TransferMetaFlags metaFlags)
+{
+ T* value = reinterpret_cast<T*> (reinterpret_cast<UInt8*> (instance) + mono_field_get_offset(field));
+ transfer.Transfer (*value, name, metaFlags);
+}
+
+template<class TransferFunction> inline
+void TransferString (MonoObject* instance, MonoClassField* field, const char* name, TransferFunction& transfer, TransferMetaFlags metaFlags)
+{
+ UnityStr stdString;
+
+ if (transfer.IsWriting ())
+ {
+ MonoString *strval;
+ mono_field_get_value (instance, field, &strval);
+ char *p = mono_string_to_utf8 (strval);
+ if (p)
+ stdString = p;
+ else
+ stdString.clear ();
+ g_free (p);
+ }
+
+ transfer.Transfer (stdString, name, metaFlags);
+
+ if (transfer.DidReadLastProperty ())
+ {
+ ScriptingStringPtr monoString = scripting_string_new(stdString);
+ mono_field_set_value (instance, field, monoString);
+ }
+}
+
+
+static bool CalculateTransferPrivateVariables (MonoClass* klass)
+{
+ MonoCustomAttrInfo* attr = mono_custom_attrs_from_class(klass);
+ bool hasPrivate = attr != NULL && mono_custom_attrs_has_attr (attr, MONO_COMMON.serializePrivateVariables);
+ if (attr != NULL)
+ mono_custom_attrs_free (attr);
+ return hasPrivate;
+}
+
+#if MONO_QUALITY_ERRORS
+static void ReplacePrivateWithNULLWrapper (MonoObject* instance, MonoClassField* field)
+{
+ // This is necessary since proxy transfer will not give us an instance for array elements
+ if (instance == NULL)
+ return;
+
+ // This way Mono won't throw an exception when an object is NULL instead the C++ side can handle it.
+ MonoType* monoType = mono_field_get_type (field);
+ int type = mono_type_get_type (monoType);
+
+ if (type == MONO_TYPE_CLASS)
+ {
+ MonoClass* referencedClass = mono_type_get_class_or_element_class (monoType);
+ int classID = Scripting::GetClassIDFromScriptingClass (referencedClass);
+ if (classID != ClassID (MonoBehaviour) && classID != -1)
+ {
+ MonoObject* referencedObject;
+ mono_field_get_value (instance, field, &referencedObject);
+ if (referencedObject == NULL)
+ {
+ referencedObject = Scripting::ScriptingObjectNULL (referencedClass);
+ mono_field_set_value (instance, field, referencedObject);
+ }
+ }
+ }
+}
+
+static MonoObject* PrepareRefcountedTransfer (MonoClassField* field, MonoClass* referencedClass, MonoObject* instance)
+{
+ MonoObject* referencedObject = NULL;
+ if (instance != NULL)
+ mono_field_get_value (instance, field, &referencedObject);
+
+ if (referencedObject == NULL)
+ {
+ referencedObject = ScriptingInstantiateObject (referencedClass);
+ mono_runtime_object_init_log_exception (referencedObject);
+ if (instance != NULL)
+ mono_field_set_value (instance, field, referencedObject);
+ }
+
+ return referencedObject;
+}
+#endif
+
+static MonoObject* PrepareTransfer (ScriptingClass* scriptingClass, MonoClassField* field, MonoObject* instance)
+{
+ MonoObject* value = NULL;
+ if (instance)
+ {
+ mono_field_get_value (instance, field, &value);
+ if (value == NULL)
+ {
+ value = ScriptingInstantiateObject (scriptingClass); mono_runtime_object_init_log_exception (value);
+ mono_field_set_value (instance, field, value);
+ }
+ }
+ return value;
+}
+
+static AnimationCurve* PrepareAnimationCurveTransfer (MonoClassField* field, MonoObject* instance)
+ {
+ MonoObject* monoObject = PrepareTransfer (MONO_COMMON.animationCurve, field, instance);
+ if (monoObject)
+ return ExtractMonoObjectData<AnimationCurve*>(monoObject);
+ return NULL;
+ }
+
+
+static GradientNEW* PrepareGradientTransfer (MonoClassField* field, MonoObject* instance)
+{
+ MonoObject* monoObject = PrepareTransfer (MONO_COMMON.gradient, field, instance);
+ if (monoObject)
+ return ExtractMonoObjectData<GradientNEW*>(monoObject);
+ return NULL;
+}
+
+static RectOffset* PrepareRectOffsetTransfer (MonoClassField* field, MonoObject* instance)
+{
+ MonoObject* monoObject = PrepareTransfer (MONO_COMMON.rectOffset, field, instance);
+ if (monoObject)
+ return ExtractMonoObjectData<RectOffset*>(monoObject);
+ return NULL;
+}
+
+static GUIStyle* PrepareGUIStyleTransfer (MonoClassField* field, MonoObject* instance)
+{
+ MonoObject* monoObject = PrepareTransfer (MONO_COMMON.guiStyle, field, instance);
+ if (monoObject)
+ return ExtractMonoObjectData<GUIStyle*>(monoObject);
+ return NULL;
+}
+
+/// Returns only true on serializable classes.
+/// But only if we are in the same assembly.
+static bool PrepareTransferEmbeddedClass (MonoClassField* field, MonoClass* referencedClass, MonoObject* instance, TransferScriptInstance& output, int currentDepth)
+{
+ if (!PrepareTransferEmbeddedClassCommonChecks(referencedClass))
+ return false;
+
+ MonoObject* referencedObject = NULL;
+ if (instance)
+ {
+ mono_field_get_value (instance, field, &referencedObject);
+
+ if (referencedObject == NULL)
+ {
+ referencedObject = ScriptingInstantiateObject (referencedClass);
+ mono_runtime_object_init_log_exception (referencedObject);
+ mono_field_set_value (instance, field, referencedObject);
+ }
+ }
+ else
+ {
+ referencedObject = ScriptingInstantiateObject (referencedClass);
+ }
+
+ output.instance = referencedObject;
+ output.klass = referencedClass;
+ output.transferPrivate = CalculateTransferPrivateVariables(referencedClass);
+ output.commonClasses = &GetMonoManager().GetCommonClasses();
+ output.depthCounter = currentDepth + 1;
+ Assert(currentDepth >= 0);
+
+ return referencedObject != NULL;
+}
+
+static bool HasAttribute(MonoClass* klass, MonoClassField *field, ScriptingClass* attributeClass)
+{
+ MonoCustomAttrInfo* attr = mono_custom_attrs_from_field (klass, field);
+
+ if (attr == NULL)
+ return false;
+
+ bool has = mono_custom_attrs_has_attr (attr, attributeClass);
+ mono_custom_attrs_free(attr);
+ return has;
+}
+
+// Want to keep this logging ability around for some time, say until 2013
+//#define TF_LOG(...) printf_console (__VA_ARGS__)
+#define TF_LOG(...)
+
+#if UNITY_EDITOR
+
+// Return true if this field should be skipped and not serialized
+static bool ProcessClassFields (char const* name, MonoType* monoType, cil::SerializeTracker& targetFields)
+{
+ char* typeName = mono_type_get_name_full (monoType, MONO_TYPE_NAME_FORMAT_IL);
+
+ // In case target doesn't have any more fields, skip the current one
+ if (!targetFields.IsFieldValid ())
+ {
+ g_free (typeName);
+ return false;
+ }
+
+ // The field doesn't match the current in the target structure, skip
+ if (!targetFields.IsCurrent (typeName, name))
+ {
+ // It can also be that target has more fields than source, but we catch that before
+ // we start to serialize, so this shouldn't happen.
+ if (targetFields.HasField (typeName, name))
+ {
+ while (targetFields.IsFieldValid () && !targetFields.IsCurrent (typeName, name))
+ {
+ cil::TypeDB::Field const& dfield = targetFields.CurrentField();
+
+ // Should not happen!
+ WarningString (Format ("Unable to properly serialize object for the player of class '%s' because of extra field '%s' of type '%s' (expecting '%s' '%s')", typeName, dfield.name.c_str(), dfield.typeName.c_str(), typeName, name
+ ));
+ TF_LOG (" - %s %s [EXTRA IN TARGET!]\n", dfield.typeName.c_str(), dfield.name.c_str());
+ ++targetFields;
+ }
+ }
+ else
+ {
+ TF_LOG (" - %s %s [SKIPPED!]\n", typeName, name);
+ g_free (typeName);
+ return false;
+ }
+ }
+ else
+ ++targetFields;
+
+ TF_LOG (" - %s %s\n", typeName, name);
+
+ g_free (typeName);
+ return true;
+}
+
+#endif // UNITY_EDITOR
+
+// Keep the traversal behaviour in sync with HashValueTypes (ScriptingTypePtr klass)
+template<class TransferFunction>
+void TransferScriptData (TransferScriptInstance& info, TransferFunction& transfer)
+{
+ Assert(info.depthCounter >= 0);
+
+ MonoObject* instance = info.instance;
+ MonoClass* klass = info.klass;
+ const CommonScriptingClasses& commonClasses = *info.commonClasses;
+
+ if (transfer.IsSerializingForGameRelease ())
+ {
+ TF_LOG ("Serializing class: %s\n", klass ? mono_class_get_name(klass) : "NULL");
+ }
+
+ // Recurse into parent classes stop when we reach the monobehaviour class
+ MonoClass* parentClass = mono_class_get_parent (klass);
+ if (parentClass && parentClass != commonClasses.monoBehaviour && parentClass != commonClasses.scriptableObject)
+ {
+ TransferScriptInstance parent = info;
+ parent.klass = parentClass;
+ parent.transferPrivate = CalculateTransferPrivateVariables(parentClass);
+ TransferScriptData (parent, transfer);
+ }
+
+#if UNITY_EDITOR
+ cil::SerializeTracker targetFields (cil::g_CurrentTargetTypeDB, klass);
+#endif
+
+ MonoClassField *field;
+ void* iter = NULL;
+ while ((field = mono_class_get_fields (klass, &iter)))
+ {
+ TransferMetaFlags metaFlags = kSimpleEditorMask;
+ // Exclude const attributes
+ int flags = mono_field_get_flags (field);
+ if (flags & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_INIT_ONLY | FIELD_ATTRIBUTE_NOT_SERIALIZED))
+ continue;
+
+ if ( ((flags & (FIELD_ATTRIBUTE_PRIVATE)) || (flags & 0xF) == FIELD_ATTRIBUTE_FAMILY) && !info.transferPrivate &&
+ !HasAttribute(klass, field, commonClasses.serializeField))
+ {
+#if UNITY_EDITOR
+ if (transfer.GetFlags () & kSerializeDebugProperties)
+ metaFlags = kDebugPropertyMask | kNotEditableMask;
+ else
+ {
+ // @todo: This code should be removed once mono has better null ptr exception handling.
+ // We set all private ptr's that are NULL to be Object wrappers with instanceID 0.
+ // This way Mono won't throw an exception when an object is NULL instead the C++ side can handle it.
+ ReplacePrivateWithNULLWrapper (instance, field);
+ continue;
+ }
+#else
+ continue;
+#endif
+ }
+
+ /// FINALLY MAKE THE SERIALIZATION CODE UNDERSTANDABLE!!!!!!!!!
+ /// I would like to be able to check if we actually need the name!!!!!!!!!!!
+ MonoType* monoType = mono_field_get_type (field);
+ int type = mono_type_get_type (monoType);
+ const char* name = mono_field_get_name (field);
+
+#if UNITY_EDITOR
+ // Skip fields that are not available in the 'player' script
+ if (transfer.IsSerializingForGameRelease () && targetFields.IsClassValid ())
+ if (!ProcessClassFields (name, monoType, targetFields))
+ continue;
+#endif
+
+#if UNITY_EDITOR
+ if (transfer.NeedNonCriticalMetaFlags () && HasAttribute(klass, field, commonClasses.hideInInspector))
+ metaFlags |= kHideInEditorMask;
+ // Make sure serialized property name does not contain any '.' characters.
+ // This can happen when serializing internal backing values of C# implicit
+ // properties, to which mono will add the namespace path (only relevant for
+ // the debug inspector).
+ std::string strippedName;
+ if (strchr(name, '.'))
+ {
+ const char *ch = name;
+ while (*ch != '\0')
+ {
+ strippedName += (*ch != '.')?*ch:'_';
+ ch++;
+ }
+ name = strippedName.c_str();
+ }
+
+ if (transfer.NeedNonCriticalMetaFlags () && HasAttribute(klass, field, commonClasses.hideInInspector))
+ metaFlags |= kHideInEditorMask;
+#endif
+
+ switch (type)
+ {
+ case MONO_TYPE_STRING:
+ TransferString (instance, field, name, transfer, metaFlags);
+ break;
+ case MONO_TYPE_I4:
+ TransferBuiltins<SInt32> (instance, field, name, transfer, metaFlags);
+ break;
+ case MONO_TYPE_R4:
+ TransferBuiltins<float> (instance, field, name, transfer, metaFlags);
+ break;
+ case MONO_TYPE_BOOLEAN:
+ TransferBuiltins<UInt8> (instance, field, name, transfer, metaFlags | kEditorDisplaysCheckBoxMask);
+ transfer.Align();
+ break;
+ case MONO_TYPE_U1:
+ TransferBuiltins<UInt8> (instance, field, name, transfer, metaFlags);
+ transfer.Align();
+ break;
+ case MONO_TYPE_R8:
+ TransferBuiltins<double> (instance, field, name, transfer, metaFlags);
+ break;
+
+
+ case MONO_TYPE_VALUETYPE:
+ {
+ MonoClass* structClass = mono_type_get_class_or_element_class (monoType);
+ if (structClass == commonClasses.vector3)
+ TransferBuiltins<Vector3f> (instance, field, name, transfer, metaFlags);
+ else if (structClass == commonClasses.vector2)
+ TransferBuiltins<Vector2f> (instance, field, name, transfer, metaFlags);
+ else if (structClass == commonClasses.vector4)
+ TransferBuiltins<Vector4f> (instance, field, name, transfer, metaFlags);
+ else if (structClass == commonClasses.rect)
+ TransferBuiltins<Rectf> (instance, field, name, transfer, metaFlags);
+ else if (structClass == commonClasses.quaternion)
+ TransferBuiltins<Quaternionf> (instance, field, name, transfer, metaFlags);
+ else if (structClass == commonClasses.matrix4x4)
+ TransferBuiltins<Matrix4x4f> (instance, field, name, transfer, metaFlags);
+ else if (structClass == commonClasses.bounds)
+ TransferBuiltins<AABB> (instance, field, name, transfer, metaFlags);
+ else if (structClass == commonClasses.color)
+ TransferBuiltins<ColorRGBAf> (instance, field, name, transfer, metaFlags);
+ else if (structClass == commonClasses.color32)
+ TransferBuiltins<ColorRGBA32> (instance, field, name, transfer, metaFlags);
+ else if (structClass == commonClasses.layerMask)
+ TransferBuiltins<BitField> (instance, field, name, transfer, metaFlags);
+ else if (mono_class_is_enum (structClass))
+ {
+ MonoType* enumMonoType = mono_class_enum_basetype (structClass);
+ int enumType = mono_type_get_type (enumMonoType);
+ switch (enumType)
+ {
+ case MONO_TYPE_I4:
+ TransferBuiltins<SInt32> (instance, field, name, transfer, metaFlags);
+ break;
+ case MONO_TYPE_U1:
+ TransferBuiltins<UInt8> (instance, field, name, transfer, metaFlags);
+ transfer.Align();
+ break;
+ default:
+ ErrorString (ErrorMessageForUnsupportedEnumField(monoType, mono_class_get_type(klass), name));
+ break;
+ }
+ }
+#if UNITY_EDITOR
+ else if ((transfer.GetFlags () & kSerializeMonoReload) && structClass == commonClasses.monoReloadableIntPtr)
+ {
+ TransferBuiltins<UIntPtr> (instance, field, name, transfer, metaFlags);
+ }
+ else if ((transfer.GetFlags () & kSerializeMonoReload) && structClass == commonClasses.monoReloadableIntPtrClear)
+ {
+ TransferBuiltins<UIntPtr> (instance, field, name, transfer, metaFlags);
+ // Clear value after writing, so that objects referencing it won't try to delete the C++ object
+ if (transfer.IsWriting())
+ {
+ void** reloadValue = reinterpret_cast<void**> (reinterpret_cast<UInt8*> (instance) + mono_field_get_offset(field));
+ *reloadValue = NULL;
+ }
+ }
+#endif
+ }
+ break;
+
+ case MONO_TYPE_CLASS:
+ {
+ // Serialize pptr
+ MonoClass* referencedClass = mono_type_get_class_or_element_class (monoType);
+
+ // Do not serialize delegates
+ // Most delegates are not serializable, but Boo generates delegate classes
+ // that are marked as serializable
+ if (mono_class_is_subclass_of (referencedClass, MONO_COMMON.multicastDelegate, false))
+ continue;
+
+ int classID = Scripting::GetClassIDFromScriptingClass (referencedClass);
+ if (classID != -1)
+ {
+ TransferPPtr (instance, field, name, referencedClass, classID, transfer, metaFlags);
+ }
+ else if (referencedClass == commonClasses.animationCurve)
+ {
+ AnimationCurve* curve = PrepareAnimationCurveTransfer(field, instance);
+ transfer.Transfer (*curve, name, metaFlags);
+ }
+ else if (referencedClass == commonClasses.gradient)
+ {
+ GradientNEW* gradient = PrepareGradientTransfer (field, instance);
+ transfer.Transfer (*gradient, name, metaFlags);
+ }
+ else if (referencedClass == commonClasses.rectOffset)
+ {
+ RectOffset* offset = PrepareRectOffsetTransfer(field, instance);
+ transfer.Transfer (*offset, name, metaFlags);
+ }
+ else if (referencedClass == commonClasses.guiStyle)
+ {
+ GUIStyle* style = PrepareGUIStyleTransfer(field, instance);
+ transfer.Transfer (*style, name, metaFlags);
+ }
+ // Serialize embedded class, but only if it has attribute serializable
+ else
+ {
+ // Unfortunately we don't support cycles.
+ if (referencedClass == klass)
+ break;
+
+ // Make sure we don't endless loop. Put a hard cap limit on the level of nested types until we have a better general solution.
+ if (info.depthCounter > kClassSerializationDepthLimit)
+ return;
+
+ TransferScriptInstance transferScriptInstance;
+ if (PrepareTransferEmbeddedClass (field, referencedClass, instance, transferScriptInstance, info.depthCounter))
+ {
+ transfer.TransferWithTypeString(transferScriptInstance, name, mono_class_get_name(referencedClass), metaFlags);
+ }
+ }
+ }
+ break;
+
+ case MONO_TYPE_GENERICINST:
+ case MONO_TYPE_SZARRAY:
+ {
+ TransferFieldOfTypeArray(instance, klass, field, name, info, transfer, monoType, type, metaFlags);
+ }
+ break;
+
+ default:
+
+ // printf_console ("unsupported type");
+
+ break;
+
+ };
+ }
+
+ if (transfer.IsReading() || transfer.IsReadingPPtr())
+ ApplyScriptDataModified(info);
+}
+#endif //ENABLE_MONO
+/// Mono Backup
+/// Problem: Sometimes the dll is not loadable or a script class can't be loaded
+/// We don't want to lose the data when loading from disk so we store it in a temporary backup until the class appears again.
+
+/// We now directly get the backup from the serialized information if we can't store it directly into the mono representation
+/// This makes the code hard because we need to individually support every single transfer function.
+/// Also we need to manually remap pptrs into instance id's
+///
+
+template<class TransferFunction>
+PPtr<MonoScript> MonoBehaviour::TransferEngineData (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ PPtr<MonoScript> newScript = m_Script;
+#if UNITY_EDITOR
+ if (!transfer.IsSerializingForGameRelease())
+ transfer.Transfer(m_EditorHideFlags, "m_EditorHideFlags", kHideInEditorMask);
+
+ if (SerializePrefabIgnoreProperties(transfer))
+ {
+ TransferMetaFlags mask = kNoTransferFlags;
+ if (m_EditorHideFlags & kHideScriptPPtr)
+ mask |= kHideInEditorMask;
+ transfer.Transfer (newScript, "m_Script", mask);
+ }
+#else
+ transfer.Transfer (newScript, "m_Script");
+#endif
+
+ transfer.Transfer(m_Name, "m_Name", kHideInEditorMask);
+ TRANSFER_EDITOR_ONLY_HIDDEN(m_EditorClassIdentifier);
+ return newScript;
+}
+
+
+std::string MonoBehaviour::GetDebugDescription()
+{
+ return Format("id:%d, script:%s",GetInstanceID(), GetScriptClassName().c_str());
+}
+
+#if ENABLE_MONO && !ENABLE_SERIALIZATION_BY_CODEGENERATION
+template<class TransferFunction>
+inline void MonoBehaviour::TransferWithInstance (TransferFunction& transfer)
+{
+ TransferWithInstance (transfer, GetInstance (), GetClass());
+}
+
+template<class TransferFunction>
+void MonoBehaviour::TransferWithInstance (TransferFunction& transfer, ScriptingObjectPtr instance, ScriptingClassPtr klass)
+{
+ SET_ALLOC_OWNER(s_MonoDomainContainer);
+
+ AssertIf (instance == NULL);
+ AssertIf (klass == NULL);
+
+ TransferScriptInstance referencedInstance;
+ referencedInstance.instance = instance;
+ referencedInstance.klass = klass;
+ referencedInstance.commonClasses = &GetMonoManager ().GetCommonClasses ();
+ referencedInstance.transferPrivate = CalculateTransferPrivateVariables(klass);
+ referencedInstance.depthCounter = 0;
+
+ TransferScriptData (referencedInstance, transfer);
+}
+#endif
+
+template<class TransferFunction>
+void MonoBehaviour::TransferEngineAndInstance (TransferFunction& transfer)
+{
+ PPtr<MonoScript> newScript = TransferEngineData (transfer);
+ if (transfer.IsReadingPPtr ())
+ SetScript (newScript);
+ if (GetInstance())
+ {
+ TransferWithInstance (transfer);
+ }
+}
+
+
+void MonoBehaviour::SetInstanceNULLAndCreateBackup ()
+{
+ if (GetInstance())
+ {
+ GetDelayedCallManager().CancelAllCallDelayed( this );
+
+#if UNITY_EDITOR
+ SetBackup (new BackupState ());
+ ExtractBackupFromInstance (GetInstance(), GetClass(), *m_Backup, 0);
+#endif
+
+ ReleaseMonoInstance ();
+ }
+}
+
+/// Simple Player specific serialization
+#if !UNITY_EDITOR
+
+#if SUPPORT_SERIALIZED_TYPETREES
+void MonoBehaviour::VirtualRedirectTransfer (SafeBinaryRead& transfer)
+{
+ SET_ALLOC_OWNER(this);
+ transfer.BeginTransfer ("Base", MonoBehaviour::GetTypeString(), NULL);
+
+ PPtr<MonoScript> newScript = TransferEngineData (transfer);
+ if (transfer.IsReadingPPtr ())
+ SetScript (newScript);
+
+ if (GetInstance())
+ {
+ transfer.OverrideRootTypeName(scripting_class_get_name(GetClass()));
+ TransferWithInstance (transfer);
+ }
+
+ transfer.EndTransfer ();
+}
+
+void MonoBehaviour::VirtualRedirectTransfer (StreamedBinaryRead<true>& transfer)
+{
+ SET_ALLOC_OWNER(this);
+ TransferEngineAndInstance(transfer);
+}
+
+#endif
+
+void MonoBehaviour::VirtualRedirectTransfer (StreamedBinaryWrite<false>& transfer)
+{
+ TransferEngineAndInstance(transfer);
+}
+
+void MonoBehaviour::VirtualRedirectTransfer (StreamedBinaryRead<false>& transfer)
+{
+ SET_ALLOC_OWNER(this);
+ TransferEngineAndInstance(transfer);
+}
+
+
+void MonoBehaviour::VirtualRedirectTransfer (ProxyTransfer& transfer)
+{
+#if !UNITY_FLASH && !UNITY_WINRT && !ENABLE_SERIALIZATION_BY_CODEGENERATION
+ transfer.BeginTransfer ("Base", MonoBehaviour::GetTypeString(), NULL, kNoTransferFlags);
+
+ TransferEngineAndInstance(transfer);
+
+ transfer.EndTransfer ();
+#endif
+}
+
+
+void MonoBehaviour::VirtualRedirectTransfer (RemapPPtrTransfer& transfer)
+{
+ TransferEngineAndInstance (transfer);
+}
+
+
+/// Unity Editor specific serialization
+#else // #if UNITY_EDITOR
+
+void ApplyDefaultReferences (MonoBehaviour& behaviour, const map<UnityStr, PPtr<Object> >& data)
+{
+ MonoObject* instance = behaviour.GetInstance();
+ if (instance == NULL)
+ return;
+
+ const CommonScriptingClasses& commonClasses = MONO_COMMON;
+ MonoClass* klass = mono_object_get_class(behaviour.GetInstance());
+ while (klass && klass != commonClasses.monoBehaviour && klass != commonClasses.scriptableObject)
+ {
+ MonoClassField *field;
+ void* iter = NULL;
+ while ((field = mono_class_get_fields (klass, &iter)))
+ {
+ int flags = mono_field_get_flags (field);
+
+ // Ignore static / nonserialized
+ if (flags & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_INIT_ONLY | FIELD_ATTRIBUTE_NOT_SERIALIZED))
+ continue;
+
+ // only public fields, or marked as serialize field
+ if ( ((flags & (FIELD_ATTRIBUTE_PRIVATE)) || (flags & 0xF) == FIELD_ATTRIBUTE_FAMILY) && !HasAttribute(klass, field, commonClasses.serializeField))
+ continue;
+
+ // Only pptrs on the root level
+ MonoType* monoType = mono_field_get_type (field);
+ int type = mono_type_get_type (monoType);
+ if (type == MONO_TYPE_CLASS)
+ {
+ MonoClass* referencedClass = mono_type_get_class_or_element_class (monoType);
+ const char* name = mono_field_get_name (field);
+ map<UnityStr, PPtr<Object> >::const_iterator found = data.find(name);
+ if (found != data.end())
+ {
+ // Check if the target variable inherits from the class
+ MonoObject* target = Scripting::ScriptingWrapperFor(found->second);
+ if (target && mono_class_is_subclass_of(mono_object_get_class(target), referencedClass, false))
+ {
+ // Never override pptr values
+ MonoObject* oldTarget;
+ mono_field_get_value (instance, field, &oldTarget);
+ if (Scripting::GetInstanceIDFromScriptingWrapper(oldTarget) == 0)
+ mono_field_set_value (instance, field, target);
+ }
+ }
+ }
+ }
+
+ klass = mono_class_get_parent (klass);
+ }
+}
+
+struct FileToMemoryID
+{
+ bool operator () (const TypeTree& typeTree, dynamic_array<UInt8>& data, int bytePosition)
+ {
+ if (!IsTypeTreePPtr (typeTree))
+ return true;
+
+ if (typeTree.m_ByteSize == 12)
+ {
+ LocalSerializedObjectIdentifier* pptrData = reinterpret_cast<LocalSerializedObjectIdentifier*> (&data[bytePosition]);
+ SInt32 instanceID = 0;
+ LocalSerializedObjectIdentifierToInstanceID (*pptrData, instanceID);
+ pptrData->localSerializedFileIndex = instanceID;
+ pptrData->localIdentifierInFile = 0;
+ }
+ else
+ {
+ SInt32* pptrData = reinterpret_cast<SInt32*> (&data[bytePosition]);
+ SInt32 instanceID = 0;
+ LocalSerializedObjectIdentifier identifier;
+ identifier.localSerializedFileIndex = pptrData[0];
+ identifier.localIdentifierInFile = pptrData[1];
+
+ LocalSerializedObjectIdentifierToInstanceID (identifier, instanceID);
+
+ pptrData[0] = instanceID;
+ pptrData[1] = 0;
+
+ }
+ return true;
+ }
+};
+
+struct MemoryIDToFileID
+{
+ bool operator () (const TypeTree& typeTree, dynamic_array<UInt8>& data, int bytePosition)
+ {
+ if (!IsTypeTreePPtr (typeTree))
+ return true;
+
+ if (typeTree.m_ByteSize == 12)
+ {
+ LocalSerializedObjectIdentifier* pptrData = reinterpret_cast<LocalSerializedObjectIdentifier*> (&data[bytePosition]);
+ SInt32 instanceID = pptrData->localSerializedFileIndex;
+ InstanceIDToLocalSerializedObjectIdentifier (instanceID, *pptrData);
+ }
+ else
+ {
+ SInt32* pptrData = reinterpret_cast<SInt32*> (&data[bytePosition]);
+
+ LocalSerializedObjectIdentifier identifier;
+ InstanceIDToLocalSerializedObjectIdentifier (pptrData[0], identifier);
+
+ pptrData[0] = identifier.localSerializedFileIndex;
+ pptrData[1] = identifier.localIdentifierInFile;
+ }
+ return true;
+ }
+};
+
+static int SkipString(CachedReader &cache, int basePos, int byteOffset, bool swapEndian, const TypeTree& type)
+{
+ UInt32 nameLength = 0;
+ cache.SetPosition(basePos + byteOffset);
+ cache.Read(&nameLength, sizeof(nameLength));
+ if (swapEndian)
+ SwapEndianBytes(nameLength);
+
+ byteOffset += sizeof(nameLength)+nameLength;
+ if (type.m_MetaFlag & kAlignBytesFlag || type.m_Children.back().m_MetaFlag & kAlignBytesFlag)
+ byteOffset = Align4(byteOffset);
+
+ return byteOffset;
+}
+
+
+static bool IsTypeTreeProperMonoBehaviour (const TypeTree& typeTree, int baseSize, int* byteOffset, int basePos, int* numberOfEngineChildren, CachedReader& cache, bool swapEndian)
+{
+ *byteOffset = 0;
+ *numberOfEngineChildren = 0;
+
+ TypeTree::const_iterator i = typeTree.m_Children.begin ();
+
+ if (i != typeTree.end() && i->m_Name == "m_ObjectHideFlags")
+ {
+ *byteOffset += i->m_ByteSize;
+ *numberOfEngineChildren += 1;
+ i++;
+ }
+
+ if (i != typeTree.end() && i->m_Name == "m_ExtensionPtr")
+ {
+ *byteOffset += i->m_ByteSize;
+ *numberOfEngineChildren += 1;
+ i++;
+ }
+
+ if (i != typeTree.end() && i->m_Name == "m_PrefabParentObject")
+ {
+ *byteOffset += i->m_ByteSize;
+ *numberOfEngineChildren += 1;
+ i++;
+ }
+
+ if (i != typeTree.end() && i->m_Name == "m_PrefabInternal")
+ {
+ *byteOffset += i->m_ByteSize;
+ *numberOfEngineChildren += 1;
+ i++;
+ }
+
+ if (i != typeTree.end() && i->m_Name == "m_GameObject")
+ {
+ *byteOffset += i->m_ByteSize;
+ *numberOfEngineChildren += 1;
+ i++;
+ }
+ else
+ return false;
+
+ if (i != typeTree.end() && i->m_Name == "m_Enabled")
+ {
+ *byteOffset += i->m_ByteSize;
+ if (i->m_MetaFlag & kAlignBytesFlag)
+ *byteOffset = Align4(*byteOffset);
+ *numberOfEngineChildren += 1;
+ i++;
+ }
+ else
+ return false;
+
+ if (i != typeTree.end() && i->m_Name == "m_EditorHideFlags")
+ {
+ *byteOffset += i->m_ByteSize;
+ *numberOfEngineChildren += 1;
+ i++;
+ }
+
+ if (i != typeTree.end() && i->m_Name == "m_Script")
+ {
+ *byteOffset += i->m_ByteSize;
+ *numberOfEngineChildren += 1;
+ i++;
+ }
+ else
+ return false;
+
+ if (i != typeTree.end() && i->m_Name == "m_Name")
+ {
+ *byteOffset = SkipString(cache, basePos, *byteOffset, swapEndian, *i);
+
+ *numberOfEngineChildren += 1;
+ i++;
+ }
+
+#if UNITY_EDITOR
+ if (i != typeTree.end() && i->m_Name == "m_EditorClassIdentifier")
+ {
+ *byteOffset = SkipString(cache, basePos, *byteOffset, swapEndian, *i);
+
+ *numberOfEngineChildren += 1;
+ i++;
+ }
+#endif
+
+ return baseSize >= *byteOffset;
+}
+
+void MonoBehaviour::ExtractBackup (SafeBinaryRead& transfer, BackupState& backup)
+{
+ backup.loadedFromDisk = true;
+
+ const TypeTree& sourceType = *transfer.m_OldBaseType;
+ int byteOffset;
+ int numberOfEngineChildren;
+ if (IsTypeTreeProperMonoBehaviour (sourceType, transfer.m_BaseByteSize, &byteOffset, transfer.m_BaseBytePosition, &numberOfEngineChildren, transfer.m_Cache, transfer.ConvertEndianess()))
+ {
+ dynamic_array<UInt8>& sourceData = backup.state;
+
+ // Extract typetree, remove all engine serialized data (m_Enabled, m_Script, m_GameObject etc)
+ backup.typeTree = sourceType;
+ TypeTree::iterator begin = backup.typeTree.m_Children.begin ();
+ TypeTree::iterator end = begin;
+ advance (end, numberOfEngineChildren);
+ RemoveFromTypeTree(backup.typeTree, begin, end);
+
+ // Extract data
+ sourceData.resize_uninitialized (transfer.m_BaseByteSize - byteOffset);
+ transfer.m_Cache.SetPosition (byteOffset + transfer.m_BaseBytePosition);
+ transfer.m_Cache.Read (sourceData.begin (), sourceData.size ());
+
+ // case 565490
+ // Remove m_EditorClassIdentifier if it is in the backup, it should not be there
+ // Remove the data from the sourceData and remove the data type from the type tree
+ // If m_EditorClassIdentifier is in the backup it will always be the first type
+ if (backup.typeTree.m_Children.size() > 0 && StrCmp(backup.typeTree.begin()->m_Name, "m_EditorClassIdentifier") == 0)
+ {
+ int skipped = 0;
+ WalkTypeTree(*backup.typeTree.m_Children.begin(), sourceData.begin(), &skipped);
+ sourceData.erase(sourceData.begin(), sourceData.begin() + skipped);
+ RemoveFromTypeTree(backup.typeTree, backup.typeTree.begin(), ++backup.typeTree.begin());
+ }
+
+
+ // Swap bytes
+ if (transfer.ConvertEndianess())
+ ByteSwapGeneric(backup.typeTree, sourceData);
+
+ if (transfer.NeedsInstanceIDRemapping ())
+ {
+ int pos = 0;
+ FileToMemoryID remap;
+ IterateTypeTree (backup.typeTree, sourceData, &pos, remap);
+ }
+ }
+ else
+ {
+ string error = "Monobehaviour has unknown format!: \n";
+ sourceType.DebugPrint (error);
+ ErrorString (error);
+ }
+}
+
+void MonoBehaviour::ExtractBackupFromInstance (MonoObject* instance, MonoClass* scriptClass, BackupState& backup, int flags)
+{
+ SET_ALLOC_OWNER(s_MonoDomainContainer);
+
+ AssertIf (instance == NULL || scriptClass == NULL);
+ // Generate type tree from instance
+ backup.typeTree = TypeTree ();
+ ProxyTransfer proxy (backup.typeTree, flags, NULL, 0);
+ proxy.BeginTransfer ("Base", mono_class_get_name(scriptClass), NULL, kNoTransferFlags);
+
+ TransferScriptInstance transferData;
+ transferData.instance = instance;
+ transferData.klass = scriptClass;
+ transferData.commonClasses = &GetMonoManager ().GetCommonClasses ();
+ transferData.transferPrivate = CalculateTransferPrivateVariables(scriptClass);
+ transferData.depthCounter = 0;
+
+ TransferScriptData (transferData, proxy);
+ proxy.EndTransfer ();
+
+ // Generate state vector
+ backup.state.clear ();
+ MemoryCacheWriter memoryCache (backup.state);
+ StreamedBinaryWrite<false> writeStream;
+ CachedWriter& writeCache = writeStream.Init (flags, BuildTargetSelection::NoTarget());
+ writeCache.InitWrite (memoryCache);
+
+ TransferScriptData (transferData, writeStream);
+
+ writeCache.CompleteWriting ();
+}
+
+void MonoBehaviour::RestoreInstanceStateFromBackup (BackupState& backup, int flags)
+{
+ if (backup.IsYaml ())
+ {
+ if (backup.HasYamlData ())
+ {
+ yaml_document_t ydoc;
+ yaml_document_initialize (&ydoc, NULL, NULL, NULL, 1, 1);
+ backup.yamlState->PopulateDocument (&ydoc);
+
+ YAMLRead transfer (&ydoc, flags);
+ TransferWithInstance (transfer);
+ yaml_document_delete (&ydoc);
+ }
+ }
+ else
+ TransferSafeBinaryInstanceOnly (backup.state, backup.typeTree, flags);
+}
+
+void MonoBehaviour::TransferSafeBinaryInstanceOnly (dynamic_array<UInt8>& data, const TypeTree& typeTree, int options)
+{
+ MemoryCacheReader memoryCache (data);
+ SafeBinaryRead readStream;
+ CachedReader& readCache = readStream.Init (typeTree, 0, data.size (), options);
+ readCache.InitRead (memoryCache, 0, data.size ());
+
+ AssertIf(GetInstance() == NULL);
+ readStream.BeginTransfer ("Base", mono_class_get_name(GetClass()), NULL);
+ TransferWithInstance (readStream);
+ readStream.EndTransfer ();
+
+ readCache.End ();
+}
+
+
+void MonoBehaviour::VirtualRedirectTransfer (SafeBinaryRead& transfer)
+{
+ SET_ALLOC_OWNER(this);
+ transfer.BeginTransfer ("Base", MonoBehaviour::GetTypeString(), NULL);
+
+ // AssertIf (GetInstance() != NULL && m_Backup != NULL);
+
+ PPtr<MonoScript> newScript = TransferEngineData (transfer);
+ if (transfer.IsReadingPPtr ())
+ {
+ if ((transfer.GetFlags() & kAutoreplaceEditorWindow) && !newScript.IsValid())
+ newScript = dynamic_instanceID_cast<MonoScript*> (GetPersistentManager().GetInstanceIDFromPathAndFileID("Library/unity default resources", 12059));
+
+ SetScript (newScript);
+ }
+
+ if (GetInstance())
+ {
+ // Override the root name to ensure that it is the same as we will be loading,
+ // since the user might have changed it in the mean time.
+ transfer.OverrideRootTypeName(mono_class_get_name(GetClass()));
+
+ TransferWithInstance (transfer);
+ }
+ else
+ ProcessBackupStateWhileReading (transfer);
+
+ transfer.EndTransfer ();
+}
+
+template <bool kSwap>
+void MonoBehaviour::VirtualRedirectTransferStreamedBinaryRead(StreamedBinaryRead<kSwap>& transfer)
+{
+ PPtr<MonoScript> newScript = TransferEngineData (transfer);
+ if (transfer.IsReadingPPtr ())
+ SetScript (newScript);
+
+ if (GetInstance())
+ TransferWithInstance (transfer);
+}
+
+void MonoBehaviour::VirtualRedirectTransfer (StreamedBinaryRead<false>& transfer)
+{
+ SET_ALLOC_OWNER(this);
+ VirtualRedirectTransferStreamedBinaryRead(transfer);
+}
+
+void MonoBehaviour::VirtualRedirectTransfer (StreamedBinaryRead<true>& transfer)
+{
+ SET_ALLOC_OWNER(this);
+ VirtualRedirectTransferStreamedBinaryRead(transfer);
+}
+
+void MonoBehaviour::VirtualRedirectTransfer (ProxyTransfer& transfer)
+{
+ transfer.BeginTransfer ("Base", MonoBehaviour::GetTypeString(), NULL, kNoTransferFlags);
+ TransferEngineData (transfer);
+
+ if (GetInstance())
+ TransferWithInstance (transfer);
+ else if (m_Backup && !transfer.IsSerializingForGameRelease())
+ {
+ AppendTypeTree (transfer.m_TypeTree, m_Backup->typeTree.begin (), m_Backup->typeTree.end ());
+ }
+
+ transfer.EndTransfer ();
+}
+
+static void ShowScriptMissingWarning(MonoBehaviour& behaviour)
+{
+ if (behaviour.GetInstance () == NULL)
+ {
+ WarningString (Format( "Script attached to '%s' in scene '%s' is missing or no valid script is attached.", behaviour.GetName(), GetApplication().GetCurrentScene().c_str() ));
+ }
+}
+
+void MonoBehaviour::VirtualRedirectTransfer (StreamedBinaryWrite<false>& transfer)
+{
+ TransferEngineData (transfer);
+
+ if (GetInstance())
+ TransferWithInstance (transfer);
+ else if (m_Backup && !transfer.IsSerializingForGameRelease() && !m_Backup->IsYaml ())
+ {
+ if (transfer.NeedsInstanceIDRemapping ())
+ {
+ dynamic_array<UInt8> sourceData = m_Backup->state;
+ int pos = 0;
+ MemoryIDToFileID remap;
+ IterateTypeTree (m_Backup->typeTree, sourceData, &pos, remap);
+ transfer.m_Cache.Write (sourceData.begin (), sourceData.size ());
+ }
+ else
+ transfer.m_Cache.Write (m_Backup->state.begin (), m_Backup->state.size ());
+ }
+
+ if (transfer.IsSerializingForGameRelease())
+ ShowScriptMissingWarning(*this);
+}
+
+void MonoBehaviour::VirtualRedirectTransfer (StreamedBinaryWrite<true>& transfer)
+{
+ TransferEngineAndInstance (transfer);
+
+ if (transfer.IsSerializingForGameRelease())
+ ShowScriptMissingWarning(*this);
+}
+
+template<class TransferFunctor>
+void MonoBehaviour::ProcessBackupStateWhileReading (TransferFunctor& transfer)
+{
+ SetBackup (new BackupState ());
+ ExtractBackup (transfer, *m_Backup);
+}
+
+char const* kRemoveNodes[] =
+{
+ "m_ObjectHideFlags", "m_ExtensionPtr", "m_PrefabParentObject", "m_PrefabInternal",
+ "m_GameObject", "m_Enabled", "m_EditorHideFlags", "m_Script", "m_Name"
+
+#if UNITY_EDITOR
+ , "m_EditorClassIdentifier"
+#endif
+};
+
+static const size_t kRemoveNodesCount = sizeof(kRemoveNodes) / sizeof(kRemoveNodes[0]);
+
+
+bool GetInstanceIDDirectlyIfAvailable (YAMLMapping* map, SInt32& instanceId)
+{
+ if (YAMLScalar* scalar = dynamic_cast<YAMLScalar*> (map->Get ("instanceID")))
+ {
+ instanceId = *scalar;
+ return true;
+ }
+
+ return false;
+}
+
+bool GetInstanceIDFromFileIDIfAvailable (YAMLMapping* map, SInt32& instanceId)
+{
+ int fileID = 0;
+
+ if (YAMLScalar* yFileId = dynamic_cast<YAMLScalar*>(map->Get("fileID")))
+ fileID = (int)*yFileId;
+ else
+ return false;
+
+ if (YAMLScalar* yGuid = dynamic_cast<YAMLScalar*>(map->Get("guid")))
+ {
+ int type = 0;
+ if (YAMLScalar* yType = dynamic_cast<YAMLScalar*>(map->Get("type")))
+ type = (int)*yType;
+
+ // GUID is present, PPtr is referencing an object from another file
+ UnityGUID guid = (UnityGUID)*yGuid;
+ std::string pathName = GetPathNameFromGUIDAndType (guid, type);
+
+ if (pathName.empty ())
+ return false;
+
+ instanceId = GetGUIDPersistentManager ().GetInstanceIDFromPathAndFileID(pathName, fileID);
+ }
+ else
+ {
+ // PPtr is referencing an object from the file we're reading
+ LocalSerializedObjectIdentifier localId;
+ localId.localIdentifierInFile = fileID;
+ GetPersistentManager ().LocalSerializedObjectIdentifierToInstanceIDInternal (localId, instanceId);
+ }
+
+ return true;
+}
+
+template<class FunctorT>
+void IterateYAMLTree (YAMLNode* node, FunctorT func)
+{
+ if (YAMLMapping* map = dynamic_cast<YAMLMapping*> (node))
+ {
+ if (func (map))
+ {
+ for (YAMLMapping::const_iterator it = map->begin (), end = map->end (); it != end; ++it)
+ IterateYAMLTree (it->second, func);
+ }
+ } else if (YAMLSequence* seq = dynamic_cast<YAMLSequence*> (node))
+ {
+ if (func (seq))
+ {
+ for (YAMLSequence::const_iterator it = seq->begin (), end = seq->end (); it != end; ++it)
+ IterateYAMLTree (*it, func);
+ }
+ } else if (YAMLScalar* scalar = dynamic_cast<YAMLScalar*> (node))
+ {
+ func (scalar);
+ }
+}
+
+struct ConvertYAMLFileIDToInstanceID
+{
+ bool operator() (YAMLNode*) const { return true; }
+
+ // We're only interested in mappings
+ bool operator() (YAMLMapping* map) const
+ {
+ SInt32 instanceId = 0;
+
+ if (GetInstanceIDFromFileIDIfAvailable (map, instanceId))
+ {
+ // Modify the mapping, changing fileID into InstanceID
+ map->Clear ();
+
+ map->Append ("instanceID", instanceId);
+
+ // We do not want to iterate this mapping anymore
+ return false;
+ }
+
+ return true;
+ }
+};
+
+struct ConvertYAMLInstanceIDToFileID
+{
+ bool m_AllowLocalIdentifierInFile;
+ ConvertYAMLInstanceIDToFileID (bool allowLocalIdentifierInFile) : m_AllowLocalIdentifierInFile (allowLocalIdentifierInFile) {}
+
+ bool operator() (YAMLNode*) const { return true; }
+
+ // We're only interested in mappings
+ bool operator() (YAMLMapping* map) const
+ {
+ SInt32 instanceID = 0;
+
+ if (GetInstanceIDDirectlyIfAvailable (map, instanceID))
+ {
+ LocalSerializedObjectIdentifier localIdentifier;
+ InstanceIDToLocalSerializedObjectIdentifier (instanceID, localIdentifier);
+
+ // Modify the mapping, changing fileID into InstanceID
+ map->Clear();
+
+ if (m_AllowLocalIdentifierInFile && localIdentifier.localSerializedFileIndex == 0)
+ {
+ map->Append ("fileID", localIdentifier.localIdentifierInFile);
+ }
+ else
+ {
+ GUIDPersistentManager& pm = GetGUIDPersistentManager();
+ pm.Lock();
+ SerializedObjectIdentifier identifier;
+ if (pm.InstanceIDToSerializedObjectIdentifier(instanceID, identifier))
+ {
+ FileIdentifier id = pm.PathIDToFileIdentifierInternal(identifier.serializedFileIndex);
+ map->Append ("fileID", identifier.localIdentifierInFile);
+ map->Append ("guid", id.guid);
+ map->Append ("type", id.type);
+ }
+ pm.Unlock();
+ }
+
+ // We do not want to iterate this mapping anymore
+ return false;
+ }
+
+ return true;
+ }
+};
+
+
+void YAMLConvertFileIDToInstanceID (YAMLMapping* map)
+{
+ ConvertYAMLFileIDToInstanceID convertor;
+ IterateYAMLTree (map, convertor);
+}
+
+void YAMLConvertInstanceIDToFileID (YAMLMapping* map, bool allowLocalIdentifierInFile)
+{
+ ConvertYAMLInstanceIDToFileID convertor (allowLocalIdentifierInFile);
+ IterateYAMLTree (map, convertor);
+}
+
+void MonoBehaviour::ExtractBackup (YAMLRead& transfer, BackupState& backup)
+{
+ backup.loadedFromDisk = true;
+ // Node graph for current object
+ YAMLNode* yroot = transfer.GetCurrentNode ();
+
+ if (YAMLMapping* monoBehaviour = dynamic_cast<YAMLMapping*> (yroot))
+ {
+ // Remove engine values
+ for (int i=0; i<kRemoveNodesCount; ++i)
+ monoBehaviour->Remove (kRemoveNodes[i]);
+
+ // case 565490
+ // Remove any duplicates of m_EditorClassIdentifier, caused by a (now fixed) bug in the backup code
+ YAMLNode* duplicateNode = NULL;
+ while ((duplicateNode = monoBehaviour->Get("m_EditorClassIdentifier")) != NULL)
+ {
+ monoBehaviour->Remove("m_EditorClassIdentifier");
+ }
+
+ // Convert PPtr from guid/fileId to instanceID
+ if (transfer.NeedsInstanceIDRemapping ())
+ YAMLConvertFileIDToInstanceID (monoBehaviour);
+ }
+
+ backup.SetYamlBackup (yroot);
+}
+
+void AppendYAMLMappingToCurrentNode (YAMLWrite& transfer, YAMLMapping* map)
+{
+ for (YAMLMapping::const_iterator it = map->begin (), end = map->end (); it != end; ++it)
+ {
+ int keyId = it->first->PopulateDocument (transfer.GetDocument ());
+ int valueId = it->second->PopulateDocument (transfer.GetDocument ());
+ yaml_document_append_mapping_pair(transfer.GetDocument (), transfer.GetCurrentNodeIndex (), keyId, valueId);
+ }
+}
+
+void MonoBehaviour::VirtualRedirectTransfer (YAMLWrite& transfer)
+{
+ transfer.BeginMetaGroup (MonoBehaviour::GetTypeString());
+ TransferEngineData (transfer);
+
+ if (GetInstance())
+ TransferWithInstance (transfer);
+ else if (m_Backup && !transfer.IsSerializingForGameRelease())
+ {
+ YAMLNode* backup = NULL;
+
+ if (m_Backup->IsYaml () && m_Backup->HasYamlData ())
+ {
+ if (transfer.NeedsInstanceIDRemapping ())
+ {
+ // We need to make a temp copy of backup state for MemoryId->FileID remapping.
+ // This is not very nice, but this code path is hit rarely and we convert through string
+ // instead of adding code to be able to clone YAMLNode.
+ std::string data = m_Backup->yamlState->EmitYAMLString ();
+ backup = ParseYAMLString (data);
+ }
+ else
+ {
+ backup = m_Backup->yamlState;
+ }
+ }
+ else if (!m_Backup->IsYaml ())
+ {
+ backup = ConvertBackupToYAML (*m_Backup);
+ }
+
+ if (YAMLMapping* monoBehaviour = dynamic_cast<YAMLMapping*> (backup))
+ {
+ if (transfer.NeedsInstanceIDRemapping ())
+ {
+ bool allowLocalIdentifier = (transfer.GetFlags () & kYamlGlobalPPtrReference) == 0;
+ YAMLConvertInstanceIDToFileID (monoBehaviour, allowLocalIdentifier);
+ }
+
+ AppendYAMLMappingToCurrentNode (transfer, monoBehaviour);
+ }
+
+ if (backup != m_Backup->yamlState)
+ delete backup;
+ }
+
+ if (transfer.IsSerializingForGameRelease() && GetInstance () == NULL)
+ {
+ WarningString (Format( "Script attached to '%s' in scene '%s' is missing or no valid script is attached.", GetName(), GetApplication().GetCurrentScene().c_str() ));
+ }
+
+ transfer.EndMetaGroup ();
+}
+
+void MonoBehaviour::VirtualRedirectTransfer (YAMLRead& transfer)
+{
+ SET_ALLOC_OWNER(this);
+ transfer.BeginMetaGroup (MonoBehaviour::GetTypeString());
+
+ PPtr<MonoScript> newScript = TransferEngineData (transfer);
+ if (transfer.IsReadingPPtr ())
+ {
+ if ((transfer.GetFlags() & kAutoreplaceEditorWindow) && !newScript.IsValid())
+ newScript = dynamic_instanceID_cast<MonoScript*> (GetPersistentManager().GetInstanceIDFromPathAndFileID("Library/unity default resources", 12059));
+
+ SetScript (newScript);
+ }
+
+ if (GetInstance())
+ TransferWithInstance (transfer);
+ else
+ ProcessBackupStateWhileReading (transfer);
+
+ transfer.EndMetaGroup ();
+}
+
+// NOTE: This functor does not assign the newly generated InstaceID over the old one!
+struct RemapPPtrFromYAMLBackup
+{
+ GenerateIDFunctor* m_GenerateIDFunctor;
+
+ RemapPPtrFromYAMLBackup (GenerateIDFunctor* functor) : m_GenerateIDFunctor (functor) {}
+
+ bool operator() (YAMLNode*) const { return true; }
+
+ // We're only interested in mappings
+ bool operator() (YAMLMapping* map) const
+ {
+ SInt32 oldInstanceID = 0;
+
+ if (GetInstanceIDDirectlyIfAvailable (map, oldInstanceID))
+ {
+ if (/*YAMLScalar* scalar =*/ dynamic_cast<YAMLScalar*> (map->Get( "instanceID")))
+ {
+ m_GenerateIDFunctor->GenerateInstanceID (oldInstanceID, kNoTransferFlags);
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+struct RemapPPtrFromBackup
+{
+ GenerateIDFunctor* idfunctor;
+
+ bool operator () (const TypeTree& typeTree, dynamic_array<UInt8>& data, int bytePosition)
+ {
+ if (!IsTypeTreePPtr (typeTree))
+ return true;
+
+ SInt32* pptrData = reinterpret_cast<SInt32*> (&data[bytePosition]);
+
+ SInt32 oldInstanceID = *pptrData;
+ SInt32 newInstanceID = idfunctor->GenerateInstanceID (oldInstanceID, typeTree.m_MetaFlag);
+ *pptrData = newInstanceID;
+ return true;
+ }
+};
+
+void MonoBehaviour::VirtualRedirectTransfer (RemapPPtrTransfer& transfer)
+{
+ PPtr<MonoScript> newScript = TransferEngineData (transfer);
+ if (transfer.IsReadingPPtr ())
+ SetScript (newScript);
+
+ if (GetInstance())
+ {
+ TransferWithInstance (transfer);
+ return;
+ }
+
+ if (!m_Backup)
+ return;
+
+ if (transfer.NeedsInstanceIDRemapping ())
+ {
+ AssertString("Impossible");
+ return;
+ }
+
+ if (!m_Backup->IsYaml ())
+ {
+ dynamic_array<UInt8> sourceData = m_Backup->state;
+ int pos = 0;
+ RemapPPtrFromBackup remap;
+ remap.idfunctor = transfer.GetGenerateIDFunctor();
+ IterateTypeTree (m_Backup->typeTree, sourceData, &pos, remap);
+ return;
+ }
+
+ if (!m_Backup->HasYamlData ())
+ return;
+
+ RemapPPtrFromYAMLBackup remap (transfer.GetGenerateIDFunctor());
+ IterateYAMLTree (m_Backup->yamlState, remap);
+}
+
+#endif // UNITY_EDITOR
+
+ScriptingObjectPtr TransferPPtrToMonoObject(int instanceID, ScriptingClassPtr klass, int classID, ScriptingFieldPtr field, ScriptingObjectPtr parentInstance, bool threadedLoading)
+{
+#if ENABLE_MONO && MONO_QUALITY_ERRORS
+ Object* obj = NULL;
+ MonoObject* instance = NULL;
+ int objClassID = 0;
+ if (!threadedLoading)
+ {
+ obj = PPtr<Object>(instanceID);
+ if (obj)
+ {
+ instance = Scripting::ScriptingWrapperFor(obj);
+ objClassID = obj->GetClassID();
+ }
+ }
+ else
+ {
+ LockObjectCreation();
+
+ obj = Object::IDToPointerNoThreadCheck(instanceID);
+ if (obj != NULL)
+ {
+ instance = Scripting::ScriptingWrapperFor(obj);
+ objClassID = obj->GetClassID();
+ UnlockObjectCreation();
+ }
+ else
+ {
+ UnlockObjectCreation();
+ obj = GetPersistentManager ().ReadObjectThreaded (instanceID);
+ if (obj != NULL)
+ {
+ instance = Scripting::ScriptingWrapperFor(obj);
+ objClassID = obj->GetClassID();
+ }
+ }
+ }
+
+ if (obj)
+ {
+ // If we got an instance and it's the right type, just return it
+ if (instance != NULL && mono_class_is_subclass_of(mono_object_get_class(instance), klass, false))
+ return instance;
+
+ // No cached object available
+ if (objClassID == ClassID (MonoBehaviour))
+ {
+ // Doesn't derive from klass, so we can use the real MonoObject
+ if (instance)
+ return NULL;
+
+ // Probably couldn't be loaded temporarily. Create fake wrapper
+ // There used by some problems with duplicating objects, but i can't think of a real world reason for this.
+ // We can probably remove it
+ if (!mono_unity_class_is_abstract(klass))
+ instance = scripting_object_new(klass);
+
+ if (!instance)
+ {
+ //if this happens, the klass was unable to initialize, often because of an exception in the klass' static constructor.
+ return NULL;
+ }
+
+ ScriptingObjectOfType<Object>(instance).SetInstanceID(instanceID);
+ return instance;
+ }
+ }
+
+ if (classID == ClassID (MonoBehaviour))
+ return NULL;
+
+ if (instanceID == 0)
+ {
+ MonoObject* obj = MonoObjectNULL(klass, UnassignedReferenceString(parentInstance, classID, field, instanceID));
+ return obj;
+ }
+
+ ScriptingObjectOfType<Object> object = mono_object_new (mono_domain_get(), klass);
+ object.SetInstanceID(instanceID);
+ object.SetError(UnassignedReferenceString(parentInstance, classID, field, instanceID));
+ return object.GetScriptingObject();
+
+#endif
+
+ return TransferPPtrToMonoObjectUnChecked(instanceID, threadedLoading);
+}
+
+ScriptingObjectPtr TransferPPtrToMonoObjectUnChecked (int instanceID, bool threadedLoading)
+{
+ if (!threadedLoading) // Non-Threaded loading path
+ return Scripting::GetScriptingWrapperForInstanceID(instanceID);
+
+ if (instanceID == 0)
+ return SCRIPTING_NULL;
+
+ LockObjectCreation();
+
+ Object* obj = Object::IDToPointerNoThreadCheck(instanceID);
+
+ UnlockObjectCreation();
+
+ if(!obj)
+ obj = GetPersistentManager().ReadObjectThreaded(instanceID);
+
+ return Scripting::ScriptingWrapperFor(obj);
+
+}
+
+#endif // ENABLE_SCRIPTING
diff --git a/Runtime/Mono/MonoBehaviourSerialization.h b/Runtime/Mono/MonoBehaviourSerialization.h
new file mode 100644
index 0000000..1e7fadb
--- /dev/null
+++ b/Runtime/Mono/MonoBehaviourSerialization.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+
+static bool CalculateTransferPrivateVariables (MonoClass* klass);
+struct BackupState;
+YAMLNode* ConvertBackupToYAML (BackupState& binary);
+
+enum { kClassSerializationDepthLimit = 7 };
+
+
+struct MonoPPtr : PPtr<Object>
+{
+ MonoPPtr () { m_Buffer = NULL; m_Class = SCRIPTING_NULL; }
+
+ char* m_Buffer;
+ ScriptingTypePtr m_Class;
+};
+
+struct TransferScriptInstance;
+
+template<class TransferFunction>
+void TransferScriptData (TransferScriptInstance& info, TransferFunction& transfer);
+
+struct TransferScriptInstance
+{
+ inline static const char* GetTypeString () { return "Generic Mono"; }
+ inline static bool IsAnimationChannel () { return false; }
+ inline static bool MightContainPPtr () { return true; }
+ inline static bool AllowTransferOptimization () { return false; }
+
+ template<class TransferFunction> inline
+ void Transfer (TransferFunction& transfer)
+ {
+ TransferScriptData (*this, transfer);
+ }
+
+ TransferScriptInstance()
+ {
+ depthCounter = -9999;
+ }
+
+ ScriptingObjectPtr instance;
+ ScriptingTypePtr klass;
+ bool transferPrivate;
+ const CommonScriptingClasses* commonClasses;
+ int depthCounter;
+};
+
+ScriptingObjectPtr TransferPPtrToMonoObjectUnChecked(int instanceID, bool threadedLoading);
+ScriptingObjectPtr TransferPPtrToMonoObject(int instanceID, ScriptingClassPtr klass, int classID, ScriptingFieldPtr field, ScriptingObjectPtr parentInstance, bool threadedLoading);
diff --git a/Runtime/Mono/MonoBehaviourSerialization_Array.h b/Runtime/Mono/MonoBehaviourSerialization_Array.h
new file mode 100644
index 0000000..b029a8c
--- /dev/null
+++ b/Runtime/Mono/MonoBehaviourSerialization_Array.h
@@ -0,0 +1,583 @@
+//This is being included by a headerfile. A bit akwardly done, but at the time it seemed like the most sane approach to be able to move serialization of arrays into its own files, to declutter the rest of monobehaviour serializationcode.
+
+#include "Runtime/Scripting/Scripting.h"
+
+#if ENABLE_MONO && !ENABLE_SERIALIZATION_BY_CODEGENERATION
+
+///@TODO: investigate if we can't get rid of the duplication of code between array transfers and non-array transfers.
+
+namespace
+{
+ template<typename ElementType>
+ struct TransferArrayHelper
+ {
+ MonoArray* array;
+ MonoObject* instance;
+ MonoClass* klass;
+ MonoClassField* field;
+ int length;
+ int elementSize;
+ unsigned* forcedSize;
+
+ vector<ElementType> vec;
+
+ TransferArrayHelper (MonoObject* instance, MonoClassField* field, MonoClass* klass, unsigned* forcedSize)
+ : array (0),
+ length (0),
+ elementSize (0),
+ forcedSize (forcedSize),
+ instance (instance),
+ field (field),
+ klass (klass)
+ {
+ }
+
+ void SetupWriting ()
+ {
+ mono_field_get_value (instance, field, &array);
+ if (array)
+ {
+ length = forcedSize != NULL ? *forcedSize : mono_array_length(array);
+ AssertIf(forcedSize && *forcedSize > mono_array_length(array));
+ elementSize = mono_class_array_element_size (klass);
+ }
+ vec.resize (length);
+ }
+
+ void SetupReading ()
+ {
+ if (array == NULL || length != vec.size())
+ array = mono_array_new (mono_domain_get (), klass, vec.size ());
+ elementSize = mono_class_array_element_size (klass);
+ }
+
+ void FinishReading ()
+ {
+ mono_field_set_value (instance, field, array);
+ if (forcedSize != NULL)
+ *forcedSize = vec.size();
+ }
+
+ template<typename T>
+ T& Get (int index)
+ {
+ return Scripting::GetScriptingArrayElement<T> (array, index);
+ }
+
+ template<typename T>
+ void Put (int index, const T& value)
+ {
+ Scripting::SetScriptingArrayElement (array, index, value);
+ }
+ };
+}
+
+
+/// TODO: Optimize away where typestring is not required
+template<class TransferFunction> inline
+void TransferArrayPPtr (MonoObject* instance, MonoClassField* field, const char* name, MonoClass* klass, int classID, TransferFunction& transfer, unsigned* forcedSize, TransferMetaFlags metaFlags)
+{
+ TransferArrayHelper<MonoPPtr> helper (instance, field, klass, forcedSize);
+
+ // Potential buffer overflow
+ char buffer[128];
+ MonoPPtr proxy;
+ proxy.m_Buffer = buffer;
+ proxy.m_Class = klass;
+
+ if (transfer.IsWritingPPtr())
+ {
+ helper.SetupWriting ();
+
+ for (int i=0;i<helper.length;i++)
+ {
+ MonoObject* mono = helper.Get<MonoObject*> (i);
+ if (mono)
+ helper.vec[i].SetInstanceID(Scripting::GetInstanceIDFromScriptingWrapper(mono));
+ }
+ }
+
+ TransferWithProxyElement (transfer, helper.vec, proxy, name, metaFlags);
+
+ if (transfer.DidReadLastPPtrProperty ())
+ {
+ helper.SetupReading ();
+
+ const int count = helper.vec.size();
+ for (int i=0;i<count;i++)
+ {
+ helper.Put (i, TransferPPtrToMonoObject(helper.vec[i].GetInstanceID(), klass, classID, field, instance, transfer.GetFlags() & kThreadedSerialization));
+ }
+
+ helper.FinishReading ();
+ }
+}
+
+
+
+template<class TransferFunction> inline
+void TransferArrayString (MonoObject* instance, MonoClassField* field, const char* name, MonoClass* klass, TransferFunction& transfer, unsigned* forcedSize, TransferMetaFlags metaFlags)
+{
+ TransferArrayHelper<UnityStr> helper (instance, field, klass, forcedSize);
+
+ if (transfer.IsWriting())
+ {
+ helper.SetupWriting ();
+
+ for (int i=0;i<helper.length;i++)
+ {
+ char *p = mono_string_to_utf8 (helper.Get<MonoString*> (i));
+ if (p)
+ helper.vec[i] = p;
+ else
+ helper.vec[i].clear ();
+ g_free (p);
+ }
+ }
+
+ transfer.Transfer (helper.vec, name, metaFlags);
+
+ if (transfer.DidReadLastProperty ())
+ {
+ helper.SetupReading ();
+
+ const int count = helper.vec.size();
+ for (int i=0;i<count;i++)
+ {
+ helper.Put (i, MonoStringNew (helper.vec[i]));
+ }
+
+ helper.FinishReading ();
+ }
+}
+
+
+/// @TODO: THIS CAN BE OPTIMIZED FOR BOTH SPACE AND SPEED. DONT CONVERT TO STL vectors.
+
+template<class T, class TransferFunction> inline
+void TransferArrayBuiltins (MonoObject* instance, MonoClassField* field, const char* name, MonoClass* klass, TransferFunction& transfer, unsigned* forcedSize, TransferMetaFlags metaFlags)
+{
+ TransferArrayHelper<T> helper (instance, field, klass, forcedSize);
+
+ if (transfer.IsWriting())
+ {
+ helper.SetupWriting ();
+
+ for (int i=0;i<helper.length;i++)
+ {
+ helper.vec[i] = helper.template Get<T> (i);
+ }
+ }
+
+ transfer.Transfer (helper.vec, name, metaFlags);
+
+ if (transfer.IsReading())
+ {
+ helper.SetupReading ();
+
+ const int count = helper.vec.size();
+ for (int i=0;i<count;i++)
+ {
+ helper.Put (i, helper.vec[i]);
+ }
+
+ helper.FinishReading ();
+ }
+}
+
+template<typename T, class TransferFunction> inline
+void TransferArrayOfT (MonoObject* instance, MonoClassField* field, const char* name, MonoClass* klass, TransferFunction& transfer, unsigned* forcedSize, TransferMetaFlags metaFlags)
+{
+ TransferArrayHelper<T> helper (instance, field, klass, forcedSize);
+
+ if (transfer.IsWriting() || transfer.IsWritingPPtr())
+ {
+ helper.SetupWriting ();
+
+ for (int i=0;i<helper.length;i++)
+ {
+ MonoObject* element = helper.template Get<MonoObject*> (i);
+ if (element != NULL)
+ helper.vec[i] = *ExtractMonoObjectData<T*> (element);
+ }
+ }
+
+ transfer.Transfer (helper.vec, name, metaFlags);
+
+ if (transfer.DidReadLastProperty () || transfer.DidReadLastPPtrProperty ())
+ {
+ helper.SetupReading ();
+
+ const int count = helper.vec.size();
+ for (int i=0;i<count;i++)
+ {
+ MonoObject*& element = helper.template Get<MonoObject*> (i);
+
+ // Make sure the element is allocated
+ if (element == NULL)
+ {
+ element = ScriptingInstantiateObject (klass);
+ mono_runtime_object_init_log_exception (element);
+ }
+
+ // Replace the contents of the element
+ T* monoElementRepresentation = ExtractMonoObjectData<T*> (element);
+ *monoElementRepresentation = helper.vec[i];
+ }
+
+ helper.FinishReading ();
+ }
+}
+
+
+
+
+struct TransferArrayScriptInstance
+{
+ MonoArray* array;
+ MonoClass* elementClass;
+ const CommonScriptingClasses* commonClasses;
+ MonoClassField* field;
+ MonoObject* instanceObject;
+ unsigned* forcedSize;
+ int depthCounter;
+
+ TransferArrayScriptInstance()
+ {
+ depthCounter = -9999;
+ }
+
+ typedef TransferScriptInstance value_type;
+
+ struct iterator
+ {
+ MonoArray* array;
+ int index;
+ TransferScriptInstance transfer_instance;
+
+ friend bool operator != (const iterator& lhs, const iterator& rhs) { return lhs.index != rhs.index; }
+ void operator++() { index++; }
+
+ TransferScriptInstance& operator * ()
+ {
+ MonoObject* instance = GetMonoArrayElement<MonoObject*> (array, index);
+
+ if (instance == NULL)
+ {
+ instance = ScriptingInstantiateObject (transfer_instance.klass);
+ mono_runtime_object_init_log_exception (instance);
+ Scripting::SetScriptingArrayElement<MonoObject*> (array, index, instance);
+ }
+
+ transfer_instance.instance = instance;
+
+ return transfer_instance;
+ }
+ };
+
+ inline int size () const
+ {
+ AssertIf (forcedSize != NULL && *forcedSize > mono_array_length_safe(array));
+ return forcedSize != NULL ? *forcedSize : mono_array_length_safe(array);
+ }
+
+ inline void ResizeGenericList (size_t size)
+ {
+ if (forcedSize != NULL)
+ *forcedSize = size;
+ }
+
+ inline bool IsGenericList () const
+ {
+ return forcedSize != NULL;
+ }
+
+ iterator begin ()
+ {
+ iterator i;
+ i.array = array;
+ i.index = 0;
+ i.transfer_instance.klass = elementClass;
+ i.transfer_instance.commonClasses = commonClasses;
+ i.transfer_instance.transferPrivate = CalculateTransferPrivateVariables(elementClass);
+ i.transfer_instance.depthCounter = depthCounter;
+ return i;
+ }
+
+ iterator end ()
+ {
+ iterator i;
+ i.index = size();
+ return i;
+ }
+};
+
+
+template<>
+class SerializeTraits<TransferArrayScriptInstance> : public SerializeTraitsBase<TransferArrayScriptInstance>
+{
+public:
+
+ typedef TransferArrayScriptInstance value_type ;
+ inline static const char* GetTypeString (void *ptr = NULL) { return "Array"; } \
+ inline static bool IsAnimationChannel () { return false; } \
+ inline static bool MightContainPPtr () { return true; } \
+ inline static bool AllowTransferOptimization () { return false; }
+
+#if SUPPORT_SERIALIZED_TYPETREES
+ static void Transfer (value_type& data, SafeBinaryRead& transfer) { transfer.TransferSTLStyleArray (data); }
+ static void Transfer (value_type& data, StreamedBinaryRead<true>& transfer) { transfer.TransferSTLStyleArray (data); }
+ static void Transfer (value_type& data, StreamedBinaryWrite<true>& transfer) { transfer.TransferSTLStyleArray (data); }
+#endif
+ static void Transfer (value_type& data, StreamedBinaryRead<false>& transfer) { transfer.TransferSTLStyleArray (data); }
+ static void Transfer (value_type& data, StreamedBinaryWrite<false>& transfer) { transfer.TransferSTLStyleArray (data); }
+ static void Transfer (value_type& data, RemapPPtrTransfer& transfer) { transfer.TransferSTLStyleArray (data); }
+#if SUPPORT_TEXT_SERIALIZATION
+ static void Transfer (value_type& data, YAMLRead& transfer) { transfer.TransferSTLStyleArray (data); }
+ static void Transfer (value_type& data, YAMLWrite& transfer) { transfer.TransferSTLStyleArray (data); }
+#endif
+
+ //template
+ static void Transfer (value_type& data, ProxyTransfer& transfer)
+ {
+ TransferScriptInstance instance;
+ instance.instance = NULL;
+ instance.klass = data.elementClass;
+ instance.commonClasses = data.commonClasses;
+ instance.transferPrivate = CalculateTransferPrivateVariables(instance.klass);
+ instance.depthCounter = data.depthCounter;
+ transfer.TransferSTLStyleArrayWithElement(instance, kNoTransferFlags);
+ }
+
+ static bool IsContinousMemoryArray () { return false; }
+
+ static void ResizeSTLStyleArray (value_type& data, int rs)
+ {
+ bool needsResize;
+ // When we deal an List<> we only need to reserve enough memory
+ // We also should reallocate if we are decreasing the actual array size because in that case otherwise GC memory
+ // that is in the unused part of the array must be cleared for which we currently have no simple function.
+ if (data.IsGenericList ())
+ needsResize = data.array == NULL || rs > mono_array_length_safe(data.array) || rs < data.size ();
+ // When we deal with an array we must ensure the array size matches exactly
+ else
+ needsResize = data.array == NULL || rs != mono_array_length_safe(data.array);
+
+ // Create the array data
+ if( needsResize )
+ {
+ MonoArray* array = mono_array_new (mono_domain_get (), data.elementClass, rs);
+ mono_field_set_value (data.instanceObject, data.field, array);
+ data.array = array;
+ }
+
+ // Synchronize List<> data to
+ data.ResizeGenericList(rs);
+ }
+};
+
+
+static bool PrepareTransferEmbeddedClassCommonChecks (MonoClass* referencedClass)
+{
+ int flags = mono_class_get_flags(referencedClass);
+ if ((flags & TYPE_ATTRIBUTE_SERIALIZABLE) == 0)
+ return false;
+
+ if (mono_unity_class_is_abstract (referencedClass) || mono_unity_class_is_interface (referencedClass))
+ return false;
+
+ MonoImage* image = mono_class_get_image(referencedClass);
+ if (image == mono_get_corlib())
+ return false;
+ else if (GetMonoManager().GetAssemblyIndexFromImage(image) == -1)
+ return false;
+
+ return true;
+}
+/// Returns only true on serializable classes.
+/// But only if we are in the same assembly.
+static bool PrepareTransferEmbeddedArrayClass (MonoClassField* field, MonoClass* referencedClass, MonoObject* instance, unsigned* forcedSize, TransferArrayScriptInstance& output, int currentDepth)
+{
+ if (!PrepareTransferEmbeddedClassCommonChecks(referencedClass))
+ return false;
+
+ MonoArray* referencedObject = NULL;
+ if (instance)
+ mono_field_get_value (instance, field, &referencedObject);
+
+ output.array = referencedObject;
+ output.elementClass = referencedClass;
+ output.commonClasses = &GetMonoManager().GetCommonClasses();
+ output.field = field;
+ output.instanceObject = instance;
+ output.forcedSize = forcedSize;
+ output.depthCounter = currentDepth + 1;
+ Assert(currentDepth >= 0);
+
+ return true;
+}
+
+void ApplyScriptDataModified (TransferScriptInstance& info)
+{
+
+ if (info.klass != info.commonClasses->guiStyle)
+ return;
+
+ if (!info.instance)
+ return;
+
+ //this needs to be kept in sinc with SerializedStateReader.as for flash.
+ ScriptingInvocation invocation("UnityEngine", "GUIStyle", "Apply");
+ invocation.object = info.instance;
+ invocation.Invoke();
+}
+
+
+
+struct GenericListData
+{
+ MonoArray* array;
+ unsigned size;
+ int version;
+};
+
+bool PrepareArrayTransfer(int type, MonoType* monoType, MonoClassField*& field, MonoObject*& instance, MonoClass*& elementClass, int& elementClassType, unsigned*& forcedSize)
+{
+ if (type != MONO_TYPE_GENERICINST)
+ {
+ elementClass = mono_type_get_class_or_element_class(monoType);
+ elementClassType = mono_type_get_type(mono_class_get_type (elementClass));
+ return true;
+ }
+ else
+ {
+ MonoClassField* arrayField = GetMonoArrayFieldFromList(type, monoType, field);
+ if (arrayField == NULL)
+ return false;
+
+ // Grab the current list class if it doesn't exist, create it
+ MonoObject* list = NULL;
+ MonoClass *klass = mono_class_from_mono_type (monoType);
+
+ if (instance)
+ {
+ mono_field_get_value (instance, field, &list);
+ if (list == NULL)
+ {
+ list = ScriptingInstantiateObject (klass);
+ mono_runtime_object_init_log_exception (list);
+ }
+ }
+ else
+ {
+ // Mono seems to crash if we dont create an instance of the object prior to accessing the fields
+ ScriptingInstantiateObject (klass);
+ }
+ if (list)
+ forcedSize = &ExtractMonoObjectData<GenericListData>(list).size;
+
+ MonoType* arrayType = mono_field_get_type(arrayField);
+
+ instance = list;
+ field = arrayField;
+ elementClass = mono_type_get_class_or_element_class(arrayType);
+ elementClassType = mono_type_get_type(mono_class_get_type (elementClass));
+ return true;
+ }
+}
+
+
+template<class TransferFunction>
+void TransferFieldOfTypeArray(ScriptingObjectPtr instance, MonoClass* klass, MonoClassField* field, const char* name, TransferScriptInstance& info, TransferFunction& transfer, MonoType* monoType, int type, TransferMetaFlags metaFlags)
+{
+ MonoClass* elementClass;
+ int elementClassType;
+ MonoObject* tempInstance = instance;
+ MonoClassField* arrayField = field;
+ const CommonScriptingClasses& commonClasses = *info.commonClasses;
+ unsigned* forcedSize = NULL;
+ if (!PrepareArrayTransfer(type, monoType, arrayField, tempInstance, elementClass, elementClassType, forcedSize))
+ return;
+
+ if (elementClassType == MONO_TYPE_I4)
+ TransferArrayBuiltins<int> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags);
+ else if (elementClassType == MONO_TYPE_R4)
+ TransferArrayBuiltins<float> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags);
+ else if (elementClassType == MONO_TYPE_U1)
+ TransferArrayBuiltins<UInt8> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags);
+ else if (elementClassType == MONO_TYPE_STRING)
+ TransferArrayString (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags);
+ else if (elementClass == commonClasses.vector3)
+ TransferArrayBuiltins<Vector3f> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags);
+ else if (elementClass == commonClasses.vector2)
+ TransferArrayBuiltins<Vector2f> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags);
+ else if (elementClass == commonClasses.vector4)
+ TransferArrayBuiltins<Vector4f> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags);
+ else if (elementClass == commonClasses.quaternion)
+ TransferArrayBuiltins<Quaternionf> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags);
+ else if (elementClass == commonClasses.matrix4x4)
+ TransferArrayBuiltins<Matrix4x4f> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags);
+ else if (elementClass == commonClasses.color)
+ TransferArrayBuiltins<ColorRGBAf> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags);
+ else if (elementClass == commonClasses.color32)
+ TransferArrayBuiltins<ColorRGBA32> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags);
+ else if (elementClass == commonClasses.rect)
+ TransferArrayBuiltins<Rectf> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags);
+ else if (elementClass == commonClasses.layerMask)
+ TransferArrayBuiltins<BitField> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags);
+ else if (elementClassType == MONO_TYPE_BOOLEAN)
+ TransferArrayBuiltins<UInt8> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags | kEditorDisplaysCheckBoxMask);
+ else if (elementClass == commonClasses.rectOffset)
+ TransferArrayOfT<RectOffset> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags);
+ else if (elementClass == commonClasses.guiStyle)
+ TransferArrayOfT<GUIStyle> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags);
+ else if (mono_class_is_enum (elementClass))
+ {
+ MonoType* enumMonoType = mono_class_enum_basetype (elementClass);
+ int enumType = mono_type_get_type (enumMonoType);
+ switch (enumType)
+ {
+ case MONO_TYPE_I4:
+ TransferArrayBuiltins<int> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags);
+ break;
+ case MONO_TYPE_U1:
+ TransferArrayBuiltins<UInt8> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags);
+ break;
+ default:
+ ErrorString (ErrorMessageForUnsupportedEnumField(mono_class_get_type(elementClass), mono_class_get_type(klass), name));
+ return;
+ }
+ }
+ else
+ {
+ int elementClassID = Scripting::GetClassIDFromScriptingClass (elementClass);
+
+ if (elementClassID != -1)
+ TransferArrayPPtr (tempInstance, arrayField, name, elementClass, elementClassID, transfer, forcedSize, metaFlags);
+ else
+ {
+ if (elementClassType != MONO_TYPE_CLASS)
+ return;
+
+ // Make sure we don't endless loop. Put a hard cap limit on the level of nested types until we have a better general solution.
+ if (info.depthCounter > kClassSerializationDepthLimit)
+ return;
+
+ TransferArrayScriptInstance transferArrayInstance;
+ if (PrepareTransferEmbeddedArrayClass (arrayField, elementClass, tempInstance, forcedSize, transferArrayInstance, info.depthCounter))
+ {
+ transfer.TransferWithTypeString(transferArrayInstance, name, mono_class_get_name(elementClass), metaFlags);
+ }
+ else
+ {
+ return;
+ }
+ }
+ }
+
+ // Assign List<> to member variable.
+ // Need to do it after serialization because only here we know if we can actually serialize the List<>
+ if (field != arrayField && instance)
+ mono_field_set_value(instance, field, tempInstance);
+}
+#endif
diff --git a/Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.cpp b/Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.cpp
new file mode 100644
index 0000000..c589319
--- /dev/null
+++ b/Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.cpp
@@ -0,0 +1,269 @@
+#include "UnityPrefix.h"
+
+#include "MonoBehaviour.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/FileCache.h"
+#include "Runtime/Serialize/IterateTypeTree.h"
+#include "MonoScript.h"
+#include "MonoManager.h"
+#include "Runtime/Mono/MonoBehaviourSerialization.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/CommonScriptingClasses.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include <stack>
+
+#include "MonoBehaviourSerialization_ByCodeGeneration.h"
+
+#if ENABLE_SCRIPTING
+
+static StreamedBinaryRead<false>* currentTransferRead;
+static CachedReader* currentCachedReader;
+static std::stack< StreamedBinaryRead<false>* > currentTranferReadStack;
+static StreamedBinaryWrite<false>* currentTransferWrite;
+static RemapPPtrTransfer* currentRemapPPTRTransfer;
+
+// ToDo: On windows standalone, we're getting :
+//(Filename: E:/Projects/win8-new-serialization/Runtime/ExportGenerated/StandalonePlayer/UnityEngineDebug.cpp Line: 54)
+//
+//ptr == NULL || GET_CURRENT_ALLOC_OWNER() == GET_DEFAULT_OWNER() || GET_CURRENT_ALLOC_OWNER() == s_MonoDomainContainer
+
+int SERIALIZATION_SCRIPT_CALL_CONVENTION GetCurrentSerializationStreamPosition()
+{
+ return (int)currentCachedReader->GetAbsoluteMemoryPosition();
+}
+
+SCRIPTINGOBJECTWRAPPER SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_GetNewInstanceToReplaceOldInstance(int oldInstanceID)
+{
+ SInt32 newInstanceID = currentRemapPPTRTransfer->GetNewInstanceIDforOldInstanceID(oldInstanceID);
+ return Scripting::GetScriptingWrapperForInstanceID(newInstanceID);
+}
+
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+
+template<>
+void MonoBehaviour::TransferWithInstance<StreamedBinaryWrite<false> >(StreamedBinaryWrite<false>& transfer)
+{
+ AssertIf (GetInstance() == SCRIPTING_NULL);
+
+ //TODO: find a way to serialize a monobehaviour that is both fast and does not have a fixed buffersize.
+ currentTransferWrite = &transfer;
+#if UNITY_WINRT
+ static ScriptingMethodPtr method = GetScriptingMethodRegistry().GetMethod(GetScriptingTypeRegistry().GetType("UnityEngine", "IUnitySerializable"), "Unity_Serialize");
+#else
+ // Don't know why above code doesn't work on Mono, says something about invalid header, but this code works
+ ScriptingMethodPtr method = GetScriptingMethodRegistry().GetMethod(GetClass(), "Serialize");
+#endif
+ ScriptingInvocationNoArgs invoke(method);
+ invoke.object = GetInstance();
+ invoke.Invoke();
+}
+
+template<>
+void MonoBehaviour::TransferWithInstance<StreamedBinaryRead<false> >(StreamedBinaryRead<false> & transfer)
+{
+ AssertIf (GetInstance() == SCRIPTING_NULL);
+
+ currentTranferReadStack.push(&transfer);
+
+ currentTransferRead = currentTranferReadStack.top();
+ if(currentTransferRead != NULL){
+ currentCachedReader = &currentTransferRead->GetCachedReader();
+ }
+
+#if UNITY_WINRT
+ static ScriptingMethodPtr method = GetScriptingMethodRegistry().GetMethod(GetScriptingTypeRegistry().GetType("UnityEngine", "IUnitySerializable"), "Unity_Deserialize");
+#else
+ // Don't know why above code doesn't work on Mono, says something about invalid header, but this code works
+ ScriptingMethodPtr method = GetScriptingMethodRegistry().GetMethod(GetClass(), "Deserialize");
+#endif
+ ScriptingInvocationNoArgs invoke(method);
+ invoke.object = GetInstance();
+ invoke.Invoke();
+
+ Assert(currentTransferRead == &transfer);
+ currentTranferReadStack.pop();
+
+ if (!currentTranferReadStack.empty()){
+ currentTransferRead = currentTranferReadStack.top();
+ currentCachedReader = &currentTransferRead->GetCachedReader();
+ }
+}
+
+template<>
+void MonoBehaviour::TransferWithInstance<RemapPPtrTransfer>(RemapPPtrTransfer& transfer)
+{
+ AssertIf (GetInstance() == SCRIPTING_NULL );
+
+ currentRemapPPTRTransfer = &transfer;
+#if UNITY_WINRT
+ static ScriptingMethodPtr method = GetScriptingMethodRegistry().GetMethod(GetScriptingTypeRegistry().GetType("UnityEngine", "IUnitySerializable"), "Unity_RemapPPtrs");
+#else
+ // Don't know why above code doesn't work on Mono, says something about invalid header, but this code works
+ ScriptingMethodPtr method = GetScriptingMethodRegistry().GetMethod(GetClass(), "RemapPPtrs");
+#endif
+ ScriptingInvocationNoArgs invoke(method);
+ invoke.object = GetInstance();
+ invoke.Invoke();
+}
+
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+void MonoBehaviour::DoLivenessCheck(RemapPPtrTransfer& transfer)
+{
+ if (GetInstance() == SCRIPTING_NULL) return;
+
+ currentRemapPPTRTransfer = &transfer;
+
+ static ScriptingMethodPtr method = GetScriptingMethodRegistry().GetMethod(GetScriptingTypeRegistry().GetType("UnityEngine", "IUnityAssetsReferenceHolder"), "Unity_LivenessCheck");
+
+ ScriptingInvocationNoArgs invoke(method);
+ invoke.object = GetInstance();
+ invoke.Invoke();
+}
+#endif
+
+#endif
+
+unsigned char SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadByte()
+{
+ unsigned char retVal = 0;
+ currentCachedReader->Read(&retVal,1);
+ return retVal;
+}
+
+char SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadBool()
+{
+ return NativeExt_MonoBehaviourSerialization_ReadByte();
+}
+
+int SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadInt()
+{
+ int retVal = 0;
+ currentCachedReader->Read(&retVal,4);
+ return retVal;
+}
+
+float SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadFloat()
+{
+ float retVal = 0;
+ currentCachedReader->Read(&retVal,4);
+ return retVal;
+}
+
+double SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadDouble()
+{
+ double retVal = 0;
+ currentCachedReader->Read(&retVal,8);
+ return retVal;
+}
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadBuffer(int ptr, int size)
+{
+ currentCachedReader->Read((void*)ptr, size);
+}
+ScriptingStringPtr SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadString()
+{
+ UnityStr stdString;
+ currentTransferRead->Transfer(stdString, "does_not_matter", kNoTransferFlags);
+ return scripting_string_new(stdString);
+}
+
+SCRIPTINGOBJECTWRAPPER SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadUnityEngineObject()
+{
+ PPtr<Object> pptr;
+ currentTransferRead->Transfer (pptr, "does_not_matter", kNoTransferFlags);
+ return TransferPPtrToMonoObjectUnChecked(pptr.GetInstanceID(), currentTransferRead->GetFlags() & kThreadedSerialization);
+}
+
+void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadGUIStyle(GUIStyle* style)
+{
+ currentTransferRead->Transfer(*style,"does_not_matter",kNoTransferFlags);
+}
+
+void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadGradient(GradientNEW* gradient)
+{
+ currentTransferRead->Transfer(*gradient,"does_not_matter",kNoTransferFlags);
+}
+
+void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadAnimationCurve(AnimationCurve* animation_curve)
+{
+ currentTransferRead->Transfer(*animation_curve,"does_not_matter",kNoTransferFlags);
+}
+
+void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadRectOffset(RectOffset* offset)
+{
+ currentTransferRead->Transfer(*offset,"does_not_matter",kNoTransferFlags);
+}
+
+void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReaderAlign()
+{
+ currentCachedReader->Align4Read();
+}
+
+void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriterAlign()
+{
+ currentTransferWrite->GetCachedWriter().Align4Write();
+}
+
+void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteByte(unsigned char value)
+{
+ currentTransferWrite->GetCachedWriter().Write(value);
+}
+
+void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteBool(int value)
+{
+ currentTransferWrite->GetCachedWriter().Write((char)(value ? 1 : 0));
+}
+
+void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteInt(int value)
+{
+ currentTransferWrite->GetCachedWriter().Write(value);
+}
+
+void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteFloat(float value)
+{
+ currentTransferWrite->GetCachedWriter().Write(value);
+}
+
+void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteDouble(double value)
+{
+ currentTransferWrite->GetCachedWriter().Write(value);
+}
+
+void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteBuffer(int ptr, int size)
+{
+ currentTransferWrite->GetCachedWriter().Write((void*)ptr, size);
+}
+
+void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteString(char* value, int size)
+{
+ // TODO: optimize this, probably creating a new UnityStr is not needed
+ UnityStr tmpStr(value, size);
+ currentTransferWrite->Transfer(tmpStr, "does_not_matter", kNoTransferFlags);
+}
+
+void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteUnityEngineObject(int instance_id)
+{
+ PPtr<Object> pptr;
+ pptr.SetInstanceID(instance_id);
+ currentTransferWrite->Transfer(pptr, "does_not_matter", kNoTransferFlags);
+}
+
+void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteGradient(GradientNEW* value)
+{
+ currentTransferWrite->Transfer(*value,"does_not_matter",kNoTransferFlags);
+}
+
+void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteAnimationCurve(AnimationCurve* value)
+{
+ currentTransferWrite->Transfer(*value,"does_not_matter",kNoTransferFlags);
+}
+
+void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteGUIStyle(GUIStyle* value)
+{
+ currentTransferWrite->Transfer(*value,"does_not_matter",kNoTransferFlags);
+}
+
+void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteRectOffset(RectOffset* value)
+{
+ currentTransferWrite->Transfer(*value,"does_not_matter",kNoTransferFlags);
+}
+#endif \ No newline at end of file
diff --git a/Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.h b/Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.h
new file mode 100644
index 0000000..a4c58cb
--- /dev/null
+++ b/Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.h
@@ -0,0 +1,58 @@
+#pragma once
+
+#include "UnityPrefix.h"
+
+#include "MonoBehaviour.h"
+#include "MonoScript.h"
+#include "MonoManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/CommonScriptingClasses.h"
+#include "Runtime/Math/AnimationCurve.h"
+#include "Runtime/Math/Gradient.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+
+#if UNITY_WINRT
+#define SCRIPTINGOBJECTWRAPPER long long
+#define SERIALIZATION_SCRIPT_CALL_CONVENTION WINAPI
+#else
+#define SCRIPTINGOBJECTWRAPPER ScriptingObjectPtr
+#define SERIALIZATION_SCRIPT_CALL_CONVENTION
+#endif
+
+
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+
+SCRIPT_BINDINGS_EXPORT_DECL int SERIALIZATION_SCRIPT_CALL_CONVENTION GetCurrentSerializationStreamPosition();
+SCRIPT_BINDINGS_EXPORT_DECL SCRIPTINGOBJECTWRAPPER SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_GetNewInstanceToReplaceOldInstance(int oldInstanceID);
+
+// Reader
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReaderAlign();
+SCRIPT_BINDINGS_EXPORT_DECL unsigned char SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadByte();
+SCRIPT_BINDINGS_EXPORT_DECL char SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadBool();
+SCRIPT_BINDINGS_EXPORT_DECL int SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadInt();
+SCRIPT_BINDINGS_EXPORT_DECL float SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadFloat();
+SCRIPT_BINDINGS_EXPORT_DECL double SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadDouble();
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadBuffer(int ptr, int size);
+SCRIPT_BINDINGS_EXPORT_DECL ScriptingStringPtr SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadString();
+SCRIPT_BINDINGS_EXPORT_DECL SCRIPTINGOBJECTWRAPPER SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadUnityEngineObject();
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadGUIStyle(GUIStyle* style);
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadGradient(GradientNEW* gradient);
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadAnimationCurve(AnimationCurve* animation_curve);
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadRectOffset(RectOffset* offset);
+
+// Writer
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriterAlign();
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteByte(unsigned char value);
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteBool(int value);
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteInt(int value);
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteFloat(float value);
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteDouble(double value);
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteBuffer(int ptr, int size);
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteString(char* value, int size);
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteUnityEngineObject(int instance_id);
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteGUIStyle(GUIStyle* style);
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteGradient(GradientNEW* gradient);
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteAnimationCurve(AnimationCurve* animation_curve);
+SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteRectOffset(RectOffset* offset);
+
+#endif
diff --git a/Runtime/Mono/MonoBehaviourSerialization_YamlBackup.cpp b/Runtime/Mono/MonoBehaviourSerialization_YamlBackup.cpp
new file mode 100644
index 0000000..9ea4681
--- /dev/null
+++ b/Runtime/Mono/MonoBehaviourSerialization_YamlBackup.cpp
@@ -0,0 +1,201 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_SCRIPTING
+#include "MonoBehaviour.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/SerializedFile.h"
+#include "Runtime/Serialize/IterateTypeTree.h"
+#include "MonoScript.h"
+#include "MonoTypeSignatures.h"
+#include "MonoManager.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Vector4.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+using namespace std;
+
+#if SUPPORT_TEXT_SERIALIZATION
+struct ArrayAsString
+{
+ char const* name;
+ bool serializeAsString;
+};
+
+ArrayAsString g_ArrayAsString[] =
+{
+#define YAML_DECLARE_WRITE_ARRAY_AS_STRING(x) \
+{ #x, YAMLSerializeTraits<x>::ShouldSerializeArrayAsCompactString () }
+
+ YAML_DECLARE_WRITE_ARRAY_AS_STRING(int),
+ YAML_DECLARE_WRITE_ARRAY_AS_STRING(char),
+ YAML_DECLARE_WRITE_ARRAY_AS_STRING(SInt32),
+};
+
+static const size_t g_ArrayAsStringCount = sizeof (g_ArrayAsString) / sizeof (g_ArrayAsString[0]);
+
+bool ConvertArrayAsString (std::string const& typeName)
+{
+ for (int i=0; i<g_ArrayAsStringCount; ++i)
+ {
+ if (g_ArrayAsString[i].name == typeName)
+ return g_ArrayAsString[i].serializeAsString;
+ }
+
+ return false;
+}
+
+struct YAMLConverterContext
+{
+ dynamic_array<UInt8>& m_Data;
+ YAMLWrite& m_Writer;
+
+ template<class T>
+ T* ExtractAndAdvance (int* bytePosition)
+ {
+ T* ptr = reinterpret_cast<T*> (&m_Data[*bytePosition]);
+ *bytePosition += sizeof (T);
+ return ptr;
+ }
+
+ YAMLConverterContext (dynamic_array<UInt8>& data, YAMLWrite& writer)
+ : m_Data (data), m_Writer (writer)
+ {}
+
+ void EmitBasicData (TypeTree const& typeTree, int* bytePosition)
+ {
+#define HANDLE_COMPLEX_TYPE(T) \
+if (typeTree.m_Type == #T) { \
+T* data = ExtractAndAdvance<T> (bytePosition); \
+m_Writer.Transfer (*data, typeTree.m_Name.c_str()); \
+} else
+
+#define HANDLE_BASIC_TYPE(T) \
+if (typeTree.m_Type == #T) { \
+T data = *reinterpret_cast<T*> (&m_Data[*bytePosition]); \
+m_Writer.Transfer (data, typeTree.m_Name.c_str()); \
+} else
+
+ HANDLE_BASIC_TYPE (int)
+ HANDLE_BASIC_TYPE (SInt32)
+ HANDLE_BASIC_TYPE (bool)
+ HANDLE_BASIC_TYPE (float)
+ HANDLE_BASIC_TYPE (double)
+ HANDLE_BASIC_TYPE (UInt8)
+ {
+ ErrorStringMsg ("Binary to YAML conversion: type %s is unsupported\n", typeTree.m_Type.c_str());
+ }
+ }
+
+ void ConvertDataToYAML (TypeTree const& typeTree)
+ {
+ int bytePosition = 0;
+
+ for (TypeTree::TypeTreeList::const_iterator i = typeTree.m_Children.begin (); i != typeTree.m_Children.end ();++i)
+ {
+ ConvertDataToYAML (*i, &bytePosition);
+ }
+ }
+
+ void ConvertDataToYAML (TypeTree const& typeTree, int* bytePosition)
+ {
+ if (typeTree.IsBasicDataType())
+ {
+ // TypeTrees can contain $initialized_XXX$ member for Mono structs. We don't want to appear in YAML
+ bool skip = !typeTree.m_Name.empty () && typeTree.m_Name[0] == '$' && typeTree.m_Name[typeTree.m_Name.size ()-1] == '$';
+ if (!skip)
+ EmitBasicData (typeTree, bytePosition);
+ }
+ else if (IsTypeTreePPtr (typeTree))
+ {
+ m_Writer.PushMetaFlag (kTransferUsingFlowMappingStyle);
+
+ m_Writer.BeginMetaGroup (typeTree.m_Name);
+ SInt32 instanceID = *reinterpret_cast<SInt32*> (&m_Data[*bytePosition]);
+ m_Writer.Transfer (instanceID, "instanceID");
+ *bytePosition += typeTree.m_ByteSize;
+
+ m_Writer.EndMetaGroup ();
+ m_Writer.PopMetaFlag ();
+ }
+ else if (typeTree.m_IsArray)
+ {
+ SInt32 arraySize = *ExtractAndAdvance<SInt32> (bytePosition);
+ Assert (typeTree.m_Children.size () == 2);
+ TypeTree::const_iterator sizeIt = typeTree.begin ();
+ TypeTree::const_iterator dataIt = sizeIt; ++dataIt;
+ TypeTree const& elementTypeTree = *dataIt;
+
+ if (elementTypeTree.IsBasicDataType () && ConvertArrayAsString (elementTypeTree.m_Type))
+ {
+ std::string str;
+ size_t elementSize = elementTypeTree.m_ByteSize;
+ size_t numBytes = arraySize * elementSize;
+ str.resize (numBytes*2);
+
+ for (size_t i=0; i<arraySize; ++i, *bytePosition += elementSize)
+ {
+ void* dataPtr = &m_Data[*bytePosition];
+ BytesToHexString (dataPtr, elementSize, &str[i*2*elementSize]);
+ }
+
+ m_Writer.TransferStringData (str);
+ }
+ else
+ {
+ m_Writer.StartSequence ();
+
+ for (size_t i = 0; i<arraySize; ++i)
+ ConvertDataToYAML (elementTypeTree, bytePosition);
+ }
+ }
+ else
+ {
+ HANDLE_COMPLEX_TYPE (Vector2f)
+ HANDLE_COMPLEX_TYPE (Vector3f)
+ HANDLE_COMPLEX_TYPE (Vector4f)
+ HANDLE_COMPLEX_TYPE (Quaternionf)
+ HANDLE_COMPLEX_TYPE (Matrix4x4f)
+ {
+ m_Writer.BeginMetaGroup (typeTree.m_Name);
+
+ for (TypeTree::TypeTreeList::const_iterator i = typeTree.m_Children.begin (); i != typeTree.m_Children.end ();++i)
+ ConvertDataToYAML (*i, bytePosition);
+
+ m_Writer.EndMetaGroup ();
+ }
+ }
+
+ if (typeTree.IsBasicDataType ())
+ *bytePosition += typeTree.m_ByteSize;
+
+ if (typeTree.m_MetaFlag & kAlignBytesFlag)
+ *bytePosition = Align4_Iterate (*bytePosition);
+ }
+};
+
+YAMLNode* ConvertBackupToYAML (BackupState& binary)
+{
+ YAMLWrite writer (0);
+ YAMLConverterContext ctx(binary.state, writer);
+
+ ctx.ConvertDataToYAML (binary.typeTree);
+
+ yaml_document_t* yaml = writer.GetDocument();
+ YAMLNode* node = YAMLDocNodeToNode (yaml, yaml_document_get_root_node(yaml));
+
+#if 0
+ if (node)
+ {
+ std::string str = node->EmitYAMLString();
+ printf_console ("CONVERTED BINARY TO YAML:\n%s<< END\n", str.c_str());
+ }
+#endif
+
+ return node;
+}
+#endif // SUPPORT_TEXT_SERIALIZATION
+
+#endif // ENABLE_SCRIPTING
diff --git a/Runtime/Mono/MonoBehaviourSerialization_flash.cpp b/Runtime/Mono/MonoBehaviourSerialization_flash.cpp
new file mode 100644
index 0000000..55910b9
--- /dev/null
+++ b/Runtime/Mono/MonoBehaviourSerialization_flash.cpp
@@ -0,0 +1,239 @@
+#include "UnityPrefix.h"
+
+#include "MonoBehaviour.h"
+#include "Runtime/Math/AnimationCurve.h"
+#include "Runtime/Math/Gradient.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/FileCache.h"
+#include "Runtime/Serialize/IterateTypeTree.h"
+#include "MonoScript.h"
+#include "MonoManager.h"
+#include "Runtime/Mono/MonoBehaviourSerialization.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/CommonScriptingClasses.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include <stack>
+
+#if UNITY_FLASH
+
+#define FLASH_SERIALIZATION_STRING_BUFFERSIZE 4*1024
+
+static StreamedBinaryRead<false>* currentTransferRead;
+static CachedReader* currentCachedReader;
+static std::stack< StreamedBinaryRead<false>* > currentTranferReadStack;
+static StreamedBinaryWrite<false>* currentTransferWrite;
+static RemapPPtrTransfer* currentRemapPPTRTransfer;
+
+static unsigned char temporaryStringBuffer[FLASH_SERIALIZATION_STRING_BUFFERSIZE];
+
+//since there is so much code involved in reading a PPtr<T> from a serializedstream, and getting the ScriptingObject
+//belonging to that, we do not do that deserialization in as3, but in c instead. as3 will call this function
+//when it wants to deserialize something that enherits from UnityEngine.Object
+extern "C" ScriptingObjectPtr ReadPPtrAndReturnScriptingWrapper()
+{
+ PPtr<Object> pptr;
+ currentTransferRead->Transfer (pptr, "does_not_matter", kNoTransferFlags);
+ return TransferPPtrToMonoObjectUnChecked(pptr.GetInstanceID(), currentTransferRead->GetFlags() & kThreadedSerialization);
+}
+
+extern "C" GradientNEW* ReadGradientAndReturnPtr()
+{
+ GradientNEW* gradient = new GradientNEW();
+ currentTransferRead->Transfer (*gradient, "does_not_matter", kNoTransferFlags);
+ return gradient;
+}
+
+extern "C" AnimationCurve* ReadAnimationCurveAndReturnPtr()
+{
+ AnimationCurve* curve = new AnimationCurve();
+ currentTransferRead->Transfer(*curve, "does_not_matter", kNoTransferFlags);
+ return curve;
+}
+
+extern "C" int GetCurrentSerializationStreamPosition()
+{
+ return (int)currentCachedReader->GetAbsoluteMemoryPosition();
+}
+
+extern "C" void WriteGradient(GradientNEW* gradient)
+{
+ currentTransferWrite->Transfer(*gradient, "doesntmatter", kNoTransferFlags);
+}
+
+extern "C" void WriteAnimationCurve(AnimationCurve* curve)
+{
+ currentTransferWrite->Transfer(*curve, "doesntmatter", kNoTransferFlags);
+}
+
+extern "C" void WritePPtrWithInstanceID(int instanceid)
+{
+ PPtr<Object> pptr;
+ pptr.SetInstanceID(instanceid);
+ currentTransferWrite->Transfer(pptr, "doesntmatter", kNoTransferFlags);
+}
+
+extern "C" ScriptingObjectPtr GetNewInstanceToReplaceOldInstance(int oldInstanceID)
+{
+ SInt32 newInstanceID = currentRemapPPTRTransfer->GetNewInstanceIDforOldInstanceID(oldInstanceID);
+ return Scripting::GetScriptingWrapperForInstanceID(newInstanceID);
+}
+
+template<>
+void MonoBehaviour::TransferWithInstance<StreamedBinaryWrite<false> > (StreamedBinaryWrite<false>& transfer)
+{
+ AssertIf (GetInstance() == NULL);
+ currentTransferWrite = &transfer;
+ Ext_SerializeMonoBehaviour(GetInstance());
+}
+
+template<>
+void MonoBehaviour::TransferWithInstance<StreamedBinaryRead<false> > (StreamedBinaryRead<false> & transfer)
+{
+ AssertIf (GetInstance() == NULL);
+
+ currentTranferReadStack.push(&transfer);
+
+ currentTransferRead = currentTranferReadStack.top();
+ if(currentTransferRead != NULL){
+ currentCachedReader = &currentTransferRead->GetCachedReader();
+ }
+
+ Ext_DeserializeMonoBehaviour(GetInstance());
+
+ Assert(currentTransferRead == &transfer);
+ currentTranferReadStack.pop();
+
+ if(!currentTranferReadStack.empty()){
+ currentTransferRead = currentTranferReadStack.top();
+ currentCachedReader = &currentTransferRead->GetCachedReader();
+ }
+}
+
+template<>
+void MonoBehaviour::TransferWithInstance<RemapPPtrTransfer> (RemapPPtrTransfer& transfer)
+{
+ AssertIf (GetInstance() == NULL);
+ currentRemapPPTRTransfer = &transfer;
+ Ext_RemapPPtrs(GetInstance());
+}
+
+extern "C" unsigned char NativeExt_MonoBehaviourSerialization_ReadByte()
+{
+ unsigned char retVal = 0;
+ currentCachedReader->Read(&retVal,1);
+ return retVal;
+}
+
+extern "C" unsigned char NativeExt_MonoBehaviourSerialization_WriteByte(unsigned char value)
+{
+ currentTransferWrite->GetCachedWriter().Write(value);
+}
+
+extern "C" char NativeExt_MonoBehaviourSerialization_ReadBool()
+{
+ return NativeExt_MonoBehaviourSerialization_ReadByte();
+}
+
+extern "C" char NativeExt_MonoBehaviourSerialization_WriteBool(bool value)
+{
+ currentTransferWrite->GetCachedWriter().Write((char)(value ? 1 : 0));
+}
+
+extern "C" int NativeExt_MonoBehaviourSerialization_ReadInt()
+{
+ int retVal = 0;
+ currentCachedReader->Read(&retVal,4);
+ return retVal;
+}
+
+extern "C" char NativeExt_MonoBehaviourSerialization_WriteInt(int value)
+{
+ currentTransferWrite->GetCachedWriter().Write(value);
+}
+
+extern "C" float NativeExt_MonoBehaviourSerialization_ReadFloat()
+{
+ float retVal = 0;
+ currentCachedReader->Read(&retVal,4);
+ return retVal;
+}
+
+extern "C" char NativeExt_MonoBehaviourSerialization_WriteFloat(float value)
+{
+ currentTransferWrite->GetCachedWriter().Write(value);
+}
+
+extern "C" double NativeExt_MonoBehaviourSerialization_ReadDouble()
+{
+ double retVal = 0;
+ currentCachedReader->Read(&retVal,8);
+ return retVal;
+}
+
+extern "C" char NativeExt_MonoBehaviourSerialization_WriteDouble(double value)
+{
+ currentTransferWrite->GetCachedWriter().Write(value);
+}
+
+extern "C" void NativeExt_MonoBehaviourSerialization_ReadString()
+{
+ int length = 0;
+ currentCachedReader->Read(&length, 4);//Length of the string
+ void* tempBuffer;
+ bool usesLocalBuffer;
+ usesLocalBuffer = !(length < FLASH_SERIALIZATION_STRING_BUFFERSIZE);
+ if(usesLocalBuffer)
+ tempBuffer = malloc(length);
+ else
+ tempBuffer = temporaryStringBuffer;
+
+
+ currentCachedReader->Read(tempBuffer,length);
+
+ __asm __volatile__("var bpos:int = heap.position;");
+ __asm __volatile__("heap.position = %0;"::"r"(tempBuffer));
+ __asm __volatile__("string_g0 = heap.readUTFBytes(%0);"::"r"(length));
+ __asm __volatile__("heap.position = bpos;");
+
+ if(usesLocalBuffer)
+ free(tempBuffer);
+
+ currentCachedReader->Align4Read();
+}
+
+extern "C" void NativeExt_MonoBehaviourSerialization_WriteString(char* value, int size)
+{
+ UnityStr tmpStr(value, size);
+ currentTransferWrite->Transfer(tmpStr, "does_not_matter", kNoTransferFlags);
+}
+
+extern "C" void NativeExt_MonoBehaviourSerialization_ReadGUIStyle(GUIStyle* style)
+{
+ currentTransferRead->Transfer(*style,"does_not_matter",kNoTransferFlags);
+}
+
+extern "C" void NativeExt_MonoBehaviourSerialization_WriteGUIStyle(GUIStyle* style)
+{
+ currentTransferWrite->Transfer(*style,"does_not_matter",kNoTransferFlags);
+}
+
+extern "C" void NativeExt_MonoBehaviourSerialization_ReadRectOffset(RectOffset* offset)
+{
+ currentTransferRead->Transfer(*offset,"does_not_matter",kNoTransferFlags);
+}
+
+extern "C" void NativeExt_MonoBehaviourSerialization_WriteRectOffset(RectOffset* offset)
+{
+ currentTransferWrite->Transfer(*offset,"does_not_matter",kNoTransferFlags);
+}
+
+extern "C" void NativeExt_MonoBehaviourSerialization_WriteAlign()
+{
+ currentTransferWrite->GetCachedWriter().Align4Write();
+}
+
+extern "C" void NativeExt_MonoBehaviourSerialization_ReadAlign()
+{
+ currentCachedReader->Align4Read();
+}
+#endif
diff --git a/Runtime/Mono/MonoBehaviourSerialization_metro.cpp b/Runtime/Mono/MonoBehaviourSerialization_metro.cpp
new file mode 100644
index 0000000..c00026f
--- /dev/null
+++ b/Runtime/Mono/MonoBehaviourSerialization_metro.cpp
@@ -0,0 +1,178 @@
+#include "UnityPrefix.h"
+
+#include "MonoBehaviour.h"
+#include "Runtime/Math/AnimationCurve.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/FileCache.h"
+#include "Runtime/Serialize/IterateTypeTree.h"
+#include "MonoScript.h"
+#include "MonoManager.h"
+#include "Runtime/Mono/MonoBehaviourSerialization.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/CommonScriptingClasses.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+
+#if ENABLE_SCRIPTING && UNITY_WINRT && !ENABLE_SERIALIZATION_BY_CODEGENERATION
+std::stack<StreamedBinaryWrite<false>*> currentTransferWrite;
+std::stack<StreamedBinaryRead<false>*> currentTransferRead;
+RemapPPtrTransfer* currentRemapper = NULL;
+
+static void SerializationReader_Read(int destination, int size, bool align)
+{
+ StreamedBinaryRead<false>* read = currentTransferRead.top();
+ read->ReadDirect((void*)destination, size);
+ if (align)
+ read->Align();
+}
+static long long SerializationReader_ReadPPtr()
+{
+ StreamedBinaryRead<false>* transfer = currentTransferRead.top();
+
+ PPtr<Object> pptr;
+ transfer->Transfer (pptr, "does_not_matter", kNoTransferFlags);
+
+ return TransferPPtrToMonoObjectUnChecked(pptr.GetInstanceID(), transfer->GetFlags() & kThreadedSerialization);
+}
+
+
+void SerializationReader_ReadGUIStyle(int dstPtr)
+{
+ StreamedBinaryRead<false>* transfer = currentTransferRead.top();
+ transfer->Transfer(*(GUIStyle*)dstPtr, "does_not_matter", kNoTransferFlags);
+}
+void SerializationReader_ReadRectOffset(int dstPtr)
+{
+ StreamedBinaryRead<false>* transfer = currentTransferRead.top();
+ transfer->Transfer(*(RectOffset*)dstPtr, "does_not_matter", kNoTransferFlags);
+}
+int SerializationReader_ReadAnimationCurve()
+{
+ StreamedBinaryRead<false>* transfer = currentTransferRead.top();
+ AnimationCurve* curve = new AnimationCurve();
+ transfer->Transfer (*curve, "does_not_matter", kNoTransferFlags);
+ return (int)curve;
+}
+
+
+static void SetupReader()
+{
+ static BridgeInterface::ISerializationReader^ reader = nullptr;
+ if (reader == nullptr)
+ {
+ reader = s_WinRTBridge->CreateSerializationReader(
+ ref new BridgeInterface::SerializationReader_Read(SerializationReader_Read),
+ ref new BridgeInterface::SerializationReader_ReadPPtr(SerializationReader_ReadPPtr),
+ ref new BridgeInterface::SerializationReader_ReadStruct(SerializationReader_ReadGUIStyle),
+ ref new BridgeInterface::SerializationReader_ReadStruct(SerializationReader_ReadRectOffset),
+ ref new BridgeInterface::SerializationReader_ReadAnimationCurve(SerializationReader_ReadAnimationCurve));
+ GetWinRTMonoBehaviourSerialization()->SetNativeReader(reader);
+ }
+}
+
+
+static void SerializationWriter_Write(int source, int size, bool align)
+{
+ StreamedBinaryWrite<false>* writer = currentTransferWrite.top();
+ writer->GetCachedWriter().Write((void*)source, size);
+ if (align)
+ writer->Align();
+}
+
+void SerializationWriter_WriteGUIStyle(int srcPtr)
+{
+ currentTransferWrite.top()->Transfer(*(GUIStyle*)srcPtr, "does_not_matter", kNoTransferFlags);
+}
+void SerializationWriter_WriteRectOffset(int srcPtr)
+{
+ currentTransferWrite.top()->Transfer(*(RectOffset*)srcPtr, "does_not_matter", kNoTransferFlags);
+}
+void SerializationWriter_WriteAnimationCurve(int srcAnimationCurvePtr)
+{
+ AnimationCurve* curve = (AnimationCurve*)srcAnimationCurvePtr;
+ currentTransferWrite.top()->Transfer(*curve,"doesntmatter", kNoTransferFlags);
+}
+
+
+static void SerializationWriter_WritePPtr(int instanceID)
+{
+ StreamedBinaryWrite<false>* transfer = currentTransferWrite.top();
+ PPtr<Object> pptr;
+ pptr.SetInstanceID(instanceID);
+ currentTransferWrite.top()->Transfer(pptr, "doesntmatter", kNoTransferFlags);
+}
+
+static void SetupWriter()
+{
+ static BridgeInterface::ISerializationWriter^ writer = nullptr;
+ if (writer == nullptr)
+ {
+ writer = s_WinRTBridge->CreateSerializationWriter(
+ ref new BridgeInterface::SerializationWriter_Write(SerializationWriter_Write),
+ ref new BridgeInterface::SerializationWriter_WritePPtr(SerializationWriter_WritePPtr),
+ ref new BridgeInterface::SerializationWriter_WriteStruct(SerializationWriter_WriteGUIStyle),
+ ref new BridgeInterface::SerializationWriter_WriteStruct(SerializationWriter_WriteRectOffset),
+ ref new BridgeInterface::SerializationWriter_WriteAnimationCurve(SerializationWriter_WriteAnimationCurve));
+ GetWinRTMonoBehaviourSerialization()->SetNativeWriter(writer);
+ }
+}
+
+
+template<>
+void MonoBehaviour::TransferWithInstance<StreamedBinaryWrite<false> > (StreamedBinaryWrite<false>& transfer)
+{
+ AssertIf (GetInstance() == SCRIPTING_NULL);
+ currentTransferWrite.push(&transfer);
+
+ SetupWriter();
+ GetWinRTMonoBehaviourSerialization()->Serialize(GetInstance());
+
+ StreamedBinaryWrite<false>* remove = currentTransferWrite.top();
+ Assert(remove == &transfer);
+ currentTransferWrite.pop();
+}
+
+template<>
+void MonoBehaviour::TransferWithInstance<StreamedBinaryRead<false> > (StreamedBinaryRead<false> & transfer)
+{
+ AssertIf (GetInstance() == SCRIPTING_NULL);
+
+ currentTransferRead.push(&transfer);
+ SetupReader();
+ GetWinRTMonoBehaviourSerialization()->Deserialize(GetInstance());
+
+ StreamedBinaryRead<false>* remove = currentTransferRead.top();
+ Assert(remove == &transfer);
+ currentTransferRead.pop();
+}
+
+static long long SerializationRemapper_GetScriptingWrapper(int oldInstanceID)
+{
+ SInt32 newInstanceID = currentRemapper->GetNewInstanceIDforOldInstanceID(oldInstanceID);
+ //printf_console("GNITRO called. old instanceid: %i, new instanceid: %i", oldInstanceID, newInstanceID);
+ return Scripting::GetScriptingWrapperForInstanceID(newInstanceID);
+}
+
+static void SetupRemapper()
+{
+ static BridgeInterface::SerializationRemapper_GetScriptingWrapper^ remapper = nullptr;
+ if (remapper == nullptr)
+ {
+ remapper = ref new BridgeInterface::SerializationRemapper_GetScriptingWrapper(SerializationRemapper_GetScriptingWrapper);
+ GetWinRTMonoBehaviourSerialization()->SetNativeRemapper(remapper);
+ }
+
+}
+
+template<>
+void MonoBehaviour::TransferWithInstance<RemapPPtrTransfer> (RemapPPtrTransfer& transfer)
+{
+ AssertIf (GetInstance() == SCRIPTING_NULL);
+ currentRemapper = &transfer;
+ SetupRemapper();
+ GetWinRTMonoBehaviourSerialization()->Remap(GetInstance());
+}
+
+void MonoBehaviour::DoLivenessCheck(RemapPPtrTransfer& transfer)
+{
+}
+#endif
diff --git a/Runtime/Mono/MonoExportUtility.cpp b/Runtime/Mono/MonoExportUtility.cpp
new file mode 100644
index 0000000..71e4451
--- /dev/null
+++ b/Runtime/Mono/MonoExportUtility.cpp
@@ -0,0 +1,49 @@
+#include "UnityPrefix.h"
+#if ENABLE_MONO
+#include "MonoExportUtility.h"
+#include "MonoTypeSignatures.h"
+#include "MonoScript.h"
+#include "MonoScriptCache.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+
+#if !GAMERELEASE
+#include "Editor/Src/Utility/ObjectNames.h"
+#endif
+
+using namespace std;
+
+#if UNITY_EDITOR
+static ScriptingClassPtr ClassIDToScriptingClassIncludingBasicTypes (int classID)
+{
+ if (classID == ClassID (Undefined))
+ return NULL;
+ else if (classID <= kLargestEditorClassID)
+ return Scripting::ClassIDToScriptingType (classID);
+ else if (classID == ClassID (int))
+ return mono_get_int32_class ();
+ else if (classID == ClassID (bool))
+ return mono_get_boolean_class();
+ else if (classID == ClassID (float))
+ return mono_get_single_class();
+ else
+ {
+ AssertString("Unsupported classID value");
+ return NULL;
+ }
+}
+
+MonoObject* ClassIDToScriptingTypeObjectIncludingBasicTypes (int classID)
+{
+ ScriptingClassPtr klass = ClassIDToScriptingClassIncludingBasicTypes (classID);
+ if (klass == NULL)
+ return NULL;
+
+ return mono_type_get_object(mono_domain_get(), mono_class_get_type(klass));
+}
+
+#endif
+
+
+#endif //ENABLE_MONO
diff --git a/Runtime/Mono/MonoExportUtility.h b/Runtime/Mono/MonoExportUtility.h
new file mode 100644
index 0000000..f6db714
--- /dev/null
+++ b/Runtime/Mono/MonoExportUtility.h
@@ -0,0 +1,14 @@
+#ifndef MONOEXPORTUTILITY_H
+#define MONOEXPORTUTILITY_H
+
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+#if ENABLE_MONO
+#include "MonoManager.h"
+
+#if UNITY_EDITOR
+ScriptingObjectPtr ClassIDToScriptingTypeObjectIncludingBasicTypes (int classID);
+#endif
+
+#endif
+#endif
diff --git a/Runtime/Mono/MonoFunctions.h b/Runtime/Mono/MonoFunctions.h
new file mode 100644
index 0000000..e93a298
--- /dev/null
+++ b/Runtime/Mono/MonoFunctions.h
@@ -0,0 +1,418 @@
+#ifndef DO_API_NO_RETURN
+#define DO_API_NO_RETURN(a,b,c) DO_API(a,b,c)
+#endif
+
+// If you add functions to this file you also need to expose them in MonoBundle.exp
+// Otherwise they wont be exported in the web plugin!
+DO_API(void,mono_thread_suspend_all_other_threads, ())
+DO_API(void,mono_thread_pool_cleanup, ())
+DO_API(void,mono_threads_set_shutting_down, ())
+DO_API(void,mono_runtime_set_shutting_down, ())
+DO_API(gboolean,mono_runtime_is_shutting_down, ())
+DO_API(gboolean,mono_domain_finalize, (MonoDomain *domain, int timeout))
+DO_API(void,mono_runtime_cleanup, (MonoDomain *domain))
+DO_API(MonoMethod*,mono_object_get_virtual_method, (MonoObject *obj, MonoMethod *method))
+
+DO_API(void,mono_verifier_set_mode,(MiniVerifierMode) );
+DO_API(void,mono_security_set_mode,(MonoSecurityMode) );
+DO_API(void,mono_add_internal_call,(const char *name, gconstpointer method))
+DO_API(void,mono_jit_cleanup,(MonoDomain *domain))
+DO_API(MonoDomain*,mono_jit_init,(const char *file))
+DO_API(MonoDomain*,mono_jit_init_version,(const char *file, const char* runtime_version))
+DO_API(int, mono_jit_exec, (MonoDomain *domain, MonoAssembly *assembly, int argc, char *argv[]))
+DO_API(MonoClass *,mono_class_from_name,(MonoImage *image, const char* name_space, const char *name))
+DO_API(MonoClass *,mono_class_from_name_case,(MonoImage *image, const char* name_space, const char *name))
+DO_API(MonoAssembly *,mono_domain_assembly_open,(MonoDomain *domain, const char *name))
+DO_API(MonoDomain *, mono_domain_create_appdomain,(const char *domainname, const char* configfile))
+DO_API(void, mono_domain_unload, (MonoDomain* domain))
+DO_API(MonoObject*,mono_object_new,(MonoDomain *domain, MonoClass *klass))
+DO_API(void,mono_runtime_object_init,(MonoObject *this_obj))
+DO_API(MonoObject*,mono_runtime_invoke,(MonoMethod *method, void *obj, void **params, MonoException **exc))
+DO_API(void,mono_field_set_value,(MonoObject *obj, MonoClassField *field, void *value))
+DO_API(void,mono_field_get_value,(MonoObject *obj, MonoClassField *field, void *value))
+DO_API(int,mono_field_get_offset,(MonoClassField *field))
+DO_API(MonoClassField*,mono_class_get_fields,(MonoClass* klass, gpointer *iter))
+DO_API(MonoMethod*,mono_class_get_methods,(MonoClass* klass, gpointer *iter))
+DO_API(MonoDomain*,mono_domain_get,())
+DO_API(MonoDomain*,mono_get_root_domain,())
+DO_API(gint32,mono_domain_get_id,(MonoDomain *domain))
+DO_API(void,mono_assembly_foreach,(GFunc func, gpointer user_data))
+DO_API(void,mono_image_close,(MonoImage *image))
+DO_API(void,mono_unity_socket_security_enabled_set,(gboolean))
+//DO_API(void,mono_set_unhandled_exception_handler,(void* function))
+DO_API(const char*, mono_image_get_name, (MonoImage *image))
+DO_API(MonoClass*, mono_get_object_class, ())
+#if UNITY_WIN || UNITY_OSX || UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+DO_API(void,mono_set_signal_chaining, (bool))
+#endif
+#if UNITY_WIN
+DO_API(long, mono_unity_seh_handler, (EXCEPTION_POINTERS*))
+DO_API(void, mono_unity_set_unhandled_exception_handler, (void*))
+#endif
+DO_API(void, mono_set_commandline_arguments, (int, const char* argv[], const char*))
+#if USE_MONO_AOT
+DO_API(void*, mono_aot_get_method,(MonoDomain *domain, MonoMethod *method))
+#endif
+//DO_API(MonoMethod*, mono_marshal_get_xappdomain_invoke, (MonoMethod*))
+
+#if UNITY_EDITOR
+// Type-safe way of looking up methods based on method signatures
+DO_API(MonoObject*,mono_runtime_invoke_array,(MonoMethod *method, void *obj, MonoArray *params, MonoException **exc))
+DO_API(MonoMethodDesc*, mono_method_desc_new, (const char *name, gboolean include_namespace))
+DO_API(MonoMethod*, mono_method_desc_search_in_class, (MonoMethodDesc* desc, MonoClass* klass))
+DO_API(void, mono_method_desc_free, (MonoMethodDesc* desc))
+DO_API(char*,mono_type_get_name_full,(MonoType *type, MonoTypeNameFormat format))
+#endif
+
+#if UNITY_WIN || UNITY_XENON
+DO_API(gunichar2*,mono_string_to_utf16,(MonoString *string_obj))
+#endif
+
+DO_API(const char*,mono_field_get_name,(MonoClassField *field))
+DO_API(MonoType*,mono_field_get_type,(MonoClassField *field))
+DO_API(int,mono_type_get_type,(MonoType *type))
+DO_API(const char*,mono_method_get_name,(MonoMethod *method))
+DO_API(MonoImage*,mono_assembly_get_image,(MonoAssembly *assembly))
+DO_API(MonoClass* ,mono_method_get_class,(MonoMethod *method))
+DO_API(MonoClass*,mono_object_get_class,(MonoObject *obj))
+DO_API(gboolean,mono_class_is_valuetype,(MonoClass *klass))
+DO_API(guint32,mono_signature_get_param_count,(MonoMethodSignature *sig))
+DO_API(char*,mono_string_to_utf8,(MonoString *string_obj))
+DO_API(MonoString*,mono_string_new_wrapper,(const char* text))
+DO_API(MonoString*,mono_string_new_len,(MonoDomain *domain, const char *text, guint32 length))
+DO_API(MonoString*,mono_string_from_utf16,(const gunichar2* text))
+DO_API(MonoClass*,mono_class_get_parent,(MonoClass *klass))
+DO_API(const char*,mono_class_get_namespace,(MonoClass *klass))
+DO_API(gboolean,mono_class_is_subclass_of,(MonoClass *klass, MonoClass *klassc, gboolean check_interfaces))
+DO_API(const char*,mono_class_get_name,(MonoClass *klass))
+DO_API(char*,mono_type_get_name,(MonoType *type))
+DO_API(MonoClass*,mono_type_get_class,(MonoType *type))
+DO_API(MonoException *,mono_exception_from_name_msg,(MonoImage *image, const char *name_space, const char *name, const char *msg))
+DO_API_NO_RETURN(void,mono_raise_exception,(MonoException *ex))
+DO_API(MonoClass*,mono_get_exception_class,())
+DO_API(MonoClass*,mono_get_array_class,())
+DO_API(MonoClass*,mono_get_string_class,())
+DO_API(MonoClass*,mono_get_boolean_class,())
+DO_API(MonoClass*,mono_get_byte_class,())
+DO_API(MonoClass*,mono_get_char_class,())
+DO_API(MonoClass*,mono_get_int16_class,())
+DO_API(MonoClass*,mono_get_int32_class,())
+DO_API(MonoClass*,mono_get_int64_class,())
+DO_API(MonoClass*,mono_get_single_class,())
+DO_API(MonoClass*,mono_get_double_class,())
+DO_API(MonoArray*,mono_array_new,(MonoDomain *domain, MonoClass *eclass, guint32 n))
+//DO_API(MonoArray *, mono_array_new_specific, (MonoVTable *vtable, guint32 n))
+DO_API(MonoArray*, mono_array_new_full, (MonoDomain *domain, MonoClass *array_class, guint32 *lengths, guint32 *lower_bounds))
+
+DO_API(MonoClass *, mono_array_class_get, (MonoClass *eclass, guint32 rank))
+
+DO_API(gint32,mono_class_array_element_size,(MonoClass *ac))
+DO_API(MonoObject*, mono_type_get_object, (MonoDomain *domain, MonoType *type))
+DO_API(gboolean, mono_class_is_generic, (MonoClass* klass))
+DO_API(gboolean, mono_class_is_inflated, (MonoClass* klass))
+
+DO_API(MonoThread *,mono_thread_attach,(MonoDomain *domain))
+
+DO_API(void, mono_thread_detach,(MonoThread *thread))
+DO_API(MonoThread *,mono_thread_exit,())
+
+DO_API(MonoThread *,mono_thread_current,(void))
+DO_API(void,mono_thread_set_main,(MonoThread* thread))
+DO_API(void,mono_set_find_plugin_callback,(gconstpointer method))
+DO_API(void,mono_security_enable_core_clr, ())
+
+typedef bool (*MonoCoreClrPlatformCB) (const char *image_name);
+DO_API(bool,mono_security_set_core_clr_platform_callback, (MonoCoreClrPlatformCB))
+
+#if MONO_2_12
+DO_API(void,mono_security_core_clr_set_options, (MonoSecurityCoreCLROptions))
+#endif
+
+DO_API(void, mono_runtime_unhandled_exception_policy_set, (MonoRuntimeUnhandledExceptionPolicy policy))
+
+#if UNITY_OSX
+//DO_API(void,SetNativeSigsegvHandler,(gconstpointer ptr))
+#endif
+#if UNITY_WIN
+//DO_API(void,SetNativeSigsegvHandlerWin,(gconstpointer ptr))
+// @TODO: move this out of windows specific
+//DO_API(void,unity_mono_redirect_output,(const char* fout, const char* ferr))
+//DO_API(void,unity_mono_close_output,())
+#endif
+
+DO_API(MonoClass*,mono_class_get_nesting_type,(MonoClass *klass))
+DO_API(MonoVTable* ,mono_class_vtable,(MonoDomain *domain, MonoClass *klass))
+DO_API(MonoReflectionMethod* ,mono_method_get_object,(MonoDomain *domain, MonoMethod *method, MonoClass *refclass))
+
+DO_API(MonoMethodSignature* ,mono_method_signature,(MonoMethod *method))
+DO_API(MonoType*,mono_signature_get_params,(MonoMethodSignature *sig, gpointer *iter))
+DO_API(MonoType*,mono_signature_get_return_type,(MonoMethodSignature *sig))
+DO_API(MonoType*,mono_class_get_type,(MonoClass *klass))
+DO_API(void,mono_set_ignore_version_and_key_when_finding_assemblies_already_loaded,(gboolean value))
+DO_API (void,mono_debug_init ,(int format))
+
+DO_API (void,mono_debug_open_image_from_memory,(MonoImage *image, const char *raw_contents, int size))
+DO_API(guint32,mono_field_get_flags,(MonoClassField *field))
+DO_API(MonoImage*,mono_image_open_from_data_full,(const void *data, guint32 data_len, gboolean need_copy, int *status, gboolean ref_only))
+DO_API(MonoImage*,mono_image_open_from_data_with_name,(char *data, guint32 data_len, gboolean need_copy, int *status, gboolean refonly, const char *name))
+DO_API(MonoAssembly *,mono_assembly_load_from,(MonoImage *image, const char*fname, int *status))
+DO_API(bool, mono_assembly_fill_assembly_name, (MonoImage *image, MonoAssemblyName *aname))
+DO_API(char* , mono_stringify_assembly_name, (MonoAssemblyName *aname))
+DO_API(int, mono_assembly_name_parse,(const char* name, MonoAssemblyName *assembly))
+DO_API(MonoAssembly*, mono_assembly_loaded, (MonoAssemblyName *aname))
+DO_API(int, mono_image_get_table_rows, (MonoImage *image, int table_id))
+DO_API(MonoClass*, mono_class_get, (MonoImage *image, guint32 type_token))
+DO_API(gboolean, mono_metadata_signature_equal, (MonoMethodSignature *sig1, MonoMethodSignature *sig2))
+
+DO_API(MonoObject *,mono_value_box,(MonoDomain *domain, MonoClass *klass, gpointer val))
+DO_API(MonoImage*,mono_class_get_image,(MonoClass *klass))
+DO_API(char,mono_signature_is_instance,(MonoMethodSignature *signature))
+DO_API(MonoMethod*,mono_method_get_last_managed,())
+DO_API(MonoClass*,mono_get_enum_class,())
+DO_API(MonoType*,mono_class_get_byref_type,(MonoClass *klass))
+
+DO_API (void,mono_field_static_get_value,(MonoVTable *vt, MonoClassField *field, void *value))
+DO_API(void,mono_unity_set_embeddinghostname,(const char* name))
+DO_API(void,mono_set_assemblies_path,(const char* name))
+
+DO_API (guint32, mono_gchandle_new, (MonoObject *obj, gboolean pinned))
+DO_API (MonoObject*, mono_gchandle_get_target, (guint32 gchandle))
+
+DO_API (guint32, mono_gchandle_new_weakref, (MonoObject *obj, gboolean track_resurrection))
+
+DO_API (gboolean, mono_gchandle_is_in_domain, (guint32 gchandle, MonoDomain *domain))
+DO_API (MonoObject*, mono_assembly_get_object, (MonoDomain *domain, MonoAssembly *assembly))
+
+DO_API (void,mono_gchandle_free, (guint32 gchandle))
+
+typedef gboolean (*MonoStackWalk) (MonoMethod *method, gint32 native_offset, gint32 il_offset, gboolean managed, gpointer data);
+DO_API (void, mono_stack_walk, (MonoStackWalk func, gpointer user_data));
+DO_API (char*, mono_pmip, (void *ip));
+DO_API (MonoObject*, mono_runtime_delegate_invoke, (MonoObject *delegate, void **params, MonoException **exc))
+
+//DO_API (void,GC_free,(void* p))
+//DO_API(void*,GC_malloc_uncollectable,(int size))
+DO_API(MonoProperty*,mono_class_get_properties,(MonoClass* klass, gpointer *iter))
+DO_API(MonoMethod*,mono_property_get_get_method,(MonoProperty *prop))
+DO_API(MonoObject *,mono_object_new_alloc_specific,(MonoVTable *vtable))
+DO_API(MonoObject *,mono_object_new_specific,(MonoVTable *vtable))
+
+DO_API (void,mono_gc_collect,(int generation))
+DO_API(int,mono_gc_max_generation,())
+
+DO_API(gint64,mono_gc_get_used_size,())
+DO_API(gint64,mono_gc_get_heap_size,())
+
+DO_API(MonoAssembly*,mono_image_get_assembly,(MonoImage *image))
+DO_API(MonoAssembly*,mono_assembly_open,(const char *filename, int *status))
+
+DO_API(gboolean,mono_class_is_enum,(MonoClass *klass))
+DO_API(MonoType* ,mono_class_enum_basetype, (MonoClass *klass))
+DO_API(gint32,mono_class_instance_size,(MonoClass *klass))
+DO_API(guint32,mono_object_get_size,(MonoObject *obj))
+DO_API(const char*,mono_image_get_filename,(MonoImage *image))
+DO_API(MonoAssembly*,mono_assembly_load_from_full,(MonoImage *image, const char *fname,int *status,gboolean refonly))
+DO_API(MonoClass*,mono_class_get_interfaces,(MonoClass* klass, gpointer *iter))
+DO_API (void,mono_assembly_close,(MonoAssembly *assembly))
+DO_API(MonoProperty*,mono_class_get_property_from_name,(MonoClass *klass, const char *name))
+DO_API(MonoMethod*,mono_class_get_method_from_name,(MonoClass *klass, const char *name, int param_count))
+DO_API(MonoClass*,mono_class_from_mono_type,(MonoType *image))
+DO_API(int, mono_class_get_rank, (MonoClass *klass));
+DO_API(MonoClass*, mono_class_get_element_class, (MonoClass *klass));
+DO_API(bool,mono_unity_class_is_interface,(MonoClass* klass))
+DO_API(bool,mono_unity_class_is_abstract,(MonoClass* klass))
+
+DO_API (gboolean,mono_domain_set,(MonoDomain *domain, gboolean force))
+DO_API (void,mono_thread_push_appdomain_ref,(MonoDomain *domain))
+DO_API (void,mono_thread_pop_appdomain_ref,())
+
+DO_API (int, mono_runtime_exec_main, (MonoMethod *method, MonoArray *args, MonoObject **exc))
+
+DO_API (MonoImage*,mono_get_corlib,())
+DO_API (MonoClassField*, mono_class_get_field_from_name, (MonoClass *klass, const char *name))
+DO_API (guint32, mono_class_get_flags, (MonoClass *klass))
+
+DO_API(int, mono_parse_default_optimizations, (const char* p))
+DO_API (void, mono_set_defaults, (int verbose_level, guint32 opts))
+DO_API(void, mono_config_parse, (const char *filename))
+DO_API(void, mono_set_dirs, (const char *assembly_dir, const char *config_dir))
+//DO_API(void,ves_icall_System_AppDomain_InternalUnload,(int domain_id))
+//DO_API(MonoObject*,ves_icall_System_AppDomain_createDomain,(MonoString *friendly_name, MonoObject *setup))
+DO_API(void,mono_jit_parse_options,(int argc, char * argv[]))
+DO_API(gpointer, mono_object_unbox, (MonoObject* o))
+
+DO_API(MonoObject*, mono_custom_attrs_get_attr, (MonoCustomAttrInfo *ainfo, MonoClass *attr_klass))
+
+DO_API(gboolean, mono_custom_attrs_has_attr, (MonoCustomAttrInfo *ainfo, MonoClass *attr_klass))
+DO_API(MonoCustomAttrInfo*, mono_custom_attrs_from_field, (MonoClass *klass, MonoClassField *field))
+DO_API(MonoCustomAttrInfo*, mono_custom_attrs_from_method, (MonoMethod *method))
+DO_API(MonoCustomAttrInfo*, mono_custom_attrs_from_class, (MonoClass *klass))
+DO_API(void, mono_custom_attrs_free, (MonoCustomAttrInfo* attr))
+
+#if UNITY_STANDALONE || UNITY_EDITOR
+// DllImport fallback handling to load native libraries from custom locations
+typedef void* (*MonoDlFallbackLoad) (const char *name, int flags, char **err, void *user_data);
+typedef void* (*MonoDlFallbackSymbol) (void *handle, const char *name, char **err, void *user_data);
+typedef void* (*MonoDlFallbackClose) (void *handle, void *user_data);
+DO_API(MonoDlFallbackHandler*, mono_dl_fallback_register, (MonoDlFallbackLoad load_func, MonoDlFallbackSymbol symbol_func, MonoDlFallbackClose close_func, void *user_data))
+DO_API(void, mono_dl_fallback_unregister, (MonoDlFallbackHandler *handler))
+#endif
+
+#if UNITY_EDITOR
+typedef void (*vprintf_func)(const char* msg, va_list args);
+DO_API(void, mono_unity_set_vprintf_func, (vprintf_func func))
+DO_API(void*, mono_loader_get_last_error, (void))
+DO_API(MonoException*, mono_loader_error_prepare_exception, (void *error))
+#endif
+
+typedef void (*register_object_callback)(gpointer* arr, int size, void* userdata);
+DO_API(void*, mono_unity_liveness_calculation_begin, (MonoClass* filter, int max_object_count, register_object_callback callback, void* userdata))
+DO_API(void, mono_unity_liveness_calculation_end, (void* state))
+DO_API(void, mono_unity_liveness_calculation_from_root, (MonoObject* root, void* state))
+DO_API(void, mono_unity_liveness_calculation_from_statics, (void* state))
+
+
+///@TODO add this as an optimization when upgrading mono, used by MonoStringNewLength:
+/// mono_string_new_len (MonoDomain *domain, const char *text, guint length);
+
+// Profiler
+#if ENABLE_MONO_MEMORY_PROFILER || ENABLE_MONO_HEAPSHOT
+typedef void (*MonoProfileFunc) (void *prof);
+typedef void (*MonoProfileMethodFunc) (void *prof, MonoMethod *method);
+typedef void (*MonoProfileExceptionFunc) (void *prof, MonoObject *object);
+typedef void (*MonoProfileExceptionClauseFunc) (void *prof, MonoMethod *method, int clause_type, int clause_num);
+typedef void (*MonoProfileGCFunc) (void *prof, int event, int generation);
+typedef void (*MonoProfileGCResizeFunc) (void *prof, SInt64 new_size);
+typedef void (*MonoProfileAllocFunc) (void *prof, MonoObject *obj, MonoClass *klass);
+typedef void (*MonoProfileStatCallChainFunc) (void *prof, int call_chain_depth, guchar **ip, void *context);
+typedef void (*MonoProfileStatFunc) (void *prof, guchar *ip, void *context);
+
+
+DO_API(void, mono_profiler_install, (void *prof, MonoProfileFunc shutdown_callback))
+DO_API(void, mono_profiler_set_events, (int events))
+DO_API(void, mono_profiler_install_enter_leave, (MonoProfileMethodFunc enter, MonoProfileMethodFunc fleave))
+DO_API(void, mono_profiler_install_gc, (MonoProfileGCFunc callback, MonoProfileGCResizeFunc heap_resize_callback))
+DO_API(void, mono_profiler_install_allocation, (MonoProfileAllocFunc callback))
+//DO_API(void, mono_gc_base_init, ())
+//DO_API(void, mono_profiler_install_statistical, (MonoProfileStatFunc callback))
+//DO_API(void, mono_profiler_install_statistical_call_chain, (MonoProfileStatCallChainFunc callback, int call_chain_depth))
+DO_API(void, mono_profiler_install_exception, (MonoProfileExceptionFunc throw_callback, MonoProfileMethodFunc exc_method_leave, MonoProfileExceptionClauseFunc clause_callback))
+
+# if ENABLE_MONO_HEAPSHOT
+ typedef void (*MonoProfileClassFunc) (void *prof, MonoClass* klass);
+ typedef void (*MonoProfileClassResult) (void *prof, MonoClass* klass,int result);
+
+ DO_API(void, mono_profiler_install_class, (MonoProfileClassFunc start_load, MonoProfileClassResult end_load, MonoProfileClassFunc start_unload, MonoProfileClassFunc end_unload))
+ DO_API(gboolean, mono_type_is_reference, (MonoType *type));
+ DO_API(gint32, mono_class_value_size, (MonoClass *klass, guint32 *align));
+ DO_API(char*, mono_array_addr_with_size, (MonoArray *array, int size, uintptr_t idx));
+ #define mono_array_addr(array,type,index) ((type*)(void*) mono_array_addr_with_size (array, sizeof (type), index))
+ #define mono_array_get(array,type,index) ( *(type*)mono_array_addr ((array), type, (index)) )
+ DO_API(gint32, mono_array_element_size, (MonoClass *ac));
+ DO_API(char*, mono_type_full_name, (MonoType *type));
+ typedef void (*MonoDomainFunc) (MonoDomain *domain, void* user_data);
+ DO_API(void, mono_domain_foreach, (MonoDomainFunc func, void* user_data));
+ DO_API(gboolean, mono_object_is_alive, (MonoObject* obj));
+# endif
+#endif
+
+#if UNITY_IPHONE || UNITY_PEPPER
+DO_API(void, mono_aot_register_module, (void *aot_info))
+#endif
+
+// GLib functions
+#if UNITY_IPHONE || UNITY_PEPPER // iPhone uses eglib which just calls free
+#define g_free free
+#elif MONO_2_12
+#define g_free mono_unity_g_free
+DO_API(void,mono_unity_g_free,(void* p))
+#else
+DO_API(void,g_free,(void* p))
+#endif
+/*
+#if UNITY_PS3 || UNITY_XENON || UNITY_ANDROID
+static inline char *g_strdup (const char *str) { if (str) {return strdup (str);} return NULL; }
+#define g_mem_set_vtable(x)
+#else
+DO_API(char*,g_strdup,(const char *image))
+DO_API(void,g_mem_set_vtable,(gpointer vtable))
+#endif*/
+
+
+//DO_API(void,macosx_register_exception_handler,())
+
+DO_API(void, mono_trace_set_level_string, (const char *value))
+DO_API(void, mono_trace_set_mask_string, (const char *value))
+
+#if UNITY_WII
+DO_API(char*, g_strdup_d, (char const* str))
+
+// memory and string functions
+DO_API(void*, console_g_malloc, ( size_t size ))
+DO_API(void*, console_g_calloc, ( size_t size, size_t elemSize ))
+DO_API(void*, console_g_realloc, (void *memblock, size_t size))
+DO_API(void, console_g_free, ( void* p ))
+
+// specific allocations
+DO_API(void*, console_g_malloc_image, (size_t size))
+DO_API(void, console_g_free_image, (void* p))
+
+DO_API(void*, console_g_malloc0_handles, (size_t size))
+DO_API(void, console_g_free_handles, (void* p))
+
+#define g_free console_g_free
+//#define g_malloc_d(x) console_g_malloc(x)
+//#define g_calloc_d(obj,size) console_g_calloc(obj,size)
+#define g_free_d(x) console_g_free(x)
+#define g_strdup_d(x) g_strdup(x)
+
+DO_API(void*, g_memdup, (void const* mem, unsigned int byte_size))
+DO_API(char*, g_strdup, (const char *str))
+
+#endif
+
+#if UNITY_XENON || UNITY_PS3
+// memory and string functions
+DO_API(void*, console_g_malloc, ( size_t size ))
+DO_API(void*, console_g_calloc, ( size_t size, size_t elemSize ))
+DO_API(void*, console_g_realloc, (void *memblock, size_t size))
+DO_API(void, console_g_free, ( void* p ))
+
+#define g_free console_g_free
+#endif
+
+#if UNITY_PEPPER
+DO_API(void, mono_set_corlib_data, (void *data, size_t size))
+DO_API(void, mono_runtime_set_no_exec, (gboolean val))
+DO_API(void, mono_jit_set_aot_only, (gboolean val))
+typedef MonoAssembly *(*MonoAssemblySearchFunc) (char **aname, void* user_data);
+
+DO_API(void, mono_install_assembly_postload_search_hook, (MonoAssemblySearchFunc func, gpointer user_data))
+#endif
+
+#if UNITY_PS3 || UNITY_XENON
+DO_API(char*, g_strdup_d, (char const* str))
+#define g_strdup_d(x) g_strdup(x)
+DO_API(void*, g_memdup, (void const* mem, unsigned int byte_size))
+//DO_API(char*, g_strdup, (const char *str))
+
+#endif
+
+#if UNITY_XENON
+DO_API(void, mono_xenon_dl_set_min_version, (DWORD min_version))
+DO_API(void, mono_runtime_set_main_args, (const char** args, int num_args))
+#endif
+
+#if UNITY_PLUGINS_AVAILABLE && UNITY_XENON
+DO_API(MonoDl*, mono_dl_open, (const char* name, int flags, char** error_msg))
+DO_API(char*, mono_dl_symbol, (void* MonoDl, const char* name, void** symbol))
+DO_API(void, mono_dl_close, (void* MonoDl))
+DO_API(MonoDl*, cached_module_load, (const char* name, int flags, char** error_msg))
+#endif
+
+#if UNITY_OSX
+DO_API(int,mono_backtrace_from_context, (void* context, void* array[], int count))
+#endif
+
+#undef DO_API
+#undef DO_API_NO_RETURN
diff --git a/Runtime/Mono/MonoHeapShot.cpp b/Runtime/Mono/MonoHeapShot.cpp
new file mode 100644
index 0000000..65b4f14
--- /dev/null
+++ b/Runtime/Mono/MonoHeapShot.cpp
@@ -0,0 +1,510 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Threads/Mutex.h"
+
+#include "MonoHeapShot.h"
+
+#if ENABLE_MONO_HEAPSHOT
+
+#if defined(SN_TARGET_PS3)
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+#endif
+
+#include "MonoHeapShotWriter.h"
+#include "Runtime/Misc/PlayerSettings.h"
+#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h"
+
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * heap-shot.c
+ *
+ * Copyright (C) 2005 Novell, Inc.
+ *
+ * This profiler is unsafe: it does in signal context almost everything
+ * that must not be done there, like taking locks, running a GC etc.
+ * It takes a lock in heap_shot_gc_func(), which can cause a deadlock.
+ * It doesn't deal with moving objects.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA.
+ */
+
+/*
+#include <string.h>
+#include <glib.h>
+#include <pthread.h>
+#include <mono/metadata/assembly.h>
+#include <mono/metadata/class.h>
+#include <mono/metadata/debug-helpers.h>
+#include <mono/metadata/object.h>
+#include <mono/metadata/profiler.h>
+#include <mono/metadata/mono-gc.h>
+#include <mono/metadata/metadata.h>
+#include <mono/metadata/debug-helpers.h>
+#include <unistd.h>
+#include <time.h>
+
+*/
+
+
+/*
+typedef pthread_mutex_t mono_mutex_t;
+#define mono_mutex_init(mutex,attr) pthread_mutex_init (mutex, attr)
+#define mono_mutex_lock(mutex) pthread_mutex_lock (mutex)
+#define mono_mutex_unlock(mutex) pthread_mutex_unlock (mutex)
+*/
+//#define MONO_CONSOLES (UNITY_XENON || UNITY_PS3)
+
+//#if MONO_CONSOLES
+//#define MONOPROFILER MonoProfiler
+//#define MONOGCEVENT MonoGCEvent
+//#else
+#define MONOPROFILER void
+#define MONOGCEVENT int
+//#endif
+
+#if UNITY_WII
+typedef OSMutex mono_mutex_t;
+#define mono_mutex_init(mutex,attr) OSInitMutex(mutex);
+#define mono_mutex_lock(mutex) OSLockMutex(mutex)
+#define mono_mutex_unlock(mutex) OSUnlockMutex(mutex);
+#else
+typedef Mutex* mono_mutex_t;
+#define mono_mutex_init(mutex,attr) *mutex = new Mutex()
+#define mono_mutex_lock(mutex) (*mutex)->Lock()
+#define mono_mutex_unlock(mutex) (*mutex)->Unlock()
+#endif
+
+
+struct _MonoProfiler
+{
+ mono_mutex_t lock;
+ mono_mutex_t dump_lock;
+ ObjectsHash *objects_hash;
+ ObjectsHash *work_objects_hash;
+
+ ClassHash *class_hash;
+ ClassHash *exclude_class_hash;
+ ClassHash *work_class_hash;
+ MonoHeapShotWriter dumpfile_writer;
+ const char *out_file_name;
+ int dump_count;
+};
+typedef _MonoProfiler MonoProfiler;
+
+static MonoProfiler* s_MonoProfiler = NULL;
+
+MonoProfiler* HeapShotCreateMonoProfiler (const char *outfilename)
+{
+ //struct sigaction sa;
+ MonoProfiler* p = new MonoProfiler();
+ s_MonoProfiler = p;
+
+ mono_mutex_init (&p->lock, NULL);
+ mono_mutex_init (&p->dump_lock, NULL);
+
+ p->objects_hash = new ObjectsHash();
+ p->class_hash = new ClassHash();
+ p->exclude_class_hash = new ClassHash();
+ p->out_file_name = outfilename;
+ p->dump_count = 0;
+
+ // Sets the PROF signal
+ //sa.sa_handler = profiler_signal_handler;
+ //sigemptyset (&sa.sa_mask);
+ //sa.sa_flags = 0;
+ //g_assert (sigaction (SIGPROF, &sa, NULL) != -1);
+
+ return p;
+}
+
+static gboolean heap_scan_object (MonoProfiler *p, MonoObject *obj, MonoClass *klass, MonoClassField *parent_field)
+{
+ gpointer iter;
+ MonoClassField *field;
+ gboolean has_refs = FALSE;
+ MonoClass *cur_klass = klass;
+
+ do {
+ iter = NULL;
+ while ((field = mono_class_get_fields (cur_klass, &iter)) != NULL) {
+ MonoType* field_type = mono_field_get_type (field);
+ // Skip static fields
+ if (mono_field_get_flags (field) & 0x0010 /*FIELD_ATTRIBUTE_STATIC*/)
+ continue;
+
+ if (mono_type_is_reference (field_type)) {
+ // Dump the object reference
+ MonoObject* ref;
+ has_refs = true;
+ mono_field_get_value (obj, field, &ref);
+ if (ref && p->work_objects_hash->find(ref) != p->work_objects_hash->end())
+ p->dumpfile_writer.DumpReference (ref, parent_field ? parent_field : field);
+ }
+ else {
+ MonoClass *fclass = mono_class_from_mono_type (field_type);
+ if (fclass && mono_class_is_valuetype (fclass)) {
+ if (p->exclude_class_hash->find(fclass) != p->exclude_class_hash->end())
+ continue;
+ // It's a value type. Check if the class is big enough to hold references
+ int size = mono_class_value_size (fclass, NULL);
+ if (size >= sizeof(gpointer) && fclass != cur_klass) {
+ // Get the object value and scan it
+ char* vop = (char*)malloc (size);
+ mono_field_get_value (obj, field, vop);
+ // Recursively scan the object
+ if (heap_scan_object (p, (MonoObject*)(vop - sizeof(MonoObject)), fclass, parent_field))
+ has_refs = true;
+ free (vop);
+ }
+ }
+ }
+ }
+ cur_klass = mono_class_get_parent (cur_klass);
+ } while (cur_klass);
+
+ // If the class doesn't contain references, register in the exclude_class_hash table,
+ // so it won't be scanned again.
+ if (!has_refs && p->exclude_class_hash->find(klass) == p->exclude_class_hash->end())
+ {
+ (*p->exclude_class_hash)[klass] = klass;
+ //g_hash_table_insert (p->exclude_class_hash, klass, klass);
+ }
+ return has_refs;
+}
+
+static void heap_scan_array (MonoProfiler *p, MonoObject *obj, MonoClass *klass)
+{
+ MonoArray *array = (MonoArray *) obj;
+ MonoClass *eklass = mono_class_get_element_class (klass);
+ gboolean has_refs = FALSE;
+
+ if (!mono_class_is_valuetype (eklass))
+ {
+ // It's an array of object references, write all of them in the output file
+ int n;
+ for (n=0; n<mono_array_length (array); n++) {
+ MonoObject *ref = mono_array_get (array, MonoObject*, n);
+ if (ref && p->work_objects_hash->find(ref) != p->work_objects_hash->end())
+ p->dumpfile_writer.DumpReference (ref, NULL);
+ }
+ has_refs = true;
+ }
+ //else if (!g_hash_table_lookup (p->exclude_class_hash, eklass))
+ else if (p->exclude_class_hash->find(eklass) == p->exclude_class_hash->end())
+ {
+ // It's an array of value type objects. Each object will be scanned
+ // by recursively calling heap_scan_object for each member
+ int n;
+ gint32 esize = mono_array_element_size (klass);
+ if (esize >= sizeof(gpointer)) {
+ // The type is big enough to contain references.
+ // Scan the array.
+ for (n=0; n<mono_array_length (array); n++) {
+ char *ref = (char *) mono_array_addr_with_size (array, esize, n);
+ ref -= sizeof (MonoObject);
+ if (heap_scan_object (p, (MonoObject *) ref, eklass, NULL))
+ has_refs = true;
+ else
+ // The class has no fields, it makes no sense to continue
+ break;
+ }
+ }
+ }
+ // If the class doesn't contain references, register in the exclude_class_hash table,
+ // so it won't be scanned again.
+ if (!has_refs && p->exclude_class_hash->find(klass) == p->exclude_class_hash->end())
+ {
+ (*p->exclude_class_hash)[klass] = klass;
+ //g_hash_table_insert (p->exclude_class_hash, klass, klass);
+ }
+}
+
+static void heap_scan_fn (gpointer key, gpointer value, gpointer user_data)
+{
+ MonoProfiler *p = (MonoProfiler*)user_data;
+ MonoObject *obj = (MonoObject*)key;
+ MonoClass *klass = (MonoClass*)value;
+
+ // Write the object header
+ p->dumpfile_writer.BeginObjectDump (obj, klass);
+
+ // If the type is registered as not having reference fields, just return
+ //if (g_hash_table_lookup (p->exclude_class_hash, klass))
+ if (p->exclude_class_hash->find(klass) != p->exclude_class_hash->end())
+ {
+ p->dumpfile_writer.EndObjectDump ();
+ return;
+ }
+
+ if (mono_class_get_rank (klass)) {
+ // It's an array
+ heap_scan_array (p, obj, klass);
+ }
+ else {
+ heap_scan_object (p, obj, klass, NULL);
+ }
+
+ // Write the object end marker
+ p->dumpfile_writer.EndObjectDump ();
+}
+
+static void dump_static_fields_fn (gpointer key, gpointer value, gpointer user_data)
+{
+ MonoClassField *field;
+ gpointer iter = NULL;
+ gboolean added = FALSE;
+ MonoClass *klass = (MonoClass*)key;
+ MonoProfiler *p = (MonoProfiler*)((gpointer*)user_data)[0];
+ MonoDomain *domain = (MonoDomain*)((gpointer*)user_data)[1];
+ MonoVTable *vtable = NULL;
+ gpointer field_value;
+
+ if (strstr (mono_type_full_name (mono_class_get_type (klass)), "`"))
+ return;
+
+ while ((field = mono_class_get_fields (klass, &iter)) != NULL) {
+ if (mono_field_get_flags (field) & 0x0010 /*FIELD_ATTRIBUTE_STATIC*/) {
+ // Dump the class only if it has static fields
+ if (!added) {
+ p->dumpfile_writer.BeginObjectDump (NULL, klass);
+ vtable = mono_class_vtable (domain, klass);
+ added = true;
+ }
+ MonoType* field_type = mono_field_get_type (field);
+
+ if (mono_type_is_reference (field_type)) {
+ mono_field_static_get_value (vtable, field, &field_value);
+ if (field_value) {
+ p->dumpfile_writer.DumpReference (field_value, field);
+ }
+ } else {
+ MonoClass *fclass = mono_class_from_mono_type (field_type);
+ if (fclass && mono_class_is_valuetype (fclass)) {
+ //if (g_hash_table_lookup (p->exclude_class_hash, fclass))
+ if (p->exclude_class_hash->find(fclass) != p->exclude_class_hash->end())
+ continue;
+ int size = mono_class_value_size (fclass, NULL);
+ if (size >= sizeof(gpointer) && fclass != klass) {
+ // Get the object value and scan it
+ char* vop = (char*)malloc (size);
+ mono_field_static_get_value (vtable, field, vop);
+ // Recursively scan the object
+ heap_scan_object (p, (MonoObject*)(vop - sizeof(MonoObject)), fclass, field);
+ free (vop);
+ }
+ }
+ }
+ }
+ }
+ if (added)
+ p->dumpfile_writer.EndObjectDump ();
+}
+
+static void dump_domain_static_fields_fn (MonoDomain *domain, gpointer user_data)
+{
+ MonoProfiler *p = (MonoProfiler*)user_data;
+ gpointer data [2];
+ data [0] = p;
+ data [1] = domain;
+ //g_hash_table_foreach (p->work_class_hash, dump_static_fields_fn, &data);
+ for (ClassHash::iterator item = p->work_class_hash->begin();
+ item != p->work_class_hash->end();
+ item++)
+ {
+ dump_static_fields_fn(item->first, item->second, &data);
+ }
+}
+
+void HeapShotDumpObjectMap (HeapShotData& data)
+{
+ if (s_MonoProfiler == NULL)
+ {
+ printf_console("Mono HeapShot is disabled? Do you have Mono Debugger enabled?");
+ return;
+ }
+ MonoProfiler *p = s_MonoProfiler;
+
+ mono_gc_collect (0);
+ mono_mutex_lock (&p->dump_lock);
+
+ // Make a copy of the hashtables which collect object and type data,
+ // to avoid deadlocks while inspecting the data
+ mono_mutex_lock (&p->lock);
+
+ p->work_objects_hash = new ObjectsHash();
+ p->work_class_hash = new ClassHash();
+ *p->work_objects_hash = *p->objects_hash;
+ //g_hash_table_foreach (p->objects_hash, clone_hash_table_fn, p->work_objects_hash);
+ //g_hash_table_foreach (p->class_hash, clone_hash_table_fn, p->work_class_hash);
+ *p->work_class_hash = *p->class_hash;
+
+ mono_mutex_unlock (&p->lock);
+
+ char fileName[255];
+ sprintf(fileName, "%s_%d.omap", p->out_file_name, p->dump_count);
+ printf_console ("Dumping object map to file '%s'\n", fileName);
+ p->dumpfile_writer.Start(&data);
+
+ // Dump object information
+ //g_hash_table_foreach (p->work_objects_hash, heap_scan_fn, p);
+ for (ObjectsHash::iterator item = p->work_objects_hash->begin();
+ item != p->work_objects_hash->end();
+ item++)
+ {
+ heap_scan_fn(item->first, item->second, p);
+ }
+
+ p->dump_count++;
+
+ // Dump static field references for each domain
+ // This can cause new object allocations
+ mono_domain_foreach (dump_domain_static_fields_fn, p);
+ p->dumpfile_writer.DumpObjectsReferencedByUnity();
+ p->dumpfile_writer.End();
+
+ //g_hash_table_destroy (p->work_objects_hash);
+ delete p->work_objects_hash;
+ delete p->work_class_hash;
+
+ printf_console ("done\n");
+
+ mono_mutex_unlock (&p->dump_lock);
+}
+
+void HeapShotAllocFunc (MonoProfiler *p, MonoObject *obj, MonoClass *klass)
+{
+ mono_mutex_lock (&p->lock);
+ //g_hash_table_insert (p->objects_hash, obj, klass);
+ (*p->objects_hash)[obj] = klass;
+ mono_mutex_unlock (&p->lock);
+}
+
+static gboolean post_gc_clean_objects_fn (gpointer key, gpointer value, gpointer user_data)
+{
+ MonoObject *obj = (MonoObject*)key;
+ return !mono_object_is_alive (obj);
+}
+
+void HeapShotGCFunc (MonoProfiler *p, MonoGCEvent e, int gen)
+{
+ if (e != MONO_GC_EVENT_MARK_END)
+ return;
+
+ mono_mutex_lock (&p->lock);
+ //g_hash_table_foreach_remove (p->objects_hash, post_gc_clean_objects_fn, NULL);
+ for (ObjectsHash::iterator item = p->objects_hash->begin();
+ item != p->objects_hash->end();
+ )
+ {
+ if (post_gc_clean_objects_fn(item->first, item->second, NULL))
+ {
+ ObjectsHash::iterator toDelete = item;
+ item++;
+ p->objects_hash->erase(toDelete);
+ }
+ else
+ {
+ item++;
+ }
+ }
+ mono_mutex_unlock (&p->lock);
+}
+
+void HeapShotLoadClassFunc (MonoProfiler *p, MonoClass *klass, int result)
+{
+ mono_mutex_lock (&p->lock);
+ //g_hash_table_insert (p->class_hash, klass, klass);
+ (*p->class_hash)[klass] = klass;
+ mono_mutex_unlock (&p->lock);
+}
+
+ void HeapShotUnloadClassFunc (MonoProfiler *p, MonoClass *klass)
+{
+ mono_mutex_lock (&p->lock);
+ //g_hash_table_remove (p->class_hash, klass);
+ p->class_hash->erase(klass);
+ mono_mutex_unlock (&p->lock);
+}
+
+static void GCMonoCallback (MONOPROFILER* prof, MONOGCEVENT event, int generation)
+{
+ HeapShotGCFunc((MonoProfiler*)prof, (MonoGCEvent)event, generation);
+}
+
+static void GCMonoResizeCallback (MONOPROFILER* prof, SInt64 new_size)
+{
+ //printf("GCMonoResizeCallback\n");
+}
+
+static void GCMonoAllocationCallback (MONOPROFILER* prof, MonoObject *obj, MonoClass *klass)
+{
+ //printf("GCMonoAllocationCallback: Allocating %d bytes for %s\n", mono_object_get_size(obj), mono_class_get_name(klass));
+ HeapShotAllocFunc((MonoProfiler*)prof, obj, klass);
+}
+static void MonoLoadClassFunc (MONOPROFILER *p, MonoClass *klass, int result)
+{
+ //printf("MonoLoadClassFunc: %s %d\n", mono_class_get_name(klass), result);
+ HeapShotLoadClassFunc((MonoProfiler*)p, klass, result);
+}
+static void MonoUnloadClassFunc (MONOPROFILER *p, MonoClass *klass)
+{
+ HeapShotUnloadClassFunc((MonoProfiler*)p, klass);
+}
+void InstallMonoHeapshot()
+{
+#if UNITY_WII
+ if (GetPlayerSettingsPtr() && GetPlayerSettings().wiiHio2Usage != 1) return;
+#endif
+
+#if ENABLE_PLAYERCONNECTION
+ if (PlayerConnection::ms_Instance && PlayerConnection::Get().AllowDebugging())
+ {
+ printf_console("\nCan not initialize mono heapshot when script debugging is on.\n");
+ return;
+ }
+#endif
+
+ printf_console("\nInitializing mono heapshot\n");
+ s_MonoProfiler = HeapShotCreateMonoProfiler ("test.txt");
+
+ mono_profiler_install (s_MonoProfiler, NULL);
+ mono_profiler_install_gc(GCMonoCallback, GCMonoResizeCallback);
+ mono_profiler_install_allocation(GCMonoAllocationCallback);
+ //mono_profiler_install_enter_leave (EnterMonoMethod, LeaveMonoMethod);
+ //mono_profiler_install_jit_compile (EnterJITCompileMonoMethod, LeaveJITCompileMonoMethod);
+ //mono_profiler_install_gc_roots (GCHandleCallback, GCRootCallback);
+ mono_profiler_install_class (NULL, MonoLoadClassFunc, MonoUnloadClassFunc, NULL);
+
+ int flags =
+ MONO_PROFILE_GC |
+ MONO_PROFILE_ALLOCATIONS |
+ //MONO_PROFILE_ENTER_LEAVE |
+ //MONO_PROFILE_JIT_COMPILATION |
+ //MONO_PROFILE_GC_ROOTS |
+ MONO_PROFILE_CLASS_EVENTS;
+
+ mono_profiler_set_events ((MonoProfileFlags)flags);
+}
+
+
+#endif
diff --git a/Runtime/Mono/MonoHeapShot.h b/Runtime/Mono/MonoHeapShot.h
new file mode 100644
index 0000000..bea44f2
--- /dev/null
+++ b/Runtime/Mono/MonoHeapShot.h
@@ -0,0 +1,81 @@
+#ifndef MONO_HEAPSHOT_H
+#define MONO_HEAPSHOT_H
+
+
+#if ENABLE_MONO_HEAPSHOT
+
+#include "Runtime/Mono/MonoTypes.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+extern "C"
+{
+ typedef enum
+ {
+ MONO_GC_EVENT_START,
+ MONO_GC_EVENT_MARK_START,
+ MONO_GC_EVENT_MARK_END,
+ MONO_GC_EVENT_RECLAIM_START,
+ MONO_GC_EVENT_RECLAIM_END,
+ MONO_GC_EVENT_END,
+ MONO_GC_EVENT_PRE_STOP_WORLD,
+ MONO_GC_EVENT_POST_STOP_WORLD,
+ MONO_GC_EVENT_PRE_START_WORLD,
+ MONO_GC_EVENT_POST_START_WORLD
+ } MonoGCEvent;
+
+ typedef struct _MonoProfiler MonoProfiler;
+ /*
+ typedef gint32 mono_bool;
+
+ extern mono_bool mono_type_is_reference (MonoType *type);
+ extern gint32 mono_class_value_size (MonoClass *klass, guint32 *align);
+ extern MonoClass* mono_class_get_element_class (MonoClass *klass);
+
+ extern char* mono_array_addr_with_size (MonoArray *array, int size, uintptr_t idx);
+ #define mono_array_addr(array,type,index) ((type*)(void*) mono_array_addr_with_size (array, sizeof (type), index))
+ #define mono_array_get(array,type,index) ( *(type*)mono_array_addr ((array), type, (index)) )
+
+ extern gint32 mono_array_element_size (MonoClass *ac);
+ extern int mono_class_get_rank (MonoClass *klass);
+ extern char* mono_type_full_name (MonoType *type);
+ typedef void (*MonoDomainFunc) (MonoDomain *domain, void* user_data);
+ extern void mono_domain_foreach (MonoDomainFunc func, void* user_data);
+ extern mono_bool mono_object_is_alive (MonoObject* obj);
+
+ // Note: theser are different than the ones defined in MonoFunctions.h
+ typedef void (*MonoProfileFunc) (MonoProfiler *prof);
+ typedef void (*MonoProfileGCFunc) (MonoProfiler *prof, MonoGCEvent event, int generation);
+ typedef void (*MonoProfileGCResizeFunc) (MonoProfiler *prof, gint64 new_size);
+ typedef void (*MonoProfileClassFunc) (MonoProfiler *prof, MonoClass *klass);
+ typedef void (*MonoProfileAllocFunc) (MonoProfiler *prof, MonoObject *obj, MonoClass *klass);
+ typedef void (*MonoProfileClassResult) (MonoProfiler *prof, MonoClass *klass, int result);
+
+ extern void mono_profiler_install (MonoProfiler *prof, MonoProfileFunc shutdown_callback);
+ extern void mono_profiler_install_allocation (MonoProfileAllocFunc callback);
+ extern void mono_profiler_install_class (MonoProfileClassFunc start_load, MonoProfileClassResult end_load, MonoProfileClassFunc start_unload, MonoProfileClassFunc end_unload);
+ extern void mono_profiler_install_gc (MonoProfileGCFunc callback, MonoProfileGCResizeFunc heap_resize_callback);
+ extern void mono_profiler_set_events (MonoProfileFlags events);
+ */
+
+ struct MonoThreadsSync;
+ struct MonoObject
+ {
+ MonoVTable *vtable;
+ MonoThreadsSync *synchronisation;
+ } ;
+}
+
+typedef std::map<MonoObject*, MonoClass*> ObjectsHash;
+typedef std::map<MonoClass*, MonoClass*> ClassHash;
+typedef std::vector<UInt8> HeapShotData;
+
+MonoProfiler* HeapShotCreateMonoProfiler (const char *outfilename);
+void HeapShotAllocFunc (MonoProfiler *p, MonoObject *obj, MonoClass *klass);
+void HeapShotGCFunc (MonoProfiler *p, MonoGCEvent e, int gen);
+void HeapShotLoadClassFunc (MonoProfiler *p, MonoClass *klass, int result);
+void HeapShotUnloadClassFunc (MonoProfiler *p, MonoClass *klass);
+void HeapShotDumpObjectMap (HeapShotData& data);
+void InstallMonoHeapshot();
+#endif
+
+#endif
diff --git a/Runtime/Mono/MonoHeapShotWriter.cpp b/Runtime/Mono/MonoHeapShotWriter.cpp
new file mode 100644
index 0000000..ae615c0
--- /dev/null
+++ b/Runtime/Mono/MonoHeapShotWriter.cpp
@@ -0,0 +1,267 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * outfile-writer.c
+ *
+ * Copyright (C) 2005 Novell, Inc.
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA.
+ */
+#include "UnityPrefix.h"
+
+#include "MonoHeapShotWriter.h"
+
+#if ENABLE_MONO_HEAPSHOT
+#include "Runtime/Serialize/SwapEndianBytes.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Allocator/LinearAllocator.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+
+#define MAGIC_NUMBER 0x4eabfdd1
+#define FILE_FORMAT_VERSION 6
+#define FILE_LABEL "heap-shot logfile"
+#define TAG_TYPE 0x01
+#define TAG_OBJECT 0x02
+#define TAG_UNITY 0x03
+#define TAG_EOS 0xff
+
+
+
+extern "C"
+{
+// extern char* mono_type_full_name (MonoType *type);
+ void mono_free (void* p)
+ {
+ g_free(p);
+ }
+}
+
+
+MonoHeapShotWriter::MonoHeapShotWriter()
+{
+ m_Data = NULL;
+ m_SeenItems = new ClassHash();
+}
+MonoHeapShotWriter::~MonoHeapShotWriter()
+{
+ delete m_SeenItems;
+}
+void MonoHeapShotWriter::Start(HeapShotData* data)
+{
+ m_Data = data;
+ m_Data->clear();
+ m_SeenItems->clear();
+ WriteUInt32 (MAGIC_NUMBER);
+ WriteInt32 (FILE_FORMAT_VERSION);
+ WriteString (FILE_LABEL);
+ m_SavedOutfileOffset = m_Data->size();
+ // we update these after dumping all objects
+ WriteInt32 (0); // total # of types
+ WriteInt32 (0); // total # of objects
+ WriteInt32 (0); // total # of references
+ WriteInt32 (0); // total # of fields
+}
+
+void MonoHeapShotWriter::End()
+{
+ // Write out the end-of-stream tag.
+ WriteByte (TAG_EOS);
+
+ memcpy(&(*m_Data)[m_SavedOutfileOffset], &m_TypeCount, sizeof(SInt32));
+ memcpy(&(*m_Data)[m_SavedOutfileOffset + sizeof(SInt32)], &m_ObjecCount, sizeof(SInt32));
+ memcpy(&(*m_Data)[m_SavedOutfileOffset + sizeof(SInt32) * 2], &m_ReferenceCount, sizeof(SInt32));
+ memcpy(&(*m_Data)[m_SavedOutfileOffset + sizeof(SInt32) * 3], &m_FieldCount, sizeof(SInt32));
+}
+void MonoHeapShotWriter::BeginObjectDump(MonoObject* obj, MonoClass* klass)
+{
+ char *name;
+
+ /* First, add this type if we haven't seen it before. */
+ //if (g_hash_table_lookup (m_SeenItems, klass) == NULL)
+ if (m_SeenItems->find(klass) == m_SeenItems->end())
+ {
+ MonoClassField *field;
+ MonoClass *cur_klass = klass;
+ void* iter;
+
+ name = mono_type_full_name (mono_class_get_type (klass));
+ WriteByte (TAG_TYPE);
+ WritePointer (klass);
+ WriteString (name);
+ mono_free (name);
+ (*m_SeenItems)[klass] = klass;
+ //g_hash_table_insert (m_SeenItems, klass, klass);
+ ++m_TypeCount;
+
+ // Write every field
+ do
+ {
+ iter = NULL;
+ while ((field = mono_class_get_fields (cur_klass, &iter)) != NULL) {
+ WritePointer (field);
+ WriteString (mono_field_get_name (field));
+ m_FieldCount++;
+ }
+ cur_klass = mono_class_get_parent (cur_klass);
+ }
+ while (cur_klass);
+ WritePointer (NULL);
+ }
+
+
+ WriteByte (TAG_OBJECT);
+ if (obj)
+ {
+ WritePointer ((void*)obj); // id of the object
+ WritePointer (klass); // class
+ WriteInt32 ((SInt32)mono_object_get_size (obj));
+ }
+ else
+ {
+ // Used to register references from static class members
+ WritePointer ((void*)klass);
+ WritePointer (klass);
+ WriteInt32 ((SInt32)0);
+ }
+ m_ObjecCount++;
+
+}
+
+void MonoHeapShotWriter::EndObjectDump()
+{
+ WritePointer (NULL); // no more references
+}
+
+void MonoHeapShotWriter::DumpReference(void* ref, void* field)
+{
+ WritePointer (ref);
+ WritePointer (field);
+ m_ReferenceCount++;
+}
+void MonoHeapShotWriter::DumpObjectsReferencedByUnity()
+{
+ FindMode mode = Scripting::kFindAssets;
+ MonoClass* compareKlass = GetMonoManager().GetBuiltinMonoClass ("Object", false);
+ if (compareKlass == NULL)
+ {
+ ErrorString ("DumpObjectsReferencedByUnity: Invalid Type");
+ return ;
+ }
+
+ int classID = GetClassIDFromScriptingClass (compareKlass);
+
+ if (classID == -1)
+ {
+ string klassName = mono_class_get_name (compareKlass);
+ ErrorString ("DumpObjectsReferencedByUnity: The type has to be derived from UnityEngine.Object. Type is " + klassName + ".");
+ return ;
+ }
+
+ // Gather the derived objects
+ std::vector<SInt32> objects;
+ Object::FindAllDerivedObjects (classID, &objects);
+
+ sort(objects.begin(), objects.end());
+
+ // We might need to ignore some objects which are not derived from the mono class but from MonoBehaviour
+ // so we store them in a buffer and then copy them into the mono array
+ MonoObject** monoObjects;
+ ALLOC_TEMP(monoObjects, MonoObject*, objects.size(), kMemProfiler);
+
+
+ int count = 0;
+ for (int i=0;i<objects.size ();i++)
+ {
+ Object& object = *PPtr<Object> (objects[i]);
+ if (object.TestHideFlag (Object::kDontSave) && mode != Scripting::kFindAnything)
+ continue;
+
+
+ MonoObject* mono = ScriptingWrapperFor (&object);
+ if (mono)
+ {
+ if (object.IsDerivedFrom (ClassID (MonoBehaviour)))
+ {
+ MonoClass* klass = mono_object_get_class (mono);
+ if (mono_class_is_subclass_of (klass, compareKlass, true))
+ monoObjects[count++] = mono;
+ }
+ else
+ monoObjects[count++] = mono;
+ }
+ }
+ WriteByte (TAG_UNITY);
+ for (int i = 0; i < count; i++)
+ {
+ WritePointer ((void*)monoObjects[i]);
+ }
+ WritePointer(0);
+}
+void MonoHeapShotWriter::WriteByte (UInt8 x)
+{
+ m_Data->push_back (x);
+}
+
+void MonoHeapShotWriter::WritePointer (void* x)
+{
+ UInt32 y = (UInt32) (x);
+ WriteUInt32 (y);
+}
+
+void MonoHeapShotWriter::WriteInt32 (SInt32 x)
+{
+ SwapEndianBytesLittleToNative(x);
+ int o = m_Data->size();
+ m_Data->resize(o + sizeof(UInt32));
+ memcpy(&(*m_Data)[o], &x, sizeof(SInt32));
+}
+
+void MonoHeapShotWriter::WriteUInt32 (UInt32 x)
+{
+ SwapEndianBytesLittleToNative(x);
+ int o = m_Data->size();
+ m_Data->resize(o + sizeof(UInt32));
+ memcpy(&(*m_Data)[o], &x, sizeof(UInt32));
+}
+
+void MonoHeapShotWriter::WriteVInt (UInt32 x)
+{
+ UInt8 y;
+ do
+ {
+ y = (UInt8) (x & 0x7f);
+ x = x >> 7;
+ if (x != 0) y |= 0x80;
+ WriteByte (y);
+ }
+ while (x != 0);
+}
+
+void MonoHeapShotWriter::WriteString (const char *str)
+{
+ int len = strlen (str);
+ WriteVInt ((UInt32) len);
+ if (len > 0)
+ {
+ int o = m_Data->size();
+ m_Data->resize(o + len);
+ memcpy(&(*m_Data)[o], str, len);
+ }
+}
+
+#endif
diff --git a/Runtime/Mono/MonoHeapShotWriter.h b/Runtime/Mono/MonoHeapShotWriter.h
new file mode 100644
index 0000000..0e86996
--- /dev/null
+++ b/Runtime/Mono/MonoHeapShotWriter.h
@@ -0,0 +1,64 @@
+#ifndef MONOHEAPSHOTWRITER_H
+#define MONOHEAPSHOTWRITER_H
+
+#include "Runtime/Mono/MonoTypes.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "MonoHeapShot.h"
+
+#if ENABLE_MONO_HEAPSHOT
+
+
+class MonoHeapShotWriter
+{
+public:
+ MonoHeapShotWriter();
+ ~MonoHeapShotWriter();
+
+ void Start(HeapShotData* data);
+
+ void End();
+
+ void DumpObjectsReferencedByUnity();
+
+ void BeginObjectDump(MonoObject* obj, MonoClass* klass);
+
+ void EndObjectDump();
+
+ void DumpReference(gpointer ref, gpointer field);
+
+ inline const UInt8* GetData() const
+ {
+ return &(*m_Data)[0];
+ }
+ inline UInt32 GetDataSize() const
+ {
+ return m_Data->size();
+ }
+
+private:
+ void WriteByte (UInt8 x);
+
+ void WritePointer (void* x);
+
+ void WriteInt32 (SInt32 x);
+
+ void WriteUInt32 (UInt32 x);
+
+ void WriteVInt (UInt32 x);
+
+ void WriteString (const char *str);
+
+ typedef std::vector<UInt8> Data;
+ HeapShotData* m_Data;
+ ClassHash* m_SeenItems;
+ int m_TypeCount;
+ int m_ObjecCount;
+ int m_ReferenceCount;
+ int m_FieldCount;
+ long m_SavedOutfileOffset;
+};
+
+#endif
+
+#endif
+
diff --git a/Runtime/Mono/MonoIncludes.h b/Runtime/Mono/MonoIncludes.h
new file mode 100644
index 0000000..4b69b9d
--- /dev/null
+++ b/Runtime/Mono/MonoIncludes.h
@@ -0,0 +1,40 @@
+#ifndef MONOINCLUDES_H
+#define MONOINCLUDES_H
+
+#include "Configuration/UnityConfigure.h"
+#include "MonoTypes.h"
+#include "MonoTypeSignatures.h"
+
+#ifndef __cplusplus
+#error somewhat unexpected
+#endif
+
+
+extern "C"
+{
+
+// on windows and linux, we always load mono functions dynamically
+
+#if ENABLE_MONO
+
+#define mono_string_chars(s) ((gunichar2*)&((s)->firstCharacter))
+#define mono_string_length(s) ((s)->length)
+
+#if (WEBPLUG || UNITY_WIN || UNITY_LINUX) && !UNITY_PEPPER
+
+#define DO_API(r,n,p) extern EXPORT_COREMODULE r (*n) p;
+#include "MonoFunctions.h"
+
+#else // WEBPLUG || UNITY_WIN || UNITY_LINUX
+
+#define DO_API(r,n,p) r n p;
+#define DO_API_NO_RETURN(r,n,p) DOES_NOT_RETURN r n p;
+#include "MonoFunctions.h"
+
+#endif // WEBPLUG || UNITY_WIN || UNITY_LINUX
+
+#endif // ENABLE_MONO
+
+}
+
+#endif
diff --git a/Runtime/Mono/MonoManager.cpp b/Runtime/Mono/MonoManager.cpp
new file mode 100644
index 0000000..871b9d0
--- /dev/null
+++ b/Runtime/Mono/MonoManager.cpp
@@ -0,0 +1,2456 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h" // include before anything, to get Prof_ENABLED if that is defined
+
+#if ENABLE_MONO
+#include "MonoManager.h"
+#include "MonoScript.h"
+#include "MonoBehaviour.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "MonoIncludes.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Serialize/TransferUtility.h"
+#include "Runtime/Serialize/TypeTree.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Export/MonoICallRegistration.h"
+#include "Runtime/Utilities/Word.h"
+#include <stdlib.h>
+#include "Runtime/Misc/Plugins.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/Serialize/FileCache.h"
+#include "Runtime/Profiler/ProfilerImpl.h"
+#include "Runtime/Misc/PreloadManager.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Utilities/PlayerPrefs.h"
+#include "Runtime/Scripting/CommonScriptingClasses.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Audio/AudioManager.h"
+#include "Runtime/Utilities/Stacktrace.h"
+#include "Runtime/Profiler/TimeHelper.h"
+#include "Runtime/Misc/DeveloperConsole.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingInvocation.h"
+#include "Runtime/Scripting/Backend/Mono/ScriptingMethodFactory_Mono.h"
+#include "Runtime/Scripting/Backend/ScriptingMethodRegistry.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+#include "Runtime/Scripting/Scripting.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/EditorModules.h"
+#endif
+
+#ifndef USE_ASSEMBLY_PREPROCESSOR
+#define USE_ASSEMBLY_PREPROCESSOR 0
+#endif
+
+#if WEBPLUG
+#include "PlatformDependent/CommonWebPlugin/CompressedFileStream.h"
+#include "Runtime/Utilities/ErrorExit.h"
+#define PLAYER_DATA_FOLDER ""
+#endif
+#if USE_ASSEMBLY_PREPROCESSOR
+#include "AssemblyModifier.h"
+#include "AssemblyModifierOnDisk.h"
+#endif
+
+#if UNITY_EDITOR
+#include "Editor/Src/EditorSettings.h"
+#include "Editor/Src/EditorHelper.h"
+#include "Editor/Platform/Interface/BugReportingTools.h"
+#include "Runtime/Utilities/Argv.h"
+#include "Editor/Platform/Interface/EditorUtility.h"
+#include "Editor/Src/EditorUserBuildSettings.h"
+#define PLAYER_DATA_FOLDER ""
+#endif
+#if UNITY_OSX || UNITY_LINUX
+#include <dlfcn.h>
+#endif
+#if UNITY_WIN
+#include "Configuration/UnityConfigureOther.h"
+#include "PlatformDependent/Win/PathUnicodeConversion.h"
+#include "PlatformDependent/Win/WinUtils.h"
+#include <signal.h>
+#endif
+#if UNITY_WII
+#include "PlatformDependent/wii/WiiUtility.h"
+#endif
+#if UNITY_OSX
+#include <mach/task.h>
+#include <sys/stat.h>
+#endif
+#ifdef _MSC_VER
+#define va_copy(a,z) ((void)((a)=(z)))
+#endif
+
+#if ENABLE_PLAYERCONNECTION
+#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h"
+#endif
+
+#ifndef PLAYER_DATA_FOLDER
+#include "Runtime/Misc/Player.h"
+#define PLAYER_DATA_FOLDER SelectDataFolder()
+#endif
+
+#define SUPPORT_MDB_FILES (UNITY_EDITOR || WEBPLUG)
+#define USE_MONO_DOMAINS (UNITY_EDITOR || WEBPLUG) && !UNITY_PEPPER
+// Turn off the extra domain for now. It doesn't buy us anything until
+// we get some sharing in the runtime. Right now, it loads the assembly
+// into the extra domain but the code will be jitted again in the child domain.
+#define USE_TWO_MONO_DOMAINS 0
+
+// Editor and development console players always load mdbs
+#define ALWAYS_LOAD_MDBS (UNITY_EDITOR || ((UNITY_XENON || UNITY_WII || UNITY_PS3) && !MASTER_BUILD) || ENABLE_PROFILER)
+
+#if UNITY_EDITOR
+extern "C"
+{
+ void debug_mono_images_leak ();
+}
+
+static const char* kEditorAssembliesPath = "Library/ScriptAssemblies";
+
+#endif // UNITY_EDITOR
+
+#if ENABLE_MONO_MEMORY_PROFILER
+void mono_profiler_startup ();
+#endif
+
+int* s_MonoDomainContainer = NULL;
+
+UNITY_TLS_VALUE(bool) MonoManager::m_IsMonoBehaviourInConstructor;
+
+#if UNITY_WIN && UNITY_WIN_ENABLE_CRASHHANDLER && !UNITY_WINRT
+
+#define USE_WIN_CRASH_HANDLER 1
+// We do cunning things with crash handler and mono - we disable
+// it when mono is initialized (because mono installs its own handler),
+// but setup a callback so that we can still launch bug reporter.
+#include "../../Tools/BugReporterWin/lib/CrashHandler.h"
+extern CrashHandler* gUnityCrashHandler;
+
+#else
+#define USE_WIN_CRASH_HANDLER 0
+#endif
+
+static void UnloadDomain ();
+static MonoDomain* CreateAndSetChildDomain();
+static void UnhandledExceptionHandler (MonoObject* object);
+double GetTimeSinceStartup ();
+
+using namespace std;
+
+static const char* kEngineAssemblyName = "UnityEngine.dll";
+static const char* kEditorInternalNameSpace = "UnityEditorInternal";
+#if MONO_2_12
+const char* kMonoClasslibsProfile = "4.5";
+#else
+const char* kMonoClasslibsProfile = "2.0";
+#endif
+
+#if UNITY_STANDALONE || UNITY_EDITOR
+static const char* gNativeLibsDir = NULL;
+#endif
+
+static MonoVTable** gClassIDToVTable = NULL;
+
+ScriptsDidChangeCallback* gUnloadDomainCallback = NULL;
+
+static bool gDebuggerEnabled = false;
+
+#if UNITY_PEPPER
+void *gCorLibMemory = NULL;
+#endif
+
+struct DomainReloadingData {
+ std::vector<SInt32> m_SavedBehaviours;
+ PPtr<MonoBehaviour> m_SavedScriptReloadProperties;
+ ABSOLUTE_TIME reloadStart;
+};
+
+void RegisterUnloadDomainCallback (ScriptsDidChangeCallback* call)
+{
+ AssertIf(gUnloadDomainCallback != NULL);
+ gUnloadDomainCallback = call;
+}
+
+static void ExtractMonoStacktrace (const std::string& condition, std::string& processedStackTrace, std::string& stackTrace, int errorNum, string& file, int* line, int type, int targetInstanceID);
+
+#if UNITY_OSX
+void HandleSignal (int i, __siginfo* info, void* p);
+#define UNITY_SA_DISABLE 0x0004 // disable taking signals on alternate stack
+#endif
+#if UNITY_WIN
+int HandleSignal( EXCEPTION_POINTERS* ep );
+void HandleAbort (int signal);
+#endif
+
+#if UNITY_EDITOR
+static std::string GetBuildToolsEngineDllPathIfExists (BuildTargetPlatform target)
+{
+ string buildToolsDirectory = GetBuildToolsDirectory(target, false);
+ if ( IsDirectoryCreated(buildToolsDirectory) )
+ {
+ string engineDLLPath = AppendPathName(buildToolsDirectory, kEngineAssemblyName);
+ if (IsFileCreated(engineDLLPath))
+ return engineDLLPath;
+ }
+ return "";
+}
+#endif // UNITY_EDITOR
+
+#if UNITY_STANDALONE || UNITY_EDITOR
+static void * mono_fallback_dlopen (const char* name, int flags, char **err, void *user_data)
+{
+ if (IsAbsoluteFilePath (name))
+ return NULL;
+
+ void* handle = NULL;
+ string fullPath = AppendPathName (gNativeLibsDir, name);
+
+ #if (UNITY_WIN && !UNITY_WINRT)
+ std::wstring widePath;
+ ConvertUnityPathName (fullPath, widePath);
+ handle = (void *) LoadLibraryW (widePath.c_str ());
+
+ #elif UNITY_OSX
+ handle = dlopen (fullPath.c_str (), RTLD_NOW);
+
+ #elif UNITY_XENON
+ handle = cached_module_load (fullPath.c_str (), MONO_DL_LAZY, 0);
+ #endif
+
+ if (!handle && err) {
+ printf_console ("Fallback handler could not load library %s\n", fullPath.c_str());
+ }
+ return handle;
+}
+
+static void* mono_fallback_lookup_symbol (void *handle, const char *name, char **err, void *user_data)
+{
+ void* symbol = NULL;
+ #if (UNITY_WIN && !UNITY_WINRT)
+ symbol = GetProcAddress ((HMODULE)handle, name);
+
+ #elif UNITY_OSX
+ symbol = dlsym (handle, name);
+
+ #elif UNITY_XENON
+ char* ret = mono_dl_symbol ((MonoDl*)handle, name, &symbol);
+ if (!symbol && err)
+ *err = ret;
+ #endif
+
+ #if !UNITY_XENON
+ if (!symbol && err) {
+ printf_console ("Fallback handler could not load symbol %s\n", name);
+ }
+ #endif
+
+ return symbol;
+}
+
+static void* mono_fallback_close (void *handle, void *user_data)
+{
+ #if (UNITY_WIN && !UNITY_METRO)
+ FreeLibrary ((HMODULE) handle);
+
+ #elif UNITY_OSX
+ dlclose (handle);
+
+ #endif
+
+ return NULL;
+}
+#endif
+
+MonoManager::MonoManager (MemLabelId label, ObjectCreationMode mode)
+: ScriptingManager(label, mode, this, UNITY_NEW(ScriptingMethodFactory_Mono(), kMemManager))
+{
+ m_AssemblyReferencingDomain = NULL;
+
+ #if UNITY_PLUGINS_AVAILABLE
+ mono_set_find_plugin_callback ((gconstpointer)FindAndLoadUnityPlugin);
+ #endif
+
+ #if UNITY_EDITOR
+ m_LogAssemblyReload = false;
+ #endif
+
+ #if UNITY_STANDALONE || UNITY_EDITOR
+ gNativeLibsDir = strdup(GetApplicationNativeLibsPath().c_str ());
+ mono_dl_fallback_register (mono_fallback_dlopen, mono_fallback_lookup_symbol, mono_fallback_close, NULL);
+ #endif
+
+ m_HasCompileErrors = false;
+
+ CleanupClassIDMaps();
+}
+
+MonoManager::~MonoManager ()
+{
+ gClassIDToVTable = NULL;
+ gClassIDToClass = NULL;
+ RegisterLogPreprocessor (NULL);
+}
+
+AssemblyMask MonoManager::GetSystemAssemblyMask (bool load)
+{
+ AssemblyMask assemblies(kScriptAssemblies);
+ assemblies[kEngineAssembly] = load;
+ #if UNITY_EDITOR
+ assemblies[kEditorAssembly] = load;
+ assemblies[kLocatorAssembly] = load;
+ #endif
+
+ return assemblies;
+}
+
+
+AssemblyMask MonoManager::GetAvailableDllAssemblyMask ()
+{
+ AssemblyMask assemblies(GetAssemblyCount());
+ assemblies[kEngineAssembly]=true;
+ #if UNITY_EDITOR
+ assemblies[kEditorAssembly]=true;
+ assemblies[kLocatorAssembly]=true;
+ #endif
+
+ for (int i=kScriptAssemblies;i<GetAssemblyCount();i++)
+ {
+ string path = GetAssemblyPath (i);
+ #if UNITY_XENON
+ path += ".mono";
+ #endif
+ #if !UNITY_PEPPER
+ if (IsFileCreated (path))
+ assemblies[i] = true;
+ #endif
+
+ #if WEBPLUG
+ if (CompressedFileStream::Get().Find(GetLastPathNameComponent(path)))
+ assemblies[i] = true;
+ #if UNITY_PEPPER
+ if (CompressedFileStream::GetResources().Find(GetLastPathNameComponent(path)))
+ assemblies[i] = true;
+ #endif
+ #endif
+ }
+
+ return assemblies;
+}
+
+void MonoManager::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ // Load assemblies in player
+ // In editor MonoImporter Reloads the assemblies
+ #if GAMERELEASE
+ ReloadAssembly (GetAvailableDllAssemblyMask());
+ #endif
+
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+ ScriptingInvocation initManagedAnalysis(GetScriptingMethodRegistry().GetMethod("UnityEngine.Serialization","ManagedLivenessAnalysis","Init"));
+ initManagedAnalysis.Invoke();
+
+ ScriptingInvocation initWriter(GetScriptingMethodRegistry().GetMethod("UnityEngine.Serialization","SerializedStateWriter","Init"));
+ initWriter.Invoke();
+
+ ScriptingInvocation initReader(GetScriptingMethodRegistry().GetMethod("UnityEngine.Serialization","SerializedStateReader","Init"));
+ initReader.Invoke();
+
+ ScriptingInvocation initRemapper(GetScriptingMethodRegistry().GetMethod("UnityEngine.Serialization","PPtrRemapper","Init"));
+ initRemapper.Invoke();
+#endif
+
+ // Disable Mono stacktrace in non-development (release) player builds
+ #if UNITY_EDITOR || UNITY_DEVELOPER_BUILD
+ RegisterLogPreprocessor (ExtractMonoStacktrace);
+ #endif
+}
+
+#if !UNITY_RELEASE
+void MonoManager::AssertInvalidAssembly (MonoClass* klass)
+{
+ if (klass == NULL)
+ return;
+
+ MonoImage* image = mono_class_get_image(klass);
+ if (image == mono_get_corlib())
+ return;
+
+ for (int i=0;i<m_ScriptImages.size();i++)
+ {
+// if (m_ScriptImages[i])
+// printf_console("compare against image file %s\n", mono_image_get_filename(m_ScriptImages[i]));
+
+ if (m_ScriptImages[i] == image)
+ return;
+ }
+ printf_console("with error class %p \n", klass);
+ printf_console("with name %s \n", mono_class_get_name(klass));
+ printf_console("with image %p \n", image);
+ printf_console("will image file name %s\n", mono_image_get_filename(image));
+ printf_console("Mono class %s is in an invalid assembly %s ! BUG", mono_class_get_name(klass), mono_image_get_filename(image));
+ #if UNITY_EDITOR
+ ErrorString("Invalid assembly loaded");
+ #endif
+ printf_console("\n\n\n\n");
+}
+#endif
+
+#if UNITY_EDITOR
+
+void MonoManager::ResizeAssemblyNames(int max)
+{
+ m_AssemblyNames.clear();
+ m_AssemblyNames.resize(max);
+}
+
+int MonoManager::InsertAssemblyName(const std::string& assemblyName)
+{
+ int index = GetAssemblyIndexFromAssemblyName(assemblyName);
+ if (index != -1)
+ return index;
+
+ index = GetAssemblyCount();
+ m_AssemblyNames.push_back(assemblyName);
+ return index;
+}
+
+void MonoManager::SetAssemblyName (unsigned assemblyIndex, const string& name)
+{
+ Assert(assemblyIndex <= m_AssemblyNames.size ());
+ m_AssemblyNames[assemblyIndex] = name;
+}
+
+
+void MonoManager::SetCustomDllPathLocation (const std::string& name, const std::string& path)
+{
+ if (m_CustomDllLocation[name] != path)
+ {
+ ////@TODO: Create warnings for duplicate names
+ m_CustomDllLocation[name] = path;
+ }
+}
+
+/*
+std::string MonoManager::AddCustomDll (const std::string& path)
+{
+ if (!DetectDotNetDll(path))
+ return "";
+
+ m_CustomDlls.insert(path);
+
+ set<std::string> names;
+
+ // Warn if a .dll name exists twice
+ for (set<std::string>::iterator i=m_CustomDlls.begin();i != m_CustomDlls.end();i++)
+ {
+ const std::string& dllpath = *i;
+ if (!IsFileCreated (dllpath))
+ continue;
+
+ if(!DetectDotNetDll(dllpath))
+ continue;
+
+ string name = GetLastPathNameComponent(dllpath);
+ if (!names.insert (name).second)
+ return Format("A .dll with name %s exists two times. The dll '%s' will be ignored.", name.c_str(), dllpath.c_str());
+ }
+
+ return "";
+}
+*/
+
+void MonoManager::SetHasCompileErrors (bool compileErrors)
+{
+ m_HasCompileErrors = compileErrors;
+}
+
+#endif
+
+#if UNITY_EDITOR
+static int gMonoPathsIndexOfManagedFolder = 0;
+//#else
+//static int gMonoPathsIndexOfManagedFolder = 1;
+#endif
+
+
+namespace MonoPathContainer
+{
+ std::vector<std::string>* g_MonoPaths = NULL;
+ void StaticInitialize(){ g_MonoPaths = UNITY_NEW(std::vector<std::string>,kMemMono);}
+ void StaticDestroy(){UNITY_DELETE(g_MonoPaths,kMemMono);}
+ std::vector<std::string>& GetMonoPaths() {return *g_MonoPaths;}
+ void SetMonoPaths(const std::vector<std::string>& paths){*g_MonoPaths = paths;}
+ void AppendMonoPath (const string& path)
+ {
+#if UNITY_WIN && !UNITY_WINRT
+ wchar_t widePath[kDefaultPathBufferSize];
+ ConvertUnityPathName(path.c_str(), widePath, kDefaultPathBufferSize);
+
+ wchar_t fullPath[kDefaultPathBufferSize];
+ GetFullPathNameW(widePath, kDefaultPathBufferSize, fullPath, NULL);
+
+ string unityPath;
+ ConvertWindowsPathName(fullPath, unityPath);
+#else
+ string unityPath(path);
+#endif
+ MonoPathContainer::GetMonoPaths().push_back(unityPath);
+ }
+};
+
+string MonoManager::GetAssemblyPath (int index)
+{
+ AssertIf (index < 0);
+
+#if UNITY_EDITOR
+ if (index == kEngineAssembly)
+ {
+ // Check if target specific assembly exists
+ std::string engineDLLPath = GetBuildToolsEngineDllPathIfExists (GetEditorUserBuildSettings().GetActiveBuildTarget());
+ if (!engineDLLPath.empty ())
+ {
+ return engineDLLPath;
+ }
+ else
+ {
+ return AppendPathName(MonoPathContainer::GetMonoPaths()[gMonoPathsIndexOfManagedFolder], m_AssemblyNames[index]);
+ }
+ }
+ else if (index < kScriptAssemblies)
+ {
+ return AppendPathName(MonoPathContainer::GetMonoPaths()[gMonoPathsIndexOfManagedFolder], m_AssemblyNames[index]);
+ }
+ else if (index < m_AssemblyNames.size ())
+ {
+ CustomDllLocation::iterator found = m_CustomDllLocation.find(m_AssemblyNames[index]);
+ if (found != m_CustomDllLocation.end())
+ return found->second;
+ else
+ return AppendPathName (kEditorAssembliesPath, m_AssemblyNames[index]);
+ }
+ else
+ return "";
+#elif WEBPLUG
+#if !UNITY_PEPPER
+ if (index < kScriptAssemblies)
+ return AppendPathName(MonoPathContainer::GetMonoPaths()[0], m_AssemblyNames[index]);
+ else
+#endif
+ if (index < m_AssemblyNames.size ())
+ return m_AssemblyNames[index];
+ else
+ return "";
+#else
+ return AppendPathName("Managed", m_AssemblyNames[index]);
+#endif
+}
+
+MonoAssembly* MonoManager::GetAssembly (int index)
+{
+ AssertIf (index < 0);
+ if (index < m_ScriptImages.size ())
+ {
+ MonoImage* image = m_ScriptImages[index];
+ if (image)
+ return mono_image_get_assembly (image);
+ }
+ return NULL;
+}
+
+BackendNativeType MonoManager::NativeTypeFor(const char* namespaze, const char* className)
+{
+ return GetMonoClass(className,namespaze);
+}
+
+ScriptingTypePtr MonoManager::Provide(BackendNativeType nativePtr)
+{
+ return (ScriptingTypePtr) nativePtr;
+}
+
+ScriptingTypePtr Provide(void* nativeType);
+
+void MonoManager::Release(ScriptingTypePtr klass)
+{
+}
+
+// If namespace is NULL then search is in any namespace
+MonoClass* MonoManager::GetMonoClassCaseInsensitive (const char* className, const char* theNameSpace /*=NULL*/)
+{
+ for (ScriptImages::iterator i=m_ScriptImages.begin ();i != m_ScriptImages.end ();i++)
+ {
+ MonoImage* curImage = *i;
+ if (!curImage)
+ continue;
+
+ MonoClass* klass = mono_class_from_name_case (curImage, theNameSpace, className);
+ if (klass)
+ return klass;
+ }
+
+ return NULL;
+}
+
+// If namespace is NULL then search is in any namespace
+MonoClass* MonoManager::GetMonoClass (const char* className, const char* theNameSpace /*=NULL*/)
+{
+ MonoClass* klass = NULL;
+ MonoImage* curImage;
+
+ klass = mono_class_from_name(mono_get_corlib(), theNameSpace, className);
+
+ ///@todo: give compile error when classes with the same name are defined in different dll's
+ for (ScriptImages::iterator i=m_ScriptImages.begin ();i != m_ScriptImages.end () && klass == NULL;i++)
+ {
+ curImage = *i;
+ if (!curImage)
+ continue;
+
+ klass = mono_class_from_name (curImage, theNameSpace, className);
+ }
+
+ return klass;
+}
+
+int MonoManager::GetAssemblyIndexFromAssemblyName (const string& name)
+{
+ vector<UnityStr>::iterator found = find (m_AssemblyNames.begin (), m_AssemblyNames.end (), name);
+ if (found == m_AssemblyNames.end ())
+ return -1;
+
+ return distance (m_AssemblyNames.begin (), found);
+}
+
+MonoClass* MonoManager::GetMonoClassWithAssemblyName (const std::string& className, const string& nameSpace, const string& assemblyName)
+{
+ int index = GetAssemblyIndexFromAssemblyName (assemblyName);
+ MonoImage* image = 0;
+ if (index == -1)
+ {
+ MonoAssemblyName aname;
+#if !UNITY_PEPPER || UNITY_NACL_WEBPLAYER
+ if (!mono_assembly_name_parse (assemblyName.c_str(),&aname))
+ return NULL;
+#endif
+ MonoAssembly* assembly = mono_assembly_loaded (&aname);
+ if (!assembly) return NULL;
+ image = mono_assembly_get_image(assembly);
+ }
+ else
+ {
+ if (index >= m_ScriptImages.size ()) return NULL;
+ image = m_ScriptImages[index];
+ }
+
+ if (!image) return NULL;
+
+ return mono_class_from_name (image, nameSpace.c_str(), className.c_str ());
+}
+
+string MonoManager::GetAssemblyIdentifierFromImage(MonoImage* image)
+{
+ for (int i=0;i<m_ScriptImages.size ();i++)
+ {
+ if (m_ScriptImages[i] == image)
+ return m_AssemblyNames[i];
+ }
+ return "";
+}
+
+int MonoManager::GetAssemblyIndexFromImage(MonoImage* image)
+{
+ for (int i=0;i<m_ScriptImages.size ();i++)
+ {
+ if (m_ScriptImages[i] == image)
+ return i;
+ }
+ return -1;
+}
+
+#if UNITY_OSX
+bool SameFileSystemEntry(const struct stat& s1, const struct stat& s2)
+{
+ return (s1.st_ino == s2.st_ino) && (s1.st_dev == s2.st_dev);
+}
+#endif
+
+bool IsPlatformPath(const std::string& path)
+{
+ std::vector<string>& monoPaths = MonoPathContainer::GetMonoPaths();
+#if UNITY_OSX
+ // On OSX we want to take symlinks into account
+ struct stat pathStat;
+ if (stat(path.c_str(), &pathStat) != 0)
+ return false;
+
+ for (std::vector<string>::iterator i = monoPaths.begin(); i != monoPaths.end(); ++i)
+ {
+ struct stat platformStat;
+ if (stat((*i).c_str(), &platformStat) == 0 && SameFileSystemEntry(pathStat, platformStat))
+ return true;
+ }
+ return false;
+#elif UNITY_WIN
+ #if !UNITY_WINRT
+ string unityPath = "";
+
+ if (!path.empty())
+ {
+ wchar_t widePath[kDefaultPathBufferSize];
+ ConvertUnityPathName(path.c_str(), widePath, kDefaultPathBufferSize);
+
+ wchar_t fullPath[kDefaultPathBufferSize];
+ if (!GetFullPathNameW(widePath, kDefaultPathBufferSize, fullPath, NULL))
+ fullPath[0] = L'\0';
+
+ ConvertWindowsPathName(fullPath, unityPath);
+ }
+
+ std::vector<string>::iterator pos = std::find(monoPaths.begin(), monoPaths.end(), unityPath);
+ return pos != monoPaths.end();
+ #else
+ #pragma message("todo: implement") // ?!-
+ return false; // ?!-
+ #endif
+#else
+ std::vector<string>::iterator pos = std::find(monoPaths.begin(), monoPaths.end(), path);
+ return pos != monoPaths.end();
+#endif
+}
+
+bool isPlatformCodeCallback(const char* image_name)
+{
+ std::string name(image_name);
+ ConvertSeparatorsToUnity(name);
+
+ bool result = IsPlatformPath(DeleteLastPathNameComponent(name));
+ printf_console(result ? "Platform assembly: %s (this message is harmless)\n" : "Non platform assembly: %s (this message is harmless)\n", image_name);
+ return result;
+}
+
+static MonoAssembly* LoadAssemblyAndSymbolsWrapper(const string& absolutePath)
+{
+ MonoAssembly* assembly = mono_domain_assembly_open (mono_domain_get (), absolutePath.c_str());
+ if (assembly)
+ {
+ #if USE_MONO_DEBUGGER
+ #if !ALWAYS_LOAD_MDBS
+ if (gDebuggerEnabled)
+ #endif
+ {
+ mono_debug_open_image_from_memory(mono_assembly_get_image(assembly), 0, 0);
+ }
+ #endif
+ }
+ return assembly;
+}
+
+static MonoAssembly* LoadAssemblyWrapper (const void* data, size_t size, const char* name)
+{
+ #if (UNITY_PS3 || UNITY_XENON)
+ Assert(data == NULL);
+ string absolutePath = PathToAbsolutePath (name);
+ ConvertSeparatorsToPlatform (absolutePath);
+
+ #if UNITY_XENON
+ // Try loading the .DLL.MONO from UPDATE: drive first.
+ // If it succeeds, mono will also load dependencies from the same path.
+ Assert(absolutePath.substr(0, 5) == "game:");
+ string absoluteUpdatePath = "update" + absolutePath.substr(4);
+ MonoAssembly* updateAssembly = LoadAssemblyAndSymbolsWrapper(absoluteUpdatePath);
+ if (updateAssembly)
+ return updateAssembly;
+ #endif
+
+ MonoAssembly* assembly = LoadAssemblyAndSymbolsWrapper(absolutePath);
+ return assembly;
+
+ #else
+
+
+ // We can't use mono_image_open_file because we continously replace the file when compiling new dll's.
+ // But mono will keep the file locked, so we need to load it into memory.
+ InputString dataString;
+
+ string absolutePath = PathToAbsolutePath(name);
+ ConvertSeparatorsToPlatform(absolutePath);
+
+ if (data == NULL)
+ {
+ #if WEBPLUG
+ return mono_domain_assembly_open (mono_domain_get (), absolutePath.c_str());
+ #endif
+
+ if (!ReadStringFromFile (&dataString, absolutePath))
+ return NULL;
+
+ data = &dataString[0];
+ size = dataString.size ();
+ }
+
+ int status = 0;
+ MonoImage* image = mono_image_open_from_data_with_name ((char*)data, size, /*Copy data*/true, &status, false /* ref only*/, absolutePath.c_str());
+ if (status != 0 || image == NULL)
+ {
+ printf_console("Failed loading assembly %s\n", name);
+ return NULL;
+ }
+
+ #if USE_MONO_DEBUGGER
+ #if !ALWAYS_LOAD_MDBS
+ if (gDebuggerEnabled)
+ #endif
+ {
+ string absolutePathMdb = PathToAbsolutePath(MdbFile(name));
+ ConvertSeparatorsToPlatform(absolutePathMdb);
+
+ #if !UNITY_PEPPER
+ if (ReadStringFromFile(&dataString, absolutePathMdb))
+ {
+ mono_debug_open_image_from_memory(image, &dataString[0], dataString.size ());
+ }
+ #endif
+ }
+ #endif
+
+ /// try out true as last argument (loads reflection only should be faster)
+ printf_console ("Loading %s into Unity Child Domain\n", absolutePath.c_str ());
+ MonoAssembly* assembly = mono_assembly_load_from_full (image, absolutePath.c_str (), &status, false/* ref only*/);
+ if (status != 0 || assembly == NULL)
+ {
+ // clear reference added by mono_image_open_from_data_with_name
+ mono_image_close (image);
+
+ printf_console("Failed loading assembly '%s'\n", name);
+ return NULL;
+ }
+
+ // We are loading a new assembly and it is already loaded????
+ if (mono_assembly_get_image(assembly) != image)
+ {
+ #if !UNITY_RELEASE
+ LogString(Format("Already loaded assembly '%s'", mono_image_get_name(mono_assembly_get_image(assembly))));
+ #endif
+ }
+
+ // clear reference added by mono_image_open_from_data_with_name
+ mono_image_close (image);
+
+ return assembly;
+ #endif
+
+}
+
+void SetSecurityMode()
+{
+ bool enableCoreClr = false;
+ bool enableVerifier = false;
+ bool enableSocketSecurity = false;
+
+#if WEBPLUG && !UNITY_PEPPER
+ enableSocketSecurity = true;
+ enableCoreClr = true;
+ enableVerifier = true;
+#endif
+
+#if UNITY_EDITOR
+ if (DoesBuildTargetUseSecuritySandbox(GetEditorUserBuildSettings().GetActiveBuildTarget ()))
+ {
+ enableSocketSecurity = true;
+ enableVerifier = true;
+ }
+#endif
+
+#if MONO_2_12
+ mono_security_core_clr_set_options ((MonoSecurityCoreCLROptions)(MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_REFLECTION | MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_DELEGATE));
+#endif
+ mono_security_set_core_clr_platform_callback (&isPlatformCodeCallback);
+
+#if !UNITY_PEPPER
+ if (enableCoreClr)
+ {
+ mono_security_set_mode (MONO_SECURITY_MODE_CORE_CLR);
+ } else {
+ mono_security_set_mode (MONO_SECURITY_MODE_NONE);
+ }
+
+ if (enableVerifier)
+ mono_verifier_set_mode (MONO_VERIFIER_MODE_VERIFIABLE);
+ else
+ mono_verifier_set_mode (MONO_VERIFIER_MODE_OFF);
+#endif
+
+ #if !UNITY_XENON && !UNITY_PS3 && !UNITY_PEPPER && !UNITY_WII
+ mono_unity_socket_security_enabled_set (enableSocketSecurity);
+ #endif
+}
+
+
+#define LogAssemblyError(x) DebugStringToFile (x, 0, __FILE__, __LINE__, kLog | kAssetImportError, 0, GetInstanceID ());
+#if WEBPLUG
+
+
+
+MonoAssembly *LoadAssemblyWebPlug (string path)
+{
+ MonoAssembly *assembly;
+ CompressedFileStream::Data* data = CompressedFileStream::Get().Find(GetLastPathNameComponent(path));
+#if UNITY_PEPPER
+ if (!data)
+ data = CompressedFileStream::GetResources().Find(GetLastPathNameComponent(path));
+#endif
+ if (data)
+ {
+ MemoryCacherReadBlocks cacher (data->blocks, data->end, kCacheBlockSize);
+ UInt8* dataCpy;
+ ALLOC_TEMP(dataCpy, UInt8, data->GetSize());
+ cacher.DirectRead(dataCpy, data->offset, data->GetSize());
+#if USE_ASSEMBLY_PREPROCESSOR
+ bool modify = true;
+ if (modify)
+ {
+ AssemblyModifier am;
+ InputString output;
+ bool success = am.PreProcessAssembly(dataCpy, data->GetSize(), output);
+ if (success)
+ assembly = LoadAssemblyWrapper (&output[0], output.size(), path.c_str ());
+ }
+ if (assembly==NULL)
+#endif
+ assembly = LoadAssemblyWrapper (dataCpy, data->GetSize(), path.c_str ());
+ if (!assembly) return NULL;
+#if !ALWAYS_LOAD_MDBS
+ if (gDebuggerEnabled)
+#endif
+ {
+ //see if there is an mdb too
+ CompressedFileStream::Data* mdbdata = CompressedFileStream::Get().Find(GetLastPathNameComponent(MdbFile(path)));
+ #if UNITY_PEPPER
+ if (!mdbdata)
+ mdbdata = CompressedFileStream::GetResources().Find(GetLastPathNameComponent(MdbFile(path)));
+ #endif
+ if (mdbdata)
+ {
+ MemoryCacherReadBlocks mdbcacher (mdbdata->blocks, mdbdata->end, kCacheBlockSize);
+ UInt8* mdbdataCpy;
+ ALLOC_TEMP(mdbdataCpy, UInt8, mdbdata->GetSize());
+ mdbcacher.DirectRead(mdbdataCpy, mdbdata->offset, mdbdata->GetSize());
+
+ MonoImage* image = mono_assembly_get_image(assembly);
+ mono_debug_open_image_from_memory(image, (const char*)mdbdataCpy, mdbdata->GetSize());
+ }
+ }
+ }
+ else
+ assembly = LoadAssemblyWrapper (NULL, 0, path.c_str ());
+ return assembly;
+}
+
+MonoAssembly *AssemblyLoadHook (char **name, void* user_data)
+{
+ return LoadAssemblyWebPlug(string(*name)+".dll");
+}
+#endif
+
+bool MonoManager::IsThisFileAnAssemblyThatCouldChange(std::string& path)
+{
+ std::string abspath = PathToAbsolutePath(path);
+ ConvertSeparatorsToUnity(abspath);
+ int startingScriptIndex = kScriptAssemblies;
+
+#if UNITY_EDITOR
+ if (IsDeveloperBuild())
+ startingScriptIndex = kEngineAssembly;
+#endif
+
+ for (int i=startingScriptIndex; i<GetAssemblyCount(); i++)
+ {
+ std::string assemblypath = PathToAbsolutePath(GetAssemblyPath (i));
+ ConvertSeparatorsToUnity(assemblypath);
+
+ if (abspath == assemblypath)
+ return true;
+ }
+ return false;
+}
+
+
+
+bool MonoManager::LoadAssemblies (AssemblyMask allAssembliesMask)
+{
+ bool firstLoad = false;
+ bool failedLoadingSomeAssemblies = false;
+
+ // Load assemblies
+ for (int i = 0; i < GetAssemblyCount() && i < allAssembliesMask.size (); i++)
+ {
+
+ // Does the assembly have to be reloaded?
+ if (allAssembliesMask[i])
+ {
+ if (m_ScriptImages.empty () || m_ScriptImages.size () <= i)
+ m_ScriptImages.resize (max<int> (i + 1, m_ScriptImages.size ()));
+
+ if (i >= kScriptAssemblies || m_ScriptImages[i] == NULL)
+ {
+ if (i < kScriptAssemblies)
+ firstLoad = true;
+
+ string path = GetAssemblyPath (i);
+ m_ScriptImages[i] = NULL;
+
+ // Load the script assembly, the script assembly might not be there
+ // if we dont have scripts this is valid
+ MonoAssembly* assembly = NULL;
+ #if WEBPLUG
+ assembly = LoadAssemblyWebPlug (path);
+ #elif UNITY_EDITOR
+ if ( i < kScriptAssemblies && !IsDeveloperBuild())
+ {
+ string s = PathToAbsolutePath (path);
+ assembly = mono_domain_assembly_open (mono_domain_get (), s.c_str());
+ }
+ else
+ assembly = LoadAssemblyWrapper (NULL, 0, path.c_str ());
+ #else
+ assembly = LoadAssemblyWrapper (NULL, 0, path.c_str ());
+ #endif
+
+ MonoImage* newImage = assembly ? mono_assembly_get_image (assembly) : NULL;
+
+ m_ScriptImages[i] = newImage;
+ if (newImage == NULL)
+ {
+ failedLoadingSomeAssemblies = true;
+ LogAssemblyError ("Loading script assembly \"" + path + "\" failed!");
+ }
+ }
+ }
+ // Should we unload the assembly?
+ else if (i < m_ScriptImages.size ())
+ {
+ m_ScriptImages[i] = NULL;
+ }
+ }
+
+ if (firstLoad)
+ {
+
+#if !UNITY_PS3 && !UNITY_XENON && !UNITY_IPHONE
+ for (int i = 0; i < m_ScriptImages.size (); i++)
+ {
+ if (m_ScriptImages[i])
+ {
+#if USE_ANCIENT_MONO
+ if (!mono_assembly_preload_references (m_ScriptImages[i]))
+ {
+ printf_console ("Failed loading references %s\n", GetAssemblyPath (i).c_str ());
+ m_ScriptImages[i] = NULL;
+ }
+#endif
+ }
+ }
+ ScriptingInvocation ("UnityEngine","ClassLibraryInitializer","Init").Invoke ();
+#endif
+#if UNITY_IOS
+ CallStaticMonoMethod ("UnhandledExceptionHandler", "RegisterUECatcher");
+#endif
+
+ }
+
+ return failedLoadingSomeAssemblies;
+}
+
+#if UNITY_EDITOR
+void MonoManager::SetupLoadedEditorAssemblies ()
+{
+ // Setup editor assemblies
+ MonoClass* editorAssemblies = GetMonoClass("EditorAssemblies", "UnityEditor");
+ if (editorAssemblies)
+ {
+ MonoClass* assemblyClass = mono_class_from_name (mono_get_corlib (), "System.Reflection", "Assembly");
+
+ vector<MonoAssembly*> assemblies;
+ assemblies.reserve(64);
+
+ for (int i=0;i<GetAssemblyCount();i++)
+ {
+ if (GetAssembly(i) && i != kEngineAssembly)
+ assemblies.push_back(GetAssembly(i));
+ }
+
+ MonoArray* array = mono_array_new (mono_domain_get (), assemblyClass, assemblies.size());
+ for (int i=0;i<assemblies.size();i++)
+ {
+ Scripting::SetScriptingArrayElement(array, i, mono_assembly_get_object(mono_domain_get(), assemblies[i]));
+ }
+
+ ScriptingInvocation invocation(editorAssemblies,"SetLoadedEditorAssemblies");
+ invocation.AddArray(array);
+ invocation.Invoke();
+ }
+}
+#endif
+
+
+MonoManager::AssemblyLoadFailure MonoManager::ReloadAssembly (AssemblyMask allAssembliesMask)
+{
+ DomainReloadingData savedData;
+ MonoManager::AssemblyLoadFailure initialLoad = BeginReloadAssembly (savedData);
+ if (initialLoad == kFailedLoadingEngineOrEditorAssemblies)
+ return initialLoad;
+
+#if UNITY_EDITOR
+ GlobalCallbacks::Get().initialDomainReloadingComplete.Invoke();
+ allAssembliesMask = GetAvailableDllAssemblyMask();
+#endif
+
+ return EndReloadAssembly (savedData, allAssembliesMask);
+}
+
+MonoManager::AssemblyLoadFailure MonoManager::BeginReloadAssembly (DomainReloadingData& savedData)
+{
+ printf_console ("Begin MonoManager ReloadAssembly\n");
+
+ // Make sure there are no preload operations queued up before we reload the domain.
+ GetPreloadManager().WaitForAllAsyncOperationsToComplete();
+ StopPreloadManager();
+
+#if UNITY_EDITOR
+ // if there's no script images on the list, no point in trying to stop, nothing has been loaded yet
+ if (!m_ScriptImages.empty())
+ ShutdownManagedModuleManager ();
+#endif
+
+ savedData.reloadStart = START_TIME;
+
+ RemoveErrorWithIdentifierFromConsole (GetInstanceID ());
+
+ #if GAMERELEASE
+ if (!m_ScriptImages.empty())
+ ErrorString("Reload Assembly may not be called multiple times in the player");
+ #endif
+
+ #if DEBUGMODE
+ ///@TODO: Also assert when the preload manager is running, write a test for it
+ if (mono_method_get_last_managed ())
+ {
+ ErrorString("Reload Assembly called from managed code directly. This will cause a crash. You should never refresh assets in synchronous mode or enter playmode synchronously from script code.");
+ return kFailedLoadingEngineOrEditorAssemblies;
+ }
+ #endif
+
+ // Find and backup all Monobehaviours to the EditorExtensionImpl
+ // If we are in play mode we use kForceSerializeAllProperties which will serialize private and public properties.
+ // This makes it possible to reload script while in playmode
+ // Otherwise we use the normal backup mechanism which doesnt persist private properties.
+
+ Object::FindAllDerivedObjects (ClassID(MonoBehaviour), &savedData.m_SavedBehaviours, false);
+ // sort all behaviours by script execution order (if set) and then instance ID, just like the persistent manager does
+ std::sort (savedData.m_SavedBehaviours.begin (), savedData.m_SavedBehaviours.end (), AwakeFromLoadQueue::SortBehaviourByExecutionOrderAndInstanceID);
+ MonoBehaviour* behaviour;
+
+ #if UNITY_EDITOR
+
+ // Allow static editor classes to store their state
+ if (GetAssembly(kEditorAssembly) != NULL)
+ CallStaticMonoMethod ("AssemblyReloadEvents", "OnBeforeAssemblyReload");
+ // Store script reload properties (For example text editing state.)
+ // Only store them if there are any other monobehaviours around, because when loading scripts for the first
+ // time we don't want this because the resource manager has not yet registered the ScriptReloadProperties
+ if (GetAssembly(kEditorAssembly) != NULL && !savedData.m_SavedBehaviours.empty ())
+ {
+ savedData.m_SavedScriptReloadProperties = ScriptingObjectToObject<MonoBehaviour> (CallStaticMonoMethod ("ScriptReloadProperties", "Store"));
+ if (savedData.m_SavedScriptReloadProperties.IsValid ())
+ savedData.m_SavedBehaviours.push_back(savedData.m_SavedScriptReloadProperties.GetInstanceID ());
+ }
+
+ for (int i = 0; i < savedData.m_SavedBehaviours.size (); i++)
+ {
+ behaviour = PPtr<MonoBehaviour> (savedData.m_SavedBehaviours[i]);
+ if (behaviour == NULL)
+ continue;
+
+ // Take backup just so that if we can load after reloading the assembly: Then we load the backup
+ // If we can't load after reloading the assembly, We just keep the backup on the behviour..
+ if (behaviour->GetInstance ())
+ {
+ Assert(behaviour->GetBackup() == NULL);
+
+ int flags = kSerializeDebugProperties | kSerializeMonoReload;
+
+ BackupState* backup = new BackupState();
+ // Due to serialization to YAML not being super-fast, we backup all MonoBehaviours in binary mode
+ // and we convert (when saving) the backup to YAML only when reload of a script fails.
+ MonoBehaviour::ExtractBackupFromInstance (behaviour->GetInstance (), behaviour->GetClass (), *backup, flags);
+ behaviour->SetBackup(backup);
+ }
+
+ // Release and unload.
+ // Calling RemoveFromManager here makes sure
+ if (behaviour->IsAddedToManager ())
+ behaviour->RemoveFromManager ();
+ else
+ behaviour->WillUnloadScriptableObject();
+ }
+
+ for (int i = 0; i < savedData.m_SavedBehaviours.size (); i++)
+ {
+ behaviour = PPtr<MonoBehaviour> (savedData.m_SavedBehaviours[i]);
+ if (behaviour == NULL)
+ continue;
+
+ behaviour->StopAllCoroutines();
+ }
+
+ #endif //#if UNITY_EDITOR
+
+ // Release all mono behaviours
+ for (int i = 0; i < savedData.m_SavedBehaviours.size (); i++)
+ {
+ behaviour = PPtr<MonoBehaviour> (savedData.m_SavedBehaviours[i]);
+ if (behaviour == NULL)
+ continue;
+
+// #if !UNITY_EDITOR
+// ErrorString(Format("MonoBehaviour not unloaded in web player unload %s instance: %d", behaviour->GetName().c_str(), behaviour->GetInstance()));
+// #endif
+
+ // Take backup just so that if we can load after reloading the assembly: Then we load the backup
+ behaviour->ReleaseMonoInstance ();
+ }
+
+ // Release all Monohandles
+ vector<Object*> allObjects;
+ Object::FindObjectsOfType (ClassID(Object), &allObjects);
+ for (int i=0;i<allObjects.size();i++)
+ {
+ if (allObjects[i]->GetCachedScriptingObject() != NULL)
+ allObjects[i]->SetCachedScriptingObject(NULL);
+ }
+
+ // Clear common scripting classes
+ ClearCommonScriptingClasses (m_CommonScriptingClasses);
+ m_ScriptingMethodRegistry->InvalidateCache();
+ m_ScriptingTypeRegistry->InvalidateCache();
+
+ #if ENABLE_PROFILER
+ UnityProfiler::Get().CleanupMonoMethodCaches();
+ #endif
+
+#if !USE_TWO_MONO_DOMAINS
+ AssemblyMask unloadMask = GetSystemAssemblyMask (false);
+ LoadAssemblies (unloadMask);
+#endif
+
+ #if USE_MONO_DOMAINS
+
+ PopulateAssemblyReferencingDomain();
+
+ if (CreateAndSetChildDomain() == NULL)
+ {
+ return kFailedLoadingEngineOrEditorAssemblies;
+ }
+
+#if USE_TWO_MONO_DOMAINS
+ if (m_AssemblyReferencingDomain == NULL)
+ m_AssemblyReferencingDomain = mono_domain_create_appdomain("AssemblyReferencingDomain",NULL);
+#endif
+
+#endif
+
+ SetSecurityMode();
+
+ #if ENABLE_MONO_MEMORY_PROFILER
+ UnityProfiler::Get().SetupProfilerEvents ();
+ #endif
+
+ // debug_mono_images_leak();
+
+ AssemblyMask loadMask = GetSystemAssemblyMask (true);
+ bool failedLoadingSomeAssemblies = LoadAssemblies(loadMask);
+
+ mono_gc_collect (mono_gc_max_generation ());
+
+ if (failedLoadingSomeAssemblies)
+ {
+ for (int i=0;i<m_ScriptImages.size();i++)
+ m_ScriptImages[i] = NULL;
+ CleanupClassIDMaps();
+ }
+
+ return failedLoadingSomeAssemblies ? kFailedLoadingEngineOrEditorAssemblies : kEverythingLoaded;
+}
+
+MonoManager::AssemblyLoadFailure MonoManager::EndReloadAssembly (const DomainReloadingData& savedData, AssemblyMask allAssembliesMask)
+{
+ MonoBehaviour* behaviour;
+
+ bool failedLoadingSomeAssemblies = LoadAssemblies(allAssembliesMask);
+
+ // Rebuild the m_ClassIDToMonoClass lookup table
+ RebuildClassIDToScriptingClass ();
+ RebuildCommonMonoClasses ();
+
+#if UNITY_EDITOR
+
+ // Rebuild all MonoScript classes cached info
+ vector<MonoScript*> scripts;
+ Object::FindObjectsOfType (&scripts);
+ for (int i=0;i<scripts.size ();i++)
+ {
+ MonoScript& script = *scripts[i];
+ MonoClass* klass = GetMonoClassWithAssemblyName (script.GetScriptClassName (), script.GetNameSpace(), script.GetAssemblyName ());
+ #if !UNITY_RELEASE
+ AssertInvalidAssembly(klass);
+ #endif
+
+ script.Rebuild (klass);
+ }
+
+ // Rebuild all mono instances!
+ for (int i = 0; i < savedData.m_SavedBehaviours.size (); i++)
+ {
+ behaviour = PPtr<MonoBehaviour>(savedData.m_SavedBehaviours[i]);
+ if (behaviour == NULL)
+ continue;
+ behaviour->RebuildMonoInstance (SCRIPTING_NULL);
+
+ if (behaviour->GetGameObjectPtr())
+ behaviour->GetGameObjectPtr ()->SetSupportedMessagesDirty ();
+ }
+
+ // Reload all MonoBehaviours from the Backup
+ for (int i = 0; i < savedData.m_SavedBehaviours.size (); i++)
+ {
+ behaviour = PPtr<MonoBehaviour>(savedData.m_SavedBehaviours[i]);
+ if (behaviour == NULL)
+ continue;
+
+ BackupState* backup = behaviour->GetBackup ();
+
+ // We now have an instance
+ // So read the state from the behaviours own backup
+ if (backup != NULL && behaviour->GetInstance ())
+ {
+ int flags = 0;
+ if (!backup->loadedFromDisk)
+ flags = kSerializeDebugProperties | kSerializeMonoReload;
+
+ behaviour->RestoreInstanceStateFromBackup (*backup, flags);
+ behaviour->SetBackup (NULL);
+ }
+
+ AssertIf (behaviour->GetInstance () != NULL && behaviour->GetBackup () != NULL);
+ }
+
+
+ // Add to manager and Awake from load comes last because we want everything to have the correct backup already loaded and ready.
+ for (int i = 0; i < savedData.m_SavedBehaviours.size (); i++)
+ {
+ behaviour = PPtr<MonoBehaviour> (savedData.m_SavedBehaviours[i]);
+ if (behaviour == NULL)
+ continue;
+
+ behaviour->CheckConsistency ();
+ behaviour->AwakeFromLoad (kDefaultAwakeFromLoad);
+
+ behaviour->DidReloadDomain();
+ }
+
+ /////////@TODO: Switch this to didReloadMonoDomain
+ // Notify AudioScriptBufferManager to reload buffers
+ if (GetAudioManager().GetScriptBufferManagerPtr())
+ GetAudioManager().GetScriptBufferManager().DidReloadDomain();
+
+ SetupLoadedEditorAssemblies ();
+
+ GlobalCallbacks::Get().didReloadMonoDomain.Invoke();
+
+ if (m_LogAssemblyReload)
+ {
+ LogString("Mono: successfully reloaded assembly\n");
+ }
+ else
+ {
+ printf_console ("Mono: successfully reloaded assembly\n");
+ }
+ #else
+ ErrorIf(Object::FindAllDerivedObjects (ClassID(MonoBehaviour), NULL) != 0);
+ ErrorIf(Object::FindAllDerivedObjects (ClassID(MonoScript), NULL) != 0);
+ #endif
+
+ #if UNITY_EDITOR
+
+ // Allow static editor classes to reconstruct their state
+ if (GetAssembly(kEditorAssembly) != NULL)
+ CallStaticMonoMethod ("AssemblyReloadEvents", "OnAfterAssemblyReload");
+
+ if (savedData.m_SavedScriptReloadProperties.IsValid ())
+ {
+ void* arg[] = { Scripting::ScriptingWrapperFor(savedData.m_SavedScriptReloadProperties) };
+ CallStaticMonoMethod("ScriptReloadProperties", "Load", arg);
+
+ DestroySingleObject (savedData.m_SavedScriptReloadProperties);
+ }
+ #endif
+
+ printf_console("- Completed reload, in %6.3f seconds\n", GetElapsedTimeInSeconds(savedData.reloadStart));
+
+ return failedLoadingSomeAssemblies ? kFailedLoadingScriptAssemblies : kEverythingLoaded;
+}
+
+void MonoManager::RebuildClassIDToScriptingClass ()
+{
+ ScriptingManager::RebuildClassIDToScriptingClass();
+ int size = m_ClassIDToMonoClass.size();
+ m_ClassIDToVTable.clear();
+ m_ClassIDToVTable.resize (size,NULL);
+ gClassIDToVTable = &m_ClassIDToVTable[0];
+
+ for(int i=0; i!=size; i++)
+ {
+ if (m_ClassIDToMonoClass[i]==NULL) continue;
+ m_ClassIDToVTable[i] = mono_class_vtable(mono_domain_get(), m_ClassIDToMonoClass[i]);
+
+ AssertIf ((m_ClassIDToMonoClass[i] == NULL) != (m_ClassIDToVTable[i] == NULL));
+ }
+}
+
+void MonoManager::CleanupClassIDMaps()
+{
+ m_ClassIDToMonoClass.clear ();
+ m_ClassIDToVTable.clear ();
+ gClassIDToVTable = &m_ClassIDToVTable[0];
+ gClassIDToClass = SCRIPTING_NULL;
+
+ memset(&m_CommonScriptingClasses, 0, sizeof(m_CommonScriptingClasses) );
+}
+
+MonoClass* MonoManager::GetBuiltinMonoClass (const char* name, bool optional)
+{
+ MonoClass* klass = NULL;
+ if (m_ScriptImages[kEngineAssembly])
+ klass = mono_class_from_name (m_ScriptImages[kEngineAssembly], "UnityEngine", name);
+
+ if (klass)
+ return klass;
+ else if (!optional)
+ {
+ ErrorString (Format ("Mono Class %s couldn't be found! This might lead to random crashes later on!", name));
+ return NULL;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+#if UNITY_EDITOR
+MonoClass* MonoManager::GetBuiltinEditorMonoClass (const char* name)
+{
+ MonoClass* klass = NULL;
+ if (m_ScriptImages[kEditorAssembly])
+ klass = mono_class_from_name (m_ScriptImages[kEditorAssembly], "UnityEditor", name);
+
+ if (klass)
+ return klass;
+ else
+ {
+ ErrorString (Format ("Mono Class %s couldn't be found! This might lead to random crashes later on!", name));
+ return NULL;
+ }
+}
+#endif
+
+void MonoManager::RebuildCommonMonoClasses ()
+{
+ FillCommonScriptingClasses(m_CommonScriptingClasses);
+
+ ScriptingMethodPtr setProject = m_CommonScriptingClasses.stackTraceUtilitySetProjectFolder;
+ if (setProject)
+ {
+ MonoException* exception;
+ std::string projectFolder = File::GetCurrentDirectory ();
+ if( !projectFolder.empty() )
+ projectFolder += kPathNameSeparator;
+ ConvertSeparatorsToPlatform( projectFolder );
+
+ void* values[1] = { MonoStringNew (projectFolder) };
+ mono_runtime_invoke_profiled (setProject->monoMethod, NULL, values, &exception);
+ if (exception)
+ Scripting::LogException (exception, 0);
+ }
+}
+
+
+MonoObject* MonoInstantiateScriptingWrapperForClassID(int classID)
+{
+ MonoVTable* vtable = gClassIDToVTable[classID];
+ if (!vtable)
+ return NULL;
+
+ return mono_object_new_alloc_specific (vtable);
+}
+DOES_NOT_RETURN void RaiseDotNetExceptionImpl (const char* ns, const char* type, const char* format, va_list list)
+{
+ va_list ap;
+ va_copy (ap, list);
+
+ char buffer[1024 * 5];
+ vsnprintf (buffer, 1024 * 5, format, ap);
+ va_end (ap);
+
+ MonoException* exception = mono_exception_from_name_msg (mono_get_corlib (), ns, type, buffer);
+ mono_raise_exception (exception);
+
+}
+
+DOES_NOT_RETURN void RaiseSystemExceptionImpl (const char* type, const char* format, va_list list)
+{
+ va_list ap;
+ va_copy (ap, list);
+ RaiseDotNetExceptionImpl("System",type,format,ap);
+}
+
+void RaiseManagedException (const char *ns, const char *type, const char *format, ...)
+{
+ va_list va;
+ va_start (va, format);
+ RaiseDotNetExceptionImpl (ns, type, format, va);
+}
+
+void RaiseMonoException (const char* format, ...)
+{
+ va_list va;
+ va_start( va, format );
+
+ char buffer[1024 * 5];
+ vsnprintf (buffer, 1024 * 5, format, va);
+
+ MonoException* exception = mono_exception_from_name_msg (GetMonoManager ().GetEngineImage (), kEngineNameSpace, "UnityException", buffer);
+ mono_raise_exception (exception);
+}
+
+void RaiseOutOfRangeException (const char* format, ...)
+{
+ va_list va;
+ va_start( va, format );
+ RaiseSystemExceptionImpl("IndexOutOfRangeException", format, va);
+}
+
+void RaiseNullException (const char* format, ...)
+{
+ va_list va;
+ va_start( va, format );
+ RaiseSystemExceptionImpl("NullReferenceException", format, va);
+}
+
+void RaiseArgumentException (const char* format, ...)
+{
+ va_list va;
+ va_start( va, format );
+ RaiseSystemExceptionImpl("ArgumentException", format, va);
+}
+
+void RaiseInvalidOperationException (const char* format, ...)
+{
+ va_list va;
+ va_start( va, format );
+ RaiseSystemExceptionImpl("InvalidOperationException", format, va);
+}
+
+void RaiseSecurityException (const char* format, ...)
+{
+ va_list va;
+ va_start( va, format );
+ RaiseDotNetExceptionImpl("System.Security","SecurityException", format, va);
+}
+
+void RaiseNullExceptionObject (MonoObject* object)
+{
+ #if MONO_QUALITY_ERRORS
+ if (object)
+ {
+ MonoClass* klass = mono_object_get_class(object);
+ if (mono_class_is_subclass_of (mono_object_get_class(object), GetMonoManager().ClassIDToScriptingClass(ClassID(Object)), false))
+ {
+ UnityEngineObjectMemoryLayout& data = ExtractMonoObjectData<UnityEngineObjectMemoryLayout>(object);
+ string error = MonoStringToCpp(data.error);
+
+ // The object was destroyed underneath us - but if we hit a MissingReferenceException then we show that instead!
+ if (data.instanceID != 0 && error.find ("MissingReferenceException:") != 0)
+ {
+ error = Format("The object of type '%s' has been destroyed but you are still trying to access it.\n"
+ "Your script should either check if it is null or you should not destroy the object.", mono_class_get_name(klass));
+
+ MonoException* exception = mono_exception_from_name_msg (GetMonoManager().GetEngineImage(), "UnityEngine", "MissingReferenceException", error.c_str());
+ mono_raise_exception (exception);
+ }
+
+ // We got an error message, parse it and throw it
+ if (data.error)
+ {
+ error = MonoStringToCpp(data.error);
+ string::size_type exceptionTypePos = error.find(':');
+ if (exceptionTypePos != string::npos)
+ {
+ string type = string(error.begin(), error.begin() + exceptionTypePos);
+ error.erase(error.begin(), error.begin()+exceptionTypePos + 1);
+
+ MonoException* exception = mono_exception_from_name_msg (GetMonoManager().GetEngineImage(), "UnityEngine", type.c_str(), error.c_str ());
+ mono_raise_exception (exception);
+ }
+ }
+ }
+ }
+
+ MonoException* exception = mono_exception_from_name_msg (mono_get_corlib (), "System", "NullReferenceException", "");
+ mono_raise_exception (exception);
+ #else
+ MonoException* exception = mono_exception_from_name_msg (mono_get_corlib (), "System", "NullReferenceException", "");
+ mono_raise_exception (exception);
+ #endif
+}
+
+MonoObject* InstantiateScriptingWrapperForClassID(int classID)
+{
+ MonoVTable* vtable = gClassIDToVTable[classID];
+ if (!vtable)
+ return NULL;
+
+ return mono_object_new_alloc_specific (vtable);
+}
+
+#if UNITY_EDITOR
+bool AmIBeingDebugged();
+#endif
+
+
+#if UNITY_EDITOR
+extern "C" {
+ void *mono_trace_parse_options (char *options);
+ extern void *mono_jit_trace_calls;
+}
+#endif
+
+#if USE_MONO_DOMAINS
+
+void MonoManager::UnloadAllAssembliesOnNextDomainReload()
+{
+#if USE_TWO_MONO_DOMAINS
+ if (m_AssemblyReferencingDomain != NULL)
+ mono_domain_unload(m_AssemblyReferencingDomain);
+ m_AssemblyReferencingDomain = NULL;
+#endif
+}
+
+void MonoManager::PopulateAssemblyReferencingDomain()
+{
+#if USE_TWO_MONO_DOMAINS
+ //if unityeditor isn't loaded yet (first time), return, since we depend on some managed code to do our work.
+ if (m_ScriptImages.empty())
+ return;
+
+ if (m_AssemblyReferencingDomain == NULL)
+ {
+ printf_console("Not loading any assemblies in assemblyreferencingdomain, since the domain doesn't exist\n");
+ return;
+ }
+
+ MonoException* exc = NULL;
+ printf_console("- GetNamesOfAssembliesLoadedInCurrentDomain\n");
+ ScriptingArrayPtr result = (ScriptingArrayPtr) CallStaticMonoMethod("AssemblyHelper","GetNamesOfAssembliesLoadedInCurrentDomain",NULL,&exc);
+
+ if (exc!=NULL)
+ {
+ ErrorString("Failed calling GetNamesOfAssembliesLoadedInCurrentDomain");
+ return;
+ }
+
+ for (int i=0; i!= GetScriptingArraySize(result); i++)
+ {
+ string filename(ScriptingStringToCpp(Scripting::GetScriptingArrayElement<MonoString*>(result,i)));
+
+ if (IsThisFileAnAssemblyThatCouldChange(filename))
+ continue;
+
+ printf_console("- loading assembly into m_AssemblyReferencingDomain: %s\n",filename.c_str());
+ MonoAssembly* a = mono_domain_assembly_open(m_AssemblyReferencingDomain,filename.c_str());
+ if (a == NULL)
+ ErrorString(Format("Failed to load assembly: %s into temporary domain\n",filename.c_str()));
+ }
+#endif
+}
+
+MonoDomain* CreateAndSetChildDomain()
+{
+ MonoDomain* old_domain = mono_domain_get();
+
+ // Unload the old domain
+ UnloadDomain ();
+
+ //there should only ever be one child domain, so if we're making a new one,
+ //we better be in the rootdomain.
+ AssertIf(mono_get_root_domain() != mono_domain_get());
+
+ MonoDomain* newDomain = mono_domain_create_appdomain("Unity Child Domain", NULL);
+
+ AssertIf(mono_get_root_domain() != mono_domain_get());
+
+ // Activate the domain!
+ if (newDomain)
+ {
+ mono_thread_push_appdomain_ref(newDomain);
+
+ if (!mono_domain_set (newDomain, false))
+ {
+ printf_console("Exception setting domain\n");
+ return NULL;
+ }
+ }
+ else
+ {
+ printf_console("Failed to create domain\n");
+ return NULL;
+ }
+
+
+ AssertIf(mono_domain_get () == mono_get_root_domain());
+ AssertIf(mono_domain_get () == NULL);
+ AssertIf(mono_domain_get () == old_domain);
+ UNUSED(old_domain);
+
+ MonoDomainIncrementUniqueId();
+
+ return mono_domain_get ();
+}
+
+
+static void UnloadDomain ()
+{
+ if (gUnloadDomainCallback)
+ gUnloadDomainCallback();
+
+ ClearLogCallback ();
+
+ //make sure to be in the domain you want to unload.
+ MonoDomain* domainToUnload = mono_domain_get();
+
+ //never unload the rootdomain
+ if (domainToUnload && domainToUnload != mono_get_root_domain())
+ {
+ // you can only unload a domain when it's not the active domain, so we're going to switch to the rootdomain,
+ // so we can kill the childdomain.
+ if (!mono_domain_set(mono_get_root_domain(), false))
+ {
+ printf_console("Exception setting domain\n");
+ }
+ mono_thread_pop_appdomain_ref();
+ mono_domain_unload(domainToUnload);
+ }
+
+ //unloading a domain is also a nice point in time to have the GC run.
+ mono_gc_collect(mono_gc_max_generation());
+
+#if UNITY_PLUGINS_AVAILABLE
+ //@TODO: Have Mono release its DLL handles (see case 373275). Until that is the case,
+ // this is disabled as it results in a change of behavior (plugin functions can get called multiple
+ // times in the editor which as long as we don't actually release the DLLs doesn't really make
+ // sense).
+ //UnloadAllPlugins ();
+#endif
+}
+#endif
+
+#if UNITY_OSX && !UNITY_IPHONE && !UNITY_PEPPER
+void SetupSignalHandler(int signal)
+{
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_sigaction = HandleSignal;
+ action.sa_flags = SA_SIGINFO;
+ if(sigaction(signal, &action, NULL) < 0)
+ printf_console("Error setting signal handler for signal %d\n",signal);
+ }
+#endif
+
+void SetupSignalHandlers()
+{
+
+#if UNITY_OSX
+# if WEBPLUG
+ //Remove possible existing mach exception handlers which interfere with our exception handling (ie Firefox)
+ mach_port_t current_task = mach_task_self();
+ exception_mask_t exceptionMask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT;
+ task_set_exception_ports(current_task, exceptionMask, 0, EXCEPTION_DEFAULT, THREAD_STATE_NONE);
+# endif
+
+# if !UNITY_IPHONE && !UNITY_PEPPER
+ SetupSignalHandler(SIGSEGV);
+ SetupSignalHandler(SIGBUS);
+ SetupSignalHandler(SIGFPE);
+ SetupSignalHandler(SIGILL);
+#endif
+#endif
+
+// Make sure abort() calls get passed to the crash handler
+#if UNITY_WIN
+ _set_abort_behavior (0, _WRITE_ABORT_MSG);
+ signal (SIGABRT, HandleAbort);
+#endif
+
+#if UNITY_WIN || UNITY_OSX || UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+ //this causes mono to forward the signal to us, if the signal is not mono related. (i.e. not a breakpoint, or a managed nullref exception, etc)
+ mono_set_signal_chaining(1);
+#endif
+}
+
+bool ShouldGiveDebuggerChanceToAttach()
+{
+#if SUPPORT_ENVIRONMENT_VARIABLES
+ if (getenv("UNITY_GIVE_CHANCE_TO_ATTACH_DEBUGGER"))
+ return true;
+#endif
+ return false;
+}
+
+void GiveDebuggerChanceToAttachIfRequired()
+{
+ if (ShouldGiveDebuggerChanceToAttach())
+ {
+#if UNITY_WIN && !UNITY_XENON && !UNITY_WINRT
+ MessageBoxW(0,L"You can attach a debugger now if you want",L"Debug",MB_OK);
+#endif
+#if UNITY_OSX
+ CFStringRef msg = CFSTR ("You can attach a debugger now if you want");
+ CFOptionFlags responseFlags;
+ CFUserNotificationDisplayAlert (0.0, kCFUserNotificationPlainAlertLevel, NULL, NULL, NULL, msg, NULL, NULL, NULL, NULL, &responseFlags);
+#endif
+ }
+}
+
+void StoreGlobalMonoPaths (const std::vector<string>& monoPaths)
+{
+#if UNITY_WIN && !UNITY_WINRT
+
+ MonoPathContainer::GetMonoPaths().reserve(monoPaths.size());
+
+ for (vector<string>::const_iterator it = monoPaths.begin(); it != monoPaths.end(); ++it)
+ {
+ wchar_t widePath[kDefaultPathBufferSize];
+ ConvertUnityPathName(it->c_str(), widePath, kDefaultPathBufferSize);
+
+ wchar_t fullPath[kDefaultPathBufferSize];
+ GetFullPathNameW(widePath, kDefaultPathBufferSize, fullPath, NULL);
+
+ string unityPath;
+ ConvertWindowsPathName(fullPath, unityPath);
+
+ MonoPathContainer::GetMonoPaths().push_back(unityPath);
+ }
+
+#else
+ MonoPathContainer::SetMonoPaths(monoPaths);
+#endif
+}
+
+static std::string BuildMonoPath (const std::vector<string>& monoPaths)
+{
+#if UNITY_WIN
+ const char* separator = ";";
+#else
+ const char* separator = ":";
+#endif
+
+ string monoPath;
+ for (int i=0;i!=monoPaths.size();i++)
+ {
+ if (i!=0) monoPath.append(separator);
+ monoPath.append(monoPaths[i]);
+ }
+
+ return monoPath;
+}
+
+static void SetupMonoPaths (const std::vector<string>& monoPaths, const std::string& monoConfigPath)
+{
+ // Mono will not look for the loaded Boo assembly if the version is different.
+ // It will look in the mono path and next to the loading dll.
+ // In web player Boo.Lang.dll is in different location than the rest of mono,
+ // hence we setup multiple assembly paths.
+ for (int i = 0; i < monoPaths.size(); i++)
+ printf_console("Mono path[%d] = '%s'\n", i, monoPaths[i].c_str());
+ printf_console ("Mono config path = '%s'\n", monoConfigPath.c_str ());
+ mono_set_dirs (monoPaths[0].c_str(), monoConfigPath.c_str());
+
+#if !UNITY_PEPPER
+ mono_set_assemblies_path(BuildMonoPath (monoPaths).c_str ());
+#endif
+
+ StoreGlobalMonoPaths (monoPaths);
+}
+
+static void InitializeMonoDebugger (bool forceEnable=false)
+{
+ // Set up debugger
+ string monoOptions;
+
+#if MONO_2_12
+ const string defaultDebuggerOptions = "--debugger-agent=transport=dt_socket,embedding=1,server=y,suspend=n";
+#else
+ const string defaultDebuggerOptions = "--debugger-agent=transport=dt_socket,embedding=1,defer=y";
+#endif
+
+#if UNITY_EDITOR
+ if (EditorPrefs::GetBool ("AllowAttachedDebuggingOfEditor", true))
+ {
+ // Editor always listens on default port
+ monoOptions = defaultDebuggerOptions;
+ }
+#endif
+
+#if SUPPORT_ENVIRONMENT_VARIABLES
+ // MONO_ARGUMENTS overrides everything
+ char* args = getenv("MONO_ARGUMENTS");
+ if (args!=0)
+ {
+ monoOptions = args;
+ }
+#endif
+#if ENABLE_PLAYERCONNECTION && !UNITY_FLASH
+ // Allow attaching using player connection discovery
+ if (monoOptions.empty ())
+ {
+ PlayerConnection::Initialize (PLAYER_DATA_FOLDER, forceEnable);
+ if (PlayerConnection::Get ().AllowDebugging ()) {
+ // Must be synchronized with Unity SDB addin
+ unsigned debuggerPort = 56000 + (PlayerConnection::Get ().GetLocalGuid () % 1000);
+ monoOptions = defaultDebuggerOptions + Format (",address=0.0.0.0:%u", debuggerPort);
+ }
+ }
+#endif
+
+
+ if (!monoOptions.empty ())
+ {
+ char *optionArray[] = { (char*)monoOptions.c_str () };
+ printf_console ("Using monoOptions %s\n", monoOptions.c_str ());
+ mono_jit_parse_options(1, optionArray);
+ gDebuggerEnabled = true;
+ }
+
+#if !ALWAYS_LOAD_MDBS
+ if (gDebuggerEnabled)
+#endif
+ {
+ //this is not so much about debugging (although also required for debugging), but it's about being able to load .mdb files, which you need for nice stacktraces, lineinfo, etc.
+ mono_debug_init (1);
+ }
+}
+
+void MonoPrintfRedirect(const char* msg, va_list list)
+{
+ va_list args;
+ va_copy (args, list);
+ printf_consolev(LogType_Debug,msg,args);
+ va_end (args);
+}
+
+#if UNITY_NACL_WEBPLAYER
+extern "C" {
+ void mono_set_pthread_suspend(int (*_pthread_suspend)(pthread_t thread, int sig));
+ int pthreadsuspend_initialize(void);
+ int pthread_suspend(pthread_t thread, int sig);
+}
+#endif
+
+bool InitializeMonoFromMain (const std::vector<string>& monoPaths, string monoConfigPath, int argc, const char** argv, bool enableDebugger)
+{
+ s_MonoDomainContainer = UNITY_NEW_AS_ROOT(int,kMemMono, "MonoDomain", "");
+ SET_ALLOC_OWNER(s_MonoDomainContainer);
+
+ MonoPathContainer::StaticInitialize();
+
+ const char *emptyarg = "";
+ GiveDebuggerChanceToAttachIfRequired();
+
+
+ #if DEBUGMODE
+ printf_console( "Initialize mono\n" );
+ #endif
+
+// Debug helpers
+#if 0
+ mono_unity_set_vprintf_func(MonoPrintfRedirect);
+
+ //setenvvar ("MONO_DEBUG", "abort-on-sigsegv");
+ //setenvvar ("MONO_LOG_MASK", "all");
+ //setenvvar ("MONO_LOG_LEVEL", "debug");
+ //setenvvar("MONO_TRACE","Console.Out:++++ ");
+
+ // Tracing mask
+ #if UNITY_EDITOR
+ mono_trace_set_mask_string("all"); // "asm", "type", "dll", "gc", "cfg", "aot", "all",
+ mono_trace_set_level_string("debug");// "error", "critical", "warning", "message", "info", "debug"
+ //mono_jit_trace_calls = mono_trace_parse_options ("Console.Out:++++ ");
+ #endif
+#endif
+
+ SetupMonoPaths (monoPaths, monoConfigPath);
+
+ mono_config_parse (NULL);
+
+ SetupSignalHandlers();
+
+ mono_set_defaults (0, mono_parse_default_optimizations (NULL));
+
+#if USE_MONO_DEBUGGER
+ InitializeMonoDebugger (enableDebugger);
+#endif
+
+ if (argv==NULL) argv = &emptyarg;
+#if !UNITY_XENON && ! UNITY_PS3 && !UNITY_IPHONE && !UNITY_PEPPER && !UNITY_WII
+ mono_set_commandline_arguments(argc,argv, NULL);
+#endif
+
+#if UNITY_NACL
+#if UNITY_NACL_WEBPLAYER
+ pthreadsuspend_initialize ();
+
+ mono_set_pthread_suspend (pthread_suspend);
+#endif
+
+ CompressedFileStream::Data* data = CompressedFileStream::GetResources().Find("mscorlib.dll");
+ if (data == NULL)
+ {
+ ErrorString ("Could not find mscorlib in web stream!");
+ return false;
+ }
+
+ MemoryCacherReadBlocks cacher (data->blocks, data->end, kCacheBlockSize);
+ gCorLibMemory = (UInt8*)UNITY_ALLOC(kMemMono, data->GetSize(), 32);
+ cacher.DirectRead(gCorLibMemory, data->offset, data->GetSize());
+ mono_set_corlib_data (gCorLibMemory, data->GetSize());
+
+ mono_install_assembly_postload_search_hook (AssemblyLoadHook, NULL);
+
+ mono_jit_parse_options(argc, (char**)argv);
+#endif
+
+
+// mono_runtime_set_no_exec (true);
+#if MONO_2_12
+ MonoDomain* domain = mono_jit_init_version ("Unity Root Domain", "v4.0.30319");
+#else
+ MonoDomain* domain = mono_jit_init_version ("Unity Root Domain", "v2.0.50727");
+#endif
+ if (domain == NULL)
+ return false;
+
+#if WEBPLUG && (!UNITY_PEPPER || UNITY_NACL_WEBPLAYER)
+ mono_set_ignore_version_and_key_when_finding_assemblies_already_loaded(true);
+#endif
+
+ // The soft debugger needs this
+ mono_thread_set_main (mono_thread_current ());
+
+#if ENABLE_MONO_MEMORY_PROFILER
+ //mono_gc_base_init (); ask joachim why we need this.
+
+ mono_profiler_startup ();
+#endif
+
+#if !UNITY_XENON && !UNITY_PS3 && !UNITY_IPHONE && !UNITY_PEPPER && !UNITY_WII
+ mono_unity_set_embeddinghostname("Unity");
+#endif
+
+#if !UNITY_PEPPER
+ mono_runtime_unhandled_exception_policy_set (MONO_UNHANDLED_POLICY_LEGACY);
+#endif
+
+ RegisterAllInternalCalls();
+
+ return true;
+}
+
+#if !UNITY_WII && !UNITY_PS3 && !UNITY_XENON && !UNITY_IPHONE && !UNITY_ANDROID && !UNITY_PEPPER && !UNITY_LINUX && !UNITY_BB10 && !UNITY_TIZEN
+
+#if UNITY_OSX
+
+#include <dlfcn.h>
+#include <cxxabi.h>
+
+void PrintStackTraceOSX (void* context)
+{
+ void *array[256];
+ size_t size;
+ char **strings;
+ size_t i;
+
+ size = mono_backtrace_from_context (context, array, 256);
+
+ printf_console ("Obtained %zu stack frames.\n", size);
+
+ for (i = 0; i < size; i++)
+ {
+ const char* symbolName = mono_pmip(array[i]);
+ if (!symbolName)
+ {
+ Dl_info dlinfo;
+ dladdr(array[i], &dlinfo);
+ symbolName = dlinfo.dli_sname;
+ if (symbolName)
+ {
+ int status = 0;
+ char* cname = abi::__cxa_demangle(symbolName, 0, 0, &status);
+ if (status == 0 && cname)
+ symbolName = cname;
+ }
+ }
+
+ printf_console ("#%-3d%016p in %s\n", i, array[i], symbolName);
+ }
+
+ free (strings);
+}
+
+
+void HandleSignal (int i, __siginfo* info, void* p)
+#endif
+#if UNITY_WIN
+int __cdecl HandleSignal( EXCEPTION_POINTERS* ep )
+#endif
+{
+ printf_console("Receiving unhandled NULL exception\n");
+
+ #if UNITY_EDITOR
+
+ // ---- editor
+
+ #if UNITY_OSX
+ printf_console("Launching bug reporter\n");
+ PrintStackTraceOSX(p);
+ LaunchBugReporter (kCrashbug);
+
+ #elif UNITY_WIN
+
+ winutils::ProcessInternalCrash(ep, false);
+
+ if( gUnityCrashHandler )
+ {
+ printf_console( "unity: Launch crash handler\n" );
+ return gUnityCrashHandler->ProcessCrash( ep );
+ }
+ return EXCEPTION_EXECUTE_HANDLER;
+
+ #else
+ #error "Unknown platform"
+ #endif
+
+ #else
+ // ---- player
+
+ // We are hitting this from inside a mono method, so lets just keep going!
+ if (mono_thread_current() != NULL && mono_method_get_last_managed() != NULL)
+ {
+ #if WEBPLUG
+ ExitWithErrorCode(kErrorFatalException);
+ #endif
+
+ #if UNITY_OSX
+ abort();
+ #endif
+
+ #if UNITY_WIN
+ return EXCEPTION_EXECUTE_HANDLER;
+ #endif
+
+ }
+ else
+ {
+ // we have a serious exception - launch crash reporter
+ #if UNITY_WIN && USE_WIN_CRASH_HANDLER
+
+ winutils::ProcessInternalCrash(ep, false);
+
+ if( gUnityCrashHandler ) {
+ printf_console( "unity: Launch crash handler\n" );
+ return gUnityCrashHandler->ProcessCrash( ep );
+ }
+ #endif
+
+ #if WEBPLUG
+ ExitWithErrorCode(kErrorFatalException);
+ #endif
+
+ #if UNITY_OSX
+ abort();
+ #endif
+
+ #if UNITY_WIN
+ return EXCEPTION_EXECUTE_HANDLER;
+ #endif
+ }
+ #endif
+}
+
+#endif // !UNITY_WII && !UNITY_PS3 && !UNITY_XENON && !UNITY_IPHONE && !UNITY_ANDROID && !UNITY_PEPPER && !UNITY_LINUX
+
+#if UNITY_WIN
+void __cdecl HandleAbort (int signal)
+{
+ printf_console ("Received abort signal from the operating system\n");
+ #if UNITY_EDITOR
+ LaunchBugReporter (kFatalError);
+ #endif
+}
+#endif
+
+void UnhandledExceptionHandler (MonoObject* object)
+{
+ printf_console("Receiving unhandled exception\n");
+
+ #if UNITY_EDITOR
+ LaunchBugReporter (kFatalError);
+ #elif WEBPLUG
+
+ #else
+ #endif
+}
+
+#if USE_MONO_DOMAINS
+bool CleanupMonoReloadable ()
+{
+
+ UnloadDomain();
+ return true;
+}
+#endif
+
+void CleanupMono ()
+{
+ #if DEBUGMODE
+ printf_console( "Cleanup mono\n" );
+ #endif
+
+ RegisterLogPreprocessor (NULL);
+
+#if USE_MONO_DOMAINS
+ UnloadDomain();
+#endif
+
+ mono_runtime_set_shutting_down ();
+#if !UNITY_PEPPER
+ mono_threads_set_shutting_down ();
+ mono_thread_pool_cleanup ();
+ mono_thread_suspend_all_other_threads ();
+#endif
+ mono_jit_cleanup(mono_get_root_domain());
+
+ #if UNITY_PEPPER
+ UNITY_FREE (kMemMono, gCorLibMemory);
+ #endif
+
+ #if UNITY_OSX
+ struct sigaction sa;
+ sa.sa_sigaction = NULL;
+ sigemptyset (&sa.sa_mask);
+ sa.sa_flags = UNITY_SA_DISABLE;
+ sigaction (SIGSEGV, &sa, NULL);
+
+ sa.sa_sigaction = NULL;
+ sigemptyset (&sa.sa_mask);
+ sa.sa_flags = UNITY_SA_DISABLE;
+ sigaction (SIGABRT, &sa, NULL);
+ #endif
+
+ MonoPathContainer::StaticDestroy();
+
+ UNITY_DELETE(s_MonoDomainContainer,kMemMono);
+}
+
+void PostprocessStacktrace(const char* stackTrace, std::string& processedStackTrace)
+{
+ if (GetMonoManagerPtr () && GetMonoManager ().GetCommonClasses ().postprocessStacktrace)
+ {
+ MonoException* exception = NULL;
+ int stripEngineInternalInformation = UNITY_RELEASE;
+ void* arg[] = { MonoStringNew(stackTrace), &stripEngineInternalInformation };
+
+
+ MonoString* msTrace = (MonoString*)mono_runtime_invoke_profiled (GetMonoManager ().GetCommonClasses ().postprocessStacktrace->monoMethod, NULL, arg, &exception);
+ if (exception)
+ {
+ printf_console ("Failed to postprocess stacktrace\n");
+ return;
+ }
+ processedStackTrace = MonoStringToCpp (msTrace);
+ }
+}
+
+static void ExtractMonoStacktrace (const std::string& condition, std::string& processedStackTrace, std::string& stackTrace, int errorNum, string& file, int* line, int type, int targetInstanceID)
+{
+#if UNITY_WII
+ // When debugger is running on Wii, mono stack extraction fails for unknown reasons
+ // But if running without debugger it works fine
+ if (wii::IsDebuggerPresent()) return;
+#endif
+
+#if SUPPORT_THREADS
+ const bool isMainThread = Thread::EqualsCurrentThreadID(Thread::mainThreadId);
+#else
+ const bool isMainThread = true;
+#endif
+ if (isMainThread && mono_thread_current() != NULL && mono_method_get_last_managed () != NULL && (type & kDontExtractStacktrace) == 0)
+ {
+ MonoClass* stackTraceUtil = GetMonoManager ().GetMonoClass ("StackTraceUtility", kEngineNameSpace);
+ if (stackTraceUtil)
+ {
+ void* iter = NULL;
+ MonoMethod* method;
+ while ((method = mono_class_get_methods (stackTraceUtil, &iter))) {
+ const char* curName = mono_method_get_name (method);
+ if (strcmp ("ExtractStackTrace", curName) == 0)
+ break;
+ }
+
+ if (method)
+ {
+ MonoException* exception = NULL;
+ MonoString* msTrace = (MonoString*)mono_runtime_invoke_profiled (method, NULL, NULL, &exception);
+ if (exception)
+ {
+ printf_console ("Failed to extract mono stacktrace from Log message\n");
+ return;
+ }
+
+ stackTrace = MonoStringToCpp (msTrace);
+ if (!stackTrace.empty ())
+ {
+ int oldLine = *line;
+ string oldFile = file;
+ ExceptionToLineAndPath (stackTrace, *line, file);
+ if (!(type & kMayIgnoreLineNumber))
+ stackTrace = Format ("%s\n[%s line %d]", stackTrace.c_str (), oldFile.c_str(), oldLine);
+
+ PostprocessStacktrace(stackTrace.c_str(), processedStackTrace);
+ }
+ }
+ }
+ }
+}
+
+MonoMethod* FindStaticMonoMethod (MonoImage* image, const char* className, const char* nameSpace, const char* methodName)
+{
+ MonoClass* klass = mono_class_from_name(image,nameSpace,className);
+ if (!klass) return NULL;
+
+ MonoMethod* method = mono_class_get_method_from_name(klass,methodName,-1);
+ if (!method) return NULL;
+
+ return method;
+}
+
+MonoMethod* FindStaticMonoMethod (const char* nameSpace, const char* className, const char* methodName)
+{
+ MonoClass* klass = GetMonoManager ().GetMonoClass (className, nameSpace);
+ if (!klass)
+ return NULL;
+ return mono_class_get_method_from_name (klass, methodName, -1);
+}
+
+MonoMethod* FindStaticMonoMethod (const char* className, const char* methodName)
+{
+ MonoClass* klass = GetMonoManager ().GetMonoClass (className, kEngineNameSpace);
+ if (!klass)
+ klass = GetMonoManager ().GetMonoClass (className, kEditorNameSpace);
+ if (!klass)
+ klass = GetMonoManager ().GetMonoClass (className, kEditorInternalNameSpace);
+ if (!klass)
+ return NULL;
+
+ return mono_class_get_method_from_name (klass, methodName, -1);
+}
+
+std::string MdbFile (const std::string& path)
+{
+ return AppendPathNameExtension (path, "mdb");
+}
+std::string PdbFile (const std::string& path)
+{
+ int f = path.find(".dll");
+ return AppendPathNameExtension (f != -1 ? path.substr(0, f) : path, "pdb");
+}
+
+void ClearLogCallback ()
+{
+#if UNITY_HAS_DEVELOPER_CONSOLE
+ // By default redirect log output to the developer console
+ RegisterLogCallback(DeveloperConsole_HandleLogFunction, true);
+#else
+ RegisterLogCallback(NULL, false);
+#endif // UNITY_HAS_DEVELOPER_CONSOLE
+}
+
+template<class TransferFunction>
+void MonoManager::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion(2);
+
+ #if UNITY_EDITOR
+ if (transfer.IsSerializingForGameRelease ())
+ {
+ transfer.Transfer (m_MonoScriptManager.m_RuntimeScripts, "m_Scripts");
+
+ //flash exports monomanager in a special way, it only requires m_Scripts
+ if (transfer.IsWritingGameReleaseData () && transfer.GetBuildingTarget().platform==kBuildFlash)
+ return;
+
+ transfer.Transfer (m_AssemblyNames, "m_AssemblyNames");
+ }
+ else if (transfer.GetFlags() & kPerformUnloadDependencyTracking)
+ {
+ transfer.Transfer (m_MonoScriptManager.m_RuntimeScripts, "m_Scripts");
+ }
+
+ #else
+ transfer.Transfer (m_MonoScriptManager.m_RuntimeScripts, "m_Scripts");
+ transfer.Transfer (m_AssemblyNames, "m_AssemblyNames");
+
+ if (transfer.IsOldVersion (1))
+ {
+ if (m_AssemblyNames.size() >= kScriptAssemblies)
+ {
+ m_AssemblyNames[0] = kEngineAssemblyName;
+ for (int i = 1; i < kScriptAssemblies; i++)
+ m_AssemblyNames[i] = "";
+ }
+
+ set<UnityStr> m_CustomDlls;
+ TRANSFER(m_CustomDlls);
+ for (std::set<UnityStr>::iterator i=m_CustomDlls.begin();i!=m_CustomDlls.end();i++)
+ {
+ if (find(m_AssemblyNames.begin(), m_AssemblyNames.end(), *i) == m_AssemblyNames.end())
+ m_AssemblyNames.push_back(*i);
+ }
+ }
+ #endif
+}
+
+
+IMPLEMENT_OBJECT_SERIALIZE (MonoManager)
+IMPLEMENT_CLASS (MonoManager)
+GET_MANAGER (MonoManager)
+GET_MANAGER_PTR (MonoManager)
+
+#endif //ENABLE_MONO
diff --git a/Runtime/Mono/MonoManager.h b/Runtime/Mono/MonoManager.h
new file mode 100644
index 0000000..b7c9b4b
--- /dev/null
+++ b/Runtime/Mono/MonoManager.h
@@ -0,0 +1,240 @@
+#ifndef MONOMANAGER_H
+#define MONOMANAGER_H
+
+#include "MonoIncludes.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Modules/ExportModules.h"
+
+#if UNITY_FLASH
+# include "Runtime/Scripting/MonoManager_Flash.h"
+#elif UNITY_WINRT
+# include "Runtime/Scripting/MonoManager_WinRT.h"
+#endif
+
+#if !ENABLE_MONO
+struct MonoAssembly;
+struct MonoVTable;
+struct MonoException;
+struct MonoClassField;
+struct MonoDomain;
+#endif
+
+#if ENABLE_MONO
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include <set>
+#include <list>
+#include "Runtime/Utilities/DateTime.h"
+#include "Runtime/Utilities/dynamic_bitset.h"
+#include "Runtime/Threads/ThreadSpecificValue.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/Mono/MonoScriptManager.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Scripting/Backend/IScriptingTypeProvider.h"
+
+namespace Unity { class Component; class GameObject; }
+extern const char* kUnityEngine;
+extern const char* kMonoClasslibsProfile;
+struct GUIState;
+struct DomainReloadingData;
+
+typedef dynamic_bitset AssemblyMask;
+
+class MonoScript;
+class MonoBehaviour;
+
+
+/*
+ The Mono Scripting runtime System consists of 3 classes.
+
+ MonoManager
+ MonoScript
+ MonoBehaviour
+
+ The MonoManager contains all information about the Assemblys it uses.
+ (An assembly is an DLL or Exe containing the classes, metadata, and IL assembly code)
+
+ When a MonoScript is loaded or rebuilt because the script has changed,
+ the MonoManager is asked to lookup the MonoClass given by the script name.
+
+ The MonoManager also keeps a lookup of ClassIDToMonoClass which is a precalculated list
+ of classID's to their MonoClass* (The lookup respects inheritance so that when the C++ class is not availible as a MonoClass its parent Class is used instead)
+
+ ---------- continue.....
+
+ MonoExport.cpp is used to wrap all the C++ objects eg. Transform
+*/
+
+
+typedef void ScriptsDidChangeCallback ();
+
+// TODO: remove
+MonoObject* MonoInstantiateScriptingWrapperForClassID(int classID);
+
+class EXPORT_COREMODULE MonoManager : public ScriptingManager, public IScriptingTypeProvider
+{
+ public:
+ REGISTER_DERIVED_CLASS (MonoManager, GlobalGameManager)
+ DECLARE_OBJECT_SERIALIZE (MonoManager)
+
+ MonoManager (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~MonoManager (); declared-by-macro
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ // If 'theNameSpace' is NULL then search is in any namespace
+ MonoClass* GetMonoClassCaseInsensitive (const char* className, const char* theNameSpace = NULL);
+ // If 'theNameSpace' is NULL then search is in any namespace
+ MonoClass* GetMonoClass (const char* className, const char* theNameSpace = NULL);
+
+ // Returns the class with className in the assembly defined by identifier (normally the pathname extension of the script)
+ MonoClass* GetMonoClassWithAssemblyName (const std::string& className, const string& nameSpace, const string& identifier);
+
+ /// Reloads all assemblies with assemblymask. If the assembly can be loaded, the bit for that assembly is cleared.
+ /// Returns which assemblies could be loaded
+
+#if UNITY_EDITOR
+ enum { kEngineAssembly = 0, kEditorAssembly = 1, kLocatorAssembly = 2, kScriptAssemblies = 3 };
+#else
+ enum { kEngineAssembly = 0, kEditorAssembly = 1, kScriptAssemblies = 2 };
+#endif
+ enum AssemblyLoadFailure { kEverythingLoaded = 0, kFailedLoadingScriptAssemblies = 1, kFailedLoadingEngineOrEditorAssemblies = 2 };
+ AssemblyLoadFailure ReloadAssembly (AssemblyMask allAssembliesMask);
+
+ AssemblyMask GetSystemAssemblyMask (bool load);
+ AssemblyMask GetAvailableDllAssemblyMask ();
+
+ MonoImage* GetEngineImage () { return m_ScriptImages[kEngineAssembly]; }
+
+ std::string GetAssemblyPath (int index);
+ MonoAssembly* GetAssembly (int index);
+
+ std::string GetAssemblyIdentifierFromImage(MonoImage* image);
+ int GetAssemblyIndexFromImage(MonoImage* image);
+
+ void AssertInvalidAssembly (MonoClass* klass);
+
+ MonoClass* GetBuiltinMonoClass (const char* name, bool optional = false);
+
+ int GetAssemblyCount() const {return m_AssemblyNames.size(); }
+ int GetAssemblyIndexFromAssemblyName (const string& identifier);
+
+ void UnloadAllAssembliesOnNextDomainReload();
+
+ #if UNITY_EDITOR
+
+ void ResizeAssemblyNames(int max);
+
+ // The assembly name should never be changed when mono dll's have already been loaded.
+ void SetAssemblyName (unsigned index, const string& name);
+
+ void RegisterScriptsChanged (ScriptsDidChangeCallback* func);
+
+ // Called when the domain and UnityEngine/UnityEditor have been reloaded but before anything else is,
+ // to give a chance for other dlls to be loaded before anything else (i.e., package manager)
+ void SetLogAssemblyReload (bool reload) { m_LogAssemblyReload = reload; }
+
+ MonoClass* GetBuiltinEditorMonoClass (const char* name);
+
+ int InsertAssemblyName(const std::string& assemblyName);
+ void SetCustomDllPathLocation (const std::string& name, const std::string& path);
+
+ bool HasCompileErrors () { return m_HasCompileErrors; }
+ void SetHasCompileErrors (bool compileErrors);
+
+ std::vector<UnityStr>& GetRawAssemblyNames () { return m_AssemblyNames; }
+
+ #else
+ bool HasCompileErrors () { return false; }
+ #endif
+
+
+ //implementation of IScriptingTypeProvider
+ BackendNativeType NativeTypeFor(const char* namespaze, const char* className);
+ ScriptingTypePtr Provide(BackendNativeType nativeType);
+ void Release(ScriptingTypePtr klass);
+
+ private:
+
+ void SetupLoadedEditorAssemblies ();
+ AssemblyLoadFailure BeginReloadAssembly (DomainReloadingData& savedData);
+ AssemblyLoadFailure EndReloadAssembly (const DomainReloadingData& savedData, AssemblyMask allAssembliesMask);
+
+ // Rebuilds the m_ClassIDToMonoClass lookup table.
+ // m_ClassIDToMonoClass maps from the classID to the MonoClass it is best represented by.
+ // If a MonoClass can't be found for the exact type the next parent class is used instead
+ void RebuildClassIDToScriptingClass ();
+ void CleanupClassIDMaps();
+ // Initializes in the CommonMonoTypes struct
+ virtual void RebuildCommonMonoClasses ();
+
+ bool LoadAssemblies (AssemblyMask allAssembliesMask);
+ void PopulateAssemblyReferencingDomain();
+
+
+ typedef std::vector<MonoImage*> ScriptImages;
+
+ ScriptImages m_ScriptImages;
+
+ std::vector<UnityStr> m_AssemblyNames;
+
+ std::vector<MonoVTable*> m_ClassIDToVTable;
+
+ bool m_HasCompileErrors;
+ static UNITY_TLS_VALUE(bool) m_IsMonoBehaviourInConstructor;
+
+ MonoDomain* m_AssemblyReferencingDomain;
+ bool IsThisFileAnAssemblyThatCouldChange(std::string& path);
+
+ #if UNITY_EDITOR
+ DateTime m_EngineDllModDate;
+
+ typedef std::map<std::string, std::string> CustomDllLocation;
+ CustomDllLocation m_CustomDllLocation;
+ bool m_LogAssemblyReload;
+ string m_ManagedEditorAssembliesBasePath;
+ #endif
+};
+
+EXPORT_COREMODULE MonoManager& GetMonoManager ();
+MonoManager* GetMonoManagerPtr ();
+
+
+namespace MonoPathContainer
+{
+ std::vector<std::string>& GetMonoPaths();
+ void AppendMonoPath (const string& path);
+};
+
+MonoMethod* FindStaticMonoMethod (MonoImage* image, const char* className, const char* nameSpace, const char* methodName);
+MonoMethod* FindStaticMonoMethod (const char* className, const char* methodName);
+MonoMethod* FindStaticMonoMethod (const char* nameSpace, const char* className, const char* methodName);
+
+/// This has to be called from main to initialize mono
+bool InitializeMonoFromMain (const std::vector<string>& monoPaths, string monoConfigPath, int argc, const char** argv, bool enableDebugger=false);
+void CleanupMono ();
+bool CleanupMonoReloadable ();
+
+#if UNITY_RELEASE
+#define AssertInvalidClass(x)
+#else
+#define AssertInvalidClass(x) GetMonoManager().AssertInvalidAssembly(x);
+#endif
+
+std::string MdbFile (const string& path);
+std::string PdbFile (const string& path);
+
+void DisableLoadSeperateDomain ();
+
+MonoDomain* CreateDomain ();
+
+void RegisterUnloadDomainCallback (ScriptsDidChangeCallback* call);
+
+void PostprocessStacktrace(const char* stackTrace, std::string& processedStackTrace);
+
+void ClearLogCallback ();
+
+#endif //ENABLE_SCRIPTING
+#endif
diff --git a/Runtime/Mono/MonoScopedThreadAttach.cpp b/Runtime/Mono/MonoScopedThreadAttach.cpp
new file mode 100644
index 0000000..7cead89
--- /dev/null
+++ b/Runtime/Mono/MonoScopedThreadAttach.cpp
@@ -0,0 +1,72 @@
+/*
+ * MonoScopedThreadAttach.cpp
+ * AllTargets.workspace
+ *
+ * Created by Søren Christiansen on 8/23/11.
+ * Copyright 2011 Unity Technologies. All rights reserved.
+ *
+ */
+#include "UnityPrefix.h"
+#include "MonoScopedThreadAttach.h"
+#include "MonoIncludes.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/Mutex.h"
+
+#if SUPPORT_MONO_THREADS
+
+static attached_thread m_AttachedThreads[MAX_ATTACHED_THREADS] = {{0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}};
+static Mutex mutex;
+
+
+// call this from the thread you want to attach!
+Thread::ThreadID AttachMonoThread(MonoDomain* domain)
+{
+ Assert(!Thread::CurrentThreadIsMainThread());
+ Mutex::AutoLock lock( mutex );
+
+ for (int i=0; i < MAX_ATTACHED_THREADS; ++i)
+ {
+ if (m_AttachedThreads[i].threadID == Thread::GetCurrentThreadID())
+ {
+ m_AttachedThreads[i].refCount++;
+ return m_AttachedThreads[i].threadID;
+ }
+ else
+ if (m_AttachedThreads[i].threadID == 0)
+ {
+ m_AttachedThreads[i].threadID = Thread::GetCurrentThreadID();
+ m_AttachedThreads[i].thread = mono_thread_attach(domain);
+ m_AttachedThreads[i].refCount = 1;
+ return m_AttachedThreads[i].threadID;
+ }
+ }
+
+ return 0;
+}
+
+// call this from the thread you want to detach!
+bool DetachMonoThread(Thread::ThreadID threadID)
+{
+ Assert(!Thread::CurrentThreadIsMainThread());
+ Mutex::AutoLock lock( mutex );
+
+ for (int i=0; i < MAX_ATTACHED_THREADS; ++i)
+ {
+ if (m_AttachedThreads[i].threadID == Thread::GetCurrentThreadID())
+ {
+ m_AttachedThreads[i].refCount--;
+ if (m_AttachedThreads[i].refCount == 0)
+ {
+ mono_thread_detach(m_AttachedThreads[i].thread);
+ m_AttachedThreads[i].threadID = 0;
+ m_AttachedThreads[i].thread = 0;
+ m_AttachedThreads[i].refCount = 0;
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#endif // SUPPORT_MONO_THREADS
diff --git a/Runtime/Mono/MonoScopedThreadAttach.h b/Runtime/Mono/MonoScopedThreadAttach.h
new file mode 100644
index 0000000..7549bac
--- /dev/null
+++ b/Runtime/Mono/MonoScopedThreadAttach.h
@@ -0,0 +1,52 @@
+/*
+ * MonoScopedThreadAttach.h
+ * AllTargets.workspace
+ *
+ * Created by Søren Christiansen on 8/23/11.
+ * Copyright 2011 Unity Technologies. All rights reserved.
+ *
+ */
+#ifndef __UNITY_MONOSCOPEDTHREADATTACH_H__
+#define __UNITY_MONOSCOPEDTHREADATTACH_H__
+
+#if SUPPORT_MONO_THREADS
+
+#include "Runtime/Threads/Thread.h"
+
+#define MAX_ATTACHED_THREADS 4
+
+struct MonoDomain;
+struct MonoThread;
+
+Thread::ThreadID AttachMonoThread(MonoDomain* domain);
+bool DetachMonoThread(Thread::ThreadID threadID);
+
+struct attached_thread
+{
+ Thread::ThreadID threadID;
+ MonoThread* thread;
+ int refCount;
+};
+
+struct ScopedThreadAttach
+{
+ ScopedThreadAttach(MonoDomain* domain) : threadID(0)
+ {
+ Assert(domain);
+ if (!Thread::CurrentThreadIsMainThread())
+ threadID = AttachMonoThread(domain);
+ }
+ ~ScopedThreadAttach()
+ {
+ if (threadID)
+ DetachMonoThread(threadID);
+ }
+
+private:
+ ScopedThreadAttach(const ScopedThreadAttach& other);
+ void operator=(const ScopedThreadAttach& other);
+ Thread::ThreadID threadID;
+};
+
+#endif // SUPPORT_MONO_THREADS
+#endif // __UNITY_MONOSCOPEDTHREADATTACH_H__
diff --git a/Runtime/Mono/MonoScript.cpp b/Runtime/Mono/MonoScript.cpp
new file mode 100644
index 0000000..75b7c30
--- /dev/null
+++ b/Runtime/Mono/MonoScript.cpp
@@ -0,0 +1,443 @@
+#include "UnityPrefix.h"
+#if ENABLE_SCRIPTING
+#include "MonoScript.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferUtility.h"
+#include "MonoManager.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "MonoScriptCache.h"
+#include "MonoBehaviour.h"
+
+#if UNITY_FLASH || UNITY_WINRT
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#endif
+
+const char* kBuildLogicGraphDllFile = "Temp/StagingArea/Data/Managed/LogicGraphs.dll";
+
+string GetGeneratedLogicGraphsDllName()
+{
+ return GetLastPathNameComponent(kBuildLogicGraphDllFile);
+}
+
+
+using namespace std;
+
+
+#if UNITY_EDITOR
+static UInt32 ComputeTypeTreeHashForScriptClass (MonoScript* script)
+{
+ if (script->GetClass () == NULL
+ || !IsValidScriptType (script->GetScriptType ())
+ || script->IsEditorScript ())
+ return 0;
+
+ // We don't want to call the constructor as that may run user code and
+ // may have all kinds of side-effects. All we need is an instance for
+ // running the TypeTree generation/serialization code on.
+ ScriptingObjectPtr instance = scripting_object_new (script->GetClass ());
+ if (!instance)
+ return 0;
+
+ // Create a temporary MonoBehaviour that we can run ProxyTransfer on.
+ MonoBehaviour* dummyInstance = NEW_OBJECT (MonoBehaviour);
+ dummyInstance->Reset ();
+ dummyInstance->HackSetAwakeWasCalled ();
+
+ // Connect MonoBehaviour to script class and to the object instance
+ // we have created.
+ dummyInstance->SetScript (script, instance);
+
+ // Generate type tree.
+ TypeTree typeTree;
+ GenerateTypeTree (*dummyInstance, &typeTree);
+ DestroySingleObject (dummyInstance);
+
+ // Compute hash of tree.
+ return HashTypeTree (typeTree);
+}
+#endif // UNITY_EDITOR
+
+
+MonoScript::MonoScript (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_PropertiesHash = 0;
+
+ m_ScriptCache = NULL;
+ m_IsEditorScript = false;
+ m_ExecutionOrder = 0;
+}
+
+MonoScript::~MonoScript ()
+{
+ CleanupScriptCache();
+}
+
+UInt32 MonoScript::GetPropertiesHash ()
+{
+ // In the editor, compute the property hash on demand based on the type
+ // tree generated by the MonoBehaviour serialization code.
+ #if UNITY_EDITOR
+ if (m_PropertiesHash == 0)
+ m_PropertiesHash = ComputeTypeTreeHashForScriptClass (this);
+ #endif
+
+ return m_PropertiesHash;
+}
+
+UnityStr MonoScript::GetScriptFullClassName () const
+{
+ if (m_Namespace.empty())
+ return m_ClassName;
+ else
+ return m_Namespace + "." + m_ClassName;
+}
+
+ScriptingClassPtr MonoScript::GetClass ()
+{
+ if (m_ScriptCache)
+ return m_ScriptCache->klass;
+ else
+ return NULL;
+}
+
+ScriptingMethodPtr MonoScript::FindMethod (const char* name)
+{
+ if (m_ScriptCache)
+ return ::FindMethod(*m_ScriptCache, name);
+ else
+ return NULL;
+}
+
+MonoScriptType MonoScript::GetScriptType() const
+{
+ if (m_ScriptCache == NULL)
+ return kScriptTypeNotInitialized;
+ else
+ return m_ScriptCache->scriptType;
+}
+
+
+void MonoScript::CleanupScriptCache ()
+{
+ if (m_ScriptCache != NULL)
+ {
+ const_cast<MonoScriptCache*> (m_ScriptCache)->Release();
+ m_ScriptCache = NULL;
+ }
+}
+
+#if ENABLE_SCRIPTING
+
+void MonoScript::Rebuild (ScriptingTypePtr klass)
+{
+ CleanupScriptCache ();
+
+ #if UNITY_EDITOR
+ m_PropertiesHash = 0;
+ #endif
+
+ m_ScriptCache = CreateMonoScriptCache (klass, m_IsEditorScript, this);
+}
+#endif
+
+void MonoScript::RebuildFromAwake()
+{
+#if ENABLE_MONO
+ Rebuild (GetMonoManager().GetMonoClassWithAssemblyName (GetScriptClassName (), GetNameSpace(), GetAssemblyName ()));
+#elif UNITY_FLASH || UNITY_WINRT
+ ScriptingTypePtr t = GetScriptingTypeRegistry().GetType(GetNameSpace().c_str(), GetScriptClassName().c_str());
+ Rebuild(t);
+#endif
+
+}
+
+// Used by WinRT & Flash
+UnityStr MonoScript::GetScriptName()
+{
+ UnityStr s = GetNameSpace();
+ if (s.size() > 0)
+ s += "." + GetScriptClassName();
+ else
+ s = GetScriptClassName();
+ return s;
+}
+
+void MonoScript::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ if ((awakeMode & kDidLoadThreaded) == 0)
+ {
+ RebuildFromAwake();
+ }
+}
+
+void MonoScript::AwakeFromLoadThreaded ()
+{
+ Super::AwakeFromLoadThreaded();
+ RebuildFromAwake();
+}
+
+template<class TransferFunction>
+void MonoScript::TransferPropertiesHash (TransferFunction& transfer)
+{
+ #if UNITY_EDITOR
+ if (transfer.IsWritingGameReleaseData())
+ {
+ // Force building the properties hash
+ GetPropertiesHash();
+ }
+ #endif
+
+ // When reading or writing for game release
+ // Transfer properties hash
+ if (transfer.IsSerializingForGameRelease ())
+ transfer.Transfer (m_PropertiesHash, "m_PropertiesHash", kNotEditableMask);
+}
+
+#if UNITY_EDITOR
+// Temporarily (When building the player during the write operation).
+// Sets the correct assembly of the MonoScript, and restores it afterwards.
+struct RemapAssemblyDuringBuild
+{
+ MonoScript* m_Script;
+ string m_AssemblyName;
+ string m_Namespace;
+ string m_ClassName;
+
+ RemapAssemblyDuringBuild (MonoScript& inScript, bool logicGraphRelease)
+ : m_Script (NULL)
+ {
+ if (!logicGraphRelease)
+ return;
+
+ m_Script = &inScript;
+
+ m_AssemblyName = m_Script->GetAssemblyName();
+ m_Namespace = m_Script->GetNameSpace();
+ m_ClassName = m_Script->GetScriptClassName();
+
+ m_Script->m_AssemblyName = GetGeneratedLogicGraphsDllName();
+ m_Script->m_Namespace = "";
+
+ m_Script->m_ClassName = m_Script->m_EditorGraphData->GetName();
+ }
+
+ ~RemapAssemblyDuringBuild ()
+ {
+ if (m_Script == NULL)
+ return;
+
+ m_Script->m_AssemblyName = m_AssemblyName;
+ m_Script->m_Namespace = m_Namespace;
+ m_Script->m_ClassName = m_ClassName;
+ }
+};
+#endif
+
+template<class TransferFunction>
+void MonoScript::Transfer (TransferFunction& transfer)
+{
+ // Don't transfer script class since we already transfer the script property ourselves.
+ Super::Super::Transfer (transfer);
+
+ transfer.SetVersion(4);
+
+ #if UNITY_EDITOR
+
+ bool logicGraphRelease = transfer.IsWritingGameReleaseData() && !m_EditorGraphData.IsNull();
+ RemapAssemblyDuringBuild remap (*this, logicGraphRelease);
+
+ if (!transfer.IsSerializingForGameRelease ())
+ {
+ transfer.Transfer (m_Script, "m_Script", kHideInEditorMask);
+ transfer.Transfer (m_DefaultReferences, "m_DefaultReferences", kHideInEditorMask);
+ transfer.Transfer (m_Icon, "m_Icon", kNoTransferFlags);
+ TRANSFER (m_EditorGraphData);
+ }
+
+ #endif
+
+
+ transfer.Transfer (m_ExecutionOrder, "m_ExecutionOrder", kNotEditableMask);
+ TransferPropertiesHash (transfer);
+
+ transfer.Transfer (m_ClassName, "m_ClassName", kNotEditableMask);
+ transfer.Transfer (m_Namespace, "m_Namespace", kNotEditableMask);
+ transfer.Transfer (m_AssemblyName, "m_AssemblyName", kNotEditableMask);
+ transfer.Transfer (m_IsEditorScript, "m_IsEditorScript", kHideInEditorMask);
+
+ // AssemblyIdentifier has been removed and is now simply the dll name
+ if (transfer.IsVersionSmallerOrEqual(1))
+ {
+ transfer.Transfer (m_AssemblyName, "m_AssemblyIdentifier", kNotEditableMask);
+
+ if (m_AssemblyName == "Unity Engine Special")
+ {
+ m_AssemblyName = "UnityEngine.dll";
+ m_Namespace = "UnityEngine";
+ }
+ else if (m_AssemblyName == "Unity Editor Special")
+ {
+ m_AssemblyName = "UnityEditor.dll";
+ m_Namespace = "UnityEditor";
+ }
+ else
+ {
+ m_AssemblyName = "Assembly - " + m_AssemblyName + ".dll";
+ }
+ }
+
+ #if UNITY_EDITOR
+ // In 2.x Assemblies compiled by Unity had spaces in the name.
+ // In 3.0 we are removing all spaces, since this makes loading assemblies easier and fixes issue on xbox where the filename is too long.
+ // ("Assembly - UnityScript.dll" -> "Assembly-UnityScript.dll")
+ if (transfer.IsVersionSmallerOrEqual(2))
+ {
+ if (BeginsWith(m_AssemblyName, "Assembly - "))
+ replace_string(m_AssemblyName, " ", "");
+ }
+ #endif
+}
+
+bool MonoScript::ShouldIgnoreInGarbageDependencyTracking ()
+{
+ // MonoScript can have referenced
+ // But it's ok to ignore them eg. references to default references, icons and editor graph
+ return true;
+}
+
+void MonoScript::Init (const ScriptString& script, const string& className, const std::string& nameSpace, const string& identifier, bool isEditorScript)
+{
+ SetScript(script);
+ m_ClassName = className;
+ m_Namespace = nameSpace;
+ m_AssemblyName = identifier;
+ m_PropertiesHash = 0;
+ m_IsEditorScript = isEditorScript;
+
+ SetDirty ();
+}
+
+#if ENABLE_SCRIPTING
+
+void MonoScript::Init (MonoClass* scriptType)
+{
+ m_ClassName = scripting_class_get_name(scriptType);
+ m_Namespace = scripting_class_get_namespace(scriptType);
+ m_PropertiesHash = 0;
+ #if ENABLE_MONO
+ MonoImage* image = mono_class_get_image(scriptType);
+ MonoAssemblyName name;
+ bool success = mono_assembly_fill_assembly_name(image, &name);
+ if (success)
+ m_AssemblyName = mono_stringify_assembly_name(&name);
+ #endif
+ m_IsEditorScript = 0;
+ SetDirty ();
+}
+#endif
+
+std::string MonoScript::GetNameSpace ()const
+{
+ return m_Namespace;
+}
+
+#if ENABLE_SCRIPTING
+MonoScript* CreateMonoScriptFromScriptingType(ScriptingTypePtr klass)
+{
+ MonoScript* result = NEW_OBJECT(MonoScript);
+ result->Reset ();
+ result->Init(klass);
+ GetMonoScriptManager().RegisterRuntimeScript (*result);
+ result->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+ return result;
+}
+#endif
+
+void MonoScript::SetExecutionOrder (SInt32 executionOrder)
+{
+ m_ExecutionOrder = executionOrder;
+ SetDirty();
+}
+
+#if UNITY_EDITOR
+bool MonoScript::IsBuiltinScript() const
+{
+ return m_AssemblyName == "UnityEngine.dll" || m_AssemblyName == "UnityEditor.dll";
+}
+
+void MonoScript::SetIcon (PPtr<Object> icon)
+{
+ m_Icon = icon;
+ // We do not call SetDirty() since MonoScript is generated data (MonoImporter holds the icon state)
+}
+
+PPtr<Object> MonoScript::GetIcon () const
+{
+ return m_Icon;
+}
+
+void MonoScript::SetEditorGraphData(Object* data)
+{
+ if (m_EditorGraphData != PPtr<Object> (data))
+ {
+ m_EditorGraphData = data;
+ SetDirty();
+ }
+}
+
+PPtr<Object> MonoScript::GetEditorGraphData()
+{
+ return m_EditorGraphData;
+}
+
+bool MonoScript::GetScriptTypeWasJustCreatedFromComponentMenu ()
+{
+ if (m_ScriptCache)
+ return m_ScriptCache->scriptTypeWasJustCreatedFromComponentMenu;
+ else
+ return false;
+}
+
+void MonoScript::SetScriptTypeWasJustCreatedFromComponentMenu ()
+{
+ if (m_ScriptCache == NULL)
+ Rebuild(NULL);
+
+ const_cast<MonoScriptCache*> (m_ScriptCache)->scriptTypeWasJustCreatedFromComponentMenu = true;
+}
+
+std::string BuildScriptClassId(const std::string& assembly, const std::string& ns, const std::string& klass)
+{
+ return assembly + ":" + ns + ":" + klass;
+}
+
+void GetScriptClassIdComponents(const std::string& scriptClassId, std::string& assembly, std::string& ns, std::string& klass)
+{
+ std::vector<std::string> parts;
+ Split(scriptClassId, ":", parts);
+
+ assembly = parts[0];
+ AssertIf(parts.size() != 2 && parts.size() != 3);
+ // empty namespace
+ if (parts.size() == 2)
+ {
+ ns.clear();
+ klass = parts[1];
+ }
+ else if (parts.size() == 3)
+ {
+ ns = parts[1];
+ klass = parts[2];
+ }
+}
+
+#endif
+
+#if ENABLE_SCRIPTING
+IMPLEMENT_CLASS (MonoScript)
+IMPLEMENT_OBJECT_SERIALIZE (MonoScript)
+#endif
+
+#endif
diff --git a/Runtime/Mono/MonoScript.h b/Runtime/Mono/MonoScript.h
new file mode 100644
index 0000000..c4f72ca
--- /dev/null
+++ b/Runtime/Mono/MonoScript.h
@@ -0,0 +1,124 @@
+#ifndef MONOSCRIPT_H
+#define MONOSCRIPT_H
+
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/Scripting/TextAsset.h"
+#include "MonoScriptType.h"
+
+class MonoBehaviour;
+class MonoManager;
+struct CommonScriptingClasses;
+struct MonoScriptCache;
+
+/*
+ The MonoScript wraps a MonoClass and caches eg. some commonly used methods for fast access.
+*/
+class MonoScript : public TextAsset
+{
+ public:
+ REGISTER_DERIVED_CLASS (MonoScript, TextAsset)
+ DECLARE_OBJECT_SERIALIZE (MonoScript)
+
+ MonoScript (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~MonoScript (); declared-by-macro
+
+ void Init (ScriptingClassPtr scriptType);
+ void Init (const ScriptString& script, const std::string& className, const std::string& nameSpace, const std::string& assemblyIdentifier, bool isEditorScript);
+
+ const MonoScriptCache* GetScriptCache () const { return m_ScriptCache; }
+
+ // Returns the MonoClass with name GetScriptClassName ()
+ // Returns NULL if the MonoClass can't be found.
+ ScriptingClassPtr GetClass ();
+
+ MonoScriptType GetScriptType() const;
+
+ bool IsEditorScript () const { return m_IsEditorScript; }
+
+
+ ScriptingMethodPtr FindMethod (const char* name);
+
+ // Returns the className of the script
+ virtual const UnityStr& GetScriptClassName () const { return m_ClassName; }
+ UnityStr GetScriptFullClassName () const;
+
+ std::string GetNameSpace ()const;
+
+ // Rebuilds the cached information about the class
+ // This function should only be called from MonoManager
+ void Rebuild (ScriptingTypePtr klass);
+
+ const UnityStr& GetAssemblyName () const { return m_AssemblyName; }
+
+
+ /// Calls Rebuild to initialize the cached information about the class
+ void AwakeFromLoad(AwakeFromLoadMode mode);
+ void AwakeFromLoadThreaded ();
+ virtual bool ShouldIgnoreInGarbageDependencyTracking ();
+
+ #if UNITY_EDITOR
+
+ bool IsBuiltinScript() const;
+ const std::map<UnityStr, PPtr<Object> > GetDefaultReferences () { return m_DefaultReferences; }
+ void SetDefaultReferences (const std::map<UnityStr, PPtr<Object> >& references) { m_DefaultReferences = references; }
+
+ void SetIcon (PPtr<Object> icon);
+ PPtr<Object> GetIcon () const;
+
+ void SetEditorGraphData(Object* data);
+ PPtr<Object> GetEditorGraphData();
+
+ bool GetScriptTypeWasJustCreatedFromComponentMenu ();
+ void SetScriptTypeWasJustCreatedFromComponentMenu ();
+
+ #endif
+
+ int GetExecutionOrder () const { return m_ExecutionOrder; }
+ void SetExecutionOrder (SInt32 executionOrder);
+
+ UnityStr GetScriptName();
+
+ UInt32 GetPropertiesHash ();
+
+private:
+
+ template<class TransferFunction>
+ void TransferPropertiesHash (TransferFunction& transfer);
+
+ void CleanupScriptCache ();
+
+
+ SInt32 m_ExecutionOrder;
+ UInt32 m_PropertiesHash;
+
+ const MonoScriptCache* m_ScriptCache;
+
+ UnityStr m_ClassName;
+ UnityStr m_Namespace;
+ UnityStr m_AssemblyName;
+ bool m_IsEditorScript;
+ #if UNITY_EDITOR
+ std::map<UnityStr, PPtr<Object> > m_DefaultReferences;
+ PPtr<Object> m_Icon;
+ PPtr<Object> m_EditorGraphData;
+ #endif
+
+ friend struct RemapAssemblyDuringBuild;
+
+ void RebuildFromAwake();
+};
+
+typedef MonoScript* MonoScriptPtr;
+typedef PPtr<MonoScript> MonoScriptPPtr;
+
+MonoScript* CreateMonoScriptFromScriptingType(ScriptingClassPtr klass);
+const char* GetNameOfMethodByIndex(int index);
+
+#if UNITY_EDITOR
+std::string BuildScriptClassId(const std::string& assembly, const std::string& ns, const std::string& klass);
+void GetScriptClassIdComponents(const std::string& scriptClassId, std::string& assembly, std::string& ns, std::string& klass);
+#endif
+
+extern const char* kBuildLogicGraphDllFile;
+
+#endif
diff --git a/Runtime/Mono/MonoScriptCache.cpp b/Runtime/Mono/MonoScriptCache.cpp
new file mode 100644
index 0000000..a1397c7
--- /dev/null
+++ b/Runtime/Mono/MonoScriptCache.cpp
@@ -0,0 +1,556 @@
+#include "UnityPrefix.h"
+#include "MonoScriptCache.h"
+
+#if ENABLE_SCRIPTING
+
+#include "Runtime/BaseClasses/MessageHandler.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Misc/MessageParameters.h"
+#include "Runtime/Scripting/Backend/ScriptingMethodRegistry.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "MonoManager.h"
+
+const char* kMethodNames[] = {
+ "Update",
+ "LateUpdate",
+ "FixedUpdate",
+ "Awake",
+ "Start",
+ "Main",
+ "OnRenderObject",
+ "OnEnable",
+ "OnDisable",
+ "OnDisableINTERNAL",
+ "Start",
+ "Main",
+ "OnRenderImage",
+ "OnDrawGizmos",
+ "OnGUI",
+ "OnValidate",
+ "OnSerializeNetworkView",
+ "OnNetworkInstantiate",
+ "OnDestroy",
+ "OnAudioFilterRead",
+ NULL
+};
+
+
+typedef MonoScriptCache::MethodCache MethodCache;
+static void ClearMethodCache (MonoScriptCache::MethodCache& methods)
+{
+ for (MonoScriptCache::MethodCache::iterator i=methods.begin ();i != methods.end ();i++)
+ delete[] const_cast<char*> (i->first);
+ methods.clear ();
+}
+
+
+// These errors get removed as soon as the assembly gets reloaded. (MonoManager::ReloadAssemblies)
+#define LogScriptError(x,script) DebugStringToFile (x, 0, __FILE__, __LINE__, kLog | kScriptCompileError, script ? script->GetInstanceID() : 0, manager.GetInstanceID ());
+
+static bool Check2MethodParameters (ScriptingMethodPtr method, MonoClass* klass0, MonoClass* klass1, Object* script, MonoManager& manager);
+static bool Check1MethodParameters (ScriptingMethodPtr method, MonoClass* klass0, Object* script, MonoManager& manager);
+
+static RegisterMonoRPCCallback* gRegisterMonoRPC = NULL;
+
+void RegisterMonoRPC (RegisterMonoRPCCallback* callback)
+{
+ gRegisterMonoRPC = callback;
+}
+
+#if UNITY_FLASH
+static MethodCache* methodCacheToInsertInto;
+
+extern "C" void Ext_InsertAllMethodsInMethodCacheForType(ScriptingClassPtr klass);
+
+void BuildMethodCache (MethodCache& methods, ScriptingTypePtr klass, bool staticMethod)
+{
+ AssertIf (klass == NULL);
+ methodCacheToInsertInto = &methods;
+ Ext_InsertAllMethodsInMethodCacheForType(klass);
+}
+
+extern "C" void InsertMethodInMethodCache(const char* name, const char* mappedName, ScriptingClassPtr klass)
+{
+ const char* namecpy = strcpy(new char[strlen(name) + 1],name);
+ ScriptingMethodPtr method = new ScriptingMethod(namecpy,mappedName,"",klass);
+ methodCacheToInsertInto->insert (std::make_pair (namecpy, method));
+}
+#endif
+
+#if !UNITY_FLASH
+static void BuildMethodCache (MethodCache& methods, ScriptingClassPtr klass, bool staticMethod)
+{
+ AssertIf (klass == NULL);
+
+ std::vector<ScriptingMethodPtr> foundMethods;
+ GetScriptingMethodRegistry().AllMethodsIn(klass, foundMethods, ScriptingMethodRegistry::kInstanceOnly);
+
+ for (std::vector<ScriptingMethodPtr>::iterator methodIterator = foundMethods.begin(); methodIterator != foundMethods.end(); methodIterator++)
+ {
+ ScriptingMethodPtr method = *methodIterator;
+
+ std::string curName = scripting_method_get_name (method);
+ if (methods.find (curName.c_str()) != methods.end ())
+ continue;
+
+ methods.insert (std::make_pair (strcpy (new char[curName.length() + 1], curName.c_str()), method));
+ }
+}
+#endif
+
+#if ENABLE_MONO
+static bool IsCoroutine (MonoMethod* method, const CommonScriptingClasses& common)
+{
+ MonoType* returnType = mono_signature_get_return_type (mono_method_signature (method));
+ if (returnType == NULL)
+ return false;
+
+ MonoClass* returnClass = mono_class_from_mono_type (returnType);
+
+ // C# iterators return iEnumerator
+ return returnClass == common.iEnumerator;
+}
+
+static bool CheckMethodParameters (ScriptingMethodPtr method, MonoClass* klass, MonoClass** klassArray, unsigned numParams, Object* errorContext, MonoManager& manager)
+{
+ MonoMethodSignature* signature = mono_method_signature (method->monoMethod);
+ int paramCount = mono_signature_get_param_count (signature);
+ if (paramCount != numParams)
+ {
+ const char* foundClass = mono_class_get_name (klass);
+ string prefix = Format ("Script error (%s): %s.\n", foundClass, mono_method_get_name (method->monoMethod));
+ string postfix = "The function will be ignored.";
+ string message = Format("%sThe function must have exactly %i parameters.\n%s", prefix.c_str(), numParams, postfix.c_str());
+ LogScriptError (message, errorContext);
+ return false;
+ }
+
+ void* iterator = NULL;
+
+ bool success = true;
+
+ for (int i=0;i<numParams;++i)
+ {
+ MonoClass* monoParameterClass = mono_class_from_mono_type (mono_signature_get_params (signature, &iterator));
+
+ if (monoParameterClass != mono_get_object_class () && !mono_class_is_subclass_of (klassArray[i], monoParameterClass, true))
+ {
+ success = false;
+ break;
+ }
+ }
+
+ if (!success)
+ {
+ const char* foundClass = mono_class_get_name (klass);
+ string prefix = Format ("Script error(%s): %s.\n", foundClass, mono_method_get_name (method->monoMethod));
+ string postfix = "The function will be ignored.";
+ string message;
+ for (int i=0;i<numParams;++i)
+ {
+ message += mono_class_get_name (klassArray[i]);
+ message += (i<numParams-1)?" and ":".";
+ }
+ LogScriptError (prefix + "The function parameters have to be of type: " + message + "\n" + postfix, errorContext);
+ return false;
+ }
+ else
+ return true;
+}
+
+static bool Check1MethodParameters (ScriptingMethodPtr method, MonoClass* klass, MonoClass* klass0, Object* errorContext, MonoManager& manager)
+{
+ MonoClass* klassArray[1];
+ klassArray[0] = klass0;
+ return CheckMethodParameters(method, klass, klassArray, 1, errorContext, manager);
+}
+
+static bool Check2MethodParameters (ScriptingMethodPtr method, MonoClass* klass, MonoClass* klass0, MonoClass* klass1, Object* errorContext, MonoManager& manager)
+{
+ MonoClass* klassArray[2];
+ klassArray[0] = klass0;
+ klassArray[1] = klass1;
+ return CheckMethodParameters(method, klass, klassArray, 2, errorContext, manager);
+}
+
+// A messages has to:
+// - return either void or bool.
+// - have 1 or zero arguments
+// - the argument has to be derived from Object or be a builtin type
+// otherwise the method gets ignored as a message receiver
+// This is done to simplify the MessageHandling at runtime.
+static bool CheckMessageParameters (MonoMethod* method, int messageIndex, Object* errorContext, MonoManager& manager)
+{
+ MessageHandler& msgHandler = GameObject::GetMessageHandler ();
+ string messageName = msgHandler.MessageIDToName (messageIndex);
+
+ string prefix = "Script error: " + string (messageName) + "\n";
+ string postfix = "The message will be ignored.";
+
+
+ // Needs to have zero or one parameter
+ MonoMethodSignature* signature = mono_method_signature (method);
+ int paramCount = mono_signature_get_param_count (signature);
+ if (paramCount != 0 && paramCount != 1)
+ {
+ LogScriptError (prefix + "The message must have 0 or 1 parameters.\n" + postfix, errorContext);
+ return false;
+ }
+
+ MonoClass* monoObjectClass = mono_get_object_class ();
+
+ MonoImage* engineImage = manager.GetEngineImage();
+
+ if (paramCount == 1)
+ {
+ // Passed msg needs to have a parameter
+ if (msgHandler.MessageIDToParameter (messageIndex) == 0)
+ {
+ LogScriptError (prefix + "The message may not have any parameters.\n" + postfix, errorContext);
+ return false;
+ }
+
+ void* iterator = NULL;
+ MonoType* monoParameterType = mono_signature_get_params (signature, &iterator);
+ MonoClass* monoParameterClass = mono_class_from_mono_type (monoParameterType);
+ int typeType = mono_type_get_type (monoParameterType);
+ MessageIdentifier msg = msgHandler.MessageIDToMessageIdentifier (messageIndex);
+
+ const char* formattedMsgParameter = "";
+
+ // Specific c to mono remap
+ if (msg.scriptParameterName)
+ {
+ MonoClass* scriptParameterClass = mono_class_from_name(engineImage, "UnityEngine", msg.scriptParameterName);
+ AssertIf(scriptParameterClass == NULL);
+ if (monoParameterClass == scriptParameterClass && scriptParameterClass)
+ return true;
+ else
+ formattedMsgParameter = msg.scriptParameterName;
+ }
+
+ if (msg.parameterClassId != -1) // Check when derived from Object
+ {
+ // javascript style
+ if (monoParameterClass == monoObjectClass)
+ return true;
+
+ // Check custom data passing.
+ // Generalize this!
+ if (msg.parameterClassId == ClassID(Collision))
+ {
+ if (monoParameterClass == manager.GetCommonClasses().collision)
+ return true;
+ formattedMsgParameter = "Collision";
+ }
+ else if (msg.parameterClassId == ClassID(Collision2D))
+ {
+ if (monoParameterClass == manager.GetCommonClasses().collision2D)
+ return true;
+ formattedMsgParameter = "Collision2D";
+ }
+ else
+ {
+ // Allow anything that is a super class
+ if (monoParameterClass)
+ {
+ int monoParameterClassID = Scripting::GetClassIDFromScriptingClass (monoParameterClass);
+
+ if (monoParameterClassID != -1 && Object::IsDerivedFromClassID (msg.parameterClassId, monoParameterClassID))
+ return true;
+ }
+
+ // Format the error nicer
+ formattedMsgParameter = Object::ClassIDToString (msg.parameterClassId).c_str ();
+ }
+ }
+
+ // Check for MonoObject passed directly
+ ///@TODO: REMOVE THIS AND DO A PROPER SendMessage for the character controller
+ if (msg.parameterClassId == ClassID(MonoObject))
+ return true;
+
+ // Check built in types
+ if (typeType == MONO_TYPE_BOOLEAN && msg.parameterClassId == ClassID (bool))
+ return true;
+ if (typeType == MONO_TYPE_I4 && msg.parameterClassId == ClassID (int))
+ return true;
+ if (typeType == MONO_TYPE_R4 && msg.parameterClassId == ClassID (float))
+ return true;
+
+ // Should have already returned if everything was OK...
+ LogScriptError (prefix + "This message parameter has to be of type: " + formattedMsgParameter + "\n" + postfix, errorContext);
+ return false;
+ }
+ return true;
+}
+
+#endif//ENABLE_MONO
+
+ScriptingMethodPtr FindMethod (const MonoScriptCache& cache, const char* name)
+{
+ MonoScriptCache::MethodCache::const_iterator found = cache.methodCache.find (name);
+ if (found != cache.methodCache.end ())
+ return found->second;
+ else
+ return SCRIPTING_NULL;
+}
+
+
+static void PopulateMethods(MonoScriptCache& cache, MonoClass* klass, Object* errorContext)
+{
+ int messageCount = GameObject::GetMessageHandler ().GetMessageCount ();
+ cache.methods.resize_initialized (MonoScriptCache::kMethodCount + messageCount, SCRIPTING_NULL);
+
+ // Check the methods we support (Eg. Update, Render ...)
+ DebugAssertIf (kMethodNames[MonoScriptCache::kMethodCount] != NULL);
+ for (int i=0;i<MonoScriptCache::kMethodCount;i++)
+ {
+ DebugAssertIf(kMethodNames[i] == NULL);
+
+ ScriptingMethodPtr method = FindMethod (cache, kMethodNames[i]);
+#if ENABLE_MONO
+ MonoManager& manager = GetMonoManager();
+ const CommonScriptingClasses& common = manager.GetCommonClasses();
+
+ if (method)
+ {
+ int parameterCount = mono_signature_get_param_count (mono_method_signature (method->monoMethod));
+#if ENABLE_NETWORK
+ if (i == MonoScriptCache::kSerializeNetView)
+ {
+ if (parameterCount == 1)
+ {
+ if (!Check1MethodParameters (method, klass, common.bitStream, errorContext, manager))
+ method = NULL;
+ }
+ else
+ {
+ if (!Check2MethodParameters (method, klass, common.bitStream, common.networkMessageInfo, errorContext, manager))
+ method = NULL;
+ }
+ }
+ else if (i == MonoScriptCache::kNetworkInstantiate)
+ {
+ if (!Check1MethodParameters (method, klass, common.networkMessageInfo, errorContext, manager))
+ method = NULL;
+ }
+ else
+#endif
+ if (i == MonoScriptCache::kRenderImageFilter)
+ {
+ if (!Check2MethodParameters (method, klass, common.renderTexture, common.renderTexture, errorContext, manager))
+ method = NULL;
+ }
+ else if (i == MonoScriptCache::kAudioFilterRead)
+ {
+ if (!Check2MethodParameters (method, klass, common.floatSingleArray, common.int_32, errorContext, manager))
+ method = NULL;
+ }
+ else if (parameterCount != 0)
+ {
+ method = NULL;
+ const char* foundClass = mono_class_get_name (klass);
+ LogScriptError (string ("Script error (") + foundClass + "): " + kMethodNames[i] + "() can not take parameters.", errorContext);
+ }
+ else if (IsCoroutine (method->monoMethod, common))
+ {
+ if (i == MonoScriptCache::kStart || i == MonoScriptCache::kMain)
+ method = NULL;
+ else if (i != MonoScriptCache::kCoroutineStart && i != MonoScriptCache::kCoroutineMain)
+ {
+ method = NULL;
+ const char* foundClass = mono_class_get_name (klass);
+ LogScriptError (string ("Script error (") + foundClass + "): " + kMethodNames[i] + "() can not be a coroutine.", errorContext);
+ }
+ }
+ }
+#endif
+ cache.methods[i] = method;
+ }
+
+ // Check which messages we support
+ for (int i=0;i<messageCount;i++)
+ {
+ MessageHandler& msgHandler = GameObject::GetMessageHandler ();
+ if ((msgHandler.MessageIDToMessageIdentifier (i).options & MessageIdentifier::kSendToScripts) == 0)
+ continue;
+
+ const char* messageName = msgHandler.MessageIDToName (i);
+ ScriptingMethodPtr method = FindMethod (cache, messageName);
+
+#if ENABLE_MONO
+ if (method)
+ {
+ if (!CheckMessageParameters (method->monoMethod, i, errorContext, GetMonoManager()))
+ method = NULL;
+ }
+
+#endif
+ cache.methods[i + MonoScriptCache::kMethodCount] = method;
+ }
+
+}
+
+static void RegisterNetworkRPC(MonoScriptCache& cache, const CommonScriptingClasses& common)
+{
+#if ENABLE_NETWORK
+ if (gRegisterMonoRPC && common.RPC)
+ {
+ for (MethodCache::iterator m=cache.methodCache.begin();m != cache.methodCache.end();m++)
+ {
+ MonoMethod* meth = m->second->monoMethod;
+ MonoCustomAttrInfo* attr = mono_custom_attrs_from_method(meth);
+ if (attr != NULL)
+ {
+ if (mono_custom_attrs_has_attr (attr, common.RPC))
+ gRegisterMonoRPC (mono_method_get_name(meth));
+ mono_custom_attrs_free(attr);
+ }
+ }
+ }
+#endif
+}
+
+MonoScriptCache::MonoScriptCache ()
+{
+ scriptType = kScriptTypeNotInitialized;
+ klass = SCRIPTING_NULL;
+ className = NULL;
+ #if UNITY_EDITOR
+ scriptTypeWasJustCreatedFromComponentMenu = false;
+ runInEditMode = false;
+ #endif
+}
+
+
+MonoScriptCache::~MonoScriptCache ()
+{
+#if ENABLE_SCRIPTING
+ ClearMethodCache (methodCache);
+#endif
+}
+
+void MonoScriptCache::Release () const
+{
+ MonoScriptCache* cache = const_cast<MonoScriptCache*> (this);
+ if (cache->refCount.Release())
+ {
+ UNITY_DELETE(cache, kMemScriptManager);
+ }
+}
+
+void MonoScriptCache::Retain () const
+{
+ const_cast<AtomicRefCounter&> (refCount).Retain();
+}
+
+MonoScriptCache* CreateMonoScriptCache (ScriptingTypePtr klass, bool isEditorScript, Object* errorContext)
+{
+ MonoScriptCache* cache = UNITY_NEW (MonoScriptCache, kMemScriptManager);
+
+ // Class still needs to be assigned, even on things not derived from MonoBehaviour or ScriptableObject (e.g. for
+ // interfaces etc.). So that any GetComponent(string) calls will be able to find us.
+ cache->klass = klass;
+
+ if (klass == NULL)
+ {
+ cache->scriptType = kScriptTypeClassNotFound;
+ return cache;
+ }
+
+ MonoManager& manager = GetMonoManager();
+ const CommonScriptingClasses& common = manager.GetCommonClasses();
+ cache->className = scripting_class_get_name (klass);
+
+ #if ENABLE_MONO
+ if (mono_class_get_flags (klass) & MONO_TYPE_ATTR_ABSTRACT)
+ {
+ cache->scriptType = kScriptTypeClassIsAbstract;
+ return cache;
+ }
+
+ #endif
+
+ #if ENABLE_MONO && (!UNITY_PEPPER)
+ // @TODO: NACL should support mono_class_is_generic
+ if (mono_class_is_generic (klass) || mono_class_is_inflated (klass))
+ {
+ cache->scriptType = kScriptTypeClassIsGeneric;
+ return cache;
+ }
+ #endif
+
+
+ if (scripting_class_is_subclass_of(klass, common.monoBehaviour))
+ cache->scriptType = kScriptTypeMonoBehaviourDerived;
+ else if (scripting_class_is_subclass_of (klass, common.scriptableObject))
+ cache->scriptType = kScriptTypeScriptableObjectDerived;
+ else
+ {
+ cache->scriptType = kScriptTypeNothingDerived;
+ return cache;
+ }
+
+ #if UNITY_EDITOR
+ if (isEditorScript)
+ {
+ if (cache->scriptType == kScriptTypeScriptableObjectDerived)
+ {
+ cache->scriptType = kScriptTypeEditorScriptableObjectDerived;
+ }
+ else
+ {
+ cache->scriptType = kScriptTypeNothingDerived;
+ return cache;
+ }
+ }
+ #endif
+
+ ClearMethodCache (cache->methodCache);
+ BuildMethodCache (cache->methodCache, cache->klass, false);
+
+ PopulateMethods(*cache, cache->klass, errorContext);
+ RegisterNetworkRPC (*cache, common);
+
+ #if UNITY_EDITOR
+ // Is this an edit mode script?
+ MonoObject* monoScriptObject = mono_type_get_object(mono_domain_get(), mono_class_get_type(klass));
+ ScriptingInvocation invocation(common.checkIsEditMode);
+ invocation.AddObject(monoScriptObject);
+
+ cache->runInEditMode = MonoObjectToBool(invocation.Invoke()) || isEditorScript;
+ #endif
+
+ return cache;
+}
+
+bool IsValidScriptType(MonoScriptType type)
+{
+ return (type == kScriptTypeMonoBehaviourDerived ||
+ type == kScriptTypeEditorScriptableObjectDerived ||
+ type == kScriptTypeScriptableObjectDerived);
+}
+
+std::string FormatScriptTypeError(MonoScriptType type, const std::string& fileName)
+{
+ if (type == kScriptTypeClassNotFound)
+ return Format("The class defined in script file named '%s' does not match the file name!", fileName.c_str());
+ if (type == kScriptTypeNothingDerived)
+ return Format("The class defined in the script file named '%s' is not derived from MonoBehaviour or ScriptableObject!", fileName.c_str());
+ if (type == kScriptTypeClassIsAbstract)
+ return Format("The class in script file named '%s' is abstract. The script class can't be abstract!", fileName.c_str());
+ if (type == kScriptTypeClassIsInterface)
+ return Format("The class in script file named '%s' is an interface. The script can't be an interface!", fileName.c_str());
+ if (type == kScriptTypeClassIsGeneric)
+ return Format("The class in script file named '%s' is generic. Generic MonoBehaviours are not supported!", fileName.c_str());
+ if (type == kScriptTypeNotInitialized)
+ return Format("The class in script file named '%s' is not yet initialized!", fileName.c_str());
+ if (type == kScriptTypeScriptMissing)
+ return "The referenced script on this Behaviour is missing!";
+
+ return "";
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/Mono/MonoScriptCache.h b/Runtime/Mono/MonoScriptCache.h
new file mode 100644
index 0000000..04271ae
--- /dev/null
+++ b/Runtime/Mono/MonoScriptCache.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/Threads/AtomicRefCounter.h"
+#include "Runtime/Utilities/CStringHash.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "MonoScriptType.h"
+
+struct CommonScriptingClasses;
+
+
+// A refcounted constant method cache for the MonoBehaviour.
+// When creating the MonoScript it precomputes all available methods etc.
+// The data is shared in this class.
+// It is refcounted, in case the MonoSript is deleted before the MonoBehaviour is destroyed.
+
+struct MonoScriptCache
+{
+ typedef std::map<const char*, ScriptingMethodPtr, compare_cstring> MethodCache;
+
+ enum { kUpdate = 0, kLateUpdate, kFixedUpdate, kAwake, kStart, kMain, kRenderObject, kAddToManager, kRemoveFromManager, kRemoveFromManagerInternal, kCoroutineStart, kCoroutineMain, kRenderImageFilter, kDrawGizmos, kGUI, kValidateProperties, kSerializeNetView, kNetworkInstantiate, kOnDestroy, kAudioFilterRead, kMethodCount };
+
+ AtomicRefCounter refCount;
+ ScriptingClassPtr klass;
+ dynamic_array<ScriptingMethodPtr> methods;
+ MethodCache methodCache;
+ MonoScriptType scriptType;
+ const char* className;
+
+ #if UNITY_EDITOR
+ bool scriptTypeWasJustCreatedFromComponentMenu;
+ bool runInEditMode;
+ #endif
+
+
+ MonoScriptCache ();
+ ~MonoScriptCache ();
+
+ void Release () const;
+ void Retain () const;
+};
+
+ScriptingMethodPtr FindMethod (const MonoScriptCache& cache, const char* name);
+
+typedef void RegisterMonoRPCCallback (const char* name);
+void RegisterMonoRPC (RegisterMonoRPCCallback* callback);
+MonoScriptCache* CreateMonoScriptCache (ScriptingTypePtr klass, bool isEditorScript, Object* errorContext);
+
+bool IsValidScriptType(MonoScriptType type);
+std::string FormatScriptTypeError(MonoScriptType type, const std::string& fileName);
diff --git a/Runtime/Mono/MonoScriptManager.cpp b/Runtime/Mono/MonoScriptManager.cpp
new file mode 100644
index 0000000..4b1c3c6
--- /dev/null
+++ b/Runtime/Mono/MonoScriptManager.cpp
@@ -0,0 +1,150 @@
+#include "UnityPrefix.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Mono/MonoScriptManager.h"
+#include "Runtime/Threads/Thread.h"
+
+#if ENABLE_SCRIPTING
+template<typename T> MonoScript* FindScript(MonoScriptManager::Scripts& scripts, T& filter)
+{
+ MonoScriptManager::Scripts::iterator i, next;
+ for (i= scripts.begin ();i != scripts.end ();i=next)
+ {
+ next = i; next++;
+ MonoScript* script = *i;
+ if (script == NULL)
+ {
+ scripts.erase (i);
+ }
+ else if (filter.Match(script))
+ return script;
+ }
+ return NULL;
+}
+
+struct MatchScriptByNameFilter
+{
+ const char* name;
+ bool Match(MonoScript* ms) { return ms->GetScriptClassName() == name; }
+};
+
+struct MatchScriptByClassFilter
+{
+ ScriptingClassPtr klass;
+ bool Match(MonoScript* ms) { return ms->GetClass() == klass; }
+};
+
+struct MatchScriptByClassNamespaceAssemblyFilter
+{
+ // hold by const ref to minimize copying
+ std::string const& scriptClassName, nameSpace, assemblyName;
+
+ MatchScriptByClassNamespaceAssemblyFilter (std::string const& scriptName, std::string const& ns, std::string const& assembly)
+ : scriptClassName (scriptName), nameSpace (ns), assemblyName (assembly)
+ {}
+
+ bool Match(MonoScript* ms)
+ {
+ return ms->GetScriptClassName () == scriptClassName
+ && ms->GetNameSpace () == nameSpace
+ && ms->GetAssemblyName () == assemblyName;
+ }
+};
+
+static MonoScript* FindScript(ScriptingClassPtr klass, MonoScriptManager::Scripts& scripts)
+{
+ MatchScriptByClassFilter filter;
+ filter.klass = klass;
+ return FindScript(scripts,filter);
+}
+
+static void AddScriptsToList(MonoScriptManager::Scripts& scripts, MonoScriptManager::AllScripts& addtothis)
+{
+ MonoScriptManager::Scripts::iterator i, next;
+
+ for (i= scripts.begin ();i != scripts.end ();i=next)
+ {
+ next = i; next++;
+ MonoScript* script = *i;
+ if (script)
+ addtothis.insert (script);
+ else
+ {
+ scripts.erase (i);
+ }
+ }
+}
+
+MonoScriptManager::AllScripts MonoScriptManager::GetAllRuntimeScripts ()
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ AllScripts scripts;
+ AddScriptsToList(m_RuntimeScripts,scripts);
+ return scripts;
+}
+
+MonoScript* MonoScriptManager::FindRuntimeScript (const string& className)
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ MatchScriptByNameFilter filter;
+ filter.name = className.c_str();
+ return FindScript(m_RuntimeScripts,filter);
+}
+
+MonoScript* MonoScriptManager::FindRuntimeScript (const string& className, string const& nameSpace, string const& assembly)
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ MatchScriptByClassNamespaceAssemblyFilter filter (className, nameSpace, assembly);
+ return FindScript (m_RuntimeScripts, filter);
+}
+
+MonoScript* MonoScriptManager::FindRuntimeScript (ScriptingClassPtr klass)
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+ return FindScript(klass,m_RuntimeScripts);
+}
+
+void MonoScriptManager::RegisterRuntimeScript (MonoScript& script)
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+ m_RuntimeScripts.insert (&script);
+}
+
+#if UNITY_EDITOR
+MonoScript* MonoScriptManager::FindEditorScript (MonoClass* klass)
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+ return FindScript(klass,m_EditorScripts);
+}
+
+MonoScript* MonoScriptManager::FindEditorScript (const string& className)
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ MatchScriptByNameFilter filter;
+ filter.name = className.c_str();
+ return FindScript(m_EditorScripts,filter);
+}
+
+void MonoScriptManager::RegisterEditorScript (MonoScript& script)
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+ m_EditorScripts.insert (&script);
+}
+
+MonoScriptManager::AllScripts MonoScriptManager::GetAllRuntimeAndEditorScripts ()
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ AllScripts scripts;
+
+ AddScriptsToList(m_RuntimeScripts,scripts);
+ AddScriptsToList(m_EditorScripts,scripts);
+
+ return scripts;
+}
+
+#endif
+#endif
diff --git a/Runtime/Mono/MonoScriptManager.h b/Runtime/Mono/MonoScriptManager.h
new file mode 100644
index 0000000..fc05127
--- /dev/null
+++ b/Runtime/Mono/MonoScriptManager.h
@@ -0,0 +1,40 @@
+#ifndef _MONOSCRIPTMANAGER_H_
+#define _MONOSCRIPTMANAGER_H_
+
+class MonoScript;
+class MonoManager;
+
+class MonoScriptManager
+{
+public:
+ void RegisterRuntimeScript (MonoScript& script);
+ void RegisterEditorScript (MonoScript& script);
+
+ typedef UNITY_SET(kMemScriptManager, MonoScript*) AllScripts;
+ typedef UNITY_SET(kMemScriptManager, PPtr<MonoScript>) Scripts;
+
+ MonoScript* FindRuntimeScript (const std::string& className);
+ MonoScript* FindRuntimeScript (const std::string& className, std::string const& nameSpace, std::string const& assembly);
+ MonoScript* FindRuntimeScript (ScriptingClassPtr klass);
+ AllScripts GetAllRuntimeScripts ();
+
+#if UNITY_EDITOR
+ AllScripts GetAllRuntimeAndEditorScripts ();
+ MonoScript* FindEditorScript (MonoClass* klass);
+ MonoScript* FindEditorScript (const std::string& className);
+#else
+ MonoScript* FindEditorScript (ScriptingClassPtr klass) { return NULL; }
+#endif
+
+
+private:
+
+ Scripts m_RuntimeScripts;
+#if UNITY_EDITOR
+ Scripts m_EditorScripts;
+#endif
+
+ friend class MonoManager;
+};
+
+#endif
diff --git a/Runtime/Mono/MonoScriptType.h b/Runtime/Mono/MonoScriptType.h
new file mode 100644
index 0000000..eefe952
--- /dev/null
+++ b/Runtime/Mono/MonoScriptType.h
@@ -0,0 +1,19 @@
+#ifndef MONOSCRIPTTYPE_H
+#define MONOSCRIPTTYPE_H
+
+enum MonoScriptType
+{
+ kScriptTypeMonoBehaviourDerived = 0,
+ kScriptTypeScriptableObjectDerived = 1,
+ kScriptTypeEditorScriptableObjectDerived = 2,
+
+ kScriptTypeNotInitialized = -1,
+ kScriptTypeNothingDerived = -2,
+ kScriptTypeClassNotFound = -3,
+ kScriptTypeClassIsAbstract = -4,
+ kScriptTypeClassIsInterface = -5,
+ kScriptTypeClassIsGeneric = -6,
+ kScriptTypeScriptMissing = -7
+};
+
+#endif // !MONOSCRIPTTYPE_H
diff --git a/Runtime/Mono/MonoTypeSignatures.h b/Runtime/Mono/MonoTypeSignatures.h
new file mode 100644
index 0000000..d3a7149
--- /dev/null
+++ b/Runtime/Mono/MonoTypeSignatures.h
@@ -0,0 +1,170 @@
+/*
+ * blob.h: Definitions used to pull information out of the Blob
+ *
+ */
+#ifndef _MONO_METADATA_BLOB_H_
+#define _MONO_METADATA_BLOB_H_
+
+#define SIGNATURE_HAS_THIS 0x20
+#define SIGNATURE_EXPLICIT_THIS 0x40
+#define SIGNATURE_VARARG 0x05
+
+/*
+ * Encoding for type signatures used in the Metadata
+ */
+typedef enum {
+ MONO_TYPE_END = 0x00, /* End of List */
+ MONO_TYPE_VOID = 0x01,
+ MONO_TYPE_BOOLEAN = 0x02,
+ MONO_TYPE_CHAR = 0x03,
+ MONO_TYPE_I1 = 0x04,
+ MONO_TYPE_U1 = 0x05,
+ MONO_TYPE_I2 = 0x06,
+ MONO_TYPE_U2 = 0x07,
+ MONO_TYPE_I4 = 0x08,
+ MONO_TYPE_U4 = 0x09,
+ MONO_TYPE_I8 = 0x0a,
+ MONO_TYPE_U8 = 0x0b,
+ MONO_TYPE_R4 = 0x0c,
+ MONO_TYPE_R8 = 0x0d,
+ MONO_TYPE_STRING = 0x0e,
+ MONO_TYPE_PTR = 0x0f, /* arg: <type> token */
+ MONO_TYPE_BYREF = 0x10, /* arg: <type> token */
+ MONO_TYPE_VALUETYPE = 0x11, /* arg: <type> token */
+ MONO_TYPE_CLASS = 0x12, /* arg: <type> token */
+ MONO_TYPE_VAR = 0x13, /* number */
+ MONO_TYPE_ARRAY = 0x14, /* type, rank, boundsCount, bound1, loCount, lo1 */
+ MONO_TYPE_GENERICINST= 0x15, /* <type> <type-arg-count> <type-1> \x{2026} <type-n> */
+ MONO_TYPE_TYPEDBYREF = 0x16,
+ MONO_TYPE_I = 0x18,
+ MONO_TYPE_U = 0x19,
+ MONO_TYPE_FNPTR = 0x1b, /* arg: full method signature */
+ MONO_TYPE_OBJECT = 0x1c,
+ MONO_TYPE_SZARRAY = 0x1d, /* 0-based one-dim-array */
+ MONO_TYPE_MVAR = 0x1e, /* number */
+ MONO_TYPE_CMOD_REQD = 0x1f, /* arg: typedef or typeref token */
+ MONO_TYPE_CMOD_OPT = 0x20, /* optional arg: typedef or typref token */
+ MONO_TYPE_INTERNAL = 0x21, /* CLR internal type */
+
+ MONO_TYPE_MODIFIER = 0x40, /* Or with the following types */
+ MONO_TYPE_SENTINEL = 0x41, /* Sentinel for varargs method signature */
+ MONO_TYPE_PINNED = 0x45 /* Local var that points to pinned object */
+} MonoTypeEnum;
+
+typedef enum {
+ MONO_PROFILE_NONE = 0,
+ MONO_PROFILE_APPDOMAIN_EVENTS = 1 << 0,
+ MONO_PROFILE_ASSEMBLY_EVENTS = 1 << 1,
+ MONO_PROFILE_MODULE_EVENTS = 1 << 2,
+ MONO_PROFILE_CLASS_EVENTS = 1 << 3,
+ MONO_PROFILE_JIT_COMPILATION = 1 << 4,
+ MONO_PROFILE_INLINING = 1 << 5,
+ MONO_PROFILE_EXCEPTIONS = 1 << 6,
+ MONO_PROFILE_ALLOCATIONS = 1 << 7,
+ MONO_PROFILE_GC = 1 << 8,
+ MONO_PROFILE_THREADS = 1 << 9,
+ MONO_PROFILE_REMOTING = 1 << 10,
+ MONO_PROFILE_TRANSITIONS = 1 << 11,
+ MONO_PROFILE_ENTER_LEAVE = 1 << 12,
+ MONO_PROFILE_COVERAGE = 1 << 13,
+ MONO_PROFILE_INS_COVERAGE = 1 << 14,
+ MONO_PROFILE_STATISTICAL = 1 << 15
+} MonoProfileFlags;
+
+/*
+ * Type Attributes (23.1.15).
+ */
+enum {
+ MONO_TYPE_ATTR_VISIBILITY_MASK = 0x00000007,
+ MONO_TYPE_ATTR_NOT_PUBLIC = 0x00000000,
+ MONO_TYPE_ATTR_PUBLIC = 0x00000001,
+ MONO_TYPE_ATTR_NESTED_PUBLIC = 0x00000002,
+ MONO_TYPE_ATTR_NESTED_PRIVATE = 0x00000003,
+ MONO_TYPE_ATTR_NESTED_FAMILY = 0x00000004,
+ MONO_TYPE_ATTR_NESTED_ASSEMBLY = 0x00000005,
+ MONO_TYPE_ATTR_NESTED_FAM_AND_ASSEM = 0x00000006,
+ MONO_TYPE_ATTR_NESTED_FAM_OR_ASSEM = 0x00000007,
+
+ MONO_TYPE_ATTR_LAYOUT_MASK = 0x00000018,
+ MONO_TYPE_ATTR_AUTO_LAYOUT = 0x00000000,
+ MONO_TYPE_ATTR_SEQUENTIAL_LAYOUT = 0x00000008,
+ MONO_TYPE_ATTR_EXPLICIT_LAYOUT = 0x00000010,
+
+ MONO_TYPE_ATTR_CLASS_SEMANTIC_MASK = 0x00000020,
+ MONO_TYPE_ATTR_CLASS = 0x00000000,
+ MONO_TYPE_ATTR_INTERFACE = 0x00000020,
+
+ MONO_TYPE_ATTR_ABSTRACT = 0x00000080,
+ MONO_TYPE_ATTR_SEALED = 0x00000100,
+ MONO_TYPE_ATTR_SPECIAL_NAME = 0x00000400,
+
+ MONO_TYPE_ATTR_IMPORT = 0x00001000,
+ MONO_TYPE_ATTR_SERIALIZABLE = 0x00002000,
+
+ MONO_TYPE_ATTR_STRING_FORMAT_MASK = 0x00030000,
+ MONO_TYPE_ATTR_ANSI_CLASS = 0x00000000,
+ MONO_TYPE_ATTR_UNICODE_CLASS = 0x00010000,
+ MONO_TYPE_ATTR_AUTO_CLASS = 0x00020000,
+ MONO_TYPE_ATTR_CUSTOM_CLASS = 0x00030000,
+ MONO_TYPE_ATTR_CUSTOM_MASK = 0x00c00000,
+
+ MONO_TYPE_ATTR_BEFORE_FIELD_INIT = 0x00100000,
+ MONO_TYPE_ATTR_FORWARDER = 0x00200000,
+
+ MONO_TYPE_ATTR_RESERVED_MASK = 0x00040800,
+ MONO_TYPE_ATTR_RT_SPECIAL_NAME = 0x00000800,
+ MONO_TYPE_ATTR_HAS_SECURITY = 0x00040000
+};
+
+
+/*
+ * Method Attributes (22.1.9)
+ */
+enum {
+ METHOD_IMPL_ATTRIBUTE_CODE_TYPE_MASK = 0x0003,
+ METHOD_IMPL_ATTRIBUTE_IL = 0x0000,
+ METHOD_IMPL_ATTRIBUTE_NATIVE = 0x0001,
+ METHOD_IMPL_ATTRIBUTE_OPTIL = 0x0002,
+ METHOD_IMPL_ATTRIBUTE_RUNTIME = 0x0003,
+
+ METHOD_IMPL_ATTRIBUTE_MANAGED_MASK = 0x0004,
+ METHOD_IMPL_ATTRIBUTE_UNMANAGED = 0x0004,
+ METHOD_IMPL_ATTRIBUTE_MANAGED = 0x0000,
+
+ METHOD_IMPL_ATTRIBUTE_FORWARD_REF = 0x0010,
+ METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG = 0x0080,
+ METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL = 0x1000,
+ METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED = 0x0020,
+ METHOD_IMPL_ATTRIBUTE_NOINLINING = 0x0008,
+ METHOD_IMPL_ATTRIBUTE_MAX_METHOD_IMPL_VAL = 0xffff,
+
+ METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK = 0x0007,
+ METHOD_ATTRIBUTE_COMPILER_CONTROLLED = 0x0000,
+ METHOD_ATTRIBUTE_PRIVATE = 0x0001,
+ METHOD_ATTRIBUTE_FAM_AND_ASSEM = 0x0002,
+ METHOD_ATTRIBUTE_ASSEM = 0x0003,
+ METHOD_ATTRIBUTE_FAMILY = 0x0004,
+ METHOD_ATTRIBUTE_FAM_OR_ASSEM = 0x0005,
+ METHOD_ATTRIBUTE_PUBLIC = 0x0006,
+
+ METHOD_ATTRIBUTE_STATIC = 0x0010,
+ METHOD_ATTRIBUTE_FINAL = 0x0020,
+ METHOD_ATTRIBUTE_VIRTUAL = 0x0040,
+ METHOD_ATTRIBUTE_HIDE_BY_SIG = 0x0080,
+
+ METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK = 0x0100,
+ METHOD_ATTRIBUTE_REUSE_SLOT = 0x0000,
+ METHOD_ATTRIBUTE_NEW_SLOT = 0x0100,
+
+ METHOD_ATTRIBUTE_ABSTRACT = 0x0400,
+ METHOD_ATTRIBUTE_SPECIAL_NAME = 0x0800,
+
+ METHOD_ATTRIBUTE_PINVOKE_IMPL = 0x2000,
+ METHOD_ATTRIBUTE_UNMANAGED_EXPORT = 0x0008,
+};
+
+
+
+inline bool IsMonoBuiltinType (int type) { return type >= MONO_TYPE_BOOLEAN && type <= MONO_TYPE_R8; }
+
+#endif
diff --git a/Runtime/Mono/MonoTypes.h b/Runtime/Mono/MonoTypes.h
new file mode 100644
index 0000000..9fa5b87
--- /dev/null
+++ b/Runtime/Mono/MonoTypes.h
@@ -0,0 +1,128 @@
+#ifndef MONOTYPES_H
+#define MONOTYPES_H
+
+//TODO use mono headers directly, so we don't get burned when the struct definitions in this file
+//go out of sync with mono's.
+//this is not done yet, because it's tricky, as the mono headers define symbols that we also define in UnityFunctions.h,
+//so we'd need to find some way to either remove those defines from the mono headers, or somehow mangle them.
+#if ENABLE_MONO
+
+struct MonoException;
+struct MonoAssembly;
+struct MonoObject;
+struct MonoClassField;
+struct MonoClass;
+struct MonoDomain;
+struct MonoImage;
+struct MonoType;
+struct MonoMethodSignature;
+struct MonoArray;
+struct MonoThread;
+struct MonoVTable;
+struct MonoProperty;
+struct MonoReflectionAssembly;
+struct MonoReflectionMethod;
+struct MonoAppDomain;
+struct MonoCustomAttrInfo;
+struct MonoDl;
+#if UNITY_STANDALONE || UNITY_EDITOR
+struct MonoDlFallbackHandler;
+#endif
+
+#if UNITY_EDITOR
+struct MonoMethodDesc;
+#endif
+
+typedef const void* gconstpointer;
+typedef void* gpointer;
+typedef int gboolean;
+typedef unsigned int guint32;
+typedef int gint32;
+typedef unsigned long gulong;
+#if UNITY_WII
+ typedef signed long long gint64;
+#else
+ typedef long gint64;
+#endif
+typedef unsigned char guchar;
+typedef UInt16 gunichar2;
+struct MonoString
+{
+ void* monoObjectPart1;
+ void* monoObjectPart2;
+ gint32 length;
+ gunichar2 firstCharacter;
+};
+
+struct MonoMethod {
+ UInt16 flags;
+ UInt16 iflags;
+};
+
+struct GPtrArray {
+ gpointer *pdata;
+ guint32 len;
+};
+
+typedef enum
+{
+ MONO_VERIFIER_MODE_OFF,
+ MONO_VERIFIER_MODE_VALID,
+ MONO_VERIFIER_MODE_VERIFIABLE,
+ MONO_VERIFIER_MODE_STRICT
+} MiniVerifierMode;
+
+typedef enum {
+ MONO_SECURITY_MODE_NONE,
+ MONO_SECURITY_MODE_CORE_CLR,
+ MONO_SECURITY_MODE_CAS,
+ MONO_SECURITY_MODE_SMCS_HACK
+} MonoSecurityMode;
+
+typedef enum {
+ MONO_TYPE_NAME_FORMAT_IL,
+ MONO_TYPE_NAME_FORMAT_REFLECTION,
+ MONO_TYPE_NAME_FORMAT_FULL_NAME,
+ MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED
+} MonoTypeNameFormat;
+
+typedef struct {
+ const char *name;
+ const char *culture;
+ const char *hash_value;
+ const UInt8* public_key;
+ // string of 16 hex chars + 1 NULL
+ guchar public_key_token [17];
+ guint32 hash_alg;
+ guint32 hash_len;
+ guint32 flags;
+ UInt16 major, minor, build, revision;
+#if MONO_2_10 || MONO_2_12
+ UInt16 arch;
+#endif
+} MonoAssemblyName;
+
+typedef void GFuncRef (void*, void*);
+typedef GFuncRef* GFunc;
+
+typedef enum {
+ MONO_UNHANDLED_POLICY_LEGACY,
+ MONO_UNHANDLED_POLICY_CURRENT
+} MonoRuntimeUnhandledExceptionPolicy;
+
+typedef enum {
+ MONO_DL_LAZY = 1,
+ MONO_DL_LOCAL = 2,
+ MONO_DL_MASK = 3
+} MonoDynamicLibraryFlag;
+
+#if MONO_2_12
+typedef enum {
+ MONO_SECURITY_CORE_CLR_OPTIONS_DEFAULT = 0,
+ MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_REFLECTION = 1,
+ MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_DELEGATE = 2
+} MonoSecurityCoreCLROptions;
+#endif
+
+#endif //ENABLE_MONO
+#endif
diff --git a/Runtime/Mono/MonoUtility.cpp b/Runtime/Mono/MonoUtility.cpp
new file mode 100644
index 0000000..1e0a4e0
--- /dev/null
+++ b/Runtime/Mono/MonoUtility.cpp
@@ -0,0 +1,685 @@
+#include "UnityPrefix.h"
+#include "MonoIncludes.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/BaseClasses/RefCounted.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Scripting.h"
+
+#if UNITY_EDITOR
+static UNITY_TLS_VALUE(void*) gStackLimit;
+
+static void* GetStackLimit()
+{
+#if UNITY_WIN
+ MEMORY_BASIC_INFORMATION mbi;
+ VirtualQuery(&mbi, &mbi, sizeof(mbi));
+
+ return mbi.AllocationBase;
+#elif UNITY_OSX
+ pthread_t self = pthread_self();
+ void* addr = pthread_get_stackaddr_np(self);
+ size_t size = pthread_get_stacksize_np(self);
+ return (void*)((char*)addr - (char*)size);
+#elif UNITY_LINUX
+ pthread_attr_t attr;
+ size_t stacksize = 0;
+ void *stackaddr = NULL;
+ pthread_t self = pthread_self ();
+ int ret = pthread_getattr_np (self, &attr);
+
+ if (ret != 0)
+ {
+ printf_console ("pthread_getattr_np ret=%d\n", ret);
+ return 0;
+ }
+
+ ret = pthread_attr_getstack (&attr, &stackaddr, &stacksize);
+
+ if (ret != 0)
+ {
+ printf_console ("pthread_attr_getstack ret=%d\n", ret);
+ return 0;
+ }
+
+ return (void*)((char*)stackaddr - (char*)stacksize);
+#else
+#error Platform does not have stack checking implemented.
+#endif
+}
+
+#define REQUIRED_SCRIPTING_STACK_SIZE (16*1024)
+
+
+// Functions to check whether we have REQUIRED_SCRIPTING_STACK_SIZE (64K) of stack space available
+// before calling into native code. This ensures a StackOverflowException will *not* occur in mono runtime
+// or engine code.
+bool IsStackLargeEnough ()
+{
+ // Note, we assume stack grows DOWN
+ void* stackLimit = gStackLimit;
+ if (stackLimit == NULL)
+ gStackLimit = stackLimit = GetStackLimit();
+
+ if (((char*)&stackLimit-(char*)stackLimit) < REQUIRED_SCRIPTING_STACK_SIZE)
+ return false;
+ else
+ return true;
+}
+
+void AssertStackLargeEnough ()
+{
+ if (!IsStackLargeEnough ())
+ {
+ Scripting::RaiseManagedException ("System", "StackOverflowException", "");
+ }
+}
+
+#endif
+
+std::string ErrorMessageForUnsupportedEnumField(MonoType* enumType, MonoType* classType, const char * fieldName)
+{
+ char* enumTypeName = mono_type_get_name (enumType);
+ char* classTypeName = mono_type_get_name (classType);
+
+ std::string message = Format("Unsupported enum type '%s' used for field '%s' in class '%s'",
+ enumTypeName,
+ fieldName,
+ classTypeName);
+
+ g_free(enumTypeName);
+ g_free(classTypeName);
+
+ return message;
+}
+
+#if MONO_QUALITY_ERRORS
+MonoObject* MonoObjectNULL (ScriptingClassPtr klass, ScriptingStringPtr error)
+{
+ AssertMsg (klass, "NULL scripting class!");
+ if (NULL == klass)
+ return NULL;
+
+ if (mono_class_is_subclass_of (klass, GetScriptingManager ().GetCommonClasses ().monoBehaviour, 0))
+ return NULL;
+ if (mono_class_is_subclass_of (klass, GetScriptingManager ().GetCommonClasses ().scriptableObject, 0))
+ return NULL;
+
+ if (!mono_class_is_subclass_of (klass, GetScriptingManager ().GetCommonClasses ().unityEngineObject, 0))
+ return NULL;
+
+ if (mono_unity_class_is_abstract (klass) || mono_unity_class_is_interface (klass))
+ return NULL;
+
+ ScriptingObjectPtr scriptingobject = mono_object_new (mono_domain_get (), klass);
+ if (scriptingobject == NULL)
+ return NULL;
+
+ ScriptingObjectOfType<Object> object (scriptingobject);
+ object.SetInstanceID (0);
+
+ if (error != NULL)
+ object.SetError (error);
+
+ return scriptingobject;
+}
+
+MonoObject* MonoObjectNULL (int classID, MonoString* error)
+{
+ AssertIf (classID == -1);
+ if (classID == ClassID (MonoBehaviour))
+ return NULL;
+
+ ScriptingObjectPtr scriptingobject = Scripting::InstantiateScriptingWrapperForClassID(classID);
+ if (scriptingobject == NULL)
+ return NULL;
+
+ ScriptingObjectOfType<Object> object(scriptingobject);
+ object.SetInstanceID(0);
+
+ if (error != NULL)
+ object.SetError(error);
+
+ return scriptingobject;
+}
+
+MonoString* MissingComponentString (GameObject& go, const char* klassName)
+{
+ return MonoStringFormat(
+ "MissingComponentException:There is no '%s' attached to the \"%s\" game object, but a script is trying to access it.\n"
+ "You probably need to add a %s to the game object \"%s\". Or your script needs to check if the component is attached before using it.",
+ klassName, go.GetName(), klassName, go.GetName());
+}
+
+MonoString* MissingComponentString (GameObject& go, int classID)
+{
+ const string& className = Object::ClassIDToString(classID);
+ return MissingComponentString(go,className.c_str());
+}
+
+MonoString* MissingComponentString (GameObject& go, ScriptingTypePtr klass)
+{
+ return MissingComponentString(go,scripting_class_get_name(klass));
+}
+
+#endif
+
+int mono_array_length (MonoArray* array)
+{
+ char* raw = sizeof(uintptr_t)*3 + (char*)array;
+ return *reinterpret_cast<uintptr_t*> (raw);
+}
+
+int mono_array_length_safe (MonoArray* array)
+{
+ if (array)
+ {
+ char* raw = sizeof(uintptr_t)*3 + (char*)array;
+ return *reinterpret_cast<uintptr_t*> (raw);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+ScriptingClassPtr GetBuiltinScriptingClass(const char* name,bool optional)
+{
+ return GetMonoManager().GetBuiltinMonoClass(name,optional);
+}
+
+
+#if USE_MONO_AOT && !(UNITY_XENON || UNITY_PS3)
+
+// Flag defined in mono, when AOT libraries are built with -ficall option
+// But that is not available in mono/consoles
+extern "C" int mono_ficall_flag;
+
+void* ResolveMonoMethodPointer(MonoDomain* domain, MonoMethod* method)
+{
+ return mono_ficall_flag && method ? mono_aot_get_method(domain, method) : NULL;
+}
+#else
+void* ResolveMonoMethodPointer(MonoDomain* domain, MonoMethod* method)
+{
+ return NULL;
+}
+#endif
+
+void mono_runtime_object_init_exception (MonoObject *thiss, MonoException** exception)
+{
+ MonoClass *klass = mono_object_get_class (thiss);
+
+ MonoMethod *method;
+ void* iter = NULL;
+ while ((method = mono_class_get_methods (klass, &iter))) {
+ MonoMethodSignature *signature = mono_method_signature (method);
+ if (!signature) {
+ ErrorString (Format ("Error looking up signature for method %s.%s", mono_class_get_name (klass), mono_method_get_name (method)));
+ continue;
+ }
+ int paramCount = mono_signature_get_param_count (signature);
+ if (!strcmp (".ctor", mono_method_get_name (method)) && signature && paramCount == 0)
+ break;
+ }
+
+ if (method)
+ {
+ AssertIf (mono_class_is_valuetype (mono_method_get_class (method)));
+ mono_runtime_invoke_profiled (method, thiss, NULL, exception);
+ }
+ else
+ {
+ *exception = NULL;
+ }
+}
+
+void mono_runtime_object_init_log_exception (MonoObject *thiss)
+{
+ if (!thiss)
+ return;
+ MonoException* exc = NULL;
+ mono_runtime_object_init_exception(thiss, &exc);
+ if (exc)
+ ::Scripting::LogException(exc, 0);
+}
+
+/*
+mono_enumerator_next (MonoObject* enumerable, gconstpointer pointer)
+{
+
+}*/
+
+bool IsUtf16InAsciiRange( gunichar2 const* str, int length )
+{
+ gunichar2 const* strEnd = str + length;
+ while( str != strEnd )
+ { //length-- ) {
+ if( (*str & ~((gunichar2)0x7f)) != 0 )
+ return false;
+ ++str;
+ }
+ return true;
+}
+
+bool FastTestAndConvertUtf16ToAscii( char* destination, gunichar2 const* str, int length )
+{
+ gunichar2 const* strEnd = str + length;
+ while( str != strEnd ) { //length-- ) {
+ if( (*str & ~((gunichar2)0x7f)) != 0 )
+ return false;
+ *destination = (char)*str;
+ ++destination;
+ ++str;
+ }
+ return true;
+}
+
+// converts symbols in the range 0x00-0x7f from unicode16 to ascii (excluding the terminating 0 character)
+void fastUtf16ToAscii( char* destination, gunichar2 const* str, int length )
+{
+ gunichar2 const* strEnd = str + length;
+ while( str != strEnd ) {
+ *destination = (char)*str;
+ ++destination;
+ ++str;
+ }
+}
+
+#if UNITY_WIN || UNITY_XENON
+std::wstring MonoStringToWideCpp (MonoString* monoString)
+{
+ if (monoString)
+ {
+ wchar_t* buf = (wchar_t*)mono_string_to_utf16(monoString);
+ std::wstring temp (buf);
+ g_free (buf);
+ return temp;
+ }
+ else
+ return std::wstring ();
+}
+#endif
+
+std::string MonoStringToCpp (MonoString* monoString)
+{
+ if (!monoString)
+ return string ();
+
+ char buff[256];
+ if(monoString->length <= 256 && FastTestAndConvertUtf16ToAscii (buff,mono_string_chars(monoString), mono_string_length(monoString)) )
+ return string((char const*)buff,mono_string_length (monoString));
+
+ char* buf = mono_string_to_utf8 (monoString);
+ string temp (buf);
+ g_free (buf);
+ return temp;
+}
+
+MonoArray *mono_array_new_2d (int size0, int size1, MonoClass *klass) {
+ guint32 sizes[] = {size0, size1};
+ MonoClass* ac = mono_array_class_get (klass, 2);
+
+ return mono_array_new_full(mono_domain_get (), ac, sizes, NULL);
+}
+
+MonoArray *mono_array_new_3d (int size0, int size1, int size2, MonoClass *klass) {
+ guint32 sizes[] = {size0, size1, size2};
+ MonoClass* ac = mono_array_class_get (klass, 3);
+
+ return mono_array_new_full(mono_domain_get (), ac, sizes, NULL);
+}
+
+std::string MonoStringToCppChecked (MonoObject* monoString)
+{
+ if (monoString && mono_type_get_type(mono_class_get_type(mono_object_get_class(monoString))) == MONO_TYPE_STRING)
+ {
+ char* buf = mono_string_to_utf8 ((MonoString*)monoString);
+ string temp (buf);
+ g_free (buf);
+ return temp;
+ }
+ else
+ return string ();
+}
+
+inline bool ExtractLineAndPath (const string& exception, string::size_type& pathBegin, int& line, string& path)
+{
+ // Extract line and path from exception ...
+ // Format is: in [0x00031] (at /Users/.../filename.cs:51)
+
+ pathBegin = exception.find ("(at ", pathBegin);
+
+ if (pathBegin != string::npos)
+ {
+ pathBegin += 4;
+
+ // On Windows, there's a ':' right at the beginning as part of drive
+ #if UNITY_WIN && UNITY_EDITOR
+ std::string::size_type pathEnd = exception.find (':', exception.size() > pathBegin+2 ? pathBegin+2 : pathBegin);
+ #else
+ std::string::size_type pathEnd = exception.find (':', pathBegin);
+ #endif
+ if (pathEnd != string::npos)
+ {
+ path.assign (exception.begin () + pathBegin, exception.begin () + pathEnd);
+ line = atoi (exception.c_str () + pathEnd + 1);
+ pathBegin = pathEnd;
+ ConvertSeparatorsToUnity(path);
+ return true;
+ }
+ }
+ return false;
+}
+
+inline bool IsScriptAssetPath (const string& path)
+{
+ const string& projectDir = File::GetCurrentDirectory ();
+ // C# returns absolute path names
+ if (path.find (projectDir) == 0)
+ return true;
+ // Boo returns relative path names
+ if (!IsAbsoluteFilePath(path) )
+ return true;
+ return false;
+}
+
+bool ExceptionToLineAndPath (const string& stackTrace, int& line, string& path)
+{
+ // Extract line and path from exception...
+ // We want the topmost exception function trace that is in the project folder.
+ // If there is nothing in the project folder we return the topmost.
+ // Format is: in [0x00031] (at /Users/.../filename.cs:51)
+ string::size_type searchStart = 0;
+
+ if (ExtractLineAndPath (stackTrace, searchStart, line, path) && !IsScriptAssetPath (path))
+ {
+ string tempPath;
+ int tempLine;
+ while (ExtractLineAndPath (stackTrace, searchStart, tempLine, tempPath))
+ {
+ if (!IsAbsoluteFilePath(tempPath))
+ {
+ path = tempPath;
+ line = tempLine;
+ break;
+ }
+ }
+ return true;
+ }
+ else
+ return false;
+}
+
+
+string SimpleGetExceptionString(MonoException* exception)
+{
+ MonoClass* klass = mono_object_get_class((MonoObject*)exception);
+ if (!klass)
+ return "";
+
+ MonoMethod* toString = mono_class_get_method_from_name(mono_get_exception_class(), "ToString", 0);
+ if (!toString)
+ return "";
+
+ MonoString* exceptionString = (MonoString*)mono_runtime_invoke_profiled(toString, (MonoObject*)exception, NULL, NULL);
+ if (!exceptionString)
+ return "";
+
+ return mono_string_to_utf8(exceptionString);
+}
+
+MonoString* MonoStringNew (const std::string& in)
+{
+ return MonoStringNew (in.c_str ());
+}
+
+MonoString* MonoStringNew (const char* in)
+{
+ Assert (in != NULL);
+ MonoString* mono = mono_string_new_wrapper (in);
+ if (mono != NULL)
+ return mono;
+ else
+ {
+ // This can happen when conversion fails eg. converting utf8 to ascii or something i guess.
+ mono = mono_string_new_wrapper ("");
+ Assert (mono != NULL);
+ return mono;
+ }
+}
+
+MonoString* MonoStringNewUTF16 (const wchar_t* in)
+{
+ Assert (in != NULL);
+ MonoString* mono = mono_string_from_utf16 ( (const gunichar2*)in );
+ if (mono != NULL)
+ return mono;
+ else
+ {
+ // See MonoStringNew
+ mono = mono_string_new_wrapper ("");
+ Assert (mono != NULL);
+ return mono;
+ }
+}
+
+MonoString* MonoStringNewLength (const char* in, int length)
+{
+ Assert (in != NULL);
+ Assert (length >= 0);
+ MonoDomain* domain = mono_domain_get ();
+ Assert (domain != NULL);
+ MonoString* mono = mono_string_new_len (domain, in, length);
+ if (mono != NULL)
+ return mono;
+ else
+ {
+ // This can happen when conversion fails eg. converting utf8 to ascii or something i guess.
+ mono = mono_string_new_wrapper ("");
+ Assert (mono != NULL);
+ return mono;
+ }
+}
+
+bool MonoSetObjectField(MonoObject* target, const char* fieldname, MonoObject* value)
+{
+ MonoClass* klass = mono_object_get_class(target);
+ MonoClassField* field = mono_class_get_field_from_name(klass,fieldname);
+ if (!field) return false;
+ mono_field_set_value(target,field,value);
+ return true;
+}
+
+bool MonoObjectToBool (MonoObject* value)
+{
+ if (value && mono_type_get_type (mono_class_get_type (mono_object_get_class (value))) == MONO_TYPE_BOOLEAN)
+ return ExtractMonoObjectData<char> (value);
+ else
+ return false;
+}
+
+int MonoObjectToInt (MonoObject* value)
+{
+ if (value && mono_type_get_type (mono_class_get_type (mono_object_get_class (value))) == MONO_TYPE_I4)
+ return ExtractMonoObjectData<int> (value);
+ else
+ return -1;
+}
+
+MonoAssembly* mono_load_assembly_from_any_monopath(const char* assemblyname)
+{
+ MonoDomain* domain = mono_domain_get();
+ std::vector<string>& monoPaths = MonoPathContainer::GetMonoPaths();
+ for (int i=0; i!=monoPaths.size(); i++)
+ {
+ MonoAssembly* ass = mono_domain_assembly_open(domain, AppendPathName(monoPaths[i],assemblyname).c_str());
+ if (ass) return ass;
+ }
+ return NULL;
+}
+
+MonoMethod* mono_unity_find_method(const char* assemblyname, const char* ns, const char* klass, const char* methodname)
+{
+//todo: be less stupid about always trying to load an assembly that will be already loaded 99% of the time
+ MonoAssembly* ass = mono_load_assembly_from_any_monopath(assemblyname);
+ if (!ass) return NULL;
+ MonoImage* img = mono_assembly_get_image(ass);
+ if (!img) return NULL;
+ MonoMethod* method = FindStaticMonoMethod(img,klass,ns,methodname);
+ if (!method) return NULL;
+ return method;
+}
+
+MonoMethod* mono_reflection_method_get_method (MonoObject* ass)
+{
+ return ExtractMonoObjectData<MonoMethod*>(ass);
+}
+
+MonoString* MonoStringFormat (const char* format, ...)
+{
+ using namespace std;
+ va_list vl;
+ va_start( vl, format );
+ char buffer[1024 * 5];
+ vsnprintf (buffer, 1024 * 5, format, vl);
+ va_end (vl);
+ return mono_string_new_wrapper(buffer);
+}
+
+void StringMonoArrayToVector (MonoArray* arr, std::vector<UnityStr>& container)
+{
+ container.resize(mono_array_length_safe(arr));
+ for (int i=0;i<container.size();i++)
+ {
+ container[i] = MonoStringToCpp(GetMonoArrayElement<MonoString*> (arr, i));
+ }
+}
+
+void StringMonoArrayToVector (MonoArray* arr, std::vector<std::string>& container)
+{
+ container.resize(mono_array_length_safe(arr));
+ for (int i=0;i<container.size();i++)
+ {
+ container[i] = MonoStringToCpp(GetMonoArrayElement<MonoString*> (arr, i));
+ }
+}
+
+
+void SetReferenceDataOnScriptingWrapper(MonoObject* wrapper, const UnityEngineObjectMemoryLayout& data)
+{
+ UnityEngineObjectMemoryLayout* wrapperdata = reinterpret_cast<UnityEngineObjectMemoryLayout*> (((char*)wrapper) + kMonoObjectOffset);
+ memcpy(wrapperdata,&data,sizeof(UnityEngineObjectMemoryLayout));
+}
+
+MonoObject* mono_class_get_object (MonoClass* klass)
+{
+ if (klass == NULL)
+ return NULL;
+
+ MonoType* type = mono_class_get_type(klass);
+ if (type)
+ return mono_type_get_object (mono_domain_get(), type);
+ else
+ return NULL;
+}
+
+MonoClass* mono_type_get_class_or_element_class (MonoType* type)
+{
+#if MONO_2_12
+ MonoClass* klass = mono_class_from_mono_type (type);
+ if (mono_class_get_rank (klass) > 0)
+ {
+ klass = mono_class_get_element_class (klass);
+ }
+
+ return klass;
+#else
+ return mono_type_get_class (type);
+#endif
+}
+
+int mono_array_length_safe_wrapper(MonoArray* array)
+{
+ return mono_array_length_safe(array);
+}
+
+#if MONO_QUALITY_ERRORS
+MonoString* UnassignedReferenceString (MonoObject* instance, int classID, MonoClassField* field, int instanceID)
+{
+ MonoClass* klass = NULL;
+ if (instance == NULL)
+ return NULL;
+ // Transfer sometimes provides us with non-object derived instances so we simply ignore those
+ klass = mono_object_get_class(instance);
+ if (!mono_class_is_subclass_of(klass, GetMonoManager().GetCommonClasses().unityEngineObject, false))
+ return NULL;
+
+ const char* fieldName = mono_field_get_name(field);
+ const char* klassName = mono_class_get_name(mono_object_get_class(instance));
+
+ if (instanceID == 0)
+ {
+ return MonoStringFormat(
+ "UnassignedReferenceException:The variable %s of '%s' has not been assigned.\n"
+ "You probably need to assign the %s variable of the %s script in the inspector.",
+ fieldName, klassName, fieldName, klassName);
+ }
+ else
+ {
+ return MonoStringFormat(
+ "MissingReferenceException:The variable %s of '%s' doesn't exist anymore.\n"
+ "You probably need to reassign the %s variable of the '%s' script in the inspector.",
+ fieldName, klassName, fieldName, klassName);
+ }
+}
+#endif
+
+MonoClassField* GetMonoArrayFieldFromList (int type, MonoType* monoType, MonoClassField* field)
+{
+ if (type != MONO_TYPE_GENERICINST)
+ return NULL;
+
+ MonoClass* elementClass = mono_class_from_mono_type(monoType);
+
+ // Check that we have a Generic List class
+ const char* className = mono_class_get_name(elementClass);
+ if (strcmp(className, "List`1") != 0 || mono_class_get_image(elementClass) != mono_get_corlib())
+ return NULL;
+
+ MonoClassField *arrayField;
+ void* iter_list = NULL;
+
+ // List<> first element is something called Default Capacity
+ // Second is the actual array
+ // But, Mono 2.12 reordered the fields
+#if !MONO_2_12
+ mono_class_get_fields (elementClass, &iter_list);
+#endif
+ arrayField = mono_class_get_fields (elementClass, &iter_list);
+
+#if !UNITY_RELEASE
+ AssertIf(strcmp(mono_field_get_name(arrayField), "_items") != 0);
+ AssertIf(mono_field_get_offset(arrayField) != kMonoObjectOffset);
+
+ MonoClassField* sizeField = mono_class_get_fields (elementClass, &iter_list);
+ AssertIf(strcmp(mono_field_get_name(sizeField), "_size") != 0);
+ AssertIf(mono_field_get_offset(sizeField) != kMonoObjectOffset + sizeof(intptr_t));
+#endif
+
+ return arrayField;
+}
+
+static int currentDomainId = 0;
+
+int MonoDomainGetUniqueId()
+{
+ return currentDomainId;
+}
+
+void MonoDomainIncrementUniqueId()
+{
+ currentDomainId++;
+}
diff --git a/Runtime/Mono/MonoUtility.h b/Runtime/Mono/MonoUtility.h
new file mode 100644
index 0000000..147d1ec
--- /dev/null
+++ b/Runtime/Mono/MonoUtility.h
@@ -0,0 +1,556 @@
+#ifndef MONOUTILITY_H
+#define MONOUTILITY_H
+
+#if !defined(SCRIPTINGUTILITY_H)
+#error "Don't include MonoUtility.h, include ScriptingUtility.h instead"
+#endif
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+
+//Unity messes around deep in mono internals, bypassing mono's API to do certain things. One of those things is we have c++
+//structs that we assume are identical to c# structs, and when a mono method calls into the unity runtime, we make the assumption
+//that we can access the data stored in the c# struct directly. When a MonoObject* gets passed to us by mono, and we know that the
+//object it represents is a struct, we take the MonoObject*, add 8 bytes, and assume the data of the c# struct is stored there in memory.
+//When you update to a newer version of mono, and get weird crashes, the assumptions that these offsets make are a good one to verify.
+enum { kMonoObjectOffset = sizeof(void*) * 2 };
+enum { kMonoArrayOffset = sizeof(void*) * 4 };
+
+#if UNITY_EDITOR
+bool IsStackLargeEnough ();
+void AssertStackLargeEnough ();
+#undef SCRIPTINGAPI_STACK_CHECK
+#define SCRIPTINGAPI_STACK_CHECK(NAME) AssertStackLargeEnough();
+#endif
+
+// TODO: move
+struct UnityEngineObjectMemoryLayout
+{
+ int instanceID;
+ void* cachedPtr;
+
+#if MONO_QUALITY_ERRORS
+ MonoString* error;
+#endif
+};
+
+struct MonoObject;
+struct MonoClass;
+struct MonoException;
+struct MonoArray;
+class TrackedReferenceBase;
+
+
+#if UNITY_WII
+#define SCRIPTINGAPI_DEFINE_REF_ARG(t, n) MonoObject* _ ## n ## __mObject
+#define SCRIPTINGAPI_FIX_REF_ARG(t, n) t n; n.object = _ ## n ## __mObject;
+#else
+#define SCRIPTINGAPI_DEFINE_REF_ARG(t, n) t n
+#define SCRIPTINGAPI_FIX_REF_ARG(t, n)
+#endif
+
+/*
+ Every MonoObject* that wraps an Object class contains the Reference::Data struct.
+ It contains the instanceID and a cachedPtr.
+ Only DEPLOY_OPTIMIZED mode (Player in release build) uses the cached ptr
+
+ Normally we play safe and always dereference the pptr and also explicitly check
+ for null so we catch null ptr's before dereferencing.
+
+ Referenced objects can of course be destroyed, resulting in stable cachedPtr's
+ Why does this work?
+
+ If the script code expects that a reference might become null it needs to check against null.
+ if (transform == null)
+ If the script code does not check and the referenced is deleted,
+ then a null exception will be thrown in the editor.
+ In the DEPLOY_OPTIZMIZED mode no exception will be thrown but the program will just crash.
+ This means that a game has to ship with no null exception thrown in the editor or debug player.
+*/
+
+template<class T>
+inline T* ExtractMonoObjectDataPtr (MonoObject* object)
+{
+ return reinterpret_cast<T*> (reinterpret_cast<char*> (object) + kMonoObjectOffset);
+}
+
+template<class T>
+inline T& ExtractMonoObjectData (MonoObject* object)
+{
+ return *reinterpret_cast<T*> (reinterpret_cast<char*> (object) + kMonoObjectOffset);
+}
+
+
+inline ScriptingObjectPtr ScriptingInstantiateObject(ScriptingClassPtr klass)
+{
+#if UNITY_EDITOR
+ if (mono_unity_class_is_abstract (klass)) {
+ // Cannot instantiate abstract class
+ return SCRIPTING_NULL;
+ }
+#endif
+ return mono_object_new(mono_domain_get(),klass);
+}
+
+template<class T> inline
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, T* dest)
+{
+ *dest = ExtractMonoObjectData<T>(so);
+}
+
+template<class T> inline
+void MarshallNativeStructIntoManaged(const T& src, ScriptingObjectPtr dest)
+{
+ ExtractMonoObjectData<T>(dest) = src;
+}
+
+
+// If MonoClass is derived from any UnityEngine class returns its classID
+// otherwise returns -1.
+
+void mono_runtime_object_init_exception (MonoObject *thiss, MonoException** exception);
+void mono_runtime_object_init_log_exception (MonoObject *thiss);
+
+template<class T>
+inline T& GetMonoArrayElement (MonoArray* array, int i)
+{
+ return Scripting::GetScriptingArrayElement<T>(array,i);
+}
+
+
+inline void* GetMonoArrayPtr(MonoArray* array)
+{
+ return (void*) (((char*) array) + kMonoArrayOffset);
+}
+
+template<class T>
+inline T* GetMono2DArrayData (MonoArray* array)
+{
+ char* raw = kMonoArrayOffset + sizeof(guint32) + (char*)array;
+ return (T*)raw;
+}
+
+
+template<class T>
+inline T* GetMono3DArrayData (MonoArray* array)
+{
+ char* raw = kMonoArrayOffset + sizeof(guint32)*2 + (char*)array;
+ return (T*)raw;
+}
+
+int MonoDomainGetUniqueId();
+void MonoDomainIncrementUniqueId();
+
+int mono_array_length (MonoArray* array);
+int mono_array_length_safe (MonoArray* array);
+
+std::string MonoStringToCpp (MonoString* monoString);
+std::string MonoStringToCppChecked (MonoObject* monoString);
+
+#if UNITY_WIN || UNITY_XENON
+std::wstring MonoStringToWideCpp (MonoString* monoString);
+#endif
+
+MonoString* MonoStringNew (const std::string& in);
+MonoString* MonoStringNew (const char* in);
+MonoString* MonoStringNewLength (const char* in, int length);
+MonoString* MonoStringNewUTF16 (const wchar_t* in);
+ScriptingStringPtr MonoStringFormat (const char* format, ...);
+
+bool ExceptionToLineAndPath (const std::string& exception, int& line, std::string& path);
+bool MonoObjectToBool (MonoObject* value);
+int MonoObjectToInt (MonoObject* value);
+MonoMethod* mono_reflection_method_get_method (MonoObject* ass);
+MonoString* MonoStringFormat (const char* format, ...);
+MonoArray *mono_array_new_3d (int size0, int size1, int size2, MonoClass *klass);
+MonoArray *mono_array_new_2d (int size0, int size1, MonoClass *klass);
+MonoAssembly* mono_load_assembly_from_any_monopath(const char* assemblyname);
+MonoMethod* mono_unity_find_method(const char* assemblyname, const char* ns, const char* klass, const char* methodname);
+MonoObject* mono_class_get_object (MonoClass* klass);
+MonoClass* mono_type_get_class_or_element_class (MonoType* type);
+MonoString* UnassignedReferenceString (MonoObject* instance, int classID, MonoClassField* field, int instanceID);
+
+bool IsUtf16InAsciiRange( gunichar2 const* str, int length );
+bool FastTestAndConvertUtf16ToAscii( char* dest, gunichar2 const* str, int length );
+// str must contain only ascii characters
+void FastUtf16ToAscii( char* destination, gunichar2 const* str, int length );
+
+// Helper functions to go over a C++ vector<arbitrary struct> & create a MonoArray of it.
+// it runs a template function on it in order to do the actual conversion of each object
+/* Example usage:
+C++RAW
+struct MonoTreePrototype {
+ MonoObject *prefab;
+};
+
+void TreePrototypeToMono (TreePrototype &src, MonoTreePrototype &dest) {
+ dest.prefab = ObjectToScriptingObject (src.prefab);
+}
+
+void TreePrototypeToCpp (MonoTreePrototype &src, TreePrototype &dest) {
+ dest.prefab = MonoObjectToObject<GameObject> (src.prefab);
+}
+
+
+CUSTOM_PROP TreePrototype[] treePrototypes
+ { return VectorToMonoStructArray<TreePrototype, MonoTreePrototype> (self->GetTreePrototypes(), MONO_COMMON.treePrototype, TreePrototypeToMono); }
+ { MonoStructArrayToVector<TreePrototype, MonoTreePrototype> (value, self->GetTreePrototypes(), TreePrototypeToCpp); }
+*/
+
+template<class T, typename Alloc>
+void MonoObjectArrayToSet (MonoArray *array, std::set<T*, std::less<T*>, Alloc >& dest) {
+ Scripting::RaiseIfNull (array);
+ int len = mono_array_length_safe(array);
+ dest.clear();
+ for (int i = 0; i < len;i++)
+ {
+ int instanceID = Scripting::GetInstanceIDFromScriptingWrapper(GetMonoArrayElement<MonoObject*>(array, i));
+ T* objectPtr = dynamic_instanceID_cast<T*> (instanceID);
+ if (objectPtr)
+ dest.insert(objectPtr);
+ }
+}
+
+template<class T>
+void MonoArrayToSet(MonoArray* array, std::set<T>& dest)
+{
+ Scripting::RaiseIfNull (array);
+ int len = mono_array_length_safe(array);
+ dest.clear();
+ for (int i = 0; i < len;i++)
+ {
+ dest.insert(GetMonoArrayElement<T>(array, i));
+ }
+}
+
+template<typename T>
+MonoArray* SetToMonoArray(const std::set<T>& src, MonoClass* klass)
+{
+ MonoArray* array = mono_array_new (mono_domain_get (), klass, src.size());
+
+ typename std::set<T>::const_iterator j = src.begin();
+ for (int i=0;i<src.size();i++, j++)
+ {
+ Scripting::SetScriptingArrayElement(array, i, *j);
+ }
+
+ return array;
+}
+
+template<class T>
+void MonoArrayToVector(MonoArray* array, std::vector<T>& dest)
+{
+ Scripting::RaiseIfNull (array);
+ int len = mono_array_length_safe(array);
+ dest.resize (len);
+ for (int i = 0; i < len; i++)
+ dest[i] = GetMonoArrayElement<T>(array, i);
+}
+
+
+template<class T, class T2, class U, class TConverter>
+MonoArray *VectorToMonoStructArray (const U &source, MonoClass *klass, TConverter converter) {
+ MonoArray *arr = mono_array_new (mono_domain_get (), klass, source.size());
+ for (int i = 0; i < source.size();i++)
+ converter (source[i], GetMonoArrayElement<T2> (arr, i));
+ return arr;
+}
+
+template<class T, class T2>
+void MonoStructArrayToVector (MonoArray *source, std::vector<T> &dest, void (*converter) (T2 &source, T &dest)) {
+ Scripting::RaiseIfNull (source);
+ int len = mono_array_length(source);
+ dest.resize (len);
+ for (int i = 0; i < len;i++)
+ converter (GetMonoArrayElement<T2> (source, i), dest[i]);
+}
+
+template<class T, class T2>
+std::vector<T> MonoStructArrayToVector (MonoArray *source, void (*converter) (T2 &source, T &dest)) {
+ std::vector<T> dest;
+ MonoStructArrayToVector<T, T2> (source, dest, converter);
+ return dest;
+}
+
+template<class T, class T2, class U>
+MonoArray *VectorToMonoClassArray (const U &source, MonoClass *klass, void (*converter) (const T &source, T2 &dest)) {
+ MonoArray *arr = mono_array_new (mono_domain_get (), klass, source.size());
+ for (int i = 0; i < source.size();i++) {
+ MonoObject *obj = ScriptingInstantiateObject (klass);
+ GetMonoArrayElement<MonoObject*> (arr,i) = obj;
+ converter (source[i], ExtractMonoObjectData<T2> (obj));
+ }
+ return arr;
+}
+
+template<class T, class T2>
+MonoArray *SetToMonoClassArray (const std::set<T> &source, MonoClass *klass, void (*converter) (const T &source, T2 &dest)) {
+ MonoArray *arr = mono_array_new (mono_domain_get (), klass, source.size());
+ int idx = 0;
+ for (typename std::set<T>::const_iterator i = source.begin(); i != source.end();i++) {
+ MonoObject *obj = ScriptingInstantiateObject (klass);
+ GetMonoArrayElement<MonoObject*> (arr,idx++) = obj;
+ converter (*i, ExtractMonoObjectData<T2> (obj));
+ }
+ return arr;
+}
+
+template<typename mapType, class T, class T2, class T3>
+MonoArray *MapToMonoClassArray (const mapType &source, MonoClass *klass, void (*converter) (const T &first, const T2 &second, T3 &dest)) {
+ MonoArray *arr = mono_array_new (mono_domain_get (), klass, source.size());
+ int idx = 0;
+ for (typename mapType::const_iterator i = source.begin(); i != source.end();i++) {
+ MonoObject *obj = ScriptingInstantiateObject (klass);
+ GetMonoArrayElement<MonoObject*> (arr,idx++) = obj;
+ converter (i->first, i->second, ExtractMonoObjectData<T3> (obj));
+ }
+ return arr;
+}
+
+template<class T, class T2>
+void MonoClassArrayToVector (MonoArray *source, std::vector<T> &dest, void (*converter) (T2 &source, T &dest)) {
+ Scripting::RaiseIfNull (source);
+ int len = mono_array_length(source);
+ dest.resize (len);
+ for (int i = 0; i < len;i++) {
+ MonoObject *obj = GetMonoArrayElement<MonoObject*> (source, i);
+ Scripting::RaiseIfNull (obj);
+ converter (ExtractMonoObjectData<T2> (obj), dest[i]);
+ }
+}
+
+template<class T, class T2>
+std::vector<T> MonoClassArrayToVector (MonoArray *source, void (*converter) (T2 &source, T &dest)) {
+ std::vector<T> dest;
+ MonoClassArrayToVector<T, T2> (source, dest, converter);
+ return dest;
+}
+
+void StringMonoArrayToVector (MonoArray* arr, std::vector<std::string>& container);
+void StringMonoArrayToVector (MonoArray* arr, std::vector<UnityStr>& container);
+
+inline ScriptingClassPtr GetScriptingTypeOfScriptingObject(ScriptingObjectPtr object)
+{
+ return mono_object_get_class(object);
+}
+
+inline bool ScriptingClassIsSubclassOf(ScriptingClassPtr c1, ScriptingClassPtr c2)
+{
+ return mono_class_is_subclass_of(c1,c2,true);
+}
+
+ScriptingClassPtr GetBuiltinScriptingClass(const char* name,bool optional=false);
+
+inline int GetScriptingArraySize(ScriptingArrayPtr a)
+{
+ return mono_array_length_safe(a);
+}
+
+
+/*
+ Code for switching between mono_runtime_invoke and mono_aot_get_method
+ (later mono_method_get_unmanaged_thunk also will be included)
+ */
+
+void* ResolveMonoMethodPointer(MonoDomain *domain, MonoMethod *method);
+bool MonoSetObjectField(MonoObject* target, const char* fieldname, MonoObject* value);
+#if UNITY_EDITOR
+bool IsStackLargeEnough ();
+#endif
+
+// Ensure current thread is attached to mono,
+// error and return otherwise
+#define MONO_RUNTIME_INVOKE_THREAD_CHECK_WITH_RET(Ret) \
+do{ \
+ if (!mono_thread_current ()) { \
+ ErrorStringWithoutStacktrace ("Thread is not attached to scripting runtime"); \
+ return Ret; \
+ } \
+} while (0)
+
+#define MONO_RUNTIME_INVOKE_THREAD_CHECK MONO_RUNTIME_INVOKE_THREAD_CHECK_WITH_RET(NULL)
+
+// Ensure we have enough stack for a reasonable invocation
+#if UNITY_EDITOR
+#define MONO_RUNTIME_INVOKE_STACK_CHECK_WITH_RET(Ret) \
+do { \
+ if (!IsStackLargeEnough ()) { \
+ *exc = mono_exception_from_name_msg (mono_get_corlib (), "System", "StackOverflowException", ""); \
+ return Ret; \
+ } \
+} while (0)
+
+#define MONO_RUNTIME_INVOKE_STACK_CHECK MONO_RUNTIME_INVOKE_STACK_CHECK_WITH_RET(NULL)
+#else
+#define MONO_RUNTIME_INVOKE_STACK_CHECK_WITH_RET(Ret) do {} while (0)
+#define MONO_RUNTIME_INVOKE_STACK_CHECK do {} while (0)
+#endif
+
+inline MonoObject* mono_runtime_invoke_profiled_fast (ScriptingMethodPtr method, MonoObject* obj, MonoException** exc, MonoClass* profileClassForCoroutine)
+{
+ MONO_RUNTIME_INVOKE_THREAD_CHECK;
+ MONO_RUNTIME_INVOKE_STACK_CHECK;
+
+ MONO_PROFILER_BEGIN(method,profileClassForCoroutine, obj);
+
+ MonoObject* ret;
+#if USE_MONO_AOT
+ if (method->fastMonoMethod)
+ ret = method->fastMonoMethod(obj, exc);
+ else
+#endif
+ ret = mono_runtime_invoke(method->monoMethod, obj, NULL, exc);
+
+ MONO_PROFILER_END;
+
+ return ret;
+}
+
+
+/// returns false if an exception was thrown
+inline bool mono_runtime_invoke_profiled_fast_bool (MonoMethod* method, FastMonoMethod fastMethod, MonoObject* obj, MonoException** exc, MonoClass* profileClassForCoroutine)
+{
+ Assert(USE_MONO_AOT || fastMethod == NULL );
+ Assert(exc != NULL);
+
+ MONO_RUNTIME_INVOKE_THREAD_CHECK_WITH_RET(false);
+ MONO_RUNTIME_INVOKE_STACK_CHECK_WITH_RET(false);
+
+ MONO_PROFILER_BEGIN(GetScriptingMethodRegistry().GetMethod(method), profileClassForCoroutine, obj);
+
+ bool result = false;
+#if USE_MONO_AOT
+ if (fastMethod)
+ result = fastMethod(obj, exc);
+ else
+#endif
+ {
+ MonoObject* _tmpres = mono_runtime_invoke(method, obj, NULL, exc);
+ if (_tmpres != NULL && *exc == NULL)
+ result = ExtractMonoObjectData<char>(_tmpres);
+ }
+
+ MONO_PROFILER_END;
+
+ return result;
+}
+
+/// Never call mono_runtime_invoke directly, otherwise the profiler will not be able to pick it up!
+inline MonoObject* mono_runtime_invoke_profiled (MonoMethod *method, MonoObject *obj, void **params, MonoException **exc, MonoClass* classContextForProfiler=NULL)
+{
+ MONO_RUNTIME_INVOKE_THREAD_CHECK;
+ MONO_RUNTIME_INVOKE_STACK_CHECK;
+
+ MONO_PROFILER_BEGIN (GetScriptingMethodRegistry().GetMethod(method), classContextForProfiler, obj);
+ MonoObject* ret = mono_runtime_invoke(method, obj, params, exc);
+ MONO_PROFILER_END;
+
+ return ret;
+}
+
+template<class T>
+ScriptingObjectPtr CreateScriptingObjectFromNativeStruct(ScriptingClassPtr klass, T& thestruct)
+{
+ ScriptingObjectPtr mono = ScriptingInstantiateObject (klass);
+ T& destStruct = ExtractMonoObjectData<T> (mono);
+ destStruct = thestruct;
+ return mono;
+}
+
+inline
+char* ScriptingStringToAllocatedChars(const ICallString& str)
+{
+ return mono_string_to_utf8(str.str);
+}
+
+MonoClassField* GetMonoArrayFieldFromList (int type, MonoType* monoType, MonoClassField* field);
+
+int EXPORT_COREMODULE mono_array_length_safe_wrapper(MonoArray* array);
+
+inline void ScriptingStringToAllocatedChars_Free(const char* str)
+{
+ g_free((void*)str);
+}
+
+template<class T>
+struct ScriptingObjectOfType;
+
+template<class T>
+inline T* ScriptingObjectToObject(ScriptingObjectPtr so)
+{
+ ScriptingObjectOfType<T> ref(so);
+ return ref.GetPtr();
+}
+
+MonoClassField* GetMonoArrayFieldFromList (int type, MonoType* monoType, MonoClassField* field);
+
+/// ToDo: remove these (MonoObjectArrayToVector, MonoObjectArrayToPPtrVector) later, or change it to more unified version
+/// vector<GameObject*> gos;
+/// gos = MonoObjectArrayToVector(MonoArray*);
+template<class T>
+void MonoObjectArrayToVector (MonoArray *source, std::vector<T*>& dest) {
+ Scripting::RaiseIfNull (source);
+ int len = mono_array_length(source);
+ dest.resize (len);
+ for (int i = 0; i < len;i++)
+ dest[i] = ScriptingObjectToObject<T> (GetMonoArrayElement<MonoObject*> (source, i));
+}
+
+/// vector<GameObject*> gos;
+/// gos = MonoObjectArrayToVector(MonoArray*);
+template<class T>
+void MonoObjectArrayToPPtrVector (MonoArray *source, std::vector<PPtr<T> >& dest) {
+ Scripting::RaiseIfNull (source);
+ int len = mono_array_length(source);
+ dest.resize (len);
+ for (int i = 0; i < len;i++)
+ dest[i] = ScriptingObjectToObject<T> (GetMonoArrayElement<MonoObject*> (source, i));
+}
+
+template<class T>
+inline ScriptingObjectPtr ScriptingGetObjectReference(PPtr<T> object)
+{
+ return ObjectToScriptingObject(object);
+}
+
+template<class T>
+inline ScriptingObjectPtr ScriptingGetObjectReference(T* object)
+{
+ return Scripting::ScriptingWrapperFor(object);
+}
+
+#define CreateScriptingParams(VariableName, ParamCount) ScriptingParams VariableName[ParamCount];
+// Don't use if Value is ScriptingObjectPtr!!!
+#define SetScriptingParam(VariableName, Index, Value) VariableName[Index] = &Value;
+#define GetSafeString(ClassName, Getter) Getter
+
+std::string ErrorMessageForUnsupportedEnumField(MonoType* enumType, MonoType* classType, const char * fieldName);
+
+MonoObject* MonoObjectNULL (int classID, MonoString* error);
+
+namespace Unity { class GameObject; class Component;}
+class Object;
+
+#if MONO_QUALITY_ERRORS
+// Wrap null ptr with pseudo null object
+ScriptingObjectPtr MonoObjectNULL (ScriptingClassPtr klass, ScriptingStringPtr error);
+inline ScriptingObjectPtr MonoObjectNULL (ScriptingClassPtr klass){ return MonoObjectNULL (klass, SCRIPTING_NULL); }
+
+// Component missing string error
+MonoString* MissingComponentString (Unity::GameObject& go, int classID);
+MonoString* MissingComponentString (Unity::GameObject& go, ScriptingTypePtr klass);
+
+#else
+
+inline ScriptingObjectPtr MonoObjectNULL (ScriptingClassPtr klass) { return SCRIPTING_NULL; }
+
+#endif
+
+#endif//ENABLE_MONO
diff --git a/Runtime/Mono/tabledefs.h b/Runtime/Mono/tabledefs.h
new file mode 100644
index 0000000..cfe9543
--- /dev/null
+++ b/Runtime/Mono/tabledefs.h
@@ -0,0 +1,231 @@
+/*
+ * tabledefs.h: This file contains the various definitions for constants
+ * found on the metadata tables
+ *
+ * Author:
+ * Miguel de Icaza (miguel@ximian.com)
+ *
+ * (C) 2001 Ximian, Inc.
+ *
+ * From the ECMA documentation
+ */
+
+#ifndef _MONO_METADATA_TABLEDEFS_H_
+#define _MONO_METADATA_TABLEDEFS_H_
+
+/*
+ * 22.1.1 Values for AssemblyHashAlgorithm
+ */
+
+enum {
+ ASSEMBLY_HASH_NONE,
+ ASSEMBLY_HASH_MD5 = 0x8003,
+ ASSEMBLY_HASH_SHA1 = 0x8004
+};
+
+/*
+ * 22.1.4 Flags for Event.EventAttributes
+ */
+
+enum {
+ EVENT_SPECIALNAME = 0x0200,
+ EVENT_RTSPECIALNAME = 0x0400
+};
+
+/*
+ * 22.1.6 Flags for FileAttributes
+ */
+
+enum {
+ FILE_CONTAINS_METADATA = 0,
+ FILE_CONTAINS_NO_METADATA = 1
+};
+
+enum {
+ SECURITY_ACTION_DEMAND = 2,
+ SECURITY_ACTION_ASSERT = 3,
+ SECURITY_ACTION_DENY = 4,
+ SECURITY_ACTION_PERMITONLY = 5,
+ SECURITY_ACTION_LINKDEMAND = 6,
+ SECURITY_ACTION_INHERITDEMAND = 7,
+ SECURITY_ACTION_REQMIN = 8,
+ SECURITY_ACTION_REQOPT = 9,
+ SECURITY_ACTION_REQREFUSE = 10
+};
+
+/*
+ * Field Attributes (21.1.5).
+ */
+
+#define FIELD_ATTRIBUTE_FIELD_ACCESS_MASK 0x0007
+#define FIELD_ATTRIBUTE_COMPILER_CONTROLLED 0x0000
+#define FIELD_ATTRIBUTE_PRIVATE 0x0001
+#define FIELD_ATTRIBUTE_FAM_AND_ASSEM 0x0002
+#define FIELD_ATTRIBUTE_ASSEMBLY 0x0003
+#define FIELD_ATTRIBUTE_FAMILY 0x0004
+#define FIELD_ATTRIBUTE_FAM_OR_ASSEM 0x0005
+#define FIELD_ATTRIBUTE_PUBLIC 0x0006
+
+#define FIELD_ATTRIBUTE_STATIC 0x0010
+#define FIELD_ATTRIBUTE_INIT_ONLY 0x0020
+#define FIELD_ATTRIBUTE_LITERAL 0x0040
+#define FIELD_ATTRIBUTE_NOT_SERIALIZED 0x0080
+#define FIELD_ATTRIBUTE_SPECIAL_NAME 0x0200
+#define FIELD_ATTRIBUTE_PINVOKE_IMPL 0x2000
+
+/* For runtime use only */
+#define FIELD_ATTRIBUTE_RESERVED_MASK 0x9500
+#define FIELD_ATTRIBUTE_RT_SPECIAL_NAME 0x0400
+#define FIELD_ATTRIBUTE_HAS_FIELD_MARSHAL 0x1000
+#define FIELD_ATTRIBUTE_HAS_DEFAULT 0x8000
+#define FIELD_ATTRIBUTE_HAS_FIELD_RVA 0x0100
+
+/*
+ * Type Attributes (21.1.13).
+ */
+#define TYPE_ATTRIBUTE_VISIBILITY_MASK 0x00000007
+#define TYPE_ATTRIBUTE_NOT_PUBLIC 0x00000000
+#define TYPE_ATTRIBUTE_PUBLIC 0x00000001
+#define TYPE_ATTRIBUTE_NESTED_PUBLIC 0x00000002
+#define TYPE_ATTRIBUTE_NESTED_PRIVATE 0x00000003
+#define TYPE_ATTRIBUTE_NESTED_FAMILY 0x00000004
+#define TYPE_ATTRIBUTE_NESTED_ASSEMBLY 0x00000005
+#define TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM 0x00000006
+#define TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM 0x00000007
+
+#define TYPE_ATTRIBUTE_LAYOUT_MASK 0x00000018
+#define TYPE_ATTRIBUTE_AUTO_LAYOUT 0x00000000
+#define TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT 0x00000008
+#define TYPE_ATTRIBUTE_EXPLICIT_LAYOUT 0x00000010
+
+#define TYPE_ATTRIBUTE_CLASS_SEMANTIC_MASK 0x00000020
+#define TYPE_ATTRIBUTE_CLASS 0x00000000
+#define TYPE_ATTRIBUTE_INTERFACE 0x00000020
+
+#define TYPE_ATTRIBUTE_ABSTRACT 0x00000080
+#define TYPE_ATTRIBUTE_SEALED 0x00000100
+#define TYPE_ATTRIBUTE_SPECIAL_NAME 0x00000400
+
+#define TYPE_ATTRIBUTE_IMPORT 0x00001000
+#define TYPE_ATTRIBUTE_SERIALIZABLE 0x00002000
+
+#define TYPE_ATTRIBUTE_STRING_FORMAT_MASK 0x00030000
+#define TYPE_ATTRIBUTE_ANSI_CLASS 0x00000000
+#define TYPE_ATTRIBUTE_UNICODE_CLASS 0x00010000
+#define TYPE_ATTRIBUTE_AUTO_CLASS 0x00020000
+
+#define TYPE_ATTRIBUTE_BEFORE_FIELD_INIT 0x00100000
+
+#define TYPE_ATTRIBUTE_RESERVED_MASK 0x00040800
+#define TYPE_ATTRIBUTE_RT_SPECIAL_NAME 0x00000800
+#define TYPE_ATTRIBUTE_HAS_SECURITY 0x00040000
+
+/*
+ * Method Attributes (22.1.9)
+ */
+
+#define METHOD_IMPL_ATTRIBUTE_CODE_TYPE_MASK 0x0003
+#define METHOD_IMPL_ATTRIBUTE_IL 0x0000
+#define METHOD_IMPL_ATTRIBUTE_NATIVE 0x0001
+#define METHOD_IMPL_ATTRIBUTE_OPTIL 0x0002
+#define METHOD_IMPL_ATTRIBUTE_RUNTIME 0x0003
+
+#define METHOD_IMPL_ATTRIBUTE_MANAGED_MASK 0x0004
+#define METHOD_IMPL_ATTRIBUTE_UNMANAGED 0x0004
+#define METHOD_IMPL_ATTRIBUTE_MANAGED 0x0000
+
+#define METHOD_IMPL_ATTRIBUTE_FORWARD_REF 0x0010
+#define METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG 0x0080
+#define METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL 0x1000
+#define METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED 0x0020
+#define METHOD_IMPL_ATTRIBUTE_NOINLINING 0x0008
+#define METHOD_IMPL_ATTRIBUTE_MAX_METHOD_IMPL_VAL 0xffff
+
+#define METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK 0x0007
+#define METHOD_ATTRIBUTE_COMPILER_CONTROLLED 0x0000
+#define METHOD_ATTRIBUTE_PRIVATE 0x0001
+#define METHOD_ATTRIBUTE_FAM_AND_ASSEM 0x0002
+#define METHOD_ATTRIBUTE_ASSEM 0x0003
+#define METHOD_ATTRIBUTE_FAMILY 0x0004
+#define METHOD_ATTRIBUTE_FAM_OR_ASSEM 0x0005
+#define METHOD_ATTRIBUTE_PUBLIC 0x0006
+
+#define METHOD_ATTRIBUTE_STATIC 0x0010
+#define METHOD_ATTRIBUTE_FINAL 0x0020
+#define METHOD_ATTRIBUTE_VIRTUAL 0x0040
+#define METHOD_ATTRIBUTE_HIDE_BY_SIG 0x0080
+
+#define METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK 0x0100
+#define METHOD_ATTRIBUTE_REUSE_SLOT 0x0000
+#define METHOD_ATTRIBUTE_NEW_SLOT 0x0100
+
+#define METHOD_ATTRIBUTE_ABSTRACT 0x0400
+#define METHOD_ATTRIBUTE_SPECIAL_NAME 0x0800
+
+#define METHOD_ATTRIBUTE_PINVOKE_IMPL 0x2000
+#define METHOD_ATTRIBUTE_UNMANAGED_EXPORT 0x0008
+
+/*
+ * For runtime use only
+ */
+#define METHOD_ATTRIBUTE_RESERVED_MASK 0xd000
+#define METHOD_ATTRIBUTE_RT_SPECIAL_NAME 0x1000
+#define METHOD_ATTRIBUTE_HAS_SECURITY 0x4000
+#define METHOD_ATTRIBUTE_REQUIRE_SEC_OBJECT 0x8000
+
+
+/*
+ * Method Semantics ([MethodSemanticAttributes]) 22.1.10
+ */
+
+#define METHOD_SEMANTIC_SETTER 0x0001
+#define METHOD_SEMANTIC_GETTER 0x0002
+#define METHOD_SEMANTIC_OTHER 0x0004
+#define METHOD_SEMANTIC_ADD_ON 0x0008
+#define METHOD_SEMANTIC_REMOVE_ON 0x0010
+#define METHOD_SEMANTIC_FIRE 0x0020
+
+/*
+ * Flags for Params (22.1.11)
+ */
+#define PARAM_ATTRIBUTE_IN 0x0001
+#define PARAM_ATTRIBUTE_OUT 0x0002
+#define PARAM_ATTRIBUTE_OPTIONAL 0x0004
+#define PARAM_ATTRIBUTE_RESERVED_MASK 0xf000
+#define PARAM_ATTRIBUTE_HAS_DEFAULT 0x1000
+#define PARAM_ATTRIBUTE_HAS_FIELD_MARSHAL 0x2000
+#define PARAM_ATTRIBUTE_UNUSED 0xcfe0
+
+/*
+ * 22.1.12 PropertyAttributes
+ */
+#define PROPERTY_ATTRIBUTE_SPECIAL_NAME 0x0200
+#define PROPERTY_ATTRIBUTE_RESERVED_MASK 0xf400
+#define PROPERTY_ATTRIBUTE_RT_SPECIAL_NAME 0x0400
+#define PROPERTY_ATTRIBUTE_HAS_DEFAULT 0x1000
+#define PROPERTY_ATTRIBUTE_UNUSED 0xe9ff
+
+/*
+ * 22.1.7 Flags for ImplMap [PInvokeAttributes]
+ */
+#define PINVOKE_ATTRIBUTE_NO_MANGLE 0x0001
+#define PINVOKE_ATTRIBUTE_CHAR_SET_MASK 0x0006
+#define PINVOKE_ATTRIBUTE_CHAR_SET_NOT_SPEC 0x0000
+#define PINVOKE_ATTRIBUTE_CHAR_SET_ANSI 0x0002
+#define PINVOKE_ATTRIBUTE_CHAR_SET_UNICODE 0x0004
+#define PINVOKE_ATTRIBUTE_CHAR_SET_AUTO 0x0006
+#define PINVOKE_ATTRIBUTE_SUPPORTS_LAST_ERROR 0x0040
+#define PINVOKE_ATTRIBUTE_CALL_CONV_MASK 0x0700
+#define PINVOKE_ATTRIBUTE_CALL_CONV_WINAPI 0x0100
+#define PINVOKE_ATTRIBUTE_CALL_CONV_CDECL 0x0200
+#define PINVOKE_ATTRIBUTE_CALL_CONV_STDCALL 0x0300
+#define PINVOKE_ATTRIBUTE_CALL_CONV_THISCALL 0x0400
+#define PINVOKE_ATTRIBUTE_CALL_CONV_FASTCALL 0x0500
+#define PINVOKE_ATTRIBUTE_CALL_CONV_GENERIC 0x0010
+#define PINVOKE_ATTRIBUTE_CALL_CONV_GENERICINST 0x000a
+
+/**
+ * 21.5 AssemblyRefs
+ */
+#define ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG 0x00000001
+#endif
diff --git a/Runtime/NavMesh/DynamicMesh.cpp b/Runtime/NavMesh/DynamicMesh.cpp
new file mode 100644
index 0000000..e4d54a7
--- /dev/null
+++ b/Runtime/NavMesh/DynamicMesh.cpp
@@ -0,0 +1,693 @@
+#include "UnityPrefix.h"
+#include "./DynamicMesh.h"
+#include <math.h>
+#include <float.h>
+#include "Runtime/Math/FloatConversion.h"
+#include "DetourCommon.h"
+
+const float MAGIC_QUANTIZE = 1e-2f;
+const bool MERGE_POLYGONS = true;
+
+// Compiling to conditional move this is typically faster than modulus operator % in the general case
+static inline size_t NextIndex (size_t index, size_t modulus)
+{
+ DebugAssert (index < modulus);
+ const size_t next = index + 1;
+ return (next == modulus) ? 0 : next;
+}
+
+// Compiling to conditional move this is typically faster than modulus operator % in the general case
+static inline size_t PrevIndex (size_t index, size_t modulus)
+{
+ DebugAssert (index < modulus);
+ return (index == 0) ? modulus-1 : index - 1;
+}
+
+// TODO : round only fraction part of the floats
+// in order to avoid integer overflow for large values of 'v'
+static inline Vector3f QuantizeVertex (const Vector3f& v)
+{
+ if (MAGIC_QUANTIZE <= 0)
+ {
+ return v;
+ }
+ else
+ {
+ Vector3f qv = 1.0f / MAGIC_QUANTIZE * v;
+ qv = Vector3f (RoundfToInt (qv.x), RoundfToInt (qv.y), RoundfToInt (qv.z)) * MAGIC_QUANTIZE;
+ return qv;
+ }
+}
+
+static inline bool IsQuantized (const Vector3f& v)
+{
+ Vector3f qv = QuantizeVertex (v);
+ return qv == v;
+}
+
+static inline bool IsStrictlyConvex (const dynamic_array< Vector3f >& vertices)
+{
+ const size_t vertexCount = vertices.size ();
+ for (size_t i = 0; i < vertexCount; ++i)
+ {
+ const float* v0 = vertices[PrevIndex (i, vertexCount)].GetPtr ();
+ const float* v1 = vertices[i].GetPtr ();
+ const float* v2 = vertices[NextIndex (i, vertexCount)].GetPtr ();
+ const float triArea = dtTriArea2D (v0, v1, v2);
+ if (triArea <= 0) return false;
+ }
+ return true;
+}
+
+static inline bool PolygonDegenerate (size_t vertexCount, const UInt16* indices, const Vector3f* vertices)
+{
+ for (size_t i = 0; i < vertexCount; ++i)
+ {
+ DebugAssert (IsQuantized (vertices[indices[i]]));
+ }
+ if (vertexCount < 3)
+ {
+ return true;
+ }
+ float area = 0.0f;
+ float maxSideSq = 0.0f;
+ for (size_t i = 2; i < vertexCount; ++i)
+ {
+ const float* v0 = vertices[indices[0]].GetPtr ();
+ const float* v1 = vertices[indices[i-1]].GetPtr ();
+ const float* v2 = vertices[indices[i]].GetPtr ();
+ const float triArea = dtTriArea2D (v0, v1, v2);
+
+ area += triArea;
+ maxSideSq = std::max (dtVdistSqr (v0, v1), maxSideSq);
+ maxSideSq = std::max (dtVdistSqr (v0, v2), maxSideSq);
+ }
+ if (area <= 0)
+ {
+ return true;
+ }
+ const float safety = 1e-2f * MAGIC_QUANTIZE;
+ return area * area <= safety * safety * maxSideSq;
+}
+
+static inline float PolygonDegenerate (const dynamic_array< Vector3f >& vertices)
+{
+ if (vertices.size () < 3)
+ {
+ return true;
+ }
+ float area = 0.0f;
+ float maxSideSq = 0.0f;
+ for (size_t i = 2; i < vertices.size (); ++i)
+ {
+ const float* v0 = vertices[0].GetPtr ();
+ const float* v1 = vertices[i-1].GetPtr ();
+ const float* v2 = vertices[i].GetPtr ();
+ const float triArea = dtTriArea2D (v0, v1, v2);
+
+ area += triArea;
+ maxSideSq = std::max (dtVdistSqr (v0, v1), maxSideSq);
+ maxSideSq = std::max (dtVdistSqr (v0, v2), maxSideSq);
+ }
+ if (area <= 0)
+ {
+ return true;
+ }
+ const float safety = 1e-2f * MAGIC_QUANTIZE;
+ return area * area <= safety * safety * maxSideSq;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+size_t DynamicMesh::AddVertexChecked (const Vector3f& v)
+{
+ const Vector3f qv = QuantizeVertex (v);
+ size_t vertexCount = m_Vertices.size ();
+ for (size_t iv = 0; iv < vertexCount; ++iv)
+ {
+ const Vector3f& ov = m_Vertices[iv];
+ if (qv == ov)
+ return iv;
+ }
+
+ m_Vertices.push_back (qv);
+ return vertexCount;
+}
+
+DynamicMesh::Poly DynamicMesh::CreatePolygon (const Polygon& vertices, const UInt32 status)
+{
+ size_t vertexCount = vertices.size ();
+ DebugAssert (vertexCount <= NUM_VERTS);
+ DebugAssert (vertexCount > 2);
+
+ Poly newPoly = {{0}, {0}, 0, 0};
+ newPoly.m_VertexCount = vertexCount;
+ newPoly.m_Status = status;
+ for (size_t i = 0; i < vertexCount; ++i)
+ {
+ size_t vi = AddVertexChecked (vertices[i]);
+ DebugAssert (vi < 0xffff); //< vertex overflow
+ newPoly.m_VertexIDs[i] = (UInt16)vi;
+ }
+ return newPoly;
+}
+
+void DynamicMesh::RemovePolygon (size_t i)
+{
+ DebugAssert (i < m_Polygons.size ());
+ DebugAssert (m_Data.size () == m_Polygons.size ());
+
+ m_Polygons.erase (m_Polygons.begin () + i);
+ m_Data.erase (m_Data.begin () + i);
+}
+
+// Clip the convex polygon 'poly' by the half-space defined by 'plane'
+void DynamicMesh::SplitPoly (Polygon& inside, const Polygon& poly, const Plane& plane) const
+{
+ inside.resize_uninitialized (0);
+ const size_t vertexCount = poly.size ();
+
+ dynamic_array< float > dist (vertexCount, kMemTempAlloc);
+ for (size_t iv = 0; iv < vertexCount; ++iv)
+ {
+ const Vector3f& v = poly[iv];
+ dist[iv] = plane.GetDistanceToPoint (v);
+ }
+
+ Vector3f prevVert = poly[vertexCount-1];
+ float prevDist = dist[vertexCount-1];
+
+ for (size_t iv = 0; iv < vertexCount; ++iv)
+ {
+ const Vector3f& currVert = poly[iv];
+ const float currDist = dist[iv];
+
+ if (currDist < 0 && prevDist > 0)
+ {
+ const float absDist = -currDist;
+ const float w = absDist / (absDist + prevDist);
+ const Vector3f newVert = Lerp (currVert, prevVert, w);
+ inside.push_back (newVert);
+ // inside.push_back (QuantizeVertex (newVert));
+ }
+ else if (currDist > 0 && prevDist < 0)
+ {
+ const float absDist = -prevDist;
+ const float w = absDist / (absDist + currDist);
+ const Vector3f newVert = Lerp (prevVert, currVert, w);
+ inside.push_back (newVert);
+ // inside.push_back (QuantizeVertex (newVert));
+ }
+
+ if (currDist <= 0)
+ {
+ inside.push_back (currVert);
+ }
+
+ prevVert = currVert;
+ prevDist = currDist;
+ }
+}
+
+// Return the intersection of 'poly' and 'carveHull'
+// Assuming convex shapes.
+void DynamicMesh::Intersection (Polygon& inside, const Polygon& poly, const Hull& carveHull) const
+{
+ DebugAssert (inside.empty ());
+ const size_t planeCount = carveHull.size ();
+ inside.reserve (planeCount + NUM_VERTS);
+ inside = poly;
+
+ Polygon insidePoly;
+ insidePoly.reserve (planeCount + NUM_VERTS);
+
+ for (size_t ic = 0; ic < planeCount; ++ic)
+ {
+ const Plane& plane = carveHull[ic];
+ SplitPoly (insidePoly, inside, plane);
+ if (insidePoly.empty ())
+ {
+ inside.resize_uninitialized (0);
+ break;
+ }
+ inside = insidePoly;
+ }
+}
+
+void DynamicMesh::FromPoly (Polygon& result, const Poly& poly) const
+{
+ DebugAssert (poly.m_VertexCount > 2);
+ DebugAssert (poly.m_VertexCount <= NUM_VERTS);
+
+ const UInt32 vertexCount = poly.m_VertexCount;
+ result.resize_uninitialized (vertexCount);
+
+ for (size_t i = 0; i < vertexCount; ++i)
+ {
+ result[i] = Vector3f (GetVertex (poly.m_VertexIDs[i]));
+ }
+}
+
+void DynamicMesh::BuildEdgeConnections (EdgeList& edges) const
+{
+ DebugAssert (edges.empty ());
+ const size_t polyCount = m_Polygons.size ();
+ for (size_t ip = 0; ip < polyCount; ++ip)
+ {
+ const Poly& poly = m_Polygons[ip];
+ if (PolygonDegenerate (poly.m_VertexCount, poly.m_VertexIDs, &m_Vertices[0]))
+ continue;
+
+ size_t vertexCount = poly.m_VertexCount;
+
+ for (size_t ivp = vertexCount-1, iv = 0; iv < vertexCount; ivp = iv++)
+ {
+ UInt16 vp = poly.m_VertexIDs[ivp];
+ UInt16 v = poly.m_VertexIDs[iv];
+
+ DebugAssert (v != vp);
+
+ // Find edge by ordered vertex indices
+ UInt16 vmin = (v<vp) ? v : vp;
+ UInt16 vmax = (v>vp) ? v : vp;
+
+ size_t ie = 0;
+ size_t edgeCount = edges.size ();
+ for (; ie < edgeCount; ++ie)
+ {
+ Edge& edge = edges[ie];
+ if (edge.v1 != vmin || edges[ie].v2 != vmax)
+ continue;
+
+ // If already connected skip it.
+ // A polygon edge cannot connect more than two polygons.
+ // Ideally this should not happen.
+ if (edge.c2 != 0xffff)
+ break;
+
+ // Found an existing unconnected edge
+ edge.p2 = ip;
+ edge.c2 = ivp;
+ break;
+ }
+ // Edge not found - insert
+ if (ie == edgeCount)
+ {
+ Edge edge =
+ { vmin, vmax, ip, 0xffff, ivp, 0xffff };
+ edges.push_back (edge);
+ }
+ }
+ }
+}
+
+// Locate furthest vertex in positive half-plane or -1 in none found.
+int DynamicMesh::FindFurthest (const Vector3f& v1, const Vector3f& v2, const VertexContainer& vertices) const
+{
+ int bestIndex = -1;
+ float bestDist = 0;
+
+ for (size_t iv = 0; iv < vertices.size (); ++iv)
+ {
+ const float* v = vertices[iv].GetPtr ();
+ float dist = dtTriArea2D(v1.GetPtr (), v2.GetPtr (), v);
+ if (dist > bestDist)
+ {
+ bestDist = dist;
+ bestIndex = iv;
+ }
+ }
+ return bestIndex;
+}
+
+void DynamicMesh::Subtract (PolygonContainer& result, const Polygon& outer, const Polygon& inner) const
+{
+ const size_t innerVertexCount = inner.size ();
+ const size_t outerVertexCount = outer.size ();
+ result.clear ();
+ Polygon tri (3, kMemTempAlloc);
+
+ if (innerVertexCount == 1)
+ {
+ DebugAssert (outerVertexCount > 0);
+ for (size_t ov = 0; ov < outerVertexCount; ++ov)
+ {
+ const size_t ovn = NextIndex (ov, outerVertexCount);
+ tri[0] = outer[ov];
+ tri[1] = outer[ovn];
+ tri[2] = inner[0];
+ if (PolygonDegenerate (tri))
+ {
+ continue;
+ }
+ tri[2] = QuantizeVertex (inner[0]);
+ if (PolygonDegenerate (tri))
+ {
+ continue;
+ }
+ result.push_back (tri);
+ }
+ return;
+ }
+
+ dynamic_array< int > ol (innerVertexCount, -1, kMemTempAlloc);
+ dynamic_array< int > oh (innerVertexCount, -1, kMemTempAlloc);
+ dynamic_array< bool > used (outerVertexCount, false, kMemTempAlloc);
+
+ for (size_t ivp = innerVertexCount-1, iv = 0; iv < innerVertexCount; ivp = iv++)
+ {
+ int bestOuter = FindFurthest (inner[iv], inner[ivp], outer);
+
+ if (bestOuter == -1)
+ {
+ continue;
+ }
+ ol[iv] = bestOuter;
+ oh[ivp] = bestOuter;
+
+ tri[0] = inner[iv];
+ tri[1] = inner[ivp];
+ tri[2] = outer[bestOuter];
+
+ if (PolygonDegenerate (tri))
+ {
+ continue;
+ }
+ tri[0] = QuantizeVertex (inner[iv]);
+ tri[1] = QuantizeVertex (inner[ivp]);
+ if (PolygonDegenerate (tri))
+ {
+ continue;
+ }
+ result.push_back (tri);
+ }
+
+ for (size_t iv = 0; iv < innerVertexCount; ++iv)
+ {
+ if (ol[iv] != -1)
+ {
+ size_t ov = ol[iv];
+ size_t iter = 0;
+ while (ov != (size_t)oh[iv])
+ {
+ const size_t ovn = NextIndex (ov, outerVertexCount);
+ if (!used[ov])
+ {
+ tri[0] = outer[ov];
+ tri[1] = outer[ovn];
+ tri[2] = inner[iv];
+ if (PolygonDegenerate (tri))
+ {
+ break;
+ }
+ tri[2] = QuantizeVertex (inner[iv]);
+ if (PolygonDegenerate (tri))
+ {
+ break;
+ }
+ result.push_back (tri);
+ used[ov] = true;
+ }
+ ov = ovn;
+ if (++iter == outerVertexCount)
+ {
+ break;
+ }
+ }
+ }
+
+ if (oh[iv] != -1)
+ {
+ size_t ov = oh[iv];
+ size_t iter = 0;
+ while (ov != (size_t)ol[iv])
+ {
+ const size_t ovp = PrevIndex (ov, outerVertexCount);
+ if (!used[ovp])
+ {
+ tri[0] = outer[ovp];
+ tri[1] = outer[ov];
+ tri[2] = inner[iv];
+ if (PolygonDegenerate (tri))
+ {
+ break;
+ }
+ tri[2] = QuantizeVertex (inner[iv]);
+ if (PolygonDegenerate (tri))
+ {
+ break;
+ }
+ result.push_back (tri);
+ used[ovp] = true;
+ }
+ ov = ovp;
+ if (++iter == outerVertexCount)
+ {
+ break;
+ }
+ }
+ }
+ }
+}
+
+bool DynamicMesh::MergePolygons (Polygon& merged, const Polygon& p1, const Polygon& p2) const
+{
+ if (!MERGE_POLYGONS)
+ return false;
+ const size_t count1 = p1.size ();
+ const size_t count2 = p2.size ();
+
+ if (count1 < 3) return false;
+ if (count2 < 3) return false;
+ if ((count1 + count2 - 2) > NUM_VERTS)
+ return false;
+
+ for (size_t iv = 0; iv < count1; ++iv)
+ {
+ const size_t ivn = NextIndex (iv, count1);
+ const Vector3f& v1 = p1[iv];
+ const Vector3f& v2 = p1[ivn];
+ for (size_t jv = 0; jv < count2; ++jv)
+ {
+ const size_t jvn = NextIndex (jv, count2);
+ const Vector3f& w1 = p2[jv];
+ const Vector3f& w2 = p2[jvn];
+ if ((v1 == w2) && (v2 == w1))
+ {
+ // Found shared edge
+
+ // Test convexity
+ const Vector3f& wn = p2[NextIndex (jvn, count2)];
+ const Vector3f& vp = p1[PrevIndex (iv, count1)];
+ if (dtTriArea2D (vp.GetPtr (), v1.GetPtr (), wn.GetPtr ()) <= 0)
+ {
+ return false;
+ }
+
+ // Test convexity
+ const Vector3f& wp = p2[PrevIndex (jv, count2)];
+ const Vector3f& vn = p1[NextIndex (ivn, count1)];
+ if (dtTriArea2D (v2.GetPtr (), vn.GetPtr (), wp.GetPtr ()) <= 0)
+ {
+ return false;
+ }
+
+ // Merge two polygon parts
+ for (size_t k = ivn ; k != iv ; k = NextIndex (k, count1))
+ {
+ merged.push_back (p1[k]);
+ }
+ for (size_t k = jvn ; k != jv ; k = NextIndex (k, count2))
+ {
+ merged.push_back (p2[k]);
+ }
+ DebugAssert (merged.size () == count1 + count2 - 2);
+ return IsStrictlyConvex (merged);
+ }
+ }
+ }
+ return false;
+}
+
+void DynamicMesh::MergePolygons ()
+{
+ // Merge list of convex non-overlapping polygons assuming identical data.
+ for (size_t ip = 0; ip < m_Polygons.size (); ++ip)
+ {
+ Polygon poly;
+ FromPoly (poly, m_Polygons[ip]);
+ for (size_t jp = m_Polygons.size () - 1; jp > ip; --jp)
+ {
+ bool dataConforms = (m_Data[ip] == m_Data[jp]);
+ if (!dataConforms)
+ continue;
+
+ Polygon merged;
+ Polygon poly2;
+ FromPoly (poly2, m_Polygons[jp]);
+ if (MergePolygons (merged, poly, poly2))
+ {
+ poly = merged;
+ m_Polygons.erase (m_Polygons.begin () + jp);
+ }
+ if (poly.size () == NUM_VERTS) break;
+ }
+ m_Polygons[ip] = CreatePolygon (poly, kGeneratedPolygon);
+ }
+}
+
+void DynamicMesh::MergePolygons (PolygonContainer& polys)
+{
+ // Merge list of convex non-overlapping polygons assuming identical data.
+ for (size_t ip = 0; ip < polys.size (); ++ip)
+ {
+ Polygon poly = polys[ip];
+ for (size_t jp = polys.size ()-1; jp>ip; --jp)
+ {
+ Polygon merged;
+ if (MergePolygons (merged, poly, polys[jp]))
+ {
+ poly = merged;
+ polys.erase (polys.begin () + jp);
+ }
+ }
+ polys[ip] = poly;
+ }
+}
+
+void DynamicMesh::ConnectPolygons ()
+{
+ EdgeList edges;
+ BuildEdgeConnections (edges);
+
+ size_t edgeCount = edges.size ();
+ for (size_t ie = 0; ie < edgeCount; ++ie)
+ {
+ const Edge& edge = edges[ie];
+ if (edge.c2 == 0xffff)
+ continue;
+ m_Polygons[edge.p1].m_Neighbours[edge.c1] = edge.p2+1;
+ m_Polygons[edge.p2].m_Neighbours[edge.c2] = edge.p1+1;
+ }
+}
+
+void DynamicMesh::RemoveDegeneratePolygons ()
+{
+ size_t count = m_Polygons.size ();
+ for (size_t ip = 0; ip < count; ++ip)
+ {
+ if (PolygonDegenerate (m_Polygons[ip].m_VertexCount, m_Polygons[ip].m_VertexIDs, &m_Vertices[0]))
+ {
+ RemovePolygon (ip);
+ --count;
+ --ip;
+ }
+ }
+}
+
+void DynamicMesh::FindNeighbors ()
+{
+ RemoveDegeneratePolygons ();
+ ConnectPolygons ();
+}
+
+void DynamicMesh::AddPolygon (const Polygon& vertices, const DataType& data)
+{
+ AddPolygon (vertices, data, kOriginalPolygon);
+}
+
+void DynamicMesh::AddPolygon (const Polygon& vertices, const DataType& data, const UInt32 status)
+{
+ // Delaying neighbor connections.
+ DebugAssert (m_Polygons.size () < 0xffff); //< poly overflow
+ DebugAssert (vertices.size () <= NUM_VERTS);
+ DebugAssert (m_Data.size () == m_Polygons.size ());
+
+ if (PolygonDegenerate (vertices))
+ {
+ return;
+ }
+
+ DebugAssert (IsStrictlyConvex (vertices));
+
+ Poly newPoly = CreatePolygon (vertices, status);
+
+ // TODO: avoid leaking vertices when not accepting the poly
+ if (PolygonDegenerate (newPoly.m_VertexCount, newPoly.m_VertexIDs, &m_Vertices[0]))
+ {
+ return;
+ }
+ m_Polygons.push_back (newPoly);
+ m_Data.push_back (data);
+}
+
+bool DynamicMesh::ClipPolys (const HullContainer& carveHulls)
+{
+ size_t hullCount = carveHulls.size ();
+ PolygonContainer outsidePolygons;
+ bool clipped = false;
+
+ for (size_t ih = 0; ih < hullCount; ++ih)
+ {
+ Hull carveHull = carveHulls[ih];
+
+ size_t count = m_Polygons.size ();
+ for (size_t ip = 0; ip < count; ++ip)
+ {
+ Polygon currentPoly;
+ FromPoly (currentPoly, m_Polygons[ip]);
+
+ Polygon inside;
+ Intersection (inside, currentPoly, carveHull);
+ if (inside.empty ())
+ continue;
+
+ clipped = true;
+
+ Subtract (outsidePolygons, currentPoly, inside);
+ MergePolygons (outsidePolygons);
+
+ DataType currentData = m_Data[ip];
+ RemovePolygon (ip);
+ --count;
+ --ip;
+
+ for (size_t io = 0; io < outsidePolygons.size (); ++io)
+ {
+ AddPolygon (outsidePolygons[io], currentData, kGeneratedPolygon);
+ }
+ }
+ }
+ return clipped;
+}
+
+void DynamicMesh::Reserve (const int vertexCount, const int polygonCount)
+{
+ m_Polygons.reserve (polygonCount);
+ m_Data.reserve (polygonCount);
+ m_Vertices.reserve (vertexCount);
+}
+
+void DynamicMesh::AddVertex (const Vector3f& v)
+{
+ const Vector3f qv = QuantizeVertex (v);
+ m_Vertices.push_back (qv);
+}
+
+void DynamicMesh::AddPolygon (const UInt16* vertexIDs, const DataType& data, size_t vertexCount)
+{
+ // TODO : figure out why this needs to be zero'ed
+ Poly poly = {{0}, {0}, 0, 0};
+
+ poly.m_Status = kOriginalPolygon;
+ poly.m_VertexCount = vertexCount;
+ for (size_t iv = 0; iv < vertexCount; ++iv)
+ {
+ poly.m_VertexIDs[iv] = vertexIDs[iv];
+ }
+ m_Polygons.push_back (poly);
+ m_Data.push_back (data);
+}
+
+
diff --git a/Runtime/NavMesh/DynamicMesh.h b/Runtime/NavMesh/DynamicMesh.h
new file mode 100644
index 0000000..f815c66
--- /dev/null
+++ b/Runtime/NavMesh/DynamicMesh.h
@@ -0,0 +1,121 @@
+#ifndef _DYNAMICMESH_H_INCLUDED_
+#define _DYNAMICMESH_H_INCLUDED_
+
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include <vector>
+
+// TODO handle T-junctions (produced by merging / culling degenerate polys).
+// TODO cleanup orphan vertices (remap indices)
+// TODO optimize using bv-tree to collect source polygons for carving
+// TODO re-create bv-tree for faster lookup (possibly by modifying source bv-tree).
+
+class DynamicMesh
+{
+ struct Edge
+ {
+ UInt16 v1, v2, p1, p2, c1, c2;
+ };
+ typedef dynamic_array< Edge > EdgeList;
+public:
+
+ enum
+ {
+ NUM_VERTS = 6
+ };
+ enum
+ {
+ kOriginalPolygon = 0,
+ kGeneratedPolygon = 1
+ };
+ struct Poly
+ {
+ UInt16 m_Neighbours[NUM_VERTS];
+ UInt16 m_VertexIDs[NUM_VERTS];
+ UInt32 m_VertexCount;
+ UInt32 m_Status;
+ };
+
+ typedef int DataType;
+ typedef dynamic_array< Plane > Hull;
+ typedef std::vector< Hull > HullContainer;
+
+ typedef dynamic_array< Vector3f > VertexContainer;
+ typedef VertexContainer Polygon;
+ typedef std::vector< Polygon > PolygonContainer;
+
+ inline void Clear ();
+ inline size_t PolyCount () const;
+ inline size_t VertCount () const;
+ inline const float* GetVertex (size_t i) const;
+ inline const Poly* GetPoly (size_t i) const;
+ inline const DataType* GetData (size_t i) const;
+
+ void MergePolygons ();
+ void FindNeighbors ();
+ void AddPolygon (const Polygon& vertices, const DataType& data);
+ bool ClipPolys (const HullContainer& carveHulls);
+
+ void Reserve (const int vertexCount, const int polygonCount);
+ void AddVertex (const Vector3f& v);
+ void AddPolygon (const UInt16* vertexIDs, const DataType& data, size_t vertexCount);
+
+private:
+ size_t AddVertexChecked (const Vector3f& v);
+ void AddPolygon (const Polygon& vertices, const DataType& data, const UInt32 status);
+ Poly CreatePolygon (const Polygon& vertices, const UInt32 status);
+ void RemovePolygon (size_t i);
+
+ void Intersection (Polygon& inside, const Polygon& poly, const Hull& clipHull) const;
+ void SplitPoly (Polygon& inside, const Polygon& poly, const Plane& plane) const;
+ void FromPoly (Polygon& result, const Poly& poly) const;
+ void BuildEdgeConnections (EdgeList& edges) const;
+ int FindFurthest (const Vector3f& v1, const Vector3f& v2, const VertexContainer& vertices) const;
+ void Subtract (PolygonContainer& result, const Polygon& outer, const Polygon& inner) const;
+ void ConnectPolygons ();
+ void RemoveDegeneratePolygons ();
+ void MergePolygons (PolygonContainer& polys);
+ bool MergePolygons (Polygon& merged, const Polygon& p1, const Polygon& p2) const;
+
+ dynamic_array<Poly> m_Polygons;
+ dynamic_array<Vector3f> m_Vertices;
+ dynamic_array<DataType> m_Data;
+};
+
+inline void DynamicMesh::Clear ()
+{
+ m_Polygons.resize_uninitialized (0);
+ m_Vertices.resize_uninitialized (0);
+ m_Data.resize_uninitialized (0);
+}
+
+inline size_t DynamicMesh::PolyCount () const
+{
+ return m_Polygons.size ();
+}
+
+inline size_t DynamicMesh::VertCount () const
+{
+ return m_Vertices.size ();
+}
+
+inline const float* DynamicMesh::GetVertex (size_t i) const
+{
+ DebugAssert (i < VertCount ());
+ return m_Vertices[i].GetPtr ();
+}
+
+inline const DynamicMesh::Poly* DynamicMesh::GetPoly (size_t i) const
+{
+ DebugAssert (i < PolyCount ());
+ return &m_Polygons[i];
+}
+
+inline const DynamicMesh::DataType* DynamicMesh::GetData (size_t i) const
+{
+ DebugAssert (i < PolyCount ());
+ return &m_Data[i];
+}
+
+#endif
diff --git a/Runtime/NavMesh/DynamicMeshTests.cpp b/Runtime/NavMesh/DynamicMeshTests.cpp
new file mode 100644
index 0000000..8f4d230
--- /dev/null
+++ b/Runtime/NavMesh/DynamicMeshTests.cpp
@@ -0,0 +1,280 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_UNIT_TESTS
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "DynamicMesh.h"
+
+SUITE (DynamicMeshTests)
+{
+ struct DynamicMeshTestFixture
+ {
+ DynamicMeshTestFixture ()
+ {
+ data = 0;
+ data2 = 2;
+
+ goodPolygon.push_back (Vector3f (0,0,0));
+ goodPolygon.push_back (Vector3f (0,0,1));
+ goodPolygon.push_back (Vector3f (1,0,1));
+
+ goodPolygonNeighbor.push_back (Vector3f (0,0,0));
+ goodPolygonNeighbor.push_back (Vector3f (1,0,1));
+ goodPolygonNeighbor.push_back (Vector3f (1,0,0));
+
+ upsideDownPolygon.push_back (Vector3f (0,0,0));
+ upsideDownPolygon.push_back (Vector3f (1,0,1));
+ upsideDownPolygon.push_back (Vector3f (0,0,1));
+
+ degeneratePolygon.push_back (Vector3f (0,0,0));
+ degeneratePolygon.push_back (Vector3f (1,0,0));
+ degeneratePolygon.push_back (Vector3f (2,0,0));
+
+ degeneratePolygon2.push_back (Vector3f (0,0,0));
+ degeneratePolygon2.push_back (Vector3f (1,0,0));
+ }
+
+ DynamicMesh mesh;
+ unsigned char data;
+ unsigned char data2;
+
+ DynamicMesh::Polygon goodPolygon;
+ DynamicMesh::Polygon goodPolygonNeighbor;
+ DynamicMesh::Polygon degeneratePolygon;
+ DynamicMesh::Polygon degeneratePolygon2;
+ DynamicMesh::Polygon upsideDownPolygon;
+ };
+
+ // Create a container for DynamicMesh to use for carving.
+ // Hull is a simple half-space defined by world-space position and normal.
+ DynamicMesh::HullContainer HullsFromNormalAndPosition (const Vector3f& normal, const Vector3f& position)
+ {
+ Plane plane;
+ plane.SetNormalAndPosition (normal, position);
+
+ DynamicMesh::Hull hull;
+ hull.push_back (plane);
+
+ DynamicMesh::HullContainer hulls;
+ hulls.push_back (hull);
+
+ return hulls;
+ }
+
+ TEST_FIXTURE (DynamicMeshTestFixture, Construction)
+ {
+ CHECK (mesh.PolyCount () == 0);
+ CHECK (mesh.VertCount () == 0);
+ }
+
+ TEST_FIXTURE (DynamicMeshTestFixture, AddPolygon)
+ {
+ mesh.AddPolygon (goodPolygon, data);
+
+ CHECK (mesh.PolyCount () == 1);
+ CHECK (mesh.VertCount () == 3);
+ }
+
+ TEST_FIXTURE (DynamicMeshTestFixture, AddPolygon_IgnoreDegeneratePolygon)
+ {
+ mesh.AddPolygon (degeneratePolygon, data);
+ mesh.AddPolygon (degeneratePolygon2, data);
+
+ CHECK (mesh.PolyCount () == 0);
+ CHECK (mesh.VertCount () == 0);
+ }
+
+ TEST_FIXTURE (DynamicMeshTestFixture, AddPolygon_IgnoreUpsideDown)
+ {
+ mesh.AddPolygon (upsideDownPolygon, data);
+
+ CHECK (mesh.PolyCount () == 0);
+ CHECK (mesh.VertCount () == 0);
+ }
+
+ TEST_FIXTURE (DynamicMeshTestFixture, AddPolygon_SameTwice)
+ {
+ mesh.AddPolygon (goodPolygon, data);
+ mesh.AddPolygon (goodPolygon, data);
+
+ CHECK (mesh.PolyCount () == 2);
+ CHECK (mesh.VertCount () == 3);
+ }
+
+ TEST_FIXTURE (DynamicMeshTestFixture, MergePolygonsWithSameData)
+ {
+ mesh.AddPolygon (goodPolygon, data);
+ mesh.AddPolygon (goodPolygonNeighbor, data);
+ mesh.MergePolygons ();
+
+ CHECK (mesh.PolyCount () == 1);
+ CHECK (mesh.VertCount () == 4);
+ }
+
+ TEST_FIXTURE (DynamicMeshTestFixture, DontMergePolygonsWithDifferentData)
+ {
+ mesh.AddPolygon (goodPolygon, data);
+ mesh.AddPolygon (goodPolygonNeighbor, data2);
+ mesh.MergePolygons ();
+
+ CHECK (mesh.PolyCount () == 2);
+ CHECK (mesh.VertCount () == 4);
+ }
+
+ // Verify mesh contains exactly one triangle. Return the area weighted normal.
+ // ie. vector in direction of the triangle normal - with a magnitude equal to the triangle area.
+ Vector3f CheckSingleTriangleGetAreaNormal (DynamicMesh& mesh)
+ {
+ // Verify a single polygon is left
+ CHECK (mesh.PolyCount () == 1);
+
+ // .. and it's a triangle
+ const DynamicMesh::Poly* poly = mesh.GetPoly (0);
+ CHECK (poly->m_VertexCount == 3);
+
+ const Vector3f v0 = Vector3f (mesh.GetVertex (poly->m_VertexIDs[0]));
+ const Vector3f v1 = Vector3f (mesh.GetVertex (poly->m_VertexIDs[1]));
+ const Vector3f v2 = Vector3f (mesh.GetVertex (poly->m_VertexIDs[2]));
+ const Vector3f triangleAreaNormal = 0.5f*Cross (v1 - v0, v2 - v0);
+ return triangleAreaNormal;
+ }
+
+ TEST_FIXTURE (DynamicMeshTestFixture, ClipTriangleWithPlane_Result_ClippedTriangle)
+ {
+ // Cut everything z > 0.5f
+ DynamicMesh::HullContainer carveHulls = HullsFromNormalAndPosition (-Vector3f::zAxis, Vector3f (0.0f, 0.0f, 0.5f));
+
+ mesh.AddPolygon (goodPolygon, data);
+ mesh.ClipPolys (carveHulls);
+
+ // Expect a triangle in the horizontal plane with area 1/8
+ const Vector3f expectedAreaNormal = Vector3f (0.0f, 0.125f, 0.0f);
+ const Vector3f triangleAreaNormal = CheckSingleTriangleGetAreaNormal (mesh);
+
+ CHECK (CompareApproximately (expectedAreaNormal, triangleAreaNormal));
+ }
+
+ TEST_FIXTURE (DynamicMeshTestFixture, ClipTriangleWithPlane_Result_OriginalTriangle)
+ {
+ // Cut everything z > 1.0f
+ DynamicMesh::HullContainer carveHulls = HullsFromNormalAndPosition (-Vector3f::zAxis, Vector3f (0.0f, 0.0f, 1.0f));
+
+ mesh.AddPolygon (goodPolygon, data);
+
+ mesh.ClipPolys (carveHulls);
+
+ // Expect a triangle in the horizontal plane with area 1/2
+ const Vector3f expectedAreaNormal = Vector3f (0.0f, 0.5f, 0.0f);
+ const Vector3f triangleAreaNormal = CheckSingleTriangleGetAreaNormal (mesh);
+ CHECK (CompareApproximately (expectedAreaNormal, triangleAreaNormal));
+ }
+
+ TEST_FIXTURE (DynamicMeshTestFixture, ClipTriangleWithPlane_Result_NoTriangle)
+ {
+ // Cut everything z > 0
+ DynamicMesh::HullContainer carveHulls = HullsFromNormalAndPosition (-Vector3f::zAxis, Vector3f (0.0f, 0.0f, 0.0f));
+
+ mesh.AddPolygon (goodPolygon, data);
+ mesh.ClipPolys (carveHulls);
+
+ // Verify that the polygon is removed
+ CHECK (mesh.PolyCount () == 0);
+ }
+
+ TEST_FIXTURE (DynamicMeshTestFixture, SplitTriangleIntoTwoPolygons)
+ {
+ // Split polygon into two
+ Vector3f planePos(0.0f, 0.0f, 0.5f);
+ Plane planeLeft, planeRight;
+ planeLeft.SetNormalAndPosition (-Vector3f::zAxis, planePos);
+ planeRight.SetNormalAndPosition (Vector3f::zAxis, planePos);
+
+ DynamicMesh::Hull hull;
+ hull.push_back (planeLeft);
+ hull.push_back (planeRight);
+
+ DynamicMesh::HullContainer carveHulls;
+ carveHulls.push_back (hull);
+
+ mesh.AddPolygon (goodPolygon, data);
+ mesh.ClipPolys (carveHulls);
+
+ // Verify that the polygon is cut in half
+ CHECK (mesh.PolyCount () == 2);
+ }
+
+ static bool HasNeighbor (const DynamicMesh::Poly* poly, int neighborId)
+ {
+ for (int i = 0; i < poly->m_VertexCount; i++)
+ if (poly->m_Neighbours[i] == neighborId)
+ return true;
+ return false;
+ }
+
+ TEST_FIXTURE (DynamicMeshTestFixture, CheckMeshConnectivity)
+ {
+ mesh.AddPolygon (goodPolygon, data);
+ mesh.AddPolygon (goodPolygonNeighbor, data2);
+ mesh.MergePolygons ();
+ mesh.FindNeighbors ();
+
+ CHECK (mesh.PolyCount () == 2);
+ CHECK (mesh.VertCount () == 4);
+
+ // Check that polygon A is connected to polygon B.
+ const DynamicMesh::Poly* pa = mesh.GetPoly (0);
+ CHECK (HasNeighbor (pa, 2)); // One based neighbor indices.
+
+ // Check that polygon B is connected to polygon A.
+ const DynamicMesh::Poly* pb = mesh.GetPoly (1);
+ CHECK (HasNeighbor (pb, 1));
+ }
+
+
+ static DynamicMesh::Hull HullFromPolygon (const dynamic_array<Vector3f>& points)
+ {
+ DynamicMesh::Hull hull;
+ for (int i = 0, j = points.size()-1; i < points.size(); j = i++)
+ {
+ Vector3f edgeDir = points[i] - points[j];
+ Vector3f edgeNormal = Normalize (Vector3f (-edgeDir.z, 0.0f, edgeDir.x));
+ Plane plane;
+ plane.SetNormalAndPosition (edgeNormal, points[j]);
+ hull.push_back (plane);
+ }
+ return hull;
+ }
+
+ TEST_FIXTURE (DynamicMeshTestFixture, CutTriangleWithRectangle)
+ {
+ // Cut triangle with rectangle
+ //
+ // o------o
+ // | x../..x
+ // | :/ :
+ // | /: :
+ // o x.....x
+ //
+ dynamic_array<Vector3f> points;
+ points.push_back (Vector3f (0.25f, 0, 0));
+ points.push_back (Vector3f (0.25f, 0, 0.75f));
+ points.push_back (Vector3f (1.0f, 0, 0.75f));
+ points.push_back (Vector3f (1.0f, 0, 0));
+ DynamicMesh::Hull hull = HullFromPolygon (points);
+
+ DynamicMesh::HullContainer carveHulls;
+ carveHulls.push_back (hull);
+
+ mesh.AddPolygon (goodPolygon, data);
+ mesh.ClipPolys (carveHulls);
+
+ // Verify that there are more polygons after clipping, because result is non-convex.
+ CHECK (mesh.PolyCount () > 1);
+ // Verify that there are 6 vertices.
+ CHECK (mesh.VertCount() == 6);
+ }
+
+
+}
+
+#endif
diff --git a/Runtime/NavMesh/HeightMeshQuery.cpp b/Runtime/NavMesh/HeightMeshQuery.cpp
new file mode 100644
index 0000000..2e26b6b
--- /dev/null
+++ b/Runtime/NavMesh/HeightMeshQuery.cpp
@@ -0,0 +1,134 @@
+#include "UnityPrefix.h"
+#include "HeightMeshQuery.h"
+#include "Runtime/Interfaces/ITerrainManager.h"
+
+
+HeightMeshQuery::HeightMeshQuery ()
+: m_HeightMaps (NULL)
+, m_VerticalRayOffset (0.0f)
+{
+}
+
+HeightMeshQuery::~HeightMeshQuery ()
+{
+}
+
+void HeightMeshQuery::Init (const HeightmapDataVector* heightMaps, float verticalRayOffset)
+{
+ m_HeightMaps = heightMaps;
+ m_VerticalRayOffset = verticalRayOffset;
+}
+
+dtStatus HeightMeshQuery::getHeight (const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* height) const
+{
+ Vector3f rayStart (pos[0], pos[1] + m_VerticalRayOffset, pos[2]);
+ float geometryHeight, terrainHeight;
+ bool bGeometryHit = GetGeometryHeight (tile, poly, rayStart, &geometryHeight);
+ bool bTerrainHit = GetTerrainHeight (rayStart, &terrainHeight);
+
+ if (bGeometryHit && bTerrainHit)
+ {
+ float geomDist = Abs (rayStart.y - geometryHeight);
+ float terrainDist = Abs (rayStart.y - terrainHeight);
+ if (geomDist < terrainDist)
+ (*height) = geometryHeight;
+ else
+ (*height) = terrainHeight;
+
+ return DT_SUCCESS;
+ }
+ else if (bGeometryHit)
+ {
+ (*height) = geometryHeight;
+ return DT_SUCCESS;
+ }
+ else if (bTerrainHit)
+ {
+ (*height) = terrainHeight;
+ return DT_SUCCESS;
+ }
+ else
+ {
+ (*height) = pos[1];
+ return DT_FAILURE;
+ }
+}
+
+
+// HeightMesh Intersection
+// Find the height of the nearest triangle with the same 2D values.
+bool HeightMeshQuery::GetGeometryHeight (const dtMeshTile* tile, const dtPoly* poly, const Vector3f& pos, float* height) const
+{
+ Assert (poly->getType () == DT_POLYTYPE_GROUND);
+ Assert (height != NULL);
+ *height = pos.y;
+
+ bool hit = false;
+ float bestHeight = std::numeric_limits<float>::infinity ();
+
+ const unsigned int ip = (unsigned int)(poly - tile->polys);
+ const dtPolyDetail* pd = &tile->detailMeshes[ip];
+ for (int j = 0; j < pd->triCount; ++j)
+ {
+ const dtPolyDetailIndex* t = &tile->detailTris[(pd->triBase+j)*4];
+ const float* v[3];
+ for (int k = 0; k < 3; ++k)
+ {
+ if (t[k] < poly->vertCount)
+ v[k] = &tile->verts[poly->verts[t[k]]*3];
+ else
+ v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
+ }
+ float h;
+ if (dtClosestHeightPointTriangle (pos.GetPtr (), v[0], v[1], v[2], h))
+ {
+ if (Abs (pos.y - h) < Abs (pos.y - bestHeight))
+ {
+ *height = h;
+ bestHeight = h;
+ hit = true;
+ }
+ }
+ }
+
+ return hit;
+}
+
+bool HeightMeshQuery::GetTerrainHeight (const Vector3f& rayStart, float* height) const
+{
+ Assert (height != NULL);
+ *height = rayStart.y;
+
+ if (!m_HeightMaps)
+ return false;
+
+ ITerrainManager* terrain = GetITerrainManager ();
+ if (terrain == NULL)
+ return false;
+
+ bool hit = false;
+ const float upperBound = rayStart.y;
+ float lowerBound = -std::numeric_limits<float>::infinity ();
+
+
+ for (int i = 0; i < m_HeightMaps->size (); ++i)
+ {
+ const HeightmapData& heightmapData = (*m_HeightMaps)[i];
+ float terrainHeight;
+
+ // TODO: this should be cleaned up. Abstracting the Terrain-manager away only to feed it
+ // (serialized!) PPtr's to terrain data is a half-assed abstraction at best.
+ Object* terrainData = InstanceIDToObjectThreadSafe (heightmapData.terrainData.GetInstanceID ());
+ bool hitTerrain = terrain->GetInterpolatedHeight (terrainData, heightmapData.position, rayStart, terrainHeight);
+ if (!hitTerrain)
+ continue;
+
+ if (terrainHeight < upperBound && terrainHeight > lowerBound)
+ {
+ (*height) = terrainHeight;
+ lowerBound = terrainHeight;
+ hit = true;
+ }
+ }
+ return hit;
+}
diff --git a/Runtime/NavMesh/HeightMeshQuery.h b/Runtime/NavMesh/HeightMeshQuery.h
new file mode 100644
index 0000000..1654ba2
--- /dev/null
+++ b/Runtime/NavMesh/HeightMeshQuery.h
@@ -0,0 +1,28 @@
+#ifndef RUNTIME_HEIGHT_MESH_QUERY
+#define RUNTIME_HEIGHT_MESH_QUERY
+
+#include "External/Recast/Detour/Include/DetourNavMeshQuery.h"
+#include "HeightmapData.h"
+
+class dtNavMeshQuery;
+class Vector3f;
+
+// Query specialization for Height Placement of a HeightMesh.
+class HeightMeshQuery : public dtHeightQuery
+{
+public:
+ HeightMeshQuery ();
+ virtual ~HeightMeshQuery ();
+
+ void Init (const HeightmapDataVector* heightMaps, float verticalRayOffset);
+ virtual dtStatus getHeight (const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* height) const;
+ bool GetTerrainHeight (const Vector3f& position, float* height) const;
+
+private:
+ bool GetGeometryHeight (const dtMeshTile* tile, const dtPoly* poly, const Vector3f& pos, float* height) const;
+
+ const HeightmapDataVector* m_HeightMaps;
+ float m_VerticalRayOffset;
+};
+
+#endif
diff --git a/Runtime/NavMesh/HeightmapData.h b/Runtime/NavMesh/HeightmapData.h
new file mode 100644
index 0000000..a6cd65d
--- /dev/null
+++ b/Runtime/NavMesh/HeightmapData.h
@@ -0,0 +1,26 @@
+#ifndef HEIGHTMAPDATA_H
+#define HEIGHTMAPDATA_H
+
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+struct HeightmapData
+{
+ DECLARE_SERIALIZE (HeightmapData)
+
+ Vector3f position;
+ PPtr<Object> terrainData;
+};
+
+typedef dynamic_array<HeightmapData> HeightmapDataVector;
+
+template<class TransferFunction>
+void HeightmapData::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (position);
+ TRANSFER (terrainData);
+}
+
+#endif
diff --git a/Runtime/NavMesh/NavMesh.cpp b/Runtime/NavMesh/NavMesh.cpp
new file mode 100644
index 0000000..c9af244
--- /dev/null
+++ b/Runtime/NavMesh/NavMesh.cpp
@@ -0,0 +1,446 @@
+#include "UnityPrefix.h"
+#include "NavMesh.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "DetourNavMeshQuery.h"
+#include "NavMeshManager.h"
+#include "NavMeshPath.h"
+#include "NavMeshLayers.h"
+#include "DetourAlloc.h"
+#include "DetourSwapEndian.h"
+#include "HeightMeshQuery.h"
+
+/*
+ TODO:
+
+ 1) Tile boundary coordinates depend on the global bounding box (worldMin + n*size) - make it independent of the global extents (eg. use origo).
+
+ 2) make remainingDistance return distance in all the time (not infinity).
+
+ 3) NavMesh baking bounding box and seed points to cull poly-count.
+
+ 4) Support non-cylinder avoidance obstacles (eg. box).
+
+ 5) Support cylinder shaped obstacle carving.
+
+ 6) Line-to-line offmeshlink type (in addition to the current point-to-point)
+
+*/
+
+NavMesh::NavMesh (MemLabelId& label, ObjectCreationMode mode)
+: Super (label, mode)
+, m_NavMesh (NULL)
+, m_NavMeshQuery (NULL)
+, m_HeightMeshQuery (NULL)
+{
+}
+
+NavMesh::~NavMesh ()
+{
+ Cleanup ();
+}
+
+int NavMesh::CalculatePolygonPath (NavMeshPath* path, const Vector3f& sourcePosition, const Vector3f& targetPosition, const dtQueryFilter& filter)
+{
+ if (m_NavMeshQuery==NULL)
+ return 0;
+
+ float targetMappedPos[3];
+ float sourceMappedPos[3];
+ dtNavMeshQuery* query = m_NavMeshQuery;
+ const float* ext = m_QueryExtents.GetPtr ();
+
+ dtPolyRef targetPolyRef;
+ query->findNearestPoly (targetPosition.GetPtr (), ext, &filter, &targetPolyRef, targetMappedPos);
+ if (targetPolyRef == 0)
+ return 0;
+
+ dtPolyRef sourcePolyRef;
+ query->findNearestPoly (sourcePosition.GetPtr (), ext, &filter, &sourcePolyRef, sourceMappedPos);
+ if (sourcePolyRef == 0)
+ return 0;
+
+ int polygonCount = 0;
+
+ // TODO: Cache an up-to-date filter in navmesh (or manager) to avoid this copy
+ dtQueryFilter filter2 = filter;
+ const NavMeshLayers& layers = GetNavMeshLayers ();
+ for (int i = 0; i < NavMeshLayers::kLayerCount; ++i)
+ filter2.setAreaCost (i, layers.GetLayerCost (i));
+
+ dtStatus status = query->initSlicedFindPath (sourcePolyRef, targetPolyRef, sourceMappedPos, targetMappedPos, &filter2);
+ if (!dtStatusFailed (status))
+ status = query->updateSlicedFindPath (65535, NULL);
+ if (!dtStatusFailed (status))
+ status = query->finalizeSlicedFindPath (path->GetPolygonPath (), &polygonCount, NavMeshPath::kMaxPathPolygons);
+
+ path->SetTimeStamp (m_NavMesh->getTimeStamp ());
+ path->SetPolygonCount (polygonCount);
+ path->SetSourcePosition (Vector3f (sourceMappedPos));
+ path->SetTargetPosition (Vector3f (targetMappedPos));
+ if (dtStatusFailed (status) || polygonCount == 0)
+ {
+ path->SetStatus (kPathInvalid);
+ return 0;
+ }
+
+ if (dtStatusDetail (status, DT_PARTIAL_RESULT))
+ {
+ // when path is partial we project the target position
+ // to the last polygon in the path.
+
+ const dtPolyRef* polygonPath = path->GetPolygonPath ();
+ const dtPolyRef lastPolyRef = polygonPath[polygonCount-1];
+ Vector3f partialTargetPos;
+ dtStatus status = query->closestPointOnPoly (lastPolyRef, targetMappedPos, partialTargetPos.GetPtr ());
+ if (dtStatusFailed (status))
+ {
+ path->SetStatus (kPathInvalid);
+ return 0;
+ }
+
+ path->SetStatus (kPathPartial);
+ path->SetTargetPosition (partialTargetPos);
+ }
+ else
+ {
+ path->SetStatus (kPathComplete);
+ }
+
+ return polygonCount;
+}
+
+int NavMesh::CalculatePathCorners (Vector3f* corners, int maxCorners, const NavMeshPath& path)
+{
+ if (m_NavMeshQuery==NULL)
+ return 0;
+
+ const dtNavMeshQuery* query = m_NavMeshQuery;
+
+ int cornerCount = 0;
+ dtStatus result;
+ Vector3f sourcePos = path.GetSourcePosition ();
+ Vector3f targetPos = path.GetTargetPosition ();
+
+ result = query->findStraightPath (sourcePos.GetPtr (), targetPos.GetPtr (),
+ path.GetPolygonPath (), path.GetPolygonCount (),
+ corners[0].GetPtr (), NULL, NULL, &cornerCount, maxCorners);
+ if (result != DT_SUCCESS)
+ return 0;
+ return cornerCount;
+}
+
+void NavMesh::Triangulate (NavMesh::Triangulation& triangulation) const
+{
+ // Mapping between old and new vertex indices.
+ typedef std::map<UInt16, SInt32> VertexMap;
+
+ dynamic_array<SInt32>& layers = triangulation.layers;
+ dynamic_array<SInt32>& indices = triangulation.indices;
+ dynamic_array<Vector3f>& vertices = triangulation.vertices;
+
+ indices.clear ();
+ vertices.clear ();
+
+ const size_t tileCount = m_NavMesh->tileCount ();
+ for (size_t it = 0; it < tileCount; ++it)
+ {
+ const dtMeshTile* tile = m_NavMesh->getTile (it);
+ if (tile == NULL || tile->header == NULL)
+ continue;
+
+ for (size_t ip = 0; ip < tile->header->polyCount; ++ip)
+ {
+ const dtPoly& p = tile->polys[ip];
+ const size_t polyVertCount = p.vertCount;
+
+ // Ignore irregular polygons.
+ if (polyVertCount < 3)
+ continue;
+
+ VertexMap vertexMap;
+
+ // Find or update new vertex index.
+ for (size_t iv = 0; iv < polyVertCount; ++iv)
+ {
+ UInt16 vi = p.verts[iv];
+ VertexMap::iterator found = vertexMap.find (vi);
+ if (found != vertexMap.end ())
+ continue;
+
+ // Lookup the height for vertex
+ dtPolyRef ref = m_NavMesh->getPolyRefBase (tile)|(dtPolyRef)ip;
+ Vector3f vertex = Vector3f (&tile->verts[3*vi]);
+ float ypos;
+ m_NavMeshQuery->getPolyHeight (ref, vertex.GetPtr (), &ypos);
+ vertex.y = ypos;
+
+ vertexMap[vi] = vertices.size ();
+ vertices.push_back (vertex);
+ }
+
+ // Add triangles for this polygon.
+ const SInt32 v0 = vertexMap[p.verts[0]];
+ SInt32 v1 = vertexMap[p.verts[1]];
+ for (size_t iv = 2; iv < polyVertCount; ++iv)
+ {
+ const SInt32 v2 = vertexMap[p.verts[iv]];
+ indices.push_back (v0);
+ indices.push_back (v1);
+ indices.push_back (v2);
+
+ v1 = v2;
+ }
+
+ // Add the navmesh layer for each triangle
+ for (size_t iv = 2; iv < polyVertCount; ++iv)
+ {
+ layers.push_back (p.getArea ());
+ }
+ }
+ }
+}
+
+bool NavMesh::Raycast (NavMeshHit* hit, const Vector3f& sourcePosition, const Vector3f& targetPosition, const dtQueryFilter& filter)
+{
+ dtPolyRef mappedPolyRef;
+ Vector3f mappedPosition;
+ if (!MapPosition (&mappedPolyRef, &mappedPosition, sourcePosition, m_QueryExtents, filter))
+ {
+ InvalidateNavMeshHit (hit);
+ return false;
+ }
+
+ float st;
+ float hitNormal[3];
+ unsigned int hitPolyFlags;
+ float height;
+ dtStatus result = m_NavMeshQuery->simpleRaycast (mappedPolyRef, mappedPosition.GetPtr (), targetPosition.GetPtr (), &filter, &st, hitNormal, &hitPolyFlags, NULL, &height);
+ if (dtStatusFailed (result))
+ {
+ InvalidateNavMeshHit (hit);
+ return false;
+ }
+
+ if (st<1.0f)
+ {
+ hit->mask = hitPolyFlags;
+ hit->hit = true;
+ hit->position = Lerp (mappedPosition, targetPosition, st);
+ }
+ else
+ {
+ hit->mask = 0;
+ hit->hit = false;
+ hit->position = targetPosition;
+ }
+
+ hit->position.y = height;
+ hit->distance = Magnitude (hit->position - sourcePosition);
+ hit->normal = Vector3f (hitNormal);
+
+ return hit->hit;
+}
+
+bool NavMesh::SamplePosition (NavMeshHit* hit, const Vector3f& sourcePosition, const dtQueryFilter& filter, float maxDistance)
+{
+ dtPolyRef mappedPolyRef;
+ Vector3f mappedPosition;
+ const Vector3f extents = Vector3f (maxDistance, maxDistance, maxDistance);
+ if (!MapPosition (&mappedPolyRef, &mappedPosition, sourcePosition, extents, filter))
+ {
+ InvalidateNavMeshHit (hit);
+ return false;
+ }
+
+ const float distance = Magnitude (mappedPosition - sourcePosition);
+ if (distance > maxDistance)
+ {
+ InvalidateNavMeshHit (hit);
+ return false;
+ }
+
+ hit->mask = m_NavMeshQuery->getPolygonFlags (mappedPolyRef);
+ hit->hit = true;
+ hit->position = mappedPosition;
+ hit->distance = distance;
+ hit->normal = Vector3f::zero;
+ return true;
+}
+
+bool NavMesh::DistanceToEdge (NavMeshHit* hit, const Vector3f& sourcePosition, const dtQueryFilter& filter)
+{
+ dtPolyRef mappedPolyRef;
+ Vector3f mappedPosition;
+ if (!MapPosition (&mappedPolyRef, &mappedPosition, sourcePosition, m_QueryExtents, filter))
+ {
+ InvalidateNavMeshHit (hit);
+ return false;
+ }
+
+ const dtStatus status = m_NavMeshQuery->findEdge (mappedPolyRef, mappedPosition.GetPtr (), &filter, &hit->mask, hit->normal.GetPtr (), hit->position.GetPtr ());
+ if (dtStatusFailed (status))
+ {
+ InvalidateNavMeshHit (hit);
+ return false;
+ }
+ hit->distance = Magnitude (hit->position - sourcePosition);
+ hit->hit = true;
+
+ return true;
+}
+
+bool NavMesh::MapPosition (dtPolyRef* mappedPolyRef, Vector3f* mappedPosition, const Vector3f& position, const Vector3f& extents, const dtQueryFilter& filter) const
+{
+ if (!m_NavMeshQuery)
+ return false;
+
+ m_NavMeshQuery->findNearestPoly (position.GetPtr (), extents.GetPtr (), &filter, mappedPolyRef, mappedPosition->GetPtr ());
+ const dtPolyRef ref = *mappedPolyRef;
+ return ref != 0;
+}
+
+void NavMesh::Create ()
+{
+ Cleanup ();
+
+ if (m_MeshData.empty ())
+ return;
+
+ Assert (m_NavMesh == NULL);
+ m_NavMesh = dtAllocNavMesh ();
+ if (!m_NavMesh)
+ return CleanupWithError ();
+
+ dtStatus status;
+
+ // try loading tile data
+ status = m_NavMesh->initTiles (GetMeshData (), GetMeshDataSize ());
+ if (dtStatusFailed (status))
+ return CleanupWithError ();
+ if (m_NavMesh->tileCount () == 0)
+ return Cleanup ();
+
+ ////@TODO: WTF IS THIS MAGIC NUMBER???
+ Assert (m_NavMeshQuery == NULL);
+ m_NavMeshQuery = dtAllocNavMeshQuery (m_NavMesh, 2048);
+ if (m_NavMeshQuery == NULL)
+ return CleanupWithError ();
+
+ // @TODO: remove tile header redundancy and enforce identical tile parameters.
+ const dtMeshTile* tile = m_NavMesh->getTile (0);
+ if (tile && tile->header)
+ {
+ const float height = tile->header->walkableHeight;
+ const float width = tile->header->walkableRadius;
+ m_QueryExtents = Vector3f (width, height, width);
+
+ // Set up HeightMeshQuery object if the baked data needs it
+ const bool useHeightMeshQuery = (tile->header->flags & DT_MESH_HEADER_USE_HEIGHT_MESH) || !m_Heightmaps.empty ();
+ if (useHeightMeshQuery)
+ {
+ if (!m_HeightMeshQuery)
+ {
+ m_HeightMeshQuery = UNITY_NEW (HeightMeshQuery, kMemNavigation) ();
+ m_HeightMeshQuery->Init (&m_Heightmaps, 1.05f*tile->header->walkableClimb);
+ }
+ m_NavMeshQuery->setHeightQuery (m_HeightMeshQuery);
+ }
+ else
+ {
+ UNITY_DELETE (m_HeightMeshQuery, kMemNavigation);
+ m_NavMeshQuery->setHeightQuery (NULL);
+ }
+ }
+ else
+ {
+ m_QueryExtents = Vector3f (1,1,1);
+ }
+}
+
+void NavMesh::CleanupWithError ()
+{
+ ErrorString ("Creating NavMesh failed");
+ Cleanup ();
+}
+
+void NavMesh::Cleanup ()
+{
+ GetNavMeshManager ().CleanupMeshDependencies (m_NavMesh);
+ if (m_NavMesh)
+ {
+ dtFreeNavMesh (m_NavMesh);
+ m_NavMesh = NULL;
+ }
+ if (m_NavMeshQuery)
+ {
+ dtFreeNavMeshQuery (m_NavMeshQuery);
+ m_NavMeshQuery = NULL;
+ }
+ if (m_HeightMeshQuery)
+ {
+ UNITY_DELETE (m_HeightMeshQuery, kMemNavigation);
+ }
+}
+
+
+void NavMesh::AwakeFromLoad (AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad (mode);
+ Create ();
+}
+
+void NavMesh::SetData (const UInt8* data, unsigned size)
+{
+ m_MeshData.assign (data, data + size);
+ Create ();
+}
+
+
+template<class TransferFunction> inline
+void TransferMeshDataByteSwap (TransferFunction& transfer, dynamic_array<UInt8>& data)
+{
+ if (transfer.IsWriting ())
+ {
+ dynamic_array<UInt8> copy = data;
+ if (!copy.empty ())
+ {
+ ErrorIf (!dtNavMeshSetSwapEndian (&copy[0], copy.size ()));
+ }
+ transfer.Transfer (copy, "m_MeshData");
+ return;
+ }
+
+ transfer.Transfer (data, "m_MeshData");
+
+ if (transfer.IsReading () && !data.empty ())
+ {
+ ErrorIf (!dtNavMeshSetSwapEndian (&data[0], data.size ()));
+ }
+}
+
+template<class TransferFunction>
+void NavMesh::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ if (!transfer.ConvertEndianess ())
+ transfer.Transfer (m_MeshData, "m_MeshData");
+ else
+ TransferMeshDataByteSwap (transfer, m_MeshData);
+
+ TRANSFER (m_Heightmaps);
+}
+
+IMPLEMENT_OBJECT_SERIALIZE (NavMesh)
+IMPLEMENT_CLASS (NavMesh)
+
+
+void InvalidateNavMeshHit (NavMeshHit* hit)
+{
+ const float maxDistance = std::numeric_limits<float>::infinity ();
+ hit->mask = 0;
+ hit->hit = false;
+ hit->position = Vector3f::infinityVec;
+ hit->distance = maxDistance;
+ hit->normal = Vector3f::zero;
+}
diff --git a/Runtime/NavMesh/NavMesh.h b/Runtime/NavMesh/NavMesh.h
new file mode 100644
index 0000000..162f80e
--- /dev/null
+++ b/Runtime/NavMesh/NavMesh.h
@@ -0,0 +1,110 @@
+#pragma once
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Math/Vector3.h"
+#include "NavMeshTypes.h"
+#include "HeightmapData.h"
+
+
+class dtNavMesh;
+class dtNavMeshQuery;
+class dtQueryFilter;
+class NavMeshPath;
+class HeightMeshQuery;
+
+class NavMesh : public NamedObject
+{
+public:
+
+ struct Triangulation
+ {
+ dynamic_array<SInt32> layers;
+ dynamic_array<SInt32> indices;
+ dynamic_array<Vector3f> vertices;
+ };
+
+ REGISTER_DERIVED_CLASS (NavMesh, NamedObject);
+ DECLARE_OBJECT_SERIALIZE (NavMesh);
+
+ NavMesh (MemLabelId& label, ObjectCreationMode mode);
+ void Create ();
+
+ bool Raycast (NavMeshHit* hit, const Vector3f& sourcePosition, const Vector3f& targetPosition, const dtQueryFilter& filter);
+ bool DistanceToEdge (NavMeshHit* hit, const Vector3f& sourcePosition, const dtQueryFilter& filter);
+ bool SamplePosition (NavMeshHit* hit, const Vector3f& sourcePosition, const dtQueryFilter& filter, float maxDistance);
+
+ int CalculatePolygonPath (NavMeshPath* path, const Vector3f& sourcePosition, const Vector3f& targetPosition, const dtQueryFilter& filter);
+ int CalculatePathCorners (Vector3f* corners, int maxCorners, const NavMeshPath& path);
+
+ void Triangulate (NavMesh::Triangulation& triangulation) const;
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode mode);
+
+ void SetData (const UInt8* data, unsigned size);
+
+ inline const UInt8* GetMeshData () const;
+ inline size_t GetMeshDataSize () const;
+ inline const HeightmapDataVector& GetHeightmaps () const;
+ inline void SetHeightmaps (HeightmapDataVector& heightmaps);
+ inline const dtNavMesh* GetInternalNavMesh () const;
+ inline dtNavMesh* GetInternalNavMesh ();
+ inline dtNavMeshQuery* GetInternalNavMeshQuery ();
+ inline const HeightMeshQuery* GetHeightMeshQuery () const;
+
+private:
+ bool MapPosition (dtPolyRef* mappedPolyRef, Vector3f* mappedPosition, const Vector3f& position, const Vector3f& extents, const dtQueryFilter& filter) const;
+
+ void Cleanup ();
+ void CleanupWithError ();
+
+ Vector3f m_QueryExtents;
+ dynamic_array<UInt8> m_MeshData;
+ HeightmapDataVector m_Heightmaps;
+
+ dtNavMesh* m_NavMesh;
+ dtNavMeshQuery* m_NavMeshQuery;
+ HeightMeshQuery* m_HeightMeshQuery;
+};
+
+inline const UInt8* NavMesh::GetMeshData () const
+{
+ return m_MeshData.begin ();
+}
+
+inline size_t NavMesh::GetMeshDataSize () const
+{
+ return m_MeshData.size ();
+}
+
+inline const HeightmapDataVector& NavMesh::GetHeightmaps () const
+{
+ return m_Heightmaps;
+}
+
+inline void NavMesh::SetHeightmaps (HeightmapDataVector& heightmaps)
+{
+ m_Heightmaps = heightmaps;
+}
+
+
+inline const dtNavMesh* NavMesh::GetInternalNavMesh () const
+{
+ return m_NavMesh;
+}
+
+inline dtNavMesh* NavMesh::GetInternalNavMesh ()
+{
+ return m_NavMesh;
+}
+
+inline dtNavMeshQuery* NavMesh::GetInternalNavMeshQuery ()
+{
+ return m_NavMeshQuery;
+}
+
+inline const HeightMeshQuery* NavMesh::GetHeightMeshQuery () const
+{
+ return m_HeightMeshQuery;
+}
+
+void InvalidateNavMeshHit (NavMeshHit* hit);
diff --git a/Runtime/NavMesh/NavMeshAgent.cpp b/Runtime/NavMesh/NavMeshAgent.cpp
new file mode 100644
index 0000000..5114554
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshAgent.cpp
@@ -0,0 +1,1129 @@
+#include "UnityPrefix.h"
+#include "NavMeshAgent.h"
+
+#include "DetourCommon.h"
+#include "DetourCrowd.h"
+#include "DetourCrowdTypes.h"
+#include "NavMesh.h"
+#include "NavMeshManager.h"
+#include "NavMeshLayers.h"
+#include "NavMeshPath.h"
+#include "NavMeshSettings.h"
+#include "OffMeshLink.h"
+#include "Runtime/BaseClasses/MessageHandler.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+#define REQUIRE_INITIALIZED(FUNC, returnValue) \
+if (!InCrowdSystem ()) \
+{ \
+ ErrorString (#FUNC " can only be called on an active agent that has been placed on a NavMesh."); \
+ return returnValue; \
+}
+
+NavMeshAgent::NavMeshAgent (MemLabelId& label, ObjectCreationMode mode)
+: Super (label, mode)
+{
+ m_ManagerHandle = -1;
+ m_CachedPolyRef = -1;
+ m_Destination = Vector3f::infinityVec;
+ m_RequestedDestination = Vector3f::infinityVec;
+ m_StopDistance = false;
+ m_StopExplicit = false;
+ m_Request = false;
+ Reset ();
+}
+
+void NavMeshAgent::Reset ()
+{
+ Super::Reset ();
+ m_Radius = 0.5F;
+ m_Height = 2.0F;
+ m_BaseOffset = 0.0F;
+ m_Acceleration = 8.0f;
+ m_AngularSpeed = 120.0F;
+ m_Speed = 3.5f;
+ m_AvoidancePriority = 50;
+ m_ObstacleAvoidanceType = kHighQualityObstacleAvoidance;
+ m_UpdatePosition = true;
+ m_UpdateRotation = true;
+ m_StopRotating = false;
+ m_AutoTraverseOffMeshLink = true;
+ m_AutoBraking = true;
+ m_AutoRepath = true;
+ m_WalkableMask = 0xFFFFFFFF;
+ m_StoppingDistance = 0.0f;
+ m_InstanceID = 0;
+}
+
+void NavMeshAgent::SmartReset ()
+{
+ Super::SmartReset ();
+ AABB aabb;
+ if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb))
+ {
+ Vector3f extents = aabb.GetCenter () + aabb.GetExtent ();
+ SetRadius (max (extents.x, extents.z));
+ SetHeight (2.0F*extents.y);
+ SetBaseOffset (extents.y);
+ }
+ else
+ {
+ SetRadius (0.5F);
+ SetHeight (2.0F);
+ SetBaseOffset (0.0F);
+ }
+}
+
+NavMeshAgent::~NavMeshAgent ()
+{
+}
+
+template<class TransferFunc>
+void NavMeshAgent::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER (m_Radius);
+ TRANSFER (m_Speed);
+ TRANSFER (m_Acceleration);
+ transfer.Transfer (m_AvoidancePriority, "avoidancePriority");
+ TRANSFER (m_AngularSpeed);
+ TRANSFER (m_StoppingDistance);
+ TRANSFER (m_AutoTraverseOffMeshLink);
+ TRANSFER (m_AutoBraking);
+ TRANSFER (m_AutoRepath);
+ transfer.Align ();
+ TRANSFER (m_Height);
+ TRANSFER (m_BaseOffset);
+ TRANSFER (m_WalkableMask);
+ TRANSFER_ENUM(m_ObstacleAvoidanceType);
+}
+
+void NavMeshAgent::InitializeClass ()
+{
+ REGISTER_MESSAGE (NavMeshAgent, kTransformChanged, OnTransformChanged, int);
+}
+
+void NavMeshAgent::OnTransformChanged (int mask)
+{
+ if (!InCrowdSystem ())
+ return;
+
+ if (mask & Transform::kPositionChanged)
+ {
+ const Vector3f npos = GetGroundPositionFromTransform ();
+ GetCrowdSystem ()->updateAgentPosition (m_AgentHandle, npos.GetPtr ());
+ }
+ if (mask & Transform::kRotationChanged)
+ m_Angle = std::numeric_limits<float>::infinity ();
+ if (mask & Transform::kScaleChanged)
+ UpdateActiveAgentParameters ();
+}
+
+int NavMeshAgent::GetCurrentPolygonMask () const
+{
+ dtPolyRef polyRef;
+ if (IsOnOffMeshLink ())
+ polyRef = GetCrowdSystem ()->getAgentAnimation (m_AgentHandle)->polyRef;
+ else
+ polyRef = GetInternalAgent ()->corridor.getFirstPoly ();
+
+ const dtNavMeshQuery* meshq = GetInternalNavMeshQuery ();
+ return meshq->getPolygonFlags (polyRef);
+}
+
+const dtQueryFilter& NavMeshAgent::GetFilter () const
+{
+ Assert (InCrowdSystem ());
+ const dtCrowd* crowd = GetCrowdSystem ();
+ return *crowd->getAgentFilter (m_AgentHandle);
+}
+
+const dtCrowdAgent* NavMeshAgent::GetInternalAgent () const
+{
+ Assert (InCrowdSystem ());
+ return GetCrowdSystem ()->getAgent (m_AgentHandle);
+}
+
+bool NavMeshAgent::SetDestination (const Vector3f& targetPos)
+{
+ REQUIRE_INITIALIZED ("SetDestination", false);
+ m_RequestedDestination = targetPos;
+ m_Request = true;
+
+ dtCrowd* crowd = GetCrowdSystem ();
+ const dtQueryFilter& filter = GetFilter ();
+ const dtNavMeshQuery* query = crowd->getNavMeshQuery ();
+ const float* ext = crowd->getQueryExtents ();
+
+ dtPolyRef newPoly;
+ float destination[3];
+
+ query->findNearestPoly (targetPos.GetPtr (), ext, &filter, &newPoly, destination);
+
+ if (!newPoly)
+ return false;
+
+ if (m_CachedPolyRef == -1 || m_CachedPolyRef != newPoly || IsPathStale ())
+ {
+ if (crowd->requestMoveTarget (m_AgentHandle, newPoly, destination))
+ {
+ m_CachedPolyRef = newPoly;
+ m_Destination = Vector3f (destination);
+ if (m_StopExplicit)
+ {
+ m_StopExplicit = false;
+ SetUpdatePosition (true);
+ }
+ return true;
+ }
+ }
+ else
+ {
+ if (crowd->adjustMoveTarget (m_AgentHandle, m_CachedPolyRef, destination))
+ {
+ m_Destination = Vector3f (destination);
+ if (m_StopExplicit)
+ {
+ m_StopExplicit = false;
+ SetUpdatePosition (true);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+Vector3f NavMeshAgent::GetDestination () const
+{
+ if (HasPath () && !PathPending ())
+ return GetEndPositionOfCurrentPath ();
+
+ return m_Destination;
+}
+
+Vector3f NavMeshAgent::GetEndPositionOfCurrentPath () const
+{
+ Assert (InCrowdSystem ());
+
+ const dtCrowdAgent* agent = GetInternalAgent ();
+ Vector3f endPosition (agent->corridor.getTarget ());
+ return endPosition;
+}
+
+void NavMeshAgent::SetInternalAgentPosition (const Vector3f& position)
+{
+ if (!InCrowdSystem ())
+ return;
+
+ const Transform& transform = GetComponent (Transform);
+ Vector3f groundPosition = transform.TransformPointWithLocalOffset (position, Vector3f (0.0f, m_BaseOffset, 0.0f));
+ GetCrowdSystem ()->moveAgent (m_AgentHandle, groundPosition.GetPtr ());
+}
+
+Vector3f NavMeshAgent::GetVelocity () const
+{
+ if (!InCrowdSystem ())
+ return Vector3f (0,0,0);
+
+ return Vector3f (GetInternalAgent ()->avel);
+}
+
+void NavMeshAgent::SetVelocity (const Vector3f& vel)
+{
+ if (!InCrowdSystem ())
+ return;
+
+ ABORT_INVALID_VECTOR3 (vel, velocity, navmeshagent);
+ GetCrowdSystem ()->updateAgentVelocity (m_AgentHandle, vel.GetPtr ());
+}
+
+Vector3f NavMeshAgent::GetNextPosition () const
+{
+ const Transform& transform = GetComponent (Transform);
+ if (!InCrowdSystem ())
+ return transform.GetPosition ();
+
+ const Vector3f position (GetInternalAgent ()->npos);
+ return transform.TransformPointWithLocalOffset (position, Vector3f (0.0f, -m_BaseOffset, 0.0f));
+}
+
+Vector3f NavMeshAgent::GetNextCorner () const
+{
+ if (!InCrowdSystem ())
+ return GetComponent (Transform).GetPosition ();
+
+ const dtCrowdAgent* agent = GetInternalAgent ();
+ Vector3f target;
+ GetCrowdSystem ()->getSteerTarget (target.GetPtr (), agent);
+ return target;
+}
+
+Vector3f NavMeshAgent::GetDesiredVelocity () const
+{
+ if (!InCrowdSystem ())
+ return Vector3f::zero;
+
+ const dtCrowdAgent* agent = GetInternalAgent ();
+ Vector3f desiredVelocity (agent->nvel);
+ return desiredVelocity;
+}
+
+bool NavMeshAgent::IsOnOffMeshLink () const
+{
+ if (!InCrowdSystem ())
+ return false;
+
+ const dtCrowdAgent* agent = GetInternalAgent ();
+ return agent->state == DT_CROWDAGENT_STATE_OFFMESH;
+}
+
+// @TODO: Deprecate (de)activation of baked offmeshlinks.
+// We really don't want to be holding an OffMeshLink instance ID here.
+// Instead promote use of: NavMeshAgent.currentOffMeshLinkData.offMeshLink
+void NavMeshAgent::ActivateCurrentOffMeshLink (bool activated)
+{
+ if (!IsOnOffMeshLink ())
+ return;
+
+ int instanceID = 0;
+ if (!activated)
+ {
+ const dtPolyRef polyref = GetCrowdSystem ()->getAgentAnimation (m_AgentHandle)->polyRef;
+ GetNavMeshManager ().GetInternalNavMesh ()->getOffMeshLinkInstanceIDByRef (polyref, &instanceID);
+ m_InstanceID = instanceID;
+ }
+ else
+ {
+ instanceID = m_InstanceID;
+ m_InstanceID = 0;
+ }
+
+ if (OffMeshLink* oml = dynamic_instanceID_cast<OffMeshLink*> (instanceID))
+ {
+ oml->SetActivated (activated);
+ }
+ else
+ {
+ const dtPolyRef polyref = GetCrowdSystem ()->getAgentAnimation (m_AgentHandle)->polyRef;
+ GetNavMeshSettings ().SetOffMeshPolyAccess (polyref, activated);
+ }
+}
+
+
+bool NavMeshAgent::GetCurrentOffMeshLinkData (OffMeshLinkData* data) const
+{
+ Assert (data);
+ memset (data, 0, sizeof (OffMeshLinkData));
+ if (!IsOnOffMeshLink ())
+ return false;
+
+ const dtCrowdAgentAnimation* agentAnimation = GetCrowdSystem ()->getAgentAnimation (m_AgentHandle);
+ if (agentAnimation == NULL)
+ return false;
+
+ if (!SetOffMeshLinkDataFlags (data, agentAnimation->polyRef))
+ return false;
+
+ data->m_StartPos = Vector3f (agentAnimation->startPos);
+ data->m_EndPos = Vector3f (agentAnimation->endPos);
+ return true;
+}
+
+bool NavMeshAgent::GetNextOffMeshLinkData (OffMeshLinkData* data) const
+{
+ Assert (data);
+ memset (data, 0, sizeof (OffMeshLinkData));
+ if (!InCrowdSystem ())
+ return false;
+
+ const dtPathCorridor& corridor = GetInternalAgent ()->corridor;
+ if (!corridor.isPathValid ())
+ return false;
+
+ const dtNavMesh* navmesh = GetNavMeshManager ().GetInternalNavMesh ();
+ const dtPolyRef* pathPolygons = corridor.getPath ();
+ const int pathCount = corridor.getPathCount ();
+ int i = 1;
+ for (; i < pathCount; ++i)
+ {
+ if (SetOffMeshLinkDataFlags (data, pathPolygons[i]))
+ break;
+ }
+
+ // Note: We intentionally fail if pathCount == 1
+ // in that case any offmeshlink is not 'next' - but current.
+ if (i >= pathCount)
+ return false;
+
+ dtStatus status = navmesh->getOffMeshConnectionPolyEndPoints (pathPolygons[i-1], pathPolygons[i], data->m_StartPos.GetPtr (), data->m_EndPos.GetPtr ());
+
+ // Having successfully called 'SetOffMeshLinkDataFlags' above should ensure valid endpoints here.
+ DebugAssert (status == DT_SUCCESS);
+ if (status != DT_SUCCESS)
+ {
+ memset (data, 0, sizeof (OffMeshLinkData));
+ return false;
+ }
+
+ return true;
+}
+
+// Set OffMeshLink data or return false
+// Returns false if polyRef is not an offmeshlink
+bool NavMeshAgent::SetOffMeshLinkDataFlags (OffMeshLinkData* data, const dtPolyRef polyRef) const
+{
+ Assert (InCrowdSystem ());
+
+ const dtNavMesh* navmesh = GetNavMeshManager ().GetInternalNavMesh ();
+
+ int instanceID, linkType, activated;
+ if (!navmesh->GetOffMeshLinkData (polyRef, &instanceID, &linkType, &activated))
+ return false;
+
+ data->m_Valid = 1;
+ data->m_Activated = activated;
+ data->m_LinkType = static_cast<OffMeshLinkType> (linkType);
+ data->m_InstanceID = instanceID;
+
+ return true;
+}
+
+void NavMeshAgent::SetUpdatePosition (bool inbool)
+{
+ if (m_UpdatePosition == inbool)
+ return;
+
+ m_UpdatePosition = inbool;
+ if (inbool)
+ {
+ Vector3f npos = GetGroundPositionFromTransform ();
+ GetCrowdSystem ()->updateAgentPosition (m_AgentHandle, npos.GetPtr ());
+ }
+}
+
+void NavMeshAgent::SetUpdateRotation (bool inbool)
+{
+ m_UpdateRotation = inbool;
+ if (inbool)
+ {
+ const Transform& transform = GetComponent (Transform);
+ Vector3f euler = QuaternionToEuler (transform.GetRotation ());
+ m_Angle = euler.y;
+ }
+}
+
+void NavMeshAgent::SetAutoTraverseOffMeshLink (bool inbool)
+{
+ m_AutoTraverseOffMeshLink = inbool;
+ UpdateActiveAgentParameters ();
+ SetDirty ();
+}
+
+void NavMeshAgent::SetAutoBraking (bool inbool)
+{
+ m_AutoBraking = inbool;
+ UpdateActiveAgentParameters ();
+ SetDirty ();
+}
+
+void NavMeshAgent::SetAutoRepath (bool inbool)
+{
+ m_AutoRepath = inbool;
+ SetDirty ();
+}
+
+void NavMeshAgent::UpdateActiveAgentParameters ()
+{
+ CheckConsistency ();
+ if (InCrowdSystem ())
+ {
+ dtCrowdAgentParams params;
+ FillAgentParams (params);
+ GetCrowdSystem ()->updateAgentParameters (m_AgentHandle, &params);
+ }
+}
+
+void NavMeshAgent::AwakeFromLoad (AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad (mode);
+ UpdateActiveAgentParameters ();
+}
+
+void NavMeshAgent::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+ m_AvoidancePriority = clamp (m_AvoidancePriority, 0, 99);
+ m_Speed = clamp (m_Speed, 0.0f, 1e15f); // squaring m_Speed keeps it below inf.
+ m_StoppingDistance = max (0.0f, m_StoppingDistance);
+ m_Acceleration = max (0.0f, m_Acceleration);
+ m_AngularSpeed = max (0.0f, m_AngularSpeed);
+ m_Height = EnsurePositive (m_Height);
+ m_Radius = EnsurePositive (m_Radius);
+}
+
+void NavMeshAgent::UpdateState ()
+{
+ if (!InCrowdSystem ())
+ return;
+
+ const float remainingDistance = GetRemainingDistance ();
+ StopOrResume (remainingDistance);
+
+ if (m_AutoRepath && m_Request)
+ RepathIfStuck (remainingDistance);
+
+ // Previously we were resetting the path when agentes reached the stopping distance.
+ // Now this is no longer done, the path is always kept. We can kill this code on the next backwards breaking release.
+ if (!IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ {
+ if (m_StopDistance && !PathPending ())
+ ResetPath ();
+ }
+}
+
+void NavMeshAgent::StopOrResume (float remainingDistance)
+{
+ m_StopDistance = remainingDistance < m_StoppingDistance && HasPath ();
+ if (!m_StopExplicit)
+ {
+ m_StopRotating = false;
+ }
+
+ if (m_StopExplicit || m_StopDistance)
+ GetCrowdSystem ()->stopAgent (m_AgentHandle);
+ else
+ GetCrowdSystem ()->resumeAgent (m_AgentHandle);
+}
+
+void NavMeshAgent::RepathIfStuck (float remainingDistance)
+{
+ bool isValid = IsPathValid ();
+ bool isOffMesh = IsOnOffMeshLink ();
+ bool isPartial = IsPathPartial ();
+ bool isStale = IsPathStale ();
+
+ // m_Radius must be scaled by transform scale - so use the (already scaled) internal radius!
+ const float agentRadius = GetInternalAgent ()->params.radius;
+ if (!isValid || (!isOffMesh && isPartial && isStale && remainingDistance <= agentRadius))
+ {
+ m_CachedPolyRef = -1;
+ SetDestination (m_RequestedDestination);
+ }
+}
+
+static inline void SetTransformMessageEnabled (bool enable)
+{
+ GameObject::GetMessageHandler ().SetMessageEnabled (ClassID (NavMeshAgent), kTransformChanged.messageID, enable);
+}
+
+void NavMeshAgent::UpdateTransform (float deltaTime)
+{
+ if (!InCrowdSystem ())
+ return;
+
+ Transform& transform = GetComponent (Transform);
+
+ // Avoid dirtying the transform twice (when updating position and rotation)
+ // Instead call "WithoutNotification" variants of the SetPosition / SetRotation
+ // and signal change once at the end using 'changeMessageMask'
+ int changeMessageMask = 0;
+
+ if (m_UpdatePosition)
+ {
+ const Vector3f groundPosition = Vector3f (GetInternalAgent ()->npos);
+ const Vector3f localOffset = Vector3f (0.0f, -m_BaseOffset, 0.0f);
+ const Vector3f newPosition = transform.TransformPointWithLocalOffset (groundPosition, localOffset);
+ transform.SetPositionWithoutNotification (newPosition);
+ changeMessageMask |= Transform::kPositionChanged;
+ }
+
+ if (m_UpdateRotation && !m_StopRotating)
+ {
+ UpdateRotation (transform, deltaTime);
+ changeMessageMask |= Transform::kRotationChanged;
+ }
+
+ if (changeMessageMask)
+ {
+ // Update the transform hierarchy w/o triggering our own callback
+ SetTransformMessageEnabled (false);
+ transform.SendTransformChanged (changeMessageMask);
+ SetTransformMessageEnabled (true);
+ }
+}
+
+void NavMeshAgent::UpdateRotation (Transform& transform, float deltaTime)
+{
+ if (m_Angle == std::numeric_limits<float>::infinity ())
+ {
+ Vector3f euler = QuaternionToEuler (transform.GetRotation ());
+ m_Angle = euler.y;
+ }
+
+ const dtCrowdAgent* agent = GetInternalAgent ();
+ float sqrLen = Sqr (agent->vel[0]) + Sqr (agent->vel[2]);
+ if (sqrLen > 0.001F)
+ {
+ float angle = atan2 (agent->vel[0], agent->vel[2]);
+ const float deltaAngle = DeltaAngleRad (m_Angle, angle);
+ const float maxAngleSpeed = std::min (Abs (deltaAngle)*(1.0f+sqrtf (sqrLen)), Deg2Rad (m_AngularSpeed));
+ m_Angle += std::min (Abs (deltaAngle), maxAngleSpeed*deltaTime) * Sign (deltaAngle);
+ }
+ Quaternionf rotation = AxisAngleToQuaternion (Vector3f::yAxis, m_Angle);
+ transform.SetRotationWithoutNotification (rotation);
+}
+
+void NavMeshAgent::FillAgentParams (dtCrowdAgentParams& params)
+{
+ Vector2f scale = CalculateDimensionScales ();
+ params.radius = max (0.00001F, m_Radius*scale.x);
+ params.height = max (0.00001F, m_Height*scale.y);
+ params.maxAcceleration = m_Acceleration;
+ params.maxSpeed = m_Speed;
+ params.priority = 99 - m_AvoidancePriority;
+ params.obstacleAvoidanceType = m_ObstacleAvoidanceType;
+ params.includeFlags = GetWalkableMask ();
+
+ params.updateFlags = 0;
+ if (m_ObstacleAvoidanceType != kNoObstacleAvoidance)
+ params.updateFlags |= DT_CROWD_OBSTACLE_AVOIDANCE;
+ if (m_AutoTraverseOffMeshLink)
+ params.updateFlags |= DT_CROWD_AUTO_TRAVERSE_OFFMESHLINK;
+ if (m_AutoBraking)
+ params.updateFlags |= DT_CROWD_AUTO_BRAKING;
+}
+
+void NavMeshAgent::OnNavMeshChanged ()
+{
+ if (InCrowdSystem ())
+ {
+ ReinstateInCrowdSystem ();
+ }
+ else
+ {
+ AddToCrowdSystem ();
+ }
+}
+
+void NavMeshAgent::AddToCrowdSystem ()
+{
+ if (!IsWorldPlaying ())
+ return;
+
+ if (GetInternalNavMeshQuery () == NULL)
+ {
+ WarningString ("Failed to create agent because there is no valid NavMesh");
+ return;
+ }
+ dtCrowd* crowd = GetCrowdSystem ();
+ Assert (crowd != NULL);
+
+ dtCrowdAgentParams params;
+ FillAgentParams (params);
+
+ Assert (!InCrowdSystem ());
+ Assert (m_CachedPolyRef == -1);
+ Vector3f currentPosition = GetGroundPositionFromTransform ();
+ if (!crowd->addAgent (m_AgentHandle, currentPosition.GetPtr (), &params))
+ {
+ WarningStringObject ("Failed to create agent because it is not close enough to the NavMesh", this);
+ return;
+ }
+
+ const dtCrowdAgent* agent = GetInternalAgent ();
+
+ m_CachedPolyRef = agent->corridor.getFirstPoly ();
+ m_Destination = Vector3f (agent->corridor.getPos ());
+ m_RequestedDestination = m_Destination;
+ m_Angle = std::numeric_limits<float>::infinity ();
+
+ // Collect global layer costs and apply to created agent
+ float layerCosts[NavMeshLayers::kLayerCount];
+ NavMeshLayers& layers = GetNavMeshLayers ();
+ for (int il = 0; il < NavMeshLayers::kLayerCount; ++il)
+ {
+ layerCosts[il] = layers.GetLayerCost (il);
+ }
+ crowd->initializeAgentFilter (m_AgentHandle, layerCosts, NavMeshLayers::kLayerCount);
+}
+
+void NavMeshAgent::ReinstateInCrowdSystem ()
+{
+ if (!InCrowdSystem ())
+ return;
+
+ dtCrowd* crowd = GetCrowdSystem ();
+ Assert (crowd);
+
+ ResetCachedPolyRef ();
+ if (!crowd->remapAgentPathStart (m_AgentHandle))
+ {
+ RemoveFromCrowdSystem ();
+ }
+ else if (m_AutoRepath && m_Request)
+ {
+ SetDestination (m_RequestedDestination);
+ }
+}
+
+void NavMeshAgent::RemoveFromCrowdSystem ()
+{
+ if (!InCrowdSystem ())
+ return;
+
+#if UNITY_EDITOR
+ dtCrowdAgentDebugInfo* debugInfo = GetNavMeshManager ().GetInternalDebugInfo ();
+ if (debugInfo && debugInfo->idx == m_AgentHandle.GetIndex ())
+ {
+ debugInfo->idx = -1;
+ }
+#endif
+
+ GetCrowdSystem ()->removeAgent (m_AgentHandle);
+ m_CachedPolyRef = -1;
+}
+
+void NavMeshAgent::AddToManager ()
+{
+ GetNavMeshManager ().RegisterAgent (*this, m_ManagerHandle);
+ AddToCrowdSystem ();
+}
+
+void NavMeshAgent::RemoveFromManager ()
+{
+ RemoveFromCrowdSystem ();
+ GetNavMeshManager ().UnregisterAgent (m_ManagerHandle);
+}
+
+float NavMeshAgent::GetRemainingDistance () const
+{
+ REQUIRE_INITIALIZED ("GetRemainingDistance", std::numeric_limits<float>::infinity ());
+ return GetCrowdSystem ()->CalculateRemainingPath (m_AgentHandle);
+}
+
+int NavMeshAgent::CalculatePolygonPath (const Vector3f& targetPosition, NavMeshPath* path)
+{
+ REQUIRE_INITIALIZED ("CalculatePolygonPath", 0)
+ const dtCrowdAgent* agent = GetInternalAgent ();
+ Vector3f sourcePosition (agent->corridor.getPos ());
+
+ const dtQueryFilter& filter = GetFilter ();
+ return GetNavMeshSettings ().GetNavMesh ()->CalculatePolygonPath (path, sourcePosition, targetPosition, filter);
+}
+
+bool NavMeshAgent::SetPath (const NavMeshPath* path)
+{
+ REQUIRE_INITIALIZED ("SetPath", false)
+
+ ResetPath ();
+
+ const NavMeshPathStatus status = path->GetStatus ();
+ const int polyCount = path->GetPolygonCount ();
+ if (status == kPathInvalid || polyCount == 0)
+ return false;
+
+ const Vector3f targetPos = path->GetTargetPosition ();
+ const Vector3f sourcePos = path->GetSourcePosition ();
+ const dtPolyRef* polyPath = path->GetPolygonPath ();
+ GetCrowdSystem ()->setAgentPath (m_AgentHandle, sourcePos.GetPtr (), targetPos.GetPtr (), polyPath, polyCount, status == kPathPartial);
+ const dtCrowdAgent* agent = GetInternalAgent ();
+
+ dtPolyRef lastPoly = agent->corridor.getLastPoly ();
+ if (lastPoly != polyPath[polyCount - 1])
+ return false;
+
+ m_CachedPolyRef = lastPoly;
+ return true;
+}
+
+void NavMeshAgent::CopyPath (NavMeshPath* path) const
+{
+ Assert (path);
+ if (!m_AgentHandle.IsValid ())
+ {
+ path->SetPolygonCount (0);
+ path->SetStatus (kPathInvalid);
+ return;
+ }
+
+ const dtCrowdAgent* agent = GetInternalAgent ();
+
+ unsigned int pathCount = agent->corridor.getPathCount ();
+ memcpy (path->GetPolygonPath (), agent->corridor.getPath (), sizeof (dtPolyRef)*pathCount);
+ path->SetPolygonCount (pathCount);
+ path->SetTargetPosition (Vector3f (agent->corridor.getTarget ()));
+ path->SetSourcePosition (Vector3f (agent->corridor.getPos ()));
+ path->SetStatus (GetPathStatus ());
+}
+
+void NavMeshAgent::ResetPath ()
+{
+ REQUIRE_INITIALIZED ("ResetPath",)
+
+ GetCrowdSystem ()->resetAgentPath (m_AgentHandle);
+ m_StopDistance = false;
+ m_StopExplicit = false;
+ m_Request = false;
+ m_CachedPolyRef = -1;
+}
+
+bool NavMeshAgent::PathPending () const
+{
+ return InCrowdSystem () && GetInternalAgent ()->pendingRequest;
+}
+
+bool NavMeshAgent::HasPath () const
+{
+ if (!InCrowdSystem () || m_CachedPolyRef == -1)
+ return false;
+
+ const dtCrowdAgent* agent = GetInternalAgent ();
+ if (agent->state == DT_CROWDAGENT_STATE_WALKING)
+ {
+ return agent->ncorners > 0;
+ }
+ else if (agent->state == DT_CROWDAGENT_STATE_OFFMESH || agent->state == DT_CROWDAGENT_STATE_WAITING_OFFMESH)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool NavMeshAgent::DistanceToEdge (NavMeshHit* hit) const
+{
+ Assert (hit);
+ REQUIRE_INITIALIZED ("DistanceToEdge", false);
+ const dtCrowdAgent* agent = GetInternalAgent ();
+ Vector3f sourcePosition (agent->corridor.getPos ());
+
+ const dtQueryFilter& filter = GetFilter ();
+ return GetNavMeshSettings ().GetNavMesh ()->DistanceToEdge (hit, sourcePosition, filter);
+}
+
+bool NavMeshAgent::Raycast (const Vector3f& targetPosition, NavMeshHit* hit)
+{
+ Assert (hit);
+ REQUIRE_INITIALIZED ("Raycast", false)
+ const dtCrowdAgent* agent = GetInternalAgent ();
+ Vector3f sourcePosition (agent->corridor.getPos ());
+
+ const dtQueryFilter& filter = GetFilter ();
+ return GetNavMeshSettings ().GetNavMesh ()->Raycast (hit, sourcePosition, targetPosition, filter);
+}
+
+// TODO: handle case where: maxDistance > remainingDistance (non-inf).
+bool NavMeshAgent::SamplePathPosition (int passableMask, float maxDistance, NavMeshHit* hit)
+{
+ Assert (hit);
+ REQUIRE_INITIALIZED ("SamplePathPosition", false);
+
+ const dtCrowdAgent* agent = GetInternalAgent ();
+ const dtNavMeshQuery* meshq = GetInternalNavMeshQuery ();
+ Vector3f sourcePosition (agent->corridor.getPos ());
+ Vector3f agentPosition (agent->npos);
+
+ // Copy and modify agent filter with provided mask
+ dtQueryFilter filter = GetFilter ();
+ filter.setIncludeFlags (passableMask & filter.getIncludeFlags ());
+
+ if (agent->ncorners == 0 || maxDistance <= 0.0f)
+ {
+ hit->position = agentPosition;
+ hit->normal = Vector3f::zero;
+ hit->distance = 0.0f;
+ hit->mask = GetCurrentPolygonMask ();
+ hit->hit = false;
+ return false;
+ }
+
+ if (!meshq->passRef (agent->corridor.getFirstPoly (), &filter))
+ {
+ hit->position = sourcePosition;
+ hit->normal = Vector3f::zero;
+ hit->distance = 0.0f;
+ hit->mask = GetCurrentPolygonMask ();
+ hit->hit = true;
+ return true;
+ }
+
+ float totalDistance = 0.0f;
+ for (int i = 0; i < agent->ncorners; ++i)
+ {
+ const Vector3f targetPosition (&agent->cornerVerts[3*i]);
+ if (GetNavMeshSettings ().GetNavMesh ()->Raycast (hit, sourcePosition, targetPosition, filter))
+ {
+ const float hitDistance = totalDistance + hit->distance;
+ if (hitDistance <= maxDistance)
+ {
+ // position, normal, mask, hit set by Raycast ()
+ hit->distance = hitDistance; // set to distance along path
+ return true;
+ }
+ }
+ float segLen = Magnitude (targetPosition-sourcePosition);
+ if (totalDistance+segLen > maxDistance)
+ {
+ // TODO: set proper height in "hit->position[1]"
+ hit->position = Lerp (sourcePosition, targetPosition, (maxDistance-totalDistance)/segLen);
+ hit->normal = Vector3f::zero;
+ hit->distance = maxDistance;
+ hit->mask = meshq->getPolygonFlags (agent->cornerPolys[i]);
+ hit->hit = false;
+ return false;
+ }
+
+ totalDistance += segLen;
+ sourcePosition = targetPosition;
+ }
+
+ // Found end of straight path in "agent->cornerVerts"
+ // - this is not necessarily the end of the path.
+ return false;
+}
+
+bool NavMeshAgent::Warp (const Vector3f& newPosition)
+{
+ RemoveFromCrowdSystem ();
+ Transform& transform = GetComponent (Transform);
+ transform.SetPosition (newPosition);
+ AddToCrowdSystem ();
+ return InCrowdSystem ();
+}
+
+void NavMeshAgent::Move (const Vector3f& motion)
+{
+ REQUIRE_INITIALIZED ("Move",)
+
+ const dtCrowdAgent* agent = GetInternalAgent ();
+ Vector3f targetPos = motion + Vector3f (agent->npos);
+ GetCrowdSystem ()->moveAgent (m_AgentHandle, targetPos.GetPtr ());
+
+ if (m_UpdatePosition)
+ {
+ SetTransformFromGroundPosition (Vector3f (agent->npos));
+ }
+}
+
+void NavMeshAgent::Stop (bool stopUpdates)
+{
+ REQUIRE_INITIALIZED ("Stop",)
+
+ m_StopExplicit = true;
+ if (stopUpdates)
+ {
+ SetVelocity (Vector3f::zero);
+ SetUpdatePosition (false);
+ m_StopRotating = true;
+ }
+}
+
+void NavMeshAgent::Resume ()
+{
+ REQUIRE_INITIALIZED ("Resume",)
+
+ m_StopExplicit = false;
+ SetUpdatePosition (true);
+}
+
+void NavMeshAgent::CompleteOffMeshLink ()
+{
+ REQUIRE_INITIALIZED ("CompleteOffMeshLink",)
+
+ GetCrowdSystem ()->completeOffMeshLink (m_AgentHandle);
+}
+
+NavMeshPathStatus NavMeshAgent::GetPathStatus () const
+{
+ if (!InCrowdSystem () || !GetInternalAgent ()->corridor.isPathValid ())
+ return kPathInvalid;
+
+ if (GetInternalAgent ()->corridor.isPathPartial ())
+ return kPathPartial;
+
+ return kPathComplete;
+}
+
+bool NavMeshAgent::IsPathValid () const
+{
+ if (!InCrowdSystem ())
+ return false;
+
+ return GetInternalAgent ()->corridor.isPathValid ();
+}
+
+bool NavMeshAgent::IsPathPartial () const
+{
+ if (!InCrowdSystem ())
+ return false;
+
+ return GetInternalAgent ()->corridor.isPathPartial ();
+}
+
+bool NavMeshAgent::IsPathStale () const
+{
+ if (!InCrowdSystem ())
+ return false;
+
+ return GetInternalAgent ()->corridor.isPathStale ();
+}
+
+void NavMeshAgent::SetLayerCost (unsigned int layer, float cost)
+{
+ REQUIRE_INITIALIZED ("SetLayerCost",)
+
+ if (layer >= NavMeshLayers::kLayerCount)
+ {
+ ErrorString ("Index out of bounds");
+ return;
+ }
+
+#if UNITY_EDITOR
+ if (cost < 1.0f)
+ {
+ WarningStringObject (NavMeshLayers::s_WarningCostLessThanOne, this);
+ }
+#endif
+
+ GetCrowdSystem ()->updateAgentFilterCost (m_AgentHandle, layer, cost);
+}
+
+float NavMeshAgent::GetLayerCost (unsigned int layer) const
+{
+ REQUIRE_INITIALIZED ("GetLayerCost", 0.0F)
+
+ if (layer >= NavMeshLayers::kLayerCount)
+ {
+ ErrorString ("Index out of bounds");
+ return 0.0F;
+ }
+
+ const dtQueryFilter& filter = GetFilter ();
+ return filter.getAreaCost (layer);
+}
+
+void NavMeshAgent::SetHeight (float height)
+{
+ ABORT_INVALID_FLOAT (height, height, navmeshagent);
+ m_Height = EnsurePositive (height);
+ UpdateActiveAgentParameters ();
+ SetDirty ();
+}
+
+void NavMeshAgent::SetBaseOffset (float baseOffset)
+{
+ ABORT_INVALID_FLOAT (baseOffset, baseOffset, navmeshagent);
+ m_BaseOffset = baseOffset;
+ SetDirty ();
+ if (!InCrowdSystem ())
+ return;
+
+ if (m_UpdatePosition)
+ {
+ const dtCrowdAgent* agent = GetInternalAgent ();
+ SetTransformFromGroundPosition (Vector3f (agent->npos));
+ }
+}
+
+Vector2f NavMeshAgent::CalculateDimensionScales () const
+{
+ Vector3f absScale = Abs (GetComponent (Transform).GetWorldScaleLossy ());
+ float scaleWidth = max (absScale.x, absScale.z);
+ float scaleHeight = absScale.y;
+ return Vector2f (scaleWidth, scaleHeight);
+}
+
+float NavMeshAgent::CalculateScaledRadius () const
+{
+ const Vector2f scale = CalculateDimensionScales ();
+ return m_Radius*scale.x;
+}
+
+float NavMeshAgent::CalculateScaledHeight () const
+{
+ const Vector2f scale = CalculateDimensionScales ();
+ return m_Height*scale.y;
+}
+
+void NavMeshAgent::SetRadius (float radius)
+{
+ ABORT_INVALID_FLOAT (radius, radius, navmeshagent);
+ m_Radius = EnsurePositive (radius);
+ UpdateActiveAgentParameters ();
+ SetDirty ();
+}
+
+void NavMeshAgent::SetSpeed (float value)
+{
+ ABORT_INVALID_FLOAT (value, speed, navmeshagent);
+ m_Speed = clamp (value, 0.0f, 1e15f); // squaring m_Speed keeps it below inf.
+ UpdateActiveAgentParameters ();
+ SetDirty ();
+}
+
+void NavMeshAgent::SetAvoidancePriority (int value)
+{
+ m_AvoidancePriority = value;
+ UpdateActiveAgentParameters ();
+ SetDirty ();
+}
+
+void NavMeshAgent::SetAngularSpeed (float value)
+{
+ ABORT_INVALID_FLOAT (value, angularSpeed, navmeshagent);
+ m_AngularSpeed = value;
+ UpdateActiveAgentParameters ();
+ SetDirty ();
+}
+
+void NavMeshAgent::SetAcceleration (float value)
+{
+ ABORT_INVALID_FLOAT (value, acceleration, navmeshagent);
+ m_Acceleration = value;
+ UpdateActiveAgentParameters ();
+ SetDirty ();
+}
+
+void NavMeshAgent::SetStoppingDistance (float value)
+{
+ ABORT_INVALID_FLOAT (value, stoppingDistance, navmeshagent);
+ m_StoppingDistance = value;
+ SetDirty ();
+}
+
+void NavMeshAgent::SetWalkableMask (UInt32 mask)
+{
+ if (m_WalkableMask == mask)
+ return;
+
+ m_WalkableMask = mask;
+
+ UpdateActiveAgentParameters ();
+ SetDirty ();
+}
+
+void NavMeshAgent::SetObstacleAvoidanceType (int type)
+{
+ m_ObstacleAvoidanceType = (ObstacleAvoidanceType) type;
+ UpdateActiveAgentParameters ();
+ SetDirty ();
+}
+
+const dtNavMeshQuery* NavMeshAgent::GetInternalNavMeshQuery ()
+{
+ return GetNavMeshManager ().GetInternalNavMeshQuery ();
+}
+
+dtCrowd* NavMeshAgent::GetCrowdSystem ()
+{
+ return GetNavMeshManager ().GetCrowdSystem ();
+}
+
+IMPLEMENT_CLASS_HAS_INIT (NavMeshAgent)
+IMPLEMENT_OBJECT_SERIALIZE (NavMeshAgent)
diff --git a/Runtime/NavMesh/NavMeshAgent.h b/Runtime/NavMesh/NavMeshAgent.h
new file mode 100644
index 0000000..fe5a943
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshAgent.h
@@ -0,0 +1,313 @@
+#pragma once
+#ifndef RUNTIME_NAVMESH_AGENT
+#define RUNTIME_NAVMESH_AGENT
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Graphics/Transform.h"
+#include "NavMeshTypes.h"
+#include "Runtime/Math/Vector2.h"
+
+class NavMeshPath;
+struct OffMeshLinkData;
+struct NavMeshHit;
+
+class dtNavMeshQuery;
+class dtCrowd;
+class dtQueryFilter;
+struct dtCrowdAgentParams;
+struct dtCrowdAgent;
+
+
+class NavMeshAgent : public Behaviour
+{
+public:
+ REGISTER_DERIVED_CLASS (NavMeshAgent, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (NavMeshAgent)
+
+ NavMeshAgent (MemLabelId& label, ObjectCreationMode mode);
+ // ~NavMeshAgent (); declared by a macro
+
+ static void InitializeClass ();
+ static void CleanupClass () {} // Avoid double free of Behavior
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode mode);
+ virtual void CheckConsistency ();
+
+ inline bool InCrowdSystem () const;
+ inline void SetManagerHandle (int handle);
+ bool SetDestination (const Vector3f& position);
+ Vector3f GetDestination () const;
+ void SetInternalAgentPosition (const Vector3f& position);
+
+ int CalculatePolygonPath (const Vector3f& targetPosition, NavMeshPath* path);
+ bool SetPath (const NavMeshPath* path);
+ void CopyPath (NavMeshPath* path) const;
+ void ResetPath ();
+ bool PathPending () const;
+ bool HasPath () const;
+ bool DistanceToEdge (NavMeshHit* hit) const;
+ bool Raycast (const Vector3f& position, NavMeshHit* hitPos);
+ float GetRemainingDistance () const;
+
+ // Consider moving these to the 'NavMeshPath' class
+ bool SamplePathPosition (int collisionMask, float maxDistance, NavMeshHit* hit);
+ Vector3f GetEndPositionOfCurrentPath () const;
+ bool IsPathValid () const;
+ bool IsPathPartial () const;
+ bool IsPathStale () const;
+ NavMeshPathStatus GetPathStatus () const;
+ inline void ResetCachedPolyRef ();
+
+ UInt32 GetWalkableMask () const { return m_WalkableMask; }
+ void SetWalkableMask (UInt32 mask);
+
+ void SetLayerCost (unsigned int layer, float cost);
+ float GetLayerCost (unsigned int layer) const;
+
+ inline ObstacleAvoidanceType GetObstacleAvoidanceType () const;
+ void SetObstacleAvoidanceType (int type);
+
+ void SetAvoidancePriority (int value);
+ inline int GetAvoidancePriority () const;
+
+ inline float GetSpeed () const;
+ void SetSpeed (float value);
+
+ inline float GetAngularSpeed () const;
+ void SetAngularSpeed (float value);
+
+ inline float GetAcceleration () const;
+ void SetAcceleration (float value);
+
+ inline float GetStoppingDistance () const;
+ void SetStoppingDistance (float value);
+
+ Vector3f GetVelocity () const;
+ void SetVelocity (const Vector3f& vel);
+ Vector3f GetNextPosition () const;
+
+ Vector3f GetNextCorner () const;
+ Vector3f GetDesiredVelocity () const;
+
+ bool IsOnOffMeshLink () const;
+ void ActivateCurrentOffMeshLink (bool activated);
+ bool GetCurrentOffMeshLinkData (OffMeshLinkData* data) const;
+ bool GetNextOffMeshLinkData (OffMeshLinkData* data) const;
+ bool SetOffMeshLinkDataFlags (OffMeshLinkData* data, const dtPolyRef polyRef) const;
+
+ inline bool GetUpdatePosition () const;
+ void SetUpdatePosition (bool inbool);
+ inline bool GetUpdateRotation () const;
+ void SetUpdateRotation (bool inbool);
+ inline bool GetAutoTraverseOffMeshLink () const;
+ void SetAutoTraverseOffMeshLink (bool inbool);
+ inline bool GetAutoBraking () const;
+ void SetAutoBraking (bool inbool);
+ inline bool GetAutoRepath () const;
+ void SetAutoRepath (bool inbool);
+
+ inline float GetRadius () const;
+ inline float GetHeight () const;
+ inline float GetBaseOffset () const;
+
+ float CalculateScaledRadius () const;
+ float CalculateScaledHeight () const;
+
+ void SetRadius (float radius);
+ void SetHeight (float height);
+ void SetBaseOffset (float baseOffset);
+
+ bool Warp (const Vector3f& newPosition);
+ void Move (const Vector3f& motion);
+ void Stop (bool stopUpdates);
+ void Resume ();
+ void CompleteOffMeshLink ();
+
+ void UpdateState ();
+ void UpdateTransform (float deltaTime);
+
+ inline Vector3f GetGroundPositionFromTransform () const;
+
+ void OnNavMeshChanged ();
+ inline void OnNavMeshCleanup ();
+
+protected:
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+ virtual void Reset ();
+ virtual void SmartReset ();
+
+ void OnTransformChanged (int mask);
+
+private:
+
+ void AddToCrowdSystem ();
+ void RemoveFromCrowdSystem ();
+ void ReinstateInCrowdSystem ();
+ void StopOrResume (float remainingDistance);
+ void RepathIfStuck (float remainingDistance);
+ void UpdateRotation (Transform& transform, float deltaTime);
+
+ void FillAgentParams (dtCrowdAgentParams& params);
+ void UpdateActiveAgentParameters ();
+ Vector2f CalculateDimensionScales () const;
+
+ inline float CalculateScaledBaseOffset (const Transform& transform) const;
+ inline void SetTransformFromGroundPosition (const Vector3f& groundPosition);
+
+ int GetCurrentPolygonMask () const;
+ const dtQueryFilter& GetFilter () const;
+ const dtCrowdAgent* GetInternalAgent () const;
+ static const dtNavMeshQuery* GetInternalNavMeshQuery ();
+ static dtCrowd* GetCrowdSystem ();
+ static inline float EnsurePositive (float value);
+
+ Vector3f m_Destination;
+ Vector3f m_RequestedDestination;
+ float m_Radius;
+ float m_Height;
+ float m_BaseOffset;
+ float m_Speed;
+ float m_AngularSpeed;
+ float m_Angle;
+ float m_Acceleration;
+ float m_StoppingDistance;
+
+ // Internal Crowd system agent index
+ dtCrowdHandle m_AgentHandle;
+ int m_InstanceID;
+ // Persistent manager handle
+ int m_ManagerHandle;
+
+ dtPolyRef m_CachedPolyRef;
+
+ ObstacleAvoidanceType m_ObstacleAvoidanceType; ///< enum { None = 0, Low Quality, Medium Quality, Good Quality, High Quality }
+ UInt32 m_WalkableMask;
+ int m_AvoidancePriority;
+ bool m_AutoTraverseOffMeshLink;
+ bool m_AutoBraking;
+ bool m_AutoRepath;
+
+ bool m_UpdatePosition : 1;
+ bool m_UpdateRotation : 1;
+ bool m_StopDistance : 1;
+ bool m_StopExplicit : 1;
+ bool m_StopRotating : 1;
+ bool m_Request : 1;
+
+ friend void DrawNavMeshAgent (const NavMeshAgent& agent);
+};
+
+inline float NavMeshAgent::EnsurePositive (float value)
+{
+ return std::max (0.00001F, value);
+}
+
+inline Vector3f NavMeshAgent::GetGroundPositionFromTransform () const
+{
+ const Transform& transform = GetComponent (Transform);
+ return transform.TransformPoint (Vector3f (0, -m_BaseOffset, 0));
+}
+
+inline void NavMeshAgent::SetTransformFromGroundPosition (const Vector3f& groundPosition)
+{
+ Transform& transform = GetComponent (Transform);
+ transform.SetPositionWithLocalOffset (groundPosition, Vector3f (0.0f, -m_BaseOffset, 0.0f));
+}
+
+inline float NavMeshAgent::CalculateScaledBaseOffset (const Transform& transform) const
+{
+ return m_BaseOffset * Abs (transform.GetWorldScaleLossy ().y);
+}
+
+inline bool NavMeshAgent::InCrowdSystem () const
+{
+ return m_AgentHandle.IsValid ();
+}
+
+inline void NavMeshAgent::SetManagerHandle (int handle)
+{
+ m_ManagerHandle = handle;
+}
+
+inline void NavMeshAgent::ResetCachedPolyRef ()
+{
+ m_CachedPolyRef = -1;
+}
+
+inline ObstacleAvoidanceType NavMeshAgent::GetObstacleAvoidanceType () const
+{
+ return m_ObstacleAvoidanceType;
+}
+
+inline int NavMeshAgent::GetAvoidancePriority () const
+{
+ return m_AvoidancePriority;
+}
+
+inline float NavMeshAgent::GetSpeed () const
+{
+ return m_Speed;
+}
+
+inline float NavMeshAgent::GetAngularSpeed () const
+{
+ return m_AngularSpeed;
+}
+
+inline float NavMeshAgent::GetAcceleration () const
+{
+ return m_Acceleration;
+}
+
+inline float NavMeshAgent::GetStoppingDistance () const
+{
+ return m_StoppingDistance;
+}
+
+inline bool NavMeshAgent::GetUpdatePosition () const
+{
+ return m_UpdatePosition;
+}
+
+inline bool NavMeshAgent::GetUpdateRotation () const
+{
+ return m_UpdateRotation;
+}
+
+inline bool NavMeshAgent::GetAutoTraverseOffMeshLink () const
+{
+ return m_AutoTraverseOffMeshLink;
+}
+
+inline bool NavMeshAgent::GetAutoBraking () const
+{
+ return m_AutoBraking;
+}
+
+inline bool NavMeshAgent::GetAutoRepath () const
+{
+ return m_AutoRepath;
+}
+
+inline float NavMeshAgent::GetRadius () const
+{
+ return m_Radius;
+}
+
+inline float NavMeshAgent::GetHeight () const
+{
+ return m_Height;
+}
+
+inline float NavMeshAgent::GetBaseOffset () const
+{
+ return m_BaseOffset;
+}
+
+inline void NavMeshAgent::OnNavMeshCleanup ()
+{
+ RemoveFromCrowdSystem ();
+}
+
+#endif
diff --git a/Runtime/NavMesh/NavMeshCarving.cpp b/Runtime/NavMesh/NavMeshCarving.cpp
new file mode 100644
index 0000000..d437bf7
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshCarving.cpp
@@ -0,0 +1,206 @@
+#include "UnityPrefix.h"
+#include "NavMeshCarving.h"
+
+#include "DetourNavMesh.h"
+#include "NavMesh.h"
+#include "NavMeshSettings.h"
+#include "DetourCrowdTypes.h"
+#include "NavMeshObstacle.h"
+#include "NavMeshProfiler.h"
+#include <float.h>
+
+// Performance note:
+// The performance of the current implementation will be sub-optimal
+// in case of many tiles compared to update count.
+// [ e.g. : tileCount >= 10*(newCarveData.size () + m_OldCarveBounds.size ()) ]
+//
+// Consider letting obstacles push themselves to lists of tiles which they cover.
+
+PROFILER_INFORMATION (gCrowdManagerCarve, "CrowdManager.CarveNavmesh", kProfilerAI)
+
+
+NavMeshCarving::NavMeshCarving ()
+{
+}
+
+NavMeshCarving::~NavMeshCarving ()
+{
+}
+
+#if ENABLE_NAVMESH_CARVING
+
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "NavMeshTileCarving.h"
+
+static void CalculateCarveBounds (MinMaxAABB& carveBounds, const NavMeshCarveData& carveData)
+{
+ AABB localCarveBounds (Vector3f::zero, carveData.size);
+ AABB worldCarveBounds;
+ TransformAABBSlow (localCarveBounds, carveData.transform, worldCarveBounds);
+ carveBounds = worldCarveBounds;
+}
+
+void NavMeshCarving::AddObstacle (NavMeshObstacle& obstacle, int& handle)
+{
+ Assert (handle == -1);
+ handle = m_ObstacleInfo.size ();
+ ObstacleCarveInfo& info = m_ObstacleInfo.push_back ();
+ info.obstacle = &obstacle;
+ memset (&info.carveData, 0, sizeof (info.carveData));
+}
+
+void NavMeshCarving::RemoveObstacle (int& handle)
+{
+ Assert (handle >= 0 && handle < m_ObstacleInfo.size ());
+ const int last = m_ObstacleInfo.size () - 1;
+ m_OldCarveBounds.push_back (m_ObstacleInfo[handle].carveBounds);
+ if (handle != last)
+ {
+ m_ObstacleInfo[handle] = m_ObstacleInfo[last];
+ m_ObstacleInfo[handle].obstacle->SetCarveHandle (handle);
+ }
+ handle = -1;
+ m_ObstacleInfo.pop_back ();
+}
+
+bool NavMeshCarving::Carve ()
+{
+ PROFILER_AUTO (gCrowdManagerCarve, NULL)
+
+ NavMesh* navmesh = GetNavMeshSettings ().GetNavMesh ();
+ if (navmesh == NULL)
+ return false;
+
+ // Temporary copy of new data for faster culling
+ dynamic_array<NavMeshCarveData> newCarveData (m_ObstacleInfo.size (), kMemTempAlloc);
+ UpdateCarveData (newCarveData);
+
+ if (newCarveData.empty () && m_OldCarveBounds.empty ())
+ return false;
+
+ return UpdateTiles (navmesh, newCarveData);
+}
+
+// For registered obstacles - collect info for those that need updating
+void NavMeshCarving::UpdateCarveData (dynamic_array<NavMeshCarveData>& newCarveData)
+{
+ newCarveData.resize_uninitialized (0);
+
+ const size_t obstacleCount = m_ObstacleInfo.size ();
+ for (size_t i = 0; i < obstacleCount; ++i)
+ {
+ if (!m_ObstacleInfo[i].obstacle->NeedsRebuild ())
+ continue;
+
+ // Store previous carved data
+ m_OldCarveBounds.push_back (m_ObstacleInfo[i].carveBounds);
+ NavMeshCarveData& data = newCarveData.push_back ();
+ m_ObstacleInfo[i].obstacle->WillRebuildNavmesh (data);
+ m_ObstacleInfo[i].carveData = data;
+ CalculateCarveBounds (m_ObstacleInfo[i].carveBounds, data);
+ }
+}
+
+// Extend the tile bounds by the carving dimensions
+// note that the asymmetry in the vertical direction.
+static void CalculateExtendedTileBounds (MinMaxAABB& bounds, const dtMeshTile* tile)
+{
+ const Vector3f tileMin = Vector3f (tile->header->bmin);
+ const Vector3f tileMax = Vector3f (tile->header->bmax);
+ const float horizontalMargin = tile->header->walkableRadius;
+ const float depthMargin = tile->header->walkableRadius;
+
+ bounds.m_Min = Vector3f (tileMin.x - horizontalMargin, tileMin.y, tileMin.z - horizontalMargin);
+ bounds.m_Max = Vector3f (tileMax.x + horizontalMargin, tileMax.y + depthMargin, tileMax.z + horizontalMargin);
+}
+
+bool NavMeshCarving::UpdateTiles (NavMesh* navmesh, const dynamic_array<NavMeshCarveData>& newCarveData)
+{
+ dtNavMesh* detourNavMesh = navmesh->GetInternalNavMesh ();
+ const size_t tileCount = detourNavMesh->tileCount ();
+ const size_t obstacleCount = m_ObstacleInfo.size ();
+
+ dynamic_array<Vector3f> sizes (obstacleCount, kMemTempAlloc);
+ dynamic_array<Matrix4x4f> transforms (obstacleCount, kMemTempAlloc);
+ dynamic_array<MinMaxAABB> aabbs (obstacleCount, kMemTempAlloc);
+
+ int updatedTileCount = 0;
+ for (size_t i = 0; i < tileCount; ++i)
+ {
+ const dtMeshTile* tile = detourNavMesh->getTile (i);
+ if (!tile || !tile->header)
+ continue;
+
+ MinMaxAABB tileBounds;
+ CalculateExtendedTileBounds (tileBounds, tile);
+
+ const TileCarveStatus status = CollectCarveDataAndStatus (transforms, sizes, aabbs, newCarveData, tileBounds);
+ DebugAssert (transforms.size () == aabbs.size ());
+ DebugAssert (transforms.size () == sizes.size ());
+ if (status == kIgnore)
+ continue;
+
+ // Reinitialize tile since we have either 'kRestore' or 'kCarve' at this point
+ updatedTileCount++;
+ detourNavMesh->restoreTile (navmesh->GetMeshData (), navmesh->GetMeshDataSize (), i);
+
+ if (status == kCarve)
+ {
+ CarveNavMeshTile (tile, detourNavMesh, transforms.size (), transforms.begin (), sizes.begin (), aabbs.begin ());
+ }
+ }
+
+ m_OldCarveBounds.resize_uninitialized (0);
+
+ return updatedTileCount > 0;
+}
+
+// Does any of the bounds in 'arrayOfBounds' overlap with 'bounds'
+static bool AnyOverlaps (const dynamic_array<MinMaxAABB>& arrayOfBounds, const MinMaxAABB& bounds)
+{
+ const size_t count = arrayOfBounds.size ();
+ for (size_t i = 0; i < count; ++i)
+ {
+ if (IntersectAABBAABB (arrayOfBounds[i], bounds))
+ return true;
+ }
+ return false;
+}
+
+NavMeshCarving::TileCarveStatus NavMeshCarving::CollectCarveDataAndStatus (dynamic_array<Matrix4x4f>& transforms, dynamic_array<Vector3f>& sizes, dynamic_array<MinMaxAABB>& aabbs, const dynamic_array<NavMeshCarveData>& newCarveData, const MinMaxAABB& tileBounds) const
+{
+ CollectOverlappingCarveData (transforms, sizes, aabbs, tileBounds);
+ if (!transforms.empty ())
+ return kCarve;
+
+ if (AnyOverlaps (m_OldCarveBounds, tileBounds))
+ return kRestore;
+
+ return kIgnore;
+}
+
+void NavMeshCarving::CollectOverlappingCarveData (dynamic_array<Matrix4x4f>& transforms, dynamic_array<Vector3f>& sizes, dynamic_array<MinMaxAABB>& aabbs, const MinMaxAABB& bounds) const
+{
+ aabbs.resize_uninitialized (0);
+ sizes.resize_uninitialized (0);
+ transforms.resize_uninitialized (0);
+
+ const size_t count = m_ObstacleInfo.size ();
+ for (size_t i = 0; i < count; ++i)
+ {
+ if (IntersectAABBAABB (m_ObstacleInfo[i].carveBounds, bounds))
+ {
+ aabbs.push_back (m_ObstacleInfo[i].carveBounds);
+ sizes.push_back (m_ObstacleInfo[i].carveData.size);
+ transforms.push_back (m_ObstacleInfo[i].carveData.transform);
+ }
+ }
+}
+
+#else
+bool NavMeshCarving::Carve () {return false;}
+void NavMeshCarving::AddObstacle (NavMeshObstacle& obstacle, int& handle) {}
+void NavMeshCarving::RemoveObstacle (int& handle) {}
+
+#endif // ENABLE_NAVMESH_CARVING
diff --git a/Runtime/NavMesh/NavMeshCarving.h b/Runtime/NavMesh/NavMeshCarving.h
new file mode 100644
index 0000000..0cec244
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshCarving.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/NavMesh/NavMeshTypes.h"
+#include "Runtime/Geometry/AABB.h"
+
+class NavMeshObstacle;
+class NavMesh;
+
+class NavMeshCarving
+{
+ enum TileCarveStatus
+ {
+ kIgnore = 0,
+ kRestore = 1,
+ kCarve = 2
+ };
+
+ struct ObstacleCarveInfo
+ {
+ NavMeshCarveData carveData;
+ MinMaxAABB carveBounds;
+ NavMeshObstacle* obstacle;
+ };
+
+public:
+ NavMeshCarving ();
+ ~NavMeshCarving ();
+
+
+ void AddObstacle (NavMeshObstacle& obstacle, int& handle);
+ void RemoveObstacle (int& handle);
+ bool Carve ();
+
+private:
+
+ void UpdateCarveData (dynamic_array<NavMeshCarveData>& newCarveData);
+ bool UpdateTiles (NavMesh* navmesh, const dynamic_array<NavMeshCarveData>& newCarveData);
+
+ TileCarveStatus CollectCarveDataAndStatus (dynamic_array<Matrix4x4f>& transforms, dynamic_array<Vector3f>& sizes, dynamic_array<MinMaxAABB>& aabbs, const dynamic_array<NavMeshCarveData>& newCarveData, const MinMaxAABB& tileBounds) const;
+ void CollectOverlappingCarveData (dynamic_array<Matrix4x4f>& transforms, dynamic_array<Vector3f>& sizes, dynamic_array<MinMaxAABB>& aabbs, const MinMaxAABB& bounds) const;
+
+ dynamic_array<ObstacleCarveInfo> m_ObstacleInfo;
+ dynamic_array<MinMaxAABB> m_OldCarveBounds;
+};
diff --git a/Runtime/NavMesh/NavMeshLayers.cpp b/Runtime/NavMesh/NavMeshLayers.cpp
new file mode 100644
index 0000000..bae3674
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshLayers.cpp
@@ -0,0 +1,156 @@
+#include "UnityPrefix.h"
+#include "NavMeshLayers.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/SerializationMetaFlags.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "NavMeshManager.h"
+
+
+const char* NavMeshLayers::s_WarningCostLessThanOne = "Setting a NavMeshLayer cost less than one can give unexpected results.";
+
+NavMeshLayers::NavMeshLayers (MemLabelId& label, ObjectCreationMode mode)
+: Super (label, mode)
+{
+
+}
+
+NavMeshLayers::~NavMeshLayers ()
+{
+
+}
+
+void NavMeshLayers::Reset ()
+{
+ Super::Reset ();
+
+ m_Layers[kNotWalkable].name = "Not Walkable";
+ m_Layers[kNotWalkable].cost = 1.0f;
+ m_Layers[kNotWalkable].editType = NavMeshLayerData::kEditNone;
+
+ m_Layers[kDefaultLayer].name = "Default";
+ m_Layers[kDefaultLayer].cost = 1.0f;
+ m_Layers[kDefaultLayer].editType = NavMeshLayerData::kEditCost;
+
+ m_Layers[kJumpLayer].name = "Jump";
+ m_Layers[kJumpLayer].cost = 2.0f;
+ m_Layers[kJumpLayer].editType = NavMeshLayerData::kEditCost;
+
+ for (int i = kBuiltinLayerCount; i < kLayerCount; ++i)
+ {
+ m_Layers[i].cost = 1.0F;
+ m_Layers[i].editType = NavMeshLayerData::kEditCost | NavMeshLayerData::kEditName;
+ }
+}
+
+
+template<class TransferFunction>
+void NavMeshLayers::NavMeshLayerData::Transfer (TransferFunction& transfer)
+{
+ TransferMetaFlags nameFlag = (editType & kEditName) ? kNoTransferFlags : kNotEditableMask;
+ TransferMetaFlags costFlag = (editType & kEditCost) ? kNoTransferFlags : kNotEditableMask;
+ transfer.Transfer (name, "name", nameFlag);
+ transfer.Transfer (cost, "cost", costFlag);
+ transfer.Transfer (editType, "editType", kNotEditableMask|kHideInEditorMask);
+}
+
+template<class TransferFunction>
+void NavMeshLayers::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ for (int i = 0; i < kLayerCount; ++i)
+ {
+ char name[64];
+ if (i < kBuiltinLayerCount)
+ sprintf (name, "Built-in Layer %d", i);
+ else
+ sprintf (name, "User Layer %d", i - kBuiltinLayerCount);
+
+ transfer.Transfer (m_Layers[i], name);
+ }
+}
+
+void NavMeshLayers::SetLayerCost (unsigned int index, float cost)
+{
+ if (index >= kLayerCount)
+ {
+ ErrorString ("Index out of bounds");
+ return;
+ }
+#if UNITY_EDITOR
+ if (cost < 1.0f)
+ {
+ WarningString(s_WarningCostLessThanOne);
+ }
+#endif
+ m_Layers[index].cost = cost;
+ GetNavMeshManager ().UpdateAllNavMeshAgentCosts (index, cost);
+
+ SetDirty ();
+}
+
+float NavMeshLayers::GetLayerCost (unsigned int index) const
+{
+ if (index >= kLayerCount)
+ {
+ ErrorString ("Index out of bounds");
+ return 0.0F;
+ }
+ return m_Layers[index].cost;
+}
+
+void NavMeshLayers::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ // When the user changes the cost in the inspector
+ if (UNITY_EDITOR && (awakeMode & kDidLoadFromDisk) == 0)
+ {
+ for (int i = 0; i < kLayerCount; ++i)
+ GetNavMeshManager ().UpdateAllNavMeshAgentCosts (i, m_Layers[i].cost);
+ }
+}
+
+int NavMeshLayers::GetNavMeshLayerFromName (const UnityStr& layerName) const
+{
+ for (int i = 0; i < kLayerCount; ++i)
+ {
+ if (m_Layers[i].name.compare (layerName) == 0)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+std::vector<std::string> NavMeshLayers::NavMeshLayerNames () const
+{
+ std::vector<std::string> layers;
+ for (int i = 0; i < kLayerCount; ++i)
+ {
+ if (m_Layers[i].name.length () != 0)
+ {
+ layers.push_back (m_Layers[i].name);
+ }
+ }
+ return layers;
+}
+
+void NavMeshLayers::CheckConsistency ()
+{
+#if UNITY_EDITOR
+ for (int i = 0; i < kLayerCount; ++i)
+ {
+ if (m_Layers[i].cost < 1.0f)
+ {
+ WarningString (s_WarningCostLessThanOne);
+ return;
+ }
+ }
+#endif
+}
+
+
+IMPLEMENT_CLASS (NavMeshLayers)
+IMPLEMENT_OBJECT_SERIALIZE (NavMeshLayers)
+GET_MANAGER (NavMeshLayers)
diff --git a/Runtime/NavMesh/NavMeshLayers.h b/Runtime/NavMesh/NavMeshLayers.h
new file mode 100644
index 0000000..0c4085f
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshLayers.h
@@ -0,0 +1,63 @@
+#ifndef NAVMESH_LAYERS_H
+#define NAVMESH_LAYERS_H
+
+#include "Runtime/BaseClasses/GameManager.h"
+
+
+
+
+class NavMeshLayers : public GlobalGameManager
+{
+public:
+ struct NavMeshLayerData
+ {
+ DECLARE_SERIALIZE (NavMeshLayerData)
+ enum
+ {
+ kEditNone = 0,
+ kEditName = 1,
+ kEditCost = 2
+ };
+
+ UnityStr name;
+ float cost;
+ int editType;
+ };
+
+ enum BuiltinNavMeshLayers
+ {
+ kDefaultLayer = 0,
+ kNotWalkable = 1,
+ kJumpLayer = 2
+ };
+
+ NavMeshLayers (MemLabelId& label, ObjectCreationMode mode);
+ // ~NavMeshLayers (); declared-by-macro
+
+ REGISTER_DERIVED_CLASS (NavMeshLayers, GlobalGameManager)
+ DECLARE_OBJECT_SERIALIZE (NavMeshLayers)
+
+ virtual void Reset ();
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+ virtual void CheckConsistency ();
+
+ void SetLayerCost (unsigned int index, float cost);
+ float GetLayerCost (unsigned int index) const;
+ int GetNavMeshLayerFromName (const UnityStr& layerName) const;
+ std::vector<std::string> NavMeshLayerNames () const;
+
+ enum
+ {
+ kBuiltinLayerCount = 3,
+ kLayerCount = 32
+ };
+
+ static const char* s_WarningCostLessThanOne;
+private:
+
+ NavMeshLayerData m_Layers[kLayerCount];
+};
+
+NavMeshLayers& GetNavMeshLayers ();
+
+#endif
diff --git a/Runtime/NavMesh/NavMeshManager.cpp b/Runtime/NavMesh/NavMeshManager.cpp
new file mode 100644
index 0000000..f909996
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshManager.cpp
@@ -0,0 +1,388 @@
+#include "UnityPrefix.h"
+#include "NavMeshManager.h"
+
+#include "DetourFeatures.h"
+#include "DetourCrowd.h"
+#include "DetourCrowdTypes.h"
+#include "HeightMeshQuery.h"
+#include "Runtime/Input/TimeManager.h"
+#include "NavMeshAgent.h"
+#include "NavMeshObstacle.h"
+#include "NavMeshProfiler.h"
+#include "OffMeshLink.h"
+#include "NavMeshCarving.h"
+#include "Runtime/Threads/JobScheduler.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Core/Callbacks/PlayerLoopCallbacks.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+
+static const int MAX_ITERS_PER_UPDATE = 100;
+const int NavMeshManager::kInitialAgentCount = 4;
+
+PROFILER_INFORMATION (gCrowdManagerUpdate, "CrowdManager.Update", kProfilerAI)
+PROFILER_INFORMATION (gNavMeshAgentsUpdateState, "CrowdManager.NavMeshAgentStates", kProfilerAI)
+PROFILER_INFORMATION (gNavMeshAgentsUpdateTransform, "CrowdManager.NavMeshAgentTransforms", kProfilerAI)
+
+NavMeshManager::NavMeshManager ()
+{
+ m_CarvingSystem = NULL;
+ m_CrowdSystem = NULL;
+ m_CrowdAgentDebugInfo = NULL;
+ m_Profiler = UNITY_NEW (CrowdProfiler, kMemNavigation) ();
+#if UNITY_EDITOR
+ m_CrowdAgentDebugInfo = UNITY_NEW (dtCrowdAgentDebugInfo, kMemNavigation);
+ memset (m_CrowdAgentDebugInfo, 0, sizeof (*m_CrowdAgentDebugInfo));
+ m_CrowdAgentDebugInfo->idx = -1;
+#endif
+}
+
+NavMeshManager::~NavMeshManager ()
+{
+ if (m_CrowdAgentDebugInfo)
+ {
+ UNITY_DELETE (m_CrowdAgentDebugInfo, kMemNavigation);
+ }
+ UNITY_DELETE (m_CrowdSystem, kMemNavigation);
+ UNITY_DELETE (m_Profiler, kMemNavigation);
+ UNITY_DELETE (m_CarvingSystem, kMemNavigation);
+}
+
+template <typename T>
+static inline int RegisterInArray (dynamic_array<T*>& array, T& element)
+{
+ int handle = array.size ();
+ array.push_back (&element);
+ return handle;
+}
+
+template <typename T>
+static inline void UnregisterFromArray (dynamic_array<T*>& array, int handle)
+{
+ Assert (handle >= 0 && handle < array.size ());
+ int last = array.size () - 1;
+ if (handle != last)
+ {
+ T* swap = array[last];
+ array[handle] = swap;
+ swap->SetManagerHandle (handle);
+ }
+ array.pop_back ();
+}
+
+void NavMeshManager::RegisterAgent (NavMeshAgent& agent, int& handle)
+{
+ Assert (handle == -1);
+ handle = RegisterInArray (m_Agents, agent);
+}
+
+void NavMeshManager::UnregisterAgent (int& handle)
+{
+ UnregisterFromArray (m_Agents, handle);
+ handle = -1;
+}
+
+void NavMeshManager::RegisterObstacle (NavMeshObstacle& obstacle, int& handle)
+{
+ Assert (handle == -1);
+ handle = RegisterInArray (m_Obstacles, obstacle);
+}
+
+void NavMeshManager::UnregisterObstacle (int& handle)
+{
+ UnregisterFromArray (m_Obstacles, handle);
+ handle = -1;
+}
+
+void NavMeshManager::RegisterOffMeshLink (OffMeshLink& link, int& handle)
+{
+ Assert (handle == -1);
+ handle = RegisterInArray (m_Links, link);
+}
+
+void NavMeshManager::UnregisterOffMeshLink (int& handle)
+{
+ UnregisterFromArray (m_Links, handle);
+ handle = -1;
+}
+
+#if ENABLE_NAVMESH_CARVING
+void NavMeshManager::UpdateCarving ()
+{
+ if (m_CarvingSystem && m_CarvingSystem->Carve ())
+ {
+ InvalidateDynamicLinks ();
+ for (size_t i=0;i<m_Agents.size (); ++i)
+ {
+ m_Agents[i]->OnNavMeshChanged ();
+ }
+ }
+}
+#else
+void NavMeshManager::UpdateCarving () {}
+#endif
+
+
+#if DT_DYNAMIC_OFFMESHLINK
+PROFILER_INFORMATION (gCrowdManagerLinks, "CrowdManager.DynamicOffMeshLinks", kProfilerAI)
+
+#include "Runtime/NavMesh/NavMesh.h"
+#include "Runtime/NavMesh/NavMeshSettings.h"
+void NavMeshManager::InvalidateDynamicLinks ()
+{
+ NavMesh* navmesh = GetNavMeshSettings ().GetNavMesh ();
+ dtNavMesh* detourNavMesh = navmesh->GetInternalNavMesh ();
+ detourNavMesh->ClearDynamicOffMeshLinks ();
+ for (size_t i = 0; i < m_Links.size (); ++i)
+ {
+ m_Links[i]->OnNavMeshChanged ();
+ }
+}
+
+void NavMeshManager::UpdateDynamicLinks ()
+{
+ PROFILER_AUTO (gCrowdManagerLinks, NULL)
+ if (IsWorldPlaying ())
+ {
+ for (size_t i = 0; i < m_Links.size (); ++i)
+ m_Links[i]->UpdateMovedPositions ();
+ }
+ else
+ {
+ for (size_t i = 0; i < m_Links.size (); ++i)
+ m_Links[i]->UpdatePositions ();
+ }
+}
+#else
+void NavMeshManager::InvalidateDynamicLinks () {}
+void NavMeshManager::UpdateDynamicLinks () {}
+#endif
+
+void NavMeshManager::UpdateCrowdSystem (float deltaTime)
+{
+ m_CrowdSystem->update (deltaTime, m_Profiler);
+}
+
+void NavMeshManager::Update ()
+{
+ if (GetInternalNavMeshQuery () == NULL)
+ return;
+
+ UpdateCarving ();
+ UpdateDynamicLinks ();
+
+ const float deltaTime = GetDeltaTime ();
+ if (deltaTime == 0.0f)
+ return;
+
+ PROFILER_BEGIN (gCrowdManagerUpdate, NULL)
+
+ UpdateCrowdSystem (deltaTime);
+
+ PROFILER_BEGIN (gNavMeshAgentsUpdateState, NULL)
+ for (size_t i = 0; i < m_Agents.size (); ++i)
+ {
+ m_Agents[i]->UpdateState ();
+ }
+ PROFILER_END
+
+ PROFILER_BEGIN (gNavMeshAgentsUpdateTransform, NULL)
+ for (size_t i = 0; i < m_Agents.size (); ++i)
+ {
+ m_Agents[i]->UpdateTransform (deltaTime);
+ }
+ PROFILER_END
+
+ PROFILER_END
+}
+
+// Cleanup references to 'mesh'.
+// Skips cleanup if the 'mesh' is not the currently loaded internal navmesh.
+// This is needed because when loading scenes we'll temporarily have two instances of NavMesh class.
+// (i.e. ctor of new NavMesh object is call before dtor of old NavMesh object).
+void NavMeshManager::CleanupMeshDependencies (const dtNavMesh* mesh)
+{
+ if (mesh == GetInternalNavMesh ())
+ {
+ CleanupMeshDependencies ();
+ }
+}
+
+// Unconditionally cleanup the navmesh dependencies
+void NavMeshManager::CleanupMeshDependencies ()
+{
+ NotifyNavMeshCleanup ();
+
+ if (m_CrowdSystem)
+ {
+ m_CrowdSystem->purge ();
+ UNITY_DELETE (m_CrowdSystem, kMemNavigation);
+ }
+}
+
+const dtMeshHeader* NavMeshManager::GetNavMeshHeader (const dtNavMesh* navmesh)
+{
+ if (navmesh == NULL || navmesh->tileCount () == 0)
+ {
+ return NULL;
+ }
+ return navmesh->getTile (0)->header;
+}
+
+void NavMeshManager::Initialize (const dtNavMesh* navMesh, const HeightMeshQuery* heightMeshQuery)
+{
+ InitializeCarvingSystem ();
+
+ const dtMeshHeader* header = GetNavMeshHeader (navMesh);
+ if (!header)
+ {
+ CleanupMeshDependencies ();
+ return;
+ }
+
+ if (!InitializeCrowdSystem (navMesh, heightMeshQuery, header))
+ {
+ CleanupMeshDependencies ();
+ return;
+ }
+
+ InitializeObstacleSamplingQuality ();
+
+ NotifyNavMeshChanged ();
+}
+
+void NavMeshManager::InitializeCarvingSystem ()
+{
+#if ENABLE_NAVMESH_CARVING
+ // Carving requires advanced version. Otherwise leave the null pointer for 'm_CarvingSystem'.
+ if (!m_CarvingSystem && GetBuildSettings ().hasAdvancedVersion)
+ {
+ m_CarvingSystem = UNITY_NEW (NavMeshCarving, kMemNavigation);
+ }
+#endif
+}
+
+bool NavMeshManager::InitializeCrowdSystem (const dtNavMesh* navmesh, const HeightMeshQuery* heightMeshQuery, const dtMeshHeader* header)
+{
+ Assert (navmesh);
+
+ // Lazily create crowd manager
+ if (m_CrowdSystem == NULL)
+ {
+ m_CrowdSystem = UNITY_NEW (dtCrowd, kMemNavigation) ();
+ if (m_CrowdSystem == NULL)
+ {
+ return false;
+ }
+ m_CrowdSystem->init (kInitialAgentCount, MAX_ITERS_PER_UPDATE); /////@TODO: WRONG
+ }
+
+ const float queryRange = 10.0f*header->walkableRadius; ///@TODO: UN-HACK
+ if (!m_CrowdSystem->setNavMesh (navmesh, queryRange))
+ {
+ return false;
+ }
+
+ m_CrowdSystem->setHeightMeshQuery (heightMeshQuery);
+
+ return true;
+}
+
+void NavMeshManager::InitializeObstacleSamplingQuality ()
+{
+ // Setup local avoidance params to different qualities.
+ dtObstacleAvoidanceParams params;
+ // Use mostly default settings, copy from dtCrowd.
+ memcpy (&params, m_CrowdSystem->getObstacleAvoidanceParams (0), sizeof (dtObstacleAvoidanceParams));
+
+ // Low (11)
+ params.adaptiveDivs = 5;
+ params.adaptiveRings = 2;
+ params.adaptiveDepth = 1;
+ m_CrowdSystem->setObstacleAvoidanceParams (kLowQualityObstacleAvoidance, &params);
+
+ // Medium (22)
+ params.adaptiveDivs = 5;
+ params.adaptiveRings = 2;
+ params.adaptiveDepth = 2;
+ m_CrowdSystem->setObstacleAvoidanceParams (kMedQualityObstacleAvoidance, &params);
+
+ // Good (45)
+ params.adaptiveDivs = 7;
+ params.adaptiveRings = 2;
+ params.adaptiveDepth = 3;
+ m_CrowdSystem->setObstacleAvoidanceParams (kGoodQualityObstacleAvoidance, &params);
+
+ // High (66)
+ params.adaptiveDivs = 7;
+ params.adaptiveRings = 3;
+ params.adaptiveDepth = 3;
+ m_CrowdSystem->setObstacleAvoidanceParams (kHighQualityObstacleAvoidance, &params);
+}
+
+void NavMeshManager::NotifyNavMeshChanged ()
+{
+ for (size_t i = 0; i < m_Agents.size (); ++i)
+ m_Agents[i]->OnNavMeshChanged ();
+
+ for (size_t i = 0; i < m_Obstacles.size (); ++i)
+ m_Obstacles[i]->OnNavMeshChanged ();
+
+ for (size_t i = 0; i < m_Links.size (); ++i)
+ m_Links[i]->OnNavMeshChanged ();
+}
+
+void NavMeshManager::NotifyNavMeshCleanup ()
+{
+ for (size_t i = 0; i < m_Agents.size (); ++i)
+ m_Agents[i]->OnNavMeshCleanup ();
+
+ for (size_t i = 0; i < m_Obstacles.size (); ++i)
+ m_Obstacles[i]->OnNavMeshCleanup ();
+
+ for (size_t i = 0; i < m_Links.size (); ++i)
+ m_Links[i]->OnNavMeshCleanup ();
+}
+
+void NavMeshManager::UpdateAllNavMeshAgentCosts (int layerIndex, float layerCost)
+{
+ if (m_CrowdSystem != NULL)
+ {
+ m_CrowdSystem->UpdateFilterCost (layerIndex, layerCost);
+ }
+}
+
+const dtNavMeshQuery* NavMeshManager::GetInternalNavMeshQuery () const
+{
+ if (m_CrowdSystem == NULL)
+ return NULL;
+ return m_CrowdSystem->getNavMeshQuery ();
+}
+
+const dtNavMesh* NavMeshManager::GetInternalNavMesh () const
+{
+ if (m_CrowdSystem == NULL || m_CrowdSystem->getNavMeshQuery () == NULL)
+ return NULL;
+ return m_CrowdSystem->getNavMeshQuery ()->getAttachedNavMesh ();
+}
+
+static NavMeshManager* gManager = NULL;
+
+void InitializeNavMeshManager ()
+{
+ Assert (gManager == NULL);
+ gManager = UNITY_NEW (NavMeshManager, kMemNavigation) ();
+
+ REGISTER_PLAYERLOOP_CALL(NavMeshUpdate, GetNavMeshManager ().Update ());
+}
+
+void CleanupNavMeshManager ()
+{
+ UNITY_DELETE (gManager, kMemNavigation);
+ gManager = NULL;
+}
+
+NavMeshManager& GetNavMeshManager ()
+{
+ return *gManager;
+}
diff --git a/Runtime/NavMesh/NavMeshManager.h b/Runtime/NavMesh/NavMeshManager.h
new file mode 100644
index 0000000..e761ef7
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshManager.h
@@ -0,0 +1,96 @@
+#pragma once
+
+#include "Runtime/BaseClasses/GameManager.h"
+
+class CrowdProfiler;
+class HeightMeshQuery;
+class NavMeshAgent;
+class NavMeshCarving;
+class NavMeshObstacle;
+class OffMeshLink;
+class dtCrowd;
+class dtNavMesh;
+class dtNavMeshQuery;
+struct dtCrowdAgentDebugInfo;
+struct dtMeshHeader;
+
+
+class NavMeshManager
+{
+public:
+
+ NavMeshManager ();
+ ~NavMeshManager ();
+
+ // NavMeshModule Interface
+ virtual void Update ();
+
+ void Initialize (const dtNavMesh* navMesh, const HeightMeshQuery* heightMeshQuery);
+ void CleanupMeshDependencies (const dtNavMesh* mesh);
+ void CleanupMeshDependencies ();
+
+ inline dtCrowd* GetCrowdSystem ();
+ inline NavMeshCarving* GetCarvingSystem ();
+ const dtNavMeshQuery* GetInternalNavMeshQuery () const;
+ const dtNavMesh* GetInternalNavMesh () const;
+
+ void RegisterAgent (NavMeshAgent& agent, int& handle);
+ void UnregisterAgent (int& handle);
+
+ void RegisterObstacle (NavMeshObstacle& obstacle, int& handle);
+ void UnregisterObstacle (int& handle);
+
+ void RegisterOffMeshLink (OffMeshLink& link, int& handle);
+ void UnregisterOffMeshLink (int& handle);
+
+ void UpdateAllNavMeshAgentCosts (int layerIndex, float layerCost);
+
+#if UNITY_EDITOR
+ inline dtCrowdAgentDebugInfo* GetInternalDebugInfo ();
+#endif
+
+private:
+ const dtMeshHeader* GetNavMeshHeader (const dtNavMesh* navmesh);
+ bool InitializeCrowdSystem (const dtNavMesh* navmesh, const HeightMeshQuery* heightMeshQuery, const dtMeshHeader* header);
+ void InitializeObstacleSamplingQuality ();
+ void InitializeCarvingSystem ();
+
+ void NotifyNavMeshChanged ();
+ void NotifyNavMeshCleanup ();
+ void UpdateCrowdSystem (float deltaTime);
+ void UpdateCarving ();
+ void UpdateDynamicLinks ();
+ void InvalidateDynamicLinks ();
+
+ dynamic_array<NavMeshAgent*> m_Agents;
+ dynamic_array<NavMeshObstacle*> m_Obstacles;
+ dynamic_array<OffMeshLink*> m_Links;
+
+ NavMeshCarving* m_CarvingSystem;
+
+ dtCrowd* m_CrowdSystem;
+ dtCrowdAgentDebugInfo* m_CrowdAgentDebugInfo;
+ CrowdProfiler* m_Profiler;
+ static const int kInitialAgentCount;
+};
+
+inline dtCrowd* NavMeshManager::GetCrowdSystem ()
+{
+ return m_CrowdSystem;
+}
+
+inline NavMeshCarving* NavMeshManager::GetCarvingSystem ()
+{
+ return m_CarvingSystem;
+}
+
+#if UNITY_EDITOR
+inline dtCrowdAgentDebugInfo* NavMeshManager::GetInternalDebugInfo ()
+{
+ return m_CrowdAgentDebugInfo;
+}
+#endif // UNITY_EDITOR
+
+NavMeshManager& GetNavMeshManager ();
+void InitializeNavMeshManager ();
+void CleanupNavMeshManager ();
diff --git a/Runtime/NavMesh/NavMeshModule.jam b/Runtime/NavMesh/NavMeshModule.jam
new file mode 100644
index 0000000..d8f27fa
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshModule.jam
@@ -0,0 +1,114 @@
+rule NavMeshModule_ReportCpp
+{
+ local navMeshSources =
+ NavMeshModule.jam
+
+ DynamicMeshTests.cpp
+ DynamicMesh.cpp
+ DynamicMesh.h
+ HeightMeshQuery.cpp
+ HeightMeshQuery.h
+ HeightmapData.h
+ NavMesh.cpp
+ NavMesh.h
+ NavMeshAgent.cpp
+ NavMeshAgent.h
+ NavMeshCarving.cpp
+ NavMeshCarving.h
+ NavMeshTileCarving.cpp
+ NavMeshTileCarving.h
+ NavMeshTileConversion.cpp
+ NavMeshTileConversion.h
+ NavMeshLayers.cpp
+ NavMeshLayers.h
+ NavigationModuleRegistration.cpp
+ NavMeshManager.cpp
+ NavMeshManager.h
+ NavMeshObstacle.cpp
+ NavMeshObstacle.h
+ NavMeshPath.cpp
+ NavMeshPath.h
+ NavMeshProfiler.h
+ NavMeshSettings.cpp
+ NavMeshSettings.h
+ NavMeshTypes.h
+ OffMeshLink.cpp
+ OffMeshLink.h
+ ;
+
+ local detourSources =
+ Detour/Include/DetourAlloc.h
+ Detour/Include/DetourAssert.h
+ Detour/Include/DetourCommon.h
+ Detour/Include/DetourContext.h
+ Detour/Include/DetourDynamicLink.h
+ Detour/Include/DetourFeatures.h
+ Detour/Include/DetourNavMesh.h
+ Detour/Include/DetourNavMeshBuilder.h
+ Detour/Include/DetourNavMeshQuery.h
+ Detour/Include/DetourNearestPolyQuery.h
+ Detour/Include/DetourNode.h
+ Detour/Include/DetourQueryFilter.h
+ Detour/Include/DetourReference.h
+ Detour/Include/DetourSwapEndian.h
+ Detour/Source/DetourAlloc.cpp
+ Detour/Source/DetourCommon.cpp
+ Detour/Source/DetourNavMesh.cpp
+ Detour/Source/DetourNavMeshBuilder.cpp
+ Detour/Source/DetourNavMeshQuery.cpp
+ Detour/Source/DetourNearestPolyQuery.cpp
+ Detour/Source/DetourNode.cpp
+ Detour/Source/DetourSwapEndian.cpp
+ DetourCrowd/Include/DetourCrowd.h
+ DetourCrowd/Include/DetourCrowdUpdate.h
+ DetourCrowd/Include/DetourCrowdTypes.h
+ DetourCrowd/Include/DetourLocalBoundary.h
+ DetourCrowd/Include/DetourObstacleAvoidance.h
+ DetourCrowd/Include/DetourOccupied.h
+ DetourCrowd/Include/DetourPathCorridor.h
+ DetourCrowd/Include/DetourPathQueue.h
+ DetourCrowd/Include/DetourProximityGrid.h
+ DetourCrowd/Source/DetourCrowd.cpp
+ DetourCrowd/Source/DetourCrowdUpdate.cpp
+ DetourCrowd/Source/DetourLocalBoundary.cpp
+ DetourCrowd/Source/DetourObstacleAvoidance.cpp
+ DetourCrowd/Source/DetourOccupied.cpp
+ DetourCrowd/Source/DetourPathCorridor.cpp
+ DetourCrowd/Source/DetourPathQueue.cpp
+ DetourCrowd/Source/DetourProximityGrid.cpp
+ ;
+
+ local modulesources =
+ Runtime/NavMesh/$(navMeshSources)
+ External/Recast/$(detourSources)
+ ;
+
+ return $(modulesources) ;
+}
+
+rule NavMeshModule_ReportTxt
+{
+ return
+ Runtime/NavMesh/ScriptBindings/NavMeshAgentBindings.txt
+ Runtime/NavMesh/ScriptBindings/NavMeshBindings.txt
+ Runtime/NavMesh/ScriptBindings/NavMeshPathBindings.txt
+ Runtime/NavMesh/ScriptBindings/NavMeshObstacleBindings.txt
+ ;
+}
+
+rule NavMeshModule_ReportIncludes
+{
+ return
+ External/Recast/Detour/Include
+ External/Recast/DetourCrowd/include
+ ;
+}
+
+rule NavMeshModule_Init
+{
+ OverrideModule NavMesh : GetModule_Cpp : byOverridingWithMethod : NavMeshModule_ReportCpp ;
+ OverrideModule NavMesh : GetModule_Txt : byOverridingWithMethod : NavMeshModule_ReportTxt ;
+ OverrideModule NavMesh : GetModule_Inc : byOverridingWithMethod : NavMeshModule_ReportIncludes ;
+}
+
+#RegisterModule NavMesh ;
diff --git a/Runtime/NavMesh/NavMeshObstacle.cpp b/Runtime/NavMesh/NavMeshObstacle.cpp
new file mode 100644
index 0000000..6808c85
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshObstacle.cpp
@@ -0,0 +1,349 @@
+#include "UnityPrefix.h"
+#include "NavMeshObstacle.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "DetourCrowd.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+#include "Runtime/BaseClasses/SupportedMessageOptimization.h"
+#include "NavMeshTypes.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "NavMeshManager.h"
+#include "NavMeshCarving.h"
+
+NavMeshObstacle::NavMeshObstacle (MemLabelId& label, ObjectCreationMode mode)
+: Super (label, mode)
+{
+ m_Velocity = Vector3f::zero;
+ m_ManagerHandle = -1;
+#if ENABLE_NAVMESH_CARVING
+ m_CarveHandle = -1;
+ m_Status = kForceRebuild;
+#endif
+ Reset ();
+}
+
+NavMeshObstacle::~NavMeshObstacle ()
+{
+}
+
+template<class TransferFunc>
+void NavMeshObstacle::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER (m_Radius);
+ TRANSFER (m_Height);
+#if ENABLE_NAVMESH_CARVING
+ TRANSFER (m_MoveThreshold);
+ TRANSFER (m_Carve);
+#endif
+}
+
+void NavMeshObstacle::CheckConsistency ()
+{
+ m_Radius = EnsurePositive (m_Radius);
+ m_Height = EnsurePositive (m_Height);
+#if ENABLE_NAVMESH_CARVING
+ m_MoveThreshold = max (0.0f, m_MoveThreshold);
+#endif
+}
+
+UInt32 NavMeshObstacle::CalculateSupportedMessages ()
+{
+ return kSupportsVelocityChanged;
+}
+
+void NavMeshObstacle::Reset ()
+{
+ Super::Reset ();
+
+ m_Radius = 0.5f;
+ m_Height = 2.0f;
+#if ENABLE_NAVMESH_CARVING
+ m_MoveThreshold = 0.0f;
+ m_Carve = false;
+#endif
+}
+
+void NavMeshObstacle::SmartReset ()
+{
+ Super::SmartReset ();
+ AABB aabb;
+ if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb))
+ {
+ Vector3f extents = aabb.GetCenter () + aabb.GetExtent ();
+
+ SetRadius (max (extents.x, extents.z));
+ SetHeight (2.0F*extents.y);
+ }
+ else
+ {
+ SetRadius (0.5F);
+ SetHeight (2.0F);
+ }
+}
+
+void NavMeshObstacle::AddToManager ()
+{
+ GetNavMeshManager ().RegisterObstacle (*this, m_ManagerHandle);
+ AddToCrowdSystem ();
+
+#if ENABLE_NAVMESH_CARVING
+ AddOrRemoveObstacle ();
+#endif
+}
+
+void NavMeshObstacle::RemoveFromManager ()
+{
+ RemoveFromCrowdSystem ();
+ GetNavMeshManager ().UnregisterObstacle (m_ManagerHandle);
+
+#if ENABLE_NAVMESH_CARVING
+ if (m_CarveHandle != -1)
+ {
+ if (NavMeshCarving* carving = GetNavMeshManager ().GetCarvingSystem ())
+ {
+ carving->RemoveObstacle (m_CarveHandle);
+ }
+ }
+#endif
+}
+
+void NavMeshObstacle::RemoveFromCrowdSystem ()
+{
+ if (!InCrowdSystem ())
+ return;
+ GetCrowdSystem ()->RemoveObstacle (m_ObstacleHandle);
+}
+
+void NavMeshObstacle::AwakeFromLoad (AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad (mode);
+
+#if ENABLE_NAVMESH_CARVING
+ if (m_ManagerHandle != -1)
+ AddOrRemoveObstacle ();
+#endif
+
+ dtCrowd* crowd = GetCrowdSystem ();
+ if (crowd == NULL || !InCrowdSystem ())
+ return;
+
+ const Vector3f position = GetPosition ();
+ const Vector3f dimensions = GetScaledDimensions ();
+ crowd->SetObstaclePosition (m_ObstacleHandle, position.GetPtr ());
+ crowd->SetObstacleDimensions (m_ObstacleHandle, dimensions.GetPtr ());
+}
+
+void NavMeshObstacle::OnNavMeshChanged ()
+{
+ if (!InCrowdSystem ())
+ {
+ AddToCrowdSystem ();
+ }
+
+#if ENABLE_NAVMESH_CARVING
+ m_Status |= kForceRebuild;
+#endif
+}
+
+void NavMeshObstacle::OnNavMeshCleanup ()
+{
+ RemoveFromCrowdSystem ();
+}
+
+void NavMeshObstacle::AddToCrowdSystem ()
+{
+ if (!IsWorldPlaying ())
+ return;
+
+ if (!GetNavMeshManager ().GetInternalNavMeshQuery ())
+ return;
+
+ Assert (!InCrowdSystem ());
+ dtCrowd* crowd = GetCrowdSystem ();
+ if (crowd == NULL)
+ return;
+
+ if (!crowd->AddObstacle (m_ObstacleHandle))
+ return;
+
+ const Vector3f position = GetPosition ();
+ const Vector3f dimensions = GetScaledDimensions ();
+ crowd->SetObstaclePosition (m_ObstacleHandle, position.GetPtr ());
+ crowd->SetObstacleDimensions (m_ObstacleHandle, dimensions.GetPtr ());
+}
+
+void NavMeshObstacle::InitializeClass ()
+{
+ REGISTER_MESSAGE (NavMeshObstacle, kTransformChanged, OnTransformChanged, int);
+ REGISTER_MESSAGE_PTR (NavMeshObstacle, kDidVelocityChange, OnVelocityChanged, Vector3f);
+}
+
+void NavMeshObstacle::OnTransformChanged (int mask)
+{
+#if ENABLE_NAVMESH_CARVING
+ m_Status |= kHasMoved;
+ if (mask & Transform::kRotationChanged)
+ {
+ m_Status = kForceRebuild;
+ }
+#endif
+ if (!InCrowdSystem ())
+ return;
+
+ if (mask & Transform::kPositionChanged)
+ {
+ const Vector3f position = GetPosition ();
+ GetCrowdSystem ()->SetObstaclePosition (m_ObstacleHandle, position.GetPtr ());
+ }
+
+ if (mask & Transform::kScaleChanged)
+ {
+ const Vector3f dimensions = GetScaledDimensions ();
+ GetCrowdSystem ()->SetObstacleDimensions (m_ObstacleHandle, dimensions.GetPtr ());
+ }
+}
+
+#if ENABLE_NAVMESH_CARVING
+
+void NavMeshObstacle::AddOrRemoveObstacle ()
+{
+ NavMeshCarving* carving = GetNavMeshManager ().GetCarvingSystem ();
+ if (!carving)
+ {
+ return;
+ }
+
+ if (m_Carve && m_CarveHandle == -1)
+ {
+ carving->AddObstacle (*this, m_CarveHandle);
+ RemoveFromCrowdSystem ();
+ }
+ else if (!m_Carve && m_CarveHandle != -1)
+ {
+ carving->RemoveObstacle (m_CarveHandle);
+ AddToCrowdSystem ();
+ }
+
+ m_Status |= kForceRebuild;
+}
+
+void NavMeshObstacle::WillRebuildNavmesh (NavMeshCarveData& carveData)
+{
+ const Vector3f position = GetPosition ();
+ CalculateTransformAndSize (carveData.transform, carveData.size);
+
+ m_LastCarvedPosition = position;
+ m_Status = kClean;
+}
+
+bool NavMeshObstacle::NeedsRebuild () const
+{
+ if (m_Status == kClean)
+ return false;
+
+ if (m_Status & kForceRebuild)
+ return true;
+
+ if (m_Status & kHasMoved)
+ {
+ const Vector3f position = GetComponent (Transform).GetPosition ();
+ const float sqrDistance = SqrMagnitude (m_LastCarvedPosition - position);
+ if (sqrDistance > m_MoveThreshold * m_MoveThreshold)
+ return true;
+ }
+
+ return false;
+}
+
+void NavMeshObstacle::SetCarving (bool carve)
+{
+ if (m_Carve == carve)
+ return;
+
+ m_Carve = carve;
+ AddOrRemoveObstacle ();
+ SetDirty ();
+}
+
+void NavMeshObstacle::SetMoveThreshold (float moveThreshold)
+{
+ ABORT_INVALID_FLOAT (moveThreshold, moveThreshold, navmeshobstacle);
+ m_MoveThreshold = moveThreshold;
+ SetDirty ();
+}
+
+#endif
+
+void NavMeshObstacle::OnVelocityChanged (Vector3f* value)
+{
+ SetVelocity (*value);
+}
+
+void NavMeshObstacle::SetVelocity (const Vector3f& value)
+{
+ ABORT_INVALID_VECTOR3 (value, velocity, navmeshobstacle);
+ m_Velocity = value;
+ if (InCrowdSystem ())
+ {
+ GetCrowdSystem ()->SetObstacleVelocity (m_ObstacleHandle, m_Velocity.GetPtr ());
+ }
+}
+
+void NavMeshObstacle::SetRadius (float value)
+{
+ ABORT_INVALID_FLOAT (value, radius, navmeshobstacle);
+ m_Radius = EnsurePositive (value);
+ SetDirty ();
+ const Vector3f dimensions = GetScaledDimensions ();
+ if (InCrowdSystem ())
+ {
+ GetCrowdSystem ()->SetObstacleDimensions (m_ObstacleHandle, dimensions.GetPtr ());
+ }
+}
+
+void NavMeshObstacle::SetHeight (float value)
+{
+ ABORT_INVALID_FLOAT (value, height, navmeshobstacle);
+ m_Height = EnsurePositive (value);
+ SetDirty ();
+ const Vector3f dimensions = GetScaledDimensions ();
+ if (InCrowdSystem ())
+ {
+ GetCrowdSystem ()->SetObstacleDimensions (m_ObstacleHandle, dimensions.GetPtr ());
+ }
+}
+
+Vector3f NavMeshObstacle::GetScaledDimensions () const
+{
+ Vector3f absScale = Abs (GetComponent (Transform).GetWorldScaleLossy ());
+ float scaledRadius = m_Radius * max (absScale.x, absScale.z);
+ float scaledHeight = m_Height * absScale.y;
+ return Vector3f (scaledRadius, scaledHeight, scaledRadius);
+}
+
+void NavMeshObstacle::CalculateTransformAndSize (Matrix4x4f& trans, Vector3f& size)
+{
+ // TODO cache result on obstacle.
+ const Transform& transform = GetComponent (Transform);
+ trans = transform.GetLocalToWorldMatrix ();
+
+ AABB aabb;
+ if (CalculateLocalAABB (GetGameObject (), &aabb))
+ {
+ size = aabb.GetExtent ();
+ }
+ else
+ {
+ size = Vector3f (m_Radius, m_Height, m_Radius);
+ }
+}
+
+dtCrowd* NavMeshObstacle::GetCrowdSystem ()
+{
+ return GetNavMeshManager ().GetCrowdSystem ();
+}
+
+IMPLEMENT_CLASS_HAS_INIT (NavMeshObstacle)
+IMPLEMENT_OBJECT_SERIALIZE (NavMeshObstacle)
diff --git a/Runtime/NavMesh/NavMeshObstacle.h b/Runtime/NavMesh/NavMeshObstacle.h
new file mode 100644
index 0000000..17c6e45
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshObstacle.h
@@ -0,0 +1,155 @@
+#ifndef RUNTIME_NAVMESHOBSTACLE
+#define RUNTIME_NAVMESHOBSTACLE
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Math/Vector3.h"
+#include "NavMeshManager.h"
+#include "DetourFeatures.h"
+#include "DetourReference.h"
+
+struct NavMeshCarveData;
+class dtCrowd;
+class dtNavMeshQuery;
+class Matrix4x4f;
+
+class NavMeshObstacle : public Behaviour
+{
+public:
+ REGISTER_DERIVED_CLASS (NavMeshObstacle, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (NavMeshObstacle)
+
+ NavMeshObstacle (MemLabelId& label, ObjectCreationMode mode);
+ // ~NavMeshObstacle (); declared by a macro
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode mode);
+ static void InitializeClass ();
+ static void CleanupClass () { }
+
+ inline bool InCrowdSystem () const;
+ inline void SetManagerHandle (int handle);
+
+ void OnNavMeshChanged ();
+ void OnNavMeshCleanup ();
+
+#if ENABLE_NAVMESH_CARVING
+ inline void SetCarveHandle (int handle);
+
+ void WillRebuildNavmesh (NavMeshCarveData& carveData);
+ bool NeedsRebuild () const;
+
+ inline bool GetCarving () const;
+ void SetCarving (bool carve);
+
+ inline float GetMoveThreshold () const;
+ void SetMoveThreshold (float moveThreshold);
+#endif
+
+ Vector3f GetScaledDimensions () const;
+ inline Vector3f GetPosition () const;
+
+ inline Vector3f GetVelocity () const;
+ void SetVelocity (const Vector3f& value);
+
+ inline float GetRadius () const;
+ void SetRadius (float value);
+
+ inline float GetHeight () const;
+ void SetHeight (float value);
+
+
+protected:
+ virtual void Reset ();
+ virtual void SmartReset ();
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+ virtual void CheckConsistency ();
+ virtual UInt32 CalculateSupportedMessages ();
+
+ void OnVelocityChanged (Vector3f* value);
+ void OnTransformChanged (int mask);
+
+private:
+ void AddToCrowdSystem ();
+ void RemoveFromCrowdSystem ();
+ void CalculateTransformAndSize (Matrix4x4f& trans, Vector3f& size);
+
+ static inline float EnsurePositive (float value);
+ static dtCrowd* GetCrowdSystem ();
+
+ int m_ManagerHandle;
+ dtCrowdHandle m_ObstacleHandle;
+ float m_Radius;
+ float m_Height;
+ Vector3f m_Velocity;
+
+#if ENABLE_NAVMESH_CARVING
+ void AddOrRemoveObstacle ();
+
+ enum
+ {
+ kClean = 0,
+ kHasMoved = 1 << 0,
+ kForceRebuild = 1 << 1
+ };
+ float m_MoveThreshold;
+ Vector3f m_LastCarvedPosition;
+ UInt32 m_Status;
+ int m_CarveHandle;
+ bool m_Carve;
+#endif
+};
+
+inline bool NavMeshObstacle::InCrowdSystem () const
+{
+ return m_ObstacleHandle.IsValid ();
+}
+
+inline void NavMeshObstacle::SetManagerHandle (int handle)
+{
+ m_ManagerHandle = handle;
+}
+
+inline float NavMeshObstacle::EnsurePositive (float value)
+{
+ return std::max (0.00001F, value);
+}
+
+inline Vector3f NavMeshObstacle::GetPosition () const
+{
+ return GetComponent (Transform).GetPosition ();
+}
+
+inline Vector3f NavMeshObstacle::GetVelocity () const
+{
+ return m_Velocity;
+}
+
+inline float NavMeshObstacle::GetRadius () const
+{
+ return m_Radius;
+}
+
+inline float NavMeshObstacle::GetHeight () const
+{
+ return m_Height;
+}
+
+#if ENABLE_NAVMESH_CARVING
+inline void NavMeshObstacle::SetCarveHandle (int handle)
+{
+ m_CarveHandle = handle;
+}
+
+inline bool NavMeshObstacle::GetCarving () const
+{
+ return m_Carve;
+}
+
+inline float NavMeshObstacle::GetMoveThreshold () const
+{
+ return m_MoveThreshold;
+}
+#endif // ENABLE_NAVMESH_CARVING
+
+#endif
diff --git a/Runtime/NavMesh/NavMeshPath.cpp b/Runtime/NavMesh/NavMeshPath.cpp
new file mode 100644
index 0000000..68f51c9
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshPath.cpp
@@ -0,0 +1,19 @@
+#include "UnityPrefix.h"
+#include "NavMeshPath.h"
+#include "NavMeshSettings.h"
+#include "OffMeshLink.h"
+#include "NavMesh.h"
+
+
+NavMeshPath::NavMeshPath ()
+: m_polygonCount (0)
+, m_status (kPathInvalid)
+, m_timeStamp (0)
+{
+}
+
+NavMeshPath::~NavMeshPath ()
+{
+}
+
+
diff --git a/Runtime/NavMesh/NavMeshPath.h b/Runtime/NavMesh/NavMeshPath.h
new file mode 100644
index 0000000..e0be375
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshPath.h
@@ -0,0 +1,90 @@
+#pragma once
+#ifndef RUNTIME_NAVMESH_PATH
+#define RUNTIME_NAVMESH_PATH
+
+#include "Runtime/Math/Vector3.h"
+#include "NavMeshTypes.h"
+
+struct OffMeshLinkData;
+
+class NavMeshPath
+{
+public:
+ enum
+ {
+ kMaxPathPolygons = 256
+ };
+
+ NavMeshPath ();
+ ~NavMeshPath ();
+
+ inline Vector3f GetSourcePosition () const;
+ inline void SetSourcePosition (const Vector3f& sourcePosition);
+ inline Vector3f GetTargetPosition () const;
+ inline void SetTargetPosition (const Vector3f& targetPosition);
+ inline int GetPolygonCount () const;
+ inline void SetPolygonCount (int polygonCount);
+
+ inline unsigned int* GetPolygonPath ();
+ inline const unsigned int* GetPolygonPath () const;
+
+ inline NavMeshPathStatus GetStatus () const;
+ inline void SetStatus (NavMeshPathStatus status);
+ inline void SetTimeStamp (unsigned int timeStamp);
+
+private:
+
+ unsigned int m_timeStamp;
+ NavMeshPathStatus m_status;
+ unsigned int m_polygons[kMaxPathPolygons];
+ int m_polygonCount;
+ Vector3f m_sourcePosition;
+ Vector3f m_targetPosition;
+};
+
+inline Vector3f NavMeshPath::GetSourcePosition () const
+{
+ return m_sourcePosition;
+}
+inline void NavMeshPath::SetSourcePosition (const Vector3f& sourcePosition)
+{
+ m_sourcePosition = sourcePosition;
+}
+inline Vector3f NavMeshPath::GetTargetPosition () const
+{
+ return m_targetPosition;
+}
+inline void NavMeshPath::SetTargetPosition (const Vector3f& targetPosition)
+{
+ m_targetPosition = targetPosition;
+}
+inline int NavMeshPath::GetPolygonCount () const
+{
+ return m_polygonCount;
+}
+inline void NavMeshPath::SetPolygonCount (int polygonCount)
+{
+ m_polygonCount = polygonCount;
+}
+inline unsigned int* NavMeshPath::GetPolygonPath ()
+{
+ return m_polygons;
+}
+inline const unsigned int* NavMeshPath::GetPolygonPath () const
+{
+ return m_polygons;
+}
+inline NavMeshPathStatus NavMeshPath::GetStatus () const
+{
+ return m_status;
+}
+inline void NavMeshPath::SetStatus (NavMeshPathStatus status)
+{
+ m_status = status;
+}
+inline void NavMeshPath::SetTimeStamp (unsigned int timeStamp)
+{
+ m_timeStamp = timeStamp;
+}
+
+#endif
diff --git a/Runtime/NavMesh/NavMeshProfiler.h b/Runtime/NavMesh/NavMeshProfiler.h
new file mode 100644
index 0000000..122b224
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshProfiler.h
@@ -0,0 +1,71 @@
+#ifndef NAVMESHPROFILER_H
+#define NAVMESHPROFILER_H
+
+#include "Runtime/Profiler/Profiler.h"
+#include "DetourContext.h"
+
+PROFILER_INFORMATION (gCrowdManagerPathFinding, "CrowdManager.PathFinding", kProfilerAI)
+PROFILER_INFORMATION (gCrowdManagerPathFollowing, "CrowdManager.PathFollowing", kProfilerAI)
+PROFILER_INFORMATION (gCrowdManagerPathFollowingLate, "CrowdManager.PathFollowing.Late", kProfilerAI)
+PROFILER_INFORMATION (gCrowdManagerAvoidance, "CrowdManager.Avoidance", kProfilerAI)
+PROFILER_INFORMATION (gCrowdManagerAvoidanceSampling, "CrowdManager.Avoidance.Sampling", kProfilerAI)
+PROFILER_INFORMATION (gCrowdManagerProximity, "CrowdManager.Proximity", kProfilerAI)
+PROFILER_INFORMATION (gCrowdManagerProximityInsert, "CrowdManager.Proximity.Insert", kProfilerAI)
+PROFILER_INFORMATION (gCrowdManagerProximityCollect, "CrowdManager.Proximity.Collect", kProfilerAI)
+PROFILER_INFORMATION (gCrowdManagerCollision, "CrowdManager.Collision", kProfilerAI)
+
+class CrowdProfiler : public dtContext
+{
+public:
+ CrowdProfiler ()
+ : dtContext (true)
+ {
+ }
+ virtual ~CrowdProfiler ()
+ {
+ }
+
+protected:
+ virtual void doStartTimer (const dtTimerLabel label)
+ {
+ switch (label)
+ {
+ case DT_TIMER_UPDATE_PATHFINDING:
+ PROFILER_BEGIN (gCrowdManagerPathFinding, NULL);
+ break;
+ case DT_TIMER_UPDATE_PATHFOLLOWING:
+ PROFILER_BEGIN (gCrowdManagerPathFollowing, NULL);
+ break;
+ case DT_TIMER_UPDATE_PATHFOLLOWING_LATE:
+ PROFILER_BEGIN (gCrowdManagerPathFollowingLate, NULL);
+ break;
+ case DT_TIMER_UPDATE_AVOIDANCE:
+ PROFILER_BEGIN (gCrowdManagerAvoidance, NULL);
+ break;
+ case DT_TIMER_UPDATE_AVOIDANCE_SAMPLING:
+ PROFILER_BEGIN (gCrowdManagerAvoidanceSampling, NULL);
+ break;
+ case DT_TIMER_UPDATE_PROXIMITY:
+ PROFILER_BEGIN (gCrowdManagerProximity, NULL);
+ break;
+ case DT_TIMER_UPDATE_PROXIMITY_INSERT:
+ PROFILER_BEGIN (gCrowdManagerProximityInsert, NULL);
+ break;
+ case DT_TIMER_UPDATE_PROXIMITY_COLLECT:
+ PROFILER_BEGIN (gCrowdManagerProximityCollect, NULL);
+ break;
+ case DT_TIMER_UPDATE_COLLISION:
+ PROFILER_BEGIN (gCrowdManagerCollision, NULL);
+ break;
+ default:
+ break;
+ }
+ }
+
+ virtual void doStopTimer (const dtTimerLabel /*label*/)
+ {
+ PROFILER_END
+ }
+};
+
+#endif
diff --git a/Runtime/NavMesh/NavMeshSettings.cpp b/Runtime/NavMesh/NavMeshSettings.cpp
new file mode 100644
index 0000000..49bdd02
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshSettings.cpp
@@ -0,0 +1,103 @@
+#include "UnityPrefix.h"
+#include "NavMeshSettings.h"
+#include "NavMeshManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "OffMeshLink.h"
+#include "NavMeshLayers.h"
+#include "NavMesh.h"
+#include "HeightmapData.h"
+#include "DetourNavMesh.h"
+
+void NavMeshSettings::InitializeClass ()
+{
+ InitializeNavMeshManager ();
+}
+
+void NavMeshSettings::CleanupClass ()
+{
+ CleanupNavMeshManager ();
+}
+
+
+NavMeshSettings::NavMeshSettings (MemLabelId& label, ObjectCreationMode mode)
+ : Super (label, mode)
+{
+}
+
+NavMeshSettings::~NavMeshSettings ()
+{
+ GetNavMeshManager ().CleanupMeshDependencies ();
+}
+
+void NavMeshSettings::Reset ()
+{
+ Super::Reset ();
+
+ #if UNITY_EDITOR
+ m_BuildSettings = NavMeshBuildSettings ();
+ #endif
+}
+
+void NavMeshSettings::AwakeFromLoad (AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad (mode);
+
+ // Initialize NavMesh
+ const dtNavMesh* internalNavMesh = NULL;
+ const HeightMeshQuery* heightMeshQuery = NULL;
+ if (m_NavMesh)
+ {
+ // Calling m_NavMesh->Create () here to ensure state of navmesh is restored.
+ // Were are already copying the data so memory usage is not affected.
+ m_NavMesh->Create ();
+ internalNavMesh = m_NavMesh->GetInternalNavMesh ();
+ heightMeshQuery = m_NavMesh->GetHeightMeshQuery ();
+ }
+ else
+ {
+ GetNavMeshManager ().CleanupMeshDependencies ();
+ }
+ GetNavMeshManager ().Initialize (internalNavMesh, heightMeshQuery);
+}
+
+template<class T>
+void NavMeshSettings::Transfer (T& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER_EDITOR_ONLY (m_BuildSettings);
+ TRANSFER (m_NavMesh);
+}
+
+bool NavMeshSettings::SetOffMeshPolyInstanceID (dtPolyRef ref, int instanceID)
+{
+ if (dtNavMesh* navmesh = GetInternalNavMesh ())
+ return navmesh->setOffMeshPolyInstanceID (ref, instanceID) == DT_SUCCESS;
+ return false;
+}
+
+void NavMeshSettings::SetOffMeshPolyCostOverride (dtPolyRef ref, float costOverride)
+{
+ if (dtNavMesh* navmesh = GetInternalNavMesh ())
+ navmesh->setOffMeshPolyCostOverride (ref, costOverride);
+}
+
+void NavMeshSettings::SetOffMeshPolyAccess (dtPolyRef ref, bool access)
+{
+ if (dtNavMesh* navmesh = GetInternalNavMesh ())
+ navmesh->setOffMeshPolyAccess (ref, access);
+}
+
+dtNavMesh* NavMeshSettings::GetInternalNavMesh ()
+{
+ NavMesh* navmesh = GetNavMesh ();
+ if (navmesh == NULL)
+ return NULL;
+ return navmesh->GetInternalNavMesh ();
+}
+
+IMPLEMENT_OBJECT_SERIALIZE (NavMeshSettings)
+IMPLEMENT_CLASS_HAS_INIT (NavMeshSettings)
+GET_MANAGER (NavMeshSettings)
diff --git a/Runtime/NavMesh/NavMeshSettings.h b/Runtime/NavMesh/NavMeshSettings.h
new file mode 100644
index 0000000..6c1b6ca
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshSettings.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/NavMesh/NavMeshTypes.h"
+#if UNITY_EDITOR
+#include "Editor/Src/NavMesh/NavMeshBuildSettings.h"
+#endif
+
+class NavMesh;
+class dtNavMesh;
+
+class NavMeshSettings : public LevelGameManager
+{
+public:
+
+ REGISTER_DERIVED_CLASS (NavMeshSettings, LevelGameManager);
+ DECLARE_OBJECT_SERIALIZE (NavMeshSettings);
+
+ NavMeshSettings (MemLabelId& label, ObjectCreationMode mode);
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode mode);
+ virtual void Reset ();
+
+ inline void SetNavMesh (NavMesh* navMesh);
+ inline NavMesh* GetNavMesh ();
+
+ bool SetOffMeshPolyInstanceID (dtPolyRef ref, int instanceID);
+ void SetOffMeshPolyCostOverride (dtPolyRef ref, float costOverride);
+ void SetOffMeshPolyAccess (dtPolyRef ref, bool access);
+
+
+ #if UNITY_EDITOR
+ inline NavMeshBuildSettings& GetNavMeshBuildSettings ();
+ #endif
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ dtNavMesh* GetInternalNavMesh ();
+private:
+
+#if UNITY_EDITOR
+ NavMeshBuildSettings m_BuildSettings;
+#endif
+
+ PPtr<NavMesh> m_NavMesh;
+};
+
+inline void NavMeshSettings::SetNavMesh (NavMesh* navMesh)
+{
+ m_NavMesh = navMesh;
+ SetDirty ();
+}
+
+inline NavMesh* NavMeshSettings::GetNavMesh ()
+{
+ return m_NavMesh;
+}
+
+#if UNITY_EDITOR
+inline NavMeshBuildSettings& NavMeshSettings::GetNavMeshBuildSettings ()
+{
+ return m_BuildSettings;
+}
+#endif
+
+NavMeshSettings& GetNavMeshSettings ();
+
diff --git a/Runtime/NavMesh/NavMeshTileCarving.cpp b/Runtime/NavMesh/NavMeshTileCarving.cpp
new file mode 100644
index 0000000..24565aa
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshTileCarving.cpp
@@ -0,0 +1,176 @@
+#include "UnityPrefix.h"
+#include "NavMeshTileCarving.h"
+#include "NavMeshTileConversion.h"
+#include "DynamicMesh.h"
+#include "DetourNavMesh.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Geometry/AABB.h"
+#include "DetourCommon.h"
+#include "DetourAlloc.h"
+
+// TODO:
+// Sort carving objects spatially - so carving order does not depend on carve index
+
+static inline Vector3f TileMidpoint (const dtMeshHeader* tileHeader);
+static bool CalculateHull (DynamicMesh::Hull& carveHull, const Matrix4x4f& transform, const Vector3f& size, const MinMaxAABB& aabb, const Vector3f& tileOffset, const float carveWidth, const float carveDepth);
+static Vector3f CalculateCarveOffsetScale (const Vector3f& axis);
+
+// Replaces a single tile in the detour navmesh with a carved tile.
+void CarveNavMeshTile (const dtMeshTile* tile, dtNavMesh* navmesh, size_t count, const Matrix4x4f* transforms, const Vector3f* sizes, const MinMaxAABB* aabbs)
+{
+ if (count == 0 || tile == NULL || tile->header == NULL)
+ {
+ return;
+ }
+
+ const Vector3f tileOffset = TileMidpoint (tile->header);
+ const float carveWidth = tile->header->walkableRadius;
+ const float carveDepth = tile->header->walkableHeight;
+
+ DynamicMesh::HullContainer carveHulls;
+ for (size_t i = 0; i < count; ++i)
+ {
+ DynamicMesh::Hull carveHull;
+ if (CalculateHull (carveHull, transforms[i], sizes[i], aabbs[i], tileOffset, carveWidth, carveDepth))
+ {
+ carveHulls.push_back (carveHull);
+ }
+ }
+
+ DynamicMesh dynamicMesh;
+ if (!TileToDynamicMesh (tile, dynamicMesh, tileOffset))
+ {
+ return;
+ }
+ if (!dynamicMesh.ClipPolys (carveHulls))
+ {
+ return;
+ }
+
+ dynamicMesh.FindNeighbors ();
+
+ int newTileSize = 0;
+ unsigned char* newTile = DynamicMeshToTile (&newTileSize, dynamicMesh, tile, tileOffset);
+
+ dtPolyRef tileRef = navmesh->getTileRef (tile);
+ navmesh->removeTile (tileRef, 0, 0);
+ dtStatus status = navmesh->addTile (newTile, newTileSize, DT_TILE_FREE_DATA, tileRef, 0);
+ if (dtStatusFailed (status))
+ {
+ dtFree (newTile);
+ }
+}
+
+static inline Vector3f TileMidpoint (const dtMeshHeader* tileHeader)
+{
+ if (!tileHeader)
+ {
+ return Vector3f::zero;
+ }
+ return 0.5f * (Vector3f (tileHeader->bmin) + Vector3f (tileHeader->bmax));
+}
+
+static inline bool AreColinear (const Vector3f& v, const Vector3f& u, const float cosAngleAccept)
+{
+ DebugAssert (IsNormalized (v));
+ DebugAssert (IsNormalized (u));
+ return Abs (Dot (v, u)) > cosAngleAccept;
+}
+
+// Compute the set of planes defining an extruded bounding box.
+// Bounding box is represented by transform and size.
+// Extrusion is based on 'carveWidth' horizontally and 'carveDepth' vertically down.
+// Everyting is translated relative to 'tileOffset'.
+static bool CalculateHull (DynamicMesh::Hull& carveHull, const Matrix4x4f& transform, const Vector3f& size, const MinMaxAABB& aabb, const Vector3f& tileOffset, const float carveWidth, const float carveDepth)
+{
+ carveHull.resize_uninitialized (12);
+
+ const float cosAngleConsiderAxisAligned = Cos (Deg2Rad (10.0f)); // Consider colinear if within 10 degrees
+ bool isAlmostAxisAlignedX = false;
+ bool isAlmostAxisAlignedY = false;
+ bool isAlmostAxisAlignedZ = false;
+
+ // First add the six planes from the OBB
+ const Vector3f carveLocalOffset = Vector3f (carveWidth, carveDepth, carveWidth);
+ const Vector3f position = transform.GetPosition () - tileOffset;
+ Vector3f axis, offset, planeOffset;
+
+ axis = transform.GetAxisX ();
+ if (CompareApproximately (axis, Vector3f::zero, 0.0001f))
+ {
+ return false;
+ }
+ offset = size.x * axis;
+ axis = Normalize (axis);
+ planeOffset = Scale (CalculateCarveOffsetScale (-axis), carveLocalOffset) - offset;
+ carveHull[0].SetNormalAndPosition (-axis, position + planeOffset);
+ planeOffset = Scale (CalculateCarveOffsetScale (axis), carveLocalOffset) + offset;
+ carveHull[1].SetNormalAndPosition (axis, position + planeOffset);
+ isAlmostAxisAlignedX = isAlmostAxisAlignedX || AreColinear (axis, Vector3f::xAxis, cosAngleConsiderAxisAligned);
+ isAlmostAxisAlignedY = isAlmostAxisAlignedY || AreColinear (axis, Vector3f::yAxis, cosAngleConsiderAxisAligned);
+ isAlmostAxisAlignedZ = isAlmostAxisAlignedZ || AreColinear (axis, Vector3f::zAxis, cosAngleConsiderAxisAligned);
+
+ axis = transform.GetAxisY ();
+ if (CompareApproximately (axis, Vector3f::zero, 0.0001f))
+ {
+ return false;
+ }
+ offset = size.y * axis;
+ axis = Normalize (axis);
+ planeOffset = Scale (CalculateCarveOffsetScale (-axis), carveLocalOffset) - offset;
+ carveHull[2].SetNormalAndPosition (-axis, position + planeOffset);
+ planeOffset = Scale (CalculateCarveOffsetScale (axis), carveLocalOffset) + offset;
+ carveHull[3].SetNormalAndPosition (axis, position + planeOffset);
+ isAlmostAxisAlignedX = isAlmostAxisAlignedX || AreColinear (axis, Vector3f::xAxis, cosAngleConsiderAxisAligned);
+ isAlmostAxisAlignedY = isAlmostAxisAlignedY || AreColinear (axis, Vector3f::yAxis, cosAngleConsiderAxisAligned);
+ isAlmostAxisAlignedZ = isAlmostAxisAlignedZ || AreColinear (axis, Vector3f::zAxis, cosAngleConsiderAxisAligned);
+
+ axis = transform.GetAxisZ ();
+ if (CompareApproximately (axis, Vector3f::zero, 0.0001f))
+ {
+ return false;
+ }
+ offset = size.z * axis;
+ axis = Normalize (axis);
+ planeOffset = Scale (CalculateCarveOffsetScale (-axis), carveLocalOffset) - offset;
+ carveHull[4].SetNormalAndPosition (-axis, position + planeOffset);
+ planeOffset = Scale (CalculateCarveOffsetScale (axis), carveLocalOffset) + offset;
+ carveHull[5].SetNormalAndPosition (axis, position + planeOffset);
+ isAlmostAxisAlignedX = isAlmostAxisAlignedX || AreColinear (axis, Vector3f::xAxis, cosAngleConsiderAxisAligned);
+ isAlmostAxisAlignedY = isAlmostAxisAlignedY || AreColinear (axis, Vector3f::yAxis, cosAngleConsiderAxisAligned);
+ isAlmostAxisAlignedZ = isAlmostAxisAlignedZ || AreColinear (axis, Vector3f::zAxis, cosAngleConsiderAxisAligned);
+
+ int planeCount = 6;
+
+ // The add the six planes from the containing AABB
+ const Vector3f min = aabb.m_Min - tileOffset;
+ const Vector3f max = aabb.m_Max - tileOffset;
+ if (!isAlmostAxisAlignedX)
+ {
+ carveHull[planeCount++].SetNormalAndPosition (-Vector3f::xAxis, min - carveWidth*Vector3f::xAxis);
+ carveHull[planeCount++].SetNormalAndPosition (Vector3f::xAxis, max + carveWidth*Vector3f::xAxis);
+ }
+ if (!isAlmostAxisAlignedY)
+ {
+ carveHull[planeCount++].SetNormalAndPosition (-Vector3f::yAxis, min - carveDepth*Vector3f::yAxis);
+ carveHull[planeCount++].SetNormalAndPosition (Vector3f::yAxis, max);
+ }
+ if (!isAlmostAxisAlignedZ)
+ {
+ carveHull[planeCount++].SetNormalAndPosition (-Vector3f::zAxis, min - carveWidth*Vector3f::zAxis);
+ carveHull[planeCount++].SetNormalAndPosition (Vector3f::zAxis, max + carveWidth*Vector3f::zAxis);
+ }
+
+ carveHull.resize_uninitialized (planeCount);
+ return true;
+}
+
+// Returns the general plane offset direction given the plane axis
+static Vector3f CalculateCarveOffsetScale (const Vector3f& axis)
+{
+ Vector3f res;
+ res.x = Sign (axis.x);
+ res.y = std::min (0.0f, Sign (axis.y));
+ res.z = Sign (axis.z);
+ return res;
+}
diff --git a/Runtime/NavMesh/NavMeshTileCarving.h b/Runtime/NavMesh/NavMeshTileCarving.h
new file mode 100644
index 0000000..6ef85a2
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshTileCarving.h
@@ -0,0 +1,12 @@
+#ifndef _NAVMESHTILECARVING_H_INCLUDED_
+#define _NAVMESHTILECARVING_H_INCLUDED_
+
+struct dtMeshTile;
+class dtNavMesh;
+class Matrix4x4f;
+class Vector3f;
+class MinMaxAABB;
+
+void CarveNavMeshTile (const dtMeshTile* tile, dtNavMesh* detourNavMesh, size_t count, const Matrix4x4f* transforms, const Vector3f* sizes, const MinMaxAABB* aabbs);
+
+#endif
diff --git a/Runtime/NavMesh/NavMeshTileConversion.cpp b/Runtime/NavMesh/NavMeshTileConversion.cpp
new file mode 100644
index 0000000..5ab6508
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshTileConversion.cpp
@@ -0,0 +1,408 @@
+#include "UnityPrefix.h"
+#include "NavMeshTileConversion.h"
+#include "DynamicMesh.h"
+#include "DetourNavMesh.h"
+#include "DetourCommon.h"
+#include "DetourAlloc.h"
+
+// TODO:
+// Create BVH for carved tile
+// Preserve detail mesh for the carved polygons.
+
+const float MAGIC_EDGE_DISTANCE = 1e-2f; // Same as used in detour navmesh builder.
+
+static void RequirementsForDetailMeshUsingHeightMesh (int* detailVertCount, int* detailTriCount, const DynamicMesh& mesh, const dtMeshTile* sourceTile);
+static void RequirementsForDetailMeshMixed (int* detailVertCount, int* detailTriCount, const DynamicMesh& mesh, const dtMeshTile* sourceTile);
+static void WritePortalFlags (const float* verts, dtPoly* polys, const int polyCount, const dtMeshHeader* sourceHeader);
+static void WriteDetailMeshUsingHeightMesh (dtPolyDetail* detail, float* dverts, dtPolyDetailIndex* dtris
+ , const DynamicMesh& mesh, const dtMeshTile* sourceTile, const int detailTriCount, const int detailVertCount);
+static void WriteDetailMeshMixed (dtPolyDetail* detail, float* dverts, dtPolyDetailIndex* dtris
+ , const DynamicMesh& mesh, const dtMeshTile* sourceTile, const int detailTriCount, const int detailVertCount);
+static void WriteOffMeshLinks (dtOffMeshConnection* offMeshCons, dtPoly* polys, float* verts, int polyCount, int vertCount, const dtMeshTile* sourceTile);
+static int SimplePolygonTriangulation (dtPolyDetail* dtl, dtPolyDetailIndex* dtris, int detailTriBase, const int polygonVertexCount);
+
+
+
+// Converts detour navmesh tile to dynamic mesh format
+bool TileToDynamicMesh (const dtMeshTile* tile, DynamicMesh& mesh, const Vector3f& tileOffset)
+{
+ if (!tile || !tile->header)
+ {
+ return false;
+ }
+
+ const int vertCount = tile->header->vertCount;
+ const int polyCount = tile->header->polyCount;
+ mesh.Reserve (vertCount, polyCount);
+
+ for (int iv = 0; iv < vertCount; ++iv)
+ {
+ const Vector3f srcVertex(&tile->verts[3*iv]);
+ mesh.AddVertex (srcVertex - tileOffset);
+ }
+
+ for (int ip = 0; ip < polyCount; ++ip)
+ {
+ const dtPoly& srcPoly = tile->polys[ip];
+ if (srcPoly.getType () == DT_POLYTYPE_GROUND)
+ mesh.AddPolygon (srcPoly.verts, ip, srcPoly.vertCount);
+ }
+
+ return true;
+}
+
+// Create tile in the format understood by the detour runtime.
+// Polygons are converted from the dynamic mesh 'mesh'.
+// Settings and static offmeshlinks are carried over from 'sourceTile'.
+unsigned char* DynamicMeshToTile (int* dataSize, const DynamicMesh& mesh, const dtMeshTile* sourceTile, const Vector3f& tileOffset)
+{
+ // Determine data size
+ DebugAssert (sourceTile);
+ const int vertCount = mesh.VertCount ();
+ const int polyCount = mesh.PolyCount ();
+ const dtMeshHeader* sourceHeader = sourceTile->header;
+
+ int polyEdgeCount = 0;
+ for (int ip = 0; ip < polyCount; ++ip)
+ {
+ polyEdgeCount += mesh.GetPoly (ip)->m_VertexCount;
+ }
+
+ const int offMeshConCount = sourceHeader->offMeshConCount;
+ const int totVertCount = vertCount + 2 * offMeshConCount;
+ const int totPolyCount = polyCount + offMeshConCount;
+ const int totLinkCount = polyEdgeCount + 4*offMeshConCount; // TODO: reserve for links to external offmeshlink connections
+
+ const bool hasHeightMesh = sourceTile->header->flags & DT_MESH_HEADER_USE_HEIGHT_MESH;
+
+ int detailVertCount = 0;
+ int detailTriCount = 0;
+ if (hasHeightMesh)
+ {
+ RequirementsForDetailMeshUsingHeightMesh (&detailVertCount, &detailTriCount, mesh, sourceTile);
+ } else
+ {
+ RequirementsForDetailMeshMixed (&detailVertCount, &detailTriCount, mesh, sourceTile);
+ }
+
+ const unsigned int headSize = dtAlign4 (sizeof (dtMeshHeader));
+ const unsigned int vertSize = dtAlign4 (totVertCount * 3*sizeof (float));
+ const unsigned int polySize = dtAlign4 (totPolyCount * sizeof (dtPoly));
+ const unsigned int linkSize = dtAlign4 (totLinkCount * sizeof (dtLink));
+ const unsigned int detailMeshesSize = dtAlign4 (polyCount * sizeof (dtPolyDetail));
+ const unsigned int detailVertsSize = dtAlign4 (detailVertCount * 3*sizeof (float));
+ const unsigned int detailTrisSize = dtAlign4 (detailTriCount * 4*sizeof (dtPolyDetailIndex));
+ const unsigned int bvTreeSize = 0;
+ const unsigned int offMeshConsSize = dtAlign4 (offMeshConCount * sizeof (dtOffMeshConnection));
+
+ const int newSize = headSize + vertSize + polySize + linkSize
+ + detailTrisSize + detailVertsSize + detailMeshesSize + bvTreeSize + offMeshConsSize;
+
+ unsigned char* newTile = dtAllocArray<unsigned char> (newSize);
+ if (newTile == NULL)
+ {
+ *dataSize = 0;
+ return NULL;
+ }
+ *dataSize = newSize;
+ memset (newTile, 0, newSize);
+
+ // Serialize in the detour recognized format
+ int offset = 0;
+ dtMeshHeader* header = (dtMeshHeader*)(newTile+offset); offset += headSize;
+ float* verts = (float*)(newTile+offset); offset += vertSize;
+ dtPoly* polys = (dtPoly*)(newTile+offset); offset += polySize;
+ /*dtLink* links = (dtLink*)(newTile+offset);*/ offset += linkSize;
+ dtPolyDetail* detail = (dtPolyDetail*)(newTile+offset); offset += detailMeshesSize;
+ float* dverts = (float*)(newTile+offset); offset += detailVertsSize;
+ dtPolyDetailIndex* dtris = (dtPolyDetailIndex*)(newTile+offset); offset += detailTrisSize;
+ /*dtBVNode* bvtree = (dtBVNode*)(newTile+offset);*/ offset += bvTreeSize;
+ dtOffMeshConnection* offMeshCons = (dtOffMeshConnection*)(newTile+offset); offset += offMeshConsSize;
+ DebugAssert (offset == newSize);
+
+ for (int iv = 0; iv < vertCount; ++iv)
+ {
+ const float* v = mesh.GetVertex (iv);
+ verts[3*iv+0] = v[0] + tileOffset.x;
+ verts[3*iv+1] = v[1] + tileOffset.y;
+ verts[3*iv+2] = v[2] + tileOffset.z;
+ }
+
+ for (int ip = 0; ip < polyCount; ++ip)
+ {
+ const DynamicMesh::Poly* p = mesh.GetPoly (ip);
+ const int sourcePolyIndex = *mesh.GetData (ip);
+ const dtPoly& srcPoly = sourceTile->polys[sourcePolyIndex];
+
+ dtPoly& poly = polys[ip];
+ memcpy (poly.verts, p->m_VertexIDs, DT_VERTS_PER_POLYGON*sizeof (UInt16));
+ memcpy (poly.neis, p->m_Neighbours, DT_VERTS_PER_POLYGON*sizeof (UInt16));
+ unsigned char area = srcPoly.getArea ();
+ poly.flags = 1<<area;
+ poly.setArea (area);
+ poly.setType (DT_POLYTYPE_GROUND);
+ poly.vertCount = p->m_VertexCount;
+ }
+
+ // Set external portal flags
+ WritePortalFlags (verts, polys, polyCount, sourceHeader);
+
+ if (hasHeightMesh)
+ {
+ WriteDetailMeshUsingHeightMesh (detail, dverts, dtris, mesh, sourceTile, detailTriCount, detailVertCount);
+ } else
+ {
+ WriteDetailMeshMixed (detail, dverts, dtris, mesh, sourceTile, detailTriCount, detailVertCount);
+ }
+
+ // Fill in offmeshlink data from source tile: vertices, polygons, connection data.
+ WriteOffMeshLinks (offMeshCons, polys, verts, polyCount, vertCount, sourceTile);
+
+ // Copy values from source
+ memcpy (header, sourceHeader, sizeof (*header));
+
+ // (re)set new tile values
+ header->polyCount = totPolyCount;
+ header->vertCount = totVertCount;
+ header->maxLinkCount = totLinkCount;
+ header->detailMeshCount = polyCount;
+ header->detailVertCount = detailVertCount;
+ header->detailTriCount = detailTriCount;
+ header->bvNodeCount = 0; // Fixme: bv-tree
+ header->offMeshConCount = offMeshConCount;
+ header->offMeshBase = polyCount; // points beyond regular polygons.
+
+ return newTile;
+}
+
+// Find vertex and triangle count needed when 'heightmesh' is enabled
+static void RequirementsForDetailMeshUsingHeightMesh (int* detailVertCount, int* detailTriCount, const DynamicMesh& mesh, const dtMeshTile* sourceTile)
+{
+ // This is so bad - but until we change the detail mesh representation to
+ // something more sane - we'll have to write code like this.
+ int vertCount = 0;
+ int triCount = 0;
+
+ // collect sizes needed for detail mesh
+ // detail mesh count is same as polyCount.
+ // they're 1-to-1 with the regular polygons
+ const int polyCount = mesh.PolyCount ();
+ for (int ip = 0; ip < polyCount; ++ip)
+ {
+ const int sourcePolyIndex = *mesh.GetData (ip);
+ const dtPolyDetail& sourceDetail = sourceTile->detailMeshes[sourcePolyIndex];
+
+ vertCount += sourceDetail.vertCount;
+ triCount += sourceDetail.triCount;
+ }
+ *detailVertCount = vertCount;
+ *detailTriCount = triCount;
+}
+
+// Find vertex and triangle count needed for regular detailmesh
+static void RequirementsForDetailMeshMixed (int* detailVertCount, int* detailTriCount, const DynamicMesh& mesh, const dtMeshTile* sourceTile)
+{
+ int vertCount = 0;
+ int triCount = 0;
+
+ // Collect sizes needed for detail mesh
+ const int polyCount = mesh.PolyCount ();
+ for (int ip = 0; ip < polyCount; ++ip)
+ {
+ const DynamicMesh::Poly* p = mesh.GetPoly (ip);
+ const int sourcePolyIndex = *mesh.GetData (ip);
+
+ if (p->m_Status == DynamicMesh::kOriginalPolygon)
+ {
+ // When preserving polygon detail mesh just add the source counts
+ const dtPolyDetail& sourceDetail = sourceTile->detailMeshes[sourcePolyIndex];
+ vertCount += sourceDetail.vertCount;
+ triCount += sourceDetail.triCount;
+ }
+ else
+ {
+ // Simple triangulation needs n-2 triangles but no extra detail vertices
+ triCount += p->m_VertexCount - 2;
+ }
+ }
+ *detailVertCount = vertCount;
+ *detailTriCount = triCount;
+}
+
+// Set flags on polygon edges colinear to tile edges.
+// Flagged edges are considered when dynamically stitching neighboring tiles.
+static void WritePortalFlags (const float* verts, dtPoly* polys, const int polyCount, const dtMeshHeader* sourceHeader)
+{
+ const float* bmax = sourceHeader->bmax;
+ const float* bmin = sourceHeader->bmin;
+ for (int ip = 0; ip < polyCount; ++ip)
+ {
+ dtPoly& poly = polys[ip];
+ for (int iv = 0; iv < poly.vertCount; ++iv)
+ {
+ // Skip already connected edges
+ if (poly.neis[iv] != 0)
+ continue;
+
+ const float* vert = &verts[3 * poly.verts[iv]];
+ const int ivn = (iv+1 == poly.vertCount) ? 0 : iv+1;
+ const float* nextVert = &verts[3 * poly.verts[ivn]];
+
+ unsigned short nei = 0;
+
+ if (dtMax (dtAbs (vert[0] - bmax[0]), dtAbs (nextVert[0] - bmax[0])) < MAGIC_EDGE_DISTANCE)
+ nei = DT_EXT_LINK | 0; // x+ portal
+ else if (dtMax (dtAbs (vert[2] - bmax[2]), dtAbs (nextVert[2] - bmax[2])) < MAGIC_EDGE_DISTANCE)
+ nei = DT_EXT_LINK | 2; // z+ portal
+ else if (dtMax (dtAbs (vert[0] - bmin[0]), dtAbs (nextVert[0] - bmin[0])) < MAGIC_EDGE_DISTANCE)
+ nei = DT_EXT_LINK | 4; // x- portal
+ else if (dtMax (dtAbs (vert[2] - bmin[2]), dtAbs (nextVert[2] - bmin[2])) < MAGIC_EDGE_DISTANCE)
+ nei = DT_EXT_LINK | 6; // z- portal
+
+ poly.neis[iv] = nei;
+ }
+ }
+}
+
+// Populate the tile with detail mesh. For the case where 'heightmesh' is enabled.
+static void WriteDetailMeshUsingHeightMesh (dtPolyDetail* detail, float* dverts, dtPolyDetailIndex* dtris
+ , const DynamicMesh& mesh, const dtMeshTile* sourceTile
+ , const int detailTriCount, const int detailVertCount)
+{
+ int detailVertBase = 0;
+ int detailTriBase = 0;
+
+ const int polyCount = mesh.PolyCount ();
+ for (int ip = 0; ip < polyCount; ++ip)
+ {
+ const int sourcePolyIndex = *mesh.GetData (ip);
+ const dtPolyDetail& sourceDetail = sourceTile->detailMeshes[sourcePolyIndex];
+
+ detail[ip].vertBase = detailVertBase;
+ detail[ip].triBase = detailTriBase;
+ detail[ip].triCount = sourceDetail.triCount;
+ detail[ip].vertCount = sourceDetail.vertCount;
+
+ // Detail vertex indices are corrected by the poly vertex count
+ // adjust for this peculiarity.
+ // NOTE: It causes detail meshes to by unusable by multiple polygons !
+ const int oldPolyVertexCount = sourceTile->polys[sourcePolyIndex].vertCount;
+ const int newPolyVertexCount = mesh.GetPoly (ip)->m_VertexCount;
+ const int vertexDelta = newPolyVertexCount - oldPolyVertexCount;
+
+ for (int iv = 0; iv < sourceDetail.vertCount; ++iv)
+ {
+ dverts[3*(detailVertBase + iv) + 0] = sourceTile->detailVerts[3*(sourceDetail.vertBase + iv) + 0];
+ dverts[3*(detailVertBase + iv) + 1] = sourceTile->detailVerts[3*(sourceDetail.vertBase + iv) + 1];
+ dverts[3*(detailVertBase + iv) + 2] = sourceTile->detailVerts[3*(sourceDetail.vertBase + iv) + 2];
+ }
+
+ for (int it = 0; it < sourceDetail.triCount; ++it)
+ {
+ dtris[4*(detailTriBase + it) + 0] = sourceTile->detailTris[4*(sourceDetail.triBase + it) + 0] + vertexDelta;
+ dtris[4*(detailTriBase + it) + 1] = sourceTile->detailTris[4*(sourceDetail.triBase + it) + 1] + vertexDelta;
+ dtris[4*(detailTriBase + it) + 2] = sourceTile->detailTris[4*(sourceDetail.triBase + it) + 2] + vertexDelta;
+ dtris[4*(detailTriBase + it) + 3] = 0;
+ }
+
+ detailVertBase += sourceDetail.vertCount;
+ detailTriBase += sourceDetail.triCount;
+ }
+ DebugAssert (detailVertBase == detailVertCount);
+ DebugAssert (detailTriBase == detailTriCount);
+}
+
+// Mix preserved detail mesh for untouched polygons with simple triangulation for generated polygons
+static void WriteDetailMeshMixed (dtPolyDetail* detail, float* dverts, dtPolyDetailIndex* dtris
+ , const DynamicMesh& mesh, const dtMeshTile* sourceTile
+ , const int detailTriCount, const int detailVertCount)
+{
+ int detailVertBase = 0;
+ int detailTriBase = 0;
+
+ const int polyCount = mesh.PolyCount ();
+ for (int ip = 0; ip < polyCount; ++ip)
+ {
+ dtPolyDetail& dtl = detail[ip];
+ const DynamicMesh::Poly* p = mesh.GetPoly (ip);
+
+ if (p->m_Status == DynamicMesh::kOriginalPolygon)
+ {
+ // Fill in the original detail mesh for this polygon
+ const int sourcePolyIndex = *mesh.GetData (ip);
+ const dtPolyDetail& sourceDetail = sourceTile->detailMeshes[sourcePolyIndex];
+ dtl.vertBase = detailVertBase;
+ dtl.vertCount = sourceDetail.vertCount;
+ dtl.triBase = detailTriBase;
+ dtl.triCount = sourceDetail.triCount;
+
+ // copy source detail vertices and triangles
+ memcpy (&dverts[3*detailVertBase], &sourceTile->detailVerts[3*sourceDetail.vertBase], 3*sizeof (float)*sourceDetail.vertCount);
+ memcpy (&dtris[4*detailTriBase], &sourceTile->detailTris[4*sourceDetail.triBase], 4*sizeof (dtPolyDetailIndex)*sourceDetail.triCount);
+
+ detailVertBase += sourceDetail.vertCount;
+ detailTriBase += sourceDetail.triCount;
+ }
+ else
+ {
+ detailTriBase = SimplePolygonTriangulation (&dtl, dtris, detailTriBase, p->m_VertexCount);
+ }
+ }
+ DebugAssert (detailTriBase == detailTriCount);
+ DebugAssert (detailVertBase == detailVertCount);
+}
+
+// Populate the tile with static offmesh links from the source tile.
+static void WriteOffMeshLinks (dtOffMeshConnection* offMeshCons, dtPoly* polys, float* verts, int polyCount, int vertCount, const dtMeshTile* sourceTile)
+{
+ const dtMeshHeader* sourceHeader = sourceTile->header;
+ const int offMeshConCount = sourceHeader->offMeshConCount;
+ if (offMeshConCount)
+ {
+ memcpy (&polys[polyCount], &sourceTile->polys[sourceHeader->offMeshBase], offMeshConCount * sizeof (dtPoly));
+ memcpy (offMeshCons, sourceTile->offMeshCons, offMeshConCount * sizeof (dtOffMeshConnection));
+
+ // Vertex base for offmeshlinks is not stored in tile header
+ // Here we assume that offmeshlink vertices are stored as the section of vertices
+ // and that each offmeshlink stores two vertices.
+ const int sourceOffMeshVertBase = sourceHeader->vertCount - 2*sourceHeader->offMeshConCount;
+
+ memcpy (&verts[3*vertCount], &sourceTile->verts[3*sourceOffMeshVertBase], 2*3*sizeof (float));
+
+ // Fixup internal index references
+ for (int i = 0; i < offMeshConCount; ++i)
+ {
+ // Vertex indices
+ dtPoly* poly = & polys[polyCount + i];
+ poly->verts[0] = (unsigned short)(vertCount + 2*i+0);
+ poly->verts[1] = (unsigned short)(vertCount + 2*i+1);
+
+ // Polygon index
+ dtOffMeshConnection* con = &offMeshCons[i];
+ con->poly = (unsigned short) (polyCount + i);
+ }
+ }
+}
+
+static int SimplePolygonTriangulation (dtPolyDetail* dtl, dtPolyDetailIndex* dtris, int detailTriBase, const int polygonVertexCount)
+{
+ dtl->vertBase = 0;
+ dtl->vertCount = 0;
+ dtl->triBase = (unsigned int)detailTriBase;
+ dtl->triCount = (dtPolyDetailIndex)(polygonVertexCount-2);
+
+ // Triangulate polygon (local indices).
+ for (int j = 2; j < polygonVertexCount; ++j)
+ {
+ dtPolyDetailIndex* t = &dtris[4*detailTriBase];
+ t[0] = 0;
+ t[1] = (dtPolyDetailIndex)(j-1);
+ t[2] = (dtPolyDetailIndex)j;
+ // Bit for each edge that belongs to poly boundary.
+ t[3] = (1<<2);
+ if (j == 2) t[3] |= (1<<0);
+ if (j == polygonVertexCount-1) t[3] |= (1<<4);
+ detailTriBase++;
+ }
+ return detailTriBase;
+}
diff --git a/Runtime/NavMesh/NavMeshTileConversion.h b/Runtime/NavMesh/NavMeshTileConversion.h
new file mode 100644
index 0000000..2bf3fd2
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshTileConversion.h
@@ -0,0 +1,11 @@
+#ifndef _NAVMESHTILECONVERSION_H_INCLUDED_
+#define _NAVMESHTILECONVERSION_H_INCLUDED_
+
+struct dtMeshTile;
+class DynamicMesh;
+class Vector3f;
+
+bool TileToDynamicMesh (const dtMeshTile* tile, DynamicMesh& mesh, const Vector3f& tileOffset);
+unsigned char* DynamicMeshToTile (int* dataSize, const DynamicMesh& mesh, const dtMeshTile* sourceTile, const Vector3f& tileOffset);
+
+#endif
diff --git a/Runtime/NavMesh/NavMeshTypes.h b/Runtime/NavMesh/NavMeshTypes.h
new file mode 100644
index 0000000..cc26075
--- /dev/null
+++ b/Runtime/NavMesh/NavMeshTypes.h
@@ -0,0 +1,76 @@
+#pragma once
+#ifndef NAVMESH_TYPES_H_INCLUDED
+#define NAVMESH_TYPES_H_INCLUDED
+
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "DetourReference.h"
+class NavMeshPath;
+
+// Keep this enum in sync with the one defined in "NavMeshAgentBindings.txt"
+enum ObstacleAvoidanceType
+{
+ kNoObstacleAvoidance = 0,
+ kLowQualityObstacleAvoidance = 1,
+ kMedQualityObstacleAvoidance = 2,
+ kGoodQualityObstacleAvoidance = 3,
+ kHighQualityObstacleAvoidance = 4
+};
+
+// Keep this struct in sync with the one defined in "NavMeshBindings.txt"
+struct NavMeshHit
+{
+ Vector3f position;
+ Vector3f normal;
+ float distance;
+ int mask;
+ int hit;
+};
+
+// Keep this struct in sync with the one defined in "NavMeshBindings.txt"
+enum NavMeshPathStatus
+{
+ kPathComplete = 0,
+ kPathPartial = 1,
+ kPathInvalid = 2
+};
+
+// Keep this enum in sync with the one defined in "NavMeshBindings.txt"
+enum OffMeshLinkType
+{
+ kLinkTypeManual = 0,
+ kLinkTypeDropDown = 1,
+ kLinkTypeJumpAcross = 2
+};
+
+// Keep this struct in sync with the one defined in "NavMeshBindings.txt"
+struct OffMeshLinkData
+{
+ int m_Valid;
+ int m_Activated;
+ int m_InstanceID;
+ OffMeshLinkType m_LinkType;
+ Vector3f m_StartPos;
+ Vector3f m_EndPos;
+};
+
+// Used in: NavMeshBindings.txt, NavMeshAgentBindings.txt, NavMeshPathBindings.txt
+struct MonoNavMeshPath
+{
+ MonoNavMeshPath ()
+ : native (NULL)
+ , corners (SCRIPTING_NULL)
+ {}
+
+ NavMeshPath* native;
+ ScriptingObjectPtr corners;
+};
+
+struct NavMeshCarveData
+{
+ Matrix4x4f transform;
+ Vector3f size;
+};
+
+#endif
diff --git a/Runtime/NavMesh/NavigationModuleRegistration.cpp b/Runtime/NavMesh/NavigationModuleRegistration.cpp
new file mode 100644
index 0000000..9bb7ed8
--- /dev/null
+++ b/Runtime/NavMesh/NavigationModuleRegistration.cpp
@@ -0,0 +1,40 @@
+#include "UnityPrefix.h"
+#include "Runtime/BaseClasses/ClassRegistration.h"
+#include "Runtime/Modules/ModuleRegistration.h"
+
+static void RegisterNavMeshClasses (ClassRegistrationContext& context)
+{
+ REGISTER_CLASS (NavMeshLayers)
+ REGISTER_CLASS (NavMesh)
+ REGISTER_CLASS (NavMeshAgent)
+ REGISTER_CLASS (NavMeshSettings)
+ REGISTER_CLASS (OffMeshLink)
+ REGISTER_CLASS (NavMeshObstacle)
+}
+
+#if ENABLE_MONO || UNITY_WINRT
+void ExportNavMeshBindings ();
+void ExportNavMeshPathBindings ();
+void ExportNavMeshAgentBindings ();
+void ExportNavMeshObstacleBindings ();
+
+static void RegisterNavmeshICallModule ()
+{
+#if !INTERNAL_CALL_STRIPPING
+ ExportNavMeshBindings ();
+ ExportNavMeshPathBindings ();
+ ExportNavMeshAgentBindings ();
+ ExportNavMeshObstacleBindings ();
+#endif
+}
+#endif
+
+extern "C" EXPORT_MODULE void RegisterModule_Navigation ()
+{
+ ModuleRegistrationInfo info;
+ info.registerClassesCallback = &RegisterNavMeshClasses;
+#if ENABLE_MONO || UNITY_WINRT
+ info.registerIcallsCallback = &RegisterNavmeshICallModule;
+#endif
+ RegisterModuleInfo (info);
+}
diff --git a/Runtime/NavMesh/OffMeshLink.cpp b/Runtime/NavMesh/OffMeshLink.cpp
new file mode 100644
index 0000000..c1646d0
--- /dev/null
+++ b/Runtime/NavMesh/OffMeshLink.cpp
@@ -0,0 +1,315 @@
+#include "UnityPrefix.h"
+#include "OffMeshLink.h"
+#include "NavMesh.h"
+#include "NavMeshSettings.h"
+#include "NavMeshManager.h"
+#include "DetourNavMesh.h"
+#include "NavMeshLayers.h"
+#include "DetourCrowd.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+OffMeshLink::OffMeshLink (MemLabelId& label, ObjectCreationMode mode)
+: Super (label, mode)
+{
+ m_NavMeshLayer = 0;
+ m_StaticPolyRef = 0;
+ m_DynamicPolyRef = 0;
+ m_ManagerHandle = -1;
+ m_CostOverride = -1.0f;
+ m_BiDirectional = true;
+ m_AutoUpdatePositions = false;
+ m_ShouldUpdateDynamic = false;
+ m_Activated = true;
+}
+
+void OffMeshLink::Reset ()
+{
+ Super::Reset ();
+}
+
+void OffMeshLink::SmartReset ()
+{
+ Super::SmartReset ();
+#if UNITY_EDITOR
+ m_NavMeshLayer = GetGameObject ().GetNavMeshLayer ();
+#endif
+}
+
+OffMeshLink::~OffMeshLink ()
+{
+ DisableDynamic ();
+}
+
+void OffMeshLink::AddToManager ()
+{
+ if (m_StaticPolyRef)
+ {
+ GetNavMeshSettings ().SetOffMeshPolyAccess (m_StaticPolyRef, m_Activated);
+
+ // TODO: We really should be uging users to update here - but this warning breaks runtime tests because of unexpected log.
+ //WarningStringObject ("This OffMeshLink is static. To make it dynamic please re-bake the navmesh or reset this OffMeshLink component", this);
+ }
+ else
+ {
+ UpdatePositions ();
+ }
+}
+
+void OffMeshLink::RemoveFromManager ()
+{
+ if (m_StaticPolyRef)
+ GetNavMeshSettings ().SetOffMeshPolyAccess (m_StaticPolyRef, m_Activated);
+
+ DisableDynamic ();
+}
+
+template<class TransferFunc>
+void OffMeshLink::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+
+ transfer.SetVersion (2);
+
+ TRANSFER (m_NavMeshLayer); //< Bake time only
+
+ #if UNITY_EDITOR
+ // NavMeshLayer has been moved from the game object to OffMeshLink in Unity 4.0
+ // This ensures that the value is pulled from the game object in AwakeFromLoad
+ if (transfer.IsOldVersion (1))
+ {
+ m_NavMeshLayer = 0xFFFFFFFF;
+ }
+ #endif
+
+ TRANSFER (m_Start); //< Bake time only
+ TRANSFER (m_End); //< Bake time only
+
+ ///@TODO: Get rid of the static polygon reference when breaking backwards compatibility
+ /// all component-based offmeshlinks should be dynamic.
+ transfer.Transfer (m_StaticPolyRef, "m_DtPolyRef", kHideInEditorMask);
+ // from being copied when duplicating an OML
+ TRANSFER (m_CostOverride); //< Changes propagated to navmesh at runtime
+
+ transfer.Align ();
+ TRANSFER (m_BiDirectional); //< Bake time only
+ TRANSFER (m_Activated); //< Changes propagated to navmesh at runtime
+ TRANSFER (m_AutoUpdatePositions); //< Changes propagated to navmesh at runtime
+
+}
+
+void OffMeshLink::AwakeFromLoad (AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad (mode);
+
+ #if UNITY_EDITOR
+ // NavMeshLayer has been moved from the game object to OffMeshLink in Unity 4.0
+ // This ensures that the value is pulled from the game object in AwakeFromLoad
+ if (m_NavMeshLayer == 0xFFFFFFFF && GetGameObjectPtr ())
+ {
+ m_NavMeshLayer = GetGameObject ().GetNavMeshLayer ();
+ }
+ #endif
+
+ NavMeshSettings& settings = GetNavMeshSettings ();
+
+ // Prioritize static polyref (baked) over dynamic
+ if (m_StaticPolyRef && !m_AutoUpdatePositions)
+ {
+ DisableDynamic ();
+ // The object instanceIDs may change from load to load.
+ // So storing the instanceID (of OffMeshLink) in baked data is not an option.
+ //
+ // Instead we attempt to set it given the polyref we got from the bake process.
+ //
+ // However: storing references (polyRef) to baked data in an object (OffMeshLink) is unsafe!
+ // eg: a user does not save the scene after baking navmesh - and later opens scene.
+ // Or: user disables an OffMeshLink, bakes navmesh, and re-enables offmeshlink.
+
+ if (settings.SetOffMeshPolyInstanceID (m_StaticPolyRef, GetInstanceID ()))
+ {
+ settings.SetOffMeshPolyCostOverride (m_StaticPolyRef, m_CostOverride);
+ settings.SetOffMeshPolyAccess (m_StaticPolyRef, m_Activated);
+ }
+ }
+ else
+ {
+ m_ShouldUpdateDynamic = true;
+ }
+}
+
+void OffMeshLink::UpdatePositions ()
+{
+ if (!IsActive ())
+ return;
+
+ DisableStatic ();
+ DisableDynamic (false);
+ EnableDynamic ();
+ m_ShouldUpdateDynamic = false;
+}
+
+void OffMeshLink::EnableDynamic ()
+{
+#if DT_DYNAMIC_OFFMESHLINK
+ if (m_ManagerHandle == -1)
+ GetNavMeshManager ().RegisterOffMeshLink (*this, m_ManagerHandle);
+
+ if (m_DynamicPolyRef || !m_Activated || !m_End || !m_Start || m_NavMeshLayer == NavMeshLayers::kNotWalkable)
+ return;
+
+ NavMeshSettings& settings = GetNavMeshSettings ();
+ dtNavMesh* navmesh = settings.GetInternalNavMesh ();
+ if (!navmesh)
+ return;
+
+ const int instanceID = GetInstanceID ();
+ const Vector3f startPos = m_Start->GetPosition ();
+ const Vector3f endPos = m_End->GetPosition ();
+
+ m_DynamicPolyRef = navmesh->AddDynamicOffMeshLink (startPos.GetPtr (), endPos.GetPtr (), instanceID, m_BiDirectional, (unsigned char)m_NavMeshLayer);
+ if (m_DynamicPolyRef)
+ {
+ settings.SetOffMeshPolyCostOverride (m_DynamicPolyRef, m_CostOverride);
+ }
+#endif
+}
+
+void OffMeshLink::CheckDynamicPositions ()
+{
+#if DT_DYNAMIC_OFFMESHLINK
+ if (m_DynamicPolyRef == 0 || m_ShouldUpdateDynamic)
+ return;
+
+ NavMeshSettings& settings = GetNavMeshSettings ();
+ dtNavMesh* navmesh = settings.GetInternalNavMesh ();
+ if (!navmesh)
+ return;
+
+ if (!m_Start || !m_End)
+ return;
+
+ const Vector3f objectStartPos = m_Start->GetPosition ();
+ const Vector3f objectEndPos = m_End->GetPosition ();
+ const dtOffMeshConnection* con = navmesh->GetDynamicOffMeshLink (m_DynamicPolyRef);
+ if (con)
+ {
+ const float connectionRadius = con->rad;
+ const Vector3f startPos = Vector3f (&con->pos[0]);
+ const Vector3f endPos = Vector3f (&con->pos[3]);
+ m_ShouldUpdateDynamic = !CompareApproximately (startPos, objectStartPos, connectionRadius) || !CompareApproximately (endPos, objectEndPos, connectionRadius);
+ }
+#endif
+}
+
+void OffMeshLink::OnNavMeshCleanup ()
+{
+ ClearDynamicPolyRef ();
+}
+
+void OffMeshLink::OnNavMeshChanged ()
+{
+ ClearDynamicPolyRef ();
+ m_ShouldUpdateDynamic = true;
+}
+
+void OffMeshLink::UpdateMovedPositions ()
+{
+ if (m_AutoUpdatePositions)
+ CheckDynamicPositions ();
+
+ if (m_ShouldUpdateDynamic || m_DynamicPolyRef == 0)
+ {
+ UpdatePositions ();
+ }
+}
+
+void OffMeshLink::DisableStatic ()
+{
+ GetNavMeshSettings ().SetOffMeshPolyAccess (m_StaticPolyRef, false);
+}
+
+void OffMeshLink::DisableDynamic (bool unregister)
+{
+#if DT_DYNAMIC_OFFMESHLINK
+ if (unregister && m_ManagerHandle != -1)
+ GetNavMeshManager ().UnregisterOffMeshLink (m_ManagerHandle);
+
+ if (m_DynamicPolyRef == 0)
+ return;
+
+ if (dtNavMesh* navmesh = GetNavMeshSettings ().GetInternalNavMesh ())
+ navmesh->RemoveDynamicOffMeshLink (m_DynamicPolyRef);
+
+ m_DynamicPolyRef = 0;
+#endif
+}
+
+void OffMeshLink::SetCostOverride (float costOverride)
+{
+ ABORT_INVALID_FLOAT (costOverride, costOverride, OffMeshLink);
+ if (m_CostOverride == costOverride)
+ return;
+
+ dtPolyRef ref = GetStaticOrDynamicPolyRef ();
+ GetNavMeshSettings ().SetOffMeshPolyCostOverride (ref, costOverride);
+ m_CostOverride = costOverride;
+ SetDirty ();
+}
+
+void OffMeshLink::SetBiDirectional (bool bidirectional)
+{
+ if (m_BiDirectional == bidirectional)
+ return;
+ m_BiDirectional = bidirectional;
+ SetDirty ();
+}
+
+void OffMeshLink::SetActivated (bool activated)
+{
+ if (m_Activated == activated)
+ return;
+
+ m_Activated = activated;
+
+ if (m_StaticPolyRef)
+ {
+ GetNavMeshSettings ().SetOffMeshPolyAccess (m_StaticPolyRef, activated);
+ }
+ else
+ {
+ if (activated && !m_DynamicPolyRef)
+ {
+ EnableDynamic ();
+ }
+ else if (!activated && m_DynamicPolyRef)
+ {
+ DisableDynamic ();
+ }
+ }
+ SetDirty ();
+}
+
+void OffMeshLink::SetNavMeshLayer (UInt32 layer)
+{
+ if (m_NavMeshLayer == layer)
+ return;
+
+ m_NavMeshLayer = layer;
+ UpdatePositions ();
+ SetDirty ();
+}
+
+
+bool OffMeshLink::GetOccupied () const
+{
+ if (const dtCrowd* crowd = GetNavMeshManager ().GetCrowdSystem ())
+ {
+ const dtPolyRef ref = GetStaticOrDynamicPolyRef ();
+ return crowd->IsRefOccupied (ref);
+ }
+ return false;
+}
+
+IMPLEMENT_CLASS (OffMeshLink)
+IMPLEMENT_OBJECT_SERIALIZE (OffMeshLink)
diff --git a/Runtime/NavMesh/OffMeshLink.h b/Runtime/NavMesh/OffMeshLink.h
new file mode 100644
index 0000000..26d31cf
--- /dev/null
+++ b/Runtime/NavMesh/OffMeshLink.h
@@ -0,0 +1,170 @@
+#ifndef RUNTIME_OFFMESHLINK
+#define RUNTIME_OFFMESHLINK
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Graphics/Transform.h"
+#include "NavMeshTypes.h"
+
+class dtNavMesh;
+
+
+
+class OffMeshLink : public Behaviour
+{
+public:
+ REGISTER_DERIVED_CLASS (OffMeshLink, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (OffMeshLink)
+
+ OffMeshLink (MemLabelId& label, ObjectCreationMode mode);
+ // ~OffMeshLink (); declared by a macro
+
+ inline void SetManagerHandle (int handle);
+ inline float GetCostOverride () const;
+ void SetCostOverride (float costOverride);
+
+ inline bool GetBiDirectional () const;
+ void SetBiDirectional (bool bidirectional);
+
+ inline bool GetActivated () const;
+ void SetActivated (bool activated);
+
+ bool GetOccupied () const;
+
+ inline UInt32 GetNavMeshLayer () const;
+ void SetNavMeshLayer (UInt32 layer);
+
+ inline void ClearDynamicPolyRef ();
+ inline bool ClearStaticPolyRef ();
+
+ inline Transform* GetStartTransform () const;
+ inline void SetStartTransform (Transform* t);
+
+ inline Transform* GetEndTransform () const;
+ inline void SetEndTransform (Transform* t);
+
+ void OnNavMeshCleanup ();
+ void OnNavMeshChanged ();
+ void UpdatePositions ();
+ void UpdateMovedPositions ();
+
+ inline bool GetAutoUpdatePositions () const;
+ inline void SetAutoUpdatePositions (bool autoUpdate);
+
+protected:
+ virtual void AwakeFromLoad (AwakeFromLoadMode mode);
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+ virtual void Reset ();
+ virtual void SmartReset ();
+
+private:
+ void EnableDynamic ();
+ void DisableDynamic (bool unregister = true);
+ void DisableStatic ();
+ void CheckDynamicPositions ();
+ inline dtPolyRef GetStaticOrDynamicPolyRef () const;
+
+ PPtr<Transform> m_Start;
+ PPtr<Transform> m_End;
+
+ UInt32 m_NavMeshLayer;
+ bool m_AutoUpdatePositions;
+ bool m_ShouldUpdateDynamic;
+
+ dtPolyRef m_StaticPolyRef;
+ dtPolyRef m_DynamicPolyRef;
+ int m_ManagerHandle;
+ float m_CostOverride;
+ bool m_BiDirectional;
+ bool m_Activated;
+};
+
+
+inline void OffMeshLink::SetManagerHandle (int handle)
+{
+ m_ManagerHandle = handle;
+}
+
+inline float OffMeshLink::GetCostOverride () const
+{
+ return m_CostOverride;
+}
+
+inline bool OffMeshLink::GetBiDirectional () const
+{
+ return m_BiDirectional;
+}
+
+inline bool OffMeshLink::GetActivated () const
+{
+ return m_Activated;
+}
+
+inline UInt32 OffMeshLink::GetNavMeshLayer () const
+{
+ return m_NavMeshLayer;
+}
+
+inline void OffMeshLink::ClearDynamicPolyRef ()
+{
+ m_DynamicPolyRef = 0;
+}
+
+inline bool OffMeshLink::ClearStaticPolyRef ()
+{
+ if (m_StaticPolyRef == 0)
+ {
+ return false;
+ }
+ m_StaticPolyRef = 0;
+ SetDirty ();
+ return true;
+}
+
+inline Transform* OffMeshLink::GetStartTransform () const
+{
+ return m_Start;
+}
+
+inline void OffMeshLink::SetStartTransform (Transform* t)
+{
+ if (t == m_Start)
+ {
+ return;
+ }
+ m_Start = t;
+ SetDirty ();
+}
+
+inline Transform* OffMeshLink::GetEndTransform () const
+{
+ return m_End;
+}
+
+inline void OffMeshLink::SetEndTransform (Transform* t)
+{
+ if (t == m_End)
+ {
+ return;
+ }
+ m_End = t;
+ SetDirty ();
+}
+
+inline bool OffMeshLink::GetAutoUpdatePositions () const
+{
+ return m_AutoUpdatePositions;
+}
+
+inline void OffMeshLink::SetAutoUpdatePositions (bool autoUpdate)
+{
+ m_AutoUpdatePositions = autoUpdate;
+ SetDirty ();
+}
+
+inline dtPolyRef OffMeshLink::GetStaticOrDynamicPolyRef () const
+{
+ return m_StaticPolyRef ? m_StaticPolyRef : m_DynamicPolyRef;
+}
+
+#endif
diff --git a/Runtime/NavMesh/ScriptBindings/NavMeshAgentBindings.txt b/Runtime/NavMesh/ScriptBindings/NavMeshAgentBindings.txt
new file mode 100644
index 0000000..973c4b3
--- /dev/null
+++ b/Runtime/NavMesh/ScriptBindings/NavMeshAgentBindings.txt
@@ -0,0 +1,263 @@
+C++RAW
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/NavMesh/NavMesh.h"
+#include "Runtime/NavMesh/NavMeshAgent.h"
+#include "Runtime/NavMesh/OffMeshLink.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+
+CSRAW
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace UnityEngine
+{
+
+// Keep this enum in sync with the one defined in "NavMeshTypes.h"
+ENUM ObstacleAvoidanceType
+ // Disable avoidance.
+ NoObstacleAvoidance = 0,
+
+ // Enable simple avoidance. Low performance impact.
+ LowQualityObstacleAvoidance = 1,
+
+ // Medium avoidance. Medium performance impact
+ MedQualityObstacleAvoidance = 2,
+
+ // Good avoidance. High performance impact
+ GoodQualityObstacleAvoidance = 3,
+
+ // Enable highest precision. Highest performance impact.
+ HighQualityObstacleAvoidance = 4
+END
+
+
+
+// Navigation mesh agent.
+CLASS NavMeshAgent : Behaviour
+
+ // Sets or updates the destination. This triggers calculation for a new path.
+ CUSTOM bool SetDestination (Vector3 target)
+ {
+ return self->SetDestination (target);
+ }
+
+ // Destination to navigate towards.
+ AUTO_PROP Vector3 destination GetDestination SetDestination
+
+ // Stop within this distance from the target position.
+ AUTO_PROP float stoppingDistance GetStoppingDistance SetStoppingDistance
+
+ // The current velocity of the [[NavMeshAgent]] component.
+ AUTO_PROP Vector3 velocity GetVelocity SetVelocity
+
+ // The next position on the path.
+ AUTO_PROP Vector3 nextPosition GetNextPosition SetInternalAgentPosition
+
+ // The current steering target - usually the next corner or end point of the current path. (RO)
+ AUTO_PROP Vector3 steeringTarget GetNextCorner
+
+ // The desired velocity of the agent including any potential contribution from avoidance. (RO)
+ AUTO_PROP Vector3 desiredVelocity GetDesiredVelocity
+
+ // Remaining distance along the current path - or infinity when not known. (RO)
+ AUTO_PROP float remainingDistance GetRemainingDistance
+
+ // The relative vertical displacement of the owning [[GameObject]].
+ AUTO_PROP float baseOffset GetBaseOffset SetBaseOffset
+
+ // Is agent currently positioned on an OffMeshLink. (RO)
+ CUSTOM_PROP bool isOnOffMeshLink
+ {
+ return self->IsOnOffMeshLink ();
+ }
+
+ // Enables or disables the current link.
+ CUSTOM void ActivateCurrentOffMeshLink (bool activated)
+ {
+ return self->ActivateCurrentOffMeshLink (activated);
+ }
+
+ // The current [[OffMeshLinkData]].
+ CSRAW public OffMeshLinkData currentOffMeshLinkData { get { return GetCurrentOffMeshLinkDataInternal (); } }
+
+ CUSTOM internal OffMeshLinkData GetCurrentOffMeshLinkDataInternal ()
+ {
+ OffMeshLinkData data;
+ self->GetCurrentOffMeshLinkData (&data);
+ return data;
+ }
+
+ // The next [[OffMeshLinkData]] on the current path.
+ CSRAW public OffMeshLinkData nextOffMeshLinkData { get { return GetNextOffMeshLinkDataInternal (); } }
+
+ CUSTOM internal OffMeshLinkData GetNextOffMeshLinkDataInternal ()
+ {
+ OffMeshLinkData data;
+ self->GetNextOffMeshLinkData (&data);
+ return data;
+ }
+
+ // Terminate OffMeshLink occupation and transfer the agent to the closest point on other side.
+ CUSTOM void CompleteOffMeshLink ()
+ {
+ return self->CompleteOffMeshLink ();
+ }
+
+ // Automate movement onto and off of OffMeshLinks.
+ AUTO_PROP bool autoTraverseOffMeshLink GetAutoTraverseOffMeshLink SetAutoTraverseOffMeshLink
+
+ // Automate braking of NavMeshAgent to avoid overshooting the destination.
+ AUTO_PROP bool autoBraking GetAutoBraking SetAutoBraking
+
+ // Attempt to acquire a new path if the existing path becomes invalid
+ AUTO_PROP bool autoRepath GetAutoRepath SetAutoRepath
+
+ // Does this agent currently have a path. (RO)
+ AUTO_PROP bool hasPath HasPath
+
+ // A path is being computed, but not yet ready. (RO)
+ AUTO_PROP bool pathPending PathPending
+
+ // Is the current path stale. (RO)
+ AUTO_PROP bool isPathStale IsPathStale
+
+ // Query the state of the current path.
+ AUTO_PROP NavMeshPathStatus pathStatus GetPathStatus
+
+ //*undocumented*
+ AUTO_PROP Vector3 pathEndPosition GetEndPositionOfCurrentPath
+
+ //*undocumented*
+ CUSTOM bool Warp (Vector3 newPosition)
+ {
+ return self->Warp(newPosition);
+ }
+
+ // Apply relative movement to current position.
+ CUSTOM void Move (Vector3 offset)
+ {
+ return self->Move (offset);
+ }
+
+ // Stop movement of this agent along its current path.
+ CUSTOM void Stop (bool stopUpdates = false)
+ {
+ return self->Stop (stopUpdates);
+ }
+
+ // Resumes the movement along the current path.
+ CUSTOM void Resume ()
+ {
+ return self->Resume ();
+ }
+
+ // Clears the current path. Note that this agent will not start looking for a new path until SetDestination is called.
+ CUSTOM void ResetPath ()
+ {
+ return self->ResetPath ();
+ }
+
+ // Assign path to this agent.
+ CUSTOM bool SetPath (NavMeshPath path)
+ {
+ Scripting::RaiseIfNull (path);
+ MonoNavMeshPath monopath;
+ MarshallManagedStructIntoNative (path, &monopath);
+ return self->SetPath (monopath.native);
+ }
+
+ // Set or get a copy of the current path.
+ CSRAW public NavMeshPath path { get { NavMeshPath path = new NavMeshPath (); CopyPathTo (path); return path; } set { if(value==null) throw new NullReferenceException(); SetPath (value); } }
+
+ CUSTOM internal void CopyPathTo (NavMeshPath path)
+ {
+ Scripting::RaiseIfNull (path);
+ MonoNavMeshPath monopath;
+ MarshallManagedStructIntoNative (path, &monopath);
+ self->CopyPath (monopath.native);
+ }
+
+ // Locate the closest NavMesh edge.
+ CUSTOM bool FindClosestEdge (out NavMeshHit hit)
+ {
+ return self->DistanceToEdge (hit);
+ }
+
+ // Trace movement towards a target postion in the NavMesh. Without moving the agent.
+ CUSTOM bool Raycast (Vector3 targetPosition, out NavMeshHit hit)
+ {
+ return self->Raycast (targetPosition, hit);
+ }
+
+ // Calculate a path to a specified point and store the resulting path.
+ CSRAW public bool CalculatePath (Vector3 targetPosition, NavMeshPath path)
+ {
+ path.ClearCorners ();
+ return CalculatePathInternal (targetPosition, path);
+ }
+
+ CUSTOM private bool CalculatePathInternal (Vector3 targetPosition, NavMeshPath path)
+ {
+ MonoNavMeshPath monopath;
+ MarshallManagedStructIntoNative (path, &monopath);
+ int actualSize = self->CalculatePolygonPath (targetPosition, monopath.native);
+ return actualSize>0;
+ }
+
+ // Sample a position along the current path.
+ CUSTOM bool SamplePathPosition (int passableMask, float maxDistance, out NavMeshHit hit)
+ {
+ return self->SamplePathPosition (passableMask, maxDistance, hit);
+ }
+
+ // Sets the cost for traversing over geometry of the layer type.
+ CUSTOM void SetLayerCost (int layer, float cost)
+ {
+ self->SetLayerCost (layer, cost);
+ }
+
+ // Gets the cost for traversing over geometry of the layer type.
+ CUSTOM float GetLayerCost (int layer)
+ {
+ return self->GetLayerCost (layer);
+ }
+
+ // Specifies which NavMesh layers are passable (bitfield). Changing /walkableMask/ will make the path stale (see ::ref::isPathStale)
+ AUTO_PROP int walkableMask GetWalkableMask SetWalkableMask
+
+ // Maximum movement speed.
+ AUTO_PROP float speed GetSpeed SetSpeed
+
+ // Maximum rotation speed in (deg/s).
+ AUTO_PROP float angularSpeed GetAngularSpeed SetAngularSpeed
+
+ // Maximum acceleration.
+ AUTO_PROP float acceleration GetAcceleration SetAcceleration
+
+ // Should the agent update the transform position.
+ AUTO_PROP bool updatePosition GetUpdatePosition SetUpdatePosition
+
+ // Should the agent update the transform orientation.
+ AUTO_PROP bool updateRotation GetUpdateRotation SetUpdateRotation
+
+ // Agent avoidance radius.
+ AUTO_PROP float radius GetRadius SetRadius
+
+ // Agent height.
+ AUTO_PROP float height GetHeight SetHeight
+
+ // The level of quality of avoidance.
+ AUTO_PROP ObstacleAvoidanceType obstacleAvoidanceType GetObstacleAvoidanceType SetObstacleAvoidanceType
+
+ // The avoidance priority level.
+ AUTO_PROP int avoidancePriority GetAvoidancePriority SetAvoidancePriority
+END
+
+CSRAW }
+
diff --git a/Runtime/NavMesh/ScriptBindings/NavMeshBindings.txt b/Runtime/NavMesh/ScriptBindings/NavMeshBindings.txt
new file mode 100644
index 0000000..5757557
--- /dev/null
+++ b/Runtime/NavMesh/ScriptBindings/NavMeshBindings.txt
@@ -0,0 +1,265 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/NavMesh/NavMesh.h"
+#include "Runtime/NavMesh/NavMeshAgent.h"
+#include "Runtime/NavMesh/OffMeshLink.h"
+#include "Runtime/NavMesh/NavMeshSettings.h"
+#include "Runtime/NavMesh/NavMeshLayers.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "External/Recast/Detour/Include/DetourNavMeshQuery.h"
+#include "Runtime/Scripting/Scripting.h"
+
+CSRAW
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace UnityEngine
+{
+
+// Keep this enum in sync with the one defined in "NavMeshTypes.h"
+// Link type specifier.
+ENUM OffMeshLinkType
+
+ // Manually specified type of link.
+ LinkTypeManual = 0,
+
+ // Vertical drop.
+ LinkTypeDropDown = 1,
+
+ // Horizontal jump.
+ LinkTypeJumpAcross = 2
+END
+
+// Keep this struct in sync with the one defined in "NavMeshTypes.h"
+// State of OffMeshLink.
+STRUCT OffMeshLinkData
+ CSRAW private int m_Valid;
+ CSRAW private int m_Activated;
+ CSRAW private int m_InstanceID;
+ CSRAW private OffMeshLinkType m_LinkType;
+ CSRAW private Vector3 m_StartPos;
+ CSRAW private Vector3 m_EndPos;
+
+ // Is link valid (RO).
+ CSRAW public bool valid { get { return m_Valid != 0; } }
+
+ // Is link active (RO).
+ CSRAW public bool activated { get { return m_Activated != 0; } }
+
+ // Link type specifier (RO).
+ CSRAW public OffMeshLinkType linkType { get { return m_LinkType; } }
+
+ // Link start world position (RO).
+ CSRAW public Vector3 startPos { get { return m_StartPos; } }
+
+ // Link end world position (RO).
+ CSRAW public Vector3 endPos { get { return m_EndPos; } }
+
+ // The [[OffMeshLink]] if the link type is a manually placed Offmeshlink (RO).
+ CSRAW public OffMeshLink offMeshLink { get { return GetOffMeshLinkInternal (m_InstanceID); } }
+
+ CUSTOM internal OffMeshLink GetOffMeshLinkInternal (int instanceID)
+ {
+ return Scripting::ScriptingWrapperFor (dynamic_instanceID_cast<OffMeshLink*> (instanceID));
+ }
+
+END
+
+// Keep this struct in sync with the one defined in "NavMeshTypes.h"
+// Result information for NavMesh queries.
+STRUCT NavMeshHit
+ CSRAW private Vector3 m_Position;
+ CSRAW private Vector3 m_Normal;
+ CSRAW private float m_Distance;
+ CSRAW private int m_Mask;
+ CSRAW private int m_Hit;
+
+ // Position of hit.
+ CSRAW public Vector3 position { get { return m_Position; } set { m_Position = value; } }
+
+ // Normal at the point of hit.
+ CSRAW public Vector3 normal { get { return m_Normal; } set { m_Normal = value; } }
+
+ // Distance to the point of hit.
+ CSRAW public float distance { get { return m_Distance; } set { m_Distance = value; } }
+
+ // Mask specifying NavMeshLayers at point of hit.
+ CSRAW public int mask { get { return m_Mask; } set { m_Mask = value; } }
+
+ // Flag set when hit.
+ CSRAW public bool hit { get { return m_Hit != 0; } set { m_Hit = value ? 1 : 0; } }
+END
+
+
+// Contains data describing a triangulation of the navmesh
+STRUCT NavMeshTriangulation
+ CSRAW public Vector3[] vertices;
+ CSRAW public int[] indices;
+ CSRAW public int[] layers;
+END
+
+
+// Navigation mesh.
+CLASS NavMesh : Object
+
+ // Trace a ray between two points on the NavMesh.
+ CUSTOM static bool Raycast (Vector3 sourcePosition, Vector3 targetPosition, out NavMeshHit hit, int passableMask)
+ {
+ NavMesh* navMesh = GetNavMeshSettings ().GetNavMesh ();
+ if (navMesh == NULL) {
+ InvalidateNavMeshHit (hit);
+ return false;
+ }
+ const dtQueryFilter filter = dtQueryFilter::createFilterForIncludeFlags (passableMask);
+ return navMesh->Raycast (hit, sourcePosition, targetPosition, filter);
+ }
+
+ // Calculate a path between two points and store the resulting path.
+ CSRAW public static bool CalculatePath (Vector3 sourcePosition, Vector3 targetPosition, int passableMask, NavMeshPath path)
+ {
+ path.ClearCorners ();
+ return CalculatePathInternal (sourcePosition, targetPosition, passableMask, path);
+ }
+
+ CUSTOM internal private static bool CalculatePathInternal (Vector3 sourcePosition, Vector3 targetPosition, int passableMask, NavMeshPath path)
+ {
+ NavMesh* navMesh = GetNavMeshSettings ().GetNavMesh ();
+ if (navMesh == NULL)
+ return false;
+
+ MonoNavMeshPath monopath;
+ MarshallManagedStructIntoNative (path, &monopath);
+
+ const dtQueryFilter filter = dtQueryFilter::createFilterForIncludeFlags (passableMask);
+ int actualSize = navMesh->CalculatePolygonPath (monopath.native, sourcePosition, targetPosition, filter);
+ return actualSize>0;
+ }
+
+ // Locate the closest NavMesh edge from a point on the NavMesh.
+ CUSTOM static bool FindClosestEdge (Vector3 sourcePosition, out NavMeshHit hit, int passableMask)
+ {
+ NavMesh* navMesh = GetNavMeshSettings ().GetNavMesh ();
+ if (navMesh == NULL) {
+ InvalidateNavMeshHit (hit);
+ return false;
+ }
+ const dtQueryFilter filter = dtQueryFilter::createFilterForIncludeFlags (passableMask);
+ return navMesh->DistanceToEdge (hit, sourcePosition, filter);
+ }
+
+ // Sample the NavMesh closest to the point specified.
+ CUSTOM static bool SamplePosition (Vector3 sourcePosition, out NavMeshHit hit, float maxDistance, int allowedMask)
+ {
+ NavMesh* navMesh = GetNavMeshSettings ().GetNavMesh ();
+ if (navMesh == NULL) {
+ InvalidateNavMeshHit (hit);
+ return false;
+ }
+ const dtQueryFilter filter = dtQueryFilter::createFilterForIncludeFlags (allowedMask);
+ return navMesh->SamplePosition (hit, sourcePosition, filter, maxDistance);
+ }
+
+ // Sets the cost for traversing over geometry of the layer type on all agents.
+ CUSTOM static void SetLayerCost (int layer, float cost)
+ {
+ GetNavMeshLayers ().SetLayerCost (layer, cost);
+ }
+
+ // Gets the cost for traversing over geometry of the layer type on all agents.
+ CUSTOM static float GetLayerCost (int layer)
+ {
+ return GetNavMeshLayers ().GetLayerCost (layer);
+ }
+
+ // Returns the layer index for a named layer.
+ CUSTOM static int GetNavMeshLayerFromName (string layerName)
+ {
+ return GetNavMeshLayers ().GetNavMeshLayerFromName (layerName.AsUTF8());
+ }
+
+ CONDITIONAL !UNITY_FLASH && !UNITY_WINRT
+ CSRAW public static NavMeshTriangulation CalculateTriangulation ()
+ {
+ NavMeshTriangulation tri = new NavMeshTriangulation ();
+ TriangulateInternal (ref tri.vertices, ref tri.indices, ref tri.layers);
+ return tri;
+ }
+
+ CONDITIONAL !UNITY_FLASH && !UNITY_WINRT
+ CUSTOM internal private static void TriangulateInternal (ref Vector3[] vertices, ref int[] indices, ref int[] layers)
+ {
+ if (NavMesh* navMesh = GetNavMeshSettings ().GetNavMesh ()) {
+ NavMesh::Triangulation triangulation;
+ navMesh->Triangulate (triangulation);
+
+ vertices = CreateScriptingArray (triangulation.vertices.begin (), triangulation.vertices.size (), MONO_COMMON.vector3 );
+ indices = CreateScriptingArray (triangulation.indices.begin (), triangulation.indices.size (), MONO_COMMON.int_32 );
+ layers = CreateScriptingArray (triangulation.layers.begin (), triangulation.layers.size (), MONO_COMMON.int_32 );
+ } else {
+ vertices = CreateEmptyStructArray (MONO_COMMON.vector3);
+ indices = CreateEmptyStructArray (MONO_COMMON.int_32);
+ layers = CreateEmptyStructArray (MONO_COMMON.int_32);
+ }
+ }
+
+ //*undocumented* DEPRECATED
+ CONDITIONAL !UNITY_FLASH && !UNITY_WINRT
+ OBSOLETE warning use NavMesh.CalculateTriangulation() instead.
+ CUSTOM static void Triangulate (out Vector3[] vertices, out int[] indices)
+ {
+ if (NavMesh* navMesh = GetNavMeshSettings ().GetNavMesh ()) {
+ NavMesh::Triangulation triangulation;
+ navMesh->Triangulate (triangulation);
+ if (vertices) {
+ *vertices = CreateScriptingArray (triangulation.vertices.begin(), triangulation.vertices.size (), MONO_COMMON.vector3 );
+ }
+ if (indices) {
+ *indices = CreateScriptingArray (triangulation.indices.begin(), triangulation.indices.size (), MONO_COMMON.int_32 );
+ }
+ }
+ }
+ //*undocumented*
+ CUSTOM static void AddOffMeshLinks () { }
+ //*undocumented*
+ CUSTOM static void RestoreNavMesh () { }
+
+END
+
+// Link allowing movement outside the planar navigation mesh.
+CLASS OffMeshLink : Component
+
+ // Is link active.
+ AUTO_PROP bool activated GetActivated SetActivated
+
+ // Is link occupied. (RO)
+ AUTO_PROP bool occupied GetOccupied
+
+ // Modify pathfinding cost for the link.
+ AUTO_PROP float costOverride GetCostOverride SetCostOverride
+
+ AUTO_PROP bool biDirectional GetBiDirectional SetBiDirectional
+
+ CUSTOM void UpdatePositions () { return self->UpdatePositions (); }
+
+ AUTO_PROP int navMeshLayer GetNavMeshLayer SetNavMeshLayer
+
+ AUTO_PROP bool autoUpdatePositions GetAutoUpdatePositions SetAutoUpdatePositions
+
+ AUTO_PTR_PROP Transform startTransform GetStartTransform SetStartTransform
+
+ AUTO_PTR_PROP Transform endTransform GetEndTransform SetEndTransform
+
+END
+
+
+CSRAW }
+
diff --git a/Runtime/NavMesh/ScriptBindings/NavMeshObstacleBindings.txt b/Runtime/NavMesh/ScriptBindings/NavMeshObstacleBindings.txt
new file mode 100644
index 0000000..4c71baf
--- /dev/null
+++ b/Runtime/NavMesh/ScriptBindings/NavMeshObstacleBindings.txt
@@ -0,0 +1,40 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/NavMesh/NavMeshObstacle.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+
+CSRAW
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace UnityEngine
+{
+
+// Navigation mesh obstacle.
+CLASS NavMeshObstacle : Behaviour
+
+ // Obstacle height.
+ AUTO_PROP float height GetHeight SetHeight
+
+ // Obstacle radius.
+ AUTO_PROP float radius GetRadius SetRadius
+
+ // Obstacle velocity.
+ AUTO_PROP Vector3 velocity GetVelocity SetVelocity
+
+ CONDITIONAL ENABLE_NAVMESH_CARVING
+ AUTO_PROP bool carving GetCarving SetCarving
+
+ CONDITIONAL ENABLE_NAVMESH_CARVING
+ AUTO_PROP float carvingMoveThreshold GetMoveThreshold SetMoveThreshold
+
+END
+
+CSRAW }
+
diff --git a/Runtime/NavMesh/ScriptBindings/NavMeshPathBindings.txt b/Runtime/NavMesh/ScriptBindings/NavMeshPathBindings.txt
new file mode 100644
index 0000000..9f67a39
--- /dev/null
+++ b/Runtime/NavMesh/ScriptBindings/NavMeshPathBindings.txt
@@ -0,0 +1,133 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/NavMesh/NavMesh.h"
+#include "Runtime/NavMesh/NavMeshPath.h"
+#include "Runtime/NavMesh/NavMeshSettings.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Mono/MonoManager.h"
+
+CSRAW
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace UnityEngine
+{
+
+// Keep this enum in sync with the one defined in "NavMeshPath.h"
+// Status of path.
+ENUM NavMeshPathStatus
+ // The path terminates at the destination.
+ PathComplete = 0,
+
+ // The path cannot reach the destination.
+ PathPartial = 1,
+
+ // The path is invalid.
+ PathInvalid = 2
+END
+
+CSRAW
+[StructLayout (LayoutKind.Sequential)]
+// Path navigation.
+CLASS NavMeshPath
+ CSRAW internal IntPtr m_Ptr;
+ CSRAW internal Vector3[] m_corners;
+
+ C++RAW
+
+#if !UNITY_FLASH && !UNITY_WEBGL && !UNITY_WINRT
+ #define GET ExtractMonoObjectData<NavMeshPath*>(self)
+#else
+ inline NavMeshPath* GetNativeNavMeshPath (ScriptingObjectPtr self)
+ {
+ MonoNavMeshPath managedpath;
+ MarshallManagedStructIntoNative (self, &managedpath);
+ return managedpath.native;
+ }
+ #define GET GetNativeNavMeshPath (self)
+#endif
+
+
+ // NavMeshPath constructor.
+ CUSTOM NavMeshPath ()
+ {
+ MonoNavMeshPath managedPath;
+ managedPath.native = new NavMeshPath ();
+ MarshallNativeStructIntoManaged (managedPath,self);
+ }
+
+ THREAD_SAFE
+ CUSTOM private void DestroyNavMeshPath ()
+ {
+ if (GET)
+ {
+ delete GET;
+ }
+ }
+
+ CSRAW ~NavMeshPath ()
+ {
+ DestroyNavMeshPath ();
+ m_Ptr = IntPtr.Zero;
+ }
+
+ CUSTOM private Vector3[] CalculateCornersInternal ()
+ {
+ NavMesh* navMesh = GetNavMeshSettings ().GetNavMesh ();
+ if (navMesh == NULL)
+ return CreateEmptyStructArray (GetMonoManager ().GetCommonClasses ().vector3);
+
+ const int polygonCount = GET->GetPolygonCount ();
+ if (polygonCount == 0)
+ return CreateEmptyStructArray (GetMonoManager ().GetCommonClasses ().vector3);
+
+ Vector3f* corners;
+ ALLOC_TEMP (corners, Vector3f, 2+polygonCount);
+
+ NavMeshPath* path = GET;
+ int cornerCount = navMesh->CalculatePathCorners (corners, 2+polygonCount, *path);
+ if (cornerCount == 0)
+ return CreateEmptyStructArray (GetMonoManager ().GetCommonClasses ().vector3);
+
+ return CreateScriptingArray<Vector3f>(corners, cornerCount, GetMonoManager ().GetCommonClasses ().vector3);
+ }
+
+ CUSTOM private void ClearCornersInternal ()
+ {
+ GET->SetPolygonCount (0);
+ }
+
+ // Erase all corner points from path.
+ CSRAW public void ClearCorners ()
+ {
+ ClearCornersInternal ();
+ m_corners = null;
+ }
+
+ CSRAW private void CalculateCorners ()
+ {
+ if (m_corners == null)
+ m_corners = CalculateCornersInternal ();
+ }
+
+ // Corner points of path. (RO)
+ CSRAW public Vector3[] corners { get { CalculateCorners (); return m_corners;} }
+
+ // Status of the path. (RO)
+ CUSTOM_PROP NavMeshPathStatus status
+ {
+ return GET->GetStatus ();
+ }
+
+ C++RAW
+ #undef GET
+END
+
+CSRAW }
+
diff --git a/Runtime/Network/BitStreamPacker.cpp b/Runtime/Network/BitStreamPacker.cpp
new file mode 100644
index 0000000..766158e
--- /dev/null
+++ b/Runtime/Network/BitStreamPacker.cpp
@@ -0,0 +1,556 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "BitStreamPacker.h"
+
+#if ENABLE_NETWORK
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Math/Vector3.h"
+#include "External/RakNet/builds/include/StringCompressor.h"
+#include "NetworkViewID.h"
+
+using namespace std;
+
+// WARNING: current impl did quite a few unaligned accesses, so we change it to use memcpy (no align restrictions)
+
+template <typename T> static inline void ReadValueUnaligned(const UInt8* data, T* val)
+{
+ ::memcpy(val, data, sizeof(T));
+}
+template <typename T> static inline void WriteValueUnaligned(UInt8* data, const T& val)
+{
+ ::memcpy(data, &val, sizeof(T));
+}
+
+
+void BitstreamPacker::ReadPackState (Quaternionf& t)
+{
+ if (m_DeltaReadPos + sizeof(Quaternionf) <= m_DeltaReadSize)
+ {
+ ReadValueUnaligned(m_ReadDeltaData + m_DeltaReadPos, &t);
+ m_DeltaReadPos += sizeof(Quaternionf);
+ }
+ else
+ {
+ t = Quaternionf (0,0,0,1);
+ m_DeltaReadPos += sizeof(Quaternionf);
+ }
+}
+
+void BitstreamPacker::ReadPackState (Vector3f& t)
+{
+ if (m_DeltaReadPos + sizeof(Vector3f) <= m_DeltaReadSize)
+ {
+ ReadValueUnaligned(m_ReadDeltaData + m_DeltaReadPos, &t);
+ m_DeltaReadPos += sizeof(Vector3f);
+ }
+ else
+ {
+ t = Vector3f(0,0,0);
+ m_DeltaReadPos += sizeof(Vector3f);
+ }
+}
+
+void BitstreamPacker::WritePackState (Vector3f& t)
+{
+ std::vector<UInt8>& data = *m_WriteDeltaData;
+ if (m_DeltaWritePos + sizeof(Vector3f) > data.size())
+ data.resize(m_DeltaWritePos + sizeof(Vector3f));
+
+ WriteValueUnaligned(&data[m_DeltaWritePos], t);
+ m_DeltaWritePos += sizeof(Vector3f);
+}
+
+void BitstreamPacker::WritePackState (Quaternionf& t)
+{
+ std::vector<UInt8>& data = *m_WriteDeltaData;
+ if (m_DeltaWritePos + sizeof(Quaternionf) > data.size())
+ data.resize(m_DeltaWritePos + sizeof(Quaternionf));
+
+ WriteValueUnaligned(&data[m_DeltaWritePos], t);
+ m_DeltaWritePos += sizeof(Quaternionf);
+}
+
+void BitstreamPacker::WritePackState (string& t)
+{
+ std::vector<UInt8>& data = *m_WriteDeltaData;
+ if (m_DeltaWritePos + t.size() > data.size())
+ data.resize(m_DeltaWritePos + t.size() + sizeof(SInt32));
+
+ WriteValueUnaligned<SInt32>(&data[m_DeltaWritePos], t.size());
+ m_DeltaWritePos += sizeof(SInt32);
+
+ memcpy(&data[m_DeltaWritePos], t.c_str(), t.size());
+ m_DeltaWritePos += t.size();
+}
+
+void BitstreamPacker::ReadPackState (string& t)
+{
+ t = string();
+ if (m_DeltaReadPos + sizeof(SInt32) <= m_DeltaReadSize)
+ {
+ SInt32 size = 0; ReadValueUnaligned<SInt32>(m_ReadDeltaData + m_DeltaReadPos, &size);
+ m_DeltaReadPos += sizeof(SInt32);
+
+ if (m_DeltaReadPos + size <= m_DeltaReadSize)
+ {
+ t.assign((char*)m_ReadDeltaData + m_DeltaReadPos, (char*)m_ReadDeltaData + m_DeltaReadPos + size);
+ }
+ m_DeltaReadPos += size;
+ }
+}
+
+void BitstreamPacker::WritePackState (char* t, int& length)
+{
+ std::vector<UInt8>& data = *m_WriteDeltaData;
+ if (m_DeltaWritePos + length > data.size())
+ data.resize(m_DeltaWritePos + length + sizeof(SInt32));
+
+ WriteValueUnaligned<SInt32>(&data[m_DeltaWritePos], length);
+ m_DeltaWritePos += sizeof(SInt32);
+ memcpy(&data[m_DeltaWritePos], t, length);
+ m_DeltaWritePos += length;
+}
+
+void BitstreamPacker::ReadPackState (char*& t, int& length)
+{
+ if (m_DeltaReadPos + sizeof(SInt32) <= m_DeltaReadSize)
+ {
+ SInt32 size = 0; ReadValueUnaligned<SInt32>(m_ReadDeltaData + m_DeltaReadPos, &size);
+ m_DeltaReadPos += sizeof(SInt32);
+
+ // Allocate new char array with correct size
+ t = new char[size];
+ length = size;
+
+ // Again check data bounds
+ if (m_DeltaReadPos + size <= m_DeltaReadSize)
+ {
+ // Assign char pointer to the correct location in memory
+
+ // WhatAFuckingHell is that
+ // should be memcpy probably ;-)
+ t = reinterpret_cast<char*>(m_ReadDeltaData + m_DeltaReadPos);
+ }
+ m_DeltaReadPos += size;
+ }
+}
+
+void BitstreamPacker::ReadPackState (NetworkViewID& t)
+{
+ if (m_DeltaReadPos + sizeof(NetworkViewID) <= m_DeltaReadSize) \
+ {
+ ReadValueUnaligned(m_ReadDeltaData + m_DeltaReadPos, &t);
+ m_DeltaReadPos += sizeof(NetworkViewID);
+ }
+ else
+ {
+ t = NetworkViewID::GetUnassignedViewID();
+ m_DeltaReadPos += sizeof(NetworkViewID);
+ }
+}
+
+void BitstreamPacker::WritePackState (NetworkViewID& t)
+{
+ std::vector<UInt8>& data = *m_WriteDeltaData;
+ if (m_DeltaWritePos + sizeof(NetworkViewID) > data.size())
+ data.resize(m_DeltaWritePos + sizeof(NetworkViewID));
+ WriteValueUnaligned(&data[m_DeltaWritePos], t);
+ m_DeltaWritePos += sizeof(NetworkViewID);
+}
+
+#define READ_WRITE_PACKSTATE(TYPE) \
+void BitstreamPacker::ReadPackState (TYPE& t) \
+{ \
+ if (m_DeltaReadPos + sizeof(TYPE) <= m_DeltaReadSize) \
+ { \
+ ReadValueUnaligned<TYPE>(m_ReadDeltaData + m_DeltaReadPos, &t); \
+ m_DeltaReadPos += sizeof(TYPE); \
+ } \
+ else \
+ { \
+ t = TYPE(); \
+ m_DeltaReadPos += sizeof(TYPE); \
+ } \
+} \
+void BitstreamPacker::WritePackState (TYPE t) \
+{ \
+ std::vector<UInt8>& data = *m_WriteDeltaData; \
+ if (m_DeltaWritePos + sizeof(TYPE) > data.size()) \
+ data.resize(m_DeltaWritePos + sizeof(TYPE)); \
+ WriteValueUnaligned<TYPE>(&data[m_DeltaWritePos], t); \
+ m_DeltaWritePos += sizeof(TYPE); \
+}
+
+READ_WRITE_PACKSTATE(UInt32)
+READ_WRITE_PACKSTATE(float)
+READ_WRITE_PACKSTATE(short)
+READ_WRITE_PACKSTATE(UInt8)
+READ_WRITE_PACKSTATE(bool)
+
+void BitstreamPacker::Serialize (NetworkViewID& value)
+{
+ if (m_IsReading)
+ {
+ if (m_WriteDeltaData != NULL)
+ {
+ NetworkViewID oldData;
+ ReadPackState(oldData);
+
+ bool readValue = false;
+ m_NoOutOfBounds &= m_BitStream->Read(readValue);
+ if (readValue)
+ m_NoOutOfBounds &= value.Read(*m_BitStream);
+ else
+ value = oldData;
+
+ WritePackState (value);
+ }
+ else
+ {
+ m_NoOutOfBounds &= value.Read(*m_BitStream);
+ }
+ }
+ else
+ {
+ if (m_WriteDeltaData != NULL)
+ {
+ NetworkViewID oldData;
+ ReadPackState(oldData);
+
+ if (value != oldData)
+ {
+ m_BitStream->Write1();
+ value.Write(*m_BitStream);
+ WritePackState (value);
+ m_IsDifferent |= true;
+ }
+ else
+ {
+ m_BitStream->Write0();
+ WritePackState (oldData);
+ }
+
+ }
+ else
+ {
+ value.Write(*m_BitStream);
+ m_IsDifferent |= true;
+ }
+ }
+
+}
+
+
+void BitstreamPacker::Serialize (float& value, float maxDelta)
+{
+ if (m_IsReading)
+ {
+ if (m_WriteDeltaData != NULL)
+ {
+ float oldData;
+ ReadPackState(oldData);
+
+ bool readValue = false;
+ m_NoOutOfBounds &= m_BitStream->Read(readValue);
+ if (readValue)
+ m_NoOutOfBounds &= m_BitStream->Read(value);
+ else
+ value = oldData;
+
+ WritePackState (value);
+ }
+ else
+ {
+ m_NoOutOfBounds &= m_BitStream->Read(value);
+ }
+ }
+ else
+ {
+ if (m_WriteDeltaData != NULL)
+ {
+ float oldData;
+ ReadPackState(oldData);
+
+ if (!CompareApproximately(value, oldData, maxDelta))
+ {
+ m_BitStream->Write1();
+ m_BitStream->Write(value);
+ WritePackState (value);
+ m_IsDifferent |= true;
+ }
+ else
+ {
+ m_BitStream->Write0();
+ WritePackState (oldData);
+ }
+
+ }
+ else
+ {
+ m_BitStream->Write(value);
+ m_IsDifferent |= true;
+ }
+ }
+}
+
+void BitstreamPacker::Serialize (bool& value)
+{
+ if (m_IsReading)
+ {
+ if (m_WriteDeltaData != NULL)
+ {
+ bool oldData;
+ ReadPackState(oldData);
+ m_NoOutOfBounds &= m_BitStream->Read(value);
+ WritePackState (value);
+ }
+ else
+ {
+ m_NoOutOfBounds &= m_BitStream->Read(value);
+ }
+ }
+ else
+ {
+ if (m_WriteDeltaData != NULL)
+ {
+ bool oldData;
+ ReadPackState(oldData);
+
+ if (value != oldData)
+ {
+ m_BitStream->Write(value);
+ WritePackState (value);
+ m_IsDifferent |= true;
+ }
+ else
+ {
+ m_BitStream->Write(value);
+ WritePackState (oldData);
+ }
+ }
+ else
+ {
+ m_BitStream->Write(value);
+ m_IsDifferent |= true;
+ }
+ }
+}
+
+#define SERIALIZE(TYPE) void BitstreamPacker::Serialize (TYPE& value) {\
+ if (m_IsReading)\
+ {\
+ if (m_WriteDeltaData != NULL)\
+ {\
+ TYPE oldData;\
+ ReadPackState(oldData);\
+ \
+ bool readValue = false;\
+ m_NoOutOfBounds &= m_BitStream->Read(readValue);\
+ if (readValue)\
+ m_NoOutOfBounds &= m_BitStream->Read(value);\
+ else\
+ value = oldData;\
+ \
+ WritePackState (value);\
+ }\
+ else\
+ {\
+ m_NoOutOfBounds &= m_BitStream->Read(value);\
+ }\
+ }\
+ else\
+ {\
+ if (m_WriteDeltaData != NULL)\
+ {\
+ TYPE oldData;\
+ ReadPackState(oldData);\
+ \
+ if (value != oldData)\
+ {\
+ m_BitStream->Write1();\
+ m_BitStream->Write(value);\
+ WritePackState (value);\
+ m_IsDifferent |= true;\
+ }\
+ else\
+ {\
+ m_BitStream->Write0();\
+ WritePackState (oldData);\
+ }\
+ }\
+ else\
+ {\
+ m_BitStream->Write(value);\
+ m_IsDifferent |= true;\
+ }\
+ }\
+}
+
+SERIALIZE(UInt32)
+SERIALIZE(short)
+SERIALIZE(UInt8)
+
+
+void BitstreamPacker::Serialize (Vector3f& value, float maxDelta)
+{
+ Serialize(value.x, maxDelta);
+ Serialize(value.y, maxDelta);
+ Serialize(value.z, maxDelta);
+}
+
+void BitstreamPacker::Serialize (Quaternionf& value, float maxDelta)
+{
+ Serialize(value.x, maxDelta);
+ Serialize(value.y, maxDelta);
+ Serialize(value.z, maxDelta);
+ Serialize(value.w, maxDelta);
+}
+
+void BitstreamPacker::Serialize (std::string& value)
+{
+ if (m_IsReading)
+ {
+ if (m_WriteDeltaData != NULL)
+ {
+ std::string oldData;
+ ReadPackState(oldData);
+
+ bool readValue = false;
+ m_BitStream->Read(readValue);
+ if (readValue)
+ {
+ char rawOut[4096];
+ if (StringCompressor::Instance()->DecodeString(rawOut, 4096, m_BitStream))
+ {
+ value = rawOut;
+ }
+ else
+ {
+ value = oldData;
+ }
+ }
+ else
+ value = oldData;
+
+ WritePackState (value);
+ }
+ else
+ {
+ char rawOut[4096];
+ if (StringCompressor::Instance()->DecodeString(rawOut, 4096, m_BitStream))
+ {
+ value = rawOut;
+ }
+ else
+ value = string();
+ }
+ }
+ else
+ {
+ if (m_WriteDeltaData != NULL)
+ {
+ string oldData;
+ ReadPackState(oldData);
+
+ if (value != oldData)
+ {
+ m_BitStream->Write1();
+ StringCompressor::Instance()->EncodeString(value.c_str(), 4096, m_BitStream);
+ WritePackState (value);
+ m_IsDifferent |= true;
+ }
+ else
+ {
+ m_BitStream->Write0();
+ }
+ WritePackState (value);
+ }
+ else
+ {
+ m_BitStream->Write(value);
+ m_IsDifferent |= true;
+ }
+ }
+}
+
+void BitstreamPacker::Serialize (char* value, int& valueLength)
+{
+ if (m_IsReading)
+ {
+ if (m_WriteDeltaData != NULL)
+ {
+ char* oldData = NULL;
+ ReadPackState(oldData, valueLength);
+
+ // Check if this contains a new state or if we should reuse the old one
+ bool readValue = false;
+ m_BitStream->Read(readValue);
+ if (readValue)
+ m_NoOutOfBounds &= m_BitStream->Read(value, valueLength);
+ else
+ value = oldData;
+ WritePackState (value, valueLength);
+ if (oldData != NULL)
+ delete[] oldData;
+ }
+ else
+ {
+ m_NoOutOfBounds &= m_BitStream->Read(value, valueLength);
+ }
+ }
+ else
+ {
+ if (m_WriteDeltaData != NULL)
+ {
+ char* oldData = NULL;
+ ReadPackState(oldData, valueLength);
+
+ // If the state has changed, then pack it, if not then reuse old state
+ if (strcmp(value,oldData) != 0)
+ {
+ m_BitStream->Write1(); // New state
+ m_BitStream->Write(value, valueLength);
+ WritePackState (value, valueLength);
+ m_IsDifferent |= true;
+ }
+ else
+ {
+ m_BitStream->Write0(); // No change
+ WritePackState (oldData, valueLength);
+ }
+ if (oldData != NULL)
+ delete[] oldData;
+ }
+ else
+ {
+ m_BitStream->Write(value, valueLength);
+ m_IsDifferent |= true;
+ }
+ }
+}
+
+
+BitstreamPacker::BitstreamPacker (RakNet::BitStream& stream, std::vector<UInt8>* delta, UInt8* readData, int readSize, bool reading)
+{
+ Init(stream, delta, readData, readSize, reading);
+}
+
+BitstreamPacker::BitstreamPacker (RakNet::BitStream& stream, bool reading)
+{
+ Init(stream, NULL, NULL, 0, reading);
+}
+
+void BitstreamPacker::Init(RakNet::BitStream& stream, std::vector<UInt8>* delta, UInt8* readData, int readSize, bool reading)
+{
+ m_BitStream = &stream;
+ m_DeltaReadPos = 0;
+ m_DeltaWritePos = 0;
+ m_WriteDeltaData = delta;
+ m_DeltaReadSize = readSize;
+ m_ReadDeltaData = readData;
+ m_IsDifferent = false;
+ m_IsReading = reading;
+ m_NoOutOfBounds = true;
+}
+
+#endif
diff --git a/Runtime/Network/BitStreamPacker.h b/Runtime/Network/BitStreamPacker.h
new file mode 100644
index 0000000..a437d88
--- /dev/null
+++ b/Runtime/Network/BitStreamPacker.h
@@ -0,0 +1,76 @@
+#pragma once
+
+#if ENABLE_NETWORK
+#include "External/RakNet/builds/include/BitStream.h"
+class Vector3f;
+class Quaternionf;
+struct NetworkViewID;
+
+
+class BitstreamPacker
+{
+ RakNet::BitStream* m_BitStream;
+
+ int m_DeltaReadPos;
+ UInt8* m_ReadDeltaData;
+ int m_DeltaReadSize;
+
+ std::vector<UInt8>* m_WriteDeltaData;
+ int m_DeltaWritePos;
+
+ bool m_IsDifferent;
+ bool m_IsReading;
+ bool m_NoOutOfBounds;
+
+ void Init(RakNet::BitStream& stream, std::vector<UInt8>* delta, UInt8* readDeltaData, int readDeltaSize, bool reading);
+
+ public:
+
+ BitstreamPacker (RakNet::BitStream& stream, std::vector<UInt8>* delta, UInt8* readDeltaData, int readDeltaSize, bool reading);
+ BitstreamPacker (RakNet::BitStream& stream, bool reading);
+
+ bool IsWriting () { return !m_IsReading; }
+ bool IsReading () { return m_IsReading; }
+ bool HasChanged () { return m_IsDifferent; }
+ bool HasReadOutOfBounds () { return !m_NoOutOfBounds; }
+
+ void Serialize (float& value, float maxDelta = -1.0F);
+ void Serialize (SInt32& value) { Serialize((UInt32&)value); }
+ void Serialize (UInt32& value);
+ void Serialize (short& value);
+ void Serialize (char& value) { Serialize((unsigned char&)value); }
+ void Serialize (unsigned char& value);
+ void Serialize (bool& value);
+ void Serialize (NetworkViewID& value);
+ void Serialize (std::string& value);
+ void Serialize (Vector3f& value, float maxDelta = -1.0F);
+ void Serialize (Quaternionf& value, float maxDelta = -1.0F);
+ void Serialize (char* value, int& valueLength);
+
+ private:
+
+ void ReadPackState (Quaternionf& t);
+ void ReadPackState (Vector3f& t);
+ void ReadPackState (float& t);
+ void ReadPackState (UInt32& t);
+ void ReadPackState (short& t);
+ void ReadPackState (unsigned char& t);
+ void ReadPackState (bool& t);
+ void ReadPackState (std::string& t);
+ void ReadPackState (char*& t, int& length);
+ void ReadPackState (NetworkViewID& t);
+
+ void WritePackState (Vector3f& t);
+ void WritePackState (Quaternionf& t);
+ void WritePackState (float t);
+ void WritePackState (UInt32 t);
+ void WritePackState (short t);
+ void WritePackState (unsigned char t);
+ void WritePackState (bool t);
+ void WritePackState (std::string& t);
+ void WritePackState (char* t, int& length);
+ void WritePackState (NetworkViewID& t);
+
+};
+
+#endif
diff --git a/Runtime/Network/DummyNetwork.cpp b/Runtime/Network/DummyNetwork.cpp
new file mode 100644
index 0000000..7d3945e
--- /dev/null
+++ b/Runtime/Network/DummyNetwork.cpp
@@ -0,0 +1,736 @@
+
+#include "NetworkManager.h"
+
+#if ENABLE_NETWORK
+#include "MasterServerInterface.h"
+#include "External/RakNet/builds/include/RakNetworkFactory.h"
+#include "External/RakNet/builds/include/RakPeerInterface.h"
+#include "External/RakNet/builds/include/RakNetStatistics.h"
+#include "External/RakNet/builds/include/RakSleep.h"
+#include "External/RakNet/builds/include/SocketLayer.h"
+#endif
+#include "Runtime/GameCode/CloneObject.h"
+#include "BitStreamPacker.h"
+#include "Runtime/Utilities/Utility.h"
+
+
+NetworkManager::NetworkManager(BaseAllocator* baseAllocator, ObjectCreationMode mode)
+: Super(baseAllocator, mode) { }
+
+NetworkManager::~NetworkManager() { }
+
+void NetworkManager::AddNetworkView (ListNode_& s) { }
+
+void NetworkManager::AddAllNetworkView (ListNode_& s) { }
+
+void NetworkManager::AddNonSyncNetworkView (ListNode_& s) { }
+
+void NetworkManager::AwakeFromLoad (AwakeFromLoadMode awakeMode) { }
+
+void NetworkManager::SetAssetToPrefab (const std::map<UnityGUID, PPtr<GameObject> >& mapping) { }
+
+void NetworkManager::NetworkOnApplicationQuit() { }
+
+void NetworkManager::InitializeSecurity() { }
+
+int NetworkManager::InitializeServer(int connections, int listenPort, bool useNat)
+{
+ return 0;
+}
+
+int NetworkManager::Connect(std::vector<string> IPs, int remotePort, int listenPort, const std::string& password)
+{
+ return 0;
+}
+
+int NetworkManager::Connect(std::string IP, int remotePort, const std::string& password)
+{
+ return 0;
+}
+
+int NetworkManager::Connect(std::string IP, int remotePort, int listenPort, const std::string& password)
+{
+ return 0;
+}
+
+void NetworkManager::Disconnect(int timeout, bool resetParams) { }
+
+void NetworkManager::CloseConnection(int target, bool sendDisconnect) { }
+
+void NetworkManager::NetworkUpdate() { }
+
+string NetworkManager::GetStats(int i)
+{
+ return string();
+}
+
+void NetworkManager::ClientConnectionDisconnected(int msgType) { }
+
+
+void NetworkManager::MsgNewConnection(SystemAddress clientAddress) { }
+
+void NetworkManager::MsgClientInit() { }
+
+void NetworkManager::RPCReceiveViewIDBatch (RPCParameters *rpcParameters) { }
+
+void NetworkManager::RPCRequestViewIDBatch (RPCParameters *rpcParameters) { }
+
+void NetworkManager::SendRPCBuffer (PlayerTable &player) { }
+
+bool NetworkManager::MayReceiveFromPlayer( SystemAddress adress, int group )
+{
+ return false;
+}
+
+bool NetworkManager::MaySendToPlayer( SystemAddress address, int group )
+{
+ return false;
+}
+
+
+void NetworkManager::SetReceivingGroupEnabled (int playerIndex, int group, bool enabled) { }
+
+void NetworkManager::SetSendingGroupEnabled (int group, bool enabled) { }
+
+void NetworkManager::SetSendingGroupEnabled (int playerIndex, int group, bool enabled) { }
+
+void NetworkManager::MsgStateUpdate(SystemAddress senderAddress) { }
+
+void NetworkManager::DestroyPlayerObjects(NetworkPlayer playerID) { }
+
+void NetworkManager::DestroyDelayed(NetworkViewID viewID) { }
+
+void NetworkManager::RPCNetworkDestroy(RPCParameters *rpcParameters) { }
+
+void NetworkManager::SetMessageQueueRunning(bool run) { }
+
+void NetworkManager::RegisterRPC(const char* reg, void ( *functionPointer ) ( RPCParameters *rpcParms )) { }
+
+void NetworkManager::PerformRPC(const std::string &function, int mode, RakNet::BitStream& parameters, NetworkViewID viewID, UInt32 group) { }
+
+void NetworkManager::BroadcastRPC(const char* name, const RakNet::BitStream *parameters, PacketPriority priority, SystemAddress target, RakNetTime *includedTimestamp, UInt32 group ) { }
+
+void NetworkManager::PerformRPCSpecificTarget(const char* function, PlayerTable *player, RakNet::BitStream& parameters, UInt32 group) { }
+
+void NetworkManager::PeformRPCRelayAll(char *name, int mode, NetworkViewID viewID, UInt32 group, RakNetTime timestamp, SystemAddress sender, RakNet::BitStream &stream) { }
+
+void NetworkManager::PerformRPCRelaySpecific(char *name, RakNet::BitStream *stream, NetworkPlayer player) { }
+
+
+void NetworkManager::AddRPC(const std::string& name, NetworkPlayer sender, NetworkViewID viewID, UInt32 group, RakNet::BitStream& stream) { }
+
+void NetworkManager::MsgRemoveRPCs() { }
+
+void NetworkManager::RemoveRPCs(NetworkPlayer playerIndex, NetworkViewID viewID, UInt32 groupMask) { }
+
+bool NetworkManager::ShouldIgnoreInGarbageDependencyTracking ()
+{
+ return true;
+}
+
+#pragma mark -
+
+NetworkView* NetworkManager::ViewIDToNetworkView(const NetworkViewID& ID)
+{
+ return NULL;
+}
+
+
+NetworkViewID NetworkManager::NetworkViewToViewID(NetworkView* view)
+{
+ NetworkViewID dummy;
+ return dummy;
+}
+
+int NetworkManager::GetValidInitIndex()
+{
+ return 0;
+}
+
+NetworkViewID NetworkManager::AllocateViewID()
+{
+ NetworkViewID dummy;
+ return dummy;
+}
+
+NetworkViewID NetworkManager::AllocateSceneViewID()
+{
+ NetworkViewID dummy;
+ return dummy;
+}
+
+NetworkViewID NetworkManager::ValidateSceneViewID(NetworkView* validateView, NetworkViewID viewID)
+{
+ NetworkViewID dummy;
+ return dummy;
+}
+
+bool NetworkManager::WasViewIdAllocatedByPlayer (NetworkViewID viewID, NetworkPlayer playerID)
+{
+ return false;
+}
+
+bool NetworkManager::WasViewIdAllocatedByMe(NetworkViewID viewID)
+{
+ return false;
+}
+
+NetworkPlayer NetworkManager::GetNetworkViewIDOwner(NetworkViewID viewID)
+{
+ NetworkPlayer dummy;
+ return dummy;
+}
+
+int NetworkManager::GetPlayerID()
+{
+ return 0;
+}
+
+int NetworkManager::GetPeerType()
+{
+ return 0;
+}
+
+int NetworkManager::GetDebugLevel()
+{
+ return 0;
+}
+
+SystemAddress NetworkManager::GetPlayerAddress()
+{
+ return UNASSIGNED_SYSTEM_ADDRESS;
+}
+
+bool NetworkManager::IsClient()
+{
+ return false;
+}
+
+bool NetworkManager::IsServer()
+{
+ return false;
+}
+
+void NetworkManager::SetSimulation (NetworkSimulation simulation) { }
+
+void NetworkManager::MsgClientDidDisconnect() { }
+
+void NetworkManager::MsgClientDidDisconnect(SystemAddress clientAddress) { }
+
+void NetworkManager::SetIncomingPassword (const std::string& incomingPassword) { }
+
+std::string NetworkManager::GetIncomingPassword ()
+{
+ return string();
+}
+
+int NetworkManager::GetMaxConnections()
+{
+ return 0;
+}
+
+int NetworkManager::GetConnectionCount()
+{
+ return 0;
+}
+
+PlayerTable* NetworkManager::GetPlayerEntry(SystemAddress playerAddress)
+{
+ return NULL;
+}
+
+PlayerTable* NetworkManager::GetPlayerEntry(NetworkPlayer index)
+{
+ return NULL;
+}
+
+SystemAddress NetworkManager::GetSystemAddressFromIndex(NetworkPlayer playerIndex)
+{
+ return UNASSIGNED_SYSTEM_ADDRESS;
+}
+
+int NetworkManager::GetIndexFromSystemAddress(SystemAddress playerAddress)
+{
+ return -1;
+}
+
+std::vector<PlayerTable> NetworkManager::GetPlayerAddresses()
+{
+ return std::vector<PlayerTable>();
+}
+
+
+bool NetworkManager::IsPasswordProtected()
+{
+ return false;
+}
+
+std::string NetworkManager::GetIPAddress()
+{
+ return string();
+}
+
+std::string NetworkManager::GetExternalIP()
+{
+ return std::string();
+}
+
+int NetworkManager::GetExternalPort()
+{
+ return 0;
+}
+
+std::string NetworkManager::GetIPAddress(int player)
+{
+ return string ();
+}
+
+int NetworkManager::GetPort()
+{
+ return 0;
+}
+
+int NetworkManager::GetPort(int player)
+{
+ return 0;
+}
+
+void NetworkManager::GetConnections(int* connection) { }
+
+bool NetworkManager::GetUseNat()
+{
+ return false;
+}
+
+void NetworkManager::SetUseNat(bool enabled) { }
+
+
+
+
+template<class TransferFunc>
+void NetworkManager::Transfer (TransferFunc& transfer) { }
+
+int NetworkManager::GetLastPing (NetworkPlayer player)
+{
+ return 0;
+}
+
+int NetworkManager::GetAveragePing (NetworkPlayer player)
+{
+ return 0;
+}
+
+Object* NetworkManager::Instantiate (Object& prefab, Vector3f pos, Quaternionf rot, UInt32 group)
+{
+ return NULL;
+}
+
+
+Object* NetworkManager::NetworkInstantiateImpl (RakNet::BitStream& bitstream, SystemAddress sender, RakNetTime time)
+{
+ return NULL;
+}
+
+void NetworkManager::RPCNetworkInstantiate (RPCParameters* rpcParameters) { }
+
+void NetworkManager::SetLevelPrefix(int levelPrefix) { }
+
+IMPLEMENT_CLASS_HAS_INIT (NetworkManager)
+IMPLEMENT_OBJECT_SERIALIZE (NetworkManager)
+GET_MANAGER (NetworkManager)
+GET_MANAGER_PTR (NetworkManager)
+
+void NetworkManager::InitializeClass () { }
+
+void NetworkManager::CleanupClass () { }
+
+bool NetworkManager::MaySend( int group )
+{
+ return false;
+}
+
+RakNetTime NetworkManager::GetTimestamp()
+{
+ return 0;
+}
+
+double NetworkManager::GetTime()
+{
+ return 0;
+}
+
+RakPeerInterface* NetworkManager::GetPeer()
+{
+ return 0;
+}
+
+void NetworkManager::SwapFacilitatorID(SystemAddress newAddress) { }
+
+void NetworkManager::SetOldMasterServerAddress(SystemAddress address) { }
+
+void NetworkManager::SetConnTesterAddress(SystemAddress address) { }
+
+int NetworkManager::TestConnection(bool forceNATType, bool forceTest)
+{
+ return -1;
+}
+
+void NetworkManager::SetMaxConnections(int connections) { }
+
+void NetworkManager::PingWrapper(Ping *time) { }
+
+
+static SystemAddress dummy_system_address;
+SystemAddress& NetworkManager::GetFacilitatorAddress(bool resolve)
+{
+ return dummy_system_address;
+}
+
+ConnectionTester::ConnectionTester(SystemAddress address) { }
+
+ConnectionTester::~ConnectionTester() { }
+
+void ConnectionTester::SetAddress(SystemAddress address) { }
+
+int ConnectionTester::Update()
+{
+ return 0;
+}
+
+void ConnectionTester::ReportTestSucceeded() { }
+
+int ConnectionTester::RunTest(bool forceNATType)
+{
+ return 0;
+}
+
+
+// NetworkView
+
+NetworkView::NetworkView (BaseAllocator* baseAllocator, ObjectCreationMode mode)
+: Super(baseAllocator, mode)
+, m_Node (this)
+, m_AllNode(this) { }
+
+NetworkView::~NetworkView () { }
+
+void NetworkView::Update() { }
+
+void NetworkView::RPCCall (const std::string &function, int inMode, MonoArray* args) { }
+
+void NetworkView::RPCCallSpecificTarget (const std::string &function, NetworkPlayer target, MonoArray* args) { }
+
+void NetworkView::AddToManager () { }
+
+void NetworkView::RemoveFromManager () { }
+
+void NetworkView::SetupSceneViewID () { }
+
+void NetworkView::AwakeFromLoad (AwakeFromLoadMode mode) { }
+
+void NetworkView::SetObserved (Unity::Component* component) { }
+
+Unity::Component* NetworkView::GetObserved ()
+{
+ return NULL;
+}
+
+void NetworkView::Unpack (RakNet::BitStream& bitStream, NetworkMessageInfo& info, int msgType) { }
+
+bool NetworkView::Pack(RakNet::BitStream &stream, PackState* writeStatePtr, UInt8* readData, int &readSize, int msgID)
+{
+ return false;
+}
+
+void NetworkView::Send (SystemAddress systemAddress, bool broadcast) { }
+
+void NetworkView::SendToAllButOwner() { }
+
+NetworkViewID NetworkView::GetViewID()
+{
+ NetworkViewID dummy;
+ return dummy;
+}
+
+void NetworkView::SetViewID(NetworkViewID viewID) { }
+
+void NetworkView::SetGroup(unsigned group) { }
+
+void NetworkView::Reset () { }
+
+void NetworkView::SetStateSynchronization (int sync) { }
+
+void NetworkView::SetInitState(int index, bool isSent) { }
+
+bool NetworkView::GetInitStateStatus(int index)
+{
+ return false;
+}
+
+void NetworkView::ClearInitStateAndOwner() { }
+
+bool NetworkView::SetPlayerScope(NetworkPlayer playerIndex, bool relevancy)
+{
+ return false;
+}
+
+void NetworkView::SetScope(unsigned int initIndex, bool relevancy) { }
+
+bool NetworkView::CheckScope(int initIndex)
+{
+ return false;
+}
+
+template<class TransferFunc>
+void NetworkView::Transfer (TransferFunc& transfer) { }
+
+SystemAddress NetworkView::GetOwnerAddress ()
+{
+ return UNASSIGNED_SYSTEM_ADDRESS;
+}
+
+void RegisterRPC (const char* name) { }
+
+void NetworkView::InitializeClass () { }
+
+void NetworkView::CleanupClass () { }
+
+
+IMPLEMENT_CLASS_HAS_INIT (NetworkView)
+IMPLEMENT_OBJECT_SERIALIZE (NetworkView)
+
+
+// BitStreamPacker
+
+void BitstreamPacker::ReadPackState (Quaternionf& t) { }
+
+void BitstreamPacker::ReadPackState (Vector3f& t) { }
+
+void BitstreamPacker::WritePackState (Vector3f& t) { }
+
+void BitstreamPacker::WritePackState (Quaternionf& t) { }
+
+void BitstreamPacker::WritePackState (string& t) { }
+
+void BitstreamPacker::ReadPackState (string& t) { }
+
+void BitstreamPacker::WritePackState (char* t, int& length) { }
+
+void BitstreamPacker::ReadPackState (char*& t, int& length) { }
+
+void BitstreamPacker::ReadPackState (NetworkViewID& t) { }
+
+void BitstreamPacker::WritePackState (NetworkViewID& t) { }
+
+#define READ_WRITE_PACKSTATE(TYPE) \
+void BitstreamPacker::ReadPackState (TYPE& t) \
+{ \
+} \
+void BitstreamPacker::WritePackState (TYPE t) \
+{ \
+}
+
+READ_WRITE_PACKSTATE(UInt32)
+READ_WRITE_PACKSTATE(float)
+READ_WRITE_PACKSTATE(short)
+READ_WRITE_PACKSTATE(UInt8)
+READ_WRITE_PACKSTATE(bool)
+
+void BitstreamPacker::Serialize (NetworkViewID& value) { }
+
+void BitstreamPacker::Serialize (float& value, float maxDelta) { }
+
+void BitstreamPacker::Serialize (bool& value) { }
+
+#define SERIALIZE(TYPE) void BitstreamPacker::Serialize (TYPE& value) {\
+}
+
+SERIALIZE(UInt32)
+SERIALIZE(short)
+SERIALIZE(UInt8)
+
+
+void BitstreamPacker::Serialize (Vector3f& value, float maxDelta) { }
+
+void BitstreamPacker::Serialize (Quaternionf& value, float maxDelta) { }
+
+void BitstreamPacker::Serialize (std::string& value) { }
+
+void BitstreamPacker::Serialize (char* value, int& valueLength) { }
+
+BitstreamPacker::BitstreamPacker (RakNet::BitStream& stream, std::vector<UInt8>* delta, UInt8* readData, int readSize, bool reading) { }
+
+BitstreamPacker::BitstreamPacker (RakNet::BitStream& stream, bool reading) { }
+
+void BitstreamPacker::Init(RakNet::BitStream& stream, std::vector<UInt8>* delta, UInt8* readData, int readSize, bool reading) { }
+
+
+
+// MasterServerInterface
+
+
+MasterServerInterface::MasterServerInterface(BaseAllocator* baseAllocator, ObjectCreationMode mode)
+: Super(baseAllocator, mode) { }
+
+MasterServerInterface::~MasterServerInterface() { }
+
+void MasterServerInterface::NetworkOnApplicationQuit() { }
+
+// Resolve the master server address if it is invalid
+void ResolveMasterServerAddress(SystemAddress& address) { }
+
+void MasterServerInterface::Connect() { }
+
+void MasterServerInterface::ProcessPacket(Packet *packet) { }
+
+void MasterServerInterface::NetworkUpdate() { }
+
+void MasterServerInterface::QueryHostList() { }
+
+void MasterServerInterface::QueryHostList(string gameType) { }
+
+void MasterServerInterface::ClearHostList() { }
+
+bool MasterServerInterface::PopulateUpdate()
+{
+ return false;
+}
+
+bool MasterServerInterface::PopulateUpdate(string gameName, string comment)
+{
+ return false;
+}
+
+void MasterServerInterface::RegisterHost(string gameType, string gameName, string comment) { }
+
+// Uses the game server peer
+void MasterServerInterface::SendHostUpdate() { }
+
+void MasterServerInterface::Disconnect() { }
+
+// Uses the game server peer
+void MasterServerInterface::UnregisterHost() { }
+
+std::vector<HostData> MasterServerInterface::PollHostList()
+{
+ return std::vector<HostData>();
+}
+
+void MasterServerInterface::ResetHostState() { }
+
+IMPLEMENT_CLASS (MasterServerInterface)
+GET_MANAGER (MasterServerInterface)
+GET_MANAGER_PTR (MasterServerInterface)
+
+
+
+// Utilities
+
+std::string GetLocalIP()
+{
+ std::string s = "0.0.0.0";
+ return s;
+}
+
+bool CheckForPublicAddress()
+{
+ return false;
+}
+
+int Ping::GetTime()
+{
+ return 0;
+}
+
+void Ping::SetTime(int value) { }
+
+int Ping::GetIsDone()
+{
+ return 0;
+}
+
+void Ping::SetIsDone(bool value) { }
+
+std::string Ping::GetIP()
+{
+ return std::string();
+}
+
+void Ping::SetIP(std::string value) { }
+
+
+
+// NetworkViewID
+
+
+std::string NetworkViewID::ToString () const
+{
+ return std::string();
+}
+
+bool operator == (const NetworkViewID& lhs, const NetworkViewID& rhs)
+{
+ return false;
+}
+
+
+// NetworkViewIDAllocator
+
+
+NetworkViewIDAllocator::NetworkViewIDAllocator() { }
+
+
+
+// RakNet
+
+
+using namespace RakNet;
+
+BitStream::BitStream() { }
+
+BitStream::~BitStream() { }
+
+LightweightDatabaseClient::LightweightDatabaseClient() { }
+
+LightweightDatabaseClient::~LightweightDatabaseClient() { }
+
+using namespace DataStructures;
+
+Table::Cell::Cell() { }
+
+Table::Cell::~Cell() { }
+
+NatPunchthroughClient::NatPunchthroughClient() { }
+
+NatPunchthroughClient::~NatPunchthroughClient() { }
+
+bool NatPunchthroughClient::OpenNAT(RakNetGUID destination, SystemAddress facilitator)
+{
+ return true;
+}
+
+void NatPunchthroughClient::Clear(void) { }
+
+void NatPunchthroughClient::OnAttach(void) { }
+
+void NatPunchthroughClient::Update(void) { }
+
+PluginReceiveResult NatPunchthroughClient::OnReceive(Packet *packet)
+{
+ return RR_STOP_PROCESSING_AND_DEALLOCATE;
+}
+
+void NatPunchthroughClient::OnClosedConnection(SystemAddress systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason) { }
+
+void NatPunchthroughClient::OnRakPeerShutdown(void) { }
+
+PluginInterface2::PluginInterface2() { }
+
+PluginInterface2::~PluginInterface2() { }
+
+const char *SystemAddress::ToString(bool writePort) const
+{
+ return "";
+}
+
+void SystemAddress::SetBinaryAddress(const char *str) { }
diff --git a/Runtime/Network/MasterServerInterface.cpp b/Runtime/Network/MasterServerInterface.cpp
new file mode 100644
index 0000000..8ce20d7
--- /dev/null
+++ b/Runtime/Network/MasterServerInterface.cpp
@@ -0,0 +1,692 @@
+#include "UnityPrefix.h"
+#include "MasterServerInterface.h"
+
+#if ENABLE_NETWORK
+#include "External/RakNet/builds/include/TableSerializer.h"
+#include "External/RakNet/builds/include/BitStream.h"
+#include "External/RakNet/builds/include/StringCompressor.h"
+#include "External/RakNet/builds/include/DS_Table.h"
+#include "External/RakNet/builds/include/RakNetworkFactory.h"
+#include "External/RakNet/builds/include/SocketLayer.h"
+#include "Configuration/UnityConfigureVersion.h"
+#include "NetworkManager.h"
+#include <time.h>
+#include "NetworkUtility.h"
+
+// Future todos
+// TODO: ATM there is a 200 ms delay on disconnects. It "could" be possible that more than 200 ms pass before a network action is completed
+// maybe this should be done only after we make sure the operation is done.
+// NOTE: Row IDs are sent to clients when they do not have a row ID (first reg) or when the master server has restarted (and forgotten everything).
+// The problem is that updates from the client are sent with the old row ID which is not found during lookup and a new one sent to the client
+// The client never receives this new row ID because the connection is always closed after sending updates. The simples solution is to
+// keep client connections persistent for clients running hosts. Clients which just query for the host list (game clients) disconnect immediately.
+
+namespace {
+ const int kMasterServerPort = 23466;
+ const time_t kMaxUpdateInterval = 2;
+}
+
+MasterServerInterface::MasterServerInterface(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_Peer = RakNetworkFactory::GetRakPeerInterface();
+ m_GameType = "";
+ m_HostName = "";
+ m_PendingRegister = false;
+ m_PendingHostUpdate = false;
+ m_PendingQuery = false;
+ m_RowID = -1;
+ m_LastHostUpdateTime = 0;
+ m_Registered = false;
+ m_MasterServerID.binaryAddress = 0;
+ m_MasterServerID.port = kMasterServerPort;
+ m_Version[0]=2; m_Version[1]=0; m_Version[2]=0;
+ m_UpdateRate = 10;
+ m_IsDedicatedServer = false;
+ time(&m_ShutdownTimer);
+
+ m_HostDatabaseClient = new LightweightDatabaseClient;
+
+ // Create the cell update that will be reused for storing last update
+ strcpy(m_LastUpdate[0].columnName,"NAT");
+ m_LastUpdate[0].columnType = DataStructures::Table::NUMERIC;
+ m_LastUpdate[0].cellValue.Set(0);
+ strcpy(m_LastUpdate[1].columnName,"Game name");
+ m_LastUpdate[1].columnType = DataStructures::Table::STRING;
+ m_LastUpdate[1].cellValue.Set(0);
+ strcpy(m_LastUpdate[2].columnName,"Connected players");
+ m_LastUpdate[2].columnType = DataStructures::Table::NUMERIC;
+ m_LastUpdate[2].cellValue.Set(0);
+ strcpy(m_LastUpdate[3].columnName,"Player limit");
+ m_LastUpdate[3].columnType = DataStructures::Table::NUMERIC;
+ m_LastUpdate[3].cellValue.Set(0);
+ strcpy(m_LastUpdate[4].columnName,"Password protected");
+ m_LastUpdate[4].columnType = DataStructures::Table::NUMERIC;
+ m_LastUpdate[4].cellValue.Set(0);
+ strcpy(m_LastUpdate[5].columnName,"IP address");
+ m_LastUpdate[5].columnType = DataStructures::Table::BINARY;
+ m_LastUpdate[5].cellValue.Set(NULL, 0);
+ strcpy(m_LastUpdate[6].columnName,"Port");
+ m_LastUpdate[6].columnType = DataStructures::Table::NUMERIC;
+ m_LastUpdate[6].cellValue.Set(0);
+ strcpy(m_LastUpdate[7].columnName,"Comment");
+ m_LastUpdate[7].columnType = DataStructures::Table::STRING;
+ m_LastUpdate[7].cellValue.Set(0);
+}
+
+MasterServerInterface::~MasterServerInterface()
+{
+ delete m_HostDatabaseClient;
+ m_HostDatabaseClient = NULL;
+ RakNetworkFactory::DestroyRakPeerInterface(m_Peer);
+ m_Peer = NULL;
+}
+
+void MasterServerInterface::NetworkOnApplicationQuit()
+{
+ m_Peer->Shutdown(100);
+ m_HostList.clear();
+ // Reset to default values
+ m_MasterServerID.binaryAddress = 0;
+ m_MasterServerID.port = kMasterServerPort;
+ m_GameType = "";
+ m_HostName = "";
+ m_HostComment = "";
+ m_PendingRegister = false;
+ m_PendingHostUpdate = false;
+ m_PendingQuery = false;
+ m_RowID = -1;
+ m_Registered = false;
+ m_UpdateRate = 10;
+ m_IsDedicatedServer = false;
+}
+
+// Resolve the master server address if it is invalid
+void MasterServerInterface::ResolveMasterServerAddress()
+{
+ ResolveAddress(m_MasterServerID, "masterserver.unity3d.com", "masterserverbeta.unity3d.com",
+ "Cannot resolve master server address, you must be connected to the internet before using it or set the address to something accessible to you.");
+}
+
+void MasterServerInterface::ClientConnect()
+{
+ ResolveMasterServerAddress();
+
+ SocketDescriptor sd(0,0);
+ if (!m_Peer->Startup(1, 30, &sd, 1))
+ {
+ SendToAllNetworkViews(kMasterServerConnectionAttemptFailed, kFailedToCreatedSocketOrThread);
+ }
+ m_Peer->AttachPlugin(&m_DatabaseClient);
+ if (!m_Peer->Connect(m_MasterServerID.ToString(false), m_MasterServerID.port, 0, 0))
+ {
+ if (m_Peer->GetMaximumNumberOfPeers() >= m_Peer->NumberOfConnections())
+ {
+ ErrorString("Internal error while connecting to master server. Too many connected peers.");
+ }
+ else
+ {
+ ErrorString("Internal error while attempting to connect to master server.");
+ SendToAllNetworkViews(kMasterServerConnectionAttemptFailed, kIncorrectParameters);
+ }
+ }
+}
+
+bool MasterServerInterface::CheckServerConnection()
+{
+ ResolveMasterServerAddress();
+
+ if (!GetNetworkManager().GetPeer()->IsConnected(m_MasterServerID))
+ {
+ ServerConnect();
+ return false;
+ }
+ if (!GetNetworkManager().GetPeer()->IsActive())
+ {
+ ServerConnect();
+ return false;
+ }
+ return true;
+}
+
+void MasterServerInterface::ServerConnect()
+{
+ if (!GetNetworkManager().GetPeer()->Connect(m_MasterServerID.ToString(false), m_MasterServerID.port, 0, 0))
+ {
+ ErrorString("Internal error while attempting to connect to master server\n");
+ SendToAllNetworkViews(kMasterServerConnectionAttemptFailed, kInternalDirectConnectFailed);
+ }
+ NetworkInfo(NULL, "Attempting to connect to master server at %s:%d", m_MasterServerID.ToString(false), m_MasterServerID.port);
+ m_PendingRegister = true;
+}
+
+void MasterServerInterface::ProcessPacket(Packet *packet)
+{
+ switch(packet->data[0])
+ {
+ // Disconnect and connection lost only occurs for clients requesting host info, not servers registering their info
+ case ID_DISCONNECTION_NOTIFICATION:
+ {
+ NetworkInfo(NULL, "Disconnected from master server");
+ SendToAllNetworkViews(kDisconnectedFromMasterServer, ID_DISCONNECTION_NOTIFICATION);
+ m_PendingQuery = false;
+ break;
+ }
+ case ID_CONNECTION_LOST:
+ {
+ // If connection was lost with master server we should re-register the host. If running as client do nothing.
+ if (GetNetworkManager().IsServer())
+ {
+ NetworkInfo(NULL, "Lost connection to master server, reconnecting and resending host info");
+ ResetHostState();
+ SendHostUpdate();
+ }
+ else
+ {
+ ErrorString("Connection with master server lost");
+ SendToAllNetworkViews(kDisconnectedFromMasterServer, ID_CONNECTION_LOST);
+ m_PendingQuery = false;
+ }
+ break;
+ }
+ case ID_CONNECTION_BANNED:
+ {
+ ErrorString("Temporarily banned from the master server");
+ SendToAllNetworkViews(kMasterServerConnectionAttemptFailed, ID_CONNECTION_BANNED);
+ break;
+ }
+ case ID_CONNECTION_REQUEST_ACCEPTED:
+ {
+ NetworkInfo(NULL, "Connected to master server at %s", packet->systemAddress.ToString());
+ if (m_PendingRegister)
+ {
+ m_PendingRegister = false;
+ RegisterHost(m_GameType, m_HostName, m_HostComment);
+ }
+ if (m_PendingQuery)
+ {
+ m_PendingQuery = false;
+ QueryHostList(m_GameType);
+ }
+ if (m_PendingHostUpdate)
+ {
+ m_PendingHostUpdate = false;
+ SendHostUpdate();
+ }
+ break;
+ }
+ case ID_ALREADY_CONNECTED:
+ {
+ NetworkError(NULL, "Already connected to the master server, the server probably hasn't cleaned up because of an abrupt disconnection.");
+ SendToAllNetworkViews(kMasterServerConnectionAttemptFailed, ID_ALREADY_CONNECTED);
+ m_PendingQuery = false;
+ break;
+ }
+ case ID_CONNECTION_ATTEMPT_FAILED:
+ {
+ ErrorString(Format("Failed to connect to master server at %s", packet->systemAddress.ToString()));
+ SendToAllNetworkViews(kMasterServerConnectionAttemptFailed, ID_CONNECTION_ATTEMPT_FAILED);
+ ResetHostState();
+ break;
+ }
+ // TODO: atm the server does not send this during client lookups, but maybe it should (as all tables are dynamically created this will never be returned to hosts)
+ case ID_DATABASE_UNKNOWN_TABLE:
+ {
+ ErrorString("Unkown game type");
+ // This uses NetworkConnectionError enums, and this status message doesn't belong in there
+ //SendToAllNetworkViews(kMasterServerConnectionAttemptFailed, kUnkownGameType);
+ break;
+ }
+ // This should never occur as we don't use db passwords directly
+ case ID_DATABASE_INCORRECT_PASSWORD:
+ {
+ ErrorString("Incorrect master server password");
+ break;
+ }
+ case ID_DATABASE_QUERY_REPLY:
+ {
+ NetworkInfo(NULL, "Incoming host list query response from master server.");
+
+ DataStructures::Table table;
+ if (TableSerializer::DeserializeTable(packet->data+sizeof(MessageID), packet->length-sizeof(MessageID), &table))
+ {
+ m_HostList.clear();
+ DataStructures::Page<unsigned, DataStructures::Table::Row*, _TABLE_BPLUS_TREE_ORDER> *cur = table.GetListHead();
+ while (cur)
+ {
+ for (int i=0; i < (unsigned)cur->size; i++)
+ {
+ // NOTE: The first three cell values are the systemId (binary), last ping response time and next ping
+ // send time(numerics)
+ DataStructures::List<DataStructures::Table::Cell*> cells = cur->data[i]->cells;
+ HostData data;
+ if (cells[5]->c != NULL &&
+ (int)cells[9]->i != 0 &&
+ cells[9]->c != NULL &&
+ (int)cells[10]->i != 0 &&
+ cells[12]->c != NULL)
+ {
+ data.useNat = (int)cells[4]->i;
+ data.gameType = m_GameType;
+ data.gameName = cells[5]->c;
+ data.connectedPlayers = (int)cells[6]->i;
+ data.playerLimit = (int)cells[7]->i;
+ data.passwordProtected = (int)cells[8]->i;
+ data.guid = cells[12]->c;
+
+ int ipCount = int(cells[9]->i / 16);
+ // If the size of the data is not a factor of 16 then something has gone wrong (IP addresses have size 16)
+ if (((int)cells[9]->i) % 16 == 0)
+ {
+ for (int ip=0; ip < ipCount; ip++)
+ {
+ const char* ipData = cells[9]->c + ip * 16;
+ if( ipData[0] == 0 ) break;
+ data.IP.push_back( ipData );
+ }
+ }
+ else
+ {
+ ErrorString(Format("Malformed data inside IP information packet. Size was %f", cells[8]->i));
+ }
+ data.port = int(cells[10]->i);
+ if (cells[11]->c != NULL)
+ data.comment = cells[11]->c;
+ else
+ data.comment = "";
+ m_HostList.push_back(data);
+ }
+ else
+ {
+ ErrorString("Received malformed data in the host list from the master server\n");
+ }
+ }
+ cur=cur->next;
+ }
+ }
+ else
+ {
+ m_HostList.clear();
+ }
+ SendToAllNetworkViews(kMasterServerEvent, kHostListReceived);
+ break;
+ }
+ case ID_DATABASE_ROWID:
+ {
+ unsigned int rowID;
+ RakNet::BitStream stream;
+ stream.Write((char*)packet->data, packet->length);
+ stream.IgnoreBits(8);
+ stream.Read(rowID);
+
+ NetworkInfo(NULL, "Received identifier %u from master server", rowID);
+ SendToAllNetworkViews(kMasterServerEvent, kRegistrationSucceeded);
+ m_RowID = rowID;
+
+ break;
+ }
+ case ID_MASTERSERVER_REDIRECT:
+ {
+ SystemAddress newMaster, newFacilitator;
+ RakNet::BitStream b(packet->data, packet->length, false);
+ b.IgnoreBits(8);
+ b.Read(newMaster);
+ b.Read(newFacilitator);
+
+ GetNetworkManager().SwapFacilitatorID(newFacilitator);
+ SystemAddress oldMasterServer = m_MasterServerID;
+ GetNetworkManager().SetOldMasterServerAddress(oldMasterServer);
+ m_MasterServerID = newMaster;
+ ResetHostState();
+
+ if (GetNetworkManager().IsServer())
+ {
+ GetNetworkManager().GetPeer()->CloseConnection(oldMasterServer, true);
+ NetworkInfo(NULL, "Redirecting master server host updates to %s", newMaster.ToString());
+ NetworkInfo(NULL, "Changing facilitator location to %s", newFacilitator.ToString());
+ SendHostUpdate();
+ }
+ else
+ {
+ NetworkInfo(NULL, "Redirecting master server host list queries to %s", newMaster.ToString());
+ NetworkInfo(NULL, "Changing facilitator location to %s", newFacilitator.ToString());
+ Disconnect();
+ QueryHostList();
+ }
+ break;
+ }
+ case ID_MASTERSERVER_MSG:
+ {
+ int msgLength;
+ RakNet::BitStream b(packet->data, packet->length, false);
+ b.IgnoreBits(8);
+ b.Read(msgLength);
+ if (msgLength > 0)
+ {
+ char* msg = new char[msgLength];
+ b.Read(msg, msgLength);
+ LogString(Format("Message from master server: %s", msg));
+ delete[] msg;
+ }
+ break;
+ }
+ default:
+ {
+ NetworkError(NULL, "Unknown message from master server (%s) %d", packet->systemAddress.ToString(), packet->data[0]);
+ break;
+ }
+ }
+}
+
+void MasterServerInterface::NetworkUpdate()
+{
+ if (!m_Peer)
+ return;
+
+ // Send heartbeat if running as server, only send if running as server and we have already registered (m_HostName set)
+ if (m_UpdateRate > 0 && m_Registered)
+ {
+ if ((time(0) - m_LastHostUpdateTime > m_UpdateRate) && m_HostName.size() > 1 && !m_PendingRegister)
+ {
+ SendHostUpdate();
+ }
+ }
+
+ if (!m_Peer->IsActive())
+ return;
+
+ // If not registering or already registered, this is a client, then check for shutdown timeout
+ if (!m_Registered && !m_PendingRegister && time(0) > m_ShutdownTimer + 20)
+ {
+ // Use a short delay, its not that bad if the disconnect notification never arrives at master server
+ m_Peer->Shutdown(50, 0);
+ }
+
+ Packet *p;
+ p=m_Peer->Receive();
+ while (p)
+ {
+ ProcessPacket(p);
+ m_Peer->DeallocatePacket(p);
+ p=m_Peer->Receive();
+ }
+}
+
+void MasterServerInterface::QueryHostList()
+{
+ QueryHostList(m_GameType);
+}
+
+void MasterServerInterface::QueryHostList(string gameType)
+{
+ time(&m_ShutdownTimer);
+
+ // Wait for previous query to clear
+ if (m_PendingQuery) return;
+
+ if (gameType.empty())
+ {
+ ErrorString("Empty game type given in QueryHostList(), aborting query.");
+ return;
+ }
+ m_GameType = gameType;
+
+ ResolveMasterServerAddress();
+
+ if (m_Peer == NULL)
+ {
+ ClientConnect();
+ m_PendingQuery = true;
+ return;
+ }
+ else if (!m_Peer->IsActive())
+ {
+ ClientConnect();
+ m_PendingQuery = true;
+ return;
+ }
+ else if (!m_Peer->IsConnected(m_MasterServerID))
+ {
+ ClientConnect();
+ m_PendingQuery = true;
+ return;
+ }
+
+ m_DatabaseClient.QueryTable(m_Version, gameType.c_str(), 0, 0, 0, 0, 0, 0, 0, m_MasterServerID, false);
+// LogString("Sent host query to master server");
+ // Disconnect after the list has arrived
+}
+
+void MasterServerInterface::ClearHostList()
+{
+ m_HostList.clear();
+}
+
+bool MasterServerInterface::PopulateUpdate()
+{
+ return PopulateUpdate(m_HostName, m_HostComment);
+}
+
+bool MasterServerInterface::PopulateUpdate(string gameName, string comment)
+{
+ // TODO: The function inside GetIPs uses char arrays which are pre-allocated. If it returns a full array then it is possible there
+ // are more IP addresses, in which case it should get a larger char array to use.
+ char ips[10][16];
+ int size = GetIPs(ips)*16;
+ if (size == 0)
+ ErrorString("Could not retrieve internal IP address. Host registration failed.");
+
+ bool changed = false;
+
+ if (((int)m_LastUpdate[0].cellValue.i) != static_cast<int>(GetNetworkManager().GetUseNat()))
+ changed = true;
+
+ if (((int)m_LastUpdate[1].cellValue.i) != 0 && changed != true)
+ {
+ if (strcmp(m_LastUpdate[1].cellValue.c,gameName.c_str()) != 0)
+ {
+ changed = true;
+ m_LastUpdate[1].cellValue.Clear();
+ m_LastUpdate[1].cellValue.Set(const_cast<char*>(gameName.c_str()));
+ }
+ }
+ else
+ {
+ changed = true;
+ }
+
+ //printf_console("connCount: Comparing %d and %d\n", intCell, GetNetworkManager().GetConnectionCount());
+ if (((int)m_LastUpdate[2].cellValue.i) != (GetNetworkManager().GetConnectionCount() + static_cast<int>(!m_IsDedicatedServer)) && changed != true)
+ changed = true;
+ //printf_console("maxCount: Comparing %d and %d\n", intCell, GetNetworkManager().GetMaxConnections());
+ if (((int)m_LastUpdate[3].cellValue.i) != GetNetworkManager().GetMaxConnections() + static_cast<int>(!m_IsDedicatedServer) && changed != true)
+ changed = true;
+ //printf_console("password: Comparing %d and %d\n", intCell, GetNetworkManager().IsPasswordProtected());
+ if (((int)m_LastUpdate[4].cellValue.i) != static_cast<int>(GetNetworkManager().IsPasswordProtected()) && changed != true)
+ changed = true;
+
+ if (((int)m_LastUpdate[5].cellValue.i) != 0 && changed != true)
+ {
+ /*printf_console("IPs: Comparing size %d and %d\n", intCell, size);
+ for (int i=0; i < intCell; i++)
+ printf_console("%x", tmpIPs[i]);
+ printf_console(" ");
+ for (int i=0; i < size; i++)
+ printf_console("%x", static_cast<char*>(ips[0])[i]);
+ printf_console("\n");*/
+ if (m_LastUpdate[5].cellValue.i != size)
+ changed = true;
+ else if (memcmp(m_LastUpdate[5].cellValue.c, ips, size) != 0)
+ changed = true;
+ }
+ else
+ {
+ changed = true;
+ }
+
+ //printf_console("port: Comparing %d and %d\n", intCell, GetNetworkManager().GetPort());
+ if (((int)m_LastUpdate[6].cellValue.i) != GetNetworkManager().GetPort() && changed != true)
+ changed = true;
+
+ if (((int)m_LastUpdate[7].cellValue.i) != 0 && changed != true)
+ {
+ //printf_console("comment: Comparing %s and %s\n", tmpComment, comment.c_str());
+ if (strcmp(m_LastUpdate[7].cellValue.c, comment.c_str()) != 0)
+ changed = true;
+ }
+ else
+ {
+ changed = true;
+ }
+
+ if (changed)
+ {
+ for (int i = 0; i < CELL_COUNT; i++)
+ m_LastUpdate[i].cellValue.Clear();
+ m_LastUpdate[0].columnType=DataStructures::Table::NUMERIC;
+ m_LastUpdate[0].cellValue.Set(GetNetworkManager().GetUseNat());
+ m_LastUpdate[1].columnType=DataStructures::Table::STRING;
+ m_LastUpdate[1].cellValue.Set(const_cast<char*>(gameName.c_str()));
+ m_LastUpdate[2].columnType=DataStructures::Table::NUMERIC;
+ m_LastUpdate[2].cellValue.Set(GetNetworkManager().GetConnectionCount() + static_cast<int>(!m_IsDedicatedServer));
+ m_LastUpdate[3].columnType=DataStructures::Table::NUMERIC;
+ m_LastUpdate[3].cellValue.Set(GetNetworkManager().GetMaxConnections() + static_cast<int>(!m_IsDedicatedServer));
+ m_LastUpdate[4].columnType=DataStructures::Table::NUMERIC;
+ m_LastUpdate[4].cellValue.Set(static_cast<int>(GetNetworkManager().IsPasswordProtected()));
+ m_LastUpdate[5].columnType=DataStructures::Table::BINARY;
+ m_LastUpdate[5].cellValue.Set((char*)ips, size);
+ m_LastUpdate[6].columnType=DataStructures::Table::NUMERIC;
+ m_LastUpdate[6].cellValue.Set(GetNetworkManager().GetPort());
+ m_LastUpdate[7].columnType=DataStructures::Table::STRING;
+ m_LastUpdate[7].cellValue.Set(const_cast<char*>(comment.c_str()));
+ }
+
+ return changed;
+}
+
+void MasterServerInterface::RegisterHost(string gameType, string gameName, string comment)
+{
+ // Wait until pending registrations have cleared or that a certain interval has passed (don't want to hammer the server)
+ if (m_PendingRegister || m_LastHostUpdateTime > time(0) - kMaxUpdateInterval)
+ return;
+
+ if (gameType.empty())
+ {
+ ErrorString("Empty game type given during host registration, aborting");
+ SendToAllNetworkViews(kMasterServerEvent, kRegistrationFailedGameName);
+ return;
+ }
+ if (gameName.empty())
+ {
+ ErrorString("Empty game name given during host registration, aborting");
+ SendToAllNetworkViews(kMasterServerEvent, kRegistrationFailedGameType);
+ return;
+ }
+ if (GetNetworkManager().GetPort() == 0)
+ {
+ ErrorString("It's not possible to register a host until it is running.");
+ SendToAllNetworkViews(kMasterServerEvent, kRegistrationFailedNoServer);
+ return;
+ }
+
+ m_GameType = gameType;
+ m_HostName = gameName;
+ m_HostComment = comment;
+
+ GetNetworkManager().GetPeer()->AttachPlugin(m_HostDatabaseClient);
+
+ if (!CheckServerConnection())
+ return;
+
+ PopulateUpdate();
+
+ m_LastHostUpdateTime = time(0);
+
+ m_HostDatabaseClient->UpdateRow(m_Version, gameType.c_str(), 0, RUM_UPDATE_OR_ADD_ROW, false, 0, m_LastUpdate, CELL_COUNT, m_MasterServerID, false );
+ NetworkLog(NULL, "Sent host registration to master server, registering a %sNAT assisted game as\n \"%s\", %d, %d, %s, \"%s\"",
+ (GetNetworkManager().GetUseNat()) ? "" : "non-",
+ gameName.c_str(),
+ GetNetworkManager().GetConnectionCount() + static_cast<int>(!m_IsDedicatedServer),
+ GetNetworkManager().GetMaxConnections() + static_cast<int>(!m_IsDedicatedServer),
+ (GetNetworkManager().IsPasswordProtected()) ? "password protected" : "not password protected",
+ comment.c_str());
+
+ m_Registered = true;
+}
+
+// Uses the game server peer
+void MasterServerInterface::SendHostUpdate()
+{
+ // Wait until pending host updates are finished
+ if (m_PendingHostUpdate)
+ {
+ NetworkInfo(NULL, "Still waiting for a master server reponse to another host update, ignoring this update.");
+ return;
+ }
+
+ if (!CheckServerConnection())
+ return;
+
+ if (!PopulateUpdate())
+ return;
+
+ m_LastHostUpdateTime = time(0);
+
+ if (m_RowID == -1)
+ {
+ m_HostDatabaseClient->UpdateRow(m_Version, m_GameType.c_str(), 0, RUM_UPDATE_OR_ADD_ROW, false, 0, m_LastUpdate, CELL_COUNT, m_MasterServerID, false );
+ NetworkInfo(NULL, "Sent new host update to master server");
+ }
+ else
+ {
+ m_HostDatabaseClient->UpdateRow(m_Version, m_GameType.c_str(), 0, RUM_UPDATE_OR_ADD_ROW, true, m_RowID, m_LastUpdate, CELL_COUNT, m_MasterServerID, false );
+ NetworkInfo(NULL, "Sent host update to master server with identifier %d", m_RowID);
+ }
+
+ m_Registered = true;
+}
+
+void MasterServerInterface::Disconnect()
+{
+ m_Peer->Shutdown(200);
+ m_Peer->DetachPlugin(&m_DatabaseClient);
+}
+
+// Uses the game server peer
+void MasterServerInterface::UnregisterHost()
+{
+ if (GetNetworkManagerPtr())
+ {
+ if (GetNetworkManager().GetPeer()->IsConnected(m_MasterServerID))
+ m_HostDatabaseClient->RemoveRow(m_GameType.c_str(), 0, m_RowID, m_MasterServerID, false);
+ // Always detach DB plugin when running as a server, nothing will happen if it's not attached
+ if (GetNetworkManager().IsServer())
+ GetNetworkManager().GetPeer()->DetachPlugin(m_HostDatabaseClient);
+ }
+
+ // Reset some local variables
+ m_RowID = -1;
+ m_GameType = "";
+ m_HostName = "";
+ m_HostComment = "";
+ m_Registered = false;
+}
+
+std::vector<HostData> MasterServerInterface::PollHostList()
+{
+ return m_HostList;
+}
+
+void MasterServerInterface::ResetHostState()
+{
+ m_PendingRegister = false;
+ m_PendingHostUpdate = false;
+ m_Registered = false;
+}
+
+
+
+IMPLEMENT_CLASS (MasterServerInterface)
+GET_MANAGER (MasterServerInterface)
+GET_MANAGER_PTR (MasterServerInterface)
+#endif
diff --git a/Runtime/Network/MasterServerInterface.h b/Runtime/Network/MasterServerInterface.h
new file mode 100644
index 0000000..9f59c37
--- /dev/null
+++ b/Runtime/Network/MasterServerInterface.h
@@ -0,0 +1,88 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_NETWORK
+#include "Runtime/GameCode/Behaviour.h"
+#include "NetworkEnums.h"
+#include "External/RakNet/builds/include/RakPeerInterface.h"
+#include "External/RakNet/builds/include/LightweightDatabaseClient.h"
+#include "External/RakNet/builds/include/MessageIdentifiers.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+
+#include <vector>
+
+const int CELL_COUNT=8;
+
+
+
+class MasterServerInterface : public GlobalGameManager
+{
+public:
+
+ REGISTER_DERIVED_CLASS (MasterServerInterface, GlobalGameManager)
+ typedef std::vector<HostData> HostList;
+
+ MasterServerInterface(MemLabelId label, ObjectCreationMode mode);
+ // ~MasterServerInterface(); declared-by-macro
+
+ virtual void NetworkOnApplicationQuit();
+ virtual void NetworkUpdate();
+
+ void ClientConnect();
+ void ServerConnect();
+ bool CheckServerConnection();
+ void QueryHostList();
+ void QueryHostList(string gameType);
+ void ClearHostList();
+ void RegisterHost(string gameType, string gameName, string comment);
+ void SendHostUpdate();
+ void UnregisterHost();
+ void Disconnect();
+ HostList PollHostList();
+ void ProcessPacket(Packet *packet);
+ void ResetHostState();
+
+ string GetIPAddress() { return string(m_MasterServerID.ToString(false)); }
+ void SetIPAddress(std::string address) { m_MasterServerID.SetBinaryAddress(address.c_str()); }
+ int GetPort() { return m_MasterServerID.port; }
+ void SetPort(int port) { m_MasterServerID.port = port; }
+ SystemAddress& GetMasterServerID() { return m_MasterServerID; }
+
+ void SetUpdateRate(int rate) { m_UpdateRate = rate; }
+ int GetUpdateRate() { return m_UpdateRate; }
+
+ bool PopulateUpdate();
+ bool PopulateUpdate(string gameName, string comment);
+
+ void SetDedicatedServer(bool value) { m_IsDedicatedServer = value; };
+ bool GetDedicatedServer() { return m_IsDedicatedServer; };
+
+private:
+ void ResolveMasterServerAddress();
+
+ RakPeerInterface *m_Peer;
+ LightweightDatabaseClient m_DatabaseClient;
+ LightweightDatabaseClient *m_HostDatabaseClient;
+ bool m_PendingRegister;
+ bool m_PendingQuery;
+ bool m_PendingHostUpdate;
+ string m_GameType;
+ string m_HostName;
+ string m_HostComment;
+ HostList m_HostList;
+ unsigned int m_RowID;
+ bool m_Registered;
+ time_t m_LastHostUpdateTime;
+ SystemAddress m_MasterServerID;
+ char m_Version[3];
+ int m_UpdateRate;
+ DatabaseCellUpdate m_LastUpdate[CELL_COUNT];
+ bool m_IsDedicatedServer;
+ time_t m_ShutdownTimer;
+};
+
+MasterServerInterface* GetMasterServerInterfacePtr ();
+MasterServerInterface& GetMasterServerInterface ();
+
+#endif
diff --git a/Runtime/Network/MulticastSocket.cpp b/Runtime/Network/MulticastSocket.cpp
new file mode 100644
index 0000000..5553be5
--- /dev/null
+++ b/Runtime/Network/MulticastSocket.cpp
@@ -0,0 +1,224 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_SOCKETS
+#include "MulticastSocket.h"
+#include "SocketUtils.h"
+#if UNITY_EDITOR && UNITY_OSX
+#include <ifaddrs.h>
+#include <net/if.h>
+#endif
+
+MulticastSocket::MulticastSocket()
+: Socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
+, m_Bound(false)
+{
+ SetReuseAddress(true);
+#if UNITY_WINRT
+ m_initalised = false;
+#endif
+}
+
+#if !UNITY_WINRT
+
+bool MulticastSocket::Initialize(const char* group, unsigned short port, bool block)
+{
+ if (!SetBlocking(block))
+ return false;
+
+ SetupAddress(inet_addr(group), htons(port), &m_MulticastAddress);
+ return true;
+}
+
+bool MulticastSocket::Join()
+{
+ if (!m_Bound)
+ {
+ struct sockaddr_in listen_addr;
+ SetupAddress(htonl(INADDR_ANY), m_MulticastAddress.sin_port, &listen_addr);
+ if (CheckError(bind(m_SocketHandle, (struct sockaddr*) &listen_addr, sizeof(sockaddr_in)), "bind failed"))
+ return false;
+ m_Bound = true;
+ }
+
+#if UNITY_EDITOR
+ // Join the multicast group on all valid network addresses, so that a lower value routing metric adapter doesn't
+ // override a higher value routing metric adapter from processing the message (e.g. Ethernet and Wifi); This is
+ // primarily needed to fix the Profiler being able to auto-discover available players
+
+ // NOTE: Simply because we are able to join a multicast group doesn't mean that the port isn't blocked by a firewall.
+ // A common issue among Windows machines is when a network is marked as "Public" and when Unity first ran the user
+ // disallowed traffic on Public networks to come through the firewall.
+ return SetOptionForAllAddresses(IP_ADD_MEMBERSHIP, "unable to join multicast group");
+#else
+ struct ip_mreq mreq;
+ mreq.imr_multiaddr.s_addr = m_MulticastAddress.sin_addr.s_addr;
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ return !CheckError(SetSocketOption(IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)), "unable to join multicast group");
+#endif
+}
+
+bool MulticastSocket::Disband()
+{
+#if UNITY_EDITOR
+ return SetOptionForAllAddresses(IP_DROP_MEMBERSHIP, "unable to disband multicast group");
+#else
+ struct ip_mreq mreq;
+ mreq.imr_multiaddr.s_addr = m_MulticastAddress.sin_addr.s_addr;
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ return !CheckError(SetSocketOption(IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)), "unable to disband multicast group");
+#endif
+}
+
+#if UNITY_EDITOR
+bool MulticastSocket::SetOptionForAllAddresses(int option, const char* msg)
+{
+ bool error = false;
+ // Windows doesn't have the getifaddrs call and OSX doesn't return all addresses with an empty
+ // string passed to getaddrinfo, so it is necessary to have two approaches.
+#if UNITY_WIN
+#define IFADDR_T ADDRINFOA
+#define PIFADDR_T PADDRINFOA
+#define GET_PSOCKADDR(i) i->ai_addr
+#define GET_NEXT_IFADDR(i) i->ai_next
+#define FREE_IFADDRS(i) freeaddrinfo(i);
+
+ // Set the filter to return network addresses that can handle multicast (udp)
+ IFADDR_T hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_ADDRCONFIG;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ PIFADDR_T ifaddr = NULL;
+ // Empty string is sent to return all addresses registered to the local computer
+ getaddrinfo("", NULL, &hints, &ifaddr);
+#else
+#define IFADDR_T ifaddrs
+#define PIFADDR_T ifaddrs*
+#define GET_PSOCKADDR(i) i->ifa_addr
+#define GET_NEXT_IFADDR(i) i->ifa_next
+#define FREE_IFADDRS(i) freeifaddrs(i);
+
+ PIFADDR_T ifaddr = NULL;
+ if (getifaddrs(&ifaddr) != -1)
+#endif
+ {
+ PIFADDR_T ifa = ifaddr;
+ while (ifa)
+ {
+#if !UNITY_WIN
+ // We only care about IPv4 interfaces that support multicast
+ if (ifa->ifa_addr != NULL && ifa->ifa_addr->sa_family == AF_INET && (ifa->ifa_flags & IFF_MULTICAST) != 0)
+#endif
+ {
+ sockaddr_in* addr = (sockaddr_in*)GET_PSOCKADDR(ifa);
+ //char* address = inet_ntoa(addr->sin_addr);
+
+ struct ip_mreq mreq;
+ mreq.imr_multiaddr= m_MulticastAddress.sin_addr;
+ mreq.imr_interface = addr->sin_addr;
+
+ error |= CheckError(SetSocketOption(IPPROTO_IP, option, &mreq, sizeof(mreq)), msg);
+ }
+
+ ifa = GET_NEXT_IFADDR(ifa);
+ }
+ }
+ FREE_IFADDRS(ifaddr);
+
+#undef IFADDR_T
+#undef PIFADDR_T
+#undef GET_PSOCKADDR
+#undef GET_NEXT_IFADDR
+#undef FREE_IFADDRS
+
+ return !error;
+}
+#endif
+
+bool MulticastSocket::SetTTL(unsigned char ttl)
+{
+#if UNITY_XENON
+ return false;
+#else
+ return !CheckError(SetSocketOption(IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)), "failed to set TTL");
+#endif
+}
+
+bool MulticastSocket::SetLoop(bool loop)
+{
+#if UNITY_XENON
+ return false;
+#else
+ return !CheckError(SetSocketOption(IPPROTO_IP, IP_MULTICAST_LOOP, loop), "failed to set loop mode");
+#endif
+}
+
+bool MulticastSocket::SetBroadcast(bool broadcast)
+{
+ return !CheckError(SetSocketOption(SOL_SOCKET, SO_BROADCAST, broadcast), "failed to set broadcast mode");
+}
+
+int MulticastSocket::Send(const void* data, size_t data_len)
+{
+ SendUserData userData;
+ userData.dstAddr = (sockaddr*)&m_MulticastAddress;
+ userData.dstLen = sizeof(m_MulticastAddress);
+ return Socket::Send(data, data_len, &userData);
+}
+
+int MulticastSocket::Recv(void* data, size_t data_len, RecvUserData* userData)
+{
+ int result = Socket::Recv(data, data_len, userData);
+ return result;
+}
+
+#undef Error
+#undef SocketError
+
+// ---------------------------------------------------------------------------
+#if ENABLE_UNIT_TESTS && !UNITY_XENON
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "NetworkUtility.h"
+SUITE (MulticastSocketTests)
+{
+ struct SocketFixture
+ {
+ SocketFixture()
+ {
+ NetworkInitialize();
+ };
+
+ ~SocketFixture()
+ {
+ NetworkCleanup();
+ }
+ };
+#if !UNITY_XENON
+ TEST_FIXTURE(SocketFixture, Multicast)
+ {
+ char actual[10], expected[] = "foobar";
+
+ MulticastSocket sender;
+ CHECK(sender.Initialize("225.0.0.224", 54996));
+ CHECK(sender.SetTTL(0));
+ CHECK(sender.SetLoop(true));
+
+ MulticastSocket receiver;
+ CHECK(receiver.Initialize("225.0.0.224", 54996, true));
+ CHECK(receiver.Join());
+
+ CHECK_EQUAL(sizeof(expected), sender.Send(expected, sizeof(expected)));
+ CHECK_EQUAL(sizeof(expected), receiver.Recv(actual, sizeof(actual)));
+ CHECK_EQUAL(expected, actual);
+ CHECK(receiver.Disband());
+ }
+#endif
+}
+
+#endif //ENABLE_UNIT_TESTS
+
+#endif // !UNITY_WINRT
+
+#endif // ENABLE_SOCKETS
diff --git a/Runtime/Network/MulticastSocket.h b/Runtime/Network/MulticastSocket.h
new file mode 100644
index 0000000..0b99d60
--- /dev/null
+++ b/Runtime/Network/MulticastSocket.h
@@ -0,0 +1,59 @@
+#ifndef MULTICASTSOCKET_H
+#define MULTICASTSOCKET_H
+
+#if ENABLE_SOCKETS
+#include "Sockets.h"
+
+#if UNITY_WINRT
+namespace UnityPlayer
+{
+ [Windows::Foundation::Metadata::WebHostHidden]
+ public ref class MulticastSocketContext sealed
+ {
+ public:
+ MulticastSocketContext( Windows::Networking::HostName^ host, Platform::String^ port );
+
+ void OnSocketMessageReceived( Windows::Networking::Sockets::DatagramSocket^ dagSocket, Windows::Networking::Sockets::DatagramSocketMessageReceivedEventArgs^ args);
+ Windows::Networking::Sockets::DatagramSocket^ GetSocket() { return dagSocket; }
+ void Bind();
+ void Send(const Platform::Array<byte>^ dataToSend);
+ void SetLoop(bool loop);
+
+ private:
+ Windows::Networking::Sockets::DatagramSocket^ dagSocket;
+ Windows::Networking::HostName^ hostName;
+ Platform::String^ portNumber;
+ bool isLoopingBroadcast;
+ };
+}
+#endif // UNITY_WINRT
+
+class MulticastSocket : protected Socket
+{
+public:
+ MulticastSocket();
+
+ bool Initialize(const char* group, unsigned short port, bool block = false);
+ bool Join();
+ bool Disband();
+ bool SetBroadcast(bool broadcast);
+ bool SetTTL(unsigned char ttl);
+ bool SetLoop(bool loop);
+ int Send(const void* data, size_t data_len);
+ int Recv(void* data, size_t data_len, RecvUserData* userData = NULL);
+
+private:
+#if UNITY_EDITOR
+ bool SetOptionForAllAddresses(int option, const char* msg = NULL);
+#endif
+ bool m_Bound;
+ struct sockaddr_in m_MulticastAddress;
+#if UNITY_WINRT
+ UnityPlayer::MulticastSocketContext^ m_context;
+ bool m_initalised;
+#endif
+};
+
+
+#endif
+#endif
diff --git a/Runtime/Network/NetworkEnums.h b/Runtime/Network/NetworkEnums.h
new file mode 100644
index 0000000..55a9c13
--- /dev/null
+++ b/Runtime/Network/NetworkEnums.h
@@ -0,0 +1,196 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_NETWORK
+#include "Runtime/BaseClasses/MessageIdentifier.h"
+#include "External/RakNet/builds/include/BitStream.h"
+#include "External/RakNet/builds/include/MessageIdentifiers.h"
+#endif
+
+#include "NetworkViewID.h"
+#include "Runtime/Threads/Mutex.h"
+
+enum { /*kMaxOrderChannels = 32, */kDefaultChannel = 0, kInternalChannel = 0, kMaxGroups = 32 };
+enum { kUndefindedPlayerIndex = -1 };
+// Default timeout when disconnecting
+enum { kDefaultTimeout = 200 };
+
+//typedef int NetworkViewID;
+typedef SInt32 NetworkPlayer;
+
+struct RPCMsg
+{
+ std::string name; // name of the function
+ NetworkViewID viewID; // view id
+ int sender; // player index of the sender
+ int group;
+ RakNet::BitStream* stream;
+};
+
+struct PlayerTable
+{
+ int playerIndex;
+ // Index for checking what players have received initial state updates, must match the m_InitReceived flag in the network views.
+ unsigned int initIndex;
+ SystemAddress playerAddress;
+ UInt32 mayReceiveGroups;
+ UInt32 maySendGroups;
+ bool isDisconnected;
+ bool relayed;
+ std::string guid;
+};
+
+struct NetworkMessageInfo
+{
+ double timestamp;
+ int sender;
+ NetworkViewID viewID;
+};
+
+// Peer type
+enum {
+ kDisconnected = 0,
+ kServer = 1,
+ kClient = 2,
+};
+
+// RPC modes
+enum {
+/*
+ self
+ buffer
+ server
+ others
+ immediate
+*/
+ /// The first 2 bits are used for the target
+ kServerOnly = 0,
+ kOthers = 1,
+ kAll = 2,
+ kSpecificTarget = 3,
+ kTargetMask = 3,
+ // The third bit is used for buffering or not
+ kBufferRPCMask = 4,
+ kRPCModeNbBits = 3
+};
+
+inline UInt32 GetTargetMode (UInt32 mode)
+{
+ return mode & kTargetMask;
+}
+
+enum { kChannelCompressedBits = 5 }; //0-32
+
+// Debug level
+enum {
+ kImportantErrors = 0,
+ kInformational = 1,
+ kCompleteLog = 2
+};
+
+enum {
+ kPlayerIDBase = 10000000
+};
+
+enum NetworkSimulation {
+ kNoSimulation = 0,
+ kBroadband = 1,
+ kDSL = 2,
+ kISDN = 3,
+ kDialUp = 4
+};
+
+enum {
+ kAlreadyConnectedToOtherServer = -1,
+ kFailedToCreatedSocketOrThread = -2,
+ kIncorrectParameters = -3,
+ kEmptyConnectTarget = -4,
+ kInternalDirectConnectFailed = -5,
+ kUnkownGameType = -6,
+ kCannotConnectToGUIDWithoutNATEnabled = -7
+};
+
+// Connection Tester status enums
+enum {
+ kConnTestError = -2,
+ /// Test result undetermined, still in progress.
+ kConnTestUndetermined = -1,
+ /// Private IP address detected which cannot do NAT punchthrough.
+ kPrivateIPNoNATPunchthrough = 0,
+ /// Private IP address detected which can do NAT punchthrough.
+ kPrivateIPHasNATPunchThrough = 1,
+ /// Public IP address detected and game listen port is accessible to the internet.
+ kPublicIPIsConnectable = 2,
+ /// Public IP address detected but the port it's not connectable from the internet.
+ kPublicIPPortBlocked = 3,
+ /// Public IP address detected but server is not initialized and no port is listening.
+ kPublicIPNoServerStarted = 4,
+ /// Port-restricted NAT type, can do NAT punchthrough to everyone except symmetric.
+ kLimitedNATPunchthroughPortRestricted = 5,
+ /// Symmetric NAT type, cannot do NAT punchthrough to other symmetric types nor port restricted type.
+ kLimitedNATPunchthroughSymmetric = 6,
+ /// Full cone type, NAT punchthrough fully supported.
+ kNATpunchthroughFullCone = 7,
+ /// Address-restricted cone type, NAT punchthrough fully supported.
+ kNATpunchthroughAddressRestrictedCone = 8
+};
+
+enum {
+ kRegistrationFailedGameName = 0,
+ kRegistrationFailedGameType = 1,
+ kRegistrationFailedNoServer = 2,
+ kRegistrationSucceeded = 3,
+ kHostListReceived = 4
+};
+
+enum {
+ kConnTestTimeout = 60
+};
+
+// Network packet types
+enum {
+ ID_STATE_UPDATE
+
+#if ENABLE_NETWORK
+ = ID_USER_PACKET_ENUM // 127
+#endif
+ ,
+
+ ID_STATE_INITIAL,
+ ID_CLIENT_INIT,
+ ID_REMOVE_RPCS,
+ ID_REQUEST_CLIENT_INIT,
+ ID_PROXY_INIT_MESSAGE,
+ ID_PROXY_CLIENT_MESSAGE,
+ ID_PROXY_SERVER_MESSAGE,
+ ID_PROXY_MESSAGE,
+ ID_PROXY_SERVER_INIT,
+ // Master server specific network messages. This must be reflected in Tools/MasterServer/MasterServerMessages.h
+ ID_DATABASE_ROWID = 200,
+ ID_MASTERSERVER_REDIRECT,
+ ID_MASTERSERVER_MSG
+};
+
+// NetworkViewIDAllocator enums
+enum { kDefaultViewIDBatchSize = 50, kMinimumViewIDs = 100 };
+
+inline double TimestampToSeconds (RakNetTime time)
+{
+ return (double)time / 1000.0;
+}
+
+// Host data used with the master server
+struct HostData
+{
+ int useNat;
+ std::string gameType;
+ std::string gameName;
+ int connectedPlayers;
+ int playerLimit;
+ std::vector<std::string> IP;
+ int port;
+ bool passwordProtected;
+ std::string comment;
+ std::string guid;
+};
diff --git a/Runtime/Network/NetworkManager.cpp b/Runtime/Network/NetworkManager.cpp
new file mode 100644
index 0000000..0db2d83
--- /dev/null
+++ b/Runtime/Network/NetworkManager.cpp
@@ -0,0 +1,2825 @@
+#include "UnityPrefix.h"
+#include "NetworkManager.h"
+
+#if !UNITY_IPHONE && !UNITY_ANDROID && !UNITY_EDITOR
+// TODO: what's a sensible default for pepper? web player?
+NetworkReachability GetInternetReachability ()
+{
+ return ReachableViaLocalAreaNetwork;
+}
+#endif
+
+#if ENABLE_NETWORK
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoScriptCache.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Profiler/Profiler.h"
+
+#include "PackMonoRPC.h"
+#include "MasterServerInterface.h"
+#include "External/RakNet/builds/include/RakNetworkFactory.h"
+#include "External/RakNet/builds/include/RakPeerInterface.h"
+#include "External/RakNet/builds/include/RakNetStatistics.h"
+#include "External/RakNet/builds/include/RakSleep.h"
+#include "External/RakNet/builds/include/SocketLayer.h"
+#include "Runtime/Input/InputManager.h"
+#include "BitStreamPacker.h"
+#include "Runtime/GameCode/CloneObject.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Configuration/UnityConfigureVersion.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/AssetPipeline/AssetDatabase.h"
+#endif
+
+#define PACKET_LOGGER 0
+#if PACKET_LOGGER
+#include "External/RakNet/builds/include/PacketLogger.h"
+PacketLogger messageHandler;
+PacketLogger messageHandler2;
+#endif
+
+namespace {
+ const int kFacilitatorPort = 50005;
+ const int kConnectionTesterPort = 10737;
+ const int kProxyPort = 10746;
+}
+
+/*
+ * TODO: When players can take ownership of another players instantiated object then
+ * the Destory and RemoveRPC functions will no longer work. They remove
+ * based on player ID/prefix
+ *
+ * TODO: When sending timestamp from client to other clients the timestamp should be the timestamp from the client it was originally sent from not from the server.
+ * when it is forwarding the RPC.
+ *
+ * TODO: optimize sending of RPCs to only include timestamp if the receiver uses it via NetworkMessageInfo
+ */
+
+
+using namespace std;
+
+NetworkManager::NetworkManager(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_Peer = RakNetworkFactory::GetRakPeerInterface();
+ m_DebugLevel = kImportantErrors;
+ m_PeerType = kDisconnected;
+ m_Sendrate = 15.0F;
+ m_MessageQueueRunning = true;
+
+ m_Peer->RegisterAsRemoteProcedureCall("__RPCNetworkInstantiate", RPCNetworkInstantiate);
+ m_Peer->RegisterAsRemoteProcedureCall("__RPCReceiveViewIDBatch", RPCReceiveViewIDBatch);
+ m_Peer->RegisterAsRemoteProcedureCall("__RPCRequestViewIDBatch", RPCRequestViewIDBatch);
+ m_Peer->RegisterAsRemoteProcedureCall("__RPCNetworkDestroy", RPCNetworkDestroy);
+
+ m_Peer->SetOccasionalPing(true);
+ m_DoNAT = false;
+ m_MinimumAllocatableViewIDs = kMinimumViewIDs;
+ Disconnect(0);
+ m_ConnTester = NULL;
+
+ m_ServerAddress = UNASSIGNED_SYSTEM_ADDRESS;
+ m_ServerPassword = "";
+ m_FacilitatorID.binaryAddress = 0;
+ m_FacilitatorID.port = kFacilitatorPort;
+ m_ConnTesterAddress.binaryAddress = 0;
+ m_ConnTesterAddress.port = kConnectionTesterPort;
+ m_ConnStatus = kConnTestUndetermined;
+ m_MaxConnections = 0;
+ m_ProxyAddress.binaryAddress = 0;
+ m_ProxyAddress.port = kProxyPort;
+ m_UseProxy = false;
+ m_ProxyPassword = "";
+#if PACKET_LOGGER
+ m_Peer->AttachPlugin(&messageHandler);
+ messageHandler.LogHeader();
+#endif
+}
+
+NetworkManager::~NetworkManager()
+{
+ RakNetworkFactory::DestroyRakPeerInterface(m_Peer);
+
+ while (!m_PingQueue.empty())
+ {
+ m_PingQueue.back()->Release();
+ m_PingQueue.pop();
+ }
+}
+
+void NetworkManager::AddNetworkView (ListNode<NetworkView>& s)
+{
+ m_Sources.push_back(s);
+}
+
+void NetworkManager::AddAllNetworkView (ListNode<NetworkView>& s)
+{
+ m_AllSources.push_back(s);
+}
+
+void NetworkManager::AddNonSyncNetworkView (ListNode<NetworkView>& s)
+{
+ m_NonSyncSources.push_back(s);
+}
+
+void NetworkManager::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad(awakeMode);
+ #if UNITY_EDITOR
+ AssertIf (!m_AssetToPrefab.empty());
+ #endif
+
+ m_PrefabToAsset.clear();
+ for (AssetToPrefab::iterator i=m_AssetToPrefab.begin();i != m_AssetToPrefab.end();i++)
+ {
+ m_PrefabToAsset.insert(make_pair (i->second, i->first));
+ }
+}
+
+void NetworkManager::SetAssetToPrefab (const std::map<UnityGUID, PPtr<GameObject> >& mapping)
+{
+ m_AssetToPrefab = mapping;
+ m_PrefabToAsset.clear();
+ for (AssetToPrefab::iterator i=m_AssetToPrefab.begin();i != m_AssetToPrefab.end();i++)
+ m_PrefabToAsset.insert(make_pair (i->second, i->first));
+}
+
+void NetworkManager::NetworkOnApplicationQuit()
+{
+// Don't tear down network if Application.CancelQuit has been called from user scripting (only for standalones)
+#if !WEBPLUG && !UNITY_EDITOR
+ if (GetInputManager().ShouldQuit()) {
+#endif
+ // Disconnect all connected peers, no wait interval, ordering channel 0
+ Disconnect(100);
+
+ m_MinimumAllocatableViewIDs = kMinimumViewIDs;
+ SetUseNat(false);
+ m_FacilitatorID.binaryAddress = 0;
+ m_FacilitatorID.port = kFacilitatorPort;
+ m_ConnTesterAddress.binaryAddress = 0;
+ m_ConnTesterAddress.port = kConnectionTesterPort;
+ delete m_ConnTester;
+ m_ConnTester = NULL;
+ m_ConnStatus = kConnTestUndetermined;
+ m_MaxConnections = 0;
+ m_PingThread.WaitForExit();
+ m_ProxyAddress.binaryAddress = 0;
+ m_ProxyAddress.port = kProxyPort;
+ m_UseProxy = false;
+ m_ProxyPassword = "";
+#if !WEBPLUG && !UNITY_EDITOR
+ }
+#endif
+}
+
+void NetworkManager::InitializeSecurity()
+{
+ if (m_Peer->IsActive())
+ {
+ ErrorString("You may not be connected when initializing security layer.");
+ return;
+ }
+
+ m_Peer->InitializeSecurity(0,0,0,0);
+}
+
+int NetworkManager::InitializeServer(int connections, int listenPort, bool useNat)
+{
+ Disconnect(kDefaultTimeout, false);
+
+ m_MaxConnections = connections;
+ SetUseNat(useNat);
+
+ SocketDescriptor sd(listenPort,0);
+ // Add two extra connections for the master server and facilitator
+ if (!m_Peer->Startup(connections+2, 1, &sd, 1))
+ {
+ ErrorString("Failed to initialize network interface. Is the listen port already in use?");
+ return kFailedToCreatedSocketOrThread;
+ }
+ // Hide the extra connections again.
+ m_Peer->SetMaximumIncomingConnections(connections);
+
+ m_PlayerID = 0;
+ m_HighestPlayerID = 0;
+ m_SendingEnabled = 0xFFFFFFFF;
+ m_ReceivedInitialState = true;
+
+ m_NetworkViewIDAllocator.Clear(kDefaultViewIDBatchSize, m_MinimumAllocatableViewIDs, m_PlayerID, kUndefindedPlayerIndex);
+ m_NetworkViewIDAllocator.FeedAvailableBatchOnServer(m_NetworkViewIDAllocator.AllocateBatch(m_PlayerID));
+
+ AssertIf(!m_Players.empty());
+
+ m_PeerType = kServer;
+ NetworkInfo(NULL, "Running as server. Player ID is 0.");
+
+ // When running as server there is no need to go through the facilitator connection routine again. You just need to stay connected.
+ if (m_DoNAT && !m_Peer->IsConnected(m_FacilitatorID))
+ {
+ GetFacilitatorAddress(true);
+
+ // NOTE: At the moment the server peer interface maintains a constant connection to the master server for keeping the NAT
+ // port open. This isn't neccessary after clients have connected as they will maintain the port. However, if all clients disconnect for
+ // a while, the port will close sooner or later. Then the master server needs to have another port for the next client. Another
+ // reason for keeping the connection persistant is that when a new client connects and needs NAT punch through the server must already
+ // have a connection open or else the connection fails. Keeping it persistant is the most easy solution.
+ if (!m_Peer->Connect(m_FacilitatorID.ToString(false),m_FacilitatorID.port,0,0))
+ {
+ ErrorString("Failed to connect to NAT facilitator\n");
+ }
+ }
+
+ // TODO: Here we assume a connection to the proxy server cannot already be established. Could this happen?
+ if (m_UseProxy)
+ {
+ ResolveProxyAddress();
+ if (!m_Peer->Connect(m_ProxyAddress.ToString(false), m_ProxyAddress.port, m_ProxyPassword.c_str(), m_ProxyPassword.size(),0))
+ {
+ ErrorString(Format("Failed to connect to proxy server at %s.", m_ProxyAddress.ToString()));
+ }
+ }
+ else
+ {
+ SendToAllNetworkViews(kServerInitialized, m_PlayerID);
+ }
+
+ return 0;
+}
+
+// Check if the IP addresses supplied are connectable and connect to the first successful IP.
+int NetworkManager::Connect(std::vector<string> IPs, int remotePort, int listenPort, const std::string& password)
+{
+ if (IPs.size() == 1)
+ {
+ Connect(IPs[0].c_str(), remotePort, listenPort, password);
+ return 0;
+ }
+
+ if (IPs.size() == 0)
+ {
+ // Must prevent this or raknet will crash when an empty address is used
+ ErrorString("Empty host IP list given in Connect\n");
+ return kEmptyConnectTarget;
+ }
+
+ if (!password.empty()) m_ServerPassword = password;
+
+ for (int i = 0; i < IPs.size(); i++)
+ {
+ if (IPs[i] != "")
+ {
+ if (!m_Peer->IsActive())
+ {
+ SocketDescriptor sd(listenPort,0);
+ if (!m_Peer->Startup(2, 1, &sd, 1))
+ {
+ ErrorString("Failed to initialize network connection before connecting.");
+ return kFailedToCreatedSocketOrThread;
+ }
+ }
+ // Do request a reply if the system is not accepting connections, then can show the error message
+ m_Peer->Ping(IPs[i].c_str(), remotePort, false);
+ m_ConnectingAfterPing = true;
+ m_PingConnectTimestamp = time(0);
+ }
+ }
+ return 0;
+}
+
+int NetworkManager::Connect(std::string IP, int remotePort, const std::string& password)
+{
+ m_ReceivedInitialState = true;
+ SetUseNat(false);
+
+ // Connect to proxy which will from then on relay all server communication.
+ // A proxy behind NAT is not supported, the UseNAT property is ignored here but
+ // it applies to the game server the proxy server will connect to
+ if (m_UseProxy)
+ {
+ ResolveProxyAddress();
+ if (!m_Peer->Connect(m_ProxyAddress.ToString(false), m_ProxyAddress.port, m_ProxyPassword.c_str(), m_ProxyPassword.size(),0))
+ {
+ ErrorString(Format("Failed to connect to proxy server at %s\n", m_ProxyAddress.ToString()));
+ return kFailedToCreatedSocketOrThread;
+ }
+ else
+ {
+ NetworkInfo(NULL, "Sent connect request to proxy at %s\n", m_ProxyAddress.ToString());
+ }
+ // Memorize server address as we will need it later when the proxy connection is established
+ m_ServerAddress.SetBinaryAddress(IP.c_str());
+ m_ServerAddress.port = (unsigned)remotePort;
+ if (!password.empty())
+ m_ServerPassword = password;
+ }
+ else
+ {
+ if (!m_Peer->Connect(IP.c_str(), remotePort, password.c_str(), password.size()))
+ {
+ ErrorString("Failed to connect. This is caused by an incorrect parameters, internal error or too many existing connections.");
+ return kIncorrectParameters;
+ }
+ }
+ AssertIf(!m_Players.empty());
+
+ NetworkInfo(NULL, "Running as client. No player ID set yet.");
+
+ return 0;
+}
+
+void NetworkManager::ResolveProxyAddress()
+{
+ ResolveAddress(m_ProxyAddress, "proxy.unity3d.com", "proxybeta.unity3d.com",
+ "Cannot resolve proxy address, make sure you are connected to the internet before connecting to a server.");
+}
+
+// NOTE: If this client is supposed to accept connections then SetMaximumIncomingConnections
+// must be set.
+int NetworkManager::Connect(std::string IP, int remotePort, int listenPort, const std::string& password)
+{
+ Disconnect(kDefaultTimeout);
+
+ // Connect returns immediately on a successful connection ATTEMPT. IsConnected() tells you if the connection actually succeded.
+ // The network message ID_UNABLE_TO_CONNECT_TO_REMOTE_HOST if the remote hosts responds (closed port etc.)
+ // NOTE: At the moment client connections are limited to only two connections. In p2p mode each peer will need a seperate connection
+ SocketDescriptor sd(listenPort,0);
+ if (!m_Peer->Startup(2, 1, &sd, 1))
+ {
+ ErrorString("Failed to initialize network connection before connecting.");
+ return kFailedToCreatedSocketOrThread;
+ }
+
+ return Connect(IP, remotePort, password);
+}
+
+int NetworkManager::Connect(RakNetGUID serverGUID, int listenPort, const std::string& password)
+{
+ SetUseNat(true);
+ Disconnect(kDefaultTimeout);
+
+ // Connect returns immediately on a successful connection ATTEMPT. IsConnected() tells you if the connection actually succeded.
+ // The network message ID_UNABLE_TO_CONNECT_TO_REMOTE_HOST if the remote hosts responds (closed port etc.)
+ // NOTE: At the moment client connections are limited to only two connections. In p2p mode each peer will need a seperate connection
+ SocketDescriptor sd(listenPort,0);
+ if (!m_Peer->Startup(2, 1, &sd, 1))
+ {
+ ErrorString("Failed to initialize network connection before connecting.");
+ return kFailedToCreatedSocketOrThread;
+ }
+
+ ResolveFacilitatorAddress();
+
+ if (!m_Peer->Connect(m_FacilitatorID.ToString(false), m_FacilitatorID.port,0,0))
+ {
+ ErrorString(Format("Failed to connect to NAT facilitator at %s\n", m_FacilitatorID.ToString()));
+ return kFailedToCreatedSocketOrThread;
+ }
+ else
+ {
+ NetworkInfo(NULL, "Sent connect request to facilitator at %s\n", m_FacilitatorID.ToString());
+ }
+
+ if (!password.empty()) m_ServerPassword = password;
+
+ m_ServerGUID = serverGUID;
+
+ return 0;
+}
+
+// The CloseConnection function allows you to disconnect a specific playerID
+void NetworkManager::Disconnect(int timeout, bool resetParams)
+{
+ if (GetMasterServerInterfacePtr())
+ {
+ GetMasterServerInterface().UnregisterHost();
+ GetMasterServerInterface().Disconnect();
+ }
+ m_Peer->Shutdown(timeout);
+
+ if (IsServer () || IsClient())
+ SendToAllNetworkViews(kDisconnectedFromServer, ID_DISCONNECTION_NOTIFICATION);
+
+ if (resetParams)
+ {
+ m_Peer->DisableSecurity();
+ SetIncomingPassword("");
+ }
+
+ m_Players.clear();
+ m_PeerType = kDisconnected;
+ m_MessageQueueRunning = true;
+ m_SendingEnabled = 0;
+ m_LevelPrefix = 0;
+
+ for (list<RPCMsg>::iterator irpc = m_RPCBuffer.begin(); irpc != m_RPCBuffer.end(); ++irpc )
+ {
+ RPCMsg& rpc = *irpc;
+ delete rpc.stream;
+ }
+ m_RPCBuffer.clear();
+
+ m_ServerAddress = UNASSIGNED_SYSTEM_ADDRESS;
+ m_ServerPassword = "";
+ m_HighestPlayerID = 0;
+ m_PlayerID = -1;
+ m_LastSendTime = -1.0F;
+ m_ConnectingAfterPing = false;
+ m_UsedInitIndices.clear();
+
+ for( NetworkViewIterator iview = m_AllSources.begin(); iview != m_AllSources.end(); ++iview ) {
+ NetworkView& view = **iview;
+ view.ClearInitStateAndOwner();
+ }
+}
+
+void NetworkManager::CloseConnection(int target, bool sendDisconnect)
+{
+ SystemAddress address = GetSystemAddressFromIndex(target);
+ if (address != UNASSIGNED_SYSTEM_ADDRESS)
+ m_Peer->CloseConnection(address, sendDisconnect, kInternalChannel);
+ else
+ {
+ ErrorString("Couldn't close connection because the player is not connected.");
+ }
+}
+
+PROFILER_INFORMATION(gNetworkUpdateProfile, "Network.Update", kProfilerNetwork)
+
+void NetworkManager::NetworkUpdate()
+{
+ PROFILER_AUTO(gNetworkUpdateProfile, NULL)
+
+ m_Packet = NULL;
+ if (m_MessageQueueRunning)
+ m_Packet = m_Peer->ReceiveIgnoreRPC(); // Receive whole message from peer
+
+ if (m_ConnectingAfterPing && time(0) - m_PingConnectTimestamp > 5)
+ {
+ m_ConnectingAfterPing = false;
+ NetworkError (NULL, "Unable to connect internally to NAT target(s), no response.");
+ SendToAllNetworkViews(kConnectionAttemptFailed, kInternalDirectConnectFailed);
+ }
+
+ // Destroy connection tester after the timeout (test should be over) or if an error occured
+ if (m_ConnTester)
+ {
+ m_ConnStatus = m_ConnTester->Update();
+ // ATM -2 = timeout || error
+ /*if (m_ConnStatus == -2)
+ {
+ delete m_ConnTester;
+ m_ConnTester = NULL;
+ }*/
+ }
+
+ // Check if there are any pending pings
+ if (!m_PingQueue.empty())
+ {
+ if (!m_PingThread.IsRunning())
+ {
+ m_PingThread.Run(&PingImpl, (void*)m_PingQueue.front(), DEFAULT_UNITY_THREAD_STACK_SIZE, 2);
+ m_PingQueue.pop();
+ }
+ }
+
+ while (m_Packet)
+ {
+ unsigned char packetIdentifier = m_Packet->data[0];
+ if (packetIdentifier == ID_TIMESTAMP && m_Packet->length > sizeof(unsigned char) + sizeof (RakNetTime))
+ {
+ packetIdentifier = m_Packet->data[sizeof(unsigned char) + sizeof(RakNetTime)];
+ }
+
+ bool fromMasterServer = m_Packet->systemAddress == GetMasterServerInterface().GetMasterServerID();
+ if (fromMasterServer)
+ GetMasterServerInterface().ProcessPacket(m_Packet);
+ else
+ ProcessPacket(packetIdentifier);
+
+ m_Peer->DeallocatePacket(m_Packet); // Ditch message
+
+ if (m_MessageQueueRunning)
+ m_Packet = m_Peer->ReceiveIgnoreRPC(); // Check if there are other messages queued
+ else
+ m_Packet = NULL;
+ }
+ m_Packet = NULL;
+
+ if (!m_Peer->IsActive())
+ return;
+
+ float realtime = GetTimeManager().GetRealtime();
+ if (realtime > m_LastSendTime + 1.0F / m_Sendrate && GetConnectionCount() >= 1)
+ {
+ // Send updates
+ for (NetworkViewIterator i = m_Sources.begin(); i != m_Sources.end(); i++) {
+ NetworkView *view = i->GetData();
+
+ // Are we allowed to send?
+ if (m_SendingEnabled & (1 << view->GetGroup()))
+ {
+ // If running as server and one or more client is connected
+ if ( IsServer() )
+ {
+ view->SendToAllButOwner();
+ }
+ // If running as client and the views owner player address matches this one then send to server
+ else if (IsClient() && m_Peer->GetInternalID() == view->GetOwnerAddress())
+ {
+ view->Send(m_ServerAddress, false);
+ }
+ }
+ }
+
+ m_LastSendTime = realtime;
+ }
+
+ RakSleep(0);
+}
+
+void NetworkManager::ProcessPacket(unsigned char packetIdentifier)
+{
+ switch (packetIdentifier)
+ {
+ case ID_CONNECTION_REQUEST_ACCEPTED:
+ {
+ bool fromFacilitator = m_Packet->systemAddress == m_FacilitatorID;
+ bool fromProxyServer = m_Packet->systemAddress == m_ProxyAddress;
+
+ // A client doing NAT punch through to game server with help from facilitator
+ if (m_DoNAT && fromFacilitator && !IsServer())
+ {
+ NetworkInfo(NULL, "Connected to facilitator at %s\n",m_Packet->systemAddress.ToString());
+ char tmp[32];
+ strcpy(tmp, m_FacilitatorID.ToString());
+ NetworkInfo(NULL, "Doing NAT punch through to %s using %s\n", m_ServerGUID.ToString(), tmp);
+ m_NatPunchthrough.OpenNAT(m_ServerGUID, m_FacilitatorID);
+ }
+ else if (fromFacilitator && IsServer())
+ {
+ NetworkInfo(NULL, "Connected to facilitator at %s\n",m_Packet->systemAddress.ToString());
+ }
+ else if (fromProxyServer && IsServer())
+ {
+ if (!m_UseProxy)
+ {
+ ErrorString("Connected to proxy server but proxy support not enabled.");
+ return;
+ }
+ m_BitStream.Reset();
+ m_BitStream.Write((unsigned char) (ID_PROXY_SERVER_INIT));
+ m_BitStream.Write(1); // Proxy protocol version
+ if (!m_Peer->Send(&m_BitStream, HIGH_PRIORITY, RELIABLE_ORDERED, kDefaultChannel, m_ProxyAddress, false))
+ {
+ ErrorString("Failed to send server init to proxy server.");
+ }
+ else
+ {
+ NetworkInfo(NULL, "Sending init req. to proxy server at %s", m_ProxyAddress.ToString());
+ }
+ }
+ // Client connected to server (proxy or game)
+ else
+ {
+ SystemAddress target = m_Packet->systemAddress;
+ m_BitStream.Reset();
+ if (m_UseProxy)
+ {
+ m_BitStream.Write((unsigned char) (ID_PROXY_INIT_MESSAGE));
+ m_BitStream.Write(1); // Proxy protocol version
+ m_BitStream.Write(m_ServerAddress);
+ if (!m_ServerPassword.empty())
+ {
+ m_BitStream.Write1();
+ m_BitStream.Write(m_ServerPassword.size());
+ m_BitStream.Write(m_ServerPassword.c_str(), m_ServerPassword.size());
+ }
+ else
+ {
+ m_BitStream.Write0();
+ }
+ m_BitStream.Write(m_DoNAT);
+ m_BitStream.Write(1); // Network protocol version
+ target = m_ProxyAddress;
+ NetworkInfo(NULL, "Sending init req. to %s, relayed through proxy", m_ServerAddress.ToString());
+ }
+ else
+ {
+ // Specifically request init from server
+ m_BitStream.Write((unsigned char) (ID_REQUEST_CLIENT_INIT));
+ m_BitStream.Write((int)1); // Network protocol version
+ m_ServerAddress = m_Packet->systemAddress; // Record server address
+ }
+ if (!m_Peer->Send(&m_BitStream, HIGH_PRIORITY, RELIABLE_ORDERED, kDefaultChannel, target, false))
+ ErrorString("Failed to send client init to server.");
+ NetworkInfo(NULL, "Connected to %s\n",m_Packet->systemAddress.ToString());
+ }
+ break;
+ }
+ case ID_NO_FREE_INCOMING_CONNECTIONS:
+ ErrorString("The system we attempted to connect to is not accepting new connections.");
+ SendToAllNetworkViews(kConnectionAttemptFailed, ID_NO_FREE_INCOMING_CONNECTIONS);
+ break;
+ case ID_RSA_PUBLIC_KEY_MISMATCH:
+ ErrorString("We preset an RSA public key which does not match what the system we connected to is using.");
+ SendToAllNetworkViews(kConnectionAttemptFailed, ID_RSA_PUBLIC_KEY_MISMATCH);
+ break;
+ case ID_INVALID_PASSWORD:
+ {
+ if (m_Packet->systemAddress == m_ProxyAddress)
+ {
+ ErrorString("The proxy server is using a password and has refused our connection because we did not set it correctly.");
+ }
+ else
+ {
+ ErrorString("The remote system is using a password and has refused our connection because we did not set the correct password.");
+ }
+ SendToAllNetworkViews(kConnectionAttemptFailed, ID_INVALID_PASSWORD);
+ break;
+ }
+ case ID_CONNECTION_ATTEMPT_FAILED:
+ {
+ ErrorString(Format("The connection request to %s failed. Are you sure the server can be connected to?", m_Packet->systemAddress.ToString()));
+ SendToAllNetworkViews(kConnectionAttemptFailed, ID_CONNECTION_ATTEMPT_FAILED);
+ break;
+ }
+ case ID_ALREADY_CONNECTED:
+ {
+ NetworkError(NULL, "Failed to connect to %s because this system is already connected. This can occur when the network connection is disconnected too quickly for the remote system to receive the disconnect notification, for example when using Network.Disconnect(0).", m_Packet->systemAddress.ToString());
+ SendToAllNetworkViews(kConnectionAttemptFailed, ID_ALREADY_CONNECTED);
+ break;
+ }
+ case ID_CONNECTION_BANNED:
+ {
+ ErrorString("We are banned from the system we attempted to connect to.");
+ SendToAllNetworkViews(kConnectionAttemptFailed, ID_CONNECTION_BANNED);
+ break;
+ }
+ case ID_NEW_INCOMING_CONNECTION:
+ {
+ // Happens during public IP firewall test
+ if (m_Packet->systemAddress == m_ConnTesterAddress)
+ {
+ if (m_ConnTester)
+ {
+ m_ConnStatus = kPublicIPIsConnectable;
+ m_ConnTester->ReportTestSucceeded();
+ }
+ return;
+ }
+
+ if (!IsServer())
+ {
+ m_Peer->CloseConnection(m_Packet->systemAddress, true);
+ NetworkError(NULL, "New incoming connection but not a server.");
+ return;
+ }
+
+ NetworkInfo(NULL, "New connection established (%s)", m_Packet->systemAddress.ToString());
+ break;
+ }
+ case ID_REQUEST_CLIENT_INIT:
+ {
+ MsgNewConnection();
+ break;
+ }
+ // TODO: Should we differentiate between these? One is expected, the other is not (important for integration tests)
+ case ID_DISCONNECTION_NOTIFICATION:
+ case ID_CONNECTION_LOST:
+ {
+ // When master server ID has been changed we need to ignore the disconnect from the old master server
+ if (m_Packet->systemAddress == m_OldMasterServerID && IsServer())
+ {
+ // Do nothing
+ }
+ // Same with the old facilitator
+ else if (m_Packet->systemAddress == m_OldFacilitatorID && IsServer())
+ {
+ // Do nothing
+ }
+ else if (m_Packet->systemAddress == m_ConnTesterAddress)
+ {
+ // Do nothing
+ }
+ // If connection was lost with the NAT facilitator we should reconnect as it must be persistent for now.
+ // If it is not persistent then the connections must be set up every time a client wants to connect (and at the moment there
+ // is no way of knowing when that is).
+ else if (m_Packet->systemAddress == m_FacilitatorID && IsServer())
+ {
+ // No need to resolve again as the above condition will never happen if the address in unresolved
+ if (!m_Peer->Connect(m_FacilitatorID.ToString(false),m_FacilitatorID.port,0,0))
+ {
+ ErrorString(Format("Failed to reconnect to NAT facilitator at %s\n", m_FacilitatorID.ToString()));
+ return;
+ }
+ else
+ {
+ LogString("Connection lost to NAT facilitator, reconnecting.\n");
+ }
+ }
+ else if (m_Packet->systemAddress == m_ProxyAddress)
+ {
+ NetworkLog(NULL, "Lost connection to proxy server at %s", m_Packet->systemAddress.ToString());
+ // If we are a client then either the proxy server connection was lost or the game server connection
+ if (IsClient())
+ {
+ //SendToAllNetworkViews(kConnectionAttemptFailed, ID_CONNECTION_ATTEMPT_FAILED);
+ SendToAllNetworkViews(kDisconnectedFromServer, m_Packet->data[0]);
+ }
+ }
+ else if (IsServer())
+ {
+ NetworkInfo(NULL, "A client has disconnected from this server. Player ID %d, IP %s", GetIndexFromSystemAddress(m_Packet->systemAddress), m_Packet->systemAddress.ToString());
+ MsgClientDidDisconnect();
+ }
+ else
+ {
+ LogString("The server has disconnected from the client. Most likely the server has shut down.");
+ ClientConnectionDisconnected(m_Packet->data[0]);
+ }
+ break;
+ }
+ case ID_STATE_UPDATE:
+ case ID_STATE_INITIAL:
+ // Normal state update, reset bitstream accordingly
+ m_BitStream.Reset();
+ m_BitStream.Write((char*)m_Packet->data, m_Packet->length);
+ MsgStateUpdate(m_Packet->systemAddress);
+ break;
+ case ID_CLIENT_INIT:
+ MsgClientInit();
+ break;
+ case ID_MODIFIED_PACKET:
+ ErrorString("A packet has been tampered with in transit.");
+ break;
+ case ID_REMOVE_RPCS:
+ MsgRemoveRPCs();
+ break;
+ // TODO: Implement other error codes: ID_NAT_TARGET_UNRESPONSIVE ID_NAT_ALREADY_IN_PROGRESS
+ case ID_NAT_PUNCHTHROUGH_SUCCEEDED:
+ {
+ unsigned char weAreTheSender = m_Packet->data[1];
+ if (weAreTheSender)
+ {
+ NetworkInfo(NULL, "Successfully performed NAT punchthrough to %s\n",m_Packet->systemAddress.ToString());
+ m_ServerAddress = m_Packet->systemAddress;
+ // TODO: Maybe we should be going through Connect just so we always go through that function.
+ if (!m_Peer->Connect(m_ServerAddress.ToString(true), m_ServerAddress.port, m_ServerPassword.c_str(), m_ServerPassword.size()))
+ {
+ ErrorString("Failed to connect. This is caused by an incorrect parameters, internal error or too many existing connections.");
+ }
+ }
+ else
+ {
+ NetworkInfo(NULL, "Successfully accepted NAT punchthrough connection from %s\n",m_Packet->systemAddress.ToString());
+ }
+ break;
+ }
+ case ID_NAT_TARGET_NOT_CONNECTED:
+ {
+ RakNetGUID remoteGuid;
+ SystemAddress systemAddress;
+ RakNet::BitStream b(m_Packet->data, m_Packet->length, false);
+ b.IgnoreBits(8); // Ignore the ID_...
+ b.Read(remoteGuid);
+
+ // TODO: Lookup IP/port of the guid we were trying to connect to
+
+ // SystemAddress.ToString() cannot be used twice in the same line because it uses a static char array
+ ErrorString(Format("NAT target %s not connected to NAT facilitator %s\n", remoteGuid.ToString(), m_Packet->systemAddress.ToString()));
+ SendToAllNetworkViews(kConnectionAttemptFailed, ID_NAT_TARGET_NOT_CONNECTED);
+ break;
+ }
+ case ID_NAT_CONNECTION_TO_TARGET_LOST:
+ {
+ RakNetGUID remoteGuid;
+ SystemAddress systemAddress;
+ RakNet::BitStream b(m_Packet->data, m_Packet->length, false);
+ b.IgnoreBits(8); // Ignore the ID_...
+ b.Read(remoteGuid);
+
+ // TODO: Lookup IP/port of the guid we were trying to connect to
+
+ ErrorString(Format("Connection to target %s lost\n", systemAddress.ToString()));
+ SendToAllNetworkViews(kConnectionAttemptFailed, ID_NAT_CONNECTION_TO_TARGET_LOST);
+ break;
+ }
+ case ID_NAT_PUNCHTHROUGH_FAILED:
+ {
+ bool isConnecting = false;
+ RakNet::BitStream b(m_Packet->data, m_Packet->length, false);
+ b.IgnoreBits(8); // Ignore the ID_...
+ b.Read(isConnecting);
+ if (isConnecting)
+ {
+ ErrorString(Format("Initiating NAT punchthrough attempt to target %s failed\n", m_Packet->guid.ToString()));
+ SendToAllNetworkViews(kConnectionAttemptFailed, ID_NAT_PUNCHTHROUGH_FAILED);
+ }
+ else
+ {
+ ErrorString(Format("Receiving NAT punchthrough attempt from target %s failed\n", m_Packet->guid.ToString()));
+ // No need to report the failed connection attempt on the receiver
+ //SendToAllNetworkViews(kConnectionAttemptFailed, ID_NAT_PUNCHTHROUGH_FAILED);
+ }
+ break;
+ }
+ case ID_PONG:
+ {
+ LogString(Format("Received pong from %s", m_Packet->systemAddress.ToString()));
+ if (m_ConnectingAfterPing)
+ {
+ m_ConnectingAfterPing = false;
+ Connect(m_Packet->systemAddress.ToString(false), m_Packet->systemAddress.port, m_ServerPassword);
+ }
+ break;
+ }
+ case ID_PING:
+ // Do nothing, this should be handled internally by RakNet (actually shouldn't be here)
+ break;
+ case ID_RPC:
+ {
+ char *message = NULL;
+ message = m_Peer->HandleRPCPacket( ( char* ) m_Packet->data, m_Packet->length, m_Packet->systemAddress );
+ if (message != NULL)
+ {
+ NetworkError(NULL, "%s", message);
+ }
+ break;
+ }
+ case ID_PROXY_SERVER_INIT:
+ {
+ int proxyVer;
+ unsigned short relayPort;
+ RakNet::BitStream b(m_Packet->data, m_Packet->length, false);
+ b.IgnoreBits(8);
+ b.Read(proxyVer); // Proxy protocol version
+ b.Read(relayPort);
+ NetworkLog(NULL, "Proxy version %d", proxyVer);
+
+ if (relayPort > 0)
+ {
+ m_RelayPort = relayPort;
+ NetworkLog(NULL, "Successfully initialized relay service from proxy server. Using port %u.", relayPort);
+ SendToAllNetworkViews(kServerInitialized, -2);
+ }
+ else if (relayPort == 0)
+ {
+ NetworkLog(NULL, "Failed to initialize relay service from proxy server. No available ports reported.");
+ SendToAllNetworkViews(kServerInitialized, -1);
+ }
+ else
+ {
+ NetworkLog(NULL, "Failed to initialize relay service from proxy server. Unkown error.");
+ SendToAllNetworkViews(kServerInitialized, -1);
+ }
+ break;
+ }
+ case ID_PROXY_MESSAGE:
+ {
+ SystemAddress senderAddress;
+ unsigned char messageID;
+ int proxyVer = 0;
+ m_BitStream.Reset();
+ m_BitStream.Write((char*)m_Packet->data, m_Packet->length);
+ m_BitStream.IgnoreBits(8);
+
+ m_BitStream.Read(senderAddress);
+ int proxiedMessageID = sizeof(MessageID) + SystemAddress::size();
+ messageID = m_Packet->data[proxiedMessageID];
+
+ // Record the proxy system address for future reference
+ // TODO: With this method, only one proxy is allowed. Enforce it? Proxy address could be added to player structure.
+ m_ProxyAddress = m_Packet->systemAddress;
+
+ // If its a time stamp then we need to dig deeper and find out what it really is.
+ // If there is a timestamp embedded, the real ID is at:
+ if (messageID == ID_TIMESTAMP)
+ {
+ int timestampedProxiedMessageID = proxiedMessageID + sizeof(MessageID) + sizeof(RakNetTime);
+ messageID = m_Packet->data[timestampedProxiedMessageID];
+ }
+
+ switch (messageID)
+ {
+ case ID_DISCONNECTION_NOTIFICATION:
+ {
+ int playerID = GetIndexFromSystemAddress(senderAddress);
+ NetworkInfo(NULL, "A proxied client has disconnected from this server. Player ID %d, IP %s", playerID, senderAddress.ToString());
+ MsgClientDidDisconnect(senderAddress);
+ break;
+ }
+ break;
+ case ID_REQUEST_CLIENT_INIT:
+ m_BitStream.IgnoreBits(8);
+ m_BitStream.Read(proxyVer); // Proxy protocol version
+ NetworkLog(NULL, "Proxy version %d", proxyVer);
+ MsgNewConnection(senderAddress);
+ break;
+ // State update from client
+ case ID_STATE_INITIAL:
+ case ID_STATE_UPDATE:
+ NetworkLog(NULL, "State update from client %s, through proxy", senderAddress.ToString());
+ MsgStateUpdate(senderAddress);
+ break;
+ case ID_RPC:
+ {
+ char *message = NULL;
+ // When passing packet data, skip over proxy data (ID+sender address)
+ message = m_Peer->HandleRPCPacket( ( char* ) m_Packet->data+7, m_Packet->length-7, senderAddress );
+ if (message != NULL)
+ NetworkError(NULL, "Error receiving proxy RPC: %s", message);
+ }
+ break;
+ default:
+ NetworkError(NULL, "Unhandled relayed message %d from %s", messageID, m_Packet->systemAddress.ToString());
+ break;
+ }
+ break;
+ }
+ default:
+ NetworkError(NULL, "Unhandled message %d from %s", (int)m_Packet->data[0], m_Packet->systemAddress.ToString());
+ break;
+ }
+}
+
+string NetworkManager::GetStats(int i)
+{
+ char buf[8000];
+ string output;
+ if (IsServer())
+ {
+ RakNetStatistics* stats = m_Peer->GetStatistics(m_Players[i].playerAddress);
+ if (stats)
+ {
+ StatisticsToString(stats, buf, 1);
+ output += Format("Player %d\n", i);
+ output += buf;
+ }
+ }
+ else if (IsClient())
+ {
+ RakNetStatistics* serverStats = m_Peer->GetStatistics(m_ServerAddress);
+ if (serverStats)
+ {
+ StatisticsToString(serverStats, buf, 1);
+ output += buf;
+ }
+ }
+ return output;
+}
+
+void NetworkManager::ClientConnectionDisconnected(int msgType)
+{
+ SendToAllNetworkViews(kDisconnectedFromServer, msgType);
+ m_PeerType = kDisconnected;
+ m_Players.clear();
+}
+
+
+void NetworkManager::MsgNewConnection(SystemAddress clientAddress)
+{
+ NetworkPlayer playerID = ++m_HighestPlayerID;
+ int networkProtocolVer = 0;
+
+ m_BitStream.Read(networkProtocolVer);
+
+ NetworkLog(NULL, "Network protocol version %d connected", networkProtocolVer);
+
+ m_BitStream.Reset();
+
+ // Record the address of this new player
+ PlayerTable player;
+ player.playerIndex = playerID;
+ player.initIndex = GetValidInitIndex();
+ player.mayReceiveGroups = 0xFFFFFFFF;
+ player.isDisconnected = false;
+ player.maySendGroups = 0xFFFFFFFF;
+ player.guid = m_Packet->guid.ToString();
+ if (clientAddress != UNASSIGNED_SYSTEM_ADDRESS)
+ {
+ player.playerAddress = clientAddress;
+ player.relayed = true;
+ NetworkInfo(NULL, "Registering new proxied client %s", clientAddress.ToString());
+ m_BitStream.Write((unsigned char)ID_PROXY_SERVER_MESSAGE);
+ m_BitStream.Write(clientAddress);
+ }
+ else
+ {
+ player.playerAddress = m_Packet->systemAddress;
+ player.relayed = false;
+ }
+ m_Players.push_back(player);
+
+ m_BitStream.Write((unsigned char) (ID_CLIENT_INIT));
+ m_BitStream.Write(1); // Server version
+ m_BitStream.Write(m_PlayerID);
+ m_BitStream.Write(playerID);
+
+ // Send Network allocator batch data
+ UInt32 batchSize = m_NetworkViewIDAllocator.GetBatchSize();
+ UInt32 nbBatches = ((m_MinimumAllocatableViewIDs - 1) / batchSize) + 1;
+ m_BitStream.Write(batchSize);
+ m_BitStream.Write(nbBatches);
+ for (int i=0;i<nbBatches;i++)
+ {
+ UInt32 batch = m_NetworkViewIDAllocator.AllocateBatch(playerID);
+ m_BitStream.Write(batch);
+ }
+ NetworkLog(NULL, "Allocated %d batches of size %d for player %d", nbBatches, batchSize, playerID);
+
+ // Send init data only to client which connected
+ if (!m_Peer->Send(&m_BitStream, HIGH_PRIORITY, RELIABLE_ORDERED, kDefaultChannel, m_Packet->systemAddress, false))
+ {
+ ErrorString("Failed to send initialization message to new client");
+ }
+ else
+ {
+ NetworkInfo(NULL, "Sent initalization to player %d", playerID);
+ }
+
+ // Send buffered RPCs
+ SendRPCBuffer(player);
+
+ SendToAllNetworkViews(kPlayerConnected, playerID);
+}
+
+void NetworkManager::MsgClientInit()
+{
+ int serverVersion = 0;
+ NetworkPlayer serverId = 0;
+
+ // Extract player ID
+ m_BitStream.Reset();
+ m_BitStream.Write((char*)m_Packet->data, m_Packet->length);
+ m_BitStream.IgnoreBits(8);
+ m_BitStream.Read(serverVersion);
+ m_BitStream.Read(serverId);
+ m_BitStream.Read(m_PlayerID);
+
+ // Setup network view id allocator
+ // Feed initial available batches
+ UInt32 batchSize = 0;
+ m_BitStream.Read(batchSize);
+ UInt32 batchCount = 0;
+ m_BitStream.Read(batchCount);
+ m_NetworkViewIDAllocator.Clear(batchSize, m_MinimumAllocatableViewIDs, serverId, m_PlayerID);
+ for (int i=0;i<batchCount;i++)
+ {
+ UInt32 batch = 0;
+ m_BitStream.Read(batch);
+ m_NetworkViewIDAllocator.FeedAvailableBatchOnClient(batch);
+ }
+
+ // Register the server as a player in the player table. Note that the server address is already in m_ServerAddress
+ PlayerTable player;
+ player.playerIndex = serverId;
+ player.initIndex = 0;
+ player.playerAddress = m_Packet->systemAddress; // This is the proxy address when proxy is in use
+ player.mayReceiveGroups = 0xFFFFFFFF;
+ player.maySendGroups = 0xFFFFFFFF;
+ player.isDisconnected = false;
+ player.relayed = false;
+ m_Players.push_back(player);
+
+ m_PeerType = kClient;
+ m_SendingEnabled = 0xFFFFFFFF;
+
+ // Set the proxy address as the server address if appropriate or network loop will break
+ if (m_UseProxy)
+ m_ServerAddress = m_ProxyAddress;
+
+ SendToAllNetworkViews(kConnectedToServer);
+
+ NetworkInfo(NULL,"Set player ID to %d\n", m_PlayerID);
+}
+
+void NetworkManager::RPCReceiveViewIDBatch (RPCParameters *rpcParameters)
+{
+ NetworkManager& nm = GetNetworkManager();
+ RakNet::BitStream bitStream;
+ bitStream.Write((char*)rpcParameters->input, BITS_TO_BYTES(rpcParameters->numberOfBitsOfData));
+ UInt32 batchIndex;
+ if (bitStream.Read(batchIndex) && rpcParameters->sender == nm.m_ServerAddress)
+ {
+ nm.m_NetworkViewIDAllocator.FeedAvailableBatchOnClient(batchIndex);
+ nm.m_NetworkViewIDAllocator.AddRequestedBatches(-1);
+ }
+ else
+ {
+ NetworkError (NULL, "Failed receiving RPC batch index");
+ }
+}
+
+void NetworkManager::RPCRequestViewIDBatch (RPCParameters *rpcParameters)
+{
+ NetworkManager& nm = GetNetworkManager();
+ NetworkPlayer player = nm.GetIndexFromSystemAddress(rpcParameters->sender);
+ if (player != kUndefindedPlayerIndex)
+ {
+ UInt32 batchIndex = nm.m_NetworkViewIDAllocator.AllocateBatch(player);
+
+ RakNet::BitStream bitStream;
+ bitStream.Write(batchIndex);
+
+ if (!nm.m_Peer->RPC("__RPCReceiveViewIDBatch", &bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, kDefaultChannel, rpcParameters->sender, false, NULL, UNASSIGNED_NETWORK_ID, 0))
+ NetworkError(NULL, "Failed to send RPC batch to client!");
+ else
+ NetworkLog(NULL, "Sent batch %d of size %d to %d\n", batchIndex, nm.m_NetworkViewIDAllocator.GetBatchSize(), player);
+ }
+ else
+ {
+ NetworkError(NULL, "Failed to send RPC batch to because he is not in the player list!");
+ }
+}
+
+void NetworkManager::SendRPCBuffer (PlayerTable &player)
+{
+ RakNetTime time = GetTimestamp();
+
+ /// Send the whole RPC buffer to the player
+ list<RPCMsg>::iterator i;
+ for( i = m_RPCBuffer.begin(); i != m_RPCBuffer.end(); i++ )
+ {
+ RPCMsg& rpc = *i;
+ // Send RPC call just to client which requested RPC flush
+
+ if (player.relayed)
+ {
+ if (!m_Peer->RPC((char*)rpc.name.c_str(), rpc.stream, HIGH_PRIORITY, RELIABLE_ORDERED, kDefaultChannel, m_ProxyAddress, false, &time, UNASSIGNED_NETWORK_ID, 0, (unsigned char) ID_PROXY_SERVER_MESSAGE, player.playerAddress))
+ ErrorString("Couldn't send buffered RPCs to proxied client");
+ }
+ else
+ {
+ if (!m_Peer->RPC((char*)rpc.name.c_str(), rpc.stream, HIGH_PRIORITY, RELIABLE_ORDERED, kDefaultChannel, player.playerAddress, false, &time, UNASSIGNED_NETWORK_ID, 0))
+ ErrorString("Couldn't send buffered RPCs to client");
+ }
+
+ /* ***** needs to be updated to match new rpc signature
+
+ // DEBUG, print out info on buffered rpc call, erase eventually
+ if (m_DebugLevel >= kInformational)
+ {
+ int parentID, newID, mode;
+ i->stream->ResetReadPointer();
+ i->stream->Read(parentID);
+ i->stream->Read(mode);
+ i->stream->Read(newID);
+ LogString(Format("Sending buffered RPC \"%s\" to new client parent ID %d, inst ID %d, mode %d\n", i->name.c_str(), parentID, newID, mode));
+ }
+ */
+ }
+}
+
+bool NetworkManager::MayReceiveFromPlayer( SystemAddress adress, int group )
+{
+ PlayerTable* player = GetPlayerEntry( adress );
+ if( player )
+ {
+ return player->mayReceiveGroups & (1 << group);
+ }
+ else
+ {
+ //ErrorString( "May not receive channel from player which does not exist" );
+ ///@TODO: ERROR ON THIS AND PREVENT SENDING!!!
+
+ return false;
+ }
+}
+
+bool NetworkManager::MaySendToPlayer( SystemAddress address, int group )
+{
+ PlayerTable* player = GetPlayerEntry( address );
+ if( player )
+ {
+ return player->maySendGroups & (1 << group);
+ }
+ else
+ {
+ NetworkInfo(NULL,"NetworkPlayer instance not found for address %s, probably not connected", address.ToString());
+ return false;
+ }
+}
+
+
+void NetworkManager::SetReceivingGroupEnabled (int playerIndex, int group, bool enabled)
+{
+ PlayerTable* player = GetPlayerEntry(playerIndex);
+ if (player)
+ {
+ if (enabled)
+ player->mayReceiveGroups |= 1 << group;
+ else
+ player->mayReceiveGroups &= ~(1 << group);
+ }
+ else
+ {
+ ErrorString("SetReceivingEnabled failed because the player is not connected.");
+ }
+}
+
+void NetworkManager::SetSendingGroupEnabled (int group, bool enabled)
+{
+ if (enabled)
+ m_SendingEnabled |= 1 << group;
+ else
+ m_SendingEnabled &= ~(1 << group);
+}
+
+void NetworkManager::SetSendingGroupEnabled (int playerIndex, int group, bool enabled)
+{
+ PlayerTable* player = GetPlayerEntry(playerIndex);
+ if (player)
+ {
+ if (enabled) {
+ NetworkInfo(NULL, "Enabling sending group %d for player %d", group, playerIndex);
+ player->maySendGroups |= 1 << group;
+ }
+ else {
+ NetworkInfo(NULL, "Disabling sending group %d for player %d", group, playerIndex);
+ player->maySendGroups &= ~(1 << group);
+ }
+ }
+ else
+ {
+ ErrorString("SetSendingEnabled failed because the player is not connected.");
+ }
+}
+
+void NetworkManager::MsgStateUpdate(SystemAddress senderAddress)
+{
+ // TODO: Maybe do some integrity checking and such to make sure
+ // there are no inconsistencies like two objects having the same
+ // network ID or one ID not being found on the receiver, etc.
+
+ //m_BitStream.Reset();
+ //m_BitStream.Write((char*)m_Packet->data, m_Packet->length);
+ // IMPORTANT: The bitstream must have the read pointer at the correct position
+
+ UInt8 msgType;
+ m_BitStream.Read(msgType);
+
+ NetworkMessageInfo info;
+ info.timestamp = -1.0F;
+ if (msgType == ID_TIMESTAMP)
+ {
+ RakNetTime timeStamp = 0;
+ if (m_BitStream.Read(timeStamp))
+ info.timestamp = TimestampToSeconds(timeStamp);
+ m_BitStream.Read(msgType);
+ }
+
+ NetworkViewID viewID;
+ viewID.Read(m_BitStream);
+
+ info.viewID = viewID;
+ info.sender = GetIndexFromSystemAddress(senderAddress);
+ AssertIf(info.sender == -1);
+
+ NetworkView* view = ViewIDToNetworkView(viewID);
+ if (view != NULL)
+ {
+ if (MayReceiveFromPlayer (senderAddress, view->GetGroup()))
+ {
+ SystemAddress owner = view->GetOwnerAddress();
+ // Check incoming packets if the sender is allowed to send them.
+ if (m_PeerType == kClient)
+ {
+ if (owner.binaryAddress != 0)
+ {
+ NetworkError (NULL, "State update for an object this players owns has been received. Packet was ignored.");
+ return;
+ }
+
+ // If the client has useProxy enabled then all state updates should come from the proxy.
+ if (m_UseProxy)
+ {
+ if (m_ProxyAddress != m_Packet->systemAddress)
+ {
+ NetworkError (NULL, "State update was received from someone else than the server. Packet was ignored. Sender was %s", m_Packet->systemAddress.ToString());
+ return;
+ }
+ }
+ else if (m_Packet->systemAddress != m_ServerAddress)
+ {
+ NetworkError (NULL, "State update was received from someone else than the server. Packet was ignored. Sender was %s", m_Packet->systemAddress.ToString());
+ return;
+ }
+ }
+ // This check is not valid with proxy servers in existance
+ // And for server
+ // We could lookup the system address in the player table list and check is it is supposed to be relayed
+ // and that the sender address is the proxy server
+ /*else
+ {
+ if ( owner != m_Packet->systemAddress )
+ {
+ NetworkError (NULL, "State update for an object received that is not owned by the sender. Packet was ignored. Sender was %s", m_Packet->systemAddress.ToString());
+ return;
+ }
+ }*/
+
+ view->Unpack(m_BitStream, info, msgType);
+ }
+ else
+ {
+ NetworkInfo(view, "Received state update for view '%s' and ignored it because the channel %d is disabled.\n", viewID.ToString().c_str(), view->GetGroup());
+ }
+ }
+ else
+ {
+ NetworkWarning(NULL, "Received state update for view id' %s' but the NetworkView doesn't exist", viewID.ToString().c_str());
+ }
+}
+
+void NetworkManager::DestroyPlayerObjects(NetworkPlayer playerID)
+{
+ if (IsClient() && playerID != GetPlayerID())
+ {
+ NetworkError(NULL, "A client can only destroy his own player objects, %d is a remote player", playerID);
+ return;
+ }
+
+ NetworkInfo(NULL, "Destroying objects belonging to player %d",playerID);
+
+ bool erased = false;
+
+ for (int s=0;s<2;s++)
+ {
+ NetworkViewList& list = s == 0 ? m_Sources : m_NonSyncSources;
+
+ SafeIterator<NetworkViewList> i (list);
+ while (i.Next())
+ {
+ NetworkView& view = *i->GetData();
+ NetworkViewID viewID = NetworkViewToViewID(&view);
+ if (WasViewIdAllocatedByPlayer(viewID, playerID))
+ {
+ DestroyDelayed(viewID);
+ erased = true;
+ }
+ }
+ }
+
+ if (!erased)
+ {
+ LogString(Format("No objects for the given player ID were deleted %d", (int)playerID));
+ }
+}
+
+void NetworkManager::DestroyDelayed(NetworkViewID viewID)
+{
+ if (m_DebugLevel >= kInformational) LogString(Format("Destroying object with view ID '%s'", viewID.ToString().c_str()));
+
+ // Destroy object locally
+ NetworkView* view = ViewIDToNetworkView(viewID);
+ if (view)
+ {
+ Scripting::DestroyObjectFromScripting(&view->GetGameObject(), -1.0F);
+ }
+ else
+ {
+ ErrorString("Couldn't destroy object because the associate network view was not found");
+ return;
+ }
+
+ // Destroy remotely
+ m_BitStream.Reset();
+ viewID.Write(m_BitStream);
+
+ if (IsClient())
+ {
+ // NOTE: It is assumed that the server is the first entry in the player table
+ PerformRPCSpecificTarget("__RPCNetworkDestroy", GetPlayerEntry(0), m_BitStream, view->GetGroup());
+ }
+ else
+ {
+ BroadcastRPC ("__RPCNetworkDestroy", &m_BitStream, HIGH_PRIORITY, UNASSIGNED_SYSTEM_ADDRESS, NULL, view->GetGroup());
+ }
+}
+
+void NetworkManager::RPCNetworkDestroy(RPCParameters *rpcParameters)
+{
+ NetworkManager& nm = GetNetworkManager();
+ RakNet::BitStream stream (rpcParameters->input, BITS_TO_BYTES(rpcParameters->numberOfBitsOfData), false);
+
+ NetworkViewID viewID;
+ viewID.Read(stream);
+
+ NetworkLog(NULL,"Network destroying view ID '%s'", viewID.ToString().c_str());
+
+ NetworkView *view = nm.ViewIDToNetworkView(viewID);
+ int group = 0;
+ if (view)
+ {
+ group = view->GetGroup();
+ Scripting::DestroyObjectFromScripting(&view->GetGameObject(), -1.0F);
+ }
+ else
+ NetworkError (NULL, "Couldn't perform remote Network.Destroy because the network view '%s' could not be located.", viewID.ToString().c_str());
+
+ stream.ResetReadPointer();
+
+ // If running as server relay the message to all except owner
+ if (nm.IsServer())
+ {
+ nm.BroadcastRPC ("__RPCNetworkDestroy", &stream, HIGH_PRIORITY, rpcParameters->sender, NULL, group);
+ }
+}
+
+void NetworkManager::SetMessageQueueRunning(bool run)
+{
+ m_MessageQueueRunning = run;
+}
+
+void NetworkManager::RegisterRPC(const char* reg, void ( *functionPointer ) ( RPCParameters *rpcParms ))
+{
+ m_Peer->RegisterAsRemoteProcedureCall(const_cast<char*> (reg), functionPointer);
+}
+
+void NetworkManager::PerformRPC(const std::string &function, int mode, RakNet::BitStream& parameters, NetworkViewID viewID, UInt32 group)
+{
+ char* name = const_cast<char*>(function.c_str());
+ RakNetTime time = GetTimestamp();
+
+ // Also check for send permission on client, he can block the server if he wants to
+ if (m_PeerType == kClient)
+ {
+ if (m_UseProxy && MaySendToPlayer(m_ServerAddress, group))
+ {
+ NetworkLog(NULL, "Performing proxied RPC '%s' to server %s", function.c_str(), m_ServerAddress.ToString());
+ // Send to proxy, the server address is actually not used, the proxy knows what server the client is using
+ if(!m_Peer->RPC(name, &parameters, HIGH_PRIORITY, RELIABLE_ORDERED, kDefaultChannel, m_ProxyAddress, false, &time, UNASSIGNED_NETWORK_ID,0, (unsigned char) ID_PROXY_CLIENT_MESSAGE, m_ServerAddress))
+ {
+ NetworkError(NULL, "Couldn't send proxied RPC function '%s' to proxy server\n", name);
+ }
+ }
+ // Send only to registered server address with no ordering, no timestamp and no reply
+ else if(!m_Peer->RPC(name, &parameters, HIGH_PRIORITY, RELIABLE_ORDERED, kDefaultChannel, m_ServerAddress, false, &time, UNASSIGNED_NETWORK_ID,0))
+ {
+ NetworkError(NULL, "Couldn't send RPC function '%s' to server\n", name);
+ }
+ }
+ else if (m_PeerType == kServer)
+ {
+ BroadcastRPC(name, &parameters, HIGH_PRIORITY, UNASSIGNED_SYSTEM_ADDRESS, &time, group);
+
+ NetworkInfo(NULL, "Sent RPC call '%s' to all connected clients\n", name);
+
+ if (mode & kBufferRPCMask)
+ {
+ AssertIf(GetTargetMode(mode) != kOthers && GetTargetMode(mode) != kAll);
+ //printf_console("DEBUG: Couldn't send RPC call: %s. Buffering call.\n", name);
+ AddRPC(function, GetPlayerID(), viewID, group, parameters);
+ }
+ }
+}
+
+void NetworkManager::BroadcastRPC(const char* name, const RakNet::BitStream *parameters, PacketPriority priority, SystemAddress target, RakNetTime *includedTimestamp, UInt32 group )
+{
+ for (int i=0;i<m_Players.size();i++)
+ {
+ SystemAddress current = m_Players[i].playerAddress;
+ if (current != target && MaySendToPlayer(current, group) && !m_Players[i].isDisconnected)
+ {
+ if (m_Players[i].relayed)
+ {
+ if (!m_Peer->RPC ((const char*)name, (const RakNet::BitStream*)parameters, priority, RELIABLE_ORDERED, kDefaultChannel, m_ProxyAddress, false, includedTimestamp, UNASSIGNED_NETWORK_ID, NULL, (unsigned char) ID_PROXY_SERVER_MESSAGE, m_Players[i].playerAddress))
+ {
+ NetworkError(NULL, "Couldn't send RPC function '%s' through proxy\n", name);
+ }
+ }
+ else
+ {
+ if (!m_Peer->RPC ((const char*)name, (const RakNet::BitStream*)parameters, priority, RELIABLE_ORDERED, kDefaultChannel, current, false, includedTimestamp, UNASSIGNED_NETWORK_ID, NULL))
+ {
+ NetworkError(NULL, "Couldn't send RPC function '%s'\n", name);
+ }
+ }
+ }
+ }
+}
+
+void NetworkManager::PerformRPCSpecificTarget(char const* function, PlayerTable *player, RakNet::BitStream& parameters, UInt32 group)
+{
+ RakNetTime timestamp = GetTimestamp();
+
+ if (MaySendToPlayer(player->playerAddress, group))
+ {
+ if (IsClient() && m_UseProxy)
+ {
+ NetworkLog(NULL, "Client sending specific target RPC '%s' to %s", function, player->playerAddress.ToString());
+ if (!m_Peer->RPC(function, &parameters, HIGH_PRIORITY, RELIABLE_ORDERED, kDefaultChannel, m_ProxyAddress, false, &timestamp, UNASSIGNED_NETWORK_ID, 0, (unsigned char) ID_PROXY_CLIENT_MESSAGE, player->playerAddress))
+ {
+ NetworkError(NULL, "Couldn't send RPC function '%s'\n", function);
+ }
+ }
+ else if (IsServer() && player->relayed)
+ {
+ NetworkLog(NULL, "Server sending specific target RPC '%s' to %s", function, player->playerAddress.ToString());
+ if (!m_Peer->RPC(function, &parameters, HIGH_PRIORITY, RELIABLE_ORDERED, kDefaultChannel, m_ProxyAddress, false, &timestamp, UNASSIGNED_NETWORK_ID, 0, (unsigned char) ID_PROXY_SERVER_MESSAGE, player->playerAddress))
+ {
+ NetworkError(NULL, "Couldn't send RPC function '%s'\n", function);
+ }
+ }
+ else
+ {
+ // Send only to registered server address with no ordering, no timestamp and no reply
+ if (!m_Peer->RPC(function, &parameters, HIGH_PRIORITY, RELIABLE_ORDERED, kDefaultChannel, player->playerAddress, false, &timestamp, UNASSIGNED_NETWORK_ID,0))
+ {
+ NetworkError(NULL, "Couldn't send RPC function '%s'\n", function);
+ }
+ }
+ }
+}
+
+void NetworkManager::PeformRPCRelayAll(char *name, int mode, NetworkViewID viewID, UInt32 group, RakNetTime timestamp, SystemAddress sender, RakNet::BitStream &stream)
+{
+ NetworkLog(NULL, "Relay RPC - name: %s - mode %d - sender %s", name, GetTargetMode(mode), sender.ToString());
+ // Relay the RPC call to all other clients!
+ if ( IsServer() && (GetTargetMode(mode) == kOthers || GetTargetMode(mode) == kAll))
+ {
+ BroadcastRPC(name, &stream, HIGH_PRIORITY, sender, &timestamp, group);
+ }
+
+ // Buffer the message if appropriate
+ if (GetNetworkManager().IsServer() && (mode & kBufferRPCMask) != 0 )
+ {
+ AssertIf(GetTargetMode(mode) != kOthers && GetTargetMode(mode) != kAll);
+ NetworkPlayer senderIndex = GetNetworkManager().GetIndexFromSystemAddress(sender);
+ AddRPC(name, senderIndex, viewID, group, stream);
+ }
+}
+
+void NetworkManager::PerformRPCRelaySpecific(char *name, RakNet::BitStream *stream, NetworkPlayer player)
+{
+ //SystemAddress address = GetSystemAddressFromIndex (player);
+ PlayerTable *playerEntry = GetPlayerEntry (player);
+
+ if (playerEntry == NULL)
+ {
+ NetworkError(NULL, "Couldn't relay RPC call '%s' because the player %d is not connected", name, player);
+ return;
+ }
+
+ RakNetTime time = GetTimestamp();
+ if (playerEntry->relayed)
+ {
+ NetworkLog(NULL, "Server sending proxied relay specific target RPC '%s' to %s", name, playerEntry->playerAddress.ToString());
+ if (!m_Peer->RPC(name, stream, HIGH_PRIORITY, RELIABLE_ORDERED, kDefaultChannel, m_ProxyAddress, false, &time, UNASSIGNED_NETWORK_ID, 0, (unsigned char)ID_PROXY_SERVER_MESSAGE, playerEntry->playerAddress))
+ {
+ NetworkError(NULL, "Couldn't relay RPC call '%s'", name);
+ }
+ }
+ else
+ {
+ if (!m_Peer->RPC(name, stream, HIGH_PRIORITY, RELIABLE_ORDERED, kDefaultChannel, playerEntry->playerAddress, false, &time, UNASSIGNED_NETWORK_ID,0))
+ {
+ NetworkError(NULL, "Couldn't relay RPC call '%s'", name);
+ }
+ }
+}
+
+
+void NetworkManager::AddRPC(const std::string& name, NetworkPlayer sender, NetworkViewID viewID, UInt32 group, RakNet::BitStream& stream)
+{
+ RPCMsg msg;
+ msg.name = name;
+ msg.viewID = viewID;
+ msg.sender = sender;
+ msg.group = group;
+ msg.stream = NULL;
+ m_RPCBuffer.push_back(msg);
+ m_RPCBuffer.back().stream = new RakNet::BitStream(stream.GetData(), stream.GetNumberOfBytesUsed(), true);
+ NetworkInfo (NULL, "Added RPC '%s' to buffer.", name.c_str());
+}
+
+void NetworkManager::MsgRemoveRPCs()
+{
+ NetworkViewID viewID;
+ int systemAddress;
+ UInt32 groupMask;
+ m_BitStream.Reset();
+ m_BitStream.Write((char*)m_Packet->data, m_Packet->length);
+ m_BitStream.IgnoreBits(8);
+ m_BitStream.Read(systemAddress);
+ viewID.Read(m_BitStream);
+ m_BitStream.Read(groupMask);
+ RemoveRPCs(systemAddress, viewID, groupMask);
+}
+
+void NetworkManager::RemoveRPCs(NetworkPlayer playerIndex, NetworkViewID viewID, UInt32 groupMask)
+{
+ if (m_PeerType == kClient)
+ {
+ m_BitStream.Reset();
+ m_BitStream.Write((unsigned char)ID_REMOVE_RPCS);
+ m_BitStream.Write(playerIndex);
+ viewID.Write(m_BitStream);
+ m_BitStream.Write(groupMask);
+ if (!m_Peer->Send(&m_BitStream, HIGH_PRIORITY, RELIABLE_ORDERED, kDefaultChannel, m_ServerAddress, false))
+ {
+ NetworkError(NULL, "Failed to send remove RPCs command to network");
+ }
+ else
+ {
+ // @todo: more details
+ NetworkInfo (NULL, "Sent remove RPCs player command to server");
+ }
+ }
+ else
+ {
+ int erased = 0;
+ RPCBuffer::iterator next = m_RPCBuffer.begin();
+ for (RPCBuffer::iterator i=next;i != m_RPCBuffer.end();i=next)
+ {
+ RPCMsg& rpc = *i;
+ next++;
+
+ if (((1 << rpc.group) & groupMask) == 0)
+ continue;
+ if (rpc.viewID != viewID && viewID != NetworkViewID::GetUnassignedViewID())
+ continue;
+ if (rpc.sender != playerIndex && playerIndex != kUndefindedPlayerIndex)
+ continue;
+
+ NetworkInfo(NULL, "RPC %s with %s, player ID %d and group %d, removed from RPC buffer.", rpc.name.c_str(), rpc.viewID.ToString().c_str(), rpc.sender, rpc.group);
+ delete rpc.stream;
+ m_RPCBuffer.erase(i);
+ erased++;
+ }
+
+ NetworkInfo (NULL, "%d RPC function were removed with RemoveRPC", erased);
+ }
+}
+
+NetworkView* NetworkManager::ViewIDToNetworkView(const NetworkViewID& ID)
+{
+ NetworkView* view;
+ NetworkViewIterator i;
+ for (i = m_Sources.begin(); i != m_Sources.end(); i++) {
+ view = i->GetData();
+ if (view->GetViewID() == ID) {
+ return view;
+ }
+ }
+
+ for (i = m_NonSyncSources.begin(); i != m_NonSyncSources.end(); i++) {
+ view = i->GetData();
+ if (view->GetViewID() == ID) {
+ return view;
+ }
+ }
+
+ ErrorString(Format("View ID %s not found during lookup. Strange behaviour may occur", ID.ToString().c_str()));
+ return NULL;
+}
+
+
+NetworkViewID NetworkManager::NetworkViewToViewID(NetworkView* view)
+{
+ if (view)
+ return view->GetViewID();
+ else
+ return NetworkViewID();
+}
+
+int NetworkManager::GetValidInitIndex()
+{
+ int available = 0;
+ while (available < m_UsedInitIndices.size() && m_UsedInitIndices[available])
+ available++;
+
+ if (available == m_UsedInitIndices.size())
+ {
+ m_UsedInitIndices.push_back(true);
+ // Must set the scope or else a scope check later on will fail (the scope does not exist up until now).
+ for (NetworkViewIterator i = m_AllSources.begin(); i != m_AllSources.end(); i++) {
+ NetworkView* view = i->GetData();
+ view->SetScope(available, true); // By default all views are in scope
+ }
+ return m_UsedInitIndices.size() - 1;
+ }
+ else
+ {
+ m_UsedInitIndices[available] = true;
+
+ // Reset the initial state variable for all network views
+ NetworkView* view;
+ NetworkViewIterator i;
+ for (i = m_AllSources.begin(); i != m_AllSources.end(); i++) {
+ view = i->GetData();
+ view->SetInitState(available, false);
+ view->SetScope(available, true); // By default all views are in scope
+ }
+
+ return available;
+ }
+}
+
+NetworkViewID NetworkManager::AllocateViewID()
+{
+ // Make sure we always have enough NetworkViewId's
+ int requested = m_NetworkViewIDAllocator.ShouldRequestMoreBatches();
+ if (requested != 0)
+ {
+ if (IsServer())
+ {
+ for (int i=0;i<requested;i++)
+ {
+ UInt32 batch = m_NetworkViewIDAllocator.AllocateBatch(m_PlayerID);
+ m_NetworkViewIDAllocator.FeedAvailableBatchOnServer(batch);
+ }
+ }
+ else if (IsClient())
+ {
+ m_NetworkViewIDAllocator.AddRequestedBatches(requested);
+ for (int i=0;i<requested;i++)
+ {
+ RakNet::BitStream bitStream;
+ if (!m_Peer->RPC("__RPCRequestViewIDBatch", &bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, kDefaultChannel, m_ServerAddress, false, NULL, UNASSIGNED_NETWORK_ID,0))
+ ErrorString("Failed to request view id batch");
+ }
+ }
+ }
+
+ NetworkViewID viewID = m_NetworkViewIDAllocator.AllocateViewID();
+
+ if (viewID == NetworkViewID())
+ ErrorString("Failed to allocate view id because no NetworkView's were available to allocate from. You should increase the minimum client NetworkViewID count.");
+ NetworkInfo(NULL, "Allocating view ID %s.\n", viewID.ToString().c_str());
+
+ return viewID;
+}
+
+NetworkViewID NetworkManager::AllocateSceneViewID()
+{
+ UInt32 highestViewID = 0;
+ for (NetworkViewIterator i=m_AllSources.begin();i != m_AllSources.end();i++)
+ {
+ NetworkView *view = i->GetData();
+ if (view->GetViewID().IsSceneID())
+ highestViewID = max(view->GetViewID().GetIndex(), highestViewID);
+ }
+
+ highestViewID++;
+
+ NetworkViewID viewID;
+ viewID.SetSceneID(highestViewID);
+
+ return viewID;
+}
+
+NetworkViewID NetworkManager::ValidateSceneViewID(NetworkView* validateView, NetworkViewID viewID)
+{
+ // Find conflicting view ids
+ bool isValidViewID = viewID.IsSceneID() && viewID.GetIndex() != 0;
+
+ for (int s=0;s<2;s++)
+ {
+ NetworkViewList& list = s == 0 ? m_Sources : m_NonSyncSources;
+ for (NetworkViewIterator i=list.begin();i != list.end();i++)
+ {
+ NetworkView *view = i->GetData();
+ if (validateView != view)
+ {
+ if (viewID == view->GetViewID())
+ isValidViewID = false;
+ }
+ }
+ }
+
+ if (!isValidViewID) {
+ LogString(Format("Fixing invalid scene view ID %s", viewID.ToString().c_str()));
+ viewID = AllocateSceneViewID();
+ }
+
+ return viewID;
+}
+
+bool NetworkManager::WasViewIdAllocatedByPlayer (NetworkViewID viewID, NetworkPlayer playerID)
+{
+ //AssertIf (!IsServer());
+ return m_NetworkViewIDAllocator.FindOwner(viewID) == playerID;
+}
+
+bool NetworkManager::WasViewIdAllocatedByMe(NetworkViewID viewID)
+{
+ return m_NetworkViewIDAllocator.FindOwner(viewID) == GetPlayerID ();
+}
+
+NetworkPlayer NetworkManager::GetNetworkViewIDOwner(NetworkViewID viewID)
+{
+ return m_NetworkViewIDAllocator.FindOwner(viewID);
+}
+
+int NetworkManager::GetPlayerID()
+{
+ return m_PlayerID;
+}
+
+int NetworkManager::GetPeerType()
+{
+ return m_PeerType;
+}
+
+int NetworkManager::GetDebugLevel()
+{
+ return m_DebugLevel;
+}
+
+void NetworkManager::SetDebugLevel(int value)
+{
+ m_DebugLevel = value;
+}
+
+SystemAddress NetworkManager::GetPlayerAddress()
+{
+ return m_Peer->GetInternalID();
+}
+
+bool NetworkManager::IsClient()
+{
+ return m_PeerType == kClient;
+}
+
+bool NetworkManager::IsServer()
+{
+ return m_PeerType == kServer;
+}
+
+void NetworkManager::SetSimulation (NetworkSimulation simulation)
+{
+ switch (simulation)
+ {
+ case kBroadband:
+ m_Peer->ApplyNetworkSimulator(1000000, 20, 0);
+ break;
+ case kDSL:
+ m_Peer->ApplyNetworkSimulator(700000, 40, 0);
+ break;
+ case kISDN:
+ m_Peer->ApplyNetworkSimulator(128000, 60, 0);
+ break;
+ case kDialUp:
+ m_Peer->ApplyNetworkSimulator(56000, 150, 100);
+ break;
+ default:
+ m_Peer->ApplyNetworkSimulator(0, 0, 0);
+ break;
+ }
+}
+
+void NetworkManager::MsgClientDidDisconnect()
+{
+ MsgClientDidDisconnect(m_Packet->systemAddress);
+}
+
+void NetworkManager::MsgClientDidDisconnect(SystemAddress clientAddress)
+{
+ int playerID = GetIndexFromSystemAddress(clientAddress);
+
+ if (playerID == -1)
+ {
+ ErrorString("A client which was not in the connected player list disconnected. ???");
+ return;
+ }
+
+ PlayerTable* player = GetPlayerEntry(playerID);
+ player->isDisconnected = true;
+
+ SendToAllNetworkViews(kPlayerDisconnected, playerID);
+
+ for (PlayerAddresses::iterator i = m_Players.begin(); i != m_Players.end(); i++)
+ {
+ PlayerTable& pl = *i;
+ if (clientAddress == pl.playerAddress)
+ {
+ if (pl.initIndex < m_UsedInitIndices.size())
+ m_UsedInitIndices[pl.initIndex] = false;
+ m_Players.erase(i);
+ break;
+ }
+ }
+}
+
+void NetworkManager::SetIncomingPassword (const std::string& incomingPassword)
+{
+ const char* pass = NULL;
+ if (!incomingPassword.empty())
+ pass = incomingPassword.c_str();
+
+ m_Peer->SetIncomingPassword(pass, incomingPassword.size());
+}
+
+std::string NetworkManager::GetIncomingPassword ()
+{
+ string password;
+ int size = 0;
+ m_Peer->GetIncomingPassword(NULL, &size);
+ password.resize(size);
+ m_Peer->GetIncomingPassword(const_cast<char*>(password.data()), &size);
+ return password;
+}
+
+void NetworkManager::SetMaxConnections(int connections)
+{
+ if (connections == -1)
+ {
+ int currentConnectionCount = m_Players.size();
+ m_Peer->SetMaximumIncomingConnections(currentConnectionCount);
+ m_MaxConnections = currentConnectionCount;
+ }
+ else
+ {
+ m_Peer->SetMaximumIncomingConnections(connections);
+ m_MaxConnections = connections;
+ }
+}
+
+int NetworkManager::GetMaxConnections()
+{
+ return m_MaxConnections;
+ //return m_Peer->GetMaximumIncomingConnections();
+}
+
+int NetworkManager::GetConnectionCount()
+{
+ return m_Players.size();
+}
+
+PlayerTable* NetworkManager::GetPlayerEntry(SystemAddress playerAddress)
+{
+ for (PlayerAddresses::iterator i = m_Players.begin(); i != m_Players.end(); i++)
+ {
+ PlayerTable& player = *i;
+ if (playerAddress == player.playerAddress)
+ {
+ return &player;
+ }
+ }
+ return NULL;
+}
+
+PlayerTable* NetworkManager::GetPlayerEntry(NetworkPlayer index)
+{
+ for (PlayerAddresses::iterator i = m_Players.begin(); i != m_Players.end(); i++)
+ {
+ PlayerTable& player = *i;
+ if (index == player.playerIndex)
+ {
+ return &player;
+ }
+ }
+ return NULL;
+}
+
+SystemAddress NetworkManager::GetSystemAddressFromIndex(NetworkPlayer playerIndex)
+{
+ for (PlayerAddresses::iterator i = m_Players.begin(); i != m_Players.end(); i++)
+ {
+ PlayerTable& player = *i;
+ if (playerIndex == player.playerIndex)
+ {
+ return player.playerAddress;
+ }
+ }
+ return UNASSIGNED_SYSTEM_ADDRESS;
+}
+
+int NetworkManager::GetIndexFromSystemAddress(SystemAddress playerAddress)
+{
+ for (PlayerAddresses::iterator i = m_Players.begin(); i != m_Players.end(); i++)
+ {
+ PlayerTable& player = *i;
+ if (playerAddress == player.playerAddress)
+ {
+ return player.playerIndex;
+ }
+ }
+ return -1;
+}
+
+std::vector<PlayerTable> NetworkManager::GetPlayerAddresses()
+{
+ return m_Players;
+}
+
+
+bool NetworkManager::IsPasswordProtected()
+{
+ int size = 0;
+ m_Peer->GetIncomingPassword(NULL, &size);
+ if (size == 0)
+ return false;
+ else
+ return true;
+}
+
+// NOTE: This returns only the internal address, in the case of a NATed address we do not know which port will open on the outside
+// and thus cannot know our external playerID
+std::string NetworkManager::GetIPAddress()
+{
+ if (m_Peer->IsActive())
+ return m_Peer->GetInternalID().ToString(false);
+ else
+ return string();
+}
+
+std::string NetworkManager::GetExternalIP()
+{
+ return std::string(m_Peer->GetExternalID(UNASSIGNED_SYSTEM_ADDRESS).ToString(false));
+}
+
+int NetworkManager::GetExternalPort()
+{
+ return m_Peer->GetExternalID(UNASSIGNED_SYSTEM_ADDRESS).port;
+}
+
+std::string NetworkManager::GetIPAddress(int player)
+{
+ if (player == -2 && IsServer() && m_UseProxy)
+ {
+ return m_ProxyAddress.ToString(false);
+ }
+ // NOTE: It will not work running this on clients as the client only has the server index number, no other numbers
+ SystemAddress address = GetSystemAddressFromIndex(player);
+ if (address != UNASSIGNED_SYSTEM_ADDRESS)
+ return address.ToString(false);
+ else
+ return string ();
+}
+
+int NetworkManager::GetPort()
+{
+ if (!m_Peer->IsActive())
+ return 0;
+ else
+ return m_Peer->GetInternalID().port;
+}
+
+int NetworkManager::GetPort(int player)
+{
+ if (player == -2 && IsServer() && m_UseProxy)
+ {
+ return m_RelayPort;
+ }
+ SystemAddress address = GetSystemAddressFromIndex(player);
+ if (address != UNASSIGNED_SYSTEM_ADDRESS)
+ return address.port;
+ else
+ return 0;
+}
+
+std::string NetworkManager::GetGUID()
+{
+ return m_Peer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS).ToString();
+}
+
+std::string NetworkManager::GetGUID(int player)
+{
+ if (player == -2 && IsServer() && m_UseProxy)
+ return m_Peer->GetGuidFromSystemAddress(m_ProxyAddress).ToString();
+ PlayerTable *playerTable = GetPlayerEntry(player);
+ if (playerTable)
+ return playerTable->guid;
+ else
+ return "0";
+}
+
+void NetworkManager::GetConnections(int* connection)
+{
+ for (int i=0;i<m_Players.size();i++)
+ {
+ PlayerTable& player = m_Players[i];
+ connection[i] = player.playerIndex;
+ }
+}
+
+bool NetworkManager::GetUseNat()
+{
+ return m_DoNAT;
+}
+
+void NetworkManager::SetUseNat(bool enabled)
+{
+ if (m_DoNAT != enabled)
+ {
+ m_DoNAT = enabled;
+ if (m_DoNAT)
+ m_Peer->AttachPlugin(&m_NatPunchthrough);
+ else
+ m_Peer->DetachPlugin(&m_NatPunchthrough);
+ }
+}
+
+bool NetworkManager::ShouldIgnoreInGarbageDependencyTracking ()
+{
+ return true;
+}
+
+
+
+/*
+float NetworkManager::GetTimeout ()
+{
+ return m_TimeoutTime;
+}
+
+void NetworkManager::SetTimeout (float timeout)
+{
+ m_TimeoutTime = timeout;
+ SetTimeoutTime();
+}
+*/
+int NetworkManager::GetLastPing (NetworkPlayer player)
+{
+ return m_Peer->GetLastPing(GetSystemAddressFromIndex(player));
+}
+
+int NetworkManager::GetAveragePing (NetworkPlayer player)
+{
+ return m_Peer->GetLastPing(GetSystemAddressFromIndex(player));
+}
+
+static void GetSetNetworkViewIDs (Transform& obj, NetworkViewID*& ids, SInt32& netViewCount, bool assign);
+static void GetSetNetworkViewIDs (Transform& obj, NetworkViewID*& ids, SInt32& netViewCount, bool assign)
+{
+ GameObject& go = obj.GetGameObject();
+ int count = go.GetComponentCount ();
+ for (int i=0;i<count;i++)
+ {
+ NetworkView* view = dynamic_pptr_cast<NetworkView*> (&go.GetComponentAtIndex(i));
+ if (view)
+ {
+ if (assign)
+ {
+ if (netViewCount <= 0)
+ {
+ netViewCount = -1;
+ return;
+ }
+
+ view->SetViewID(ids[0]);
+ ids++;
+ netViewCount--;
+ }
+ else
+ {
+ netViewCount++;
+ }
+ }
+ }
+
+ for (int i=0;i<obj.GetChildrenCount();i++)
+ {
+ Transform& child = obj.GetChild(i);
+ GetSetNetworkViewIDs(child, ids, netViewCount, assign);
+ }
+}
+
+Object* NetworkManager::Instantiate (Object& prefab, Vector3f pos, Quaternionf rot, UInt32 group)
+{
+ if (!IsConnected())
+ {
+ NetworkError(NULL, "Failed Network.Instantiate because we are not connected.");
+ return NULL;
+ }
+
+ // Grab the prefab game object
+ GameObject* prefabGO = dynamic_pptr_cast<GameObject*> (&prefab);
+ Unity::Component* prefabCom = dynamic_pptr_cast<Unity::Component*> (&prefab);
+ if (prefabCom != NULL)
+ prefabGO = prefabCom->GetGameObjectPtr();
+
+ if (prefabGO == NULL)
+ {
+ NetworkError(NULL, "The prefab '%s' reference must be a component or game object.", prefab.GetName());
+ return NULL;
+ }
+
+ // Get the UnityGUID of the prefab
+ #if UNITY_EDITOR
+ UnityGUID guid = ObjectToGUID(prefabGO);
+
+ const Asset* asset = AssetDatabase::Get().AssetPtrFromGUID(guid);
+ if (asset == NULL)
+ {
+ NetworkError(NULL, "The object %s must be a prefab in the project view.", prefab.GetName());
+ return NULL;
+ }
+
+ Object* mainRep = asset->mainRepresentation.object;
+ if (mainRep != prefabGO)
+ {
+ NetworkError(NULL, "The object is not the main asset. Only root objecs of a prefab may be used for Instantiating");
+ return NULL;
+ }
+
+ #else
+ PrefabToAsset::iterator found = m_PrefabToAsset.find(PPtr<GameObject> (prefabGO));
+ if (found == m_PrefabToAsset.end())
+ {
+ NetworkError(NULL, "The object %s must be a prefab in the project view.", prefab.GetName());
+ return NULL;
+ }
+ UnityGUID guid = found->second;
+ #endif
+
+ // Get the component index
+ UInt8 componentIndex = 0xFF;
+ if (prefabCom)
+ {
+ AssertIf(prefabCom->GetGameObject().GetComponentIndex(prefabCom) >= std::numeric_limits<UInt8>::max());
+ componentIndex = prefabGO->GetComponentIndex(prefabCom);
+ }
+
+ // Allocate view ids for all network views and assign them
+ SInt32 networkViewCount = 0;
+ NetworkViewID* viewIDs = NULL;
+ GetSetNetworkViewIDs (prefabGO->GetComponent(Transform), viewIDs, networkViewCount, false);
+ ALLOC_TEMP(viewIDs, NetworkViewID, networkViewCount)
+
+ for (int i=0;i<networkViewCount;i++)
+ viewIDs[i] = AllocateViewID();
+
+ /// Send message to everyone
+ RakNet::BitStream stream;
+ BitstreamPacker packer (stream, false);
+ packer.Serialize(group);
+ packer.Serialize(guid.data[0]);
+ packer.Serialize(guid.data[1]);
+ packer.Serialize(guid.data[2]);
+ packer.Serialize(guid.data[3]);
+ packer.Serialize(componentIndex);
+ packer.Serialize(pos);
+ packer.Serialize(rot);
+ packer.Serialize(networkViewCount);
+ for (int i=0;i<networkViewCount;i++)
+ packer.Serialize(viewIDs[i]);
+
+ /// Send/Buffer RPC on to other machines
+ NetworkViewID sourceViewID = NetworkViewID();
+ if (networkViewCount > 0)
+ sourceViewID = viewIDs[0];
+ PerformRPC("__RPCNetworkInstantiate", kAll | kBufferRPCMask, stream, sourceViewID, group);
+
+ stream.ResetReadPointer();
+ return NetworkInstantiateImpl(stream, GetPlayerAddress(), GetTimestamp());
+}
+
+static void RecursiveOnNetworkInstantiate (Transform& obj, RakNetTime time, SystemAddress sender)
+{
+ GameObject& go = obj.GetGameObject();
+ int count = go.GetComponentCount ();
+ RakNet::BitStream stream;
+ for (int i=0;i<count;i++)
+ {
+ MonoBehaviour* behaviour = dynamic_pptr_cast<MonoBehaviour*> (&go.GetComponentAtIndex(i));
+ if (behaviour)
+ {
+ if (!behaviour->GetInstance())
+ {
+ NetworkError(&go, "Network instantiated object, %s, has a missing script component attached", go.GetName());
+ }
+ else
+ {
+ ScriptingMethodPtr method = behaviour->GetMethod(MonoScriptCache::kNetworkInstantiate);
+ if (method)
+ {
+ int readoffset = stream.GetReadOffset();
+ UnpackAndInvokeRPCMethod(*behaviour, method->monoMethod, stream, sender, NetworkViewID(), time, NULL);
+ stream.SetReadOffset(readoffset);
+ }
+ }
+ }
+ }
+
+ for (int i=0;i<obj.GetChildrenCount();i++)
+ {
+ Transform& child = obj.GetChild(i);
+ RecursiveOnNetworkInstantiate(child, time, sender);
+ }
+}
+
+PROFILER_INFORMATION(gInstantiateProfile, "Network.Instantiate", kProfilerNetwork)
+
+Object* NetworkManager::NetworkInstantiateImpl (RakNet::BitStream& bitstream, SystemAddress sender, RakNetTime time)
+{
+ PROFILER_AUTO(gInstantiateProfile, NULL);
+
+ SInt32 networkViewCount = 0;
+ NetworkViewID* viewIDs = NULL;
+ UnityGUID guid;
+ UInt32 group;
+ Vector3f pos;
+ Quaternionf rot;
+ UInt8 componentIndex;
+ // Read Instantiate data
+ BitstreamPacker packer (bitstream, true);
+ packer.Serialize(group);
+ packer.Serialize(guid.data[0]);
+ packer.Serialize(guid.data[1]);
+ packer.Serialize(guid.data[2]);
+ packer.Serialize(guid.data[3]);
+ packer.Serialize(componentIndex);
+ packer.Serialize(pos);
+ packer.Serialize(rot);
+ packer.Serialize(networkViewCount);
+ ALLOC_TEMP(viewIDs, NetworkViewID, networkViewCount)
+ for (int i=0;i<networkViewCount;i++)
+ packer.Serialize(viewIDs[i]);
+
+ /// Find the asset
+ #if UNITY_EDITOR
+ const Asset* asset = AssetDatabase::Get().AssetPtrFromGUID(guid);
+ if (asset == NULL)
+ {
+ NetworkError(NULL, "Network.Instantiate on the receiving client failed because the asset couldn't be found in the project");
+ return NULL;
+ }
+
+ GameObject* prefabGO = dynamic_pptr_cast<GameObject*> (asset->mainRepresentation.object);
+ #else
+ AssetToPrefab::iterator found = m_AssetToPrefab.find(guid);
+ if (found == m_AssetToPrefab.end())
+ {
+ NetworkError(NULL, "Network.Instantiate on the receiving client failed because the asset couldn't be found in the project");
+ return NULL;
+ }
+ GameObject* prefabGO = found->second;
+ #endif
+
+ // Make sure we got the game object
+ if (prefabGO == NULL)
+ {
+ NetworkError(NULL, "Network.Instantiate sent component but found asset is not a prefab.");
+ return NULL;
+ }
+
+ // Get the right component
+ Object* prefab = prefabGO;
+ if (componentIndex != 0xFF)
+ {
+ if (componentIndex >= prefabGO->GetComponentCount())
+ {
+ NetworkError(NULL, "Network.Instantiate component index is out of bounds.");
+ return NULL;
+ }
+
+ prefab = &prefabGO->GetComponentAtIndex(componentIndex);
+ }
+
+ // Instantiate the object
+ TempRemapTable ptrs;
+ Object& clone = InstantiateObject(*prefab, pos, rot, ptrs);
+
+ Transform* cloneTransform = NULL;
+ GameObject* cloneGO = dynamic_pptr_cast<GameObject*> (&clone);
+ Unity::Component* cloneComponent = dynamic_pptr_cast<Unity::Component*> (&clone);
+ if (cloneGO)
+ cloneTransform = cloneGO->QueryComponent(Transform);
+ if (cloneComponent)
+ cloneTransform = cloneComponent->QueryComponent(Transform);
+ AssertIf(!cloneTransform);
+
+ // Assign view ids for all network views
+ GetSetNetworkViewIDs(*cloneTransform, viewIDs, networkViewCount, true);
+ if (networkViewCount != -0)
+ {
+ NetworkError(NULL, "Network.Instantiate received non-matching number of view id's as contained in prefab");
+ }
+
+ AwakeAndActivateClonedObjects(ptrs);
+
+ // Call network instantiate function
+ RecursiveOnNetworkInstantiate(*cloneTransform, time, sender);
+
+ return &clone;
+}
+
+void NetworkManager::RPCNetworkInstantiate (RPCParameters* rpcParameters)
+{
+ NetworkManager& nm = GetNetworkManager();
+ RakNet::BitStream stream (rpcParameters->input, BITS_TO_BYTES(rpcParameters->numberOfBitsOfData), false);
+
+ UInt32 group = 0;
+ stream.Read(group);
+
+ // Should we be receiving this at all?
+ if (!nm.MayReceiveFromPlayer(rpcParameters->sender, group))
+ {
+ NetworkInfo (NULL, "Network.Instantiate was ignored since the group of the network view is disabled.");
+ return;
+ }
+
+ stream.ResetReadPointer();
+ nm.NetworkInstantiateImpl (stream, rpcParameters->sender, rpcParameters->remoteTimestamp);
+
+ stream.ResetReadPointer();
+ nm.PeformRPCRelayAll(rpcParameters->functionName, kAll | kBufferRPCMask, NetworkViewID(), group, rpcParameters->remoteTimestamp, rpcParameters->sender, stream);
+}
+
+void NetworkManager::SetLevelPrefix(int levelPrefix)
+{
+ m_LevelPrefix = levelPrefix;
+}
+
+IMPLEMENT_CLASS_HAS_INIT (NetworkManager)
+IMPLEMENT_OBJECT_SERIALIZE (NetworkManager)
+GET_MANAGER (NetworkManager)
+GET_MANAGER_PTR (NetworkManager)
+
+void NetworkManager::InitializeClass ()
+{
+ #if UNITY_EDITOR
+ RegisterAllowNameConversion ("NetworkViewID", "m_SceneID", "m_ID");
+ #endif
+}
+
+void NetworkManager::CleanupClass ()
+{
+}
+
+bool NetworkManager::MaySend( int group )
+{
+ return m_SendingEnabled & (1 << group);
+}
+
+// Shouldn't errors always be shown???
+void NetworkError (Object* obj, const char* format, ...)
+{
+ if (GetNetworkManager().GetDebugLevel() >= kImportantErrors)
+ {
+ va_list va;
+ va_start( va, format );
+ ErrorStringObject(VFormat(format, va), obj);
+ }
+}
+
+
+void NetworkWarning (Object* obj, const char* format, ...)
+{
+ if (GetNetworkManager().GetDebugLevel() >= kImportantErrors)
+ {
+ va_list va;
+ va_start( va, format );
+ ErrorStringObject(VFormat(format, va), obj);
+ }
+}
+
+void NetworkInfo (Object* obj, const char* format, ...)
+{
+ if (GetNetworkManager().GetDebugLevel() >= kInformational)
+ {
+ va_list va;
+ va_start( va, format );
+ LogStringObject(VFormat(format, va), obj);
+ }
+}
+
+void NetworkLog (Object* obj, const char* format, ...)
+{
+ if (GetNetworkManager().GetDebugLevel() >= kCompleteLog)
+ {
+ va_list va;
+ va_start( va, format );
+ LogStringObject(VFormat(format, va), obj);
+ }
+}
+
+RakNetTime NetworkManager::GetTimestamp()
+{
+ return RakNet::GetTime();
+}
+
+double NetworkManager::GetTime()
+{
+ return TimestampToSeconds(RakNet::GetTime());
+}
+
+RakPeerInterface* NetworkManager::GetPeer()
+{
+ return m_Peer;
+}
+
+void NetworkManager::SwapFacilitatorID(SystemAddress newAddress)
+{
+ m_Peer->CloseConnection(m_FacilitatorID, true);
+ m_OldFacilitatorID = m_FacilitatorID;
+ m_FacilitatorID = newAddress;
+ if (m_Peer->IsConnected(m_OldFacilitatorID))
+ {
+ if (!m_Peer->Connect(newAddress.ToString(false), newAddress.port, 0, 0))
+ ErrorString("Internal problem connecting to new facilitator address");
+ }
+}
+
+void NetworkManager::SetOldMasterServerAddress(SystemAddress address)
+{
+ m_OldMasterServerID = address;
+}
+
+void NetworkManager::SetConnTesterAddress(SystemAddress address)
+{
+ m_ConnTesterAddress = address;
+ if (!m_ConnTester)
+ m_ConnTester = new ConnectionTester(m_ConnTesterAddress);
+ m_ConnTester->SetAddress(address);
+
+ // Reset test
+ m_ConnStatus = kConnTestUndetermined;
+}
+
+int NetworkManager::TestConnection(bool forceNATType, bool forceTest)
+{
+ // If the test is undetermined or if a test is forced -> it's OK to recreate tester and/or reenter test function
+ // If there is a kConnTestError then the test must be forced to get a new result
+ if ( m_ConnStatus == kConnTestUndetermined || forceTest)
+ {
+ if (!m_ConnTester)
+ m_ConnTester = new ConnectionTester(m_ConnTesterAddress);
+ m_ConnStatus = m_ConnTester->RunTest(forceNATType);
+ }
+ return m_ConnStatus;
+}
+
+void NetworkManager::PingWrapper(Ping *time)
+{
+ time->Retain();
+
+ // If the thread is not running then execute ping now, else put it in queue
+ if (!m_PingThread.IsRunning())
+ m_PingThread.Run(&PingImpl, (void*)time);
+ else
+ m_PingQueue.push(time);
+}
+
+void NetworkManager::ResolveFacilitatorAddress()
+{
+ ResolveAddress(m_FacilitatorID, "facilitator.unity3d.com", "facilitatorbeta.unity3d.com",
+ "Cannot resolve facilitator address, make sure you are connected to the internet before connecting to a server with NAT punchthrough enabled");
+}
+
+SystemAddress& NetworkManager::GetFacilitatorAddress(bool resolve)
+{
+ if (resolve)
+ ResolveFacilitatorAddress();
+
+ return m_FacilitatorID;
+}
+
+
+/* The connection tester.
+ m_ConnStatus indicates the connection test results:
+ -2 error
+ -1 undetermined, test not completed
+ 0 No NAT punchthrough capability
+ 1 NAT puConnectionTesternchthrough test successful
+ 2 Public IP test successful, listen port directly connectable
+ 3 Public IP test unsuccessful, tester unable to connect to listen port (firewall blocking access)
+ 4 Public IP test unsuccessful because no port is listening (server not initialized)
+ m_TestRunning indicates type of test:
+ 0 No test running
+ 1 NAT test running
+ 2 Public IP test running
+
+ The only reason m_TestRunning needs to display seperate test types is because we need to be able to interpret what
+ has happened when a successful connect has occured. I.e. did NAT punchthrough connect work or did we just connect to
+ the tester server do start a public IP connectivity test.
+*/
+ConnectionTester::ConnectionTester(SystemAddress& address)
+{
+ ResolveAddress(address, "connectiontester.unity3d.com", "connectiontesterbeta.unity3d.com",
+ "Cannot resolve connection tester address, you must be connected to the internet before performing this or set the address to something accessible to you.");
+
+ m_ConnTesterAddress = address;
+ m_Peer = RakNetworkFactory::GetRakPeerInterface();
+ m_NatTypeDetection = new RakNet::NatTypeDetectionClient;
+ m_Peer->AttachPlugin(m_NatTypeDetection);
+ m_ConnStatus = kConnTestUndetermined;
+ m_TestRunning = 0;
+ #if PACKET_LOGGER
+ m_Peer->AttachPlugin(&messageHandler2);
+ messageHandler2.LogHeader();
+ #endif
+}
+
+ConnectionTester::~ConnectionTester()
+{
+ m_Peer->DetachPlugin(m_NatTypeDetection);
+ delete m_NatTypeDetection;
+ m_NatTypeDetection = NULL;
+ RakNetworkFactory::DestroyRakPeerInterface(m_Peer);
+ m_Peer = NULL;
+}
+
+void ConnectionTester::SetAddress(SystemAddress address)
+{
+ m_ConnTesterAddress = address;
+ FinishTest(kConnTestUndetermined);
+}
+
+// TODO: At the moment -2 is returned on all errors, -1 on undetermined and 0 or 1 on success or failure. Maybe there
+// should be seperate error numbers for each kind of error and "less than -1" indicates and error
+int ConnectionTester::Update()
+{
+ // Check for timeout, if it has expired some problem has occured
+ if (m_TestRunning > 0 && time(0) - m_Timestamp > kConnTestTimeout)
+ {
+ LogString("Timeout during connection test");
+ m_TestRunning = 0;
+ return kConnTestError;
+ }
+
+ if (!m_Peer->IsActive())
+ return m_ConnStatus;
+
+ if (m_TestRunning > 0)
+ {
+ Packet* packet;
+ packet = m_Peer->Receive();
+ while (packet)
+ {
+ switch(packet->data[0])
+ {
+ case ID_CONNECTION_REQUEST_ACCEPTED:
+ StartTest();
+ break;
+ case ID_NO_FREE_INCOMING_CONNECTIONS:
+ NetworkError(NULL, "The connection tester is not accepting new connections, test finished.");
+ FinishTest();
+ break;
+ case ID_CONNECTION_ATTEMPT_FAILED:
+ NetworkInfo(NULL, "Failed to connect to connection tester at %s", packet->systemAddress.ToString());
+ FinishTest();
+ break;
+ case ID_CONNECTION_BANNED:
+ NetworkInfo(NULL, "The connection tester has banned this connection.");
+ FinishTest();
+ break;
+ case ID_DISCONNECTION_NOTIFICATION:
+ NetworkInfo(NULL, "Disconnected from connection tester.");
+ FinishTest();
+ break;
+ case ID_CONNECTION_LOST:
+ NetworkError(NULL, "Lost connection to connection tester.");
+ FinishTest();
+ break;
+ case ID_NAT_TYPE_DETECTION_RESULT:
+ {
+ // TODO: We know certain types might not be able to connect to another one. How
+ // should this be exposed... It's not as straight forward as saying NAT punchthrough
+ // capable or not capable. Maybe expose Network.CanConnect(type1,type2).
+ RakNet::NATTypeDetectionResult r = (RakNet::NATTypeDetectionResult) packet->data[1];
+ // TODO: Now check if router is UPNP compatible
+ switch (r)
+ {
+ case RakNet::NAT_TYPE_PORT_RESTRICTED: m_ConnStatus = kLimitedNATPunchthroughPortRestricted; break;
+ case RakNet::NAT_TYPE_SYMMETRIC: m_ConnStatus = kLimitedNATPunchthroughSymmetric; break;
+ case RakNet::NAT_TYPE_FULL_CONE: m_ConnStatus = kNATpunchthroughFullCone; break;
+ case RakNet::NAT_TYPE_ADDRESS_RESTRICTED: m_ConnStatus = kNATpunchthroughAddressRestrictedCone; break;
+ case RakNet::NAT_TYPE_NONE: m_ConnStatus = kPublicIPIsConnectable; break;
+ default:
+ m_ConnStatus = kConnTestError;
+ NetworkError(NULL, "Connection Tester returned invalid NAT type.");
+ break;
+ }
+ m_TestRunning = 0;
+ m_Peer->Shutdown(kDefaultTimeout);
+ }
+ break;
+ // Public IP test: tester is reporting that the couln't connect to the game server IP and port, test unsuccessful
+ case 254:
+ FinishTest(kPublicIPPortBlocked);
+ break;
+ case ID_ALREADY_CONNECTED:
+ NetworkInfo(NULL, "Already connected to connection tester, attempting to trigger new test.");
+ StartTest();
+ break;
+ default:
+ NetworkInfo(NULL, "Received invalid message type %d from connection tester at %s", (int)packet->data[0], packet->systemAddress.ToString());
+ break;
+ }
+ m_Peer->DeallocatePacket(packet);
+ packet = m_Peer->Receive();
+ }
+ }
+ return m_ConnStatus;
+}
+
+void ConnectionTester::StartTest()
+{
+ // NAT test: successfully connected with NAT punchthrough
+ if (m_TestRunning == 1)
+ {
+ NetworkInfo(NULL, "Starting NAT connection test.");
+ m_NatTypeDetection->DetectNATType(m_ConnTesterAddress);
+ }
+ // Public IP test: connected with tester so send him the connect-to-me command
+ else
+ {
+ RakNet::BitStream bitStream;
+ bitStream.Write((unsigned char)253);
+ bitStream.Write(GetNetworkManager().GetPort());
+ m_Peer->Send(&bitStream, HIGH_PRIORITY, RELIABLE, 0, m_ConnTesterAddress, false);
+ NetworkInfo(NULL, "Connection Tester requesting test on external IP and port %d", GetNetworkManager().GetPort());
+ }
+}
+
+void ConnectionTester::ReportTestSucceeded()
+{
+ FinishTest(kPublicIPIsConnectable);
+}
+
+void ConnectionTester::FinishTest(int status)
+{
+ m_ConnStatus = status;
+ m_TestRunning = 0;
+ m_Peer->Shutdown(kDefaultTimeout);
+}
+
+int ConnectionTester::RunTest(bool forceNATType)
+{
+ // If a problem occured the timeout must expire before anything is done. See below
+ if ( m_ConnStatus > kConnTestError && !(m_ConnStatus == kConnTestUndetermined && m_TestRunning > 0) )
+ {
+ // If test has already been triggered, stop here
+ if (m_TestRunning > 0)
+ return kConnTestUndetermined;
+
+ // First check if the peer interface has been initialized
+ if (!m_Peer->IsActive())
+ {
+ SocketDescriptor sd(0,0);
+ if (!m_Peer->Startup(2, 1, &sd, 1))
+ {
+ ErrorString("Failed to initialize network connection before NAT test.");
+ return kConnTestError;
+ }
+ }
+
+ // Set the timer, this ensures this instance will be destroyed after the timeout
+ m_Timestamp = time(0);
+
+ // There are two kinds of tests, one for public IP addresses and one for private addresses, either one is executed
+ if (CheckForPublicAddress() && !forceNATType)
+ {
+ NetworkInfo(NULL, "Starting public address connection test.");
+ if (GetNetworkManager().IsServer())
+ {
+ if (!m_Peer->Connect(m_ConnTesterAddress.ToString(false),m_ConnTesterAddress.port,0,0))
+ {
+ ErrorString("Failed to connect the connection tester.");
+ return kConnTestError;
+ }
+ m_TestRunning = 2;
+ }
+ else
+ {
+ FinishTest(kPublicIPNoServerStarted);
+ return m_ConnStatus;
+ }
+ }
+ else
+ {
+ if (!m_Peer->IsConnected(m_ConnTesterAddress))
+ {
+ NetworkInfo(NULL, "Connecting to connection tester at %s", m_ConnTesterAddress.ToString());
+ if (!m_Peer->Connect(m_ConnTesterAddress.ToString(false),m_ConnTesterAddress.port,0,0))
+ {
+ ErrorString("Failed to connect to connection tester during NAT test.");
+ return kConnTestError;
+ }
+ }
+
+ m_TestRunning = 1;
+ }
+
+ return m_ConnStatus = kConnTestUndetermined;
+ }
+ // If there is a problem (-2) and the timeout is expired you may try again (reset test and recall RunTest())
+ else if (m_ConnStatus == kConnTestError && (time(0) - m_Timestamp > kConnTestTimeout))
+ {
+ m_Timestamp = time(0);
+ m_ConnStatus = kConnTestUndetermined;
+ return RunTest(forceNATType);
+ }
+
+ return m_ConnStatus;
+}
+
+template<class TransferFunc>
+void NetworkManager::Transfer (TransferFunc& transfer) {
+
+ AssertIf(transfer.GetFlags() & kPerformUnloadDependencyTracking);
+
+ Super::Transfer (transfer);
+ TRANSFER(m_DebugLevel);
+ TRANSFER(m_Sendrate);
+ transfer.Transfer(m_AssetToPrefab, "m_AssetToPrefab", kHideInEditorMask);
+}
+
+#endif // ENABLE_NETWORK
diff --git a/Runtime/Network/NetworkManager.h b/Runtime/Network/NetworkManager.h
new file mode 100644
index 0000000..6109960
--- /dev/null
+++ b/Runtime/Network/NetworkManager.h
@@ -0,0 +1,358 @@
+#ifndef UNITY_NETWORK_MANAGER_H_
+#define UNITY_NETWORK_MANAGER_H_
+
+#include "Configuration/UnityConfigure.h"
+
+enum NetworkReachability
+{
+ NotReachable = 0,
+ ReachableViaCarrierDataNetwork,
+ ReachableViaLocalAreaNetwork,
+};
+
+NetworkReachability GetInternetReachability ();
+
+#if ENABLE_NETWORK
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include <set>
+#include <list>
+#include <queue>
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Utilities/GUID.h"
+
+#include "NetworkEnums.h"
+#include "NetworkView.h"
+#include "Runtime/Graphics/Transform.h"
+#include "External/RakNet/builds/include/GetTime.h"
+#include "External/RakNet/builds/include/MessageIdentifiers.h"
+#include "External/RakNet/builds/include/BitStream.h"
+#include "External/RakNet/builds/include/RakNetTypes.h"
+#include "External/RakNet/builds/include/NatPunchthroughClient.h"
+#include "External/RakNet/builds/include/NatTypeDetectionClient.h"
+#include "NetworkViewIDAllocator.h"
+#include <time.h>
+#include "Runtime/Threads/Thread.h"
+#include "NetworkUtility.h"
+
+
+//class RakPeer;
+
+class ConnectionTester
+{
+ public:
+ ConnectionTester(SystemAddress& testerAddress);
+ ~ConnectionTester();
+ void SetAddress(SystemAddress address);
+ int Update();
+ int RunTest(bool forceNATType);
+ void ReportTestSucceeded();
+
+ private:
+ void StartTest();
+ void FinishTest(int status = kConnTestError);
+ int m_ConnStatus;
+ int m_TestRunning;
+ RakPeerInterface* m_Peer;
+ NatPunchthroughClient m_NATPunchthrough;
+ RakNet::NatTypeDetectionClient *m_NatTypeDetection;
+ SystemAddress m_ConnTesterAddress;
+ time_t m_Timestamp;
+};
+
+
+
+class NetworkManager : public GlobalGameManager
+{
+public:
+ typedef std::list<RPCMsg> RPCBuffer;
+ typedef std::vector<PlayerTable> PlayerAddresses;
+ typedef std::map<UnityGUID, PPtr<GameObject> > AssetToPrefab;
+ typedef std::map<PPtr<GameObject>, UnityGUID > PrefabToAsset;
+ typedef std::queue<Ping*> PingQueue;
+
+ REGISTER_DERIVED_CLASS (NetworkManager, GlobalGameManager)
+ DECLARE_OBJECT_SERIALIZE (NetworkManager)
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ NetworkManager (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~NetworkManager (); declared-by-macro
+
+ /// Virtual API exposed in GameManager to avoid direct depdendencies to the network layer
+ virtual void NetworkUpdate ();
+ virtual void NetworkOnApplicationQuit();
+
+ void AddNetworkView (ListNode<NetworkView>& s);
+ void AddNonSyncNetworkView (ListNode<NetworkView>& s);
+ void AddAllNetworkView (ListNode<NetworkView>& s);
+ bool ShouldIgnoreInGarbageDependencyTracking ();
+
+ // Destroy locally and broadcast destroy message to peers (or just server)
+ void DestroyDelayed(NetworkViewID viewID);
+
+ // Destory all objects which belong the the given player locally and remotely and
+ // possibly remove all RPC calls currently in server RPC buffer.
+ void DestroyPlayerObjects(NetworkPlayer playerID);
+
+ void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ int InitializeServer(int connections, int listenPort, bool useNat);
+ void InitializeSecurity();
+
+ int Connect(std::string IP, int remotePort, int listenPort, const std::string& password);
+ int Connect(std::string IP, int remotePort, const std::string& password);
+ int Connect(std::vector<string> IPs, int remotePort, int listenPort, const std::string& password);
+ int Connect(RakNetGUID serverGUID, int listenPort, const std::string& password);
+ void Disconnect(int timeout, bool resetParams = true);
+ void CloseConnection(int target, bool sendDisconnect);
+
+ NetworkView* ViewIDToNetworkView (const NetworkViewID& ID);
+ NetworkViewID NetworkViewToViewID(NetworkView* view);
+
+ void LoadLevel(const std::string& level);
+
+ void SetIncomingPassword (const std::string& incomingPassword);
+ std::string GetIncomingPassword ();
+
+ void RegisterRPC(const char* reg, void ( *functionPointer ) ( RPCParameters *rpcParms ));
+ void BroadcastRPC(const char* name, const RakNet::BitStream *parameters, PacketPriority priority, SystemAddress target, RakNetTime *includedTimestamp, UInt32 group);
+
+ // Send an RPC call to every connected peer except the owner of the call (systemAddress)
+ void PerformRPC(const std::string &function, int mode, RakNet::BitStream& parameters, NetworkViewID viewID, UInt32 group);
+ void PerformRPCSpecificTarget(char const* function, PlayerTable *player, RakNet::BitStream& parameters, UInt32 group);
+
+ // Perform RPC to every connected client except systemAddress
+ void PeformRPCRelayAll(char *name, int mode, NetworkViewID viewID, UInt32 group, RakNetTime timestamp, SystemAddress sender, RakNet::BitStream &stream);
+ void PerformRPCRelaySpecific(char *name, RakNet::BitStream *stream, NetworkPlayer player);
+ void AddRPC(const std::string& name, NetworkPlayer sender, NetworkViewID viewID, UInt32 group, RakNet::BitStream& stream);
+
+ // This function must give out unique ID numbers per game. Getting a view ID automatically registers it.
+ // The editor must always "run" as a server so he will get the correct prefix (no prefix),
+ // then after play has been pressed the proper type is selected (server or client).
+ // m_playerID, which is used as a prefix on clients, will be updated to a proper value by the server after the connect.
+ NetworkViewID AllocateViewID();
+ // This allocate routine is for use in the editor, here we don't want any prefix and the
+ // number is decremented with each new ID (opposed to incrementing it like AllocateViewID does).
+ NetworkViewID AllocateSceneViewID();
+ NetworkViewID ValidateSceneViewID(NetworkView* validateView, NetworkViewID viewID);
+
+
+ void SetLevelPrefix(int levelPrefix);
+ int GetLevelPrefix() { return m_LevelPrefix; }
+
+ int GetPlayerID();
+
+ int GetPeerType();
+ int GetDebugLevel();
+ void SetDebugLevel(int value);
+
+ // Get this players address
+ SystemAddress GetPlayerAddress();
+
+ // Lookup a player ID number in the player address table
+ /// UNASSIGNED_SYSTEM_ADDRESS if can't be found
+ SystemAddress GetSystemAddressFromIndex(NetworkPlayer playerIndex);
+
+ /// the player index for the adress.
+ // -1 if can't be found. Server is always 0.
+ int GetIndexFromSystemAddress(SystemAddress playerAddress);
+
+ PlayerTable* GetPlayerEntry(SystemAddress playerAddress);
+ PlayerTable* GetPlayerEntry(NetworkPlayer playerAddress);
+
+ // Get all the registered player addresses
+ std::vector<PlayerTable> GetPlayerAddresses();
+
+ // For user scripting
+ bool IsClient();
+ bool IsServer();
+
+ void SetReceivingGroupEnabled (int player, int group, bool enabled);
+ void SetSendingGroupEnabled (int group, bool enabled);
+ void SetSendingGroupEnabled (int playerIndex, int group, bool enabled);
+
+ bool MayReceiveFromPlayer( SystemAddress player, int group );
+ bool MaySendToPlayer( SystemAddress adress, int group );
+ bool MaySend( int group );
+
+ void SetMessageQueueRunning(bool run);
+ bool GetMessageQueueRunning() { return m_MessageQueueRunning; }
+
+ bool WasViewIdAllocatedByMe(NetworkViewID viewID);
+ bool WasViewIdAllocatedByPlayer (NetworkViewID viewID, NetworkPlayer playerID);
+ NetworkPlayer GetNetworkViewIDOwner(NetworkViewID viewID);
+
+ void SetSimulation (NetworkSimulation simulation);
+ string GetStats(int player);
+
+ void SetSendRate (float rate) { m_Sendrate = rate; }
+ float GetSendRate () { return m_Sendrate; }
+
+// void LoadLevel(const std::string& levelName);
+// void LoadLevel(int level);
+
+ int GetMaxConnections();
+ int GetConnectionCount();
+ void GetConnections(int* connection);
+
+ bool IsPasswordProtected();
+
+ std::string GetIPAddress();
+ std::string GetIPAddress(int player);
+ SystemAddress GetServerAddress() { return m_ServerAddress; }
+ std::string GetExternalIP();
+ int GetExternalPort();
+
+ int GetPort();
+ int GetPort(int player);
+
+ std::string GetGUID();
+ std::string GetGUID(int player);
+
+// void SetChannelEnabled (UInt32 channel, bool enabled);
+// bool GetChannelEnabled (UInt32 channel) { return m_EnabledChannels & (1 << channel); }
+
+ // If viewId is 0 all view ids will be removed
+ // If playerIndex is -1 all players will be removed
+ void RemoveRPCs(NetworkPlayer playerIndex, NetworkViewID viewID, UInt32 channelMask);
+
+ bool IsConnected() { return m_PeerType != kDisconnected; };
+
+ double GetTime();
+ RakNetTime GetTimestamp();
+
+ bool GetUseNat();
+ void SetUseNat(bool enabled);
+
+ int GetLastPing (NetworkPlayer player);
+ int GetAveragePing (NetworkPlayer player);
+
+ Object* Instantiate (Object& asset, Vector3f pos, Quaternionf rot, UInt32 group);
+ Object* NetworkInstantiateImpl (RakNet::BitStream& bitstream, SystemAddress sender, RakNetTime time);
+ static void RPCNetworkInstantiate (RPCParameters* params);
+
+ void SetAssetToPrefab (const AssetToPrefab& mapping);
+
+ RakPeerInterface* GetPeer();
+
+ void ResolveFacilitatorAddress();
+ SystemAddress& GetFacilitatorAddress(bool resolve = true);
+ void SwapFacilitatorID(SystemAddress newAddress);
+
+ int TestConnection(bool forceNATType, bool forceTest = false);
+ SystemAddress GetConnTesterAddress() { return m_ConnTesterAddress; }
+ void SetConnTesterAddress(SystemAddress address);
+
+ void SetOldMasterServerAddress(SystemAddress address);
+
+ void SetMinimumAllocatableViewIDs (int v) { m_MinimumAllocatableViewIDs = v; m_NetworkViewIDAllocator.SetMinAvailableViewIDs(v); }
+ int GetMinimumAllocatableViewIDs () { return m_MinimumAllocatableViewIDs; }
+
+ void SetMaxConnections(int connections);
+
+ void PingWrapper(Ping *time);
+
+ void SetProxyIP(string address) { m_ProxyAddress.SetBinaryAddress(address.c_str()); }
+ string GetProxyIP() { return string(m_ProxyAddress.ToString(false)); }
+ void SetProxyPort(int port) { m_ProxyAddress.port = port; }
+ int GetProxyPort() { return m_ProxyAddress.port; }
+ SystemAddress GetProxyAddress() { return m_ProxyAddress; }
+ void SetUseProxy(bool value) { m_UseProxy = value; }
+ bool GetUseProxy() { return m_UseProxy; }
+ string GetProxyPassword() { return m_ProxyPassword; }
+ void SetProxyPassword(string password) { m_ProxyPassword = password; }
+
+ int GetInitIndexSize() { return m_UsedInitIndices.size(); }
+
+ private:
+
+ void ProcessPacket(unsigned char packetIdentifier);
+ void ResolveProxyAddress();
+
+// void DumpNewConnectionInitialData( SystemAddress player, int channel );
+ void SendRPCBuffer (PlayerTable &player);
+ static void RPCReceiveViewIDBatch (RPCParameters *rpcParameters);
+ static void RPCRequestViewIDBatch (RPCParameters *rpcParameters);
+ static void RPCNetworkDestroy(RPCParameters *rpcParms);
+
+ // Successfully connected to server. Request rpc buffer contents.
+ void MsgConnected();
+ // New client connected to server. Send initialization message back to him (contains player ID number)
+ void MsgNewConnection(SystemAddress sender = UNASSIGNED_SYSTEM_ADDRESS);
+ void MsgStateUpdate(SystemAddress senderAddress);
+ void MsgClientInit();
+ void MsgRemoveRPCs();
+ void MsgRemovePlayerRPCs();
+ void MsgClientDidDisconnect();
+ void MsgClientDidDisconnect(SystemAddress clientAddress);
+
+ int GetValidInitIndex();
+ void ClientConnectionDisconnected(int msgType);
+
+private:
+
+ typedef List< ListNode<NetworkView> > NetworkViewList;
+ typedef NetworkViewList::iterator NetworkViewIterator;
+
+ bool m_MessageQueueRunning;
+ float m_Sendrate;
+ float m_LastSendTime;
+// float m_TimeoutTime;
+ int m_PeerType; ///< enum { Server = 0, Client = 1, Server-Client = 2}
+ int m_PlayerID; // Player ID number
+ int m_HighestPlayerID;// Available player ID numbers (server) This is used only on the server
+ int m_MinimumAllocatableViewIDs;
+ RakPeerInterface *m_Peer;
+ Packet *m_Packet;
+ int m_LevelPrefix;
+ RakNet::BitStream m_BitStream;
+ SystemAddress m_ServerAddress; // The system address of the server
+ std::string m_ServerPassword;
+ RakNetGUID m_ServerGUID;
+ RPCBuffer m_RPCBuffer;
+ NetworkViewList m_Sources; // The set of object views which are to be synchronized
+ NetworkViewList m_NonSyncSources; // Network views which do not sync with other network objects
+ NetworkViewList m_AllSources;
+
+ PlayerAddresses m_Players;
+ int m_DebugLevel; ///< enum { Off = 0, Informational = 1, Full = 2}
+ NetworkViewIDAllocator m_NetworkViewIDAllocator;
+ UInt32 m_SendingEnabled;
+ UInt32 m_ReceivedInitialState;
+ bool m_DoNAT;
+ NatPunchthroughClient m_NatPunchthrough;
+ SystemAddress m_FacilitatorID;
+ bool m_ConnectingAfterPing;
+ time_t m_PingConnectTimestamp;
+ SystemAddress m_OldMasterServerID;
+ SystemAddress m_OldFacilitatorID;
+ dynamic_bitset m_UsedInitIndices;
+ ConnectionTester *m_ConnTester;
+ int m_ConnStatus;
+ SystemAddress m_ConnTesterAddress;
+ int m_MaxConnections;
+ Thread m_PingThread;
+ PingQueue m_PingQueue;
+ SystemAddress m_ProxyAddress;
+ bool m_UseProxy;
+ string m_ProxyPassword;
+ unsigned short m_RelayPort;
+
+ AssetToPrefab m_AssetToPrefab;
+ PrefabToAsset m_PrefabToAsset;
+};
+
+void NetworkError (Object* obj, const char* format, ...);
+void NetworkWarning (Object* obj, const char* format, ...);
+void NetworkInfo (Object* obj, const char* format, ...);
+void NetworkLog (Object* obj, const char* format, ...);
+
+NetworkManager& GetNetworkManager ();
+NetworkManager* GetNetworkManagerPtr ();
+
+#endif
+#endif //UNITY_NETWORK_MANAGER_H_
diff --git a/Runtime/Network/NetworkStubs.h b/Runtime/Network/NetworkStubs.h
new file mode 100644
index 0000000..1cca813
--- /dev/null
+++ b/Runtime/Network/NetworkStubs.h
@@ -0,0 +1,77 @@
+#ifndef UNITY_NETWORK_STUBS_H_
+#define UNITY_NETWORK_STUBS_H_
+
+#if !ENABLE_NETWORK
+typedef int RPCParameters;
+
+struct SystemAddress
+{
+ const char *ToString(bool writePort=true) const { return "dummy-system-address"; }
+ void SetBinaryAddress(const char *str) {}
+
+ unsigned int binaryAddress;
+ unsigned short port;
+};
+
+//typedef int NetworkPlayer;
+typedef int AssetToPrefab;
+typedef int NatPunchthrough;
+
+typedef unsigned int RakNetTime;
+typedef long long RakNetTimeNS;
+
+class RakPeerInterface;
+//class NetworkMessageInfo;
+
+//typedef int PlayerTable;
+//typedef int HostData;
+
+/*class Ping
+{
+ int m_Time;
+ bool m_IsDone;
+ std::string m_IP;
+// Mutex m_Mutex;
+
+ public:
+ int GetTime() { return m_Time; }
+ void SetTime(int value) { m_Time = value; }
+ int GetIsDone() { return m_IsDone; }
+ void SetIsDone(bool value) { m_IsDone=value; }
+ std::string GetIP() { return m_IP; }
+ void SetIP(std::string value) { m_IP = value; }
+};*/
+
+class BitstreamPacker
+{
+public:
+ template<class A>
+ void Serialize( A const& a ) {}
+
+ template<class A, class B>
+ void Serialize( A const& a, B const& b ) {}
+
+ bool IsReading() const { return false; }
+ bool IsWriting() const { return false; }
+};
+
+namespace RakNet {
+
+class BitStream;
+
+}
+
+typedef int PacketPriority;
+/*{
+ SYSTEM_PRIORITY, /// \internal Used by RakNet to send above-high priority messages.
+ HIGH_PRIORITY, /// High priority messages are send before medium priority messages.
+ MEDIUM_PRIORITY, /// Medium priority messages are send before low priority messages.
+ LOW_PRIORITY, /// Low priority messages are only sent when no other messages are waiting.
+ NUMBER_OF_PRIORITIES
+};*/
+
+
+#endif
+
+
+#endif // UNITY_NETWORK_STUBS_H_
diff --git a/Runtime/Network/NetworkUtility.cpp b/Runtime/Network/NetworkUtility.cpp
new file mode 100644
index 0000000..976366b
--- /dev/null
+++ b/Runtime/Network/NetworkUtility.cpp
@@ -0,0 +1,1076 @@
+#include "UnityPrefix.h"
+#include "NetworkUtility.h"
+
+#if UNITY_OSX
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+#include <errno.h>
+#include "External/RakNet/builds/include/GetTime.h"
+#elif UNITY_WIN
+#include "External/RakNet/builds/include/GetTime.h"
+#include <winsock2.h>
+#if UNITY_WINRT
+#include "PlatformDependent/MetroPlayer/MetroUtils.h"
+#endif
+#if !UNITY_WP8
+#include <iphlpapi.h>
+#include <windns.h>
+#endif
+#elif UNITY_WII || UNITY_PEPPER
+#pragma message("Dummy network")
+#elif UNITY_IPHONE || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+#include <ifaddrs.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#elif UNITY_PS3
+#include <netex/libnetctl.h>
+typedef unsigned long u_long;
+#elif UNITY_XENON
+#include <Xtl.h>
+#elif UNITY_ANDROID
+#include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+#include "PlatformDependent/AndroidPlayer/AndroidSystemInfo.h"
+#include "Runtime/Threads/Thread.h"
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <errno.h>
+#include <android/log.h>
+#if DEBUGMODE
+ #define TRACE_PING(...) __android_log_print(ANDROID_LOG_VERBOSE, "PING", __VA_ARGS__)
+#else
+ #define TRACE_PING(...)
+#endif
+#elif UNITY_FLASH
+#elif ENABLE_NETWORK
+#error "Unsupported platform"
+#endif
+
+
+#if ENABLE_NETWORK
+#include "Runtime/BaseClasses/MessageIdentifier.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "NetworkEnums.h"
+#include "External/RakNet/builds/include/GetTime.h"
+#include "External/RakNet/builds/include/SocketLayer.h"
+#include "Configuration/UnityConfigureVersion.h"
+#endif
+
+
+void NetworkInitialize ()
+{
+#if USE_WINSOCK_APIS
+ WSADATA WsaData;
+ WSAStartup( MAKEWORD(2,2), &WsaData );
+#endif
+}
+
+void NetworkCleanup ()
+{
+#if USE_WINSOCK_APIS
+ WSACleanup();
+#endif
+}
+
+
+std::string GetLocalIP()
+{
+ std::string returnValue = "0.0.0.0";
+
+ #if UNITY_OSX || UNITY_IPHONE || UNITY_LINUX || UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+
+ struct in_addr remote;
+ struct sockaddr_in raddress;
+ struct sockaddr *raddr = (struct sockaddr *)&raddress;
+
+ struct sockaddr_in laddress;
+ struct sockaddr *laddr = (struct sockaddr *)&laddress;
+
+ int sock;
+ int err;
+
+ std::string result;
+
+ sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
+ if ( sock < 1 ) {
+ perror("GetLocalIP: Error setting socket");
+ return returnValue;
+ }
+
+ // www.unity3d.com hardcoded
+ inet_aton("83.221.146.11", &remote);
+
+ raddress.sin_port = htons(80);
+ raddress.sin_family = AF_INET;
+ raddress.sin_addr = remote;
+
+ err = connect(sock, raddr, sizeof(raddress ));
+ if ( err < 0 ) {
+ perror("GetLocalIP: Error during connect");
+ char availableIPs[10][16];
+ int ipCount = GetIPs(availableIPs);
+ close(sock);
+ if (ipCount > 0) return availableIPs[0];
+ return returnValue;
+ }
+
+ socklen_t len = sizeof(laddress);
+ err = getsockname(sock, laddr, &len );
+ if ( err < 0 ) {
+ perror("GetLocalIP: Error using getsockname");
+ char availableIPs[10][16];
+ int ipCount = GetIPs(availableIPs);
+ close(sock);
+ if (ipCount > 0) return availableIPs[0];
+ return returnValue;
+ }
+
+ close(sock);
+
+ returnValue = std::string(inet_ntoa(laddress.sin_addr));
+
+ #elif UNITY_WIN
+
+ #if !UNITY_METRO
+
+ WSADATA WSAData;
+ SOCKET s;
+
+ // Initialize winsock
+ if( ::WSAStartup(MAKEWORD(2, 2), &WSAData) != 0 ) {
+ printf_console( "GetLocalIP: Failed to initialize winsock\n" );
+ return returnValue;
+ }
+
+ s = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
+ if ( s == INVALID_SOCKET ) {
+ printf_console("GetLocalIP: Error setting socket, %d", ::WSAGetLastError());
+ return returnValue;
+ }
+
+ struct sockaddr_in raddress;
+ struct sockaddr *raddr = (struct sockaddr *)&raddress;
+
+ struct sockaddr_in laddress;
+ struct sockaddr * laddr = (struct sockaddr *)&laddress;
+
+ int err;
+
+ // www.unity3d.com hardcoded
+ char target[16];
+ strcpy(target, "83.221.146.11");
+
+ raddress.sin_family = AF_INET;
+ raddress.sin_port = htons(80);
+ raddress.sin_addr.s_addr = inet_addr(target);
+
+ err = connect(s, raddr, sizeof(raddress ));
+ if ( err != 0 ) {
+ printf_console("GetLocalIP: Error during connect, %d ", ::WSAGetLastError());
+ return returnValue;
+ }
+
+ int len = sizeof(laddress);
+ err = getsockname(s, laddr, &len );
+ if ( err == SOCKET_ERROR ) {
+ printf_console("GetLocalIP: Error using getsockname, %d ", ::WSAGetLastError());
+ return returnValue;
+ }
+
+ closesocket(s);
+
+ ::WSACleanup();
+
+ returnValue = std::string(inet_ntoa(laddress.sin_addr));
+
+ #else
+
+ #pragma message("todo: implement") // ?!-
+
+ #endif
+
+ #elif UNITY_WII || UNITY_PS3 || UNITY_PEPPER || UNITY_FLASH
+
+ returnValue = "127.0.0.1";
+
+ #elif UNITY_XENON
+
+ XNADDR xboxAddr;
+ DWORD ret = XNetGetTitleXnAddr(&xboxAddr);
+ if ((ret != XNET_GET_XNADDR_PENDING) && // IP address can not be determined yet
+ (ret & XNET_GET_XNADDR_NONE) == 0) // IP address is not available
+ {
+ char buffer[64];
+ if (XNetInAddrToString(xboxAddr.ina, buffer, sizeof(buffer)) == 0)
+ {
+ returnValue = buffer;
+ }
+ }
+
+ #elif ENABLE_NETWORK
+ #error "Unsupported platform"
+ #endif
+
+ return returnValue;
+}
+
+int GetIPs( char ips[10][16] )
+{
+ memset( ips, 0, 10*16 );
+ int ipnumber = 0;
+
+#if UNITY_OSX || UNITY_IPHONE || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+
+ struct ifaddrs *myaddrs, *ifa;
+ struct sockaddr_in *addr;
+ int status;
+ status = getifaddrs(&myaddrs);
+ if (status != 0)
+ {
+ perror("Error getting interface addresses");
+ }
+
+ for (ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next)
+ {
+ if (ifa->ifa_addr == NULL) continue;
+ // Discard inactive interfaces
+ if ((ifa->ifa_flags & IFF_UP) == 0) continue;
+
+ // Get IPV4 address
+ if (ifa->ifa_addr->sa_family == AF_INET)
+ {
+ addr = (struct sockaddr_in *)(ifa->ifa_addr);
+ if (inet_ntop(ifa->ifa_addr->sa_family, (void *)&(addr->sin_addr), ips[ipnumber], sizeof(ips[ipnumber])) == NULL)
+ {
+ printf_console("%s: inet_ntop failed!\n", ifa->ifa_name);
+ }
+ else
+ {
+ if (strcmp(ips[ipnumber], "127.0.0.1")==0)
+ continue;
+ else
+ {
+ ipnumber++;
+ if (ipnumber == 10) break;
+ }
+ }
+ }
+ }
+
+ freeifaddrs(myaddrs);
+
+#elif UNITY_WIN
+
+ #if !UNITY_WINRT
+
+ PMIB_IPADDRTABLE ipAddrTable;
+ DWORD tableSize = 0;
+ IN_ADDR in_addr;
+
+ ipAddrTable = (MIB_IPADDRTABLE*)UNITY_MALLOC(kMemNetwork, sizeof(MIB_IPADDRTABLE));
+
+ if (ipAddrTable)
+ {
+ // If sizeof(MIB_IPADDRTABLE) is not enough, allocate appropriate size
+ if (GetIpAddrTable(ipAddrTable, &tableSize, 0) == ERROR_INSUFFICIENT_BUFFER)
+ {
+ UNITY_FREE(kMemNetwork, ipAddrTable);
+ ipAddrTable = (MIB_IPADDRTABLE*)UNITY_MALLOC(kMemNetwork, tableSize);
+ }
+ }
+ if (ipAddrTable == NULL)
+ {
+ printf_console("Memory allocation failed for GetIpAddrTable\n");
+ return 0;
+ }
+ else
+ {
+ // Get actual data
+ DWORD status = 0;
+ if ((status = GetIpAddrTable(ipAddrTable, &tableSize, 0)) != NO_ERROR)
+ {
+ printf_console("GetIpAddrTable failed with error %d\n", status);
+ LPVOID messageBuffer;
+ if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, status, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&messageBuffer, 0, NULL))
+ {
+ printf_console("Error: %s", messageBuffer);
+ LocalFree(messageBuffer);
+ UNITY_FREE(kMemNetwork, ipAddrTable);
+ ipAddrTable = NULL;
+ }
+ return 0;
+ }
+ }
+
+ for (int i = 0; i < (int)ipAddrTable->dwNumEntries; i++)
+ {
+ in_addr.S_un.S_addr = (u_long)ipAddrTable->table[i].dwAddr;
+ strcpy( ips[ipnumber], inet_ntoa(in_addr) );
+ if (strcmp(ips[ipnumber], "127.0.0.1") == 0)
+ continue;
+ else
+ {
+ ++ipnumber;
+ if (ipnumber == 10) break;
+ }
+ }
+
+ if (ipAddrTable)
+ {
+ UNITY_FREE(kMemNetwork, ipAddrTable);
+ ipAddrTable = NULL;
+ }
+
+ #else
+ using namespace Windows::Networking::Connectivity;
+
+ auto hostnames = NetworkInformation::GetHostNames();
+ for (int i = hostnames->Size-1; i >= 0; --i)
+ {
+ auto ipInfo = hostnames->GetAt(i)->IPInformation;
+
+ if (ipInfo == nullptr)
+ continue;
+
+ auto name = hostnames->GetAt(i)->CanonicalName;
+
+ // IPv4 only - TODO IPv6
+ if (name->Length() > 16)
+ continue;
+
+ strcpy( ips[ipnumber], ConvertStringToUtf8(name).c_str() );
+ ++ipnumber;
+ if (ipnumber == 10) break;
+ }
+ if (ipnumber < 10)
+ {
+ strcpy( ips[ipnumber], "127.0.0.1" );
+ ++ipnumber;
+ }
+ #endif
+
+#elif UNITY_WII || UNITY_PEPPER || UNITY_FLASH
+
+#elif UNITY_PS3
+ cellNetCtlInit();
+ CellNetCtlInfo info;
+ cellNetCtlGetInfo(CELL_NET_CTL_INFO_IP_ADDRESS, &info);
+ ipnumber = 1;
+ strcpy(&ips[0][0], info.ip_address);
+
+
+#elif UNITY_XENON
+
+ std::string localIp = GetLocalIP();
+ if (localIp.length() > 0 && localIp.length() < 16)
+ {
+ ipnumber = 1;
+ strcpy(&ips[0][0], localIp.c_str());
+ }
+
+#elif UNITY_ANDROID
+ struct ifconf ifc;
+ struct ifreq ifreqs[8];
+ memset(&ifc, 0, sizeof(ifc));
+ ifc.ifc_buf = (char*) (ifreqs);
+ ifc.ifc_len = sizeof(ifreqs);
+
+ struct ifreq* IFR;
+
+ int sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0)
+ {
+ printf_console("android.permission.INTERNET not available?");
+ return 0;
+ }
+
+ if ((ioctl(sock, SIOCGIFCONF, (char*) &ifc )) < 0 )
+ ifc.ifc_len = 0;
+
+ char* ifrp;
+ struct ifreq* ifr, ifr2;
+ IFR = ifc.ifc_req;
+ for (ifrp = ifc.ifc_buf;
+ (ifrp - ifc.ifc_buf) < ifc.ifc_len;
+ ifrp += sizeof(ifr->ifr_name) + sizeof(struct sockaddr))
+ {
+ ifr = (struct ifreq*)ifrp;
+
+ // Get network interface flags
+ ifr2 = *ifr;
+ if (ioctl(sock, SIOCGIFFLAGS, &ifr2) < 0)
+ continue;
+
+ // Discard inactive interfaces
+ if ((ifr2.ifr_flags & IFF_UP) == 0)
+ continue;
+
+ // Skip the loopback/localhost interface
+ if ((ifr2.ifr_flags & IFF_LOOPBACK))
+ continue;
+
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+
+ strcpy(&ips[ipnumber++][0], inet_ntoa(((struct sockaddr_in*)(&ifr->ifr_addr))->sin_addr));
+ if (ipnumber == 10) break;
+ }
+ close(sock);
+
+#elif ENABLE_NETWORK
+#error "Unsupported platform"
+#endif
+
+ return ipnumber;
+}
+
+std::string GetHostName ()
+{
+#if UNITY_OSX || (UNITY_WIN && !UNITY_WINRT) || UNITY_IPHONE
+ // Get local host name
+ char szHostName[128] = "";
+
+ if( ::gethostname(szHostName, sizeof(szHostName)) )
+ printf_console( "Failed to get host name - returning IP\n" );
+ else
+ return std::string(szHostName);
+#elif UNITY_WINRT
+ using namespace Windows::Networking::Connectivity;
+
+ auto hostnames = NetworkInformation::GetHostNames();
+ if (hostnames->Size > 0)
+ {
+ auto hostname = hostnames->GetAt(0)->DisplayName;
+ return ConvertStringToUtf8(hostname);
+ }
+#elif UNITY_XENON && !MASTER_BUILD
+
+ // Only available in development builds
+ char hostName[256] = "";
+ DWORD length = sizeof(hostName);
+ if (XBDM_NOERR == DmGetXboxName(hostName, &length))
+ return std::string(hostName);
+ else
+ printf_console( "Failed to get host name - returning IP\n" );
+#elif UNITY_ANDROID
+ return std::string(android::systeminfo::HardwareModel()) + ":" + GetLocalIP();
+#endif
+
+ return GetLocalIP();
+}
+
+
+
+
+#if ENABLE_SOCKETS && !UNITY_WINRT
+
+std::string InAddrToIP(sockaddr_in* in)
+{
+ #if UNITY_XENON
+ char ip[256] = "";
+ XNetInAddrToString(in->sin_addr, ip, sizeof(ip));
+ return std::string(ip);
+ #else
+ return std::string(inet_ntoa(in->sin_addr));
+ #endif
+}
+
+#endif
+
+#if ENABLE_NETWORK
+
+bool CompareToPrivateRange(u_long host_number)
+{
+ u_long network_number = htonl(host_number);
+
+ if ( (!(network_number > 167772160u && network_number < 184549375u) &&
+ !(network_number > 2851995648u && network_number < 2852061183u) &&
+ !(network_number > 2886729728u && network_number < 2887778303u) &&
+ !(network_number > 3232235520u && network_number < 3232301055u) ) &&
+ (network_number != 2130706433) )
+ return true;
+ return false;
+}
+
+bool CheckForPublicAddress()
+{
+ bool isPublic = false;
+
+ #if UNITY_OSX || UNITY_IPHONE || UNITY_LINUX || UNITY_ANDROID || UNITY_WIN || UNITY_BB10 || UNITY_TIZEN
+
+ char availableIPs[10][16];
+ int ipCount = GetIPs(availableIPs);
+ for (int i=0; i<ipCount; ++i)
+ {
+ #if UNITY_WIN
+ unsigned int address;
+ #else
+ in_addr_t address;
+ #endif
+ address = inet_addr(availableIPs[i]);
+ isPublic = CompareToPrivateRange((u_long)address);
+ }
+
+ #elif UNITY_WII || UNITY_PEPPER || UNITY_PS3 || UNITY_XENON
+
+ #else
+ #error "Unsupported platform"
+ #endif
+
+ return isPublic;
+}
+
+#if UNITY_WIN
+char* DNSQueryRecursive(const char* domainName)
+{
+ IN_ADDR ipaddr;
+ char* ipAddress;
+ PDNS_RECORD pDnsRecord = NULL;
+
+ DNS_STATUS status = DnsQuery(domainName, DNS_TYPE_A, DNS_QUERY_STANDARD, NULL, &pDnsRecord, NULL);
+
+ if (status)
+ printf_console("DNSLookup: Error looking up %s (%d)\n", domainName, status);
+ // If it's a CNAME then the A record is bogus, so lookup again
+ else if (pDnsRecord->wType == DNS_TYPE_CNAME)
+ {
+ char* aliasName;
+ size_t size = strlen(pDnsRecord->Data.CNAME.pNameHost);
+ ALLOC_TEMP(aliasName, char, size+1);
+ strncpy(aliasName, pDnsRecord->Data.CNAME.pNameHost, size+1);
+ DnsRecordListFree(pDnsRecord, DnsFreeRecordListDeep);
+ return DNSQueryRecursive(aliasName);
+ }
+ else
+ {
+ ipaddr.S_un.S_addr = (pDnsRecord->Data.A.IpAddress);
+ ipAddress = inet_ntoa(ipaddr);
+ DnsRecordListFree(pDnsRecord, DnsFreeRecordListDeep);
+ return ipAddress;
+ }
+
+ return NULL;
+}
+#endif
+
+
+char* DNSLookup(const char* domainName)
+{
+ char* ipAddress;
+
+#if UNITY_WIN
+ ipAddress = DNSQueryRecursive(domainName);
+ // Fall back to gethostbyname()
+ if (!ipAddress)
+#endif
+ ipAddress = const_cast<char*>(SocketLayer::Instance()->DomainNameToIP(domainName));
+
+ return ipAddress;
+}
+
+void ResolveAddress(SystemAddress& address, const char* domainName, const char* betaDomainName, const char* errorMessage)
+{
+ if (address.binaryAddress == 0)
+ {
+ char* resolvedAddress;
+ if (UNITY_IS_BETA)
+ resolvedAddress = DNSLookup(betaDomainName);
+ else
+ resolvedAddress = DNSLookup(domainName);
+ if (resolvedAddress)
+ address.SetBinaryAddress(resolvedAddress);
+ else
+ ErrorString(errorMessage);
+ }
+}
+
+unsigned short makeChecksum(unsigned short *buffer, int size)
+{
+ unsigned long cksum=0;
+ while (size > 1)
+ {
+ cksum += *buffer++;
+ size -= sizeof(unsigned short);
+ }
+ if (size)
+ {
+ cksum += *(unsigned char*)buffer;
+ }
+ cksum = (cksum >> 16) + (cksum & 0xffff);
+ cksum += (cksum >>16);
+ return (unsigned short)(~cksum);
+}
+
+
+#if UNITY_WIN
+/*
+// ICMP structures for Windows
+typedef struct {
+ unsigned char Ttl; // Time To Live
+ unsigned char Tos; // Type Of Service
+ unsigned char Flags; // IP header flags
+ unsigned char OptionsSize; // Size in bytes of options data
+ unsigned char *OptionsData; // Pointer to options data
+} IP_OPTION_INFORMATION, * PIP_OPTION_INFORMATION;
+
+typedef struct {
+ DWORD Address; // Replying address
+ unsigned long Status; // Reply status
+ unsigned long RoundTripTime; // RTT in milliseconds
+ unsigned short DataSize; // Echo data size
+ unsigned short Reserved; // Reserved for system use
+ void *Data; // Pointer to the echo data
+ IP_OPTION_INFORMATION Options; // Reply options
+} ICMP_ECHO_REPLY, * PICMP_ECHO_REPLY;
+*/
+
+// Wrapper for the ICMP library instance handle to make sure it unloads on quit.
+class PingLib
+{
+public:
+ PingLib() : hIcmp(NULL) {}
+
+ ~PingLib()
+ {
+ if(hIcmp)
+ FreeLibrary(hIcmp);
+ }
+
+ HINSTANCE getInstance() {
+ if (hIcmp == NULL)
+ {
+ hIcmp = LoadLibrary("icmp.dll");
+ if (hIcmp == 0)
+ printf_console("Unable to locate icmp.dll");
+ }
+ return hIcmp;
+ }
+private:
+ HINSTANCE hIcmp;
+};
+
+PingLib pingLib;
+
+#endif
+
+
+void* PingImpl(void* data)
+{
+ Ping& time = *(Ping*)data;
+
+ time.SetTime(-1);
+ time.SetIsDone(false);
+
+ #if UNITY_WIN
+
+ // Get an instance of the libary, it is only loaded once.
+ HINSTANCE hIcmp = pingLib.getInstance();
+
+ typedef HANDLE (WINAPI* pfnHV)(VOID);
+ typedef BOOL (WINAPI* pfnBH)(HANDLE);
+ typedef DWORD (WINAPI* pfnDHDPWPipPDD)(HANDLE, DWORD, LPVOID, WORD, PIP_OPTION_INFORMATION, LPVOID, DWORD, DWORD);
+ pfnHV pIcmpCreateFile;
+ pfnBH pIcmpCloseHandle;
+ pfnDHDPWPipPDD pIcmpSendEcho;
+ pIcmpCreateFile = (pfnHV)GetProcAddress(hIcmp, "IcmpCreateFile");
+ pIcmpCloseHandle = (pfnBH)GetProcAddress(hIcmp, "IcmpCloseHandle");
+ pIcmpSendEcho = (pfnDHDPWPipPDD)GetProcAddress(hIcmp, "IcmpSendEcho");
+ if ((pIcmpCreateFile == 0) || (pIcmpCloseHandle == 0) || (pIcmpSendEcho == 0))
+ {
+ printf_console("Failed to get proc addr info for ICMP functions.");
+ time.Release();
+ return NULL;
+ }
+
+ unsigned int targetAddress = inet_addr(time.GetIP().c_str());
+
+ HANDLE h = pIcmpCreateFile();
+ if (h == INVALID_HANDLE_VALUE) {
+ printf_console("Ping: Error creating icmp handle\n");
+ time.Release();
+ return NULL;
+ }
+
+ char pingBody[56];
+ memset(pingBody, 'X', 56);
+
+ LPVOID replyBuffer = malloc(sizeof(ICMP_ECHO_REPLY) + sizeof(pingBody));
+ if (!replyBuffer) {
+ printf_console("Ping: Error allocating reply buffer\n");
+ pIcmpCloseHandle(h);
+ time.Release();
+ return NULL;
+ }
+
+ unsigned int retval = pIcmpSendEcho(h, targetAddress, &pingBody, sizeof(pingBody), 0, replyBuffer, sizeof(ICMP_ECHO_REPLY) + sizeof(pingBody), 1000);
+ if (retval == 0)
+ {
+ printf_console("Ping: Error performing ICMP transmission. Possibly because of a timeout\n");
+ pIcmpCloseHandle(h);
+ free(replyBuffer);
+ time.Release();
+ return NULL;
+ }
+
+ PICMP_ECHO_REPLY rep = (PICMP_ECHO_REPLY)replyBuffer;
+
+ time.SetIsDone(true);
+ time.SetTime((int)rep->RoundTripTime);
+
+ pIcmpCloseHandle(h);
+ free(replyBuffer);
+
+ #elif UNITY_OSX || UNITY_IPHONE || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+
+ int err = 0;
+ char packet[64];
+ int psize = 64;
+ unsigned short checksum;
+ struct sockaddr_in address_in;
+ struct sockaddr *address = (struct sockaddr *)&address_in;
+
+ if (time.GetIP().empty())
+ {
+ ErrorString("No IP present in PingTime structure.");
+ time.Release();
+ return NULL;
+ }
+
+ address_in.sin_family = AF_INET;
+ address_in.sin_addr.s_addr = inet_addr(time.GetIP().c_str());
+
+ // Use a ICMP datagram type IPv4 socket
+ int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
+
+ if (s < 0)
+ {
+ perror ("Ping: Error creating socket");
+ time.Release();
+ return NULL;
+ }
+
+ // Set timeout for recv
+ struct timeval timeout;
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ err = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
+ if (err < 0)
+ {
+ perror("Ping: Error setting socket options");
+ time.Release();
+ return NULL;
+ }
+
+ err = connect(s, address, sizeof(address_in));
+ if (err < 0)
+ {
+ perror("Ping: Error during connect");
+ close(s);
+ time.Release();
+ return NULL;
+ }
+
+ // Prepare ping packet
+ memset(packet, 0, psize-1);
+ memset(packet+8, 'X', psize-9);
+ ((struct icmpheader*)packet)->type = 0x8;
+ checksum = makeChecksum( (unsigned short*)&packet, psize);
+ ((struct icmpheader*)packet)->checksum = checksum;
+
+ /*printf_console("Checksum is: %x\n", checksum);
+ printf_console("Packet contents: \n");
+ for (int i=0; i<psize-1; i++)
+ printf_console("%x", packet[i]);
+ printf_console("\n");*/
+
+ // NOTE: This is capable of doing multiple pings in a row with updated sequence numbers, but at the moment only a single ping is ever performed
+ RakNetTime currTime;
+ int count = 1;
+ int pingTime = -1;
+ for (int i=0; i < count; i++)
+ {
+ currTime = RakNet::GetTime();
+
+ // Send ping
+ err = send(s, (packet), psize, 0);
+ if ( err != psize )
+ {
+ perror("Ping: Error sending ICMP packet");
+ close(s);
+ time.Release();
+ return NULL;
+ }
+
+ // Receive reponse
+ char buffer[64];
+ err = recv(s, buffer, 64, 0);
+ if ( err < 0 )
+ {
+ // This is a timeout
+ perror("Ping: Error receiving ICMP packet response. Possibly a timeout.");
+ //lets just return -1 to keep it consistent with windows version
+ }
+ else if ( err == 0)
+ {
+ printf_console("Ping: Nothing to receive");
+ close(s);
+ time.Release();
+ return NULL;
+ }
+ else
+ {
+ pingTime = (int)(RakNet::GetTime() - currTime);
+ //printf_console("Ping: Ping nr. %d - Packet size is %d - Ping time %d ms\n", i, psize, pingTime);
+ }
+
+ // Update ping packet
+ ((struct icmpheader*)packet)->seq_num = htons(i+1);
+ ((struct icmpheader*)packet)->checksum = 0;
+ checksum = makeChecksum( (unsigned short*)&packet, psize);
+ ((struct icmpheader*)packet)->checksum = checksum;
+ }
+
+ // Stop receiving and transmitting
+ err = close(s);
+ if (err < 0)
+ perror ("Ping: Error closing socket");
+
+ time.SetTime(pingTime);
+ time.SetIsDone(true);
+
+ #elif UNITY_WII || UNITY_PEPPER || UNITY_PS3 || UNITY_XENON
+
+ data = 0;
+
+ #elif UNITY_ANDROID
+ // ICMP sockets are not available without root. The only protocols supported for applications are TCP and UDP.
+ // Because /system/bin/ping is a setuid program, we can use that to send ICMP packets.
+ // (using "/system/bin/ping -qnc 1 <addr>")
+
+ char ip[256];
+ strncpy(ip, time.GetIP().c_str(), sizeof(ip)); ip[sizeof(ip)-1] = 0;
+ time.SetTime(-1); // in case of error, return -1 to keep it consistent with windows version
+
+ // create stdout pipe from child process
+ int rwpipes[2];
+ if( pipe(rwpipes) )
+ {
+ ErrorStringMsg("Error creating pipe! (%s, %i)", strerror(errno), errno);
+ time.SetIsDone(true);
+ return NULL;
+ }
+ if( fcntl(rwpipes[0], F_SETFL, O_NONBLOCK) != 0 ||
+ fcntl(rwpipes[1], F_SETFL, O_NONBLOCK) != 0 )
+ {
+ ErrorStringMsg("Unable to set non-blocking pipe! (%s, %i)", strerror(errno), errno);
+ time.SetIsDone(true);
+ return NULL;
+ }
+
+ TRACE_PING("forking process @ %s:%i", __FUNCTION__, __LINE__);
+ // fork process
+ pid_t pid;
+ if( (pid=fork()) == -1)
+ {
+ ErrorStringMsg("Error forking process! (%s, %i)", strerror(errno), errno);
+ time.SetIsDone(true);
+ return NULL;
+ }
+
+ TRACE_PING("process forked @ %s:%i", __FUNCTION__, __LINE__);
+
+ if (!pid) // <child> process fork
+ {
+ TRACE_PING("secondary process @ %s:%i", __FUNCTION__, __LINE__);
+
+ TRACE_PING("dup'ing pipes @ %s:%i", __FUNCTION__, __LINE__);
+ dup2(rwpipes[1],1); // replace stdout with write pipe ..
+ dup2(rwpipes[1],2); // .. and stderr too ..
+
+ TRACE_PING("closing pipes @ %s:%i", __FUNCTION__, __LINE__);
+ close(rwpipes[0]); // .. close read pipe ..
+
+ TRACE_PING("exec'ing @ %s:%i", __FUNCTION__, __LINE__);
+ const char* ping = "/system/bin/ping";
+ if(execl(ping, ping, "-qnc", "1", ip, NULL) == -1)
+ {
+ printf("Error spawning child process '%s'! (%s, %i)", ping, strerror(errno), errno);
+ exit(1);
+ }
+
+ TRACE_PING("error? @ %s:%i", __FUNCTION__, __LINE__);
+ }
+
+ // <parent> process fork ...
+ TRACE_PING("parent fork @ %s:%i", __FUNCTION__, __LINE__);
+
+ // close write pipe
+ close(rwpipes[1]);
+
+ std::string output;
+ char buffer[256];
+
+ TRACE_PING("starting looping @ %s:%i", __FUNCTION__, __LINE__);
+
+ // loop until child process has died...
+ const double sleep_value = 0.5; // seconds
+ const double max_wait = 5.0; // seconds
+ int num_loops = (int)ceil(max_wait/sleep_value);
+ int ret_value;
+ while(waitpid(pid, &ret_value, WNOHANG) == 0)
+ {
+ TRACE_PING("reading @ %s:%i", __FUNCTION__, __LINE__);
+ int num_read = read(rwpipes[0], buffer, sizeof(buffer));
+ if (num_read > 0)
+ output.append(buffer, num_read);
+
+ TRACE_PING("sleeping # %i @ %s:%i", num_loops, __FUNCTION__, __LINE__);
+ Thread::Sleep(sleep_value);
+ if (num_loops-- < 0)
+ break;
+ }
+
+ // .. read any leftovers in the pipe ..
+ int num_read = read(rwpipes[0], buffer, sizeof(buffer));
+ if (num_read > 0)
+ output.append(buffer, num_read);
+
+ // ... and close read pipe
+ close(rwpipes[0]);
+
+ if (num_loops < 0)
+ {
+ TRACE_PING("pid killed @ %s:%i", __FUNCTION__, __LINE__);
+ kill(pid, SIGKILL);
+ ret_value = num_loops;
+ }
+
+ if (ret_value != 0)
+ {
+ ErrorString(output.c_str());
+ time.SetIsDone(true);
+ return NULL;
+ }
+
+ float ping_rtt;
+ size_t rtt_pos = output.rfind(" = ");
+ if (rtt_pos == std::string::npos || sscanf(&output[rtt_pos], "%*s%f", &ping_rtt) != 1)
+ {
+ ErrorStringMsg("Error parsing ping output!\n%s", output.c_str());
+ time.SetIsDone(true);
+ return NULL;
+ }
+
+ TRACE_PING("ping done %i @ %s:%i", (int)ping_rtt, __FUNCTION__, __LINE__);
+
+ time.SetTime((int)ping_rtt);
+ time.SetIsDone(true);
+
+ #else
+ #error "Unsupported platform"
+ #endif
+
+ time.Release();
+ return NULL;
+}
+
+Ping::Ping (const std::string& ip)
+{
+ m_Time = -1;
+ m_IsDone = false;
+ m_IP = ip;
+ m_Refcount = 1;
+}
+
+int Ping::GetTime()
+{
+ Mutex::AutoLock lock(m_Mutex);
+ return m_Time;
+}
+
+void Ping::SetTime(int value)
+{
+ Mutex::AutoLock lock(m_Mutex);
+ m_Time = value;
+}
+
+int Ping::GetIsDone()
+{
+ Mutex::AutoLock lock(m_Mutex);
+ return m_IsDone;
+}
+
+void Ping::SetIsDone(bool value)
+{
+ Mutex::AutoLock lock(m_Mutex);
+ m_IsDone = value;
+}
+
+std::string Ping::GetIP()
+{
+ Mutex::AutoLock lock(m_Mutex);
+ return m_IP;
+}
+
+void Ping::Retain()
+{
+ Mutex::AutoLock lock(m_Mutex);
+ m_Refcount++;
+}
+
+void Ping::Release()
+{
+ Mutex::AutoLock lock(m_Mutex);
+ m_Refcount--;
+ if (m_Refcount == 0)
+ delete this;
+}
+
+void SendToAllNetworkViews (const MessageIdentifier& msg, int inData)
+{
+ MessageData data;
+ data.SetData(inData, ClassID (int));
+ SendMessageToEveryone(msg, data);
+}
+
+void SendToAllNetworkViews (const MessageIdentifier& msg)
+{
+ MessageData data;
+ SendMessageToEveryone(msg, data);
+}
+
+// These are machine specific + depend on network connection, so we can't run them by default
+#if ENABLE_UNIT_TESTS && 0
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+TEST(ResolveAddressWorks)
+{
+ char* ip = DNSLookup("masterserver.unity3d.com");
+ const char* expected = "67.225.180.24";
+ bool isValid = strncmp(expected, ip, strlen(expected)) == 0;
+ CHECK(isValid);
+}
+
+TEST(CollectAllIPsWorks)
+{
+ char ips[10][16];
+ int count = GetIPs(ips);
+ printf ("Got %d ips\n", count);
+ for (int i=0; i<count; i++)
+ printf("\t%s\n", ips[i]);
+ CHECK(count == 3);
+}
+
+#endif // ENABLE_UNIT_TESTS
+
+#endif // ENABLE_NETWORK
diff --git a/Runtime/Network/NetworkUtility.h b/Runtime/Network/NetworkUtility.h
new file mode 100644
index 0000000..703ac28
--- /dev/null
+++ b/Runtime/Network/NetworkUtility.h
@@ -0,0 +1,74 @@
+#pragma once
+
+#include "Runtime/Threads/Mutex.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Network/Sockets.h"
+
+#if ENABLE_NETWORK
+#include "External/RakNet/builds/include/RakNetTypes.h"
+#endif
+
+void NetworkInitialize();
+void NetworkCleanup();
+
+std::string GetLocalIP();
+int GetIPs(char ips[10][16]);
+std::string GetHostName();
+
+#if ENABLE_SOCKETS && !UNITY_WINRT
+std::string InAddrToIP(sockaddr_in* in);
+#endif
+
+#if ENABLE_NETWORK
+
+class MessageIdentifier;
+
+bool CheckForPublicAddress();
+
+char* DNSLookup(const char* domainName);
+void ResolveAddress(SystemAddress& address, const char* domainName, const char* betaDomainName, const char* errorMessage);
+
+unsigned short makeChecksum(unsigned short *buffer, int size);
+
+void* PingImpl(void* data);
+
+void SendToAllNetworkViews (const MessageIdentifier& msg, int inData);
+void SendToAllNetworkViews (const MessageIdentifier& msg);
+
+// The structure of the ICMP header. Mainly used to reference locations inside the ping packet.
+struct icmpheader
+{
+ unsigned char type;
+ unsigned char code;
+ unsigned short checksum;
+ unsigned short identifier;
+ unsigned short seq_num;
+};
+
+
+class Ping
+{
+ int m_Time;
+ bool m_IsDone;
+ std::string m_IP;
+ int m_Refcount;
+ Mutex m_Mutex;
+
+ public:
+
+ Ping (const std::string& ip);
+
+ int GetTime();
+ void SetTime(int value);
+
+ int GetIsDone();
+ void SetIsDone(bool value);
+
+ std::string GetIP();
+
+ void Retain ();
+ void Release ();
+
+};
+
+#endif
diff --git a/Runtime/Network/NetworkView.cpp b/Runtime/Network/NetworkView.cpp
new file mode 100644
index 0000000..eff6f7d
--- /dev/null
+++ b/Runtime/Network/NetworkView.cpp
@@ -0,0 +1,733 @@
+#include "UnityPrefix.h"
+#include "NetworkView.h"
+
+#if ENABLE_NETWORK
+#include "NetworkManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "PackStateSpecialized.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Dynamics/RigidBody.h"
+#include "Runtime/Animation/Animation.h"
+#include "Runtime/Mono/MonoScriptCache.h"
+#include "Runtime/Utilities/Utility.h"
+#include "PackMonoRPC.h"
+#include "BitStreamPacker.h"
+#include "External/RakNet/builds/include/RakPeerInterface.h"
+
+NetworkView::NetworkView (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_Node (this)
+, m_AllNode(this)
+{
+ m_OwnerAddress.binaryAddress = 0;
+ m_StateSynchronization = kReliableDeltaCompressed;
+ m_Group = 0;
+ m_InitState.clear();
+ m_Scope.clear();
+ m_HasReceivedInitialState = false;
+}
+
+void NetworkView::Update()
+{
+}
+
+// This static RPC function executes the user scripted function by taking name and input from rpcParameters.
+// Name contains the function name called and input contains the parameters for the function (in a BitStream).
+static void NetworkViewRPCCallScript (RPCParameters *rpcParameters);
+static void NetworkViewRPCCallScript (RPCParameters *rpcParameters)
+{
+ NetworkManager& nm = GetNetworkManager ();
+ NetworkViewID viewID;
+ UInt8 mode = 0;
+ RakNet::BitStream input (rpcParameters->input, BITS_TO_BYTES(rpcParameters->numberOfBitsOfData), false);
+
+ viewID.Read(input);
+ input.ReadBits(&mode, kRPCModeNbBits);
+
+ NetworkLog(NULL, "Received RPC '%s'- mode %d - sender %s", rpcParameters->functionName, GetTargetMode(mode), rpcParameters->sender.ToString());
+ // Specific target handling.
+ // We might have to reroute the RPC call to another player.
+ if (GetTargetMode(mode) == kSpecificTarget)
+ {
+ bool relaySpecificTarget = false;
+ input.Read(relaySpecificTarget);
+
+ // We have to reroute the RPC call to another player
+ if (relaySpecificTarget)
+ {
+ NetworkPlayer relayTarget;
+ input.Read(relayTarget);
+
+ NetworkLog(NULL, "Relay RPC to specifc target - player ID %s", relayTarget);
+
+ RakNet::BitStream rerouted;
+ rerouted.Write(viewID);
+ rerouted.WriteBits(&mode, kRPCModeNbBits);
+ rerouted.Write0();
+
+ int unreadBits = input.GetNumberOfUnreadBits();
+ UInt8* data;
+ ALLOC_TEMP(data, UInt8, BITS_TO_BYTES (unreadBits));
+ input.ReadBits(data, unreadBits, false);
+ rerouted.WriteBits(data, unreadBits, false);
+
+ nm.PerformRPCRelaySpecific(rpcParameters->functionName, &rerouted, relayTarget);
+ return;
+ }
+ }
+
+ // Get the view and observer which should receive the rpc call
+ NetworkView* view = nm.ViewIDToNetworkView(viewID);
+ int group = 0;
+ if (view != NULL)
+ {
+ group = view->GetGroup();
+ }
+ else
+ {
+ NetworkWarning(NULL, "Could't invoke RPC function '%s' because the networkView '%s' doesn't exist", rpcParameters->functionName, viewID.ToString().c_str());
+ return;
+ }
+
+ // Unpack and invoke rpc call
+ if (nm.MayReceiveFromPlayer(rpcParameters->sender, group))
+ {
+ nm.PeformRPCRelayAll(rpcParameters->functionName, mode, viewID, group, rpcParameters->remoteTimestamp, rpcParameters->sender, input);
+ UnpackAndInvokeRPCMethod (view->GetGameObject(), rpcParameters->functionName, input, rpcParameters->sender, view->GetViewID(), rpcParameters->remoteTimestamp, view);
+ }
+ else
+ {
+ NetworkInfo (NULL, "RPC %s is ignored since the group of the network view is disabled.");
+ return;
+ }
+}
+
+void NetworkView::RPCCall (const std::string &function, int inMode, MonoArray* args)
+{
+ NetworkManager& nm = GetNetworkManager();
+ if (!nm.IsConnected())
+ {
+ NetworkError(NULL, "Can't send RPC function since no connection was started.");
+ return;
+ }
+
+ if (!nm.MaySend(m_Group))
+ {
+ NetworkInfo (NULL, "RPC %s is ignored since the group of its network view is disabled.");
+ return;
+ }
+
+ UInt8 mode = inMode;
+
+ RakNet::BitStream parameters;
+ m_ViewID.Write(parameters);
+ parameters.WriteBits(&mode, kRPCModeNbBits);
+
+ int parameterOffset = parameters.GetWriteOffset();
+
+ if (PackRPCParameters (GetGameObject(), function.c_str(), parameters, args, this))
+ {
+ // Call ourselves immediately
+ if (GetTargetMode(mode) == kAll)
+ {
+ parameters.SetReadOffset(parameterOffset);
+ UnpackAndInvokeRPCMethod (GetGameObject(), function.c_str(), parameters, nm.GetPlayerAddress(), GetViewID(), nm.GetTimestamp(), this);
+ }
+
+ // Send the RPC function via network
+ nm.PerformRPC(function, mode, parameters, m_ViewID, m_Group);
+ }
+ else
+ {
+ //NetworkError(NULL, "Could't relay remote function message to Unity object");
+ }
+}
+
+void NetworkView::RPCCallSpecificTarget (const std::string &function, NetworkPlayer target, MonoArray* args)
+{
+ NetworkManager& nm = GetNetworkManager();
+ if (!nm.IsConnected())
+ {
+ NetworkError(NULL, "Can't send RPC function since no connection was started.");
+ return;
+ }
+
+ if (!nm.MaySend(m_Group))
+ {
+ NetworkInfo (NULL, "RPC %s is ignored since the group of its network view is disabled.");
+ return;
+ }
+
+ UInt8 mode = kSpecificTarget;
+
+ RakNet::BitStream parameters;
+ m_ViewID.Write(parameters);
+ parameters.WriteBits(&mode, kRPCModeNbBits);
+
+ PlayerTable *targetPlayer = GetNetworkManager().GetPlayerEntry(target);;
+ if (targetPlayer != NULL)
+ {
+ parameters.Write0();
+ }
+ else if (GetNetworkManager().IsClient())
+ {
+ parameters.Write1();
+ parameters.Write(target);
+ targetPlayer = GetNetworkManager().GetPlayerEntry(0);
+ }
+ else
+ {
+ NetworkError(this, "Can't send RPC function because the target is not connected to the server.");
+ return;
+ }
+
+ if (PackRPCParameters (GetGameObject(), function.c_str(), parameters, args, this))
+ {
+ // Send the rpc function via network
+ nm.PerformRPCSpecificTarget(const_cast<char*> (function.c_str()), targetPlayer, parameters, m_Group);
+ }
+ else
+ {
+ //NetworkError(NULL, "Could't relay remote function message to Unity object");
+ }
+}
+
+
+// Add this network view to the network manager so it will be synchronized over the network.
+void NetworkView::AddToManager ()
+{
+ if (m_StateSynchronization != kNoStateSynch)
+ GetNetworkManager ().AddNetworkView (m_Node);
+ else
+ GetNetworkManager ().AddNonSyncNetworkView (m_Node);
+}
+
+void NetworkView::RemoveFromManager ()
+{
+ m_Node.RemoveFromList();
+}
+
+void NetworkView::SetupSceneViewID ()
+{
+ // When in edit mode ensure that all objects get a view id.
+ // In play mode we maintain view id's
+ if (!IsWorldPlaying())
+ {
+ // No view id assigned, allocate new one
+ if (m_ViewID == NetworkViewID())
+ {
+ if (GetNetworkManager().GetDebugLevel() >= kInformational) LogString("Allocating scene view ID to new object");
+ m_ViewID = GetNetworkManager ().AllocateSceneViewID();
+ }
+ else
+ {
+ m_ViewID = GetNetworkManager ().ValidateSceneViewID(this, m_ViewID);
+ }
+ }
+}
+
+void NetworkView::AwakeFromLoad (AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad (mode);
+
+ GetNetworkManager().AddAllNetworkView(m_AllNode);
+
+ if (IsActive())
+ SetupSceneViewID ();
+
+ // When loading a new scene replace level prefix of all objects that are being loaded!
+ if (mode & kDidLoadFromDisk)
+ {
+ if (m_ViewID.IsSceneID())
+ m_ViewID.ReplaceLevelPrefix(GetNetworkManager().GetLevelPrefix());
+ }
+
+ if (IsPrefabParent())
+ m_ViewID.SetAllocatedID(0);
+}
+
+void NetworkView::SetObserved (Unity::Component* component)
+{
+ m_Observed = component;
+ SetDirty();
+}
+
+Unity::Component* NetworkView::GetObserved ()
+{
+ return m_Observed;
+}
+
+void NetworkView::Unpack (RakNet::BitStream& bitStream, NetworkMessageInfo& info, int msgType)
+{
+ // Initial can always be received, but make sure that the last unpack state is clear
+ if (msgType == ID_STATE_INITIAL)
+ {
+ m_HasReceivedInitialState = true;
+ m_LastUnpackState.clear();
+ }
+ // Updates can only be received after some initial state was already sent! Except when delta compression is not being used.
+ else
+ {
+ if (!m_HasReceivedInitialState && m_StateSynchronization == kReliableDeltaCompressed)
+ {
+ NetworkError(NULL, "Received state update for view ID %s but no initial state has ever been sent. Ignoring message.\n", m_ViewID.ToString().c_str());
+ return;
+ }
+ }
+
+ // Calculate the last state to perform delta compression against!
+ PackState writeState;
+ PackState* writeStatePtr = NULL;
+ UInt8* readData = NULL;
+ int readSize = 0;
+ if (m_StateSynchronization == kReliableDeltaCompressed)
+ {
+ writeState.resize(m_LastUnpackState.size());
+ writeStatePtr = &writeState;
+ readData = &m_LastUnpackState[0];
+ readSize = m_LastUnpackState.size();
+ }
+
+ // Pack state
+ BitstreamPacker packer (bitStream, writeStatePtr, readData, readSize, true);
+
+ Unity::Component* observed = GetObserved ();
+ Rigidbody* body = dynamic_pptr_cast<Rigidbody*> (observed);
+ Transform* transform = dynamic_pptr_cast<Transform*> (observed);
+ Animation* animation = dynamic_pptr_cast<Animation*> (observed);
+ MonoBehaviour* mono = dynamic_pptr_cast<MonoBehaviour*> (observed);
+
+ if (body)
+ SerializeRigidbody(*body, packer);
+ else if (transform)
+ UnpackTransform(*transform, packer);
+ else if (animation)
+ SerializeAnimation(*animation, packer);
+ else if (mono)
+ SerializeMono(*mono, packer, info);
+ else if (observed)
+ {
+ ErrorStringObject ("Network View synchronization error. Received packet but the observed class is not supported as a synchronization type", this);
+ }
+ else
+ {
+ LogStringObject("Receiving state for an object whose network view exists but the observed object no longer exists", this);
+ }
+
+ NetworkLog(NULL, "Received state update for view ID %s\n", m_ViewID.ToString().c_str());
+
+ m_LastUnpackState.swap(writeState);
+}
+
+bool NetworkView::Pack(RakNet::BitStream &stream, PackState* writeStatePtr, UInt8* readData, int &readSize, int msgID)
+{
+ // Pack the state with the appropriate specialized packer
+ Unity::Component* observed = GetObserved ();
+ Rigidbody* body = dynamic_pptr_cast<Rigidbody*> (observed);
+ Transform* transform = dynamic_pptr_cast<Transform*> (observed);
+ Animation* animation = dynamic_pptr_cast<Animation*> (observed);
+ MonoBehaviour* mono = dynamic_pptr_cast<MonoBehaviour*> (observed);
+
+ bool doSend = false;
+ stream.Reset();
+
+ if (GetNetworkManager().GetUseProxy() && GetNetworkManager().IsClient())
+ {
+ stream.Write((unsigned char) ID_PROXY_CLIENT_MESSAGE);
+ }
+ // For now always include timestamp
+ bool useTimeStamp = true;
+ RakNetTime timeStamp = GetNetworkManager().GetTimestamp();
+ if (useTimeStamp)
+ {
+ stream.Write((unsigned char)ID_TIMESTAMP);
+ stream.Write(timeStamp);
+ }
+
+ // Msg type
+ stream.Write((unsigned char)msgID);
+ // View ID
+ m_ViewID.Write(stream);
+
+ // Pack data
+ BitstreamPacker packer (stream, writeStatePtr, readData, readSize, false);
+ NetworkMessageInfo info;
+ info.timestamp = -1.0;
+ info.sender = -1;
+ info.viewID = GetViewID();
+
+ if (body)
+ doSend |= SerializeRigidbody(*body, packer);
+ else if (transform)
+ doSend |= PackTransform(*transform, packer);
+ else if (animation)
+ doSend |= SerializeAnimation(*animation, packer);
+ else if (mono)
+ doSend |= SerializeMono(*mono, packer, info);
+ else if (observed)
+ {
+ NetworkError (this, "Network View synchronization of %s is not supported. Pack the state manually from a script.", this);
+ return false;
+ }
+
+ return doSend;
+}
+
+inline bool MayReceiveGroup (PlayerTable& table, int group)
+{
+ return (table.mayReceiveGroups & (1<<group)) != 0;
+}
+
+// When broadcast is enabled, every connected peer gets the message except the one given in the system address
+// TODO: Optimize code for initial/update proxied clients, they could go into seperate list(s) and get different streams (no memcpy)
+// Right now real stream is memcpd'd into a stream with proxy header for each client which is proxied
+void NetworkView::Send (SystemAddress systemAddress, bool broadcast)
+{
+ // Calculate the last state to perform delta compression against!
+ PackState writeState;
+ PackState* writeStatePtr = NULL;
+ UInt8* readData = NULL;
+ int readSize = 0;
+
+ typedef std::vector<PlayerTable> Addresses;
+ Addresses initialStateAddresses;
+ Addresses updateStateAddresses;
+
+ // If doing broadcast, check if there is any intial state which needs to be sent
+ std::vector<PlayerTable> players = GetNetworkManager().GetPlayerAddresses();
+ updateStateAddresses.reserve(players.size());
+
+ // Find the players which need update or initial state
+ for (int i = 0; i != players.size(); i++)
+ {
+ // Calculate if we should include address based:
+ // * broadcast flag and ignore address
+ // * single cast must match address
+ bool include = broadcast && systemAddress != players[i].playerAddress;
+ include |= !broadcast && systemAddress == players[i].playerAddress;
+ bool maySendToPlayer = GetNetworkManager().MaySendToPlayer(players[i].playerAddress, m_Group);
+
+ if (((include && maySendToPlayer) || GetNetworkManager().IsClient()) && CheckScope(players[i].initIndex) )
+ {
+ bool hasInitialState = GetInitStateStatus(players[i].initIndex);
+ if (!hasInitialState)
+ {
+ initialStateAddresses.push_back(players[i]);
+ SetInitState(players[i].initIndex, true);
+ }
+ else
+ {
+ updateStateAddresses.push_back(players[i]);
+ }
+ }
+ }
+
+ RakPeerInterface* peer = GetNetworkManager().GetPeer();
+
+ // Send update state data
+ if (!updateStateAddresses.empty())
+ {
+ if (m_StateSynchronization == kReliableDeltaCompressed)
+ {
+ writeState.clear();
+ writeState.reserve(m_LastPackState.size());
+ writeStatePtr = &writeState;
+ readData = &m_LastPackState[0];
+ readSize = m_LastPackState.size();
+ }
+ else
+ {
+ writeStatePtr = NULL;
+ readData = NULL;
+ readSize = 0;
+ }
+
+ RakNet::BitStream stream;
+ RakNet::BitStream relayStream;
+ bool doSend = Pack(stream, writeStatePtr, readData, readSize, ID_STATE_UPDATE);
+ if (doSend)
+ {
+ PacketReliability reliability;
+ if (m_StateSynchronization == kReliableDeltaCompressed)
+ reliability = RELIABLE_ORDERED;
+ else
+ reliability = UNRELIABLE_SEQUENCED;
+
+ for (int i=0;i<updateStateAddresses.size();i++)
+ {
+ if (updateStateAddresses[i].relayed == true)
+ {
+ relayStream.Reset();
+ relayStream.Write((MessageID) ID_PROXY_SERVER_MESSAGE);
+ relayStream.Write(updateStateAddresses[i].playerAddress);
+ relayStream.Write((char*)stream.GetData(), stream.GetNumberOfBytesUsed());
+ if (!peer->Send (&relayStream, (PacketPriority)HIGH_PRIORITY, reliability, kDefaultChannel, GetNetworkManager().GetProxyAddress(), false))
+ NetworkError (this, "Failed to send relayed state update");
+
+ NetworkLog(this, "Sending state update relay message through proxy, destination is %s", updateStateAddresses[i].playerAddress.ToString());
+ }
+ else
+ {
+ if (!peer->Send (&stream, (PacketPriority)HIGH_PRIORITY, reliability, kDefaultChannel, updateStateAddresses[i].playerAddress, false))
+ NetworkError (this, "Failed to send state update");
+ }
+ }
+
+ NetworkLog(this, "Sending generic state update, broadcast %s, view ID '%s'\n", (broadcast)?"on":"off", m_ViewID.ToString().c_str());
+
+ m_LastPackState.swap(writeState);
+ }
+ }
+
+ // Send initial state data
+ if (!initialStateAddresses.empty())
+ {
+ if (m_StateSynchronization == kReliableDeltaCompressed)
+ {
+ writeState.clear();
+ writeState.reserve(m_LastPackState.size());
+ writeStatePtr = &writeState;
+ readData = NULL;
+ readSize = 0;
+ }
+ else
+ {
+ writeStatePtr = NULL;
+ readData = NULL;
+ readSize = 0;
+ }
+
+ RakNet::BitStream stream;
+ RakNet::BitStream relayStream;
+
+ Pack(stream, writeStatePtr, readData, readSize, ID_STATE_INITIAL);
+
+ for (int i=0;i<initialStateAddresses.size();i++)
+ {
+ if (initialStateAddresses[i].relayed == true)
+ {
+ relayStream.Reset();
+ relayStream.Write((MessageID) ID_PROXY_SERVER_MESSAGE);
+ relayStream.Write(initialStateAddresses[i].playerAddress);
+ relayStream.Write((char*)stream.GetData(), stream.GetNumberOfBytesUsed());
+ if (!peer->Send (&relayStream, (PacketPriority)HIGH_PRIORITY, (PacketReliability)RELIABLE_ORDERED, kDefaultChannel, GetNetworkManager().GetProxyAddress(), false))
+ NetworkError (this, "Failed to send relayed initial update");
+
+ NetworkLog(NULL, "Sending initial state relay message through proxy, destination is %s", initialStateAddresses[i].playerAddress.ToString());
+ }
+ else
+ {
+ if (!peer->Send (&stream, (PacketPriority)HIGH_PRIORITY, RELIABLE_ORDERED, kDefaultChannel, initialStateAddresses[i].playerAddress, false))
+ NetworkError (this, "Failed to send initial update");
+ }
+ }
+
+ NetworkLog(this, "Sending generic initial state update, broadcast %s, view ID '%s'\n", (broadcast)?"on":"off", m_ViewID.ToString().c_str());
+
+ m_LastPackState.swap(writeState);
+ }
+
+}
+
+void NetworkView::SendToAllButOwner()
+{
+ Send(m_OwnerAddress, true);
+}
+
+NetworkViewID NetworkView::GetViewID()
+{
+ return m_ViewID;
+}
+
+void NetworkView::SetViewID(NetworkViewID viewID)
+{
+ NetworkManager& nm = GetNetworkManager();
+ NetworkLog(NULL, "Assigning a view ID: old view ID '%s', new view ID '%s'\n", m_ViewID.ToString().c_str(), viewID.ToString().c_str());
+
+ // If this viewID does not belong to my pool of IDs (i.e. another player)
+ if ( nm.WasViewIdAllocatedByMe(viewID) )
+ {
+ m_OwnerAddress = nm.GetPlayerAddress();
+ }
+ // Since this is our own view object, send our playerId address to server
+ else
+ {
+ // If we are a server, look up from player address table
+ if (nm.IsServer())
+ {
+ NetworkPlayer player = nm.GetNetworkViewIDOwner(viewID);
+ m_OwnerAddress = nm.GetSystemAddressFromIndex(player);
+ }
+ // If we are a client, we default to server owning, since we don't know the players anyway.
+ else
+ {
+ m_OwnerAddress.binaryAddress = 0;
+ }
+ }
+
+ // Make sure all current players get added into the scope of this network view
+ m_Scope.resize(nm.GetInitIndexSize(), true);
+
+ m_ViewID = viewID;
+}
+
+void NetworkView::SetGroup(unsigned group)
+{
+ if (group < kMaxGroups)
+ {
+ m_Group = group;
+ }
+ else
+ {
+ ErrorString("Groups must be between 0 and 31.");
+ }
+}
+
+void NetworkView::Reset ()
+{
+ Super::Reset();
+
+ if (!m_Observed && GetGameObjectPtr())
+ m_Observed = QueryComponent(Transform);
+}
+
+void NetworkView::SetStateSynchronization (int sync)
+{
+ m_StateSynchronization = sync;
+ SetDirty();
+}
+
+void NetworkView::SetInitState(int index, bool isSent)
+{
+ if (index < m_InitState.size())
+ {
+ m_InitState[index] = isSent;
+ NetworkInfo(NULL, "Initial state being sent to index %d", index);
+ }
+ else
+ {
+ if (isSent)
+ {
+ m_InitState.resize(index + 1, false);
+ m_InitState[index] = isSent;
+ }
+ }
+}
+
+bool NetworkView::GetInitStateStatus(int index)
+{
+ if (index < m_InitState.size())
+ {
+ return m_InitState[index];
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void NetworkView::ClearInitStateAndOwner()
+{
+ m_InitState.clear();
+ m_OwnerAddress.binaryAddress = 0;
+}
+
+// Get initIndex for player and set/unset according to bool
+bool NetworkView::SetPlayerScope(NetworkPlayer playerIndex, bool relevancy)
+{
+ std::vector<PlayerTable> players = GetNetworkManager().GetPlayerAddresses();
+ unsigned int initIndex = 0xFFFFFFFF;
+
+ // Find the players which need update or initial state
+ for (int i = 0; i != players.size(); i++)
+ {
+ if (playerIndex == players[i].playerIndex)
+ {
+ initIndex = players[i].initIndex;
+ break;
+ }
+ }
+
+ if (initIndex != 0xFFFFFFFF)
+ {
+ SetScope(initIndex, relevancy);
+ return true;
+ }
+ else
+ {
+ NetworkError(NULL, "Player index %d not found when setting scope in network view %s", playerIndex, m_ViewID.ToString().c_str());
+ return false;
+ }
+}
+
+void NetworkView::SetScope(unsigned int initIndex, bool relevancy)
+{
+ if (initIndex < m_Scope.size())
+ {
+ // Unset means this is in scope (so if relevancy==true => scope==0)
+ m_Scope[initIndex] = relevancy;
+ NetworkInfo(NULL, "Scope index %d is now %s scope for %s", initIndex, relevancy?"in":"out of", m_ViewID.ToString().c_str());
+ }
+ else
+ {
+ m_Scope.resize(initIndex + 1, false);
+ m_Scope[initIndex] = relevancy;
+ NetworkInfo(NULL, "New scope index %d is now %s scope for %s", initIndex, relevancy?"in":"out of", m_ViewID.ToString().c_str());
+ }
+}
+
+// Check if given index is in this network views scope
+bool NetworkView::CheckScope(int initIndex)
+{
+ if (initIndex < m_Scope.size())
+ {
+ return m_Scope[initIndex];
+ }
+ else
+ {
+ // Scope does not exist, create it with default value
+ SetScope(initIndex, true);
+ return true;
+ }
+}
+
+SystemAddress NetworkView::GetOwnerAddress ()
+{
+ return m_OwnerAddress;
+}
+
+NetworkView::~NetworkView ()
+{
+}
+
+
+
+template<class TransferFunc>
+void NetworkView::Transfer (TransferFunc& transfer) {
+ Super::Transfer (transfer);
+
+ TRANSFER(m_StateSynchronization);
+ TRANSFER(m_Observed);
+ transfer.Transfer(m_ViewID, "m_ViewID", kNotEditableMask);
+}
+
+void RegisterRPC (const char* name)
+{
+ GetNetworkManager().RegisterRPC(name, NetworkViewRPCCallScript);
+}
+
+void NetworkView::InitializeClass ()
+{
+ RegisterMonoRPC(RegisterRPC);
+}
+
+void NetworkView::CleanupClass ()
+{
+}
+
+
+IMPLEMENT_CLASS_HAS_INIT (NetworkView)
+IMPLEMENT_OBJECT_SERIALIZE (NetworkView)
+
+#endif // ENABLE_NETWORK
diff --git a/Runtime/Network/NetworkView.h b/Runtime/Network/NetworkView.h
new file mode 100644
index 0000000..6b9aae9
--- /dev/null
+++ b/Runtime/Network/NetworkView.h
@@ -0,0 +1,116 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_NETWORK
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "Configuration/UnityConfigure.h"
+#include <deque>
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Utilities/dynamic_bitset.h"
+#include "NetworkViewID.h"
+#include "NetworkEnums.h"
+#include "External/RakNet/builds/include/BitStream.h"
+
+
+
+struct MonoArray;
+enum {
+ kNoStateSynch = 0,
+ kReliableDeltaCompressed = 1,
+ kUnreliableBruteForce = 2
+};
+
+class NetworkView : public Behaviour
+{
+public:
+ typedef std::set<Component*> Components; // Declare the NetworkView container
+ typedef std::vector<UInt8> PackState;
+
+ REGISTER_DERIVED_CLASS (NetworkView, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (NetworkView)
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ NetworkView (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~NetworkView (); declared-by-macro
+
+ void Update();
+ void AwakeFromLoad (AwakeFromLoadMode mode);
+ void Reset ();
+
+ bool Pack(RakNet::BitStream &stream, PackState* writeStatePtr, UInt8* readData, int &readSize, int msgID);
+ void SendWithInitialState(std::vector<SystemAddress> &initAddresses, std::vector<SystemAddress> &normalAddresses, unsigned char msgID);
+ void Send(SystemAddress systemAddress, bool broadcast);
+ void SendToAllButOwner();
+
+ NetworkViewID GetViewID();
+
+ // Assign the given view ID to this view. Add the player address information
+ // to the view as appropriate (depening on owner/peer type)
+ void SetViewID(NetworkViewID viewID);
+
+ // Call the given user scripted RPC function. The mode defines if it should be
+ // buffered on the server and how it should be relayed. The array stores all the user
+ // defined variables.
+ void RPCCall (const std::string& function, int mode, MonoArray* args);
+
+ // Call the given user scripted RPC function. The mode defines if it should be
+ // buffered on the server and how it should be relayed. The array stores all the user
+ // defined variables.
+ void RPCCallSpecificTarget (const std::string &function, NetworkPlayer target, MonoArray* args);
+
+ SystemAddress GetOwnerAddress();
+
+ unsigned GetGroup() { return m_Group; }
+ void SetGroup (unsigned group);
+
+ void SetStateReliability(int reliability);
+ int GetStateReliability();
+
+ Unity::Component* GetObserved ();
+ void SetObserved (Unity::Component* component);
+
+ void Unpack (RakNet::BitStream& stream, NetworkMessageInfo& msgData, int msgType);
+ bool Pack (RakNet::BitStream& stream);
+
+ int GetStateSynchronization () { return m_StateSynchronization; }
+ void SetStateSynchronization (int sync);
+
+ void SetInitState(int index, bool isSent);
+ void GrowInitState();
+ bool GetInitStateStatus(int index);
+ void ClearInitStateAndOwner();
+
+ bool SetPlayerScope(NetworkPlayer playerIndex, bool relevancy);
+ void SetScope(unsigned int initIndex, bool relevancy);
+ bool CheckScope(int initIndex);
+
+private:
+ void SetupSceneViewID ();
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+
+ NetworkViewID m_ViewID;
+ PPtr<Unity::Component> m_Observed;
+ SystemAddress m_OwnerAddress; // The address of the player which owns this object. Used by server when relaying to other players.
+ int m_Group;
+ int m_StateSynchronization; ///< enum { Off = 0, Reliable Delta Compressed = 1, Unreliable = 2 }
+
+ // Pack state for delta compression
+ // We need seperate pack / unpack states because the server can receive & send at the same time.
+ PackState m_LastPackState;
+ PackState m_LastUnpackState;
+ dynamic_bitset m_InitState;
+ dynamic_bitset m_Scope;
+
+ ListNode<NetworkView> m_Node;
+ ListNode<NetworkView> m_AllNode;
+ bool m_HasReceivedInitialState;
+};
+
+#endif
diff --git a/Runtime/Network/NetworkViewID.cpp b/Runtime/Network/NetworkViewID.cpp
new file mode 100644
index 0000000..14356f1
--- /dev/null
+++ b/Runtime/Network/NetworkViewID.cpp
@@ -0,0 +1,193 @@
+#include "UnityPrefix.h"
+#include "NetworkViewID.h"
+
+#if ENABLE_NETWORK
+#include "Runtime/Serialize/SwapEndianBytes.h"
+
+enum {
+ k4Bits = (1 << 4) - 1,
+ k10Bits = (1 << 10) - 1,
+ k14Bits = (1 << 14) - 1,
+ k15Bits = (1 << 15) - 1,
+ k29Bits = (1 << 29) - 1
+};
+
+void NetworkViewID::Write (RakNet::BitStream& stream)
+{
+ UInt32 endianSceneID = m_ID;
+ UInt32 endianLevelPrefix = m_LevelPrefix;
+
+ #if UNITY_BIG_ENDIAN
+ SwapEndianBytes(endianSceneID);
+ SwapEndianBytes(endianLevelPrefix);
+ #endif
+
+ if (m_Type == kAllocatedID)
+ {
+ // 16 bit mode
+ if (m_ID <= k14Bits)
+ {
+ stream.Write0();// 16 bit mode
+ stream.Write1();// allocated
+ stream.WriteBits(reinterpret_cast<UInt8*>(&endianSceneID), 14);
+ }
+ // 32 bit mode
+ else if (m_ID <= k29Bits)
+ {
+ stream.Write1();// 32 bit mode
+ stream.Write0();// 32 bit mode
+ stream.Write1();// allocated
+ stream.WriteBits(reinterpret_cast<UInt8*>(&endianSceneID), 29);
+ }
+ // 64 bit mode
+ else
+ {
+ AssertString("64 bit mode not supported yet");
+ }
+ }
+ else
+ {
+ // 16 bit mode
+ if (m_ID <= k10Bits && m_LevelPrefix <= k4Bits)
+ {
+ stream.Write0();// 16 bit mode
+ stream.Write0();// allocated
+ stream.WriteBits(reinterpret_cast<UInt8*>(&endianLevelPrefix), 4);
+ stream.WriteBits(reinterpret_cast<UInt8*>(&endianSceneID), 10);
+ }
+ // 32 bit mode
+ else if (m_ID <= k14Bits && m_LevelPrefix <= k15Bits)
+ {
+ stream.Write1();// 32 bit mode
+ stream.Write0();// 32 bit mode
+ stream.Write0();// allocated
+ stream.WriteBits(reinterpret_cast<UInt8*>(&endianLevelPrefix), 15);
+ stream.WriteBits(reinterpret_cast<UInt8*>(&endianSceneID), 14);
+ }
+ // 64 bit mode
+ else
+ {
+ AssertString("64 bit mode not supported yet");
+ }
+ }
+}
+
+bool NetworkViewID::Read (RakNet::BitStream& stream)
+{
+ m_ID = 0;
+ m_LevelPrefix = 0;
+ m_Type = 0;
+
+ if (stream.GetNumberOfUnreadBits() < 16)
+ return false;
+
+ int size = 16;
+ int bitsRead = 1;
+ if (stream.ReadBit())
+ {
+ size = 32;
+ bitsRead++;
+ if (stream.ReadBit())
+ size = 64;
+
+ if (stream.GetNumberOfUnreadBits() < size-bitsRead)
+ {
+ AssertString(Format("Only %d bits left, but expected %d\n", stream.GetNumberOfUnreadBits(), size-bitsRead));
+ return false;
+ }
+ }
+
+ bool isAllocated = stream.ReadBit();
+
+ if (isAllocated)
+ {
+ m_Type = kAllocatedID;
+
+ if (size == 16)
+ stream.ReadBits(reinterpret_cast<UInt8*>(&m_ID), 14);
+ else if (size == 32)
+ stream.ReadBits(reinterpret_cast<UInt8*>(&m_ID), 29);
+ else
+ AssertString("Unsupported allocated view ID size");
+ }
+ else
+ {
+ m_Type = kSceneID;
+ if (size == 16)
+ {
+ stream.ReadBits(reinterpret_cast<UInt8*>(&m_LevelPrefix), 4);
+ stream.ReadBits(reinterpret_cast<UInt8*>(&m_ID), 10);
+ }
+ else if (size == 32)
+ {
+ stream.ReadBits(reinterpret_cast<UInt8*>(&m_LevelPrefix), 15);
+ stream.ReadBits(reinterpret_cast<UInt8*>(&m_ID), 14);
+ }
+ else
+ {
+ AssertString("Unsupported scene view ID size");
+ }
+ }
+
+ #if UNITY_BIG_ENDIAN
+ SwapEndianBytes(m_LevelPrefix);
+ SwapEndianBytes(m_ID);
+ #endif
+
+ return true;
+}
+
+void NetworkViewID::SetSceneID (UInt32 sceneID)
+{
+ m_Type = kSceneID;
+ m_LevelPrefix = 0;
+ m_ID = sceneID;
+}
+
+void NetworkViewID::SetAllocatedID (UInt32 sceneID)
+{
+ m_Type = kAllocatedID;
+ m_LevelPrefix = 0;
+ m_ID = sceneID;
+}
+
+void NetworkViewID::ReplaceLevelPrefix (UInt32 levelPrefix)
+{
+ AssertIf(m_Type != kSceneID);
+ m_LevelPrefix = levelPrefix;
+}
+
+bool operator < (const NetworkViewID& lhs, const NetworkViewID& rhs)
+{
+ if (lhs.m_ID != rhs.m_ID)
+ return lhs.m_ID < rhs.m_ID;
+ else
+ {
+ if (lhs.m_LevelPrefix != rhs.m_LevelPrefix)
+ return lhs.m_LevelPrefix < rhs.m_LevelPrefix;
+ else
+ return lhs.m_Type < rhs.m_Type;
+ }
+}
+
+std::string NetworkViewID::ToString () const
+{
+ char buffer[128];
+ if (m_Type == kSceneID)
+ sprintf(buffer, "SceneID: %lu Level Prefix: %lu", m_ID, m_LevelPrefix);
+ else
+ sprintf(buffer, "AllocatedID: %ld", m_ID);
+ return buffer;
+}
+
+bool operator == (const NetworkViewID& lhs, const NetworkViewID& rhs)
+{
+ return lhs.m_ID == rhs.m_ID && lhs.m_LevelPrefix == rhs.m_LevelPrefix && lhs.m_Type == rhs.m_Type;
+}
+
+bool operator != (const NetworkViewID& lhs, const NetworkViewID& rhs)
+{
+ return lhs.m_ID != rhs.m_ID || lhs.m_LevelPrefix != rhs.m_LevelPrefix || lhs.m_Type != rhs.m_Type;
+}
+
+#endif
diff --git a/Runtime/Network/NetworkViewID.h b/Runtime/Network/NetworkViewID.h
new file mode 100644
index 0000000..903a953
--- /dev/null
+++ b/Runtime/Network/NetworkViewID.h
@@ -0,0 +1,56 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_NETWORK
+#include "External/RakNet/builds/include/BitStream.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+
+struct NetworkViewID
+{
+ private:
+
+ UInt32 m_LevelPrefix;
+ UInt32 m_ID;
+ UInt32 m_Type;///< enum { Allocated = 0, Scene = 1 }
+
+ public:
+
+ DECLARE_SERIALIZE(NetworkViewID)
+
+ NetworkViewID () :
+ m_LevelPrefix (0),
+ m_ID (0),
+ m_Type (0)
+ { }
+
+ enum { kAllocatedID = 0, kSceneID = 1 };
+
+ // Pack the view id into a 16 bit or 32 bit number based on how big the number is
+ void Write (RakNet::BitStream& stream);
+ bool Read (RakNet::BitStream& stream);
+
+ friend bool operator < (const NetworkViewID& lhs, const NetworkViewID& rhs);
+ friend bool operator != (const NetworkViewID& lhs, const NetworkViewID& rhs);
+ friend bool operator == (const NetworkViewID& lhs, const NetworkViewID& rhs);
+
+ void SetSceneID (UInt32 sceneID);
+ void SetAllocatedID (UInt32 sceneID);
+ bool IsSceneID() { return m_Type == kSceneID; }
+ void ReplaceLevelPrefix (UInt32 levelPrefix);
+
+ UInt32 GetIndex () const { return m_ID; }
+
+ std::string ToString() const;
+
+ static NetworkViewID GetUnassignedViewID() { NetworkViewID viewID; return viewID; }
+};
+
+template<class TransferFunc>
+void NetworkViewID::Transfer (TransferFunc& transfer)
+{
+ TRANSFER(m_ID);
+ TRANSFER(m_Type);
+}
+
+#endif
diff --git a/Runtime/Network/NetworkViewIDAllocator.cpp b/Runtime/Network/NetworkViewIDAllocator.cpp
new file mode 100644
index 0000000..95e0aaa
--- /dev/null
+++ b/Runtime/Network/NetworkViewIDAllocator.cpp
@@ -0,0 +1,130 @@
+#include "UnityPrefix.h"
+#include "NetworkViewIDAllocator.h"
+
+#if ENABLE_NETWORK
+
+NetworkViewIDAllocator::NetworkViewIDAllocator()
+{
+ Clear(kDefaultViewIDBatchSize, kMinimumViewIDs, 0, kUndefindedPlayerIndex);
+}
+
+UInt32 NetworkViewIDAllocator::AllocateBatch (NetworkPlayer player)
+{
+ UInt32 batchIndex = m_AllocatedViewIDBatches.size();
+ m_AllocatedViewIDBatches.push_back(player);
+ return batchIndex;
+}
+
+NetworkViewID NetworkViewIDAllocator::AllocateViewID ()
+{
+ if (!m_AvailableBatches.empty())
+ {
+ AvailableBatch& batch = m_AvailableBatches.front();
+
+ NetworkViewID viewID;
+ viewID.SetAllocatedID(batch.first);
+ batch.first++;
+ batch.count--;
+ if (batch.count == 0)
+ m_AvailableBatches.erase(m_AvailableBatches.begin());
+
+ return viewID;
+ }
+ else
+ {
+ return NetworkViewID();
+ }
+}
+
+
+NetworkPlayer NetworkViewIDAllocator::FindOwner (NetworkViewID viewID)
+{
+ if (viewID.IsSceneID())
+ {
+ return m_ServerPlayer;
+ }
+ else
+ {
+ UInt32 index = viewID.GetIndex();
+ index /= m_BatchSize;
+
+ // On Clients we use received batches. On the client we can only find out if the batch is ours.
+ // Otherwise it defaults to the server
+ if (!m_ReceivedBatches.empty())
+ {
+ for (ReceivedBatches::iterator i=m_ReceivedBatches.begin();i != m_ReceivedBatches.end();i++)
+ {
+ if (*i == index)
+ return m_ClientPlayer;
+ }
+
+ return m_ServerPlayer;
+ }
+ // The Server allocates all network view id's so he knows all players for all view ids
+ else
+ {
+ if (index < m_AllocatedViewIDBatches.size())
+ return m_AllocatedViewIDBatches[index];
+ else
+ return kUndefindedPlayerIndex;
+ }
+
+ return 0;
+ }
+}
+
+void NetworkViewIDAllocator::FeedAvailableBatchOnClient (UInt32 batchIndex)
+{
+ m_ReceivedBatches.push_back(batchIndex);
+
+ AvailableBatch batch;
+ batch.first = batchIndex * m_BatchSize;
+ batch.count = m_BatchSize;
+ m_AvailableBatches.push_back(batch);
+}
+
+void NetworkViewIDAllocator::FeedAvailableBatchOnServer (UInt32 batchIndex)
+{
+ AvailableBatch batch;
+ batch.first = batchIndex * m_BatchSize;
+ batch.count = m_BatchSize;
+ if (batchIndex == 0)
+ {
+ batch.first++;
+ batch.count--;
+ }
+ m_AvailableBatches.push_back(batch);
+}
+
+int NetworkViewIDAllocator::ShouldRequestMoreBatches ()
+{
+ // Count how many id's we have left
+ int viewIDsLeft = 0;
+ for (int i=0;i<m_AvailableBatches.size();i++)
+ viewIDsLeft += m_AvailableBatches[i].count;
+
+ // Do we need to request new id batches
+ viewIDsLeft += m_BatchSize * m_RequestedBatches;
+ if (viewIDsLeft < m_MinAvailableViewIDs)
+ {
+ int extraRequiredViewIDs = m_MinAvailableViewIDs - viewIDsLeft;
+ int requestedBatches = ((extraRequiredViewIDs -1) / m_BatchSize) + 1;
+ return requestedBatches;
+ }
+
+ return 0;
+}
+
+void NetworkViewIDAllocator::Clear (int batchSize, int minimumViewIDs, NetworkPlayer server, NetworkPlayer client)
+{
+ m_MinAvailableViewIDs = minimumViewIDs;
+ m_BatchSize = batchSize;
+ m_AllocatedViewIDBatches.clear();
+ m_AvailableBatches.clear();
+ m_ReceivedBatches.clear();
+ m_RequestedBatches = 0;
+ m_ClientPlayer = client;
+ m_ServerPlayer = server;
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/Network/NetworkViewIDAllocator.h b/Runtime/Network/NetworkViewIDAllocator.h
new file mode 100644
index 0000000..124feb5
--- /dev/null
+++ b/Runtime/Network/NetworkViewIDAllocator.h
@@ -0,0 +1,58 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_NETWORK
+#include "NetworkEnums.h"
+
+class NetworkViewIDAllocator
+{
+
+ struct AvailableBatch
+ {
+ UInt32 first;
+ UInt32 count;
+ };
+
+ typedef std::vector<NetworkPlayer> AllocatedViewIDBatches;
+ AllocatedViewIDBatches m_AllocatedViewIDBatches; // Used by the server to track who own which NetworkViewID's
+
+ typedef std::vector<UInt32> ReceivedBatches;
+ ReceivedBatches m_ReceivedBatches; // Used by the client to track which batches were received by him.
+
+ typedef std::vector<AvailableBatch> AvailableBatches;
+ AvailableBatches m_AvailableBatches; // Used by client/server to allocate ViewID's from
+
+ int m_BatchSize;
+ int m_MinAvailableViewIDs; // We always make sure that m_MinAvailableViewIDs are around. If not we request more view id's
+ int m_RequestedBatches;
+ NetworkPlayer m_ClientPlayer;
+ NetworkPlayer m_ServerPlayer;
+
+ public:
+
+ NetworkViewIDAllocator();
+
+ void Clear (int batchSize, int minimumViewIDs, NetworkPlayer server, NetworkPlayer client);
+
+ NetworkViewID AllocateViewID ();
+
+ UInt32 AllocateBatch (NetworkPlayer player);
+
+ void FeedAvailableBatchOnClient (UInt32 batchIndex);
+ void FeedAvailableBatchOnServer (UInt32 batchIndex);
+ UInt32 GetBatchSize () { return m_BatchSize; }
+
+ // How many more view id batches should be requested!
+ // You are expected to actually request or allocate those view id's
+ int ShouldRequestMoreBatches ();
+ void AddRequestedBatches (int requestedBatches) { m_RequestedBatches += requestedBatches; }
+
+ void SetMinAvailableViewIDs(int size) { m_MinAvailableViewIDs = size; }
+
+ /// On Server: Find Owner returns the player who allocated the view id. If it hasn't been allocated, it returns kUndefindedPlayerIndex.
+ /// On Clients: FindOwner returns the clients player ID for its own objects and otherwise the server, since he can't possibly know the owner.
+ NetworkPlayer FindOwner (NetworkViewID viewID);
+};
+
+#endif \ No newline at end of file
diff --git a/Runtime/Network/OnlineServices.h b/Runtime/Network/OnlineServices.h
new file mode 100644
index 0000000..6b18509
--- /dev/null
+++ b/Runtime/Network/OnlineServices.h
@@ -0,0 +1,10 @@
+#pragma once
+
+// Disable these features on xbox for now
+#if 0
+# define VOID_IMPL ;
+# define RET_IMPL(value) ;
+#else
+# define VOID_IMPL {}
+# define RET_IMPL(value) { return value; }
+#endif
diff --git a/Runtime/Network/PackMonoRPC.cpp b/Runtime/Network/PackMonoRPC.cpp
new file mode 100644
index 0000000..08b16e0
--- /dev/null
+++ b/Runtime/Network/PackMonoRPC.cpp
@@ -0,0 +1,488 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_NETWORK
+#include "NetworkManager.h"
+#include "PackMonoRPC.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Allocator/LinearAllocator.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "External/RakNet/builds/include/StringCompressor.h"
+#include "BitStreamPacker.h"
+#include "Runtime/Scripting/Backend/ScriptingMethodRegistry.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#if UNITY_WIN
+#include <malloc.h> // alloca
+#endif
+
+#include "Runtime/Scripting/Scripting.h"
+
+enum RPCFindResult { kRPCFailure = -1, kRPCNotFound = 0, kRPCFound = 1 };
+
+static RPCFindResult FindRPCMethod (Object* target, const char* function, MonoMethod** outMethod, Object* netview);
+
+static RPCFindResult FindRPCMethod (Object* target, const char* function, MonoMethod** outMethod, Object* netview)
+{
+ // We need the target to be a script
+ MonoBehaviour* behaviour = dynamic_pptr_cast<MonoBehaviour*> (target);
+ if (behaviour == NULL)
+ {
+ ErrorStringObject ("RPC call failed because the observed object is not a script.", netview);
+ return kRPCFailure;
+ }
+
+ // We need the rpc method to exist
+ ScriptingMethodPtr method = behaviour->FindMethod (function);
+ if (method == NULL)
+ {
+ if (behaviour->GetInstance() != NULL)
+ {
+ return kRPCNotFound;
+ }
+ else
+ {
+ ErrorStringObject (Format("RPC call failed because the script couldn't be loaded. The function was '%s'.", function), netview);
+ return kRPCFailure;
+ }
+ }
+
+ bool hasRPCAttribute = scripting_method_has_attribute (method, GetMonoManager().GetCommonClasses().RPC);
+ if (!hasRPCAttribute)
+ {
+ const char* className = mono_class_get_name(mono_method_get_class(method->monoMethod));
+ ErrorStringObject (Format("RPC call failed because the function '%s' in '%s' does not have the RPC attribute. You need to add the RPC attribute in front of the function declaration", function, className), netview);
+ return kRPCFailure;
+ }
+
+ *outMethod = method->monoMethod;
+
+ return kRPCFound;
+}
+
+template<class T>
+void UnpackBuiltinValue (BitstreamPacker& stream, ForwardLinearAllocator& fwdallocator, void*& outData)
+{
+ outData = fwdallocator.allocate(sizeof(T));
+ stream.Serialize (*reinterpret_cast<T*>(outData));
+}
+
+template<class T>
+void UnpackBuiltinMaxError (BitstreamPacker& stream, ForwardLinearAllocator& fwdallocator, void*& outData)
+{
+ outData = fwdallocator.allocate(sizeof(T));
+ stream.Serialize (*reinterpret_cast<T*>(outData), 0.0F);
+}
+
+
+bool UnpackString (RakNet::BitStream& stream, void*& outData)
+{
+ char rawOut[4096];
+
+ if (StringCompressor::Instance()->DecodeString(rawOut, 4096, &stream))
+ {
+ outData = MonoStringNew(rawOut);
+ return true;
+ }
+ else
+ {
+ outData = NULL;
+ return false;
+ }
+}
+
+void PackString (RakNet::BitStream& stream, MonoObject* str)
+{
+ std::string cppStr = MonoStringToCpp((MonoString*)str);
+ if (cppStr.size() >= 4096)
+ {
+ ErrorString("Strings sent via RPC calls may not be larger than 4096 UTF8 characters");
+ }
+
+ StringCompressor::Instance()->EncodeString(cppStr.c_str(), 4096, &stream);
+}
+
+bool UnpackAndInvokeRPCMethod (GameObject& target, const char* name, RakNet::BitStream& parameters, SystemAddress sender, NetworkViewID viewID, RakNetTime timestamp, Object* netview)
+{
+ int readoffset = parameters.GetReadOffset();
+ bool invokedAny = false;
+ for (int i=0;i<target.GetComponentCount();i++)
+ {
+ if (target.GetComponentClassIDAtIndex(i) != ClassID(MonoBehaviour))
+ continue;
+
+ MonoBehaviour* targetBehaviour = static_cast<MonoBehaviour*> (&target.GetComponentAtIndex(i));
+ MonoMethod* method;
+ RPCFindResult findResult = FindRPCMethod (targetBehaviour, name, &method, netview);
+ if (findResult == kRPCNotFound)
+ ;
+ else if (findResult == kRPCFound)
+ {
+ parameters.SetReadOffset(readoffset);
+ if (UnpackAndInvokeRPCMethod (*targetBehaviour, method, parameters, sender, viewID, timestamp, netview))
+ return false;
+
+ invokedAny = true;
+ }
+ else
+ return false;
+ }
+
+ if (!invokedAny)
+ {
+ ErrorStringObject (Format("RPC call failed because the function '%s' does not exist in any script attached to'%s'", name, target.GetName()), netview);
+ }
+
+ return invokedAny;
+}
+
+bool PackRPCParameters (GameObject& target, const char* name, RakNet::BitStream& inStream, MonoArray* data, Object* netview)
+{
+ bool invokedAny = false;
+ for (int i=0;i<target.GetComponentCount();i++)
+ {
+ if (target.GetComponentClassIDAtIndex(i) != ClassID(MonoBehaviour))
+ continue;
+
+ MonoBehaviour* targetBehaviour = static_cast<MonoBehaviour*> (&target.GetComponentAtIndex(i));
+ MonoMethod* method;
+ RPCFindResult findResult = FindRPCMethod (targetBehaviour, name, &method, netview);
+
+ if (findResult == kRPCNotFound)
+ ;
+ else if (findResult == kRPCFound)
+ {
+ if (!PackRPCParameters (*targetBehaviour, method, inStream, data, netview, !invokedAny))
+ return false;
+ invokedAny = true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ if (!invokedAny)
+ {
+ ErrorStringObject (Format("RPC call failed because the function '%s' does not exist in the any script attached to'%s'", name, target.GetName()), netview);
+ }
+
+ return invokedAny;
+}
+
+static bool IsValidRPCParameterArrayType(MonoClass* arrayClass)
+{
+ MonoClass* elementClass = mono_class_get_element_class(arrayClass);
+ MonoType* elementType = mono_class_get_type(elementClass);
+ int elementTypeCode = mono_type_get_type(elementType);
+
+ switch (elementTypeCode)
+ {
+ case MONO_TYPE_U1:
+ case MONO_TYPE_BOOLEAN:
+ case MONO_TYPE_I4:
+ case MONO_TYPE_R4:
+ case MONO_TYPE_STRING:
+ return true;
+ case MONO_TYPE_VALUETYPE:
+ {
+ const CommonScriptingClasses& commonClasses = MONO_COMMON;
+ return (elementClass == commonClasses.networkPlayer ||
+ elementClass == commonClasses.networkViewID ||
+ elementClass == commonClasses.vector3 ||
+ elementClass == commonClasses.quaternion);
+ }
+ break;
+ }
+ return false;
+}
+
+bool UnpackAndInvokeRPCMethod (MonoBehaviour& targetBehaviour, MonoMethod* method, RakNet::BitStream& parameters, SystemAddress sender, NetworkViewID viewID, RakNetTime timestamp, Object* netview)
+{
+ DebugAssertIf(method == NULL);
+ MonoMethodSignature* sig = mono_method_signature(method);
+ int paramCount = mono_signature_get_param_count(sig);
+ ForwardLinearAllocator fwdallocator (1024, kMemTempAlloc);
+ #if ENABLE_THREAD_CHECK_IN_ALLOCS
+ fwdallocator.SetThreadIDs(Thread::GetCurrentThreadID(), Thread::GetCurrentThreadID());
+ #endif
+
+ void** variables = (void** )alloca(paramCount * sizeof(void*));
+
+ const CommonScriptingClasses& commonClasses = MONO_COMMON;
+
+ BitstreamPacker stream(parameters, NULL, NULL, 0, true);
+
+ NetworkMessageInfo msgInfo;
+
+ // Loop through the parameters specified by the rpc function and extract it out of the param array
+ MonoType* methodType;
+ void* iter = NULL;
+ int i = 0;
+ bool success = true;
+ while ((methodType = mono_signature_get_params (sig, &iter)))
+ {
+ int expectedTypecode = mono_type_get_type (methodType);
+
+ switch (expectedTypecode)
+ {
+ // byte array
+ case MONO_TYPE_SZARRAY:
+ {
+ MonoClass* arrayClass = mono_class_from_mono_type (methodType);
+ if (IsValidRPCParameterArrayType(arrayClass))
+ {
+ MonoClass* elementClass = mono_class_get_element_class(arrayClass);
+ int elementSize = mono_class_array_element_size(elementClass);
+ int arrayLength;
+ stream.Serialize(reinterpret_cast<SInt32&>(arrayLength));
+ MonoArray* array = mono_array_new (mono_domain_get (), elementClass, arrayLength);
+ char* ptr = Scripting::GetScriptingArrayStart<char>(array);
+ int charLength = arrayLength * elementSize;
+ stream.Serialize (ptr, charLength);
+
+ variables[i] = array;
+ }
+ else
+ {
+ success = false;
+ }
+ }
+ break;
+ case MONO_TYPE_BOOLEAN:
+ {
+ bool extractedValue;
+ stream.Serialize (extractedValue);
+ char* writeHere = reinterpret_cast<char*>(fwdallocator.allocate(sizeof(char)));
+ *writeHere = extractedValue;
+ variables[i] = writeHere;
+ }
+ break;
+ case MONO_TYPE_I4:
+ UnpackBuiltinValue<SInt32>(stream, fwdallocator, variables[i]);
+ break;
+ case MONO_TYPE_R4:
+ UnpackBuiltinMaxError<float>(stream, fwdallocator, variables[i]);
+ break;
+ case MONO_TYPE_STRING:
+ UnpackString (parameters, variables[i]);
+ break;
+ case MONO_TYPE_VALUETYPE:
+ {
+ MonoClass* structClass = mono_type_get_class_or_element_class (methodType);
+ if (structClass == commonClasses.networkPlayer)
+ UnpackBuiltinValue<NetworkPlayer>(stream, fwdallocator, variables[i]);
+ else if (structClass == commonClasses.networkViewID)
+ UnpackBuiltinValue<NetworkViewID>(stream, fwdallocator, variables[i]);
+ else if (structClass == commonClasses.vector3)
+ UnpackBuiltinMaxError<Vector3f>(stream, fwdallocator, variables[i]);
+ else if (structClass == commonClasses.quaternion)
+ UnpackBuiltinMaxError<Quaternionf>(stream, fwdallocator, variables[i]);
+ else if (structClass == commonClasses.networkMessageInfo)
+ {
+ msgInfo.timestamp = TimestampToSeconds(timestamp);
+ msgInfo.sender = GetNetworkManager().GetIndexFromSystemAddress ( sender );
+ msgInfo.viewID = viewID;
+ AssertIf(mono_class_array_element_size(structClass) != sizeof(NetworkMessageInfo));
+ variables[i] = &msgInfo;
+ }
+ else
+ success = false;
+ }
+ break;
+ default:
+ success = false;
+ break;
+ }
+
+ i++;
+ }
+
+ if (stream.HasReadOutOfBounds())
+ success = false;
+
+ if (!success)
+ {
+ const char* classname = mono_class_get_name(mono_method_get_class(method));
+ ErrorStringObject(Format ("Failed to invoke arriving RPC method because the parameters didn't match the function declaration. '%s' of '%s'.", mono_method_get_name(method), classname), netview);
+
+ return false;
+ }
+
+ MonoObject* instance = targetBehaviour.GetInstance();
+ // Invoke the method
+ MonoException* exception;
+ MonoObject* returnValue = mono_runtime_invoke_profiled (method, instance, variables, &exception);
+ if (returnValue && exception == NULL)
+ {
+ ScriptingMethodPtr scriptingMethod = GetScriptingMethodRegistry().GetMethod(method);
+ targetBehaviour.HandleCoroutineReturnValue (scriptingMethod, returnValue);
+ }
+
+
+ if (exception == NULL)
+ return true;
+ else
+ {
+ Scripting::LogException(exception, Scripting::GetInstanceIDFromScriptingWrapper(instance));
+ return false;
+ }
+}
+
+bool PackRPCParameters (MonoBehaviour& targetBehaviour, MonoMethod* method, RakNet::BitStream& inStream, MonoArray* data, Object* netview, bool doPack)
+{
+ DebugAssertIf(method == NULL);
+ bool requireTimeStamp = false;
+
+ int arraySize = mono_array_length_safe(data);
+
+ BitstreamPacker stream(inStream, NULL, NULL, 0, false);
+
+ // Check the number of parameters
+ // Handle message info as last element case
+ MonoMethodSignature* sig = mono_method_signature(method);
+ if (arraySize != mono_signature_get_param_count(sig) && arraySize + 1 != mono_signature_get_param_count(sig))
+ {
+ ErrorStringObject(Format( "Sending RPC '%s' failed because the number of supplied parameters doesn't match the rpc declaration. Expected %d but got %d parameters.", mono_method_get_name(method), mono_signature_get_param_count(sig), mono_array_length_safe(data)), netview);
+ return false;
+ }
+
+ const CommonScriptingClasses& commonClasses = MONO_COMMON;
+
+ // Loop through the parameters specified by the rpc function and extract it out of the param array
+ MonoType* methodType;
+ void* iter = NULL;
+ int i = 0;
+ while ((methodType = mono_signature_get_params (sig, &iter)))
+ {
+ int expectedTypecode = mono_type_get_type (methodType);
+
+ // Handle message info as last element case
+ if (i == arraySize)
+ {
+ if (expectedTypecode == MONO_TYPE_VALUETYPE && mono_type_get_class_or_element_class(methodType) == commonClasses.networkMessageInfo)
+ {
+ requireTimeStamp = true;
+ return true;
+ }
+ else
+ {
+ ErrorStringObject(Format( "Sending RPC '%s' failed because the number of supplied parameters doesn't match the RPC declaration. Expected %d but got %d parameters.", mono_method_get_name(method), mono_signature_get_param_count(sig), mono_array_length_safe(data)), netview);
+ return false;
+ }
+ }
+
+ // @TODO check element against null
+ ///@TODO What if you intentionally want to pass in null. Eg. a null reference to a game object
+ MonoObject* element = GetMonoArrayElement<MonoObject*>(data, i);
+ if (element == NULL)
+ {
+ ErrorStringObject(Format("Sending RPC failed because '%s' parameter %d was null", mono_method_get_name(method), i), netview);
+ return false;
+ }
+ MonoClass* elementClass = mono_object_get_class(element);
+ MonoType* elementType = mono_class_get_type(elementClass);
+ int elementTypeCode = mono_type_get_type(elementType);
+ if (elementTypeCode == expectedTypecode)
+ {
+ switch (elementTypeCode)
+ {
+ // byte array
+ case MONO_TYPE_SZARRAY:
+ if (!IsValidRPCParameterArrayType(elementClass))
+ {
+ ErrorStringObject(Format("Sending RPC failed because '%s' parameter %d (%s) is not supported.", mono_method_get_name(method), i, mono_type_get_name(elementType)), netview);
+ return false;
+ }
+ if (doPack)
+ {
+ MonoArray* array = GetMonoArrayElement<MonoArray*>(data, i);
+ MonoClass* arrayElementClass = mono_class_get_element_class(elementClass);
+ int elementSize = mono_class_array_element_size(arrayElementClass);
+ int length = mono_array_length(array);
+ char* ptr = Scripting::GetScriptingArrayStart<char>(array);
+ stream.Serialize (reinterpret_cast<SInt32&>(length));
+ int charLength = length * elementSize;
+ stream.Serialize (ptr, charLength);
+ }
+ break;
+ case MONO_TYPE_BOOLEAN:
+ if (doPack)
+ {
+ bool boolValue = ExtractMonoObjectData<UInt8> (element);
+ stream.Serialize (boolValue);
+ }
+ break;
+ case MONO_TYPE_I4:
+ if (doPack)
+ stream.Serialize (ExtractMonoObjectData<SInt32> (element));
+ break;
+ case MONO_TYPE_R4:
+ if (doPack)
+ stream.Serialize (ExtractMonoObjectData<float> (element), 0);
+ break;
+ case MONO_TYPE_STRING:
+ if (doPack)
+ PackString (inStream, element);
+ break;
+ case MONO_TYPE_VALUETYPE:
+ {
+ MonoClass* methodClass = mono_type_get_class_or_element_class(methodType);
+ if (elementClass == methodClass)
+ {
+ if (elementClass == commonClasses.networkPlayer)
+ {
+ if (doPack)
+ stream.Serialize (ExtractMonoObjectData<NetworkPlayer> (element));
+ }
+ else if (elementClass == commonClasses.networkViewID)
+ {
+ if (doPack)
+ stream.Serialize (ExtractMonoObjectData<NetworkViewID> (element));
+ }
+ else if (elementClass == commonClasses.vector3)
+ {
+ if (doPack)
+ stream.Serialize (ExtractMonoObjectData<Vector3f> (element), 0.0F);
+ }
+ else if (elementClass == commonClasses.quaternion)
+ {
+ if (doPack)
+ stream.Serialize (ExtractMonoObjectData<Quaternionf> (element), 0.0F);
+ }
+ else
+ {
+ ErrorStringObject(Format("Sending RPC failed because '%s' parameter %d (%s) is not supported.", mono_method_get_name(method), i, mono_type_get_name(elementType)), netview);
+ return false;
+ }
+ }
+ else
+ {
+ const char* expectedTypeName = mono_type_get_name(methodType);
+ const char* gotTypeName = mono_type_get_name(elementType);
+ ErrorStringObject(Format("Sending RPC failed because '%s' parameter %d didn't match the RPC declaration. Expected '%s' but got '%s'", mono_method_get_name(method), i, expectedTypeName, gotTypeName), netview);
+ return false;
+ }
+ }
+ break;
+
+ default:
+ ErrorStringObject(Format("Sending RPC failed because '%s' parameter %d (%s) is not supported.", mono_method_get_name(method), i, mono_type_get_name(elementType)), netview);
+ return false;
+ break;
+ }
+ }
+ else
+ {
+ const char* expectedTypeName = mono_type_get_name(methodType);
+ const char* gotTypeName = mono_type_get_name(elementType);
+ ErrorStringObject(Format("Sending RPC failed because '%s' parameter %d didn't match the RPC declaration. Expected '%s' but got '%s'", mono_method_get_name(method), i, expectedTypeName, gotTypeName), netview);
+ return false;
+ }
+ i++;
+ }
+
+ return true;
+}
+
+#endif
diff --git a/Runtime/Network/PackMonoRPC.h b/Runtime/Network/PackMonoRPC.h
new file mode 100644
index 0000000..bd16900
--- /dev/null
+++ b/Runtime/Network/PackMonoRPC.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_NETWORK
+
+#include "Runtime/Mono/MonoIncludes.h"
+#include "External/RakNet/builds/include/BitStream.h"
+#include "NetworkEnums.h"
+
+typedef void NetworkViewRpcFunc (RPCParameters *rpcParameters);
+
+bool UnpackAndInvokeRPCMethod (GameObject& target, const char* name, RakNet::BitStream& parameters, SystemAddress sender, NetworkViewID viewID, RakNetTime timestamp, Object* netview);
+bool PackRPCParameters (GameObject& target, const char* name, RakNet::BitStream& inStream, MonoArray* data, Object* netview);
+
+bool UnpackAndInvokeRPCMethod (MonoBehaviour& targetBehaviour, MonoMethod* method, RakNet::BitStream& parameters, SystemAddress sender, NetworkViewID viewID, RakNetTime timestamp, Object* netview);
+bool PackRPCParameters (MonoBehaviour& targetBehaviour, MonoMethod* method, RakNet::BitStream& inStream, MonoArray* data, Object* netview, bool doPack);
+
+#endif \ No newline at end of file
diff --git a/Runtime/Network/PackStateSpecialized.cpp b/Runtime/Network/PackStateSpecialized.cpp
new file mode 100644
index 0000000..9c6ca61
--- /dev/null
+++ b/Runtime/Network/PackStateSpecialized.cpp
@@ -0,0 +1,124 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_NETWORK
+
+#include "PackStateSpecialized.h"
+#include "Runtime/Dynamics/RigidBody.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Animation/Animation.h"
+#include "Runtime/Animation/AnimationState.h"
+#include "BitStreamPacker.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoScriptCache.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Interfaces/IPhysics.h"
+#include "Runtime/Interfaces/IAnimationStateNetworkProvider.h"
+
+const float kPositionEpsilon = 0.00001F;
+const float kRotationEpsilon = 0.0001F;
+
+bool PackTransform (Transform& transform, BitstreamPacker& packer)
+{
+ Vector3f pos = transform.GetLocalPosition();
+ Quaternionf rot = transform.GetLocalRotation();
+ Vector3f scale = transform.GetLocalScale();
+
+ packer.Serialize(pos, kPositionEpsilon);
+ packer.Serialize(rot, kRotationEpsilon);
+ packer.Serialize(scale, kPositionEpsilon);
+ return packer.HasChanged();
+}
+
+void UnpackTransform (Transform& transform, BitstreamPacker& packer)
+{
+ Vector3f pos;
+ Quaternionf rot;
+ Vector3f scale;
+
+ packer.Serialize(pos, kPositionEpsilon);
+ packer.Serialize(rot, kRotationEpsilon);
+ packer.Serialize(scale, kPositionEpsilon);
+
+ transform.SetLocalTRS(pos, rot, scale);
+}
+
+bool SerializeAnimation (Animation& animation, BitstreamPacker& packer)
+{
+ IAnimationStateNetworkProvider* animationSystem = GetIAnimationStateNetworkProvider();
+ if (animationSystem == NULL)
+ return false;
+
+ int count = animationSystem->GetNetworkAnimationStateCount (animation);
+
+ AnimationStateForNetwork* serialized;
+ ALLOC_TEMP(serialized, AnimationStateForNetwork, count);
+
+ if (packer.IsWriting())
+ animationSystem->GetNetworkAnimationState (animation, serialized, count);
+
+ for (int i=0;i<count;i++)
+ {
+ packer.Serialize(serialized[i].enabled);
+ packer.Serialize(serialized[i].weight, 0.01F);
+ packer.Serialize(serialized[i].time, 0.01F);
+ }
+
+ if (packer.IsReading())
+ animationSystem->SetNetworkAnimationState (animation, serialized, count);
+
+ return packer.HasChanged();
+}
+
+
+/* Specialized function for packing rigid bodies. We want certain parameters
+ * from the Rigidbody object: position, rotation, anglar velocity and velocity.
+ */
+bool SerializeRigidbody (Rigidbody& rigidbody, BitstreamPacker& packer)
+{
+ IPhysics::RigidBodyState state;
+ IPhysics& module = *GetIPhysics();
+
+ if (packer.IsWriting())
+ module.GetRigidBodyState(rigidbody, &state);
+
+ packer.Serialize(state.position, kPositionEpsilon);
+ packer.Serialize(state.rotation, kRotationEpsilon);
+ packer.Serialize(state.velocity, kPositionEpsilon);
+ packer.Serialize(state.avelocity, kPositionEpsilon);
+
+ if (packer.IsReading())
+ module.SetRigidBodyState(rigidbody, state);
+
+ return packer.HasChanged();
+}
+
+bool SerializeMono (MonoBehaviour& mono, BitstreamPacker& deltaState, NetworkMessageInfo& timeStamp)
+{
+ if (mono.IsActive() && mono.GetInstance())
+ {
+ ScriptingMethodPtr method = mono.GetMethod(MonoScriptCache::kSerializeNetView);
+ if (method)
+ {
+ MonoObject* monoStream = mono_object_new(mono_domain_get(), GetMonoManager().GetCommonClasses().bitStream);
+ ExtractMonoObjectData<BitstreamPacker*> (monoStream) = &deltaState;
+ void* values[] = { monoStream, &timeStamp };
+ MonoException* exception;
+ MonoObject* instance = mono.GetInstance();
+ mono_runtime_invoke_profiled (method->monoMethod, mono.GetInstance(), values, &exception);
+
+ if (exception != NULL)
+ Scripting::LogException(exception, Scripting::GetInstanceIDFromScriptingWrapper(instance));
+
+ ExtractMonoObjectData<BitstreamPacker*> (monoStream) = NULL;
+ }
+ }
+
+ return deltaState.HasChanged();
+}
+
+
+#endif
diff --git a/Runtime/Network/PackStateSpecialized.h b/Runtime/Network/PackStateSpecialized.h
new file mode 100644
index 0000000..93e066e
--- /dev/null
+++ b/Runtime/Network/PackStateSpecialized.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_NETWORK
+
+class BitstreamPacker;
+class Rigidbody;
+class MonoBehaviour;
+class Transform;
+class Animation;
+struct NetworkMessageInfo;
+
+bool SerializeRigidbody (Rigidbody& rigidbody, BitstreamPacker& packer);
+
+void UnpackTransform (Transform& transform, BitstreamPacker& packer);
+bool PackTransform (Transform& transform, BitstreamPacker& packer);
+
+bool SerializeAnimation (Animation& animation, BitstreamPacker& packer);
+
+bool SerializeMono (MonoBehaviour& mono, BitstreamPacker& deltaState, NetworkMessageInfo& timeStamp);
+
+#endif \ No newline at end of file
diff --git a/Runtime/Network/PlayerCommunicator/EditorConnection.cpp b/Runtime/Network/PlayerCommunicator/EditorConnection.cpp
new file mode 100644
index 0000000..d8814fa
--- /dev/null
+++ b/Runtime/Network/PlayerCommunicator/EditorConnection.cpp
@@ -0,0 +1,335 @@
+#include "UnityPrefix.h"
+
+#if UNITY_EDITOR
+
+#include "EditorConnection.h"
+#include "Editor/Platform/Interface/EditorWindows.h"
+#include "Runtime/Misc/SystemInfo.h"
+
+#include "Runtime/Serialize/FileCache.h"
+#include "Runtime/Serialize/CacheWrap.h"
+
+#include "Runtime/Network/NetworkUtility.h"
+#include "Runtime/Profiler/TimeHelper.h"
+#include "Runtime/Math/Random/rand.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Utilities/PlayerPrefs.h"
+#include "Runtime/Threads/Thread.h"
+
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Network/PlayerCommunicator/GeneralConnectionInternals.h"
+#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h"
+
+static const char* kLastConnectedPlayer = "LastConnectedPlayer";
+
+EditorConnection* EditorConnection::ms_Instance = NULL;
+
+
+EditorConnection::EditorConnection(unsigned short multicastPort)
+: GeneralConnection()
+{
+ ABSOLUTE_TIME_INIT(m_LastCleanup);
+ if (!m_MulticastSocket.Initialize(PLAYER_MULTICAST_GROUP, multicastPort))
+ ErrorStringMsg("Failed to setup multicast socket for player connection.");
+ if (!m_MulticastSocket.Join())
+ ErrorStringMsg("Unable to join player connection multicast group.");
+}
+
+EditorConnection& EditorConnection::Get()
+{
+ return *ms_Instance;
+}
+
+void EditorConnection::Initialize ()
+{
+ GeneralConnection::Initialize();
+ Assert(ms_Instance == NULL);
+ ms_Instance = new EditorConnection();
+}
+void EditorConnection::Cleanup ()
+{
+ Assert(ms_Instance != NULL);
+ delete ms_Instance;
+ ms_Instance = NULL;
+ GeneralConnection::Cleanup();
+}
+
+int EditorConnection::ConnectPlayerDirectIP(const std::string& IP)
+{
+ Connection* cnx = GetConnection(PLAYER_DIRECT_IP_CONNECT_GUID);
+ if (cnx && cnx->IsValid())
+ return PLAYER_DIRECT_IP_CONNECT_GUID;
+
+ int socketHandle = -1;
+ int timeout = 1;
+ // Poll at 1 ms, 10 ms, and 100 ms before giving up
+ while (socketHandle == -1)
+ {
+ // A forced repaint is required, since this method is a blocking call
+ LogStringMsg("Attempting to connect to player ip: %s with a %d ms timeout", IP.c_str(), timeout);
+ ForceRepaintOnAllViews();
+
+ for(int i = 0; i <= PLAYER_PORT_MASK; i++)
+ {
+ socketHandle = ::Socket::Connect(IP.c_str(), PLAYER_LISTEN_PORT + i, timeout, true, false);
+ if(socketHandle != -1)
+ break;
+ }
+ if (socketHandle != -1)
+ break;
+
+ timeout *= 10;
+ if (timeout > 100)
+ break;
+ }
+ if(socketHandle == -1)
+ {
+ ErrorStringMsg("Failed to connect to player ip: %s", IP.c_str());
+ return -1;
+ }
+
+ RegisterConnection(PLAYER_DIRECT_IP_CONNECT_GUID, socketHandle);
+ if (!ms_RunningUnitTests)
+ EditorPrefs::SetString(kLastConnectedPlayer, "");
+
+ return PLAYER_DIRECT_IP_CONNECT_GUID;
+}
+
+int EditorConnection::ConnectPlayer (UInt32 guid)
+{
+ AvailablePlayerMap::iterator found = m_AvailablePlayers.find(guid);
+ if (found == m_AvailablePlayers.end())
+ return -1;
+
+ MulticastInfo& multicastInfo = found->second.m_MulticastInfo;
+ if(!multicastInfo.IsValid())
+ return -1;
+
+ Assert(multicastInfo.GetGuid() == guid);
+ Connection* cnx = GetConnection(guid);
+ if (cnx && cnx->IsValid())
+ return guid;
+
+ int socketHandle;
+ if (SOCK_ERROR(socketHandle = ::Socket::Connect(multicastInfo.GetIP().c_str(), multicastInfo.GetPort(), 500 /*timeout*/)))
+ {
+ ErrorStringMsg("Failed to connect to player ip: %s, port: %d", multicastInfo.GetIP().c_str(), multicastInfo.GetPort());
+ return 0;
+ }
+
+ RegisterConnection(guid, socketHandle);
+ if (!ms_RunningUnitTests)
+ EditorPrefs::SetString(kLastConnectedPlayer, multicastInfo.GetIdentifier());
+
+ return guid;
+}
+
+UInt32 EditorConnection::AddPlayer(std::string hostName, std::string localIP, unsigned short port, UInt32 guid, int flags)
+{
+ std::string buffer = Format(SERVER_IDENTIFICATION_FORMAT, localIP.c_str(), port, flags, guid, -1, ms_Version, hostName.c_str(), 0 );
+ MulticastInfo multicastInfo;
+ if(multicastInfo.Parse(buffer.c_str()))
+ {
+ AvailablePlayerMap::iterator found = m_AvailablePlayers.find(multicastInfo.GetGuid());
+ if(found != m_AvailablePlayers.end())
+ {
+ // entry already in the list - renew the timestamp
+ found->second.m_LastPing = START_TIME;
+ }
+ else
+ {
+ m_AvailablePlayers.insert (std::make_pair(multicastInfo.GetGuid(), AvailablePlayer(START_TIME, multicastInfo))).first;
+ }
+ return multicastInfo.GetGuid();
+ }
+ return 0;
+}
+
+void EditorConnection::RemovePlayer(UInt32 guid)
+{
+ Disconnect(guid);
+ AvailablePlayerMap::iterator found = m_AvailablePlayers.find(guid);
+ if(found != m_AvailablePlayers.end())
+ m_AvailablePlayers.erase(found);
+}
+
+void EditorConnection::EnablePlayer(UInt32 guid, bool enable)
+{
+ AvailablePlayerMap::iterator player = m_AvailablePlayers.find(guid);
+ if (player != m_AvailablePlayers.end())
+ player->second.m_Enabled = enable;
+}
+
+void
+EditorConnection::ResetLastPlayer()
+{
+ EditorPrefs::SetString(kLastConnectedPlayer, "");
+}
+
+void EditorConnection::GetAvailablePlayers( std::vector<UInt32>& values )
+{
+ AvailablePlayerMap::iterator it = m_AvailablePlayers.begin();
+ for( ; it != m_AvailablePlayers.end(); ++it){
+ if (it->second.m_Enabled && it->second.m_MulticastInfo.GetIP() == m_LocalIP)
+ values.push_back(it->second.m_MulticastInfo.GetGuid());
+ }
+ it = m_AvailablePlayers.begin();
+ for( ; it != m_AvailablePlayers.end(); ++it){
+ if (it->second.m_Enabled && it->second.m_MulticastInfo.GetIP() != m_LocalIP)
+ values.push_back(it->second.m_MulticastInfo.GetGuid());
+ }
+}
+
+void EditorConnection::GetAvailablePlayers(RuntimePlatform platform, vector<UInt32>& values)
+{
+ string const id = systeminfo::GetRuntimePlatformString(platform);
+
+ for (AvailablePlayerMap::const_iterator it = m_AvailablePlayers.begin() ; it != m_AvailablePlayers.end(); ++it)
+ {
+ AvailablePlayer const& player = it->second;
+ if (player.m_Enabled && BeginsWith(player.m_MulticastInfo.GetIdentifier(), id) && (player.m_MulticastInfo.GetIP() == m_LocalIP))
+ values.push_back(player.m_MulticastInfo.GetGuid());
+ }
+
+ for (AvailablePlayerMap::const_iterator it = m_AvailablePlayers.begin() ; it != m_AvailablePlayers.end(); ++it)
+ {
+ AvailablePlayer const& player = it->second;
+ if (player.m_Enabled && BeginsWith(player.m_MulticastInfo.GetIdentifier(), id) && (player.m_MulticastInfo.GetIP() != m_LocalIP))
+ values.push_back(player.m_MulticastInfo.GetGuid());
+ }
+}
+
+std::string EditorConnection::GetConnectionIdentifier( UInt32 guid )
+{
+ AvailablePlayerMap::iterator it = m_AvailablePlayers.find(guid);
+ if (it != m_AvailablePlayers.end())
+ return it->second.m_MulticastInfo.GetIdentifier();
+ return "None";
+}
+
+bool EditorConnection::IsIdentifierConnectable( UInt32 guid )
+{
+ AvailablePlayerMap::iterator it = m_AvailablePlayers.find(guid);
+ if (it != m_AvailablePlayers.end())
+ return it->second.m_MulticastInfo.IsValid();
+ return false;
+}
+
+bool EditorConnection::IsIdentifierOnLocalhost( UInt32 guid )
+{
+ AvailablePlayerMap::iterator it = m_AvailablePlayers.find(guid);
+ if (it != m_AvailablePlayers.end())
+ return it->second.m_MulticastInfo.IsLocalhost();
+ return false;
+}
+
+GeneralConnection::MulticastInfo EditorConnection::PollWithCustomMessage()
+{
+ MulticastInfo multicastInfo;
+
+ sockaddr_in srcaddr;
+ socklen_t srcaddr_len = sizeof(srcaddr);
+ char buffer[kMulticastBufferSize];
+ RecvUserData recvData;
+ recvData.srcAddr = (struct sockaddr*)&srcaddr;
+ recvData.srcLen = srcaddr_len;
+ int size = m_MulticastSocket.Recv(buffer, kMulticastBufferSize, &recvData);
+ if (!SOCK_ERROR(size))
+ {
+ // Ensure that buffer is null terminated string
+ buffer[size] = 0;
+ if(multicastInfo.Parse(buffer, &srcaddr))
+ {
+ AvailablePlayerMap::iterator found = m_AvailablePlayers.find(multicastInfo.GetGuid());
+ if(found != m_AvailablePlayers.end())
+ {
+ // entry already in the list - renew the timestamp
+ found->second.m_LastPing = START_TIME;
+ }
+ else
+ {
+ // remove old player if new one has connected
+ for (AvailablePlayerMap::const_iterator it = m_AvailablePlayers.begin(); it != m_AvailablePlayers.end(); ++it)
+ if (!it->second.m_Enabled && !StrCmp(it->second.m_MulticastInfo.GetIdentifier(), multicastInfo.GetIdentifier()))
+ {
+ RemovePlayer(it->first);
+ break;
+ }
+
+ m_AvailablePlayers.insert (std::make_pair(multicastInfo.GetGuid(), AvailablePlayer(START_TIME, multicastInfo)));
+ if (multicastInfo.IsValid() && multicastInfo.ImmediateConnect())
+ {
+ bool atemptAutoConnect;
+ if (ms_RunningUnitTests)
+ {
+ atemptAutoConnect = false;
+ if (!IsConnected())
+ {
+ const char* connectingIp = multicastInfo.GetIP().c_str();
+
+ const int maxNumberOfIps = 10;
+ char localIps[maxNumberOfIps][16];
+ GetIPs(localIps);
+ for (int i = 0; i < maxNumberOfIps; ++i)
+ {
+ if (strcmp(localIps[i], connectingIp) == 0)
+ {
+ atemptAutoConnect = true;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ string lastPlayerId = EditorPrefs::GetString(kLastConnectedPlayer);
+
+ // No prior player are known and new player is built by us and requires autoconnection
+ if (lastPlayerId.empty() && multicastInfo.GetEditorGuid() == GetLocalGuid())
+ atemptAutoConnect = true;
+ else
+ atemptAutoConnect = (lastPlayerId == multicastInfo.GetIdentifier());
+ }
+
+ if (atemptAutoConnect)
+ ConnectPlayer(multicastInfo.GetGuid());
+ }
+ }
+ }
+ else
+ {
+ // can not connect to this player (version mismatch)
+
+ }
+ }
+
+ // clean up the list every 2 seconds ( players not pinging in 10 seconds are removed from the map )
+ if(GetProfileTime(ELAPSED_TIME(m_LastCleanup)) > 2*kTimeSecond)
+ {
+ m_LastCleanup = START_TIME;
+ AvailablePlayerMap::iterator it = m_AvailablePlayers.begin();
+ while (it != m_AvailablePlayers.end())
+ {
+ UInt32 guid = it->first;
+ AvailablePlayer& player = it->second;
+ if (player.m_MulticastInfo.GetGuid() == PLAYER_DIRECTCONNECT_GUID)
+ {
+ ++it;
+ continue;
+ }
+ if (!GetConnection(guid) && (GetProfileTime(ELAPSED_TIME(player.m_LastPing)) > 10*kTimeSecond))
+ {
+ AvailablePlayerMap::iterator erase = it++;
+ m_AvailablePlayers.erase(erase);
+ }
+ else
+ ++it;
+ }
+ }
+
+ GeneralConnection::Poll();
+
+ return multicastInfo;
+}
+
+#endif // ENABLE_PLAYERCONNECTION
diff --git a/Runtime/Network/PlayerCommunicator/EditorConnection.h b/Runtime/Network/PlayerCommunicator/EditorConnection.h
new file mode 100644
index 0000000..c2f020d
--- /dev/null
+++ b/Runtime/Network/PlayerCommunicator/EditorConnection.h
@@ -0,0 +1,77 @@
+#pragma once
+
+#if UNITY_EDITOR
+
+#include "GeneralConnection.h"
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+
+
+#if UNITY_WIN
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <errno.h>
+#endif
+#include "Runtime/Profiler/TimeHelper.h"
+#include "Runtime/Threads/Mutex.h"
+
+
+class EditorConnection : public GeneralConnection
+{
+public:
+ EditorConnection (unsigned short multicastPort = PLAYER_MULTICAST_PORT);
+
+ static void Initialize ();
+ static void Cleanup ();
+
+ void ResetLastPlayer();
+
+ void GetAvailablePlayers( std::vector<UInt32>& values );
+ void GetAvailablePlayers( RuntimePlatform platform, std::vector<UInt32>& values );
+ std::string GetConnectionIdentifier( UInt32 guid );
+ bool IsIdentifierConnectable( UInt32 guid );
+ bool IsIdentifierOnLocalhost( UInt32 guid );
+
+ int ConnectPlayer (UInt32 guid);
+ int ConnectPlayerDirectIP(const std::string& IP);
+
+ UInt32 AddPlayer(std::string hostName, std::string localIP, unsigned short port, UInt32 guid, int flags);
+ void RemovePlayer(UInt32 guid);
+ void EnablePlayer(UInt32 guid, bool enable);
+
+ // Singleton accessor for editorconnection
+ static EditorConnection& Get ();
+ static EditorConnection* ms_Instance;
+
+ MulticastInfo PollWithCustomMessage ();
+
+private:
+ virtual bool IsServer () { return false; }
+
+ struct AvailablePlayer
+ {
+ AvailablePlayer(ABSOLUTE_TIME time, const MulticastInfo& info)
+ : m_LastPing (time)
+ , m_MulticastInfo(info)
+ , m_Enabled(true)
+ {}
+
+ ABSOLUTE_TIME m_LastPing;
+ MulticastInfo m_MulticastInfo;
+ bool m_Enabled;
+ };
+
+private:
+ typedef std::map< UInt32, AvailablePlayer > AvailablePlayerMap;
+ AvailablePlayerMap m_AvailablePlayers;
+
+ ABSOLUTE_TIME m_LastCleanup;
+};
+
+#endif
diff --git a/Runtime/Network/PlayerCommunicator/GeneralConnection.cpp b/Runtime/Network/PlayerCommunicator/GeneralConnection.cpp
new file mode 100644
index 0000000..dc4c89d
--- /dev/null
+++ b/Runtime/Network/PlayerCommunicator/GeneralConnection.cpp
@@ -0,0 +1,666 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_PLAYERCONNECTION
+
+#include "GeneralConnection.h"
+#include "GeneralConnectionInternals.h"
+#include "Runtime/Math/Random/rand.h"
+#include "Runtime/Network/NetworkUtility.h"
+#include "Runtime/Profiler/TimeHelper.h"
+
+bool GeneralConnection::ms_RunningUnitTests = false;
+int GeneralConnection::ms_Version = 0x00100100;
+
+GeneralConnection::GeneralConnection ()
+: m_LogEnabled(true)
+{
+ char ips[10][16];
+ int count = GetIPs(ips);
+ if (count != 0)
+ m_LocalIP = ips[0];
+ else
+ m_LocalIP = "0.0.0.0";
+
+#if UNITY_BB10
+ for(int x = 0; x < sizeof(ips); x++)
+ {
+ if(std::string(ips[x]).find("169.254") == 0)
+ {
+ m_LocalIP = ips[x];
+ break;
+ }
+ }
+#endif
+
+ m_LocalGuid = Rand((int)GetProfileTime(START_TIME)).Get();
+ // We reserve ANY_PLAYERCONNECTION guid for special use
+ if (m_LocalGuid == ANY_PLAYERCONNECTION) m_LocalGuid = ANY_PLAYERCONNECTION ^ 1;
+}
+
+UInt32 GeneralConnection::GetLocalGuid() const
+{
+ return m_LocalGuid;
+}
+
+void GeneralConnection::WaitForFinish()
+{
+ int msgWait = 60;
+
+ while (HasBytesToSend())
+ {
+ if (msgWait++ == 60)
+ {
+ printf_console("Waiting for finish\n");
+ msgWait = 0;
+ }
+
+ Poll();
+ Thread::Sleep(0.05);
+ }
+}
+
+bool GeneralConnection::HasBytesToSend() const
+{
+ ConnectionMap::const_iterator it = m_Connections.begin();
+ for ( ; it != m_Connections.end(); ++it)
+ if (it->second->HasBytesToSend())
+ return true;
+
+ return false;
+}
+
+GeneralConnection::~GeneralConnection()
+{
+ DisconnectAll();
+}
+
+
+void GeneralConnection::DisconnectAll()
+{
+ ConnectionMap::iterator it = m_Connections.begin();
+ std::vector< int > disconnectGuids;
+ for( ; it != m_Connections.end(); ++it)
+ disconnectGuids.push_back(it->first);
+
+ for (int i = 0; i < disconnectGuids.size(); i++)
+ Disconnect(disconnectGuids[i]);
+
+ Assert(m_Connections.empty());
+}
+
+
+void GeneralConnection::Initialize ()
+{
+ NetworkInitialize();
+}
+
+void GeneralConnection::Cleanup ()
+{
+ NetworkCleanup();
+}
+
+
+GeneralConnection::Connection* GeneralConnection::GetConnection(UInt32 guid)
+{
+ ConnectionMap::iterator it = m_Connections.find(guid);
+ if (it == m_Connections.end())
+ return NULL;
+ return it->second;
+}
+
+void GeneralConnection::RegisterConnection(UInt32 guid, TSocketHandle socketHandle)
+{
+ if (GetConnection(guid))
+ Disconnect(guid);
+
+ LOG_PLAYER_CONNECTION(Format("PlayerConnection registered: %d", guid));
+ m_Connections[guid] = new Connection(socketHandle);
+ for(int i = 0; i < m_ConnectionHandlers.size(); i++)
+ (m_ConnectionHandlers[i])(guid);
+}
+
+void GeneralConnection::Disconnect(UInt32 guid)
+{
+ ConnectionMap::iterator it = m_Connections.find(guid);
+ if (it == m_Connections.end())
+ return;
+
+ LOG_PLAYER_CONNECTION(Format("PlayerConnection disconnecting: %d", guid));
+ for (int i = 0; i < m_DisconnectionHandlers.size(); i++)
+ (m_DisconnectionHandlers[i])(guid);
+
+#if ENABLE_PLAYER_CONNECTION_DEBUG_LOG
+ Connection& connection = *it->second;
+ LOG_PLAYER_CONNECTION(Format("GeneralConnection::Connection send buffer mem usage %d\n", connection.GetSendBufferSize()));
+ LOG_PLAYER_CONNECTION(Format("GeneralConnection::Connection recv buffer mem usage %d\n", connection.GetRecvBufferSize()));
+#endif
+
+ delete it->second;
+
+ m_Connections.erase(it);
+}
+
+
+void GeneralConnection::SendMessage(UInt32 guid, MessageID id, const void* data, UInt32 size)
+{
+ NetworkMessage message;
+ message.InitializeMessage();
+ message.SetID(id);
+ message.SetDataSize(size);
+
+ bool oldlog = IsLogEnabled();
+ SetLogEnabled(false);
+
+ if (guid == ANY_PLAYERCONNECTION)
+ {
+ LOG_PLAYER_CONNECTION(Format("PlayerConnection send message to ALL, id '%d', size '%d'", id, size));
+ ConnectionMap::iterator it = m_Connections.begin();
+ for ( ; it != m_Connections.end(); ++it)
+ it->second->SendMessage(message, data);
+ }
+ else
+ {
+ LOG_PLAYER_CONNECTION(Format("PlayerConnection send message to '%d', id '%d', size '%d'", guid, id, size));
+ ConnectionMap::iterator it = m_Connections.find(guid);
+ if (it != m_Connections.end())
+ it->second->SendMessage(message, data);
+ }
+ SetLogEnabled(oldlog);
+}
+
+void GeneralConnection::Poll()
+{
+ ABSOLUTE_TIME start = START_TIME;
+ ConnectionMap::iterator it = m_Connections.begin();
+ UNITY_TEMP_VECTOR(int) disconnectGuids;
+ for ( ; it != m_Connections.end(); ++it)
+ {
+ Connection& connection = *(it->second);
+ connection.Poll();
+ const void* messageDataPtr;
+ NetworkMessage messageHeader;
+ while ( (GetProfileTime(ELAPSED_TIME(start)) < 20*kTimeMillisecond) && ((messageDataPtr = connection.ReceiveMessage(&messageHeader)) != NULL) )
+ {
+ LOG_PLAYER_CONNECTION(Format("PlayerConnection recv message id '%d', size '%d'", messageHeader.GetID(), messageHeader.GetDataSize()));
+ std::map< MessageID, MessageHandlerFunc >::iterator handler = m_HandlerMap.find(messageHeader.GetID());
+ if (handler != m_HandlerMap.end())
+ (handler->second)(messageDataPtr, messageHeader.GetDataSize(), it->first);
+ connection.ReleaseReceivedMessage();
+ }
+ if (!connection.IsValid())
+ disconnectGuids.push_back(it->first);
+ }
+
+ for (int i = 0; i < disconnectGuids.size(); i++)
+ Disconnect(disconnectGuids[i]);
+}
+
+// Handlers
+void GeneralConnection::RegisterMessageHandler( MessageID messageID, MessageHandlerFunc func )
+{
+ if(m_HandlerMap.find(messageID) != m_HandlerMap.end())
+ ErrorString("MessageHandler already registered");
+ m_HandlerMap[messageID] = func;
+}
+
+void GeneralConnection::RegisterConnectionHandler( ConnectionHandlerFunc func )
+{
+ for (int i = 0; i < m_ConnectionHandlers.size(); i++)
+ Assert(m_ConnectionHandlers[i] != func);
+
+ m_ConnectionHandlers.push_back(func);
+
+ // call the connect handler on already connected sockets
+ ConnectionMap::iterator it = m_Connections.begin();
+ for( ; it != m_Connections.end(); ++it)
+ (func)(it->first);
+}
+
+void GeneralConnection::RegisterDisconnectionHandler( ConnectionHandlerFunc func )
+{
+ for (int i = 0; i < m_DisconnectionHandlers.size(); i++)
+ Assert(m_DisconnectionHandlers[i] != func);
+
+ m_DisconnectionHandlers.push_back(func);
+}
+
+void GeneralConnection::UnregisterMessageHandler( MessageID messageID, MessageHandlerFunc func )
+{
+ std::map< MessageID, MessageHandlerFunc >::iterator found = m_HandlerMap.find(messageID);
+ if(found == m_HandlerMap.end())
+ ErrorString("MessageHandler not registered");
+ Assert(found->second == func);
+ m_HandlerMap.erase(found);
+}
+
+void GeneralConnection::UnregisterConnectionHandler( ConnectionHandlerFunc func )
+{
+ std::vector< ConnectionHandlerFunc >::iterator handlerIt = m_ConnectionHandlers.begin();
+ for ( ; handlerIt != m_ConnectionHandlers.end(); ++handlerIt)
+ {
+ if (*handlerIt == func)
+ {
+ m_ConnectionHandlers.erase(handlerIt);
+ return;
+ }
+ }
+}
+
+void GeneralConnection::UnregisterDisconnectionHandler( ConnectionHandlerFunc func )
+{
+ // call the disconnect handler on already connected sockets
+ ConnectionMap::iterator it = m_Connections.begin();
+ for( ; it != m_Connections.end(); ++it)
+ (func)(it->first);
+
+ std::vector< ConnectionHandlerFunc >::iterator handlerIt = m_DisconnectionHandlers.begin();
+ for ( ; handlerIt != m_DisconnectionHandlers.end(); ++handlerIt)
+ {
+ if (*handlerIt == func)
+ {
+ m_DisconnectionHandlers.erase(handlerIt);
+ return;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------
+// Connection
+// ------------------------------------------------------------------------------
+GeneralConnection::Connection::Connection(TSocketHandle socketHandle)
+: m_SocketStream(socketHandle, kDataBufferMaxSize, kDataBufferMaxSize)
+, m_PendingMessageData(NULL)
+, m_HasPendingMessage(false)
+{
+}
+
+GeneralConnection::Connection::~Connection()
+{
+ if (m_PendingMessageData)
+ ReleaseReceivedMessage();
+}
+
+const void* GeneralConnection::Connection::ReceiveMessage(NetworkMessage* message)
+{
+ AssertBreak(!m_PendingMessageData);
+ Mutex::AutoLock lock (m_RecvMutex);
+
+ ExtendedGrowingRingbuffer& recvBuffer = m_SocketStream.RecvBuffer();
+ UInt32 bytesAvailable = recvBuffer.GetAvailableSize();
+ if (!m_HasPendingMessage && bytesAvailable >= sizeof(NetworkMessage))
+ {
+ m_SocketStream.RecvAll(&m_PendingMessage, sizeof(NetworkMessage));
+ m_HasPendingMessage = true;
+ bytesAvailable = recvBuffer.GetAvailableSize();
+ Assert(recvBuffer.GetSize() >= m_PendingMessage.GetDataSize() && "Buffer is not large enough for the message.");
+
+ }
+
+ if (m_HasPendingMessage)
+ {
+ UInt32 messageSize = m_PendingMessage.GetDataSize();
+ if (m_HasPendingMessage && bytesAvailable >= messageSize)
+ {
+ m_HasPendingMessage = false;
+ UInt32 linearData = messageSize;
+ m_PendingMessageData = (void*) recvBuffer.ReadPtr(&linearData);
+ m_LocallyAllocatedMessageData = linearData < messageSize;
+ if (m_LocallyAllocatedMessageData)
+ {
+ m_PendingMessageData = UNITY_MALLOC(kMemNetwork, messageSize);
+ m_SocketStream.RecvAll(m_PendingMessageData, messageSize);
+ }
+ memcpy(message, &m_PendingMessage, sizeof(NetworkMessage));
+ return m_PendingMessageData;
+ }
+ }
+ return NULL;
+}
+
+void GeneralConnection::Connection::ReleaseReceivedMessage()
+{
+ AssertBreak(m_PendingMessageData);
+ Mutex::AutoLock lock (m_RecvMutex);
+
+ if (m_LocallyAllocatedMessageData)
+ UNITY_FREE(kMemNetwork, m_PendingMessageData);
+ else
+ m_SocketStream.RecvBuffer().ReadPtrUpdate(m_PendingMessageData, m_PendingMessage.GetDataSize());
+ m_PendingMessageData = NULL;
+}
+
+void GeneralConnection::Connection::SendMessage(NetworkMessage& message, const void* data)
+{
+ Mutex::AutoLock lock (m_SendMutex);
+ if(message.AllowSkip() && !m_SocketStream.CanSendNonblocking(sizeof(NetworkMessage) + message.GetDataSize()))
+ {
+ WarningString("Skipping profile frame. Reciever can not keep up with the amount of data sent");
+ return;
+ }
+ m_SocketStream.SendAll(&message, sizeof(NetworkMessage));
+ m_SocketStream.SendAll(data, message.GetDataSize());
+}
+
+// ------------------------------------------------------------------------------
+// NetworkMessage
+// ------------------------------------------------------------------------------
+void GeneralConnection::NetworkMessage::InitializeMessage()
+{
+ UInt16 magicNumber = PLAYER_MESSAGE_MAGIC_NUMBER;
+ SwapEndianBytesLittleToNative(magicNumber);
+ m_MagicNumber = magicNumber;
+}
+
+bool GeneralConnection::NetworkMessage::CheckMessageValidity()
+{
+ UInt16 magicNumber = m_MagicNumber;
+ SwapEndianBytesLittleToNative(magicNumber);
+ return magicNumber == PLAYER_MESSAGE_MAGIC_NUMBER;
+}
+
+void GeneralConnection::NetworkMessage::SetID( GeneralConnection::MessageID messageID )
+{
+ UInt16 id = messageID;
+ SwapEndianBytesLittleToNative(id);
+ m_ID = id;
+}
+
+GeneralConnection::MessageID GeneralConnection::NetworkMessage::GetID()
+{
+ UInt16 id = m_ID;
+ SwapEndianBytesLittleToNative(id);
+ return (MessageID) id;
+}
+
+void GeneralConnection::NetworkMessage::SetDataSize( UInt32 size )
+{
+ SwapEndianBytesLittleToNative(size);
+ m_Size = size;
+}
+
+UInt32 GeneralConnection::NetworkMessage::GetDataSize()
+{
+ UInt32 size = m_Size;
+ SwapEndianBytesLittleToNative(size);
+ return size;
+}
+
+// ------------------------------------------------------------------------------
+// MulticastInfo
+// ------------------------------------------------------------------------------
+#if ENABLE_MULTICAST
+GeneralConnection::MulticastInfo::MulticastInfo()
+{
+ Clear();
+}
+
+void GeneralConnection::MulticastInfo::Clear()
+{
+ m_Valid = false;
+ m_Flags = 0;
+ m_Port = 0;
+ m_Guid = 0;
+ m_EditorGuid = 0;
+}
+bool GeneralConnection::MulticastInfo::Parse( const char* buffer, void* in)
+{
+ char ip[kMulticastBufferSize];
+ char id[kMulticastBufferSize];
+ int version;
+ int debug;
+ if(sscanf(buffer, SERVER_IDENTIFICATION_FORMAT, ip, &m_Port, &m_Flags, &m_Guid, &m_EditorGuid, &version, id, &debug) != 8)
+ {
+ Clear();
+ m_Valid = false;
+ m_IsLocalhost = false;
+ return false;
+ //ErrorString(Format("MulticastInfo should be in this format: '%s' but got: '%s'", SERVER_IDENTIFICATION_FORMAT, buffer));
+ }
+#if !UNITY_WINRT
+ m_Ip = in ? InAddrToIP((sockaddr_in*)in) : std::string(ip);
+#endif
+
+ m_Identifier = std::string(id);
+ m_Valid = (version == ms_Version);
+ SetLocalhostFlag();
+ return true;
+}
+
+void GeneralConnection::MulticastInfo::SetLocalhostFlag()
+{
+ std::string localIP;
+ char ips[10][16];
+ int count = GetIPs(ips);
+ if (count != 0)
+ localIP = ips[0];
+ else
+ localIP = "0.0.0.0";
+ m_IsLocalhost = (m_Ip.compare("0.0.0.0") == 0) || (m_Ip.compare("127.0.0.1") == 0) || (m_Ip.compare(localIP) == 0);
+}
+
+#endif //ENABLE_MULTICAST
+
+
+/// These tests are unstable and run for too long?
+// ---------------------------------------------------------------------------
+#if ENABLE_UNIT_TESTS && 0
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "EditorConnection.h"
+#include "PlayerConnection.h"
+
+SUITE (GeneralConnectionTests)
+{
+ bool isClientConnected = false;
+ UInt32 clientConnectionGuid = -1;
+ void ClientConnectionHandler (UInt32 guid) { isClientConnected = true; clientConnectionGuid = guid; }
+ void ClientDisconnectionHandler (UInt32 guid) { isClientConnected = false; clientConnectionGuid = -1;}
+
+ bool isServerConnected = false;
+ UInt32 serverConnectionGuid = -1;
+ void ServerConnectionHandler (UInt32 guid) { isServerConnected = true; serverConnectionGuid = guid;}
+ void ServerDisconnectionHandler (UInt32 guid) { isServerConnected = false; serverConnectionGuid = -1;}
+
+ struct GeneralConnectionFixture
+ {
+ GeneralConnectionFixture ()
+ {
+ Initialize();
+ }
+ ~GeneralConnectionFixture ()
+ {
+ Destroy();
+ }
+
+ void Initialize ()
+ {
+ GeneralConnection::RunningUnitTest();
+ Rand r (GetProfileTime(START_TIME));
+ int multicastport = PLAYER_UNITTEST_MULTICAST_PORT + (r.Get() & PLAYER_PORT_MASK);
+ player = new PlayerConnection("", multicastport);
+ editor = new EditorConnection(multicastport);
+ editor->RegisterConnectionHandler( ClientConnectionHandler );
+ player->RegisterConnectionHandler( ServerConnectionHandler );
+ editor->RegisterDisconnectionHandler( ClientDisconnectionHandler );
+ player->RegisterDisconnectionHandler( ServerDisconnectionHandler );
+ }
+
+ void Destroy ()
+ {
+ delete player;
+ delete editor;
+ player = NULL;
+ editor = NULL;
+ }
+
+ void Connect ()
+ {
+ //record time
+ ABSOLUTE_TIME start = START_TIME;
+ while (GetProfileTime(ELAPSED_TIME(start)) < 1*kTimeSecond)
+ {
+ player->Poll();
+ editor->PollWithCustomMessage();
+ if (player->IsConnected() && editor->IsConnected()) break;
+ }
+ }
+
+ void CheckConnectionState(bool state)
+ {
+ // is internal connection state
+ CHECK_EQUAL (state, player->IsConnected() );
+ CHECK_EQUAL (state, editor->IsConnected() );
+ // is custom connect handler called
+ CHECK_EQUAL (state, isClientConnected);
+ CHECK_EQUAL (state, isServerConnected);
+ }
+
+ PlayerConnection* player;
+ EditorConnection* editor;
+ };
+
+ std::string message;
+ void HandleGeneralConnectionMessage (const void* data, UInt32 size, UInt32 guid)
+ {
+ message = std::string((char*)data);
+ }
+
+ int* largeMessage;
+ void HandleLargeMessage(const void* data, UInt32 size, UInt32 guid)
+ {
+ largeMessage = new int[size/sizeof(int)];
+ memcpy(largeMessage, data, size);
+ }
+
+ int messageCount = 0;
+ void HandleManyMessages(const void* data, UInt32 size, UInt32 guid)
+ {
+ CHECK_EQUAL(((char*)data)[10],messageCount);
+ messageCount++;
+ }
+
+ TEST_FIXTURE(GeneralConnectionFixture, CanDisconnectServer)
+ {
+ Connect();
+ CheckConnectionState(true);
+
+ player->DisconnectAll();
+
+ ABSOLUTE_TIME start = START_TIME;
+ while (GetProfileTime(ELAPSED_TIME(start)) < 1*kTimeSecond)
+ {
+ editor->PollWithCustomMessage();
+ if (!editor->IsConnected()) break;
+ }
+ CheckConnectionState(false);
+ }
+
+ TEST_FIXTURE(GeneralConnectionFixture, CanConnect)
+ {
+ Connect();
+ CheckConnectionState(true);
+ }
+
+ TEST_FIXTURE(GeneralConnectionFixture, CanDisconnectClient)
+ {
+ Connect();
+ CheckConnectionState(true);
+
+ editor->DisconnectAll();
+
+ ABSOLUTE_TIME start = START_TIME;
+ while (GetProfileTime(ELAPSED_TIME(start)) < 1*kTimeSecond)
+ {
+ player->Poll();
+ if (!player->IsConnected()) break;
+ }
+ CheckConnectionState(false);
+ }
+
+ TEST_FIXTURE(GeneralConnectionFixture, CanSendMessage)
+ {
+ Connect();
+ editor->RegisterMessageHandler(GeneralConnection::kLastMessageID,HandleGeneralConnectionMessage);
+ player->RegisterMessageHandler(GeneralConnection::kLastMessageID,HandleGeneralConnectionMessage);
+
+ player->SendMessage(serverConnectionGuid, GeneralConnection::kLastMessageID, "Hello World", 12);
+ ABSOLUTE_TIME start = START_TIME;
+ while (GetProfileTime(ELAPSED_TIME(start)) < 1*kTimeSecond)
+ {
+ player->Poll();
+ editor->PollWithCustomMessage();
+ if (!message.empty()) break;
+ }
+ CHECK_EQUAL (std::string("Hello World"), message);
+
+ message = std::string("");
+ editor->SendMessage(clientConnectionGuid, GeneralConnection::kLastMessageID, "Are you there", 14);
+ start = START_TIME;
+ while (GetProfileTime(ELAPSED_TIME(start)) < 1*kTimeSecond)
+ {
+ player->Poll();
+ editor->PollWithCustomMessage();
+ if (!message.empty()) break;
+ }
+ CHECK_EQUAL (std::string("Are you there"), message);
+
+ }
+
+ TEST_FIXTURE(GeneralConnectionFixture, SendLargeMessage)
+ {
+ Connect();
+ editor->RegisterMessageHandler(GeneralConnection::kLastMessageID,HandleLargeMessage);
+
+ int msgSize = 400*1024;
+ int* buffer = new int[msgSize];
+ for(int i = 0; i < msgSize; i++)
+ buffer[i] = i;
+ largeMessage = NULL;
+ player->SendMessage(serverConnectionGuid, GeneralConnection::kLastMessageID, (char*)buffer, msgSize*sizeof(int));
+ ABSOLUTE_TIME start = START_TIME;
+ while (GetProfileTime(ELAPSED_TIME(start)) < 2*kTimeSecond)
+ {
+ player->Poll();
+ editor->PollWithCustomMessage();
+ if (largeMessage != NULL) break;
+ }
+ if (largeMessage != NULL)
+ {
+ CHECK_EQUAL (1, largeMessage[1]);
+ CHECK_EQUAL (1024, largeMessage[1024]);
+ CHECK_EQUAL (10000, largeMessage[10000]);
+ CHECK_EQUAL (msgSize -1, largeMessage[msgSize-1]);
+ }
+ else
+ CHECK(largeMessage != NULL);
+
+ delete[] largeMessage; largeMessage = NULL;
+ }
+
+ TEST_FIXTURE(GeneralConnectionFixture, SendMultipleMessage)
+ {
+ Connect();
+ editor->RegisterMessageHandler(GeneralConnection::kLastMessageID,HandleManyMessages);
+
+ char message[20];
+
+ for(int i = 0; i < 100; i++)
+ {
+ message[10] = (char)i;
+ player->SendMessage(serverConnectionGuid, GeneralConnection::kLastMessageID, message, 20);
+ }
+
+ ABSOLUTE_TIME start = START_TIME;
+ while (GetProfileTime(ELAPSED_TIME(start)) < 1*kTimeSecond)
+ {
+ player->Poll();
+ editor->PollWithCustomMessage();
+ if (messageCount == 100) break;
+ }
+ CHECK_EQUAL (100, messageCount);
+ }
+}
+
+#endif
+
+#endif // ENABLE_PLAYERCONNECTION
diff --git a/Runtime/Network/PlayerCommunicator/GeneralConnection.h b/Runtime/Network/PlayerCommunicator/GeneralConnection.h
new file mode 100644
index 0000000..1f253d8
--- /dev/null
+++ b/Runtime/Network/PlayerCommunicator/GeneralConnection.h
@@ -0,0 +1,266 @@
+#pragma once
+
+#if ENABLE_PLAYERCONNECTION
+#define ENABLE_MULTICAST (!UNITY_FLASH)
+
+#include "Runtime/Serialize/SwapEndianBytes.h"
+#include "Runtime/Network/MulticastSocket.h"
+#include "Runtime/Network/ServerSocket.h"
+#include "Runtime/Network/SocketStreams.h"
+
+
+#if UNITY_WIN
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#elif UNITY_XENON
+#include <xtl.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <errno.h>
+#endif
+#include "Runtime/Profiler/TimeHelper.h"
+#include "Runtime/Threads/Mutex.h"
+
+
+// used ports:
+// MulticastPort : 54998
+// ListenPorts : 55000 - 55511
+// Multicast(unittests) : 55512 - 56023
+
+#define ENABLE_PLAYER_CONNECTION_DEBUG_LOG 0
+
+#if ENABLE_PLAYER_CONNECTION_DEBUG_LOG
+#define LOG_PLAYER_CONNECTION(str) { \
+ bool oldlog = IsLogEnabled(); \
+ SetLogEnabled(false); \
+ printf_console ("[%04x] %s\n", Thread::GetCurrentThreadID(), (str).c_str()); \
+ SetLogEnabled(oldlog); \
+}
+#else
+#define LOG_PLAYER_CONNECTION(str)
+#endif
+
+#if SUPPORT_THREADS
+#define LOG_PLAYER_CONNECTION_CRITICAL(str) printf_console ("[%04x] %s\n", Thread::GetCurrentThreadID(), (str).c_str())
+#else
+#define LOG_PLAYER_CONNECTION_CRITICAL(str) printf_console ("[] %s\n", (str).c_str())
+#endif
+
+#define PLAYER_MULTICAST_GROUP "225.0.0.222"
+#define PLAYER_MULTICAST_PORT 54997
+
+#define PLAYER_DIRECTCONNECT_PORT 54999
+#define PLAYER_DIRECTCONNECT_GUID 1337
+
+#define PLAYER_DIRECT_IP_CONNECT_GUID 0xFEED
+
+#define PLAYER_LISTEN_PORT 55000
+#define PLAYER_UNITTEST_MULTICAST_PORT 55512
+#define PLAYER_PORT_MASK 511
+#define PLAYER_MESSAGE_MAGIC_NUMBER 0x4E8F
+
+#define PLAYER_CONNECTION_CONFIG_DATA_FORMAT_LISTEN "listen %u %d %d %d"
+#define PLAYER_CONNECTION_CONFIG_DATA_FORMAT_LISTEN_PRINT "listen <guid> <debugging> <waitonstartup> <startprofiler>"
+#define PLAYER_CONNECTION_CONFIG_DATA_FORMAT_CONNECT_LIST "connect %s %s %s %s %s %s %s %s %s %s"
+#define PLAYER_CONNECTION_CONFIG_DATA_FORMAT_CONNECT "connect %s"
+#define PLAYER_CONNECTION_CONFIG_DATA_FORMAT_CONNECT_PRINT "connect <ip>"
+
+#define SERVER_IDENTIFICATION_FORMAT "[IP] %s [Port] %lu [Flags] %lu [Guid] %lu [EditorId] %lu [Version] %d [Id] %s [Debug] %d"
+
+
+#define ANY_PLAYERCONNECTION 0
+
+bool SocketCallSucceeded(int res);
+
+class GeneralConnection
+{
+public:
+ enum MulticastFlags
+ {
+ kRequestImmediateConnect = 1<<0,
+ kSupportsProfile = 1<<1,
+ kCustomMessage = 1<<2
+ };
+
+ enum { kMulticastBufferSize = 256 };
+ enum { kDataBufferMaxSize = 8*1024*1024 };
+ enum { kDataBufferFlushThreshold = 7*1024*1024 };
+
+#if ENABLE_MULTICAST
+ // Parsed struct from a buffer that contains all the information sent by the player to the editor
+ // Used for showing a list of all available profilers and performing autoconnect on the profiler
+ struct MulticastInfo
+ {
+ MulticastInfo();
+
+ bool Parse (const char* buffer, void* in = NULL);
+
+ bool IsValid () const { return m_Valid; }
+ bool IsLocalhost() const { return m_IsLocalhost; }
+
+ UInt32 GetGuid () const { return m_Guid; }
+ UInt32 GetEditorGuid () const { return m_EditorGuid; }
+
+ UInt32 GetPort () const { return m_Port; }
+ std::string const& GetIP () const { return m_Ip; }
+
+ bool ImmediateConnect() const { return (m_Flags & kRequestImmediateConnect) != 0; }
+ bool HasCustomMessage() const { return (m_Flags & kCustomMessage) != 0; }
+ bool HasProfiler() const { return (m_Flags & kSupportsProfile) != 0; }
+
+ std::string const& GetIdentifier() const { return m_Identifier; }
+ private:
+
+ void Clear ();
+ void SetLocalhostFlag();
+
+ std::string m_Buffer;
+ std::string m_Ip;
+ UInt32 m_Port;
+ UInt32 m_Flags;
+ UInt32 m_Guid;
+ UInt32 m_EditorGuid;
+ std::string m_Identifier;
+ bool m_Valid;
+ bool m_IsLocalhost;
+ };
+#endif
+
+ enum MessageID{
+ // messageID 1 - 31 reserved for (future) internal use
+ kProfileDataMessage = 32,
+ kProfileStartupInformation = 33,
+
+ kObjectMemoryProfileSnapshot = 40, //request the memory profile
+ kObjectMemoryProfileDataMessage = 41, //send the object memory profile
+
+ kLogMessage = 100,
+ kCleanLogMessage = 101,
+
+ kFileTransferMessage = 200,
+ kFileReadyMessage = 201,
+ kCaptureHeaphshotMessage = 202,
+
+ kPingAliveMessage = 300,
+ kApplicationQuitMessage = 301,
+ kLastMessageID,
+ };
+
+ struct NetworkMessage
+ {
+ void InitializeMessage();
+ bool CheckMessageValidity();
+ void SetID (MessageID id);
+ MessageID GetID ();
+ void SetDataSize (UInt32 size);
+ UInt32 GetDataSize ();
+
+ bool AllowSkip(){return m_ID == kProfileDataMessage;}
+
+ private:
+ UInt16 m_MagicNumber; // in little endian
+ UInt16 m_ID; // in little endian
+ UInt32 m_Size; // in little endian
+ };
+
+ struct Connection
+ {
+ public:
+ Connection(TSocketHandle socketHandle);
+ ~Connection();
+
+ bool IsValid() const { return m_SocketStream.IsConnected(); };
+ bool Poll() { return m_SocketStream.Poll(5); }
+ bool HasBytesToSend() const { return m_SocketStream.SendBuffer().GetAvailableSize() > 0; }
+ const void* ReceiveMessage(NetworkMessage* message);
+ void ReleaseReceivedMessage();
+ void SendMessage(NetworkMessage& message, const void* data);
+
+ UInt32 GetSendBufferSize() const { return m_SocketStream.SendBuffer().GetAllocatedSize(); }
+ UInt32 GetRecvBufferSize() const { return m_SocketStream.RecvBuffer().GetAllocatedSize(); }
+
+ private:
+ Mutex m_SendMutex;
+ Mutex m_RecvMutex;
+ NetworkMessage m_PendingMessage;
+ void* m_PendingMessageData;
+ bool m_LocallyAllocatedMessageData;
+ bool m_HasPendingMessage;
+#if UNITY_FLASH
+ BufferedSocketStream m_SocketStream;
+#elif UNITY_WINRT
+ BufferedSocketStream m_SocketStream;
+#else
+ ThreadedSocketStream m_SocketStream;
+#endif
+ };
+
+ virtual ~GeneralConnection ();
+
+ bool IsConnected () { return !m_Connections.empty(); }
+ void SetLogEnabled (bool v) { m_LogEnabled = v; }
+ bool IsLogEnabled () const { return m_LogEnabled; }
+ void DisconnectAll ();
+
+ void SendMessage (UInt32 guid, MessageID id, const void* data, UInt32 size);
+
+ typedef void (*MessageHandlerFunc) (const void* data, UInt32 size, UInt32 guid);
+ void RegisterMessageHandler (MessageID messageID, MessageHandlerFunc func);
+ void UnregisterMessageHandler (MessageID messageID, MessageHandlerFunc func);
+
+ typedef void (*ConnectionHandlerFunc) (UInt32 guid);
+ void RegisterDisconnectionHandler( ConnectionHandlerFunc func );
+ void RegisterConnectionHandler( ConnectionHandlerFunc func );
+ void UnregisterDisconnectionHandler( ConnectionHandlerFunc func );
+ void UnregisterConnectionHandler( ConnectionHandlerFunc func );
+
+ virtual void WaitForFinish();
+
+ UInt32 GetLocalGuid() const;
+
+ static void RunningUnitTest() { ms_RunningUnitTests = true;}
+
+protected:
+ GeneralConnection ();
+
+ bool HasBytesToSend() const;
+
+ static void Initialize ();
+ static void Cleanup ();
+
+ void RegisterConnection (UInt32 guid, TSocketHandle socketHandle);
+ void Disconnect (UInt32 guid);
+
+ Connection* GetConnection(UInt32 guid);
+
+ virtual bool IsServer () = 0;
+
+ void Poll ();
+
+protected:
+ std::string m_LocalIP;
+
+#if ENABLE_MULTICAST
+ MulticastSocket m_MulticastSocket;
+#endif
+
+ typedef std::map< int, Connection* > ConnectionMap;
+ ConnectionMap m_Connections;
+
+ std::map< MessageID, MessageHandlerFunc > m_HandlerMap;
+ std::vector< ConnectionHandlerFunc > m_ConnectionHandlers;
+ std::vector< ConnectionHandlerFunc > m_DisconnectionHandlers;
+
+ UInt32 m_LocalGuid;
+
+ bool m_LogEnabled;
+
+ static bool ms_RunningUnitTests;
+ static int ms_Version;
+};
+
+#endif // ENABLE_PLAYERCONNECTION
diff --git a/Runtime/Network/PlayerCommunicator/GeneralConnectionInternals.h b/Runtime/Network/PlayerCommunicator/GeneralConnectionInternals.h
new file mode 100644
index 0000000..15f5714
--- /dev/null
+++ b/Runtime/Network/PlayerCommunicator/GeneralConnectionInternals.h
@@ -0,0 +1,56 @@
+#pragma once
+
+#include "Runtime/Threads/AtomicOps.h"
+#include "Runtime/Network/SocketConsts.h"
+#if UNITY_WIN
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#elif UNITY_XENON
+#include <xtl.h>
+typedef int socklen_t;
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <errno.h>
+#endif
+#if UNITY_PS3
+#include <sys/time.h>
+#include <netex/net.h>
+#include <netex/errno.h>
+#define SOCK_ERROR(s) ((s) < 0)
+#elif UNITY_WINRT
+#define SOCK_ERROR(s) ((s) == nullptr)
+#else
+#define SOCK_ERROR(s) ((s) == -1)
+#endif
+
+
+static const ProfileTimeFormat kTimeMillisecond = 1000000ULL;
+static const ProfileTimeFormat kTimeSecond = 1000000000ULL;
+static const ProfileTimeFormat kPlayerConnectionInitialWaitTimeout = 5*kTimeSecond;
+
+#if UNITY_FLASH
+extern "C" void Ext_GetSocketPolicyFile(const char* ipString);
+extern int g_AlchemySocketErrno;
+extern "C" int flash_socket(int domain, int type, int protocol);
+extern "C" int flash_connect(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
+extern "C" int flash_close(int sockfd);
+extern "C" ssize_t flash_recvfrom (int sockfd, void* buffer, size_t length, int flags, struct sockaddr* address, socklen_t* address_len);
+extern "C" ssize_t flash_sendto(int, const void *, size_t, int, const struct sockaddr*, socklen_t);
+extern "C" int flash_setsockopt(int sockfd, int level, int option_name, const void* option_value, socklen_t option_len);
+#define setsockopt flash_setsockopt
+#define recvfrom flash_recvfrom
+#define sendto flash_sendto
+#define socket flash_socket
+#define connect flash_connect
+#define close flash_close
+#endif
+
+static UInt32 NextGUID()
+{
+ static volatile int guid_counter = 0;
+ return (UInt32) AtomicIncrement(&guid_counter);
+}
diff --git a/Runtime/Network/PlayerCommunicator/PlayerConnection.cpp b/Runtime/Network/PlayerCommunicator/PlayerConnection.cpp
new file mode 100644
index 0000000..04beece
--- /dev/null
+++ b/Runtime/Network/PlayerCommunicator/PlayerConnection.cpp
@@ -0,0 +1,535 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_PLAYERCONNECTION
+
+#include "PlayerConnection.h"
+#include "Runtime/Misc/SystemInfo.h"
+
+#include "Runtime/Serialize/FileCache.h"
+#include "Runtime/Serialize/CacheWrap.h"
+
+#include "Runtime/Network/NetworkUtility.h"
+#include "Runtime/Profiler/TimeHelper.h"
+#include "Runtime/Math/Random/rand.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Utilities/PlayerPrefs.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Network/PlayerCommunicator/GeneralConnectionInternals.h"
+#include "Runtime/Network/SocketConsts.h"
+
+#if UNITY_ANDROID
+#include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+#include "PlatformDependent/AndroidPlayer/DVMCalls.h"
+#endif
+
+#define ALL_INTERFACES_IP "0.0.0.0"
+
+const char* kPlayerConnectionConfigFile = "PlayerConnectionConfigFile";
+
+PlayerConnection* PlayerConnection::ms_Instance = NULL;
+
+PlayerConnection::PlayerConnection(const std::string& dataPath, unsigned short multicastPort, bool enableDebugging)
+: GeneralConnection()
+, m_WaitingForPlayerConnectionBeforeStartingPlayback(false)
+#if ENABLE_LISTEN_SOCKET
+ , m_ListenSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
+#endif
+#if UNITY_ANDROID
+, m_UnixSocket(AF_LOCAL, SOCK_STREAM, 0)
+#endif
+{
+ ABSOLUTE_TIME_INIT(m_LastMulticast);
+
+ m_IsPlayerConnectionEnabled = false;
+
+ bool hasConfigFile = ReadConfigFile(dataPath);
+ if (!hasConfigFile)
+ m_AllowDebugging = (enableDebugging? 1: 0);
+
+ if (!PLATFORM_SUPPORTS_PLAYERCONNECTION_LISTENING && !hasConfigFile)
+ {
+ printf_console("PlayerConnection disabled - listening mode not supported\n");
+ return;
+ }
+
+ m_IsPlayerConnectionEnabled = true;
+
+ if (m_InitiateMode == kPlayerConnectionInitiateByConnecting)
+ {
+ for (int i = 0; i < m_NumIPs; ++i)
+ {
+ m_ConnectToIP = m_ConnectToIPList[i];
+#if UNITY_FLASH
+ Ext_GetSocketPolicyFile(m_ConnectToIP.c_str());
+#endif
+ printf_console("Connecting directly to [%s]...\n", m_ConnectToIP.c_str());
+ // Try to connect to next IP
+ Poll();
+ if (IsConnected())
+ {
+ break;
+ }
+ }
+
+ if (!IsConnected())
+ {
+ ErrorString("Connecting to host failed, aborting playback");
+#if UNITY_XENON
+ XLaunchNewImage(XLAUNCH_KEYWORD_DEFAULT_APP, 0);
+#elif !UNITY_FLASH // Cant handle exit
+ exit(1);
+#endif
+ }
+
+ return;
+ }
+
+ // so we are in listening mode.
+
+ CreateListenSocket ();
+#if UNITY_ANDROID
+ CreateUnixSocket();
+#endif
+ m_HostName = GetHostName ();
+ std::replace (m_HostName.begin (), m_HostName.end (), ' ', '_');
+ m_WhoAmI = ConstructWhoamiString ();
+ InitializeMulticastAddress (multicastPort);
+
+ if (m_WaitingForPlayerConnectionBeforeStartingPlayback)
+ {
+ ABSOLUTE_TIME startTime = START_TIME;
+ printf_console("Waiting for connection from host on [%s:%i]...\n", m_LocalIP.c_str(), (int)m_ListenPort);
+
+ // Try to connect for some time
+ while((GetProfileTime(ELAPSED_TIME(startTime)) < kPlayerConnectionInitialWaitTimeout) && (!IsConnected()))
+ {
+ Poll();
+ Thread::Sleep(0.05);
+ }
+ }
+
+ if (!IsConnected() && m_WaitingForPlayerConnectionBeforeStartingPlayback)
+ printf_console("Timed out. Continuing without host connection.\n");
+}
+
+std::string PlayerConnection::ConstructWhoamiString ()
+{
+ std::string runtimeAndHostName = Format ("%s(%s)",
+ systeminfo::GetRuntimePlatformString ().c_str (),
+ m_HostName.c_str ());
+ UInt32 flags = (ImmediateConnect () ? kRequestImmediateConnect : 0);
+ flags |= (ENABLE_PROFILER ? kSupportsProfile : 0);
+ std::string whoAmI = Format (SERVER_IDENTIFICATION_FORMAT,
+ m_LocalIP.c_str (), (UInt32)m_ListenPort,
+ flags, m_LocalGuid,
+ m_EditorGuid, ms_Version,
+ runtimeAndHostName.c_str (),
+ m_AllowDebugging);
+ return whoAmI;
+}
+
+void PlayerConnection::InitializeMulticastAddress (UInt16 multicastPort)
+{
+ Assert (m_InitiateMode == kPlayerConnectionInitiateByListening);
+
+#if !UNITY_FLASH
+ // We use broadcast in case of XBOX360 or AdHoc connection
+ // For AdHoc connections we need to specify Auto IP broadcast address instead of 255.255.255.255
+ if (UNITY_XENON || m_LocalIP.find("169.254") == 0)
+ {
+ const char* broadcastAddress = (UNITY_XENON || UNITY_BB10) ? "255.255.255.255" : "169.254.255.255";
+ if (!m_MulticastSocket.Initialize(broadcastAddress, multicastPort))
+ ErrorString("Unable to setup multicast socket for player connection.");
+ if (!m_MulticastSocket.SetBroadcast(true))
+ ErrorString("Unable to set broadcast mode for player connection socket.");
+ printf_console("Broadcasting \"%s\" to [%s:%i]...\n", m_WhoAmI.c_str(), broadcastAddress, (int)multicastPort);
+ }
+ // For all other cases we use multicast address
+ else
+ {
+ if (!m_MulticastSocket.Initialize(PLAYER_MULTICAST_GROUP, multicastPort))
+ ErrorString("Unable to setup multicast socket for player connection.");
+ printf_console("Multi-casting \"%s\" to [%s:%i]...\n", m_WhoAmI.c_str(), PLAYER_MULTICAST_GROUP, (int)multicastPort);
+ #if UNITY_EDITOR
+ m_MulticastSocket.SetTTL(ms_RunningUnitTests ? 0 : 31);
+ #else
+ m_MulticastSocket.SetTTL(31);
+ #endif
+ m_MulticastSocket.SetLoop(true);
+ }
+
+
+#endif
+}
+
+
+void PlayerConnection::CreateListenSocket ()
+{
+ Assert (m_InitiateMode == kPlayerConnectionInitiateByListening);
+
+ // create a random listen port (will be send out with the multicast ping)
+ Rand r (GetProfileTime(START_TIME));
+ m_ListenPort = PLAYER_LISTEN_PORT + (r.Get() & PLAYER_PORT_MASK);
+
+#if ENABLE_LISTEN_SOCKET
+ InitializeListenSocket(m_ListenSocket, ALL_INTERFACES_IP, m_ListenPort);
+#endif
+}
+
+#if ENABLE_LISTEN_SOCKET
+void PlayerConnection::InitializeListenSocket(ServerSocket& socket, const std::string& localIP, int listenPort)
+{
+ printf_console("PlayerConnection initialized network socket : %s %i\n", localIP.c_str(), listenPort);
+ socket.StartListening(localIP.c_str(), listenPort, false);
+}
+#endif
+
+#if UNITY_ANDROID
+void PlayerConnection::CreateUnixSocket ()
+{
+ Assert (m_InitiateMode == kPlayerConnectionInitiateByListening);
+ InitializeUnixSocket (m_UnixSocket, Format("Unity-%s", DVM::GetPackageName()));
+}
+#endif
+
+#if UNITY_ANDROID
+void PlayerConnection::InitializeUnixSocket (ServerSocket& socket, const std::string& name)
+{
+ printf_console("PlayerConnection initialized unix socket : %s\n", name.c_str());
+ size_t len = name.length();
+
+ struct sockaddr_un address;
+ Assert (len < sizeof(address.sun_path));
+ memset(&address, 0, sizeof(sockaddr_un));
+ memcpy(address.sun_path + 1, name.data(), len);
+ address.sun_path[0] = 0;
+ address.sun_family = AF_LOCAL;
+
+ socklen_t address_len = offsetof(struct sockaddr_un, sun_path) + len + 1;
+ socket.StartListening((const sockaddr *) &address, address_len, false);
+}
+#endif
+
+bool PlayerConnection::ReadConfigFile (const std::string& dataPath)
+{
+ m_InitiateMode = kPlayerConnectionInitiateByListening;
+ m_EditorGuid = -1;
+ m_AllowDebugging = 0;
+ m_EnableProfiler = 0;
+ m_WaitingForPlayerConnectionBeforeStartingPlayback = 0;
+ int tmpWaiting = 0;
+
+ std::string configFile = AppendPathName(dataPath, kPlayerConnectionConfigFile);
+ if (!IsFileCreated(configFile))
+ return false;
+
+ InputString confData;
+ ReadStringFromFile(&confData, configFile);
+ char tmp[100];
+
+ if (sscanf(confData.c_str(), PLAYER_CONNECTION_CONFIG_DATA_FORMAT_LISTEN, (unsigned*)&m_EditorGuid, &m_AllowDebugging, &tmpWaiting, &m_EnableProfiler) == 4)
+ {
+ m_WaitingForPlayerConnectionBeforeStartingPlayback = tmpWaiting;
+ m_InitiateMode = kPlayerConnectionInitiateByListening;
+ return true;
+ }
+
+ m_NumIPs = sscanf(confData.c_str(), PLAYER_CONNECTION_CONFIG_DATA_FORMAT_CONNECT_LIST, m_ConnectToIPList[0], m_ConnectToIPList[1], m_ConnectToIPList[2],
+ m_ConnectToIPList[3], m_ConnectToIPList[5], m_ConnectToIPList[6], m_ConnectToIPList[7], m_ConnectToIPList[8], m_ConnectToIPList[9]);
+
+ if (m_NumIPs > 0)
+ {
+ m_InitiateMode = kPlayerConnectionInitiateByConnecting;
+ return true;
+ }
+
+ ErrorString(Format("PlayerConnection config should be in the format: \"%s\" or \"%s\"", PLAYER_CONNECTION_CONFIG_DATA_FORMAT_LISTEN_PRINT, PLAYER_CONNECTION_CONFIG_DATA_FORMAT_CONNECT_PRINT));
+ return false;
+}
+
+PlayerConnection& PlayerConnection::Get()
+{
+ return *ms_Instance;
+}
+
+void PlayerConnection::Initialize (const std::string& dataPath, bool enableDebugging)
+{
+ if (ms_Instance == NULL)
+ {
+ SET_ALLOC_OWNER(NULL);
+ printf_console("PlayerConnection initialized from %s (debug = %i)\n", dataPath.c_str(), enableDebugging);
+ GeneralConnection::Initialize();
+ ms_Instance = new PlayerConnection(dataPath, PLAYER_MULTICAST_PORT, enableDebugging);
+ }
+ else
+ {
+ if (ms_Instance->m_IsPlayerConnectionEnabled)
+ {
+ switch (ms_Instance->m_InitiateMode)
+ {
+ case kPlayerConnectionInitiateByListening:
+ printf_console("PlayerConnection already initialized - listening to [%s:%i]\n", ms_Instance->m_LocalIP.c_str(), (int)ms_Instance->m_ListenPort);
+ break;
+ case kPlayerConnectionInitiateByConnecting:
+ printf_console("PlayerConnection already initialized - connecting to [%s:%i]\n", ms_Instance->m_ConnectToIP.c_str(), PLAYER_DIRECTCONNECT_PORT);
+ break;
+ default:
+ printf_console("PlayerConnection already initialized - unknown mode\n");
+ break;
+ }
+ }
+ else
+ {
+ printf_console("PlayerConnection already initialized, but disabled\n");
+ }
+ }
+}
+
+void PlayerConnection::Cleanup ()
+{
+ Assert(ms_Instance != NULL);
+ delete ms_Instance;
+ ms_Instance = NULL;
+ GeneralConnection::Cleanup();
+}
+
+void PlayerConnection::PollListenMode()
+{
+ Assert (m_InitiateMode == kPlayerConnectionInitiateByListening);
+#if ENABLE_LISTEN_SOCKET
+ if(!m_IsPlayerConnectionEnabled )
+ return;
+
+ if (!IsConnected() || GetProfileTime(ELAPSED_TIME(m_LastMulticast)) > 1*kTimeSecond)
+ {
+ TSocketHandle socketHandle;
+#if UNITY_WINRT
+ if(m_ListenSocket.IsListening() && !SOCK_ERROR(socketHandle = m_ListenSocket.Accept()))
+ {
+ printf_console("PlayerConnection accepted from WinRT socket\n");
+ CreateAndReportConnection(socketHandle);
+ }
+#endif
+#if UNITY_ANDROID
+ if (m_UnixSocket.IsListening() && !SOCK_ERROR(socketHandle = m_UnixSocket.Accept()))
+ {
+ printf_console("PlayerConnection accepted from unix socket\n");
+ CreateAndReportConnection(socketHandle);
+ }
+#endif
+#if !UNITY_WINRT
+ // player looking for connections
+ struct sockaddr_in remoteAddr;
+ socklen_t remoteAddrLen = sizeof(remoteAddr);
+ if(m_ListenSocket.IsListening() && !SOCK_ERROR(socketHandle = m_ListenSocket.Accept((sockaddr*)&remoteAddr, &remoteAddrLen)))
+ {
+ printf_console("PlayerConnection accepted from [%s]\n", InAddrToIP(&remoteAddr).c_str());
+ CreateAndReportConnection(socketHandle);
+ }
+#endif
+
+ // broadcast ip and port with 1 sec interval
+ // 10ms interval if immediate connect is set
+ UInt64 interval = 1*kTimeSecond;
+ if (!IsConnected() && ImmediateConnect())
+ interval = 10*kTimeMillisecond;
+
+ if (GetProfileTime(ELAPSED_TIME(m_LastMulticast)) > interval)
+ {
+ m_LastMulticast = START_TIME;
+ m_MulticastSocket.Send(m_WhoAmI.c_str (), m_WhoAmI.length () + 1);
+ }
+ }
+#endif
+}
+
+void PlayerConnection::CreateAndReportConnection(TSocketHandle socketHandle)
+{
+ RegisterConnection(NextGUID(), socketHandle);
+}
+
+void PlayerConnection::PollConnectMode()
+{
+ Assert (m_InitiateMode == kPlayerConnectionInitiateByConnecting);
+
+ if(!m_IsPlayerConnectionEnabled )
+ return;
+
+ if (IsConnected())
+ return;
+
+ int port = PLAYER_DIRECTCONNECT_PORT;
+ TSocketHandle socketHandle;
+ if (SOCK_ERROR(socketHandle = ::Socket::Connect(m_ConnectToIP.c_str(), port)))
+ {
+ ErrorStringMsg("Connect failed for direct socket. Ip=%s, port=%d", m_ConnectToIP.c_str(), port);
+ return;
+ }
+ CreateAndReportConnection(socketHandle);
+}
+
+
+void PlayerConnection::Poll()
+{
+ GeneralConnection::Poll();
+
+ switch(m_InitiateMode)
+ {
+ case kPlayerConnectionInitiateByListening:
+ PollListenMode ();
+ break;
+ case kPlayerConnectionInitiateByConnecting:
+ PollConnectMode ();
+ break;
+ }
+}
+
+static int custom_asprintf(char** buffer, const char* log, va_list alist)
+{
+ va_list list, sizelist;
+ va_copy (list, alist);
+ va_copy (sizelist, alist);
+ int result = 0;
+
+#if USE_WINSOCK_APIS || UNITY_WINRT
+ int bufferSize = _vscprintf(log, list) + 1;
+ *buffer = (char *)UNITY_MALLOC_ALIGNED(kMemUtility, bufferSize, 4);
+ result = vsnprintf(*buffer, bufferSize, log, list);
+#elif UNITY_PS3 || defined(__GNUC__)
+ int bufferSize = vsnprintf(0, 0, log, sizelist) + 1;
+ *buffer = (char *)UNITY_MALLOC_ALIGNED(kMemUtility, bufferSize, 4);;
+ result = vsnprintf(*buffer, bufferSize, log, list);
+#else
+#error "Not implemented"
+#endif
+
+ va_end (sizelist);
+ va_end (list);
+ return result;
+}
+
+void LogToPlayerConnectionMessage(LogType logType, PlayerConnection::MessageID msgId, const char* log, va_list alist)
+{
+ va_list list;
+ va_copy (list, alist);
+
+ PlayerConnection& pc = PlayerConnection::Get();
+ if (pc.IsConnected() && pc.IsLogEnabled())
+ {
+
+ // don't try to recursively sent logs from inside player connection over player connection
+ pc.SetLogEnabled (false);
+
+ char* buffer = NULL;
+ int len = custom_asprintf(&buffer, log, list);
+
+ if (len >= 0 && buffer && buffer[0] != 0)
+ PlayerConnection::Get().SendMessage(ANY_PLAYERCONNECTION, msgId, buffer, len);
+
+ if (buffer)
+ UNITY_FREE (kMemUtility, buffer);
+
+ pc.SetLogEnabled (true);
+ }
+ va_end (list);
+}
+
+bool PlainLogToPlayerConnection (LogType logType, const char* log, va_list alist)
+{
+ va_list list;
+ va_copy (list, alist);
+ LogToPlayerConnectionMessage(logType, PlayerConnection::kLogMessage, log, list);
+ va_end (list);
+ return true;
+}
+
+bool CleanLogToPlayerConnection (LogType logType, const char* log, va_list alist)
+{
+ va_list list;
+ va_copy (list, alist);
+ LogToPlayerConnectionMessage(logType, PlayerConnection::kCleanLogMessage, log, list);
+ va_end (list);
+ return true;
+}
+
+void InstallPlayerConnectionLogging (bool install)
+{
+ if (install)
+ {
+ SetLogEntryHandler(&PlainLogToPlayerConnection);
+ AddCleanLogEntryHandler(&CleanLogToPlayerConnection);
+ }
+ else
+ {
+ SetLogEntryHandler(NULL);
+ }
+}
+
+void TransferFileOverPlayerConnection(const std::string& fname, void* body, unsigned int length, void* header, unsigned int headerLength)
+{
+#if !UNITY_EDITOR
+ printf_console("about to send file over playerconnection %s with length %d\n",fname.c_str(),length);
+
+ dynamic_array<UInt8> buffer;
+
+ MemoryCacheWriter memoryCache (buffer);
+ CachedWriter writeCache;
+
+ unsigned int fnameLength = fname.length();
+
+ unsigned int fnameLengthLE = fnameLength;
+ unsigned int lengthLE = length + headerLength;
+ SwapEndianBytesNativeToLittle(fnameLengthLE);
+ SwapEndianBytesNativeToLittle(lengthLE);
+
+ writeCache.InitWrite (memoryCache);
+ writeCache.Write(&fnameLengthLE, sizeof(fnameLengthLE));
+ writeCache.Write((void*)fname.c_str(), fnameLength);
+ writeCache.Write(&lengthLE, sizeof(lengthLE));
+ if (headerLength > 0)
+ writeCache.Write(header, headerLength);
+ writeCache.Write(body, length);
+
+ writeCache.CompleteWriting();
+
+ PlayerConnection::Get().SendMessage(ANY_PLAYERCONNECTION, GeneralConnection::kFileTransferMessage, &buffer[0], buffer.size());
+
+ // ugly hack to fix gfx tests
+ PlayerConnection& playercnx = PlayerConnection::Get();
+ while (playercnx.IsTestrigMode())
+ {
+ playercnx.Poll();
+ if (!playercnx.HasBytesToSend())
+ break;
+
+ Thread::Sleep(0.005);
+ }
+
+#endif //!UNITY_EDITOR
+}
+
+void NotifyFileReadyOverPlayerConnection(const std::string& fname)
+{
+#if !UNITY_EDITOR
+ dynamic_array<UInt8> buffer;
+
+ MemoryCacheWriter memoryCache (buffer);
+ CachedWriter writeCache;
+
+ unsigned int fnameLength = fname.length();
+ unsigned int fnameLengthLE = fnameLength;
+ SwapEndianBytesNativeToLittle(fnameLengthLE);
+
+ writeCache.InitWrite (memoryCache);
+ writeCache.Write(&fnameLengthLE, sizeof(fnameLengthLE));
+ writeCache.Write((void*)fname.c_str(), fnameLength);
+
+ writeCache.CompleteWriting();
+
+ PlayerConnection::Get().SendMessage(ANY_PLAYERCONNECTION, GeneralConnection::kFileReadyMessage, &buffer[0], buffer.size());
+#endif //!UNITY_EDITOR
+}
+
+
+#endif // ENABLE_PLAYERCONNECTION
diff --git a/Runtime/Network/PlayerCommunicator/PlayerConnection.h b/Runtime/Network/PlayerCommunicator/PlayerConnection.h
new file mode 100644
index 0000000..07e2159
--- /dev/null
+++ b/Runtime/Network/PlayerCommunicator/PlayerConnection.h
@@ -0,0 +1,111 @@
+#pragma once
+
+#if ENABLE_PLAYERCONNECTION
+#define ENABLE_LISTEN_SOCKET (!UNITY_FLASH)
+
+#include "GeneralConnection.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+
+
+#if UNITY_WIN
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#elif UNITY_XENON
+#include <xtl.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <errno.h>
+#endif
+#include "Runtime/Profiler/TimeHelper.h"
+#include "Runtime/Threads/Mutex.h"
+#if UNITY_ANDROID
+#include <sys/un.h>
+#endif
+
+// flags are bits denoting capabilities of the broadcaster
+enum PlayerConnectionInitiateMode
+ {
+ kPlayerConnectionInitiateByListening,
+ kPlayerConnectionInitiateByConnecting
+ };
+
+class PlayerConnection : public GeneralConnection
+{
+public:
+ PlayerConnection (const std::string& dataPath = "", unsigned short multicastPort = PLAYER_MULTICAST_PORT, bool enableDebugging=false);
+
+ static void Initialize (const std::string& dataPath, bool enableDebugging=false);
+ static void Cleanup ();
+
+ // Singleton accessor for playerconnection
+ static PlayerConnection& Get ();
+ static PlayerConnection* ms_Instance;
+
+ void Poll ();
+ inline bool AllowDebugging () { return (0 != m_AllowDebugging); }
+ bool ShouldEnableProfiler() { return m_EnableProfiler != 0; }
+
+ // ugly hack to fix gfx tests
+ inline bool IsTestrigMode() { return (m_InitiateMode == kPlayerConnectionInitiateByConnecting); }
+ inline bool HasBytesToSend() { return GeneralConnection::HasBytesToSend(); }
+
+private:
+ virtual bool IsServer() { return true; }
+ bool ReadConfigFile (const std::string& dataPath);
+ void CreateListenSocket ();
+ void CreateUnixSocket();
+ void InitializeMulticastAddress (UInt16 multicastPort);
+ void PollListenMode ();
+ void PollConnectMode ();
+ void CreateAndReportConnection(TSocketHandle socketHandle);
+ std::string ConstructWhoamiString ();
+
+ bool ImmediateConnect () const
+ {
+ return ms_RunningUnitTests
+ || m_WaitingForPlayerConnectionBeforeStartingPlayback;
+ }
+
+#if ENABLE_LISTEN_SOCKET
+ static void InitializeListenSocket(ServerSocket& socket, const std::string& localIP, int listenPort);
+#endif
+#if UNITY_ANDROID
+ static void InitializeUnixSocket(ServerSocket& socket, const std::string& socketname);
+#endif
+
+
+private:
+ bool m_IsPlayerConnectionEnabled;
+ PlayerConnectionInitiateMode m_InitiateMode;
+ bool m_WaitingForPlayerConnectionBeforeStartingPlayback;
+
+ // player specific
+ unsigned short m_ListenPort;
+ std::string m_HostName;
+ std::string m_WhoAmI;
+#if ENABLE_LISTEN_SOCKET
+ ServerSocket m_ListenSocket; // player only
+#endif
+#if UNITY_ANDROID
+ ServerSocket m_UnixSocket; // local
+#endif
+
+ UInt32 m_EditorGuid;
+ int m_AllowDebugging;
+ int m_EnableProfiler;
+ int m_NumIPs;
+ std::string m_ConnectToIP;
+ char m_ConnectToIPList[10][16];
+
+ ABSOLUTE_TIME m_LastMulticast;
+};
+
+void InstallPlayerConnectionLogging (bool install);
+void TransferFileOverPlayerConnection(const std::string& fname, void* body, unsigned int length, void* header = 0, unsigned int headerLength = 0);
+void NotifyFileReadyOverPlayerConnection(const std::string& fname);
+
+#endif // ENABLE_PLAYERCONNECTION
diff --git a/Runtime/Network/ServerSocket.cpp b/Runtime/Network/ServerSocket.cpp
new file mode 100644
index 0000000..a89782f
--- /dev/null
+++ b/Runtime/Network/ServerSocket.cpp
@@ -0,0 +1,117 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_SOCKETS
+#include "ServerSocket.h"
+#include "SocketUtils.h"
+
+ServerSocket::ServerSocket(int domain, int type, int protocol)
+: Socket(domain, type, protocol)
+, m_IsListening(false)
+{
+ SetReuseAddress(true);
+}
+
+#if !UNITY_WINRT
+
+// Metro does not support BSD sockets, so these methods are reimplemented
+// in Metro platform specific codebase
+
+bool ServerSocket::StartListening(unsigned short port, bool block)
+{
+ struct sockaddr_in addr;
+ SetupAddress(htonl(INADDR_ANY), htons(port), &addr);
+ return StartListening((const sockaddr*) &addr, sizeof(addr), block);
+}
+
+bool ServerSocket::StartListening(const char* ip, unsigned short port, bool block)
+{
+ struct sockaddr_in addr;
+ SetupAddress(inet_addr(ip), htons(port), &addr);
+ return StartListening((const sockaddr*) &addr, sizeof(addr), block);
+}
+
+bool ServerSocket::StartListening(const sockaddr* addr, socklen_t addr_len, bool block)
+{
+ if (!m_IsListening)
+ {
+ if (!SetBlocking(block))
+ return false;
+
+ if (CheckError(bind(m_SocketHandle, addr, addr_len), "bind failed"))
+ return false;
+
+ if (CheckError(listen(m_SocketHandle, 5), "listen failed"))
+ return false;
+
+ m_IsListening = true;
+ return true;
+ }
+ ErrorStringMsg("already listening");
+ return false;
+}
+
+int ServerSocket::GetPort()
+{
+ sockaddr_in addr;
+ socklen_t addr_len = sizeof(addr);
+ if (CheckError(getsockname(m_SocketHandle, (struct sockaddr *) &addr, &addr_len)))
+ return -1;
+ return ntohs(addr.sin_port);
+}
+
+TSocketHandle ServerSocket::Accept()
+{
+ return Accept(NULL, NULL);
+}
+
+TSocketHandle ServerSocket::Accept(sockaddr* addr, socklen_t* addr_len)
+{
+ int socketHandle = accept(m_SocketHandle, addr, addr_len);
+ if (CheckError(socketHandle, "accept failed", kPlatformAcceptWouldBlock))
+ return socketHandle; // Shutdown?
+ return socketHandle;
+}
+
+
+#undef Error
+#undef SocketError
+
+// ---------------------------------------------------------------------------
+#if ENABLE_UNIT_TESTS && !UNITY_XENON
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "NetworkUtility.h"
+SUITE (ServerSocketTests)
+{
+ struct SocketFixture
+ {
+ SocketFixture()
+ {
+ NetworkInitialize();
+ };
+
+ ~SocketFixture()
+ {
+ NetworkCleanup();
+ }
+ };
+
+ TEST_FIXTURE(SocketFixture, ServerSocket_Connect)
+ {
+ int socketHandle, port;
+
+ ServerSocket socket;
+ CHECK((socket.StartListening("127.0.0.1", 0, false)) == true);
+ CHECK((port = socket.GetPort()) > 0);
+ CHECK((socketHandle = Socket::Connect("127.0.0.1", port)) >= 0);
+
+ Socket::Close(socketHandle);
+ CHECK(socket.IsListening());
+ }
+}
+
+#endif //ENABLE_UNIT_TESTS
+
+#endif // UNITY_WINRT
+
+#endif // ENABLE_SOCKETS
diff --git a/Runtime/Network/ServerSocket.h b/Runtime/Network/ServerSocket.h
new file mode 100644
index 0000000..89fd375
--- /dev/null
+++ b/Runtime/Network/ServerSocket.h
@@ -0,0 +1,31 @@
+#ifndef SERVERSOCKET_H
+#define SERVERSOCKET_H
+
+#if ENABLE_SOCKETS
+#include "Sockets.h"
+#include "SocketConsts.h"
+
+class ServerSocket : protected Socket
+{
+public:
+ ServerSocket(int domain = AF_INET, int type = SOCK_STREAM, int protocol = IPPROTO_TCP);
+
+ bool StartListening(unsigned short port, bool block);
+ bool StartListening(const char* ip, unsigned short port, bool block);
+#if !UNITY_WINRT
+ bool StartListening(const sockaddr* addr, socklen_t addr_len, bool block);
+#endif
+
+ int GetPort();
+ bool IsListening() const { return m_IsListening; }
+
+ TSocketHandle Accept();
+#if !UNITY_WINRT
+ TSocketHandle Accept(sockaddr* addr, socklen_t* addr_len);
+#endif
+private:
+ bool m_IsListening;
+};
+
+#endif // ENABLE_SOCKETS
+#endif // SERVERSOCKET_H
diff --git a/Runtime/Network/SocketConsts.h b/Runtime/Network/SocketConsts.h
new file mode 100644
index 0000000..d2b2691
--- /dev/null
+++ b/Runtime/Network/SocketConsts.h
@@ -0,0 +1,89 @@
+#ifndef SOCKETCONSTS_H
+#define SOCKETCONSTS_H
+
+#if UNITY_WINRT
+
+ #include <winsock2.h>
+
+#if UNITY_METRO
+ // WP8's winsock2.h is newer with full socket support.
+ // TEMP use local defines for Metro, until Microsoft updates Metro API.
+ #define AF_INET 2
+ #define SOCK_STREAM 1
+ #define IPPROTO_TCP 6
+ #define IPPROTO_UDP 17
+ #define SOCK_DGRAM 2
+ #define SOL_SOCKET 0xffff
+ #define SO_BROADCAST 0x0020
+ #define IPPROTO_IP 0
+ #define IP_MULTICAST_TTL 10
+ typedef USHORT ADDRESS_FAMILY;
+ typedef struct sockaddr {
+ ADDRESS_FAMILY sa_family;
+ CHAR sa_data[14];
+ } SOCKADDR, *PSOCKADDR, FAR *LPSOCKADDR;
+ typedef struct sockaddr_in {
+ ADDRESS_FAMILY sin_family;
+ USHORT sin_port;
+ char sin_addr[32];
+ CHAR sin_zero[8];
+ } SOCKADDR_IN, *PSOCKADDR_IN;
+
+#endif
+
+ struct SocketWrapper;
+ typedef SocketWrapper* TSocketHandle;
+ typedef int socklen_t;
+
+// typedef void SendUserData;
+// typedef void RecvUserData;
+
+#else
+
+ #if UNITY_WIN
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+ #elif UNITY_XENON
+ #include <xtl.h>
+ typedef int socklen_t;
+ #else
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <sys/select.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <fcntl.h>
+ #include <errno.h>
+ #include <netdb.h>
+ #endif
+ #if UNITY_PS3
+ #include <sys/time.h>
+ #include <netex/net.h>
+ #include <netex/errno.h>
+ #endif
+
+
+ typedef int TSocketHandle;
+#endif
+
+struct SendUserData
+{
+ int flags;
+ struct sockaddr* dstAddr;
+ socklen_t dstLen;
+ SendUserData() : flags(0) {}
+};
+struct RecvUserData
+{
+ int flags;
+ struct sockaddr* srcAddr;
+ socklen_t srcLen;
+ RecvUserData() : flags(0) {}
+};
+
+enum { kDefaultBufferSize = 16*1024 };
+enum { kDefaultPollTime = 1 };
+
+#define USE_WINSOCK_APIS ((UNITY_WIN && !UNITY_WINRT) || UNITY_XENON)
+
+#endif
diff --git a/Runtime/Network/SocketStreams.cpp b/Runtime/Network/SocketStreams.cpp
new file mode 100644
index 0000000..83abf16
--- /dev/null
+++ b/Runtime/Network/SocketStreams.cpp
@@ -0,0 +1,411 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_SOCKETS //|| UNITY_WINRT
+
+#include "SocketStreams.h"
+#include "ServerSocket.h"
+#include "Runtime/Profiler/TimeHelper.h"
+
+static const ProfileTimeFormat kTimeMillisecond = 1000000ULL;
+
+// ---------------------------------------------------------------
+// SocketStream
+// ---------------------------------------------------------------
+SocketStream::SocketStream(TSocketHandle socketHandle, bool block)
+: Socket(socketHandle)
+, m_IsConnected(true)
+, m_IsBlocking(block)
+{
+ if (!SetBlocking(block))
+ {
+ ErrorStringMsg("Unable to set blocking mode for socket stream, shutting down socket!");
+ Shutdown(); // Shutdown if unable to switch mode
+ }
+}
+
+int SocketStream::Send(const void* data, UInt32 data_len)
+{
+ if (data_len == 0)
+ return data_len;
+
+ int result = Socket::Send(data, data_len);
+ if (result < 0 && !Socket::WouldBlockError())
+ OnSocketError();
+
+ return result;
+}
+
+int SocketStream::Recv(void* data, UInt32 data_len)
+{
+ if (data_len == 0)
+ return data_len;
+
+ int result = Socket::Recv(data, data_len);
+ if (result == 0 || (result < 0 && !Socket::WouldBlockError()))
+ OnSocketError();
+
+ return result;
+}
+
+bool SocketStream::SendAll(const void* data, UInt32 data_len)
+{
+ while (data_len > 0)
+ {
+ int nBytes = Send(data, data_len);
+ if (nBytes <= 0 && WouldBlockError())
+ {
+ if(!Poll())
+ return false;
+ continue;
+ }
+ if (nBytes < 0)
+ return false;
+
+ data_len -= nBytes;
+ data = (char*)data + nBytes;
+ }
+ return true;
+}
+
+bool SocketStream::RecvAll(void* data, UInt32 data_len)
+{
+ while (data_len > 0)
+ {
+ int nBytes = Recv(data, data_len);
+ if (nBytes < 0 && WouldBlockError())
+ {
+ if(!Poll())
+ return false;
+ continue;
+ }
+ if (nBytes <= 0)
+ return false;
+
+ data_len -= nBytes;
+ data = (char*)data + nBytes;
+ }
+ return true;
+}
+
+bool SocketStream::Shutdown()
+{
+ if (!m_IsConnected)
+ return true;
+
+#if USE_WINSOCK_APIS
+ if (CheckError(shutdown(m_SocketHandle, SD_BOTH), "failed to shutdown stream", WSAENOTCONN))
+#elif UNITY_WINRT
+ if (CheckError(false, "failed to shutdown stream", ENOTCONN))
+#else
+ if (CheckError(shutdown(m_SocketHandle, SHUT_RDWR), "failed to shutdown stream", ENOTCONN))
+#endif
+ {
+ m_IsConnected = false; // always tag as disconnected to avoid loops
+ return false;
+ }
+ m_IsConnected = false;
+ return true;
+}
+
+void SocketStream::OnSocketError()
+{
+ Shutdown();
+}
+
+bool SocketStream::CanSendNonblocking( UInt32 data_len )
+{
+ return true;
+}
+
+
+// ---------------------------------------------------------------
+// BufferedSocketStream
+// ---------------------------------------------------------------
+BufferedSocketStream::BufferedSocketStream(TSocketHandle socketHandle, UInt32 sendbufferMaxSize, UInt32 recvbufferMaxSize)
+#if UNITY_FLASH
+ // This is like the worst hack ever, flash can't read data so it never blocks on recv
+: SocketStream(socketHandle, true)
+#else
+: SocketStream(socketHandle, false)
+#endif
+, m_IsArtificiallyConnected(false)
+, m_Sendbuffer(kMemNetwork, sendbufferMaxSize)
+, m_Recvbuffer(kMemNetwork, recvbufferMaxSize)
+{}
+
+BufferedSocketStream::BufferedSocketStream(TSocketHandle socketHandle, UInt32 sendbufferMaxSize, UInt32 recvbufferMaxSize, bool block)
+: SocketStream(socketHandle, block)
+, m_IsArtificiallyConnected(false)
+, m_Sendbuffer(kMemNetwork, sendbufferMaxSize)
+, m_Recvbuffer(kMemNetwork, recvbufferMaxSize)
+{}
+
+bool BufferedSocketStream::FillRecvbuffer()
+{
+ UInt32 recvBufferFree = m_Recvbuffer.GetFreeSize();
+ if (!recvBufferFree)
+ {
+ if (!SocketStream::IsBlocking())
+ return false;
+ m_Recvbuffer.BlockUntilFree();
+ }
+
+ void* recvBuffer = m_Recvbuffer.WritePtr(&recvBufferFree);
+ int nRecvBytes = SocketStream::Recv(recvBuffer, recvBufferFree);
+ if (nRecvBytes <= 0)
+ return false;
+
+ m_Recvbuffer.WritePtrUpdate(recvBuffer, nRecvBytes);
+ return true;
+}
+
+bool BufferedSocketStream::FlushSendbuffer()
+{
+ UInt32 sendBufferAvail = m_Sendbuffer.GetAvailableSize();
+ if (!sendBufferAvail)
+ {
+ if (!SocketStream::IsBlocking())
+ return false;
+ m_Sendbuffer.BlockUntilAvailable();
+ }
+
+ const void* sendBuffer = m_Sendbuffer.ReadPtr(&sendBufferAvail);
+ int nSentBytes = SocketStream::Send(sendBuffer, sendBufferAvail);
+ if (nSentBytes < 0)
+ return false;
+
+ m_Sendbuffer.ReadPtrUpdate(sendBuffer, nSentBytes);
+ return true;
+}
+
+bool BufferedSocketStream::Poll(UInt64 timeoutMS)
+{
+ if (!m_IsConnected)
+ return false;
+
+ Mutex::AutoLock lock(m_PollMutex);
+
+ ABSOLUTE_TIME start = START_TIME;
+
+ bool notBlocked = true;
+ while (notBlocked && GetProfileTime(ELAPSED_TIME(start)) < timeoutMS * kTimeMillisecond)
+ {
+ notBlocked = FlushSendbuffer();
+ notBlocked = FillRecvbuffer() || notBlocked;
+ notBlocked = m_IsConnected && notBlocked;
+ }
+
+ return m_IsConnected;
+}
+
+int BufferedSocketStream::Send(const void* data, UInt32 data_len)
+{
+ if (!m_IsConnected)
+ return -1;
+
+ void* sendBuffer = m_Sendbuffer.WritePtr(&data_len);
+ memcpy(sendBuffer, data, data_len);
+ m_Sendbuffer.WritePtrUpdate(sendBuffer, data_len);
+
+ return data_len;
+}
+
+int BufferedSocketStream::Recv(void* data, UInt32 data_len)
+{
+ if (!m_IsConnected && !m_IsArtificiallyConnected)
+ return 0;
+
+ const void* recvBuffer = m_Recvbuffer.ReadPtr(&data_len);
+ memcpy(data, recvBuffer, data_len);
+ m_Recvbuffer.ReadPtrUpdate(recvBuffer, data_len);
+
+ if (data_len == 0)
+ {
+ if (m_IsArtificiallyConnected)
+ Shutdown();
+ else
+ return -1;
+ }
+
+ return data_len;
+}
+
+void BufferedSocketStream::OnSocketError()
+{
+ m_IsArtificiallyConnected = 0 < m_Recvbuffer.GetAvailableSize();
+ SocketStream::Shutdown();
+}
+
+bool BufferedSocketStream::Shutdown()
+{
+ bool result = SocketStream::Shutdown();
+ m_IsArtificiallyConnected = false;
+ m_Sendbuffer.ReleaseBlockedThreads();
+ m_Recvbuffer.ReleaseBlockedThreads();
+ return result;
+}
+
+bool BufferedSocketStream::CanSendNonblocking( UInt32 data_len )
+{
+ UInt32 sendBufferFree = m_Sendbuffer.GetFreeSize();
+ return sendBufferFree >= data_len;
+}
+
+// ---------------------------------------------------------------
+// ThreadedSocketStream
+// ---------------------------------------------------------------
+#if SUPPORT_THREADS
+ThreadedSocketStream::ThreadedSocketStream(TSocketHandle socketHandle, UInt32 sendbufferSize, UInt32 recvbufferSize)
+: BufferedSocketStream(socketHandle, sendbufferSize, recvbufferSize, false)
+{
+ m_Reader.SetName("UnitySocketReader");
+ m_Writer.SetName("UnitySocketWriter");
+ m_Reader.Run(ReaderLoop, this);
+ m_Writer.Run(WriterLoop, this);
+}
+
+ThreadedSocketStream::~ThreadedSocketStream()
+{
+ Shutdown();
+ m_Reader.WaitForExit();
+ m_Writer.WaitForExit();
+}
+
+void* ThreadedSocketStream::ReaderLoop(void* _arg)
+{
+ ThreadedSocketStream* _this = reinterpret_cast<ThreadedSocketStream*>(_arg);
+
+
+ while(_this->m_IsConnected)
+ {
+ if(_this->WaitForAvailableRecvData(10))
+ _this->FillRecvbuffer();
+ }
+ return NULL;
+}
+
+void* ThreadedSocketStream::WriterLoop(void* _arg)
+{
+ ThreadedSocketStream* _this = reinterpret_cast<ThreadedSocketStream*>(_arg);
+ while(_this->m_IsConnected)
+ {
+ _this->SendBuffer().BlockUntilAvailable();
+ if(_this->WaitForAvailableSendBuffer(10))
+ _this->FlushSendbuffer();
+ }
+ return NULL;
+}
+#endif // SUPPORT_THREADS
+
+// ---------------------------------------------------------------------------
+#if ENABLE_UNIT_TESTS && !UNITY_XENON
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Network/NetworkUtility.h"
+SUITE (SocketStreamTests)
+{
+ struct SocketStreamFixture
+ {
+ SocketStreamFixture()
+ {
+ NetworkInitialize();
+ m_Socket = new ServerSocket();
+ CHECK((m_Socket->StartListening("127.0.0.1", 0, true)) == true);
+ CHECK((m_Port = m_Socket->GetPort()) > 0);
+ };
+
+ ~SocketStreamFixture()
+ {
+ delete m_Socket;
+ NetworkCleanup();
+ }
+
+ int Accept()
+ {
+ return m_Socket->Accept();
+ };
+
+ int Connect()
+ {
+ return Socket::Connect("127.0.0.1", m_Port);
+ };
+
+ int m_Port;
+ ServerSocket* m_Socket;
+ };
+
+ void TestNonBlockingSendAndRecv(SocketStream& server, SocketStream& client)
+ {
+ char buffer[4096];
+ int nBytesToSend = sizeof(buffer);
+ int nBytesToRecv = sizeof(buffer);
+ while (nBytesToRecv)
+ {
+ int nBytesSent = client.Send(buffer, nBytesToSend);
+ if (nBytesSent > 0)
+ nBytesToSend -= nBytesSent;
+ AssertBreak(nBytesSent >= 0 || client.WouldBlockError());
+
+ int nBytesRecv = server.Recv(buffer, nBytesToRecv);
+ if (nBytesRecv > 0)
+ nBytesToRecv -= nBytesRecv;
+ AssertBreak(nBytesRecv > 0 || (nBytesRecv < 0 && server.WouldBlockError()));
+ }
+ CHECK_EQUAL(nBytesToSend, nBytesToRecv);
+ }
+
+ TEST_FIXTURE(SocketStreamFixture, SocketStreamNB_SendRecv)
+ {
+ SocketStream client(Connect(), false);
+ SocketStream server(Accept(), false);
+ TestNonBlockingSendAndRecv(server, client);
+ }
+
+#if SUPPORT_THREADS
+ static void* PollBufferedStream(void* arg)
+ {
+ BufferedSocketStream* stream = reinterpret_cast<BufferedSocketStream*>(arg);
+ while (stream->Poll(1000)) { /* spin */ }
+ return NULL;
+ }
+
+ TEST_FIXTURE(SocketStreamFixture, BufferedSocketStreamNB_SendRecvNonBlocking)
+ {
+ BufferedSocketStream client(Connect(), 1024, 1024);
+ BufferedSocketStream server(Accept(), 1024, 1024);
+
+ Thread clientPoller, serverPoller;
+ clientPoller.Run(PollBufferedStream, &client);
+ serverPoller.Run(PollBufferedStream, &server);
+
+ TestNonBlockingSendAndRecv(server, client);
+
+ char buffer[4096];
+ CHECK(client.SendAll(buffer, sizeof(buffer)));
+ CHECK(server.RecvAll(buffer, sizeof(buffer)));
+
+ server.Shutdown();
+ client.Shutdown();
+ serverPoller.WaitForExit();
+ clientPoller.WaitForExit();
+ }
+
+ TEST_FIXTURE(SocketStreamFixture, ThreadedSocketStreamNB_SendRecvNonBlocking)
+ {
+ ThreadedSocketStream client(Connect(), 1024, 1024);
+ ThreadedSocketStream server(Accept(), 1024, 1024);
+ TestNonBlockingSendAndRecv(server, client);
+
+ char buffer[4096];
+ CHECK(client.SendAll(buffer, sizeof(buffer)));
+ CHECK(server.RecvAll(buffer, sizeof(buffer)));
+
+ server.Shutdown();
+ client.Shutdown();
+ }
+#endif // SUPPORT_THREADS
+}
+
+#endif //ENABLE_UNIT_TESTS
+
+#endif // ENABLE_SOCKETS
diff --git a/Runtime/Network/SocketStreams.h b/Runtime/Network/SocketStreams.h
new file mode 100644
index 0000000..6132b98
--- /dev/null
+++ b/Runtime/Network/SocketStreams.h
@@ -0,0 +1,94 @@
+#ifndef RUNTIME_NETWORK_SOCKETSTREAMS_H
+#define RUNTIME_NETWORK_SOCKETSTREAMS_H
+
+#if ENABLE_SOCKETS //|| UNITY_WINRT
+
+#include "Sockets.h"
+#include "Runtime/Containers/ExtendedRingbuffer.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Threads/Thread.h"
+
+class SocketStream : public Socket
+{
+public:
+ SocketStream(TSocketHandle socketHandle, bool block);
+ virtual ~SocketStream() {};
+
+ virtual int Send(const void* data, UInt32 data_len);
+ virtual int Recv(void* data, UInt32 data_len);
+ virtual bool IsBlocking() const { return m_IsBlocking; };
+ virtual bool IsConnected() const { return m_IsConnected; };
+ virtual bool WouldBlockError() { return Socket::WouldBlockError(); }
+ virtual bool Shutdown();
+
+ bool SendAll(const void* data, UInt32 data_len);
+ bool RecvAll(void* data, UInt32 data_len);
+
+ virtual bool CanSendNonblocking(UInt32 data_len);
+
+protected:
+ virtual bool Poll(UInt64 /*timeoutMS*/ = kDefaultPollTime) { return false; }
+ virtual void OnSocketError();
+
+protected:
+ bool m_IsBlocking;
+ volatile bool m_IsConnected;
+};
+
+class BufferedSocketStream : public SocketStream
+{
+public:
+ BufferedSocketStream(TSocketHandle socketHandle, UInt32 sendbufferMaxSize = kDefaultBufferSize, UInt32 recvbufferMaxSize = kDefaultBufferSize);
+ virtual ~BufferedSocketStream() {};
+
+ virtual int Send(const void* data, UInt32 data_len);
+ virtual int Recv(void* data, UInt32 data_len);
+ virtual bool IsBlocking() const { return false; };
+ virtual bool IsConnected() const { return (m_IsArtificiallyConnected && m_Recvbuffer.GetAvailableSize() != 0) || m_IsConnected; };
+ virtual bool WouldBlockError() { return IsConnected(); }
+ virtual bool Shutdown();
+
+ virtual bool Poll(UInt64 timeoutMS = kDefaultPollTime);
+
+ ExtendedGrowingRingbuffer& SendBuffer() { return m_Sendbuffer; };
+ ExtendedGrowingRingbuffer& RecvBuffer() { return m_Recvbuffer; };
+
+ const ExtendedGrowingRingbuffer& SendBuffer() const { return m_Sendbuffer; };
+ const ExtendedGrowingRingbuffer& RecvBuffer() const { return m_Recvbuffer; };
+
+ virtual bool CanSendNonblocking(UInt32 data_len);
+
+protected:
+ BufferedSocketStream(TSocketHandle socketHandle, UInt32 sendbufferMaxSize, UInt32 recvbufferMaxSize, bool block);
+
+ void OnSocketError();
+ bool FlushSendbuffer();
+ bool FillRecvbuffer();
+
+private:
+ volatile bool m_IsArtificiallyConnected;
+ ExtendedGrowingRingbuffer m_Sendbuffer;
+ ExtendedGrowingRingbuffer m_Recvbuffer;
+ Mutex m_PollMutex;
+};
+
+#if SUPPORT_THREADS
+class ThreadedSocketStream : public BufferedSocketStream
+{
+public:
+ ThreadedSocketStream(TSocketHandle socketHandle, UInt32 sendbufferMaxSize = kDefaultBufferSize, UInt32 recvbufferMaxSize = kDefaultBufferSize);
+ virtual ~ThreadedSocketStream();
+
+ virtual bool Poll(UInt64 /*timeoutMS*/ = kDefaultPollTime) { return IsConnected(); }
+
+private:
+ static void* WriterLoop(void* _arg);
+ static void* ReaderLoop(void* _arg);
+
+ Thread m_Reader;
+ Thread m_Writer;
+};
+#endif // SUPPORT_THREADS
+
+#endif // ENABLE_SOCKETS
+#endif // RUNTIME_NETWORK_SOCKETSTREAMS_H
diff --git a/Runtime/Network/SocketUtils.h b/Runtime/Network/SocketUtils.h
new file mode 100644
index 0000000..1edf54a
--- /dev/null
+++ b/Runtime/Network/SocketUtils.h
@@ -0,0 +1,56 @@
+#ifndef SOCKETUTILS_H
+#define SOCKETUTILS_H
+
+#include "Sockets.h"
+#if !UNITY_WINRT
+inline void SetupAddress(unsigned long addr, unsigned short port, sockaddr_in* sock_addr)
+{
+ memset(sock_addr, 0, sizeof(sockaddr_in));
+ sock_addr->sin_family = AF_INET;
+ sock_addr->sin_addr.s_addr = addr;
+ sock_addr->sin_port = port;
+}
+#endif
+enum
+{
+#if UNITY_WINRT
+ kPlatformConnectWouldBlock = E_PENDING,
+ kPlatformStreamWouldBlock = E_PENDING,
+ kPlatformAcceptWouldBlock = E_PENDING,
+#elif USE_WINSOCK_APIS
+ kPlatformConnectWouldBlock = WSAEWOULDBLOCK,
+ kPlatformStreamWouldBlock = WSAEWOULDBLOCK,
+ kPlatformAcceptWouldBlock = WSAEWOULDBLOCK
+#elif UNITY_PS3
+ kPlatformConnectWouldBlock = SYS_NET_EINPROGRESS,
+ kPlatformStreamWouldBlock = SYS_NET_EAGAIN,
+ kPlatformAcceptWouldBlock = SYS_NET_EWOULDBLOCK
+#else
+ kPlatformConnectWouldBlock = EINPROGRESS,
+ kPlatformStreamWouldBlock = EAGAIN,
+ kPlatformAcceptWouldBlock = EWOULDBLOCK
+#endif
+};
+
+#if UNITY_FLASH
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+extern "C" int flash_errno();
+extern "C" int flash_set_errno(int error);
+extern "C" int flash_socket(int domain, int type, int protocol);
+extern "C" int flash_connect(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
+extern "C" int flash_close(int sockfd);
+extern "C" ssize_t flash_recvfrom (int sockfd, void* buffer, size_t length, int flags, struct sockaddr* address, socklen_t* address_len);
+extern "C" ssize_t flash_sendto(int, const void *, size_t, int, const struct sockaddr*, socklen_t);
+extern "C" int flash_setsockopt(int sockfd, int level, int option_name, const void* option_value, socklen_t option_len);
+
+#define setsockopt flash_setsockopt
+#define recvfrom flash_recvfrom
+#define sendto flash_sendto
+#define socket flash_socket
+#define connect flash_connect
+#define close flash_close
+#endif
+
+
+#endif
diff --git a/Runtime/Network/Sockets.cpp b/Runtime/Network/Sockets.cpp
new file mode 100644
index 0000000..cdbc15f
--- /dev/null
+++ b/Runtime/Network/Sockets.cpp
@@ -0,0 +1,384 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_SOCKETS
+#include "Runtime/Network/Sockets.h"
+#include "Runtime/Profiler/TimeHelper.h"
+#include "PlatformDependent/CommonWebPlugin/Verification.h"
+
+#if UNITY_WIN
+#include "PlatformDependent/Win/GetFormattedErrorString.h"
+#endif
+
+#define SUPPORT_NON_BLOCKING !UNITY_FLASH
+
+#include "SocketUtils.h"
+
+#define SocketError(msg, identifier) { DebugStringToFile(Format("Socket: %s, error: %s(%d)", msg, GetSocketErrorMsg(Socket::GetError()).c_str(), Socket::GetError()), 0, __FILE_STRIPPED__, __LINE__, kError, 0, identifier); }
+
+#if !UNITY_WINRT
+
+int Socket::PollAsyncConnection(int socketHandle, time_t timeoutMS)
+{
+#if SUPPORT_NON_BLOCKING
+ // Set timeout on socket connection
+ struct timeval tv;
+ tv.tv_sec = timeoutMS / 1000;
+ tv.tv_usec = (timeoutMS - tv.tv_sec * 1000) * 1000;
+
+ if (timeoutMS == 0)
+ tv.tv_usec = 10;
+
+ fd_set fdset;
+ FD_ZERO(&fdset);
+ FD_SET(socketHandle, &fdset);
+
+ fd_set exceptfds;
+ FD_ZERO(&exceptfds);
+ FD_SET(socketHandle, &exceptfds);
+
+#if UNITY_PS3
+ int selectedDescriptors = socketselect(socketHandle + 1, NULL, &fdset, &exceptfds, &tv);
+#else
+ int selectedDescriptors = select(socketHandle + 1, NULL, &fdset, &exceptfds, &tv);
+#endif
+ // we need one selected descriptor to be able to determine socket state
+ if (selectedDescriptors == 1)
+ {
+ #if USE_WINSOCK_APIS
+ if (FD_ISSET(socketHandle, &fdset) && !FD_ISSET(socketHandle, &exceptfds))
+ {
+ Socket::SetError(0);
+ return 0;
+ }
+ Socket::SetError(WSAECONNREFUSED);
+ #else
+ int so_error = 0;
+ socklen_t len = sizeof (so_error);
+ if (getsockopt(socketHandle, SOL_SOCKET, SO_ERROR, &so_error, &len) > -1)
+ if (Socket::SetError(so_error) == 0)
+ return 0;
+ #endif
+ }
+ // we don't have any writeable or errornous descriptors yet
+ else if (selectedDescriptors == 0)
+ {
+ Socket::SetError(kPlatformConnectWouldBlock);
+ }
+
+ return -1;
+#endif // SUPPORT_NON_BLOCKING
+ return 0;
+}
+
+// ---------------------------------------------------------------
+// Socket::Statics
+// ---------------------------------------------------------------
+int Socket::Close(int socketHandle)
+{
+#if USE_WINSOCK_APIS
+ return closesocket(socketHandle);
+#elif UNITY_PS3
+ return socketclose(socketHandle);
+#else
+ return close(socketHandle);
+#endif
+}
+
+int Socket::Connect(const char* ip, unsigned short port, time_t timeoutMS, bool polling, bool logConnectError)
+{
+ struct sockaddr_in addr;
+ SetupAddress(inet_addr(ip), htons(port), &addr);
+ return Connect((const sockaddr*) &addr, sizeof(addr), timeoutMS, polling, logConnectError);
+}
+
+UInt32 ComputeIdentifier(const sockaddr* addr)
+{
+ if (addr == NULL)
+ return 0;
+
+ UInt32 identifier = CRCBegin();
+ identifier = CRCFeed( identifier, (const UInt8*)&( ((const sockaddr_in*)addr)->sin_addr.s_addr ), sizeof(unsigned long) );
+ identifier = CRCFeed( identifier, (const UInt8*)&( ((const sockaddr_in*)addr)->sin_port ), sizeof(unsigned short) );
+ identifier = CRCDone(identifier);
+
+ return identifier;
+}
+
+int Socket::Connect(const sockaddr* addr, socklen_t addr_len, time_t timeoutMS, bool polling, bool logConnectError)
+{
+ int socketHandle;
+ int identifier = (int)ComputeIdentifier(addr);
+ CheckError(socketHandle = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP), polling ? NULL : "failed to create socket", 0, identifier);
+ int so_error = 0;
+#if SUPPORT_NON_BLOCKING
+ if (!SetBlocking(socketHandle, false) && !polling)
+ ErrorStringMsg("unable to set blocking mode");
+ if (CheckError(connect(socketHandle, addr, addr_len), (logConnectError && !polling) ? "connect failed" : NULL, kPlatformConnectWouldBlock, identifier))
+ so_error = -1;
+
+ // Ensure that the connection is alive
+ if (so_error == 0 && timeoutMS != -1)
+ {
+ if (CheckError(PollAsyncConnection(socketHandle, timeoutMS), (logConnectError && !polling) ? "connect failed" : NULL, 0, identifier))
+ so_error = -1;
+ }
+#else
+ if (CheckError(connect(socketHandle, addr, addr_len), (logConnectError && !polling) ? "connect failed" : NULL, 0, identifier))
+ so_error = -1;
+#endif
+ // Handle connection error
+ if (so_error != 0)
+ {
+ if(logConnectError && !polling)
+ DebugStringToFile(Format("connect failed"), 0, __FILE_STRIPPED__, __LINE__, kError, 0, identifier);
+ Close(socketHandle);
+ return -1;
+ }
+
+ RemoveErrorWithIdentifierFromConsole(identifier);
+
+ return socketHandle;
+}
+
+bool Socket::SetBlocking(int socketHandle, bool blocking)
+{
+ bool success = true;
+
+#if !SUPPORT_NON_BLOCKING
+ success = blocking;
+#elif UNITY_PS3
+ int nonBlockingValue = blocking ? 0 : 1;
+ if (setsockopt(socketHandle, SOL_SOCKET, SO_NBIO, &nonBlockingValue, sizeof(nonBlockingValue)) != 0)
+ success = false;
+#elif USE_WINSOCK_APIS
+ u_long nonBlocking = blocking ? 0 : 1;
+ if (ioctlsocket(socketHandle, FIONBIO, &nonBlocking) != 0)
+ success = false;
+#else // unix
+ if ((fcntl(socketHandle, F_SETFL, blocking ? 0 : O_NONBLOCK) == -1))
+ success = false;
+#endif
+ return success;
+}
+
+// ---------------------------------------------------------------
+// Base Socket
+// ---------------------------------------------------------------
+Socket::Socket(int domain, int type, int protocol)
+: m_SocketError(0)
+, m_SendRecvFlags(0)
+{
+ if (!CheckError(m_SocketHandle = socket(domain, type, protocol), "unable to create socket"))
+ SetIgnoreSIGPIPE(true);
+}
+
+int Socket::SetSocketOption(int level, int option, void* value, size_t value_len)
+{
+ return setsockopt(m_SocketHandle, level, option, (char*) value, value_len);
+}
+
+int Socket::SetSocketOption(int level, int option, bool optionValue)
+{
+#if USE_WINSOCK_APIS
+ BOOL val = optionValue ? TRUE : FALSE;
+#else
+ int val = !!optionValue;
+#endif
+ return SetSocketOption(level, option, &val, sizeof(val));
+}
+
+bool Socket::SetReuseAddress(bool reuse)
+{
+ if (CheckError(SetSocketOption(SOL_SOCKET, SO_REUSEADDR, reuse), "set reusable addr failed"))
+ return false;
+#ifdef SO_REUSEPORT
+ if (CheckError(SetSocketOption(SOL_SOCKET, SO_REUSEPORT, reuse), "set reusable port failed"))
+ return false;
+#endif
+ return true;
+}
+
+int Socket::Send(const void* data, size_t data_len, SendUserData* userData)
+{
+ int result;
+ if (userData != NULL)
+ {
+ result = sendto(m_SocketHandle, (char*) data, data_len, userData->flags | m_SendRecvFlags, userData->dstAddr, userData->dstLen);
+ }
+ else
+ {
+ result = sendto(m_SocketHandle, (char*) data, data_len, m_SendRecvFlags, NULL, 0);
+ }
+ CheckError(result);
+ return result;
+}
+
+int Socket::Recv(void* data, size_t data_len, RecvUserData* userData)
+{
+ int result;
+ if (userData != NULL)
+ {
+ result = recvfrom(m_SocketHandle, (char*) data, data_len, userData->flags | m_SendRecvFlags, userData->srcAddr, &userData->srcLen);
+ }
+ else
+ {
+ result = recvfrom(m_SocketHandle, (char*) data, data_len, m_SendRecvFlags, NULL, NULL);
+ }
+ CheckError(result);
+ return result;
+}
+
+bool Socket::WaitForAvailableSendBuffer( time_t timeoutMS )
+{
+ // Set timeout on socket connection
+ struct timeval tv;
+ tv.tv_sec = timeoutMS / 1000;
+ tv.tv_usec = (timeoutMS - tv.tv_sec * 1000) * 1000;
+
+ fd_set fdset;
+ FD_ZERO(&fdset);
+ FD_SET(m_SocketHandle, &fdset);
+
+#if UNITY_PS3
+ return socketselect(m_SocketHandle + 1, NULL, &fdset, NULL, &tv) == 1;
+#else
+ return select(m_SocketHandle + 1, NULL, &fdset, NULL, &tv) == 1;
+#endif
+}
+
+bool Socket::WaitForAvailableRecvData( time_t timeoutMS )
+{
+ // Set timeout on socket connection
+ struct timeval tv;
+ tv.tv_sec = timeoutMS / 1000;
+ tv.tv_usec = (timeoutMS - tv.tv_sec * 1000) * 1000;
+
+ fd_set fdset;
+ FD_ZERO(&fdset);
+ FD_SET(m_SocketHandle, &fdset);
+
+#if UNITY_PS3
+ return socketselect(m_SocketHandle + 1, &fdset, NULL, NULL, &tv) == 1;
+#else
+ return select(m_SocketHandle + 1, &fdset, NULL, NULL, &tv) == 1;
+#endif
+}
+
+#endif // !UNITY_WINRT
+
+std::string GetSocketErrorMsg(int error)
+{
+#if UNITY_WIN
+ return WideToUtf8( GetHResultErrorMessage(error) );
+#elif UNITY_FLASH || UNITY_XENON
+ return "Unknown network error";
+#else
+ return strerror(error);
+#endif
+}
+
+Socket::Socket(TSocketHandle socketHandle)
+: m_SocketError(0)
+, m_SendRecvFlags(0)
+, m_SocketHandle(socketHandle)
+{
+#if UNITY_WINRT
+ AssertBreak(nullptr != m_SocketHandle);
+#else
+ AssertBreak(m_SocketHandle >= 0);
+#endif
+ SetIgnoreSIGPIPE(true);
+}
+
+Socket::~Socket()
+{
+ Close(m_SocketHandle);
+}
+
+bool Socket::WouldBlockError()
+{
+ int error = Socket::GetError();
+ return error == kPlatformConnectWouldBlock
+ || error == kPlatformStreamWouldBlock
+ || error == kPlatformAcceptWouldBlock;
+}
+
+bool Socket::SetIgnoreSIGPIPE(bool ignore)
+{
+#if USE_WINSOCK_APIS || UNITY_FLASH || UNITY_PS3 || UNITY_WINRT
+ return true;
+#elif UNITY_OSX || UNITY_IPHONE
+ return !CheckError(SetSocketOption(SOL_SOCKET, SO_NOSIGPIPE, ignore), "failed to install NOSIGPIPE");
+#else // linux
+ m_SendRecvFlags = MSG_NOSIGNAL;
+ return true;
+#endif
+}
+
+bool Socket::SetBlocking(bool blocking)
+{
+ if (!SetBlocking(m_SocketHandle, blocking))
+ {
+ ErrorStringMsg("failed to set blocking mode");
+ return false;
+ }
+ return true;
+}
+
+#if UNITY_WINRT
+// Implemented in MetroSockets.cpp
+int SocketWrapperErrorState(TSocketHandle socketHandle);
+int SocketWrapperErrorState(TSocketHandle socketHandle, int error);
+#endif // UNITY_WINRT
+
+int Socket::GetError()
+{
+#if UNITY_WINRT
+ return SocketWrapperErrorState(m_SocketHandle);
+#elif USE_WINSOCK_APIS
+ return WSAGetLastError();
+#elif UNITY_FLASH
+ return flash_errno();
+#elif UNITY_PS3
+ return sys_net_errno;
+#else
+ return errno;
+#endif
+}
+
+int Socket::SetError(int error)
+{
+#if UNITY_WINRT
+ SocketWrapperErrorState(m_SocketHandle, error);
+#elif USE_WINSOCK_APIS
+ WSASetLastError(error);
+#elif UNITY_FLASH
+ flash_set_errno(error);
+#elif UNITY_PS3
+ sys_net_errno = error;
+#else
+ errno = error;
+#endif
+ return error;
+}
+
+bool Socket::CheckError(int result, const char* msg, int validState, int identifier)
+{
+ if (result < 0)
+ {
+ int error = Socket::GetError();
+ if (error != validState)
+ {
+ if (msg)
+ SocketError(msg, identifier);
+ return true;
+ }
+ }
+ else
+ {
+ Socket::SetError(0);
+ }
+ return false;
+}
+
+#endif // ENABLE_SOCKETS
diff --git a/Runtime/Network/Sockets.h b/Runtime/Network/Sockets.h
new file mode 100644
index 0000000..6a4f5d7
--- /dev/null
+++ b/Runtime/Network/Sockets.h
@@ -0,0 +1,92 @@
+#ifndef RUNTIME_NETWORK_SOCKETS_H
+#define RUNTIME_NETWORK_SOCKETS_H
+
+#include "SocketConsts.h"
+
+#if ENABLE_SOCKETS
+
+#include "Runtime/Utilities/NonCopyable.h"
+
+//
+// Socket implementations, all sockets are by default non-blocking.
+// Metro: the implementation of this is located in PlatformDependent\MetroPlayer\MetroSocket.cpp
+//
+class Socket : public NonCopyable
+{
+#if UNITY_WINRT
+# define UNITY_SOCKET_STATIC_ERROR_STATE
+#else
+# define UNITY_SOCKET_STATIC_ERROR_STATE static
+#endif // UNITY_WINRT
+
+public:
+ static TSocketHandle Connect(const char* ip, unsigned short port, time_t timeoutMS = 4000, bool polling = false, bool logConnectError = true);
+#if !UNITY_WINRT
+ static int Connect(const sockaddr* addr, socklen_t addr_len, time_t timeoutMS = 4000, bool polling = false, bool logConnectError = true);
+#endif
+ static int Close(TSocketHandle socketHandle);
+
+ static int PollAsyncConnection(TSocketHandle socketHandle, time_t timeoutMS = 0);
+ UNITY_SOCKET_STATIC_ERROR_STATE bool CheckError(int result, const char* msg = NULL, int valid_state = 0, int identifier = 0);
+ UNITY_SOCKET_STATIC_ERROR_STATE bool WouldBlockError();
+
+ bool WaitForAvailableSendBuffer(time_t timeoutMS);
+ bool WaitForAvailableRecvData(time_t timeoutMS);
+
+protected:
+ Socket(TSocketHandle socketHandle);
+ Socket(int domain, int type, int protocol);
+ virtual ~Socket();
+
+ int SetSocketOption(int level, int option, void* value, size_t value_len);
+ int SetSocketOption(int level, int option, bool value);
+
+ bool SetReuseAddress(bool reuse);
+
+ int Send(const void* data, size_t data_len, SendUserData* userData = NULL);
+ int Recv(void* data, size_t data_len, RecvUserData* userData = NULL);
+
+ bool SetIgnoreSIGPIPE(bool ignore);
+ bool SetBlocking(bool block);
+
+protected:
+ TSocketHandle m_SocketHandle;
+ int m_SendRecvFlags;
+ volatile int m_SocketError;
+
+private:
+ static bool SetBlocking(TSocketHandle socketHandle, bool block);
+
+ UNITY_SOCKET_STATIC_ERROR_STATE int GetError();
+ UNITY_SOCKET_STATIC_ERROR_STATE int SetError(int error);
+
+#undef UNITY_SOCKET_STATIC_ERROR_STATE
+};
+
+
+#if UNITY_WINRT
+namespace UnityPlayer
+{
+ [Windows::Foundation::Metadata::WebHostHidden]
+ public ref class StreamListenerContext sealed
+ {
+ public:
+ StreamListenerContext( Windows::Networking::HostName^ hostname, Platform::String^ serviceName );
+ void OnConnection( Windows::Networking::Sockets::StreamSocketListener^ streamSocket, Windows::Networking::Sockets::StreamSocketListenerConnectionReceivedEventArgs^ args);
+ Windows::Networking::Sockets::StreamSocketListener^ GetStreamSocket() { return m_listener; }
+ Windows::Networking::Sockets::StreamSocket^ GetConnectionSocket() { return m_connectionSocket; }
+ void Bind();
+
+ private:
+ Windows::Networking::Sockets::StreamSocketListener^ m_listener;
+ Windows::Networking::Sockets::StreamSocket^ m_connectionSocket;
+ Windows::Networking::HostName^ m_hostname;
+ Platform::String^ m_port;
+ };
+}
+#endif // UNITY_WINRT
+
+
+#endif // ENABLE_SOCKETS
+
+#endif // RUNTIME_NETWORK_SOCKETS_H
diff --git a/Runtime/Physics2D/BoxCollider2D.cpp b/Runtime/Physics2D/BoxCollider2D.cpp
new file mode 100644
index 0000000..7b32134
--- /dev/null
+++ b/Runtime/Physics2D/BoxCollider2D.cpp
@@ -0,0 +1,167 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+#include "Runtime/Physics2D/BoxCollider2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+
+PROFILER_INFORMATION(gPhysics2DProfileBoxColliderCreate, "Physics2D.BoxColliderCreate", kProfilerPhysics)
+
+IMPLEMENT_CLASS (BoxCollider2D)
+IMPLEMENT_OBJECT_SERIALIZE (BoxCollider2D)
+
+
+// --------------------------------------------------------------------------
+
+
+BoxCollider2D::BoxCollider2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+
+BoxCollider2D::~BoxCollider2D ()
+{
+}
+
+
+template<class TransferFunction>
+void BoxCollider2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER (m_Size);
+ TRANSFER (m_Center);
+}
+
+
+void BoxCollider2D::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ if (IsFinite (m_Size))
+ {
+ m_Size.x = std::max (PHYSICS_2D_SMALL_RANGE_CLAMP, m_Size.x);
+ m_Size.y = std::max (PHYSICS_2D_SMALL_RANGE_CLAMP, m_Size.y);
+ }
+ else
+ {
+ m_Size.Set (1.0f, 1.0f);
+ }
+
+ if (!IsFinite (m_Center))
+ m_Center = Vector2f::zero;
+}
+
+
+void BoxCollider2D::Reset ()
+{
+ Super::Reset ();
+
+ m_Size.Set (1.0f, 1.0f);
+ m_Center = Vector2f::zero;
+}
+
+
+void BoxCollider2D::SmartReset ()
+{
+ Super::SmartReset ();
+
+ AABB aabb;
+ if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb))
+ {
+ m_Size.x = aabb.GetExtent().x * 2.0f;
+ m_Size.y = aabb.GetExtent().y * 2.0f;
+ m_Center.x = aabb.GetCenter().x;
+ m_Center.y = aabb.GetCenter().y;
+ return;
+ }
+
+ m_Size.Set (1.0f, 1.0f);
+ m_Center = Vector2f::zero;
+}
+
+
+void BoxCollider2D::SetSize (const Vector2f& size)
+{
+ ABORT_INVALID_VECTOR2 (size, size, BoxCollider2D);
+
+ // Finish if no change.
+ if (m_Size == size)
+ return;
+
+ m_Size.x = std::max (PHYSICS_2D_SMALL_RANGE_CLAMP, size.x);
+ m_Size.y = std::max (PHYSICS_2D_SMALL_RANGE_CLAMP, size.y);
+
+ SetDirty ();
+
+ Create();
+}
+
+
+void BoxCollider2D::SetCenter (const Vector2f& center)
+{
+ ABORT_INVALID_VECTOR2 (center, center, BoxCollider2D);
+
+ // Finish if no change.
+ if (m_Center == center)
+ return;
+
+ m_Center = center;
+ SetDirty ();
+
+ Create();
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void BoxCollider2D::Create (const Rigidbody2D* ignoreRigidbody)
+{
+ PROFILER_AUTO(gPhysics2DProfileBoxColliderCreate, NULL);
+
+ // Ensure we're cleaned-up.
+ Cleanup ();
+
+ // Ignore if not active.
+ if (!IsActive())
+ return;
+
+ // Calculate collider transformation.
+ Matrix4x4f relativeTransform;
+ b2Body* body;
+ CalculateColliderTransformation (ignoreRigidbody, &body, relativeTransform);
+
+ // Calculate collider center.
+ const Vector3f scale = GetComponent(Transform).GetWorldScaleLossy();
+ Vector3f center = relativeTransform.MultiplyPoint3 (Vector3f(m_Center.x * scale.x, m_Center.y * scale.y, 0.0f));
+
+ // Calculate rotation.
+ Quaternionf quat;
+ MatrixToQuaternion(relativeTransform, quat);
+ const Vector3f euler = QuaternionToEuler(quat);
+
+ // Calculate scaled size.
+ Vector3f size = GetComponent (Transform).GetWorldScaleLossy ();
+ size.x = max(Abs(size.x * m_Size.x), PHYSICS_2D_SMALL_RANGE_CLAMP);
+ size.y = max(Abs(size.y * m_Size.y), PHYSICS_2D_SMALL_RANGE_CLAMP);
+
+ // Create the shape.
+ b2PolygonShape shape;
+ shape.SetAsBox (size.x*0.5f, size.y*0.5f, b2Vec2(center.x, center.y), euler.z);
+ b2FixtureDef def;
+ def.shape = &shape;
+
+ // Finalize the creation.
+ FinalizeCreate (def, body);
+}
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/BoxCollider2D.h b/Runtime/Physics2D/BoxCollider2D.h
new file mode 100644
index 0000000..2c40727
--- /dev/null
+++ b/Runtime/Physics2D/BoxCollider2D.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Runtime/Math/Vector2.h"
+
+
+// --------------------------------------------------------------------------
+
+
+class BoxCollider2D : public Collider2D
+{
+public:
+ REGISTER_DERIVED_CLASS (BoxCollider2D, Collider2D)
+ DECLARE_OBJECT_SERIALIZE (BoxCollider2D)
+
+ BoxCollider2D (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void CheckConsistency ();
+ virtual void Reset ();
+ virtual void SmartReset ();
+
+ void SetSize (const Vector2f& size);
+ const Vector2f& GetSize () const { return m_Size; }
+
+ void SetCenter (const Vector2f& center);
+ const Vector2f& GetCenter() const { return m_Center; }
+
+protected:
+ virtual void Create (const Rigidbody2D* ignoreRigidbody = NULL);
+
+private:
+ Vector2f m_Size; ///< The size of the box.
+ Vector2f m_Center; ///< The offset of the box.
+};
+
+#endif
diff --git a/Runtime/Physics2D/CircleCollider2D.cpp b/Runtime/Physics2D/CircleCollider2D.cpp
new file mode 100644
index 0000000..a34a94b
--- /dev/null
+++ b/Runtime/Physics2D/CircleCollider2D.cpp
@@ -0,0 +1,157 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+#include "Runtime/Physics2D/CircleCollider2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+
+PROFILER_INFORMATION(gPhysics2DProfileCircleColliderCreate, "Physics2D.CircleColliderCreate", kProfilerPhysics)
+
+
+IMPLEMENT_CLASS (CircleCollider2D)
+IMPLEMENT_OBJECT_SERIALIZE (CircleCollider2D)
+
+
+// --------------------------------------------------------------------------
+
+
+CircleCollider2D::CircleCollider2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+
+CircleCollider2D::~CircleCollider2D ()
+{
+}
+
+
+template<class TransferFunction>
+void CircleCollider2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER (m_Radius);
+ TRANSFER (m_Center);
+}
+
+
+void CircleCollider2D::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_Radius = clamp<float> (m_Radius, PHYSICS_2D_SMALL_RANGE_CLAMP, PHYSICS_2D_LARGE_RANGE_CLAMP);
+
+ if (!IsFinite (m_Center))
+ m_Center = Vector2f::zero;
+}
+
+
+void CircleCollider2D::Reset ()
+{
+ Super::Reset ();
+
+ m_Radius = 0.5f;
+ m_Center = Vector2f::zero;
+}
+
+
+void CircleCollider2D::SmartReset ()
+{
+ Super::SmartReset ();
+
+ AABB aabb;
+ if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb))
+ {
+ Vector3f dist = aabb.GetExtent ();
+ m_Radius = clamp<float> (std::max(dist.x, dist.y), PHYSICS_2D_SMALL_RANGE_CLAMP, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ m_Center.x = aabb.GetCenter().x;
+ m_Center.y = aabb.GetCenter().y;
+ return;
+ }
+
+ m_Radius = 0.5f;
+ m_Center = Vector2f::zero;
+}
+
+
+void CircleCollider2D::SetRadius (float radius)
+{
+ ABORT_INVALID_FLOAT (radius, radius, CircleCollider2D);
+
+ // Finish if no change.
+ if (m_Radius == radius)
+ return;
+
+ m_Radius = clamp<float> (radius, PHYSICS_2D_SMALL_RANGE_CLAMP, PHYSICS_2D_LARGE_RANGE_CLAMP);
+
+ if (GetShape() == NULL)
+ return;
+
+ SetDirty ();
+ Create();
+}
+
+
+void CircleCollider2D::SetCenter (const Vector2f& center)
+{
+ ABORT_INVALID_VECTOR2 (center, center, CircleCollider2D);
+
+ // Finish if no change.
+ if (m_Center == center)
+ return;
+
+ m_Center = center;
+
+ SetDirty ();
+ Create();
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void CircleCollider2D::Create (const Rigidbody2D* ignoreRigidbody)
+{
+ PROFILER_AUTO(gPhysics2DProfileCircleColliderCreate, NULL);
+
+ // Ensure we're cleaned-up.
+ Cleanup ();
+
+ // Ignore if not active.
+ if (!IsActive())
+ return;
+
+ // Calculate collider transformation.
+ Matrix4x4f relativeTransform;
+ b2Body* body;
+ CalculateColliderTransformation (ignoreRigidbody, &body, relativeTransform);
+
+ // Fetch scale.
+ const Vector3f scale = GetComponent(Transform).GetWorldScaleLossy();
+
+ // Calculate collider center.
+ Vector3f center = relativeTransform.MultiplyPoint3(Vector3f(m_Center.x * scale.x, m_Center.y * scale.y, 0.0f));
+
+ // Calculate scaled radius.
+ const float scaledRadius = clamp<float> (max( PHYSICS_2D_SMALL_RANGE_CLAMP, max (Abs (scale.x), Abs (scale.y)) * m_Radius), PHYSICS_2D_SMALL_RANGE_CLAMP, PHYSICS_2D_LARGE_RANGE_CLAMP);
+
+ // Create the shape.
+ b2CircleShape shape;
+ shape.m_p.Set(center.x, center.y);
+ shape.m_radius = scaledRadius;
+ b2FixtureDef def;
+ def.shape = &shape;
+
+ // Finalize the creation.
+ FinalizeCreate (def, body);
+}
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/CircleCollider2D.h b/Runtime/Physics2D/CircleCollider2D.h
new file mode 100644
index 0000000..f30b733
--- /dev/null
+++ b/Runtime/Physics2D/CircleCollider2D.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Runtime/Math/Vector2.h"
+
+
+// --------------------------------------------------------------------------
+
+
+class CircleCollider2D : public Collider2D
+{
+public:
+ REGISTER_DERIVED_CLASS (CircleCollider2D, Collider2D)
+ DECLARE_OBJECT_SERIALIZE (CircleCollider2D)
+
+ CircleCollider2D (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~CircleCollider2D (); declared-by-macro
+
+ virtual void CheckConsistency ();
+ virtual void Reset ();
+ virtual void SmartReset ();
+
+ void SetRadius (float radius);
+ float GetRadius () const { return m_Radius; }
+
+ void SetCenter (const Vector2f& center);
+ const Vector2f& GetCenter() const { return m_Center; }
+
+protected:
+ virtual void Create (const Rigidbody2D* ignoreRigidbody = NULL);
+
+private:
+ float m_Radius; ///< The radius of the circle. range { 0.0001, 1000000 }
+ Vector2f m_Center; ///< The offset of the circle.
+};
+
+#endif
diff --git a/Runtime/Physics2D/Collider2D.cpp b/Runtime/Physics2D/Collider2D.cpp
new file mode 100644
index 0000000..9e34e60
--- /dev/null
+++ b/Runtime/Physics2D/Collider2D.cpp
@@ -0,0 +1,441 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Physics2D/Physics2DSettings.h"
+#include "Runtime/Physics2D/Physics2DMaterial.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Utilities/Utility.h"
+
+
+PROFILER_INFORMATION(gPhysics2DProfileColliderCleanup, "Physics2D.ColliderCleanup", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileColliderShapeGeneration, "Physics2D.ColliderShapeGeneration", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileColliderTransformChanged, "Physics2D.ColliderTransformChanged", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileColliderTransformParentChanged, "Physics2D.ColliderTransformParentChanged", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileColliderTransformScaleChanged, "Physics2D.ColliderTransformScaleChanged", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileColliderTransformPositionRotationChanged, "Physics2D.ColliderTransformPositionRotationChanged", kProfilerPhysics)
+
+
+IMPLEMENT_CLASS_HAS_INIT (Collider2D)
+IMPLEMENT_OBJECT_SERIALIZE (Collider2D)
+INSTANTIATE_TEMPLATE_TRANSFER (Collider2D)
+
+// --------------------------------------------------------------------------
+
+
+Collider2D::Collider2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_IsTrigger(false)
+, m_IsStaticBody(false)
+, m_LocalTransformInitialized(false)
+{
+}
+
+
+Collider2D::~Collider2D ()
+{
+ Cleanup ();
+}
+
+
+void Collider2D::InitializeClass ()
+{
+ REGISTER_MESSAGE (Collider2D, kTransformChanged, TransformChanged, int);
+}
+
+
+template<class TransferFunction>
+void Collider2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER(m_Material);
+ transfer.Transfer (m_IsTrigger, "m_IsTrigger");
+ transfer.Align();
+}
+
+
+void Collider2D::Reset ()
+{
+ Super::Reset ();
+
+ m_Material = 0;
+ m_IsTrigger = false;
+}
+
+
+void Collider2D::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ // Ignore if not active.
+ if (!IsActive())
+ return;
+
+ // (Re)create collider if appropriate.
+ Create ();
+}
+
+
+void Collider2D::Deactivate (DeactivateOperation operation)
+{
+ Super::Deactivate (operation);
+ Cleanup ();
+
+ // Destroy collider collisions immediately.
+ GetPhysics2DManager().DestroyColliderCollisions (this);
+}
+
+
+void Collider2D::Cleanup ()
+{
+ PROFILER_AUTO(gPhysics2DProfileColliderCleanup, NULL)
+
+ // Process any shapes.
+ if (m_Shapes.size() > 0)
+ {
+ if (m_IsStaticBody)
+ {
+ // Destroy the fixtures
+ b2Body* groundBody = GetPhysicsGroundBody ();
+ for (int i = 0; i < m_Shapes.size(); ++i)
+ groundBody->DestroyFixture(m_Shapes[i]);
+ }
+ else
+ {
+ Rigidbody2D* rigidBody = GetRigidbody();
+ if (rigidBody)
+ {
+ // Destroy the fixtures.
+ b2Body* body = rigidBody->GetBody ();
+ for (int i = 0; i < m_Shapes.size(); ++i)
+ body->DestroyFixture(m_Shapes[i]);
+
+ // Recalculate the collider body-mass.
+ rigidBody->CalculateColliderBodyMass ();
+ }
+ }
+
+ m_Shapes.clear();
+
+ // Invalidate any persisted contact information.
+ GetPhysics2DManager().InvalidateColliderCollisions (this);
+ }
+
+ m_IsStaticBody = false;
+}
+
+
+void Collider2D::RecreateCollider (const Rigidbody2D* ignoreRigidbody)
+{
+ if (IsActive () && GetEnabled())
+ Create (ignoreRigidbody);
+ else
+ Cleanup ();
+}
+
+
+void Collider2D::AddToManager ()
+{
+ // Create the collider if not already created.
+ if (m_Shapes.size() == 0)
+ Create ();
+}
+
+
+void Collider2D::RemoveFromManager ()
+{
+ // Destroy the collider.
+ Cleanup ();
+}
+
+
+void Collider2D::SetIsTrigger (bool trigger)
+{
+ // Finish if no change.
+ if (m_IsTrigger == trigger)
+ return;
+
+ m_IsTrigger = trigger;
+
+ SetDirty();
+
+ if (IsActive () && GetEnabled())
+ Create ();
+}
+
+
+PPtr<PhysicsMaterial2D> Collider2D::GetMaterial ()
+{
+ return m_Material;
+}
+
+
+void Collider2D::SetMaterial (PPtr<PhysicsMaterial2D> material)
+{
+ if (m_Material == material)
+ return;
+
+ SetDirty ();
+ m_Material = material;
+}
+
+
+Rigidbody2D* Collider2D::GetRigidbody ()
+{
+ // Finish if a static body or no shapes.
+ if (m_IsStaticBody || m_Shapes.size() == 0)
+ return NULL;
+
+ // Fetch body from first shape.
+ b2Body* body = m_Shapes[0]->GetBody();
+
+ if (!body)
+ return NULL;
+
+ return (Rigidbody2D*)body->GetUserData();
+}
+
+
+bool Collider2D::OverlapPoint (const Vector2f& point) const
+{
+ // Cannot perform query if not active.
+ if (!IsActive ())
+ return false;
+
+ const b2Vec2 testPoint(point.x, point.y);
+
+ // Test all fixtures for the point.
+ for (FixtureArray::const_iterator fixtureItr = m_Shapes.begin (); fixtureItr != m_Shapes.end (); ++fixtureItr)
+ {
+ if ((*fixtureItr)->TestPoint (testPoint))
+ return true;
+ }
+
+ return false;
+}
+
+
+void Collider2D::TransformChanged (int changeMask)
+{
+ PROFILER_AUTO(gPhysics2DProfileColliderTransformChanged, NULL)
+
+ // Finish if transform message is disabled.
+ if (!GetPhysics2DManager().IsTransformMessageEnabled())
+ return;
+
+ // Recreate the collider if the parent body has changed.
+ if (changeMask & Transform::kParentingChanged)
+ {
+ PROFILER_AUTO(gPhysics2DProfileColliderTransformParentChanged, NULL)
+
+ Rigidbody2D* currentBody = GetRigidbody();
+ Rigidbody2D* newBody = Rigidbody2D::FindRigidbody (GetGameObjectPtr());
+ if (newBody != currentBody)
+ {
+ Create();
+ return;
+ }
+ }
+
+ // Recreate the collider if the scale has changed.
+ if (changeMask & Transform::kScaleChanged)
+ {
+ PROFILER_AUTO(gPhysics2DProfileColliderTransformScaleChanged, NULL)
+
+ Create();
+ return;
+ }
+
+ // Recreate the collider if the position or rotation has changed.
+ if (changeMask & (Transform::kPositionChanged | Transform::kRotationChanged))
+ {
+ PROFILER_AUTO(gPhysics2DProfileColliderTransformPositionRotationChanged, NULL)
+
+ // Always recreate static bodies but only recreate non-static bodies if the rigid-body exists
+ // on a parent GameObject i.e. this is a compound object.
+ if (!m_IsStaticBody && (QueryComponent (Rigidbody2D) != NULL || !HasLocalTransformChanged ()))
+ return;
+
+ Create();
+ }
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void Collider2D::FinalizeCreate(b2FixtureDef& def, b2Body* body, const dynamic_array<b2Shape*>* shapes)
+{
+ Assert (m_Shapes.size() == 0);
+
+ PROFILER_AUTO(gPhysics2DProfileColliderShapeGeneration, NULL)
+
+ // Don't create the collider if not active and enabled.
+ if (!(IsActive () && GetEnabled ()))
+ return;
+
+ // Initialize fixture definition.
+ if (m_Material.IsNull ())
+ {
+ PhysicsMaterial2D* defaultMaterial = GetPhysics2DSettings ().GetDefaultPhysicsMaterial ();
+ if (defaultMaterial != NULL )
+ {
+ def.friction = defaultMaterial->GetFriction ();
+ def.restitution = defaultMaterial->GetBounciness ();
+ }
+ else
+ {
+ def.friction = 0.4f;
+ def.restitution = 0.0f;
+ }
+ }
+ else
+ {
+ def.friction = m_Material->GetFriction();
+ def.restitution = m_Material->GetBounciness();
+ }
+ def.density = 1.0f; // Body mass is calculated based upon a mass calculation of each shape unit-density.
+ def.userData = this;
+ def.isSensor = m_IsTrigger;
+
+ // Fetch the associated rigid-body.
+ // NOTE: There won't be one if the body is the ground-body!
+ Rigidbody2D* rigidBody = (Rigidbody2D*)body->GetUserData ();
+
+ // Do we have any shapes?
+ if (shapes)
+ {
+ // Yes, so add shape to existing set.
+ const int shapeCount = shapes->size();
+
+ Assert(shapeCount > 0);
+ Assert(def.shape == 0);
+
+ m_Shapes.resize_uninitialized(shapeCount);
+ for (int i = 0; i < shapeCount; ++i)
+ {
+ b2FixtureDef prototype = def;
+ prototype.shape = (*shapes)[i];
+ m_Shapes[i] = body->CreateFixture(&prototype);
+ }
+ }
+ else
+ {
+ // No, so add new shape to set.
+ m_Shapes.resize_uninitialized(1);
+ m_Shapes[0] = body->CreateFixture(&def);
+ }
+
+ // Calculate the collider body mass.
+ if (rigidBody != NULL)
+ rigidBody->CalculateColliderBodyMass ();
+
+ // Update the local transform.
+ UpdateLocalTransform ();
+}
+
+
+void Collider2D::CalculateColliderTransformation (const Rigidbody2D* ignoreRigidbody, b2Body** attachedBody, Matrix4x4f& matrix)
+{
+ // Fetch the transform.
+ const Transform& transform = GetComponent(Transform);
+
+ // Do we have a rigid-body on the same game object?
+ Rigidbody2D* rigidBody = QueryComponent (Rigidbody2D);
+ if (rigidBody && rigidBody != ignoreRigidbody && rigidBody->IsActive () && rigidBody->GetBody () != NULL)
+ {
+ // Yes, so no transformation.
+ matrix.SetIdentity();
+
+ // Ensure the rigid-body is available.
+ Assert (rigidBody->GetBody() != NULL);
+
+ // Set attached body.
+ *attachedBody = rigidBody->GetBody();
+
+ // Flag as non-static.
+ m_IsStaticBody = false;
+
+ return;
+ }
+
+ // Find valid rigid-body parent transform.
+ Transform* parentTransform = transform.GetParent ();
+
+ while(parentTransform)
+ {
+ // Fetch the grandparent transform.
+ Transform* grandParentTransform = parentTransform->GetParent ();
+
+ // Fetch the parent game object.
+ GameObject* go = parentTransform->GetGameObjectPtr ();
+ if (go)
+ {
+ // Fetch any rigid-body.
+ rigidBody = go->QueryComponent (Rigidbody2D);
+
+ // Is the rigid-body valid or we're at the transform root?
+ if (rigidBody && rigidBody != ignoreRigidbody && rigidBody->IsActive () && rigidBody->GetBody () != NULL)
+ {
+ // Yes, so calculate its relative transformation.
+ Vector3f childPosition = transform.GetPosition ();
+ Quaternionf childRotation = transform.GetRotation ();
+ Matrix4x4f childMatrix, parentMatrix;
+ childMatrix.SetTR (childPosition, childRotation);
+ parentMatrix = parentTransform->GetWorldToLocalMatrixNoScale ();
+ MultiplyMatrices4x4 (&parentMatrix, &childMatrix, &matrix);
+
+ // Ensure the rigid-body is available.
+ Assert (rigidBody->GetBody() != NULL);
+
+ // Set attached body.
+ *attachedBody = rigidBody->GetBody();
+
+ // Flag as non-static.
+ m_IsStaticBody = false;
+
+ return;
+ }
+ }
+
+ // Move up hierarchy.
+ parentTransform = grandParentTransform;
+ }
+
+ // Fetch the transformation from the origin (ground-body position).
+ matrix = transform.GetLocalToWorldMatrixNoScale();
+
+ // Set attached body (ground-body).
+ *attachedBody = GetPhysicsGroundBody();
+
+ // Flag as static.
+ m_IsStaticBody = true;
+}
+
+
+bool Collider2D::HasLocalTransformChanged () const
+{
+ // Fetch the transform.
+ const Transform& transform = GetComponent(Transform);
+
+ return !m_LocalTransformInitialized || m_LocalPosition != transform.GetLocalPosition () || m_LocalRotation != transform.GetLocalRotation ();
+}
+
+
+void Collider2D::UpdateLocalTransform ()
+{
+ // Fetch the transform.
+ const Transform& transform = GetComponent(Transform);
+
+ m_LocalPosition = transform.GetLocalPosition ();
+ m_LocalRotation = transform.GetLocalRotation ();
+ m_LocalTransformInitialized = true;
+}
+
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/Collider2D.h b/Runtime/Physics2D/Collider2D.h
new file mode 100644
index 0000000..8b62ce5
--- /dev/null
+++ b/Runtime/Physics2D/Collider2D.h
@@ -0,0 +1,90 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "External/Box2D/Box2D/Box2D.h"
+
+class Rigidbody2D;
+class Transform;
+class Matrix4x4f;
+class PhysicsMaterial2D;
+
+
+// --------------------------------------------------------------------------
+
+
+class Collider2D : public Behaviour
+{
+public:
+ typedef dynamic_array<b2Fixture*> FixtureArray;
+
+public:
+ REGISTER_DERIVED_ABSTRACT_CLASS (Collider2D, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (Collider2D)
+
+ Collider2D (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~Collider2D (); declared-by-macro
+
+ static void InitializeClass ();
+ static void CleanupClass () {}
+
+ virtual void Reset ();
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+ virtual void Deactivate (DeactivateOperation operation);
+ void Cleanup ();
+
+ // Colliders are recreated when deleting a rigibody component from existing game object;
+ // since colliders now have to be attached to the "static body". Old RigidBody2D component
+ // is not destroyed at this point yet, and thus it should be ignored when finding which
+ // RB to attach colliders to.
+ void RecreateCollider (const Rigidbody2D* ignoreRigidbody);
+
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+
+ void SetIsTrigger (bool trigger);
+ bool GetIsTrigger () const { return m_IsTrigger; }
+
+ PPtr<PhysicsMaterial2D> GetMaterial ();
+ void SetMaterial (PPtr<PhysicsMaterial2D> material);
+
+ bool OverlapPoint (const Vector2f& point) const;
+
+ // Shapes.
+ inline int GetShapeCount() const { return m_Shapes.size(); }
+ const b2Fixture* GetShape() const { return (m_Shapes.size() ? m_Shapes[0] : 0); }
+ b2Fixture* GetShape() { return (m_Shapes.size() ? m_Shapes[0] : 0); }
+ const FixtureArray& GetShapes() const { return m_Shapes; }
+ FixtureArray& GetShapes() { return m_Shapes; }
+
+ Rigidbody2D* GetRigidbody ();
+
+ void TransformChanged (int changeMask);
+
+protected:
+ virtual void Create (const Rigidbody2D* ignoreRigidbody = NULL) = 0;
+ void FinalizeCreate (b2FixtureDef& def, b2Body* body, const dynamic_array<b2Shape*>* shapes = 0);
+ void CalculateColliderTransformation (const Rigidbody2D* ignoreRigidbody, b2Body** attachedBody, Matrix4x4f& matrix);
+
+ bool HasLocalTransformChanged () const;
+ void UpdateLocalTransform ();
+
+protected:
+ PPtr<PhysicsMaterial2D> m_Material;
+ bool m_IsTrigger;
+ bool m_IsStaticBody;
+ FixtureArray m_Shapes;
+
+private:
+ Vector3f m_LocalPosition;
+ Quaternionf m_LocalRotation;
+ bool m_LocalTransformInitialized;
+};
+
+#endif
diff --git a/Runtime/Physics2D/CollisionListener2D.cpp b/Runtime/Physics2D/CollisionListener2D.cpp
new file mode 100644
index 0000000..2e7d2b6
--- /dev/null
+++ b/Runtime/Physics2D/CollisionListener2D.cpp
@@ -0,0 +1,400 @@
+#include "UnityPrefix.h"
+#include "CollisionListener2D.h"
+
+#if ENABLE_2D_PHYSICS
+
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+
+#include "Runtime/Profiler/Profiler.h"
+
+PROFILER_INFORMATION(gPhysics2DProfileContactPreSolveAcquire, "Physics2D.ContactPreSolveAcquire", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileContactBeginAcquire, "Physics2D.ContactBeginAcquire", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileContactEndAcquire, "Physics2D.ContactEndAcquire", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileContactReporting, "Physics2D.ContactReporting", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileContactReportTriggers, "Physics2D.ContactReportTriggers", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileContactReportCollisions, "Physics2D.ContactReportCollisions", kProfilerPhysics)
+
+
+// --------------------------------------------------------------------------
+
+
+inline void VerifyObjectPtr(Object* obj)
+{
+ #if !UNITY_RELEASE
+ if (obj == NULL)
+ return;
+ Assert(Object::IDToPointer(obj->GetInstanceID()) == obj);
+ #endif
+}
+
+
+CollisionListener2D::CollisionListener2D()
+ : m_ReportingCollisions(false)
+{
+ m_Collisions.set_empty_key(std::make_pair((Collider2D*)NULL,(Collider2D*)NULL));
+ m_Collisions.set_deleted_key(std::make_pair((Collider2D*)~0,(Collider2D*)~0));
+}
+
+
+void CollisionListener2D::PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
+{
+ PROFILER_AUTO(gPhysics2DProfileContactPreSolveAcquire, NULL);
+ Assert (!m_ReportingCollisions);
+
+ // Fetch the fixtures and colliders.
+ b2Fixture* fixture = contact->GetFixtureA();
+ b2Fixture* otherFixture = contact->GetFixtureB();
+ Collider2D* collider = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ VerifyObjectPtr (collider);
+ Collider2D* otherCollider = reinterpret_cast<Collider2D*>(otherFixture->GetUserData());
+ VerifyObjectPtr (otherCollider);
+
+ // Calculate the contact key.
+ Collider2D* firstCollider = collider;
+ Collider2D* secondCollider = otherCollider;
+ if (firstCollider->GetInstanceID() > secondCollider->GetInstanceID ())
+ std::swap (firstCollider, secondCollider);
+ const ColliderKey contactKey = std::make_pair (firstCollider, secondCollider);
+
+ // Find the contact.
+ ColliderMap::iterator colliderItr = m_Collisions.find (contactKey);
+ Assert (colliderItr != m_Collisions.end ());
+ Collision2D& collision = colliderItr->second;
+
+ // Ignore if the contact was not just added.
+ if (collision.m_ContactMode != Collision2D::ContactAdded)
+ return;
+
+ // Calculate the contacts.
+ collision.m_ContactCount = contact->GetManifold()->pointCount;
+ contact->GetWorldManifold( &collision.m_ContactManifold );
+
+ // Fetch the rigid-bodies.
+ Rigidbody2D* rigidbody = collider ? collider->GetRigidbody() : NULL;
+ VerifyObjectPtr (rigidbody);
+ Rigidbody2D* otherRigidbody = otherCollider ? otherCollider->GetRigidbody() : NULL;
+ VerifyObjectPtr (otherRigidbody);
+ b2Body* body = rigidbody != NULL ? rigidbody->GetBody () : GetPhysicsGroundBody ();
+ b2Body* otherBody = otherRigidbody != NULL ? otherRigidbody->GetBody () : GetPhysicsGroundBody ();
+
+ // Calculate relative velocity.
+ const b2Vec2& contactPoint = collision.m_ContactManifold.points[0];
+ const b2Vec2 bodyVelocity = body->GetLinearVelocityFromWorldPoint(contactPoint);
+ const b2Vec2 otherBodyVelocity = otherBody->GetLinearVelocityFromWorldPoint(contactPoint);
+ collision.m_RelativeVelocity.Set (otherBodyVelocity.x - bodyVelocity.x, otherBodyVelocity.y - bodyVelocity.y);
+}
+
+
+void CollisionListener2D::BeginContact(b2Contact* contact)
+{
+ PROFILER_AUTO(gPhysics2DProfileContactBeginAcquire, NULL);
+
+ Assert (!m_ReportingCollisions);
+ Assert (contact->IsTouching());
+
+ // Fetch the fixtures and colliders.
+ b2Fixture* fixture = contact->GetFixtureA();
+ b2Fixture* otherFixture = contact->GetFixtureB();
+ Collider2D* collider = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ VerifyObjectPtr (collider);
+ Collider2D* otherCollider = reinterpret_cast<Collider2D*>(otherFixture->GetUserData());
+ VerifyObjectPtr (otherCollider);
+
+ // Calculate the contact key.
+ Collider2D* firstCollider = collider;
+ Collider2D* secondCollider = otherCollider;
+ if (firstCollider->GetInstanceID() > secondCollider->GetInstanceID ())
+ std::swap (firstCollider, secondCollider);
+ const ColliderKey contactKey = std::make_pair (firstCollider, secondCollider);
+
+ // Find the contact.
+ ColliderMap::iterator colliderItr = m_Collisions.find (contactKey);
+
+ // If we already have a contact added for this collider-pair then we'll just bump-up the contact references.
+ // We do this because each collider can have multiple fixtures resulting in multiple contacts generated.
+ if (colliderItr != m_Collisions.end ())
+ {
+ Collision2D& collision = colliderItr->second;
+
+ // Increase contact references.
+ collision.m_ContactReferences++;
+
+ // Contact mode should revert to stay if it was removed else it's just added.
+ collision.m_ContactMode = collision.m_ContactMode == Collision2D::ContactRemoved ? Collision2D::ContactStay : Collision2D::ContactAdded;
+
+ return;
+ }
+
+ // Add the contact.
+ Collision2D& collision = m_Collisions[contactKey];
+
+ // Fetch the rigid-bodies.
+ Rigidbody2D* rigidbody = collider ? collider->GetRigidbody() : NULL;
+ VerifyObjectPtr (rigidbody);
+ Rigidbody2D* otherRigidbody = otherCollider ? otherCollider->GetRigidbody() : NULL;
+ VerifyObjectPtr (otherRigidbody);
+
+ // Populate the collision entry.
+ collision.m_ContactReferences = 1;
+ collision.m_Rigidbody = rigidbody;
+ collision.m_OtherRigidbody = otherRigidbody;
+ collision.m_Collider = collider;
+ collision.m_OtherCollider = otherCollider;
+ collision.m_Flipped = false;
+ collision.m_ContactMode = Collision2D::ContactAdded;
+ collision.m_TriggerCollision = fixture->IsSensor() || otherFixture->IsSensor();
+ collision.m_ContactCount = 0;
+ collision.m_RelativeVelocity = Vector2f::zero;
+}
+
+
+void CollisionListener2D::EndContact(b2Contact* contact)
+{
+ if (m_ReportingCollisions)
+ return;
+
+ PROFILER_AUTO(gPhysics2DProfileContactEndAcquire, NULL);
+
+ // Fetch the fixtures and colliders.
+ b2Fixture* fixture = contact->GetFixtureA();
+ b2Fixture* otherFixture = contact->GetFixtureB();
+ Collider2D* collider = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ VerifyObjectPtr (collider);
+ Collider2D* otherCollider = reinterpret_cast<Collider2D*>(otherFixture->GetUserData());
+ VerifyObjectPtr (otherCollider);
+
+ // Calculate the contact key.
+ Collider2D* firstCollider = collider;
+ Collider2D* secondCollider = otherCollider;
+ if (firstCollider->GetInstanceID() > secondCollider->GetInstanceID ())
+ std::swap (firstCollider, secondCollider);
+ const ColliderKey contactKey = std::make_pair (firstCollider, secondCollider);
+
+ // Find the contact.
+ ColliderMap::iterator colliderItr = m_Collisions.find (contactKey);
+ Assert (colliderItr != m_Collisions.end ());
+ Collision2D& collision = colliderItr->second;
+
+ // We need to reduce the contact references.
+ // We do this because each collider can have multiple fixtures resulting in multiple contacts generated.
+ UInt32& contactReferences = collision.m_ContactReferences;
+ contactReferences--;
+
+ // Finish if there still exists contact references.
+ if (contactReferences > 0)
+ return;
+
+ // Flag the contact as invalid if either collider is not active.
+ if (!collider->IsActive () || !collider->GetEnabled () || !otherCollider->IsActive() || !otherCollider->GetEnabled ())
+ {
+ collision.m_ContactMode = Collision2D::ContactInvalid;
+ return;
+ }
+
+ // Sanity!
+ VerifyObjectPtr (collision.m_Collider);
+ VerifyObjectPtr (collision.m_OtherCollider);
+ VerifyObjectPtr (collision.m_Rigidbody);
+ VerifyObjectPtr (collision.m_OtherRigidbody);
+
+ // Flag as just removed.
+ collision.m_ContactMode = Collision2D::ContactRemoved;
+}
+
+
+void CollisionListener2D::InvalidateColliderCollisions(Collider2D* collider)
+{
+ // Flag collider collision information for the specified collider as invalid.
+ // NOTE: Does not send any collision/trigger messages (matches what 3D physics is doing).
+ for (ColliderMap::iterator colliderItr = m_Collisions.begin(); colliderItr != m_Collisions.end(); ++colliderItr)
+ {
+ // Does this contact relate to this collider?
+ if (colliderItr->first.first == collider || colliderItr->first.second == collider)
+ {
+ // Yes, so flag it as a bad contact.
+ colliderItr->second.m_ContactMode = Collision2D::ContactInvalid;
+ }
+ }
+}
+
+
+void CollisionListener2D::DestroyColliderCollisions(Collider2D* collider)
+{
+ // Destroy collider collision information for the specified collider immediately.
+ for (ColliderMap::iterator colliderItr = m_Collisions.begin(); colliderItr != m_Collisions.end(); /**/)
+ {
+ // Fetch next collider.
+ ColliderMap::iterator nextColliderItr = colliderItr;
+ ++nextColliderItr;
+
+ // Does this contact relate to this collider?
+ if (colliderItr->first.first == collider || colliderItr->first.second == collider)
+ {
+ // Yes, so remove it.
+ m_Collisions.erase (colliderItr);
+ }
+
+ colliderItr = nextColliderItr;
+ }
+}
+
+
+void CollisionListener2D::ReportCollisions()
+{
+ PROFILER_AUTO(gPhysics2DProfileContactReporting, NULL);
+
+ Assert (!m_ReportingCollisions);
+ m_ReportingCollisions = true;
+
+ // Iterate all the active collider collisions.
+ for (ColliderMap::iterator colliderItr = m_Collisions.begin(); colliderItr != m_Collisions.end(); /**/)
+ {
+ // Fetch next collider.
+ ColliderMap::iterator nextColliderItr = colliderItr;
+ ++nextColliderItr;
+
+ // Fetch the collision.
+ Collision2D& collision = colliderItr->second;
+
+ // Fetch the contact mode.
+ Collision2D::ContactMode& contactMode = collision.m_ContactMode;
+
+ // Process the collision if it's not invalid.
+ if (contactMode != Collision2D::ContactInvalid)
+ {
+ // Further validate the collision.
+ VerifyObjectPtr (collision.m_Collider);
+ VerifyObjectPtr (collision.m_OtherCollider);
+ VerifyObjectPtr (collision.m_Rigidbody);
+ VerifyObjectPtr (collision.m_OtherRigidbody);
+
+ // Calculate the message targets.
+ Unity::Component* messageTarget = (Unity::Component*)collision.m_Collider;
+ Unity::Component* otherMessageTarget = (Unity::Component*)collision.m_OtherCollider;
+
+ // Reset the callback message.
+ const MessageIdentifier* callbackMessage = NULL;
+
+ // Is this a trigger collision?
+ if (collision.m_TriggerCollision)
+ {
+ PROFILER_AUTO(gPhysics2DProfileContactReportTriggers, NULL);
+
+ // Yes, so calculate the appropriate trigger callback message.
+ if (collision.m_ContactMode == Collision2D::ContactAdded)
+ callbackMessage = &kTriggerEnter2D;
+ else if (collision.m_ContactMode == Collision2D::ContactRemoved)
+ callbackMessage = &kTriggerExit2D;
+ else
+ callbackMessage = &kTriggerStay2D;
+
+ // Send trigger callbacks to both colliders
+ messageTarget->SendMessage (*callbackMessage, collision.m_OtherCollider, ClassID (Collider2D));
+ otherMessageTarget->SendMessage (*callbackMessage, collision.m_Collider, ClassID(Collider2D));
+ }
+ else
+ {
+ PROFILER_AUTO(gPhysics2DProfileContactReportCollisions, NULL);
+
+ // No, so calculate the appropriate collision callback message.
+ if (collision.m_ContactMode == Collision2D::ContactAdded)
+ callbackMessage = &kCollisionEnter2D;
+ else if (collision.m_ContactMode == Collision2D::ContactRemoved)
+ callbackMessage = &kCollisionExit2D;
+ else
+ callbackMessage = &kCollisionStay2D;
+
+ // Send collision callbacks to both colliders.
+ collision.m_Flipped = true;
+ messageTarget->SendMessage (*callbackMessage, &collision, ClassID (Collision2D));
+ collision.m_Flipped = false;
+ otherMessageTarget->SendMessage (*callbackMessage, &collision, ClassID (Collision2D));
+ }
+ }
+
+ // Remove contacts that are flagged as being removed.
+ if (contactMode == Collision2D::ContactRemoved || contactMode == Collision2D::ContactInvalid)
+ {
+ m_Collisions.erase (colliderItr);
+ }
+ else
+ {
+ // The collision is now at "stay" mode.
+ contactMode = Collision2D::ContactStay;
+ }
+
+ colliderItr = nextColliderItr;
+ }
+
+ m_ReportingCollisions = false;
+}
+
+
+#if ENABLE_SCRIPTING
+ScriptingObjectPtr ConvertCollision2DToScripting (Collision2D* input)
+{
+ Collision2D& collision = *reinterpret_cast<Collision2D*>(input);
+ ScriptingCollision2D scriptCollision;
+ ScriptingObjectPtr collider;
+ ScriptingObjectPtr otherCollider;
+
+ // Populate object targets.
+ if (collision.m_Flipped)
+ {
+ scriptCollision.rigidbody = Scripting::ScriptingWrapperFor (collision.m_OtherRigidbody);
+ collider = scriptCollision.collider = Scripting::ScriptingWrapperFor (collision.m_OtherCollider);
+ otherCollider = Scripting::ScriptingWrapperFor (collision.m_Collider);
+ scriptCollision.relativeVelocity = -collision.m_RelativeVelocity;
+ }
+ else
+ {
+ scriptCollision.rigidbody = Scripting::ScriptingWrapperFor (collision.m_Rigidbody);
+ collider = scriptCollision.collider = Scripting::ScriptingWrapperFor (collision.m_Collider);
+ otherCollider = Scripting::ScriptingWrapperFor (collision.m_OtherCollider);
+ scriptCollision.relativeVelocity = collision.m_RelativeVelocity;
+ }
+
+ // Populate contact array.
+ ScriptingArrayPtr contacts = CreateScriptingArray<ScriptingContactPoint2D>(GetScriptingManager ().GetCommonClasses ().contactPoint2D, collision.m_ContactCount);
+ scriptCollision.contacts = contacts;
+
+ // Fetch collision normal.
+ const b2Vec2& manifoldNormal = collision.m_Flipped ? -collision.m_ContactManifold.normal : collision.m_ContactManifold.normal;
+ const Vector2f collisionNormal (manifoldNormal.x, manifoldNormal.y);
+
+ // Populate contacts.
+ for (int index = 0; index < collision.m_ContactCount; ++index )
+ {
+#if UNITY_WINRT
+ ScriptingContactPoint2D contactPoint;
+#else
+ ScriptingContactPoint2D& contactPoint = Scripting::GetScriptingArrayElement<ScriptingContactPoint2D> (contacts, index);
+#endif
+ // Set contact point.
+ const b2Vec2& manifoldPoint = collision.m_ContactManifold.points[index];
+ contactPoint.point.Set (manifoldPoint.x, manifoldPoint.y);
+ contactPoint.normal = collisionNormal;
+
+ // Set colliders.
+ contactPoint.collider = collider;
+ contactPoint.otherCollider = otherCollider;
+
+#if UNITY_WINRT
+ // A slower way to set a value in the array:
+ // * we create a scripting object;
+ // * then marshal data from contactPoint to that scripting object
+ // * and only then we're setting it in the array
+ // At the moment there's no other way, unless we remove all ScriptingObjectPtr from ScriptingContactPoint2D
+ Scripting::SetScriptingArrayElement(contacts, index, CreateScriptingObjectFromNativeStruct<ScriptingContactPoint2D>(GetScriptingManager ().GetCommonClasses ().contactPoint2D, contactPoint));
+#endif
+ }
+
+ return CreateScriptingObjectFromNativeStruct<ScriptingCollision2D>(GetScriptingManager ().GetCommonClasses ().collision2D, scriptCollision);
+}
+#endif
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/CollisionListener2D.h b/Runtime/Physics2D/CollisionListener2D.h
new file mode 100644
index 0000000..f87cecb
--- /dev/null
+++ b/Runtime/Physics2D/CollisionListener2D.h
@@ -0,0 +1,123 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/Utilities/dense_hash_map.h"
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Box2D/Box2D.h"
+#include <list>
+
+class Rigidbody2D;
+class b2Contact;
+
+
+// --------------------------------------------------------------------------
+
+
+struct Collision2D
+{
+ Collision2D() :
+ m_Rigidbody(NULL),
+ m_OtherRigidbody(NULL),
+ m_Collider(NULL),
+ m_OtherCollider(NULL),
+ m_ContactMode(ContactInvalid),
+ m_Flipped(false),
+ m_TriggerCollision(false),
+ m_ContactCount(0),
+ m_ContactReferences(0) {}
+
+ Rigidbody2D* m_Rigidbody;
+ Rigidbody2D* m_OtherRigidbody;
+ Collider2D* m_Collider;
+ Collider2D* m_OtherCollider;
+
+ UInt32 m_ContactCount;
+ UInt32 m_ContactReferences;
+ b2WorldManifold m_ContactManifold;
+ Vector2f m_RelativeVelocity;
+
+ enum ContactMode
+ {
+ ContactInvalid,
+ ContactAdded,
+ ContactRemoved,
+ ContactStay
+ };
+
+ ContactMode m_ContactMode;
+ bool m_Flipped;
+ bool m_TriggerCollision;
+};
+
+
+// --------------------------------------------------------------------------
+
+#if ENABLE_SCRIPTING
+struct ScriptingContactPoint2D
+{
+ Vector2f point;
+ Vector2f normal;
+ ScriptingObjectPtr collider;
+ ScriptingObjectPtr otherCollider;
+};
+
+struct ScriptingCollision2D
+{
+ ScriptingObjectPtr rigidbody;
+ ScriptingObjectPtr collider;
+ ScriptingArrayPtr contacts;
+ Vector2f relativeVelocity;
+};
+
+ScriptingObjectPtr ConvertCollision2DToScripting (Collision2D* input);
+#endif
+
+
+// --------------------------------------------------------------------------
+
+
+class CollisionListener2D : public b2ContactListener
+{
+public:
+ CollisionListener2D();
+
+ // b2ContactListener interface
+ virtual void BeginContact(b2Contact* contact);
+ virtual void EndContact(b2Contact* contact);
+ virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold);
+
+ void ReportCollisions();
+ void InvalidateColliderCollisions(Collider2D* collider);
+ void DestroyColliderCollisions(Collider2D* collider);
+
+private:
+ // Collision information is stored for each collider pair (the key is sorted by instance IDs so it always identifies the pair).
+ // In the collision information, contact points etc. are stored.
+ typedef std::pair<Collider2D*,Collider2D*> ColliderKey;
+ struct ColliderKeyHashFunctor
+ {
+ inline size_t operator()(const ColliderKey& x) const
+ {
+ UInt32 xa = x.first->GetInstanceID();
+ UInt32 xb = x.second->GetInstanceID();
+ UInt32 a = xa;
+ a = (a+0x7ed55d16) + (a<<12);
+ a = (a^0xc761c23c) ^ (a>>19);
+ a ^= xb;
+ a = (a+0x165667b1) + (a<<5);
+ a = (a+0xd3a2646c) ^ (a<<9);
+ return a;
+ }
+ };
+ typedef std::pair<const ColliderKey, Collision2D> ColliderKeyToCollisionPair;
+ typedef dense_hash_map<ColliderKey, Collision2D, ColliderKeyHashFunctor, std::equal_to<ColliderKey>, STL_ALLOCATOR(kMemSTL, ColliderKeyToCollisionPair) > ColliderMap;
+
+private:
+ ColliderMap m_Collisions;
+ bool m_ReportingCollisions;
+};
+
+#endif
diff --git a/Runtime/Physics2D/DistanceJoint2D.cpp b/Runtime/Physics2D/DistanceJoint2D.cpp
new file mode 100644
index 0000000..85b932a
--- /dev/null
+++ b/Runtime/Physics2D/DistanceJoint2D.cpp
@@ -0,0 +1,161 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Physics2D/DistanceJoint2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+
+
+IMPLEMENT_CLASS (DistanceJoint2D)
+IMPLEMENT_OBJECT_SERIALIZE (DistanceJoint2D)
+
+
+// --------------------------------------------------------------------------
+
+
+DistanceJoint2D::DistanceJoint2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+
+DistanceJoint2D::~DistanceJoint2D ()
+{
+}
+
+
+template<class TransferFunction>
+void DistanceJoint2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer(transfer);
+
+ TRANSFER (m_Anchor);
+ TRANSFER (m_ConnectedAnchor);
+ TRANSFER (m_Distance);
+}
+
+
+void DistanceJoint2D::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_Distance = clamp<float> (m_Distance, b2_linearSlop, PHYSICS_2D_LARGE_RANGE_CLAMP);
+
+ if (!IsFinite(m_Anchor))
+ m_Anchor = Vector2f::zero;
+
+ if (!IsFinite(m_ConnectedAnchor))
+ m_ConnectedAnchor = Vector2f::zero;
+}
+
+
+void DistanceJoint2D::Reset ()
+{
+ Super::Reset ();
+
+ m_Distance = 1.0f;
+ m_Anchor = Vector2f::zero;
+ m_ConnectedAnchor = Vector2f::zero;
+}
+
+
+void DistanceJoint2D::SetAnchor (const Vector2f& anchor)
+{
+ ABORT_INVALID_VECTOR2 (anchor, anchor, DistanceJoint2D);
+
+ m_Anchor = anchor;
+ SetDirty();
+
+ // Recreate the joint.
+ if (m_Joint != NULL)
+ ReCreate();
+}
+
+
+void DistanceJoint2D::SetConnectedAnchor (const Vector2f& anchor)
+{
+ ABORT_INVALID_VECTOR2 (anchor, connectedAnchor, DistanceJoint2D);
+
+ m_ConnectedAnchor = anchor;
+ SetDirty();
+
+ // Recreate the joint.
+ if (m_Joint != NULL)
+ ReCreate();
+}
+
+
+void DistanceJoint2D::SetDistance (float distance)
+{
+ ABORT_INVALID_FLOAT (distance, distance, DistanceJoint2D);
+
+ m_Distance = clamp<float> (distance, b2_linearSlop, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ SetDirty();
+
+ if (m_Joint != NULL)
+ ((b2RopeJoint*)m_Joint)->SetMaxLength (m_Distance);
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void DistanceJoint2D::Create ()
+{
+ Assert (m_Joint == NULL);
+
+ if (!IsActive ())
+ return;
+
+ // Fetch transform scales.
+ const Vector3f scale = GetComponent (Transform).GetWorldScaleLossy ();
+ const Vector3f connectedScale = m_ConnectedRigidBody.IsNull () ? Vector3f::one : m_ConnectedRigidBody->GetComponent (Transform).GetWorldScaleLossy ();
+
+ // Configure the joint definition.
+ b2RopeJointDef jointDef;
+ jointDef.maxLength = m_Distance;
+ jointDef.localAnchorA.Set (m_Anchor.x * scale.x, m_Anchor.y * scale.y);
+ jointDef.localAnchorB.Set (m_ConnectedAnchor.x * connectedScale.x, m_ConnectedAnchor.y * connectedScale.y);
+
+ // Create the joint.
+ FinalizeCreateJoint (&jointDef);
+}
+
+
+void DistanceJoint2D::AutoCalculateDistance ()
+{
+ // Reset to default.
+ m_Distance = 1.0f;
+
+ if (m_ConnectedRigidBody.IsNull ())
+ return;
+
+ // Find the appropriate rigid body A.
+ Rigidbody2D* rigidBodyA = QueryComponent(Rigidbody2D);
+ Assert (rigidBodyA != NULL);
+
+ // Find the appropriate rigid body B.
+ Rigidbody2D* rigidBodyB = m_ConnectedRigidBody;
+
+ Transform* transformA = QueryComponent (Transform);
+ Vector3f pointA = transformA->TransformPoint(Vector3f(m_Anchor.x, m_Anchor.y, 0.0f));
+
+ if (rigidBodyB == NULL)
+ {
+ m_Distance = Magnitude(Vector2f(pointA.x, pointA.y));
+ return;
+ }
+
+ Transform* transformB = rigidBodyB->GetGameObjectPtr ()->QueryComponent (Transform);
+ Vector3f pointB = transformB->TransformPoint(Vector3f(m_ConnectedAnchor.x, m_ConnectedAnchor.y, 0.0f));
+
+ m_Distance = Magnitude(Vector2f(pointA.x-pointB.x, pointA.y-pointB.y));
+}
+
+#endif //ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/DistanceJoint2D.h b/Runtime/Physics2D/DistanceJoint2D.h
new file mode 100644
index 0000000..5eeedc2
--- /dev/null
+++ b/Runtime/Physics2D/DistanceJoint2D.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Physics2D/Joint2D.h"
+
+class Vector2f;
+
+
+// --------------------------------------------------------------------------
+
+
+class DistanceJoint2D : public Joint2D
+{
+public:
+
+ REGISTER_DERIVED_CLASS (DistanceJoint2D, Joint2D)
+ DECLARE_OBJECT_SERIALIZE (DistanceJoint2D)
+
+ DistanceJoint2D (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~DistanceJoint2D (); declared-by-macro
+
+ virtual void CheckConsistency();
+ virtual void Reset ();
+
+ Vector2f GetAnchor () const { return m_Anchor; }
+ virtual void SetAnchor (const Vector2f& anchor);
+
+ Vector2f GetConnectedAnchor () const { return m_ConnectedAnchor; }
+ virtual void SetConnectedAnchor (const Vector2f& anchor);
+
+ void SetDistance (float distance);
+ float GetDistance () const { return m_Distance; }
+
+protected:
+ virtual void Create ();
+ void AutoCalculateDistance ();
+
+protected:
+ float m_Distance; ///< The maximum distance which the joint should attempt to maintain between attached bodies. range { 0.005, 1000000 }
+ Vector2f m_Anchor; ///< The local-space anchor from the base rigid-body.
+ Vector2f m_ConnectedAnchor; ///< The local-space anchor from the connected rigid-body.
+};
+
+#endif
diff --git a/Runtime/Physics2D/EdgeCollider2D.cpp b/Runtime/Physics2D/EdgeCollider2D.cpp
new file mode 100644
index 0000000..1c49f96
--- /dev/null
+++ b/Runtime/Physics2D/EdgeCollider2D.cpp
@@ -0,0 +1,176 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+
+#include "Runtime/Physics2D/EdgeCollider2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Graphics/SpriteFrame.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "Runtime/Filters/Mesh/SpriteRenderer.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+#include "External/libtess2/libtess2/tesselator.h"
+
+PROFILER_INFORMATION(gPhysics2DProfileEdgeColliderCreate, "Physics2D.EdgeColliderCreate", kProfilerPhysics)
+
+IMPLEMENT_CLASS (EdgeCollider2D)
+IMPLEMENT_OBJECT_SERIALIZE (EdgeCollider2D)
+
+#define EDGE_COLLIDER_2D_MIN_DISTANCE (b2_linearSlop * b2_linearSlop * 2.01f)
+
+// --------------------------------------------------------------------------
+
+
+EdgeCollider2D::EdgeCollider2D (MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode)
+{
+}
+
+
+EdgeCollider2D::~EdgeCollider2D ()
+{
+}
+
+
+template<class TransferFunction>
+void EdgeCollider2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER (m_Points);
+}
+
+
+void EdgeCollider2D::Reset ()
+{
+ Super::Reset ();
+
+ m_Points.clear ();
+ m_Points.push_back (Vector2f (-0.5f, 0.0f));
+ m_Points.push_back (Vector2f (0.5f, 0.0f));
+}
+
+
+void EdgeCollider2D::SmartReset ()
+{
+ Super::SmartReset ();
+
+ AABB aabb;
+ if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb))
+ {
+ if (aabb.GetExtent ().x < EDGE_COLLIDER_2D_MIN_DISTANCE)
+ {
+ m_Points.clear ();
+ m_Points.push_back (Vector2f (-0.5f, 0.0f));
+ m_Points.push_back (Vector2f (0.5f, 0.0f));
+ return;
+ }
+
+ const Vector3f min = aabb.GetMin();
+ const Vector3f max = aabb.GetMax();
+ const float y = (min.y + max.y) * 0.5f;
+
+ Vector2f points[2] = { Vector2f(min.x, y), Vector2f(max.x, y) };
+ SetPoints (points, 2);
+ }
+}
+
+
+bool EdgeCollider2D::SetPoints (const Vector2f* points, size_t count)
+{
+ // Fail if point count is invalid.
+ if (count < 2)
+ return false;
+
+ m_Points.clear ();
+ while (count-- > 0)
+ {
+ m_Points.push_back (*points++);
+ }
+
+ // Create the points.
+ Create();
+
+ return true;
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void EdgeCollider2D::Create (const Rigidbody2D* ignoreRigidbody)
+{
+ PROFILER_AUTO(gPhysics2DProfileEdgeColliderCreate, NULL);
+
+ // Ensure we're cleaned-up.
+ Cleanup ();
+
+ // Ignore if not active.
+ if (!IsActive() || m_Points.size () < 2)
+ return;
+
+ // Calculate collider transformation.
+ Matrix4x4f relativeTransform;
+ b2Body* body;
+ CalculateColliderTransformation (ignoreRigidbody, &body, relativeTransform);
+
+ // Fetch the collider scale.
+ const Vector3f scale = GetComponent(Transform).GetWorldScaleLossy();
+
+ // Transform the chain.
+ b2Vec2* transformedPoints;
+ ALLOC_TEMP(transformedPoints, b2Vec2, m_Points.size () + 1); // We add an extra one in-case we need to extend.
+ const int validPointCount = TransformPoints (relativeTransform, scale, transformedPoints);
+
+ // Invalid chain if a single edge doesn't exit.
+ if (validPointCount < 2)
+ return;
+
+ // Check vertex distances.
+ for (int i = 1; i < validPointCount; ++i)
+ if (b2DistanceSquared (transformedPoints[i-1], transformedPoints[i]) < EDGE_COLLIDER_2D_MIN_DISTANCE)
+ return;
+
+ // Generate the chain shape.
+ b2ChainShape chainShape;
+ chainShape.CreateChain (transformedPoints, validPointCount);
+
+ // Create the chain fixture.
+ dynamic_array<b2Shape*> chainShapes(kMemTempAlloc);
+ chainShapes.push_back (&chainShape);
+ b2FixtureDef def;
+ FinalizeCreate(def, body, &chainShapes);
+}
+
+
+// --------------------------------------------------------------------------
+
+
+int EdgeCollider2D::TransformPoints(const Matrix4x4f& relativeTransform, const Vector3f& scale, b2Vec2* outPoints)
+{
+ const size_t inPointCount = m_Points.size ();
+ const Vector2f* edgePoint = m_Points.data ();
+ int outCount = 0;
+ for (size_t i = 0; i <inPointCount; ++i, ++edgePoint)
+ {
+ // Calculate transform points.
+ const Vector3f vertex3D = relativeTransform.MultiplyPoint3 (Vector3f(edgePoint->x * scale.x, edgePoint->y * scale.y, 0.0f));
+ const b2Vec2 vertex2D(vertex3D.x, vertex3D.y);
+
+ // Skip point if they end up being too close. Box2d fires asserts if distance between neighbors is less than b2_linearSlop.
+ if (outCount > 0 && b2DistanceSquared(*(outPoints-1), vertex2D) <= EDGE_COLLIDER_2D_MIN_DISTANCE)
+ continue;
+
+ *outPoints++ = vertex2D;
+ ++outCount;
+ }
+
+ return outCount;
+}
+
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/EdgeCollider2D.h b/Runtime/Physics2D/EdgeCollider2D.h
new file mode 100644
index 0000000..f595640
--- /dev/null
+++ b/Runtime/Physics2D/EdgeCollider2D.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Graphics/Polygon2D.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+
+
+// --------------------------------------------------------------------------
+
+
+class EdgeCollider2D : public Collider2D
+{
+public:
+ REGISTER_DERIVED_CLASS (EdgeCollider2D, Collider2D)
+ DECLARE_OBJECT_SERIALIZE (EdgeCollider2D)
+
+ typedef dynamic_array<Vector2f> Points;
+
+ EdgeCollider2D (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~EdgeCollider2D (); declared-by-macro
+
+ virtual void Reset ();
+ virtual void SmartReset ();
+
+ bool SetPoints (const Vector2f* points, size_t count);
+ const Points& GetPoints() const { return m_Points; }
+ const Vector2f& GetPoint (int index) { Assert(index < m_Points.size ()); return m_Points[index]; }
+ size_t GetPointCount() const { return m_Points.size (); }
+ size_t GetEdgeCount() const { return m_Points.size () - 1; }
+
+protected:
+ virtual void Create (const Rigidbody2D* ignoreRigidbody = NULL);
+
+private:
+ int TransformPoints(const Matrix4x4f& relativeTransform, const Vector3f& scale, b2Vec2* outPoints);
+
+private:
+ Points m_Points;
+};
+
+#endif
diff --git a/Runtime/Physics2D/HingeJoint2D.cpp b/Runtime/Physics2D/HingeJoint2D.cpp
new file mode 100644
index 0000000..23e6429
--- /dev/null
+++ b/Runtime/Physics2D/HingeJoint2D.cpp
@@ -0,0 +1,211 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Physics2D/HingeJoint2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Math/Simd/math.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+
+
+IMPLEMENT_CLASS (HingeJoint2D)
+IMPLEMENT_OBJECT_SERIALIZE (HingeJoint2D)
+
+
+// --------------------------------------------------------------------------
+
+
+HingeJoint2D::HingeJoint2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_OldReferenceAngle (std::numeric_limits<float>::infinity ())
+{
+}
+
+
+HingeJoint2D::~HingeJoint2D ()
+{
+}
+
+
+template<class TransferFunction>
+void HingeJoint2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer(transfer);
+
+ TRANSFER (m_Anchor);
+ TRANSFER (m_ConnectedAnchor);
+ TRANSFER (m_UseMotor);
+ transfer.Align ();
+ TRANSFER (m_Motor);
+ TRANSFER (m_UseLimits);
+ transfer.Align ();
+ TRANSFER (m_AngleLimits);
+}
+
+
+void HingeJoint2D::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_Motor.CheckConsistency ();
+ m_AngleLimits.CheckConsistency ();
+
+ if (!IsFinite(m_Anchor))
+ m_Anchor = Vector2f::zero;
+
+ if (!IsFinite(m_ConnectedAnchor))
+ m_ConnectedAnchor = Vector2f::zero;
+}
+
+
+void HingeJoint2D::Reset ()
+{
+ Super::Reset ();
+
+ m_UseMotor = false;
+ m_UseLimits = false;
+ m_Motor.Initialize ();
+ m_AngleLimits.Initialize ();
+
+ m_Anchor = Vector2f::zero;
+ m_ConnectedAnchor = Vector2f::zero;
+}
+
+
+void HingeJoint2D::SetAnchor (const Vector2f& anchor)
+{
+ ABORT_INVALID_VECTOR2 (anchor, anchor, HingeJoint2D);
+
+ m_Anchor = anchor;
+ SetDirty();
+
+ // Recreate the joint.
+ if (m_Joint != NULL)
+ ReCreate();
+}
+
+
+void HingeJoint2D::SetConnectedAnchor (const Vector2f& anchor)
+{
+ ABORT_INVALID_VECTOR2 (anchor, connectedAnchor, HingeJoint2D);
+
+ m_ConnectedAnchor = anchor;
+ SetDirty();
+
+ // Recreate the joint.
+ if (m_Joint != NULL)
+ ReCreate();
+}
+
+
+void HingeJoint2D::SetUseMotor (bool enable)
+{
+ m_UseMotor = enable;
+ SetDirty();
+
+ if (m_Joint != NULL)
+ ((b2RevoluteJoint*)m_Joint)->EnableMotor(m_UseMotor);
+}
+
+
+void HingeJoint2D::SetUseLimits (bool enable)
+{
+ m_UseLimits = enable;
+ SetDirty();
+
+ if (m_Joint != NULL)
+ ((b2RevoluteJoint*)m_Joint)->EnableLimit(m_UseLimits);
+}
+
+
+void HingeJoint2D::SetMotor (const JointMotor2D& motor)
+{
+ m_Motor = motor;
+ m_Motor.CheckConsistency ();
+ SetDirty();
+
+ // Motor is automatically enabled if motor is set.
+ SetUseMotor(true);
+
+ if (m_Joint != NULL)
+ {
+ b2RevoluteJoint* joint = (b2RevoluteJoint*)m_Joint;
+ joint->SetMotorSpeed (math::radians (m_Motor.m_MotorSpeed));
+ joint->SetMaxMotorTorque (m_Motor.m_MaximumMotorForce);
+ }
+}
+
+
+void HingeJoint2D::SetLimits (const JointAngleLimits2D& limits)
+{
+ m_AngleLimits = limits;
+ m_AngleLimits.CheckConsistency ();
+ SetDirty();
+
+ // Limits ares automatically enabled if limits are set.
+ SetUseLimits(true);
+
+ if (m_Joint != NULL)
+ {
+ b2RevoluteJoint* joint = (b2RevoluteJoint*)m_Joint;
+ joint->SetLimits(math::radians (m_AngleLimits.m_LowerAngle), math::radians (m_AngleLimits.m_UpperAngle));
+ }
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void HingeJoint2D::Create ()
+{
+ Assert (m_Joint == NULL);
+
+ if (!IsActive ())
+ return;
+
+ // Fetch transform scales.
+ const Vector3f scale = GetComponent (Transform).GetWorldScaleLossy ();
+ const Vector3f connectedScale = m_ConnectedRigidBody.IsNull () ? Vector3f::one : m_ConnectedRigidBody->GetComponent (Transform).GetWorldScaleLossy ();
+
+ // Configure the joint definition.
+ b2RevoluteJointDef jointDef;
+ jointDef.localAnchorA.Set (m_Anchor.x * scale.x, m_Anchor.y * scale.y);
+ jointDef.localAnchorB.Set (m_ConnectedAnchor.x * connectedScale.x, m_ConnectedAnchor.y * connectedScale.y);
+ jointDef.enableMotor = m_UseMotor;
+ jointDef.enableLimit = m_UseLimits;
+ jointDef.motorSpeed = math::radians (m_Motor.m_MotorSpeed);
+ jointDef.maxMotorTorque = m_Motor.m_MaximumMotorForce;
+ jointDef.lowerAngle = math::radians (m_AngleLimits.m_LowerAngle);
+ jointDef.upperAngle = math::radians (m_AngleLimits.m_UpperAngle);
+ if (jointDef.lowerAngle > jointDef.upperAngle)
+ std::swap(jointDef.lowerAngle, jointDef.upperAngle);
+ jointDef.referenceAngle = m_OldReferenceAngle == std::numeric_limits<float>::infinity () ? FetchBodyB()->GetAngle() - FetchBodyA()->GetAngle() : m_OldReferenceAngle;
+
+ // Create the joint.
+ FinalizeCreateJoint (&jointDef);
+}
+
+
+void HingeJoint2D::ReCreate()
+{
+ // Do we have an existing joint and we're still active/enabled?
+ if (m_Joint != NULL && IsActive () && GetEnabled ())
+ {
+ // Yes, so keep reference angle.
+ m_OldReferenceAngle = ((b2RevoluteJoint*)m_Joint)->GetReferenceAngle ();
+ }
+ else
+ {
+ // No, so reset reference angle.
+ m_OldReferenceAngle = std::numeric_limits<float>::infinity ();
+ }
+
+ Super::ReCreate ();
+}
+
+#endif //ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/HingeJoint2D.h b/Runtime/Physics2D/HingeJoint2D.h
new file mode 100644
index 0000000..6da16b7
--- /dev/null
+++ b/Runtime/Physics2D/HingeJoint2D.h
@@ -0,0 +1,61 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Math/Vector2.h"
+#include "Joint2D.h"
+
+class Vector2f;
+
+
+// --------------------------------------------------------------------------
+
+
+class HingeJoint2D : public Joint2D
+{
+public:
+
+ REGISTER_DERIVED_CLASS (HingeJoint2D, Joint2D)
+ DECLARE_OBJECT_SERIALIZE (HingeJoint2D)
+
+ HingeJoint2D (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~HingeJoint2D (); declared-by-macro
+
+ virtual void CheckConsistency();
+ virtual void Reset ();
+
+ Vector2f GetAnchor () const { return m_Anchor; }
+ virtual void SetAnchor (const Vector2f& anchor);
+
+ Vector2f GetConnectedAnchor () const { return m_ConnectedAnchor; }
+ virtual void SetConnectedAnchor (const Vector2f& anchor);
+
+ bool GetUseMotor () const { return m_UseMotor; }
+ void SetUseMotor (bool enable);
+
+ bool GetUseLimits () const { return m_UseLimits; }
+ void SetUseLimits (bool enable);
+
+ JointMotor2D GetMotor () const { return m_Motor; }
+ void SetMotor (const JointMotor2D& motor);
+
+ JointAngleLimits2D GetLimits () const { return m_AngleLimits; }
+ void SetLimits (const JointAngleLimits2D& limits);
+
+protected:
+ virtual void Create ();
+ virtual void ReCreate();
+
+protected:
+ Vector2f m_Anchor; ///< The local-space anchor from the base rigid-body.
+ Vector2f m_ConnectedAnchor; ///< The local-space anchor from the connected rigid-body.
+ JointMotor2D m_Motor; ///< The joint motor.
+ JointAngleLimits2D m_AngleLimits; ///< The joint angle limits.
+ bool m_UseMotor; ///< Whether to use the joint motor or not.
+ bool m_UseLimits; ///< Whether to use the angle limits or not.
+
+private:
+ float m_OldReferenceAngle;
+};
+
+#endif
diff --git a/Runtime/Physics2D/Joint2D.cpp b/Runtime/Physics2D/Joint2D.cpp
new file mode 100644
index 0000000..ed0de54
--- /dev/null
+++ b/Runtime/Physics2D/Joint2D.cpp
@@ -0,0 +1,201 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+#include "Runtime/Physics2D/Joint2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "External/Box2D/Box2D/Box2D.h"
+
+
+IMPLEMENT_CLASS (Joint2D)
+IMPLEMENT_OBJECT_SERIALIZE (Joint2D)
+INSTANTIATE_TEMPLATE_TRANSFER (Joint2D)
+
+// --------------------------------------------------------------------------
+
+
+Joint2D::Joint2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_Joint(NULL)
+{
+}
+
+
+Joint2D::~Joint2D ()
+{
+ Cleanup ();
+}
+
+
+template<class TransferFunction>
+void Joint2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer(transfer);
+
+ TRANSFER (m_CollideConnected);
+ transfer.Align();
+ TRANSFER (m_ConnectedRigidBody);
+}
+
+
+void Joint2D::Reset ()
+{
+ Super::Reset ();
+
+ Cleanup ();
+
+ m_ConnectedRigidBody = NULL;
+ m_CollideConnected = false;
+}
+
+
+void Joint2D::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ // Recreate joint if appropriate.
+ // Most of Box2D joint properties are immutable once created
+ // thus causing us to regenerate the joint if a property changes in the editor.
+ if ((awakeMode == kDefaultAwakeFromLoad))
+ ReCreate ();
+}
+
+
+void Joint2D::Deactivate (DeactivateOperation operation)
+{
+ Cleanup ();
+ Super::Deactivate (operation);
+}
+
+
+void Joint2D::AddToManager ()
+{
+ // Create the joint.
+ ReCreate ();
+}
+
+
+void Joint2D::RemoveFromManager ()
+{
+ // Destroy the joint.
+ Cleanup ();
+}
+
+
+void Joint2D::RecreateJoint (const Rigidbody2D* ignoreRigidbody)
+{
+ if (IsActive () && GetEnabled())
+ ReCreate ();
+ else
+ Cleanup ();
+}
+
+
+void Joint2D::SetConnectedBody (PPtr<Rigidbody2D> rigidBody)
+{
+ m_ConnectedRigidBody = rigidBody;
+ SetDirty ();
+
+ ReCreate ();
+}
+
+
+void Joint2D::SetCollideConnected (bool collide)
+{
+ m_CollideConnected = collide;
+ SetDirty ();
+
+ ReCreate ();
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void Joint2D::ReCreate()
+{
+ Cleanup();
+
+ if (IsActive () && GetEnabled ())
+ Create();
+}
+
+
+void Joint2D::Cleanup ()
+{
+ // Finish if no joint to clean-up.
+ if (!m_Joint)
+ return;
+
+ // Destroy the joint.
+ GetPhysics2DWorld ()->DestroyJoint (m_Joint);
+ m_Joint = NULL;
+}
+
+
+b2Body* Joint2D::FetchBodyA () const
+{
+ // Find the rigid-body A.
+ Rigidbody2D* rigidBodyA = QueryComponent(Rigidbody2D);
+ Assert (rigidBodyA != NULL);
+
+ // Ensure the rigid-body (body) is available.
+ if ( rigidBodyA )
+ rigidBodyA->Create();
+
+ // Fetch the body A.
+ return rigidBodyA->GetBody();
+}
+
+
+b2Body* Joint2D::FetchBodyB () const
+{
+ // Find the appropriate rigid body B.
+ Rigidbody2D* rigidBodyB = m_ConnectedRigidBody;
+
+ // Ensure the rigid-body (body) is available.
+ if ( rigidBodyB )
+ rigidBodyB->Create();
+
+ // Fetch the appropriate body B.
+ return rigidBodyB != NULL ? rigidBodyB->GetBody() : GetPhysicsGroundBody();
+}
+
+
+void Joint2D::FinalizeCreateJoint (b2JointDef* jointDef)
+{
+ Assert (jointDef != NULL);
+
+ if (!IsActive ())
+ return;
+
+ // Fetch the appropriate body A.
+ b2Body* bodyA = FetchBodyA();
+
+ // Fetch the appropriate body B.
+ b2Body* bodyB = FetchBodyB();
+
+ // Finish if the same body is being used.
+ if ( bodyA == bodyB )
+ {
+ WarningStringObject(Format("Cannot create 2D joint on '%s' as it connects to itself.\n",
+ GetGameObjectPtr()->GetName()), this);
+ return;
+ }
+
+ // Populate the basic joint definition information.
+ jointDef->bodyA = bodyA;
+ jointDef->bodyB = bodyB;
+ jointDef->collideConnected = m_CollideConnected;
+ jointDef->userData = this;
+
+ // Create the joint.
+ m_Joint = GetPhysics2DWorld ()->CreateJoint (jointDef);
+}
+
+
+#endif //ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/Joint2D.h b/Runtime/Physics2D/Joint2D.h
new file mode 100644
index 0000000..a624709
--- /dev/null
+++ b/Runtime/Physics2D/Joint2D.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Physics2D/JointDescriptions2D.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/GameCode/Behaviour.h"
+
+class Rigidbody2D;
+class b2Joint;
+class b2Body;
+struct b2JointDef;
+
+// --------------------------------------------------------------------------
+
+
+class Joint2D : public Behaviour
+{
+ friend class Rigidbody2D;
+
+public:
+ REGISTER_DERIVED_ABSTRACT_CLASS (Joint2D, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (Joint2D)
+
+ Joint2D (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~Joint2D (); declared-by-macro
+
+ virtual void Reset ();
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+ virtual void Deactivate (DeactivateOperation operation);
+
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+
+ void RecreateJoint (const Rigidbody2D* ignoreRigidbody);
+
+ void SetConnectedBody (PPtr<Rigidbody2D> body);
+ PPtr<Rigidbody2D> GetConnectedBody () const { return m_ConnectedRigidBody; }
+
+ void SetCollideConnected (bool Collide);
+ bool GetCollideConnected () const { return m_CollideConnected; }
+
+protected:
+ virtual void Create () = 0;
+ virtual void ReCreate();
+ virtual void Cleanup ();
+
+ b2Body* FetchBodyA () const;
+ b2Body* FetchBodyB () const;
+ void FinalizeCreateJoint (b2JointDef* jointDef);
+
+protected:
+ PPtr<Rigidbody2D> m_ConnectedRigidBody; ///< The rigid body to connect to. No rigid body connects to the scene.
+ bool m_CollideConnected; ///< Whether rigid bodies connected with this joint can collide or not.
+ b2Joint* m_Joint;
+};
+
+#endif //ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/JointDescriptions2D.h b/Runtime/Physics2D/JointDescriptions2D.h
new file mode 100644
index 0000000..23e2062
--- /dev/null
+++ b/Runtime/Physics2D/JointDescriptions2D.h
@@ -0,0 +1,101 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+
+// --------------------------------------------------------------------------
+
+
+struct JointMotor2D
+{
+ float m_MotorSpeed; ///< The target motor speed in degrees/second. range { -1000000, 1000000 }
+ float m_MaximumMotorForce; ///< The maximum force the motor can use to achieve the desired motor speed. range { 0.0, 1000000 }
+
+ void Initialize ()
+ {
+ m_MotorSpeed = 0.0f;
+ m_MaximumMotorForce = 10000.0f;
+ }
+
+ void CheckConsistency ()
+ {
+ m_MotorSpeed = clamp<float> (m_MotorSpeed, -PHYSICS_2D_LARGE_RANGE_CLAMP, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ m_MaximumMotorForce = clamp<float> (m_MaximumMotorForce, 0, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ }
+
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (JointMotor2D)
+};
+
+template<class TransferFunction>
+void JointMotor2D::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (m_MotorSpeed);
+ TRANSFER (m_MaximumMotorForce);
+}
+
+
+// --------------------------------------------------------------------------
+
+
+struct JointAngleLimits2D
+{
+ float m_LowerAngle; ///< The lower angle (in degrees) limit to constrain the joint to. range { -359.9999, 359.9999 }
+ float m_UpperAngle; ///< The upper angle (in degrees) limit to constrain the joint to. range { -359.9999, 359.9999 }
+
+ void Initialize ()
+ {
+ m_LowerAngle = 0.0f;
+ m_UpperAngle = 359.0f;
+ }
+
+ void CheckConsistency ()
+ {
+ m_LowerAngle = clamp<float> (m_LowerAngle, -359.9999f, 359.9999f);
+ m_UpperAngle = clamp<float> (m_UpperAngle, -359.9999f, 359.9999f);
+ }
+
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (JointAngleLimit2D)
+};
+
+template<class TransferFunction>
+void JointAngleLimits2D::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (m_LowerAngle);
+ TRANSFER (m_UpperAngle);
+}
+
+
+// --------------------------------------------------------------------------
+
+
+struct JointTranslationLimits2D
+{
+ float m_LowerTranslation; ///< The lower translation limit to constrain the joint to. range { -1000000, 1000000 }
+ float m_UpperTranslation; ///< The upper translation limit to constrain the joint to. range { -1000000, 1000000 }
+
+ void Initialize ()
+ {
+ m_LowerTranslation = 0.0f;
+ m_UpperTranslation = 0.0f;
+ }
+
+ void CheckConsistency ()
+ {
+ m_LowerTranslation = clamp<float> (m_LowerTranslation, -PHYSICS_2D_LARGE_RANGE_CLAMP, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ m_UpperTranslation = clamp<float> (m_UpperTranslation, -PHYSICS_2D_LARGE_RANGE_CLAMP, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ }
+
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (JointTranslationLimits2D)
+};
+
+template<class TransferFunction>
+void JointTranslationLimits2D::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (m_LowerTranslation);
+ TRANSFER (m_UpperTranslation);
+}
+
+#endif
diff --git a/Runtime/Physics2D/Physics2DManager.cpp b/Runtime/Physics2D/Physics2DManager.cpp
new file mode 100644
index 0000000..cf6dcd3
--- /dev/null
+++ b/Runtime/Physics2D/Physics2DManager.cpp
@@ -0,0 +1,1228 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Physics2D/CollisionListener2D.h"
+#include "Runtime/Physics2D/Physics2DSettings.h"
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+#include "Runtime/Core/Callbacks/PlayerLoopCallbacks.h"
+#include "Runtime/Geometry/Ray.h"
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/BaseClasses/MessageHandler.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Interfaces/IPhysics2D.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Math/Simd/math.h"
+#include "Runtime/Profiler/ProfilerStats.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+
+
+PROFILER_INFORMATION(gPhysics2DDynamicUpdateProfile, "Physics2D.DynamicUpdate", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DFixedUpdateProfile, "Physics2D.FixedUpdate", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DInterpolationsProfile, "Physics2D.Interpolation", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DSimProfile, "Physics2D.Simulate", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DUpdateTransformsProfile, "Physics2D.UpdateTransforms", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DCallbacksProfile, "Physics2D.Callbacks", kProfilerPhysics)
+PROFILER_INFORMATION(gLinecast2DProfile, "Physics2D.Linecast", kProfilerPhysics)
+PROFILER_INFORMATION(gLinecastAll2DProfile, "Physics2D.LinecastAll", kProfilerPhysics)
+PROFILER_INFORMATION(gRaycast2DProfile, "Physics2D.Raycast", kProfilerPhysics)
+PROFILER_INFORMATION(gRaycastAll2DProfile, "Physics2D.RaycastAll", kProfilerPhysics)
+PROFILER_INFORMATION(gOverlapPoint2DProfile, "Physics2D.OverlapPoint", kProfilerPhysics)
+PROFILER_INFORMATION(gOverlapPointAll2DProfile, "Physics2D.OverlapPointAll", kProfilerPhysics)
+PROFILER_INFORMATION(gOverlapCircle2DProfile, "Physics2D.OverlapCircle", kProfilerPhysics)
+PROFILER_INFORMATION(gOverlapCircleAll2DProfile, "Physics2D.OverlapCircleAll", kProfilerPhysics)
+PROFILER_INFORMATION(gOverlapArea2DProfile, "Physics2D.OverlapArea", kProfilerPhysics)
+PROFILER_INFORMATION(gOverlapAreaAll2DProfile, "Physics2D.OverlapAreaAll", kProfilerPhysics)
+PROFILER_INFORMATION(gGetRayIntersection2DProfile, "Physics2D.GetRayIntersection", kProfilerPhysics)
+PROFILER_INFORMATION(gGetRayIntersectionAll2DProfile, "Physics2D.GetRayIntersectionAll", kProfilerPhysics)
+
+
+// --------------------------------------------------------------------------
+
+
+inline bool CheckFixtureLayer(b2Fixture* fixture, int layerMask)
+{
+ DebugAssert(fixture);
+
+ Collider2D* col = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ if (!col)
+ return false; // no collider
+
+ GameObject* go = col->GetGameObjectPtr();
+ if (!go)
+ return false; // no GO
+
+ int goMask = go->GetLayerMask();
+ if ((goMask & layerMask) == 0)
+ return false; // ignoring this layer
+
+ return true; // all passed
+}
+
+// --------------------------------------------------------------------------
+
+
+inline bool CheckColliderDepth(Collider2D* collider, const float minDepth, const float maxDepth)
+{
+ DebugAssert(collider);
+
+ // Fetch depth of the collider.
+ const float depth = collider->GetComponent(Transform).GetPosition ().z;
+
+ // Check if in the depth range.
+ return !(depth < minDepth || depth > maxDepth);
+}
+
+
+// --------------------------------------------------------------------------
+
+
+inline void NormalizeDepthRange(float& minDepth, float& maxDepth)
+{
+ // Clamp any range bounds specified as +- infinity to real values.
+ minDepth = (minDepth == -std::numeric_limits<float>::infinity ()) ? -std::numeric_limits<float>::max () : minDepth;
+ maxDepth = (maxDepth == std::numeric_limits<float>::infinity ()) ? std::numeric_limits<float>::max () : maxDepth;
+
+ if (minDepth < maxDepth)
+ return;
+
+ std::swap (minDepth, maxDepth);
+}
+
+
+// --------------------------------------------------------------------------
+
+
+struct ContactFilter2D : public b2ContactFilter
+{
+ virtual bool ShouldCollide(b2Fixture* fixtureA, b2Fixture* fixtureB)
+ {
+ Collider2D* colliderA = reinterpret_cast<Collider2D*>(fixtureA->GetUserData ());
+ Collider2D* colliderB = reinterpret_cast<Collider2D*>(fixtureB->GetUserData ());
+
+ if (!colliderA->GetEnabled () || !colliderB->GetEnabled ())
+ return false;
+
+ // Fetch collider layers.
+ const int layerA = colliderA->GetGameObject ().GetLayer();
+ const int layerB = colliderB->GetGameObject ().GetLayer();
+
+ // Decide on the contact based upon the layer collision mask.
+ return GetPhysics2DSettings().GetLayerCollisionMask(layerA) & (1<<layerB);
+ }
+};
+
+
+// --------------------------------------------------------------------------
+
+
+struct ColliderHitsByDepthComparitor
+{
+ inline bool operator()(const Collider2D* a, const Collider2D* b)
+ {
+ return a->GetComponent(Transform).GetPosition ().z < b->GetComponent(Transform).GetPosition ().z;
+ }
+
+ inline int CompareDepth(const Collider2D* a, const Collider2D* b)
+ {
+ const float depthA = a->GetComponent(Transform).GetPosition ().z;
+ const float depthB = b->GetComponent(Transform).GetPosition ().z;
+
+ if (depthA < depthB) return -1;
+ if (depthA > depthB) return 1;
+ return 0;
+ }
+} m_CompareDepth;
+
+
+// --------------------------------------------------------------------------
+
+
+struct RayHitsByDepthComparitor
+{
+ inline bool operator()(const RaycastHit2D& a, const RaycastHit2D& b)
+ {
+ return a.collider->GetComponent(Transform).GetPosition ().z < b.collider->GetComponent(Transform).GetPosition ().z;
+ }
+};
+
+struct RayHitsByInverseDepthComparitor
+{
+ inline bool operator()(const RaycastHit2D& a, const RaycastHit2D& b)
+ {
+ return a.collider->GetComponent(Transform).GetPosition ().z > b.collider->GetComponent(Transform).GetPosition ().z;
+ }
+};
+
+
+// --------------------------------------------------------------------------
+
+
+class Raycast2DQuery : public b2RayCastCallback
+{
+public:
+ Raycast2DQuery(const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<RaycastHit2D>& outHits)
+ : m_PointA(pointA)
+ , m_PointB(pointB)
+ , m_LayerMask(layerMask)
+ , m_MinDepth(minDepth)
+ , m_MaxDepth(maxDepth)
+ , m_Hits(outHits)
+ {
+ NormalizeDepthRange(m_MinDepth, m_MaxDepth);
+ }
+
+ int RunQuery ()
+ {
+ // Calculate if the ray has zero-length or not.
+ const bool rayHasMagnitude = SqrMagnitude (m_PointB - m_PointA) > b2_epsilon * b2_epsilon;
+
+ // Perform a discrete check at the start-point (Box2D does not discover these for a ray-cast).
+ dynamic_array<Collider2D*> colliderHits(kMemTempAlloc);
+ if (GetPhysics2DManager ().OverlapPointAll (m_PointA, m_LayerMask, m_MinDepth, m_MaxDepth, &colliderHits) > 0)
+ {
+ const Vector2f collisionNormal = rayHasMagnitude ? NormalizeFast (m_PointA-m_PointB) : Vector2f::zero;
+ for (dynamic_array<Collider2D*>::iterator colliderItr = colliderHits.begin (); colliderItr != colliderHits.end (); ++colliderItr)
+ {
+ RaycastHit2D rayHit;
+ rayHit.collider = *colliderItr;
+ rayHit.point = m_PointA;
+ rayHit.normal = collisionNormal;
+ rayHit.fraction = 0.0f;
+ m_Hits.push_back (rayHit);
+ }
+ }
+
+ if (rayHasMagnitude)
+ {
+ // Perform the ray-cast.
+ GetPhysics2DWorld ()->RayCast (this, b2Vec2(m_PointA.x, m_PointA.y), b2Vec2(m_PointB.x, m_PointB.y));
+
+ // Sort the hits by fraction.
+ std::sort (m_Hits.begin(), m_Hits.end(), RaycastHitsByFractionComparitor());
+ }
+
+ return m_Hits.size ();
+ }
+
+ virtual float32 ReportFixture (b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float32 fraction)
+ {
+ // Handle whether ray-casts are hitting triggers or not.
+ if (fixture->IsSensor () && !GetPhysics2DSettings ().GetRaycastsHitTriggers ())
+ return -1.0f;
+
+ // Ignore if not in the selected fixture layer.
+ if (!CheckFixtureLayer (fixture, m_LayerMask))
+ return -1.0f; // ignore and continue
+
+ // Ignore if not in the selected depth-range.
+ Collider2D* collider = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ if (!CheckColliderDepth (collider, m_MinDepth, m_MaxDepth))
+ return -1.0f; // ignore and continue;
+
+ RaycastHit2D hit;
+ hit.point.Set (point.x, point.y);
+ hit.normal.Set (normal.x, normal.y);
+ hit.fraction = fraction;
+ hit.collider = collider;
+
+ // For chain colliders, Box2D will report all individual segments that are hit; also similar situation
+ // when using composite convex colliders. Try searching for a hit with same Collider2D, and replace info
+ // if closer hit. Unfortunately, this is is O(n).
+ for (size_t i = 0, n = m_Hits.size(); i != n; ++i)
+ {
+ if (m_Hits[i].collider == collider)
+ {
+ if (fraction < m_Hits[i].fraction)
+ m_Hits[i] = hit;
+
+ return 1.0f; // continue
+ }
+ }
+
+ // Add new hit
+ m_Hits.push_back (hit);
+
+ return 1.0f; // continue
+ }
+
+private:
+ struct RaycastHitsByFractionComparitor
+ {
+ inline bool operator()(const RaycastHit2D& a, const RaycastHit2D& b)
+ {
+ return a.fraction < b.fraction;
+ }
+ };
+
+private:
+ int m_LayerMask;
+ float m_MinDepth;
+ float m_MaxDepth;
+ Vector2f m_PointA;
+ Vector2f m_PointB;
+ dynamic_array<RaycastHit2D>& m_Hits;
+};
+
+
+// --------------------------------------------------------------------------
+
+
+class OverlapPointQuery2D : public b2QueryCallback
+{
+public:
+ OverlapPointQuery2D(const Vector2f& point, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>& outHits)
+ : m_LayerMask(layerMask)
+ , m_MinDepth(minDepth)
+ , m_MaxDepth(maxDepth)
+ , m_Hits(outHits)
+ {
+ NormalizeDepthRange(m_MinDepth, m_MaxDepth);
+
+ m_Point.Set (point.x, point.y);
+ }
+
+ int RunQuery ()
+ {
+ // Reset results.
+ m_Hits.clear();
+
+ b2AABB aabb;
+ aabb.lowerBound = aabb.upperBound = m_Point;
+ GetPhysics2DWorld ()->QueryAABB (this, aabb);
+
+ // Sort the hits by depth.
+ std::sort (m_Hits.begin(), m_Hits.end(), ColliderHitsByDepthComparitor());
+
+ return m_Hits.size ();
+ }
+
+ virtual bool ReportFixture (b2Fixture* fixture)
+ {
+ // Handle whether ray-casts are hitting triggers or not.
+ if (fixture->IsSensor () && !GetPhysics2DSettings ().GetRaycastsHitTriggers ())
+ return true;
+
+ // Ignore if not in the selected fixture layer.
+ if (!CheckFixtureLayer (fixture, m_LayerMask))
+ return true; // ignore and continue
+
+ // Ignore if not in the selected depth-range.
+ Collider2D* collider = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ if (!CheckColliderDepth (collider, m_MinDepth, m_MaxDepth))
+ return true; // ignore and continue;
+
+ // Ignore if we've already selected this collider and it has a higher depth.
+ for (size_t i = 0; i != m_Hits.size (); ++i)
+ {
+ if (m_Hits[i] == collider)
+ {
+ if (m_CompareDepth.CompareDepth (m_Hits[i], collider) == 1)
+ m_Hits[i] = collider;
+ return true;
+ }
+ }
+
+ // Test point against fixture.
+ if (!fixture->TestPoint (m_Point))
+ return true;
+
+ // Add new hit
+ m_Hits.push_back (collider);
+
+ return true;
+ }
+
+private:
+ b2Vec2 m_Point;
+ int m_LayerMask;
+ float m_MinDepth;
+ float m_MaxDepth;
+ dynamic_array<Collider2D*>& m_Hits;
+};
+
+
+// --------------------------------------------------------------------------
+
+
+class OverlapCircleQuery2D : public b2QueryCallback
+{
+public:
+ OverlapCircleQuery2D(const Vector2f& point, const float radius, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>& outHits)
+ : m_LayerMask(layerMask)
+ , m_MinDepth(minDepth)
+ , m_MaxDepth(maxDepth)
+ , m_Hits(outHits)
+ {
+ NormalizeDepthRange(m_MinDepth, m_MaxDepth);
+
+ // Extremely small radii indicates a point query.
+ if (radius < 0.00001f)
+ {
+ m_Point.Set (point.x, point.y);
+ m_AABB.lowerBound = m_AABB.upperBound = m_Point;
+ m_PointQuery = true;
+ }
+ else
+ {
+ // Calculate circle shape and its AABB.
+ m_CircleShape.m_p.Set (point.x, point.y);
+ m_CircleShape.m_radius = radius;
+ m_QueryTransform.SetIdentity ();
+ m_CircleShape.ComputeAABB( &m_AABB, m_QueryTransform, 0 );
+ m_PointQuery = false;
+ }
+ }
+
+
+ int RunQuery ()
+ {
+ // Reset results.
+ m_Hits.clear();
+
+ GetPhysics2DWorld ()->QueryAABB (this, m_AABB);
+
+ // Sort the hits by depth.
+ std::sort (m_Hits.begin(), m_Hits.end(), ColliderHitsByDepthComparitor());
+
+ return m_Hits.size ();
+ }
+
+ virtual bool ReportFixture (b2Fixture* fixture)
+ {
+ // Handle whether ray-casts are hitting triggers or not.
+ if (fixture->IsSensor () && !GetPhysics2DSettings ().GetRaycastsHitTriggers ())
+ return true;
+
+ // Ignore if not in the selected fixture layer.
+ if (!CheckFixtureLayer (fixture, m_LayerMask))
+ return true; // ignore and continue
+
+ // Ignore if not in the selected depth-range.
+ Collider2D* collider = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ if (!CheckColliderDepth (collider, m_MinDepth, m_MaxDepth))
+ return true; // ignore and continue;
+
+ // Ignore if we've already selected this collider and it has a higher depth.
+ for (size_t i = 0; i != m_Hits.size (); ++i)
+ {
+ if (m_Hits[i] == collider)
+ {
+ if (m_CompareDepth.CompareDepth (m_Hits[i], collider) == 1)
+ m_Hits[i] = collider;
+ return true;
+ }
+ }
+
+ if (m_PointQuery)
+ {
+ // Test point against fixture.
+ if (!fixture->TestPoint (m_Point))
+ return true;
+ }
+ else
+ {
+ // Test circle against fixture.
+ if ( !b2TestOverlap( &m_CircleShape, 0, fixture->GetShape (), 0, m_QueryTransform, fixture->GetBody ()->GetTransform () ) )
+ return true;
+ }
+
+ // Add new hit
+ m_Hits.push_back (collider);
+
+ return true;
+ }
+
+private:
+ int m_LayerMask;
+ float m_MinDepth;
+ float m_MaxDepth;
+ b2Vec2 m_Point;
+ b2CircleShape m_CircleShape;
+ b2AABB m_AABB;
+ b2Transform m_QueryTransform;
+ dynamic_array<Collider2D*>& m_Hits;
+ bool m_PointQuery;
+};
+
+
+// --------------------------------------------------------------------------
+
+
+class OverlapAreaQuery2D : public b2QueryCallback
+{
+public:
+ OverlapAreaQuery2D(Vector2f pointA, Vector2f pointB, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>& outHits)
+ : m_LayerMask(layerMask)
+ , m_MinDepth(minDepth)
+ , m_MaxDepth(maxDepth)
+ , m_Hits(outHits)
+ {
+ NormalizeDepthRange(m_MinDepth, m_MaxDepth);
+
+ // Normalize the points.
+ if (pointA.x > pointB.x)
+ std::swap (pointA.x, pointB.x);
+ if (pointA.y > pointB.y)
+ std::swap (pointA.y, pointB.y);
+
+ // Calculate the AABB.
+ m_PolygonAABB.lowerBound.Set (pointA.x, pointA.y);
+ m_PolygonAABB.upperBound.Set (pointB.x, pointB.y);
+
+ // Calculate polygon shape.
+ b2Vec2 verts[4];
+ verts[0].Set( pointA.x, pointA.y );
+ verts[1].Set( pointB.x, pointA.y );
+ verts[2].Set( pointB.x, pointB.y );
+ verts[3].Set( pointA.x, pointB.y );
+ m_PolygonShape.Set( verts, 4 );
+
+ m_QueryTransform.SetIdentity ();
+ }
+
+ int RunQuery ()
+ {
+ // Reset results.
+ m_Hits.clear();
+
+ // Finish if AABB is invalid.
+ if (!m_PolygonAABB.IsValid())
+ return 0;
+
+ GetPhysics2DWorld ()->QueryAABB (this, m_PolygonAABB);
+
+ // Sort the hits by depth.
+ std::sort (m_Hits.begin(), m_Hits.end(), ColliderHitsByDepthComparitor());
+
+ return m_Hits.size ();
+ }
+
+ virtual bool ReportFixture (b2Fixture* fixture)
+ {
+ // Handle whether ray-casts are hitting triggers or not.
+ if (fixture->IsSensor () && !GetPhysics2DSettings ().GetRaycastsHitTriggers ())
+ return true;
+
+ // Ignore if not in the selected fixture layer.
+ if (!CheckFixtureLayer (fixture, m_LayerMask))
+ return true; // ignore and continue
+
+ // Ignore if not in the selected depth-range.
+ Collider2D* collider = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ if (!CheckColliderDepth (collider, m_MinDepth, m_MaxDepth))
+ return true; // ignore and continue;
+
+ // Ignore if we've already selected this collider and it has a higher depth.
+ for (size_t i = 0; i != m_Hits.size (); ++i)
+ {
+ if (m_Hits[i] == collider)
+ {
+ if (m_CompareDepth.CompareDepth (m_Hits[i], collider) == 1)
+ m_Hits[i] = collider;
+ return true;
+ }
+ }
+
+ // Test polygon against fixture.
+ if ( !b2TestOverlap( &m_PolygonShape, 0, fixture->GetShape (), 0, m_QueryTransform, fixture->GetBody ()->GetTransform () ) )
+ return true;
+
+ // Add new hit
+ m_Hits.push_back (collider);
+
+ return true;
+ }
+
+private:
+ int m_LayerMask;
+ float m_MinDepth;
+ float m_MaxDepth;
+ b2PolygonShape m_PolygonShape;
+ b2AABB m_PolygonAABB;
+ b2Transform m_QueryTransform;
+ dynamic_array<Collider2D*>& m_Hits;
+};
+
+
+// --------------------------------------------------------------------------
+
+
+struct Physics2DState
+{
+ Physics2DState() :
+ m_PhysicsManager(NULL),
+ m_PhysicsWorld(NULL),
+ m_PhysicsGroundBody(NULL) { }
+
+ void Initialize();
+ void Cleanup();
+
+ Physics2DManager* m_PhysicsManager;
+ b2World* m_PhysicsWorld;
+ b2Body* m_PhysicsGroundBody;
+ CollisionListener2D m_Collisions;
+ ContactFilter2D m_ContactFilter;
+};
+
+static Physics2DState g_Physics2DState;
+
+
+// --------------------------------------------------------------------------
+
+void Physics2DState::Initialize()
+{
+ Assert (m_PhysicsManager == NULL);
+ Assert (m_PhysicsWorld == NULL);
+
+ m_PhysicsManager = new Physics2DManager ();
+ SetIPhysics2D (m_PhysicsManager);
+
+ // Initialize the Box2D physics world.
+ b2Vec2 gravity(0.0f, -9.81f);
+ m_PhysicsWorld = new b2World (gravity);
+ m_PhysicsWorld->SetContactListener (&m_Collisions);
+ m_PhysicsWorld->SetContactFilter (&m_ContactFilter);
+
+ // Initialize the static ground-body.
+ b2BodyDef groundBodyDef;
+ m_PhysicsGroundBody = GetPhysics2DWorld()->CreateBody (&groundBodyDef);
+
+ // Register physics updates.
+ REGISTER_PLAYERLOOP_CALL (Physics2DFixedUpdate, GetPhysics2DManager().FixedUpdate());
+ REGISTER_PLAYERLOOP_CALL (Physics2DUpdate, GetPhysics2DManager().DynamicUpdate());
+ REGISTER_PLAYERLOOP_CALL (Physics2DResetInterpolatedTransformPosition, GetPhysics2DManager().ResetInterpolations());
+}
+
+
+void Physics2DState::Cleanup()
+{
+ delete m_PhysicsWorld;
+ m_PhysicsWorld = NULL;
+
+ delete m_PhysicsManager;
+ m_PhysicsManager = NULL;
+ SetIPhysics2D (NULL);
+}
+
+
+void InitializePhysics2DManager ()
+{
+ g_Physics2DState.Initialize ();
+}
+
+
+void CleanupPhysics2DManager ()
+{
+ g_Physics2DState.Cleanup ();
+}
+
+
+b2World* GetPhysics2DWorld ()
+{
+ Assert (g_Physics2DState.m_PhysicsWorld);
+ return g_Physics2DState.m_PhysicsWorld;
+}
+
+
+b2Body* GetPhysicsGroundBody ()
+{
+ Assert (g_Physics2DState.m_PhysicsGroundBody);
+ return g_Physics2DState.m_PhysicsGroundBody;
+}
+
+
+Physics2DManager& GetPhysics2DManager()
+{
+ Assert (g_Physics2DState.m_PhysicsManager);
+ return *g_Physics2DState.m_PhysicsManager;
+}
+
+
+// --------------------------------------------------------------------------
+
+
+Physics2DManager::Physics2DManager()
+ : m_RigidbodyTransformMessageEnabled(true)
+{
+ Object::FindAllDerivedClasses (ClassID (Collider2D), &m_AllCollider2DTypes);
+}
+
+
+void Physics2DManager::FixedUpdate()
+{
+ PROFILER_AUTO(gPhysics2DFixedUpdateProfile, NULL)
+
+ // Gather interpolation info.
+ {
+ PROFILER_AUTO(gPhysics2DInterpolationsProfile, NULL)
+
+ // Store interpolated position
+ for (InterpolatedBodiesIterator i=m_InterpolatedBodies.begin();i!=m_InterpolatedBodies.end();++i)
+ {
+ Rigidbody2D* rigidBody = i->body;
+ i->disabled = 0;
+
+ if ( rigidBody->GetBody() == NULL )
+ continue;
+
+ if (rigidBody->GetInterpolation() == kInterpolate2D)
+ {
+ i->position = rigidBody->GetBodyPosition();
+ i->rotation = rigidBody->GetBodyRotation();
+ }
+ }
+ }
+
+ // simulate
+ {
+ PROFILER_AUTO(gPhysics2DSimProfile, NULL)
+
+ const Physics2DSettings& settings = GetPhysics2DSettings();
+ g_Physics2DState.m_PhysicsWorld->Step (GetTimeManager().GetFixedDeltaTime(), settings.GetVelocityIterations(), settings.GetPositionIterations());
+ }
+
+ // update data back from simulation
+ {
+ PROFILER_AUTO(gPhysics2DUpdateTransformsProfile, NULL)
+
+ // Disable rigid body, collider & joint transform changed message handler.
+ // Turns off setting the pose while we are fetching the state.
+ SetTransformMessageEnabled (false);
+
+ // Update position / rotation of all rigid bodies
+ b2Body* body = GetPhysics2DWorld()->GetBodyList();
+ while (body != NULL)
+ {
+ Rigidbody2D* rb = (Rigidbody2D*)body->GetUserData();
+ if (rb != NULL && body->GetType() != b2_staticBody && body->IsAwake())
+ {
+ GameObject& go = rb->GetGameObject();
+ Transform& transform = go.GetComponent (Transform);
+
+ // Calculate new position.
+ const b2Vec2& pos2 = body->GetPosition();
+ Vector3f pos3 = transform.GetPosition();
+ pos3.x = pos2.x;
+ pos3.y = pos2.y;
+
+ // Calculate new rotation.
+ Vector3f localEuler = QuaternionToEuler (transform.GetLocalRotation ());
+ localEuler.z = body->GetAngle ();
+
+ // Update position and rotation.
+ transform.SetPositionAndRotation (pos3, EulerToQuaternion(localEuler));
+ }
+
+ body = body->GetNext();
+ }
+
+ // Enable transform change notifications back.
+ SetTransformMessageEnabled (true);
+ }
+
+ // do script callbacks
+ {
+ PROFILER_AUTO(gPhysics2DCallbacksProfile, NULL)
+
+ const bool oldDisableDestruction = GetDisableImmediateDestruction();
+ SetDisableImmediateDestruction(true);
+
+ // report collisions and triggers
+ g_Physics2DState.m_Collisions.ReportCollisions();
+
+ SetDisableImmediateDestruction(oldDisableDestruction);
+ }
+}
+
+
+void Physics2DManager::DynamicUpdate()
+{
+ PROFILER_AUTO(gPhysics2DDynamicUpdateProfile, NULL);
+
+ {
+ PROFILER_AUTO(gPhysics2DInterpolationsProfile, NULL)
+
+ // Disable rigid body / collider transform changed message handler.
+ // Turns off setting the pose while we are fetching the state.
+ SetTransformMessageEnabled (false);
+
+ // Also disable rigidbody (2D) transform changed message, otherwise interpolation will affect physics results.
+ // This is not done in SetTransformMessageEnabled, as we need the rigidbody (2D) messages in the physics fixed update
+ // so kinematic child rigid-bodies (2D) are moved with their parents.
+ GameObject::GetMessageHandler ().SetMessageEnabled (ClassID(Rigidbody2D), kTransformChanged.messageID, false);
+
+ // Interpolation time is [0...1] between the two steps
+ // Extrapolation time the delta time since the last fixed step
+ const float dynamicTime = GetTimeManager().GetCurTime();
+ const float step = GetTimeManager().GetFixedDeltaTime();
+ const float fixedTime = GetTimeManager().GetFixedTime();
+
+ // Update interpolated position
+ for (InterpolatedBodiesIterator i=m_InterpolatedBodies.begin ();i!=m_InterpolatedBodies.end ();++i)
+ {
+ Rigidbody2D* rigidBody = i->body;
+ const RigidbodyInterpolation2D interpolation = rigidBody->GetInterpolation ();
+
+ // Ignore if disabled, no interpolation is specified or the body is sleeping.
+ if (i->disabled || interpolation == kNoInterpolation2D || rigidBody->IsSleeping ())
+ continue;
+
+ // Interpolate between this physics and last physics frame.
+ if (interpolation == kInterpolate2D)
+ {
+ const float interpolationTime = clamp01 ((dynamicTime - fixedTime) / step);
+
+ // Interpolate position.
+ const Vector3f position = Lerp (i->position, rigidBody->GetBodyPosition(), interpolationTime);
+
+ // Interpolate rotation.
+ const Quaternionf rotation = Slerp (i->rotation, rigidBody->GetBodyRotation (), interpolationTime);
+
+ // Update position/rotation.
+ rigidBody->GetComponent(Transform).SetPositionAndRotationSafe (position, rotation);
+ continue;
+ }
+
+ // Extrapolate current position using velocity.
+ else if (interpolation == kExtrapolate2D)
+ {
+ const float extrapolationTime = dynamicTime - fixedTime;
+
+ // Interpolate position.
+ const Vector2f linearVelocity2D = rigidBody->GetVelocity();
+ const Vector3f linearVelocity3D(linearVelocity2D.x * extrapolationTime, linearVelocity2D.y * extrapolationTime, 0.0f);
+ const Vector3f position = rigidBody->GetBodyPosition () + linearVelocity3D;
+
+ // Interpolate rotation.
+ const float angularVelocity2D = rigidBody->GetAngularVelocity ();
+ const Quaternionf rotation = CompareApproximately (angularVelocity2D, 0.0f) ? AngularVelocityToQuaternion(Vector3f(0.0f, 0.0f, angularVelocity2D), extrapolationTime) * rigidBody->GetBodyRotation() : rigidBody->GetBodyRotation();
+
+ // Update position/rotation.
+ rigidBody->GetComponent(Transform).SetPositionAndRotationSafe (position, rotation);
+ continue;
+ }
+ }
+
+ // Re-enable rigidbody (2D) transform changed messages.
+ GameObject::GetMessageHandler ().SetMessageEnabled (ClassID(Rigidbody2D), kTransformChanged.messageID, true);
+
+ // Enable transform change notifications back.
+ SetTransformMessageEnabled (true);
+ }
+}
+
+
+void Physics2DManager::ResetInterpolations ()
+{
+ PROFILER_AUTO(gPhysics2DInterpolationsProfile, NULL)
+
+ for (InterpolatedBodiesIterator i=m_InterpolatedBodies.begin ();i!=m_InterpolatedBodies.end ();++i)
+ {
+ Rigidbody2D* rigidBody = i->body;
+ if (rigidBody->GetBody() == NULL || rigidBody->IsSleeping ())
+ continue;
+
+ Transform& transform = rigidBody->GetComponent(Transform);
+
+ Vector3f pos = rigidBody->GetBodyPosition();
+ Quaternionf rot = rigidBody->GetBodyRotation();
+ transform.SetPositionAndRotationSafeWithoutNotification (pos, rot);
+ }
+}
+
+
+int Physics2DManager::Linecast (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, RaycastHit2D* outHits, const int outHitsSize)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gLinecast2DProfile, NULL)
+
+ // Finish if no available hits capacity.
+ if (outHitsSize == 0)
+ return 0;
+
+ dynamic_array<RaycastHit2D> raycastHits(kMemTempAlloc);
+ Raycast2DQuery query (pointA, pointB, layerMask, minDepth, maxDepth, raycastHits);
+ const int resultCount = query.RunQuery ();
+
+ // Transfer the first n-results.
+ const int allowedResultCount = std::min (resultCount, outHitsSize);
+ for (int index = 0; index < allowedResultCount; ++index)
+ *(outHits++) = raycastHits[index];
+
+ return allowedResultCount;
+}
+
+
+int Physics2DManager::LinecastAll (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<RaycastHit2D>* outHits)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gLinecastAll2DProfile, NULL)
+
+ Raycast2DQuery query (pointA, pointB, layerMask, minDepth, maxDepth, *outHits);
+ return query.RunQuery ();
+}
+
+
+int Physics2DManager::Raycast (const Vector2f& origin, const Vector2f& direction, const float distance, const int layerMask, const float minDepth, const float maxDepth, RaycastHit2D* outHits, const int outHitsSize)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gRaycast2DProfile, NULL)
+
+ // Finish if no available hits capacity.
+ if (outHitsSize == 0)
+ return 0;
+
+ // Calculate destination point.
+ const bool isInfiniteDistance = distance == std::numeric_limits<float>::infinity();
+ Vector2f normalizedDirection = NormalizeFast (direction);
+ const Vector2f pointB = origin + (normalizedDirection * (isInfiniteDistance ? PHYSICS_2D_RAYCAST_DISTANCE : distance));
+
+ dynamic_array<RaycastHit2D> raycastHits(kMemTempAlloc);
+ Raycast2DQuery query (origin, pointB, layerMask, minDepth, maxDepth, raycastHits);
+ const int resultCount = query.RunQuery ();
+
+ // Transfer the first n-results.
+ const int allowedResultCount = std::min (resultCount, outHitsSize);
+ for (int index = 0; index < allowedResultCount; ++index)
+ {
+ RaycastHit2D& hit = raycastHits[index];
+ if (isInfiniteDistance)
+ hit.fraction *= PHYSICS_2D_RAYCAST_DISTANCE;
+
+ *(outHits++) = hit;
+ }
+
+ return allowedResultCount;
+}
+
+
+int Physics2DManager::RaycastAll (const Vector2f& origin, const Vector2f& direction, const float distance, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<RaycastHit2D>* outHits)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gRaycastAll2DProfile, NULL)
+
+ // Calculate points.
+ const bool isInfiniteDistance = distance == std::numeric_limits<float>::infinity();
+ Vector2f normalizedDirection = NormalizeFast (direction);
+ const Vector2f pointB = origin + (normalizedDirection * (isInfiniteDistance ? PHYSICS_2D_RAYCAST_DISTANCE : distance));
+
+ Raycast2DQuery query (origin, pointB, layerMask, minDepth, maxDepth, *outHits);
+ const int resultCount = query.RunQuery ();
+
+ // Finish if not infinite distance.
+ if (!isInfiniteDistance || resultCount == 0)
+ return resultCount;
+
+ // Change fraction to distance.
+ for (dynamic_array<RaycastHit2D>::iterator hitItr = outHits->begin(); hitItr != outHits->end(); ++hitItr)
+ hitItr->fraction *= PHYSICS_2D_RAYCAST_DISTANCE;
+
+ return resultCount;
+}
+
+
+int Physics2DManager::GetRayIntersection(const Vector3f& origin, const Vector3f& direction, const float distance, const int layerMask, RaycastHit2D* outHits, const int outHitsSize)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gGetRayIntersection2DProfile, NULL)
+
+ // Finish if no available hits capacity.
+ if (outHitsSize == 0)
+ return 0;
+
+ dynamic_array<RaycastHit2D> raycastHits(kMemTempAlloc);
+ const int resultCount = GetRayIntersectionAll (origin, direction, distance, layerMask, &raycastHits);
+
+ // Transfer the first n-results.
+ const int allowedResultCount = std::min (resultCount, outHitsSize);
+ for (int index = 0; index < allowedResultCount; ++index)
+ *(outHits++) = raycastHits[index];
+
+ return allowedResultCount;
+}
+
+
+int Physics2DManager::GetRayIntersectionAll(const Vector3f& origin, const Vector3f& direction, const float distance, const int layerMask, dynamic_array<RaycastHit2D>* outHits)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gGetRayIntersectionAll2DProfile, NULL)
+
+ // Clear hits.
+ outHits->clear ();
+
+ // Set ray.
+ Ray ray;
+ ray.SetOrigin (origin);
+ ray.SetApproxDirection (direction);
+
+ // Calculate destination point.
+ const bool isInfiniteDistance = distance == std::numeric_limits<float>::infinity();
+ const float rayDistanceScale = isInfiniteDistance ? 1.0f : 1.0f / distance;
+ const Vector3f destination = ray.GetPoint (isInfiniteDistance ? PHYSICS_2D_RAYCAST_DISTANCE : distance);
+
+ // If the ray is parallel to the X/Y plane then no intersections are possible.
+ if (CompareApproximately (origin.z, destination.z))
+ return 0;
+
+ // Calculate 2D start/end points.
+ const Vector2f pointA = Vector2f(origin.x, origin.y);
+ const Vector2f pointB = Vector2f(destination.x, destination.y);
+
+ // Find all the colliders that hit somewhere along the ray.
+ // NOTE:- These will all be unique colliders (not duplicates).
+ dynamic_array<RaycastHit2D> potentialHits(kMemTempAlloc);
+ if (LinecastAll (pointA, pointB, layerMask, origin.z, destination.z, &potentialHits) == 0)
+ return 0;
+
+ // Sort the hits by depth.
+ const bool isForwardDepth = origin.z < direction.z;
+ if (isForwardDepth)
+ std::sort (potentialHits.begin(), potentialHits.end(), RayHitsByDepthComparitor());
+ else
+ std::sort (potentialHits.begin(), potentialHits.end(), RayHitsByInverseDepthComparitor());
+
+ // Calculate the plane normal.
+ const Vector3f planeNormal(0.0f, 0.0f, isForwardDepth ? 1.0f : -1.0f);
+
+ // Iterate all hits and check collider intersections.
+ for (dynamic_array<RaycastHit2D>::iterator hitItr = potentialHits.begin (); hitItr != potentialHits.end (); ++hitItr)
+ {
+ // Fetch the hit.
+ RaycastHit2D& hit = *hitItr;
+
+ // Fetch the collider depth.
+ const Collider2D* collider = hit.collider;
+ const float depth = collider->GetGameObject ().GetComponent (Transform).GetPosition ().z;
+
+ // Configure a collider plane.
+ Plane colliderPlane;
+ colliderPlane.SetNormalAndPosition (planeNormal, Vector3f(0.0f, 0.0f, depth));
+
+ // Test ray for intersection position.
+ float intersectionDistance;
+ if (!IntersectRayPlane (ray, colliderPlane, &intersectionDistance))
+ continue;
+
+ // Test if the intersection point overlaps the collider.
+ const Vector3f intersectionPoint3 = ray.GetPoint (intersectionDistance);
+ const Vector2f intersectionPoint2 = Vector2f(intersectionPoint3.x, intersectionPoint3.y);
+ if (!collider->OverlapPoint (intersectionPoint2))
+ continue;
+
+ // Update the hit with the 3D intersection details.
+ // NOTE: We leave the normal in 2D space as we can't use the plane normal.
+ hit.point = intersectionPoint2;
+ hit.fraction = intersectionDistance * rayDistanceScale;
+
+ // Add hit result.
+ outHits->push_back (hit);
+ }
+
+ return outHits->size ();
+}
+
+
+int Physics2DManager::OverlapPoint (const Vector2f& point, const int layerMask, const float minDepth, const float maxDepth, Collider2D** outHits, const int outHitsSize)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gOverlapPoint2DProfile, NULL)
+
+ dynamic_array<Collider2D*> colliderHits(kMemTempAlloc);
+ OverlapPointQuery2D query (point, layerMask, minDepth, maxDepth, colliderHits);
+ const int resultCount = query.RunQuery ();
+
+ // Transfer the first n-results.
+ const int allowedResultCount = std::min (resultCount, outHitsSize);
+ for (int index = 0; index < allowedResultCount; ++index)
+ *(outHits++) = colliderHits[index];
+
+ return allowedResultCount;
+}
+
+
+int Physics2DManager::OverlapPointAll (const Vector2f& point, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>* outHits)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gOverlapPointAll2DProfile, NULL)
+
+ OverlapPointQuery2D query (point, layerMask, minDepth, maxDepth, *outHits);
+ return query.RunQuery ();
+}
+
+
+int Physics2DManager::OverlapCircle (const Vector2f& point, const float radius, const int layerMask, const float minDepth, const float maxDepth, Collider2D** outHits, const int outHitsSize)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gOverlapCircle2DProfile, NULL)
+
+ dynamic_array<Collider2D*> colliderHits(kMemTempAlloc);
+ OverlapCircleQuery2D query (point, radius, layerMask, minDepth, maxDepth, colliderHits);
+ const int resultCount = query.RunQuery ();
+
+ // Transfer the first n-results.
+ const int allowedResultCount = std::min (resultCount, outHitsSize);
+ for (int index = 0; index < allowedResultCount; ++index)
+ *(outHits++) = colliderHits[index];
+
+ return allowedResultCount;
+}
+
+
+int Physics2DManager::OverlapCircleAll (const Vector2f& point, const float radius, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>* outHits)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gOverlapCircleAll2DProfile, NULL)
+
+ OverlapCircleQuery2D query (point, radius, layerMask, minDepth, maxDepth, *outHits);
+ return query.RunQuery ();
+}
+
+
+int Physics2DManager::OverlapArea (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, Collider2D** outHits, const int outHitsSize)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gOverlapArea2DProfile, NULL)
+
+ dynamic_array<Collider2D*> colliderHits(kMemTempAlloc);
+ OverlapAreaQuery2D query (pointA, pointB, layerMask, minDepth, maxDepth, colliderHits);
+ const int resultCount = query.RunQuery ();
+
+ // Transfer the first n-results.
+ const int allowedResultCount = std::min (resultCount, outHitsSize);
+ for (int index = 0; index < allowedResultCount; ++index)
+ *(outHits++) = colliderHits[index];
+
+ return allowedResultCount;
+}
+
+
+int Physics2DManager::OverlapAreaAll (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>* outHits)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gOverlapAreaAll2DProfile, NULL)
+
+ OverlapAreaQuery2D query (pointA, pointB, layerMask, minDepth, maxDepth, *outHits);
+ return query.RunQuery ();
+}
+
+void Physics2DManager::InvalidateColliderCollisions (Collider2D* collider)
+{
+ g_Physics2DState.m_Collisions.InvalidateColliderCollisions (collider);
+}
+
+
+void Physics2DManager::DestroyColliderCollisions (Collider2D* collider)
+{
+ g_Physics2DState.m_Collisions.DestroyColliderCollisions (collider);
+}
+
+
+#if ENABLE_PROFILER
+void Physics2DManager::GetProfilerStats (Physics2DStats& stats)
+{
+ // Fetch the physics world.
+ b2World* world = g_Physics2DState.m_PhysicsWorld;
+
+ // Cannot populate stats without a world.
+ if (world == NULL)
+ return;
+
+ // Calculate body metrics.
+ int dynamicBodyCount = 0;
+ int kinematicBodyCount = 0;
+ int activeBodyCount = 0;
+ int sleepingBodyCount = 0;
+ int discreteBodyCount = 0;
+ int continuousBodyCount = 0;
+ int activeColliderShapesCount = 0;
+ int sleepingColliderShapesCount = 0;
+
+ for (b2Body* body = world->GetBodyList (); body; body = body->GetNext ())
+ {
+ // Check body type.
+ const b2BodyType bodyType = body->GetType ();
+ if (bodyType == b2_staticBody)
+ continue;
+ else if (bodyType == b2_dynamicBody)
+ dynamicBodyCount++;
+ else if (bodyType == b2_kinematicBody)
+ kinematicBodyCount++;
+
+ // Check sleep state.
+ if (body->IsAwake ())
+ {
+ activeBodyCount++;
+ activeColliderShapesCount += body->GetFixtureCount ();
+ }
+ else
+ {
+ sleepingBodyCount++;
+ sleepingColliderShapesCount += body->GetFixtureCount ();
+ }
+
+ // Check CCD state.
+ if (body->IsBullet ())
+ continuousBodyCount++;
+ else
+ discreteBodyCount++;
+ }
+
+ // Populate profile counts.
+ stats.m_TotalBodyCount = world->GetBodyCount () - 1; // Ignore the hidden static ground-body.
+ stats.m_ActiveBodyCount = activeBodyCount;
+ stats.m_SleepingBodyCount = sleepingBodyCount;
+ stats.m_DynamicBodyCount = dynamicBodyCount;
+ stats.m_KinematicBodyCount = kinematicBodyCount;
+ stats.m_DiscreteBodyCount = discreteBodyCount;
+ stats.m_ContinuousBodyCount = continuousBodyCount;
+ stats.m_ActiveColliderShapesCount = activeColliderShapesCount;
+ stats.m_SleepingColliderShapesCount = sleepingColliderShapesCount;
+ stats.m_JointCount = world->GetJointCount ();
+ stats.m_ContactCount = world->GetContactCount ();
+
+ // Populate profile times.
+ const b2Profile& timeProfile = world->GetProfile ();
+ const float millisecondUpscale = 1000000.0f;
+ stats.m_StepTime = (int)(timeProfile.step * millisecondUpscale);
+ stats.m_CollideTime = (int)(timeProfile.collide * millisecondUpscale);
+ stats.m_SolveTime = (int)(timeProfile.solve * millisecondUpscale);
+ stats.m_SolveInitialization = (int)(timeProfile.solveInit * millisecondUpscale);
+ stats.m_SolveVelocity = (int)(timeProfile.solveVelocity * millisecondUpscale);
+ stats.m_SolvePosition = (int)(timeProfile.solvePosition * millisecondUpscale);
+ stats.m_SolveBroadphase = (int)(timeProfile.broadphase * millisecondUpscale);
+ stats.m_SolveTimeOfImpact = (int)(timeProfile.solveTOI * millisecondUpscale);
+}
+
+#endif
+
+// --------------------------------------------------------------------------
+
+
+void Physics2DManager::SetTransformMessageEnabled (const bool enable)
+{
+ for (size_t i = 0, n = m_AllCollider2DTypes.size(); i < n; ++i)
+ GameObject::GetMessageHandler ().SetMessageEnabled (m_AllCollider2DTypes[i], kTransformChanged.messageID, enable);
+
+ m_RigidbodyTransformMessageEnabled = enable;
+}
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/Physics2DManager.h b/Runtime/Physics2D/Physics2DManager.h
new file mode 100644
index 0000000..cef3917
--- /dev/null
+++ b/Runtime/Physics2D/Physics2DManager.h
@@ -0,0 +1,113 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Interfaces/IPhysics2D.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/LinkedList.h"
+
+class Rigidbody2D;
+class Collider2D;
+class b2World;
+class b2Body;
+class Ray;
+struct Physics2DStats;
+
+#define PHYSICS_2D_RAYCAST_DISTANCE (1e+5f)
+#define PHYSICS_2D_LARGE_RANGE_CLAMP (1e+6f)
+#define PHYSICS_2D_SMALL_RANGE_CLAMP (0.0001f)
+
+// --------------------------------------------------------------------------
+
+
+struct Rigidbody2DInterpolationInfo : public ListElement
+{
+ Vector3f position;
+ Quaternionf rotation;
+ Rigidbody2D* body;
+ int disabled;
+};
+
+
+// --------------------------------------------------------------------------
+
+
+struct RaycastHit2D
+{
+ Vector2f point;
+ Vector2f normal;
+ float fraction;
+ Collider2D* collider;
+};
+
+// --------------------------------------------------------------------------
+
+
+class Physics2DManager : public IPhysics2D
+{
+private:
+ typedef List<Rigidbody2DInterpolationInfo> InterpolatedBodiesList;
+ typedef InterpolatedBodiesList::iterator InterpolatedBodiesIterator;
+
+public:
+ Physics2DManager();
+ // ~Physics2DManager() // declared-by-macro
+
+ // IPhysics2D interface
+ virtual void FixedUpdate ();
+ virtual void DynamicUpdate ();
+ virtual void ResetInterpolations ();
+
+ // 2D line-casts.
+ int Linecast (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, RaycastHit2D* outHits, const int outHitsSize);
+ int LinecastAll (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<RaycastHit2D>* outHits);
+
+ // 2D ray-casts.
+ int Raycast (const Vector2f& origin, const Vector2f& direction, const float distance, const int layerMask, const float minDepth, const float maxDepth, RaycastHit2D* outHits, const int outHitsSize);
+ int RaycastAll (const Vector2f& origin, const Vector2f& direction, const float distance, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<RaycastHit2D>* outHits);
+
+ // 3D ray-intersections.
+ int GetRayIntersection(const Vector3f& origin, const Vector3f& direction, const float distance, const int layerMask, RaycastHit2D* outHits, const int outHitsSize);
+ int GetRayIntersectionAll(const Vector3f& origin, const Vector3f& direction, const float distance, const int layerMask, dynamic_array<RaycastHit2D>* outHits);
+
+ // 2D geometry overlaps.
+ int OverlapPoint (const Vector2f& point, const int layerMask, const float minDepth, const float maxDepth, Collider2D** outHits, const int outHitsSize);
+ int OverlapPointAll (const Vector2f& point, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>* outHits);
+ int OverlapCircle (const Vector2f& point, const float radius, const int layerMask, const float minDepth, const float maxDepth, Collider2D** outHits, const int outHitsSize);
+ int OverlapCircleAll (const Vector2f& point, const float radius, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>* outHits);
+ int OverlapArea (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, Collider2D** outHits, const int outHitsSize);
+ int OverlapAreaAll (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>* outHits);
+
+ void InvalidateColliderCollisions (Collider2D* collider);
+ void DestroyColliderCollisions (Collider2D* collider);
+
+ inline bool IsTransformMessageEnabled() const { return m_RigidbodyTransformMessageEnabled; }
+ inline List<Rigidbody2DInterpolationInfo>& GetInterpolatedBodies() { return m_InterpolatedBodies; }
+
+#if ENABLE_PROFILER
+ virtual void GetProfilerStats (Physics2DStats& stats);
+#endif
+
+private:
+ void SetTransformMessageEnabled (const bool enable);
+
+private:
+ std::vector<SInt32> m_AllCollider2DTypes;
+ bool m_RigidbodyTransformMessageEnabled;
+ InterpolatedBodiesList m_InterpolatedBodies;
+};
+
+
+// --------------------------------------------------------------------------
+
+
+void InitializePhysics2DManager ();
+void CleanupPhysics2DManager ();
+b2World* GetPhysics2DWorld ();
+b2Body* GetPhysicsGroundBody ();
+Physics2DManager& GetPhysics2DManager ();
+
+#endif
diff --git a/Runtime/Physics2D/Physics2DMaterial.cpp b/Runtime/Physics2D/Physics2DMaterial.cpp
new file mode 100644
index 0000000..ff47e78
--- /dev/null
+++ b/Runtime/Physics2D/Physics2DMaterial.cpp
@@ -0,0 +1,65 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+#include "Runtime/Physics2D/Physics2DMaterial.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+IMPLEMENT_CLASS (PhysicsMaterial2D)
+IMPLEMENT_OBJECT_SERIALIZE (PhysicsMaterial2D)
+
+
+// --------------------------------------------------------------------------
+
+
+PhysicsMaterial2D::PhysicsMaterial2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+
+PhysicsMaterial2D::~PhysicsMaterial2D ()
+{
+}
+
+
+void PhysicsMaterial2D::Reset ()
+{
+ Super::Reset();
+ m_Friction = 0.4f;
+ m_Bounciness = 0.0f;
+}
+
+
+void PhysicsMaterial2D::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_Friction = clamp(m_Friction, 0.0f, 100000.0f);
+ m_Bounciness = clamp(m_Bounciness, 0.0f, 1.0f);
+}
+
+
+template<class TransferFunction>
+void PhysicsMaterial2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ transfer.Transfer (m_Friction, "friction", kSimpleEditorMask);
+ transfer.Transfer (m_Bounciness, "bounciness", kSimpleEditorMask);
+}
+
+
+void PhysicsMaterial2D::SetFriction (float friction)
+{
+ m_Friction = clamp (friction, 0.0f, 100000.0f);
+ SetDirty ();
+}
+
+
+void PhysicsMaterial2D::SetBounciness (float bounce)
+{
+ m_Bounciness = clamp (bounce, 0.0f, 1.0f);
+ SetDirty ();
+}
+
+#endif //ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/Physics2DMaterial.h b/Runtime/Physics2D/Physics2DMaterial.h
new file mode 100644
index 0000000..0130b3b
--- /dev/null
+++ b/Runtime/Physics2D/Physics2DMaterial.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/BaseClasses/NamedObject.h"
+
+
+// --------------------------------------------------------------------------
+
+
+class PhysicsMaterial2D : public NamedObject
+{
+public:
+ REGISTER_DERIVED_CLASS (PhysicsMaterial2D, NamedObject)
+ DECLARE_OBJECT_SERIALIZE (PhysicsMaterial2D)
+
+ PhysicsMaterial2D (MemLabelId label, ObjectCreationMode mode);
+ // ~PhysicsMaterial2D (); declared-by-macro
+
+ virtual void Reset ();
+ virtual void CheckConsistency ();
+
+ float GetFriction () const { return m_Friction; }
+ void SetFriction (float friction);
+
+ float GetBounciness () const { return m_Bounciness; }
+ void SetBounciness (float bounce);
+
+private:
+ float m_Friction; ///< Friction. Range { 0.0, 100000.0 }
+ float m_Bounciness; ///< Bounciness. Range { 0.0, 1.0 }
+
+ PPtr<Object> m_Owner;
+};
+
+#endif //ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/Physics2DModule.jam b/Runtime/Physics2D/Physics2DModule.jam
new file mode 100644
index 0000000..85ef5aa
--- /dev/null
+++ b/Runtime/Physics2D/Physics2DModule.jam
@@ -0,0 +1,178 @@
+rule Physics2DModule_ReportCpp
+{
+ local physics2DSources =
+
+ Physics2DModule.jam
+
+ Collider2D.cpp
+ Collider2D.h
+ BoxCollider2D.cpp
+ BoxCollider2D.h
+ CircleCollider2D.cpp
+ CircleCollider2D.h
+ EdgeCollider2D.cpp
+ EdgeCollider2D.h
+ PolygonColliderBase2D.cpp
+ PolygonColliderBase2D.h
+ PolygonCollider2D.cpp
+ PolygonCollider2D.h
+ SpriteCollider2D.cpp
+ SpriteCollider2D.h
+ CollisionListener2D.cpp
+ CollisionListener2D.h
+ DistanceJoint2D.cpp
+ DistanceJoint2D.h
+ HingeJoint2D.cpp
+ HingeJoint2D.h
+ Joint2D.cpp
+ Joint2D.h
+ Physics2DManager.cpp
+ Physics2DManager.h
+ Physics2DMaterial.cpp
+ Physics2DMaterial.h
+ Physics2DModuleRegistration.cpp
+ Physics2DSettings.cpp
+ Physics2DSettings.h
+ Rigidbody2D.cpp
+ Rigidbody2D.h
+ JointDescriptions2D.h
+ SliderJoint2D.cpp
+ SliderJoint2D.h
+ SpringJoint2D.cpp
+ SpringJoint2D.h
+ ;
+
+ local box2DSources =
+
+ Box2D.h
+ Collision/b2BroadPhase.cpp
+ Collision/b2BroadPhase.h
+ Collision/b2CollideCircle.cpp
+ Collision/b2CollideEdge.cpp
+ Collision/b2CollidePolygon.cpp
+ Collision/b2Collision.cpp
+ Collision/b2Collision.h
+ Collision/b2Distance.cpp
+ Collision/b2Distance.h
+ Collision/b2DynamicTree.cpp
+ Collision/b2DynamicTree.h
+ Collision/b2TimeOfImpact.cpp
+ Collision/b2TimeOfImpact.h
+
+ Collision/Shapes/b2ChainShape.cpp
+ Collision/Shapes/b2ChainShape.h
+ Collision/Shapes/b2CircleShape.cpp
+ Collision/Shapes/b2CircleShape.h
+ Collision/Shapes/b2EdgeShape.cpp
+ Collision/Shapes/b2EdgeShape.h
+ Collision/Shapes/b2PolygonShape.cpp
+ Collision/Shapes/b2PolygonShape.h
+ Collision/Shapes/b2Shape.h
+
+ Common/b2BlockAllocator.cpp
+ Common/b2BlockAllocator.h
+ Common/b2Draw.cpp
+ Common/b2Draw.h
+ Common/b2GrowableStack.h
+ Common/b2Math.cpp
+ Common/b2Math.h
+ Common/b2Settings.cpp
+ Common/b2Settings.h
+ Common/b2StackAllocator.cpp
+ Common/b2StackAllocator.h
+ Common/b2Timer.cpp
+ Common/b2Timer.h
+
+ Dynamics/b2Body.cpp
+ Dynamics/b2Body.h
+ Dynamics/b2ContactManager.cpp
+ Dynamics/b2ContactManager.h
+ Dynamics/b2Fixture.cpp
+ Dynamics/b2Fixture.h
+ Dynamics/b2Island.cpp
+ Dynamics/b2Island.h
+ Dynamics/b2TimeStep.h
+ Dynamics/b2World.cpp
+ Dynamics/b2World.h
+ Dynamics/b2WorldCallbacks.cpp
+ Dynamics/b2WorldCallbacks.h
+
+ Dynamics/Contacts/b2ChainAndCircleContact.cpp
+ Dynamics/Contacts/b2ChainAndCircleContact.h
+ Dynamics/Contacts/b2ChainAndPolygonContact.cpp
+ Dynamics/Contacts/b2ChainAndPolygonContact.h
+ Dynamics/Contacts/b2CircleContact.cpp
+ Dynamics/Contacts/b2CircleContact.h
+ Dynamics/Contacts/b2Contact.cpp
+ Dynamics/Contacts/b2Contact.h
+ Dynamics/Contacts/b2ContactSolver.cpp
+ Dynamics/Contacts/b2ContactSolver.h
+ Dynamics/Contacts/b2EdgeAndCircleContact.cpp
+ Dynamics/Contacts/b2EdgeAndCircleContact.h
+ Dynamics/Contacts/b2EdgeAndPolygonContact.cpp
+ Dynamics/Contacts/b2EdgeAndPolygonContact.h
+ Dynamics/Contacts/b2PolygonAndCircleContact.cpp
+ Dynamics/Contacts/b2PolygonAndCircleContact.h
+ Dynamics/Contacts/b2PolygonContact.cpp
+ Dynamics/Contacts/b2PolygonContact.h
+
+ Dynamics/Joints/b2DistanceJoint.cpp
+ Dynamics/Joints/b2DistanceJoint.h
+ Dynamics/Joints/b2FrictionJoint.cpp
+ Dynamics/Joints/b2FrictionJoint.h
+ Dynamics/Joints/b2GearJoint.cpp
+ Dynamics/Joints/b2GearJoint.h
+ Dynamics/Joints/b2Joint.cpp
+ Dynamics/Joints/b2Joint.h
+ Dynamics/Joints/b2MotorJoint.cpp
+ Dynamics/Joints/b2MotorJoint.h
+ Dynamics/Joints/b2MouseJoint.cpp
+ Dynamics/Joints/b2MouseJoint.h
+ Dynamics/Joints/b2PrismaticJoint.cpp
+ Dynamics/Joints/b2PrismaticJoint.h
+ Dynamics/Joints/b2PulleyJoint.cpp
+ Dynamics/Joints/b2PulleyJoint.h
+ Dynamics/Joints/b2RevoluteJoint.cpp
+ Dynamics/Joints/b2RevoluteJoint.h
+ Dynamics/Joints/b2RopeJoint.cpp
+ Dynamics/Joints/b2RopeJoint.h
+ Dynamics/Joints/b2WeldJoint.cpp
+ Dynamics/Joints/b2WeldJoint.h
+ Dynamics/Joints/b2WheelJoint.cpp
+ Dynamics/Joints/b2WheelJoint.h
+
+ Rope/b2Rope.cpp
+ Rope/b2Rope.h
+ ;
+
+ local modulesources =
+ Runtime/Physics2D/$(physics2DSources)
+ External/Box2D/Box2D/$(box2DSources)
+ ;
+
+ return $(modulesources) ;
+}
+
+rule Physics2DModule_ReportTxt
+{
+ return
+ Runtime/Physics2D/ScriptBindings/Physics2DBindings.txt
+ ;
+}
+
+rule Physics2DModule_ReportIncludes
+{
+ return
+ External/Box2D
+ Projects/PrecompiledHeaders
+ ;
+}
+
+rule Physics2DModule_Init
+{
+ OverrideModule Physics2D : GetModule_Cpp : byOverridingWithMethod : Physics2DModule_ReportCpp ;
+ OverrideModule Physics2D : GetModule_Txt : byOverridingWithMethod : Physics2DModule_ReportTxt ;
+ OverrideModule Physics2D : GetModule_Inc : byOverridingWithMethod : Physics2DModule_ReportIncludes ;
+}
+
+#RegisterModule Physics2D ;
diff --git a/Runtime/Physics2D/Physics2DModuleRegistration.cpp b/Runtime/Physics2D/Physics2DModuleRegistration.cpp
new file mode 100644
index 0000000..cd0b9b3
--- /dev/null
+++ b/Runtime/Physics2D/Physics2DModuleRegistration.cpp
@@ -0,0 +1,53 @@
+#include "UnityPrefix.h"
+#include "Runtime/BaseClasses/ClassRegistration.h"
+#include "Runtime/Modules/ModuleRegistration.h"
+
+#if ENABLE_2D_PHYSICS
+
+static void RegisterPhysics2DClasses (ClassRegistrationContext& context)
+{
+ REGISTER_CLASS (Physics2DSettings)
+ REGISTER_CLASS (Rigidbody2D)
+
+ REGISTER_CLASS (Collider2D)
+ REGISTER_CLASS (CircleCollider2D)
+ REGISTER_CLASS (PolygonCollider2D)
+ REGISTER_CLASS (PolygonColliderBase2D)
+ #if ENABLE_SPRITECOLLIDER
+ REGISTER_CLASS (SpriteCollider2D)
+ #endif
+ REGISTER_CLASS (BoxCollider2D)
+ REGISTER_CLASS (EdgeCollider2D)
+
+ REGISTER_CLASS (Joint2D)
+ REGISTER_CLASS (SpringJoint2D)
+ REGISTER_CLASS (DistanceJoint2D)
+ REGISTER_CLASS (HingeJoint2D)
+ REGISTER_CLASS (SliderJoint2D)
+
+ REGISTER_CLASS (PhysicsMaterial2D)
+}
+
+
+#if ENABLE_MONO || UNITY_WINRT
+void ExportPhysics2DBindings();
+
+static void RegisterPhysics2DICallModule ()
+{
+ #if !INTERNAL_CALL_STRIPPING
+ ExportPhysics2DBindings ();
+ #endif
+}
+#endif
+
+extern "C" EXPORT_MODULE void RegisterModule_Physics2D()
+{
+ ModuleRegistrationInfo info;
+ info.registerClassesCallback = &RegisterPhysics2DClasses;
+ #if ENABLE_MONO || UNITY_WINRT
+ info.registerIcallsCallback = &RegisterPhysics2DICallModule;
+ #endif
+ RegisterModuleInfo(info);
+}
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/Physics2DSettings.cpp b/Runtime/Physics2D/Physics2DSettings.cpp
new file mode 100644
index 0000000..9dd61d7
--- /dev/null
+++ b/Runtime/Physics2D/Physics2DSettings.cpp
@@ -0,0 +1,167 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+#include "Runtime/Physics2D/Physics2DSettings.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+
+#include "Runtime/BaseClasses/Tags.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+
+
+// --------------------------------------------------------------------------
+
+
+Physics2DSettings::Physics2DSettings (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_VelocityIterations = 8;
+ m_PositionIterations = 3;
+ m_Gravity = Vector2f (0, -9.81f);
+ m_RaycastsHitTriggers = true;
+ m_LayerCollisionMatrix.resize_initialized (kNumLayers, 0xffffffff);
+}
+
+
+Physics2DSettings::~Physics2DSettings ()
+{
+}
+
+
+void Physics2DSettings::InitializeClass ()
+{
+ InitializePhysics2DManager ();
+}
+
+
+void Physics2DSettings::CleanupClass ()
+{
+ CleanupPhysics2DManager ();
+}
+
+
+void Physics2DSettings::Reset ()
+{
+ Super::Reset();
+ m_VelocityIterations = 8;
+ m_PositionIterations = 3;
+ m_Gravity = Vector2f (0, -9.81F);
+ m_LayerCollisionMatrix.resize_initialized (kNumLayers, 0xffffffff);
+}
+
+
+void Physics2DSettings::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ SetGravity (m_Gravity);
+}
+
+
+void Physics2DSettings::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_VelocityIterations = std::max (1, m_VelocityIterations);
+ m_PositionIterations = std::max (1, m_PositionIterations);
+}
+
+
+template<class TransferFunction>
+void Physics2DSettings::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER (m_Gravity);
+ TRANSFER (m_DefaultMaterial);
+ TRANSFER (m_VelocityIterations);
+ TRANSFER (m_PositionIterations);
+ TRANSFER (m_RaycastsHitTriggers);
+ transfer.Align ();
+ transfer.Transfer (m_LayerCollisionMatrix, "m_LayerCollisionMatrix", kHideInEditorMask);
+}
+
+
+void Physics2DSettings::SetGravity (const Vector2f& value)
+{
+ m_Gravity = value;
+ SetDirty ();
+
+ GetPhysics2DWorld()->SetGravity(b2Vec2(m_Gravity.x, m_Gravity.y));
+
+ if (m_Gravity == Vector2f::zero)
+ return;
+
+ // Wake all dynamic bodies that have non-zero gravity-scale.
+ for (b2Body* body = GetPhysics2DWorld()->GetBodyList (); body != NULL; body = body->GetNext ())
+ {
+ if (body->GetType () == b2_dynamicBody && body->GetGravityScale () != 0.0f)
+ body->SetAwake (true);
+ }
+}
+
+
+void Physics2DSettings::SetVelocityIterations (const int velocityIterations)
+{
+ if ( velocityIterations == m_VelocityIterations )
+ return;
+
+ m_VelocityIterations = std::max (1,velocityIterations);
+ SetDirty ();
+}
+
+
+void Physics2DSettings::SetPositionIterations (const int positionIterations)
+{
+ if ( positionIterations == m_PositionIterations )
+ return;
+
+ m_PositionIterations = std::max (1,positionIterations);
+ SetDirty ();
+}
+
+
+void Physics2DSettings::IgnoreCollision(int layer1, int layer2, bool ignore)
+{
+ if (layer1 >= kNumLayers || layer2 >= kNumLayers)
+ {
+ ErrorString(Format("layer numbers must be between 0 and %d", kNumLayers));
+ return;
+ }
+
+ Assert (kNumLayers <= m_LayerCollisionMatrix.size());
+ Assert (kNumLayers <= sizeof(m_LayerCollisionMatrix[0])*8);
+
+ if (ignore)
+ {
+ m_LayerCollisionMatrix[layer1] &= ~(1<<layer2);
+ m_LayerCollisionMatrix[layer2] &= ~(1<<layer1);
+ }
+ else
+ {
+ m_LayerCollisionMatrix[layer1] |= 1<<layer2;
+ m_LayerCollisionMatrix[layer2] |= 1<<layer1;
+ }
+ SetDirty();
+}
+
+
+bool Physics2DSettings::GetIgnoreCollision(int layer1, int layer2) const
+{
+ if (layer1 >= kNumLayers || layer2 >= kNumLayers)
+ {
+ ErrorString(Format("layer numbers must be between 0 and %d", kNumLayers));
+ return false;
+ }
+
+ bool collides = m_LayerCollisionMatrix[layer1] & (1<<layer2);
+ return !collides;
+}
+
+
+GET_MANAGER (Physics2DSettings)
+GET_MANAGER_PTR (Physics2DSettings)
+IMPLEMENT_CLASS_HAS_INIT (Physics2DSettings)
+IMPLEMENT_OBJECT_SERIALIZE (Physics2DSettings)
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/Physics2DSettings.h b/Runtime/Physics2D/Physics2DSettings.h
new file mode 100644
index 0000000..8a8b3ed
--- /dev/null
+++ b/Runtime/Physics2D/Physics2DSettings.h
@@ -0,0 +1,62 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Physics2D/Physics2DMaterial.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+
+// --------------------------------------------------------------------------
+
+
+class Physics2DSettings : public GlobalGameManager
+{
+public:
+ Physics2DSettings (MemLabelId label, ObjectCreationMode mode);
+ // ~Physics2DSettings (); declared-by-macro
+
+ REGISTER_DERIVED_CLASS (Physics2DSettings, GlobalGameManager)
+ DECLARE_OBJECT_SERIALIZE (Physics2DSettings)
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+ virtual void CheckConsistency ();
+ virtual void Reset ();
+
+ const Vector2f& GetGravity () const { return m_Gravity; }
+ void SetGravity (const Vector2f& value);
+
+ int GetVelocityIterations () const { return m_VelocityIterations; }
+ void SetVelocityIterations (const int velocityIterations);
+
+ int GetPositionIterations () const { return m_PositionIterations; }
+ void SetPositionIterations (const int positionIterations);
+
+ inline bool GetRaycastsHitTriggers () const { return m_RaycastsHitTriggers; }
+ inline void SetRaycastsHitTriggers (const bool raycastsHitTriggers) { m_RaycastsHitTriggers = raycastsHitTriggers; }
+
+ void IgnoreCollision (int layer1, int layer2, bool ignore);
+ bool GetIgnoreCollision(int layer1, int layer2) const;
+ UInt32 GetLayerCollisionMask(int layer) const { return m_LayerCollisionMatrix[layer]; }
+
+ PhysicsMaterial2D* GetDefaultPhysicsMaterial () { return m_DefaultMaterial; }
+
+private:
+ Vector2f m_Gravity; ///< The gravity applied to all rigid bodies in the scene.
+ PPtr<PhysicsMaterial2D> m_DefaultMaterial; ///< The default material to use on a collider if no material is specified on it.
+ int m_VelocityIterations; ///< The number of iterations used to solve simulation velocities. More iterations yield a better simulation but is more expensive. (Default 8) range { 1 , infinity }
+ int m_PositionIterations; ///< The number of iterations used to solve simulation positions. More iterations yield a better simulation but is more expensive. (Default 3) range { 1 , infinity }
+ bool m_RaycastsHitTriggers; ///< Whether ray/line casts hit triggers or not.
+
+ dynamic_array<UInt32> m_LayerCollisionMatrix;
+
+};
+
+Physics2DSettings& GetPhysics2DSettings ();
+Physics2DSettings* GetPhysics2DSettingsPtr ();
+
+#endif
diff --git a/Runtime/Physics2D/PolygonCollider2D.cpp b/Runtime/Physics2D/PolygonCollider2D.cpp
new file mode 100644
index 0000000..da41b0d
--- /dev/null
+++ b/Runtime/Physics2D/PolygonCollider2D.cpp
@@ -0,0 +1,134 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+
+#include "Runtime/Physics2D/PolygonCollider2D.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Math/FloatConversion.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Filters/AABBUtility.h"
+#if ENABLE_SPRITES
+#include "Runtime/Graphics/SpriteFrame.h"
+#include "Runtime/Filters/Mesh/SpriteRenderer.h"
+#endif
+
+IMPLEMENT_CLASS (PolygonCollider2D)
+IMPLEMENT_OBJECT_SERIALIZE (PolygonCollider2D)
+
+
+// --------------------------------------------------------------------------
+
+
+PolygonCollider2D::PolygonCollider2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+
+PolygonCollider2D::~PolygonCollider2D ()
+{
+}
+
+
+template<class TransferFunction>
+void PolygonCollider2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER (m_Poly);
+}
+
+void PolygonCollider2D::Reset ()
+{
+ Super::Reset ();
+
+ // Create pentagon shape
+ CreateNgon (5, Vector2f(1, 1), Vector2f(0,0), m_Poly);
+}
+
+
+void PolygonCollider2D::SmartReset ()
+{
+ float radius;
+ Vector2f offset;
+
+ GameObject* go = GetGameObjectPtr();
+#if ENABLE_SPRITES
+ if (go)
+ {
+ SpriteRenderer* sr = go->QueryComponentT<SpriteRenderer>(ClassID(SpriteRenderer));
+ if (sr)
+ {
+ Sprite* sprite = sr->GetSprite();
+ if (sprite)
+ {
+ m_Poly.GenerateFrom(sprite, Vector2f(0, 0), 0.25f, 200, true);
+ if (m_Poly.GetPathCount() > 0) // We might fail if all pixels are under the threshold. No workaround in 4.3.
+ return;
+ }
+ }
+ }
+#endif
+
+ // Resolve what size collider we should have from object bounds
+ AABB aabb;
+ if (go && CalculateLocalAABB (GetGameObject (), &aabb))
+ {
+ Vector3f dist = aabb.GetExtent ();
+ radius = std::max(dist.x, dist.y);
+ if (radius <= 0.0f)
+ radius = 1.0f;
+ offset.x = aabb.GetCenter().x;
+ offset.y = aabb.GetCenter().y;
+ }
+ else
+ {
+ radius = 1.0f;
+ offset = Vector2f::zero;
+ }
+
+ // Create pentagon shape
+ CreateNgon (5, Vector2f(radius, radius), offset, m_Poly);
+}
+
+
+void PolygonCollider2D::RefreshPoly()
+{
+ Create();
+ SetDirty();
+}
+
+
+void PolygonCollider2D::CreatePrimitive (int sides, Vector2f scale, Vector2f offset)
+{
+ Assert (sides > 2);
+ Assert (scale.x > 0.0f);
+ Assert (scale.y > 0.0f);
+
+ CreateNgon (sides, scale, offset, m_Poly);
+
+ // Create polygon shape.
+ Create();
+ SetDirty();
+}
+
+
+void PolygonCollider2D::CreateNgon (const int sides, const Vector2f scale, const Vector2f offset, Polygon2D& polygon2D)
+{
+ Polygon2D::TPath path;
+
+ // Generate regular n-sided polygon.
+ const float tau = kPI * 2.0f;
+ const float chordAngle = tau / (float)sides;
+ float angle = 0.0f;
+ for (int chord = 0; chord < sides; ++chord, angle += chordAngle)
+ {
+ path.push_back (Vector2f(offset.x + scale.x * Sin(angle), offset.y + scale.y * Cos(angle)));
+ }
+
+ // Set polygon path.
+ polygon2D.SetPathCount (1);
+ polygon2D.SetPath (0, path);
+}
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/PolygonCollider2D.h b/Runtime/Physics2D/PolygonCollider2D.h
new file mode 100644
index 0000000..6a5ed55
--- /dev/null
+++ b/Runtime/Physics2D/PolygonCollider2D.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Runtime/Physics2D/PolygonColliderBase2D.h"
+
+
+// --------------------------------------------------------------------------
+
+
+class PolygonCollider2D : public PolygonColliderBase2D
+{
+public:
+ REGISTER_DERIVED_CLASS (PolygonCollider2D, PolygonColliderBase2D)
+ DECLARE_OBJECT_SERIALIZE (PolygonCollider2D)
+
+ PolygonCollider2D (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void Reset ();
+ virtual void SmartReset ();
+
+ virtual const Polygon2D& GetPoly() const { return m_Poly; }
+ Polygon2D& GetPoly() { return m_Poly; }
+ void RefreshPoly();
+
+ void CreatePrimitive (int sides, Vector2f scale = Vector2f(1.0f, 1.0f), Vector2f offset = Vector2f::zero);
+ static void CreateNgon (const int sides, const Vector2f scale, const Vector2f offset, Polygon2D& polygon2D);
+
+private:
+ Polygon2D m_Poly;
+};
+
+#endif
diff --git a/Runtime/Physics2D/PolygonColliderBase2D.cpp b/Runtime/Physics2D/PolygonColliderBase2D.cpp
new file mode 100644
index 0000000..1dfcb80
--- /dev/null
+++ b/Runtime/Physics2D/PolygonColliderBase2D.cpp
@@ -0,0 +1,336 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+
+#include "Runtime/Physics2D/PolygonColliderBase2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Graphics/SpriteFrame.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Profiler/Profiler.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+#include "External/libtess2/libtess2/tesselator.h"
+
+PROFILER_INFORMATION(gPhysics2DProfilePolygonColliderBaseCreate, "Physics2D.PolygonColliderCreate", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfilePolygonColliderBaseDecomposition, "Physics2D.PolygonColliderDecomposition", kProfilerPhysics)
+
+IMPLEMENT_CLASS (PolygonColliderBase2D)
+
+
+// --------------------------------------------------------------------------
+
+
+PolygonColliderBase2D::PolygonColliderBase2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+
+PolygonColliderBase2D::~PolygonColliderBase2D ()
+{
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void PolygonColliderBase2D::Create (const Rigidbody2D* ignoreRigidbody)
+{
+ PROFILER_AUTO(gPhysics2DProfilePolygonColliderBaseCreate, NULL);
+
+ // Ensure we're cleaned-up.
+ Cleanup ();
+
+ // Ignore if not active.
+ if (!IsActive())
+ return;
+
+ const Polygon2D& poly = GetPoly();
+ if (poly.IsEmpty())
+ return;
+
+ const int pathCount = poly.GetPathCount();
+ if (pathCount == 0)
+ return;
+
+ // Calculate collider transformation.
+ Matrix4x4f relativeTransform;
+ b2Body* body;
+ CalculateColliderTransformation (ignoreRigidbody, &body, relativeTransform);
+
+ {
+ PROFILER_AUTO(gPhysics2DProfilePolygonColliderBaseDecomposition, NULL);
+
+ // Extract the convex shapes from the path(s).
+ dynamic_array<b2Shape*> shapePtr;
+ b2Shape* shapeMem = ExtractConvexShapes(shapePtr, relativeTransform);
+
+ // Finish if no shapes generated.
+ if (shapeMem == NULL)
+ return;
+
+ b2FixtureDef def;
+ FinalizeCreate(def, body, &shapePtr);
+
+ FREE_TEMP_MANUAL(shapeMem);
+ }
+}
+
+
+b2Shape* PolygonColliderBase2D::ExtractConvexShapes(dynamic_array<b2Shape*>& shapes, const Matrix4x4f& relativeTransform )
+{
+ // Calculate the maximum number of vertices for the shape type.
+ const int kMaxPolygonVerts = b2_maxPolygonVertices;
+ const int kVertexSize = 2;
+ const Vector3f scale = GetComponent(Transform).GetWorldScaleLossy();
+
+ // Tessellation
+ TESStesselator* tess = tessNewTess (NULL);
+
+ // Add all paths as a tessellation contour.
+ const Polygon2D& poly = GetPoly();
+ const int pathCount = poly.GetPathCount();
+ int addedContours = 0;
+ for (int pathIndex = 0; pathIndex < pathCount; ++pathIndex)
+ {
+ // Fetch the path.
+ const Polygon2D::TPath& path = poly.GetPath(pathIndex);
+
+ // Ignore illegal path.
+ if (path.size() < 3)
+ continue;
+
+ // Validate the path.
+ b2Vec2* points;
+ ALLOC_TEMP(points, b2Vec2, path.size());
+ const int validPointCount = TransformPoints (path, relativeTransform, scale, points);
+
+ // Add path contour.
+ tessAddContour (tess, kVertexSize, points, sizeof(b2Vec2), validPointCount);
+ addedContours++;
+ }
+
+ // Finish if no contours added.
+ if (addedContours == 0)
+ return NULL;
+
+ // Perform the tessellation.
+ const int tessError = tessTesselate(tess, TESS_WINDING_ODD, TESS_POLYGONS, kMaxPolygonVerts, kVertexSize, NULL);
+ AssertBreak(tessError == 1);
+
+ // Allocate the shape array.
+ const int elemCount = tessGetElementCount (tess);
+
+ // Finish if no elements.
+ if (elemCount == 0)
+ return NULL;
+
+ shapes.resize_uninitialized(elemCount);
+ b2PolygonShape* polygons = ALLOC_TEMP_MANUAL(b2PolygonShape, elemCount);
+
+ // Extract the tessellation results into the shape array.
+ const TESSindex* elements = tessGetElements(tess);
+ const TESSreal* real = tessGetVertices(tess);
+ b2Vec2* buffer;
+ ALLOC_TEMP(buffer, b2Vec2, kMaxPolygonVerts);
+ int totalElementCount=0;
+ for (int elementIndex = 0; elementIndex < elemCount; ++elementIndex)
+ {
+ const int* indices = &elements[elementIndex * kMaxPolygonVerts];
+
+ // Extract vertices
+ int bufSize = 0;
+ for (int i = 0; i < kMaxPolygonVerts && indices[i] != TESS_UNDEF; ++i)
+ {
+ const float& x = real[indices[i]*kVertexSize];
+ const float& y = real[indices[i]*kVertexSize + 1];
+
+ b2Vec2 newPoint(x, y);
+ if (bufSize > 0 && b2DistanceSquared(buffer[bufSize-1], newPoint) <= b2_linearSlop * b2_linearSlop)
+ continue;
+
+ buffer[bufSize] = newPoint;
+ ++bufSize;
+ }
+
+ // Ignore small paths.
+ if (bufSize < 3)
+ continue;
+
+ // Fill shape
+ if (ValidatePolygonShape (buffer, bufSize))
+ {
+ b2PolygonShape& shape = polygons[totalElementCount];
+ new (&shape) b2PolygonShape();
+ shape.Set(buffer, bufSize);
+ shapes[totalElementCount++] = &shape;
+ }
+ }
+
+ tessDeleteTess(tess);
+
+ // Finish if nothing generated.
+ if (totalElementCount == 0)
+ {
+ if (polygons)
+ FREE_TEMP_MANUAL(polygons);
+ return NULL;
+ }
+
+ shapes.resize_initialized(totalElementCount);
+
+ return polygons;
+}
+
+
+int PolygonColliderBase2D::TransformPoints(const Polygon2D::TPath& path, const Matrix4x4f& relativeTransform, const Vector3f& scale, b2Vec2* outPoints)
+{
+ int outCount = 0;
+ for (size_t i = 0; i < path.size(); ++i)
+ {
+ // Calculate 3D vertex.
+ const Vector3f vertex3D = relativeTransform.MultiplyPoint3 (Vector3f(path[i].x * scale.x, path[i].y * scale.y, 0.0f));
+
+ // If any vertex are infinite or are a very large scale then abort transformation.
+ // We abort here rather than ignore the vertex otherwise we may end-up with large-scale collider geometry warping if
+ // only a few points are infinite or out-of-bounds. This less likely an issue with the small-scale.
+ if (!IsFinite (vertex3D) || SqrMagnitude(vertex3D ) > (PHYSICS_2D_LARGE_RANGE_CLAMP*PHYSICS_2D_LARGE_RANGE_CLAMP))
+ return 0;
+
+ // Fetch 2D vertex.
+ b2Vec2 vertex2D(vertex3D.x, vertex3D.y);
+
+ // Skip point if they end up being too close. Box2d fires asserts if distance between neighbors is less than b2_linearSlop.
+ if (outCount > 0 && b2DistanceSquared(*(outPoints-1), vertex2D) <= b2_linearSlop * b2_linearSlop)
+ continue;
+
+ *outPoints++ = vertex2D;
+ ++outCount;
+ }
+
+ return outCount;
+}
+
+
+bool PolygonColliderBase2D::ValidatePolygonShape(const b2Vec2* const points, const int pointCount)
+{
+ // Invalid polygon if the vertex count isn't in range.
+ if (pointCount < 3 || pointCount > b2_maxPolygonVertices)
+ return false;
+
+ // Validate the polygon using the exact same code that Box2D uses. This at least
+ // ensures that we don't trigger any runtime asserts in Box2D.
+
+ // Copy vertices into local buffer
+ b2Vec2 ps[b2_maxPolygonVertices];
+ for (int32 i = 0; i < pointCount; ++i)
+ ps[i] = points[i];
+
+ // Create the convex hull using the Gift wrapping algorithm
+ // http://en.wikipedia.org/wiki/Gift_wrapping_algorithm
+
+ // Find the right most point on the hull
+ int32 i0 = 0;
+ float32 x0 = ps[0].x;
+ for (int32 i = 1; i < pointCount; ++i)
+ {
+ float32 x = ps[i].x;
+ if (x > x0 || (x == x0 && ps[i].y < ps[i0].y))
+ {
+ i0 = i;
+ x0 = x;
+ }
+ }
+
+ int32 hull[b2_maxPolygonVertices];
+ int32 validPointCount = 0;
+ int32 ih = i0;
+
+ for (;;)
+ {
+ hull[validPointCount] = ih;
+
+ int32 ie = 0;
+ for (int32 j = 1; j < pointCount; ++j)
+ {
+ if (ie == ih)
+ {
+ ie = j;
+ continue;
+ }
+
+ b2Vec2 r = ps[ie] - ps[hull[validPointCount]];
+ b2Vec2 v = ps[j] - ps[hull[validPointCount]];
+ float32 c = b2Cross(r, v);
+ if (c < 0.0f)
+ {
+ ie = j;
+ }
+
+ // Collinearity check
+ if (c == 0.0f && v.LengthSquared() > r.LengthSquared())
+ {
+ ie = j;
+ }
+ }
+
+ ++validPointCount;
+ ih = ie;
+
+ if (ie == i0)
+ {
+ break;
+ }
+ }
+
+ // Finish if invalid point count.
+ if (validPointCount < 3)
+ return false;
+
+
+ // The following code is directly from Box2D.
+ // Unfortunately Box2D simply asserts if the area inside the polygon is below a specific threshold when it
+ // is calculating the centroid so using Box2Ds code and invalidating the polygon rather than throwing an assert is required.
+
+ // Copy vertices.
+ b2Vec2 vertices[b2_maxPolygonVertices];
+ for (int32 i = 0; i < validPointCount; ++i)
+ {
+ vertices[i] = ps[hull[i]];
+ }
+
+ b2Vec2 c; c.Set(0.0f, 0.0f);
+ float32 area = 0.0f;
+
+ // pRef is the reference point for forming triangles.
+ // It's location doesn't change the result (except for rounding error).
+ b2Vec2 pRef(0.0f, 0.0f);
+
+ const float32 inv3 = 1.0f / 3.0f;
+
+ for (int32 i = 0; i < validPointCount; ++i)
+ {
+ // Triangle vertices.
+ b2Vec2 p1 = pRef;
+ b2Vec2 p2 = vertices[i];
+ b2Vec2 p3 = i + 1 < validPointCount ? vertices[i+1] : vertices[0];
+
+ b2Vec2 e1 = p2 - p1;
+ b2Vec2 e2 = p3 - p1;
+
+ float32 D = b2Cross(e1, e2);
+
+ float32 triangleArea = 0.5f * D;
+ area += triangleArea;
+
+ // Area weighted centroid
+ c += triangleArea * inv3 * (p1 + p2 + p3);
+ }
+
+ // Check for valid area.
+ return IsFinite (area) && area > b2_epsilon;
+}
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/PolygonColliderBase2D.h b/Runtime/Physics2D/PolygonColliderBase2D.h
new file mode 100644
index 0000000..02d7069
--- /dev/null
+++ b/Runtime/Physics2D/PolygonColliderBase2D.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Collider2D.h"
+#include "Runtime/Graphics/Polygon2D.h"
+
+class Vector3f;
+
+
+// --------------------------------------------------------------------------
+
+
+class PolygonColliderBase2D : public Collider2D
+{
+public:
+ REGISTER_DERIVED_ABSTRACT_CLASS (PolygonColliderBase2D, Collider2D)
+
+ PolygonColliderBase2D (MemLabelId label, ObjectCreationMode mode);
+
+ virtual const Polygon2D& GetPoly() const = 0;
+
+protected:
+ virtual void Create (const Rigidbody2D* ignoreRigidbody = NULL);
+
+ b2Shape* ExtractConvexShapes(dynamic_array<b2Shape*>& shapes, const Matrix4x4f& relativeTransform);
+ static int TransformPoints(const Polygon2D::TPath& path, const Matrix4x4f& relativeTransform, const Vector3f& scale, b2Vec2* outPoints);
+ static bool ValidatePolygonShape(const b2Vec2* const points, const int pointCount);
+};
+
+#endif
diff --git a/Runtime/Physics2D/RigidBody2D.h b/Runtime/Physics2D/RigidBody2D.h
new file mode 100644
index 0000000..ed75dca
--- /dev/null
+++ b/Runtime/Physics2D/RigidBody2D.h
@@ -0,0 +1,125 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Physics2D/Physics2DSettings.h"
+
+class Vector2f;
+class Vector3f;
+class Quaternionf;
+class b2Body;
+struct b2Vec2;
+struct RootMotionData;
+
+
+// --------------------------------------------------------------------------
+
+enum RigidbodyInterpolation2D { kNoInterpolation2D = 0, kInterpolate2D = 1, kExtrapolate2D = 2 };
+enum RigidbodySleepMode2D { kNeverSleep2D = 0, kStartAwake2D = 1, kStartAsleep2D = 2 };
+enum CollisionDetectionMode2D { kDiscreteCollision2D = 0, kContinuousCollision2D = 1 };
+
+// --------------------------------------------------------------------------
+
+
+class Rigidbody2D : public Unity::Component
+{
+public:
+ REGISTER_DERIVED_CLASS (Rigidbody2D, Unity::Component)
+ DECLARE_OBJECT_SERIALIZE (Rigidbody2D)
+
+ Rigidbody2D (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~Rigidbody2D(); declared-by-macro
+
+ static void InitializeClass ();
+ static void CleanupClass () {}
+
+ virtual void CheckConsistency ();
+ virtual void Reset ();
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+ virtual void Deactivate (DeactivateOperation operation);
+
+ void Create ();
+
+ void TransformChanged (int changeMask);
+ void ApplyRootMotionBuiltin (RootMotionData* rootMotion);
+
+ float GetDrag () const { return m_LinearDrag; }
+ void SetDrag (float drag);
+
+ float GetAngularDrag () const { return m_AngularDrag; }
+ void SetAngularDrag (float drag);
+
+ float GetGravityScale () const { return m_GravityScale; }
+ void SetGravityScale (float scale);
+
+ void SetIsKinematic (bool isKinematic);
+ bool GetIsKinematic () const { return m_IsKinematic; }
+
+ void SetFixedAngle (bool fixedAngle);
+ bool IsFixedAngle () const { return m_FixedAngle; }
+
+ RigidbodyInterpolation2D GetInterpolation () { return (RigidbodyInterpolation2D)m_Interpolate; }
+ void SetInterpolation (RigidbodyInterpolation2D interpolation);
+
+ RigidbodySleepMode2D GetSleepMode () { return (RigidbodySleepMode2D)m_SleepingMode; }
+ void SetSleepMode (RigidbodySleepMode2D mode);
+
+ CollisionDetectionMode2D GetCollisionDetectionMode () { return (CollisionDetectionMode2D)m_CollisionDetection; }
+ void SetCollisionDetectionMode (CollisionDetectionMode2D mode);
+
+ // Get position and rotation. This can be different from transform state when using interpolation.
+ Vector3f GetBodyPosition () const;
+ Quaternionf GetBodyRotation () const;
+
+ // Linear velocity.
+ Vector2f GetVelocity () const;
+ void SetVelocity (const Vector2f& velocity);
+
+ // Angular velocity.
+ float GetAngularVelocity () const;
+ void SetAngularVelocity (float velocity);
+
+ // Sleeping.
+ void SetSleeping (bool sleeping);
+ bool IsSleeping () const;
+
+ // Mass.
+ float GetMass () const { return m_Mass; }
+ void SetMass (float mass);
+ void CalculateColliderBodyMass ();
+
+ // Add forces.
+ void AddForce (const Vector2f& force);
+ void AddTorque (float torque);
+ void AddForceAtPosition (const Vector2f& force, const Vector2f& position);
+
+ inline b2Body* GetBody() { return m_Body; }
+
+ static Rigidbody2D* FindRigidbody (const GameObject* gameObject, const Rigidbody2D* ignoreRigidbody = NULL);
+
+private:
+ void FetchPoseFromTransform (b2Vec2* outPos, float* outRot);
+ void UpdateInterpolationInfo ();
+ void Cleanup ();
+ void InformCollidersOfNewBody ();
+ void ReCreateInboundJoints ();
+ void RetargetDependencies (Rigidbody2D* ignoreRigidBody);
+
+private:
+ b2Body* m_Body;
+ Rigidbody2DInterpolationInfo* m_InterpolationInfo;
+
+ float m_Mass; ///< The mass of the body. range { 0.0001, 1000000 }
+ float m_LinearDrag; ///< The linear drag coefficient. 0 means no damping. range { 0, 1000000 }
+ float m_AngularDrag; ///< The angular drag coefficient. 0 means no damping. range { 0, 1000000 }
+ float m_GravityScale; ///< How much gravity affects this body. range { -1000000, 1000000 }
+ bool m_FixedAngle; ///< Whether the body's angle is fixed or not.
+ bool m_IsKinematic; ///< Whether the body is kinematic or not. If not then the body is dynamic.
+ UInt8 m_Interpolate; ///< The per-frame update mode for the body. enum { None = 0, Interpolate = 1, Extrapolate = 2 }
+ UInt8 m_SleepingMode; ///< The sleeping mode for the body. enum { Never Sleep = 0, Start Awake = 1, Start Asleep = 2 }
+ UInt8 m_CollisionDetection; ///< The collision detection mode for the body. enum { Discrete = 0, Continuous = 1 }
+};
+
+#endif
diff --git a/Runtime/Physics2D/Rigidbody2D.cpp b/Runtime/Physics2D/Rigidbody2D.cpp
new file mode 100644
index 0000000..6b2b01a
--- /dev/null
+++ b/Runtime/Physics2D/Rigidbody2D.cpp
@@ -0,0 +1,700 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Runtime/Physics2D/Joint2D.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/BaseClasses/MessageHandler.h"
+#include "Runtime/GameCode/RootMotionData.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Math/Simd/math.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+
+IMPLEMENT_CLASS_HAS_INIT (Rigidbody2D)
+IMPLEMENT_OBJECT_SERIALIZE (Rigidbody2D)
+
+
+// --------------------------------------------------------------------------
+
+
+Rigidbody2D::Rigidbody2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_Body(NULL)
+, m_InterpolationInfo(NULL)
+{
+}
+
+Rigidbody2D::~Rigidbody2D ()
+{
+ Cleanup ();
+}
+
+
+void Rigidbody2D::InitializeClass ()
+{
+ REGISTER_MESSAGE (Rigidbody2D, kTransformChanged, TransformChanged, int);
+ REGISTER_MESSAGE_PTR (Rigidbody2D, kAnimatorMoveBuiltin, ApplyRootMotionBuiltin, RootMotionData);
+}
+
+
+template<class TransferFunction>
+void Rigidbody2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER (m_Mass);
+ TRANSFER (m_LinearDrag);
+ TRANSFER (m_AngularDrag);
+ TRANSFER (m_GravityScale);
+ TRANSFER (m_FixedAngle);
+ TRANSFER (m_IsKinematic);
+ TRANSFER (m_Interpolate);
+ TRANSFER (m_SleepingMode);
+ TRANSFER (m_CollisionDetection);
+ transfer.Align();
+}
+
+
+void Rigidbody2D::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_Mass = clamp<float> (m_Mass, 0.0001f, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ m_LinearDrag = clamp<float> (m_LinearDrag, 0.0f, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ m_AngularDrag = clamp<float> (m_AngularDrag, 0.0f, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ m_GravityScale = clamp<float> (m_GravityScale, -PHYSICS_2D_LARGE_RANGE_CLAMP, PHYSICS_2D_LARGE_RANGE_CLAMP);
+
+ if (m_Interpolate>2)
+ m_Interpolate = kNoInterpolation2D;
+
+ if (m_SleepingMode>2)
+ m_SleepingMode = kStartAwake2D;
+
+ if (m_CollisionDetection!=0 && m_CollisionDetection!=1)
+ m_CollisionDetection = kDiscreteCollision2D;
+}
+
+
+void Rigidbody2D::Reset ()
+{
+ Super::Reset ();
+
+ m_Mass = 1.0f;
+ m_LinearDrag = 0.0f;
+ m_AngularDrag = 0.05f;
+ m_GravityScale = 1.0f;
+ m_FixedAngle = false;
+ m_IsKinematic = false;
+ m_Interpolate = (RigidbodyInterpolation2D)kNoInterpolation2D;
+ m_SleepingMode = (RigidbodySleepMode2D)kStartAwake2D;
+ m_CollisionDetection = (CollisionDetectionMode2D)kDiscreteCollision2D;
+}
+
+
+void Rigidbody2D::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ Assert (GameObject::GetMessageHandler().HasMessageCallback (ClassID(Rigidbody2D), kTransformChanged.messageID));
+
+ // Create the body if it's not created already.
+ if (IsActive() && m_Body == NULL)
+ Create ();
+
+ // Apply all body properties.
+ // Note that we should not allow anything to adjust the current sleep-state of the body here when the component
+ // is first woken otherwise we'll potentially override the need to be initially asleep (sleep mode).
+ if (!(awakeMode & (kDidLoadFromDisk | kInstantiateOrCreateFromCodeAwakeFromLoad)))
+ {
+ SetMass (m_Mass);
+ SetDrag (m_LinearDrag);
+ SetAngularDrag (m_AngularDrag);
+ SetGravityScale (m_GravityScale);
+ SetIsKinematic (m_IsKinematic);
+ SetFixedAngle (m_FixedAngle);
+ SetCollisionDetectionMode ((CollisionDetectionMode2D)m_CollisionDetection);
+ SetSleepMode ((RigidbodySleepMode2D)m_SleepingMode);
+ }
+
+ // Inform the colliders about the new body.
+ if (awakeMode & kInstantiateOrCreateFromCodeAwakeFromLoad)
+ InformCollidersOfNewBody ();
+}
+
+
+void Rigidbody2D::Deactivate (DeactivateOperation operation)
+{
+ Cleanup ();
+}
+
+
+void Rigidbody2D::Create ()
+{
+ if (m_Body != NULL)
+ return;
+
+ // Configure the body definitio0n.
+ b2BodyDef bodyDef;
+ bodyDef.type = m_IsKinematic ? b2_kinematicBody : b2_dynamicBody;
+ bodyDef.userData = this;
+ bodyDef.bullet = m_CollisionDetection == kContinuousCollision2D;
+ bodyDef.linearDamping = m_LinearDrag;
+ bodyDef.angularDamping = m_AngularDrag;
+ bodyDef.gravityScale = m_GravityScale;
+ bodyDef.fixedRotation = m_FixedAngle;
+ bodyDef.allowSleep = m_SleepingMode != kNeverSleep2D;
+ bodyDef.awake = m_SleepingMode != kStartAsleep2D;;
+
+ // Fetch the body pose.
+ if (IsActive ())
+ FetchPoseFromTransform (&bodyDef.position, &bodyDef.angle);
+
+ // Create the body.
+ m_Body = GetPhysics2DWorld()->CreateBody (&bodyDef);
+
+ // Calculate the collider body mass.
+ CalculateColliderBodyMass ();
+
+ // Set interpolation.
+ SetInterpolation ((RigidbodyInterpolation2D)m_Interpolate);
+}
+
+
+void Rigidbody2D::TransformChanged (int changeMask)
+{
+ // Finish if no body exists.
+ if (!m_Body)
+ return;
+
+ // Finish if transform message is disabled.
+ if (!GetPhysics2DManager().IsTransformMessageEnabled())
+ return;
+
+ // Update the rigid-body transform if the position or rotation has changed.
+ if (changeMask & (Transform::kPositionChanged | Transform::kRotationChanged))
+ {
+ b2Vec2 position;
+ float angle;
+ FetchPoseFromTransform (&position, &angle);
+ m_Body->SetTransform (position, angle);
+ m_Body->SetAwake(true);
+
+ // Disable interpolation if transform has changed.
+ if (m_InterpolationInfo)
+ m_InterpolationInfo->disabled = 1;
+ }
+
+ // Recreate inbound joints if the scale has changed.
+ if (changeMask & Transform::kScaleChanged)
+ {
+ ReCreateInboundJoints ();
+ return;
+ }
+}
+
+
+void Rigidbody2D::ApplyRootMotionBuiltin (RootMotionData* rootMotion)
+{
+ if (m_Body == NULL || rootMotion->didApply)
+ return;
+
+ if(GetIsKinematic())
+ {
+ b2Vec2 position = m_Body->GetPosition ();
+ position.x += rootMotion->deltaPosition.x;
+ position.y += rootMotion->deltaPosition.y;
+ m_Body->SetTransform (position, rootMotion->targetRotation.z);
+ }
+ else
+ {
+ Quaternionf rotation = GetComponent(Transform).GetRotation();
+ Quaternionf invRotation = Inverse(rotation);
+
+ // Get the physics velocity in local space
+ const Vector2f velocity2 = GetVelocity();
+ const Vector3f physicsVelocityLocal = RotateVectorByQuat(invRotation, Vector3f(velocity2.x, velocity2.y, 0.0f));
+
+ // Get the local space velocity and blend it with the physics velocity on the y-axis based on gravity weight
+ // We do this in local space in order to support moving on a curve earth with gravity changing direction
+ Vector3f animVelocityGlobal = rootMotion->deltaPosition * GetInvDeltaTime();
+ Vector3f localVelocity = RotateVectorByQuat (invRotation, animVelocityGlobal);
+ localVelocity.y = Lerp (localVelocity.y, physicsVelocityLocal.y, rootMotion->gravityWeight);
+
+ // If we use gravity, when we are in a jumping root motion, we have to cancel out the gravity
+ // applied by default. When doing for example a jump the only thing affecting velocity should be the animation data.
+ // The animation already has gravity applied in the animation data so to speak...
+ if (GetGravityScale () > 0.0f)
+ AddForce(GetPhysics2DSettings ().GetGravity() * -Lerp(1.0F, 0.0F, rootMotion->gravityWeight));
+
+ // Apply velocity & rotation
+ Vector3f globalVelocity = RotateVectorByQuat(rotation, localVelocity);
+ m_Body->SetLinearVelocity (b2Vec2(globalVelocity.x, globalVelocity.y));
+ const Vector3f localEuler = QuaternionToEuler (rootMotion->targetRotation);
+ m_Body->SetTransform (m_Body->GetPosition (), localEuler.z);
+ }
+
+ m_Body->SetAwake(true);
+ rootMotion->didApply = true;
+}
+
+
+void Rigidbody2D::SetDrag (float drag)
+{
+ ABORT_INVALID_FLOAT (drag, drag, Rigidbody2D);
+
+ m_LinearDrag = clamp<float> (drag, 0.0f, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ SetDirty ();
+
+ if (m_Body)
+ m_Body->SetLinearDamping (m_LinearDrag);
+}
+
+
+void Rigidbody2D::SetAngularDrag (float drag)
+{
+ ABORT_INVALID_FLOAT (drag, angularDrag, Rigidbody2D);
+
+ m_AngularDrag = clamp<float> (drag, 0.0f, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ SetDirty ();
+
+ if (m_Body)
+ m_Body->SetAngularDamping (m_AngularDrag);
+}
+
+
+void Rigidbody2D::SetGravityScale (float scale)
+{
+ ABORT_INVALID_FLOAT (scale, gravityScale, Rigidbody2D);
+
+ m_GravityScale = clamp<float> (scale, -PHYSICS_2D_LARGE_RANGE_CLAMP, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ SetDirty ();
+
+ if (m_Body)
+ {
+ m_Body->SetGravityScale (m_GravityScale);
+
+ // Wake body if a non-zero gravity is applied.
+ if (m_GravityScale < 0.0f || m_GravityScale > 0.0f)
+ m_Body->SetAwake (true);
+ }
+}
+
+
+void Rigidbody2D::SetIsKinematic (bool isKinematic)
+{
+ m_IsKinematic = isKinematic;
+ SetDirty();
+
+ if (m_Body)
+ {
+ m_Body->SetType (m_IsKinematic ? b2_kinematicBody : b2_dynamicBody);
+
+ // Wake the body.
+ m_Body->SetAwake(true);
+
+ // If transitioning into a kinematic body, reset velocities to match what 3D physics does.
+ if (m_IsKinematic)
+ {
+ m_Body->SetLinearVelocity (b2Vec2_zero);
+ m_Body->SetAngularVelocity (0.0f);
+ }
+
+ // Calculate the collider body mass.
+ // NOTE: Unfortunately we must do this here as Box2D resets its mass-data when this is changed.
+ CalculateColliderBodyMass ();
+ }
+}
+
+
+void Rigidbody2D::SetFixedAngle (bool fixedAngle)
+{
+ m_FixedAngle = fixedAngle;
+ SetDirty();
+
+ if (m_Body)
+ {
+ m_Body->SetFixedRotation(m_FixedAngle);
+
+ // Calculate the collider body mass.
+ // NOTE: Unfortunately we must do this here as Box2D resets its mass-data when this is changed.
+ CalculateColliderBodyMass ();
+ }
+}
+
+
+void Rigidbody2D::SetInterpolation (RigidbodyInterpolation2D interpolation)
+{
+ m_Interpolate = interpolation;
+ SetDirty();
+
+ if (m_Body)
+ UpdateInterpolationInfo();
+}
+
+
+void Rigidbody2D::SetSleepMode (RigidbodySleepMode2D mode)
+{
+ m_SleepingMode = mode;
+ SetDirty ();
+
+ if (m_Body)
+ m_Body->SetSleepingAllowed (mode != kNeverSleep2D);
+}
+
+
+void Rigidbody2D::SetCollisionDetectionMode (CollisionDetectionMode2D mode)
+{
+ m_CollisionDetection = mode;
+ SetDirty ();
+
+ if (m_Body)
+ m_Body->SetBullet( mode == kContinuousCollision2D );
+}
+
+
+Vector3f Rigidbody2D::GetBodyPosition () const
+{
+ Assert (m_Body != NULL);
+
+ const b2Vec2& pos2D = m_Body->GetPosition();
+ const Transform& transform = GetGameObject().GetComponent (Transform);
+ Vector3f pos3D = transform.GetPosition();
+ pos3D.x = pos2D.x;
+ pos3D.y = pos2D.y;
+
+ return pos3D;
+}
+
+
+Quaternionf Rigidbody2D::GetBodyRotation () const
+{
+ Assert (m_Body != NULL);
+
+ return EulerToQuaternion (Vector3f(0, 0, m_Body->GetAngle()));
+}
+
+
+Vector2f Rigidbody2D::GetVelocity () const
+{
+ // Return no linear velocity if no body is available.
+ if (m_Body == NULL)
+ return Vector2f::zero;
+
+ // Return the body linear velocity.
+ const b2Vec2& velocity = m_Body->GetLinearVelocity ();
+ return Vector2f (velocity.x, velocity.y);
+}
+
+
+void Rigidbody2D::SetVelocity (const Vector2f& velocity)
+{
+ ABORT_INVALID_VECTOR2 (velocity, velocity, Rigidbody2D);
+
+ // Ignore linear velocity if no body is available.
+ if (m_Body == NULL)
+ return;
+
+ m_Body->SetLinearVelocity( b2Vec2(velocity.x, velocity.y) );
+}
+
+
+float Rigidbody2D::GetAngularVelocity () const
+{
+ // Return no angular velocity if no body is available.
+ if (m_Body == NULL)
+ return 0.0f;
+
+ // Return the body angular velocity.
+ return math::degrees (m_Body->GetAngularVelocity());
+}
+
+
+void Rigidbody2D::SetAngularVelocity (float velocity)
+{
+ ABORT_INVALID_FLOAT (velocity, angularVelocity, Rigidbody2D);
+
+ // Ignore linear velocity if no body is available.
+ if (m_Body == NULL)
+ return;
+
+ m_Body->SetAngularVelocity (math::radians (velocity));
+}
+
+
+void Rigidbody2D::SetSleeping (bool sleeping)
+{
+ // Ignore sleeping if no body is available.
+ if (m_Body == NULL)
+ return;
+
+ m_Body->SetAwake(!sleeping);
+}
+
+
+bool Rigidbody2D::IsSleeping() const
+{
+ // Not sleeping if no body is available.
+ if (m_Body == NULL)
+ return false;
+
+ return !m_Body->IsAwake();
+}
+
+
+void Rigidbody2D::SetMass (float mass)
+{
+ ABORT_INVALID_FLOAT (mass, mass, Rigidbody2D);
+
+ // Clamp mass.
+ m_Mass = clamp<float> (mass, 0.0001f, PHYSICS_2D_LARGE_RANGE_CLAMP);
+
+ // Calculate the collider body mass.
+ CalculateColliderBodyMass ();
+}
+
+
+void Rigidbody2D::CalculateColliderBodyMass ()
+{
+ // Finish if body is not dynamic or has no fixtures.
+ if (m_Body == NULL ||
+ m_Body->GetType () != b2_dynamicBody)
+ return;
+
+ // Reset the mass-data.
+ m_Body->ResetMassData ();
+
+ // Scale body mass to target mass.
+ b2MassData bodyMassData;
+ m_Body->GetMassData (&bodyMassData);
+ const float massScale = m_Mass / bodyMassData.mass;
+ bodyMassData.mass *= massScale;
+
+ // Scale or set rotational inertia.
+ if (m_Body->GetFixtureCount () > 0)
+ bodyMassData.I *= massScale;
+ else
+ bodyMassData.I = m_Mass * 10.0f;
+
+ m_Body->SetMassData (&bodyMassData);
+}
+
+
+void Rigidbody2D::AddForce (const Vector2f& force)
+{
+ ABORT_INVALID_ARG_VECTOR2 (force, force, AddForce, Rigidbody2D);
+
+ // Ignore force if no body is available.
+ if (m_Body == NULL)
+ return;
+
+ m_Body->ApplyForceToCenter (b2Vec2(force.x,force.y), true);
+}
+
+
+void Rigidbody2D::AddTorque (float torque)
+{
+ ABORT_INVALID_ARG_FLOAT (torque, torque, AddTorque, Rigidbody2D);
+
+ // Ignore torque if no body is available.
+ if (m_Body == NULL)
+ return;
+
+ m_Body->ApplyTorque (torque, true);
+}
+
+
+void Rigidbody2D::AddForceAtPosition (const Vector2f& force, const Vector2f& position)
+{
+ ABORT_INVALID_ARG_VECTOR2 (force, force, AddForceAtPosition, Rigidbody2D);
+ ABORT_INVALID_ARG_VECTOR2 (position, position, AddForceAtPosition, Rigidbody2D);
+
+ // Ignore force-at-position if no body is available.
+ if (m_Body == NULL)
+ return;
+
+ m_Body->ApplyForce (b2Vec2(force.x, force.y), b2Vec2(position.x, position.y), true);
+}
+
+
+Rigidbody2D* Rigidbody2D::FindRigidbody (const GameObject* gameObject, const Rigidbody2D* ignoreRigidbody)
+{
+ Assert (gameObject != NULL);
+
+ // If there's a rigid-body on this game-object then return it.
+ Rigidbody2D* rigidBody = gameObject->QueryComponent (Rigidbody2D);
+ if (rigidBody && rigidBody != ignoreRigidbody && rigidBody->IsActive ())
+ return rigidBody;
+
+ // Search for a rigid-body up the transform hierarchy.
+ Transform* parent = gameObject->GetComponent (Transform).GetParent ();
+ while (parent)
+ {
+ GameObject* parentGameObject = parent->GetGameObjectPtr ();
+ if (parentGameObject)
+ rigidBody = parentGameObject->QueryComponent (Rigidbody2D);
+ else
+ rigidBody = NULL;
+
+ if (rigidBody && rigidBody != ignoreRigidbody && rigidBody->IsActive ())
+ return rigidBody;
+
+ parent = parent->GetParent ();
+ }
+
+ // Nothing found!
+ return NULL;
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void Rigidbody2D::FetchPoseFromTransform (b2Vec2* outPos, float* outRot)
+{
+ Transform& transform = GetComponent (Transform);
+ Vector3f pos = transform.GetPosition ();
+ Quaternionf rot = transform.GetRotation ();
+
+ AssertFiniteParameter(pos)
+ AssertFiniteParameter(rot)
+
+ float rotZ = QuaternionToEuler(rot).z;
+ outPos->Set (pos.x, pos.y);
+ *outRot = rotZ;
+}
+
+
+void Rigidbody2D::UpdateInterpolationInfo ()
+{
+ // Remove the interpolation info if no interpolation selected.
+ if (m_Interpolate == kNoInterpolation2D)
+ {
+ UNITY_DELETE(m_InterpolationInfo, kMemPhysics);
+ return;
+ }
+
+ // Finish if we already have interpolation info.
+ if (m_InterpolationInfo != NULL)
+ return;
+
+ // Generate the interpolation info.
+ m_InterpolationInfo = UNITY_NEW(Rigidbody2DInterpolationInfo, kMemPhysics);
+ Rigidbody2DInterpolationInfo& info = *m_InterpolationInfo;
+ info.body = this;
+ info.disabled = 1;
+ info.position = Vector3f::zero;
+ info.rotation = Quaternionf::identity();
+ GetPhysics2DManager().GetInterpolatedBodies().push_back(*m_InterpolationInfo);
+}
+
+
+void Rigidbody2D::Cleanup ()
+{
+ if (m_Body == NULL)
+ return;
+
+ // Process all colliders as the rigid-body is going away.
+ const int fixtureCount = m_Body->GetFixtureCount ();
+ if (fixtureCount > 0 )
+ {
+ dynamic_array<Collider2D*> attachedColliders(kMemTempAlloc);
+
+ // Gather all attached colliders.
+ for (b2Fixture* fixture = m_Body->GetFixtureList (); fixture != NULL; fixture = fixture->GetNext ())
+ {
+ Collider2D* collider = (Collider2D*)fixture->GetUserData ();
+ attachedColliders.push_back (collider);
+ }
+
+ // Process all colliders.
+ for (dynamic_array<Collider2D*>::iterator colliderItr = attachedColliders.begin (); colliderItr != attachedColliders.end (); ++colliderItr)
+ (*colliderItr)->Cleanup ();
+ }
+
+ // Process all joints as the rigid-body is going away.
+ if (m_Body->GetJointList () != NULL)
+ {
+ dynamic_array<Joint2D*> attachedJoints(kMemTempAlloc);
+
+ // Gather all joints.
+ for (b2JointEdge* joints = m_Body->GetJointList(); joints != NULL; joints = joints->next)
+ attachedJoints.push_back ((Joint2D*)joints->joint->GetUserData());
+
+ // Process all joints.
+ for (dynamic_array<Joint2D*>::iterator jointItr = attachedJoints.begin (); jointItr != attachedJoints.end (); ++jointItr)
+ (*jointItr)->Cleanup ();
+ }
+
+ // Destroy the body.
+ GetPhysics2DWorld()->DestroyBody (m_Body);
+ m_Body = NULL;
+
+ // Destroy the body interpolation information.
+ UNITY_DELETE(m_InterpolationInfo, kMemPhysics);
+}
+
+
+void Rigidbody2D::InformCollidersOfNewBody ()
+{
+ // Fetch all potential colliders.
+ dynamic_array<Unity::Component*> colliders (kMemTempAlloc);
+ GetComponentsInChildren (GetGameObject (), false, ClassID (Collider2D), colliders);
+
+ // Finish if no colliders are found.
+ if (colliders.size () == 0)
+ return;
+
+ // Recreate the colliders.
+ for (dynamic_array<Unity::Component*>::iterator colliderItr = colliders.begin (); colliderItr != colliders.end (); ++colliderItr)
+ {
+ Collider2D* collider = (Collider2D*)*colliderItr;
+
+ // Ignore if not enabled.
+ if (!collider->GetEnabled ())
+ continue;
+
+ collider->RecreateCollider (NULL);
+ }
+}
+
+
+void Rigidbody2D::ReCreateInboundJoints ()
+{
+ // Finish if not appropriate.
+ if (m_Body == NULL)
+ return;
+
+ // Fetch the joints this body is connected to.
+ // This can occur when there's "inbound" joints from other components on other game objects.
+ b2JointEdge* joints = m_Body->GetJointList();
+
+ // Process all joints.
+ while (joints != NULL)
+ {
+ // Fetch the joint.
+ b2Joint* joint = joints->joint;
+
+ // Fetch the next joint.
+ b2JointEdge* nextJoint = joints->next;
+
+ // Fetch the joint component.
+ Joint2D* jointComponent = (Joint2D*)joint->GetUserData();
+ Assert (jointComponent != NULL);
+
+ // Recreate the joint.
+ jointComponent->ReCreate ();
+
+ // Next joint.
+ joints = nextJoint;
+ }
+}
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/ScriptBindings/Physics2DBindings.txt b/Runtime/Physics2D/ScriptBindings/Physics2DBindings.txt
new file mode 100644
index 0000000..6298c40
--- /dev/null
+++ b/Runtime/Physics2D/ScriptBindings/Physics2DBindings.txt
@@ -0,0 +1,820 @@
+C++RAW
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Physics2D/Physics2DMaterial.h"
+#include "Runtime/Physics2D/Physics2DSettings.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/PolygonCollider2D.h"
+#include "Runtime/Physics2D/SpriteCollider2D.h"
+#include "Runtime/Physics2D/CircleCollider2D.h"
+#include "Runtime/Physics2D/BoxCollider2D.h"
+#include "Runtime/Physics2D/EdgeCollider2D.h"
+
+#include "Runtime/Physics2D/Joint2D.h"
+#include "Runtime/Physics2D/SpringJoint2D.h"
+#include "Runtime/Physics2D/DistanceJoint2D.h"
+#include "Runtime/Physics2D/HingeJoint2D.h"
+#include "Runtime/Physics2D/SliderJoint2D.h"
+
+#include "Runtime/Geometry/Ray.h"
+#include "Runtime/Graphics/SpriteFrame.h"
+#include "Runtime/Graphics/Polygon2D.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+
+CSRAW
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace UnityEngine
+{
+
+
+CONDITIONAL ENABLE_2D_PHYSICS
+NONSEALED_CLASS Physics2D
+ CSRAW public const int IgnoreRaycastLayer = 1 << 2;
+ CSRAW public const int DefaultRaycastLayers = ~IgnoreRaycastLayer;
+ CSRAW public const int AllLayers = ~0;
+
+
+ // --------------------------------------------------------------------------
+
+
+ THREAD_SAFE
+ CUSTOM_PROP static int velocityIterations { return GetPhysics2DSettings().GetVelocityIterations (); } { SCRIPTINGAPI_THREAD_CHECK(SetVelocityIterations) return GetPhysics2DSettings().SetVelocityIterations (value); }
+ CUSTOM_PROP static int positionIterations { return GetPhysics2DSettings().GetPositionIterations (); } { SCRIPTINGAPI_THREAD_CHECK(SetPositionIterations) return GetPhysics2DSettings().SetPositionIterations (value); }
+ CUSTOM_PROP static Vector2 gravity { return GetPhysics2DSettings().GetGravity (); } { SCRIPTINGAPI_THREAD_CHECK(set_gravity) return GetPhysics2DSettings().SetGravity (value); }
+ CUSTOM_PROP static bool raycastsHitTriggers { return GetPhysics2DSettings().GetRaycastsHitTriggers (); } { SCRIPTINGAPI_THREAD_CHECK(SetRaycastsHitTriggers) return GetPhysics2DSettings().SetRaycastsHitTriggers (value); }
+
+
+ // --------------------------------------------------------------------------
+
+
+ CUSTOM static public void IgnoreLayerCollision (int layer1, int layer2, bool ignore = true)
+ {
+ GetPhysics2DSettings().IgnoreCollision(layer1, layer2, ignore);
+ }
+
+ CUSTOM static public bool GetIgnoreLayerCollision (int layer1, int layer2)
+ {
+ return GetPhysics2DSettings().GetIgnoreCollision(layer1, layer2);
+ }
+
+
+ // --------------------------------------------------------------------------
+
+
+ C++RAW
+
+ // Create a managed array of native ray-cast hits.
+ static ScriptingArrayPtr CreateManagedRaycastArrayFromNative (RaycastHit2D* nativeHits, size_t size)
+ {
+ // Return an empty array if no hits.
+ if (size == 0)
+ return CreateEmptyStructArray (MONO_COMMON.raycastHit2D);
+
+ // Generate scripting array.
+ ScriptingArrayPtr scriptArray = CreateScriptingArray<RaycastHit2D> (MONO_COMMON.raycastHit2D, size);
+
+ // Transfer elements.
+ RaycastHit2D* scriptHit = Scripting::GetScriptingArrayStart<RaycastHit2D> (scriptArray);
+ for (size_t i = 0; i < size; ++i, ++scriptHit, ++nativeHits)
+ {
+ // Transfer members.
+ scriptHit->point = nativeHits->point;
+ scriptHit->normal = nativeHits->normal;
+ scriptHit->fraction = nativeHits->fraction;
+ scriptHit->collider = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (nativeHits->collider));
+ }
+ return scriptArray;
+ }
+
+ CSRAW
+
+
+ // --------------------------------------------------------------------------
+
+
+ CUSTOM private static void Internal_Linecast (Vector2 start, Vector2 end, int layerMask, float minDepth, float maxDepth, out RaycastHit2D raycastHit)
+ {
+ if (GetPhysics2DManager ().Linecast (start, end, layerMask, minDepth, maxDepth, raycastHit, 1) == 1)
+ raycastHit->collider = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (raycastHit->collider));
+ }
+
+ // Returns the first hit along the specified line.
+ CSRAW public static RaycastHit2D Linecast (Vector2 start, Vector2 end, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ RaycastHit2D raycastHit;
+ Internal_Linecast (start, end, layerMask, minDepth, maxDepth, out raycastHit);
+ return raycastHit;
+ }
+
+ // Returns all hits along the specified line.
+ CUSTOM public static RaycastHit2D[] LinecastAll (Vector2 start, Vector2 end, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ dynamic_array<RaycastHit2D> raycastHits(kMemTempAlloc);
+ if (GetPhysics2DManager ().LinecastAll (start, end, layerMask, minDepth, maxDepth, &raycastHits) == 0)
+ return CreateEmptyStructArray (MONO_COMMON.raycastHit2D);
+
+ return CreateManagedRaycastArrayFromNative (raycastHits.data (), raycastHits.size ());
+ }
+
+ // Returns all hits along the line (limited by the size of the array). This does not produce any garbage.
+ CUSTOM public static int LinecastNonAlloc (Vector2 start, Vector2 end, RaycastHit2D[] results, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ RaycastHit2D* raycastHits = Scripting::GetScriptingArrayStart<RaycastHit2D>(results);
+ const int resultCount = GetPhysics2DManager ().Linecast (start, end, layerMask, minDepth, maxDepth, raycastHits, GetScriptingArraySize(results));
+
+ for (size_t i = 0; i < resultCount; ++i, ++raycastHits)
+ raycastHits->collider = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (raycastHits->collider));
+
+ return resultCount;
+ }
+
+ CUSTOM private static void Internal_Raycast (Vector2 origin, Vector2 direction, float distance, int layerMask, float minDepth, float maxDepth, out RaycastHit2D raycastHit)
+ {
+ if (GetPhysics2DManager ().Raycast (origin, direction, distance, layerMask, minDepth, maxDepth, raycastHit, 1) == 1)
+ raycastHit->collider = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (raycastHit->collider));
+ }
+
+ // Returns the first hit along the ray.
+ CSRAW public static RaycastHit2D Raycast (Vector2 origin, Vector2 direction, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ RaycastHit2D raycastHit;
+ Internal_Raycast (origin, direction, distance, layerMask, minDepth, maxDepth, out raycastHit);
+ return raycastHit;
+ }
+
+ // Returns all hits along the ray.
+ CUSTOM public static RaycastHit2D[] RaycastAll (Vector2 origin, Vector2 direction, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ dynamic_array<RaycastHit2D> raycastHits(kMemTempAlloc);
+ if (GetPhysics2DManager ().RaycastAll (origin, direction, distance, layerMask, minDepth, maxDepth, &raycastHits) == 0)
+ return CreateEmptyStructArray(MONO_COMMON.raycastHit2D);
+
+ return CreateManagedRaycastArrayFromNative (raycastHits.data (), raycastHits.size ());
+ }
+
+ // Returns all hits along the ray (limited by the size of the array). This does not produce any garbage.
+ CUSTOM public static int RaycastNonAlloc (Vector2 origin, Vector2 direction, RaycastHit2D[] results, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ RaycastHit2D* raycastHits = Scripting::GetScriptingArrayStart<RaycastHit2D>(results);
+ const int resultCount = GetPhysics2DManager ().Raycast (origin, direction, distance, layerMask, minDepth, maxDepth, raycastHits, GetScriptingArraySize(results));
+
+ for (size_t i = 0; i < resultCount; ++i, ++raycastHits)
+ raycastHits->collider = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (raycastHits->collider));
+
+ return resultCount;
+ }
+
+
+ // --------------------------------------------------------------------------
+
+
+ CUSTOM private static void Internal_GetRayIntersection (Ray ray, float distance, int layerMask, out RaycastHit2D raycastHit)
+ {
+ if (GetPhysics2DManager ().GetRayIntersection (ray.GetOrigin (), ray.GetDirection (), distance, layerMask, raycastHit, 1) == 1)
+ raycastHit->collider = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (raycastHit->collider));
+ }
+
+ // Returns the first hit intersecting the 3D ray.
+ CSRAW public static RaycastHit2D GetRayIntersection (Ray ray, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
+ {
+ RaycastHit2D raycastHit;
+ Internal_GetRayIntersection (ray, distance, layerMask, out raycastHit);
+ return raycastHit;
+ }
+
+ // Returns all hits intersecting the 3D ray.
+ CUSTOM public static RaycastHit2D[] GetRayIntersectionAll (Ray ray, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
+ {
+ dynamic_array<RaycastHit2D> raycastHits(kMemTempAlloc);
+ if (GetPhysics2DManager ().GetRayIntersectionAll (ray.GetOrigin (), ray.GetDirection (), distance, layerMask, &raycastHits) == 0)
+ return CreateEmptyStructArray(MONO_COMMON.raycastHit2D);
+
+ return CreateManagedRaycastArrayFromNative (raycastHits.data (), raycastHits.size ());
+ }
+
+ // Returns all hits intersecting the 3D ray (limited by the size of the array). This does not produce any garbage.
+ CUSTOM public static int GetRayIntersectionNonAlloc (Ray ray, RaycastHit2D[] results, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
+ {
+ RaycastHit2D* raycastHits = Scripting::GetScriptingArrayStart<RaycastHit2D>(results);
+ const int resultCount = GetPhysics2DManager ().GetRayIntersection (ray.GetOrigin (), ray.GetDirection (), distance, layerMask, raycastHits, GetScriptingArraySize(results));
+
+ for (size_t i = 0; i < resultCount; ++i, ++raycastHits)
+ raycastHits->collider = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (raycastHits->collider));
+
+ return resultCount;
+ }
+
+
+ // --------------------------------------------------------------------------
+
+
+ // Returns a collider overlapping the point.
+ CUSTOM public static Collider2D OverlapPoint (Vector2 point, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ Collider2D* collider;
+ const int resultCount = GetPhysics2DManager ().OverlapPoint (point, layerMask, minDepth, maxDepth, &collider, 1);
+
+ if (resultCount == 0)
+ return SCRIPTING_NULL;
+
+ return Scripting::ScriptingWrapperFor (collider);
+ }
+
+ // Returns all colliders overlapping the point.
+ CUSTOM public static Collider2D[] OverlapPointAll (Vector2 point, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ dynamic_array<Collider2D*> colliderHits(kMemTempAlloc);
+ GetPhysics2DManager ().OverlapPointAll (point, layerMask, minDepth, maxDepth, &colliderHits);
+
+ // Generate scripting array.
+ return CreateScriptingArrayFromUnityObjects(colliderHits, ScriptingClassFor(Collider2D));
+ }
+
+ // Returns all colliders overlapping the point (limited by the size of the array). This does not produce any garbage.
+ CUSTOM public static int OverlapPointNonAlloc (Vector2 point, Collider2D[] results, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ Collider2D** colliderHits = Scripting::GetScriptingArrayStart<Collider2D*>(results);
+ const int resultCount = GetPhysics2DManager ().OverlapPoint (point, layerMask, minDepth, maxDepth, colliderHits, GetScriptingArraySize(results));
+
+ for (size_t i = 0; i < resultCount; ++i)
+ colliderHits[i] = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (colliderHits[i]));
+
+ return resultCount;
+ }
+
+ // Returns a collider overlapping the circle.
+ CUSTOM public static Collider2D OverlapCircle (Vector2 point, float radius, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ Collider2D* collider;
+ const int resultCount = GetPhysics2DManager ().OverlapCircle (point, radius, layerMask, minDepth, maxDepth, &collider, 1);
+
+ if (resultCount == 0)
+ return SCRIPTING_NULL;
+
+ return Scripting::ScriptingWrapperFor (collider);
+ }
+
+ // Returns all colliders overlapping the circle.
+ CUSTOM public static Collider2D[] OverlapCircleAll (Vector2 point, float radius, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ dynamic_array<Collider2D*> colliderHits(kMemTempAlloc);
+ GetPhysics2DManager ().OverlapCircleAll (point, radius, layerMask, minDepth, maxDepth, &colliderHits);
+
+ // Generate scripting array.
+ return CreateScriptingArrayFromUnityObjects(colliderHits, ScriptingClassFor(Collider2D));
+ }
+
+ // Returns all colliders overlapping the circle (limited by the size of the array). This does not produce any garbage.
+ CUSTOM public static int OverlapCircleNonAlloc (Vector2 point, float radius, Collider2D[] results, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ Collider2D** colliderHits = Scripting::GetScriptingArrayStart<Collider2D*>(results);
+ const int resultCount = GetPhysics2DManager ().OverlapCircle (point, radius, layerMask, minDepth, maxDepth, colliderHits, GetScriptingArraySize(results));
+
+ for (size_t i = 0; i < resultCount; ++i)
+ colliderHits[i] = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (colliderHits[i]));
+
+ return resultCount;
+ }
+
+ // Returns a collider overlapping the area.
+ CUSTOM public static Collider2D OverlapArea (Vector2 pointA, Vector2 pointB, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ Collider2D* collider;
+ const int resultCount = GetPhysics2DManager ().OverlapArea (pointA, pointB, layerMask, minDepth, maxDepth, &collider, 1);
+
+ if (resultCount == 0)
+ return SCRIPTING_NULL;
+
+ return Scripting::ScriptingWrapperFor (collider);
+ }
+
+ // Returns all colliders overlapping the area.
+ CUSTOM public static Collider2D[] OverlapAreaAll (Vector2 pointA, Vector2 pointB, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ dynamic_array<Collider2D*> colliderHits(kMemTempAlloc);
+ GetPhysics2DManager ().OverlapAreaAll (pointA, pointB, layerMask, minDepth, maxDepth, &colliderHits);
+
+ // Generate scripting array.
+ return CreateScriptingArrayFromUnityObjects(colliderHits, ScriptingClassFor(Collider2D));
+ }
+
+ // Returns all colliders overlapping the area (limited by the size of the array). This does not produce any garbage.
+ CUSTOM public static int OverlapAreaNonAlloc (Vector2 pointA, Vector2 pointB, Collider2D[] results, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ Collider2D** colliderHits = Scripting::GetScriptingArrayStart<Collider2D*>(results);
+ const int resultCount = GetPhysics2DManager ().OverlapArea (pointA, pointB, layerMask, minDepth, maxDepth, colliderHits, GetScriptingArraySize(results));
+
+ for (size_t i = 0; i < resultCount; ++i)
+ colliderHits[i] = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (colliderHits[i]));
+
+ return resultCount;
+ }
+
+END
+
+
+// NOTE: must match memory layout of native RaycastHit2D
+CONDITIONAL ENABLE_2D_PHYSICS
+STRUCT RaycastHit2D
+ CSRAW private Vector2 m_Point;
+ CSRAW private Vector2 m_Normal;
+ CSRAW private float m_Fraction;
+ #if UNITY_WINRT
+ CSRAW private int m_ColliderHandle;
+ #else
+ CSRAW private Collider2D m_Collider;
+ #endif
+
+ CSRAW public Vector2 point { get { return m_Point; } set { m_Point = value; } }
+ CSRAW public Vector2 normal { get { return m_Normal; } set { m_Normal = value; } }
+ CSRAW public float fraction { get { return m_Fraction; } set { m_Fraction = value; } }
+
+ CONDITIONAL !UNITY_WINRT
+ CSRAW public Collider2D collider { get { return m_Collider; } }
+
+ CONDITIONAL UNITY_WINRT
+ CSRAW public Collider2D collider { get { return UnityEngineInternal.ScriptingUtils.GCHandleToObject<Collider2D>(m_ColliderHandle); } }
+
+ CSRAW public Rigidbody2D rigidbody { get { return collider != null ? collider.attachedRigidbody : null; } }
+
+ CSRAW public Transform transform { get {
+ Rigidbody2D body = rigidbody;
+ if (body != null)
+ return body.transform;
+ else if (collider != null)
+ return collider.transform;
+ else
+ return null;
+ } }
+
+ // Implicitly convert a hit to a boolean based upon whether a collider reference exists or not.
+ CSRAW public static implicit operator bool (RaycastHit2D hit) { return hit.collider != null; }
+
+ // Compare the hit by fraction along the ray. If no colliders exist then fraction is moved "up". This allows sorting an array of sparse results.
+ CSRAW public int CompareTo(RaycastHit2D other) { if (collider == null) return 1; if (other.collider == null) return -1; return fraction.CompareTo(other.fraction); }
+END
+
+
+// [[Rigidbody2D]] interpolation mode.
+ENUM RigidbodyInterpolation2D
+ // No Interpolation.
+ None = 0,
+
+ // Interpolation will always lag a little bit behind but can be smoother than extrapolation.
+ Interpolate = 1,
+
+ // Extrapolation will predict the position of the rigidbody based on the current velocity.
+ Extrapolate = 2
+END
+
+
+// [[Rigidbody2D]] sleeping mode.
+ENUM RigidbodySleepMode2D
+ // Never sleep.
+ NeverSleep = 0,
+
+ // Start the rigid body awake.
+ StartAwake = 1,
+
+ // Start the rigid body asleep.
+ StartAsleep = 2
+END
+
+
+// [[Rigidbody2D]] collision detection mode.
+ENUM CollisionDetectionMode2D
+ // Provides standard (and least expensive) collision detection suitable for most circumstances.
+ None = 0,
+
+ // Provides the most accurate collision detection to present tunnelling but is more expensive. Use for vert fast moving objects only.
+ Continuous = 1
+END
+
+
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS Rigidbody2D : Component
+ // The linear velocity vector of the object.
+ AUTO_PROP Vector2 velocity GetVelocity SetVelocity
+
+ // The angular velocity vector of the object in degrees/sec.
+ AUTO_PROP float angularVelocity GetAngularVelocity SetAngularVelocity
+
+ // The (linear) drag of the object.
+ AUTO_PROP float drag GetDrag SetDrag
+
+ // The angular drag of the object.
+ AUTO_PROP float angularDrag GetAngularDrag SetAngularDrag
+
+ // Controls the effect of gravity on the object.
+ AUTO_PROP float gravityScale GetGravityScale SetGravityScale
+
+ // Controls whether the object is kinematic or dynamic.
+ AUTO_PROP bool isKinematic GetIsKinematic SetIsKinematic
+
+ // Controls whether the objects angle can be changed by collisions with other objects.
+ AUTO_PROP bool fixedAngle IsFixedAngle SetFixedAngle
+
+ // Checks whether the rigid body is sleeping or not.
+ CUSTOM bool IsSleeping() { return self->IsSleeping(); }
+
+ // Checks whether the rigid body is awake or not.
+ CUSTOM bool IsAwake() { return !self->IsSleeping(); }
+
+ // Sets the rigid body into a sleep state.
+ CUSTOM void Sleep() { self->SetSleeping(true); }
+
+ // Wakes the rigid from sleeping.
+ CUSTOM void WakeUp() { self->SetSleeping(false); }
+
+ // Interpolation allows you to smooth out the effect of running physics at a fixed rate.
+ AUTO_PROP RigidbodyInterpolation2D interpolation GetInterpolation SetInterpolation
+
+ // Controls how the object sleeps.
+ AUTO_PROP RigidbodySleepMode2D sleepMode GetSleepMode SetSleepMode
+
+ // The Rigidbody's collision detection mode.
+ AUTO_PROP CollisionDetectionMode2D collisionDetectionMode GetCollisionDetectionMode SetCollisionDetectionMode
+
+ // Controls the mass of the object by adjusting the density of all colliders attached to the object.
+ AUTO_PROP float mass GetMass SetMass
+
+ CUSTOM void AddForce (Vector2 force) { self->AddForce (force); }
+ CUSTOM void AddTorque (float torque) { self->AddTorque (torque); }
+ CUSTOM void AddForceAtPosition (Vector2 force, Vector2 position) { self->AddForceAtPosition (force, position); }
+END
+
+
+CONDITIONAL ENABLE_2D_PHYSICS
+NONSEALED_CLASS Collider2D : Behaviour
+ AUTO_PROP bool isTrigger GetIsTrigger SetIsTrigger
+ AUTO_PTR_PROP Rigidbody2D attachedRigidbody GetRigidbody
+
+ // Gets the number of shapes this collider has generated.
+ AUTO_PROP int shapeCount GetShapeCount
+
+ // Checks whether the specified point overlaps the collider or not.
+ CUSTOM public bool OverlapPoint (Vector2 point) { return self->OverlapPoint(point); }
+
+ // The shared physics material of this collider.
+ CUSTOM_PROP PhysicsMaterial2D sharedMaterial { return Scripting::ScriptingWrapperFor (self->GetMaterial ()); } { self->SetMaterial (value); }
+
+ // OnCollisionEnter2D is called when this [[Collider2D]] has begun touching another [[Collider2D]].
+ CSNONE void OnCollisionEnter2D (Collision2D info);
+
+ // OnCollisionStay2D is called once per frame for this [[Collider2D]] if it is continuing to touch another [[Collider2D]].
+ CSNONE void OnCollisionStay2D (Collision2D info);
+
+ // OnCollisionExit2D is called when this [[Collider2D]] has stopped touching another [[Collider2D]].
+ CSNONE void OnCollisionExit2D (Collision2D info);
+
+ // OnTriggerEnter2D is called when a [[Collider2D]] has begun touching another [[Collider2D]] configured as a trigger.
+ CSNONE void OnTriggerEnter2D (Collider2D other);
+
+ // OnTriggerStay2D is called once per frame for a [[Collider2D]] if it is continuing to touch another [[Collider2D]] configured as a trigger.
+ CSNONE void OnTriggerStay2D (Collider2D other);
+
+ // OnTriggerExit2D is called when a [[Collider2D]] has stopped touching another [[Collider2D]] configured as a trigger.
+ CSNONE void OnTriggerExit2D (Collider2D other);
+
+END
+
+
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS CircleCollider2D : Collider2D
+ AUTO_PROP Vector2 center GetCenter SetCenter
+ AUTO_PROP float radius GetRadius SetRadius
+END
+
+
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS BoxCollider2D : Collider2D
+ AUTO_PROP Vector2 center GetCenter SetCenter
+ AUTO_PROP Vector2 size GetSize SetSize
+END
+
+
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS EdgeCollider2D : Collider2D
+
+ // Reset to a single horizontal edge.
+ CUSTOM void Reset() { self->Reset (); }
+
+ // Get the number of edges. This is one less than the number of points.
+ CUSTOM_PROP int edgeCount { { return self->GetEdgeCount (); } }
+
+ // Get the number of points. This cannot be less than two which will form a single edge.
+ CUSTOM_PROP int pointCount { { return self->GetPointCount (); } }
+
+ // Get or set the points defining multiple continuous edges.
+ CUSTOM_PROP Vector2[] points
+ {
+ return CreateScriptingArrayStride<Vector2f>(self->GetPoints ().data (), self->GetPointCount (), MONO_COMMON.vector2, sizeof(Vector2f));
+ }
+ {
+ if (self->SetPoints (Scripting::GetScriptingArrayStart<Vector2f> (value), GetScriptingArraySize (value)))
+ return;
+
+ ErrorString("Invalid points assigned to 2D edge collider.");
+ }
+
+END
+
+
+CONDITIONAL (ENABLE_2D_PHYSICS && ENABLE_SPRITECOLLIDER)
+CLASS SpriteCollider2D : Collider2D
+
+ CUSTOM_PROP Sprite sprite
+ {
+ return Scripting::ScriptingWrapperFor(self->GetSprite());
+ }
+ {
+ self->SetSprite(value);
+ }
+
+END
+
+
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS PolygonCollider2D : Collider2D
+
+ // Get/Set a single path of points.
+ CUSTOM_PROP Vector2[] points
+ {
+ return CreateScriptingArrayStride<Vector2f>(self->GetPoly().GetPoints(), self->GetPoly().GetPointCount(), MONO_COMMON.vector2, sizeof(*self->GetPoly().GetPoints()));
+ }
+ {
+ self->GetPoly().SetPoints(Scripting::GetScriptingArrayStart<Vector2f>(value), GetScriptingArraySize(value));
+ self->RefreshPoly();
+ }
+
+ // Get the specified path of points.
+ CUSTOM Vector2[] GetPath(int index)
+ {
+ if (index >= self->GetPoly().GetPathCount())
+ {
+ Scripting::RaiseOutOfRangeException("Path %d does not exist.", index);
+ return SCRIPTING_NULL;
+ }
+
+ const Polygon2D::TPath& path = self->GetPoly().GetPath(index);
+ return CreateScriptingArrayStride<Vector2f>(path.data(), path.size(), MONO_COMMON.vector2, sizeof(*path.data()));
+ }
+
+ // Set the specified path of points.
+ CUSTOM void SetPath(int index, Vector2[] points)
+ {
+ const Vector2f* arrayStart = Scripting::GetScriptingArrayStart<Vector2f>(points);
+ const int arraySize = GetScriptingArraySize(points);
+ Polygon2D::TPath path;
+ path.assign(arrayStart, arrayStart+arraySize);
+ self->GetPoly().SetPath(index, path);
+ self->RefreshPoly();
+ }
+
+ // Get the number of paths.
+ CUSTOM_PROP int pathCount { return self->GetPoly().GetPathCount(); } { self->GetPoly().SetPathCount(value); self->RefreshPoly(); }
+
+ // Get the total number of points in all paths.
+ CUSTOM int GetTotalPointCount() { return self->GetPoly().GetTotalPointCount(); }
+
+ // Create a primitive n-sided polygon.
+ CUSTOM void CreatePrimitive(int sides, Vector2 scale = Vector2.one, Vector2 offset = Vector2.zero)
+ {
+ if (sides < 3)
+ {
+ ErrorString ("Cannot create a 2D polygon primitive collider with less than two sides.");
+ return;
+ }
+
+ if (scale.x <= 0.0f || scale.y <= 0.0f)
+ {
+ ErrorString ("Cannot create a 2D polygon primitive collider with an axis scale less than or equal to zero.");
+ return;
+ }
+
+ self->CreatePrimitive (sides, scale, offset);
+ }
+
+END
+
+// Describes a contact point where the collision occurs.
+CONDITIONAL ENABLE_2D_PHYSICS
+STRUCT ContactPoint2D
+ CSRAW internal Vector2 m_Point;
+ CSRAW internal Vector2 m_Normal;
+ CSRAW internal Collider2D m_Collider;
+ CSRAW internal Collider2D m_OtherCollider;
+
+ // The point of contact.
+ CSRAW public Vector2 point { get { return m_Point; } }
+
+ // Normal of the contact point.
+ CSRAW public Vector2 normal { get { return m_Normal; } }
+
+ // The first collider in contact.
+ CSRAW public Collider2D collider { get { return m_Collider; } }
+
+ // The other collider in contact.
+ CSRAW public Collider2D otherCollider { get { return m_OtherCollider; } }
+END
+
+CONDITIONAL ENABLE_2D_PHYSICS
+CSRAW [StructLayout (LayoutKind.Sequential)]
+NONSEALED_CLASS Collision2D
+ CSRAW internal Rigidbody2D m_Rigidbody;
+ CSRAW internal Collider2D m_Collider;
+ CSRAW internal ContactPoint2D[] m_Contacts;
+ CSRAW internal Vector2 m_RelativeVelocity;
+
+ CSRAW public Rigidbody2D rigidbody { get { return m_Rigidbody; } }
+ CSRAW public Collider2D collider { get { return m_Collider; } }
+ CSRAW public Transform transform { get { return rigidbody != null ? rigidbody.transform : collider.transform; } }
+ CSRAW public GameObject gameObject { get { return m_Rigidbody != null ? m_Rigidbody.gameObject : m_Collider.gameObject; } }
+ CSRAW public ContactPoint2D[] contacts { get { return m_Contacts; } }
+ CSRAW public Vector2 relativeVelocity { get { return m_RelativeVelocity; } }
+
+END
+
+
+// JointAngleLimits2D is used by the [[HingeJoint2D]] to limit the joints angle.
+CONDITIONAL ENABLE_2D_PHYSICS
+STRUCT JointAngleLimits2D
+ CSRAW private float m_LowerAngle;
+ CSRAW private float m_UpperAngle;
+
+ // The lower angle limit of the joint.
+ CSRAW public float min { get { return m_LowerAngle; } set { m_LowerAngle = value; } }
+
+ // The upper angle limit of the joint.
+ CSRAW public float max { get { return m_UpperAngle; } set { m_UpperAngle = value; } }
+END
+
+
+// JointTranslationLimits2D is used by the [[SliderJoint2D]] to limit the joints translation.
+CONDITIONAL ENABLE_2D_PHYSICS
+STRUCT JointTranslationLimits2D
+ CSRAW private float m_LowerTranslation;
+ CSRAW private float m_UpperTranslation;
+
+ // The lower translation limit of the joint.
+ CSRAW public float min { get { return m_LowerTranslation; } set { m_LowerTranslation = value; } }
+
+ // The upper translation limit of the joint.
+ CSRAW public float max { get { return m_UpperTranslation; } set { m_UpperTranslation = value; } }
+END
+
+
+// JointMotor2D is used by the [[HingeJoint2D]] and [[SliderJoint2D]] to motorize a joint.
+CONDITIONAL ENABLE_2D_PHYSICS
+STRUCT JointMotor2D
+ CSRAW private float m_MotorSpeed;
+ CSRAW private float m_MaximumMotorTorque;
+
+ // The target motor speed in degrees/second.
+ CSRAW public float motorSpeed { get { return m_MotorSpeed; } set { m_MotorSpeed = value; } }
+
+ // The maximum torque in N-m the motor can use to achieve the desired motor speed.
+ CSRAW public float maxMotorTorque { get { return m_MaximumMotorTorque; } set { m_MaximumMotorTorque = value; } }
+END
+
+
+// Joint2D is the base class for all 2D joints.
+CONDITIONAL ENABLE_2D_PHYSICS
+NONSEALED_CLASS Joint2D : Behaviour
+
+ // A reference to another rigid-body this joint connects to.
+ AUTO_PTR_PROP Rigidbody2D connectedBody GetConnectedBody SetConnectedBody
+
+ // Whether the connected rigid-bodies should collide with each other or not.
+ AUTO_PROP bool collideConnected GetCollideConnected SetCollideConnected
+
+END
+
+// The SpringJoint2D ensures that the two connected rigid-bodies stay at a specific distance apart using a spring system.
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS SpringJoint2D : Joint2D
+
+ // The Position of the anchor around which the joints motion is constrained.
+ AUTO_PROP Vector2 anchor GetAnchor SetAnchor
+
+ // The Position of the anchor around which the joints motion is constrained.
+ AUTO_PROP Vector2 connectedAnchor GetConnectedAnchor SetConnectedAnchor
+
+ // The distance the joint should maintain between the two connected rigid-bodies.
+ AUTO_PROP float distance GetDistance SetDistance
+
+ // The damping ratio for the oscillation whilst trying to achieve the specified distance. 0 means no damping. 1 means critical damping. range { 0.0, 1.0 }
+ AUTO_PROP float dampingRatio GetDampingRatio SetDampingRatio
+
+ // The frequency in Hertz for the oscillation whilst trying to achieve the specified distance. range { 0.0, infinity }
+ AUTO_PROP float frequency GetFrequency SetFrequency
+
+END
+
+
+// The DistanceJoint2D ensures that the two connected rigid-bodies stay at a maximum specific distance apart.
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS DistanceJoint2D : Joint2D
+
+ // The Position of the anchor around which the joints motion is constrained.
+ AUTO_PROP Vector2 anchor GetAnchor SetAnchor
+
+ // The Position of the anchor around which the joints motion is constrained.
+ AUTO_PROP Vector2 connectedAnchor GetConnectedAnchor SetConnectedAnchor
+
+ // The maximum distance the joint should maintain between the two connected rigid-bodies.
+ AUTO_PROP float distance GetDistance SetDistance
+
+END
+
+
+// The HingeJoint2D constrains the two connected rigid-bodies around the anchor points not restricting the relative rotation of them. Can be used for wheels, rollers, chains, rag-dol joints, levers etc.
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS HingeJoint2D : Joint2D
+
+ // Setting the motor or limit automatically enabled them.
+
+ // The Position of the anchor around which the joints motion is constrained.
+ AUTO_PROP Vector2 anchor GetAnchor SetAnchor
+
+ // The Position of the anchor around which the joints motion is constrained.
+ AUTO_PROP Vector2 connectedAnchor GetConnectedAnchor SetConnectedAnchor
+
+ // Enables the joint's motor.
+ AUTO_PROP bool useMotor GetUseMotor SetUseMotor
+
+ // Enables the joint's limits.
+ AUTO_PROP bool useLimits GetUseLimits SetUseLimits
+
+ // The motor will apply a force up to a maximum torque to achieve the target velocity in degrees per second.
+ AUTO_PROP JointMotor2D motor GetMotor SetMotor
+
+ // The limits of the hinge joint.
+ AUTO_PROP JointAngleLimits2D limits GetLimits SetLimits
+
+END
+
+// The SliderJoint2D constrains the two connected rigid-bodies to have on degree of freedom: translation along a fixed axis. Relative motion is prevented.
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS SliderJoint2D : Joint2D
+
+ // Setting the motor or limit automatically enabled them.
+
+ // The Position of the anchor around which the joints motion is constrained.
+ AUTO_PROP Vector2 anchor GetAnchor SetAnchor
+
+ // The Position of the anchor around which the joints motion is constrained.
+ AUTO_PROP Vector2 connectedAnchor GetConnectedAnchor SetConnectedAnchor
+
+ // The translation angle that the joint slides along.
+ AUTO_PROP float angle GetAngle SetAngle
+
+ // Enables the joint's motor.
+ AUTO_PROP bool useMotor GetUseMotor SetUseMotor
+
+ // Enables the joint's limits.
+ AUTO_PROP bool useLimits GetUseLimits SetUseLimits
+
+ // The motor will apply a force up to a maximum torque to achieve the target velocity in degrees per second.
+ AUTO_PROP JointMotor2D motor GetMotor SetMotor
+
+ // The limits of the slider joint.
+ AUTO_PROP JointTranslationLimits2D limits GetLimits SetLimits
+
+END
+
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS PhysicsMaterial2D : Object
+
+ CUSTOM private static void Internal_Create ([Writable]PhysicsMaterial2D mat, string name)
+ {
+ PhysicsMaterial2D* material = NEW_OBJECT (PhysicsMaterial2D);
+ SmartResetObject(*material);
+ material->SetNameCpp (name);
+ Scripting::ConnectScriptingWrapperToObject (mat.GetScriptingObject(), material);
+ }
+
+ // Creates a new material.
+ CSRAW public PhysicsMaterial2D () { Internal_Create (this,null); }
+
+ // Creates a new material named /name/.
+ CSRAW public PhysicsMaterial2D (string name) { Internal_Create (this,name); }
+
+ // How bouncy is the surface? A value of 0 will not bounce. A value of 1 will bounce without any loss of energy.
+ AUTO_PROP float bounciness GetBounciness SetBounciness
+
+ // The friction.
+ AUTO_PROP float friction GetFriction SetFriction
+
+END
+
+CSRAW }
diff --git a/Runtime/Physics2D/SliderJoint2D.cpp b/Runtime/Physics2D/SliderJoint2D.cpp
new file mode 100644
index 0000000..d802c17
--- /dev/null
+++ b/Runtime/Physics2D/SliderJoint2D.cpp
@@ -0,0 +1,231 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Physics2D/SliderJoint2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Math/Simd/math.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "External/Box2D/Box2D/Box2D.h"
+
+
+IMPLEMENT_CLASS (SliderJoint2D)
+IMPLEMENT_OBJECT_SERIALIZE (SliderJoint2D)
+
+
+// --------------------------------------------------------------------------
+
+
+SliderJoint2D::SliderJoint2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_OldReferenceAngle (std::numeric_limits<float>::infinity ())
+{
+}
+
+
+SliderJoint2D::~SliderJoint2D ()
+{
+}
+
+
+template<class TransferFunction>
+void SliderJoint2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer(transfer);
+
+ TRANSFER (m_Anchor);
+ TRANSFER (m_ConnectedAnchor);
+ TRANSFER (m_Angle);
+ TRANSFER (m_UseMotor);
+ transfer.Align ();
+ TRANSFER (m_Motor);
+ TRANSFER (m_UseLimits);
+ transfer.Align ();
+ TRANSFER (m_TranslationLimits);
+}
+
+
+void SliderJoint2D::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_Motor.CheckConsistency ();
+ m_TranslationLimits.CheckConsistency ();
+
+ m_Angle = clamp<float> (m_Angle, 0.0f, 359.9999f);
+
+ if (!IsFinite(m_Anchor))
+ m_Anchor = Vector2f::zero;
+
+ if (!IsFinite(m_ConnectedAnchor))
+ m_ConnectedAnchor = Vector2f::zero;
+}
+
+
+void SliderJoint2D::Reset ()
+{
+ Super::Reset ();
+
+ m_Angle = 0.0f;
+ m_UseMotor = false;
+ m_UseLimits = false;
+ m_Motor.Initialize ();
+ m_TranslationLimits.Initialize ();
+
+ m_Anchor = Vector2f::zero;
+ m_ConnectedAnchor = Vector2f::zero;
+}
+
+
+void SliderJoint2D::SetAnchor (const Vector2f& anchor)
+{
+ ABORT_INVALID_VECTOR2 (anchor, anchor, SliderJoint2D);
+
+ m_Anchor = anchor;
+ SetDirty();
+
+ // Recreate the joint.
+ if (m_Joint != NULL)
+ ReCreate();
+}
+
+
+void SliderJoint2D::SetConnectedAnchor (const Vector2f& anchor)
+{
+ ABORT_INVALID_VECTOR2 (anchor, connectedAnchor, SliderJoint2D);
+
+ m_ConnectedAnchor = anchor;
+ SetDirty();
+
+ // Recreate the joint.
+ if (m_Joint != NULL)
+ ReCreate();
+}
+
+
+void SliderJoint2D::SetAngle (float angle)
+{
+ ABORT_INVALID_FLOAT (angle, angle, DistanceJoint2D);
+
+ m_Angle = clamp<float> (angle, 0.0f, 359.9999f);
+ SetDirty();
+
+ // Recreate the joint.
+ if (m_Joint != NULL)
+ ReCreate();
+}
+
+
+void SliderJoint2D::SetUseMotor (bool enable)
+{
+ m_UseMotor = enable;
+ SetDirty();
+
+ if (m_Joint != NULL)
+ ((b2PrismaticJoint*)m_Joint)->EnableMotor(m_UseMotor);
+}
+
+
+void SliderJoint2D::SetUseLimits (bool enable)
+{
+ m_UseLimits = enable;
+ SetDirty();
+
+ if (m_Joint != NULL)
+ ((b2PrismaticJoint*)m_Joint)->EnableLimit(m_UseLimits);
+}
+
+
+void SliderJoint2D::SetMotor (const JointMotor2D& motor)
+{
+ m_Motor = motor;
+ m_Motor.CheckConsistency ();
+ SetDirty();
+
+ // Motor is automatically enabled if motor is set.
+ SetUseMotor(true);
+
+ if (m_Joint != NULL)
+ {
+ b2PrismaticJoint* joint = (b2PrismaticJoint*)m_Joint;
+ joint->SetMotorSpeed (math::radians (m_Motor.m_MotorSpeed));
+ joint->SetMaxMotorForce (m_Motor.m_MaximumMotorForce);
+ }
+}
+
+
+void SliderJoint2D::SetLimits (const JointTranslationLimits2D& limits)
+{
+ m_TranslationLimits = limits;
+ m_TranslationLimits.CheckConsistency ();
+ SetDirty();
+
+ // Limits ares automatically enabled if limits are set.
+ SetUseLimits(true);
+
+ if (m_Joint != NULL)
+ {
+ b2PrismaticJoint* joint = (b2PrismaticJoint*)m_Joint;
+ joint->SetLimits (m_TranslationLimits.m_LowerTranslation, m_TranslationLimits.m_UpperTranslation);
+ }
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void SliderJoint2D::Create ()
+{
+ Assert (m_Joint == NULL);
+
+ if (!IsActive ())
+ return;
+
+ // Fetch transform scales.
+ const Vector3f scale = GetComponent (Transform).GetWorldScaleLossy ();
+ const Vector3f connectedScale = m_ConnectedRigidBody.IsNull () ? Vector3f::one : m_ConnectedRigidBody->GetComponent (Transform).GetWorldScaleLossy ();
+
+ // Fetch bodies.
+ b2Body* bodyA = FetchBodyA();
+ b2Body* bodyB = FetchBodyB();
+
+ // Configure the joint definition.
+ b2PrismaticJointDef jointDef;
+ jointDef.localAnchorA.Set (m_Anchor.x * scale.x, m_Anchor.y * scale.y);
+ jointDef.localAnchorB.Set (m_ConnectedAnchor.x * connectedScale.x, m_ConnectedAnchor.y * connectedScale.y);
+ jointDef.enableMotor = m_UseMotor;
+ jointDef.enableLimit = m_UseLimits;
+ jointDef.motorSpeed = math::radians (m_Motor.m_MotorSpeed);
+ jointDef.maxMotorForce = m_Motor.m_MaximumMotorForce;
+ jointDef.lowerTranslation = m_TranslationLimits.m_LowerTranslation;
+ jointDef.upperTranslation = m_TranslationLimits.m_UpperTranslation;
+ jointDef.referenceAngle = m_OldReferenceAngle == std::numeric_limits<float>::infinity () ? bodyB->GetAngle() - bodyA->GetAngle() : m_OldReferenceAngle;
+
+ float angle = math::radians (m_Angle);
+ jointDef.localAxisA = bodyA->GetLocalVector (b2Vec2 (math::sin (angle), -math::cos(angle)));
+
+ // Create the joint.
+ FinalizeCreateJoint (&jointDef);
+}
+
+
+void SliderJoint2D::ReCreate()
+{
+ // Do we have an existing joint and we're still active/enabled?
+ if (m_Joint != NULL && IsActive () && GetEnabled ())
+ {
+ // Yes, so keep reference angle.
+ m_OldReferenceAngle = ((b2RevoluteJoint*)m_Joint)->GetReferenceAngle ();
+ }
+ else
+ {
+ // No, so reset reference angle.
+ m_OldReferenceAngle = std::numeric_limits<float>::infinity ();
+ }
+
+ Super::ReCreate ();
+}
+#endif //ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/SliderJoint2D.h b/Runtime/Physics2D/SliderJoint2D.h
new file mode 100644
index 0000000..6088f44
--- /dev/null
+++ b/Runtime/Physics2D/SliderJoint2D.h
@@ -0,0 +1,65 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Physics2D/Joint2D.h"
+
+class Vector2f;
+
+
+// --------------------------------------------------------------------------
+
+
+class SliderJoint2D : public Joint2D
+{
+public:
+
+ REGISTER_DERIVED_CLASS (SliderJoint2D, Joint2D)
+ DECLARE_OBJECT_SERIALIZE (SliderJoint2D)
+
+ SliderJoint2D (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~SliderJoint2D (); declared-by-macro
+
+ virtual void CheckConsistency();
+ virtual void Reset ();
+
+ Vector2f GetAnchor () const { return m_Anchor; }
+ virtual void SetAnchor (const Vector2f& anchor);
+
+ Vector2f GetConnectedAnchor () const { return m_ConnectedAnchor; }
+ virtual void SetConnectedAnchor (const Vector2f& anchor);
+
+ float GetAngle () const { return m_Angle; }
+ void SetAngle (float angle);
+
+ bool GetUseMotor () const { return m_UseMotor; }
+ void SetUseMotor (bool enable);
+
+ bool GetUseLimits () const { return m_UseLimits; }
+ void SetUseLimits (bool enable);
+
+ JointMotor2D GetMotor () const { return m_Motor; }
+ void SetMotor (const JointMotor2D& motor);
+
+ JointTranslationLimits2D GetLimits () const { return m_TranslationLimits; }
+ void SetLimits (const JointTranslationLimits2D& limits);
+
+protected:
+ virtual void Create ();
+ virtual void ReCreate();
+
+protected:
+ Vector2f m_Anchor; ///< The local-space anchor from the base rigid-body.
+ Vector2f m_ConnectedAnchor; ///< The local-space anchor from the connected rigid-body.
+ float m_Angle; ///< The translation angle that the joint slides along. range { 0.0, 359.9999 }
+ JointMotor2D m_Motor; ///< The joint motor.
+ JointTranslationLimits2D m_TranslationLimits; ///< The joint angle limits.
+ bool m_UseMotor; ///< Whether to use the joint motor or not.
+ bool m_UseLimits; ///< Whether to use the angle limits or not.
+
+private:
+ float m_OldReferenceAngle;
+};
+
+#endif
diff --git a/Runtime/Physics2D/SpringJoint2D.cpp b/Runtime/Physics2D/SpringJoint2D.cpp
new file mode 100644
index 0000000..cfbc6a0
--- /dev/null
+++ b/Runtime/Physics2D/SpringJoint2D.cpp
@@ -0,0 +1,161 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Physics2D/SpringJoint2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+
+IMPLEMENT_CLASS (SpringJoint2D)
+IMPLEMENT_OBJECT_SERIALIZE (SpringJoint2D)
+
+
+// --------------------------------------------------------------------------
+
+
+SpringJoint2D::SpringJoint2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+
+SpringJoint2D::~SpringJoint2D ()
+{
+}
+
+
+template<class TransferFunction>
+void SpringJoint2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer(transfer);
+
+ TRANSFER (m_Anchor);
+ TRANSFER (m_ConnectedAnchor);
+ TRANSFER (m_Distance);
+ TRANSFER (m_DampingRatio);
+ TRANSFER (m_Frequency);
+}
+
+
+void SpringJoint2D::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_Distance = clamp<float> (m_Distance, b2_linearSlop, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ m_Frequency = clamp<float> (m_Frequency, 0.0f, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ m_DampingRatio = clamp(m_DampingRatio, 0.0f, 1.0f);
+
+ if (!IsFinite(m_Anchor))
+ m_Anchor = Vector2f::zero;
+
+ if (!IsFinite(m_ConnectedAnchor))
+ m_ConnectedAnchor = Vector2f::zero;
+}
+
+
+void SpringJoint2D::Reset ()
+{
+ Super::Reset ();
+
+ m_Distance = 1.0f;
+ m_DampingRatio = 0.0f;
+ m_Frequency = 10.0f;
+ m_Anchor = Vector2f::zero;
+ m_ConnectedAnchor = Vector2f::zero;
+}
+
+
+void SpringJoint2D::SetAnchor (const Vector2f& anchor)
+{
+ ABORT_INVALID_VECTOR2 (anchor, anchor, SpringJoint2D);
+
+ m_Anchor = anchor;
+ SetDirty();
+
+ // Recreate the joint.
+ if (m_Joint != NULL)
+ ReCreate();
+}
+
+
+void SpringJoint2D::SetConnectedAnchor (const Vector2f& anchor)
+{
+ ABORT_INVALID_VECTOR2 (anchor, connectedAnchor, SpringJoint2D);
+
+ m_ConnectedAnchor = anchor;
+ SetDirty();
+
+ // Recreate the joint.
+ if (m_Joint != NULL)
+ ReCreate();
+}
+
+
+void SpringJoint2D::SetDistance (float distance)
+{
+ ABORT_INVALID_FLOAT (distance, distance, SpringJoint2D);
+
+ m_Distance = clamp<float> (distance, b2_linearSlop, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ SetDirty();
+
+ if (m_Joint != NULL)
+ ((b2DistanceJoint*)m_Joint)->SetLength (m_Distance);
+}
+
+
+void SpringJoint2D::SetDampingRatio (float ratio)
+{
+ ABORT_INVALID_FLOAT (ratio, dampingRatio, SpringJoint2D);
+
+ m_DampingRatio = clamp(ratio, 0.0f, 1.0f);
+ SetDirty();
+
+ if (m_Joint != NULL)
+ ((b2DistanceJoint*)m_Joint)->SetDampingRatio (m_DampingRatio);
+}
+
+
+void SpringJoint2D::SetFrequency (float frequency)
+{
+ ABORT_INVALID_FLOAT (frequency, frequency, SpringJoint2D);
+
+ m_Frequency = clamp<float> (frequency, 0.0f, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ SetDirty();
+
+ if (m_Joint != NULL)
+ ((b2DistanceJoint*)m_Joint)->SetFrequency (m_Frequency);
+}
+
+// --------------------------------------------------------------------------
+
+
+void SpringJoint2D::Create ()
+{
+ Assert (m_Joint == NULL);
+
+ if (!IsActive ())
+ return;
+
+ // Fetch transform scales.
+ const Vector3f scale = GetComponent (Transform).GetWorldScaleLossy ();
+ const Vector3f connectedScale = m_ConnectedRigidBody.IsNull () ? Vector3f::one : m_ConnectedRigidBody->GetComponent (Transform).GetWorldScaleLossy ();
+
+ // Configure the joint definition.
+ b2DistanceJointDef jointDef;
+ jointDef.dampingRatio = m_DampingRatio;
+ jointDef.frequencyHz = m_Frequency;
+ jointDef.length = m_Distance;
+ jointDef.localAnchorA.Set (m_Anchor.x * scale.x, m_Anchor.y * scale.y);
+ jointDef.localAnchorB.Set (m_ConnectedAnchor.x * connectedScale.x, m_ConnectedAnchor.y * connectedScale.y);
+
+ // Create the joint.
+ FinalizeCreateJoint (&jointDef);
+}
+
+
+#endif //ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/SpringJoint2D.h b/Runtime/Physics2D/SpringJoint2D.h
new file mode 100644
index 0000000..6d46cd4
--- /dev/null
+++ b/Runtime/Physics2D/SpringJoint2D.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Physics2D/Joint2D.h"
+
+class Vector2f;
+
+
+// --------------------------------------------------------------------------
+
+
+class SpringJoint2D : public Joint2D
+{
+public:
+
+ REGISTER_DERIVED_CLASS (SpringJoint2D, Joint2D)
+ DECLARE_OBJECT_SERIALIZE (SpringJoint2D)
+
+ SpringJoint2D (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~SpringJoint2D (); declared-by-macro
+
+ virtual void CheckConsistency();
+ virtual void Reset ();
+
+ Vector2f GetAnchor () const { return m_Anchor; }
+ virtual void SetAnchor (const Vector2f& anchor);
+
+ Vector2f GetConnectedAnchor () const { return m_ConnectedAnchor; }
+ virtual void SetConnectedAnchor (const Vector2f& anchor);
+
+ void SetDistance (float distance);
+ float GetDistance () const { return m_Distance; }
+
+ void SetDampingRatio (float ratio);
+ float GetDampingRatio () const { return m_DampingRatio; }
+
+ void SetFrequency (float frequency);
+ float GetFrequency () const { return m_Frequency; }
+
+protected:
+ virtual void Create ();
+ void AutoCalculateDistance ();
+
+protected:
+ float m_Distance; ///< The distance which the joint should attempt to maintain between attached bodies. range { 0.005, 1000000 }
+ float m_DampingRatio; ///< The damping ratio for the oscillation whilst trying to achieve the specified distance. 0 means no damping. 1 means critical damping. range { 0.0, 1.0 }
+ float m_Frequency; ///< The frequency in Hertz for the oscillation whilst trying to achieve the specified distance. range { 0.0, 1000000 }
+ Vector2f m_Anchor; ///< The local-space anchor from the base rigid-body.
+ Vector2f m_ConnectedAnchor; ///< The local-space anchor from the connected rigid-body.
+};
+
+#endif
diff --git a/Runtime/Physics2D/SpriteCollider2D.cpp b/Runtime/Physics2D/SpriteCollider2D.cpp
new file mode 100644
index 0000000..772bf0d
--- /dev/null
+++ b/Runtime/Physics2D/SpriteCollider2D.cpp
@@ -0,0 +1,91 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS && ENABLE_SPRITECOLLIDER
+
+#include "Runtime/Physics2D/SpriteCollider2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Filters/Mesh/SpriteRenderer.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+#include "External/libtess2/libtess2/tesselator.h"
+
+PROFILER_INFORMATION(gPhysics2DProfileSpriteColliderCreate, "Physics2D.SpriteColliderCreate", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileSpriteColliderDecomposition, "Physics2D.SpriteColliderDecomposition", kProfilerPhysics)
+
+IMPLEMENT_CLASS_INIT_ONLY (SpriteCollider2D)
+IMPLEMENT_OBJECT_SERIALIZE (SpriteCollider2D)
+
+
+// --------------------------------------------------------------------------
+
+
+static Polygon2D gEmptyPolygon2D;
+void SpriteCollider2D::InitializeClass()
+{
+ gEmptyPolygon2D.Clear();
+}
+
+
+SpriteCollider2D::SpriteCollider2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+
+SpriteCollider2D::~SpriteCollider2D ()
+{
+}
+
+
+template<class TransferFunction>
+void SpriteCollider2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER (m_Sprite);
+}
+
+
+void SpriteCollider2D::Reset ()
+{
+ Super::Reset ();
+
+ m_Sprite = NULL;
+}
+
+
+void SpriteCollider2D::SmartReset ()
+{
+ Super::SmartReset ();
+#if UNITY_EDITOR
+ GameObject* go = GetGameObjectPtr();
+ if (go)
+ {
+ SpriteRenderer* sr = go->QueryComponent(SpriteRenderer);
+ if (sr)
+ m_Sprite = sr->GetSprite();
+ }
+#endif
+}
+
+void SpriteCollider2D::SetSprite(PPtr<Sprite> sprite)
+{
+ if (m_Sprite != sprite)
+ {
+ m_Sprite = sprite;
+
+ Create();
+ SetDirty();
+ }
+}
+
+const Polygon2D& SpriteCollider2D::GetPoly() const
+{
+ return m_Sprite.IsNull() ? gEmptyPolygon2D : m_Sprite->GetPoly();
+}
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/SpriteCollider2D.h b/Runtime/Physics2D/SpriteCollider2D.h
new file mode 100644
index 0000000..948b296
--- /dev/null
+++ b/Runtime/Physics2D/SpriteCollider2D.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#if (ENABLE_2D_PHYSICS || DOXYGEN) && ENABLE_SPRITECOLLIDER
+
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Graphics/SpriteFrame.h"
+#include "Runtime/Physics2D/PolygonColliderBase2D.h"
+
+class Sprite;
+
+
+// --------------------------------------------------------------------------
+
+
+class SpriteCollider2D : public PolygonColliderBase2D
+{
+public:
+ REGISTER_DERIVED_CLASS (SpriteCollider2D, PolygonColliderBase2D)
+ DECLARE_OBJECT_SERIALIZE (SpriteCollider2D)
+
+ SpriteCollider2D (MemLabelId label, ObjectCreationMode mode);
+ static void InitializeClass();
+
+ virtual const Polygon2D& GetPoly() const;
+
+ virtual void Reset ();
+ virtual void SmartReset ();
+
+ PPtr<Sprite> GetSprite() const { return m_Sprite; }
+ void SetSprite(PPtr<Sprite> sprite);
+
+private:
+ PPtr<Sprite> m_Sprite;
+};
+
+#endif
diff --git a/Runtime/Profiler/CollectProfilerStats.cpp b/Runtime/Profiler/CollectProfilerStats.cpp
new file mode 100644
index 0000000..2efe7c3
--- /dev/null
+++ b/Runtime/Profiler/CollectProfilerStats.cpp
@@ -0,0 +1,193 @@
+#include "UnityPrefix.h"
+#include "CollectProfilerStats.h"
+#include "Runtime/Allocator/MemoryManager.h"
+
+#if ENABLE_PROFILER
+
+#if UNITY_OSX
+#include <mach/mach.h>
+#elif UNITY_WIN && !UNITY_WP8
+#include "Psapi.h"
+#elif UNITY_XENON
+#include "PlatformDependent/Xbox360/Source/XenonMemory.h"
+#endif
+
+#include "Runtime/Profiler/ProfilerStats.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/Filters/Deformation/SkinnedMeshFilter.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Profiler/ProfilerImpl.h"
+#include "Runtime/Profiler/TimeHelper.h"
+#include "Runtime/Audio/AudioManager.h"
+#include "Runtime/Profiler/MemoryProfilerStats.h"
+#include "MemoryProfiler.h"
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/Dynamics/PhysicsModule.h"
+#include "Runtime/Interfaces/IPhysics.h"
+#include "Runtime/Interfaces/IPhysics2D.h"
+#include "Runtime/Interfaces/IAudio.h"
+
+void CollectDrawStats (DrawStats& drawStats)
+{
+ // Read GFX Device and populate rendering statistics with obtained information
+ drawStats.drawCalls = GetGfxDevice().GetFrameStats().GetDrawStats().calls;
+ drawStats.triangles = GetGfxDevice().GetFrameStats().GetDrawStats().tris;
+ drawStats.vertices = GetGfxDevice().GetFrameStats().GetDrawStats().verts;
+
+ drawStats.batchedDrawCalls = GetGfxDevice().GetFrameStats().GetDrawStats().batchedCalls;
+ drawStats.batchedTriangles = GetGfxDevice().GetFrameStats().GetDrawStats().batchedTris;
+ drawStats.batchedVertices = GetGfxDevice().GetFrameStats().GetDrawStats().batchedVerts;
+
+ drawStats.shadowCasters = GetGfxDevice().GetFrameStats().GetClientStats().shadowCasters;
+
+ GetGfxDevice().GetFrameStats().AccumulateUsedTextureUsage();
+ drawStats.usedTextureCount = GetGfxDevice().GetFrameStats().GetDrawStats().usedTextureCount;
+ drawStats.usedTextureBytes = GetGfxDevice().GetFrameStats().GetDrawStats().usedTextureBytes;
+
+ drawStats.renderTextureCount = RenderTexture::GetCreatedRenderTextureCount();
+ drawStats.renderTextureBytes = RenderTexture::GetCreatedRenderTextureBytes();
+ drawStats.renderTextureStateChanges = GetGfxDevice().GetFrameStats().GetStateChanges().renderTexture;
+
+ drawStats.screenWidth = GetGfxDevice().GetFrameStats().GetMemoryStats().screenWidth;
+ drawStats.screenHeight = GetGfxDevice().GetFrameStats().GetMemoryStats().screenHeight;
+ drawStats.screenFSAA = GetGfxDevice().GetFrameStats().GetMemoryStats().screenFSAA;
+ drawStats.screenBytes = GetGfxDevice().GetFrameStats().GetMemoryStats().screenBytes;
+
+ drawStats.vboTotal = GetGfxDevice().GetTotalVBOCount();
+ drawStats.vboTotalBytes = GetGfxDevice().GetTotalVBOBytes();
+ drawStats.vboUploads = GetGfxDevice().GetFrameStats().GetStateChanges().vboUploads;
+ drawStats.vboUploadBytes = GetGfxDevice().GetFrameStats().GetStateChanges().vboUploadBytes;
+ drawStats.ibUploads = GetGfxDevice().GetFrameStats().GetStateChanges().ibUploads;
+ drawStats.ibUploadBytes = GetGfxDevice().GetFrameStats().GetStateChanges().ibUploadBytes;
+ drawStats.totalAvailableVRamMBytes = RoundfToInt(gGraphicsCaps.videoMemoryMB);
+
+ drawStats.visibleSkinnedMeshes = SkinnedMeshRenderer::GetVisibleSkinnedMeshRendererCount();
+}
+
+
+
+static void GatherObjectAllocationInformation(const dynamic_array<Object*>& objs, int& nbObjects, int& bytes)
+{
+ nbObjects = objs.size();
+ bytes = 0;
+ for (int i=0; i<nbObjects; i++)
+ bytes += objs[i]->GetRuntimeMemorySize();
+}
+
+void CollectMemoryAllocationStats(MemoryStats& memoryStats)
+{
+ GatherObjectAllocationInformation( GetMemoryProfilerStats().GetTextures(), memoryStats.textureCount, memoryStats.textureBytes);
+ GatherObjectAllocationInformation( GetMemoryProfilerStats().GetMeshes(), memoryStats.meshCount, memoryStats.meshBytes);
+ GatherObjectAllocationInformation( GetMemoryProfilerStats().GetMaterials(), memoryStats.materialCount, memoryStats.materialBytes);
+ GatherObjectAllocationInformation( GetMemoryProfilerStats().GetAnimationClips(), memoryStats.animationClipCount, memoryStats.animationClipBytes);
+ GatherObjectAllocationInformation( GetMemoryProfilerStats().GetAudioClips(), memoryStats.audioCount, memoryStats.audioBytes);
+ memoryStats.totalObjectsCount = Object::GetLoadedObjectCount();
+
+#if ENABLE_MEMORY_MANAGER
+
+ #if UNITY_XENON
+ size_t additionalUsedMemoryUnity = xenon::GetOtherMemoryAllocated();
+ size_t additionalUsedMemorySystem = 32 * 1024 * 1024; // OS reserved
+ #else
+ size_t additionalUsedMemoryUnity = 0;
+ size_t additionalUsedMemorySystem = 0;
+ #endif
+
+ memoryStats.bytesUsedProfiler = GetMemoryManager().GetAllocator(kMemProfiler)->GetAllocatedMemorySize();
+ memoryStats.bytesUsedFMOD = GetMemoryManager().GetAllocatedMemory(kMemFMOD);
+ memoryStats.bytesUsedUnity = GetUsedHeapSize() - memoryStats.bytesUsedProfiler - memoryStats.bytesUsedFMOD + additionalUsedMemoryUnity;
+ #if ENABLE_MONO
+ memoryStats.bytesUsedMono = mono_gc_get_used_size();
+ #else
+ memoryStats.bytesUsedMono = 0;
+ #endif
+ memoryStats.bytesUsedGFX = GetMemoryManager().GetRegisteredGFXDriverMemory();
+ memoryStats.bytesUsedTotal = memoryStats.bytesUsedUnity + memoryStats.bytesUsedMono + memoryStats.bytesUsedGFX + memoryStats.bytesUsedProfiler + additionalUsedMemorySystem;
+
+ memoryStats.bytesReservedProfiler = GetMemoryManager().GetAllocator(kMemProfiler)->GetReservedSizeTotal();
+ memoryStats.bytesReservedFMOD = GetMemoryManager().GetAllocatedMemory(kMemFMOD);
+ memoryStats.bytesReservedUnity = GetMemoryManager().GetTotalReservedMemory() - memoryStats.bytesReservedProfiler - memoryStats.bytesReservedFMOD + additionalUsedMemoryUnity;
+ #if ENABLE_MONO
+ memoryStats.bytesReservedMono = mono_gc_get_heap_size();
+ #else
+ memoryStats.bytesReservedMono = 0;
+ #endif
+ memoryStats.bytesReservedGFX = GetMemoryManager().GetRegisteredGFXDriverMemory();
+ memoryStats.bytesReservedTotal = memoryStats.bytesReservedUnity + memoryStats.bytesReservedMono + memoryStats.bytesReservedGFX + memoryStats.bytesReservedProfiler + additionalUsedMemorySystem;
+
+ memoryStats.assetCount = GetMemoryProfilerStats().GetAssetCount();
+ memoryStats.sceneObjectCount = GetMemoryProfilerStats().GetSceneObjectCount();
+ memoryStats.gameObjectCount = GetMemoryProfilerStats().GetGameObjectCount();
+ memoryStats.classCount = GetMemoryProfilerStats().GetClassCount();
+
+ memoryStats.bytesVirtual = systeminfo::GetUsedVirtualMemoryMB() * 1024*1024;
+
+ #if UNITY_WP8
+ memoryStats.bytesCommitedLimit = systeminfo::GetCommitedMemoryLimitMB() * 1024 * 1024;
+ memoryStats.bytesCommitedTotal = systeminfo::GetCommitedMemoryMB() * 1024 * 1024;
+ #else
+ memoryStats.bytesCommitedLimit = 0;
+ memoryStats.bytesCommitedTotal = 0;
+ #endif
+
+#if ENABLE_MEM_PROFILER
+ //memoryStats.memoryOverview = GetMemoryProfiler()->GetOverview();
+ #endif
+
+#endif // #if ENABLE_MEMORY_MANAGER
+}
+
+void CollectProfilerStats (AllProfilerStats& stats)
+{
+ CollectMemoryAllocationStats(stats.memoryStats);
+ CollectDrawStats(stats.drawStats);
+
+ UnityProfiler::Get().GetDebugStats(stats.debugStats);
+
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ audioModule->GetProfilerStats(stats.audioStats);
+
+ IPhysics* physicsModule = GetIPhysics();
+ if (physicsModule)
+ physicsModule->GetProfilerStats(stats.physicsStats);
+
+ IPhysics2D* physics2DModule = GetIPhysics2D ();
+ if (physics2DModule)
+ physics2DModule->GetProfilerStats (stats.physics2DStats);
+}
+
+ProfilerString GetMiniMemoryOverview()
+{
+#if ENABLE_MEMORY_MANAGER
+ return ("Allocated: " + FormatBytes(GetUsedHeapSize()) + " Objects: " + IntToString(Object::GetLoadedObjectCount ())).c_str();
+#else
+ return "";
+#endif
+}
+
+#endif
+
+unsigned GetUsedHeapSize()
+{
+#if ENABLE_MEMORY_MANAGER
+
+#if (UNITY_OSX && UNITY_EDITOR)
+ UInt32 osxversion = 0;
+ Gestalt(gestaltSystemVersion, (MacSInt32 *) &osxversion);
+ if(osxversion >= 0x01060)
+ return MemoryManager::m_LowLevelAllocated - GetMemoryManager().GetTotalUnusedReservedMemory();
+ else
+ return 0;
+#else
+ return GetMemoryManager().GetTotalAllocatedMemory();
+#endif
+
+#else
+ return 0;
+#endif
+}
diff --git a/Runtime/Profiler/CollectProfilerStats.h b/Runtime/Profiler/CollectProfilerStats.h
new file mode 100644
index 0000000..cf446a9
--- /dev/null
+++ b/Runtime/Profiler/CollectProfilerStats.h
@@ -0,0 +1,18 @@
+#ifndef _COLLECT_PROFILER_STATS_H_
+#define _COLLECT_PROFILER_STATS_H_
+
+#include "Configuration/UnityConfigure.h"
+
+struct AllProfilerStats;
+struct MemoryStats;
+
+#if ENABLE_PROFILER
+
+void CollectProfilerStats (AllProfilerStats& stats);
+ProfilerString GetMiniMemoryOverview();
+
+#endif
+
+unsigned GetUsedHeapSize();
+
+#endif \ No newline at end of file
diff --git a/Runtime/Profiler/DeprecatedFrameStatsProfiler.cpp b/Runtime/Profiler/DeprecatedFrameStatsProfiler.cpp
new file mode 100644
index 0000000..be04bcb
--- /dev/null
+++ b/Runtime/Profiler/DeprecatedFrameStatsProfiler.cpp
@@ -0,0 +1,23 @@
+#include "Runtime/Profiler/DeprecatedFrameStatsProfiler.h"
+#include "Runtime/Profiler/TimeHelper.h"
+
+#if UNITY_OSX || UNITY_IPHONE
+#include <mach/mach_time.h>
+#endif
+
+FrameStats::Timestamp GetTimestamp()
+{
+#if UNITY_OSX || UNITY_IPHONE
+ return mach_absolute_time();
+#elif UNITY_ANDROID
+ return START_TIME;
+#else
+ return (signed long long)(START_TIME);//GetTimeSinceStartup ()*1000000000.0);
+#endif
+}
+static FrameStats gFrameStats;
+
+FrameStats const& GetFrameStats()
+{
+ return gFrameStats;
+} \ No newline at end of file
diff --git a/Runtime/Profiler/DeprecatedFrameStatsProfiler.h b/Runtime/Profiler/DeprecatedFrameStatsProfiler.h
new file mode 100644
index 0000000..24eb11b
--- /dev/null
+++ b/Runtime/Profiler/DeprecatedFrameStatsProfiler.h
@@ -0,0 +1,28 @@
+#pragma once
+
+struct FrameStats
+{
+ typedef signed long long Timestamp;
+
+ void reset() {
+ fixedBehaviourManagerDt = 0;
+ fixedPhysicsManagerDt = 0;
+ dynamicBehaviourManagerDt = 0;
+ coroutineDt = 0;
+ skinMeshUpdateDt = 0;
+ animationUpdateDt = 0;
+ renderDt = 0;
+
+ fixedUpdateCount = 0;
+ }
+
+ Timestamp fixedBehaviourManagerDt;
+ Timestamp fixedPhysicsManagerDt;
+ Timestamp dynamicBehaviourManagerDt;
+ Timestamp coroutineDt;
+ Timestamp skinMeshUpdateDt;
+ Timestamp animationUpdateDt;
+ Timestamp renderDt;
+ int fixedUpdateCount;
+};
+FrameStats const& GetFrameStats(); \ No newline at end of file
diff --git a/Runtime/Profiler/ExternalGraphicsProfiler.h b/Runtime/Profiler/ExternalGraphicsProfiler.h
new file mode 100644
index 0000000..89b82cc
--- /dev/null
+++ b/Runtime/Profiler/ExternalGraphicsProfiler.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "Profiler.h"
+
+#if ENABLE_PROFILER
+
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+struct AutoGfxEventMainThread {
+ AutoGfxEventMainThread(const char* name) { GetGfxDevice().BeginProfileEvent(name); }
+ ~AutoGfxEventMainThread() { GetGfxDevice().EndProfileEvent(); }
+};
+#define PROFILER_AUTO_GFX(INFO, OBJECT_PTR) PROFILER_AUTO(INFO,OBJECT_PTR); AutoGfxEventMainThread _gfx_event(INFO.name);
+
+
+#else // no profiling compiled in
+
+# define PROFILER_AUTO_GFX(INFO, OBJECT_PTR)
+
+#endif
diff --git a/Runtime/Profiler/ExtractLoadedObjectInfo.cpp b/Runtime/Profiler/ExtractLoadedObjectInfo.cpp
new file mode 100644
index 0000000..aede649
--- /dev/null
+++ b/Runtime/Profiler/ExtractLoadedObjectInfo.cpp
@@ -0,0 +1,76 @@
+#include "UnityPrefix.h"
+#if ENABLE_PROFILER
+#include "ExtractLoadedObjectInfo.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Misc/GarbageCollectSharedAssets.h"
+
+LoadedObjectMemoryType GetLoadedObjectReason (Object* object)
+{
+ bool referencedOnlyByNativeData = false;
+
+ if(!object->GetCachedScriptingObject ())
+ referencedOnlyByNativeData = true;
+
+ bool isPersistent = object->IsPersistent();
+
+ // Builtin resource (Treated as root)
+ if (isPersistent)
+ {
+ string path = GetPersistentManager().GetPathName(object->GetInstanceID());
+ if (path == "library/unity editor resources" || path == "library/unity default resources")
+ {
+ return kBuiltinResource;
+ }
+ }
+
+ // Objects marked DontSave (Treated as root)
+ if (object->TestHideFlag(Object::kDontSave))
+ {
+ return kMarkedDontSave;
+ }
+
+ // Asset marked dirty in the editor (Treated as root)
+#if UNITY_EDITOR
+ if (isPersistent && object->IsPersistentDirty() && ShouldPersistentDirtyObjectBeKeptAlive(object->GetInstanceID()))
+ {
+ return kAssetMarkedDirtyInEditor;
+ }
+#endif
+
+ // gameobjects and components in the scene (treated as roots)
+ if (!isPersistent)
+ {
+ int classID = object->GetClassID();
+ if (classID == ClassID (GameObject))
+ {
+ return kSceneObject;
+ }
+ else if (Object::IsDerivedFromClassID(classID, ClassID(Component)))
+ {
+ Unity::Component* com = static_cast<Unity::Component*> (object);
+ if (com->GetGameObjectPtr())
+ return kSceneObject;
+ }
+ }
+
+ // Asset is being referenced from something, specify what
+ if (!isPersistent)
+ {
+ if (referencedOnlyByNativeData)
+ return kSceneAssetReferencedByNativeCodeOnly;
+ else
+ return kSceneAssetReferenced;
+ }
+ else
+ {
+ if (referencedOnlyByNativeData)
+ return kAssetReferencedByNativeCodeOnly;
+ else
+ return kAssetReferenced;
+ }
+
+ return kNotApplicable;
+}
+
+#endif
diff --git a/Runtime/Profiler/ExtractLoadedObjectInfo.h b/Runtime/Profiler/ExtractLoadedObjectInfo.h
new file mode 100644
index 0000000..50cd643
--- /dev/null
+++ b/Runtime/Profiler/ExtractLoadedObjectInfo.h
@@ -0,0 +1,26 @@
+#ifndef EXTRACT_LOADED_OBJECT_INFO_H
+#define EXTRACT_LOADED_OBJECT_INFO_H
+
+#if ENABLE_PROFILER
+
+// Enum is in sync with ProfilerAPI.txt GarbageCollectReason
+enum LoadedObjectMemoryType
+{
+ kSceneObject = 0,
+ kBuiltinResource = 1,
+ kMarkedDontSave = 2,
+ kAssetMarkedDirtyInEditor = 3,
+
+ kSceneAssetReferencedByNativeCodeOnly = 5,
+ kSceneAssetReferenced = 6,
+
+ kAssetReferencedByNativeCodeOnly = 8,
+ kAssetReferenced = 9,
+ kNotApplicable = 10
+};
+
+LoadedObjectMemoryType GetLoadedObjectReason (Object* object);
+
+#endif
+
+#endif
diff --git a/Runtime/Profiler/GPUProfiler.cpp b/Runtime/Profiler/GPUProfiler.cpp
new file mode 100644
index 0000000..c452853
--- /dev/null
+++ b/Runtime/Profiler/GPUProfiler.cpp
@@ -0,0 +1,100 @@
+#include "UnityPrefix.h"
+#if ENABLE_PROFILER
+
+#include "GPUProfiler.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/GfxDevice/GfxTimerQuery.h"
+#include "ProfilerImpl.h"
+#include "ProfilerFrameData.h"
+
+PROFILER_INFORMATION(gBeginQueriesProf, "GPUProfiler.BeginQueries", kProfilerOverhead)
+PROFILER_INFORMATION(gEndQueriesProf, "GPUProfiler.EndQueries", kProfilerOverhead)
+
+
+void GPUProfiler::GPUTimeSample()
+{
+ // GPU samples should only be added on the main thread.
+ DebugAssert (Thread::CurrentThreadIsMainThread ());
+
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ DebugAssert(prof);
+ if (!prof->GetIsActive() || !gGraphicsCaps.hasTimerQuery || gGraphicsCaps.buggyTimerQuery)
+ return;
+
+ GfxTimerQuery* timer = ProfilerFrameData::AllocTimerQuery();
+ timer->Measure();
+ ProfilerData::GPUTime sample = {prof->GetActiveSampleIndex(), timer, 0xFFFFFFFF, g_CurrentGPUSection};
+ prof->AddGPUSample(sample);
+}
+
+void GPUProfiler::BeginFrame()
+{
+ PROFILER_AUTO(gBeginQueriesProf, NULL);
+ GetGfxDevice().BeginTimerQueries();
+}
+
+void GPUProfiler::EndFrame()
+{
+ GPU_TIMESTAMP();
+ PROFILER_AUTO(gEndQueriesProf, NULL);
+ GetGfxDevice().EndTimerQueries();
+}
+
+bool GPUProfiler::CollectGPUTime( dynamic_array<ProfilerData::GPUTime>& gpuSamples, bool wait )
+{
+ if(!gGraphicsCaps.hasTimerQuery)
+ return false;
+
+ UInt32 flags = wait ? GfxTimerQuery::kWaitAll : GfxTimerQuery::kWaitRenderThread;
+
+ // Gather query times
+ for(int i = 0; i < gpuSamples.size(); i++)
+ {
+ ProfilerData::GPUTime& sample = gpuSamples[i];
+ if (sample.timerQuery != NULL)
+ {
+ ProfileTimeFormat elapsed = sample.timerQuery->GetElapsed(flags);
+ sample.gpuTimeInMicroSec = elapsed == kInvalidProfileTime? 0xFFFFFFFF: elapsed/1000;
+ if (wait || sample.gpuTimeInMicroSec != 0xFFFFFFFF)
+ {
+ // Recycle query object
+ ProfilerFrameData::ReleaseTimerQuery(sample.timerQuery);
+ sample.timerQuery = NULL;
+ }
+ }
+ }
+ return true;
+}
+
+int GPUProfiler::ComputeGPUTime( dynamic_array<ProfilerData::GPUTime>& gpuSamples )
+{
+ if (!CollectGPUTime(gpuSamples, true))
+ return 0;
+
+ // Why is the first sample invalid?
+ if (!gpuSamples.empty())
+ gpuSamples[0].gpuTimeInMicroSec = 0;
+
+ int totalTimeMicroSec = 0;
+ for(int i = 0; i < gpuSamples.size(); i++)
+ {
+ totalTimeMicroSec += gpuSamples[i].gpuTimeInMicroSec;
+ }
+ return totalTimeMicroSec;
+}
+
+void GPUProfiler::ClearTimerQueries ( dynamic_array<ProfilerData::GPUTime>& gpuSamples )
+{
+ for(int i = 0; i < gpuSamples.size(); i++)
+ {
+ ProfilerData::GPUTime& sample = gpuSamples[i];
+ if (sample.timerQuery != NULL)
+ {
+ // Recycle query object
+ ProfilerFrameData::ReleaseTimerQuery(sample.timerQuery);
+ sample.timerQuery = NULL;
+ }
+ }
+}
+
+#endif // ENABLE_PROFILER
diff --git a/Runtime/Profiler/GPUProfiler.h b/Runtime/Profiler/GPUProfiler.h
new file mode 100644
index 0000000..7d2bf17
--- /dev/null
+++ b/Runtime/Profiler/GPUProfiler.h
@@ -0,0 +1,24 @@
+#ifndef _GPUPROFILER_H_
+#define _GPUPROFILER_H_
+
+#if ENABLE_PROFILER
+
+struct ProfilerSample;
+#include "ProfilerImpl.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+class GPUProfiler
+{
+public:
+ static void BeginFrame();
+ static void EndFrame();
+
+ static void GPUTimeSample ( );
+
+ static bool CollectGPUTime ( dynamic_array<ProfilerData::GPUTime>& gpuSamples, bool wait );
+ static int ComputeGPUTime ( dynamic_array<ProfilerData::GPUTime>& gpuSamples);
+ static void ClearTimerQueries ( dynamic_array<ProfilerData::GPUTime>& gpuSamples );
+};
+
+#endif
+#endif
diff --git a/Runtime/Profiler/IntelGPAProfiler.cpp b/Runtime/Profiler/IntelGPAProfiler.cpp
new file mode 100644
index 0000000..8400d0b
--- /dev/null
+++ b/Runtime/Profiler/IntelGPAProfiler.cpp
@@ -0,0 +1,14 @@
+#include "UnityPrefix.h"
+#include "IntelGPAProfiler.h"
+
+#if INTEL_GPA_PROFILER_AVAILABLE
+
+__itt_domain* g_IntelGPADomain;
+
+void InitializeIntelGPAProfiler()
+{
+ g_IntelGPADomain = __itt_domain_createA("Unity.Player");
+}
+
+
+#endif
diff --git a/Runtime/Profiler/IntelGPAProfiler.h b/Runtime/Profiler/IntelGPAProfiler.h
new file mode 100644
index 0000000..bd538d0
--- /dev/null
+++ b/Runtime/Profiler/IntelGPAProfiler.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+
+#define INTEL_GPA_PROFILER_AVAILABLE (ENABLE_PROFILER && UNITY_WIN && !UNITY_EDITOR && !WEBPLUG && !UNITY_64 && !UNITY_WINRT)
+
+#if INTEL_GPA_PROFILER_AVAILABLE
+#include "External/IntelGPASDK/include/ittnotify.h"
+
+void InitializeIntelGPAProfiler();
+extern __itt_domain* g_IntelGPADomain;
+
+#define INTEL_GPA_INFORMATION_DATA __itt_string_handle* intelGPAData;
+#define INTEL_GPA_INFORMATION_INITIALIZE() intelGPAData = __itt_string_handle_create(functionName)
+#define INTEL_GPA_SAMPLE_BEGIN(info) __itt_task_begin(g_IntelGPADomain, __itt_null, __itt_null, (__itt_string_handle*) info->intelGPAData)
+#define INTEL_GPA_SAMPLE_END() __itt_task_end(g_IntelGPADomain)
+
+#else
+
+#define INTEL_GPA_INFORMATION_DATA
+#define INTEL_GPA_INFORMATION_INITIALIZE()
+#define INTEL_GPA_SAMPLE_BEGIN(info)
+#define INTEL_GPA_SAMPLE_END()
+
+#endif
diff --git a/Runtime/Profiler/MemoryProfiler.cpp b/Runtime/Profiler/MemoryProfiler.cpp
new file mode 100644
index 0000000..485712d
--- /dev/null
+++ b/Runtime/Profiler/MemoryProfiler.cpp
@@ -0,0 +1,1003 @@
+#include "UnityPrefix.h"
+
+#include "Runtime/Profiler/MemoryProfiler.h"
+#include "Runtime/Allocator/MemoryManager.h"
+
+#if ENABLE_MEM_PROFILER
+
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Utilities/BitUtility.h"
+
+#if RECORD_ALLOCATION_SITES
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Mono/MonoUtility.h"
+#endif
+
+#if !UNITY_EDITOR && ENABLE_STACKS_ON_ALL_ALLOCS
+#include "Runtime/Utilities/Stacktrace.cpp"
+#endif
+
+#include "Runtime/Threads/AtomicOps.h"
+
+#define ROOT_UNRELATED_ALLOCATIONS 0
+
+MemoryProfiler* MemoryProfiler::s_MemoryProfiler = NULL;
+UNITY_TLS_VALUE(ProfilerAllocationHeader**) MemoryProfiler::m_RootStack;
+UNITY_TLS_VALUE(UInt32) MemoryProfiler::m_RootStackSize;
+UNITY_TLS_VALUE(ProfilerAllocationHeader**) MemoryProfiler::m_CurrentRootHeader;
+UNITY_TLS_VALUE(bool) MemoryProfiler::m_RecordingAllocation;
+
+struct ProfilerAllocationHeader
+{
+ enum RootStatusMask
+ {
+ kIsRootAlloc = 0x1,
+ kRegisteredRoot = 0x2
+ };
+
+#if MAINTAIN_RELATED_ALLOCATION_LIST
+ // pointer to next allocation in root chain. Root is first element in this list
+ ProfilerAllocationHeader* next;
+ union
+ {
+ // pointer to prev allocation in root chain. Needed for removal
+ ProfilerAllocationHeader* prev;
+ // for Roots, the rootReference points to the AllocationRootReference,
+ AllocationRootReference* rootReference;
+ };
+
+#endif
+
+ volatile int accumulatedSize; // accumulated size of every allocation that relates to this one - just size if child alloc
+ ProfilerAllocationHeader* GetRootPtr() { return (ProfilerAllocationHeader*)((size_t)relatesTo&~0x3); }
+ void SetRootPtr (ProfilerAllocationHeader* ptr) { relatesTo = ptr; }
+ bool IsRoot () { return ((size_t)relatesTo&kIsRootAlloc)!=0; }
+ bool IsRegisteredRoot () { return ((size_t)relatesTo&kRegisteredRoot)!=0; }
+
+ void SetAsRegisteredRoot () { relatesTo = (ProfilerAllocationHeader*)(kIsRootAlloc|kRegisteredRoot); }
+
+ void SetAsRoot () { relatesTo = (ProfilerAllocationHeader*)(kIsRootAlloc); }
+
+ ProfilerAllocationHeader* relatesTo;
+
+#if RECORD_ALLOCATION_SITES
+ const MemoryProfiler::AllocationSite* site; // Site of where the allocation was done
+#endif
+};
+
+
+void MemoryProfiler::StaticInitialize()
+{
+ // This delayed initialization is necessary to avoid recursion, when TLS variables that are used
+ // by the profiler, are themselves managed by the memory manager.
+ MemoryProfiler* temp = new (MemoryManager::LowLevelAllocate(sizeof(MemoryProfiler))) MemoryProfiler();
+ temp->AllocateStructs();
+
+ s_MemoryProfiler = temp;
+}
+
+void MemoryProfiler::StaticDestroy()
+{
+ s_MemoryProfiler->~MemoryProfiler();
+ MemoryManager::LowLevelFree(s_MemoryProfiler);
+ s_MemoryProfiler = NULL;
+}
+
+#include "Runtime/Allocator/BaseAllocator.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Utilities/Stacktrace.h"
+
+MemoryProfiler::MemoryProfiler()
+: m_DefaultRootHeader (NULL)
+, m_SizeUsed (0)
+, m_NumAllocations (0)
+, m_AccSizeUsed (0)
+, m_AccNumAllocations (0)
+{
+ memset (m_SizeDistribution, 0, sizeof(m_SizeDistribution));
+}
+
+MemoryProfiler::~MemoryProfiler()
+{
+ m_RecordingAllocation = true;
+#if RECORD_ALLOCATION_SITES
+ UNITY_DELETE(m_AllocationSites,kMemMemoryProfiler);
+ UNITY_DELETE(m_AllocationSizes,kMemMemoryProfiler);
+#endif
+ UNITY_DELETE(m_ReferenceIDSizes,kMemMemoryProfiler);
+ UNITY_DELETE(m_RootAllocationTypes,kMemMemoryProfiler);
+
+ // using allocator directly to avoid allocation registration
+ BaseAllocator* alloc = GetMemoryManager().GetAllocator(kMemProfiler);
+ alloc->Deallocate(m_RootStack);
+ m_RecordingAllocation = false;
+}
+
+
+void MemoryProfiler::SetupAllocationHeader(ProfilerAllocationHeader* header, ProfilerAllocationHeader* root, int size)
+{
+ header->SetRootPtr(root);
+ header->accumulatedSize = size;
+#if MAINTAIN_RELATED_ALLOCATION_LIST
+ header->next = NULL;
+ header->prev = NULL;
+#endif
+#if RECORD_ALLOCATION_SITES
+ header->site = NULL;
+#endif
+}
+
+void MemoryProfiler::AllocateStructs()
+{
+ m_RecordingAllocation = true;
+ m_RootStackSize = 20;
+ // using allocator directly to avoid allocation registration
+ BaseAllocator* alloc = GetMemoryManager().GetAllocator(kMemProfiler);
+ m_RootStack = (ProfilerAllocationHeader**)alloc->Allocate(m_RootStackSize*sizeof(ProfilerAllocationHeader*), kDefaultMemoryAlignment);
+ m_CurrentRootHeader = &m_RootStack[0];
+ *m_CurrentRootHeader = NULL;
+
+#if RECORD_ALLOCATION_SITES
+ m_AllocationSites = UNITY_NEW(AllocationSites, kMemMemoryProfiler)();
+ m_AllocationSizes = UNITY_NEW(AllocationSizes,kMemMemoryProfiler)();
+#endif
+ m_ReferenceIDSizes = UNITY_NEW(ReferenceID,kMemMemoryProfiler)();
+ m_RootAllocationTypes = UNITY_NEW(RootAllocationTypes, kMemMemoryProfiler)();
+
+#if ROOT_UNRELATED_ALLOCATIONS
+ int* defaultRootContainer = UNITY_NEW(int, kMemProfiler);
+ m_DefaultRootHeader = GetMemoryManager().GetAllocator(kMemProfiler)->GetProfilerHeader(defaultRootContainer);
+ SetupAllocationHeader(m_DefaultRootHeader, NULL, sizeof(int));
+ RegisterRootAllocation(defaultRootContainer, GetMemoryManager().GetAllocator(kMemProfiler), "UnrootedAllocations", "");
+ *m_CurrentRootHeader = m_DefaultRootHeader;
+#endif
+
+ m_RecordingAllocation = false;
+}
+
+void MemoryProfiler::ThreadCleanup()
+{
+ m_RecordingAllocation = true;
+ if(m_RootStack)
+ {
+ // using allocator directly to avoid allocation registration
+ BaseAllocator* alloc = GetMemoryManager().GetAllocator(kMemProfiler);
+ alloc->Deallocate(m_RootStack);
+ }
+ m_RecordingAllocation = false;
+}
+
+void* g_LastAllocations[2];
+int g_LastAllocationIndex = 0;
+
+void MemoryProfiler::InitAllocation(void* ptr, BaseAllocator* alloc)
+{
+ Assert(!s_MemoryProfiler);
+ if (alloc)
+ {
+ ProfilerAllocationHeader* const header = alloc->GetProfilerHeader(ptr);
+ if (header)
+ {
+ size_t const size = alloc->GetPtrSize(ptr);
+ SetupAllocationHeader(header, NULL, size);
+ }
+ }
+}
+
+void MemoryProfiler::RegisterAllocation(void* ptr, MemLabelRef label, const char* file, int line, size_t allocsize)
+{
+ BaseAllocator* alloc = GetMemoryManager().GetAllocator(label);
+ size_t size = alloc ? alloc->GetPtrSize(ptr) : allocsize;
+
+#if RECORD_ALLOCATION_SITES
+ Mutex::AutoLock lock(m_Mutex);
+#endif
+ if (m_RecordingAllocation)
+ {
+ //mircea@ due to stupid init order, the gConsolePath std::string is not initialized when the assert triggers.
+ // we really really really really need to find a good solution to avoid shit like that!
+ //Assert(label.label == kMemMemoryProfilerId);
+ ProfilerAllocationHeader* header = alloc ? alloc->GetProfilerHeader(ptr) : NULL;
+ if(header)
+ {
+ SetupAllocationHeader(header, NULL, size);
+ }
+ return;
+ }
+ m_RecordingAllocation = true;
+
+ ProfilerAllocationHeader* root = label.GetRootHeader();
+
+ if(label.UseAutoRoot())
+ {
+ if(m_CurrentRootHeader != NULL)
+ root = *m_CurrentRootHeader;
+ else
+ root = NULL;
+ }
+
+#if RECORD_ALLOCATION_SITES
+ m_SizeUsed += size;
+ m_NumAllocations++;
+ m_AccSizeUsed += size;
+ m_AccNumAllocations++;
+ m_SizeDistribution[HighestBit(size)]++;
+
+ AllocationSite site;
+ site.label = label.label;
+ site.file = file;
+ site.line = line;
+ site.allocated = 0;
+ site.alloccount = 0;
+ site.ownedAllocated = 0;
+ site.ownedCount = 0;
+ site.cummulativeAllocated = 0;
+ site.cummulativeAlloccount = 0;
+#if ENABLE_STACKS_ON_ALL_ALLOCS
+ site.stack[0] = 0;
+ static volatile bool track = false;
+ //if(root && track)
+ {
+ // if(Thread::CurrentThreadIsMainThread())
+ {
+ //static int counter = 0;
+ //if(((counter++) & 0x100) == 0 || file == NULL)
+ {
+ SET_ALLOC_OWNER(NULL);
+ site.stackHash = GetStacktrace(site.stack, 20, 3);
+ }
+ }
+ }
+#endif
+ AllocationSites::iterator it = m_AllocationSites->find(site); // std::set will allocate on insertion (very rare)
+ if(it == m_AllocationSites->end())
+ it = m_AllocationSites->insert(site).first;
+ AllocationSite* mutablesite = const_cast<AllocationSite*>(&(*it));
+ mutablesite->allocated += size;
+ mutablesite->alloccount++;
+ mutablesite->cummulativeAllocated += size;
+ mutablesite->cummulativeAlloccount++;
+ if(root)
+ {
+ mutablesite->ownedAllocated += size;
+ mutablesite->ownedCount++;
+ }
+#endif
+
+ ProfilerAllocationHeader* header = alloc ? alloc->GetProfilerHeader(ptr) : NULL;
+ if(header)
+ {
+ SetupAllocationHeader(header, root, size);
+
+#if RECORD_ALLOCATION_SITES
+ header->site = &(*it);
+#endif
+
+ if(root != NULL)
+ {
+ // Find the root owner and add this to the allocation list and accumulated size
+ if(root)
+ {
+ AtomicAdd(&root->accumulatedSize, size);
+#if MAINTAIN_RELATED_ALLOCATION_LIST
+ InsertAfterRoot(root, header);
+#endif
+ }
+ }
+
+ }
+ else if(alloc) // no alloc present for stray mallocs
+ {
+#if RECORD_ALLOCATION_SITES
+ LocalHeaderInfo info = {size, &(*it)};
+ m_AllocationSizes->insert(std::make_pair(ptr, info)); // Will allocate
+#endif
+ }
+
+ g_LastAllocations[g_LastAllocationIndex]= ptr;
+ g_LastAllocationIndex ^= 1;
+
+ m_RecordingAllocation = false;
+}
+
+void MemoryProfiler::UnregisterAllocation(void* ptr, BaseAllocator* alloc, size_t freesize, ProfilerAllocationHeader** outputRootHeader, MemLabelRef label)
+{
+ if(ptr == NULL)
+ return;
+
+#if RECORD_ALLOCATION_SITES
+ Mutex::AutoLock lock(m_Mutex);
+#endif
+ if (m_RecordingAllocation)
+ return;
+
+ size_t size = alloc ? alloc->GetPtrSize(ptr) : freesize;
+ ProfilerAllocationHeader* header = alloc ? alloc->GetProfilerHeader(ptr) : NULL;
+#if RECORD_ALLOCATION_SITES
+ if(header && header->site == NULL)
+ return;
+#endif
+
+ ProfilerAllocationHeader* root = NULL;
+ m_RecordingAllocation = true;
+
+// Assert(!header || header->IsRoot() || header->GetRootPtr() == label.GetRootHeader());
+
+#if RECORD_ALLOCATION_SITES
+ const AllocationSite* site = NULL;
+ if(!alloc)
+ {
+ #if ENABLE_STACKS_ON_ALL_ALLOCS
+ AllocationSite tmpsite = {kMemLabelCount, {0}, 0, NULL, 0, 0, 0, 0, 0, 0, 0};
+ #else
+ AllocationSite tmpsite = {kMemLabelCount, NULL, 0, 0, 0, 0, 0, 0, 0};
+ #endif
+ AllocationSites::iterator it = m_AllocationSites->insert(tmpsite).first;
+ site = &(*it);
+ }
+ else if ( !header )
+ {
+ AllocationSizes::iterator itPtrSize = m_AllocationSizes->find(ptr);
+
+ #if UNITY_IPHONE
+ // oh hello osam apple. When we override global new - we override it for everything.
+ // BUT some libraries calls delete on pointers that were new-ed with system operator new
+ // effectively crashing here. So play safe, yes
+ if( itPtrSize == m_AllocationSizes->end() )
+ {
+ m_RecordingAllocation = false;
+ return kMemNewDeleteId;
+ }
+ #endif
+
+ Assert(itPtrSize != m_AllocationSizes->end());
+
+ size = (*itPtrSize).second.size;
+ site = (*itPtrSize).second.site;
+ m_AllocationSizes->erase(itPtrSize);
+ }
+#endif
+ if(header)
+ {
+ root = header->GetRootPtr();
+ if (header->GetRootPtr() || header->IsRoot())
+ {
+ if(header->IsRoot())
+ root = header;
+
+ int memoryleft = AtomicAdd(&root->accumulatedSize, -(int)size);
+ if (root == header)
+ {
+ // This is the root object. Check that all linked allocations are deleted. If not, unlink all.
+ if (memoryleft != 0)
+ {
+#if MAINTAIN_RELATED_ALLOCATION_LIST
+ Mutex::AutoLock lock(m_Mutex);
+ UnlinkAllAllocations(root);
+#else
+ ErrorString("Not all allocations related to a root has been deleted - might cause unity to crash later on!!");
+#endif
+ }
+ root->rootReference->root = NULL;
+ root->rootReference->Release();
+ root->rootReference = NULL;
+ }
+#if MAINTAIN_RELATED_ALLOCATION_LIST
+ UnlinkHeader(header);
+#endif
+ }
+ else
+ AtomicAdd(&header->accumulatedSize, -(int)size);
+#if RECORD_ALLOCATION_SITES
+ site = header->site;
+#endif
+ }
+
+#if RECORD_ALLOCATION_SITES
+ AllocationSite* mutablesite = const_cast<AllocationSite*>(site);
+ mutablesite->allocated -= size;
+ mutablesite->alloccount--;
+ if(root)
+ {
+ mutablesite->ownedAllocated -= size;
+ mutablesite->ownedCount--;
+ }
+ m_SizeUsed -= size;
+ m_NumAllocations--;
+ m_SizeDistribution[HighestBit(size)]--;
+#endif
+ // Roots are registered with their related data pointing to themselves.
+ if(header && header->IsRegisteredRoot() )
+ UnregisterRootAllocation (ptr);
+
+ if (outputRootHeader != NULL)
+ *outputRootHeader = root;
+
+ m_RecordingAllocation = false;
+ return;
+}
+
+void MemoryProfiler::TransferOwnership(void* ptr, BaseAllocator* alloc, ProfilerAllocationHeader* newRootHeader)
+{
+ Assert(alloc);
+ size_t size = alloc->GetPtrSize(ptr);
+ ProfilerAllocationHeader* header = alloc->GetProfilerHeader(ptr);
+
+ if(header)
+ {
+ ProfilerAllocationHeader* root = header->GetRootPtr();
+ if(root != NULL)
+ {
+#if MAINTAIN_RELATED_ALLOCATION_LIST
+ // Unlink from currentRoot
+ UnlinkHeader(header);
+#endif
+ AtomicAdd(&root->accumulatedSize, -(int)size);
+ header->SetRootPtr(NULL);
+#if RECORD_ALLOCATION_SITES
+ AllocationSite* mutablesite = const_cast<AllocationSite*>(header->site);
+ mutablesite->ownedAllocated -= size;
+ mutablesite->ownedCount--;
+#endif
+ }
+
+ if(newRootHeader == NULL)
+ newRootHeader = m_DefaultRootHeader;
+
+ // we have a new root. Relink
+ if(newRootHeader)
+ {
+ DebugAssert(newRootHeader->IsRoot());
+
+ AtomicAdd(&newRootHeader->accumulatedSize, size);
+#if MAINTAIN_RELATED_ALLOCATION_LIST
+ InsertAfterRoot(newRootHeader, header);
+#endif
+ header->SetRootPtr(newRootHeader);
+#if RECORD_ALLOCATION_SITES
+ AllocationSite* mutablesite = const_cast<AllocationSite*>(header->site);
+ mutablesite->ownedAllocated += size;
+ mutablesite->ownedCount++;
+#endif
+ }
+ }
+}
+
+#if MAINTAIN_RELATED_ALLOCATION_LIST
+void MemoryProfiler::UnlinkAllAllocations(ProfilerAllocationHeader* root)
+{
+#if ENABLE_STACKS_ON_ALL_ALLOCS
+ // Print stack for root and stacks for all unallocated child allocations
+ {
+ char buffer[4048];
+ void*const* s = root->site->stack;
+ GetReadableStackTrace(buffer, 4048, (void**)(s), 20);
+ printf_console(FormatString<TEMP_STRING>("Root still has %d allocated memory\nRoot allocated from\n%s\n",root->accumulatedSize, buffer).c_str());
+
+ ProfilerAllocationHeader* header = root->next;
+ while(header)
+ {
+ // for each allocation: print stack and decrease sites ownedcount
+ char buffer[4048];
+ void*const* s = header->site->stack;
+ GetReadableStackTrace(buffer, 4048, (void**)(s), 20);
+ printf_console(FormatString<TEMP_STRING>("%s\n", buffer).c_str());
+ AllocationSite* mutablesite = const_cast<AllocationSite*>(header->site);
+
+ mutablesite->ownedAllocated -= header->accumulatedSize;
+ mutablesite->ownedCount--;
+ header = header->next;
+ }
+ }
+#else
+// ErrorString("Not all allocations related to a root has been deleted - might cause unity to crash later on!!");
+#endif
+
+ // unlink all allocations
+ ProfilerAllocationHeader* header = root->next;
+ while(header)
+ {
+ ProfilerAllocationHeader* nextHeader = header->next;
+ UnlinkHeader(header);
+#if ROOT_UNRELATED_ALLOCATIONS
+ header->SetRootPtr(m_DefaultRootHeader); // set root owner
+ InsertAfterRoot(m_DefaultRootHeader, header);
+#else
+ header->SetRootPtr(NULL); // clear root owner
+#endif
+ header = nextHeader;
+ }
+ root->next = NULL;
+}
+
+void MemoryProfiler::InsertAfterRoot( ProfilerAllocationHeader* root, ProfilerAllocationHeader* header )
+{
+ DebugAssert(root->IsRoot());
+ Mutex::AutoLock lock(m_Mutex);
+ if(root->next)
+ root->next->prev = header;
+ header->next = root->next;
+ header->prev = root;
+ root->next = header;
+}
+
+void MemoryProfiler::UnlinkHeader(ProfilerAllocationHeader* header )
+{
+ Mutex::AutoLock lock(m_Mutex);
+
+ DebugAssert(header->prev == NULL || header->prev->next == header);
+ DebugAssert(header->next == NULL || header->next->prev == header);
+
+ if(header->prev)
+ header->prev->next = header->next;
+ if(header->next)
+ header->next->prev = header->prev;
+ header->prev = NULL;
+ header->next = NULL;
+}
+
+
+AllocationRootReference* MemoryProfiler::GetRootReferenceFromHeader(ProfilerAllocationHeader* root)
+{
+ if (root == NULL || root->rootReference == NULL || GetMemoryProfiler()->IsRecording())
+ return NULL;
+ Assert(root->IsRoot());
+ root->rootReference->Retain();
+ return root->rootReference;
+}
+
+#endif
+
+void MemoryProfiler::ValidateRoot(ProfilerAllocationHeader* root)
+{
+ if(!root)
+ return;
+ size_t accSize = 0;
+ Assert(root->IsRoot());
+ Assert(root->next == NULL || root->next->prev == root);
+ Assert(root->rootReference != NULL && root->rootReference->root != root);
+ ProfilerAllocationHeader* header = root->next;
+ while(header)
+ {
+ Assert (header->next == NULL || header->next->prev == header);
+ Assert (header->relatesTo == root);
+ accSize += header->accumulatedSize;
+ header = header->next;
+ }
+ Assert(root->accumulatedSize >= accSize);
+}
+
+bool MemoryProfiler::PushAllocationRoot(void* root, bool forcePush)
+{
+ ProfilerAllocationHeader** current_root_header = m_CurrentRootHeader;
+ if(current_root_header == NULL)
+ {
+ if (root == NULL)
+ return false;
+ m_RecordingAllocation = true;
+ m_RootStackSize = 10;
+ // using allocator directly to avoid allocation registration
+ BaseAllocator* alloc = GetMemoryManager().GetAllocator(kMemProfiler);
+ m_RootStack = (ProfilerAllocationHeader**)alloc->Allocate(m_RootStackSize*sizeof(ProfilerAllocationHeader*), kDefaultMemoryAlignment);
+ m_CurrentRootHeader = &m_RootStack[0];
+ *m_CurrentRootHeader = 0;
+ current_root_header = m_CurrentRootHeader;
+ m_RecordingAllocation = false;
+ }
+
+ ProfilerAllocationHeader* rootHeader = NULL;
+ if(root != NULL)
+ {
+ BaseAllocator* rootAlloc = GetMemoryManager().GetAllocatorContainingPtr(root);
+ Assert(rootAlloc);
+ rootHeader = (ProfilerAllocationHeader*)rootAlloc->GetProfilerHeader(root);
+ Assert(rootHeader == NULL || rootHeader->IsRoot());
+ }
+
+ if(!forcePush && rootHeader == *current_root_header)
+ return false;
+
+ UInt32 offset = m_CurrentRootHeader-m_RootStack;
+ if(offset == m_RootStackSize-1)
+ {
+ m_RecordingAllocation = true;
+ m_RootStackSize = m_RootStackSize*2;
+ // using allocator directly to avoid allocation registration
+ BaseAllocator* alloc = GetMemoryManager().GetAllocator(kMemProfiler);
+ m_RootStack = (ProfilerAllocationHeader**)alloc->Reallocate(&m_RootStack[0],m_RootStackSize*sizeof(ProfilerAllocationHeader*), kDefaultMemoryAlignment);
+ m_CurrentRootHeader = &m_RootStack[offset];
+ current_root_header = m_CurrentRootHeader;
+ m_RecordingAllocation = false;
+ }
+
+ *(++current_root_header) = rootHeader;
+ m_CurrentRootHeader = current_root_header;
+ return true;
+}
+
+void MemoryProfiler::PopAllocationRoot()
+{
+ --m_CurrentRootHeader;
+}
+
+ProfilerAllocationHeader* MemoryProfiler::GetCurrentRootHeader()
+{
+ ProfilerAllocationHeader* root = m_CurrentRootHeader ? *m_CurrentRootHeader : NULL;
+ return root;
+}
+
+int MemoryProfiler::GetHeaderSize()
+{
+ return sizeof(ProfilerAllocationHeader);
+}
+
+size_t MemoryProfiler::GetRelatedMemorySize(const void* ptr){
+ // this could use a linked list of related allocations, and thereby this info could be viewed as a hierarchy
+ BaseAllocator* alloc = GetMemoryManager().GetAllocatorContainingPtr(ptr);
+ ProfilerAllocationHeader* header = alloc ? alloc->GetProfilerHeader(ptr) : NULL;
+ return header ? header->accumulatedSize : 0;
+}
+
+void MemoryProfiler::RegisterMemoryToID( size_t id, size_t size )
+{
+ Mutex::AutoLock lock(m_Mutex);
+ m_RecordingAllocation = true;
+ ReferenceID::iterator itIDSize = m_ReferenceIDSizes->find(id);
+ if(itIDSize == m_ReferenceIDSizes->end())
+ m_ReferenceIDSizes->insert(std::make_pair(id,size));
+ else
+ itIDSize->second += size;
+ m_RecordingAllocation = false;
+}
+
+void MemoryProfiler::UnregisterMemoryToID( size_t id, size_t size )
+{
+ Mutex::AutoLock lock(m_Mutex);
+ m_RecordingAllocation = true;
+ ReferenceID::iterator itIDSize = m_ReferenceIDSizes->find(id);
+ if (itIDSize != m_ReferenceIDSizes->end())
+ {
+ itIDSize->second -= size;
+ if(itIDSize->second == 0)
+ m_ReferenceIDSizes->erase(itIDSize);
+ }
+ else
+ ErrorString("Id not found in map");
+ m_RecordingAllocation = false;
+}
+
+size_t MemoryProfiler::GetRelatedIDMemorySize(size_t id)
+{
+ Mutex::AutoLock lock(m_Mutex);
+ ReferenceID::iterator itIDSize = m_ReferenceIDSizes->find(id);
+ if(itIDSize == m_ReferenceIDSizes->end())
+ return 0;
+ return itIDSize->second;
+}
+
+
+ProfilerString MemoryProfiler::GetOverview() const
+{
+#if RECORD_ALLOCATION_SITES
+ struct MemCatInfo
+ {
+ UInt64 totalMem;
+ UInt64 totalCount;
+ UInt64 cummulativeMem;
+ UInt64 cummulativeCount;
+ };
+ MemCatInfo info[kMemLabelCount+1];
+ MemCatInfo allocatorMemUsage[16];
+ MemCatInfo totalMemUsage;
+ BaseAllocator* allocators[16];
+
+ memset (&info,0,sizeof(info));
+ memset (&allocatorMemUsage,0,sizeof(allocatorMemUsage));
+ memset (&totalMemUsage,0,sizeof(totalMemUsage));
+ memset (&allocators,0,sizeof(allocators));
+
+
+ AllocationSites::iterator it = m_AllocationSites->begin();
+ for( ;it != m_AllocationSites->end(); ++it)
+ {
+ MemLabelIdentifier label = (*it).label;
+ if (label >= kMemLabelCount)
+ label = kMemLabelCount;
+ info[label].totalMem += (*it).allocated;
+ info[label].totalCount += (*it).alloccount;
+ info[label].cummulativeMem += (*it).cummulativeAllocated;
+ info[label].cummulativeCount += (*it).cummulativeAlloccount;
+
+ MemLabelId memLabel(label, NULL);
+ BaseAllocator* alloc = GetMemoryManager().GetAllocator(memLabel);
+ int allocatorindex = GetMemoryManager().GetAllocatorIndex(alloc);
+ allocatorMemUsage[allocatorindex].totalMem += (*it).allocated;
+ allocatorMemUsage[allocatorindex].totalCount += (*it).alloccount;
+ allocatorMemUsage[allocatorindex].cummulativeMem += (*it).cummulativeAllocated;
+ allocatorMemUsage[allocatorindex].cummulativeCount += (*it).cummulativeAlloccount;
+ allocators[allocatorindex] = alloc;
+ totalMemUsage.totalMem += (*it).allocated;
+ totalMemUsage.totalCount += (*it).alloccount;
+ totalMemUsage.cummulativeMem += (*it).cummulativeAllocated;
+ totalMemUsage.cummulativeCount += (*it).cummulativeAlloccount;
+ }
+
+ const int kStringSize = 400*1024;
+ TEMP_STRING str;
+ str.reserve(kStringSize);
+ // Total memory registered in all allocators
+ str += FormatString<TEMP_STRING>("[ Total Memory ] : %0.2fMB ( %d ) [%0.2fMB ( %d )]\n\n", (float)(totalMemUsage.totalMem)/(1024.f*1024.f), totalMemUsage.totalCount,
+ (float)(totalMemUsage.cummulativeMem)/(1024.f*1024.f), totalMemUsage.cummulativeCount);
+
+ // Memory registered by allocators
+ for(int i = 0; i < 16; i++)
+ {
+ if (allocatorMemUsage[i].cummulativeCount == 0)
+ continue;
+ BaseAllocator* alloc = allocators[i];
+ str += FormatString<TEMP_STRING>("[ %s ] : %0.2fKB ( %d ) [acc: %0.2fMB ( %d )] (Requested:%0.2fKB, Overhead:%0.2fKB, Reserved:%0.2fMB)\n", (alloc?alloc->GetName():"Custom"), (float)(allocatorMemUsage[i].totalMem)/1024.f, allocatorMemUsage[i].totalCount,
+ (float)(allocatorMemUsage[i].cummulativeMem)/(1024.f*1024.f), allocatorMemUsage[i].cummulativeCount,(float) (alloc?alloc->GetAllocatedMemorySize():0)/1024.f, (float)((alloc?alloc->GetAllocatorSizeTotalUsed():0) - (alloc?alloc->GetAllocatedMemorySize():0))/1024.f, (alloc?alloc->GetReservedSizeTotal():0)/(1024.f*1024.f));
+ }
+
+ // Memory registered on labels
+ for(int i = 0; i <= kMemLabelCount; i++)
+ {
+ if (info[i].cummulativeCount == 0)
+ continue;
+ MemLabelId label((MemLabelIdentifier)i, NULL);
+ BaseAllocator* alloc = GetMemoryManager().GetAllocator(label);
+ str += FormatString<TEMP_STRING>("\n[ %s : %s ]\n", GetMemoryManager().GetMemcatName(label), alloc?alloc->GetName():"Custom");
+ str += FormatString<TEMP_STRING>(" TotalAllocated : %0.2fKB ( %d ) [%0.2fMB ( %d )]\n", (float)(info[i].totalMem)/1024.f, info[i].totalCount,
+ (float)(info[i].cummulativeMem)/(1024.f*1024.f), info[i].cummulativeCount);
+ }
+
+#if ENABLE_STACKS_ON_ALL_ALLOCS
+ const int kBufferSize = 8*1024;
+ char buffer[kBufferSize];
+ UNITY_VECTOR(kMemMemoryProfiler,const AllocationSite*) sortedVector;
+ sortedVector.reserve(m_AllocationSites->size());
+ it = m_AllocationSites->begin();
+ for( ;it != m_AllocationSites->end(); ++it)
+ sortedVector.push_back(&(*it));
+ std::sort(sortedVector.begin(), sortedVector.end(), AllocationSite::Sorter());
+ {
+ UNITY_VECTOR(kMemMemoryProfiler, const AllocationSite*)::iterator it = sortedVector.begin();
+ for( ;it != sortedVector.end(); ++it)
+ {
+ if(str.length()>kStringSize-kBufferSize)
+ break;
+ if( (*it)->alloccount == 0)
+ break;
+ str += FormatString<TEMP_STRING>("\n[ %s ]\n", GetMemoryManager().GetMemcatName(MemLabelId((*it)->label, NULL)));
+ if((*it)->stack[0] != 0)
+ {
+ GetReadableStackTrace(buffer, kBufferSize, (void**)((*it)->stack), 20);
+ str += FormatString<TEMP_STRING>("%s\n", buffer);
+ }
+ else
+ str += FormatString<TEMP_STRING>("[ %s:%d ]\n", (*it)->file, (*it)->line);
+ //, (*it).file);
+ str += FormatString<TEMP_STRING>(" TotalAllocated : %0.2fKB ( %d ) [%0.2fMB ( %d )]\n", (float)((*it)->allocated)/1024.f, (*it)->alloccount,
+ (float)((*it)->cummulativeAllocated)/(1024.f*1024.f), (*it)->cummulativeAlloccount);
+ }
+ }
+#endif
+ return ProfilerString(str.c_str());
+#else
+ return ProfilerString();
+#endif
+}
+
+#if RECORD_ALLOCATION_SITES
+struct MonoObjectMemoryStackInfo
+{
+ bool expanded;
+ bool sorted;
+ int allocated;
+ int ownedAllocated;
+ MonoArray* callerSites;
+ ScriptingStringPtr name;
+};
+
+MonoObject* MemoryProfiler::MemoryStackEntry::Deserialize()
+{
+ MonoClass* klass = GetMonoManager().GetMonoClass ("ObjectMemoryStackInfo", "UnityEditorInternal");
+ MonoObject* obj = mono_object_new (mono_domain_get(), klass);
+
+ MonoObjectMemoryStackInfo& memInfo = ExtractMonoObjectData<MonoObjectMemoryStackInfo> (obj);
+ memInfo.expanded = false;
+ memInfo.sorted = false;
+ memInfo.allocated = totalMemory;
+ memInfo.ownedAllocated = ownedMemory;
+ string tempname (name.c_str(),name.size()-2);
+ memInfo.name = MonoStringNew(tempname);
+ memInfo.callerSites = mono_array_new(mono_domain_get(), klass, callerSites.size());
+
+ std::map<void*,MemoryStackEntry>::iterator it = callerSites.begin();
+ int index = 0;
+ while(it != callerSites.end())
+ {
+ MonoObject* child = (*it).second.Deserialize();
+ GetMonoArrayElement<MonoObject*> (memInfo.callerSites,index++) = child;
+ ++it;
+ }
+ return obj;
+}
+
+MemoryProfiler::MemoryStackEntry* MemoryProfiler::GetStackOverview() const
+{
+ m_RecordingAllocation = true;
+ MemoryStackEntry* topLevel = UNITY_NEW(MemoryStackEntry,kMemProfiler);
+ topLevel->name = "Allocated unity memory ";
+ AllocationSites::iterator it = m_AllocationSites->begin();
+ for( ;it != m_AllocationSites->end(); ++it)
+ {
+ int size = (*it).allocated;
+ if(size == 0)
+ continue;
+ int ownedsize = (*it).ownedAllocated;
+ void*const* stack = &(*it).stack[0];
+ MemoryStackEntry* currentLevel = topLevel;
+ currentLevel->totalMemory += size;
+ currentLevel->ownedMemory += ownedsize;
+ while(*stack)
+ {
+ currentLevel = &(currentLevel->callerSites[*stack]);
+ if(currentLevel->totalMemory == 0)
+ {
+ char buffer[2048];
+ void* temp[2];
+ temp[0] = *stack;
+ temp[1] = 0;
+ GetReadableStackTrace(buffer, 2048, (void**)temp, 1);
+ currentLevel->name = std::string(buffer,40);
+ }
+ currentLevel->totalMemory += size;
+ currentLevel->ownedMemory += ownedsize;
+ ++stack;
+ }
+ }
+ m_RecordingAllocation = false;
+ return topLevel;
+}
+
+void MemoryProfiler::ClearStackOverview(MemoryProfiler::MemoryStackEntry* entry) const
+{
+ m_RecordingAllocation = true;
+ UNITY_DELETE(entry, kMemProfiler);
+ m_RecordingAllocation = false;
+}
+#endif
+
+void MemoryProfiler::RegisterRootAllocation (void* root, BaseAllocator* allocator, const char* areaName, const char* objectName)
+{
+ bool result = true;
+ if(areaName != NULL)
+ {
+ m_Mutex.Lock();
+ m_RecordingAllocation = true;
+
+ RootAllocationType typeData;
+ typeData.areaName = areaName;
+ typeData.objectName = objectName;
+ result = m_RootAllocationTypes->insert(std::make_pair (root, typeData)).second;
+
+ m_RecordingAllocation = false;
+ m_Mutex.Unlock();
+ }
+ if (result)
+ {
+ ProfilerAllocationHeader* header = allocator->GetProfilerHeader(root);
+ if(header)
+ {
+ if (!header->rootReference)
+ {
+ m_RecordingAllocation = true;
+ header->rootReference = UNITY_NEW(AllocationRootReference, kMemProfiler) (header);
+ m_RecordingAllocation = false;
+ }
+ if(areaName)
+ header->SetAsRegisteredRoot();
+ else
+ header->SetAsRoot();
+ }
+ }
+ else
+ {
+ ErrorString("Registered allocation root already exists");
+ }
+}
+
+void MemoryProfiler::UnregisterRootAllocation (void* root)
+{
+ m_Mutex.Lock();
+ m_RecordingAllocation = true;
+ bool result = m_RootAllocationTypes->erase(root);
+ m_RecordingAllocation = false;
+ m_Mutex.Unlock();
+
+ if (!result)
+ {
+ ErrorString("Allocation root has already been deleted");
+ }
+}
+
+void MemoryProfiler::SetRootAllocationObjectName (void* root, const char* objectName)
+{
+ Mutex::AutoLock lock (m_Mutex);
+ m_RecordingAllocation = true;
+ RootAllocationTypes::iterator it = m_RootAllocationTypes->find(root);
+ Assert(it != m_RootAllocationTypes->end());
+ (*it).second.objectName = objectName;
+ m_RecordingAllocation = false;
+}
+
+void MemoryProfiler::GetRootAllocationInfos (RootAllocationInfos& infos)
+{
+ Mutex::AutoLock lock (m_Mutex);
+ m_RecordingAllocation = true;
+
+ int index = infos.size();
+ infos.resize_uninitialized(infos.size() + m_RootAllocationTypes->size());
+ for (RootAllocationTypes::const_iterator i=m_RootAllocationTypes->begin();i != m_RootAllocationTypes->end();++i)
+ {
+ RootAllocationInfo& info = infos[index++];
+ info.memorySize = GetRelatedMemorySize(i->first);
+ info.areaName = i->second.areaName;
+ info.objectName = i->second.objectName.c_str();
+ }
+ m_RecordingAllocation = false;
+}
+
+ProfilerAllocationHeader* MemoryProfiler::GetAllocationRootHeader(void* ptr, MemLabelRef label) const
+{
+ BaseAllocator* alloc = GetMemoryManager().GetAllocator(label);
+ ProfilerAllocationHeader* header = alloc?alloc->GetProfilerHeader(ptr):NULL;
+ return header?header->GetRootPtr(): NULL;
+}
+
+
+#if RECORD_ALLOCATION_SITES
+ProfilerString MemoryProfiler::GetUnrootedAllocationsOverview()
+{
+ ProfilerString result;
+#if ROOT_UNRELATED_ALLOCATIONS
+ m_RecordingAllocation = true;
+ std::map<const AllocationSite*,size_t> allocations;
+ ProfilerAllocationHeader* next = m_DefaultRootHeader->next;
+ while(next)
+ {
+ if(!next->IsRoot())
+ {
+ Assert(next->relatesTo == m_DefaultRootHeader);
+ allocations[next->site]+=next->accumulatedSize;
+ }
+ next = next->next;
+ }
+ std::vector<std::pair<const AllocationSite*,size_t> > sorted;
+ std::map<const AllocationSite*,size_t>::iterator it = allocations.begin();
+ for( ;it != allocations.end(); ++it)
+ {
+ sorted.push_back(*it);
+ }
+ std::sort(sorted.begin(), sorted.end(), AllocationSiteSizeSorter());
+ for(int i = 0;i < sorted.size(); ++i)
+ {
+ int size = sorted[i].second;
+ printf_console("%db\n", size);
+ char buffer[2048];
+ GetReadableStackTrace(buffer, 2048, (void**)sorted[i].first->stack, 10);
+ printf_console("%s\n", buffer);
+ if(i > 20)
+ break;
+ }
+ m_RecordingAllocation = false;
+#endif
+ return result;
+}
+
+#endif
+
+
+#endif
+
diff --git a/Runtime/Profiler/MemoryProfiler.h b/Runtime/Profiler/MemoryProfiler.h
new file mode 100644
index 0000000..a7c0727
--- /dev/null
+++ b/Runtime/Profiler/MemoryProfiler.h
@@ -0,0 +1,237 @@
+#pragma once
+
+#include "Runtime/Allocator/MemoryMacros.h"
+
+#if ENABLE_MEM_PROFILER
+
+#define RECORD_ALLOCATION_SITES 0
+#define ENABLE_STACKS_ON_ALL_ALLOCS 0
+#define MAINTAIN_RELATED_ALLOCATION_LIST 1
+
+#if ENABLE_STACKS_ON_ALL_ALLOCS
+ #undef RECORD_ALLOCATION_SITES
+ #define RECORD_ALLOCATION_SITES 1
+#endif
+
+
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Threads/ThreadSpecificValue.h"
+#include "Runtime/Threads/AtomicRefCounter.h"
+#include <map>
+#include "Runtime/Utilities/MemoryPool.h"
+
+struct MonoObject;
+// header for all allocations
+
+struct AllocationRootReference
+{
+ AllocationRootReference(ProfilerAllocationHeader* root): root(root) {}
+
+ void Retain() { m_Counter.Retain(); }
+ void Release()
+ {
+ if(m_Counter.Release())
+ delete_internal(this, kMemProfiler);
+ }
+
+ AtomicRefCounter m_Counter;
+ ProfilerAllocationHeader* root;
+};
+
+
+class MemoryProfiler
+{
+public:
+ MemoryProfiler();
+ ~MemoryProfiler();
+
+ static void StaticInitialize();
+ static void StaticDestroy();
+
+ void ThreadCleanup();
+
+ static void InitAllocation(void* ptr, BaseAllocator* alloc);
+ void RegisterAllocation(void* ptr, MemLabelRef label, const char* file, int line, size_t size = 0);
+ void UnregisterAllocation(void* ptr, BaseAllocator* alloc, size_t size, ProfilerAllocationHeader** rootHeader, MemLabelRef label);
+
+ void TransferOwnership(void* ptr, BaseAllocator* alloc, ProfilerAllocationHeader* newRootHeader);
+
+ static int GetHeaderSize();
+
+ ProfilerString GetOverview() const;
+
+ ProfilerString GetUnrootedAllocationsOverview();
+ ProfilerAllocationHeader* GetAllocationRootHeader(void* ptr, MemLabelRef label) const;
+
+ // Roots allocations can be created using UNITY_NEW_AS_ROOT.
+ // They automatically show up in the memory profiler as a seperate section.
+ struct RootAllocationInfo
+ {
+ const char* areaName;
+ const char* objectName;
+ size_t memorySize;
+ };
+ typedef dynamic_array<RootAllocationInfo> RootAllocationInfos;
+
+ void RegisterRootAllocation (void* root, BaseAllocator* allocator, const char* areaName, const char* objectName);
+ void UnregisterRootAllocation (void* root);
+ void GetRootAllocationInfos (RootAllocationInfos& infos);
+ void SetRootAllocationObjectName (void* root, const char* objectName);
+
+ static AllocationRootReference* GetRootReferenceFromHeader(ProfilerAllocationHeader* root);
+
+ bool PushAllocationRoot(void* root, bool forcePush);
+ void PopAllocationRoot();
+ ProfilerAllocationHeader* GetCurrentRootHeader();
+
+ void RegisterMemoryToID(size_t id, size_t size);
+ void UnregisterMemoryToID( size_t id, size_t size );
+ size_t GetRelatedIDMemorySize(size_t id);
+
+ size_t GetRelatedMemorySize(const void* ptr);
+
+ static bool IsRecording() {return m_RecordingAllocation;}
+
+
+ struct MemoryStackEntry
+ {
+ MemoryStackEntry():totalMemory(0),ownedMemory(0){}
+ MonoObject* Deserialize();
+ int totalMemory;
+ int ownedMemory;
+ std::map<void*, MemoryStackEntry> callerSites;
+ std::string name;
+ };
+ MemoryStackEntry* GetStackOverview() const;
+ void ClearStackOverview(MemoryStackEntry* entry) const;
+
+ static MemoryProfiler* s_MemoryProfiler;
+
+ void AllocateStructs();
+ struct AllocationSite;
+private:
+
+ static void SetupAllocationHeader(ProfilerAllocationHeader* header, ProfilerAllocationHeader* root, int size);
+ void ValidateRoot(ProfilerAllocationHeader* root);
+
+#if MAINTAIN_RELATED_ALLOCATION_LIST
+ void UnlinkAllAllocations(ProfilerAllocationHeader* root);
+ void InsertAfterRoot( ProfilerAllocationHeader* root, ProfilerAllocationHeader* header );
+ void UnlinkHeader(ProfilerAllocationHeader* header);
+#endif
+
+ static UNITY_TLS_VALUE(bool) m_RecordingAllocation;
+
+ Mutex m_Mutex;
+
+ size_t m_SizeUsed;
+ UInt32 m_NumAllocations;
+
+ size_t m_AccSizeUsed;
+ UInt64 m_AccNumAllocations;
+
+ UInt32 m_SizeDistribution[32];
+
+ size_t m_InternalMemoryUsage;
+
+ typedef std::pair< size_t, size_t> ReferenceIDPair;
+ typedef STL_ALLOCATOR( kMemMemoryProfiler, ReferenceIDPair ) ReferenceIDAllocator;
+ typedef std::map< size_t, size_t, std::less<size_t>, ReferenceIDAllocator > ReferenceID;
+ ReferenceID* m_ReferenceIDSizes;
+
+ struct RootAllocationType
+ {
+ const char* areaName;
+ ProfilerString objectName;
+ };
+
+ typedef std::pair< void*, RootAllocationType> RootAllocationTypePair;
+ typedef STL_ALLOCATOR( kMemMemoryProfiler, RootAllocationTypePair ) AllocationRootTypeAllocator;
+
+ typedef std::map< void*, RootAllocationType, std::less<void*>, AllocationRootTypeAllocator > RootAllocationTypes;
+ RootAllocationTypes* m_RootAllocationTypes;
+
+ static UNITY_TLS_VALUE(ProfilerAllocationHeader**) m_RootStack;
+ static UNITY_TLS_VALUE(UInt32) m_RootStackSize;
+ static UNITY_TLS_VALUE(ProfilerAllocationHeader**) m_CurrentRootHeader;
+
+ ProfilerAllocationHeader* m_DefaultRootHeader;
+
+#if RECORD_ALLOCATION_SITES
+public:
+ struct AllocationSite
+ {
+ MemLabelIdentifier label;
+#if ENABLE_STACKS_ON_ALL_ALLOCS
+ void* stack[20];
+ UInt32 stackHash;
+#endif
+ const char* file;
+ int line;
+ int allocated;
+ int alloccount;
+ int ownedAllocated;
+ int ownedCount;
+ size_t cummulativeAllocated;
+ size_t cummulativeAlloccount;
+
+ bool operator()(const AllocationSite& s1, const AllocationSite& s2) const
+ {
+ return s1.line != s2.line ? s1.line < s2.line :
+ s1.label != s2.label ? s1.label < s2.label:
+#if ENABLE_STACKS_ON_ALL_ALLOCS
+ s1.stackHash != s2.stackHash? s1.stackHash< s2.stackHash:
+#endif
+ s1.file < s2.file ;
+ }
+
+ struct Sorter
+ {
+ bool operator()( const AllocationSite* a, const AllocationSite* b ) const
+ {
+ return a->allocated > b->allocated;
+ }
+ };
+ };
+ typedef std::set<AllocationSite, AllocationSite, STL_ALLOCATOR(kMemMemoryProfiler, AllocationSite) > AllocationSites;
+ AllocationSites* m_AllocationSites;
+private:
+ struct LocalHeaderInfo
+ {
+ size_t size;
+ const AllocationSite* site;
+ };
+
+ // map used for headers for allocations that don't support profileheaders
+ typedef std::pair< void* const, LocalHeaderInfo> SiteHeaderPair;
+ typedef STL_ALLOCATOR( kMemMemoryProfiler, SiteHeaderPair ) MapAllocator;
+ typedef std::map< void*, LocalHeaderInfo, std::less<void*>, MapAllocator > AllocationSizes;
+ AllocationSizes* m_AllocationSizes;
+#endif
+
+ struct AllocationSiteSizeSorter {
+ bool operator()( const std::pair<const AllocationSite*,size_t>& a, const std::pair<const AllocationSite*,size_t>& b ) const
+ {
+ return a.second > b.second;
+ }
+ };
+};
+
+inline MemoryProfiler* GetMemoryProfiler()
+{
+ return MemoryProfiler::s_MemoryProfiler;
+}
+
+#else
+
+class MemoryProfiler
+{
+public:
+ static int GetHeaderSize() { return 0; }
+ static void SetInLLAllocator (bool inLowLevelAllocator) { }
+ size_t GetRelatedMemorySize(const void* ptr) { return 0;}
+ static bool IsRecording() {return false;}
+};
+
+#endif
+
diff --git a/Runtime/Profiler/MemoryProfilerStats.cpp b/Runtime/Profiler/MemoryProfilerStats.cpp
new file mode 100644
index 0000000..9e7e7e9
--- /dev/null
+++ b/Runtime/Profiler/MemoryProfilerStats.cpp
@@ -0,0 +1,158 @@
+#if ENABLE_PROFILER
+
+#include "UnityPrefix.h"
+#include "MemoryProfilerStats.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Threads/AtomicOps.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Serialize/PersistentManager.h"
+
+void profiler_register_object(Object* obj)
+{
+ GetMemoryProfilerStats().RegisterObject(obj);
+}
+
+void profiler_unregister_object(Object* obj)
+{
+ GetMemoryProfilerStats().UnregisterObject(obj);
+}
+
+void profiler_change_persistancy(int instanceID, bool oldvalue, bool newvalue)
+{
+ GetMemoryProfilerStats().ChangePersistancyflag(instanceID, oldvalue, newvalue);
+}
+
+
+void TestAndInsertObject(Object* obj, int objClassID, int classID, dynamic_array<Object*>& objs)
+{
+ if (objClassID == classID)
+ objs.push_back(obj);
+}
+
+void TestAndRemoveObject(Object* obj, int objClassID, int classID, dynamic_array<Object*>& objs)
+{
+ if (objClassID == classID)
+ {
+ // run from the end - last created objects are most likely to be destoyed
+ dynamic_array<Object*>::iterator it = objs.end();
+ while(it != objs.begin())
+ {
+ --it;
+ if(*it == obj){
+ objs.erase(it, it+1);
+ return;
+ }
+ }
+ ErrorString(Format("An object that was removed, was not found in the object list for that type (%s)", Object::ClassIDToString(classID).c_str()));
+ }
+}
+
+void MemoryProfilerStats::ChangePersistancyflag(int instanceID, bool oldvalue, bool newvalue)
+{
+ if(oldvalue == newvalue)
+ return;
+#if SUPPORT_THREADS
+ if(!Thread::EqualsCurrentThreadID(GetPersistentManager().GetMainThreadID()))
+ return;
+#endif
+ Object* obj = Object::IDToPointer(instanceID);
+ if(obj == NULL)
+ return;
+
+ if(oldvalue == true)
+ {
+ AtomicDecrement(&assetCount);
+ AddDynamicObjectCount(obj, obj->GetClassID());
+ }
+ else
+ {
+ AtomicIncrement(&assetCount);
+ RemoveDynamicObjectCount(obj, obj->GetClassID());
+ }
+}
+
+void MemoryProfilerStats::AddDynamicObjectCount(Object* obj, int classID)
+{
+ AtomicIncrement(&sceneObjectCount);
+ if( classID == ClassID(GameObject) )
+ AtomicIncrement(&gameObjectCount);
+}
+
+void MemoryProfilerStats::RemoveDynamicObjectCount(Object* obj, int classID)
+{
+ AtomicDecrement(&sceneObjectCount);
+ if( classID == ClassID(GameObject) )
+ AtomicDecrement(&gameObjectCount);
+}
+
+void MemoryProfilerStats::RegisterObject ( Object* obj )
+{
+ int classID = obj->GetClassID();
+
+ TestAndInsertObject(obj, classID, ClassID(Texture2D), textures);
+ TestAndInsertObject(obj, classID, ClassID(Mesh), meshes);
+ TestAndInsertObject(obj, classID, ClassID(Material), materials);
+ TestAndInsertObject(obj, classID, ClassID(AnimationClip), animations);
+ TestAndInsertObject(obj, classID, ClassID(AudioClip), audioclips);
+
+ if(classCount.size() <= classID)
+ classCount.resize_initialized(classID+1,0);
+ ++classCount[classID];
+
+ if(obj->IsPersistent())
+ AtomicIncrement(&assetCount);
+ else
+ AddDynamicObjectCount(obj, classID);
+}
+
+void MemoryProfilerStats::UnregisterObject ( Object* obj )
+{
+ int classID = obj->GetClassID();
+ TestAndRemoveObject(obj, classID, ClassID(Texture2D), textures);
+ TestAndRemoveObject(obj, classID, ClassID(Mesh), meshes);
+ TestAndRemoveObject(obj, classID, ClassID(Material), materials);
+ TestAndRemoveObject(obj, classID, ClassID(AnimationClip), animations);
+ TestAndRemoveObject(obj, classID, ClassID(AudioClip), audioclips);
+
+ Assert (classCount.size() > classID);
+ --classCount[classID];
+
+ if(obj->IsPersistent())
+ AtomicDecrement(&assetCount);
+ else
+ RemoveDynamicObjectCount(obj, classID);
+}
+
+MemoryProfilerStats::MemoryProfilerStats()
+: assetCount(0)
+, sceneObjectCount(0)
+, gameObjectCount(0)
+{
+}
+
+MemoryProfilerStats::~MemoryProfilerStats()
+{
+}
+
+
+MemoryProfilerStats* gMemoryProfilerStats = NULL;
+MemoryProfilerStats& GetMemoryProfilerStats()
+{
+ Assert(gMemoryProfilerStats != NULL);
+ return *gMemoryProfilerStats;
+}
+
+void InitializeMemoryProfilerStats()
+{
+ Assert(gMemoryProfilerStats == NULL);
+ gMemoryProfilerStats = new MemoryProfilerStats();
+}
+
+void CleanupMemoryProfilerStats()
+{
+ Assert(gMemoryProfilerStats != NULL);
+ delete gMemoryProfilerStats;
+ gMemoryProfilerStats = NULL;
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/Profiler/MemoryProfilerStats.h b/Runtime/Profiler/MemoryProfilerStats.h
new file mode 100644
index 0000000..a148484
--- /dev/null
+++ b/Runtime/Profiler/MemoryProfilerStats.h
@@ -0,0 +1,75 @@
+#ifndef _MEMORY_PROFILER_STATS_H_
+#define _MEMORY_PROFILER_STATS_H_
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+
+#if ENABLE_PROFILER
+
+
+class Object;
+
+class MemoryProfilerStats
+{
+public:
+ MemoryProfilerStats();
+ ~MemoryProfilerStats();
+
+ void RegisterObject ( Object* obj );
+ void UnregisterObject ( Object* obj );
+ void ChangePersistancyflag (int instanceID, bool oldvalue, bool newvalue);
+
+ typedef dynamic_array<Object*> ObjectVector;
+ const ObjectVector& GetTextures() const {return textures;}
+ const ObjectVector& GetMeshes() const {return meshes;}
+ const ObjectVector& GetMaterials() const {return materials;}
+ const ObjectVector& GetAnimationClips() const {return animations;}
+ const ObjectVector& GetAudioClips() const {return audioclips;}
+
+ const dynamic_array<int>& GetClassCount() const {return classCount;}
+
+ int GetAssetCount() const {return assetCount;}
+ int GetSceneObjectCount() const {return sceneObjectCount;}
+ int GetGameObjectCount() const {return gameObjectCount;}
+private:
+
+ ObjectVector textures;
+ ObjectVector meshes;
+ ObjectVector materials;
+ ObjectVector animations;
+ ObjectVector audioclips;
+
+ volatile int assetCount;
+ volatile int sceneObjectCount;
+ volatile int gameObjectCount;
+
+ dynamic_array<int> classCount;
+
+ void AddDynamicObjectCount(Object* obj, int classID);
+ void RemoveDynamicObjectCount(Object* obj, int classID);
+
+};
+
+MemoryProfilerStats& GetMemoryProfilerStats();
+void InitializeMemoryProfilerStats();
+void CleanupMemoryProfilerStats();
+
+void profiler_register_object(Object* obj);
+void profiler_unregister_object(Object* obj);
+void profiler_change_persistancy(int instanceID, bool oldvalue, bool newvalue);
+
+#define PROFILER_REGISTER_OBJECT(obj) profiler_register_object(obj);
+#define PROFILER_UNREGISTER_OBJECT(obj) profiler_unregister_object(obj);
+#define PROFILER_CHANGE_PERSISTANCY(id,oldvalue,newvalue) profiler_change_persistancy(id,oldvalue,newvalue);
+
+#else
+
+#define PROFILER_REGISTER_OBJECT(obj)
+#define PROFILER_UNREGISTER_OBJECT(obj)
+#define PROFILER_CHANGE_PERSISTANCY(id,oldvalue,newvalue)
+
+#endif
+
+
+#endif \ No newline at end of file
diff --git a/Runtime/Profiler/MemoryProfilerTests.cpp b/Runtime/Profiler/MemoryProfilerTests.cpp
new file mode 100644
index 0000000..4c02f54
--- /dev/null
+++ b/Runtime/Profiler/MemoryProfilerTests.cpp
@@ -0,0 +1,73 @@
+#include "UnityPrefix.h"
+#include "MemoryProfiler.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+#if ENABLE_PROFILER
+#include "Runtime/Profiler/MemoryProfiler.h"
+
+SUITE (MemoryProfilerTests)
+{
+ struct MemoryProfilerFixture
+ {
+ MemoryProfilerFixture()
+ {
+ // string allocates length + 1 rounded up to mod 16
+ stringlength = 32;
+#if UNITY_OSX
+ // on osx, there is allocated room for refcounting as well
+ stringlength += 2;
+#endif
+ }
+ ~MemoryProfilerFixture()
+ {
+ }
+ int stringlength;
+ };
+
+ struct VectorOfStrings
+ {
+ UNITY_VECTOR(kMemDefault, UnityStr) vec;
+ };
+
+ struct StructWithStrings
+ {
+ UnityStr str1;
+ UnityStr str2;
+ };
+
+ TEST_FIXTURE(MemoryProfilerFixture, GetRelatedMemorySize_StringVector_MatchesExpectedSize)
+ {
+ VectorOfStrings* vec = UNITY_NEW_AS_ROOT(VectorOfStrings, kMemDefault, "", "");
+ vec->vec.reserve(10);
+ {
+ UnityStr str("HelloWorld HelloWorld");
+ SET_ALLOC_OWNER(vec);
+ vec->vec.push_back(str);
+ }
+
+ CHECK_EQUAL(GetMemoryProfiler()->GetRelatedMemorySize(vec), sizeof(UnityStr)*10 + sizeof(UNITY_VECTOR(kMemDefault, UnityStr)) + stringlength);
+ UNITY_DELETE(vec, kMemDefault);
+ }
+
+ TEST_FIXTURE(MemoryProfilerFixture, GetRelatedMemorySize_StringLosingOwner_MatchesExpectedSize)
+ {
+ StructWithStrings* strings;
+ {
+ VectorOfStrings* vec = UNITY_NEW_AS_ROOT(VectorOfStrings,kMemDefault, "", "");
+ SET_ALLOC_OWNER(vec);
+ strings = UNITY_NEW(StructWithStrings, kMemDefault);
+ strings->str1 = "HelloWorld HelloWorld";
+ strings->str2 = "HelloWorld HelloWorld";
+ CHECK_EQUAL(GetMemoryProfiler()->GetRelatedMemorySize(vec), sizeof(UNITY_VECTOR(kMemDefault, UnityStr)) + 2 * stringlength + sizeof(StructWithStrings) );
+ UNITY_DELETE(vec, kMemDefault);
+ }
+ UNITY_DELETE(strings, kMemDefault);
+ }
+
+}
+
+#endif
+#endif \ No newline at end of file
diff --git a/Runtime/Profiler/ObjectMemoryProfiler.cpp b/Runtime/Profiler/ObjectMemoryProfiler.cpp
new file mode 100644
index 0000000..31350c8
--- /dev/null
+++ b/Runtime/Profiler/ObjectMemoryProfiler.cpp
@@ -0,0 +1,245 @@
+#include "UnityPrefix.h"
+#include "ObjectMemoryProfiler.h"
+#include "MemoryProfiler.h"
+#include "SerializationUtility.h"
+#include "Runtime/Misc/GarbageCollectSharedAssets.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "ExtractLoadedObjectInfo.h"
+
+#if ENABLE_MEM_PROFILER
+
+#if UNITY_EDITOR
+#include "Editor/Src/Prefabs/Prefab.h"
+#endif
+
+namespace ObjectMemoryProfiler
+{
+
+#if ENABLE_MEM_PROFILER && ENABLE_PLAYERCONNECTION
+#define USE_MONO_LIVENESS (ENABLE_MONO && !UNITY_NACL)
+#endif
+
+
+typedef std::vector<Object*> ObjectList;
+static const UInt32 OBJECT_MEMORY_STREAM_VERSION = 0x00000002;
+static const UInt32 OBJECT_MEMORY_STREAM_TAIL = 0xAFAFAFAF;
+
+
+#if UNITY_EDITOR
+struct MonoObjectMemoryInfo
+{
+ int instanceId;
+ UInt32 memorySize;
+ int count;
+ int reason;
+ ScriptingStringPtr name;
+ ScriptingStringPtr className;
+};
+#endif
+
+static void Serialize(dynamic_array<int>& stream, const char* customAreaName, const char* objectName, size_t memorySize)
+{
+ stream.push_back(0);
+ stream.push_back(memorySize);
+ stream.push_back(0);
+ stream.push_back(kNotApplicable);
+ WriteString(stream, objectName);
+ WriteString(stream, customAreaName);
+}
+
+static void Serialize(dynamic_array<int>& stream, const char* objectName, int count )
+{
+ stream.push_back(0);
+ stream.push_back(0);
+ stream.push_back(count);
+ stream.push_back(kNotApplicable);
+ WriteString(stream, objectName);
+ WriteString(stream, "");
+}
+
+static void Serialize(dynamic_array<int>& stream, Object* object, int count )
+{
+ const char* objectName = object->GetName();
+ const std::string& className = object->GetClassName();
+
+ stream.push_back(object->GetInstanceID());
+ stream.push_back(object->GetRuntimeMemorySize());
+ stream.push_back(count);
+ stream.push_back(GetLoadedObjectReason(object));
+ if(object->GetClassID() == ClassID (MonoBehaviour))
+ WriteString(stream, ((MonoBehaviour*)(object))->GetScriptFullClassName().c_str());
+#if UNITY_EDITOR
+ else if(object->GetClassID() == ClassID (Prefab))
+ WriteString(stream, ((Prefab*)(object))->GetRootGameObject()->GetName());
+#endif
+ else
+ WriteString(stream, objectName);
+ WriteString(stream, className.c_str());
+}
+
+static void SerializeHeader(dynamic_array<int>& stream)
+{
+ stream.push_back(UNITY_LITTLE_ENDIAN);
+ int version = OBJECT_MEMORY_STREAM_VERSION;
+#if ENABLE_STACKS_ON_ALL_ALLOCS
+ version += 0x10000000;
+#endif
+ stream.push_back(version);
+}
+
+void TakeMemorySnapshot(dynamic_array<int>& stream)
+{
+ dynamic_array<Object*> loadedObjects;
+ dynamic_array<const char*> additionalCategories;
+ dynamic_array<UInt32> indexCounts;
+ dynamic_array<UInt32> referencedObjectIndices;
+
+ CalculateAllObjectReferences (loadedObjects, additionalCategories, indexCounts, referencedObjectIndices);
+
+ // MemoryProfiler Roots
+ MemoryProfiler::RootAllocationInfos rootInfos (kMemProfiler);
+ GetMemoryProfiler ()->GetRootAllocationInfos(rootInfos);
+
+ // loaded objects contain all loaded game objects
+ // this is followed by additional strings that has references as well
+ // indexCounts contain the count for each of the previous object references
+ // the indices into the object array for the references
+
+ SerializeHeader(stream);
+
+ // serialize the referenceIndices
+ stream.push_back(referencedObjectIndices.size());
+ WriteIntArray(stream, (int*)&referencedObjectIndices[0],referencedObjectIndices.size());
+
+ // serialize loaded objects followed by additionalCats
+ int totalObjects = loadedObjects.size() + additionalCategories.size() + rootInfos.size() + 1
+ #if ENABLE_MONO
+ + 2
+ #endif
+ ;
+ stream.push_back(totalObjects);
+
+ for(int i = 0 ; i < loadedObjects.size() ; i++)
+ {
+ Serialize(stream, loadedObjects[i], indexCounts[i]);
+ }
+
+ for(int i=0;i<additionalCategories.size();i++)
+ {
+ Serialize(stream, additionalCategories[i], indexCounts[i+loadedObjects.size()]);
+ }
+
+ for(int i=0;i<rootInfos.size();i++)
+ Serialize(stream, rootInfos[i].areaName, rootInfos[i].objectName, rootInfos[i].memorySize);
+
+ Serialize(stream, "System.ExecutableAndDlls", "", systeminfo::GetExecutableSizeMB()*1024*1024);
+
+ #if ENABLE_MONO
+ Serialize(stream, "ManagedHeap.UsedSize", "", mono_gc_get_used_size ());
+ Serialize(stream, "ManagedHeap.ReservedUnusedSize", "", mono_gc_get_heap_size () - mono_gc_get_used_size ());
+
+ ///@TOOD: Other mono metadata
+ #endif
+ stream.push_back(OBJECT_MEMORY_STREAM_TAIL);
+}
+
+
+#if UNITY_EDITOR
+
+static void Deserialize(const void* data, size_t size, MonoArray ** objectArray, MonoArray ** referenceArray)
+{
+ int* current_offset = (int*)data;
+ int wordsize = size/sizeof(int);
+ int* endBuffer = current_offset + wordsize;
+
+ int dataIsLittleEndian = *current_offset++;
+ bool swapdata = UNITY_LITTLE_ENDIAN ? dataIsLittleEndian == 0 : dataIsLittleEndian != 0;
+ if(swapdata)
+ {
+ int* ptr = current_offset;
+ while(ptr < endBuffer)
+ SwapEndianBytes(*(ptr++));
+ }
+
+ // header
+
+ int version = OBJECT_MEMORY_STREAM_VERSION;
+#if ENABLE_STACKS_ON_ALL_ALLOCS
+ version += 0x10000000;
+#endif
+ if(*current_offset!=version)
+ return;
+ current_offset++;
+
+ // deserialize the referenceIndices
+ int numberOfReferences = *current_offset++;
+ MonoArray* reference_array = mono_array_new(mono_domain_get(), MONO_COMMON.int_32, numberOfReferences);
+ ReadIntArray(&current_offset, &Scripting::GetScriptingArrayElement<int> (reference_array, 0), numberOfReferences);
+
+ // objectCount
+ int numberOfObjects = *current_offset++;
+ MonoClass* klass = GetMonoManager().GetMonoClass ("ObjectMemoryInfo", "UnityEditorInternal");
+ MonoArray* object_array = mono_array_new(mono_domain_get(), klass, numberOfObjects);
+
+ for (int i = 0; i < numberOfObjects;i++)
+ {
+ MonoObject* obj = mono_object_new (mono_domain_get(), klass);
+ GetMonoArrayElement<MonoObject*> (object_array,i) = obj;
+ }
+
+ for (int i = 0; i < numberOfObjects;i++)
+ {
+ MonoObjectMemoryInfo& memInfo = ExtractMonoObjectData<MonoObjectMemoryInfo> (GetMonoArrayElement<MonoObject*> (object_array,i));
+ memInfo.instanceId = *current_offset++;
+ memInfo.memorySize = *current_offset++;
+ memInfo.count = *current_offset++;
+ memInfo.reason = *current_offset++;
+ std::string name;
+ std::string className;
+ ReadString(&current_offset, name, swapdata);
+ ReadString(&current_offset, className, swapdata);
+ memInfo.name = MonoStringNew(name);
+ memInfo.className = MonoStringNew(className);
+ }
+
+ Assert(*current_offset==OBJECT_MEMORY_STREAM_TAIL);
+
+ *objectArray = object_array;
+ *referenceArray = reference_array;
+}
+
+void DeserializeAndApply (const void* data, size_t size)
+{
+ MonoArray* objectArray;
+ MonoArray* indexArray;
+ Deserialize (data, size, &objectArray, &indexArray);
+
+ ScriptingInvocation invocation ("UnityEditor", "ProfilerWindow", "SetMemoryProfilerInfo");
+ invocation.AddArray(objectArray);
+ invocation.AddArray(indexArray);
+ invocation.Invoke();
+}
+
+void SetDataFromEditor ()
+{
+ dynamic_array<int> data;
+ TakeMemorySnapshot(data);
+ DeserializeAndApply(data.begin(), data.size()*sizeof(int));
+
+#if RECORD_ALLOCATION_SITES
+ MemoryProfiler::MemoryStackEntry* stack = GetMemoryProfiler()->GetStackOverview();
+ MonoObject* obj = stack->Deserialize ();
+ void* arguments[] = { obj };
+ CallStaticMonoMethod("MemoryProfiler", "SetMemoryProfilerStackInfo", arguments);
+ GetMemoryProfiler()->ClearStackOverview(stack);
+
+ ProfilerString unrooted = GetMemoryProfiler()->GetUnrootedAllocationsOverview();
+#endif
+}
+
+#endif
+}
+
+#endif
diff --git a/Runtime/Profiler/ObjectMemoryProfiler.h b/Runtime/Profiler/ObjectMemoryProfiler.h
new file mode 100644
index 0000000..95c94b3
--- /dev/null
+++ b/Runtime/Profiler/ObjectMemoryProfiler.h
@@ -0,0 +1,19 @@
+#ifndef _OBJECT_MEMORY_PROFILER
+#define _OBJECT_MEMORY_PROFILER
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+#if ENABLE_MEM_PROFILER
+
+namespace ObjectMemoryProfiler
+{
+ void TakeMemorySnapshot (dynamic_array<int>& stream);
+
+#if UNITY_EDITOR
+ void SetDataFromEditor ();
+ void DeserializeAndApply (const void* data, size_t size);
+#endif
+};
+#endif
+#endif
diff --git a/Runtime/Profiler/Profiler.h b/Runtime/Profiler/Profiler.h
new file mode 100644
index 0000000..ee47e6c
--- /dev/null
+++ b/Runtime/Profiler/Profiler.h
@@ -0,0 +1,187 @@
+#ifndef _PROFILER_H_
+#define _PROFILER_H_
+
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_PROFILER
+
+#include "Runtime/Utilities/EnumFlags.h"
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+
+/*
+ // Example of profiling a code block:
+ // Define PROFILER_INFORMATION outside of a function
+
+ PROFILER_INFORMATION (gMyReallyFastFunctionProfile, "MyClass.MyFunction", kProfilerRender)
+
+ void MyFunction ()
+ {
+ PROFILER_AUTO (gMyReallyFastFunctionProfile, this or NULL);
+ // or
+ PROFILER_BEGIN (gMyReallyFastFunctionProfile, this or NULL);
+ PROFILER_END
+ }
+
+ // PROFILER_AUTO_THREAD_SAFE etc. can be used if you are not sure if the code might be called from another thread
+ // PROFILER_AUTO_INTERNAL etc. can be used if you do not want the profiler blocks to be in released
+ // unity builds only in internal developer builds
+
+ */
+
+enum ProfilerMode
+{
+ kProfilerEnabled = 1 << 0,
+ kProfilerGame = 1 << 1,
+ kProfilerDeepScripts = 1 << 2,
+ kProfilerEditor = 1 << 3,
+};
+ENUM_FLAGS(ProfilerMode);
+
+class Object;
+struct ProfilerSample;
+
+// ProfilerHistory uses AddToChart to sum the different groups into different charts.
+// Only kProfilerRender, kProfilerScripts, kProfilerPhysics, kProfilerGC, kProfilerVSync currently make any impact in the UI.
+enum ProfilerGroup
+{
+ kProfilerRender,
+ kProfilerScripts,
+ kProfilerGUI,
+ kProfilerPhysics,
+ kProfilerAnimation,
+ kProfilerAI,
+ kProfilerAudio,
+ kProfilerParticles,
+ kProfilerNetwork,
+ kProfilerLoading,
+ kProfilerOther,
+ kProfilerGC,
+ kProfilerVSync,
+ kProfilerOverhead,
+ kProfilerPlayerLoop,
+ kProfilerGroupCount
+};
+
+enum GpuSection
+{
+ kGPUSectionOther,
+ kGPUSectionOpaquePass,
+ kGPUSectionTransparentPass,
+ kGPUSectionShadowPass,
+ kGPUSectionDeferedPrePass,
+ kGPUSectionDeferedLighting,
+ kGPUSectionPostProcess
+};
+
+struct EXPORT_COREMODULE ProfilerInformation
+{
+ ProfilerInformation (const char* const functionName, ProfilerGroup grp, bool warn = false );
+
+ const char* name; // function
+ UInt16 group; // ProfilerGroup
+ enum { kDefault = 0, kScriptMonoRuntimeInvoke = 1, kScriptEnterLeave = 2 };
+ UInt8 flags;
+ UInt8 isWarning;
+
+ void* intelGPAData;
+};
+
+void EXPORT_COREMODULE profiler_begin(ProfilerInformation* info, const Object* obj);
+void EXPORT_COREMODULE profiler_end();
+
+void EXPORT_COREMODULE profiler_begin_thread_safe(ProfilerInformation* info, const Object* obj);
+void EXPORT_COREMODULE profiler_end_thread_safe();
+
+ProfilerSample* EXPORT_COREMODULE mono_profiler_begin(ScriptingMethodPtr method, ScriptingClassPtr profileKlass, ScriptingObjectPtr instance);
+void EXPORT_COREMODULE mono_profiler_end(ProfilerSample* beginsample);
+
+void EXPORT_COREMODULE gpu_time_sample();
+
+void profiler_begin_frame();
+void profiler_end_frame();
+void profiler_start_mode(ProfilerMode flags);
+void profiler_end_mode(ProfilerMode flags);
+
+// Create&destroy a profiler for a specific thread (Used by worker threads & GPU thread)
+void profiler_initialize_thread (const char* name, bool separateBeginEnd);
+void profiler_cleanup_thread ();
+
+// API for worker threads & GfxThread
+void profiler_set_active_seperate_thread (bool enabled);
+void profiler_begin_frame_seperate_thread (ProfilerMode mode);
+void profiler_end_frame_seperate_thread (int frameIDAndValid);
+void profiler_disable_sampling_seperate_thread ();
+
+// Profiler interface macros
+#define PROFILER_INFORMATION(VAR_NAME, NAME, GROUP) static ProfilerInformation VAR_NAME(NAME, GROUP);
+#define PROFILER_WARNING(VAR_NAME, NAME, GROUP) static ProfilerInformation VAR_NAME(NAME, GROUP, true);
+#define PROFILER_AUTO(INFO, OBJECT_PTR) ProfilerAutoObject _PROFILER_AUTO_OBJECT_(&INFO, OBJECT_PTR);
+#define PROFILER_BEGIN(INFO, OBJECT_PTR) profiler_begin (&INFO, OBJECT_PTR);
+#define PROFILER_END profiler_end();
+
+#define MONO_PROFILER_BEGIN(monomethod,monoclass,obj) ProfilerSample* beginsample = mono_profiler_begin (monomethod, monoclass,obj);
+#define MONO_PROFILER_END mono_profiler_end(beginsample);
+
+#define PROFILER_AUTO_THREAD_SAFE(INFO, OBJECT_PTR) ProfilerAutoObjectThreadSafe _PROFILER_AUTO_OBJECT_(&INFO, OBJECT_PTR);
+
+#define GPU_TIMESTAMP() gpu_time_sample();
+#define GPU_AUTO_SECTION(section) AutoGpuSection autoGpuSection(section);
+
+struct ProfilerAutoObject
+{
+ ProfilerAutoObject (ProfilerInformation* info, const Object* obj) { profiler_begin(info, obj); }
+ ~ProfilerAutoObject() { profiler_end(); }
+};
+
+struct ProfilerAutoObjectThreadSafe
+{
+ ProfilerAutoObjectThreadSafe (ProfilerInformation* info, const Object* obj) { profiler_begin_thread_safe (info, obj); }
+ ~ProfilerAutoObjectThreadSafe() { profiler_end_thread_safe(); }
+};
+
+extern GpuSection g_CurrentGPUSection;
+
+class AutoGpuSection
+{
+public:
+ AutoGpuSection(GpuSection section) { oldGPUSection = g_CurrentGPUSection; g_CurrentGPUSection = section; }
+ ~AutoGpuSection() { g_CurrentGPUSection = oldGPUSection; }
+private:
+ GpuSection oldGPUSection;
+};
+
+
+#else
+
+#define PROFILER_INFORMATION(VAR_NAME, NAME, GROUP)
+#define PROFILER_WARNING(VAR_NAME, NAME, GROUP)
+#define PROFILER_AUTO(INFO, OBJECT_PTR)
+#define PROFILER_BEGIN(INFO, OBJECT_PTR)
+#define PROFILER_END
+#define MONO_PROFILER_BEGIN(monomethod,monoclass,obj)
+#define MONO_PROFILER_END
+
+#define PROFILER_AUTO_THREAD_SAFE(INFO, OBJECT_PTR)
+
+#define GPU_TIMESTAMP()
+#define GPU_AUTO_SECTION(section)
+
+
+#endif
+
+
+#if ENABLE_PROFILER_INTERNAL_CALLS
+#define PROFILER_AUTO_INTERNAL(INFO, OBJECT_PTR) PROFILER_AUTO(INFO, OBJECT_PTR)
+#define PROFILER_BEGIN_INTERNAL(INFO, OBJECT_PTR) PROFILER_BEGIN(INFO, OBJECT_PTR)
+#define PROFILER_END_INTERNAL PROFILER_END
+
+#define PROFILER_AUTO_THREAD_SAFE_INTERNAL(INFO, OBJECT_PTR) PROFILER_AUTO_THREAD_SAFE(INFO, OBJECT_PTR)
+#else
+#define PROFILER_AUTO_INTERNAL(INFO, OBJECT_PTR)
+#define PROFILER_BEGIN_INTERNAL(INFO, OBJECT_PTR)
+#define PROFILER_END_INTERNAL
+
+#define PROFILER_AUTO_THREAD_SAFE_INTERNAL(INFO, OBJECT_PTR)
+#endif
+
+#endif /*_PROFILER_H_*/
diff --git a/Runtime/Profiler/ProfilerConnection.cpp b/Runtime/Profiler/ProfilerConnection.cpp
new file mode 100644
index 0000000..7d299dc
--- /dev/null
+++ b/Runtime/Profiler/ProfilerConnection.cpp
@@ -0,0 +1,353 @@
+#include "UnityPrefix.h"
+#include "ProfilerConnection.h"
+
+#if ENABLE_PROFILER
+
+#if ENABLE_PLAYERCONNECTION
+#include "ProfilerImpl.h"
+#include "ProfilerFrameData.h"
+#include "ProfilerStats.h"
+#include "Runtime/Mono/MonoHeapShot.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Network/NetworkUtility.h"
+#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h"
+#include "Runtime/Network/PlayerCommunicator/EditorConnection.h"
+#include "ProfilerHistory.h"
+#include "ObjectMemoryProfiler.h"
+#endif
+
+#if UNITY_EDITOR
+#include "Editor/Src/ProjectWizardUtility.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Mono/MonoManager.h"
+#include <ctime>
+#endif
+
+UInt32 ProfilerConnection::ms_EditorGuid = 0xFFFFFFFF;
+UInt32 ProfilerConnection::ms_CustomIPGuid = 0xFFFFFFFE;
+ProfilerConnection* ProfilerConnection::ms_Instance = NULL;
+
+void ProfilerConnection::Initialize()
+{
+ Assert(ms_Instance == NULL);
+ ms_Instance = new ProfilerConnection();
+#if ENABLE_PLAYERCONNECTION
+ ms_Instance->PrepareConnections();
+#endif
+}
+
+void ProfilerConnection::Cleanup()
+{
+ Assert(ms_Instance != NULL);
+#if ENABLE_PLAYERCONNECTION
+ ms_Instance->RemoveConnections();
+#endif
+ delete ms_Instance;
+ ms_Instance = NULL;
+}
+
+ProfilerConnection::ProfilerConnection()
+: m_ConnectedProfiler(ms_EditorGuid)
+, m_CurrentBuildTarget(kBuildNoTargetPlatform)
+{
+}
+
+#if UNITY_EDITOR
+
+void ProfilerConnection::DirectIPConnect(const std::string& IP)
+{
+ m_ConnectedProfiler = EditorConnection::Get().ConnectPlayerDirectIP(IP);
+ if(m_ConnectedProfiler == PLAYER_DIRECT_IP_CONNECT_GUID)
+ EnableConnectedProfiler(true);
+}
+
+void ProfilerConnection::GetAvailableProfilers ( std::vector<UInt32>& values )
+{
+ values.clear();
+ values.push_back(ms_EditorGuid);
+ EditorConnection::Get().GetAvailablePlayers(values);
+}
+
+void ProfilerConnection::EnableConnectedProfiler ( bool enable )
+{
+ int enabled = enable;
+ EditorConnection::Get().SendMessage(m_ConnectedProfiler, GeneralConnection::kProfileStartupInformation, &enabled, 4);
+}
+
+std::string ProfilerConnection::GetConnectionIdentification(UInt32 guid)
+{
+ if (guid != ms_EditorGuid)
+ return EditorConnection::Get().GetConnectionIdentifier(guid);
+ return "Editor";
+}
+
+bool ProfilerConnection::IsIdentifierConnectable(UInt32 guid)
+{
+ if (guid != ms_EditorGuid)
+ return EditorConnection::Get().IsIdentifierConnectable(guid);
+ return true;
+}
+
+bool ProfilerConnection::IsIdentifierOnLocalhost(UInt32 guid)
+{
+ if (guid != ms_EditorGuid)
+ return EditorConnection::Get().IsIdentifierOnLocalhost(guid);
+ return true;
+}
+
+bool ProfilerConnection::IsConnectionEditor()
+{
+ return m_ConnectedProfiler == ms_EditorGuid;
+}
+
+void ProfilerConnection::SetConnectedProfiler( UInt32 guid, bool sendDisable)
+{
+ if (guid == ms_EditorGuid){
+ if(m_ConnectedProfiler != guid && sendDisable)
+ EnableConnectedProfiler(false);
+ m_ConnectedProfiler = ms_EditorGuid;
+ return;
+ }
+
+ m_ConnectedProfiler = EditorConnection::Get().ConnectPlayer(guid);
+ if(m_ConnectedProfiler == guid)
+ EnableConnectedProfiler(true);
+}
+
+void ProfilerConnection::SetupTargetSpecificConnection(BuildTargetPlatform targetPlatform)
+{
+ if (m_CurrentBuildTarget == targetPlatform)
+ return;
+ m_CurrentBuildTarget = targetPlatform;
+ EditorConnection::Get().RemovePlayer(PLAYER_DIRECTCONNECT_GUID);
+ switch (targetPlatform)
+ {
+ case kBuild_Android:
+ {
+ std::string localhost = "127.0.0.1";
+ std::string hostName = Format("AndroidPlayer(ADB@%s:%i)", localhost.c_str(), PLAYER_DIRECTCONNECT_PORT);
+ UInt32 guid = EditorConnection::Get().AddPlayer(hostName, localhost, PLAYER_DIRECTCONNECT_PORT, PLAYER_DIRECTCONNECT_GUID, GeneralConnection::kSupportsProfile);
+ AssertMsg(guid == PLAYER_DIRECTCONNECT_GUID, "Unable to add Android direct profiler connection");
+ }
+ }
+}
+
+UInt32 ProfilerConnection::GetConnectedProfiler()
+{
+ return m_ConnectedProfiler;
+}
+
+UInt32 ProfilerConnection::GetEditorGuid()
+{
+ return ms_EditorGuid;
+}
+
+void ProfilerConnection::HandleDisconnectionMessage (UInt32 guid)
+{
+ Assert (UNITY_EDITOR);
+ ProfilerConnection::Get().SetConnectedProfiler(ms_EditorGuid, false);
+}
+
+#endif
+
+
+
+
+// Network communication and serialization
+
+#if ENABLE_PLAYERCONNECTION
+
+void ProfilerConnection::PrepareConnections()
+{
+#if UNITY_EDITOR
+ EditorConnection::Get().RegisterConnectionHandler(&ProfilerConnection::HandleConnectionMessage);
+ EditorConnection::Get().RegisterDisconnectionHandler(&ProfilerConnection::HandleDisconnectionMessage);
+ EditorConnection::Get().RegisterMessageHandler(GeneralConnection::kProfileDataMessage, &ProfilerConnection::HandleProfilerDataMessage);
+ EditorConnection::Get().RegisterMessageHandler(GeneralConnection::kObjectMemoryProfileDataMessage, &ProfilerConnection::HandleObjectMemoryProfileDataMessage);
+ EditorConnection::Get().RegisterMessageHandler(GeneralConnection::kFileTransferMessage, &ProfilerConnection::HandleFileDataMessage);
+#else
+ PlayerConnection::Get().RegisterConnectionHandler(&ProfilerConnection::HandleConnectionMessage);
+ PlayerConnection::Get().RegisterMessageHandler(GeneralConnection::kProfileStartupInformation, &ProfilerConnection::EnableProfilerMessage);
+ PlayerConnection::Get().RegisterMessageHandler(GeneralConnection::kObjectMemoryProfileSnapshot, &ProfilerConnection::GetObjectMemoryProfile);
+ PlayerConnection::Get().RegisterMessageHandler(GeneralConnection::kCaptureHeaphshotMessage, &ProfilerConnection::HandleCaptureHeapshotMessage);
+#endif
+}
+
+void ProfilerConnection::RemoveConnections()
+{
+#if UNITY_EDITOR
+ EditorConnection::Get().UnregisterConnectionHandler(&ProfilerConnection::HandleConnectionMessage);
+ EditorConnection::Get().UnregisterDisconnectionHandler(&ProfilerConnection::HandleDisconnectionMessage);
+ EditorConnection::Get().UnregisterMessageHandler(GeneralConnection::kProfileDataMessage, &ProfilerConnection::HandleProfilerDataMessage);
+ EditorConnection::Get().UnregisterMessageHandler(GeneralConnection::kObjectMemoryProfileDataMessage, &ProfilerConnection::HandleObjectMemoryProfileDataMessage);
+ EditorConnection::Get().UnregisterMessageHandler(GeneralConnection::kFileTransferMessage, &ProfilerConnection::HandleFileDataMessage);
+#else
+ PlayerConnection::Get().UnregisterConnectionHandler(&ProfilerConnection::HandleConnectionMessage);
+ PlayerConnection::Get().UnregisterMessageHandler(GeneralConnection::kProfileStartupInformation, &ProfilerConnection::EnableProfilerMessage);
+ PlayerConnection::Get().UnregisterMessageHandler(GeneralConnection::kObjectMemoryProfileSnapshot, &ProfilerConnection::GetObjectMemoryProfile);
+ PlayerConnection::Get().UnregisterMessageHandler(GeneralConnection::kCaptureHeaphshotMessage, &ProfilerConnection::HandleCaptureHeapshotMessage);
+#endif
+}
+
+void ProfilerConnection::HandleConnectionMessage (UInt32 guid)
+{
+ ProfilerConnection::Get().m_ConnectedProfiler = guid;
+#if UNITY_EDITOR
+ ProfilerConnection::Get().EnableConnectedProfiler(true);
+#endif
+}
+
+
+void ProfilerConnection::EnableProfilerMessage ( const void* data, UInt32 size, UInt32 guid)
+{
+ // message sent from Editor to Player, to start and stop the profiler
+ Assert(!UNITY_EDITOR);
+ if(GetBuildSettingsPtr() && !GetBuildSettingsPtr()->hasAdvancedVersion)
+ {
+ ErrorString("Profiler is only supported with Pro License");
+ return;
+ }
+ bool enable = *(int*)data != 0;
+ if (enable)
+ ProfilerConnection::Get().m_ConnectedProfiler = guid;
+ if ( ProfilerConnection::Get().m_ConnectedProfiler == guid )
+ UnityProfiler::Get().SetEnabled(enable);
+}
+
+void ProfilerConnection::GetObjectMemoryProfile( const void* data, UInt32 size, UInt32 guid)
+{
+ // message recieved on the player from Editor
+ Assert(!UNITY_EDITOR);
+ if ( ProfilerConnection::Get().m_ConnectedProfiler != guid )
+ return;
+
+ if(GetBuildSettingsPtr() && !GetBuildSettingsPtr()->hasAdvancedVersion)
+ {
+ ErrorString("Profiler is only supported with Pro License");
+ return;
+ }
+
+#if ENABLE_MEM_PROFILER
+ dynamic_array<int> buffer;
+ ObjectMemoryProfiler::TakeMemorySnapshot(buffer);
+ PlayerConnection::Get().SendMessage(ProfilerConnection::Get().m_ConnectedProfiler,PlayerConnection::kObjectMemoryProfileDataMessage, &buffer[0], buffer.size()*sizeof(int));
+#endif
+}
+
+
+void ProfilerConnection::HandleCaptureHeapshotMessage (const void* data, UInt32 size, UInt32 guid)
+{
+#if ENABLE_MONO_HEAPSHOT
+ printf_console("Capturing heapshot\n");
+ HeapShotData heapShotData;
+ HeapShotDumpObjectMap(heapShotData);
+ //bool WriteBytesToFile (const void *data, int byteLength, const string& pathName);
+ //WriteBytesToFile(&data[0], data.size(), "game:\\test.dump");
+ if (heapShotData.size() > 0)
+ TransferFileOverPlayerConnection("testplayer.heapshot", &heapShotData[0], heapShotData.size());
+#endif
+}
+
+
+void ProfilerConnection::SendFrameDataToEditor ( ProfilerFrameData& data )
+{
+ // TODO: Send partial data ( right now we double buffer the frame on the profiler side, to wait for GPU data)
+ dynamic_array<int> buffer;
+
+ UnityProfiler::SerializeFrameData(data, buffer);
+
+ if(buffer.size()<128*1024)
+ PlayerConnection::Get().SendMessage(m_ConnectedProfiler,PlayerConnection::kProfileDataMessage, &buffer[0], buffer.size()*sizeof(int));
+}
+
+
+
+#if UNITY_EDITOR
+
+void ProfilerConnection::SendCaptureHeapshotMessage()
+{
+ int enabled = 1;
+ //printf_console("Sending capture heapshot cmd");
+ EditorConnection::Get().SendMessage(m_ConnectedProfiler, GeneralConnection::kCaptureHeaphshotMessage, &enabled, sizeof(int));
+}
+
+void ProfilerConnection::HandleFileDataMessage (const void* data, UInt32 size, UInt32 guid)
+{
+ UInt8* uData = (UInt8*) data;
+ //printf_console("HandleFileDataMessage");
+
+ UInt32 fileNameLength = *(UInt32*)uData;
+ uData += sizeof(UInt32);
+
+ char rawFileName[255];
+ memcpy(rawFileName, uData, fileNameLength);
+ rawFileName[fileNameLength] = '\0';
+ uData += fileNameLength;
+
+ UInt32 contentLength = *(UInt32*)uData;
+ uData += sizeof(UInt32);
+
+ //printf_console("Name: %s Content length: %d\n", rawFileName, contentLength);
+
+ time_t now;
+ time(&now);
+ struct tm nowTime;
+ nowTime = *localtime(&now);
+
+ std::string fileName = Format("%04d-%02d-%02d_%02dh%02dm%02ds.heapshot", nowTime.tm_year + 1900, nowTime.tm_mon + 1, nowTime.tm_mday,
+ nowTime.tm_hour, nowTime.tm_min, nowTime.tm_sec);
+ //printf_console(fileName.c_str());
+ // ToDo: figure it out what we're saving, for now think always that it's a heapshot file
+ std::string heapShotDirectory = AppendPathName (GetProjectPath (), "Heapshots");
+ if (CreateDirectorySafe(heapShotDirectory))
+ {
+ std::string fullPath = AppendPathName (heapShotDirectory, fileName);
+ WriteBytesToFile(uData, contentLength, fullPath);
+
+ void* params[] = {scripting_string_new(fileName)};
+ CallStaticMonoMethod ("HeapshotWindow", "EventHeapShotReceived", params);
+ }
+}
+void ProfilerConnection::HandleProfilerDataMessage ( const void* data, UInt32 size, UInt32 guid )
+{
+ if (ProfilerConnection::Get().GetConnectedProfiler() != guid)
+ return;
+
+ if (!UnityProfiler::Get().GetEnabled())
+ return;
+
+ ProfilerFrameData* frame = UNITY_NEW(ProfilerFrameData, kMemProfiler) (1, 0);
+ if( UnityProfiler::DeserializeFrameData(frame, data, size) )
+ ProfilerHistory::Get().AddFrameDataAndTransferOwnership(frame, guid);
+ else
+ UNITY_DELETE(frame, kMemProfiler);
+}
+
+void ProfilerConnection::SendGetObjectMemoryProfile()
+{
+#if ENABLE_MEM_PROFILER
+ if(m_ConnectedProfiler == ms_EditorGuid)
+ ObjectMemoryProfiler::SetDataFromEditor();
+ else
+ EditorConnection::Get().SendMessage(m_ConnectedProfiler, GeneralConnection::kObjectMemoryProfileSnapshot, NULL, 0);
+#endif
+}
+
+void ProfilerConnection::HandleObjectMemoryProfileDataMessage ( const void* data, UInt32 size, UInt32 guid )
+{
+#if ENABLE_MEM_PROFILER
+ if (ProfilerConnection::Get().GetConnectedProfiler() != guid)
+ return;
+
+ ObjectMemoryProfiler::DeserializeAndApply(data,size);
+#endif
+}
+
+#endif
+
+
+
+#endif
+#endif
diff --git a/Runtime/Profiler/ProfilerConnection.h b/Runtime/Profiler/ProfilerConnection.h
new file mode 100644
index 0000000..ffde5b3
--- /dev/null
+++ b/Runtime/Profiler/ProfilerConnection.h
@@ -0,0 +1,91 @@
+#ifndef _PROFILERCONNECTION_H_
+#define _PROFILERCONNECTION_H_
+
+#if ENABLE_PROFILER
+
+#include "Configuration/UnityConfigure.h"
+
+#include "Runtime/Threads/Thread.h"
+#include "ProfilerImpl.h"
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/Serialize/SerializationMetaFlags.h"
+
+class ProfilerFrameData;
+
+enum ConnectedProfiler
+{
+ kConnectedProfilerEditor,
+ kConnectedProfilerStandalone,
+ kConnectedProfilerWeb,
+ kConnectedProfileriPhone,
+ kConnectedProfilerAndroid,
+ kConnectedProfilerXenon,
+ kConnectedProfilerPS3,
+ kConnectedProfilerWII,
+ kConnectedProfilerPepper,
+ kConnectedProfilerCount
+};
+
+class ProfilerConnection
+{
+public:
+ static void Initialize();
+ static void Cleanup();
+ // Singleton accessor for ProfilerConnection
+ static ProfilerConnection& Get() { return *ms_Instance; }
+
+#if UNITY_EDITOR
+ void GetAvailableProfilers (std::vector<UInt32>& values);
+
+ void EnableConnectedProfiler ( bool enable );
+ void SetConnectedProfiler (UInt32 guid, bool sendDisable = true);
+ UInt32 GetConnectedProfiler ();
+ static UInt32 GetEditorGuid();
+ void DirectIPConnect(const std::string& IP);
+
+ std::string GetConnectionIdentification(UInt32 guid);
+ bool IsIdentifierConnectable(UInt32 guid);
+ bool IsIdentifierOnLocalhost(UInt32 guid);
+ bool IsConnectionEditor();
+ void SetupTargetSpecificConnection(BuildTargetPlatform targetPlatform);
+
+ static void HandleFileDataMessage (const void* data, UInt32 size, UInt32 guid);
+ void SendCaptureHeapshotMessage();
+
+ void SendGetObjectMemoryProfile();
+#endif
+
+ void SendFrameDataToEditor( ProfilerFrameData& data );
+
+
+private:
+ ProfilerConnection();
+
+#if ENABLE_PLAYERCONNECTION
+
+ void PrepareConnections();
+ void RemoveConnections();
+
+ static void HandleProfilerDataMessage (const void* data, UInt32 size, UInt32 guid);
+ static void HandleObjectMemoryProfileDataMessage (const void* data, UInt32 size, UInt32 guid);
+ static void HandlePlayerConnectionMessage (const void* data, UInt32 size, UInt32 guid);
+ static void HandleConnectionMessage (UInt32 guid);
+ static void HandleDisconnectionMessage (UInt32 guid);
+ static void EnableProfilerMessage ( const void* data, UInt32 size, UInt32 guid);
+ static void GetObjectMemoryProfile( const void* data, UInt32 size, UInt32 guid);
+ static void HandleCaptureHeapshotMessage ( const void* data, UInt32 size, UInt32 guid);
+#endif
+
+private:
+ UInt32 m_ConnectedProfiler;
+ int m_CurrentBuildTarget;
+
+ // ProfilerConnection instance to use with singleton pattern
+ static ProfilerConnection* ms_Instance;
+ static UInt32 ms_EditorGuid;
+ static UInt32 ms_CustomIPGuid;
+};
+
+#endif
+
+#endif
diff --git a/Runtime/Profiler/ProfilerFrameData.cpp b/Runtime/Profiler/ProfilerFrameData.cpp
new file mode 100644
index 0000000..d56c2fe
--- /dev/null
+++ b/Runtime/Profiler/ProfilerFrameData.cpp
@@ -0,0 +1,230 @@
+#include "UnityPrefix.h"
+#include "ProfilerFrameData.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+#if ENABLE_PROFILER
+
+
+
+// -------------------------------------------------------------------
+
+
+dynamic_array<GfxTimerQuery*> ProfilerFrameData::m_UnusedQueries;
+
+ProfilerFrameData::ProfilerFrameData(int threadCount, int frameID)
+: m_FrameID(frameID)
+{
+ Assert(threadCount > 0);
+ m_ThreadData = new ThreadData[threadCount];
+ m_ThreadCount = threadCount;
+}
+
+
+ProfilerFrameData::~ProfilerFrameData()
+{
+ for (int t = 0; t < m_ThreadCount; ++t)
+ {
+ ThreadData& td = m_ThreadData[t];
+ for (int i = 0; i < td.m_GPUTimeSamples.size(); i++)
+ {
+ GfxTimerQuery* query = td.m_GPUTimeSamples[i].timerQuery;
+ if (query)
+ m_UnusedQueries.push_back(query);
+ }
+ }
+ delete[] m_ThreadData;
+}
+
+GfxTimerQuery* ProfilerFrameData::AllocTimerQuery()
+{
+ if (!m_UnusedQueries.empty())
+ {
+ GfxTimerQuery* query = m_UnusedQueries.back();
+ m_UnusedQueries.pop_back();
+ return query;
+ }
+ else
+ return GetGfxDevice().CreateTimerQuery();
+}
+
+void ProfilerFrameData::ReleaseTimerQuery(GfxTimerQuery* query)
+{
+ m_UnusedQueries.push_back(query);
+}
+
+void ProfilerFrameData::FreeAllTimerQueries()
+{
+ for (int i = 0; i < m_UnusedQueries.size(); i++)
+ GetGfxDevice().DeleteTimerQuery(m_UnusedQueries[i]);
+ m_UnusedQueries.clear();
+}
+
+void ProfilerFrameData::ThreadData::ExtractAllChildSamples (UInt32 index, dynamic_array<UInt32>& allChildren) const
+{
+ const ProfilerSample* sample = &m_AllSamples[index];
+ const ProfilerSample* currentSample = sample + 1;
+ for (int i=0;i<sample->nbChildren;i++)
+ {
+ UInt32 currentIndex = currentSample - m_AllSamples.begin();
+ allChildren.push_back(currentIndex);
+ currentSample = SkipSampleRecurse(currentSample);
+ }
+}
+
+
+
+// -------------------------------------------------------------------
+
+
+
+#if UNITY_EDITOR
+
+#include "ProfilerHistory.h"
+
+ProfilerFrameDataIterator::ProfilerFrameDataIterator()
+: m_ThreadIdx(0)
+, m_FrameData(NULL)
+, m_CurrIndex(0)
+{
+}
+
+const ProfilerSample& ProfilerFrameDataIterator::GetSample(UInt32 index) const
+{
+ DebugAssert(m_FrameData);
+ const ProfilerFrameData::ThreadData& tdata = m_FrameData->m_ThreadData[m_ThreadIdx];
+ Assert(index < tdata.m_AllSamples.size());
+
+ return tdata.m_AllSamples[index];
+}
+
+int ProfilerFrameDataIterator::GetGroup() const
+{
+ const ProfilerSample& s = GetSample(m_CurrIndex);
+ return s.information ? s.information->group : kProfilerOther;
+}
+
+float ProfilerFrameDataIterator::GetStartTimeMS () const
+{
+ const ProfilerSample& s = GetSample(m_CurrIndex);
+ return (s.startTimeUS - m_FrameData->m_StartTimeUS) / 1000.0;
+}
+
+float ProfilerFrameDataIterator::GetDurationMS () const
+{
+ const ProfilerSample& s = GetSample(m_CurrIndex);
+ return s.timeUS / 1000.0;
+}
+
+
+int ProfilerFrameDataIterator::GetThreadCount(int frame) const
+{
+ ProfilerFrameData* frameData = ProfilerHistory::Get().GetFrameData(frame);
+ if (frameData == NULL)
+ return 0;
+ return frameData->m_ThreadCount;
+}
+
+double ProfilerFrameDataIterator::GetFrameStartS(int frame) const
+{
+ ProfilerFrameData* frameData = ProfilerHistory::Get().GetFrameData(frame);
+ if (frameData == NULL)
+ return 0.0;
+ return frameData->m_StartTimeUS / 1000000.0;
+}
+
+const std::string* ProfilerFrameDataIterator::GetThreadName () const
+{
+ if (!m_FrameData)
+ return NULL;
+ const ProfilerFrameData::ThreadData& tdata = m_FrameData->m_ThreadData[m_ThreadIdx];
+ return &tdata.m_ThreadName;
+}
+
+
+void ProfilerFrameDataIterator::SetRoot(int frame, int threadIdx)
+{
+ m_Stack.clear();
+ m_CurrIndex = 0;
+ m_FrameData = NULL;
+
+ ProfilerFrameData* frameData = ProfilerHistory::Get().GetFrameData(frame);
+ if (frameData == NULL)
+ return;
+
+ if (threadIdx < 0 || threadIdx >= frameData->m_ThreadCount)
+ return;
+
+ m_FrameData = frameData;
+ m_ThreadIdx = threadIdx;
+
+ m_CurrIndex = 0;
+}
+
+float ProfilerFrameDataIterator::GetFrameTimeMS() const
+{
+ if (m_FrameData)
+ return m_FrameData->m_TotalCPUTimeInMicroSec/1000.0;
+ else
+ return 0.0f;
+}
+
+bool ProfilerFrameDataIterator::GetNext(bool expanded)
+{
+ if (m_FrameData == NULL)
+ return false;
+
+ const ProfilerFrameData::ThreadData& tdata = m_FrameData->m_ThreadData[m_ThreadIdx];
+ const UInt32 nSamples = tdata.m_AllSamples.size();
+ if (m_CurrIndex >= nSamples)
+ {
+ DebugAssert(nSamples == 0);
+ return false;
+ }
+
+ const ProfilerSample* s = tdata.GetSample (m_CurrIndex);
+ const ProfilerSample* nextSameLevel = SkipSampleRecurse (s);
+ const UInt32 idxNextSameLevel = nextSameLevel - tdata.m_AllSamples.data();
+
+ const bool hasChildren = s->nbChildren != 0;
+ if (hasChildren && expanded)
+ {
+ // entering into children, push our range into stack
+ StackInfo info;
+ if (!m_Stack.empty())
+ info.path = m_Stack.back().path;
+ const ProfilerSample* ps = tdata.GetSample(m_CurrIndex);
+ if (ps && ps->information)
+ {
+ info.path += (ps && ps->information) ? ps->information->name : "?";
+ info.path += '/';
+ }
+
+ info.sampleBegin = m_CurrIndex;
+ info.sampleEnd = idxNextSameLevel;
+ m_Stack.push_back(info);
+ ++m_CurrIndex;
+ }
+ else
+ {
+ // We'll go to sample idxNextSameLevel. But if we've reached end of our level,
+ // that sample might be at parent level already. Pop scope until we're at
+ // the right level.
+ m_CurrIndex = idxNextSameLevel;
+ while (!m_Stack.empty() && idxNextSameLevel >= m_Stack.back().sampleEnd)
+ m_Stack.pop_back();
+ if (m_Stack.empty())
+ return false; // reached very end
+ }
+
+ s = tdata.GetSample (m_CurrIndex);
+
+ m_FunctionName = s->information ? s->information->name : "?";
+ m_FunctionPath = m_Stack.back().path + m_FunctionName;
+
+ return true;
+}
+
+
+#endif // #if UNITY_EDITOR
+
+#endif // #if ENABLE_PROFILER
diff --git a/Runtime/Profiler/ProfilerFrameData.h b/Runtime/Profiler/ProfilerFrameData.h
new file mode 100644
index 0000000..97bb273
--- /dev/null
+++ b/Runtime/Profiler/ProfilerFrameData.h
@@ -0,0 +1,134 @@
+#ifndef _PROFILERFRAMEDATA_H_
+#define _PROFILERFRAMEDATA_H_
+
+
+#include "ProfilerImpl.h"
+
+#if ENABLE_PROFILER
+
+
+// -------------------------------------------------------------------
+
+
+// Profiling data stored for one frame
+class ProfilerFrameData
+{
+public:
+ struct ThreadData
+ {
+ ThreadData()
+ : m_AllSamples(kMemProfiler)
+ , m_GPUTimeSamples(kMemProfiler)
+ , m_InstanceIDSamples(kMemProfiler)
+ , m_AllocatedGCMemorySamples(kMemProfiler)
+ , m_WarningSamples(kMemProfiler)
+ , m_ThreadName("<no data this frame>")
+ {
+ }
+
+ const ProfilerSample* GetRoot () const { return &m_AllSamples[0]; }
+ ProfilerSample* GetRoot () { return &m_AllSamples[0]; }
+ const ProfilerSample* GetSample (int sample) const { return &m_AllSamples[sample]; }
+
+ void ExtractAllChildSamples (UInt32 index, dynamic_array<UInt32>& allChildren) const;
+
+ dynamic_array<ProfilerSample> m_AllSamples;
+ dynamic_array<ProfilerData::GPUTime> m_GPUTimeSamples;
+ dynamic_array<ProfilerData::InstanceID> m_InstanceIDSamples;
+ dynamic_array<ProfilerData::AllocatedGCMemory> m_AllocatedGCMemorySamples;
+ dynamic_array<UInt32> m_WarningSamples;
+
+ std::string m_ThreadName;
+ };
+
+ ProfilerFrameData(int threadCount, int frameID);
+ ~ProfilerFrameData();
+
+ int m_FrameID;
+ int frameIndex;
+ int realFrame;
+
+ // Automatic statistic value extraction can extract any int value from here
+ AllProfilerStats allStats;
+ int selectedTime;
+ // Until here
+
+ ThreadData* m_ThreadData;
+ int m_ThreadCount;
+
+ ProfileTimeFormat m_StartTimeUS;
+ int m_TotalCPUTimeInMicroSec;
+ int m_TotalGPUTimeInMicroSec;
+
+#if ENABLE_PLAYERCONNECTION
+ void Serialize(dynamic_array<int>& bs);
+ void Deserialize(int** bs, bool swapdata);
+
+ static ProfilerInformation* DeserializeProfilerInformation( int** bitstream, bool swapdata );
+ static void SerializeProfilerInformation( const ProfilerInformation& info, dynamic_array<int>& bitstream );
+
+#endif
+
+ static GfxTimerQuery* AllocTimerQuery();
+ static void ReleaseTimerQuery(GfxTimerQuery* query);
+ static void FreeAllTimerQueries();
+
+private:
+ static dynamic_array<GfxTimerQuery*> m_UnusedQueries;
+};
+
+
+// -------------------------------------------------------------------
+
+
+#if UNITY_EDITOR
+
+// Hierarchical iterator over raw samples in one profiler frame
+class ProfilerFrameDataIterator
+{
+public:
+ ProfilerFrameDataIterator();
+
+ int GetThreadCount(int frame) const;
+ double GetFrameStartS(int frame) const;
+ const std::string* GetThreadName () const;
+ float GetFrameTimeMS() const;
+ float GetStartTimeMS() const;
+
+ float GetDurationMS() const;
+ int GetGroup() const;
+ int GetID() const { return m_CurrIndex; }
+ int GetDepth() const { return m_Stack.size(); }
+ const std::string& GetFunctionName() const { return m_FunctionName; }
+ const std::string& GetFunctionPath() const { return m_FunctionPath; }
+
+ void SetRoot(int frame, int threadIdx);
+ bool GetNext(bool expanded);
+ bool IsFrameValid() const { return m_FrameData != NULL; }
+
+private:
+ const ProfilerSample& GetSample(UInt32 index) const;
+
+private:
+ struct StackInfo
+ {
+ std::string path;
+ UInt32 sampleBegin;
+ UInt32 sampleEnd;
+ };
+
+ ProfilerFrameData* m_FrameData;
+ int m_ThreadIdx;
+
+ UNITY_VECTOR(kMemProfiler,StackInfo) m_Stack;
+ UInt32 m_CurrIndex;
+
+ std::string m_FunctionName; // name (e.g. Camera.Render)
+ std::string m_FunctionPath; // hierarchical name, e.g. PlayerLoop/RenderCameras/Camera.Render
+};
+
+#endif // #if UNITY_EDITOR
+
+#endif // #if ENABLE_PROFILER
+
+#endif
diff --git a/Runtime/Profiler/ProfilerHistory.cpp b/Runtime/Profiler/ProfilerHistory.cpp
new file mode 100644
index 0000000..d0f9056
--- /dev/null
+++ b/Runtime/Profiler/ProfilerHistory.cpp
@@ -0,0 +1,560 @@
+#include "UnityPrefix.h"
+#include "ProfilerHistory.h"
+
+#if ENABLE_PROFILER && UNITY_EDITOR
+
+#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h"
+#include "Runtime/Network/PlayerCommunicator/EditorConnection.h"
+#include "ProfilerStats.h"
+#include "Profiler.h"
+#include "GPUProfiler.h"
+#include "Runtime/Utilities/Word.h"
+#include "ProfilerImpl.h"
+#include "ProfilerProperty.h"
+#include "ProfilerConnection.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+
+#include <fstream>
+#include <sstream>
+#include "Runtime/Misc/SystemInfo.h"
+
+static const ProfilerSample* CalculateSampleAtPath (const ProfilerSample* sample, const char* path, dynamic_array<ProfilerSample*>& outputSamples);
+
+ProfilerHistory* ProfilerHistory::ms_Instance = NULL;
+
+// ProfilerHistory class implementation
+//
+
+void ProfilerHistory::Initialize()
+{
+ Assert(ms_Instance == NULL);
+ ms_Instance = new ProfilerHistory();
+}
+
+void ProfilerHistory::Cleanup()
+{
+ Assert(ms_Instance != NULL);
+ delete ms_Instance;
+ ms_Instance = NULL;
+}
+
+ProfilerHistory::ProfilerHistory()
+ : m_MaxFrameHistoryLength(300)
+ , m_FrameCounter(0)
+ , m_BytesUsedLastFrame(0u)
+ , m_HistoryPosition(-1)
+ , m_Frames(kMemProfiler)
+ , m_Properties(kMemProfiler)
+{
+ InitializeStatisticsProperties(m_Properties);
+
+ m_FramesWithGPUData = 0;
+#if SUPPORT_THREADS
+ m_MainThreadID = Thread::GetCurrentThreadID();
+#endif
+}
+
+ProfilerHistory::~ProfilerHistory()
+{
+ CleanupFrameHistory();
+}
+
+
+static inline int FindSeperator (const char* in)
+{
+ const char* c = in;
+ while (*c != '/' && *c != '\0')
+ c++;
+ return c - in;
+}
+
+static const ProfilerSample* CalculateSampleAtPath (const ProfilerSample* sample, const char* path, dynamic_array<ProfilerSample*>& outputSamples)
+{
+ int seperatorIndex = FindSeperator(path);
+
+ const ProfilerSample* child = sample + 1;
+ for (int i=0;i<sample->nbChildren;i++)
+ {
+ if (strncmp(child->information->name, path, seperatorIndex) == 0)
+ {
+ if (seperatorIndex == strlen (path))
+ {
+ outputSamples.push_back(const_cast<ProfilerSample*>(child));
+ child = SkipSampleRecurse(child);
+ }
+ else
+ {
+ child = CalculateSampleAtPath(child, path + seperatorIndex + 1, outputSamples);
+ }
+ }
+ else
+ {
+ child = SkipSampleRecurse(child);
+ }
+ }
+
+ return child;
+}
+
+static void AddToChart(ChartSample& data, int group, int sampleTime, int direction)
+{
+ // Add times to their groups
+ if (group == kProfilerRender)
+ data.rendering += sampleTime * direction;
+ else if (group == kProfilerScripts)
+ data.scripts += sampleTime * direction;
+ else if (group == kProfilerPhysics)
+ data.physics += sampleTime * direction;
+ else if (group == kProfilerGC)
+ data.gc += sampleTime * direction;
+ else if (group == kProfilerVSync)
+ data.vsync += sampleTime * direction;
+ else
+ data.others += sampleTime * direction;
+}
+
+
+
+
+// Adds the self time of each sample into the group.
+// Expects that ChartSample has all elements set to zero, except the other time which needs to be the root time
+// of the root sample.
+static const ProfilerSample* RecursiveAdjustChartForGroupChange(ChartSample& data, const ProfilerSample* root, int direction)
+{
+ Assert(root != NULL);
+
+ const ProfilerSample* childSample = root + 1;
+
+ // Walk through the children and collect chart information recursively
+ for (int i=0;i<root->nbChildren;i++)
+ {
+ //@TODO: Make this cleaner
+ if (root->information == NULL || root->information->group != childSample->information->group)
+ {
+ int childSampleTime = (int) childSample->timeUS*1000;
+
+ // Add times to their groups
+ AddToChart(data, childSample->information->group, childSampleTime, direction);
+
+ // Remove this sample time from the group the parent is in, thus we end up with only the self time of the parent
+ if (root->information != NULL)
+ AddToChart(data, root->information->group, childSampleTime, -direction);
+ }
+
+ childSample = RecursiveAdjustChartForGroupChange(data, childSample, direction);
+ }
+
+ return childSample;
+}
+
+static void GatherChartInformation (ChartSample& data, const ProfilerSample* sample, int size)
+{
+ const ProfilerSample* end = RecursiveAdjustChartForGroupChange(data, sample, 1);
+ Assert(end - sample == size || size == -1);
+}
+
+static void GatherGPUChartInformation (ChartSample& data, const ProfilerData::GPUTime* sample, int size)
+{
+ data.hasGPUProfiler = size != 0;
+ for(int i = 0; i < size; i++)
+ {
+ int sampletime = sample[i].gpuTimeInMicroSec*1000;
+ switch(sample[i].gpuSection)
+ {
+ case kGPUSectionShadowPass:
+ data.gpuShadows += sampletime;
+ break;
+ case kGPUSectionOpaquePass:
+ data.gpuOpaque += sampletime;
+ break;
+ case kGPUSectionTransparentPass:
+ data.gpuTransparent += sampletime;
+ break;
+ case kGPUSectionPostProcess:
+ data.gpuPostProcess += sampletime;
+ break;
+ case kGPUSectionDeferedPrePass:
+ data.gpuDeferredPrePass += sampletime;
+ break;
+ case kGPUSectionDeferedLighting:
+ data.gpuDeferredLighting += sampletime;
+ break;
+ default:
+ data.gpuOther += sampletime;
+ break;
+ };
+ }
+}
+
+void ProfilerHistory::AddFrameDataAndTransferOwnership (ProfilerFrameData* frame, int guid)
+{
+ Assert(UNITY_EDITOR);
+
+ if(!AddFrame(frame, guid))
+ UNITY_DELETE(frame, kMemProfiler);
+}
+
+bool ProfilerHistory::AddFrame (ProfilerFrameData* frame, int identifier)
+{
+ if (ProfilerConnection::Get().GetConnectedProfiler() != identifier)
+ return false;
+
+ // Fill in group timing information for the charts
+ ChartSample& activeChartSample = frame->allStats.chartSample;
+ activeChartSample = ChartSample();
+
+ const ProfilerFrameData::ThreadData& tdata = frame->m_ThreadData[0];
+ GatherChartInformation(activeChartSample, tdata.m_AllSamples.begin(), tdata.m_AllSamples.size());
+ GatherGPUChartInformation(activeChartSample, tdata.m_GPUTimeSamples.begin(), tdata.m_GPUTimeSamples.size());
+
+ CalculateSelectedTimeAndChart(*frame);
+
+#if SUPPORT_THREADS
+ Assert (Thread::EqualsCurrentThreadID(m_MainThreadID));
+#endif
+
+ if (m_Frames.size() >= m_MaxFrameHistoryLength)
+ KillOneFrame();
+
+ frame->frameIndex = m_FrameCounter++;
+
+ // We count frames with GPU data to detect if there is any GPU profiling data
+ if (frame->allStats.chartSample.hasGPUProfiler)
+ m_FramesWithGPUData++;
+
+ m_Frames.push_back(frame);
+ return true;
+}
+
+
+void ProfilerHistory::CalculateSelectedTimeAndChart (ProfilerFrameData& frame)
+{
+ frame.selectedTime = 0;
+ frame.allStats.chartSampleSelected = ChartSample();
+ if (!m_SelectedPropertyPath.empty())
+ {
+ const ProfilerFrameData::ThreadData& tdata = frame.m_ThreadData[0];
+ dynamic_array<ProfilerSample*> selectedSamples;
+ const ProfilerSample* endSample = CalculateSampleAtPath(tdata.GetRoot(), m_SelectedPropertyPath.c_str(), selectedSamples);
+ Assert(endSample - tdata.GetRoot() == tdata.m_AllSamples.size());
+
+ for (int i=0;i<selectedSamples.size();i++)
+ {
+ ProfilerSample* sample = selectedSamples[i];
+
+ AddToChart(frame.allStats.chartSampleSelected, sample->information->group, (int)sample->timeUS*1000, 1);
+ GatherChartInformation(frame.allStats.chartSampleSelected, sample, -1);
+ frame.selectedTime += sample->timeUS*1000;
+ }
+ }
+}
+
+void ProfilerHistory::SetSelectedPropertyPath (const std::string& samplePath)
+{
+ if (m_SelectedPropertyPath != samplePath)
+ {
+ m_SelectedPropertyPath = samplePath;
+
+ // Recompute cached selected frame time from new path
+ for (int i=0;i<m_Frames.size();i++)
+ CalculateSelectedTimeAndChart(*m_Frames[i]);
+ }
+}
+
+bool ProfilerHistory::IsGPUProfilerBuggyOnDriver ()
+{
+ return gGraphicsCaps.buggyTimerQuery;
+}
+
+bool ProfilerHistory::IsGPUProfilerSupportedByOS ()
+{
+#if UNITY_OSX
+ return systeminfo::GetOperatingSystemNumeric() >= 1070;
+#else
+ return true;
+#endif
+}
+
+bool ProfilerHistory::IsGPUProfilerSupported ()
+{
+ if (m_Frames.empty())
+ return true;
+ else
+ return m_FramesWithGPUData != 0;
+}
+
+
+void ProfilerHistory::KillOneFrame()
+{
+ if (m_Frames.size() < 3)
+ return;
+
+ // Kill first or second frame. But always keep the frame the user is currently viewing (m_HistoryPosition)
+ int killIndex = 0;
+ if (m_Frames[0]->frameIndex == m_HistoryPosition)
+ killIndex = 1;
+
+ // We count frames with GPU data to detect if there is any GPU profiling data
+ if (m_Frames[killIndex]->allStats.chartSample.hasGPUProfiler)
+ m_FramesWithGPUData--;
+
+ UNITY_DELETE(m_Frames[killIndex], kMemProfiler);
+ m_Frames.erase(&m_Frames[killIndex] , &m_Frames[killIndex+1]);
+
+}
+
+const ProfilerFrameData* ProfilerHistory::GetLastFrameData() const
+{
+ if (!m_Frames.empty())
+ return m_Frames.back();
+ else
+ return NULL;
+}
+
+ProfilerFrameData* ProfilerHistory::GetLastFrameData()
+{
+ if (!m_Frames.empty())
+ return m_Frames.back();
+ else
+ return NULL;
+}
+
+const ProfilerFrameData* ProfilerHistory::GetFrameData(int frame) const
+{
+ if (frame == -1)
+ return GetLastFrameData();
+ else
+ {
+ if (m_Frames.size () > 1)
+ {
+ int index = m_Frames.size() - (m_Frames.back()->frameIndex - frame) - 1;
+ if (index >= 0 && index < m_Frames.size() && m_Frames[index]->frameIndex == frame)
+ return m_Frames[index];
+ }
+
+ int size = m_Frames.size();
+ for (int i=0;i<size;i++)
+ {
+ if (frame == m_Frames[i]->frameIndex)
+ return m_Frames[i];
+ }
+ return NULL;
+ }
+}
+
+ProfilerFrameData* ProfilerHistory::GetFrameData(int frame)
+{
+ if (frame == -1)
+ return GetLastFrameData();
+ else
+ {
+ if (m_Frames.size () > 1)
+ {
+ int index = m_Frames.size() - (m_Frames.back()->frameIndex - frame) - 1;
+ if (index >= 0 && index < m_Frames.size() && m_Frames[index]->frameIndex == frame)
+ return m_Frames[index];
+ }
+
+ int size = m_Frames.size();
+ for (int i=0;i<size;i++)
+ {
+ if (frame == m_Frames[i]->frameIndex)
+ return m_Frames[i];
+ }
+ return NULL;
+ }
+}
+
+int ProfilerHistory::GetFirstFrameIndex ()
+{
+ if (m_Frames.size() >= 1)
+ return m_Frames[0]->frameIndex;
+ else
+ return -1;
+}
+
+int ProfilerHistory::GetLastFrameIndex ()
+{
+ if (!m_Frames.empty())
+ return m_Frames.back()->frameIndex;
+ else
+ return -1;
+}
+
+int ProfilerHistory::GetNextFrameIndex (int frame)
+{
+ if (frame == -1)
+ return -1;
+
+ int size = m_Frames.size();
+ for (int i=0;i<size;i++)
+ {
+ if (m_Frames[i]->frameIndex > frame)
+ return m_Frames[i]->frameIndex;
+ }
+ return -1;
+}
+
+int ProfilerHistory::GetPreviousFrameIndex (int frame)
+{
+ if (frame == -1)
+ frame = std::numeric_limits<int>::max();
+
+ int size = m_Frames.size();
+ for (int i=size-1;i>=0;i--)
+ {
+ if (m_Frames[i]->frameIndex < frame)
+ return m_Frames[i]->frameIndex;
+ }
+ return -1;
+}
+
+void ProfilerHistory::CleanupFrameHistory()
+{
+ for (int i = 0; i < m_Frames.size(); ++i)
+ UNITY_DELETE(m_Frames[i], kMemProfiler);
+ m_Frames.clear();
+
+ m_FrameCounter = 0;
+ m_HistoryPosition = -1;
+ m_BytesUsedLastFrame = 0u;
+
+ UnityProfiler::Get().ClearPendingFrames();
+}
+
+static std::string FormatCount (int b)
+{
+ if (b < 1000)
+ return Format ("%i", b);
+ if (b < 1000000)
+ return Format ("%01.1fk", b/1000.0);
+ b /= 1000;
+ return Format ("%01.1fM", b/1000.0);
+}
+
+std::string ProfilerHistory::GetFormattedStatisticsValue(ProfilerFrameData& frameData, int identifier)
+{
+ Assert (identifier >= 0 && identifier < m_Properties.size());
+
+ int val = GetStatisticsValue(frameData,identifier);
+ if (val == -1)
+ return "";
+
+ switch(m_Properties[identifier].format) {
+ case kFormatTime: return Format("%i.%ims", val/1000000, val%1000000/100000);
+ case kFormatCount: return FormatCount (val);
+ case kFormatBytes: return FormatBytes(val);
+ case kFormatPercentage: return Format("%i.%i %%", val / 10, val % 10);
+ default: AssertString("unknown format"); return "";
+ }
+}
+
+std::string ProfilerHistory::GetFormattedStatisticsValue(int frame, int identifier)
+{
+ Assert (identifier >= 0 && identifier < m_Properties.size());
+
+ ProfilerFrameData* frameData = GetFrameData(frame);
+ if (frameData == NULL)
+ return "";
+ return GetFormattedStatisticsValue (*frameData, identifier);
+}
+
+int ProfilerHistory::GetStatisticsValue(ProfilerFrameData& data, int identifier)
+{
+ if (identifier < 0 || identifier >= m_Properties.size())
+ return -1;
+
+ return ::GetStatisticsValue(m_Properties[identifier].offset, data.allStats);
+}
+
+
+void ProfilerHistory::GetStatisticsValuesBatch(int identifier, int firstValue, float scale, float* buffer, int size, float* outMaxValue)
+{
+ int offset = -1;
+ if (identifier >= 0 && identifier < m_Properties.size())
+ offset = m_Properties[identifier].offset;
+
+ *outMaxValue = 0;
+
+ for (int i=0;i<size;i++)
+ {
+ int frame = firstValue + i;
+ int value = -1;
+
+ ///@TODO: Optimize GetFrameData
+ if (frame >= 0 && offset != -1)
+ {
+ ProfilerFrameData* data = GetFrameData(frame);
+ if (data)
+ value = ::GetStatisticsValue(offset, data->allStats);
+ }
+ if (value >= 0)
+ {
+ float scaledValue = double(value) * double(scale);
+ buffer[i] = scaledValue;
+ *outMaxValue = std::max(*outMaxValue, scaledValue);
+ }
+ else
+ {
+ buffer[i] = -1;
+ }
+ }
+}
+
+int ProfilerHistory::GetStatisticsIdentifier(const std::string& statName)
+{
+ for (size_t i = 0; i < m_Properties.size(); ++i)
+ if (statName == m_Properties[i].name)
+ return i;
+ return -1;
+}
+
+void ProfilerHistory::GetAllStatisticsProperties(std::vector<std::string>& all)
+{
+ all.reserve(m_Properties.size());
+ for (size_t i = 0; i < m_Properties.size(); ++i)
+ all.push_back(m_Properties[i].name);
+}
+
+void ProfilerHistory::GetGraphStatisticsPropertiesForArea(ProfilerArea area, std::vector<std::string>& all)
+{
+ all.reserve(m_Properties.size());
+ for (size_t i = 0; i < m_Properties.size(); ++i)
+ {
+ if (area == m_Properties[i].area && m_Properties[i].showGraph)
+ all.push_back(m_Properties[i].name);
+ }
+}
+
+ProfilerString ProfilerHistory::GetOverviewTextForProfilerArea (int frame, ProfilerArea profilerArea)
+{
+ ProfilerFrameData* frameData = GetFrameData(frame);
+ if (frameData == NULL)
+ return "";
+
+ /////@TODO: USE ONLY GENERIC CODE BELOW> STOP HARDCODING SHIT
+ if (profilerArea == kProfilerAreaCPU)
+ {
+
+ }
+ else if (profilerArea == kProfilerAreaRendering)
+ return frameData->allStats.drawStats.ToString();
+ else if (profilerArea == kProfilerAreaMemory)
+ return frameData->allStats.memoryStats.ToString();
+
+ TEMP_STRING overview;
+ for (int i=0;i<m_Properties.size();i++)
+ {
+ StatisticsProperty& prop = m_Properties[i];
+ if (prop.area != profilerArea)
+ continue;
+
+ overview += FormatString<TEMP_STRING>("%s: %s\n", prop.name.c_str(), GetFormattedStatisticsValue(*frameData, i).c_str());
+ }
+
+ return overview.c_str();
+}
+
+#endif // ENABLE_PROFILER
diff --git a/Runtime/Profiler/ProfilerHistory.h b/Runtime/Profiler/ProfilerHistory.h
new file mode 100644
index 0000000..e5bd04c
--- /dev/null
+++ b/Runtime/Profiler/ProfilerHistory.h
@@ -0,0 +1,113 @@
+#ifndef _PROFILERHISTORY_H_
+#define _PROFILERHISTORY_H_
+
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_PROFILER && UNITY_EDITOR
+
+#include "Runtime/Threads/Thread.h"
+#include "TimeHelper.h"
+#include "ProfilerImpl.h"
+#include "ProfilerFrameData.h"
+#include "ProfilerStats.h"
+#include "Runtime/Misc/SystemInfo.h"
+
+struct ProfilerSample;
+
+class ProfilerHistory
+{
+public:
+
+ typedef dynamic_array<ProfilerFrameData*> ProfileFrameVector;
+
+ // Memory overview
+ //void* memoryOverview;
+
+ ~ProfilerHistory();
+
+ static void Initialize();
+ static void Cleanup();
+
+ // Singleton accessor for profiler
+ static ProfilerHistory& Get() { return *ms_Instance; }
+
+ void AddFrameDataAndTransferOwnership(ProfilerFrameData* frame, int guid);
+
+ // Deletes one frame from the history if we have too many in the history
+ void KillOneFrame();
+
+ // Release memory allocated for all frame history
+ void CleanupFrameHistory();
+
+ const ProfilerFrameData* GetFrameData(int frame) const;
+ ProfilerFrameData* GetFrameData(int frame);
+ const ProfilerFrameData* GetLastFrameData() const;
+ ProfilerFrameData* GetLastFrameData();
+
+ // Frame index access functions
+ int GetFirstFrameIndex ();
+ int GetLastFrameIndex ();
+ int GetNextFrameIndex (int frame);
+ int GetPreviousFrameIndex (int frame);
+
+ // The maximum amount of history samples. (-1 because there is one sample reserved for the currently selected history position, which is not shown in graphs)
+ int GetMaxFrameHistoryLength () { return m_MaxFrameHistoryLength - 1; }
+
+ void GetStatisticsValuesBatch(int identifier, int firstValue, float scale, float* buffer, int size, float* outMaxValue);
+ std::string GetFormattedStatisticsValue(ProfilerFrameData& frame, int identifier);
+ std::string GetFormattedStatisticsValue(int frame, int identifier);
+
+ ProfilerString GetOverviewTextForProfilerArea (int frame, ProfilerArea profilerArea);
+
+ int GetStatisticsValue(ProfilerFrameData& frameData, int identifier);
+ int GetStatisticsIdentifier(const std::string& statName);
+ void GetAllStatisticsProperties(std::vector<std::string>& all);
+ void GetGraphStatisticsPropertiesForArea(ProfilerArea area, std::vector<std::string>& all);
+
+ void SetHistoryPosition (int pos) { m_HistoryPosition = pos; }
+ int GetHistoryPosition () const { return m_HistoryPosition; }
+ int GetFrameCounter () { return m_FrameCounter; }
+
+ bool IsGPUProfilerBuggyOnDriver ();
+ bool IsGPUProfilerSupportedByOS ();
+ bool IsGPUProfilerSupported ();
+
+ void SetSelectedPropertyPath (const std::string& samplePath);
+ const std::string& GetSelectedPropertyPath () { return m_SelectedPropertyPath; }
+
+private:
+ ProfilerHistory();
+
+ void CalculateSelectedTimeAndChart (ProfilerFrameData& frameData);
+ void CleanupActiveFrame();
+
+ bool AddFrame (ProfilerFrameData* frame, int source);
+
+private:
+ dynamic_array<StatisticsProperty> m_Properties;
+
+ // Profile history parameters
+ int m_MaxFrameHistoryLength;
+ int m_FrameCounter;
+ int m_HistoryPosition;
+
+ int m_FramesWithGPUData;
+
+ UInt32 m_BytesUsedLastFrame;
+
+ // STL vector to store lists of profile sample objects of all functions being called within one frame
+ ProfileFrameVector m_Frames;
+
+#if SUPPORT_THREADS
+ Thread::ThreadID m_MainThreadID;
+#endif
+
+ std::string m_SelectedPropertyPath;
+
+ // Profiler instance to use with singleton pattern
+ static ProfilerHistory* ms_Instance;
+};
+
+#endif
+
+#endif
diff --git a/Runtime/Profiler/ProfilerImpl.cpp b/Runtime/Profiler/ProfilerImpl.cpp
new file mode 100644
index 0000000..2abafa5
--- /dev/null
+++ b/Runtime/Profiler/ProfilerImpl.cpp
@@ -0,0 +1,1682 @@
+#include "UnityPrefix.h"
+#include "Profiler.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+
+#include <string.h>
+#if ENABLE_PROFILER
+
+#include "ProfilerImpl.h"
+#include "GPUProfiler.h"
+#include "ProfilerHistory.h"
+#include "ProfilerFrameData.h"
+#include "CollectProfilerStats.h"
+#include "ProfilerConnection.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Threads/JobScheduler.h"
+#include "IntelGPAProfiler.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+
+#if UNITY_PS3
+#include "External/libsntuner.h"
+#define PS3_TUNER_SAMPLE_BEGIN(s) snPushMarker((s)->name)
+#define PS3_TUNER_SAMPLE_END() snPopMarker()
+#else
+#define PS3_TUNER_SAMPLE_BEGIN(s)
+#define PS3_TUNER_SAMPLE_END()
+#endif
+
+
+#define DEBUG_AUTO_PROFILER_LOG 0
+#if DEBUG_AUTO_PROFILER_LOG && !UNITY_BUILD_COPY_PROTECTED
+#error "Must disable DEBUG_AUTO_PROFILER_LOG in deployed build"
+#endif
+
+#if UNITY_EDITOR
+#include "Editor/Src/EditorHelper.h"
+#endif
+
+
+#if ENABLE_MONO
+#include "Runtime/Scripting/CommonScriptingClasses.h"
+const int kMonoProfilerDefaultFlags = MONO_PROFILE_GC | MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_EXCEPTIONS;
+#endif // #if ENABLE_MONO
+
+const int kProfilerDataStreamVersion = 0x20122123;
+
+void profiler_begin_frame()
+{
+ if (UnityProfiler::GetPtr())
+ UnityProfiler::Get().BeginFrame();
+}
+
+void profiler_end_frame()
+{
+ if (UnityProfiler::GetPtr())
+ UnityProfiler::Get().EndFrame();
+}
+
+void profiler_start_mode(ProfilerMode flags)
+{
+ if (UnityProfiler::GetPtr())
+ UnityProfiler::Get().StartProfilingMode(flags);
+}
+
+void profiler_end_mode(ProfilerMode flags)
+{
+ if (UnityProfiler::GetPtr())
+ UnityProfiler::Get().EndProfilingMode(flags);
+}
+
+void profiler_begin_thread_safe(ProfilerInformation* info, const Object* obj)
+{
+ PS3_TUNER_SAMPLE_BEGIN(info);
+
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof && prof->m_ProfilerAllowSampling)
+ {
+ SET_ALLOC_OWNER(NULL);
+ prof->BeginSample(info, obj);
+ }
+}
+
+void profiler_end_thread_safe()
+{
+ PS3_TUNER_SAMPLE_END();
+
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof && prof->m_ProfilerAllowSampling)
+ prof->EndSample(START_TIME);
+}
+
+
+void profiler_begin(ProfilerInformation* info, const Object* obj)
+{
+ INTEL_GPA_SAMPLE_BEGIN(info);
+ PS3_TUNER_SAMPLE_BEGIN(info);
+
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof && prof->m_ProfilerAllowSampling)
+ {
+ SET_ALLOC_OWNER(NULL);
+ prof->BeginSample(info, obj);
+ }
+}
+
+void profiler_end()
+{
+ INTEL_GPA_SAMPLE_END();
+ PS3_TUNER_SAMPLE_END();
+
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof && prof->m_ProfilerAllowSampling)
+ prof->EndSample(START_TIME);
+}
+
+GpuSection g_CurrentGPUSection = kGPUSectionOther;
+
+void gpu_time_sample()
+{
+ GPUProfiler::GPUTimeSample();
+}
+
+void profiler_initialize_thread (const char* name, bool separateBeginEnd)
+{
+#if UNITY_EDITOR
+ if(IsDeveloperBuild())
+ UnityProfilerPerThread::Initialize(name, true);
+#endif
+}
+
+void profiler_cleanup_thread ()
+{
+ UnityProfilerPerThread::Cleanup();
+}
+
+void profiler_set_active_seperate_thread (bool enabled)
+{
+ UnityProfiler* prof = UnityProfiler::GetPtr();
+ if (prof)
+ prof->SetActiveSeparateThread(enabled);
+}
+
+void profiler_begin_frame_seperate_thread (ProfilerMode mode)
+{
+ UnityProfiler* prof = UnityProfiler::GetPtr();
+ if (prof)
+ prof->BeginFrameSeparateThread(mode);
+
+}
+
+void profiler_disable_sampling_seperate_thread ()
+{
+ UnityProfiler* prof = UnityProfiler::GetPtr();
+ if (prof)
+ prof->DisableSamplingSeparateThread();
+
+}
+
+void profiler_end_frame_seperate_thread (int frameIDAndValid)
+{
+ UnityProfiler* prof = UnityProfiler::GetPtr();
+ if (prof)
+ prof->EndFrameSeparateThread(frameIDAndValid);
+}
+
+
+// -------------------------------------------------------------------------
+
+
+
+ProfilerInformation::ProfilerInformation (const char* const functionName, ProfilerGroup grp, bool warn)
+: name(functionName)
+, group(grp)
+, flags(kDefault)
+, isWarning(warn)
+{
+ INTEL_GPA_INFORMATION_INITIALIZE();
+}
+
+
+
+// -------------------------------------------------------------------------
+// Per-thread profiler class
+
+const int kDeepProfilingMaxSamples = 1024 * 1024 * 4; // 80 MB on 32bit
+const int kNormalProfilingMaxSamples = 1024 * 512; // 10 MB on 32bit
+
+
+UNITY_TLS_VALUE(UnityProfilerPerThread*) UnityProfilerPerThread::ms_InstanceTLS;
+
+void UnityProfilerPerThread::Initialize(const char* threadName, bool separateBeginEnd)
+{
+ SET_ALLOC_OWNER(UnityProfiler::GetPtr());
+ Assert(ms_InstanceTLS == NULL);
+ ms_InstanceTLS = UNITY_NEW(UnityProfilerPerThread, kMemProfiler)(threadName, separateBeginEnd);
+
+ UnityProfiler::ms_Instance->AddPerThreadProfiler(ms_InstanceTLS);
+}
+
+void UnityProfilerPerThread::Cleanup()
+{
+ if(ms_InstanceTLS == NULL)
+ return;
+
+ UnityProfiler::ms_Instance->RemovePerThreadProfiler(ms_InstanceTLS);
+
+ UnityProfilerPerThread* instance = ms_InstanceTLS;
+ UNITY_DELETE(instance, kMemProfiler);
+ ms_InstanceTLS = NULL;
+}
+
+UnityProfilerPerThread::UnityProfilerPerThread(const char* threadName, bool separateBeginEnd)
+: m_ProfilerAllowSampling(false)
+, m_ActiveGlobalAllocator(1024 * 128, kMemProfiler)
+, m_OutOfSampleMemory(false)
+, m_ErrorDurringFrame(false)
+, m_ActiveSamples(kMemProfiler)
+, m_SampleStack(kMemProfiler)
+, m_SampleTimeBeginStack(kMemProfiler)
+, m_GPUTimeSamples(kMemProfiler)
+, m_InstanceIDSamples(kMemProfiler)
+, m_AllocatedGCMemorySamples(kMemProfiler)
+, m_WarningSamples(kMemProfiler)
+, m_ProfilersListNode(this)
+, m_GCCollectTime(0)
+, m_ThreadName(threadName)
+, m_ThreadIndex(0)
+, m_SeparateBeginEnd(separateBeginEnd)
+{
+ #if ENABLE_THREAD_CHECK_IN_ALLOCS
+ Thread::ThreadID tid = Thread::GetCurrentThreadID();
+ m_ActiveGlobalAllocator.SetThreadIDs(tid, tid);
+ #endif
+}
+
+UnityProfilerPerThread::~UnityProfilerPerThread()
+{
+ m_ActiveGlobalAllocator.purge (true);
+ m_ActiveMethodCache.clear();
+ m_DynamicMethodCache.clear();
+}
+
+void UnityProfilerPerThread::BeginFrame(ProfilerMode mode)
+{
+ if (mode & kProfilerDeepScripts)
+ m_ActiveSamples.resize_uninitialized(kDeepProfilingMaxSamples);
+ else
+ m_ActiveSamples.resize_uninitialized(kNormalProfilingMaxSamples);
+
+ m_ActiveSamples[0] = ProfilerSample();
+ m_ActiveSamples[0].startTimeUS = GetProfileTime(START_TIME)/1000;
+ m_NextSampleIndex = 1;
+
+ m_AllocatedGCMemorySamples.resize_uninitialized(0);
+ m_GPUTimeSamples.resize_uninitialized(0);
+ m_InstanceIDSamples.resize_uninitialized(0);
+ m_WarningSamples.resize_uninitialized(0);
+
+ m_SampleStack.resize_uninitialized(0);
+ m_SampleStack.push_back(0);
+
+ m_SampleTimeBeginStack.push_back(START_TIME);
+
+ m_GCCollectTime = 0;
+}
+
+
+bool UnityProfilerPerThread::EndFrame()
+{
+ Assert (!GetIsActive());
+
+ ProfilerSample* rootSample = GetRoot();
+ if (m_SampleStack.size()>1)
+ {
+ if (!m_ErrorDurringFrame)
+ ErrorString("Too many Profiler.BeginSample (BeginSample and EndSample count must match)");
+ m_ErrorDurringFrame = true;
+ }
+
+ bool ok = (rootSample->nbChildren != 0 && !m_OutOfSampleMemory && !m_ErrorDurringFrame);
+ return ok;
+}
+
+
+void UnityProfilerPerThread::ClearFrame ()
+{
+ m_NextSampleIndex = 0;
+
+ m_SampleStack.resize_uninitialized(0);
+ m_SampleTimeBeginStack.resize_uninitialized(0);
+ m_GPUTimeSamples.resize_uninitialized(0);
+ m_InstanceIDSamples.resize_uninitialized(0);
+ m_AllocatedGCMemorySamples.resize_uninitialized(0);
+ m_WarningSamples.resize_uninitialized(0);
+
+ m_ProfilerAllowSampling = false;
+ m_OutOfSampleMemory = false;
+ m_ErrorDurringFrame = false;
+}
+
+
+void UnityProfilerPerThread::SetIsActive (bool enabled)
+{
+ if (!enabled && m_ProfilerAllowSampling)
+ {
+ if (m_GCCollectTime != 0)
+ InjectGCCollectSample();
+ }
+ m_ProfilerAllowSampling = enabled && !m_SampleStack.empty();
+}
+
+void UnityProfilerPerThread::AddMiscSamplesAfterFrame(ProfileTimeFormat frameDuration, bool addOverhead)
+{
+ // Insert GC.Collect sample if we had one
+ if (m_GCCollectTime != 0)
+ InjectGCCollectSample();
+
+ ProfilerSample* rootSample = GetRoot();
+ if (rootSample)
+ rootSample->timeUS = frameDuration / 1000;
+
+ if (addOverhead)
+ CreateOverheadSample();
+}
+
+
+ProfilerSample* UnityProfilerPerThread::BeginSample(ProfilerInformation* info, const Object* obj)
+{
+ DebugAssert(this);
+
+ DebugAssert(m_ProfilerAllowSampling);
+ DebugAssert(!m_SampleStack.empty() == m_ProfilerAllowSampling);
+
+ // Insert GC.Collect sample if we had one
+ if (m_GCCollectTime != 0)
+ InjectGCCollectSample();
+
+ // Add child to children
+ m_ActiveSamples[m_SampleStack.back()].nbChildren++;
+
+ // Add new sample and insert into stack
+ m_SampleStack.push_back(m_NextSampleIndex);
+ ProfilerSample* sample = &m_ActiveSamples[m_NextSampleIndex];
+ m_NextSampleIndex++;
+ if(m_NextSampleIndex > ((int)m_ActiveSamples.size()-1))
+ {
+ m_NextSampleIndex = m_ActiveSamples.size()-1;
+ if(!m_OutOfSampleMemory)
+ {
+ m_OutOfSampleMemory = true;
+ ErrorString("The profiler has run out of samples for this frame. This frame will be skipped.");
+ }
+ }
+
+ // Initialize sample object
+ sample->information = info;
+
+ if(info->isWarning)
+ m_WarningSamples.push_back(GetActiveSampleIndex ());
+
+ sample->nbChildren = 0;
+ sample->startTimeUS = 0;
+
+ if(obj != NULL)
+ {
+ ProfilerData::InstanceID instanceSample = {GetActiveSampleIndex(), obj->GetInstanceID()};
+ m_InstanceIDSamples.push_back(instanceSample);
+ }
+
+ // Get current processor time and store it in the sample
+ m_SampleTimeBeginStack.push_back(START_TIME);
+ return sample;
+}
+
+
+void UnityProfilerPerThread::EndSample(ABSOLUTE_TIME time)
+{
+ DebugAssert(this);
+
+ DebugAssert(m_ProfilerAllowSampling);
+ if(m_SampleStack.size() <= 1)
+ {
+ if(!m_ErrorDurringFrame)
+ ErrorString("Non matching Profiler.EndSample (BeginSample and EndSample count must match)");
+ m_ErrorDurringFrame = true;
+ return;
+ }
+ DebugAssert(m_SampleStack.empty() != m_ProfilerAllowSampling);
+
+ ProfilerSample* sample = &m_ActiveSamples[m_SampleStack.back()];
+ // Set duration and start time
+ sample->startTimeUS = GetProfileTime(m_SampleTimeBeginStack.back())/1000;
+ sample->timeUS = GetProfileTime(SUBTRACTED_TIME(time, m_SampleTimeBeginStack.back()))/1000;
+
+ // Insert GC.Collect sample if we had one
+ if (m_GCCollectTime != 0)
+ InjectGCCollectSample();
+
+ m_SampleStack.pop_back();
+ m_SampleTimeBeginStack.pop_back();
+}
+
+
+void UnityProfilerPerThread::BeginSampleDynamic(const std::string& name, const Object* obj)
+{
+ DebugAssert(this);
+ if (!m_ProfilerAllowSampling)
+ return;
+
+ DynamicMethodCache::iterator found = m_DynamicMethodCache.find(name);
+ if (found != m_DynamicMethodCache.end())
+ {
+ BeginSample(&found->second, obj);
+ }
+ else
+ {
+ found = m_DynamicMethodCache.insert(make_pair(name, ProfilerInformation(NULL, kProfilerScripts))).first;
+ found->second.name = found->first.c_str();
+ found->second.group = kProfilerScripts;
+ BeginSample(&found->second, obj);
+ }
+}
+
+
+void UnityProfilerPerThread::SaveToFrameData (ProfilerFrameData& dst) const
+{
+ ProfilerFrameData::ThreadData& tdata = dst.m_ThreadData[m_ThreadIndex];
+ tdata.m_ThreadName = m_ThreadName;
+ tdata.m_AllSamples.assign(m_ActiveSamples.begin(), &m_ActiveSamples[m_NextSampleIndex]);
+ tdata.m_GPUTimeSamples.assign(m_GPUTimeSamples.begin(), m_GPUTimeSamples.end());
+ tdata.m_InstanceIDSamples.assign(m_InstanceIDSamples.begin(), m_InstanceIDSamples.end());
+ tdata.m_AllocatedGCMemorySamples.assign(m_AllocatedGCMemorySamples.begin(), m_AllocatedGCMemorySamples.end());
+ tdata.m_WarningSamples.assign(m_WarningSamples.begin(), m_WarningSamples.end());
+}
+
+
+
+// -------------------------------------------------------------------------
+// Global Profiler class
+
+
+
+UnityProfiler* UnityProfiler::ms_Instance = NULL;
+
+PROFILER_INFORMATION(gGCCollect, "GC.Collect", kProfilerGC)
+PROFILER_INFORMATION(gOverheadProfile, "Overhead", kProfilerOverhead)
+
+
+#if SUPPORT_THREADS
+#define IS_MAIN_THREAD(p) Thread::EqualsCurrentThreadID(p->m_MainThreadID)
+#else
+#define IS_MAIN_THREAD(p) (true)
+#endif
+
+
+void UnityProfiler::Initialize ()
+{
+ Assert(ms_Instance == NULL);
+ ms_Instance = UNITY_NEW_AS_ROOT(UnityProfiler,kMemProfiler, "Profiler", "");
+ UnityProfilerPerThread::Initialize("Main Thread");
+}
+
+void UnityProfiler::CleanupGfx ()
+{
+ Assert(ms_Instance != NULL);
+ for (int i = 0; i < kFrameCount; i++)
+ {
+ if (ms_Instance->m_PreviousFrames[i] != NULL)
+ GPUProfiler::ClearTimerQueries(ms_Instance->m_PreviousFrames[i]->m_ThreadData[0].m_GPUTimeSamples);
+ }
+ ProfilerFrameData::FreeAllTimerQueries();
+}
+
+void UnityProfiler::Cleanup ()
+{
+ UnityProfilerPerThread::Cleanup();
+
+ Assert(ms_Instance != NULL);
+ UNITY_DELETE(ms_Instance, kMemProfiler);
+ ms_Instance = NULL;
+}
+
+
+
+UnityProfiler::UnityProfiler()
+: m_ProfilerEnabledThisFrame(false)
+, m_ProfilerEnabledLastFrame(false)
+, m_ProfilerAllowSamplingGlobal(false)
+, m_EnabledCount(0)
+, m_FramesLogged(0)
+, m_TextFile(NULL)
+, m_DataFile(NULL)
+, m_BinaryLogEnabled(false)
+, m_ProfilerCount(0)
+, m_FrameIDCounter(1)
+{
+#if SUPPORT_THREADS
+ m_MainThreadID = Thread::GetCurrentThreadID();
+#endif
+
+ m_PendingProfilerMode = kProfilerGame;
+ m_ProfilerMode = m_PendingProfilerMode;
+ ABSOLUTE_TIME_INIT(m_LastEnabledTime);
+
+ memset(m_PreviousFrames, 0, sizeof(m_PreviousFrames));
+
+ #if DEBUG_AUTO_PROFILER_LOG
+#if UNITY_OSX
+ SetEnabled(true);
+ string result = getenv ("HOME");
+ SetLogPath(AppendPathName( result, "Library/Logs/Unity/Profiler.log"));
+#elif UNITY_WIN
+ SetEnabled(true);
+ const char* tempPath = ::getenv("TEMP");
+ if (!tempPath)
+ tempPath = "C:";
+ SetLogPath(AppendPathName (tempPath, "UnityProfiler.log"));
+#endif
+ #endif // #if DEBUG_AUTO_PROFILER_LOG
+}
+
+
+UnityProfiler::~UnityProfiler()
+{
+ SetLogPath("");
+ UNITY_DELETE(m_TextFile, kMemProfiler);
+ UNITY_DELETE(m_DataFile, kMemProfiler);
+}
+
+void UnityProfiler::AddPerThreadProfiler (UnityProfilerPerThread* prof)
+{
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ m_Profilers.push_back(prof->GetProfilersListNode());
+ ++m_ProfilerCount;
+}
+
+void UnityProfiler::RemovePerThreadProfiler (UnityProfilerPerThread* prof)
+{
+ if (!this)
+ return;
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ prof->GetProfilersListNode().RemoveFromList();
+ --m_ProfilerCount;
+}
+
+
+void UnityProfiler::CheckPro()
+{
+ if (GetEnabled())
+ {
+ BuildSettings* buildSettings = GetBuildSettingsPtr();
+#if UNITY_EDITOR
+ if (buildSettings && !buildSettings->hasPROVersion)
+#else
+ if (buildSettings && !buildSettings->hasAdvancedVersion)
+#endif
+ {
+ ErrorString("Profiler is only supported in Unity Pro.");
+ SetEnabled(false);
+ }
+ }
+}
+
+void UnityProfiler::SetEnabled (bool val)
+{
+ BuildSettings* buildSettings = GetBuildSettingsPtr();
+#if UNITY_EDITOR
+ if (buildSettings && !buildSettings->hasPROVersion)
+ return;
+#else
+ if (buildSettings && !buildSettings->hasAdvancedVersion)
+ return;
+#endif
+
+ if(val)
+ m_PendingProfilerMode |= kProfilerEnabled;
+ else
+ m_PendingProfilerMode &= ~kProfilerEnabled;
+}
+
+void UnityProfiler::RecordPreviousFrame(ProfilerMode mode)
+{
+ UnityProfiler* profiler = UnityProfiler::GetPtr();
+ if(!profiler)
+ return;
+
+ if(profiler->m_ProfilerEnabledLastFrame)
+ {
+ GPUProfiler::EndFrame();
+ profiler->EndProfilingMode(mode);
+ profiler->EndFrame();
+ profiler->m_ProfilerEnabledLastFrame = false;
+ }
+}
+
+bool UnityProfiler::StartNewFrame(ProfilerMode mode)
+{
+ UnityProfiler* profiler = UnityProfiler::GetPtr();
+ if(!profiler)
+ return false;
+
+ UnityProfiler::Get().UpdateEnabled();
+
+ if (UnityProfiler::Get().GetEnabled())
+ {
+ profiler->BeginFrame();
+ profiler->StartProfilingMode(mode);
+ GPUProfiler::BeginFrame();
+ profiler->m_ProfilerEnabledLastFrame = true;
+ }
+
+ return profiler->m_ProfilerEnabledLastFrame;
+}
+
+void UnityProfiler::BeginFrame ()
+{
+ DebugAssert(IS_MAIN_THREAD(this));
+
+ CheckPro();
+
+ GfxDevice& device = GetGfxDevice();
+ device.ProfileControl(GfxDevice::kGfxProfDisableSampling, 0);
+
+ m_ProfilerMode = m_PendingProfilerMode;
+ m_ProfilerEnabledThisFrame = m_ProfilerMode & kProfilerEnabled;
+
+ {
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ UnityProfilerPerThread& prof = **it;
+ if (!prof.IsSeparateBeginEnd())
+ prof.m_ProfilerAllowSampling = false;
+ }
+ }
+ m_ProfilerAllowSamplingGlobal = false;
+
+ if(!m_ProfilerEnabledThisFrame)
+ return;
+
+ device.ProfileControl(GfxDevice::kGfxProfBeginFrame, m_ProfilerMode);
+ {
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ UnityProfilerPerThread& prof = **it;
+ if (!prof.IsSeparateBeginEnd())
+ prof.BeginFrame(m_ProfilerMode);
+ }
+ }
+
+
+ m_TotalProfilerFrameDuration = START_TIME;
+ // accumulates all time from startFrame(N) to startFrame(N+1)
+ m_ProfilerEnabledDuration = 0;
+}
+
+
+void UnityProfiler::BeginFrameSeparateThread(ProfilerMode mode)
+{
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ UnityProfilerPerThread* profTLS = UnityProfilerPerThread::ms_InstanceTLS;
+ if (!profTLS)
+ return;
+ Assert(profTLS->IsSeparateBeginEnd());
+
+ profTLS->BeginFrame(mode);
+}
+
+void UnityProfiler::DisableSamplingSeparateThread()
+{
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ UnityProfilerPerThread* profTLS = UnityProfilerPerThread::ms_InstanceTLS;
+ if (!profTLS)
+ return;
+ Assert(profTLS->IsSeparateBeginEnd());
+
+ profTLS->m_ProfilerAllowSampling = false;
+}
+
+void UnityProfiler::SetActiveSeparateThread(bool enabled)
+{
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ UnityProfilerPerThread* profTLS = UnityProfilerPerThread::ms_InstanceTLS;
+ if (!profTLS)
+ return;
+ Assert(profTLS->IsSeparateBeginEnd());
+
+ profTLS->SetIsActive(enabled);
+}
+
+
+void UnityProfiler::EndFrame ()
+{
+ Assert (m_EnabledCount == 0);
+
+ if (!m_ProfilerEnabledThisFrame)
+ return;
+
+ bool profileFrameValid = true;
+ int threadIdx = 0;
+ {
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ UnityProfilerPerThread& prof = **it;
+ prof.SetThreadIndex(threadIdx);
+ if (!prof.IsSeparateBeginEnd())
+ {
+ bool threadValid = prof.EndFrame();
+ if (threadIdx == 0 && !threadValid)
+ profileFrameValid = false;
+ }
+ ++threadIdx;
+ }
+ }
+
+ int curFrameID = 0;
+ if (profileFrameValid)
+ {
+ StartProfilingMode(kProfilerGame);
+
+ threadIdx = 0;
+ {
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ UnityProfilerPerThread& prof = **it;
+ if (!prof.IsSeparateBeginEnd())
+ prof.AddMiscSamplesAfterFrame(m_ProfilerEnabledDuration, threadIdx==0);
+ ++threadIdx;
+ }
+ }
+
+ EndProfilingMode(kProfilerGame);
+
+
+ // Collect GPU timing for recent frames (non blocking)
+ for (int i = kFrameCount - 2; i >= 0; i--)
+ {
+ if (m_PreviousFrames[i] != NULL)
+ GPUProfiler::CollectGPUTime(m_PreviousFrames[i]->m_ThreadData[0].m_GPUTimeSamples, false);
+ }
+
+ // Compute GPU timing for oldest frames (blocking)
+ ProfilerFrameData* oldestFrame = m_PreviousFrames[kFrameCount-1];
+ if(oldestFrame)
+ {
+ oldestFrame->m_TotalGPUTimeInMicroSec = GPUProfiler::ComputeGPUTime(oldestFrame->m_ThreadData[0].m_GPUTimeSamples);
+
+ Mutex::AutoLock prevFramesLock(m_PrevFramesMutex);
+
+ LogFrame(oldestFrame);
+#if UNITY_EDITOR
+ // profilerhistory takes ownership and pushes pointer on a vector;
+ ProfilerHistory::Get().AddFrameDataAndTransferOwnership (oldestFrame, ProfilerConnection::GetEditorGuid());
+ // allocate new frame
+ oldestFrame = UNITY_NEW(ProfilerFrameData, kMemProfiler) (m_ProfilerCount, ++m_FrameIDCounter);
+#elif ENABLE_PLAYERCONNECTION
+ ProfilerConnection::Get().SendFrameDataToEditor (*oldestFrame); // reuse allocated memory
+ // GPUProfiler::ExtractGPUTime(m_PreviousGPUTimeSamples); // TODO use this sceme instead to reduce memory
+ // ProfilerConnection::Get().TransferPartialData(...)
+ // m_PreviousGPUTimeSamples.swap(m_GPUTimeSamples);
+#endif
+ }
+
+ ProfilerFrameData* curFrame = NULL;
+ {
+ Mutex::AutoLock prevFramesLock(m_PrevFramesMutex);
+ for (int i = 1; i < kFrameCount; i++)
+ m_PreviousFrames[i] = m_PreviousFrames[i - 1];
+
+ m_PreviousFrames[0] = oldestFrame;
+
+ if (m_PreviousFrames[0] == NULL)
+ {
+ m_PreviousFrames[0] = UNITY_NEW(ProfilerFrameData,kMemProfiler) (m_ProfilerCount, ++m_FrameIDCounter);
+ }
+
+ curFrame = m_PreviousFrames[0];
+ }
+
+ curFrameID = curFrame->m_FrameID;
+ {
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ UnityProfilerPerThread& prof = **it;
+ if (!prof.IsSeparateBeginEnd())
+ prof.SaveToFrameData(*curFrame);
+ }
+ }
+
+ CollectProfilerStats(curFrame->allStats);
+ }
+
+ const unsigned frameIDAndValidFlag = curFrameID | (profileFrameValid ? 0x80000000 : 0);
+ GetGfxDevice().ProfileControl(GfxDevice::kGfxProfEndFrame, frameIDAndValidFlag);
+ #if ENABLE_JOB_SCHEDULER
+ GetJobScheduler().EndProfilerFrame (frameIDAndValidFlag);
+ #endif
+
+ ABSOLUTE_TIME frameStartTime = m_TotalProfilerFrameDuration;
+ m_TotalProfilerFrameDuration = SUBTRACTED_TIME(START_TIME, frameStartTime);
+ if (profileFrameValid)
+ {
+ m_PreviousFrames[0]->m_StartTimeUS = GetProfileTime(frameStartTime) / 1000;
+ m_PreviousFrames[0]->m_TotalCPUTimeInMicroSec = GetProfileTime(m_TotalProfilerFrameDuration) / 1000;
+ }
+
+ {
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ UnityProfilerPerThread& prof = **it;
+ if (!prof.IsSeparateBeginEnd())
+ prof.ClearFrame();
+ }
+ }
+ m_ProfilerEnabledThisFrame = false;
+ m_ProfilerAllowSamplingGlobal = false;
+}
+
+
+
+void UnityProfiler::EndFrameSeparateThread(unsigned frameIDAndValid)
+{
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ UnityProfilerPerThread* profTLS = UnityProfilerPerThread::ms_InstanceTLS;
+ if (!profTLS)
+ return;
+ Assert(profTLS->IsSeparateBeginEnd());
+
+ profTLS->EndFrame();
+
+ const bool frameValid = (frameIDAndValid & 0x80000000);
+ if (frameValid)
+ {
+ Mutex::AutoLock prevFramesLock(m_PrevFramesMutex);
+ int frameID = frameIDAndValid & 0x7fffffff;
+ for (int i = 0; i < kFrameCount; ++i)
+ {
+ ProfilerFrameData* frame = m_PreviousFrames[i];
+ if (!frame)
+ continue;
+ if (frame->m_FrameID != frameID)
+ continue;
+ profTLS->SaveToFrameData(*frame);
+ }
+ }
+
+ profTLS->ClearFrame();
+}
+
+
+void UnityProfiler::ClearPendingFrames()
+{
+ Mutex::AutoLock prevFramesLock(m_PrevFramesMutex);
+ for (int i = 0; i < kFrameCount; i++)
+ {
+ UNITY_DELETE(m_PreviousFrames[i],kMemProfiler);
+ m_PreviousFrames[i] = NULL;
+ }
+}
+
+
+const ProfilerSample* UnityProfilerPerThread::GetActiveSample(int parentLevel) const
+{
+ const int indexInStack = m_SampleStack.size()-1 - parentLevel;
+ if (indexInStack < 0 || indexInStack >= m_SampleStack.size())
+ return NULL;
+ const int sampleIndex = m_SampleStack[indexInStack];
+ const ProfilerSample* sample = &m_ActiveSamples[sampleIndex];
+ return sample;
+}
+
+
+void UnityProfilerPerThread::InjectGCCollectSample()
+{
+ ProfileTimeFormat collectTime = m_GCCollectTime;
+ m_GCCollectTime = 0;
+
+ BeginSample(&gGCCollect, NULL);
+ ProfilerSample* gcSample = GetActiveSample();
+ EndSample(START_TIME);
+ gcSample->timeUS = collectTime/1000;
+}
+
+
+void UnityProfilerPerThread::CreateOverheadSample()
+ {
+ BeginSample(&gOverheadProfile, NULL);
+ ProfilerSample* overheadSample = GetActiveSample();
+ EndSample(START_TIME);
+
+ // Calculate overhead time be taking the root time and subtracting all children.
+ ProfilerSample* root = GetRoot();
+ ProfileTimeFormat overheadTime = root->timeUS * 1000;
+ const ProfilerSample* sample = root + 1;
+ for (int i=0;i<root->nbChildren;i++)
+ {
+ overheadTime -= sample->timeUS*1000;
+ sample = SkipSampleRecurse(sample);
+ }
+ overheadSample->timeUS += overheadTime/1000;
+}
+
+
+const ProfilerSample* SkipSampleRecurse (const ProfilerSample* sample)
+{
+ const ProfilerSample* child = sample + 1;
+ for (int i=0;i<sample->nbChildren;i++)
+ child = SkipSampleRecurse(child);
+
+ return child;
+}
+
+
+static void UpdateWithSmallestTime (ABSOLUTE_TIME& val, ABSOLUTE_TIME newval, int iteration)
+{
+ if (iteration == 0 || IsSmallerAbsoluteTime (newval, val))
+ val = newval;
+}
+
+
+void UnityProfiler::StartProfilingMode (ProfilerMode mode)
+{
+ if(m_ProfilerMode & mode)
+ {
+ SetIsActive(true);
+ }
+}
+
+void UnityProfiler::EndProfilingMode (ProfilerMode mode)
+{
+ if(m_ProfilerMode & mode)
+ SetIsActive(false);
+}
+
+
+void UnityProfiler::SetIsActive (bool enabled)
+{
+ if (enabled)
+ {
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ UnityProfilerPerThread& prof = **it;
+ if (!prof.IsSeparateBeginEnd())
+ prof.ClearGCCollectTime();
+ }
+ }
+
+ if(!m_ProfilerEnabledThisFrame)
+ return;
+ // m_EnabledCount can go below 0 (double disable), but will not start before enableCount is 1
+ m_EnabledCount += (enabled?1:-1);
+ if ( enabled && (m_EnabledCount != 1))
+ return;
+
+ if (!enabled && (m_EnabledCount != 0))
+ return;
+
+
+ if (!enabled && m_ProfilerAllowSamplingGlobal)
+ {
+ m_ProfilerEnabledDuration += GetProfileTime(SUBTRACTED_TIME(START_TIME, m_LastEnabledTime));
+ ABSOLUTE_TIME_INIT(m_LastEnabledTime);
+ }
+
+ m_ProfilerAllowSamplingGlobal = enabled;
+ {
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ UnityProfilerPerThread& prof = **it;
+ if (!prof.IsSeparateBeginEnd())
+ prof.SetIsActive (enabled);
+ }
+ }
+ GetGfxDevice().ProfileControl(GfxDevice::kGfxProfSetActive, enabled ? 1 : 0);
+
+ if (enabled && m_ProfilerAllowSamplingGlobal)
+ {
+ Assert(GetProfileTime (m_LastEnabledTime) == 0);
+ m_LastEnabledTime = START_TIME;
+ }
+}
+
+
+void UnityProfiler::GetDebugStats (DebugStats& debugStats)
+{
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ debugStats.m_ProfilerMemoryUsage = 0;
+ debugStats.m_AllocatedProfileSamples = 0;
+
+ debugStats.m_ProfilerMemoryUsageOthers = 0;
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ debugStats.m_ProfilerMemoryUsageOthers += (*it)->GetAllocatedBytes();
+ }
+}
+
+void UnityProfiler::CleanupMonoMethodCaches()
+{
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ (*it)->CleanupMonoMethodCache();
+ }
+}
+
+
+#if ENABLE_MONO || UNITY_WINRT
+
+ProfilerSample* mono_profiler_begin(ScriptingMethodPtr method, ScriptingClassPtr profileKlass, ScriptingObjectPtr instance)
+{
+ UnityProfilerPerThread* profTLS = UnityProfilerPerThread::ms_InstanceTLS;
+ if (!profTLS || !profTLS->m_ProfilerAllowSampling)
+ return NULL;
+ UnityProfiler* profiler = UnityProfiler::ms_Instance;
+
+ if (!IS_MAIN_THREAD(profiler))
+ return NULL;
+
+ // If deep profiling, we return the current sample, which is the one we have to get back to when this call exits
+ if ((profiler->m_ProfilerMode & kProfilerDeepScripts) != 0)
+ return profTLS->GetActiveSample();
+
+ DebugAssert(profTLS->m_SampleStack.empty() != profTLS->m_ProfilerAllowSampling);
+
+ // Extract Object ptr for profiler
+ Object* objPtr = NULL;
+
+#if ENABLE_MONO
+ if (instance)
+ {
+ MonoClass* instanceClass = mono_object_get_class(instance);
+ if (mono_class_is_subclass_of(instanceClass, MONO_COMMON.unityEngineObject, false))
+ objPtr = ScriptingObjectOfType<Object>(instance).GetPtr();
+ }
+#endif
+
+ // Do we have this method's info in the cache?
+ ProfilerInformation* information;
+ UnityProfilerPerThread::MethodInfoCache::iterator it = profTLS->m_ActiveMethodCache.find(method);
+ if (it != profTLS->m_ActiveMethodCache.end())
+ information = it->second;
+ else
+ information = profTLS->CreateProfilerInformationForMethod(instance, method, scripting_method_get_name(method), profileKlass, ProfilerInformation::kScriptMonoRuntimeInvoke);
+
+ // Begin Sample
+ return profTLS->BeginSample(information, objPtr);
+}
+
+void mono_profiler_end(ProfilerSample* beginsample)
+{
+ UnityProfilerPerThread* profTLS = UnityProfilerPerThread::ms_InstanceTLS;
+ if (!profTLS || !profTLS->m_ProfilerAllowSampling)
+ return;
+ UnityProfiler* profiler = UnityProfiler::ms_Instance;
+
+ if (!IS_MAIN_THREAD(profiler))
+ return;
+
+ // roll back the stack if there has been an exception, and some end samples have been skipped
+ while(profTLS->GetActiveSample() != beginsample)
+ profTLS->EndSample(START_TIME);
+
+ // if not deep profiling, end the current sample
+ if ((profiler->m_ProfilerMode & kProfilerDeepScripts) == 0)
+ profTLS->EndSample(START_TIME);
+}
+
+#endif // #if ENABLE_MONO || UNITY_WINRT
+
+
+ProfilerInformation* UnityProfilerPerThread::CreateProfilerInformationForMethod(ScriptingObjectPtr object, ScriptingMethodPtr method, const char* methodName, ScriptingTypePtr profileKlass, int flags)
+{
+#if !ENABLE_MONO && !UNITY_WINRT
+ return 0;
+#else // ENABLE_MONO || UNITY_WINRT
+
+ ProfilerInformation* information = static_cast<ProfilerInformation*> (m_ActiveGlobalAllocator.allocate(sizeof(ProfilerInformation)));
+ information->group = kProfilerScripts;
+ information->flags = flags;
+ information->isWarning = false;
+
+#if ENABLE_MONO
+ const char* klassName = mono_class_get_name(mono_method_get_class(method->monoMethod));
+#else // UNITY_WINRT
+ const char* klassName = "";
+ if (object != SCRIPTING_NULL)
+ {
+ ScriptingTypePtr klass = scripting_object_get_class(object, GetScriptingTypeRegistry());
+ if (klass != NULL)
+ klassName = scripting_class_get_name(klass);
+ }
+#endif
+
+ if (profileKlass == NULL)
+ {
+ // Optimized snprintf(buffer, kNameBufferSize, "%s.%s()", klassName, methodName); to minimize profiler overhead
+ int size = 4;
+ for(int i=0;i<klassName[i] != 0;i++) { size++; }
+ for(int i=0;i<methodName[i] != 0;i++) { size++; }
+
+ char* allocatedBuffer = static_cast<char*> (m_ActiveGlobalAllocator.allocate(size));
+ information->name = allocatedBuffer;
+ char* c = allocatedBuffer;
+ for(int i=0;i<klassName[i] != 0;i++)
+ {
+ *c = klassName[i]; c++;
+ }
+
+ *c = '.'; c++;
+ for(int i=0;i<methodName[i] != 0;i++)
+ {
+ *c = methodName[i]; c++;
+ }
+
+ c[0] = '(';
+ c[1] = ')';
+ c[2] = 0;
+
+ // Put into method cache
+ m_ActiveMethodCache.insert (std::make_pair(method, information));
+
+ return information;
+ }
+ else
+ {
+ enum { kNameBufferSize = 256 };
+ char buffer[kNameBufferSize];
+ char coroutineMethodName[kNameBufferSize] = { 0 };
+
+ // The generated class for a coroutine is called "<Start>Iterator_1"
+ // We want to extract "Start" and use that as the method name.
+ const char* coroutineMethodNameEnd = NULL;
+ if (klassName[0] == '<')
+ coroutineMethodNameEnd = strchr(klassName, '>');
+
+ if (coroutineMethodNameEnd != NULL)
+ strncpy(coroutineMethodName, klassName + 1, std::min((int)(coroutineMethodNameEnd - (klassName + 1)), (int)kNameBufferSize));
+ else
+ strncpy(coroutineMethodName, klassName, kNameBufferSize);
+
+ snprintf(buffer, kNameBufferSize, "%s.%s() [Coroutine: %s]", scripting_class_get_name(profileKlass), coroutineMethodName, methodName);
+
+ int size = strlen(buffer)+1;
+ char* copyBuffer = static_cast<char*> (m_ActiveGlobalAllocator.allocate(size));
+ memcpy(copyBuffer, buffer, size);
+ information->name = copyBuffer;
+
+ // Put into method cache
+ m_ActiveMethodCache.insert (std::make_pair(method, information));
+
+ return information;
+ }
+#endif // ENABLE_MONO || UNITY_WINRT
+}
+
+
+
+ProfilerInformation* UnityProfilerPerThread::GetProfilerInformation (const std::string& name, UInt16 group, UInt16 flags, bool isWarning)
+{
+ DynamicMethodCache::iterator found = m_DynamicMethodCache.find(name);
+ if (found != m_DynamicMethodCache.end())
+ return &found->second;
+ else
+ {
+ // Put into method cache
+ DynamicMethodCache::iterator found = m_DynamicMethodCache.insert (std::make_pair(name, ProfilerInformation(NULL, (ProfilerGroup) group))).first;
+ found->second.name = found->first.c_str();
+ found->second.flags = flags;
+ found->second.isWarning = isWarning;
+ return &found->second;
+ }
+}
+
+
+void UnityProfilerPerThread::EnterMonoMethod(MonoMethod *method)
+{
+#if ENABLE_MONO
+ if (!m_ProfilerAllowSampling)
+ return;
+ const char* methodName = mono_method_get_name(method);
+ if (strncmp (methodName, "runtime_invoke", 14) == 0)
+ return;
+
+ // Do we have this method's info in the cache?
+ ScriptingMethodPtr scriptingMethod = GetScriptingMethodRegistry().GetMethod(method);
+ MethodInfoCache::iterator it = m_ActiveMethodCache.find(scriptingMethod);
+ if (it != m_ActiveMethodCache.end())
+ {
+ BeginSample (it->second, NULL);
+ return;
+ }
+
+ // Method info not in the cache; create and cache it
+ ProfilerInformation* information = CreateProfilerInformationForMethod(NULL, scriptingMethod, methodName, NULL, ProfilerInformation::kScriptEnterLeave);
+
+ // Begin Sample
+ BeginSample(information, NULL);
+ #endif // #if ENABLE_MONO
+}
+
+
+void UnityProfilerPerThread::LeaveMonoMethod(MonoMethod *method)
+{
+#if ENABLE_MONO
+ if (!m_ProfilerAllowSampling)
+ return;
+ const char* methodName = mono_method_get_name(method);
+ if (strncmp (methodName, "runtime_invoke", 14) == 0)
+ return;
+
+ ABSOLUTE_TIME time = START_TIME;
+ EndSample(time);
+ #endif // #if ENABLE_MONO
+}
+
+
+void UnityProfilerPerThread::SampleGCAllocation (MonoObject *obj, MonoClass *klass)
+{
+#if ENABLE_MONO
+ if (!m_ProfilerAllowSampling)
+ return;
+
+#if 0
+ // We can extract the name of the class being allocated here.
+ string info = Format("\n\t\t%s size: %d", mono_class_get_name(klass), size);
+#endif
+
+ int size = mono_object_get_size(obj);
+ ProfilerData::AllocatedGCMemory allocSample = {GetActiveSampleIndex(), size};
+ m_AllocatedGCMemorySamples.push_back(allocSample);
+ #endif // #if ENABLE_MONO
+}
+
+
+static void enter_mono_sample(void* pr, MonoMethod* method)
+{
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof)
+ prof->EnterMonoMethod(method);
+}
+
+static void leave_mono_sample(void* pr, MonoMethod* method)
+{
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof)
+ prof->LeaveMonoMethod(method);
+}
+
+
+#if ENABLE_MONO_MEMORY_PROFILER
+
+void UnityProfiler::SetupProfilerEvents ()
+{
+ int flags = kMonoProfilerDefaultFlags;
+ if (m_PendingProfilerMode & kProfilerDeepScripts)
+ flags |= MONO_PROFILE_ENTER_LEAVE;
+
+ mono_profiler_set_events (flags);
+}
+
+static void sample_mono_shutdown (void *prof)
+{
+}
+
+
+void UnityProfilerPerThread::SampleGCMonoCallback (void* pr, int event, int generation)
+{
+ if (event == 1)
+ {
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof)
+ {
+ prof->m_GCStartTime = START_TIME;
+ }
+ }
+
+ if (event == 4)
+ {
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof)
+ {
+ prof->m_GCCollectTime += GetProfileTime(ELAPSED_TIME(prof->m_GCStartTime));
+ }
+ }
+}
+
+static void sample_gc_resize (void *pr, SInt64 new_size)
+{
+ // printf_console("--- GC resize %d\n", (int)new_size);
+}
+
+static void sample_allocation (void* pr, MonoObject *obj, MonoClass *klass)
+{
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof)
+ prof->SampleGCAllocation(obj, klass);
+}
+
+
+void mono_profiler_startup ()
+{
+ mono_profiler_install (NULL, sample_mono_shutdown);
+
+ mono_profiler_install_gc(UnityProfilerPerThread::SampleGCMonoCallback, sample_gc_resize);
+ mono_profiler_install_allocation(sample_allocation);
+#if UNITY_EDITOR
+ // Deep profiling is only be available in editor
+ mono_profiler_install_enter_leave (enter_mono_sample, leave_mono_sample);
+#endif
+ int flags = kMonoProfilerDefaultFlags;
+ mono_profiler_set_events (flags);
+}
+
+#endif // ENABLE_MONO_MEMORY_PROFILER
+
+
+
+void UnityProfiler::LogFrame(ProfilerFrameData* data)
+{
+#if !UNITY_PEPPER
+ if (m_LogFile.empty())
+ return;
+
+ float fps = 1000000.0 / data->m_ThreadData[0].GetRoot()->timeUS;
+
+#if DEBUG_AUTO_PROFILER_LOG
+ const int kMinimumFramerate = 20;
+ if (fps > kMinimumFramerate)
+ return;
+#endif
+
+ {
+ string fpsCategory;
+ if (fps < 10)
+ fpsCategory = "Very Low";
+ else if (fps < 20)
+ fpsCategory = "Low";
+ else if (fps < 25)
+ fpsCategory = "Okay";
+ else if (fps < 25)
+ fpsCategory = "Average";
+ else if (fps < 40)
+ fpsCategory = "Good";
+ else
+ fpsCategory = "Very Good";
+ std::string output = Format(" -- Frame %d Framerate: %.1f [%s Framerate]\n", ++m_FramesLogged, fps, fpsCategory.c_str());
+
+ m_TextFile->Write(output.c_str(),output.length());
+ }
+
+ if(m_BinaryLogEnabled)
+ {
+#if ENABLE_PLAYERCONNECTION
+ /// TODO: make async write. Causes stalls
+ dynamic_array<int> buffer;
+
+ SerializeFrameData(*data, buffer);
+ int size = buffer.size()*sizeof(int);
+ m_DataFile->Write(&size,sizeof(size));
+ int threadCount = data->m_ThreadCount;
+ m_DataFile->Write(&threadCount, sizeof(threadCount));
+ m_DataFile->Write(buffer.begin(), size);
+#endif
+ }
+#endif
+}
+
+void UnityProfiler::SetLogPath (std::string logPath)
+{
+#if WEBPLUG
+ if(!logPath.empty())
+ {
+ std::string name(GetConsoleLogPath());
+ ConvertSeparatorsToUnity(name);
+ logPath = DeleteLastPathNameComponent(name)+"/profile.log";
+ }
+#endif
+ if (m_LogFile != logPath)
+ {
+ m_LogFile = logPath;
+#if !UNITY_PEPPER
+ if (!logPath.empty())
+ {
+ m_FramesLogged = 0;
+ if(!m_TextFile)
+ m_TextFile = UNITY_NEW(File, kMemProfiler);
+ if(!m_DataFile)
+ m_DataFile = UNITY_NEW(File, kMemProfiler);
+ m_TextFile->Open(m_LogFile, File::kWritePermission);
+ m_DataFile->Open(m_LogFile+".data", File::kWritePermission);
+ }
+ else
+ {
+ if(m_TextFile)
+ m_TextFile->Close();
+ if(m_DataFile)
+ m_DataFile->Close();
+ }
+#endif
+ }
+}
+void UnityProfiler::AddFramesFromFile(string path)
+{
+#if UNITY_EDITOR
+ File dataFile;
+ dataFile.Open(path+".data", File::kReadPermission);
+ int size;
+ dynamic_array<int> buffer;
+ while(dataFile.Read(&size,sizeof(size)))
+ {
+ int threadCount;
+ dataFile.Read(&threadCount,sizeof(threadCount));
+ buffer.resize_uninitialized(size/sizeof(int));
+ dataFile.Read(buffer.begin(),size);
+ ProfilerFrameData* frame = UNITY_NEW(ProfilerFrameData, kMemProfiler) (threadCount, 0);
+
+ int fileguid = ProfilerConnection::Get().GetConnectedProfiler();
+ if( UnityProfiler::DeserializeFrameData(frame,buffer.begin(),size) )
+ ProfilerHistory::Get().AddFrameDataAndTransferOwnership(frame, fileguid);
+ else
+ UNITY_DELETE(frame, kMemProfiler);
+ }
+ dataFile.Close();
+#endif
+}
+
+void UnityProfiler::SerializeFrameData(ProfilerFrameData& frame, dynamic_array<int>& buffer)
+{
+#if ENABLE_PLAYERCONNECTION
+ buffer.push_back(UNITY_LITTLE_ENDIAN);
+ buffer.push_back(kProfilerDataStreamVersion);
+
+ frame.Serialize(buffer);
+
+ buffer.push_back(0xAFAFAFAF);
+#endif
+}
+
+bool UnityProfiler::DeserializeFrameData(ProfilerFrameData* frame, const void* data, int size)
+{
+#if ENABLE_PLAYERCONNECTION
+ int* buffer = (int*)data;
+ int wordsize = size/sizeof(int);
+ int* endBuffer = buffer + wordsize;
+ int** bitstream = &buffer;
+
+ int dataIsLittleEndian = *((*bitstream)++);
+ bool shouldswap = UNITY_LITTLE_ENDIAN ? dataIsLittleEndian == 0 : dataIsLittleEndian != 0;
+ if(shouldswap)
+ {
+ int* ptr = *bitstream;
+ while(ptr < endBuffer)
+ SwapEndianBytes(*(ptr++));
+ }
+
+ int version = *((*bitstream)++);
+
+ if(version != kProfilerDataStreamVersion)
+ return false;
+
+ frame->Deserialize(bitstream, shouldswap);
+ Assert(**bitstream == 0xAFAFAFAF);
+ return true;
+#else
+ return false;
+#endif
+}
+
+
+// --------------------------------------------------------------------------
+// Profiler serialization
+
+
+#if ENABLE_PLAYERCONNECTION
+template< class T >
+void WriteArray(dynamic_array<int>& bitstream, const dynamic_array<T>& array)
+{
+ bitstream.push_back(array.size());
+ if(array.size() > 0)
+ {
+ int startindex = bitstream.size();
+ bitstream.resize_uninitialized( startindex + array.size() * sizeof(T) / sizeof(int) );
+ memcpy( (char*)&bitstream[startindex], (char*)&array[0], sizeof(T) * array.size() );
+ }
+}
+
+template< class T >
+void ReadArray( int** bitstream, dynamic_array<T>& array)
+{
+ int size = *((*bitstream)++);
+ array.resize_uninitialized(size);
+ if(size > 0)
+ {
+ memcpy((char*)&array[0], (char*)*bitstream, sizeof(T) * size);
+ *bitstream += sizeof(T) * size / sizeof(int);
+ }
+}
+
+template< typename T >
+void ReadArrayFixup( int** bitstream, dynamic_array<T>& array, bool swapdata)
+{
+ ReadArray<T>(bitstream, array);
+ if (swapdata)
+ {
+ for (typename dynamic_array<T>::iterator it = array.begin(); it != array.end(); ++it)
+ {
+ (*it).Fixup();
+ }
+ }
+}
+
+void WriteConditionaly(dynamic_array<int>& bitstream, ProfilerInformation* object)
+{
+ if (object)
+ {
+ bitstream.push_back(1);
+ ProfilerFrameData::SerializeProfilerInformation(*object, bitstream);
+ }
+ else
+ bitstream.push_back(0);
+}
+
+void ReadConditionaly( int** bitstream, ProfilerInformation*& object, bool swapdata)
+{
+ int condition = *((*bitstream)++);
+ if(condition)
+ object = ProfilerFrameData::DeserializeProfilerInformation(bitstream, swapdata);
+}
+
+static void SerializeString (dynamic_array<int>& bitstream, int len, const char* str)
+{
+ int startindex = bitstream.size();
+ bitstream.resize_initialized( startindex + len/4 + 1);
+ memcpy((char*)&bitstream[startindex], str, len+1);
+}
+
+static std::string DeserializeString (int**& bitstream, bool swapdata)
+{
+ char* chars = (char*)*bitstream;
+ if (swapdata)
+ {
+ int wordcount = strlen(chars)/4 + 1;
+ for(int i = 0; i < wordcount; i++)
+ SwapEndianBytes((*bitstream)[i]);
+ }
+ std::string name((char*)*bitstream);
+ (*bitstream) += name.length()/4 + 1;
+ return name;
+}
+
+
+#define HIPART(x) ((x>>32) & 0xFFFFFFFF)
+#define LOPART(x) (x & 0xFFFFFFFF)
+
+void ProfilerFrameData::Serialize( dynamic_array<int>& bitstream )
+{
+ bitstream.push_back(frameIndex);
+ bitstream.push_back(realFrame);
+ bitstream.push_back(m_StartTimeUS);
+ bitstream.push_back(m_TotalCPUTimeInMicroSec);
+ bitstream.push_back(m_TotalGPUTimeInMicroSec);
+ allStats.Serialize(bitstream);
+
+ bitstream.push_back(m_ThreadCount);
+ for (int t = 0; t < m_ThreadCount; ++t)
+ {
+ const ThreadData& tdata = m_ThreadData[t];
+
+ SerializeString(bitstream, tdata.m_ThreadName.size(), tdata.m_ThreadName.c_str());
+ bitstream.push_back(tdata.m_AllSamples.size());
+ for(int i = 0; i < tdata.m_AllSamples.size(); i++)
+ {
+ bitstream.push_back(tdata.m_AllSamples[i].timeUS);
+ bitstream.push_back(tdata.m_AllSamples[i].startTimeUS);
+ bitstream.push_back(tdata.m_AllSamples[i].nbChildren);
+ }
+
+ bitstream.push_back(tdata.m_GPUTimeSamples.size());
+ for(int i = 0; i < tdata.m_GPUTimeSamples.size(); i++)
+ {
+ bitstream.push_back(tdata.m_GPUTimeSamples[i].gpuTimeInMicroSec);
+ bitstream.push_back(tdata.m_GPUTimeSamples[i].relatedSampleIndex);
+ bitstream.push_back(tdata.m_GPUTimeSamples[i].gpuSection);
+ }
+
+ // Don't write m_InstanceIDSamples, since the IDs are not portable
+ WriteArray(bitstream, tdata.m_AllocatedGCMemorySamples);
+ for(int i = 0; i < tdata.m_AllSamples.size(); i++)
+ WriteConditionaly(bitstream,tdata.m_AllSamples[i].information);
+
+ WriteArray(bitstream, tdata.m_WarningSamples);
+ }
+}
+
+void ProfilerFrameData::Deserialize( int** bitstream, bool swapdata )
+{
+ frameIndex = *((*bitstream)++);
+ realFrame = *((*bitstream)++);
+ m_StartTimeUS = *((*bitstream)++);
+ m_TotalCPUTimeInMicroSec = *((*bitstream)++);
+ m_TotalGPUTimeInMicroSec = *((*bitstream)++);
+ allStats.Deserialize(bitstream, swapdata);
+
+ int threadCount = *((*bitstream)++);
+ if (threadCount != m_ThreadCount)
+ {
+ delete[] m_ThreadData;
+ m_ThreadData = new ThreadData[threadCount];
+ m_ThreadCount = threadCount;
+ }
+
+ for (int t = 0; t < m_ThreadCount; ++t)
+ {
+ ThreadData& tdata = m_ThreadData[t];
+
+ tdata.m_ThreadName = DeserializeString (bitstream, swapdata);
+
+ tdata.m_AllSamples.resize_uninitialized(*((*bitstream)++));
+
+ for(int i = 0; i < tdata.m_AllSamples.size(); i++)
+ {
+ tdata.m_AllSamples[i].timeUS = *((*bitstream)++);
+ tdata.m_AllSamples[i].startTimeUS = *((*bitstream)++);
+ tdata.m_AllSamples[i].nbChildren = *((*bitstream)++);
+ tdata.m_AllSamples[i].information = NULL;
+ }
+
+ tdata.m_GPUTimeSamples.resize_uninitialized(*((*bitstream)++));
+ for(int i = 0; i < tdata.m_GPUTimeSamples.size(); i++)
+ {
+ tdata.m_GPUTimeSamples[i].gpuTimeInMicroSec = *((*bitstream)++);
+ tdata.m_GPUTimeSamples[i].relatedSampleIndex = *((*bitstream)++);
+ tdata.m_GPUTimeSamples[i].gpuSection = (GpuSection)(*((*bitstream)++));
+ tdata.m_GPUTimeSamples[i].timerQuery = NULL;
+ }
+
+ // m_InstanceIDSamples are not written, since the IDs are not portable
+ tdata.m_InstanceIDSamples.resize_uninitialized(0);
+ ReadArray(bitstream, tdata.m_AllocatedGCMemorySamples);
+ for(int i = 0; i < tdata.m_AllSamples.size(); i++)
+ ReadConditionaly(bitstream, tdata.m_AllSamples[i].information, swapdata);
+
+ ReadArray(bitstream, tdata.m_WarningSamples);
+ }
+}
+
+
+void ProfilerFrameData::SerializeProfilerInformation( const ProfilerInformation& info, dynamic_array<int>& bitstream )
+{
+ SerializeString (bitstream, strlen(info.name), info.name);
+ bitstream.push_back((info.group << 16) | (info.flags << 8) | info.isWarning);
+}
+
+ProfilerInformation* ProfilerFrameData::DeserializeProfilerInformation( int** bitstream, bool swapdata )
+{
+ std::string name = DeserializeString (bitstream, swapdata);
+
+ int groupFlags = *((*bitstream)++);
+ UInt16 group = groupFlags >> 16;
+ UInt8 flags = (groupFlags & 0xFF00) >> 8;
+ UInt8 warn = groupFlags & 0xFF;
+
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ DebugAssert(prof);
+ return prof->GetProfilerInformation(name, group, flags, warn);
+}
+
+#endif // #if ENABLE_PLAYERCONNECTION
+
+
+
+
+#endif // #if ENABLE_PROFILER
diff --git a/Runtime/Profiler/ProfilerImpl.h b/Runtime/Profiler/ProfilerImpl.h
new file mode 100644
index 0000000..413b0ad
--- /dev/null
+++ b/Runtime/Profiler/ProfilerImpl.h
@@ -0,0 +1,304 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_PROFILER
+
+#include "Runtime/Utilities/LogAssert.h"
+#include "TimeHelper.h"
+#include "Runtime/Allocator/LinearAllocator.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/ThreadSpecificValue.h"
+#include "Profiler.h"
+#include "ProfilerStats.h"
+#include "TimeHelper.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include <map>
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+
+class GfxTimerQuery;
+class ProfilerFrameData;
+class File;
+
+
+// Additional data possibly "attached" to base ProfilerSamples.
+// GPU times, object IDs etc. Separated out since only a few
+// samples need it.
+namespace ProfilerData
+{
+ // GPU time per draw call or other interesting GPU event.
+ struct GPUTime
+ {
+ UInt32 relatedSampleIndex;
+ GfxTimerQuery* timerQuery;
+ int gpuTimeInMicroSec;
+ GpuSection gpuSection;
+ };
+
+ // Instance ID of object that the source of profiled event.
+ struct InstanceID
+ {
+ UInt32 relatedSampleIndex;
+ SInt32 instanceID;
+ };
+
+ // Managed GC memory allocated during the call.
+ struct AllocatedGCMemory
+ {
+ UInt32 relatedSampleIndex;
+ UInt32 allocatedGCMemory;
+ };
+};
+
+// Base profiler sample with CPU times;
+// minimal amount of actual per-sample data.
+struct ProfilerSample
+{
+ ProfilerSample()
+ : information(NULL)
+ , startTimeUS(0)
+ , timeUS(0)
+ , nbChildren(0)
+ {
+ }
+
+ UInt64 startTimeUS; // start time in microsecs
+ UInt32 timeUS; // duration in microsecs
+
+ ProfilerInformation* information; // actual information
+
+ int nbChildren;
+};
+
+const ProfilerSample* SkipSampleRecurse (const ProfilerSample* sample);
+
+
+
+class UnityProfilerPerThread
+{
+public:
+ static UNITY_TLS_VALUE(UnityProfilerPerThread*) ms_InstanceTLS;
+ bool m_ProfilerAllowSampling; // public for performance in free sampling functions
+
+public:
+ static void Initialize(const char* threadName, bool separateBeginEnd = false);
+ static void Cleanup();
+
+ ~UnityProfilerPerThread();
+
+ bool GetIsActive () const { return m_ProfilerAllowSampling; }
+ void SetIsActive (bool enabled);
+
+ void BeginFrame(ProfilerMode mode);
+ bool EndFrame();
+ void ClearFrame();
+ void AddMiscSamplesAfterFrame(ProfileTimeFormat frameDuration, bool addOverhead);
+ void SaveToFrameData (ProfilerFrameData& dst) const;
+
+ ProfilerSample* BeginSample(ProfilerInformation* info, const Object* obj);
+ void EndSample(ABSOLUTE_TIME time);
+
+ void BeginSampleDynamic(const std::string& name, const Object* obj);
+ void EndSampleDynamic() { if (m_ProfilerAllowSampling) EndSample(START_TIME); }
+
+ void AddGPUSample (const ProfilerData::GPUTime& sample) { m_GPUTimeSamples.push_back(sample); }
+
+
+ // Clear the mono method cache, but do not clear the memory used to store info/names.
+ // We still need that memory to display profiler information.
+ void CleanupMonoMethodCache() { m_ActiveMethodCache.clear(); }
+
+ ProfilerSample* GetRoot () { return m_NextSampleIndex == 0 ? NULL : &m_ActiveSamples[0]; }
+ UInt32 GetActiveSampleIndex () { return m_SampleStack.back(); }
+ ProfilerSample* GetActiveSample ()
+ {
+ // @TODO out of memory?
+ return &m_ActiveSamples[ GetActiveSampleIndex () ];
+ }
+ const ProfilerSample* GetActiveSample(int parentLevel) const;
+
+ ListNode<UnityProfilerPerThread>& GetProfilersListNode() { return m_ProfilersListNode; }
+
+ ProfilerInformation* CreateProfilerInformationForMethod(ScriptingObjectPtr object, ScriptingMethodPtr method, const char* methodName, ScriptingTypePtr profileKlass, int flags);
+ ProfilerInformation* GetProfilerInformation( const std::string& name, UInt16 group, UInt16 flags, bool isWarning);
+
+ void EnterMonoMethod (MonoMethod *method);
+ void LeaveMonoMethod (MonoMethod *method);
+ void SampleGCAllocation (MonoObject *obj, MonoClass *klass);
+ static void SampleGCMonoCallback (void *prof, int event, int generation);
+ void ClearGCCollectTime() { m_GCCollectTime = 0; }
+
+ size_t GetAllocatedBytes() const { return m_ActiveGlobalAllocator.GetAllocatedBytes(); }
+ void SetThreadIndex(int idx) { m_ThreadIndex = idx; }
+ bool IsSeparateBeginEnd() const { return m_SeparateBeginEnd; }
+
+private:
+ UnityProfilerPerThread(const char* threadName, bool separateBeginEnd); // prevent public creation
+
+ void InjectGCCollectSample();
+ void CreateOverheadSample();
+
+private:
+ int m_NextSampleIndex;
+ dynamic_array<ProfilerSample> m_ActiveSamples;
+ dynamic_array<UInt32> m_SampleStack;
+ dynamic_array<ABSOLUTE_TIME> m_SampleTimeBeginStack;
+ dynamic_array<ProfilerData::GPUTime> m_GPUTimeSamples;
+ dynamic_array<ProfilerData::InstanceID> m_InstanceIDSamples;
+ dynamic_array<ProfilerData::AllocatedGCMemory> m_AllocatedGCMemorySamples;
+ dynamic_array<UInt32> m_WarningSamples;
+ bool m_OutOfSampleMemory;
+ bool m_ErrorDurringFrame;
+
+ ProfileTimeFormat m_GCCollectTime;
+
+ typedef UNITY_MAP(kMemProfiler, ScriptingMethodPtr, ProfilerInformation*) MethodInfoCache;
+ typedef UNITY_MAP(kMemProfiler, std::string, ProfilerInformation) DynamicMethodCache;
+
+ ForwardLinearAllocator m_ActiveGlobalAllocator;
+ MethodInfoCache m_ActiveMethodCache;
+ DynamicMethodCache m_DynamicMethodCache;
+
+ ListNode<UnityProfilerPerThread> m_ProfilersListNode;
+
+ ABSOLUTE_TIME m_GCStartTime;
+
+ const char* m_ThreadName;
+ int m_ThreadIndex;
+ bool m_SeparateBeginEnd;
+
+ friend ProfilerSample* mono_profiler_begin(ScriptingMethodPtr method, ScriptingClassPtr profileKlass, ScriptingObjectPtr instance);
+ friend void mono_profiler_end(ProfilerSample* beginsample);
+};
+
+
+
+class UnityProfiler
+{
+public:
+ static UnityProfiler* ms_Instance; // singleton
+
+public:
+ // Destructor
+ ~UnityProfiler();
+
+ static void Initialize ();
+ static void CleanupGfx ();
+ static void Cleanup ();
+
+ // Singleton accessor for profiler
+ static UnityProfiler& Get() { return *ms_Instance; }
+ static UnityProfiler* GetPtr() { return ms_Instance; }
+
+ void SetEnabled (bool val);
+ void UpdateEnabled() { m_ProfilerMode = m_PendingProfilerMode; }
+ bool GetEnabled () { return (m_ProfilerMode & kProfilerEnabled) != 0; }
+
+ void SetProfileEditor (bool val) { val ? m_PendingProfilerMode |= kProfilerEditor : m_PendingProfilerMode &= ~kProfilerEditor ; }
+ bool GetProfileEditor () { return (m_PendingProfilerMode & kProfilerEditor) != 0; }
+
+ void SetDeepProfiling (bool val) { val ? m_PendingProfilerMode |= kProfilerDeepScripts : m_PendingProfilerMode &= ~kProfilerDeepScripts ; }
+ bool GetDeepProfiling () { return (m_PendingProfilerMode & kProfilerDeepScripts) != 0; }
+
+ void StartProfilingMode (ProfilerMode mode);
+ void EndProfilingMode (ProfilerMode mode);
+
+ void BeginFrame ();
+ void EndFrame ();
+ void BeginFrameSeparateThread(ProfilerMode mode);
+ void EndFrameSeparateThread(unsigned frameIDAndValid);
+ void DisableSamplingSeparateThread();
+ void SetActiveSeparateThread(bool enabled);
+
+ void ClearPendingFrames();
+
+
+ void SetupProfilerEvents ();
+ ProfilerSample* AllocateSample();
+
+ void GetDebugStats (DebugStats& debugStats);
+
+ void CleanupMonoMethodCaches();
+
+
+ void SetLogPath (std::string logPath);
+ std::string GetLogPath () { return m_LogFile; }
+
+ void EnableBinaryLog(bool val) { m_BinaryLogEnabled = val; }
+ bool BinaryLogEnabled() { return m_BinaryLogEnabled; }
+
+ static void AddFramesFromFile(std::string path);
+
+ static void SerializeFrameData(ProfilerFrameData& frame, dynamic_array<int>& buffer);
+ static bool DeserializeFrameData(ProfilerFrameData* frame, const void* data, int size);
+
+ void Serialize( dynamic_array<int>& bs );
+ static ProfilerInformation* Deserialize( int** bs, bool swapdata );
+
+ void AddPerThreadProfiler (UnityProfilerPerThread* prof);
+ void RemovePerThreadProfiler (UnityProfilerPerThread* prof);
+
+ static void RecordPreviousFrame(ProfilerMode mode);
+ static bool StartNewFrame(ProfilerMode mode);
+
+private:
+ UnityProfiler(); // prevent accidental creation
+
+ void SetIsActive (bool enabled);
+
+ void CheckPro();
+
+ void LogFrame (ProfilerFrameData* frame);
+
+private:
+ bool m_ProfilerEnabledThisFrame;
+ bool m_ProfilerEnabledLastFrame;
+ bool m_ProfilerAllowSamplingGlobal;
+ int m_EnabledCount;
+
+
+ // hold framedata for sevral frames to wait for GPU samples
+ enum { kFrameCount = 2 };
+ ProfilerFrameData* m_PreviousFrames[kFrameCount];
+
+ // indicates Disabled or what elements we are profiling
+ ProfilerMode m_ProfilerMode;
+ ProfilerMode m_PendingProfilerMode;
+
+ #if SUPPORT_THREADS
+ Thread::ThreadID m_MainThreadID;
+ #endif
+
+ // accumulates time inside profiler start/stop (multiple start/stop pair are allowed during the frame)
+ ABSOLUTE_TIME m_TotalProfilerFrameDuration;
+ // accumulates all time from startFrame(N) to startFrame(N+1)
+ ProfileTimeFormat m_ProfilerEnabledDuration;
+ ABSOLUTE_TIME m_LastEnabledTime;
+
+ typedef List< ListNode<UnityProfilerPerThread> > ProfilersList;
+ ProfilersList m_Profilers;
+ Mutex m_ProfilersMutex;
+ int m_ProfilerCount;
+
+ Mutex m_PrevFramesMutex;
+ int m_FrameIDCounter;
+
+
+ std::string m_LogFile;
+ int m_FramesLogged;
+ bool m_BinaryLogEnabled;
+ File* m_TextFile;
+ File* m_DataFile;
+
+
+ friend class ProfilerHistory;
+ friend ProfilerSample* mono_profiler_begin(ScriptingMethodPtr method, ScriptingClassPtr profileKlass, ScriptingObjectPtr instance);
+ friend void mono_profiler_end(ProfilerSample* beginsample);
+};
+
+
+
+#endif // #if ENABLE_PROFILER
diff --git a/Runtime/Profiler/ProfilerProperty.cpp b/Runtime/Profiler/ProfilerProperty.cpp
new file mode 100644
index 0000000..c854908
--- /dev/null
+++ b/Runtime/Profiler/ProfilerProperty.cpp
@@ -0,0 +1,789 @@
+#include "UnityPrefix.h"
+#include "ProfilerProperty.h"
+
+#if ENABLE_PROFILER && UNITY_EDITOR
+
+#include "ProfilerImpl.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+#include <iostream>
+#include <sstream>
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+
+/*
+TODO:
+* Sync time filter does not work
+* non multiple selection in raw hierarch
+
+* select object in double click
+* Tooltip showing all names
+ * deep profiler???
+ */
+
+
+
+// Constant and static variables initialization
+static const char* const kRadixPoint = "0.";
+static const char* const kPercentFmt = "%d.%d%%";
+static const char* kNotAvailable = "N/A";
+
+
+struct ProfilerHierarchy
+{
+ ProfilerHierarchy() : oneSample(~0), sampleIdx(kMemProfiler) {}
+
+ // Can correspond to one or more individual samples. Optimize common use case (especially in timeline view)
+ // when there is exactly one sample: that is stored in 'oneSample'. If there are more samples, they
+ // are put into an array.
+ UInt32 oneSample;
+ dynamic_array<UInt32> sampleIdx;
+
+ UNITY_VECTOR(kMemProfiler,ProfilerHierarchy) children;
+ ProfilerHierarchy* parent;
+
+ ProfilerSampleData data;
+};
+
+// Implement swap to avoid massive amounts of copying during the sort function
+namespace std
+{
+ template<>
+ inline void swap(ProfilerHierarchy& __a, ProfilerHierarchy& __b)
+ {
+ std::swap(__a.oneSample, __b.oneSample);
+ __a.sampleIdx.swap(__b.sampleIdx);
+ __a.children.swap(__b.children);
+ std::swap(__a.parent, __b.parent);
+ std::swap(__a.data, __b.data);
+ }
+}
+
+
+static std::string GetFormattedPercent(ProfileTimeFormat inTime, ProfileTimeFormat totalTime)
+{
+ if (totalTime == 0)
+ return "0.0%";
+
+ // Calculate percentage, format obtained value and convert into string
+ UInt64 time = inTime * (UInt64) 1000;
+ UInt64 integer = time / totalTime;
+
+ return Format(kPercentFmt, (int) integer / 10, (int) integer % 10);
+}
+
+static std::string GetFormattedTime(ProfileTimeFormat time, int decimals = 2)
+{
+ time /= (1000000/Pow(10,decimals));
+
+ std::string value = Format("%u", time);
+
+ int length = value.length();
+
+ if (length > decimals)
+ {
+ value.insert(length - decimals, 1, '.');
+ }
+ else
+ {
+ std::string tmp;
+ tmp.assign(decimals, '0');
+
+ value.copy((char*) tmp.c_str() + (decimals - length), length);
+
+ return kRadixPoint + tmp;
+ }
+
+ return value;
+}
+
+static const char* GetObjectNameFromInstanceID (SInt32 instanceID)
+{
+ Object* obj = dynamic_instanceID_cast<Object*>(instanceID);
+ if (obj)
+ return obj->GetName();
+ else
+ return kNotAvailable;
+}
+
+static const char* GetFunctionName(const ProfilerSampleData& input)
+{
+ if (input.information)
+ return input.information->name;
+ else
+ return "Invalid";
+}
+
+static bool LargerFunctionName(const ProfilerSampleData& lhs, const ProfilerSampleData& rhs)
+{
+
+ if (!lhs.information || !rhs.information)
+ return &lhs < &rhs;
+
+ return StrICmp(lhs.information->name, rhs.information->name) > 0;
+}
+
+
+static void GetSortedInstanceIDs (const dynamic_array<AdditionalProfilerSampleData*>& additionalData, UInt32 oneSample, const dynamic_array<UInt32>& samples, dynamic_array<SInt32>& outputInstanceIDs)
+{
+ // Get sorted list of instanceID's
+ outputInstanceIDs.push_back(additionalData[oneSample]?additionalData[oneSample]->instanceID:0);
+ for (int i=0;i<samples.size();i++)
+ outputInstanceIDs.push_back(additionalData[samples[i]]?additionalData[samples[i]]->instanceID:0);
+ std::sort (outputInstanceIDs.begin(), outputInstanceIDs.end());
+}
+
+static const char* GetObjectNameSummary (UInt32 oneSample, const dynamic_array<UInt32>& samples, const dynamic_array<AdditionalProfilerSampleData*>& additionalData)
+{
+ int instanceID = additionalData[oneSample] ? additionalData[oneSample]->instanceID : 0;
+ const char* name = GetObjectNameFromInstanceID(instanceID);
+
+ for (int i=0;i<samples.size();i++)
+ {
+ int instanceID = additionalData[samples[i]]?additionalData[samples[i]]->instanceID:0;
+ const char* tempName = GetObjectNameFromInstanceID(instanceID);
+ if (name != tempName) //@TODO: this is just comparing pointers?!
+ return "multiple";
+ }
+
+ return name;
+}
+
+static std::string GetProfilerColumn (const ProfilerSampleData& data, UInt32 oneSample, const dynamic_array<UInt32>& samples, const dynamic_array<AdditionalProfilerSampleData*>& additionalData, bool supportsGPUProfiler, ProfilerColumn column)
+{
+ switch (column)
+ {
+ case kFunctionNameColumn:
+ return GetFunctionName(data);
+
+ case kTotalPercentColumn:
+ return GetFormattedPercent(data.time, data.rootTime);
+
+ case kSelfPercentColumn:
+ if (data.time > data.childrenTime)
+ return GetFormattedPercent(data.time - data.childrenTime, data.rootTime);
+ else
+ return GetFormattedPercent(0u, 1u);
+
+ case kTotalGPUPercentColumn:
+ if (!supportsGPUProfiler)
+ return kNotAvailable;
+ else if (supportsGPUProfiler)
+ return GetFormattedPercent(data.gpuTime, data.rootGPUTime);
+ else
+ return kNotAvailable;
+
+ case kSelfGPUPercentColumn:
+ if (!supportsGPUProfiler)
+ return kNotAvailable;
+ else if (data.gpuTime > data.childrenGPUTime)
+ return GetFormattedPercent(data.gpuTime - data.childrenGPUTime, data.rootGPUTime);
+ else
+ return GetFormattedPercent(0u, 1u);
+
+ case kCallsColumn:
+ return Format("%u", data.numberOfCalls);
+
+ case kWarningColumn:
+ if(data.warningCount == 0)
+ return "";
+ return Format("%u", data.warningCount);
+
+ case kGCMemory:
+ return FormatBytes(data.allocatedGCMemory);
+
+ case kTotalTimeColumn:
+ return GetFormattedTime(data.time);
+
+ case kSelfTimeColumn:
+ if (data.time > data.childrenTime)
+ return GetFormattedTime(data.time - data.childrenTime);
+ else
+ return GetFormattedTime(0u);
+
+ case kDrawCallsColumn:
+ if (supportsGPUProfiler)
+ return Format("%u", data.gpuSamplesCount);
+ else
+ return kNotAvailable;
+
+ case kTotalGPUTimeColumn:
+ if (supportsGPUProfiler)
+ return GetFormattedTime(data.gpuTime,3);
+ else
+ return kNotAvailable;
+
+ case kSelfGPUTimeColumn:
+ if (!supportsGPUProfiler)
+ return kNotAvailable;
+ else if (data.gpuTime > data.childrenGPUTime)
+ return GetFormattedTime(data.gpuTime - data.childrenGPUTime);
+ else
+ return GetFormattedTime(0u);
+
+ case kObjectNameColumn:
+
+ return GetObjectNameSummary(oneSample, samples, additionalData);
+
+ default:
+ AssertString("Unimplemented GetProfilerColumn ");
+ return "";
+ }
+}
+
+
+static bool IsSmaller (const ProfilerSampleData& lhs, const ProfilerSampleData& rhs, ProfilerColumn criteria)
+{
+ if (criteria == kTotalTimeColumn || criteria == kTotalPercentColumn)
+ {
+ if (lhs.time != rhs.time)
+ return lhs.time < rhs.time;
+ }
+ else if (criteria == kTotalGPUTimeColumn || criteria == kTotalGPUPercentColumn)
+ {
+ if (lhs.gpuTime != rhs.gpuTime)
+ return lhs.gpuTime < rhs.gpuTime;
+ }
+ else if (criteria == kSelfTimeColumn || criteria == kSelfPercentColumn)
+ {
+ if ((lhs.time - lhs.childrenTime) != (rhs.time - rhs.childrenTime))
+ return (lhs.time - lhs.childrenTime) < (rhs.time - rhs.childrenTime);
+ }
+ else if (criteria == kSelfGPUTimeColumn || criteria == kSelfGPUPercentColumn)
+ {
+ if ((lhs.gpuTime - lhs.childrenGPUTime) != (rhs.gpuTime - rhs.childrenGPUTime))
+ return (lhs.gpuTime - lhs.childrenGPUTime) < (rhs.gpuTime - rhs.childrenGPUTime);
+ }
+ else if (criteria == kGCMemory)
+ {
+ if (lhs.allocatedGCMemory != rhs.allocatedGCMemory)
+ return lhs.allocatedGCMemory < rhs.allocatedGCMemory;
+ }
+ else if (criteria == kDrawCallsColumn)
+ {
+ if (lhs.gpuSamplesCount != rhs.gpuSamplesCount)
+ return lhs.gpuSamplesCount < rhs.gpuSamplesCount;
+ }
+ else if (criteria == kFunctionNameColumn)
+ {
+ return LargerFunctionName(lhs, rhs);
+ }
+ else if (criteria == kWarningColumn)
+ {
+ return lhs.warningCount < rhs.warningCount;
+ }
+ else if (criteria == kCallsColumn)
+ {
+ if (lhs.numberOfCalls != rhs.numberOfCalls)
+ return lhs.numberOfCalls < rhs.numberOfCalls;
+ }
+ else if (criteria == kObjectNameColumn)
+ {
+ // Not supported. Screws with the data flow and not worth it...
+ return LargerFunctionName(lhs, rhs);
+ }
+ else
+ {
+ AssertString("Unsupported sortmode");
+ return false;
+ }
+
+ // As a fallback if the results are the same. Sort by name
+ return LargerFunctionName(lhs, rhs);
+}
+
+
+// ProfilerProperty class implementation
+//
+
+MemoryPool ProfilerProperty::s_AdditionalDataPool(false, "AdditionalData Pool", sizeof (AdditionalProfilerSampleData), 16 * 1024, kMemProfiler);
+
+ProfilerProperty::ProfilerProperty()
+ :m_Root(NULL)
+ ,m_FrameData(NULL)
+ ,m_ProfilerViewType(kViewHierarchy)
+ ,m_ProfilerSortColumn(kTotalTimeColumn)
+ ,m_Depth(0)
+ ,m_ActiveHierarchy(NULL)
+ ,m_OnlyShowGPUSamples(false)
+ ,m_AdditionalData(kMemProfiler)
+ ,m_ThreadIdx(0)
+{
+}
+
+ProfilerProperty::~ProfilerProperty()
+{
+ CleanupProperty();
+}
+
+struct SortByColumn
+{
+ ProfilerColumn column;
+
+ SortByColumn (ProfilerColumn c) : column (c) { }
+
+ bool operator () (const ProfilerHierarchy& lhs, const ProfilerHierarchy& rhs) const
+ {
+ return !IsSmaller(lhs.data, rhs.data, column);
+ }
+};
+
+struct SortByFunctionName
+{
+ const ProfilerFrameData& frameData;
+ int threadIdx;
+
+ SortByFunctionName (const ProfilerFrameData& f, int t) : frameData (f), threadIdx(t) { }
+
+ bool operator () (const UInt32 lhs, const UInt32 rhs) const
+ {
+ const char* lhsName = frameData.m_ThreadData[threadIdx].GetSample(lhs)->information->name;
+ const char* rhsName = frameData.m_ThreadData[threadIdx].GetSample(rhs)->information->name;
+ return strcmp(lhsName, rhsName) < 0;
+ }
+};
+
+static AdditionalProfilerSampleData* AllocateAdditionalData()
+{
+ void* ptr = ProfilerProperty::s_AdditionalDataPool.Allocate();
+ memset(ptr, 0, sizeof(AdditionalProfilerSampleData));
+ return (AdditionalProfilerSampleData*) ptr;
+}
+
+static void InitializeAdditionalProfilerData(const ProfilerFrameData& frameData, int threadIdx, dynamic_array<AdditionalProfilerSampleData*>& additionalData)
+{
+ const ProfilerFrameData::ThreadData& tdata = frameData.m_ThreadData[threadIdx];
+ UInt32 sampleCount = tdata.m_AllSamples.size();
+
+ for(int i = 0; i < additionalData.size(); i++)
+ ProfilerProperty::s_AdditionalDataPool.Deallocate(additionalData[i]);
+
+ additionalData.resize_uninitialized(sampleCount);
+ if (sampleCount > 0)
+ memset(&additionalData[0], 0, sampleCount*sizeof(AdditionalProfilerSampleData*));
+
+ dynamic_array<ProfilerData::AllocatedGCMemory>::const_iterator itGCMem = tdata.m_AllocatedGCMemorySamples.begin();
+ for(; itGCMem != tdata.m_AllocatedGCMemorySamples.end(); ++itGCMem)
+ {
+ if(additionalData[itGCMem->relatedSampleIndex] == 0)
+ additionalData[itGCMem->relatedSampleIndex] = AllocateAdditionalData();
+ additionalData[itGCMem->relatedSampleIndex]->allocatedGCMemory += itGCMem->allocatedGCMemory;
+ }
+
+ dynamic_array<ProfilerData::InstanceID>::const_iterator itID = tdata.m_InstanceIDSamples.begin();
+ for(; itID != tdata.m_InstanceIDSamples.end(); ++itID)
+ {
+ if(additionalData[itID->relatedSampleIndex] == 0)
+ additionalData[itID->relatedSampleIndex] = AllocateAdditionalData();
+ additionalData[itID->relatedSampleIndex]->instanceID = itID->instanceID;
+ }
+ dynamic_array<ProfilerData::GPUTime>::const_iterator itGPUTime = tdata.m_GPUTimeSamples.begin();
+ for(; itGPUTime != tdata.m_GPUTimeSamples.end(); ++itGPUTime)
+ {
+ if(additionalData[itGPUTime->relatedSampleIndex] == 0)
+ additionalData[itGPUTime->relatedSampleIndex] = AllocateAdditionalData();
+ additionalData[itGPUTime->relatedSampleIndex]->gpuTime += itGPUTime->gpuTimeInMicroSec*1000;
+ additionalData[itGPUTime->relatedSampleIndex]->drawCalls++;
+ additionalData[itGPUTime->relatedSampleIndex]->gpuSection = itGPUTime->gpuSection;
+ }
+
+ dynamic_array<UInt32>::const_iterator itWarningSample = tdata.m_WarningSamples.begin();
+ for(; itWarningSample != tdata.m_WarningSamples.end(); ++itWarningSample)
+ {
+ if(additionalData[*itWarningSample] == 0)
+ additionalData[*itWarningSample] = AllocateAdditionalData();
+ additionalData[*itWarningSample]->warningCount += 1;
+ }
+}
+
+
+static UInt32 FillAdditionalProfilerData(const ProfilerFrameData& frameData, int threadIdx, dynamic_array<AdditionalProfilerSampleData*>& additionalData, UInt32 index = 0)
+{
+ const ProfilerFrameData::ThreadData& tdata = frameData.m_ThreadData[threadIdx];
+ if (tdata.m_AllSamples.empty())
+ return 0;
+
+ int childIndex = index+1;
+ for(int i = 0; i < tdata.GetSample(index)->nbChildren; i++)
+ {
+ int nextChild = FillAdditionalProfilerData(frameData, threadIdx, additionalData, childIndex);
+ if(additionalData[childIndex])
+ {
+ if(additionalData[index] == NULL)
+ additionalData[index] = AllocateAdditionalData();
+ additionalData[index]->gpuTime += additionalData[childIndex]->gpuTime;
+ additionalData[index]->drawCalls += additionalData[childIndex]->drawCalls;
+ additionalData[index]->allocatedGCMemory += additionalData[childIndex]->allocatedGCMemory;
+ additionalData[index]->warningCount += additionalData[childIndex]->warningCount;
+ }
+ childIndex = nextChild;
+ }
+ return childIndex;
+}
+
+static void ProcessProfilerOneSample (const ProfilerFrameData::ThreadData& tdata, UInt32 sampleIndex, ProfilerSampleData& data, const dynamic_array<AdditionalProfilerSampleData*>& additionalData)
+{
+ const ProfilerSample* sample = tdata.GetSample(sampleIndex);
+ data.time += sample->timeUS*1000;
+ if(additionalData[sampleIndex])
+ {
+ data.allocatedGCMemory += additionalData[sampleIndex]->allocatedGCMemory;
+ data.gpuTime += additionalData[sampleIndex]->gpuTime;
+ data.gpuSamplesCount += additionalData[sampleIndex]->drawCalls;
+ data.warningCount += additionalData[sampleIndex]->warningCount;
+ }
+ ///@TODO: maybe not depend on data structure of samples here...
+ const ProfilerSample* child = sample + 1;
+ for (int c=0;c<sample->nbChildren;c++)
+ {
+ UInt32 childSampleIndex = child - tdata.GetRoot();
+ data.childrenTime += child->timeUS*1000;
+
+ if(additionalData[childSampleIndex])
+ {
+ data.childrenGPUTime += additionalData[childSampleIndex]->gpuTime;
+ data.childrenGPUSamplesCount += additionalData[childSampleIndex]->drawCalls;
+ }
+ child = SkipSampleRecurse(child);
+ }
+}
+
+static void ProcessProfilerSampleData (const ProfilerFrameData& frameData, int threadIdx, ProfilerHierarchy& hierarchy, const dynamic_array<AdditionalProfilerSampleData*>& additionalData)
+{
+ const ProfilerFrameData::ThreadData& tdata = frameData.m_ThreadData[threadIdx];
+ Assert(hierarchy.oneSample < tdata.m_AllSamples.size());
+
+ ProfilerSampleData& data = hierarchy.data;
+ data.time = 0;
+ data.childrenTime = 0;
+ data.childrenGPUTime = 0;
+ data.childrenGPUSamplesCount = 0;
+ data.allocatedGCMemory = 0;
+ data.gpuTime = 0;
+ data.gpuSamplesCount = 0;
+ data.startTime = 0;
+ data.numberOfCalls = 1 + hierarchy.sampleIdx.size();
+ data.warningCount = 0;
+
+ ProcessProfilerOneSample (tdata, hierarchy.oneSample, data, additionalData);
+ for (int i=0;i<hierarchy.sampleIdx.size();i++)
+ ProcessProfilerOneSample (tdata, hierarchy.sampleIdx[i], data, additionalData);
+
+ UInt32 firstIndex = hierarchy.oneSample;
+ data.rootTime = tdata.GetRoot()->timeUS*1000;
+ if(additionalData[0])
+ data.rootGPUTime = additionalData[0]->gpuTime;
+ data.information = tdata.GetSample(firstIndex)->information;
+ data.startTime = tdata.GetSample(firstIndex)->startTimeUS;
+}
+
+std::string ProfilerProperty::GetProfilerColumn (ProfilerColumn column) const
+{
+ return ::GetProfilerColumn(m_ActiveHierarchy->data, m_ActiveHierarchy->oneSample, m_ActiveHierarchy->sampleIdx, m_AdditionalData, SupportsGPUProfiler(), column);
+}
+
+
+static void BuildHierarchyLevel (const ProfilerFrameData& frameData, int threadIdx, dynamic_array<UInt32>& allSamplesThisLevel, ProfilerHierarchy* parent, ProfilerColumn sortColumn, ProfilerViewType viewType, const dynamic_array<AdditionalProfilerSampleData*>& additionData)
+{
+ /////@TODO: Dont do this.
+ if (!parent->children.empty())
+ return;
+
+ Assert(parent->children.empty()); // should only be called once to build child list
+
+ const ProfilerFrameData::ThreadData& tdata = frameData.m_ThreadData[threadIdx];
+
+ // hierarchy merging
+ if (viewType == kViewHierarchy)
+ {
+ std::sort (allSamplesThisLevel.begin(), allSamplesThisLevel.end(), SortByFunctionName(frameData,threadIdx));
+
+ const char * lastUniqueName = NULL;
+ int count = 0;
+ for (int i=0;i<allSamplesThisLevel.size();i++)
+ {
+ const char* name = tdata.GetSample(allSamplesThisLevel[i])->information->name;
+ if (lastUniqueName == NULL || strcmp(name, lastUniqueName) != 0)
+ count++;
+ }
+ parent->children.reserve(count);
+
+ lastUniqueName = NULL;
+ for (int i=0;i<allSamplesThisLevel.size();i++)
+ {
+ const char* name = tdata.GetSample(allSamplesThisLevel[i])->information->name;
+ if (lastUniqueName == NULL || strcmp(name, lastUniqueName) != 0)
+ {
+ lastUniqueName = name;
+ parent->children.push_back(ProfilerHierarchy());
+ parent->children.back().oneSample = allSamplesThisLevel[i];
+ continue;
+ }
+
+ ProfilerHierarchy& element = parent->children.back();
+ element.sampleIdx.push_back(allSamplesThisLevel[i]);
+ }
+
+ }
+ // raw data. no merging of data is necessary
+ else
+ {
+ const size_t size = allSamplesThisLevel.size();
+ parent->children.resize(size, ProfilerHierarchy());
+ for (size_t i = 0; i < size; ++i)
+ {
+ ProfilerHierarchy& element = parent->children[i];
+ element.oneSample = allSamplesThisLevel[i];
+ }
+ }
+
+ // Calculate profiler display data for all generated children
+ for (int i=0;i<parent->children.size();i++)
+ {
+ ProfilerHierarchy& element = parent->children[i];
+ element.parent = parent;
+ ProcessProfilerSampleData (frameData, threadIdx, element, additionData);
+ }
+
+ // Sort by selected column
+ // IMPORTANT: stable_sort is necessary here. std::sort crashes since it doesn't take advantage of the std::swap of ProfilerHierarchy.
+ if (sortColumn != kDontSortProfilerColumn)
+ std::stable_sort (parent->children.begin(), parent->children.end(), SortByColumn(sortColumn));
+}
+
+static void BuildHierarchyLevel (ProfilerFrameData& frameData, int threadIdx, ProfilerHierarchy* parent, ProfilerColumn sortColumn, ProfilerViewType viewType, const dynamic_array<AdditionalProfilerSampleData*>& additionData)
+{
+ const ProfilerFrameData::ThreadData& tdata = frameData.m_ThreadData[threadIdx];
+
+ int childCount = tdata.GetSample(parent->oneSample)->nbChildren;
+ for(int i = 0; i < parent->sampleIdx.size(); i++)
+ childCount += tdata.GetSample(parent->sampleIdx[i])->nbChildren;
+
+ dynamic_array<UInt32> allSamplesThisLevel(kMemTempAlloc);
+ allSamplesThisLevel.reserve(childCount);
+ tdata.ExtractAllChildSamples(parent->oneSample, allSamplesThisLevel);
+ for(int i = 0; i < parent->sampleIdx.size(); i++)
+ tdata.ExtractAllChildSamples(parent->sampleIdx[i], allSamplesThisLevel);
+
+ BuildHierarchyLevel(frameData, threadIdx, allSamplesThisLevel, parent, sortColumn, viewType, additionData);
+}
+
+
+void ProfilerProperty::GetInstanceIDs(dynamic_array<SInt32>& instanceIDs)
+{
+ GetSortedInstanceIDs (m_AdditionalData, m_ActiveHierarchy->oneSample, m_ActiveHierarchy->sampleIdx, instanceIDs);
+}
+
+std::string ProfilerProperty::GetTooltip (ProfilerColumn column)
+{
+ if (column == kTotalGPUTimeColumn)
+ {
+ if (!SupportsGPUProfiler ())
+ return "The platform you are profiling does not support GPU profiling\nMac OS X 10.7 and higher supports profiling, many mobile platforms have no builtin GPU profiling capabilities.";
+ }
+
+ return "";
+}
+
+
+
+void ProfilerProperty::SetRoot(int frame, ProfilerColumn sortColumn, ProfilerViewType viewType)
+{
+ m_ProfilerViewType = viewType;
+ m_ProfilerSortColumn = sortColumn;
+
+ // @TODO: Dont cleanup here, but call it when done with the data
+ if(m_Root)
+ CleanupProperty();
+ Assert(m_Root == NULL);
+ Assert(m_FrameData == NULL);
+
+ ProfilerFrameData* frameData = ProfilerHistory::Get().GetFrameData(frame);
+ if (frameData == NULL)
+ return;
+
+ m_FrameData = frameData;
+ m_ThreadIdx = 0;
+
+ InitializeAdditionalProfilerData(*m_FrameData, m_ThreadIdx, m_AdditionalData);
+ FillAdditionalProfilerData(*m_FrameData, m_ThreadIdx, m_AdditionalData);
+
+ m_Root = new ProfilerHierarchy();
+ m_Root->oneSample = 0;
+ m_Root->parent = NULL;
+
+ BuildHierarchyLevel(*m_FrameData, m_ThreadIdx, m_Root, m_ProfilerSortColumn, m_ProfilerViewType, m_AdditionalData);
+ ProcessProfilerSampleData(*m_FrameData, m_ThreadIdx, *m_Root, m_AdditionalData);
+
+ m_ActiveHierarchy = m_Root;
+}
+
+void ProfilerProperty::InitializeDetailProperty (const ProfilerProperty& sourceProperty)
+{
+ m_FrameData = sourceProperty.m_FrameData;
+ m_ThreadIdx = sourceProperty.m_ThreadIdx;
+ m_ProfilerViewType = kViewDetailFlat;
+ m_ProfilerSortColumn = sourceProperty.m_ProfilerSortColumn;
+
+ InitializeAdditionalProfilerData(*m_FrameData, m_ThreadIdx, m_AdditionalData);
+ FillAdditionalProfilerData(*m_FrameData, m_ThreadIdx, m_AdditionalData);
+
+ m_Root = new ProfilerHierarchy();
+ m_Root->oneSample = sourceProperty.m_ActiveHierarchy->parent->oneSample;
+ m_Root->sampleIdx = sourceProperty.m_ActiveHierarchy->parent->sampleIdx;
+ m_Root->parent = NULL;
+
+ dynamic_array<UInt32> allSamples = sourceProperty.m_ActiveHierarchy->sampleIdx;
+ allSamples.push_back (sourceProperty.m_ActiveHierarchy->oneSample);
+
+ BuildHierarchyLevel(*m_FrameData, m_ThreadIdx, allSamples, m_Root, m_ProfilerSortColumn, m_ProfilerViewType, m_AdditionalData);
+ ProcessProfilerSampleData(*m_FrameData, m_ThreadIdx, *m_Root, m_AdditionalData);
+
+ m_ActiveHierarchy = m_Root;
+}
+
+std::string ProfilerProperty::GetFrameTime() const
+{
+ if (!m_FrameData)
+ return "--";
+
+ return GetFormattedTime(m_FrameData->m_ThreadData[m_ThreadIdx].GetRoot()->timeUS*1000);
+}
+
+std::string ProfilerProperty::GetFrameGpuTime() const
+{
+ if (m_FrameData){
+ return GetFormattedTime(m_FrameData->m_TotalGPUTimeInMicroSec*1000);
+ }
+ return "--";
+}
+
+std::string ProfilerProperty::GetFrameFPS() const
+{
+ if (m_FrameData)
+ {
+ double frame = 1000000.0 / (double)m_FrameData->m_ThreadData[m_ThreadIdx].GetRoot()->timeUS;
+ return Format("%.1f", (float)frame);
+ }
+ else
+ return "--";
+}
+
+bool ProfilerProperty::HasChildren() const
+{
+ // Detail view does not haven any children.
+ if (m_ProfilerViewType == kViewDetailFlat && m_Root != m_ActiveHierarchy)
+ return false;
+
+ if (m_OnlyShowGPUSamples)
+ return m_ActiveHierarchy->data.childrenGPUSamplesCount != 0;
+
+ if (m_FrameData->m_ThreadData[m_ThreadIdx].GetSample(m_ActiveHierarchy->oneSample)->nbChildren != 0)
+ return true;
+
+ dynamic_array<UInt32>& samples = m_ActiveHierarchy->sampleIdx;
+ for (int i=0;i<samples.size();i++)
+ {
+ if (m_FrameData->m_ThreadData[m_ThreadIdx].GetSample(samples[i])->nbChildren != 0)
+ return true;
+ }
+ return false;
+}
+
+bool ProfilerProperty::GetNext(bool expanded)
+{
+ if (!GetNextInternal (expanded))
+ return false;
+
+ while (m_OnlyShowGPUSamples && m_ActiveHierarchy->data.gpuSamplesCount == 0)
+ {
+ if (!GetNextInternal (expanded))
+ return false;
+ }
+
+ return true;
+}
+
+bool ProfilerProperty::GetNextInternal(bool expanded)
+{
+ if (m_ActiveHierarchy == NULL)
+ return false;
+
+ if (HasChildren() && expanded)
+ {
+ BuildHierarchyLevel(*m_FrameData, m_ThreadIdx, m_ActiveHierarchy, m_ProfilerSortColumn, m_ProfilerViewType, m_AdditionalData);
+
+ // Step into children list
+ m_ActiveHierarchy = &m_ActiveHierarchy->children[0];
+
+ // Increase depth
+ ++m_Depth;
+ }
+ else
+ {
+ if (m_ActiveHierarchy->parent == 0)
+ return false;
+
+ int nextIndex = (m_ActiveHierarchy - &m_ActiveHierarchy->parent->children[0]) + 1;
+
+ // Locating next profile sample object until one is found or end of samples is reached
+ // step out of children list to its parent if end of list is reached
+ while(nextIndex == m_ActiveHierarchy->parent->children.size())
+ {
+ // Go to parent
+ m_ActiveHierarchy = m_ActiveHierarchy->parent;
+
+ if (!m_ActiveHierarchy->parent)
+ {
+ // End of samples
+ return false;
+ }
+ nextIndex = (m_ActiveHierarchy - &m_ActiveHierarchy->parent->children[0]) + 1;
+
+ // Decrease depth
+ --m_Depth;
+ }
+
+ m_ActiveHierarchy = &m_ActiveHierarchy->parent->children[nextIndex];
+ }
+
+ // Read profile sample name
+ m_FunctionPath = m_FunctionName = m_ActiveHierarchy->data.information->name;
+
+ // Assemble path name (used by profile property to handle fold in / fold out)
+ ProfilerHierarchy* sample = m_ActiveHierarchy;
+ sample = sample ? sample->parent : NULL;
+ int depth = m_Depth - 1;
+
+ // Construct path string of all parent names and name of this sample
+ while (sample != NULL && sample->data.information)
+ {
+ // Using Format("%s/%s") takes 80ms to display profiler timeone on Windows, Core i7 2600K.
+ // Replacing that with manual string operations gets time down to 20ms.
+
+ //m_FunctionPath = Format("%s/%s", sample->data.information->name, m_FunctionPath.c_str());
+ const size_t nameLen = strlen(sample->data.information->name);
+ m_FunctionPath.reserve (m_FunctionPath.size() + nameLen + 1); // name length plus '/' char
+ m_FunctionPath.insert (0, sample->data.information->name, nameLen+1); // insert whole name and trailing zero
+ m_FunctionPath[nameLen] = '/'; // replace just inserted trailing zero with '/'
+
+ sample = sample->parent;
+ depth--;
+ }
+
+ // Found profile sample
+ return true;
+}
+
+void ProfilerProperty::CleanupProperty()
+{
+ m_Depth = 0;
+ delete m_Root; m_Root = NULL;
+ m_FrameData = NULL;
+ m_ActiveHierarchy = NULL;
+ for(int i = 0; i < m_AdditionalData.size(); i++)
+ ProfilerProperty::s_AdditionalDataPool.Deallocate(m_AdditionalData[i]);
+ m_AdditionalData.clear();
+}
+
+
+#endif // #if ENABLE_PROFILER && UNITY_EDITOR
diff --git a/Runtime/Profiler/ProfilerProperty.h b/Runtime/Profiler/ProfilerProperty.h
new file mode 100644
index 0000000..2b706fe
--- /dev/null
+++ b/Runtime/Profiler/ProfilerProperty.h
@@ -0,0 +1,162 @@
+#ifndef _PROFILERPROPERTY_H_
+#define _PROFILERPROPERTY_H_
+
+#include "Configuration/UnityConfigure.h"
+
+enum ProfilerViewType
+{
+ kViewHierarchy = 0, // Functions merged and sorted
+ kViewTimeline, // same as kViewRawHierarchy, only different in the UI
+ kViewRawHierarchy, // Unmerged and unsorted
+ kViewDetailFlat, // Unmerged and unsorted and no children
+};
+
+enum ProfilerColumn
+{
+ kDontSortProfilerColumn = -1,
+ kFunctionNameColumn = 0,
+
+ kTotalPercentColumn,
+ kSelfPercentColumn,
+ kCallsColumn,
+ kGCMemory,
+ kTotalTimeColumn,
+ kSelfTimeColumn,
+
+ kDrawCallsColumn,
+ kTotalGPUTimeColumn,
+ kSelfGPUTimeColumn,
+ kTotalGPUPercentColumn,
+ kSelfGPUPercentColumn,
+
+ kWarningColumn,
+
+ kObjectNameColumn,
+
+ kProfilerColumnCount
+};
+
+#if ENABLE_PROFILER && UNITY_EDITOR
+
+#include "Profiler.h"
+#include "ProfilerHistory.h"
+#include "Runtime/Utilities/MemoryPool.h"
+#include <iosfwd>
+
+class Object;
+struct ProfilerHierarchy;
+
+struct AdditionalProfilerSampleData
+{
+ ProfileTimeFormat gpuTime;
+ UInt32 drawCalls;
+ GpuSection gpuSection;
+ SInt32 instanceID;
+ UInt32 allocatedGCMemory;
+ UInt32 warningCount;
+};
+
+// ProfilerProperty class for navigation through profiling information map in Profiler
+class ProfilerProperty
+{
+public:
+ ProfilerProperty();
+ ~ProfilerProperty();
+
+ std::string GetProfilerColumn (ProfilerColumn column) const;
+
+ std::string GetFrameTime() const;
+ std::string GetFrameGpuTime() const;
+ std::string GetFrameFPS() const;
+
+ void InitializeDetailProperty (const ProfilerProperty& sourceProperty);
+
+ const std::string& GetFunctionName() const { return m_FunctionName; }
+ const std::string& GetFunctionPath() const { return m_FunctionPath; }
+
+ // Returns call stack depth (profiled function nested level)
+ int GetDepth() const { return m_Depth; }
+
+ // Checks if the property has children (other profiled functions called within)
+ bool HasChildren() const;
+
+ // Sets profile sample list root
+ void SetRoot(int frame, ProfilerColumn sortColumn, ProfilerViewType viewType);
+
+ // Retrieves next profile sample
+ bool GetNext(bool expanded);
+
+ void GetInstanceIDs(dynamic_array<SInt32>& instanceIDs);
+
+ // Returns current frame data
+ ProfilerFrameData* GetFrameData() { return m_FrameData; }
+
+ // Clean up property parameters (such as depth, ect)
+ void CleanupProperty();
+
+ std::string GetTooltip (ProfilerColumn column);
+
+ bool GetOnlyShowGPUSamples () { return m_OnlyShowGPUSamples; }
+ void SetOnlyShowGPUSamples (bool value) { m_OnlyShowGPUSamples = value; }
+
+ static MemoryPool s_AdditionalDataPool;
+
+private:
+
+ bool SupportsGPUProfiler () const { return !m_FrameData->m_ThreadData[m_ThreadIdx].m_GPUTimeSamples.empty(); }
+ bool GetNextInternal(bool expanded);
+
+ std::string m_FunctionName; // name (e.g. Camera.Render)
+ std::string m_FunctionPath; // hierarchical name, e.g. PlayerLoop/RenderCameras/Camera.Render
+
+ // Call stack depth (profiled function nested level)
+ int m_Depth;
+
+ bool m_OnlyShowGPUSamples;
+
+ // Requested profile data view type
+ ProfilerViewType m_ProfilerViewType;
+ ProfilerColumn m_ProfilerSortColumn;
+
+ ProfilerHierarchy* m_Root;
+ ProfilerFrameData* m_FrameData;
+ ProfilerHierarchy* m_ActiveHierarchy;
+ dynamic_array<AdditionalProfilerSampleData*>m_AdditionalData;
+ int m_ThreadIdx;
+};
+
+struct ProfilerSampleData
+{
+ // constant across all samples
+ ProfileTimeFormat rootTime;
+ ProfileTimeFormat rootGPUTime;
+
+ ProfileTimeFormat time;
+
+ ProfileTimeFormat gpuTime;
+
+ ProfileTimeFormat startTime;
+
+ UInt32 gpuSamplesCount;
+ // Stores execution time per frame of all function called within this one
+ ProfileTimeFormat childrenTime;
+ ProfileTimeFormat childrenGPUTime;
+
+ UInt32 childrenGPUSamplesCount;
+
+ // Stores amount of allocated GC memory
+ UInt32 allocatedGCMemory;
+
+ // Stores number of calls made to the function in one frame
+ UInt32 numberOfCalls;
+
+ UInt32 warningCount;
+
+ // Stores profile information
+ ProfilerInformation* information;
+};
+
+
+#endif // #if ENABLE_PROFILER && UNITY_EDITOR
+
+#endif /*_PROFILERPROPERTY_H_*/
diff --git a/Runtime/Profiler/ProfilerStats.cpp b/Runtime/Profiler/ProfilerStats.cpp
new file mode 100644
index 0000000..c4ddb6c
--- /dev/null
+++ b/Runtime/Profiler/ProfilerStats.cpp
@@ -0,0 +1,432 @@
+#include "UnityPrefix.h"
+#include "ProfilerStats.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "MemoryProfiler.h"
+#include "SerializationUtility.h"
+
+#if ENABLE_PROFILER
+
+using namespace std;
+struct ProfilerSample;
+
+
+void InitializeStatisticsProperties (dynamic_array<StatisticsProperty>& statisticProperties)
+{
+ AllProfilerStats* proxy = NULL;
+
+
+ #define ADD_STAT(_area,_graph,_name,_val,_format) { \
+ StatisticsProperty& prop = statisticProperties.push_back(); \
+ new (&prop) StatisticsProperty(); \
+ prop.name = _name; \
+ prop.offset = reinterpret_cast<UInt8*>(&proxy->_val) - reinterpret_cast<UInt8*>(proxy); \
+ prop.format = _format; \
+ prop.showGraph = _graph; \
+ prop.area = _area; \
+ }
+
+ // Any int stats value can be added by specifying the name, variable and display format into the macro
+#define ADD_STAT_CHART(_area,_name,_val) \
+ ADD_STAT(_area, true, _name, chartSample._val, kFormatTime); \
+ ADD_STAT(_area, false, "Selected" _name, chartSampleSelected._val, kFormatTime)
+
+ // CPU overview
+ ADD_STAT_CHART(kProfilerAreaCPU, "Rendering", rendering);
+ ADD_STAT_CHART(kProfilerAreaCPU, "Scripts", scripts);
+ ADD_STAT_CHART(kProfilerAreaCPU, "Physics", physics);
+ ADD_STAT_CHART(kProfilerAreaCPU, "GarbageCollector", gc);
+ ADD_STAT_CHART(kProfilerAreaCPU, "VSync", vsync);
+ ADD_STAT_CHART(kProfilerAreaCPU, "Others", others);
+
+ // GPU overview
+
+ ADD_STAT_CHART(kProfilerAreaGPU, "Opaque", gpuOpaque);
+ ADD_STAT_CHART(kProfilerAreaGPU, "Transparent", gpuTransparent);
+ ADD_STAT_CHART(kProfilerAreaGPU, "Shadows/Depth", gpuShadows);
+ ADD_STAT_CHART(kProfilerAreaGPU, "Deferred PrePass", gpuDeferredPrePass);
+ ADD_STAT_CHART(kProfilerAreaGPU, "Deferred Lighting", gpuDeferredLighting);
+ ADD_STAT_CHART(kProfilerAreaGPU, "PostProcess", gpuPostProcess);
+ ADD_STAT_CHART(kProfilerAreaGPU, "Other", gpuOther);
+
+
+ // Graphics
+ ADD_STAT(kProfilerAreaRendering, true, "Draw Calls", drawStats.drawCalls, kFormatCount);
+ ADD_STAT(kProfilerAreaRendering, true, "Triangles", drawStats.triangles, kFormatCount);
+ ADD_STAT(kProfilerAreaRendering, true, "Vertices", drawStats.vertices, kFormatCount);
+
+ // Memory
+ ADD_STAT(kProfilerAreaMemory, true, "Total Allocated", memoryStats.bytesUsedTotal, kFormatBytes);
+ ADD_STAT(kProfilerAreaMemory, true, "Texture Memory", drawStats.usedTextureBytes, kFormatBytes);
+ ADD_STAT(kProfilerAreaMemory, false, "Texture Count", memoryStats.textureCount, kFormatCount);
+ ADD_STAT(kProfilerAreaMemory, true, "Mesh Count", memoryStats.meshCount, kFormatCount);
+ ADD_STAT(kProfilerAreaMemory, true, "Material Count", memoryStats.materialCount, kFormatCount);
+ ADD_STAT(kProfilerAreaMemory, true, "Object Count", memoryStats.totalObjectsCount, kFormatCount);
+
+ // Audio
+ ADD_STAT(kProfilerAreaAudio, true, "Playing Sources", audioStats.playingSources, kFormatCount);
+ ADD_STAT(kProfilerAreaAudio, true, "Paused Sources", audioStats.pausedSources, kFormatCount);
+ ADD_STAT(kProfilerAreaAudio, true, "Audio Voices", audioStats.audioVoices, kFormatCount);
+ ADD_STAT(kProfilerAreaAudio, false, "Audio CPU Usage", audioStats.audioCPUusage, kFormatPercentage);
+ ADD_STAT(kProfilerAreaAudio, true, "Audio Memory", audioStats.audioMemUsage, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Max Audio Memory Usage", audioStats.audioMaxMemUsage, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, true, "Audio Clip Count", audioStats.audioClipCount, kFormatCount);
+ ADD_STAT(kProfilerAreaAudio, true, "Audio Source Count", audioStats.audioSourceCount, kFormatCount);
+ ADD_STAT(kProfilerAreaAudio, false, "Detailed Audio Memory Usage", audioStats.audioMemDetailsUsage, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Memory not accounted for by other types", audioStats.audioMemDetails.other, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "String data", audioStats.audioMemDetails.string, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "System object and various internals", audioStats.audioMemDetails.system, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Plugin objects and internals", audioStats.audioMemDetails.plugins, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Output module object and internals", audioStats.audioMemDetails.output, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Channel related memory", audioStats.audioMemDetails.channel, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "ChannelGroup objects and internals", audioStats.audioMemDetails.channelgroup, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Codecs allocated for streaming", audioStats.audioMemDetails.codec, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "File buffers and structures", audioStats.audioMemDetails.file, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Sound objects and internals", audioStats.audioMemDetails.sound, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Sound data stored in secondary RAM", audioStats.audioMemDetails.secondaryram, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "SoundGroup objects and internals", audioStats.audioMemDetails.soundgroup, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Stream buffer memory", audioStats.audioMemDetails.streambuffer, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "DSPConnection objects and internals", audioStats.audioMemDetails.dspconnection, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "DSP implementation objects", audioStats.audioMemDetails.dsp, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Realtime file format decoding DSP objects", audioStats.audioMemDetails.dspcodec, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Profiler memory footprint", audioStats.audioMemDetails.profile, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Buffer used to store recorded data from microphone", audioStats.audioMemDetails.recordbuffer, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Reverb implementation objects", audioStats.audioMemDetails.reverb, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Reverb channel properties structs", audioStats.audioMemDetails.reverbchannelprops, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Geometry objects and internals", audioStats.audioMemDetails.geometry, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Sync point memory", audioStats.audioMemDetails.syncpoint, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "EventSystem and various internals", audioStats.audioMemDetails.eventsystem, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "MusicSystem and various internals", audioStats.audioMemDetails.musicsystem, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Definition of objects contained in all loaded projects e.g. events, groups, categories", audioStats.audioMemDetails.fev, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Data loaded with preloadFSB", audioStats.audioMemDetails.memoryfsb, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "EventProject objects and internals", audioStats.audioMemDetails.eventproject, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "EventGroup objects and internals", audioStats.audioMemDetails.eventgroupi, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Objects used to manage wave banks", audioStats.audioMemDetails.soundbankclass, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Data used to manage lists of wave bank usage", audioStats.audioMemDetails.soundbanklist, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Stream objects and internals", audioStats.audioMemDetails.streaminstance, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Sound definition objects", audioStats.audioMemDetails.sounddefclass, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Sound definition static data objects", audioStats.audioMemDetails.sounddefdefclass, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Sound definition pool data", audioStats.audioMemDetails.sounddefpool, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Reverb definition objects", audioStats.audioMemDetails.reverbdef, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Reverb objects", audioStats.audioMemDetails.eventreverb, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "User property objects", audioStats.audioMemDetails.userproperty, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Event instance base objects", audioStats.audioMemDetails.eventinstance, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Complex event instance objects", audioStats.audioMemDetails.eventinstance_complex, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Simple event instance objects", audioStats.audioMemDetails.eventinstance_simple, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Event layer instance objects", audioStats.audioMemDetails.eventinstance_layer, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Event sound instance objects", audioStats.audioMemDetails.eventinstance_sound, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Event envelope objects", audioStats.audioMemDetails.eventenvelope, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Event envelope definition objects", audioStats.audioMemDetails.eventenvelopedef, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Event parameter objects", audioStats.audioMemDetails.eventparameter, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Event category objects", audioStats.audioMemDetails.eventcategory, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Event envelope point objects", audioStats.audioMemDetails.eventenvelopepoint, kFormatBytes);
+ ADD_STAT(kProfilerAreaAudio, false, "Event instance pool memory", audioStats.audioMemDetails.eventinstancepool, kFormatBytes);
+
+
+ // Physics
+ ////@TODO: Add some kind of warning when moving static colliders because that has a very high performance impact!
+ ADD_STAT(kProfilerAreaPhysics, true, "Active Rigidbodies", physicsStats.activeRigidbodies, kFormatCount);
+ ADD_STAT(kProfilerAreaPhysics, false, "Sleeping Rigidbodies", physicsStats.sleepingRigidbodies, kFormatCount);
+
+ ADD_STAT(kProfilerAreaPhysics, true, "Number of Contacts", physicsStats.numberOfShapePairs, kFormatCount);
+
+ ADD_STAT(kProfilerAreaPhysics, false, "Static Colliders", physicsStats.numberOfStaticColliders, kFormatCount);
+ ADD_STAT(kProfilerAreaPhysics, false, "Dynamic Colliders", physicsStats.numberOfDynamicColliders, kFormatCount);
+
+ // Physics (2D).
+ ADD_STAT(kProfilerAreaPhysics2D, false, "Total Bodies", physics2DStats.m_TotalBodyCount, kFormatCount);
+ ADD_STAT(kProfilerAreaPhysics2D, true, "Active Bodies", physics2DStats.m_ActiveBodyCount, kFormatCount);
+ ADD_STAT(kProfilerAreaPhysics2D, true, "Sleeping Bodies", physics2DStats.m_SleepingBodyCount, kFormatCount);
+ ADD_STAT(kProfilerAreaPhysics2D, true, "Dynamic Bodies", physics2DStats.m_DynamicBodyCount, kFormatCount);
+ ADD_STAT(kProfilerAreaPhysics2D, true, "Kinematic Bodies", physics2DStats.m_KinematicBodyCount, kFormatCount);
+ ADD_STAT(kProfilerAreaPhysics2D, true, "Discrete Bodies", physics2DStats.m_DiscreteBodyCount, kFormatCount);
+ ADD_STAT(kProfilerAreaPhysics2D, true, "Continuous Bodies", physics2DStats.m_ContinuousBodyCount, kFormatCount);
+ ADD_STAT(kProfilerAreaPhysics2D, true, "Joints", physics2DStats.m_JointCount, kFormatCount);
+ ADD_STAT(kProfilerAreaPhysics2D, true, "Contacts", physics2DStats.m_ContactCount, kFormatCount);
+ ADD_STAT(kProfilerAreaPhysics2D, false, "Active Collider Shapes", physics2DStats.m_ActiveColliderShapesCount, kFormatCount);
+ ADD_STAT(kProfilerAreaPhysics2D, false, "Sleeping Collider Shapes", physics2DStats.m_SleepingColliderShapesCount, kFormatCount);
+ ADD_STAT(kProfilerAreaPhysics2D, false, "Step Time", physics2DStats.m_StepTime, kFormatTime);
+ ADD_STAT(kProfilerAreaPhysics2D, false, "Contact Time", physics2DStats.m_CollideTime, kFormatTime);
+ ADD_STAT(kProfilerAreaPhysics2D, false, "Solve Time", physics2DStats.m_SolveTime, kFormatTime);
+ ADD_STAT(kProfilerAreaPhysics2D, false, "Solve Initialization Time", physics2DStats.m_SolveInitialization, kFormatTime);
+ ADD_STAT(kProfilerAreaPhysics2D, false, "Solve Velocity Time", physics2DStats.m_SolveVelocity, kFormatTime);
+ ADD_STAT(kProfilerAreaPhysics2D, false, "Solve Position Time", physics2DStats.m_SolvePosition, kFormatTime);
+ ADD_STAT(kProfilerAreaPhysics2D, false, "Solve Broadphase Time", physics2DStats.m_SolveBroadphase, kFormatTime);
+ ADD_STAT(kProfilerAreaPhysics2D, false, "Solve TOI Time", physics2DStats.m_SolveTimeOfImpact, kFormatTime);
+}
+
+static inline ProfilerString FormatNumber (int num)
+{
+ if (num < 1000)
+ return FormatString<ProfilerString>("%d", num);
+ else if (num < 1000000)
+ return FormatString<ProfilerString>("%1.1fk", (num*0.001F));
+ else
+ return FormatString<ProfilerString>("%1.1fM", num*0.000001F);
+}
+
+ProfilerString DrawStats::ToString () const
+{
+ int vramUsageMin = screenBytes + renderTextureBytes;
+ int vramUsageMax = screenBytes + usedTextureBytes + renderTextureBytes + vboTotalBytes;
+
+ return
+ FormatString<ProfilerString>("Draw Calls: %d \tTris: %s \t Verts: %s", drawCalls, FormatNumber(triangles).c_str(), FormatNumber(vertices).c_str()) +
+ FormatString<ProfilerString>("\nBatched Draw Calls: %d \tBatched Tris: %s \t Batched Verts: %s", batchedDrawCalls, FormatNumber(batchedTriangles).c_str(), FormatNumber(batchedVertices).c_str()) +
+ FormatString<ProfilerString>("\nUsed Textures: %d / %s", usedTextureCount, FormatBytes(usedTextureBytes).c_str()) +
+ FormatString<ProfilerString>("\nRenderTextures: %d / %s", renderTextureCount, FormatBytes(renderTextureBytes).c_str()) +
+ FormatString<ProfilerString>("\nRenderTexture Switches: %d", renderTextureStateChanges) +
+ FormatString<ProfilerString>("\nScreen: %s / %s", GetScreenResString().c_str(), FormatBytes(screenBytes).c_str()) +
+ FormatString<ProfilerString>("\nVRAM usage: %s to %s (of %s)", FormatBytes(vramUsageMin).c_str(), FormatBytes(vramUsageMax).c_str(), FormatBytes(totalAvailableVRamMBytes * 1024 * 1024).c_str()) +
+ FormatString<ProfilerString>("\nVBO Total: %d - %s", vboTotal, FormatBytes(vboTotalBytes).c_str()) +
+ FormatString<ProfilerString>("\nVB Uploads: %d - %s", vboUploads, FormatBytes(vboUploadBytes).c_str()) +
+ FormatString<ProfilerString>("\nIB Uploads: %d - %s", ibUploads, FormatBytes(ibUploadBytes).c_str()) +
+ FormatString<ProfilerString>("\nShadow Casters: %d\t ", shadowCasters);
+}
+
+
+ProfilerString GetFormattedSmallTime(ProfileTimeFormat time)
+{
+ if (time < 100000)
+ return FormatString<ProfilerString>("%d nano", (int)time);
+
+ time /= 100;
+
+ ProfilerString value = FormatString<ProfilerString>("%u", (unsigned)time);
+
+ int length = value.length();
+
+ if (length > 4)
+ {
+ value.insert(length - 4, 1, '.');
+ }
+ else
+ {
+ ProfilerString tmp;
+ tmp.assign(4, '0');
+
+ value.copy((char*) tmp.c_str() + (4 - length), length);
+
+ return "0." + tmp;
+ }
+
+ return value;
+}
+
+ProfilerString DebugStats::ToString () const
+{
+ return
+ FormatString<ProfilerString>("Profiler Memory: %s\n", FormatBytes(m_ProfilerMemoryUsage).c_str()) +
+ FormatString<ProfilerString>("Profiler Memory Misc: %s\n", FormatBytes(m_ProfilerMemoryUsageOthers).c_str())
+#if DEBUGMODE
+ + FormatString<ProfilerString>("Profiler Sample Count: %d\n", m_AllocatedProfileSamples)
+#endif
+ ;
+}
+
+ProfilerString MemoryStats::ToString () const
+{
+ ProfilerString str =
+ FormatString<ProfilerString>("Used Total: %s ", FormatBytes(bytesUsedTotal).c_str()) +
+ FormatString<ProfilerString>("Unity: %s ", FormatBytes(bytesUsedUnity).c_str()) +
+ FormatString<ProfilerString>("Mono: %s ", FormatBytes(bytesUsedMono).c_str()) +
+ FormatString<ProfilerString>("GfxDriver: %s ", FormatBytes(bytesUsedGFX).c_str()) +
+ FormatString<ProfilerString>("FMOD: %s ", FormatBytes(bytesUsedFMOD).c_str()) +
+ FormatString<ProfilerString>("Profiler: %s ", FormatBytes(bytesUsedProfiler).c_str()) +
+ FormatString<ProfilerString>("\nReserved Total: %s ", FormatBytes(bytesReservedTotal).c_str()) +
+ FormatString<ProfilerString>("Unity: %s ", FormatBytes(bytesReservedUnity).c_str()) +
+ FormatString<ProfilerString>("Mono: %s ", FormatBytes(bytesReservedMono).c_str()) +
+ FormatString<ProfilerString>("GfxDriver: %s ", FormatBytes(bytesReservedGFX).c_str()) +
+ FormatString<ProfilerString>("FMOD: %s ", FormatBytes(bytesReservedFMOD).c_str()) +
+ FormatString<ProfilerString>("Profiler: %s ", FormatBytes(bytesReservedProfiler).c_str()) +
+
+ FormatString<ProfilerString>("\nTotal System Memory Usage: %s ", FormatBytes(bytesVirtual).c_str()) +
+ FormatString<ProfilerString>("\n(WP8) Commited Limit: %s ", FormatBytes(bytesCommitedLimit).c_str()) +
+ FormatString<ProfilerString>("Commited Total: %s ", FormatBytes(bytesCommitedTotal).c_str()) +
+
+ FormatString<ProfilerString>("\n\nTextures: %d / %s", textureCount, FormatBytes(textureBytes).c_str()) +
+ FormatString<ProfilerString>("\nMeshes: %d / %s", meshCount, FormatBytes(meshBytes).c_str()) +
+ FormatString<ProfilerString>("\nMaterials: %d / %s", materialCount, FormatBytes(materialBytes).c_str()) +
+ FormatString<ProfilerString>("\nAnimationClips: %d / %s", animationClipCount, FormatBytes(animationClipBytes).c_str()) +
+ FormatString<ProfilerString>("\nAudioClips: %d / %s", audioCount, FormatBytes(audioBytes).c_str()) +
+ FormatString<ProfilerString>("\nAssets: %d ", assetCount) +
+ FormatString<ProfilerString>("\nGameObjects in Scene: %d ", gameObjectCount) +
+ FormatString<ProfilerString>("\nTotal Objects in Scene: %d ", sceneObjectCount) +
+ FormatString<ProfilerString>("\nTotal Object Count: %d", totalObjectsCount);// +
+/* Format("\nMost Occurring Objects:");
+ std::vector<int> sorted;
+ sorted.resize(classCount.size());
+ for(int i = 0; i < classCount.size(); i++)
+ sorted[i] = (classCount[i]<<12)+i;
+ std::sort(sorted.begin(), sorted.end());
+
+ for(int i = sorted.size()-1; i >= 0 ; i--)
+ {
+ int count = sorted[i] >> 12;
+ int index = sorted[i] & 0xFFF;
+ if(count != 0)
+ str += Format("\n\t%s: %d",Object::ClassIDToString(index).c_str(), count);
+ }*/
+ /*str += "\n\n";
+ for(int i = 0; i < memoryAllocatorInformation.size(); i++)
+ {
+ str += FormatString<ProfilerString>("\n%s: used %s (reserved %s)",memoryAllocatorInformation[i].name.c_str(), FormatBytes(memoryAllocatorInformation[i].used).c_str(), FormatBytes(memoryAllocatorInformation[i].reserved).c_str());
+ }
+ str += "\n\n";
+ str += memoryOverview;
+ */
+ return str;
+}
+
+ProfilerString DrawStats::GetScreenResString () const
+{
+ if (screenFSAA > 1)
+ return FormatString<ProfilerString>("%ix%i %ixAA", screenWidth, screenHeight, screenFSAA);
+ else
+ return FormatString<ProfilerString>("%ix%i", screenWidth, screenHeight);
+}
+
+#if ENABLE_PLAYERCONNECTION
+
+void AllProfilerStats::Serialize( dynamic_array<int>& bitstream )
+{
+ memoryStats.Serialize(bitstream);
+ WriteIntArray (bitstream, drawStats);
+ WriteIntArray (bitstream, physicsStats);
+ WriteIntArray (bitstream, physics2DStats);
+ debugStats.Serialize(bitstream); // not all ints
+ WriteIntArray (bitstream, audioStats);
+ WriteIntArray (bitstream, chartSample);
+ WriteIntArray (bitstream, chartSampleSelected);
+}
+
+void DebugStats::Serialize( dynamic_array<int>& bitstream )
+{
+ bitstream.push_back (m_ProfilerMemoryUsage);
+ bitstream.push_back (m_ProfilerMemoryUsageOthers);
+ bitstream.push_back (m_AllocatedProfileSamples);
+}
+
+void MemoryStats::Serialize( dynamic_array<int>& bitstream )
+{
+ bitstream.push_back (bytesUsedTotal/1024);
+ bitstream.push_back (bytesUsedUnity/1024);
+ bitstream.push_back (bytesUsedMono/1024);
+ bitstream.push_back (bytesUsedGFX/1024);
+ bitstream.push_back (bytesUsedFMOD/1024);
+ bitstream.push_back (bytesUsedProfiler/1024);
+
+ bitstream.push_back (bytesReservedTotal/1024);
+ bitstream.push_back (bytesReservedUnity/1024);
+ bitstream.push_back (bytesReservedMono/1024);
+ bitstream.push_back (bytesReservedGFX/1024);
+ bitstream.push_back (bytesReservedFMOD/1024);
+ bitstream.push_back (bytesReservedProfiler/1024);
+
+ bitstream.push_back (bytesVirtual/1024);
+ bitstream.push_back (bytesCommitedLimit/1024);
+ bitstream.push_back (bytesCommitedTotal/1024);
+
+ bitstream.push_back (textureCount);
+ bitstream.push_back (textureBytes);
+ bitstream.push_back (meshCount);
+ bitstream.push_back (meshBytes);
+ bitstream.push_back (materialCount);
+ bitstream.push_back (materialBytes);
+ bitstream.push_back (animationClipCount);
+ bitstream.push_back (animationClipBytes);
+ bitstream.push_back (audioCount);
+ bitstream.push_back (audioBytes);
+ bitstream.push_back (assetCount);
+ bitstream.push_back (sceneObjectCount);
+ bitstream.push_back (gameObjectCount);
+ bitstream.push_back (totalObjectsCount);
+
+ // write size, and index,entry for all non 0 entries
+ bitstream.push_back (classCount.size());
+ for(int i = 0; i < classCount.size(); i++)
+ {
+ if(classCount[i] != 0)
+ {
+ bitstream.push_back (i);
+ bitstream.push_back (classCount[i]);
+ }
+ }
+ bitstream.push_back (-1);
+
+ /// TODO
+// WriteString(bitstream, memoryOverview.c_str());
+
+}
+
+
+void AllProfilerStats::Deserialize( int** bitstream, bool swapdata )
+{
+ memoryStats.Deserialize(bitstream, swapdata);
+ ReadIntArray (bitstream, drawStats);
+ ReadIntArray (bitstream, physicsStats);
+ ReadIntArray (bitstream, physics2DStats);
+ debugStats.Deserialize(bitstream); // not all ints
+ ReadIntArray (bitstream, audioStats);
+ ReadIntArray (bitstream, chartSample);
+ ReadIntArray (bitstream, chartSampleSelected);
+}
+
+void DebugStats::Deserialize( int** bitstream )
+{
+ m_ProfilerMemoryUsage = *((*bitstream)++);
+ m_ProfilerMemoryUsageOthers = *((*bitstream)++);
+ m_AllocatedProfileSamples = *((*bitstream)++);
+}
+
+void MemoryStats::Deserialize( int** bitstream, bool swapdata )
+{
+ bytesUsedTotal = *((*bitstream)++)*1024;
+ bytesUsedUnity = *((*bitstream)++)*1024;
+ bytesUsedMono = *((*bitstream)++)*1024;
+ bytesUsedGFX = *((*bitstream)++)*1024;
+ bytesUsedFMOD = *((*bitstream)++)*1024;
+ bytesUsedProfiler = *((*bitstream)++)*1024;
+
+ bytesReservedTotal = *((*bitstream)++)*1024;
+ bytesReservedUnity = *((*bitstream)++)*1024;
+ bytesReservedMono = *((*bitstream)++)*1024;
+ bytesReservedGFX = *((*bitstream)++)*1024;
+ bytesReservedFMOD = *((*bitstream)++)*1024;
+ bytesReservedProfiler = *((*bitstream)++)*1024;
+
+ bytesVirtual = *((*bitstream)++)*1024;
+ bytesCommitedLimit = *((*bitstream)++)*1024;
+ bytesCommitedTotal = *((*bitstream)++)*1024;
+
+ textureCount = *((*bitstream)++);
+ textureBytes = *((*bitstream)++);
+ meshCount = *((*bitstream)++);
+ meshBytes = *((*bitstream)++);
+ materialCount = *((*bitstream)++);
+ materialBytes = *((*bitstream)++);
+ animationClipCount = *((*bitstream)++);
+ animationClipBytes = *((*bitstream)++);
+ audioCount = *((*bitstream)++);
+ audioBytes = *((*bitstream)++);
+ assetCount = *((*bitstream)++);
+ sceneObjectCount = *((*bitstream)++);
+ gameObjectCount = *((*bitstream)++);
+ totalObjectsCount = *((*bitstream)++);
+ int count;
+ count = *((*bitstream)++);
+ classCount.resize_initialized(count,0);
+ int index = *((*bitstream)++);
+ while(index != -1)
+ {
+ classCount[index] = *((*bitstream)++);
+ index = *((*bitstream)++);
+ }
+
+// ReadString(bitstream, memoryOverview, swapdata);
+
+}
+#endif
+
+#endif
+
+
diff --git a/Runtime/Profiler/ProfilerStats.h b/Runtime/Profiler/ProfilerStats.h
new file mode 100644
index 0000000..f49d7d6
--- /dev/null
+++ b/Runtime/Profiler/ProfilerStats.h
@@ -0,0 +1,323 @@
+#ifndef _PROFILERSTATS_H_
+#define _PROFILERSTATS_H_
+
+#include "Configuration/UnityConfigure.h"
+
+enum ValueFormat
+{
+ kFormatTime, // milliseconds
+ kFormatCount, // number (optionally with k/M)
+ kFormatBytes, // number b/kB/m
+ kFormatPercentage // Percentage in % * 10 as an int, so that we can represent 10.1%
+};
+
+enum ProfilerArea
+{
+ kProfilerAreaCPU,
+ kProfilerAreaGPU,
+ kProfilerAreaRendering,
+ kProfilerAreaMemory,
+ kProfilerAreaAudio,
+ kProfilerAreaPhysics,
+ kProfilerAreaPhysics2D,
+ kProfilerAreaDebug,
+ kProfilerAreaCount = kProfilerAreaDebug
+};
+
+#if ENABLE_PROFILER
+
+#include "TimeHelper.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+struct MemoryStats
+{
+ // used bytes: Total, unity(-profiler), mono, DX/OGL, Profiler, FMOD??, Executable??
+ // reserved bytes: Total, unity, mono, DX/OGL, Profiler, FMOD??, Executable??
+ size_t bytesUsedTotal;
+ size_t bytesUsedUnity;
+ size_t bytesUsedMono;
+ size_t bytesUsedGFX;
+ size_t bytesUsedFMOD;
+ size_t bytesUsedProfiler;
+
+ size_t bytesReservedTotal;
+ size_t bytesReservedUnity;
+ size_t bytesReservedMono;
+ size_t bytesReservedGFX;
+ size_t bytesReservedFMOD;
+ size_t bytesReservedProfiler;
+
+ size_t bytesVirtual;
+ size_t bytesCommitedLimit;
+ size_t bytesCommitedTotal;
+
+ int bytesUsedDelta;
+
+ int textureCount;
+ int textureBytes;
+
+ int meshCount;
+ int meshBytes;
+
+ int materialCount;
+ int materialBytes;
+
+ int animationClipCount;
+ int animationClipBytes;
+
+ int audioCount;
+ int audioBytes;
+
+ int assetCount;
+ int sceneObjectCount;
+ int gameObjectCount;
+
+ int totalObjectsCount;
+ int profilerMemUsed;
+ int profilerNumAllocations;
+ // NB! Everything above here will be cleared with a memset() in the constructor! ^^^^^^^^^^^^
+ dynamic_array<int> classCount;
+ ProfilerString memoryOverview;
+
+ MemoryStats () : classCount(kMemProfiler) { memset(this, 0, ptrdiff_t(&classCount) - ptrdiff_t(this)); classCount.clear(); memoryOverview.clear(); }
+#if ENABLE_PLAYERCONNECTION
+ void Serialize( dynamic_array<int>& bitstream );
+ void Deserialize( int** bitstream, bool swapdata );
+#endif
+
+ ProfilerString ToString () const;
+};
+
+struct DrawStats
+{
+ int drawCalls;
+ int triangles;
+ int vertices;
+
+ int batchedDrawCalls;
+ int batchedTriangles;
+ int batchedVertices;
+
+ int shadowCasters;
+
+ int usedTextureCount;
+ int usedTextureBytes;
+
+ int renderTextureCount;
+ int renderTextureBytes;
+ int renderTextureStateChanges;
+
+ int screenWidth;
+ int screenHeight;
+ int screenFSAA;
+ int screenBytes;
+
+ int vboTotal;
+ int vboTotalBytes;
+ int vboUploads;
+ int vboUploadBytes;
+ int ibUploads;
+ int ibUploadBytes;
+
+ int visibleSkinnedMeshes;
+
+ int totalAvailableVRamMBytes;
+
+ // int textureStateChanges;
+ // int lightStateChanges;
+ // int pixelShaderStateChanges;
+ // int vertexShaderStateChanges;
+
+ DrawStats () { memset(this, 0, sizeof(*this)); }
+ ProfilerString GetScreenResString () const;
+
+
+ ProfilerString ToString () const;
+};
+
+struct PhysicsStats
+{
+ int activeRigidbodies;
+ int sleepingRigidbodies;
+
+ int numberOfShapePairs;
+
+ int numberOfStaticColliders;
+ int numberOfDynamicColliders;
+
+ PhysicsStats () { memset(this, 0, sizeof(*this)); }
+};
+
+struct Physics2DStats
+{
+ int m_TotalBodyCount;
+ int m_ActiveBodyCount;
+ int m_SleepingBodyCount;
+ int m_DynamicBodyCount;
+ int m_KinematicBodyCount;
+ int m_DiscreteBodyCount;
+ int m_ContinuousBodyCount;
+ int m_JointCount;
+ int m_ContactCount;
+ int m_ActiveColliderShapesCount;
+ int m_SleepingColliderShapesCount;
+
+ int m_StepTime;
+ int m_CollideTime;
+ int m_SolveTime;
+ int m_SolveInitialization;
+ int m_SolveVelocity;
+ int m_SolvePosition;
+ int m_SolveBroadphase;
+ int m_SolveTimeOfImpact;
+
+ Physics2DStats () { memset(this, 0, sizeof(*this)); }
+};
+
+struct DebugStats
+{
+ DebugStats () { memset(this, 0, sizeof(*this)); }
+
+ int m_ProfilerMemoryUsage;
+ int m_ProfilerMemoryUsageOthers;
+ int m_AllocatedProfileSamples;
+
+ ProfilerString ToString () const;
+#if ENABLE_PLAYERCONNECTION
+ void Serialize( dynamic_array<int>& bitstream );
+ void Deserialize( int** bitstream );
+#endif
+};
+
+struct AudioStats
+{
+ AudioStats () { memset(this, 0, sizeof(*this)); }
+
+ int playingSources;
+ int pausedSources;
+
+ int audioCPUusage;
+ int audioMemUsage;
+ int audioMaxMemUsage;
+ int audioVoices;
+
+ int audioClipCount;
+ int audioSourceCount;
+
+ unsigned int audioMemDetailsUsage;
+
+ struct Details
+ {
+ unsigned int other; /* [out] Memory not accounted for by other types */
+ unsigned int string; /* [out] String data */
+ unsigned int system; /* [out] System object and various internals */
+ unsigned int plugins; /* [out] Plugin objects and internals */
+ unsigned int output; /* [out] Output module object and internals */
+ unsigned int channel; /* [out] Channel related memory */
+ unsigned int channelgroup; /* [out] ChannelGroup objects and internals */
+ unsigned int codec; /* [out] Codecs allocated for streaming */
+ unsigned int file; /* [out] File buffers and structures */
+ unsigned int sound; /* [out] Sound objects and internals */
+ unsigned int secondaryram; /* [out] Sound data stored in secondary RAM */
+ unsigned int soundgroup; /* [out] SoundGroup objects and internals */
+ unsigned int streambuffer; /* [out] Stream buffer memory */
+ unsigned int dspconnection; /* [out] DSPConnection objects and internals */
+ unsigned int dsp; /* [out] DSP implementation objects */
+ unsigned int dspcodec; /* [out] Realtime file format decoding DSP objects */
+ unsigned int profile; /* [out] Profiler memory footprint. */
+ unsigned int recordbuffer; /* [out] Buffer used to store recorded data from microphone */
+ unsigned int reverb; /* [out] Reverb implementation objects */
+ unsigned int reverbchannelprops; /* [out] Reverb channel properties structs */
+ unsigned int geometry; /* [out] Geometry objects and internals */
+ unsigned int syncpoint; /* [out] Sync point memory. */
+ unsigned int eventsystem; /* [out] EventSystem and various internals */
+ unsigned int musicsystem; /* [out] MusicSystem and various internals */
+ unsigned int fev; /* [out] Definition of objects contained in all loaded projects e.g. events, groups, categories */
+ unsigned int memoryfsb; /* [out] Data loaded with preloadFSB */
+ unsigned int eventproject; /* [out] EventProject objects and internals */
+ unsigned int eventgroupi; /* [out] EventGroup objects and internals */
+ unsigned int soundbankclass; /* [out] Objects used to manage wave banks */
+ unsigned int soundbanklist; /* [out] Data used to manage lists of wave bank usage */
+ unsigned int streaminstance; /* [out] Stream objects and internals */
+ unsigned int sounddefclass; /* [out] Sound definition objects */
+ unsigned int sounddefdefclass; /* [out] Sound definition static data objects */
+ unsigned int sounddefpool; /* [out] Sound definition pool data */
+ unsigned int reverbdef; /* [out] Reverb definition objects */
+ unsigned int eventreverb; /* [out] Reverb objects */
+ unsigned int userproperty; /* [out] User property objects */
+ unsigned int eventinstance; /* [out] Event instance base objects */
+ unsigned int eventinstance_complex; /* [out] Complex event instance objects */
+ unsigned int eventinstance_simple; /* [out] Simple event instance objects */
+ unsigned int eventinstance_layer; /* [out] Event layer instance objects */
+ unsigned int eventinstance_sound; /* [out] Event sound instance objects */
+ unsigned int eventenvelope; /* [out] Event envelope objects */
+ unsigned int eventenvelopedef; /* [out] Event envelope definition objects */
+ unsigned int eventparameter; /* [out] Event parameter objects */
+ unsigned int eventcategory; /* [out] Event category objects */
+ unsigned int eventenvelopepoint; /* [out] Event envelope point objects */
+ unsigned int eventinstancepool; /* [out] Event instance pool memory */
+ };
+
+ Details audioMemDetails;
+};
+
+// Stores samples for profiler charts
+struct ChartSample
+{
+ ChartSample () { memset(this, 0, sizeof(*this)); }
+
+ int rendering;
+ int scripts;
+ int physics;
+ int gc;
+ int vsync;
+ int others;
+
+ int gpuOpaque;
+ int gpuTransparent;
+ int gpuShadows;
+ int gpuPostProcess;
+ int gpuDeferredPrePass;
+ int gpuDeferredLighting;
+ int gpuOther;
+
+ int hasGPUProfiler;
+};
+
+struct AllProfilerStats
+{
+ MemoryStats memoryStats;
+ DrawStats drawStats;
+ PhysicsStats physicsStats;
+ Physics2DStats physics2DStats;
+ DebugStats debugStats;
+ AudioStats audioStats;
+ ChartSample chartSample;
+ ChartSample chartSampleSelected;
+#if ENABLE_PLAYERCONNECTION
+ void Serialize( dynamic_array<int>& bitstream );
+ void Deserialize( int** bitstream, bool swapdata );
+#endif
+};
+
+struct StatisticsProperty
+{
+ std::string name;
+ int offset;
+ ValueFormat format;
+ ProfilerArea area;
+ bool showGraph;
+};
+
+
+void InitializeStatisticsProperties (dynamic_array<StatisticsProperty>& statisticProperties);
+
+inline int GetStatisticsValue (int offset, AllProfilerStats& stats)
+{
+ AssertIf( offset < 0 || offset >= sizeof(AllProfilerStats) );
+ UInt8* dataPtr = reinterpret_cast<UInt8*>(&stats) + offset;
+ return *reinterpret_cast<int*> (dataPtr);
+}
+
+#endif
+#endif
diff --git a/Runtime/Profiler/SerializationUtility.cpp b/Runtime/Profiler/SerializationUtility.cpp
new file mode 100644
index 0000000..d9214ba
--- /dev/null
+++ b/Runtime/Profiler/SerializationUtility.cpp
@@ -0,0 +1,24 @@
+#include "UnityPrefix.h"
+#include "SerializationUtility.h"
+
+void WriteString(dynamic_array<int>& bitstream, const char* str)
+{
+ int len = strlen(str); // length including null terminator
+ int startindex = bitstream.size();
+ bitstream.resize_initialized( startindex + len/4 + 1);
+ memcpy((char*)&bitstream[startindex], str, len+1);
+}
+
+void WriteIntArray(dynamic_array<int>& bitstream, int* data, int count)
+{
+ for(int i = 0; i < count; i++)
+ bitstream.push_back (data[i]);
+}
+
+void ReadIntArray(int** bitstream, int* data, int count)
+{
+ for(int i = 0; i < count; i++)
+ data[i] = *((*bitstream)++);
+}
+
+
diff --git a/Runtime/Profiler/SerializationUtility.h b/Runtime/Profiler/SerializationUtility.h
new file mode 100644
index 0000000..4de6339
--- /dev/null
+++ b/Runtime/Profiler/SerializationUtility.h
@@ -0,0 +1,41 @@
+#ifndef _SERIALIZATIONUTILITY_H_
+#define _SERIALIZATIONUTILITY_H_
+
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+
+void WriteString(dynamic_array<int>& bitstream, const char* str);
+
+template <typename stringtype>
+void ReadString(int** bitstream, stringtype& str, bool swapdata)
+{
+ char* chars = (char*)*bitstream;
+ if(swapdata)
+ {
+ int wordcount = strlen(chars)/4 + 1;
+ for(int i = 0; i < wordcount; i++)
+ SwapEndianBytes((*bitstream)[i]);
+ }
+ str = stringtype((char*)*bitstream);
+ (*bitstream) += str.length()/4 + 1;
+}
+
+void WriteIntArray(dynamic_array<int>& bitstream, int* data, int count);
+
+template <typename writestruct>
+void WriteIntArray(dynamic_array<int>& bitstream, writestruct& data)
+{
+ int count = sizeof(data)/4;
+ WriteIntArray(bitstream, (int*)&data, count);
+}
+
+void ReadIntArray(int** bitstream, int* data, int count);
+
+template <typename writestruct>
+void ReadIntArray(int** bitstream, writestruct& data)
+{
+ int count = sizeof(data)/4;
+ ReadIntArray(bitstream, (int*)&data, count);
+}
+
+#endif
diff --git a/Runtime/Profiler/SharkProfiler.cpp b/Runtime/Profiler/SharkProfiler.cpp
new file mode 100644
index 0000000..1b3d592
--- /dev/null
+++ b/Runtime/Profiler/SharkProfiler.cpp
@@ -0,0 +1,93 @@
+#include "UnityPrefix.h"
+#include "SharkProfiler.h"
+#include "Configuration/UnityConfigure.h"
+
+#define ENABLE_SHARK_PROFILER ENABLE_PROFILER && UNITY_OSX
+
+#if ENABLE_SHARK_PROFILE
+#include <mach-o/dyld.h>
+
+typedef int (*fpChudAcquireRemoteAccess) ( void );
+typedef int (*fpChudReleaseRemoteAccess) ( void );
+typedef int (*fpChudStartRemotePerfMonitor) ( const char *label );
+typedef int (*fpChudStopRemotePerfMonitor) ( void );
+typedef int (*fpChudInitialize) ( void );
+typedef int (*fpChudCleanup) ( void );
+typedef int (*fpChudIsInitialized) ( void );
+
+// declare storage for each API's function pointers
+static int gChudLoaded = -1;
+static fpChudAcquireRemoteAccess gChudAcquireRemoteAccess = NULL;
+static fpChudReleaseRemoteAccess gChudReleaseRemoteAccess = NULL;
+static fpChudStartRemotePerfMonitor gChudStartRemotePerfMonitor = NULL;
+static fpChudStopRemotePerfMonitor gChudStopRemotePerfMonitor = NULL;
+static fpChudInitialize gChudInitialize = NULL;
+static fpChudCleanup gChudCleanup = NULL;
+static fpChudIsInitialized gChudIsInitialized = NULL;
+
+static bool LoadChudFunctionPointers ()
+{
+ if (gChudLoaded == 1)
+ return true;
+ if (gChudLoaded == 0)
+ return false;
+
+ CFStringRef bundlePath = CFSTR("/System/Library/PrivateFrameworks/CHUD.framework/Versions/A/Frameworks/CHUDCore.framework");
+ CFURLRef bundleURL = CFURLCreateWithFileSystemPath (kCFAllocatorDefault, bundlePath, kCFURLPOSIXPathStyle, true);
+ CFBundleRef bundle = CFBundleCreate (kCFAllocatorDefault, bundleURL);
+
+ if (bundle == NULL)
+ {
+ ErrorString("Failed loading Shark system library");
+ gChudLoaded = 0;
+ return false;
+ }
+
+ gChudAcquireRemoteAccess=(fpChudAcquireRemoteAccess)CFBundleGetFunctionPointerForName (bundle, CFSTR("chudAcquireRemoteAccess"));
+ gChudReleaseRemoteAccess=(fpChudReleaseRemoteAccess)CFBundleGetFunctionPointerForName (bundle, CFSTR("chudReleaseRemoteAccess"));
+ gChudStartRemotePerfMonitor=(fpChudStartRemotePerfMonitor)CFBundleGetFunctionPointerForName (bundle, CFSTR("chudStartRemotePerfMonitor"));
+ gChudStopRemotePerfMonitor=(fpChudStopRemotePerfMonitor)CFBundleGetFunctionPointerForName (bundle, CFSTR("chudStopRemotePerfMonitor"));
+ gChudInitialize=(fpChudInitialize)CFBundleGetFunctionPointerForName (bundle, CFSTR("chudInitialize"));
+ gChudCleanup=(fpChudCleanup)CFBundleGetFunctionPointerForName (bundle, CFSTR("chudCleanup"));
+ gChudIsInitialized=(fpChudIsInitialized)CFBundleGetFunctionPointerForName (bundle, CFSTR("chudIsInitialized"));
+
+ if (gChudAcquireRemoteAccess == NULL || gChudReleaseRemoteAccess == NULL || gChudStartRemotePerfMonitor == NULL
+ || gChudStartRemotePerfMonitor == NULL || gChudStopRemotePerfMonitor == NULL || gChudInitialize == NULL || gChudCleanup == NULL || gChudIsInitialized == NULL)
+ {
+ ErrorString("Failed loading Shark system library function");
+ gChudLoaded = 0;
+ return false;
+ }
+ else
+ {
+ gChudLoaded = 1;
+ return true;
+ }
+}
+#endif
+
+void SharkBeginRemoteProfiling ()
+{
+#if ENABLE_SHARK_PROFILER
+ if (!LoadChudFunctionPointers ())
+ return;
+
+ if (gChudInitialize() != 0)
+ ErrorString("Launching Shark in SharkBeginProfile failed");
+ if (gChudAcquireRemoteAccess() != 0)
+ ErrorString("Launching Shark in SharkBeginProfile failed");
+ if (gChudStartRemotePerfMonitor("Unity") != 0)
+ ErrorString("Launching Shark in SharkBeginProfile failed");
+#endif
+}
+
+void SharkEndRemoteProfiling ()
+{
+#if ENABLE_SHARK_PROFILER
+ if (!LoadChudFunctionPointers ())
+ return;
+
+ gChudStopRemotePerfMonitor();
+ gChudReleaseRemoteAccess();
+#endif
+} \ No newline at end of file
diff --git a/Runtime/Profiler/SharkProfiler.h b/Runtime/Profiler/SharkProfiler.h
new file mode 100644
index 0000000..60a352c
--- /dev/null
+++ b/Runtime/Profiler/SharkProfiler.h
@@ -0,0 +1,7 @@
+#ifndef _SHARK_PROFILER_H_
+#define _SHARK_PROFILER_H_
+
+void SharkBeginRemoteProfiling ();
+void SharkEndRemoteProfiling ();
+
+#endif //_SHARK_PROFILER_H_
diff --git a/Runtime/Profiler/TimeHelper.cpp b/Runtime/Profiler/TimeHelper.cpp
new file mode 100644
index 0000000..1f78f4a
--- /dev/null
+++ b/Runtime/Profiler/TimeHelper.cpp
@@ -0,0 +1,190 @@
+#include "UnityPrefix.h"
+#include "TimeHelper.h"
+
+#if UNITY_IPHONE || UNITY_OSX
+
+#include <mach/mach_time.h>
+
+static bool g_ProfileTimeInited = false;
+static UInt64 g_Numer;
+static UInt64 g_Denom;
+
+static void InitTime()
+{
+ if( !g_ProfileTimeInited )
+ {
+ mach_timebase_info_data_t timeInfo;
+ mach_timebase_info(&timeInfo);
+
+ g_Numer = timeInfo.numer;
+ g_Denom = timeInfo.denom;
+
+ g_ProfileTimeInited = true;
+ }
+}
+
+ProfileTimeFormat GetProfileTime(UInt64 elapsedTime)
+{
+ InitTime();
+ return ( (elapsedTime*g_Numer) / g_Denom );
+}
+
+ABSOLUTE_TIME ProfileTimeToAbsoluteTime(ProfileTimeFormat elapsedTime)
+{
+ InitTime();
+ return ( (elapsedTime*g_Denom) / g_Numer );
+}
+
+ABSOLUTE_TIME DivideAbsoluteTime(ABSOLUTE_TIME elapsedTime, int divisor)
+{
+ return ProfileTimeToAbsoluteTime(GetProfileTime(elapsedTime) / (UInt64)divisor);
+}
+
+
+ABSOLUTE_TIME SubtractAbsoluteTimeClamp (ABSOLUTE_TIME lhs, ABSOLUTE_TIME rhs)
+{
+ return (lhs < rhs) ? (ABSOLUTE_TIME)0 : lhs-rhs;
+}
+
+#elif UNITY_WIN | UNITY_XENON
+
+// Timer granularity of QuertPerformanceCounters is that of frequency.
+// Usually that is around 2M - resulting in a granularity of 500ns.
+// This is not optimal for performance profiling
+
+ABSOLUTE_TIME GetStartTime()
+{
+ LARGE_INTEGER start;
+
+ QueryPerformanceCounter(&start);
+
+ return (UInt64) start.QuadPart;
+}
+
+ABSOLUTE_TIME GetElapsedTime(ABSOLUTE_TIME startTime)
+{
+ LARGE_INTEGER end;
+
+ QueryPerformanceCounter(&end);
+
+ return (UInt64) end.QuadPart - startTime;
+}
+
+static bool s_HaveFrequency = false;
+static LARGE_INTEGER s_Frequency;
+
+ProfileTimeFormat GetProfileTime(ABSOLUTE_TIME elapsedTime)
+{
+ if (!s_HaveFrequency)
+ {
+ QueryPerformanceFrequency (&s_Frequency);
+ s_HaveFrequency = true;
+ }
+
+ return (elapsedTime * 1000000000LL) / s_Frequency.QuadPart;
+}
+
+ABSOLUTE_TIME ProfileTimeToAbsoluteTime(ProfileTimeFormat elapsedTime)
+{
+ if (!s_HaveFrequency)
+ {
+ QueryPerformanceFrequency (&s_Frequency);
+ s_HaveFrequency = true;
+ }
+
+ return (elapsedTime * s_Frequency.QuadPart) / 1000000000LL;
+}
+
+ABSOLUTE_TIME DivideAbsoluteTime(ABSOLUTE_TIME elapsedTime, int divisor)
+{
+ return elapsedTime / (UInt64)divisor;
+}
+
+#elif UNITY_PS3
+
+#include <sys/sys_time.h>
+
+inline ProfileTimeFormat timebase_frequency()
+{
+ static ProfileTimeFormat tf = sys_time_get_timebase_frequency();
+ return tf;
+}
+
+ProfileTimeFormat GetProfileTime(ABSOLUTE_TIME elapsedTime)
+{
+ return (elapsedTime * 1000000000LL) / timebase_frequency() ;
+}
+
+ABSOLUTE_TIME ProfileTimeToAbsoluteTime(ProfileTimeFormat elapsedTime)
+{
+ return (elapsedTime * timebase_frequency()) / 1000000000LL;
+}
+
+ABSOLUTE_TIME DivideAbsoluteTime(ABSOLUTE_TIME elapsedTime, int divisor)
+{
+ return elapsedTime / (UInt64)divisor;
+}
+
+#elif UNITY_ANDROID || UNITY_PEPPER || UNITY_LINUX || UNITY_FLASH || UNITY_WEBGL || UNITY_BB10 || UNITY_TIZEN
+
+ProfileTimeFormat GetProfileTime(ABSOLUTE_TIME elapsedTime)
+{
+ return elapsedTime;
+}
+
+ABSOLUTE_TIME ProfileTimeToAbsoluteTime(ProfileTimeFormat elapsedTime)
+{
+ return elapsedTime;
+}
+
+ABSOLUTE_TIME DivideAbsoluteTime(ABSOLUTE_TIME elapsedTime, int divisor)
+{
+ return elapsedTime / (UInt64)divisor;
+}
+
+ABSOLUTE_TIME SubtractAbsoluteTimeClamp(ABSOLUTE_TIME lhs, ABSOLUTE_TIME rhs)
+{
+ if (IsSmallerAbsoluteTime(lhs, rhs))
+ {
+ ABSOLUTE_TIME zero;
+ ABSOLUTE_TIME_INIT(zero);
+ return zero;
+ }
+ else
+ {
+ return lhs - rhs;
+ }
+}
+
+#elif UNITY_WII
+
+ABSOLUTE_TIME GetStartTime()
+{
+ // ToDo
+ return 0;
+}
+ABSOLUTE_TIME GetElapsedTime(ABSOLUTE_TIME startTime)
+{
+ // ToDo:
+ return 0;
+}
+ProfileTimeFormat GetProfileTime(ABSOLUTE_TIME elapsedTime)
+{
+ return OSTicksToNanoseconds (elapsedTime);
+}
+
+ABSOLUTE_TIME ProfileTimeToAbsoluteTime(ProfileTimeFormat elapsedTime)
+{
+ return OSNanosecondsToTicks (elapsedTime);
+}
+
+ABSOLUTE_TIME DivideAbsoluteTime(ABSOLUTE_TIME elapsedTime, int divisor)
+{
+ return elapsedTime / (UInt64)divisor;
+}
+
+#else
+
+#error IMPLEMENT ME
+
+#endif \ No newline at end of file
diff --git a/Runtime/Profiler/TimeHelper.h b/Runtime/Profiler/TimeHelper.h
new file mode 100644
index 0000000..d3e9350
--- /dev/null
+++ b/Runtime/Profiler/TimeHelper.h
@@ -0,0 +1,121 @@
+#ifndef _TIMEHELPER_H_
+#define _TIMEHELPER_H_
+
+// The profiler interprets this value as nanoseconds
+typedef UInt64 ProfileTimeFormat;
+#define kInvalidProfileTime (~ProfileTimeFormat(0))
+
+#if UNITY_IPHONE || UNITY_OSX
+
+ extern "C" { uint64_t mach_absolute_time(void); }
+
+ #define ABSOLUTE_TIME UInt64
+ #define ABSOLUTE_TIME_INIT(VAR) VAR = 0u;
+
+ #define START_TIME mach_absolute_time()
+ #define ELAPSED_TIME(VAR) (START_TIME - VAR)
+ #define COMBINED_TIME(VAR1, VAR2) (VAR1 + VAR2)
+ #define SUBTRACTED_TIME(VAR1, VAR2) (VAR1 - VAR2)
+
+
+ ABSOLUTE_TIME SubtractAbsoluteTimeClamp(ABSOLUTE_TIME lhs, ABSOLUTE_TIME rhs);
+ ABSOLUTE_TIME DivideAbsoluteTime(ABSOLUTE_TIME elapsedTime, int divisor);
+ inline bool IsEqualAbsoluteTime (ABSOLUTE_TIME lhs, ABSOLUTE_TIME rhs) { return rhs == lhs; }
+ inline bool IsSmallerAbsoluteTime (ABSOLUTE_TIME lhs, ABSOLUTE_TIME rhs) { return rhs > lhs; }
+
+ ProfileTimeFormat GetProfileTime(ABSOLUTE_TIME elapsedTime);
+ ABSOLUTE_TIME ProfileTimeToAbsoluteTime(ProfileTimeFormat elapsedTime);
+
+#elif UNITY_ANDROID || UNITY_PEPPER || UNITY_LINUX || UNITY_FLASH || UNITY_WEBGL || UNITY_BB10 || UNITY_TIZEN
+
+ #include <sys/time.h>
+
+ #define ABSOLUTE_TIME UInt64
+ #define ABSOLUTE_TIME_INIT(VAR) VAR = 0ull;
+
+ inline const ABSOLUTE_TIME _StartTime() { timeval time; gettimeofday(&time, 0); return time.tv_usec * 1000ULL + time.tv_sec * 1000000000ULL; }
+ #define START_TIME _StartTime()
+ #define ELAPSED_TIME(VAR) (START_TIME - VAR)
+ #define COMBINED_TIME(VAR1, VAR2) (VAR1 + VAR2)
+ #define SUBTRACTED_TIME(VAR1, VAR2) (VAR1 - VAR2)
+
+ ABSOLUTE_TIME SubtractAbsoluteTimeClamp(ABSOLUTE_TIME lhs, ABSOLUTE_TIME rhs);
+ ABSOLUTE_TIME DivideAbsoluteTime(ABSOLUTE_TIME elapsedTime, int divisor);
+ inline bool IsEqualAbsoluteTime (ABSOLUTE_TIME lhs, ABSOLUTE_TIME rhs) { return rhs == lhs; }
+ inline bool IsSmallerAbsoluteTime (ABSOLUTE_TIME lhs, ABSOLUTE_TIME rhs) { return rhs > lhs; }
+
+ ProfileTimeFormat GetProfileTime(ABSOLUTE_TIME elapsedTime);
+ ABSOLUTE_TIME ProfileTimeToAbsoluteTime(ProfileTimeFormat elapsedTime);
+
+#elif UNITY_WIN || UNITY_XENON
+ #define ABSOLUTE_TIME UInt64
+ #define ABSOLUTE_TIME_INIT(VAR) VAR = 0u;
+
+ #define START_TIME GetStartTime()
+ #define ELAPSED_TIME(VAR) GetElapsedTime(VAR)
+ #define COMBINED_TIME(VAR1, VAR2) VAR1 + VAR2
+ #define SUBTRACTED_TIME(VAR1, VAR2) VAR1 - VAR2
+
+ inline ABSOLUTE_TIME SubtractAbsoluteTimeClamp(ABSOLUTE_TIME lhs, ABSOLUTE_TIME rhs) { if (lhs > rhs) return lhs - rhs; else return 0; }
+ ABSOLUTE_TIME DivideAbsoluteTime(ABSOLUTE_TIME elapsedTime, int divisor);
+ inline bool IsEqualAbsoluteTime (ABSOLUTE_TIME lhs, ABSOLUTE_TIME rhs) { return lhs == rhs; }
+ inline bool IsSmallerAbsoluteTime (ABSOLUTE_TIME lhs, ABSOLUTE_TIME rhs) { return lhs < rhs; }
+
+ ABSOLUTE_TIME GetStartTime();
+ ABSOLUTE_TIME GetElapsedTime(ABSOLUTE_TIME startTime);
+ ProfileTimeFormat GetProfileTime(ABSOLUTE_TIME elapsedTime);
+ ABSOLUTE_TIME ProfileTimeToAbsoluteTime(ProfileTimeFormat elapsedTime);
+
+
+#elif UNITY_PS3
+
+ #include <sys/time_util.h>
+
+ #define ABSOLUTE_TIME UInt64
+ #define ABSOLUTE_TIME_INIT(VAR) VAR = 0u;
+
+ inline const ABSOLUTE_TIME _StartTime() { ABSOLUTE_TIME tb; SYS_TIMEBASE_GET(tb); return tb; }
+ #define START_TIME _StartTime()
+ #define ELAPSED_TIME(VAR) START_TIME - VAR
+ #define COMBINED_TIME(VAR1, VAR2) VAR1 + VAR2
+ #define SUBTRACTED_TIME(VAR1, VAR2) VAR1 - VAR2
+ ProfileTimeFormat GetProfileTime(ABSOLUTE_TIME elapsedTime);
+
+ inline ABSOLUTE_TIME SubtractAbsoluteTimeClamp(ABSOLUTE_TIME lhs, ABSOLUTE_TIME rhs) { if (lhs > rhs) return lhs - rhs; else return 0; }
+ ABSOLUTE_TIME DivideAbsoluteTime(ABSOLUTE_TIME elapsedTime, int divisor);
+ inline bool IsEqualAbsoluteTime (ABSOLUTE_TIME lhs, ABSOLUTE_TIME rhs) { return lhs == rhs; }
+ inline bool IsSmallerAbsoluteTime (ABSOLUTE_TIME lhs, ABSOLUTE_TIME rhs) { return lhs < rhs; }
+#elif UNITY_WII
+ #include <revolution/os.h>
+ #define ABSOLUTE_TIME OSTime
+ #define ABSOLUTE_TIME_INIT(VAR) VAR = 0u;
+
+ #define START_TIME OSGetTime()
+ #define ELAPSED_TIME(VAR) GetElapsedTime(VAR)
+ #define COMBINED_TIME(VAR1, VAR2) VAR1 + VAR2
+ #define SUBTRACTED_TIME(VAR1, VAR2) VAR1 - VAR2
+
+ inline ABSOLUTE_TIME SubtractAbsoluteTimeClamp(ABSOLUTE_TIME lhs, ABSOLUTE_TIME rhs) { if (lhs > rhs) return lhs - rhs; else return 0; }
+ ABSOLUTE_TIME DivideAbsoluteTime(ABSOLUTE_TIME elapsedTime, int divisor);
+ inline bool IsEqualAbsoluteTime (ABSOLUTE_TIME lhs, ABSOLUTE_TIME rhs) { return lhs == rhs; }
+ inline bool IsSmallerAbsoluteTime (ABSOLUTE_TIME lhs, ABSOLUTE_TIME rhs) { return lhs < rhs; }
+
+ ABSOLUTE_TIME GetStartTime();
+ ABSOLUTE_TIME GetElapsedTime(ABSOLUTE_TIME startTime);
+ ProfileTimeFormat GetProfileTime(ABSOLUTE_TIME elapsedTime);
+ ABSOLUTE_TIME ProfileTimeToAbsoluteTime(ProfileTimeFormat elapsedTime);
+
+#else
+ #error IMPLEMENT ME
+#endif
+
+inline float ProfileTimeToSeconds (ProfileTimeFormat elapsedTime) { return elapsedTime * 0.000000001; }
+inline float AbsoluteTimeToSeconds (ABSOLUTE_TIME absoluteTime) { return ProfileTimeToSeconds(GetProfileTime(absoluteTime)); }
+inline float GetElapsedTimeInSeconds (ABSOLUTE_TIME elapsedTime) { return ProfileTimeToSeconds(GetProfileTime(ELAPSED_TIME(elapsedTime))); }
+
+inline float AbsoluteTimeToMilliseconds (ABSOLUTE_TIME time)
+{
+ return AbsoluteTimeToSeconds(time) * 1000.0F;
+}
+
+#endif /*_TIMEHELPER_H_*/
diff --git a/Runtime/Scripting/AS3Utility.cpp b/Runtime/Scripting/AS3Utility.cpp
new file mode 100644
index 0000000..8c93499
--- /dev/null
+++ b/Runtime/Scripting/AS3Utility.cpp
@@ -0,0 +1,31 @@
+#include "UnityPrefix.h"
+#include "ScriptingUtility.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/Backend/ScriptingArguments.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+
+extern "C" bool NativeExt_SendMessage(ScriptingStringPtr path, ScriptingStringPtr method, ScriptingObjectPtr value)
+{
+ Transform* transform = FindActiveTransformWithPath( (const char*) path );
+ if( transform != NULL)
+ {
+ return Scripting::SendScriptingMessage( transform->GetGameObject(), (const char*) method, value );
+ }
+ return false;
+}
+
+bool ReadStringFromFile (TEMP_STRING* outData, const string& path)
+{
+ const char* outDataFlash = Ext_FileContainer_ReadStringFromFile(path.c_str());
+ if(outDataFlash != NULL){
+ *outData = outDataFlash;
+ return true;
+ }
+ return false;
+}
+
+ScriptingObjectPtr ScriptingInstantiateObjectFromClassName(const char* name)
+{
+ return Ext_GetNewMonoBehaviour(name);
+}
diff --git a/Runtime/Scripting/AS3Utility.h b/Runtime/Scripting/AS3Utility.h
new file mode 100644
index 0000000..31dd7fc
--- /dev/null
+++ b/Runtime/Scripting/AS3Utility.h
@@ -0,0 +1,144 @@
+#ifndef AS3UTILITY_H
+#define AS3UTILITY_H
+
+#if !defined(SCRIPTINGUTILITY_H)
+#error "Don't include AS3Utility.h, include ScriptingUtility.h instead"
+#endif
+
+typedef UInt32 AS3Handle;
+struct UTF16String;
+
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+
+#include "Scripting.h"
+
+using namespace std;
+
+#define htons(A) (A)
+#define htonl(A) (A)
+#define ntohs htons
+#define ntohl htohl
+
+#define SCRIPTINGAPI_THREAD_CHECK(NAME)
+#define SCRIPTINGAPI_CONSTRUCTOR_CHECK(NAME)
+#define SCRIPTINGAPI_DEFINE_REF_ARG(t, n) t n
+#define SCRIPTINGAPI_FIX_REF_ARG(t, n)
+
+extern "C" void Ext_FileContainer_MakeFilesAvailable();
+extern "C" int Ext_FileContainer_GetFileLength(const char* filename);
+extern "C" char* Ext_Flash_GetNameFromClass(ScriptingType* handle);
+extern "C" void Ext_Stack_Store(void* ptr);
+extern "C" void Ext_Flash_LogCallstack();
+extern "C" void Ext_Flash_ThrowError(const char*) __attribute__((noreturn));
+
+extern "C" int Ext_MarshallTo(ScriptingObjectPtr obj, void* dest);
+extern "C" int Ext_UnmarshallFrom(ScriptingObjectPtr obj, void* src);
+//extern "C" void Ext_SetNativePtr(ScriptingObjectPtr obj, void* ptr);
+//extern "C" void* Ext_GetNativePtr(ScriptingObjectPtr obj);
+
+//ScriptingArray* MonoFindObjectsOfType (ScriptingType* reflectionTypeObject, int mode);
+
+namespace Unity { class GameObject; class Component; }
+using namespace Unity;
+
+extern "C" const char* Ext_FileContainer_ReadStringFromFile(const char* filename);
+
+//Used by MonoBehaviourSerialization
+extern "C" void Ext_SerializeMonoBehaviour(ScriptingObjectPtr behaviour);
+extern "C" UInt8* Ext_DeserializeMonoBehaviour(ScriptingObjectPtr behaviour);
+extern "C" void Ext_RemapPPtrs(ScriptingObjectPtr behaviour);
+
+extern "C" ScriptingObjectPtr Ext_Scripting_InstantiateScriptingWrapperForClassWithName(const char* name);
+
+extern "C" bool Ext_FileContainer_IsFileCreatedAt(const char* filename);
+extern "C" void Ext_WriteTextAndToolTipIntoUTF16Strings(ScriptingObject* obj, UTF16String* text, UTF16String* tooltip);
+
+extern "C" bool Ext_Trace(const char* msg);
+extern "C" ScriptingObjectPtr Ext_GetNewMonoBehaviour(const char* name);
+
+//External interface / openurl / application.
+extern "C" void Ext_OpenURL(const char* url);
+extern "C" ScriptingString* Ext_ExternalCall(const char* function, ScriptingObjectPtr args);
+
+extern "C" ScriptingObjectPtr Ext_Flash_getProperty(ScriptingObjectPtr object, const char* property);
+
+template<class T>
+ScriptingObjectPtr Flash_CreateScriptingObjectFromNativeStruct(ScriptingClass* klass, T& thestruct);
+
+template<class T> inline
+ScriptingObjectPtr CreateScriptingObjectFromNativeStruct(ScriptingClass* klass, T& thestruct)
+{
+ //printf_console("creating instance of klass: %d",klass);
+ ScriptingObjectPtr obj = scripting_object_new(klass);
+ //printf_console("populating it with native data stored at %d",&thestruct);
+ Ext_UnmarshallFrom(obj, &thestruct);
+ return obj;
+}
+
+inline void RaiseIfNull(ScriptingObjectPtr o) {}
+
+inline int GetScriptingArraySize(ScriptingArray* a)
+{
+ if(a == NULL)
+ return 0;
+
+ //POD arrays are encoded by the as3 glue as a memoryblob: one int describing the size, following the actual bytes.
+ return *(int*)(a);
+}
+
+extern "C" int Ext_MarshallTo(ScriptingObjectPtr obj, void* dest);
+extern "C" int Ext_UnmarshallFrom(ScriptingObjectPtr obj, void* src);
+
+template<class T> inline
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, T* dest)
+{
+ Ext_MarshallTo(so,(void*)dest);
+}
+
+template<class T> inline
+void MarshallNativeStructIntoManaged(T& src, ScriptingObjectPtr dest)
+{
+ Ext_UnmarshallFrom(dest,(void*)&src);
+}
+
+template<class T>
+inline T* ScriptingObjectToObject(ScriptingObjectPtr so)
+{
+ //todo: create the optimized path where we directly grab the cachedptr
+ PPtr<T> p;
+ p.SetInstanceID(Scripting::GetInstanceIDFromScriptingWrapper(so));
+ return p;
+}
+
+ScriptingObjectPtr ScriptingInstantiateObjectFromClassName(const char* name);
+
+inline
+char* ScriptingStringToAllocatedChars(const ICallString& str)
+{
+ return strdup(str.utf8stream);
+}
+
+inline void ScriptingStringToAllocatedChars_Free(const char* str)
+{
+ free((void*)str);
+}
+
+template<class T>
+inline ScriptingObjectPtr ScriptingGetObjectReference(PPtr<T> object)
+{
+ return Scripting::ScriptingWrapperFor(object);
+}
+template<class T>
+inline ScriptingObjectPtr ScriptingGetObjectReference(T* object)
+{
+ return Scripting::ScriptingWrapperFor(object);
+}
+
+#define CreateScriptingParams(VariableName, ParamCount) ScriptingParams VariableName[ParamCount];
+// Don't use if Value is ScriptingObjectPtr!!!
+#define SetScriptingParam(VariableName, Index, Value) VariableName[Index] = &Value;
+#define GetSafeString(ClassName, Getter) Getter
+
+#endif \ No newline at end of file
diff --git a/Runtime/Scripting/Backend/Flash/ScriptingBackendApi_Flash.cpp b/Runtime/Scripting/Backend/Flash/ScriptingBackendApi_Flash.cpp
new file mode 100644
index 0000000..8022f96
--- /dev/null
+++ b/Runtime/Scripting/Backend/Flash/ScriptingBackendApi_Flash.cpp
@@ -0,0 +1,287 @@
+#include "UnityPrefix.h"
+
+#include "../ScriptingTypes.h"
+#include "ScriptingBackendApi_Flash.h"
+#include "../ScriptingTypeRegistry.h"
+#include "../ScriptingMethodRegistry.h"
+#include <string>
+#include "../ScriptingArguments.h"
+#include "PlatformDependent\FlashSupport\cpp\FlashUtils.h"
+
+std::string scripting_cpp_string_for(ScriptingStringPtr str)
+{
+ return std::string((const char*)str);
+}
+
+//implemented in raw actionscript
+extern "C" ScriptingClass* Ext_Flash_GetBaseClassFromScriptingClass(ScriptingClass* klass);
+bool scripting_method_is_instance(ScriptingMethodPtr method)
+{
+ //not implemented on flash yet.
+ return true;
+}
+
+ScriptingStringPtr scripting_string_new(const char* str)
+{
+ __asm __volatile__("returnString = strFromPtr(%0);" : : "r" (str) );
+ return (ScriptingStringPtr)str;
+}
+
+ScriptingStringPtr scripting_string_new(const std::string& str)
+{
+ return scripting_string_new(str.c_str());
+}
+
+extern "C" int Ext_Flash_ScriptingGetMethodParamCount(ScriptingObjectPtr object, AS3String as3String);
+int scripting_method_get_argument_count(ScriptingMethodPtr method, ScriptingTypeRegistry& typeRegistry)
+{
+ ScriptingObjectPtr methodInfo = method->GetSystemReflectionMethodInfo();
+ if (methodInfo==NULL)
+ return 0;
+
+ int result;
+ FLASH_ASM_WITH_NEWSP("%0 = ScriptingMethodHelper.NumberOfArgumentsOf(marshallmap.getObjectWithId(%1));" : "=r"(result) : "r"(methodInfo));
+
+ return result;
+}
+
+ScriptingTypePtr scripting_method_get_nth_argumenttype(ScriptingMethodPtr method, int index, ScriptingTypeRegistry& typeRegistry)
+{
+ ScriptingObjectPtr methodInfo = method->GetSystemReflectionMethodInfo();
+ if (methodInfo==NULL)
+ return NULL;
+
+ ScriptingTypePtr result;
+ FLASH_ASM_WITH_NEWSP("scratch_object = ScriptingMethodHelper.NthArgumentType(marshallmap.getObjectWithId(%0), %1)" : : "r"(methodInfo), "r"(index));
+ FLASH_ASM_WITH_NEWSP("if (scratch_object!=null) %0 = marshallmap.getIdForObject(scratch_object.GetClass()); else %0 = 0;" : "=r"(result));
+ return result;
+}
+
+const char* scripting_method_get_name(ScriptingMethodPtr method)
+{
+ return method->GetName();
+}
+
+bool scripting_method_has_attribute (ScriptingMethodPtr method, ScriptingClassPtr attribute)
+{
+ //not implemented on flash yet
+ return false;
+}
+
+ScriptingTypePtr scripting_class_get_parent(ScriptingTypePtr type, ScriptingTypeRegistry& typeRegistry)
+{
+ return Ext_Flash_GetBaseClassFromScriptingClass(type);
+}
+
+bool scripting_class_is_enum(ScriptingTypePtr type)
+{
+ //todo: implement
+ return false;
+}
+
+extern "C" const char* Ext_Flash_GetNameFromClass(ScriptingClassPtr klass);
+const char* scripting_class_get_name(ScriptingClassPtr klass)
+{
+ //Horrible, we can only clean this up once we create a struct ScriptingClass for flash.
+ std::string* s = new std::string(Ext_Flash_GetNameFromClass(klass));
+ return s->c_str();
+}
+
+extern "C" const char* Ext_Flash_GetNameSpaceFromScriptingType(ScriptingType* scriptingType);
+const char* scripting_class_get_namespace(ScriptingClassPtr klass)
+{
+ //Horrible, we can only clean this up once we create a struct ScriptingClass for flash.
+ std::string* s = new std::string(Ext_Flash_GetNameSpaceFromScriptingType(klass));
+ return s->c_str();
+}
+
+extern "C" bool Ext_Flash_ScriptingClassIsSubclassOf(ScriptingTypePtr t1, ScriptingTypePtr t2);
+bool scripting_class_is_subclass_of(ScriptingTypePtr t1, ScriptingTypePtr t2)
+{
+ return Ext_Flash_ScriptingClassIsSubclassOf(t1,t2);
+}
+
+ScriptingTypePtr scripting_method_get_returntype(ScriptingMethodPtr method, ScriptingTypeRegistry& typeRegistry)
+{
+ return method->GetReturnType();
+}
+
+extern "C" ScriptingObjectPtr Ext_Flash_InvokeMethodOnObject(void* object, AS3String as3String, ScriptingException** exception);
+ScriptingObjectPtr scripting_method_invoke(ScriptingMethodPtr method, ScriptingObjectPtr object, ScriptingArguments& arguments, ScriptingExceptionPtr* exception)
+{
+ void* objectToInvokeOn = object;
+ *exception = NULL;
+ if (object==NULL)
+ {
+ objectToInvokeOn = method->m_Class;
+ if (objectToInvokeOn == NULL)
+ ErrorString("flash_invoke_method called with NULL object, and scriptmethod->m_Class is NULL too.");
+ }
+
+ __asm __volatile__("invocation_arguments.length = 0;\n");
+
+ for (int i=0; i!=arguments.GetCount(); i++)
+ {
+ switch (arguments.GetTypeAt(i))
+ {
+ case ScriptingArguments::ARGTYPE_BOOLEAN:
+ __asm __volatile__("invocation_arguments.push(%0 ? true : false);" : : "r"(arguments.GetBooleanAt(i)));
+ break;
+ case ScriptingArguments::ARGTYPE_INT:
+ __asm __volatile__("invocation_arguments.push(%0);" : : "r"(arguments.GetIntAt(i)));
+ break;
+ case ScriptingArguments::ARGTYPE_FLOAT:
+ __asm __volatile__("invocation_arguments.push(%0);" : : "f"(arguments.GetFloatAt(i)));
+ break;
+ case ScriptingArguments::ARGTYPE_STRING:
+ __asm __volatile__("invocation_arguments.push(strFromPtr(%0));" : : "r"(arguments.GetStringAt(i)));
+ break;
+ case ScriptingArguments::ARGTYPE_OBJECT:
+ __asm __volatile__("invocation_arguments.push(marshallmap.getObjectWithId(%0));" : : "r"(arguments.GetObjectAt(i)));
+ break;
+ default:
+ ErrorString(Format("Flash does not support calling managed methods with this type of argument: %d",arguments.GetTypeAt(i)));
+ break;
+ }
+ }
+ return Ext_Flash_InvokeMethodOnObject(objectToInvokeOn, method->m_As3String,exception);
+}
+
+ScriptingTypePtr scripting_class_from_systemtypeinstance(ScriptingObjectPtr systemTypeInstance, ScriptingTypeRegistry& typeRegistry)
+{
+ //todo: think about if this is actually correct.
+ return (ScriptingClassPtr)systemTypeInstance;
+}
+
+extern "C" ScriptingObjectPtr Ext_Flash_CreateInstance(ScriptingClass* klass);
+ScriptingObjectPtr scripting_object_new(ScriptingTypePtr t)
+{
+ return Ext_Flash_CreateInstance(t);
+}
+
+extern "C" ScriptingClassPtr Ext_Flash_GetScriptingTypeOfScriptingObject(ScriptingObjectPtr);
+ScriptingTypePtr scripting_object_get_class(ScriptingObjectPtr o, ScriptingTypeRegistry& typeRegistry)
+{
+ return Ext_Flash_GetScriptingTypeOfScriptingObject(o);
+}
+
+ScriptingMethodPtr scripting_object_get_virtual_method(ScriptingObjectPtr o, ScriptingMethodPtr method, ScriptingMethodRegistry& methodRegistry)
+{
+ return method;
+}
+
+void scripting_object_invoke_default_constructor(ScriptingObjectPtr o, ScriptingExceptionPtr* exc)
+{
+ *exc = NULL;
+ //todo: properly deal with exception.
+ FLASH_ASM_WITH_NEWSP("marshallmap.getObjectWithId(%0).cil2as_DefaultConstructor()" : : "r"(o));
+}
+
+int scripting_gchandle_new(ScriptingObjectPtr o)
+{
+ __asm __volatile__("marshallmap.gcHandle(%0);" : : "r" (o));
+ return (int)o;
+}
+
+int scripting_gchandle_weak_new(ScriptingObjectPtr o)
+{
+ FatalErrorMsg("ToDo");
+ return 0;
+}
+
+void scripting_gchandle_free(int handle)
+{
+ __asm __volatile__("marshallmap.gcFree(%0);" : : "r" (handle));
+}
+
+ScriptingObjectPtr scripting_gchandle_get_target(int handle)
+{
+ return (ScriptingObjectPtr) handle;
+}
+
+int scripting_gc_maxgeneration()
+{
+ return 0;
+}
+
+void scripting_gc_collect(int maxGeneration)
+{
+ FatalErrorMsg("ToDo");
+}
+
+void ScriptingMethod::Init(const char* name, const char* mappedName, const char* sig, ScriptingClass* klass)
+{
+ m_Name = strcpy(new char[strlen(name) + 1],name);
+ m_Mappedname = strcpy(new char[strlen(mappedName) + 1],mappedName);
+ __asm __volatile__("%0 = getAS3StringForPtr(%1);\n" : "=r" (m_As3String) : "r" (m_Mappedname));
+
+ m_Signature = strcpy(new char[strlen(sig) + 1],sig);
+ m_Class = klass;
+ m_MethodInfo = NULL;
+ FLASH_ASM_WITH_NEWSP("%0 = marshallmap.getIdForObject(System.Type.ForClass(marshallmap.getObjectWithId(%1)));\n" : "=r" (m_SystemType) : "r"(m_Class) );
+ scripting_gchandle_new(m_SystemType);
+}
+
+extern "C" const char* Ext_GetMappedMethodName(const char* name, ScriptingType* klass);
+ScriptingMethod::ScriptingMethod(const char* name, ScriptingClass* klass)
+{
+ const char* mappedName = Ext_GetMappedMethodName(name,klass);
+ Init(name,mappedName,"",klass);
+}
+
+ScriptingMethod::ScriptingMethod(const char* name, const char* mappedName, const char* sig, ScriptingClass* klass)
+{
+ Init(name,mappedName,sig,klass);
+}
+
+extern "C" ScriptingClass* Ext_Flash_GetMethodReturnType(const char* methodname, ScriptingClass* klass);
+ScriptingClass* ScriptingMethod::GetReturnType()
+{
+ return Ext_Flash_GetMethodReturnType(m_Mappedname, m_Class);
+}
+
+ScriptingObjectPtr ScriptingMethod::GetSystemReflectionMethodInfo()
+{
+ if (m_MethodInfo)
+ return m_MethodInfo;
+
+ FLASH_ASM_WITH_NEWSP("%0 = marshallmap.getIdForObject(ScriptingMethodHelper.GetMethodInfo(marshallmap.getObjectWithId(%1), strFromPtr(%2)));" : "=r"(m_MethodInfo) : "r"(m_SystemType),"r"(m_Name) );
+
+ scripting_gchandle_new(m_MethodInfo);
+ return m_MethodInfo;
+}
+
+ScriptingObjectPtr scripting_class_get_object(ScriptingClassPtr klass)
+{
+ FatalErrorMsg("ToDo");
+ return SCRIPTING_NULL;
+}
+
+ScriptingArrayPtr scripting_cast_object_to_array(ScriptingObjectPtr o)
+{
+ FatalErrorMsg("ToDo");
+ return SCRIPTING_NULL;
+}
+
+void scripting_stack_trace_info_for(ScriptingExceptionPtr exception, StackTraceInfo& info)
+{
+ AssertIf (exception == NULL);
+
+ __asm __volatile__("var errorStr:String = marshallmap.getObjectWithId(%0) as String;"::"r"(exception));
+ int length;
+ __asm __volatile__("%0=getStringMarshallingLength(errorStr);":"=r"(length));
+ char* errorStrPtr = (char*)alloca(length);
+ __asm __volatile__("var ptr:int = placeStringAtPtr(errorStr,%0);"::"r"(errorStrPtr));
+
+ info.condition = errorStrPtr;
+ info.strippedStacktrace = "";
+ info.stacktrace = "";
+ info.errorNum = 0;
+ info.file = "";
+ info.line = 0;
+}
+
+void* scripting_array_element_ptr(ScriptingArrayPtr array, int i, size_t element_size)
+{
+ return (UInt8*)array + sizeof(int) + i * element_size;
+} \ No newline at end of file
diff --git a/Runtime/Scripting/Backend/Flash/ScriptingBackendApi_Flash.h b/Runtime/Scripting/Backend/Flash/ScriptingBackendApi_Flash.h
new file mode 100644
index 0000000..c2372a2
--- /dev/null
+++ b/Runtime/Scripting/Backend/Flash/ScriptingBackendApi_Flash.h
@@ -0,0 +1,45 @@
+#ifndef _SCRIPTINGBACKENDAPI_FLASH_H_
+#define _SCRIPTINGBACKENDAPI_FLASH_H_
+
+#include <string>
+
+#include "../ScriptingTypes.h"
+#include "../ScriptingBackendApi.h"
+
+//todo: remove
+typedef ScriptingObject* (*FastMonoMethod) (void* thiz, ScriptingException** ex);
+typedef int AS3String;
+
+struct ScriptingMethod
+{
+ const char* m_Name;
+ const char* m_Mappedname;
+ const char* m_Signature;
+ AS3String m_As3String;
+
+ ScriptingClass* m_Class;
+ ScriptingObject* m_SystemType;
+ ScriptingObject* m_MethodInfo;
+
+ ScriptingMethod(const char* name, ScriptingClass* klass);
+ ScriptingMethod(const char* name, const char* mappedName, const char* sig, ScriptingClass* klass);
+ void Init(const char* name, const char* mappedName, const char* sig, ScriptingClass* klass);
+ ScriptingClass* GetReturnType();
+ const char* GetName() { return m_Name; }
+ ScriptingObjectPtr GetSystemReflectionMethodInfo();
+};
+
+struct ScriptingField
+{
+ ScriptingField(const char* name,const char* type)
+ : m_name(name)
+ , m_type(type)
+ {
+
+ }
+
+ std::string m_name;
+ std::string m_type;
+};
+
+#endif
diff --git a/Runtime/Scripting/Backend/Flash/ScriptingMethodFactory_Flash.h b/Runtime/Scripting/Backend/Flash/ScriptingMethodFactory_Flash.h
new file mode 100644
index 0000000..35b7015
--- /dev/null
+++ b/Runtime/Scripting/Backend/Flash/ScriptingMethodFactory_Flash.h
@@ -0,0 +1,40 @@
+#ifndef _SCRIPTINGMETHODFACTORY_FLASH_
+#define _SCRIPTINGMETHODFACTORY_FLASH_
+
+#include "../ScriptingMethodFactory.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "ScriptingBackendApi_Flash.h"
+
+#if UNITY_FLASH
+
+extern "C" const char* Ext_GetMappedMethodName(const char* name, ScriptingType* klass);
+
+class ScriptingMethodFactory_Flash : public IScriptingMethodFactory
+{
+public:
+ virtual ScriptingMethodPtr Produce(ScriptingTypePtr klass, const char* name, int searchFilter)
+ {
+ //todo: respect the searchfilter
+ std::string mappedName(Ext_GetMappedMethodName(name,klass));
+
+ //remove this hack when interfaces contain mapping information
+ if (mappedName.size()==0 && strcmp(name,"MoveNext")==0)
+ mappedName.assign("IEnumerator_MoveNext");
+ return new ScriptingMethod(name, mappedName.c_str(), "",klass);
+ }
+
+ virtual ScriptingMethodPtr Produce(void* nativeMethod)
+ {
+ //not implemented for flash.
+ return NULL;
+ }
+
+ virtual void Release(ScriptingMethodPtr method)
+ {
+ delete method;
+ }
+};
+
+#endif
+
+#endif
diff --git a/Runtime/Scripting/Backend/Flash/ScriptingTypeProvider_Flash.h b/Runtime/Scripting/Backend/Flash/ScriptingTypeProvider_Flash.h
new file mode 100644
index 0000000..eb2142c
--- /dev/null
+++ b/Runtime/Scripting/Backend/Flash/ScriptingTypeProvider_Flash.h
@@ -0,0 +1,53 @@
+#ifndef _SCRIPTINGTYPEPROVIDER_FLASH
+#define _SCRIPTINGTYPEPROVIDER_FLASH
+
+#include "../IScriptingTypeProvider.h"
+//#include "Runtime/Scripting/ScriptingUtility.h"
+
+#if UNITY_FLASH
+
+extern "C" ScriptingTypePtr Ext_Flash_GetScriptingTypeFromName(const char* name);
+
+class ScriptingTypeProvider_Flash : public IScriptingTypeProvider
+{
+public:
+ virtual BackendNativeType NativeTypeFor(const char* namespaze, const char* name)
+ {
+ if (strcmp(name,"Object") == 0)
+ name = "_Object";
+
+ if (strcmp(namespaze,"System")==0)
+ {
+ if (strcmp(name,"String")==0)
+ return Ext_Flash_GetScriptingTypeFromName("String");
+ if (strcmp(name,"Int32")==0)
+ return Ext_Flash_GetScriptingTypeFromName("int");
+ if (strcmp(name,"Single")==0)
+ return Ext_Flash_GetScriptingTypeFromName("Number");
+ if (strcmp(name,"Double")==0)
+ return Ext_Flash_GetScriptingTypeFromName("Number");
+ if (strcmp(name,"Byte")==0)
+ return Ext_Flash_GetScriptingTypeFromName("int");
+ }
+
+ std::string combined(namespaze);
+ if (combined.size() > 0)
+ combined+=".";
+ combined+=name;
+
+ return Ext_Flash_GetScriptingTypeFromName(combined.c_str());
+ }
+
+ virtual ScriptingTypePtr Provide(BackendNativeType nativePtr)
+ {
+ return (ScriptingTypePtr)nativePtr;
+ }
+
+ virtual void Release(ScriptingTypePtr t)
+ {
+ }
+};
+
+#endif
+
+#endif
diff --git a/Runtime/Scripting/Backend/IScriptingTypeProvider.h b/Runtime/Scripting/Backend/IScriptingTypeProvider.h
new file mode 100644
index 0000000..c8a9fe7
--- /dev/null
+++ b/Runtime/Scripting/Backend/IScriptingTypeProvider.h
@@ -0,0 +1,16 @@
+#ifndef _ISCRIPTINGTYPEPROVIDER_H_
+#define _ISCRIPTINGTYPEPROVIDER_H_
+
+#include "ScriptingTypes.h"
+#include "Runtime/Modules/ExportModules.h"
+
+class EXPORT_COREMODULE IScriptingTypeProvider
+{
+public:
+ virtual ~IScriptingTypeProvider() {}
+ virtual BackendNativeType NativeTypeFor(const char* namespaze, const char* name) = 0;
+ virtual ScriptingTypePtr Provide(BackendNativeType nativeType) = 0;
+ virtual void Release(ScriptingTypePtr t) = 0;
+};
+
+#endif
diff --git a/Runtime/Scripting/Backend/Mono/ScriptingBackendApi_Mono.cpp b/Runtime/Scripting/Backend/Mono/ScriptingBackendApi_Mono.cpp
new file mode 100644
index 0000000..b27269b
--- /dev/null
+++ b/Runtime/Scripting/Backend/Mono/ScriptingBackendApi_Mono.cpp
@@ -0,0 +1,280 @@
+#include "UnityPrefix.h"
+
+#include "../ScriptingTypes.h"
+#include "Runtime/Mono/MonoIncludes.h"
+#include "ScriptingBackendApi_Mono.h"
+#include "../ScriptingMethodRegistry.h"
+#include "../ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/ScriptingUtility.h" //required for ExtractMonoobjectData, todo: see if we can remove that.
+
+std::string scripting_cpp_string_for(ScriptingStringPtr str)
+{
+ return MonoStringToCpp(str);
+}
+
+bool scripting_method_is_instance(ScriptingMethodPtr method)
+{
+ return method->isInstance;
+}
+
+const char* scripting_method_get_name(ScriptingMethodPtr method)
+{
+ return mono_method_get_name(method->monoMethod);
+}
+
+int scripting_method_get_argument_count(ScriptingMethodPtr method, ScriptingTypeRegistry& typeRegistry)
+{
+ MonoMethodSignature* sig = mono_method_signature(method->monoMethod);
+ Assert(sig);
+ return mono_signature_get_param_count(sig);
+}
+
+ScriptingTypePtr scripting_method_get_returntype(ScriptingMethodPtr method, ScriptingTypeRegistry& registry)
+{
+ MonoMethodSignature* sig = mono_method_signature (method->monoMethod);
+ MonoType* returnType = mono_signature_get_return_type (sig);
+ if (returnType == NULL)
+ return NULL;
+
+ return mono_class_from_mono_type (returnType);
+}
+
+ScriptingTypePtr scripting_method_get_nth_argumenttype(ScriptingMethodPtr method, int index, ScriptingTypeRegistry& typeRegistry)
+{
+ MonoMethodSignature* sig = mono_method_signature (method->monoMethod);
+ void* iterator = NULL;
+ MonoType* type = mono_signature_get_params (sig, &iterator);
+ if (type == NULL)
+ return NULL;
+ MonoClass* methodClass = mono_class_from_mono_type (type);
+ return typeRegistry.GetType(methodClass);
+}
+
+bool scripting_method_has_attribute(ScriptingMethodPtr method, ScriptingClassPtr attribute)
+{
+ bool hasAttribute = false;
+ MonoCustomAttrInfo* attrInfo = mono_custom_attrs_from_method (method->monoMethod);
+ if (attrInfo != NULL && mono_custom_attrs_has_attr (attrInfo, attribute))
+ hasAttribute = true;
+
+ if (attrInfo)
+ mono_custom_attrs_free(attrInfo);
+
+ return hasAttribute;
+}
+
+ScriptingTypePtr scripting_class_get_parent(ScriptingTypePtr t, ScriptingTypeRegistry& registry)
+{
+ return mono_class_get_parent(t);
+}
+
+void scripting_class_get_methods(ScriptingTypePtr t, ScriptingMethodRegistry& registry, std::vector<ScriptingMethodPtr>& result)
+{
+ void* iterator = NULL;
+ ScriptingMethodPtr scriptingMethod = NULL;
+ while (MonoMethod* method = mono_class_get_methods(t, &iterator))
+ if ((scriptingMethod = registry.GetMethod (method)))
+ result.push_back(scriptingMethod);
+}
+
+ScriptingTypePtr scripting_class_from_systemtypeinstance(ScriptingObjectPtr systemTypeInstance, ScriptingTypeRegistry& typeRegistry)
+{
+ if (!systemTypeInstance)
+ return NULL;
+
+ MonoClass* klass = mono_class_from_mono_type(ExtractMonoObjectData<MonoType*>(systemTypeInstance));
+ return typeRegistry.GetType(klass);
+}
+
+const char* scripting_class_get_name(ScriptingClassPtr klass)
+{
+ return mono_class_get_name(klass);
+}
+
+const char* scripting_class_get_namespace(ScriptingClassPtr klass)
+{
+ return mono_class_get_namespace(klass);
+}
+
+bool scripting_class_is_subclass_of(ScriptingClassPtr c1, ScriptingClassPtr c2)
+{
+ return mono_class_is_subclass_of(c1,c2,true);
+}
+
+bool scripting_class_is_enum(ScriptingClassPtr klass)
+{
+ return mono_class_is_enum (klass);
+}
+
+ScriptingTypePtr scripting_object_get_class(ScriptingObjectPtr t, ScriptingTypeRegistry& registry)
+{
+ return mono_object_get_class(t);
+}
+
+void scripting_object_invoke_default_constructor(ScriptingObjectPtr t, ScriptingExceptionPtr* exc)
+{
+ mono_runtime_object_init_exception(t,exc);
+}
+
+ScriptingMethodPtr scripting_object_get_virtual_method(ScriptingObjectPtr o, ScriptingMethodPtr method, ScriptingMethodRegistry& methodRegistry)
+{
+ return methodRegistry.GetMethod(mono_object_get_virtual_method(o,method->monoMethod));
+}
+
+ScriptingObjectPtr scripting_object_new(ScriptingTypePtr t)
+{
+#if UNITY_EDITOR
+ if (mono_unity_class_is_abstract (t)) {
+ // Cannot instantiate abstract class
+ return SCRIPTING_NULL;
+ }
+#endif
+ return mono_object_new(mono_domain_get(), t);
+}
+
+int scripting_gchandle_new(ScriptingObjectPtr o)
+{
+ return mono_gchandle_new(o,1);
+}
+
+int scripting_gchandle_weak_new(ScriptingObjectPtr o)
+{
+ return mono_gchandle_new_weakref(o, 1);
+}
+
+void scripting_gchandle_free(int handle)
+{
+ mono_gchandle_free(handle);
+}
+
+ScriptingObjectPtr scripting_gchandle_get_target(int handle)
+{
+ return mono_gchandle_get_target(handle);
+}
+
+int scripting_gc_maxgeneration()
+{
+ return mono_gc_max_generation();
+}
+
+void scripting_gc_collect(int maxGeneration)
+{
+ mono_gc_collect(maxGeneration);
+}
+
+ScriptingObjectPtr scripting_method_invoke(ScriptingMethodPtr method, ScriptingObjectPtr object, ScriptingArguments& arguments, ScriptingExceptionPtr* exception)
+{
+#if UNITY_EDITOR
+ bool IsStackLargeEnough ();
+ if (!IsStackLargeEnough ())
+ {
+ *exception = mono_exception_from_name_msg (mono_get_corlib (), "System", "StackOverflowException", "");
+ return NULL;
+ }
+#endif
+
+ if (method->fastMonoMethod)
+ {
+ Assert(arguments.GetCount()==0);
+ Assert(object);
+ return method->fastMonoMethod(object,exception);
+ }
+
+ return mono_runtime_invoke(method->monoMethod, object, arguments.InMonoFormat(), exception);
+}
+
+ScriptingObjectPtr scripting_class_get_object(ScriptingClassPtr klass)
+{
+ return mono_class_get_object(klass);
+}
+
+ScriptingArrayPtr scripting_cast_object_to_array(ScriptingObjectPtr o)
+{
+ return (ScriptingArrayPtr)o;
+}
+
+ScriptingStringPtr scripting_string_new(const std::string& str)
+{
+ return scripting_string_new(str.c_str());
+}
+
+ScriptingStringPtr scripting_string_new(const UnityStr& str)
+{
+ return scripting_string_new(str.c_str());
+}
+
+ScriptingStringPtr scripting_string_new(const char* str)
+{
+ return MonoStringNew(str);
+}
+
+ScriptingStringPtr scripting_string_new(const wchar_t* str)
+{
+ return MonoStringNewUTF16(str);
+}
+
+ScriptingStringPtr scripting_string_new(const char* str, unsigned int length)
+{
+ return MonoStringNewLength(str, length);
+}
+
+void scripting_stack_trace_info_for(ScriptingExceptionPtr exception, StackTraceInfo& info)
+{
+ AssertIf (exception == NULL);
+
+ MonoException* tempException = NULL;
+ MonoString* monoStringMessage = NULL;
+ MonoString* monoStringTrace = NULL;
+ void* args[] = { exception, &monoStringMessage, &monoStringTrace };
+
+ if (GetMonoManagerPtr () && GetMonoManager ().GetCommonClasses ().extractStringFromException)
+ {
+ // Call mono_runtime_invoke directly to avoid our stack size check in mono_runtime_invoke_profiled.
+ // We *should* have enough stack here to make this call, and a stack trace would be useful for the user.
+ mono_runtime_invoke (GetMonoManager ().GetCommonClasses ().extractStringFromException->monoMethod, (MonoObject*)exception, args, &tempException);
+ }
+
+ if (tempException)
+ {
+ char const* exceptionClassName = mono_class_get_name(mono_object_get_class((MonoObject*)tempException));
+ ErrorString ("Couldn't extract exception string from exception (another exception of class '"
+ + std::string (exceptionClassName) + "' was thrown while processing the stack trace)");
+ return;
+ }
+
+ // Log returned string
+ string message;
+
+ char* extractedMessage = NULL;
+ if (monoStringMessage)
+ message = extractedMessage = mono_string_to_utf8 (monoStringMessage);
+
+ char* extractedTrace = NULL;
+ if (monoStringTrace)
+ extractedTrace = mono_string_to_utf8 (monoStringTrace);
+
+ string processedStackTrace;
+ int line = -1;
+ string path;
+
+ if (extractedTrace && *extractedTrace != 0)
+ {
+ PostprocessStacktrace(extractedTrace, processedStackTrace);
+ ExceptionToLineAndPath (processedStackTrace, line, path);
+ }
+
+ info.condition = message;
+ info.strippedStacktrace = processedStackTrace;
+ info.stacktrace = extractedTrace;
+ info.errorNum = 0;
+ info.file = path;
+ info.line = line;
+
+ g_free (extractedMessage);
+ g_free (extractedTrace);
+}
+
+void* scripting_array_element_ptr(ScriptingArrayPtr array, int i, size_t element_size)
+{
+ return kMonoArrayOffset + i * element_size + (char*)array;
+} \ No newline at end of file
diff --git a/Runtime/Scripting/Backend/Mono/ScriptingBackendApi_Mono.h b/Runtime/Scripting/Backend/Mono/ScriptingBackendApi_Mono.h
new file mode 100644
index 0000000..e6ae12a
--- /dev/null
+++ b/Runtime/Scripting/Backend/Mono/ScriptingBackendApi_Mono.h
@@ -0,0 +1,15 @@
+#ifndef _SCRIPTINGBACKENDAPI_MONO_H_
+#define _SCRIPTINGBACKENDAPI_MONO_H_
+
+#include "../ScriptingTypes.h"
+
+typedef MonoObject* (*FastMonoMethod) (void* thiz, MonoException** ex);
+
+struct ScriptingMethod
+{
+ MonoMethod* monoMethod;
+ FastMonoMethod fastMonoMethod;
+ bool isInstance;
+};
+
+#endif
diff --git a/Runtime/Scripting/Backend/Mono/ScriptingMethodFactory_Mono.h b/Runtime/Scripting/Backend/Mono/ScriptingMethodFactory_Mono.h
new file mode 100644
index 0000000..a7519f8
--- /dev/null
+++ b/Runtime/Scripting/Backend/Mono/ScriptingMethodFactory_Mono.h
@@ -0,0 +1,83 @@
+#ifndef _SCRIPTINGMETHODFACTORY_MONO_
+#define _SCRIPTINGMETHODFACTORY_MONO_
+
+#include "../ScriptingMethodFactory.h"
+#include "Runtime/Mono/MonoIncludes.h"
+#include "../ScriptingMethodRegistry.h"
+#if ENABLE_MONO
+
+
+// Flag defined in mono, when AOT libraries are built with -ficall option
+// But that is not available in mono/consoles
+extern "C" int mono_ficall_flag;
+
+FastMonoMethod FastMonoMethodPtrFor(MonoMethod* method)
+{
+#if USE_MONO_AOT && !(UNITY_XENON || UNITY_PS3)
+ return mono_ficall_flag && method ? (FastMonoMethod) mono_aot_get_method(mono_domain_get(), method) : NULL;
+#else
+ return NULL;
+#endif
+}
+
+static bool MethodMatchesSearchFilter(MonoMethod* method, int searchFilter)
+{
+ MonoMethodSignature* sig = mono_method_signature(method);
+ return MethodDescriptionMatchesSearchFilter(searchFilter, mono_signature_is_instance(sig), mono_signature_get_param_count(sig));
+}
+
+class ScriptingMethodFactory_Mono : public IScriptingMethodFactory
+{
+public:
+ virtual ScriptingMethodPtr Produce(ScriptingTypePtr klass, const char* name, int searchFilter)
+ {
+ void* iterator = NULL;
+ while (MonoMethod* method = mono_class_get_methods(klass, &iterator))
+ {
+ if (!method)
+ return NULL;
+
+ if (strcmp(mono_method_get_name(method), name)!=0)
+ continue;
+
+ if (MethodMatchesSearchFilter(method,searchFilter))
+ return Produce(method);
+ }
+ return NULL;
+ }
+
+ virtual ScriptingMethodPtr Produce(BackendNativeMethod nativeMethod)
+ {
+ ScriptingMethodPtr result = new ScriptingMethod();
+ result->monoMethod = nativeMethod;
+ MonoMethodSignature* sig = mono_method_signature(nativeMethod);
+#if UNITY_EDITOR
+ if (!sig) {
+ // Loader error - usually missing reference
+ Scripting::LogException(mono_loader_error_prepare_exception (mono_loader_get_last_error ()), 0);
+ return NULL;
+ }
+#endif
+ result->isInstance = mono_signature_is_instance(sig);
+ result->fastMonoMethod = IsSignatureSupportedForFastAotCalls(sig) ? FastMonoMethodPtrFor(nativeMethod) : NULL;
+ return result;
+ }
+
+ virtual void Release(ScriptingMethodPtr method)
+ {
+ delete method;
+ }
+
+private:
+ bool IsSignatureSupportedForFastAotCalls(MonoMethodSignature* sig)
+ {
+ if (!mono_signature_is_instance(sig))
+ return false;
+
+ return mono_signature_get_param_count(sig) == 0;
+ }
+};
+
+#endif
+
+#endif
diff --git a/Runtime/Scripting/Backend/ScriptingArguments.cpp b/Runtime/Scripting/Backend/ScriptingArguments.cpp
new file mode 100644
index 0000000..9fcbaad
--- /dev/null
+++ b/Runtime/Scripting/Backend/ScriptingArguments.cpp
@@ -0,0 +1,218 @@
+#include "UnityPrefix.h"
+#include "ScriptingTypes.h"
+
+#if ENABLE_SCRIPTING
+
+#include "ScriptingArguments.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+ScriptingArguments::ScriptingArguments()
+ : m_Count(0)
+{
+#if UNITY_WINRT
+ m_StringArguments = ref new Platform::Array<Platform::String^>(MAXARGS);
+#else
+ memset(m_Arguments, 0, sizeof(m_Arguments));
+#endif
+ memset(&m_PrimitiveStorage, 0, sizeof(m_PrimitiveStorage));
+ memset(m_ArgumentTypes, 0, sizeof(m_ArgumentTypes));
+}
+
+void ScriptingArguments::AddBoolean(bool value)
+{
+ m_PrimitiveStorage.ints[m_Count] = value ? 1 : 0;
+#if UNITY_WINRT
+ m_Arguments[m_Count] = m_PrimitiveStorage.ints[m_Count];
+#else
+ m_Arguments[m_Count] = &m_PrimitiveStorage.ints[m_Count];
+#endif
+ m_ArgumentTypes[m_Count] = ARGTYPE_BOOLEAN;
+ m_Count++;
+}
+
+void ScriptingArguments::AddInt(int value)
+{
+ m_PrimitiveStorage.ints[m_Count] = value;
+#if UNITY_WINRT
+ m_Arguments[m_Count] = *(long long*)&value;
+#else
+ m_Arguments[m_Count] = &m_PrimitiveStorage.ints[m_Count];
+#endif
+ m_ArgumentTypes[m_Count] = ARGTYPE_INT;
+ m_Count++;
+}
+
+void ScriptingArguments::AddFloat(float value)
+{
+ m_PrimitiveStorage.floats[m_Count] = value;
+#if UNITY_WINRT
+ m_Arguments[m_Count] = *(long long*)&value;
+#else
+ m_Arguments[m_Count] = &m_PrimitiveStorage.floats[m_Count];
+#endif
+ m_ArgumentTypes[m_Count] = ARGTYPE_FLOAT;
+ m_Count++;
+}
+
+void ScriptingArguments::AddString(const char* str)
+{
+#if ENABLE_MONO
+ m_Arguments[m_Count] = MonoStringNew(str);
+#elif UNITY_FLASH
+ m_Arguments[m_Count] = str;
+#elif UNITY_WINRT
+ m_StringArguments[m_Count] = ConvertUtf8ToString(str);
+ m_Arguments[m_Count] = (long long)m_StringArguments[m_Count]->Data();
+#endif
+ m_ArgumentTypes[m_Count] = ARGTYPE_STRING;
+ m_Count++;
+}
+
+void ScriptingArguments::AddString(std::string& str)
+{
+ AddString(str.c_str());
+}
+
+void ScriptingArguments::AddObject(ScriptingObjectPtr scriptingObject)
+{
+#if UNITY_WINRT
+ m_Arguments[m_Count] = scriptingObject.GetHandle();
+#else
+ m_Arguments[m_Count] = scriptingObject;
+#endif
+ m_ArgumentTypes[m_Count] = ARGTYPE_OBJECT;
+ m_Count++;
+}
+
+void ScriptingArguments::AddStruct(void* pointerToStruct)
+{
+#if UNITY_WINRT
+ // We need to pass struct size, and ScriptingType here, to set the struct for metro
+ FatalErrorMsg("ToDo");
+#else
+ m_Arguments[m_Count] = pointerToStruct;
+#endif
+ m_ArgumentTypes[m_Count] = ARGTYPE_STRUCT;
+ m_Count++;
+}
+
+void ScriptingArguments::AddArray(ScriptingArrayPtr arr)
+{
+#if UNITY_WINRT
+ m_Arguments[m_Count] = arr.GetHandle();
+#elif UNITY_FLASH
+ FatalErrorMsg("ToDo");
+#else
+ m_Arguments[m_Count] = arr;
+#endif
+ m_ArgumentTypes[m_Count] = ARGTYPE_ARRAY;
+ m_Count++;
+}
+
+void ScriptingArguments::AddEnum(int value)
+{
+ AddInt(value);
+ m_ArgumentTypes[m_Count-1] = ARGTYPE_ENUM;
+}
+
+bool ScriptingArguments::GetBooleanAt(int index)
+{
+ return m_PrimitiveStorage.ints[index] == 1;
+}
+
+int ScriptingArguments::GetIntAt(int index)
+{
+ return m_PrimitiveStorage.ints[index];
+}
+
+float ScriptingArguments::GetFloatAt(int index)
+{
+ return m_PrimitiveStorage.floats[index];
+}
+
+const void* ScriptingArguments::GetStringAt(int index)
+{
+#if UNITY_WINRT
+ return m_StringArguments[index]->Data();
+#else
+ return m_Arguments[index];
+#endif
+}
+
+ScriptingObjectPtr ScriptingArguments::GetObjectAt(int index)
+{
+#if UNITY_WINRT
+ return ScriptingObjectPtr(safe_cast<long long>(m_Arguments[index]));
+#else
+ return (ScriptingObjectPtr) m_Arguments[index];
+#endif
+}
+
+void** ScriptingArguments::InMonoFormat()
+{
+ return (void**) &m_Arguments[0];
+}
+
+int ScriptingArguments::GetTypeAt(int index)
+{
+ return m_ArgumentTypes[index];
+}
+
+int ScriptingArguments::GetCount()
+{
+ return m_Count;
+}
+
+void ScriptingArguments::AdjustArgumentsToMatchMethod(ScriptingMethodPtr method)
+{
+#if ENABLE_MONO
+ MonoMethodSignature* sig = mono_method_signature (method->monoMethod);
+ int methodCount = mono_signature_get_param_count (sig);
+ if (methodCount < m_Count)
+ m_Count = methodCount;
+#endif
+}
+
+bool ScriptingArguments::CheckArgumentsAgainstMethod(ScriptingMethodPtr method)
+{
+#if !ENABLE_MONO
+ return true;
+#else
+
+ MonoMethodSignature* sig = mono_method_signature (method->monoMethod);
+ int argCount = mono_signature_get_param_count (sig);
+ if (argCount != GetCount())
+ return false;
+
+ void* iterator = NULL;
+ int argIndex = -1;
+ while(true)
+ {
+ argIndex++;
+ MonoType* methodType = mono_signature_get_params (sig, &iterator);
+ if (methodType == NULL)
+ return true;
+
+ if (GetTypeAt(argIndex) != ScriptingArguments::ARGTYPE_OBJECT)
+ continue;
+
+ MonoClass* invokingArgument = mono_object_get_class(GetObjectAt(argIndex));
+ MonoClass* receivingArgument = mono_class_from_mono_type (methodType);
+
+ if (!mono_class_is_subclass_of (invokingArgument, receivingArgument, false))
+ return false;
+ }
+ return true;
+#endif
+}
+
+
+#if UNITY_WINRT
+ScriptingParamsPtr ScriptingArguments::InMetroFormat()
+{
+ if (m_Count <= 0) return SCRIPTING_NULL;
+ return &m_Arguments[0];
+}
+#endif
+
+#endif
diff --git a/Runtime/Scripting/Backend/ScriptingArguments.h b/Runtime/Scripting/Backend/ScriptingArguments.h
new file mode 100644
index 0000000..9ea823f
--- /dev/null
+++ b/Runtime/Scripting/Backend/ScriptingArguments.h
@@ -0,0 +1,79 @@
+#ifndef _SCRIPTINGARGUMENTS_H_
+#define _SCRIPTINGARGUMENTS_H_
+
+#if ENABLE_SCRIPTING
+#include "ScriptingTypes.h"
+#include <string>
+#include "Runtime/Modules/ExportModules.h"
+
+
+struct EXPORT_COREMODULE ScriptingArguments
+{
+ enum Constants
+ {
+ MAXARGS=10
+ };
+
+ enum ArgType
+ {
+ ARGTYPE_BOOLEAN,
+ ARGTYPE_INT,
+ ARGTYPE_FLOAT,
+ ARGTYPE_STRING,
+ ARGTYPE_OBJECT,
+ ARGTYPE_STRUCT,
+ ARGTYPE_ARRAY,
+ ARGTYPE_ENUM
+ };
+
+ //this setup is kind of weird. some types of arguments just need to be stuffed in m_Arguments,
+ //however for ints and floats, instead of stuffing them in, mono actually excepts a pointer to one.
+ //to make it happy, we store the actual int in a field, and store a pointer to it in the ScriptingParam.
+ union
+ {
+ int ints[MAXARGS];
+ float floats[MAXARGS];
+ } m_PrimitiveStorage;
+
+#if UNITY_WINRT
+ Platform::Array<Platform::String^>^ m_StringArguments;
+ ScriptingParams m_Arguments[MAXARGS];
+#else
+ const void* m_Arguments[MAXARGS];
+#endif
+ int m_ArgumentTypes[MAXARGS];
+ int m_Count;
+
+ ScriptingArguments();
+
+ void AddBoolean(bool value);
+ void AddInt(int value);
+ void AddFloat(float value);
+ void AddString(const char* str);
+ void AddString(std::string& str);
+ void AddObject(ScriptingObjectPtr scriptingObject);
+ void AddStruct(void* pointerToStruct);
+ void AddEnum(int value);
+ void AddArray(ScriptingArrayPtr arr);
+
+ bool GetBooleanAt(int index);
+ int GetIntAt(int index);
+ float GetFloatAt(int index);
+ const void* GetStringAt(int index);
+ ScriptingObjectPtr GetObjectAt(int index);
+
+ void** InMonoFormat();
+
+ void AdjustArgumentsToMatchMethod(ScriptingMethodPtr method);
+ bool CheckArgumentsAgainstMethod(ScriptingMethodPtr method);
+#if UNITY_WINRT
+ ScriptingParamsPtr InMetroFormat();
+#endif
+
+ int GetTypeAt(int index);
+ int GetCount();
+};
+
+#endif
+
+#endif
diff --git a/Runtime/Scripting/Backend/ScriptingBackendApi.h b/Runtime/Scripting/Backend/ScriptingBackendApi.h
new file mode 100644
index 0000000..1e729b7
--- /dev/null
+++ b/Runtime/Scripting/Backend/ScriptingBackendApi.h
@@ -0,0 +1,72 @@
+#ifndef _SCRIPTINGBACKEND_API_H_
+#define _SCRIPTINGBACKEND_API_H_
+
+#include <string>
+
+#include "ScriptingTypes.h"
+class ScriptingMethodRegistry;
+class ScriptingTypeRegistry;
+struct ScriptingArguments;
+
+struct StackTraceInfo
+{
+ std::string condition;
+ std::string strippedStacktrace;
+ std::string stacktrace;
+ int errorNum;
+ std::string file;
+ int line;
+};
+
+bool scripting_method_is_instance(ScriptingMethodPtr method);
+const char* scripting_method_get_name(ScriptingMethodPtr method);
+int scripting_method_get_argument_count(ScriptingMethodPtr method, ScriptingTypeRegistry& typeRegistry);
+ScriptingTypePtr scripting_method_get_returntype(ScriptingMethodPtr method, ScriptingTypeRegistry& typeRegistry);
+bool scripting_method_has_attribute(ScriptingMethodPtr method, ScriptingClassPtr attribute);
+ScriptingTypePtr scripting_method_get_nth_argumenttype(ScriptingMethodPtr method, int index, ScriptingTypeRegistry& typeRegistry);
+ScriptingObjectPtr scripting_method_invoke(ScriptingMethodPtr method, ScriptingObjectPtr object, ScriptingArguments& arguments, ScriptingExceptionPtr* exception);
+
+ScriptingTypePtr scripting_class_get_parent(ScriptingTypePtr t, ScriptingTypeRegistry& typeRegistry);
+void scripting_class_get_methods(ScriptingTypePtr t, ScriptingMethodRegistry& registry, std::vector<ScriptingMethodPtr>& result);
+const char* scripting_class_get_name(ScriptingTypePtr t);
+const char* scripting_class_get_namespace(ScriptingTypePtr t);
+bool scripting_class_is_subclass_of(ScriptingTypePtr t1, ScriptingTypePtr t2);
+bool scripting_class_is_enum(ScriptingTypePtr t);
+ScriptingObjectPtr scripting_object_new(ScriptingTypePtr t);
+void scripting_object_invoke_default_constructor(ScriptingObjectPtr o, ScriptingExceptionPtr* exc);
+ScriptingTypePtr scripting_object_get_class(ScriptingObjectPtr t, ScriptingTypeRegistry& typeRegistry);
+ScriptingMethodPtr scripting_object_get_virtual_method(ScriptingObjectPtr o, ScriptingMethodPtr method, ScriptingMethodRegistry& methodRegistry);
+
+ScriptingTypePtr scripting_class_from_systemtypeinstance(ScriptingObjectPtr systemTypeInstance, ScriptingTypeRegistry& typeRegistry);
+
+EXPORT_COREMODULE int scripting_gchandle_new(ScriptingObjectPtr o);
+EXPORT_COREMODULE int scripting_gchandle_weak_new(ScriptingObjectPtr o);
+EXPORT_COREMODULE void scripting_gchandle_free(int handle);
+EXPORT_COREMODULE ScriptingObjectPtr scripting_gchandle_get_target(int handle);
+
+int scripting_gc_maxgeneration();
+void scripting_gc_collect(int maxGeneration);
+
+ScriptingObjectPtr scripting_class_get_object(ScriptingClassPtr klass);
+ScriptingArrayPtr scripting_cast_object_to_array(ScriptingObjectPtr o);
+
+ScriptingStringPtr scripting_string_new(const char* str);
+ScriptingStringPtr scripting_string_new(const std::string& str);
+ScriptingStringPtr scripting_string_new(const wchar_t* str);
+ScriptingStringPtr scripting_string_new(const char* str, unsigned int length);
+
+void scripting_stack_trace_info_for(ScriptingExceptionPtr exception, StackTraceInfo& info);
+EXPORT_COREMODULE std::string scripting_cpp_string_for(ScriptingStringPtr ptr);
+
+void* scripting_array_element_ptr(ScriptingArrayPtr array, int i, size_t element_size);
+
+// TODO: temporary, these includes are going to disappear very soon
+
+#if UNITY_FLASH
+#include "Runtime/Scripting/Backend/Flash/ScriptingBackendApi_Flash.h"
+#elif UNITY_WINRT
+#elif ENABLE_MONO
+#include "Runtime/Scripting/Backend/Mono/ScriptingBackendApi_Mono.h"
+#endif
+
+#endif
diff --git a/Runtime/Scripting/Backend/ScriptingInvocation.cpp b/Runtime/Scripting/Backend/ScriptingInvocation.cpp
new file mode 100644
index 0000000..71ead05
--- /dev/null
+++ b/Runtime/Scripting/Backend/ScriptingInvocation.cpp
@@ -0,0 +1,223 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_SCRIPTING
+
+#include "ScriptingInvocation.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "ScriptingArguments.h"
+#include "ScriptingMethodRegistry.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+
+#if ENABLE_MONO
+#include "Runtime/Mono/MonoIncludes.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Mono/MonoUtility.h"
+#include "Runtime/Mono/MonoScript.h"
+#endif
+
+ScriptingInvocation::ScriptingInvocation()
+{
+}
+
+ScriptingInvocation::ScriptingInvocation(ScriptingMethodPtr in_method)
+ : ScriptingInvocationNoArgs(in_method)
+{
+}
+
+#if ENABLE_MONO || UNITY_WINRT
+ScriptingInvocation::ScriptingInvocation(const char* namespaze, const char* klassName, const char* methodName)
+{
+ method = GetScriptingMethodRegistry().GetMethod(namespaze, klassName, methodName);
+}
+
+ScriptingInvocation::ScriptingInvocation(ScriptingClassPtr klass, const char* methodName)
+{
+ method = GetScriptingMethodRegistry().GetMethod(klass, methodName);
+}
+
+ScriptingInvocation::ScriptingInvocation(BackendNativeMethod monoMethod)
+{
+ method = GetScriptingMethodRegistry().GetMethod(monoMethod);
+}
+#endif
+
+bool ScriptingInvocation::Check()
+{
+#if !ENABLE_MONO
+ return true;
+#else
+ return ScriptingInvocationNoArgs::Check() && arguments.CheckArgumentsAgainstMethod(method);
+#endif
+}
+
+template<class T>
+T ScriptingInvocation::Invoke()
+{
+ ScriptingExceptionPtr ex = NULL;
+ return Invoke<T>(&ex);
+}
+
+template<>
+ScriptingObjectPtr ScriptingInvocation::Invoke<ScriptingObjectPtr>()
+{
+ return Invoke();
+}
+
+template<>
+bool ScriptingInvocation::Invoke<bool>(ScriptingExceptionPtr* exception)
+{
+ ScriptingObjectPtr o = Invoke(exception);
+ if (*exception != NULL)
+ return false;
+
+ #if ENABLE_MONO
+ if (method->fastMonoMethod)
+ return (bool)o;
+ else
+ return ExtractMonoObjectData<char>(o);
+ #elif UNITY_FLASH
+ bool boolResult;
+ __asm __volatile__("%0 = marshallmap.getObjectWithId(%1);" : "=r"(boolResult) : "r"(o));
+ return boolResult;
+ #elif UNITY_WINRT
+ return o != SCRIPTING_NULL ? o.ToBool() : false;
+ #endif
+}
+
+ScriptingObjectPtr ScriptingInvocation::Invoke()
+{
+ ScriptingExceptionPtr ex = NULL;
+ return Invoke(&ex);
+}
+
+ScriptingObjectPtr ScriptingInvocation::Invoke(ScriptingExceptionPtr* exception)
+{
+ return Invoke(exception, false);
+}
+
+ScriptingObjectPtr ScriptingInvocation::Invoke(ScriptingExceptionPtr* exception, bool convertArguments)
+{
+ ScriptingObjectPtr returnValue;
+
+ *exception = NULL;
+
+#if ENABLE_MONO || UNITY_FLASH || UNITY_WINRT
+ MONO_PROFILER_BEGIN (method, classContextForProfiler, object)
+#if UNITY_WINRT
+ ScriptingObjectPtr metro_invoke_method(ScriptingMethodPtr method, ScriptingObjectPtr object, ScriptingArguments* arguments, ScriptingExceptionPtr* exception, bool convertArgs);
+ returnValue = metro_invoke_method(method, object, &arguments, exception, convertArguments);
+#else
+ returnValue = scripting_method_invoke(method, object, arguments, exception);
+#endif
+ MONO_PROFILER_END
+#elif !UNITY_EXTERNAL_TOOL
+ ErrorString("Invoke() not implemented on this platform");
+#else
+ return NULL;
+#endif
+
+ if (! *exception) return returnValue;
+
+ this->exception = *exception;
+#if !UNITY_EXTERNAL_TOOL
+ if (logException)
+ Scripting::LogException(*exception, objectInstanceIDContextForException );
+#endif
+
+ return SCRIPTING_NULL;
+}
+
+void ScriptingInvocation::AdjustArgumentsToMatchMethod()
+{
+ arguments.AdjustArgumentsToMatchMethod(method);
+}
+
+
+#if ENABLE_MONO
+
+MonoObject* CallStaticMonoMethod (MonoClass* klass , const char* methodName, void** parameters)
+{
+ MonoException* exception = NULL;
+ return CallStaticMonoMethod(klass, methodName, parameters, &exception);
+}
+
+
+static MonoObject* CallStaticMonoMethod (MonoMethod* method, void** parameters, MonoException** exception)
+{
+ MonoObject* returnValue = mono_runtime_invoke_profiled (method, NULL, parameters, exception);
+ if (! *exception) return returnValue;
+
+ Scripting::LogException(*exception, 0);
+ return NULL;
+}
+
+
+static MonoObject* CallStaticMonoMethod (MonoMethod* method, void** parameters)
+{
+ MonoException* exception = NULL;
+ return CallStaticMonoMethod(method, parameters, &exception);
+}
+
+MonoObject* CallStaticMonoMethod (const char* className, const char* methodName, void** parameters)
+{
+ MonoException* exception = NULL;
+ return CallStaticMonoMethod(className, methodName, parameters, &exception);
+}
+
+MonoObject* CallStaticMonoMethod (MonoClass* klass , const char* methodName, void** parameters, MonoException** exception)
+{
+ MonoMethod* method = mono_class_get_method_from_name (klass, methodName, -1);
+
+ if (!method)
+ {
+ ErrorString (Format ("Couldn't call method %s in class %s because it wasn't found.", methodName, mono_class_get_name(klass)));
+ return NULL;
+ }
+
+ return CallStaticMonoMethod(method, parameters, exception);
+}
+
+MonoObject* CallStaticMonoMethod (const char* className, const char* methodName, void** parameters, MonoException** exception)
+{
+ MonoMethod* m = FindStaticMonoMethod(className, methodName);
+ if (!m)
+ {
+ ErrorString (Format ("Couldn't call method %s because the class %s couldn't be found.", methodName, className));
+ return NULL;
+ }
+
+ return CallStaticMonoMethod(m, parameters, exception);
+}
+
+MonoObject* CallStaticMonoMethod (const char* className, const char* nameSpace, const char* methodName, void** parameters)
+{
+ MonoException* exception = NULL;
+ MonoMethod* m = FindStaticMonoMethod(nameSpace, className, methodName);
+ if (!m)
+ {
+ ErrorString (Format ("Couldn't call method %s because the class %s couldn't be found.", methodName, className));
+ return NULL;
+ }
+
+ return CallStaticMonoMethod(m, parameters, &exception);
+}
+
+MonoObject* CallStaticMonoMethod (const char* className, const char* nameSpace, const char* methodName, void** parameters, MonoException** exception)
+{
+ MonoMethod* m = FindStaticMonoMethod(nameSpace, className, methodName);
+ if (!m)
+ {
+ ErrorString (Format ("Couldn't call method %s because the class %s couldn't be found.", methodName, className));
+ return NULL;
+ }
+
+ return CallStaticMonoMethod(m, parameters, exception);
+}
+
+
+#endif
+
+#endif
diff --git a/Runtime/Scripting/Backend/ScriptingInvocation.h b/Runtime/Scripting/Backend/ScriptingInvocation.h
new file mode 100644
index 0000000..2bad02c
--- /dev/null
+++ b/Runtime/Scripting/Backend/ScriptingInvocation.h
@@ -0,0 +1,73 @@
+#ifndef _SCRIPTINGINVOCATION_H_
+#define _SCRIPTINGINVOCATION_H_
+
+#if ENABLE_SCRIPTING
+
+#include "ScriptingInvocationNoArgs.h"
+#include "ScriptingArguments.h"
+
+
+/// ScriptingInvocation invocation (scriptingMethod); // Slow alternative (Don't use this in runtime code) ScriptingInvocation invocation ("MyNameSpace", "MyClassName", "MyMethod");
+/// invocation.AddInt(5);
+/// invocation.Invoke (); // < you are certain that the bound method accepts those exact parameters
+/// invocation.InvokeChecked(); // < you cant guarantee that the bound method has the passed parameters
+
+/// By default logs the exception (Can be turned off via invocation.logException = false;)
+/// Parameters are setup AddInt, AddFloat, AddStruct functions below
+class ScriptingInvocation : public ScriptingInvocationNoArgs
+{
+public:
+ ScriptingInvocation();
+ ScriptingInvocation(ScriptingMethodPtr in_method);
+#if ENABLE_MONO || UNITY_WINRT
+ ScriptingInvocation(const char* namespaze, const char* klass, const char* name);
+ ScriptingInvocation(ScriptingClassPtr klass, const char* name);
+ ScriptingInvocation(BackendNativeMethod method);
+#endif
+
+ //convenience forwarders to arguments
+ void AddBoolean(bool value) { arguments.AddBoolean(value); }
+ void AddInt(int value) { arguments.AddInt(value); }
+ void AddFloat(float value) { arguments.AddFloat(value); }
+ void AddString(const char* str) { arguments.AddString(str); }
+ void AddString(std::string& str) { arguments.AddString(str); }
+ void AddObject(ScriptingObjectPtr scriptingObject) { arguments.AddObject(scriptingObject); }
+ void AddStruct(void* pointerToStruct) { arguments.AddStruct(pointerToStruct); }
+ void AddEnum(int value) { arguments.AddEnum(value); }
+ void AddArray(ScriptingArrayPtr arr) { arguments.AddArray(arr); }
+
+ void AdjustArgumentsToMatchMethod();
+
+ template<class T> T Invoke();
+ template<class T> T Invoke(ScriptingException**);
+ ScriptingObjectPtr Invoke();
+ ScriptingObjectPtr Invoke(ScriptingException**);
+ ScriptingObjectPtr Invoke(ScriptingExceptionPtr*, bool);
+
+ ScriptingArguments& Arguments() { return arguments; }
+protected:
+ ScriptingArguments arguments;
+ virtual bool Check();
+};
+
+
+#if ENABLE_MONO
+
+struct MonoObject;
+struct MonoException;
+class Object;
+struct MonoMethod;
+struct MonoClass;
+class MonoScript;
+
+MonoObject* CallStaticMonoMethod (const char* className, const char* methodName, void** parameters = NULL);
+MonoObject* CallStaticMonoMethod (const char* className, const char* methodName, void** parameters, MonoException** exception);
+MonoObject* CallStaticMonoMethod (const char* className, const char* nameSpace, const char* methodName, void** paramenters = NULL);
+MonoObject* CallStaticMonoMethod (const char* className, const char* nameSpace, const char* methodName, void** paramenters, MonoException** exception);
+MonoObject* CallStaticMonoMethod (MonoClass* klass , const char* methodName, void** parameters = NULL);
+MonoObject* CallStaticMonoMethod (MonoClass* klass , const char* methodName, void** parameters, MonoException** exception);
+#endif
+
+#endif
+
+#endif
diff --git a/Runtime/Scripting/Backend/ScriptingInvocationNoArgs.cpp b/Runtime/Scripting/Backend/ScriptingInvocationNoArgs.cpp
new file mode 100644
index 0000000..19ab0f0
--- /dev/null
+++ b/Runtime/Scripting/Backend/ScriptingInvocationNoArgs.cpp
@@ -0,0 +1,123 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_SCRIPTING
+
+#include "ScriptingInvocationNoArgs.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "ScriptingMethodRegistry.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Profiler/Profiler.h"
+
+#if ENABLE_MONO
+#include "Runtime/Mono/MonoIncludes.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Mono/MonoUtility.h"
+#include "Runtime/Mono/MonoScript.h"
+#endif
+
+ScriptingInvocationNoArgs::ScriptingInvocationNoArgs()
+{
+ SetDefaults();
+}
+
+ScriptingInvocationNoArgs::ScriptingInvocationNoArgs(ScriptingMethodPtr in_method)
+{
+ SetDefaults();
+ method = in_method;
+}
+
+void ScriptingInvocationNoArgs::SetDefaults()
+{
+ object = SCRIPTING_NULL;
+ method = SCRIPTING_NULL;
+ classContextForProfiler = NULL;
+ logException = true;
+ objectInstanceIDContextForException = 0;
+ exception = SCRIPTING_NULL;
+}
+
+bool ScriptingInvocationNoArgs::Check()
+{
+#if !ENABLE_MONO
+ return true;
+#else
+
+ // Check method
+ if (method == NULL)
+ {
+ ErrorString("Failed to call function because it was null");
+ return false;
+ }
+
+ bool methodIsInstance = mono_signature_is_instance (mono_method_signature(method->monoMethod));
+ bool invokingInstance = object != NULL;
+
+ if (methodIsInstance && !invokingInstance)
+ {
+ DebugStringToFile (Format("Failed to call instance function %s because the no object was provided", mono_method_get_name (method->monoMethod)), 0, __FILE_STRIPPED__, __LINE__,
+ kError, objectInstanceIDContextForException);
+ return false;
+ }
+
+ if (!methodIsInstance && invokingInstance)
+ {
+ DebugStringToFile (Format("Failed to call static function %s because an object was provided", mono_method_get_name (method->monoMethod)), 0, __FILE_STRIPPED__, __LINE__,
+ kError, objectInstanceIDContextForException);
+ return false;
+ }
+
+ return true;
+
+#endif
+}
+
+
+ScriptingObjectPtr ScriptingInvocationNoArgs::Invoke()
+{
+ ScriptingExceptionPtr ex = NULL;
+ return Invoke(&ex);
+}
+
+ScriptingObjectPtr ScriptingInvocationNoArgs::Invoke(ScriptingExceptionPtr* exception)
+{
+ ScriptingObjectPtr returnValue;
+
+ *exception = NULL;
+
+#if ENABLE_MONO || UNITY_FLASH || UNITY_WINRT
+ ScriptingArguments arguments;
+ MONO_PROFILER_BEGIN (method, classContextForProfiler, object)
+#if UNITY_WINRT
+ ScriptingObjectPtr metro_invoke_method(ScriptingMethodPtr method, ScriptingObjectPtr object, ScriptingArguments* arguments, ScriptingExceptionPtr* exception, bool convertArgs);
+ returnValue = metro_invoke_method(method, object, NULL, exception, false);
+#else
+ returnValue = scripting_method_invoke(method, object, arguments, exception);
+#endif
+ MONO_PROFILER_END
+#elif !UNITY_EXTERNAL_TOOL
+ ErrorString("Invoke() not implemented on this platform");
+#else
+ return NULL;
+#endif
+
+ if (! *exception) return returnValue;
+
+ this->exception = *exception;
+#if !UNITY_EXTERNAL_TOOL
+ if (logException)
+ Scripting::LogException(*exception, objectInstanceIDContextForException);
+#endif
+
+ return SCRIPTING_NULL;
+}
+
+ScriptingObjectPtr ScriptingInvocationNoArgs::InvokeChecked()
+{
+ if (!Check())
+ return SCRIPTING_NULL;
+
+ return Invoke();
+}
+
+#endif
diff --git a/Runtime/Scripting/Backend/ScriptingInvocationNoArgs.h b/Runtime/Scripting/Backend/ScriptingInvocationNoArgs.h
new file mode 100644
index 0000000..ece7010
--- /dev/null
+++ b/Runtime/Scripting/Backend/ScriptingInvocationNoArgs.h
@@ -0,0 +1,31 @@
+#ifndef _SCRIPTINGINVOCATIONNOARGS_H_
+#define _SCRIPTINGINVOCATIONNOARGS_H_
+
+#if ENABLE_SCRIPTING
+
+#include "ScriptingTypes.h"
+
+class ScriptingInvocationNoArgs
+{
+public:
+ ScriptingInvocationNoArgs();
+ ScriptingInvocationNoArgs(ScriptingMethodPtr in_method);
+
+ ScriptingMethodPtr method;
+ ScriptingObjectPtr object;
+ int objectInstanceIDContextForException;
+ ScriptingTypePtr classContextForProfiler;
+ bool logException;
+ ScriptingExceptionPtr exception;
+
+ ScriptingObjectPtr Invoke();
+ virtual ScriptingObjectPtr Invoke(ScriptingException**);
+ ScriptingObjectPtr InvokeChecked();
+protected:
+ void SetDefaults();
+ virtual bool Check();
+};
+
+#endif
+
+#endif
diff --git a/Runtime/Scripting/Backend/ScriptingMethodFactory.h b/Runtime/Scripting/Backend/ScriptingMethodFactory.h
new file mode 100644
index 0000000..a497d62
--- /dev/null
+++ b/Runtime/Scripting/Backend/ScriptingMethodFactory.h
@@ -0,0 +1,18 @@
+#ifndef _SCRIPTINGMETHODFACTORY_H_
+#define _SCRIPTINGMETHODFACTORY_H_
+
+#include "ScriptingTypes.h"
+
+
+// !!TODO: ScriptingMethodPtr Produce(ScriptingTypePtr klass, const char* name, int searchFilter) & ScriptingMethodPtr Produce(BackendNativeMethod nativeMethod) must return the same
+// method if internally method is the same !!!
+class IScriptingMethodFactory
+{
+public:
+ virtual ~IScriptingMethodFactory() {}
+ virtual ScriptingMethodPtr Produce(ScriptingTypePtr klass, const char* name, int searchFilter) = 0;
+ virtual ScriptingMethodPtr Produce(BackendNativeMethod nativeMethod) = 0;
+ virtual void Release(ScriptingMethodPtr) = 0;
+};
+
+#endif
diff --git a/Runtime/Scripting/Backend/ScriptingMethodRegistry.cpp b/Runtime/Scripting/Backend/ScriptingMethodRegistry.cpp
new file mode 100644
index 0000000..e9df7fd
--- /dev/null
+++ b/Runtime/Scripting/Backend/ScriptingMethodRegistry.cpp
@@ -0,0 +1,165 @@
+#include "UnityPrefix.h"
+#if ENABLE_SCRIPTING
+#include "ScriptingMethodRegistry.h"
+#include "ScriptingMethodFactory.h"
+#include "ScriptingTypeRegistry.h"
+#include "Runtime/Mono/MonoIncludes.h"
+#include "ScriptingBackendApi.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+
+using std::vector;
+
+
+ScriptingMethodRegistry::ScriptingMethodRegistry(IScriptingMethodFactory* scriptingMethodFactory, ScriptingTypeRegistry* scriptingTypeRegistry)
+ : m_ScriptingMethodFactory(scriptingMethodFactory),
+ m_ScriptingTypeRegistry(scriptingTypeRegistry)
+{
+}
+
+ScriptingMethodPtr ScriptingMethodRegistry::GetMethod(const char* namespaze, const char* className, const char* methodName, int searchOptions)
+{
+ ScriptingTypePtr klass = m_ScriptingTypeRegistry->GetType(namespaze,className);
+ if (klass==SCRIPTING_NULL)
+ return SCRIPTING_NULL;
+ return GetMethod(klass,methodName,searchOptions);
+}
+
+ScriptingMethodPtr ScriptingMethodRegistry::GetMethod(BackendNativeMethod nativeMethod)
+{
+ NativeMethodToScriptingMethod::iterator i = m_NativeMethodToScriptingMethod.find(nativeMethod);
+ if (i != m_NativeMethodToScriptingMethod.end())
+ return i->second;
+
+ ScriptingMethodPtr scriptingMethod = m_ScriptingMethodFactory->Produce(nativeMethod);
+ m_NativeMethodToScriptingMethod[nativeMethod] = scriptingMethod;
+ return scriptingMethod;
+}
+
+ScriptingMethodPtr ScriptingMethodRegistry::GetMethod(ScriptingTypePtr klass, const char* methodName, int searchOptions)
+{
+ bool found;
+ ScriptingMethodPtr cached = FindInCache(klass,methodName,searchOptions,&found);
+ if (found)
+ return cached;
+
+ ScriptingMethodPtr scriptingMethod = m_ScriptingMethodFactory->Produce(klass,methodName, searchOptions);
+
+ if (!scriptingMethod && (!(searchOptions & kDontSearchBaseTypes)))
+ {
+ ScriptingTypePtr baseClass = scripting_class_get_parent (klass, *m_ScriptingTypeRegistry);
+ if (baseClass)
+ scriptingMethod = GetMethod(baseClass, methodName, searchOptions);
+ }
+
+ PlaceInCache(klass,methodName,scriptingMethod);
+
+ return scriptingMethod;
+}
+
+bool MethodDescriptionMatchesSearchFilter(int searchFilter, bool isInstance, int argCount)
+{
+ if ((searchFilter & ScriptingMethodRegistry::kStaticOnly) && isInstance)
+ return false;
+
+ if ((searchFilter & ScriptingMethodRegistry::kInstanceOnly) && !isInstance)
+ return false;
+
+ if ((searchFilter & ScriptingMethodRegistry::kWithoutArguments) && argCount>0)
+ return false;
+
+ return true;
+}
+
+static bool MethodMatchesSearchFilter(ScriptingMethodPtr method, int searchFilter)
+{
+ return MethodDescriptionMatchesSearchFilter(searchFilter, scripting_method_is_instance(method), scripting_method_get_argument_count(method, GetScriptingTypeRegistry()));
+}
+
+void ScriptingMethodRegistry::AllMethodsIn(ScriptingTypePtr klass, vector<ScriptingMethodPtr>& result, int searchOptions)
+{
+ vector<ScriptingMethodPtr> methods;
+ scripting_class_get_methods(klass, *this, methods);
+ for (vector<ScriptingMethodPtr>::iterator item = methods.begin();
+ item != methods.end();
+ item++)
+ {
+ if (!MethodMatchesSearchFilter(*item, searchOptions))
+ continue;
+
+ result.push_back(*item);
+ }
+
+ if (searchOptions & kDontSearchBaseTypes)
+ return;
+
+ ScriptingTypePtr baseClass = scripting_class_get_parent(klass, *m_ScriptingTypeRegistry);
+ if (baseClass == SCRIPTING_NULL)
+ return;
+
+ return AllMethodsIn(baseClass, result, searchOptions);
+}
+
+void ScriptingMethodRegistry::InvalidateCache()
+{
+ //make sure to not delete a scriptingmethod twice, as it could be in the cache in multiple places. (in case of classes implementing interfaces & virtual methods)
+ MethodsSet todelete;
+ for (ClassToMethodsByName::iterator cci = m_Cache.begin(); cci != m_Cache.end(); cci++)
+ for (MethodsByName::iterator mbni = cci->second.begin(); mbni != cci->second.end(); mbni++)
+ for (VectorOfMethods::iterator smpi = mbni->second.begin(); smpi != mbni->second.end(); smpi++)
+ todelete.insert(*smpi);
+
+ for (NativeMethodToScriptingMethod::iterator i = m_NativeMethodToScriptingMethod.begin(); i != m_NativeMethodToScriptingMethod.end(); i++)
+ todelete.insert(i->second);
+
+ for (MethodsSet::iterator i = todelete.begin(); i != todelete.end(); i++)
+ m_ScriptingMethodFactory->Release(*i);
+
+ m_Cache.clear();
+ m_NativeMethodToScriptingMethod.clear();
+}
+
+ScriptingMethodPtr ScriptingMethodRegistry::FindInCache(ScriptingTypePtr klass, const char* name, int searchFilter, bool* found)
+{
+ *found = false;
+ MethodsByName* cc = FindMethodsByNameFor(klass);
+ if (cc == NULL)
+ return SCRIPTING_NULL;
+
+ MethodsByName::iterator mbni = cc->find(name);
+ if (mbni == cc->end())
+ return SCRIPTING_NULL;
+
+ for (VectorOfMethods::iterator i = mbni->second.begin(); i != mbni->second.end(); i++)
+ {
+ if (*i == NULL || MethodMatchesSearchFilter(*i, searchFilter))
+ {
+ *found = true;
+ return *i;
+ }
+ }
+ return SCRIPTING_NULL;
+}
+
+void ScriptingMethodRegistry::PlaceInCache(ScriptingTypePtr klass, const char* name, ScriptingMethodPtr method)
+{
+ MethodsByName* mbn = FindMethodsByNameFor(klass);
+ if (mbn == NULL)
+ {
+ m_Cache.insert(std::make_pair(klass, MethodsByName()));
+ mbn = FindMethodsByNameFor(klass);
+ }
+
+ VectorOfMethods vom;
+ vom.push_back(method);
+ mbn->insert(std::make_pair(name,vom));
+}
+
+ScriptingMethodRegistry::MethodsByName* ScriptingMethodRegistry::FindMethodsByNameFor(ScriptingTypePtr klass)
+{
+ ClassToMethodsByName::iterator cc = m_Cache.find(klass);
+ if (cc == m_Cache.end())
+ return NULL;
+ return &cc->second;
+}
+
+#endif
diff --git a/Runtime/Scripting/Backend/ScriptingMethodRegistry.h b/Runtime/Scripting/Backend/ScriptingMethodRegistry.h
new file mode 100644
index 0000000..82d7e40
--- /dev/null
+++ b/Runtime/Scripting/Backend/ScriptingMethodRegistry.h
@@ -0,0 +1,60 @@
+#ifndef _SCRIPTINGMETHODREGISTRY_H_
+#define _SCRIPTINGMETHODREGISTRY_H_
+
+#if ENABLE_SCRIPTING
+#include "ScriptingTypes.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+class IScriptingMethodFactory;
+class ScriptingTypeRegistry;
+
+class ScriptingMethodRegistry
+{
+public:
+ ScriptingMethodRegistry(IScriptingMethodFactory* scriptingMethodFactory, ScriptingTypeRegistry* scriptingTypeRegistry);
+ ~ScriptingMethodRegistry() {InvalidateCache();}
+
+ enum SearchFilter
+ {
+ kNone = 0,
+ kInstanceOnly = 1,
+ kStaticOnly = 2,
+ kWithoutArguments = 4,
+ kDontSearchBaseTypes = 8,
+ };
+
+ ScriptingMethodPtr GetMethod(const char* namespaze, const char* klassName, const char* methodName, int searchOptions=kNone);
+ ScriptingMethodPtr GetMethod(ScriptingTypePtr klass, const char* methodName, int searchOptions=kNone);
+ ScriptingMethodPtr GetMethod(BackendNativeMethod nativeMathod);
+
+ void AllMethodsIn(ScriptingTypePtr klass, std::vector<ScriptingMethodPtr>& result, int searchOptions=kNone);
+
+ void InvalidateCache();
+
+private:
+ typedef std::vector<ScriptingMethodPtr> VectorOfMethods;
+ typedef std::map< std::string,VectorOfMethods > MethodsByName;
+ typedef std::map<ScriptingTypePtr, MethodsByName> ClassToMethodsByName;
+ typedef std::map<BackendNativeMethod, ScriptingMethodPtr> NativeMethodToScriptingMethod;
+ typedef std::set<ScriptingMethodPtr> MethodsSet;
+
+ ClassToMethodsByName m_Cache;
+
+
+ NativeMethodToScriptingMethod m_NativeMethodToScriptingMethod;
+
+ IScriptingMethodFactory* m_ScriptingMethodFactory;
+ ScriptingTypeRegistry* m_ScriptingTypeRegistry;
+
+ ScriptingMethodPtr FindInCache(ScriptingTypePtr klass, const char* name, int searchFilter, bool* out_found);
+ void PlaceInCache(ScriptingTypePtr klass, const char* name, ScriptingMethodPtr method);
+ MethodsByName* FindMethodsByNameFor(ScriptingTypePtr klass);
+};
+
+bool MethodDescriptionMatchesSearchFilter(int searchFilter, bool isInstance, int argCount);
+#endif
+
+#endif \ No newline at end of file
diff --git a/Runtime/Scripting/Backend/ScriptingMethodRegistryTests.cpp b/Runtime/Scripting/Backend/ScriptingMethodRegistryTests.cpp
new file mode 100644
index 0000000..e3735b0
--- /dev/null
+++ b/Runtime/Scripting/Backend/ScriptingMethodRegistryTests.cpp
@@ -0,0 +1,237 @@
+#include "UnityPrefix.h"
+#if DEDICATED_UNIT_TEST_BUILD
+#include "ScriptingMethodRegistry.h"
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "ScriptingMethodFactory.h"
+#include "Tests/ScriptingBackendApi_Tests.h"
+#include <queue>
+#include <set>
+#include <vector>
+using std::queue;
+using std::vector;
+
+class ScriptingMethodFactory_Dummy : public IScriptingMethodFactory
+{
+public:
+ queue<ScriptingMethodPtr> produceThis;
+ ScriptingTypePtr returnNullForRequestson;
+ int produceCallCount;
+
+ ScriptingMethodFactory_Dummy() : produceCallCount(0) {}
+
+ virtual ScriptingMethodPtr Produce(ScriptingTypePtr klass, const char* name, int searchFilter)
+ {
+ produceCallCount++;
+
+ if (returnNullForRequestson == klass)
+ return NULL;
+
+ if (produceThis.size()==0)
+ return NULL;
+
+ ScriptingMethodPtr result = produceThis.front();
+
+ if (result && !MethodDescriptionMatchesSearchFilter(searchFilter, result->instance, result->args))
+ return NULL;
+
+ produceThis.pop();
+ return result;
+ }
+
+ virtual ScriptingMethodPtr Produce(void* nativePtr)
+ {
+ return Produce(NULL,NULL, 0);
+ }
+
+ virtual void Release(ScriptingMethodPtr method)
+ {
+ }
+};
+
+struct Fixture
+{
+ ScriptingMethodFactory_Dummy methodFactory;
+ ScriptingMethodRegistry registry;
+
+ ScriptingType normalType;
+
+ ScriptingMethod method1;
+ ScriptingMethod method2;
+
+ Fixture() : registry(&methodFactory,NULL)
+ {
+ }
+};
+
+TEST_FIXTURE(Fixture,RegistryProducesNullForNonExistingMethod)
+{
+ CHECK_EQUAL((ScriptingMethodPtr)NULL,registry.GetMethod(&normalType,"Method1"));
+}
+
+TEST_FIXTURE(Fixture,RegistryProducesMethod)
+{
+ methodFactory.produceThis.push(&method1);
+ CHECK_EQUAL(&method1,registry.GetMethod(&normalType,"Method1"));
+}
+
+TEST_FIXTURE(Fixture,RegistryReturnsSameMethodWhenQueriedTwiceWithSameArguments)
+{
+ methodFactory.produceThis.push(&method1);
+
+ CHECK_EQUAL(&method1,registry.GetMethod(&normalType,"Method1"));
+ CHECK_EQUAL(&method1,registry.GetMethod(&normalType,"Method1"));
+}
+
+TEST_FIXTURE(Fixture,RegistryReturnsDifferentMethodsWhenQueriedForTwoDifferentClasses)
+{
+ methodFactory.produceThis.push(&method1);
+ methodFactory.produceThis.push(&method2);
+
+ CHECK_EQUAL(&method1,registry.GetMethod(&normalType,"Method1"));
+ CHECK_EQUAL(&method2,registry.GetMethod((ScriptingTypePtr)124,"Method1"));
+}
+
+TEST_FIXTURE(Fixture,RegistryReturnsDifferentMethodsForDifferentSearchFilters)
+{
+ methodFactory.produceThis.push(&method1);
+ methodFactory.produceThis.push(&method2);
+ method1.instance = false;
+ method2.instance = true;
+
+ CHECK_EQUAL(&method1,registry.GetMethod(&normalType,"Method1", ScriptingMethodRegistry::kStaticOnly));
+ CHECK_EQUAL(&method2,registry.GetMethod(&normalType,"Method1", ScriptingMethodRegistry::kInstanceOnly));
+}
+
+TEST_FIXTURE(Fixture,RegistryDoesNotFindMethodsNotMatchingSearchFilter)
+{
+ methodFactory.produceThis.push(&method1);
+ method1.instance = true;
+
+ ScriptingMethodPtr found = registry.GetMethod(&normalType,"Method1", ScriptingMethodRegistry::kStaticOnly);
+ if (found==NULL)
+ CHECK_EQUAL(1,1);
+ else
+ CHECK_EQUAL(1,0);
+}
+
+TEST_FIXTURE(Fixture,RegistryRemembersNonFoundMethods)
+{
+ methodFactory.produceThis.push(NULL);
+ methodFactory.produceThis.push(NULL);
+
+ registry.GetMethod(&normalType,"NonExisting");
+ registry.GetMethod(&normalType,"NonExisting");
+
+ CHECK_EQUAL(1,methodFactory.produceCallCount);
+}
+
+
+
+TEST_FIXTURE(Fixture,InvalidateCacheCausesNewMethodToBeProduced)
+{
+ methodFactory.produceThis.push(&method1);
+ methodFactory.produceThis.push(&method2);
+
+ CHECK_EQUAL(&method1,registry.GetMethod(&normalType,"Method1"));
+ registry.InvalidateCache();
+ CHECK_EQUAL(&method2,registry.GetMethod(&normalType,"Method1"));
+}
+
+struct FixtureWithBaseTypeHavingMethod : public Fixture
+{
+ ScriptingType normalType;
+ ScriptingType baseType;
+
+ FixtureWithBaseTypeHavingMethod()
+ {
+ normalType.baseType = &baseType;
+ methodFactory.produceThis.push(&method1);
+ methodFactory.returnNullForRequestson = &normalType;
+ }
+};
+
+TEST_FIXTURE(FixtureWithBaseTypeHavingMethod,RegistryWillSearchBaseTypesIfRequested)
+{
+ CHECK_EQUAL(&method1,registry.GetMethod(&normalType,"Method1"));
+}
+
+TEST_FIXTURE(FixtureWithBaseTypeHavingMethod, RegistryWillNotSearchBaseTypesIfNotRequested)
+{
+ CHECK_EQUAL((ScriptingMethodPtr)NULL,registry.GetMethod(&normalType,"Method1", ScriptingMethodRegistry::kDontSearchBaseTypes));
+}
+
+TEST_FIXTURE(Fixture, CanGetMethodFromNativeMethodPointer)
+{
+ int nativeMethod;
+ methodFactory.produceThis.push(&method1);
+ CHECK_EQUAL(&method1,registry.GetMethod( &nativeMethod));
+}
+
+TEST_FIXTURE(Fixture, CanGetSameMethodFromNativeMethodTwice)
+{
+ int nativeMethod;
+ methodFactory.produceThis.push(&method1);
+ CHECK_EQUAL(&method1,registry.GetMethod(&nativeMethod));
+ CHECK_EQUAL(&method1,registry.GetMethod(&nativeMethod));
+}
+
+TEST_FIXTURE(Fixture, InvalidateCacheClearsNativeMethodWrappers)
+{
+ int nativeMethod;
+ methodFactory.produceThis.push(&method1);
+ methodFactory.produceThis.push(&method2);
+ CHECK_EQUAL(&method1,registry.GetMethod(&nativeMethod));
+ registry.InvalidateCache();
+ CHECK_EQUAL(&method2,registry.GetMethod(&nativeMethod));
+}
+
+TEST_FIXTURE(Fixture, AllMethodsInType)
+{
+ normalType.methods.push_back(&method1);
+
+ vector<ScriptingMethodPtr> result;
+ registry.AllMethodsIn(&normalType, result);
+ CHECK_EQUAL(1, result.size());
+ CHECK_EQUAL(&method1, result[0]);
+}
+
+TEST_FIXTURE(Fixture, AllMethodsInTypeWillSearchBaseTypes)
+{
+ ScriptingType baseType;
+ baseType.methods.push_back(&method1);
+ normalType.baseType = &baseType;
+
+ vector<ScriptingMethodPtr> result;
+ registry.AllMethodsIn(&normalType, result);
+ CHECK_EQUAL(1, result.size());
+ CHECK_EQUAL(&method1, result[0]);
+}
+
+TEST_FIXTURE(Fixture, AllMethodsInTypeCanSkipStaticMethods)
+{
+ normalType.methods.push_back(&method1);
+ normalType.methods.push_back(&method2);
+
+ method2.instance = true;
+
+ vector<ScriptingMethodPtr> result;
+ registry.AllMethodsIn(&normalType, result, ScriptingMethodRegistry::kInstanceOnly);
+ CHECK_EQUAL(1, result.size());
+ CHECK_EQUAL(&method2, result[0]);
+}
+
+TEST_FIXTURE(Fixture, SearchFilterCanSkipMethodsWithArguments)
+{
+ normalType.methods.push_back(&method1);
+ normalType.methods.push_back(&method2);
+
+ method1.args=0;
+ method2.args=2;
+
+ vector<ScriptingMethodPtr> result;
+ registry.AllMethodsIn(&normalType, result, ScriptingMethodRegistry::kWithoutArguments );
+ CHECK_EQUAL(1, result.size());
+ CHECK_EQUAL(&method1, result[0]);
+}
+
+#endif
diff --git a/Runtime/Scripting/Backend/ScriptingTypeRegistry.cpp b/Runtime/Scripting/Backend/ScriptingTypeRegistry.cpp
new file mode 100644
index 0000000..0e4fcbd
--- /dev/null
+++ b/Runtime/Scripting/Backend/ScriptingTypeRegistry.cpp
@@ -0,0 +1,214 @@
+#include "UnityPrefix.h"
+#if ENABLE_SCRIPTING
+#include "ScriptingTypeRegistry.h"
+#include "ScriptingBackendApi.h"
+
+ScriptingTypeRegistry::ScriptingTypeRegistry(IScriptingTypeProvider* scriptingTypeProvider)
+ : m_ScriptingTypeProvider(scriptingTypeProvider)
+{
+}
+
+ScriptingTypePtr ScriptingTypeRegistry::GetType(const char* namespaze, const char* name)
+{
+ NameSpaceAndNamePair key = std::make_pair(namespaze,name);
+
+ Cache::iterator i = m_Cache.find(key);
+ if (i != m_Cache.end())
+ return i->second;
+
+ BackendNativeType nativetype = m_ScriptingTypeProvider->NativeTypeFor(namespaze, name);
+ ScriptingTypePtr type = m_ScriptingTypeProvider->Provide(nativetype);
+
+ m_Cache.insert(std::make_pair(key,type));
+ return type;
+}
+
+ScriptingTypePtr ScriptingTypeRegistry::GetType(BackendNativeType nativeType)
+{
+ NativeTypeCache::iterator i = m_NativeTypeCache.find(ScriptingObjectToComPtr(nativeType));
+ if (i != m_NativeTypeCache.end())
+ return i->second;
+
+ ScriptingTypePtr type = m_ScriptingTypeProvider->Provide(nativeType);
+
+ m_NativeTypeCache.insert(std::make_pair(ScriptingObjectToComPtr(nativeType), type));
+ return type;
+}
+
+ScriptingTypePtr ScriptingTypeRegistry::GetType(ScriptingObjectPtr systemTypeInstance)
+{
+ return scripting_class_from_systemtypeinstance(systemTypeInstance, *this);
+}
+
+void ScriptingTypeRegistry::InvalidateCache()
+{
+ m_Cache.clear();
+ m_NativeTypeCache.clear();
+}
+
+#if DEDICATED_UNIT_TEST_BUILD
+#include "External/UnitTest++/src/UnitTest++.h"
+#include <queue>
+#include "Tests/ScriptingBackendApi_Tests.h"
+using std::queue;
+
+class ScriptingTypeProvider_test : public IScriptingTypeProvider
+{
+public:
+ BackendNativeType NativeTypeFor(const char* namespaze, const char* name)
+ {
+ BackendNativeType ptr = provideThis.front();
+ provideThis.pop();
+ return ptr;
+ }
+
+ ScriptingTypePtr Provide(BackendNativeType nativeType)
+ {
+ return (ScriptingTypePtr)nativeType;
+ }
+
+ void Release(ScriptingTypePtr ptr) {}
+
+ queue<BackendNativeType> provideThis;
+};
+
+struct MyFixture
+{
+ ScriptingTypeProvider_test typeProvider;
+
+ ScriptingType type1;
+ ScriptingType type2;
+ int nativeType1;
+ int nativeType2;
+ FakeSystemTypeInstance systemTypeInstance1;
+ FakeSystemTypeInstance systemTypeInstance2;
+};
+
+TEST_FIXTURE(MyFixture,ScriptingTypeRegistryReturnsSameTypeWhenAskedForSameName)
+{
+ typeProvider.provideThis.push(&type1);
+
+ ScriptingTypeRegistry registry(&typeProvider);
+ ScriptingTypePtr t1 = registry.GetType("mynamespace","mytype");
+ ScriptingTypePtr t2 = registry.GetType("mynamespace","mytype");
+ CHECK_EQUAL(&type1,t1);
+ CHECK_EQUAL(&type1,t2);
+}
+
+TEST_FIXTURE(MyFixture,ScriptingTypeRegistryReturnsDifferentTypeWhenAskedForDifferentName)
+{
+ typeProvider.provideThis.push(&type1);
+ typeProvider.provideThis.push(&type2);
+
+ ScriptingTypeRegistry registry(&typeProvider);
+ ScriptingTypePtr t1 = registry.GetType("mynamespace", "mytype");
+ ScriptingTypePtr t2 = registry.GetType("mynamespace", "myothertype");
+ CHECK_EQUAL(&type1,t1);
+ CHECK_EQUAL(&type2,t2);
+}
+
+TEST_FIXTURE(MyFixture,ScriptingTypeRegistryReturnsDifferentTypeWhenAskedForTypesWithDifferentNamespace)
+{
+ typeProvider.provideThis.push(&type1);
+ typeProvider.provideThis.push(&type2);
+
+ ScriptingTypeRegistry registry(&typeProvider);
+ ScriptingTypePtr t1 = registry.GetType("mynamespace", "mytype");
+ ScriptingTypePtr t2 = registry.GetType("myothernamespace", "mytype");
+ CHECK_EQUAL(&type1,t1);
+ CHECK_EQUAL(&type2,t2);
+}
+
+TEST_FIXTURE(MyFixture,ScriptingTypeRegistryInvalidateCacheCausesNewTypeBeingProduced)
+{
+ typeProvider.provideThis.push(&type1);
+ typeProvider.provideThis.push(&type2);
+
+ ScriptingTypeRegistry registry(&typeProvider);
+ ScriptingTypePtr t1 = registry.GetType("mynamespace","mytype");
+ registry.InvalidateCache();
+ ScriptingTypePtr t2 = registry.GetType("mynamespace", "mytype");
+ CHECK_EQUAL(&type1,t1);
+ CHECK_EQUAL(&type2,t2);
+}
+
+TEST_FIXTURE(MyFixture,AskingForTheSameNativeTypeTwiceReturnsSameScriptingTypeTwice)
+{
+ typeProvider.provideThis.push(&type1);
+
+ ScriptingTypeRegistry registry(&typeProvider);
+
+ ScriptingTypePtr t1 = registry.GetType(&nativeType1);
+ ScriptingTypePtr t2 = registry.GetType(&nativeType1);
+ CHECK_EQUAL(&type1,t1);
+ CHECK_EQUAL(&type1,t2);
+}
+
+TEST_FIXTURE(MyFixture,AskingForTwoDifferentNativeTypesReturnsDifferentScriptingTypes)
+{
+ typeProvider.provideThis.push(&type1);
+ typeProvider.provideThis.push(&type2);
+
+ ScriptingTypeRegistry registry(&typeProvider);
+ ScriptingTypePtr t1 = registry.GetType(&nativeType1);
+ ScriptingTypePtr t2 = registry.GetType(&nativeType2);
+ CHECK_EQUAL(&type1,t1);
+ CHECK_EQUAL(&type2,t2);
+}
+
+TEST_FIXTURE(MyFixture,InvalidateCacheCausesNewTypeToBeReturned)
+{
+ typeProvider.provideThis.push(&type1);
+ typeProvider.provideThis.push(&type2);
+
+ ScriptingTypeRegistry registry(&typeProvider);
+
+ ScriptingTypePtr t1 = registry.GetType(&nativeType1);
+ registry.InvalidateCache();
+ ScriptingTypePtr t2 = registry.GetType(&nativeType1);
+ CHECK_EQUAL(&type1,t1);
+ CHECK_EQUAL(&type2,t2);
+}
+
+TEST_FIXTURE(MyFixture,AskingForTypeOfSystemTypeInstanceTwiceReturnsSameType)
+{
+ typeProvider.provideThis.push(&type1);
+ ScriptingTypeRegistry registry(&typeProvider);
+
+ systemTypeInstance1.nativeType = &nativeType1;
+ ScriptingTypePtr t1 = registry.GetType((ScriptingObjectPtr) &systemTypeInstance1);
+ ScriptingTypePtr t2 = registry.GetType((ScriptingObjectPtr) &systemTypeInstance1);
+ CHECK_EQUAL(&type1,t1);
+ CHECK_EQUAL(&type1,t2);
+}
+/*
+TEST_FIXTURE(MyFixture,AskingForDifferentSystemTypeInstancesReturnsDifferentTypes)
+{
+ typeProvider.provideThis.push(&type1);
+ typeProvider.provideThis.push(&type2);
+ ScriptingTypeRegistry registry(&typeProvider);
+
+ systemTypeInstance1.nativeType = &nativeType1;
+ systemTypeInstance1.nativeType = &nativeType2;
+ ScriptingTypePtr t1 = registry.GetType((ScriptingObjectPtr) &systemTypeInstance1);
+ ScriptingTypePtr t2 = registry.GetType((ScriptingObjectPtr) &systemTypeInstance2);
+ CHECK_EQUAL(&type1,t1);
+ CHECK_EQUAL(&type2,t2);
+}
+*/
+TEST_FIXTURE(MyFixture,InvalidateCacheCausesSystemTypeInstanceQueryToProvideNewType)
+{
+ typeProvider.provideThis.push(&type1);
+ typeProvider.provideThis.push(&type2);
+ ScriptingTypeRegistry registry(&typeProvider);
+
+ systemTypeInstance1.nativeType = &nativeType1;
+ ScriptingTypePtr t1 = registry.GetType((ScriptingObjectPtr) &systemTypeInstance1);
+ registry.InvalidateCache();
+ ScriptingTypePtr t2 = registry.GetType((ScriptingObjectPtr) &systemTypeInstance1);
+ CHECK_EQUAL(&type1,t1);
+ CHECK_EQUAL(&type2,t2);
+}
+
+#endif
+#endif
diff --git a/Runtime/Scripting/Backend/ScriptingTypeRegistry.h b/Runtime/Scripting/Backend/ScriptingTypeRegistry.h
new file mode 100644
index 0000000..be2328f
--- /dev/null
+++ b/Runtime/Scripting/Backend/ScriptingTypeRegistry.h
@@ -0,0 +1,33 @@
+#ifndef _SCRIPTINGTYPEREGISTRY_Y_
+#define _SCRIPTINGTYPEREGISTRY_Y_
+
+#if ENABLE_SCRIPTING
+
+#include <map>
+#include <string>
+#include "ScriptingTypes.h"
+#include "IScriptingTypeProvider.h"
+
+class ScriptingTypeRegistry
+{
+public:
+ ScriptingTypeRegistry(IScriptingTypeProvider* scriptingTypeProvider);
+ ScriptingTypePtr GetType(const char* namespaze, const char* name);
+ ScriptingTypePtr GetType(BackendNativeType nativeType);
+ ScriptingTypePtr GetType(ScriptingObjectPtr systemTypeInstance);
+ void InvalidateCache();
+
+private:
+ typedef std::pair<std::string,std::string> NameSpaceAndNamePair;
+ typedef std::map<NameSpaceAndNamePair, ScriptingTypePtr> Cache;
+ Cache m_Cache;
+
+ typedef std::map<void*, ScriptingTypePtr> NativeTypeCache;
+ NativeTypeCache m_NativeTypeCache;
+
+ IScriptingTypeProvider* m_ScriptingTypeProvider;
+};
+
+#endif
+
+#endif
diff --git a/Runtime/Scripting/Backend/ScriptingTypes.h b/Runtime/Scripting/Backend/ScriptingTypes.h
new file mode 100644
index 0000000..45911b0
--- /dev/null
+++ b/Runtime/Scripting/Backend/ScriptingTypes.h
@@ -0,0 +1,202 @@
+#ifndef _SCRIPTINGTYPES_H_
+#define _SCRIPTINGTYPES_H_
+
+#if ENABLE_MONO
+struct MonoObject;
+struct MonoString;
+struct MonoArray;
+struct MonoType;
+struct MonoClass;
+struct MonoException;
+struct MonoMethod;
+struct MonoClassField;
+typedef MonoObject ScriptingObject;
+typedef MonoString ScriptingString;
+typedef MonoArray ScriptingArray;
+typedef MonoClass ScriptingType;
+typedef MonoClass ScriptingClass;
+typedef MonoException ScriptingException;
+typedef MonoClassField ScriptingField;
+struct ScriptingMethod;
+typedef MonoMethod* BackendNativeMethod;
+typedef MonoClass* BackendNativeType;
+
+#elif UNITY_FLASH
+struct ScriptingObject;
+struct ScriptingString;
+struct ScriptingArray;
+struct ScriptingClass;
+typedef ScriptingClass ScriptingType;
+struct ScriptingException;
+
+struct ScriptingMethod;
+struct ScriptingField;
+struct MonoImage;
+
+//hack to not have to change everything in one go.
+struct MonoMethod;
+struct MonoObject;
+typedef ScriptingType MonoClass;
+typedef void* BackendNativeType;
+typedef void* BackendNativeMethod;
+#elif UNITY_WINRT && ENABLE_SCRIPTING
+struct WinRTNullPtrImplementation
+{
+ inline operator long long()
+ {
+ return -1;
+ }
+ inline operator decltype(__nullptr)()
+ {
+ return nullptr;
+ }
+};
+
+// Implemented in WinRTUtility.cpp
+class WinRTScriptingObjectWrapper
+{
+public:
+ // Must be in sync with WinRTBridge\GCHandledObjects.cs, would be nice to somehow get those values directly...
+ static const int kMaxHandlesShift = 11;
+ static const int kMaxHandlesPerBlock = 1 << kMaxHandlesShift;
+ static const int kHandleMask = kMaxHandlesPerBlock - 1;
+public:
+ WinRTScriptingObjectWrapper();
+ WinRTScriptingObjectWrapper(decltype(__nullptr));
+ WinRTScriptingObjectWrapper(long long handleId);
+ WinRTScriptingObjectWrapper(const WinRTNullPtrImplementation& interopParam);
+ WinRTScriptingObjectWrapper(const WinRTScriptingObjectWrapper& handleId);
+ ~WinRTScriptingObjectWrapper();
+
+ const WinRTScriptingObjectWrapper &operator = (const WinRTScriptingObjectWrapper &ptr);
+ WinRTScriptingObjectWrapper& operator=(decltype(__nullptr));
+ WinRTScriptingObjectWrapper& operator=(const WinRTNullPtrImplementation& nullptrimpl);
+ inline operator bool () const
+ {
+ return m_Handle != -1;
+ }
+ inline operator long long() const
+ {
+ return m_Handle;
+ }
+ int GetReferenceCount() const;
+
+ // Converters
+ bool ToBool();
+
+ inline long long GetHandle() const { return m_Handle;}
+ inline int GetBlockId() const {return m_BlockId; }
+ inline int GetHandleInBlock() const { return m_HandleInBlock; }
+
+ void Validate();
+
+ static void FreeHandles();
+ static void ClearCache();
+ static void ValidateCache();
+private:
+ friend bool operator == (const WinRTScriptingObjectWrapper& a, decltype(__nullptr));
+ friend bool operator != (const WinRTScriptingObjectWrapper& a, decltype(__nullptr));
+ friend bool operator == (const WinRTScriptingObjectWrapper& a, const WinRTNullPtrImplementation& b);
+ friend bool operator != (const WinRTScriptingObjectWrapper& a, const WinRTNullPtrImplementation& b);
+ static int* GetPtrToReferences(int blockId);
+
+ void Init(long long handleId);
+ void InternalAddRef();
+ void InternalRelease();
+
+ long long m_Handle;
+ int m_BlockId;
+ int m_HandleInBlock;
+ int* m_PtrToReferences;
+
+ static const int kMaxCachedPtrToReferences = 4096;
+ static int* m_CachedPtrToReferences[kMaxCachedPtrToReferences];
+ static int m_CachedBlockCount;
+};
+
+bool operator == (const WinRTScriptingObjectWrapper& a, decltype(__nullptr));
+bool operator != (const WinRTScriptingObjectWrapper& a, decltype(__nullptr));
+bool operator == (const WinRTScriptingObjectWrapper& a, const WinRTNullPtrImplementation& b);
+bool operator != (const WinRTScriptingObjectWrapper& a, const WinRTNullPtrImplementation& b);
+// Don't implement this, as same objects can have different handle ids, so currently there's no way of effectively comparing them
+//bool operator == (const WinRTScriptingObjectWrapper& a, const WinRTScriptingObjectWrapper& b);
+
+struct ScriptingObject;
+struct ScriptingString;
+typedef Platform::Object ScriptingArray;
+//struct ScriptingArray;
+struct ScriptingType;
+typedef ScriptingType ScriptingClass;
+struct ScriptingException;
+
+typedef int BackendNativeMethod;
+typedef BridgeInterface::IScriptingClassWrapper^ BackendNativeType;
+
+struct ScriptingMethod;
+struct ScriptingField;
+struct MonoImage;
+
+//hack to not have to change everything in one go.
+struct MonoMethod;
+struct MonoObject;
+typedef ScriptingType MonoClass;
+#else
+struct ScriptingObject;
+struct ScriptingString;
+struct ScriptingException;
+struct ScriptingMethod;
+struct ScriptingField;
+struct ScriptingString;
+struct ScriptingClass;
+struct ScriptingType;
+struct ScriptingArray;
+struct ScriptingString;
+struct MonoMethod;
+struct MonoObject;
+struct MonoClass;
+typedef void* BackendNativeType;
+typedef void* BackendNativeMethod;
+#endif
+
+
+#if UNITY_WINRT && ENABLE_SCRIPTING
+typedef WinRTScriptingObjectWrapper ScriptingObjectPtr;
+typedef Platform::Object^ ScriptingStringPtr;
+typedef WinRTScriptingObjectWrapper ScriptingArrayPtr;
+typedef WinRTScriptingObjectWrapper ScriptingPinnedArrayPtr;
+typedef WinRTScriptingObjectWrapper const ScriptingArrayConstPtr;
+typedef WinRTScriptingObjectWrapper ScriptingPinnedArrayConstPtr;
+typedef ScriptingType* ScriptingTypePtr;
+typedef ScriptingClass* ScriptingClassPtr;
+typedef ScriptingException* ScriptingExceptionPtr;
+typedef ScriptingMethod* ScriptingMethodPtr;
+typedef ScriptingField* ScriptingFieldPtr;
+//typedef Platform::Array<Platform::Object^> ScriptingParams;
+//typedef ScriptingParams^ ScriptingParamsPtr;
+typedef long long ScriptingParams;
+typedef long long* ScriptingParamsPtr;
+#define ScriptingBool bool
+#define SCRIPTING_NULL WinRTNullPtrImplementation()
+#define ScriptingObjectComPtr(Type) Microsoft::WRL::ComPtr<IInspectable>
+#define ScriptingObjectToComPtr(Object) reinterpret_cast<IInspectable*>(Object)
+#define ScriptingComPtrToObject(Type, Object) reinterpret_cast<Type>((Object).Get())
+#else
+typedef ScriptingObject* ScriptingObjectPtr;
+typedef ScriptingString* ScriptingStringPtr;
+typedef ScriptingArray* ScriptingArrayPtr;
+typedef ScriptingType* ScriptingTypePtr;
+typedef ScriptingClass* ScriptingClassPtr;
+typedef ScriptingException* ScriptingExceptionPtr;
+typedef ScriptingMethod* ScriptingMethodPtr;
+typedef ScriptingField* ScriptingFieldPtr;
+typedef void* ScriptingParams;
+typedef ScriptingParams* ScriptingParamsPtr;
+#define ScriptingBool short
+#define SCRIPTING_NULL NULL
+#define ScriptingObjectComPtr(Type) Type
+#define ScriptingObjectToComPtr(Object) Object
+#define ScriptingComPtrToObject(Type, Object) Object
+#endif
+
+
+#endif
diff --git a/Runtime/Scripting/Backend/Tests/ScriptingBackendApi_Tests.cpp b/Runtime/Scripting/Backend/Tests/ScriptingBackendApi_Tests.cpp
new file mode 100644
index 0000000..c8c0122
--- /dev/null
+++ b/Runtime/Scripting/Backend/Tests/ScriptingBackendApi_Tests.cpp
@@ -0,0 +1,35 @@
+#include "UnityPrefix.h"
+
+#if DEDICATED_UNIT_TEST_BUILD
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/Scripting/Backend/Tests/ScriptingBackendApi_Tests.h"
+#include "Runtime/Scripting/Backend/ScriptingMethodRegistry.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+
+bool scripting_method_is_instance(ScriptingMethodPtr method)
+{
+ return method->instance;
+}
+
+int scripting_method_get_argument_count(ScriptingMethodPtr method)
+{
+ return method->args;
+}
+
+ScriptingTypePtr scripting_class_get_parent(ScriptingTypePtr type, ScriptingTypeRegistry& typeRegistry)
+{
+ return type->baseType;
+}
+
+void scripting_class_get_methods(ScriptingTypePtr type, ScriptingMethodRegistry& registry, std::vector<ScriptingMethodPtr>& result)
+{
+ result = type->methods;
+}
+
+ScriptingTypePtr scripting_class_from_systemtypeinstance(ScriptingObjectPtr type, ScriptingTypeRegistry& typeRegistry)
+{
+ FakeSystemTypeInstance* sti = (FakeSystemTypeInstance*) type;
+ return typeRegistry.GetType(sti->nativeType);
+}
+
+#endif
diff --git a/Runtime/Scripting/Backend/Tests/ScriptingBackendApi_Tests.h b/Runtime/Scripting/Backend/Tests/ScriptingBackendApi_Tests.h
new file mode 100644
index 0000000..2ab7765
--- /dev/null
+++ b/Runtime/Scripting/Backend/Tests/ScriptingBackendApi_Tests.h
@@ -0,0 +1,30 @@
+#ifndef _SCRIPTINGBACKENDAPI_TESTS_H_
+#define _SCRIPTINGBACKENDAPI_TESTS_H_
+
+#if DEDICATED_UNIT_TEST_BUILD
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include <vector>
+
+struct ScriptingType
+{
+ ScriptingTypePtr baseType;
+ ScriptingType() : baseType(NULL) {}
+
+ std::vector<ScriptingMethodPtr> methods;
+};
+
+struct ScriptingMethod
+{
+ bool instance;
+ int args;
+
+ ScriptingMethod() : instance(false), args(0) {}
+};
+
+struct FakeSystemTypeInstance
+{
+ void* nativeType;
+};
+
+#endif
+#endif
diff --git a/Runtime/Scripting/CommonScriptingClasses.cpp b/Runtime/Scripting/CommonScriptingClasses.cpp
new file mode 100644
index 0000000..6a7d266
--- /dev/null
+++ b/Runtime/Scripting/CommonScriptingClasses.cpp
@@ -0,0 +1,268 @@
+#include "UnityPrefix.h"
+#if ENABLE_SCRIPTING
+#include "CommonScriptingClasses.h"
+#include "Runtime/Mono/MonoIncludes.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/Backend/ScriptingMethodRegistry.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+
+static ScriptingMethodPtr OptionalMethod(const char* namespaze, const char* klass, const char* name)
+{
+ return GetScriptingManager().GetScriptingMethodRegistry().GetMethod(namespaze,klass,name);
+}
+
+static ScriptingMethodPtr RequireMethod(const char* namespaze, const char* klass, const char* name)
+{
+ ScriptingMethodPtr method = GetScriptingManager().GetScriptingMethodRegistry().GetMethod(namespaze,klass,name);
+ if (!method)
+ ErrorString(Format("Unable to find method %s in %s",name,klass));
+ return method;
+}
+
+
+static ScriptingTypePtr OptionalType(const char* namespaze, const char* name)
+{
+ return GetScriptingTypeRegistry().GetType(namespaze,name);
+}
+
+static ScriptingTypePtr RequireType(const char* namespaze, const char* name)
+{
+ ScriptingTypePtr t = OptionalType(namespaze,name);
+ if (!t)
+ ErrorString(Format("Unable to find type %s.%s",namespaze,name));
+ return t;
+}
+
+static ScriptingTypePtr RequireUnityEngineType(const char* name)
+{
+ return RequireType("UnityEngine",name);
+}
+
+static ScriptingTypePtr OptionalUnityEngineType(const char* name)
+{
+ return OptionalType("UnityEngine",name);
+}
+
+void FillCommonScriptingClasses(CommonScriptingClasses& commonScriptingClasses)
+{
+ ScriptingTypeRegistry& typeRegistry = GetScriptingManager().GetScriptingTypeRegistry();
+
+#if ENABLE_SCRIPTING
+ commonScriptingClasses.monoBehaviour = RequireUnityEngineType ("MonoBehaviour");
+ commonScriptingClasses.component = RequireUnityEngineType ("Component");
+ commonScriptingClasses.scriptableObject = RequireUnityEngineType ("ScriptableObject");
+ commonScriptingClasses.vector2 = RequireUnityEngineType ("Vector2");
+ commonScriptingClasses.vector3 = RequireUnityEngineType ("Vector3");
+ commonScriptingClasses.vector4 = RequireUnityEngineType ("Vector4");
+ commonScriptingClasses.rect = RequireUnityEngineType ("Rect");
+ commonScriptingClasses.rectOffset = RequireUnityEngineType ("RectOffset");
+ commonScriptingClasses.quaternion = RequireUnityEngineType ("Quaternion");
+ commonScriptingClasses.matrix4x4 = RequireUnityEngineType ("Matrix4x4");
+ commonScriptingClasses.bounds = RequireUnityEngineType ("Bounds");
+ commonScriptingClasses.resolution = RequireUnityEngineType ("Resolution");
+ commonScriptingClasses.particle = RequireUnityEngineType ("Particle");
+ commonScriptingClasses.color = RequireUnityEngineType ("Color");
+ commonScriptingClasses.color32 = RequireUnityEngineType ("Color32");
+ commonScriptingClasses.raycastHit = OptionalUnityEngineType ("RaycastHit");
+ commonScriptingClasses.raycastHit2D = OptionalUnityEngineType ("RaycastHit2D");
+ commonScriptingClasses.animationState = OptionalUnityEngineType ("AnimationState");
+ commonScriptingClasses.collider = OptionalUnityEngineType ("Collider");
+ commonScriptingClasses.camera = RequireUnityEngineType ("Camera");
+ commonScriptingClasses.renderTexture = RequireUnityEngineType ("RenderTexture");
+ commonScriptingClasses.layerMask = RequireUnityEngineType ("LayerMask");
+ commonScriptingClasses.waitForSeconds = RequireUnityEngineType ("WaitForSeconds");
+ commonScriptingClasses.waitForFixedUpdate = RequireUnityEngineType ("WaitForFixedUpdate");
+ commonScriptingClasses.waitForEndOfFrame = RequireUnityEngineType ("WaitForEndOfFrame");
+ commonScriptingClasses.characterInfo = RequireUnityEngineType ("CharacterInfo");
+ commonScriptingClasses.font_InvokeFontTextureRebuildCallback_Internal = RequireMethod ("UnityEngine","Font","InvokeFontTextureRebuildCallback_Internal");
+#if ENABLE_WWW
+ commonScriptingClasses.www = OptionalUnityEngineType ("WWW");
+#endif
+#if UNITY_WII
+ commonScriptingClasses.waitAsyncOperationFinish = GetBuiltinScriptingClass ("WaitAsyncOperationFinish");
+#endif
+ commonScriptingClasses.lodMesh = OptionalUnityEngineType ("Mesh");
+ commonScriptingClasses.coroutine = RequireUnityEngineType ("Coroutine");
+ commonScriptingClasses.collision = OptionalUnityEngineType ("Collision");
+ commonScriptingClasses.contactPoint = OptionalUnityEngineType ("ContactPoint");
+ commonScriptingClasses.controllerColliderHit = OptionalUnityEngineType ("ControllerColliderHit");
+ commonScriptingClasses.collision2D = OptionalUnityEngineType ("Collision2D");
+ commonScriptingClasses.contactPoint2D = OptionalUnityEngineType ("ContactPoint2D");
+ commonScriptingClasses.unityEngineObject = RequireUnityEngineType ("Object");
+ commonScriptingClasses.event = RequireUnityEngineType ("Event");
+#if ENABLE_MONO
+ commonScriptingClasses.serializeField = RequireUnityEngineType ("SerializeField");
+ commonScriptingClasses.serializePrivateVariables = GetBuiltinScriptingClass ("SerializePrivateVariables");
+ commonScriptingClasses.hideInInspector = GetBuiltinScriptingClass ("HideInInspector");
+#endif
+#if ENABLE_NETWORK
+ commonScriptingClasses.RPC = OptionalUnityEngineType ("RPC");
+ commonScriptingClasses.hostData = OptionalUnityEngineType ("HostData");
+ commonScriptingClasses.bitStream = OptionalUnityEngineType ("BitStream");
+ commonScriptingClasses.networkPlayer = OptionalUnityEngineType ("NetworkPlayer");
+ commonScriptingClasses.networkViewID = OptionalUnityEngineType ("NetworkViewID");
+ commonScriptingClasses.networkMessageInfo = OptionalUnityEngineType ("NetworkMessageInfo");
+#endif // ENABLE_NETWORK
+ commonScriptingClasses.guiStyle = RequireUnityEngineType ("GUIStyle");
+ commonScriptingClasses.animationCurve = RequireUnityEngineType ("AnimationCurve");
+ commonScriptingClasses.boneWeight = RequireUnityEngineType ("BoneWeight");
+#if ENABLE_TERRAIN
+ commonScriptingClasses.terrain = OptionalUnityEngineType("Terrain");
+ commonScriptingClasses.detailPrototype = OptionalUnityEngineType ("DetailPrototype");
+ commonScriptingClasses.treePrototype = OptionalUnityEngineType ("TreePrototype");
+ commonScriptingClasses.treeInstance = OptionalUnityEngineType ("TreeInstance");
+ commonScriptingClasses.splatPrototype = OptionalUnityEngineType ("SplatPrototype");
+#endif
+ commonScriptingClasses.animationEvent = RequireUnityEngineType ("AnimationEvent");
+ commonScriptingClasses.assetBundleRequest = RequireUnityEngineType ("AssetBundleRequest");
+ commonScriptingClasses.asyncOperation = RequireUnityEngineType ("AsyncOperation");
+#if ENABLE_WWW
+ commonScriptingClasses.assetBundleCreateRequest = RequireUnityEngineType ("AssetBundleCreateRequest");
+ // Stop scaring the customers and only load this when it's needed and available
+ commonScriptingClasses.cacheIndex = RequireUnityEngineType ("CacheIndex");
+#else
+ commonScriptingClasses.cacheIndex = SCRIPTING_NULL;
+#endif
+ commonScriptingClasses.cachedFile = OptionalUnityEngineType ("CachedFile");
+
+ commonScriptingClasses.keyframe = RequireUnityEngineType ("Keyframe");
+ commonScriptingClasses.inputEvent = RequireUnityEngineType ("Event");
+ commonScriptingClasses.imageEffectOpaque = OptionalUnityEngineType ("ImageEffectOpaque");
+ commonScriptingClasses.imageEffectTransformsToLDR = OptionalUnityEngineType ("ImageEffectTransformsToLDR");
+ commonScriptingClasses.iEnumerator = typeRegistry.GetType("System.Collections", "IEnumerator");
+ commonScriptingClasses.systemObject = typeRegistry.GetType("System","Object");
+
+
+ commonScriptingClasses.string = typeRegistry.GetType("System", "String");
+ commonScriptingClasses.int_32 = typeRegistry.GetType("System", "Int32");
+ commonScriptingClasses.floatSingle = typeRegistry.GetType("System", "Single");
+ commonScriptingClasses.floatDouble = typeRegistry.GetType("System", "Double");
+ commonScriptingClasses.byte = typeRegistry.GetType("System", "Byte");
+
+#if ENABLE_MONO || UNITY_WINRT
+ commonScriptingClasses.intptr = typeRegistry.GetType("System", "IntPtr");
+ commonScriptingClasses.uInt_16 = typeRegistry.GetType("System", "UInt16");
+ commonScriptingClasses.uInt_32 = typeRegistry.GetType("System", "UInt32");
+ commonScriptingClasses.int_16 = typeRegistry.GetType("System", "Int16");
+ commonScriptingClasses.multicastDelegate = typeRegistry.GetType("System", "MulticastDelegate");
+ commonScriptingClasses.extractRequiredComponents = RequireMethod ("UnityEngine","AttributeHelperEngine", "GetRequiredComponents");
+#endif
+
+#if ENABLE_MONO || UNITY_WP8
+ commonScriptingClasses.invokeMember = RequireMethod ("UnityEngine","SetupCoroutine", "InvokeMember");
+ commonScriptingClasses.invokeStatic = RequireMethod ("UnityEngine","SetupCoroutine", "InvokeStatic");
+#endif
+#if ENABLE_MONO
+ commonScriptingClasses.checkIsEditMode = RequireMethod ("UnityEngine","AttributeHelperEngine", "CheckIsEditorScript");
+ commonScriptingClasses.extractStacktrace = RequireMethod ("UnityEngine","StackTraceUtility", "ExtractStackTrace");
+ commonScriptingClasses.extractStringFromException = RequireMethod ("UnityEngine","StackTraceUtility", "ExtractStringFromExceptionInternal");
+ commonScriptingClasses.postprocessStacktrace = RequireMethod ("UnityEngine","StackTraceUtility", "PostprocessStacktrace");
+#endif
+
+ commonScriptingClasses.IEnumerator_MoveNext = RequireMethod("System.Collections", "IEnumerator","MoveNext");
+#if ENABLE_MONO || UNITY_WINRT
+ commonScriptingClasses.callLogCallback = RequireMethod ("UnityEngine", "Application", "CallLogCallback");
+ commonScriptingClasses.IEnumerator_Current = GetScriptingMethodRegistry().GetMethod("System.Collections", "IEnumerator","get_Current");
+ commonScriptingClasses.IDisposable_Dispose = GetScriptingMethodRegistry().GetMethod("System", "IDisposable","Dispose");
+#endif
+
+ commonScriptingClasses.beginGUI = RequireMethod ("UnityEngine", "GUIUtility", "BeginGUI");
+ commonScriptingClasses.endGUI = RequireMethod ("UnityEngine", "GUIUtility", "EndGUI");
+ commonScriptingClasses.callGUIWindowDelegate = RequireMethod ("UnityEngine", "GUI", "CallWindowDelegate");
+ commonScriptingClasses.makeMasterEventCurrent = RequireMethod ("UnityEngine", "Event", "Internal_MakeMasterEventCurrent");
+ commonScriptingClasses.doSendMouseEvents = RequireMethod ("UnityEngine", "SendMouseEvents", "DoSendMouseEvents");
+
+#if ENABLE_MONO
+ commonScriptingClasses.stackTraceUtilitySetProjectFolder = RequireMethod ("UnityEngine", "StackTraceUtility", "SetProjectFolder");
+#endif
+
+#if ENABLE_MONO
+ commonScriptingClasses.enumClass = mono_get_enum_class ();
+ commonScriptingClasses.floatSingleArray = mono_array_class_get(commonScriptingClasses.floatSingle, 1);
+#endif // ENABLE_MONO
+
+
+#if UNITY_EDITOR
+ commonScriptingClasses.monoReloadableIntPtr = GetMonoManager().GetBuiltinEditorMonoClass ("MonoReloadableIntPtr");
+ commonScriptingClasses.monoReloadableIntPtrClear = GetMonoManager().GetBuiltinEditorMonoClass ("MonoReloadableIntPtrClear");
+ commonScriptingClasses.gameViewStatsGUI = RequireMethod ("UnityEditor", "GameViewGUI", "GameViewStatsGUI");
+ commonScriptingClasses.beginHandles = RequireMethod ("UnityEditor", "HandleUtility", "BeginHandles");
+ commonScriptingClasses.endHandles = RequireMethod ("UnityEditor", "HandleUtility", "EndHandles");
+ commonScriptingClasses.setViewInfo = RequireMethod ("UnityEditor", "HandleUtility", "SetViewInfo");
+ commonScriptingClasses.handleControlID = RequireMethod ("UnityEditor","EditorGUIUtility","HandleControlID");
+ commonScriptingClasses.callGlobalEventHandler = RequireMethod ("UnityEditor", "EditorApplication", "Internal_CallGlobalEventHandler");
+ commonScriptingClasses.callAnimationClipAwake = RequireMethod ("UnityEditor", "AnimationUtility", "Internal_CallAnimationClipAwake");
+ commonScriptingClasses.statusBarChanged = RequireMethod ("UnityEditor", "AppStatusBar", "StatusChanged");
+ commonScriptingClasses.lightmappingDone = RequireMethod ("UnityEditor", "LightmappingWindow", "LightmappingDone");
+ commonScriptingClasses.consoleLogChanged = RequireMethod ("UnityEditor", "ConsoleWindow", "LogChanged");
+ commonScriptingClasses.clearUndoSnapshotTarget = RequireMethod ("UnityEditor", "Undo", "ClearSnapshotTarget");
+ commonScriptingClasses.repaintAllProfilerWindows = RequireMethod ("UnityEditor", "ProfilerWindow", "RepaintAllProfilerWindows");
+ commonScriptingClasses.getGameViewAspectRatio = RequireMethod ("UnityEditor", "CameraEditor", "GetGameViewAspectRatio");
+ commonScriptingClasses.substanceMaterialInformation = RequireType("UnityEditor", "ProceduralMaterialInformation" );
+ commonScriptingClasses.propertyModification = RequireType("UnityEditor", "PropertyModification" );
+ commonScriptingClasses.undoPropertyModification = RequireType("UnityEditor", "UndoPropertyModification" );
+ commonScriptingClasses.callbackOrderAttribute = RequireType("UnityEditor", "CallbackOrderAttribute" );
+ commonScriptingClasses.postProcessBuildAttribute = RequireType ("UnityEditor.Callbacks", "PostProcessBuildAttribute");
+ commonScriptingClasses.postProcessSceneAttribute = RequireType ("UnityEditor.Callbacks", "PostProcessSceneAttribute");
+ commonScriptingClasses.didReloadScripts = RequireType ("UnityEditor.Callbacks", "DidReloadScripts");
+ commonScriptingClasses.onOpenAssetAttribute = RequireType ("UnityEditor.Callbacks", "OnOpenAssetAttribute");
+
+ commonScriptingClasses.animationClipSettings = RequireType ("UnityEditor","AnimationClipSettings");
+ commonScriptingClasses.muscleClipQualityInfo = RequireType ("UnityEditor","MuscleClipQualityInfo");
+#endif // UNITY_EDITOR
+
+ commonScriptingClasses.gradient = RequireUnityEngineType ("Gradient");
+ commonScriptingClasses.gradientColorKey = RequireUnityEngineType ("GradientColorKey");
+ commonScriptingClasses.gradientAlphaKey = RequireUnityEngineType ("GradientAlphaKey");
+
+#if ENABLE_SUBSTANCE
+ commonScriptingClasses.substancePropertyDescription = OptionalUnityEngineType ("ProceduralPropertyDescription");
+#endif
+
+ commonScriptingClasses.animatorStateInfo= RequireUnityEngineType ("AnimatorStateInfo");
+ commonScriptingClasses.animatorTransitionInfo= RequireUnityEngineType ("AnimatorTransitionInfo");
+ commonScriptingClasses.animationInfo= RequireUnityEngineType ("AnimationInfo");
+ commonScriptingClasses.skeletonBone = RequireUnityEngineType ("SkeletonBone");
+ commonScriptingClasses.humanBone = RequireUnityEngineType ("HumanBone");
+
+#if ENABLE_GAMECENTER
+ commonScriptingClasses.gameCenter = GetMonoManager().GetMonoClass("GameCenterPlatform", "UnityEngine.SocialPlatforms.GameCenter");
+ commonScriptingClasses.gcAchievement = GetMonoManager().GetMonoClass("GcAchievementData", "UnityEngine.SocialPlatforms.GameCenter");
+ commonScriptingClasses.gcAchievementDescription = GetMonoManager().GetMonoClass("GcAchievementDescriptionData", "UnityEngine.SocialPlatforms.GameCenter");
+ commonScriptingClasses.gcScore = GetMonoManager().GetMonoClass("GcScoreData", "UnityEngine.SocialPlatforms.GameCenter");
+ commonScriptingClasses.gcUserProfile = GetMonoManager().GetMonoClass("GcUserProfileData", "UnityEngine.SocialPlatforms.GameCenter");
+#endif
+
+#if ENABLE_WEBCAM
+ // We support stripping there
+ #if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+ commonScriptingClasses.webCamDevice = OptionalUnityEngineType("WebCamDevice");
+ #else
+ commonScriptingClasses.webCamDevice = RequireUnityEngineType("WebCamDevice");
+ #endif
+#endif
+
+ commonScriptingClasses.display = RequireUnityEngineType("Display");
+ commonScriptingClasses.displayRecreateDisplayList = RequireMethod("UnityEngine","Display","RecreateDisplayList");
+ commonScriptingClasses.displayFireDisplaysUpdated = RequireMethod("UnityEngine","Display","FireDisplaysUpdated");
+
+#if UNITY_IPHONE
+ commonScriptingClasses.adBannerView = OptionalUnityEngineType("ADBannerView");
+ commonScriptingClasses.adInterstitialAd = OptionalUnityEngineType("ADInterstitialAd");
+ commonScriptingClasses.adFireBannerWasClicked = OptionalMethod ("UnityEngine", "ADBannerView", "FireBannerWasClicked");
+ commonScriptingClasses.adFireBannerWasLoaded = OptionalMethod ("UnityEngine", "ADBannerView", "FireBannerWasLoaded");
+ commonScriptingClasses.adFireInterstitialWasLoaded = OptionalMethod ("UnityEngine", "ADInterstitialAd", "FireInterstitialWasLoaded");
+#endif
+
+
+#endif
+}
+
+void ClearCommonScriptingClasses(CommonScriptingClasses& commonScriptingClasses)
+{
+ memset(&commonScriptingClasses, 0, sizeof(commonScriptingClasses));
+}
+
+#endif
diff --git a/Runtime/Scripting/CommonScriptingClasses.h b/Runtime/Scripting/CommonScriptingClasses.h
new file mode 100644
index 0000000..6699706
--- /dev/null
+++ b/Runtime/Scripting/CommonScriptingClasses.h
@@ -0,0 +1,206 @@
+#pragma once
+#if ENABLE_SCRIPTING
+#include "Runtime/Mono/MonoTypes.h"
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+
+struct CommonScriptingClasses
+{
+ ScriptingClassPtr monoBehaviour;
+ ScriptingClassPtr component;
+ ScriptingClassPtr scriptableObject;
+ ScriptingClassPtr vector2;
+ ScriptingClassPtr vector3;
+ ScriptingClassPtr vector4;
+ ScriptingClassPtr rect;
+ ScriptingClassPtr rectOffset;
+ ScriptingClassPtr quaternion;
+ ScriptingClassPtr matrix4x4;
+ ScriptingClassPtr bounds;
+ ScriptingClassPtr resolution;
+ ScriptingClassPtr particle;
+ ScriptingClassPtr color;
+ ScriptingClassPtr color32;
+ ScriptingClassPtr raycastHit;
+ ScriptingClassPtr raycastHit2D;
+ ScriptingClassPtr animationState;
+ ScriptingClassPtr collider;
+ ScriptingClassPtr camera;
+ ScriptingClassPtr renderTexture;
+ ScriptingClassPtr layerMask;
+ ScriptingClassPtr serializeField;
+ ScriptingClassPtr enumClass;
+ ScriptingClassPtr iEnumerator;
+ ScriptingClassPtr systemObject;
+
+#if !UNITY_FLASH
+ ScriptingClassPtr intptr;
+ ScriptingClassPtr uInt_16;
+ ScriptingClassPtr uInt_32;
+ ScriptingClassPtr int_16;
+ ScriptingClassPtr multicastDelegate;
+#endif
+
+ ScriptingClassPtr byte;
+ ScriptingClassPtr int_32;
+ ScriptingClassPtr string;
+ ScriptingClassPtr floatSingle;
+ ScriptingClassPtr floatDouble;
+ ScriptingClassPtr waitForSeconds;
+ ScriptingClassPtr waitForFixedUpdate;
+ ScriptingClassPtr waitForEndOfFrame;
+
+ ScriptingClassPtr characterInfo;
+ ScriptingMethodPtr font_InvokeFontTextureRebuildCallback_Internal;
+#if ENABLE_WWW
+ ScriptingClassPtr assetBundleCreateRequest;
+ ScriptingClassPtr www;
+#endif
+#if UNITY_WII || UNITY_PS3 || UNITY_XENON
+ ScriptingClassPtr waitAsyncOperationFinish;
+#endif
+ ScriptingClassPtr meshData;
+ ScriptingClassPtr lodMesh;
+ ScriptingClassPtr coroutine;
+ ScriptingClassPtr collision;
+ ScriptingClassPtr contactPoint;
+ ScriptingClassPtr controllerColliderHit;
+ ScriptingClassPtr collision2D;
+ ScriptingClassPtr contactPoint2D;
+ ScriptingClassPtr event;
+ ScriptingClassPtr unityEngineObject;
+ ScriptingClassPtr hideInInspector;
+ ScriptingClassPtr serializePrivateVariables;
+#if ENABLE_NETWORK
+ ScriptingClassPtr RPC;
+ ScriptingClassPtr hostData;
+ ScriptingClassPtr bitStream;
+ ScriptingClassPtr networkPlayer;
+ ScriptingClassPtr networkViewID;
+ ScriptingClassPtr networkMessageInfo;
+#endif
+
+ ScriptingClassPtr guiStyle;
+ ScriptingClassPtr animationCurve;
+ ScriptingClassPtr keyframe;
+ ScriptingClassPtr boneWeight;
+
+#if ENABLE_MONO || UNITY_WP8
+ ScriptingMethodPtr invokeMember;
+ ScriptingMethodPtr invokeStatic;
+#endif
+#if ENABLE_MONO
+ ScriptingMethodPtr checkIsEditMode;
+ ScriptingMethodPtr extractStacktrace;
+ ScriptingMethodPtr extractStringFromException;
+ ScriptingMethodPtr postprocessStacktrace;
+#endif
+ ScriptingMethodPtr IEnumerator_MoveNext;
+#if ENABLE_MONO || UNITY_WINRT
+ ScriptingMethodPtr callLogCallback;
+ ScriptingMethodPtr IEnumerator_Current;
+ ScriptingMethodPtr IDisposable_Dispose;
+ ScriptingMethodPtr extractRequiredComponents;
+#endif
+
+ ScriptingMethodPtr beginGUI;
+ ScriptingMethodPtr endGUI;
+ ScriptingMethodPtr callGUIWindowDelegate;
+
+#if ENABLE_TERRAIN
+ ScriptingClassPtr terrain;
+ ScriptingClassPtr detailPrototype;
+ ScriptingClassPtr treePrototype;
+ ScriptingClassPtr treeInstance;
+ ScriptingClassPtr splatPrototype;
+#endif
+ ScriptingClassPtr animationEvent;
+ ScriptingClassPtr assetBundleRequest;
+ ScriptingClassPtr asyncOperation;
+ ScriptingClassPtr cacheIndex;
+ ScriptingClassPtr cachedFile;
+ ScriptingClassPtr inputEvent;
+ ScriptingClassPtr imageEffectOpaque;
+ ScriptingClassPtr muscleBoneInfo;
+ ScriptingClassPtr animationClipSettings;
+ ScriptingClassPtr muscleClipQualityInfo;
+ ScriptingClassPtr imageEffectTransformsToLDR;
+
+ ScriptingMethodPtr makeMasterEventCurrent;
+ ScriptingMethodPtr doSendMouseEvents;
+ ScriptingMethodPtr stackTraceUtilitySetProjectFolder;
+
+
+
+#if UNITY_EDITOR
+ ScriptingClassPtr monoReloadableIntPtr;
+ ScriptingClassPtr monoReloadableIntPtrClear;
+ ScriptingMethodPtr gameViewStatsGUI;
+ ScriptingMethodPtr beginHandles;
+ ScriptingMethodPtr endHandles;
+ ScriptingMethodPtr setViewInfo;
+ ScriptingMethodPtr handleControlID;
+ ScriptingMethodPtr callGlobalEventHandler;
+ ScriptingMethodPtr callAnimationClipAwake;
+ ScriptingMethodPtr statusBarChanged;
+ ScriptingMethodPtr lightmappingDone;
+ ScriptingMethodPtr consoleLogChanged;
+ ScriptingMethodPtr clearUndoSnapshotTarget;
+ ScriptingMethodPtr repaintAllProfilerWindows;
+ ScriptingMethodPtr getGameViewAspectRatio;
+ ScriptingClassPtr substanceMaterialInformation;
+ ScriptingClassPtr propertyModification;
+ ScriptingClassPtr undoPropertyModification;
+ ScriptingClassPtr exportExtensionClassAttribute;
+#endif // #if UNITY_EDITOR
+
+ ScriptingClassPtr gradient;
+ ScriptingClassPtr gradientColorKey;
+ ScriptingClassPtr gradientAlphaKey;
+ ScriptingClassPtr callbackOrderAttribute;
+ ScriptingClassPtr postProcessBuildAttribute;
+ ScriptingClassPtr postProcessSceneAttribute;
+ ScriptingClassPtr didReloadScripts;
+ ScriptingClassPtr onOpenAssetAttribute;
+
+#if ENABLE_SUBSTANCE
+ ScriptingClassPtr substancePropertyDescription;
+#endif
+
+ ScriptingClassPtr animatorStateInfo;
+ ScriptingClassPtr animatorTransitionInfo;
+ ScriptingClassPtr animationInfo;
+ ScriptingClassPtr floatSingleArray;
+ ScriptingClassPtr skeletonBone;
+ ScriptingClassPtr humanBone;
+
+#if ENABLE_GAMECENTER
+ ScriptingClassPtr gameCenter;
+ ScriptingClassPtr gcScore;
+ ScriptingClassPtr gcAchievement;
+ ScriptingClassPtr gcAchievementDescription;
+ ScriptingClassPtr gcUserProfile;
+#endif
+
+#if ENABLE_WEBCAM
+ ScriptingClassPtr webCamDevice;
+#endif
+
+ ScriptingClassPtr display;
+ ScriptingMethodPtr displayRecreateDisplayList;
+ ScriptingMethodPtr displayFireDisplaysUpdated;
+
+#if UNITY_IPHONE
+ ScriptingClassPtr adBannerView;
+ ScriptingClassPtr adInterstitialAd;
+ ScriptingMethodPtr adFireBannerWasClicked;
+ ScriptingMethodPtr adFireBannerWasLoaded;
+ ScriptingMethodPtr adFireInterstitialWasLoaded;
+#endif
+};
+
+void FillCommonScriptingClasses(CommonScriptingClasses& commonScriptingClasses);
+void ClearCommonScriptingClasses(CommonScriptingClasses& commonScriptingClasses);
+
+#define MONO_COMMON GetScriptingManager().GetCommonClasses()
+
+#endif
diff --git a/Runtime/Scripting/DelayedCallUtility.cpp b/Runtime/Scripting/DelayedCallUtility.cpp
new file mode 100644
index 0000000..5e7d9f0
--- /dev/null
+++ b/Runtime/Scripting/DelayedCallUtility.cpp
@@ -0,0 +1,73 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_SCRIPTING
+
+#include "Runtime/Scripting/DelayedCallUtility.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/GameCode/CallDelayed.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoScript.h"
+
+static void ForwardInvokeDelayed (Object* o, void* userData)
+{
+ const char* methodName = (const char*)userData;
+ MonoBehaviour* behaviour = static_cast<MonoBehaviour*> (o);
+ if (behaviour->GetInstance ())
+ {
+ bool didCall = behaviour->CallMethodInactive (methodName);
+ #if !DEPLOY_OPTIMIZED
+ if (!didCall)
+ LogStringObject ("Trying to Invoke method: " + behaviour->GetScript ()->GetScriptClassName () + "." + methodName + " couldn't be called.", o);
+ #else
+ UNUSED(didCall);
+ #endif
+
+ }
+}
+
+static void ForwardInvokeDelayedCleanup (void* userData)
+{
+ ScriptingStringToAllocatedChars_Free ((char*)userData);
+}
+
+void InvokeDelayed (MonoBehaviour& behaviour, ICallString& monoMethodName, float time, float repeatRate)
+{
+ char* methodName = ScriptingStringToAllocatedChars (monoMethodName);
+ if (repeatRate > 0.00001F || repeatRate == 0.0F)
+ CallDelayed (&ForwardInvokeDelayed, &behaviour, time, methodName, repeatRate, &ForwardInvokeDelayedCleanup);
+ else
+ Scripting::RaiseMonoException ("Invoke repeat rate has to be larger than 0.00001F)");
+}
+
+
+static bool ShouldCancelInvoke (void* lhs, void* rhs)
+{
+ return strcmp ((char*)lhs, (char*)rhs) == 0;
+}
+
+void CancelInvoke (MonoBehaviour& behaviour, ICallString& monoMethodName)
+{
+ char* methodName = ScriptingStringToAllocatedChars (monoMethodName);
+ GetDelayedCallManager ().CancelCallDelayed (&behaviour, &ForwardInvokeDelayed, &ShouldCancelInvoke, methodName);
+ ScriptingStringToAllocatedChars_Free (methodName);
+}
+
+void CancelInvoke (MonoBehaviour& behaviour)
+{
+ GetDelayedCallManager ().CancelCallDelayed (&behaviour, &ForwardInvokeDelayed, NULL, NULL);
+}
+
+bool IsInvoking (MonoBehaviour& behaviour)
+{
+ return GetDelayedCallManager ().HasDelayedCall (&behaviour, &ForwardInvokeDelayed, NULL, NULL);
+}
+
+bool IsInvoking (MonoBehaviour& behaviour, ICallString& monoMethodName)
+{
+ char* methodName = ScriptingStringToAllocatedChars (monoMethodName);
+ bool result = GetDelayedCallManager ().HasDelayedCall (&behaviour, &ForwardInvokeDelayed, &ShouldCancelInvoke, methodName);
+ ScriptingStringToAllocatedChars_Free (methodName);
+ return result;
+}
+
+#endif
diff --git a/Runtime/Scripting/DelayedCallUtility.h b/Runtime/Scripting/DelayedCallUtility.h
new file mode 100644
index 0000000..aae493d
--- /dev/null
+++ b/Runtime/Scripting/DelayedCallUtility.h
@@ -0,0 +1,16 @@
+#ifndef _DELAYEDCALLUTILITYH_
+#define _DELAYEDCALLUTILITYH_
+
+#if ENABLE_SCRIPTING
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/Scripting/ICallString.h"
+
+class MonoBehaviour;
+
+void InvokeDelayed (MonoBehaviour& behaviour, ICallString& monoMethodName, float time, float repeatRate);
+void CancelInvoke (MonoBehaviour& behaviour, ICallString& monoMethodName);
+void CancelInvoke (MonoBehaviour& behaviour);
+bool IsInvoking (MonoBehaviour& behaviour, ICallString& monoMethodName);
+bool IsInvoking (MonoBehaviour& behaviour);
+#endif
+#endif
diff --git a/Runtime/Scripting/GetComponent.cpp b/Runtime/Scripting/GetComponent.cpp
new file mode 100644
index 0000000..d30bb41
--- /dev/null
+++ b/Runtime/Scripting/GetComponent.cpp
@@ -0,0 +1,260 @@
+#include "UnityPrefix.h"
+#if ENABLE_SCRIPTING
+#include "ScriptingUtility.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Animation/Animation.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Utilities/dense_hash_map.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Scripting.h"
+
+inline static bool ComponentMatchesRequirement_ByScriptingClass(Unity::GameObject& go, int componentIndex, ScriptingClassPtr compareClass)
+{
+ int classID = go.GetComponentClassIDAtIndex(componentIndex);
+ if (classID != ClassID (MonoBehaviour))
+ return false;
+
+ Unity::Component& component = go.GetComponentAtIndex(componentIndex);
+ MonoBehaviour& behaviour = static_cast<MonoBehaviour&> (component);
+ ScriptingClassPtr klass = behaviour.GetClass ();
+ if (klass == SCRIPTING_NULL)
+ return false;
+
+ if (klass == compareClass)
+ return true;
+
+ return scripting_class_is_subclass_of (klass, compareClass);
+}
+
+inline static bool ComponentMatchesRequirement_ByClassID(Unity::GameObject& go, int componentIndex, int compareClassID, bool isCompareClassSealed)
+{
+ int classID = go.GetComponentClassIDAtIndex(componentIndex);
+
+ if (classID == compareClassID)
+ return true;
+
+ if (isCompareClassSealed)
+ return false;
+
+ return Object::IsDerivedFromClassID(classID,compareClassID);
+}
+
+namespace SearchMethod
+{
+ enum {
+ ByClassID,
+ ByScriptingClass,
+ DontEvenStart
+ };
+}
+
+template<bool getOnlyOne, int searchMethod>
+static bool GetComponentsImplementation (GameObject& go, bool includeInactive, void* compareData, void* output)
+{
+ if (!includeInactive && !go.IsActive())
+ return false;
+
+ bool foundAtLeastOne = false;
+
+ bool isSealed;
+ UInt64 compareClassID = (UInt64)(uintptr_t)(compareData);
+ if (searchMethod == SearchMethod::ByClassID)
+ {
+ Assert(Transform::IsSealedClass());
+ Assert(Animation::IsSealedClass());
+ Assert(Camera::IsSealedClass());
+ isSealed = compareClassID == ClassID(Transform) || compareClassID == ClassID(Animation) || compareClassID == ClassID(Camera);
+ }
+
+ int count = go.GetComponentCount ();
+ for (int i = 0; i < count; i++)
+ {
+ if (searchMethod == SearchMethod::ByClassID && !ComponentMatchesRequirement_ByClassID(go,i,compareClassID,isSealed))
+ continue;
+
+ if (searchMethod == SearchMethod::ByScriptingClass && !ComponentMatchesRequirement_ByScriptingClass(go,i, reinterpret_cast<ScriptingClassPtr>(compareData)))
+ continue;
+
+ Unity::Component* component = &go.GetComponentAtIndex(i);
+ if (getOnlyOne)
+ {
+ *((Unity::Component**) output) = component;
+ return true;
+ }
+
+ dynamic_array<Unity::Component*>* manyResults = (dynamic_array<Unity::Component*>*) output;
+ if (manyResults->empty())
+ manyResults->reserve(10);
+ manyResults->push_back(component);
+ foundAtLeastOne = true;
+ }
+
+ return foundAtLeastOne;
+}
+
+template<bool getOnlyOne, int searchMethod>
+static bool GetComponentsImplementationRecurse (GameObject& go, bool includeInactive, void* compareData, void* output)
+{
+ bool foundAtLeastOne = GetComponentsImplementation<getOnlyOne, searchMethod>(go,includeInactive,compareData,output);
+ if (foundAtLeastOne && getOnlyOne)
+ return true;
+
+ // Recurse Transform hierarchy
+ Transform& transform = go.GetComponentT<Transform> (ClassID (Transform));
+ int count = transform.GetChildrenCount();
+ for (int i = 0; i < count; i++)
+ {
+ Transform& child = transform.GetChild(i);
+ foundAtLeastOne = GetComponentsImplementationRecurse<getOnlyOne,searchMethod>(child.GetGameObject(), includeInactive, compareData,output);
+ if (foundAtLeastOne && getOnlyOne)
+ return true;
+ }
+ return false;
+}
+
+#if MONO_QUALITY_ERRORS
+static bool ScriptingClassIsValidGetComponentArgument(ScriptingClass* compareClass)
+{
+ if (mono_class_is_subclass_of(compareClass, GetMonoManager().GetCommonClasses().component, true))
+ return true;
+
+ if (mono_unity_class_is_interface(compareClass))
+ return true;
+
+ return false;
+}
+
+
+static void VerifyGetComponentsOfTypeFromGameObjectArgument(GameObject& go, ScriptingClassPtr compareKlass)
+{
+ if (compareKlass == NULL)
+ {
+ ScriptWarning("GetComponent asking for invalid type", &go);
+ return;
+ }
+
+ if (ScriptingClassIsValidGetComponentArgument(compareKlass))
+ return;
+
+ const char* klassName = mono_class_get_name (compareKlass);
+ ScriptWarning(Format("GetComponent requires that the requested component '%s' derives from MonoBehaviour or Component or is an interface.", klassName), &go);
+}
+#endif
+
+static void DetermineSearchMethod(ScriptingClassPtr klass, int* outputSearchMethod, void** outputCompareData)
+{
+ int classid = GetScriptingManager().ClassIDForScriptingClass(klass);
+ if (classid == -1)
+ {
+ *outputSearchMethod = SearchMethod::ByScriptingClass;
+ *outputCompareData = (void*)klass;
+ return;
+ }
+
+ *outputCompareData = (void*) classid;
+ *outputSearchMethod = SearchMethod::ByClassID;
+}
+
+typedef bool GetComponentsFunction (GameObject& go, bool includeInactive, void* compareData, void* output);
+
+template<bool getOnlyOne>
+static GetComponentsFunction* DetermineGetComponentsImplementationFunction(int searchMethod,bool recursive)
+{
+ if (searchMethod == SearchMethod::ByClassID && recursive)
+ return GetComponentsImplementationRecurse<getOnlyOne,SearchMethod::ByClassID>;
+
+ if (searchMethod == SearchMethod::ByClassID && !recursive)
+ return GetComponentsImplementation<getOnlyOne,SearchMethod::ByClassID>;
+
+ if (searchMethod == SearchMethod::ByScriptingClass && recursive)
+ return GetComponentsImplementationRecurse<getOnlyOne,SearchMethod::ByScriptingClass>;
+
+ if (searchMethod == SearchMethod::ByScriptingClass && !recursive)
+ return GetComponentsImplementation<getOnlyOne,SearchMethod::ByScriptingClass>;
+
+ if (searchMethod == SearchMethod::DontEvenStart)
+ return NULL;
+
+ return NULL;
+}
+
+template<bool getOnlyOne>
+static void GetComponentsOfTypeFromGameObject(GameObject& go, ScriptingClassPtr compareKlass, bool generateErrors, bool recursive, bool includeInactive, void* results)
+{
+#if MONO_QUALITY_ERRORS
+ if (generateErrors)
+ VerifyGetComponentsOfTypeFromGameObjectArgument(go,compareKlass);
+#endif
+
+ if (compareKlass == SCRIPTING_NULL)
+ return;
+
+ int searchMethod;
+ void* compareData;
+
+ DetermineSearchMethod(compareKlass,&searchMethod,&compareData);
+
+ GetComponentsFunction* getComponents = DetermineGetComponentsImplementationFunction<getOnlyOne>(searchMethod,recursive);
+ if (getComponents)
+ getComponents(go,includeInactive,compareData,results);
+}
+
+ScriptingArrayPtr ScriptingGetComponentsOfType (GameObject& go, ScriptingObjectPtr systemTypeInstance, bool useSearchTypeAsArrayReturnType, bool recursive, bool includeInactive)
+{
+ dynamic_array<Unity::Component*> components(kMemTempAlloc);
+ ScriptingClassPtr compareKlass = GetScriptingTypeRegistry().GetType (systemTypeInstance);
+ GetComponentsOfTypeFromGameObject<false>(go,compareKlass, true, recursive, includeInactive, &components);
+
+#if ENABLE_MONO || UNITY_WINRT
+ ScriptingClassPtr componentClass = GetMonoManager ().ClassIDToScriptingClass (ClassID (Component));
+ ScriptingClassPtr classForArray = useSearchTypeAsArrayReturnType ? compareKlass : componentClass;
+ // ToDo: why aren't we using useSearchTypeAsArrayReturnType for flash ?! Lucas, Ralph?
+#elif UNITY_FLASH
+ ScriptingClassPtr classForArray = compareKlass;
+#endif
+ return CreateScriptingArrayFromUnityObjects(components,classForArray);
+}
+
+ScriptingObjectPtr ScriptingGetComponentOfType (GameObject& go, ScriptingObjectPtr systemTypeInstance, bool generateErrors)
+{
+ if (systemTypeInstance == SCRIPTING_NULL)
+ {
+ Scripting::RaiseArgumentException ("Type can not be null.");
+ return SCRIPTING_NULL;
+ }
+
+ ScriptingClassPtr compareKlass = GetScriptingTypeRegistry().GetType (systemTypeInstance);
+ Unity::Component* result = NULL;
+
+ GetComponentsOfTypeFromGameObject<true>(go,compareKlass, generateErrors, false, true, &result);
+
+ if (result != NULL)
+ return Scripting::ScriptingWrapperFor(result);
+
+#if MONO_QUALITY_ERRORS
+ if(generateErrors)
+ return MonoObjectNULL(compareKlass, MissingComponentString(go,compareKlass));
+#endif
+
+ return SCRIPTING_NULL;
+}
+
+ScriptingObjectPtr ScriptingGetComponentOfType (GameObject& go, ScriptingClassPtr systemTypeInstance)
+{
+ Unity::Component* result = NULL;
+ GetComponentsOfTypeFromGameObject<true>(go, systemTypeInstance, false, false, true, &result);
+ if (result)
+ return Scripting::ScriptingWrapperFor(result);
+
+ return SCRIPTING_NULL;
+}
+
+
+#endif
diff --git a/Runtime/Scripting/GetComponent.h b/Runtime/Scripting/GetComponent.h
new file mode 100644
index 0000000..f9aff0a
--- /dev/null
+++ b/Runtime/Scripting/GetComponent.h
@@ -0,0 +1,16 @@
+#ifndef GETCOMPONENT_H
+#define GETCOMPONENT_H
+
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Mono/MonoIncludes.h"
+
+#if ENABLE_SCRIPTING
+
+ScriptingArrayPtr ScriptingGetComponentsOfType (Unity::GameObject& go, ScriptingObjectPtr reflectionTypeObject, bool useSearchTypeAsArrayReturnType, bool recursive, bool includeInactive);
+ScriptingObjectPtr ScriptingGetComponentOfType (Unity::GameObject& go, ScriptingObjectPtr reflectionTypeObject, bool generateErrors = true);
+ScriptingObjectPtr ScriptingGetComponentOfType (GameObject& go, ScriptingClassPtr systemTypeInstance);
+
+#endif
+
+#endif
diff --git a/Runtime/Scripting/ICallString.cpp b/Runtime/Scripting/ICallString.cpp
new file mode 100644
index 0000000..ad1cceb
--- /dev/null
+++ b/Runtime/Scripting/ICallString.cpp
@@ -0,0 +1,40 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_SCRIPTING
+
+#if !UNITY_FLASH
+#include "ICallString.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+
+
+#if ENABLE_MONO
+std::string ICallString::AsUTF8() const
+{
+ return scripting_cpp_string_for(str).c_str();
+}
+#elif UNITY_WINRT
+#include "PlatformDependent/MetroPlayer/MetroUtils.h"
+std::string ICallString::AsUTF8() const
+{
+ return ConvertStringToUtf8(str);
+}
+#endif
+
+#if ENABLE_MONO
+// todo: remove this useless include once we figure out where to take mono_string_length from.
+#include "Runtime/Scripting/ScriptingUtility.h"
+#endif
+
+int ICallString::Length()
+{
+#if ENABLE_MONO
+ return mono_string_length(str);
+#elif UNITY_WINRT
+ return wcslen(str);
+ //return safe_cast<Platform::String^>(str)->Length();
+#endif
+}
+#endif
+
+#endif
+
diff --git a/Runtime/Scripting/ICallString.h b/Runtime/Scripting/ICallString.h
new file mode 100644
index 0000000..6c096d2
--- /dev/null
+++ b/Runtime/Scripting/ICallString.h
@@ -0,0 +1,91 @@
+#pragma once
+
+#if ENABLE_SCRIPTING
+
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include <string>
+
+#if ENABLE_MONO
+struct ICallString
+{
+ MonoString* str;
+
+ EXPORT_COREMODULE std::string AsUTF8() const;
+ operator std::string () const { return AsUTF8(); }
+ int Length();
+ bool IsNull () {return !str;}
+
+ MonoString* GetNativeString() {return str;}
+};
+#endif
+
+#if UNITY_FLASH
+struct ICallString
+{
+ const char* utf8stream;
+
+ std::string AsUTF8() const { return utf8stream; }
+ operator std::string () const { return AsUTF8(); }
+
+ int Length() { return strlen(utf8stream); }
+ bool IsNull () {return !utf8stream;}
+ const char* GetNativeString() {return utf8stream;}
+};
+#endif
+
+#if UNITY_WINRT
+struct ICallString
+{
+private:
+ const wchar_t* str;
+ //Platform::Object^ str;
+
+ ICallString(const ICallString& other){}
+ ICallString& operator = (const ICallString& rhs)
+ {
+ return *this;
+ }
+public:
+ ICallString(){}
+ ICallString(const wchar_t* _str)
+ : str(_str)
+ {
+ }
+ std::string AsUTF8() const;
+ operator std::string () const { return AsUTF8(); }
+
+ int Length();
+ bool IsNull () {return (str == SCRIPTING_NULL);}
+
+ ScriptingStringPtr GetNativeString() {return ref new Platform::String(str);}
+};
+#endif
+
+/*
+//For the next step
+
+struct structwithsomename
+{
+char buf[256];
+char* fallback;
+
+structwithsomename(ICallString& ics)
+{
+
+
+}
+
+~structwithsomename()
+{
+delete fallback;
+}
+
+const char* Get()
+{
+fastutf8into(buf);
+}
+
+const char* operator () { return Get(); }
+};*/
+
+#endif
diff --git a/Runtime/Scripting/MonoManager_Flash.cpp b/Runtime/Scripting/MonoManager_Flash.cpp
new file mode 100644
index 0000000..22cc7f4
--- /dev/null
+++ b/Runtime/Scripting/MonoManager_Flash.cpp
@@ -0,0 +1,41 @@
+#include "UnityPrefix.h"
+
+#include "MonoManager_Flash.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Scripting/CommonScriptingClasses.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+
+#include "Runtime/Scripting/Backend/Flash/ScriptingTypeProvider_Flash.h"
+#include "Runtime/Scripting/Backend/Flash/ScriptingMethodFactory_Flash.h"
+
+#include "Runtime/Scripting/Backend/ScriptingMethodRegistry.h"
+
+#include "Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.h"
+
+MonoManager::MonoManager (MemLabelId label, ObjectCreationMode mode)
+ : ScriptingManager(label, mode, UNITY_NEW( ScriptingTypeProvider_Flash(), kMemManager), UNITY_NEW( ScriptingMethodFactory_Flash(), kMemManager))
+{
+ FillCommonScriptingClasses(m_CommonScriptingClasses);
+}
+
+MonoManager::~MonoManager ()
+{
+
+}
+
+template<class TransferFunction>
+void MonoManager::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion(2);
+ transfer.Transfer (m_MonoScriptManager.m_RuntimeScripts, "m_Scripts");
+}
+
+
+IMPLEMENT_OBJECT_SERIALIZE (MonoManager)
+IMPLEMENT_CLASS (MonoManager)
+GET_MANAGER (MonoManager)
+GET_MANAGER_PTR (MonoManager)
diff --git a/Runtime/Scripting/MonoManager_Flash.h b/Runtime/Scripting/MonoManager_Flash.h
new file mode 100644
index 0000000..2367fbd
--- /dev/null
+++ b/Runtime/Scripting/MonoManager_Flash.h
@@ -0,0 +1,27 @@
+#ifndef MONOMANAGER_FLASH_H
+#define MONOMANAGER_FLASH_H
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Mono/MonoScriptManager.h"
+#include "Runtime/Scripting/CommonScriptingClasses.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+
+class MonoManager : public ScriptingManager
+{
+public:
+ REGISTER_DERIVED_CLASS (MonoManager, GlobalGameManager)
+ DECLARE_OBJECT_SERIALIZE (MonoManager)
+
+ MonoManager (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~MonoManager (); declared-by-macro
+
+ ScriptingObjectPtr CreateInstance(ScriptingClassPtr klass);
+
+private:
+
+};
+
+MonoManager& GetMonoManager ();
+MonoManager* GetMonoManagerPtr ();
+
+#endif
diff --git a/Runtime/Scripting/MonoManager_WinRT.cpp b/Runtime/Scripting/MonoManager_WinRT.cpp
new file mode 100644
index 0000000..7719478
--- /dev/null
+++ b/Runtime/Scripting/MonoManager_WinRT.cpp
@@ -0,0 +1,105 @@
+#include "UnityPrefix.h"
+
+#include "MonoManager_WinRT.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Scripting/CommonScriptingClasses.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+
+#include "Runtime/Scripting/Backend/Metro/ScriptingTypeProvider_Metro.h"
+#include "Runtime/Scripting/Backend/Metro/ScriptingMethodFactory_Metro.h"
+
+#include "Runtime/Scripting/Backend/ScriptingMethodRegistry.h"
+
+#include "Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.h"
+
+#include "PlatformDependent/MetroPlayer/MetroUtils.h"
+
+MonoManager::MonoManager (MemLabelId label, ObjectCreationMode mode)
+ : ScriptingManager(label, mode, UNITY_NEW( ScriptingTypeProvider_Metro(), kMemManager), UNITY_NEW( ScriptingMethodFactory_Metro(), kMemManager))
+{
+ METRO_DBG_MARK_TIME("MonoManager::ctor");
+ InputString data;
+ const char* managedAssemblies = "managedAssemblies.txt";
+ if (ReadStringFromFile(&data, PathToAbsolutePath(managedAssemblies)))
+ {
+ GetWinRTUtils()->LoadAssemblies(ConvertUtf8ToString(data.c_str()));
+ }
+ else
+ {
+ FatalErrorMsg("Failed to load %s", managedAssemblies);
+ }
+ METRO_DBG_MARK_TIME("MonoManager::FillCommonScriptingClasses begin...");
+ FillCommonScriptingClasses(m_CommonScriptingClasses);
+ METRO_DBG_MARK_TIME("MonoManager::FillCommonScriptingClasses end...");
+ SetupExceptionHandler();
+ SetupMarshalCallbacks();
+ void RegisterAllInternalCalls();
+ RegisterAllInternalCalls();
+
+# if ENABLE_SERIALIZATION_BY_CODEGENERATION
+
+ s_WinRTBridge->SetupSerializationReader(
+ (long long)(NativeExt_MonoBehaviourSerialization_ReaderAlign),
+ (long long)(NativeExt_MonoBehaviourSerialization_ReadBuffer),
+ (long long)(NativeExt_MonoBehaviourSerialization_ReadUnityEngineObject),
+ (long long)(NativeExt_MonoBehaviourSerialization_ReadGUIStyle),
+ (long long)(NativeExt_MonoBehaviourSerialization_ReadRectOffset),
+ (long long)(NativeExt_MonoBehaviourSerialization_ReadAnimationCurve),
+ (long long)(NativeExt_MonoBehaviourSerialization_ReadGradient));
+
+ s_WinRTBridge->SetupSerializationWriter(
+ (long long)(NativeExt_MonoBehaviourSerialization_WriterAlign),
+ (long long)(NativeExt_MonoBehaviourSerialization_WriteBuffer),
+ (long long)(NativeExt_MonoBehaviourSerialization_WriteUnityEngineObject),
+ (long long)(NativeExt_MonoBehaviourSerialization_WriteGUIStyle),
+ (long long)(NativeExt_MonoBehaviourSerialization_WriteRectOffset),
+ (long long)(NativeExt_MonoBehaviourSerialization_WriteAnimationCurve),
+ (long long)(NativeExt_MonoBehaviourSerialization_WriteGradient));
+
+ s_WinRTBridge->SetupSerializationRemapper(
+ (long long)(NativeExt_MonoBehaviourSerialization_GetNewInstanceToReplaceOldInstance));
+
+ ScriptingInvocation initManagedAnalysis(GetScriptingMethodRegistry().GetMethod("UnityEngine.Serialization","ManagedLivenessAnalysis","Init"));
+ initManagedAnalysis.Invoke();
+# endif
+ // It tries to get classes like LevelGameManager which doesn't exist in managed land, is this intended?
+ RebuildClassIDToScriptingClass();
+
+# if !ENABLE_WINRT_PINVOKE
+ METRO_DBG_MARK_TIME("MonoManager::SetupDelegates begin...");
+ GetWinRTUtils()->SetupDelegates();
+ METRO_DBG_MARK_TIME("MonoManager::SetupDelegates end...");
+# else
+ if (GetWinRTUtils()->IsPlatformInvokeSupported() == false)
+ FatalErrorMsg("WinRTBridge: Platform Invoke should be supported on this platform.");
+ if (UnityEngineDelegates::PlatformInvoke::IsSupported() == false)
+ FatalErrorMsg("UnityEngineDelegates: Platform Invoke should be supported on this platform.");
+# endif
+
+}
+
+MonoManager::~MonoManager ()
+{
+
+}
+
+template<class TransferFunction>
+void MonoManager::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion(2);
+ transfer.Transfer (m_MonoScriptManager.m_RuntimeScripts, "m_Scripts");
+
+ // ToDo: Temporary fix if we're deserializing for metro see MonoManager::Transfer function
+ std::vector<UnityStr> assemblyNames;
+ transfer.Transfer (assemblyNames, "m_AssemblyNames");
+}
+
+
+IMPLEMENT_OBJECT_SERIALIZE (MonoManager)
+IMPLEMENT_CLASS (MonoManager)
+GET_MANAGER (MonoManager)
+GET_MANAGER_PTR (MonoManager)
diff --git a/Runtime/Scripting/MonoManager_WinRT.h b/Runtime/Scripting/MonoManager_WinRT.h
new file mode 100644
index 0000000..cc43c8e
--- /dev/null
+++ b/Runtime/Scripting/MonoManager_WinRT.h
@@ -0,0 +1,27 @@
+#ifndef MONOMANAGER_WINRT_H
+#define MONOMANAGER_WINRT_H
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Mono/MonoScriptManager.h"
+#include "Runtime/Scripting/CommonScriptingClasses.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+
+class MonoManager : public ScriptingManager
+{
+public:
+ REGISTER_DERIVED_CLASS (MonoManager, GlobalGameManager)
+ DECLARE_OBJECT_SERIALIZE (MonoManager)
+
+ MonoManager (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~MonoManager (); declared-by-macro
+
+ ScriptingObjectPtr CreateInstance(ScriptingClassPtr klass);
+
+private:
+
+};
+
+MonoManager& GetMonoManager ();
+MonoManager* GetMonoManagerPtr ();
+
+#endif
diff --git a/Runtime/Scripting/ReadOnlyScriptingObjectOfType.h b/Runtime/Scripting/ReadOnlyScriptingObjectOfType.h
new file mode 100644
index 0000000..2a837e6
--- /dev/null
+++ b/Runtime/Scripting/ReadOnlyScriptingObjectOfType.h
@@ -0,0 +1,151 @@
+#pragma once
+
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Scripting.h"
+
+#define SUPPORT_DIRECT_CACHEDPTR_PASSING_FOR_READONLY_OBJECTS UNITY_WINRT
+
+#if !SUPPORT_DIRECT_CACHEDPTR_PASSING_FOR_READONLY_OBJECTS
+template<class T>
+struct ReadOnlyScriptingObjectOfType
+{
+private:
+ ScriptingObjectPtr object;
+public:
+ ReadOnlyScriptingObjectOfType(ScriptingObjectPtr object)
+ {
+ this->object = object;
+ }
+
+ T& GetReference () const
+ {
+ T* ptr = GetPtr();
+
+ if (ptr != NULL)
+ return *ptr;
+
+ Scripting::RaiseNullExceptionObject (object);
+ return *(T*)NULL;
+ }
+
+ T& operator * () const
+ {
+ return GetReference ();
+ }
+
+ operator T* () const
+ {
+ return GetPtr ();
+ }
+
+ T* operator -> () const
+ {
+ return &GetReference ();
+ }
+
+ bool IsNull() const
+ {
+ return object == SCRIPTING_NULL;
+ }
+
+ operator PPtr<T> () const
+ {
+ if (IsNull())
+ return PPtr<T> ();
+
+ return PPtr<T> (GetInstanceID());
+ }
+
+private:
+#if UNITY_EDITOR
+public:
+ // ToDo: Fix this later, ask TomasD or Lucas
+ inline ScriptingObjectPtr GetScriptingObject() const
+ {
+ return object;
+ }
+#endif
+
+ inline int GetInstanceID() const
+ {
+ return Scripting::GetInstanceIDFromScriptingWrapper(object);
+ }
+ T* GetPtr () const
+ {
+ if (IsNull())
+ return NULL;
+
+ void* cachedPtr = GetCachedPtr();
+ if (cachedPtr != NULL)
+ {
+ AssertIf(reinterpret_cast<Object*> (cachedPtr)->GetInstanceID() != GetInstanceID());
+ return (T*)cachedPtr;
+ }
+
+ T* temp = dynamic_instanceID_cast<T*> (GetInstanceID());
+ return temp;
+ }
+
+ inline void* GetCachedPtr() const
+ {
+ return Scripting::GetCachedPtrFromScriptingWrapper(object);
+ }
+};
+
+#else
+
+template<class T>
+struct ReadOnlyScriptingObjectOfType
+{
+private:
+ T* cachedPtr;
+public:
+ ReadOnlyScriptingObjectOfType(void* p)
+ {
+ this->cachedPtr = (T*)p;
+ }
+
+ T& GetReference () const
+ {
+ if (cachedPtr != NULL)
+ return *(T*)cachedPtr;
+
+ Scripting::RaiseNullExceptionObject (SCRIPTING_NULL);
+ return *(T*)NULL;
+ }
+
+ T& operator * () const
+ {
+ return GetReference ();
+ }
+
+ operator T* () const
+ {
+ return cachedPtr;
+ }
+
+ T* operator -> () const
+ {
+ return &GetReference ();
+ }
+
+ bool IsNull() const
+ {
+ return cachedPtr == NULL;
+ }
+
+ operator PPtr<T> () const
+ {
+ if (IsNull())
+ return PPtr<T> ();
+
+ return PPtr<T> (cachedPtr->GetInstanceID());
+ }
+
+};
+
+#endif
+
+
+
+
diff --git a/Runtime/Scripting/ScriptLanguagePortUtility.cpp b/Runtime/Scripting/ScriptLanguagePortUtility.cpp
new file mode 100644
index 0000000..7f53640
--- /dev/null
+++ b/Runtime/Scripting/ScriptLanguagePortUtility.cpp
@@ -0,0 +1,11 @@
+#include "UnityPrefix.h"
+#if UNITY_FLASH
+
+#include "ScriptLanguagePortUtility.h"
+
+bool IsFileCreated(const std::string& path)
+{
+ return Ext_FileContainer_IsFileCreatedAt(path.c_str());
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/Scripting/ScriptLanguagePortUtility.h b/Runtime/Scripting/ScriptLanguagePortUtility.h
new file mode 100644
index 0000000..913aab4
--- /dev/null
+++ b/Runtime/Scripting/ScriptLanguagePortUtility.h
@@ -0,0 +1,10 @@
+#ifndef SCRIPTLANGUAGEPORTUTILITY_H
+#define SCRIPTLANGUAGEPORTUTILITY_H
+
+#if UNITY_FLASH || UNITY_WEBGL
+
+extern "C" bool Ext_FileContainer_IsFileCreatedAt(const char* filename);
+extern "C" bool Ext_LogCallstack();
+
+#endif
+#endif
diff --git a/Runtime/Scripting/ScriptPopupMenus.cpp b/Runtime/Scripting/ScriptPopupMenus.cpp
new file mode 100644
index 0000000..fdc3c1d
--- /dev/null
+++ b/Runtime/Scripting/ScriptPopupMenus.cpp
@@ -0,0 +1,142 @@
+#include "UnityPrefix.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Scripting/ScriptPopupMenus.h"
+#include "Runtime/Mono/tabledefs.h"
+#include "Runtime/Serialize/TypeTree.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Serialize/IterateTypeTree.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+using namespace std;
+
+#if UNITY_EDITOR
+
+static void ExtractPopupFromEnum (MonoClass* klass, map<string, int>& popup)
+{
+ MonoVTable * vtable = mono_class_vtable (mono_domain_get (), klass);
+ MonoClassField *field;
+ void* iter = NULL;
+
+ MonoType* enumMonoType = mono_class_enum_basetype (klass);
+ int enumType = mono_type_get_type (enumMonoType);
+
+ while ((field = mono_class_get_fields (klass, &iter)))
+ {
+ int value;
+ const char* name = mono_field_get_name (field);
+ int flags = mono_field_get_flags (field);
+ if (flags & FIELD_ATTRIBUTE_STATIC)
+ {
+ switch (enumType)
+ {
+ case MONO_TYPE_I4:
+ mono_field_static_get_value (vtable, field, &value);
+ break;
+ case MONO_TYPE_U1:
+ UInt8 byteValue;
+ mono_field_static_get_value (vtable, field, &byteValue);
+ value = byteValue;
+ break;
+ default:
+ ErrorString (Format("Unsupported enum type: %s", name));
+ break;
+ }
+ popup[name] = value;
+ }
+ }
+}
+
+const string GetFieldIdentifierForEnum(const TypeTree* typeTree)
+{
+ string identifier;
+ while (typeTree != NULL && typeTree->m_Father != NULL)
+ {
+ // Skip the extra arary type injected by the serialization system
+ if (typeTree->m_Father->m_Father && IsTypeTreeArray(*typeTree->m_Father))
+ typeTree = typeTree->m_Father->m_Father;
+
+ if (identifier.empty())
+ identifier = typeTree->m_Name;
+ else
+ identifier = Format("%s.%s", typeTree->m_Name.c_str(), identifier.c_str());
+
+ typeTree = typeTree->m_Father;
+ }
+
+ return identifier;
+}
+
+static const string AppendName(const std::string& previous, MonoClassField* field)
+{
+ const char* name = mono_field_get_name(field);
+
+ if (previous.empty())
+ return name;
+ else
+ return Format("%s.%s", previous.c_str(), name);
+}
+
+static void BuildScriptPopupMenus (MonoClass* klass, const std::string& parentName, std::map<std::string, std::map<std::string, int> >& popups, std::set<MonoClass*> collected)
+{
+ const CommonScriptingClasses& commonClasses = GetMonoManager ().GetCommonClasses ();
+ MonoClass* unityEngineObject = GetMonoManager().ClassIDToScriptingClass(ClassID(Object));
+
+ while (klass && klass != commonClasses.monoBehaviour && klass != commonClasses.scriptableObject)
+ {
+ // Only collect classes once
+ if (!collected.insert (klass).second)
+ return;
+
+ MonoClassField *field;
+ void* iter = NULL;
+ while ((field = mono_class_get_fields (klass, &iter)))
+ {
+ MonoType* monoType = mono_field_get_type (field);
+ int typeType = mono_type_get_type (monoType);
+ MonoClass* elementClass;
+
+ int flags = mono_field_get_flags (field);
+ if (flags & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_INIT_ONLY | FIELD_ATTRIBUTE_NOT_SERIALIZED))
+ continue;
+
+ // Extract the array field from the generic list
+ MonoClassField* arrayField = GetMonoArrayFieldFromList(typeType, monoType, field);
+ if (arrayField)
+ {
+ monoType = mono_field_get_type(arrayField);
+ typeType = mono_type_get_type (monoType);
+ }
+
+ if (typeType == MONO_TYPE_VALUETYPE || typeType == MONO_TYPE_SZARRAY)
+ {
+ elementClass = mono_type_get_class_or_element_class (monoType);
+ if (mono_class_is_enum (elementClass))
+ {
+ string name = AppendName(parentName, field);
+ map<string, int>& items = popups[name];
+ items.clear ();
+ ExtractPopupFromEnum (elementClass, items);
+ continue;
+ }
+ }
+ if (typeType == MONO_TYPE_CLASS || typeType == MONO_TYPE_SZARRAY)
+ {
+ elementClass = mono_type_get_class_or_element_class (monoType);
+ MonoImage* image = mono_class_get_image(elementClass);
+ int classflags = mono_class_get_flags(elementClass);
+
+ if ((classflags & TYPE_ATTRIBUTE_SERIALIZABLE) && image != mono_get_corlib() && GetMonoManager().GetAssemblyIndexFromImage(image) != -1 && !mono_class_is_subclass_of(elementClass, unityEngineObject, false))
+ BuildScriptPopupMenus(elementClass, AppendName(parentName, field), popups, collected);
+ }
+ }
+
+ klass = mono_class_get_parent (klass);
+ }
+}
+
+void BuildScriptPopupMenus (MonoBehaviour& behaviour, std::map<string, std::map<std::string, int> >& popups)
+{
+ std::set<MonoClass*> collected;
+ BuildScriptPopupMenus(behaviour.GetClass (), "", popups, collected);
+}
+#endif \ No newline at end of file
diff --git a/Runtime/Scripting/ScriptPopupMenus.h b/Runtime/Scripting/ScriptPopupMenus.h
new file mode 100644
index 0000000..2c2f3e0
--- /dev/null
+++ b/Runtime/Scripting/ScriptPopupMenus.h
@@ -0,0 +1,11 @@
+#ifndef __SCRIPTPOPUPMENUSH__
+#define __SCRIPTPOPUPMENUSH__
+
+#include <map>
+
+class TypeTree;
+class MonoBehaviour;
+
+void BuildScriptPopupMenus (MonoBehaviour& behaviour, std::map<std::string, std::map<std::string, int> >& popups);
+const std::string GetFieldIdentifierForEnum(const TypeTree* typeTree);
+#endif \ No newline at end of file
diff --git a/Runtime/Scripting/Scripting.cpp b/Runtime/Scripting/Scripting.cpp
new file mode 100644
index 0000000..5e01feb
--- /dev/null
+++ b/Runtime/Scripting/Scripting.cpp
@@ -0,0 +1,768 @@
+#include "UnityPrefix.h"
+
+#include "Scripting.h"
+
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/BaseClasses/RefCounted.h"
+#include "Runtime/GameCode/DestroyDelayed.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoScriptCache.h"
+#include "Runtime/Scripting/ScriptingObjectOfType.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+
+#if UNITY_EDITOR
+# include "Editor/Src/AssetPipeline/MonoCompilationPipeline.h"
+#endif
+
+namespace Scripting
+{
+
+ScriptingObjectPtr ScriptingWrapperFor(Object* o)
+{
+#if !ENABLE_SCRIPTING
+ return 0;
+#else
+ if(!o)
+ return SCRIPTING_NULL;
+
+ ScriptingObjectPtr cachedInstance = o->GetCachedScriptingObject();
+ if(cachedInstance != SCRIPTING_NULL)
+ return cachedInstance;
+
+ int classID = o->GetClassID();
+ if(classID == ClassID(MonoBehaviour))
+ {
+ AssertIf(static_cast<MonoBehaviour*> (o)->GetInstance());
+ return SCRIPTING_NULL;
+ }
+
+ ScriptingObjectPtr object = InstantiateScriptingWrapperForClassID(classID);
+ return (object != SCRIPTING_NULL) ? ConnectScriptingWrapperToObject(object, o) : SCRIPTING_NULL;
+#endif
+}
+
+ScriptingObjectPtr ConnectScriptingWrapperToObject(ScriptingObjectPtr object, Object* ptr)
+{
+#if !ENABLE_SCRIPTING
+ return 0;
+#else
+ // ConnectScriptingWrapperToObject might get called from different threads
+ // - References to objects are setup from loading thread.
+ // The Object could already be loaded in memory and in that case it is possible that the main thread might asign a MonoObject* while the loader thread is doing the same.
+ // - @TODO: If this is a performance bottleneck we could ensure that the loader thread calls ConnectScriptingWrapperToObject only during safe places.
+
+ LockObjectCreation();
+
+ AssertIf(object == SCRIPTING_NULL);
+ AssertIf(ptr == NULL);
+
+ if(ptr->GetCachedScriptingObject() != SCRIPTING_NULL)
+ {
+ ScriptingObjectPtr res = ptr->GetCachedScriptingObject();
+ UnlockObjectCreation();
+
+ return res;
+ }
+
+ ScriptingObjectOfType<Object> wrapper(object);
+
+#if DEBUGMODE && !UNITY_FLASH && !UNITY_WINRT //in flash, we do not yet initialize chachedPtr to 0
+ AssertIf(wrapper.GetCachedPtr() != NULL);
+#endif
+
+ wrapper.SetCachedPtr(ptr);
+ wrapper.SetInstanceID(ptr->GetInstanceID());
+
+#if MONO_QUALITY_ERRORS
+ wrapper.SetError(NULL);
+#endif
+
+ AssertIf(ptr->GetCachedScriptingObject() != SCRIPTING_NULL);
+ ptr->SetCachedScriptingObject(object);
+
+ UnlockObjectCreation();
+
+ return object;
+#endif
+}
+
+bool BroadcastScriptingMessage(GameObject& go, const char* name, ScriptingObjectPtr param)
+{
+ bool didSend = SendScriptingMessage(go, name, param);
+
+ Transform* transform = go.QueryComponent(Transform);
+ if(transform)
+ {
+ for(int i = 0; i < transform->GetChildrenCount(); i++ )
+ didSend |= BroadcastScriptingMessage(transform->GetChild(i).GetGameObject(), name, param);
+ }
+
+ return didSend;
+}
+
+
+bool SendScriptingMessageUpwards(GameObject& go, const char* name, ScriptingObjectPtr param)
+{
+ bool didSend = SendScriptingMessage(go, name, param);
+
+ Transform* transform = go.QueryComponent(Transform);
+ if (transform)
+ transform = transform->GetParent();
+
+ while(transform)
+ {
+ didSend |= SendScriptingMessage(transform->GetGameObject(), name, param);
+ transform = transform->GetParent();
+ }
+
+ return didSend;
+}
+
+bool SendScriptingMessage(GameObject& go, const char* name, ScriptingObjectPtr param)
+{
+ bool didSend = false;
+
+ if(!go.IsActive())
+ return false;
+
+ int instanceID = go.GetInstanceID();
+
+ for(int i = 0; i < go.GetComponentCount(); i++)
+ {
+ if(go.GetComponentClassIDAtIndex(i) == ClassID(MonoBehaviour))
+ {
+ MonoBehaviour& behaviour = static_cast<MonoBehaviour&>(go.GetComponentAtIndex(i));
+
+ ScriptingObjectPtr instance = behaviour.GetInstance();
+ if(instance)
+ {
+ ScriptingMethodPtr method = behaviour.FindMethod(name);
+ if(method == SCRIPTING_NULL)
+ continue;
+
+ behaviour.InvokeMethodOrCoroutineChecked(method, param);
+ didSend = true;
+
+ // Check if the gameObject was destroyed.
+ if(!PPtr<Object>(instanceID).IsValid())
+ return didSend;
+ }
+ }
+ }
+
+ return didSend;
+}
+
+bool BroadcastScriptingMessage(GameObject& go, const std::string& methodName, ScriptingObjectPtr param, int options)
+{
+ bool didSend = BroadcastScriptingMessage(go, methodName.c_str(), param);
+
+#if !DEPLOY_OPTIMIZED
+ if(!didSend && options == 0)
+ ErrorStringObject(Format("BroadcastMessage %s has no receiver!", methodName.c_str()), &go);
+#endif
+
+ return didSend;
+}
+
+bool SendScriptingMessageUpwards(GameObject& go, const std::string& methodName, ScriptingObjectPtr param, int options)
+{
+ bool didSend = SendScriptingMessageUpwards(go, methodName.c_str(), param);
+
+#if !DEPLOY_OPTIMIZED
+ if(!didSend && options == 0)
+ ErrorStringObject(Format("SendMessage %s has no receiver!", methodName.c_str()), &go);
+#endif
+
+ return didSend;
+}
+
+bool SendScriptingMessage(GameObject& go, const std::string& methodName, ScriptingObjectPtr param, int options)
+{
+ bool didSend = SendScriptingMessage(go, methodName.c_str(), param);
+
+#if !DEPLOY_OPTIMIZED
+ if(!didSend && options == 0)
+ ErrorStringObject(Format("SendMessage %s has no receiver!", methodName.c_str()), &go);
+#endif
+
+ return didSend;
+}
+
+/// Compares two Object classes.
+/// Returns true if both have the same instance id
+/// or both are NULL (Null can either mean that the object is gone or that the instanceID is 0)
+bool CompareBaseObjects (ScriptingObjectPtr lhs, ScriptingObjectPtr rhs)
+{
+ int lhsInstanceID = 0;
+ int rhsInstanceID = 0;
+ bool isLhsNull = true, isRhsNull = true;
+ if (lhs)
+ {
+ lhsInstanceID = GetInstanceIDFromScriptingWrapper (lhs);
+ ScriptingObjectOfType<Object> lhsRef(lhs);
+ isLhsNull = !lhsRef.IsValidObjectReference();
+ }
+ if (rhs)
+ {
+ rhsInstanceID = GetInstanceIDFromScriptingWrapper (rhs);
+ ScriptingObjectOfType<Object> rhsRef(rhs);
+ isRhsNull = !rhsRef.IsValidObjectReference();
+ }
+ if (isLhsNull || isRhsNull)
+ return isLhsNull == isRhsNull;
+ else
+ return lhsInstanceID == rhsInstanceID;
+}
+
+void DestroyObjectFromScriptingImmediate(Object* object, bool allowDestroyingAssets)
+{
+#if !DEPLOY_OPTIMIZED
+ if(object && object->IsPersistent() && !allowDestroyingAssets)
+ {
+ ErrorStringObject ("Destroying assets is not permitted to avoid data loss.\nIf you really want to remove an asset use DestroyImmediate (theObject, true);", object);
+ return;
+ }
+#endif
+
+ DestroyObjectHighLevel(object);
+}
+
+void UnloadAssetFromScripting(Object* object)
+{
+ if(object == NULL)
+ return;
+
+#if !DEPLOY_OPTIMIZED
+ if(!object->IsPersistent())
+ {
+ ErrorStringObject ("UnloadAsset can only be used on assets;", object);
+ return;
+ }
+
+ bool disallowedUnloadTypes = object->IsDerivedFrom(ClassID(Component)) || object->IsDerivedFrom(ClassID(GameObject)) || object->IsDerivedFrom(ClassID(AssetBundle));
+ if(disallowedUnloadTypes)
+ {
+ ErrorStringObject ("UnloadAsset may only be used on individual assets and can not be used on GameObject's / Components or AssetBundles", object);
+ return;
+ }
+#endif
+
+ UnloadObject(object);
+}
+
+static void DisableBehaviours( GameObject* target )
+{
+ for ( unsigned compI = 0; compI < target->GetComponentCount(); ++compI )
+ {
+ Behaviour* behaviour = dynamic_pptr_cast<Behaviour*> (&target->GetComponentAtIndex(compI));
+ if (behaviour)
+ behaviour->SetEnabled(false);
+ }
+}
+
+// @TODO: Test for DestroyImmediate from Destroy in OnDisable!
+void DestroyObjectFromScripting (PPtr<Object> object, float t)
+{
+ if (!IsWorldPlaying())
+ {
+ ErrorString("Destroy may not be called from edit mode! Use DestroyImmediate instead.\nAlso think twice if you really want to destroy something in edit mode. Since this will destroy objects permanently.");
+ return;
+ }
+
+ if (object)
+ {
+#if !DEPLOY_OPTIMIZED
+ if (object->IsPersistent ())
+ {
+ ErrorStringObject ("Destroying assets is not permitted to avoid data loss.\nIf you really want to remove an asset use DestroyImmediate (theObject, true);", object);
+ return;
+ }
+#endif
+
+ // if we want to destroy it this frame (t <= 0.0) - we need to imitate "destroy right away" behaviour
+ if (t <= 0.0)
+ {
+ Behaviour* behaviour = dynamic_pptr_cast<Behaviour*> (object);
+ if (behaviour)
+ {
+ // if this Object is some kind of Behaviour, it is already in update queue.
+ // if it was updated before we can do nothing, but if not - just disable it
+ behaviour->SetEnabled(false);
+ }
+
+ GameObject* go = dynamic_pptr_cast<GameObject*> (object);
+ if (go)
+ {
+ // if we destroy game object - disable all behaviours (children included)
+ DisableBehaviours(go);
+
+ ////@TODO: This needs to be done recursively
+
+ Transform& root = go->GetComponentT<Transform>(ClassID(Transform));
+ for ( unsigned childI = 0; childI < root.GetChildrenCount(); ++childI )
+ DisableBehaviours( root.GetChild(childI).GetGameObjectPtr() );
+ }
+ }
+
+ DestroyObjectDelayed (object, t);
+ }
+}
+
+static MonoScript* FindScriptableObjectFromClass (ScriptingTypePtr klass, bool errorOnMissingScript = true)
+{
+ if (klass == SCRIPTING_NULL)
+ {
+ ErrorString ("Instance couldn't be created because type was null.");
+ return NULL;
+ }
+
+ MonoScript* script = GetMonoScriptManager().FindRuntimeScript(klass);
+ if (script == NULL)
+ {
+ script = GetMonoScriptManager().FindEditorScript(klass);
+ if (script == NULL)
+ {
+ if (errorOnMissingScript)
+ {
+ ErrorString (Format ("Instance of %s couldn't be created because there is no script with that name.", scripting_class_get_name(klass)));
+ }
+ return NULL;
+ }
+ }
+
+ if (script->GetScriptType() != kScriptTypeScriptableObjectDerived && script->GetScriptType() != kScriptTypeEditorScriptableObjectDerived)
+ {
+ ErrorString (Format ("Instance of %s couldn't be created. The the script class needs to derive from ScriptableObject.", scripting_class_get_name(klass)));
+ return NULL;
+ }
+
+ if (script->GetClass() == SCRIPTING_NULL)
+ {
+ ErrorString (Format ("Instance of %s couldn't be created. All script needs to successfully compile first!", scripting_class_get_name(klass)));
+ return NULL;
+ }
+
+ return script;
+}
+
+ScriptingObjectPtr CreateScriptableObjectWithType (ScriptingObjectPtr systemTypeInstance)
+{
+ ScriptingTypePtr type = GetScriptingTypeRegistry().GetType(systemTypeInstance);
+ if(type == NULL)
+ return SCRIPTING_NULL;
+
+ MonoBehaviour* behaviour = 0;
+
+ bool errorOnMissingScript = UNITY_EDITOR ? false : true;
+
+ MonoScript* script = FindScriptableObjectFromClass(type, errorOnMissingScript);
+ if(script == NULL)
+ {
+#if UNITY_EDITOR
+ MonoImage* image = mono_class_get_image(type);
+ std::string imageFileName = mono_image_get_filename(image);
+
+ if (IsEditorOnlyAssembly(imageFileName, true))
+ {
+ std::string imageName = mono_image_get_name(image);
+ std::string id = BuildScriptClassId(imageName, mono_class_get_namespace(type), mono_class_get_name(type));
+
+ behaviour = NEW_OBJECT (MonoBehaviour);
+ behaviour->SetClassIdentifier(id);
+ }
+ else
+ return SCRIPTING_NULL;
+#else
+ return SCRIPTING_NULL;
+#endif
+ } else
+ {
+ behaviour = NEW_OBJECT (MonoBehaviour);
+ behaviour->SetScript(script);
+ }
+
+ ResetAndApplyDefaultReferencesOnNewMonoBehaviour(*behaviour);
+ return behaviour->GetInstance();
+}
+
+ScriptingObjectPtr CreateScriptableObject(const std::string& name)
+{
+ MonoScript* script = GetMonoScriptManager().FindRuntimeScript(name);
+
+#if UNITY_EDITOR
+ if(script == NULL)
+ script = GetMonoScriptManager().FindEditorScript(name);
+#endif
+
+ if(script == NULL)
+ {
+ ErrorString(Format("Instance of %s couldn't be created because there is no script with that name.", name.c_str()));
+ return SCRIPTING_NULL;
+ }
+
+ if(
+ script->GetScriptType() != kScriptTypeScriptableObjectDerived
+#if UNITY_EDITOR
+ && script->GetScriptType() != kScriptTypeEditorScriptableObjectDerived
+#endif
+ )
+ {
+ ErrorString(Format("Instance of %s couldn't be created. The the script class needs to derive from ScriptableObject.", name.c_str()));
+ return SCRIPTING_NULL;
+ }
+
+ if(script->GetClass() == SCRIPTING_NULL)
+ {
+ ErrorString(Format("Instance of %s couldn't be created. All script needs to successfully compile first!", name.c_str()));
+ return SCRIPTING_NULL;
+ }
+
+ MonoBehaviour* behaviour = NEW_OBJECT(MonoBehaviour);
+ behaviour->SetScript(script);
+
+ ResetAndApplyDefaultReferencesOnNewMonoBehaviour(*behaviour);
+
+ return behaviour->GetInstance();
+}
+
+void CreateEngineScriptableObject(ScriptingObjectPtr object)
+{
+ // Avoid recursive loop. This object is already created from c++
+
+ if(GetInstanceIDFromScriptingWrapper(object) != 0)
+ return;
+
+ SCRIPTINGAPI_THREAD_CHECK(ScriptableObject.ctor);
+
+ ScriptingClassPtr klass = scripting_object_get_class(object, GetScriptingTypeRegistry());
+
+ const char *classNamespace = scripting_class_get_namespace(klass);
+ const char *className = scripting_class_get_name(klass);
+
+ WarningStringMsg("%s%s%s must be instantiated using the ScriptableObject.CreateInstance method instead of new %s.", classNamespace, (classNamespace[0] ? "." : ""), className, className);
+
+ MonoScript* script = FindScriptableObjectFromClass(klass);
+ if(script == NULL)
+ return;
+
+ MonoBehaviour* behaviour = NEW_OBJECT(MonoBehaviour);
+ behaviour->SetScript(script, object);
+
+#if UNITY_EDITOR
+ if(script->GetScriptType() == kScriptTypeEditorScriptableObjectDerived)
+ behaviour->SetEditorHideFlags (MonoBehaviour::kHideScriptPPtr);
+#endif
+
+ ResetAndApplyDefaultReferencesOnNewMonoBehaviour(*behaviour);
+}
+
+ScriptingObjectPtr GetScriptingWrapperForInstanceID(int instanceID)
+{
+ if(instanceID == 0)
+ return SCRIPTING_NULL;
+
+ Object* object2 = PPtr<Object>(instanceID);
+ return ScriptingWrapperFor(object2);
+}
+
+int GetClassIDFromScriptingClass(ScriptingClassPtr klass)
+{
+ int classID = 0;
+
+ // Look up this classes classID
+ ScriptingClassPtr curClass = klass;
+
+ const char* className = scripting_class_get_name(curClass);
+ const char* nameSpace = scripting_class_get_namespace(curClass);
+ const char* kEngineNameSpace = "UnityEngine";
+
+ if(strcmp(nameSpace, kEngineNameSpace) == 0)
+ {
+ if(strcmp(className, "ScriptableObject") == 0)
+ className = "MonoBehaviour";
+
+ classID = Object::StringToClassID(className);
+ if(classID != -1)
+ return classID;
+ }
+
+ // It is not an engine class so check the parents
+ classID = -1;
+
+ ScriptingClassPtr parentClass = scripting_class_get_parent(klass, GetScriptingTypeRegistry());
+ if(parentClass)
+ classID = GetClassIDFromScriptingClass(parentClass);
+
+ return classID;
+}
+
+ScriptingTypePtr ClassIDToScriptingType(int classID)
+{
+ return GetScriptingManager().ClassIDToScriptingClass(classID);
+}
+
+ScriptingObjectPtr GetScriptingWrapperOfComponentOfGameObject (GameObject& go, const std::string& name)
+{
+ int classID = Object::StringToClassID(name);
+ if (classID != -1 && Object::IsDerivedFromClassID(classID, ClassID(Component)))
+ {
+ Unity::Component* component = go.QueryComponentT<Unity::Component> (classID);
+
+#if MONO_QUALITY_ERRORS
+ if (component == NULL)
+ return MonoObjectNULL (classID, MissingComponentString(go, classID));
+#endif
+ return ScriptingWrapperFor (component);
+ }
+
+ MonoScript* script = GetMonoScriptManager().FindRuntimeScript(name);
+ if (script == NULL)
+ {
+
+#if MONO_QUALITY_ERRORS
+ if (classID != -1)
+ ScriptWarning(Format("GetComponent requires that the requested component inherits from Component or MonoBehaviour.\nYou used GetComponent(%s) which does not inherit from Component.\n", name.c_str()), &go);
+#endif
+
+ return SCRIPTING_NULL;
+ }
+
+ ScriptingTypePtr compareKlass = script->GetClass();
+ if (compareKlass == SCRIPTING_NULL)
+ return SCRIPTING_NULL;
+
+ int count = go.GetComponentCount ();
+ for (int i=0;i<count;i++)
+ {
+ // We are looking only for MonoBehaviours
+ int clsID = go.GetComponentClassIDAtIndex (i);
+ if (!Object::IsDerivedFromClassID (clsID, ClassID (MonoBehaviour)))
+ continue;
+
+ MonoBehaviour& behaviour = static_cast<MonoBehaviour&> (go.GetComponentAtIndex (i));
+ ScriptingObjectPtr object = behaviour.GetInstance ();
+
+ if(!object)
+ continue;
+
+ ScriptingTypePtr klass = scripting_object_get_class(object, GetScriptingTypeRegistry());
+ if (scripting_class_is_subclass_of(klass, compareKlass))
+ return object;
+ }
+
+ return SCRIPTING_NULL;
+}
+
+void LogException(ScriptingExceptionPtr exception, int instanceID, const std::string& error)
+{
+ StackTraceInfo info;
+
+ scripting_stack_trace_info_for(exception, info);
+
+ if (!error.empty())
+ info.condition = error + info.condition;
+
+ DebugStringToFilePostprocessedStacktrace(info.condition.c_str(), info.strippedStacktrace.c_str(), info.stacktrace.c_str(), info.errorNum, info.file.c_str(), info.line, kLog | kScriptingError | kScriptingException, instanceID, 0);
+}
+
+static ScriptingArrayPtr CreateScriptingArrayFromScriptingObjects(ScriptingObjectPtr* objects, int size, ScriptingClassPtr typeForArray)
+{
+ // copy into array
+ ScriptingArrayPtr array = CreateScriptingArray<ScriptingObjectPtr> (typeForArray, size);
+ for (int i = 0; i < size; i++)
+ {
+ Scripting::SetScriptingArrayElement (array, i, objects[i]);
+ }
+
+ return array;
+}
+
+static bool IsActiveSceneObject (Object& o)
+{
+ if (o.IsPersistent ())
+ return false;
+
+ GameObject* go = dynamic_pptr_cast<GameObject*> (&o);
+ if (go)
+ return go->IsActive ();
+
+ Unity::Component* com = dynamic_pptr_cast<Unity::Component*> (&o);
+ if (com)
+ {
+ MonoBehaviour* behaviour = dynamic_pptr_cast<MonoBehaviour*> (&o);
+ if (behaviour)
+ {
+ MonoScript* script = behaviour->GetScript();
+ if (script && script->GetScriptType () == kScriptTypeScriptableObjectDerived)
+ return true;
+ else
+ return com->IsActive ();
+ }
+ else
+ return com->IsActive ();
+ }
+
+ return true;
+}
+
+ScriptingArrayPtr FindObjectsOfType(ScriptingObjectPtr systemTypeInstance, FindMode mode)
+{
+ ScriptingClassPtr compareKlass = GetScriptingTypeRegistry().GetType(systemTypeInstance);
+
+ if(compareKlass == SCRIPTING_NULL)
+ {
+ ErrorString("FindAllObjectsOfType: Invalid Type");
+ return SCRIPTING_NULL;
+ }
+
+ int classID = GetClassIDFromScriptingClass(compareKlass);
+ if(classID == -1)
+ {
+ string klassName = scripting_class_get_name(compareKlass);
+ ErrorString("FindAllObjectsOfType: The type has to be derived from UnityEngine.Object. Type is " + klassName + ".");
+ return SCRIPTING_NULL;
+ }
+
+ // Gather the derived objects
+ vector<SInt32> objects;
+ Object::FindAllDerivedObjects(classID, &objects);
+
+ std::sort(objects.begin(), objects.end());
+
+ // We might need to ignore some objects which are not derived from the mono class but from MonoBehaviour
+ // so we store them in a buffer and then copy them into the mono array
+
+ ScriptingObjectPtr* scriptingObjects;
+
+#if UNITY_WINRT
+ scriptingObjects = new ScriptingObjectPtr[objects.size ()];
+#else
+ ALLOC_TEMP(scriptingObjects, ScriptingObjectPtr, objects.size ());
+#endif
+
+ int count = 0;
+ for(int i = 0;i < objects.size (); i++)
+ {
+ Object& object = *PPtr<Object>(objects[i]);
+ if(object.TestHideFlag (Object::kDontSave) && mode != Scripting::kFindAnything)
+ continue;
+
+ if(mode == Scripting::kFindActiveSceneObjects && !IsActiveSceneObject (object))
+ continue;
+
+ ScriptingObjectPtr mono = ScriptingWrapperFor(&object); //Todo: this used to call ObjectToScriptingObject, but that
+ //hit a bug in the alchemy compiler
+ if(mono)
+ {
+ // Cubemap is derived from Texture2D in serialization, but not in Scripting
+ if (object.IsDerivedFrom (ClassID (MonoBehaviour)) || object.IsDerivedFrom (ClassID (Cubemap)))
+ {
+ ScriptingClassPtr klass = scripting_object_get_class (mono, GetScriptingTypeRegistry());
+ if (scripting_class_is_subclass_of (klass, compareKlass))
+ scriptingObjects[count++] = mono;
+ }
+ else
+ {
+ scriptingObjects[count++] = mono;
+ }
+ }
+ }
+
+ ScriptingArrayPtr result = CreateScriptingArrayFromScriptingObjects(scriptingObjects,count,compareKlass);
+
+#if UNITY_WINRT
+ delete[]scriptingObjects;
+#endif
+
+ return result;
+}
+
+ScriptingObjectPtr TrackedReferenceBaseToScriptingObjectImpl (TrackedReferenceBase* base, ScriptingClassPtr klass)
+{
+ if (base)
+ {
+ // In the editor a weak reference (for example animation state)
+ // might get leaked when reloading domains while in playmode
+ // in that case we just recycle it
+ #if UNITY_EDITOR
+ if (base->m_MonoObjectReference != 0 && !mono_gchandle_is_in_domain(base->m_MonoObjectReference, mono_domain_get()))
+ {
+ mono_gchandle_free(base->m_MonoObjectReference);
+ base->m_MonoObjectReference = 0;
+ }
+ #endif
+#if ENABLE_MONO && !UNITY_PEPPER
+ AssertIf(base->m_MonoObjectReference != 0 && !mono_gchandle_is_in_domain(base->m_MonoObjectReference, mono_domain_get()));
+#endif
+
+ // Get cached mono object reference
+ ScriptingObjectPtr target = SCRIPTING_NULL;
+ if (base->m_MonoObjectReference != 0)
+ {
+ target = scripting_gchandle_get_target (base->m_MonoObjectReference);
+ if (target)
+ return target;
+
+ AssertString("This should never happen");
+
+ scripting_gchandle_free(base->m_MonoObjectReference);
+ base->m_MonoObjectReference = 0;
+ }
+
+ ScriptingObjectWithIntPtrField<TrackedReferenceBase> newTarget(scripting_object_new(klass));
+
+ base->m_MonoObjectReference = scripting_gchandle_new(newTarget.object);
+ newTarget.SetPtr(base);
+
+ return newTarget.object;
+ }
+ else
+ {
+#if DEPLOY_OPTIMIZED
+ return SCRIPTING_NULL;
+#else
+ ScriptingObjectPtr object = scripting_object_new (klass);
+ void* nativePointer = 0;
+ MarshallNativeStructIntoManaged(nativePointer,object);
+ return object;
+#endif
+ }
+}
+
+template <class StringType>
+ScriptingArrayPtr StringVectorToMono (const std::vector<StringType>& source)
+{
+ ScriptingArrayPtr arr = CreateScriptingArray<ScriptingObjectPtr>(MONO_COMMON.string, source.size());
+ for (int i = 0; i < source.size();i++)
+ {
+ Scripting::SetScriptingArrayElement(arr, i, scripting_string_new(source[i]));
+ }
+ return arr;
+}
+
+template ScriptingArrayPtr StringVectorToMono (const std::vector<std::string>& source);
+template ScriptingArrayPtr StringVectorToMono (const std::vector<UnityStr>& source);
+
+ScriptingObjectPtr GetComponentObjectToScriptingObject(Unity::Component* com, Unity::GameObject& go, int classID)
+{
+
+#if MONO_QUALITY_ERRORS
+ if(com == NULL)
+ return MonoObjectNULL(classID, MissingComponentString(go, classID));
+#endif
+
+ return ScriptingWrapperFor(com);
+}
+
+ScriptingObjectPtr GetComponentObjectToScriptingObject(Object* com, Unity::GameObject& go, int classID)
+{
+ return ScriptingWrapperFor(com);
+}
+
+} // namespace Scripting
diff --git a/Runtime/Scripting/Scripting.h b/Runtime/Scripting/Scripting.h
new file mode 100644
index 0000000..bdcdc91
--- /dev/null
+++ b/Runtime/Scripting/Scripting.h
@@ -0,0 +1,197 @@
+#pragma once
+
+#include "UnityPrefix.h"
+
+#include "Runtime/Modules/ExportModules.h"
+
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+
+#define SCRIPTINGAPI_STACK_CHECK(NAME)
+
+#if WEBPLUG
+# undef DOES_NOT_RETURN
+# define DOES_NOT_RETURN
+#endif
+
+class Object;
+class TrackedReferenceBase;
+
+namespace Unity
+{
+ class GameObject;
+ class Component;
+}
+
+template<typename T>
+class PPtr;
+
+
+namespace Scripting
+{
+ScriptingObjectPtr ScriptingObjectNULL(ScriptingClassPtr klass);
+ScriptingObjectPtr ScriptingObjectNULL(ScriptingClassPtr klass, ScriptingStringPtr error);
+
+//Exception helpers, we'll need to move this again, later.
+DOES_NOT_RETURN TAKES_PRINTF_ARGS(1,2) void RaiseMonoException(const char* format, ...);
+DOES_NOT_RETURN TAKES_PRINTF_ARGS(1,2) EXPORT_COREMODULE void RaiseNullException(const char* format, ...);
+DOES_NOT_RETURN TAKES_PRINTF_ARGS(1,2) EXPORT_COREMODULE void RaiseArgumentException(const char* format, ...);
+DOES_NOT_RETURN TAKES_PRINTF_ARGS(1,2) void RaiseOutOfRangeException(const char* format, ...);
+DOES_NOT_RETURN TAKES_PRINTF_ARGS(1,2) void RaiseSecurityException(const char* format, ...);
+DOES_NOT_RETURN TAKES_PRINTF_ARGS(1,2) void RaiseInvalidOperationException(const char* format, ...);
+DOES_NOT_RETURN TAKES_PRINTF_ARGS(3,4) void RaiseManagedException(const char* ns, const char* type, const char* format, ...);
+DOES_NOT_RETURN EXPORT_COREMODULE void RaiseNullExceptionObject(ScriptingObjectPtr object);
+
+void RaiseIfNull(void* object);
+void RaiseIfNull(ScriptingObjectPtr object);
+
+EXPORT_COREMODULE int GetInstanceIDFromScriptingWrapper(ScriptingObjectPtr wrapper);
+EXPORT_COREMODULE void SetInstanceIDOnScriptingWrapper(ScriptingObjectPtr wrapper, int instanceID);
+EXPORT_COREMODULE void* GetCachedPtrFromScriptingWrapper(ScriptingObjectPtr wrapper);
+EXPORT_COREMODULE void SetCachedPtrOnScriptingWrapper(ScriptingObjectPtr wrapper, void* cachedPtr);
+void SetErrorOnScriptingWrapper(ScriptingObjectPtr wrapper, ScriptingStringPtr error);
+
+ScriptingTypePtr ClassIDToScriptingType(int classiD);
+ScriptingObjectPtr InstantiateScriptingWrapperForClassID(int classID);
+ScriptingObjectPtr ConnectScriptingWrapperToObject(ScriptingObjectPtr object, Object* ptr);
+
+/**These don't belong here either**/
+bool SendScriptingMessage(Unity::GameObject& go, const char* name, ScriptingObjectPtr param);
+bool BroadcastScriptingMessage(Unity::GameObject& go, const char* name, ScriptingObjectPtr param);
+bool SendScriptingMessageUpwards(Unity::GameObject& go, const char* name, ScriptingObjectPtr param);
+bool SendScriptingMessage(Unity::GameObject& go, const std::string& name, ScriptingObjectPtr param, int options);
+bool BroadcastScriptingMessage(Unity::GameObject& go, const std::string& name, ScriptingObjectPtr param, int options);
+bool SendScriptingMessageUpwards(Unity::GameObject& go, const std::string& name, ScriptingObjectPtr param, int options);
+/** end **/
+
+void DestroyObjectFromScripting(PPtr<Object> object, float t);
+void DestroyObjectFromScriptingImmediate(Object* object, bool allowDestroyingAssets);
+void UnloadAssetFromScripting(Object* object);
+
+ScriptingObjectPtr GetScriptingWrapperOfComponentOfGameObject (Unity::GameObject& go, const std::string& name);
+
+ScriptingObjectPtr GetScriptingWrapperForInstanceID(int instanceID);
+ScriptingObjectPtr CreateScriptableObject(const std::string& name);
+ScriptingObjectPtr CreateScriptableObjectWithType(ScriptingObjectPtr klassType);
+void CreateEngineScriptableObject(ScriptingObjectPtr object);
+int GetClassIDFromScriptingClass(ScriptingClassPtr klass);
+
+bool CompareBaseObjects(ScriptingObjectPtr lhs, ScriptingObjectPtr rhs);
+
+ScriptingObjectPtr EXPORT_COREMODULE ScriptingWrapperFor(Object* object);
+
+void LogException(ScriptingExceptionPtr exception, int instanceID, const std::string& error = std::string());
+
+enum FindMode
+{
+ kFindAssets = 0,
+ kFindActiveSceneObjects = 1,
+ kFindAnything = 2
+};
+
+ScriptingArrayPtr FindObjectsOfType(ScriptingObjectPtr reflectionTypeObject, FindMode mode);
+
+ScriptingObjectPtr GetComponentObjectToScriptingObject(Unity::Component* com, Unity::GameObject& go, int classID);
+ScriptingObjectPtr GetComponentObjectToScriptingObject(Object* com, Unity::GameObject& go, int classID);
+
+//RH / GAB : Discuss with Joe what to do with this
+/// Creates a MonoObject
+/// object is a ptr to a RefCounted class.
+/// type is the name MonoManager common classes
+/// eg. animationState -> GetScriptingManager().GetCommonClasses().animationStates
+#define TrackedReferenceBaseToScriptingObject(object, type) \
+ Scripting::TrackedReferenceBaseToScriptingObjectImpl(object, GetScriptingManager().GetCommonClasses().type)
+
+ScriptingObjectPtr TrackedReferenceBaseToScriptingObjectImpl(TrackedReferenceBase* base, ScriptingClassPtr klass);
+
+template <class StringType>
+ScriptingArrayPtr StringVectorToMono (const std::vector<StringType>& source);
+
+// Array handling
+
+void SetScriptingArrayObjectElementImpl(ScriptingArrayPtr a, int i, ScriptingObjectPtr value);
+void SetScriptingArrayStringElementImpl(ScriptingArrayPtr a, int i, ScriptingStringPtr value);
+ScriptingStringPtr* GetScriptingArrayStringElementImpl(ScriptingArrayPtr a, int i);
+ScriptingObjectPtr* GetScriptingArrayObjectElementImpl(ScriptingArrayPtr a, int i);
+ScriptingStringPtr* GetScriptingArrayStringStartImpl(ScriptingArrayPtr a);
+ScriptingObjectPtr* GetScriptingArrayObjectStartImpl(ScriptingArrayPtr a);
+ScriptingStringPtr GetScriptingArrayStringElementNoRefImpl(ScriptingArrayPtr a, int i);
+ScriptingObjectPtr GetScriptingArrayObjectElementNoRefImpl(ScriptingArrayPtr a, int i);
+
+template<class T>
+inline void SetScriptingArrayElement(ScriptingArrayPtr array, int i, T value)
+{
+ void* raw = scripting_array_element_ptr(array, i, sizeof(T));
+ *(T*)raw = value;
+}
+
+template<>
+inline void SetScriptingArrayElement (ScriptingArrayPtr a, int i, ScriptingObjectPtr value)
+{
+ SetScriptingArrayObjectElementImpl(a, i, value);
+}
+
+template<>
+inline void SetScriptingArrayElement (ScriptingArrayPtr a, int i, ScriptingStringPtr value)
+{
+ SetScriptingArrayStringElementImpl(a, i, value);
+}
+
+
+template<class T>
+inline T* GetScriptingArrayStart(ScriptingArrayPtr array)
+{
+ return (T*)scripting_array_element_ptr(array, 0, sizeof(T));
+}
+
+template<>
+inline ScriptingStringPtr* GetScriptingArrayStart(ScriptingArrayPtr a)
+{
+ return GetScriptingArrayStringStartImpl(a);
+}
+
+template<>
+inline ScriptingObjectPtr* GetScriptingArrayStart(ScriptingArrayPtr a)
+{
+ return GetScriptingArrayObjectStartImpl(a);
+}
+
+
+template<class T>
+inline T& GetScriptingArrayElement(ScriptingArrayPtr a, int i)
+{
+ return *((T*)scripting_array_element_ptr(a, i, sizeof(T)));
+}
+
+template<>
+inline ScriptingStringPtr& GetScriptingArrayElement(ScriptingArrayPtr a, int i)
+{
+ return *GetScriptingArrayStringElementImpl(a, i);
+}
+
+template<>
+inline ScriptingObjectPtr& GetScriptingArrayElement(ScriptingArrayPtr a, int i)
+{
+ return *GetScriptingArrayObjectElementImpl(a, i);
+}
+
+
+template<class T>
+inline T GetScriptingArrayElementNoRef(ScriptingArrayPtr a, int i)
+{
+ return *((T*)scripting_array_element_ptr(a, i, sizeof(T)));
+}
+
+template<>
+inline ScriptingStringPtr GetScriptingArrayElementNoRef(ScriptingArrayPtr a, int i)
+{
+ return GetScriptingArrayStringElementNoRefImpl(a, i);
+}
+
+template<>
+inline ScriptingObjectPtr GetScriptingArrayElementNoRef(ScriptingArrayPtr a, int i)
+{
+ return GetScriptingArrayObjectElementNoRefImpl(a, i);
+}
+
+}//namespace Scripting \ No newline at end of file
diff --git a/Runtime/Scripting/ScriptingExportUtility.h b/Runtime/Scripting/ScriptingExportUtility.h
new file mode 100644
index 0000000..3061cfb
--- /dev/null
+++ b/Runtime/Scripting/ScriptingExportUtility.h
@@ -0,0 +1,265 @@
+#ifndef _SCRIPTINGEXPORTUTILITY_H_
+#define _SCRIPTINGEXPORTUTILITY_H_
+
+#if ENABLE_SCRIPTING
+
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ICallString.h"
+#include "Runtime/Scripting/Scripting.h"
+
+
+template<class T>
+inline
+ScriptingArrayPtr CreateScriptingArray (ScriptingClassPtr klass, int count)
+{
+#if ENABLE_MONO
+ //here to force template argument being used on caller, so the callsite will be compatible
+ //with the flash version of CreateScriptingArray, which actually needs to know what will be in the array beforehand.
+ //TODO: find a cleaner way of enforcing usage:
+# if DEBUGMODE
+ int nativeSize = sizeof(T);
+ int monoSize = mono_class_array_element_size(klass);
+ DebugAssert(nativeSize == monoSize || !count);
+ (void)monoSize; // silence unused variable warning
+ nativeSize++;
+# endif
+ return mono_array_new(mono_domain_get(),klass,count);
+
+#elif UNITY_FLASH
+ UInt8* memoryblob = (UInt8*) malloc(sizeof(T) * count + sizeof(int));
+ *(int*)memoryblob = count;
+ return (ScriptingArrayPtr) memoryblob;
+#elif UNITY_WINRT
+ return ScriptingArrayPtr(GetWinRTObjectInstantiation()->CreateScriptingArrayGC(klass->metroType, count));
+#endif
+}
+
+template<class T>
+inline
+ScriptingArrayPtr CreateScriptingArray2D (ScriptingClassPtr klass, int count1, int count2)
+{
+#if ENABLE_MONO
+ return mono_array_new_2d (count1, count2, klass);
+#elif UNITY_FLASH
+ FatalErrorMsg("ToDo:CreateScriptingArray2D");
+#elif UNITY_WINRT
+ return ScriptingArrayPtr(GetWinRTObjectInstantiation()->CreateScriptingArray2DGC(klass->metroType, count1, count2));
+#endif
+}
+template<class T>
+inline
+ScriptingArrayPtr CreateScriptingArray3D (ScriptingClassPtr klass, int count1, int count2, int count3)
+{
+#if ENABLE_MONO
+ return mono_array_new_3d (count1, count2, count3, klass);
+#elif UNITY_FLASH
+ FatalErrorMsg("ToDo:CreateScriptingArray3D");
+#elif UNITY_WINRT
+ return ScriptingArrayPtr(GetWinRTObjectInstantiation()->CreateScriptingArray3DGC(klass->metroType, count1, count2, count3));
+#endif
+}
+
+
+inline
+ScriptingArrayPtr CreateEmptyStructArray (ScriptingClassPtr klass)
+{
+ return CreateScriptingArray<int>(klass,0); //using int as random template argument, as it doesn't matter, since size=0
+}
+
+template<class T>
+inline
+ScriptingArrayPtr CreateScriptingArray (const T* data, int count, ScriptingClassPtr klass)
+{
+ if (data == NULL)
+ count = 0;
+
+ ScriptingArrayPtr array = CreateScriptingArray<T>(klass,count);
+ memcpy(Scripting::GetScriptingArrayStart<T>(array), data, sizeof(T) * count );
+ return array;
+}
+
+template<class T>
+ScriptingArrayPtr CreateScriptingArrayFromUnityObjects (const T& container, int classID)
+{
+ ScriptingClassPtr klass = GetScriptingManager().ClassIDToScriptingClass (classID);
+
+ ScriptingArrayPtr array = CreateScriptingArray<ScriptingObjectPtr>(klass,container.size());
+ typename T::const_iterator j = container.begin();
+ for (int i=0;i<container.size();i++, j++)
+ {
+ Scripting::SetScriptingArrayElement(array,i, Scripting::ScriptingWrapperFor(*j));
+ }
+ return array;
+}
+
+template<class T > inline
+ScriptingArrayPtr CreateScriptingArrayFromUnityObjects(T& unityobjects,ScriptingClassPtr classForArray)
+{
+ ScriptingArrayPtr array = CreateScriptingArray<ScriptingObjectPtr> (classForArray , unityobjects.size ());
+ for (int i=0;i<unityobjects.size ();i++)
+ Scripting::SetScriptingArrayElement (array, i, Scripting::ScriptingWrapperFor (unityobjects[i]));
+ return array;
+}
+
+template<class T>
+inline
+ScriptingArrayPtr CreateScriptingArrayStride (const void* data, int count, ScriptingClassPtr klass, int inputStride)
+{
+ if (data == NULL)
+ count = 0;
+
+ ScriptingArrayPtr array = CreateScriptingArray<T> (klass, count);
+ UInt8* src = (UInt8*)data;
+ UInt8* dst = (UInt8*)Scripting::GetScriptingArrayStart<T> (array);
+ for (int i=0; i<count; ++i, src += inputStride, dst += sizeof(T))
+ memcpy(dst, src, sizeof(T));
+
+ return array;
+}
+
+template<class T, class T2, class U, class TConverter>
+void ScriptingStructArrayToVector (ScriptingArrayPtr source, U &dest, TConverter converter)
+{
+ dest.clear();
+ if (source != SCRIPTING_NULL)
+ {
+ int len = GetScriptingArraySize(source);
+ dest.resize (len);
+ for (int i = 0; i < len;i++)
+ converter (Scripting::GetScriptingArrayElement<T2>(source, i), dest[i]);
+ }
+}
+
+template<class T, class T2, class U, class TConverter>
+void ScriptingStructArrayToDynamicArray (ScriptingArrayPtr source, U &dest, TConverter converter)
+{
+ dest.clear();
+ if (source != SCRIPTING_NULL)
+ {
+ int len = GetScriptingArraySize(source);
+ dest.resize_initialized (len);
+ for (int i = 0; i < len;i++)
+ converter (Scripting::GetScriptingArrayElement<T2>(source, i), dest[i]);
+ }
+}
+
+template<class T, class T2, class U>
+void ScriptingClassArrayToVector (ScriptingArrayPtr source, U &dest, void (*converter) (T2 &source, T &dest))
+{
+ dest.clear();
+ if (source != SCRIPTING_NULL)
+ {
+ int len = GetScriptingArraySize(source);
+ dest.resize (len);
+ for (int i = 0; i < len;i++)
+ {
+ T2 nativeSourceObject;
+ ScriptingObjectPtr element = Scripting::GetScriptingArrayElementNoRef<ScriptingObjectPtr>(source, i);
+ Scripting::RaiseIfNull(element);
+ MarshallManagedStructIntoNative<T2>(element, &nativeSourceObject);
+ converter (nativeSourceObject, dest[i]);
+ }
+ }
+}
+
+template<class T, class T2>
+std::vector<T> ScriptingClassArrayToVector (ScriptingArrayPtr source, void (*converter) (T2 &source, T &dest))
+{
+ std::vector<T> dest;
+ ScriptingClassArrayToVector (source, dest, converter);
+ return dest;
+}
+
+
+template<class T, class T2, class U>
+ScriptingArrayPtr VectorToScriptingClassArray (const U &source, ScriptingClassPtr klass, void (*converter) (const T &source, T2 &dest))
+{
+ // ToDo: if all good, remove mono pass, and use unified pass
+#if ENABLE_MONO
+ return VectorToMonoClassArray<T, T2>(source, klass, converter);
+#else
+ ScriptingArrayPtr arr = CreateScriptingArray<ScriptingObjectPtr>(klass, source.size());
+ for (int i = 0; i < source.size();i++)
+ {
+ T2 obj;
+ converter (source[i], obj);
+ ScriptingObjectPtr managedObject = CreateScriptingObjectFromNativeStruct(klass, obj);
+ Scripting::SetScriptingArrayElement(arr, i, managedObject);
+ }
+ return arr;
+#endif
+}
+
+template<class T, class T2, class U, class TConverter>
+ScriptingArrayPtr VectorToScriptingStructArray (const U &source, ScriptingClassPtr klass, TConverter converter)
+{
+ // ToDo: if all good, remove mono pass, and use unified pass
+#if ENABLE_MONO
+ return VectorToMonoStructArray<T, T2>(source, klass, converter);
+#else
+ ScriptingArrayPtr arr = CreateScriptingArray<ScriptingObjectPtr>(klass, source.size());
+ for (int i = 0; i < source.size();i++)
+ {
+ T2 obj;
+ converter (source[i], obj);
+ Scripting::SetScriptingArrayElement(arr, i, obj);
+ }
+ return arr;
+#endif
+}
+
+
+template<class T>
+void ScriptingArrayToDynamicArray(ScriptingArrayPtr a, dynamic_array<T>& dest)
+{
+ Scripting::RaiseIfNull (a);
+ int len = GetScriptingArraySize(a);
+ dest.resize_uninitialized (len);
+ for (int i = 0; i < len; i++)
+ dest[i] = Scripting::GetScriptingArrayElement<T>(a, i);
+}
+
+template<class T, class T2>
+void ScriptingArrayToDynamicArray(ScriptingArrayPtr a, dynamic_array<T>& dest, void (*converter) (T2 &source, T &dest))
+{
+ Scripting::RaiseIfNull (a);
+ int len = GetScriptingArraySize(a);
+ dest.resize_uninitialized (len);
+ for (int i = 0; i < len; i++)
+ converter (Scripting::GetScriptingArrayElement<T2> (a, i), dest[i]);
+}
+
+template<class T, class T2>
+ScriptingArrayPtr DynamicArrayToScriptingStructArray (const dynamic_array<T> &source, ScriptingClassPtr klass, void (*converter) (const T &source, T2 &dest)) {
+ ScriptingArrayPtr arr = CreateScriptingArray<T2> (klass, source.size());
+ for (int i = 0; i < source.size();i++)
+ converter (source[i], Scripting::GetScriptingArrayElement<T2> (arr, i));
+ return arr;
+}
+
+inline
+std::string GetStringFromArray(ScriptingArrayPtr a, int i)
+{
+#if UNITY_WINRT
+ return ConvertStringToUtf8(safe_cast<Platform::String^>(Scripting::GetScriptingArrayStringElementNoRefImpl(a, i)));
+#elif UNITY_FLASH
+ // not supported yet
+ return "";
+#else
+ return MonoStringToCpp(Scripting::GetScriptingArrayStringElementNoRefImpl(a, i));
+#endif
+}
+
+inline
+bool GetBoolFromArray(ScriptingArrayPtr a, int i)
+{
+#if UNITY_WINRT
+ return safe_cast<Platform::Boolean>(Scripting::GetScriptingArrayObjectElementNoRefImpl(a, i));
+#else
+ return Scripting::GetScriptingArrayObjectElementNoRefImpl(a, i);
+#endif
+}
+
+#endif
+#endif
diff --git a/Runtime/Scripting/ScriptingManager.cpp b/Runtime/Scripting/ScriptingManager.cpp
new file mode 100644
index 0000000..5dcfd5c
--- /dev/null
+++ b/Runtime/Scripting/ScriptingManager.cpp
@@ -0,0 +1,150 @@
+#include "UnityPrefix.h"
+#if ENABLE_SCRIPTING
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include <vector>
+#include <stdlib.h>
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/Scripting/Backend/ScriptingMethodFactory.h"
+#include "Runtime/Scripting/Backend/ScriptingMethodRegistry.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+
+const char* kEngineNameSpace = "UnityEngine";
+const char* kEditorNameSpace = "UnityEditor";
+const char* kEditorInternalNameSpace = "UnityEditorInternal";
+
+ScriptingClassPtr gClassIDToClass = SCRIPTING_NULL;
+
+using namespace std;
+
+ScriptingManager::ScriptingManager (MemLabelId label, ObjectCreationMode mode, IScriptingTypeProvider* scriptingTypeProvider, IScriptingMethodFactory* scriptingMethodFactory)
+ : GlobalGameManager(label, mode)
+{
+ m_ScriptingMethodFactory = scriptingMethodFactory;
+ m_ScriptingTypeRegistry = UNITY_NEW(ScriptingTypeRegistry(scriptingTypeProvider), kMemManager);
+ m_ScriptingMethodRegistry = UNITY_NEW(ScriptingMethodRegistry(scriptingMethodFactory, m_ScriptingTypeRegistry), kMemManager);
+
+ //Registering ourselves as the monomanager manually, so that during the execution of the constructor GetMonoManager() will work.
+ SetManagerPtrInContext(ManagerContext::kMonoManager, this);
+}
+
+ScriptingManager::~ScriptingManager ()
+{
+ UNITY_DELETE(m_ScriptingTypeRegistry, kMemManager);
+ UNITY_DELETE(m_ScriptingMethodRegistry, kMemManager);
+
+ UNITY_DELETE(m_ScriptingMethodFactory, kMemManager);
+}
+
+static ScriptingClassPtr FindScriptingClassForClassID(int classID, ScriptingClassPtr baseObject)
+{
+ const char* className_c;
+#if UNITY_FLASH
+ if(classID == ClassID(Object))
+ className_c = "_Object";
+ else
+#endif
+ className_c = Object::ClassIDToString (classID).c_str();
+
+ // Found a class?
+ // Also make sure it derives from object otherwise it's not valid (Eg. RenderSettings only has public accessors and doesn't inherit from Object)
+
+ ScriptingTypeRegistry& typeRegistry = GetScriptingManager().GetScriptingTypeRegistry();
+
+ ScriptingTypePtr result = typeRegistry.GetType(kEngineNameSpace,className_c);
+
+#if UNITY_EDITOR
+ if (result== SCRIPTING_NULL)
+ result = typeRegistry.GetType (kEditorNameSpace, className_c);
+ if (result == SCRIPTING_NULL)
+ result = typeRegistry.GetType (kEditorInternalNameSpace, className_c);
+#endif
+
+ if (result != SCRIPTING_NULL && scripting_class_is_subclass_of (result, baseObject))
+ return result;
+
+ return SCRIPTING_NULL;
+}
+
+
+static ScriptingClassPtr FindScriptingClassForClassIDRecursive (int classID, ScriptingClassPtr baseObject)
+{
+ ScriptingClassPtr result = FindScriptingClassForClassID(classID,baseObject);
+
+ if (result != SCRIPTING_NULL)
+ return result;
+
+ if (classID == ClassID (Object))
+ return SCRIPTING_NULL;
+
+ return FindScriptingClassForClassIDRecursive (Object::GetSuperClassID (classID),baseObject);
+}
+
+void ScriptingManager::RebuildClassIDToScriptingClass ()
+{
+#if ENABLE_SCRIPTING
+ // Collect all engine classes
+ vector<SInt32> allEngineClasses;
+ Object::FindAllDerivedClasses (ClassID (Object), &allEngineClasses, false);
+
+ // Resize lookup table to fit all engine classes
+ SInt32 highest = 0;
+ for (int i=0;i<allEngineClasses.size ();i++)
+ highest = max (allEngineClasses[i], highest);
+ m_ClassIDToMonoClass.clear ();
+ m_ClassIDToMonoClass.resize (highest +1, SCRIPTING_NULL);
+ gClassIDToClass = m_ClassIDToMonoClass[0];
+
+ m_ScriptingClassToClassID.clear();
+ // Get the mono class from the classID by looking up the mono class by name
+ // if a mono class can't be found check the super classes recursively
+ ScriptingClassPtr baseObject = GetScriptingTypeRegistry().GetType("UnityEngine","Object");
+ for (int i=0;i<allEngineClasses.size ();i++)
+ {
+ int classID = allEngineClasses[i];
+
+ ScriptingClassPtr klass = FindScriptingClassForClassIDRecursive (classID,baseObject);
+ AssertIf(klass == SCRIPTING_NULL);
+ m_ClassIDToMonoClass[classID] = klass;
+
+ //search again, but without searching basetypes. we need to do this because some enginetypes have no managed wrapper. (EllipsoidParticleEmitter),
+ //so they can return the baseclass type. we want that for m_ClassIDToMonoClass but we do not want that for m_ScriptingClassToClassID.
+
+ klass = FindScriptingClassForClassID (classID,baseObject);
+ if (klass)
+ m_ScriptingClassToClassID[klass] = classID;
+ }
+#endif
+}
+
+ScriptingClassPtr ScriptingManager::ClassIDToScriptingClass (int classID)
+{
+ AssertIf (classID == -1);
+ AssertIf (m_ClassIDToMonoClass.size () <= classID);
+ return m_ClassIDToMonoClass[classID];
+}
+
+int ScriptingManager::ClassIDForScriptingClass (ScriptingClassPtr klass)
+{
+ ScriptingClassMap::iterator iterator = m_ScriptingClassToClassID.find(klass);
+ if (iterator == m_ScriptingClassToClassID.end())
+ return -1;
+
+ return iterator->second;
+}
+
+ScriptingObjectPtr ScriptingManager::CreateInstance(ScriptingClassPtr klass)
+{
+ if (!klass)
+ return SCRIPTING_NULL;
+
+ return scripting_object_new (klass);
+}
+
+ScriptingManager& GetScriptingManager()
+{
+ return reinterpret_cast<ScriptingManager&> (GetManagerFromContext (ManagerContext::kMonoManager));
+}
+#endif
diff --git a/Runtime/Scripting/ScriptingManager.h b/Runtime/Scripting/ScriptingManager.h
new file mode 100644
index 0000000..4c446f2
--- /dev/null
+++ b/Runtime/Scripting/ScriptingManager.h
@@ -0,0 +1,63 @@
+#ifndef _SCRIPTINGMANAGER_H_
+#define _SCRIPTINGMANAGER_H_
+
+#if ENABLE_SCRIPTING
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Mono/MonoScriptManager.h"
+#include "Runtime/Scripting/CommonScriptingClasses.h"
+#include "Runtime/Utilities/vector_map.h"
+#include "Runtime/Scripting/Backend/ScriptingMethodRegistry.h"
+#include "Runtime/Modules/ExportModules.h"
+
+class IScriptingTypeProvider;
+class ScriptingMethodRegistry;
+class ScriptingTypeRegistry;
+class IScriptingMethodFactory;
+
+extern const char* kEngineNameSpace;
+extern const char* kEditorNameSpace;
+extern ScriptingClassPtr gClassIDToClass;
+
+class EXPORT_COREMODULE ScriptingManager : public GlobalGameManager
+{
+public:
+ MonoScriptManager& GetMonoScriptManager() { return m_MonoScriptManager; }
+ ScriptingMethodRegistry& GetScriptingMethodRegistry() { return *m_ScriptingMethodRegistry; }
+ ScriptingTypeRegistry& GetScriptingTypeRegistry() { return *m_ScriptingTypeRegistry; }
+ const CommonScriptingClasses& GetCommonClasses () { return m_CommonScriptingClasses; }
+ ScriptingClassPtr ClassIDToScriptingClass (int classID);
+ int ClassIDForScriptingClass(ScriptingClassPtr klass);
+ virtual void RebuildClassIDToScriptingClass ();
+ ScriptingObjectPtr CreateInstance(ScriptingClassPtr klass);
+ virtual bool IsTrustedToken (const std::string& /*publicKeyToken*/){ return false; }
+
+ ~ScriptingManager();
+protected:
+ ScriptingManager (MemLabelId label, ObjectCreationMode mode, IScriptingTypeProvider* scriptingTypeProvider, IScriptingMethodFactory* scriptingMethodFactory);
+
+ MonoScriptManager m_MonoScriptManager;
+ CommonScriptingClasses m_CommonScriptingClasses;
+ typedef std::vector<ScriptingTypePtr> ClassIDToMonoClass;
+ ClassIDToMonoClass m_ClassIDToMonoClass;
+
+ typedef vector_map<ScriptingTypePtr, int> ScriptingClassMap;
+ ScriptingClassMap m_ScriptingClassToClassID;
+
+ ScriptingMethodRegistry* m_ScriptingMethodRegistry;
+ ScriptingTypeRegistry* m_ScriptingTypeRegistry;
+ IScriptingMethodFactory* m_ScriptingMethodFactory;
+};
+
+EXPORT_COREMODULE ScriptingManager& GetScriptingManager ();
+
+inline MonoScriptManager& GetMonoScriptManager() { return GetScriptingManager().GetMonoScriptManager(); }
+
+inline ScriptingMethodRegistry& GetScriptingMethodRegistry() { return GetScriptingManager().GetScriptingMethodRegistry(); }
+inline ScriptingTypeRegistry& GetScriptingTypeRegistry() { return GetScriptingManager().GetScriptingTypeRegistry(); }
+
+#define ScriptingClassFor(x) GetScriptingManager().ClassIDToScriptingClass(CLASS_##x)
+
+#endif
+
+#endif
diff --git a/Runtime/Scripting/ScriptingObjectOfType.h b/Runtime/Scripting/ScriptingObjectOfType.h
new file mode 100644
index 0000000..cca6bde
--- /dev/null
+++ b/Runtime/Scripting/ScriptingObjectOfType.h
@@ -0,0 +1,142 @@
+#pragma once
+
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Scripting.h"
+#include "ScriptingUtility.h"//We need to go back and remove this.
+
+template<class T>
+struct ScriptingObjectOfType
+{
+private:
+ ScriptingObjectPtr object;
+public:
+ ScriptingObjectOfType(ScriptingObjectPtr object)
+ {
+ this->object = object;
+ }
+
+ T& GetReference () const
+ {
+ T* ptr = GetPtr();
+
+ if (ptr != NULL)
+ return *ptr;
+
+ Scripting::RaiseNullExceptionObject (object);
+ return *(T*)NULL;
+ }
+
+ T* GetPtr () const
+ {
+ if (IsNullPtr())
+ return NULL;
+
+ void* cachedPtr = GetCachedPtr();
+ if (cachedPtr != NULL)
+ {
+ AssertIf(reinterpret_cast<Object*> (cachedPtr)->GetInstanceID() != GetInstanceID());
+ return (T*)cachedPtr;
+ }
+
+ T* temp = dynamic_instanceID_cast<T*> (GetInstanceID());
+ return temp;
+ }
+
+ bool IsValidObjectReference () const
+ {
+ if (IsNullPtr())
+ return false;
+
+ void* cachedPtr = GetCachedPtr();
+ if (cachedPtr != NULL)
+ {
+ AssertIf(reinterpret_cast<Object*> (cachedPtr)->GetInstanceID() != GetInstanceID());
+ return (T*)cachedPtr;
+ }
+
+ T* temp = dynamic_instanceID_cast<T*> (GetInstanceID());
+ if (temp == NULL)
+ return false;
+
+ // MonoBheaviours are only allowed to be the instance themselves (cached pointer)
+ // otherwise they are dangling references who happen to have the same instanceID.
+ // Thus not a valid reference.
+ if (temp->GetClassID() == ClassID(MonoBehaviour))
+ return false;
+
+ return true;
+ }
+
+
+ operator T* () const
+ {
+ return GetPtr ();
+ }
+
+ operator PPtr<T> () const
+ {
+ if (IsNullPtr())
+ return PPtr<T> ();
+
+ return PPtr<T> (GetInstanceID());
+ }
+
+ T& operator * () const
+ {
+ return GetReference ();
+ }
+
+ T* operator -> () const
+ {
+ return &GetReference ();
+ }
+
+ bool IsNullPtr() const
+ {
+ return object == SCRIPTING_NULL;
+ }
+
+ inline int GetInstanceID() const
+ {
+ return Scripting::GetInstanceIDFromScriptingWrapper(object);
+ }
+
+ inline void SetCachedPtr (void* cachedPtr)
+ {
+ Scripting::SetCachedPtrOnScriptingWrapper(object, cachedPtr);
+ }
+
+ inline void SetInstanceID (int instanceID)
+ {
+ Scripting::SetInstanceIDOnScriptingWrapper(object, instanceID);
+ }
+
+ inline void* GetCachedPtr() const
+ {
+ return Scripting::GetCachedPtrFromScriptingWrapper(object);
+ }
+
+ #if MONO_QUALITY_ERRORS
+ inline void SetError (ScriptingStringPtr error)
+ {
+ Scripting::SetErrorOnScriptingWrapper(object, error);
+ }
+ #endif
+
+#if UNITY_WINRT
+ ScriptingObjectOfType<T>& operator = (decltype(__nullptr))
+ {
+ object = nullptr;
+ return *this;
+ }
+#endif
+
+ inline ScriptingObjectPtr GetScriptingObject() const
+ {
+ return object;
+ }
+};
+
+
+
+
diff --git a/Runtime/Scripting/ScriptingObjectWithIntPtrField.h b/Runtime/Scripting/ScriptingObjectWithIntPtrField.h
new file mode 100644
index 0000000..e4cbf99
--- /dev/null
+++ b/Runtime/Scripting/ScriptingObjectWithIntPtrField.h
@@ -0,0 +1,139 @@
+#pragma once
+
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+#if 1 //!UNITY_WINRT
+#if ENABLE_MONO
+extern int* s_MonoDomainContainer;
+#endif
+template<class T>
+struct ScriptingObjectWithIntPtrField
+{
+ ScriptingObjectPtr object;
+
+ ScriptingObjectWithIntPtrField(ScriptingObjectPtr o) : object(o) {}
+
+ T& operator * () const
+ {
+ return GetReference ();
+ }
+
+ T* operator -> () const
+ {
+ return &GetReference ();
+ }
+
+ operator T* () const
+ {
+ return GetPtr ();
+ }
+
+ inline T& GetReference () const
+ {
+ void* nativePointer = GetPtr();
+
+ if (nativePointer == NULL)
+ Scripting::RaiseNullException ("");
+
+ return *reinterpret_cast<T*> (nativePointer);
+ }
+
+ typedef void ExecuteOnManagedFinalizer(void* obj);
+
+ inline void SetPtr(T* ptr, ExecuteOnManagedFinalizer* eomf = NULL)
+ {
+ #if ENABLE_MONO
+ Assert(ptr == NULL || GET_CURRENT_ALLOC_ROOT_HEADER() == NULL
+ || GET_CURRENT_ALLOC_ROOT_HEADER() == GET_ALLOC_HEADER(s_MonoDomainContainer, kMemMono));
+ ExtractMonoObjectData<T*>(object) = ptr;
+ (void)eomf;
+ #elif UNITY_FLASH
+
+ __asm __volatile__("obj_g0 = marshallmap.getObjectWithId(%0);"::"r"(object));
+ __asm __volatile__("obj_g0.m_Ptr = %0;"::"r"(ptr));
+
+ //We only insert this if we actually have a way to clean it...if we don't then there's no point in inserting it in the finalizer map.
+ if(eomf == NULL)
+ return;
+
+ __asm __volatile__("finalizePtrsMap[%0] = %1;" : : "r"(ptr),"r"(eomf));
+ __asm __volatile__("finalizeMap[obj_g0] = %0;"::"r"(ptr));
+ #elif UNITY_WINRT
+ static BridgeInterface::IMarshalling^ marshaller = s_WinRTBridge->Marshalling;
+ marshaller->MarshalNativePtrIntoFirstFieldGC(object.GetHandle(), (int)ptr);
+ //typedef void (__stdcall *TMarshalNativePtrIntoFirstField)(ScriptingObjectPtr, int);
+ //static TMarshalNativePtrIntoFirstField call = (TMarshalNativePtrIntoFirstField)GetWinRTMarshalling()->GetMarshalNativePtrIntoFirstFieldDelegatePtr();
+ //call(object, (int)ptr);
+ #endif
+ }
+
+ inline T* GetPtr() const
+ {
+ if (!object)
+ return NULL;
+
+ #if ENABLE_MONO
+ return ExtractMonoObjectData<T*>(object);
+ #elif UNITY_FLASH
+ T* result;
+ __asm __volatile__("%0 = marshallmap.getObjectWithId(%1).m_Ptr;" : "=r"(result) : "r"(object));
+ return result;
+ #elif UNITY_WINRT
+ static BridgeInterface::IMarshalling^ marshaller = s_WinRTBridge->Marshalling;
+ return (T*)marshaller->MarshalFirstFieldIntoNativePtrGC(object.GetHandle());
+ //typedef int (__stdcall *TMarshalFirstFieldIntoNativePtr)(ScriptingObjectPtr);
+ //static TMarshalFirstFieldIntoNativePtr call = (TMarshalFirstFieldIntoNativePtr)GetWinRTMarshalling()->GetMarshalFirstFieldIntoNativePtrDelegatePtr();
+ //return (T*)call(object);
+ #endif
+ }
+
+ inline ScriptingObjectPtr GetScriptingObject()
+ {
+ return object;
+ }
+};
+#else
+// THIS WON'T WORK because cachedPtr doesn't derive from Object, thus we cannot extract ScriptingObjectPtr from cachedPtr
+template<class T>
+struct ScriptingObjectWithIntPtrField
+{
+ T* cachedPtr;
+
+ ScriptingObjectWithIntPtrField(void* o) : cachedPtr((T*)o) {}
+
+ T& operator * () const
+ {
+ return *cachedPtr;
+ }
+
+ T* operator -> () const
+ {
+ return cachedPtr;
+ }
+
+ operator T* () const
+ {
+ return cachedPtr;
+ }
+
+ inline T& GetReference () const
+ {
+ return *cachedPtr;
+ }
+
+ inline T* GetPtr() const
+ {
+ return cachedPtr;
+ }
+
+ typedef void ExecuteOnManagedFinalizer(void* obj);
+
+ inline void SetPtr(T* ptr, ExecuteOnManagedFinalizer* eomf = NULL)
+ {
+ if (cachedPtr != NULL)
+ {
+ s_WinRTBridge->Marshalling->MarshalNativePtrIntoFirstField(ObjectToScriptingObjectImpl(cachedPtr), (int)ptr);
+ }
+ }
+};
+#endif
diff --git a/Runtime/Scripting/ScriptingUtility.h b/Runtime/Scripting/ScriptingUtility.h
new file mode 100644
index 0000000..7670d59
--- /dev/null
+++ b/Runtime/Scripting/ScriptingUtility.h
@@ -0,0 +1,48 @@
+#ifndef SCRIPTINGUTILITY_H
+#define SCRIPTINGUTILITY_H
+
+#if ENABLE_SCRIPTING
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+
+#include "Runtime/BaseClasses/BaseObject.h"
+
+#include "Runtime/Scripting/Backend/ScriptingArguments.h"
+#include "Runtime/Scripting/Backend/ScriptingInvocation.h"
+#include "Runtime/Scripting/ICallString.h"
+
+#if ENABLE_MONO_API_THREAD_CHECK && ENABLE_MONO
+# include "Runtime/Threads/Thread.h"
+# define SCRIPTINGAPI_CONSTRUCTOR_CHECK(NAME) \
+ if (GetMonoBehaviourInConstructor() == 0) ; else { \
+ Scripting::RaiseArgumentException("You are not allowed to call " #NAME " when declaring a variable.\nMove it to the line after without a variable declaration.\nDon't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function."); \
+ }
+
+# define SCRIPTINGAPI_THREAD_CHECK(NAME) \
+ if ( Thread::CurrentThreadIsMainThread() ) ; else \
+ {\
+ ErrorString(#NAME " can only be called from the main thread.\nConstructors and field initializers will be executed from the loading thread when loading a scene.\nDon't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function."); \
+ Scripting::RaiseArgumentException(#NAME " can only be called from the main thread.\nConstructors and field initializers will be executed from the loading thread when loading a scene.\nDon't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.");\
+ }
+
+#else
+# define SCRIPTINGAPI_THREAD_CHECK(NAME)
+# define SCRIPTINGAPI_CONSTRUCTOR_CHECK(NAME)
+#endif
+
+#if ENABLE_MONO
+# include "Runtime/Mono/MonoIncludes.h"
+# include "Runtime/Mono/MonoUtility.h"
+#elif UNITY_WINRT
+#elif UNITY_FLASH
+# include "AS3Utility.h"
+#endif
+
+#include "Scripting.h"
+
+//todo: put these back at the top when we've cleaned up the dependency mess
+#include "Runtime/Scripting/ScriptingObjectOfType.h"
+#include "Runtime/Scripting/ReadOnlyScriptingObjectOfType.h"
+
+#endif //ENABLE_SCRIPTING
+
+#endif
diff --git a/Runtime/Scripting/Scripting_Flash.cpp b/Runtime/Scripting/Scripting_Flash.cpp
new file mode 100644
index 0000000..a36a950
--- /dev/null
+++ b/Runtime/Scripting/Scripting_Flash.cpp
@@ -0,0 +1,176 @@
+#include "UnityPrefix.h"
+
+#include "Scripting.h"
+#include "ScriptingUtility.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+
+namespace Scripting
+{
+
+void RaiseAS3NativeException(const char* type, const char* format, va_list list)
+{
+ va_list ap = list;
+ va_copy (ap, list);
+ char buffer[1024 * 5];
+ vsnprintf (buffer, 1024 * 5, format, ap);
+ string typeString;
+ typeString += type;
+ typeString += ":";
+ typeString += buffer;
+ //ErrorString(typeString);
+ Ext_Flash_ThrowError(typeString.c_str());
+ va_end (ap);
+}
+
+void RaiseArgumentException(const char* format, ...)
+{
+ va_list va;
+ va_start( va, format );
+ RaiseAS3NativeException("RaiseArgumentException", format, va);
+}
+
+void RaiseSecurityException(const char* format, ...)
+{
+ va_list va;
+ va_start( va, format );
+ RaiseAS3NativeException("RaiseSecurityException", format, va);
+}
+
+void RaiseNullException(const char* format, ...)
+{
+ va_list va;
+ va_start( va, format );
+ RaiseAS3NativeException("NullReferenceException", format, va);
+}
+
+void RaiseMonoException (const char* format, ...)
+{
+ va_list va;
+ va_start( va, format );
+ RaiseAS3NativeException("RaiseMonoException", format, va);
+}
+
+void RaiseNullExceptionObject(ScriptingObjectPtr object)
+{
+ ErrorString("RaiseNullExceptionObject not implemented");
+ Ext_Flash_ThrowError("RaiseNullExceptionObject");
+}
+
+void RaiseOutOfRangeException(const char* format, ...)
+{
+ ErrorString("RaiseOutOfRangeException not implemented");
+ Ext_Flash_ThrowError("RaiseOutOfRangeException");
+}
+
+
+void* GetCachedPtrFromScriptingWrapper(ScriptingObjectPtr object)
+{
+ if(object == SCRIPTING_NULL)
+ return NULL;
+
+ void* result;
+ __asm __volatile__("%0 = (marshallmap.getObjectWithId(%1) as UnityEngine._Object).m_CachedPtr;" : "=r"(result) : "r"(object));
+ return result;
+}
+
+void SetCachedPtrOnScriptingWrapper(ScriptingObjectPtr object, void* cachedPtr)
+{
+ __asm __volatile__("(marshallmap.getObjectWithId(%0) as UnityEngine._Object).m_CachedPtr = %1;" : : "r"(object), "r"(cachedPtr));
+}
+
+int GetInstanceIDFromScriptingWrapper(ScriptingObjectPtr object)
+{
+ if(object == SCRIPTING_NULL)
+ return 0;
+
+ int result;
+ __asm __volatile__("%0 = (marshallmap.getObjectWithId(%1) as UnityEngine._Object).m_InstanceID;" : "=r"(result) : "r"(object));
+ return result;
+}
+
+void SetInstanceIDOnScriptingWrapper(ScriptingObjectPtr object, int instanceID)
+{
+ __asm __volatile__("(marshallmap.getObjectWithId(%0) as UnityEngine._Object).m_InstanceID = %1;" : : "r"(object), "r"(instanceID));
+}
+
+void SetErrorOnScriptingWrapper(ScriptingObjectPtr object, ScriptingStringPtr error)
+{
+
+}
+
+ScriptingObjectPtr InstantiateScriptingWrapperForClassID(int classID)
+{
+ const string& name = Object::ClassIDToString(classID);
+ const char* cname = name.c_str();
+
+ ScriptingObjectPtr result = Ext_Scripting_InstantiateScriptingWrapperForClassWithName(cname);
+ if(result != NULL)
+ return result;
+
+ int superClassID = Object::GetSuperClassID(classID);
+ if(superClassID != ClassID(Object))
+ return InstantiateScriptingWrapperForClassID(superClassID);
+
+ return NULL;
+}
+
+ScriptingObjectPtr ScriptingObjectNULL(ScriptingClassPtr klass)
+{
+ return SCRIPTING_NULL;
+}
+
+void RaiseIfNull(ScriptingObjectPtr object)
+{
+ if(object == SCRIPTING_NULL)
+ RaiseNullException("(null)");
+}
+
+void RaiseIfNull(void* object)
+{
+ if(object == NULL)
+ RaiseNullException("(null)");
+}
+
+void SetScriptingArrayObjectElementImpl(ScriptingArrayPtr a, int i, ScriptingObjectPtr value)
+{
+ void* raw = scripting_array_element_ptr(a, i, sizeof(ScriptingObjectPtr));
+ *(ScriptingObjectPtr*)raw = value;
+}
+
+void SetScriptingArrayStringElementImpl(ScriptingArrayPtr a, int i, ScriptingStringPtr value)
+{
+ void* raw = scripting_array_element_ptr(a, i, sizeof(ScriptingStringPtr));
+ *(ScriptingStringPtr*)raw = value;
+}
+
+ScriptingStringPtr* GetScriptingArrayStringElementImpl(ScriptingArrayPtr a, int i)
+{
+ return (ScriptingStringPtr*)scripting_array_element_ptr(a, i, sizeof(ScriptingStringPtr));
+}
+
+ScriptingObjectPtr* GetScriptingArrayObjectElementImpl(ScriptingArrayPtr a, int i)
+{
+ return (ScriptingObjectPtr*)scripting_array_element_ptr(a, i, sizeof(ScriptingObjectPtr));
+}
+
+ScriptingStringPtr* GetScriptingArrayStringStartImpl(ScriptingArrayPtr a)
+{
+ return (ScriptingStringPtr*)scripting_array_element_ptr(a, 0, sizeof(ScriptingStringPtr));
+}
+
+ScriptingObjectPtr* GetScriptingArrayObjectStartImpl(ScriptingArrayPtr a)
+{
+ return (ScriptingObjectPtr*)scripting_array_element_ptr(a, 0, sizeof(ScriptingObjectPtr));
+}
+
+ScriptingStringPtr GetScriptingArrayStringElementNoRefImpl(ScriptingArrayPtr a, int i)
+{
+ return *((ScriptingStringPtr*)scripting_array_element_ptr(a, i, sizeof(ScriptingStringPtr)));
+}
+
+ScriptingObjectPtr GetScriptingArrayObjectElementNoRefImpl(ScriptingArrayPtr a, int i)
+{
+ return *((ScriptingObjectPtr*)scripting_array_element_ptr(a, i, sizeof(ScriptingObjectPtr)));
+}
+
+}//namespace Scripting \ No newline at end of file
diff --git a/Runtime/Scripting/Scripting_Mono.cpp b/Runtime/Scripting/Scripting_Mono.cpp
new file mode 100644
index 0000000..848a6a0
--- /dev/null
+++ b/Runtime/Scripting/Scripting_Mono.cpp
@@ -0,0 +1,247 @@
+#include "UnityPrefix.h"
+
+#include <stdarg.h>
+#include "Scripting.h"
+#include "Runtime/Mono/MonoManager.h"
+
+#ifdef _MSC_VER
+#define va_copy(a,z) ((void)((a)=(z)))
+#endif
+
+static UnityEngineObjectMemoryLayout* GetUnityEngineObjectMemoryLayout(ScriptingObjectPtr object)
+{
+ return reinterpret_cast<UnityEngineObjectMemoryLayout*>(((char*)object) + kMonoObjectOffset);
+}
+
+namespace Scripting
+{
+
+void* GetCachedPtrFromScriptingWrapper(ScriptingObjectPtr object)
+{
+ if(object == SCRIPTING_NULL)
+ return NULL;
+
+ return GetUnityEngineObjectMemoryLayout(object)->cachedPtr;
+}
+
+void SetCachedPtrOnScriptingWrapper(ScriptingObjectPtr object, void* cachedPtr)
+{
+ GetUnityEngineObjectMemoryLayout(object)->cachedPtr = cachedPtr;
+}
+
+int GetInstanceIDFromScriptingWrapper(ScriptingObjectPtr object)
+{
+ if(object == SCRIPTING_NULL)
+ return 0;
+
+ return GetUnityEngineObjectMemoryLayout(object)->instanceID;
+}
+
+void SetInstanceIDOnScriptingWrapper(ScriptingObjectPtr object, int instanceID)
+{
+ GetUnityEngineObjectMemoryLayout(object)->instanceID = instanceID;
+}
+
+void SetErrorOnScriptingWrapper(ScriptingObjectPtr object, ScriptingStringPtr error)
+{
+#if MONO_QUALITY_ERRORS
+ GetUnityEngineObjectMemoryLayout(object)->error = error;
+#endif
+}
+
+ScriptingObjectPtr InstantiateScriptingWrapperForClassID(int classID)
+{
+ return (ScriptingObjectPtr)MonoInstantiateScriptingWrapperForClassID(classID);
+}
+
+ScriptingObjectPtr ScriptingObjectNULL(ScriptingClassPtr klass)
+{
+ return MonoObjectNULL(klass);
+}
+
+ScriptingObjectPtr ScriptingObjectNULL(ScriptingClassPtr klass, ScriptingStringPtr error)
+{
+#if MONO_QUALITY_ERRORS
+ return MonoObjectNULL(klass,error);
+#else
+ return MonoObjectNULL(klass);
+#endif
+}
+
+
+DOES_NOT_RETURN void RaiseDotNetExceptionImpl (const char* ns, const char* type, const char* format, va_list list)
+{
+ va_list ap;
+ va_copy (ap, list);
+
+ char buffer[1024 * 5];
+ vsnprintf (buffer, 1024 * 5, format, ap);
+ va_end (ap);
+
+ MonoException* exception = mono_exception_from_name_msg (mono_get_corlib (), ns, type, buffer);
+ mono_raise_exception (exception);
+
+}
+
+DOES_NOT_RETURN void RaiseSystemExceptionImpl (const char* type, const char* format, va_list list)
+{
+ va_list ap;
+ va_copy (ap, list);
+ RaiseDotNetExceptionImpl("System",type,format,ap);
+}
+
+void RaiseManagedException (const char *ns, const char *type, const char *format, ...)
+{
+ va_list va;
+ va_start (va, format);
+ RaiseDotNetExceptionImpl (ns, type, format, va);
+}
+
+void RaiseMonoException (const char* format, ...)
+{
+ va_list va;
+ va_start( va, format );
+
+ char buffer[1024 * 5];
+ vsnprintf (buffer, 1024 * 5, format, va);
+
+ MonoException* exception = mono_exception_from_name_msg (GetMonoManager ().GetEngineImage (), kEngineNameSpace, "UnityException", buffer);
+ mono_raise_exception (exception);
+}
+
+void RaiseOutOfRangeException (const char* format, ...)
+{
+ va_list va;
+ va_start( va, format );
+ RaiseSystemExceptionImpl("IndexOutOfRangeException", format, va);
+}
+
+void RaiseNullException (const char* format, ...)
+{
+ va_list va;
+ va_start( va, format );
+ RaiseSystemExceptionImpl("NullReferenceException", format, va);
+}
+
+void RaiseArgumentException (const char* format, ...)
+{
+ va_list va;
+ va_start( va, format );
+ RaiseSystemExceptionImpl("ArgumentException", format, va);
+}
+
+void RaiseInvalidOperationException (const char* format, ...)
+{
+ va_list va;
+ va_start( va, format );
+ RaiseSystemExceptionImpl("InvalidOperationException", format, va);
+}
+
+void RaiseSecurityException (const char* format, ...)
+{
+ va_list va;
+ va_start( va, format );
+ RaiseDotNetExceptionImpl("System.Security","SecurityException", format, va);
+}
+
+void RaiseNullExceptionObject (MonoObject* object)
+{
+ #if MONO_QUALITY_ERRORS
+ if (object)
+ {
+ MonoClass* klass = mono_object_get_class(object);
+ if (mono_class_is_subclass_of (mono_object_get_class(object), GetMonoManager().ClassIDToScriptingClass(ClassID(Object)), false))
+ {
+ UnityEngineObjectMemoryLayout& data = ExtractMonoObjectData<UnityEngineObjectMemoryLayout>(object);
+ string error = MonoStringToCpp(data.error);
+
+ // The object was destroyed underneath us - but if we hit a MissingReferenceException then we show that instead!
+ if (data.instanceID != 0 && error.find ("MissingReferenceException:") != 0)
+ {
+ error = Format("The object of type '%s' has been destroyed but you are still trying to access it.\n"
+ "Your script should either check if it is null or you should not destroy the object.", mono_class_get_name(klass));
+
+ MonoException* exception = mono_exception_from_name_msg (GetMonoManager().GetEngineImage(), "UnityEngine", "MissingReferenceException", error.c_str());
+ mono_raise_exception (exception);
+ }
+
+ // We got an error message, parse it and throw it
+ if (data.error)
+ {
+ error = MonoStringToCpp(data.error);
+ string::size_type exceptionTypePos = error.find(':');
+ if (exceptionTypePos != string::npos)
+ {
+ string type = string(error.begin(), error.begin() + exceptionTypePos);
+ error.erase(error.begin(), error.begin()+exceptionTypePos + 1);
+
+ MonoException* exception = mono_exception_from_name_msg (GetMonoManager().GetEngineImage(), "UnityEngine", type.c_str(), error.c_str ());
+ mono_raise_exception (exception);
+ }
+ }
+ }
+ }
+
+ MonoException* exception = mono_exception_from_name_msg (mono_get_corlib (), "System", "NullReferenceException", "");
+ mono_raise_exception (exception);
+ #else
+ MonoException* exception = mono_exception_from_name_msg (mono_get_corlib (), "System", "NullReferenceException", "");
+ mono_raise_exception (exception);
+ #endif
+}
+
+void RaiseIfNull(ScriptingObjectPtr object)
+{
+ if(object == SCRIPTING_NULL)
+ RaiseNullException("(null)");
+}
+
+void RaiseIfNull(void* object)
+{
+ if(object == NULL)
+ RaiseNullException("(null)");
+}
+
+void SetScriptingArrayObjectElementImpl(ScriptingArrayPtr a, int i, ScriptingObjectPtr value)
+{
+ void* raw = scripting_array_element_ptr(a, i, sizeof(ScriptingObjectPtr));
+ *(ScriptingObjectPtr*)raw = value;
+}
+
+void SetScriptingArrayStringElementImpl(ScriptingArrayPtr a, int i, ScriptingStringPtr value)
+{
+ void* raw = scripting_array_element_ptr(a, i, sizeof(ScriptingStringPtr));
+ *(ScriptingStringPtr*)raw = value;
+}
+
+ScriptingStringPtr* GetScriptingArrayStringElementImpl(ScriptingArrayPtr a, int i)
+{
+ return (ScriptingStringPtr*)scripting_array_element_ptr(a, i, sizeof(ScriptingStringPtr));
+}
+
+ScriptingObjectPtr* GetScriptingArrayObjectElementImpl(ScriptingArrayPtr a, int i)
+{
+ return (ScriptingObjectPtr*)scripting_array_element_ptr(a, i, sizeof(ScriptingObjectPtr));
+}
+
+ScriptingStringPtr* GetScriptingArrayStringStartImpl(ScriptingArrayPtr a)
+{
+ return (ScriptingStringPtr*)scripting_array_element_ptr(a, 0, sizeof(ScriptingStringPtr));
+}
+
+ScriptingObjectPtr* GetScriptingArrayObjectStartImpl(ScriptingArrayPtr a)
+{
+ return (ScriptingObjectPtr*)scripting_array_element_ptr(a, 0, sizeof(ScriptingObjectPtr));
+}
+
+ScriptingStringPtr GetScriptingArrayStringElementNoRefImpl(ScriptingArrayPtr a, int i)
+{
+ return *((ScriptingStringPtr*)scripting_array_element_ptr(a, i, sizeof(ScriptingStringPtr)));
+}
+
+ScriptingObjectPtr GetScriptingArrayObjectElementNoRefImpl(ScriptingArrayPtr a, int i)
+{
+ return *((ScriptingObjectPtr*)scripting_array_element_ptr(a, i, sizeof(ScriptingObjectPtr)));
+}
+
+}//namespace Scripting
diff --git a/Runtime/Scripting/Scripting_WinRT.cpp b/Runtime/Scripting/Scripting_WinRT.cpp
new file mode 100644
index 0000000..e995ca7
--- /dev/null
+++ b/Runtime/Scripting/Scripting_WinRT.cpp
@@ -0,0 +1,175 @@
+#include "UnityPrefix.h"
+
+#include "Scripting.h"
+#include "ScriptingManager.h"
+#include "Backend/ScriptingTypeRegistry.h"
+#include "Backend/ScriptingBackendApi.h"
+#include "PlatformDependent/MetroPlayer/MetroUtils.h"
+
+#include <wrl/wrappers/corewrappers.h>
+
+static BridgeInterface::IArrayTools^& GetWinRTArrayTools()
+{
+ static BridgeInterface::IArrayTools^ s_Cached = s_WinRTBridge->ArrayTools;
+ return s_Cached;
+}
+
+static BridgeInterface::IUnityEngineObjectTools^& GetWinRTUnityEngineObjectTools()
+{
+ static BridgeInterface::IUnityEngineObjectTools^ s_Cached = s_WinRTBridge->UnityEngineObjectTools;
+ return s_Cached;
+}
+
+namespace Scripting
+{
+
+void* GetCachedPtrFromScriptingWrapper(ScriptingObjectPtr object)
+{
+ if(object == SCRIPTING_NULL)
+ return NULL;
+
+ return (void*)GetWinRTUnityEngineObjectTools()->ScriptingObjectGetReferenceDataCachedPtrGC(object.GetHandle());
+}
+
+void SetCachedPtrOnScriptingWrapper(ScriptingObjectPtr object, void* cachedPtr)
+{
+ GetWinRTUnityEngineObjectTools()->ScriptingObjectSetReferenceDataCachedPtrGC(object.GetHandle(), (int)cachedPtr);
+}
+
+int GetInstanceIDFromScriptingWrapper(ScriptingObjectPtr object)
+{
+ if(object == SCRIPTING_NULL)
+ return 0;
+
+ return GetWinRTUnityEngineObjectTools()->ScriptingObjectGetReferenceDataInstanceIDGC(object.GetHandle());
+}
+
+void SetInstanceIDOnScriptingWrapper(ScriptingObjectPtr object, int instanceID)
+{
+ GetWinRTUnityEngineObjectTools()->ScriptingObjectSetReferenceDataInstanceIDGC(object.GetHandle(), instanceID);
+}
+
+void SetErrorOnScriptingWrapper(ScriptingObjectPtr object, ScriptingStringPtr error)
+{
+
+}
+
+#if !UNITY_EXTERNAL_TOOL
+ScriptingObjectPtr InstantiateScriptingWrapperForClassID(int classID)
+{
+ const string& name = Object::ClassIDToString(classID);
+
+ ScriptingClassPtr klass = GetScriptingTypeRegistry().GetType("UnityEngine", name.c_str());
+ if (klass != SCRIPTING_NULL)
+ return scripting_object_new(klass);
+
+ int superClassID = Object::GetSuperClassID(classID);
+ if(superClassID != ClassID(Object))
+ return InstantiateScriptingWrapperForClassID(superClassID);
+
+ return SCRIPTING_NULL;
+}
+#endif
+
+ScriptingObjectPtr ScriptingObjectNULL(ScriptingClassPtr klass)
+{
+ return SCRIPTING_NULL;
+}
+
+void RaiseSecurityException(const char* format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ throw ref new Platform::FailureException(ConvertUtf8ToString(VFormat(format, va)));
+}
+
+void RaiseNullException(const char* format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ throw ref new Platform::FailureException(ConvertUtf8ToString(VFormat(format, va)));
+}
+
+void RaiseNullExceptionObject(ScriptingObjectPtr object)
+{
+ throw ref new Platform::NullReferenceException();
+}
+
+void RaiseMonoException (const char* format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ throw ref new Platform::FailureException(ConvertUtf8ToString(VFormat(format, va)));
+}
+
+void RaiseOutOfRangeException(const char* format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ throw ref new Platform::OutOfBoundsException(ConvertUtf8ToString(VFormat(format, va)));
+}
+
+void RaiseArgumentException (const char* format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ throw ref new Platform::InvalidArgumentException(ConvertUtf8ToString(VFormat(format, va)));
+}
+
+void RaiseIfNull(ScriptingObjectPtr object)
+{
+ if(object == SCRIPTING_NULL)
+ throw ref new Platform::NullReferenceException();
+}
+
+void RaiseIfNull(void* object)
+{
+ if(object == NULL)
+ RaiseNullException("(null)");
+}
+
+void SetScriptingArrayObjectElementImpl(ScriptingArrayPtr a, int i, ScriptingObjectPtr value)
+{
+ GetWinRTArrayTools()->SetArrayValue(a.GetHandle(), i, value.GetHandle());
+}
+
+void SetScriptingArrayStringElementImpl(ScriptingArrayPtr a, int i, ScriptingStringPtr value)
+{
+ GetWinRTArrayTools()->SetArrayStringValue(a.GetHandle(), i, value);
+}
+
+ScriptingStringPtr* GetScriptingArrayStringElementImpl(ScriptingArrayPtr a, int i)
+{
+ FatalErrorMsg("Not allowed");
+ return NULL;
+}
+
+ScriptingObjectPtr* GetScriptingArrayObjectElementImpl(ScriptingArrayPtr a, int i)
+{
+ FatalErrorMsg("Not allowed");
+ return NULL;
+}
+
+ScriptingStringPtr* GetScriptingArrayStringStartImpl(ScriptingArrayPtr a)
+{
+ FatalErrorMsg("Not allowed");
+ return NULL;
+}
+
+ScriptingObjectPtr* GetScriptingArrayObjectStartImpl(ScriptingArrayPtr a)
+{
+ FatalErrorMsg("Not allowed");
+ return NULL;
+}
+
+ScriptingStringPtr GetScriptingArrayStringElementNoRefImpl(ScriptingArrayPtr a, int i)
+{
+ return GetWinRTArrayTools()->GetArrayStringValue(a.GetHandle(), i);
+}
+
+ScriptingObjectPtr GetScriptingArrayObjectElementNoRefImpl(ScriptingArrayPtr a, int i)
+{
+ return WinRTScriptingObjectWrapper(GetWinRTArrayTools()->GetArrayValue(a.GetHandle(), i));
+}
+
+} // namespace Scripting \ No newline at end of file
diff --git a/Runtime/Scripting/TextAsset.cpp b/Runtime/Scripting/TextAsset.cpp
new file mode 100644
index 0000000..f21b0e6
--- /dev/null
+++ b/Runtime/Scripting/TextAsset.cpp
@@ -0,0 +1,70 @@
+#include "UnityPrefix.h"
+#include "TextAsset.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#if ENABLE_MONO
+#include "Runtime/Mono/MonoIncludes.h"
+#endif
+
+
+TextAsset::TextAsset(MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode)
+{
+}
+
+TextAsset::~TextAsset ()
+{
+}
+
+template<class TransferFunction>
+void TextAsset::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer ( transfer);
+ transfer.Transfer (m_Script, "m_Script", kHideInEditorMask);
+ transfer.Transfer (m_PathName, "m_PathName", kHideInEditorMask);
+}
+
+void TextAsset::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+}
+
+void TextAsset::SetScriptDontDirty (const ScriptString& script) {
+ m_Script = script;
+}
+
+bool TextAsset::SetScript (const ScriptString& script) {
+ return SetScript(script,false);
+}
+
+bool TextAsset::SetScript (const ScriptString& script, bool actuallyContainsBinaryData)
+{
+ SET_ALLOC_OWNER(this);
+ m_Script = script;
+
+ #if ENABLE_MONO
+ // Fallback. If mono doesn't accept the string, strip everything non-ASCII
+ if(mono_string_new_wrapper(script.c_str()) == NULL && !actuallyContainsBinaryData)
+ {
+ m_Script.clear();
+
+ for(int i=0; i<script.size(); i++)
+ {
+ if((unsigned char)script[i] < 0x7f)
+ m_Script += script[i];
+ }
+ }
+ #endif
+
+ SetDirty ();
+ return true;
+}
+
+const UnityStr& TextAsset::GetScriptClassName() const {
+ static UnityStr sEmpty;
+ return sEmpty;
+}
+
+
+IMPLEMENT_CLASS (TextAsset)
+IMPLEMENT_OBJECT_SERIALIZE (TextAsset)
+INSTANTIATE_TEMPLATE_TRANSFER (TextAsset)
diff --git a/Runtime/Scripting/TextAsset.h b/Runtime/Scripting/TextAsset.h
new file mode 100644
index 0000000..5c42fb2
--- /dev/null
+++ b/Runtime/Scripting/TextAsset.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Configuration/UnityConfigure.h"
+
+#include "Runtime/Misc/Allocator.h"
+
+
+class TextAsset : public NamedObject {
+public:
+
+ typedef UnityStr ScriptString;
+
+ REGISTER_DERIVED_CLASS (TextAsset, NamedObject)
+ DECLARE_OBJECT_SERIALIZE (TextAsset)
+
+ TextAsset(MemLabelId label, ObjectCreationMode mode);
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ // Set the script string.
+ // Subclasses override this in order to implement compiling, etc...
+ // @return whether the compilation succeeded
+ virtual bool SetScript (const ScriptString& script, bool actuallyContainsBinaryData);
+ virtual bool SetScript (const ScriptString& script);
+
+ // Get the script string.
+ const ScriptString &GetScript () const { return m_Script; }
+
+ void SetPathName (const std::string& path) { m_PathName = path; SetDirty (); }
+ const UnityStr& GetPathName () { return m_PathName; }
+
+ virtual const UnityStr& GetScriptClassName() const;
+ void SetScriptDontDirty (const ScriptString& script);
+
+protected:
+
+ UnityStr m_PathName;
+ ScriptString m_Script;
+};
diff --git a/Runtime/Scripting/WinRTHelper.h b/Runtime/Scripting/WinRTHelper.h
new file mode 100644
index 0000000..2fef260
--- /dev/null
+++ b/Runtime/Scripting/WinRTHelper.h
@@ -0,0 +1,29 @@
+#ifndef WINRTHELPER_H
+#define WINRTHELPER_H
+
+#if UNITY_WINRT
+
+# if UNITY_METRO_VS2012
+# if defined(__arm__)
+# include "build/temp/MetroSupport/metroARM/AutoGeneratedFuncDefs.h"
+# else
+# include "build/temp/MetroSupport/metro32/AutoGeneratedFuncDefs.h"
+# endif
+# elif UNITY_METRO_VS2013
+# if defined(__arm__)
+# include "build/temp/MetroSupport/metroBlueARM/AutoGeneratedFuncDefs.h"
+# else
+# include "build/temp/MetroSupport/metroBlue32/AutoGeneratedFuncDefs.h"
+# endif
+# elif UNITY_WP8
+# include "build/temp/WP8Support/AutoGeneratedFuncDefs.h"
+# endif
+#endif
+
+
+inline long long* GetWinRTFuncDefsPointers()
+{
+ return (long long*)UnityEngineDelegates::FunctionDefsDictionary::GetPointers();
+}
+
+#endif
diff --git a/Runtime/Scripting/WinRTMarshalers.cpp b/Runtime/Scripting/WinRTMarshalers.cpp
new file mode 100644
index 0000000..80d4d48
--- /dev/null
+++ b/Runtime/Scripting/WinRTMarshalers.cpp
@@ -0,0 +1,306 @@
+#include "UnityPrefix.h"
+
+#if UNITY_WINRT && ENABLE_SCRIPTING
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+
+#include "Runtime/Misc/AssetBundleUtility.h"
+#include "Runtime/Camera/LODGroup.h"
+#include "Runtime/NavMesh/NavMeshTypes.h"
+#include "Runtime/Terrain/SplatDatabase.h"
+#include "Runtime/Terrain/DetailDatabase.h"
+#include "Runtime/Terrain/TreeDatabase.h"
+#include "Runtime/IMGUI/GUIContent.h"
+#include "Runtime/Mono/Coroutine.h"
+#include "Runtime/Export/WWW.h"
+#include "Runtime/Graphics/LightmapSettings.h"
+#include "Runtime/Animation/AnimationEvent.h"
+
+#include "Runtime/Dynamics/CharacterController.h"
+#include "Runtime/Misc/AsyncOperation.h"
+#include "Runtime/Dynamics/Collider.h"
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Runtime/Physics2D/CollisionListener2D.h"
+#include "Runtime/Video/VideoTexture.h"
+#include "Runtime/Threads/ThreadSpecificValue.h"
+#include "Runtime/Filters/Misc/Font.h"
+
+UNITY_TLS_VALUE(void*) s_CurrentTarget;
+
+void _MarshalSetMonoLOD(float screenRelativeTransitionHeight, /*WinRTScriptingObjectWrapper*/long long renderers)
+{
+ MonoLOD* monoLOD = (MonoLOD*)s_CurrentTarget;
+ monoLOD->screenRelativeTransitionHeight = screenRelativeTransitionHeight;
+ monoLOD->renderers = renderers;
+}
+void _MarshalSetNavMeshPath(int ptr, /*WinRTScriptingObjectWrapper*/long long corners)
+{
+ MonoNavMeshPath* monoNavMeshPath = (MonoNavMeshPath*)s_CurrentTarget;
+ monoNavMeshPath->native = (NavMeshPath*)ptr;
+ monoNavMeshPath->corners = corners;
+}
+void _MarshalSetMonoGUIContent(Platform::String^ text, Platform::String^ tooltip, /*WinRTScriptingObjectWrapper*/long long image)
+{
+ MonoGUIContent* monoGUIContent = (MonoGUIContent*)s_CurrentTarget;
+ monoGUIContent->m_Text = text;
+ monoGUIContent->m_Tooltip = tooltip;
+ monoGUIContent->m_Image = image;
+}
+void _MarshalSetLightmapDataMono(/*WinRTScriptingObjectWrapper*/long long lightmap, /*WinRTScriptingObjectWrapper*/long long indirectLightmap)
+{
+ LightmapDataMono* monoLightmap = (LightmapDataMono*)s_CurrentTarget;
+ monoLightmap->m_Lightmap = lightmap;
+ monoLightmap->m_IndirectLightmap = indirectLightmap;
+}
+void _MarshalSetMonoTreePrototype(/*WinRTScriptingObjectWrapper*/long long prefab, float bendFactor)
+{
+ MonoTreePrototype* monoTreePrototype = (MonoTreePrototype*)s_CurrentTarget;
+ monoTreePrototype->prefab = prefab;
+ monoTreePrototype->bendFactor = bendFactor;
+}
+
+
+void _MarshalSetMonoDetailPrototype1(/*WinRTScriptingObjectWrapper*/long long prototype, /*WinRTScriptingObjectWrapper*/long long prototypeTexture,
+ float healthyColorR, float healthyColorG, float healthyColorB, float healthyColorA,
+ float dryColorR, float dryColorG, float dryColorB, float dryColorA)
+{
+ MonoDetailPrototype* monoDetailPrototype = (MonoDetailPrototype*)s_CurrentTarget;
+ monoDetailPrototype->prototype = prototype;
+ monoDetailPrototype->prototypeTexture = prototypeTexture;
+
+ monoDetailPrototype->healthyColor = ColorRGBAf(healthyColorR, healthyColorG, healthyColorB, healthyColorA);
+ monoDetailPrototype->dryColor = ColorRGBAf(dryColorR, dryColorG, dryColorB, dryColorA);
+}
+void _MarshalSetMonoDetailPrototype2(float minWidth, float maxWidth,
+ float minHeight, float maxHeight,
+ float noiseSpread,
+ float bendFactor,
+ int renderMode,
+ int usePrototypeMesh)
+{
+ MonoDetailPrototype* monoDetailPrototype = (MonoDetailPrototype*)s_CurrentTarget;
+
+ monoDetailPrototype->minWidth = minWidth;
+ monoDetailPrototype->maxWidth = maxWidth;
+ monoDetailPrototype->minHeight = minHeight;
+ monoDetailPrototype->maxHeight = maxHeight;
+ monoDetailPrototype->noiseSpread = noiseSpread;
+ monoDetailPrototype->bendFactor = bendFactor;
+ monoDetailPrototype->renderMode = renderMode;
+ monoDetailPrototype->usePrototypeMesh = usePrototypeMesh;
+}
+void _MarshalSetMonoSplatPrototype(/*WinRTScriptingObjectWrapper*/long long texture, /*WinRTScriptingObjectWrapper*/long long normalMap,
+ float tileSizeX, float tileSizeY,
+ float tileOffsetX, float tileOffsetY)
+{
+ MonoSplatPrototype* monoSplatPrototype = (MonoSplatPrototype*)s_CurrentTarget;
+ monoSplatPrototype->texture = texture;
+ monoSplatPrototype->normalMap = normalMap;
+ monoSplatPrototype->tileSize = Vector2f(tileSizeX, tileSizeY);
+ monoSplatPrototype->tileOffset = Vector2f(tileOffsetX, tileOffsetX);
+}
+void _MarshalSetScriptingCharacterInfo(int index, BridgeInterface::RectWrapper uv, BridgeInterface::RectWrapper vert, float width, int size, int style, bool flipped)
+{
+ ScriptingCharacterInfo* o = (ScriptingCharacterInfo*)s_CurrentTarget;
+ o->index = index;
+ o->uv = Rectf(uv.m_XMin, uv.m_YMin, uv.m_Width, uv.m_Height);
+ o->vert = Rectf(vert.m_XMin, vert.m_YMin, vert.m_Width, vert.m_Height);
+ o->width = width;
+ o->size = size;
+ o->style = style;
+ o->flipped = flipped;
+}
+
+void SetupMarshalCallbacks()
+{
+ GetWinRTMarshalling()->SetupMarshalCallbacks(
+ ref new BridgeInterface::MarshalSetMonoLOD(_MarshalSetMonoLOD),
+ ref new BridgeInterface::MarshalSetNavMeshPath(_MarshalSetNavMeshPath),
+ ref new BridgeInterface::MarshalSetMonoGUIContent(_MarshalSetMonoGUIContent),
+ ref new BridgeInterface::MarshalSetLightmapDataMono(_MarshalSetLightmapDataMono),
+ ref new BridgeInterface::MarshalSetMonoTreePrototype(_MarshalSetMonoTreePrototype),
+ ref new BridgeInterface::MarshalSetMonoDetailPrototype1(_MarshalSetMonoDetailPrototype1),
+ ref new BridgeInterface::MarshalSetMonoDetailPrototype2(_MarshalSetMonoDetailPrototype2),
+ ref new BridgeInterface::MarshalSetMonoSplatPrototype(_MarshalSetMonoSplatPrototype),
+ ref new BridgeInterface::MarshalSetScriptingCharacterInfo(_MarshalSetScriptingCharacterInfo));
+}
+//
+// Marshalling: Managed - to - Native
+//
+
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, AssetBundleCreateRequest** dest)
+{
+ *dest = (AssetBundleCreateRequest*)GetWinRTMarshalling()->MarshalGetAssetBundleCreateRequestPtr(so.GetHandle());
+}
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, MonoLOD* dest)
+{
+ s_CurrentTarget = dest;
+ GetWinRTMarshalling()->MarshallManagedMonoLOD(so.GetHandle());
+}
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, MonoNavMeshPath* dest)
+{
+ s_CurrentTarget = dest;
+ GetWinRTMarshalling()->MarshallManagedMonoNavMeshPath(so.GetHandle());
+}
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, MonoGUIContent* dest)
+{
+ s_CurrentTarget = dest;
+ GetWinRTMarshalling()->MarshallManagedMonoGUIContent(so.GetHandle());
+}
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, float* dest)
+{
+ *dest = GetWinRTMarshalling()->MarshallGetManagedWaitForSecondsWait(so.GetHandle());
+}
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, Coroutine** dest)
+{
+ *dest = (Coroutine*)GetWinRTMarshalling()->MarshallGetManagedCoroutinePtr(so.GetHandle());
+}
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, WWW** dest)
+{
+ *dest = (WWW*)GetWinRTMarshalling()->MarshallGetManagedWWWPtr(so.GetHandle());
+}
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, LightmapDataMono* dest)
+{
+ s_CurrentTarget = dest;
+ GetWinRTMarshalling()->MarshallManagedLightmapDataMono(so.GetHandle());
+}
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, MonoTreePrototype* dest)
+{
+ s_CurrentTarget = dest;
+ GetWinRTMarshalling()->MarshallManagedMonoTreePrototype(so.GetHandle());
+}
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, MonoDetailPrototype* dest)
+{
+ s_CurrentTarget = dest;
+ GetWinRTMarshalling()->MarshallManagedMonoDetailPrototype(so.GetHandle());
+}
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, MonoSplatPrototype* dest)
+{
+ s_CurrentTarget = dest;
+ GetWinRTMarshalling()->MarshallManagedMonoSplatPrototype(so.GetHandle());
+}
+
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, ScriptingCharacterInfo* dest)
+{
+ s_CurrentTarget = dest;
+ GetWinRTMarshalling()->MarshallManagedScriptingCharacterInfo(so.GetHandle());
+}
+
+//
+// Marshalling: Native - to - Managed
+//
+void MarshallNativeStructIntoManaged(const AnimationEvent* src, ScriptingObjectPtr& dest)
+{
+ GetWinRTMarshalling()->MarshallNativeAnimationEventPtr((int)src, dest.GetHandle());
+}
+void MarshallNativeStructIntoManaged(const AssetBundleRequestMono& src, ScriptingObjectPtr& dest)
+{
+ GetWinRTMarshalling()->MarshallNativeAssetBundleRequestTo((int)src.m_Result, src.m_AssetBundle, src.m_Path, src.m_Type, dest.GetHandle());
+}
+void MarshallNativeStructIntoManaged(const MonoNavMeshPath& src, ScriptingObjectPtr& dest)
+{
+ GetWinRTMarshalling()->MarshallNativeNavMashPathTo((int)src.native, src.corners, dest.GetHandle());
+}
+void MarshallNativeStructIntoManaged(const void* src, ScriptingObjectPtr& dest)
+{
+ GetWinRTMarshalling()->MarshallNativeVoidPtrTo((int)src, dest.GetHandle());
+}
+
+void MarshallNativeStructIntoManaged(const ControllerColliderHit& src, ScriptingObjectPtr& dest)
+{
+ GetWinRTMarshalling()->MarshallNativeControllerColliderHitTo(src.controller, src.collider,
+ src.point.x, src.point.y, src.point.z,
+ src.normal.x, src.normal.y, src.normal.z,
+ src.motionDirection.x, src.motionDirection.y, src.motionDirection.z,
+ src.motionLength,
+ src.push,
+ dest);
+}
+void MarshallNativeStructIntoManaged(const AsyncOperation* src, ScriptingObjectPtr& dest)
+{
+ GetWinRTMarshalling()->MarshallNativeAsyncOperationPtrTo((int)src, dest.GetHandle());
+}
+void MarshallNativeStructIntoManaged(const MonoContactPoint& src, ScriptingObjectPtr& dest)
+{
+ dest = GetWinRTMarshalling()->MarshallNativeMonoContactPointTo(src.point.x, src.point.y, src.point.z,
+ src.normal.x, src.normal.y, src.normal.z,
+ src.thisCollider,
+ src.otherCollider);
+}
+void MarshallNativeStructIntoManaged(const MonoTreePrototype& src, ScriptingObjectPtr& dest)
+{
+ GetWinRTMarshalling()->MarshallNativeMonoTreePrototypeTo(src.prefab, src.bendFactor, dest.GetHandle());
+}
+
+#if ENABLE_WEBCAM
+void MarshallNativeStructIntoManaged(const MonoWebCamDevice& src, ScriptingObjectPtr& dest)
+{
+ dest = GetWinRTMarshalling()->MarshallNativeMonoWebCamDeviceTo(src.name, src.flags);
+}
+#endif
+
+void MarshallNativeStructIntoManaged(const MonoCollision& src, ScriptingObjectPtr& dest)
+{
+ GetWinRTMarshalling()->MarshallNativeMonoCollisionTo(src.relativeVelocity.x, src.relativeVelocity.y, src.relativeVelocity.z,
+ src.rigidbody, src.collider, src.contacts.GetHandle(), dest.GetHandle());
+}
+void MarshallNativeStructIntoManaged(const LightmapDataMono& src, ScriptingObjectPtr& dest)
+{
+ GetWinRTMarshalling()->MarshallNativeLightmapDataMonoTo(src.m_Lightmap, src.m_IndirectLightmap, dest.GetHandle());
+}
+
+void MarshallNativeStructIntoManaged(const MonoDetailPrototype& src, ScriptingObjectPtr& dest)
+{
+ GetWinRTMarshalling()->MarshallNativeMonoDetailPrototype1To(
+ src.prototype, src.prototypeTexture,
+ src.healthyColor.r, src.healthyColor.g, src.healthyColor.b, src.healthyColor.a,
+ src.dryColor.r, src.dryColor.g, src.dryColor.b, src.dryColor.a,
+ dest);
+ GetWinRTMarshalling()->MarshallNativeMonoDetailPrototype2To(
+ src.minWidth, src.maxWidth,
+ src.minHeight, src.maxHeight,
+ src.noiseSpread, src.bendFactor,
+ src.renderMode, src.usePrototypeMesh,
+ dest);
+}
+void MarshallNativeStructIntoManaged(const MonoSplatPrototype& src, ScriptingObjectPtr& dest)
+{
+ GetWinRTMarshalling()->MarshallNativeMonoSplatPrototypeTo(src.texture, src.normalMap,
+ src.tileSize.x, src.tileSize.y,
+ src.tileOffset.x, src.tileOffset.y,
+ dest);
+}
+#if ENABLE_2D_PHYSICS
+void MarshallNativeStructIntoManaged(const ScriptingCollision2D& src, ScriptingObjectPtr& dest)
+{
+ GetWinRTMarshalling()->MarshallNativeCollision2DTo(src.rigidbody.GetHandle(), src.collider.GetHandle(), src.contacts.GetHandle(), dest.GetHandle());
+}
+
+void MarshallNativeStructIntoManaged(const ScriptingContactPoint2D& src, ScriptingObjectPtr& dest)
+{
+ dest = GetWinRTMarshalling()->MarshallNativeContactPoint2DTo(
+ src.point.x, src.point.y,
+ src.normal.x, src.normal.y,
+ src.collider,
+ src.otherCollider);
+}
+#endif
+void MarshallNativeStructIntoManaged(const ScriptingCharacterInfo& src, ScriptingObjectPtr& dest)
+{
+ BridgeInterface::RectWrapper uv;
+ BridgeInterface::RectWrapper vert;
+ uv.m_XMin = src.uv.x;
+ uv.m_YMin = src.uv.y;
+ uv.m_Width = src.uv.width;
+ uv.m_Height = src.uv.height;
+
+ vert.m_XMin = src.vert.x;
+ vert.m_YMin = src.vert.y;
+ vert.m_Width = src.vert.width;
+ vert.m_Height = src.vert.height;
+ dest = GetWinRTMarshalling()->MarshallNativeScriptingCharacterInfoTo(src.index,
+ uv, vert,
+ src.width, src.size, src.style, src.flipped);
+}
+#endif
diff --git a/Runtime/Scripting/WinRTMarshalers.h b/Runtime/Scripting/WinRTMarshalers.h
new file mode 100644
index 0000000..c125a22
--- /dev/null
+++ b/Runtime/Scripting/WinRTMarshalers.h
@@ -0,0 +1,66 @@
+#ifndef WINRTMARSHALERS_H
+#define WINRTMARSHALERS_H
+
+#if UNITY_WINRT && ENABLE_SCRIPTING
+
+class AssetBundleCreateRequest;
+struct AssetBundleRequestMono;
+struct MonoLOD;
+struct MonoNavMeshPath;
+struct MonoGUIContent;
+struct Coroutine;
+class WWW;
+struct LightmapDataMono;
+struct MonoTreePrototype;
+struct AnimationEvent;
+struct ControllerColliderHit;
+class AsyncOperation;
+struct MonoContactPoint;
+struct MonoWebCamDevice;
+struct MonoCollision;
+struct MonoDetailPrototype;
+struct MonoSplatPrototype;
+struct ScriptingCollision2D;
+struct ScriptingContactPoint2D;
+struct ScriptingCharacterInfo;
+
+void SetupMarshalCallbacks();
+
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, AssetBundleCreateRequest** dest);
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, MonoLOD* dest);
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, MonoNavMeshPath* dest);
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, MonoGUIContent* dest);
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, float* dest);
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, Coroutine** dest);
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, WWW** dest);
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, LightmapDataMono* dest);
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, MonoTreePrototype* dest);
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, MonoDetailPrototype* dest);
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, MonoSplatPrototype* dest);
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, ScriptingCharacterInfo* dest);
+
+template<class T> inline
+void MarshallManagedStructIntoNative(ScriptingObjectPtr so, T* dest)
+{
+ MarshallManagedStructIntoNative(so, dest);
+}
+
+void MarshallNativeStructIntoManaged(const AnimationEvent* src, ScriptingObjectPtr& dest);
+void MarshallNativeStructIntoManaged(const AssetBundleRequestMono& src, ScriptingObjectPtr& dest);
+void MarshallNativeStructIntoManaged(const MonoNavMeshPath& src, ScriptingObjectPtr& dest);
+void MarshallNativeStructIntoManaged(const void* src, ScriptingObjectPtr& dest);
+void MarshallNativeStructIntoManaged(const ControllerColliderHit& src, ScriptingObjectPtr& dest);
+void MarshallNativeStructIntoManaged(const AsyncOperation* src, ScriptingObjectPtr& dest);
+void MarshallNativeStructIntoManaged(const MonoContactPoint& src, ScriptingObjectPtr& dest);
+void MarshallNativeStructIntoManaged(const MonoTreePrototype& src, ScriptingObjectPtr& dest);
+void MarshallNativeStructIntoManaged(const MonoWebCamDevice& src, ScriptingObjectPtr& dest);
+void MarshallNativeStructIntoManaged(const MonoCollision& src, ScriptingObjectPtr& dest);
+void MarshallNativeStructIntoManaged(const LightmapDataMono& src, ScriptingObjectPtr& dest);
+void MarshallNativeStructIntoManaged(const MonoDetailPrototype& src, ScriptingObjectPtr& dest);
+void MarshallNativeStructIntoManaged(const MonoSplatPrototype& src, ScriptingObjectPtr& dest);
+void MarshallNativeStructIntoManaged(const ScriptingCollision2D& src, ScriptingObjectPtr& dest);
+void MarshallNativeStructIntoManaged(const ScriptingContactPoint2D& src, ScriptingObjectPtr& dest);
+void MarshallNativeStructIntoManaged(const ScriptingCharacterInfo& src, ScriptingObjectPtr& dest);
+#endif
+
+#endif
diff --git a/Runtime/Serialize/AwakeFromLoadQueue.cpp b/Runtime/Serialize/AwakeFromLoadQueue.cpp
new file mode 100644
index 0000000..fdb31de
--- /dev/null
+++ b/Runtime/Serialize/AwakeFromLoadQueue.cpp
@@ -0,0 +1,395 @@
+#include "UnityPrefix.h"
+#include "AwakeFromLoadQueue.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Misc/BuildSettings.h"
+#if UNITY_EDITOR
+#include "Editor/Src/Prefabs/PrefabBackwardsCompatibility.h"
+#endif
+
+PROFILER_INFORMATION(gAwakeFromLoadQueue, "Loading.AwakeFromLoad", kProfilerLoading)
+
+AwakeFromLoadQueue::AwakeFromLoadQueue (MemLabelRef label)
+{
+ for(int i=0;i<kMaxQueues;++i)
+ m_ItemArrays[i].set_memory_label(label);
+}
+
+void AwakeFromLoadQueue::Reserve (unsigned size)
+{
+ for (int i=0;i<kMaxQueues;i++)
+ {
+ if (i == kManagersQueue)
+ continue;
+
+ m_ItemArrays[i].reserve(size);
+ }
+}
+
+void AwakeFromLoadQueue::RegisterObjectInstanceIDs ()
+{
+ LockObjectCreation();
+ for (int i=0;i<kMaxQueues;i++)
+ RegisterObjectInstanceIDsInternal(m_ItemArrays[i].begin(), m_ItemArrays[i].size());
+ UnlockObjectCreation();
+}
+
+void AwakeFromLoadQueue::RegisterObjectInstanceIDsInternal (Item* objects, unsigned size)
+{
+ for (int i=0;i<size;i++)
+ {
+ Object* ptr = objects[i].registerObjectPtr;
+ Assert(ptr != NULL);
+ Object::RegisterInstanceIDNoLock(ptr);
+ }
+}
+
+int AwakeFromLoadQueue::DetermineQueueIndex(ClassIDType classID)
+{
+ if (classID == ClassID(MonoBehaviour))
+ return kMonoBehaviourQueue;
+ else if (classID == ClassID(TerrainData))
+ return kTerrainsQueue;
+ else if (classID == ClassID(Animator))
+ return kAnimatorQueue;
+ else if (classID == ClassID(Rigidbody)
+#if ENABLE_2D_PHYSICS
+ || classID == ClassID(Rigidbody2D)
+#endif
+ )
+ return kRigidbodyQueue;
+ else if (classID == ClassID(GameObject) || Object::IsDerivedFromClassID(classID, ClassID(Component)))
+ return kGameObjectAndComponentQueue;
+ else if (Object::IsDerivedFromClassID(classID, ClassID(GameManager)))
+ return kManagersQueue;
+ else
+ return kAssetQueue;
+
+}
+
+bool AwakeFromLoadQueue::IsInQueue(Object& target)
+{
+ int queueIndex = DetermineQueueIndex(target.GetClassID());
+
+ for (int i = 0; i < m_ItemArrays[queueIndex].size(); i++)
+ {
+ if (m_ItemArrays[queueIndex][i].objectPPtr == PPtr<Object>(target.GetInstanceID()))
+ return true;
+ }
+
+ return false;
+}
+
+
+void AwakeFromLoadQueue::Add (Object& target, TypeTree* oldType, bool safeBinaryLoaded, AwakeFromLoadMode awakeOverride)
+{
+ Item item;
+ item.registerObjectPtr = &target;
+ item.objectPPtr = &target;
+ item.classID = target.GetClassID();
+ #if UNITY_EDITOR
+ item.oldType = oldType;
+ item.safeBinaryLoaded = safeBinaryLoaded;
+ item.awakeModeOverride = awakeOverride;
+ #else
+ Assert(awakeOverride == -1);
+ #endif
+
+ int queueIndex = DetermineQueueIndex(item.classID);
+
+ m_ItemArrays[queueIndex].push_back(item);
+}
+
+bool SortItemByInstanceID (const AwakeFromLoadQueue::Item& lhs, const AwakeFromLoadQueue::Item& rhs)
+{
+ return lhs.objectPPtr.GetInstanceID() < rhs.objectPPtr.GetInstanceID();
+}
+
+static int GetScriptExecutionOrder (int instanceID)
+{
+ #if ENABLE_SCRIPTING
+ MonoBehaviour* behaviour = dynamic_instanceID_cast<MonoBehaviour*> (instanceID);
+ if (behaviour != NULL)
+ {
+ MonoScript* script = behaviour->GetScript();
+ if (script)
+ return script->GetExecutionOrder();
+ }
+ #endif
+ return 0;
+}
+
+bool AwakeFromLoadQueue::SortBehaviourByExecutionOrderAndInstanceID (int lhs, int rhs)
+{
+ int lhsExecutionOrder = GetScriptExecutionOrder(lhs);
+ int rhsExecutionOrder = GetScriptExecutionOrder(rhs);
+
+ if (lhsExecutionOrder != rhsExecutionOrder)
+ return lhsExecutionOrder < rhsExecutionOrder;
+ else
+ return lhs < rhs;
+}
+
+bool SortBehaviourByExecutionOrderAndReverseInstanceID (int lhs, int rhs)
+{
+ int lhsExecutionOrder = GetScriptExecutionOrder(lhs);
+ int rhsExecutionOrder = GetScriptExecutionOrder(rhs);
+
+ if (lhsExecutionOrder != rhsExecutionOrder)
+ return lhsExecutionOrder < rhsExecutionOrder;
+ else
+ return lhs > rhs;
+}
+
+static bool SortBehaviourItemByExecutionOrderAndInstanceID (const AwakeFromLoadQueue::Item& lhs, const AwakeFromLoadQueue::Item& rhs)
+{
+ return AwakeFromLoadQueue::SortBehaviourByExecutionOrderAndInstanceID(lhs.objectPPtr.GetInstanceID(), rhs.objectPPtr.GetInstanceID());
+}
+
+static bool SortBehaviourItemByExecutionOrderAndReverseInstanceID (const AwakeFromLoadQueue::Item& lhs, const AwakeFromLoadQueue::Item& rhs)
+{
+ return SortBehaviourByExecutionOrderAndReverseInstanceID(lhs.objectPPtr.GetInstanceID(), rhs.objectPPtr.GetInstanceID());
+}
+
+///@TODO: Should check consistency always be called immediately before calling Awake???
+
+void AwakeFromLoadQueue::PersistentManagerAwakeFromLoad (AwakeFromLoadMode mode, SafeBinaryReadCallbackFunction* safeBinaryCallback)
+{
+ for (int i=0;i<kMaxQueues;i++)
+ PersistentManagerAwakeFromLoad(i, mode, safeBinaryCallback);
+}
+
+void AwakeFromLoadQueue::ClearQueue (int queueIndex)
+{
+ m_ItemArrays[queueIndex].clear();
+}
+
+
+void AwakeFromLoadQueue::Clear ()
+{
+ for (int i=0;i<kMaxQueues;i++)
+ ClearQueue (i);
+}
+
+
+void AwakeFromLoadQueue::PersistentManagerAwakeFromLoad (int queueIndex, AwakeFromLoadMode mode, SafeBinaryReadCallbackFunction* safeBinaryCallback)
+{
+ Item* array = m_ItemArrays[queueIndex].begin();
+ size_t size = m_ItemArrays[queueIndex].size();
+
+ std::sort(array, array + size, SortItemByInstanceID);
+
+ if (queueIndex == kMonoBehaviourQueue)
+ {
+ if (UNITY_EDITOR)
+ {
+ // In the editor the execution order can be changed by the user at any time,
+ // thus we need to sort on load
+ std::sort(array, array + size, SortBehaviourItemByExecutionOrderAndInstanceID);
+ }
+ else
+ {
+ // In the player we write the scene files sorted by execution order
+ for (int j=1;j<size;j++)
+ {
+ AssertIf(SortBehaviourItemByExecutionOrderAndInstanceID(array[j], array[j-1]));
+ }
+ }
+ }
+
+ InvokePersistentManagerAwake(array, size, mode, safeBinaryCallback);
+}
+
+void AwakeFromLoadQueue::AwakeFromLoad (AwakeFromLoadMode mode)
+{
+ for (int i=0;i<kMaxQueues;i++)
+ {
+ // In 4.0 we started sorting prefab instantiation by script execution order and instanceID
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ {
+ if (i == kMonoBehaviourQueue)
+ {
+ // For Instantiate we need sort by execution order
+ // The default order in Instantiate is determined by walking the hierarchy and components.
+ // Since instanceIDs are generated negative and decreasing we sort by reverse instanceID for components that do not.
+ std::sort(m_ItemArrays[i].begin(), m_ItemArrays[i].end(), SortBehaviourItemByExecutionOrderAndReverseInstanceID);
+ }
+ }
+
+ InvokeAwakeFromLoad(m_ItemArrays[i].begin(), m_ItemArrays[i].size(), mode);
+ }
+}
+
+void AwakeFromLoadQueue::CheckConsistency ()
+{
+ for (int i=0;i<kMaxQueues;i++)
+ InvokeCheckConsistency(m_ItemArrays[i].begin(), m_ItemArrays[i].size());
+}
+
+#if UNITY_EDITOR
+
+void AwakeFromLoadQueue::InsertAwakeFromLoadQueue (ItemArray& src, ItemArray& dst, AwakeFromLoadMode awakeOverride)
+{
+ std::sort(src.begin(), src.end(), SortItemByInstanceID);
+ std::sort(dst.begin(), dst.end(), SortItemByInstanceID);
+
+ // Inject any non-duplicate elements into the dst array
+ int oldDstSize = dst.size();
+ int d = 0;
+ for (int i=0;i<src.size();i++)
+ {
+ while (d < oldDstSize && dst[d].objectPPtr.GetInstanceID() < src[i].objectPPtr.GetInstanceID())
+ d++;
+
+ if (d >= oldDstSize || dst[d].objectPPtr.GetInstanceID() != src[i].objectPPtr.GetInstanceID())
+ {
+ dst.push_back(src[i]);
+ }
+ // else -> The object is in the destination array already.
+ }
+}
+
+void AwakeFromLoadQueue::InsertAwakeFromLoadQueue (AwakeFromLoadQueue& awakeFromLoadQueue, AwakeFromLoadMode awakeOverride)
+{
+ for (int i=0;i<kMaxQueues;i++)
+ InsertAwakeFromLoadQueue(awakeFromLoadQueue.m_ItemArrays[i], m_ItemArrays[i], awakeOverride);
+}
+
+void AwakeFromLoadQueue::PatchPrefabBackwardsCompatibility ()
+{
+ for (int q=0;q<kMaxQueues;q++)
+ {
+ int size = m_ItemArrays[q].size();
+ Item* objects = m_ItemArrays[q].begin();
+
+ for (int i=0;i<size;i++)
+ {
+ Object* ptr = objects[i].objectPPtr;
+ if (ptr)
+ {
+ TypeTree* typeTree = objects[i].oldType;
+ if (typeTree != NULL && objects[i].safeBinaryLoaded)
+ {
+ objects[i].safeBinaryLoaded = false;
+ RemapOldPrefabOverrideFromLoading (*ptr, *typeTree);
+ }
+
+ Prefab* prefab = dynamic_pptr_cast<Prefab*> (ptr);
+ if (prefab)
+ prefab->PatchPrefabBackwardsCompatibility();
+
+ EditorExtension* extension = dynamic_pptr_cast<EditorExtension*> (ptr);
+ if (extension)
+ extension->PatchPrefabBackwardsCompatibility();
+ }
+ }
+ }
+}
+#endif
+
+
+void AwakeFromLoadQueue::InvokePersistentManagerAwake (Item* objects, unsigned size, AwakeFromLoadMode awakeMode, SafeBinaryReadCallbackFunction* safeBinaryCallback)
+{
+ #if DEBUGMODE
+ int previousInstanceID = 0;
+ #endif
+
+ for (int i=0;i<size;i++)
+ {
+ PROFILER_AUTO(gAwakeFromLoadQueue, NULL)
+
+ // The AwakeFromLoadQueue should never have any duplicate elements.
+ #if DEBUGMODE
+ Assert(objects[i].objectPPtr.GetInstanceID() != previousInstanceID);
+ previousInstanceID = objects[i].objectPPtr.GetInstanceID();
+ #endif
+
+ Object* ptr = objects[i].objectPPtr;
+ if (ptr == NULL)
+ continue;
+
+ #if UNITY_EDITOR
+ // Loaded with SafeBinaryRead thus needs the callback
+ if (objects[i].safeBinaryLoaded && safeBinaryCallback != NULL)
+ safeBinaryCallback (*ptr, *objects[i].oldType);
+ ptr->CheckConsistency ();
+
+ #endif
+
+ AwakeFromLoadMode objectAwakeMode = awakeMode;
+ #if UNITY_EDITOR
+ if (objects[i].awakeModeOverride != kDefaultAwakeFromLoadInvalid)
+ objectAwakeMode = objects[i].awakeModeOverride;
+ #endif
+
+ ptr->AwakeFromLoad (objectAwakeMode);
+ ptr->ClearPersistentDirty ();
+ }
+}
+
+void AwakeFromLoadQueue::PersistentManagerAwakeSingleObject (Object& o, TypeTree* oldType, AwakeFromLoadMode awakeMode, bool safeBinaryLoaded, SafeBinaryReadCallbackFunction* safeBinaryCallback)
+{
+ PROFILER_AUTO(gAwakeFromLoadQueue, &o)
+
+ #if UNITY_EDITOR
+ // Loaded with SafeBinaryRead thus needs the callback
+ if (safeBinaryLoaded && safeBinaryCallback != NULL)
+ safeBinaryCallback (o, *oldType);
+ o.CheckConsistency ();
+ #endif
+
+ o.AwakeFromLoad (awakeMode);
+ o.ClearPersistentDirty ();
+}
+
+void AwakeFromLoadQueue::InvokeAwakeFromLoad (Item* objects, unsigned size, AwakeFromLoadMode mode)
+{
+ for (int i=0;i<size;i++)
+ {
+ Object* ptr = objects[i].objectPPtr;
+ if (ptr)
+ {
+ #if UNITY_EDITOR
+ if (objects[i].awakeModeOverride != kDefaultAwakeFromLoadInvalid)
+ {
+ ptr->AwakeFromLoad(objects[i].awakeModeOverride);
+ continue;
+ }
+
+ #endif
+
+
+ ptr->AwakeFromLoad(mode);
+ }
+ }
+}
+
+void AwakeFromLoadQueue::InvokeCheckConsistency (Item* objects, unsigned size)
+{
+ for (int i=0;i<size;i++)
+ {
+ Object* ptr = objects[i].objectPPtr;
+ if (ptr)
+ ptr->CheckConsistency();
+ }
+}
+
+void AwakeFromLoadQueue::ExtractAllObjects (dynamic_array<PPtr<Object> >& outObjects)
+{
+ Assert(outObjects.empty());
+
+ int count = 0;
+ for (int q=0;q<kMaxQueues;q++)
+ count += m_ItemArrays[q].size();
+ outObjects.reserve(count);
+
+ for (int q=0;q<kMaxQueues;q++)
+ {
+ int size = m_ItemArrays[q].size();
+ Item* objects = m_ItemArrays[q].begin();
+
+ for (int i=0;i<size;i++)
+ outObjects.push_back(objects[i].objectPPtr);
+ }
+}
diff --git a/Runtime/Serialize/AwakeFromLoadQueue.h b/Runtime/Serialize/AwakeFromLoadQueue.h
new file mode 100644
index 0000000..cc8fbe2
--- /dev/null
+++ b/Runtime/Serialize/AwakeFromLoadQueue.h
@@ -0,0 +1,83 @@
+#ifndef AWAKE_FROM_LOAD_QUEUE_H
+#define AWAKE_FROM_LOAD_QUEUE_H
+
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Utilities/dynamic_array.h"
+class TypeTree;
+
+
+// Rigidbodies must run before Colliders
+// Animators must come after all other components because they bind properties (eg. SkinnedMeshRenderer needs to be fully initialized to bind blendshapes)
+// Terrains depend on prefabs for trees and textures to be fully prepared.
+// MonoBehaviours always come last since they can call anything on any component or asset.
+
+enum { kManagersQueue = 0, kAssetQueue, kRigidbodyQueue, kGameObjectAndComponentQueue, kAnimatorQueue, kTerrainsQueue, kMonoBehaviourQueue, kMaxQueues };
+
+class AwakeFromLoadQueue
+{
+public:
+
+ struct Item
+ {
+ Object* registerObjectPtr;
+
+ PPtr<Object> objectPPtr;
+ ClassIDType classID;
+
+#if UNITY_EDITOR
+ TypeTree* oldType;
+ bool safeBinaryLoaded;
+ AwakeFromLoadMode awakeModeOverride;
+#endif
+ };
+
+ AwakeFromLoadQueue (MemLabelRef label);
+
+ typedef dynamic_array<Item> ItemArray;
+
+ typedef void SafeBinaryReadCallbackFunction (Object& object, const TypeTree& oldTypeTree);
+
+ bool IsInQueue(Object& target);
+
+ void Reserve (unsigned size);
+ void Add (Object& target, TypeTree* oldType = NULL, bool safeBinaryLoaded = false, AwakeFromLoadMode awakeOverride = kDefaultAwakeFromLoadInvalid);
+
+ static void PersistentManagerAwakeSingleObject (Object& o, TypeTree* oldType, AwakeFromLoadMode awakeMode, bool safeBinaryLoaded, SafeBinaryReadCallbackFunction* safeBinaryCallback);
+
+ void PersistentManagerAwakeFromLoad (AwakeFromLoadMode mode, SafeBinaryReadCallbackFunction* safeBinaryCallback);
+ void PersistentManagerAwakeFromLoad (int queueIndex, AwakeFromLoadMode mode, SafeBinaryReadCallbackFunction* safeBinaryCallback);
+
+ void ClearQueue (int queueIndex);
+ void Clear ();
+
+ void AwakeFromLoad (AwakeFromLoadMode mode);
+ void CheckConsistency ();
+
+ void RegisterObjectInstanceIDs ();
+
+ #if UNITY_EDITOR
+ void PatchPrefabBackwardsCompatibility ();
+ void InsertAwakeFromLoadQueue (AwakeFromLoadQueue& awakeFromLoadQueue, AwakeFromLoadMode awakeOverride);
+ #endif
+
+ void ExtractAllObjects (dynamic_array<PPtr<Object> >& outObjects);
+
+ ItemArray& GetItemArray (int queueIndex) { return m_ItemArrays[queueIndex]; }
+
+ static bool SortBehaviourByExecutionOrderAndInstanceID (int lhs, int rhs);
+
+private:
+ int DetermineQueueIndex(ClassIDType classID);
+
+ static void InvokeAwakeFromLoad (Item* objects, unsigned size, AwakeFromLoadMode mode);
+ static void InvokeCheckConsistency (Item* objects, unsigned size);
+ static void InvokePersistentManagerAwake (Item* objects, unsigned size, AwakeFromLoadMode awakeMode, SafeBinaryReadCallbackFunction* safeBinaryCallback);
+ static void RegisterObjectInstanceIDsInternal (Item* objects, unsigned size);
+
+ void InsertAwakeFromLoadQueue (dynamic_array<Item>& src, dynamic_array<Item>& dst, AwakeFromLoadMode awakeOverride);
+
+
+ ItemArray m_ItemArrays[kMaxQueues];
+};
+
+#endif \ No newline at end of file
diff --git a/Runtime/Serialize/Blobification/BlobSize.h b/Runtime/Serialize/Blobification/BlobSize.h
new file mode 100644
index 0000000..a7aaf6d
--- /dev/null
+++ b/Runtime/Serialize/Blobification/BlobSize.h
@@ -0,0 +1,161 @@
+#ifndef BLOBSIZE_H
+#define BLOBSIZE_H
+
+#include "Runtime/Serialize/TransferFunctions/TransferBase.h"
+
+struct ReduceCopyData;
+
+class BlobSize : public TransferBase
+{
+private:
+
+ typedef OffsetPtr<void*>::offset_type offset_type;
+
+ size_t m_Size;
+ bool m_IgnorePtr;
+ bool m_HasDebugOffsetPtr;
+ bool m_Use64Ptr;
+ int m_TargetPlatform;
+
+ std::size_t AlignAddress(std::size_t addr, std::size_t align)
+ {
+ return addr + ((~addr + 1U) & (align - 1U));
+ }
+
+public:
+
+
+ BlobSize (bool hasDebugOffsetPtr, bool use64BitOffsetPtr)
+ : m_Size (0)
+ , m_IgnorePtr (false)
+ , m_HasDebugOffsetPtr (hasDebugOffsetPtr)
+ , m_Use64Ptr (use64BitOffsetPtr) { }
+
+ bool NeedsInstanceIDRemapping () { return m_Flags & kNeedsInstanceIDRemapping; }
+ int GetFlags () { return m_Flags; }
+ bool IsWritingGameReleaseData ()
+ {
+ return true;
+ }
+ bool IsSerializingForGameRelease ()
+ {
+ return true;
+ }
+ bool IsBuildingTargetPlatform (BuildTargetPlatform platform)
+ {
+ if (platform == kBuildAnyPlayerData)
+ return m_TargetPlatform >= kBuildValidPlayer;
+ else
+ return m_TargetPlatform == platform;
+ }
+
+ BuildTargetPlatform GetBuildingTargetPlatform () { return static_cast<BuildTargetPlatform>(m_TargetPlatform); }
+
+ template<class T>
+ void Transfer (T& data, const char* name, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ template<class T>
+ void TransferWithTypeString (T& data, const char* name, const char* typeName, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ template<class T>
+ void TransferBasicData (T& data);
+
+ template<class T>
+ void TransferPtr (bool isValidPtr, ReduceCopyData* reduceCopyData);
+
+ template<class T>
+ void TransferSTLStyleArray (T& data, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ template<class T>
+ static size_t CalculateSize (T& data, bool hasDebugOffsetPtr, bool use64BitOffsetPtr)
+ {
+ BlobSize sizer (hasDebugOffsetPtr, use64BitOffsetPtr);
+ sizer.Transfer(data, "Base");
+
+ // size_t size = sizeof(T);
+ // Assert( sizeof(T) == sizer.m_Size);
+
+ return sizer.m_Size;
+ }
+
+ template<class> friend class BlobSizeTransferSTLStyleArrayImpl;
+};
+
+
+template<typename T> class BlobSizeTransferSTLStyleArrayImpl
+{
+public:
+ void operator()(T& data, TransferMetaFlags metaFlags, BlobSize& transfer)
+ {
+ AssertString ("STL array are not support for BlobWrite");
+ }
+};
+
+template<typename T> class BlobSizeTransferSTLStyleArrayImpl< OffsetPtrArrayTransfer<T> >
+{
+public:
+ void operator()(OffsetPtrArrayTransfer<T>& data, TransferMetaFlags metaFlags, BlobSize& transfer)
+ {
+ transfer.m_IgnorePtr = false;
+ }
+};
+
+template<typename T, int SIZE> class BlobSizeTransferSTLStyleArrayImpl< StaticArrayTransfer<T, SIZE> >
+{
+public:
+ void operator()(StaticArrayTransfer<T, SIZE>& data, TransferMetaFlags metaFlags, BlobSize& transfer)
+ {
+ transfer.m_Size = transfer.AlignAddress(transfer.m_Size, ALIGN_OF(T));
+ transfer.m_Size += sizeof(T)*data.size();
+ }
+};
+
+
+template<class T> inline
+void BlobSize::TransferSTLStyleArray (T& data, TransferMetaFlags metaFlags)
+{
+ BlobSizeTransferSTLStyleArrayImpl<T> transfer;
+ transfer(data, metaFlags, *this);
+}
+
+template<class T> inline
+void BlobSize::Transfer (T& data, const char* name, TransferMetaFlags)
+{
+ if (m_IgnorePtr)
+ {
+ m_IgnorePtr = false;
+ return;
+ }
+
+ m_Size = AlignAddress(m_Size, SerializeTraits<T>::GetAlignOf() );
+
+ SerializeTraits<T>::Transfer (data, *this);
+
+ m_Size = AlignAddress(m_Size, SerializeTraits<T>::GetAlignOf() );
+}
+
+template<class T> inline
+void BlobSize::TransferWithTypeString (T& data, const char*, const char*, TransferMetaFlags)
+{
+ SerializeTraits<T>::Transfer (data, *this);
+}
+
+template<class T> inline
+void BlobSize::TransferBasicData (T& srcData)
+{
+ m_Size += sizeof(T);
+}
+
+template<class T> inline
+void BlobSize::TransferPtr (bool isValidPtr, ReduceCopyData* reduceCopyData)
+{
+ m_Size += m_Use64Ptr ? sizeof(SInt64) : sizeof(SInt32);
+
+ if (m_HasDebugOffsetPtr)
+ m_Size += sizeof(void*);
+
+ if (isValidPtr)
+ m_IgnorePtr = true;
+}
+
+#endif
diff --git a/Runtime/Serialize/Blobification/BlobTests.cpp b/Runtime/Serialize/Blobification/BlobTests.cpp
new file mode 100644
index 0000000..8b0a2d3
--- /dev/null
+++ b/Runtime/Serialize/Blobification/BlobTests.cpp
@@ -0,0 +1,358 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS && !(UNITY_LINUX && UNITY_64)
+#include "External/UnitTest++/src/UnitTest++.h"
+#include <limits>
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Simd/math.h"
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/Allocator/MemoryManager.h"
+#include "Runtime/Serialize/SerializeTraits.h"
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+#include "Runtime/Serialize/Blobification/BlobWrite.h"
+
+template<typename TYPE> TYPE* Construct()
+{
+ #if ENABLE_MEMORY_MANAGER
+ void* _where = GetMemoryManager().Allocate (sizeof(TYPE), ALIGN_OF(TYPE), MemLabelId(kMemNewDeleteId, NULL), kAllocateOptionNone, "Construct");
+ #else
+ void* _where = malloc(sizeof(TYPE));
+ #endif
+ return new(_where) TYPE;
+}
+
+template<typename TYPE> TYPE* ConstructArray(size_t num)
+{
+ #if ENABLE_MEMORY_MANAGER
+ void* _where = GetMemoryManager().Allocate (sizeof(TYPE)*num, ALIGN_OF(TYPE), MemLabelId(kMemNewDeleteId, NULL), kAllocateOptionNone, "ConstructArray");
+ #else
+ void* _where = malloc(sizeof(TYPE)*num);
+ #endif
+ return new(_where) TYPE[num];
+}
+
+template<typename TYPE> void Destruct(TYPE* p)
+{
+ #if ENABLE_MEMORY_MANAGER
+ GetMemoryManager().Deallocate (p, MemLabelId(kMemNewDeleteId, NULL));
+ #else
+ free(p);
+ #endif
+}
+
+
+struct SampleDataA
+{
+ enum {
+ kLastIndex = 20
+ };
+
+ int intValue1;
+ math::float4 float4Value; // (intentially unaligned)
+ Vector3f vector3;
+
+ mecanim::uint32_t index[kLastIndex];
+
+ OffsetPtr<float> nullPtr;
+
+ OffsetPtr<float> floatPtr;
+
+ mecanim::uint32_t arraySize;
+ OffsetPtr<float> array;
+
+ mecanim::uint32_t emptyArraySize;
+ OffsetPtr<math::float4> emptyArray;
+
+ int intValue2;
+
+ DECLARE_SERIALIZE(SampleData)
+};
+
+struct SampleData
+{
+ int intValue1; // 0
+ math::float4 float4Value; // 16 (intentially unaligned)
+ Vector3f vector3; // 32
+
+ OffsetPtr<float> nullPtr; // 44
+
+ OffsetPtr<float> floatPtr; // 52
+
+ mecanim::uint32_t arraySize; // 60
+ OffsetPtr<double> array; // 64
+
+ mecanim::uint32_t emptyArraySize; // 72
+ OffsetPtr<math::float4> emptyArray; // 76
+
+ mecanim::uint32_t sampleDataASize; // 84
+ OffsetPtr<SampleDataA> sampleDataA; // 88
+
+ mecanim::uint32_t sampleDataAHandleSize; // 96
+ OffsetPtr<OffsetPtr<SampleDataA> > sampleDataAHandle; // 100
+
+ int intValue2; // 108
+
+ DECLARE_SERIALIZE(SampleData)
+};
+
+
+
+template<class TransferFunction> inline
+void SampleDataA::Transfer(TransferFunction& transfer)
+{
+ TRANSFER(intValue1);
+ TRANSFER(float4Value);
+ TRANSFER(vector3);
+
+ STATIC_ARRAY_TRANSFER(mecanim::uint32_t, index, kLastIndex);
+
+ TRANSFER(nullPtr);
+ TRANSFER(floatPtr);
+
+ TRANSFER_BLOB_ONLY(arraySize);
+ MANUAL_ARRAY_TRANSFER2(float, array, arraySize);
+
+
+ TRANSFER_BLOB_ONLY(emptyArraySize);
+ MANUAL_ARRAY_TRANSFER2(math::float4, emptyArray, emptyArraySize);
+
+ TRANSFER(intValue2);
+}
+
+
+template<class TransferFunction> inline
+void SampleData::Transfer(TransferFunction& transfer)
+{
+ TRANSFER(intValue1);
+ TRANSFER(float4Value);
+ TRANSFER(vector3);
+
+ TRANSFER(nullPtr);
+ TRANSFER(floatPtr);
+
+ TRANSFER_BLOB_ONLY(arraySize);
+ MANUAL_ARRAY_TRANSFER2(double, array, arraySize);
+
+
+ TRANSFER_BLOB_ONLY(emptyArraySize);
+ MANUAL_ARRAY_TRANSFER2(math::float4, emptyArray, emptyArraySize);
+
+ TRANSFER_BLOB_ONLY(sampleDataASize);
+ MANUAL_ARRAY_TRANSFER2(SampleDataA, sampleDataA, sampleDataASize);
+
+ TRANSFER_BLOB_ONLY(sampleDataAHandleSize);
+ MANUAL_ARRAY_TRANSFER2(OffsetPtr<SampleDataA>, sampleDataAHandle, sampleDataAHandleSize);
+
+ TRANSFER(intValue2);
+}
+
+static void SetupTestDataA (SampleDataA& sourceData)
+{
+ sourceData.intValue1 = 1;
+ sourceData.float4Value = math::float4(1, 2, 3, 4);
+ sourceData.vector3 = Vector3f(1,2,3);
+
+ mecanim::uint32_t i;
+ for(i=0; i<SampleDataA::kLastIndex;i++)
+ sourceData.index[i] = i;
+
+ sourceData.nullPtr = NULL;
+ sourceData.floatPtr = new float;
+ *sourceData.floatPtr = 5.5F;
+
+ sourceData.emptyArraySize = 0;
+ sourceData.emptyArray = NULL;
+
+ sourceData.arraySize = 3;
+ sourceData.array = new float[3];
+ sourceData.array[0] = 6.5f;
+ sourceData.array[1] = 7.5f;
+ sourceData.array[2] = 8.5f;
+ sourceData.intValue2 = 2;
+}
+
+static void DeleteTestDataA (SampleDataA& sourceData)
+{
+ delete[] sourceData.array.Get();
+ delete sourceData.floatPtr.Get();
+}
+
+
+static void SetupTestData (SampleData& sourceData)
+{
+ sourceData.intValue1 = 1;
+ sourceData.float4Value = math::float4(1, 2, 3, 4);
+ sourceData.vector3 = Vector3f(1,2,3);
+ sourceData.nullPtr = NULL;
+ sourceData.floatPtr = new float;
+ *sourceData.floatPtr = 5.5F;
+
+ sourceData.emptyArraySize = 0;
+ sourceData.emptyArray = NULL;
+
+ sourceData.arraySize = 3;
+ sourceData.array = new double[3];
+ sourceData.array[0] = 6.5;
+ sourceData.array[1] = 7.5;
+ sourceData.array[2] = 8.5;
+ sourceData.intValue2 = 2;
+
+ sourceData.sampleDataASize = 4;
+
+ sourceData.sampleDataA = ConstructArray<SampleDataA>(sourceData.sampleDataASize);
+ for(int i=0;i<sourceData.sampleDataASize;i++)
+ {
+ SetupTestDataA(sourceData.sampleDataA[i]);
+ }
+
+ sourceData.sampleDataAHandleSize = 2;
+ sourceData.sampleDataAHandle = new OffsetPtr<SampleDataA> [2];
+ sourceData.sampleDataAHandle[0] = Construct<SampleDataA>();
+ SetupTestDataA(*sourceData.sampleDataAHandle[0]);
+ sourceData.sampleDataAHandle[1] = Construct<SampleDataA>();
+ SetupTestDataA(*sourceData.sampleDataAHandle[1]);
+}
+
+
+static void DeleteTestData (SampleData& sourceData)
+{
+ for(int i=0;i<sourceData.sampleDataASize;i++)
+ DeleteTestDataA (sourceData.sampleDataA[i]);
+
+ Destruct (sourceData.sampleDataA.Get());
+ Destruct (sourceData.sampleDataAHandle[0].Get());
+ Destruct (sourceData.sampleDataAHandle[1].Get());
+
+ delete sourceData.floatPtr.Get();
+ delete[] sourceData.array.Get();
+ delete[] sourceData.sampleDataAHandle.Get();
+}
+
+
+static void TestDataA (SampleDataA& deserialized)
+{
+ CHECK (reinterpret_cast<UInt32>(&deserialized) % ALIGN_OF(SampleDataA) == 0 );
+ CHECK (deserialized.intValue1 == 1);
+ CHECK (dot(deserialized.float4Value - math::float4(1, 2, 3, 4)) == math::float1::zero());
+
+ mecanim::uint32_t i;
+ for(i=0; i<SampleDataA::kLastIndex;i++)
+ CHECK (deserialized.index[i] == i);
+
+ CHECK (deserialized.nullPtr.IsNull());
+
+ float ptr = *deserialized.floatPtr;
+ CHECK (ptr == 5.5F);
+
+ float* array = deserialized.array.Get();
+ CHECK (array[0] == 6.5F);
+ CHECK (array[1] == 7.5F);
+ CHECK (array[2] == 8.5F);
+
+ CHECK (deserialized.emptyArray.IsNull());
+ CHECK (deserialized.emptyArraySize == 0);
+
+
+ CHECK (deserialized.vector3 == Vector3f(1,2,3));
+ CHECK (deserialized.intValue2 == 2);
+}
+
+
+static void TestData (SampleData& deserialized)
+{
+ CHECK (reinterpret_cast<UInt32>(&deserialized) % ALIGN_OF(SampleData) == 0);
+ CHECK (deserialized.intValue1 == 1);
+ CHECK (dot(deserialized.float4Value - math::float4(1, 2, 3, 4)) == math::float1::zero() );
+
+ CHECK (deserialized.nullPtr.IsNull());
+
+ float ptr = *deserialized.floatPtr;
+ CHECK (ptr == 5.5F);
+
+ double* array = deserialized.array.Get();
+ CHECK (array[0] == 6.5);
+ CHECK (array[1] == 7.5);
+ CHECK (array[2] == 8.5);
+
+ CHECK (deserialized.emptyArray.IsNull());
+ CHECK (deserialized.emptyArraySize == 0);
+
+ CHECK (deserialized.vector3 == Vector3f(1,2,3));
+ CHECK (deserialized.intValue2 == 2);
+
+ CHECK (deserialized.sampleDataASize == 4);
+ for(int i=0;i<deserialized.sampleDataASize;i++)
+ {
+ TestDataA(deserialized.sampleDataA[i]);
+ }
+
+ CHECK (deserialized.sampleDataAHandleSize == 2);
+ for(int i=0;i<deserialized.sampleDataAHandleSize;i++)
+ {
+ TestDataA(*deserialized.sampleDataAHandle[i]);
+ }
+}
+
+// This test crashes the linux-amd64 editor right now
+SUITE (BlobTests)
+{
+ TEST (Blobification_BlobPtrs)
+ {
+ SampleData sourceData;
+ SetupTestData (sourceData);
+ TestData(sourceData);
+
+ // Generate blob
+ BlobWrite::container_type data;
+ BlobWrite blobWrite (data, kNoTransferInstructionFlags, kBuildNoTargetPlatform);
+ blobWrite.Transfer(sourceData, "Base");
+ TestData(*reinterpret_cast<SampleData*> (data.begin()));
+
+ // Generate blob with reduce copy
+ BlobWrite::container_type dataReduced;
+ BlobWrite blobWriteReduce (dataReduced, kNoTransferInstructionFlags, kBuildNoTargetPlatform);
+ blobWriteReduce.SetReduceCopy(true);
+ blobWriteReduce.Transfer(sourceData, "Base");
+ TestData(*reinterpret_cast<SampleData*> (dataReduced.begin()));
+
+ // Ensure reduced blob is actually smaller.
+ CHECK (dataReduced.size() < data.size());
+
+ // Ensure that 64 bit data is larger than non-64 bit data
+ BlobWrite::container_type data64;
+ BlobWrite blobWrite64 (data64, kNoTransferInstructionFlags, kBuildStandaloneWin64Player);
+ blobWrite64.Transfer(sourceData, "Base");
+
+ BlobWrite::container_type data32;
+ BlobWrite blobWrite32 (data32, kNoTransferInstructionFlags, kBuildStandaloneWinPlayer);
+ blobWrite32.Transfer(sourceData, "Base");
+ CHECK (data64.size() > data32.size());
+
+ DeleteTestData (sourceData);
+ }
+
+ TEST (Blobification_OffsetPtr)
+ {
+ OffsetPtr<size_t>* ptrHigh = new OffsetPtr<size_t>;
+ OffsetPtr<size_t>* ptrLow = new OffsetPtr<size_t>;
+
+ size_t* ptrH = reinterpret_cast<size_t*>(std::numeric_limits<size_t>::max()-4);
+ size_t* ptrL = reinterpret_cast<size_t*>(4);
+
+ ptrHigh->reset(ptrH);
+ ptrLow->reset(ptrL);
+
+ size_t h = reinterpret_cast<size_t>(ptrHigh->Get());
+ size_t l = reinterpret_cast<size_t>(ptrLow->Get());
+
+
+ CHECK (h == std::numeric_limits<size_t>::max()-4);
+ CHECK (l == 4);
+
+ delete ptrHigh;
+ delete ptrLow;
+ }
+}
+
+#endif //ENABLE_UNIT_TESTS
diff --git a/Runtime/Serialize/Blobification/BlobWrite.cpp b/Runtime/Serialize/Blobification/BlobWrite.cpp
new file mode 100644
index 0000000..dcb7705
--- /dev/null
+++ b/Runtime/Serialize/Blobification/BlobWrite.cpp
@@ -0,0 +1,148 @@
+#include "UnityPrefix.h"
+#include "BlobWrite.h"
+#include "Configuration/UnityConfigure.h"
+#include "BlobWriteTargetSupport.h"
+
+BlobWrite::BlobWrite (container_type& blob, TransferInstructionFlags flags, BuildTargetPlatform targetPlatform)
+: m_Blob(blob),
+ m_CopyData(true),
+ m_ReduceCopy(false),
+ m_TargetPlatform(targetPlatform)
+{
+ m_Flags = false;
+ m_SwapEndianess = m_Flags & kSwapEndianess;
+ m_Use64BitOffsetPtr = IsBuildTarget64BitBlob (targetPlatform);
+}
+
+void BlobWrite::Push (size_t size, void* srcDataPtr, size_t align)
+{
+ Assert (m_CopyData);
+
+ size_t offset = AlignAddress(m_Blob.size(), align);
+ m_Context.push( TypeContext(offset, 0, reinterpret_cast<UInt8*> (srcDataPtr), size) );
+ m_Blob.resize_initialized(offset + size, 0);
+ m_CopyData = false;
+}
+
+void BlobWrite::WritePtrValueAtLocation (size_t locationInBlob, SInt64 value)
+{
+ if (m_Use64BitOffsetPtr)
+ {
+ SInt64 offset64 = value;
+
+ if (m_SwapEndianess)
+ SwapEndianBytes(offset64);
+ memcpy (&m_Blob[locationInBlob], &offset64, sizeof(offset64));
+ }
+ else
+ {
+ SInt32 offset32 = value;
+
+ if (m_SwapEndianess)
+ SwapEndianBytes(offset32);
+ memcpy (&m_Blob[locationInBlob], &offset32, sizeof(offset32));
+ }
+}
+
+
+void BlobWrite::TransferPtrImpl (bool isValidPtr, ReduceCopyData* reduceCopyData, size_t alignOfT)
+{
+ Assert(!m_CopyData);
+ // When the data is null we will not call Transfer.
+ m_CopyData = isValidPtr;
+
+ // Need to update OffsetPtr's member 'mOffset'
+ // compute member offset in memory buffer
+ size_t dataPosition = AlignAddress(m_Blob.size(), alignOfT);
+ size_t offset = GetActiveOffset();
+ offset = dataPosition - offset;
+ if (!isValidPtr)
+ offset = 0;
+
+ // Write the ptr
+ WritePtrValueAtLocation(GetActiveOffset (), offset);
+
+ // Setup reduce copy data for later use by ReduceCopyImpl
+ if (reduceCopyData != NULL)
+ {
+ if (isValidPtr)
+ {
+ reduceCopyData->ptrPosition = GetActiveOffset();
+ reduceCopyData->dataStart = dataPosition;
+ reduceCopyData->blobSize = m_Blob.size();
+ }
+ else
+ {
+ reduceCopyData->ptrPosition = 0xFFFFF;
+ reduceCopyData->dataStart = 0xFFFFF;
+ reduceCopyData->blobSize = 0xFFFFF;
+ }
+ }
+
+
+ // Offset write location in the blob
+ m_Context.top().m_Offset += m_Use64BitOffsetPtr ? sizeof(SInt64) : sizeof(SInt32);
+ if (HasOffsetPtrWithDebugPtr())
+ m_Context.top().m_Offset += sizeof(void*);
+}
+
+bool BlobWrite::HasOffsetPtrWithDebugPtr () const
+{
+ return m_TargetPlatform == kBuildNoTargetPlatform;
+}
+
+bool BlobWrite::AllowDataLayoutValidation () const
+{
+ size_t targetOffsetPtrSize = Use64BitOffsetPtr () ? sizeof(SInt64) : sizeof(SInt32);
+ if (HasOffsetPtrWithDebugPtr ())
+ targetOffsetPtrSize += sizeof(void*);
+
+ size_t srcOffsetPtrSize = sizeof(OffsetPtr<UInt8>);
+
+ return targetOffsetPtrSize == srcOffsetPtrSize;
+}
+
+// Ensure that the user has matching Transfer calls & Data layout in the struct
+void BlobWrite::ValidateSerializedLayout (void* srcData, const char* name)
+{
+ UInt8* srcDataPtr = reinterpret_cast<UInt8*> (srcData);
+
+ // (float4 and some others transfer functions, transfer temporary data, we ignore layout checks on those and hope for the best)
+ int srcDataOffset = srcDataPtr - m_Context.top().m_SourceDataPtr;
+ if (srcDataOffset < 0 || srcDataOffset >= m_Context.top().m_SourceDataSize)
+ return;
+
+ // When targeting a platform with a different layout than our own, obviously these checks dont make sense...
+ if (!AllowDataLayoutValidation ())
+ return;
+
+ int blobOffset = m_Context.top().m_Offset;
+ if (srcDataOffset != blobOffset)
+ {
+ AssertString(Format("BlobWrite: Transfer '%s' is not called in the same order as the struct is laid out. Expected: %d got: %d ", name, srcDataOffset, blobOffset));
+ }
+}
+
+void BlobWrite::ReduceCopyImpl (const ReduceCopyData& reduce, size_t alignOfT)
+{
+ if (!m_ReduceCopy || reduce.dataStart == 0xFFFFF)
+ return;
+
+ // Find any data in the blob that matches the last written data.
+ // if we find it, delete it again and reference the previous memory block instead
+ size_t dataSize = m_Blob.size() - reduce.dataStart;
+ for (int i=0;i < reduce.dataStart;i+=alignOfT)
+ {
+ if (memcmp(&m_Blob[i], &m_Blob[reduce.dataStart], dataSize) == 0)
+ {
+ // Update offset pointer
+ SInt64 offset = i - reduce.ptrPosition;
+ WritePtrValueAtLocation (reduce.ptrPosition, offset);
+
+ // resize blob based on the reduce copy
+ m_Blob.resize_initialized(reduce.blobSize, 0);
+
+ return;
+ }
+ }
+}
diff --git a/Runtime/Serialize/Blobification/BlobWrite.h b/Runtime/Serialize/Blobification/BlobWrite.h
new file mode 100644
index 0000000..56a0340
--- /dev/null
+++ b/Runtime/Serialize/Blobification/BlobWrite.h
@@ -0,0 +1,220 @@
+#ifndef BLOBWRITE_H
+#define BLOBWRITE_H
+
+#include "Runtime/Serialize/TransferFunctions/TransferBase.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+#include "Runtime/Animation/MecanimArraySerialization.h"
+#include "offsetptr.h"
+#include "BlobSize.h"
+#include "ReduceCopyData.h"
+#include <stack>
+#include "Runtime/Modules/ExportModules.h"
+
+class EXPORT_COREMODULE BlobWrite : public TransferBase
+{
+public:
+
+ typedef dynamic_array<UInt8, 16> container_type;
+
+private:
+
+ container_type& m_Blob;
+ int m_TargetPlatform;
+ bool m_CopyData;
+ bool m_ReduceCopy;
+ bool m_Use64BitOffsetPtr;
+ bool m_SwapEndianess;
+
+ struct TypeContext
+ {
+ TypeContext(size_t root, size_t offset, UInt8* srcDataPtr,size_t srcDataSize):m_Root(root),m_Offset(offset),m_SourceDataPtr(srcDataPtr),m_SourceDataSize(srcDataSize) {}
+
+ size_t m_Root;
+ size_t m_Offset;
+
+ UInt8* m_SourceDataPtr;
+ size_t m_SourceDataSize;
+ };
+ std::stack<TypeContext> m_Context;
+
+ std::size_t AlignAddress(std::size_t addr, std::size_t align)
+ {
+ return addr + ((~addr + 1U) & (align - 1U));
+ }
+
+ size_t GetActiveOffset () const
+ {
+ return m_Context.top().m_Root + m_Context.top().m_Offset;
+ }
+
+ UInt8* GetActiveBlobPtr ()
+ {
+ return &m_Blob[GetActiveOffset ()];
+ }
+
+ void WritePtrValueAtLocation (size_t locationInBlob, SInt64 value);
+
+ void ValidateSerializedLayout (void* srcData, const char* name);
+
+ void Push (size_t size, void* srcDataPtr, size_t align);
+
+ void TransferPtrImpl (bool isValidPtr, ReduceCopyData* reduceCopyData, size_t alignOfT);
+ void ReduceCopyImpl (const ReduceCopyData& reduce, size_t alignOfT);
+
+ bool HasOffsetPtrWithDebugPtr () const;
+ bool Use64BitOffsetPtr() const { return m_Use64BitOffsetPtr; }
+ bool AllowDataLayoutValidation () const;
+
+public:
+
+ BlobWrite (container_type& blob, TransferInstructionFlags flags, BuildTargetPlatform targetPlatform);
+
+ void SetReduceCopy (bool reduce) { m_ReduceCopy = reduce; }
+
+ bool IsWriting () { return true; }
+ bool IsWritingPPtr () { return true; }
+ bool NeedsInstanceIDRemapping () { return m_Flags & kNeedsInstanceIDRemapping; }
+ bool ConvertEndianess () { return m_SwapEndianess; }
+ bool IsWritingGameReleaseData ()
+ {
+ return true;
+ }
+ bool IsSerializingForGameRelease ()
+ {
+ return true;
+ }
+ bool IsBuildingTargetPlatform (BuildTargetPlatform platform)
+ {
+ if (platform == kBuildAnyPlayerData)
+ return m_TargetPlatform >= kBuildValidPlayer;
+ else
+ return m_TargetPlatform == platform;
+ }
+
+ BuildTargetPlatform GetBuildingTargetPlatform () { return static_cast<BuildTargetPlatform>(m_TargetPlatform); }
+
+ template<class T>
+ void Transfer (T& data, const char* name, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ template<class T>
+ void TransferWithTypeString (T& data, const char* name, const char* typeName, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ template<class T>
+ void TransferBasicData (T& data);
+
+ template<class T>
+ void TransferPtr (bool isValidPtr, ReduceCopyData* reduceCopyData);
+
+ template<class T>
+ void ReduceCopy (const ReduceCopyData& reduce);
+
+ template<class T>
+ void TransferSTLStyleArray (T& data, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ template<class> friend class BlobWriteTransferSTLStyleArrayImpl;
+};
+
+template<typename T> class BlobWriteTransferSTLStyleArrayImpl
+{
+public:
+ void operator()(T& data, TransferMetaFlags metaFlags, BlobWrite& transfer)
+ {
+ AssertString ("STL array are not support for BlobWrite");
+ }
+};
+
+template<typename T> class BlobWriteTransferSTLStyleArrayImpl< OffsetPtrArrayTransfer<T> >
+{
+public:
+ void operator()(OffsetPtrArrayTransfer<T>& data, TransferMetaFlags metaFlags, BlobWrite& transfer)
+ {
+ if (data.size() == 0)
+ {
+ Assert (!transfer.m_CopyData);
+ return;
+ }
+ Assert (transfer.m_CopyData);
+
+ size_t arrayByteSize = BlobSize::CalculateSize(*data.begin(), transfer.HasOffsetPtrWithDebugPtr(), transfer.Use64BitOffsetPtr()) * data.size();
+ transfer.Push(arrayByteSize, &*data.begin(), ALIGN_OF( typename OffsetPtrArrayTransfer<T>::value_type ));
+
+ typename OffsetPtrArrayTransfer<T>::iterator end = data.end ();
+ for (typename OffsetPtrArrayTransfer<T>::iterator i = data.begin ();i != end;++i)
+ transfer.Transfer (*i, "data");
+
+ transfer.m_Context.pop();
+ }
+};
+
+template<typename T, int SIZE> class BlobWriteTransferSTLStyleArrayImpl< StaticArrayTransfer<T, SIZE> >
+{
+public:
+ void operator()(StaticArrayTransfer<T, SIZE>& data, TransferMetaFlags metaFlags, BlobWrite& transfer)
+ {
+ typename StaticArrayTransfer<T, SIZE>::iterator end = data.end ();
+ for (typename StaticArrayTransfer<T, SIZE>::iterator i = data.begin ();i != end;++i)
+ transfer.Transfer (*i, "data");
+ }
+};
+
+template<class T> inline
+void BlobWrite::TransferSTLStyleArray (T& data, TransferMetaFlags metaFlags)
+{
+ BlobWriteTransferSTLStyleArrayImpl<T> transfer;
+ transfer(data, metaFlags, *this);
+}
+
+template<class T> inline
+void BlobWrite::Transfer (T& data, const char* name, TransferMetaFlags)
+{
+ bool copyData = m_CopyData;
+ if (m_CopyData)
+ Push(BlobSize::CalculateSize(data, HasOffsetPtrWithDebugPtr(), Use64BitOffsetPtr()), &data, SerializeTraits<T>::GetAlignOf() );
+
+ // Follow natural c++ alignment
+ size_t head = m_Context.top().m_Root;
+ size_t& offset = m_Context.top().m_Offset;
+ // always align head + offset not only offset otherwise you may get wrong align for nested data structure
+ offset = AlignAddress(head + offset, SerializeTraits<T>::GetAlignOf()) - head;
+
+ ValidateSerializedLayout(&data, name);
+
+ SerializeTraits<T>::Transfer (data, *this);
+
+ if (copyData)
+ m_Context.pop();
+}
+
+template<class T> inline
+void BlobWrite::TransferWithTypeString (T& data, const char*, const char*, TransferMetaFlags)
+{
+ SerializeTraits<T>::Transfer (data, *this);
+}
+
+template<class T> inline
+void BlobWrite::TransferBasicData (T& srcData)
+{
+ Assert(m_Blob.size() >= GetActiveOffset() + sizeof(T));
+
+ // Write basic data into blob & endianswap
+ UInt8* blobPtr = GetActiveBlobPtr();
+ memcpy(blobPtr, &srcData, sizeof(T));
+ if (m_SwapEndianess)
+ SwapEndianBytes(*reinterpret_cast<T*> (blobPtr));
+
+ m_Context.top().m_Offset += sizeof(T);
+}
+
+template<class T> inline
+void BlobWrite::TransferPtr (bool isValidPtr, ReduceCopyData* reduceCopyData)
+{
+ TransferPtrImpl (isValidPtr, reduceCopyData, ALIGN_OF(T));
+}
+
+template<class T> inline
+void BlobWrite::ReduceCopy (const ReduceCopyData& reduce)
+{
+ ReduceCopyImpl(reduce, ALIGN_OF(T));
+}
+
+#endif
diff --git a/Runtime/Serialize/Blobification/BlobWriteTargetSupport.cpp b/Runtime/Serialize/Blobification/BlobWriteTargetSupport.cpp
new file mode 100644
index 0000000..b66b941
--- /dev/null
+++ b/Runtime/Serialize/Blobification/BlobWriteTargetSupport.cpp
@@ -0,0 +1,31 @@
+#include "UnityPrefix.h"
+#include "BlobWriteTargetSupport.h"
+
+bool DoesBuildTargetSupportBlobification (BuildTargetPlatform target, TransferInstructionFlags flags)
+{
+ // If we are writing typetrees, then we can't use blobification
+ bool writeTypeTree = (flags & kDisableWriteTypeTree) == 0;
+ if (writeTypeTree)
+ return false;
+
+
+ // Webplayer & Editor should never use blobification
+ Assert(target != kBuildWebPlayerLZMA && target != kBuildWebPlayerLZMAStreamed && target != kBuildAnyPlayerData || target == kBuildNoTargetPlatform);
+ return true;
+}
+
+bool IsBuildTarget64BitBlob (BuildTargetPlatform target)
+{
+ Assert(target != kBuildAnyPlayerData && target != kBuildWebPlayerLZMA && target != kBuildWebPlayerLZMAStreamed);
+
+ // Building blob for the editor (Choose whatever we are running with)
+ if (target == kBuildNoTargetPlatform)
+ return sizeof(size_t) == sizeof(UInt64);
+
+ // Known 64 bit platform?
+ bool target64Bit = target == kBuildMetroPlayerX64 || target == kBuildStandaloneWin64Player || target == kBuildStandaloneLinux64 || target == kBuildStandaloneLinuxUniversal;
+ if (target64Bit)
+ return true;
+
+ return false;
+} \ No newline at end of file
diff --git a/Runtime/Serialize/Blobification/BlobWriteTargetSupport.h b/Runtime/Serialize/Blobification/BlobWriteTargetSupport.h
new file mode 100644
index 0000000..76191f5
--- /dev/null
+++ b/Runtime/Serialize/Blobification/BlobWriteTargetSupport.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include "Runtime/Serialize/SerializationMetaFlags.h"
+
+bool DoesBuildTargetSupportBlobification (BuildTargetPlatform target, TransferInstructionFlags flags);
+bool IsBuildTarget64BitBlob (BuildTargetPlatform target); \ No newline at end of file
diff --git a/Runtime/Serialize/Blobification/OffsetPtrTest.cpp b/Runtime/Serialize/Blobification/OffsetPtrTest.cpp
new file mode 100644
index 0000000..9ddd2a8
--- /dev/null
+++ b/Runtime/Serialize/Blobification/OffsetPtrTest.cpp
@@ -0,0 +1,31 @@
+#include "UnityPrefix.h"
+
+#include "Runtime/Serialize/SerializeTraits.h"
+#include "offsetptr.h"
+
+
+#include <limits>
+
+
+void TestOffsetPtr ()
+{
+ OffsetPtr<size_t>* ptrHigh = new OffsetPtr<size_t>;
+ OffsetPtr<size_t>* ptrLow = new OffsetPtr<size_t>;
+
+ size_t* ptrH = reinterpret_cast<size_t*>(std::numeric_limits<size_t>::max()-4);
+ size_t* ptrL = reinterpret_cast<size_t*>(4);
+
+ ptrHigh->reset(ptrH);
+ ptrLow->reset(ptrL);
+
+ size_t h = reinterpret_cast<size_t>(ptrHigh->Get());
+ size_t l = reinterpret_cast<size_t>(ptrLow->Get());
+
+
+ Assert(h == std::numeric_limits<size_t>::max()-4);
+ Assert(l == 4);
+
+ delete ptrHigh;
+ delete ptrLow;
+
+}
diff --git a/Runtime/Serialize/Blobification/ReduceCopyData.h b/Runtime/Serialize/Blobification/ReduceCopyData.h
new file mode 100644
index 0000000..ae4952f
--- /dev/null
+++ b/Runtime/Serialize/Blobification/ReduceCopyData.h
@@ -0,0 +1,8 @@
+#pragma once
+
+struct ReduceCopyData
+{
+ size_t ptrPosition;
+ size_t dataStart;
+ size_t blobSize;
+};
diff --git a/Runtime/Serialize/Blobification/offsetptr.h b/Runtime/Serialize/Blobification/offsetptr.h
new file mode 100644
index 0000000..7ef1809
--- /dev/null
+++ b/Runtime/Serialize/Blobification/offsetptr.h
@@ -0,0 +1,257 @@
+#pragma once
+
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/Serialize/SerializeTraits.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Serialize/SerializeTraitsBase.h"
+#include "Runtime/Serialize/TransferFunctionFwd.h"
+#include "Runtime/Utilities/TypeUtilities.h"
+#include "ReduceCopyData.h"
+
+template<typename TYPE>
+class OffsetPtr
+{
+public:
+ typedef TYPE value_type;
+ typedef TYPE* ptr_type;
+ typedef TYPE const* const_ptr_type;
+ typedef TYPE& reference_type;
+ typedef TYPE const& const_reference_type;
+ typedef size_t offset_type;
+
+ OffsetPtr():m_Offset(0),m_DebugPtr(0)
+ {
+ }
+
+ OffsetPtr (const OffsetPtr<value_type>& ptr):m_Offset(ptr.m_Offset)
+ {
+ }
+
+ OffsetPtr& operator = (const OffsetPtr<value_type>& ptr)
+ {
+ m_Offset = ptr.m_Offset;
+ return *this;
+ }
+
+ void reset(ptr_type ptr)
+ {
+ m_Offset = ptr != 0 ? reinterpret_cast<size_t>(ptr) - reinterpret_cast<size_t>(this) : 0;
+#ifdef UNITY_EDITOR
+ m_DebugPtr = ptr;
+#endif
+ }
+
+ OffsetPtr& operator = (const ptr_type ptr)
+ {
+ reset (ptr);
+ return *this;
+ }
+
+ ptr_type operator->()
+ {
+ ptr_type ptr = reinterpret_cast<ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset);
+#ifdef UNITY_EDITOR
+ m_DebugPtr = ptr;
+#endif
+ return ptr;
+ }
+ const_ptr_type operator->()const
+ {
+ return reinterpret_cast<const_ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset);
+ }
+
+ reference_type operator*()
+ {
+ ptr_type ptr = reinterpret_cast<ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset);
+#ifdef UNITY_EDITOR
+ m_DebugPtr = ptr;
+#endif
+ return *ptr;
+ }
+
+ const_reference_type operator*()const
+ {
+ return *reinterpret_cast<const_ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset);
+ }
+
+ value_type& operator[](std::size_t i )
+ {
+ assert(i != std::numeric_limits<std::size_t>::max());
+ ptr_type ptr = reinterpret_cast<ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset);
+#ifdef UNITY_EDITOR
+ m_DebugPtr = ptr;
+#endif
+ return ptr[i];
+ }
+
+ value_type const& operator[](std::size_t i ) const
+ {
+ assert(i != std::numeric_limits<std::size_t>::max());
+ const_ptr_type ptr = reinterpret_cast<const_ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset);
+ return ptr[i];
+ }
+
+ bool IsNull()const
+ {
+ return m_Offset == 0;
+ }
+
+ ptr_type Get()
+ {
+#ifdef UNITY_EDITOR
+ m_DebugPtr = reinterpret_cast<ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset);
+#endif
+ // TODO: serialize trait for offset ptr call begin and end which call OffsetPtr::Get
+ //Assert(!IsNull());
+ return reinterpret_cast<ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset);
+ }
+
+ const_ptr_type Get()const
+ {
+#ifdef UNITY_EDITOR
+ m_DebugPtr = reinterpret_cast<ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset);
+#endif
+ // TODO: serialize trait for offset ptr call begin and end which call OffsetPtr::Get
+ //Assert(!IsNull());
+ return reinterpret_cast<ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset);
+ }
+
+
+ size_t get_size () const
+ {
+ return sizeof(TYPE);
+ }
+
+protected:
+ offset_type m_Offset;
+#ifdef UNITY_EDITOR
+ mutable ptr_type m_DebugPtr;
+#endif
+};
+
+
+template<typename TYPE>
+class SerializeTraits< OffsetPtr<TYPE> > : public SerializeTraitsBase< OffsetPtr<TYPE> >
+{
+ public:
+
+ typedef OffsetPtr<TYPE> value_type;
+ inline static const char* GetTypeString (void*) { return "OffsetPtr"; }
+ inline static bool IsAnimationChannel () { return false; }
+ inline static bool MightContainPPtr () { return true; }
+ inline static bool AllowTransferOptimization () { return false; }
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ if(IsSameType<TransferFunction, BlobWrite>::result)
+ {
+ ReduceCopyData reduce;
+ transfer.template TransferPtr<TYPE>(!data.IsNull(), &reduce);
+ if (!data.IsNull())
+ {
+ transfer.Transfer(*data, "data");
+ }
+ transfer.template ReduceCopy<TYPE> (reduce);
+ }
+ else if(transfer.IsReading () || transfer.IsWriting ())
+ {
+ bool isNull = data.IsNull();
+
+ transfer.template TransferPtr<TYPE>(true, NULL);
+ if (isNull)
+ {
+ mecanim::memory::ChainedAllocator* allocator = static_cast<mecanim::memory::ChainedAllocator*> (transfer.GetUserData());
+ data = allocator->Construct<TYPE>();
+ }
+
+ transfer.Transfer(*data, "data");
+ }
+ else if(IsSameType<TransferFunction, BlobSize>::result)
+ {
+ transfer.template TransferPtr<TYPE>(false, NULL);
+ }
+ // Support for ProxyTransfer
+ else
+ {
+ transfer.template TransferPtr<TYPE>(false, NULL);
+
+ TYPE p;
+ transfer.Transfer(p, "data");
+ }
+ }
+};
+
+template<class T>
+struct OffsetPtrArrayTransfer
+{
+ typedef T* iterator;
+ typedef T value_type;
+
+ OffsetPtr<T>& m_Data;
+ UInt32& m_ArraySize;
+ void* m_Allocator;
+ bool m_ClearPtrs;
+
+ OffsetPtrArrayTransfer (OffsetPtr<T>& data, UInt32& size, void* allocator, bool clearPtrs)
+ : m_Data(data),m_ArraySize(size)
+ {
+ m_Allocator = allocator;
+ m_ClearPtrs = clearPtrs;
+ }
+
+ T* begin () { return m_Data.Get(); }
+ T* end () { return m_Data.Get() + m_ArraySize; }
+ size_t size() { return m_ArraySize; }
+
+ void resize (int newSize)
+ {
+ m_ArraySize = newSize;
+
+ mecanim::memory::ChainedAllocator* allocator = static_cast<mecanim::memory::ChainedAllocator*> (m_Allocator);
+ Assert(allocator != NULL);
+
+ if (newSize != 0)
+ {
+ m_Data = allocator->ConstructArray<value_type> (newSize);
+ if (m_ClearPtrs)
+ memset(begin(), 0, sizeof(value_type) * newSize);
+ }
+ else
+ m_Data = NULL;
+ }
+};
+
+template<class T>
+class SerializeTraits<OffsetPtrArrayTransfer<T> > : public SerializeTraitsBase<OffsetPtrArrayTransfer<T> >
+{
+public:
+
+ typedef OffsetPtrArrayTransfer<T> value_type;
+ DEFINE_GET_TYPESTRING_CONTAINER (vector)
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ ReduceCopyData reduceCopy;
+ transfer.template TransferPtr<typename value_type::value_type>(transfer.IsReading() || transfer.IsWriting() ? data.m_ArraySize != 0 : false, &reduceCopy);
+
+ transfer.TransferSTLStyleArray (data);
+ transfer.template ReduceCopy<typename value_type::value_type>(reduceCopy);
+ }
+
+ static bool IsContinousMemoryArray () { return true; }
+ static void ResizeSTLStyleArray (value_type& data, int rs)
+ {
+ data.resize(rs);
+ }
+
+ static void resource_image_assign_external (value_type& data, void* begin, void* end)
+ {
+ }
+};
+
+
+#define MANUAL_ARRAY_TRANSFER2(TYPE,DATA,SIZE) OffsetPtrArrayTransfer<TYPE> DATA##ArrayTransfer (DATA, SIZE, transfer.GetUserData(), false); transfer.Transfer(DATA##ArrayTransfer, #DATA);
+#define TRANSFER_BLOB_ONLY(DATA) if (IsSameType<TransferFunction, BlobWrite>::result || IsSameType<TransferFunction, BlobSize>::result) transfer.Transfer(DATA, #DATA);
+
diff --git a/Runtime/Serialize/BuildTargetVerification.h b/Runtime/Serialize/BuildTargetVerification.h
new file mode 100644
index 0000000..5bfdb2f
--- /dev/null
+++ b/Runtime/Serialize/BuildTargetVerification.h
@@ -0,0 +1,78 @@
+#ifndef BUILDTARGETVERIFICATION_H
+#define BUILDTARGETVERIFICATION_H
+
+#include "SerializationMetaFlags.h"
+#include "Configuration/UnityConfigure.h"
+
+inline bool IsPCStandaloneTargetPlatform (BuildTargetPlatform targetPlatform)
+{
+ return
+ // We don't support building for these any more, but we still need the constants for asset bundle
+ // backwards compatibility.
+ targetPlatform == kBuildStandaloneOSXUniversal ||
+ targetPlatform == kBuildStandaloneOSXPPC ||
+
+ targetPlatform == kBuildStandaloneOSXIntel ||
+ targetPlatform == kBuildStandaloneOSXIntel64 ||
+ targetPlatform == kBuildStandaloneWinPlayer ||
+ targetPlatform == kBuildStandaloneWin64Player ||
+ targetPlatform == kBuildMetroPlayerX86 ||
+ targetPlatform == kBuildMetroPlayerX64 ||
+ targetPlatform == kBuildMetroPlayerARM ||
+ targetPlatform == kBuildStandaloneLinux ||
+ targetPlatform == kBuildStandaloneLinux64 ||
+ targetPlatform == kBuildStandaloneLinuxUniversal ||
+ targetPlatform == kBuildWinGLESEmu;
+}
+
+inline bool IsWebPlayerTargetPlatform (BuildTargetPlatform targetPlatform)
+{
+ return targetPlatform == kBuildWebPlayerLZMA || targetPlatform == kBuildWebPlayerLZMAStreamed || targetPlatform == kBuildNaCl;
+}
+
+inline bool IsMetroTargetPlatform (BuildTargetPlatform targetPlatform)
+{
+ return targetPlatform == kBuildMetroPlayerX86 || targetPlatform == kBuildMetroPlayerX64 || targetPlatform == kBuildMetroPlayerARM;
+}
+
+inline bool CanLoadFileBuiltForTargetPlatform(BuildTargetPlatform targetPlatform)
+{
+ // Editor and BinaryToTextFile can load anything
+#if UNITY_EDITOR || UNITY_EXTERNAL_TOOL
+ return true;
+ // Web players can handle all web formats
+#elif WEBPLUG
+ return IsWebPlayerTargetPlatform(targetPlatform) || IsPCStandaloneTargetPlatform(targetPlatform);
+#elif UNITY_METRO
+ // !! this code should be before #elif UNITY_WIN, because on Metro UNITY_WIN is defined as well !!
+ return IsMetroTargetPlatform(targetPlatform);
+#elif UNITY_WP8
+ // !! this code should be before #elif UNITY_WIN, because on WP8 UNITY_WIN is defined as well !!
+ return targetPlatform == kBuildWP8Player;
+#elif UNITY_OSX || (UNITY_WIN && !UNITY_WINRT) || UNITY_LINUX
+ // Standalone can handle all web and standalone formats
+ return IsPCStandaloneTargetPlatform(targetPlatform) || IsWebPlayerTargetPlatform(targetPlatform);
+#elif UNITY_WII
+ return targetPlatform == kBuildWii;
+#elif UNITY_XENON
+ return targetPlatform == kBuildXBOX360;
+#elif UNITY_PS3
+ return targetPlatform == kBuildPS3;
+#elif UNITY_IPHONE
+ return targetPlatform == kBuild_iPhone;
+#elif UNITY_ANDROID
+ return targetPlatform == kBuild_Android;
+#elif UNITY_FLASH
+ return targetPlatform == kBuildFlash;
+#elif UNITY_WEBGL
+ return targetPlatform == kBuildWebGL;
+#elif UNITY_BB10
+ return targetPlatform == kBuildBB10;
+#elif UNITY_TIZEN
+ return targetPlatform == kBuildTizen;
+#else
+#error Unknown platform
+#endif
+}
+
+#endif
diff --git a/Runtime/Serialize/CacheWrap.cpp b/Runtime/Serialize/CacheWrap.cpp
new file mode 100644
index 0000000..5ea4190
--- /dev/null
+++ b/Runtime/Serialize/CacheWrap.cpp
@@ -0,0 +1,461 @@
+#include "UnityPrefix.h"
+#include "CacheWrap.h"
+#include "FileCache.h"
+#include "Runtime/Utilities/Utility.h"
+#include <algorithm>
+#if UNITY_EDITOR
+#include "Editor/Src/GUIDPersistentManager.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#endif
+
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+using namespace std;
+
+#if UNITY_WINRT
+#define OUTPUT_FIELDS_OF_LAST_SERIALIZABLE OutputFieldsOfLastSerializableObject();
+#else
+#define OUTPUT_FIELDS_OF_LAST_SERIALIZABLE
+#endif
+
+CachedReader::CachedReader ()
+{
+ m_Cacher = 0;
+ m_Block = -1;
+ m_OutOfBoundsRead = false;
+ m_ActiveResourceImage = NULL;
+ #if CHECK_SERIALIZE_ALIGNMENT
+ m_CheckSerializeAlignment = true;
+ #endif
+}
+
+void CachedReader::InitRead (CacheReaderBase& cacher, size_t position, size_t readSize)
+{
+ AssertIf (m_Block != -1);
+ m_Cacher = &cacher;
+ AssertIf (m_Cacher == NULL);
+ m_CacheSize = m_Cacher->GetCacheSize ();
+ m_Block = position / m_CacheSize;
+ m_MaximumPosition = position + readSize;
+ m_MinimumPosition = position;
+
+ LockCacheBlockBounded ();
+
+ SetPosition (position);
+}
+
+void CachedReader::InitResourceImages (ResourceImageGroup& resourceImageGroup)
+{
+ Assert(m_ActiveResourceImage == NULL);
+ m_ResourceImageGroup = resourceImageGroup;
+}
+
+
+void CachedReader::LockCacheBlockBounded ()
+{
+ m_Cacher->LockCacheBlock (m_Block, &m_CacheStart, &m_CacheEnd);
+ UInt8* maxPos = m_MaximumPosition - m_Block * m_CacheSize + m_CacheStart;
+ m_CacheEnd = min(m_CacheEnd, maxPos);
+}
+
+size_t CachedReader::End ()
+{
+ AssertIf (m_Block == -1);
+ size_t position = GetPosition ();
+ OutOfBoundsError (position, 0);
+
+ m_Cacher->UnlockCacheBlock (m_Block);
+ m_Block = -1;
+ return position;
+}
+
+void CachedReader::GetStreamingInfo (size_t offset, size_t size, StreamingInfo* streamingInfo)
+{
+ Assert (m_ActiveResourceImage != NULL || m_ActiveResourceImage == m_ResourceImageGroup.resourceImages[kStreamingResourceImage]);
+
+ streamingInfo->offset = offset;
+ streamingInfo->size = size;
+ streamingInfo->path = m_ActiveResourceImage->GetStreamingPath();
+}
+
+CachedReader::~CachedReader ()
+{
+ AssertIf (m_Block != -1);
+}
+
+UInt8* CachedReader::FetchResourceImageData (size_t offset, size_t size)
+{
+ if (m_ActiveResourceImage == NULL)
+ {
+ ErrorString("Resource image for '" + m_Cacher->GetPathName() + "' couldn't be loaded!");
+ return NULL;
+ }
+
+ return m_ActiveResourceImage->Fetch (offset, size);
+}
+
+ResourceImage::ResourceImage (const std::string& path, bool streaming)
+{
+ if (!streaming)
+ {
+ m_Size = GetFileLength(path);
+ m_Data = static_cast<UInt8*> (UNITY_MALLOC(kMemResource, m_Size));
+
+ if (!ReadFromFile(path, m_Data, 0, m_Size))
+ {
+ ErrorString("Resource image couldn't be loaded completely");
+ }
+ }
+ else
+ {
+ m_StreamingPath = path;
+ }
+}
+
+ResourceImage::~ResourceImage ()
+{
+ if (m_Data)
+ UNITY_FREE(kMemResource, m_Data);
+}
+
+void CachedReader::Read (void* data, size_t size)
+{
+ if (m_CachePosition + size <= m_CacheEnd)
+ {
+ memcpy (data, m_CachePosition, size);
+ m_CachePosition += size;
+ }
+ else
+ {
+ // Read some data directly if it is coming in big chunks and we are not hitting the end of the file!
+ size_t position = GetPosition ();
+ OutOfBoundsError (position, size);
+
+ if (m_OutOfBoundsRead)
+ {
+ memset(data, 0, size);
+ return;
+ }
+
+ // Read enough bytes from the cache to align the position with the cache size
+ if (position % m_CacheSize != 0)
+ {
+ int blockEnd = ((position / m_CacheSize) + 1) * m_CacheSize;
+ int curReadSize = min<int> (size, blockEnd - position);
+ UpdateReadCache (data, curReadSize);
+ (UInt8*&)data += curReadSize;
+ size -= curReadSize;
+ position += curReadSize;
+ }
+
+ // If we have a big block of data read directly without a cache, all aligned reads
+ int physicallyLimitedSize = min ((position + size), m_Cacher->GetFileLength ()) - position;
+ int blocksToRead = physicallyLimitedSize / m_CacheSize;
+ if (blocksToRead > 0)
+ {
+ int curReadSize = blocksToRead * m_CacheSize;
+ m_Cacher->DirectRead ((UInt8*)data, position, curReadSize);
+ m_CachePosition += curReadSize;
+ (UInt8*&)data += curReadSize;
+ size -= curReadSize;
+ }
+
+ // Read the rest of the data from the cache!
+ while (size != 0)
+ {
+ int curReadSize = min<int> (size, m_CacheSize);
+ UpdateReadCache (data, curReadSize);
+ (UInt8*&)data += curReadSize;
+ size -= curReadSize;
+ }
+ }
+}
+
+void CachedReader::Skip (int size)
+{
+ if (m_CachePosition + size <= m_CacheEnd)
+ {
+ m_CachePosition += size;
+ }
+ else
+ {
+ int position = GetPosition ();
+ SetPosition(position + size);
+ }
+}
+
+void memcpy_constrained_src (void* dst, const void* src, int size, const void* srcFrom, void* srcTo);
+void memcpy_constrained_dst (void* dst, const void* src, int size, const void* dstFrom, void* dstTo);
+
+void memcpy_constrained_src (void* dst, const void* src, int size, const void* srcFrom, void* srcTo)
+{
+ UInt8* fromClamped = clamp ((UInt8*)src, (UInt8*)srcFrom, (UInt8*)srcTo);
+ UInt8* toClamped = clamp ((UInt8*)src + size, (UInt8*)srcFrom, (UInt8*)srcTo);
+
+ int offset = fromClamped - (UInt8*)src;
+ size = toClamped - fromClamped;
+ memcpy ((UInt8*)dst + offset, (UInt8*)src + offset, size);
+}
+
+void memcpy_constrained_dst (void* dst, const void* src, int size, const void* dstFrom, void* dstTo)
+{
+ UInt8* fromClamped = clamp ((UInt8*)dst, (UInt8*)dstFrom, (UInt8*)dstTo);
+ UInt8* toClamped = clamp ((UInt8*)dst + size, (UInt8*)dstFrom, (UInt8*)dstTo);
+
+ int offset = fromClamped - (UInt8*)dst;
+ size = toClamped - fromClamped;
+ memcpy ((UInt8*)dst + offset, (UInt8*)src + offset, size);
+}
+
+void CachedReader::UpdateReadCache (void* data, size_t size)
+{
+ AssertIf (m_Cacher == NULL);
+ AssertIf (size > m_CacheSize);
+
+ size_t position = GetPosition ();
+ OutOfBoundsError(position, size);
+
+ if (m_OutOfBoundsRead)
+ {
+ memset(data, 0, size);
+ return;
+ }
+
+ // copy data oldblock
+ SetPosition (position);
+ memcpy_constrained_src (data, m_CachePosition, size, m_CacheStart, m_CacheEnd);
+
+ // Read next cache block only if we actually need it.
+ if (m_CachePosition + size > m_CacheEnd)
+ {
+ // Check if the cache block
+ // copy data new block
+ SetPosition (position + size);
+ UInt8* cachePosition = position - m_Block * m_CacheSize + m_CacheStart;
+ memcpy_constrained_src (data, cachePosition, size, m_CacheStart, m_CacheEnd);
+ }
+ else
+ {
+ m_CachePosition += size;
+ }
+}
+
+std::string GetNicePath (const CacheReaderBase& cacher)
+{
+ #if UNITY_EDITOR
+ string path = cacher.GetPathName();
+ string assetPath = AssetPathNameFromAnySerializedPath(GetProjectRelativePath(cacher.GetPathName()));
+
+ if (!assetPath.empty())
+ return cacher.GetPathName() + "\' - \'" + assetPath;
+ else
+ return cacher.GetPathName();
+ #else
+ return cacher.GetPathName();
+ #endif
+}
+
+void CachedReader::OutOfBoundsError (size_t position, size_t size)
+{
+ if (m_OutOfBoundsRead)
+ return;
+
+ #define ERROR_FMT "The file \'%s\' is corrupted! Remove it and launch unity again!\n" \
+ "[Position out of bounds! %" PRINTF_SIZET_FORMAT " %s %" PRINTF_SIZET_FORMAT "]"
+
+ if (position + size > m_Cacher->GetFileLength ())
+ {
+ OUTPUT_FIELDS_OF_LAST_SERIALIZABLE;
+ FatalErrorMsg (ERROR_FMT, GetNicePath(*m_Cacher).c_str(), position + size, ">", m_Cacher->GetFileLength ());
+ m_OutOfBoundsRead = true;
+ }
+
+ if (position + size > m_MaximumPosition)
+ {
+ OUTPUT_FIELDS_OF_LAST_SERIALIZABLE;
+ FatalErrorMsg (ERROR_FMT, GetNicePath(*m_Cacher).c_str(), position + size, ">", m_MaximumPosition);
+ m_OutOfBoundsRead = true;
+ }
+
+ if (position < m_MinimumPosition)
+ {
+ OUTPUT_FIELDS_OF_LAST_SERIALIZABLE;
+ FatalErrorMsg (ERROR_FMT, GetNicePath(*m_Cacher).c_str(), position + size, "<", m_MinimumPosition);
+ m_OutOfBoundsRead = true;
+ }
+}
+
+void CachedReader::SetPosition (size_t position)
+{
+ OutOfBoundsError(position, 0);
+ if (m_OutOfBoundsRead)
+ return;
+
+ if (position / m_CacheSize != m_Block)
+ {
+ m_Cacher->UnlockCacheBlock (m_Block);
+ m_Block = position / m_CacheSize;
+ m_Cacher->LockCacheBlock (m_Block, &m_CacheStart, &m_CacheEnd);
+ }
+ m_CachePosition = position - m_Block * m_CacheSize + m_CacheStart;
+}
+
+void CachedReader::Align4Read ()
+{
+ UInt32 offset = m_CachePosition - m_CacheStart;
+ offset = ((offset + 3) >> 2) << 2;
+ m_CachePosition = m_CacheStart + offset;
+}
+
+
+//////
+
+void CachedWriter::InitActiveWriter (ActiveWriter& activeWriter, CacheWriterBase& cacher)
+{
+ Assert (activeWriter.block == -1);
+ Assert (&cacher != NULL);
+
+ activeWriter.cacheBase = &cacher;
+ activeWriter.block = 0;
+ activeWriter.cacheBase->LockCacheBlock (activeWriter.block, &activeWriter.cacheStart, &activeWriter.cacheEnd);
+ activeWriter.cachePosition = activeWriter.cacheStart;
+}
+
+void CachedWriter::Align4Write ()
+{
+ UInt32 leftOver = Align4LeftOver (m_ActiveWriter.cachePosition - m_ActiveWriter.cacheStart);
+ UInt8 value = 0;
+ for (UInt32 i=0;i<leftOver;i++)
+ Write(value);
+}
+
+void CachedWriter::Write (const void* data, size_t size)
+{
+ if (m_ActiveWriter.cachePosition + size < m_ActiveWriter.cacheEnd)
+ {
+ memcpy (m_ActiveWriter.cachePosition, data, size);
+ m_ActiveWriter.cachePosition += size;
+ }
+ else
+ {
+ while (size != 0)
+ {
+ size_t curWriteSize = min (size, m_ActiveWriter.cacheBase->GetCacheSize());
+ UpdateWriteCache (data, curWriteSize);
+ (UInt8*&)data += curWriteSize;
+ size -= curWriteSize;
+ }
+ }
+}
+
+void CachedWriter::UpdateWriteCache (const void* data, size_t size)
+{
+ Assert (m_ActiveWriter.cacheBase != NULL);
+ AssertIf (size > m_ActiveWriter.cacheBase->GetCacheSize());
+
+ size_t position = GetPosition ();
+ size_t cacheSize = m_ActiveWriter.cacheBase->GetCacheSize();
+ // copy data from oldblock
+ memcpy_constrained_dst (m_ActiveWriter.cachePosition, data, size, m_ActiveWriter.cacheStart, m_ActiveWriter.cacheEnd);
+
+ SetPosition (position + size);
+
+ // copy data new block
+ UInt8* cachePosition = position - m_ActiveWriter.block * cacheSize + m_ActiveWriter.cacheStart;
+ memcpy_constrained_dst (cachePosition, data, size, m_ActiveWriter.cacheStart, m_ActiveWriter.cacheEnd);
+}
+
+void CachedWriter::SetPosition (size_t position)
+{
+ size_t cacheSize = m_ActiveWriter.cacheBase->GetCacheSize();
+ int newBlock = position / cacheSize;
+ if (newBlock != m_ActiveWriter.block)
+ {
+ Assert(newBlock == m_ActiveWriter.block + 1);
+
+ m_ActiveWriter.cacheBase->UnlockCacheBlock (m_ActiveWriter.block);
+ m_ActiveWriter.block = newBlock;
+ m_ActiveWriter.cacheBase->LockCacheBlock (m_ActiveWriter.block, &m_ActiveWriter.cacheStart, &m_ActiveWriter.cacheEnd);
+ }
+ m_ActiveWriter.cachePosition = position - m_ActiveWriter.block * cacheSize + m_ActiveWriter.cacheStart;
+}
+
+size_t CachedWriter::GetPosition () const
+{
+ return m_ActiveWriter.GetPosition();
+}
+
+
+bool CachedWriter::CompleteWriting ()
+{
+ m_ActiveWriter.cacheBase->UnlockCacheBlock (m_ActiveWriter.block);
+
+ bool success = m_ActiveWriter.cacheBase->CompleteWriting (m_ActiveWriter.GetPosition());
+
+ #if UNITY_EDITOR
+ if (m_ActiveResourceImageMode != kResourceImageNotSupported)
+ {
+ for (int i=0;i<kNbResourceImages;i++)
+ {
+ success &= m_ResourceImageWriters[i].cacheBase->CompleteWriting (m_ResourceImageWriters[i].GetPosition());
+ success &= m_ResourceImageWriters[i].cacheBase->WriteHeaderAndCloseFile (NULL, 0, 0);
+ }
+ }
+ #endif
+
+ return success;
+}
+
+size_t CachedWriter::ActiveWriter::GetPosition () const
+{
+ return cachePosition - cacheStart + block * cacheBase->GetCacheSize();
+}
+
+#if UNITY_EDITOR
+
+void CachedWriter::InitWrite (CacheWriterBase& cacher)
+{
+ InitActiveWriter(m_ActiveWriter, cacher);
+ m_DefaultWriter = m_ActiveWriter;
+ m_ActiveResourceImageMode = kResourceImageNotSupported;
+}
+
+void CachedWriter::InitResourceImage (ActiveResourceImage index, CacheWriterBase& resourceImage)
+{
+ m_ActiveResourceImageMode = kResourceImageInactive;
+ InitActiveWriter(m_ResourceImageWriters[index], resourceImage);
+}
+
+void CachedWriter::BeginResourceImage (ActiveResourceImage resourceImage)
+{
+ if (m_ActiveResourceImageMode == kResourceImageNotSupported)
+ return;
+
+ Assert(m_ActiveResourceImageMode == kResourceImageInactive);
+ Assert(resourceImage > kResourceImageInactive);
+
+ m_ActiveResourceImageMode = resourceImage;
+
+ m_DefaultWriter = m_ActiveWriter;
+ m_ActiveWriter = m_ResourceImageWriters[m_ActiveResourceImageMode];
+ Assert(m_ActiveWriter.cacheBase != NULL);
+}
+
+void CachedWriter::EndResourceImage ()
+{
+ Assert(IsWritingResourceImage());
+
+ m_ResourceImageWriters[m_ActiveResourceImageMode] = m_ActiveWriter;
+ m_ActiveWriter = m_DefaultWriter;
+
+ m_ActiveResourceImageMode = kResourceImageInactive;
+}
+
+#else
+
+void CachedWriter::InitWrite (CacheWriterBase& cacher)
+{
+ InitActiveWriter(m_ActiveWriter, cacher);
+}
+
+#endif
diff --git a/Runtime/Serialize/CacheWrap.h b/Runtime/Serialize/CacheWrap.h
new file mode 100644
index 0000000..6ded041
--- /dev/null
+++ b/Runtime/Serialize/CacheWrap.h
@@ -0,0 +1,229 @@
+#ifndef CACHEWRAP_H
+#define CACHEWRAP_H
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Serialize/FileCache.h"
+#include "Runtime/Serialize/SerializationMetaFlags.h"
+
+class CachedWriter
+{
+ struct ActiveWriter
+ {
+ UInt8* cachePosition;
+ UInt8* cacheStart;
+ UInt8* cacheEnd;
+ SInt32 block;
+ CacheWriterBase* cacheBase;
+
+ ActiveWriter () { cachePosition = NULL; cacheStart = NULL; cacheEnd = NULL; block = -1; cacheBase = NULL; }
+ size_t GetPosition () const;
+ };
+
+ ActiveWriter m_ActiveWriter;
+
+ #if UNITY_EDITOR
+
+ ActiveResourceImage m_ActiveResourceImageMode;
+
+ ActiveWriter m_DefaultWriter;
+ ActiveWriter m_ResourceImageWriters[kNbResourceImages];
+
+ #endif
+
+ static void InitActiveWriter (CachedWriter::ActiveWriter& activeWriter, CacheWriterBase& cacher);
+ void SetPosition (size_t position);
+ void EXPORT_COREMODULE UpdateWriteCache (const void* data, size_t size);
+
+ public:
+
+ void InitWrite (CacheWriterBase& cacher);
+
+ bool CompleteWriting ();
+
+
+#if UNITY_EDITOR
+ void InitResourceImage (ActiveResourceImage index, CacheWriterBase& resourceImage);
+
+ void BeginResourceImage (ActiveResourceImage resourceImageType);
+ void EndResourceImage ();
+ bool IsWritingResourceImage () { return m_ActiveResourceImageMode > kResourceImageInactive; }
+
+ CacheWriterBase& GetCacheBase () { return *m_ActiveWriter.cacheBase; }
+
+#endif
+
+ template<class T>
+ void Write (const T& data)
+ {
+#if CHECK_SERIALIZE_ALIGNMENT
+ if (m_CheckSerializeAlignment)
+ {
+ SInt32 position = reinterpret_cast<SInt32>(m_ActiveWriter.cachePosition);
+ SInt32 size = sizeof(T);
+ SInt32 align = position % size;
+ if (align != 0)
+ {
+ ErrorString("Alignment error ");
+ }
+ }
+#endif
+
+ if (m_ActiveWriter.cachePosition + sizeof (T) < m_ActiveWriter.cacheEnd)
+ {
+ *reinterpret_cast<T*> (m_ActiveWriter.cachePosition) = data;
+ m_ActiveWriter.cachePosition += sizeof (T);
+ }
+ else
+ UpdateWriteCache (&data, sizeof (data));
+ }
+
+ void Align4Write ();
+
+ void Write (const void* data, size_t size);
+
+ size_t GetPosition () const;
+};
+
+struct StreamingInfo
+{
+ size_t offset;
+ size_t size;
+ std::string path;
+
+ bool IsValid () const { return !path.empty(); }
+
+ StreamingInfo () { offset = 0; size = 0; }
+};
+
+
+struct ResourceImage
+{
+ UInt8* m_Data;
+ UInt32 m_Size;
+ std::string m_StreamingPath;
+
+ public:
+
+ ResourceImage (const std::string& path, bool stream);
+ ~ResourceImage ();
+
+ UInt8* Fetch (size_t offset, size_t size)
+ {
+ Assert(m_Data != NULL);
+ Assert(size + offset <= m_Size);
+ return m_Data + offset;
+ }
+
+ const std::string& GetStreamingPath () { Assert(!m_StreamingPath.empty()); return m_StreamingPath; }
+};
+
+struct EXPORT_COREMODULE ResourceImageGroup
+{
+ ResourceImage* resourceImages[kNbResourceImages];
+
+ ResourceImageGroup () { memset(this, 0, sizeof(ResourceImageGroup)); }
+};
+
+class EXPORT_COREMODULE CachedReader
+{
+ private:
+
+ UInt8* m_CachePosition;
+ UInt8* m_CacheStart;
+ UInt8* m_CacheEnd;
+ CacheReaderBase* m_Cacher;
+ SInt32 m_Block;
+ size_t m_CacheSize;
+ size_t m_MinimumPosition;
+ size_t m_MaximumPosition;
+ bool m_OutOfBoundsRead;
+
+ ResourceImage* m_ActiveResourceImage;
+ ResourceImageGroup m_ResourceImageGroup;
+
+ void UpdateReadCache (void* data, size_t size);
+
+ CachedReader (const CachedReader& c);// undefined
+ CachedReader& operator = (const CachedReader& c);// undefined
+
+ void OutOfBoundsError (size_t position, size_t size);
+ void LockCacheBlockBounded();
+
+ public:
+
+ CachedReader ();
+ ~CachedReader ();
+
+ void InitRead (CacheReaderBase& cacher, size_t position, size_t size);
+ void InitResourceImages (ResourceImageGroup& resourceImage);
+
+ size_t GetEndPosition () { return m_MaximumPosition; }
+
+ size_t End ();
+
+ template<class T>
+ void Skip ()
+ {
+ m_CachePosition += sizeof (T);
+ }
+
+ void Skip (int size);
+
+ UInt8* FetchResourceImageData (size_t offset, size_t size);
+
+ void GetStreamingInfo (size_t offset, size_t size, StreamingInfo* streamingInfo);
+
+ void BeginResourceImage (ActiveResourceImage index) { m_ActiveResourceImage = m_ResourceImageGroup.resourceImages[index]; }
+ void EndResourceImage () { m_ActiveResourceImage = NULL; }
+ bool IsReadingResourceImage () { return m_ActiveResourceImage != NULL; }
+ const char* GetSerializedFilePathName() { return m_Cacher->GetPathName().c_str(); }
+
+ template<class T>
+ void Read (T& data, size_t position)
+ {
+ m_CachePosition = m_CacheStart + position - m_Block * m_CacheSize;
+ if (m_CachePosition >= m_CacheStart && m_CachePosition + sizeof (data) <= m_CacheEnd)
+ {
+ data = *reinterpret_cast<T*> (m_CachePosition);
+ m_CachePosition += sizeof (T);
+ }
+ else
+ UpdateReadCache (&data, sizeof (data));
+ }
+
+ template<class T>
+ void Read (T& data)
+ {
+ if (m_CachePosition + sizeof (T) <= m_CacheEnd)
+ {
+ data = *reinterpret_cast<T*> (m_CachePosition);
+ m_CachePosition += sizeof (T);
+ }
+ else
+ UpdateReadCache (&data, sizeof (data));
+ }
+
+ void Align4Read ();
+
+ size_t GetPosition () const { return m_CachePosition - m_CacheStart + m_Block * m_CacheSize; }
+ void SetPosition (size_t position);
+ void SetAbsoluteMemoryPosition(UInt8* position) { m_CachePosition = position; }
+ UInt8* GetAbsoluteMemoryPosition() { return m_CachePosition; }
+
+ void Read (void* data, size_t size);
+
+ CacheReaderBase* GetCacher () const { return m_Cacher; }
+};
+
+inline UInt32 Align4 (UInt32 size)
+{
+ UInt32 value = ((size + 3) >> 2) << 2;
+ return value;
+}
+
+inline UInt32 Align4LeftOver (UInt32 size)
+{
+ return Align4(size) - size;
+}
+
+#endif
diff --git a/Runtime/Serialize/DumpSerializedDataToText.cpp b/Runtime/Serialize/DumpSerializedDataToText.cpp
new file mode 100644
index 0000000..1be6ddf
--- /dev/null
+++ b/Runtime/Serialize/DumpSerializedDataToText.cpp
@@ -0,0 +1,372 @@
+#include "UnityPrefix.h"
+#include "DumpSerializedDataToText.h"
+#include "TypeTree.h"
+#include "CacheWrap.h"
+#include "Runtime/Utilities/GUID.h"
+
+using namespace std;
+
+#if (UNITY_EDITOR || UNITY_INCLUDE_SERIALIZATION_DUMP || DEBUGMODE)
+
+void DumpSerializedDataToText (const TypeTree& typeTree, dynamic_array<UInt8>& data)
+{
+ int offset = 0;
+ RecursiveOutput(typeTree, data.begin(), &offset, 0, cout, kDumpNormal, 0, false, -1);
+}
+#define TAB for (int t=0;t<tab;t++) os << '\t';
+
+template<class T>
+void DoSwap (T& t, bool swapBytes)
+{
+ if (swapBytes)
+ SwapEndianBytes(t);
+}
+
+
+SInt32 CalculateByteSize(const TypeTree& type) {
+ if(type.m_ByteSize != -1)
+ return type.m_ByteSize;
+
+ SInt32 r=0;
+ for (TypeTree::const_iterator i=type.begin ();i != type.end ();i++)
+ r+=CalculateByteSize(*i);
+ return r;
+}
+
+void OutputType (const TypeTree& type, ostream& os)
+{
+ os << "Name: " << type.m_Name;
+ os << " Type: " << type.m_Type;
+ os << " ByteSize: " << type.m_ByteSize;
+ os << " TypeTreePosition: " << type.m_Index;
+ os << " IsArray: " << type.m_IsArray;
+ os << " Version: " << type.m_Version;
+ os << " MetaFlag: " << type.m_MetaFlag;
+ os << " IsArray: " << type.m_IsArray;
+}
+
+string ExtractString (const TypeTree& type, const UInt8* data, int* offset, bool doSwap)
+{
+ string value;
+ SInt32 size = *reinterpret_cast<const SInt32*> (data + *offset);
+ DoSwap(size, doSwap);
+ value.reserve (size);
+ for (int i=0;i<size;i++)
+ {
+ value += data[*offset + i + sizeof(SInt32)];
+ }
+
+ *offset += sizeof(SInt32) + size;
+
+ if (type.m_MetaFlag & (kAnyChildUsesAlignBytesFlag | kAlignBytesFlag))
+ *offset = Align4(*offset);
+
+ return value;
+}
+
+string ExtractMdFour (const TypeTree& type, const UInt8* data, int* offset, bool doSwap)
+{
+ string value;
+ SInt32 size = *reinterpret_cast<const SInt32*> (data + *offset);
+ DoSwap(size, doSwap);
+ value.reserve (size*2);
+ for (int i=0;i<size;i++)
+ {
+ value += Format("%02x", data[*offset + i + sizeof(SInt32)]);
+ }
+
+ *offset += sizeof(SInt32) + size;
+
+ if (type.m_MetaFlag & (kAnyChildUsesAlignBytesFlag | kAlignBytesFlag))
+ *offset = Align4(*offset);
+
+ return value;
+}
+
+string ExtractVector (const TypeTree& type, const UInt8* data, int* offset, bool doSwap, int dimension)
+{
+ AssertIf(type.m_Father == NULL);
+ AssertIf(type.m_Children.size() != dimension);
+ AssertIf(CalculateByteSize(type) != dimension*4);
+
+ string val = "(";
+ for (int i = 0; i < dimension; ++i)
+ {
+ float v = *reinterpret_cast<const float*> (data + *offset);
+ DoSwap(v, doSwap);
+ if (i != 0)
+ val += ' ';
+ val += Format("%g", v);
+ *offset += 4;
+ }
+ val += ')';
+
+ if (type.m_MetaFlag & kAlignBytesFlag)
+ {
+ *offset = Align4(*offset);
+ }
+
+ return val;
+}
+
+string ExtractRectOffset (const TypeTree& type, const UInt8* data, int* offset, bool doSwap)
+{
+ AssertIf(type.m_Father == NULL);
+ AssertIf(type.m_Children.size() != 4);
+
+ string val = "(";
+ TypeTree::TypeTreeList::const_iterator it = type.m_Children.begin();
+ for (int i = 0; i < 4; ++i, ++it)
+ {
+ int v = *reinterpret_cast<const int*> (data + *offset);
+ DoSwap(v, doSwap);
+ if (i != 0)
+ val += ' ';
+ val += Format("%s %i", it->m_Name.c_str(), v);
+ *offset += 4;
+ }
+ val += ')';
+
+ if (type.m_MetaFlag & kAlignBytesFlag)
+ {
+ *offset = Align4(*offset);
+ }
+
+ return val;
+}
+
+
+string ExtractPPtr (const TypeTree& type, const UInt8* data, int* offset, bool doSwap)
+{
+ SInt32 fileID = *reinterpret_cast<const SInt32*>(data + *offset);
+ SInt32 pathID = *reinterpret_cast<const SInt32*>(data + *offset + 4);
+ DoSwap(fileID, doSwap);
+ DoSwap(pathID, doSwap);
+
+ if (type.m_MetaFlag & kAlignBytesFlag)
+ {
+ *offset = Align4(*offset);
+ }
+
+ *offset += 8;
+
+ return Format ("(file %i path %i)", (int)fileID, (int)pathID);
+}
+
+string ExtractGUID (const TypeTree& type, const UInt8* data, int* offset, bool doSwap)
+{
+ AssertIf(type.m_Father == NULL);
+ AssertIf(type.m_Children.size() != 4);
+ AssertIf(CalculateByteSize(type) != 4*4);
+
+ UnityGUID val;
+ for (int i = 0; i < 4; ++i) {
+ UInt32 v = *reinterpret_cast<const UInt32*> (data + *offset);
+ val.data[i]=v;
+ *offset += 4;
+ }
+
+ if (type.m_MetaFlag & kAlignBytesFlag)
+ {
+ *offset = Align4(*offset);
+ }
+
+ return GUIDToString(val);
+}
+
+
+void OutputValue (const TypeTree& type, const UInt8* data, int* offset, ostream& os, bool doSwap)
+{
+#define OUTPUT(x) \
+else if (type.m_Type == #x) \
+{ \
+x value = *reinterpret_cast<const x*> (data + *offset); \
+DoSwap(value, doSwap);\
+os << value; \
+}
+
+#define OUTPUT_INT(x) \
+else if (type.m_Type == #x) \
+{ \
+x value = *reinterpret_cast<const x*> (data + *offset); \
+DoSwap(value, doSwap);\
+int intValue = value; \
+os << intValue; \
+}
+
+
+ if (false) { }
+ OUTPUT (float)
+ OUTPUT (double)
+ OUTPUT (int)
+ OUTPUT (unsigned int)
+ OUTPUT (SInt32)
+ OUTPUT (UInt32)
+ OUTPUT (SInt16)
+ OUTPUT (UInt16)
+ OUTPUT (SInt64)
+ OUTPUT (UInt64)
+ OUTPUT_INT (SInt8)
+ OUTPUT_INT (UInt8)
+ OUTPUT (char)
+ OUTPUT (bool)
+ else
+ {
+ AssertString ("Unsupported type! " + type.m_Type);
+ }
+ *offset = *offset + type.m_ByteSize;
+}
+
+const int kArrayMemberColumns = 25;
+
+void RecursiveOutput (const TypeTree& type, const UInt8* data, int* offset, int tab, ostream& os, DumpOutputMode mode, int pathID, bool doSwap, int arrayIndex)
+{
+ if (type.m_Type == "Vector3f" && type.m_ByteSize != 12)
+ {
+ AssertString ("Unsupported type! " + type.m_Type);
+ }
+
+ if (!type.m_IsArray && arrayIndex == -1 && mode != kDumpClean)
+ os << Format("% 5d: ", (int)type.m_Index);
+
+ if (type.IsBasicDataType ())
+ {
+ // basic data type
+ if (arrayIndex == -1)
+ {
+ TAB os << type.m_Name << " ";
+ OutputValue (type, data, offset, os, doSwap);
+ os << " (" << type.m_Type << ")";
+ os << endl;
+ }
+ else
+ {
+ // array members: multiple members per line
+ if (arrayIndex % kArrayMemberColumns == 0)
+ {
+ if (arrayIndex != 0)
+ os << endl;
+ TAB os << type.m_Name << " (" << type.m_Type << ") #" << arrayIndex << ": ";
+ OutputValue (type, data, offset, os, doSwap);
+ }
+ else
+ {
+ os << ' ';
+ OutputValue (type, data, offset, os, doSwap);
+ }
+ }
+ }
+ else if (type.m_IsArray)
+ {
+ // Extract and Print size
+ int size = *reinterpret_cast<const SInt32*> (data + *offset);
+ DoSwap(size, doSwap);
+
+ RecursiveOutput (type.m_Children.front (), data, offset, tab, os, mode, 0, doSwap, -1);
+ // Print children
+ for (int i=0;i<size;i++)
+ {
+ // char buffy[64]; sprintf (buffy, "%s[%d]", type.m_Name.c_str (), i);
+ RecursiveOutput (type.m_Children.back (), data, offset, tab, os, mode, 0, doSwap, i);
+ }
+ os << endl;
+ }
+ else if (type.m_Type == "string")
+ {
+ TAB os << type.m_Name << " ";
+ os << "\""<< ExtractString (type, data, offset, doSwap) << "\"";
+
+ os << " (" << type.m_Type << ")" << endl;
+ }
+ else if (type.m_Type == "Vector4f" && type.m_ByteSize == 16)
+ {
+ TAB os << type.m_Name << " ";
+ os << ExtractVector (type, data, offset, doSwap, 4);
+ os << " (" << type.m_Type << ")" << endl;
+ }
+ else if (type.m_Type == "Vector3f" && type.m_ByteSize == 12)
+ {
+ TAB os << type.m_Name << " ";
+ os << ExtractVector (type, data, offset, doSwap, 3);
+ os << " (" << type.m_Type << ")" << endl;
+ }
+ else if (type.m_Type == "Vector2f" && type.m_ByteSize == 8)
+ {
+ TAB os << type.m_Name << " ";
+ os << ExtractVector (type, data, offset, doSwap, 2);
+ os << " (" << type.m_Type << ")" << endl;
+ }
+ else if (type.m_Type == "ColorRGBA" && type.m_ByteSize == 16)
+ {
+ TAB os << type.m_Name << " ";
+ os << ExtractVector (type, data, offset, doSwap, 4);
+ os << " (" << type.m_Type << ")" << endl;
+ }
+ else if (type.m_Type == "FastPropertyName" && type.m_Children.size()==1 && type.m_Children.front().m_Type=="string")
+ {
+ TAB os << type.m_Name << " ";
+ os << "\""<< ExtractString (type, data, offset, doSwap) << "\"";
+ os << " (" << type.m_Type << ")" << endl;
+ if (type.m_MetaFlag & kAlignBytesFlag)
+ {
+ *offset = Align4(*offset);
+ }
+ }
+ else if (type.m_Type == "RectOffset" && type.m_ByteSize == 16)
+ {
+ TAB os << type.m_Name << " ";
+ os << ExtractRectOffset (type, data, offset, doSwap);
+ os << " (" << type.m_Type << ")" << endl;
+ }
+ else if (BeginsWith(type.m_Type, "PPtr<") && type.m_ByteSize == 8)
+ {
+ TAB os << type.m_Name << " ";
+ os << ExtractPPtr (type, data, offset, doSwap);
+ os << " (" << type.m_Type << ")" << endl;
+ }
+ else if (type.m_Type == "GUID")
+ {
+ TAB os << type.m_Name << " ";
+ os << ExtractGUID (type, data, offset, doSwap);
+ os << " (" << type.m_Type << ")" << endl;
+ }
+ else if (type.m_Type == "MdFour")
+ {
+ TAB os << type.m_Name << " ";
+ os << ExtractMdFour (type, data, offset, doSwap);
+ os << " (" << type.m_Type << ")" << endl;
+ }
+ else
+ {
+ TAB
+ if (type.m_Father != NULL)
+ {
+ os << type.m_Name << " ";
+ os << " (" << type.m_Type << ")" ;
+ }
+ else
+ {
+ os << type.m_Type ;
+ }
+ if (mode != kDumpClean)
+ {
+ if (pathID == 0)
+ os << Format(" [size: %d, children: %d]", (int)CalculateByteSize(type), (int)type.m_Children.size());
+ else
+ os << Format(" [size: %d, children: %d pathID: %d]", (int)CalculateByteSize(type), (int)type.m_Children.size(), pathID);
+ }
+ os << endl;
+
+ tab++;
+ for (TypeTree::const_iterator i=type.begin ();i != type.end ();i++)
+ RecursiveOutput (*i, data, offset, tab, os, mode, 0, doSwap, -1);
+ tab--;
+ }
+
+ if (type.m_MetaFlag & kAlignBytesFlag)
+ {
+ *offset = Align4(*offset);
+ }
+}
+
+#endif // UNITY_EDITOR || UNITY_INCLUDE_SERIALIZATION_DUMP
diff --git a/Runtime/Serialize/DumpSerializedDataToText.h b/Runtime/Serialize/DumpSerializedDataToText.h
new file mode 100644
index 0000000..f78cb81
--- /dev/null
+++ b/Runtime/Serialize/DumpSerializedDataToText.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#ifndef UNITY_INCLUDE_SERIALIZATION_DUMP
+#define UNITY_INCLUDE_SERIALIZATION_DUMP 0
+#endif
+
+// Debugging functions that dump the state of an object or typetree/bytearray to stdout
+// Used in BinaryToTextFile by defining UNITY_INCLUDE_SERIALIZATION_DUMP
+#if UNITY_EDITOR || UNITY_INCLUDE_SERIALIZATION_DUMP || DEBUGMODE
+#include <iostream>
+#include "Runtime/Utilities/dynamic_array.h"
+
+class TypeTree;
+
+enum DumpOutputMode
+{
+ kDumpNormal,
+ kDumpClean,
+};
+
+void DumpSerializedDataToText (const TypeTree& typeTree, dynamic_array<UInt8>& data);
+void RecursiveOutput (const TypeTree& type, const UInt8* data, int* offset, int tab, std::ostream& os, DumpOutputMode mode, int pathID, bool doSwap, int arrayIndex);
+#endif
+
diff --git a/Runtime/Serialize/FileCache.cpp b/Runtime/Serialize/FileCache.cpp
new file mode 100644
index 0000000..c297ed6
--- /dev/null
+++ b/Runtime/Serialize/FileCache.cpp
@@ -0,0 +1,461 @@
+#include "UnityPrefix.h"
+#include "FileCache.h"
+#include "Runtime/Utilities/algorithm_utility.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+
+#if SUPPORT_SERIALIZE_WRITE
+#include "Runtime/Utilities/FileUtilities.h"
+#endif
+
+using namespace std;
+
+#define USE_OPEN_FILE_CACHE UNITY_EDITOR
+
+#if USE_OPEN_FILE_CACHE
+
+struct OpenFilesCache
+{
+ enum { kOpenedFileCacheCount = 5 };
+ File* m_Cache[kOpenedFileCacheCount];
+ UInt32 m_TimeStamps[kOpenedFileCacheCount];
+ UInt32 m_TimeStamp;
+
+ OpenFilesCache ()
+ {
+ m_TimeStamp = 0;
+ for (int i=0;i<kOpenedFileCacheCount;i++)
+ {
+ m_Cache[i] = NULL;
+ m_TimeStamps[i] = 0;
+ }
+ }
+
+ void OpenCached (File* theFile, const std::string& thePath)
+ {
+ m_TimeStamp++;
+
+ // find cache, don't do anything if we are in the cache
+ for (int i=0;i<kOpenedFileCacheCount;i++)
+ {
+ if (theFile == m_Cache[i])
+ {
+ m_TimeStamps[i] = m_TimeStamp;
+ return;
+ }
+ }
+
+ // Find Least recently used cache entry
+ UInt32 lruTimeStamp = m_TimeStamps[0];
+ int lruIndex = 0;
+ for (int i=1;i<kOpenedFileCacheCount;i++)
+ {
+ if (m_TimeStamps[i] < lruTimeStamp)
+ {
+ lruTimeStamp = m_TimeStamps[i];
+ lruIndex = i;
+ }
+ }
+
+ // replace the least recently used cache entry
+ if (m_Cache[lruIndex] != NULL)
+ {
+ #if UNITY_OSX
+ m_Cache[lruIndex]->Lock (File::kNone, false);
+ #endif
+ m_Cache[lruIndex]->Close ();
+ }
+
+ m_Cache[lruIndex] = theFile;
+ m_TimeStamps[lruIndex] = m_TimeStamp;
+
+ if(!theFile->Open (thePath, File::kReadPermission, File::kSilentReturnOnOpenFail))
+ ErrorString(Format("Could not open file %s for read", thePath.c_str()));
+
+ #if UNITY_OSX
+ theFile->Lock (File::kShared, false);
+ #endif
+ }
+
+ void ForceCloseAll ()
+ {
+ // Find and close cache
+ for (int i=0;i<kOpenedFileCacheCount;i++)
+ {
+ if (m_Cache[i] != NULL)
+ ForceClose (m_Cache[i]);
+ }
+ }
+
+ void ForceClose (File* cachable)
+ {
+ // Find and close cache
+ for (int i=0;i<kOpenedFileCacheCount;i++)
+ {
+ if (m_Cache[i] == cachable)
+ {
+ #if UNITY_OSX
+ m_Cache[i]->Lock (File::kNone, false);
+ #endif
+ m_Cache[i]->Close ();
+ m_Cache[i] = NULL;
+ m_TimeStamps[i] = 0;
+ return;
+ }
+ }
+ }
+};
+OpenFilesCache gOpenFilesCache;
+
+void ForceCloseAllOpenFileCaches ()
+{
+ gOpenFilesCache.ForceCloseAll ();
+}
+
+#endif
+
+
+CacheReaderBase::~CacheReaderBase ()
+{}
+
+CacheWriterBase::~CacheWriterBase ()
+{}
+
+FileCacherRead::FileCacherRead (const string& pathName, size_t cacheSize, size_t cacheCount)
+{
+ m_RootHeader = GET_CURRENT_ALLOC_ROOT_HEADER();
+ m_Path = PathToAbsolutePath(pathName);
+
+ // Initialize Cache
+ m_MaxCacheCount = cacheCount;
+ m_CacheSize = cacheSize;
+ m_TimeStamp = 0;
+
+ // Get File size
+ m_FileSize = ::GetFileLength(m_Path);
+
+ #if !USE_OPEN_FILE_CACHE
+ if(!m_File.Open(m_Path, File::kReadPermission, File::kSilentReturnOnOpenFail))
+ ErrorString(Format("Could not open file %s for read", m_Path.c_str()));
+
+ // Make the file non-overwritable by the cache (to make caching behaviour imitate windows)
+ #if UNITY_OSX
+ m_File.Lock (File::kShared, false);
+ #endif
+
+ #endif
+ #if DEBUG_LINEAR_FILE_ACCESS
+ m_LastFileAccessPosition = 0;
+ #endif
+}
+
+FileCacherRead::~FileCacherRead ()
+{
+ for (CacheBlocks::iterator i = m_CacheBlocks.begin ();i != m_CacheBlocks.end ();++i)
+ {
+ AssertIf (i->second.lockCount);
+ UNITY_FREE(kMemFile,i->second.data);
+ }
+
+ m_CacheBlocks.clear ();
+
+ #if USE_OPEN_FILE_CACHE
+ gOpenFilesCache.ForceClose (&m_File);
+ #else
+ // Make the file overwritable by the cache.
+ #if UNITY_OSX
+ m_File.Lock (File::kNone, false);
+ #endif
+
+ m_File.Close();
+
+ #endif
+}
+
+void FileCacherRead::ReadCacheBlock (CacheBlock& cacheBlock)
+{
+ int block = cacheBlock.block;
+ // Watch out for not reading over eof
+ int readSize = min<int> (m_CacheSize, m_FileSize - block * m_CacheSize);
+
+ // load the data from disk
+ // only if the physical file contains any data for this block
+ if (readSize > 0)
+ {
+ #if USE_OPEN_FILE_CACHE
+ gOpenFilesCache.OpenCached (&m_File, m_Path);
+ #endif
+
+ #if DEBUG_LINEAR_FILE_ACCESS
+ size_t position = block * m_CacheSize;
+ #endif
+
+ m_File.Read (block * m_CacheSize, cacheBlock.data, readSize);
+
+ #if DEBUG_LINEAR_FILE_ACCESS
+ // printf_console("ACCESS: [%08x] %s %i bytes @ %i to %08x\n",this, __FUNCTION__, readSize, block * m_CacheSize, cacheBlock.data);
+ std::string fileName = GetLastPathNameComponent(m_Path);
+ if (position < m_LastFileAccessPosition && fileName != "mainData" && fileName != "unity default resources")
+ {
+ ErrorString(Format("File access: %s is not linear Reading: %d Seek position: %d", fileName.c_str(), position, m_LastFileAccessPosition));
+ }
+
+ m_LastFileAccessPosition = position + readSize;
+ #endif
+ }
+}
+
+void FileCacherRead::DirectRead (void* data, size_t position, size_t size)
+{
+ // load the data from disk
+ // only if the physical file contains any data for this block
+ FatalErrorIf (m_FileSize - position < size);
+
+ #if USE_OPEN_FILE_CACHE
+ gOpenFilesCache.OpenCached (&m_File, m_Path);
+ #endif
+
+ m_File.Read (position, data, size);
+
+ #if DEBUG_LINEAR_FILE_ACCESS
+ // printf_console("ACCCESS: [%08x] %s %i bytes @ %i to %08x\n",this, __FUNCTION__, size, position, data);
+ std::string fileName = GetLastPathNameComponent(m_Path);
+ if (position < m_LastFileAccessPosition && fileName != "mainData" && fileName != "unity default resources")
+ {
+ ErrorString(Format("File access: %s is not linear Reading: %d Seek position: %d", fileName.c_str(), position, m_LastFileAccessPosition));
+ }
+
+ m_LastFileAccessPosition = position + size;
+ #endif
+}
+
+
+bool FileCacherRead::FreeSingleCache ()
+{
+ unsigned lowestTimeStamp = -1;
+ CacheBlocks::iterator lowestCacheBlock = m_CacheBlocks.end ();
+
+ CacheBlocks::iterator i;
+ for (i=m_CacheBlocks.begin ();i != m_CacheBlocks.end ();i++)
+ {
+ if (i->second.lockCount == 0)
+ {
+ if (i->second.timeStamp < lowestTimeStamp)
+ {
+ lowestTimeStamp = i->second.timeStamp;
+ lowestCacheBlock = i;
+ }
+ }
+ }
+
+ if (lowestCacheBlock != m_CacheBlocks.end ())
+ {
+ CacheBlock& block = lowestCacheBlock->second;
+ UNITY_FREE(kMemFile,block.data);
+ m_CacheBlocks.erase (lowestCacheBlock);
+ return true;
+ }
+ else
+ return false;
+}
+
+FileCacherRead::CacheBlock& FileCacherRead::AllocateCacheBlock (int block)
+{
+ AssertIf (m_CacheBlocks.count (block));
+ CacheBlock cacheBlock;
+ cacheBlock.block = block;
+ cacheBlock.lockCount = 0;
+ cacheBlock.timeStamp = 0;
+ cacheBlock.data = (UInt8*)UNITY_MALLOC(MemLabelId(kMemFileId, m_RootHeader),m_CacheSize);
+ m_CacheBlocks[block] = cacheBlock;
+ return m_CacheBlocks[block];
+}
+
+void FileCacherRead::LockCacheBlock (int block, UInt8** startPos, UInt8** endPos)
+{
+ CacheBlock* newCacheBlock;
+ CacheBlocks::iterator i = m_CacheBlocks.find (block);
+ if (i != m_CacheBlocks.end ())
+ newCacheBlock = &i->second;
+ else
+ {
+ // Make room for a new cache block
+ if (m_CacheBlocks.size () >= m_MaxCacheCount)
+ FreeSingleCache ();
+
+ newCacheBlock = &AllocateCacheBlock (block);
+ ReadCacheBlock (*newCacheBlock);
+ }
+ AssertIf (newCacheBlock->block != block);
+
+ newCacheBlock->timeStamp = ++m_TimeStamp;
+ newCacheBlock->lockCount++;
+
+ *startPos = newCacheBlock->data;
+ *endPos = newCacheBlock->data + min<int> (m_FileSize - block * GetCacheSize (), GetCacheSize ());
+}
+
+void FileCacherRead::UnlockCacheBlock (int block)
+{
+ AssertIf (!m_CacheBlocks.count (block));
+ CacheBlock& cacheBlock = m_CacheBlocks.find (block)->second;
+ cacheBlock.lockCount--;
+ if (cacheBlock.lockCount == 0)
+ {
+ // LockCacheBlock sometimes locks more caches than m_MaxCacheCount
+ // Thus we should Free them as soon as they become unlocked
+ if (m_CacheBlocks.size () > m_MaxCacheCount)
+ FreeSingleCache ();
+ }
+}
+
+std::string FileCacherRead::GetPathName() const
+{
+ return m_Path;
+}
+
+#if SUPPORT_SERIALIZE_WRITE
+FileCacherWrite::FileCacherWrite ()
+{
+ m_Success = true;
+ m_Block = -1;
+ m_Locked = false;
+ m_CacheSize = 0;
+ m_DataCache = NULL;
+}
+
+void FileCacherWrite::InitWriteFile (const std::string& pathName, size_t cacheSize)
+{
+ m_Path = PathToAbsolutePath(pathName);
+ m_Success = true;
+
+ m_File.Open(m_Path, File::kWritePermission);
+ // file we're writing to always is a temporary, non-indexable file
+ SetFileFlags(m_Path, kAllFileFlags, kFileFlagDontIndex|kFileFlagTemporary);
+
+ m_Block = -1;
+ m_Locked = false;
+ m_CacheSize = cacheSize;
+ m_DataCache = (UInt8*)UNITY_MALLOC (kMemFile, m_CacheSize);
+}
+
+bool FileCacherWrite::CompleteWriting (size_t size)
+{
+ Assert(m_Block != -1);
+
+ size_t remainingData = size - (m_Block * m_CacheSize);
+ Assert(remainingData <= m_CacheSize);
+
+ m_Success &= m_File.Write(m_Block * m_CacheSize, m_DataCache, remainingData);
+
+ return m_Success;
+}
+
+bool FileCacherWrite::WriteHeaderAndCloseFile (void* data, size_t position, size_t size)
+{
+ Assert(position == 0);
+ if (size != 0)
+ m_Success &= m_File.Write(position, data, size);
+ m_Success &= m_File.Close();
+
+ return m_Success;
+}
+
+
+FileCacherWrite::~FileCacherWrite()
+{
+ if (m_DataCache)
+ {
+ UNITY_FREE (kMemFile, m_DataCache);
+ m_DataCache = NULL;
+ }
+
+ m_File.Close();
+}
+
+void FileCacherWrite::LockCacheBlock (int block, UInt8** startPos, UInt8** endPos)
+{
+ AssertIf (block == -1);
+ Assert (block == m_Block || m_Block+1 == block );
+ Assert (!m_Locked);
+
+ if (m_Block != block)
+ {
+ AssertIf (m_Locked != 0);
+
+ if (m_Block != -1)
+ m_Success &= m_File.Write(m_DataCache, m_CacheSize);
+
+ m_Block = block;
+ }
+
+ *startPos = m_DataCache;
+ *endPos = m_DataCache + m_CacheSize;
+ m_Locked++;
+}
+
+void FileCacherWrite::UnlockCacheBlock (int block)
+{
+ Assert (block == m_Block);
+ Assert (m_Locked);
+
+ m_Locked = false;
+}
+
+
+std::string FileCacherWrite::GetPathName() const
+{
+ return m_Path;
+}
+
+#endif // SUPPORT_SERIALIZE_WRITE
+
+
+MemoryCacherReadBlocks::MemoryCacherReadBlocks (UInt8** blocks, int size, size_t cacheBlockSize)
+: m_CacheBlockSize(cacheBlockSize)
+, m_Memory(blocks)
+, m_FileSize(size)
+{
+}
+
+
+MemoryCacherReadBlocks::~MemoryCacherReadBlocks ()
+{
+}
+
+void MemoryCacherReadBlocks::LockCacheBlock (int block, UInt8** startPos, UInt8** endPos)
+{
+ /// VERIFY OUT OF BOUNDS!!! ???
+ AssertIf(block > m_FileSize / m_CacheBlockSize);
+ *startPos = m_Memory[block];
+ *endPos = *startPos + min<int> (GetFileLength () - block * m_CacheBlockSize, m_CacheBlockSize);
+}
+
+void MemoryCacherReadBlocks::DirectRead (void* data, size_t position, size_t size)
+{
+ ReadFileCache(*this, data, position, size);
+}
+
+void ReadFileCache (CacheReaderBase& cacher, void* data, size_t position, size_t size)
+{
+ UInt8 *cacheStart, *cacheEnd;
+ UInt8 *from, *fromClamped, *to, *toClamped;
+
+ int block = position / cacher.GetCacheSize ();
+ int lastBlock = (position + size - 1) / cacher.GetCacheSize ();
+
+ while (block <= lastBlock)
+ {
+ cacher.LockCacheBlock (block, &cacheStart, &cacheEnd);
+
+ // copy data from oldblock and unlock
+ from = cacheStart + (position - block * cacher.GetCacheSize ());
+ fromClamped = clamp (from, cacheStart, cacheEnd);
+ to = cacheStart + (position + size - block * cacher.GetCacheSize ());
+ toClamped = clamp<UInt8*> (to, cacheStart, cacheEnd);
+ memcpy ((UInt8*)data + (fromClamped - from), fromClamped, toClamped - fromClamped);
+
+ cacher.UnlockCacheBlock (block);
+ block++;
+ }
+}
diff --git a/Runtime/Serialize/FileCache.h b/Runtime/Serialize/FileCache.h
new file mode 100644
index 0000000..6705b44
--- /dev/null
+++ b/Runtime/Serialize/FileCache.h
@@ -0,0 +1,360 @@
+#ifndef FILECACHE_H
+#define FILECACHE_H
+
+#include "Runtime/Utilities/LogAssert.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Utilities/File.h"
+#include <list>
+#include <vector>
+#include <map>
+#include <deque>
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+#define DEBUG_LINEAR_FILE_ACCESS 0
+
+using std::max;
+using std::min;
+using std::list;
+
+class EXPORT_COREMODULE CacheReaderBase
+{
+public:
+
+ enum
+ {
+ kImmediatePriority = 0,
+ kPreloadPriority = 1,
+ kLoadPriorityCount = 2
+ };
+
+ enum
+ {
+ kUnloadPriority = 0,
+ kPreloadedPriority = 1,
+ kRequiredPriority = 2,
+ kUnloadPriorityCount = 3
+ };
+
+ virtual ~CacheReaderBase () = 0;
+
+ virtual void DirectRead (void* data, size_t position, size_t size) = 0;
+ virtual void LockCacheBlock (int block, UInt8** startPos, UInt8** endPos) = 0;
+ virtual void UnlockCacheBlock (int block) = 0;
+
+ virtual size_t GetCacheSize () const = 0;
+ virtual std::string GetPathName() const = 0;
+ virtual size_t GetFileLength () const = 0;
+ virtual UInt8* GetAddressOfMemory() { ErrorString("GetAddressOfMemory called on CacheReaderBase which does not support it"); return NULL; }
+};
+
+class CacheWriterBase
+{
+public:
+ virtual ~CacheWriterBase () = 0;
+
+ virtual bool CompleteWriting (size_t size) = 0;
+ virtual bool WriteHeaderAndCloseFile (void* /*data*/, size_t /*position*/, size_t /*size*/) { AssertString("Only used for writing serialized files"); return false; }
+
+ virtual void LockCacheBlock (int block, UInt8** startPos, UInt8** endPos) = 0;
+ virtual void UnlockCacheBlock (int block) = 0;
+
+ virtual size_t GetCacheSize () const = 0;
+ virtual std::string GetPathName() const = 0;
+ virtual size_t GetFileLength () const = 0;
+ virtual UInt8* GetAddressOfMemory() { ErrorString("GetAddressOfMemory called on CacheWriterBase which does not support it"); return NULL; }
+};
+
+class MemoryCacheReader : public CacheReaderBase
+{
+protected:
+ enum
+ {
+ kCacheSize = 256
+ };
+
+ dynamic_array<UInt8>& m_Memory;
+ SInt32 m_LockCount;
+
+public:
+ MemoryCacheReader (dynamic_array<UInt8>& mem) : m_Memory (mem), m_LockCount(0) { }
+ virtual ~MemoryCacheReader () { AssertIf (m_LockCount != 0); }
+
+ virtual void LockCacheBlock (int block, UInt8** startPos, UInt8** endPos)
+ {
+ *startPos = m_Memory.size() > block * kCacheSize ? &m_Memory[block * kCacheSize] : NULL;
+ *endPos = *startPos + min<int> (GetFileLength () - block * kCacheSize, kCacheSize);
+ m_LockCount++;
+ }
+
+ virtual void DirectRead (void* data, size_t position, size_t size)
+ {
+ AssertIf (m_Memory.size () - position < size);
+ memcpy (data, &m_Memory[position], size);
+ }
+
+ virtual void UnlockCacheBlock (int /*block*/) { m_LockCount--; }
+
+ virtual size_t GetFileLength () const { return m_Memory.size (); }
+ virtual size_t GetCacheSize () const { return kCacheSize; }
+ virtual std::string GetPathName() const { return "MemoryStream"; }
+ virtual UInt8* GetAddressOfMemory() { return &m_Memory[0]; }
+};
+
+class MemoryCacheWriter : public CacheWriterBase
+{
+protected:
+ enum
+ {
+ kCacheSize = 256
+ };
+
+ dynamic_array<UInt8>& m_Memory;
+ SInt32 m_LockCount;
+
+public:
+ MemoryCacheWriter (dynamic_array<UInt8>& mem) : m_Memory (mem), m_LockCount(0) { }
+ virtual ~MemoryCacheWriter () { AssertIf (m_LockCount != 0); }
+
+ virtual void LockCacheBlock (int block, UInt8** startPos, UInt8** endPos)
+ {
+ m_Memory.resize_uninitialized (max<int> ((block + 1) * kCacheSize, m_Memory.size ()), true);
+ *startPos = &m_Memory[block * kCacheSize];
+ *endPos = *startPos + kCacheSize;
+
+ m_LockCount++;
+ }
+
+ virtual bool CompleteWriting (size_t size) { m_Memory.resize_uninitialized (size); m_Memory.shrink_to_fit(); return true; }
+
+ virtual void UnlockCacheBlock (int /*block*/) { m_LockCount--; }
+
+ virtual size_t GetFileLength () const { return m_Memory.size (); }
+ virtual size_t GetCacheSize () const { return kCacheSize; }
+ virtual std::string GetPathName() const { return "MemoryStream"; }
+};
+
+enum
+{
+ kBlockCacherCacheSize = 256
+};
+
+class BlockMemoryCacheWriter : public CacheWriterBase
+{
+protected:
+
+ enum
+ {
+ kNumBlockReservations = 256
+ };
+
+ size_t m_Size;
+ SInt32 m_LockCount;
+ MemLabelId m_AllocLabel;
+
+ // It is possible to use the custom allocator for this index as well -- however,
+ // using the tracking linear tempory allocator is most efficient, when deallocating
+ // in the exact opposite order of allocating, which can only be guaranteed, when all allocations
+ // are in our control.
+ typedef UNITY_VECTOR(kMemFile, UInt8*) BlockVector;
+ BlockVector m_Blocks;
+
+ public:
+
+ BlockMemoryCacheWriter (MemLabelId label)
+ : m_AllocLabel(label)
+ , m_Blocks()
+ {
+ m_Blocks.reserve(kNumBlockReservations);
+ m_Size = 0;
+ m_LockCount = 0;
+ }
+
+ ~BlockMemoryCacheWriter () {
+ AssertIf (m_LockCount != 0);
+ for(BlockVector::reverse_iterator i = m_Blocks.rbegin(); i != m_Blocks.rend(); i++)
+ UNITY_FREE(m_AllocLabel, *i);
+ }
+
+ void ResizeBlocks (int newBlockSize)
+ {
+ int oldBlockSize = m_Blocks.size();
+
+ for(int block = oldBlockSize-1; block>=newBlockSize; block--)
+ UNITY_FREE(m_AllocLabel, m_Blocks[block]);
+
+ if(m_Blocks.capacity() < newBlockSize)
+ m_Blocks.reserve(m_Blocks.capacity() * 2);
+
+ m_Blocks.resize(newBlockSize);
+
+ for(int block = oldBlockSize; block<newBlockSize; block++)
+ m_Blocks[block] = (UInt8*)UNITY_MALLOC(m_AllocLabel, kBlockCacherCacheSize);
+ }
+
+ virtual void LockCacheBlock (int block, UInt8** startPos, UInt8** endPos)
+ {
+ ResizeBlocks (max<int>(block+1, m_Blocks.size()));
+ *startPos = m_Blocks[block];
+ *endPos = *startPos + kBlockCacherCacheSize;
+ m_LockCount++;
+ }
+
+ virtual bool CompleteWriting (size_t size)
+ {
+ m_Size = size;
+ ResizeBlocks(m_Size/kBlockCacherCacheSize + 1);
+ return true;
+ }
+ virtual void UnlockCacheBlock (int /*block*/) { m_LockCount--; }
+ virtual size_t GetFileLength () const { return m_Size; }
+ virtual size_t GetCacheSize () const { return kBlockCacherCacheSize; }
+ virtual std::string GetPathName() const { return "MemoryStream"; }
+
+ // Expose, so the internal data can be used with MemoryCacherReadBlocks (see below)
+ UInt8** GetCacheBlocks () { return m_Blocks.empty () ? NULL : &*m_Blocks.begin (); }
+};
+
+#if SUPPORT_SERIALIZE_WRITE
+
+/// Used by SerializedFile to write to disk.
+/// Currently it doesn't allow any seeking that is, you can only write blocks in consecutive order
+class FileCacherWrite : public CacheWriterBase
+{
+public:
+ FileCacherWrite ();
+ void InitWriteFile (const std::string& pathName, size_t cacheSize);
+
+ virtual ~FileCacherWrite ();
+
+ virtual void LockCacheBlock (int block, UInt8** startPos, UInt8** endPos);
+ virtual void UnlockCacheBlock (int block);
+
+ virtual bool WriteHeaderAndCloseFile (void* data, size_t position, size_t size);
+
+ virtual bool CompleteWriting (size_t size);
+
+ virtual size_t GetCacheSize () const { return m_CacheSize; }
+ virtual std::string GetPathName() const;
+ virtual size_t GetFileLength () const { AssertString("Dont use"); return 0; }
+
+private:
+ void WriteBlock (int block);
+
+ int m_Block;
+ UInt8* m_DataCache;
+ size_t m_CacheSize;
+
+ File m_File;
+ bool m_Success;
+ bool m_Locked;
+ std::string m_Path;
+};
+
+#endif // SUPPORT_SERIALIZE_WRITE
+
+
+/// Used by SerializedFile to read from disk
+class FileCacherRead : public CacheReaderBase
+{
+public:
+ FileCacherRead (const std::string& pathName, size_t cacheSize, size_t cacheCount);
+ ~FileCacherRead ();
+
+ virtual void LockCacheBlock (int block, UInt8** startPos, UInt8** endPos);
+ virtual void UnlockCacheBlock (int block);
+
+ virtual void DirectRead (void* data, size_t position, size_t size);
+
+ virtual size_t GetFileLength () const { return m_FileSize; }
+ virtual size_t GetCacheSize () const { return m_CacheSize; }
+ virtual std::string GetPathName() const;
+
+private:
+
+ // Finishes all reading, deletes all caches
+ void Flush ();
+
+ struct CacheBlock
+ {
+ UInt8* data;
+ int block;
+ int lockCount;
+ unsigned timeStamp;
+ };
+
+ // Reads the cacheBlock from disk.
+ void ReadCacheBlock (CacheBlock& cacheBlock);
+
+ /// Allocates an cache block at block
+ CacheBlock& AllocateCacheBlock (int block);
+
+ // Frees the cache block with the smallest timestamp that is not locked.
+ // Returns whether or not a cache block could be freed.
+ bool FreeSingleCache ();
+
+ typedef UNITY_MAP(kMemFile, int, CacheBlock) CacheBlocks;
+ CacheBlocks m_CacheBlocks;
+
+ size_t m_CacheSize;
+ size_t m_MaxCacheCount;
+ size_t m_FileSize;
+ UInt32 m_TimeStamp;
+ std::string m_Path;
+ File m_File;
+ ProfilerAllocationHeader* m_RootHeader;
+ friend struct OpenFilesCache;
+
+ #if DEBUG_LINEAR_FILE_ACCESS
+ int m_LastFileAccessPosition;
+ #endif
+
+};
+
+enum
+{
+ // Evil: Must match the in CompressedFileStreamMemory.h, UncompressedFileStreamMemory.h"
+ kCacheBlockSize = 1024 * 100
+};
+
+/// Used by SerializedFile to read from disk
+class MemoryCacherReadBlocks : public CacheReaderBase
+{
+public:
+
+ MemoryCacherReadBlocks (UInt8** blocks, int size, size_t cacheBlockSize);
+ ~MemoryCacherReadBlocks ();
+
+ virtual void LockCacheBlock (int block, UInt8** startPos, UInt8** endPos);
+ virtual void UnlockCacheBlock (int /*block*/) {}
+
+ virtual void DirectRead (void* data, size_t position, size_t size);
+
+ virtual size_t GetFileLength () const { return m_FileSize; }
+
+ virtual size_t GetCacheSize () const { return m_CacheBlockSize; }
+
+ virtual std::string GetPathName() const { return "none"; }
+
+ virtual UInt8* GetAddressOfMemory()
+ {
+ return m_Memory[0];
+ }
+
+private:
+
+ // Finishes all reading, deletes all caches
+ void Flush ();
+
+ UInt8** m_Memory;
+ size_t m_FileSize;
+ size_t m_CacheBlockSize;
+};
+
+void ReadFileCache (CacheReaderBase& cacher, void* data, size_t position, size_t size);
+#if UNITY_EDITOR
+void ForceCloseAllOpenFileCaches ();
+#endif
+
+#endif
+
diff --git a/Runtime/Serialize/FloatStringConversion.cpp b/Runtime/Serialize/FloatStringConversion.cpp
new file mode 100644
index 0000000..ce057d3
--- /dev/null
+++ b/Runtime/Serialize/FloatStringConversion.cpp
@@ -0,0 +1,80 @@
+#include "UnityPrefix.h"
+
+#if UNITY_EDITOR
+// This are the definitions of std::numeric_limits<>::max_digits10, which we cannot use
+// because it is only in the C++11 standard.
+const int kMaxFloatDigits = std::floor(std::numeric_limits<float>::digits * 3010.0/10000.0 + 2);
+const int kMaxDoubleDigits = std::floor(std::numeric_limits<double>::digits * 3010.0/10000.0 + 2);
+
+#include "External/gdtoa/gdtoa.h"
+
+bool FloatToStringAccurate (float f, char* buffer, size_t maximumSize)
+{
+ return g_ffmt (buffer, (float*)&f, kMaxFloatDigits, maximumSize) != NULL;
+}
+
+bool DoubleToStringAccurate (double f, char* buffer, size_t maximumSize)
+{
+ return g_dfmt (buffer, (double*)&f, kMaxDoubleDigits, maximumSize) != NULL;
+}
+
+bool FloatToStringAccurate (float f, UnityStr& output)
+{
+ char buf[64];
+ if (FloatToStringAccurate (f, buf, 64))
+ {
+ output = buf;
+ return true;
+ }
+ else
+ return false;
+}
+
+bool DoubleToStringAccurate (double f, UnityStr& output)
+{
+ char buf[64];
+ if (DoubleToStringAccurate (f, buf, 64))
+ {
+ output = buf;
+ return true;
+ }
+ else
+ return false;
+}
+
+float StringToFloatAccurate (const char* buffer)
+{
+ return strtof (buffer, NULL);
+}
+
+double StringToDoubleAccurate (const char* buffer)
+{
+ return strtod (buffer, NULL);
+}
+
+#if ENABLE_UNIT_TESTS
+#include "../../External/UnitTest++/src/UnitTest++.h"
+
+SUITE (FloatStringConversionTests)
+{
+TEST(FloatToStringConversion_AccurateWorks)
+{
+ // Make sure no locale is used
+ CHECK_EQUAL (0.0F, StringToFloatAccurate("0,5"));
+
+ UnityStr buf;
+
+ FloatToStringAccurate(1.0F, buf);
+ CHECK_EQUAL ("1", buf);
+
+ CHECK_EQUAL (1.0F, StringToFloatAccurate("1.0"));
+
+
+ FloatToStringAccurate(1.5F, buf);
+ CHECK_EQUAL ("1.5", buf);
+ CHECK_EQUAL (1.5F, StringToFloatAccurate("1.5"));
+}
+}
+#endif
+
+#endif
diff --git a/Runtime/Serialize/FloatStringConversion.h b/Runtime/Serialize/FloatStringConversion.h
new file mode 100644
index 0000000..c93d143
--- /dev/null
+++ b/Runtime/Serialize/FloatStringConversion.h
@@ -0,0 +1,16 @@
+#if UNITY_EDITOR
+
+/// Converts float/double to and from strings.
+
+/// Binary exact float<-> string conversion functions.
+/// Supports the full range of all float values, including nan, inf and denormalized values.
+
+bool FloatToStringAccurate (float f, char* buffer, size_t maximumSize);
+bool DoubleToStringAccurate (double f, char* buffer, size_t maximumSize);
+
+bool FloatToStringAccurate (float f, UnityStr& output);
+bool DoubleToStringAccurate (double f, UnityStr& output);
+
+float StringToFloatAccurate (const char* buffer);
+double StringToDoubleAccurate (const char* buffer);
+#endif \ No newline at end of file
diff --git a/Runtime/Serialize/IterateTypeTree.h b/Runtime/Serialize/IterateTypeTree.h
new file mode 100644
index 0000000..4532b9b
--- /dev/null
+++ b/Runtime/Serialize/IterateTypeTree.h
@@ -0,0 +1,155 @@
+#ifndef ITERATETYPETREE_H
+#define ITERATETYPETREE_H
+
+#include "TypeTree.h"
+#include "SerializeTraits.h"
+#include "TransferUtility.h"
+
+
+/* Iterate typetree is used to process serialized data in arbitrary ways.
+
+struct IterateTypeTreeFunctor
+{
+ // return true if you want to recurse into the function
+ bool operator () (const TypeTree& typeTree, dynamic_array<UInt8>& data, int bytePosition)
+ {
+
+ }
+}
+
+TypeTree typeTree;
+dynamic_array<UInt8> data
+// Create typetree and data
+GenerateTypeTree(object);
+WriteObjectToVector(object, &data);
+
+// Modify data
+IterateTypeTreeFunctor func;
+IterateTypeTree (typeTree, data, func);
+
+ReadObjectFromVector(&object, data, typeTree);
+object.CheckConsistency ();
+object.AwakeFromLoad (false);
+object.SetDirty ();
+
+*/
+
+inline SInt32 ExtractPPtrInstanceID (const UInt8* data)
+{
+ return *reinterpret_cast<const SInt32*> (data);
+}
+
+inline SInt32 ExtractPPtrInstanceID (const dynamic_array<UInt8>& data, int bytePosition)
+{
+ return ExtractPPtrInstanceID(&data[bytePosition]);
+}
+
+inline void SetPPtrInstanceID (SInt32 instanceID, dynamic_array<UInt8>& data, int bytePosition)
+{
+ *reinterpret_cast<SInt32*> (&data[bytePosition]) = instanceID;
+}
+
+inline bool IsTypeTreePPtr (const TypeTree& typeTree)
+{
+ return typeTree.m_Type.find ("PPtr<") == 0;
+}
+
+inline bool IsTypeTreeString (const TypeTree& typeTree)
+{
+ return typeTree.m_Type == "string" && typeTree.m_Children.size() == 1 && typeTree.m_Children.back().m_IsArray;
+}
+
+inline bool IsTypeTreePPtrArray (const TypeTree& typeTree)
+{
+ return typeTree.m_IsArray && typeTree.m_Children.back().m_Type.find ("PPtr<") == 0;
+}
+
+inline bool IsTypeTreeArraySize (const TypeTree& typeTree)
+{
+ return typeTree.m_Father != NULL && typeTree.m_Father->m_IsArray && &typeTree.m_Father->m_Children.front() == &typeTree;
+}
+
+inline bool IsTypeTreeArrayElement (const TypeTree& typeTree)
+{
+ return typeTree.m_Father != NULL && typeTree.m_Father->m_IsArray && &typeTree.m_Father->m_Children.back() == &typeTree;
+}
+
+inline bool IsTypeTreeArrayOrArrayContainer (const TypeTree& typeTree)
+{
+ return typeTree.m_IsArray || (typeTree.m_Children.size() == 1 && typeTree.m_Children.back().m_IsArray);
+}
+
+inline bool IsTypeTreeArray (const TypeTree& typeTree)
+{
+ return typeTree.m_IsArray;
+}
+
+inline int ExtractArraySize (const UInt8* data)
+{
+ return *reinterpret_cast<const SInt32*> (data);
+}
+
+inline void SetArraySize (UInt8* data, SInt32 size)
+{
+ *reinterpret_cast<SInt32*> (data) = size;
+}
+
+inline int ExtractArraySize (const dynamic_array<UInt8>& data, int bytePosition)
+{
+ return *reinterpret_cast<const SInt32*> (&data[bytePosition]);
+}
+
+
+inline UInt32 Align4_Iterate (UInt32 size)
+{
+ UInt32 value = ((size + 3) >> 2) << 2;
+ return value;
+}
+#if UNITY_EDITOR
+
+template<class Functor>
+void IterateTypeTree (const TypeTree& typeTree, dynamic_array<UInt8>& data, Functor& functor)
+{
+ int bytePosition = 0;
+ IterateTypeTree (typeTree, data, &bytePosition, functor);
+}
+template<class Functor>
+void IterateTypeTree (const TypeTree& typeTree, dynamic_array<UInt8>& data, int* bytePosition, Functor& functor)
+{
+ if (functor (typeTree, data, *bytePosition))
+ {
+ if (typeTree.m_IsArray)
+ {
+ // First child in an array is the size
+ // Second child is the homogenous type of the array
+ AssertIf (typeTree.m_Children.front ().m_Type != SerializeTraits<SInt32>::GetTypeString (NULL) || typeTree.m_Children.front ().m_Name != "size" || typeTree.m_Children.size () != 2);
+
+ functor (typeTree.m_Children.front (), data, *bytePosition);
+
+ SInt32 arraySize, i;
+ arraySize = *reinterpret_cast<SInt32*> (&data[*bytePosition]);
+ *bytePosition += sizeof (arraySize);
+
+ for (i=0;i<arraySize;i++)
+ IterateTypeTree (typeTree.m_Children.back (), data, bytePosition, functor);
+ }
+ else
+ {
+ TypeTree::TypeTreeList::const_iterator i;
+ for (i = typeTree.m_Children.begin (); i != typeTree.m_Children.end ();++i)
+ IterateTypeTree (*i, data, bytePosition, functor);
+ }
+
+ if (typeTree.IsBasicDataType ())
+ *bytePosition += typeTree.m_ByteSize;
+
+ if (typeTree.m_MetaFlag & kAlignBytesFlag)
+ *bytePosition = Align4_Iterate (*bytePosition);
+ }
+ else
+ {
+ WalkTypeTree(typeTree, data.begin (), bytePosition);
+ }
+}
+#endif
+#endif
diff --git a/Runtime/Serialize/LoadProgress.h b/Runtime/Serialize/LoadProgress.h
new file mode 100644
index 0000000..d8fdabb
--- /dev/null
+++ b/Runtime/Serialize/LoadProgress.h
@@ -0,0 +1,24 @@
+#ifndef LOAD_PROGRESS_H
+#define LOAD_PROGRESS_H
+
+class LoadProgress
+{
+ volatile float* progressIndicator;
+ float progressInterval;
+
+public:
+ float totalItems;
+ float processedItems;
+
+ LoadProgress(unsigned total, float interval, float* indicator) : processedItems(0), totalItems(total), progressIndicator(indicator), progressInterval (interval) {}
+
+ void ItemProcessed (int count = 1)
+ {
+ processedItems = std::min (totalItems, processedItems + count);
+
+ if (progressIndicator)
+ *progressIndicator = totalItems == 0 ? 1.0f : progressInterval * processedItems / totalItems;
+ }
+
+};
+#endif
diff --git a/Runtime/Serialize/PathNamePersistentManager.cpp b/Runtime/Serialize/PathNamePersistentManager.cpp
new file mode 100644
index 0000000..81022e7
--- /dev/null
+++ b/Runtime/Serialize/PathNamePersistentManager.cpp
@@ -0,0 +1,51 @@
+#include "UnityPrefix.h"
+#include "PathNamePersistentManager.h"
+#include "Runtime/Utilities/Word.h"
+
+using namespace std;
+
+int PathNamePersistentManager::InsertPathNameInternal (const string& pathname, bool create)
+{
+ SET_ALLOC_OWNER(NULL);
+ AssertIf (!pathname.empty () && (pathname[0] == '/' || pathname[0] == '\\'));
+
+ string lowerCasePathName = ToLower (pathname);
+
+ PathToStreamID::iterator found = m_PathToStreamID.find (lowerCasePathName);
+ if (found != m_PathToStreamID.end())
+ return found->second;
+
+ if (create)
+ {
+ m_PathToStreamID.insert (make_pair (lowerCasePathName, m_PathNames.size ()));
+ m_PathNames.push_back (pathname);
+ AddStream ();
+ return m_PathNames.size () - 1;
+ }
+ else
+ return -1;
+}
+
+int PathNamePersistentManager::InsertFileIdentifierInternal (FileIdentifier file, bool create)
+{
+ return InsertPathNameInternal(file.pathName, create);
+}
+
+FileIdentifier PathNamePersistentManager::PathIDToFileIdentifierInternal (int pathID)
+{
+ AssertIf (pathID < 0 || pathID >= m_PathNames.size ());
+ FileIdentifier f;
+ f.pathName = m_PathNames[pathID];
+ return f;
+}
+
+string PathNamePersistentManager::PathIDToPathNameInternal (int pathID)
+{
+ AssertIf (pathID < 0 || pathID >= m_PathNames.size ());
+ return m_PathNames[pathID];
+}
+
+void InitPathNamePersistentManager()
+{
+ UNITY_NEW_AS_ROOT( PathNamePersistentManager(0), kMemManager, "PathNameManager", "");
+}
diff --git a/Runtime/Serialize/PathNamePersistentManager.h b/Runtime/Serialize/PathNamePersistentManager.h
new file mode 100644
index 0000000..11c3767
--- /dev/null
+++ b/Runtime/Serialize/PathNamePersistentManager.h
@@ -0,0 +1,31 @@
+#ifndef PATHNAMEPERSISTENTMANAGER_H
+#define PATHNAMEPERSISTENTMANAGER_H
+
+#include "PersistentManager.h"
+#include "SerializedFile.h"
+
+class PathNamePersistentManager : public PersistentManager
+{
+ typedef map<string, SInt32> PathToStreamID;
+ PathToStreamID m_PathToStreamID; // Contains lower case pathnames
+ vector<string> m_PathNames;// Contains pathnames as they were given
+
+ public:
+
+ PathNamePersistentManager (int options, int cacheCount = 2)
+ : PersistentManager (options, cacheCount) {}
+
+ protected:
+
+ virtual int InsertPathNameInternal (const std::string& pathname, bool create);
+ virtual int InsertFileIdentifierInternal (FileIdentifier file, bool create);
+
+ virtual string PathIDToPathNameInternal (int pathID);
+
+ virtual FileIdentifier PathIDToFileIdentifierInternal (int pathID);
+};
+
+void InitPathNamePersistentManager();
+
+
+#endif
diff --git a/Runtime/Serialize/PersistentManager.cpp b/Runtime/Serialize/PersistentManager.cpp
new file mode 100644
index 0000000..3f596f4
--- /dev/null
+++ b/Runtime/Serialize/PersistentManager.cpp
@@ -0,0 +1,2291 @@
+#include "UnityPrefix.h"
+#include "PersistentManager.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "SerializedFile.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Remapper.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Utilities/File.h"
+#include "AwakeFromLoadQueue.h"
+
+#define DEBUG_THREAD_LOAD 0
+#define DEBUG_THREAD_LOAD_LONG_ACTIVATE !UNITY_RELEASE
+#define DEBUG_MAINTHREAD_LOADING 0
+
+
+#if DEBUG_THREAD_LOAD
+#define printf_debug_thread printf_console
+#else
+#define printf_debug_thread
+#endif
+
+#include "Runtime/Threads/ProfilerMutex.h"
+#if ENABLE_PROFILER
+#include "Runtime/Profiler/MemoryProfiler.h"
+#endif
+PROFILER_INFORMATION(gMakeObjectUnpersistentProfiler, "Loading.MakeObjectUnpersistent", kProfilerLoading)
+PROFILER_INFORMATION(gMakeObjectPersistentProfiler, "Loading.MakeObjectUnpersistent", kProfilerLoading)
+PROFILER_INFORMATION(gAwakeFromLoadManager, "Loading.AwakeFromLoad", kProfilerLoading)
+PROFILER_INFORMATION(gIDRemappingProfiler, "Loading.IDRemapping", kProfilerLoading)
+PROFILER_INFORMATION(gWriteFileProfiler, "Loading.WriteFile", kProfilerLoading)
+PROFILER_INFORMATION(gFindInActivationQueueProfiler, "Loading.FindInThreadedActivationQueue", kProfilerLoading)
+PROFILER_INFORMATION(gReadObjectProfiler, "Loading.ReadObject", kProfilerLoading)
+PROFILER_INFORMATION(gLoadFileProfiler, "Loading.LoadFile", kProfilerLoading)
+PROFILER_INFORMATION(gIsObjectAvailable, "Loading.IsObjectAvailaable", kProfilerLoading)
+PROFILER_INFORMATION(gLoadStreamNameSpaceProfiler, "Loading.LoadFileHeaders", kProfilerLoading)
+PROFILER_INFORMATION(gLoadLockPersistentManager, "Loading.LockPersistentManager", kProfilerLoading)
+PROFILER_INFORMATION(gLoadFromActivationQueueStall, "Loading.LoadFromActivationQueue stalled [wait for loading operation to finish]", kProfilerLoading)
+// @TODO: Write test for cross references between monobehaviours in prefab and in scene!
+
+
+static PersistentManager* gPersistentManager = NULL;
+static PersistentManager::InOrderDeleteCallbackFunction* gInOrderDeleteCallback = NULL;
+static PersistentManager::SafeBinaryReadCallbackFunction* gSafeBinaryReadCallback = NULL;
+
+static const char* kSerializedFileArea = "SerializedFile";
+static const char* kRemapperAllocArea = "PersistentManager.Remapper";
+
+#if UNITY_EDITOR || SUPPORT_RESOURCE_IMAGE_LOADING
+static const char* kResourceImageExtensions[] = { "resG", "res", "resS" };
+#endif
+
+double GetTimeSinceStartup ();
+
+using namespace std;
+
+bool PersistentManager::InstanceIDToSerializedObjectIdentifier (int instanceID, SerializedObjectIdentifier& identifier)
+{
+ PROFILER_AUTO_THREAD_SAFE(gIDRemappingProfiler, NULL);
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ return m_Remapper->InstanceIDToSerializedObjectIdentifier(instanceID, identifier);
+}
+
+int PersistentManager::SerializedObjectIdentifierToInstanceID (const SerializedObjectIdentifier& identifier)
+{
+ PROFILER_AUTO_THREAD_SAFE(gIDRemappingProfiler, NULL);
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ return m_Remapper->GetOrGenerateMemoryID (identifier);
+}
+
+
+LocalIdentifierInFileType PersistentManager::GetLocalFileID(SInt32 instanceID)
+{
+ SerializedObjectIdentifier identifier;
+ InstanceIDToSerializedObjectIdentifier (instanceID, identifier);
+ return identifier.localIdentifierInFile;
+}
+
+SInt32 PersistentManager::GetInstanceIDFromPathAndFileID (const string& path, LocalIdentifierInFileType localIdentifierInFile)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ SerializedObjectIdentifier identifier;
+ identifier.serializedFileIndex = InsertPathNameInternal (path, true);
+ identifier.localIdentifierInFile = localIdentifierInFile;
+ return m_Remapper->GetOrGenerateMemoryID (identifier);
+}
+
+int PersistentManager::GetClassIDFromPathAndFileID (const string& path, LocalIdentifierInFileType localIdentifierInFile)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ SerializedObjectIdentifier identifier;
+ identifier.serializedFileIndex = InsertPathNameInternal (path, true);
+ identifier.localIdentifierInFile = localIdentifierInFile;
+
+ SerializedFile* stream = GetSerializedFileInternal (identifier.serializedFileIndex);
+ if (stream == NULL)
+ return -1;
+
+ if (!stream->IsAvailable (identifier.localIdentifierInFile))
+ return -1;
+
+ return stream->GetClassID (identifier.localIdentifierInFile);
+}
+
+static void CleanupStream (StreamNameSpace& stream)
+{
+ SerializedFile* oldFile = stream.stream;
+ stream.stream = NULL;
+
+ UNITY_DELETE (oldFile, kMemSerialization);
+}
+
+int PersistentManager::GetSerializedClassID (int instanceID)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ SerializedObjectIdentifier identifier;
+ if (!m_Remapper->InstanceIDToSerializedObjectIdentifier(instanceID, identifier))
+ return -1;
+
+ SerializedFile* stream = GetSerializedFileInternal (identifier.serializedFileIndex);
+ if (stream == NULL)
+ return -1;
+
+ if (!stream->IsAvailable (identifier.localIdentifierInFile))
+ return -1;
+
+ return stream->GetClassID (identifier.localIdentifierInFile);
+}
+
+void PersistentManager::GetAllFileIDs (const string& pathName, vector<LocalIdentifierInFileType>* objects)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ AssertIf (objects == NULL);
+
+ int serializedFileIndex = InsertPathNameInternal (pathName, true);
+ SerializedFile* stream = GetSerializedFileInternal (serializedFileIndex);
+ if (stream == NULL)
+ return;
+
+ stream->GetAllFileIDs (objects);
+}
+
+bool PersistentManager::RemoveObjectsFromPath (const std::string& pathName)
+{
+#if DEBUGMODE
+ AssertIf(!m_AllowLoadingFromDisk);
+#endif
+
+ ASSERT_RUNNING_ON_MAIN_THREAD
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ SInt32 serializedFileIndex = InsertPathNameInternal (pathName, false);
+ if (serializedFileIndex == -1)
+ return false;
+
+ vector<SInt32> temp;
+ m_Remapper->RemoveCompletePathID(serializedFileIndex, temp);
+
+ return true;
+}
+
+void PersistentManager::MakeObjectUnpersistent (int memoryID, UnpersistMode mode)
+{
+ PROFILER_AUTO_THREAD_SAFE(gMakeObjectUnpersistentProfiler, NULL);
+
+#if DEBUGMODE
+ AssertIf(!m_AllowLoadingFromDisk);
+#endif
+
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ Object* o = Object::IDToPointer (memoryID);
+ if (o && !o->IsPersistent ())
+ {
+// #if DEBUGMODE && !UNITY_RELEASE
+// AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, GetMainThreadID(), "PersistentManager.MakeObjectUnpersistent");
+// AssertIf (m_Remapper->GetPathID (memoryID) != -1);
+// #endif
+ return;
+ }
+
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+ if (mode == kDestroyFromFile)
+ DestroyFromFileInternal (memoryID);
+
+ m_Remapper->Remove (memoryID);
+
+ if (o)
+ o->SetIsPersistent (false);
+}
+
+#if UNITY_EDITOR
+void PersistentManager::MakeObjectPersistent (int heapID, const string& pathName)
+{
+ MakeObjectPersistentAtFileID (heapID, 0, pathName);
+}
+
+void PersistentManager::MakeObjectPersistentAtFileID (int heapID, LocalIdentifierInFileType fileID, const string& pathName)
+{
+ MakeObjectsPersistent (&heapID, &fileID, 1, pathName);
+}
+
+void PersistentManager::MakeObjectsPersistent (const int* heapIDs, LocalIdentifierInFileType* fileIDs, int size, const string& pathName, int options)
+{
+ PROFILER_AUTO_THREAD_SAFE(gMakeObjectPersistentProfiler, NULL);
+
+ AssertIf(!m_AllowLoadingFromDisk);
+ AssertIf(!Thread::EqualsCurrentThreadID(GetMainThreadID()));
+
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ AssertIf(pathName.empty());
+ SInt32 globalNameSpace = InsertPathNameInternal (pathName, true);
+ StreamNameSpace* streamNameSpace = NULL;
+
+ for (int i=0;i<size;i++)
+ {
+ int heapID = heapIDs[i];
+ LocalIdentifierInFileType fileID = fileIDs[i];
+
+ Object* o = Object::IDToPointer (heapID);
+
+ if ((options & kMakePersistentDontRequireToBeLoadedAndDontUnpersist) == 0)
+ {
+ // Making an object that is not in memory persistent
+ if (o == NULL)
+ {
+ ErrorString("Make Objects Persistent failed because the object can not be loaded");
+ continue;
+ }
+
+ // Make Object unpersistent first
+ if (o->IsPersistent ())
+ {
+ SerializedObjectIdentifier identifier;
+ InstanceIDToSerializedObjectIdentifier(heapID, identifier);
+ AssertIf (identifier.serializedFileIndex == -1);
+
+ // Return if the file and serializedFileIndex is not going to change
+ if (globalNameSpace == identifier.serializedFileIndex)
+ {
+ if (fileID == 0 || identifier.localIdentifierInFile == fileID)
+ continue;
+ }
+
+ MakeObjectUnpersistent (heapID, kDestroyFromFile);
+ }
+ }
+
+ if (streamNameSpace == NULL)
+ streamNameSpace = &GetStreamNameSpaceInternal (globalNameSpace);
+
+ // Allocate an fileID for this object in the File
+ if (fileID == 0)
+ {
+ fileID = streamNameSpace->highestID;
+ if (streamNameSpace->stream)
+ fileID = max (streamNameSpace->highestID, streamNameSpace->stream->GetHighestID ());
+ fileID++;
+ }
+ streamNameSpace->highestID = max (streamNameSpace->highestID, fileID);
+
+ SerializedObjectIdentifier identifier;
+ identifier.serializedFileIndex = globalNameSpace;
+ identifier.localIdentifierInFile = fileID;
+ m_Remapper->SetupRemapping (heapID, identifier);
+ fileIDs[i] = fileID;
+
+ if (o)
+ {
+
+ AssertIf (o->TestHideFlag (Object::kDontSave) && (options & kAllowDontSaveObjectsToBePersistent) == 0);
+ o->SetIsPersistent (true);
+ o->SetDirty ();
+ }
+ }
+}
+#endif
+
+
+void PersistentManager::LocalSerializedObjectIdentifierToInstanceIDInternal (const LocalSerializedObjectIdentifier& localIdentifier, SInt32& outInstanceID)
+{
+ int activeNameSpace = m_ActiveNameSpace.top();
+ LocalSerializedObjectIdentifierToInstanceIDInternal (activeNameSpace, localIdentifier, outInstanceID);
+}
+
+void PersistentManager::LocalSerializedObjectIdentifierToInstanceIDInternal (int activeNameSpace, const LocalSerializedObjectIdentifier& localIdentifier, SInt32& outInstanceID)
+{
+ PROFILER_AUTO_THREAD_SAFE(gIDRemappingProfiler, NULL);
+
+ LocalIdentifierInFileType localIdentifierInFile = localIdentifier.localIdentifierInFile;
+ int localSerializedFileIndex = localIdentifier.localSerializedFileIndex;
+
+ if (localIdentifierInFile == 0)
+ {
+ outInstanceID = 0;
+ return;
+ }
+
+ AssertIf (localSerializedFileIndex == -1);
+
+ int globalFileIndex;
+ if (localSerializedFileIndex == 0)
+ globalFileIndex = activeNameSpace;
+ else
+ {
+ AssertIf (m_Streams[activeNameSpace].stream == NULL);
+
+ AssertIf(activeNameSpace >= m_LocalToGlobalNameSpace.size() || activeNameSpace < 0);
+
+ IDRemap::iterator found = m_LocalToGlobalNameSpace[activeNameSpace].find (localSerializedFileIndex);
+
+ if (found != m_LocalToGlobalNameSpace[activeNameSpace].end ())
+ {
+ globalFileIndex = found->second;
+ }
+ else
+ {
+ AssertString ("illegal LocalPathID in persistentmanager");
+ outInstanceID = 0;
+ return;
+ }
+ }
+
+ SerializedObjectIdentifier globalIdentifier;
+ globalIdentifier.serializedFileIndex = globalFileIndex;
+ globalIdentifier.localIdentifierInFile = localIdentifierInFile;
+
+ #if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+ ApplyInstanceIDRemap(globalIdentifier);
+ #endif
+
+ outInstanceID = m_Remapper->GetOrGenerateMemoryID (globalIdentifier);
+}
+
+#if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+void PersistentManager::ApplyInstanceIDRemap(SerializedObjectIdentifier& id)
+{
+ InstanceIDRemap::iterator foundIDRemap = m_InstanceIDRemap.find(id);
+ if (foundIDRemap != m_InstanceIDRemap.end())
+ id = foundIDRemap->second;
+}
+#endif // #if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+
+
+LocalSerializedObjectIdentifier PersistentManager::GlobalToLocalSerializedFileIndexInternal (const SerializedObjectIdentifier& globalIdentifier)
+{
+ LocalIdentifierInFileType localIdentifierInFile = globalIdentifier.localIdentifierInFile;
+ int localSerializedFileIndex;
+
+ // Remap globalPathID to localPathID
+ int activeNameSpace = m_ActiveNameSpace.top ();
+
+ IDRemap& globalToLocalNameSpace = m_GlobalToLocalNameSpace[activeNameSpace];
+ IDRemap& localToGlobalNameSpace = m_LocalToGlobalNameSpace[activeNameSpace];
+
+ IDRemap::iterator found = globalToLocalNameSpace.find (globalIdentifier.serializedFileIndex);
+ if (found == globalToLocalNameSpace.end ())
+ {
+ SET_ALLOC_OWNER(NULL);
+ AssertIf (activeNameSpace >= m_Streams.size());
+ AssertIf (m_Streams[activeNameSpace].stream == NULL);
+ SerializedFile& serialize = *m_Streams[activeNameSpace].stream;
+
+ serialize.AddExternalRef (PathIDToFileIdentifierInternal (globalIdentifier.serializedFileIndex));
+
+ localSerializedFileIndex = serialize.GetExternalRefs ().size ();
+ globalToLocalNameSpace[globalIdentifier.serializedFileIndex] = localSerializedFileIndex;
+ localToGlobalNameSpace[localSerializedFileIndex] = globalIdentifier.serializedFileIndex;
+ }
+ else
+ localSerializedFileIndex = found->second;
+
+ // Setup local identifier
+ LocalSerializedObjectIdentifier localIdentifier;
+
+ localIdentifier.localSerializedFileIndex = localSerializedFileIndex;
+ localIdentifier.localIdentifierInFile = localIdentifierInFile;
+
+ return localIdentifier;
+}
+
+void PersistentManager::InstanceIDToLocalSerializedObjectIdentifierInternal (SInt32 instanceID, LocalSerializedObjectIdentifier& localIdentifier)
+{
+ PROFILER_AUTO_THREAD_SAFE(gIDRemappingProfiler, NULL);
+
+ AssertIf (m_ActiveNameSpace.empty ());
+ if (instanceID == 0)
+ {
+ localIdentifier.localSerializedFileIndex = 0;
+ localIdentifier.localIdentifierInFile = 0;
+ return;
+ }
+
+ SerializedObjectIdentifier globalIdentifier;
+ if (!m_Remapper->InstanceIDToSerializedObjectIdentifier (instanceID, globalIdentifier))
+ {
+ localIdentifier.localSerializedFileIndex = 0;
+ localIdentifier.localIdentifierInFile = 0;
+ return;
+ }
+
+ localIdentifier = GlobalToLocalSerializedFileIndexInternal(globalIdentifier);
+}
+
+bool PersistentManager::IsInstanceIDFromCurrentFileInternal (SInt32 instanceID)
+{
+ if (instanceID == 0)
+ return false;
+
+ SerializedObjectIdentifier globalIdentifier;
+
+ if (!m_Remapper->InstanceIDToSerializedObjectIdentifier (instanceID, globalIdentifier))
+ return false;
+
+ int activeNameSpace = m_ActiveNameSpace.top ();
+ return globalIdentifier.serializedFileIndex == activeNameSpace;
+}
+
+#if UNITY_EDITOR
+
+int PersistentManager::GetSerializedFileIndexFromPath (const std::string& path)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+ return InsertPathNameInternal (path, true);
+}
+
+bool PersistentManager::TestNeedWriteFile (const string& pathName, const std::set<int>* cachedDirtyPathsHint)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ int serializedFileIndex = InsertPathNameInternal (pathName, false);
+ return TestNeedWriteFileInternal(serializedFileIndex, cachedDirtyPathsHint);
+}
+
+bool PersistentManager::TestNeedWriteFile (int globalFileIndex, const std::set<int>* cachedDirtyPathsHint)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+ return TestNeedWriteFileInternal(globalFileIndex, cachedDirtyPathsHint);
+}
+
+bool PersistentManager::TestNeedWriteFileInternal (int globalFileIndex, const std::set<int>* cachedDirtyPathsHint)
+{
+ if (globalFileIndex == -1)
+ return false;
+
+ SerializedFile* stream = m_Streams[globalFileIndex].stream;
+
+ bool isFileDirty = stream != NULL && stream->IsFileDirty ();
+
+ // Something was deleted from the file. Must write it!
+ if (isFileDirty)
+ return true;
+
+ // Use Dirty path indices to quickly determine if a file needs writing
+ if (cachedDirtyPathsHint != NULL)
+ return cachedDirtyPathsHint->count (globalFileIndex);
+
+ Object* o;
+ // Find out if file needs to write to disk
+ // - Get all loaded objects that have registered themselves for being at that file
+ set<SInt32> loadedWriteObjects;
+ m_Remapper->GetAllLoadedObjectsAtPath (globalFileIndex, &loadedWriteObjects);
+ for (set<SInt32>::iterator i=loadedWriteObjects.begin ();i != loadedWriteObjects.end ();i++)
+ {
+ o = Object::IDToPointer (*i);
+ if (o && o->IsPersistent () && o->IsPersistentDirty ())
+ return true;
+ }
+
+ return false;
+}
+
+void PersistentManager::CleanupStreamAndNameSpaceMapping (unsigned serializedFileIndex)
+{
+ // Unload the file any way
+ // This saves memory - especially when reimporting lots of assets like when rebuilding the library
+ CleanupStream(m_Streams[serializedFileIndex]);
+
+ m_GlobalToLocalNameSpace[serializedFileIndex].clear ();
+ m_LocalToGlobalNameSpace[serializedFileIndex].clear ();
+}
+
+static bool InitTempWriteFile (FileCacherWrite& writer, const std::string& path, unsigned cacheSize)
+{
+ string tempWriteFileName = GenerateUniquePathSafe (path);
+ if (tempWriteFileName.empty())
+ return false;
+
+ writer.InitWriteFile(path, cacheSize);
+
+ return true;
+}
+
+int PersistentManager::WriteFile (const std::string& path, BuildTargetSelection target, int options)
+{
+ PROFILER_AUTO_THREAD_SAFE(gWriteFileProfiler, NULL);
+
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ int serializedFileIndex;
+ serializedFileIndex = InsertPathNameInternal(path, false);
+ if (serializedFileIndex == -1)
+ return kNoError;
+
+ bool needsWrite = TestNeedWriteFile(serializedFileIndex);
+
+ // Early out
+ if (!needsWrite)
+ {
+ // @TODO: THIS SHOULD NOT BE HACKED IN HERE. Make test coverage against increased leaking then remove this and call CleanupStream explicitly.
+ CleanupStreamAndNameSpaceMapping(serializedFileIndex);
+ return kNoError;
+ }
+
+ set<SInt32> writeObjects;
+ if (options & kDontReadObjectsFromDiskBeforeWriting)
+ {
+ GetLoadedInstanceIDsAtPath (path, &writeObjects);
+ Assert(!writeObjects.empty());
+ }
+ else
+ {
+ // Load all writeobjects into memory
+ // (dont use LoadFileCompletely, since that reads all objects
+ // even those that might have been changed in memory)
+ GetInstanceIDsAtPath (path, &writeObjects);
+ }
+
+ vector<WriteData> writeData;
+
+ for (set<SInt32>::iterator i=writeObjects.begin ();i != writeObjects.end ();i++)
+ {
+ SInt32 instanceID = *i;
+
+ // Force load object from disk.
+ Object* o = dynamic_instanceID_cast<Object*> (instanceID);
+
+ if (o == NULL)
+ continue;
+
+ #if UNITY_EDITOR
+ // Disable text serialization for terrain data. Just too much data, you'd never want to merge.
+ int cid = o->GetClassID();
+ if (IsClassNonTextSerialized(cid))
+ options &= ~kAllowTextSerialization;
+ #endif
+
+ AssertIf (o != NULL && !o->IsPersistent ());
+
+ SerializedObjectIdentifier identifier;
+ m_Remapper->InstanceIDToSerializedObjectIdentifier(instanceID, identifier);
+
+ Assert (identifier.serializedFileIndex == serializedFileIndex);
+
+ DebugAssertIf (!o->IsPersistent ());
+ DebugAssertIf (m_Remapper->GetSerializedFileIndex (instanceID) != serializedFileIndex);
+ DebugAssertIf (!m_Remapper->IsSetup (identifier));
+
+ writeData.push_back(WriteData (identifier.localIdentifierInFile, instanceID, BuildUsageTag()));
+ }
+
+ sort(writeData.begin(), writeData.end());
+
+ int result = WriteFileInternal(path, serializedFileIndex, &writeData[0], writeData.size(), NULL, target, options);
+ if (result != kNoError && options & kAllowTextSerialization)
+ // Try binary serialization as a fallback.
+ result = WriteFileInternal(path, serializedFileIndex, &writeData[0], writeData.size(), NULL, target, options &~kAllowTextSerialization);
+
+ return result;
+}
+
+int PersistentManager::WriteFileInternal (const string& path, int serializedFileIndex, const WriteData* writeObjectData, int size, VerifyWriteObjectCallback* verifyCallback, BuildTargetSelection target, int options)
+{
+ //printf_console("Writing file %s\n", pathName.c_str());
+
+ // Create writing tools
+ CachedWriter writer;
+
+ FileCacherWrite serializedFileWriter;
+ FileCacherWrite resourceImageWriters[kNbResourceImages];
+ if (!InitTempWriteFile (serializedFileWriter, "Temp/tempFile", kCacheSize))
+ return kFileCouldNotBeWritten;
+ writer.InitWrite(serializedFileWriter);
+
+ if (options & kBuildResourceImage)
+ {
+ for (int i=0;i<kNbResourceImages;i++)
+ {
+ string path = AppendPathNameExtension("Temp/tempFile", kResourceImageExtensions[i]);
+ if (!InitTempWriteFile (resourceImageWriters[i], path, kCacheSize))
+ return kFileCouldNotBeWritten;
+ writer.InitResourceImage((ActiveResourceImage)i, resourceImageWriters[i]);
+ }
+ }
+
+ // Cleanup old stream and mapping
+ CleanupStreamAndNameSpaceMapping(serializedFileIndex);
+
+ // Setup global to self namespace mapping
+ m_GlobalToLocalNameSpace[serializedFileIndex][serializedFileIndex] = 0;
+ m_LocalToGlobalNameSpace[serializedFileIndex][0] = serializedFileIndex;
+
+ // Create writable stream
+ //@TODO: Object name might want to be
+ SerializedFile* tempSerialize = UNITY_NEW_AS_ROOT(SerializedFile, kMemSerialization, kSerializedFileArea, "");
+ #if ENABLE_MEM_PROFILER
+ tempSerialize->SetDebugPath(PathIDToPathNameInternal(serializedFileIndex));
+ GetMemoryProfiler()->SetRootAllocationObjectName(tempSerialize, tempSerialize->GetDebugPath().c_str());
+ #endif
+
+ tempSerialize->InitializeWrite (writer, target, options);
+ m_Streams[serializedFileIndex].stream = tempSerialize;
+
+ m_ActiveNameSpace.push (serializedFileIndex);
+
+ bool writeSuccess = true;
+ // Write Objects in fileID order
+ for (int i=0;i<size;i++)
+ {
+ LocalIdentifierInFileType localIdentifierInFile = writeObjectData[i].localIdentifierInFile;
+ SInt32 instanceID = writeObjectData[i].instanceID;
+
+ SerializedObjectIdentifier identifier (serializedFileIndex, localIdentifierInFile);
+
+ bool shouldUnloadImmediately = false;
+
+ Object* o = Object::IDToPointer (instanceID);;
+ if (o == NULL)
+ {
+ if (options & kLoadAndUnloadAssetsDuringBuild)
+ {
+ o = dynamic_instanceID_cast<Object*> (instanceID);
+ shouldUnloadImmediately = true;
+ }
+
+ // Object can not be loaded, don't write it
+ if (o == NULL)
+ {
+ continue;
+ }
+ }
+
+ if (verifyCallback != NULL && !verifyCallback (o, target.platform))
+ writeSuccess = false;
+
+ tempSerialize->WriteObject (*o, localIdentifierInFile, writeObjectData[i].buildUsage);
+ o->ClearPersistentDirty ();
+
+ if (shouldUnloadImmediately)
+ UnloadObject(o);
+ }
+
+ m_ActiveNameSpace.pop();
+
+ writeSuccess = writeSuccess && tempSerialize->FinishWriting() && !tempSerialize->HasErrors();
+
+ // Delete temp stream
+ if (m_Streams[serializedFileIndex].stream != tempSerialize)
+ {
+ writeSuccess = false;
+ UNITY_DELETE (tempSerialize, kMemSerialization);
+ tempSerialize = NULL;
+ }
+
+ // Delete mappings
+ CleanupStreamAndNameSpaceMapping(serializedFileIndex);
+
+ if (!writeSuccess)
+ {
+// ErrorString ("Writing file: " + path + " failed. The temporary file " + serializedFileWriter.GetPathName() + " couldn't be written.");
+ return kFileCouldNotBeWritten;
+ }
+
+ // Atomically move the serialized file into the target location
+ string actualNewPathName = RemapToAbsolutePath (path);
+ if (!MoveReplaceFile (serializedFileWriter.GetPathName(), actualNewPathName))
+ {
+ ErrorString ("File " + path + " couldn't be written. Because moving " + serializedFileWriter.GetPathName() + " to " + actualNewPathName + " failed.");
+ return kFileCouldNotBeWritten;
+ }
+ SetFileFlags(actualNewPathName, kFileFlagTemporary, 0);
+
+
+ if (options & kBuildResourceImage)
+ {
+ // Move the resource images into the target location
+ for (int i=0;i<kNbResourceImages;i++)
+ {
+ string targetPath = AppendPathNameExtension(actualNewPathName, kResourceImageExtensions[i]);
+
+ ::DeleteFile(targetPath);
+
+ string tempWriteFileName = resourceImageWriters[i].GetPathName();
+
+ if (GetFileLength(tempWriteFileName) > 0)
+ {
+ if (!MoveReplaceFile (tempWriteFileName, targetPath))
+ {
+ ErrorString ("File " + path + " couldn't be written. Because moving " + tempWriteFileName + " to " + actualNewPathName + " failed.");
+ return kFileCouldNotBeWritten;
+ }
+ SetFileFlags(targetPath, kFileFlagTemporary, 0);
+ }
+ }
+ }
+
+ return kNoError;
+}
+
+#endif
+
+string PersistentManager::GetPathName (SInt32 memoryID)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ SInt32 serializedFileIndex = m_Remapper->GetSerializedFileIndex (memoryID);
+ if (serializedFileIndex == -1)
+ return string ();
+ else
+ return PathIDToPathNameInternal (serializedFileIndex);
+}
+
+void PersistentManager::RegisterAndAwakeThreadedObjectAndUnlockIntegrationMutex (const ThreadedAwakeData& awake)
+{
+ // Register instance ID first and then unlock integration mutex so there is no chance of an object being loaded twice
+ AssertIf(!awake.completedThreadAwake);
+ if (awake.object != NULL)
+ {
+ Object::RegisterInstanceID(awake.object);
+
+ m_IntegrationMutex.Unlock();
+
+ AwakeFromLoadMode mode = (AwakeFromLoadMode)(kDidLoadFromDisk | kDidLoadThreaded);
+ AwakeFromLoadQueue::PersistentManagerAwakeSingleObject(*awake.object, awake.oldType, mode, awake.checkConsistency, gSafeBinaryReadCallback);
+}
+ else
+ {
+ m_IntegrationMutex.Unlock();
+ }
+}
+
+
+#if THREADED_LOADING
+void PersistentManager::AllowIntegrationWithTimeoutAndWait ()
+{
+ m_IntegrationMutex.Lock();
+ m_AllowIntegrateThreadedObjectsWithTimeout = true;
+ m_IntegrationMutex.Unlock();
+
+ // Wait until the integration thread has integrated all assets
+ while (true)
+ {
+ m_IntegrationMutex.Lock();
+
+ if (m_ThreadedObjectActivationQueue.empty())
+ {
+ m_IntegrationMutex.Unlock();
+ break;
+ }
+ else
+ {
+ m_IntegrationMutex.Unlock();
+ }
+
+ Thread::Sleep(0.1F);
+ }
+
+ m_IntegrationMutex.Lock();
+ m_AllowIntegrateThreadedObjectsWithTimeout = false;
+ m_IntegrationMutex.Unlock();
+}
+#else
+void PersistentManager::AllowIntegrationWithTimeoutAndWait ()
+{
+ IntegrateAllThreadedObjects();
+}
+#endif
+
+#if DEBUG_THREAD_LOAD
+int gDependencyCounter = 0;
+int gDependencyCounterCost = 0;
+int gDependencyCounterCostActivation = 0;
+int gDependencyCounterCostNonActivation = 0;
+int gDependencyCounterCostNotFound = 0;
+#endif
+
+Object* PersistentManager::LoadFromActivationQueue (int heapID)
+{
+ PROFILER_AUTO_THREAD_SAFE(gFindInActivationQueueProfiler, NULL);
+
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ LOCK_MUTEX(m_IntegrationMutex, gLoadFromActivationQueueStall);
+
+ ThreadedObjectActivationMap::iterator imap = m_ThreadedObjectActivationMap.find(heapID);
+
+ if (imap != m_ThreadedObjectActivationMap.end())
+ {
+ ThreadedObjectActivationQueue::iterator i = imap->second;
+ if (!i->completedThreadAwake)
+ {
+ ErrorString("Internal thread activation error. Activating object that has not been fully thread loaded.");
+ m_IntegrationMutex.Unlock();
+
+ return NULL;
+ }
+
+ ThreadedAwakeData data = *i;
+ m_ThreadedObjectActivationQueue.erase(i);
+ m_ThreadedObjectActivationMap.erase(imap);
+ #if DEBUG_THREAD_LOAD
+// printf_debug_thread("Activating dependency %d / %d [%d]\n", ++gDependencyCounter, gDependencyCounterCost, thisCost);
+// if (thisCost > 300)
+// {
+// printf_debug_thread("");
+// }
+ #endif
+
+ RegisterAndAwakeThreadedObjectAndUnlockIntegrationMutex(data);
+
+ return data.object;
+ }
+
+ #if DEBUG_THREAD_LOAD
+// if (thisCost > 300)
+// {
+// printf_debug_thread("Expensive load from activation queue and not found [%d]\n", thisCost);
+// }
+ #endif
+
+ m_IntegrationMutex.Unlock();
+
+ return NULL;
+}
+
+// GetFromActivationQueue is called a lot, so this function must be very efficient.
+// In one of the games called Harvest, m_ThreadedObjectActivationQueue contained 70000 items, and this function was called 5678 times
+// so linear searching was ubber slow!!!
+Object* PersistentManager::GetFromActivationQueue (int heapID)
+{
+ PROFILER_AUTO_THREAD_SAFE(gFindInActivationQueueProfiler, NULL);
+
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_IntegrationMutex,gLoadFromActivationQueueStall);
+
+ ThreadedObjectActivationMap::iterator item = m_ThreadedObjectActivationMap.find(heapID);
+ if (item != m_ThreadedObjectActivationMap.end())
+ {
+ return item->second->object;
+ }
+
+ AssertIf(m_OnDemandThreadLoadedObjects.count(heapID));
+
+ return NULL;
+}
+
+bool PersistentManager::FindInActivationQueue (int heapID)
+{
+ PROFILER_AUTO_THREAD_SAFE(gFindInActivationQueueProfiler, NULL);
+
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_IntegrationMutex, gLoadFromActivationQueueStall);
+
+ ThreadedObjectActivationMap::iterator item = m_ThreadedObjectActivationMap.find(heapID);
+ if (item != m_ThreadedObjectActivationMap.end())
+ {
+ return true;
+ }
+
+ if (m_OnDemandThreadLoadedObjects.count(heapID))
+ return true;
+
+ return false;
+}
+
+bool PersistentManager::HasThreadedObjectsToIntegrate ()
+{
+ Mutex::AutoLock lock (m_IntegrationMutex);
+ return !m_ThreadedObjectActivationQueue.empty ();
+}
+
+///@TODO: Prevent Object destruction during AwakeFromLoad!!!
+
+void PersistentManager::IntegrateThreadedObjects (float timeout)
+{
+ // Early out if there is nothing to be integrated.
+ if (!m_AllowIntegrateThreadedObjectsWithTimeout)
+ return;
+
+ if (!m_IntegrationMutex.TryLock())
+ {
+ //printf_debug_thread("\nINTEGRATION THREAD IS LOCKED\n");
+ return;
+ }
+
+ if (m_ThreadedObjectActivationQueue.empty() || !m_AllowIntegrateThreadedObjectsWithTimeout)
+ {
+ m_IntegrationMutex.Unlock();
+ return;
+ }
+
+ double startTime = GetTimeSinceStartup();
+ #if DEBUG_THREAD_LOAD
+ int integratedObjects = 0;
+ #endif
+
+ while (true)
+ {
+ ThreadedAwakeData awake = m_ThreadedObjectActivationQueue.front();
+
+ if (!awake.completedThreadAwake)
+ {
+ ErrorString("Stalling Integration because ThreadAwake for object is not completed");
+ break;
+ }
+
+ #if DEBUG_THREAD_LOAD
+ integratedObjects++;
+ #endif
+ m_ThreadedObjectActivationMap.erase(awake.instanceID);
+ m_ThreadedObjectActivationQueue.pop_front();
+
+ #if DEBUG_THREAD_LOAD_LONG_ACTIVATE
+ double time = GetTimeSinceStartup();
+ #endif
+
+ RegisterAndAwakeThreadedObjectAndUnlockIntegrationMutex(awake);
+
+ #if DEBUG_THREAD_LOAD_LONG_ACTIVATE
+ time = GetTimeSinceStartup() - time;
+ time *= 1000.0F;
+ if (time > 30.0F)
+ {
+ printf_console("Long thread activation time (%d ms) for object %s (%s)\n", (int)time, awake.object->GetName(), awake.object->GetClassName().c_str());
+ }
+ #endif
+
+ if (!m_AllowIntegrateThreadedObjectsWithTimeout || !m_IntegrationMutex.TryLock())
+ {
+ #if DEBUG_THREAD_LOAD
+ startTime = (GetTimeSinceStartup() - startTime) * 1000.0F;
+ printf_debug_thread("Integrate Threaded Objects ms: %f %d\n\n", (float)startTime, integratedObjects);
+ #endif
+ return;
+ }
+
+ double delta = (GetTimeSinceStartup() - startTime);
+ if (m_ThreadedObjectActivationQueue.empty() || delta > timeout)
+ break;
+ }
+
+ #if DEBUG_THREAD_LOAD
+ startTime = (GetTimeSinceStartup() - startTime) * 1000.0F;
+ printf_debug_thread("Integrate Threaded Objects ms: %f %d\n\n", (float)startTime, integratedObjects);
+ #endif
+
+ m_IntegrationMutex.Unlock();
+}
+
+void PersistentManager::IntegrateAllThreadedObjects ()
+{
+ AwakeFromLoadQueue awakeQueue (kMemTempAlloc);
+
+ PrepareAllThreadedObjectsStep1 (awakeQueue);
+ awakeQueue.RegisterObjectInstanceIDs();
+ IntegrateAllThreadedObjectsStep2 (awakeQueue);
+}
+
+void PersistentManager::PrepareAllThreadedObjectsStep1 (AwakeFromLoadQueue& awakeQueue)
+{
+ AQUIRE_AUTOLOCK (m_IntegrationMutex, gLoadFromActivationQueueStall);
+
+ // Add to AwakeFromLoadQueue - this will take care of ensuring sort order
+ awakeQueue.Reserve(m_ThreadedObjectActivationQueue.size());
+
+ ThreadedObjectActivationQueue::iterator end = m_ThreadedObjectActivationQueue.end();
+ for (ThreadedObjectActivationQueue::iterator i=m_ThreadedObjectActivationQueue.begin();i != end;++i )
+ {
+ ThreadedAwakeData& awake = *i;
+
+ if (awake.object)
+ {
+ Assert (awake.completedThreadAwake);
+ awakeQueue.Add(*awake.object, awake.oldType, awake.checkConsistency);
+ }
+ }
+ m_ThreadedObjectActivationQueue.clear();
+ m_ThreadedObjectActivationMap.clear();
+}
+
+void PersistentManager::IntegrateAllThreadedObjectsStep2 (AwakeFromLoadQueue& awakeQueue)
+{
+ Assert(m_ThreadedObjectActivationQueue.empty() && m_ThreadedObjectActivationMap.empty());
+
+ // Invoke AwakeFromLoadQueue in sorted order
+ AwakeFromLoadMode awakeMode = (AwakeFromLoadMode)(kDidLoadFromDisk | kDidLoadThreaded);
+
+ awakeQueue.PersistentManagerAwakeFromLoad(awakeMode, gSafeBinaryReadCallback);
+
+ Assert(m_ThreadedObjectActivationQueue.empty() && m_ThreadedObjectActivationMap.empty());
+}
+
+
+bool PersistentManager::ReloadFromDisk (Object* obj)
+{
+ PROFILER_AUTO_THREAD_SAFE(gReadObjectProfiler, obj);
+
+#if DEBUGMODE
+ AssertIf(!m_AllowLoadingFromDisk);
+#endif
+
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ SerializedObjectIdentifier identifier;
+ if (!m_Remapper->InstanceIDToSerializedObjectIdentifier(obj->GetInstanceID(), identifier))
+ {
+ ErrorString("Trying to reload asset from disk that is not stored on disk");
+ return false;
+ }
+
+ SerializedFile* stream = GetSerializedFileInternal(identifier.serializedFileIndex);
+ if (stream == NULL)
+ {
+ ErrorString("Trying to reload asset but can't find object on disk");
+ return false;
+ }
+
+ m_ActiveNameSpace.push (identifier.serializedFileIndex);
+ TypeTree* oldType;
+ bool didTypeTreeChange;
+ stream->ReadObject (identifier.localIdentifierInFile, obj->GetInstanceID(), kCreateObjectDefault, true, &oldType, &didTypeTreeChange, &obj);
+ m_ActiveNameSpace.pop ();
+
+ // Awake the object
+ if (obj)
+ {
+ AwakeFromLoadQueue::PersistentManagerAwakeSingleObject (*obj, oldType, kDidLoadFromDisk, didTypeTreeChange, gSafeBinaryReadCallback);
+ }
+
+ return true;
+}
+
+Object* PersistentManager::ReadObject (int heapID)
+{
+ PROFILER_AUTO_THREAD_SAFE(gReadObjectProfiler, NULL);
+
+#if DEBUGMODE
+ AssertIf(!m_AllowLoadingFromDisk);
+#endif
+
+ #if !UNITY_EDITOR
+ AssertIf(heapID < 0);
+ #endif
+
+ #if DEBUG_MAINTHREAD_LOADING
+ double time = GetTimeSinceStartup ();
+ #endif
+
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ LOCK_MUTEX(m_Mutex, gLoadLockPersistentManager);
+
+ Object* o = LoadFromActivationQueue(heapID);
+ if (o != NULL)
+{
+ m_Mutex.Unlock();
+ return o;
+ }
+
+ // Find and load the right stream
+ SerializedObjectIdentifier identifier;
+ if (!m_Remapper->InstanceIDToSerializedObjectIdentifier(heapID, identifier))
+ {
+ m_Mutex.Unlock();
+ return NULL;
+ }
+
+#if DEBUGMODE
+ AssertIf(!m_IsLoadingSceneFile && StrICmp(GetPathNameExtension(PathIDToPathNameInternal(identifier.serializedFileIndex)),"unity") == 0);
+#endif
+
+ SerializedFile* stream = GetSerializedFileInternal (identifier.serializedFileIndex);
+ if (stream == NULL)
+ {
+ #if DEBUG_MAINTHREAD_LOADING
+ LogString(Format("--- Loading from main thread failed loading stream %f", (GetTimeSinceStartup () - time) * 1000.0F));
+ #endif
+
+ m_Mutex.Unlock();
+ return NULL;
+}
+
+ AssertIf(Object::IDToPointer (heapID) != NULL);
+
+ // Find file id in stream and read the object
+
+ m_ActiveNameSpace.push (identifier.serializedFileIndex);
+ TypeTree* oldType;
+ bool didTypeTreeChange;
+ o = NULL;
+ stream->ReadObject (identifier.localIdentifierInFile, heapID, kCreateObjectDefault, true, &oldType, &didTypeTreeChange, &o);
+ m_ActiveNameSpace.pop ();
+
+ // Awake the object
+ if (o)
+ {
+ AwakeFromLoadQueue::PersistentManagerAwakeSingleObject (*o, oldType, kDidLoadFromDisk, didTypeTreeChange, gSafeBinaryReadCallback);
+ }
+
+// printf_console ("Read %d %s (%s)\n", heapID, o ? o->GetName () : "<null>", o ? o->GetClassName ().c_str() : "");
+
+ m_Mutex.Unlock();
+
+ #if DEBUG_MAINTHREAD_LOADING
+ if (o)
+ {
+ LogString(Format("--- Loading from main thread %s (%s) %f ms", o->GetName(), o->GetClassName().c_str(), (GetTimeSinceStartup () - time) * 1000.0F));
+ }
+ else
+ {
+ LogString(Format("--- Loading from main thread (NULL) %f ms", (GetTimeSinceStartup () - time) * 1000.0F));
+ }
+ #endif
+
+ return o;
+}
+
+void PersistentManager::SetupThreadActivationQueueObject (ThreadedAwakeData& awakeData, TypeTree* oldType, bool didTypeTreeChange)
+{
+ Object* obj = awakeData.object;
+ if (obj != NULL)
+{
+ awakeData.oldType = oldType;
+ awakeData.checkConsistency = didTypeTreeChange;
+ obj->AwakeFromLoadThreaded();
+ }
+ awakeData.completedThreadAwake = true;
+}
+
+ThreadedAwakeData* PersistentManager::CreateThreadActivationQueueEntry (SInt32 instanceID)
+{
+ DebugAssertIf(Object::IDToPointerThreadSafe(instanceID) != NULL);
+
+ ThreadedAwakeData awake;
+ awake.instanceID = instanceID;
+ awake.checkConsistency = false;
+ awake.completedThreadAwake = false;
+ awake.oldType = NULL;
+ awake.object = NULL;
+
+ ThreadedAwakeData* result;
+ m_IntegrationMutex.Lock();
+
+ m_ThreadedObjectActivationQueue.push_back(awake);
+ result = &m_ThreadedObjectActivationQueue.back();
+ m_ThreadedObjectActivationMap[instanceID] = --m_ThreadedObjectActivationQueue.end();
+
+ m_IntegrationMutex.Unlock();
+ DebugAssertIf(Object::IDToPointerThreadSafe(instanceID) != NULL);
+ return result;
+}
+
+void PersistentManager::CheckInstanceIDsLoaded (SInt32* heapIDs, int size)
+{
+ m_IntegrationMutex.Lock();
+
+ set<SInt32> ids;
+
+ // Search through threaded object activation queue and activate the object if necessary.
+ ThreadedObjectActivationQueue::iterator i, end;
+ end = m_ThreadedObjectActivationQueue.end();
+ for (i=m_ThreadedObjectActivationQueue.begin();i != end;i++)
+ ids.insert(i->instanceID);
+
+ for (int j=0;j<size;j++)
+ {
+ if (ids.count(heapIDs[j]))
+ heapIDs[j] = 0;
+ }
+
+ m_IntegrationMutex.Unlock();
+
+ // Check which objects are already loaded all at once to lock object creation only once for a short amount of time
+ // Since we have locked persistentmanager already no objects can be loaded in the mean time
+ LockObjectCreation();
+ Object::CheckInstanceIDsLoaded(heapIDs, size);
+ UnlockObjectCreation();
+}
+
+Object* PersistentManager::ReadObjectThreaded (int heapID)
+{
+#if DEBUGMODE
+ AssertIf(!m_AllowLoadingFromDisk);
+#endif
+
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ Object* o = GetFromActivationQueue(heapID);
+ if (o != NULL)
+ return o;
+
+ // Find and load the right stream
+ SerializedObjectIdentifier identifier;
+ if (!m_Remapper->InstanceIDToSerializedObjectIdentifier(heapID, identifier))
+ return NULL;
+
+ SerializedFile* stream = GetSerializedFileInternal (identifier.serializedFileIndex);
+ if (stream == NULL)
+ return NULL;
+#if DEBUGMODE
+ AssertIf(!m_IsLoadingSceneFile && PathIDToPathNameInternal(identifier.serializedFileIndex).find(".unity") != string::npos);
+#endif
+ AssertIf(Object::IDToPointerThreadSafe (heapID) != NULL);
+
+ // Find file id in stream and read the object
+
+ m_ActiveNameSpace.push (identifier.serializedFileIndex);
+ TypeTree* oldType;
+ bool didTypeTreeChange;
+
+ ThreadedAwakeData* awakeData = CreateThreadActivationQueueEntry(heapID);
+
+ // Inject into m_OnDemandThreadLoadedObjects so we can track which objects have been read during a LoadFileCompletelyThreaded or LoadThreadedObjects.
+ // This prevents loading objects twice
+ m_IntegrationMutex.Lock();
+ m_OnDemandThreadLoadedObjects.insert(heapID);
+ m_IntegrationMutex.Unlock();
+
+ awakeData->object = NULL;
+ stream->ReadObject (identifier.localIdentifierInFile, heapID, kCreateObjectFromNonMainThread, !m_Remapper->IsSceneID (heapID), &oldType, &didTypeTreeChange, &awakeData->object);
+
+ m_ActiveNameSpace.pop ();
+
+ o = awakeData->object;
+ SetupThreadActivationQueueObject(*awakeData, oldType, didTypeTreeChange);
+
+ return o;
+}
+
+void PersistentManager::LoadObjectsThreaded (SInt32* heapIDs, int size, LoadProgress* loadProgress)
+{
+ if (size == 0)
+ return;
+
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ vector<SInt32> heapIDsCopy;
+ heapIDsCopy.assign(heapIDs, heapIDs + size);
+
+ // Check which objects are already loaded all at once to lock object creation only once for a short amount of time
+ // Since we have locked persistentmanager already no objects can be loaded in the mean time
+ CheckInstanceIDsLoaded(&heapIDsCopy[0], size);
+
+ #if DEBUGMODE
+ m_IsLoadingSceneFile = true;
+ #endif
+
+ for (int i=0;i<size;i++)
+ {
+ SInt32 heapID = heapIDsCopy[i];
+ if (heapID == 0)
+ {
+ if (loadProgress)
+ loadProgress->ItemProcessed ();
+ continue;
+ }
+
+ m_IntegrationMutex.Lock();
+ if (m_OnDemandThreadLoadedObjects.count(heapID))
+ {
+ if (loadProgress)
+ loadProgress->ItemProcessed ();
+ m_IntegrationMutex.Unlock();
+ continue;
+ }
+ m_IntegrationMutex.Unlock();
+
+ DebugAssertIf(Object::IDToPointerThreadSafe(heapID) != NULL);
+ //DebugAssertIf(FindInActivationQueue(heapID));
+
+ // Find and load the right stream
+ SerializedObjectIdentifier identifier;
+ if (!m_Remapper->InstanceIDToSerializedObjectIdentifier(heapID, identifier))
+ {
+ if (loadProgress)
+ loadProgress->ItemProcessed ();
+ continue;
+ }
+
+ SerializedFile* stream = GetSerializedFileInternal (identifier.serializedFileIndex);
+ if (stream == NULL)
+ {
+ if (loadProgress)
+ loadProgress->ItemProcessed ();
+ continue;
+ }
+
+ // Find file id in stream and read the object
+
+ m_ActiveNameSpace.push (identifier.serializedFileIndex);
+ TypeTree* oldType;
+ bool didTypeTreeChange;
+
+ ThreadedAwakeData* awakeData = CreateThreadActivationQueueEntry (heapID);
+ awakeData->object = NULL;
+ stream->ReadObject (identifier.localIdentifierInFile, heapID, kCreateObjectFromNonMainThread, true, &oldType, &didTypeTreeChange, &awakeData->object);
+ if (loadProgress)
+ loadProgress->ItemProcessed ();
+
+ AssertIf (m_Remapper->IsSceneID (heapID));
+
+ SetupThreadActivationQueueObject(*awakeData, oldType, didTypeTreeChange);
+
+ m_ActiveNameSpace.pop ();
+}
+
+ m_IntegrationMutex.Lock();
+ m_OnDemandThreadLoadedObjects.clear();
+ m_IntegrationMutex.Unlock();
+
+ #if DEBUGMODE
+ m_IsLoadingSceneFile = false;
+ #endif
+ }
+
+int PersistentManager::LoadFileCompletelyThreaded (const std::string& pathname, LocalIdentifierInFileType* fileIDs, SInt32* instanceIDs, int size, bool loadScene, LoadProgress* loadProgress)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadFromActivationQueueStall);
+
+ // Find Stream
+ int pathID = InsertPathNameInternal (pathname, true);
+ SerializedFile* stream = GetSerializedFileInternal (pathID);
+ if (stream == NULL)
+ return kFileCouldNotBeRead;
+
+ AssertIf(fileIDs != NULL && size == -1);
+ AssertIf(instanceIDs != NULL && size == -1);
+
+ // Get all file IDs we want to load and generate instance ids
+ vector<LocalIdentifierInFileType> fileIDsVector;
+ vector<SInt32> instanceIDsVector;
+ if (size == -1)
+ {
+ stream->GetAllFileIDs (&fileIDsVector);
+ fileIDs = &fileIDsVector[0];
+ size = fileIDsVector.size();
+ if (loadProgress)
+ loadProgress->totalItems += size;
+ instanceIDsVector.resize(size);
+ instanceIDs = &instanceIDsVector[0];
+ }
+
+ // In the editor we can not use preallocate ranges since fileID's might be completely arbitrary ranges
+ if (loadScene && !UNITY_EDITOR)
+ {
+ LocalIdentifierInFileType highestFileID = 0;
+ for (int i=0;i<size;i++)
+ {
+ AssertIf(fileIDs[i] < 0);
+ highestFileID = max(highestFileID, fileIDs[i]);
+ }
+
+ m_Remapper->PreallocateIDs(highestFileID, pathID);
+
+ for (int i=0;i<size;i++)
+ {
+ LocalIdentifierInFileType fileID = fileIDs[i];
+ AssertIf(m_Remapper->IsSetup(SerializedObjectIdentifier(pathID, fileID)));
+ instanceIDs[i] = m_Remapper->m_ActivePreallocatedIDBase + fileID * 2;
+ }
+
+ #if DEBUGMODE
+ //SHOULDN"T BE NEEDED!!!!!! - TAKE THIS OUT DEBUG ONLY!!!!
+ CheckInstanceIDsLoaded(&instanceIDs[0], size);
+ for (int i=0;i<size;i++)
+ {
+ AssertIf(instanceIDs[i] == 0);
+ }
+ #endif
+ }
+ else
+ {
+ for (int i=0;i<size;i++)
+ {
+ LocalIdentifierInFileType fileID = fileIDs[i];
+ SInt32 heapID = m_Remapper->GetOrGenerateMemoryID (SerializedObjectIdentifier(pathID, fileID));
+
+ if (heapID == 0)
+ {
+ AssertString ("Loading an object that was made unpersistent but wasn't destroyed before reloading it");
+ }
+ instanceIDs[i] = heapID;
+ }
+ // - Figure out which ones are already loaded
+ CheckInstanceIDsLoaded(&instanceIDs[0], size);
+
+ #if UNITY_EDITOR
+ // Ugly hack to prevent IsPersistent to be on for scene objects.
+ // Recursive serialization is to be blamed for this. When that is fixed this can be removed.
+ if (loadScene)
+ {
+ m_Remapper->m_LoadingSceneInstanceIDs.assign_clear_duplicates(instanceIDs, instanceIDs + size);
+ }
+ #endif
+ }
+
+ // Load all objects
+ m_ActiveNameSpace.push (pathID);
+ #if DEBUGMODE
+ m_IsLoadingSceneFile = true;
+ #endif
+
+ for (int i=0;i<size;i++)
+ {
+ SInt32 heapID = instanceIDs[i];
+ SInt32 fileID = fileIDs[i];
+
+ if (heapID == 0)
+ {
+ if (loadProgress)
+ loadProgress->ItemProcessed ();
+ continue;
+ }
+
+ m_IntegrationMutex.Lock();
+ if (m_OnDemandThreadLoadedObjects.count(heapID))
+ {
+ if (loadProgress)
+ loadProgress->ItemProcessed ();
+ m_IntegrationMutex.Unlock();
+ continue;
+ }
+ m_IntegrationMutex.Unlock();
+
+ DebugAssertIf(Object::IDToPointerThreadSafe (heapID) != NULL);
+
+ TypeTree* oldType;
+ bool didTypeTreeChange;
+ ThreadedAwakeData* awakeData = CreateThreadActivationQueueEntry(heapID);
+ awakeData->object = NULL;
+ stream->ReadObject (fileID, heapID, kCreateObjectFromNonMainThread, !loadScene, &oldType, &didTypeTreeChange, &awakeData->object);
+ if (loadProgress)
+ loadProgress->ItemProcessed ();
+
+ ////@TODO: NEED TO HANDLE DELETION OF OBJECTS WHEN THAT HAPPENS BETWEEN threaded loading and activation
+ SetupThreadActivationQueueObject(*awakeData, oldType, didTypeTreeChange);
+ }
+
+ #if DEBUGMODE
+ m_IsLoadingSceneFile = false;
+ #endif
+
+ m_ActiveNameSpace.pop ();
+
+ m_IntegrationMutex.Lock();
+ m_OnDemandThreadLoadedObjects.clear();
+ m_IntegrationMutex.Unlock();
+ if (loadScene)
+ {
+ #if UNITY_EDITOR
+ for (int i=0;i<size;i++)
+ m_Remapper->Remove (instanceIDs[i]);
+ m_Remapper->m_LoadingSceneInstanceIDs.clear();
+ #else
+ m_Remapper->ClearPreallocateIDs();
+ #endif
+
+ }
+
+ return kNoError;
+}
+
+int PersistentManager::LoadFileCompletely (const string& path)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ int result = GetPersistentManager().LoadFileCompletelyThreaded(path, NULL, NULL, -1, false, (LoadProgress*)NULL);
+ GetPersistentManager().IntegrateAllThreadedObjects ();
+
+ return result;
+}
+
+
+#if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+void PersistentManager::RemapInstanceIDOnLoad (const std::string& srcPath, LocalIdentifierInFileType srcLocalFileID, const std::string& dstPath, LocalIdentifierInFileType dstLocalFileID)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ SerializedObjectIdentifier src;
+ SerializedObjectIdentifier dst;
+
+ src.serializedFileIndex = InsertPathNameInternal (srcPath, true);
+ src.localIdentifierInFile = srcLocalFileID;
+ Assert(src.serializedFileIndex != -1);
+
+ // Destination path needs to be inserted (in unity_web_old case)
+ dst.serializedFileIndex = InsertPathNameInternal (dstPath, true);
+ dst.localIdentifierInFile = dstLocalFileID;
+ Assert(dst.serializedFileIndex != -1);
+
+ m_InstanceIDRemap[src] = dst;
+}
+#endif // #if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+
+
+
+#if UNITY_EDITOR
+
+void PersistentManager::SuggestFileIDToHeapIDs (const string& pathname, map<LocalIdentifierInFileType, SInt32>& fileIDToHeapIDHint)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ int serializedFileIndex = InsertPathNameInternal (pathname, true);
+
+ SerializedFile* stream = GetSerializedFileInternal (serializedFileIndex);
+ if (stream == NULL)
+ return;
+
+ vector<LocalIdentifierInFileType> fileIDs;
+ stream->GetAllFileIDs (&fileIDs);
+
+ for (vector<LocalIdentifierInFileType>::iterator i=fileIDs.begin ();i!=fileIDs.end ();++i)
+ {
+ LocalIdentifierInFileType fileID = *i;
+ SerializedObjectIdentifier identifier (serializedFileIndex, fileID);
+
+ // fileIDToHeapIDHint is a hint which is used to keep the same fileID when entering/exiting playmode for example.
+ // It is only a hint and may not be fulfilled
+ if (!m_Remapper->IsSetup(identifier))
+ {
+ if (fileIDToHeapIDHint.count(fileID))
+ {
+ int suggestedHeapID = fileIDToHeapIDHint.find(fileID)->second;
+ if (Object::IDToPointer (suggestedHeapID) == NULL && !m_Remapper->IsHeapIDSetup(suggestedHeapID))
+ {
+ m_Remapper->SetupRemapping(suggestedHeapID, identifier);
+ }
+ }
+ }
+ }
+}
+#endif
+
+void PersistentManager::GetLoadedInstanceIDsAtPath (const string& pathName, set<SInt32>* objects)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ AssertIf (objects == NULL);
+
+ int serializedFileIndex = InsertPathNameInternal (pathName, false);
+ if (serializedFileIndex != -1)
+ {
+ // Get all objects that were made persistent but might not already be written to the file
+ m_Remapper->GetAllLoadedObjectsAtPath (serializedFileIndex, objects);
+ }
+}
+
+void PersistentManager::GetPersistentInstanceIDsAtPath (const string& pathName, set<SInt32>* objects)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ AssertIf (objects == NULL);
+
+ int pathID = InsertPathNameInternal (pathName, true);
+ if (pathID == -1)
+ return;
+
+ // Get all objects that were made persistent but might not already be written to the file
+ m_Remapper->GetAllPersistentObjectsAtPath (pathID, objects);
+}
+
+void PersistentManager::GetInstanceIDsAtPath (const string& pathName, set<SInt32>* objects)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ AssertIf (objects == NULL);
+
+ int serializedFileIndex = InsertPathNameInternal (pathName, true);
+ if (serializedFileIndex == -1)
+ return;
+
+ SerializedFile* serialize = GetSerializedFileInternal (serializedFileIndex);
+ if (serialize)
+ {
+ // Get all objects in the file
+ vector<LocalIdentifierInFileType> fileIDs;
+ serialize->GetAllFileIDs (&fileIDs);
+ for (vector<LocalIdentifierInFileType>::iterator i=fileIDs.begin ();i!=fileIDs.end ();++i)
+ {
+ SerializedObjectIdentifier identifier (serializedFileIndex, *i);
+ SInt32 memoryID = m_Remapper->GetOrGenerateMemoryID (identifier);
+
+ if (memoryID != 0)
+ objects->insert (memoryID);
+ }
+ }
+
+ // Get all objects that were made persistent but might not already be written to the file
+ m_Remapper->GetAllLoadedObjectsAtPath (serializedFileIndex, objects);
+}
+
+void PersistentManager::GetInstanceIDsAtPath (const string& pathName, vector<SInt32>* objects)
+{
+ set<SInt32> temp;
+ GetInstanceIDsAtPath(pathName, &temp);
+ objects->assign(temp.begin(), temp.end());
+}
+
+int PersistentManager::CountInstanceIDsAtPath (const string& pathName)
+{
+ set<SInt32> objects;
+ GetInstanceIDsAtPath (pathName, &objects);
+ return objects.size ();
+}
+
+bool PersistentManager::IsObjectAvailable (int heapID)
+{
+ PROFILER_AUTO_THREAD_SAFE(gIsObjectAvailable, NULL);
+
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ //////////////////////// DO WE WANT THIS!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ if (FindInActivationQueue(heapID))
+ return true;
+
+ SerializedObjectIdentifier identifier;
+
+ if (!m_Remapper->InstanceIDToSerializedObjectIdentifier(heapID, identifier))
+ return false;
+
+ SerializedFile* stream = GetSerializedFileInternal (identifier.serializedFileIndex);
+ // Stream can't be found
+ if (stream == NULL)
+ return false;
+
+ if (!stream->IsAvailable (identifier.localIdentifierInFile))
+ return false;
+
+ // Check if the class can be produced
+ int classID = stream->GetClassID (identifier.localIdentifierInFile);
+ Object::RTTI* rtti = Object::ClassIDToRTTI (classID);
+ if (rtti && !rtti->isAbstract)
+ return true;
+ else
+ return false;
+}
+
+bool PersistentManager::IsObjectAvailableDontCheckActualFile (int heapID)
+{
+ PROFILER_AUTO_THREAD_SAFE(gIsObjectAvailable, NULL);
+
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ int serializedFileIndex = m_Remapper->GetSerializedFileIndex (heapID);
+ if (serializedFileIndex == -1)
+ return false;
+ else
+ return true;
+}
+
+
+void PersistentManager::Lock()
+{
+ LOCK_MUTEX(m_Mutex, gLoadLockPersistentManager);
+}
+
+void PersistentManager::Unlock()
+{
+ m_Mutex.Unlock();
+}
+
+void PersistentManager::DoneLoadingManagers()
+{
+ // We are done loading managers. Start instance IDs from a high constant value here,
+ // so new managers and built-in resources can be added without changed instanceIDs
+ // used by the content.
+
+ //AssertIf(m_Remapper->GetHighestInUseHeapID() > 10000);
+
+ if (m_Remapper->m_HighestMemoryID < 10000)
+ {
+ m_Remapper->m_HighestMemoryID = 10000;
+ }
+
+ Object::DoneLoadingManagers();
+}
+
+
+PersistentManager& GetPersistentManager ()
+{
+ AssertIf (gPersistentManager == NULL);
+ return *gPersistentManager;
+}
+
+PersistentManager* GetPersistentManagerPtr ()
+{
+ return gPersistentManager;
+}
+
+void CleanupPersistentManager()
+{
+ UNITY_DELETE( gPersistentManager, kMemManager);
+ gPersistentManager = NULL;
+}
+
+PersistentManager::PersistentManager (int options, int cacheCount)
+// list node size is base data type size + two node pointers.
+:
+#if ENABLE_CUSTOM_ALLOCATORS_FOR_STDMAP
+ m_ThreadedAwakeDataPool (false, "Remapper pool", sizeof (ThreadedAwakeData) + sizeof(void*)*2, 16 * 1024),
+ m_ThreadedAwakeDataPoolMap (false, "RemapperMap pool", sizeof (ThreadedObjectActivationQueue::iterator) + sizeof(void*)*5, 16 * 1024),
+ m_ThreadedObjectActivationQueue (m_ThreadedAwakeDataPool),
+ m_ThreadedObjectActivationMap (std::less<SInt32>(), m_ThreadedAwakeDataPoolMap),
+#endif
+ m_AllowIntegrateThreadedObjectsWithTimeout (false)
+{
+ AssertIf (gPersistentManager);
+ gPersistentManager = this;
+ m_Options = options;
+ m_CacheCount = cacheCount;
+ m_Remapper = UNITY_NEW_AS_ROOT(Remapper (),kMemSerialization, kRemapperAllocArea, "");
+ #if DEBUGMODE
+ m_IsLoadingSceneFile = true;
+ #endif
+ #if DEBUGMODE
+ m_PreventLoadingFromFile = -1;
+ m_AllowLoadingFromDisk = true;
+ #endif
+
+ InitializeStdConverters ();
+}
+
+PersistentManager::~PersistentManager ()
+{
+ AssertIf(m_Mutex.IsLocked());
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ AssertIf (!m_ActiveNameSpace.empty ());
+
+ for (StreamContainer::iterator i=m_Streams.begin ();i!=m_Streams.end ();++i)
+ CleanupStream(*i);
+
+ UNITY_DELETE(m_Remapper, kMemSerialization);
+ CleanupStdConverters();
+}
+
+#if DEBUGMODE
+void PersistentManager::SetDebugAssertLoadingFromFile (const std::string& path)
+{
+ if (path.empty())
+ {
+ m_PreventLoadingFromFile = -1;
+ }
+ else
+ {
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+ m_PreventLoadingFromFile = InsertPathNameInternal (path, false);
+ }
+}
+#endif
+
+
+static bool IsPathBuiltinResourceFile (const std::string& pathName)
+{
+ return StrICmp(pathName, "library/unity default resources") == 0 || StrICmp(pathName, "library/unity_web_old") == 0
+ || StrICmp(pathName, "library/unity editor resources") == 0;
+}
+
+StreamNameSpace& PersistentManager::GetStreamNameSpaceInternal (int nameSpaceID)
+{
+#if DEBUGMODE
+ AssertIf(m_PreventLoadingFromFile == nameSpaceID);
+#endif
+
+ StreamNameSpace& nameSpace = m_Streams[nameSpaceID];
+
+ // Stream already loaded
+ if (nameSpace.stream)
+ return nameSpace;
+
+ #if SUPPORT_SERIALIZATION_FROM_DISK
+
+ PROFILER_AUTO_THREAD_SAFE(gLoadStreamNameSpaceProfiler, NULL);
+
+ // Load Stream
+ string pathName = PathIDToPathNameInternal (nameSpaceID);
+ if (pathName.empty ())
+ return nameSpace;
+
+ // File not found
+ string absolutePath = RemapToAbsolutePath (pathName);
+ if (!IsFileCreated (absolutePath))
+ {
+ #if !UNITY_EDITOR
+ AssertString("PersistentManager: Failed to open file at path: " + pathName);
+ #endif
+ return nameSpace;
+ }
+
+ // Is Builtin resource file?
+ int options = 0;
+ if (IsPathBuiltinResourceFile(pathName))
+ options |= kIsBuiltinResourcesFile;
+
+ nameSpace.stream = UNITY_NEW_AS_ROOT(SerializedFile, kMemSerialization, kSerializedFileArea, "");
+ SET_ALLOC_OWNER(nameSpace.stream);
+ #if ENABLE_MEM_PROFILER
+ nameSpace.stream->SetDebugPath(pathName);
+ GetMemoryProfiler()->SetRootAllocationObjectName(nameSpace.stream, nameSpace.stream->GetDebugPath().c_str());
+ #endif
+
+ // Resource image loading is only supported in the player!
+ ResourceImageGroup group;
+ #if SUPPORT_RESOURCE_IMAGE_LOADING
+ for (int i=0;i<kNbResourceImages;i++)
+ {
+ string resourceImagePath = AppendPathNameExtension(absolutePath, kResourceImageExtensions[i]);
+ if (IsFileCreated (resourceImagePath))
+ group.resourceImages[i] = new ResourceImage(resourceImagePath, i == kStreamingResourceImage);
+ }
+ #endif
+
+ if (!nameSpace.stream->InitializeRead (RemapToAbsolutePath (pathName), group, kCacheSize, m_CacheCount, options))
+ {
+ CleanupStream(nameSpace);
+ return nameSpace;
+ }
+
+ PostLoadStreamNameSpace(nameSpace, nameSpaceID);
+#endif
+
+ return m_Streams[nameSpaceID];
+}
+
+#if SUPPORT_SERIALIZATION_FROM_DISK
+bool PersistentManager::LoadCachedFile (const std::string& pathName, const std::string actualAbsolutePath)
+{
+ PROFILER_AUTO_THREAD_SAFE(gLoadStreamNameSpaceProfiler, NULL);
+
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ int nameSpaceID = InsertPathNameInternal (pathName, true);
+ if (nameSpaceID == -1)
+ return false;
+
+ StreamNameSpace& nameSpace = m_Streams[nameSpaceID];
+
+ // Stream already loaded
+ if (nameSpace.stream)
+ ErrorString ("Tryng to load a stream which is already loaded.");
+
+ // File not found
+ if (!IsFileCreated (actualAbsolutePath))
+ return false;
+
+ ResourceImageGroup group;
+ nameSpace.stream = UNITY_NEW_AS_ROOT(SerializedFile,kMemSerialization, kSerializedFileArea, "");
+ #if ENABLE_MEM_PROFILER
+ nameSpace.stream->SetDebugPath(actualAbsolutePath);
+ GetMemoryProfiler()->SetRootAllocationObjectName(nameSpace.stream, nameSpace.stream->GetDebugPath().c_str());
+ #endif
+ if (!nameSpace.stream->InitializeRead (actualAbsolutePath, group, kCacheSize, m_CacheCount, kSerializeGameRelease))
+ {
+ CleanupStream(nameSpace);
+ return false;
+ }
+
+ nameSpace.stream->SetIsCachedFileStream(true);
+ PostLoadStreamNameSpace(nameSpace, nameSpaceID);
+
+ Mutex::AutoLock lock2 (m_MemoryLoadedOrCachedPathsMutex);
+ m_MemoryLoadedOrCachedPaths.insert(pathName);
+
+ return true;
+}
+#endif
+
+bool PersistentManager::LoadMemoryBlockStream (const std::string& pathName, UInt8** data, int offset, int end, const char* url)
+{
+ PROFILER_AUTO_THREAD_SAFE(gLoadStreamNameSpaceProfiler, NULL);
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ int nameSpaceID = InsertPathNameInternal (pathName, true);
+ if (nameSpaceID == -1)
+ return false;
+
+ StreamNameSpace& nameSpace = m_Streams[nameSpaceID];
+
+ // Stream already loaded
+ AssertIf (nameSpace.stream);
+
+ nameSpace.stream = UNITY_NEW_AS_ROOT(SerializedFile,kMemSerialization, kSerializedFileArea, "");
+
+#if ENABLE_MEM_PROFILER
+ nameSpace.stream->SetDebugPath(url?url:pathName);
+ GetMemoryProfiler()->SetRootAllocationObjectName(nameSpace.stream, nameSpace.stream->GetDebugPath().c_str());
+#endif
+
+ int options = kSerializeGameRelease;
+
+ // In NaCl & Flash, default resources are not loaded from disk,
+ // but also from a web stream.
+ // Need to set the proper flag here, so they don't get unloaded
+ // in Garbage Collection.
+ if (IsPathBuiltinResourceFile(pathName))
+ options |= kIsBuiltinResourcesFile;
+
+ if (!nameSpace.stream->InitializeMemoryBlocks (RemapToAbsolutePath(pathName), data, end, offset, options))
+ {
+ CleanupStream(nameSpace);
+ return false;
+ }
+
+ PostLoadStreamNameSpace(nameSpace, nameSpaceID);
+
+ m_MemoryLoadedOrCachedPathsMutex.Lock();
+ m_MemoryLoadedOrCachedPaths.insert(pathName);
+ m_MemoryLoadedOrCachedPathsMutex.Unlock();
+
+ return true;
+}
+
+
+#if SUPPORT_SERIALIZATION_FROM_DISK
+bool PersistentManager::LoadExternalStream (const std::string& pathName, const std::string& absolutePath, int flags, int readOffset)
+{
+ PROFILER_AUTO_THREAD_SAFE(gLoadStreamNameSpaceProfiler, NULL);
+
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ int nameSpaceID = InsertPathNameInternal (pathName, true);
+ if (nameSpaceID == -1)
+ return false;
+
+ StreamNameSpace& nameSpace = m_Streams[nameSpaceID];
+
+ // Stream already loaded
+ if (nameSpace.stream)
+ return false;
+
+ // File not found
+ if (!IsFileCreated (absolutePath))
+ return false;
+
+ ResourceImageGroup group;
+ nameSpace.stream = UNITY_NEW_AS_ROOT(SerializedFile,kMemSerialization, kSerializedFileArea, "");
+
+#if ENABLE_MEM_PROFILER
+ nameSpace.stream->SetDebugPath(absolutePath);
+ GetMemoryProfiler()->SetRootAllocationObjectName(nameSpace.stream, nameSpace.stream->GetDebugPath().c_str());
+#endif
+
+ if (!nameSpace.stream->InitializeRead (absolutePath, group, kCacheSize, m_CacheCount, flags, readOffset))
+ {
+ CleanupStream(nameSpace);
+ return false;
+ }
+
+ nameSpace.stream->SetIsCachedFileStream(true);
+ PostLoadStreamNameSpace(nameSpace, nameSpaceID);
+
+ m_MemoryLoadedOrCachedPathsMutex.Lock();
+ m_MemoryLoadedOrCachedPaths.insert(pathName);
+ m_MemoryLoadedOrCachedPathsMutex.Unlock();
+
+ return true;
+}
+#endif
+
+void PersistentManager::PostLoadStreamNameSpace (StreamNameSpace& nameSpace, int nameSpaceID)
+{
+ nameSpace.highestID = std::max (nameSpace.highestID, nameSpace.stream->GetHighestID ());
+ SET_ALLOC_OWNER ( this );
+ const dynamic_block_vector<FileIdentifier>& externalRefs = nameSpace.stream->GetExternalRefs ();
+ // Read all local pathnames and generate global<->localnamespace mapping
+ for (unsigned int i=0;i!=externalRefs.size ();i++)
+ {
+ int serializedFileIndex = InsertFileIdentifierInternal (externalRefs[i], true);
+ m_GlobalToLocalNameSpace[nameSpaceID][serializedFileIndex] = i + 1;
+ m_LocalToGlobalNameSpace[nameSpaceID][i + 1] = serializedFileIndex;
+ }
+
+ // Setup global to self namespace mapping
+ m_GlobalToLocalNameSpace[nameSpaceID][nameSpaceID] = 0;
+ m_LocalToGlobalNameSpace[nameSpaceID][0] = nameSpaceID;
+}
+
+bool PersistentManager::IsFileEmpty (const string& pathName)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ SerializedFile* serialize = GetSerializedFileInternal (InsertPathNameInternal (pathName, true));
+ if (serialize == NULL)
+ return true;
+ else
+ return serialize->IsEmpty ();
+}
+
+#if UNITY_EDITOR
+bool PersistentManager::DeleteFile (const string& pathName, DeletionFlags flag)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ int globalNameSpace = InsertPathNameInternal (pathName, true);
+ if (globalNameSpace == -1)
+ return false;
+
+ StreamNameSpace& stream = GetStreamNameSpaceInternal (globalNameSpace);
+
+ set<SInt32> objectsInFile;
+ GetInstanceIDsAtPath (pathName, &objectsInFile);
+
+ for (set<SInt32>::iterator i=objectsInFile.begin ();i != objectsInFile.end ();++i)
+ MakeObjectUnpersistent (*i, kDontDestroyFromFile);
+
+ if (flag & kDeleteLoadedObjects)
+ {
+ for (set<SInt32>::iterator i=objectsInFile.begin ();i != objectsInFile.end ();++i)
+ {
+ Object* obj = Object::IDToPointer(*i);
+ UnloadObject(obj);
+ }
+
+ #if DEBUGMODE
+ for (set<SInt32>::iterator i=objectsInFile.begin ();i != objectsInFile.end ();++i)
+ AssertIf (PPtr<Object> (*i));
+ #endif
+ }
+
+ #if DEBUGMODE
+ for (set<SInt32>::iterator i=objectsInFile.begin ();i != objectsInFile.end ();++i)
+ {
+ if (m_Remapper->GetSerializedFileIndex (*i) != -1)
+ {
+ Object* obj = PPtr<Object>(*i);
+ ErrorStringObject("NOT CORRECTLY UNLOADED!!!!", obj);
+ }
+ }
+ #endif
+
+ if (stream.stream)
+ CleanupStream(stream);
+
+ m_GlobalToLocalNameSpace[globalNameSpace].clear ();
+ m_LocalToGlobalNameSpace[globalNameSpace].clear ();
+
+ string absolutePath = RemapToAbsolutePath (pathName);
+ if (IsFileCreated (absolutePath))
+ {
+ if (!::DeleteFile (absolutePath))
+ return false;
+ }
+ return true;
+}
+
+#endif
+
+void PersistentManager::UnloadNonDirtyStreams ()
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+ // printf_console("------ Unloading non dirty stream\n");
+ int dirtyStreams = 0;
+ int loadedStreams = 0;
+ int unloadedStreams = 0;
+ for (int i=0;i<m_Streams.size ();i++)
+ {
+ StreamNameSpace& nameSpace = m_Streams[i];
+ AssertIf (nameSpace.stream == NULL && !m_GlobalToLocalNameSpace[i].empty ());
+ if (nameSpace.stream == NULL)
+ continue;
+
+ if (nameSpace.stream->IsMemoryStream () || nameSpace.stream->IsCachedFileStream())
+ {
+ loadedStreams++;
+ continue;
+ }
+
+ bool unloadStream = true;
+ #if UNITY_EDITOR
+ unloadStream = !nameSpace.stream->IsFileDirty();
+ #endif
+
+ if (unloadStream)
+ {
+ unloadedStreams++;
+ CleanupStream(nameSpace);
+ m_GlobalToLocalNameSpace[i].clear ();
+ m_LocalToGlobalNameSpace[i].clear ();
+ }
+ else
+ {
+ FileIdentifier identifier = PathIDToFileIdentifierInternal(i);
+ printf_console("Can't unload serialized file because it is dirty: %s\n", identifier.pathName.c_str());
+ dirtyStreams++;
+ loadedStreams++;
+ }
+ }
+
+ printf_console("Unloading %d Unused Serialized files (Serialized files now loaded: %d / Dirty serialized files: %d)\n", unloadedStreams, loadedStreams, dirtyStreams);
+// printf_console("Streams that can't be unloaded Files %d\n", loadedStreams);
+// printf_console("ID mapping count %d -- %d \n", m_Remapper->m_FileToHeapID.size(), m_Remapper->m_FileToHeapID.size() * 24);
+// printf_console("RESURRRECT count %d -- %d \n", m_Remapper->m_ResurrectHeapID.size(), m_Remapper->m_ResurrectHeapID.size() * 24);
+}
+
+
+void PersistentManager::UnloadStreams ()
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ for (int i=0;i<m_Streams.size ();i++)
+ {
+ StreamNameSpace& nameSpace = m_Streams[i];
+ AssertIf (nameSpace.stream == NULL && !m_GlobalToLocalNameSpace[i].empty ());
+ if (nameSpace.stream == NULL)
+ continue;
+/*
+ #if DEBUGMODE
+ set<SInt32> debug;
+ m_Remapper->GetAllLoadedObjectsAtPath (i, &debug);
+ AssertIf (!debug.empty ());
+ #endif
+*/
+ CleanupStream(nameSpace);
+
+ m_GlobalToLocalNameSpace[i].clear ();
+ m_LocalToGlobalNameSpace[i].clear ();
+ }
+}
+
+void PersistentManager::UnloadMemoryStreams ()
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ for (int i=0;i<m_Streams.size ();i++)
+ {
+ StreamNameSpace& nameSpace = m_Streams[i];
+ AssertIf (nameSpace.stream == NULL && !m_GlobalToLocalNameSpace[i].empty ());
+ if (nameSpace.stream == NULL )
+ continue;
+
+ if (!nameSpace.stream->IsMemoryStream () && !nameSpace.stream->IsCachedFileStream())
+ continue;
+
+ #if DEBUGMODE
+ set<SInt32> debug;
+ m_Remapper->GetAllLoadedObjectsAtPath (i, &debug);
+ AssertIf (!debug.empty ());
+ #endif
+
+ CleanupStream(nameSpace);
+
+ m_GlobalToLocalNameSpace[i].clear ();
+ m_LocalToGlobalNameSpace[i].clear ();
+ }
+
+ Mutex::AutoLock lock2 (m_MemoryLoadedOrCachedPathsMutex);
+ m_MemoryLoadedOrCachedPaths.clear();
+}
+
+void PersistentManager::UnloadStream (const std::string& pathName)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ int nameSpaceID = InsertPathNameInternal (pathName, false);
+ if (nameSpaceID == -1)
+ return;
+
+ StreamNameSpace& nameSpace = m_Streams[nameSpaceID];
+ if (nameSpace.stream == NULL)
+ return;
+
+ CleanupStream(nameSpace);
+
+ m_GlobalToLocalNameSpace[nameSpaceID].clear ();
+ m_LocalToGlobalNameSpace[nameSpaceID].clear ();
+
+ Mutex::AutoLock lock2 (m_MemoryLoadedOrCachedPathsMutex);
+ m_MemoryLoadedOrCachedPaths.erase(pathName);
+}
+
+bool PersistentManager::HasMemoryOrCachedSerializedFile (const std::string& path)
+{
+ Mutex::AutoLock lock2 (m_MemoryLoadedOrCachedPathsMutex);
+ return m_MemoryLoadedOrCachedPaths.count(path) == 1;
+}
+
+void PersistentManager::ResetHighestFileIDAtPath (const string& pathName)
+{
+ AQUIRE_AUTOLOCK(m_Mutex, gLoadLockPersistentManager);
+
+ int globalNameSpace = InsertPathNameInternal (pathName, true);
+ if (globalNameSpace == -1)
+ return;
+
+ AssertIf (m_Streams[globalNameSpace].stream != NULL);
+ m_Streams[globalNameSpace].highestID = 0;
+}
+
+#if UNITY_EDITOR
+// AwakeFromLoadQueue only supports this in the editor
+void PersistentManager::RegisterSafeBinaryReadCallback (SafeBinaryReadCallbackFunction* callback)
+{
+ AssertIf (gSafeBinaryReadCallback);
+ gSafeBinaryReadCallback = callback;
+}
+#endif
+
+void PersistentManager::RegisterInOrderDeleteCallback (InOrderDeleteCallbackFunction* callback)
+{
+ AssertIf (gInOrderDeleteCallback);
+ gInOrderDeleteCallback = callback;
+}
+
+SerializedFile* PersistentManager::GetSerializedFileInternal (const string& path)
+{
+ return GetSerializedFileInternal(InsertPathNameInternal (path, true));
+}
+
+SerializedFile* PersistentManager::GetSerializedFileInternal (int serializedFileIndex)
+{
+ if (serializedFileIndex == -1)
+ return NULL;
+
+ StreamNameSpace& stream = GetStreamNameSpaceInternal (serializedFileIndex);
+ return stream.stream;
+}
+
+
+bool PersistentManager::IsStreamLoaded (const std::string& pathName)
+{
+ AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(m_Mutex, gLoadLockPersistentManager);
+
+ int globalNameSpace = InsertPathNameInternal (pathName, false);
+ if (globalNameSpace == -1)
+ return false;
+
+ StreamNameSpace& nameSpace = m_Streams[globalNameSpace];
+ return nameSpace.stream != NULL;
+}
+
+void PersistentManager::AddStream ()
+{
+ m_Streams.push_back (StreamNameSpace ());
+ m_GlobalToLocalNameSpace.push_back (IDRemap ());
+ m_LocalToGlobalNameSpace.push_back (IDRemap ());
+}
+
+void PersistentManager::DestroyFromFileInternal (int memoryID)
+{
+ SerializedObjectIdentifier identifier;
+ m_Remapper->InstanceIDToSerializedObjectIdentifier(memoryID, identifier);
+ SerializedFile* serialize = GetSerializedFileInternal (identifier.serializedFileIndex);
+ if (serialize)
+ serialize->DestroyObject (identifier.localIdentifierInFile);
+}
+
+string PersistentManager::RemapToAbsolutePath (const string& path)
+{
+ UserPathRemap::iterator found = m_UserPathRemap.find(path);
+ if (found != m_UserPathRemap.end())
+ return found->second;
+
+ return PathToAbsolutePath(path);
+}
+
+void PersistentManager::SetPathRemap (const string& path, const string& absoluteRemappedPath)
+{
+ if (!absoluteRemappedPath.empty())
+ {
+ Assert (m_UserPathRemap.count(path) == 0);
+ m_UserPathRemap.insert(make_pair(path, absoluteRemappedPath));
+ }
+ else
+ {
+ Assert (m_UserPathRemap.count(path) == 1);
+ m_UserPathRemap.erase(path);
+ }
+}
+
+#if UNITY_EDITOR
+bool PersistentManager::IsClassNonTextSerialized( int cid )
+{
+ return m_NonTextSerializedClasses.find (cid) != m_NonTextSerializedClasses.end();
+}
+#endif
diff --git a/Runtime/Serialize/PersistentManager.h b/Runtime/Serialize/PersistentManager.h
new file mode 100644
index 0000000..73f3f67
--- /dev/null
+++ b/Runtime/Serialize/PersistentManager.h
@@ -0,0 +1,473 @@
+#ifndef PERSISTENTMANAGER_H
+#define PERSISTENTMANAGER_H
+
+
+#define SUPPORT_INSTANCE_ID_REMAP_ON_LOAD (UNITY_EDITOR || WEBPLUG)
+
+class SerializedFile;
+class Object;
+class TypeTree;
+struct FileIdentifier;
+
+#include <map>
+#include <string>
+#include <vector>
+#include <stack>
+#include <deque>
+#include <set>
+#include "Runtime/Utilities/vector_map.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Utilities/CStringHash.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/Mutex.h"
+#include "WriteData.h"
+#include "Runtime/Utilities/MemoryPool.h"
+#include "LoadProgress.h"
+
+using std::map;
+using std::set;
+using std::vector;
+using std::string;
+using std::stack;
+
+class AwakeFromLoadQueue;
+class Remapper;
+
+struct StreamNameSpace
+{
+ SerializedFile* stream;
+ LocalIdentifierInFileType highestID;
+
+ StreamNameSpace () { stream = NULL; highestID = 0; }
+};
+
+enum
+{
+ kNoError = 0,
+ kFileCouldNotBeRead = 1,
+ kTypeTreeIsDifferent = 2,
+ kFileCouldNotBeWritten = 3
+};
+
+enum UnpersistMode {
+ kDontDestroyFromFile = 0,
+ kDestroyFromFile = 1
+};
+
+
+struct ThreadedAwakeData
+{
+ SInt32 instanceID;
+ TypeTree* oldType;
+ Object* object;
+ bool checkConsistency; /// refactor to safeLoaded
+
+ // Has the object been fully loaded with AwakeFromLoadThreaded.
+ // We have to make sure the Object* is available already, so that recursive PPtr's to each other from Mono can correctly be resolved.
+ // In this case, neither object has been fully created, but we can setup pointers between them already.
+ bool completedThreadAwake;
+};
+
+struct SerializedObjectIdentifier
+{
+ SInt32 serializedFileIndex;
+ LocalIdentifierInFileType localIdentifierInFile;
+
+ SerializedObjectIdentifier (SInt32 inSerializedFileIndex, LocalIdentifierInFileType inLocalIdentifierInFile)
+ : serializedFileIndex (inSerializedFileIndex)
+ , localIdentifierInFile (inLocalIdentifierInFile)
+ { }
+
+ SerializedObjectIdentifier ()
+ : serializedFileIndex (0)
+ , localIdentifierInFile (0)
+ { }
+
+
+ friend bool operator < (const SerializedObjectIdentifier& lhs, const SerializedObjectIdentifier& rhs)
+ {
+ if (lhs.serializedFileIndex < rhs.serializedFileIndex)
+ return true;
+ else if (lhs.serializedFileIndex > rhs.serializedFileIndex)
+ return false;
+ else
+ return lhs.localIdentifierInFile < rhs.localIdentifierInFile;
+ }
+
+ friend bool operator != (const SerializedObjectIdentifier& lhs, const SerializedObjectIdentifier& rhs)
+ {
+ return lhs.serializedFileIndex != rhs.serializedFileIndex || lhs.localIdentifierInFile != rhs.localIdentifierInFile;
+ }
+};
+
+struct LocalSerializedObjectIdentifier;
+
+// There are three types of ids.
+// fileID, is an id that is local to a file. It ranges from [1 ... kNameSpaceSize]
+// heapID, is an id that was allocated for an object that was not loaded from disk. [1 ... infinity]
+// pathID, is an id to a file. Every file has a unique id. They are not recycled unless you delete the PersistentManager
+
+class PersistentManager
+{
+ protected:
+ enum
+ {
+// Recursive serialization causes the deflated stream to be reset repeatedly - use bigger cache chunks to limit the impact on load times.
+#if UNITY_ANDROID
+ kCacheSize = 1024 * 256
+#elif UNITY_XBOX360
+ kCacheSize = 1024 * 32
+#elif UNITY_WINRT || UNITY_BB10
+ kCacheSize = 1024 * 64
+#else
+ kCacheSize = 1024 * 7
+#endif
+ };
+
+ typedef std::pair<SInt32, SInt32> IntPair;
+ typedef vector_map<SInt32, SInt32, std::less<SInt32>, STL_ALLOCATOR(kMemSerialization, IntPair) > IDRemap;
+ typedef UNITY_VECTOR(kMemSerialization,StreamNameSpace) StreamContainer;
+
+ StreamContainer m_Streams;
+ UNITY_VECTOR(kMemSerialization,IDRemap) m_GlobalToLocalNameSpace;
+ UNITY_VECTOR(kMemSerialization,IDRemap) m_LocalToGlobalNameSpace;
+ Remapper* m_Remapper;
+
+ typedef std::pair<std::string,std::string> StringPair;
+ typedef vector_map<std::string, std::string, compare_string_insensitive,STL_ALLOCATOR(kMemSerialization,StringPair)> UserPathRemap;
+ UserPathRemap m_UserPathRemap;
+
+ #if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+ typedef vector_map<SerializedObjectIdentifier, SerializedObjectIdentifier,
+ std::less<SerializedObjectIdentifier>,STL_ALLOCATOR(kMemSerialization,SerializedObjectIdentifier) > InstanceIDRemap;
+ InstanceIDRemap m_InstanceIDRemap;
+ #endif // #if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+
+ #if UNITY_EDITOR
+ UNITY_SET(kMemSerialization,int) m_NonTextSerializedClasses;
+ #endif
+
+ SInt32 m_CacheCount;
+
+ stack<SInt32, std::deque<SInt32, STL_ALLOCATOR(kMemSerialization,SInt32) > > m_ActiveNameSpace;
+ int m_Options;
+
+ #if DEBUGMODE
+ bool m_AllowLoadingFromDisk;
+ bool m_IsLoadingSceneFile;
+ int m_PreventLoadingFromFile;
+ #endif
+
+ UNITY_SET(kMemSerialization, std::string) m_MemoryLoadedOrCachedPaths;
+
+ Mutex m_Mutex;
+ Mutex m_IntegrationMutex;
+
+ Mutex m_MemoryLoadedOrCachedPathsMutex;
+
+ bool m_AllowIntegrateThreadedObjectsWithTimeout; // Mutex protected by m_IntegrationMutex
+
+#if ENABLE_CUSTOM_ALLOCATORS_FOR_STDMAP
+ MemoryPool m_ThreadedAwakeDataPool;
+ MemoryPool m_ThreadedAwakeDataPoolMap;
+ typedef std::list<ThreadedAwakeData, memory_pool_explicit<ThreadedAwakeData> > ThreadedObjectActivationQueue;
+ typedef std::map<SInt32, ThreadedObjectActivationQueue::iterator, std::less<SInt32>, memory_pool_explicit<ThreadedObjectActivationQueue::iterator> > ThreadedObjectActivationMap;
+#else
+ typedef std::list<ThreadedAwakeData> ThreadedObjectActivationQueue;
+ typedef std::map<SInt32, ThreadedObjectActivationQueue::iterator> ThreadedObjectActivationMap;
+#endif
+ ThreadedObjectActivationQueue m_ThreadedObjectActivationQueue; // protected by m_IntegrationMutex
+ ThreadedObjectActivationMap m_ThreadedObjectActivationMap; // protected by m_IntegrationMutex
+ UNITY_SET(kMemSerialization, SInt32) m_OnDemandThreadLoadedObjects; /// DONT USE POOL HERE NOT THREAD SAFE
+
+ StreamNameSpace& GetStreamNameSpaceInternal (int nameSpaceID);
+ void DestroyFromFileInternal (int memoryID);
+ public:
+
+ PersistentManager (int options, int cacheCount);
+ virtual ~PersistentManager ();
+
+ /// Loads all objects in pathName
+ /// Returns kNoError, kFileCouldNotBeRead
+ int LoadFileCompletely (const string& pathname);
+
+ #if UNITY_EDITOR
+ // Makes an object persistent and generates a unique fileID in pathName
+ // The object can now be referenced by other objects that write to disk
+ void MakeObjectPersistent (int heapID, const string& pathName);
+ // Makes an object persistent if fileID == 0 a new unique fileID in pathName will be generated
+ // The object can now be referenced by other objects that write to disk
+ // If the object is already persistent in another file or another fileID it will be destroyed from that file.
+ void MakeObjectPersistentAtFileID (int heapID, LocalIdentifierInFileType fileID, const string& pathName);
+
+ /// Batch multiple heapID's and fileID's into one path name.
+ /// on return fileID's will contain the file id's that were generated (if fileIds[i] is non-zero that fileID will be used instead)
+ enum { kMakePersistentDontRequireToBeLoadedAndDontUnpersist = 1 << 0, kAllowDontSaveObjectsToBePersistent = 1 << 1 };
+ void MakeObjectsPersistent (const int* heapIDs, LocalIdentifierInFileType* fileIDs, int size, const string& pathName, int options = 0);
+ #endif
+
+ // Makes an object unpersistent
+ void MakeObjectUnpersistent (int memoryID, UnpersistMode unpersistMode);
+
+ bool RemoveObjectsFromPath (const std::string& pathName);
+
+ // Returns the pathname the referenced object is stored at, if its not persistent empty string is returned
+ string GetPathName (SInt32 memoryID);
+ // Returns the localFileID the referenced object has inside its file.
+
+ bool InstanceIDToSerializedObjectIdentifier (int instanceID, SerializedObjectIdentifier& identifier);
+ int SerializedObjectIdentifierToInstanceID (const SerializedObjectIdentifier& identifier);
+
+ LocalIdentifierInFileType GetLocalFileID(SInt32 instanceID);
+
+ // Generates or returns an instanceID from path and fileID which can then
+ // be used to load the object at that instanceID
+ ///@TODO: RENAME TO LocalIdentifierInFile
+ SInt32 GetInstanceIDFromPathAndFileID (const string& path, LocalIdentifierInFileType localIdentifierInFile);
+
+ // Returns classID from path and fileID.
+ int GetClassIDFromPathAndFileID (const string& path, LocalIdentifierInFileType localIdentifierInFile);
+
+ // Reads the object referenced by heapID, if there is no object with heapID, the object will first be produced.
+ // Returns the created and read object, or NULL if the object couldnt be found or was destroyed.
+ Object* ReadObject (int heapID);
+
+ // Unloads all streams that are open.
+ // After UnloadStreams is called files may be safely replaced.
+ // May only be called if there are no dirty files open.
+ void UnloadStreams ();
+ void UnloadStream (const std::string& pathName);
+
+ bool IsStreamLoaded (const std::string& pathName);
+
+ #if UNITY_EDITOR
+
+ typedef bool VerifyWriteObjectCallback (Object* verifyDeployment, BuildTargetPlatform target);
+
+ // Writes all persistent objects in memory that are made peristent at pathname to the file
+ // And completes all write operation (including writing the header)
+ // Returns the error (kNoError)
+ // options: kSerializeGameRelease, kSwapEndianess, kBuildPlayerOnlySerializeBuildProperties
+ int WriteFile (const string& pathName, BuildTargetSelection target = BuildTargetSelection::NoTarget(), int options = 0);
+
+ bool IsClassNonTextSerialized(int cid);
+
+ int WriteFileInternal (const std::string& path, int serializedFileIndex, const WriteData* writeData, int size, VerifyWriteObjectCallback* verifyCallback, BuildTargetSelection target, int options);
+
+ #if UNITY_EDITOR
+ bool TestNeedWriteFile (const std::string& pathName, const std::set<int>* dirtyPaths = NULL);
+ bool TestNeedWriteFile (int pathID, const std::set<int>* dirtyPaths = NULL);
+ #endif
+
+ // Delete file deletes the file referenced by pathName
+ // Makes all loaded objects unpersistent
+ // deleteLoadedObjects & kDeleteLoadedObjects -> All objects on the disk will be attempted to be destroyed
+ // deleteLoadedObjects & kDontDeleteLoadedObjects -> Doesn't delete any loaded objects, but marks them unpersistent from the file.
+ enum DeletionFlags { kDontDeleteLoadedObjects = 0, kDeleteLoadedObjects = 1 << 0 };
+ bool DeleteFile (const string& pathName, DeletionFlags flag);
+
+ void AddNonTextSerializedClass (int classID) { m_NonTextSerializedClasses.insert (classID); }
+ #endif
+
+ // On return: objects are the instanceIDs of all objects resident in the file referenced by pathName
+ typedef std::set<SInt32> ObjectIDs;
+ void GetInstanceIDsAtPath (const string& pathName, ObjectIDs* objects);
+ void GetInstanceIDsAtPath (const string& pathName, vector<SInt32>* objects);
+
+ void GetLoadedInstanceIDsAtPath (const string& pathName, ObjectIDs* objects);
+ void GetPersistentInstanceIDsAtPath (const string& pathName, std::set<SInt32>* objects);
+
+ int CountInstanceIDsAtPath (const string& pathName);
+
+ void SetAllowIntegrateThreadedObjectsWithTimeout (bool value);
+
+ bool IsFileEmpty (const string& pathName);
+
+ // Finds out if the referenced object can be loaded.
+ // By looking for it on the disk. And checking if the classID can be produced.
+ bool IsObjectAvailable (int heapID);
+ bool IsObjectAvailableDontCheckActualFile (int heapID);
+
+ void GetAllFileIDs (const string& pathName, vector<LocalIdentifierInFileType>* objects);
+
+ // Finds the
+ int GetSerializedClassID (int instanceID);
+
+
+
+
+ // Resets the fileIDs. This can only be used if the file has just been deleted.
+ void ResetHighestFileIDAtPath (const string& pathName);
+
+ // Computes the memoryID (object->GetInstanceID ()) from fileID
+ // fileID is relative to the file we are currently writing/reading from.
+ // It can only be called when reading/writing objects in order to
+ // convert ptrs from file space to global space
+ void LocalSerializedObjectIdentifierToInstanceIDInternal (const LocalSerializedObjectIdentifier& identifier, SInt32& memoryID);
+ void LocalSerializedObjectIdentifierToInstanceIDInternal (int activeNameSpace, const LocalSerializedObjectIdentifier& localIdentifier, SInt32& outInstanceID);
+
+ // fileID from memory ID (object->GetInstanceID ())
+ // It can only be called when reading/writing objects in order
+ // to convert ptrs from global space to file space
+ void InstanceIDToLocalSerializedObjectIdentifierInternal (SInt32 memoryID, LocalSerializedObjectIdentifier& identifier);
+
+ // Translates globalIdentifier.serializedFileIndex from a global index into the local file index based on what file we are currently writing.
+ // It can only be called when reading/writing objects in order
+ // to convert ptrs from global space to file space
+ LocalSerializedObjectIdentifier GlobalToLocalSerializedFileIndexInternal (const SerializedObjectIdentifier& globalIdentifier);
+
+ /// Is this instanceID mapped to the file we are currently writing,
+ /// in other words is the referenced instanceID read or written from the same file then this will return true.
+ bool IsInstanceIDFromCurrentFileInternal (SInt32 memoryID);
+
+ #if UNITY_EDITOR
+ // Hints a fileID to heap id mapping.
+ // This i used to keep similar instanceID's when entering / exiting playmode
+ void SuggestFileIDToHeapIDs (const string& pathname, std::map<LocalIdentifierInFileType, SInt32>& fileIDToHeapIDHint);
+
+ int GetSerializedFileIndexFromPath (const std::string& path);
+
+ #endif
+
+ #if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+ void RemapInstanceIDOnLoad (const std::string& srcPath, LocalIdentifierInFileType srcLocalFileID, const std::string& dstPath, LocalIdentifierInFileType dstLocalFileID);
+ void ApplyInstanceIDRemap(SerializedObjectIdentifier& id);
+ #endif // #if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+
+ /// NOTE: Returns an object that has not been completely initialized (Awake has not been called yet)
+ Object* ReadObjectThreaded (int heapID);
+
+ /// Check if we have objects to integrate, ie. if calling IntegrateThreadedObjects will perform some useful work
+ bool HasThreadedObjectsToIntegrate ();
+
+ /// Integrates all thread loaded objects into the world (Called from PlayerLoop)
+ void IntegrateThreadedObjects (float timeout);
+
+ /// Called from outise the loading thread (non-main thread), allows IntegrateThreadedObjects to be called with time slicing
+ /// Stalls until all objects have been integrated
+ void AllowIntegrationWithTimeoutAndWait ();
+
+ void IntegrateAllThreadedObjects ();
+
+
+ void PrepareAllThreadedObjectsStep1 (AwakeFromLoadQueue& awakeQueue);
+ void IntegrateAllThreadedObjectsStep2 (AwakeFromLoadQueue& awakeQueue/*, bool loadScene*/);
+
+
+
+ /// Load the entire file from a different thread
+ int LoadFileCompletelyThreaded (const std::string& pathname, LocalIdentifierInFileType* fileIDs, SInt32* instanceIDs, int size, bool loadScene, LoadProgress* loadProgress);
+
+ /// Loads a number of objects threaded
+ void LoadObjectsThreaded (SInt32* heapIDs, int size, LoadProgress* loadProgress);
+
+ #if SUPPORT_THREADS
+ const Mutex& GetMutex () { return m_Mutex; }
+ Thread::ThreadID GetMainThreadID () { return Thread::mainThreadId; }
+ #endif
+
+ #if DEBUGMODE
+ /// Allows you to assert on any implicit loading operations for a specific file
+ void SetDebugAssertLoadingFromFile (const std::string& path);
+ /// Allows you to assert on any implicit loading operations, because for example when unloading objects that is usually not desired.
+ void SetDebugAssertAllowLoadingAnything (bool allowLoading) { m_AllowLoadingFromDisk = allowLoading; }
+
+ void SetIsLoadingSceneFile(bool inexplicit) { m_IsLoadingSceneFile = inexplicit; }
+ #endif
+
+ void Lock();
+ void Unlock();
+ /// Loads the contents of the object from disk again
+ /// Performs serialization and calls AwakeFromLoad
+ bool ReloadFromDisk (Object* obj);
+
+ /// Load a memory stream directly from memory
+ /// - You should call this function only on assets that have been writting using kSerializeGameReleaswe
+ bool LoadMemoryBlockStream (const std::string& pathName, UInt8** data, int offset, int end, const char* url = NULL);
+
+ // Loads a file at actualAbsolutePath and pretends that it is actually at path.
+ /// - You should call this function only on assets that have been writting using kSerializeGameReleaswe
+ bool LoadCachedFile (const std::string& path, const std::string actualAbsolutePath);
+
+ void UnloadMemoryStreams ();
+
+ // A registered SafeBinaryReadCallbackFunction will be called when an objects old typetree is different from the new one
+ // and variables might have gone away, added, or changed
+ typedef void SafeBinaryReadCallbackFunction (Object& object, const TypeTree& oldTypeTree);
+ static void RegisterSafeBinaryReadCallback (SafeBinaryReadCallbackFunction* callback);
+
+ // In order for DeleteFile to work you have to support a callback that deletes the objects referenced by instanceID
+ typedef void InOrderDeleteCallbackFunction (const set<SInt32>& objects, bool safeDestruction);
+ static void RegisterInOrderDeleteCallback (InOrderDeleteCallbackFunction* callback);
+
+ /// Thread locking must be performed from outside using Lock/Unlock
+ SerializedFile* GetSerializedFileInternal (const string& path);
+ SerializedFile* GetSerializedFileInternal (int serializedFileIndex);
+
+ void SetPathRemap (const string& path, const string& absoluteRemappedPath);
+
+ // Non-Locking method to find out if a memorystream or cached file is set up.
+ bool HasMemoryOrCachedSerializedFile (const std::string& path);
+
+ #if SUPPORT_SERIALIZATION_FROM_DISK
+ bool LoadExternalStream (const std::string& pathName, const std::string& absolutePath, int flags, int readOffset = 0);
+ #endif
+
+ void UnloadNonDirtyStreams ();
+
+ /// NOTE: Function postfixed Internal are not thread safe and you have to call PersistentManager.Lock / Unlock prior to calling them from outside persistentmanager
+
+
+ //// Subclasses have to override these methods which map from PathIDs to FileIdentifier
+ /// Maps a pathname/fileidentifier to a pathID. If the pathname is not yet known, you have to call AddStream ().
+ /// The pathIDs start at 0 and increment by 1
+ virtual int InsertFileIdentifierInternal (FileIdentifier file, bool create) = 0;
+
+ std::string RemapToAbsolutePath (const std::string& path);
+
+ void DoneLoadingManagers();
+
+ protected:
+
+ virtual int InsertPathNameInternal (const std::string& pathname, bool create) = 0;
+
+ /// maps a pathID to a pathname/file guid/fileidentifier.
+ /// (pathID can be assumed to be allocated before with InsertPathName)
+ virtual string PathIDToPathNameInternal (int pathID) = 0;
+ virtual FileIdentifier PathIDToFileIdentifierInternal (int pathID) = 0;
+
+ /// Adds a new empty stream. Used by subclasses inside InsertPathName when a new pathID has to be added
+ void AddStream ();
+
+ private:
+
+ void CleanupStreamAndNameSpaceMapping (unsigned pathID);
+
+ void RegisterAndAwakeThreadedObjectAndUnlockIntegrationMutex (const ThreadedAwakeData& awake);
+
+ ThreadedAwakeData* CreateThreadActivationQueueEntry (SInt32 instanceID);
+ void SetupThreadActivationQueueObject (ThreadedAwakeData& data, TypeTree* oldType, bool didTypeTreeChange);
+
+ /// Goes through object activation queue and calls AwakeFromLoad if it has been serialized already but not AwakeFromLoad called.
+ Object* LoadFromActivationQueue (int heapID);
+ Object* GetFromActivationQueue (int heapID);
+ bool FindInActivationQueue (int heapID);
+ void CheckInstanceIDsLoaded (SInt32* heapIDs, int size);
+
+ protected:
+
+ void PostLoadStreamNameSpace (StreamNameSpace& nameSpace, int namespaceID);
+
+#if UNITY_EDITOR
+ bool TestNeedWriteFileInternal (int pathID, const std::set<int>* cachedDirtyPathsHint);
+#endif
+
+ friend class Object;
+};
+
+PersistentManager& GetPersistentManager ();
+PersistentManager* GetPersistentManagerPtr ();
+
+void CleanupPersistentManager();
+
+#endif
diff --git a/Runtime/Serialize/Remapper.h b/Runtime/Serialize/Remapper.h
new file mode 100644
index 0000000..6e1debc
--- /dev/null
+++ b/Runtime/Serialize/Remapper.h
@@ -0,0 +1,297 @@
+#ifndef REMAPPER_H
+#define REMAPPER_H
+
+#include <limits>
+#include "Runtime/Utilities/MemoryPool.h"
+#include "Configuration/UnityConfigure.h"
+
+class Remapper
+{
+ public:
+#if ENABLE_CUSTOM_ALLOCATORS_FOR_STDMAP
+ MemoryPool m_SerializedObjectIdentifierPool;
+ typedef std::map<SerializedObjectIdentifier, SInt32, std::less<SerializedObjectIdentifier>, memory_pool_explicit<std::pair<const SerializedObjectIdentifier, SInt32> > > FileToHeapIDMap;
+ typedef std::map<SInt32, SerializedObjectIdentifier, std::less<SInt32>, memory_pool_explicit<std::pair<const SInt32, SerializedObjectIdentifier> > > HeapIDToFileMap;
+#else
+ typedef std::map<SerializedObjectIdentifier, SInt32, std::less<SerializedObjectIdentifier> > FileToHeapIDMap;
+ typedef std::map<SInt32, SerializedObjectIdentifier, std::less<SInt32> > HeapIDToFileMap;
+#endif
+
+ typedef FileToHeapIDMap::iterator FileToHeapIDIterator;
+ typedef HeapIDToFileMap::iterator HeapIDToFileIterator;
+
+ FileToHeapIDMap m_FileToHeapID;
+ HeapIDToFileMap m_HeapIDToFile;
+ #if UNITY_EDITOR
+ vector_set<SInt32> m_LoadingSceneInstanceIDs;
+ #endif
+
+
+
+ // Instance ID's are simply allocated in an increasing index
+ int m_HighestMemoryID;
+
+ // When loading scenes we can fast path because objects are not kept persistent / unloaded / loaded again etc.
+ // So we just preallocate a bunch of id's and use those without going through a lot of table lookups.
+ int m_ActivePreallocatedIDBase;
+ int m_ActivePreallocatedIDEnd;
+ int m_ActivePreallocatedPathID;
+
+ Remapper ()
+// map node contains 3 pointers (left, right, parent)
+
+#if ENABLE_CUSTOM_ALLOCATORS_FOR_STDMAP
+: m_SerializedObjectIdentifierPool( false, "Remapper pool", sizeof (SerializedObjectIdentifier) + sizeof(SInt32)*2 + sizeof(void*)*3, 16 * 1024)
+, m_FileToHeapID (std::less<SerializedObjectIdentifier> (), m_SerializedObjectIdentifierPool)
+, m_HeapIDToFile (std::less<SInt32> (), m_SerializedObjectIdentifierPool)
+#endif
+ { m_HighestMemoryID = 0; m_ActivePreallocatedIDBase = 0; m_ActivePreallocatedIDEnd = 0; m_ActivePreallocatedPathID = -1; }
+
+
+ void PreallocateIDs (LocalIdentifierInFileType highestFileID, int pathID)
+ {
+ AssertIf(m_ActivePreallocatedPathID != -1);
+ AssertIf(pathID == -1);
+ m_HighestMemoryID += 2;
+ m_ActivePreallocatedIDBase = m_HighestMemoryID;
+ m_HighestMemoryID += highestFileID * 2;
+ m_ActivePreallocatedIDEnd = m_HighestMemoryID;
+ //printf_console("Preallocating %d .. %d\n", m_ActivePreallocatedIDBase, m_ActivePreallocatedIDEnd);
+ m_ActivePreallocatedPathID = pathID;
+ }
+
+ void ClearPreallocateIDs ()
+ {
+ AssertIf(m_ActivePreallocatedPathID == -1);
+ m_ActivePreallocatedIDBase = 0;
+ m_ActivePreallocatedIDEnd = 0;
+ m_ActivePreallocatedPathID = -1;
+ }
+
+ void Remove (int memoryID)
+ {
+ AssertIf(m_ActivePreallocatedPathID != -1);
+
+ HeapIDToFileIterator i = m_HeapIDToFile.find (memoryID);
+ if (i == m_HeapIDToFile.end ())
+ return;
+
+ FileToHeapIDIterator j = m_FileToHeapID.find (i->second);
+ AssertIf (j == m_FileToHeapID.end ());
+ SerializedObjectIdentifier bug = j->first;
+
+ m_HeapIDToFile.erase (i);
+ m_FileToHeapID.erase (j);
+ AssertIf (m_FileToHeapID.find (bug) != m_FileToHeapID.end ());
+ }
+
+ void RemoveCompletePathID (int serializedFileIndex, vector<SInt32>& objects)
+ {
+ AssertIf(m_ActivePreallocatedPathID != -1);
+
+ SerializedObjectIdentifier proxy;
+ proxy.serializedFileIndex = serializedFileIndex;
+ proxy.localIdentifierInFile = std::numeric_limits<LocalIdentifierInFileType>::min();
+
+ FileToHeapIDIterator begin = m_FileToHeapID.lower_bound (proxy);
+ proxy.localIdentifierInFile = std::numeric_limits<LocalIdentifierInFileType>::max();
+ FileToHeapIDIterator end = m_FileToHeapID.upper_bound (proxy);
+ for (FileToHeapIDIterator i=begin;i != end;i++)
+ {
+ ErrorIf(i->first.serializedFileIndex != serializedFileIndex);
+ m_HeapIDToFile.erase (m_HeapIDToFile.find(i->second));
+ objects.push_back(i->second);
+ }
+ m_FileToHeapID.erase(begin, end);
+ }
+
+ bool IsSceneID (int memoryID)
+ {
+#if UNITY_EDITOR
+ return m_LoadingSceneInstanceIDs.count(memoryID) != 0;
+#endif
+ return IsPreallocatedID (memoryID);
+ }
+
+ bool IsPreallocatedID (int memoryID)
+ {
+ return m_ActivePreallocatedPathID != -1 && memoryID >= m_ActivePreallocatedIDBase && memoryID <= m_ActivePreallocatedIDEnd;
+ }
+
+ bool InstanceIDToSerializedObjectIdentifier (int instanceID, SerializedObjectIdentifier& identifier)
+ {
+ if (IsPreallocatedID(instanceID))
+ {
+ identifier.serializedFileIndex = m_ActivePreallocatedPathID;
+ identifier.localIdentifierInFile = (instanceID - m_ActivePreallocatedIDBase) / 2;
+ return true;
+ }
+
+ HeapIDToFileIterator i = m_HeapIDToFile.find (instanceID);
+ if (i == m_HeapIDToFile.end ())
+ {
+ identifier.serializedFileIndex = -1;
+ identifier.localIdentifierInFile = 0;
+ return false;
+ }
+ identifier = i->second;
+
+ #if LOCAL_IDENTIFIER_IN_FILE_SIZE != 32
+ -- fix this, should we use UInt32 for localIdentifierInFile?
+ USInt64 debugLocalIdentifier = identifier.localIdentifierInFile;
+ AssertIf(debugLocalIdentifier >= (1ULL << LOCAL_IDENTIFIER_IN_FILE_SIZE) || debugLocalIdentifier <= -(1ULL << LOCAL_IDENTIFIER_IN_FILE_SIZE));
+ #endif
+
+ return true;
+ }
+
+
+ int GetSerializedFileIndex (int memoryID)
+ {
+ SerializedObjectIdentifier identifier;
+ InstanceIDToSerializedObjectIdentifier (memoryID, identifier);
+ return identifier.serializedFileIndex;
+ }
+
+ bool IsSetup (const SerializedObjectIdentifier& identifier)
+ {
+ return m_FileToHeapID.find (identifier) != m_FileToHeapID.end ();
+ }
+
+ bool IsHeapIDSetup (int memoryID)
+ {
+ AssertIf(m_ActivePreallocatedPathID != -1);
+ return m_HeapIDToFile.count(memoryID);
+ }
+
+ int GetOrGenerateMemoryID (const SerializedObjectIdentifier& identifier)
+ {
+ if (identifier.serializedFileIndex == -1)
+ return 0;
+
+ if (m_ActivePreallocatedPathID != -1 && m_ActivePreallocatedPathID == identifier.serializedFileIndex)
+ {
+ return identifier.localIdentifierInFile * 2 + m_ActivePreallocatedIDBase;
+ }
+
+ #if LOCAL_IDENTIFIER_IN_FILE_SIZE != 32
+ -- fix this, should we use UInt32 for localIdentifierInFile?
+ USInt64 debugLocalIdentifier = identifier.localIdentifierInFile;
+ AssertIf(debugLocalIdentifier >= (1ULL << LOCAL_IDENTIFIER_IN_FILE_SIZE) || debugLocalIdentifier <= -(1ULL << LOCAL_IDENTIFIER_IN_FILE_SIZE));
+ #endif
+ std::pair<FileToHeapIDIterator, bool> inserted = m_FileToHeapID.insert (std::make_pair (identifier, 0));
+ if (inserted.second)
+ {
+ int memoryID = 0;
+
+ m_HighestMemoryID += 2;
+ memoryID = m_HighestMemoryID;
+
+ inserted.first->second = memoryID;
+
+ AssertIf (m_HeapIDToFile.find (memoryID) != m_HeapIDToFile.end ());
+ m_HeapIDToFile.insert (std::make_pair (memoryID, identifier));
+
+ return memoryID;
+ }
+ else
+ return inserted.first->second;
+ }
+
+ void SetupRemapping (int memoryID, const SerializedObjectIdentifier& identifier)
+ {
+ AssertIf(m_ActivePreallocatedPathID != -1);
+ #if LOCAL_IDENTIFIER_IN_FILE_SIZE != 32
+ -- fix this, should we use UInt32 for localIdentifierInFile?
+ USInt64 debugLocalIdentifier = identifier.localIdentifierInFile;
+ AssertIf(debugLocalIdentifier >= (1ULL << LOCAL_IDENTIFIER_IN_FILE_SIZE) || debugLocalIdentifier <= -(1ULL << LOCAL_IDENTIFIER_IN_FILE_SIZE));
+ #endif
+
+ if (m_HeapIDToFile.find (memoryID) != m_HeapIDToFile.end ())
+ {
+ m_FileToHeapID.erase(m_HeapIDToFile.find (memoryID)->second);
+ m_HeapIDToFile.erase(memoryID);
+ }
+
+ if (m_FileToHeapID.find (identifier) != m_FileToHeapID.end ())
+ {
+ m_HeapIDToFile.erase(m_FileToHeapID.find (identifier)->second);
+ m_FileToHeapID.erase(identifier);
+ }
+
+ m_HeapIDToFile[memoryID] = identifier;
+ m_FileToHeapID[identifier] = memoryID;
+
+/*
+// This code asserts more when something goes wrong but also in edge cases that are allowed.
+ SerializedObjectIdentifier id;
+ id.fileID = fileID;
+ id.pathID = pathID;
+
+ HeapIDToFileIterator inserted;
+ inserted = m_HeapIDToFile.insert (std::make_pair (memoryID, id)).first;
+ AssertIf (inserted->second != id);
+ inserted->second = id;
+
+ FileToHeapIDIterator inserted2;
+ #if DEBUGMODE
+ inserted2 = m_FileToHeapID.find (id);
+ AssertIf (inserted2 != m_FileToHeapID.end () && inserted2->second != memoryID);
+ #endif
+
+ inserted2 = m_FileToHeapID.insert (std::make_pair (id, memoryID)).first;
+ AssertIf (inserted2->second != memoryID);
+ inserted2->second = memoryID;
+*/
+ }
+
+ void GetAllLoadedObjectsAtPath (int pathID, set<SInt32>* objects)
+ {
+ AssertIf(m_ActivePreallocatedPathID != -1);
+ AssertIf (objects == NULL);
+
+ SerializedObjectIdentifier proxy;
+ proxy.localIdentifierInFile = std::numeric_limits<LocalIdentifierInFileType>::min ();
+ proxy.serializedFileIndex = pathID;
+ FileToHeapIDIterator begin = m_FileToHeapID.lower_bound (proxy);
+ proxy.localIdentifierInFile = std::numeric_limits<LocalIdentifierInFileType>::max ();
+ FileToHeapIDIterator end = m_FileToHeapID.upper_bound (proxy);
+
+ for (FileToHeapIDIterator i=begin;i != end;++i)
+ {
+ int instanceID = i->second;
+ Object* o = Object::IDToPointer (instanceID);
+ if (o)
+ objects->insert (instanceID);
+ }
+ }
+
+ void GetAllPersistentObjectsAtPath (int pathID, set<SInt32>* objects)
+ {
+ AssertIf(m_ActivePreallocatedPathID != -1);
+ AssertIf (objects == NULL);
+
+ SerializedObjectIdentifier proxy;
+ proxy.localIdentifierInFile = std::numeric_limits<LocalIdentifierInFileType>::min ();
+ proxy.serializedFileIndex = pathID;
+ FileToHeapIDIterator begin = m_FileToHeapID.lower_bound (proxy);
+ proxy.localIdentifierInFile = std::numeric_limits<LocalIdentifierInFileType>::max ();
+ FileToHeapIDIterator end = m_FileToHeapID.upper_bound (proxy);
+
+ for (FileToHeapIDIterator i=begin;i != end;++i)
+ {
+ int instanceID = i->second;
+ objects->insert(instanceID);
+ }
+ }
+
+ int GetHighestInUseHeapID ()
+ {
+ if (!m_HeapIDToFile.empty())
+ return m_HeapIDToFile.rbegin ()->first;
+ else
+ return 0;
+ }
+};
+
+#endif
diff --git a/Runtime/Serialize/SerializationMetaFlags.h b/Runtime/Serialize/SerializationMetaFlags.h
new file mode 100644
index 0000000..fc47207
--- /dev/null
+++ b/Runtime/Serialize/SerializationMetaFlags.h
@@ -0,0 +1,292 @@
+#ifndef SERIALIZATIONMETAFLAGS_H
+#define SERIALIZATIONMETAFLAGS_H
+
+#include "Runtime/Utilities/EnumFlags.h"
+
+/// Meta flags can be used like this:
+/// transfer.Transfer (someVar, "varname", kHideInEditorMask);
+/// The proxytransfer for example reads the metaflag mask and stores it in the TypeTree
+enum TransferMetaFlags
+{
+ kNoTransferFlags = 0,
+ /// Putting this mask in a transfer will make the variable be hidden in the property editor
+ kHideInEditorMask = 1 << 0,
+
+ /// Makes a variable not editable in the property editor
+ kNotEditableMask = 1 << 4,
+
+ /// There are 3 types of PPtrs: kStrongPPtrMask, default (weak pointer)
+ /// a Strong PPtr forces the referenced object to be cloned.
+ /// A Weak PPtr doesnt clone the referenced object, but if the referenced object is being cloned anyway (eg. If another (strong) pptr references this object)
+ /// this PPtr will be remapped to the cloned object
+ /// If an object referenced by a WeakPPtr is not cloned, it will stay the same when duplicating and cloning, but be NULLed when templating
+ kStrongPPtrMask = 1 << 6,
+ // unused = 1 << 7,
+
+ /// kEditorDisplaysCheckBoxMask makes an integer variable appear as a checkbox in the editor
+ kEditorDisplaysCheckBoxMask = 1 << 8,
+
+ // unused = 1 << 9,
+ // unused = 1 << 10,
+
+ /// Show in simplified editor
+ kSimpleEditorMask = 1 << 11,
+
+ /// When the options of a serializer tells you to serialize debug properties kSerializeDebugProperties
+ /// All debug properties have to be marked kDebugPropertyMask
+ /// Debug properties are shown in expert mode in the inspector but are not serialized normally
+ kDebugPropertyMask = 1 << 12,
+
+ kAlignBytesFlag = 1 << 14,
+ kAnyChildUsesAlignBytesFlag = 1 << 15,
+ kIgnoreWithInspectorUndoMask= 1 << 16,
+
+ // unused = 1 << 18,
+
+ // Ignore this property when reading or writing .meta files
+ kIgnoreInMetaFiles = 1 << 19,
+
+ // When reading meta files and this property is not present, read array entry name instead (for backwards compatibility).
+ kTransferAsArrayEntryNameInMetaFiles = 1 << 20,
+
+ // When writing YAML Files, uses the flow mapping style (all properties in one line, with "{}").
+ kTransferUsingFlowMappingStyle = 1 << 21,
+
+ // Tells SerializedProperty to generate bitwise difference information for this field.
+ kGenerateBitwiseDifferences = 1 << 22,
+
+ kDontAnimate = 1 << 23,
+
+};
+ENUM_FLAGS(TransferMetaFlags);
+
+enum TransferInstructionFlags
+{
+ kNoTransferInstructionFlags = 0,
+
+ kNeedsInstanceIDRemapping = 1 << 0, // Should we convert PPtrs into pathID, fileID using the PerisistentManager or should we just store the memory InstanceID in the fileID?
+ kAssetMetaDataOnly = 1 << 1, // Only serialize data needed for .meta files
+ kYamlGlobalPPtrReference = 1 << 2,
+ #if UNITY_EDITOR
+ kLoadAndUnloadAssetsDuringBuild = 1 << 3,
+ kSerializeDebugProperties = 1 << 4, // Should we serialize debug properties (eg. Serialize mono private variables)
+ #endif
+ kIgnoreDebugPropertiesForIndex = 1 << 5, // Should we ignore Debug properties when calculating the TypeTree index
+ #if UNITY_EDITOR
+ kBuildPlayerOnlySerializeBuildProperties = 1 << 6, // Used by eg. build player to make materials cull any properties are aren't used anymore !
+ #endif
+ kWorkaround35MeshSerializationFuckup = 1 << 7,
+
+ kSerializeGameRelease = 1 << 8, // Should Transfer classes use optimized reading. Allowing them to read memory directly that normally has a type using ReadDirect.
+ kSwapEndianess = 1 << 9, // Should we swap endianess when reading / writing a file
+ kSaveGlobalManagers = 1 << 10, // Should global managers be saved when writing the game build
+ kDontReadObjectsFromDiskBeforeWriting = 1 << 11,
+ kSerializeMonoReload = 1 << 12, // Should we backupmono mono variables for an assembly reload?
+ kDontRequireAllMetaFlags = 1 << 13, // Can we fast path calculating all meta data. This lets us skip a bunch of code when serializing mono data.
+ kSerializeForPrefabSystem = 1 << 14,
+ #if UNITY_EDITOR
+ kWarnAboutLeakedObjects = 1 << 15,
+ // Unused = 1 << 16,
+ // Unused = 1 << 17,
+ kEditorPlayMode = 1 << 18,
+ kBuildResourceImage = 1 << 19,
+ kSerializeEditorMinimalScene = 1 << 21,
+ kGenerateBakedPhysixMeshes = 1 << 22,
+ #endif
+ kThreadedSerialization = 1 << 23,
+ kIsBuiltinResourcesFile = 1 << 24,
+ kPerformUnloadDependencyTracking = 1 << 25,
+ kDisableWriteTypeTree = 1 << 26,
+ kAutoreplaceEditorWindow = 1 << 27,// Editor only
+ kSerializeForInspector = 1 << 29,
+ kSerializedAssetBundleVersion = 1 << 30, // When writing (typetrees disabled), allow later Unity versions an attempt to read SerializedFile.
+ kAllowTextSerialization = 1 << 31
+};
+ENUM_FLAGS(TransferInstructionFlags);
+
+enum BuildAssetBundleOptions
+{
+ kAssetBundleUncompressed = 1 << 11,
+ kAssetBundleCollectDependencies = 1 << 20,
+ kAssetBundleIncludeCompleteAssets = 1 << 21,
+ kAssetBundleDisableWriteTypeTree = 1 << 26,
+ kAssetBundleDeterministic = 1 << 28,
+};
+ENUM_FLAGS(BuildAssetBundleOptions);
+
+
+enum ActiveResourceImage
+{
+ kResourceImageNotSupported = -2,
+ kResourceImageInactive = -1,
+ kGPUResourceImage = 0,
+ kResourceImage = 1,
+ kStreamingResourceImage = 2,
+ kNbResourceImages = 3
+};
+
+/// This needs to be in Sync with BuildTarget in C#
+enum BuildTargetPlatform
+{
+ kBuildNoTargetPlatform = -2,
+ kBuildAnyPlayerData = -1,
+ kBuildValidPlayer = 1,
+
+ // We don't support building for these any more, but we still need the constants for asset bundle
+ // backwards compatibility.
+ kBuildStandaloneOSXPPC = 3,
+
+ kBuildStandaloneOSXIntel = 4,
+ kBuildStandaloneOSXIntel64 = 27,
+ kBuildStandaloneOSXUniversal = 2,
+ kBuildStandaloneWinPlayer = 5,
+ kBuildWebPlayerLZMA = 6,
+ kBuildWebPlayerLZMAStreamed = 7,
+ kBuildWii = 8,
+ kBuild_iPhone = 9,
+ kBuildPS3 = 10,
+ kBuildXBOX360 = 11,
+ // was kBuild_Broadcom = 12,
+ kBuild_Android = 13,
+ kBuildWinGLESEmu = 14,
+ // was kBuildWinGLES20Emu = 15,
+ kBuildNaCl = 16,
+ kBuildStandaloneLinux = 17,
+ kBuildFlash = 18,
+ kBuildStandaloneWin64Player = 19,
+ kBuildWebGL = 20,
+ kBuildMetroPlayerX86 = 21,
+ kBuildMetroPlayerX64 = 22,
+ kBuildMetroPlayerARM = 23,
+ kBuildStandaloneLinux64 = 24,
+ kBuildStandaloneLinuxUniversal = 25,
+ kBuildWP8Player = 26,
+ kBuildBB10 = 28,
+ kBuildTizen = 29,
+ kBuildPlayerTypeCount = 30,
+};
+
+struct BuildUsageTag
+{
+ bool forceTextureReadable;
+ bool strippedPrefabObject;
+ UInt32 meshUsageFlags;
+ UInt32 meshSupportedChannels;
+
+ BuildUsageTag ()
+ {
+ forceTextureReadable = false;
+ meshUsageFlags = 0;
+ meshSupportedChannels = 0;
+ strippedPrefabObject = false;
+ }
+};
+
+
+struct BuildTargetSelection
+{
+ BuildTargetPlatform platform;
+ int subTarget;
+
+ BuildTargetSelection() : platform(kBuildNoTargetPlatform), subTarget(0) { }
+ BuildTargetSelection(BuildTargetPlatform platform_, int subTarget_) : platform(platform_), subTarget(subTarget_) {}
+
+ bool operator == (const BuildTargetSelection& rhs) const
+ {
+ if (platform != rhs.platform)
+ return false;
+ if (subTarget != rhs.subTarget)
+ return false;
+
+ return true;
+ }
+ bool operator != (const BuildTargetSelection& rhs) const
+ {
+ return ! operator == (rhs);
+ }
+
+ static BuildTargetSelection NoTarget() { return BuildTargetSelection(kBuildNoTargetPlatform,0); }
+};
+
+
+enum WebPlayerBuildSubTarget
+{
+ kWebBuildSubtargetDefault = 0,
+ kWebBuildSubtargetDirect3D = 1, // windows only (D3D9 & D3D11)
+ kWebBuildSubtargetOpenGL = 2, // non-windows only (OpenGL)
+};
+
+
+/// This needs to be in Sync with XboxRunMethod in C#
+enum XboxBuildSubtarget
+{
+ kXboxBuildSubtargetDevelopment = 0,
+ kXboxBuildSubtargetMaster = 1,
+ kXboxBuildSubtargetDebug = 2
+};
+
+/// This needs to be in Sync with WiiBuildDebugLevel in C#
+enum WiiBuildDebugLevel
+{
+ kWiiBuildDebugLevel_Full = 0,
+ kWiiBuildDebugLevel_Minimal = 1,
+ kWiiBuildDebugLevel_None = 2,
+};
+
+/// This needs to be in Sync with XboxRunMethod in C#
+enum XboxRunMethod
+{
+ kXboxRunMethodHDD = 0,
+ kXboxRunMethodDiscEmuFast = 1,
+ kXboxRunMethodDiscEmuAccurate = 2
+};
+
+/// This needs to be in Sync with AndroidBuildSubtarget in C#
+enum AndroidBuildSubtarget
+{
+ kAndroidBuildSubtarget_Generic = 0,
+ kAndroidBuildSubtarget_DXT = 1,
+ kAndroidBuildSubtarget_PVRTC = 2,
+ kAndroidBuildSubtarget_ATC = 3,
+ kAndroidBuildSubtarget_ETC = 4,
+ kAndroidBuildSubtarget_ETC2 = 5,
+ kAndroidBuildSubtarget_ASTC = 6,
+};
+
+/// This needs to be in Sync with BB10BuildSubtarget in C#
+enum BlackBerryBuildSubtarget
+{
+ kBlackBerryBuildSubtarget_Generic = 0,
+ kBlackBerryBuildSubtarget_PVRTC = 1,
+ kBlackBerryBuildSubtarget_ATC = 2,
+ kBlackBerryBuildSubtarget_ETC = 3
+};
+
+/// This needs to be in Sync with BB10BuildType in C#
+enum BlackBerryBuildType
+{
+ kBlackBerryBuildType_Debug = 0,
+ kBlackBerryBuildType_Submission = 1
+};
+
+/// This needs to be in Sync with BuildOptions in C#
+enum BuildPlayerOptions
+{
+ kBuildPlayerOptionsNone = 0,
+ kDevelopmentBuild = 1 << 0,
+ kAutoRun = 1 << 2,
+ kSelectBuiltPlayer = 1 << 3,
+ kBuildAdditionalStreamedScenes = 1 << 4,
+ kAcceptExternalModificationsToPlayer = 1 << 5,
+ kInstallInBuildsFolder = 1 << 6,
+ kWebPlayerOfflineDeployment = 1 << 7,
+ kConnectWithProfiler = 1 << 8,
+ kAllowDebugging = 1 << 9,
+ kSymlinkLibraries = 1 << 10,
+ kBuildPlayerUncompressed = 1 << 11,
+ kConnectToHost = 1 << 12,
+ kDeployOnline = 1 << 13,
+ kHeadlessModeEnabled = 1 << 14
+};
+
+#endif
diff --git a/Runtime/Serialize/SerializationTests.cpp b/Runtime/Serialize/SerializationTests.cpp
new file mode 100644
index 0000000..def94dd
--- /dev/null
+++ b/Runtime/Serialize/SerializationTests.cpp
@@ -0,0 +1,242 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "Runtime/Testing/Testing.h"
+#include "Runtime/Testing/TestFixtures.h"
+
+SUITE (SerializationTests)
+{
+ //-------------------------------------------------------------------------
+
+ DEFINE_TRANSFER_TEST_FIXTURE (DidReadExistingProperty)
+ {
+ float m_FloatProperty;
+ TRANSFER (m_FloatProperty);
+ if (transfer.IsReading ())
+ {
+ CHECK (transfer.DidReadLastProperty ());
+ }
+ }
+
+ TEST_FIXTURE (DidReadExistingPropertyTestFixture, SafeBinaryRead_DidReadLastProperty_WithExistingProperty_IsTrue)
+ {
+ DoSafeBinaryTransfer ();
+ }
+
+ TEST_FIXTURE (DidReadExistingPropertyTestFixture, YAMLRead_DidReadLastProperty_WithExistingProperty_IsTrue)
+ {
+ DoTextTransfer ();
+ }
+
+ //-------------------------------------------------------------------------
+
+ DEFINE_TRANSFER_TEST_FIXTURE (DidNotReadMissingProperty)
+ {
+ float m_Foobar;
+ TRANSFER (m_Foobar);
+
+ if (transfer.IsReading ())
+ {
+ UnityStr value = "foobar";
+ TRANSFER (value);
+
+ CHECK (!transfer.DidReadLastProperty ());
+ CHECK (value == "foobar");
+ }
+ }
+
+ TEST_FIXTURE (DidNotReadMissingPropertyTestFixture, SafeBinaryRead_DidReadLastProperty_WithMissingProperty_IsFalse)
+ {
+ DoSafeBinaryTransfer ();
+ }
+
+ TEST_FIXTURE (DidNotReadMissingPropertyTestFixture, YAMLRead_DidReadLastProperty_WithMissingProperty_IsFalse)
+ {
+ DoTextTransfer ();
+ }
+
+ //-------------------------------------------------------------------------
+
+#define kDoubleValue 0.1
+#define kFloatValue -2.5f
+#define kIntValue 1337
+#define kLongLongValue 1234567890123456789LL
+#define kCharValue 'X'
+#define kBoolValue true
+#define kStringValue "UnityFTW"
+#define kVectorSize 3
+
+template<class T>
+struct FloatingPointConsistencyTest
+{
+ #define kNumFloatValues 12
+ T values[kNumFloatValues];
+
+ T Get(int i)
+ {
+ switch (i)
+ {
+ case 0: return std::numeric_limits<T>::min();
+ case 1: return std::numeric_limits<T>::max();
+ case 2: return std::numeric_limits<T>::denorm_min();
+ case 3: return std::numeric_limits<T>::infinity();
+ case 4: return -std::numeric_limits<T>::infinity();
+ case 5: return std::numeric_limits<T>::quiet_NaN();
+ case 6: return std::numeric_limits<T>::epsilon();
+ case 7: return -0.0;
+ case 8: return (T)12345678901234567890.123456789012345678900;
+ case 9: return (T)0.1;
+ case 10: return (T)(1.0 / 3.0);
+ case 11: return (T)(3 * 1024 * 1024 * 0.19358);
+ default: ErrorString("Should not happen!"); return 0;
+ }
+ }
+
+ void FillStruct ()
+ {
+ for (int i=0; i<kNumFloatValues; i++)
+ values[i] = Get(i);
+ }
+
+ void VerifyStruct ()
+ {
+ for (int i=0; i<kNumFloatValues; i++)
+ {
+ T expected = Get(i);
+
+ // Use memcmp instead of == to test for negative zero and NaN.
+ CHECK (memcmp (&expected, values+i, sizeof(T)) == 0);
+ }
+ }
+
+ DECLARE_SERIALIZE (FloatingPointConsistencyTest)
+};
+
+template<class T>
+template<class TransferFunction> inline
+void FloatingPointConsistencyTest<T>::Transfer (TransferFunction& transfer)
+{
+ for (int i=0; i<kNumFloatValues; i++)
+ TRANSFER(values[i]);
+}
+
+struct TestStruct {
+ float m_Float;
+ int m_Int;
+ long long m_LongLong;
+ char m_Char;
+ bool m_Bool;
+
+ void FillStruct ()
+ {
+ m_Float = kFloatValue;
+ m_Int = kIntValue;
+ m_Char = kCharValue;
+ m_Bool = kBoolValue;
+ m_LongLong = kLongLongValue;
+ }
+
+ void VerifyStruct ()
+ {
+ CHECK_EQUAL (m_Float, kFloatValue);
+ CHECK_EQUAL (m_Int, kIntValue);
+ CHECK_EQUAL (m_Char, kCharValue);
+ CHECK_EQUAL (m_Bool, kBoolValue);
+ CHECK_EQUAL (m_LongLong, kLongLongValue);
+ }
+ DECLARE_SERIALIZE (TestStruct)
+};
+
+struct TestStruct2 {
+ FloatingPointConsistencyTest<float> m_FloatTest;
+ FloatingPointConsistencyTest<double> m_DoubleTest;
+ UnityStr m_String;
+ TestStruct m_Struct;
+ std::vector<TestStruct> m_Vector;
+ std::map<int, TestStruct> m_Map;
+ char m_TypelessData[kVectorSize];
+
+ void FillStruct ()
+ {
+ m_FloatTest.FillStruct();
+ m_DoubleTest.FillStruct();
+ m_String = kStringValue;
+ m_Struct.FillStruct();
+ m_Vector.resize(kVectorSize);
+ for (int i=0;i<kVectorSize;i++)
+ m_Vector[i].FillStruct();
+ m_Map[42].FillStruct();
+ m_Map[666].FillStruct();
+ m_Map[23].FillStruct();
+ for (int i=0;i<kVectorSize;i++)
+ m_TypelessData[i] = i;
+ }
+
+ void VerifyStruct ()
+ {
+ CHECK_EQUAL (m_String, kStringValue);
+ m_Struct.VerifyStruct();
+ CHECK_EQUAL (m_Vector.size(), kVectorSize);
+ for (int i=0;i<kVectorSize;i++)
+ m_Vector[i].VerifyStruct();
+
+ m_Map[42].VerifyStruct();
+ m_Map[666].VerifyStruct();
+ m_Map[23].VerifyStruct();
+ for (int i=0;i<kVectorSize;i++)
+ CHECK_EQUAL (m_TypelessData[i], i);
+ m_FloatTest.VerifyStruct();
+ m_DoubleTest.VerifyStruct();
+ }
+
+ DECLARE_SERIALIZE (TestStruct2)
+};
+
+template<class TransferFunction> inline
+void TestStruct::Transfer (TransferFunction& transfer)
+{
+ TRANSFER(m_Float);
+ TRANSFER(m_Int);
+ TRANSFER(m_Char);
+ TRANSFER(m_Bool);
+ TRANSFER(m_LongLong);
+}
+
+template<class TransferFunction> inline
+void TestStruct2::Transfer (TransferFunction& transfer)
+{
+ TRANSFER(m_FloatTest);
+ TRANSFER(m_DoubleTest);
+ TRANSFER(m_String);
+ TRANSFER(m_Struct);
+ TRANSFER(m_Vector);
+
+ TRANSFER(m_Map);
+ transfer.TransferTypelessData (kVectorSize, m_TypelessData, 0);
+}
+
+#if SUPPORT_TEXT_SERIALIZATION
+TEST (SerialializeYAMLStruct)
+{
+ TestStruct2 input;
+ input.FillStruct ();
+
+ YAMLWrite write (0);
+ input.Transfer( write );
+ std::string str;
+ write.OutputToString(str);
+ TestStruct2 output;
+
+ YAMLRead read (str.c_str(), str.size(), 0);
+ output.Transfer( read );
+
+ output.VerifyStruct();
+
+} //TEST
+#endif
+
+} //SUITE
+
+#endif
diff --git a/Runtime/Serialize/SerializeConversion.h b/Runtime/Serialize/SerializeConversion.h
new file mode 100644
index 0000000..782ea5b
--- /dev/null
+++ b/Runtime/Serialize/SerializeConversion.h
@@ -0,0 +1,34 @@
+#ifndef SERIALIZECONVERSION_H
+#define SERIALIZECONVERSION_H
+
+#if SUPPORT_SERIALIZED_TYPETREES
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "SerializeTraits.h"
+
+// Trys to convert from an old type to a new one
+template<class OldFormat, class NewFormat>
+bool StdTemplateConversionFunction (void* inData, SafeBinaryRead& transfer)
+{
+ NewFormat& data = *reinterpret_cast<NewFormat*> (inData);
+ const TypeTree& oldTypeTree = transfer.GetActiveOldTypeTree ();
+ AssertIf (SerializeTraits<OldFormat>::GetTypeString (NULL) != oldTypeTree.m_Type);
+ OldFormat oldData;
+
+ SafeBinaryRead safeRead;
+ CachedReader& temp = safeRead.Init (transfer);
+
+ safeRead.Transfer (oldData, oldTypeTree.m_Name.c_str ());
+
+ temp.End ();
+
+ data = oldData;
+
+ return true;
+}
+
+#define REGISTER_CONVERTER(from, to) \
+SafeBinaryRead::RegisterConverter (SerializeTraits<from>::GetTypeString (NULL), SerializeTraits<to>::GetTypeString (NULL), \
+ StdTemplateConversionFunction<from, to>)
+
+#endif
+#endif
diff --git a/Runtime/Serialize/SerializeTraits.h b/Runtime/Serialize/SerializeTraits.h
new file mode 100644
index 0000000..9d5b946
--- /dev/null
+++ b/Runtime/Serialize/SerializeTraits.h
@@ -0,0 +1,533 @@
+#ifndef SERIALIZETRAITS_H
+#define SERIALIZETRAITS_H
+
+#include "TypeTree.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "SerializeUtility.h"
+#include "SerializationMetaFlags.h"
+#include "Runtime/Utilities/vector_utility.h"
+#include "Runtime/Utilities/vector_map.h"
+#include "Runtime/Utilities/vector_set.h"
+#include "Runtime/Utilities/dense_hash_map.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include <map>
+#include <set>
+#include <list>
+#include <deque>
+#include "SerializeTraitsBase.h"
+
+class SerializedFile;
+class SafeBinaryRead;
+
+typedef void TransferTypelessCallback (UInt8* data, int byteSize, int instanceID, int userdata);
+
+/*
+
+ You can use SerializeTraits to setup transfer functions for classes where you can't change to code, eg. the STL.
+ You might also want to use it when writing custom converters.
+
+ template<>
+ class SerializeTraits<Vector4f> : public SerializeTraitsBase<Vector4f>
+ {
+ public:
+
+ typedef Vector4f value_type;
+
+ inline static const char* GetTypeString () { return value_type::GetTypeString (); }
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ data.Transfer (transfer);
+ }
+
+ template<class TransferFunction>
+ static void Convert (value_type& data, TransferFunction& transfer)
+ {
+ const TypeTree& oldTypeTree = transfer.GetActiveOldTypeTree ();
+ const std::string& oldType = transfer.GetActiveOldTypeTree ().m_Type;
+ if (oldType == "Vector3f")
+ {
+ Vector3f temp = data;
+ temp.Transfer (transfer);
+ data = temp;
+ return true;
+ }
+ else
+ return false;
+ }
+
+ /// Returns whether or not a this type is to be treated as a seperate channel in the animation system
+ static bool IsAnimationChannel () { return T::IsAnimationChannel (); }
+ };
+
+*/
+
+
+#define DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL_TRAITS(x) \
+ inline static const char* GetTypeString (void* p = 0) { return #x; } \
+ inline static bool IsAnimationChannel () { return true; } \
+ inline static bool MightContainPPtr () { return false; } \
+ inline static bool AllowTransferOptimization () { return true; }
+
+template<>
+struct SerializeTraits<float> : public SerializeTraitsBaseForBasicType<float>
+{
+ DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL_TRAITS (float)
+};
+
+template<>
+struct SerializeTraits<double> : public SerializeTraitsBaseForBasicType<double>
+{
+ DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL_TRAITS (double)
+};
+
+template<>
+struct SerializeTraits<SInt32> : public SerializeTraitsBaseForBasicType<SInt32>
+{
+ // We use "int" rather than "SInt32" here for backwards-compatibility reasons.
+ // "SInt32" and "int" used to be two different types (as were "UInt32" and "unsigned int")
+ // that we now serialize through same path. We use "int" instead of "SInt32" as the common
+ // identifier as it was more common.
+ DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL_TRAITS (int)
+};
+
+template<>
+struct SerializeTraits<UInt32> : public SerializeTraitsBaseForBasicType<UInt32>
+{
+ DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL_TRAITS (unsigned int) // See definition of "int" above.
+};
+
+template<>
+struct SerializeTraits<SInt64> : public SerializeTraitsBaseForBasicType<SInt64>
+{
+ DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL_TRAITS (SInt64)
+};
+template<>
+struct SerializeTraits<UInt64> : public SerializeTraitsBaseForBasicType<UInt64>
+{
+ DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL_TRAITS (UInt64)
+};
+
+template<>
+struct SerializeTraits<SInt16> : public SerializeTraitsBaseForBasicType<SInt16>
+{
+ DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL_TRAITS (SInt16)
+};
+
+template<>
+struct SerializeTraits<UInt16> : public SerializeTraitsBaseForBasicType<UInt16>
+{
+ DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL_TRAITS (UInt16)
+};
+
+template<>
+struct SerializeTraits<SInt8> : public SerializeTraitsBaseForBasicType<SInt8>
+{
+ DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL_TRAITS (SInt8)
+};
+
+template<>
+struct SerializeTraits<UInt8> : public SerializeTraitsBaseForBasicType<UInt8>
+{
+ DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL_TRAITS (UInt8)
+};
+
+template<>
+struct SerializeTraits<char> : public SerializeTraitsBaseForBasicType<char>
+{
+ DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL_TRAITS (char)
+};
+
+template<>
+struct SerializeTraits<bool> : public SerializeTraitsBase<bool>
+{
+ typedef bool value_type;
+ DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL_TRAITS (bool)
+
+ static int GetByteSize () { return 1; }
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ #if (defined __ppc__) && !UNITY_WII
+ AssertIf (sizeof(bool) != 4);
+ UInt8& temp = *(reinterpret_cast<UInt8*>(&data) + 3);
+
+ transfer.TransferBasicData (temp);
+
+ // When running in debug mode in OS X (-O0 in gcc),
+ // bool values which are not exactly 0x01 are treated as false.
+ // We don't want this. Cast UInt8 to bool to fix this.
+ if (transfer.IsReading())
+ data = temp;
+ #if DEBUGMODE
+ AssertIf((transfer.IsReading() || transfer.IsWriting()) && (reinterpret_cast<int&> (data) != 0 && reinterpret_cast<int&> (data) != 1));
+ #endif
+ #else
+ AssertIf (sizeof(bool) != 1);
+ UInt8& temp = reinterpret_cast<UInt8&>(data);
+ transfer.TransferBasicData (temp);
+
+ // When running in debug mode in OS X (-O0 in gcc),
+ // bool values which are not exactly 0x01 are treated as false.
+ // We don't want this. Cast UInt8 to bool to fix this.
+ #if DEBUGMODE
+ if (transfer.IsReading())
+ data = temp;
+ // You constructor or Reset function is not setting the bool value to a defined value!
+ AssertIf((transfer.IsReading() || transfer.IsWriting()) && (temp != 0 && temp != 1));
+ #endif
+ #endif
+ }
+};
+
+
+
+#define DEFINE_GET_TYPESTRING_MAP_CONTAINER(x) \
+inline static const char* GetTypeString (void*) { return #x; } \
+inline static bool IsAnimationChannel () { return false; } \
+inline static bool MightContainPPtr () { return SerializeTraits<FirstClass>::MightContainPPtr() || SerializeTraits<SecondClass>::MightContainPPtr(); } \
+inline static bool AllowTransferOptimization () { return false; }
+
+template<>
+class SerializeTraits<UnityStr> : public SerializeTraitsBase<UnityStr>
+{
+public:
+
+ typedef UnityStr value_type;
+ inline static const char* GetTypeString (value_type* x = NULL) { return "string"; }
+ inline static bool IsAnimationChannel () { return false; }
+ inline static bool MightContainPPtr () { return false; }
+ inline static bool AllowTransferOptimization () { return false; }
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ transfer.TransferSTLStyleArray (data, kHideInEditorMask);
+ transfer.Align();
+ }
+
+ static bool IsContinousMemoryArray () { return true; }
+
+ static void ResizeSTLStyleArray (value_type& data, int rs)
+ {
+ data.resize (rs, 1);
+ }
+
+};
+
+// Do not add this serialization function. All serialized strings should use UnityStr instead of std::string
+//template<class Traits, class Allocator>
+//class SerializeTraits<std::basic_string<char,Traits,Allocator> > : public SerializeTraitsBase<std::basic_string<char,Traits,Allocator> >
+
+template<class T, class Allocator>
+class SerializeTraits<std::vector<T, Allocator> > : public SerializeTraitsBase<std::vector<T, Allocator> >
+{
+ public:
+
+ typedef std::vector<T,Allocator> value_type;
+ DEFINE_GET_TYPESTRING_CONTAINER (vector)
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ transfer.TransferSTLStyleArray (data);
+ }
+
+ static bool IsContinousMemoryArray () { return true; }
+ static void ResizeSTLStyleArray (value_type& data, int rs) { resize_trimmed (data, rs); }
+};
+
+template<class Allocator>
+class SerializeTraits<std::vector<UInt8,Allocator> > : public SerializeTraitsBase<std::vector<UInt8,Allocator> >
+{
+ public:
+
+ typedef std::vector<UInt8,Allocator> value_type;
+
+ inline static const char* GetTypeString (void* x = NULL) { return "vector"; }
+ inline static bool IsAnimationChannel () { return false; }
+ inline static bool MightContainPPtr () { return false; }
+ inline static bool AllowTransferOptimization () { return false; }
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ transfer.TransferSTLStyleArray (data);
+ transfer.Align();
+ }
+
+ static bool IsContinousMemoryArray () { return true; }
+ static void ResizeSTLStyleArray (value_type& data, int rs) { resize_trimmed (data, rs); }
+};
+
+template<class T, class Allocator>
+class SerializeTraits<std::list<T,Allocator> > : public SerializeTraitsBase<std::list<T,Allocator> >
+{
+ public:
+
+ typedef std::list<T,Allocator> value_type;
+ DEFINE_GET_TYPESTRING_CONTAINER (vector)
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ transfer.TransferSTLStyleArray (data);
+ }
+
+ static bool IsContinousMemoryArray () { return false; }
+ static void ResizeSTLStyleArray (value_type& data, int rs) { data.resize (rs); }
+};
+
+template<class FirstClass, class SecondClass>
+class SerializeTraits<std::pair<FirstClass, SecondClass> > : public SerializeTraitsBase<std::pair<FirstClass, SecondClass> >
+{
+ public:
+
+ typedef std::pair<FirstClass, SecondClass> value_type;
+ inline static const char* GetTypeString (void* x = NULL) { return "pair"; }
+ inline static bool IsAnimationChannel () { return false; }
+ inline static bool MightContainPPtr () { return SerializeTraits<FirstClass>::MightContainPPtr() || SerializeTraits<SecondClass>::MightContainPPtr(); }
+// inline static bool AllowTransferOptimization () { return SerializeTraits<FirstClass>::AllowTransferOptimization() || SerializeTraits<SecondClass>::AllowTransferOptimization(); }
+ inline static bool AllowTransferOptimization () { return false; }
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ transfer.Transfer (data.first, "first");
+ transfer.Transfer (data.second, "second");
+ }
+};
+
+template<class FirstClass, class SecondClass, class Compare, class Allocator>
+class SerializeTraits<std::map<FirstClass, SecondClass, Compare, Allocator> > : public SerializeTraitsBase<std::map<FirstClass, SecondClass, Compare, Allocator> >
+{
+ public:
+
+ typedef std::map<FirstClass, SecondClass, Compare, Allocator> value_type;
+ DEFINE_GET_TYPESTRING_MAP_CONTAINER(map)
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ AssertIf(transfer.IsRemapPPtrTransfer() && SerializeTraits<FirstClass>::MightContainPPtr() && transfer.IsReadingPPtr());
+ transfer.TransferSTLStyleMap (data);
+ }
+};
+
+template<class FirstClass, class SecondClass, class HashFunction, class Compare, class Allocator>
+class SerializeTraits<dense_hash_map<FirstClass, SecondClass, HashFunction, Compare, Allocator> > : public SerializeTraitsBase<dense_hash_map<FirstClass, SecondClass, HashFunction, Compare, Allocator> >
+{
+ public:
+
+ typedef dense_hash_map<FirstClass, SecondClass, HashFunction, Compare, Allocator> value_type;
+ DEFINE_GET_TYPESTRING_MAP_CONTAINER(map)
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ AssertIf(transfer.IsRemapPPtrTransfer() && SerializeTraits<FirstClass>::MightContainPPtr() && transfer.IsReadingPPtr());
+ transfer.TransferSTLStyleMap (data);
+ }
+};
+
+template<class FirstClass, class SecondClass, class Compare, class Allocator>
+class SerializeTraits<std::multimap<FirstClass, SecondClass, Compare, Allocator> > : public SerializeTraitsBase<std::multimap<FirstClass, SecondClass, Compare, Allocator> >
+{
+ public:
+
+ typedef std::multimap<FirstClass, SecondClass, Compare, Allocator> value_type;
+ DEFINE_GET_TYPESTRING_MAP_CONTAINER(map)
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ AssertIf(transfer.IsRemapPPtrTransfer() && SerializeTraits<FirstClass>::MightContainPPtr() && transfer.IsReadingPPtr());
+ transfer.TransferSTLStyleMap (data);
+ }
+};
+
+
+template<class T, class Compare, class Allocator>
+class SerializeTraits<std::set<T, Compare, Allocator> > : public SerializeTraitsBase<std::set<T, Compare, Allocator> >
+{
+ public:
+
+ typedef std::set<T, Compare, Allocator> value_type;
+ DEFINE_GET_TYPESTRING_CONTAINER (set)
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ AssertIf(transfer.IsRemapPPtrTransfer() && transfer.IsReadingPPtr());
+ transfer.TransferSTLStyleMap (data);
+ }
+};
+
+template<class FirstClass, class SecondClass, class Compare, class Allocator>
+class SerializeTraits<vector_map<FirstClass, SecondClass, Compare, Allocator> > : public SerializeTraitsBase<vector_map<FirstClass, SecondClass, Compare, Allocator> >
+{
+ public:
+
+ typedef vector_map<FirstClass, SecondClass, Compare, Allocator> value_type;
+ DEFINE_GET_TYPESTRING_MAP_CONTAINER (map)
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ AssertIf(transfer.IsRemapPPtrTransfer() && transfer.IsReadingPPtr());
+ transfer.TransferSTLStyleArray (data);
+ }
+
+ static bool IsContinousMemoryArray () { return true; }
+ static void ResizeSTLStyleArray (value_type& data, int rs) { data.get_vector ().resize (rs); }
+};
+
+
+
+template<class FirstClass, class SecondClass, class Compare, class Allocator>
+class SerializeTraits<us_vector_map<FirstClass, SecondClass, Compare, Allocator> > : public SerializeTraitsBase<vector_map<FirstClass, SecondClass, Compare, Allocator> >
+{
+ public:
+
+ typedef vector_map<FirstClass, SecondClass, Compare, Allocator> value_type;
+ DEFINE_GET_TYPESTRING_MAP_CONTAINER (map)
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ transfer.TransferSTLStyleArray (data);
+ }
+
+ static bool IsContinousMemoryArray () { return true; }
+ static void ResizeSTLStyleArray (value_type& data, int rs) { data.get_vector ().resize (rs); }
+};
+
+template<class T, class Compare, class Allocator>
+class SerializeTraits<vector_set<T, Compare, Allocator> > : public SerializeTraitsBase<vector_set<T, Compare, Allocator> >
+{
+ public:
+
+ typedef vector_set<T, Compare, Allocator> value_type;
+ DEFINE_GET_TYPESTRING_CONTAINER (set)
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ AssertIf(transfer.IsRemapPPtrTransfer() && transfer.IsReadingPPtr());
+ transfer.TransferSTLStyleArray (data);
+ }
+
+ static bool IsContinousMemoryArray () { return true; }
+ static void ResizeSTLStyleArray (value_type& data, int rs) { data.get_vector ().resize (rs); }
+};
+
+template<class T, class Compare, class Allocator>
+class SerializeTraits<us_vector_set<T, Compare, Allocator> > : public SerializeTraitsBase<vector_set<T, Compare, Allocator> >
+{
+ public:
+
+ typedef vector_set<T, Compare, Allocator> value_type;
+ DEFINE_GET_TYPESTRING_CONTAINER (set)
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ transfer.TransferSTLStyleArray (data);
+ }
+
+ static bool IsContinousMemoryArray () { return true; }
+ static void ResizeSTLStyleArray (value_type& data, int rs) { data.get_vector ().resize (rs); }
+};
+
+
+// Vector<bool> serialization is not allowed
+template<class Allocator>
+class SerializeTraits<std::vector<bool, Allocator> > : public SerializeTraitsBase<std::vector<bool, Allocator> >
+{
+ public:
+ // disallow vector<bool> serialization
+};
+
+
+template<class T, size_t align>
+class SerializeTraits<dynamic_array<T, align> > : public SerializeTraitsBase<dynamic_array<T, align> >
+{
+public:
+
+ typedef dynamic_array<T, align> value_type;
+ DEFINE_GET_TYPESTRING_CONTAINER (vector)
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ transfer.TransferSTLStyleArray (data);
+ }
+
+ static bool IsContinousMemoryArray () { return true; }
+ static void ResizeSTLStyleArray (value_type& data, int rs) { data.resize_initialized(rs); }
+
+ static void resource_image_assign_external (value_type& data, void* begin, void* end)
+ {
+ data.assign_external(reinterpret_cast<T*> (begin), reinterpret_cast<T*> (end));
+ }
+};
+
+template<>
+class SerializeTraits<dynamic_array<UInt8> > : public SerializeTraitsBase<dynamic_array<UInt8> >
+{
+public:
+
+ typedef dynamic_array<UInt8> value_type;
+ typedef UInt8 T;
+ DEFINE_GET_TYPESTRING_CONTAINER (vector)
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ transfer.TransferSTLStyleArray (data);
+ transfer.Align();
+ }
+
+ static bool IsContinousMemoryArray () { return true; }
+ static void ResizeSTLStyleArray (value_type& data, int rs) { data.resize_initialized(rs); }
+
+ static void resource_image_assign_external (value_type& data, void* begin, void* end)
+ {
+ data.assign_external(reinterpret_cast<UInt8*> (begin), reinterpret_cast<UInt8*> (end));
+ }
+};
+
+
+template<class T>
+struct NonConstContainerValueType
+{
+ typedef typename T::value_type value_type;
+};
+
+template<class T>
+struct NonConstContainerValueType<std::set<T> >
+{
+ typedef T value_type;
+};
+
+template<class T0, class T1, class Compare, class Allocator>
+struct NonConstContainerValueType<std::map<T0, T1, Compare, Allocator> >
+{
+ typedef std::pair<T0, T1> value_type;
+};
+
+template<class T0, class T1, class Compare, class Allocator>
+struct NonConstContainerValueType<std::multimap<T0, T1, Compare, Allocator> >
+{
+ typedef std::pair<T0, T1> value_type;
+};
+
+template<class T0, class T1, class HashFunction, class Compare, class Allocator>
+struct NonConstContainerValueType<dense_hash_map<T0, T1, HashFunction, Compare, Allocator> >
+{
+ typedef std::pair<T0, T1> value_type;
+};
+
+#endif
diff --git a/Runtime/Serialize/SerializeTraitsBase.h b/Runtime/Serialize/SerializeTraitsBase.h
new file mode 100644
index 0000000..b3a85fd
--- /dev/null
+++ b/Runtime/Serialize/SerializeTraitsBase.h
@@ -0,0 +1,61 @@
+#pragma once
+
+template<class T>
+class SerializeTraitsBase
+{
+ public:
+
+ typedef T value_type;
+
+ static int GetByteSize () {return sizeof (value_type);}
+ static size_t GetAlignOf() {return ALIGN_OF(value_type);}
+
+ static void resource_image_assign_external (value_type& /*data*/, void* /*begin*/, void* /*end*/)
+ {
+ AssertString("Unsupported");
+ }
+};
+
+template<class T>
+class SerializeTraitsBaseForBasicType : public SerializeTraitsBase<T>
+{
+public:
+ typedef T value_type;
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ transfer.TransferBasicData (data);
+ }
+};
+
+template<class T>
+class SerializeTraits : public SerializeTraitsBase<T>
+{
+public:
+
+ typedef T value_type;
+
+ inline static const char* GetTypeString (void* /*ptr*/) { return value_type::GetTypeString (); }
+ inline static bool MightContainPPtr () { return value_type::MightContainPPtr (); }
+ /// Returns whether or not a this type is to be treated as a seperate channel in the animation system
+ static bool IsAnimationChannel () { return T::IsAnimationChannel (); }
+
+ /// AllowTransferOptimization can be used for type that have the same memory format as serialized format.
+ /// Eg. a float or a Vector3f.
+ /// StreamedBinaryRead will collapse the read into a direct read when reading an array with values that have AllowTransferOptimization.
+ static bool AllowTransferOptimization () { return T::AllowTransferOptimization (); }
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ data.Transfer (transfer);
+ }
+
+};
+
+#define DEFINE_GET_TYPESTRING_CONTAINER(x) \
+inline static const char* GetTypeString (void*) { return #x; } \
+inline static bool IsAnimationChannel () { return false; } \
+inline static bool MightContainPPtr () { return SerializeTraits<T>::MightContainPPtr(); } \
+inline static bool AllowTransferOptimization () { return false; }
diff --git a/Runtime/Serialize/SerializeUtility.h b/Runtime/Serialize/SerializeUtility.h
new file mode 100644
index 0000000..086f0e3
--- /dev/null
+++ b/Runtime/Serialize/SerializeUtility.h
@@ -0,0 +1,95 @@
+#ifndef SERIALIZEUTILITY_H
+#define SERIALIZEUTILITY_H
+
+
+#include "SerializationMetaFlags.h"
+
+#define TRANSFER(x) transfer.Transfer (x, #x)
+#define TRANSFER_SIMPLE(x) transfer.Transfer (x, #x, kSimpleEditorMask)
+
+#if UNITY_EDITOR
+#define TRANSFER_EDITOR_ONLY(x) if (!transfer.IsSerializingForGameRelease()) { transfer.Transfer (x, #x, kDontAnimate); }
+#else
+#define TRANSFER_EDITOR_ONLY(x) { }
+#endif
+
+#if UNITY_EDITOR
+#define TRANSFER_EDITOR_ONLY_HIDDEN(x) if (!transfer.IsSerializingForGameRelease()) { transfer.Transfer (x, #x, kHideInEditorMask); }
+#else
+#define TRANSFER_EDITOR_ONLY_HIDDEN(x) { }
+#endif
+
+#define TRANSFER_WITH_CUSTOM_GET_SET(TYPE, STR_NAME, GET, SET, OPTIONS) \
+ { \
+ TYPE value; \
+ if (transfer.IsWriting ()) { GET ; } \
+ transfer.Transfer(value, STR_NAME, OPTIONS); \
+ if (transfer.DidReadLastProperty ()) { SET ; } \
+ }
+
+#define TRANSFER_PROPERTY(TYPE,NAME,GET,SET)\
+ TRANSFER_WITH_CUSTOM_GET_SET(TYPE, #NAME, value = GET (), SET (value), kNoTransferFlags)
+
+#define TRANSFER_ENUM(x) { Assert(sizeof(x) == sizeof(int)); transfer.Transfer ((int&)x, #x); }
+
+#if UNITY_EDITOR
+#define TRANSFER_DEBUG(x) { if (transfer.GetFlags () & kSerializeDebugProperties) transfer.Transfer (x, #x, kDebugPropertyMask | kNotEditableMask); }
+#else
+#define TRANSFER_DEBUG(x)
+#endif
+
+template<class T>
+inline bool SerializePrefabIgnoreProperties (T& transfer)
+{
+ return (transfer.GetFlags() & kSerializeForPrefabSystem) == 0;
+}
+
+/// Usage: TRANSFER_PROPERTY_DEBUG(bool, m_Enabled, data->GetEnabled)
+#define TRANSFER_PROPERTY_DEBUG(TYPE,NAME,GET) \
+if (transfer.GetFlags () & kSerializeDebugProperties){\
+ TYPE NAME;\
+ if (transfer.IsWriting ())\
+ NAME = GET ();\
+ transfer.Transfer (NAME, #NAME, kDebugPropertyMask | kNotEditableMask);\
+}
+
+
+#define DEFINE_GET_TYPESTRING(x) \
+ inline static const char* GetTypeString () { return #x; } \
+ inline static bool IsAnimationChannel () { return false; } \
+ inline static bool MightContainPPtr () { return true; } \
+ inline static bool AllowTransferOptimization () { return false; }
+
+#define DEFINE_GET_TYPESTRING_IS_ANIMATION_CHANNEL(x) \
+ inline static const char* GetTypeString () { return #x; } \
+ inline static bool IsAnimationChannel () { return true; } \
+ inline static bool MightContainPPtr () { return false; } \
+ inline static bool AllowTransferOptimization () { return true; }
+
+
+#define DECLARE_SERIALIZE(x) \
+ inline static const char* GetTypeString () { return #x; } \
+ inline static bool IsAnimationChannel () { return false; } \
+ inline static bool MightContainPPtr () { return true; } \
+ inline static bool AllowTransferOptimization () { return false; } \
+ template<class TransferFunction> \
+ void Transfer (TransferFunction& transfer);
+
+#define DECLARE_SERIALIZE_NO_PPTR(x) \
+ inline static const char* GetTypeString () { return #x; } \
+ inline static bool IsAnimationChannel () { return false; } \
+ inline static bool MightContainPPtr () { return false; } \
+ inline static bool AllowTransferOptimization () { return false; } \
+ template<class TransferFunction> \
+ void Transfer (TransferFunction& transfer);
+
+#define DECLARE_SERIALIZE_OPTIMIZE_TRANSFER(x) \
+ inline static const char* GetTypeString () { return #x; } \
+ inline static bool IsAnimationChannel () { return false; } \
+ inline static bool MightContainPPtr () { return false; } \
+ inline static bool AllowTransferOptimization () { return true; } \
+ template<class TransferFunction> \
+ void Transfer (TransferFunction& transfer);
+
+
+#endif
diff --git a/Runtime/Serialize/SerializedFile.cpp b/Runtime/Serialize/SerializedFile.cpp
new file mode 100644
index 0000000..26a0a3b
--- /dev/null
+++ b/Runtime/Serialize/SerializedFile.cpp
@@ -0,0 +1,1520 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "SerializedFile.h"
+#include "Runtime/Utilities/Utility.h"
+#include "SerializeConversion.h"
+#include "TransferUtility.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "CacheWrap.h"
+#include "Runtime/Utilities/Word.h"
+#include "Configuration/UnityConfigureVersion.h"
+#include "BuildTargetVerification.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#if UNITY_WII
+#include "PlatformDependent/Wii/WiiUtility.h"
+#include "PlatformDependent/Wii/WiiLoadingScreen.h"
+#endif
+
+#include "Runtime/Misc/Allocator.h"
+
+/// Set this to 1 to dump type trees to the console when they don't match between
+/// the runtime and the file that is being loaded. This is most useful when debugging
+/// loading issues in players (also enable DEBUG_FORCE_ALWAYS_WRITE_TYPETREES in
+/// BuildPlayerUtility.cpp to have type trees included in player data).
+#define DEBUG_LOG_TYPETREE_MISMATCHES 0
+
+using namespace std;
+
+enum { kCurrentSerializeVersion = 9 };
+
+const char* kAssetBundleVersionNumber = "1";
+const char* kUnityTextMagicString = "%YAML 1.1";
+#define kUnityTextHeaderFileID -1
+
+bool IsSerializedFileTextFile(string pathName)
+{
+ const int magiclen = strlen(kUnityTextMagicString);
+
+ char compare[256];
+ if (!ReadFromFile (pathName, compare, 0, magiclen))
+ return false;
+
+ compare[magiclen] = '\0';
+ if (strcmp(compare, kUnityTextMagicString) == 0)
+ return true;
+
+ return false;
+}
+
+
+#if ENABLE_SECURITY
+#define TEST_LEN(x) if (iterator + sizeof(x) > end) \
+{\
+ return false; \
+}
+
+#define TEST_READ_SIZE(x) if (iterator + x > end) \
+{\
+return false; \
+}
+
+#else
+#define TEST_LEN(x)
+#define TEST_READ_SIZE(x)
+#endif
+
+static const int kHeaderSize_Ver8 = 12;
+static const int kPreallocateFront = 4096;
+
+struct SerializedFileHeader
+{
+ // This header is always in BigEndian when in file
+ // Metadata follows directly after the header
+ UInt32 m_MetadataSize;
+ UInt32 m_FileSize;
+ UInt32 m_Version;
+ UInt32 m_DataOffset;
+ UInt8 m_Endianess;
+ UInt8 m_Reserved[3];
+
+ void SwapEndianess ()
+ {
+ SwapEndianBytes (m_MetadataSize);
+ SwapEndianBytes (m_FileSize);
+ SwapEndianBytes (m_Version);
+ SwapEndianBytes (m_DataOffset);
+ }
+};
+
+int RemapClassIDToNewClassID (int classID)
+{
+ switch(classID)
+ {
+ case 1012: return 1011; // AvatarSkeletonMask -> AvatarMask
+ default: return classID;
+ }
+}
+
+SerializedFile::SerializedFile ()
+: m_Externals(1024,kMemSerialization)
+{
+ m_ReadOffset = 0;
+ m_WriteDataOffset = 0;
+ m_IsDirty = false;
+ m_MemoryStream = false;
+ m_HasErrors = false;
+ m_CachedFileStream = false;
+ m_TargetPlatform = kBuildNoTargetPlatform;
+ m_SubTarget = 0;
+
+ #if SUPPORT_TEXT_SERIALIZATION
+ m_IsTextFile = false;
+ #endif
+
+ #if SUPPORT_SERIALIZE_WRITE
+ m_CachedWriter = NULL;
+ #endif
+
+ m_ReadFile = NULL;
+}
+
+
+#if SUPPORT_SERIALIZE_WRITE
+bool SerializedFile::InitializeWrite (CachedWriter& cachedWriter, BuildTargetSelection target, int options)
+{
+ SET_ALLOC_OWNER(this);
+ m_TargetPlatform = target.platform;
+ m_SubTarget = target.subTarget;
+
+ m_CachedWriter = &cachedWriter;
+
+ Assert (!((options & kAllowTextSerialization) && (options & kSerializeGameRelease)));
+ m_IsTextFile = options & kAllowTextSerialization;
+
+ if (!m_IsTextFile)
+ {
+ void* buffer = alloca (kPreallocateFront);
+ memset (buffer, 0, kPreallocateFront);
+
+ // Write header and reserve space for metadata. In case the resulting metadata will not fit
+ // in the preallocated space we'll remove it and write it tightly packed in FinalizeWrite later.
+ // In case it fits, we'll have a hole between meta and object data and that's fine.
+
+ m_CachedWriter->Write(buffer, kPreallocateFront);
+ m_WriteDataOffset = m_CachedWriter->GetPosition ();
+ }
+
+ return FinalizeInit(options);
+}
+#endif
+
+bool SerializedFile::InitializeRead (const string& path, ResourceImageGroup& resourceImage, unsigned cacheSize, unsigned cacheCount, int options, int readOffset)
+{
+ SET_ALLOC_OWNER(this);
+ m_ReadOffset = readOffset;
+ m_ReadFile = UNITY_NEW( FileCacherRead (path, cacheSize, cacheCount), kMemFile);
+ m_ResourceImageGroup = resourceImage;
+
+ return FinalizeInit(options);
+}
+
+bool SerializedFile::InitializeMemoryBlocks (const string& path, UInt8** buffer, unsigned size, unsigned offset, int options)
+{
+ SET_ALLOC_OWNER(this);
+ m_MemoryStream = true;
+ m_ReadOffset = offset;
+ m_ReadFile = UNITY_NEW( MemoryCacherReadBlocks (buffer, size, kCacheBlockSize), kMemFile);
+
+ return FinalizeInit(options);
+}
+
+bool SerializedFile::FinalizeInit (int options)
+{
+ m_Options = options;
+ #if GAMERELEASE
+ m_Options |= kSerializeGameRelease;
+ #endif
+
+ if (m_Options & kSwapEndianess)
+ m_FileEndianess = kOppositeEndianess;
+ else
+ m_FileEndianess = kActiveEndianess;
+
+ if (m_ReadFile)
+ {
+#if SUPPORT_TEXT_SERIALIZATION
+ const int magiclen = strlen(kUnityTextMagicString);
+ char compare[256];
+ if (m_ReadFile->GetFileLength () >= magiclen)
+ {
+ ReadFileCache (*m_ReadFile, compare, 0 + m_ReadOffset, magiclen);
+ compare[magiclen] = '\0';
+ if (strcmp(compare, kUnityTextMagicString) == 0)
+ {
+ m_IsTextFile = true;
+ m_ReadOffset += magiclen;
+ return ReadHeaderText();
+ }
+ }
+ m_IsTextFile = false;
+#endif
+ return ReadHeader();
+ }
+ else
+ {
+#if SUPPORT_TEXT_SERIALIZATION
+ if (m_IsTextFile)
+ {
+ string label = kUnityTextMagicString;
+ label += "\n%TAG !u! tag:unity3d.com,2011:\n";
+ m_CachedWriter->Write (&label[0], label.length());
+ }
+#endif
+ return true;
+}
+}
+
+SerializedFile::~SerializedFile ()
+{
+ UNITY_DELETE( m_ReadFile, kMemFile);
+}
+
+
+#if SUPPORT_SERIALIZE_WRITE
+bool SerializedFile::FinishWriting ()
+{
+ AssertIf(m_CachedWriter == NULL);
+
+ if (m_CachedWriter != NULL)
+ {
+ if (!m_IsTextFile)
+ {
+ SerializationCache metadataBuffer;
+
+ if (!ShouldSwapEndian())
+ {
+ BuildMetadataSection<false> (metadataBuffer, m_WriteDataOffset);
+ return WriteHeader<false> (metadataBuffer);
+ }
+ else
+ {
+ BuildMetadataSection<true> (metadataBuffer, m_WriteDataOffset);
+ return WriteHeader<true> (metadataBuffer);
+ }
+ }
+ else
+ {
+ bool success = m_CachedWriter->CompleteWriting();
+ success &= m_CachedWriter->GetCacheBase().WriteHeaderAndCloseFile(NULL, 0, 0);
+ return success;
+ }
+ }
+
+ return false;
+}
+
+static void WriteAlignmentData (File& file, size_t misalignment)
+{
+ Assert (misalignment < SerializedFile::kSectionAlignment);
+ UInt8 data[SerializedFile::kSectionAlignment];
+ memset (data, 0, misalignment);
+ file.Write(data, misalignment);
+}
+
+template<bool kSwap>
+bool SerializedFile::WriteHeader (SerializationCache& metadata)
+{
+ bool success = true;
+
+ // The aggregated metadata fits into the pre-written block, so write it directly.
+ if (metadata.size () <= kPreallocateFront - sizeof (SerializedFileHeader))
+ {
+ UInt8* temp = (UInt8*)alloca (kPreallocateFront);
+
+ SerializedFileHeader& header = *(SerializedFileHeader*)temp;
+ header.m_MetadataSize = metadata.size ();
+ header.m_FileSize = m_CachedWriter->GetPosition ();
+ header.m_Version = kCurrentSerializeVersion;
+ header.m_DataOffset = m_WriteDataOffset;
+ header.m_Endianess = m_FileEndianess;
+ memset (header.m_Reserved, 0, sizeof header.m_Reserved);
+
+ if (kActiveEndianess != kBigEndian)
+ header.SwapEndianess ();
+
+ std::copy (metadata.begin (), metadata.end (), temp + sizeof (SerializedFileHeader));
+ success &= m_CachedWriter->CompleteWriting();
+ success &= m_CachedWriter->GetCacheBase ().WriteHeaderAndCloseFile (temp, 0, sizeof (SerializedFileHeader) + metadata.size ());
+ }
+ else
+ {
+ // metadata doesn't fit, therefore close the file, write header + metadata to another file
+ // and copy data over from 'this' one.
+
+ success &= m_CachedWriter->CompleteWriting();
+ success &= m_CachedWriter->GetCacheBase ().WriteHeaderAndCloseFile (NULL, 0, 0);
+
+ size_t dataFileSize = m_CachedWriter->GetPosition ();
+ if (dataFileSize < kPreallocateFront)
+ return false;
+
+ size_t dataSize = dataFileSize - kPreallocateFront;
+ size_t dataOffsetOriginal = sizeof (SerializedFileHeader) + metadata.size ();
+ size_t dataOffset = RoundUp (dataOffsetOriginal, kSectionAlignment);
+
+ std::string originalPath = m_CachedWriter->GetCacheBase().GetPathName ();
+ std::string tempPath = GenerateUniquePathSafe (originalPath);
+
+ SerializedFileHeader header =
+ {
+ metadata.size (), dataOffset + dataSize,
+ kCurrentSerializeVersion,
+ dataOffset,
+ m_FileEndianess, 0, 0, 0
+ };
+
+ if (kActiveEndianess != kBigEndian)
+ header.SwapEndianess ();
+
+ File file;
+ success &= file.Open(tempPath, File::kWritePermission);
+
+ // header
+ success &= file.Write (&header, sizeof (header));
+
+ // metadata
+ success &= file.Write (&*metadata.begin (), metadata.size ());
+ if (dataOffset != dataOffsetOriginal)
+ WriteAlignmentData (file, dataOffset - dataOffsetOriginal);
+ FatalErrorIf (dataOffset != file.GetPosition ());
+
+ {
+ enum { kCopyChunck = 1 * 1024 * 1024 };
+
+ UInt8* buffer;
+ ALLOC_TEMP(buffer, UInt8, kCopyChunck);
+
+ File srcFile;
+ success &= srcFile.Open(originalPath, File::kReadPermission);
+
+ size_t position = kPreallocateFront;
+ size_t left = dataSize;
+ while (left > 0 && success)
+ {
+ size_t toRead = (std::min)((size_t)kCopyChunck, left);
+ int wasRead = srcFile.Read (position, buffer, toRead);
+ success &= file.Write (buffer, wasRead);
+ position += toRead;
+ left -= toRead;
+ }
+ success &= srcFile.Close ();
+
+ success &= file.Close ();
+ }
+
+ // move the temp file over to the destination
+ success &= DeleteFile(originalPath);
+ success &= MoveFileOrDirectory (tempPath, originalPath);
+ }
+
+ return success;
+}
+#endif // SUPPORT_SERIALIZE_WRITE
+
+enum { kMaxTypeCount = 100000 };
+
+bool SerializedFile::ReadHeader ()
+{
+ AssertIf (m_ReadFile == NULL);
+
+ SerializedFileHeader header;
+
+ if (m_ReadFile->GetFileLength () < sizeof (header))
+ return false;
+
+ ReadFileCache (*m_ReadFile, &header, m_ReadOffset, sizeof (header));
+
+ if (kActiveEndianess == kLittleEndian)
+ header.SwapEndianess ();
+
+ // Consistency check if the file is a valid serialized file.
+ if (header.m_MetadataSize == -1)
+ return false;
+ if (header.m_Version == 1)
+ return false;
+ if (header.m_Version > kCurrentSerializeVersion)
+ return false;
+
+ unsigned metadataSize, metadataOffset;
+ unsigned dataSize, dataOffset;
+ unsigned dataEnd;
+
+ if (header.m_Version >= 9)
+ {
+ // If we're reading a stream file, m_ReadOffset + header.m_FileSize will not necessarilly be equal to m_ReadFile->GetFileLength(),
+ // because there can be few padding bytes which doesn't count into header.m_FileSize
+ // See WriteStreamFile in BuildPlayerUtility.cpp
+ if ((m_ReadOffset + header.m_FileSize) > m_ReadFile->GetFileLength () || header.m_DataOffset > header.m_FileSize)
+ return false;
+
+ // [header][metadata[...]][data]
+
+ metadataOffset = sizeof header;
+ metadataSize = header.m_MetadataSize;
+
+ m_FileEndianess = header.m_Endianess;
+
+ dataOffset = header.m_DataOffset;
+ dataSize = header.m_FileSize - header.m_DataOffset;
+ dataEnd = dataOffset + dataSize;
+ }
+ else
+ {
+ // [header][data][metadata]
+
+ // We set dataOffset to zero, because offsets in object table are file-start based
+ dataOffset = 0;
+ dataSize = header.m_FileSize - header.m_MetadataSize - kHeaderSize_Ver8;
+ dataEnd = header.m_FileSize - header.m_MetadataSize;
+
+ // Offset by one, because we're reading the endianess flag right here
+ metadataOffset = header.m_FileSize - header.m_MetadataSize + 1;
+ metadataSize = header.m_MetadataSize - 1;
+
+ if (metadataSize == -1 || (m_ReadOffset + header.m_FileSize) > m_ReadFile->GetFileLength () || dataEnd > header.m_FileSize)
+ return false;
+
+ ReadFileCache (*m_ReadFile, &m_FileEndianess, m_ReadOffset + metadataOffset - 1, sizeof (m_FileEndianess));
+ }
+
+ // Check endianess validity
+ if (m_FileEndianess != kBigEndian && m_FileEndianess != kLittleEndian)
+ return false;
+
+ SerializationCache metadataBuffer;
+ metadataBuffer.resize (metadataSize);
+ ReadFileCache (*m_ReadFile, &metadataBuffer[0], m_ReadOffset + metadataOffset, metadataSize);
+
+ bool result;
+ if (m_FileEndianess == kActiveEndianess)
+ {
+ result = ReadMetadata<false>(header.m_Version, dataOffset, &*metadataBuffer.begin (), metadataBuffer.size (), dataEnd);
+ }
+ else
+ {
+ result = ReadMetadata<true>(header.m_Version, dataOffset, &*metadataBuffer.begin (), metadataBuffer.size (), dataEnd);
+ }
+
+ if (!result)
+ {
+ ErrorString(Format("Failed to read file '%s' because it is corrupted.", m_ReadFile->GetPathName ().c_str()));
+ }
+ return result;
+}
+
+#if SUPPORT_TEXT_SERIALIZATION
+bool SerializedFile::IndexTextFile()
+{
+ const size_t kBufferLength = 1024;
+ const size_t kMaxLineLength = 256;
+ bool hasMergeConflicts = false;
+ string read;
+ read.resize (kBufferLength);
+
+ size_t readPos = 0;
+ size_t lineStart = 0;
+ int lineCount = 1; //We start counting lines at one, not zero.
+ ObjectInfo *curInfo = NULL;
+
+ const char *guidLabel = "guid: ";
+ int guidLabelLen = strlen (guidLabel);
+ int guidLabelPos = 0;
+ bool lineContainsGUID = false;
+ std::string prevLine;
+ std::set <FileIdentifier> externals;
+ while (readPos < m_ReadFile->GetFileLength() - m_ReadOffset)
+ {
+ size_t readBufferLength = std::min (m_ReadFile->GetFileLength() - m_ReadOffset - readPos, kBufferLength);
+ ReadFileCache (*m_ReadFile, &read[0], m_ReadOffset + readPos, readBufferLength);
+
+ for (size_t i=0; i<readBufferLength; i++)
+ {
+ if (read[i] == guidLabel[guidLabelPos])
+ {
+ guidLabelPos++;
+ if (guidLabelPos == guidLabelLen)
+ lineContainsGUID = true;
+ }
+ else
+ guidLabelPos = 0;
+
+ if (read[i] == '\n')
+ {
+ lineCount++;
+ size_t lineEnd = i+1+readPos;
+ size_t lineLength = std::min(lineEnd - lineStart, kMaxLineLength);
+ string line;
+ if (lineStart < readPos)
+ {
+ line.resize (lineLength);
+ ReadFileCache (*m_ReadFile, &line[0], m_ReadOffset + lineStart, lineLength);
+ }
+ else
+ line = read.substr (lineStart - readPos, lineLength);
+
+ if (line.length())
+ {
+ switch (line[0])
+ {
+ case ' ':
+ break; //fast path for most common case.
+ case '-':
+ {
+ if (curInfo)
+ curInfo->byteSize = lineStart - curInfo->byteStart;
+ SInt32 fileID, classID;
+ if (sscanf(line.c_str(), "--- !u!%d &%d", (int*)&classID, (int*)&fileID) == 2)
+ {
+ curInfo = &m_Object[fileID];
+ curInfo->classID = RemapClassIDToNewClassID(classID);
+ curInfo->typeID = 0;
+ curInfo->byteStart = lineEnd;
+ curInfo->isDestroyed = false;
+ curInfo->debugLineStart = lineCount;
+ }
+ }
+ break;
+ #if UNITY_EDITOR
+ case '>':
+ case '<':
+ case '=':
+ if (!hasMergeConflicts)
+ WarningStringMsg ("The file %s seems to have merge conflicts. Please open it in a text editor and fix the merge.\n", m_DebugPath.c_str());
+ hasMergeConflicts = true;
+ break;
+ #endif
+ }
+ if (lineContainsGUID)
+ {
+ if (line.find ('}') == string::npos)
+ prevLine = line;
+ else
+ {
+ line = prevLine + line;
+ line = line.substr(line.find ('{'));
+ YAMLRead read (line.c_str(), line.size(), 0, &m_DebugPath, lineCount-1);
+
+ FileIdentifier id;
+
+ read.Transfer (id.guid, "guid");
+ read.Transfer (id.type, "type");
+ id.Fix_3_5_BackwardsCompatibility();
+
+ if (id.guid != UnityGUID())
+ externals.insert (id);
+ else
+ ErrorStringMsg ("Could not extract GUID in text file %s at line %d.", m_DebugPath.c_str(), lineCount-1);
+
+ lineContainsGUID = false;
+ prevLine = "";
+ }
+ }
+ }
+ lineStart = lineEnd;
+ }
+ }
+
+ readPos += readBufferLength;
+ }
+ if (curInfo)
+ curInfo->byteSize = readPos - curInfo->byteStart;
+
+ m_Externals.assign (externals.begin(), externals.end());
+ return !hasMergeConflicts;
+}
+
+bool SerializedFile::ReadHeaderText ()
+{
+ Assert (m_ReadFile != NULL);
+ return IndexTextFile ();
+}
+
+template<class T>
+void SerializedFile::WriteTextSerialized (std::string &label, T &data, int options)
+{
+ Assert (m_CachedWriter != NULL);
+
+ m_CachedWriter->Write (&label[0], label.length());
+
+ YAMLWrite writeStream (options, &m_DebugPath);
+ data.VirtualRedirectTransfer (writeStream);
+ writeStream.OutputToCachedWriter(m_CachedWriter);
+ if (writeStream.HasError())
+ m_HasErrors = true;
+}
+
+#endif
+
+// The header is put at the end of and is only allowed to be read at startup
+
+template<bool kSwap>
+bool SerializedFile::ReadMetadata (int version, unsigned dataOffset, UInt8 const* data, size_t length, size_t dataFileEnd)
+{
+ AssertIf(kSwap && kActiveEndianess == m_FileEndianess);
+ AssertIf(!kSwap && kOppositeEndianess == m_FileEndianess);
+ SET_ALLOC_OWNER(this);
+
+ UInt8 const* iterator = data, *end = data + length;
+
+ // Read Unity version file was built with
+ UnityStr unityVersion;
+ if (version >= 7)
+ {
+ if (!ReadString(unityVersion, iterator, end))
+ return false;
+ }
+
+ // Build target platform verification
+ if (version >= 8)
+ {
+ TEST_LEN(m_TargetPlatform);
+ ReadHeaderCache<kSwap> (m_TargetPlatform, iterator);
+
+ if (!CanLoadFileBuiltForTargetPlatform(static_cast<BuildTargetPlatform>(m_TargetPlatform)))
+ {
+ ErrorStringMsg(
+ "The file can not be loaded because it was created for another build target that is not compatible with this platform.\n"
+ "Please make sure to build asset bundles using the build target platform that it is used by.\n"
+ "File's Build target is: %d\n",
+ (int)m_TargetPlatform
+ );
+ return false;
+ }
+ }
+
+ // Read number of types
+ SInt32 typeCount;
+ TEST_LEN(typeCount);
+ ReadHeaderCache<kSwap> (typeCount, iterator);
+
+ #if SUPPORT_SERIALIZED_TYPETREES
+ // Read types
+ for (int i=0;i<typeCount;i++)
+ {
+ TypeTree* readType = UNITY_NEW (TypeTree, kMemTypeTree);
+ TypeMap::key_type classID;
+ TEST_LEN(classID);
+ ReadHeaderCache<kSwap> (classID, iterator);
+ if (!ReadTypeTree (*readType, iterator, end, version, kSwap))
+ return false;
+ classID = RemapClassIDToNewClassID(classID);
+
+ m_Type[classID].SetOldType (readType);
+ }
+ #else
+ if (typeCount != 0)
+ {
+ ErrorString("Serialized file contains typetrees but the target can't use them. Will ignore typetrees.");
+ }
+ #endif
+
+ SInt32 bigIDEnabled = 0;
+ if (version >= 7)
+ ReadHeaderCache<kSwap> (bigIDEnabled, iterator);
+
+ // Read number of objects
+ SInt32 objectCount;
+ TEST_LEN(objectCount);
+ ReadHeaderCache<kSwap> (objectCount, iterator);
+
+ // Check if the size is roughly out of bounds, we only want to prevent running out of memory due to insane objectCount value here.
+ TEST_READ_SIZE(objectCount * 12)
+
+ // Read Objects
+ m_Object.reserve(objectCount);
+ for (int i=0;i<objectCount;i++)
+ {
+ LocalIdentifierInFileType fileID;
+ ObjectMap::mapped_type value;
+
+ if (bigIDEnabled)
+ {
+// AssertIf(fileID64 > LOCAL_IDENTIFIER_IN_FILE_SIZE);
+ UInt64 fileID64;
+ TEST_LEN(fileID64);
+ ReadHeaderCache<kSwap> (fileID64, iterator);
+ fileID = fileID64;
+ }
+ else
+ {
+ UInt32 fileID32;
+ TEST_LEN(fileID32);
+ ReadHeaderCache<kSwap> (fileID32, iterator);
+ fileID = fileID32;
+ }
+
+ TEST_LEN(value);
+ ReadHeaderCache<kSwap> (value.byteStart, iterator);
+ ReadHeaderCache<kSwap> (value.byteSize, iterator);
+ ReadHeaderCache<kSwap> (value.typeID, iterator);
+ ReadHeaderCache<kSwap> (value.classID, iterator);
+ ReadHeaderCache<kSwap> (value.isDestroyed, iterator);
+
+ value.byteStart += dataOffset;
+
+ // TODO check this with joachim
+ value.typeID = RemapClassIDToNewClassID(value.typeID);
+ value.classID = RemapClassIDToNewClassID(value.classID);
+
+ AssertIf (value.byteStart + value.byteSize > dataFileEnd);
+ if (value.byteStart < 0 || value.byteSize < 0 || value.byteStart + value.byteSize < value.byteStart || value.byteStart + value.byteSize > dataFileEnd)
+ return false;
+
+ m_Object.push_unsorted(fileID, value);
+ //printf_console ("fileID: %d byteStart: %d classID: %d \n", fileID, value.byteStart, value.classID);
+ }
+
+ // If there's no type tree then Unity version must mach exactly.
+ //
+ // For asset bundles we write the asset bundle serialize version and compare against that.
+ // The asset bundle itself contains hashes of all serialized classes and uses it to figure out if an asset bundle can be loaded.
+ bool needsVersionCheck = !m_Object.empty() && typeCount == 0 && (m_Options & kIsBuiltinResourcesFile) == 0;
+ if (needsVersionCheck)
+ {
+ bool versionPasses;
+ string::size_type newLinePosition = unityVersion.find('\n');
+ // Compare Unity version number
+ if (newLinePosition == string::npos)
+ versionPasses = unityVersion == UNITY_VERSION;
+ // Compare asset bundle serialize version
+ else
+ versionPasses = string (unityVersion.begin() + newLinePosition + 1, unityVersion.end()) == kAssetBundleVersionNumber;
+
+ if (!versionPasses)
+ {
+ ErrorStringMsg("Invalid serialized file version. File: \"%s\". Expected version: " UNITY_VERSION ". Actual version: %s.", m_ReadFile->GetPathName().c_str(), unityVersion.c_str());
+ return false;
+ }
+ }
+
+ #if SUPPORT_SERIALIZED_TYPETREES
+ if (unityVersion.find("3.5.0f5") == 0)
+ m_Options |= kWorkaround35MeshSerializationFuckup;
+ #endif
+
+// printf_console("file version: %s - '%s'\n", unityVersion.c_str(), m_DebugPath.c_str());
+
+ // Read externals/pathnames
+ SInt32 externalsCount;
+ TEST_LEN(externalsCount);
+
+ ReadHeaderCache<kSwap> (externalsCount, iterator);
+ // Check if the size is roughly out of bounds, we only want to prevent running out of memory due to insane externalsCount value here.
+ TEST_READ_SIZE(externalsCount)
+
+ m_Externals.resize (externalsCount);
+
+ for (int i=0;i<externalsCount;i++)
+ {
+ if (version >= 5)
+ {
+ if (version >= 6)
+ {
+ ///@TODO: Remove from serialized file format
+ UnityStr tempEmpty;
+ if (!ReadString(tempEmpty, iterator, end))
+ return false;
+ }
+
+ TEST_LEN(m_Externals[i].guid.data);
+ for (int g=0;g<4;g++)
+ ReadHeaderCache<kSwap> (m_Externals[i].guid.data[g], iterator);
+
+ TEST_LEN(m_Externals[i].type);
+ ReadHeaderCache<kSwap> (m_Externals[i].type, iterator);
+ if (!ReadString (m_Externals[i].pathName, iterator, end))
+ return false;
+ }
+ else
+ {
+ if (!ReadString (m_Externals[i].pathName, iterator, end))
+ return false;
+ }
+
+ #if UNITY_EDITOR
+ m_Externals[i].Fix_3_5_BackwardsCompatibility ();
+ #endif
+
+ m_Externals[i].CheckValidity ();
+ }
+
+ // Read Userinfo string
+ if (version >= 5)
+ {
+ UnityStr userInformation;
+ if (!ReadString (userInformation, iterator, end))
+ return false;
+ }
+
+ Assert (iterator == end);
+
+ return true;
+}
+
+#if SUPPORT_SERIALIZE_WRITE
+
+template<bool kSwap>
+void SerializedFile::BuildMetadataSection (SerializationCache& cache, unsigned dataOffsetInFile)
+{
+ // Write Unity version file is being built with
+ UnityStr version = UNITY_VERSION;
+ if (m_Options & kSerializedAssetBundleVersion)
+ {
+ version += "\n";
+ version += kAssetBundleVersionNumber;
+ }
+ WriteString (version, cache);
+
+ WriteHeaderCache<kSwap> (m_TargetPlatform, cache);
+
+ if ((m_Options & kDisableWriteTypeTree) == 0)
+ {
+ // Write number of types
+ SInt32 typeCount = m_Type.size ();
+ WriteHeaderCache<kSwap> (typeCount, cache);
+
+ // Write type data
+ for (TypeMap::iterator i = m_Type.begin ();i != m_Type.end ();i++)
+ {
+ AssertIf (i->second.GetOldType () == NULL);
+ WriteHeaderCache<kSwap> (i->first, cache);
+ WriteTypeTree (*i->second.GetOldType (), cache, kSwap);
+ }
+ }
+ else
+ {
+ SInt32 typeCount = 0;
+ WriteHeaderCache<kSwap> (typeCount, cache);
+ }
+
+ SInt32 bigIDEnabled = LOCAL_IDENTIFIER_IN_FILE_SIZE > 32;
+ WriteHeaderCache<kSwap> (bigIDEnabled, cache);
+
+ // Write number of objects
+ SInt32 objectCount = m_Object.size ();
+ WriteHeaderCache<kSwap> (objectCount, cache);
+ for (ObjectMap::iterator i = m_Object.begin ();i != m_Object.end ();i++)
+ {
+ if (bigIDEnabled)
+ {
+ UInt64 bigID = i->first;
+ WriteHeaderCache<kSwap> (bigID, cache);
+ }
+ else
+ {
+ UInt32 smallID = i->first;
+ WriteHeaderCache<kSwap> (smallID, cache);
+ }
+
+ WriteHeaderCache<kSwap> (i->second.byteStart - dataOffsetInFile, cache);
+ WriteHeaderCache<kSwap> (i->second.byteSize, cache);
+ WriteHeaderCache<kSwap> (i->second.typeID, cache);
+ WriteHeaderCache<kSwap> (i->second.classID, cache);
+ WriteHeaderCache<kSwap> (i->second.isDestroyed, cache);
+
+ //printf_console ("fileID: %d byteStart: %d classID: %d \n", i->first, i->second.byteStart, i->second.classID);
+ }
+
+ // Write externals
+ objectCount = m_Externals.size ();
+ WriteHeaderCache<kSwap> (objectCount, cache);
+ for (int i=0;i<objectCount;i++)
+ {
+ UnityStr tempEmpty;
+ WriteString (tempEmpty, cache);
+ for (int g=0;g<4;g++)
+ WriteHeaderCache<kSwap> (m_Externals[i].guid.data[g], cache);
+ WriteHeaderCache<kSwap> (m_Externals[i].type, cache);
+ WriteString (m_Externals[i].pathName, cache);
+ }
+
+ // Write User info
+ UnityStr tempUserInformation;
+ WriteString (tempUserInformation, cache);
+}
+#endif
+
+bool SerializedFile::IsAvailable (LocalIdentifierInFileType id) const
+{
+ ObjectMap::const_iterator i = m_Object.find (id);
+ if (i == m_Object.end ())
+ return false;
+ else
+ return ! i->second.isDestroyed;
+}
+
+int SerializedFile::GetClassID (LocalIdentifierInFileType id) const
+{
+ ObjectMap::const_iterator i = m_Object.find (id);
+ AssertIf (i == m_Object.end ());
+ return i->second.classID;
+}
+
+int SerializedFile::GetByteStart (LocalIdentifierInFileType id) const
+{
+ ObjectMap::const_iterator i = m_Object.find (id);
+ AssertIf (i == m_Object.end ());
+ return i->second.byteStart;
+}
+
+int SerializedFile::GetByteSize (LocalIdentifierInFileType id) const
+{
+ ObjectMap::const_iterator i = m_Object.find (id);
+ AssertIf (i == m_Object.end ());
+ return i->second.byteSize;
+}
+#if SUPPORT_SERIALIZED_TYPETREES
+const TypeTree* SerializedFile::GetTypeTree (LocalIdentifierInFileType id)
+{
+ ObjectMap::iterator found = m_Object.find(id);
+ if (found == m_Object.end())
+ return NULL;
+
+ TypeMap::iterator type = m_Type.find (found->second.typeID);
+ if (type == m_Type.end ())
+ return NULL;
+ return type->second.GetOldType ();
+}
+#endif
+
+#if !UNITY_EXTERNAL_TOOL
+// objects: On return, all fileIDs to all objects in this Serialze
+void SerializedFile::GetAllFileIDs (vector<LocalIdentifierInFileType>* objects)const
+{
+ AssertIf (objects == NULL);
+
+ objects->reserve (m_Object.size ());
+ ObjectMap::const_iterator i;
+ for (i=m_Object.begin ();i!=m_Object.end ();i++)
+ {
+ if (i->second.isDestroyed)
+ continue;
+
+ Object::RTTI* rtti = Object::ClassIDToRTTI(i->second.classID);
+ if (rtti == NULL || rtti->factory == NULL)
+ continue;
+
+ objects->push_back (i->first);
+ }
+}
+#endif
+// objects: On return, all fileIDs to all objects in this Serialze
+void SerializedFile::GetAllFileIDsUnchecked (vector<LocalIdentifierInFileType>* objects)const
+{
+ AssertIf (objects == NULL);
+
+ objects->reserve (m_Object.size ());
+ ObjectMap::const_iterator i;
+ for (i=m_Object.begin ();i!=m_Object.end ();i++)
+ {
+ if (i->second.isDestroyed)
+ continue;
+
+ objects->push_back (i->first);
+ }
+}
+
+LocalIdentifierInFileType SerializedFile::GetHighestID () const
+{
+ if (m_Object.empty ())
+ return 0;
+ else
+ return m_Object.rbegin ()->first;
+}
+
+// Returns whether or not the object referenced by id was destroyed
+bool SerializedFile::DestroyObject (LocalIdentifierInFileType id)
+{
+ #if SUPPORT_SERIALIZE_WRITE
+ AssertIf(m_CachedWriter);
+ #endif
+ m_IsDirty = true;
+
+ ObjectMap::iterator o;
+ o = m_Object.find (id);
+ if (o == m_Object.end ())
+ {
+ SET_ALLOC_OWNER(this);
+ m_Object[id].isDestroyed = true;
+ return false;
+ }
+ else
+ {
+ o->second.isDestroyed = true;
+ return true;
+ }
+}
+
+#if SUPPORT_SERIALIZE_WRITE
+
+void SerializedFile::WriteObject (Object& object, LocalIdentifierInFileType fileID, const BuildUsageTag& buildUsage)
+{
+ AssertIf (m_CachedWriter == NULL);
+ SET_ALLOC_OWNER(this);
+
+ bool perObjectTypeTree = object.GetNeedsPerObjectTypeTree ();
+
+ int typeID = object.GetClassID ();
+
+ int mask = kNeedsInstanceIDRemapping | m_Options;
+
+#if UNITY_EDITOR
+ object.SetFileIDHint (fileID);
+#endif
+
+#if SUPPORT_TEXT_SERIALIZATION
+ if (!m_IsTextFile)
+ {
+#endif
+
+ // Native C++ object typetrees share typetree by class id
+ if (!perObjectTypeTree)
+ {
+ // Include Type
+ Type& type = m_Type[typeID];
+
+ // If we need a perObjectTypeTree there is only one object using it (the one we are writing now)
+ // Thus we can safely replace the old typeTree
+ // If we have a per class typetree do not override the old typetree
+ if (type.GetOldType () == NULL)
+ {
+ // Create new type and init it using a proxy transfer
+ TypeTree* typeTree = UNITY_NEW (TypeTree, kMemTypeTree);
+ GenerateTypeTree (object, typeTree, mask | kDontRequireAllMetaFlags);
+
+ // Register the type tree
+ type.SetOldType (typeTree);
+ }
+ }
+ // Scripted objects we search the registered typetrees for duplicates and share
+ // or otherwise allocate a new typetree.
+ else
+ {
+ // Create new type and init it using a proxy transfer
+ TypeTree* typeTree = UNITY_NEW (TypeTree, kMemTypeTree);
+
+ GenerateTypeTree (object, typeTree, mask | kDontRequireAllMetaFlags);
+
+ typeID = 0;
+
+ // Find if there
+ for (TypeMap::iterator i=m_Type.begin();i!=m_Type.end();i++)
+ {
+ if (i->first > 0)
+ break;
+
+ if (IsStreamedBinaryCompatbile(*typeTree, *i->second.GetOldType()))
+ {
+ typeID = i->first;
+ UNITY_DELETE(typeTree, kMemTypeTree);
+ break;
+ }
+ }
+
+ // Allocate type id
+ if (typeID == 0)
+ {
+ if (m_Type.empty())
+ typeID = -1;
+ else if (m_Type.begin()->first < 0)
+ typeID = m_Type.begin()->first - 1;
+ else
+ typeID = -1;
+
+ Type& type = m_Type[typeID];
+
+ // Create new type and init it using a proxy transfer
+ // Register the type tree
+ type.SetOldType (typeTree);
+ }
+ }
+
+#if SUPPORT_TEXT_SERIALIZATION
+ }
+#endif
+
+ // We are not taking care of fragmentation.
+ const unsigned kFileAlignment = 8;
+
+ unsigned unalignedByteStart = m_CachedWriter->GetPosition();
+
+ // Align the object to a kFileAlignment byte boundary
+ unsigned alignedByteStart = unalignedByteStart;
+ if (unalignedByteStart % kFileAlignment != 0)
+ alignedByteStart += kFileAlignment - unalignedByteStart % kFileAlignment;
+
+
+ AssertIf (m_Object.find (fileID) != m_Object.end () && m_Object.find (fileID)->second.classID != object.GetClassID ());
+ ObjectInfo& info = m_Object[fileID];
+ info.byteStart = alignedByteStart;
+ info.classID = object.GetClassID ();
+ info.isDestroyed = false;
+ info.typeID = typeID;
+
+/* ////// PRINT OUT serialized Data as ascii to console
+ if (false && gPrintfDataHack)
+ {
+ printf_console ("\n\nPrinting object: %d\n", fileID);
+
+ // Set write marker to end of file and register the objects position in file
+ StreamedTextWrite writeStream;
+ CachedWriter& cache = writeStream.Init (kNeedsInstanceIDRemapping);
+ cache.Init (m_FileCacher, alignedByteStart, 0, false);
+
+ // Write the object
+ object.VirtualRedirectTransfer (writeStream);
+ cache.End ();
+ }
+*/
+
+#if SUPPORT_TEXT_SERIALIZATION
+ if (m_IsTextFile)
+ {
+ string label = Format("--- !u!%d &%d\n", info.classID, (int)fileID);
+ WriteTextSerialized (label, object, mask);
+ }
+ else
+#endif
+ if (!ShouldSwapEndian())
+ {
+ // Set write marker to end of file and register the objects position in file
+ StreamedBinaryWrite<false> writeStream;
+
+ CachedWriter& cache = writeStream.Init (*m_CachedWriter, mask, BuildTargetSelection(static_cast<BuildTargetPlatform>(m_TargetPlatform),m_SubTarget), buildUsage);
+ char kZeroAlignment[kFileAlignment] = {0, 0, 0, 0, 0, 0, 0, 0};
+ cache.Write (kZeroAlignment, alignedByteStart - unalignedByteStart);
+
+ // Write the object
+ object.VirtualRedirectTransfer (writeStream);
+
+ *m_CachedWriter = cache;
+ }
+ else
+ {
+ // Set write marker to end of file and register the objects position in file
+ StreamedBinaryWrite<true> writeStream;
+ CachedWriter& cache = writeStream.Init (*m_CachedWriter, mask, BuildTargetSelection(static_cast<BuildTargetPlatform>(m_TargetPlatform),m_SubTarget), buildUsage);
+ char kZeroAlignment[kFileAlignment] = {0, 0, 0, 0, 0, 0, 0, 0};
+ cache.Write (kZeroAlignment, alignedByteStart - unalignedByteStart);
+
+ // Write the object
+ object.VirtualRedirectTransfer (writeStream);
+
+ *m_CachedWriter = cache;
+ }
+
+ info.byteSize = m_CachedWriter->GetPosition() - info.byteStart;
+}
+#endif
+
+#if !UNITY_EXTERNAL_TOOL
+
+enum { kMonoBehaviourClassID = 114 };
+static void OutOfBoundsReadingError (int classID, int expected, int was)
+{
+ if (classID == kMonoBehaviourClassID)
+ {
+ // This code should not access any member variables
+ // because that might dereference pointers etc during threaded loading.
+ ErrorString(Format("A script behaviour has a different serialization layout when loading. (Read %d bytes but expected %d bytes)\nDid you #ifdef UNITY_EDITOR a section of your serialized properties in any of your scripts?", was, expected));
+ }
+ else
+ {
+ ErrorString(Format("Mismatched serialization in the builtin class '%s'. (Read %d bytes but expected %d bytes)", Object::ClassIDToString(classID).c_str(), was, expected));
+ }
+}
+
+void SerializedFile::ReadObject (LocalIdentifierInFileType fileID, int instanceId, ObjectCreationMode mode, bool isPersistent, TypeTree** oldTypeTree, bool* didChangeTypeTree, Object** outObjectPtr)
+{
+#if UNITY_WII
+ wii::ProcessShutdownAndReset();
+ wii::ProcessLoadingScreen();
+#endif
+// printf_console("Reading instance: %d fileID: %d filePtr: %d\n", instanceId, fileID, this);
+
+ ObjectMap::iterator iter = m_Object.find (fileID);
+
+ // Test if the object is in stream
+ if (iter == m_Object.end ())
+ return;
+
+ if (iter->second.isDestroyed)
+ {
+ return;
+ }
+
+ const ObjectInfo& info = iter->second;
+
+ // Create empty object
+ Object* objectPtr = *outObjectPtr;
+ if (objectPtr == NULL)
+ {
+ *outObjectPtr = objectPtr = Object::Produce (info.classID, instanceId, kMemBaseObject, mode);
+ }
+
+ if (objectPtr == NULL)
+ {
+ ErrorString ("Could not produce class with ID " + IntToString (info.classID));
+ return;
+ }
+ SET_ALLOC_OWNER(this);
+#if UNITY_EDITOR
+ (**outObjectPtr).SetFileIDHint (fileID);
+#endif
+
+ #if SUPPORT_SERIALIZED_TYPETREES
+ AssertIf (objectPtr->GetClassID () != info.classID);
+ bool perClassTypeTree = true;
+ Type* type = NULL;
+ if (!m_Type.empty()
+#if SUPPORT_TEXT_SERIALIZATION
+ && !m_IsTextFile
+#endif
+ )
+ {
+ // Find TypeTree
+ type = &m_Type[info.typeID];
+ AssertIf (type->GetOldType () == NULL);
+
+ perClassTypeTree = !objectPtr->GetNeedsPerObjectTypeTree ();
+
+ AssertIf (perClassTypeTree && objectPtr->GetClassID () != info.typeID);
+ // If we have a per class type tree we do not generate a typetree before reading the object
+ // That also means we always use SafeBinaryRead.
+
+ // Setup new header type data
+ // If we have a perObject TypeTree we dont need the new type since we safebinaryread anyway.
+ // If we have a per class typetree we generate the typetree only once since it can't change
+ // while the application is running
+ if (type->GetNewType () == NULL && perClassTypeTree)
+ {
+ // Create new type and init it using a proxy transfer
+ TypeTree* typeTree = UNITY_NEW (TypeTree, kMemTypeTree);
+
+ int typeTreeOptions = kDontRequireAllMetaFlags | m_Options;
+ GenerateTypeTree (*objectPtr, typeTree, typeTreeOptions);
+
+ // Register the type tree
+ type->SetNewType (typeTree);
+
+ #if DEBUG_LOG_TYPETREE_MISMATCHES // Log when loaded typetree is out of sync.
+ if (!type->EqualTypes ())
+ {
+ printf_console("Typetree mismatch in path: %s\n", m_DebugPath.c_str());
+ printf_console("TYPETREE USED BY RUNTIME IS: \n");
+ string buffer0;
+ type->GetOldType()->DebugPrint(buffer0);
+ printf_console(buffer0.c_str());
+
+ printf_console("TYPETREE IN FILE IS:\n");
+ string buffer1;
+ type->GetNewType()->DebugPrint(buffer1);
+ printf_console(buffer1.c_str());
+ }
+ #endif
+ }
+ AssertIf (type->EqualTypes () && !perClassTypeTree);
+ }
+ #endif
+
+ int options = kNeedsInstanceIDRemapping | m_Options;
+ if (ShouldSwapEndian())
+ options |= kSwapEndianess;
+ if (mode == kCreateObjectFromNonMainThread)
+ options |= kThreadedSerialization;
+
+ objectPtr->SetIsPersistent(isPersistent);
+
+ int byteStart = info.byteStart + m_ReadOffset;
+
+ #if SUPPORT_SERIALIZED_TYPETREES
+ // Fill object with data
+
+ // Never use the SafeBinaryRead code path for Meshes serialized with 3.5, because that needs a special workaround,
+ // which requires using StreamedBinaryRead.
+ if (type != NULL && !type->EqualTypes () && !((options & kWorkaround35MeshSerializationFuckup) && info.classID == ClassID(Mesh)))
+ {
+ SafeBinaryRead readStream;
+
+ CachedReader& cache = readStream.Init (*type->GetOldType (), byteStart, info.byteSize , options);
+ cache.InitRead (*m_ReadFile, byteStart, info.byteSize);
+ Assert(m_ResourceImageGroup.resourceImages[0] == NULL);
+
+ objectPtr->Reset ();
+
+ // Read the object
+ objectPtr->VirtualRedirectTransfer (readStream);
+ int position = cache.End ();
+ if (position - byteStart > info.byteSize)
+ OutOfBoundsReadingError (info.classID, info.byteSize, position - byteStart);
+
+ *didChangeTypeTree = perClassTypeTree;
+ }
+ else
+ #endif
+ {
+ // we will read up that object - no need to call Reset as we will construct it fully
+ objectPtr->HackSetResetWasCalled();
+
+ ///@TODO: Strip endianess!
+#if SUPPORT_TEXT_SERIALIZATION
+ if (m_IsTextFile)
+ {
+ YAMLRead readStream (m_ReadFile, byteStart, byteStart + info.byteSize, options, &m_DebugPath, info.debugLineStart);
+ objectPtr->VirtualRedirectTransfer (readStream);
+ *didChangeTypeTree = false;
+ }
+ else
+#endif
+ if (!ShouldSwapEndian())
+ {
+ StreamedBinaryRead<false> readStream;
+ CachedReader& cache = readStream.Init (options);
+ cache.InitRead (*m_ReadFile, info.byteStart + m_ReadOffset, info.byteSize);
+ cache.InitResourceImages (m_ResourceImageGroup);
+
+ // Read the object
+ objectPtr->VirtualRedirectTransfer (readStream);
+ int position = cache.End ();
+ if (position - byteStart != info.byteSize)
+ OutOfBoundsReadingError (info.classID, info.byteSize, position - byteStart);
+
+ *didChangeTypeTree = false;
+ }
+ else
+ {
+ #if SUPPORT_SERIALIZED_TYPETREES
+ StreamedBinaryRead<true> readStream;
+ CachedReader& cache = readStream.Init (options);
+
+ cache.InitRead (*m_ReadFile, byteStart, info.byteSize);
+ Assert(m_ResourceImageGroup.resourceImages[0] == NULL);
+
+ // Read the object
+ objectPtr->VirtualRedirectTransfer (readStream);
+ int position = cache.End ();
+ if (position - byteStart != info.byteSize)
+ OutOfBoundsReadingError (info.classID, info.byteSize, position - byteStart);
+
+ *didChangeTypeTree = false;
+ #else
+ AssertString("reading endian swapped is not supported");
+ #endif
+ }
+ }
+
+ #if SUPPORT_SERIALIZED_TYPETREES
+ if (type)
+ *oldTypeTree = type->GetOldType ();
+ else
+ *oldTypeTree = NULL;
+ #endif
+
+ // Setup hide flags when loading from a resource file
+ if (m_Options & kIsBuiltinResourcesFile)
+ objectPtr->SetHideFlagsObjectOnly(Object::kHideAndDontSave | Object::kHideInspector);
+
+ return;
+}
+
+#endif
+
+bool SerializedFile::IsEmpty () const
+{
+ for (ObjectMap::const_iterator i = m_Object.begin ();
+ i != m_Object.end (); ++i)
+ {
+ if (!i->second.isDestroyed)
+ return false;
+ }
+ return true;
+}
+
+void SerializedFile::AddExternalRef (const FileIdentifier& pathName)
+{
+ // Dont' check for pathname here - it can be empty, if we are getting a GUID from
+ // a text serialized file, and the file belonging to the GUID is missing. In that
+ // case we just keep the GUID.
+ #if SUPPORT_SERIALIZE_WRITE
+ Assert (m_CachedWriter != NULL);
+ #endif
+ m_Externals.push_back (pathName);
+ m_Externals.back().CheckValidity();
+}
+
+void InitializeStdConverters ()
+{
+ RegisterAllowTypeNameConversion ("PPtr<TransformComponent>", "PPtr<Transform>");
+ RegisterAllowTypeNameConversion ("PPtr<FileTexture>", "PPtr<Texture2D>");
+ RegisterAllowTypeNameConversion ("PPtr<FileTexture>", "PPtr<Texture>");
+ RegisterAllowTypeNameConversion ("PPtr<Animation>", "PPtr<AnimationClip>");
+ RegisterAllowTypeNameConversion ("PPtr<GOComponent>", "PPtr<Component>");
+ RegisterAllowTypeNameConversion ("PPtr<Script>", "PPtr<TextAsset>");
+ RegisterAllowTypeNameConversion ("UniqueIdentifier", "GUID");
+ RegisterAllowTypeNameConversion ("Vector3f", "Vector2f");
+ RegisterAllowTypeNameConversion ("Vector3f", "Vector4f");
+ RegisterAllowTypeNameConversion ("vector_set", "set");
+ RegisterAllowTypeNameConversion ("vector_map", "map");
+ RegisterAllowTypeNameConversion ("map", "vector");
+ RegisterAllowTypeNameConversion ("set", "vector");
+ RegisterAllowTypeNameConversion ("list", "vector");
+ RegisterAllowTypeNameConversion ("deque", "vector");
+ RegisterAllowTypeNameConversion ("dynamic_array", "vector");
+ RegisterAllowTypeNameConversion ("TypelessData", "dynamic_array");
+ RegisterAllowTypeNameConversion ("tricky_vector", "vector");
+ RegisterAllowTypeNameConversion ("PPtr<DataTemplate>", "PPtr<Prefab>");
+ RegisterAllowTypeNameConversion ("PPtr<EditorExtension>", "PPtr<Object>");
+
+ // Support for converting MonoBehaviour GUIStyle arrays to C++ native GUIStyle
+ RegisterAllowTypeNameConversion ("GUIStyle", "vector");
+ RegisterAllowTypeNameConversion ("Generic Mono", "GUIStyle");
+ RegisterAllowTypeNameConversion ("Generic Mono", "RectOffset");
+ RegisterAllowTypeNameConversion ("Generic Mono", "GUIStyleState");
+ RegisterAllowTypeNameConversion ("PPtr<$Texture2D>", "PPtr<Texture2D>");
+ RegisterAllowTypeNameConversion ("PPtr<$Font>", "PPtr<Font>");
+
+ RegisterAllowTypeNameConversion ("PPtr<AvatarBodyMask>", "PPtr<AvatarMask>");
+ RegisterAllowTypeNameConversion ("PPtr<AnimationSet>", "PPtr<AnimatorOverrideController>");
+ RegisterAllowTypeNameConversion ("AvatarSkeletonMaskElement", "TransformMaskElement");
+ RegisterAllowTypeNameConversion ("HumanLayerConstant", "LayerConstant");
+ RegisterAllowTypeNameConversion ("PPtr<AnimatorController>", "PPtr<RuntimeAnimatorController>");
+
+#if SUPPORT_SERIALIZED_TYPETREES
+ REGISTER_CONVERTER (float, double);
+ REGISTER_CONVERTER (double, float);
+
+ REGISTER_CONVERTER (int, float);
+
+ #define REGISTER_BASIC_INTEGERTYPE_CONVERTER(x) \
+ REGISTER_CONVERTER (x, UInt64); \
+ REGISTER_CONVERTER (x, SInt64); \
+ REGISTER_CONVERTER (x, SInt32); \
+ REGISTER_CONVERTER (x, UInt32); \
+ REGISTER_CONVERTER (x, UInt16); \
+ REGISTER_CONVERTER (x, SInt16); \
+ REGISTER_CONVERTER (x, UInt8); \
+ REGISTER_CONVERTER (x, SInt8); \
+ REGISTER_CONVERTER (x, bool)
+
+
+ REGISTER_BASIC_INTEGERTYPE_CONVERTER(UInt64);
+ REGISTER_BASIC_INTEGERTYPE_CONVERTER(SInt32);
+ REGISTER_BASIC_INTEGERTYPE_CONVERTER(UInt32);
+ REGISTER_BASIC_INTEGERTYPE_CONVERTER(UInt16);
+ REGISTER_BASIC_INTEGERTYPE_CONVERTER(SInt16);
+ REGISTER_BASIC_INTEGERTYPE_CONVERTER(UInt8);
+ REGISTER_BASIC_INTEGERTYPE_CONVERTER(SInt8);
+ REGISTER_BASIC_INTEGERTYPE_CONVERTER(bool);
+
+ #undef REGISTER_BASIC_INTEGERTYPE_CONVERTER
+
+#endif
+}
+
+void CleanupStdConverters()
+{
+ ClearTypeNameConversion();
+ SafeBinaryRead::CleanupConverterTable();
+}
+
+#if UNITY_EDITOR
+bool SerializedFile::ExtractObjectData (LocalIdentifierInFileType fileID, SerializedFile::ObjectData& data)
+{
+ ObjectMap::iterator iter = m_Object.find (fileID);
+ if (iter == m_Object.end ())
+ return false;
+
+ const ObjectInfo& info = iter->second;
+ SET_ALLOC_OWNER(this);
+ // Find TypeTree
+ if (info.typeID)
+ {
+ Type& type = m_Type[info.typeID];
+ AssertIf (type.GetOldType () == NULL);
+ data.typeTree = type.GetOldType ();
+ }
+ else
+ data.typeTree = NULL;
+
+ data.classID = info.classID;
+
+ if(info.byteSize > 0)
+ {
+ CachedReader cache;
+ cache.InitRead (*m_ReadFile, m_ReadOffset + info.byteStart, info.byteSize);
+
+ data.data.resize_uninitialized(info.byteSize);
+ cache.Read(data.data.begin(), info.byteSize);
+ cache.End();
+ }
+ return true;
+}
+
+void FileIdentifier::CheckValidity ()
+{
+ FatalErrorIf (type == kMetaAssetType && !pathName.empty());
+ FatalErrorIf (type == kSerializedAssetType && !pathName.empty());
+ FatalErrorIf (type == kDeprecatedCachedAssetType);
+}
+
+void FileIdentifier::Fix_3_5_BackwardsCompatibility ()
+{
+ // Backwards compatibility with 3.5 and before.
+ // We no longer store the path for asset types. (It is implicit by the GUID)
+ if (type == FileIdentifier::kDeprecatedCachedAssetType)
+ type = FileIdentifier::kMetaAssetType;
+ if (type == FileIdentifier::kSerializedAssetType || type == FileIdentifier::kMetaAssetType)
+ pathName.clear();
+}
+
+#endif
diff --git a/Runtime/Serialize/SerializedFile.h b/Runtime/Serialize/SerializedFile.h
new file mode 100644
index 0000000..c3e431a
--- /dev/null
+++ b/Runtime/Serialize/SerializedFile.h
@@ -0,0 +1,264 @@
+#ifndef SERIALIZEDFILE_H
+#define SERIALIZEDFILE_H
+
+#include <map>
+#include <string>
+#include "TypeTree.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "FileCache.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Utilities/vector_map.h"
+#include "Runtime/Utilities/dynamic_block_vector.h"
+#if !GAMERELEASE
+#include "Runtime/Utilities/GUID.h"
+#endif
+
+extern const char* kUnityTextMagicString;
+
+using std::map;
+using std::string;
+using std::list;
+using std::vector;
+
+class CachedWriter;
+enum ObjectCreationMode;
+struct ResourceImageGroup;
+
+struct FileIdentifier
+{
+ enum { kNonAssetType = 0, kDeprecatedCachedAssetType = 1, kSerializedAssetType = 2, kMetaAssetType = 3 };
+
+ UnityStr pathName;
+ SInt32 type;
+
+ #if GAMERELEASE
+ struct GUIDPlaceHolder { UInt32 data[4]; };
+ GUIDPlaceHolder guid;
+
+ void CheckValidity () {}
+
+ #else
+
+ UnityGUID guid;
+
+ FileIdentifier (const string& p, const UnityGUID& g, int t)
+ : pathName (p), guid (g), type (t)
+ {
+ CheckValidity ();
+ }
+
+ void CheckValidity ();
+ void Fix_3_5_BackwardsCompatibility ();
+
+ bool operator < (const FileIdentifier &other) const
+ {
+ if (guid < other.guid)
+ return true;
+ else if (guid != other.guid)
+ return false;
+ else return type < other.type;
+ }
+ #endif
+
+ FileIdentifier () { type = 0; }
+
+ #if SUPPORT_TEXT_SERIALIZATION
+ DECLARE_SERIALIZE (FileIdentifier);
+ #endif
+};
+
+class SerializedFile
+{
+ struct ObjectInfo
+ {
+ SInt32 byteStart;
+ SInt32 byteSize;
+ SInt32 typeID;
+ SInt16 classID;
+ UInt16 isDestroyed;
+#if SUPPORT_TEXT_SERIALIZATION
+ SInt32 debugLineStart;
+#endif
+ };
+
+ typedef vector_map<LocalIdentifierInFileType, ObjectInfo> ObjectMap;
+
+
+#if SUPPORT_TEXT_SERIALIZATION
+ bool m_IsTextFile;
+#endif
+
+ unsigned m_ReadOffset;
+
+ #if SUPPORT_SERIALIZED_TYPETREES
+ typedef map<SInt32, Type> TypeMap;
+ TypeMap m_Type;
+ #endif
+ ObjectMap m_Object;
+ UInt8 m_FileEndianess;
+
+ bool m_IsDirty;
+ bool m_MemoryStream;
+ bool m_CachedFileStream;
+ bool m_HasErrors;
+ UInt32 m_Options;
+ UInt32 m_TargetPlatform; // enum BuildTargetPlatform
+ int m_SubTarget;
+ UInt32 m_WriteDataOffset;
+
+ dynamic_block_vector<FileIdentifier>m_Externals;
+
+ CacheReaderBase* m_ReadFile;
+ ResourceImageGroup m_ResourceImageGroup;
+
+ #if ENABLE_PROFILER || UNITY_EDITOR
+ std::string m_DebugPath;
+ #endif
+
+ #if SUPPORT_SERIALIZE_WRITE
+ CachedWriter* m_CachedWriter;
+
+ //unsigned m_ObjectBufferStart;
+ #endif
+ public:
+
+ enum { kSectionAlignment = 16 };
+
+ enum
+ {
+ kLittleEndian = 0,
+ kBigEndian = 1,
+
+ #if UNITY_BIG_ENDIAN
+ kActiveEndianess = kBigEndian,
+ kOppositeEndianess = kLittleEndian
+ #else
+ kActiveEndianess = kLittleEndian,
+ kOppositeEndianess = kBigEndian
+ #endif
+ };
+
+ SerializedFile ();
+
+ // options: kSerializeGameRelease, kSwapEndianess, kBuildPlayerOnlySerializeBuildProperties
+ bool InitializeWrite (CachedWriter& cachedWriter, BuildTargetSelection target, int options);
+ bool InitializeRead (const std::string& path, ResourceImageGroup& resourceImage, unsigned cacheSize, unsigned cacheCount, int options, int readOffset = 0);
+ bool InitializeMemory (const string& path, UInt8* buffer, unsigned size, int options);
+ bool InitializeMemoryBlocks (const string& path, UInt8** buffer, unsigned size, unsigned offset, int options);
+
+ ~SerializedFile ();
+
+ static void DeleteNoFlush (SerializedFile* file);
+
+ #if ENABLE_PROFILER || UNITY_EDITOR
+ void SetDebugPath(const std::string& path) { m_DebugPath = path; }
+ const std::string& GetDebugPath() const { return m_DebugPath; }
+ #endif
+
+ // Writes an object with id to the file. (Possibly overriding any older versions)
+ // Writing to a stream which includes objects with an older typetree version is not possible and false will be returned
+ void WriteObject (Object& object, LocalIdentifierInFileType fileID, const BuildUsageTag& buildUsage);
+
+ // Reads the object referenced by id from disk
+ // Returns a pointer to the object. (NULL if no object was found on disk)
+ // object is either PRODUCED or the object already in memory referenced by id is used
+ // isMarkedDestroyed is a returned by value (non-NULL)
+ // registerInstanceID should the instanceID be register with the ID To Object lookup (false for threaded loading)
+ // And reports whether the object read was marked as destroyed or not
+ void ReadObject (LocalIdentifierInFileType fileID, int instanceId, ObjectCreationMode mode, bool isPersistent, TypeTree** oldTypeTree, bool* didChangeTypeTree, Object** readObject);
+
+ // Returns whether or not the object referenced by id was destroyed
+ bool DestroyObject (LocalIdentifierInFileType id);
+
+ // objects: On return, all fileIDs to all objects in this Serialze
+ void GetAllFileIDs (vector<LocalIdentifierInFileType>* objects)const;
+ void GetAllFileIDsUnchecked (vector<LocalIdentifierInFileType>* objects)const;
+
+ // Returns the biggest id of all the objects in the file.
+ // if no objects are in the file 0 is returned
+ LocalIdentifierInFileType GetHighestID () const;
+
+ // Returns whether or not an object is available in the stream
+ bool IsAvailable (LocalIdentifierInFileType id) const;
+ // Returns the classID of the object at id
+ int GetClassID (LocalIdentifierInFileType id) const;
+
+ // Returns the size the object takes up on the disk
+ int GetByteSize (LocalIdentifierInFileType id) const;
+
+ // Returns the seek position in the file where the object with id is stored
+ int GetByteStart (LocalIdentifierInFileType id) const;
+
+ // Returns the seek position in the file where the object with id is stored
+ const TypeTree* GetTypeTree (LocalIdentifierInFileType id);
+
+ // Are there any objects stored in this serialize.
+ bool IsEmpty () const;
+
+ bool HasErrors () const { return m_HasErrors; }
+
+ #if UNITY_EDITOR
+ struct ObjectData
+ {
+ int classID;
+ dynamic_array<UInt8> data;
+ TypeTree* typeTree;
+ };
+ bool ExtractObjectData (LocalIdentifierInFileType fileID, ObjectData& data);
+ #endif
+
+ // Get/Set the list of FileIdentifiers this file uses
+ const dynamic_block_vector<FileIdentifier>& GetExternalRefs ()const { return m_Externals; }
+
+ // Add an external reference
+ void AddExternalRef (const FileIdentifier& pathName);
+
+ // Is the header of the file written?
+ bool IsFileDirty () const { return m_IsDirty; }
+
+ inline bool ShouldSwapEndian () const { return m_FileEndianess != kActiveEndianess; }
+
+ bool IsMemoryStream () const { return m_MemoryStream; }
+ bool IsCachedFileStream () const { return m_CachedFileStream; }
+
+ void SetIsCachedFileStream (bool cache) { m_CachedFileStream = cache; }
+
+ #if SUPPORT_SERIALIZE_WRITE
+ bool FinishWriting ();
+ #endif
+
+ #if SUPPORT_TEXT_SERIALIZATION
+ bool IsTextFile () const { return m_IsTextFile; }
+ #endif
+private:
+ // Writes everything that is in the caches out to the disk.
+ void Flush ();
+
+ bool FinalizeInit (int options);
+ bool ReadHeader ();
+
+ template<bool kSwap> bool ReadMetadata (int version, unsigned dataOffset, UInt8 const* data, size_t length, size_t dataFileSize);
+
+#if SUPPORT_SERIALIZE_WRITE
+ template<bool kSwap> void BuildMetadataSection (SerializationCache& cache, unsigned dataOffsetInFile);
+ template<bool kSwap> bool WriteHeader (SerializationCache& cache);
+#endif
+
+#if SUPPORT_TEXT_SERIALIZATION
+ template<class T>
+ void WriteTextSerialized (string& label, T &data, int options);
+ bool IndexTextFile ();
+ bool ReadHeaderText ();
+#endif
+
+
+};
+
+#if SUPPORT_TEXT_SERIALIZATION
+bool IsSerializedFileTextFile(string pathName);
+#endif
+
+void InitializeStdConverters ();
+void CleanupStdConverters();
+
+#endif
diff --git a/Runtime/Serialize/SerializedFileTests.cpp b/Runtime/Serialize/SerializedFileTests.cpp
new file mode 100644
index 0000000..7847aa9
--- /dev/null
+++ b/Runtime/Serialize/SerializedFileTests.cpp
@@ -0,0 +1,39 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "SerializedFile.h"
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Serialize/FileCache.h"
+
+SUITE (SerializedFile)
+{
+ #if SUPPORT_SERIALIZE_WRITE
+ TEST_FIXTURE (SerializedFile, ReadWriteSerializedFileWorks)
+ {
+ CachedWriter writer;
+ FileCacherWrite writeFile;
+ writeFile.InitWriteFile("test.serialized", 16);
+ writer.InitWrite(writeFile);
+
+ SerializedFile* file = UNITY_NEW_AS_ROOT (SerializedFile(), kMemSerialization, "SerializedFile", "");
+ CHECK(file->InitializeWrite (writer, BuildTargetSelection::NoTarget(), 0));
+ CHECK(file->FinishWriting());
+ UNITY_DELETE(file, kMemSerialization);
+
+ file = UNITY_NEW_AS_ROOT (SerializedFile(), kMemSerialization, "SerializedFile", "");
+
+ ResourceImageGroup resources;
+ CHECK(file->InitializeRead("test.serialized", resources, 16, 2, 0));
+ CHECK(!file->IsFileDirty());
+ CHECK(file->IsEmpty());
+
+ UNITY_DELETE(file, kMemSerialization);
+ DeleteFile("test.serialized");
+ }
+ #endif
+
+} // SUITE
+
+
+#endif
diff --git a/Runtime/Serialize/SwapEndianArray.h b/Runtime/Serialize/SwapEndianArray.h
new file mode 100644
index 0000000..2fbf30f
--- /dev/null
+++ b/Runtime/Serialize/SwapEndianArray.h
@@ -0,0 +1,27 @@
+#ifndef SWAPENDIANARRAY_H
+#define SWAPENDIANARRAY_H
+
+#include "SwapEndianBytes.h"
+
+inline void SwapEndianArray (void* data, int bytesPerComponent, int count)
+{
+
+ if (bytesPerComponent == 2)
+ {
+ UInt16* p = (UInt16*)data;
+ for (int i=0;i<count;i++)
+ SwapEndianBytes (*p++);
+ }
+ else if (bytesPerComponent == 4)
+ {
+ UInt32* p = (UInt32*)data;
+ for (int i=0;i<count;i++)
+ SwapEndianBytes (*p++);
+ }
+ else
+ {
+ AssertIf (bytesPerComponent != 1);
+ }
+}
+
+#endif
diff --git a/Runtime/Serialize/SwapEndianBytes.h b/Runtime/Serialize/SwapEndianBytes.h
new file mode 100644
index 0000000..2a55246
--- /dev/null
+++ b/Runtime/Serialize/SwapEndianBytes.h
@@ -0,0 +1,89 @@
+#ifndef SWAPENDIANBYTES_H
+#define SWAPENDIANBYTES_H
+
+inline void SwapEndianBytes (char&) { }
+inline void SwapEndianBytes (unsigned char&) { }
+inline void SwapEndianBytes (bool&) { }
+inline void SwapEndianBytes (signed char&) { }
+
+#if UNITY_WII
+inline void SwapEndianBytes (register UInt16& i)
+{
+ __asm {
+ lhbrx r3, 0, i
+ sth r3, 0(i)
+ }
+}
+#else
+inline void SwapEndianBytes (UInt16& i) { i = static_cast<UInt16>((i << 8) | (i >> 8)); }
+#endif
+
+inline void SwapEndianBytes (SInt16& i) { SwapEndianBytes (reinterpret_cast<UInt16&> (i)); }
+
+#if UNITY_WII
+inline void SwapEndianBytes (register UInt32& i)
+{
+ __asm {
+ lwbrx r3, 0, i
+ stw r3, 0(i)
+ }
+}
+#else
+inline void SwapEndianBytes (UInt32& i) { i = (i >> 24) | ((i >> 8) & 0x0000ff00) | ((i << 8) & 0x00ff0000) | (i << 24); }
+#endif
+
+inline void SwapEndianBytes (SInt32& i) { SwapEndianBytes (reinterpret_cast<UInt32&> (i)); }
+inline void SwapEndianBytes (float& i) { SwapEndianBytes (reinterpret_cast<UInt32&> (i)); }
+
+inline void SwapEndianBytes (UInt64& i)
+{
+#if UNITY_WII
+ register unsigned int temp1, temp2;
+ register UInt32* p0 = reinterpret_cast<UInt32*>( &i );
+ register UInt32* p1 = reinterpret_cast<UInt32*>( &i ) + 1;
+ __asm {
+ lwbrx temp1, 0, p0
+ lwbrx temp2, 0, p1
+ stw temp1, 0(p1)
+ stw temp2, 0(p0)
+ }
+#else
+ UInt32* p = reinterpret_cast<UInt32*>(&i);
+ UInt32 u = (p[0] >> 24) | (p[0] << 24) | ((p[0] & 0x00ff0000) >> 8) | ((p[0] & 0x0000ff00) << 8);
+ p[0] = (p[1] >> 24) | (p[1] << 24) | ((p[1] & 0x00ff0000) >> 8) | ((p[1] & 0x0000ff00) << 8);
+ p[1] = u;
+#endif
+}
+
+inline void SwapEndianBytes (SInt64& i) { SwapEndianBytes (reinterpret_cast<UInt64&> (i)); }
+inline void SwapEndianBytes (double& i) { SwapEndianBytes (reinterpret_cast<UInt64&> (i)); }
+
+#if UNITY_64 && UNITY_OSX
+inline void SwapEndianBytes (size_t &i) { SwapEndianBytes (reinterpret_cast<UInt64&> (i)); }
+#endif
+
+inline bool IsBigEndian ()
+{
+ #if UNITY_BIG_ENDIAN
+ return true;
+ #else
+ return false;
+ #endif
+}
+
+inline bool IsLittleEndian ()
+{
+ return !IsBigEndian();
+}
+
+#if UNITY_BIG_ENDIAN
+#define SwapEndianBytesBigToNative(x)
+#define SwapEndianBytesLittleToNative(x) SwapEndianBytes(x)
+#define SwapEndianBytesNativeToLittle(x) SwapEndianBytes(x)
+#elif UNITY_LITTLE_ENDIAN
+#define SwapEndianBytesBigToNative(x) SwapEndianBytes(x)
+#define SwapEndianBytesLittleToNative(x)
+#define SwapEndianBytesNativeToLittle(x)
+#endif
+
+#endif
diff --git a/Runtime/Serialize/TransferFunctionFwd.h b/Runtime/Serialize/TransferFunctionFwd.h
new file mode 100644
index 0000000..2307dcf
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctionFwd.h
@@ -0,0 +1,14 @@
+#ifndef TRANFER_FUNCTION_FWD_H_
+#define TRANFER_FUNCTION_FWD_H_
+
+class ProxyTransfer;
+class RemapPPtrTransfer;
+class SafeBinaryRead;
+template<bool kSwapEndianess> class StreamedBinaryRead;
+template<bool kSwapEndianess> class StreamedBinaryWrite;
+class YAMLRead;
+class YAMLWrite;
+class BlobWrite;
+class BlobSize;
+
+#endif
diff --git a/Runtime/Serialize/TransferFunctions/ProxyTransfer.cpp b/Runtime/Serialize/TransferFunctions/ProxyTransfer.cpp
new file mode 100644
index 0000000..de4f731
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/ProxyTransfer.cpp
@@ -0,0 +1,250 @@
+#include "UnityPrefix.h"
+#include "ProxyTransfer.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Serialize/CacheWrap.h"
+#include "Runtime/Serialize/SerializationMetaFlags.h"
+
+using namespace std;
+
+ProxyTransfer::ProxyTransfer (TypeTree& t, int options, void* objectPtr, int objectSize)
+ : m_TypeTree (t),
+ m_ActiveFather (NULL)
+{
+ m_Flags = options;
+
+ m_ObjectPtr = (char*)objectPtr;
+ m_ObjectSize = objectSize;
+ m_Index = 0;
+ m_SimulatedByteOffset = 0;
+ m_RequireTypelessData = false;
+ m_DidErrorAlignment = false;
+}
+
+void ProxyTransfer::BeginArrayTransfer (const char* name, const char* typeString, SInt32& size, TransferMetaFlags metaFlag)
+{
+ AssertIf (m_ActiveFather->m_IsArray);
+ BeginTransfer (name, typeString, NULL, metaFlag);
+ m_ActiveFather->m_IsArray = true;
+
+ // transfer size
+ Transfer (size, "size");
+}
+
+void ProxyTransfer::BeginTransfer (const char* name, const char* typeString, char* dataPtr, TransferMetaFlags metaFlag)
+{
+ #if !UNITY_RELEASE
+ if (m_RequireTypelessData)
+ {
+ AssertString ("TransferTypeless needs to be followed by TransferTypelessData with no other variables in between!");
+ }
+ #endif
+
+ #if DEBUGMODE
+ // skip this check for debug mode inspector, as we can have interface names from C# in the debug data.
+ if (!(metaFlag & kDebugPropertyMask) && (strstr (name, ".") != NULL || strstr (name, "Array[") != NULL))
+ {
+ string s = "Illegal serialize property name :";
+ GetTypePath (m_ActiveFather, s);
+ s += name;
+ s += "\n The name may not contain '.' or Array[";
+ ErrorString (s);
+ }
+ #endif
+
+ TypeTree* typeTree;
+ // Setup a normal typetree child
+ if (m_ActiveFather != NULL)
+ {
+ // Check for multiple occurences of same name
+ #if DEBUGMODE
+ TypeTree::TypeTreeList::iterator i;
+ for (i=m_ActiveFather->m_Children.begin ();i != m_ActiveFather->m_Children.end ();++i)
+ {
+ if (i->m_Name == name)
+ {
+ string s = "The same field name is serialized multiple names in the class or it's parent class. This is not supported: ";
+ GetTypePath (m_ActiveFather, s);
+ s += name;
+ ErrorString (s);
+ }
+ }
+ #endif
+
+ m_ActiveFather->m_Children.push_back (TypeTree ());
+ // Setup new type
+ typeTree = &m_ActiveFather->m_Children.back ();
+ typeTree->m_Father = m_ActiveFather;
+ typeTree->m_Type = typeString;
+ typeTree->m_Name = name;
+ typeTree->m_MetaFlag = metaFlag | m_ActiveFather->m_MetaFlag;
+ AssertIf(typeTree->m_MetaFlag & kAlignBytesFlag);
+ typeTree->m_MetaFlag &= ~(kAnyChildUsesAlignBytesFlag);
+ typeTree->m_ByteSize = 0;
+ }
+ // Setup root TypeTree
+ else
+ {
+ m_TypeTree.m_Father = NULL;
+ m_TypeTree.m_Type = typeString;
+ m_TypeTree.m_Name = name;
+ m_TypeTree.m_MetaFlag = metaFlag;
+ m_TypeTree.m_ByteSize = 0;
+ typeTree = &m_TypeTree;
+ }
+
+ // Calculate typetree index
+ if ((typeTree->m_MetaFlag & kDebugPropertyMask) == 0)
+ typeTree->m_Index = m_Index++;
+ else
+ {
+ if (m_Flags & kIgnoreDebugPropertiesForIndex)
+ typeTree->m_Index = -1;
+ else
+ typeTree->m_Index = m_Index++;
+ }
+
+ m_ActiveFather = typeTree;
+
+ int offset = dataPtr - m_ObjectPtr;
+ if (m_ObjectPtr && dataPtr && offset >= 0 && offset < m_ObjectSize)
+ m_ActiveFather->m_ByteOffset = offset;
+
+ m_ActiveFather->m_DirectPtr = dataPtr;
+}
+
+void ProxyTransfer::AssertContainsNoPPtr (const TypeTree* typeTree)
+{
+ AssertIf(typeTree->m_Type.find("PPtr<") == 0);
+ for (TypeTree::const_iterator i=typeTree->begin();i != typeTree->end();i++)
+ AssertContainsNoPPtr(&*i);
+}
+
+void ProxyTransfer::AssertOptimizeTransfer (int sizeofSize)
+{
+ if (m_ActiveFather->IsBasicDataType())
+ {
+ AssertIf(sizeofSize != m_ActiveFather->m_ByteSize);
+ return;
+ }
+
+ int size = 0;
+ int baseOffset = m_ActiveFather->m_ByteOffset;
+ for (TypeTree::const_iterator i=m_ActiveFather->begin();i != m_ActiveFather->end();i++)
+ {
+ AssertOptimizeTransferImpl(*i, baseOffset, &size);
+ }
+
+ // Assert if serialized size is different from sizeof size.
+ // - Ignore when serializing for game release. We might be serializing differently in that case. (AnimationCurves)
+ AssertIf(sizeofSize != size && (m_Flags & kSerializeGameRelease) == 0);
+}
+
+void ProxyTransfer::AssertOptimizeTransferImpl (const TypeTree& typetree, int baseOffset, int* totalSize)
+{
+ if (typetree.m_ByteOffset != -1)
+ AssertIf (typetree.m_ByteOffset - baseOffset != *totalSize);
+
+ AssertIf (typetree.m_MetaFlag & kAlignBytesFlag);
+
+ if (typetree.IsBasicDataType())
+ {
+ *totalSize += typetree.m_ByteSize;
+ return;
+ }
+
+ for (TypeTree::const_iterator i=typetree.begin();i != typetree.end();i++)
+ AssertOptimizeTransferImpl(*i, baseOffset, totalSize);
+}
+
+void ProxyTransfer::EndTransfer ()
+{
+ TypeTree* current = m_ActiveFather;
+ // Add bytesize to parent!
+ m_ActiveFather = m_ActiveFather->m_Father;
+ if (m_ActiveFather)
+ {
+ if (current->m_ByteSize != -1 && m_ActiveFather->m_ByteSize != -1)
+ m_ActiveFather->m_ByteSize += current->m_ByteSize;
+ else
+ m_ActiveFather->m_ByteSize = -1;
+
+ // Propagate if any child uses alignment up to parents
+ if (current->m_MetaFlag & kAnyChildUsesAlignBytesFlag)
+ {
+ m_ActiveFather->m_MetaFlag |= kAnyChildUsesAlignBytesFlag;
+ }
+
+ DebugAssertIf (m_ActiveFather->m_ByteSize == 0 && m_ActiveFather->m_Type == "Generic Mono");
+ }
+}
+
+void ProxyTransfer::EndArrayTransfer ()
+{
+ m_ActiveFather->m_ByteSize = -1;
+ EndTransfer ();
+}
+
+void ProxyTransfer::SetVersion (int version)
+{
+ // You can not set the version twice on the same type.
+ // Probably an inherited class already calls SetVersion
+ AssertIf (m_ActiveFather->m_Version != 1);
+
+ m_ActiveFather->m_Version = version;
+}
+
+void ProxyTransfer::TransferTypeless (unsigned* byteSize, const char* name, TransferMetaFlags metaFlag)
+{
+ SInt32 size;
+ BeginArrayTransfer (name, "TypelessData", size, metaFlag);
+
+ UInt8 temp;
+ Transfer (temp, "data", metaFlag);
+
+ m_RequireTypelessData = true;
+
+ EndArrayTransfer ();
+
+ Align ();
+}
+
+void ProxyTransfer::TransferTypelessData (unsigned byteSize, void* copyData, int metaData)
+{
+ m_RequireTypelessData = false;
+}
+
+void ProxyTransfer::Align ()
+{
+ m_SimulatedByteOffset = Align4(m_SimulatedByteOffset);
+
+ if (m_ActiveFather && !m_ActiveFather->m_Children.empty())
+ {
+ m_ActiveFather->m_Children.back().m_MetaFlag |= kAlignBytesFlag;
+ m_ActiveFather->m_MetaFlag |= kAnyChildUsesAlignBytesFlag;
+ }
+ else
+ {
+ AssertString("Trying to align type data before anything has been serialized!");
+ }
+}
+
+#if UNITY_EDITOR
+void ProxyTransfer::LogUnalignedTransfer ()
+{
+ if (m_DidErrorAlignment)
+ return;
+
+ // For now we only support 4 byte alignment
+ int size = m_ActiveFather->m_ByteSize;
+ if (size == 8)
+ size = 4;
+ if (m_SimulatedByteOffset % size == 0)
+ return;
+
+ m_DidErrorAlignment = true;
+
+ string path;
+ GetTypePath(m_ActiveFather, path);
+ LogString(Format("Unaligned transfer in '%s' at variable '%s'.\nNext unaligned data path: %s", m_TypeTree.m_Type.c_str(), m_ActiveFather->m_Name.c_str(), path.c_str()));
+}
+#endif
diff --git a/Runtime/Serialize/TransferFunctions/ProxyTransfer.h b/Runtime/Serialize/TransferFunctions/ProxyTransfer.h
new file mode 100644
index 0000000..662ad05
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/ProxyTransfer.h
@@ -0,0 +1,159 @@
+#ifndef PROXYTRANSFER_H
+#define PROXYTRANSFER_H
+
+#include "Runtime/Serialize/TransferFunctions/TransferBase.h"
+
+class YAMLNode;
+struct StreamingInfo;
+struct ReduceCopyData;
+
+class EXPORT_COREMODULE ProxyTransfer : public TransferBase
+{
+ TypeTree& m_TypeTree;
+ TypeTree* m_ActiveFather;
+ char* m_ObjectPtr;
+ int m_ObjectSize;
+ int m_Index;
+ int m_SimulatedByteOffset;
+ bool m_DidErrorAlignment;
+ bool m_RequireTypelessData;
+ friend class MonoBehaviour;
+
+public:
+
+ ProxyTransfer (TypeTree& t, int flags, void* objectPtr, int objectSize);
+
+ void SetVersion (int version);
+ bool IsVersionSmallerOrEqual (int version) { Assert(m_ActiveFather->m_Version > version); return false; }
+ bool IsOldVersion (int version) { Assert(m_ActiveFather->m_Version > version); return false; }
+ bool NeedNonCriticalMetaFlags () { return (m_Flags & kDontRequireAllMetaFlags) == 0; }
+
+ void AddMetaFlag (TransferMetaFlags flag) { m_ActiveFather->m_MetaFlag = m_ActiveFather->m_MetaFlag | flag; }
+
+ template<class T>
+ void Transfer (T& data, const char* name, TransferMetaFlags metaFlag = kNoTransferFlags);
+ template<class T>
+ void TransferWithTypeString (T& data, const char* name, const char* typeName, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ void TransferTypeless (unsigned* byteSize, const char* name, TransferMetaFlags metaFlag = kNoTransferFlags);
+ void TransferTypelessData (unsigned, void*, int metaData = 0);
+
+ template<class T>
+ void TransferBasicData (T& data);
+
+ template<class T>
+ void TransferPtr (bool, ReduceCopyData*){}
+
+ template<class T>
+ void TransferSTLStyleArray (T& data, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ template<class T>
+ void TransferSTLStyleMap (T& data, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ template<class T> inline
+ void TransferSTLStyleArrayWithElement (T& elementType, TransferMetaFlags metaFlag);
+
+ void Align ();
+
+ void BeginTransfer (const char* name, const char* typeString, char* data, TransferMetaFlags metaFlag);
+ void EndTransfer ();
+
+ bool GetTransferFileInfo(unsigned* /*position*/, const char** /*filePath*/) const { return false; }
+
+private:
+
+ void LogUnalignedTransfer ();
+ void AssertContainsNoPPtr (const TypeTree* typeTree);
+ void AssertOptimizeTransfer (int sizeofSize);
+ void AssertOptimizeTransferImpl (const TypeTree& typetree, int baseOffset, int* totalSize);
+ void CheckAlignment ();
+
+
+ void BeginArrayTransfer (const char* name, const char* typeString, SInt32& size, TransferMetaFlags metaFlag);
+ void EndArrayTransfer ();
+};
+
+
+template<class T> inline
+void ProxyTransfer::TransferSTLStyleArray (T& /*data*/, TransferMetaFlags metaFlag)
+{
+ SInt32 size;
+ BeginArrayTransfer ("Array", "Array", size, metaFlag);
+
+ typename T::value_type p;
+ Transfer (p, "data");
+
+ // Make sure MightContainPPtr and AllowTransferOptimization is setup correctly
+ DebugAssertIf(SerializeTraits<T>::AllowTransferOptimization());
+
+ EndArrayTransfer ();
+}
+
+template<class T> inline
+void ProxyTransfer::TransferSTLStyleArrayWithElement (T& elementType, TransferMetaFlags metaFlag)
+{
+ SInt32 size;
+ BeginArrayTransfer ("Array", "Array", size, metaFlag);
+
+ Transfer (elementType, "data");
+
+ EndArrayTransfer ();
+}
+
+
+template<class T> inline
+void ProxyTransfer::TransferSTLStyleMap (T&, TransferMetaFlags metaFlag)
+{
+ SInt32 size;
+ BeginArrayTransfer ("Array", "Array", size, metaFlag);
+
+ typename NonConstContainerValueType<T>::value_type p;
+ Transfer (p, "data");
+
+ #if !UNITY_RELEASE
+ DebugAssertIf(SerializeTraits<T>::AllowTransferOptimization());
+ #endif
+
+ EndArrayTransfer ();
+}
+
+template<class T> inline
+void ProxyTransfer::Transfer (T& data, const char* name, TransferMetaFlags metaFlag)
+{
+ BeginTransfer (name, SerializeTraits<T>::GetTypeString (&data), (char*)&data, metaFlag);
+ SerializeTraits<T>::Transfer (data, *this);
+
+ // Make sure MightContainPPtr and AllowTransferOptimization is setup correctly
+ #if !UNITY_RELEASE
+ if (!SerializeTraits<T>::MightContainPPtr())
+ AssertContainsNoPPtr(m_ActiveFather);
+ if (SerializeTraits<T>::AllowTransferOptimization())
+ AssertOptimizeTransfer(SerializeTraits<T>::GetByteSize());
+ #endif
+
+ EndTransfer ();
+}
+
+template<class T> inline
+void ProxyTransfer::TransferWithTypeString (T& data, const char* name, const char* typeName, TransferMetaFlags metaFlag)
+{
+ BeginTransfer (name, typeName, (char*)&data, metaFlag);
+ SerializeTraits<T>::Transfer (data, *this);
+ EndTransfer ();
+}
+
+
+template<class T>
+inline void ProxyTransfer::TransferBasicData (T&)
+{
+ m_ActiveFather->m_ByteSize = SerializeTraits<T>::GetByteSize ();
+ #if UNITY_EDITOR
+ if (m_SimulatedByteOffset % m_ActiveFather->m_ByteSize != 0)
+ {
+ LogUnalignedTransfer();
+ }
+ m_SimulatedByteOffset += m_ActiveFather->m_ByteSize;
+ #endif
+}
+
+#endif
diff --git a/Runtime/Serialize/TransferFunctions/ProxyTransferTests.cpp b/Runtime/Serialize/TransferFunctions/ProxyTransferTests.cpp
new file mode 100644
index 0000000..2f62542
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/ProxyTransferTests.cpp
@@ -0,0 +1,216 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Serialize/TypeTree.h"
+#include "Runtime/Serialize/TransferFunctions/ProxyTransfer.h"
+
+struct TypeTreeTestItem
+{
+public:
+ DECLARE_SERIALIZE (TypeTreeTestItem);
+
+ float m_float;
+ double m_double;
+ int m_int; // Same as SInt32 to serialization system.
+ unsigned int m_unsignedint; // Same as UInt32 to serialization system.
+ SInt64 m_SInt64;
+ UInt64 m_UInt64;
+ SInt32 m_SInt32;
+ UInt32 m_UInt32;
+ SInt16 m_SInt16;
+ UInt16 m_UInt16;
+ SInt8 m_SInt8;
+ UInt8 m_UInt8;
+ char m_char;
+ bool m_bool;
+ UnityStr m_UnityStr;
+};
+
+template<class TransferFunction>
+void TypeTreeTestItem::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (m_float);
+ TRANSFER (m_double);
+ TRANSFER (m_int);
+ TRANSFER (m_unsignedint);
+ TRANSFER (m_SInt64);
+ TRANSFER (m_UInt64);
+ TRANSFER (m_SInt32);
+ TRANSFER (m_UInt32);
+ TRANSFER (m_SInt16);
+ TRANSFER (m_UInt16);
+ TRANSFER (m_SInt8);
+ TRANSFER (m_UInt8);
+ TRANSFER (m_char);
+ TRANSFER (m_bool);
+ TRANSFER (m_UnityStr);
+}
+
+SUITE (ProxyTransferTests)
+{
+ TEST (TypeTree_GenerateBaseTypes)
+ {
+ // Create test item.
+ TypeTreeTestItem item;
+
+ // Populate test item.
+ item.m_float = 123.0f;
+ item.m_double = 456.0;
+ item.m_int = 2^31-1;
+ item.m_unsignedint = 2^32-1;
+ item.m_SInt64 = 2^63-1;
+ item.m_UInt64 = 2^64-1;
+ item.m_SInt32 = 2^31-1;
+ item.m_UInt32 = 2^32-1;
+ item.m_SInt16 = 2^15-1;
+ item.m_UInt16 = 2^16-1;
+ item.m_SInt8 = 2^7-1;
+ item.m_UInt8 = 2^8-1;
+ item.m_char = '@';
+ item.m_bool = true;
+ item.m_UnityStr = "ProxyTransferTests";
+
+ // Generate type tree.
+ TypeTree tree;
+ ProxyTransfer proxy (tree, 0, &item, sizeof(TypeTreeTestItem));
+ proxy.Transfer( item, "Base" );
+
+ // Validate the following expected output:
+ //
+ // TypeTree: Base Type:TypeTreeTestItem ByteSize:-1 MetaFlag:32768
+ // m_float Type:float ByteSize:4 MetaFlag:0
+ // m_double Type:double ByteSize:8 MetaFlag:0
+ // m_int Type:int ByteSize:4 MetaFlag:0
+ // m_unsignedint Type:unsigned int ByteSize:4 MetaFlag:0
+ // m_SInt64 Type:SInt64 ByteSize:8 MetaFlag:0
+ // m_UInt64 Type:UInt64 ByteSize:8 MetaFlag:0
+ // m_SInt32 Type:SInt32 ByteSize:4 MetaFlag:0
+ // m_UInt32 Type:UInt32 ByteSize:4 MetaFlag:0
+ // m_SInt16 Type:SInt16 ByteSize:2 MetaFlag:0
+ // m_UInt16 Type:UInt16 ByteSize:2 MetaFlag:0
+ // m_SInt8 Type:SInt8 ByteSize:1 MetaFlag:0
+ // m_UInt8 Type:UInt8 ByteSize:1 MetaFlag:0
+ // m_char Type:char ByteSize:1 MetaFlag:0
+ // m_bool Type:bool ByteSize:1 MetaFlag:0
+ // m_UnityStr Type:string ByteSize:-1 MetaFlag:32768
+ // Array Type:Array ByteSize:-1 MetaFlag:16385 IsArray
+ // size Type:SInt32 ByteSize:4 MetaFlag:1
+ // data Type:char ByteSize:1 MetaFlag:1
+
+ // Check correct number of children.
+ const int expectedChildrenCount = 15;
+ CHECK_EQUAL (expectedChildrenCount, tree.m_Children.size());
+
+ // Transfer children for explicit checking.
+ TypeTree childTree[expectedChildrenCount];
+ int childIndex = 0;
+ for( TypeTree::iterator treeItr = tree.begin(); treeItr != tree.end(); ++treeItr )
+ childTree[childIndex++] = *treeItr;
+
+ // Check children types...
+
+ TypeTree* child = childTree;
+ CHECK_EQUAL (SerializeTraits<float>::GetTypeString (), child->m_Type);
+ CHECK_EQUAL ("m_float", child->m_Name);
+ CHECK_EQUAL (4, child->m_ByteSize);
+ CHECK_EQUAL (0, child->m_Children.size());
+
+ ++child;
+ CHECK_EQUAL (SerializeTraits<double>::GetTypeString (), child->m_Type);
+ CHECK_EQUAL ("m_double", child->m_Name);
+ CHECK_EQUAL (8, child->m_ByteSize);
+ CHECK_EQUAL (0, child->m_Children.size());
+
+ ++child;
+ CHECK_EQUAL (SerializeTraits<SInt32>::GetTypeString (), child->m_Type);
+ CHECK_EQUAL ("m_int", child->m_Name);
+ CHECK_EQUAL (4, child->m_ByteSize);
+ CHECK_EQUAL (0, child->m_Children.size());
+
+ ++child;
+ CHECK_EQUAL (SerializeTraits<UInt32>::GetTypeString (), child->m_Type);
+ CHECK_EQUAL ("m_unsignedint", child->m_Name);
+ CHECK_EQUAL (4, child->m_ByteSize);
+ CHECK_EQUAL (0, child->m_Children.size());
+
+ ++child;
+ CHECK_EQUAL (SerializeTraits<SInt64>::GetTypeString (), child->m_Type);
+ CHECK_EQUAL ("m_SInt64", child->m_Name);
+ CHECK_EQUAL (8, child->m_ByteSize);
+ CHECK_EQUAL (0, child->m_Children.size());
+
+ ++child;
+ CHECK_EQUAL (SerializeTraits<UInt64>::GetTypeString (), child->m_Type);
+ CHECK_EQUAL ("m_UInt64", child->m_Name);
+ CHECK_EQUAL (8, child->m_ByteSize);
+ CHECK_EQUAL (0, child->m_Children.size());
+
+ ++child;
+ CHECK_EQUAL (SerializeTraits<SInt32>::GetTypeString (), child->m_Type);
+ CHECK_EQUAL ("m_SInt32", child->m_Name);
+ CHECK_EQUAL (4, child->m_ByteSize);
+ CHECK_EQUAL (0, child->m_Children.size());
+
+ ++child;
+ CHECK_EQUAL (SerializeTraits<UInt32>::GetTypeString(), child->m_Type);
+ CHECK_EQUAL ("m_UInt32", child->m_Name);
+ CHECK_EQUAL (4, child->m_ByteSize);
+ CHECK_EQUAL (0, child->m_Children.size());
+
+ ++child;
+ CHECK_EQUAL (SerializeTraits<SInt16>::GetTypeString (), child->m_Type);
+ CHECK_EQUAL ("m_SInt16", child->m_Name);
+ CHECK_EQUAL (2, child->m_ByteSize);
+ CHECK_EQUAL (0, child->m_Children.size());
+
+ ++child;
+ CHECK_EQUAL (SerializeTraits<UInt16>::GetTypeString (), child->m_Type);
+ CHECK_EQUAL ("m_UInt16", child->m_Name);
+ CHECK_EQUAL (2, child->m_ByteSize);
+ CHECK_EQUAL (0, child->m_Children.size());
+
+ ++child;
+ CHECK_EQUAL (SerializeTraits<SInt8>::GetTypeString (), child->m_Type);
+ CHECK_EQUAL ("m_SInt8", child->m_Name);
+ CHECK_EQUAL (1, child->m_ByteSize);
+ CHECK_EQUAL (0, child->m_Children.size());
+
+ ++child;
+ CHECK_EQUAL (SerializeTraits<UInt8>::GetTypeString (), child->m_Type);
+ CHECK_EQUAL ("m_UInt8", child->m_Name);
+ CHECK_EQUAL (1, child->m_ByteSize);
+ CHECK_EQUAL (0, child->m_Children.size());
+
+ ++child;
+ CHECK_EQUAL (SerializeTraits<char>::GetTypeString (), child->m_Type);
+ CHECK_EQUAL ("m_char", child->m_Name);
+ CHECK_EQUAL (1, child->m_ByteSize);
+ CHECK_EQUAL (0, child->m_Children.size());
+
+ ++child;
+ CHECK_EQUAL ("m_bool", child->m_Name);
+ CHECK_EQUAL (SerializeTraits<bool>::GetTypeString (), child->m_Type);
+ CHECK_EQUAL (1, child->m_ByteSize);
+ CHECK_EQUAL (0, child->m_Children.size());
+
+ ++child;
+ CHECK_EQUAL ("m_UnityStr", child->m_Name); // String.
+ CHECK_EQUAL (SerializeTraits<UnityStr>::GetTypeString (), child->m_Type);
+ CHECK_EQUAL (-1, child->m_ByteSize);
+ CHECK_EQUAL (1, child->m_Children.size());
+ CHECK_EQUAL ("Array", (*child->begin()).m_Name); // String is an array.
+ CHECK_EQUAL ("Array", (*child->begin()).m_Type);
+ CHECK_EQUAL (-1, (*child->begin()).m_ByteSize);
+ CHECK ( (*child->begin()).m_IsArray != 0);
+ CHECK_EQUAL ("size", (*(*child->begin()).begin()).m_Name); // Array size.
+ CHECK_EQUAL (SerializeTraits<SInt32>::GetTypeString (), (*(*child->begin()).begin()).m_Type);
+ CHECK_EQUAL (4, (*(*child->begin()).begin()).m_ByteSize);
+ CHECK_EQUAL ("data", (*++(*child->begin()).begin()).m_Name); // Array data.
+ CHECK_EQUAL (SerializeTraits<char>::GetTypeString (), (*++(*child->begin()).begin()).m_Type);
+ CHECK_EQUAL (1, (*++(*child->begin()).begin()).m_ByteSize);
+ }
+}
+
+#endif //ENABLE_UNIT_TESTS
+
diff --git a/Runtime/Serialize/TransferFunctions/RemapPPtrTransfer.cpp b/Runtime/Serialize/TransferFunctions/RemapPPtrTransfer.cpp
new file mode 100644
index 0000000..62c5a36
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/RemapPPtrTransfer.cpp
@@ -0,0 +1,31 @@
+#include "UnityPrefix.h"
+#include "RemapPPtrTransfer.h"
+
+RemapPPtrTransfer::RemapPPtrTransfer (int flags, bool readPPtrs)
+{
+ m_ReadPPtrs = readPPtrs;
+ m_Flags = flags;
+ m_UserData = NULL;
+ m_GenerateIDFunctor = NULL;
+ m_MetaMaskStack.reserve(4);
+ m_MetaMaskStack.push_back (kNoTransferFlags);
+ m_CachedMetaMaskStackTop = kNoTransferFlags;
+}
+
+void RemapPPtrTransfer::PushMetaFlag (TransferMetaFlags flag)
+{
+ m_MetaMaskStack.push_back (m_MetaMaskStack.back() | flag);
+ m_CachedMetaMaskStackTop = m_MetaMaskStack.back ();
+}
+
+void RemapPPtrTransfer::PopMetaFlag ()
+{
+ m_MetaMaskStack.pop_back();
+ m_CachedMetaMaskStackTop = m_MetaMaskStack.back ();
+}
+
+void RemapPPtrTransfer::AddMetaFlag (TransferMetaFlags flag)
+{
+ m_MetaMaskStack.back () |= flag;
+ m_CachedMetaMaskStackTop = m_MetaMaskStack.back ();
+} \ No newline at end of file
diff --git a/Runtime/Serialize/TransferFunctions/RemapPPtrTransfer.h b/Runtime/Serialize/TransferFunctions/RemapPPtrTransfer.h
new file mode 100644
index 0000000..853d83b
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/RemapPPtrTransfer.h
@@ -0,0 +1,178 @@
+#ifndef REMAPPPTRTRANSFER_H
+#define REMAPPPTRTRANSFER_H
+
+#include "Runtime/Serialize/TransferFunctions/TransferBase.h"
+#include "Runtime/Serialize/SerializationMetaFlags.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+
+#include <stack>
+
+template<class T>
+class PPtr;
+template<class T>
+class ImmediatePtr;
+struct StreamingInfo;
+
+class GenerateIDFunctor
+{
+ public:
+
+ /// Calls GenerateInstanceID for every PPtr that is found while transferring.
+ /// oldInstanceID is the instanceID of the PPtr. metaFlag is the ored metaFlag of the the Transfer trace to the currently transferred pptr.
+ /// After GenerateInstanceID returns, the PPtr will be set to the returned instanceID
+ /// If you dont want to change the PPtr return oldInstanceID
+ virtual SInt32 GenerateInstanceID (SInt32 oldInstanceID, TransferMetaFlags metaFlag) = 0;
+};
+
+
+////@todo: Think about a smart way to calculate to let the compiler optimize transfer code of non-pptr classes away
+//// currently we have a serializeTraits::MightContainPPtr but this should be somehow automatic!
+
+
+/// Transfer that scans for PPtrs and optionally allows to replace them in-place. Is given a GenerateIDFunctor which maps one
+/// or more existing instance IDs to new instance IDs and then crawls through an object's transfers looking for PPtr transfers.
+/// Does not touch any other data.
+class EXPORT_COREMODULE RemapPPtrTransfer : public TransferBase
+{
+private:
+
+ GenerateIDFunctor* m_GenerateIDFunctor;
+ UNITY_TEMP_VECTOR(TransferMetaFlags) m_MetaMaskStack;
+ TransferMetaFlags m_CachedMetaMaskStackTop;
+ bool m_ReadPPtrs;
+
+public:
+
+ RemapPPtrTransfer (int flags, bool readPPtrs);
+
+ void SetGenerateIDFunctor (GenerateIDFunctor* functor) { m_GenerateIDFunctor = functor; }
+ GenerateIDFunctor* GetGenerateIDFunctor () const { return m_GenerateIDFunctor; }
+
+ bool IsReadingPPtr () { return m_ReadPPtrs; }
+ bool IsWritingPPtr () { return true; }
+ bool IsRemapPPtrTransfer () { return true; }
+
+ bool DidReadLastPPtrProperty () { return true; }
+
+ void AddMetaFlag (TransferMetaFlags flag);
+
+ void PushMetaFlag (TransferMetaFlags flag);
+ void PopMetaFlag ();
+
+ template<class T>
+ void Transfer (T& data, const char* name, TransferMetaFlags metaFlag = kNoTransferFlags);
+ template<class T>
+ void TransferWithTypeString (T& data, const char*, const char*, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ void TransferTypeless (unsigned*, const char*, TransferMetaFlags /*metaFlag*/ = kNoTransferFlags) { }
+
+ void TransferTypelessData (unsigned, void*, TransferMetaFlags /*metaFlag*/ = kNoTransferFlags) { }
+
+ template<class T>
+ void TransferSTLStyleArray (T& data, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ template<class T>
+ void TransferSTLStyleMap (T& data, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ SInt32 GetNewInstanceIDforOldInstanceID (SInt32 oldInstanceID) { return m_GenerateIDFunctor->GenerateInstanceID (oldInstanceID, m_CachedMetaMaskStackTop);}
+};
+
+template<class T>
+struct RemapPPtrTraits
+{
+ static bool IsPPtr () { return false; }
+ static int GetInstanceIDFromPPtr (const T& ) { AssertString ("Never"); return 0; }
+ static void SetInstanceIDOfPPtr (T& , SInt32) { AssertString ("Never"); }
+};
+
+template<class T>
+struct RemapPPtrTraits<PPtr<T> >
+{
+ static bool IsPPtr () { return true; }
+ static int GetInstanceIDFromPPtr (const PPtr<T>& data) { return data.GetInstanceID (); }
+ static void SetInstanceIDOfPPtr (PPtr<T>& data, SInt32 instanceID) { data.SetInstanceID (instanceID); }
+};
+
+template<class T>
+struct RemapPPtrTraits<ImmediatePtr<T> >
+{
+ static bool IsPPtr () { return true; }
+ static int GetInstanceIDFromPPtr (const ImmediatePtr<T>& data) { return data.GetInstanceID (); }
+ static void SetInstanceIDOfPPtr (ImmediatePtr<T>& data, SInt32 instanceID) { data.SetInstanceID (instanceID); }
+};
+
+template<class T>
+void RemapPPtrTransfer::Transfer (T& data, const char*, TransferMetaFlags metaFlag)
+{
+ if (SerializeTraits<T>::MightContainPPtr ())
+ {
+ if (metaFlag != 0)
+ PushMetaFlag(metaFlag);
+
+ if (RemapPPtrTraits<T>::IsPPtr ())
+ {
+ AssertIf (m_GenerateIDFunctor == NULL);
+ ANALYSIS_ASSUME (m_GenerateIDFunctor);
+ SInt32 oldInstanceID = RemapPPtrTraits<T>::GetInstanceIDFromPPtr (data);
+ SInt32 newInstanceID = GetNewInstanceIDforOldInstanceID(oldInstanceID);
+
+ if (m_ReadPPtrs)
+ {
+ RemapPPtrTraits<T>::SetInstanceIDOfPPtr (data, newInstanceID);
+ }
+ else
+ {
+ AssertIf(oldInstanceID != newInstanceID);
+ }
+ }
+ else
+ SerializeTraits<T>::Transfer (data, *this);
+
+ if (metaFlag != 0)
+ PopMetaFlag();
+ }
+}
+
+template<class T>
+void RemapPPtrTransfer::TransferWithTypeString (T& data, const char*, const char*, TransferMetaFlags metaFlag)
+{
+ Transfer(data, NULL, metaFlag);
+}
+
+
+template<class T>
+void RemapPPtrTransfer::TransferSTLStyleArray (T& data, TransferMetaFlags metaFlag)
+{
+ if (SerializeTraits<typename T::value_type>::MightContainPPtr ())
+ {
+ typename T::iterator i = data.begin ();
+ typename T::iterator end = data.end ();
+ while (i != end)
+ {
+ Transfer (*i, "data", metaFlag);
+ ++i;
+ }
+ }
+}
+
+template<class T>
+void RemapPPtrTransfer::TransferSTLStyleMap (T& data, TransferMetaFlags metaFlag)
+{
+ typedef typename NonConstContainerValueType<T>::value_type NonConstT;
+ if (SerializeTraits<NonConstT>::MightContainPPtr ())
+ {
+ typename T::iterator i = data.begin ();
+ typename T::iterator end = data.end ();
+
+ // maps value_type is: pair<const First, Second>
+ // So we have to write to maps non-const value type
+ while (i != end)
+ {
+ NonConstT& p = (NonConstT&) (*i);
+ Transfer (p, "data", metaFlag);
+ i++;
+ }
+ }
+}
+
+#endif
diff --git a/Runtime/Serialize/TransferFunctions/RemapPPtrTransferTests.cpp b/Runtime/Serialize/TransferFunctions/RemapPPtrTransferTests.cpp
new file mode 100644
index 0000000..ea4710b
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/RemapPPtrTransferTests.cpp
@@ -0,0 +1,57 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "Runtime/Testing/Testing.h"
+#include "Runtime/Testing/TestFixtures.h"
+
+SUITE (RemapPPtrTransferTests)
+{
+ //-------------------------------------------------------------------------
+
+ DEFINE_TRANSFER_TEST_FIXTURE (DoesNotTouchNonPPtrProperty)
+ {
+ UnityStr m_NonPPtrProperty = "test";
+ TRANSFER (m_NonPPtrProperty);
+ CHECK (m_NonPPtrProperty == "test");
+ }
+
+ TEST_FIXTURE (DoesNotTouchNonPPtrPropertyTestFixture, Transfer_WithNonPPtrProperty_DoesNotChangeProperty)
+ {
+ DoRemapPPtrTransfer ();
+ }
+
+ //-------------------------------------------------------------------------
+
+ DEFINE_TRANSFER_TEST_FIXTURE (RemapsPPtrProperty)
+ {
+ PPtr<Object> m_PPtrProperty (1234);
+ TRANSFER (m_PPtrProperty);
+ CHECK (m_PPtrProperty.GetInstanceID () == 4321);
+ }
+
+ TEST_FIXTURE (RemapsPPtrPropertyTestFixture, Transfer_WithPPtrProperty_MapsToNewInstanceID)
+ {
+ AddPPtrRemap (1234, 4321);
+ DoRemapPPtrTransfer ();
+ }
+
+ //-------------------------------------------------------------------------
+
+ DEFINE_TRANSFER_TEST_FIXTURE (DidReadLastPPtrProperty)
+ {
+ PPtr<Object> m_PPtrProperty;
+ TRANSFER (m_PPtrProperty);
+ CHECK (transfer.DidReadLastPPtrProperty ());
+ }
+
+ TEST_FIXTURE (DidReadLastPPtrPropertyTestFixture, DidReadLastPPtrProperty_WithPPtrProperty_IsTrue)
+ {
+ DoRemapPPtrTransfer ();
+ }
+
+} //TEST
+
+#endif // ENABLE_UNIT_TESTS
+
diff --git a/Runtime/Serialize/TransferFunctions/SafeBinaryRead.cpp b/Runtime/Serialize/TransferFunctions/SafeBinaryRead.cpp
new file mode 100644
index 0000000..0fe91e8
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/SafeBinaryRead.cpp
@@ -0,0 +1,487 @@
+#include "UnityPrefix.h"
+#include "SafeBinaryRead.h"
+#include "TransferNameConversions.h"
+#include "Runtime/Utilities/dynamic_bitset.h"
+#include "Runtime/Utilities/algorithm_utility.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+
+#define LOG_MISSING_VARIBALES 0
+
+#if SUPPORT_SERIALIZED_TYPETREES
+
+using namespace std;
+
+typedef map<pair<char*, char*>, ConversionFunction*, smaller_cstring_pair> ConverterFunctions;
+static ConverterFunctions* gConverterFunctions;
+
+namespace SafeBinaryReadManager
+{
+ void StaticInitialize()
+ {
+ gConverterFunctions = UNITY_NEW(ConverterFunctions,kMemSerialization);
+ }
+ void StaticDestroy()
+ {
+ UNITY_DELETE(gConverterFunctions,kMemSerialization);
+ }
+}
+static RegisterRuntimeInitializeAndCleanup s_SafeBinaryReadManagerCallbacks(SafeBinaryReadManager::StaticInitialize, SafeBinaryReadManager::StaticDestroy);
+
+ConversionFunction* FindConverter (const char* oldType, const char* newTypeName)
+{
+ pair<char*, char*> arg = make_pair(const_cast<char*> (oldType), const_cast<char*> (newTypeName));
+
+ ConverterFunctions::iterator found = gConverterFunctions->find (arg);
+ if (found == gConverterFunctions->end())
+ return NULL;
+
+ return found->second;
+}
+
+void SafeBinaryRead::RegisterConverter (const char* oldType, const char* newType, ConversionFunction* converter)
+{
+
+ pair<char*, char*> arg = make_pair(const_cast<char*> (oldType), const_cast<char*> (newType));
+ AssertMsg (!gConverterFunctions->count (arg), "Duplicate conversion registered");
+ (*gConverterFunctions)[arg] = converter;
+}
+
+void SafeBinaryRead::CleanupConverterTable ()
+{
+ (*gConverterFunctions).clear();
+}
+
+static void Walk (const TypeTree& typeTree, CachedReader& cache, SInt32* bytePosition, bool endianSwap);
+
+CachedReader& SafeBinaryRead::Init (const TypeTree& oldBase, int bytePosition, int byteSize, int flags)
+{
+ AssertIf (!m_StackInfo.empty ());
+ m_OldBaseType = &oldBase;
+ m_BaseBytePosition = bytePosition;
+ AssertIf (m_BaseBytePosition < 0);
+ m_BaseByteSize = byteSize;
+ m_Flags = flags;
+ m_UserData = NULL;
+ m_DidReadLastProperty = false;
+ #if UNITY_EDITOR
+ m_TypeTreeHasChanged = false;
+ #endif
+ return m_Cache;
+}
+
+CachedReader& SafeBinaryRead::Init (SafeBinaryRead& transfer)
+{
+ int newBasePosition = transfer.m_StackInfo.top ().bytePosition;
+ int size = transfer.m_BaseByteSize - (newBasePosition - transfer.m_BaseBytePosition);
+ Init (*transfer.m_StackInfo.top ().type, newBasePosition, size, transfer.m_Flags);
+ m_Cache.InitRead (*transfer.m_Cache.GetCacher(), transfer.m_StackInfo.top ().bytePosition, size);
+ m_UserData = NULL;
+ m_DidReadLastProperty = false;
+ #if UNITY_EDITOR
+ m_TypeTreeHasChanged = false;
+ #endif
+
+ return m_Cache;
+}
+
+SafeBinaryRead::~SafeBinaryRead ()
+{
+ AssertIf (!m_StackInfo.empty ());
+ AssertIf (!m_PositionInArray.empty ());
+}
+
+static void Walk (const TypeTree& typeTree, CachedReader& cache, SInt32* bytePosition, bool endianSwap)
+{
+ AssertIf (bytePosition == NULL);
+
+ AssertIf((typeTree.m_ByteSize != -1 && ((typeTree.m_MetaFlag & kAnyChildUsesAlignBytesFlag) == 0 || typeTree.m_Children.empty())) != (typeTree.m_ByteSize != -1 && (typeTree.m_MetaFlag & kAnyChildUsesAlignBytesFlag) == 0));
+
+ if (typeTree.m_ByteSize != -1 && (typeTree.m_MetaFlag & kAnyChildUsesAlignBytesFlag) == 0)
+ {
+ *bytePosition += typeTree.m_ByteSize;
+ }
+ else if (typeTree.m_IsArray)
+ {
+ // First child in an array is the size
+ // Second child is the homogenous type of the array
+ AssertIf (typeTree.m_Children.front ().m_Type != SerializeTraits<SInt32>::GetTypeString (NULL));
+ AssertIf (typeTree.m_Children.front ().m_Name != "size");
+ AssertIf (typeTree.m_Children.size () != 2);
+
+ SInt32 arraySize, i;
+ cache.Read (arraySize, *bytePosition);
+ if (endianSwap)
+ SwapEndianBytes(arraySize);
+
+ *bytePosition += sizeof (arraySize);
+
+ const TypeTree& elementTypeTree = typeTree.m_Children.back ();
+
+ // If the bytesize is known we can simply skip the recursive loop
+ if (elementTypeTree.m_ByteSize != -1 && (elementTypeTree.m_MetaFlag & (kAnyChildUsesAlignBytesFlag | kAlignBytesFlag)) == 0)
+ *bytePosition += arraySize * elementTypeTree.m_ByteSize;
+ // Otherwise recursively Walk element typetree
+ else
+ {
+ for (i=0;i<arraySize;i++)
+ Walk (typeTree.m_Children.back (), cache, bytePosition, endianSwap);
+ }
+ }
+ else
+ {
+ TypeTree::TypeTreeList::const_iterator i;
+ for (i = typeTree.m_Children.begin (); i != typeTree.m_Children.end ();++i)
+ Walk (*i, cache, bytePosition, endianSwap);
+ }
+
+ if (typeTree.m_MetaFlag & kAlignBytesFlag)
+ {
+ #if UNITY_EDITOR
+// const TypeTree* root = &typeTree;
+// while (root->m_Father != NULL)
+// root = root->m_Father;
+// if (root->m_Type == "MonoBehaviour")
+// ErrorString("Alignment in monobehaviour???");
+ #endif
+ *bytePosition = Align4(*bytePosition);
+ }
+}
+
+// Walk through typetree and data to find the bytePosition
+void SafeBinaryRead::Walk (const TypeTree& typeTree, SInt32* bytePosition)
+{
+ ::Walk (typeTree, m_Cache, bytePosition, ConvertEndianess());
+}
+
+void SafeBinaryRead::OverrideRootTypeName (const char* typeString)
+{
+ Assert(m_StackInfo.size() == 1);
+ m_StackInfo.top().currentTypeName = typeString;
+ #if !UNITY_RELEASE
+ m_StackInfo.top().currentTypeNameCheck = typeString;
+ #endif
+}
+
+
+int SafeBinaryRead::BeginTransfer (const char* name, const char* typeString, ConversionFunction** converter)
+{
+ if (converter != NULL)
+ *converter = NULL;
+
+ m_DidReadLastProperty = false;
+
+ // For the first Transfer only setup the stack to the base parameters
+ if (m_StackInfo.empty ())
+ {
+ ErrorIf (name != m_OldBaseType->m_Name);
+
+ StackedInfo newInfo;
+ newInfo.type = m_OldBaseType;
+ newInfo.bytePosition = m_BaseBytePosition;
+ newInfo.version = 1;
+ #if UNITY_EDITOR
+ newInfo.lookupCount = 0;
+ #endif
+ newInfo.currentTypeName = typeString;
+ #if !UNITY_RELEASE
+ newInfo.currentTypeNameCheck = typeString;
+ #endif
+ newInfo.cachedIterator = newInfo.type->begin();
+ newInfo.cachedBytePosition = m_BaseBytePosition;
+ m_StackInfo.push(newInfo);
+ m_CurrentStackInfo = &m_StackInfo.top();
+
+ return kMatchesType;
+ }
+
+ TypeTree::TypeTreeList::const_iterator c;
+
+ StackedInfo& info = *m_CurrentStackInfo;
+
+ const TypeTree::TypeTreeList& children = info.type->m_Children;
+
+ // Start searching at the cached position
+ SInt32 newBytePosition = info.cachedBytePosition;
+ int count = 0;
+ for (c=info.cachedIterator;c != children.end ();++c)
+ {
+ if (c->m_Name == name)
+ break;
+
+ // Walk through old typetree, updating position
+ Walk (*c, &newBytePosition);
+ count++;
+ }
+
+ #if UNITY_EDITOR
+ if (count > 1)
+ m_TypeTreeHasChanged = true;
+ #endif
+
+ // Didn't find it, try again starting at the first child
+ if (c == children.end ())
+ {
+ #if UNITY_EDITOR
+ m_TypeTreeHasChanged = true;
+ #endif
+
+ // Find name conversion lookup for this type
+ #if !UNITY_RELEASE
+ DebugAssertIf(info.currentTypeName != info.currentTypeNameCheck);
+ #endif
+ const AllowNameConversion::mapped_type* nameConversion = GetAllowedNameConversions(info.currentTypeName, name);
+
+ newBytePosition = info.bytePosition;
+ for (c=children.begin();c != children.end();++c)
+ {
+ if (c->m_Name == name)
+ break;
+ if (nameConversion && nameConversion->count(const_cast<char*>(c->m_Name.c_str())))
+ break;
+
+ // Walk through old typetree, updating position
+ Walk (*c, &newBytePosition);
+ }
+
+ // No child with name was found?
+ if (c == children.end ())
+ {
+ #if LOG_MISSING_VARIBALES
+ string s ("Variable not found in old file ");
+ GetTypePath (m_StackInfo.top ().type, s);
+ s = s + " new name and type: " + name;
+ s = s + '(' + typeString + ")\n";
+ m_OldBaseType->DebugPrint (s);
+ LogString (s);
+ #endif
+
+ return kNotFound;
+ }
+ }
+
+ #if UNITY_EDITOR
+ m_CurrentStackInfo->lookupCount++;
+ #endif
+
+ info.cachedIterator = c;
+ info.cachedBytePosition = newBytePosition;
+
+ /*Unoptimized version:
+
+ // Find name in children typeTree, updating position
+ SInt32 newBytePosition = info.bytePosition;
+
+ // Find name conversion lookup for this type
+ const AllowNameConversion::mapped_type* nameConversion = NULL;
+ DebugAssertIf(info.currentTypeName != info.currentTypeNameCheck);
+ AllowNameConversion::iterator foundNameConversion = gAllowNameConversion.find(make_pair(const_cast<char*>(info.currentTypeName), const_cast<char*>(name)));
+ if (foundNameConversion == gAllowNameConversion.end())
+ nameConversion = &foundNameConversion->second;
+
+ for (c=children.begin ();c != children.end ();++c)
+ {
+ if (c->m_Name == name)
+ break;
+ if (nameConversion && nameConversion->count(const_cast<char*>(c->m_Name.c_str())))
+ break;
+
+ // Walk through old typetree, updating position
+ Walk (*c, &newBytePosition);
+ }
+
+ // No child with name was found?
+ if (c == children.end ())
+ {
+ #if LOG_MISSING_VARIBALES
+ string s ("Variable not found in old file ");
+ GetTypePath (m_OldType.top (), s);
+ s = s + " new name and type: " + name;
+ s = s + '(' + typeString + ")\n";
+ m_OldBaseType->DebugPrint (s);
+ AssertStringQuiet (s);
+ #endif
+
+ return kNotFound;
+ }
+ */
+
+ // Walk trough the already iterated array elements
+ if (info.type->m_IsArray && c != children.begin ())
+ {
+ SInt32 arrayPosition = *m_CurrentPositionInArray;
+
+ // There are no arrays in the subtree so
+ // we can simply use the cached bytesize
+ // Alignment cuts across this so use the slow path in that case
+ if (c->m_ByteSize != -1 && (c->m_MetaFlag & (kAnyChildUsesAlignBytesFlag | kAlignBytesFlag)) == 0)
+ {
+ newBytePosition += c->m_ByteSize * arrayPosition;
+ }
+ // Walk through old typetree, updating position
+ else
+ {
+ ArrayPositionInfo& arrayInfo = m_PositionInArray.top();
+ SInt32 cachedArrayPosition = 0;
+ if (arrayInfo.cachedArrayPosition <= arrayPosition)
+ {
+ newBytePosition = arrayInfo.cachedBytePosition;
+ cachedArrayPosition = arrayInfo.cachedArrayPosition;
+ }
+
+ for (SInt32 i = cachedArrayPosition;i < arrayPosition;i++)
+ Walk (*c, &newBytePosition);
+
+ arrayInfo.cachedArrayPosition = arrayPosition;
+ arrayInfo.cachedBytePosition = newBytePosition;
+ }
+
+ (*m_CurrentPositionInArray)++;
+ }
+
+ StackedInfo newInfo;
+ newInfo.type = &*c;
+ newInfo.bytePosition = newBytePosition;
+ newInfo.version = 1;
+ #if UNITY_EDITOR
+ newInfo.lookupCount = 0;
+ #endif
+ newInfo.cachedIterator = newInfo.type->begin();
+ newInfo.cachedBytePosition = newBytePosition;
+ newInfo.currentTypeName = typeString;
+ #if !UNITY_RELEASE
+ newInfo.currentTypeNameCheck = typeString;
+ #endif
+
+ m_StackInfo.push(newInfo);
+ m_CurrentStackInfo = &m_StackInfo.top();
+
+ int conversion = kNeedConversion;
+ // Does the type match (compare type string)
+ // The root type should get a transfer in any case because the type might change
+ // Eg. TransformComponent renamed to Transform (Typename mismatch but we still want to serialize)
+ if (c->m_Type == typeString || m_StackInfo.size () == 1)
+ {
+ conversion = kMatchesType;
+ if (c->m_ByteSize != -1 && (c->m_MetaFlag & (kAnyChildUsesAlignBytesFlag | kAlignBytesFlag)) == 0)
+ conversion = kFastPathMatchesType;
+ }
+ else if (AllowTypeNameConversion (c->m_Type, typeString))
+ {
+ conversion = kMatchesType;
+ if (c->m_ByteSize != -1 && (c->m_MetaFlag & (kAnyChildUsesAlignBytesFlag | kAlignBytesFlag)) == 0)
+ conversion = kFastPathMatchesType;
+ #if UNITY_EDITOR
+ m_TypeTreeHasChanged = true;
+ #endif
+ }
+ else
+ {
+ #if UNITY_EDITOR
+ m_TypeTreeHasChanged = true;
+ #endif
+ }
+
+ if (conversion == kNeedConversion && converter != NULL)
+ *converter = FindConverter(c->m_Type.c_str(), typeString);
+
+ return conversion;
+}
+
+void SafeBinaryRead::SetVersion (int version)
+{
+ m_CurrentStackInfo->version = version;
+}
+
+
+void SafeBinaryRead::EndTransfer ()
+{
+ #if UNITY_EDITOR
+ if (m_CurrentStackInfo && m_CurrentStackInfo->lookupCount != m_CurrentStackInfo->type->m_Children.size())
+ {
+ m_TypeTreeHasChanged = true;
+ }
+ #endif
+
+ m_StackInfo.pop();
+ if (!m_StackInfo.empty())
+ {
+ m_CurrentStackInfo = &m_StackInfo.top();
+ }
+ else
+ m_CurrentStackInfo = NULL;
+
+ m_DidReadLastProperty = true;
+}
+
+bool SafeBinaryRead::BeginArrayTransfer (const char* name, const char* typeString, SInt32& size)
+{
+ if (BeginTransfer (name, typeString, NULL) == kNotFound)
+ return false;
+
+ Transfer (size, "size");
+ ArrayPositionInfo info;
+ info.arrayPosition = 0;
+ info.cachedBytePosition = -1;
+ info.cachedArrayPosition = std::numeric_limits<SInt32>::max();
+ m_PositionInArray.push (info);
+ m_CurrentPositionInArray = &m_PositionInArray.top().arrayPosition;
+
+ Assert (GetActiveOldTypeTree ().m_Children.front ().m_Name == "size");
+
+ return true;
+}
+
+void SafeBinaryRead::EndArrayTransfer ()
+{
+ m_PositionInArray.pop ();
+ if (!m_PositionInArray.empty())
+ m_CurrentPositionInArray = &m_PositionInArray.top().arrayPosition;
+ else
+ m_CurrentPositionInArray = NULL;
+
+ #if UNITY_EDITOR
+ m_TypeTreeHasChanged = true;
+ #endif
+
+ EndTransfer ();
+}
+
+bool SafeBinaryRead::IsCurrentVersion ()
+{
+ return m_CurrentStackInfo->version == m_CurrentStackInfo->type->m_Version;
+}
+
+bool SafeBinaryRead::IsOldVersion (int version)
+{
+ return m_CurrentStackInfo->type->m_Version == version;
+}
+
+bool SafeBinaryRead::IsVersionSmallerOrEqual (int version)
+{
+ return m_CurrentStackInfo->type->m_Version <= version;
+}
+
+void SafeBinaryRead::TransferTypeless (unsigned* byteSize, const char* name, TransferMetaFlags metaflag)
+{
+ SInt32 size;
+ if (!BeginArrayTransfer (name, "TypelessData", size))
+ {
+ *byteSize = 0;
+ return;
+ }
+ // We can only transfer the array if the size was transferred as well
+ AssertIf (GetActiveOldTypeTree ().m_Children.front ().m_Name != "size");
+
+ *byteSize = size;
+
+ EndArrayTransfer ();
+}
+
+void SafeBinaryRead::TransferTypelessData (unsigned byteSize, void* copyData, int metaData)
+{
+ if (copyData == NULL || byteSize == 0) return;
+
+ m_Cache.Read (copyData, byteSize);
+}
+
+#endif // SUPPORT_SERIALIZED_TYPETREES
diff --git a/Runtime/Serialize/TransferFunctions/SafeBinaryRead.h b/Runtime/Serialize/TransferFunctions/SafeBinaryRead.h
new file mode 100644
index 0000000..e6cd5a3
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/SafeBinaryRead.h
@@ -0,0 +1,290 @@
+#ifndef SAFEBINARYREAD_H
+#define SAFEBINARYREAD_H
+
+#include "Configuration/UnityConfigure.h"
+
+#if SUPPORT_SERIALIZED_TYPETREES
+
+#include "Runtime/Serialize/TransferFunctions/TransferBase.h"
+#include "Runtime/Serialize/CacheWrap.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+
+#include <stack>
+class dynamic_bitset;
+class SafeBinaryRead;
+
+#define LOG_CONVERTING_VARIBALES 0
+#define LOG_MISSING_VARIBALES 0
+
+typedef bool ConversionFunction (void* inData, SafeBinaryRead& transfer);
+
+class EXPORT_COREMODULE SafeBinaryRead : public TransferBase
+{
+ CachedReader m_Cache;
+ SInt32 m_BaseBytePosition;
+ SInt32 m_BaseByteSize;
+
+ const TypeTree* m_OldBaseType;
+ #if UNITY_EDITOR
+ bool m_TypeTreeHasChanged;
+ #endif
+
+ enum { kNotFound = 0, kMatchesType = 1, kFastPathMatchesType = 2, kNeedConversion = -1 };
+
+ struct StackedInfo
+ {
+ const TypeTree* type; /// The type tree of the old type we are reading data from
+ const char* currentTypeName; /// The name of the type we are currently reading (This is the new type name and not from the stored data)
+ int bytePosition;/// byte position of that element
+ int version; /// current version (This is the new version and not from the stored data)
+
+ int cachedBytePosition; /// The cached byte position of the last visited child
+ TypeTree::const_iterator cachedIterator; /// The cached iterator of the last visited child
+ #if UNITY_EDITOR
+ int lookupCount; // counts number of looks, used to determine if the typetree matches
+ #endif
+
+ #if !UNITY_RELEASE
+ std::string currentTypeNameCheck;/// For debugging purposes in case someone changes the typename string while still reading!
+ #endif
+
+ };
+
+ StackedInfo* m_CurrentStackInfo;
+ SInt32* m_CurrentPositionInArray;
+ std::stack<StackedInfo> m_StackInfo;
+
+ struct ArrayPositionInfo
+ {
+ SInt32 arrayPosition;
+ SInt32 cachedBytePosition;
+ SInt32 cachedArrayPosition;
+ };
+
+ std::stack<ArrayPositionInfo> m_PositionInArray;// position in an array
+
+ bool m_DidReadLastProperty;
+
+ friend class MonoBehaviour;
+
+public:
+
+ CachedReader& Init (const TypeTree& oldBase, int bytePosition, int byteSize, int flags);
+ CachedReader& Init (SafeBinaryRead& transfer);
+ ~SafeBinaryRead ();
+
+ void SetVersion (int version);
+ bool IsCurrentVersion ();
+ bool IsOldVersion (int version);
+ bool IsVersionSmallerOrEqual (int version);
+
+ bool IsReading () { return true; }
+ bool IsReadingPPtr () { return true; }
+ bool IsReadingBackwardsCompatible () { return true; }
+ bool NeedsInstanceIDRemapping () { return m_Flags & kNeedsInstanceIDRemapping; }
+ bool ConvertEndianess () { return m_Flags & kSwapEndianess; }
+
+ bool DidReadLastProperty () { return m_DidReadLastProperty; }
+ bool DidReadLastPPtrProperty () { return m_DidReadLastProperty; }
+
+ template<class T>
+ void Transfer (T& data, const char* name, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ template<class T>
+ void TransferWithTypeString (T& data, const char* name, const char* typeName, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ /// In order to transfer typeless data (Read: transfer data real fast)
+ /// Call TransferTypeless. You have to always do this. Even for a proxytransfer. Then when you want to access the datablock.
+ /// Call TransferTypelessData
+ /// On return:
+ /// When reading bytesize will contain the size of the data block that should be read,
+ /// when writing bytesize has to contain the size of the datablock.
+ /// MarkerID will contain an marker which you have to give TransferTypelessData when you want to start the actual transfer.
+ /// optional: A serializedFile will be seperated into two chunks. One is the normal object data. (It is assumed that they are all relatively small)
+ /// So caching them makes a lot of sense. Big datachunks will be stored in another part of the file.
+ /// They will not be cached but usually read directly into the allocated memory, probably reading them asynchronously
+ void TransferTypeless (unsigned* byteSize, const char* name, TransferMetaFlags metaFlag = kNoTransferFlags);
+ // markerID is the id that was given by TransferTypeless.
+ // byteStart is the bytestart relative to the beginning of the typeless data
+ // copyData is a pointer to where the data will be written or read from
+ /// optional: if metaFlag is kTransferBigData the data will be optimized into seperate blocks,
+ void TransferTypelessData (unsigned byteSize, void* copyData, int metaData = 0);
+
+ template<class T>
+ void TransferBasicData (T& data);
+
+ template<class T>
+ void TransferPtr (bool, ReduceCopyData*){}
+
+ template<class T>
+ void TransferSTLStyleArray (T& data, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ template<class T>
+ void TransferSTLStyleMap (T& data, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ bool GetTransferFileInfo(unsigned* position, const char** filePath) const;
+
+ const TypeTree& GetActiveOldTypeTree () { return *m_CurrentStackInfo->type; }
+
+ static void RegisterConverter (const char* oldType, const char* newType, ConversionFunction* converter);
+ static void CleanupConverterTable ();
+
+ #if UNITY_EDITOR
+ /// Returns if the typetree is different from what was loaded in.
+ /// Currently this is incomplete. Arrays will always return true.
+ bool HasDifferentTypeTree ()
+ {
+ return m_TypeTreeHasChanged;
+ }
+ #endif
+
+private:
+
+ // BeginTransfer / EndTransfer
+ int BeginTransfer (const char* name, const char* typeString, ConversionFunction** converter);
+ bool BeginArrayTransfer (const char* name, const char* typeString, SInt32& size);
+
+ // Override the root type name, this is used by scripts that can only determine the class type name after the mono class has actually been loaded
+ void OverrideRootTypeName (const char* typeString);
+
+ void EndTransfer ();
+ void EndArrayTransfer ();
+
+ void Walk (const TypeTree& typeTree, SInt32* bytePosition);
+};
+
+template<class T>inline
+void SafeBinaryRead::TransferBasicData (T& data)
+{
+ m_Cache.Read (data, m_CurrentStackInfo->bytePosition);
+ if (ConvertEndianess())
+ {
+ SwapEndianBytes (data);
+ }
+}
+
+template<class T> inline
+void SafeBinaryRead::TransferSTLStyleArray (T& data, TransferMetaFlags)
+{
+ SInt32 size = data.size ();
+ if (!BeginArrayTransfer ("Array", "Array", size))
+ return;
+
+ SerializeTraits<T>::ResizeSTLStyleArray (data, size);
+
+ typename T::iterator i;
+ typename T::iterator end = data.end ();
+ if (size != 0)
+ {
+ int conversion = BeginTransfer ("data", SerializeTraits<typename T::value_type>::GetTypeString(&*data.begin()), NULL);
+ int elementSize = m_CurrentStackInfo->type->m_ByteSize;
+ *m_CurrentPositionInArray = 0;
+ // If the data types are matching and element size can be determined
+ // then we fast path the whole thing and skip all the duplicate stack walking
+ if (conversion == kFastPathMatchesType)
+ {
+ int basePosition = m_CurrentStackInfo->bytePosition;
+
+ for (i = data.begin ();i != end;++i)
+ {
+ int currentBytePosition = basePosition + (*m_CurrentPositionInArray) * elementSize;
+ m_CurrentStackInfo->cachedBytePosition = currentBytePosition;
+ m_CurrentStackInfo->bytePosition = currentBytePosition;
+ m_CurrentStackInfo->cachedIterator = m_CurrentStackInfo->type->begin();
+ (*m_CurrentPositionInArray)++;
+ SerializeTraits<typename T::value_type>::Transfer (*i, *this);
+ }
+ EndTransfer();
+ }
+ // Fall back to converting variables
+ else
+ {
+ EndTransfer();
+ for (i = data.begin ();i != end;++i)
+ Transfer (*i, "data");
+ }
+ }
+
+ EndArrayTransfer ();
+}
+
+template<class T> inline
+void SafeBinaryRead::TransferSTLStyleMap (T& data, TransferMetaFlags)
+{
+ SInt32 size = data.size ();
+ if (!BeginArrayTransfer ("Array", "Array", size))
+ return;
+
+ // maps value_type is: pair<const First, Second>
+ // So we have to write to maps non-const value type
+ typename NonConstContainerValueType<T>::value_type p;
+
+ data.clear ();
+ for (int i=0;i<size;i++)
+ {
+ Transfer (p, "data");
+ data.insert (p);
+ }
+ EndArrayTransfer ();
+}
+
+template<class T> inline
+void SafeBinaryRead::TransferWithTypeString (T& data, const char* name, const char* typeName, TransferMetaFlags)
+{
+ ConversionFunction* converter;
+ int conversion = BeginTransfer (name, typeName, &converter);
+ if (conversion == kNotFound)
+ return;
+
+ if (conversion >= kMatchesType)
+ SerializeTraits<T>::Transfer (data, *this);
+ // Try conversion
+ else
+ {
+ bool success = false;
+ if (converter != NULL)
+ success = converter (&data, *this);
+
+ #if LOG_CONVERTING_VARIBALES
+ {
+ string s ("Converting variable ");
+ if (success)
+ s += " succeeded ";
+ else
+ s += " failed ";
+
+ GetTypePath (m_OldType.top (), s);
+ s = s + " new type: ";
+ s = s + " new type: (" + SerializeTraits<T>::GetTypeString () + ")\n";
+ m_OldBaseType->DebugPrint (s);
+ AssertStringQuiet (s);
+ }
+ #endif
+ }
+ EndTransfer ();
+}
+
+template<class T> inline
+void SafeBinaryRead::Transfer (T& data, const char* name, TransferMetaFlags)
+{
+ TransferWithTypeString(data, name, SerializeTraits<T>::GetTypeString(&data), kNoTransferFlags);
+}
+
+#else
+
+namespace SafeBinaryReadManager
+{
+ inline void StaticInitialize(){};
+ inline void StaticDestroy(){};
+}
+
+class SafeBinaryRead
+{
+public:
+ static void RegisterAllowTypeNameConversion (const char* oldTypeName, const char* newTypeName) { }
+ static void RegisterAllowNameConversion (const char* type, const char* oldName, const char* newName) { }
+ static void CleanupConverterTable() { }
+};
+
+#endif
+#endif
diff --git a/Runtime/Serialize/TransferFunctions/SerializeTransfer.h b/Runtime/Serialize/TransferFunctions/SerializeTransfer.h
new file mode 100644
index 0000000..f96d7d5
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/SerializeTransfer.h
@@ -0,0 +1,14 @@
+#ifndef SERIALIZETRANSFER_H
+#define SERIALIZETRANSFER_H
+
+#if SUPPORT_TEXT_SERIALIZATION
+#include "YAMLRead.h"
+#include "YAMLWrite.h"
+#endif
+#include "RemapPPtrTransfer.h"
+#include "StreamedBinaryRead.h"
+#include "ProxyTransfer.h"
+#include "SafeBinaryRead.h"
+#include "StreamedBinaryWrite.h"
+
+#endif
diff --git a/Runtime/Serialize/TransferFunctions/StreamedBinaryRead.cpp b/Runtime/Serialize/TransferFunctions/StreamedBinaryRead.cpp
new file mode 100644
index 0000000..4daa8fb
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/StreamedBinaryRead.cpp
@@ -0,0 +1,54 @@
+#include "UnityPrefix.h"
+#include "StreamedBinaryRead.h"
+
+template <bool kSwapEndianess>
+void StreamedBinaryRead<kSwapEndianess>::Align ()
+{
+ m_Cache.Align4Read();
+}
+
+template <bool kSwapEndianess>
+void StreamedBinaryRead<kSwapEndianess>::TransferTypeless (unsigned* byteSize, const char*/* name*/, TransferMetaFlags/* metaFlag*/)
+{
+ SInt32 size;
+ Transfer (size, "size");
+ *byteSize = size;
+}
+
+// markerID is the id that was given by TransferTypeless.
+// optional copyData: is a pointer to where the data will be written or read from
+template <bool kSwapEndianess>
+void StreamedBinaryRead<kSwapEndianess>::TransferTypelessData (unsigned byteSize, void* copyData, int metaData)
+{
+ if (byteSize == 0)
+ return;
+
+ if (copyData == NULL)
+ {
+ // seek byte
+ m_Cache.Skip(byteSize);
+ }
+ else
+ m_Cache.Read (copyData, byteSize);
+ Align();
+}
+
+template <bool kSwapEndianess>
+void StreamedBinaryRead<kSwapEndianess>::ReadDirect (void* data, int byteSize)
+{
+ AssertIf (kSwapEndianess);
+ m_Cache.Read (data, byteSize);
+}
+
+
+template void StreamedBinaryRead<true>::ReadDirect (void* data, int byteSize);
+template void StreamedBinaryRead<false>::ReadDirect (void* data, int byteSize);
+
+template void StreamedBinaryRead<true>::Align();
+template void StreamedBinaryRead<false>::Align();
+
+template void StreamedBinaryRead<true>::TransferTypelessData (unsigned byteSize, void* copyData, int metaData);
+template void StreamedBinaryRead<false>::TransferTypelessData (unsigned byteSize, void* copyData, int metaData);
+
+template void StreamedBinaryRead<true>::TransferTypeless (unsigned* byteSize, const char*/* name*/, TransferMetaFlags/* metaFlag*/);
+template void StreamedBinaryRead<false>::TransferTypeless (unsigned* byteSize, const char*/* name*/, TransferMetaFlags/* metaFlag*/);
diff --git a/Runtime/Serialize/TransferFunctions/StreamedBinaryRead.h b/Runtime/Serialize/TransferFunctions/StreamedBinaryRead.h
new file mode 100644
index 0000000..81a015c
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/StreamedBinaryRead.h
@@ -0,0 +1,174 @@
+#ifndef STREAMEDBINARYREAD_H
+#define STREAMEDBINARYREAD_H
+
+#include "Runtime/Serialize/TransferFunctions/TransferBase.h"
+#include "Runtime/Serialize/CacheWrap.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+
+
+template<bool kSwapEndianess>
+class StreamedBinaryRead : public TransferBase
+{
+ CachedReader m_Cache;
+
+ friend class MonoBehaviour;
+
+public:
+
+ CachedReader& Init (int flags) { m_UserData = NULL; m_Flags = flags; return m_Cache; }
+
+ bool IsReading () { return true; }
+ bool IsReadingPPtr () { return true; }
+ bool NeedsInstanceIDRemapping () { return m_Flags & kNeedsInstanceIDRemapping; }
+ bool ConvertEndianess () { return kSwapEndianess; }
+
+ bool DidReadLastProperty () { return true; }
+ bool DidReadLastPPtrProperty () { return true; }
+
+ void EnableResourceImage (ActiveResourceImage targetResourceImage) { m_Cache.BeginResourceImage(targetResourceImage); }
+ bool ReadStreamingInfo(StreamingInfo* streamingInfo);
+
+ bool ShouldChannelOverride ();
+ CachedReader& GetCachedReader () { return m_Cache; }
+
+ const char* GetSerializedFilePathName() { return m_Cache.GetSerializedFilePathName(); }
+
+ template<class T>
+ void Transfer (T& data, const char* name, TransferMetaFlags metaFlag = kNoTransferFlags);
+ template<class T>
+ void TransferWithTypeString (T& data, const char* name, const char* typeName, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ void TransferTypeless (unsigned* byteSize, const char* name, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ // markerID is the id that was given by TransferTypeless.
+ // optional copyData: is a pointer to where the data will be written or read from
+ void TransferTypelessData (unsigned byteSize, void* copyData, int metaData = 0);
+
+ /// Reads byteSize bytes into data. This may onle be used if UseOptimizedReading returns true.
+ void EXPORT_COREMODULE ReadDirect (void* data, int byteSize);
+
+ void EXPORT_COREMODULE Align ();
+
+ template<class T>
+ void TransferBasicData (T& data);
+
+ template<class T>
+ void TransferPtr (bool, ReduceCopyData*){}
+
+ template<class T>
+ void TransferSTLStyleArray (T& data, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ template<class T>
+ void TransferSTLStyleMap (T& data, TransferMetaFlags metaFlag = kNoTransferFlags);
+};
+
+template <bool kSwapEndianess>
+bool StreamedBinaryRead<kSwapEndianess>::ReadStreamingInfo(StreamingInfo* streamingInfo)
+{
+ Assert(streamingInfo != NULL);
+
+ if (!m_Cache.IsReadingResourceImage())
+ return false;
+
+ // Read the size & offset values from the serialized file
+ // The size & offset describes where the data is in the streamed file
+ UInt32 offset, size;
+ Transfer (size, "ri_size");
+ Transfer (offset, "ri_offset");
+
+ m_Cache.GetStreamingInfo (offset, size, streamingInfo);
+ return true;
+}
+
+
+
+template<bool kSwapEndianess>
+template<class T>
+void StreamedBinaryRead<kSwapEndianess>::TransferSTLStyleArray (T& data, TransferMetaFlags /*metaFlags*/)
+{
+ if (m_Cache.IsReadingResourceImage())
+ {
+ // Read the size & offset from the serialized file
+ UInt32 offset, size;
+ Transfer (size, "ri_size");
+ Transfer (offset, "ri_offset");
+
+ // Fetch the pointer from the pre-loaded resource image.
+ unsigned bufferSize = sizeof (typename T::value_type) * size;
+ UInt8* buffer = m_Cache.FetchResourceImageData (offset, bufferSize);
+ SerializeTraits<T>::resource_image_assign_external (data, buffer, buffer + bufferSize);
+
+ m_Cache.EndResourceImage();
+ }
+ else
+ {
+ SInt32 size;
+ Transfer (size, "size");
+
+ SerializeTraits<T>::ResizeSTLStyleArray (data, size);
+
+ if (!kSwapEndianess && SerializeTraits<typename T::value_type>::AllowTransferOptimization () && SerializeTraits<T>::IsContinousMemoryArray ())
+ {
+ //AssertIf (size != distance (data.begin (), data.end ()));
+ if( size != 0 )
+ ReadDirect (&*data.begin (), size * sizeof (typename T::value_type));
+ }
+ else
+ {
+ typename T::iterator i;
+ typename T::iterator end = data.end ();
+ //AssertIf (size != distance (data.begin (), end));
+ for (i = data.begin ();i != end;++i)
+ Transfer (*i, "data");
+ }
+ }
+}
+
+template<bool kSwapEndianess>
+template<class T>
+void StreamedBinaryRead<kSwapEndianess>::TransferSTLStyleMap (T& data, TransferMetaFlags)
+{
+ SInt32 size;
+ Transfer (size, "size");
+
+ // maps value_type is: pair<const First, Second>
+ // So we have to write to maps non-const value type
+ typename NonConstContainerValueType<T>::value_type p;
+
+ data.clear ();
+ for (int i=0;i<size;i++)
+ {
+ Transfer (p, "data");
+ data.insert (p);
+ }
+}
+
+template<bool kSwapEndianess>
+template<class T>
+void StreamedBinaryRead<kSwapEndianess>::Transfer (T& data, const char*, TransferMetaFlags)
+{
+ SerializeTraits<T>::Transfer (data, *this);
+}
+
+template<bool kSwapEndianess>
+template<class T>
+void StreamedBinaryRead<kSwapEndianess>::TransferWithTypeString (T& data, const char*, const char*, TransferMetaFlags)
+{
+ SerializeTraits<T>::Transfer (data, *this);
+}
+
+template<bool kSwapEndianess>
+template<class T> inline
+void StreamedBinaryRead<kSwapEndianess>::TransferBasicData (T& data)
+{
+ AssertIf (sizeof (T) > 8);
+ m_Cache.Read (data);
+ if (kSwapEndianess)
+ {
+ SwapEndianBytes (data);
+ }
+}
+#endif
+
+
+
diff --git a/Runtime/Serialize/TransferFunctions/StreamedBinaryWrite.cpp b/Runtime/Serialize/TransferFunctions/StreamedBinaryWrite.cpp
new file mode 100644
index 0000000..a7699e9
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/StreamedBinaryWrite.cpp
@@ -0,0 +1,76 @@
+#include "UnityPrefix.h"
+#include "StreamedBinaryWrite.h"
+#include "Configuration/UnityConfigure.h"
+
+template <bool kSwapEndianess>
+CachedWriter& StreamedBinaryWrite<kSwapEndianess>::Init (int flags, BuildTargetSelection target)
+{
+ m_Flags = flags;
+ m_UserData = NULL;
+ m_Target = target;
+
+#if UNITY_EDITOR && CHECK_SERIALIZE_ALIGNMENT
+ m_Cache.SetCheckSerializeAlignment(true);
+ #endif
+ return m_Cache;
+}
+
+template <bool kSwapEndianess>
+CachedWriter& StreamedBinaryWrite<kSwapEndianess>::Init (const CachedWriter& cachedWriter, int flags, BuildTargetSelection target, const BuildUsageTag& buildUsageTag)
+{
+ m_Flags = flags;
+ m_Target = target;
+ m_Cache = cachedWriter;
+ m_UserData = NULL;
+
+ #if UNITY_EDITOR
+ m_BuildUsageTag = buildUsageTag;
+ #endif
+
+#if UNITY_EDITOR && CHECK_SERIALIZE_ALIGNMENT
+ m_Cache.SetCheckSerializeAlignment(true);
+#endif
+ return m_Cache;
+}
+
+template <bool kSwapEndianess>
+void StreamedBinaryWrite<kSwapEndianess>::Align ()
+{
+ m_Cache.Align4Write();
+}
+
+
+template <bool kSwapEndianess>
+void StreamedBinaryWrite<kSwapEndianess>::TransferTypeless (unsigned* byteSize, const char* /* name*/, TransferMetaFlags/* metaFlag*/)
+{
+ SInt32 size = *byteSize;
+ Transfer (size, "size");
+}
+
+// markerID is the id that was given by TransferTypeless.
+// byteStart is
+// optional temporaryDataHandle: temporaryDataHandle is a handle to the data
+// optional copyData: is a pointer to where the data will be written or read from
+template <bool kSwapEndianess>
+void StreamedBinaryWrite<kSwapEndianess>::TransferTypelessData (unsigned byteSize, void* copyData, int/* metaData*/)
+{
+ AssertIf(copyData == NULL && byteSize != 0);
+ m_Cache.Write (copyData, byteSize);
+ Align();
+}
+
+
+template CachedWriter& StreamedBinaryWrite<false>::Init (int flags, BuildTargetSelection target);
+template CachedWriter& StreamedBinaryWrite<true>::Init (int flags, BuildTargetSelection target);
+
+template CachedWriter& StreamedBinaryWrite<false>::Init (const CachedWriter& cachedWriter, int flags, BuildTargetSelection target, const BuildUsageTag& buildUsageTag);
+template CachedWriter& StreamedBinaryWrite<true>::Init (const CachedWriter& cachedWriter, int flags, BuildTargetSelection target, const BuildUsageTag& buildUsageTag);
+
+template void StreamedBinaryWrite<false>::Align ();
+template void StreamedBinaryWrite<true>::Align ();
+
+template void StreamedBinaryWrite<false>::TransferTypeless (unsigned* byteSize, const char*/* name*/, TransferMetaFlags/* metaFlag*/);
+template void StreamedBinaryWrite<true>::TransferTypeless (unsigned* byteSize, const char*/* name*/, TransferMetaFlags/* metaFlag*/);
+
+template void StreamedBinaryWrite<false>::TransferTypelessData (unsigned byteSize, void* copyData, int metaData);
+template void StreamedBinaryWrite<true>::TransferTypelessData (unsigned byteSize, void* copyData, int metaData);
diff --git a/Runtime/Serialize/TransferFunctions/StreamedBinaryWrite.h b/Runtime/Serialize/TransferFunctions/StreamedBinaryWrite.h
new file mode 100644
index 0000000..b5d9074
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/StreamedBinaryWrite.h
@@ -0,0 +1,187 @@
+#ifndef STREAMEDBINARYWRITE_H
+#define STREAMEDBINARYWRITE_H
+
+#include "Runtime/Serialize/TransferFunctions/TransferBase.h"
+#include "Runtime/Serialize/CacheWrap.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+
+template<bool kSwapEndianess>
+class EXPORT_COREMODULE StreamedBinaryWrite : public TransferBase
+{
+ CachedWriter m_Cache;
+ BuildTargetSelection m_Target;
+
+ #if UNITY_EDITOR
+ BuildUsageTag m_BuildUsageTag;
+ #endif
+ friend class MonoBehaviour;
+
+public:
+
+ CachedWriter& Init (int flags, BuildTargetSelection target);
+ CachedWriter& Init (const CachedWriter& cachedWriter, int flags, BuildTargetSelection target, const BuildUsageTag& buildUsageTag);
+
+ bool IsWriting () { return true; }
+ bool IsWritingPPtr () { return true; }
+ bool NeedsInstanceIDRemapping () { return m_Flags & kNeedsInstanceIDRemapping; }
+ bool ConvertEndianess () { return kSwapEndianess; }
+ bool IsWritingGameReleaseData ()
+ {
+ return IsSerializingForGameRelease ();
+ }
+ bool IsBuildingTargetPlatform (BuildTargetPlatform platform)
+ {
+ #if UNITY_EDITOR
+ if (platform == kBuildAnyPlayerData)
+ return m_Target.platform >= kBuildValidPlayer;
+ else
+ return m_Target.platform == platform;
+ #else
+ return false;
+ #endif
+ }
+
+ #if UNITY_EDITOR
+ BuildUsageTag GetBuildUsage ()
+ {
+ return m_BuildUsageTag;
+ }
+ #endif
+
+ BuildTargetSelection GetBuildingTarget () { return m_Target; }
+
+ template<class T>
+ void Transfer (T& data, const char* name, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ template<class T>
+ void TransferWithTypeString (T& data, const char* name, const char* typeName, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ void EnableResourceImage (ActiveResourceImage targetResourceImage)
+ {
+ #if UNITY_EDITOR
+ m_Cache.BeginResourceImage (targetResourceImage);
+ #endif
+ }
+
+ /// In order to transfer typeless data (Read: transfer data real fast)
+ /// Call TransferTypeless. You have to always do this. Even for a proxytransfer. Then when you want to access the datablock.
+ /// Call TransferTypelessData
+ /// On return:
+ /// When reading bytesize will contain the size of the data block that should be read,
+ /// when writing bytesize has to contain the size of the datablock.
+ /// MarkerID will contain an marker which you have to give TransferTypelessData when you want to start the actual transfer.
+ /// optional: A serializedFile will be seperated into two chunks. One is the normal object data. (It is assumed that they are all relatively small)
+ /// So caching them makes a lot of sense. Big datachunks will be stored in another part of the file.
+ /// They will not be cached but usually read directly into the allocated memory, probably reading them asynchronously
+ void TransferTypeless (unsigned* byteSize, const char* name, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ // markerID is the id that was given by TransferTypeless.
+ // copyData is a pointer to where the data will be written or read from
+ void TransferTypelessData (unsigned byteSize, void* copyData, int metaFlag = 0);
+
+ bool GetTransferFileInfo(unsigned* position, const char** filePath) const;
+
+ template<class T>
+ void TransferBasicData (T& data);
+
+ template<class T>
+ void TransferPtr (bool, ReduceCopyData*){}
+
+ template<class T>
+ void TransferSTLStyleArray (T& data, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ template<class T>
+ void TransferSTLStyleMap (T& data, TransferMetaFlags metaFlag = kNoTransferFlags);
+
+ void Align ();
+
+ CachedWriter& GetCachedWriter() { return m_Cache; }
+};
+
+
+
+template<bool kSwapEndianess>
+template<class T> inline
+void StreamedBinaryWrite<kSwapEndianess>::TransferSTLStyleArray (T& data, TransferMetaFlags /*metaFlags*/)
+{
+ #if UNITY_EDITOR
+ if (m_Cache.IsWritingResourceImage())
+ {
+ // Grab the offset where the resourceImage is currently at
+ UInt32 offsetInResourceImage = m_Cache.GetPosition();
+
+ // Write the actual data to the resource image
+ typename T::iterator end = data.end ();
+ for (typename T::iterator i = data.begin ();i != end;++i)
+ Transfer (*i, "data");
+
+ Assert (m_Cache.IsWritingResourceImage());
+ m_Cache.EndResourceImage ();
+ Assert (!m_Cache.IsWritingResourceImage());
+
+ UInt32 size = data.size ();
+
+ // Writ ethe size & offset to the serialized file
+ Transfer (size, "ri_size");
+ Transfer (offsetInResourceImage, "ri_offset");
+ }
+ else
+ #endif
+ {
+ SInt32 size = data.size ();
+ Transfer (size, "size");
+ typename T::iterator end = data.end ();
+ for (typename T::iterator i = data.begin ();i != end;++i)
+ Transfer (*i, "data");
+ }
+}
+
+
+template<bool kSwapEndianess>
+template<class T> inline
+void StreamedBinaryWrite<kSwapEndianess>::TransferSTLStyleMap (T& data, TransferMetaFlags)
+{
+ SInt32 size = data.size ();
+ Transfer (size, "size");
+
+ // maps value_type is: pair<const First, Second>
+ // So we have to write to maps non-const value type
+ typedef typename NonConstContainerValueType<T>::value_type non_const_value_type;
+
+ typename T::iterator end = data.end ();
+ for (typename T::iterator i = data.begin ();i != end;++i)
+ {
+ non_const_value_type& p = (non_const_value_type&)(*i);
+ Transfer (p, "data");
+ }
+}
+
+template<bool kSwapEndianess>
+template<class T> inline
+void StreamedBinaryWrite<kSwapEndianess>::Transfer (T& data, const char*, TransferMetaFlags)
+{
+ SerializeTraits<T>::Transfer (data, *this);
+}
+
+template<bool kSwapEndianess>
+template<class T> inline
+void StreamedBinaryWrite<kSwapEndianess>::TransferWithTypeString (T& data, const char*, const char*, TransferMetaFlags)
+{
+ SerializeTraits<T>::Transfer (data, *this);
+}
+
+template<bool kSwapEndianess>
+template<class T> inline
+void StreamedBinaryWrite<kSwapEndianess>::TransferBasicData (T& data)
+{
+ if (kSwapEndianess)
+ {
+ T temp = data;
+ SwapEndianBytes (temp);
+ m_Cache.Write (temp);
+ }
+ else
+ m_Cache.Write (data);
+}
+
+#endif
diff --git a/Runtime/Serialize/TransferFunctions/TransferBase.h b/Runtime/Serialize/TransferFunctions/TransferBase.h
new file mode 100644
index 0000000..aae527a
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/TransferBase.h
@@ -0,0 +1,177 @@
+#ifndef TRANSFERBASE_H
+#define TRANSFERBASE_H
+
+#include "Runtime/Serialize/SerializeTraits.h"
+#include "Runtime/Serialize/SerializationMetaFlags.h"
+
+
+struct ReduceCopyData;
+struct StreamingInfo;
+
+
+class EXPORT_COREMODULE TransferBase
+{
+public:
+
+ TransferBase ()
+ : m_Flags (0)
+ , m_UserData (NULL) {}
+
+ /// @name Predicates
+ /// @{
+
+ /// Get the TransferInstructionFlags.
+ /// Commonly used to special case transfer functions for specific operations.
+ int GetFlags () { return m_Flags; }
+
+ /// If true, the transfer is reading data from a source. (Could be fread from a file or reading from a block of memory)
+ /// @note There are transfers for which neither IsReading() nor IsWriting() is true (for example when generating a typetree).
+ /// IsReading is NOT the inverse of IsWriting.
+ bool IsReading () { return false; }
+
+ /// If true, the transfer reads PPtrs (Object references)
+ /// This is true when reading from a memory stream or file but also when using RemapPPtrTransfer (A generic way of iterating all object references)
+ bool IsReadingPPtr () { return false; }
+
+ /// Whether the last Transfer() resulted in a value store, i.e. had actual data
+ /// transfered from the stream.
+ /// It is important to use this function instead of IsReading when
+ /// When reading from a stream that does not define all the data, the desired behaviour is that default values from constructor are fully preserved.
+ /// All transfer functions do this internally (transferred properties are left untouched when the data does not exist for example in a Yaml file)
+ /// But when the serialized property needs to be manually converted in the Transfer function, then it is important to check if the value was actually read.
+ /// CODE EXAMPLE:
+ /// bool enabled;
+ /// if (transfer.IsWriting ())
+ /// enabled = m_Flags == 1;
+ /// TRANSFER (enabled);
+ /// if (transfer.DidReadLastProperty ())
+ /// m_Flags = enabled ? 1 : 0;
+ bool DidReadLastProperty () const { return false; }
+
+ /// Same as DidReadLastProperty, but only returns true when reading PPtr properties.
+ /// A compile time optimization necessary for removing generated code by RemapPPtrTransfer.
+ bool DidReadLastPPtrProperty () const { return false; }
+
+
+ /// If true, the transfer is writing out data.
+ bool IsWriting () { return false; }
+ /// If true, the transfer is writing out PPtr data. This is true when writing to a memory stream or file, but also when using RemapPPtrTransfer.
+ bool IsWritingPPtr () { return false; }
+
+ /// Are we reading data from a data source that is not guaranteed to have the same data layout as the Transfer function.
+ /// eg. StreamedBinaryRead always returns false. YamlRead & SafeBinaryRead return true.
+ bool IsReadingBackwardsCompatible () { return false; }
+
+ /// When writing or reading from disk we need to translate instanceID
+ /// to LocalIdentifierInFile & LocalSerializedFileIndex or in the case of Yaml files, guids + LocalIdentifierInFile.
+ /// This returns true when remapping of the instanceID should be performed.
+ bool NeedsInstanceIDRemapping () { return false; }
+
+ /// Are we transferring data with endianess swapping. (We might neither endianess swap on write or read based on IsReading / IsWriting)
+ /// The endianess conversion is done by the TransferBackend, but there are some special cases where you might want to handle it yourself.
+ /// (For example a texture data is transferred a single UInt8* array, so all endianess swapping is the responsibiltiy of the texture transfer function.)
+ bool ConvertEndianess () { return false; }
+
+ /// Are we reading/writing a .meta file (Asset importers use it to differentiate reading/writing of a Library/metadata cached file. )
+ /// @TODO: We should rename Library/metadata to Library/cachedata and cleanup the usage of metadata vs .meta file. It is confusing.
+ bool AssetMetaDataOnly () { return false; }
+
+ /// Is this a RemapPPtrTransfer backend. Commonly used to do very specialized code when generating dependencies using RemapPPtrTransfer.
+ bool IsRemapPPtrTransfer () { return false; }
+
+ /// Return true if we are writing the data for a player.
+ bool IsWritingGameReleaseData () { return false; }
+
+ /// Are we serializing data for use by the player.
+ /// This includes reading/writing/generating typetrees. And can be when creating data from the editor for player or when simply reading/writing data in the player.
+ /// Commonly used to not serialize data that does not exist in the player.
+ bool IsSerializingForGameRelease ()
+ {
+ #if UNITY_EDITOR
+ return m_Flags & kSerializeGameRelease;
+ #else
+ return true;
+ #endif
+ }
+
+ /// @}
+
+ /// @name Build Targets
+ /// @{
+
+ /// Returns true in the editor when writing the data for a player of the specified target platform.
+ bool IsBuildingTargetPlatform (BuildTargetPlatform) { return false; }
+ /// Returns the target platform we are building for. Only returns the target platform when writing data.
+ BuildTargetSelection GetBuildingTarget () { return BuildTargetSelection::NoTarget (); }
+
+ #if UNITY_EDITOR
+ /// BuildUsageTag carries information generated by the build process about the object being serialized.
+ /// For example the buildpipeline might instruct the transfer system to strip normals and tangents from a Mesh,
+ /// because it knows that no renderers & materials in all scenes actually use them.
+ BuildUsageTag GetBuildUsage () { return BuildUsageTag (); }
+ #endif
+
+ /// @}
+
+ /// @name Versioning
+ /// @{
+
+ /// Sets the "version of the class currently transferred"
+ void SetVersion (int) {}
+
+ /// Returns if the transferred data's version is the version used by the source code
+ bool IsVersionSmallerOrEqual (int /*version*/) { return false; }
+
+ /// Deprecated: use IsVersionSmallerOrEqual instead.
+ bool IsOldVersion (int /*version*/) { return false; }
+ bool IsCurrentVersion () { return true; }
+
+ /// @}
+
+ /// @name Transfers
+ /// @{
+
+ /// Alignment in the serialization system is done manually.
+ /// The serialization system only ever cares about 4 byte alignment.
+ /// When writing data that has an alignment of less than 4 bytes, followed by data that has 4 byte alignment,
+ /// then Align must be called before the 4 byte aligned data.
+ /// TRANSFER (1byte);
+ /// TRANSFER (1byte);
+ /// transfer.Align ();
+ /// TRANSFER (4byte);
+ void Align () {}
+
+
+ /// Internal function. Should only be called from SerializeTraits
+ template<class T>
+ void TransferBasicData (T&) { }
+
+ /// Internal function. Should only be called from SerializeTraits
+ template<class T>
+ void TransferPtr (bool, ReduceCopyData*) {}
+
+ /// Internal function. Should only be called from SerializeTraits
+ template<class T>
+ void ReduceCopy (const ReduceCopyData&){}
+ /// @}
+
+ /// user data.
+ void* GetUserData () { return m_UserData; }
+ void SetUserData (void* userData) { m_UserData = userData; }
+
+ void AddMetaFlag(int /*mask*/) {}
+
+ /// Deprecated
+ void BeginMetaGroup (std::string /*name*/) {}
+ void EndMetaGroup () {}
+ void EnableResourceImage (ActiveResourceImage /*targetResourceImage*/) {}
+ bool ReadStreamingInfo (StreamingInfo* /*streamingInfo*/) { return false; }
+ bool NeedNonCriticalMetaFlags () { return false; }
+
+protected:
+
+ int m_Flags;
+ void* m_UserData;
+};
+
+#endif // !TRANSFER_BASE
diff --git a/Runtime/Serialize/TransferFunctions/TransferNameConversions.cpp b/Runtime/Serialize/TransferFunctions/TransferNameConversions.cpp
new file mode 100644
index 0000000..35ae03f
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/TransferNameConversions.cpp
@@ -0,0 +1,76 @@
+#include "UnityPrefix.h"
+#include "TransferNameConversions.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+
+
+TranferNameConversionsManager::TranferNameConversionsManager()
+{
+ m_AllowTypeNameConversions = UNITY_NEW(AllowTypeNameConversions,kMemSerialization);
+ m_AllowNameConversion = UNITY_NEW(AllowNameConversion,kMemSerialization);
+}
+
+TranferNameConversionsManager::~TranferNameConversionsManager()
+{
+ UNITY_DELETE(m_AllowTypeNameConversions,kMemSerialization);
+ UNITY_DELETE(m_AllowNameConversion,kMemSerialization);
+}
+
+TranferNameConversionsManager* TranferNameConversionsManager::s_Instance = NULL;
+void TranferNameConversionsManager::StaticInitialize()
+{
+ s_Instance = UNITY_NEW_AS_ROOT(TranferNameConversionsManager, kMemManager, "SerializationBackwardsCompatibility", "");
+}
+void TranferNameConversionsManager::StaticDestroy()
+{
+ UNITY_DELETE(s_Instance, kMemManager);
+}
+static RegisterRuntimeInitializeAndCleanup s_TranferNameConversionsManagerCallbacks(TranferNameConversionsManager::StaticInitialize, TranferNameConversionsManager::StaticDestroy);
+
+bool AllowTypeNameConversion (const UnityStr& oldType, const char* newTypeName)
+{
+ pair<AllowTypeNameConversions::iterator, AllowTypeNameConversions::iterator> range;
+ range = GetTranferNameConversionsManager().m_AllowTypeNameConversions->equal_range (const_cast<char*>(oldType.c_str()));
+ for (;range.first != range.second;range.first++)
+ {
+ if (strcmp(range.first->second, newTypeName) == 0)
+ return true;
+ }
+
+ // Special support for Mono PPtr's
+ // With Unity 1.6 MonoBehaviour pointers have a special prefix and keep the class name in the PPtr. [ PPtr<$MyClass> ]
+ // With Unity 1.5.1 it was simply PPtr<MonoBehaviour>. This made correct typechecking unneccessarily hard.
+ // Here we provide backwards compatibility with the old method.
+ if (strncmp("PPtr<$", newTypeName, 6) == 0)
+ {
+ if (oldType.find("PPtr<") == 0)
+ return true;
+ }
+
+ return false;
+}
+
+const AllowNameConversion::mapped_type* GetAllowedNameConversions (const char* type, const char* name)
+{
+ const AllowNameConversion::mapped_type* nameConversion = NULL;
+ AllowNameConversion::iterator foundNameConversion = GetTranferNameConversionsManager().m_AllowNameConversion->find(make_pair(const_cast<char*>(type), const_cast<char*>(name)));
+ if (foundNameConversion != GetTranferNameConversionsManager().m_AllowNameConversion->end())
+ nameConversion = &foundNameConversion->second;
+ return nameConversion;
+}
+
+void RegisterAllowTypeNameConversion (const char* from, const char* to)
+{
+ GetTranferNameConversionsManager().m_AllowTypeNameConversions->insert(make_pair(const_cast<char*>(from), const_cast<char*>(to)));
+}
+
+void RegisterAllowNameConversion (const char* type, const char* oldName, const char* newName)
+{
+ AllowNameConversion::mapped_type& allowed = (*GetTranferNameConversionsManager().m_AllowNameConversion)[make_pair(const_cast<char*>(type), const_cast<char*>(newName))];
+ allowed.insert (const_cast<char*>(oldName));
+}
+
+void ClearTypeNameConversion()
+{
+ GetTranferNameConversionsManager().m_AllowTypeNameConversions->clear();
+ GetTranferNameConversionsManager().m_AllowNameConversion->clear();
+}
diff --git a/Runtime/Serialize/TransferFunctions/TransferNameConversions.h b/Runtime/Serialize/TransferFunctions/TransferNameConversions.h
new file mode 100644
index 0000000..3082f8f
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/TransferNameConversions.h
@@ -0,0 +1,56 @@
+#ifndef TRANSFERNAMECONVERSIONS_H
+#define TRANSFERNAMECONVERSIONS_H
+
+#include "Runtime/Utilities/CStringHash.h"
+#include "Runtime/Modules/ExportModules.h"
+
+using namespace std;
+
+struct smaller_cstring_pair : std::binary_function<std::pair<char*, char*>, std::pair<char*, char*>, std::size_t>
+{
+ bool operator () (pair<char*, char*> lhs, pair<char*, char*> rhs) const
+ {
+ int first = strcmp (lhs.first, rhs.first);
+ if (first != 0)
+ return first < 0;
+ else
+ return strcmp (lhs.second, rhs.second) < 0;
+ }
+};
+
+typedef std::multimap<char*, char*, smaller_cstring> AllowTypeNameConversions;
+typedef std::map<std::pair<char*, char*>, set<char*, smaller_cstring>, smaller_cstring_pair> AllowNameConversion;
+
+class TranferNameConversionsManager
+{
+public:
+ AllowTypeNameConversions* m_AllowTypeNameConversions;
+ AllowNameConversion* m_AllowNameConversion;
+
+ TranferNameConversionsManager();
+ ~TranferNameConversionsManager();
+
+ static TranferNameConversionsManager* s_Instance;
+ static void StaticInitialize();
+ static void StaticDestroy();
+};
+inline TranferNameConversionsManager& GetTranferNameConversionsManager() { return *TranferNameConversionsManager::s_Instance; }
+
+
+/// Allows type name conversion from oldTypeName to newTypeName(The passed strings will not be copied so you can only pass in constant strings)
+/// (Useful for depracating types -> RegisterAllowTypeNameConversion ("UniqueIdentifier", "GUID");)
+/// "UniqueIdentifier" can now be renamed to "GUID" and serialization will just work!
+void RegisterAllowTypeNameConversion (const char* oldTypeName, const char* newTypeName);
+
+/// Allows name conversion from oldName to newName? (The passed strings will not be copied so you can only pass in constant strings)
+/// (Useful for deprecating names -> m_NewPosition will now load from m_DeprecatedPosition in an old serialized file
+/// RegisterAllowNameConversion (MyClass::GetClassStringStatic(), "m_DeprecatedPosition", "m_NewPosition");
+EXPORT_COREMODULE void RegisterAllowNameConversion (const char* type, const char* oldName, const char* newName);
+
+const AllowNameConversion::mapped_type* GetAllowedNameConversions (const char* type, const char* name);
+
+bool AllowTypeNameConversion (const UnityStr& oldType, const char* newTypeName);
+
+void ClearTypeNameConversion ();
+
+#endif
diff --git a/Runtime/Serialize/TransferFunctions/YAMLRead.cpp b/Runtime/Serialize/TransferFunctions/YAMLRead.cpp
new file mode 100644
index 0000000..4a9ca42
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/YAMLRead.cpp
@@ -0,0 +1,238 @@
+#include "UnityPrefix.h"
+#include "YAMLRead.h"
+#include "../FileCache.h"
+
+int YAMLRead::GetDataVersion ()
+{
+ if (m_Versions.back() == -1)
+ {
+ yaml_node_t *node = m_CurrentNode;
+ int i = m_MetaParents.size();
+ do
+ {
+ yaml_node_t *versionNode = GetValueForKey(node, "serializedVersion");
+ if (versionNode)
+ {
+ Assert (versionNode->type == YAML_SCALAR_NODE);
+ sscanf ((char*)versionNode->data.scalar.value, "%d", &m_Versions.back());
+ return m_Versions.back();
+ }
+ // If "serializedVersion" is not found, look for "importerVersion" for backwards compatibility.
+ versionNode = GetValueForKey(node, "importerVersion");
+ if (versionNode)
+ {
+ Assert (versionNode->type == YAML_SCALAR_NODE);
+ sscanf ((char*)versionNode->data.scalar.value, "%d", &m_Versions.back());
+ return m_Versions.back();
+ }
+ if (i>0)
+ node = m_MetaParents[--i];
+ else
+ node = NULL;
+ }
+ while (node != NULL);
+ m_Versions.back() = 1;
+ }
+ return m_Versions.back();
+}
+
+yaml_node_t *YAMLRead::GetValueForKey (yaml_node_t* parentNode, const char* keystr)
+{
+ if (parentNode && parentNode->type == YAML_MAPPING_NODE)
+ {
+ // The code below does not handle empty yaml arrays.
+ if (parentNode->data.mapping.pairs.top == parentNode->data.mapping.pairs.start)
+ return NULL;
+
+ yaml_node_pair_t* start;
+ if (m_CachedIndex < parentNode->data.mapping.pairs.top
+ && m_CachedIndex >= parentNode->data.mapping.pairs.start)
+ start = m_CachedIndex;
+ else
+ start = parentNode->data.mapping.pairs.start;
+
+ yaml_node_pair_t* top = parentNode->data.mapping.pairs.top;
+ yaml_node_pair_t* i = start;
+
+ do
+ {
+ yaml_node_pair_t* next = i+1;
+ if (next == top)
+ next = parentNode->data.mapping.pairs.start;
+
+ yaml_node_t* key = yaml_document_get_node(m_ActiveDocument, i->key);
+ if (key == NULL)
+ {
+ // I've seen a crash bug report with no repro, indicating that this is happening.
+ // If you ever get this error and can repro it, let me know! jonas.
+ ErrorString ("YAML Node is NULL!\n");
+ }
+ else
+ {
+ Assert (key->type == YAML_SCALAR_NODE);
+
+ if (strcmp((char*)key->data.scalar.value, keystr) == 0)
+ {
+ m_CachedIndex = next;
+ return yaml_document_get_node(m_ActiveDocument, i->value);
+ }
+ }
+ i = next;
+ }
+ while (i != start);
+ }
+ return NULL;
+}
+
+
+void YAMLRead::Init(int flags, yaml_read_handler_t *handler, std::string *debugFileName, int debugLineCount)
+{
+ m_UserData = NULL;
+ m_CurrentVersion = 0;
+ m_Flags = flags;
+ m_CachedIndex = NULL;
+ m_ReadHandler = handler;
+
+ yaml_parser_t parser;
+
+ memset(&parser, 0, sizeof(parser));
+ memset(&m_Document, 0, sizeof(m_Document));
+
+ if (!yaml_parser_initialize(&parser))
+ {
+ ErrorString("Could not initialize yaml parser\n");
+ return;
+ }
+
+ yaml_parser_set_input(&parser, handler, this );
+ yaml_parser_load(&parser, &m_Document);
+
+ if (parser.error != YAML_NO_ERROR)
+ {
+ if (debugFileName != NULL)
+ {
+ ErrorStringMsg("Unable to parse file %s: [%s] at line %d\n", debugFileName->c_str(), parser.problem, debugLineCount + (int)parser.problem_mark.line);
+ }
+ else
+ {
+ ErrorStringMsg("Unable to parse YAML file: [%s] at line %d\n", parser.problem, debugLineCount + (int)parser.problem_mark.line);
+ }
+ }
+
+ yaml_parser_delete(&parser);
+
+ m_Versions.push_back(-1);
+ m_CurrentNode = yaml_document_get_root_node(&m_Document);
+ m_ActiveDocument = &m_Document;
+ m_DidReadLastProperty = false;
+}
+
+YAMLRead::YAMLRead (yaml_document_t* yamlDocument, int flags)
+: m_ReadHandler (NULL)
+, m_ActiveDocument (yamlDocument)
+, m_CurrentVersion (0)
+, m_CachedIndex (0)
+, m_DidReadLastProperty (false)
+{
+ m_Flags = flags;
+ memset(&m_Document, 0, sizeof(m_Document));
+ m_Versions.push_back(-1);
+ m_CurrentNode = yaml_document_get_root_node(m_ActiveDocument);
+}
+
+
+int YAMLRead::YAMLReadCacheHandler(void *data, unsigned char *buffer, size_t size, size_t *size_read)
+{
+ YAMLRead *read = (YAMLRead*)data;
+
+ if (read->m_ReadOffset + size > read->m_EndOffset)
+ size = read->m_EndOffset - read->m_ReadOffset;
+
+ ReadFileCache (*(CacheReaderBase*)read->m_ReadData, buffer, read->m_ReadOffset, size);
+ read->m_ReadOffset += size;
+ *size_read = size;
+
+ return true;
+}
+
+int YAMLRead::YAMLReadStringHandler(void *data, unsigned char *buffer, size_t size, size_t *size_read)
+{
+ YAMLRead *read = (YAMLRead*)data;
+
+ if (read->m_ReadOffset + size > read->m_EndOffset)
+ size = read->m_EndOffset - read->m_ReadOffset;
+
+ const char* readData = reinterpret_cast<const char*> (read->m_ReadData);
+
+ memcpy (buffer, readData + read->m_ReadOffset, size);
+ read->m_ReadOffset += size;
+ *size_read = size;
+
+ return true;
+}
+
+YAMLRead::YAMLRead (const char* strBuffer, int size, int flags, std::string *debugFileName, int debugLineCount)
+{
+ m_ReadOffset = 0;
+ m_EndOffset = size;
+ m_ReadData = const_cast<char*> (strBuffer);
+
+ Init (flags, YAMLReadStringHandler, debugFileName, debugLineCount);
+}
+
+YAMLRead::YAMLRead (const CacheReaderBase *input, size_t readOffset, size_t endOffset, int flags, std::string *debugFileName, int debugLineCount)
+{
+ m_ReadOffset = readOffset;
+ m_EndOffset = endOffset;
+ m_ReadData = (void*)input;
+
+ Init (flags, YAMLReadCacheHandler, debugFileName, debugLineCount);
+}
+
+YAMLRead::~YAMLRead()
+{
+ yaml_document_delete(&m_Document);
+}
+
+YAMLNode* YAMLRead::GetCurrentNode ()
+{
+ return YAMLDocNodeToNode(m_ActiveDocument, m_CurrentNode);
+}
+
+YAMLNode* YAMLRead::GetValueNodeForKey (const char* key)
+{
+ return YAMLDocNodeToNode (m_ActiveDocument, GetValueForKey (m_CurrentNode, key));
+}
+
+int YAMLRead::StringOutputHandler(void *data, unsigned char *buffer, size_t size)
+{
+ string* theString = reinterpret_cast<string*> (data);
+ theString->append( (char *) buffer, size);
+ return 1;
+}
+
+void YAMLRead::BeginMetaGroup (std::string name)
+{
+ m_MetaParents.push_back(m_CurrentNode);
+ m_CurrentNode = GetValueForKey(m_CurrentNode, name.c_str());
+}
+
+void YAMLRead::EndMetaGroup ()
+{
+ m_CurrentNode = m_MetaParents.back();
+ m_MetaParents.pop_back();
+}
+
+void YAMLRead::TransferTypelessData (unsigned size, void* data, int metaFlag)
+{
+ UnityStr dataString;
+ Transfer(dataString, "_typelessdata", metaFlag);
+ dataString.resize (size * 2);
+ HexStringToBytes (&dataString[0], size, data);
+}
+
+bool YAMLRead::HasNode (const char* name)
+{
+ return GetValueForKey(m_CurrentNode, name) != NULL;
+}
+
diff --git a/Runtime/Serialize/TransferFunctions/YAMLRead.h b/Runtime/Serialize/TransferFunctions/YAMLRead.h
new file mode 100644
index 0000000..41e6148
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/YAMLRead.h
@@ -0,0 +1,422 @@
+#ifndef YAMLREAD_H
+#define YAMLREAD_H
+
+#include "Runtime/Serialize/TransferFunctions/TransferBase.h"
+#include "TransferNameConversions.h"
+#include "YAMLSerializeTraits.h"
+#include "Editor/Src/Utility/YAMLNode.h"
+#include "External/yaml/include/yaml.h"
+#include "Runtime/Serialize/FloatStringConversion.h"
+#include "Runtime/Utilities/TypeUtilities.h"
+
+class CacheReaderBase;
+struct StreamingInfo;
+
+class YAMLRead : public TransferBase
+{
+private:
+
+ int m_CurrentVersion;
+ std::string m_CurrentType;
+ std::string m_NodeName;
+ bool m_DidReadLastProperty;
+
+ yaml_document_t m_Document;
+ yaml_document_t* m_ActiveDocument;
+ std::vector<yaml_node_t*> m_MetaParents;
+ std::vector<int> m_Versions;
+ yaml_node_t* m_CurrentNode;
+ yaml_node_pair_t* m_CachedIndex;
+ yaml_read_handler_t *m_ReadHandler;
+ void* m_ReadData;
+ size_t m_ReadOffset;
+ size_t m_EndOffset;
+
+ int GetDataVersion ();
+ yaml_node_t *GetValueForKey(yaml_node_t* parentNode, const char* keystr);
+
+ static int YAMLReadCacheHandler(void *data, unsigned char *buffer, size_t size, size_t *size_read);
+ static int YAMLReadStringHandler(void *data, unsigned char *buffer, size_t size, size_t *size_read);
+
+ void Init(int flags, yaml_read_handler_t *handler, std::string *debugFileName, int debugLineCount);
+
+public:
+
+ YAMLRead (const char* strBuffer, int size, int flags, std::string *debugFileName = NULL, int debugLineCount = 0);
+ YAMLRead (const CacheReaderBase *input, size_t readOffset, size_t endOffset, int flags, std::string *debugFileName = NULL, int debugLineCount = 0);
+ YAMLRead (yaml_document_t* yamlDocument, int flags);
+ ~YAMLRead();
+
+ void SetVersion (int version) { m_CurrentVersion = version; }
+ bool IsCurrentVersion () { return m_CurrentVersion == GetDataVersion(); }
+ bool IsOldVersion (int version) { return version == GetDataVersion(); }
+ bool IsVersionSmallerOrEqual (int version) { return version >= GetDataVersion(); }
+
+ bool IsReading () { return true; }
+ bool IsReadingPPtr () { return true; }
+ bool IsReadingBackwardsCompatible() { return true; }
+ bool NeedsInstanceIDRemapping () { return m_Flags & kNeedsInstanceIDRemapping; }
+ bool DirectStringTransfer () { return true; }
+ bool AssetMetaDataOnly () { return m_Flags & kAssetMetaDataOnly; }
+
+ bool IsSerializingForGameRelease () { return false; }
+
+ bool DidReadLastProperty () { return m_DidReadLastProperty; }
+ bool DidReadLastPPtrProperty () { return m_DidReadLastProperty; }
+
+ YAMLNode* GetCurrentNode ();
+ YAMLNode* GetValueNodeForKey (const char* key);
+
+ static int StringOutputHandler(void *data, unsigned char *buffer, size_t size) ;
+ void BeginMetaGroup (std::string name);
+ void EndMetaGroup ();
+
+ template<class T>
+ void Transfer (T& data, const char* name, int metaFlag = 0);
+ template<class T>
+ void TransferWithTypeString (T& data, const char*, const char*, int metaFlag = 0);
+
+ void TransferTypeless (unsigned* value, const char* name, int metaFlag = 0)
+ {
+ Transfer(*value, name, metaFlag);
+ }
+
+ bool HasNode (const char* name);
+
+ void TransferTypelessData (unsigned size, void* data, int metaFlag = 0);
+
+ template<class T>
+ void TransferBasicData (T& data);
+
+ template<class T>
+ void TransferPtr (bool, ReduceCopyData*){}
+
+ template<class T>
+ void TransferStringData (T& data);
+
+ template<class T>
+ void TransferSTLStyleArray (T& data, int metaFlag = 0);
+
+ template<class T>
+ void TransferSTLStyleMap (T& data, int metaFlag = 0);
+
+ template<class T>
+ void TransferSTLStyleSet (T& data, int metaFlag = 0);
+
+ template<class T>
+ void TransferPair (T& data, int metaFlag = 0, yaml_node_pair_t* pair = NULL);
+};
+
+template<>
+inline void YAMLRead::TransferBasicData<bool> (bool& data)
+{
+ int i;
+ sscanf ((char*)m_CurrentNode->data.scalar.value, "%d", &i);
+ data = (i == 0);
+}
+
+template<>
+inline void YAMLRead::TransferBasicData<char> (char& data)
+{
+ //scanf on msvc does not support %hhd. read int instead
+ int i;
+ sscanf ((char*)m_CurrentNode->data.scalar.value, "%d", &i);
+ data = i;
+}
+
+template<>
+inline void YAMLRead::TransferBasicData<SInt8> (SInt8& data)
+{
+ TransferBasicData (reinterpret_cast<char&> (data));
+}
+
+template<>
+inline void YAMLRead::TransferBasicData<UInt8> (UInt8& data)
+{
+ //scanf on msvc does not support %hhu. read unsigned int instead
+ unsigned int i;
+ sscanf ((char*)m_CurrentNode->data.scalar.value, "%u", &i);
+ data = i;
+}
+
+template<>
+inline void YAMLRead::TransferBasicData<SInt16> (SInt16& data)
+{
+ sscanf ((char*)m_CurrentNode->data.scalar.value, "%hd", &data);
+}
+
+template<>
+inline void YAMLRead::TransferBasicData<UInt16> (UInt16& data)
+{
+ sscanf ((char*)m_CurrentNode->data.scalar.value, "%hu", &data);
+}
+
+template<>
+inline void YAMLRead::TransferBasicData<SInt32> (SInt32& data)
+{
+ sscanf ((char*)m_CurrentNode->data.scalar.value, "%d", &data);
+}
+
+template<>
+inline void YAMLRead::TransferBasicData<UInt32> (UInt32& data)
+{
+ sscanf ((char*)m_CurrentNode->data.scalar.value, "%u", &data);
+}
+
+template<>
+inline void YAMLRead::TransferBasicData<SInt64> (SInt64& data)
+{
+ // msvc does not like %lld. Just read hex data directly.
+ Assert (strlen((char*)m_CurrentNode->data.scalar.value) == 16);
+ HexStringToBytes ((char*)m_CurrentNode->data.scalar.value, sizeof(SInt64), &data);
+}
+
+template<>
+inline void YAMLRead::TransferBasicData<UInt64> (UInt64& data)
+{
+ // msvc does not like %lld. Just read hex data directly.
+ Assert (strlen((char*)m_CurrentNode->data.scalar.value) == 16);
+ HexStringToBytes ((char*)m_CurrentNode->data.scalar.value, sizeof(UInt64), &data);
+}
+
+template<>
+inline void YAMLRead::TransferBasicData<double> (double& data)
+{
+ data = StringToDoubleAccurate((char*)m_CurrentNode->data.scalar.value);
+}
+
+template<>
+inline void YAMLRead::TransferBasicData<float> (float& data)
+{
+ data = StringToFloatAccurate ((char*)m_CurrentNode->data.scalar.value);
+}
+
+template<class T>
+inline void YAMLRead::TransferStringData (T& data)
+{
+ data = (char*)m_CurrentNode->data.scalar.value;
+}
+
+template<class T>
+void YAMLRead::Transfer (T& data, const char* _name, int metaFlag)
+{
+ m_DidReadLastProperty = false;
+
+ if (metaFlag & kIgnoreInMetaFiles)
+ return;
+
+ std::string name = YAMLSerializeTraits<T>::ParseName(_name, AssetMetaDataOnly());
+
+ yaml_node_t *parentNode = m_CurrentNode;
+ m_CurrentNode = GetValueForKey(parentNode, name.c_str());
+ if (!m_CurrentNode)
+ {
+ const AllowNameConversion::mapped_type* nameConversion = GetAllowedNameConversions (m_CurrentType.c_str(), _name);
+ if (nameConversion)
+ {
+ for (AllowNameConversion::mapped_type::const_iterator i = nameConversion->begin(); i!=nameConversion->end();i++)
+ {
+ m_CurrentNode = GetValueForKey(parentNode, *i);
+ if (m_CurrentNode)
+ break;
+ }
+ }
+ if (!m_CurrentNode)
+ {
+ if (strcmp (_name, "Base") == 0)
+ {
+ if (parentNode && parentNode->type == YAML_MAPPING_NODE)
+ {
+ if (parentNode->data.mapping.pairs.start != parentNode->data.mapping.pairs.top)
+ m_CurrentNode = yaml_document_get_node(m_ActiveDocument, parentNode->data.mapping.pairs.start->value);
+ }
+ }
+ else if (metaFlag & kTransferAsArrayEntryNameInMetaFiles)
+ {
+ YAMLSerializeTraits<T>::TransferStringToData (data, m_NodeName);
+ m_CurrentNode = parentNode;
+ return;
+ }
+ }
+ }
+
+ std::string parentType = m_CurrentType;
+ m_CurrentType = SerializeTraits<T>::GetTypeString (&data);
+
+ if (m_CurrentNode != NULL)
+ {
+ m_Versions.push_back(-1);
+ YAMLSerializeTraits<T>::Transfer (data, *this);
+ m_Versions.pop_back();
+ m_DidReadLastProperty = true;
+ }
+
+ m_CurrentNode = parentNode;
+ m_CurrentType = parentType;
+}
+
+template<class T>
+void YAMLRead::TransferWithTypeString (T& data, const char* name, const char*, int metaFlag)
+{
+ Transfer(data, name, metaFlag);
+}
+
+
+template<class T>
+void YAMLRead::TransferSTLStyleArray (T& data, int /*metaFlag*/)
+{
+ yaml_node_t *parentNode = m_CurrentNode;
+ typedef typename NonConstContainerValueType<T>::value_type non_const_value_type;
+
+ switch (m_CurrentNode->type)
+ {
+ case YAML_SCALAR_NODE:
+ {
+#if UNITY_BIG_ENDIAN
+#error "Needs swapping to be implemented to work on big endian platforms!"
+#endif
+ std::string str;
+ TransferStringData (str);
+ size_t byteLength = str.size() / 2;
+ size_t numElements = byteLength / sizeof(non_const_value_type);
+ SerializeTraits<T>::ResizeSTLStyleArray (data, numElements);
+ typename T::iterator dataIterator = data.begin ();
+ for (size_t i=0; i<numElements; i++)
+ {
+ HexStringToBytes (&str[i*2*sizeof(non_const_value_type)], sizeof(non_const_value_type), (void*)&*dataIterator);
+ ++dataIterator;
+ }
+ }
+ break;
+
+ case YAML_SEQUENCE_NODE:
+ {
+ yaml_node_item_t* start = m_CurrentNode->data.sequence.items.start;
+ yaml_node_item_t* top = m_CurrentNode->data.sequence.items.top;
+
+ SerializeTraits<T>::ResizeSTLStyleArray (data, top - start);
+ typename T::iterator dataIterator = data.begin ();
+
+ for(yaml_node_item_t* i = start; i != top; i++)
+ {
+ m_CurrentNode = yaml_document_get_node(m_ActiveDocument, *i);
+ YAMLSerializeTraits<non_const_value_type>::Transfer (*dataIterator, *this);
+ ++dataIterator;
+ }
+ }
+ break;
+
+ // Some stupid old-style meta data writing code unnecessarily used mappings
+ // instead of sequences to encode arrays. So, we're able to read that as well.
+ case YAML_MAPPING_NODE:
+ {
+ yaml_node_pair_t* start = m_CurrentNode->data.mapping.pairs.start;
+ yaml_node_pair_t* top = m_CurrentNode->data.mapping.pairs.top;
+
+ SerializeTraits<T>::ResizeSTLStyleArray (data, top - start);
+ typename T::iterator dataIterator = data.begin ();
+
+ for(yaml_node_pair_t* i = start; i != top; i++)
+ {
+ yaml_node_t* key = yaml_document_get_node(m_ActiveDocument, i->key);
+ Assert (key->type == YAML_SCALAR_NODE);
+
+ m_NodeName = (std::string)(char*)key->data.scalar.value;
+ m_CurrentNode = yaml_document_get_node(m_ActiveDocument, i->value);
+
+ YAMLSerializeTraits<non_const_value_type>::Transfer (*dataIterator, *this);
+ ++dataIterator;
+ }
+ }
+ break;
+
+ default:
+ ErrorString("Unexpected node type.");
+ }
+
+ m_CurrentNode = parentNode;
+}
+
+template<class T>
+void YAMLRead::TransferSTLStyleMap (T& data, int metaFlag)
+{
+ if (m_CurrentNode->type == YAML_MAPPING_NODE)
+ {
+ yaml_node_pair_t* start = m_CurrentNode->data.mapping.pairs.start;
+ yaml_node_pair_t* top = m_CurrentNode->data.mapping.pairs.top;
+
+ data.clear();
+
+ yaml_node_t *parentNode = m_CurrentNode;
+
+ for(yaml_node_pair_t* i = start; i != top; i++)
+ {
+ typedef typename NonConstContainerValueType<T>::value_type non_const_value_type;
+ typedef typename non_const_value_type::first_type first_type;
+ non_const_value_type p;
+
+ if (!YAMLSerializeTraits<first_type>::IsBasicType())
+ {
+ m_CurrentNode = yaml_document_get_node(m_ActiveDocument, i->value);
+
+ YAMLSerializeTraits<non_const_value_type>::Transfer (p, *this);
+ }
+ else
+ TransferPair (p, metaFlag, i);
+
+ data.insert (p);
+ }
+ m_CurrentNode = parentNode;
+ }
+}
+
+template<class T>
+void YAMLRead::TransferSTLStyleSet (T& data, int /*metaFlag*/)
+{
+ if (m_CurrentNode->type == YAML_SEQUENCE_NODE)
+ {
+ yaml_node_item_t* start = m_CurrentNode->data.sequence.items.start;
+ yaml_node_item_t* top = m_CurrentNode->data.sequence.items.top;
+
+ data.clear();
+
+ yaml_node_t *parentNode = m_CurrentNode;
+
+ for(yaml_node_item_t* i = start; i != top; i++)
+ {
+ typedef typename NonConstContainerValueType<T>::value_type non_const_value_type;
+ non_const_value_type p;
+
+ m_CurrentNode = yaml_document_get_node(m_ActiveDocument, *i);
+
+ YAMLSerializeTraits<non_const_value_type>::Transfer (p, *this);
+
+ data.insert (p);
+ }
+ m_CurrentNode = parentNode;
+ }
+}
+
+template<class T>
+void YAMLRead::TransferPair (T& data, int /*metaFlag*/, yaml_node_pair_t* pair)
+{
+ typedef typename T::first_type first_type;
+ typedef typename T::second_type second_type;
+
+ if (pair == NULL)
+ {
+ yaml_node_pair_t* start = m_CurrentNode->data.mapping.pairs.start;
+ yaml_node_pair_t* top = m_CurrentNode->data.mapping.pairs.top;
+ if (start == top)
+ return;
+ pair = start;
+ }
+
+ yaml_node_t* parent = m_CurrentNode;
+ m_CurrentNode = yaml_document_get_node(m_ActiveDocument, pair->key);
+ YAMLSerializeTraits<first_type>::Transfer (data.first, *this);
+ m_CurrentNode = yaml_document_get_node(m_ActiveDocument, pair->value);
+ YAMLSerializeTraits<second_type>::Transfer (data.second, *this);
+ m_CurrentNode = parent;
+}
+#endif
diff --git a/Runtime/Serialize/TransferFunctions/YAMLSerializeTraits.cpp b/Runtime/Serialize/TransferFunctions/YAMLSerializeTraits.cpp
new file mode 100644
index 0000000..4dfffb8
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/YAMLSerializeTraits.cpp
@@ -0,0 +1,139 @@
+#include "UnityPrefix.h"
+#include "TransferNameConversions.h"
+#include "YAMLRead.h"
+#include "YAMLWrite.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Editor/Src/GUIDPersistentManager.h"
+#include "Runtime/Serialize/SerializedFile.h"
+/*
+@TODO:
+
+- Meta file for texture importer has additional unnecessary settings:
+ TextureImporter:
+ fileIDToRecycleName: {}
+
+- Add line offsets to SerializedFile ObjectInfo, so we can have proper line numbers in YAML errors.
+
+*/
+
+// Text transfer of Unity References:
+// If NeedsInstanceIDRemapping() is false or for null references:
+// {instanceID: id}
+// For local objects in same file:
+// {fileID: id}
+// For objects from other file with GUID:
+// {fileID: id, guid: g, type: t}
+
+template<class T>
+void TransferYAMLPtr (T& data, YAMLRead& transfer)
+{
+ SInt32 instanceID = 0;
+ if (!transfer.NeedsInstanceIDRemapping())
+ {
+ transfer.Transfer (instanceID, "instanceID");
+ data.SetInstanceID (instanceID);
+ }
+ else
+ {
+ bool allowLocalIdentifier = (transfer.GetFlags () & kYamlGlobalPPtrReference) == 0;
+
+ LocalIdentifierInFileType fileID = 0;
+ TRANSFER (fileID);
+
+ if (transfer.HasNode("guid"))
+ {
+ FileIdentifier id;
+ transfer.Transfer (id.guid, "guid");
+ transfer.Transfer (id.type, "type");
+
+ id.Fix_3_5_BackwardsCompatibility ();
+ PersistentManager& pm = GetPersistentManager();
+ SInt32 globalIndex = pm.InsertFileIdentifierInternal(id, true);
+ SerializedObjectIdentifier identifier (globalIndex, fileID);
+
+ #if SUPPORT_INSTANCE_ID_REMAP_ON_LOAD
+ pm.ApplyInstanceIDRemap (identifier);
+ #endif
+
+ instanceID = pm.SerializedObjectIdentifierToInstanceID (identifier);
+ }
+ else if (allowLocalIdentifier)
+ {
+ // local fileID
+ LocalSerializedObjectIdentifier identifier;
+ identifier.localIdentifierInFile = fileID;
+ identifier.localSerializedFileIndex = 0;
+ LocalSerializedObjectIdentifierToInstanceID (identifier, instanceID);
+ }
+
+ data.SetInstanceID (instanceID);
+ }
+}
+
+template<class T>
+void TransferYAMLPtr (T& data, YAMLWrite& transfer)
+{
+ transfer.AddMetaFlag(kTransferUsingFlowMappingStyle);
+ SInt32 instanceID = data.GetInstanceID();
+ if (!transfer.NeedsInstanceIDRemapping())
+ transfer.Transfer (instanceID, "instanceID");
+ else
+ {
+ // By default we allow writing self references that exclude guid & type.
+ // This way references inside of the file will never be lost even if the guid of the file changes
+ bool allowLocalIdentifier = (transfer.GetFlags () & kYamlGlobalPPtrReference) == 0;
+ if (allowLocalIdentifier)
+ {
+ LocalSerializedObjectIdentifier localIdentifier;
+ InstanceIDToLocalSerializedObjectIdentifier (instanceID, localIdentifier);
+ if (localIdentifier.localSerializedFileIndex == 0)
+ {
+ transfer.Transfer (localIdentifier.localIdentifierInFile, "fileID");
+ return;
+ }
+ }
+
+ GUIDPersistentManager& pm = GetGUIDPersistentManager();
+ pm.Lock();
+ SerializedObjectIdentifier identifier;
+ if (pm.InstanceIDToSerializedObjectIdentifier(instanceID, identifier))
+ {
+ FileIdentifier id = pm.PathIDToFileIdentifierInternal(identifier.serializedFileIndex);
+ transfer.Transfer (identifier.localIdentifierInFile, "fileID");
+ transfer.Transfer (id.guid, "guid");
+ transfer.Transfer (id.type, "type");
+ }
+ else
+ {
+ instanceID = 0;
+ transfer.Transfer (instanceID, "instanceID");
+ }
+ pm.Unlock();
+ }
+}
+
+template<>
+void TransferYAMLPPtr (PPtr<Object> &data, YAMLRead& transfer) { TransferYAMLPtr (data, transfer); }
+template<>
+void TransferYAMLPPtr (PPtr<Object> &data, YAMLWrite& transfer) { TransferYAMLPtr (data, transfer); }
+template<>
+void TransferYAMLPPtr (ImmediatePtr<Object> &data, YAMLRead& transfer) { TransferYAMLPtr (data, transfer); }
+template<>
+void TransferYAMLPPtr (ImmediatePtr<Object> &data, YAMLWrite& transfer) { TransferYAMLPtr (data, transfer); }
+
+
+template<>
+void YAMLSerializeTraits<UnityGUID>::Transfer (UnityGUID& data, YAMLRead& transfer)
+{
+ std::string str;
+ transfer.TransferStringData(str);
+ data = StringToGUID(str);
+}
+
+template<>
+void YAMLSerializeTraits<UnityGUID>::Transfer (UnityGUID& data, YAMLWrite& transfer)
+{
+ std::string str = GUIDToString(data);
+ transfer.TransferStringData(str);
+}
+
diff --git a/Runtime/Serialize/TransferFunctions/YAMLSerializeTraits.h b/Runtime/Serialize/TransferFunctions/YAMLSerializeTraits.h
new file mode 100644
index 0000000..04cf3aa
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/YAMLSerializeTraits.h
@@ -0,0 +1,236 @@
+#ifndef YAMLSERIALIZETRAITS_H
+#define YAMLSERIALIZETRAITS_H
+
+class Object;
+template<class T>
+class PPtr;
+template<class T>
+class ImmediatePtr;
+struct UnityGUID;
+class YAMLRead;
+class YAMLWrite;
+
+template<class T>
+class YAMLSerializeTraitsBase
+{
+ public:
+ inline static std::string ParseName (const char* _name, bool stripNames)
+ {
+ std::string name = _name;
+
+ if (name == "Base")
+ name = SerializeTraits<T>::GetTypeString (NULL);
+ else if (stripNames)
+ {
+ if (name.rfind(".") != std::string::npos)
+ name = name.substr(name.rfind(".") + 1);
+ if (name.length() >= 3 && name.find("m_") == 0)
+ name = (char)tolower(name[2]) + name.substr(3);
+ }
+ return name;
+ }
+
+ inline static bool ShouldSerializeArrayAsCompactString ()
+ {
+ return false;
+ }
+
+ inline static bool IsBasicType ()
+ {
+ return false;
+ }
+
+ template<class TransferFunction> inline
+ static void Transfer (T& data, TransferFunction& transfer)
+ {
+ SerializeTraits<T>::Transfer (data, transfer);
+ }
+
+ inline static void TransferStringToData (T& /*data*/, std::string& /*str*/)
+ {
+ }
+};
+
+template<class T>
+class YAMLSerializeTraits : public YAMLSerializeTraitsBase<T> {};
+
+template<class T>
+class YAMLSerializeTraitsForBasicType : public YAMLSerializeTraitsBase<T>
+{
+ public:
+ inline static bool ShouldSerializeArrayAsCompactString ()
+ {
+ return true;
+ }
+
+ inline static bool IsBasicType ()
+ {
+ return true;
+ }
+};
+
+template<>
+class YAMLSerializeTraits<UInt16> : public YAMLSerializeTraitsForBasicType<UInt16> {};
+
+template<>
+class YAMLSerializeTraits<SInt16> : public YAMLSerializeTraitsForBasicType<SInt16> {};
+
+template<>
+class YAMLSerializeTraits<UInt32> : public YAMLSerializeTraitsForBasicType<UInt32> {};
+
+template<>
+class YAMLSerializeTraits<SInt32> : public YAMLSerializeTraitsForBasicType<SInt32> {};
+
+template<>
+class YAMLSerializeTraits<UInt64> : public YAMLSerializeTraitsForBasicType<UInt64> {};
+
+template<>
+class YAMLSerializeTraits<SInt64> : public YAMLSerializeTraitsForBasicType<SInt64> {};
+
+template<>
+class YAMLSerializeTraits<UInt8> : public YAMLSerializeTraitsForBasicType<UInt8> {};
+
+template<>
+class YAMLSerializeTraits<SInt8> : public YAMLSerializeTraitsForBasicType<SInt8> {};
+
+template<>
+class YAMLSerializeTraits<char> : public YAMLSerializeTraitsForBasicType<char> {};
+
+template<>
+class YAMLSerializeTraits<bool> : public YAMLSerializeTraitsForBasicType<bool> {};
+
+template<>
+class YAMLSerializeTraits<UnityStr> : public YAMLSerializeTraitsBase<UnityStr >
+{
+public:
+
+ template<class TransferFunction> inline
+ static void Transfer (UnityStr& data, TransferFunction& transfer)
+ {
+ transfer.TransferStringData (data);
+ }
+
+ inline static void TransferStringToData (UnityStr& data, std::string &str)
+ {
+ data = str.c_str();
+ }
+
+ inline static bool IsBasicType ()
+ {
+ return true;
+ }
+};
+
+// Do not add this serialization function. All serialized strings should use UnityStr instead of std::string
+//template<class Traits, class Allocator>
+//class YAMLSerializeTraits<std::basic_string<char,Traits,Allocator> > : public YAMLSerializeTraitsBase<std::basic_string<char,Traits,Allocator> >
+
+template<class FirstClass, class SecondClass>
+class YAMLSerializeTraits<std::pair<FirstClass, SecondClass> > : public YAMLSerializeTraitsBase<std::pair<FirstClass, SecondClass> >
+{
+ public:
+
+
+ template<class TransferFunction> inline
+ static void Transfer (std::pair<FirstClass, SecondClass>& data, TransferFunction& transfer)
+ {
+ if (YAMLSerializeTraits<FirstClass>::IsBasicType())
+ transfer.TransferPair (data);
+ else
+ {
+ transfer.Transfer (data.first, "first");
+ transfer.Transfer (data.second, "second");
+ }
+ }
+};
+
+template<class FirstClass, class SecondClass, class Compare, class Allocator>
+class YAMLSerializeTraits<std::map<FirstClass, SecondClass, Compare, Allocator> > : public YAMLSerializeTraitsBase<std::map<FirstClass, SecondClass, Compare, Allocator> >
+{
+ public:
+
+ typedef std::map<FirstClass, SecondClass, Compare, Allocator> value_type;
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ AssertIf(transfer.IsRemapPPtrTransfer() && SerializeTraits<FirstClass>::MightContainPPtr() && transfer.IsReadingPPtr());
+ transfer.TransferSTLStyleMap (data);
+ }
+};
+
+template<class FirstClass, class SecondClass, class Compare, class Allocator>
+class YAMLSerializeTraits<std::multimap<FirstClass, SecondClass, Compare, Allocator> > : public YAMLSerializeTraitsBase<std::multimap<FirstClass, SecondClass, Compare, Allocator> >
+{
+ public:
+
+ typedef std::multimap<FirstClass, SecondClass, Compare, Allocator> value_type;
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ AssertIf(transfer.IsRemapPPtrTransfer() && SerializeTraits<FirstClass>::MightContainPPtr() && transfer.IsReadingPPtr());
+ transfer.TransferSTLStyleMap (data);
+ }
+};
+
+
+template<class T, class Compare, class Allocator>
+class YAMLSerializeTraits<std::set<T, Compare, Allocator> > : public YAMLSerializeTraitsBase<std::set<T, Compare, Allocator> >
+{
+ public:
+
+ typedef std::set<T, Compare, Allocator> value_type;
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ AssertIf(transfer.IsRemapPPtrTransfer() && transfer.IsReadingPPtr());
+ transfer.TransferSTLStyleSet (data);
+ }
+};
+
+
+template<class TransferFunction>
+void TransferYAMLPPtr (PPtr<Object> &data, TransferFunction& transfer);
+template<class TransferFunction>
+void TransferYAMLPPtr (ImmediatePtr<Object> &data, TransferFunction& transfer);
+
+template<class T>
+class YAMLSerializeTraits<PPtr<T> > : public YAMLSerializeTraitsBase<PPtr<T> >
+{
+ public:
+
+ template<class TransferFunction> inline
+ static void Transfer (PPtr<T>& data, TransferFunction& transfer)
+ {
+ TransferYAMLPPtr ((PPtr<Object>&)data, transfer);
+ }
+};
+
+template<class T>
+class YAMLSerializeTraits<ImmediatePtr<T> > : public YAMLSerializeTraitsBase<ImmediatePtr<T> >
+{
+ public:
+
+ template<class TransferFunction> inline
+ static void Transfer (ImmediatePtr<T>& data, TransferFunction& transfer)
+ {
+ TransferYAMLPPtr ((ImmediatePtr<Object>&)data, transfer);
+ }
+};
+
+template<>
+class YAMLSerializeTraits<UnityGUID> : public YAMLSerializeTraitsBase<UnityGUID>
+{
+ public:
+
+ template<class TransferFunction>
+ static void Transfer (UnityGUID& data, TransferFunction& transfer);
+
+ inline static bool IsBasicType ()
+ {
+ return true;
+ }
+};
+#endif
diff --git a/Runtime/Serialize/TransferFunctions/YAMLWrite.cpp b/Runtime/Serialize/TransferFunctions/YAMLWrite.cpp
new file mode 100644
index 0000000..ec2ea68
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/YAMLWrite.cpp
@@ -0,0 +1,183 @@
+#include "UnityPrefix.h"
+#include "YAMLWrite.h"
+#include "../CacheWrap.h"
+#include <string>
+
+void YAMLWrite::TransferStringToCurrentNode (const char* str)
+{
+ if (m_Error)
+ return;
+ int node = yaml_document_add_scalar(&m_Document, NULL, (yaml_char_t*)str, strlen(str), YAML_ANY_SCALAR_STYLE);
+ if (node)
+ m_CurrentNode = node;
+ else
+ m_Error = true;
+}
+
+int YAMLWrite::NewMapping ()
+{
+ int node = yaml_document_add_mapping(&m_Document, NULL, (m_MetaFlags.back() & kTransferUsingFlowMappingStyle)? YAML_FLOW_MAPPING_STYLE : YAML_ANY_MAPPING_STYLE);
+ if (node == 0)
+ m_Error = true;
+ return node;
+}
+
+int YAMLWrite::NewSequence ()
+{
+ int node = yaml_document_add_sequence(&m_Document, NULL, YAML_ANY_SEQUENCE_STYLE);
+ if (node == 0)
+ m_Error = true;
+ return node;
+}
+
+int YAMLWrite::GetNode ()
+{
+ if (m_CurrentNode == -1)
+ m_CurrentNode = NewMapping();
+ return m_CurrentNode;
+}
+
+void YAMLWrite::AppendToNode(int parentNode, const char* keyStr, int valueNode)
+{
+ yaml_node_t* parent = yaml_document_get_node(&m_Document, parentNode);
+ switch (parent->type)
+ {
+ case YAML_MAPPING_NODE:
+ {
+ int keyNode = yaml_document_add_scalar(&m_Document, NULL, (yaml_char_t*)keyStr, strlen(keyStr), YAML_ANY_SCALAR_STYLE);
+ if (keyNode == 0)
+ m_Error = true;
+ yaml_document_append_mapping_pair(&m_Document, parentNode, keyNode, valueNode);
+ }
+ break;
+
+ case YAML_SEQUENCE_NODE:
+ yaml_document_append_sequence_item(&m_Document, parentNode, valueNode);
+ break;
+
+ default:
+ ErrorString("Unexpected node type.");
+ }
+}
+
+int YAMLWrite::StringOutputHandler(void *data, unsigned char *buffer, size_t size)
+{
+ string* theString = reinterpret_cast<string*> (data);
+ theString->append( (char *) buffer, size);
+ return 1;
+}
+
+int YAMLWrite::CacheOutputHandler(void *data, unsigned char *buffer, size_t size)
+{
+ CachedWriter* cache = reinterpret_cast<CachedWriter*> (data);
+ cache->Write(buffer, size);
+ return 1;
+}
+
+void YAMLWrite::OutputToHandler (yaml_write_handler_t *handler, void *data)
+{
+ yaml_node_t *root = yaml_document_get_root_node (&m_Document);
+ if (root->type == YAML_MAPPING_NODE && root->data.mapping.pairs.start != root->data.mapping.pairs.top)
+ {
+ yaml_emitter_t emitter;
+ memset(&emitter, 0, sizeof(emitter));
+
+ if (!yaml_emitter_initialize (&emitter))
+ {
+ ErrorStringMsg ("Unable to write text file %s: yaml_emitter_initialize failed.", m_DebugFileName.c_str());
+ return;
+ }
+
+ yaml_emitter_set_output(&emitter, handler, data );
+ yaml_emitter_dump(&emitter, &m_Document);
+
+ if (emitter.error != YAML_NO_ERROR)
+ ErrorStringMsg ("Unable to write text file %s: %s.", m_DebugFileName.c_str(), emitter.problem);
+
+ yaml_emitter_delete(&emitter);
+ }
+}
+
+YAMLWrite::YAMLWrite (int flags, std::string *debugFileName)
+{
+ if (debugFileName)
+ m_DebugFileName = *debugFileName;
+
+ memset(&m_Document, 0, sizeof(m_Document));
+
+ m_CurrentNode = -1;
+ m_Flags = flags;
+ m_Error = false;
+ m_MetaFlags.push_back (0);
+ m_UserData = NULL;
+
+ if (!yaml_document_initialize(&m_Document, NULL, NULL, NULL, 1, 1))
+ {
+ ErrorStringMsg ("Unable to write text file %s: yaml_document_initialize failed.", m_DebugFileName.c_str());
+ m_Error = true;
+ }
+}
+
+YAMLWrite::~YAMLWrite()
+{
+ yaml_document_delete(&m_Document);
+}
+
+void YAMLWrite::OutputToCachedWriter (CachedWriter* writer)
+{
+ if (m_Error)
+ {
+ ErrorStringMsg ("Could not serialize text file %s because an error occured - we probably ran out of memory.", m_DebugFileName.c_str());
+ return;
+ }
+ OutputToHandler (CacheOutputHandler, reinterpret_cast<void *>(writer));
+}
+
+void YAMLWrite::OutputToString (std::string& str)
+{
+ if (m_Error)
+ {
+ ErrorStringMsg ("Could not serialize text file %s because an error occured - we probably ran out of memory.", m_DebugFileName.c_str());
+ return;
+ }
+
+ OutputToHandler (StringOutputHandler, reinterpret_cast<void *>(&str));
+}
+
+void YAMLWrite::SetVersion (int version)
+{
+ char valueStr[256];
+ snprintf(valueStr, 256, "%d", version);
+ int value = yaml_document_add_scalar(&m_Document, NULL, (yaml_char_t*)valueStr, strlen(valueStr), YAML_ANY_SCALAR_STYLE);
+
+ AppendToNode (GetNode(), "serializedVersion", value);
+}
+
+void YAMLWrite::BeginMetaGroup (std::string name)
+{
+ m_MetaParents.push_back (MetaParent());
+ m_MetaParents.back().node = GetNode ();
+ m_MetaParents.back().name = name;
+ m_CurrentNode = NewMapping ();
+}
+
+void YAMLWrite::EndMetaGroup ()
+{
+ AppendToNode (m_MetaParents.back().node, m_MetaParents.back().name.c_str(), m_CurrentNode);
+ m_CurrentNode = m_MetaParents.back().node;
+ m_MetaParents.pop_back();
+}
+
+void YAMLWrite::StartSequence ()
+{
+ m_CurrentNode = NewSequence();
+}
+
+void YAMLWrite::TransferTypelessData (unsigned size, void* data, int metaFlag)
+{
+ UnityStr dataString;
+ dataString.resize (size * 2);
+ BytesToHexString (data, size, &dataString[0]);
+ Transfer(dataString, "_typelessdata", metaFlag);
+}
+
diff --git a/Runtime/Serialize/TransferFunctions/YAMLWrite.h b/Runtime/Serialize/TransferFunctions/YAMLWrite.h
new file mode 100644
index 0000000..89c509c
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/YAMLWrite.h
@@ -0,0 +1,340 @@
+#ifndef YAMLWRITE_H
+#define YAMLWRITE_H
+
+#include "Runtime/Serialize/TransferFunctions/TransferBase.h"
+#include "YAMLSerializeTraits.h"
+#include "Editor/Src/Utility/YAMLNode.h"
+#include "External/yaml/include/yaml.h"
+#include "Runtime/Serialize/FloatStringConversion.h"
+
+class CachedWriter;
+struct YAMLConverterContext;
+
+class YAMLWrite : public TransferBase
+{
+private:
+
+ struct MetaParent
+ {
+ int node;
+ std::string name;
+ };
+
+ std::vector<MetaParent> m_MetaParents;
+ std::vector<int> m_MetaFlags;
+ yaml_document_t m_Document;
+ int m_CurrentNode;
+ bool m_Error;
+ std::string m_DebugFileName;
+
+ void TransferStringToCurrentNode (const char* str);
+ int NewMapping ();
+ int NewSequence ();
+ int GetNode ();
+ void AppendToNode(int parentNode, const char* keyStr, int valueNode);
+ static int StringOutputHandler(void *data, unsigned char *buffer, size_t size);
+ static int CacheOutputHandler(void *data, unsigned char *buffer, size_t size);
+
+ void OutputToHandler (yaml_write_handler_t *handler, void *data);
+
+public:
+
+ YAMLWrite (int flags, std::string *debugFileName = NULL);
+ ~YAMLWrite();
+
+ void OutputToCachedWriter (CachedWriter* writer);
+ void OutputToString (std::string& str);
+
+ // Sets the "version of the class currently transferred"
+ void SetVersion (int version);
+
+ bool HasError () { return m_Error; }
+ bool IsWriting () { return true; }
+ bool IsWritingPPtr () { return true; }
+ bool NeedsInstanceIDRemapping () { return m_Flags & kNeedsInstanceIDRemapping; }
+ bool AssetMetaDataOnly () { return m_Flags & kAssetMetaDataOnly; }
+ bool IsSerializingForGameRelease () { return false; }
+
+ void SetFlowMappingStyle (bool on);
+
+ yaml_document_t* GetDocument () { return &m_Document; }
+ int GetCurrentNodeIndex () { return m_CurrentNode; }
+
+ void PushMetaFlag (int flag) { m_MetaFlags.push_back(flag | m_MetaFlags.back());}
+ void PopMetaFlag () { m_MetaFlags.pop_back(); }
+ void AddMetaFlag(int mask) { m_MetaFlags.back() |= mask; }
+
+ void BeginMetaGroup (std::string name);
+ void EndMetaGroup ();
+ void StartSequence ();
+
+ template<class T>
+ void Transfer (T& data, const char* name, int metaFlag = 0);
+ template<class T>
+ void TransferWithTypeString (T& data, const char*, const char*, int metaFlag = 0);
+
+ void TransferTypeless (unsigned* value, const char* name, int metaFlag = 0)
+ {
+ Transfer(*value, name, metaFlag);
+ }
+
+ void TransferTypelessData (unsigned size, void* data, int metaFlag = 0);
+
+ template<class T>
+ void TransferBasicData (T& data);
+
+ template<class T>
+ void TransferPtr (bool, ReduceCopyData*){}
+
+ template<class T>
+ void TransferStringData (T& data);
+
+ template<class T>
+ void TransferSTLStyleArray (T& data, int metaFlag = 0);
+
+ template<class T>
+ void TransferSTLStyleMap (T& data, int metaFlag = 0);
+
+ template<class T>
+ void TransferSTLStyleSet (T& data, int metaFlag = 0);
+
+ template<class T>
+ void TransferPair (T& data, int metaFlag = 0, int parent = -1);
+};
+
+template<>
+inline void YAMLWrite::TransferBasicData<SInt64> (SInt64& data)
+{
+ char valueStr[17];
+ BytesToHexString (&data, sizeof(SInt64), valueStr);
+ valueStr[16] = '\0';
+ TransferStringToCurrentNode (valueStr);
+}
+
+template<>
+inline void YAMLWrite::TransferBasicData<UInt64> (UInt64& data)
+{
+ char valueStr[17];
+ BytesToHexString (&data, sizeof(UInt64), valueStr);
+ valueStr[16] = '\0';
+ TransferStringToCurrentNode (valueStr);
+}
+
+// This are the definitions of std::numeric_limits<>::max_digits10, which we cannot use
+// because it is only in the C++11 standard.
+const int kMaxFloatDigits = std::floor(std::numeric_limits<float>::digits * 3010.0/10000.0 + 2);
+const int kMaxDoubleDigits = std::floor(std::numeric_limits<double>::digits * 3010.0/10000.0 + 2);
+
+template<>
+inline void YAMLWrite::TransferBasicData<float> (float& data)
+{
+ char valueStr[64];
+ if (FloatToStringAccurate(data, valueStr, 64))
+ TransferStringToCurrentNode (valueStr);
+ else
+ TransferStringToCurrentNode ("error");
+}
+
+template<>
+inline void YAMLWrite::TransferBasicData<double> (double& data)
+{
+ char valueStr[64];
+ if (DoubleToStringAccurate (data, valueStr, 64))
+ TransferStringToCurrentNode (valueStr);
+ else
+ TransferStringToCurrentNode ("error");
+}
+
+template<>
+inline void YAMLWrite::TransferBasicData<char> (char& data)
+{
+ char valueStr[16];
+ snprintf(valueStr, 16, "%hhd", data);
+ TransferStringToCurrentNode (valueStr);
+}
+
+template<>
+inline void YAMLWrite::TransferBasicData<SInt8> (SInt8& data)
+{
+ char valueStr[16];
+ snprintf(valueStr, 16, "%hhd", data);
+ TransferStringToCurrentNode (valueStr);
+}
+
+template<>
+inline void YAMLWrite::TransferBasicData<UInt8> (UInt8& data)
+{
+ char valueStr[16];
+ snprintf(valueStr, 16, "%hhu", data);
+ TransferStringToCurrentNode (valueStr);
+}
+
+template<>
+inline void YAMLWrite::TransferBasicData<SInt32> (SInt32& data)
+{
+ char valueStr[16];
+ snprintf(valueStr, 16, "%d", data);
+ TransferStringToCurrentNode (valueStr);
+}
+
+template<>
+inline void YAMLWrite::TransferBasicData<UInt32> (UInt32& data)
+{
+ char valueStr[16];
+ snprintf(valueStr, 16, "%u", data);
+ TransferStringToCurrentNode (valueStr);
+}
+
+template<>
+inline void YAMLWrite::TransferBasicData<SInt16> (SInt16& data)
+{
+ char valueStr[16];
+ snprintf(valueStr, 16, "%hd", data);
+ TransferStringToCurrentNode (valueStr);
+}
+
+template<>
+inline void YAMLWrite::TransferBasicData<UInt16> (UInt16& data)
+{
+ char valueStr[16];
+ snprintf(valueStr, 16, "%hu", data);
+ TransferStringToCurrentNode (valueStr);
+}
+
+template<class T>
+inline void YAMLWrite::TransferStringData (T& data)
+{
+ TransferStringToCurrentNode (data.c_str());
+}
+
+template<class T>
+void YAMLWrite::Transfer (T& data, const char* _name, int metaFlag)
+{
+ if (m_Error)
+ return;
+
+ if (metaFlag & kIgnoreInMetaFiles)
+ return;
+
+ std::string name = YAMLSerializeTraits<T>::ParseName(_name, AssetMetaDataOnly());
+
+ PushMetaFlag(0);
+
+ int parent = GetNode();
+ m_CurrentNode = -1;
+
+ YAMLSerializeTraits<T>::Transfer (data, *this);
+
+ if (m_CurrentNode != -1)
+ AppendToNode (parent, name.c_str(), m_CurrentNode);
+
+ PopMetaFlag();
+
+ m_CurrentNode = parent;
+}
+
+template<class T>
+void YAMLWrite::TransferWithTypeString (T& data, const char* name, const char*, int metaFlag)
+{
+ Transfer(data, name, metaFlag);
+}
+
+
+template<class T>
+void YAMLWrite::TransferSTLStyleArray (T& data, int metaFlag)
+{
+ typedef typename NonConstContainerValueType<T>::value_type non_const_value_type;
+ if (YAMLSerializeTraits<non_const_value_type>::ShouldSerializeArrayAsCompactString())
+ {
+#if UNITY_BIG_ENDIAN
+#error "Needs swapping to be implemented to work on big endian platforms!"
+#endif
+ std::string str;
+ size_t numElements = data.size();
+ size_t numBytes = numElements * sizeof(non_const_value_type);
+ str.resize (numBytes*2);
+
+ typename T::iterator dataIterator = data.begin ();
+ for (size_t i=0; i<numElements; i++)
+ {
+ BytesToHexString ((void*)&*dataIterator, sizeof(non_const_value_type), &str[i*2*sizeof(non_const_value_type)]);
+ ++dataIterator;
+ }
+
+ TransferStringData (str);
+ }
+ else
+ {
+ m_CurrentNode = NewSequence ();
+
+ typename T::iterator i = data.begin ();
+ typename T::iterator end = data.end ();
+ while (i != end)
+ {
+ Transfer (*i, "data", metaFlag);
+ ++i;
+ }
+ }
+}
+
+template<class T>
+void YAMLWrite::TransferSTLStyleMap (T& data, int metaFlag)
+{
+ m_CurrentNode = NewMapping ();
+
+ typename T::iterator i = data.begin ();
+ typename T::iterator end = data.end ();
+
+
+ // maps value_type is: pair<const First, Second>
+ // So we have to write to maps non-const value type
+ typedef typename NonConstContainerValueType<T>::value_type non_const_value_type;
+ typedef typename non_const_value_type::first_type first_type;
+ while (i != end)
+ {
+ non_const_value_type& p = (non_const_value_type&)(*i);
+ if (YAMLSerializeTraits<first_type>::IsBasicType())
+ TransferPair (p, metaFlag, m_CurrentNode);
+ else
+ Transfer (p, "data", metaFlag);
+ i++;
+ }
+}
+
+template<class T>
+void YAMLWrite::TransferSTLStyleSet (T& data, int metaFlag)
+{
+ m_CurrentNode = NewSequence ();
+
+ typename T::iterator i = data.begin ();
+ typename T::iterator end = data.end ();
+
+ typedef typename NonConstContainerValueType<T>::value_type non_const_value_type;
+ while (i != end)
+ {
+ non_const_value_type& p = (non_const_value_type&)(*i);
+ Transfer (p, "data", metaFlag);
+ ++i;
+ }
+}
+
+template<class T>
+void YAMLWrite::TransferPair (T& data, int /*metaFlag*/, int parent)
+{
+ typedef typename T::first_type first_type;
+ typedef typename T::second_type second_type;
+ if (parent == -1)
+ parent = NewMapping ();
+
+ m_CurrentNode = -1;
+ YAMLSerializeTraits<first_type>::Transfer (data.first, *this);
+ int key = m_CurrentNode;
+
+ m_CurrentNode = -1;
+ YAMLSerializeTraits<second_type>::Transfer (data.second, *this);
+
+ yaml_document_append_mapping_pair(&m_Document, parent, key, m_CurrentNode);
+
+ m_CurrentNode = parent;
+}
+#endif
diff --git a/Runtime/Serialize/TransferFunctions/YAMLWriteTests.cpp b/Runtime/Serialize/TransferFunctions/YAMLWriteTests.cpp
new file mode 100644
index 0000000..b0596d6
--- /dev/null
+++ b/Runtime/Serialize/TransferFunctions/YAMLWriteTests.cpp
@@ -0,0 +1,58 @@
+#include "UnityPrefix.h"
+
+#ifdef ENABLE_UNIT_TESTS
+
+#include "YAMLWrite.h"
+#include "Runtime/Testing/Testing.h"
+#include <map>
+
+SUITE (YAMLWriteTests)
+{
+ struct Fixture
+ {
+ YAMLWrite instanceUnderTest;
+ Fixture ()
+ : instanceUnderTest (0) {}
+ };
+
+ #define ROOT (yaml_document_get_root_node (instanceUnderTest.GetDocument ()))
+ #define FIRST_KEY_OF(node) (yaml_document_get_node (instanceUnderTest.GetDocument (), node->data.mapping.pairs.start->key))
+ #define FIRST_VALUE_OF(node) (yaml_document_get_node (instanceUnderTest.GetDocument (), node->data.mapping.pairs.start->value))
+
+ TEST_FIXTURE (Fixture, TransferSTLStyleMap_WithEmptyMap_ProducesMappingNode)
+ {
+ std::map<float, UnityStr> testMap;
+ instanceUnderTest.TransferSTLStyleMap (testMap);
+ CHECK (ROOT->type == YAML_MAPPING_NODE);
+ }
+
+ TEST_FIXTURE (Fixture, TransferSTLStyleMap_WithComplexKey_WritesDataChild)
+ {
+ // Arrange.
+ std::map<PPtr<Object>, UnityStr> testMap;
+ testMap[PPtr<Object> ()] = "bar";
+
+ // Act.
+ instanceUnderTest.TransferSTLStyleMap (testMap);
+
+ // Assert.
+ CHECK (FIRST_KEY_OF (ROOT)->type == YAML_SCALAR_NODE);
+ CHECK (strcmp ((const char*) FIRST_KEY_OF (ROOT)->data.scalar.value, "data") == 0);
+ }
+
+ TEST_FIXTURE (Fixture, TransferSTLStyleMap_WithBasicTypeKey_DoesNotWriteDataChild)
+ {
+ // Arrange.
+ std::map<int, UnityStr> testMap;
+ testMap[1234] = "bar";
+
+ // Act.
+ instanceUnderTest.TransferSTLStyleMap (testMap);
+
+ // Assert.
+ CHECK (FIRST_KEY_OF (ROOT)->type == YAML_SCALAR_NODE);
+ CHECK (strcmp ((const char*) FIRST_KEY_OF (ROOT)->data.scalar.value, "1234") == 0);
+ }
+}
+
+#endif // ENABLE_UNIT_TESTS
diff --git a/Runtime/Serialize/TransferUtility.cpp b/Runtime/Serialize/TransferUtility.cpp
new file mode 100644
index 0000000..e98f6c8
--- /dev/null
+++ b/Runtime/Serialize/TransferUtility.cpp
@@ -0,0 +1,461 @@
+#include "UnityPrefix.h"
+#include "TransferUtility.h"
+#include "TypeTree.h"
+#include "IterateTypeTree.h"
+#include "SwapEndianBytes.h"
+#include "FileCache.h"
+#include "CacheWrap.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+
+#define DEBUG_PRINT_WALK 0
+
+using namespace std;
+
+#if UNITY_EDITOR
+
+void CountVariables (const TypeTree& typeTree, UInt8** data, int* variableCount);
+
+std::string ExtractPPtrClassName (const TypeTree& typeTree)
+{
+ return ExtractPPtrClassName(typeTree.m_Type);
+}
+
+std::string ExtractMonoPPtrClassName (const TypeTree& typeTree)
+{
+ return ExtractMonoPPtrClassName(typeTree.m_Type);
+}
+
+std::string ExtractPPtrClassName (const std::string& typeName)
+{
+ if (typeName.size () >= 6 && typeName.find ("PPtr<") == 0)
+ {
+ if (typeName[5] == '$')
+ return std::string ();
+ else
+ {
+ string className (typeName.begin() + 5, typeName.end() - 1);
+ return className;
+ }
+ }
+ else
+ return std::string ();
+}
+
+std::string ExtractMonoPPtrClassName (const std::string& typeName)
+{
+ if (typeName.size () >= 7 && typeName.find ("PPtr<$") == 0)
+ {
+ string className (typeName.begin() + 6, typeName.end() - 1);
+ return className;
+ }
+ else
+ return std::string ();
+}
+
+// Walk through typetree and data to find the bytePosition and variablePosition.
+void WalkTypeTree (const TypeTree& typeTree, const UInt8* data, int* bytePosition)
+{
+ AssertIf (bytePosition == NULL);
+
+#if DEBUG_PRINT_WALK
+ const TypeTree* parent = &typeTree;
+ while (parent->m_Father)
+ {
+ parent = parent->m_Father;
+ printf_console("\t");
+ }
+
+ printf_console("%s (%s) position: %d\n", typeTree.m_Type.c_str(), typeTree.m_Name.c_str(), *bytePosition);
+#endif
+
+ AssertIf((typeTree.m_ByteSize != -1 && ((typeTree.m_MetaFlag & kAnyChildUsesAlignBytesFlag) == 0 || typeTree.m_Children.empty())) != (typeTree.m_ByteSize != -1 && (typeTree.m_MetaFlag & kAnyChildUsesAlignBytesFlag) == 0));
+
+ /// During the 2.1 beta we had a bug that generated kAnyChildUsesAlignBytesFlag incorrectly,
+ /// this was only available in development builds thus we require the (|| typeTree.m_Children.empty())
+ bool hasBasicTypeSize = typeTree.m_ByteSize != -1 && (typeTree.m_MetaFlag & kAnyChildUsesAlignBytesFlag) == 0 || typeTree.m_Children.empty();
+
+ if (hasBasicTypeSize)
+ {
+ AssertIf (typeTree.m_ByteSize == -1);
+ *bytePosition += typeTree.m_ByteSize;
+ }
+ else if (typeTree.m_IsArray)
+ {
+ // First child in an array is the size
+ // Second child is the homogenous type of the array
+ AssertIf (typeTree.m_Children.front ().m_Type != SerializeTraits<SInt32>::GetTypeString (NULL));
+ AssertIf (typeTree.m_Children.front ().m_Name != "size");
+ AssertIf (typeTree.m_Children.size () != 2);
+
+ SInt32 arraySize, i;
+
+ arraySize = *reinterpret_cast<const SInt32*> (&data[*bytePosition]);
+
+#if DEBUG_PRINT_WALK
+ printf_console("Array Size %d position: %d\n", arraySize, *bytePosition);
+#endif
+
+ *bytePosition += sizeof (arraySize);
+
+ const TypeTree& elementTypeTree = typeTree.m_Children.back ();
+
+ // If the bytesize is known we can simply skip the recursive loop
+ if (elementTypeTree.m_ByteSize != -1 && (elementTypeTree.m_MetaFlag & (kAlignBytesFlag | kAnyChildUsesAlignBytesFlag)) == 0)
+ *bytePosition += arraySize * elementTypeTree.m_ByteSize;
+ // Otherwise recursively Walk element typetree
+ else
+ {
+ for (i=0;i<arraySize;i++)
+ WalkTypeTree (elementTypeTree, data, bytePosition);
+ }
+ }
+ else
+ {
+ AssertIf (typeTree.m_Children.empty ());
+
+ TypeTree::TypeTreeList::const_iterator i;
+ for (i = typeTree.m_Children.begin (); i != typeTree.m_Children.end ();++i)
+ WalkTypeTree (*i, data, bytePosition);
+ }
+
+ if (typeTree.m_MetaFlag & kAlignBytesFlag)
+ {
+#if DEBUG_PRINT_WALK
+ printf_console("Align %d %d", *bytePosition, Align4(*bytePosition));
+#endif
+ *bytePosition = Align4(*bytePosition);
+ }
+}
+
+struct ByteSwapGenericIterator
+{
+ bool operator () (const TypeTree& typeTree, dynamic_array<UInt8>& data, int bytePosition)
+ {
+ if (typeTree.IsBasicDataType ())
+ {
+ if (typeTree.m_ByteSize == 4)
+ {
+ // Color does not get byteswapped
+ if (typeTree.m_Father->m_Type != "ColorRGBA")
+ SwapEndianBytes(*reinterpret_cast<UInt32*> (&data[bytePosition]));
+ }
+ else if (typeTree.m_ByteSize == 2)
+ SwapEndianBytes(*reinterpret_cast<UInt16*> (&data[bytePosition]));
+ else if (typeTree.m_ByteSize == 8)
+ SwapEndianBytes(*reinterpret_cast<double*> (&data[bytePosition]));
+ else if (typeTree.m_ByteSize != 1)
+ {
+ AssertString (Format("Unsupported data type when byteswapping %s", typeTree.m_Type.c_str()));
+ }
+
+ if (typeTree.m_Type == "TypelessData")
+ {
+ AssertString ("It is not possible to use Generic byteswap for typeless arrays!");
+ }
+ }
+ return true;
+ }
+};
+
+void ByteSwapGeneric (const TypeTree& typeTree, dynamic_array<UInt8>& data)
+{
+ ByteSwapGenericIterator functor;
+ IterateTypeTree (typeTree, data, functor);
+}
+
+SInt32 CalculateByteSize(const TypeTree& type, const UInt8* data)
+{
+ int position = 0;
+ WalkTypeTree (type, data, &position);
+ return position;
+}
+
+#endif // UNITY_EDITOR
+
+#if !UNITY_EXTERNAL_TOOL
+
+int FindTypeTreeSeperator (const char* in)
+{
+ const char* c = in;
+ while (*c != '.' && *c != '\0')
+ c++;
+ return c - in;
+}
+
+const TypeTree* FindAttributeInTypeTreeNoArrays (const TypeTree& typeTree, const char* path)
+{
+ int seperator = FindTypeTreeSeperator (path);
+ // Search all typetree children for a name that is the same as the string path with length seperator
+ for (TypeTree::const_iterator i=typeTree.begin ();i != typeTree.end ();++i)
+ {
+ // Early out if size is not the same
+ if (i->m_Name.size () != seperator)
+ continue;
+
+ // continue if the name isn't the same
+ TypeTreeString::const_iterator n = i->m_Name.begin ();
+ int j;
+ for (j=0;j<seperator;j++,n++)
+ {
+ if (path[j] != *n)
+ break;
+ }
+ if (j != seperator)
+ continue;
+
+ // We found the attribute we were searching for
+ if (path[seperator] == '\0')
+ return &*i;
+ // Recursively find in the children
+ else
+ return FindAttributeInTypeTreeNoArrays (*i, path + seperator + 1);
+ }
+ return NULL;
+}
+
+void GenerateTypeTree (Object& object, TypeTree* typeTree, int options)
+{
+ AssertIf (typeTree == NULL);
+ *typeTree = TypeTree ();
+ ProxyTransfer proxy (*typeTree, options, &object, Object::ClassIDToRTTI (object.GetClassID ())->size);
+ object.VirtualRedirectTransfer (proxy);
+}
+
+template<bool swapEndian>
+static inline void WriteObjectToVector (Object& object, dynamic_array<UInt8>* data, int options)
+{
+ Assert (data != NULL);
+ data->clear ();
+
+ MemoryCacheWriter memoryCache (*data);
+ StreamedBinaryWrite<swapEndian> writeStream;
+ CachedWriter& writeCache = writeStream.Init (options, BuildTargetSelection::NoTarget());
+
+ writeCache.InitWrite (memoryCache);
+ object.VirtualRedirectTransfer (writeStream);
+
+ if (!writeCache.CompleteWriting () || writeCache.GetPosition() != data->size ())
+ ErrorString ("Error while writing serialized data.");
+}
+
+void WriteObjectToVector (Object& object, dynamic_array<UInt8>* data, int options)
+{
+ WriteObjectToVector<false> (object, data, options);
+}
+
+template<bool swapEndian>
+void ReadObjectFromVector (Object* object, const dynamic_array<UInt8>& data, int options)
+{
+ Assert (object != NULL);
+
+ MemoryCacheReader memoryCache (const_cast<dynamic_array<UInt8>&> (data));
+ StreamedBinaryRead<swapEndian> readStream;
+ CachedReader& readCache = readStream.Init (options);
+ readCache.InitRead (memoryCache, 0, data.size ());
+
+ object->VirtualRedirectTransfer (readStream);
+ unsigned position = readCache.End ();
+
+ // we read up that object - no need to call Reset as we constructed it fully
+ object->HackSetResetWasCalled();
+
+ if (position > (int) data.size ())
+ ErrorString ("Error while reading serialized data.");
+}
+
+void ReadObjectFromVector (Object* object, const dynamic_array<UInt8>& data, int options)
+{
+ ReadObjectFromVector<false> (object, data, options);
+}
+
+#if SUPPORT_TEXT_SERIALIZATION
+
+string WriteObjectToString (Object& object, int options)
+{
+ YAMLWrite write (options);
+ object.VirtualRedirectTransfer (write);
+
+ string result;
+ write.OutputToString(result);
+
+ return result;
+}
+
+void ReadObjectFromString (Object* object, string& string, int options)
+{
+ Assert (object != NULL);
+ YAMLRead read (string.c_str (), string.length (), options);
+ object->VirtualRedirectTransfer (read);
+}
+
+#endif // SUPPORT_TEXT_SERIALIZATION
+
+#endif // !UNITY_EXTERNAL_TOOL
+
+#if UNITY_EDITOR
+
+void ReadObjectFromVector (Object* object, const dynamic_array<UInt8>& data, const TypeTree& typeTree, int options)
+{
+ Assert (object != NULL);
+
+ MemoryCacheReader memoryCache (const_cast<dynamic_array<UInt8>&> (data));
+ SafeBinaryRead readStream;
+ CachedReader& readCache = readStream.Init (typeTree, 0, data.size (), options);
+ readCache.InitRead (memoryCache, 0, data.size ());
+
+ object->VirtualRedirectTransfer (readStream);
+ readCache.End ();
+
+ // we will read up that object - no need to call Reset as we will construct it fully
+ object->HackSetResetWasCalled();
+}
+
+void WriteObjectToVectorSwapEndian (Object& object, dynamic_array<UInt8>* data, int options)
+{
+ WriteObjectToVector<true> (object, data, options);
+}
+
+void CountVariables (const TypeTree& typeTree, UInt8** data, int* variableCount)
+{
+ if (typeTree.m_IsArray)
+ {
+ AssertIf (typeTree.m_Children.size () != 2);
+ AssertIf (typeTree.m_Children.front ().m_Type != "SInt32");
+
+ SInt32 arraySize = *reinterpret_cast<SInt32*> (*data);
+ *data += sizeof (SInt32);
+ *variableCount += 1;
+
+ for (int i=0;i<arraySize;i++)
+ CountVariables (typeTree.m_Children.back (), data, variableCount);
+ }
+ else if (!typeTree.IsBasicDataType ())
+ {
+ for (TypeTree::const_iterator i=typeTree.m_Children.begin ();i != typeTree.m_Children.end ();i++)
+ CountVariables (*i, data, variableCount);
+ }
+ else
+ {
+ *variableCount += 1;
+ *data += typeTree.m_ByteSize;
+ }
+}
+
+int CountTypeTreeVariables (const TypeTree& typeTree)
+{
+ if (typeTree.m_Children.empty ())
+ return typeTree.m_Index + 1;
+ else
+ return CountTypeTreeVariables (*typeTree.m_Children.rbegin ());
+}
+
+#endif // UNITY_EDITOR
+
+#if !UNITY_EXTERNAL_TOOL
+class IDCollectorFunctor : public GenerateIDFunctor
+{
+ set<SInt32>* m_IDs;
+
+public:
+
+ IDCollectorFunctor (set<SInt32>* ptrs)
+ {
+ m_IDs = ptrs;
+ }
+ virtual ~IDCollectorFunctor () {}
+
+
+ virtual SInt32 GenerateInstanceID (SInt32 oldInstanceID, TransferMetaFlags metaFlag = kNoTransferFlags)
+ {
+ // Only strong pptrs can insert new ids
+ if ((metaFlag & kStrongPPtrMask) == 0)
+ return oldInstanceID;
+
+ Object* object = PPtr<Object> (oldInstanceID);
+ if (object == NULL)
+ return oldInstanceID;
+
+ // Already Inserted?
+ if (!m_IDs->insert (oldInstanceID).second)
+ return oldInstanceID;
+
+ RemapPPtrTransfer transferFunction (0, false);
+ transferFunction.SetGenerateIDFunctor (this);
+ object->VirtualRedirectTransfer (transferFunction);
+
+ return oldInstanceID;
+ }
+};
+
+void CollectPPtrs (Object& object, set<SInt32>* collectedPtrs)
+{
+ IDCollectorFunctor collectFunctor (collectedPtrs);
+ collectFunctor.GenerateInstanceID (object.GetInstanceID (), kStrongPPtrMask);
+}
+
+void CollectPPtrs (Object& object, vector<Object*>& collectedObjects)
+{
+ Assert(collectedObjects.empty());
+ set<SInt32> output;
+ CollectPPtrs (object, &output);
+
+ for (set<SInt32>::iterator i=output.begin();i != output.end();i++)
+ {
+ Object* obj = dynamic_instanceID_cast<Object*> (*i);
+ if (obj)
+ collectedObjects.push_back(obj);
+ }
+
+}
+
+/// We should use a serialize remapper which clears strong pptrs instead.
+void CopySerialized(Object& src, Object& dst)
+{
+ dynamic_array<UInt8> data(kMemTempAlloc);
+
+ if (src.GetClassID() != dst.GetClassID())
+ {
+ ErrorString("Source and Destination Types do not match");
+ return;
+ }
+
+ if (src.GetNeedsPerObjectTypeTree())
+ {
+ // Verify that the typetree matches, otherwise when the script pointer changes comparing the data will read out of bounds.
+ TypeTree srcTypeTree;
+ TypeTree dstTypeTree;
+ GenerateTypeTree (src, &srcTypeTree, kSerializeForPrefabSystem);
+ GenerateTypeTree (dst, &dstTypeTree, kSerializeForPrefabSystem);
+
+ if (!IsStreamedBinaryCompatbile(srcTypeTree, dstTypeTree))
+ {
+ ErrorString("Source and Destination Types do not match");
+ return;
+ }
+ }
+
+
+ WriteObjectToVector(src, &data, kSerializeForPrefabSystem);
+ ReadObjectFromVector(&dst, data, kSerializeForPrefabSystem);
+
+#if UNITY_EDITOR
+ dst.CloneAdditionalEditorProperties(src);
+#endif
+
+ // we copied that object - no need to call Reset as we constructed it fully
+ dst.HackSetResetWasCalled();
+
+ dst.AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+
+ dst.SetDirty();
+}
+
+void CompletedReadObjectFromVector (Object& object)
+{
+ object.CheckConsistency ();
+ object.AwakeFromLoad (kDefaultAwakeFromLoad);
+ object.SetDirty ();
+}
+
+#endif // !UNITY_EXTERNAL_TOOL
diff --git a/Runtime/Serialize/TransferUtility.h b/Runtime/Serialize/TransferUtility.h
new file mode 100644
index 0000000..6c70448
--- /dev/null
+++ b/Runtime/Serialize/TransferUtility.h
@@ -0,0 +1,100 @@
+#ifndef TRANSFERUTILITY_H
+#define TRANSFERUTILITY_H
+
+#include "Runtime/Utilities/dynamic_array.h"
+
+class TypeTree;
+class Object;
+class dynamic_bitset;
+
+#if !UNITY_EXTERNAL_TOOL
+
+/// Generates a TypeTree by serializing object using the ProxyTransfer
+void GenerateTypeTree (Object& object, TypeTree* typeTree, int options = 0);
+
+const TypeTree* FindAttributeInTypeTreeNoArrays (const TypeTree& typeTree, const char* propertyPath);
+
+/// Serializes an object using to a memory buffer as binary data.
+///
+/// @note No type information will be written to the buffer. To read the data,
+/// a matching version of the serialization code must be used.
+void WriteObjectToVector (Object& object, dynamic_array<UInt8>* data, int options = 0);
+
+/// Unserializes an object from a memory buffer using binary serialization without type trees
+/// (meaning the structure of the serialized data must match exactly with what the runtime
+/// classes currently look like).
+///
+/// @note Dont forget calling CheckConsistency (), AwakeFromLoad (), SetDirty () after calling this method.
+void ReadObjectFromVector (Object* object, const dynamic_array<UInt8>& data, int options = 0);
+
+#if SUPPORT_TEXT_SERIALIZATION
+
+/// Serialize the given object as text and return the resulting string.
+std::string WriteObjectToString (Object& object, int options = 0);
+
+///
+void ReadObjectFromString (Object* object, std::string& string, int options = 0);
+
+#endif // SUPPORT_TEXT_SERIALIZATION
+
+/// Dont forget calling CheckConsistency (), AwakeFromLoad (), SetDirty () after calling ReadObjectFromVector
+
+/// Call this after using CompletedReadObjectFromVector
+void CompletedReadObjectFromVector (Object& object);
+
+/// Collects the island of ptrs starting with object.
+/// the collectedPtrs set should in most cases be empty on calling since when
+/// a ptr is already inserted it will not insert his ptrs to the set
+void CollectPPtrs (Object& object, std::set<SInt32>* collectedPtrs);
+void CollectPPtrs (Object& object, std::vector<Object*>& collectedObjects);
+
+/// Copies all values from one object to another!
+/// - does call set dirty and awake
+/// - no pptr remapping or deep copying is done here!
+void CopySerialized(Object& src, Object& dst);
+
+int FindTypeTreeSeperator (const char* in);
+
+#endif // !UNITY_EXTERNAL_TOOL
+
+
+#if UNITY_EDITOR
+
+/// Unserializes raw data and a typeTree of the data.
+/// The serialized raw data and typeTree can be serialized in a different format.
+/// The serializion code will try to match variables and convert data if possible
+/// override can be NULL. If it is defined, only variables that are set to be overridden will be written to the object.
+/// Every variables override is determined using override[variableTypeTree.m_Index]
+/// where variableTypeTree is some child of a variable in typeTree
+void ReadObjectFromVector (Object* object, const dynamic_array<UInt8>& data, const TypeTree& typeTree, int options = 0);
+
+void WriteObjectToVectorSwapEndian (Object& object, dynamic_array<UInt8>* data, int options = 0);
+
+// Counts the variables that are contained in a typeTree
+int CountTypeTreeVariables (const TypeTree& typeTree);
+
+/// Walks over a typeTree, data pair moving the byteposition while going along.
+/// The start position is data + *bytePosition
+/// Takes into account arrays.
+void WalkTypeTree (const TypeTree& typeTree, const UInt8* data, int* bytePosition);
+
+/// CalculateByteSize from type and data
+SInt32 CalculateByteSize(const TypeTree& type, const UInt8* data);
+
+/// Byteswaps a data array with typetree.
+/// * This will not work correctly if the data contains typeless data transfer!
+void ByteSwapGeneric (const TypeTree& typeTree, dynamic_array<UInt8>& data);
+
+/// Returns the class name part of the PPtr reference.
+/// eg. returns "Transform" for "PPtr<Transform>"
+/// For Mono classes it returns ""
+std::string ExtractPPtrClassName (const TypeTree& typeTree);
+std::string ExtractPPtrClassName (const std::string& typeTree);
+
+/// Returns the class name of the pptr.
+std::string ExtractMonoPPtrClassName (const TypeTree& typeTree);
+std::string ExtractMonoPPtrClassName (const std::string& typeName);
+
+#endif // UNITY_EDITOR
+
+#endif
diff --git a/Runtime/Serialize/TypeTree.cpp b/Runtime/Serialize/TypeTree.cpp
new file mode 100644
index 0000000..d2e6778
--- /dev/null
+++ b/Runtime/Serialize/TypeTree.cpp
@@ -0,0 +1,531 @@
+#include "UnityPrefix.h"
+#include "TypeTree.h"
+#include <iostream>
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Utilities/Word.h"
+#include "SwapEndianBytes.h"
+#include "SerializationMetaFlags.h"
+#include "SerializeTraits.h"
+#include "Configuration/UnityConfigure.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/AssetPipeline/MdFourGenerator.h"
+#endif
+
+#if ENABLE_SECURITY
+#define TEST_LEN(x) if (iterator + sizeof(x) > end) \
+{\
+ return false; \
+}
+#else
+#define TEST_LEN(x)
+#endif
+
+using namespace std;
+
+static void RecalculateTypeTreeByteSize (TypeTree& typetree, int options);
+static void RecalculateTypeTreeByteSize (TypeTree& typetree, int* typePosition, int options);
+
+
+static void DeprecatedConvertUnity43BetaIntegerTypeNames (TypeTreeString& type)
+{
+ const char* rawText = type.c_str ();
+ if (rawText[0] != 'S' && rawText[0] != 'U')
+ return;
+
+ if (strcmp (rawText, "SInt32") == 0)
+ type = SerializeTraits<SInt32>::GetTypeString ();
+ else if (strcmp (rawText, "UInt32") == 0)
+ type = SerializeTraits<UInt32>::GetTypeString ();
+}
+
+
+TypeTree::TypeTree ()
+{
+ m_Father = NULL;
+ m_Index = -1;
+ m_ByteOffset = -1;
+ m_IsArray = false;
+ m_Version = 1;
+ m_MetaFlag = kNoTransferFlags;
+ m_DirectPtr = NULL;
+ m_ByteSize = -1;
+}
+
+TypeTree::TypeTree (const std::string& name, const std::string& type, SInt32 size)
+{
+ m_Father = NULL;
+ m_Index = -1;
+ m_ByteOffset = -1;
+ m_IsArray = false;
+ m_Version = 1;
+ m_MetaFlag = kNoTransferFlags;
+ m_DirectPtr = NULL;
+
+ m_Type = type;
+ m_Name = name;
+ m_ByteSize = size;
+}
+
+void TypeTree::DebugPrint (string& buffer, int level) const
+{
+ int i;
+ for (i=0;i<level;i++)
+ buffer += "\t";
+ buffer += m_Name.c_str();
+ buffer += " Type:";
+ buffer += m_Type.c_str();
+ buffer += " ByteSize:" + IntToString (m_ByteSize);
+ buffer += " MetaFlag:" + IntToString (m_MetaFlag);
+ if (m_IsArray)
+ buffer += " IsArray";
+ buffer += "\n";
+
+ TypeTree::TypeTreeList::const_iterator iter;
+ for (iter = m_Children.begin ();iter != m_Children.end ();++iter)
+ iter->DebugPrint (buffer, level + 1);
+}
+
+void GetTypePath (const TypeTree* type, std::string& s)
+{
+ if (type != NULL)
+ {
+ if (type->m_Father != NULL)
+ {
+ s += " -> ";
+ GetTypePath (type->m_Father, s);
+ }
+ s += type->m_Name.c_str();
+ s += '(';
+ s += type->m_Type.c_str();
+ s += ") ";
+ }
+}
+
+void TypeTree::operator = (const TypeTree& typeTree)
+{
+ m_Children = typeTree.m_Children;
+ for (iterator i = begin ();i != end ();i++)
+ i->m_Father = this;
+ m_Type = typeTree.m_Type;
+ m_Name = typeTree.m_Name;
+ m_ByteSize = typeTree.m_ByteSize;
+ m_Index = typeTree.m_Index;
+ m_IsArray = typeTree.m_IsArray;
+ m_Version = typeTree.m_Version;
+ m_MetaFlag = typeTree.m_MetaFlag;
+}
+/*
+bool TypeTree::operator == (const TypeTree& t) const
+{
+ if (m_ByteSize != t.m_ByteSize || m_Name != t.m_Name || m_Type != t.m_Type || m_Version != t.m_Version)
+ return false;
+ return m_Children == t.m_Children;
+}
+*/
+
+bool IsStreamedBinaryCompatbile (const TypeTree& lhs, const TypeTree& rhs)
+{
+ if (lhs.m_ByteSize != rhs.m_ByteSize || lhs.m_Name != rhs.m_Name || lhs.m_Type != rhs.m_Type || lhs.m_Version != rhs.m_Version)
+ return false;
+ if ((lhs.m_MetaFlag & kAlignBytesFlag) != (rhs.m_MetaFlag & kAlignBytesFlag))
+ return false;
+
+ if (lhs.m_Children.size() != rhs.m_Children.size())
+ return false;
+
+ TypeTree::const_iterator i, k;
+ for (i=lhs.begin(), k=rhs.begin();i != lhs.end();i++,k++)
+ {
+ if (!IsStreamedBinaryCompatbile(*i, *k))
+ return false;
+ }
+
+ return true;
+}
+
+bool IsStreamedBinaryCompatbileAndIndices (const TypeTree& lhs, const TypeTree& rhs)
+{
+ if (lhs.m_ByteSize != rhs.m_ByteSize || lhs.m_Name != rhs.m_Name || lhs.m_Type != rhs.m_Type || lhs.m_Version != rhs.m_Version || lhs.m_Index != rhs.m_Index)
+ return false;
+ if ((lhs.m_MetaFlag & kAlignBytesFlag) != (rhs.m_MetaFlag & kAlignBytesFlag))
+ return false;
+
+ if (lhs.m_Children.size() != rhs.m_Children.size())
+ return false;
+
+ TypeTree::const_iterator i, k;
+ for (i=lhs.begin(), k=rhs.begin();i != lhs.end();i++,k++)
+ {
+ if (!IsStreamedBinaryCompatbileAndIndices(*i, *k))
+ return false;
+ }
+
+ return true;
+}
+
+void AppendTypeTree (TypeTree& typeTree, TypeTree::iterator begin, TypeTree::iterator end)
+{
+ Assert(typeTree.m_Father == NULL);
+ typeTree.m_Children.insert (typeTree.end (), begin, end);
+ RecalculateTypeTreeByteSize( typeTree, 0);
+}
+
+void AppendTypeTree (TypeTree& typeTree, const TypeTree& typeTreeToAdd)
+{
+ Assert(typeTree.m_Father == NULL);
+ typeTree.m_Children.push_back(typeTreeToAdd);
+ RecalculateTypeTreeByteSize( typeTree, 0);
+}
+
+void RemoveFromTypeTree (TypeTree& typeTree, TypeTree::iterator begin, TypeTree::iterator end)
+{
+ Assert(typeTree.m_Father == NULL);
+ typeTree.m_Children.erase (begin, end);
+ RecalculateTypeTreeByteSize( typeTree, 0);
+}
+
+static void RecalculateTypeTreeByteSize (TypeTree& typetree, int* typePosition, int options)
+{
+ DebugAssertIf (typetree.m_ByteSize == 0 && typetree.m_Type == "Generic Mono");
+
+ if ((typetree.m_MetaFlag & kDebugPropertyMask) == 0)
+ {
+ typetree.m_Index = *typePosition;
+ (*typePosition)++;
+ }
+ else
+ {
+ if (options & kIgnoreDebugPropertiesForIndex)
+ typetree.m_Index = -1;
+ else
+ {
+ typetree.m_Index = *typePosition;
+ (*typePosition)++;
+ }
+ }
+
+ if (typetree.m_Children.empty ())
+ {
+ AssertIf (typetree.m_IsArray);
+ return;
+ }
+
+ bool cantDetermineSize = false;
+ typetree.m_ByteSize = 0;
+
+ TypeTree::TypeTreeList::iterator i;
+ for (i = typetree.m_Children.begin ();i != typetree.m_Children.end ();++i)
+ {
+ RecalculateTypeTreeByteSize (*i, typePosition, options);
+ if (i->m_ByteSize == -1)
+ cantDetermineSize = true;
+
+ if (!cantDetermineSize)
+ typetree.m_ByteSize += i->m_ByteSize;
+
+ i->m_Father = &typetree;
+ }
+
+ if (typetree.m_IsArray || cantDetermineSize)
+ typetree.m_ByteSize = -1;
+
+ DebugAssertIf (typetree.m_ByteSize == 0 && typetree.m_Type == "Generic Mono");
+}
+
+static void RecalculateTypeTreeByteSize (TypeTree& typetree, int options)
+{
+// AssertIf (typetree.m_Father != NULL);
+// AssertIf (typetree.m_Name != "Base");
+ typetree.m_ByteSize = -1;
+ int typePosition = 0;
+ RecalculateTypeTreeByteSize (typetree, &typePosition, options);
+}
+
+/*
+void GetTypePath (const TypeTree* type, string& s)
+{
+ if (type != NULL)
+ {
+ GetTypePath (type->m_Father, s);
+ s += "::";
+ s += type->m_Name;
+ s += '(';
+ s += type->m_Type;
+ s += ") ";
+ }
+}*/
+
+
+
+Type::Type ()
+ : m_OldType (NULL),
+ m_NewType (NULL),
+ m_Equals (false)
+{}
+
+Type::~Type ()
+{
+ UNITY_DELETE(m_OldType, kMemTypeTree);
+ UNITY_DELETE(m_NewType, kMemTypeTree);
+}
+
+void Type::SetOldType (TypeTree* t)
+{
+ UNITY_DELETE(m_OldType, kMemTypeTree);
+ m_OldType = t;
+ m_Equals = m_OldType != NULL && m_NewType != NULL && IsStreamedBinaryCompatbile(*m_OldType, *m_NewType);
+}
+
+void Type::SetNewType (TypeTree* t)
+{
+ UNITY_DELETE (m_NewType, kMemTypeTree);
+ m_NewType = t;
+ m_Equals = m_OldType != NULL && m_NewType != NULL && IsStreamedBinaryCompatbile(*m_OldType, *m_NewType);
+}
+
+const TypeTree& GetElementTypeFromContainer (const TypeTree& typeTree)
+{
+ AssertIf (typeTree.m_Children.size () != 1);
+ AssertIf (typeTree.m_Children.back ().m_Children.size () != 2);
+ return typeTree.m_Children.back ().m_Children.back ();
+}
+
+SInt32 GetContainerArraySize (const TypeTree& typeTree, void* data)
+{
+ AssertIf (data == NULL);
+ AssertIf (typeTree.m_Children.size () != 1);
+ AssertIf (typeTree.m_Children.back ().m_Children.size () != 2);
+ AssertIf (!typeTree.m_Children.back ().m_IsArray);
+ SInt32* casted = reinterpret_cast<SInt32*> (data);
+ return *casted;
+}
+
+template<bool kSwap>
+bool ReadTypeTreeImpl (TypeTree& t, UInt8 const*& iterator, UInt8 const* end, int version)
+{
+ // Read Type
+ if (!ReadString (t.m_Type, iterator, end))
+ return false;
+
+ // During the 4.3 beta cycle, we had a couple of versions that were using "SInt32" and "UInt32"
+ // instead of "int" and "unsigned int". Get rid of these type names here.
+ //
+ // NOTE: For now, we always do this. Ideally, we only want this for old data. However, ATM we
+ // don't version TypeTrees independently (they are tied to kCurrentSerializeVersion). TypeTree
+ // serialization is going to change soonish so we wait with bumping the version until then.
+ DeprecatedConvertUnity43BetaIntegerTypeNames (t.m_Type);
+
+ // Read Name
+ if (!ReadString (t.m_Name, iterator, end))
+ return false;
+
+ // Read bytesize
+ TEST_LEN(t.m_ByteSize);
+ ReadHeaderCache<kSwap> (t.m_ByteSize, iterator);
+
+ // Read variable count
+ if (version == 2)
+ {
+ SInt32 variableCount;
+ TEST_LEN(variableCount);
+ ReadHeaderCache<kSwap> (variableCount, iterator);
+ }
+
+ // Read Typetree position
+ if (version != 3)
+ {
+ TEST_LEN(t.m_Index);
+ ReadHeaderCache<kSwap> (t.m_Index, iterator);
+ }
+
+ // Read IsArray
+ TEST_LEN(t.m_IsArray);
+ ReadHeaderCache<kSwap> (t.m_IsArray, iterator);
+
+ // Read version
+ TEST_LEN(t.m_Version);
+ ReadHeaderCache<kSwap> (t.m_Version, iterator);
+
+ // Read metaflag
+ if (version != 3)
+ {
+ TEST_LEN(t.m_MetaFlag);
+ ReadHeaderCache<kSwap> ((UInt32&)t.m_MetaFlag, iterator);
+ }
+
+ // Read Children count
+ SInt32 childrenCount;
+ TEST_LEN(childrenCount);
+ ReadHeaderCache<kSwap> (childrenCount, iterator);
+
+ enum { kMaxDepth = 50, kMaxChildrenCount = 5000 };
+ static int depth = 0;
+ depth++;
+ if (depth > kMaxDepth || childrenCount < 0 || childrenCount > kMaxChildrenCount)
+ {
+ depth--;
+ ErrorString ("Fatal error while reading file. Header is invalid!");
+ return false;
+ }
+ // Read children
+ for (int i=0;i<childrenCount;i++)
+ {
+ TypeTree newType;
+ t.m_Children.push_back (newType);
+ if (!ReadTypeTree (t.m_Children.back (), iterator, end, version, kSwap))
+ {
+ depth--;
+ return false;
+ }
+ t.m_Children.back ().m_Father = &t;
+ }
+ depth--;
+ return true;
+}
+
+template<bool kSwap>
+void WriteTypeTreeImpl (TypeTree& t, SerializationCache& cache)
+{
+ // Write type
+ WriteString (t.m_Type, cache);
+
+ // Write name
+ WriteString (t.m_Name, cache);
+
+ // Write bytesize
+ WriteHeaderCache<kSwap> (t.m_ByteSize, cache);
+
+ // Write typetree position
+ WriteHeaderCache<kSwap> (t.m_Index, cache);
+
+ // Write IsArray
+ WriteHeaderCache<kSwap> (t.m_IsArray, cache);
+
+ // Write version
+ WriteHeaderCache<kSwap> (t.m_Version, cache);
+
+ // Write metaflag
+ WriteHeaderCache<kSwap> ((UInt32)t.m_MetaFlag, cache);
+
+ // Write Children Count
+ SInt32 childrenCount = t.m_Children.size ();
+ WriteHeaderCache<kSwap> (childrenCount, cache);
+
+ // Write children
+ TypeTree::TypeTreeList::iterator i;
+ for (i = t.m_Children.begin ();i != t.m_Children.end ();i++)
+ WriteTypeTreeImpl<kSwap> (*i, cache);
+}
+
+void WriteTypeTree (TypeTree& t, SerializationCache& cache, bool swapEndian)
+{
+ if (swapEndian)
+ WriteTypeTreeImpl<true> (t, cache);
+ else
+ WriteTypeTreeImpl<false> (t, cache);
+}
+
+bool ReadTypeTree (TypeTree& t, UInt8 const*& iterator, UInt8 const* end, int version, bool swapEndian)
+{
+ if (swapEndian)
+ return ReadTypeTreeImpl<true> (t, iterator, end, version);
+ else
+ return ReadTypeTreeImpl<false> (t, iterator, end, version);
+}
+
+bool ReadVersionedTypeTreeFromVector (TypeTree* typeTree, UInt8 const*& iterator, UInt8 const* end, bool swapEndian)
+{
+ if (iterator == end)
+ {
+ *typeTree = TypeTree ();
+ return false;
+ }
+ SInt32 version;
+
+ if (swapEndian)
+ {
+ ReadHeaderCache<true> (version, iterator);
+ ReadTypeTreeImpl<true> (*typeTree, iterator, end, version);
+ }
+ else
+ {
+ ReadHeaderCache<false> (version, iterator);
+ ReadTypeTreeImpl<false> (*typeTree, iterator, end, version);
+ }
+ return true;
+}
+
+void WriteString (UnityStr const& s, SerializationCache& cache)
+{
+ int size = cache.size ();
+ cache.resize (size + s.size () + 1);
+ memcpy (&cache[size], s.data (), s.size ());
+ cache.back () = '\0';
+}
+
+void WriteString (TypeTreeString const& s, SerializationCache& cache)
+{
+ int size = cache.size ();
+ cache.resize (size + s.size () + 1);
+ memcpy (&cache[size], s.data (), s.size ());
+ cache.back () = '\0';
+}
+
+
+bool ReadString (TypeTreeString& s, UInt8 const*& iterator, UInt8 const* end)
+{
+ UInt8 const* base = iterator;
+ while (*iterator != 0 && iterator != end)
+ iterator++;
+
+#if ENABLE_SECURITY
+ if (iterator == end)
+ return false;
+#endif
+
+ s.assign(base, iterator);
+ iterator++;
+ return true;
+}
+
+bool ReadString (UnityStr& s, UInt8 const*& iterator, UInt8 const* end)
+{
+ UInt8 const* base = iterator;
+ while (*iterator != 0 && iterator != end)
+ iterator++;
+
+#if ENABLE_SECURITY
+ if (iterator == end)
+ return false;
+#endif
+
+ s.assign(base, iterator);
+ iterator++;
+ return true;
+}
+
+
+#if UNITY_EDITOR
+void HashTypeTree (MdFourGenerator& gen, TypeTree& typeTree)
+{
+ gen.Feed (typeTree.m_Type);
+ gen.Feed (typeTree.m_Name);
+ gen.Feed (typeTree.m_ByteSize);
+ gen.Feed (typeTree.m_IsArray);
+ gen.Feed (typeTree.m_Version);
+
+ for (TypeTree::iterator it = typeTree.begin (), end = typeTree.end (); it != end; ++it)
+ {
+ HashTypeTree (gen, *it);
+ }
+}
+
+UInt32 HashTypeTree (TypeTree& typeTree)
+{
+ MdFourGenerator gen;
+ HashTypeTree (gen, typeTree);
+ MdFour hash = gen.Finish ();
+ return hash.PackToUInt32 ();
+}
+#endif
diff --git a/Runtime/Serialize/TypeTree.h b/Runtime/Serialize/TypeTree.h
new file mode 100644
index 0000000..0fd6e2f
--- /dev/null
+++ b/Runtime/Serialize/TypeTree.h
@@ -0,0 +1,130 @@
+#ifndef TYPETREE_H
+#define TYPETREE_H
+
+#include <list>
+#include <vector>
+#include <string>
+
+#include "Runtime/Misc/Allocator.h"
+#include "Configuration/UnityConfigure.h"
+#include "SerializationMetaFlags.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+
+typedef UNITY_VECTOR(kMemSerialization, UInt8) SerializationCache;
+
+template<bool kSwap, class T>
+// Need this in order for Asset bundles to load correctly when the code is optimized
+#if UNITY_BB10
+__attribute__ ((noinline))
+#endif
+void ReadHeaderCache (T& t, UInt8 const*& c)
+{
+ t = *(T const*)c;
+ if (kSwap)
+ SwapEndianBytes (t);
+ c += sizeof (T);
+}
+
+template<bool kSwap, class T>
+void WriteHeaderCache (const T& t, SerializationCache& vec)
+{
+ vec.resize (vec.size () + sizeof (T));
+ T& dst = *reinterpret_cast<T*> (&vec[vec.size () - sizeof (T)]);
+ dst = t;
+ if (kSwap)
+ SwapEndianBytes (dst);
+}
+
+UNITY_STR_IMPL(TypeTreeString, kMemTypeTree);
+
+/// A TypeTree contains information on serialized data.
+/// It is a tree storing the name, and type and other information generated by the serialization code,
+/// of every variable and all its variables that it might contain.
+/// A TypeTree can be generated using a ProxyTransfer class.
+/// There are convenience function which generate them from an object in TransferUtility.h
+
+class TypeTree
+{
+ public:
+
+ typedef UNITY_LIST(kMemTypeTree,TypeTree) TypeTreeList;
+ typedef TypeTreeList::iterator iterator;
+ typedef TypeTreeList::const_iterator const_iterator;
+ TypeTreeList m_Children; // The children of the Type (eg. a Vector3f has 3 children, float x, float y, float z)
+ TypeTree* m_Father;
+
+ TypeTreeString m_Type;// The type of the variable (eg. "Vector3f", "int")
+ TypeTreeString m_Name;// The name of the property (eg. "m_LocalPosition")
+ SInt32 m_ByteSize;//= -1 if its not determinable (arrays)
+ SInt32 m_Index; // The index of the property (Prefabs use this index in the override bitset)
+ SInt32 m_IsArray;// Is the TypeTree an array (first child is the size, second child is the type of the array elements)
+ SInt32 m_Version; // The version of the serialization format as represented by this type tree. Usually determined by Transfer() functions.
+
+ // Serialization meta data (eg. to hide variables in the property editor)
+ // Children or their meta flags with their parents!
+ TransferMetaFlags m_MetaFlag;
+ SInt32 m_ByteOffset; // The byteoffset into the property in memory. When a variable on the stack is serialized m_ByteOffset is -1.
+ void* m_DirectPtr; // The direct ptr into the property in memory. NULL if it can't be used.
+
+ TypeTree ();
+ TypeTree (const std::string& name, const std::string& type, SInt32 size);
+
+ void DebugPrint (std::string& buffer, int level = 0) const;
+ bool IsBasicDataType ()const { return m_Children.empty () && m_ByteSize > 0; }
+
+ iterator begin () { return m_Children.begin (); }
+ iterator end () { return m_Children.end (); }
+ const_iterator begin () const { return m_Children.begin (); }
+ const_iterator end () const { return m_Children.end (); }
+
+ void operator = (const TypeTree& typeTree);
+};
+
+void GetTypePath (const TypeTree* type, std::string& s);
+const TypeTree& GetElementTypeFromContainer (const TypeTree& typeTree);
+SInt32 GetContainerArraySize (const TypeTree& typeTree, void* data);
+void AppendTypeTree (TypeTree& typeTree, TypeTree::iterator begin, TypeTree::iterator end);
+void AppendTypeTree (TypeTree& typeTree, const TypeTree& typeTreeToAdd);
+void RemoveFromTypeTree (TypeTree& typeTree, TypeTree::iterator begin, TypeTree::iterator end);
+
+class Type
+{
+ TypeTree* m_OldType; // Type loaded from Disk
+ TypeTree* m_NewType; // Active type
+ bool m_Equals; // Are oldType and newType equal
+
+ public:
+
+ Type ();
+
+ ~Type ();
+
+ TypeTree* GetOldType () { return m_OldType; }
+ TypeTree* GetNewType () { return m_NewType; }
+
+ bool EqualTypes () { return m_Equals; }
+
+ void SetOldType (TypeTree* t);
+ void SetNewType (TypeTree* t);
+};
+
+/// Reads/Writes a typetree to a vector<UInt8> with the first four bits being the version of the typetree
+bool ReadVersionedTypeTreeFromVector (TypeTree* typeTree, UInt8 const*& iterator, UInt8 const* end, bool swapEndianess);
+
+/// Reads/Writes a typeTree to a cache, used direcly by SerializedFile
+bool ReadTypeTree (TypeTree& t, UInt8 const*& iterator, UInt8 const* end, int version, bool swapEndian);
+void WriteTypeTree (TypeTree& t, SerializationCache& cache, bool swapEndianess);
+
+bool IsStreamedBinaryCompatbile (const TypeTree& lhs, const TypeTree& rhs);
+bool IsStreamedBinaryCompatbileAndIndices (const TypeTree& lhs, const TypeTree& rhs);
+
+
+void WriteString (TypeTreeString const& s, SerializationCache& cache);
+void WriteString (UnityStr const& s, SerializationCache& cache);
+bool ReadString (TypeTreeString& s, UInt8 const*& iterator, UInt8 const* end);
+bool ReadString (UnityStr& s, UInt8 const*& iterator, UInt8 const* end);
+
+UInt32 HashTypeTree (TypeTree& typeTree);
+
+#endif
diff --git a/Runtime/Serialize/WriteData.h b/Runtime/Serialize/WriteData.h
new file mode 100644
index 0000000..52be58c
--- /dev/null
+++ b/Runtime/Serialize/WriteData.h
@@ -0,0 +1,27 @@
+#ifndef WRITE_DATA_H
+#define WRITE_DATA_H
+
+#include "SerializationMetaFlags.h"
+
+struct WriteData
+{
+ LocalIdentifierInFileType localIdentifierInFile;
+ SInt32 instanceID;
+ BuildUsageTag buildUsage;
+
+ WriteData () : localIdentifierInFile(0), instanceID(0) { }
+
+ WriteData (LocalIdentifierInFileType local, SInt32 instance, const BuildUsageTag& tag)
+ : localIdentifierInFile (local), instanceID(instance), buildUsage(tag)
+ { }
+
+ WriteData (LocalIdentifierInFileType local, SInt32 instance)
+ : localIdentifierInFile (local), instanceID(instance)
+ { }
+
+ friend bool operator < (const WriteData& lhs, const WriteData& rhs)
+ {
+ return lhs.localIdentifierInFile < rhs.localIdentifierInFile;
+ }
+};
+#endif
diff --git a/Runtime/Serialize/WriteTypeToBuffer.h b/Runtime/Serialize/WriteTypeToBuffer.h
new file mode 100644
index 0000000..3065f23
--- /dev/null
+++ b/Runtime/Serialize/WriteTypeToBuffer.h
@@ -0,0 +1,21 @@
+#ifndef WRITE_TYPE_TO_BUFFER_H
+#define WRITE_TYPE_TO_BUFFER_H
+
+#include "Runtime/Allocator/MemoryManager.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Serialize/TransferFunctions/StreamedBinaryWrite.h"
+
+template <typename TYPE> void WriteTypeToVector (TYPE& object, dynamic_array<UInt8>* data, int options = 0)
+{
+ data->clear ();
+ MemoryCacheWriter memoryCache (*data);
+ StreamedBinaryWrite<false> writeStream;
+ CachedWriter& writeCache = writeStream.Init (0, BuildTargetSelection::NoTarget());
+
+ writeCache.InitWrite(memoryCache);
+ writeStream.Transfer(object, "Base");
+
+ if (!writeCache.CompleteWriting () || writeCache.GetPosition() != data->size ())
+ ErrorString ("Error while writing serialized data.");
+}
+#endif
diff --git a/Runtime/Shaders/BufferedVBO.cpp b/Runtime/Shaders/BufferedVBO.cpp
new file mode 100644
index 0000000..452d993
--- /dev/null
+++ b/Runtime/Shaders/BufferedVBO.cpp
@@ -0,0 +1,142 @@
+#include "UnityPrefix.h"
+#include "BufferedVBO.h"
+
+
+BufferedVBO::BufferedVBO()
+{
+ m_AllocatedSize = 0;
+}
+
+BufferedVBO::~BufferedVBO()
+{
+ UnbufferVertexData();
+}
+
+void BufferedVBO::BufferAllVertexData( const VertexBufferData& buffer )
+{
+ bool copyModes[kStreamModeCount];
+ std::fill(copyModes, copyModes + kStreamModeCount, true);
+ BufferVertexData(buffer, copyModes);
+}
+
+void BufferedVBO::BufferAccessibleVertexData( const VertexBufferData& buffer )
+{
+ bool copyModes[kStreamModeCount];
+ std::fill(copyModes, copyModes + kStreamModeCount, true);
+ copyModes[kStreamModeNoAccess] = false;
+ BufferVertexData(buffer, copyModes);
+}
+
+void BufferedVBO::BufferVertexData( const VertexBufferData& buffer, bool copyModes[kStreamModeCount] )
+{
+ std::copy(buffer.channels, buffer.channels + kShaderChannelCount, m_VertexData.channels);
+
+ int streamOffset = 0;
+ for (int s = 0; s < kMaxVertexStreams; s++)
+ {
+ StreamInfo& stream = m_VertexData.streams[s];
+ const StreamInfo& srcStream = buffer.streams[s];
+ UInt8 mode = m_StreamModes[s];
+ Assert(mode < kStreamModeCount);
+ if (copyModes[mode] && srcStream.channelMask)
+ {
+ stream = srcStream;
+ stream.offset = streamOffset;
+ streamOffset += CalculateVertexStreamSize(buffer, s);
+ streamOffset = VertexData::AlignStreamSize(streamOffset);
+ }
+ else
+ stream.Reset();
+ }
+ size_t allocSize = VertexData::GetAllocateDataSize(streamOffset);
+ if (allocSize != m_AllocatedSize)
+ {
+ UnbufferVertexData();
+ m_AllocatedSize = allocSize;
+ m_VertexData.buffer = (UInt8*)UNITY_MALLOC_ALIGNED (kMemVertexData, allocSize, VertexData::kVertexDataAlign);
+ }
+ m_VertexData.bufferSize = streamOffset;
+ m_VertexData.vertexCount = buffer.vertexCount;
+
+ for (int s = 0; s < kMaxVertexStreams; s++)
+ {
+ const StreamInfo& srcStream = buffer.streams[s];
+ UInt8 mode = m_StreamModes[s];
+ if (copyModes[mode] && srcStream.channelMask)
+ CopyVertexStream(buffer, GetStreamBuffer(s), s);
+ }
+}
+
+void BufferedVBO::UnbufferVertexData()
+{
+ if (m_AllocatedSize > 0)
+ UNITY_FREE(kMemVertexData, m_VertexData.buffer);
+
+ m_AllocatedSize = 0;
+ m_VertexData.buffer = NULL;
+}
+
+bool BufferedVBO::MapVertexStream( VertexStreamData& outData, unsigned stream )
+{
+ Assert(!m_IsStreamMapped[stream]);
+ DebugAssert(m_StreamModes[stream] != kStreamModeNoAccess);
+ if (m_StreamModes[stream] == kStreamModeNoAccess)
+ return false;
+ DebugAssert(m_VertexData.buffer);
+ if (!m_VertexData.buffer)
+ return false;
+ outData.buffer = m_VertexData.buffer + m_VertexData.streams[stream].offset;
+ outData.channelMask = m_VertexData.streams[stream].channelMask;
+ outData.stride = m_VertexData.streams[stream].stride;
+ outData.vertexCount = m_VertexData.vertexCount;
+ m_IsStreamMapped[stream] = true;
+ return true;
+}
+
+void BufferedVBO::UnmapVertexStream( unsigned stream )
+{
+ Assert(m_IsStreamMapped[stream]);
+ m_IsStreamMapped[stream] = false;
+}
+
+int BufferedVBO::GetRuntimeMemorySize() const
+{
+ return m_AllocatedSize;
+}
+
+UInt8* BufferedVBO::GetStreamBuffer(unsigned stream)
+{
+ return m_VertexData.buffer + m_VertexData.streams[stream].offset;
+}
+
+UInt8* BufferedVBO::GetChannelDataAndStride(ShaderChannel channel, UInt32& outStride)
+{
+ const ChannelInfo& info = m_VertexData.channels[channel];
+ if (info.IsValid())
+ {
+ outStride = info.CalcStride(m_VertexData.streams);
+ return m_VertexData.buffer + info.CalcOffset(m_VertexData.streams);
+ }
+ outStride = 0;
+ return NULL;
+}
+
+void BufferedVBO::GetChannelDataAndStrides(void* channelData[kShaderChannelCount], UInt32 outStrides[kShaderChannelCount])
+{
+ for (int i = 0; i < kShaderChannelCount; i++)
+ channelData[i] = GetChannelDataAndStride(ShaderChannel(i), outStrides[i]);
+}
+
+void BufferedVBO::GetChannelOffsetsAndStrides(void* channelOffsets[kShaderChannelCount], UInt32 outStrides[kShaderChannelCount])
+{
+ for (int i = 0; i < kShaderChannelCount; i++)
+ {
+ channelOffsets[i] = reinterpret_cast<void*>(m_VertexData.channels[i].CalcOffset(m_VertexData.streams));
+ outStrides[i] = m_VertexData.channels[i].CalcStride(m_VertexData.streams);
+ }
+}
+
+void BufferedVBO::UnloadSourceVertices()
+{
+ UnbufferVertexData();
+}
diff --git a/Runtime/Shaders/BufferedVBO.h b/Runtime/Shaders/BufferedVBO.h
new file mode 100644
index 0000000..5932b28
--- /dev/null
+++ b/Runtime/Shaders/BufferedVBO.h
@@ -0,0 +1,35 @@
+#ifndef BUFFEREDVBO_H
+#define BUFFEREDVBO_H
+
+#include "VBO.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+class BufferedVBO : public VBO
+{
+public:
+ BufferedVBO();
+ ~BufferedVBO();
+
+ virtual bool MapVertexStream( VertexStreamData& outData, unsigned stream );
+ virtual void UnmapVertexStream( unsigned stream );
+
+ virtual int GetRuntimeMemorySize() const;
+
+ virtual void UnloadSourceVertices();
+
+protected:
+ void BufferAllVertexData( const VertexBufferData& buffer );
+ void BufferAccessibleVertexData( const VertexBufferData& buffer );
+ void BufferVertexData( const VertexBufferData& buffer, bool copyModes[kStreamModeCount] );
+ void UnbufferVertexData();
+
+ UInt8* GetStreamBuffer(unsigned stream);
+ UInt8* GetChannelDataAndStride(ShaderChannel channel, UInt32& outStride);
+ void GetChannelDataAndStrides(void* channelData[kShaderChannelCount], UInt32 outStrides[kShaderChannelCount]);
+ void GetChannelOffsetsAndStrides(void* channelOffsets[kShaderChannelCount], UInt32 outStrides[kShaderChannelCount]);
+
+ VertexBufferData m_VertexData;
+ size_t m_AllocatedSize;
+};
+
+#endif
diff --git a/Runtime/Shaders/ComputeShader.cpp b/Runtime/Shaders/ComputeShader.cpp
new file mode 100644
index 0000000..f5b9f40
--- /dev/null
+++ b/Runtime/Shaders/ComputeShader.cpp
@@ -0,0 +1,425 @@
+#include "UnityPrefix.h"
+#include "ComputeShader.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Profiler/Profiler.h"
+
+PROFILER_INFORMATION(gDispatchComputeProfile, "Compute.Dispatch", kProfilerRender)
+
+
+// --------------------------------------------------------------------------
+
+
+struct ComputeProgramStruct
+{
+ ComputeProgramHandle handle;
+ int cbBindPoints[kMaxSupportedConstantBuffers];
+ int texBindPoints[kMaxSupportedComputeResources];
+ TextureID textures[kMaxSupportedComputeResources];
+ unsigned builtinSamplers[kMaxSupportedComputeResources]; // highest 16 bits - builtin sampler type; lowest 16 bits - bind point
+ int inBufBindPoints[kMaxSupportedComputeResources];
+ ComputeBufferID inBuffers[kMaxSupportedComputeResources];
+ UInt32 outBufBindPoints[kMaxSupportedComputeResources]; // highest bit indicates whether this is raw UAV or a texture UAV
+ ComputeBufferID outBuffers[kMaxSupportedComputeResources];
+ TextureID outTextures[kMaxSupportedComputeResources];
+};
+
+
+ComputeShader::ComputeShader(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_Programs(NULL)
+, m_ProgramCount(0)
+, m_DataBuffer(NULL)
+, m_DataBufferSize(0)
+, m_CBDirty(0)
+{
+}
+
+ComputeShader::~ComputeShader ()
+{
+ DestroyRuntimeData ();
+}
+
+
+void ComputeShader::DestroyRuntimeData ()
+{
+ GfxDevice& device = GetGfxDevice();
+ for (int i = 0; i < m_ProgramCount; ++i)
+ {
+ device.DestroyComputeProgram (m_Programs[i].handle);
+ }
+ delete[] m_Programs;
+ m_Programs = NULL;
+ m_ProgramCount = 0;
+
+ device.DestroyComputeConstantBuffers (m_ConstantBuffers.size(), m_CBs);
+
+ delete[] m_DataBuffer;
+ m_DataBuffer = NULL;
+ m_DataBufferSize = 0;
+ m_CBDirty = 0;
+}
+
+void ComputeShader::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ DestroyRuntimeData ();
+ CreateRuntimeData();
+}
+
+void ComputeShader::CreateRuntimeData()
+{
+ // create compute programs
+ GfxDevice& device = GetGfxDevice();
+ m_ProgramCount = m_Kernels.size();
+ m_Programs = new ComputeProgramStruct[m_ProgramCount];
+ for (int i = 0; i < m_ProgramCount; ++i)
+ {
+ const ComputeShaderKernel& kernelData = m_Kernels[i];
+ m_Programs[i].handle = device.CreateComputeProgram (kernelData.code.data(), kernelData.code.size());
+
+ // compute CS bind points for this kernel
+ memset (m_Programs[i].cbBindPoints, -1, sizeof(m_Programs[i].cbBindPoints));
+ for (size_t r = 0, nr = kernelData.cbs.size(); r < nr; ++r)
+ {
+ const ComputeShaderResource& res = kernelData.cbs[r];
+ for (size_t cb = 0, ncb = m_ConstantBuffers.size(); cb < ncb; ++cb)
+ {
+ if (m_ConstantBuffers[cb].name == res.name)
+ {
+ m_Programs[i].cbBindPoints[cb] = res.bindPoint;
+ break;
+ }
+ }
+ }
+
+ for (size_t t = 0, nt = kernelData.textures.size(); t < nt; ++t)
+ {
+ m_Programs[i].texBindPoints[t] = kernelData.textures[t].bindPoint;
+ m_Programs[i].textures[t].m_ID = 0;
+ }
+ for (size_t s = 0, ns = kernelData.builtinSamplers.size(); s < ns; ++s)
+ {
+ m_Programs[i].builtinSamplers[s] = (kernelData.builtinSamplers[s].sampler << 16) | (kernelData.builtinSamplers[s].bindPoint);
+ }
+ for (size_t b = 0, nb = kernelData.inBuffers.size(); b < nb; ++b)
+ {
+ m_Programs[i].inBufBindPoints[b] = kernelData.inBuffers[b].bindPoint;
+ m_Programs[i].inBuffers[b] = ComputeBufferID();
+ }
+ for (size_t b = 0, nb = kernelData.outBuffers.size(); b < nb; ++b)
+ {
+ m_Programs[i].outBufBindPoints[b] = kernelData.outBuffers[b].bindPoint;
+ m_Programs[i].outBuffers[b] = ComputeBufferID();
+ m_Programs[i].outTextures[b].m_ID = 0;
+ }
+ }
+
+ // calculate space to hold all CBs, and offsets into the buffer
+ m_CBDirty = 0;
+ m_DataBufferSize = 0;
+ for (int cb = 0; cb < m_ConstantBuffers.size(); ++cb)
+ {
+ m_CBSizes[cb] = m_ConstantBuffers[cb].byteSize;
+ m_CBOffsets[cb] = m_DataBufferSize;
+ m_DataBufferSize += m_ConstantBuffers[cb].byteSize;
+ }
+ m_DataBuffer = new UInt8[m_DataBufferSize];
+ memset (m_DataBuffer, 0, m_DataBufferSize);
+
+ // create constant buffers
+ device.CreateComputeConstantBuffers (m_ConstantBuffers.size(), m_CBSizes, m_CBs);
+}
+
+
+int ComputeShader::FindKernel (const FastPropertyName& name) const
+{
+ for (size_t i = 0, n = m_Kernels.size(); i < n; ++i)
+ {
+ if (m_Kernels[i].name == name)
+ return i;
+ }
+ return -1;
+}
+
+
+void ComputeShader::SetValueParam (const FastPropertyName& name, int byteCount, const void* data)
+{
+ for (size_t icb = 0, ncb = m_ConstantBuffers.size(); icb < ncb; ++icb)
+ {
+ const ComputeShaderCB& cb = m_ConstantBuffers[icb];
+ for (size_t p = 0, np = cb.params.size(); p < np; ++p)
+ {
+ if (cb.params[p].name == name)
+ {
+ const int cbOffset = cb.params[p].offset;
+ const int cbByteSize = cb.byteSize;
+ if (cbOffset >= cbByteSize || cbOffset + byteCount > cbByteSize)
+ return;
+
+ m_CBDirty |= (1<<icb);
+ memcpy (m_DataBuffer + m_CBOffsets[icb] + cbOffset, data, byteCount);
+ }
+ }
+ }
+}
+
+
+void ComputeShader::SetTextureParam (unsigned kernelIdx, const FastPropertyName& name, TextureID tid)
+{
+ if (kernelIdx >= m_ProgramCount)
+ return;
+
+ const std::vector<ComputeShaderResource>& textures = m_Kernels[kernelIdx].textures;
+ for (size_t i = 0, n = textures.size(); i < n; ++i)
+ {
+ if (textures[i].name == name)
+ {
+ m_Programs[kernelIdx].textures[i] = tid;
+ break;
+ }
+ }
+
+ const std::vector<ComputeShaderResource>& outBuffers = m_Kernels[kernelIdx].outBuffers;
+ for (size_t i = 0, n = outBuffers.size(); i < n; ++i)
+ {
+ if (outBuffers[i].name == name)
+ {
+ m_Programs[kernelIdx].outTextures[i] = tid;
+ m_Programs[kernelIdx].outBufBindPoints[i] |= 0x80000000; // set highest bit, indicating it's a texture param
+ break;
+ }
+ }
+}
+
+void ComputeShader::SetBufferParam (unsigned kernelIdx, const FastPropertyName& name, ComputeBufferID handle)
+{
+ if (kernelIdx >= m_ProgramCount)
+ return;
+
+ const std::vector<ComputeShaderResource>& inBuffers = m_Kernels[kernelIdx].inBuffers;
+ for (size_t i = 0, n = inBuffers.size(); i < n; ++i)
+ {
+ if (inBuffers[i].name == name)
+ {
+ m_Programs[kernelIdx].inBuffers[i] = handle;
+ break;
+ }
+ }
+
+ const std::vector<ComputeShaderResource>& outBuffers = m_Kernels[kernelIdx].outBuffers;
+ for (size_t i = 0, n = outBuffers.size(); i < n; ++i)
+ {
+ if (outBuffers[i].name == name)
+ {
+ m_Programs[kernelIdx].outBuffers[i] = handle;
+ m_Programs[kernelIdx].outBufBindPoints[i] &= 0x7FFFFFFF; // clear highest bit, indicating it's a buffer param
+ break;
+ }
+ }
+}
+
+
+void ComputeShader::DispatchComputeShader (unsigned kernelIdx, int threadsX, int threadsY, int threadsZ)
+{
+ if (!gGraphicsCaps.hasComputeShader)
+ {
+ ErrorString ("Platform does not support compute shaders");
+ return;
+ }
+
+ if (kernelIdx >= m_ProgramCount)
+ return;
+
+ GPU_AUTO_SECTION(kGPUSectionOther);
+ PROFILER_AUTO(gDispatchComputeProfile, this)
+
+ GfxDevice& device = GetGfxDevice();
+ const unsigned cbCount = m_ConstantBuffers.size();
+ device.UpdateComputeConstantBuffers (cbCount, m_CBs, m_CBDirty, m_DataBufferSize, m_DataBuffer, m_CBSizes, m_CBOffsets, m_Programs[kernelIdx].cbBindPoints);
+ device.UpdateComputeResources (
+ m_Kernels[kernelIdx].textures.size(), m_Programs[kernelIdx].textures, m_Programs[kernelIdx].texBindPoints,
+ m_Kernels[kernelIdx].builtinSamplers.size(), m_Programs[kernelIdx].builtinSamplers,
+ m_Kernels[kernelIdx].inBuffers.size(), m_Programs[kernelIdx].inBuffers, m_Programs[kernelIdx].inBufBindPoints,
+ m_Kernels[kernelIdx].outBuffers.size(), m_Programs[kernelIdx].outBuffers, m_Programs[kernelIdx].outTextures, m_Programs[kernelIdx].outBufBindPoints
+ );
+ device.DispatchComputeProgram (m_Programs[kernelIdx].handle, threadsX, threadsY, threadsZ);
+ GPU_TIMESTAMP();
+
+ // CBs we have just used aren't dirty anymore
+ for (unsigned i = 0; i < cbCount; ++i)
+ {
+ if (m_Programs[kernelIdx].cbBindPoints[i] >= 0)
+ {
+ UInt32 dirtyMask = (1<<i);
+ m_CBDirty &= ~dirtyMask;
+ }
+ }
+}
+
+
+
+template<class TransferFunc>
+void ComputeShaderParam::Transfer (TransferFunc& transfer)
+{
+ TRANSFER(name);
+ TRANSFER_ENUM(type)
+ TRANSFER(offset);
+ TRANSFER(arraySize);
+ TRANSFER(rowCount);
+ TRANSFER(colCount);
+}
+
+template<class TransferFunc>
+void ComputeShaderCB::Transfer (TransferFunc& transfer)
+{
+ TRANSFER(name);
+ TRANSFER(byteSize);
+ TRANSFER(params);
+}
+
+template<class TransferFunc>
+void ComputeShaderResource::Transfer (TransferFunc& transfer)
+{
+ TRANSFER(name);
+ TRANSFER(bindPoint);
+}
+
+template<class TransferFunc>
+void ComputeShaderBuiltinSampler::Transfer (TransferFunc& transfer)
+{
+ transfer.Transfer((int&)sampler, "sampler");
+ TRANSFER(bindPoint);
+}
+
+template<class TransferFunc>
+void ComputeShaderKernel::Transfer (TransferFunc& transfer)
+{
+ TRANSFER(name);
+ TRANSFER(cbs);
+ TRANSFER(textures);
+ TRANSFER(builtinSamplers);
+ TRANSFER(inBuffers);
+ TRANSFER(outBuffers);
+ transfer.Transfer(code, "code", kHideInEditorMask);
+}
+
+
+template<class TransferFunc>
+void ComputeShader::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+
+ transfer.Transfer (m_Kernels, "kernels");
+ transfer.Transfer (m_ConstantBuffers, "constantBuffers");
+
+ #if UNITY_EDITOR
+ if (!transfer.IsSerializingForGameRelease())
+ transfer.Transfer (m_Errors.GetErrors(), "errors");
+ #endif
+}
+
+
+IMPLEMENT_CLASS (ComputeShader)
+IMPLEMENT_OBJECT_SERIALIZE (ComputeShader)
+
+
+// --------------------------------------------------------------------------
+
+
+typedef List< ListNode<ComputeBuffer> > ComputeBufferList;
+static ComputeBufferList s_ComputeBuffers;
+
+
+ComputeBuffer::ComputeBuffer (size_t count, size_t stride, UInt32 flags)
+: m_Count(count)
+, m_Stride(stride)
+, m_Flags(flags)
+, m_ComputeBuffersNode(this)
+{
+ s_ComputeBuffers.push_back(m_ComputeBuffersNode);
+ ReloadToGfxDevice();
+}
+
+ComputeBuffer::~ComputeBuffer ()
+{
+ UnloadFromGfxDevice();
+ m_ComputeBuffersNode.RemoveFromList();
+}
+
+void ComputeBuffer::UnloadFromGfxDevice()
+{
+ GfxDevice& device = GetGfxDevice();
+ device.DestroyComputeBuffer (m_BufferHandle);
+ device.FreeComputeBufferID (m_BufferHandle);
+ m_BufferHandle.m_ID = 0;
+}
+
+void ComputeBuffer::ReloadToGfxDevice()
+{
+ GfxDevice& device = GetGfxDevice();
+ m_BufferHandle = device.CreateComputeBufferID();
+ device.CreateComputeBuffer (m_BufferHandle, m_Count, m_Stride, m_Flags);
+}
+
+
+static size_t ValidateSizeAgainstBufferAndStride (size_t size, size_t bufferSize, size_t stride)
+{
+ if (0 == stride)
+ return 0;
+
+ size = std::min (size, bufferSize);
+ size /= stride;
+ size *= stride;
+ return size;
+}
+
+void ComputeBuffer::SetData (const void* data, size_t size)
+{
+ if (!data || !size || !m_BufferHandle.IsValid())
+ return;
+
+ // make sure size is not too large and multiple of stride
+ size = ValidateSizeAgainstBufferAndStride (size, m_Count * m_Stride, m_Stride);
+ GetGfxDevice().SetComputeBufferData (m_BufferHandle, data, size);
+}
+
+
+void ComputeBuffer::GetData (void* dest, size_t destSize)
+{
+ if (!dest || !destSize || !m_BufferHandle.IsValid())
+ return;
+
+ // make sure size is not too large and multiple of stride
+ destSize = ValidateSizeAgainstBufferAndStride (destSize, m_Count * m_Stride, m_Stride);
+ GetGfxDevice().GetComputeBufferData (m_BufferHandle, dest, destSize);
+}
+
+
+
+void ComputeBuffer::CopyCount (ComputeBuffer* src, ComputeBuffer* dst, UInt32 dstOffset)
+{
+ if (!src || !src->m_BufferHandle.IsValid())
+ return;
+ if (!dst || !dst->m_BufferHandle.IsValid())
+ return;
+ UInt32 srcTypeFlags = src->m_Flags & kCBFlagTypeMask;
+ if (!(srcTypeFlags & kCBFlagAppend) && !(srcTypeFlags & kCBFlagCounter))
+ return;
+
+ GetGfxDevice().CopyComputeBufferCount (src->m_BufferHandle, dst->m_BufferHandle, dstOffset);
+}
+
+
+void ComputeBuffer::UnloadAllFromGfxDevice ()
+{
+ for (ComputeBufferList::iterator i = s_ComputeBuffers.begin(); i != s_ComputeBuffers.end(); ++i)
+ (**i).UnloadFromGfxDevice();
+}
+
+void ComputeBuffer::ReloadAllToGfxDevice ()
+{
+ for (ComputeBufferList::iterator i = s_ComputeBuffers.begin(); i != s_ComputeBuffers.end(); ++i)
+ (**i).ReloadToGfxDevice();
+}
diff --git a/Runtime/Shaders/ComputeShader.h b/Runtime/Shaders/ComputeShader.h
new file mode 100644
index 0000000..9a91c8a
--- /dev/null
+++ b/Runtime/Shaders/ComputeShader.h
@@ -0,0 +1,164 @@
+#pragma once
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/GfxDevice/GfxDeviceObjects.h"
+#include "External/shaderlab/Library/FastPropertyName.h"
+#include "External/shaderlab/Library/ShaderLabErrors.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Utilities/NonCopyable.h"
+#include <vector>
+
+using ShaderLab::FastPropertyName;
+
+
+struct ComputeProgramStruct;
+
+
+enum ComputeShaderParamType {
+ kCSParamFloat = 0,
+ kCSParamInt = 1,
+ kCSParamUInt = 2,
+ kCSParamForce32BitEnum = 0x7FFFFFFF
+};
+
+struct ComputeShaderParam {
+ DECLARE_SERIALIZE_NO_PPTR (ComputeShaderParam)
+
+ FastPropertyName name;
+ ComputeShaderParamType type;
+ int offset;
+ int arraySize;
+ int rowCount;
+ int colCount;
+};
+
+struct ComputeShaderCB {
+ DECLARE_SERIALIZE_NO_PPTR (ComputeShaderCB)
+
+ FastPropertyName name;
+ int byteSize;
+ std::vector<ComputeShaderParam> params;
+};
+
+struct ComputeShaderResource {
+ DECLARE_SERIALIZE_NO_PPTR (ComputeShaderResource)
+ FastPropertyName name;
+ int bindPoint;
+};
+
+struct ComputeShaderBuiltinSampler {
+ DECLARE_SERIALIZE_NO_PPTR (ComputeShaderBuiltinSampler)
+ BuiltinSamplerState sampler;
+ int bindPoint;
+};
+
+struct ComputeShaderKernel {
+ DECLARE_SERIALIZE_NO_PPTR (ComputeShaderKernel)
+
+ FastPropertyName name;
+ std::vector<ComputeShaderResource> cbs;
+ // For textures, bind point is two 16 bit fields:
+ // lower 16 bits - texture bind point
+ // upper 16 bits - sampler bind point, or 0xFFFF if no sampler needed
+ std::vector<ComputeShaderResource> textures;
+ std::vector<ComputeShaderBuiltinSampler> builtinSamplers;
+ std::vector<ComputeShaderResource> inBuffers;
+ std::vector<ComputeShaderResource> outBuffers;
+ dynamic_array<UInt8> code;
+};
+
+
+
+class ComputeShader : public NamedObject {
+public:
+ typedef std::vector<ComputeShaderKernel> KernelArray;
+ typedef std::vector<ComputeShaderCB> CBArray;
+
+public:
+ REGISTER_DERIVED_CLASS (ComputeShader, NamedObject)
+ DECLARE_OBJECT_SERIALIZE (ComputeShader)
+
+ ComputeShader (MemLabelId label, ObjectCreationMode mode);
+ // ~ComputeShader (); declared-by-macro
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ KernelArray& GetKernels() { return m_Kernels; }
+ CBArray& GetConstantBuffers() { return m_ConstantBuffers; }
+
+ #if UNITY_EDITOR
+ const ShaderErrors& GetErrors() const { return m_Errors; }
+ ShaderErrors& GetErrors() { return m_Errors; }
+ #endif
+
+ int FindKernel (const FastPropertyName& name) const;
+
+ void SetValueParam (const FastPropertyName& name, int byteCount, const void* data);
+ void SetTextureParam (unsigned kernelIdx, const FastPropertyName& name, TextureID tid);
+ void SetBufferParam (unsigned kernelIdx, const FastPropertyName& name, ComputeBufferID handle);
+
+ void DispatchComputeShader (unsigned kernelIdx, int threadsX, int threadsY, int threadsZ);
+
+ void UnloadFromGfxDevice() { DestroyRuntimeData(); }
+ void ReloadToGfxDevice() { CreateRuntimeData(); }
+
+private:
+ void DestroyRuntimeData ();
+ void CreateRuntimeData ();
+
+private:
+ KernelArray m_Kernels;
+ CBArray m_ConstantBuffers;
+
+ ComputeProgramStruct* m_Programs;
+ int m_ProgramCount;
+
+ UInt8* m_DataBuffer;
+ int m_DataBufferSize;
+ UInt32 m_CBDirty; // bit per CB
+ UInt32 m_CBOffsets[kMaxSupportedConstantBuffers]; // offset of each CB into data
+ UInt32 m_CBSizes[kMaxSupportedConstantBuffers];
+ ConstantBufferHandle m_CBs[kMaxSupportedConstantBuffers];
+
+ #if UNITY_EDITOR
+ ShaderErrors m_Errors;
+ #endif
+};
+
+
+
+// --------------------------------------------------------------------------
+
+
+
+class ComputeBuffer : public NonCopyable
+{
+public:
+ ComputeBuffer (size_t count, size_t stride, UInt32 flags);
+ ~ComputeBuffer ();
+
+ ComputeBufferID GetBufferHandle() const { return m_BufferHandle; }
+ size_t GetTotalSize() const { return m_Count * m_Stride; }
+ size_t GetCount() const { return m_Count; }
+ size_t GetStride() const { return m_Stride; }
+
+ void SetData (const void* data, size_t size);
+ void GetData (void* dest, size_t destSize);
+
+ static void CopyCount (ComputeBuffer* src, ComputeBuffer* dst, UInt32 dstOffset);
+
+ static void UnloadAllFromGfxDevice();
+ static void ReloadAllToGfxDevice();
+
+private:
+ void UnloadFromGfxDevice();
+ void ReloadToGfxDevice();
+
+private:
+ size_t m_Count;
+ size_t m_Stride;
+ UInt32 m_Flags;
+ ComputeBufferID m_BufferHandle;
+ ListNode<ComputeBuffer> m_ComputeBuffersNode;
+};
diff --git a/Runtime/Shaders/GraphicsCaps.cpp b/Runtime/Shaders/GraphicsCaps.cpp
new file mode 100644
index 0000000..a2382ac
--- /dev/null
+++ b/Runtime/Shaders/GraphicsCaps.cpp
@@ -0,0 +1,610 @@
+#include "UnityPrefix.h"
+#include "GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/SystemInfo.h"
+
+GraphicsCaps gGraphicsCaps;
+
+#if UNITY_EDITOR
+
+#include "Runtime/Graphics/GraphicsHelper.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Camera/Light.h"
+#include "Editor/Platform/Interface/EditorWindows.h"
+#include "Editor/Src/EditorUserBuildSettings.h"
+#include "Editor/Src/BuildPipeline/BuildTargetPlatformSpecific.h"
+#include "Runtime/Misc/PlayerSettings.h"
+#include "Runtime/Modules/ExportModules.h"
+
+static bool gDidInitializeOriginalCaps = false;
+GraphicsCaps EXPORT_COREMODULE gOriginalCaps;
+
+void InvalidateGraphicsStateInEditorWindows(); // EditorWindow
+
+
+#if GFX_SUPPORTS_D3D9
+void InitializeCombinerCapsD3D9(); // CombinerD3D.cpp
+#endif
+
+inline bool RarelySupportedFormat(RenderTextureFormat format)
+{
+ return (format == kRTFormatARGB4444) || (format == kRTFormatARGB1555);
+}
+
+bool CheckEmulationValid( const GraphicsCaps& origCaps, GraphicsCaps& emulatedCaps )
+{
+ if (origCaps.shaderCaps < emulatedCaps.shaderCaps)
+ return false;
+ if (!origCaps.hasFixedFunction && emulatedCaps.hasFixedFunction)
+ return false;
+ for (int i = 0; i < kRTFormatCount; ++i)
+ {
+ if (!RarelySupportedFormat((RenderTextureFormat)i))
+ if (!origCaps.supportsRenderTextureFormat[i] && emulatedCaps.supportsRenderTextureFormat[i])
+ return false;
+ }
+ if (origCaps.maxTexUnits < emulatedCaps.maxTexUnits)
+ return false;
+ if (!origCaps.has3DTexture && emulatedCaps.has3DTexture)
+ return false;
+ if (origCaps.npot < emulatedCaps.npot)
+ return false;
+ if (!origCaps.hasAutoMipMapGeneration && emulatedCaps.hasAutoMipMapGeneration)
+ return false;
+ if (!origCaps.hasRenderTargetStencil && emulatedCaps.hasRenderTargetStencil)
+ return false;
+
+ #if GFX_SUPPORTS_OPENGL
+ if (GetGfxDevice().GetRenderer() == kGfxRendererOpenGL)
+ {
+ if (!origCaps.gl.hasGLSL && emulatedCaps.gl.hasGLSL)
+ return false;
+ if (!origCaps.gl.hasTextureEnvCombine3ATI && emulatedCaps.gl.hasTextureEnvCombine3ATI)
+ return false;
+ }
+ #endif
+
+ return true;
+}
+
+void GraphicsCaps::ApplyEmulationSetingsOnly( const GraphicsCaps& origCaps, const Emulation emul )
+{
+ for (int q = 0; q < kTexFormatPCCount; ++q)
+ supportsTextureFormat[q] = true;
+
+ bool glesTargetAndroid = GetBuildTargetGroup(GetEditorUserBuildSettings().GetActiveBuildTarget()) == kPlatformAndroid;
+
+ switch( emul )
+ {
+ case kEmulNone:
+ *this = origCaps;
+ break;
+ case kEmulSM3:
+ // do not set texture units; just let all SM3.0 hardware use whatever they support
+ shaderCaps = kShaderLevel3;
+ supportsRenderTextureFormat[kRTFormatDepth] = true;
+ supportsRenderTextureFormat[kRTFormatARGBHalf] = true;
+ hasComputeShader = false;
+ // GL specific
+ #if GFX_SUPPORTS_OPENGL
+ gl.hasGLSL = true;
+ #endif
+ // names
+ rendererString = "Emulated Shader Model 3.0";
+ vendorString = "Emulated";
+ #if UNITY_WIN
+ fixedVersionString = "Direct3D 9.0c [emulated]";
+ #else
+ fixedVersionString = "OpenGL 2.0 [emulated]";
+ #endif
+ break;
+ case kEmulSM2:
+ shaderCaps = kShaderLevel2;
+ supportsRenderTextureFormat[kRTFormatDepth] = true;
+ supportsRenderTextureFormat[kRTFormatARGBHalf] = false;
+ maxTexUnits = 4;
+ maxTexCoords = 8;
+ hasComputeShader = false;
+ hasInstancing = false;
+ // GL specific
+ #if GFX_SUPPORTS_OPENGL
+ gl.hasGLSL = true;
+ #endif
+ // names
+ rendererString = "Emulated Shader Model 2.0";
+ vendorString = "Emulated";
+ #if UNITY_WIN
+ fixedVersionString = "Direct3D 9.0c [emulated]";
+ #else
+ fixedVersionString = "OpenGL 2.0 [emulated]";
+ #endif
+ break;
+ case kEmulGLES20:
+ shaderCaps = kShaderLevel3;
+ //hasSRGBReadWrite = glesTargetAndroid; // while only supported on handful devices, lets allow people to use it in emu
+ hasSRGBReadWrite = false;
+ hasComputeShader = false;
+ hasInstancing = false;
+ supportsTextureFormat[kTexFormatAlphaLum16] = false;
+ supportsTextureFormat[kTexFormatDXT1] = glesTargetAndroid;
+ supportsTextureFormat[kTexFormatDXT3] = glesTargetAndroid;
+ supportsTextureFormat[kTexFormatDXT5] = glesTargetAndroid;
+ supportsTextureFormat[kTexFormatPVRTC_RGB2] = true;
+ supportsTextureFormat[kTexFormatPVRTC_RGBA2] = true;
+ supportsTextureFormat[kTexFormatPVRTC_RGB4] = true;
+ supportsTextureFormat[kTexFormatPVRTC_RGBA4] = true;
+ supportsTextureFormat[kTexFormatETC_RGB4] = true;
+ supportsTextureFormat[kTexFormatATC_RGB4] = glesTargetAndroid;
+ supportsTextureFormat[kTexFormatATC_RGBA8] = glesTargetAndroid;
+ supportsRenderTextureFormat[kRTFormatARGB32] = true;
+ supportsRenderTextureFormat[kRTFormatARGBHalf] = true; // supported on high-end mobiles ;-)
+ hasRenderToTexture = true;
+ hasTiledGPU = true; // assume tiled GPU when emulating ES2
+ warnRenderTargetUnresolves = true;
+
+ // don't touch 16bit rt formats, as they depend on underlying hw
+
+ hasNativeShadowMap = false;
+ supportsRenderTextureFormat[kRTFormatDepth] = true;
+ supportsRenderTextureFormat[kRTFormatShadowMap] = false; // will use depth texture for shadows
+
+ // There is no fixed function texture units in ES2
+ maxTexUnits = maxTexImageUnits = maxTexCoords = 8;
+ maxMRTs = 1;
+ has3DTexture = false;
+
+ // gles2.0 has hard shadows.
+ disableSoftShadows = true;
+
+ hasRenderToCubemap = true;
+ npotRT = npot = kNPOTFull;
+ maxTextureSize = 4096;
+ maxCubeMapSize = 512;
+ // GL specific
+ #if GFX_SUPPORTS_OPENGL
+ gl.hasGLSL = true;
+ gl.hasTextureEnvCombine3ATI = false;
+ gl.hasTextureEnvCombine3NV = false;
+ #endif
+ // D3D9 specific
+ #if GFX_SUPPORTS_D3D9
+ d3d.hasTextureFormatL16 = false;
+ d3d.hasTextureFormatA8L8 = false;
+ d3d.d3dcaps.MaxSimultaneousTextures = maxTexUnits;
+ d3d.d3dcaps.RasterCaps &= ~D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS;
+ #endif
+ rendererString = "Emulated GPU running OpenGL ES 2.0";
+ vendorString = "Emulated";
+ fixedVersionString = "OpenGL ES 2.0 [emulated]";
+ break;
+
+ case kEmulXBox360:
+ case kEmulPS3:
+ shaderCaps = kShaderLevel3;
+ hasFixedFunction = false;
+
+ maxLights = 8;
+ maxMRTs = 1; //@TODO: possibly needs change for MRTs on console emulation
+ hasAnisoFilter = true;
+ maxAnisoLevel = 16;
+ hasMipLevelBias = true;
+
+ hasMultiSample = (emul == kEmulPS3);
+
+ hasComputeShader = false;
+ hasInstancing = false;
+
+ hasBlendSquare = true;
+ hasSeparateAlphaBlend = true;
+
+ hasS3TCCompression = true;
+ hasVertexTextures = true;
+
+ hasAutoMipMapGeneration = false;
+
+ maxTexImageUnits = 16;
+ maxTexCoords = 8;
+
+ maxTexUnits = 8;
+
+ maxTextureSize = 4096;
+ maxCubeMapSize = 512;
+
+ supportsRenderTextureFormat[kRTFormatARGB32] = true;
+ supportsRenderTextureFormat[kRTFormatDepth] = true;
+ supportsRenderTextureFormat[kRTFormatARGBHalf] = true;
+ supportsRenderTextureFormat[kRTFormatShadowMap] = (emul == kEmulPS3);
+
+ has3DTexture = true;
+ npotRT = npot = kNPOTFull;
+
+ hasRenderToTexture = true;
+ hasRenderToCubemap = true;
+ hasTwoSidedStencil = true;
+ hasSRGBReadWrite = true;
+ hasNativeDepthTexture = true;
+ hasStencilInDepthTexture = true;
+ hasNativeShadowMap = (emul == kEmulPS3);
+
+ rendererString = "Emulated Shader Model 3.0 (XBOX360/PS3)";
+ vendorString = "Emulated";
+ fixedVersionString = "XBOX360/PS3 (emulated)";
+ break;
+ case kEmulDX11_9_1:
+ case kEmulDX11_9_3:
+ shaderCaps = emul == kEmulDX11_9_3 ? kShaderLevel3 : kShaderLevel2;
+ hasFixedFunction = emul == kEmulDX11_9_3;
+
+ maxLights = 8;
+ maxMRTs = 1;
+ hasAnisoFilter = true;
+ maxAnisoLevel = emul == kEmulDX11_9_3 ? 16 : 2;
+ hasMipLevelBias = true;
+
+ hasMultiSample = true;
+
+ hasComputeShader = false;
+ hasInstancing = false;
+
+ hasBlendSquare = true;
+ hasSeparateAlphaBlend = false;
+
+ hasS3TCCompression = true;
+ hasVertexTextures = true;
+
+ maxTexImageUnits = 16;
+ maxTexCoords = 8;
+
+ maxTexUnits = 8;
+
+ maxTextureSize = emul == kEmulDX11_9_3 ? 4096 : 2048;
+ maxCubeMapSize = emul == kEmulDX11_9_3 ? 4096 : 512;
+
+ supportsRenderTextureFormat[kRTFormatARGB32] = true;
+ supportsRenderTextureFormat[kRTFormatDepth] = true;
+ supportsRenderTextureFormat[kRTFormatShadowMap] = true;
+
+ has3DTexture = true;
+ npotRT = npot = kNPOTRestricted;
+
+ hasTiledGPU = true; // assume tiled GPU when emulating DX11 9.x
+ warnRenderTargetUnresolves = true;
+
+ hasRenderToTexture = true;
+ hasRenderToCubemap = false;
+ hasTwoSidedStencil = true;
+ hasSRGBReadWrite = true;
+ hasNativeDepthTexture = true;
+ hasStencilInDepthTexture = true;
+ hasNativeShadowMap = true;
+#if GFX_SUPPORTS_D3D11
+ d3d11.hasShadows10Level9 = true;
+#endif
+
+ rendererString = "Emulated DirectX 11 9.1 No Fixed Function Shaders";
+ vendorString = "Emulated";
+ fixedVersionString = "DirectX 11 9.1 (emulated)";
+ break;
+ }
+}
+
+void GraphicsCaps::InitializeOriginalEmulationCapsIfNeeded()
+{
+ if (!gDidInitializeOriginalCaps)
+ {
+ gDidInitializeOriginalCaps = true;
+ SET_ALLOC_OWNER(NULL);
+ gOriginalCaps = *this;
+ }
+}
+
+void GraphicsCaps::ResetOriginalEmulationCaps()
+{
+ gDidInitializeOriginalCaps = false;
+}
+
+bool GraphicsCaps::CheckEmulationSupported( Emulation emul )
+{
+ InitializeOriginalEmulationCapsIfNeeded();
+
+ GraphicsCaps emulatedCaps = gOriginalCaps;
+ emulatedCaps.ApplyEmulationSetingsOnly(gOriginalCaps, emul);
+
+ return CheckEmulationValid(gOriginalCaps, emulatedCaps);
+}
+
+bool EmulationWants16bitDepth( GraphicsCaps::Emulation emul )
+{
+ if( (emul == GraphicsCaps::kEmulGLES20)
+ && GetBuildTargetGroup(GetEditorUserBuildSettings().GetActiveBuildTarget()) == kPlatformAndroid
+ && !GetPlayerSettings().GetUse24BitDepthBuffer()
+ )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+GraphicsCaps::Emulation _CurrentEmulation = GraphicsCaps::kEmulNone;
+
+void GraphicsCaps::ApplyEmulationSettingsAffectingGUI()
+{
+ // well, not the best solution...
+
+ if( EmulationWants16bitDepth(_CurrentEmulation) )
+ GUIView::RecreateAllOnDepthBitsChange(32, 16);
+ else
+ GUIView::RecreateAllOnDepthBitsChange(16, 32);
+}
+
+GraphicsCaps::Emulation GraphicsCaps::GetEmulation() const
+{
+ return _CurrentEmulation;
+}
+
+void GraphicsCaps::SetEmulation( Emulation emul )
+{
+ if( emul == _CurrentEmulation )
+ return;
+
+ if (!CheckEmulationSupported(emul))
+ {
+ ErrorString("Attempting to switch to unsupported emulation");
+ return;
+ }
+
+ // must be released before the emulation is set, since the memory usage is estimated from the deviceCaps
+ RenderTexture::ReleaseAll();
+
+ _CurrentEmulation = emul;
+
+ InitializeOriginalEmulationCapsIfNeeded();
+
+ *this = gOriginalCaps;
+
+ GfxDevice& device = GetGfxDevice();
+ device.InvalidateState();
+ ShaderLab::SubProgram* nullShaders[kShaderTypeCount] = {0};
+ GraphicsHelper::SetShaders (device, nullShaders, 0);
+ InvalidateGraphicsStateInEditorWindows();
+
+ ApplyEmulationSettingsAffectingGUI();
+ ApplyEmulationSetingsOnly(gOriginalCaps, emul);
+ SharedCapsPostInitialize();
+
+ device.CommonReloadResources(GfxDevice::kReleaseRenderTextures | GfxDevice::kReloadShaders | GfxDevice::kReloadTextures);
+
+ #if GFX_SUPPORTS_D3D9
+ InitializeCombinerCapsD3D9();
+ #endif
+
+ // Give a chance for image effects (which mostly execute in edit mode
+ // as well) to recheck hardware support for them.
+ MonoBehaviour::RestartExecuteInEditModeScripts();
+
+ // update all lights
+ std::vector<Light*> lights;
+ Object::FindObjectsOfType (&lights);
+ for( std::vector<Light*>::iterator i = lights.begin(); i != lights.end(); ++i )
+ {
+ Light& light = (**i);
+ light.Precalc();
+ }
+}
+
+bool GraphicsCaps::IsEmulatingGLES20() const
+{
+ return _CurrentEmulation == kEmulGLES20;
+}
+
+#endif // UNITY_EDITOR
+
+
+std::string GraphicsCaps::CheckGPUSupported() const
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+ GfxDeviceRenderer type = GetGfxDevice().GetRenderer();
+
+ // OpenGL
+# if GFX_SUPPORTS_OPENGL
+ if (type == kGfxRendererOpenGL)
+ {
+ extern bool QueryExtensionGL (const char* ext);
+
+ // OS X 10.5 on GMA 950 has GL1.2. When we drop 10.5 support we can move
+ // up to GL1.4.
+ if (gl.glVersion < 12)
+ {
+ return Format("OpenGL 1.2 is required. Your GPU (%s) only supports OpenGL %i.%i", rendererString.c_str(), gl.glVersion/10, gl.glVersion%10);
+ }
+ // Require GLSL
+ if (!gl.hasGLSL)
+ {
+ return Format("OpenGL GLSL support is required. Your GPU (%s) does not support it", rendererString.c_str());
+ }
+ // Require ARB VP/FP
+ if (!QueryExtensionGL("GL_ARB_vertex_program") || !QueryExtensionGL("GL_ARB_fragment_program"))
+ {
+ return Format("OpenGL vertex/fragment program support is required. Your GPU (%s) does not support it", rendererString.c_str());
+ }
+
+ // Require VBO, multitexture, ...
+ if (gl.glVersion < 15 && !QueryExtensionGL("GL_ARB_vertex_buffer_object"))
+ {
+ return Format("OpenGL vertex buffer support is required. Your GPU (%s) does not support it", rendererString.c_str());
+ }
+ if (gl.glVersion < 13 && !QueryExtensionGL ("GL_ARB_multitexture"))
+ {
+ return Format("OpenGL multitexture support is required. Your GPU (%s) does not support it", rendererString.c_str());
+ }
+ if (gl.glVersion < 13 && !QueryExtensionGL ("GL_ARB_texture_cube_map"))
+ {
+ return Format("OpenGL cubemap support is required. Your GPU (%s) does not support it", rendererString.c_str());
+ }
+ if (gl.glVersion < 13 && !QueryExtensionGL ("GL_ARB_texture_env_dot3"))
+ {
+ return Format("OpenGL dot3 combiner support is required. Your GPU (%s) does not support it", rendererString.c_str());
+ }
+ }
+# endif // if GFX_SUPPORTS_OPENGL
+
+
+ // D3D9
+# if GFX_SUPPORTS_D3D9
+ if (type == kGfxRendererD3D9)
+ {
+ // Require SM2.0 at least.
+ // Note, vertex shaders can be reported as zero in case of software vertex processing.
+ // That is a valid & supported case (d3d runtime will run shaders on the CPU, but
+ // otherwise they will behave as supported).
+ const int kShaderVersion20 = (2 << 8) + 0;
+ const int d3dVS = LOWORD(d3d.d3dcaps.VertexShaderVersion);
+ const int d3dPS = LOWORD(d3d.d3dcaps.PixelShaderVersion);
+ if ((d3dVS != 0 && d3dVS < kShaderVersion20) || (d3dPS < kShaderVersion20))
+ {
+ return Format("DirectX9 GPU (Shader Model 2.0) is required.\r\nYour GPU (%s)\r\nonly supports Shader Model %i.%i", rendererString.c_str(), d3dPS>>8, d3dPS&0xFF);
+ }
+ }
+# endif // if GFX_SUPPORTS_D3D9
+
+ UNUSED(type);
+
+#endif
+ return ""; // all ok
+}
+
+
+void GraphicsCaps::SharedCapsPostInitialize()
+{
+ // do some sanity checks
+ Assert (!hasStencilInDepthTexture || (hasStencilInDepthTexture && hasNativeDepthTexture)); // stencil in depth texture implies native depth texture
+
+ // requirements for deferred lighting
+ const int systemMemoryMB = systeminfo::GetPhysicalMemoryMB();
+
+ hasPrePassRenderLoop =
+# if GFX_SUPPORTS_RENDERLOOP_PREPASS
+ (shaderCaps >= kShaderLevel3) && // needs SM3.0
+ hasRenderToTexture && // needs render textures
+ supportsRenderTextureFormat[kRTFormatDepth] && // needs depth RT
+ hasRenderTargetStencil && // needs stencil in RT
+ hasTwoSidedStencil && // needs two sided stencil
+ (systemMemoryMB==0 || systemMemoryMB >= 450) // disable on old devices that have <512MB RAM (check for zero in case platform doesn't implement it at all...)
+# else
+ false
+# endif
+ ;
+}
+
+
+void GraphicsCaps::InitWithBuildVersion()
+{
+ #if GFX_SUPPORTS_OPENGL
+ // Before 4.2, we never used native shadow maps on GL, so keep that behavior.
+ if (GetGfxDevice().GetRenderer() == kGfxRendererOpenGL)
+ {
+ // First restore original flag (same player instance can be
+ // reused across different content versions)
+ hasNativeShadowMap = gl.originalHasNativeShadowMaps;
+ supportsRenderTextureFormat[kRTFormatShadowMap] = hasNativeShadowMap;
+
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1))
+ {
+ hasNativeShadowMap = false;
+ supportsRenderTextureFormat[kRTFormatShadowMap] = false;
+ }
+ }
+ #endif
+}
+
+
+#if GFX_SUPPORTS_OPENGL || GFX_SUPPORTS_OPENGLES20
+bool FindGLExtension (const char* extensions, const char* ext)
+{
+ // Extension names should not have spaces, be NULL or empty
+ if (!ext || ext[0] == 0)
+ return false;
+ if (strchr(ext, ' '))
+ return false;
+
+ Assert (extensions != NULL);
+ if (!extensions)
+ return false;
+
+ const char* start = extensions;
+ for (;;)
+ {
+ const char* where = strstr (start, ext);
+ if (!where)
+ break;
+ const char* terminator = where + strlen (ext);
+ if (where == start || *(where - 1) == ' ')
+ {
+ if (*terminator == ' ' || *terminator == '\0')
+ return true;
+ }
+ start = terminator;
+ }
+ return false;
+}
+#endif // GFX_SUPPORTS_OPENGL || GFX_SUPPORTS_OPENGLES20
+
+#if GFX_SUPPORTS_OPENGLES20
+#if GFX_SUPPORTS_OPENGLES20
+# include "Runtime/GfxDevice/opengles20/IncludesGLES20.h"
+#endif
+
+bool QueryExtension (const char* ext)
+{
+ static const char* extensions = NULL;
+ if (!extensions)
+ extensions = (const char*)glGetString(GL_EXTENSIONS);
+ if (!extensions)
+ return false;
+ return FindGLExtension (extensions, ext);
+}
+
+#endif // GFX_SUPPORTS_OPENGLES20
+
+
+
+// -------------------------------------------------------------------
+
+
+
+#if ENABLE_UNIT_TESTS
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (GraphicsCapsTest)
+{
+ TEST (GraphicsCaps_DeviceIDs)
+ {
+ #if UNITY_EDITOR
+ if (gGraphicsCaps.GetEmulation() != GraphicsCaps::kEmulNone)
+ return;
+ #endif
+
+ const int vendorID = gGraphicsCaps.vendorID;
+ if (vendorID == 0)
+ return;
+
+ std::string vendor = ToLower(gGraphicsCaps.vendorString);
+
+ if (vendorID == 0x10de)
+ {
+ CHECK (vendor.find("nvidia") != std::string::npos);
+ }
+ if (vendorID == 0x1002)
+ {
+ CHECK (vendor.find("ati") != std::string::npos || vendor.find("amd") != std::string::npos);
+ }
+ if (vendorID == 0x8086)
+ {
+ CHECK (vendor.find("intel") != std::string::npos);
+ }
+ }
+}
+
+#endif // #if ENABLE_UNIT_TESTS
diff --git a/Runtime/Shaders/GraphicsCaps.h b/Runtime/Shaders/GraphicsCaps.h
new file mode 100644
index 0000000..0e1e37d
--- /dev/null
+++ b/Runtime/Shaders/GraphicsCaps.h
@@ -0,0 +1,435 @@
+#pragma once
+
+#include <string>
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+enum ShaderCapsLevel
+{
+ kShaderLevel2 = 20, // shader model 2.0 & 2.x
+ kShaderLevel3 = 30, // shader model 3.0
+ kShaderLevel4 = 40, // shader model 4.0
+ kShaderLevel4_1 = 41, // shader model 4.1
+ kShaderLevel5 = 50, // shader model 5.0
+};
+
+enum NPOTCaps
+{
+ kNPOTNone = 0, // no NPOT texture capability
+ kNPOTRestricted, // NPOT available with restrictions (no mips, no wrap mode, no compression)
+ kNPOTFull, // NPOT available, no restrictions
+};
+
+
+#if GFX_SUPPORTS_D3D9
+#include "PlatformDependent/Win/WinDriverUtils.h"
+#include "Runtime/GfxDevice/d3d/D3D9Includes.h"
+struct GraphicsCapsD3D9
+{
+ D3DCAPS9 d3dcaps;
+ bool hasBaseTextureFormat[kTexFormatPCCount];
+ bool hasTextureFormatA8;
+ bool hasTextureFormatL8;
+ bool hasTextureFormatA8L8;
+ bool hasTextureFormatL16;
+ bool hasATIDepthFormat16; // Has ATI DF16 render texture format?
+ bool hasNVDepthFormatINTZ;
+ bool hasNVDepthFormatRAWZ;
+ bool hasNULLFormat;
+ bool hasDepthResolveRESZ;
+
+ // ---- hardware/driver workarounds
+ bool slowINTZSampling; // very slow at using depth testing and sampling INTZ at the same time; prefer RESZ copy into separate surface
+};
+#endif
+
+#if GFX_SUPPORTS_D3D11
+enum DX11FeatureLevel {
+ kDX11Level9_1,
+ kDX11Level9_2,
+ kDX11Level9_3,
+ kDX11Level10_0,
+ kDX11Level10_1,
+ kDX11Level11_0,
+ kDX11LevelCount
+};
+
+struct GraphicsCapsD3D11
+{
+ UInt32 msaa; // regular backbuffer, bit for each sample count we want
+ UInt32 msaaSRGB; // sRGB backbuffer, bit for each sample count we want
+ DX11FeatureLevel featureLevel;
+ bool hasShadows10Level9;
+ bool buggyPartialPrecision10Level9; // half precision broken on 10level9 shaders (crashes)
+};
+#endif
+
+#if GFX_SUPPORTS_OPENGL
+struct GraphicsCapsGL
+{
+ int glVersion; // e.g. 20 for 2.0, 43 for 4.3
+ int vertexAttribCount; // How many generic vertex attributes are supported?
+
+ int nativeVPInstructions;
+ int nativeFPInstructions;
+ int nativeFPTexInstructions;
+ int nativeFPALUInstructions;
+ int nativeFPTemporaries;
+
+ int maxSamples;
+
+ bool hasGLSL; // GLSL
+
+ bool hasTextureEnvCombine3ATI; // GL_ATI_texture_env_combine3 - the preferred way
+ bool hasTextureEnvCombine3NV; // GL_NV_texture_env_combine4 - fallback to emulate with TNT combiners
+
+ #if UNITY_WIN
+ bool hasWglARBPixelFormat;
+ bool hasWglSwapControl;
+ #endif
+
+ bool hasArbMapBufferRange;
+ bool hasArbSync;
+
+ #if UNITY_OSX
+ bool hasAppleFence;
+ bool hasAppleFlushBufferRange;
+ #endif
+
+ bool hasFrameBufferBlit;
+
+ // ---- hardware/driver workarounds
+ bool buggyArbPrecisionHint; // ARB precision hint in FPs is broken, postprocess programs to remove it
+ bool cacheFPParamsWithEnvs; // For fragment programs, use Env parameters instead of Local ones, and cache their values
+ bool forceColorBufferWithDepthFBO; // For depth FBO, always use color buffer, with no color buffer it will be wrong
+ bool force24DepthForFBO; // 16 bit depth does not work for FBO depth textures
+ bool buggyPackedDepthStencil;
+ bool mustWriteToDepthBufferBeforeClear; // depth buffer will contain garbage if it isn't written to at least once per frame.
+ bool originalHasNativeShadowMaps;
+};
+#endif
+
+
+
+#if GFX_SUPPORTS_OPENGLES20
+struct GraphicsCapsGLES20
+{
+ int maxAttributes;
+ int maxVaryings;
+ int maxSamples;
+ bool hasGLSL;
+ bool hasVBOOrphaning;
+ bool slowAlphaTest;
+ bool slowDynamicVBO; // will force dynamic draw from mem (except static batch indices)
+ bool forceStaticBatchFromMem; // will force static batch indices to be used from mem
+
+ // features
+
+ bool hasBinaryShaders;
+ bool has24DepthForFBO;
+ bool hasMapbuffer;
+ bool hasMapbufferRange;
+ bool hasDebugMarkers;
+ bool hasDiscardFramebuffer;
+ bool hasHalfLinearFilter;
+
+ bool hasAppleMSAA;
+ bool hasImgMSAA;
+
+ // bugs workaround
+
+ bool buggyColorMaskBlendMSAA; // on some ios msaa+color mask+blend results in crazy rendering artefacts
+ bool buggyDisableVAttrKeepsActive; // on some mali drivers vattrs are kept active even after disabling them
+ bool forceHighpFSPrec; // on some nvidia drivers there is a bug: they fail to report samplers if default prec is not high
+ bool buggyVFetchPatching; // some adreno drivers fails to patch vfetch instructions (if vertex layout changed)
+ bool buggyVprogTextures; // some mali drivers have crashes in shader compiler if vprog texture is used
+ bool needToRenderWatermarkDueToDrawFromMemoryBuggy; // on some pvr it may crash when rendering from mem (it seems one shader used is culprit)
+ bool needFlushAfterTextureUpload;
+
+ // vendor specific
+
+ bool hasNLZ; // nvidia non-linear z
+ bool hasNVMRT; // nvidia mrt support
+ bool hasAlphaTestQCOM; // adreno special gles11 alpha test
+};
+#endif
+
+#if GFX_SUPPORTS_OPENGLES30
+struct GraphicsCapsGLES30
+{
+ GraphicsCapsGLES30 (void)
+ : maxAttributes (16)
+ , maxVaryings (16)
+ , maxSamples (4)
+ , useProgramBinary (true)
+ , useMapBuffer (true)
+ , useTFSkinning (true)
+ , hasDebugMarkers (false)
+ , hasAlphaTestQCOM (false)
+ {
+ }
+
+ // Limits.
+ int maxAttributes;
+ int maxVaryings;
+ int maxSamples; // Max sample count for renderbuffers.
+
+ // Feature configuration.
+ bool useProgramBinary; // ProgramBinary support is in core, but in case of we want to ignore it on some platforms.
+ bool useMapBuffer; // One some platforms mapping buffers is very slow.
+ bool useTFSkinning; // Use transform feedback for skinning.
+
+ // Extension bits.
+ bool hasDebugMarkers;
+ bool hasAlphaTestQCOM;
+};
+#endif
+
+#if GFX_SUPPORTS_OPENGL || GFX_SUPPORTS_OPENGLES20
+bool FindGLExtension (const char* extensions, const char* ext);
+#endif
+
+#if GFX_SUPPORTS_OPENGLES20
+bool QueryExtension (const char* ext);
+#endif
+
+
+struct GraphicsCaps
+{
+ GraphicsCaps()
+ {
+ ::memset(this, 0x00, sizeof(GraphicsCaps));
+
+ for (int i = 0 ; i < kTexFormatPCCount; ++i)
+ supportsTextureFormat[i] = true;
+
+ // Default RT format is always enabled until we don't have render-to-texture at all
+ // It is dummy value used to select most appropriate rt format
+ supportsRenderTextureFormat[kRTFormatDefault] = true;
+ supportsRenderTextureFormat[kRTFormatDefaultHDR] = true;
+
+ new (&rendererString) StaticString();
+ new (&vendorString) StaticString();
+ new (&driverVersionString) StaticString();
+ new (&fixedVersionString) StaticString();
+ new (&driverLibraryString) StaticString();
+
+ shaderCaps = kShaderLevel2;
+ videoMemoryMB = 16.0f;
+
+ maxLights = 8;
+ maxAnisoLevel = 1;
+ maxTexImageUnits = 8;
+ maxTexCoords = 1;
+ maxTexUnits = 1;
+
+ maxTextureSize = 256;
+ maxCubeMapSize = 64;
+ maxRenderTextureSize = 128;
+ maxMRTs = 1;
+
+ hasFixedFunction = true;
+ npotRT = npot = kNPOTNone;
+ hasNonFullscreenClear = true;
+ hasShadowCollectorPass = true;
+ hasHighPrecisionTextureCombiners = true;
+
+ hasGrabPass = true;
+ }
+
+ void SharedCapsPostInitialize();
+
+ // ---------- caps common for all devices
+ StaticString rendererString; // graphics card name
+ StaticString vendorString; // graphics card vendor name
+ StaticString driverVersionString; // (GL) version as reported by the driver
+ StaticString fixedVersionString; // (GL) correct GL version appended in front by us
+ StaticString driverLibraryString; // Name of driver's DLL and version
+ int vendorID;
+ int rendererID;
+
+ ShaderCapsLevel shaderCaps; // Generic "shader capability level"
+
+ float videoMemoryMB; // Approx. amount of video memory in MB. Used to limit texture, render target and so on sizes to sane values.
+
+ int maxVSyncInterval; // Max frames that device can wait for vertical blank
+ int maxLights; // vertex light count
+ int maxAnisoLevel;
+ int maxTexImageUnits; // Part of arb-fragment program (otherwise they match textureUnitCount)
+ int maxTexCoords;
+ int maxTexUnits;
+
+ int maxTextureSize;
+ int maxCubeMapSize;
+ int maxRenderTextureSize; // usually maxTextureSize, except on some crappy cards
+
+ int maxMRTs;
+
+ bool hasAnisoFilter; // has anisotropic filtering?
+ bool hasMipLevelBias; // can apply bias for mips?
+ bool hasMipMaxLevel; // can specify max mip level
+
+ bool hasFixedFunction; // Supports T&L and texture combine stages
+
+ bool hasMultiSample;
+ bool hasMultiSampleAutoResolve; // uses texture instead, and under-the-hood rb will be auto-resolved to it
+
+ bool hasBlendSquare;
+ bool hasSeparateAlphaBlend;
+ bool hasBlendSub; // both sub and revsub
+ bool hasBlendMinMax; // max,min
+ bool hasBlendLogicOps; // kBlendOpLogical*
+
+ bool hasS3TCCompression;
+ bool hasVertexTextures; // Can read textures in a vertex shader?
+
+ bool hasTimerQuery;
+
+ bool hasAutoMipMapGeneration; // can auto-generate mipmaps for textures?
+
+ bool supportsTextureFormat[kTexFormatTotalCount];
+ bool supportsRenderTextureFormat[kRTFormatCount];
+
+ bool has3DTexture;
+
+ NPOTCaps npot;
+ NPOTCaps npotRT;
+
+ bool hasSRGBReadWrite;
+ bool hasComputeShader;
+ bool hasInstancing;
+ bool hasNonFullscreenClear; // Can do clears on non-full screen? (e.g. D3D11 can not)
+
+ bool hasRenderToTexture; // We have render-to-texture functionality
+ bool hasRenderToCubemap; // We have render-to-cubemap functionality
+ bool hasRenderTo3D; // We have render-to-volume functionality
+ bool hasStencil; // Stencil support good enough to be used by the users via the shaderlab state.
+ bool hasRenderTargetStencil; // Has sane way of having stencil buffer on render targets
+ bool hasNativeDepthTexture; // Depth textures come from actual depth buffer
+ bool hasStencilInDepthTexture; // Has native depth texture AND stencil buffer of it can be used at the same time
+ bool hasNativeShadowMap; // Depth textures have native shadow map comparison sampling
+ bool hasShadowCollectorPass; // mobiles don't usage collector pass, go direct SMs!
+ bool hasTiledGPU; // Uses tiled rendering (clear/discard preferred!)
+
+ bool hasTwoSidedStencil;
+ bool hasHighPrecisionTextureCombiners;
+
+ bool has16BitFloatVertex; // Supports at least 2D and 4D vertex channels as 16 bit floats
+ bool needsToSwizzleVertexColors; // Should vertex colors be passed as B,G,R,A bytes instead of R,G,B,A
+
+ bool disableSubTextureUpload; // Can we do UploadTextureSubData2D?
+ bool warnRenderTargetUnresolves; // Warn when doing RT un-resolves
+
+ bool hasGrabPass; // Can read from active render target back into a render texture?
+
+ // cross platform caps initialized in SharedCapsPostInitialize
+ bool hasPrePassRenderLoop;
+
+ bool SupportsRGBM() const { return maxTexUnits >= 3 && hasHighPrecisionTextureCombiners; }
+
+ std::string CheckGPUSupported() const;
+
+ // ---- hardware/driver workarounds for all renderers
+
+ bool disableSoftShadows; // Soft shadows should work, but the driver is buggy on those shaders
+ bool buggyCameraRenderToCubemap;// Render to cubemap sort-of-does-not-work (but works for point light shadows).
+ bool buggyFullscreenFSAA; // FSAA in fullscreen is buggy.
+ bool buggyTextureBothColorAndDepth; // Can't have render target where both color & depth will be sampled as textures
+ bool buggyDynamicVBOWithTangents; // DynamicVBO with tangents is buggy
+ bool buggyMipmappedCubemaps; // Cubemaps with mipmaps are buggy; keep only 1st mip level
+ bool buggyMipmapped3DTextures; // 3D textures with mipmaps are buggy; keep only 1st mip level
+ bool buggyShadowMapBilinearSampling; // Never use hardware bilinear PCF on shadow maps due to bugs?
+ bool buggySpotNativeShadowMap; // Never use hardware shadow maps on spot lights
+ bool buggyTimerQuery; // Has timer queries, but they aren't very good!
+
+ // ---- caps specific for renderers
+
+#if GFX_SUPPORTS_OPENGL
+ GraphicsCapsGL gl;
+#endif
+#if GFX_SUPPORTS_OPENGLES20
+ GraphicsCapsGLES20 gles20;
+#endif
+#if GFX_SUPPORTS_D3D9
+ GraphicsCapsD3D9 d3d;
+#endif
+#if GFX_SUPPORTS_D3D11
+ GraphicsCapsD3D11 d3d11;
+#endif
+#if GFX_SUPPORTS_OPENGLES30
+ GraphicsCapsGLES30 gles30;
+#endif
+
+ #if GFX_SUPPORTS_OPENGL
+ void InitGL();
+ #endif
+ #if GFX_SUPPORTS_D3D9
+ void InitD3D9();
+ #endif
+ #if GFX_SUPPORTS_D3D11
+ void InitD3D11();
+ #endif
+ #if GFX_SUPPORTS_NULL
+ void InitNull();
+ #endif
+ #if GFX_SUPPORTS_MOLEHILL
+ void InitMolehill();
+ #endif
+ #if GFX_SUPPORTS_XENON
+ void InitXenon();
+ #endif
+ #if GFX_SUPPORTS_GCM
+ void InitGcm();
+ UInt32 videoMemoryStart;
+ #endif
+ #if GFX_SUPPORTS_HOLLYWOOD
+ void InitHollywood();
+ #endif
+ #if GFX_SUPPORTS_OPENGLES20
+ void InitGLES20();
+ #endif
+ #if GFX_SUPPORTS_OPENGLES30
+ void InitGLES30();
+ #endif
+
+ void InitWithBuildVersion();
+
+
+ // Implemented in GraphicsCaps.cpp
+ #if UNITY_EDITOR
+ enum Emulation {
+ kEmulNone, // whatever card naturally has
+ kEmulSM3, // Shader Model 3.0
+ kEmulSM2, // Shader Model 2.0, but with 4 texture stages
+ kEmulGLES20, // OpenGL ES 2.0
+ kEmulXBox360,
+ kEmulPS3,
+ kEmulDX11_9_1, // DirectX 11 Feature Level 9.1, No Fixed Function shaders (Shader Model 2)
+ kEmulDX11_9_3, // DirectX 11 Feature Level 9.1, Fixed Function shaders (Shader Model 3)
+ kEmulCount
+ };
+ Emulation GetEmulation() const;
+ void SetEmulation( Emulation emul );
+ bool IsEmulatingGLES20() const;
+ bool CheckEmulationSupported( Emulation emul );
+ void InitializeOriginalEmulationCapsIfNeeded();
+ void ResetOriginalEmulationCaps();
+
+ static void ApplyEmulationSettingsAffectingGUI();
+
+ private:
+ void ApplyEmulationSetingsOnly( const GraphicsCaps& origCaps, const Emulation emul );
+ #endif
+
+
+private:
+
+ #if GFX_SUPPORTS_OPENGL
+ void AdjustBuggyVersionGL( int& version, int& major, int& minor ) const;
+ void DetectDriverBugsGL( int version );
+ #endif
+ #if GFX_SUPPORTS_D3D9
+ void DetectDriverBugsD3D9( UInt32 vendorCode, const windriverutils::VersionInfo& version );
+ #endif
+};
+
+extern GraphicsCaps gGraphicsCaps;
diff --git a/Runtime/Shaders/Material.cpp b/Runtime/Shaders/Material.cpp
new file mode 100644
index 0000000..3e6aaf5
--- /dev/null
+++ b/Runtime/Shaders/Material.cpp
@@ -0,0 +1,1096 @@
+#include "UnityPrefix.h"
+#include "Material.h"
+#include "Shader.h"
+#include "ShaderNameRegistry.h"
+#include "External/shaderlab/Library/properties.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Graphics/ProceduralMaterial.h"
+#include "Runtime/GfxDevice/GfxDisplayList.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Profiler/Profiler.h"
+
+#if UNITY_EDITOR
+#include "External/shaderlab/Library/SLParserData.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/Backend/ScriptingInvocation.h"
+
+extern ShaderLab::ShaderState* g_EditorPixelShaderOverride;
+#endif
+
+
+PROFILER_INFORMATION(gSetShadowCasterPass, "Shader.SetShadowCasterPass", kProfilerRender);
+PROFILER_INFORMATION(gSetShadowCollectorPass, "Shader.SetShadowCollectorPass", kProfilerRender);
+
+
+using namespace std;
+
+namespace Unity
+{
+
+template<class TransferFunc>
+void Material::Transfer (TransferFunc& transfer)
+{
+ transfer.SetVersion (3);
+ Super::Transfer (transfer);
+ transfer.Transfer (m_Shader, "m_Shader");
+ transfer.Transfer (m_ShaderKeywords, "m_ShaderKeywords");
+ transfer.Transfer (m_CustomRenderQueue, "m_CustomRenderQueue");
+
+#if UNITY_EDITOR
+ if (transfer.IsBuildingTargetPlatform(kBuild_Android) && m_Shader && m_Shader->HasClip())
+ {
+ DebugStringToFile ( "Shader is using clip instruction (usually caused by alpha test). It might cause problems on some Qualcomm/Adreno drivers.",
+ 0, __FILE__, __LINE__, kAssetImportWarning, GetInstanceID()
+ );
+
+ }
+#endif
+
+ // Cull unused properties when making build!
+ #if UNITY_EDITOR
+ if ((transfer.GetFlags () & kBuildPlayerOnlySerializeBuildProperties))
+ {
+ Shader *shader = m_Shader;
+ if (shader)
+ {
+ UnityPropertySheet tempProps = m_SavedProperties;
+ tempProps.CullUnusedProperties (shader->GetParsedForm());
+ transfer.Transfer (tempProps, "m_SavedProperties");
+ }
+ else
+ {
+ UnityPropertySheet tempProps;
+ transfer.Transfer (tempProps, "m_SavedProperties");
+ }
+ }
+ else
+ #endif
+ {
+ TRANSFER (m_SavedProperties);
+ }
+}
+
+
+void Material::Reset()
+{
+ Super::Reset();
+
+ m_CustomRenderQueue = -1;
+
+ ClearProperties();
+ m_SavedProperties = UnityPropertySheet();
+
+ Shader *shader = m_Shader;
+ if( !shader )
+ shader = Shader::GetDefault();
+
+ BuildShaderKeywordSet (); // build keywords before properties, so that pass hashing gets correct keywords
+ BuildProperties ();
+#if UNITY_EDITOR
+ ResetDefaultTextures (true);
+ if (GetCachedScriptingObject())
+ ApplyMaterialPropertyDrawers();
+#endif
+}
+
+
+int Material::GetActualRenderQueue() const
+{
+ // Use custom render queue if set (case 548478)
+ if (m_CustomRenderQueue >= 0)
+ return m_CustomRenderQueue;
+
+ const Shader* shader = GetShader();
+ Assert (shader);
+ return shader->GetShaderLabShader()->GetRenderQueue();
+}
+
+
+void Material::BuildProperties ()
+{
+ SET_ALLOC_OWNER(NULL);
+ SAFE_RELEASE_LABEL(m_Properties,kMemShader);
+ Shader *shader = m_Shader;
+ if( !shader )
+ shader = Shader::GetDefault();
+
+ // This happens on Flash when loading some materials - at AwakeFromLoad
+ // time, the shader is not parsed yet (?). Just return in that case, the
+ // properties will be build later on demand. Worst that can happen is that
+ // for one frame shadow casters might get batched slightly wrong.
+ if (!shader->GetShaderLabShader())
+ return;
+
+ // Build the shaderlab property sheet.
+ m_Properties = shader->MakeProperties();
+ m_Properties->SetOwnerMaterial(this);
+
+ // Make sure our serialized properties (m_SavedProperties) have
+ // all the properties from the shader's properties block.
+ // (we might not be up-to-date if the shader has changed and added more
+ // properties).
+ m_SavedProperties.AddNewShaderlabProps (*shader->GetShaderLabShader()->GetDefaultProperties());
+
+ /// Get the properties from the material.
+ m_SavedProperties.AssignDefinedPropertiesTo (*m_Properties);
+
+ if( m_Shader )
+ m_Shader->AddMaterialUser( m_ShaderUserNode );
+
+ UpdateHashes();
+}
+
+
+void Material::UpdateHashesOnPropertyChange (ShaderLab::FastPropertyName name)
+{
+ Shader* shader = m_Shader;
+ if (!shader || !m_Properties)
+ return;
+ ShaderLab::Pass* passCaster = shader->GetShadowCasterPass();
+ if (passCaster)
+ {
+ if (passCaster->IsPropertyAffectingPass(name))
+ m_ShadowCasterHash = passCaster->ComputePassValuesHash (m_ShaderKeywordSet, m_Properties);
+ }
+ ShaderLab::Pass* passCollector = shader->GetShadowCollectorPass();
+ if (passCollector)
+ {
+ if (passCollector->IsPropertyAffectingPass(name))
+ m_ShadowCollectorHash = passCollector->ComputePassValuesHash (m_ShaderKeywordSet, m_Properties);
+ }
+
+ const dynamic_array<int>& propsAffectingBlocks = shader->GetShaderLabShader()->GetPropsAffectingStateBlocks();
+ if (std::find(propsAffectingBlocks.begin(), propsAffectingBlocks.end(), name.index) != propsAffectingBlocks.end())
+ {
+ m_StateKeyHash = ShaderLab::ComputeStateBlockValuesHash (propsAffectingBlocks, m_Properties);
+ shader->GetShaderLabShader()->CreateStateBlocksForKey (m_StateKeyHash, m_Properties);
+ }
+}
+
+void Material::UpdateHashes ()
+{
+ m_ShadowCasterHash = 0;
+ m_ShadowCollectorHash = 0;
+ m_StateKeyHash = 0;
+ Shader* shader = m_Shader;
+ if (!shader || !m_Properties)
+ return;
+ ShaderLab::Pass* passCaster = shader->GetShadowCasterPass();
+ if (passCaster)
+ m_ShadowCasterHash = passCaster->ComputePassValuesHash (m_ShaderKeywordSet, m_Properties);
+ ShaderLab::Pass* passCollector = shader->GetShadowCollectorPass();
+ if (passCollector)
+ m_ShadowCollectorHash = passCollector->ComputePassValuesHash (m_ShaderKeywordSet, m_Properties);
+
+ m_StateKeyHash = ShaderLab::ComputeStateBlockValuesHash (shader->GetShaderLabShader()->GetPropsAffectingStateBlocks(), m_Properties);
+ shader->GetShaderLabShader()->CreateStateBlocksForKey (m_StateKeyHash, m_Properties);
+}
+
+
+void Material::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ BuildShaderKeywordSet (); // build keywords before properties, so that pass hashing gets correct keywords
+
+ ClearProperties ();
+
+ // Enforce that everything is preloaded in the player, reduces hiccups at runtime
+ // Don't do that in the editor because it can easily run out of memory and increase build times.
+ #if !UNITY_EDITOR
+ BuildProperties();
+ #endif
+}
+
+
+Material::Material(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_ShaderUserNode(this)
+{
+ m_Properties = NULL;
+ m_PropertiesDirty = false;
+ m_Shader = 0;
+ m_CustomRenderQueue = -1;
+ m_ShadowCasterHash = 0;
+ m_ShadowCollectorHash = 0;
+ m_StateKeyHash = 0;
+}
+
+Material::~Material()
+{
+ SAFE_RELEASE_LABEL(m_Properties, kMemShader);
+ InvalidateDisplayLists();
+}
+
+void Material::SetShader (Shader *s)
+{
+ m_ShaderUserNode.RemoveFromList(); // remove ourselves from old shader's users
+
+ m_Shader = s;
+ if( !s )
+ {
+ SetDirty ();
+ return;
+ }
+
+ BuildProperties ();
+
+ SetDirty ();
+ InvalidateDisplayLists();
+}
+
+Material *Material::CreateMaterial (const char *shaderStr, int hideFlags, bool scriptingObjectIsBeingCreated)
+{
+ // Create temporary shader
+ Shader *shader = NEW_OBJECT (Shader);
+ shader->Reset();
+
+ shader->SetHideFlags(hideFlags);
+ shader->SetScript (shaderStr);
+ #if UNITY_EDITOR
+ shader->GetErrors().LogErrors (shader->GetName(), shader->GetNamedObjectName(), shader, shader->GetInstanceID());
+ #endif
+
+ shader->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+
+ // TODO: in editor parse shaderlab errors to console
+ //shader->ParseShaderErrorsToConsole();
+ return CreateMaterial(*shader, hideFlags, scriptingObjectIsBeingCreated);
+}
+
+Material *Material::CreateMaterial (Shader& shader, int hideFlags, bool scriptingObjectIsBeingCreated)
+{
+ // Create temporary material
+ Material *mat = CreateObjectFromCode<Material>();
+
+ mat->SetHideFlags(hideFlags);
+ mat->SetName(shader.GetName ());
+ mat->m_Shader = &shader;
+
+ mat->BuildProperties ();
+
+#if UNITY_EDITOR
+ mat->ResetDefaultTextures (true);
+ if (!scriptingObjectIsBeingCreated)
+ mat->ApplyMaterialPropertyDrawers();
+#endif
+
+ return mat;
+}
+
+Material *Material::CreateMaterial (const Material& material, int hideFlags, bool scriptingObjectIsBeingCreated)
+{
+ // Create temporary material
+ Material *mat = CreateObjectFromCode<Material>();
+
+ mat->SetHideFlags(hideFlags);
+ mat->SetName(material.GetName ());
+
+ mat->m_Shader = material.m_Shader;
+ mat->m_SavedProperties.AddNewSerializedProps (material.m_SavedProperties);
+ mat->BuildProperties ();
+
+ return mat;
+}
+
+Material *Material::GetDefault ()
+{
+ static Material* s_DefaultMaterial = NULL;
+ if (!s_DefaultMaterial) {
+ s_DefaultMaterial = CreateObjectFromCode<Material>();
+ s_DefaultMaterial->SetHideFlags (kHideAndDontSave);
+ }
+ return s_DefaultMaterial;
+}
+
+Material *Material::GetDefaultDiffuseMaterial ()
+{
+ #if WEBPLUG
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion_OldWebResourcesAdded))
+ return GetBuiltinOldWebResource<Material> ("Default-Diffuse.mat");
+ #endif
+
+ #if UNITY_EDITOR
+ // In the editor, important to return an asset here: e.g. if we create a cube,
+ // then we want to turn that into a prefab with a proper material.
+ return GetBuiltinExtraResource<Material> ("Default-Diffuse.mat");
+ #endif
+
+ static PPtr<Material> s_DefaultDiffuseMaterial;
+ if (s_DefaultDiffuseMaterial.IsNull())
+ {
+ Shader* shader = GetScriptMapper().FindShader("Diffuse");
+ if (!shader)
+ shader = Shader::GetDefault();
+ s_DefaultDiffuseMaterial = CreateMaterial(*shader, kHideAndDontSave);
+ }
+ return s_DefaultDiffuseMaterial;
+}
+
+struct ApplyKeywords
+{
+ ShaderKeywordSet prev;
+ ApplyKeywords (ShaderKeywordSet mask) {
+ prev = g_ShaderKeywords;
+ g_ShaderKeywords.SetMask (prev.GetMask () | mask.GetMask ());
+ }
+ ~ApplyKeywords () {
+ g_ShaderKeywords = prev;
+ }
+};
+
+const ChannelAssigns* Material::SetPass (int passNo, int subshaderIndex, bool allowRecording)
+{
+ ApplyKeywords applyKeywords (m_ShaderKeywordSet);
+
+ Shader *shader = m_Shader;
+ if (!shader)
+ shader = Shader::GetDefault();
+ EnsurePropertiesExist();
+
+ if (m_PropertiesDirty)
+ InvalidateDisplayLists();
+
+#if GFX_SUPPORTS_DISPLAY_LISTS
+
+ // editor shader override won't work with recorded display lists
+ #if UNITY_EDITOR
+ if (g_EditorPixelShaderOverride)
+ allowRecording = false;
+ #endif
+
+ CachedShaderPass* cachedPass = NULL;
+ if (allowRecording && !m_PropertiesDirty)
+ {
+ if (subshaderIndex >= m_CachedSubShaders.size())
+ m_CachedSubShaders.resize(subshaderIndex + 1);
+ CachedSubShader& subshader = m_CachedSubShaders[subshaderIndex];
+ if (passNo >= subshader.passes.size())
+ subshader.passes.resize_initialized(passNo + 1);
+ cachedPass = &subshader.passes[passNo];
+ }
+
+ GfxDevice& device = GetGfxDevice();
+
+ UInt64 shaderKeywords = g_ShaderKeywords.GetMask();
+ using ShaderLab::g_GlobalFogMode;
+ bool recording = false;
+ if (cachedPass)
+ {
+ if (cachedPass->displayList &&
+ cachedPass->shaderKeywords == shaderKeywords &&
+ cachedPass->globalFogMode == g_GlobalFogMode)
+ {
+ cachedPass->displayList->Call();
+ return cachedPass->channelAssigns;
+ }
+ SAFE_RELEASE(cachedPass->displayList);
+ if (shader->CanPassBeRecorded(subshaderIndex, passNo))
+ recording = device.BeginRecording();
+ }
+
+#endif
+
+ const ChannelAssigns* channels;
+ channels = shader->SetPass(subshaderIndex, passNo, m_StateKeyHash, &GetProperties());
+
+#if GFX_SUPPORTS_DISPLAY_LISTS
+ if (recording && device.EndRecording(&cachedPass->displayList))
+ {
+ cachedPass->channelAssigns = channels;
+ cachedPass->shaderKeywords = shaderKeywords;
+ cachedPass->globalFogMode = g_GlobalFogMode;
+ }
+#endif
+ return channels;
+}
+
+const ChannelAssigns* Material::SetPassWithShader( int passNo, Shader* shader, int subshaderIndex )
+{
+ if (shader == m_Shader)
+ return SetPass(passNo, subshaderIndex);
+
+ ApplyKeywords applyKeywords (m_ShaderKeywordSet);
+ Assert(shader != NULL);
+ return shader->SetPass( subshaderIndex, passNo, m_StateKeyHash, &GetProperties() );
+}
+
+const ChannelAssigns* Material::SetShadowCasterPassWithShader(Shader* shader, int subshaderIndex)
+{
+ if (shader == m_Shader)
+ return SetShadowCasterPass(subshaderIndex);
+
+ ApplyKeywords applyKeywords (m_ShaderKeywordSet);
+ PROFILER_AUTO(gSetShadowCasterPass, this)
+ ShaderLab::Pass* pass = shader->GetShadowCasterPassToUse(subshaderIndex);
+ return pass->ApplyPass(m_StateKeyHash, &GetProperties());
+}
+
+const ChannelAssigns* Material::SetShadowCasterPass(int subshaderIndex)
+{
+ PROFILER_AUTO(gSetShadowCasterPass, this)
+
+ ApplyKeywords applyKeywords (m_ShaderKeywordSet);
+ Shader *shader = GetShader();
+ ShaderLab::Pass* pass = shader->GetShadowCasterPassToUse(subshaderIndex);
+ EnsurePropertiesExist();
+
+#if GFX_SUPPORTS_DISPLAY_LISTS
+ if (m_PropertiesDirty)
+ InvalidateDisplayLists();
+
+ UInt64 shaderKeywords = g_ShaderKeywords.GetMask();
+ bool recording = false;
+ bool allowRecording = true;
+
+ // editor shader override won't work with recorded display lists
+ #if UNITY_EDITOR
+ if (g_EditorPixelShaderOverride)
+ allowRecording = false;
+ #endif
+
+ GfxDevice& device = GetGfxDevice();
+ CachedShaderPass* cachedPass = NULL;
+ if (!m_PropertiesDirty && allowRecording)
+ {
+ if (subshaderIndex >= m_CachedSubShaders.size())
+ m_CachedSubShaders.resize(subshaderIndex + 1);
+
+ CachedSubShader& subshader = m_CachedSubShaders[subshaderIndex];
+ cachedPass = &subshader.shadowCasterPass;
+ Assert (cachedPass != NULL);
+
+ if (cachedPass->displayList &&
+ cachedPass->shaderKeywords == shaderKeywords)
+ {
+ cachedPass->displayList->Call();
+ return cachedPass->channelAssigns;
+ }
+ SAFE_RELEASE(cachedPass->displayList);
+ if (pass->CanPassBeRecorded())
+ recording = device.BeginRecording();
+ }
+#endif
+
+ const ChannelAssigns* channels;
+ channels = pass->ApplyPass(m_StateKeyHash, &GetProperties());
+
+#if GFX_SUPPORTS_DISPLAY_LISTS
+ if (recording && device.EndRecording(&cachedPass->displayList))
+ {
+ cachedPass->channelAssigns = channels;
+ cachedPass->shaderKeywords = shaderKeywords;
+ }
+#endif
+
+ return channels;
+}
+
+const ChannelAssigns* Material::SetShadowCollectorPassWithShader(Shader* shader, int subshaderIndex)
+{
+ if (shader == m_Shader)
+ return SetShadowCollectorPass(subshaderIndex);
+
+ ApplyKeywords applyKeywords (m_ShaderKeywordSet);
+ PROFILER_AUTO(gSetShadowCollectorPass, this)
+ ShaderLab::Pass* pass = shader->GetShadowCollectorPassToUse(subshaderIndex);
+ return pass->ApplyPass(m_StateKeyHash, &GetProperties());
+}
+
+const ChannelAssigns* Material::SetShadowCollectorPass(int subshaderIndex)
+{
+ PROFILER_AUTO(gSetShadowCollectorPass, this)
+ ApplyKeywords applyKeywords (m_ShaderKeywordSet);
+ Shader *shader = GetShader();
+ ShaderLab::Pass* pass = shader->GetShadowCollectorPassToUse(subshaderIndex);
+ EnsurePropertiesExist();
+
+#if GFX_SUPPORTS_DISPLAY_LISTS
+ if (m_PropertiesDirty)
+ InvalidateDisplayLists();
+
+ UInt64 shaderKeywords = g_ShaderKeywords.GetMask();
+ bool recording = false;
+ bool allowRecording = true;
+
+ // editor shader override won't work with recorded display lists
+#if UNITY_EDITOR
+ if (g_EditorPixelShaderOverride)
+ allowRecording = false;
+#endif
+
+ GfxDevice& device = GetGfxDevice();
+ CachedShaderPass* cachedPass = NULL;
+ if (!m_PropertiesDirty && allowRecording)
+ {
+ if (subshaderIndex >= m_CachedSubShaders.size())
+ m_CachedSubShaders.resize(subshaderIndex + 1);
+
+ CachedSubShader& subshader = m_CachedSubShaders[subshaderIndex];
+ cachedPass = &subshader.shadowCollectorPass;
+ Assert (cachedPass != NULL);
+
+ if (cachedPass->displayList &&
+ cachedPass->shaderKeywords == shaderKeywords)
+ {
+ cachedPass->displayList->Call();
+ return cachedPass->channelAssigns;
+ }
+ SAFE_RELEASE(cachedPass->displayList);
+ if (pass->CanPassBeRecorded())
+ recording = device.BeginRecording();
+ }
+#endif
+
+ const ChannelAssigns* channels;
+ channels = pass->ApplyPass(m_StateKeyHash, &GetProperties());
+
+#if GFX_SUPPORTS_DISPLAY_LISTS
+ if (recording && device.EndRecording(&cachedPass->displayList))
+ {
+ cachedPass->channelAssigns = channels;
+ cachedPass->shaderKeywords = shaderKeywords;
+ }
+#endif
+ return channels;
+}
+
+const Shader *Material::GetShader() const
+{
+ const Shader *shader = m_Shader;
+ if (shader)
+ return shader;
+ else
+ return Shader::GetDefault();
+}
+
+PPtr<Shader> Material::GetShaderPPtr() const
+{
+ return m_Shader;
+}
+
+
+Shader *Material::GetShader()
+{
+ Shader *shader = m_Shader;
+ if (shader)
+ return shader;
+ else
+ return Shader::GetDefault();
+}
+std::string Material::GetTag( const string& tag, bool currentSubShaderOnly, const string& defaultValue ) const
+{
+ Shader* shader = m_Shader;
+ if (!shader)
+ return defaultValue;
+ int tagValueID = shader->GetShaderLabShader()->GetTag (ShaderLab::GetShaderTagID(tag), currentSubShaderOnly);
+ if (tagValueID < 0)
+ return defaultValue;
+ return ShaderLab::GetShaderTagName(tagValueID);
+}
+
+int Material::GetPassCount()
+{
+ Shader* shader = m_Shader;
+ if( !shader )
+ shader = Shader::GetDefault();
+ return shader->GetShaderLabShader()->GetActiveSubShader().GetValidPassCount();
+}
+
+Material& Material::GetInstantiatedMaterial (Material* material, Object& renderer, bool allowInEditMode)
+{
+ if (material == NULL)
+ material = GetDefaultDiffuseMaterial();
+
+ if (material->m_Owner == PPtr<Object> (&renderer))
+ return *material;
+ else
+ {
+ if (!allowInEditMode && !IsWorldPlaying())
+ ErrorStringObject("Instantiating material due to calling renderer.material during edit mode. This will leak materials into the scene. You most likely want to use renderer.sharedMaterial instead.", &renderer);
+
+ Material* instance;
+ if (material->GetClassID()==ProceduralMaterial::GetClassIDStatic())
+ {
+ instance = static_cast<ProceduralMaterial*>(material)->Clone();
+ }
+ else
+ {
+ instance = CreateObjectFromCode<Material>();
+ }
+
+ instance->SetNameCpp (Append (material->GetName (), " (Instance)"));
+ instance->m_Shader = material->m_Shader;
+ instance->m_Owner = &renderer;
+ if (material->m_Properties)
+ {
+ SET_ALLOC_OWNER(instance);
+ SAFE_RELEASE_LABEL(instance->m_Properties, kMemShader);
+ instance->m_Properties = UNITY_NEW(ShaderLab::PropertySheet (*material->m_Properties), kMemShader);
+ instance->m_Properties->SetOwnerMaterial(instance);
+ }
+ instance->m_CustomRenderQueue = material->m_CustomRenderQueue;
+ instance->m_SavedProperties = material->m_SavedProperties;
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1))
+ {
+ instance->m_ShaderKeywords = material->m_ShaderKeywords;
+ instance->m_ShaderKeywordSet = material->m_ShaderKeywordSet;
+ }
+ instance->m_ShadowCollectorHash = material->m_ShadowCollectorHash;
+ instance->m_ShadowCasterHash = material->m_ShadowCasterHash;
+ instance->m_StateKeyHash = material->m_StateKeyHash;
+ if( instance->m_Shader )
+ instance->m_Shader->AddMaterialUser( instance->m_ShaderUserNode );
+ return *instance;
+ }
+}
+
+
+
+#if UNITY_EDITOR
+inline void EmitWarningAboutSettingBuiltinParam(const char* name)
+{
+ static const std::string kWarningPrefix = std::string("Trying to set builtin parameter \"");
+ static const std::string kWarningSuffix = std::string("\". Will be ignored.");
+
+ WarningString( kWarningPrefix + name + kWarningSuffix );
+}
+#endif
+
+
+void Material::SetColor (ShaderLab::FastPropertyName name, const ColorRGBAf &col)
+{
+#if UNITY_EDITOR
+ if( IsVectorBuiltinParam(name.GetName()) )
+ EmitWarningAboutSettingBuiltinParam(name.GetName());
+#endif
+
+ UnityPropertySheet::ColorMap::iterator i = m_SavedProperties.m_Colors.find (name);
+ if( i != m_SavedProperties.m_Colors.end() )
+ {
+ #if UNITY_EDITOR
+ if (i->second.NotEquals(col))
+ #endif
+ {
+ i->second = col;
+ SetDirty();
+ }
+ }
+
+ ShaderLab::PropertySheet& properties = GetWritableProperties();
+ if (properties.GetColorTag(name))
+ properties.SetVector (name, GammaToActiveColorSpace(col).GetPtr());
+ else
+ properties.SetVector (name, col.GetPtr());
+
+ UpdateHashesOnPropertyChange (name);
+}
+
+
+void Material::SetColorIndexed (ShaderLab::FastPropertyName name, int index, float value)
+{
+#if UNITY_EDITOR
+ if( IsVectorBuiltinParam(name.GetName()) )
+ EmitWarningAboutSettingBuiltinParam(name.GetName());
+#endif
+
+ UnityPropertySheet::ColorMap::iterator i = m_SavedProperties.m_Colors.find (name);
+ if( i != m_SavedProperties.m_Colors.end() )
+ {
+ #if UNITY_EDITOR
+ if (i->second.GetPtr()[index] != value)
+ #endif
+ {
+ i->second.GetPtr()[index] = value;
+ SetDirty();
+ }
+ }
+
+ ShaderLab::PropertySheet& properties = GetWritableProperties();
+ if (properties.GetColorTag(name))
+ properties.SetVectorIndexed (name, index, GammaToActiveColorSpace(value));
+ else
+ properties.SetVectorIndexed (name, index, value);
+
+ UpdateHashesOnPropertyChange (name);
+}
+
+ColorRGBAf Material::GetColor (ShaderLab::FastPropertyName name)
+{
+ EnsurePropertiesExist ();
+
+ const Vector4f* prop = m_Properties->FindVector(name);
+ if( prop == NULL ) {
+ AssertStringObject (Format ("Material doesn't have a color property '%s'", name.GetName()), this);
+ return ColorRGBAf (0,0,0,0);
+ }
+
+ ColorRGBAf color (prop->x, prop->y, prop->z, prop->w);
+ if (m_Properties->GetColorTag(name))
+ return ActiveToGammaColorSpace (color);
+ else
+ return color;
+}
+
+
+void Material::SetFloat (ShaderLab::FastPropertyName name, float val)
+{
+#if UNITY_EDITOR
+ if( IsVectorBuiltinParam(name.GetName()) )
+ EmitWarningAboutSettingBuiltinParam(name.GetName());
+#endif
+
+ UnityPropertySheet::FloatMap::iterator i = m_SavedProperties.m_Floats.find (name);
+ if( i != m_SavedProperties.m_Floats.end() )
+ {
+ #if UNITY_EDITOR
+ if (i->second != val)
+ #endif
+ {
+ i->second = val;
+ SetDirty();
+ }
+ }
+ GetWritableProperties().SetFloat (name, val);
+
+ UpdateHashesOnPropertyChange (name);
+}
+
+float Material::GetFloat (ShaderLab::FastPropertyName name)
+{
+ EnsurePropertiesExist ();
+
+ const float* prop = m_Properties->FindFloat(name);
+ if( prop == NULL ) {
+ AssertStringObject (Format ("Material doesn't have a float or range property '%s'", name.GetName()), this);
+ return 0;
+ }
+ return *prop;
+}
+
+bool Material::HasProperty (ShaderLab::FastPropertyName name)
+{
+ EnsurePropertiesExist ();
+ return m_Properties->HasProperty(name);
+}
+
+
+void Material::ApplyMaterialPropertyDrawers ()
+{
+# if UNITY_EDITOR
+ // Don't try to apply property drawers for the very initial Reset()
+ // that happens; the shader isn't set up yet anyway.
+ if (!m_Shader.IsValid())
+ return;
+ if (!GetMonoManagerPtr())
+ return;
+
+ void* params[] = {Scripting::ScriptingWrapperFor(this)};
+ CallStaticMonoMethod ("MaterialEditor", "ApplyMaterialPropertyDrawers", params);
+# endif // if UNITY_EDITOR
+}
+
+
+#if UNITY_EDITOR
+bool Material::ActuallyHasTextureProperty (ShaderLab::FastPropertyName name) const
+{
+ return m_SavedProperties.m_TexEnvs.find (name) != m_SavedProperties.m_TexEnvs.end();
+}
+
+
+void Material::ResetDefaultTextures(bool overrideSetTextures)
+{
+ if( !m_Shader )
+ return;
+
+ const Shader::DefaultTexturesMap& defaultTextures = m_Shader->GetDefaultTextures();
+ for (Shader::DefaultTexturesMap::const_iterator it = defaultTextures.begin(); it != defaultTextures.end(); ++it)
+ {
+ bool nullDefaultTexture = it->second.IsNull();
+
+ for (int i = 0; i < m_Shader->GetPropertyCount (); i++)
+ {
+ const ShaderLab::ParserProperty* shaderProperty = m_Shader->GetPropertyInfo (i);
+
+ // only assign if it is a texture where the name and dimensions match
+ if (shaderProperty->m_Name != it->first.c_str()
+ || shaderProperty->m_Type != ShaderLab::ParserProperty::kTexture
+ || (!nullDefaultTexture && shaderProperty->m_DefTexture.m_TexDim != it->second->GetDimension ()))
+ continue;
+
+ if (!overrideSetTextures)
+ {
+ if (GetTexture(ShaderLab::Property(it->first)) != NULL)
+ continue;
+ }
+
+ SetTexture (ShaderLab::Property(it->first), it->second);
+ break;
+ }
+ }
+}
+#endif
+
+
+// Get/Set a matrix value in the material
+// We don't allow user editing of matrix props, hence they don't have a representation in the
+// UnityPropertySheet, hence we work directly with the SL properties here
+void Material::SetMatrix (ShaderLab::FastPropertyName name, const Matrix4x4f &val)
+{
+#if UNITY_EDITOR
+ if( IsMatrixBuiltinParam(name.GetName()) )
+ EmitWarningAboutSettingBuiltinParam(name.GetName());
+#endif
+
+ GetWritableProperties().SetValueProp (name, 16, val.GetPtr());
+
+ UpdateHashesOnPropertyChange (name);
+}
+
+const Matrix4x4f &Material::GetMatrix (ShaderLab::FastPropertyName name)
+{
+ EnsurePropertiesExist ();
+
+ int count = 0;
+ const float* prop = m_Properties->GetValueProp (name, &count);
+ if (prop == NULL || count != 16)
+ {
+ AssertStringObject (Format ("Material doesn't have a matrix property '%s'", name.GetName()), this);
+ return Matrix4x4f::identity;
+ }
+ return *reinterpret_cast<const Matrix4x4f*>(prop);
+}
+
+
+void Material::SetTexture (ShaderLab::FastPropertyName name, Texture *val)
+{
+ UnityPropertySheet::TexEnvMap::iterator i = m_SavedProperties.m_TexEnvs.find (name);
+ if( i != m_SavedProperties.m_TexEnvs.end() )
+ {
+ #if UNITY_EDITOR
+ if (i->second.m_Texture != PPtr<Texture>(val))
+ #endif
+ {
+ i->second.m_Texture = val;
+ SetDirty();
+ }
+ }
+ else
+ {
+ //AssertStringObject (Format ("Material '%s' doesn't have a texture property '%s'", GetName(), name.GetName()), this);
+ }
+
+ GetWritableProperties().SetTexture (name, val);
+
+ UpdateHashesOnPropertyChange (name);
+}
+
+
+Texture *Material::GetTexture (ShaderLab::FastPropertyName name)
+{
+ EnsurePropertiesExist ();
+
+ UnityPropertySheet::TexEnvMap::const_iterator i = m_SavedProperties.m_TexEnvs.find (name);
+ if (i == m_SavedProperties.m_TexEnvs.end())
+ {
+ AssertStringObject (Format ("Material doesn't have a texture property '%s'", name.GetName()), this);
+ return 0;
+ }
+ return PPtr<Texture> (i->second.m_Texture);
+}
+
+void Material::SetTextureOffset( ShaderLab::FastPropertyName name, const Vector2f& val )
+{
+ UnityPropertySheet::TexEnvMap::iterator i = m_SavedProperties.m_TexEnvs.find (name);
+ if( i != m_SavedProperties.m_TexEnvs.end() )
+ {
+ #if UNITY_EDITOR
+ if (i->second.m_Offset != val)
+ #endif
+ {
+ i->second.m_Offset.Set( val.x, val.y );
+ SetDirty();
+ }
+ }
+ GetWritableProperties().SetTextureOffset( name, val.x, val.y );
+
+ UpdateHashesOnPropertyChange (name);
+}
+
+void Material::SetTextureScale( ShaderLab::FastPropertyName name, const Vector2f& val )
+{
+ UnityPropertySheet::TexEnvMap::iterator i = m_SavedProperties.m_TexEnvs.find (name);
+ if( i != m_SavedProperties.m_TexEnvs.end() )
+ {
+ #if UNITY_EDITOR
+ if (i->second.m_Scale != val)
+ #endif
+ {
+ i->second.m_Scale.Set( val.x, val.y );
+ SetDirty();
+ }
+ }
+ GetWritableProperties().SetTextureScale( name, val.x, val.y );
+
+ UpdateHashesOnPropertyChange (name);
+}
+
+void Material::SetTextureScaleAndOffsetIndexed( ShaderLab::FastPropertyName name, int index, float value)
+{
+ UnityPropertySheet::TexEnvMap::iterator i = m_SavedProperties.m_TexEnvs.find (name);
+ if( i != m_SavedProperties.m_TexEnvs.end() )
+ {
+ if (index < 2)
+ {
+ i->second.m_Scale[index] = value;
+ }
+ else
+ {
+ i->second.m_Offset[index - 2] = value;
+ }
+ SetDirty();
+ }
+ GetWritableProperties().SetTextureScaleAndOffsetIndexed( name, index, value );
+
+ UpdateHashesOnPropertyChange (name);
+}
+
+Vector2f Material::GetTextureOffset( ShaderLab::FastPropertyName name )
+{
+ // get from current runtime sheet
+ const ShaderLab::TexEnv* texEnv = GetProperties().GetTexEnv (name);
+ if (texEnv != NULL)
+ return texEnv->GetTextureOffset ();
+
+ // if not there, get from serialized properties
+ UnityPropertySheet::TexEnvMap::const_iterator it = m_SavedProperties.m_TexEnvs.find (name);
+ if (it != m_SavedProperties.m_TexEnvs.end())
+ return it->second.m_Offset;
+
+ AssertStringObject (Format ("Material doesn't have a texture property '%s'", name.GetName()), this);
+ return Vector2f(0,0);
+}
+
+Vector2f Material::GetTextureScale( ShaderLab::FastPropertyName name )
+{
+ // get from current runtime sheet
+ const ShaderLab::TexEnv* texEnv = GetProperties().GetTexEnv (name);
+ if (texEnv != NULL)
+ return texEnv->GetTextureScale ();
+
+ // if not there, get from serialized properties
+ UnityPropertySheet::TexEnvMap::const_iterator it = m_SavedProperties.m_TexEnvs.find (name);
+ if (it != m_SavedProperties.m_TexEnvs.end())
+ return it->second.m_Scale;
+
+ AssertStringObject (Format ("Material doesn't have a texture property '%s'", name.GetName()), this);
+ return Vector2f(1,1);
+}
+
+void Material::SetComputeBuffer (ShaderLab::FastPropertyName name, ComputeBufferID val)
+{
+ GetWritableProperties().SetComputeBuffer (name, val);
+}
+
+void Material::CopyPropertiesFromMaterial(Material& other)
+{
+ m_SavedProperties = other.GetSavedProperties();
+ SAFE_RELEASE_LABEL(m_Properties, kMemShader);
+ {
+ SET_ALLOC_OWNER(this);
+ m_Properties = UNITY_NEW(ShaderLab::PropertySheet(other.GetProperties()), kMemShader);
+ m_Properties->SetOwnerMaterial(this);
+ }
+ m_PropertiesDirty = true;
+
+ Shader *shader = m_Shader;
+ if( !shader )
+ shader = Shader::GetDefault();
+
+ UpdateHashes();
+}
+
+void Material::ClearProperties ()
+{
+ SAFE_RELEASE_LABEL(m_Properties, kMemShader);
+ m_PropertiesDirty = true;
+ m_ShaderUserNode.RemoveFromList();
+}
+
+int Material::GetRuntimeMemorySize() const
+{ int size = Super::GetRuntimeMemorySize();
+ if (m_Properties)
+ size += m_Properties->GetMemoryUsage();
+ return size;
+}
+
+void Material::InvalidateDisplayLists()
+{
+ int subShaderCount = m_CachedSubShaders.size();
+ for (int i = 0; i < subShaderCount; i++)
+ {
+ CachedSubShader& subshader = m_CachedSubShaders[i];
+ int passCount = subshader.passes.size();
+ for (int j = 0; j < passCount; j++)
+ {
+ CachedShaderPass& pass = subshader.passes[j];
+ SAFE_RELEASE(pass.displayList);
+ }
+ SAFE_RELEASE(subshader.shadowCasterPass.displayList);
+ SAFE_RELEASE(subshader.shadowCollectorPass.displayList);
+ }
+ m_PropertiesDirty = false;
+}
+
+void Material::BuildShaderKeywordSet ()
+{
+ m_ShaderKeywordSet.Reset ();
+ for (size_t q = 0; q < m_ShaderKeywords.size (); ++q)
+ m_ShaderKeywordSet.Enable (keywords::Create (m_ShaderKeywords[q]));
+}
+
+void Material::SetShaderKeywords (const ShaderKeywordsT& keywords)
+{
+ m_ShaderKeywords = keywords;
+ BuildShaderKeywordSet ();
+ UpdateHashes();
+ SetDirty();
+}
+
+void Material::EnableKeyword (const std::string& key)
+{
+ // already set -> do nothing
+ if (std::find(m_ShaderKeywords.begin(), m_ShaderKeywords.end(), key) != m_ShaderKeywords.end())
+ return;
+ m_ShaderKeywords.push_back (key);
+ BuildShaderKeywordSet ();
+ UpdateHashes();
+ SetDirty();
+}
+
+void Material::DisableKeyword (const std::string& key)
+{
+ // no keyword -> do nothing
+ ShaderKeywordsT::iterator it = std::find(m_ShaderKeywords.begin(), m_ShaderKeywords.end(), key);
+ if (it == m_ShaderKeywords.end())
+ return;
+
+ m_ShaderKeywords.erase (it);
+ BuildShaderKeywordSet ();
+ UpdateHashes();
+ SetDirty();
+}
+
+
+}
+
+IMPLEMENT_CLASS (Material)
+IMPLEMENT_OBJECT_SERIALIZE (Material)
+INSTANTIATE_TEMPLATE_TRANSFER (Material)
diff --git a/Runtime/Shaders/Material.h b/Runtime/Shaders/Material.h
new file mode 100644
index 0000000..46dd954
--- /dev/null
+++ b/Runtime/Shaders/Material.h
@@ -0,0 +1,233 @@
+#ifndef MATERIAL_H
+#define MATERIAL_H
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Vector4.h"
+#include "Runtime/Math/Color.h"
+#include "UnityPropertySheet.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Shaders/ShaderKeywords.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Modules/ExportModules.h"
+
+namespace ShaderLab
+{
+ class PropertySheet;
+ struct FastPropertyName;
+ class ShaderState;
+ class Pass;
+}
+
+class ChannelAssigns;
+class Shader;
+class Texture;
+class Matrix4x4f;
+class GfxDisplayList;
+
+
+
+namespace Unity
+{
+
+class EXPORT_COREMODULE Material : public NamedObject
+{
+ public:
+ REGISTER_DERIVED_CLASS (Material, NamedObject)
+ DECLARE_OBJECT_SERIALIZE (Material)
+
+ Material (MemLabelId label, ObjectCreationMode mode);
+ // ~Material (); declared-by-macro
+ virtual void Reset();
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ /// Get how many passes this material requires to render
+ int GetPassCount ();
+
+ // Set the pass to render next time.
+ // Calling this function sets up the shader.
+ // Returns vertex channels used; NULL if this pass should not be rendered.
+ const ChannelAssigns* SetPass (int passNo, int subshaderIndex = 0, bool allowRecording = true);
+
+ // Set the pass to render next time.
+ // Can supply a different shader from the one in material, or just cached PPtr deref.
+ // Also indicates which subshader to use.
+ // Calling this function sets up the shader.
+ // Returns vertex channels used; NULL if this pass should not be rendered.
+ const ChannelAssigns* SetPassWithShader( int passNo, Shader* shader, int subshaderIndex );
+
+ const ChannelAssigns* SetShadowCasterPass(int subshaderIndex);
+ const ChannelAssigns* SetShadowCasterPassWithShader(Shader* shader, int subshaderIndex);
+ const ChannelAssigns* SetShadowCollectorPass(int subshaderIndex);
+ const ChannelAssigns* SetShadowCollectorPassWithShader(Shader* shader, int subshaderIndex);
+
+ int GetActualRenderQueue() const;
+ int GetCustomRenderQueue() const { return m_CustomRenderQueue; }
+ void SetCustomRenderQueue (int q) { if (m_CustomRenderQueue != q) { m_CustomRenderQueue = q; SetDirty(); } }
+
+ inline const ShaderLab::PropertySheet& GetProperties ()
+ {
+ EnsurePropertiesExist ();
+ return *m_Properties;
+ }
+
+ inline void EnsurePropertiesExist ()
+ {
+// #if UNITY_EDITOR || WEBPLUG
+ #if 1
+ // Don't preload in the editor because it can easily run out of memory and increase build times.
+ // In the webplayer we also have some hacks where we call ClearProperties that must be fixed before we can remove the WEBPLUG condition
+ if( !m_Properties )
+ BuildProperties();
+ #else
+ // Enforce that everything is preloaded in the player, reduces hiccups at runtime.
+ Assert(m_Properties != NULL);
+ #endif
+ }
+
+ inline ShaderLab::PropertySheet& GetWritableProperties ()
+ {
+ EnsurePropertiesExist ();
+ SetPropertiesDirty();
+ return *m_Properties;
+ }
+
+ void SetPropertiesDirty ()
+ {
+ m_PropertiesDirty = true;
+ }
+
+ /// Clears the cached properties.
+ /// This is used in the web player to avoid caching issues with builtin resources when reloading the player.
+ void ClearProperties ();
+
+ // Sets the materials properties
+ void CopyPropertiesFromMaterial(Material& other);
+
+ // Get/Set a color value in the material
+ void SetColor (ShaderLab::FastPropertyName name, const ColorRGBAf &col);
+ void SetColorIndexed (ShaderLab::FastPropertyName name, int indexed, float value);
+ ColorRGBAf GetColor (ShaderLab::FastPropertyName name);
+
+ // Get/Set a float value in the material
+ void SetFloat (ShaderLab::FastPropertyName name, float val);
+ float GetFloat (ShaderLab::FastPropertyName name);
+
+ // Get/Set a matrix value in the material
+ void SetMatrix ( ShaderLab::FastPropertyName name, const Matrix4x4f &val);
+ const Matrix4x4f &GetMatrix (ShaderLab::FastPropertyName name);
+
+ // Get/Set a texture value in the material
+ void SetTexture (ShaderLab::FastPropertyName name, Texture *val);
+ Texture *GetTexture (ShaderLab::FastPropertyName name);
+
+ bool HasProperty (ShaderLab::FastPropertyName name);
+
+ // Texture placement
+ void SetTextureOffset( ShaderLab::FastPropertyName name, const Vector2f& offset );
+ void SetTextureScale( ShaderLab::FastPropertyName name, const Vector2f& scale );
+
+ Vector2f GetTextureOffset( ShaderLab::FastPropertyName name );
+ Vector2f GetTextureScale( ShaderLab::FastPropertyName name );
+
+ void SetTextureScaleAndOffsetIndexed (ShaderLab::FastPropertyName name, int indexed, float value);
+
+ void SetComputeBuffer (ShaderLab::FastPropertyName name, ComputeBufferID val);
+
+
+ // Get the default material
+ static Material *GetDefault ();
+ static Material *GetDefaultDiffuseMaterial ();
+
+ static Material *CreateMaterial (const char *shaderStr, int hideFlags, bool scriptingObjectIsBeingCreated = false);
+ static Material *CreateMaterial (Shader& shader, int hideFlags, bool scriptingObjectIsBeingCreated = false);
+ static Material *CreateMaterial (const Material& material, int hideFlags, bool scriptingObjectIsBeingCreated = false);
+
+ // Set the shader that drives the material
+ void SetShader (Shader *s);
+
+ const Shader *GetShader() const;
+ Shader *GetShader();
+ PPtr<Shader> GetShaderPPtr() const;
+
+ std::string GetTag( const string& tag, bool currentSubShaderOnly, const string& defaultValue ) const;
+
+ typedef std::vector<UnityStr> ShaderKeywordsT;
+ const ShaderKeywordsT& GetShaderKeywords () const { return m_ShaderKeywords; }
+ void SetShaderKeywords (const ShaderKeywordsT& keywords);
+ ShaderKeywordSet GetShaderKeywordSet () const { return m_ShaderKeywordSet; }
+ void EnableKeyword (const std::string& key);
+ void DisableKeyword (const std::string& key);
+ void ApplyMaterialPropertyDrawers();
+
+ #if UNITY_EDITOR
+ bool ActuallyHasTextureProperty (ShaderLab::FastPropertyName name) const;
+
+ void ResetDefaultTextures (bool overrideSetTextures);
+ #endif
+
+ inline UnityPropertySheet& GetSavedProperties () { return m_SavedProperties; }
+ inline const UnityPropertySheet& GetSavedProperties () const { return m_SavedProperties; }
+
+ // If m_Owner equals renderer returns this
+ // Otherwise creates a copy and sets m_Owner to renderer.
+ // Used by Animation system and scripting to animate material properties of a single renderer.
+ static Material& GetInstantiatedMaterial (Material* material, Object& renderer, bool allowInEditMode);
+ PPtr<Object> GetOwner () { return m_Owner; }
+
+ virtual int GetRuntimeMemorySize () const;
+
+ void InvalidateDisplayLists ();
+
+ inline UInt32 GetShadowCasterHash() { EnsurePropertiesExist (); return m_ShadowCasterHash; }
+ inline UInt32 GetShadowCollectorHash() { EnsurePropertiesExist (); return m_ShadowCollectorHash; }
+
+private:
+ /// Build the ShaderLab property sheet (m_Properties) from the saved properties.
+ /// This will correctly handle default properties supplied by the shader...
+ void BuildProperties ();
+ void BuildShaderKeywordSet ();
+
+ void UpdateHashesOnPropertyChange (ShaderLab::FastPropertyName name);
+ void UpdateHashes ();
+
+ struct CachedShaderPass
+ {
+ CachedShaderPass() : displayList(NULL), channelAssigns(NULL), shaderKeywords(0), globalFogMode(kFogDisabled) {}
+
+ GfxDisplayList* displayList;
+ const ChannelAssigns* channelAssigns;
+ UInt64 shaderKeywords;
+ FogMode globalFogMode;
+ };
+
+ struct CachedSubShader
+ {
+ dynamic_array<CachedShaderPass> passes;
+ CachedShaderPass shadowCasterPass;
+ CachedShaderPass shadowCollectorPass;
+ };
+
+ PPtr<Shader> m_Shader;
+ ShaderLab::PropertySheet* m_Properties;
+ bool m_PropertiesDirty;
+ std::vector<CachedSubShader> m_CachedSubShaders;
+ int m_CustomRenderQueue; // -1 if should use shader's
+ PPtr<Object> m_Owner;
+ UnityPropertySheet m_SavedProperties;
+
+ ListNode<Material> m_ShaderUserNode;
+ ShaderKeywordsT m_ShaderKeywords;
+ ShaderKeywordSet m_ShaderKeywordSet;
+ UInt32 m_ShadowCollectorHash;
+ UInt32 m_ShadowCasterHash;
+ UInt32 m_StateKeyHash;
+};
+
+}
+
+using namespace Unity;
+
+#endif
diff --git a/Runtime/Shaders/MaterialIsTransparent.h b/Runtime/Shaders/MaterialIsTransparent.h
new file mode 100644
index 0000000..072b054
--- /dev/null
+++ b/Runtime/Shaders/MaterialIsTransparent.h
@@ -0,0 +1,14 @@
+#include "Material.h"
+
+inline bool IsTransparentOrCutoutMaterial (Material& material)
+{
+ string tag = material.GetTag("RenderType", false, string());
+
+ if (material.GetActualRenderQueue() >= kAlphaTestRenderQueue)
+ return true;
+
+ if (tag == "TreeTransparentCutout" || tag == "GrassBillboard" || tag == "Grass" || tag == "TreeLeaf" || tag == "TranparentCutout" || tag == "Tranparent")
+ return true;
+
+ return false;
+}
diff --git a/Runtime/Shaders/MaterialProperties.cpp b/Runtime/Shaders/MaterialProperties.cpp
new file mode 100644
index 0000000..357a73f
--- /dev/null
+++ b/Runtime/Shaders/MaterialProperties.cpp
@@ -0,0 +1,195 @@
+#include "UnityPrefix.h"
+#include "MaterialProperties.h"
+#include "Material.h"
+#include "External/shaderlab/Library/properties.h"
+#include "Runtime/Math/Matrix4x4.h"
+
+inline size_t CalculateSizeFromProperty (const MaterialPropertyBlock::Property& prop)
+{
+ return prop.rows * prop.cols * prop.arraySize;
+}
+
+MaterialPropertyBlock::MaterialPropertyBlock(Property* props, size_t propCount, float* buffer, size_t bufSize)
+{
+ m_Properties.assign_external(props, props + propCount);
+ m_Buffer.assign_external(buffer, buffer + bufSize);
+}
+
+void MaterialPropertyBlock::AddProperty(const ShaderLab::FastPropertyName& name, const float* data, UInt8 rows, UInt8 cols, size_t arraySize)
+{
+ size_t offset = m_Buffer.size();
+ Property prop = { name.index, rows, cols, kTexDimNone, arraySize, offset };
+ m_Properties.push_back(prop);
+ size_t size = rows * cols * arraySize;
+ m_Buffer.resize_uninitialized(offset + size);
+ memcpy(&m_Buffer[offset], data, size * sizeof(float));
+}
+
+void MaterialPropertyBlock::AddPropertyTexture(const ShaderLab::FastPropertyName& name, TextureDimension dim, TextureID tid)
+{
+ AddProperty (name, reinterpret_cast<float*> (&tid.m_ID), 1, 1, 1);
+ m_Properties.back().texDim = dim;
+}
+
+void MaterialPropertyBlock::AddPropertyFloat(const ShaderLab::FastPropertyName& name, float val)
+{
+ AddProperty(name, &val, 1, 1, 1);
+}
+
+void MaterialPropertyBlock::AddPropertyVector(const ShaderLab::FastPropertyName& name, const Vector4f& vec)
+{
+ AddProperty(name, vec.GetPtr(), 1, 4, 1);
+}
+
+void MaterialPropertyBlock::AddPropertyColor(const ShaderLab::FastPropertyName& name, const ColorRGBAf& col)
+{
+ ColorRGBAf converted = GammaToActiveColorSpace (col);
+ AddProperty(name, converted.GetPtr(), 1, 4, 1);
+}
+
+void MaterialPropertyBlock::AddPropertyMatrix(const ShaderLab::FastPropertyName& name, const Matrix4x4f& mat)
+{
+ AddProperty(name, mat.GetPtr(), 4, 4, 1);
+}
+
+void MaterialPropertyBlock::ReplacePropertyTexture(const ShaderLab::FastPropertyName& name, TextureDimension dim, TextureID tid)
+{
+ int index = GetPropertyIndex(name);
+ if (index == -1)
+ AddPropertyTexture(name, dim, tid);
+ else
+ {
+ Property& prop = m_Properties[index];
+ if (prop.rows == 1 && prop.cols == 1 && prop.arraySize == 1)
+ {
+ TextureID* buf = reinterpret_cast<TextureID*> (&m_Buffer[prop.offset]);
+ *buf = tid;
+ prop.texDim = dim;
+ }
+ else
+ {
+ ErrorString("The material property is different from already stored property.");
+ }
+ }
+}
+
+void MaterialPropertyBlock::ReplacePropertyColor(const ShaderLab::FastPropertyName& name, const ColorRGBAf& col)
+{
+ ColorRGBAf activeColor = GammaToActiveColorSpace (col);
+ ReplacePropertyVector (name, *reinterpret_cast<const Vector4f*> (&activeColor));
+}
+
+void MaterialPropertyBlock::ReplacePropertyVector(const ShaderLab::FastPropertyName& name, const Vector4f& vec)
+{
+ int index = GetPropertyIndex(name);
+ if (index == -1)
+ AddPropertyVector(name, vec);
+ else
+ {
+ const Property& prop = m_Properties[index];
+ if (prop.rows == 1 && prop.cols == 4 && prop.arraySize == 1)
+ {
+ Vector4f* buf = reinterpret_cast<Vector4f*> (&m_Buffer[prop.offset]);
+ *buf = vec;
+ }
+ else
+ {
+ ErrorString("The material property is different from already stored property.");
+ }
+ }
+}
+
+void MaterialPropertyBlock::ReplacePropertyFloat(const ShaderLab::FastPropertyName& name, float data)
+{
+ ReplacePartialFloatProperty(name, data, 1, 0);
+}
+
+void MaterialPropertyBlock::ReplacePartialFloatColorProperty(const ShaderLab::FastPropertyName& name, float data, UInt8 cols, UInt8 colIndex)
+{
+ ReplacePartialFloatProperty(name, GammaToActiveColorSpace(data), cols, colIndex);
+}
+
+void MaterialPropertyBlock::ReplacePartialFloatProperty(const ShaderLab::FastPropertyName& name, float data, UInt8 cols, UInt8 colIndex)
+{
+ int index = GetPropertyIndex(name);
+ if (index == -1)
+ {
+ float prop[4] = { 0.0F, 0.0F, 0.0F, 0.0F };
+ prop[colIndex] = data;
+
+ AddProperty(name, prop, 1, cols, 1);
+ }
+ else
+ {
+ const Property& prop = m_Properties[index];
+ if (prop.rows == 1 && prop.cols == cols && prop.arraySize == 1)
+ {
+ float* buffer = reinterpret_cast<float*> (&m_Buffer[prop.offset]);
+ buffer[colIndex] = data;
+ }
+ else
+ {
+ ErrorString("The material property is different from already stored property.");
+ }
+ }
+}
+
+int MaterialPropertyBlock::GetPropertyIndex (const ShaderLab::FastPropertyName& name) const
+{
+ for (int i=0;i<m_Properties.size();i++)
+ {
+ if (m_Properties[i].nameIndex == name.index)
+ return i;
+ }
+ return -1;
+}
+
+const void* MaterialPropertyBlock::Find(const ShaderLab::FastPropertyName& name, UInt8 rows, UInt8 cols, size_t arraySize) const
+{
+ for (size_t i = 0, n = m_Properties.size(); i != n; ++i)
+ {
+ const Property& prop = m_Properties[i];
+ if (name.index == prop.nameIndex && prop.cols == cols && prop.rows == rows)
+ return &m_Buffer[prop.offset];
+ }
+ return NULL;
+}
+
+
+const float* MaterialPropertyBlock::FindFloat(const ShaderLab::FastPropertyName& name) const
+{
+ return static_cast<const float*> (Find(name, 1, 1, 1));
+}
+
+const Vector4f* MaterialPropertyBlock::FindVector(const ShaderLab::FastPropertyName& name) const
+{
+ return static_cast<const Vector4f*> (Find(name, 1, 4, 1));
+}
+
+const Matrix4x4f* MaterialPropertyBlock::FindMatrix(const ShaderLab::FastPropertyName& name) const
+{
+ return static_cast<const Matrix4x4f*> (Find(name, 4, 4, 1));
+}
+
+bool MaterialPropertyBlock::GetColor(const ShaderLab::FastPropertyName& name, ColorRGBAf& outColor) const
+{
+ const ColorRGBAf* color = static_cast<const ColorRGBAf*> (Find(name, 1, 4, 1));
+ if (color != NULL)
+ {
+ outColor = ActiveToGammaColorSpace(*color);
+ return true;
+ }
+ else
+ return false;
+}
+
+const TextureID MaterialPropertyBlock::FindTexture(const ShaderLab::FastPropertyName& name) const
+{
+ for (size_t i = 0, n = m_Properties.size(); i != n; ++i)
+ {
+ const Property& prop = m_Properties[i];
+ if (name.index == prop.nameIndex && prop.texDim != kTexDimNone)
+ return TextureID(*(const int*)&m_Buffer[prop.offset]);
+ }
+ return TextureID();
+}
diff --git a/Runtime/Shaders/MaterialProperties.h b/Runtime/Shaders/MaterialProperties.h
new file mode 100644
index 0000000..5c1c0bd
--- /dev/null
+++ b/Runtime/Shaders/MaterialProperties.h
@@ -0,0 +1,85 @@
+#ifndef MATERIAL_PROPERTIES_H
+#define MATERIAL_PROPERTIES_H
+
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Modules/ExportModules.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+class Vector4f;
+class Matrix4x4f;
+class ColorRGBAf;
+namespace Unity { class Material; }
+namespace ShaderLab { struct FastPropertyName; }
+
+// Tightly packed buffer of material properties
+class EXPORT_COREMODULE MaterialPropertyBlock
+{
+public:
+ struct Property
+ {
+ int nameIndex;
+ UInt8 rows;
+ UInt8 cols;
+ UInt8 texDim; // if texDim==None, this is a value property
+ // These should not be size_t, as the GfxDevice may run across processes of different
+ // bitness, and the data serialized in the command buffer must match.
+ UInt32 arraySize;
+ UInt32 offset;
+ };
+
+ MaterialPropertyBlock() {}
+
+ // Does not copy data!
+ MaterialPropertyBlock(Property* props, size_t propCount, float* buffer, size_t bufSize);
+
+ // Clear all properties
+ void Clear();
+
+ // Add Properties without checking if another property with the same name exists
+ void AddProperty(const ShaderLab::FastPropertyName& name, const float* data, UInt8 rows, UInt8 cols, size_t arraySize);
+ void AddPropertyFloat(const ShaderLab::FastPropertyName& name, float val);
+ void AddPropertyVector(const ShaderLab::FastPropertyName& name, const Vector4f& vec);
+ void AddPropertyColor(const ShaderLab::FastPropertyName& name, const ColorRGBAf& col);
+ void AddPropertyMatrix(const ShaderLab::FastPropertyName& name, const Matrix4x4f& mat);
+ void AddPropertyTexture(const ShaderLab::FastPropertyName& name, TextureDimension dim, TextureID tid);
+
+ // Replace properties
+ void ReplacePropertyFloat(const ShaderLab::FastPropertyName& name, float data);
+ void ReplacePropertyVector(const ShaderLab::FastPropertyName& name, const Vector4f& col);
+ void ReplacePropertyColor(const ShaderLab::FastPropertyName& name, const ColorRGBAf& col);
+ void ReplacePropertyTexture(const ShaderLab::FastPropertyName& name, TextureDimension dim, TextureID tid);
+
+ // Replaces a single float property on either a float1 or one component of a float4.
+ /// If other components on a float4 are not yet defined, they will be initialized to zero
+ void ReplacePartialFloatProperty(const ShaderLab::FastPropertyName& name, float data, UInt8 cols, UInt8 colIndex);
+ void ReplacePartialFloatColorProperty(const ShaderLab::FastPropertyName& name, float data, UInt8 cols, UInt8 colIndex);
+
+
+ const float* FindFloat(const ShaderLab::FastPropertyName& name) const;
+ const Vector4f* FindVector(const ShaderLab::FastPropertyName& name) const;
+ bool GetColor(const ShaderLab::FastPropertyName& name, ColorRGBAf& outColor) const;
+ const Matrix4x4f* FindMatrix(const ShaderLab::FastPropertyName& name) const;
+ const TextureID FindTexture(const ShaderLab::FastPropertyName& name) const;
+
+ const Property* GetPropertiesBegin() const { return m_Properties.begin(); }
+ const Property* GetPropertiesEnd() const { return m_Properties.end(); }
+ const float* GetBufferBegin() const { return m_Buffer.begin(); }
+ const float* GetBufferEnd() const { return m_Buffer.end(); }
+
+
+ const void* Find(const ShaderLab::FastPropertyName& name, UInt8 rows, UInt8 cols, size_t arraySize) const;
+ int GetPropertyIndex (const ShaderLab::FastPropertyName& name) const;
+
+private:
+ dynamic_array<Property> m_Properties;
+ dynamic_array<float> m_Buffer;
+};
+
+inline void MaterialPropertyBlock::Clear()
+{
+ m_Properties.resize_uninitialized(0);
+ m_Buffer.resize_uninitialized(0);
+}
+
+
+#endif
diff --git a/Runtime/Shaders/NameToObjectMap.h b/Runtime/Shaders/NameToObjectMap.h
new file mode 100644
index 0000000..6c63f22
--- /dev/null
+++ b/Runtime/Shaders/NameToObjectMap.h
@@ -0,0 +1,153 @@
+#ifndef NAMETOOBJECTMAP_H
+#define NAMETOOBJECTMAP_H
+
+#include <string>
+#include <map>
+#include "Runtime/BaseClasses/BaseObject.h"
+#if UNITY_EDITOR
+#include "Editor/Src/BuildPipeline/BuildSerialization.h"
+#endif
+
+using std::map;
+using std::string;
+using std::pair;
+
+
+static inline bool IsBuiltinResourceObject(Object* o)
+{
+ // built-in resources have this flag set
+ if (o->TestHideFlag(Object::kHideAndDontSave))
+ return true;
+
+ // Resources from builtin_extra only have "not editable" flag set, so the above check doesn't catch them.
+ // In the editor (where this matters mostly when building resource files), catch that by detecting
+ // if the object came from any built-in resources file.
+ #if UNITY_EDITOR
+ if (IsAnyDefaultResourcesObject(o->GetInstanceID()))
+ return true;
+ #endif
+
+ return false;
+}
+
+template<class Type, class ObjectToName, class NameToObject>
+class NameToObjectMap
+{
+private:
+ typedef typename ObjectToName::iterator ObjectToNameIterator;
+ typedef typename NameToObject::iterator NameToObjectIterator;
+
+ ObjectToName m_ObjectToName;
+ NameToObject m_NameToObject;
+ Object* m_ObjectToDirty;
+
+public:
+
+ DECLARE_SERIALIZE (NameToObjectMap)
+
+ void SetObjectToDirty (Object* dirty) { m_ObjectToDirty = dirty; }
+
+ void Add (const string& name, PPtr<Type> o)
+ {
+ Remove (o);
+ m_ObjectToName.insert (make_pair (o, name));
+ m_NameToObject.insert (make_pair (name, o));
+ AssertIf (m_NameToObject.size () != m_ObjectToName.size ());
+ m_ObjectToDirty->SetDirty ();
+ }
+
+ bool Remove (PPtr<Type> object)
+ {
+ AssertIf (m_NameToObject.size () != m_ObjectToName.size ());
+ int oldSize = m_NameToObject.size ();
+ {
+ pair<NameToObjectIterator, NameToObjectIterator> range = make_pair(m_NameToObject.begin(), m_NameToObject.end());
+
+ NameToObjectIterator i, next;
+ for (i=range.first;i!=range.second;i=next)
+ {
+ next = i; next++;
+ if (i->second == object)
+ {
+ m_NameToObject.erase (i);
+ }
+ }
+ }
+
+ {
+ pair<ObjectToNameIterator, ObjectToNameIterator> range;
+ range = m_ObjectToName.equal_range (object);
+ m_ObjectToName.erase(range.first, range.second);
+ }
+
+ m_ObjectToDirty->SetDirty ();
+ AssertIf (m_NameToObject.size () != m_ObjectToName.size ());
+ return oldSize != m_NameToObject.size ();
+ }
+
+ Type* Find (const string& name)
+ {
+ // Get all with name 'name'
+ pair<NameToObjectIterator, NameToObjectIterator> range;
+ range = m_NameToObject.equal_range (name);
+ NameToObjectIterator i, next;
+ Type* found = NULL;
+ // Then find the first that is loaded, those that can't be loaded
+ // are removed.
+ for (i=range.first;i!=range.second;i=next)
+ {
+ next = i; next++;
+ Type* o = i->second;
+
+ if (o)
+ {
+ // When there are two shaders one builtin resource and one normal shader
+ // Then we want the one in the project folder not the builtin one. So people can override shaders
+ // At some point we should try to get the ordering of shader includes better defined!
+ if (found && IsBuiltinResourceObject(o))
+ continue;
+
+ found = o;
+ }
+ }
+
+ return found;
+ }
+
+ std::vector<PPtr<Type> > GetAllObjects ()
+ {
+ std::vector<PPtr<Type> > objects;
+ for (NameToObjectIterator i=m_NameToObject.begin ();i!=m_NameToObject.end ();i++)
+ {
+ objects.push_back(i->second);
+ }
+ return objects;
+ }
+
+ const NameToObject& GetAll ()
+ {
+ return m_NameToObject;
+ }
+
+private:
+
+ void Rebuild ()
+ {
+ // Rebuild name -> object
+ m_NameToObject.clear ();
+ ObjectToNameIterator i;
+ for (i=m_ObjectToName.begin ();i != m_ObjectToName.end ();i++)
+ m_NameToObject.insert (make_pair (i->second, i->first));
+ }
+};
+
+template<class Type, class ObjectToName, class NameToObject>
+template<class TransferFunction>
+void NameToObjectMap<Type, ObjectToName, NameToObject>::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (m_ObjectToName);
+ if (transfer.IsReading ())
+ Rebuild ();
+}
+
+#endif
diff --git a/Runtime/Shaders/Shader.cpp b/Runtime/Shaders/Shader.cpp
new file mode 100644
index 0000000..b33aa00
--- /dev/null
+++ b/Runtime/Shaders/Shader.cpp
@@ -0,0 +1,734 @@
+#include "UnityPrefix.h"
+#include "Shader.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "Material.h"
+#include "ShaderNameRegistry.h"
+#include "Runtime/Utilities/Word.h"
+#include "External/shaderlab/Library/ShaderParser.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "GraphicsCaps.h"
+#include "External/shaderlab/Library/SLParserData.h"
+
+#if UNITY_EDITOR
+#include "External/shaderlab/Library/ShaderWriter.h"
+#endif
+#include "Runtime/Profiler/MemoryProfiler.h"
+
+using std::vector;
+
+// To get a precompiled version of specific platform shader
+// Create this shader in UnityEditor and click on "Open compiled shader"
+//Shader "DefaultShader"
+//{
+// SubShader
+// {
+// Pass
+// {
+// CGPROGRAM
+//
+// #pragma vertex vert
+// #pragma fragment frag
+// float4 vert (float4 pos : POSITION) : SV_POSITION { return mul(UNITY_MATRIX_MVP,pos); }
+// float4 frag () : COLOR { return float4(1,0,1,1); }
+// ENDCG
+// }
+// }
+//}
+
+
+
+static const char *gDefaultString =
+"Shader \"Default\" {"
+#if UNITY_XENON
+"SubShader { Pass { Cull Off \n"
+"Program \"vp\" { SubProgram \"xbox360 \" { Keywords { }\n"
+" Bind \"vertex\" Vertex\n"
+" Matrix 0 [glstate_matrix_mvp]\n"
+" \"vs_360\n"
+" backbbabaaaaaalmaaaaaagmaaaaaaaaaaaaaaceaaaaaaaaaaaaaajaaaaaaaaa\n"
+" aaaaaaaaaaaaaagiaaaaaabmaaaaaaflpppoadaaaaaaaaabaaaaaabmaaaaaaaa\n"
+" aaaaaafeaaaaaadaaaacaaaaaaaeaaaaaaaaaaeeaaaaaaaaghgmhdhegbhegffp\n"
+" gngbhehcgjhifpgnhghaaaklaaadaaadaaaeaaaeaaabaaaaaaaaaaaahghdfpdd\n"
+" fpdaaadccodacodjdddcdicodaaaklklaaaaaaaaaaaaaagmaaabaaabaaaaaaaa\n"
+" aaaaaaaaaaaaaaaaaaaaaaabaaaaaaabaaaaaaaaaaaaacjaaaaaaaadbaabbaad\n"
+" aaaabcaamcaaaaaaaaaaeaaeaaaabcaameaaaaaaaaaaaaadaaaaccaaaaaaaaaa\n"
+" afpibaaaaaaaagiiaaaaaaaamiapaaaaaabliiaakbabadaamiapaaaaaamgiiaa\n"
+" klabacaamiapaaaaaalbdejeklababaamiapiadoaagmaadeklabaaaaaaaaaaaa\n"
+" aaaaaaaaaaaaaaaa\"\n"
+"}}\n"
+"Program \"fp\" { SubProgram \"xbox360 \" { Keywords { }\n"
+"\"ps_360\n"
+" backbbaaaaaaaakaaaaaaageaaaaaaaaaaaaaaceaaaaaafiaaaaaaiaaaaaaaaa\n"
+" aaaaaaaaaaaaaadaaaaaaabmaaaaaacdppppadaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
+" aaaaaabmhahdfpddfpdaaadccodacodjdddcdicodaaaklklaaaaaaaaaaaaaaab\n"
+" aaaaaaaaaaaaaaaaaaaaaabeabpmaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
+" aaaaaaeaaaaaaacebaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab\n"
+" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
+" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadpiaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
+" aaaaaaaabaabmeaaccaaaaaabenmmaaaaaaaaagmmcaaaappaaaaaaaaaaaaaaaa\n"
+" aaaaaaaa\"\n"
+"}}\n"
+"}}\n"
+#elif UNITY_PS3
+"SubShader { Pass { Cull Off \n"
+"Program \"vp\" { SubProgram \"ps3 \" { Keywords { }\n"
+"Matrix 256 [glstate_matrix_mvp]\n"
+"Bind \"vertex\" Vertex\n"
+"\"sce_vp_rsx // 4 instructions using 1 registers\n"
+"[Configuration]\n"
+"8\n"
+"0000000400010100\n"
+"[Microcode]\n"
+"64\n"
+"401f9c6c01d0300d8106c0c360403f80401f9c6c01d0200d8106c0c360405f80\n"
+"401f9c6c01d0100d8106c0c360409f80401f9c6c01d0000d8106c0c360411f81\n"
+"\"\n"
+"}}\n"
+"Program \"fp\" { SubProgram \"ps3 \" { Keywords { }\n"
+"\"sce_fp_rsx // 2 instructions using 2 registers\n"
+"[Configuration]\n"
+"24\n"
+"ffffffff000000200000ffff000000000000840002000000\n"
+"[Microcode]\n"
+"32\n"
+"1e81014008021c9cc8000001c800000100003f80000000000000000000000000\n"
+"\"\n"
+"}}\n"
+"}}\n"
+
+#else
+
+#if GFX_SUPPORTS_D3D11
+"SubShader { Pass {\n"
+"Program \"vp\" {\n"
+"SubProgram \"d3d11 \" { Bind \"vertex\" Vertex ConstBuffer \"UnityPerDraw\" 336 Matrix 0 [glstate_matrix_mvp] 4 BindCB \"UnityPerDraw\" 0\n"
+"\"vs_dx11\n"
+"eefiecedijhpljdppnfhjnjaadaickkmhicpkjbcabaaaaaaheabaaaaadaaaaaa\n"
+"cmaaaaaagaaaaaaajeaaaaaaejfdeheocmaaaaaaabaaaaaaaiaaaaaacaaaaaaa\n"
+"aaaaaaaaaaaaaaaaadaaaaaaaaaaaaaaapapaaaafaepfdejfeejepeoaaklklkl\n"
+"epfdeheocmaaaaaaabaaaaaaaiaaaaaacaaaaaaaaaaaaaaaabaaaaaaadaaaaaa\n"
+"aaaaaaaaapaaaaaafdfgfpfaepfdejfeejepeoaafdeieefcniaaaaaaeaaaabaa\n"
+"dgaaaaaafjaaaaaeegiocaaaaaaaaaaaaeaaaaaafpaaaaadpcbabaaaaaaaaaaa\n"
+"ghaaaaaepccabaaaaaaaaaaaabaaaaaagiaaaaacabaaaaaadiaaaaaipcaabaaa\n"
+"aaaaaaaafgbfbaaaaaaaaaaaegiocaaaaaaaaaaaabaaaaaadcaaaaakpcaabaaa\n"
+"aaaaaaaaegiocaaaaaaaaaaaaaaaaaaaagbabaaaaaaaaaaaegaobaaaaaaaaaaa\n"
+"dcaaaaakpcaabaaaaaaaaaaaegiocaaaaaaaaaaaacaaaaaakgbkbaaaaaaaaaaa\n"
+"egaobaaaaaaaaaaadcaaaaakpccabaaaaaaaaaaaegiocaaaaaaaaaaaadaaaaaa\n"
+"pgbpbaaaaaaaaaaaegaobaaaaaaaaaaadoaaaaab\"\n"
+"}\n"
+"SubProgram \"d3d11_9x \" {\n"
+"Bind \"vertex\" Vertex\n"
+"ConstBuffer \"UnityPerDraw\" 336 \n"
+"Matrix 0 [glstate_matrix_mvp] 4\n"
+"BindCB \"UnityPerDraw\" 0\n"
+"\"vs_4_0_level_9_1\n"
+"eefieceddggiiplcpkoeljnckhkjahapknjdfpkhabaaaaaadeacaaaaaeaaaaaa\n"
+"daaaaaaaomaaaaaammabaaaaaaacaaaaebgpgodjleaaaaaaleaaaaaaaaacpopp\n"
+"iaaaaaaadeaaaaaaabaaceaaaaaadaaaaaaadaaaaaaaceaaabaadaaaaaaaaaaa\n"
+"aeaaabaaaaaaaaaaaaaaaaaaaaacpoppbpaaaaacafaaaaiaaaaaapjaafaaaaad\n"
+"aaaaapiaaaaaffjaacaaoekaaeaaaaaeaaaaapiaabaaoekaaaaaaajaaaaaoeia\n"
+"aeaaaaaeaaaaapiaadaaoekaaaaakkjaaaaaoeiaaeaaaaaeaaaaapiaaeaaoeka\n"
+"aaaappjaaaaaoeiaaeaaaaaeaaaaadmaaaaappiaaaaaoekaaaaaoeiaabaaaaac\n"
+"aaaaammaaaaaoeiappppaaaafdeieefcniaaaaaaeaaaabaadgaaaaaafjaaaaae\n"
+"egiocaaaaaaaaaaaaeaaaaaafpaaaaadpcbabaaaaaaaaaaaghaaaaaepccabaaa\n"
+"aaaaaaaaabaaaaaagiaaaaacabaaaaaadiaaaaaipcaabaaaaaaaaaaafgbfbaaa\n"
+"aaaaaaaaegiocaaaaaaaaaaaabaaaaaadcaaaaakpcaabaaaaaaaaaaaegiocaaa\n"
+"aaaaaaaaaaaaaaaaagbabaaaaaaaaaaaegaobaaaaaaaaaaadcaaaaakpcaabaaa\n"
+"aaaaaaaaegiocaaaaaaaaaaaacaaaaaakgbkbaaaaaaaaaaaegaobaaaaaaaaaaa\n"
+"dcaaaaakpccabaaaaaaaaaaaegiocaaaaaaaaaaaadaaaaaapgbpbaaaaaaaaaaa\n"
+"egaobaaaaaaaaaaadoaaaaabejfdeheocmaaaaaaabaaaaaaaiaaaaaacaaaaaaa\n"
+"aaaaaaaaaaaaaaaaadaaaaaaaaaaaaaaapapaaaafaepfdejfeejepeoaaklklkl\n"
+"epfdeheocmaaaaaaabaaaaaaaiaaaaaacaaaaaaaaaaaaaaaabaaaaaaadaaaaaa\n"
+"aaaaaaaaapaaaaaafdfgfpfaepfdejfeejepeoaa\"\n"
+"}\n"
+"}\n"
+"Program \"fp\" {\n"
+"SubProgram \"d3d11 \" {\n"
+"\"ps_dx11\n"
+"eefiecedlfbokalingbkfpbcgbelaibibjjpegjnabaaaaaalaaaaaaaadaaaaaa\n"
+"cmaaaaaadmaaaaaahaaaaaaaejfdeheoaiaaaaaaaaaaaaaaaiaaaaaaepfdeheo\n"
+"cmaaaaaaabaaaaaaaiaaaaaacaaaaaaaaaaaaaaaaaaaaaaaadaaaaaaaaaaaaaa\n"
+"apaaaaaafdfgfpfegbhcghgfheaaklklfdeieefcdiaaaaaaeaaaaaaaaoaaaaaa\n"
+"gfaaaaadpccabaaaaaaaaaaadgaaaaaipccabaaaaaaaaaaaaceaaaaaaaaaiadp\n"
+"aaaaaaaaaaaaiadpaaaaiadpdoaaaaab\"\n"
+"}\n"
+"SubProgram \"d3d11_9x \" {\n"
+"\"ps_4_0_level_9_1\n"
+"eefiecednclpehpkkhodapchnemhifoohfkahfmoabaaaaaaamabaaaaaeaaaaaa\n"
+"daaaaaaaiiaaaaaamiaaaaaaniaaaaaaebgpgodjfaaaaaaafaaaaaaaaaacpppp\n"
+"cmaaaaaaceaaaaaaaaaaceaaaaaaceaaaaaaceaaaaaaceaaaaaaceaaaaacpppp\n"
+"fbaaaaafaaaaapkaaaaaiadpaaaaaaaaaaaaiadpaaaaiadpabaaaaacaaaiapia\n"
+"aaaaoekappppaaaafdeieefcdiaaaaaaeaaaaaaaaoaaaaaagfaaaaadpccabaaa\n"
+"aaaaaaaadgaaaaaipccabaaaaaaaaaaaaceaaaaaaaaaiadpaaaaaaaaaaaaiadp\n"
+"aaaaiadpdoaaaaabejfdeheoaiaaaaaaaaaaaaaaaiaaaaaaepfdeheocmaaaaaa\n"
+"abaaaaaaaiaaaaaacaaaaaaaaaaaaaaaaaaaaaaaadaaaaaaaaaaaaaaapaaaaaa\n"
+"fdfgfpfegbhcghgfheaaklkl\"\n"
+"}\n"
+"}\n"
+"}}"
+#endif
+
+" SubShader { Tags { \"ForceSupported\" = \"True\" } Pass{ Cull Off SetTexture[_Dummy] { constantColor (1,0,1,1) combine constant }}} \n"
+#endif
+"}";
+
+int* gShaderLabContainer = NULL;
+
+static ShaderLab::IntShader* gDefaultShaderLabShader = NULL;
+static Shader* gDefaultShader = NULL;
+static Shader* s_ClearShader = NULL;
+static void MakeDefaultShaderLabShader ();
+
+Shader::Shader(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_ShaderIsBaked(false)
+{
+ MakeDefaultShaderLabShader ();
+ m_Shader = gDefaultShaderLabShader;
+ m_ShadowCasterPass = NULL;
+ m_ShadowCollectorPass = NULL;
+ m_NeedsParsing = false;
+
+ #if UNITY_EDITOR
+ m_ParsedForm = NULL;
+ #endif
+
+ if (mode != kCreateObjectFromNonMainThread)
+ PostLoad ();
+}
+
+Shader *Shader::GetDefault ()
+{
+ if (!gDefaultShader) {
+ SET_ALLOC_OWNER(gShaderLabContainer);
+ gDefaultShader = NEW_OBJECT(Shader);
+ gDefaultShader->Reset();
+ #if !UNITY_RELEASE
+ // in Shader's AwakeFromLoad it will try to parse script and call PostLoad
+ // we basically doing the same, but we assign default shader directly (w/o script)
+ gDefaultShader->HackSetAwakeWasCalled();
+ #endif
+
+ gDefaultShader->m_Shader = gDefaultShaderLabShader;
+ gDefaultShader->PostLoad();
+ gDefaultShader->SetHideFlags (kHideAndDontSave);
+ }
+ return gDefaultShader;
+}
+
+Shader* Shader::GetScreenClearShader ()
+{
+ Assert (s_ClearShader);
+ return s_ClearShader;
+}
+
+
+void Shader::LoadDefaultShaders ()
+{
+ GetDefault ();
+
+ #if GFX_SUPPORTS_D3D11 || GFX_SUPPORTS_XENON
+ if (IsGfxDevice())
+ {
+ Assert (!s_ClearShader);
+ s_ClearShader = GetBuiltinResource<Shader> ("Internal-Clear.shader");
+ }
+ #endif
+}
+
+
+Shader::~Shader ()
+{
+ if (m_Shader != gDefaultShaderLabShader)
+ UNITY_DELETE(m_Shader, kMemShader);
+
+ if (this == gDefaultShader)
+ gDefaultShader = NULL;
+
+ #if UNITY_EDITOR
+ UNITY_DELETE( m_ParsedForm, kMemShader);
+ #endif
+}
+
+bool Shader::MainThreadCleanup ()
+{
+ if (m_Shader != gDefaultShaderLabShader)
+ {
+ m_Shader->MainThreadCleanup ();
+ }
+
+ return true;
+}
+
+ShaderLab::PropertySheet *Shader::MakeProperties () const
+{
+ return m_Shader->MakeProperties ();
+}
+
+#if UNITY_EDITOR
+
+UInt32 Shader::CalculateUsedVertexComponents (bool lightmapped)
+{
+ if (m_Shader == NULL)
+ return 0;
+
+ UInt32 mask = 0;
+
+ const ShaderLab::IntShader::SubShaders& subshaders = m_Shader->GetSubShaders();
+ for (int i=0;i<subshaders.size();i++)
+ {
+ int npasses = subshaders[i]->GetTotalPassCount();
+ for (int p = 0; p < npasses; ++p)
+ {
+ mask |= subshaders[i]->GetPass(p)->CalculateUsedVertexComponents(lightmapped);
+ }
+ }
+
+ return mask;
+}
+
+int Shader::GetPropertyCount () const
+{
+ if (!m_ParsedForm)
+ return 0;
+ return m_ParsedForm->m_PropInfo.GetPropertyCount();
+}
+
+const ShaderLab::ParserProperty* Shader::GetPropertyInfo (int propertyNo) const
+{
+ if (!m_ParsedForm)
+ return NULL;
+ return m_ParsedForm->m_PropInfo.GetPropertyInfo (propertyNo);
+}
+
+bool Shader::HasClip() const
+{
+ return m_ParsedForm ? m_ParsedForm->HasClip() : false;
+}
+
+char const* Shader::GetCustomEditorName() const
+{
+ if (!m_Shader)
+ return NULL;
+ return m_Shader->GetCustomEditor ().c_str ();
+}
+
+#endif
+
+PROFILER_INFORMATION(gSetPassProfile, "Shader.SetPass", kProfilerRender);
+
+const ChannelAssigns* Shader::SetPass( int subshaderIndex, int passNo, UInt32 stateKey, const ShaderLab::PropertySheet* props )
+{
+ PROFILER_AUTO(gSetPassProfile, this)
+ return m_Shader->GetSubShader(subshaderIndex).SetPass (passNo, stateKey, props);
+}
+
+bool Shader::CanPassBeRecorded(int subshaderIndex, int passNo) const
+{
+ const ShaderLab::Pass* pass = m_Shader->GetSubShader(subshaderIndex).GetPass(passNo);
+ return pass->CanPassBeRecorded();
+}
+
+ShaderLab::Pass* Shader::GetShadowCasterPassToUse(int subshaderIndex)
+{
+ ShaderLab::SubShader& subShader = m_Shader->GetSubShader(subshaderIndex);
+ if (!subShader.ShadowCasterPassEnabled())
+ return NULL;
+ int ssPass = subShader.GetShadowCasterPassIndex();
+ if (ssPass >= 0)
+ return subShader.GetPass(ssPass);
+ return m_ShadowCasterPass;
+}
+
+ShaderLab::Pass* Shader::GetShadowCollectorPassToUse(int subshaderIndex)
+{
+ ShaderLab::SubShader& subShader = m_Shader->GetSubShader(subshaderIndex);
+ int ssPass = subShader.GetShadowCollectorPassIndex();
+ if (ssPass >= 0)
+ return subShader.GetPass(ssPass);
+ return m_ShadowCollectorPass;
+}
+
+
+int Shader::GetActiveSubShaderIndex () const
+{
+ return GetShaderLabShader()->GetActiveSubShaderIndex();
+}
+
+bool Shader::SetScript (const TextAsset::ScriptString& script)
+{
+ Super::SetScriptDontDirty (script);
+
+ m_ShadowCasterPass = NULL;
+ m_ShadowCollectorPass = NULL;
+
+ ParseAndPostLoad( script.c_str(), script.size() );
+
+ return m_Shader != gDefaultShaderLabShader;
+}
+
+
+namespace ShaderLab {
+extern const char* kPassLightModeTypeNames[kShaderPassTypeCount];
+}
+
+
+static ShaderLab::Pass* FindLightModePass( ShaderLab::IntShader* shader, ShaderPassType type )
+{
+ using ShaderLab::TagMap;
+ typedef ShaderLab::IntShader::SubShaders SubShaders;
+
+ static const int lightModeTagID = ShaderLab::GetShaderTagID("LIGHTMODE");
+
+ const SubShaders& subshaders = shader->GetSubShaders();
+ for( SubShaders::const_iterator it = subshaders.begin(); it != subshaders.end(); ++it )
+ {
+ ShaderLab::SubShader& ss = **it;
+ for( int i = 0; i < ss.GetValidPassCount (); ++i )
+ {
+ const TagMap& tags = ss.GetPass(i)->GetTags();
+ TagMap::const_iterator found = tags.find (lightModeTagID);
+ if( found != tags.end() && (StrICmp( ShaderLab::GetShaderTagName(found->second), ShaderLab::kPassLightModeTypeNames[type] ) == 0 ) )
+ {
+ return ss.GetPass(i);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void Shader::PostLoad ()
+{
+ m_Shader->PostLoad();
+
+ // Find out shadow caster/collector passes
+ Assert (m_ShadowCasterPass == NULL && m_ShadowCollectorPass == NULL);
+ m_ShadowCasterPass = FindLightModePass( m_Shader, kPassShadowCaster );
+ m_ShadowCollectorPass = FindLightModePass( m_Shader, kPassShadowCollector );
+}
+
+void Shader::AddMaterialUser( ListNode<Unity::Material>& node )
+{
+ m_Users.push_back( node );
+}
+
+PROFILER_INFORMATION(gShaderParseProfile, "Shader.Parse", kProfilerRender);
+
+void Shader::Parse (const char *str, size_t strLength)
+{
+ PROFILER_AUTO(gShaderParseProfile, this);
+ SET_ALLOC_OWNER(this);
+
+ m_NeedsParsing = false;
+ if( m_Shader != gDefaultShaderLabShader )
+ UNITY_DELETE(m_Shader, kMemShader);
+
+ #if UNITY_EDITOR
+ UNITY_DELETE( m_ParsedForm, kMemShader);
+ m_ParsedForm = NULL;
+ ShaderErrors& errors = m_Errors;
+ #else
+ ShaderErrors errors;
+ #endif
+
+ ShaderLab::ParserShader* parsedForm = NULL;
+ m_Shader = ParseShader (str, strLength, m_Dependencies, m_ShaderIsBaked, &parsedForm, errors, GetGfxDevice().GetRenderer());
+ // In editor, keep the parsed form so we can serialize shaders out specific for a platform. In players, delete parsed form now.
+ #if UNITY_EDITOR
+ m_ParsedForm = parsedForm;
+ #else
+ UNITY_DELETE( parsedForm, kMemShader);
+ #endif
+
+
+ m_ShadowCasterPass = NULL;
+ m_ShadowCollectorPass = NULL;
+ m_ShaderName = g_LastParsedShaderName;
+
+ // For no-fixed function hardware and shaders that ended up having only shadow related passes
+ // (like VertexLit), insert the pass from default shader. Otherwise the shader is supported, but
+ // has no passes suitable for any rendering path, hence all objects with it are not rendered at all.
+ if (!gGraphicsCaps.hasFixedFunction && m_Shader && gDefaultShaderLabShader)
+ {
+ ShaderLab::SubShader& ss = m_Shader->GetActiveSubShader();
+ if (ss.GetHasOnlyShadowPasses()) {
+ printf_console("Unsupported: %s\n", m_ShaderName.c_str());
+ ss.InsertDefaultPass (gDefaultShaderLabShader->GetActiveSubShader().GetPass(0));
+ }
+ }
+
+ if( !m_Shader )
+ {
+ printf_console("Unsupported: %s\n", m_ShaderName.c_str());
+ m_Shader = gDefaultShaderLabShader;
+ return;
+ }
+
+ if (m_Shader->HasNoSubShaders())
+ {
+ char buf[256];
+ snprintf(buf, 255, "No valid subshaders in '%s'.shader", GetScriptClassName().c_str());
+ ErrorStringObject (buf, this);
+ UNITY_DELETE(m_Shader, kMemShader);
+ m_Shader = gDefaultShaderLabShader;
+ }
+}
+
+
+void Shader::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ SET_ALLOC_OWNER(this);
+ Super::AwakeFromLoad (awakeMode);
+ const ScriptString& s = GetScript();
+ ParseAndPostLoad( s.c_str(), s.size() );
+}
+
+void Shader::AwakeFromLoadThreaded ()
+{
+ Super::AwakeFromLoadThreaded ();
+
+ // In some cases a shader is constructed (on the loading thread, has shader pointing
+ // to the default one, and m_NeedsParsing flag set to false). Later on
+ // actual shader is being loaded, but no one resets the m_NeedsParsing flag.
+ // This seems to happen only in a very rare combination of shaders loaded via fallbacks
+ // combined with shaders being referenced from scripts only; and even then it
+ // highly depends on the order the shaders end up being loaded. Couldn't
+ // make an automated repro case.
+ if (m_Shader == gDefaultShaderLabShader)
+ {
+ m_Shader = NULL;
+ m_ShadowCasterPass = NULL;
+ m_ShadowCollectorPass = NULL;
+ m_NeedsParsing = true;
+ }
+}
+
+template<class TransferFunc>
+void Shader::Transfer (TransferFunc& transfer)
+{
+ #if UNITY_EDITOR
+ if (transfer.IsBuildingTargetPlatform(kBuildAnyPlayerData))
+ {
+ UnityStr newSource = ProduceTargetSpecificShader (m_Script, m_ParsedForm, transfer.GetBuildingTarget());
+
+ Super::Super::Transfer (transfer);
+ transfer.Transfer (newSource, "m_Script", kHideInEditorMask);
+ transfer.Transfer (m_PathName, "m_PathName", kHideInEditorMask);
+ }
+ else
+ Super::Transfer (transfer);
+ #else
+ Super::Transfer (transfer);
+ #endif
+
+ transfer.Transfer (m_Dependencies, "m_Dependencies");
+
+ // Shaders in player data files and asset bundles should get "shader is baked" flag,
+ // so they get their dependencies directly instead of through script mapper.
+ // Need to set this flag to true when building the data files, but properly
+ // reset it back afterwards.
+ if (transfer.IsWritingGameReleaseData())
+ {
+ bool shaderIsBaked = true;
+ transfer.Transfer (shaderIsBaked, "m_ShaderIsBaked");
+ }
+ else
+ {
+ transfer.Transfer (m_ShaderIsBaked, "m_ShaderIsBaked");
+ }
+
+ transfer.Align();
+
+ #if UNITY_EDITOR
+ if (!transfer.IsSerializingForGameRelease() && !transfer.IsRemapPPtrTransfer ())
+ {
+ transfer.Transfer (m_Errors.GetErrors(), "errors");
+ transfer.Transfer (m_DefaultTextures, "m_DefaultTextures", kHideInEditorMask);
+ }
+ #endif
+
+}
+
+char const* Shader::GetName () const {
+ // shader name is the name of the shader, not the filename
+ return GetScriptClassName().c_str();
+}
+
+
+bool Shader::IsSupported () const
+{
+ return m_Shader != gDefaultShaderLabShader;
+}
+
+int Shader::GetSubShaderWithTagValue (int tagNameID, int tagValueID) const
+{
+ using ShaderLab::TagMap;
+ typedef ShaderLab::IntShader::SubShaders SubShaders;
+
+ const SubShaders& ss = m_Shader->GetSubShaders();
+ int idx = 0;
+ for( SubShaders::const_iterator i = ss.begin(); i != ss.end(); ++i )
+ {
+ const TagMap& tags = (**i).GetTags();
+ TagMap::const_iterator j = tags.find(tagNameID);
+ if (j != tags.end () && j->second == tagValueID)
+ return idx;
+ idx++;
+ }
+ return -1;
+}
+
+bool Shader::IsDependentOn (PPtr<Shader> shader) const
+{
+ for (int i = 0; i < m_Dependencies.size (); ++i)
+ if (m_Dependencies[i] == shader ||
+ m_Dependencies[i]->IsDependentOn (shader))
+ return true;
+
+ return false;
+}
+
+Shader* Shader::GetDependency (const std::string& name)
+{
+ if (!m_Shader)
+ return NULL;
+ const std::string* dependencyName = m_Shader->GetDependency (name);
+ if (!dependencyName || dependencyName->empty())
+ return NULL;
+ Shader* res = FindShaderLabShader (*dependencyName, m_Dependencies, m_ShaderIsBaked);
+ return res;
+}
+
+
+void Shader::UnloadDefaultShaderLabShader()
+{
+ UNITY_DELETE(gDefaultShaderLabShader, kMemShader);
+ gDefaultShaderLabShader = NULL;
+ if (gDefaultShader)
+ gDefaultShader->m_Shader = NULL;
+}
+
+void Shader::LoadDefaultShaderLabShader()
+{
+ ShaderLab::ParserShader* parsedForm = NULL;
+ ShaderErrors errors;
+ ShaderPtrVector dependencies;
+ gDefaultShaderLabShader = ParseShader (gDefaultString, strlen(gDefaultString), dependencies, false, &parsedForm, errors, GetGfxDevice().GetRenderer());
+ if (errors.HasErrorsOrWarnings()) {
+ ErrorString ("Default Shader has errors! Something is wrong.");
+ }
+ UNITY_DELETE(parsedForm, kMemShader);
+
+ if (gDefaultShader)
+ {
+ gDefaultShader->m_Shader = gDefaultShaderLabShader;
+ gDefaultShader->PostLoad();
+ }
+}
+
+static void MakeDefaultShaderLabShader ()
+{
+ if (gDefaultShaderLabShader)
+ return;
+ gShaderLabContainer = UNITY_NEW_AS_ROOT(int, kMemShader, "ShaderLab", "");
+ SET_ALLOC_OWNER(gShaderLabContainer);
+ ShaderLab::InitShaderLab ();
+
+ Shader::LoadDefaultShaderLabShader();
+}
+
+void CleanupShaders ()
+{
+ Shader::UnloadDefaultShaderLabShader();
+ ShaderLab::CleanupShaderLab();
+ // still some allocations left in shaderlab, so we don't want to delete this one before someone has cleaned up shaderlab
+// UNITY_DELETE(gShaderLabContainer, kMemShader);
+}
+
+void Shader::DeleteAllShaders( std::vector<SInt32>& outShaderObjects )
+{
+ outShaderObjects.clear();
+ Object::FindAllDerivedObjects (ClassID (Shader), &outShaderObjects);
+ for (std::vector<SInt32>::iterator i = outShaderObjects.begin(); i != outShaderObjects.end(); i++)
+ {
+ Shader *s = PPtr<Shader> (*i);
+ for (MaterialList::iterator sit = s->m_Users.begin(); sit != s->m_Users.end(); ++sit)
+ {
+ (*sit)->InvalidateDisplayLists();
+ }
+ if (s->m_Shader != gDefaultShaderLabShader)
+ {
+ UNITY_DELETE(s->m_Shader, kMemShader);
+ s->m_Shader = NULL;
+ s->m_ShadowCasterPass = NULL;
+ s->m_ShadowCollectorPass = NULL;
+ s->m_NeedsParsing = true;
+ }
+ }
+}
+
+void Shader::RecreateAllShaders( const std::vector<SInt32>& shaderObjects )
+{
+ for( std::vector<SInt32>::const_iterator i = shaderObjects.begin(); i != shaderObjects.end(); ++i )
+ {
+ Shader *s = PPtr<Shader> (*i);
+ if( s != gDefaultShader )
+ {
+ const ScriptString& script = s->GetScript();
+ s->ParseAndPostLoad( script.c_str(), script.size() );
+ }
+ }
+}
+
+
+void Shader::ReloadAllShaders()
+{
+ std::vector<SInt32> derivedObjects;
+ DeleteAllShaders( derivedObjects );
+ RecreateAllShaders( derivedObjects );
+}
+
+void Shader::SetGLobalMaximumShaderLOD( int lod )
+{
+ if( ShaderLab::g_GlobalMaximumShaderLOD == lod )
+ return;
+ ShaderLab::g_GlobalMaximumShaderLOD = lod;
+
+ // notify all shaders of global LOD change
+ std::vector<SInt32> shaders;
+ Object::FindAllDerivedObjects (ClassID (Shader), &shaders);
+ for (std::vector<SInt32>::iterator i = shaders.begin(); i != shaders.end(); ++i)
+ {
+ Shader* s = PPtr<Shader> (*i);
+ s->m_Shader->PostLoad();
+ }
+}
+
+int Shader::GetGlobalMaximumShaderLOD()
+{
+ return ShaderLab::g_GlobalMaximumShaderLOD;
+}
+
+void Shader::SetMaximumShaderLOD( int lod )
+{
+ if(GetMaximumShaderLOD() == lod)
+ return;
+
+ GetShaderLabShader()->SetMaximumShaderLOD(lod);
+}
+
+int Shader::GetMaximumShaderLOD( ) const
+{
+ return GetShaderLabShader()->GetMaximumShaderLOD();
+}
+
+
+IMPLEMENT_CLASS (Shader)
+IMPLEMENT_OBJECT_SERIALIZE (Shader)
diff --git a/Runtime/Shaders/Shader.h b/Runtime/Shaders/Shader.h
new file mode 100644
index 0000000..a992d1d
--- /dev/null
+++ b/Runtime/Shaders/Shader.h
@@ -0,0 +1,174 @@
+#ifndef SHADER_H
+#define SHADER_H
+
+#include "Runtime/Scripting/TextAsset.h"
+#include <string>
+#include "Runtime/Math/Color.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "External/shaderlab/Library/ShaderLabErrors.h"
+#include "Runtime/Graphics/Texture.h"
+
+using std::string;
+
+namespace ShaderLab
+{
+ class IntShader;
+ class PropertySheet;
+ struct ParserProperty;
+ class Pass;
+ struct ParserShader;
+}
+namespace Unity { class Material; }
+class Shader;
+class ChannelAssigns;
+typedef std::vector< PPtr<Shader> > ShaderPtrVector;
+
+
+// A Wrapper for a ShaderLab shader.
+// This class handles all the Unity-specific interfacing into the shader.
+class Shader : public TextAsset {
+ public:
+ REGISTER_DERIVED_CLASS (Shader, TextAsset)
+ DECLARE_OBJECT_SERIALIZE (Shader)
+
+ Shader (MemLabelId label, ObjectCreationMode mode);
+ // ~Shader (); declared-by-macro
+
+ virtual bool MainThreadCleanup ();
+
+ // Set the next pass to render
+ const ChannelAssigns* SetPass (int subshaderIndex, int passNo, UInt32 stateKey, const ShaderLab::PropertySheet* props);
+
+ bool CanPassBeRecorded(int subshaderIndex, int passNo) const;
+
+ ShaderLab::Pass* GetShadowCasterPassToUse(int subshaderIndex);
+ ShaderLab::Pass* GetShadowCollectorPassToUse(int subshaderIndex);
+
+ bool AnySubshaderHasShadowCasterPass() const { return m_ShadowCollectorPass != NULL; }
+ bool HasShadowCollectorPass() const { return m_ShadowCollectorPass != NULL; }
+ int GetActiveSubShaderIndex () const;
+
+ // Set the shader string
+ virtual bool SetScript (const ScriptString& script);
+
+ // Did this shader compile & work, or are we using the default
+ bool IsSupported () const;
+
+ // Get the default Shader
+ static Shader* GetDefault ();
+ static Shader* GetScreenClearShader ();
+ static void LoadDefaultShaders ();
+
+ static void UnloadDefaultShaderLabShader();
+ static void LoadDefaultShaderLabShader();
+ void ResetInternalPointersToNull()
+ {
+ m_Shader = NULL;
+ m_ShadowCasterPass = NULL;
+ m_ShadowCollectorPass = NULL;
+ }
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+ virtual void AwakeFromLoadThreaded ();
+
+ // Make a new property sheet.
+ ShaderLab::PropertySheet *MakeProperties () const;
+
+
+ #if UNITY_EDITOR
+ int GetPropertyCount () const;
+ const ShaderLab::ParserProperty *GetPropertyInfo (int propertyNo) const;
+ #endif
+
+ // from TextAsset, returns shader name
+ virtual const UnityStr& GetScriptClassName () const { return m_ShaderName; }
+ // from Object, returns shader name
+ virtual char const* GetName () const;
+ // GetName() is overriden, use this to return name from named object
+ // A bit of a hack, see case 364626
+ char const* GetNamedObjectName() const { return m_Name.empty() ? GetName() : m_Name.c_str (); }
+
+ const ShaderLab::IntShader *GetShaderLabShader() const { return m_Shader; }
+ ShaderLab::IntShader *GetShaderLabShader() { return m_Shader; }
+
+ // reparses and reloads all shaders
+ static void ReloadAllShaders();
+ // those two do the same as ReloadAllShaders. Use if there is something to be done in between.
+ static void DeleteAllShaders( std::vector<SInt32>& outShaderObjects );
+ static void RecreateAllShaders( const std::vector<SInt32>& shaderObjects );
+
+ static void SetGLobalMaximumShaderLOD( int lod );
+ static int GetGlobalMaximumShaderLOD();
+
+ void SetMaximumShaderLOD( int lod );
+ int GetMaximumShaderLOD() const;
+
+ // Get the index of a subshader with a given type tag.
+ int GetSubShaderWithTagValue (int tagNameID, int tagValueID) const;
+
+ void AddMaterialUser( ListNode<Unity::Material>& node );
+
+ bool GetNeedsParsing() const { return m_NeedsParsing; }
+
+ void ParseAndPostLoad( const char* str, size_t strLength ) { Parse(str,strLength); PostLoad(); }
+
+ bool IsDependentOn (PPtr<Shader> shader) const;
+ Shader* GetDependency (const std::string& name);
+ const ShaderPtrVector& GetDependencies () const { return m_Dependencies; }
+
+ ShaderLab::Pass* GetShadowCasterPass() { return m_ShadowCasterPass; }
+ ShaderLab::Pass* GetShadowCollectorPass() { return m_ShadowCollectorPass; }
+
+ #if UNITY_EDITOR
+ const ShaderLab::ParserShader* GetParsedForm() const { return m_ParsedForm; }
+ const ShaderErrors& GetErrors() const { return m_Errors; }
+ ShaderErrors& GetErrors() { return m_Errors; }
+ UInt32 CalculateUsedVertexComponents (bool lightmapped);
+
+ typedef std::map<UnityStr, PPtr<Texture> > DefaultTexturesMap;
+ const DefaultTexturesMap& GetDefaultTextures () const { return m_DefaultTextures; }
+ void SetDefaultTextures (const DefaultTexturesMap& textures) { m_DefaultTextures = textures; }
+
+ bool HasClip() const;
+ char const* GetCustomEditorName() const;
+ #endif
+
+private:
+ // Function to extract renderqueue & channels from a shader.
+ void PostLoad ();
+ // Parse a string into the shader
+ void Parse (const char *str, size_t strLength);
+
+ ShaderLab::IntShader* m_Shader; // the actual shader
+ ShaderLab::Pass* m_ShadowCasterPass; // shadow caster pass, if any
+ ShaderLab::Pass* m_ShadowCollectorPass; // shadow collector pass, if any
+
+ // List of Materials that use this shader
+ typedef List< ListNode<Unity::Material> > MaterialList;
+ MaterialList m_Users;
+
+
+ // Shaders that are not supported get assigned a "default shader". However, we really
+ // want to remember it's original name (so we can build players with proper ScriptMapper info,
+ // and display the correct name in shader popups, etc.).
+ //
+ // So remember the actual name here, and not in ShaderLab
+ // shader instance (since single "default" shaderlab instance is shared by all unsupported shaders).
+ UnityStr m_ShaderName;
+
+ ShaderPtrVector m_Dependencies; // shader pointers used by Dependencies / UsePasses / Fallbacks from this shader
+ bool m_ShaderIsBaked;
+
+ bool m_NeedsParsing;
+
+ #if UNITY_EDITOR
+ ShaderErrors m_Errors;
+ ShaderLab::ParserShader* m_ParsedForm;
+
+ DefaultTexturesMap m_DefaultTextures;
+ #endif
+};
+
+void CleanupShaders ();
+
+#endif
diff --git a/Runtime/Shaders/ShaderKeywords.cpp b/Runtime/Shaders/ShaderKeywords.cpp
new file mode 100644
index 0000000..c751574
--- /dev/null
+++ b/Runtime/Shaders/ShaderKeywords.cpp
@@ -0,0 +1,90 @@
+#include "UnityPrefix.h"
+#include "ShaderKeywords.h"
+#include <map>
+
+typedef std::map<std::string, ShaderKeyword> KeywordMap;
+
+static KeywordMap* s_KeywordMap;
+ShaderKeywordSet g_ShaderKeywords;
+
+
+void keywords::Initialize()
+{
+ Assert(!s_KeywordMap);
+ s_KeywordMap = UNITY_NEW( KeywordMap, kMemShader);
+ SET_ALLOC_OWNER(NULL);
+ // precreate common keywords
+
+ // Important: these must go first
+ Create( "SPOT" );
+ Create( "DIRECTIONAL" );
+ Create( "DIRECTIONAL_COOKIE" );
+ Create( "POINT" );
+ Create( "POINT_COOKIE" );
+
+ Create( "SHADOWS_OFF" );
+ Create( "SHADOWS_DEPTH" );
+ Create( "SHADOWS_SCREEN" );
+ Create( "SHADOWS_CUBE" );
+ Create( "SHADOWS_SOFT" );
+ Create( "SHADOWS_SPLIT_SPHERES" );
+ Create( "SHADOWS_NATIVE" );
+
+ Create( "LIGHTMAP_OFF" );
+ Create( "LIGHTMAP_ON" );
+
+ Create( "DIRLIGHTMAP_OFF" );
+ Create( "DIRLIGHTMAP_ON" );
+
+ Create( "VERTEXLIGHT_ON" );
+ Create( "SOFTPARTICLES_ON" );
+
+ Create( "HDR_LIGHT_PREPASS_ON" );
+}
+
+void keywords::Cleanup()
+{
+ UNITY_DELETE( s_KeywordMap, kMemShader);
+ s_KeywordMap = NULL;
+}
+
+ShaderKeyword keywords::Create( const std::string& name )
+{
+ if( !s_KeywordMap ) {
+ Initialize();
+ ANALYSIS_ASSUME(s_KeywordMap);
+ }
+
+ KeywordMap::const_iterator it = s_KeywordMap->find( name );
+ if( it != s_KeywordMap->end() )
+ return it->second;
+
+ ShaderKeyword key = s_KeywordMap->size();
+ if( key >= kMaxShaderKeywords )
+ {
+ AssertString( Format("Maximum number (%i) of shader keywords exceeded, keyword %s will be ignored", kMaxShaderKeywords, name.c_str()) );
+ }
+ else
+ {
+ SET_ALLOC_OWNER(NULL);
+ s_KeywordMap->insert( std::make_pair( name, key ) );
+ }
+ return key;
+}
+
+#if UNITY_EDITOR
+std::string keywords::GetKeywordName (ShaderKeyword k)
+{
+ if (!s_KeywordMap) {
+ Initialize();
+ ANALYSIS_ASSUME(s_KeywordMap);
+ }
+ for (KeywordMap::const_iterator it = s_KeywordMap->begin(); it != s_KeywordMap->end(); ++it)
+ {
+ if (it->second == k)
+ return it->first;
+ }
+ AssertString ("Requesting non existing shader keyword");
+ return "";
+}
+#endif
diff --git a/Runtime/Shaders/ShaderKeywords.h b/Runtime/Shaders/ShaderKeywords.h
new file mode 100644
index 0000000..7a273bb
--- /dev/null
+++ b/Runtime/Shaders/ShaderKeywords.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <string>
+
+typedef int ShaderKeyword;
+
+// We use about 14 builtin keywords now, 18 more for the user should be fine now
+// (shader size explosion will happen before that limit is reached anyways...)
+const int kMaxShaderKeywords = 64;
+struct ShaderKeywordSet {
+public:
+ explicit ShaderKeywordSet () : mask(0) {}
+
+ void Enable (ShaderKeyword key) { mask |= (0x1ULL<<key); }
+ void Disable (ShaderKeyword key) { mask &= ~(0x1ULL<<key); }
+ bool IsEnabled (ShaderKeyword key) const { return (mask & (0x1ULL<<key)) != 0; }
+ void Reset () { mask = 0; }
+
+ UInt64 GetMask() const { return mask; }
+ void SetMask (UInt64 m) { mask = m; }
+
+ bool operator== (const ShaderKeywordSet& o) const { return mask==o.mask; }
+ bool operator!= (const ShaderKeywordSet& o) const { return mask!=o.mask; }
+
+private:
+ UInt64 mask;
+};
+
+extern ShaderKeywordSet g_ShaderKeywords;
+
+
+namespace keywords {
+
+ void Initialize();
+ void Cleanup();
+
+ ShaderKeyword Create( const std::string& name );
+
+ #if UNITY_EDITOR
+ std::string GetKeywordName (ShaderKeyword k);
+ #endif
+
+} // namespace keywords
diff --git a/Runtime/Shaders/ShaderNameRegistry.cpp b/Runtime/Shaders/ShaderNameRegistry.cpp
new file mode 100644
index 0000000..58e9e34
--- /dev/null
+++ b/Runtime/Shaders/ShaderNameRegistry.cpp
@@ -0,0 +1,235 @@
+#include "UnityPrefix.h"
+#include "ShaderNameRegistry.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "External/shaderlab/Library/shaderlab.h"
+
+
+ScriptMapper::ScriptMapper (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_Shaders.SetObjectToDirty (this);
+}
+
+ScriptMapper::~ScriptMapper ()
+{
+}
+
+
+void ScriptMapper::AddShader (Shader& ptr)
+{
+ m_Shaders.Add (ptr.GetScriptClassName (), &ptr);
+}
+
+void ScriptMapper::AddBuiltinShader (const string& shaderClassName, PPtr<Shader> ptr)
+{
+ m_Shaders.Add (shaderClassName, ptr);
+}
+
+
+static const char* kBackwardsCompatShaderNames[] = {
+ " VertexLit", "VertexLit",
+ " Diffuse", "Diffuse",
+ " Glossy", "Specular",
+ " Bumped", "Bumped Diffuse",
+ " BumpedSpecular", "Bumped Specular",
+ "ParallaxBump/Diffuse", "Parallax Diffuse",
+ "ParallaxBump/Specular", "Parallax Specular",
+
+ "Alpha/VertexLit", "Transparent/VertexLit",
+ "Alpha/Diffuse", "Transparent/Diffuse",
+ "Alpha/Glossy", "Transparent/Specular",
+ "Alpha/Bumped", "Transparent/Bumped Diffuse",
+ "Alpha/BumpedSpecular", "Transparent/Bumped Specular",
+ "ParallaxBump/AlphaDiffuse", "Transparent/Parallax Diffuse",
+ "ParallaxBump/AlphaSpecular", "Transparent/Parallax Specular",
+
+ //"Reflective/VertexLit", "Reflective/VertexLit",
+ //"Reflective/Diffuse", "Reflective/Diffuse",
+ "Reflective/Glossy", "Reflective/Specular",
+ "Reflective/Bumped", "Reflective/Bumped Diffuse",
+ "Reflective/BumpedSpecular", "Reflective/Bumped Specular",
+ "ParallaxBump/ReflectDiffuse", "Reflective/Parallax Diffuse",
+ "ParallaxBump/ReflectSpecular", "Reflective/Parallax Specular",
+
+ "Lightmapped/Glossy", "Legacy Shaders/Lightmapped/Specular",
+ "Lightmapped/Bumped", "Legacy Shaders/Lightmapped/Bumped Diffuse",
+ "Lightmapped/BumpedSpecular", "Legacy Shaders/Lightmapped/Bumped Specular",
+ "Lightmapped/VertexLit", "Legacy Shaders/Lightmapped/VertexLit",
+ "Lightmapped/Diffuse", "Legacy Shaders/Lightmapped/Diffuse",
+ "Lightmapped/Specular", "Legacy Shaders/Lightmapped/Specular",
+ "Lightmapped/Bumped Diffuse", "Legacy Shaders/Lightmapped/Bumped Diffuse",
+ "Lightmapped/Bumped Specular", "Legacy Shaders/Lightmapped/Bumped Specular",
+ "Diffuse Fast", "Legacy Shaders/Diffuse Fast",
+
+
+ //"Self-Illumin/VertexLit", "Self-Illumin/VertexLit",
+ //"Self-Illumin/Diffuse", "Self-Illumin/Diffuse",
+ "Self-Illumin/Glossy", "Self-Illumin/Specular",
+ "Self-Illumin/Bumped", "Self-Illumin/Bumped Diffuse",
+ "Self-Illumin/BumpedSpecular", "Self-Illumin/Bumped Specular",
+ "ParallaxBump/IlluminDiffuse", "Self-Illumin/Parallax Diffuse",
+ "ParallaxBump/IlluminSpecular", "Self-Illumin/Parallax Specular",
+
+ " DiffuseDetail", "Diffuse Detail",
+ " Diffuse (fast)", "Legacy Shaders/Diffuse Fast",
+ " Decal", "Decal",
+
+ "Hidden/TerrainEngine/Splatmap/Lightmap-FirstPass", "Nature/Terrain/Diffuse",
+};
+
+
+static const char* GetNonLegacyShaderName(const std::string& name)
+{
+ const int kShaderNameCount = sizeof(kBackwardsCompatShaderNames) / sizeof(kBackwardsCompatShaderNames[0]) / 2;
+ for (int i = 0; i < kShaderNameCount; ++i)
+ {
+ if (strcmp( kBackwardsCompatShaderNames[i*2+0], name.c_str()) == 0)
+ return kBackwardsCompatShaderNames[i*2+1];
+ }
+ return NULL;
+}
+
+
+Shader *ScriptMapper::FindShader (const string &name)
+{
+ Shader* result = m_Shaders.Find (name);
+ if( result != NULL )
+ return result;
+
+ // If shader is not found, try old names (before 2.0) of built-in shaders
+ const char* otherName = GetNonLegacyShaderName(name);
+ if (otherName)
+ return m_Shaders.Find(otherName);
+
+ return NULL;
+}
+
+
+
+template<class TransferFunction>
+void ScriptMapper::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ AssertIf (transfer.GetFlags() & kPerformUnloadDependencyTracking);
+ transfer.Transfer (m_Shaders, "m_Shaders");
+}
+
+bool ScriptMapper::ShouldIgnoreInGarbageDependencyTracking ()
+{
+ return true;
+}
+IMPLEMENT_CLASS (ScriptMapper)
+IMPLEMENT_OBJECT_SERIALIZE (ScriptMapper)
+GET_MANAGER (ScriptMapper)
+GET_MANAGER_PTR(ScriptMapper)
+
+
+static void ParseShaderIfNeeded (Shader* shader)
+{
+ Assert (!shader->GetNeedsParsing() || !shader->GetShaderLabShader());
+ if (!shader->GetNeedsParsing())
+ return;
+
+ const Shader::ScriptString& script = shader->GetScript();
+ // Save current shader name around shader parsing
+ std::string saveShaderName = g_LastParsedShaderName;
+ shader->ParseAndPostLoad (script.c_str(), script.size());
+ g_LastParsedShaderName = saveShaderName;
+}
+
+
+Shader* FindShaderLabShader (const std::string& name, ShaderPtrVector& shaderLookup, bool useLookup)
+{
+ Assert(GetScriptMapperPtr());
+
+ // Finding shaders can load new ones. So save and
+ // restore the currently parsed shader name.
+ std::string saveShaderName = g_LastParsedShaderName;
+
+ Shader* shader = NULL;
+ if (useLookup)
+ {
+ for (size_t i = 0; i < shaderLookup.size(); ++i)
+ {
+ Shader* s = shaderLookup[i];
+ if (s == NULL)
+ continue;
+ ParseShaderIfNeeded (s);
+ const std::string& shaderName = s->GetScriptClassName();
+ if (shaderName == name)
+ {
+ shader = s;
+ break;
+ }
+ // also try non-legacy name of the shader we're searching for,
+ // in case someone does " Diffuse" but we're meant
+ // to find "Diffuse" etc.
+ const char* nonLegacyShaderName = GetNonLegacyShaderName(name.c_str());
+ if (nonLegacyShaderName && !strcmp(shaderName.c_str(), nonLegacyShaderName))
+ {
+ shader = s;
+ break;
+ }
+ }
+ }
+ else
+ {
+ shader = GetScriptMapper().FindShader (name);
+ }
+
+ g_LastParsedShaderName = saveShaderName;
+
+
+ if (shader)
+ {
+ ParseShaderIfNeeded (shader);
+
+ if (!useLookup)
+ {
+ PPtr<Shader> shaderPtr(shader);
+ if (std::find(shaderLookup.begin(), shaderLookup.end(), shaderPtr) == shaderLookup.end())
+ shaderLookup.push_back (shaderPtr);
+ }
+
+ return shader;
+ }
+
+ return NULL;
+}
+
+
+
+// -------------------------------------------------------------------
+
+
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/shaderlab/Library/SLParserData.h"
+
+
+static void TestCanFindLegacyShaderNamesWhenUsingLookup ()
+{
+ const char* oldName = "Lightmapped/VertexLit"; // legacy name
+ ScriptMapper& sm = GetScriptMapper();
+ Shader* shader1 = sm.FindShader(oldName);
+ Assert (shader1 != NULL);
+
+ // now check if FindShaderLabShader can find it when using lookup table
+ ShaderPtrVector lookup;
+ lookup.push_back(shader1);
+ Shader* shader2 = FindShaderLabShader (oldName, lookup, true);
+ Assert (shader2 == shader1);
+}
+
+
+void TestHighLevelShaderNameRegistryTests()
+{
+ TestCanFindLegacyShaderNamesWhenUsingLookup();
+}
+
+#endif // #if ENABLE_UNIT_TESTS
diff --git a/Runtime/Shaders/ShaderNameRegistry.h b/Runtime/Shaders/ShaderNameRegistry.h
new file mode 100644
index 0000000..1643f4d
--- /dev/null
+++ b/Runtime/Shaders/ShaderNameRegistry.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include <string>
+#include <map>
+#include "Runtime/Shaders/NameToObjectMap.h"
+#include "Runtime/Modules/ExportModules.h"
+
+using std::string;
+
+class Shader;
+typedef std::vector< PPtr<Shader> > ShaderPtrVector;
+
+
+class EXPORT_COREMODULE ScriptMapper : public GlobalGameManager
+{
+public:
+ typedef std::multimap<UnityStr, PPtr<Shader> > StringToShader;
+ REGISTER_DERIVED_CLASS (ScriptMapper, GlobalGameManager)
+ DECLARE_OBJECT_SERIALIZE (ScriptMapper)
+
+ typedef NameToObjectMap<Shader, std::map<PPtr<Shader>, UnityStr>, std::multimap<UnityStr, PPtr<Shader> > > Shaders;
+
+ ScriptMapper (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~ScriptMapper (); declared-by-macro
+
+ // Add a shader to the system.
+ void AddShader (Shader& ptr);
+ void AddBuiltinShader (const std::string& className, PPtr<Shader> ptr);
+
+ // Get a shader by name.
+ // If the shader could not be loaded (e.g. because it was deleted), it is removed from the master list.
+ Shader* FindShader (const string &name);
+
+ bool ShouldIgnoreInGarbageDependencyTracking ();
+
+ #if UNITY_EDITOR
+
+ Shader *GetDefaultShader() { return FindShader( "Diffuse" ); }
+
+ const Shaders& GetShaders () { return m_Shaders; }
+ void SetShaders (const Shaders& shaders) { m_Shaders = shaders; }
+
+ #endif // UNITY_EDITOR
+
+private:
+ Shaders m_Shaders;
+};
+
+ScriptMapper& GetScriptMapper ();
+ScriptMapper* GetScriptMapperPtr ();
+
+
+Shader* FindShaderLabShader (const std::string& name, ShaderPtrVector& shaderLookup, bool useLookup);
diff --git a/Runtime/Shaders/ShaderSupportScripts.cpp b/Runtime/Shaders/ShaderSupportScripts.cpp
new file mode 100644
index 0000000..3f60afa
--- /dev/null
+++ b/Runtime/Shaders/ShaderSupportScripts.cpp
@@ -0,0 +1,19 @@
+#include "UnityPrefix.h"
+#include "ShaderSupportScripts.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+
+IMPLEMENT_CLASS (CGProgram)
+
+CGProgram::CGProgram (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{}
+
+CGProgram::~CGProgram ()
+{
+}
+
+void CGProgram::SetCGProgram( const std::string& str )
+{
+ m_CGProgram = str;
+ m_CGProgramName = GetLastPathNameComponent(m_CGProgram);
+}
diff --git a/Runtime/Shaders/ShaderSupportScripts.h b/Runtime/Shaders/ShaderSupportScripts.h
new file mode 100644
index 0000000..e799721
--- /dev/null
+++ b/Runtime/Shaders/ShaderSupportScripts.h
@@ -0,0 +1,26 @@
+#ifndef SHADERSUPPORTSCRIPTS_H
+#define SHADERSUPPORTSCRIPTS_H
+
+#include "Runtime/Scripting/TextAsset.h"
+#include <string>
+
+
+// Shader include files (mainly exist so that a different icon can be used on them)
+class CGProgram : public TextAsset {
+public:
+ REGISTER_DERIVED_CLASS (CGProgram, TextAsset)
+
+ CGProgram (MemLabelId label, ObjectCreationMode mode);
+
+ virtual const UnityStr& GetScriptClassName() const { return m_CGProgramName; }
+
+ void SetCGProgram( const std::string& str );
+ const UnityStr& GetCGProgram () const { return m_CGProgram; }
+
+private:
+ // Placeholder string used for the original CG program - we only use this at importing
+ UnityStr m_CGProgram;
+ UnityStr m_CGProgramName;
+};
+
+#endif
diff --git a/Runtime/Shaders/ShaderTags.h b/Runtime/Shaders/ShaderTags.h
new file mode 100644
index 0000000..252b165
--- /dev/null
+++ b/Runtime/Shaders/ShaderTags.h
@@ -0,0 +1,27 @@
+#ifndef SHADER_TAGS_H
+#define SHADER_TAGS_H
+
+// The pass type of a given pass.
+// The shader sets this as a hint to the app as to how it wants to be rendered using the LightMode tag
+enum ShaderPassType {
+ kPassAlways, // Always executed. No lighting.
+ kPassVertex, // Vertex lights + ambient
+ kPassVertexLM, // Vertex lighting w/ lightmaps
+ kPassVertexLMRGBM, // Vertex lighting w/ lightmaps encoded as RGBM
+ kPassForwardBase,
+ kPassForwardAdd,
+ kPassLightPrePassBase,
+ kPassLightPrePassFinal,
+ kPassShadowCaster, // Renders object as shadow caster
+ kPassShadowCollector, // Renders object, collecting shadows into screen space texture
+ kShaderPassTypeCount // Keep this last!
+};
+
+
+enum ShaderRenderOptions {
+ kShaderOptionSoftVegetation = 0, // soft vegetation is currently on
+ kShaderRenderOptionCount // keep this last!
+};
+
+
+#endif
diff --git a/Runtime/Shaders/ShaderTests.cpp b/Runtime/Shaders/ShaderTests.cpp
new file mode 100644
index 0000000..2139543
--- /dev/null
+++ b/Runtime/Shaders/ShaderTests.cpp
@@ -0,0 +1,285 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+#include "External/UnitTest++/src/UnitTest++.h"
+
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+
+#include "External/shaderlab/Library/intshader.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/ShaderParser.h"
+#include "External/shaderlab/Library/SLParserData.h"
+#include "External/shaderlab/Library/ShaderParserInterm.h"
+#include "External/shaderlab/Library/ShaderParser.h"
+
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Utilities/Word.h"
+
+using namespace ShaderLab;
+
+static void CheckPassAffectingProps_TestHelper (const char* desc, const ShaderLab::Pass* pass, const char* propName, ...)
+{
+ const PropertyNamesSet& names = pass->GetAffectingProps();
+
+ vector_set<int> namesFound;
+ bool error = false;
+
+ // See if properties we expect do in fact affect the pass
+ const char* currName = propName;
+ va_list ap;
+ va_start (ap, propName);
+ while (currName != NULL)
+ {
+ ShaderLab::FastPropertyName currProp = ShaderLab::Property(currName);
+ if (names.names.find(currProp.index) == names.names.end())
+ {
+ printf_console("ShaderTests '%s': property %s expected to affect it, but it doesn't!\n", desc, currName);
+ error = true;
+ }
+ else
+ namesFound.insert(currProp.index);
+ currName = va_arg (ap, const char*);
+ };
+ va_end (ap);
+
+ // Now print all properties that are affecting the pass, but we didn't expect them to
+ for (vector_set<int>::const_iterator it = names.names.begin(); it != names.names.end(); ++it)
+ {
+ if (namesFound.find(*it) == namesFound.end())
+ {
+ ShaderLab::FastPropertyName prop;
+ prop.index = *it;
+ printf_console("Shader test %s: property %s is affecting shader, but is not expected to!\n", desc, prop.GetName());
+ error = true;
+ }
+ }
+
+ // If we had errors, print out all properties that are affecting the pass
+ if (error)
+ {
+ std::string msg = "..shader test failed, properties that are affecting: ";
+ for (vector_set<int>::const_iterator it = names.names.begin(); it != names.names.end(); ++it)
+ {
+ ShaderLab::FastPropertyName name;
+ name.index = *it;
+ msg += name.GetName();
+ msg += " ";
+ }
+ printf_console("%s\n\n", msg.c_str());
+ printf_console ("ShaderTests: Shader Pass affecting properties test failed, check output log");
+ CHECK (false);
+ }
+}
+
+struct ParseTestFixture
+{
+ ShaderErrors errors;
+ bool ParseTest (const char* text)
+ {
+ errors.Clear();
+ ShaderLab::ParserShader* parsedForm = PreParseShader (text, strlen(text), kGfxRendererNull, errors);
+ if (!parsedForm)
+ return false;
+ UNITY_DELETE(parsedForm,kMemShader);
+ return true;
+ }
+};
+
+SUITE (ShaderTests)
+{
+#if UNITY_EDITOR
+
+ TEST (Shader_ShadowCollectorPassCorrectZWrite_EditorOnly)
+ {
+ if (GetGfxDevice().GetRenderer() == kGfxRendererNull)
+ return;
+
+ // Ensure the presence of the 'VertexLit' shader.
+ Shader* shader = GetScriptMapper().FindShader ("VertexLit");
+ CHECK (shader != NULL);
+
+ // 'VertexLit' shader should always have shadow collector pass.
+ dynamic_array<ShaderLab::Pass*> collectorPasses;
+ shader->GetShaderLabShader()->GetActiveSubShader().FindNamedPasses ("SHADOWCOLLECTOR", collectorPasses);
+ CHECK (!collectorPasses.empty());
+
+ // shadow collector should use LEqual depth test!
+ const DeviceDepthState* depthState = collectorPasses[0]->GetState().GetDepthState();
+ CHECK (depthState);
+ CHECK (depthState->sourceState.depthFunc == kFuncLEqual);
+
+ // FindNamedPasses calls Retain(), so do not leak memory: need to release them
+ for (int i = 0; i < collectorPasses.size(); ++i)
+ collectorPasses[i]->Release();
+ }
+
+ TEST (Shader_RenderQueueRespectsLOD_EditorOnly)
+ {
+ // two subshaders with different LOD, using different render queues
+ const char* kShaderText =
+ "Shader \"__Foo\" {\n"
+ "SubShader { LOD 300 Tags {Queue=Geometry} Pass { } }\n"
+ "SubShader { LOD 100 Tags {Queue=Transparent} Pass { } }\n"
+ "}\n";
+ ShaderLab::ParserShader* parsedForm = NULL;
+ ShaderErrors errors;
+ ShaderPtrVector dependencies;
+ ShaderLab::IntShader* shader = ParseShader(kShaderText, strlen(kShaderText), dependencies, false, &parsedForm, errors, kGfxRendererD3D9);
+ CHECK (shader && !errors.HasErrorsOrWarnings());
+ CHECK (shader->GetSubShaders().size() == 2);
+
+ // uses first subshader, Geometry queue
+ CHECK (shader->GetRenderQueue() == 2000);
+
+ // set LOD to not use the first subshader
+ shader->SetMaximumShaderLOD(200);
+ // should get queue from the 2nd subshader
+ CHECK (shader->GetRenderQueue() == 3000);
+
+ UNITY_DELETE( parsedForm, kMemShader);
+ UNITY_DELETE( shader, kMemShader);
+ }
+
+ TEST (Shader_DependenciesDontHaveDuplicates_EditorOnly)
+ {
+ // two subshaders with different LOD, using different render queues
+ const char* kShaderText =
+ "Shader \"__Foo\" {\n"
+ "SubShader {\n"
+ "UsePass \"VertexLit/SHADOWCASTER\"\n"
+ "UsePass \"VertexLit/SHADOWCASTER\"\n"
+ "UsePass \"VertexLit/SHADOWCASTER\"\n"
+ "}\n"
+ "Fallback Off\n"
+ "}\n";
+ ShaderLab::ParserShader* parsedForm = NULL;
+ ShaderErrors errors;
+ ShaderPtrVector dependencies;
+ ShaderLab::IntShader* shader = ParseShader(kShaderText, strlen(kShaderText), dependencies, false, &parsedForm, errors, kGfxRendererD3D9);
+ CHECK (shader && !errors.HasErrorsOrWarnings());
+ CHECK (shader->GetSubShaders().size() == 1);
+
+ CHECK (dependencies.size() == 1);
+
+ UNITY_DELETE (parsedForm, kMemShader);
+ UNITY_DELETE (shader, kMemShader);
+ }
+
+ static void UsedVertexComponents_TestHelper (const char* name, UInt32 expected, UInt32 expectedLM)
+ {
+ Shader* shader = GetScriptMapper().FindShader (name);
+ Assert (shader);
+ UInt32 channels = shader->CalculateUsedVertexComponents(false);
+ Assert (channels == expected);
+ channels = shader->CalculateUsedVertexComponents(true);
+ Assert (channels == expectedLM);
+ }
+
+ TEST (Shader_UsedVertexComponents_EditorOnly)
+ {
+ if (gGraphicsCaps.GetEmulation() != GraphicsCaps::kEmulNone ||
+ GetGfxDevice().GetRenderer() == kGfxRendererNull ||
+ GetGfxDevice().GetRenderer() == kGfxRendererD3D11)
+ return;
+
+ UInt32 expLightingUV = (1<<kShaderChannelVertex) | (1<<kShaderChannelNormal) | (1<<kShaderChannelTexCoord0);
+ UInt32 expLightingUV2 = (1<<kShaderChannelVertex) | (1<<kShaderChannelNormal) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelTexCoord1);
+ UInt32 expLightingTanUV = (1<<kShaderChannelVertex) | (1<<kShaderChannelNormal) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelTangent);
+ UInt32 expLightingTanUV2 = (1<<kShaderChannelVertex) | (1<<kShaderChannelNormal) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelTexCoord1) | (1<<kShaderChannelTangent);
+ UInt32 expUV1 = (1<<kShaderChannelVertex) | (1<<kShaderChannelTexCoord0);
+ UInt32 expColorUV1 = (1<<kShaderChannelVertex) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelColor);
+
+ UsedVertexComponents_TestHelper ("VertexLit", expLightingUV, expLightingUV2);
+ UsedVertexComponents_TestHelper ("Diffuse", expLightingUV, expLightingUV2);
+ UsedVertexComponents_TestHelper ("Bumped Diffuse", expLightingTanUV, expLightingTanUV2);
+ UsedVertexComponents_TestHelper ("Unlit/Texture", expUV1, expUV1);
+ UsedVertexComponents_TestHelper ("Particles/Additive", expColorUV1, expColorUV1);
+ }
+
+ TEST (Shader_PassAffectingPropsWork_EditorOnly)
+ {
+ if (GetGfxDevice().GetRenderer() == kGfxRendererNull || gGraphicsCaps.shaderCaps < kShaderLevel2)
+ return;
+
+ Shader* shSpec = GetBuiltinExtraResource<Shader>("Normal-Glossy.shader");
+ CHECK (shSpec != NULL);
+
+ CheckPassAffectingProps_TestHelper("Specular ForwardBase pass", shSpec->GetShaderLabShader()->GetSubShader(0).GetPass(0), "_MainTex", "_Color", "_Shininess", "_SpecColor", "_MainTex_ST", "_ShadowMapTexture", NULL);
+ CheckPassAffectingProps_TestHelper("Specular ForwardAdd pass", shSpec->GetShaderLabShader()->GetSubShader(0).GetPass(1), "_MainTex", "_Color", "_Shininess", "_SpecColor", "_MainTex_ST", "_LightTexture0", "_LightTextureB0", NULL);
+
+ // None of user-accessible properties are affecting shadow caster pass
+ CheckPassAffectingProps_TestHelper("Specular ShadowCaster pass", shSpec->GetShadowCasterPass(), NULL);
+
+ // Specular falls back to VertexLit, so subshader #1 is vertex lit
+ CheckPassAffectingProps_TestHelper("VertexLit vertex pass", shSpec->GetShaderLabShader()->GetSubShader(1).GetPass(0), "_MainTex", "_Color", "_Shininess", "_SpecColor", "_Emission", NULL);
+
+ Shader* shCutoutBSpec = GetBuiltinExtraResource<Shader>("AlphaTest-Bumped.shader");
+ CHECK (shCutoutBSpec != NULL);
+
+ CheckPassAffectingProps_TestHelper("Cutout BumpSpec ForwardBase pass", shCutoutBSpec->GetShaderLabShader()->GetSubShader(0).GetPass(0), "_MainTex", "_Cutoff", "_BumpMap", "_Color", "_MainTex_ST", "_BumpMap_ST", "_ShadowMapTexture", NULL);
+
+ // Alpha tested shader: texture, cutoff etc. affect shadow caster pass. Bumpmap doesn't affect it.
+ CheckPassAffectingProps_TestHelper("Cutout BumpSpec pass", shCutoutBSpec->GetShadowCasterPass(), "_MainTex", "_Cutoff", "_Color", "_MainTex_ST", NULL);
+ }
+
+ TEST (Shader_MaterialGetDefaultProperties_EditorOnly)
+ {
+ // Check that a shader with some default property values actually gets them in the material.
+ const char* kShader =
+ "Shader \"\" {\n"
+ "Properties {\n"
+ "myColor (\"\", color) = (1,2,3,4)\n"
+ "myFloat (\"\", float) = 0.5\n"
+ "myRange (\"\", range(0,10)) = 5.0\n"
+ "myVector (\"\", vector) = (5,6,7,8)\n"
+ "}\n"
+ "SubShader { Pass { } }\n"
+ "}";
+ Material* mat = Material::CreateMaterial(kShader,Object::kHideAndDontSave);
+ Shader* myShader = mat->GetShader();
+
+ ShaderLab::FastPropertyName propShine = ShaderLab::Property("_Shininess");
+ ShaderLab::FastPropertyName propMyColor = ShaderLab::Property("myColor");
+ ShaderLab::FastPropertyName propMyFloat = ShaderLab::Property("myFloat");
+ ShaderLab::FastPropertyName propMyRange = ShaderLab::Property("myRange");
+ ShaderLab::FastPropertyName propMyVector = ShaderLab::Property("myVector");
+
+ // Don't compare in linear space; returned value close but slightly different due to float conversions.
+ if (GetActiveColorSpace() == kGammaColorSpace)
+ {
+ CHECK (mat->GetColor(propMyColor).Equals(ColorRGBAf(1,2,3,4)));
+ }
+ CHECK (mat->GetFloat(propMyFloat) == 0.5f);
+ CHECK (mat->GetFloat(propMyRange) == 5.0f);
+ CHECK (mat->GetColor(propMyVector).Equals(ColorRGBAf(5,6,7,8)));
+
+ // Assign a different shader.
+ Shader* vlitShader = GetScriptMapper().FindShader("VertexLit");
+ CHECK (!mat->HasProperty(propShine));
+ mat->SetShader (vlitShader);
+
+ // Now we should get _Shininess, with the default value.
+ float shininess = mat->GetFloat(propShine);
+ CHECK (shininess == 0.7f);
+
+ // Now, new shader doesn't have myFloat, so it shouldn't be in the runtime PropertySheet.
+ CHECK (!mat->HasProperty(propMyFloat));
+ // But the value should still be intact in the serialized sheet!
+ CHECK (mat->GetSavedProperties().m_Floats[propMyFloat] == 0.5f);
+
+ DestroySingleObject (mat);
+ DestroySingleObject (myShader);
+ }
+
+#endif // #if UNITY_EDITOR
+}
+
+#endif // #if ENABLE_UNIT_TESTS
diff --git a/Runtime/Shaders/UnityPropertySheet.cpp b/Runtime/Shaders/UnityPropertySheet.cpp
new file mode 100644
index 0000000..7dc894d
--- /dev/null
+++ b/Runtime/Shaders/UnityPropertySheet.cpp
@@ -0,0 +1,144 @@
+#include "UnityPrefix.h"
+#include "UnityPropertySheet.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/shaderlab/Library/SLParserData.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+
+UnityPropertySheet::UnityTexEnv::UnityTexEnv()
+{
+ m_Scale = Vector2f(1,1);
+ m_Offset = Vector2f(0,0);
+ m_Texture = 0;
+}
+
+
+#if UNITY_EDITOR
+void UnityPropertySheet::CullUnusedProperties (const ShaderLab::ParserShader* source)
+{
+ if (!source)
+ return;
+
+ TexEnvMap::iterator curTex = m_TexEnvs.begin();
+ TexEnvMap::iterator texEnd = m_TexEnvs.end();
+
+ while (curTex != texEnd)
+ {
+ const char* propName = curTex->first.GetName();
+
+ bool propFound = false;
+ for (unsigned i = 0; i < source->m_PropInfo.m_Props.size(); ++i)
+ {
+ const ShaderLab::ParserProperty& srcProp = source->m_PropInfo.m_Props[i];
+ if(srcProp.m_Type == ShaderLab::ParserProperty::kTexture
+ && ::strcmp( srcProp.m_Name.c_str(), propName ) == 0
+ )
+ {
+ propFound = true;
+ break;
+ }
+ }
+
+ if (propFound)
+ ++curTex;
+ else
+ m_TexEnvs.erase(curTex++);
+
+ }
+}
+#endif
+
+void UnityPropertySheet::AssignDefinedPropertiesTo (ShaderLab::PropertySheet &target)
+{
+ for (ShaderLab::PropertySheet::Floats::iterator i = target.GetFloatsMap().begin(); i != target.GetFloatsMap().end(); i++)
+ {
+ FloatMap::iterator j = m_Floats.find(i->first);
+ if (j != m_Floats.end())
+ {
+ i->second = j->second;
+ }
+ }
+ for (ShaderLab::PropertySheet::Vectors::iterator i = target.GetVectorMap().begin(); i != target.GetVectorMap().end(); i++)
+ {
+ ColorMap::iterator j = m_Colors.find(i->first);
+ if (j != m_Colors.end())
+ {
+ if (target.GetColorTag(i->first))
+ target.SetVector (i->first, GammaToActiveColorSpace(j->second).GetPtr());
+ else
+ target.SetVector (i->first, j->second.GetPtr());
+ }
+ }
+ for (ShaderLab::PropertySheet::TexEnvs::iterator i = target.GetTexEnvsMap().begin(); i != target.GetTexEnvsMap().end(); i++)
+ {
+ TexEnvMap::iterator j = m_TexEnvs.find(i->first);
+ if (j != m_TexEnvs.end())
+ {
+ target.SetTextureWithPlacement( i->first, j->second.m_Texture, j->second.m_Scale, j->second.m_Offset );
+ }
+ }
+}
+
+
+bool UnityPropertySheet::AddNewShaderlabProps (const ShaderLab::PropertySheet &source)
+{
+ bool addedAny = false;
+ for (ShaderLab::PropertySheet::Floats::const_iterator i = source.GetFloatsMap().begin(); i != source.GetFloatsMap().end(); i++)
+ {
+ if (m_Floats.insert( std::make_pair(i->first, i->second) ).second)
+ addedAny = true;
+ }
+ for (ShaderLab::PropertySheet::Vectors::const_iterator i = source.GetVectorMap().begin(); i != source.GetVectorMap().end(); i++)
+ {
+ // skip texture _ST & _TexelSize properties
+ bool isTextureProp = false;
+ for (ShaderLab::PropertySheet::TexEnvs::const_iterator j = source.GetTexEnvsMap().begin(); j != source.GetTexEnvsMap().end(); ++j)
+ {
+ if (j->second.scaleOffsetValue == &i->second || j->second.texelSizeValue == &i->second)
+ {
+ isTextureProp = true;
+ break;
+ }
+ }
+ if (isTextureProp)
+ continue;
+
+ if (m_Colors.insert( std::make_pair(i->first, ColorRGBAf(i->second.x, i->second.y, i->second.z, i->second.w)) ).second)
+ addedAny = true;
+ }
+ for (ShaderLab::PropertySheet::TexEnvs::const_iterator i = source.GetTexEnvsMap().begin(); i != source.GetTexEnvsMap().end(); i++)
+ {
+ if (m_TexEnvs.find (i->first) == m_TexEnvs.end())
+ {
+ UnityTexEnv ut;
+ const Matrix4x4f& mat = i->second.texEnv->GetMatrix();
+ ut.m_Scale.Set( mat.Get(0,0), mat.Get(1,1) );
+ Vector3f pos = mat.GetPosition();
+ ut.m_Offset = Vector2f (pos.x, pos.y);
+ m_TexEnvs [i->first] = ut;
+ addedAny = true;
+ }
+ }
+
+ return addedAny;
+}
+
+void UnityPropertySheet::AddNewSerializedProps (const UnityPropertySheet &source)
+{
+ for (UnityPropertySheet::FloatMap::const_iterator i = source.m_Floats.begin(); i != source.m_Floats.end(); i++)
+ {
+ if (m_Floats.find (i->first) == m_Floats.end())
+ m_Floats [i->first] = i->second;
+ }
+ for (UnityPropertySheet::ColorMap::const_iterator i = source.m_Colors.begin(); i != source.m_Colors.end(); i++)
+ {
+ if (m_Colors.find (i->first) == m_Colors.end())
+ m_Colors [i->first] = i->second;
+ }
+ for (UnityPropertySheet::TexEnvMap::const_iterator i = source.m_TexEnvs.begin(); i != source.m_TexEnvs.end(); i++)
+ {
+ if (m_TexEnvs.find (i->first) == m_TexEnvs.end())
+ m_TexEnvs [i->first] = i->second;
+ }
+}
diff --git a/Runtime/Shaders/UnityPropertySheet.h b/Runtime/Shaders/UnityPropertySheet.h
new file mode 100644
index 0000000..88d7b0d
--- /dev/null
+++ b/Runtime/Shaders/UnityPropertySheet.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include "External/shaderlab/Library/FastPropertyName.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Graphics/Texture.h"
+
+namespace ShaderLab {
+ class PropertySheet;
+ struct ParserShader;
+}
+
+// Serialized material property data (colors, textures, ...).
+// This is used only for saving & loading of materials; at runtime ShaderLab PropertySheets
+// are used (they can have more data that's not serialized, like matrices etc.).
+class UnityPropertySheet
+{
+public:
+ DECLARE_SERIALIZE (UnityPropertySheet)
+ struct UnityTexEnv {
+ UnityTexEnv();
+
+ DECLARE_SERIALIZE (UnityTexEnv)
+ Vector2f m_Scale;
+ Vector2f m_Offset;
+ PPtr<Texture> m_Texture;
+ };
+ typedef std::map<ShaderLab::FastPropertyName, UnityTexEnv> TexEnvMap;
+ typedef std::map<ShaderLab::FastPropertyName, float> FloatMap;
+ typedef std::map<ShaderLab::FastPropertyName, ColorRGBAf> ColorMap;
+ TexEnvMap m_TexEnvs;
+ FloatMap m_Floats;
+ ColorMap m_Colors;
+
+ // Set the properties of target.
+ // This attempts to fill the properties of target with any info this may have.
+ // It never adds a property to target.
+ void AssignDefinedPropertiesTo (ShaderLab::PropertySheet &target);
+
+ // Add any properties defined by source, without overwriting anything already here.
+ bool AddNewShaderlabProps (const ShaderLab::PropertySheet &source);
+ void AddNewSerializedProps (const UnityPropertySheet &source);
+
+#if UNITY_EDITOR
+ // Remove any properties not used by source.
+ // This is called when building a player by Material::Transfer
+ void CullUnusedProperties (const ShaderLab::ParserShader* source);
+#endif
+};
+
+template<class TransferFunc>
+void UnityPropertySheet::Transfer (TransferFunc& transfer)
+{
+ transfer.SetVersion (2);
+
+ TRANSFER (m_TexEnvs);
+ TRANSFER (m_Floats);
+ TRANSFER (m_Colors);
+}
+
+template<class TransferFunc>
+void UnityPropertySheet::UnityTexEnv::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (m_Texture);
+ TRANSFER (m_Scale);
+ TRANSFER (m_Offset);
+}
diff --git a/Runtime/Shaders/VBO.cpp b/Runtime/Shaders/VBO.cpp
new file mode 100644
index 0000000..700be8a
--- /dev/null
+++ b/Runtime/Shaders/VBO.cpp
@@ -0,0 +1,170 @@
+#include "UnityPrefix.h"
+#include "VBO.h"
+#include "GraphicsCaps.h"
+
+#include <cstring>
+#include "Runtime/Utilities/OptimizationUtility.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+#if UNITY_XENON
+#include <ppcintrinsics.h>
+#endif
+
+
+
+static const int kChannelVertexSize[kShaderChannelCount] = {
+ 3 * sizeof (float), // position
+ 3 * sizeof (float), // normal
+ 1 * sizeof (UInt32),// color
+ 2 * sizeof (float), // uv0
+ 2 * sizeof (float), // uv1
+ 4 * sizeof (float), // tangent
+};
+
+static const UInt8 kDefaultVertexChannelDimensionCount [kShaderChannelCount] = {
+ 3, 3, 1, 2, 2, 4
+};
+
+// -----------------------------------------------------------------------------
+// VBO common
+
+int VBO::GetDefaultChannelByteSize (int channelNum)
+{
+ DebugAssertIf( channelNum < 0 || channelNum >= kShaderChannelCount );
+ return kChannelVertexSize[channelNum];
+}
+
+int VBO::GetDefaultChannelFormat(int channelNum)
+{
+ DebugAssertIf( channelNum < 0 || channelNum >= kShaderChannelCount );
+ return (kShaderChannelColor == channelNum) ? kChannelFormatColor : kChannelFormatFloat;
+}
+
+int VBO::GetDefaultChannelDimension(int channelNum)
+{
+ return kDefaultVertexChannelDimensionCount [channelNum];
+}
+
+bool VBO::IsAnyStreamMapped() const
+{
+ for (int i = 0; i < kMaxVertexStreams; ++i)
+ if (m_IsStreamMapped[i])
+ return true;
+
+ return false;
+}
+
+bool VBO::HasStreamWithMode(StreamMode mode) const
+{
+ for (int i = 0; i < kMaxVertexStreams; ++i)
+ if (m_StreamModes[i] == mode && m_Streams[i].channelMask)
+ return true;
+
+ return false;
+}
+
+size_t GetVertexSize (unsigned shaderChannelsMask)
+{
+ size_t size = 0;
+ for (int i=0; i<kShaderChannelCount; ++i, shaderChannelsMask >>= 1)
+ if (shaderChannelsMask & 1)
+ size += kChannelVertexSize[i];
+ return size;
+}
+
+DynamicVBO::DynamicVBO()
+: m_LastChunkShaderChannelMask(0)
+, m_LastChunkStride(0)
+, m_LastChunkVertices(0)
+, m_LastChunkIndices(0)
+, m_LastRenderMode(kDrawIndexedTriangles)
+, m_LendedChunk(false)
+{
+}
+
+
+// ----------------------------------------------------------------------
+
+
+void CopyVertexStream( const VertexBufferData& sourceData, void* buffer, unsigned stream )
+{
+ DebugAssert(stream < kMaxVertexStreams);
+
+ if (!sourceData.buffer)
+ return;
+
+ const StreamInfo& info = sourceData.streams[stream];
+ const UInt8* src = static_cast<const UInt8*>(sourceData.buffer) + info.offset;
+ size_t size = CalculateVertexStreamSize(sourceData, stream);
+ #if UNITY_XENON
+ XMemCpyStreaming_WriteCombined(buffer, src, size);
+ #else
+ memcpy(buffer, src, size);
+ #endif
+}
+
+void CopyVertexBuffer( const VertexBufferData& sourceData, void* buffer )
+{
+ if (sourceData.buffer)
+ memcpy(buffer, sourceData.buffer, sourceData.bufferSize);
+}
+
+void GetVertexStreamOffsets( const VertexBufferData& sourceData, size_t dest[kShaderChannelCount], size_t baseOffset, unsigned stream )
+{
+ for (int i = 0; i < kShaderChannelCount; ++i)
+ {
+ const ChannelInfo& channel = sourceData.channels[i];
+ dest[i] = channel.IsValid() ? baseOffset + channel.CalcOffset(sourceData.streams) : 0;
+ }
+}
+
+void GetVertexStreamPointers( const VertexBufferData& sourceData, void* dest[kShaderChannelCount], void* basePtr, unsigned stream )
+{
+ for (int i = 0; i < kShaderChannelCount; ++i)
+ {
+ const ChannelInfo& channel = sourceData.channels[i];
+ dest[i] = channel.IsValid() ? (UInt8*)basePtr + channel.CalcOffset(sourceData.streams) : NULL;
+ }
+}
+
+
+void FillIndexBufferForQuads (UInt16* dst, int dstSize, const UInt16* src, int quadCount)
+{
+ int canFitQuads = dstSize / kVBOIndexSize / 6;
+ quadCount = std::min(quadCount, canFitQuads);
+
+ for (int i = 0; i < quadCount; ++i)
+ {
+ *dst++ = src[0];
+ *dst++ = src[1];
+ *dst++ = src[2];
+ *dst++ = src[0];
+ *dst++ = src[2];
+ *dst++ = src[3];
+ src += 4;
+ }
+}
+
+
+
+// --------------------------------------------------------------------------
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (VBOTests)
+{
+
+ TEST(FillIndexBufferForQuadsDoesNotOverwritePastEnd)
+ {
+ UInt16 srcBuffer[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
+ UInt16 dstBuffer[] = { 17, 13, 17, 13, 17, 13, 1337 };
+ FillIndexBufferForQuads (dstBuffer, sizeof(dstBuffer)-2, srcBuffer, 2);
+ CHECK_EQUAL (1337, dstBuffer[6]);
+ }
+
+} // SUITE
+
+#endif // ENABLE_UNIT_TESTS
diff --git a/Runtime/Shaders/VBO.h b/Runtime/Shaders/VBO.h
new file mode 100644
index 0000000..d8931ab
--- /dev/null
+++ b/Runtime/Shaders/VBO.h
@@ -0,0 +1,308 @@
+#ifndef VBO_H
+#define VBO_H
+
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Filters/Mesh/VertexData.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Modules/ExportModules.h"
+
+class ChannelAssigns;
+class Matrix4x4f;
+
+
+struct MeshPartitionInfo
+{
+ UInt32 submeshStart;
+ UInt32 partitionCount;
+ MeshPartitionInfo() {submeshStart=partitionCount=0;}
+
+ DECLARE_SERIALIZE_NO_PPTR (MeshPartitionInfo);
+};
+
+struct MeshPartition
+{
+ int vertexCount;
+ int vertexOffset;
+ int indexCount;
+ int indexByteOffset;
+ MeshPartition() { vertexCount = vertexOffset = indexCount = indexByteOffset = 0; }
+
+ DECLARE_SERIALIZE_NO_PPTR (MeshPartition);
+};
+
+struct VertexBufferData
+{
+ ChannelInfoArray channels;
+ StreamInfoArray streams;
+ UInt8* buffer;
+ int bufferSize;
+ int vertexCount;
+
+ VertexBufferData()
+ {
+ // Channels and streams have default constructors
+ buffer = 0;
+ bufferSize = 0;
+ vertexCount = 0;
+#if UNITY_PS3
+ numBones = 0;
+ numInfluences = 0;
+ inflPerVertex = 0;
+ bones = NULL;
+ influences = NULL;
+#endif
+ }
+
+#if UNITY_PS3
+ UInt32 numBones;
+ UInt32 numInfluences;
+ UInt32 inflPerVertex;
+ Matrix4x4f* bones;
+ void* influences;
+
+ UNITY_VECTOR(kMemVertexData, MeshPartitionInfo) partInfo;
+ UNITY_VECTOR(kMemVertexData, MeshPartition) partitions;
+#endif
+};
+
+struct IndexBufferData
+{
+ void* indices;
+ int count;
+ UInt32 hasTopologies; // bitmask
+};
+
+struct VertexStreamData
+{
+ UInt8* buffer;
+ UInt32 channelMask;
+ int stride;
+ int vertexCount;
+};
+
+size_t GetChannelFormatSize (UInt8 format);
+
+class EXPORT_COREMODULE VBO : public ListElement
+{
+public:
+ virtual ~VBO() { }
+
+ virtual void UpdateVertexData( const VertexBufferData& buffer ) = 0;
+ virtual void UpdateIndexData (const IndexBufferData& buffer) = 0;
+ virtual void DrawVBO (const ChannelAssigns& channels, UInt32 firstIndexByte, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 firstVertex, UInt32 vertexCount ) = 0;
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ virtual void DrawCustomIndexed( const ChannelAssigns& channels, void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount ) = 0;
+ #endif
+
+ // recreate hardware buffers
+ virtual void Recreate() { }
+
+ enum { kMaxQuads = 65536/4 - 4 }; // so we fit into 16 bit indices, minus some more just in case
+
+ // For writing directly to VBO. VBO must be filled (UpdateData)
+ // at least once; and vertex layout + topology from the last fill
+ // is used. For example, for skinned meshes you have to call
+ // UpdateData on start and each time layout/topology changes;
+ // then map,write,unmap for each skinning.
+ //
+ // In some situations a vertex buffer might become lost; then you need to do UpdateData
+ // again before using Map.
+ virtual bool MapVertexStream( VertexStreamData& outData, unsigned stream ) = 0;
+ virtual void UnmapVertexStream( unsigned stream ) = 0;
+ virtual bool IsVertexStreamMapped( unsigned stream ) const { return m_IsStreamMapped[stream]; }
+
+ virtual bool IsVertexBufferLost() const = 0;
+ virtual bool IsIndexBufferLost() const { return false; }
+
+ virtual bool IsUsingSourceVertices() const { return false; }
+ virtual bool IsUsingSourceIndices() const { return false; }
+
+ // WARNING: no checks will be done. So use wuth caution.
+ // Actually you should only use it coupled with Mesh unloading its VertexData
+ virtual void UnloadSourceVertices() {}
+
+ // Tell vertices will be mapped from render thread
+ virtual void SetMappedFromRenderThread( bool ) {}
+
+ // Whats the access pattern for modifying vertices?
+ enum StreamMode
+ {
+ kStreamModeNoAccess,
+ kStreamModeWritePersist,
+ kStreamModeDynamic,
+ kStreamModeCount
+ };
+
+ virtual void SetVertexStreamMode( unsigned stream, StreamMode mode ) { m_StreamModes[stream] = mode; }
+ StreamMode GetVertexStreamMode( unsigned stream ) const { return StreamMode(m_StreamModes[stream]); }
+ int GetStreamStride( unsigned stream = 0) const { return m_Streams[stream].stride; }
+
+ // TODO: probably unify with vertex streams mode, or extract ibo data altogether in different class
+ virtual void SetIndicesDynamic(bool dynamic) { m_IndicesDynamic = dynamic; }
+ bool AreIndicesDynamic() const { return m_IndicesDynamic; }
+
+ static int GetDefaultChannelByteSize (int channelNum);
+ static int GetDefaultChannelFormat(int channelNum);
+ static int GetDefaultChannelDimension(int channelNum);
+
+ virtual int GetRuntimeMemorySize() const = 0;
+
+ bool GetHideFromRuntimeStats() const { return m_HideFromRuntimeStats; }
+ void SetHideFromRuntimeStats( bool flag ) { m_HideFromRuntimeStats = flag; }
+
+#if GFX_SUPPORTS_D3D9
+ virtual void ResetDynamicVB() {}
+#endif
+
+ // TODO: that is actually how it would/should work in the feature
+ // or at least what i understood speaking with kaspar
+ // for now lets limit to gles2 where it is really needed
+#if GFX_SUPPORTS_OPENGLES20
+ virtual void MarkBuffersLost() {};
+#endif
+
+ virtual void UseAsStreamOutput() { }
+
+#if UNITY_XENON
+ virtual void AddExtraUvChannels( const UInt8* data, UInt32 size, int extraUvCount ) = 0;
+ virtual void CopyExtraUvChannels( VBO* source ) = 0;
+#endif
+
+protected:
+ VBO()
+ {
+ for (int i=0; i<kMaxVertexStreams; i++)
+ {
+ m_Streams[i].Reset();
+ m_StreamModes[i] = kStreamModeNoAccess;
+ m_IsStreamMapped[i] = false;
+ }
+ m_HideFromRuntimeStats = false;
+ m_IndicesDynamic = false;
+ }
+
+ bool IsAnyStreamMapped() const;
+ bool HasStreamWithMode(StreamMode mode) const;
+
+ StreamInfoArray m_Streams;
+ UInt8 m_StreamModes[kMaxVertexStreams];
+ bool m_IsStreamMapped[kMaxVertexStreams];
+ bool m_HideFromRuntimeStats;
+ bool m_IndicesDynamic;
+};
+
+
+// Usage:
+// 1) GetChunk
+// if this returns false, don't use data pointers, bail out
+// 2) fill with data
+// 3) ReleaseChunk
+// 4) DrawChunk (possibly multiple times)
+//
+// The key is that drawing must happen immediately after filling the chunk, because the next
+// GetChunk might destroy the previous chunk's data. So never count on chunks being persistent.
+class DynamicVBO {
+public:
+ virtual ~DynamicVBO() { }
+
+ enum RenderMode {
+ kDrawIndexedTriangles, // arbitrary triangle list
+ kDrawTriangleStrip, // no index buffer, one strip
+ kDrawQuads, // no index buffer, four vertices per quad
+ kDrawIndexedTriangleStrip, // arbitrary triangle strip
+ kDrawIndexedLines, // arbitraty line list
+ kDrawIndexedPoints, // arbitraty point lits
+ kDrawIndexedQuads, // arbitraty quad lits
+#if UNITY_FLASH
+ // ONLY IMPLEMENTED there for better immediate performance
+ kDrawTriangles, // no index buffer triangle list
+#endif
+ };
+
+ // Gets a chunk of vertex/index buffer to write into.
+ //
+ // maxVertices/maxIndices is the capacity of the returned chunk; you have to pass actually used
+ // amounts in ReleaseChunk afterwards.
+ //
+ // maxIndices and outIB are only used for kDrawIndexedTriangles render mode.
+ // For other ones they must be 0/NULL.
+ //
+ // Returns false if can't obtain a chunk for whatever reason.
+ virtual bool GetChunk( UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode renderMode, void** outVB, void** outIB ) = 0;
+
+ virtual void ReleaseChunk( UInt32 actualVertices, UInt32 actualIndices ) = 0;
+
+ virtual void DrawChunk (const ChannelAssigns& channels) = 0;
+
+ // dynamic vbo might be backed by real vbo that needs to be recreated
+ virtual void Recreate() {}
+
+ static bool IsIndexed(RenderMode mode) { return mode == kDrawIndexedTriangles || mode == kDrawIndexedTriangleStrip; }
+
+protected:
+ DynamicVBO();
+
+protected:
+ UInt32 m_LastChunkShaderChannelMask;
+ UInt32 m_LastChunkStride, m_LastChunkVertices, m_LastChunkIndices;
+ RenderMode m_LastRenderMode;
+ bool m_LendedChunk;
+};
+
+
+void CopyVertexStream( const VertexBufferData& sourceData, void* buffer, unsigned stream );
+void CopyVertexBuffer( const VertexBufferData& sourceData, void* buffer );
+
+void FillIndexBufferForQuads (UInt16* dst, int dstSize, const UInt16* src, int quadCount);
+
+void GetVertexStreamOffsets( const VertexBufferData& sourceData, size_t dest[kShaderChannelCount], size_t baseOffset, unsigned stream );
+void GetVertexStreamPointers( const VertexBufferData& sourceData, void* dest[kShaderChannelCount], void* basePtr, unsigned stream );
+
+static inline int GetVertexChannelSize( const VertexBufferData& buffer, int i )
+{
+ return VBO::GetDefaultChannelByteSize(i) * buffer.vertexCount;
+}
+
+int CalculateOffset( UInt32 channelMask );
+
+static inline int CalculateVertexStreamSize (const StreamInfo& stream, int vertexCount)
+{
+ return stream.stride * vertexCount;
+}
+
+static inline int CalculateVertexStreamSize (const VertexBufferData& buffer, unsigned stream)
+{
+ Assert(stream < kMaxVertexStreams);
+ return CalculateVertexStreamSize(buffer.streams[stream], buffer.vertexCount);
+}
+
+const int kVBOIndexSize = sizeof(UInt16);
+inline int CalculateIndexBufferSize (const IndexBufferData& buffer)
+{
+ int size = 0;
+ if (buffer.indices)
+ size += buffer.count * kVBOIndexSize;
+ return size;
+}
+
+static inline int GetPrimitiveCount (int indexCount, GfxPrimitiveType topology, bool nativeQuads)
+{
+ switch (topology) {
+ case kPrimitiveTriangles: return indexCount / 3;
+ case kPrimitiveTriangleStripDeprecated: return indexCount - 2;
+ case kPrimitiveQuads: return nativeQuads ? indexCount/4 : indexCount/4*2;
+ case kPrimitiveLines: return indexCount / 2;
+ case kPrimitiveLineStrip: return indexCount - 1;
+ case kPrimitivePoints: return indexCount;
+ default: Assert ("unknown primitive type"); return 0;
+ };
+}
+
+// Return vertex size in bytes, with components present as specified by the argument
+size_t GetVertexSize (unsigned shaderChannelsMask);
+
+#endif
diff --git a/Runtime/Terrain/DetailDatabase.cpp b/Runtime/Terrain/DetailDatabase.cpp
new file mode 100644
index 0000000..bc11b30
--- /dev/null
+++ b/Runtime/Terrain/DetailDatabase.cpp
@@ -0,0 +1,1106 @@
+#include "UnityPrefix.h"
+#include "DetailDatabase.h"
+
+#if ENABLE_TERRAIN
+
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Filters/Mesh/LodMeshFilter.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Geometry/TextureAtlas.h"
+#include "PerlinNoise.h"
+#include "TerrainData.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Graphics/LightmapSettings.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+
+const int kClampedVertexCount = 50000;
+enum { kResolutionPerPatch = 8 };
+
+static void UpdateAtlasTextureColorSpace(Texture2D* atlasTexture, Texture2D** sourceTextures, int sourceTextureCount)
+{
+ for (int i = 0; i < sourceTextureCount; ++i)
+ {
+ if (sourceTextures[i] != NULL && sourceTextures[i]->GetStoredColorSpace() != kTexColorSpaceLinear)
+ {
+ atlasTexture->SetStoredColorSpaceNoDirtyNoApply (kTexColorSpaceSRGB);
+ return;
+ }
+ }
+ atlasTexture->SetStoredColorSpaceNoDirtyNoApply(kTexColorSpaceLinear);
+}
+
+// 8x8 dither table from http://en.wikipedia.org/wiki/Ordered_dithering
+const float kDitherTable[] = {
+ 1, 49, 13, 61, 4, 52, 16, 64,
+ 33, 17, 45, 29, 36, 20, 48, 32,
+ 9, 57, 5, 53, 12, 60, 8, 56,
+ 41, 25, 37, 21, 44, 28, 40, 24,
+ 3, 51, 15, 63, 2, 50, 14, 62,
+ 35, 19, 47, 31, 34, 18, 46, 30,
+ 11, 59, 7, 55, 10, 58, 6, 54,
+ 43, 27, 39, 23, 42, 26, 38, 22,
+};
+
+DetailDatabase::DetailDatabase (TerrainData* terrainData, TreeDatabase* database)
+{
+ m_TerrainData = terrainData;
+ m_TreeDatabase = database;
+ m_WavingGrassTint = ColorRGBAf (0.7f, 0.6f, 0.5f, 0.0f);
+ m_WavingGrassStrength = .5F;
+ m_WavingGrassAmount = .5F;
+ m_WavingGrassSpeed = .5F;
+ m_PatchCount = 0;
+ m_PatchSamples = kResolutionPerPatch;
+ m_IsPrototypesDirty = true;
+ m_AtlasTexture = NULL;
+}
+
+bool DetailDatabase::IsPatchEmpty (int x, int y) const
+{
+ return GetPatch(x,y).numberOfObjects.empty ();
+}
+bool DetailDatabase::IsPatchDirty (int x, int y) const
+{
+ return GetPatch(x,y).dirty;
+}
+
+void DetailDatabase::SetDetailResolution (int resolution, int resolutionPerPatch)
+{
+ m_PatchCount = clamp<int>(resolution / resolutionPerPatch, 0, 10000);
+ m_PatchSamples = clamp<int>(resolutionPerPatch, 8, 1000);
+
+ m_Patches.clear();
+ m_Patches.resize (m_PatchCount * m_PatchCount);
+
+ SetDirty();
+}
+
+void DetailDatabase::SetDetailPrototypesDirty ()
+{
+ m_IsPrototypesDirty = true;
+}
+
+void DetailDatabase::ResetDirtyDetails ()
+{
+ for (int i=0;i<m_Patches.size();i++)
+ m_Patches[i].dirty = false;
+}
+
+int DetailDatabase::AddLayerIndex (int detailIndex, DetailPatch &patch)
+{
+ for (int i=0;i<patch.layerIndices.size();i++)
+ {
+ if (patch.layerIndices[i] == detailIndex)
+ return i;
+ }
+ patch.layerIndices.push_back (detailIndex);
+ patch.numberOfObjects.resize (patch.numberOfObjects.size() + m_PatchSamples * m_PatchSamples);
+ return patch.layerIndices.size() - 1;
+}
+
+void DetailDatabase::RemoveLocalLayerIndex (int detailIndex, DetailPatch& patch)
+{
+ Assert(detailIndex >= 0 || detailIndex < patch.layerIndices.size());
+
+ // Remove detail index out of numberofObjectsArray
+ int begin = detailIndex * m_PatchSamples * m_PatchSamples;
+ patch.numberOfObjects.erase (patch.numberOfObjects.begin() + begin, patch.numberOfObjects.begin() + begin + m_PatchSamples * m_PatchSamples);
+
+ patch.layerIndices.erase(patch.layerIndices.begin() + detailIndex);
+}
+
+int DetailDatabase::GetSupportedLayers (int xBase, int yBase, int totalWidth, int totalHeight, int *buffer) const
+{
+ if( m_PatchCount <= 0 )
+ {
+ ErrorString ("Terrain has zero detail resolution");
+ return 0;
+ }
+
+ int* enabledLayers;
+ int prototypeCount = m_DetailPrototypes.size ();
+ ALLOC_TEMP(enabledLayers, int, prototypeCount);
+ memset(enabledLayers,0,sizeof (int) * prototypeCount);
+
+ int minPatchX = clamp(xBase / m_PatchSamples, 0, m_PatchCount - 1);
+ int minPatchY = clamp(yBase / m_PatchSamples, 0, m_PatchCount - 1);
+ int maxPatchX = clamp((xBase+totalWidth) / m_PatchSamples, 0, m_PatchCount - 1);
+ int maxPatchY = clamp((yBase+totalHeight) / m_PatchSamples, 0, m_PatchCount - 1);
+
+ for (int patchY=minPatchY;patchY<=maxPatchY;patchY++)
+ {
+ for (int patchX=minPatchX;patchX<=maxPatchX;patchX++)
+ {
+ int minX = clamp (xBase - patchX * m_PatchSamples, 0, m_PatchSamples - 1);
+ int minY = clamp (yBase - patchY * m_PatchSamples, 0, m_PatchSamples - 1);
+
+ int maxX = clamp (xBase + totalWidth - patchX * m_PatchSamples, 0, m_PatchSamples);
+ int maxY = clamp (yBase + totalHeight - patchY * m_PatchSamples, 0, m_PatchSamples);
+
+ int width = maxX - minX;
+ int height = maxY - minY;
+ if (width == 0 || height == 0)
+ continue;
+
+ const DetailPatch& patch = GetPatch(patchX, patchY);
+ for (int l=0;l<patch.layerIndices.size();l++)
+ {
+ int layer = patch.layerIndices[l];
+ enabledLayers[layer] = 1;
+ }
+ }
+ }
+
+ int enabledCount = 0;
+ for (int i=0;i<prototypeCount;i++)
+ {
+ if (enabledLayers[i])
+ {
+ if (buffer)
+ buffer[enabledCount] = i;
+ enabledCount++;
+ }
+ }
+
+ return enabledCount;
+}
+
+
+void DetailDatabase::GetLayer (int xBase, int yBase, int totalWidth, int totalHeight, int detailIndex, int *buffer) const
+{
+ if( m_PatchCount <= 0 )
+ {
+ ErrorString ("Terrain has zero detail resolution");
+ return;
+ }
+
+ int minPatchX = clamp(xBase / m_PatchSamples, 0, m_PatchCount - 1);
+ int minPatchY = clamp(yBase / m_PatchSamples, 0, m_PatchCount - 1);
+ int maxPatchX = clamp((xBase+totalWidth) / m_PatchSamples, 0, m_PatchCount - 1);
+ int maxPatchY = clamp((yBase+totalHeight) / m_PatchSamples, 0, m_PatchCount - 1);
+
+ for (int patchY=minPatchY;patchY<=maxPatchY;patchY++)
+ {
+ for (int patchX=minPatchX;patchX<=maxPatchX;patchX++)
+ {
+ int minX = clamp(xBase - patchX * m_PatchSamples, 0, m_PatchSamples - 1);
+ int minY = clamp(yBase - patchY * m_PatchSamples, 0, m_PatchSamples - 1);
+
+ int maxX = clamp(xBase + totalWidth - patchX * m_PatchSamples, 0, m_PatchSamples);
+ int maxY = clamp(yBase + totalHeight - patchY * m_PatchSamples, 0, m_PatchSamples);
+
+ int width = maxX - minX;
+ int height = maxY - minY;
+ if (width == 0 || height == 0)
+ continue;
+
+ int xOffset = minX + patchX * m_PatchSamples - xBase;
+ int yOffset = minY + patchY * m_PatchSamples - yBase;
+
+ const DetailPatch &patch = GetPatch(patchX, patchY);
+
+ const UInt8 *numberOfObjects = &patch.numberOfObjects[0];
+ for (int l=0;l<patch.layerIndices.size();l++)
+ {
+ int layer = patch.layerIndices[l];
+ if (layer != detailIndex)
+ continue;
+
+ for (int y=0;y<height;y++)
+ {
+ for (int x=0;x<width;x++)
+ {
+ int nbOfObjects = numberOfObjects[GetIndex(minX + x, minY + y, l)];
+ buffer[x + xOffset + (y + yOffset) * totalWidth] = nbOfObjects;
+ }
+ }
+ }
+ }
+ }
+}
+
+void DetailDatabase::SetLayer (int xBase, int yBase, int totalWidth, int totalHeight, int detailIndex, const int *buffer)
+{
+ if (detailIndex >= m_DetailPrototypes.size())
+ {
+ ErrorString ("Detail index out of bounds in DetailDatabase.SetLayers");
+ return;
+ }
+ if (m_PatchCount <= 0)
+ {
+ ErrorString ("Terrain has zero detail resolution");
+ return;
+ }
+ int minPatchX = clamp(xBase / m_PatchSamples, 0, m_PatchCount - 1);
+ int minPatchY = clamp(yBase / m_PatchSamples, 0, m_PatchCount - 1);
+ int maxPatchX = clamp((xBase+totalWidth) / m_PatchSamples, 0, m_PatchCount - 1);
+ int maxPatchY = clamp((yBase+totalHeight) / m_PatchSamples, 0, m_PatchCount - 1);
+
+ for (int patchY=minPatchY;patchY<=maxPatchY;patchY++)
+ {
+ for (int patchX=minPatchX;patchX<=maxPatchX;patchX++)
+ {
+ int minX = clamp(xBase - patchX * m_PatchSamples, 0, m_PatchSamples - 1);
+ int minY = clamp(yBase - patchY * m_PatchSamples, 0, m_PatchSamples - 1);
+
+ int maxX = clamp(xBase + totalWidth - patchX * m_PatchSamples, 0, m_PatchSamples);
+ int maxY = clamp(yBase + totalHeight - patchY * m_PatchSamples, 0, m_PatchSamples);
+
+ int width = maxX - minX;
+ int height = maxY - minY;
+ if (width == 0 || height == 0)
+ continue;
+
+ int xOffset = minX + patchX * m_PatchSamples - xBase;
+ int yOffset = minY + patchY * m_PatchSamples - yBase;
+
+ DetailPatch& patch = GetPatch(patchX, patchY);
+
+ int localLayerIndex = AddLayerIndex(detailIndex, patch);
+ UInt8* numberOfObjects = &patch.numberOfObjects[0];
+
+ for (int y=0;y<height;y++)
+ {
+ for (int x=0;x<width;x++)
+ {
+ // TODO: Is this the right order?
+ int nb = clamp(buffer[x + xOffset + (y + yOffset) * totalWidth], 0, 255);
+ int nbIndex = GetIndex(minX + x, minY + y, localLayerIndex);
+ if (nb != numberOfObjects[nbIndex])
+ {
+ numberOfObjects[nbIndex] = nb;
+ patch.dirty = true;
+ }
+ }
+ }
+
+ // Detect if this patch has zero details on this layer
+ // In that case delete the layer completely to save space
+ unsigned hasSomething = 0;
+ int oneLayerSampleCount = m_PatchSamples * m_PatchSamples;
+ for (int i=0;i<oneLayerSampleCount;i++)
+ hasSomething += numberOfObjects[localLayerIndex * oneLayerSampleCount + i];
+
+ if (hasSomething == 0)
+ RemoveLocalLayerIndex(localLayerIndex, patch);
+ }
+ }
+ SetDirty ();
+
+ // All detail renderers will reload details that have patch.dirty set
+ // Then reset the patch.dirty = false on all patches.
+ m_TerrainData->UpdateUsers (TerrainData::kRemoveDirtyDetailsImmediately);
+ ResetDirtyDetails();
+}
+
+void DetailDatabase::CleanupPrototype (DetailPrototype &proto, string const& error)
+{
+ proto.vertices.clear();
+ proto.uvs.clear();
+ proto.colors.clear();
+ proto.triangles.clear();
+}
+
+
+DetailDatabase::~DetailDatabase ()
+{
+ DestroySingleObject(m_AtlasTexture);
+}
+
+namespace DetailDatabase_Static
+{
+static SHADERPROP(MainTex);
+} // namespace DetailDatabase_Static
+
+#if UNITY_EDITOR
+// For thread loading we need to know the textures. Going through the meshes then textures is not thread safe.
+void DetailDatabase::SetupPreloadTextureAtlasData ()
+{
+ m_PreloadTextureAtlasData.resize(m_DetailPrototypes.size());
+
+ Texture2D** sourceTextures;
+ ALLOC_TEMP(sourceTextures, Texture2D*, m_DetailPrototypes.size());
+
+ RefreshPrototypesStep1(sourceTextures);
+
+ for (int i=0;i<m_DetailPrototypes.size();i++)
+ {
+ m_PreloadTextureAtlasData[i] = sourceTextures[i];
+ if (sourceTextures[i] == NULL)
+ {
+ WarningString("Missing detail texture in Terrain, degraded loading performance");
+ m_PreloadTextureAtlasData.clear();
+ break;
+ }
+ }
+
+ SetDetailPrototypesDirty();
+}
+
+#endif
+
+void DetailDatabase::GenerateTextureAtlasThreaded ()
+{
+ if (!m_PreloadTextureAtlasData.empty())
+ {
+ AssertIf(m_PreloadTextureAtlasData.size() != m_DetailPrototypes.size());
+
+ Texture2D** sourceTextures;
+ ALLOC_TEMP(sourceTextures, Texture2D*, m_PreloadTextureAtlasData.size());
+
+ int i;
+ for (i=0;i<m_PreloadTextureAtlasData.size();i++)
+ {
+ Texture2D* tex = dynamic_pptr_cast<Texture2D*> (InstanceIDToObjectThreadSafe(m_PreloadTextureAtlasData[i].GetInstanceID()));
+ if (tex == NULL)
+ break;
+ sourceTextures[i] = tex;
+ }
+
+ if (i == m_PreloadTextureAtlasData.size())
+ {
+ AssertIf (m_AtlasTexture != NULL);
+
+ m_AtlasTexture = NEW_OBJECT_FULL(Texture2D, kCreateObjectFromNonMainThread);
+ m_AtlasTexture->Reset();
+ m_AtlasTexture->AwakeFromLoadThreaded();
+
+ // ok, just from performance standpoint we don't want to upload texture here
+ // or get an assert from uninited texture, so, let's cheat
+ m_AtlasTexture->HackSetAwakeDidLoadThreadedWasCalled();
+
+ m_PreloadTextureAtlasUVLayout.resize(m_PreloadTextureAtlasData.size());
+
+ UpdateAtlasTextureColorSpace(m_AtlasTexture, sourceTextures, m_PreloadTextureAtlasData.size());
+ PackTextureAtlasSimple (m_AtlasTexture, 2048, m_PreloadTextureAtlasData.size(), sourceTextures, &m_PreloadTextureAtlasUVLayout[0], 0, false, false);
+ }
+ }
+}
+
+void DetailDatabase::RefreshPrototypesStep1 (Texture2D** sourceTextures)
+{
+ using namespace DetailDatabase_Static;
+
+ for (int i=0;i<m_DetailPrototypes.size();i++)
+ {
+ DetailPrototype& proto = m_DetailPrototypes[i];
+ sourceTextures[i] = NULL;
+
+ GameObject *prototype = proto.prototype;
+ if (proto.usePrototypeMesh && prototype)
+ {
+ Renderer* renderer = prototype->QueryComponent (Renderer);
+ if (renderer == NULL)
+ {
+ CleanupPrototype(proto, Append("Missing renderer ", prototype->GetName()));
+ continue;
+ }
+
+ if (renderer->GetMaterialCount() != 1)
+ {
+ CleanupPrototype(proto, Append(proto.prototype->GetName(), " must have exactly one material."));
+ continue;
+ }
+
+ Material *sharedMaterial = renderer->GetMaterial (0);
+ if (sharedMaterial == NULL)
+ {
+ CleanupPrototype(proto, Append("Missing material ", proto.prototype->GetName()));
+ continue;
+ }
+
+ MeshFilter *filter = prototype->QueryComponent(MeshFilter);
+ if (filter == NULL)
+ {
+ CleanupPrototype(proto, Append("Missing mesh filter ", proto.prototype->GetName()));
+ continue;
+ }
+
+ Mesh* mesh = filter->GetSharedMesh();
+ if (mesh == NULL)
+ {
+ CleanupPrototype(proto, Append ("Missing mesh ", proto.prototype->GetName()));
+ continue;
+ }
+
+ proto.vertices.assign (mesh->GetVertexBegin(), mesh->GetVertexEnd());
+ if (proto.vertices.empty())
+ {
+ CleanupPrototype(proto, Append ("No vertices available ", prototype->GetName()));
+ continue;
+ }
+
+ // Colors and normals are not optional here. Default to something
+ if (mesh->IsAvailable (kShaderChannelColor))
+ {
+ proto.colors.assign (mesh->GetColorBegin (), mesh->GetColorEnd () );
+ }
+ else
+ {
+ proto.colors.clear ();
+ proto.colors.resize (mesh->GetVertexCount(), ColorRGBA32(0xFFFFFFFF));
+ }
+
+ if (mesh->IsAvailable (kShaderChannelNormal))
+ {
+ proto.normals.assign(mesh->GetNormalBegin (), mesh->GetNormalEnd ());
+ }
+ else
+ {
+ proto.normals.clear ();
+ proto.normals.resize (mesh->GetVertexCount(), Vector3f(0,1,0));
+ }
+
+ if (mesh->IsAvailable (kShaderChannelTexCoord0))
+ {
+ proto.uvs.assign (mesh->GetUvBegin(0), mesh->GetUvEnd(0));
+ }
+ else
+ {
+ CleanupPrototype(proto, Append("No uvs available ", proto.prototype->GetName()));
+ continue;
+ }
+
+ Mesh::TemporaryIndexContainer tempBuffer;
+ mesh->GetTriangles (tempBuffer);
+ proto.triangles.assign (tempBuffer.begin(), tempBuffer.end());
+
+ if (proto.triangles.empty())
+ {
+ CleanupPrototype(proto, Append("No triangles available ", proto.prototype->GetName()));
+ continue;
+ }
+
+ if (sharedMaterial)
+ sourceTextures[i] = dynamic_pptr_cast <Texture2D*>(sharedMaterial->GetTexture (kSLPropMainTex));
+ }
+ // We don't have a mesh, but we have a texture: it's grass quads
+ else if( !proto.usePrototypeMesh && proto.prototypeTexture.IsValid() )
+ {
+ float halfWidth = 0.5F;
+ float height = 1.0F;
+ // color modifier at the top of the grass.
+ // billboard top vertex color = topColor * perlinNoise * 2
+ // Was 1.5f before we doublemultiplied
+ ColorRGBA32 topColor = GfxDevice::ConvertToDeviceVertexColor( ColorRGBA32 (255, 255, 255, 255) );
+ ColorRGBA32 bottomColor = GfxDevice::ConvertToDeviceVertexColor( ColorRGBA32 (160,160,160, 0) );
+
+ Vector3f vertices[] = {
+ Vector3f (-halfWidth, 0, 0),
+ Vector3f (-halfWidth, height, 0),
+ Vector3f (halfWidth, height, 0),
+ Vector3f (halfWidth, 0, 0),
+ };
+
+ ColorRGBA32 colors[] = {
+ bottomColor, topColor, topColor, bottomColor,
+ };
+ Vector2f uvs[] = {
+ Vector2f (0, 0), Vector2f (0, 1), Vector2f (1, 1), Vector2f (1, 0),
+ };
+ UInt16 triangles[] = {
+ 0, 1, 2, 2, 3, 0,
+ };
+
+ const int actualVertexCount = 4;
+ const int actualIndexCount = 6;
+
+ // skip normals creation, since they will be taken from the terrain in GenerateMesh()
+
+ proto.vertices.assign (vertices, vertices + actualVertexCount);
+ proto.colors.assign (colors, colors + actualVertexCount);
+ proto.uvs.assign (uvs, uvs + actualVertexCount);
+ proto.triangles.assign (triangles, triangles + actualIndexCount);
+ sourceTextures[i] = proto.prototypeTexture;
+ }
+ else
+ {
+ if (proto.prototype)
+ CleanupPrototype(proto, Append("Missing prototype ", proto.prototype->GetName()));
+ else
+ CleanupPrototype(proto, "Missing prototype");
+ continue;
+ }
+ }
+}
+
+
+void DetailDatabase::RefreshPrototypes ()
+{
+ Texture2D** sourceTextures;
+ ALLOC_TEMP(sourceTextures, Texture2D*, m_DetailPrototypes.size());
+
+ RefreshPrototypesStep1(sourceTextures);
+
+ // Normal non-threaded creation mode
+ if (m_AtlasTexture == NULL || m_AtlasTexture->IsInstanceIDCreated())
+ {
+ // Not created yet
+ if (m_AtlasTexture == NULL)
+ {
+ m_AtlasTexture = CreateObjectFromCode<Texture2D>();
+ m_AtlasTexture->SetHideFlags (Object::kHideAndDontSave);
+ m_AtlasTexture->InitTexture(2, 2, kTexFormatARGB32, Texture2D::kMipmapMask, 1);
+ m_AtlasTexture->SetWrapMode(kTexWrapClamp);
+ }
+
+ // TODO: Make 4096 a property & clamp to GFX card, detail settings
+ Rectf* rects;
+ ALLOC_TEMP(rects, Rectf, m_DetailPrototypes.size());
+
+ UpdateAtlasTextureColorSpace(m_AtlasTexture, sourceTextures, m_DetailPrototypes.size());
+ PackTextureAtlasSimple (m_AtlasTexture, 2048, m_DetailPrototypes.size(), sourceTextures, rects, 0, true, false);
+
+ for (int i=0;i<m_DetailPrototypes.size();i++)
+ {
+ DetailPrototype &proto = m_DetailPrototypes[i];
+ Rectf r = rects[i];
+ float w = r.Width();
+ float h = r.Height();
+ for (int v=0;v<proto.uvs.size();v++)
+ {
+ proto.uvs[v].x = proto.uvs[v].x * w + r.x;
+ proto.uvs[v].y = proto.uvs[v].y * h + r.y;
+ }
+ }
+ }
+ // Generated in loading thread - Just upload
+ else
+ {
+ Object::AllocateAndAssignInstanceID(m_AtlasTexture);
+ m_AtlasTexture->SetHideFlags (Object::kHideAndDontSave);
+ m_AtlasTexture->SetWrapMode( kTexWrapClamp );
+
+ AssertIf (m_PreloadTextureAtlasUVLayout.size() != m_DetailPrototypes.size());
+ for (int i=0;i<m_DetailPrototypes.size();i++)
+ {
+ DetailPrototype &proto = m_DetailPrototypes[i];
+ Rectf r = m_PreloadTextureAtlasUVLayout[i];
+ float w = r.Width();
+ float h = r.Height();
+ for (int v=0;v<proto.uvs.size();v++)
+ {
+ proto.uvs[v].x = proto.uvs[v].x * w + r.x;
+ proto.uvs[v].y = proto.uvs[v].y * h + r.y;
+ }
+ }
+
+ m_AtlasTexture->AwakeFromLoad(kDefaultAwakeFromLoad);
+ }
+
+ m_IsPrototypesDirty = false;
+}
+
+
+void DetailDatabase::SetDirty ()
+{
+ m_TerrainData->SetDirty ();
+}
+
+void DetailDatabase::SetDetailPrototypes (const vector<DetailPrototype> & detailPrototypes)
+{
+ m_DetailPrototypes = detailPrototypes;
+ RefreshPrototypes ();
+ SetDirty ();
+ m_TerrainData->UpdateUsers (TerrainData::kFlushEverythingImmediately);
+}
+
+void DetailDatabase::RemoveDetailPrototype (int index)
+{
+ #if UNITY_EDITOR
+
+ if( index < 0 || index >= m_DetailPrototypes.size() )
+ {
+ ErrorString("invalid detail prototype index");
+ return;
+ }
+
+ // erase detail prototype
+ m_DetailPrototypes.erase( m_DetailPrototypes.begin() + index );
+
+ // update detail patches
+ for( size_t i = 0; i < m_Patches.size(); ++i )
+ {
+ DetailPatch& patch = m_Patches[i];
+ int localIndex = -1;
+ for( size_t j = 0; j < patch.layerIndices.size(); ++j )
+ {
+ if( patch.layerIndices[j] == index )
+ localIndex = j;
+ else if( patch.layerIndices[j] > index )
+ --patch.layerIndices[j];
+ }
+ if( localIndex == -1 )
+ continue;
+
+ AssertIf( patch.numberOfObjects.size() != patch.layerIndices.size() * m_PatchSamples * m_PatchSamples );
+
+ patch.layerIndices.erase( patch.layerIndices.begin() + localIndex );
+ patch.numberOfObjects.erase(
+ patch.numberOfObjects.begin() + localIndex * m_PatchSamples * m_PatchSamples,
+ patch.numberOfObjects.begin() + (localIndex+1) * m_PatchSamples * m_PatchSamples );
+ }
+
+ RefreshPrototypes ();
+ SetDirty();
+ m_TerrainData->UpdateUsers (TerrainData::kFlushEverythingImmediately);
+
+ #else
+ ErrorString("only implemented in editor");
+ #endif
+}
+
+static void CopyVertex (Vector3f *src, Vector3f *dst, const Matrix4x4f &transform, int offset, int count)
+{
+ for (int i=0;i<count;i++)
+ dst[i+offset] = transform.MultiplyPoint3(src[i]);
+}
+
+static void CopyVertex (Vector3f pos, Vector3f *dst, int offset, int count)
+{
+ for (int i=0;i<count;i++)
+ dst[i+offset] = pos;
+}
+
+static void CopyNormal (Vector3f* src, Vector3f* dst, Quaternionf rot, int offset, int count)
+{
+ for (int i=0;i<count;i++)
+ dst[i+offset] = RotateVectorByQuat (rot, src[i]);
+}
+
+static void CopyNormalFromTerrain (const Heightmap& heightmap, float normalizedX, float normalizedZ, Vector3f* dst, int offset, int count)
+{
+ Vector3f terrainNormal = heightmap.GetInterpolatedNormal(normalizedX, normalizedZ);
+
+ for (int i = 0; i < count; i++)
+ dst[i+offset] = terrainNormal;
+}
+
+static void CopyUV (Vector2f* src, Vector2f *dst, int offset, int count)
+{
+ for (int i=0;i<count;i++)
+ dst[i+offset] = src[i];
+}
+
+static void CopyTangents (Vector2f* src, Vector4f *dst, int offset, int count)
+{
+ for (int i = 0; i < count; i++)
+ {
+ Vector2f srcVector = src[i];
+ Vector4f& dstVector = dst[i+offset];
+
+ dstVector.x = srcVector.x;
+ dstVector.y = srcVector.y;
+ }
+}
+
+static void CopyUVFromTerrain (float normalizedX, float normalizedZ, Vector2f *dst, int offset, int count)
+{
+ Vector2f lightmapUV(normalizedX, normalizedZ);
+ for (int i = 0; i < count; i++)
+ dst[i+offset] = lightmapUV;
+}
+
+inline ColorRGBA32 MultiplyDouble (const ColorRGBA32 &inC0, const ColorRGBA32 &inC1)
+{
+ return ColorRGBA32 (
+ std::min (((int)inC0.r * (int)inC1.r) / 128, 255),
+ std::min (((int)inC0.g * (int)inC1.g) / 128, 255),
+ std::min (((int)inC0.b * (int)inC1.b) / 128, 255),
+ std::min (((int)inC0.a * (int)inC1.a) / 128, 255)
+ );
+}
+
+static void CopyColor (ColorRGBA32* src, ColorRGBA32* dst, ColorRGBA32 scale, int offset, int count)
+{
+ for (int i=0;i<count;i++)
+ dst[i+offset] = src[i] * scale;
+}
+
+/*Rectf DetailDatabase::GetNormalizedArea (int x, int y)
+{
+ float fx = (float)x / m_PatchCount;
+ float fy = (float)y / m_PatchCount;
+ float size = 1.0F / m_PatchCount;
+ return Rectf (fx, fy, fx+size, fy+size);
+}
+*/
+/*
+void DetailDatabase::GenerateBounds (DetailPatch &patch, int patchX, int patchY)
+{
+ if (patch.numberOfObjects.size() != 0)
+ {
+ Mesh mesh = new Mesh ();
+
+ GenerateMesh (mesh, patch, patchX, patchY, m_Heightmap.size, false, DetailRenderMode.Grass);
+ patch.bounds = mesh.bounds;
+// Debug.Log(patch.bounds.min);
+// Debug.Log(patch.bounds.max);
+ DestroyImmediate (mesh);
+ }
+}
+*/
+
+PROFILER_INFORMATION(gBuildDetailMesh, "Terrain.Details.BuildPatchMesh", kProfilerRender)
+PROFILER_INFORMATION(gExtractLightmap, "DetailMesh.ExtractLightmap", kProfilerRender);
+PROFILER_INFORMATION(gSetup, "DetailMesh.Setup", kProfilerRender);
+PROFILER_INFORMATION(gBuildData, "DetailMesh.BuildData", kProfilerRender);
+PROFILER_INFORMATION(gAssignToMesh, "DetailMesh.AssignToMesh", kProfilerRender);
+
+Mesh* DetailDatabase::BuildMesh (int patchX, int patchY, Vector3f size, int lightmapIndex, DetailRenderMode renderMode, float density)
+{
+ int totalTriangleCount, totalVertexCount;
+
+ PROFILER_AUTO(gBuildDetailMesh, NULL)
+
+ DetailPatch &patch = GetPatch (patchX, patchY);
+ ComputeVertexAndTriangleCount(patch, renderMode, density, &totalVertexCount, &totalTriangleCount);
+ if (totalTriangleCount == 0 || totalVertexCount == 0)
+ return NULL;
+ else
+ {
+ Mesh* mesh = NEW_OBJECT(Mesh);
+ mesh->Reset();
+ mesh->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+
+ mesh->SetHideFlags(Object::kHideAndDontSave);
+ GenerateMesh (*mesh, patchX, patchY, size, lightmapIndex, renderMode, density, totalVertexCount, totalTriangleCount);
+ return mesh;
+ }
+}
+
+// Fixes bounds of the patch of billboards. Since each quad making a billboard
+// has its vertices collapsed in one point, the bounding box does not take
+// the height nor the width of the billboard into account.
+inline void ExpandDetailBillboardBounds(Mesh& mesh, float detailMaxHalfWidth, float detailMaxHeight)
+{
+ // The origin of the billboard is in the middle of the bottom edge.
+ AABB aabb = mesh.GetBounds();
+
+ // The billboard always faces the camera, so when looking from the top
+ // it's the height of the billboard that extends in the XZ plane.
+ float maxHalfWidth = std::max(detailMaxHalfWidth, detailMaxHeight);
+
+ aabb.m_Extent += Vector3f(maxHalfWidth, 0.5f * detailMaxHeight, maxHalfWidth);
+ aabb.m_Center += Vector3f(0, 0.5f * detailMaxHeight, 0);
+ mesh.SetBounds(aabb);
+}
+
+void DetailDatabase::GenerateMesh (Mesh& mesh, int patchX, int patchY, Vector3f size, int lightmapIndex, DetailRenderMode renderMode, float density, int totalVertexCount, int totalTriangleCount)
+{
+ PROFILER_BEGIN_INTERNAL(gSetup, NULL);
+ DetailPatch &patch = GetPatch (patchX, patchY);
+ Vector3f* vertices;
+ ALLOC_TEMP(vertices, Vector3f, totalVertexCount);
+
+ Vector2f* uvs;
+ ALLOC_TEMP(uvs, Vector2f, totalVertexCount);
+
+ int uv2Count = totalVertexCount;
+
+ Vector2f* uvs2 = NULL;
+ ALLOC_TEMP(uvs2, Vector2f, uv2Count);
+
+ int tangentCount = 0;
+ if (renderMode == kDetailBillboard)
+ tangentCount = totalVertexCount;
+
+ Vector4f* tangents = NULL;
+ ALLOC_TEMP(tangents, Vector4f, tangentCount);
+
+ ColorRGBA32* colors;
+ ALLOC_TEMP(colors, ColorRGBA32, totalVertexCount);
+
+ int normalCount = totalVertexCount;
+ Vector3f* normals = NULL;
+ ALLOC_TEMP (normals, Vector3f, normalCount);
+
+ UInt16* triangles;
+ ALLOC_TEMP (triangles, UInt16, totalTriangleCount);
+
+ int triangleCount = 0;
+ int vertexCount = 0;
+ float randomResolutionSize = 1.0F / GetResolution();
+ int res = m_PatchSamples;
+
+ Heightmap* heightmap = &m_TerrainData->GetHeightmap();
+/// int samplesPerHeixel = m_SamplesPerHeixel;
+/// int heixels = m_SamplesPerHeixel;
+
+// int xBaseHeightmap = patchX * m_PatchSamples / m_SamplesPerHeixel;
+// int yBaseHeightmap = patchY * m_PatchSamples / m_SamplesPerHeixel;
+
+ PROFILER_END_INTERNAL;
+
+ PROFILER_BEGIN_INTERNAL(gBuildData, NULL);
+
+ float detailMaxHalfWidth = 0.0f;
+ float detailMaxHeight = 0.0f;
+
+ for (int i=0;i<patch.layerIndices.size();i++)
+ {
+ DetailPrototype& prototype = m_DetailPrototypes[patch.layerIndices[i]];
+
+ if (prototype.renderMode != renderMode)
+ continue;
+
+ Vector3f* prototypeVertices = prototype.vertices.size() > 0 ? &prototype.vertices[0] : NULL;
+ Vector3f* prototypeNormals = prototype.normals.size() > 0 ? &prototype.normals[0] : NULL;
+ Vector2f* prototypeUvs = prototype.uvs.size() > 0 ? &prototype.uvs[0] : NULL;
+ ColorRGBA32* prototypeColors = prototype.colors.size() > 0 ? &prototype.colors[0] : NULL;
+ UInt16* prototypeTris = prototype.triangles.size() > 0 ? &prototype.triangles[0] : NULL;
+ float noiseSpread = prototype.noiseSpread;
+ ColorRGBAf dry = prototype.dryColor;
+ ColorRGBAf healthy = prototype.healthyColor;
+
+ float halfGrassWidth = prototype.minWidth * 0.5F;
+ float halfGrassWidthDelta = (prototype.maxWidth - prototype.minWidth) * .5F;
+ float grassHeight = prototype.minHeight;
+ float grassHeightDelta = prototype.maxHeight - prototype.minHeight;
+ int prototypeTrisSize = prototype.triangles.size();
+ int prototypeVerticesSize = prototype.vertices.size();
+
+ if (prototypeVerticesSize == 0)
+ continue;
+
+ for (int y=0;y<res;y++)
+ {
+ for (int x=0;x<res;x++)
+ {
+ int nbIndex = y * res + x + i * res * res;
+ int origCount = patch.numberOfObjects[nbIndex];
+ if (origCount == 0)
+ continue;
+
+ float nx = (float)patchX / m_PatchCount + (float)x / (res * m_PatchCount);
+ float ny = (float)patchY / m_PatchCount + (float)y / (res * m_PatchCount);
+ m_Random.SetSeed (nbIndex + (patchX * m_PatchCount + patchY) * 1013);
+
+ // Clamp the number of genrated details to not generate more than kClampedVertex vertices
+ int maxCount = (kClampedVertexCount - vertexCount) / prototypeVerticesSize;
+ origCount = std::min(maxCount, origCount);
+
+ int newCount = (int)(origCount * density + (kDitherTable[(x&7)*8+(y&7)] - 0.5f) / 64.0f);
+ for (int k=0;k<newCount;k++)
+ {
+ // Generate position & rotation
+
+ float normalizedX = nx + m_Random.GetFloat() * randomResolutionSize;
+ float normalizedZ = ny + m_Random.GetFloat() * randomResolutionSize;
+
+ // normalizedX = nx + 0.5F * randomSize;
+ // normalzedZ = ny + 0.5F * randomSize;
+ Vector3f pos;
+ pos.y = heightmap->GetInterpolatedHeight (normalizedX, normalizedZ);
+ pos.x = normalizedX * size.x;
+ pos.z = normalizedZ * size.z;
+
+ float noise = PerlinNoise::NoiseNormalized(pos.x * noiseSpread, pos.z * noiseSpread);
+ ColorRGBA32 healthyDryColor = Lerp (dry, healthy, noise);
+ healthyDryColor = GfxDevice::ConvertToDeviceVertexColor (healthyDryColor);
+
+ // set second UVs to point to the fragment of the terrain lightmap underneath the detail mesh
+ CopyUVFromTerrain (normalizedX, normalizedZ, uvs2, vertexCount, prototypeVerticesSize);
+
+ if (renderMode == kDetailBillboard)
+ {
+ DebugAssertIf (prototypeVerticesSize != 4);
+ DebugAssertIf (prototypeTrisSize != 6);
+
+ float grassX = halfGrassWidth + halfGrassWidthDelta * noise;
+ float grassY = grassHeight + grassHeightDelta * noise;
+
+ detailMaxHalfWidth = std::max(detailMaxHalfWidth, grassX);
+ detailMaxHeight = std::max(detailMaxHeight, grassY);
+
+ Vector2f billboardSize[] =
+ {
+ Vector2f (-grassX, 0),
+ Vector2f (-grassX, grassY),
+ Vector2f (grassX, grassY),
+ Vector2f (grassX, 0)
+ };
+
+ CopyVertex (pos, vertices, vertexCount, prototypeVerticesSize);
+ CopyUV (prototypeUvs, uvs, vertexCount, prototypeVerticesSize);
+
+ // used for offsetting vertices in the vertex shader
+ CopyTangents (billboardSize, tangents, vertexCount, prototypeVerticesSize);
+
+ CopyColor (prototypeColors, colors, healthyDryColor, vertexCount, prototypeVerticesSize);
+
+ CopyNormalFromTerrain(*heightmap, normalizedX, normalizedZ, normals, vertexCount, prototypeVerticesSize);
+
+ for (int t=0;t<prototypeTrisSize;t++)
+ triangles[t+triangleCount] = prototypeTris[t] + vertexCount;
+
+ triangleCount += prototypeTrisSize;
+ vertexCount += prototypeVerticesSize;
+ }
+ else
+ {
+ Quaternionf rot = AxisAngleToQuaternion (Vector3f (0,1,0), m_Random.GetFloat() * 6.2831852f);
+ float scaleX = Lerp (prototype.minWidth, prototype.maxWidth, noise);
+ float scaleZ = Lerp (prototype.minHeight, prototype.maxHeight, noise);
+ Vector3f scale = Vector3f (scaleX, scaleZ, scaleX);
+ Matrix4x4f transform;
+ transform.SetTRS (pos, rot, scale);
+
+ CopyVertex (prototypeVertices, vertices, transform, vertexCount, prototypeVerticesSize);
+ CopyUV (prototypeUvs, uvs, vertexCount, prototypeVerticesSize);
+ CopyColor (prototypeColors, colors, healthyDryColor, vertexCount, prototypeVerticesSize);
+
+ if (renderMode == kDetailMeshGrass)
+ {
+ CopyNormalFromTerrain(*heightmap, normalizedX, normalizedZ, normals, vertexCount, prototypeVerticesSize);
+ }
+ else if (normals != NULL)
+ {
+ CopyNormal (prototypeNormals, normals, rot, vertexCount, prototypeVerticesSize);
+ }
+
+ for (int t=0;t<prototypeTrisSize;t++)
+ triangles[t+triangleCount] = prototypeTris[t] + vertexCount;
+
+ triangleCount += prototypeTrisSize;
+ vertexCount += prototypeVerticesSize;
+ }
+ }
+ }
+ }
+ }
+ PROFILER_END_INTERNAL;
+
+ PROFILER_BEGIN_INTERNAL(gAssignToMesh, NULL);
+
+ AssertIf(triangleCount != totalTriangleCount);
+ AssertIf(vertexCount != totalVertexCount);
+
+ // Assign the mesh
+ mesh.Clear(true);
+ unsigned meshFormat = VERTEX_FORMAT5 (Vertex, Normal, Color, TexCoord0, TexCoord1);
+ if (renderMode == kDetailBillboard)
+ meshFormat |= VERTEX_FORMAT1 (Tangent);
+
+ mesh.ResizeVertices (vertexCount, meshFormat);
+ strided_copy (vertices, vertices + vertexCount, mesh.GetVertexBegin ());
+ strided_copy (colors, colors + vertexCount, mesh.GetColorBegin ());
+ strided_copy (normals, normals + vertexCount, mesh.GetNormalBegin ());
+ strided_copy (uvs, uvs + vertexCount, mesh.GetUvBegin (0));
+ strided_copy (uvs2, uvs2 + vertexCount, mesh.GetUvBegin (1));
+ if (renderMode == kDetailBillboard)
+ strided_copy (tangents, tangents + vertexCount, mesh.GetTangentBegin ());
+
+ mesh.SetIndicesComplex (triangles, triangleCount, 0, kPrimitiveTriangles, Mesh::k16BitIndices);
+ mesh.SetChannelsDirty (mesh.GetAvailableChannels (), true);
+ mesh.RecalculateBounds ();
+
+ if (renderMode == kDetailBillboard)
+ ExpandDetailBillboardBounds(mesh, detailMaxHalfWidth, detailMaxHeight);
+
+ PROFILER_END_INTERNAL;
+}
+
+void DetailDatabase::ComputeVertexAndTriangleCount(DetailPatch &patch, DetailRenderMode renderMode, float density, int* vertexCount, int* triangleCount)
+{
+ *triangleCount = 0;
+ *vertexCount = 0;
+ int res = m_PatchSamples;
+
+ for (int i=0;i<patch.layerIndices.size();i++)
+ {
+ DetailPrototype &prototype = m_DetailPrototypes[patch.layerIndices[i]];
+ if (prototype.renderMode != renderMode)
+ continue;
+
+ if (prototype.vertices.empty())
+ continue;
+
+ int count = 0;
+ for (int y=0;y<res;y++)
+ {
+ for (int x=0;x<res;x++)
+ {
+ int nbIndex = y * res + x + i * res * res;
+ int origCount = patch.numberOfObjects[nbIndex];
+ if (!origCount)
+ continue;
+ int newCount = (int)(origCount * density + (kDitherTable[(x&7)*8+(y&7)] - 0.5f) / 64.0f);
+ count += newCount;
+ }
+ }
+
+
+
+ // Clamp the number of genrated details to not generate more than kClampedVertex vertices
+ int maxCount = (kClampedVertexCount - *vertexCount) / prototype.vertices.size();
+ count = std::min(maxCount, count);
+
+ *triangleCount += prototype.triangles.size() * count;
+ *vertexCount += prototype.vertices.size() * count;
+ }
+}
+
+
+void DetailDatabase::UpdateDetailPrototypesIfDirty ()
+{
+ if (m_IsPrototypesDirty)
+ RefreshPrototypes();
+}
+
+#if UNITY_EDITOR
+/*
+AssetDatabase::RegisterPostprocessCallback (Postprocess);
+static void Postprocess (const std::set<UnityGUID>& refreshed, const std::set<UnityGUID>& removed, const std::set<UnityGUID>& moved);
+{
+ for (std::set<UnityGUID>::iterator i=refresh.begin();i != refreshed.end();i++)
+ {
+ PPtr<Texture> tex = dynamic_pptr_cast<Texture*> (GetMainAsset());
+ if (tex)
+ {
+ vector<TerrainData*> data;
+ FindObjectsOfType(data);
+ data.GetDetailDatabase().RefreshPrototypes();
+ }
+ }
+
+}
+*/
+#endif
+
+
+
+void DetailPrototypeToMono (const DetailPrototype &src, MonoDetailPrototype &dest) {
+ dest.prototype = Scripting::ScriptingWrapperFor (src.prototype);
+ dest.prototypeTexture = Scripting::ScriptingWrapperFor (src.prototypeTexture);
+ dest.healthyColor = src.healthyColor;
+ dest.dryColor = src.dryColor;
+ dest.minWidth = src.minWidth;
+ dest.maxWidth = src.maxWidth;
+ dest.minHeight = src.minHeight;
+ dest.maxHeight = src.maxHeight;
+ dest.noiseSpread = src.noiseSpread;
+ dest.bendFactor = src.bendFactor;
+ dest.renderMode = src.renderMode;
+ dest.usePrototypeMesh = src.usePrototypeMesh;
+
+}
+void DetailPrototypeToCpp (MonoDetailPrototype &src, DetailPrototype &dest) {
+ dest.prototype = ScriptingObjectToObject<GameObject> (src.prototype);
+ dest.prototypeTexture = ScriptingObjectToObject<Texture2D> (src.prototypeTexture);
+ dest.healthyColor = src.healthyColor;
+ dest.dryColor = src.dryColor;
+ dest.minWidth = src.minWidth;
+ dest.maxWidth = src.maxWidth;
+ dest.minHeight = src.minHeight;
+ dest.maxHeight = src.maxHeight;
+ dest.noiseSpread = src.noiseSpread;
+ dest.bendFactor = src.bendFactor;
+ dest.renderMode = src.renderMode;
+ dest.usePrototypeMesh = src.usePrototypeMesh;
+}
+
+
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/DetailDatabase.h b/Runtime/Terrain/DetailDatabase.h
new file mode 100644
index 0000000..52cfc7e
--- /dev/null
+++ b/Runtime/Terrain/DetailDatabase.h
@@ -0,0 +1,270 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_TERRAIN
+
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Math/Random/rand.h"
+#include "Runtime/Math/Rect.h"
+#include <vector>
+#include "TreeDatabase.h"
+
+class TerrainData;
+class Heightmap;
+class Mesh;
+
+using std::vector;
+
+struct DetailPatch
+{
+ public:
+ DECLARE_SERIALIZE (DetailPatch)
+ AABB bounds;
+
+ bool dirty;
+
+ vector<UInt8> layerIndices;
+ vector<UInt8> numberOfObjects;
+
+ DetailPatch () { dirty = false; }
+};
+
+template<class TransferFunc>
+void DetailPatch::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (bounds);
+ TRANSFER (layerIndices);
+ TRANSFER (numberOfObjects);
+}
+
+enum DetailRenderMode
+{
+ kDetailBillboard = 0, // billboard
+ kDetailMeshLit, // just a mesh, lit like everything else
+ kDetailMeshGrass, // mesh (user supplied or generated grass crosses), waves in the wind
+ kDetailRenderModeCount
+};
+
+struct DetailPrototype
+{
+ DECLARE_SERIALIZE (DetailPrototype)
+ PPtr<GameObject> prototype;
+ PPtr<Texture2D> prototypeTexture;
+
+ float minWidth, maxWidth; ///< Width of the grass billboards (if renderMode is grassBillboard)
+ float minHeight, maxHeight; ///< Height of the grass billboards (if renderMode is grassBillboard)
+ float noiseSpread;
+ float bendFactor;
+ ColorRGBAf healthyColor;
+ ColorRGBAf dryColor;
+ float lightmapFactor;
+ int renderMode;
+ int usePrototypeMesh;
+
+ vector<Vector3f> vertices;
+ vector<Vector3f> normals;
+ vector<Vector2f> uvs;
+ vector<ColorRGBA32> colors;
+ vector<UInt16> triangles;
+
+ DetailPrototype () :
+ healthyColor (67/255.0F, 249/255.0F, 42/255.0F, 1 ),
+ dryColor(205/255.0F, 188/255.0F, 26/255.0F, 1.0F )
+ {
+ minWidth = 1.0F;
+ maxWidth = 2.0F;
+ minHeight = 1.0F;
+ maxHeight = 2.0F;
+ noiseSpread = 10.0F;
+ bendFactor = 1.0F;
+ lightmapFactor = 1.0F;
+ renderMode = kDetailMeshGrass;
+ usePrototypeMesh = false;
+ }
+};
+
+template<class TransferFunc>
+void DetailPrototype::Transfer (TransferFunc& transfer)
+{
+ transfer.SetVersion(2);
+
+ TRANSFER (prototype);
+ TRANSFER (prototypeTexture);
+ TRANSFER (minWidth);
+ TRANSFER (maxWidth);
+ TRANSFER (minHeight);
+ TRANSFER (maxHeight);
+ TRANSFER (noiseSpread);
+ TRANSFER (bendFactor);
+ TRANSFER (healthyColor);
+ TRANSFER (dryColor);
+ TRANSFER (lightmapFactor);
+ TRANSFER (renderMode);
+ TRANSFER (usePrototypeMesh);
+
+ if (transfer.IsOldVersion(1))
+ {
+ if (prototype)
+ usePrototypeMesh = 1;
+ else
+ usePrototypeMesh = 0;
+ }
+}
+
+
+class DetailDatabase
+{
+ public:
+ DetailDatabase (TerrainData* terrainData, TreeDatabase* treeDatabase);
+ ~DetailDatabase ();
+
+ DECLARE_SERIALIZE (DetailDatabase)
+
+ Texture2D* GetAtlasTexture () { return m_AtlasTexture; }
+ GET_SET (ColorRGBAf, WavingGrassTint, m_WavingGrassTint);
+ GET_SET (float, WavingGrassStrength, m_WavingGrassStrength);
+ GET_SET (float, WavingGrassAmount, m_WavingGrassAmount);
+ GET_SET (float, WavingGrassSpeed, m_WavingGrassSpeed);
+
+ int GetPatchCount () const { return m_PatchCount; }
+
+ const vector<DetailPrototype> &GetDetailPrototypes () const { return m_DetailPrototypes; }
+ void SetDetailPrototypes (const vector<DetailPrototype> &treePrototypes );
+ void RemoveDetailPrototype (int index);
+
+ void SetDetailResolution (int resolution, int resolutionPerPatch);
+
+ void ResetDirtyDetails ();
+ void SetDetailPrototypesDirty ();
+ void UpdateDetailPrototypesIfDirty ();
+ void GenerateTextureAtlasThreaded ();
+
+ void SetDirty ();
+
+ int GetWidth () const { return GetResolution (); }
+ int GetHeight () const { return GetResolution (); }
+ int GetResolution () const { return m_PatchSamples * m_PatchCount; }
+ int GetResolutionPerPatch () const { return m_PatchSamples; }
+
+ int GetSupportedLayers (int xBase, int yBase, int totalWidth, int totalHeight, int *buffer) const;
+ void GetLayer (int xBase, int yBase, int totalWidth, int totalHeight, int detailIndex, int *buffer) const;
+ void SetLayer (int xBase, int yBase, int totalWidth, int totalHeight, int detailIndex, const int *buffer);
+
+ int GetIndex (int x, int y, int l) const
+ {
+ int res = m_PatchSamples;
+ int nbIndex = y * res + x + l * res * res;
+ return nbIndex;
+ }
+
+ void RefreshPrototypes ();
+
+ Rectf GetNormalizedArea (int x, int y) const
+ {
+ float fx = (float)x / m_PatchCount;
+ float fy = (float)y / m_PatchCount;
+ float size = 1.0F / m_PatchCount;
+ return Rectf (fx, fy, size, size);
+ }
+
+// void GenerateBounds (DetailPatch &patch, int patchX, int patchY);
+
+ Mesh* BuildMesh (int patchX, int patchY, Vector3f size, int lightmapIndex, DetailRenderMode renderMode, float density);
+ bool IsPatchEmpty (int x, int y) const;
+ bool IsPatchDirty (int x, int y) const;
+
+
+
+private:
+ void GenerateMesh (Mesh& mesh, int patchX, int patchY, Vector3f size, int lightmapIndex, DetailRenderMode renderMode, float density, int totalVertexCount, int totalTriangleCount);
+
+ #if UNITY_EDITOR
+ void SetupPreloadTextureAtlasData ();
+ #endif
+ void RefreshPrototypesStep1 (Texture2D** sourceTextures);
+
+ void CleanupPrototype (DetailPrototype &proto, string const& error);
+ void ComputeVertexAndTriangleCount(DetailPatch &patch, DetailRenderMode renderMode, float density, int* vertexCount, int* triangleCount);
+ static int GetActualResolution (int resolution, int heightmapResolution);
+ int AddLayerIndex (int detailIndex, DetailPatch &patch);
+ void RemoveLocalLayerIndex (int detailIndex, DetailPatch& patch);
+
+ const DetailPatch& GetPatch (int x, int y) const { return m_Patches[y * m_PatchCount + x]; }
+ DetailPatch& GetPatch (int x, int y) { return m_Patches[y * m_PatchCount + x]; }
+
+ bool m_IsPrototypesDirty;
+ vector<DetailPatch> m_Patches;
+ vector<DetailPrototype> m_DetailPrototypes;
+ TerrainData* m_TerrainData;
+ TreeDatabase* m_TreeDatabase;
+ int m_PatchCount;
+ int m_PatchSamples;
+ vector<Vector3f> m_RandomRotations;
+ Texture2D* m_AtlasTexture;
+
+ ColorRGBAf m_WavingGrassTint;
+ float m_WavingGrassStrength;
+ float m_WavingGrassAmount;
+ float m_WavingGrassSpeed;
+
+ Rand m_Random;
+
+ vector<PPtr<Texture2D> > m_PreloadTextureAtlasData;
+ vector<Rectf> m_PreloadTextureAtlasUVLayout;
+};
+
+
+template<class TransferFunc>
+void DetailDatabase::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (m_Patches);
+ TRANSFER (m_DetailPrototypes);
+ TRANSFER (m_PatchCount);
+ TRANSFER (m_PatchSamples);
+ TRANSFER (m_RandomRotations);
+ transfer.Transfer( m_WavingGrassTint, "WavingGrassTint" );
+ TRANSFER (m_WavingGrassStrength);
+ TRANSFER (m_WavingGrassAmount);
+ TRANSFER (m_WavingGrassSpeed);
+ transfer.Transfer (m_TreeDatabase->GetInstances(), "m_TreeInstances");
+ transfer.Transfer (m_TreeDatabase->GetTreePrototypes(), "m_TreePrototypes");
+
+ #if UNITY_EDITOR
+ if (transfer.IsBuildingTargetPlatform(kBuildAnyPlayerData))
+ {
+ SetupPreloadTextureAtlasData();
+ TRANSFER (m_PreloadTextureAtlasData);
+ m_PreloadTextureAtlasData.clear();
+ }
+ else
+ #endif
+ {
+ TRANSFER (m_PreloadTextureAtlasData);
+ }
+}
+
+
+struct MonoDetailPrototype {
+ ScriptingObjectPtr prototype;
+ ScriptingObjectPtr prototypeTexture;
+
+ ColorRGBAf healthyColor;
+ ColorRGBAf dryColor;
+
+ float minWidth, maxWidth;
+ float minHeight, maxHeight;
+ float noiseSpread;
+ float bendFactor;
+ int renderMode;
+ int usePrototypeMesh;
+};
+
+
+void DetailPrototypeToMono (const DetailPrototype &src, MonoDetailPrototype &dest);
+void DetailPrototypeToCpp (MonoDetailPrototype &src, DetailPrototype &dest) ;
+
+
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/DetailRenderer.cpp b/Runtime/Terrain/DetailRenderer.cpp
new file mode 100644
index 0000000..b591fdc
--- /dev/null
+++ b/Runtime/Terrain/DetailRenderer.cpp
@@ -0,0 +1,303 @@
+#include "UnityPrefix.h"
+#include "DetailRenderer.h"
+
+#if ENABLE_TERRAIN
+
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "Runtime/Shaders/MaterialProperties.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/Camera/IntermediateRenderer.h"
+
+namespace DetailRenderer_Static
+{
+static SHADERPROP(Cutoff);
+static SHADERPROP(MainTex);
+static SHADERPROP(maxDistanceSqr);
+static SHADERPROP(CameraPosition);
+static SHADERPROP(WaveAndDistance);
+static SHADERPROP(WavingTint);
+static SHADERPROP(CameraRight);
+static SHADERPROP(CameraUp);
+} // namespace SplatMaterials_Static
+
+DetailRenderer::DetailRenderer (PPtr<TerrainData> terrain, Vector3f position, int lightmapIndex)
+{
+ using namespace DetailRenderer_Static;
+
+ m_Database = terrain;
+ m_Position = position;
+ m_LightmapIndex = lightmapIndex;
+
+ const char* shaders[] = {
+ "Hidden/TerrainEngine/Details/BillboardWavingDoublePass",
+ "Hidden/TerrainEngine/Details/Vertexlit",
+ "Hidden/TerrainEngine/Details/WavingDoublePass"
+ };
+
+ ScriptMapper& sm = GetScriptMapper ();
+ bool shaderNotFound = false;
+ for (int i = 0; i < kDetailRenderModeCount; i++)
+ {
+ Shader* shader = sm.FindShader(shaders[i]);
+ if (shader == NULL)
+ {
+ shaderNotFound = true;
+ shader = sm.FindShader("Diffuse");
+ }
+
+ m_Materials[i] = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+ m_Materials[i]->SetFloat (kSLPropCutoff, .5f * .75f);
+ }
+
+ if (shaderNotFound)
+ {
+ ErrorString("Unable to find shaders used for the terrain engine. Please include Nature/Terrain/Diffuse shader in Graphics settings.");
+ }
+
+ m_RenderCount = 0;
+ m_LastTime = 0;
+}
+
+DetailPatchRender& DetailRenderer::GrabCachedPatch (int x, int y, int lightmapIndex, DetailRenderMode mode, float density)
+{
+ DetailList &patches = m_Patches[mode];
+ UInt32 index = x + y*m_Database->GetDetailDatabase().GetPatchCount();
+
+ DetailPatchRender& render = patches[index];
+ if(!render.inited)
+ {
+ render.mesh = m_Database->GetDetailDatabase().BuildMesh(x, y, m_TerrainSize, lightmapIndex, mode, density);
+ render.isMeshNull = render.mesh == NULL;
+ render.x = x;
+ render.y = y;
+ render.inited = true;
+ }
+ render.lastUsed = m_RenderCount;
+ return render;
+}
+
+void DetailRenderer::Render (Camera* camera, float viewDistance, int layer, float detailDensity)
+{
+ using namespace DetailRenderer_Static;
+
+ detailDensity = clamp01(detailDensity);
+
+ DetailDatabase& detail = m_Database->GetDetailDatabase();
+ m_LastTime += (IsWorldPlaying() ? GetDeltaTime() * detail.GetWavingGrassStrength() * .05f : 0);
+ m_RenderCount++;
+
+
+ m_TerrainSize = m_Database->GetHeightmap().GetSize();
+ int patchCount = detail.GetPatchCount();
+ if (patchCount == 0)
+ return;
+
+ detail.UpdateDetailPrototypesIfDirty();
+
+ for (int i=0;i<kDetailRenderModeCount;i++)
+ {
+ if (m_Materials[i]->HasProperty(kSLPropMainTex))
+ m_Materials[i]->SetTexture (kSLPropMainTex, detail.GetAtlasTexture());
+ }
+
+ Transform* camT = camera->QueryComponent (Transform);
+
+ Vector3f position = camT->GetPosition() - m_Position;
+ int centerX = RoundfToInt(position.x * patchCount / m_TerrainSize.x);
+ int centerY = RoundfToInt(position.z * patchCount / m_TerrainSize.z);
+
+ int halfWidth = int(Ceilf(patchCount * viewDistance / m_TerrainSize.x) + 1);
+ int halfHeight = int(Ceilf(patchCount * viewDistance / m_TerrainSize.z) + 1);
+
+ int minx = centerX - halfWidth;
+ if(minx < 0) minx = 0;
+ if(minx > patchCount - 1) minx = patchCount - 1;
+
+ int miny = centerY - halfHeight;
+ if(miny < 0) miny = 0;
+ if(miny > patchCount - 1) miny = patchCount - 1;
+
+ int maxx = centerX + halfWidth;
+ if(maxx < 0) maxx = 0;
+ if(maxx > patchCount - 1) maxx = patchCount - 1;
+
+ int maxy = centerY + halfHeight;
+ if(maxy < 0) maxy = 0;
+ if(maxy > patchCount - 1) maxy = patchCount - 1;
+
+ float sqrViewDistance = viewDistance * viewDistance;
+
+ Plane planes[6];
+ ExtractProjectionPlanes( camera->GetWorldToClipMatrix(), planes);
+
+// DetailList *newPatches = new DetailList[3];
+ int totalVisible = 0;
+ int total = 0;
+
+ bool supportsBillboards = m_Materials[0]->GetShader()->IsSupported();
+
+ // Find and cull all visible patches
+ for (int y=miny;y<=maxy;y++)
+ {
+ for (int x=minx;x<=maxx;x++)
+ {
+ if (detail.IsPatchEmpty (x,y))
+ continue;
+
+ for (int i=0;i<kDetailRenderModeCount;i++)
+ {
+ // Skip billboard rendering if not supported
+ if( i == 0 && !supportsBillboards )
+ continue;
+
+ // Grab the cached patch
+ DetailPatchRender &render = GrabCachedPatch (x, y, m_LightmapIndex, (DetailRenderMode)i, detailDensity);
+
+ if (render.isMeshNull)
+ {
+ render.isCulledVisible = false;
+ }
+ else
+ {
+ AABB bounds = render.mesh->GetBounds();
+
+ render.isCulledVisible = true;
+ if (CalculateSqrDistance(position,bounds) > sqrViewDistance)
+ {
+ render.isCulledVisible = false;
+ }
+ else
+ {
+ bounds.GetCenter() += m_Position;
+ if (!IntersectAABBFrustumFull(bounds, planes))
+ render.isCulledVisible = false;
+ }
+
+ if(render.isCulledVisible)
+ totalVisible ++;
+ }
+ total++;
+ }
+ }
+ }
+
+ Vector3f up = camT->InverseTransformDirection (Vector3f(0.0f,1.0f,0.0f));
+ up.z = 0;
+ up = camT->TransformDirection(up);
+ up = NormalizeSafe(up);
+
+ MaterialPropertyBlock props;
+
+ Vector3f right = Cross (camT->TransformDirection(Vector3f(0.0f,0.0f,-1.0f)), up);
+ right = NormalizeSafe(right);
+
+ Matrix4x4f matrix;
+ matrix.SetTranslate( m_Position );
+
+ for (int r=0;r<kDetailRenderModeCount;r++)
+ {
+ Material *material = m_Materials[r];
+
+ props.Clear();
+ props.AddPropertyFloat(kSLPropmaxDistanceSqr, sqrViewDistance);
+ Vector4f prop;
+ prop[0] = position.x;
+ prop[1] = position.y;
+ prop[2] = position.z;
+ prop[3] = 1.0f / sqrViewDistance;
+ props.AddPropertyVector(kSLPropCameraPosition, prop);
+ prop[0] = m_LastTime;
+ prop[1] = detail.GetWavingGrassSpeed() * .4f;
+ // cancel wind on mesh lit
+ prop[2] = r == kDetailMeshLit ? 0 : detail.GetWavingGrassAmount() * 6.0f;
+ prop[3] = sqrViewDistance;
+ props.AddPropertyVector(kSLPropWaveAndDistance, prop);
+ ColorRGBAf color = detail.GetWavingGrassTint();
+ props.AddPropertyColor(kSLPropWavingTint, color);
+ prop[0] = right.x;
+ prop[1] = right.y;
+ prop[2] = right.z;
+ prop[3] = 0.0f;
+ props.AddPropertyVector(kSLPropCameraRight, prop);
+ prop[0] = up.x;
+ prop[1] = up.y;
+ prop[2] = up.z;
+ prop[3] = 0.0f;
+ props.AddPropertyVector(kSLPropCameraUp, prop);
+
+
+ DetailList &curPatches = m_Patches[r];
+ for (DetailList::iterator i = curPatches.begin(); i != curPatches.end(); i++)
+ {
+ if (i->second.isCulledVisible)
+ {
+ IntermediateRenderer* r = AddMeshIntermediateRenderer( matrix, i->second.mesh, material, layer, false, true, 0, camera );
+ r->SetPropertyBlock( props );
+ r->SetLightmapIndexIntNoDirty(m_LightmapIndex);
+ }
+ }
+ }
+
+ for (int r=0;r<kDetailRenderModeCount;r++)
+ {
+ DetailList &curPatches = m_Patches[r];
+ DetailList::iterator next;
+ for (DetailList::iterator render = curPatches.begin(); render != curPatches.end(); render=next)
+ {
+ next = render;
+ next++;
+ if(render->second.lastUsed < m_RenderCount)
+ curPatches.erase(render);
+ }
+ }
+}
+
+/// Cleanup all the cached render patches
+void DetailRenderer::Cleanup ()
+{
+ for(int i=0;i<kDetailRenderModeCount;i++)
+ {
+ DestroySingleObject (m_Materials[i]);
+ DetailList &curPatches = m_Patches[i];
+
+ for (DetailList::iterator render = curPatches.begin(); render != curPatches.end(); ++render)
+ {
+ render->second.inited = false;
+ DestroySingleObject(render->second.mesh);
+ render->second.mesh = NULL;
+ }
+ }
+}
+
+void DetailRenderer::ReloadAllDetails ()
+{
+ for (int i=0;i<kDetailRenderModeCount;i++)
+ {
+ m_Patches[i].clear();
+ }
+}
+
+void DetailRenderer::ReloadDirtyDetails ()
+{
+ DetailDatabase& detail = m_Database->GetDetailDatabase();
+ for (int i=0;i<kDetailRenderModeCount;i++)
+ {
+ DetailList &curPatches = m_Patches[i];
+ DetailList::iterator next;
+ for (DetailList::iterator render = curPatches.begin(); render != curPatches.end(); render=next)
+ {
+ next = render;
+ next++;
+ if (detail.IsPatchDirty (render->second.x, render->second.y))
+ curPatches.erase(render);
+ }
+ }
+}
+
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/DetailRenderer.h b/Runtime/Terrain/DetailRenderer.h
new file mode 100644
index 0000000..2e11fe2
--- /dev/null
+++ b/Runtime/Terrain/DetailRenderer.h
@@ -0,0 +1,56 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_TERRAIN
+
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "TerrainData.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Utilities/MemoryPool.h"
+
+class DetailPatchRender
+{
+public:
+ Mesh *mesh;
+ bool isCulledVisible;
+ bool isMeshNull;
+ bool inited;
+ int lastUsed;
+ int x;
+ int y;
+
+ DetailPatchRender() { inited = false; mesh = NULL; }
+ ~DetailPatchRender() { DestroySingleObject (mesh); }
+};
+
+class DetailRenderer
+{
+public:
+ Material *m_Materials[kDetailRenderModeCount];
+
+ DetailRenderer (PPtr<TerrainData> terrain, Vector3f position, int lightmapIndex);
+ void Render (Camera *camera, float viewDistance, int layer, float detailDensity);
+ void Cleanup ();
+ void ReloadAllDetails();
+ void ReloadDirtyDetails();
+
+ int GetLightmapIndex() { return m_LightmapIndex; }
+ void SetLightmapIndex(int value) { m_LightmapIndex = value; }
+
+private:
+ typedef std::map<UInt32,DetailPatchRender, std::less<UInt32> ,memory_pool<std::pair<const UInt32, DetailPatchRender> > > DetailList;
+
+ PPtr<TerrainData> m_Database;
+ Vector3f m_TerrainSize;
+ UInt8 m_LightmapIndex;
+
+ DetailList m_Patches[kDetailRenderModeCount];
+ Vector3f m_Position;
+ int m_RenderCount;
+ float m_LastTime;
+
+ DetailPatchRender& GrabCachedPatch (int x, int y, int lightmapIndex, DetailRenderMode mode, float density);
+};
+
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/Heightmap.cpp b/Runtime/Terrain/Heightmap.cpp
new file mode 100644
index 0000000..cf3bf15
--- /dev/null
+++ b/Runtime/Terrain/Heightmap.cpp
@@ -0,0 +1,892 @@
+#include "UnityPrefix.h"
+#include "Heightmap.h"
+
+#if ENABLE_TERRAIN
+
+#include "Runtime/Terrain/TerrainRenderer.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Utilities/Utility.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxHeightField.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxHeightFieldDesc.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxHeightFieldShape.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxHeightFieldShapeDesc.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxHeightFieldSample.h"
+#include "Runtime/Dynamics/PhysicsManager.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxScene.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxActor.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxActorDesc.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxCapsuleShape.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxCapsuleShapeDesc.h"
+#include "Runtime/Terrain/DetailDatabase.h"
+#include "Runtime/Dynamics/NxWrapperUtility.h"
+#include "math.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Terrain/TerrainData.h"
+#include "Runtime/BaseClasses/SupportedMessageOptimization.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Terrain/TerrainIndexGenerator.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Dynamics/TerrainCollider.h"
+#include "Runtime/Interfaces/IPhysics.h"
+
+enum { kMaxHeight = 32766 };
+
+using namespace std;
+
+inline UInt32 GetLowMaterialIndex (UInt32 index)
+{
+ return index & 0x7F;
+}
+
+inline UInt32 GetHighMaterialIndex (UInt32 index)
+{
+ return index >> 7;
+}
+
+static void UpdatePatchMeshInternal (
+ const Heightmap& heightmap,
+ const StrideIterator<Vector3f>& vertices,
+ const StrideIterator<Vector3f>& normals,
+ const StrideIterator<Vector2f>& uvs,
+ int xPatch, int yPatch, int mipLevel, int edgeMask, TerrainRenderer *renderer)
+{
+ Vector3f hmScale = heightmap.GetScale();
+
+ float skipScale = 1 << mipLevel;
+ float scale = hmScale.y / (float)(kMaxHeight);
+
+ Vector2f uvscale;
+ uvscale.x = 1.0F / (heightmap.GetWidth() - 1) * skipScale;
+ uvscale.y = 1.0F / (heightmap.GetHeight() - 1) * skipScale;
+
+ Vector2f uvoffset;
+ uvoffset.x = xPatch * (1 << mipLevel) * (kPatchSize -1);
+ uvoffset.x /= (heightmap.GetWidth() - 1);
+ uvoffset.y = yPatch * (1 << mipLevel) * (kPatchSize -1);
+ uvoffset.y /= (heightmap.GetHeight() - 1);
+
+ int skip = 1 << mipLevel;
+ int xBase = xPatch * (kPatchSize -1);
+ int yBase = yPatch * (kPatchSize -1);
+
+ StrideIterator<Vector3f> itVertices = vertices;
+ StrideIterator<Vector3f> itNormals = normals;
+ StrideIterator<Vector2f> itUVs = uvs;
+
+ for (int x=0;x<kPatchSize;x++)
+ {
+ for (int y=0;y<kPatchSize;y++)
+ {
+ int sampleIndex = (y + yBase) + (x + xBase) * heightmap.GetHeight();
+ sampleIndex *= skip;
+ float height = heightmap.GetRawHeight(sampleIndex);
+ height *= scale;
+
+ int index = y + x * kPatchSize;
+
+ // Vertex
+ itVertices[index].x = (x + xBase) * hmScale.x * skipScale;
+ itVertices[index].y = height;
+ itVertices[index].z = (y + yBase) * hmScale.z * skipScale;
+
+ // UV
+ itUVs[index].x = (float)x * uvscale.x + uvoffset.x;
+ itUVs[index].y = (float)y * uvscale.y + uvoffset.y;
+
+ // Normal
+ Vector3f normal = heightmap.CalculateNormalSobelRespectingNeighbors ((x + xBase) * skip, (y + yBase) * skip, renderer);
+ itNormals[index] = normal;
+ }
+ }
+}
+
+Heightmap::Heightmap (TerrainData *owner)
+: m_Scale(1.0f,1.0f,1.0f)
+{
+ m_TerrainData = owner;
+#if ENABLE_PHYSICS
+ m_NxHeightField = NULL;
+#endif
+}
+
+Heightmap::~Heightmap ()
+{
+#if ENABLE_PHYSICS
+ CleanupNx ();
+#endif
+}
+
+/// Calculates the index of the patch given it's level and x, y index
+int Heightmap::GetPatchIndex (int x, int y, int level) const
+{
+ int index = 0;
+ for (int i=0;i<level;i++)
+ {
+ int size = 1 << (m_Levels - i);
+ index += size * size;
+ }
+
+ int width = 1 << (m_Levels - level);
+ index += width * y;
+ index += x;
+ return index;
+}
+
+float Heightmap::InterpolatePatchHeight (float* data, float fx, float fy) const
+{
+ int lx = (int)(fx * kPatchSize);
+ int ly = (int)(fy * kPatchSize);
+
+ int hx = lx + 1;
+ if (hx >= kPatchSize)
+ hx = kPatchSize - 1;
+ int hy = ly + 1;
+ if (hy >= kPatchSize)
+ hy = kPatchSize - 1;
+
+ float s00 = GetPatchHeight (data, lx, ly);
+ float s01 = GetPatchHeight (data, lx, hy);
+ float s10 = GetPatchHeight (data, hx, ly);
+ float s11 = GetPatchHeight (data, hx, hy);
+
+ float dx = fx * kPatchSize - lx;
+ float dy = fy * kPatchSize - ly;
+
+ float x = Lerp(s00, s10, dx);
+ float y = Lerp(s01, s11, dx);
+ float value = Lerp(x, y, dy);
+
+ return value;
+}
+
+
+Vector3f Heightmap::CalculateNormalSobelRespectingNeighbors (int x, int y, const TerrainRenderer *renderer) const
+{
+ Vector3f normal;
+ float dY, dX;
+ // Do X sobel filter
+ dX = GetHeightRespectingNeighbors (x-1, y-1, renderer) * -1.0F;
+ dX += GetHeightRespectingNeighbors (x-1, y , renderer) * -2.0F;
+ dX += GetHeightRespectingNeighbors (x-1, y+1, renderer) * -1.0F;
+ dX += GetHeightRespectingNeighbors (x+1, y-1, renderer) * 1.0F;
+ dX += GetHeightRespectingNeighbors (x+1, y , renderer) * 2.0F;
+ dX += GetHeightRespectingNeighbors (x+1, y+1, renderer) * 1.0F;
+
+ dX /= m_Scale.x;
+
+ // Do Y sobel filter
+ dY = GetHeightRespectingNeighbors (x-1, y-1, renderer) * -1.0F;
+ dY += GetHeightRespectingNeighbors (x , y-1, renderer) * -2.0F;
+ dY += GetHeightRespectingNeighbors (x+1, y-1, renderer) * -1.0F;
+ dY += GetHeightRespectingNeighbors (x-1, y+1, renderer) * 1.0F;
+ dY += GetHeightRespectingNeighbors (x , y+1, renderer) * 2.0F;
+ dY += GetHeightRespectingNeighbors (x+1, y+1, renderer) * 1.0F;
+ dY /= m_Scale.z;
+
+ // Cross Product of components of gradient reduces to
+ normal.x = -dX;
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_3_a1))
+ normal.y = 8;
+ else
+ // 5 here is just wrong!
+ normal.y = 5;
+ normal.z = -dY;
+ normal = NormalizeFast (normal);
+
+ return normal;
+}
+
+Vector3f Heightmap::CalculateNormalSobel (int x, int y) const
+{
+ Vector3f normal;
+ float dY, dX;
+ // Do X sobel filter
+ dX = GetHeight (x-1, y-1) * -1.0F;
+ dX += GetHeight (x-1, y ) * -2.0F;
+ dX += GetHeight (x-1, y+1) * -1.0F;
+ dX += GetHeight (x+1, y-1) * 1.0F;
+ dX += GetHeight (x+1, y ) * 2.0F;
+ dX += GetHeight (x+1, y+1) * 1.0F;
+
+ dX /= m_Scale.x;
+
+ // Do Y sobel filter
+ dY = GetHeight (x-1, y-1) * -1.0F;
+ dY += GetHeight (x , y-1) * -2.0F;
+ dY += GetHeight (x+1, y-1) * -1.0F;
+ dY += GetHeight (x-1, y+1) * 1.0F;
+ dY += GetHeight (x , y+1) * 2.0F;
+ dY += GetHeight (x+1, y+1) * 1.0F;
+ dY /= m_Scale.z;
+
+ // Cross Product of components of gradient reduces to
+ normal.x = -dX;
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_3_a1))
+ normal.y = 8;
+ else
+ // 5 here is just wrong!
+ normal.y = 5;
+ normal.z = -dY;
+ normal = NormalizeFast (normal);
+
+ return normal;
+}
+
+float Heightmap::ComputeMaximumHeightError (int xPatch, int yPatch, int level) const
+{
+ // Lod zero never has error
+ if (level == 0)
+ return 0.0F;
+
+ float* data = new float[kPatchSize * kPatchSize];
+ GetPatchData (xPatch, yPatch, level, data);
+ float deltaMax = 0.0F;
+
+ int skip = 1 << level;
+ int xBase = xPatch * (kPatchSize - 1) * skip;
+ int yBase = yPatch * (kPatchSize - 1) * skip;
+
+ int size = (kPatchSize - 1) * skip + 1;
+ float normalizeScale = 1.0F / (kPatchSize * skip);
+
+ for (int x=0;x<size;x++)
+ {
+ for (int y=0;y<size;y++)
+ {
+ float fx = (float)x * normalizeScale;
+ float fy = (float)y * normalizeScale;
+ float interpolatedHeight = InterpolatePatchHeight (data, fx, fy);
+ float realHeight = GetHeight (x + xBase, y + yBase);
+ float delta = Abs(realHeight - interpolatedHeight);
+ deltaMax = max(deltaMax, delta);
+ }
+ }
+
+ delete[] data;
+ return deltaMax;
+}
+
+
+Vector3f Heightmap::GetSize () const
+{
+ return Vector3f (m_Scale.x * (m_Width - 1), m_Scale.y, m_Scale.z * (m_Height - 1));
+}
+
+void Heightmap::SetSize (const Vector3f& size)
+{
+ m_Scale.x = size.x / (m_Width - 1);
+ m_Scale.y = size.y;
+ m_Scale.z = size.z / (m_Height - 1);
+
+ PrecomputeError(0, 0, m_Width, m_Height, false);
+
+#if ENABLE_PHYSICS
+ UpdateNx ();
+ RecreateShapes();
+#endif
+
+ m_TerrainData->SetDirty();
+ m_TerrainData->UpdateUsers (TerrainData::kHeightmap);
+}
+
+void Heightmap::AwakeFromLoad ()
+{
+#if ENABLE_PHYSICS
+ CreateNx ();
+ RecreateShapes ();
+#endif
+}
+
+#if ENABLE_PHYSICS
+void Heightmap::RecreateShapes ()
+{
+ for (TerrainColliderList::iterator i=m_TerrainColliders.begin();i != m_TerrainColliders.end();)
+ {
+ TerrainCollider& col = **i;
+ i++;
+ col.Create(NULL);
+ }
+}
+#endif
+
+// Precompute error only on a part of the heightmap
+// if forceHighestLod is enabled we simply set the error to infinity
+// This casues the heightmap to be rendered at full res (Used while editing)
+void Heightmap::PrecomputeError (int minX, int minY, int width, int height, bool forceHighestLod)
+{
+ for (int level=0;level <= m_Levels;level++)
+ {
+ for (int y=0;y<GetPatchCountY(level);y++)
+ {
+ for (int x=0;x<GetPatchCountX(level);x++)
+ {
+ int skip = 1 << level;
+ int curXBase = x * (kPatchSize - 1) * skip;
+ int curYBase = y * (kPatchSize - 1) * skip;
+ int curPatchSize = kPatchSize * skip;
+
+ // Are we in the bounds horizontally
+ if (curXBase + curPatchSize < minX)
+ continue;
+ if (curXBase > minX + width)
+ continue;
+
+ // Are we in the bounds vertically
+ if (curYBase + curPatchSize < minY)
+ continue;
+ if (curYBase > minY + height)
+ continue;
+
+ if (forceHighestLod)
+ {
+ m_PrecomputedError[GetPatchIndex(x, y, level)] = std::numeric_limits<float>::infinity();
+ }
+ else
+ {
+ float error = ComputeMaximumHeightError(x, y, level);
+ m_PrecomputedError[GetPatchIndex(x, y, level)] = error;
+ }
+ RecalculateMinMaxHeight(x, y, level);
+ }
+ }
+ }
+ m_TerrainData->SetDirty();
+}
+
+/// After editing is complete we need to recompute the error for the modified patches
+/// We also update the min&max height of each patch
+void Heightmap::RecomputeInvalidPatches(UNITY_TEMP_VECTOR(int)& recomputedPatches)
+{
+ recomputedPatches.clear();
+ for (int level=0;level <= m_Levels;level++)
+ {
+ for (int y=0;y<GetPatchCountY(level);y++)
+ {
+ for (int x=0;x<GetPatchCountX(level);x++)
+ {
+ int patchIndex = GetPatchIndex(x, y, level);
+ if (m_PrecomputedError[patchIndex] == std::numeric_limits<float>::infinity())
+ {
+ float error = ComputeMaximumHeightError(x, y, level);
+ m_PrecomputedError[patchIndex] = error;
+ RecalculateMinMaxHeight(x, y, level);
+
+ recomputedPatches.push_back(patchIndex);
+ }
+ }
+ }
+ }
+ if (!recomputedPatches.empty())
+ m_TerrainData->SetDirty();
+}
+
+float Heightmap::GetMaximumHeightError (int x, int y, int level) const
+{
+ return m_PrecomputedError[GetPatchIndex(x, y, level)];
+}
+
+
+float Heightmap::Bilerp (const float* corners, float u, float v)
+{
+ // Corners are laid out like this
+ /// 0 1
+ /// 2 3
+ if (u > v)
+ {
+ float z00 = corners[0];
+ float z01 = corners[1];
+ float z11 = corners[3];
+ return z00 + (z01-z00) * u + (z11 - z01) * v;
+ }
+ else
+ {
+ float z00 = corners[0];
+ float z10 = corners[2];
+ float z11 = corners[3];
+ return z00 + (z11-z10) * u + (z10 - z00) * v;
+ }
+}
+
+/// The scaled interpolated height at the normalized coordinate x, y [0...1]
+/// Out of bounds x and y will be clamped
+float Heightmap::GetInterpolatedHeight (float x, float y) const
+{
+ float fx = x * (m_Width - 1);
+ float fy = y * (m_Height - 1);
+ int lx = (int)fx;
+ int ly = (int)fy;
+
+ float u = fx - lx;
+ float v = fy - ly;
+
+ if (u > v)
+ {
+ float z00 = GetHeight (lx+0, ly+0);
+ float z01 = GetHeight (lx+1, ly+0);
+ float z11 = GetHeight (lx+1, ly+1);
+ return z00 + (z01-z00) * u + (z11 - z01) * v;
+ }
+ else
+ {
+ float z00 = GetHeight (lx+0, ly+0);
+ float z10 = GetHeight (lx+0, ly+1);
+ float z11 = GetHeight (lx+1, ly+1);
+ return z00 + (z11-z10) * u + (z10 - z00) * v;
+ }
+}
+
+// Gets the interpolated normal of the terrain at a
+float Heightmap::GetSteepness (float x, float y) const
+{
+ float steepness = Dot(GetInterpolatedNormal(x, y), Vector3f(0.0F, 1.0F, 0.0F));
+ steepness = Rad2Deg(acos (steepness));
+ return steepness;
+}
+
+// Gets the interpolated normal of the terrain at a
+Vector3f Heightmap::GetInterpolatedNormal (float x, float y) const
+{
+ float fx = x * (m_Width - 1);
+ float fy = y * (m_Height - 1);
+ int lx = (int)fx;
+ int ly = (int)fy;
+
+ Vector3f n00 = CalculateNormalSobel (lx+0, ly+0);
+ Vector3f n10 = CalculateNormalSobel (lx+1, ly+0);
+ Vector3f n01 = CalculateNormalSobel (lx+0, ly+1);
+ Vector3f n11 = CalculateNormalSobel (lx+1, ly+1);
+
+ float u = fx - lx;
+ float v = fy - ly;
+
+ Vector3f s = Lerp(n00, n10, u);
+ Vector3f t = Lerp(n01, n11, u);
+ Vector3f value = Lerp(s, t, v);
+ value = NormalizeFast(value);
+
+ return value;
+}
+
+void Heightmap::GetHeights (int xBase, int yBase, int width, int height, float* heights) const
+{
+ float toNormalize = 1.0F / kMaxHeight;
+ for (int x=0;x<width;x++)
+ {
+ for (int y=0;y<height;y++)
+ {
+ NxHeightFieldSample sample = reinterpret_cast<const NxHeightFieldSample&> (m_Heights[(y + yBase) + (x + xBase) * m_Height]);
+ float height = sample.height;
+ height *= toNormalize;
+ heights[y * width + x] = height;
+ }
+ }
+}
+
+#define SUPPORT_PHYSX_UPDATE_BLOCKS 1
+
+void Heightmap::SetHeights (int xBase, int yBase, int width, int height, const float* heights, bool delayLodComputation)
+{
+ UInt32 min = 0;
+ float normalizedTo16 = kMaxHeight;
+
+ #if ENABLE_PHYSICS && SUPPORT_PHYSX_UPDATE_BLOCKS
+ NxHeightFieldSample* nxSamples = new NxHeightFieldSample[width * height];
+ int materialIndex = GetLowMaterialIndex(GetMaterialIndex());
+ #endif
+
+ for (int x=0;x<width;x++)
+ {
+ for (int y=0;y<height;y++)
+ {
+ float nHeight = heights[y * width + x] * normalizedTo16;
+ SInt32 iheight = RoundfToInt(nHeight);
+ iheight = clamp<SInt32> (iheight, min, kMaxHeight);
+
+ // Update height value
+ m_Heights[(y + yBase) + (x + xBase) * m_Height] = iheight;
+
+ #if ENABLE_PHYSICS && SUPPORT_PHYSX_UPDATE_BLOCKS
+ // Build update buffer for novodex
+ NxHeightFieldSample sample;
+ sample.height = iheight;
+ sample.materialIndex0 = materialIndex;
+ sample.materialIndex1 = materialIndex;
+ sample.tessFlag = 1;
+ sample.unused = 0;
+ nxSamples[height * x + y] = sample;
+ #endif
+ }
+ }
+
+ #if ENABLE_PHYSICS
+ #if SUPPORT_PHYSX_UPDATE_BLOCKS
+ if (m_NxHeightField)
+ {
+ m_NxHeightField->updateBlock(xBase, yBase, width, height, height * sizeof(NxHeightFieldSample), nxSamples);
+ RecreateShapes();
+ }
+
+ delete[] nxSamples;
+ #else
+ UpdateNx ();
+ RecreateShapes();
+ #endif
+ #endif
+
+ PrecomputeError(xBase, yBase, width, height, delayLodComputation);
+
+ m_TerrainData->SetDirty();
+
+ m_TerrainData->UpdateUsers (delayLodComputation ? TerrainData::kDelayedHeightmapUpdate : TerrainData::kHeightmap);
+}
+
+int Heightmap::GetAdjustedSize (int size) const
+{
+ int levels = HighestBit( NextPowerOfTwo( size / kPatchSize ) );
+ levels = max<int>(1, levels);
+ return (1 << levels) * (kPatchSize - 1) + 1;
+}
+
+#if ENABLE_PHYSICS
+void Heightmap::SetPhysicMaterial(PPtr<PhysicMaterial> mat)
+{
+ m_DefaultPhysicMaterial = mat;
+ UpdateNx ();
+ RecreateShapes();
+}
+#endif
+
+void Heightmap::SetResolution (int resolution)
+{
+ m_Levels = HighestBit( NextPowerOfTwo( resolution / kPatchSize ) );
+ m_Levels = max<int>(1, m_Levels);
+ m_Width = (1 << m_Levels) * (kPatchSize - 1) + 1;
+ m_Height = (1 << m_Levels) * (kPatchSize - 1) + 1;
+
+ UInt32 materialIndex = GetLowMaterialIndex(GetMaterialIndex());//
+
+ NxHeightFieldSample sample;
+ sample.height = 0;
+ sample.materialIndex0 = materialIndex;
+ sample.materialIndex1 = materialIndex;
+ sample.tessFlag = 1;
+ sample.unused = 0;
+
+ m_Heights.clear();
+ m_Heights.resize(m_Width * m_Height, reinterpret_cast<UInt32&> (sample));
+
+ m_PrecomputedError.clear();
+ m_PrecomputedError.resize(GetTotalPatchCount());
+
+ m_MinMaxPatchHeights.clear();
+ m_MinMaxPatchHeights.resize(GetTotalPatchCount() * 2);
+
+#if ENABLE_PHYSICS
+ UpdateNx ();
+ RecreateShapes();
+#endif
+
+ m_TerrainData->SetDirty();
+ m_TerrainData->UpdateUsers (TerrainData::kHeightmap);
+}
+
+float Heightmap::GetHeight (int x, int y) const
+{
+ x = clamp<int>(x, 0, m_Width-1);
+ y = clamp<int>(y, 0, m_Height-1);
+ float scale = m_Scale.y / (float)(kMaxHeight);
+
+ NxHeightFieldSample sample = reinterpret_cast<const NxHeightFieldSample&> (m_Heights[y + x * m_Height]);
+ return sample.height * scale;
+}
+
+float Heightmap::GetHeightRespectingNeighbors (int x, int y, const TerrainRenderer *renderer) const
+{
+ const Heightmap *lookup = this;
+ if(x<0)
+ {
+ if(renderer && renderer->m_LeftNeighbor && renderer->m_LeftNeighbor->GetTerrainData())
+ {
+ renderer = renderer->m_LeftNeighbor;
+ lookup = &(renderer->GetTerrainData()->GetHeightmap());
+ x += lookup->m_Width - 1;
+ }
+ }
+ if(x >= lookup->m_Width)
+ {
+ if(renderer && renderer->m_RightNeighbor && renderer->m_RightNeighbor->GetTerrainData())
+ {
+ x -= lookup->m_Width - 1;
+ renderer = renderer->m_RightNeighbor;
+ lookup = &(renderer->GetTerrainData()->GetHeightmap());
+ }
+ }
+ if(y<0)
+ {
+ if(renderer && renderer->m_BottomNeighbor && renderer->m_BottomNeighbor->GetTerrainData())
+ {
+ renderer = renderer->m_BottomNeighbor;
+ lookup = &(renderer->GetTerrainData()->GetHeightmap());
+ y += lookup->m_Height - 1;
+ }
+ }
+ if(y >= lookup->m_Height)
+ {
+ if(renderer && renderer->m_TopNeighbor && renderer->m_TopNeighbor->GetTerrainData())
+ {
+ y -= lookup->m_Height - 1;
+ renderer = renderer->m_TopNeighbor;
+ lookup = &(renderer->GetTerrainData()->GetHeightmap());
+ }
+ }
+ return lookup->GetHeight(x,y);
+}
+
+void Heightmap::UpdatePatchIndices (Mesh& mesh, int xPatch, int yPatch, int level, int edgeMask)
+{
+ unsigned int count;
+
+ unsigned short *tris = TerrainIndexGenerator::GetOptimizedIndexStrip(edgeMask, count);
+ mesh.SetIndicesComplex (tris, count, 0, kPrimitiveTriangleStripDeprecated, Mesh::k16BitIndices|Mesh::kDontSupportSubMeshVertexRanges);
+}
+
+void Heightmap::UpdatePatchMesh (Mesh& mesh, int xPatch, int yPatch, int mipLevel, int edgeMask, TerrainRenderer *renderer)
+{
+ int vertexCount = kPatchSize * kPatchSize;
+ mesh.ResizeVertices (vertexCount, mesh.GetAvailableChannels() | VERTEX_FORMAT3(Vertex, TexCoord0, Normal));
+
+ UpdatePatchMeshInternal(*this,
+ mesh.GetVertexBegin(),
+ mesh.GetNormalBegin(),
+ mesh.GetUvBegin(),
+ xPatch, yPatch, mipLevel, edgeMask, renderer);
+
+ mesh.SetBounds(GetBounds(xPatch, yPatch, mipLevel));
+ mesh.SetChannelsDirty (mesh.GetAvailableChannels(), false);
+}
+
+void Heightmap::UpdatePatchIndices (VBO& vbo, SubMesh& subMesh, int xPatch, int yPatch, int level, int edgeMask)
+{
+ unsigned int count;
+
+ unsigned short *tris = TerrainIndexGenerator::GetOptimizedIndexStrip(edgeMask, count);
+ IndexBufferData ibd = {
+ tris,
+ count,
+ 1 << kPrimitiveTriangleStripDeprecated
+ };
+ vbo.UpdateIndexData(ibd);
+ subMesh.indexCount = count;
+ subMesh.topology = kPrimitiveTriangleStripDeprecated;
+}
+
+void Heightmap::UpdatePatchMesh (VBO& vbo, SubMesh& subMesh, int xPatch, int yPatch, int mipLevel, int edgeMask, TerrainRenderer *renderer)
+{
+ const VertexChannelsLayout& format = VertexDataInfo::kVertexChannelsDefault;
+ UInt8 normalOffset = format.channels[kShaderChannelVertex].dimension * GetChannelFormatSize(format.channels[kShaderChannelVertex].format);
+ UInt8 uvOffset = format.channels[kShaderChannelNormal].dimension
+ * GetChannelFormatSize(format.channels[kShaderChannelNormal].format)
+ + normalOffset;
+ UInt8 stride = format.channels[kShaderChannelTexCoord0].dimension
+ * GetChannelFormatSize(format.channels[kShaderChannelTexCoord0].format)
+ + uvOffset;
+
+ VertexBufferData vbd;
+
+ vbd.channels[kShaderChannelVertex].format = format.channels[kShaderChannelVertex].format;
+ vbd.channels[kShaderChannelVertex].dimension = format.channels[kShaderChannelVertex].dimension;
+ vbd.channels[kShaderChannelNormal].format = format.channels[kShaderChannelNormal].format;
+ vbd.channels[kShaderChannelNormal].dimension = format.channels[kShaderChannelNormal].dimension;
+ vbd.channels[kShaderChannelNormal].offset = normalOffset;
+ vbd.channels[kShaderChannelTexCoord0].format = format.channels[kShaderChannelTexCoord0].format;
+ vbd.channels[kShaderChannelTexCoord0].dimension = format.channels[kShaderChannelTexCoord0].dimension;
+ vbd.channels[kShaderChannelTexCoord0].offset = uvOffset;
+
+ // UV1 is used for lightmapping and it shares with UV0
+ vbd.channels[kShaderChannelTexCoord1] = vbd.channels[kShaderChannelTexCoord0];
+
+ vbd.streams[0].channelMask = VERTEX_FORMAT4(Vertex, Normal, TexCoord0, TexCoord1);
+ vbd.streams[0].stride = stride;
+
+ vbd.vertexCount = kPatchSize * kPatchSize;
+
+ vbd.bufferSize = VertexDataInfo::AlignStreamSize(vbd.vertexCount * stride);
+ UInt8* buffer;
+ ALLOC_TEMP_ALIGNED(buffer, UInt8, vbd.bufferSize, VertexDataInfo::kVertexDataAlign);
+ vbd.buffer = buffer;
+
+ UpdatePatchMeshInternal(*this,
+ StrideIterator<Vector3f>(vbd.buffer, stride),
+ StrideIterator<Vector3f>(vbd.buffer + normalOffset, stride),
+ StrideIterator<Vector2f>(vbd.buffer + uvOffset, stride),
+ xPatch, yPatch, mipLevel, edgeMask, renderer);
+
+ vbo.UpdateVertexData(vbd);
+ subMesh.localAABB = GetBounds(xPatch, yPatch, mipLevel);
+ subMesh.vertexCount = vbd.vertexCount;
+}
+
+void Heightmap::GetPatchData (int xPatch, int yPatch, int mipLevel, float* heights) const
+{
+ int skip = 1 << mipLevel;
+ int xBase = xPatch * (kPatchSize -1);
+ int yBase = yPatch * (kPatchSize -1);
+
+ float scale = m_Scale.y / (float)(kMaxHeight);
+
+ for (int x=0;x<kPatchSize;x++)
+ {
+ for (int y=0;y<kPatchSize;y++)
+ {
+ int index = (y + yBase) + (x + xBase) * m_Height;
+ index *= skip;
+ float height = m_Heights[index];
+ height *= scale;
+ heights[y + x * kPatchSize] = height;
+ }
+ }
+}
+
+void Heightmap::RecalculateMinMaxHeight (int xPatch, int yPatch, int mipLevel)
+{
+ int totalSamples = kPatchSize * kPatchSize;
+ float* heights = new float[totalSamples];
+ GetPatchData (xPatch, yPatch, mipLevel, heights);
+ float minHeight = std::numeric_limits<float>::infinity();
+ float maxHeight = -std::numeric_limits<float>::infinity();
+ for (int i=0;i<totalSamples;i++)
+ {
+ minHeight = min(minHeight, heights[i]);
+ maxHeight = max(maxHeight, heights[i]);
+ }
+
+ int patchIndex = GetPatchIndex(xPatch, yPatch, mipLevel);
+ m_MinMaxPatchHeights[patchIndex * 2 + 0] = minHeight / m_Scale.y ;
+ m_MinMaxPatchHeights[patchIndex * 2 + 1] = maxHeight / m_Scale.y;
+
+ delete[] heights;
+}
+
+AABB Heightmap::GetBounds () const
+{
+ return GetBounds(0, 0, m_Levels);
+}
+
+
+AABB Heightmap::GetBounds (int xPatch, int yPatch, int mipLevel) const
+{
+ int patchIndex = GetPatchIndex(xPatch, yPatch, mipLevel);
+
+ float minHeight = m_MinMaxPatchHeights[patchIndex * 2 + 0];
+ float maxHeight = m_MinMaxPatchHeights[patchIndex * 2 + 1];
+
+ Vector3f min;
+ min.x = (xPatch) * (1 << mipLevel) * (kPatchSize -1) * m_Scale.x;
+ min.z = (yPatch) * (1 << mipLevel) * (kPatchSize -1) * m_Scale.z;
+ min.y = minHeight * m_Scale.y;
+
+ Vector3f max;
+ max.x = (xPatch + 1) * (1 << mipLevel) * (kPatchSize -1) * m_Scale.x;
+ max.z = (yPatch + 1) * (1 << mipLevel) * (kPatchSize -1) * m_Scale.z;
+ max.y = maxHeight * m_Scale.y;
+
+ MinMaxAABB bounds = MinMaxAABB (min, max);
+ return bounds;
+}
+
+
+int Heightmap::GetMaterialIndex () const
+{
+#if ENABLE_PHYSICS
+ const PhysicMaterial* material = m_DefaultPhysicMaterial;
+ if (material)
+ return material->GetMaterialIndex ();
+ else
+#endif
+ return 0;
+}
+
+#if ENABLE_PHYSICS
+NxHeightField* Heightmap::GetNxHeightField ()
+{
+ if (m_NxHeightField == NULL)
+ CreateNx();
+ return m_NxHeightField;
+}
+
+void Heightmap::CreateNx ()
+{
+ IPhysics* physicsModule = GetIPhysics();
+
+ if (!physicsModule)
+ return;
+
+ if (m_NxHeightField == NULL)
+ {
+ NxHeightFieldDesc desc;
+ BuildDesc(desc);
+
+ m_NxHeightField = physicsModule->CreateNxHeightField(desc);
+
+ free(desc.samples);
+ }
+ else
+ UpdateNx ();
+}
+
+// builds an nsdesc of the heightmap (you need to deallocate the samples array yourself)
+void Heightmap::BuildDesc (NxHeightFieldDesc& desc)
+{
+ NxHeightFieldSample* nxSamples = (NxHeightFieldSample*)malloc (sizeof(NxHeightFieldSample) * m_Width * m_Height);
+ AssertIf(!nxSamples);
+ desc.nbRows = m_Width;
+ desc.nbColumns = m_Height;
+ desc.samples = nxSamples;
+ desc.sampleStride = 4;
+ // the threshold seems to be the difference between two samples in 16 height space
+ // Thus a value of less than 1 doesn't make a difference
+ desc.convexEdgeThreshold = 4;
+ int materialIndex = GetLowMaterialIndex(GetMaterialIndex());
+
+ for (int x=0;x<m_Width;x++)
+ {
+ for (int y=0;y<m_Height;y++)
+ {
+ // Build update buffer for novodex
+ NxHeightFieldSample sample;
+ sample.height = m_Heights[m_Height * x + y];
+ sample.materialIndex0 = materialIndex;
+ sample.materialIndex1 = materialIndex;
+ sample.tessFlag = 1;
+ sample.unused = 0;
+ nxSamples[m_Height * x + y] = sample;
+ }
+ }
+}
+
+void Heightmap::UpdateNx ()
+{
+ if (!m_NxHeightField)
+ return;
+
+ NxHeightFieldDesc desc;
+ BuildDesc(desc);
+
+
+ m_NxHeightField->loadFromDesc(desc);
+ free(desc.samples);
+}
+
+void Heightmap::CleanupNx ()
+{
+ if (m_NxHeightField)
+ {
+ GetIPhysics()->ReleaseHeightField(*m_NxHeightField);
+ m_NxHeightField = NULL;
+ }
+}
+
+#endif
+#endif
diff --git a/Runtime/Terrain/Heightmap.h b/Runtime/Terrain/Heightmap.h
new file mode 100644
index 0000000..cc294f4
--- /dev/null
+++ b/Runtime/Terrain/Heightmap.h
@@ -0,0 +1,199 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_TERRAIN
+
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Dynamics/Collider.h"
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Dynamics/PhysicMaterial.h"
+
+class Mesh;
+class NxHeightField;
+class NxHeightFieldDesc;
+struct SubMesh;
+class TerrainData;
+class TerrainCollider;
+class TerrainRenderer;
+class VBO;
+
+enum
+{
+ kDirectionLeft = 0, kDirectionRight = 1, kDirectionUp = 2, kDirectionDown = 3,
+ kDirectionLeftUp = 0, kDirectionRightUp = 1, kDirectionLeftDown = 2, kDirectionRightDown = 3,
+};
+
+enum
+{
+ kDirectionLeftFlag = (1<<kDirectionLeft),
+ kDirectionRightFlag = (1<<kDirectionRight),
+ kDirectionUpFlag = (1<<kDirectionUp),
+ kDirectionDownFlag = (1<<kDirectionDown),
+ kDirectionDirectNeighbourMask = (kDirectionLeftFlag|kDirectionRightFlag|kDirectionUpFlag|kDirectionDownFlag),
+};
+
+//made this value constant. It is not currently being modified anyways, and this way,
+//cached triangles strips can be used across several terrains.
+//Must be an odd number.
+#define kPatchSize 17
+
+class Heightmap
+{
+ public:
+ enum { kMaxHeight = 32766 };
+
+ DECLARE_SERIALIZE(Heightmap)
+
+ Heightmap (TerrainData* owner);
+ virtual ~Heightmap ();
+
+ int GetWidth () const { return m_Width; }
+ int GetHeight () const { return m_Height; }
+ int GetMipLevels () const { return m_Levels; }
+
+ const Vector3f& GetScale () const { return m_Scale; }
+
+ Vector3f GetSize () const;
+
+ void SetSize (const Vector3f& size);
+
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode){}
+
+ /// After editing is complete we need to recompute the error for the modified patches
+ /// We also update the min&max height of each patch
+ void RecomputeInvalidPatches (UNITY_TEMP_VECTOR(int)& recomputedPatches);
+
+ float GetMaximumHeightError (int x, int y, int level) const;
+
+ /// The scaled interpolated height at the normalized coordinate x, y [0...1]
+ /// Out of bounds x and y will be clamped
+ float GetInterpolatedHeight (float x, float y) const;
+
+ /// Interpolates 4 height values just like GetInterpolated height
+ /// Used for optimized heightmap lookups when you know the corners.
+ // Corners are laid out like this
+ /// 0 1
+ /// 2 3
+ static float Bilerp (const float* corners, float u, float v);
+
+ /// The scaled height at height map pixel x, y.
+ /// Out of bounds x and y will be clamped
+ float GetHeight (int x, int y) const;
+
+ SInt16 GetRawHeight(int sampleIndex) const { return m_Heights[sampleIndex]; }
+
+ float GetHeightRespectingNeighbors (int x, int y, const TerrainRenderer *renderer) const;
+
+ float GetSteepness (float x, float y) const;
+
+ Vector3f GetInterpolatedNormal (float x, float y) const;
+
+ void GetHeights (int xBase, int yBase, int width, int height, float* heights) const;
+ void SetHeights (int xBase, int yBase, int width, int height, const float* heights, bool delay);
+
+ int GetAdjustedSize (int size) const;
+
+ void SetResolution (int resolution);
+ int GetResolution () const { return m_Width; }
+
+ int GetTotalPatchCount () const { return GetPatchIndex(0, 0, m_Levels) + 1; }
+
+ int GetMaterialIndex () const;
+
+ void GetPatchData (int xPatch, int yPatch, int mipLevel, float* heights) const;
+
+ void UpdatePatchIndices (Mesh& mesh, int xPatch, int yPatch, int level, int edgeMask);
+ void UpdatePatchMesh (Mesh& mesh, int xPatch, int yPatch, int mipLevel, int edgeMask, TerrainRenderer *renderer);
+ void UpdatePatchIndices (VBO& vbo, SubMesh& subMesh, int xPatch, int yPatch, int level, int edgeMask);
+ void UpdatePatchMesh (VBO& vbo, SubMesh& subMesh, int xPatch, int yPatch, int mipLevel, int edgeMask, TerrainRenderer *renderer);
+ AABB GetBounds (int xPatch, int yPatch, int mipLevel) const;
+
+ NxHeightField* GetNxHeightField ();
+
+ typedef List< ListNode<TerrainCollider> > TerrainColliderList;
+ TerrainColliderList& GetTerrainColliders () { return m_TerrainColliders; }
+
+ ///@TODO: THIS IS STILL INCORRECT
+ Vector3f CalculateNormalSobel (int x, int y) const;
+ Vector3f CalculateNormalSobelRespectingNeighbors (int x, int y, const TerrainRenderer *renderer) const;
+
+ AABB GetBounds () const;
+
+ void AwakeFromLoad ();
+
+#if ENABLE_PHYSICS
+ PPtr<PhysicMaterial> GetPhysicMaterial() const { return m_DefaultPhysicMaterial; }
+ void SetPhysicMaterial(PPtr<PhysicMaterial> mat);
+#endif
+
+private:
+
+ TerrainData* m_TerrainData;
+ /// Raw heightmap
+ std::vector<SInt16> m_Heights;
+
+ // Precomputed error of every patch
+ std::vector<float> m_PrecomputedError;
+ // Precomputed min&max value of each terrain patch
+ std::vector<float> m_MinMaxPatchHeights;
+
+ TerrainColliderList m_TerrainColliders;
+
+ int m_Width;
+ int m_Height;
+ int m_Levels;
+ Vector3f m_Scale;
+
+ int GetPatchCountX (int level) const { return 1 << (m_Levels - level); }
+ int GetPatchCountY (int level) const { return 1 << (m_Levels - level); }
+
+ inline float GetPatchHeight (float* data, int x, int y) const
+ {
+ return data[y + x * kPatchSize];
+ }
+
+ /// Calculates the index of the patch given it's level and x, y index
+ int GetPatchIndex (int x, int y, int level) const;
+
+ float InterpolatePatchHeight (float* data, float fx, float fy) const;
+
+ float ComputeMaximumHeightError (int xPatch, int yPatch, int level) const;
+
+ void RecalculateMinMaxHeight (int xPatch, int yPatch, int mipLevel);
+ // Precompute error only on a part of the heightmap
+ // if forceHighestLod is enabled we simply set the error to infinity
+ // This casues the heightmap to be rendered at full res (Used while editing)
+ void PrecomputeError (int minX, int minY, int width, int height, bool forceHighestLod);
+
+#if ENABLE_PHYSICS
+ PPtr<PhysicMaterial> m_DefaultPhysicMaterial;
+ NxHeightField* m_NxHeightField;
+
+ void CleanupNx ();
+ void CreateNx ();
+ void UpdateNx ();
+ void BuildDesc (NxHeightFieldDesc& desc);
+
+ void RecreateShapes ();
+#endif
+};
+
+template<class TransferFunc>
+void Heightmap::Transfer (TransferFunc& transfer)
+{
+ TRANSFER(m_Heights);
+ transfer.Align();
+ TRANSFER(m_PrecomputedError);
+ TRANSFER(m_MinMaxPatchHeights);
+#if ENABLE_PHYSICS
+ TRANSFER(m_DefaultPhysicMaterial);
+#endif
+ TRANSFER(m_Width);
+ TRANSFER(m_Height);
+ TRANSFER(m_Levels);
+ TRANSFER(m_Scale);
+}
+
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/ImposterRenderTexture.cpp b/Runtime/Terrain/ImposterRenderTexture.cpp
new file mode 100644
index 0000000..7fc737b
--- /dev/null
+++ b/Runtime/Terrain/ImposterRenderTexture.cpp
@@ -0,0 +1,229 @@
+#include "UnityPrefix.h"
+#include "ImposterRenderTexture.h"
+
+#if ENABLE_TERRAIN
+
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Graphics/DrawUtil.h"
+#include "External/shaderlab/Library/FastPropertyName.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Camera/Renderqueue.h"
+
+
+#if UNITY_PS3
+# include "Runtime/GfxDevice/ps3/GfxDevicePS3.h"
+#endif
+
+const float kMinimumAngleDelta = 0.1f * kDeg2Rad;
+const float kImpostorPadding = 1.0f;
+
+ImposterRenderTexture::ImposterRenderTexture (const TreeDatabase& treeDB)
+: m_TreeDatabase(treeDB)
+, m_AngleX(std::numeric_limits<float>::infinity())
+, m_AngleY(std::numeric_limits<float>::infinity())
+, m_ImposterHeight(256)
+, m_MaxImposterSize(2048)
+, m_CameraOrientationMatrix(Matrix4x4f::identity)
+{
+ // if render textures or vertex shaders are not supported, there shall be
+ // no impostors.
+ RenderTexture::SetTemporarilyAllowIndieRenderTexture (true);
+ if (!RenderTexture::IsEnabled())
+ {
+ RenderTexture::SetTemporarilyAllowIndieRenderTexture (false);
+ m_Supported = false;
+ m_Texture = NULL;
+ m_Camera = NULL;
+ return;
+ }
+ m_Supported = true;
+
+ const std::vector<TreeDatabase::Prototype>& prototypes = m_TreeDatabase.GetPrototypes();
+
+ m_Areas.resize(prototypes.size());
+ // Calculate how many pixel width we need!
+ float totalPixelWidth = 0.0F;
+ for (int i = 0; i < prototypes.size(); ++i)
+ {
+ totalPixelWidth += m_ImposterHeight * prototypes[i].getBillboardAspect() + kImpostorPadding;
+ }
+
+ int textureWidth = std::min<int>(m_MaxImposterSize, ClosestPowerOfTwo(RoundfToIntPos(totalPixelWidth)));
+
+ float uOffset = kImpostorPadding / (float)textureWidth;
+
+ // Calculate areas
+ float runOffset = 0.0F;
+ for (int i = 0; i < prototypes.size(); ++i)
+ {
+ float width = (m_ImposterHeight * prototypes[i].getBillboardAspect()) / totalPixelWidth;
+ m_Areas[i].Set(runOffset + uOffset, 0.0F, width - uOffset - uOffset, 1.0F);
+ runOffset += width;
+ }
+
+ // Setup Render texture
+ m_Texture = NEW_OBJECT (RenderTexture);
+ m_Texture->Reset();
+
+ m_Texture->SetHideFlags(Object::kDontSave);
+ m_Texture->SetWidth(textureWidth);
+ m_Texture->SetHeight(m_ImposterHeight);
+ m_Texture->SetColorFormat(kRTFormatARGB32);
+ m_Texture->SetDepthFormat(kDepthFormat16);
+ m_Texture->SetName("Tree Imposter Texture");
+ m_Texture->SetMipMap(true);
+ m_Texture->SetMipMapBias(-1);
+
+ m_Texture->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+
+ // Setup camera
+ GameObject* cameraGO = &CreateGameObjectWithHideFlags ("Imposter Camera", true, Object::kHideAndDontSave, "Camera", NULL);
+ m_Camera = &cameraGO->GetComponent(Camera);
+ m_Camera->SetTargetTexture(m_Texture);
+ m_Camera->SetClearFlags(Camera::kSolidColor);
+ m_Camera->SetBackgroundColor(ColorRGBAf(.2f,.2f,.2f,0));
+ m_Camera->SetOrthographic(true);
+ m_Camera->SetCullingMask(0);
+ m_Camera->SetEnabled(false);
+
+#if UNITY_PS3
+ ((GfxDevicePS3&)GetGfxDevice()).RegisterSurfaceLostCallback(&InvalidateRenderTexture, this);
+#endif
+
+ RenderTexture::SetTemporarilyAllowIndieRenderTexture (false);
+}
+
+ImposterRenderTexture::~ImposterRenderTexture()
+{
+#if UNITY_PS3
+ ((GfxDevicePS3&)GetGfxDevice()).UnregisterSurfaceLostCallback(&InvalidateRenderTexture, this);
+#endif
+ if (m_Camera != NULL)
+ DestroyObjectHighLevel(&m_Camera->GetGameObject());
+ if (m_Texture != NULL)
+ DestroyObjectHighLevel(m_Texture);
+}
+
+void ImposterRenderTexture::UpdateImposters (const Camera& mainCamera)
+{
+ // Only update if we really have to because the viewing angle is very different
+ const Transform& transform = mainCamera.GetComponent (Transform);
+ const Vector3f& cameraEuler = QuaternionToEuler(transform.GetRotation());
+ if (m_Texture->IsCreated())
+ {
+ if (m_AngleX != std::numeric_limits<float>::infinity() && std::abs(DeltaAngleRad (m_AngleX, cameraEuler.x)) < kMinimumAngleDelta &&
+ m_AngleY != std::numeric_limits<float>::infinity() && std::abs(DeltaAngleRad (m_AngleY, cameraEuler.y)) < kMinimumAngleDelta)
+ return;
+ }
+
+ m_AngleX = cameraEuler.x;
+ m_AngleY = cameraEuler.y;
+
+ m_Camera->GetComponent (Transform).SetLocalEulerAngles(Vector3f (cameraEuler.x, cameraEuler.y, 0) * Rad2Deg(1));
+ m_CameraOrientationMatrix = m_Camera->GetCameraToWorldMatrix();
+
+ Camera& oldCamera = GetCurrentCamera();
+
+ // Clear the whole render texture
+ m_Camera->SetNormalizedViewportRect(Rectf (0, 0, 1, 1));
+ m_Camera->SetClearFlags(Camera::kSolidColor);
+
+ m_Camera->StandaloneRender(Camera::kRenderFlagDontRestoreRenderState | Camera::kRenderFlagSetRenderTarget, NULL, "");
+
+ const std::vector<TreeDatabase::Prototype>& prototypes = m_TreeDatabase.GetPrototypes();
+ for (int i = 0; i < prototypes.size(); ++i)
+ {
+ const Rectf& rect = m_Areas[i];//new Rect (offset , 0.0F, width, 1.0F);
+ UpdateImposter(rect, prototypes[i]);
+ }
+
+ oldCamera.StandaloneSetup();
+}
+
+// angleFactor - used for:
+// 1) non-uniform scale compensation
+// 2) blending between vertical (viewing from front) and horizontal (viewing from above/below) billboard modes
+// offsetFactor - used for offsetting billboard from the ground when billboard is in horizontal mode
+//
+// See TerrainBillboardTree in TerrainEngine.cginc for more detailed explanation
+void ImposterRenderTexture::GetBillboardParams(float& angleFactor, float& offsetFactor) const
+{
+ float angles = Rad2Deg(m_AngleX);
+ angles = angles <= 90 ? angles : angles - 360;
+
+ AssertMsg(angles >= -90 && angles <= 90, "Invalid angle: %f", angles);
+
+ angleFactor = 1 - cos(Deg2Rad(angles));
+
+ {
+ // calculate modeFactor
+ const float kLimitAngle = 40;
+
+ float factor = fabsf(angles);
+ factor = factor < kLimitAngle ? 0 : SmoothStep(0, 1, (factor - kLimitAngle) / (90 - kLimitAngle));
+
+ // we never want to use bottom-center mode completely, because it can cause intersection
+ // of billboard with terrain, so we just raise it a bit all the time
+ offsetFactor = std::max(0.1f, factor);
+ }
+}
+
+void ImposterRenderTexture::UpdateImposter (const Rectf& rect, const TreeDatabase::Prototype& prototype)
+{
+ if (prototype.imposterMaterials.empty()
+ || !prototype.mesh.IsValid())
+ {
+ return;
+ }
+
+ {
+ Transform& transform = m_Camera->GetComponent (Transform);
+ // Setup camera location
+ transform.SetPosition(Vector3f (0, prototype.getCenterOffset(), 0));
+ // Just move far away enough to get the whole tree into the view. (How far doesnt matter since it is orthographic projection)
+ transform.SetPosition(transform.GetPosition() + transform.TransformDirection (-Vector3f::zAxis * (prototype.treeHeight + prototype.treeWidth) * 2));
+ }
+
+ // Setup camera rect
+ m_Camera->SetClearFlags(Camera::kDontClear);
+ m_Camera->SetNormalizedViewportRect(rect);
+
+ m_Camera->SetAspect(prototype.getBillboardAspect());
+ m_Camera->SetOrthographicSize(prototype.getBillboardHeight() * 0.5F);
+
+ // Setup render target
+ m_Camera->StandaloneRender(Camera::kRenderFlagDontRestoreRenderState | Camera::kRenderFlagSetRenderTarget, NULL, "");
+
+ const ShaderLab::FastPropertyName colorProperty = ShaderLab::Property("_Color");
+ const ShaderLab::FastPropertyName halfOverCutoffProperty = ShaderLab::Property("_HalfOverCutoff");
+ const ShaderLab::FastPropertyName terrainEngineBendTreeProperty = ShaderLab::Property("_TerrainEngineBendTree");
+
+ for (int m=0; m<prototype.imposterMaterials.size(); ++m)
+ {
+ const ColorRGBAf& color = prototype.originalMaterialColors[m];
+ float cutoff = prototype.inverseAlphaCutoff[m];
+ Material* mat = prototype.imposterMaterials[m];
+ mat->SetColor(colorProperty, color);
+ mat->SetFloat(halfOverCutoffProperty, cutoff);
+ mat->SetMatrix(terrainEngineBendTreeProperty, Matrix4x4f::identity);
+ for (int p=0; p<mat->GetPassCount(); ++p)
+ {
+ if (CheckShouldRenderPass (p, *mat))
+ {
+ const ChannelAssigns* channels = mat->SetPass(p);
+ if (channels)
+ {
+ DrawUtil::DrawMesh (*channels, *prototype.mesh, Vector3f::zero, Quaternionf::identity(), m);
+ }
+ }
+ }
+ }
+}
+
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/ImposterRenderTexture.h b/Runtime/Terrain/ImposterRenderTexture.h
new file mode 100644
index 0000000..de7714f
--- /dev/null
+++ b/Runtime/Terrain/ImposterRenderTexture.h
@@ -0,0 +1,65 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_TERRAIN
+
+#include "TreeDatabase.h"
+#include "Runtime/Math/Rect.h"
+#include <vector>
+
+
+class RenderTexture;
+class Camera;
+
+
+class ImposterRenderTexture
+{
+public:
+ ImposterRenderTexture(const TreeDatabase& treeDB);
+ ~ImposterRenderTexture();
+
+ const Rectf& GetArea(int index) const {
+ return m_Areas[index];
+ }
+
+ RenderTexture* GetTexture() const { return m_Texture; }
+ bool GetSupported() const { return m_Supported; }
+
+ void UpdateImposters(const Camera& mainCamera);
+
+ void InvalidateAngles ()
+ {
+ m_AngleX = std::numeric_limits<float>::infinity();
+ m_AngleY = std::numeric_limits<float>::infinity();
+ }
+
+ const Matrix4x4f& getCameraOrientation() const { return m_CameraOrientationMatrix; }
+
+ void GetBillboardParams(float& angleFactor, float& offsetFactor) const;
+private:
+ void UpdateImposter(const Rectf& rect, const TreeDatabase::Prototype& prototype);
+
+private:
+ const TreeDatabase& m_TreeDatabase;
+
+ std::vector<Rectf> m_Areas;
+ Camera* m_Camera;
+ RenderTexture* m_Texture;
+ bool m_Supported;
+
+ float m_AngleX;
+ float m_AngleY;
+
+ int m_ImposterHeight;
+ int m_MaxImposterSize;
+
+ Matrix4x4f m_CameraOrientationMatrix;
+
+ static void InvalidateRenderTexture(void* userData) {
+ ((ImposterRenderTexture*)userData)->InvalidateAngles();
+ }
+};
+
+#endif // ENABLE_TERRAIN
+
+
diff --git a/Runtime/Terrain/PerlinNoise.cpp b/Runtime/Terrain/PerlinNoise.cpp
new file mode 100644
index 0000000..6507f03
--- /dev/null
+++ b/Runtime/Terrain/PerlinNoise.cpp
@@ -0,0 +1,73 @@
+#include "UnityPrefix.h"
+#include "PerlinNoise.h"
+
+inline static float fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }
+inline static float lerp(float t, float a, float b) { return a + t * (b - a); }
+inline static float grad(int hash, float x, float y, float z) {
+ int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE
+ float u = h<8 ? x : y, // INTO 12 GRADIENT DIRECTIONS.
+ v = h<4 ? y : h==12||h==14 ? x : z;
+ return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
+}
+
+inline static float grad2(int hash, float x, float y) {
+ int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE
+ float u = h < 8 ? x : y, // INTO 12 GRADIENT DIRECTIONS.
+ v = h < 4 ? y : h==12 || h==14 ? x : 0;
+ return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
+}
+
+static int p[] =
+{
+ 151,160,137,91,90,15,
+ 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
+ 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
+ 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
+ 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
+ 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
+ 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
+ 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
+ 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
+ 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
+ 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
+ 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
+ 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,
+ 151,160,137,91,90,15,
+ 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
+ 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
+ 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
+ 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
+ 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
+ 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
+ 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
+ 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
+ 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
+ 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
+ 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
+ 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
+};
+
+
+
+float PerlinNoise::Noise(float x, float y) {
+ x = Abs(x);
+ y = Abs(y);
+
+ int floorX = int(x);
+ int floorY = int(y);
+
+ int X = floorX & 255; // FIND UNIT CUBE THAT
+ int Y = floorY & 255; // CONTAINS POINT.
+ x -= floorX; // FIND RELATIVE X,Y,Z
+ y -= floorY; // OF POINT IN CUBE.
+ float u = fade(std::min(x, 1.0f)); // COMPUTE FADE CURVES
+ float v = fade(std::min(y, 1.0f)); // FOR EACH OF X,Y,Z.
+ int A = p[X ]+Y, AA = p[A], AB = p[A+1], // HASH COORDINATES OF
+ B = p[X+1]+Y, BA = p[B], BB = p[B+1]; // THE 8 CUBE CORNERS,
+
+ float res = lerp(v, lerp(u, grad2(p[AA ], x , y ), // AND ADD
+ grad2(p[BA ], x-1, y )), // BLENDED
+ lerp(u, grad2(p[AB ], x , y-1 ), // RESULTS
+ grad2(p[BB ], x-1, y-1 )));// FROM 8
+ return res;
+}
diff --git a/Runtime/Terrain/PerlinNoise.h b/Runtime/Terrain/PerlinNoise.h
new file mode 100644
index 0000000..7a9a978
--- /dev/null
+++ b/Runtime/Terrain/PerlinNoise.h
@@ -0,0 +1,16 @@
+#ifndef PERLINNOISE_H
+#define PERLINNOISE_H
+class PerlinNoise {
+public:
+ static float Noise(float x, float y);
+ // Returns noise between 0 - 1
+ inline static float NoiseNormalized(float x, float y)
+ {
+ //-0.697 - 0.795 + 0.697
+ float value = Noise(x, y);
+ value = (value + 0.69F) / (0.793F + 0.69F);
+ return value;
+ }
+};
+
+#endif
diff --git a/Runtime/Terrain/QuadTreeNodeRenderer.cpp b/Runtime/Terrain/QuadTreeNodeRenderer.cpp
new file mode 100644
index 0000000..c2eca30
--- /dev/null
+++ b/Runtime/Terrain/QuadTreeNodeRenderer.cpp
@@ -0,0 +1,124 @@
+#include "UnityPrefix.h"
+#include "QuadTreeNodeRenderer.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Terrain/TerrainRenderer.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Camera/UnityScene.h"
+#if UNITY_PS3
+# include "Runtime/GfxDevice/ps3/GfxGCMVBO.h"
+#endif
+
+DEFINE_POOLED_ALLOC(QuadTreeNodeRenderer, 64 * 1024);
+
+void QuadTreeNodeRenderer::StaticInitialize()
+{
+ STATIC_INITIALIZE_POOL(QuadTreeNodeRenderer);
+}
+
+void QuadTreeNodeRenderer::StaticDestroy()
+{
+ STATIC_DESTROY_POOL(QuadTreeNodeRenderer);
+}
+
+static RegisterRuntimeInitializeAndCleanup s_QuadTreeNodeRendererCallbacks(QuadTreeNodeRenderer::StaticInitialize, QuadTreeNodeRenderer::StaticDestroy);
+
+#ifdef TERRAIN_PREFER_VBO_OVER_MESH
+
+QuadTreeNodeRenderer::QuadTreeNodeRenderer()
+: m_TerrainRenderer(NULL)
+, m_QuadTreeNode(NULL)
+{
+}
+
+void QuadTreeNodeRenderer::Initialize(const Matrix4x4f& matrix, const AABB& localAABB, PPtr<Material> material, int layer, bool castShadows, bool receiveShadows)
+{
+ MeshIntermediateRenderer::Initialize(matrix, NULL, localAABB, material, layer, castShadows, receiveShadows, 0);
+}
+
+QuadTreeNodeRenderer::~QuadTreeNodeRenderer()
+{
+}
+
+void QuadTreeNodeRenderer::Setup(TerrainRenderer* terrainRenderer, QuadTreeNode* quadTreeNode)
+{
+ AssertIf(terrainRenderer == NULL || quadTreeNode == NULL);
+
+ m_TerrainRenderer = terrainRenderer;
+ m_QuadTreeNode = quadTreeNode;
+}
+
+
+void QuadTreeNodeRenderer::Render( int /*subsetIndex*/, const ChannelAssigns& channels )
+{
+ if (m_TerrainRenderer == NULL || m_QuadTreeNode == NULL)
+ {
+ return;
+ }
+
+ if (m_QuadTreeNode->vbo == NULL)
+ {
+ m_QuadTreeNode->vbo = m_TerrainRenderer->CreateVBO();
+
+ // these two flags shall always be set if a new VBO is requested
+ AssertIf(!m_QuadTreeNode->updateMesh || !m_QuadTreeNode->updateIndices);
+ }
+
+ if (m_QuadTreeNode->updateMesh)
+ {
+ m_TerrainRenderer->GetTerrainData()->GetHeightmap().UpdatePatchMesh(
+ *m_QuadTreeNode->vbo,
+ m_QuadTreeNode->subMesh,
+ m_QuadTreeNode->x,
+ m_QuadTreeNode->y,
+ m_QuadTreeNode->level,
+ m_QuadTreeNode->edgeMask, m_TerrainRenderer);
+ m_QuadTreeNode->updateMesh = false;
+ }
+
+ if (m_QuadTreeNode->updateIndices)
+ {
+ m_TerrainRenderer->GetTerrainData()->GetHeightmap().UpdatePatchIndices(
+ *m_QuadTreeNode->vbo,
+ m_QuadTreeNode->subMesh,
+ m_QuadTreeNode->x,
+ m_QuadTreeNode->y,
+ m_QuadTreeNode->level,
+ m_QuadTreeNode->edgeMask);
+ m_QuadTreeNode->updateIndices = false;
+ }
+
+ if (m_CustomProperties)
+ {
+ GetGfxDevice().SetMaterialProperties (*m_CustomProperties);
+ }
+
+ SubMesh& subMesh = m_QuadTreeNode->subMesh;
+
+ //PROFILER_AUTO(gDrawMeshVBOProfile, &mesh)
+#if UNITY_PS3
+ GfxGCMVBO* gcmVBO = static_cast<GfxGCMVBO*>(m_QuadTreeNode->vbo);
+ gcmVBO->DrawSubmesh(channels, 0, &subMesh);
+#else
+ m_QuadTreeNode->vbo->DrawVBO(channels, subMesh.firstByte, subMesh.indexCount, subMesh.topology, subMesh.firstVertex, subMesh.vertexCount);
+#endif
+ GPU_TIMESTAMP();
+}
+
+QuadTreeNodeRenderer* AddQuadTreeNodeRenderer( const Matrix4x4f& matrix, const AABB& localAABB, PPtr<Material> material, int layer, bool castShadows, bool receiveShadows, Camera* camera )
+{
+ QuadTreeNodeRenderer* renderer = new QuadTreeNodeRenderer ();
+ renderer->Initialize(matrix, localAABB, material, layer, castShadows, receiveShadows);
+
+ IntermediateRenderers* renderers;
+ if (camera != NULL)
+ renderers = &camera->GetIntermediateRenderers();
+ else
+ renderers = &GetScene().GetIntermediateRenderers();
+ renderers->Add(renderer, layer);
+
+ return renderer;
+}
+
+#endif
diff --git a/Runtime/Terrain/QuadTreeNodeRenderer.h b/Runtime/Terrain/QuadTreeNodeRenderer.h
new file mode 100644
index 0000000..7f70ee7
--- /dev/null
+++ b/Runtime/Terrain/QuadTreeNodeRenderer.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "Runtime/Camera/IntermediateRenderer.h"
+
+struct QuadTreeNode;
+class Camera;
+class TerrainRenderer;
+
+class QuadTreeNodeRenderer : public MeshIntermediateRenderer
+{
+public:
+ QuadTreeNodeRenderer ();
+
+ void Initialize (const Matrix4x4f& matrix, const AABB& localAABB, PPtr<Material> material, int layer, bool castShadows, bool receiveShadows);
+
+ virtual ~QuadTreeNodeRenderer();
+
+ void Setup(TerrainRenderer* terrainRenderer, QuadTreeNode* node);
+
+ virtual void Render( int materialIndex, const ChannelAssigns& channels );
+
+ static void StaticInitialize ();
+ static void StaticDestroy ();
+
+protected:
+
+
+private:
+ // Note: not using per-frame linear allocator, because in the editor
+ // it can render multiple frames using single player loop run (e.g. when editor is paused).
+ // Clearing per-frame data and then trying to use it later leads to Bad Things.
+ DECLARE_POOLED_ALLOC(QuadTreeNodeRenderer);
+
+ TerrainRenderer* m_TerrainRenderer; // It's safe to store raw pointer to the TerrainRender object here
+ // because TerrainRenderers are deleted before Render() happens
+ // and the life-time of TerrainVBORenderer object is only one frame.
+ QuadTreeNode* m_QuadTreeNode;
+};
+
+QuadTreeNodeRenderer* AddQuadTreeNodeRenderer( const Matrix4x4f& matrix, const AABB& localAABB, PPtr<Material> material, int layer, bool castShadows, bool receiveShadows, Camera* camera );
diff --git a/Runtime/Terrain/ScriptBindings/TerrainDataBindings.txt b/Runtime/Terrain/ScriptBindings/TerrainDataBindings.txt
new file mode 100644
index 0000000..c14d84a
--- /dev/null
+++ b/Runtime/Terrain/ScriptBindings/TerrainDataBindings.txt
@@ -0,0 +1,406 @@
+C++RAW
+#include "UnityPrefix.h"
+#include "Runtime/Terrain/Heightmap.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Terrain/DetailDatabase.h"
+#include "Runtime/Terrain/SplatDatabase.h"
+#include "Runtime/Terrain/TerrainData.h"
+#include "Runtime/Terrain/TerrainInstance.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Terrain/TerrainRenderer.h"
+#include "Runtime/Camera/Light.h"
+#include "Runtime/Terrain/DetailRenderer.h"
+#include "Runtime/Terrain/ImposterRenderTexture.h"
+#include "Runtime/Terrain/TreeRenderer.h"
+#include "Runtime/Terrain/Wind.h"
+#include "Runtime/Terrain/Tree.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+
+using namespace Unity;
+using namespace std;
+
+CSRAW
+
+#if ENABLE_TERRAIN
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace UnityEngine
+{
+
+CSRAW [StructLayout (LayoutKind.Sequential)]
+CONDITIONAL ENABLE_TERRAIN
+CLASS TreePrototype
+ CSRAW internal GameObject m_Prefab;
+ CSRAW internal float m_BendFactor;
+
+ CSRAW public GameObject prefab { get { return m_Prefab; } set { m_Prefab = value; } }
+
+ CSRAW public float bendFactor { get { return m_BendFactor; } set { m_BendFactor = value; } }
+END
+
+ENUM DetailRenderMode
+ GrassBillboard = 0,
+ VertexLit = 1,
+ Grass = 2
+END
+
+CSRAW [StructLayout (LayoutKind.Sequential)]
+CONDITIONAL ENABLE_TERRAIN
+CLASS DetailPrototype
+ CSRAW GameObject m_Prototype = null;
+ CSRAW Texture2D m_PrototypeTexture = null;
+ CSRAW Color m_HealthyColor = new Color (67/255F, 249/255F, 42/255F, 1 );
+ CSRAW Color m_DryColor = new Color (205/255.0F, 188/255.0F, 26/255.0F, 1.0F ) ;
+ CSRAW float m_MinWidth = 1.0F;
+ CSRAW float m_MaxWidth = 2.0F;
+ CSRAW float m_MinHeight = 1F;
+ CSRAW float m_MaxHeight = 2F;
+ CSRAW float m_NoiseSpread = 0.1F;
+ CSRAW float m_BendFactor = 0.1F;
+ CSRAW int m_RenderMode = 2;
+ CSRAW int m_UsePrototypeMesh = 0;
+
+ CSRAW public GameObject prototype { get { return m_Prototype; } set { m_Prototype = value; } }
+
+ CSRAW public Texture2D prototypeTexture { get { return m_PrototypeTexture; } set { m_PrototypeTexture = value; } }
+
+ CSRAW public float minWidth { get { return m_MinWidth; } set { m_MinWidth = value; } }
+
+ CSRAW public float maxWidth { get { return m_MaxWidth; } set { m_MaxWidth = value; } }
+
+ CSRAW public float minHeight { get { return m_MinHeight; } set { m_MinHeight = value; } }
+
+ CSRAW public float maxHeight { get { return m_MaxHeight; } set { m_MaxHeight = value; } }
+
+ CSRAW public float noiseSpread { get { return m_NoiseSpread; } set { m_NoiseSpread = value; } }
+
+ CSRAW public float bendFactor { get { return m_BendFactor; } set { m_BendFactor = value; } }
+
+ CSRAW public Color healthyColor { get { return m_HealthyColor; } set { m_HealthyColor = value; } }
+
+ CSRAW public Color dryColor { get { return m_DryColor; } set { m_DryColor = value; } }
+
+ CSRAW public DetailRenderMode renderMode { get { return (DetailRenderMode)m_RenderMode; } set { m_RenderMode = (int)value; } }
+
+ CSRAW public bool usePrototypeMesh { get { return m_UsePrototypeMesh != 0; } set { m_UsePrototypeMesh = value ? 1 : 0; } }
+END
+
+CSRAW [StructLayout (LayoutKind.Sequential)]
+CONDITIONAL ENABLE_TERRAIN
+CLASS SplatPrototype
+ CSRAW Texture2D m_Texture;
+ CSRAW Texture2D m_NormalMap;
+ CSRAW Vector2 m_TileSize = new Vector2 (15,15);
+ CSRAW Vector2 m_TileOffset = new Vector2 (0, 0);
+
+ CSRAW public Texture2D texture { get { return m_Texture; } set { m_Texture = value; } }
+
+ CSRAW public Texture2D normalMap { get { return m_NormalMap; } set { m_NormalMap = value; } }
+
+ CSRAW public Vector2 tileSize { get { return m_TileSize; } set { m_TileSize = value; } }
+
+ CSRAW public Vector2 tileOffset { get { return m_TileOffset; } set { m_TileOffset = value; } }
+END
+
+
+CONDITIONAL ENABLE_TERRAIN
+STRUCT TreeInstance
+ CSRAW Vector3 m_Position;
+ CSRAW float m_WidthScale;
+ CSRAW float m_HeightScale;
+ CSRAW Color32 m_Color;
+ CSRAW Color32 m_LightmapColor;
+ CSRAW int m_Index;
+ CSRAW float m_TemporaryDistance;
+
+ public Vector3 position { get { return m_Position; } set { m_Position = value; } }
+
+ public float widthScale { get { return m_WidthScale; } set { m_WidthScale = value; } }
+
+ public float heightScale { get { return m_HeightScale; } set { m_HeightScale = value; } }
+
+ public Color color { get { return m_Color; } set { m_Color = value; } }
+
+ public Color lightmapColor { get { return m_LightmapColor; } set { m_LightmapColor = value; } }
+
+ public int prototypeIndex { get { return m_Index; } set { m_Index = value; } }
+
+ internal float temporaryDistance { get { return m_TemporaryDistance; } set { m_TemporaryDistance = value; } }
+END
+
+CONDITIONAL ENABLE_TERRAIN
+CLASS TerrainData : Object
+ CSRAW public TerrainData ()
+ {
+ Internal_Create(this);
+ }
+
+ CUSTOM internal void Internal_Create ([Writable]TerrainData terrainData)
+ {
+ TerrainData* td = NEW_OBJECT (TerrainData);
+ td->Reset();
+
+ //this is only for ensuring, that HeightMap initialized properly before someone uses TerrainData
+ if (td)
+ td->GetHeightmap().SetResolution(0);
+
+ Scripting::ConnectScriptingWrapperToObject (terrainData.GetScriptingObject(), td);
+ td->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+ }
+
+ CUSTOM internal bool HasUser (GameObject user) { return self->HasUser (user); }
+ CUSTOM internal void AddUser (GameObject user) { self->AddUser (user); }
+ CUSTOM internal void RemoveUser (GameObject user) { self->RemoveUser (user); }
+
+ // HEIGHTMAP INTERFACE
+ C++RAW #define GETHEIGHT (&(self->GetHeightmap()))
+
+ // =============================================
+ C++RAW #define GETDETAIL (&(self->GetDetailDatabase()))
+
+ C++RAW #define GETTREEDATABASE (&(self->GetTreeDatabase()))
+
+ // PhysicMaterial the terrain.
+ CONDITIONAL ENABLE_PHYSICS
+ CUSTOM_PROP PhysicMaterial physicMaterial { return Scripting::ScriptingWrapperFor(GETHEIGHT->GetPhysicMaterial ()); } { GETHEIGHT->SetPhysicMaterial (value); }
+ CUSTOM_PROP int heightmapWidth { return GETHEIGHT->GetWidth (); }
+ CUSTOM_PROP int heightmapHeight { return GETHEIGHT->GetHeight (); }
+
+ CUSTOM_PROP int heightmapResolution { return GETHEIGHT->GetResolution (); } { GETHEIGHT->SetResolution (value); }
+
+ CUSTOM_PROP Vector3 heightmapScale { return GETHEIGHT->GetScale (); }
+
+ CUSTOM_PROP Vector3 size { return GETHEIGHT->GetSize (); } { GETHEIGHT->SetSize (value); }
+
+ CUSTOM float GetHeight (int x, int y) { return GETHEIGHT->GetHeight (x,y); }
+ CUSTOM float GetInterpolatedHeight (float x, float y) { return GETHEIGHT->GetInterpolatedHeight (x,y); }
+
+ CUSTOM public float[,] GetHeights (int xBase, int yBase, int width, int height) {
+
+ if(xBase < 0 || yBase < 0 || xBase+width > GETHEIGHT->GetWidth() || yBase+height > GETHEIGHT->GetHeight ())
+ {
+ Scripting::RaiseMonoException ("Trying to access out-of-bounds terrain height information.");
+ return SCRIPTING_NULL;
+ }
+
+ ScriptingArrayPtr map = CreateScriptingArray2D<float> (MONO_COMMON.floatSingle, height, width);
+ GETHEIGHT->GetHeights (xBase, yBase, width, height, &Scripting::GetScriptingArrayElement<float>(map, 0));
+ return map;
+ }
+
+ CSRAW public void SetHeights (int xBase, int yBase, float[,] heights) {
+ if (heights == null)
+ {
+ throw new System.NullReferenceException ();
+ }
+ if (xBase+heights.GetLength(1) > heightmapWidth || xBase < 0 || yBase < 0 || yBase+heights.GetLength(0) > heightmapHeight)
+ {
+ throw new System.Exception (UnityString.Format ("X or Y base out of bounds. Setting up to {0}x{1} while map size is {2}x{3}", xBase+heights.GetLength(1), yBase+heights.GetLength(0), heightmapWidth, heightmapHeight));
+ }
+
+ Internal_SetHeights (xBase, yBase, heights.GetLength(1), heights.GetLength(0), heights);
+ }
+
+ CUSTOM private void Internal_SetHeights (int xBase, int yBase, int width, int height, float[,] heights)
+ {
+ GETHEIGHT->SetHeights(xBase, yBase, width, height, &Scripting::GetScriptingArrayElement<float>(heights, 0), false);
+ GETTREEDATABASE->RecalculateTreePositions();
+ }
+
+ CUSTOM private void Internal_SetHeightsDelayLOD (int xBase, int yBase, int width, int height, float[,] heights)
+ {
+ GETHEIGHT->SetHeights(xBase, yBase, width, height, &Scripting::GetScriptingArrayElement<float>(heights, 0), true);
+ }
+
+ CSRAW internal void SetHeightsDelayLOD (int xBase, int yBase, float[,] heights)
+ {
+ Internal_SetHeightsDelayLOD (xBase, yBase, heights.GetLength(1), heights.GetLength(0), heights);
+ }
+
+ CUSTOM float GetSteepness (float x, float y) { return GETHEIGHT->GetSteepness (x,y); }
+
+ CUSTOM Vector3 GetInterpolatedNormal (float x, float y) { return GETHEIGHT->GetInterpolatedNormal (x,y); }
+
+
+ CUSTOM internal int GetAdjustedSize (int size) { return GETHEIGHT->GetAdjustedSize (size); }
+
+ C++RAW #undef GETHEIGHT
+
+ CUSTOM_PROP float wavingGrassStrength { return GETDETAIL->GetWavingGrassStrength(); } { GETDETAIL->SetWavingGrassStrength (value); self->SetDirty(); }
+ CUSTOM_PROP float wavingGrassAmount { return GETDETAIL->GetWavingGrassAmount(); } { GETDETAIL-> SetWavingGrassAmount (value); self->SetDirty(); }
+ CUSTOM_PROP float wavingGrassSpeed { return GETDETAIL->GetWavingGrassSpeed(); } { GETDETAIL-> SetWavingGrassSpeed (value); self->SetDirty(); }
+ CUSTOM_PROP Color wavingGrassTint { return GETDETAIL->GetWavingGrassTint(); } { GETDETAIL-> SetWavingGrassTint (value); self->SetDirty(); }
+
+ CUSTOM_PROP int detailWidth { return GETDETAIL->GetWidth (); }
+
+ CUSTOM_PROP int detailHeight { return GETDETAIL->GetHeight (); }
+
+ CUSTOM void SetDetailResolution (int detailResolution, int resolutionPerPatch)
+ {
+ GETDETAIL->SetDetailResolution(detailResolution, resolutionPerPatch);
+ }
+
+ CUSTOM_PROP int detailResolution { return GETDETAIL->GetResolution (); }
+
+ CUSTOM_PROP internal int detailResolutionPerPatch { return GETDETAIL->GetResolutionPerPatch (); }
+
+ CUSTOM internal void ResetDirtyDetails () { GETDETAIL->ResetDirtyDetails (); }
+
+ CUSTOM void RefreshPrototypes ()
+ {
+ GETDETAIL->RefreshPrototypes ();
+ GETTREEDATABASE->RefreshPrototypes ();
+ }
+
+ CUSTOM_PROP DetailPrototype[] detailPrototypes
+ {
+ return VectorToScriptingClassArray<DetailPrototype, MonoDetailPrototype> (GETDETAIL->GetDetailPrototypes(), MONO_COMMON.detailPrototype, DetailPrototypeToMono);
+ }
+ {
+ GETDETAIL->SetDetailPrototypes (ScriptingClassArrayToVector<DetailPrototype, MonoDetailPrototype> (value, DetailPrototypeToCpp));
+ }
+
+ CUSTOM int[] GetSupportedLayers (int xBase, int yBase, int totalWidth, int totalHeight) {
+ int size = GETDETAIL->GetSupportedLayers (xBase, yBase, totalWidth, totalHeight, NULL); // Get the count of layers
+ ScriptingArrayPtr arr = CreateScriptingArray<int>(MONO_COMMON.int_32, size);
+ GETDETAIL->GetSupportedLayers (xBase, yBase, totalWidth, totalHeight, &Scripting::GetScriptingArrayElement<int> (arr, 0));
+ return arr;
+ }
+
+ CUSTOM int[,] GetDetailLayer (int xBase, int yBase, int width, int height, int layer) {
+ ScriptingArrayPtr map = CreateScriptingArray2D<int> (MONO_COMMON.int_32, height, width);
+ GETDETAIL->GetLayer (xBase, yBase, width, height, layer, &Scripting::GetScriptingArrayElement<int> (map, 0));
+ return map;
+ }
+
+ CSRAW public void SetDetailLayer (int xBase, int yBase, int layer, int[,] details) {
+ Internal_SetDetailLayer (xBase, yBase, details.GetLength(1), details.GetLength(0), layer, details);
+ }
+
+ CUSTOM private void Internal_SetDetailLayer (int xBase, int yBase, int totalWidth, int totalHeight, int detailIndex, int[,] data)
+ {
+ GETDETAIL->SetLayer (xBase, yBase, totalWidth, totalHeight, detailIndex, &Scripting::GetScriptingArrayElement<int> (data, 0));
+ }
+
+ CUSTOM_PROP TreeInstance[] treeInstances
+ {
+ return CreateScriptingArray(&GETTREEDATABASE->GetInstances()[0], GETTREEDATABASE->GetInstances().size(), MONO_COMMON.treeInstance);
+ }
+ {
+ Scripting::RaiseIfNull((ScriptingObjectPtr)value);
+ TreeInstance *first = &Scripting::GetScriptingArrayElement<TreeInstance> (value, 0);
+ GETTREEDATABASE->GetInstances().assign (first, first + GetScriptingArraySize(value));
+ GETTREEDATABASE->UpdateTreeInstances();
+ }
+ CUSTOM_PROP TreePrototype[] treePrototypes
+ { return VectorToScriptingClassArray<TreePrototype, MonoTreePrototype> (GETTREEDATABASE->GetTreePrototypes(), MONO_COMMON.treePrototype, TreePrototypeToMono); }
+ { GETTREEDATABASE->SetTreePrototypes (ScriptingClassArrayToVector<TreePrototype, MonoTreePrototype> (value, TreePrototypeToCpp)); }
+
+ CUSTOM internal void RemoveTreePrototype (int index)
+ {
+ GETTREEDATABASE->RemoveTreePrototype (index);
+ }
+
+ CUSTOM internal void RecalculateTreePositions ()
+ {
+ GETTREEDATABASE->RecalculateTreePositions ();
+ }
+
+ CUSTOM internal void RemoveDetailPrototype (int index)
+ {
+ GETDETAIL->RemoveDetailPrototype (index);
+ }
+
+ C++RAW #undef GETDETAIL
+
+
+ // OLD SPLAT DATABASE
+ // =============================================
+ C++RAW #define GETSPLAT (&(self->GetSplatDatabase()))
+
+ CUSTOM_PROP int alphamapLayers { return GETSPLAT->GetDepth(); }
+ CUSTOM public float[,,] GetAlphamaps (int x, int y, int width, int height) {
+ ScriptingArrayPtr map = CreateScriptingArray3D<float> (MONO_COMMON.floatSingle, height, width, GETSPLAT->GetDepth ());
+ GETSPLAT->GetAlphamaps (x, y, width, height, &Scripting::GetScriptingArrayElement<float>(map, 0));
+ return map;
+ }
+
+ CUSTOM_PROP int alphamapResolution { return GETSPLAT->GetAlphamapResolution(); } { return GETSPLAT->SetAlphamapResolution(value); }
+ CUSTOM_PROP int alphamapWidth { return GETSPLAT->GetAlphamapResolution(); }
+ CUSTOM_PROP int alphamapHeight { return GETSPLAT->GetAlphamapResolution(); }
+ CUSTOM_PROP int baseMapResolution { return GETSPLAT->GetBaseMapResolution(); } { return GETSPLAT->SetBaseMapResolution(value); }
+
+ CSRAW public void SetAlphamaps (int x, int y, float[,,] map)
+ {
+ if (map.GetLength(2) != alphamapLayers) {
+ throw new System.Exception (UnityString.Format ("Float array size wrong (layers should be {0})", alphamapLayers));
+ }
+ // TODO: crop the map or throw if outside,
+
+ Internal_SetAlphamaps (x,y, map.GetLength(1), map.GetLength(0), map);
+ }
+ CUSTOM private void Internal_SetAlphamaps (int x, int y, int width, int height, float[,,] map)
+ {
+ GETSPLAT->SetAlphamaps (x, y, width, height, &Scripting::GetScriptingArrayElement<float>(map, 0));
+ }
+
+ CUSTOM internal void RecalculateBasemapIfDirty()
+ {
+ GETSPLAT->RecalculateBasemapIfDirty();
+ }
+
+ CUSTOM internal void SetBasemapDirty(bool dirty)
+ {
+ GETSPLAT->SetBasemapDirty(dirty);
+ }
+
+ CUSTOM private Texture2D GetAlphamapTexture(int index) {
+ return Scripting::ScriptingWrapperFor (GETSPLAT->GetAlphaTexture(index));
+ }
+
+ CUSTOM_PROP private int alphamapTextureCount {
+ return GETSPLAT->GetAlphaTextureCount();
+ }
+
+ CSRAW internal Texture2D[] alphamapTextures
+ {
+ get {
+ Texture2D[] splatTextures = new Texture2D[alphamapTextureCount];
+ for (int i=0;i<splatTextures.Length;i++)
+ splatTextures[i] = GetAlphamapTexture(i);
+ return splatTextures;
+ }
+ }
+
+ CUSTOM_PROP SplatPrototype[] splatPrototypes
+ { return VectorToScriptingClassArray<SplatPrototype, MonoSplatPrototype> (GETSPLAT->GetSplatPrototypes(), MONO_COMMON.splatPrototype, SplatPrototypeToMono); }
+ { GETSPLAT->SetSplatPrototypes (ScriptingClassArrayToVector<SplatPrototype, MonoSplatPrototype> (value, SplatPrototypeToCpp)); }
+ C++RAW #undef GET
+
+ CUSTOM internal bool HasTreeInstances ()
+ {
+ return !self->GetTreeDatabase().GetInstances().empty();
+ }
+
+ CUSTOM internal void AddTree (out TreeInstance tree)
+ {
+ self->GetTreeDatabase().AddTree (*tree);
+ }
+
+ CUSTOM internal int RemoveTrees (Vector2 position, float radius, int prototypeIndex)
+ {
+ return self->GetTreeDatabase().RemoveTrees (position, radius, prototypeIndex);
+ }
+END
+
+CSRAW
+}
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/ScriptBindings/Terrains.txt b/Runtime/Terrain/ScriptBindings/Terrains.txt
new file mode 100644
index 0000000..4335595
--- /dev/null
+++ b/Runtime/Terrain/ScriptBindings/Terrains.txt
@@ -0,0 +1,675 @@
+C++RAW
+#include "UnityPrefix.h"
+#include "Runtime/Terrain/Heightmap.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Terrain/DetailDatabase.h"
+#include "Runtime/Terrain/SplatDatabase.h"
+#include "Runtime/Terrain/TerrainData.h"
+#include "Runtime/Terrain/TerrainInstance.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Terrain/TerrainRenderer.h"
+#include "Runtime/Camera/Light.h"
+#include "Runtime/Terrain/DetailRenderer.h"
+#include "Runtime/Terrain/ImposterRenderTexture.h"
+#include "Runtime/Terrain/TreeRenderer.h"
+#include "Runtime/Terrain/Wind.h"
+#include "Runtime/Terrain/Tree.h"
+#include "Runtime/Scripting/GetComponent.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingInvocation.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Interfaces/ITerrainManager.h"
+
+using namespace Unity;
+using namespace std;
+
+CSRAW
+
+#if ENABLE_TERRAIN
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace UnityEngine
+{
+
+// List of changes done to the terrain for OnTerrainChanged
+// OnTerrainChanged is called with a bitfield of these items telling it what was changed.
+CSRAW
+[Flags]
+internal enum TerrainChangedFlags
+{
+ NoChange = 0,
+ Heightmap = 1,
+ TreeInstances = 2,
+ DelayedHeightmapUpdate = 4,
+ FlushEverythingImmediately = 8,
+ RemoveDirtyDetailsImmediately = 16,
+ WillBeDestroyed = 256,
+}
+
+ENUM TerrainRenderFlags
+ heightmap = 1,
+ trees = 2,
+ details = 4,
+ all = heightmap | trees | details
+END
+
+CSRAW
+CONDITIONAL ENABLE_TERRAIN
+[AddComponentMenu("")]
+[ExecuteInEditMode]
+CLASS Terrain : MonoBehaviour
+ CSRAW
+ [SerializeField]
+ private TerrainData m_TerrainData;
+ [SerializeField]
+ float m_TreeDistance = 5000.0F;
+ [SerializeField]
+ float m_TreeBillboardDistance = 50.0F;
+ [SerializeField]
+ float m_TreeCrossFadeLength = 5.0F;
+ [SerializeField]
+ int m_TreeMaximumFullLODCount = 50;
+ [SerializeField]
+ float m_DetailObjectDistance = 80.0F;
+ [SerializeField]
+ float m_DetailObjectDensity = 1.0f;
+ [SerializeField]
+ float m_HeightmapPixelError = 5.0F;
+ [SerializeField]
+ float m_SplatMapDistance = 1000.0F;
+ [SerializeField]
+ int m_HeightmapMaximumLOD = 0;
+ [SerializeField]
+ bool m_CastShadows = true;
+ [SerializeField]
+ int m_LightmapIndex = -1;
+ [SerializeField]
+ int m_LightmapSize = 1024;
+ [SerializeField]
+ bool m_DrawTreesAndFoliage = true;
+ [SerializeField]
+ Material m_MaterialTemplate;
+
+ [System.NonSerialized]
+ IntPtr m_TerrainInstance;
+
+ // Since the terrain object can be disabled on being loaded, there is
+ // no way to reliably initialize the TerrainInstance after loaded.
+ // So initialization is moved here.
+ private IntPtr InstanceObject
+ {
+ get
+ {
+ if (m_TerrainInstance == IntPtr.Zero)
+ {
+ m_TerrainInstance = Construct();
+ Internal_SetTerrainData(m_TerrainInstance, m_TerrainData);
+ Internal_SetTreeDistance(m_TerrainInstance, m_TreeDistance);
+ Internal_SetTreeBillboardDistance(m_TerrainInstance, m_TreeBillboardDistance);
+ Internal_SetTreeCrossFadeLength(m_TerrainInstance, m_TreeCrossFadeLength);
+ Internal_SetTreeMaximumFullLODCount(m_TerrainInstance, m_TreeMaximumFullLODCount);
+ Internal_SetDetailObjectDistance(m_TerrainInstance, m_DetailObjectDistance);
+ Internal_SetDetailObjectDensity(m_TerrainInstance, m_DetailObjectDensity);
+ Internal_SetHeightmapPixelError(m_TerrainInstance, m_HeightmapPixelError);
+ Internal_SetBasemapDistance(m_TerrainInstance, m_SplatMapDistance);
+ Internal_SetHeightmapMaximumLOD(m_TerrainInstance, m_HeightmapMaximumLOD);
+ Internal_SetCastShadows(m_TerrainInstance, m_CastShadows);
+ Internal_SetLightmapIndex(m_TerrainInstance, m_LightmapIndex);
+ Internal_SetLightmapSize(m_TerrainInstance, m_LightmapSize);
+ Internal_SetDrawTreesAndFoliage(m_TerrainInstance, m_DrawTreesAndFoliage);
+ Internal_SetMaterialTemplate(m_TerrainInstance, m_MaterialTemplate);
+ }
+ return m_TerrainInstance;
+ }
+ set
+ {
+ m_TerrainInstance = value;
+ }
+ }
+
+ private void OnDestroy()
+ {
+ OnDisable();
+
+ // Using InstanceObject potentially creates the object then immediately destroy it
+ Cleanup(m_TerrainInstance);
+ }
+
+ CUSTOM private void Cleanup(IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ UNITY_DELETE(ti, kMemTerrain);
+ }
+
+ CSRAW public TerrainRenderFlags editorRenderFlags
+ {
+ get { return (TerrainRenderFlags)GetEditorRenderFlags(InstanceObject); }
+ set { SetEditorRenderFlags(InstanceObject, (int)value); }
+ }
+ CUSTOM private int GetEditorRenderFlags(IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ return ti->GetEditorRenderFlags();
+ }
+ CUSTOM private void SetEditorRenderFlags(IntPtr terrainInstance, int flags)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->SetEditorRenderFlags((TerrainInstance::RenderFlags)flags);
+ }
+
+ CSRAW public TerrainData terrainData
+ {
+ get { m_TerrainData = Internal_GetTerrainData(InstanceObject); return m_TerrainData; }
+ set { m_TerrainData = value; Internal_SetTerrainData(InstanceObject, value); }
+ }
+ CUSTOM private TerrainData Internal_GetTerrainData(IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ return Scripting::ScriptingWrapperFor(ti->GetTerrainData());
+ }
+ CUSTOM private void Internal_SetTerrainData(IntPtr terrainInstance, TerrainData value)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->SetTerrainData(value);
+ }
+
+ CSRAW public float treeDistance
+ {
+ get { m_TreeDistance = Internal_GetTreeDistance(InstanceObject); return m_TreeDistance; }
+ set { m_TreeDistance = value; Internal_SetTreeDistance(InstanceObject, value); }
+ }
+ CUSTOM private float Internal_GetTreeDistance(IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ return ti->GetTreeDistance();
+ }
+ CUSTOM private void Internal_SetTreeDistance(IntPtr terrainInstance, float value)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->SetTreeDistance(value);
+ }
+
+ CSRAW public float treeBillboardDistance
+ {
+ get { m_TreeBillboardDistance = Internal_GetTreeBillboardDistance(InstanceObject); return m_TreeBillboardDistance; }
+ set { m_TreeBillboardDistance = value; Internal_SetTreeBillboardDistance(InstanceObject, value); }
+ }
+ CUSTOM private float Internal_GetTreeBillboardDistance(IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ return ti->GetTreeBillboardDistance();
+ }
+ CUSTOM private void Internal_SetTreeBillboardDistance(IntPtr terrainInstance, float value)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->SetTreeBillboardDistance(value);
+ }
+
+ CSRAW public float treeCrossFadeLength
+ {
+ get { m_TreeCrossFadeLength = Internal_GetTreeCrossFadeLength(InstanceObject); return m_TreeCrossFadeLength; }
+ set { m_TreeCrossFadeLength = value; Internal_SetTreeCrossFadeLength(InstanceObject, value); }
+ }
+ CUSTOM private float Internal_GetTreeCrossFadeLength(IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ return ti->GetTreeCrossFadeLength();
+ }
+ CUSTOM private void Internal_SetTreeCrossFadeLength(IntPtr terrainInstance, float value)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->SetTreeCrossFadeLength(value);
+ }
+
+ CSRAW public int treeMaximumFullLODCount
+ {
+ get { m_TreeMaximumFullLODCount = Internal_GetTreeMaximumFullLODCount(InstanceObject); return m_TreeMaximumFullLODCount; }
+ set { m_TreeMaximumFullLODCount = value; Internal_SetTreeMaximumFullLODCount(InstanceObject, value); }
+ }
+ CUSTOM private int Internal_GetTreeMaximumFullLODCount(IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ return ti->GetTreeMaximumFullLODCount();
+ }
+ CUSTOM private void Internal_SetTreeMaximumFullLODCount(IntPtr terrainInstance, int value)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->SetTreeMaximumFullLODCount(value);
+ }
+
+ CSRAW public float detailObjectDistance
+ {
+ get { m_DetailObjectDistance = Internal_GetDetailObjectDistance(InstanceObject); return m_DetailObjectDistance; }
+ set { m_DetailObjectDistance = value; Internal_SetDetailObjectDistance(InstanceObject, value); }
+ }
+ CUSTOM private float Internal_GetDetailObjectDistance(IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ return ti->GetDetailObjectDistance();
+ }
+ CUSTOM private void Internal_SetDetailObjectDistance(IntPtr terrainInstance, float value)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->SetDetailObjectDistance(value);
+ }
+
+ CSRAW public float detailObjectDensity
+ {
+ get { m_DetailObjectDensity = Internal_GetDetailObjectDensity(InstanceObject); return m_DetailObjectDensity; }
+ set { m_DetailObjectDensity = value; Internal_SetDetailObjectDensity(InstanceObject, value); }
+ }
+ CUSTOM private float Internal_GetDetailObjectDensity(IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ return ti->GetDetailObjectDensity();
+ }
+ CUSTOM private void Internal_SetDetailObjectDensity(IntPtr terrainInstance, float value)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->SetDetailObjectDensity(value);
+ }
+
+ CSRAW public float heightmapPixelError
+ {
+ get { m_HeightmapPixelError = Internal_GetHeightmapPixelError(InstanceObject); return m_HeightmapPixelError; }
+ set { m_HeightmapPixelError = value; Internal_SetHeightmapPixelError(InstanceObject, value); }
+ }
+ CUSTOM private float Internal_GetHeightmapPixelError(IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ return ti->GetHeightmapPixelError();
+ }
+ CUSTOM private void Internal_SetHeightmapPixelError(IntPtr terrainInstance, float value)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->SetHeightmapPixelError(value);
+ }
+
+ CSRAW public int heightmapMaximumLOD
+ {
+ get { m_HeightmapMaximumLOD = Internal_GetHeightmapMaximumLOD(InstanceObject); return m_HeightmapMaximumLOD; }
+ set { m_HeightmapMaximumLOD = value; Internal_SetHeightmapMaximumLOD(InstanceObject, value); }
+ }
+ CUSTOM private int Internal_GetHeightmapMaximumLOD(IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ return ti->GetHeightmapMaximumLOD();
+ }
+ CUSTOM private void Internal_SetHeightmapMaximumLOD(IntPtr terrainInstance, int value)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->SetHeightmapMaximumLOD(value);
+ }
+
+ CSRAW public float basemapDistance
+ {
+ get { m_SplatMapDistance = Internal_GetBasemapDistance(InstanceObject); return m_SplatMapDistance; }
+ set { m_SplatMapDistance = value; Internal_SetBasemapDistance(InstanceObject, value); }
+ }
+ CUSTOM private float Internal_GetBasemapDistance(IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ return ti->GetBasemapDistance();
+ }
+ CUSTOM private void Internal_SetBasemapDistance(IntPtr terrainInstance, float value)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->SetBasemapDistance(value);
+ }
+
+ OBSOLETE error use basemapDistance
+ CSRAW public float splatmapDistance
+ {
+ get { return basemapDistance; }
+ set { basemapDistance = value; }
+ }
+
+ CSRAW public int lightmapIndex
+ {
+ get { m_LightmapIndex = Internal_GetLightmapIndex(InstanceObject); return m_LightmapIndex; }
+ set { m_LightmapIndex = value; Internal_SetLightmapIndex(InstanceObject, value); }
+ }
+ CSRAW private void SetLightmapIndex(int value)
+ {
+ lightmapIndex = value;
+ }
+ CSRAW private void ShiftLightmapIndex(int offset)
+ {
+ lightmapIndex += offset;
+ }
+ CUSTOM private int Internal_GetLightmapIndex(IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ return ti->GetLightmapIndex();
+ }
+ CUSTOM private void Internal_SetLightmapIndex(IntPtr terrainInstance, int value)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->SetLightmapIndex(value);
+ }
+
+ CSRAW internal int lightmapSize
+ {
+ get { m_LightmapSize = Internal_GetLightmapSize(InstanceObject); return m_LightmapSize; }
+ set { m_LightmapSize = value; Internal_SetLightmapSize(InstanceObject, value); }
+ }
+ CUSTOM private int Internal_GetLightmapSize(IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ return ti->GetLightmapSize();
+ }
+ CUSTOM private void Internal_SetLightmapSize(IntPtr terrainInstance, int value)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->SetLightmapSize(value);
+ }
+
+ CSRAW public bool castShadows
+ {
+ get { m_CastShadows = Internal_GetCastShadows(InstanceObject); return m_CastShadows; }
+ set { m_CastShadows = value; Internal_SetCastShadows(InstanceObject, value); }
+ }
+ CUSTOM private bool Internal_GetCastShadows(IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ return ti->GetCastShadows();
+ }
+ CUSTOM private void Internal_SetCastShadows(IntPtr terrainInstance, bool value)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->SetCastShadows(value);
+ }
+
+ CSRAW public Material materialTemplate
+ {
+ get { m_MaterialTemplate = Internal_GetMaterialTemplate(InstanceObject); return m_MaterialTemplate; }
+ set { m_MaterialTemplate = value; Internal_SetMaterialTemplate(InstanceObject, value); }
+ }
+ CUSTOM private Material Internal_GetMaterialTemplate(IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ return Scripting::ScriptingWrapperFor(const_cast<Material*>(ti->GetMaterialTemplate()));
+ }
+ CUSTOM private void Internal_SetMaterialTemplate(IntPtr terrainInstance, Material value)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->SetMaterialTemplate(value);
+ }
+
+ CSRAW internal bool drawTreesAndFoliage
+ {
+ get { m_DrawTreesAndFoliage = Internal_GetDrawTreesAndFoliage(InstanceObject); return m_DrawTreesAndFoliage; }
+ set { m_DrawTreesAndFoliage = value; Internal_SetDrawTreesAndFoliage(InstanceObject, value); }
+ }
+ CUSTOM private bool Internal_GetDrawTreesAndFoliage(IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ return ti->GetDrawTreesAndFoliage();
+ }
+ CUSTOM private void Internal_SetDrawTreesAndFoliage(IntPtr terrainInstance, bool value)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->SetDrawTreesAndFoliage(value);
+ }
+
+ CSRAW public float SampleHeight(Vector3 worldPosition)
+ {
+ return Internal_SampleHeight(InstanceObject, worldPosition);
+ }
+ CUSTOM private float Internal_SampleHeight (IntPtr terrainInstance, Vector3 worldPosition)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ return ti->SampleHeight(worldPosition);
+ }
+
+ CSRAW internal void ApplyDelayedHeightmapModification()
+ {
+ Internal_ApplyDelayedHeightmapModification(InstanceObject);
+ }
+ CUSTOM internal void Internal_ApplyDelayedHeightmapModification (IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->ApplyDelayedHeightmapModification();
+ }
+
+ CSRAW public void AddTreeInstance(TreeInstance instance)
+ {
+ Internal_AddTreeInstance(InstanceObject, instance);
+ }
+ CUSTOM private void Internal_AddTreeInstance (IntPtr terrainInstance, TreeInstance instance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->AddTreeInstance(instance);
+ }
+
+ CSRAW public void SetNeighbors (Terrain left, Terrain top, Terrain right, Terrain bottom)
+ {
+ Internal_SetNeighbors(InstanceObject,
+ left != null ? left.InstanceObject : IntPtr.Zero,
+ top != null ? top.InstanceObject : IntPtr.Zero,
+ right != null ? right.InstanceObject : IntPtr.Zero,
+ bottom != null ? bottom.InstanceObject : IntPtr.Zero);
+ }
+ CUSTOM private void Internal_SetNeighbors (IntPtr terrainInstance, IntPtr left, IntPtr top, IntPtr right, IntPtr bottom)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ TerrainInstance* leftInstance = static_cast<TerrainInstance*>(left);
+ TerrainInstance* topInstance = static_cast<TerrainInstance*>(top);
+ TerrainInstance* rightInstance = static_cast<TerrainInstance*>(right);
+ TerrainInstance* bottomInstance = static_cast<TerrainInstance*>(bottom);
+ ti->SetNeighbors(leftInstance, topInstance, rightInstance, bottomInstance);
+ }
+
+ CSRAW public Vector3 GetPosition ()
+ {
+ return Internal_GetPosition(InstanceObject);
+ }
+ CUSTOM private Vector3 Internal_GetPosition (IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ return ti->GetPosition();
+ }
+
+ CSRAW public void Flush ()
+ {
+ Internal_Flush(InstanceObject);
+ }
+ CUSTOM private void Internal_Flush (IntPtr terrainInstance)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->Flush();
+ }
+
+ CSRAW internal void RemoveTrees (Vector2 position, float radius, int prototypeIndex)
+ {
+ Internal_RemoveTrees(InstanceObject, position, radius, prototypeIndex);
+ }
+ CUSTOM private void Internal_RemoveTrees (IntPtr terrainInstance, Vector2 position, float radius, int prototypeIndex)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->RemoveTrees(position, radius, prototypeIndex);
+ }
+
+ CSRAW private void OnTerrainChanged (TerrainChangedFlags flags)
+ {
+ Internal_OnTerrainChanged(InstanceObject, flags);
+ }
+
+ CUSTOM private void Internal_OnTerrainChanged (IntPtr terrainInstance, TerrainChangedFlags flags)
+ {
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ Assert(ti != NULL);
+ ti->OnTerrainChanged(flags);
+ }
+
+ CUSTOM private IntPtr Construct ()
+ {
+ SET_ALLOC_OWNER(self->GetGameObjectPtr());
+ return UNITY_NEW(TerrainInstance, kMemTerrain)(self->GetGameObjectPtr());
+ }
+
+ CSRAW
+ internal void OnEnable ()
+ {
+ Internal_OnEnable(InstanceObject);
+ }
+
+ CUSTOM private void Internal_OnEnable (IntPtr terrainInstance)
+ {
+ Assert(terrainInstance != NULL);
+
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ ti->OnEnable();
+ GetITerrainManager()->AddTerrainAndSetActive(ti);
+ }
+
+ CSRAW
+ internal void OnDisable()
+ {
+ Internal_OnDisable(InstanceObject);
+ }
+
+ CUSTOM private void Internal_OnDisable (IntPtr terrainInstance)
+ {
+ Assert(terrainInstance != NULL);
+
+ TerrainInstance* ti = static_cast<TerrainInstance*>(terrainInstance);
+ GetITerrainManager()->RemoveTerrain(ti);
+ ti->OnDisable();
+ ti->Flush();
+ }
+
+ C++RAW
+ ScriptingObjectPtr TerrainInstanceToMonoBehaviour(TerrainInstance* ti)
+ {
+ if (!ti)
+ return SCRIPTING_NULL;
+
+ GameObject* go = ti->GetGameObject();
+ Assert(go != NULL);
+ return ScriptingGetComponentOfType(*go, MONO_COMMON.terrain);
+ }
+
+ CUSTOM_PROP static Terrain activeTerrain
+ {
+ TerrainInstance* ti = GetITerrainManager()->GetActiveTerrain();
+ return TerrainInstanceToMonoBehaviour(ti);
+ }
+
+ CUSTOM_PROP static Terrain[] activeTerrains
+ {
+ TerrainList tl = GetITerrainManager()->GetActiveTerrains();
+
+ ScriptingClassPtr terrainClass = MONO_COMMON.terrain;
+ ScriptingArrayPtr array = CreateScriptingArray<ScriptingObjectPtr>(terrainClass, tl.size());
+
+ int index = 0;
+ for (TerrainList::iterator i = tl.begin(); i != tl.end(); ++i, index++)
+ Scripting::SetScriptingArrayElement(array, index, TerrainInstanceToMonoBehaviour (*i));
+ return array;
+ }
+
+ CSRAW
+ public static GameObject CreateTerrainGameObject (TerrainData assignTerrain)
+ {
+ // Also create the renderer game object
+ #if ENABLE_PHYSICS
+ GameObject go = new GameObject("Terrain", typeof(Terrain), typeof(TerrainCollider));
+ #else
+ GameObject go = new GameObject("Terrain", typeof(Terrain));
+ #endif
+ go.isStatic = true;
+ Terrain terrain = go.GetComponent(typeof(Terrain)) as Terrain;
+ #if ENABLE_PHYSICS
+ TerrainCollider collider = go.GetComponent(typeof(TerrainCollider)) as TerrainCollider;
+ collider.terrainData = assignTerrain;
+ #endif
+ terrain.terrainData = assignTerrain;
+
+ // The terrain already got an OnEnable, but the terrain data had not been set up correctly.
+ terrain.OnEnable ();
+
+ return go;
+ }
+
+ // This method is used internally by the engine to reconnect Terrain objects to TerrainData.
+ private static void ReconnectTerrainData()
+ {
+ List<Terrain> activeTerrains = new List<Terrain>(Terrain.activeTerrains);
+ foreach (Terrain terrain in activeTerrains)
+ {
+ // we could delete asset directly - remove it here (we are calling this function on StopAssetEditing
+ if (terrain.terrainData == null )
+ {
+ terrain.OnDisable();
+ continue;
+ }
+
+ // Check if connection to m_TerrainData has been lost
+ if (!terrain.terrainData.HasUser(terrain.gameObject))
+ {
+ // destroy and recreate data
+ terrain.OnDisable();
+ terrain.OnEnable();
+ }
+ }
+ }
+END
+
+CONDITIONAL ENABLE_TERRAIN
+CLASS Tree : Component
+
+ AUTO_PTR_PROP ScriptableObject data GetTreeData SetTreeData
+
+END
+
+CSRAW
+}
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/ScriptBindings/WindZoneBindings.txt b/Runtime/Terrain/ScriptBindings/WindZoneBindings.txt
new file mode 100644
index 0000000..b9e98ea
--- /dev/null
+++ b/Runtime/Terrain/ScriptBindings/WindZoneBindings.txt
@@ -0,0 +1,183 @@
+C++RAW
+#include "UnityPrefix.h"
+#include "Runtime/Terrain/Heightmap.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Terrain/DetailDatabase.h"
+#include "Runtime/Terrain/SplatDatabase.h"
+#include "Runtime/Terrain/TerrainData.h"
+#include "Runtime/Terrain/TerrainInstance.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Terrain/TerrainRenderer.h"
+#include "Runtime/Camera/Light.h"
+#include "Runtime/Terrain/DetailRenderer.h"
+#include "Runtime/Terrain/ImposterRenderTexture.h"
+#include "Runtime/Terrain/TreeRenderer.h"
+#include "Runtime/Terrain/Wind.h"
+#include "Runtime/Terrain/Tree.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+
+using namespace Unity;
+using namespace std;
+
+CSRAW
+
+#if ENABLE_TERRAIN
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace UnityEngine
+{
+
+/// Modes a Wind Zone can have, either Spherical or Directional
+/// You can have more than one Spherical Wind Zone in a scene, but it does not make much
+/// sense to have more than one Directional Wind Zone in your scene as it affects
+/// the whole scene. This Wind Zone Mode is used by the WindZone.mode member.
+CONDITIONAL ENABLE_TERRAIN
+ENUM internal WindZoneMode
+ /// Wind zone only has an effect inside the radius, and has a falloff from the center towards the edge.
+ CONVERTEXAMPLE
+ BEGIN EX
+ // Creates a Directional Wind Zone that blows wind up.
+
+ function Start() {
+ // var wind : WindZone = gameObject.AddComponent(WindZone);
+ // wind.mode = WindZoneMode.Directional;
+ // transform.rotation = Quaternion.LookRotation(Vector3.up);
+ }
+ END EX
+ ///
+ Directional = 0,
+ /// Wind zone affects the entire scene in one direction.
+ CONVERTEXAMPLE
+ BEGIN EX
+ // Creates a Spherical Wind Zone.
+
+ function Start() {
+ // var wind : WindZone = gameObject.AddComponent(WindZone);
+ // wind.mode = WindZoneMode.Spherical;
+ }
+ END EX
+ ///
+ Spherical = 1
+END
+
+/// Wind Zones add realism to the trees you create by making them wave their branches and leaves as if blown by the wind.
+///
+/// __Note:__ This only works with trees created by the tree creator.
+CSRAW
+CONDITIONAL ENABLE_TERRAIN
+CLASS internal WindZone : Component
+
+ /// Defines the type of wind zone to be used (Spherical or Directional).
+ CONVERTEXAMPLE
+ BEGIN EX
+ // Creates a Directional Wind Zone.
+
+ function Start() {
+ // var wind : WindZone = gameObject.AddComponent(WindZone);
+ // wind.mode = WindZoneMode.Directional;
+ }
+ END EX
+ ///
+ AUTO_PROP WindZoneMode mode GetMode SetMode
+
+ /// Radius of the Spherical Wind Zone (only active if the WindZoneMode is set to Spherical).
+ CONVERTEXAMPLE
+ BEGIN EX
+ // Creates a Spherical Wind Zone and sets its radius to 10.
+
+ function Start() {
+ // var wind : WindZone = gameObject.AddComponent(WindZone);
+ // wind.mode = WindZoneMode.Spherical;
+ // wind.radius = 10;
+ }
+ END EX
+ ///
+ AUTO_PROP float radius GetRadius SetRadius
+
+ /// The primary wind force.
+ /// It produces a softly changing wind Pressure.
+ CONVERTEXAMPLE
+ BEGIN EX
+ // Creates a wind zone with the effect of a helicopter passing by
+ // Just place this into an empty game object and move it over a tree
+
+ function Start() {
+ // var wind : WindZone = gameObject.AddComponent(WindZone);
+ // wind.mode = WindZoneMode.Spherical;
+ // wind.radius = 10.0;
+ // wind.windMain = 3.0;
+ // wind.windTurbulence = 0.5;
+ // wind.windPulseMagnitude = 2.0;
+ // wind.windPulseFrequency = 0.01;
+ }
+ END EX
+ ///
+ AUTO_PROP float windMain GetWindMain SetWindMain
+
+ /// The turbulence wind force.
+ /// Produces a rapidly changing wind pressure.
+ CONVERTEXAMPLE
+ BEGIN EX
+ // Creates a wind zone to produce a softly changing general wind
+ // Just place this into an empty game object
+
+ function Start() {
+ // var wind : WindZone = gameObject.AddComponent(WindZone);
+ // wind.mode = WindZoneMode.Directional;
+ // wind.windMain = 0.70;
+ // wind.windTurbulence = 0.1;
+ // wind.windPulseMagnitude = 2.0;
+ // wind.windPulseFrequency = 0.25;
+ }
+ END EX
+ ///
+ AUTO_PROP float windTurbulence GetWindTurbulence SetWindTurbulence
+
+ /// Defines ow much the wind changes over time.
+ CONVERTEXAMPLE
+ BEGIN EX
+ // Creates a wind zone with the effect of a helicopter passing by
+ // Just place this into an empty game object and move it over a tree
+
+ function Start() {
+ // var wind : WindZone = gameObject.AddComponent(WindZone);
+ // wind.mode = WindZoneMode.Spherical;
+ // wind.radius = 10.0;
+ // wind.windMain = 3.0;
+ // wind.windTurbulence = 0.5;
+ // wind.windPulseMagnitude = 2.0;
+ // wind.windPulseFrequency = 0.01;
+ }
+ END EX
+ ///
+ AUTO_PROP float windPulseMagnitude GetWindPulseMagnitude SetWindPulseMagnitude
+
+ /// Defines the frequency of the wind changes.
+ CONVERTEXAMPLE
+ BEGIN EX
+ // Creates a wind zone to produce a softly changing general wind
+ // Just place this into an empty game object
+
+ function Start() {
+ // var wind : WindZone = gameObject.AddComponent(WindZone);
+ // wind.mode = WindZoneMode.Directional;
+ // wind.windMain = 0.70;
+ // wind.windTurbulence = 0.1;
+ // wind.windPulseMagnitude = 2.0;
+ // wind.windPulseFrequency = 0.25;
+ }
+ END EX
+ ///
+ AUTO_PROP float windPulseFrequency GetWindPulseFrequency SetWindPulseFrequency
+END
+
+CSRAW
+}
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/SplatDatabase.cpp b/Runtime/Terrain/SplatDatabase.cpp
new file mode 100644
index 0000000..41cdf7b
--- /dev/null
+++ b/Runtime/Terrain/SplatDatabase.cpp
@@ -0,0 +1,600 @@
+#include "UnityPrefix.h"
+#include "SplatDatabase.h"
+
+#if ENABLE_TERRAIN
+
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Graphics/Image.h"
+#include "TerrainData.h"
+#include "Runtime/Scripting/Scripting.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/AssetPipeline/AssetImporter.h"
+#endif
+
+#define PRINT_BASEMAP_TIME 0
+
+#if PRINT_BASEMAP_TIME
+double GetTimeSinceStartup();
+#endif
+
+
+using namespace std;
+
+SplatPrototype::SplatPrototype ()
+: tileSize(15, 15),
+ tileOffset(0, 0)
+{
+
+}
+
+SplatDatabase::SplatDatabase (TerrainData *owner)
+: m_BaseMap(NULL)
+, m_AlphamapResolution(512)
+, m_BaseMapResolution(512)
+, m_BaseMapDirty (true)
+{
+ m_TerrainData = owner;
+}
+
+SplatDatabase::~SplatDatabase()
+{
+ if( m_BaseMap )
+ DestroySingleObject( m_BaseMap );
+}
+
+void SplatDatabase::UploadBasemap()
+{
+ if (!m_BaseMap->IsInstanceIDCreated())
+ {
+ Object::AllocateAndAssignInstanceID(m_BaseMap);
+ m_BaseMap->SetHideFlags (Object::kHideAndDontSave);
+ m_BaseMap->SetWrapMode( kTexWrapClamp );
+ }
+ m_BaseMap->AwakeFromLoad(kDefaultAwakeFromLoad);
+}
+
+
+// Typical time spent in recalculate base maps (core duo w/ gcc):
+// 6 splats, basemap size 2048:
+// 0.53 seconds (0.072 getting the mips, 0.031 getting alpha map, 0.268 blending, 0.16 uploading texture)
+// 6 splats, basemap size 1024:
+// 0.21 seconds (0.072 getting the mips, 0.031 getting alpha map, 0.067 blending, 0.04 uploading texture)
+//
+// Here we operate on ColorRGBA32 colors. The first quick implementation used ColorRGBAf,
+// that was about 40% slower.
+
+void SplatDatabase::RecalculateBasemap(bool allowUpload)
+{
+ //
+ // create/resize base map texture
+
+ int basemapSize = m_BaseMapResolution;
+ if( !m_BaseMap )
+ {
+ m_BaseMap = NEW_OBJECT_FULL (Texture2D, kCreateObjectFromNonMainThread);
+ m_BaseMap->Reset();
+
+ // ok, just from performance standpoint we don't want to upload texture here
+ // or get an assert from uninited texture, so, let's cheat
+ m_BaseMap->HackSetAwakeDidLoadThreadedWasCalled();
+
+ // uncomment this for proper handling (but assert will fire in current impl)
+ //m_BaseMap->AwakeFromLoad(kDidLoadThreaded);
+
+ m_BaseMap->InitTexture( basemapSize, basemapSize, kTexFormatRGBA32, Texture2D::kMipmapMask | Texture2D::kThreadedInitialize, 1 );
+
+ // uncomment this for proper handling (but you will upload texture twice in current impl)
+ //m_BaseMap->AwakeFromLoad(kDidLoadThreaded);
+ }
+ else
+ {
+ if( m_BaseMap->GetDataWidth() != basemapSize || m_BaseMap->GetDataHeight() != basemapSize || m_BaseMap->GetTextureFormat() != kTexFormatRGBA32 || !m_BaseMap->HasMipMap() )
+ m_BaseMap->ResizeWithFormat (basemapSize, basemapSize, kTexFormatRGBA32, Texture2D::kMipmapMask);
+ }
+
+ Vector3f terrainSize = m_TerrainData->GetHeightmap().GetSize();
+
+ #if PRINT_BASEMAP_TIME
+ double t0 = GetTimeSinceStartup();
+ #endif
+
+ #define SPLAT_FIX_BITS 16 // use 16.16 fixed point
+
+ // Note to optimizers: separating this struct into hot (x,y,yoffset) & cold (the rest) data
+ // actually made it slower (about 20%, core duo w/ gcc), due to more housekeeping in the
+ // innermost loop.
+ struct SplatData
+ {
+ int xmask; // 16.16 fixed point
+ int ymask; // 16.16 fixed point
+ int dx; // 16.16 fixed point
+ int dy; // 16.16 fixed point
+ int x; // 16.16 fixed point
+ int y; // 16.16 fixed point
+ int xoffset;// 16.16 fixed point
+ int yoffset;
+ int width;
+ ColorRGBA32* mip;
+ };
+
+
+ //
+ // Get data needed from the splat textures.
+ // This gets the optimal mip level in RGBA32 colors, and computes step sizes etc.
+
+ int splatCount = m_Splats.size();
+
+ SplatData* splatData = new SplatData[splatCount];
+
+ // Set up sRGB state for splat maps
+ bool sRGBEncountered = false;
+ for( int i = 0; i < splatCount; ++i )
+ {
+ // figure out which mip level of the splat map to use
+ Texture2D* splatTex = dynamic_pptr_cast<Texture2D*> (InstanceIDToObjectThreadSafe(m_Splats[i].texture.GetInstanceID()));
+ if( !splatTex )
+ {
+ // if no splat texture, fill in a dummy one white pixel
+ ErrorStringObject( Format ("Terrain splat %d is null.", i), m_TerrainData );
+ splatData[i].mip = new ColorRGBA32[1];
+ splatData[i].mip[0] = 0xFFFFFFFF;
+ splatData[i].width = 1;
+ splatData[i].xmask = 0;
+ splatData[i].ymask = 0;
+ splatData[i].dx = 0;
+ splatData[i].dy = 0;
+ splatData[i].x = 0;
+ splatData[i].y = 0;
+ continue;
+ }
+
+ if (splatTex->GetStoredColorSpace () != kTexColorSpaceLinear)
+ sRGBEncountered = true;
+
+ #if !UNITY_EDITOR
+ AssertIf(!splatTex->GetIsReadable());
+ #endif
+ int splatWidth = splatTex->GetDataWidth();
+ int splatHeight = splatTex->GetDataHeight();
+ float tilesX = terrainSize.x / m_Splats[i].tileSize.x;
+ float tilesY = terrainSize.z / m_Splats[i].tileSize.y;
+ int splatBaseWidth = std::max( (int)(basemapSize / tilesX), 1 );
+ int splatBaseHeight = std::max( (int)(basemapSize / tilesY), 1 );
+ float areaRatio = (splatWidth * splatHeight) / (splatBaseWidth * splatBaseHeight);
+
+ const float tileOffsetX = m_Splats[i].tileOffset.x / terrainSize.x * tilesX;
+ const float tileOffsetY = m_Splats[i].tileOffset.y / terrainSize.z * tilesY;
+
+ int mipLevel = int(0.5f * Log2( areaRatio ));
+ mipLevel = clamp( mipLevel, 0, splatTex->CountDataMipmaps() - 1);
+
+ // get the pixels of this mip level
+ int minSplatSize = GetMinimumTextureMipSizeForFormat( splatTex->GetTextureFormat() );
+ int width = std::max( splatWidth >> mipLevel, minSplatSize );
+ int height = std::max( splatHeight >> mipLevel, minSplatSize );
+ splatData[i].mip = new ColorRGBA32[width * height];
+ if( !splatTex->GetPixels32( mipLevel, splatData[i].mip ) )
+ {
+ memset( splatData[i].mip, 0, width*height*sizeof(ColorRGBA32) );
+ ErrorStringObject( "Failed to get pixels of splat texture", m_TerrainData );
+ }
+
+ // compute step sizes for this splat
+ splatData[i].width = width;
+ splatData[i].xmask = (width << SPLAT_FIX_BITS) - 1;
+ splatData[i].ymask = (height << SPLAT_FIX_BITS) - 1;
+ float dx = tilesX * width / basemapSize;
+ splatData[i].dx = (int)( dx * (1<<SPLAT_FIX_BITS) );
+ float dy = tilesY * height / basemapSize;
+ splatData[i].dy = (int)( dy * (1<<SPLAT_FIX_BITS) );
+ splatData[i].xoffset = (int)(tileOffsetX * width * (1<<SPLAT_FIX_BITS));
+ int yoffset = (int)(tileOffsetY * height * (1<<SPLAT_FIX_BITS));
+ splatData[i].x = 0;
+ splatData[i].y = yoffset + (splatData[i].dy / 2) & splatData[i].ymask; // start at mid-pixel
+ }
+
+ if (sRGBEncountered)
+ m_BaseMap->SetStoredColorSpaceNoDirtyNoApply (kTexColorSpaceSRGB);
+ else
+ m_BaseMap->SetStoredColorSpaceNoDirtyNoApply (kTexColorSpaceLinear);
+
+ #if PRINT_BASEMAP_TIME
+ double t1 = GetTimeSinceStartup();
+ #endif
+
+ //
+ // get alpha map
+
+ dynamic_array<UInt8> alphamap;
+ GetAlphamaps( alphamap );
+
+ #if PRINT_BASEMAP_TIME
+ double t2 = GetTimeSinceStartup();
+ #endif
+
+ //
+ // blend splats directly into texture data
+
+ AssertIf( m_BaseMap->GetTextureFormat() != kTexFormatRGBA32 );
+ ColorRGBA32* pix = reinterpret_cast<ColorRGBA32*>( m_BaseMap->GetRawImageData() );
+
+ int alphaFixStep = (m_AlphamapResolution << SPLAT_FIX_BITS) / basemapSize; // 16.16 fixed point
+ int alphaFixY = 0;
+ int pixIndex = 0;
+ for( int y = 0; y < basemapSize; ++y, alphaFixY += alphaFixStep )
+ {
+ int alphaY = (alphaFixY >> SPLAT_FIX_BITS);
+ int alphaYIndex = alphaY * m_AlphamapResolution * splatCount;
+
+ int alphaFixX = 0;
+ for( int s = 0; s < splatCount; ++s ) {
+ splatData[s].x = (splatData[s].dx / 2 + splatData[s].xoffset) & splatData[s].xmask;
+ splatData[s].yoffset = (splatData[s].y >> SPLAT_FIX_BITS) * splatData[s].width;
+ }
+ for( int x = 0; x < basemapSize; ++x, alphaFixX += alphaFixStep, ++pixIndex )
+ {
+ int alphaX = (alphaFixX >> SPLAT_FIX_BITS);
+ int alphaXIndex = alphaX * splatCount;
+
+ // Per-pixel loop: accumulate splat textures into final color.
+ // No need for pixel clipping since alphamap weights are always normalized.
+ UInt32 c = 0;
+ for( int s = 0; s < splatCount; ++s )
+ {
+ SplatData& splat = splatData[s];
+ ColorRGBA32 splatCol = splat.mip[ splat.yoffset + (splat.x >> SPLAT_FIX_BITS) ];
+
+ // Note for optimizers: trying to get rid of multiplies in color*byte by using
+ // a 256x256 lookup table is about 2x slower (core duo w/ gcc).
+
+ // We always have to set alpha to zero. Using color * byte and then setting
+ // alpha to zero does not get optimized away by gcc, so use directly changed code
+ // from color * byte.
+ int scale = alphamap[alphaYIndex + alphaXIndex + s];
+ const UInt32& u = reinterpret_cast<const UInt32&> (splatCol);
+ #if UNITY_LITTLE_ENDIAN
+ UInt32 lsb = (((u & 0x00ff00ff) * scale) >> 8) & 0x00ff00ff;
+ UInt32 msb = (((u & 0xff00ff00) >> 8) * scale) & 0x0000ff00;
+ #else
+ UInt32 lsb = (((u & 0x00ff00ff) * scale) >> 8) & 0x00ff0000;
+ UInt32 msb = (((u & 0xff00ff00) >> 8) * scale) & 0xff00ff00;
+ #endif
+ c += lsb | msb;
+
+ splat.x = (splat.x + splat.dx) & splat.xmask; // next splat texel
+ }
+ pix[pixIndex] = ColorRGBA32(c);
+
+ }
+ for( int s = 0; s < splatCount; ++s )
+ splatData[s].y = (splatData[s].y + splatData[s].dy) & splatData[s].ymask;
+ }
+
+ if (splatCount == 0)
+ {
+ memset(pix, 0xFFFFFFFF, basemapSize * basemapSize * GetBytesFromTextureFormat(m_BaseMap->GetTextureFormat()));
+ }
+
+ #if PRINT_BASEMAP_TIME
+ double t3 = GetTimeSinceStartup();
+ #endif
+
+ m_BaseMap->RebuildMipMap();
+
+ // Upload base map texture
+
+ if(allowUpload)
+ UploadBasemap();
+
+ // Cleanup
+ for( int i = 0; i < splatCount; ++i )
+ delete[] splatData[i].mip;
+ delete[] splatData;
+
+ #if PRINT_BASEMAP_TIME
+ double t4 = GetTimeSinceStartup();
+ printf_console( "basemap time calc: %.2f (%.3f mips, %.3f alpha, %.3f blend, %.2f upload)\n", (t4-t0), (t1-t0), (t2-t1), (t3-t2), (t4-t3) );
+ #endif
+ m_BaseMapDirty = false;
+}
+
+
+void SplatDatabase::GetAlphamaps (int xBase, int yBase, int width, int height, float* buffer)
+{
+ int layers = GetDepth();
+
+ ColorRGBAf *tempBuffer;
+ ALLOC_TEMP(tempBuffer, ColorRGBAf, width * height);
+
+
+ for (int m=0;m<m_AlphaTextures.size();m++)
+ {
+ int componentCount = min(layers - m * 4, 4);
+
+ Texture2D* texture = m_AlphaTextures[m];
+ if (texture) {
+ texture->GetPixels (xBase, yBase, width, height, 0, tempBuffer);
+ } else {
+ ErrorStringObject (Format ("splatdatabase alphamap %d is null", m), m_TerrainData);
+ memset (tempBuffer, 0, width * height * sizeof(ColorRGBAf));
+ }
+
+ for (int y=0;y<height;y++)
+ {
+ for (int x=0;x<width;x++)
+ {
+ float *pixel = reinterpret_cast<float*> (&tempBuffer[y*width+x]);
+ for (int a=0;a<componentCount;a++) {
+ int layer = m * 4+a;
+ buffer[y * width * layers + x * layers + layer] = pixel[a];
+ }
+ }
+ }
+ }
+}
+
+void SplatDatabase::GetAlphamaps( dynamic_array<UInt8>& buffer )
+{
+ // resync the m_AlphamapResolution with textures in m_AlphaTextures array
+ for (int m = 0; m < m_AlphaTextures.size(); ++m)
+ {
+ Texture2D* texture = dynamic_pptr_cast<Texture2D*>(InstanceIDToObjectThreadSafe(m_AlphaTextures[m].GetInstanceID()));
+ if (texture == NULL)
+ {
+ ErrorStringObject(Format("splatdatabase alphamap %d is null", m), m_TerrainData);
+ continue;
+ }
+
+ if (texture->GetDataWidth() != m_AlphamapResolution)
+ {
+ if (m == 0)
+ {
+ WarningStringObject(Format("splatdatabase alphamap %d texture size doesn't match alphamap resolution setting: please resave the terrain asset.", m), m_TerrainData);
+ m_AlphamapResolution = texture->GetDataWidth();
+ }
+ else
+ {
+ ErrorStringObject(Format("splatdatabase alphamap %d texture size doesn't match to other alphamap textures.", m), m_TerrainData);
+ }
+ }
+ }
+
+ int size = m_AlphamapResolution;
+ int pixelCount = size * size;
+ int layers = GetDepth();
+
+ buffer.resize_uninitialized( size * size * layers );
+
+ ColorRGBA32 *tempBuffer;
+ ALLOC_TEMP(tempBuffer, ColorRGBA32, size * size);
+
+ for( int m=0;m<m_AlphaTextures.size();m++ )
+ {
+ int componentCount = min(layers - m * 4, 4);
+
+ Texture2D* texture = dynamic_pptr_cast<Texture2D*>(InstanceIDToObjectThreadSafe(m_AlphaTextures[m].GetInstanceID()));
+ if (texture != NULL && texture->GetDataWidth() == size && texture->GetDataHeight() == size)
+ {
+ texture->GetPixels32( 0, tempBuffer );
+ }
+ else
+ {
+ ErrorStringObject (Format ("splatdatabase alphamap %d is invalid", m), m_TerrainData);
+ memset (tempBuffer, 0, pixelCount * sizeof(ColorRGBA32));
+ }
+
+ int bufferIndex = m * 4;
+ for( int i = 0; i < pixelCount; ++i )
+ {
+ UInt8* pixel = reinterpret_cast<UInt8*>( &tempBuffer[i] );
+ for( int a = 0; a < componentCount; a++ )
+ {
+ buffer[bufferIndex + a] = pixel[a];
+ }
+ bufferIndex += layers;
+ }
+ }
+}
+
+
+/// Assign back the alpha map in the given area
+void SplatDatabase::SetAlphamaps (int xBase, int yBase, int width, int height, float* buffer)
+{
+ int layers = GetDepth ();
+
+ ColorRGBAf *tempBuffer;
+ ALLOC_TEMP(tempBuffer, ColorRGBAf, width * height);
+ int alphamaps = m_AlphaTextures.size();
+ for (int m=0; m < alphamaps; m++)
+ {
+ memset (tempBuffer, 0, width * height* sizeof (ColorRGBAf));
+ int componentCount = min(layers - m * 4, 4);
+
+ for (int y=0;y<height;y++)
+ {
+ for (int x=0;x<width;x++)
+ {
+ float *pixel = reinterpret_cast<float*> (&tempBuffer[y*width+x]);
+ for (int a=0; a<componentCount; a++) {
+ int layer = m * 4 + a;
+ pixel[a] = buffer[y * width * layers + x * layers + layer];
+
+ }
+ }
+ }
+ Texture2D* texture = m_AlphaTextures[m];
+ if (!texture) {
+ ErrorStringObject (Format ("splatdatabase alphamap %d is null", m), m_TerrainData);
+ continue;
+ }
+
+ texture->SetPixels(xBase, yBase, width, height, width * height, tempBuffer, 0 );
+ texture->UpdateImageData();
+ }
+ m_BaseMapDirty = true;
+}
+
+static void ClearAlphaMap (Texture2D * map, const ColorRGBAf &color)
+{
+ ImageReference image;
+ if( !map->GetWriteImageReference(&image, 0, 0) )
+ {
+ ErrorString("Unable to retrieve image reference");
+ return;
+ }
+
+ ColorRGBA32 tempColor (color);
+ ColorRGBA32 colorARGB (tempColor.a, tempColor.r, tempColor.g, tempColor.b);
+
+ int texWidth = image.GetWidth();
+ int texHeight = image.GetHeight();
+ for( int iy = 0; iy < texHeight; ++iy )
+ {
+ ColorRGBA32* pixel = (ColorRGBA32*)image.GetRowPtr(iy);
+ for( int ix = 0; ix < texWidth; ++ix )
+ {
+ *pixel = colorARGB;
+ ++pixel;
+ }
+ }
+ map->UpdateImageData();
+}
+
+
+Texture2D *SplatDatabase::AllocateAlphamap (const ColorRGBAf &color)
+{
+ Texture2D* map = CreateObjectFromCode<Texture2D>(kDefaultAwakeFromLoad);
+ map->ResizeWithFormat(m_AlphamapResolution, m_AlphamapResolution, kTexFormatARGB32, Texture2D::kMipmapMask);
+ map->SetWrapMode (kTexWrapClamp);
+
+ ClearAlphaMap(map, color);
+
+ map->SetName(Format ("SplatAlpha %u", (int)m_AlphaTextures.size()).c_str ());
+ #if UNITY_EDITOR
+ if (m_TerrainData->IsPersistent ())
+ AddAssetToSameFile (*map, *m_TerrainData);
+ #endif
+ return map;
+}
+
+void SplatDatabase::AwakeFromLoad (AwakeFromLoadMode mode)
+{
+ if ((mode & kDidLoadThreaded) == 0)
+ {
+ // Ensure that the alpha map textures are allocated properly.
+ // When removing some splat texture, that can cause an alpha map to be destroyed.
+ // If later the removal is undone, we need to recreate the alpha map texture again.
+ int splatCount = m_Splats.size();
+ int required = (splatCount / 4) + ((splatCount % 4) != 0 ? 1 : 0);
+ AssertIf( m_AlphaTextures.size() != required );
+ for( int i = 0; i < required; ++i )
+ {
+ Texture2D* tex = m_AlphaTextures[i];
+ if( tex == NULL )
+ {
+ ColorRGBAf color = ColorRGBAf (0,0,0,0);
+ if( i == 0 )
+ color.r = 1.0f;
+ m_AlphaTextures[i] = AllocateAlphamap (color);
+ }
+ }
+
+ m_BaseMapDirty = true;
+ }
+}
+
+
+void SplatDatabase::Init (int alphamapResolution, int basemapResolution)
+{
+ m_AlphamapResolution = alphamapResolution;
+ m_BaseMapResolution = basemapResolution;
+}
+
+void SplatDatabase::SetAlphamapResolution (int res)
+{
+ m_AlphamapResolution = clamp( res, 16, 2048 );
+ for (int i=0;i<m_AlphaTextures.size();i++)
+ {
+ Texture2D* map = m_AlphaTextures[i];
+ if (map)
+ {
+ map->ResizeWithFormat(m_AlphamapResolution, m_AlphamapResolution, kTexFormatARGB32, Texture2D::kMipmapMask);
+ ClearAlphaMap(map, i == 0 ? ColorRGBAf (1, 0, 0, 0) : ColorRGBAf (0, 0, 0, 0));
+ }
+ }
+ RecalculateBasemap(true);
+}
+
+void SplatDatabase::SetBaseMapResolution( int res )
+{
+ m_BaseMapResolution = clamp( res, 16, 2048 );
+ RecalculateBasemap(true);
+}
+
+bool SplatDatabase::RecalculateBasemapIfDirty()
+{
+ if (m_BaseMapDirty)
+ {
+ RecalculateBasemap(true);
+ return true;
+ }
+ return false;
+}
+
+void SplatDatabase::SetSplatPrototypes (const vector<SplatPrototype> &splats )
+{
+ // TODO: TEST for adding & removing
+ // TODO: renormalize & optionally ditch an alphatexture when removing one
+ // Do we need another one
+ int required = (splats.size() / 4) + ((splats.size() % 4) != 0 ? 1 : 0);
+ if (m_AlphaTextures.size() < required)
+ {
+ for (int i = m_AlphaTextures.size(); i < required; i++)
+ {
+ ColorRGBAf color = ColorRGBAf (0,0,0,0);
+ if (m_AlphaTextures.empty ())
+ color.r = 1;
+ m_AlphaTextures.push_back (AllocateAlphamap (color));
+ }
+ }
+ else if ( m_AlphaTextures.size() > required)
+ {
+ for (int i = required; i < m_AlphaTextures.size(); i++)
+ {
+ DestroySingleObject(m_AlphaTextures[i]);
+ }
+ m_AlphaTextures.resize (required);
+ }
+ m_Splats = splats;
+ RecalculateBasemap(true);
+ m_TerrainData->SetDirty();
+}
+
+Texture2D * SplatDatabase::GetAlphaTexture (int index)
+{
+ return m_AlphaTextures[index];
+}
+
+Texture2D* SplatDatabase::GetBasemap()
+{
+ return m_BaseMap;
+}
+
+
+void SplatPrototypeToMono (const SplatPrototype &src, MonoSplatPrototype &dest) {
+ dest.texture = Scripting::ScriptingWrapperFor (src.texture);
+ dest.normalMap = Scripting::ScriptingWrapperFor (src.normalMap);
+ dest.tileSize = src.tileSize;
+ dest.tileOffset = src.tileOffset;
+}
+void SplatPrototypeToCpp (MonoSplatPrototype &src, SplatPrototype &dest) {
+ dest.texture = ScriptingObjectToObject<Texture2D> (src.texture);
+ dest.normalMap = ScriptingObjectToObject<Texture2D> (src.normalMap);
+ dest.tileSize = src.tileSize;
+ dest.tileOffset = src.tileOffset;
+}
+
+
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/SplatDatabase.h b/Runtime/Terrain/SplatDatabase.h
new file mode 100644
index 0000000..d96d9f2
--- /dev/null
+++ b/Runtime/Terrain/SplatDatabase.h
@@ -0,0 +1,111 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_TERRAIN
+
+#include "Runtime/Math/Vector2.h"
+#include "Heightmap.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+
+using std::vector;
+class Texture2D;
+class ColorRGBAf;
+class TerrainData;
+
+struct SplatPrototype
+{
+ DECLARE_SERIALIZE (SplatPrototype)
+ PPtr<Texture2D> texture;
+ PPtr<Texture2D> normalMap;
+ Vector2f tileSize;
+ Vector2f tileOffset;
+
+ SplatPrototype ();
+};
+
+template<class TransferFunc>
+void SplatPrototype::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (texture);
+ TRANSFER (normalMap);
+ TRANSFER (tileSize);
+ TRANSFER (tileOffset);
+}
+
+class SplatDatabase {
+public:
+ DECLARE_SERIALIZE (SplatDatabase)
+
+ SplatDatabase (TerrainData *owner);
+ ~SplatDatabase();
+
+ void AwakeFromLoad (AwakeFromLoadMode mode);
+
+ void Init (int splatResolution, int basemapResolution);
+ void GetSplatTextures (vector<Texture2D*> *dest);
+
+ int GetAlphamapResolution () { return m_AlphamapResolution; }
+ int GetBaseMapResolution () { return m_BaseMapResolution; }
+ int GetDepth () const {return m_Splats.size (); }
+ void SetAlphamapResolution (int res);
+ void SetBaseMapResolution (int res);
+
+ // Extract a copy of the alpha map in the given area
+ void GetAlphamaps (int xBase, int yBase, int width, int height, float* buffer);
+ // Set alpha map in the given area
+ void SetAlphamaps (int xBase, int yBase, int width, int height, float* buffer);
+ // Extract whole alpha map, as byte weights
+ void GetAlphamaps (dynamic_array<UInt8>& buffer);
+
+ // NOT IMPLEMENTED void SetResolution (int width, int height);
+
+
+ Texture2D *GetAlphaTexture (int index);
+ int GetAlphaTextureCount () { return m_AlphaTextures.size(); }
+
+ const vector<SplatPrototype> &GetSplatPrototypes () const { return m_Splats; }
+ void SetSplatPrototypes (const vector<SplatPrototype> &splats );
+
+ Texture2D* GetBasemap();
+ void UploadBasemap();
+ bool RecalculateBasemapIfDirty();
+ void RecalculateBasemap(bool allowUpload);
+ void SetBasemapDirty(bool dirty) { m_BaseMapDirty = dirty; }
+
+private:
+
+ Texture2D *AllocateAlphamap (const ColorRGBAf &color);
+
+ vector<SplatPrototype> m_Splats;
+ vector<PPtr<Texture2D> > m_AlphaTextures;
+ Texture2D* m_BaseMap;
+ TerrainData* m_TerrainData;
+ int m_AlphamapResolution;
+ int m_BaseMapResolution;
+ bool m_BaseMapDirty;
+};
+
+template<class TransferFunc>
+void SplatDatabase::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (m_Splats);
+ TRANSFER (m_AlphaTextures);
+ TRANSFER (m_AlphamapResolution);
+ TRANSFER (m_BaseMapResolution);
+}
+
+
+struct MonoSplatPrototype
+{
+ ScriptingObjectPtr texture;
+ ScriptingObjectPtr normalMap;
+ Vector2f tileSize;
+ Vector2f tileOffset;
+};
+
+
+void SplatPrototypeToMono (const SplatPrototype &src, MonoSplatPrototype &dest);
+void SplatPrototypeToCpp (MonoSplatPrototype &src, SplatPrototype &dest);
+
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/SplatMaterials.cpp b/Runtime/Terrain/SplatMaterials.cpp
new file mode 100644
index 0000000..3a3acb7
--- /dev/null
+++ b/Runtime/Terrain/SplatMaterials.cpp
@@ -0,0 +1,228 @@
+#include "UnityPrefix.h"
+#include "SplatMaterials.h"
+
+#if ENABLE_TERRAIN
+
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+
+namespace SplatMaterials_Static
+{
+static SHADERPROP(MainTex);
+static SHADERPROP(Control);
+} // namespace SplatMaterials_Static
+
+static const char* kSplatNameStrings[4] = {"_Splat0","_Splat1","_Splat2","_Splat3"};
+static const char* kSplatNormalNameStrings[4] = {"_Normal0","_Normal1","_Normal2","_Normal3"};
+
+
+SplatMaterials::SplatMaterials (PPtr<TerrainData> terrain)
+: m_CurrentTemplateMaterial(NULL)
+{
+ m_TerrainData = terrain;
+ for(int i=0;i<kTerrainShaderCount;i++)
+ m_Shaders[i] = NULL;
+ m_BaseMapMaterial = NULL;
+ for(int i = 0; i < 32; i++)
+ m_AllocatedMaterials[i] = NULL;
+}
+
+SplatMaterials::~SplatMaterials ()
+{
+ Cleanup ();
+}
+
+void SplatMaterials::Cleanup ()
+{
+ for (int s = 0; s < ARRAY_SIZE(m_Shaders); ++s)
+ {
+ m_Shaders[s] = NULL;
+ }
+ for (int x = 0; x < ARRAY_SIZE(m_AllocatedMaterials); ++x)
+ {
+ DestroySingleObject (m_AllocatedMaterials[x]);
+ m_AllocatedMaterials[x] = NULL;
+ }
+ DestroySingleObject (m_BaseMapMaterial);
+ m_BaseMapMaterial = NULL;
+ m_CurrentTemplateMaterial = NULL;
+}
+
+
+
+void SplatMaterials::LoadSplatShaders (Material* templateMat)
+{
+ // If material is already created, and our template material is still the same:
+ // nothing to do
+ if ((m_Shaders[0] != NULL) && (templateMat == m_CurrentTemplateMaterial))
+ return;
+
+ // Material template has changed; recreate internal materials
+ if (templateMat != m_CurrentTemplateMaterial)
+ {
+ Cleanup();
+ m_CurrentTemplateMaterial = templateMat;
+ }
+
+ Shader* templateShader = NULL;
+ if (templateMat)
+ templateShader = templateMat->GetShader();
+ if (templateShader)
+ {
+ // Shader in the template is for the first pass;
+ // additive pass and basemap are queried from dependencies
+ m_Shaders[kTerrainShaderFirst] = templateShader;
+ m_Shaders[kTerrainShaderAdd] = templateShader->GetDependency("AddPassShader");
+ m_Shaders[kTerrainShaderBaseMap] = templateShader->GetDependency("BaseMapShader");
+ }
+
+ ScriptMapper& sm = GetScriptMapper ();
+
+ // Note: No good reason to keep "Lightmap-" in the built-in terrain shader names, except
+ // to be able to run Unity 2.x content on regression rig with 3.0 player. The shader
+ // names just have to match _some_ existing built-in shader in Unity 2.x.
+
+ if (m_Shaders[kTerrainShaderBaseMap] == NULL)
+ m_Shaders[kTerrainShaderBaseMap] = sm.FindShader("Diffuse");
+ if (m_Shaders[kTerrainShaderFirst] == NULL)
+ m_Shaders[kTerrainShaderFirst] = sm.FindShader("Hidden/TerrainEngine/Splatmap/Lightmap-FirstPass");
+ if (m_Shaders[kTerrainShaderAdd] == NULL)
+ m_Shaders[kTerrainShaderAdd] = sm.FindShader("Hidden/TerrainEngine/Splatmap/Lightmap-AddPass");
+
+ bool shaderNotFound = false;
+ for (int i = kTerrainShaderFirst; i <= kTerrainShaderAdd; ++i)
+ {
+ if (m_Shaders[i] == NULL)
+ {
+ shaderNotFound = true;
+ m_Shaders[i] = sm.FindShader("Diffuse");
+ }
+ }
+
+ if (shaderNotFound)
+ {
+ ErrorString("Unable to find shaders used for the terrain engine. Please include Nature/Terrain/Diffuse shader in Graphics settings.");
+ }
+}
+
+
+
+
+Material *SplatMaterials::GetSplatBaseMaterial (Material* templateMat)
+{
+ using namespace SplatMaterials_Static;
+
+ LoadSplatShaders (templateMat);
+
+ Material *mat = m_BaseMapMaterial;
+ if (mat && mat->GetTexture(kSLPropMainTex) == NULL)
+ {
+ DestroySingleObject( mat );
+ mat = NULL;
+ }
+ if (!mat) {
+ mat = Material::CreateMaterial (*m_Shaders[kTerrainShaderBaseMap], Object::kHideAndDontSave);
+ mat->SetTexture( kSLPropMainTex, m_TerrainData->GetSplatDatabase().GetBasemap() );
+ m_BaseMapMaterial = mat;
+ }
+ if (templateMat)
+ {
+ mat->CopyPropertiesFromMaterial (*templateMat);
+ mat->SetTexture (kSLPropMainTex, m_TerrainData->GetSplatDatabase().GetBasemap());
+ }
+ return mat;
+}
+
+Material **SplatMaterials::GetMaterials (Material* templateMat, int &materialCount)
+{
+ using namespace SplatMaterials_Static;
+
+ TerrainData *terrainData = m_TerrainData;
+ LoadSplatShaders (templateMat);
+
+ const bool setNormalMaps = IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1);
+
+ int materialIndex = -1;
+ Material *splatMaterial = NULL;
+ int actualNbSplats = terrainData->GetSplatDatabase().GetDepth();
+ int nbSplats = actualNbSplats>1?actualNbSplats: 1;
+ for( int i = 0; i < nbSplats; ++i )
+ {
+ if (i / 4 != materialIndex)
+ {
+ materialIndex = i / 4;
+
+ splatMaterial = m_AllocatedMaterials[materialIndex];
+ if (splatMaterial == NULL)
+ {
+ const int shaderIndex = (materialIndex != 0) ? kTerrainShaderAdd : kTerrainShaderFirst;
+ Shader* shader = m_Shaders[shaderIndex];
+ splatMaterial = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+ splatMaterial->SetCustomRenderQueue(splatMaterial->GetActualRenderQueue() + materialIndex);
+ m_AllocatedMaterials[materialIndex] = splatMaterial;
+ }
+
+ if (templateMat)
+ splatMaterial->CopyPropertiesFromMaterial (*templateMat);
+
+ if( splatMaterial->HasProperty(kSLPropMainTex) )
+ splatMaterial->SetTexture(kSLPropMainTex, terrainData->GetSplatDatabase().GetBasemap() );
+
+ // As soon as our shader does not support 4 splats per pass,
+ // that means (at least currently) it's a single pass base map.
+ // So stop adding materials.
+ if( splatMaterial->GetTag("SplatCount",false,"") != "4" )
+ break;
+ }
+
+ int localSplatIndex = i - (materialIndex * 4);
+ if (materialIndex < actualNbSplats)
+ {
+ splatMaterial->SetTexture( kSLPropControl, terrainData->GetSplatDatabase().GetAlphaTexture(materialIndex));
+ }
+ else
+ splatMaterial->SetTexture( kSLPropControl, NULL);
+ SetupSplat(*splatMaterial, localSplatIndex, i, setNormalMaps);
+ }
+
+ materialCount = materialIndex + 1;
+
+ return m_AllocatedMaterials;
+}
+
+
+void SplatMaterials::SetupSplat (Material &m, int splatIndex, int index, bool setNormalMap)
+{
+ ShaderLab::FastPropertyName slName = ShaderLab::Property(kSplatNameStrings[splatIndex]);
+ ShaderLab::FastPropertyName slNormalName = ShaderLab::Property(kSplatNormalNameStrings[splatIndex]);
+ const bool hasTex = m.HasProperty(slName);
+ const bool hasNormalMap = setNormalMap && m.HasProperty(slNormalName);
+
+ if (index < m_TerrainData->GetSplatDatabase().GetDepth())
+ {
+ const SplatPrototype& splat = m_TerrainData->GetSplatDatabase().GetSplatPrototypes()[index];
+
+ Vector3f heightmapSize = m_TerrainData->GetHeightmap().GetSize();
+ Vector2f splatScale(heightmapSize.x / splat.tileSize.x, heightmapSize.z / splat.tileSize.y);
+ Vector2f splatOffset(splat.tileOffset.x / heightmapSize.x * splatScale.x, splat.tileOffset.y / heightmapSize.z * splatScale.y);
+
+ if (hasTex)
+ {
+ m.SetTexture (slName, splat.texture);
+ m.SetTextureScale (slName, splatScale);
+ m.SetTextureOffset (slName, splatOffset);
+ }
+ if (hasNormalMap)
+ m.SetTexture (slNormalName, splat.normalMap);
+ }
+ else
+ {
+ if (hasTex)
+ m.SetTexture (slName, NULL);
+ if (hasNormalMap)
+ m.SetTexture (slNormalName, NULL);
+ }
+}
+
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/SplatMaterials.h b/Runtime/Terrain/SplatMaterials.h
new file mode 100644
index 0000000..27b6696
--- /dev/null
+++ b/Runtime/Terrain/SplatMaterials.h
@@ -0,0 +1,41 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_TERRAIN
+
+#include "TerrainData.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Shaders/Shader.h"
+
+enum{
+ kTerrainShaderBaseMap,
+ kTerrainShaderFirst,
+ kTerrainShaderAdd,
+ kTerrainShaderCount
+};
+
+
+class SplatMaterials
+{
+public:
+ SplatMaterials (PPtr<TerrainData> terrain);
+ ~SplatMaterials ();
+
+ Material** GetMaterials (Material* templateMat, int &materialCount);
+ Material* GetSplatBaseMaterial (Material* templateMat);
+ void Cleanup ();
+
+private:
+ void LoadSplatShaders (Material* templateMat);
+ void SetupSplat (Material &m, int splatIndex, int index, bool setNormalMap);
+
+private:
+ PPtr<TerrainData> m_TerrainData;
+ Shader *m_Shaders[kTerrainShaderCount];
+
+ Material* m_AllocatedMaterials[32];
+ Material* m_BaseMapMaterial;
+ Material* m_CurrentTemplateMaterial;
+};
+
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/TerrainData.cpp b/Runtime/Terrain/TerrainData.cpp
new file mode 100644
index 0000000..ef9d934
--- /dev/null
+++ b/Runtime/Terrain/TerrainData.cpp
@@ -0,0 +1,169 @@
+#include "UnityPrefix.h"
+#include "TerrainData.h"
+
+#if ENABLE_TERRAIN
+
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "TerrainManager.h"
+#if UNITY_EDITOR
+#include "Runtime/Serialize/PersistentManager.h"
+#endif
+
+IMPLEMENT_CLASS_HAS_INIT (TerrainData)
+IMPLEMENT_OBJECT_SERIALIZE(TerrainData)
+
+PROFILER_INFORMATION(gAwakeFromLoadTerrain, "TerrainData.AwakeFromLoad", kProfilerLoading)
+
+TerrainData::TerrainData(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode), m_Heightmap (this), m_TreeDatabase(*this), m_DetailDatabase(this, &m_TreeDatabase), m_SplatDatabase(this)
+{
+}
+
+TerrainData::~TerrainData ()
+{
+ UpdateUsers(kWillBeDestroyed);
+}
+
+void TerrainData::InitializeClass ()
+{
+#if UNITY_EDITOR
+ GetPersistentManager().AddNonTextSerializedClass (ClassID (TerrainData));
+#endif
+ TerrainManager::InitializeClass();
+}
+
+void TerrainData::CleanupClass ()
+{
+ TerrainManager::CleanupClass();
+}
+
+SplatDatabase &TerrainData::GetSplatDatabase ()
+{
+ return m_SplatDatabase;
+}
+
+DetailDatabase &TerrainData::GetDetailDatabase ()
+{
+ return m_DetailDatabase;
+}
+
+void TerrainData::ExtractPreloadShaders (vector<PPtr<Object> >& shaders)
+{
+ ScriptMapper& sm = GetScriptMapper ();
+ shaders.push_back(sm.FindShader("Hidden/TerrainEngine/BillboardTree"));
+ shaders.push_back(sm.FindShader("Hidden/TerrainEngine/Details/BillboardWavingDoublePass"));
+ shaders.push_back(sm.FindShader("Hidden/TerrainEngine/Details/Vertexlit"));
+ shaders.push_back(sm.FindShader("Hidden/TerrainEngine/Details/WavingDoublePass"));
+ shaders.push_back(sm.FindShader("Hidden/Nature/Tree Soft Occlusion Leaves Rendertex"));
+ shaders.push_back(sm.FindShader("Hidden/Nature/Tree Soft Occlusion Bark Rendertex"));
+ shaders.push_back(sm.FindShader("Hidden/TerrainEngine/Details/Vertexlit"));
+
+ shaders.push_back(sm.FindShader("Diffuse"));
+ shaders.push_back(sm.FindShader("Hidden/TerrainEngine/Splatmap/Lightmap-FirstPass"));
+ shaders.push_back(sm.FindShader("Hidden/TerrainEngine/Splatmap/Lightmap-AddPass"));
+
+ for (int i=0;i<shaders.size();i++)
+ {
+ if (!shaders[i].IsValid())
+ {
+ ErrorString("Terrain preloaded shaders could not be found");
+ }
+ }
+}
+
+void TerrainData::AwakeFromLoadThreaded ()
+{
+ Super::AwakeFromLoadThreaded();
+ m_SplatDatabase.RecalculateBasemap(false);
+ m_DetailDatabase.SetDetailPrototypesDirty();
+ m_DetailDatabase.GenerateTextureAtlasThreaded();
+}
+
+void TerrainData::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ PROFILER_AUTO(gAwakeFromLoadTerrain, this)
+
+ Super::AwakeFromLoad(awakeMode);
+ m_SplatDatabase.AwakeFromLoad(awakeMode);
+ m_DetailDatabase.SetDetailPrototypesDirty();
+
+ if (awakeMode & kDidLoadThreaded)
+ m_DetailDatabase.UpdateDetailPrototypesIfDirty();
+
+ m_TreeDatabase.RefreshPrototypes();
+
+ UpdateUsers(kFlushEverythingImmediately);
+
+ m_Heightmap.AwakeFromLoad();
+
+ // Just upload image - threaded basemap calculation already done
+ if (awakeMode & kDidLoadThreaded)
+ {
+ m_SplatDatabase.UploadBasemap();
+ }
+ // Do full recalculation
+ else
+ {
+ m_SplatDatabase.RecalculateBasemapIfDirty();
+ }
+}
+
+template<class TransferFunc>
+void TerrainData::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer(transfer);
+
+ transfer.Transfer (m_SplatDatabase, "m_SplatDatabase", kHideInEditorMask);
+ transfer.Transfer (m_DetailDatabase, "m_DetailDatabase", kHideInEditorMask);
+ transfer.Transfer (m_Heightmap, "m_Heightmap", kHideInEditorMask);
+
+#if UNITY_EDITOR
+ // Are we collecting all references for preloading?
+ if ((transfer.GetFlags () & kBuildPlayerOnlySerializeBuildProperties) && transfer.IsRemapPPtrTransfer())
+ {
+ vector<PPtr<Object> > preloadShader;
+ ExtractPreloadShaders(preloadShader);
+ TRANSFER(preloadShader);
+ }
+#endif
+}
+
+bool TerrainData::HasUser (GameObject *user) const
+{
+ return m_Users.find(user) != m_Users.end();
+}
+
+void TerrainData::AddUser (GameObject *user)
+{
+ m_Users.insert (user);
+}
+
+void TerrainData::RemoveUser (GameObject *user)
+{
+ m_Users.erase (user);
+}
+
+void TerrainData::UpdateUsers (ChangedFlags changedFlag)
+{
+ for (std::set<PPtr<GameObject> >::iterator i = m_Users.begin(); i != m_Users.end(); i++)
+ {
+ GameObject *go = *i;
+ if (go)
+ go->SendMessage(kTerrainChanged, (int)changedFlag, ClassID (int));
+ }
+}
+
+void TerrainData::SetLightmapIndexOnUsers(int lightmapIndex)
+{
+ for (std::set<PPtr<GameObject> >::iterator i = m_Users.begin(); i != m_Users.end(); i++)
+ {
+ GameObject *go = *i;
+ if (go)
+ go->SendMessage(kSetLightmapIndex, (int)lightmapIndex, ClassID (int));
+ }
+}
+
+#endif // ENABLE_TERRAIN
+
diff --git a/Runtime/Terrain/TerrainData.h b/Runtime/Terrain/TerrainData.h
new file mode 100644
index 0000000..3851c8c
--- /dev/null
+++ b/Runtime/Terrain/TerrainData.h
@@ -0,0 +1,66 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_TERRAIN
+
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+
+#include "SplatDatabase.h"
+#include "DetailDatabase.h"
+#include "Heightmap.h"
+#include "TreeDatabase.h"
+#include "Runtime/Utilities/NonCopyable.h"
+
+
+class TerrainData : public NamedObject, NonCopyable
+{
+ public:
+ REGISTER_DERIVED_CLASS(TerrainData, NamedObject)
+ DECLARE_OBJECT_SERIALIZE(TerrainData)
+
+ TerrainData (MemLabelId label, ObjectCreationMode mode);
+
+ SplatDatabase &GetSplatDatabase ();
+ DetailDatabase &GetDetailDatabase ();
+ Heightmap &GetHeightmap () { return m_Heightmap; }
+ TreeDatabase& GetTreeDatabase () { return m_TreeDatabase; }
+
+ bool HasUser (GameObject *user) const;
+ void AddUser (GameObject *user);
+ void RemoveUser (GameObject *user);
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ enum ChangedFlags
+ {
+ kNoChange = 0,
+ kHeightmap = 1,
+ kTreeInstances = 2,
+ kDelayedHeightmapUpdate = 4,
+ kFlushEverythingImmediately = 8,
+ kRemoveDirtyDetailsImmediately = 16,
+ kWillBeDestroyed = 256,
+ };
+
+ // Sends a callback to any users of this terrainsData (typically C# Terrain objects) so they can update their renderers, etc.
+ void UpdateUsers (ChangedFlags changedFlag);
+ void SetLightmapIndexOnUsers(int lightmapIndex);
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+ void AwakeFromLoadThreaded();
+
+ void ExtractPreloadShaders (vector<PPtr<Object> >& shaders);
+
+ private:
+ SplatDatabase m_SplatDatabase;
+ TreeDatabase m_TreeDatabase;
+ DetailDatabase m_DetailDatabase;
+ Heightmap m_Heightmap;
+ std::set<PPtr<GameObject> > m_Users; // List of terrains for the client callbacks
+};
+ENUM_FLAGS(TerrainData::ChangedFlags);
+
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/TerrainIndexGenerator.cpp b/Runtime/Terrain/TerrainIndexGenerator.cpp
new file mode 100644
index 0000000..bcbc8d3
--- /dev/null
+++ b/Runtime/Terrain/TerrainIndexGenerator.cpp
@@ -0,0 +1,379 @@
+#include "UnityPrefix.h"
+#include "TerrainIndexGenerator.h"
+
+#if ENABLE_TERRAIN
+
+#include "Runtime/Graphics/TriStripper.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Terrain/Heightmap.h"
+
+static unsigned int AddSliverTriangles (unsigned int *triangles, unsigned int index, int direction, int edgeMask);
+static unsigned int AddSliverCorner (unsigned int* triangles, unsigned int index, int direction, int edgeMask);
+static void FlipTriangle (unsigned int *triangles, unsigned int index);
+static unsigned int AddQuad (unsigned int *triangles, unsigned int index, int xBase, int yBase);
+
+
+struct CachedStrip {
+ unsigned int count;
+ unsigned short* triangles;
+
+ CachedStrip() {count = 0; triangles = NULL;}
+ ~CachedStrip() { if(triangles) delete[] triangles;}
+};
+
+static CachedStrip gCachedStrips[16];
+
+unsigned int *TerrainIndexGenerator::GetIndexBuffer (int edgeMask, unsigned int &count, int stride)
+{
+ unsigned int *triangles = new unsigned int[(kPatchSize) * (kPatchSize) * 6];
+ unsigned int index = 0;
+ int size = kPatchSize;
+
+ int minX = 0;
+ int minY = 0;
+ int maxX = kPatchSize-1;
+ int maxY = kPatchSize-1;
+
+ if((edgeMask & kDirectionLeftFlag) == 0)
+ {
+ minX+=1;
+ index = AddSliverTriangles (triangles, index, kDirectionLeft, edgeMask);
+ }
+ if((edgeMask & kDirectionRightFlag) == 0)
+ {
+ maxX-=1;
+ index = AddSliverTriangles (triangles, index, kDirectionRight, edgeMask);
+ }
+ if((edgeMask & kDirectionUpFlag) == 0)
+ {
+ maxY-=1;
+ index = AddSliverTriangles (triangles, index, kDirectionUp, edgeMask);
+ }
+ if((edgeMask & kDirectionDownFlag) == 0)
+ {
+ minY+=1;
+ index = AddSliverTriangles (triangles, index, kDirectionDown, edgeMask);
+ }
+
+ if((edgeMask & kDirectionLeftFlag) == 0 || (edgeMask & kDirectionUpFlag) == 0)
+ index = AddSliverCorner (triangles, index, kDirectionLeftUp, edgeMask);
+ if((edgeMask & kDirectionRightFlag) == 0 || (edgeMask & kDirectionUpFlag) == 0)
+ index = AddSliverCorner (triangles, index, kDirectionRightUp, edgeMask);
+ if((edgeMask & kDirectionLeftFlag) == 0 || (edgeMask & kDirectionDownFlag) == 0)
+ index = AddSliverCorner (triangles, index, kDirectionLeftDown, edgeMask);
+ if((edgeMask & kDirectionRightFlag) == 0 || (edgeMask & kDirectionDownFlag) == 0)
+ index = AddSliverCorner (triangles, index, kDirectionRightDown, edgeMask);
+
+ for (int y=minY;y<maxY;y++)
+ {
+ for (int x=minX;x<maxX;x++)
+ {
+ // For each grid cell output two triangles
+ triangles[index++] = y + (x * size);
+ triangles[index++] = (y+1) + x * size;
+ triangles[index++] = (y+1) + (x + 1) * size;
+
+ triangles[index++] = y + x * size;
+ triangles[index++] = (y+1) + (x + 1) * size;
+ triangles[index++] = y + (x + 1) * size;
+ }
+ }
+
+ count = index;
+ return triangles;
+}
+
+unsigned short *TerrainIndexGenerator::GetOptimizedIndexStrip (int edgeMask, unsigned int &count)
+{
+ edgeMask &= kDirectionDirectNeighbourMask;
+ if (gCachedStrips[edgeMask].triangles == NULL)
+ {
+ unsigned int *triangles = GetIndexBuffer (edgeMask, count, 0);
+
+ Mesh::TemporaryIndexContainer newStrip;
+
+ Stripify ((const UInt32*)triangles, count, newStrip);
+
+ delete[] triangles;
+
+ count = newStrip.size();
+ unsigned short *strip = new unsigned short[count];
+ for(int i=0;i<count;i++)
+ strip[i] = newStrip[i];
+ gCachedStrips[edgeMask].count = count;
+ gCachedStrips[edgeMask].triangles = strip;
+ }
+
+ count = gCachedStrips[edgeMask].count;
+ return gCachedStrips[edgeMask].triangles;
+}
+
+static void FlipTriangle (unsigned int *triangles, unsigned int index)
+{
+ int temp = triangles[index];
+ triangles[index] = triangles[index+1];
+ triangles[index+1] = temp;
+}
+
+static unsigned int AddQuad (unsigned int *triangles, unsigned int index, int xBase, int yBase)
+{
+ triangles[index++] = (xBase + 0) * kPatchSize + (yBase + 0);
+ triangles[index++] = (xBase + 0) * kPatchSize + (yBase + 1);
+ triangles[index++] = (xBase + 1) * kPatchSize + (yBase + 1);
+
+ triangles[index++] = (xBase + 0) * kPatchSize + (yBase + 0);
+ triangles[index++] = (xBase + 1) * kPatchSize + (yBase + 1);
+ triangles[index++] = (xBase + 1) * kPatchSize + (yBase + 0);
+
+ return index;
+}
+
+static unsigned int AddSliverCorner (unsigned int* triangles, unsigned int index, int direction, int edgeMask)
+{
+ int xBase, yBase, ox, oy;
+ bool flip = false;
+
+ int vMask = 0;
+ int hMask = 0;
+
+ if (direction == kDirectionLeftDown)
+ {
+ xBase = 1;
+ yBase = 1;
+ ox = 1;
+ oy = 1;
+ flip = false;
+
+ hMask = 1 << kDirectionLeft;
+ vMask = 1 << kDirectionDown;
+ }
+ else if (direction == kDirectionRightDown)
+ {
+ xBase = kPatchSize-2;
+ yBase = 1;
+ ox = -1;
+ oy = 1;
+ flip = true;
+
+ hMask = 1 << kDirectionRight;
+ vMask = 1 << kDirectionDown;
+ }
+ else if (direction == kDirectionLeftUp)
+ {
+ xBase = 1;
+ yBase = kPatchSize-2;
+ ox = 1;
+ oy = -1;
+ flip = true;
+
+ hMask = 1 << kDirectionLeft;
+ vMask = 1 << kDirectionUp;
+ }
+ else
+ {
+ xBase = kPatchSize-2;
+ yBase = kPatchSize-2;
+ ox = -1;
+ oy = -1;
+ flip = false;
+
+ hMask = 1 << kDirectionRight;
+ vMask = 1 << kDirectionUp;
+ }
+
+ int mask = 0;
+ if ((hMask & edgeMask) != 0)
+ mask |= 1;
+ if ((vMask & edgeMask) != 0)
+ mask |= 2;
+
+ // Both edges are tesselated
+ // Vertical edge is tesselated
+ if (mask == 1)
+ {
+ // Stitch big down and small up
+ triangles[index++] = (xBase + 0) * kPatchSize + (yBase + 0);
+ triangles[index++] = (xBase - ox) * kPatchSize + (yBase - oy);
+ triangles[index++] = (xBase - ox) * kPatchSize + (yBase + 0);
+
+ // rigth up small
+ triangles[index++] = (xBase + ox) * kPatchSize + (yBase - oy);
+ triangles[index++] = (xBase + 0) * kPatchSize + (yBase + 0);
+ triangles[index++] = (xBase + ox) * kPatchSize + (yBase + 0);
+
+ // Down Big span
+ triangles[index++] = (xBase + 0) * kPatchSize + (yBase + 0);
+ triangles[index++] = (xBase + ox) * kPatchSize + (yBase - oy);
+ triangles[index++] = (xBase - ox) * kPatchSize + (yBase - oy);
+
+ if (flip)
+ {
+ FlipTriangle(triangles, index - 9);
+ FlipTriangle(triangles, index - 6);
+ FlipTriangle(triangles, index - 3);
+ }
+
+ }
+ // Horizontal edge is tesselated
+ else if (mask == 2)
+ {
+ // Left up small
+ triangles[index++] = (xBase + 0) * kPatchSize + (yBase + oy);
+ triangles[index++] = (xBase + 0) * kPatchSize + (yBase + 0);
+ triangles[index++] = (xBase - ox) * kPatchSize + (yBase + oy);
+
+ // Left Big span
+ triangles[index++] = (xBase + 0) * kPatchSize + (yBase + 0);
+ triangles[index++] = (xBase - ox) * kPatchSize + (yBase - oy);
+ triangles[index++] = (xBase - ox) * kPatchSize + (yBase + oy);
+
+ // Stitch right-down and big left span
+ triangles[index++] = (xBase - ox) * kPatchSize + (yBase - oy);
+ triangles[index++] = (xBase + 0) * kPatchSize + (yBase + 0);
+ triangles[index++] = (xBase + 0) * kPatchSize + (yBase - oy);
+
+ if (flip)
+ {
+ FlipTriangle(triangles, index - 9);
+ FlipTriangle(triangles, index - 6);
+ FlipTriangle(triangles, index - 3);
+ }
+
+ }
+ // Nothing tesselated
+ else
+ {
+ // Left up small
+ triangles[index++] = (xBase + 0) * kPatchSize + (yBase + 0);
+ triangles[index++] = (xBase - ox) * kPatchSize + (yBase + oy);
+ triangles[index++] = (xBase + 0) * kPatchSize + (yBase + oy);
+ // right up small
+ triangles[index++] = (xBase + ox) * kPatchSize + (yBase - oy);
+ triangles[index++] = (xBase + 0) * kPatchSize + (yBase + 0);
+ triangles[index++] = (xBase + ox) * kPatchSize + (yBase + 0);
+ // Left Big span
+ triangles[index++] = (xBase + 0) * kPatchSize + (yBase + 0);
+ triangles[index++] = (xBase - ox) * kPatchSize + (yBase - oy);
+ triangles[index++] = (xBase - ox) * kPatchSize + (yBase + oy);
+ // Down Big span
+ triangles[index++] = (xBase + 0) * kPatchSize + (yBase + 0);
+ triangles[index++] = (xBase + ox) * kPatchSize + (yBase - oy);
+ triangles[index++] = (xBase - ox) * kPatchSize + (yBase - oy);
+
+ if (flip)
+ {
+ FlipTriangle(triangles, index - 12);
+ FlipTriangle(triangles, index - 9);
+ FlipTriangle(triangles, index - 6);
+ FlipTriangle(triangles, index - 3);
+ }
+ }
+
+ return index;
+}
+
+static unsigned int AddSliverTriangles (unsigned int *triangles, unsigned int index, int direction, int edgeMask)
+{
+ int directionMask = 1 << direction;
+ if ((edgeMask & directionMask) != 0)
+ {
+ for (int y=2;y<kPatchSize-3;y++)
+ {
+ if (direction == kDirectionLeft)
+ index = AddQuad(triangles, index, 0, y);
+ else if (direction == kDirectionRight)
+ index = AddQuad(triangles, index, kPatchSize - 2, y);
+ else if (direction == kDirectionUp)
+ index = AddQuad(triangles, index, y, kPatchSize - 2);
+ else if (direction == kDirectionDown)
+ index = AddQuad(triangles, index, y, 0);
+ }
+ }
+ else
+ {
+ for (int i=2;i<kPatchSize-3;i+=2)
+ {
+ if (direction == kDirectionLeft)
+ {
+ int x = 0;
+ int y = i;
+
+ // fixup bottom
+ triangles[index++] = (x + 1) * kPatchSize + (y + 0);
+ triangles[index++] = (x + 0) * kPatchSize + (y + 0);
+ triangles[index++] = (x + 1) * kPatchSize + (y + 1);
+
+ // Big span
+ triangles[index++] = (x + 0) * kPatchSize + (y + 0);
+ triangles[index++] = (x + 0) * kPatchSize + (y + 2);
+ triangles[index++] = (x + 1) * kPatchSize + (y + 1);
+
+ // fixup top
+ triangles[index++] = (x + 0) * kPatchSize + (y + 2);
+ triangles[index++] = (x + 1) * kPatchSize + (y + 2);
+ triangles[index++] = (x + 1) * kPatchSize + (y + 1);
+ }
+ else if (direction == kDirectionRight)
+ {
+ int x = kPatchSize - 1;
+ int y = i;
+
+ // fixup bottom
+ triangles[index++] = (x - 1) * kPatchSize + (y + 0);
+ triangles[index++] = (x - 1) * kPatchSize + (y + 1);
+ triangles[index++] = (x - 0) * kPatchSize + (y + 0);
+
+ // Big span
+ triangles[index++] = (x - 0) * kPatchSize + (y + 0);
+ triangles[index++] = (x - 1) * kPatchSize + (y + 1);
+ triangles[index++] = (x - 0) * kPatchSize + (y + 2);
+
+ // fixup top
+ triangles[index++] = (x - 0) * kPatchSize + (y + 2);
+ triangles[index++] = (x - 1) * kPatchSize + (y + 1);
+ triangles[index++] = (x - 1) * kPatchSize + (y + 2);
+ }
+ else if (direction == kDirectionDown)
+ {
+ int x = i;
+ int y = 0;
+
+ // fixup bottom
+ triangles[index++] = (x + 0) * kPatchSize + (y + 0);
+ triangles[index++] = (x + 0) * kPatchSize + (y + 1);
+ triangles[index++] = (x + 1) * kPatchSize + (y + 1);
+
+ // Big span
+ triangles[index++] = (x + 1) * kPatchSize + (y + 1);
+ triangles[index++] = (x + 2) * kPatchSize + (y + 0);
+ triangles[index++] = (x + 0) * kPatchSize + (y + 0);
+ // fixup top
+ triangles[index++] = (x + 2) * kPatchSize + (y + 0);
+ triangles[index++] = (x + 1) * kPatchSize + (y + 1);
+ triangles[index++] = (x + 2) * kPatchSize + (y + 1);
+ }
+ else
+ {
+ int x = i;
+ int y = kPatchSize - 1;
+
+ // fixup bottom
+ triangles[index++] = (x + 0) * kPatchSize + (y - 0);
+ triangles[index++] = (x + 1) * kPatchSize + (y - 1);
+ triangles[index++] = (x + 0) * kPatchSize + (y - 1);
+
+ // Big span
+ triangles[index++] = (x + 1) * kPatchSize + (y - 1);
+ triangles[index++] = (x + 0) * kPatchSize + (y - 0);
+ triangles[index++] = (x + 2) * kPatchSize + (y - 0);
+ // fixup top
+ triangles[index++] = (x + 2) * kPatchSize + (y - 0);
+ triangles[index++] = (x + 2) * kPatchSize + (y - 1);
+ triangles[index++] = (x + 1) * kPatchSize + (y - 1);
+ }
+ }
+ }
+ return index;
+}
+
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/TerrainIndexGenerator.h b/Runtime/Terrain/TerrainIndexGenerator.h
new file mode 100644
index 0000000..cf7da96
--- /dev/null
+++ b/Runtime/Terrain/TerrainIndexGenerator.h
@@ -0,0 +1,12 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_TERRAIN
+
+struct TerrainIndexGenerator
+{
+ static unsigned int *GetIndexBuffer (int edgeMask, unsigned int &count, int stride);
+ static unsigned short *GetOptimizedIndexStrip (int edgeMask, unsigned int &count);
+};
+
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/TerrainInstance.cpp b/Runtime/Terrain/TerrainInstance.cpp
new file mode 100644
index 0000000..3438fc1
--- /dev/null
+++ b/Runtime/Terrain/TerrainInstance.cpp
@@ -0,0 +1,344 @@
+#include "UnityPrefix.h"
+#include "TerrainInstance.h"
+
+#if ENABLE_TERRAIN
+#include "Runtime/Terrain/TerrainData.h"
+#include "Runtime/Terrain/TerrainManager.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Camera/Culler.h"
+#include "Runtime/Camera/LightManager.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Camera/RenderSettings.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Input/TimeManager.h"
+
+#include "DetailRenderer.h"
+#include "TreeRenderer.h"
+#include "TerrainRenderer.h"
+
+#include <list>
+
+using namespace std;
+
+TerrainInstance::TerrainInstance (GameObject* go)
+: m_Position(0,0,0)
+, m_GameObject(go)
+, m_HeightmapPixelError(5.0f)
+, m_HeightmapMaximumLOD(0)
+, m_SplatMapDistance(1000.0f)
+, m_TreeDistance(5000.0f)
+, m_TreeBillboardDistance(50.0f)
+, m_TreeCrossFadeLength(5.0f)
+, m_TreeMaximumFullLODCount(50)
+, m_DetailObjectDistance(80.0f)
+, m_DetailObjectDensity(1.0f)
+, m_CastShadows(true)
+, m_DrawTreesAndFoliage(true)
+, m_LeftNeighbor(NULL)
+, m_RightNeighbor(NULL)
+, m_BottomNeighbor(NULL)
+, m_TopNeighbor(NULL)
+, m_EditorRenderFlags(kRenderAll)
+, m_LightmapIndex(-1)
+, m_LightmapSize(1024)
+{
+ Assert(go != NULL);
+}
+
+TerrainInstance::~TerrainInstance()
+{
+ // Determine what to clean up
+}
+
+void TerrainInstance::OnEnable()
+{
+ if (m_TerrainData.IsValid())
+ {
+ Assert(m_GameObject != NULL);
+ m_TerrainData->AddUser(m_GameObject);
+ }
+}
+
+void TerrainInstance::OnDisable()
+{
+ if (m_TerrainData.IsValid())
+ {
+ Assert(m_GameObject != NULL);
+ m_TerrainData->RemoveUser(m_GameObject);
+ }
+}
+
+const Vector3f TerrainInstance::GetPosition() const
+{
+ return m_GameObject->GetComponentT<Transform>(Transform::GetClassIDStatic()).GetPosition();
+}
+
+void TerrainInstance::Flush()
+{
+ for (dynamic_array<Renderer>::iterator i = m_Renderers.begin(); i != m_Renderers.end(); ++i)
+ {
+ UNITY_DELETE ((*i).trees, kMemTerrain);
+ UNITY_DELETE ((*i).terrain, kMemTerrain);
+ UNITY_DELETE ((*i).details, kMemTerrain);
+ }
+
+ m_Renderers.clear();
+}
+
+void TerrainInstance::GarbageCollectRenderers()
+{
+ int frame = GetTimeManager ().GetRenderFrameCount ();
+ // traverse backwards so we can remove renderers
+ dynamic_array<Renderer>::iterator i = m_Renderers.begin();
+ while (i != m_Renderers.end())
+ {
+ int frameDiff = frame - (*i).lastUsedFrame;
+ // cleanup renderers after they are unused for some frames; handle wrap-around just in case
+ // also cleanup immediately when camera is destroyed
+ if( frameDiff > 100 || frameDiff < 0 || (*i).camera == NULL) {
+ UNITY_DELETE((*i).trees, kMemTerrain);
+ UNITY_DELETE((*i).terrain, kMemTerrain);
+ UNITY_DELETE((*i).details, kMemTerrain);
+ i = m_Renderers.erase(i);
+ }
+ else
+ ++i;
+ }
+}
+
+void TerrainInstance::FlushDirty ()
+{
+ bool reloadDetails = false;
+ bool reloadTrees = false;
+
+ // Figure out what we need to recalc depending on what to update.
+ // we build some bool flags so we never do more than one update no matter
+ // what was changed and which dependencies they have of each other.
+ if ((m_DirtyFlags & TerrainData::kHeightmap) != 0)
+ reloadDetails = reloadTrees = true;
+ if ((m_DirtyFlags & TerrainData::kTreeInstances) != 0)
+ reloadTrees = true;
+
+ // Optimized live terrain painting update mode
+ if ((m_DirtyFlags & TerrainData::kDelayedHeightmapUpdate) != 0)
+ {
+ // Reload precomputed error, this will make affected patches reload the vertices!
+ for (dynamic_array<Renderer>::iterator i = m_Renderers.begin(); i != m_Renderers.end(); ++i)
+ (*i).terrain->ReloadPrecomputedError ();
+ }
+
+ if (reloadTrees)
+ {
+ for (dynamic_array<Renderer>::iterator i = m_Renderers.begin(); i != m_Renderers.end(); ++i)
+ (*i).trees->ReloadTrees();
+ }
+
+ if (reloadDetails)
+ {
+ for (dynamic_array<Renderer>::iterator i = m_Renderers.begin(); i != m_Renderers.end(); ++i)
+ (*i).details->ReloadAllDetails ();
+ }
+
+
+ if ((m_DirtyFlags & TerrainData::kHeightmap) != 0)
+ {
+ for (dynamic_array<Renderer>::iterator i = m_Renderers.begin(); i != m_Renderers.end(); ++i)
+ (*i).terrain->ReloadAll();
+ }
+
+ m_DirtyFlags = TerrainData::kNoChange;
+}
+
+const TerrainInstance::Renderer* TerrainInstance::GetRenderer()
+{
+ Camera* cam = GetCurrentCameraPtr();
+
+ if ((cam->GetCullingMask() & (1 << m_GameObject->GetLayer())) == 0)
+ return NULL;
+
+#if UNITY_EDITOR
+ if (!cam->IsFiltered(*m_GameObject))
+ return NULL;
+#endif
+
+ int frame = GetTimeManager ().GetRenderFrameCount ();
+ for (dynamic_array<Renderer>::iterator i = m_Renderers.begin(); i != m_Renderers.end(); ++i)
+ {
+ if (i->camera == cam)
+ {
+ if (i->terrain->GetTerrainData().IsNull())
+ {
+ Flush();
+ break;
+ }
+ i->lastUsedFrame = frame;
+ return &*i;
+ }
+ }
+
+ SET_ALLOC_OWNER(m_GameObject);
+
+ if (m_TerrainData.IsValid())
+ {
+ Vector3f position = GetPosition();
+
+ m_Renderers.resize_uninitialized(m_Renderers.size() + 1, false);
+ Renderer& renderer = m_Renderers.back();
+ renderer.camera = cam;
+ renderer.terrain = UNITY_NEW(TerrainRenderer, kMemTerrain) (m_GameObject->GetInstanceID(), m_TerrainData, position, m_LightmapIndex);
+#if UNITY_EDITOR
+ renderer.terrain->SetLightmapSize(m_LightmapSize);
+#endif
+ renderer.trees = UNITY_NEW(TreeRenderer, kMemTerrain) (m_TerrainData->GetTreeDatabase(), position, m_LightmapIndex);
+ renderer.details = UNITY_NEW(DetailRenderer, kMemTerrain) (m_TerrainData, position, m_LightmapIndex);
+ renderer.lastUsedFrame = frame;
+ return &renderer;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+TerrainRenderer* TerrainInstance::GetTerrainRendererDontCreate()
+{
+ Camera* cam = GetCurrentCameraPtr();
+
+ if ((cam->GetCullingMask() & (1 << m_GameObject->GetLayer())) == 0)
+ return NULL;
+
+ for (dynamic_array<Renderer>::iterator r = m_Renderers.begin(); r != m_Renderers.end(); ++r)
+ {
+ if (r->camera == cam)
+ return r->terrain;
+ }
+
+ return NULL;
+}
+
+void TerrainInstance::SetLightmapIndex(int value)
+{
+ m_LightmapIndex = value;
+ for (dynamic_array<Renderer>::iterator r = m_Renderers.begin(); r != m_Renderers.end(); ++r)
+ {
+ r->terrain->SetLightmapIndex(value);
+ r->trees->SetLightmapIndex(value);
+ r->details->SetLightmapIndex(value);
+ }
+}
+
+void TerrainInstance::InitializeClass()
+{
+}
+
+void TerrainInstance::CleanupClass()
+{
+}
+
+void TerrainInstance::SetDetailObjectDensity(float value)
+{
+ value = ::clamp(value,0.0f,1.0f);
+ bool changed = (value != m_DetailObjectDensity);
+ m_DetailObjectDensity = value;
+ if (changed)
+ {
+ for (dynamic_array<Renderer>::iterator r = m_Renderers.begin(); r != m_Renderers.end(); ++r)
+ r->details->ReloadAllDetails();
+ }
+}
+
+void TerrainInstance::SetLightmapSize(int value)
+{
+#if UNITY_EDITOR
+ m_LightmapSize = value > 0 ? value : 1;
+ for (dynamic_array<Renderer>::iterator r = m_Renderers.begin(); r != m_Renderers.end(); ++r)
+ r->terrain->SetLightmapSize(value);
+#endif
+}
+
+void TerrainInstance::ApplyDelayedHeightmapModification()
+{
+ UNITY_TEMP_VECTOR(int) invalidPatches;
+ m_TerrainData->GetHeightmap().RecomputeInvalidPatches(invalidPatches);
+ if (invalidPatches.size() != 0)
+ {
+ m_TerrainData->GetTreeDatabase().RecalculateTreePositions();
+
+ for (dynamic_array<Renderer>::iterator r = m_Renderers.begin(); r != m_Renderers.end(); ++r)
+ {
+ r->terrain->ReloadPrecomputedError ();
+ r->terrain->ReloadBounds ();
+ r->details->ReloadAllDetails ();
+ }
+ }
+
+}
+
+///TODO: This should be moved to TerrainData. Each TreeRenderer should register when its using a TerrainData with a linked list and when a tree is added,
+/// a callback should be invoked which lets the TreeRenderer ineject the tree into the spatial database
+void TerrainInstance::AddTreeInstance(const TreeInstance& tree)
+{
+ bool hasTrees = !m_TerrainData->GetTreeDatabase().GetInstances().empty();
+ m_TerrainData->GetTreeDatabase().AddTree(tree);
+
+ for (dynamic_array<Renderer>::iterator r = m_Renderers.begin(); r != m_Renderers.end(); ++r)
+ {
+ if (hasTrees)
+ {
+ r->trees->InjectTree (m_TerrainData->GetTreeDatabase().GetInstances().back());
+ }
+ else
+ {
+ delete r->trees;
+ r->trees = new TreeRenderer (m_TerrainData->GetTreeDatabase(), GetPosition(), m_LightmapIndex);
+ }
+ }
+}
+
+void TerrainInstance::RemoveTrees(const Vector2f& position, float radius, int prototypeIndex)
+{
+ int trees = m_TerrainData->GetTreeDatabase().RemoveTrees(position, radius, prototypeIndex);
+ if (trees != 0)
+ {
+ for (dynamic_array<Renderer>::iterator r = m_Renderers.begin(); r != m_Renderers.end(); ++r)
+ {
+ r->trees->RemoveTrees(Vector3f(position[0], position[1], 0.0f), radius, prototypeIndex);
+ }
+ }
+}
+
+void TerrainInstance::OnTerrainChanged(TerrainData::ChangedFlags flags)
+{
+ // Dirty details must be deleted immediately because we are clearing data that stores what was set dirty immediately afterwards.
+ if ((flags & TerrainData::kRemoveDirtyDetailsImmediately) != 0)
+ {
+ for (dynamic_array<Renderer>::iterator r = m_Renderers.begin(); r != m_Renderers.end(); ++r)
+ {
+ r->details->ReloadDirtyDetails();
+ }
+ }
+
+ if ((flags & TerrainData::kFlushEverythingImmediately) != 0)
+ Flush ();
+ else
+ m_DirtyFlags |= flags;
+}
+
+void TerrainInstance::SetNeighbors (TerrainInstance* left, TerrainInstance* top, TerrainInstance* right, TerrainInstance* bottom)
+{
+ m_TopNeighbor = top;
+ m_LeftNeighbor = left;
+ m_RightNeighbor = right;
+ m_BottomNeighbor = bottom;
+}
+
+float TerrainInstance::SampleHeight(Vector3f worldPosition) const
+{
+ worldPosition -= GetPosition();
+ worldPosition.x /= m_TerrainData->GetHeightmap().GetSize().x;
+ worldPosition.z /= m_TerrainData->GetHeightmap().GetSize().z;
+ return m_TerrainData->GetHeightmap().GetInterpolatedHeight(worldPosition.x, worldPosition.z);
+}
+#undef LISTFOREACH
+
+#endif //ENABLE_TERRAIN
diff --git a/Runtime/Terrain/TerrainInstance.h b/Runtime/Terrain/TerrainInstance.h
new file mode 100644
index 0000000..d0ce1cd
--- /dev/null
+++ b/Runtime/Terrain/TerrainInstance.h
@@ -0,0 +1,132 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_TERRAIN
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Terrain/TerrainData.h"
+#include "Runtime/Utilities/NonCopyable.h"
+
+class Camera;
+class DetailRenderer;
+class TerrainManager;
+class TerrainRenderer;
+class TreeRenderer;
+
+class TerrainInstance
+{
+public:
+ enum RenderFlags
+ {
+ kRenderHeightmap = 1,
+ kRenderTrees = 2,
+ kRenderDetails = 4,
+ kRenderAll = kRenderHeightmap | kRenderTrees | kRenderDetails
+ };
+
+ TerrainInstance (GameObject* go);
+ ~TerrainInstance ();
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ void OnEnable ();
+ void OnDisable ();
+ GameObject* GetGameObject() const { return m_GameObject; }
+
+ const Vector3f GetPosition () const;
+ void SetNeighbors (TerrainInstance* left, TerrainInstance* top, TerrainInstance* right, TerrainInstance* bottom);
+
+ void Flush ();
+ void FlushDirty ();
+ void GarbageCollectRenderers ();
+
+ int GetLightmapIndex() const { return m_LightmapIndex; }
+ void SetLightmapIndex (int value);
+
+ const int GetLightmapSize () const { return m_LightmapSize; }
+ void SetLightmapSize (int value);
+
+ GET_SET(PPtr<TerrainData>, TerrainData, m_TerrainData);
+ GET_SET(float, HeightmapPixelError, m_HeightmapPixelError);
+ GET_SET(int, HeightmapMaximumLOD, m_HeightmapMaximumLOD);
+ GET_SET(float, BasemapDistance, m_SplatMapDistance);
+ GET_SET(float, TreeDistance, m_TreeDistance);
+ GET_SET(float, TreeBillboardDistance, m_TreeBillboardDistance);
+ GET_SET(float, TreeCrossFadeLength, m_TreeCrossFadeLength);
+ GET_SET(int, TreeMaximumFullLODCount, m_TreeMaximumFullLODCount);
+ GET_SET(float, DetailObjectDistance, m_DetailObjectDistance);
+ GET_SET(bool, CastShadows, m_CastShadows);
+ GET_SET(bool, DrawTreesAndFoliage, m_DrawTreesAndFoliage);
+ GET_SET(Material*, MaterialTemplate, m_MaterialTemplate);
+ GET_SET(RenderFlags, EditorRenderFlags, m_EditorRenderFlags);
+
+ const float GetDetailObjectDensity () const { return m_DetailObjectDensity; }
+ void SetDetailObjectDensity (float value);
+
+ float SampleHeight (Vector3f worldPosition) const;
+
+ void ApplyDelayedHeightmapModification ();
+
+ void AddTreeInstance (const TreeInstance& tree);
+ void RemoveTrees (const Vector2f& position, float radius, int prototypeIndex);
+
+ void OnTerrainChanged(TerrainData::ChangedFlags flags);
+
+ TerrainRenderer* GetTerrainRendererDontCreate ();
+
+ bool NeedRenderTerrainGeometry() const { return (m_EditorRenderFlags & kRenderHeightmap) != 0; }
+ bool NeedRenderDetails() const { return (m_EditorRenderFlags & kRenderDetails) != 0 && m_DrawTreesAndFoliage && m_DetailObjectDistance > 0.001; }
+ bool NeedRenderTrees() const { return (m_EditorRenderFlags & kRenderTrees) != 0 && m_DrawTreesAndFoliage && m_TreeDistance > 0.001; }
+
+private:
+ friend class TerrainManager;
+
+ struct Renderer
+ {
+ Camera* camera;
+ TerrainRenderer* terrain;
+ TreeRenderer* trees;
+ DetailRenderer* details;
+ int lastUsedFrame;
+
+ Renderer() : camera(NULL), terrain(NULL), trees(NULL), details(NULL), lastUsedFrame(0) {}
+ };
+
+ const Renderer* GetRenderer();
+
+ PPtr<TerrainData> m_TerrainData;
+ Vector3f m_Position;
+
+ GameObject* m_GameObject;
+
+ float m_HeightmapPixelError;
+ int m_HeightmapMaximumLOD;
+ float m_SplatMapDistance;
+ int m_LightmapIndex;
+ int m_LightmapSize;
+ float m_TreeDistance;
+ float m_TreeBillboardDistance;
+ float m_TreeCrossFadeLength;
+ int m_TreeMaximumFullLODCount;
+ float m_DetailObjectDistance;
+ float m_DetailObjectDensity;
+ bool m_CastShadows;
+ bool m_DrawTreesAndFoliage;
+ PPtr<Material> m_MaterialTemplate;
+
+ TerrainInstance* m_LeftNeighbor;
+ TerrainInstance* m_RightNeighbor;
+ TerrainInstance* m_BottomNeighbor;
+ TerrainInstance* m_TopNeighbor;
+
+ RenderFlags m_EditorRenderFlags;
+
+ // Which part of the terrain is dirty
+ TerrainData::ChangedFlags m_DirtyFlags;
+
+ dynamic_array<Renderer> m_Renderers;
+};
+
+#endif \ No newline at end of file
diff --git a/Runtime/Terrain/TerrainManager.cpp b/Runtime/Terrain/TerrainManager.cpp
new file mode 100644
index 0000000..3f3805f
--- /dev/null
+++ b/Runtime/Terrain/TerrainManager.cpp
@@ -0,0 +1,280 @@
+#include "UnityPrefix.h"
+#if ENABLE_TERRAIN
+#include "TerrainManager.h"
+#include "TerrainData.h"
+#include "TerrainInstance.h"
+#include "DetailRenderer.h"
+#include "TreeRenderer.h"
+#include "TerrainRenderer.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Graphics/Transform.h"
+
+#include "Runtime/Camera/LightManager.h"
+using namespace std;
+
+#define LISTFOREACH(TYPE, X, Y) for (UNITY_LIST(kMemRenderer, TYPE)::iterator Y = X.begin(); Y != X.end(); ++Y)
+
+PROFILER_INFORMATION(gTerrainFlushDirty, "TerrainInstance.FlushDirty", kProfilerRender);
+PROFILER_INFORMATION(gTerrainRenderStep1, "Terrain.Heightmap.RenderStep1", kProfilerRender);
+PROFILER_INFORMATION(gTerrainRenderStep2, "Terrain.Heightmap.RenderStep2", kProfilerRender);
+PROFILER_INFORMATION(gTerrainRenderStep3, "Terrain.Heightmap.RenderStep3", kProfilerRender);
+PROFILER_INFORMATION(gTerrainDetailsRender, "Terrain.Details.Render", kProfilerRender);
+PROFILER_INFORMATION(gTerrainTreesRender, "Terrain.Trees.Render", kProfilerRender);
+
+void TerrainManager::InitializeClass ()
+{
+ SetITerrainManager (new TerrainManager());
+}
+
+void TerrainManager::CleanupClass ()
+{
+ TerrainManager* manager = reinterpret_cast<TerrainManager*> (GetITerrainManager());
+ delete manager;
+ SetITerrainManager (NULL);
+}
+
+TerrainManager::TerrainManager()
+: m_ActiveTerrain(NULL)
+{
+}
+
+
+bool TerrainManager::GetInterpolatedHeight (const Object* inTerrainData, const Vector3f& terrainPosition, const Vector3f& position, float& outputHeight)
+{
+ TerrainData* terrainData = dynamic_pptr_cast<TerrainData*> (inTerrainData);
+ if (terrainData == NULL)
+ return false;
+
+ const Vector3f terrainSize (terrainData->GetHeightmap ().GetSize ());
+ const Vector3f localPosition = Scale (position-terrainPosition, Inverse (terrainSize));
+
+ // Outside of terrain.. Skip.
+ if (localPosition.x > 1.0f || localPosition.x < 0.0f || localPosition.z > 1.0f || localPosition.z < 0.0f)
+ return false;
+
+ outputHeight = terrainPosition.y + terrainData->GetHeightmap ().GetInterpolatedHeight (localPosition.x, localPosition.z);
+ return true;
+}
+
+void TerrainManager::CullAllTerrains(int cullingMask)
+{
+ m_TempCulledTerrains.clear();
+
+ // Generate basic terrain data and do basic tessellation
+ LISTFOREACH(TerrainInstance*, m_ActiveTerrains, i)
+ {
+ TerrainInstance* terrain = (*i);
+
+ int terrainLayer = terrain->m_GameObject->GetLayer();
+ if( ((1<<terrainLayer) & cullingMask) == 0 )
+ continue;
+
+ m_TempCulledTerrains.push_back(*i);
+ const Vector3f& currentTerrrainPosition = terrain->GetPosition();
+ if (currentTerrrainPosition != terrain->m_Position)
+ {
+ terrain->m_Position = currentTerrrainPosition;
+ terrain->Flush ();
+ }
+
+ terrain->GarbageCollectRenderers ();
+ PROFILER_BEGIN(gTerrainFlushDirty, NULL)
+ terrain->FlushDirty ();
+ PROFILER_END
+
+ PROFILER_BEGIN(gTerrainRenderStep1, NULL)
+ const TerrainInstance::Renderer* renderer = terrain->GetRenderer ();
+ if (renderer != NULL)
+ {
+ // Draw terrain
+ terrain->m_TerrainData->GetSplatDatabase().RecalculateBasemapIfDirty();
+
+ if (terrain->NeedRenderTerrainGeometry())
+ {
+ float splatMapDistance = terrain->m_EditorRenderFlags == TerrainInstance::kRenderHeightmap ? FLT_MAX : terrain->m_SplatMapDistance;
+ renderer->terrain->RenderStep1 (renderer->camera, terrain->m_HeightmapMaximumLOD, terrain->m_HeightmapPixelError, splatMapDistance);
+ }
+ }
+ PROFILER_END
+ }
+
+ // Setup neighbors
+ LISTFOREACH(TerrainInstance*, m_TempCulledTerrains, i)
+ {
+ TerrainInstance* terrain = (*i);
+ TerrainRenderer* renderer = terrain->GetTerrainRendererDontCreate ();
+ if (renderer != NULL && terrain->NeedRenderTerrainGeometry())
+ {
+ // Find neighbor Terrains and update TerrainRenderer
+ TerrainRenderer* left = NULL, * right = NULL, * top = NULL, * bottom = NULL;
+ if (terrain->m_LeftNeighbor != NULL)
+ left = terrain->m_LeftNeighbor->GetTerrainRendererDontCreate ();
+ if (terrain->m_RightNeighbor != NULL)
+ right = terrain->m_RightNeighbor->GetTerrainRendererDontCreate ();
+ if (terrain->m_TopNeighbor != NULL)
+ top = terrain->m_TopNeighbor->GetTerrainRendererDontCreate ();
+ if (terrain->m_BottomNeighbor != NULL)
+ bottom = terrain->m_BottomNeighbor->GetTerrainRendererDontCreate ();
+ renderer->SetNeighbors(left, top, right, bottom);
+ }
+ }
+
+ // Apply force splitting on boundaries
+ LISTFOREACH(TerrainInstance*, m_TempCulledTerrains, i)
+ {
+ TerrainInstance* terrain = (*i);
+ TerrainRenderer* renderer = terrain->GetTerrainRendererDontCreate ();
+ if (renderer != NULL && terrain->NeedRenderTerrainGeometry())
+ {
+ PROFILER_BEGIN(gTerrainRenderStep2, NULL)
+ renderer->RenderStep2 ();
+ PROFILER_END
+ }
+ }
+
+ // Do the actual rendering
+ LISTFOREACH(TerrainInstance*, m_TempCulledTerrains, i)
+ {
+ TerrainInstance* terrain = (*i);
+ const TerrainInstance::Renderer* renderer = terrain->GetRenderer ();
+ if (renderer != NULL)
+ {
+ int terrainLayer = terrain->m_GameObject->GetLayer();
+
+ UNITY_VECTOR(kMemRenderer, Light*) lights = GetLightManager().GetLights(kLightDirectional, terrainLayer);
+
+ PROFILER_BEGIN(gTerrainRenderStep3, NULL)
+ if (terrain->NeedRenderTerrainGeometry())
+ {
+ renderer->terrain->RenderStep3 (renderer->camera, terrainLayer, terrain->m_CastShadows, terrain->m_MaterialTemplate);
+ }
+ PROFILER_END
+
+ PROFILER_BEGIN(gTerrainDetailsRender, NULL)
+ if (terrain->NeedRenderDetails())
+ {
+ renderer->details->Render (renderer->camera, terrain->m_DetailObjectDistance, terrainLayer, terrain->m_DetailObjectDensity);
+ }
+ PROFILER_END
+
+ PROFILER_BEGIN(gTerrainTreesRender, NULL)
+ if (terrain->NeedRenderTrees())
+ {
+ renderer->trees->Render (*renderer->camera, lights, terrain->m_TreeBillboardDistance, terrain->m_TreeDistance, terrain->m_TreeCrossFadeLength, terrain->m_TreeMaximumFullLODCount, terrainLayer);
+ }
+ PROFILER_END
+ // terrain.m_DebugTreeRenderTex = renderer.trees.GetImposterRenderTexture();
+ }
+ }
+}
+
+void TerrainManager::SetLightmapIndexOnAllTerrains (int lightmapIndex)
+{
+ LISTFOREACH(TerrainInstance*, m_ActiveTerrains, terrain)
+ {
+ (*terrain)->SetLightmapIndex(lightmapIndex);
+ }
+}
+
+/// Creates a Terrain including collider from [[TerrainData]]
+PPtr<GameObject> TerrainManager::CreateTerrainGameObject (const TerrainData& assignTerrain)
+{
+/* // Also create the renderer game object
+#if ENABLE_PHYSICS
+ GameObject* go = &CreateGameObjectWithHideFlags ("Terrain", true, 0, "Terrain", NULL, NULL);
+#else
+ GameObject* go = &CreateGameObjectWithHideFlags ("Terrain", true, 0, "Terrain", NULL, NULL);
+#endif
+ go->SetIsStatic (true);
+ TerrainInstance* terrain = go.GetComponent(typeof(Terrain)) as Terrain;
+#if ENABLE_PHYSICS
+ TerrainCollider collider = go.GetComponent(typeof(TerrainCollider)) as TerrainCollider;
+ collider.terrainData = assignTerrain;
+ terrain.terrainData = assignTerrain;
+#endif
+ // The terrain already got an OnEnable, but the terrain data had not been set up correctly.
+ terrain->OnEnable ();
+
+ return go;*/
+ return NULL;
+}
+
+void TerrainManager::AddTerrainAndSetActive(TerrainInstance* terrain)
+{
+ if (std::find(m_ActiveTerrains.begin(), m_ActiveTerrains.end(), terrain) == m_ActiveTerrains.end())
+ m_ActiveTerrains.push_back(terrain);
+
+ m_ActiveTerrain = terrain;
+}
+
+void TerrainManager::RemoveTerrain(TerrainInstance* terrain)
+{
+ TerrainList::iterator i = std::find(m_ActiveTerrains.begin(), m_ActiveTerrains.end(), terrain);
+ if (i != m_ActiveTerrains.end())
+ m_ActiveTerrains.erase(i);
+
+ if (m_ActiveTerrain == terrain)
+ m_ActiveTerrain = NULL;
+}
+
+void TerrainManager::UnloadTerrainsFromGfxDevice()
+{
+ LISTFOREACH(TerrainInstance*, m_ActiveTerrains, terrain)
+ {
+ dynamic_array<TerrainInstance::Renderer> renderers = (*terrain)->m_Renderers;
+ for (int i = 0; i < renderers.size(); ++i)
+ {
+ renderers[i].terrain->UnloadVBOFromGfxDevice();
+ }
+ }
+}
+
+void TerrainManager::ReloadTerrainsToGfxDevice()
+{
+ LISTFOREACH(TerrainInstance*, m_ActiveTerrains, terrain)
+ {
+ dynamic_array<TerrainInstance::Renderer> renderers = (*terrain)->m_Renderers;
+ for (int i = 0; i < renderers.size(); ++i)
+ {
+ renderers[i].terrain->ReloadVBOToGfxDevice();
+ }
+ }
+}
+
+#if ENABLE_PHYSICS
+NxHeightField* TerrainManager::Heightmap_GetNxHeightField(Heightmap& heightmap)
+{
+ return heightmap.GetNxHeightField();
+}
+#endif
+
+int TerrainManager::Heightmap_GetMaterialIndex(Heightmap& heightmap)
+{
+ return heightmap.GetMaterialIndex();
+}
+
+Vector3f TerrainManager::Heightmap_GetSize(Heightmap& heightmap)
+{
+ return heightmap.GetSize();
+}
+
+void TerrainManager::CollectTreeRenderers(dynamic_array<SceneNode>& sceneNodes, dynamic_array<AABB>& boundingBoxes) const
+{
+ for (TerrainList::const_iterator it = m_ActiveTerrains.begin(); it != m_ActiveTerrains.end(); ++it)
+ {
+ if (!(*it)->NeedRenderTrees())
+ {
+ continue;
+ }
+
+ const TerrainInstance::Renderer* renderer = (*it)->GetRenderer();
+ if (renderer != NULL && renderer->trees != NULL)
+ {
+ renderer->trees->CollectTreeRenderers(sceneNodes, boundingBoxes);
+ }
+ }
+}
+
+#undef LISTFOREACH
+
+#endif
diff --git a/Runtime/Terrain/TerrainManager.h b/Runtime/Terrain/TerrainManager.h
new file mode 100644
index 0000000..d3904c1
--- /dev/null
+++ b/Runtime/Terrain/TerrainManager.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Interfaces/ITerrainManager.h"
+#include <list>
+
+class TerrainData;
+class HeightMap;
+
+class TerrainManager : public ITerrainManager
+{
+public:
+ TerrainManager();
+
+ virtual void CullAllTerrains (int cullingMask);
+ virtual void SetLightmapIndexOnAllTerrains (int lightmapIndex);
+ virtual void AddTerrainAndSetActive (TerrainInstance* terrain);
+ virtual void RemoveTerrain (TerrainInstance* terrain);
+ TerrainInstance* GetActiveTerrain() const { return m_ActiveTerrain; }
+ const TerrainList& GetActiveTerrains() const { return m_ActiveTerrains; }
+ void UnloadTerrainsFromGfxDevice ();
+ void ReloadTerrainsToGfxDevice ();
+
+ // TODO Move these to heightmap, does not really belong here
+#if ENABLE_PHYSICS
+ virtual NxHeightField* Heightmap_GetNxHeightField(Heightmap& heightmap);
+#endif
+ virtual int Heightmap_GetMaterialIndex(Heightmap& heightmap);
+ virtual Vector3f Heightmap_GetSize(Heightmap& heightmap);
+
+ // TODO this should move to TerrainData
+ /// Extracts the height on the heightmap from a TerrainData
+ virtual bool GetInterpolatedHeight (const Object* terrainData, const Vector3f& terrainPosition, const Vector3f& position, float& outputHeight);
+
+ virtual void CollectTreeRenderers(dynamic_array<SceneNode>& sceneNodes, dynamic_array<AABB>& boundingBoxes) const;
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+ PPtr<GameObject> CreateTerrainGameObject (const TerrainData& assignTerrain);
+
+private:
+
+ TerrainList m_TempCulledTerrains;
+ TerrainList m_ActiveTerrains;
+ TerrainInstance* m_ActiveTerrain;
+};
diff --git a/Runtime/Terrain/TerrainModule.jam b/Runtime/Terrain/TerrainModule.jam
new file mode 100644
index 0000000..09de7d8
--- /dev/null
+++ b/Runtime/Terrain/TerrainModule.jam
@@ -0,0 +1,67 @@
+rule TerrainModule_ReportCpp
+{
+ return
+ Runtime/Terrain/TerrainModule.jam
+
+ Runtime/Terrain/DetailDatabase.cpp
+ Runtime/Terrain/DetailDatabase.h
+ Runtime/Terrain/DetailRenderer.cpp
+ Runtime/Terrain/DetailRenderer.h
+ Runtime/Terrain/ImposterRenderTexture.cpp
+ Runtime/Terrain/ImposterRenderTexture.h
+ Runtime/Terrain/SplatDatabase.cpp
+ Runtime/Terrain/SplatDatabase.h
+ Runtime/Terrain/SplatMaterials.cpp
+ Runtime/Terrain/SplatMaterials.h
+ Runtime/Terrain/TerrainData.cpp
+ Runtime/Terrain/TerrainData.h
+ Runtime/Terrain/TerrainIndexGenerator.cpp
+ Runtime/Terrain/TerrainIndexGenerator.h
+ Runtime/Terrain/TerrainInstance.cpp
+ Runtime/Terrain/TerrainInstance.h
+ Runtime/Terrain/TerrainRenderer.cpp
+ Runtime/Terrain/TerrainRenderer.h
+ Runtime/Terrain/TerrainManager.cpp
+ Runtime/Terrain/TerrainManager.h
+ Runtime/Terrain/TerrainModuleRegistration.cpp
+ Runtime/Terrain/Tree.cpp
+ Runtime/Terrain/Tree.h
+ Runtime/Terrain/TreeDatabase.cpp
+ Runtime/Terrain/TreeDatabase.h
+ Runtime/Terrain/TreeRenderer.cpp
+ Runtime/Terrain/TreeRenderer.h
+ Runtime/Terrain/Heightmap.cpp
+ Runtime/Terrain/Heightmap.h
+ Runtime/Terrain/QuadTreeNodeRenderer.cpp
+ Runtime/Terrain/QuadTreeNodeRenderer.h
+ ;
+}
+
+rule TerrainModule_ReportTxt
+{
+ return
+ Runtime/Terrain/ScriptBindings/TerrainDataBindings.txt
+ Runtime/Terrain/ScriptBindings/Terrains.txt
+ Runtime/Terrain/ScriptBindings/WindZoneBindings.txt
+ ;
+}
+
+rule TerrainModule_ReportIncludes
+{
+ return
+ External/PhysX/builds/SDKs/Foundation/include
+ External/PhysX/builds/SDKs/Physics/include
+ External/PhysX/builds/SDKs/PhysXLoader/include
+ Projects/PrecompiledHeaders/
+ ;
+}
+
+rule TerrainModule_Init
+{
+ OverrideModule Terrain : GetModule_Cpp : byOverridingWithMethod : TerrainModule_ReportCpp ;
+ OverrideModule Terrain : GetModule_Txt : byOverridingWithMethod : TerrainModule_ReportTxt ;
+ OverrideModule Terrain : GetModule_Inc : byOverridingWithMethod : TerrainModule_ReportIncludes ;
+}
+
+
+#RegisterModule Terrain ; \ No newline at end of file
diff --git a/Runtime/Terrain/TerrainModuleRegistration.cpp b/Runtime/Terrain/TerrainModuleRegistration.cpp
new file mode 100644
index 0000000..e178ace
--- /dev/null
+++ b/Runtime/Terrain/TerrainModuleRegistration.cpp
@@ -0,0 +1,37 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_TERRAIN
+#include "Runtime/BaseClasses/ClassRegistration.h"
+#include "Runtime/Modules/ModuleRegistration.h"
+
+static void RegisterTerrainClasses (ClassRegistrationContext& context)
+{
+ REGISTER_CLASS (TerrainData)
+ REGISTER_CLASS (Tree)
+}
+
+#if ENABLE_MONO || UNITY_WINRT
+void ExportTerrains ();
+void ExportTerrainDataBindings ();
+void ExportWindZoneBindings ();
+
+static void RegisterTerrainICallModule ()
+{
+#if !INTERNAL_CALL_STRIPPING
+ ExportTerrains ();
+ ExportTerrainDataBindings();
+ ExportWindZoneBindings();
+#endif
+}
+#endif
+
+extern "C" EXPORT_MODULE void RegisterModule_Terrain ()
+{
+ ModuleRegistrationInfo info;
+ info.registerClassesCallback = &RegisterTerrainClasses;
+#if ENABLE_MONO || UNITY_WINRT
+ info.registerIcallsCallback = &RegisterTerrainICallModule;
+#endif
+ RegisterModuleInfo (info);
+}
+#endif \ No newline at end of file
diff --git a/Runtime/Terrain/TerrainRenderer.cpp b/Runtime/Terrain/TerrainRenderer.cpp
new file mode 100644
index 0000000..8ab2e0f
--- /dev/null
+++ b/Runtime/Terrain/TerrainRenderer.cpp
@@ -0,0 +1,798 @@
+#include "UnityPrefix.h"
+#include "TerrainRenderer.h"
+
+#if ENABLE_TERRAIN
+
+#ifdef TERRAIN_PREFER_VBO_OVER_MESH
+# include "Runtime/GfxDevice/GfxDevice.h"
+# include "Runtime/Terrain/QuadTreeNodeRenderer.h"
+#else
+# include "Runtime/Camera/IntermediateRenderer.h"
+#endif
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Graphics/LightmapSettings.h"
+#include "Runtime/Camera/Camera.h"
+
+TerrainRenderer::TerrainRenderer (SInt32 instanceID, PPtr<TerrainData> terrainData, const Vector3f &position, int lightmapIndex)
+#if UNITY_EDITOR
+: m_InstanceID(instanceID)
+#endif
+{
+ m_Scale = terrainData->GetHeightmap().GetScale();
+ m_Levels = terrainData->GetHeightmap().GetMipLevels();
+ m_TerrainData = terrainData;
+ m_Position = position;
+ m_LeftNeighbor = NULL;
+ m_RightNeighbor = NULL;
+ m_BottomNeighbor = NULL;
+ m_TopNeighbor = NULL;
+
+ m_SplatMaterials = new SplatMaterials (m_TerrainData);
+
+ m_LightmapIndex = lightmapIndex;
+
+ RebuildNodes ();
+}
+
+TerrainRenderer::~TerrainRenderer ()
+{
+ delete m_SplatMaterials;
+
+ for (std::vector<QuadTreeNode>::iterator it = m_Quadtree.begin(), itEnd = m_Quadtree.end(); it != itEnd; ++it)
+ {
+ RemoveMesh(*it);
+ }
+
+#ifdef TERRAIN_PREFER_VBO_OVER_MESH
+ for (std::list<VBO*>::iterator it = m_FreeVBOPool.begin(), itEnd = m_FreeVBOPool.end(); it != itEnd; ++it)
+ {
+ GetGfxDevice().DeleteVBO(*it);
+ }
+#endif
+}
+
+void TerrainRenderer::SetNeighbors (TerrainRenderer *left, TerrainRenderer *top, TerrainRenderer *right, TerrainRenderer *bottom)
+{
+ m_TopNeighbor = top;
+ m_RightNeighbor = right;
+ m_BottomNeighbor = bottom;
+ m_LeftNeighbor = left;
+}
+
+void TerrainRenderer::SetTerrainData(PPtr<TerrainData> value)
+{
+ m_TerrainData = value;
+}
+
+#ifdef TERRAIN_PREFER_VBO_OVER_MESH
+
+VBO* TerrainRenderer::CreateVBO()
+{
+ if (!m_FreeVBOPool.empty())
+ {
+ VBO* vbo = m_FreeVBOPool.back();
+ m_FreeVBOPool.pop_back();
+ return vbo;
+ }
+
+ // create new VBO
+ return GetGfxDevice().CreateVBO();
+}
+
+void TerrainRenderer::ReclaimVBO(VBO* vbo)
+{
+ AssertIf(vbo == NULL);
+ m_FreeVBOPool.push_back(vbo);
+}
+
+#endif
+
+int TerrainRenderer::GetIndex (int x, int y, int level)
+{
+ int index = 0;
+ for (int i=0;i<level;i++)
+ {
+ int size = 1 << (m_Levels - i);
+ index += size * size;
+ }
+
+ int width = 1 << (m_Levels - level);
+ index += width * y;
+ index += x;
+
+ Assert (index >= 0 && index < m_Quadtree.size());
+
+ return index;
+}
+
+QuadTreeNode *TerrainRenderer::GetNode (int x, int y, int level)
+{
+ if (level < 0 || level > m_Levels)
+ return NULL;
+ int size = 1 << (m_Levels - level);
+
+ if (x < 0 || x >= size || y < 0 || y >= size)
+ {
+ // Left
+ if (x == -1 && m_LeftNeighbor != NULL)
+ return m_LeftNeighbor->GetNode (size - 1, y, level);
+
+ // right
+ if (x == size && m_RightNeighbor != NULL)
+ return m_RightNeighbor->GetNode (0, y, level);
+
+ // top
+ if (y == size && m_TopNeighbor != NULL)
+ return m_TopNeighbor->GetNode (x, 0, level);
+
+ // bottom
+ if (y == -1 && m_BottomNeighbor != NULL)
+ return m_BottomNeighbor->GetNode (x, size-1, level);
+
+ return NULL;
+ }
+
+ return &m_Quadtree[GetIndex(x, y, level)];
+}
+
+QuadTreeNode *TerrainRenderer::GetNodeAndRenderer (int x, int y, int level, TerrainRenderer *&renderer)
+{
+ if (level < 0 || level > m_Levels)
+ {
+ renderer = NULL;
+ return NULL;
+ }
+ int size = 1 << (m_Levels - level);
+
+ if (x < 0 || x >= size || y < 0 || y >= size)
+ {
+ // Left
+ if (x == -1 && m_LeftNeighbor != NULL)
+ {
+ renderer = m_LeftNeighbor;
+ return m_LeftNeighbor->GetNode (size - 1, y, level);
+ }
+
+ // right
+ if (x == size && m_RightNeighbor != NULL)
+ {
+ renderer = m_RightNeighbor;
+ return m_RightNeighbor->GetNode (0, y, level);
+ }
+
+ // top
+ if (y == size && m_TopNeighbor != NULL)
+ {
+ renderer = m_TopNeighbor;
+ return m_TopNeighbor->GetNode (x, 0, level);
+ }
+
+ // bottom
+ if (y == -1 && m_BottomNeighbor != NULL)
+ {
+ renderer = m_BottomNeighbor;
+ return m_BottomNeighbor->GetNode (x, size-1, level);
+ }
+
+ renderer = NULL;
+ return NULL;
+ }
+
+ renderer = this;
+ return &m_Quadtree[GetIndex(x, y, level)];
+}
+
+int TerrainRenderer::GetPatchCountX (int level)
+{
+ return 1 << (m_Levels - level);
+}
+
+int TerrainRenderer::GetPatchCountY (int level)
+{
+ return 1 << (m_Levels - level);
+}
+
+void TerrainRenderer::RebuildNodes ()
+{
+ Heightmap &heightmap = m_TerrainData->GetHeightmap();
+ int totalSize = heightmap.GetTotalPatchCount();
+
+ m_Quadtree.resize(totalSize);
+ for (int level=0;level <= m_Levels;level++)
+ {
+ for (int y=0;y<GetPatchCountY(level);y++)
+ {
+ for (int x=0;x<GetPatchCountX(level);x++)
+ {
+ int index = GetIndex(x, y, level);
+ m_Quadtree[index].x = x;
+ m_Quadtree[index].y = y;
+ m_Quadtree[index].level = level;
+ m_Quadtree[index].maxHeightError = heightmap.GetMaximumHeightError(x, y, level);
+ m_Quadtree[index].bounds = heightmap.GetBounds(x, y, level);
+ m_Quadtree[index].bounds.GetCenter() += m_Position;
+ }
+ }
+ }
+}
+
+Vector3f TerrainRenderer::GetNodePosition (QuadTreeNode &node)
+{
+ Vector3f position = Vector3f::zero;
+ position += m_Position;
+ return position;
+}
+
+int TerrainRenderer::CalculateEdgeMask (QuadTreeNode &node)
+{
+ //@TODO: Handle case where invisible (dont need to switch index buffers)
+ int mask = 0;
+ for (int i=kDirectionLeft;i<=kDirectionDown;i++)
+ {
+ QuadTreeNode *neighbor = FindNeighbor(node, i);
+ if (neighbor != NULL)
+ {
+ if (neighbor->visibility == kVisibilityDrawChild || neighbor->visibility == kVisibilityDrawSelf)
+ mask |= 1 << i;
+ }
+ else
+ mask |= 1 << i;
+ }
+ return mask;
+}
+
+QuadTreeNode *TerrainRenderer::FindNeighbor (QuadTreeNode &node, int direction)
+{
+ if (direction == kDirectionUp)
+ return GetNode(node.x, node.y + 1, node.level);
+ else if (direction == kDirectionDown)
+ return GetNode(node.x, node.y - 1, node.level);
+ else if (direction == kDirectionLeft)
+ return GetNode(node.x - 1, node.y, node.level);
+ else
+ return GetNode(node.x + 1, node.y, node.level);
+}
+
+QuadTreeNode *TerrainRenderer::FindNeighborAndRenderer (QuadTreeNode &node, int direction, TerrainRenderer *&renderer)
+{
+ if (direction == kDirectionUp)
+ return GetNodeAndRenderer(node.x, node.y + 1, node.level, renderer);
+ else if (direction == kDirectionDown)
+ return GetNodeAndRenderer(node.x, node.y - 1, node.level, renderer);
+ else if (direction == kDirectionLeft)
+ return GetNodeAndRenderer(node.x - 1, node.y, node.level, renderer);
+ else
+ return GetNodeAndRenderer(node.x + 1, node.y, node.level, renderer);
+}
+
+QuadTreeNode *TerrainRenderer::FindChild (QuadTreeNode &node, int direction)
+{
+ if (direction == kDirectionLeftUp)
+ return GetNode(node.x * 2, node.y * 2, node.level - 1);
+ else if (direction == kDirectionRightUp)
+ return GetNode(node.x * 2 + 1, node.y * 2, node.level - 1);
+ else if (direction == kDirectionLeftDown)
+ return GetNode(node.x * 2, node.y * 2 + 1, node.level - 1);
+ else
+ return GetNode(node.x * 2 + 1, node.y * 2 + 1, node.level - 1);
+}
+
+QuadTreeNode *TerrainRenderer::FindParent (QuadTreeNode &node)
+{
+ return GetNode(node.x / 2, node.y / 2, node.level + 1);
+}
+
+void TerrainRenderer::RenderStep1 (Camera *camera, int maxLodLevel, float tau, float splatDistance)
+{
+ // clamp LOD level to valid values
+ maxLodLevel = clamp(maxLodLevel, 0, m_Levels);
+
+ float nearClip = camera->GetNear();
+ float vTop = nearClip * tanf( Deg2Rad( camera->GetFov() / 2.0F ) );
+ float vres = camera->GetScreenViewportRect().height;
+
+ float A = nearClip / fabs(vTop);
+ float T = 2 * tau / (float)vres;
+ float kC = A / T;
+
+ QuadTreeNode& root = GetRootNode();
+
+ m_CachedCameraPosition = camera-> QueryComponent(Transform)->GetPosition();
+ m_CachedMaxLodLevel = maxLodLevel;
+ m_CachedkC = kC;
+ m_CachedSqrSplatDistance = splatDistance * splatDistance;
+
+ RecursiveCalculateLod(root);
+}
+
+void TerrainRenderer::RenderStep2 ()
+{
+ QuadTreeNode& root = GetRootNode();
+
+ EnforceLodTransitions(root);
+}
+
+void TerrainRenderer::RenderStep3 (Camera *camera, int layer, bool castShadows, Material* mat)
+{
+ QuadTreeNode &root = GetRootNode();
+
+ m_CurrentLayer = layer;
+ m_CurrentCamera = camera;
+ m_CastShadows = castShadows;
+
+ // Get Splat materials
+ m_CurrentMaterials = m_SplatMaterials->GetMaterials (mat, m_CurrentMaterialCount);
+ m_CurrentBaseMaterial = m_SplatMaterials->GetSplatBaseMaterial (mat);
+
+ RecursiveRenderMeshes(root, m_TerrainData->GetHeightmap());
+}
+
+
+void TerrainRenderer::CleanupMeshes ()
+{
+ for (std::vector<QuadTreeNode>::iterator node=m_Quadtree.begin();node != m_Quadtree.end(); node++)
+ {
+ if (node->visibility == kVisibilityDrawSelf)
+ RemoveMesh(*node);
+ node->visibility = kVisibilityLoddedAway;
+ node->oldVisibility = kVisibilityLoddedAway;
+ }
+}
+
+void TerrainRenderer::ReloadAll () {
+ Cleanup ();
+
+ m_Scale = m_TerrainData->GetHeightmap().GetScale();
+ m_Levels = m_TerrainData->GetHeightmap().GetMipLevels();
+
+
+ delete m_SplatMaterials;
+ m_SplatMaterials = new SplatMaterials (m_TerrainData);
+
+ RebuildNodes ();
+}
+
+void TerrainRenderer::Cleanup () {
+ CleanupMeshes();
+ m_SplatMaterials->Cleanup ();
+}
+
+void TerrainRenderer::UnloadVBOFromGfxDevice ()
+{
+#ifdef TERRAIN_PREFER_VBO_OVER_MESH
+ // This will put all VBOs back into m_FreeVBOPool
+ for (std::vector<QuadTreeNode>::iterator node=m_Quadtree.begin();node != m_Quadtree.end(); node++)
+ {
+ RemoveMesh(*node);
+ node->updateIndices = true;
+ node->updateMesh = true;
+ }
+
+ for (std::list<VBO*>::iterator it = m_FreeVBOPool.begin(), itEnd = m_FreeVBOPool.end(); it != itEnd; ++it)
+ {
+ GetGfxDevice().DeleteVBO(*it);
+ }
+ m_FreeVBOPool.clear();
+#endif
+}
+
+void TerrainRenderer::ReloadVBOToGfxDevice ()
+{
+ // No need to do anything, since QuadTreeNodeRenderers will recreate themselves
+}
+
+float TerrainRenderer::CalculateSqrDistance (Vector3f &rkPoint, AABB &rkBox)
+{
+ // compute coordinates of point in box coordinate system
+ Vector3f kClosest = rkPoint - rkBox.GetCenter();
+
+ // project test point onto box
+ float fSqrDistance = 0.0f;
+ float fDelta;
+ Vector3f& extents = rkBox.GetExtent();
+
+ for (int i=0;i<3;i++)
+ {
+ if ( kClosest[i] < -extents[i] )
+ {
+ fDelta = kClosest[i] + extents[i];
+ fSqrDistance += fDelta * fDelta;
+ kClosest[i] = -extents[i];
+ }
+ else if ( kClosest[i] > extents[i] )
+ {
+ fDelta = kClosest[i] - extents[i];
+ fSqrDistance += fDelta * fDelta;
+ kClosest[i] = extents[i];
+ }
+ }
+
+ return fSqrDistance;
+}
+
+void TerrainRenderer::MarkChildVisibilityRecurse (QuadTreeNode &node, int newVisibility)
+{
+ if (node.level == 0)
+ return;
+
+ for (int i=kDirectionLeftUp;i<=kDirectionRightDown;i++)
+ {
+ QuadTreeNode* childNode = FindChild (node, i);
+ if (childNode->visibility != newVisibility)
+ {
+ childNode->visibility = newVisibility;
+ MarkChildVisibilityRecurse(*childNode, newVisibility);
+ }
+ }
+}
+
+void TerrainRenderer::ReloadPrecomputedError ()
+{
+ Heightmap &heightmap = m_TerrainData->GetHeightmap();
+ for (std::vector<QuadTreeNode>::iterator node=m_Quadtree.begin();node != m_Quadtree.end(); node++)
+ node->maxHeightError = heightmap.GetMaximumHeightError(node->x, node->y, node->level);
+}
+
+void TerrainRenderer::ReloadBounds ()
+{
+ Heightmap &heightmap = m_TerrainData->GetHeightmap();
+ for (std::vector<QuadTreeNode>::iterator node=m_Quadtree.begin();node != m_Quadtree.end(); node++)
+ {
+ node->bounds = heightmap.GetBounds(node->x, node->y, node->level);
+ node->bounds.GetCenter() += m_Position;
+ }
+}
+
+void TerrainRenderer::RecursiveCalculateLod (QuadTreeNode &node)
+{
+ // Distance to bounding volume based
+ float distance = CalculateSqrDistance(m_CachedCameraPosition, node.bounds);
+
+ float D2 = m_CachedkC * node.maxHeightError;
+ D2 *= D2;
+
+ // Node has good enough lod, render it
+ if (distance > D2 || node.level == m_CachedMaxLodLevel)
+ {
+ node.visibility = kVisibilityDrawSelf;
+ node.useSplatmap = distance < m_CachedSqrSplatDistance;
+ MarkChildVisibilityRecurse(node, kVisibilityLoddedAway);
+ }
+ // Node needs subdivision
+ else
+ {
+ node.visibility = kVisibilityDrawChild;
+
+ for (int i=kDirectionLeftUp;i<=kDirectionRightDown;i++)
+ {
+ QuadTreeNode* childNode = FindChild (node, i);
+ RecursiveCalculateLod (*childNode);
+ }
+ }
+}
+
+void TerrainRenderer::ForceSplitParent (QuadTreeNode &node)
+{
+ QuadTreeNode* parent = FindParent(node);
+ Assert (parent != NULL);
+ if (parent->visibility == kVisibilityLoddedAway)
+ ForceSplitParent (*parent);
+
+ if (parent->visibility == kVisibilityDrawSelf)
+ {
+ for (int i=kDirectionLeftUp;i<=kDirectionRightDown;i++)
+ {
+ QuadTreeNode* childNode = FindChild (*parent, i);
+ Assert (childNode != NULL);
+
+ childNode->visibility = kVisibilityDrawSelf;
+ childNode->useSplatmap = CalculateSqrDistance(m_CachedCameraPosition, childNode->bounds) < m_CachedSqrSplatDistance;
+
+ MarkChildVisibilityRecurse(*childNode, kVisibilityLoddedAway);
+ }
+ for (int i=kDirectionLeftUp;i<=kDirectionRightDown;i++)
+ {
+ QuadTreeNode *childNode = FindChild (*parent, i);
+ Assert (childNode != NULL);
+
+ EnforceLodTransitions(*childNode);
+ }
+ parent->visibility = kVisibilityDrawChild;
+ }
+}
+
+// Go through all nodes that are being rendered
+// Check if the parent neighbor
+void TerrainRenderer::EnforceLodTransitions (QuadTreeNode &node)
+{
+ if (node.visibility == kVisibilityLoddedAway)
+ return;
+
+ if (node.visibility == kVisibilityDrawSelf)
+ {
+ for (int i=kDirectionLeft;i<=kDirectionDown;i++)
+ {
+ TerrainRenderer *renderer;
+
+ // If we have a neighbor, make sure it's either a root node that's completely LOD'd away
+ // or that it's at most one detail level away from us (i.e. that the neighbor's tree is split down at
+ // least to the level of the neighbor's parent).
+ QuadTreeNode *neighbor = FindNeighborAndRenderer (node, i, renderer);
+ if (neighbor != NULL && neighbor->visibility == kVisibilityLoddedAway && !renderer->IsRootNode (*neighbor))
+ {
+ QuadTreeNode *neighborParent = renderer->FindParent(*neighbor);
+ Assert (neighborParent != NULL);
+
+ if (neighborParent->visibility == kVisibilityLoddedAway)
+ {
+ renderer->ForceSplitParent(*neighborParent);
+ }
+ }
+ }
+ }
+ else
+ {
+ for (int i=kDirectionLeftUp;i<=kDirectionRightDown;i++)
+ {
+ QuadTreeNode *childNode = FindChild (node, i);
+ Assert (childNode != NULL);
+
+ EnforceLodTransitions(*childNode);
+ }
+ }
+}
+
+void TerrainRenderer::RemoveMesh (QuadTreeNode &node)
+{
+#ifdef TERRAIN_PREFER_VBO_OVER_MESH
+ if (node.vbo != NULL)
+ {
+ ReclaimVBO(node.vbo);
+ node.vbo = NULL;
+ node.updateMesh = false;
+ node.updateIndices = false;
+ }
+#else
+ // Cleanup self
+ if(node.mesh)
+ {
+ DestroySingleObject (node.mesh);
+ node.mesh = NULL;
+ }
+#endif
+}
+
+void TerrainRenderer::RecursiveRemoveMeshes (QuadTreeNode &node)
+{
+ if (node.oldVisibility == kVisibilityLoddedAway)
+ return;
+
+ if (node.oldVisibility == kVisibilityDrawSelf)
+ {
+ RemoveMesh(node);
+ }
+ else if (node.oldVisibility == kVisibilityDrawChild)
+ {
+ for (int i=kDirectionLeftUp;i<=kDirectionRightDown;i++)
+ {
+ QuadTreeNode* childNode = FindChild (node, i);
+ RecursiveRemoveMeshes(*childNode);
+ }
+ }
+
+ node.oldVisibility = kVisibilityLoddedAway;
+}
+
+void TerrainRenderer::RenderNode (QuadTreeNode &node)
+{
+ int layer = m_CurrentLayer;
+ Camera *camera = m_CurrentCamera;
+ Vector3f position = GetNodePosition(node);
+
+ Matrix4x4f matrix;
+ matrix.SetTranslate( position );
+ bool castShadows = m_CastShadows;
+
+ if (node.useSplatmap)
+ {
+ int count = m_CurrentMaterialCount;
+ for (int m=0;m<count;++m)
+ {
+ Material* mat = m_CurrentMaterials[m];
+#ifdef TERRAIN_PREFER_VBO_OVER_MESH
+ QuadTreeNodeRenderer* r = AddQuadTreeNodeRenderer( matrix, node.subMesh.localAABB, mat, layer, castShadows, true, camera );
+ r->Setup( this, &node );
+#else
+ IntermediateRenderer* r = AddIntermediateRenderer (matrix, node.mesh, mat, layer, castShadows, true, 0, camera);
+#endif
+ r->SetLightmapIndexIntNoDirty(m_LightmapIndex);
+ #if UNITY_EDITOR
+ r->SetScaleInLightmap(m_LightmapSize);
+ r->SetInstanceID (m_InstanceID);
+ #endif
+ }
+ }
+ else
+ {
+#ifdef TERRAIN_PREFER_VBO_OVER_MESH
+ QuadTreeNodeRenderer* r = AddQuadTreeNodeRenderer( matrix, node.subMesh.localAABB, m_CurrentBaseMaterial, layer, castShadows, true, camera );
+ r->Setup( this, &node );
+#else
+ IntermediateRenderer* r = new IntermediateRenderer( matrix, node.mesh, m_CurrentBaseMaterial, layer, castShadows, true, 0 );
+#endif
+ r->SetLightmapIndexIntNoDirty(m_LightmapIndex);
+ #if UNITY_EDITOR
+ r->SetScaleInLightmap(m_LightmapSize);
+ r->SetInstanceID (m_InstanceID);
+ #endif
+ }
+}
+
+void TerrainRenderer::BuildRenderer (QuadTreeNode &node, int edgeMask)
+{
+ // Build mesh
+#ifdef TERRAIN_PREFER_VBO_OVER_MESH
+ RemoveMesh(node);
+#else
+ DestroySingleObject (node.mesh);
+
+ node.mesh = NEW_OBJECT (Mesh);
+ node.mesh->Reset();
+ node.mesh->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+
+ node.mesh->SetHideFlags(Object::kDontSave);
+#endif
+
+ AABB bounds = m_TerrainData->GetHeightmap().GetBounds(node.x, node.y, node.level);
+
+#ifdef TERRAIN_PREFER_VBO_OVER_MESH
+ node.subMesh.localAABB = bounds;
+ node.updateMesh = true;
+ node.updateIndices = true;
+#else
+ node.mesh->SetBounds(bounds);
+ m_TerrainData->GetHeightmap().UpdatePatchMesh(*node.mesh, node.x, node.y, node.level, edgeMask, this);
+ m_TerrainData->GetHeightmap().UpdatePatchIndices(*node.mesh, node.x, node.y, node.level, edgeMask);
+ Assert(node.mesh->GetBounds().GetCenter () == bounds.GetCenter() && node.mesh->GetBounds().GetExtent() == bounds.GetExtent ());
+#endif
+}
+
+
+void TerrainRenderer::RecursiveRenderMeshes (QuadTreeNode &node, Heightmap &heightmap)
+{
+ // The node itself needs to be rendered
+ if (node.visibility == kVisibilityDrawSelf)
+ {
+ int newEdgeMask = CalculateEdgeMask(node);
+ // We didn't render the node last frame. So we need to build from scratch
+ if (node.oldVisibility != kVisibilityDrawSelf)
+ {
+ BuildRenderer(node, newEdgeMask);
+ node.edgeMask = newEdgeMask;
+ RenderNode(node);
+ }
+ // If highest lod error is infinity we rebuild the mesh constantly
+ // (This is set while the terrain is being painted)
+ else if (node.maxHeightError == std::numeric_limits<float>::infinity())
+ {
+#ifdef TERRAIN_PREFER_VBO_OVER_MESH
+ node.updateMesh = true;
+#else
+ heightmap.UpdatePatchMesh(*node.mesh, node.x, node.y, node.level, newEdgeMask, this);
+#endif
+ if (newEdgeMask != node.edgeMask)
+ {
+#ifdef TERRAIN_PREFER_VBO_OVER_MESH
+ node.updateIndices = true;
+#else
+ heightmap.UpdatePatchIndices(*node.mesh, node.x, node.y, node.level, newEdgeMask);
+#endif
+ node.edgeMask = newEdgeMask;
+ }
+
+ RenderNode(node);
+ }
+ // The edge mask has changed, we need to update the triangle indices
+ else if (newEdgeMask != node.edgeMask)
+ {
+#ifdef TERRAIN_PREFER_VBO_OVER_MESH
+ node.updateIndices = true;
+#else
+ heightmap.UpdatePatchIndices(*node.mesh, node.x, node.y, node.level, newEdgeMask);
+#endif
+ node.edgeMask = newEdgeMask;
+ RenderNode(node);
+ }
+ // Nothing changed just paint
+ else
+ {
+ RenderNode(node);
+ }
+
+ // All old children need to be removed!
+ if (node.oldVisibility == kVisibilityDrawChild)
+ {
+ for (int i=kDirectionLeftUp;i<=kDirectionRightDown;i++)
+ {
+ QuadTreeNode *childNode = FindChild (node, i);
+ Assert (childNode != NULL);
+ RecursiveRemoveMeshes (*childNode);
+ }
+ }
+ }
+ // Some children of the node need to be rendered
+ else if (node.visibility == kVisibilityDrawChild)
+ {
+ // Remove the old render mesh
+ if (node.oldVisibility == kVisibilityDrawSelf)
+ RemoveMesh (node);
+
+ // Recurse into children
+ for (int i=kDirectionLeftUp;i<=kDirectionRightDown;i++)
+ {
+ QuadTreeNode *childNode = FindChild (node, i);
+ RecursiveRenderMeshes (*childNode, heightmap);
+ }
+ }
+ // Node is invisible
+ else
+ {
+ // All old children need to be removed!
+ if (node.oldVisibility == kVisibilityDrawChild)
+ {
+ for (int i=kDirectionLeftUp;i<=kDirectionRightDown;i++)
+ {
+ QuadTreeNode *childNode = FindChild (node, i);
+ RecursiveRemoveMeshes (*childNode);
+ }
+ }
+ // Remove the old render mesh
+ else if (node.oldVisibility == kVisibilityDrawSelf)
+ RemoveMesh (node);
+ }
+
+ node.oldVisibility = node.visibility;
+}
+
+Mesh* GetMeshForPatch(int x, int y, int level, Heightmap& heightmap, TerrainRenderer* terrainRenderer)
+{
+ // Build mesh
+ Mesh* mesh = NEW_OBJECT (Mesh);
+ mesh->Reset();
+ mesh->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+
+ mesh->SetHideFlags(Object::kDontSave);
+
+ // judging by CalculateEdgeMask() the edgeMask equal to 15 means all the neighbouring
+ // patches are visible
+ heightmap.UpdatePatchMesh(*mesh, x, y, level, 15, terrainRenderer);
+ heightmap.UpdatePatchIndices(*mesh, x, y, level, 15);
+
+ mesh->RecalculateBounds();
+
+ return mesh;
+}
+
+std::vector<Mesh*> TerrainRenderer::GetMeshPatches()
+{
+ std::vector<Mesh*> meshes;
+ Heightmap &heightmap = m_TerrainData->GetHeightmap();
+
+ const int level = 0;
+ int xPatches = GetPatchCountX(level);
+ int yPatches = GetPatchCountY(level);
+
+ for (int y = 0; y < yPatches; y++)
+ {
+ for (int x = 0; x < xPatches; x++)
+ {
+ Mesh* mesh = GetMeshForPatch(x, y, level, heightmap, this);
+ mesh->SetName(Format("%s[%i][%i]", m_TerrainData->GetName(), x, y).c_str());
+ meshes.push_back(mesh);
+ }
+ }
+
+ return meshes;
+}
+
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/TerrainRenderer.h b/Runtime/Terrain/TerrainRenderer.h
new file mode 100644
index 0000000..d9bf683
--- /dev/null
+++ b/Runtime/Terrain/TerrainRenderer.h
@@ -0,0 +1,168 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_TERRAIN
+
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Geometry/AABB.h"
+#include "TerrainData.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Shaders/Material.h"
+#include "SplatMaterials.h"
+
+#define TERRAIN_PREFER_VBO_OVER_MESH
+
+class Camera;
+
+enum
+{
+ kVisibilityLoddedAway = 0,
+ kVisibilityDrawChild = 1,
+ kVisibilityDrawSelf = 2
+};
+
+struct QuadTreeNode
+{
+#ifdef TERRAIN_PREFER_VBO_OVER_MESH
+ VBO *vbo;
+ SubMesh subMesh;
+#else
+ Mesh *mesh;
+#endif
+ int edgeMask;
+ float maxHeightError;
+
+ int visibility;
+ int oldVisibility;
+
+ int x;
+ int y;
+ int level;
+ AABB bounds;
+ bool useSplatmap : 1;
+
+#ifdef TERRAIN_PREFER_VBO_OVER_MESH
+ bool updateMesh : 1;
+ bool updateIndices : 1;
+#endif
+
+ QuadTreeNode()
+ {
+ edgeMask = -1;
+ maxHeightError = 1;
+ visibility = kVisibilityLoddedAway;
+ oldVisibility = kVisibilityLoddedAway;
+#ifdef TERRAIN_PREFER_VBO_OVER_MESH
+ vbo = NULL;
+ updateMesh = false;
+ updateIndices = false;
+#else
+ mesh = NULL;
+#endif
+ }
+
+ ~QuadTreeNode()
+ {
+#ifdef TERRAIN_PREFER_VBO_OVER_MESH
+ AssertIf(vbo != NULL);
+#else
+ DestroySingleObject(mesh);
+#endif
+ }
+};
+
+class TerrainRenderer
+{
+public:
+ TerrainRenderer *m_TopNeighbor;
+ TerrainRenderer *m_RightNeighbor;
+ TerrainRenderer *m_BottomNeighbor;
+ TerrainRenderer *m_LeftNeighbor;
+
+ Vector3f m_Scale;
+ std::vector<QuadTreeNode> m_Quadtree;
+ int m_Levels;
+ SplatMaterials *m_SplatMaterials;
+ Vector3f m_Position;
+ bool m_CastShadows;
+
+ Vector3f m_CachedCameraPosition;
+ int m_CachedMaxLodLevel;
+ float m_CachedkC;
+ float m_CachedSqrSplatDistance;
+
+ TerrainRenderer (SInt32 instanceID, PPtr<TerrainData> terrainData, const Vector3f &position, int lightmapIndex);
+ ~TerrainRenderer ();
+ void RenderStep1 (Camera *camera, int maxLodLevel, float tau, float splatDistance);
+ void RenderStep2 ();
+ void RenderStep3 (Camera *camera, int layer, bool castShadow, Material* mat);
+ void ReloadAll ();
+ void Cleanup ();
+ void UnloadVBOFromGfxDevice ();
+ void ReloadVBOToGfxDevice ();
+ void ReloadPrecomputedError ();
+ void ReloadBounds ();
+ void RecursiveCalculateLod (QuadTreeNode &node);
+ void SetNeighbors (TerrainRenderer *left, TerrainRenderer *top, TerrainRenderer *right, TerrainRenderer *bottom);
+
+ PPtr<TerrainData> GetTerrainData() const { return m_TerrainData; }
+ void SetTerrainData(PPtr<TerrainData> value);
+
+ int GetLightmapIndex() { return m_LightmapIndex; }
+ void SetLightmapIndex(int value) { m_LightmapIndex = value; }
+
+ std::vector<Mesh*> GetMeshPatches();
+
+ #if UNITY_EDITOR
+ void SetLightmapSize(int lightmapSize) { m_LightmapSize = lightmapSize; }
+ #endif
+
+private:
+
+ int m_CurrentLayer;
+ Camera *m_CurrentCamera;
+ Material *m_CurrentBaseMaterial;
+ int m_CurrentMaterialCount;
+ Material **m_CurrentMaterials;
+ PPtr<TerrainData> m_TerrainData;
+ int m_LightmapIndex;
+ #if UNITY_EDITOR
+ SInt32 m_InstanceID;
+ int m_LightmapSize;
+ #endif
+
+ std::list<VBO*> m_FreeVBOPool;
+ VBO* CreateVBO();
+ void ReclaimVBO(VBO* vbo);
+
+ QuadTreeNode &GetRootNode() { return m_Quadtree.back(); }
+ bool IsRootNode (const QuadTreeNode& node) { return (node.level == GetRootNode().level); }
+
+
+ int GetIndex (int x, int y, int level);
+ QuadTreeNode *GetNode (int x, int y, int level);
+ QuadTreeNode *GetNodeAndRenderer (int x, int y, int level, TerrainRenderer *&renderer);
+ int GetPatchCountX (int level);
+ int GetPatchCountY (int level);
+ void RebuildNodes ();
+ Vector3f GetNodePosition (QuadTreeNode &node);
+ int CalculateEdgeMask (QuadTreeNode &node);
+ QuadTreeNode *FindNeighbor (QuadTreeNode &node, int direction);
+ QuadTreeNode *FindNeighborAndRenderer (QuadTreeNode &node, int direction, TerrainRenderer *&renderer);
+ QuadTreeNode *FindChild (QuadTreeNode &node, int direction);
+ QuadTreeNode *FindParent (QuadTreeNode &node);
+ void CleanupMeshes ();
+ static float CalculateSqrDistance (Vector3f &rkPoint, AABB &rkBox);
+ void MarkChildVisibilityRecurse (QuadTreeNode &node, int newVisibility);
+ void ForceSplitParent (QuadTreeNode &node);
+ void EnforceLodTransitions (QuadTreeNode &node);
+ void RemoveMesh (QuadTreeNode &node);
+ void RecursiveRemoveMeshes (QuadTreeNode &node);
+ void RenderNode (QuadTreeNode &node);
+ void BuildRenderer (QuadTreeNode &node, int edgeMask);
+ void RecursiveRenderMeshes (QuadTreeNode &node, Heightmap &heightmap);
+
+ friend class QuadTreeNodeRenderer;
+};
+
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/Tree.cpp b/Runtime/Terrain/Tree.cpp
new file mode 100644
index 0000000..630e319
--- /dev/null
+++ b/Runtime/Terrain/Tree.cpp
@@ -0,0 +1,78 @@
+#include "UnityPrefix.h"
+#include "Tree.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Shaders/MaterialProperties.h"
+#include "Runtime/Filters/Mesh/MeshRenderer.h"
+#include "External/shaderlab/Library/FastPropertyName.h"
+#include "Wind.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/BaseClasses/SupportedMessageOptimization.h"
+
+IMPLEMENT_CLASS_HAS_INIT(Tree)
+IMPLEMENT_OBJECT_SERIALIZE(Tree)
+
+SHADERPROP(Wind);
+
+Tree::Tree(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+Tree::~Tree()
+{
+}
+
+void Tree::InitializeClass ()
+{
+ REGISTER_MESSAGE_VOID (Tree, kOnWillRenderObject, OnWillRenderObject);
+}
+
+UInt32 Tree::CalculateSupportedMessages ()
+{
+ return kHasOnWillRenderObject;
+}
+
+
+template<class TransferFunc>
+void Tree::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer(transfer);
+ TRANSFER_EDITOR_ONLY(m_TreeData);
+}
+
+void Tree::SetTreeData (PPtr<MonoBehaviour> tree)
+{
+ #if UNITY_EDITOR
+ m_TreeData = tree;
+ SetDirty();
+ #endif
+}
+
+PPtr<MonoBehaviour> Tree::GetTreeData ()
+{
+ #if UNITY_EDITOR
+ return m_TreeData;
+ #else
+ return NULL;
+ #endif
+}
+
+void Tree::OnWillRenderObject()
+{
+ MeshRenderer* renderer = QueryComponent(MeshRenderer);
+ if (renderer == NULL)
+ return;
+
+ AABB bounds;
+ renderer->GetWorldAABB(bounds);
+
+ // Compute wind factor from wind zones
+ Vector4f wind = WindManager::GetInstance().ComputeWindForce(bounds);
+
+ // Apply material property block
+ MaterialPropertyBlock& block = renderer->GetPropertyBlockRememberToUpdateHash ();
+ block.Clear();
+ block.AddPropertyVector(kSLPropWind, wind);
+ renderer->ComputeCustomPropertiesHash();
+}
diff --git a/Runtime/Terrain/Tree.h b/Runtime/Terrain/Tree.h
new file mode 100644
index 0000000..af4e069
--- /dev/null
+++ b/Runtime/Terrain/Tree.h
@@ -0,0 +1,36 @@
+#ifndef TREE_H
+#define TREE_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+
+class MonoBehaviour;
+
+
+
+class Tree : public Unity::Component
+{
+public:
+public:
+ REGISTER_DERIVED_CLASS(Tree, Component)
+ DECLARE_OBJECT_SERIALIZE(Tree)
+
+ Tree(MemLabelId label, ObjectCreationMode mode);
+ // ~Tree(); declared by a macro
+
+ void SetTreeData (PPtr<MonoBehaviour> tree);
+ PPtr<MonoBehaviour> GetTreeData ();
+
+ static void InitializeClass ();
+ static void CleanupClass () {}
+
+ void OnWillRenderObject ();
+
+private:
+ UInt32 CalculateSupportedMessages ();
+
+ #if UNITY_EDITOR
+ PPtr<MonoBehaviour> m_TreeData;
+ #endif
+};
+
+#endif
diff --git a/Runtime/Terrain/TreeDatabase.cpp b/Runtime/Terrain/TreeDatabase.cpp
new file mode 100644
index 0000000..bec9140
--- /dev/null
+++ b/Runtime/Terrain/TreeDatabase.cpp
@@ -0,0 +1,283 @@
+#include "UnityPrefix.h"
+#include "TreeDatabase.h"
+
+#if ENABLE_TERRAIN
+
+#include "TerrainData.h"
+#include "Runtime/Filters/Mesh/LodMeshFilter.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Scripting/Scripting.h"
+
+TreeDatabase::Prototype::Prototype()
+: treeHeight (1)
+, treeVisibleHeight (1)
+, treeWidth (1)
+, treeAspectRatio (1)
+, bounds (Vector3f::zero, Vector3f::zero)
+, bendFactor (0)
+{
+ prefab = NULL;
+ mesh = NULL;
+}
+
+TreeDatabase::Prototype::~Prototype()
+{
+ for (std::vector<Material*>::iterator it = imposterMaterials.begin(), end = imposterMaterials.end(); it != end; ++it)
+ DestroySingleObject(*it);
+
+ for (std::vector<Material*>::iterator it = materials.begin(), end = materials.end(); it != end; ++it)
+ DestroySingleObject(*it);
+}
+
+void TreeDatabase::Prototype::Set(const PPtr<Unity::GameObject>& source, float inBendFactor, const TerrainData& terrainData)
+{
+ prefab = source;
+ if (!source)
+ {
+ ErrorStringObject(
+ Format(
+ "A tree couldn't be loaded because the prefab is missing.\nPlease select any instance of the %s asset in a scene and make it reference only tree prefabs that exist.",
+ terrainData.GetName ()),
+ &terrainData);
+ return;
+ }
+
+ MeshFilter* filter = prefab->QueryComponent(MeshFilter);
+ Renderer* renderer = prefab->QueryComponent(Renderer);
+
+ if (!filter || !filter->GetSharedMesh() || !renderer)
+ {
+ WarningStringObject(std::string() + "The tree " + source->GetName() + " couldn't be instanced because the prefab contains no valid mesh renderer", source);
+ return;
+ }
+
+ mesh = filter->GetSharedMesh();
+ const Renderer::MaterialArray& originalMaterials = renderer->GetMaterialArray();
+ for (int m = 0; m < originalMaterials.size(); ++m) {
+ if (!originalMaterials[m])
+ {
+ WarningStringObject(std::string() + "The tree " + source->GetName() + " couldn't be instanced because one of the materials is missing", source);
+ return;
+ }
+ }
+
+ // * Create cloned imposter materials using special billboard shader
+ // * Create cloned normal materials so we can modify the color in place
+ // * Setup orginal colors array
+ //materials = originalMaterials;
+ imposterMaterials.resize(originalMaterials.size());
+ materials.resize(originalMaterials.size());
+ originalMaterialColors.resize(originalMaterials.size());
+ inverseAlphaCutoff.resize(originalMaterials.size());
+
+ for (int m = 0; m < originalMaterials.size(); m++)
+ {
+ if (!SetMaterial (m, originalMaterials[m]))
+ {
+ WarningStringObject(std::string() + "The tree " + source->GetName() + " must use the Nature/Soft Occlusion shader. Otherwise billboarding/lighting will not work correctly.", source);
+ }
+ }
+
+
+ bounds = mesh->GetBounds();
+ MinMaxAABB minMaxBounds(bounds);
+ treeVisibleHeight = minMaxBounds.GetMax().y;
+ treeHeight = bounds.GetExtent().y * 2;
+
+ float x = std::max(std::abs(minMaxBounds.GetMin().x), std::abs(minMaxBounds.GetMax().x));
+ float z = std::max(std::abs(minMaxBounds.GetMin().z), std::abs(minMaxBounds.GetMax().z));
+
+ treeWidth = std::max(x, z) * 1.9F;
+
+ treeAspectRatio = treeWidth / treeHeight;
+ bendFactor = inBendFactor;
+}
+
+
+bool TreeDatabase::Prototype::SetMaterial (int index, Material* material)
+{
+ if (index < 0 || index >= materials.size())
+ return true;
+
+ const ShaderLab::FastPropertyName colorProperty = ShaderLab::Property("_Color");
+ const ShaderLab::FastPropertyName cutoffProperty = ShaderLab::Property("_Cutoff");
+
+ if (material->HasProperty(colorProperty))
+ originalMaterialColors[index] = material->GetColor(colorProperty);
+ else
+ originalMaterialColors[index].Set(1, 1, 1, 1);
+
+ inverseAlphaCutoff[index] = 1.0F;
+ if (material->HasProperty(cutoffProperty))
+ inverseAlphaCutoff[index] = 0.5F / material->GetFloat(cutoffProperty);
+
+ // Instantiate normal material.
+ if (materials[index])
+ DestroySingleObject (materials[index]);
+ materials[index] = Material::CreateMaterial (*material, Object::kHideAndDontSave);
+
+ // Instantiate a special billboarding material.
+ // Eg. leaves shader needs specialized premultiplied alpha rendering into render tex.
+ if (imposterMaterials[index])
+ DestroySingleObject (imposterMaterials[index]);
+ imposterMaterials[index] = Material::CreateMaterial (*material, Object::kHideAndDontSave);
+ Shader* imposterShader = imposterMaterials[index]->GetShader()->GetDependency ("BillboardShader");
+ if (!imposterShader)
+ return false;
+
+ imposterMaterials[index]->SetShader(imposterShader);
+ return true;
+}
+
+
+TreeDatabase::TreeDatabase (TerrainData& source)
+: m_SourceData(source)
+{
+}
+
+void TreeDatabase::RefreshPrototypes ()
+{
+ m_Prototypes.clear();
+
+ const vector<TreePrototype>& sourcePrototypes = GetTreePrototypes();
+ m_Prototypes.resize(sourcePrototypes.size());
+ for (int i = 0; i < m_Prototypes.size(); ++i)
+ m_Prototypes[i].Set(sourcePrototypes[i].prefab, sourcePrototypes[i].bendFactor, m_SourceData);
+
+ m_SourceData.UpdateUsers (TerrainData::kFlushEverythingImmediately);
+}
+
+
+// TODO: make sure all trees are within bounds.
+void TreeDatabase::ValidateTrees ()
+{
+ int prototypeCount = m_TreePrototypes.size();
+ for (vector<TreeInstance>::iterator i = m_Instances.begin(); i != m_Instances.end(); i++)
+ {
+ i->position.x = clamp01(i->position.x);
+ i->position.y = clamp01(i->position.y);
+ i->position.z = clamp01(i->position.z);
+ i->index = clamp(i->index,0,prototypeCount - 1);
+ }
+}
+
+void TreeDatabase::RecalculateTreePositions ()
+{
+ Heightmap *heightmap = &m_SourceData.GetHeightmap();
+ Vector3f terrainSize = heightmap->GetSize();
+ for (int i = 0; i < m_Instances.size(); i++)
+ {
+ Vector3f pos = m_Instances[i].position;
+ pos.y = heightmap->GetInterpolatedHeight(pos.x, pos.z) / terrainSize.y;
+ m_Instances[i].position = pos;
+ }
+
+ UpdateTreeInstances();
+}
+
+void TreeDatabase::AddTree (const TreeInstance& tree)
+{
+ m_Instances.push_back(tree);
+
+ const Heightmap& heightmap = m_SourceData.GetHeightmap();
+
+ float height = heightmap.GetInterpolatedHeight(tree.position.x, tree.position.z);
+ height /= heightmap.GetSize().y;
+ m_Instances.back().position.y = height;
+
+ UpdateTreeInstances();
+}
+
+int TreeDatabase::RemoveTrees (const Vector2f& position, float radius, int prototypeIndex)
+{
+ float sqrRadius = radius * radius;
+ std::vector<TreeInstance> instances;
+ instances.reserve(m_Instances.size());
+ for (int i=0; i<m_Instances.size(); ++i)
+ {
+ const TreeInstance& instance = m_Instances[i];
+ Vector2f offset(instance.position.x - position.x, instance.position.z - position.y);
+ bool shouldRemovePrototypeIndex = prototypeIndex == instance.index || prototypeIndex == -1;
+ if (!shouldRemovePrototypeIndex || SqrMagnitude(offset) > sqrRadius)
+ instances.push_back(instance);
+ }
+
+ int removedTrees = 0;
+ if (m_Instances.size() != instances.size())
+ {
+ removedTrees = m_Instances.size() - instances.size();
+ m_Instances = instances;
+
+ UpdateTreeInstances();
+ }
+ return removedTrees;
+}
+
+void TreeDatabase::UpdateTreeInstances ()
+{
+ ValidateTrees ();
+ m_SourceData.SetDirty ();
+ m_SourceData.UpdateUsers (TerrainData::kTreeInstances);
+}
+
+void TreeDatabase::SetTreePrototypes (const vector<TreePrototype> &treePrototypes)
+{
+ m_TreePrototypes = treePrototypes;
+ ValidateTrees ();
+
+ RefreshPrototypes();
+
+ m_SourceData.SetDirty ();
+}
+
+void TreeDatabase::RemoveTreePrototype (int index)
+{
+#if UNITY_EDITOR
+
+ if( index < 0 || index >= m_TreePrototypes.size() )
+ {
+ ErrorString("invalid tree prototype index");
+ return;
+ }
+
+ // erase tree prototype
+ m_TreePrototypes.erase( m_TreePrototypes.begin() + index );
+
+ // update tree instance indices
+ for( vector<TreeInstance>::iterator it = m_Instances.begin(); it != m_Instances.end(); /**/ )
+ {
+ if( it->index == index )
+ {
+ it = m_Instances.erase( it );
+ }
+ else
+ {
+ if( it->index > index )
+ it->index--;
+ ++it;
+ }
+ }
+
+ m_SourceData.SetDirty();
+
+ RefreshPrototypes();
+
+#else
+ ErrorString("only implemented in editor");
+#endif
+}
+
+
+void TreePrototypeToMono (const TreePrototype &src, MonoTreePrototype &dest) {
+ dest.prefab = Scripting::ScriptingWrapperFor (src.prefab);
+ dest.bendFactor = src.bendFactor;
+}
+void TreePrototypeToCpp (MonoTreePrototype &src, TreePrototype &dest) {
+ dest.prefab = ScriptingObjectToObject<GameObject> (src.prefab);
+ dest.bendFactor = src.bendFactor;
+}
+
+
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/TreeDatabase.h b/Runtime/Terrain/TreeDatabase.h
new file mode 100644
index 0000000..6f07e8c
--- /dev/null
+++ b/Runtime/Terrain/TreeDatabase.h
@@ -0,0 +1,152 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_TERRAIN
+
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+
+#include <vector>
+
+class Vector2f;
+class ColorRGBAf;
+class TerrainData;
+struct TreeInstance;
+
+namespace Unity
+{
+ class GameObject;
+ class Material;
+}
+
+struct TreeInstance
+{
+ DECLARE_SERIALIZE (TreeInstance)
+
+ Vector3f position;
+ float widthScale;
+ float heightScale;
+ ColorRGBA32 color;
+ ColorRGBA32 lightmapColor;
+ int index;
+ float temporaryDistance;
+};
+
+template<class TransferFunc>
+void TreeInstance::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (position);
+ TRANSFER (widthScale);
+ TRANSFER (heightScale);
+ TRANSFER (color);
+ TRANSFER (lightmapColor);
+ TRANSFER (index);
+}
+
+struct TreePrototype
+{
+ DECLARE_SERIALIZE (TreePrototype)
+
+ PPtr<GameObject> prefab;
+ float bendFactor;
+
+ TreePrototype ()
+ {
+ bendFactor = 1.0F;
+ }
+};
+
+
+struct MonoTreePrototype {
+ ScriptingObjectPtr prefab;
+ float bendFactor;
+};
+void TreePrototypeToMono (const TreePrototype &src, MonoTreePrototype &dest) ;
+void TreePrototypeToCpp (MonoTreePrototype &src, TreePrototype &dest);
+
+template<class TransferFunc>
+void TreePrototype::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (prefab);
+ TRANSFER (bendFactor);
+}
+
+class TreeDatabase
+{
+public:
+ class Prototype
+ {
+ public:
+ Prototype();
+ ~Prototype();
+
+ void Set (const PPtr<Unity::GameObject>& source, float inBendFactor, const TerrainData& terrainData);
+ bool SetMaterial (int index, Material* material);
+
+ // if a tree is more tall than wide then we want to use square billboards,
+ // because tree has to fit into billboard when looking from above (or bellow)
+ float getBillboardAspect() const { return std::min<float>(1.f, treeAspectRatio); }
+ float getBillboardHeight() const { return std::max<float>(treeHeight, treeWidth); }
+
+ // offset of center point of the tree from the ground (i.e. from pivot point/root of the tree)
+ float getCenterOffset() const { return treeVisibleHeight - treeHeight / 2; }
+
+ public:
+ PPtr<Unity::GameObject> prefab;
+ PPtr<Mesh> mesh;
+
+ std::vector<float> inverseAlphaCutoff;
+ std::vector<Material*> materials;
+ std::vector<ColorRGBAf> originalMaterialColors;
+ std::vector<Material*> imposterMaterials;
+
+ // actual tree height from the bottom to the top
+ float treeHeight;
+ // visible tree height, i.e. the part which is above terrain (i.e. from pivot point/root of the tree to the top)
+ float treeVisibleHeight;
+ // The width of the tree
+ float treeWidth;
+ // How wide is the tree relative to the height. (Usually less than 1)
+ float treeAspectRatio;
+ AABB bounds;
+ float bendFactor;
+ };
+
+ typedef std::vector<Prototype> PrototypeVector;
+
+public:
+ TreeDatabase (TerrainData& source);
+
+ void AddTree (const TreeInstance& tree);
+ int RemoveTrees (const Vector2f& position, float radius, int prototypeIndex);
+
+ TerrainData& GetTerrainData() const { return m_SourceData; }
+
+ PrototypeVector& GetPrototypes() { return m_Prototypes; }
+ const std::vector<Prototype>& GetPrototypes() const { return m_Prototypes; }
+
+
+ std::vector<TreeInstance> &GetInstances () { return m_Instances; }
+ std::vector<TreePrototype> &GetTreePrototypes () { return m_TreePrototypes; }
+
+ void SetTreePrototypes (const std::vector<TreePrototype> &treePrototypes );
+ void RefreshPrototypes ();
+ void RemoveTreePrototype (int index);
+
+ void UpdateTreeInstances ();
+ void RecalculateTreePositions ();
+ void ValidateTrees ();
+
+private:
+ TerrainData& m_SourceData;
+ std::vector<TreePrototype> m_TreePrototypes;
+ std::vector<TreeInstance> m_Instances;
+
+ PrototypeVector m_Prototypes;
+};
+
+#endif // ENABLE_TERRAIN
+
+
diff --git a/Runtime/Terrain/TreeRenderer.cpp b/Runtime/Terrain/TreeRenderer.cpp
new file mode 100644
index 0000000..6ebb3f0
--- /dev/null
+++ b/Runtime/Terrain/TreeRenderer.cpp
@@ -0,0 +1,1223 @@
+#include "UnityPrefix.h"
+#include "TreeRenderer.h"
+
+#if ENABLE_TERRAIN
+
+#include "TerrainData.h"
+#include "PerlinNoise.h"
+#include "Wind.h"
+
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Camera/Light.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Camera/IntermediateRenderer.h"
+#include "Runtime/Camera/RenderSettings.h"
+#include "Runtime/Camera/Shadows.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Shaders/MaterialProperties.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "External/shaderlab/Library/properties.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+
+static ShaderLab::FastPropertyName kTerrainTreeLightDirections[4] = {
+ ShaderLab::Property("_TerrainTreeLightDirections0"),
+ ShaderLab::Property("_TerrainTreeLightDirections1"),
+ ShaderLab::Property("_TerrainTreeLightDirections2"),
+ ShaderLab::Property("_TerrainTreeLightDirections3"),
+};
+static ShaderLab::FastPropertyName kTerrainTreeLightColors[4] = {
+ ShaderLab::Property("_TerrainTreeLightColors0"),
+ ShaderLab::Property("_TerrainTreeLightColors1"),
+ ShaderLab::Property("_TerrainTreeLightColors2"),
+ ShaderLab::Property("_TerrainTreeLightColors3"),
+};
+
+
+// when we have shadow we need to emit all renderers to let them casting shadows
+#define EMIT_ALL_TREE_RENDERERS ENABLE_SHADOWS
+
+
+// --------------------------------------------------------------------------
+
+
+struct TreeBinaryTree : public NonCopyable
+{
+ TreeBinaryTree(TreeDatabase* database)
+ : database(database), mesh(0), sortIndex(0), targetSortIndex(0), visible(0)
+ {
+ Assert(database != NULL);
+ }
+
+ ~TreeBinaryTree()
+ {
+ DestroySingleObject(mesh);
+ }
+
+ TreeDatabase* database;
+ std::vector<int> instances;
+ AABB bounds;
+ Mesh* mesh;
+
+ int sortIndex;
+ int targetSortIndex;
+ int visible;
+
+ Plane splittingPlane;
+ std::vector<int> usedPrototypes;
+
+ std::auto_ptr<TreeBinaryTree> left;
+ std::auto_ptr<TreeBinaryTree> right;
+};
+
+class TreeBinaryTreeBuilder
+{
+public:
+ static bool AddLastTree (TreeBinaryTree& binTree, const Vector3f& position, const Vector3f& scale, int minimumInstances)
+ {
+ const std::vector<TreeInstance>& instances = binTree.database->GetInstances();
+ Assert(!instances.empty());
+ const TreeInstance& instance = instances.back();
+
+ const std::vector<TreeDatabase::Prototype>& prototypes = binTree.database->GetPrototypes();
+
+ EncapsulateBounds(binTree.bounds, instance, prototypes, position, scale);
+
+ // Leaf node
+ if (!binTree.left.get())
+ {
+ binTree.sortIndex = -1;
+ if (binTree.visible != 0)
+ {
+ binTree.visible = 0;
+ DestroySingleObject(binTree.mesh);
+ binTree.mesh = 0;
+ }
+
+ if (binTree.instances.empty())
+ {
+ binTree.instances.resize(1);
+ binTree.instances[0] = instances.size() - 1;
+ binTree.bounds = CalculateBounds(instances, binTree.instances, prototypes, position, scale);
+ binTree.usedPrototypes = CalculateSupportedInstances(instances, binTree.instances, prototypes);
+
+ return true;
+ }
+ else if (binTree.instances.size() < minimumInstances * 2)
+ {
+ binTree.instances.push_back(instances.size() - 1);
+ EncapsulateBounds(binTree.bounds, instance, prototypes, position, scale);
+ binTree.usedPrototypes = CalculateSupportedInstances(instances, binTree.instances, prototypes);
+ return true;
+ }
+ else
+ // Adding will actually fail once all leaves are filled up to minimumInstances * 2.
+ // Is that what we want?
+ return false;
+ }
+ else
+ {
+ const Vector3f pos = Scale(instance.position, scale);
+ if (binTree.splittingPlane.GetSide (pos))
+ return AddLastTree(*binTree.left, position, scale, minimumInstances);
+ else
+ return AddLastTree(*binTree.right, position, scale, minimumInstances);
+ }
+ }
+
+ static std::auto_ptr<TreeBinaryTree> Build(TreeDatabase& database, const Vector3f& position, const Vector3f& scale, int minimumInstances)
+ {
+ std::auto_ptr<TreeBinaryTree> tree(new TreeBinaryTree(&database));
+ const std::vector<TreeInstance>& instances = database.GetInstances();
+ if (instances.empty())
+ return tree;
+
+ tree->instances.resize(instances.size());
+ for (int i = 0; i < instances.size(); ++i)
+ {
+ tree->instances[i] = i;
+ }
+
+ Assert(Magnitude(scale) > 0);
+ Split(*tree, position, scale, minimumInstances);
+ return tree;
+ }
+
+private:
+ static void Split(TreeBinaryTree& bintree, const Vector3f& position, const Vector3f& scale, int minimumInstances)
+ {
+ const std::vector<TreeInstance>& allInstances = bintree.database->GetInstances();
+ const std::vector<TreeDatabase::Prototype>& prototypes = bintree.database->GetPrototypes();
+ bintree.bounds = CalculateBounds(allInstances, bintree.instances, prototypes, position, scale);
+ bintree.usedPrototypes = CalculateSupportedInstances(allInstances, bintree.instances, prototypes);
+
+ // Stop splitting when we reach a certain amount of trees
+ if (bintree.instances.size() <= minimumInstances)
+ return;
+
+ {
+ // We need to calculate bounds of position of all instances and then use median position (i.e. center)
+ // as a splitting point. We can't just use center of bintree.bounds, because it might be shifted sideways
+ // if bounds of each prototype is shifted and that would result in left or right bintree being empty
+ // (see case 345094)
+ AABB posBounds = CalculatePosBounds (allInstances, bintree.instances, position, scale);
+
+ // The extent might be 0,0,0 if all instances are at the same position. In that case just stop subdiving this subtree.
+ // That will cause this subtree to have more instances than minimumInstances. If that's not we want, then we should
+ // just split the instances in half by the index.
+ if (CompareApproximately (posBounds.GetExtent().x, 0.0f) && CompareApproximately (posBounds.GetExtent().z, 0.0f))
+ return;
+
+ // Splitting plane is the largest axis
+ bintree.splittingPlane.SetNormalAndPosition(
+ posBounds.GetExtent().x > posBounds.GetExtent().z ? Vector3f::xAxis : Vector3f::zAxis,
+ posBounds.GetCenter()
+ );
+ }
+
+ // Now just queue up the binary tree
+ std::vector<int> left, right;
+ for (std::vector<int>::const_iterator it = bintree.instances.begin(), end = bintree.instances.end(); it != end; ++it)
+ {
+ if (bintree.splittingPlane.GetSide (Scale(allInstances[*it].position, scale) + position))
+ left.push_back(*it);
+ else
+ right.push_back(*it);
+ }
+
+ // Due to numerical errors we might still end up with one of the lists empty (not if the the epsilon for CompareApprox was big enough,
+ // but that's another story). In that case just don't split this subtree.
+ if (left.empty() || right.empty())
+ return;
+
+ bintree.instances.clear();
+ bintree.usedPrototypes.clear();
+ bintree.left.reset(new TreeBinaryTree(bintree.database));
+ bintree.right.reset(new TreeBinaryTree(bintree.database));
+ bintree.left->instances.swap(left);
+ bintree.right->instances.swap(right);
+
+ Split (*bintree.left, position, scale, minimumInstances);
+ Split (*bintree.right, position, scale, minimumInstances);
+ }
+
+ // Build bounds of tree positions for a bunch of trees
+ static AABB CalculatePosBounds (const std::vector<TreeInstance>& allInstances, const std::vector<int>& instances, const Vector3f& position, const Vector3f& scale)
+ {
+ AssertIf(instances.empty());
+ AABB bounds(Scale(allInstances[instances[0]].position, scale) + position, Vector3f::zero);
+ for (std::vector<int>::const_iterator it = instances.begin(), end = instances.end(); it != end; ++it)
+ bounds.Encapsulate (Scale(allInstances[*it].position, scale) + position);
+
+ return bounds;
+ }
+
+ // Build bounds for a bunch of trees
+ static AABB CalculateBounds (const std::vector<TreeInstance>& allInstances, const std::vector<int>& instances, const TreeDatabase::PrototypeVector& prototypes, const Vector3f& position, const Vector3f& scale)
+ {
+ AssertIf(instances.empty());
+ AABB bounds(Scale(allInstances[instances[0]].position, scale) + position, Vector3f::zero);
+ for (std::vector<int>::const_iterator it = instances.begin(), end = instances.end(); it != end; ++it)
+ EncapsulateBounds(bounds, allInstances[*it], prototypes, position, scale);
+
+ return bounds;
+ }
+
+ // Encapsulate a single tree in the bounds
+ static void EncapsulateBounds (AABB& bounds, const TreeInstance& instance, const TreeDatabase::PrototypeVector& prototypes, const Vector3f& position, const Vector3f& scale)
+ {
+ Vector3f pos = Scale(instance.position, scale) + position;
+ Vector3f treeScale(instance.widthScale, instance.heightScale, instance.widthScale);
+ const AABB& pb = prototypes[instance.index].bounds;
+ bounds.Encapsulate (pos + Scale(treeScale, pb.GetMin()));
+ bounds.Encapsulate (pos + Scale(treeScale, pb.GetMax()));
+ }
+
+
+ // Calculates an array of indices, for which prototypes are being used in the batch
+ static std::vector<int> CalculateSupportedInstances (const std::vector<TreeInstance>& allInstances, const std::vector<int>& instances, const TreeDatabase::PrototypeVector& prototypes)
+ {
+ std::vector<char> supported(prototypes.size(), 0);
+ for (std::vector<int>::const_iterator it = instances.begin(), end = instances.end(); it != end; ++it)
+ supported[allInstances[*it].index] = 1;
+
+ std::vector<int> supportedIndices;
+ supportedIndices.reserve(prototypes.size());
+ for (int i = 0; i < supported.size(); ++i)
+ {
+ if (supported[i])
+ supportedIndices.push_back(i);
+ }
+
+ // we do explicit copy because we want to reduce reserved size
+ return std::vector<int>(supportedIndices.begin(), supportedIndices.end());
+ }
+};
+
+struct TreeInstanceIndexSorter : public std::binary_function<int, int, bool>
+{
+ const std::vector<TreeInstance>* m_AllInstances;
+
+ TreeInstanceIndexSorter(const std::vector<TreeInstance>& allInstances)
+ : m_AllInstances(&allInstances)
+ { }
+
+ bool operator ()(int lhs, int rhs) const
+ {
+ return (*m_AllInstances)[lhs].temporaryDistance > (*m_AllInstances)[rhs].temporaryDistance;
+ }
+};
+
+class TreeMeshIntermediateRenderer : public MeshIntermediateRenderer
+{
+public:
+ static ForwardLinearAllocator* s_Allocator;
+
+ inline void* operator new( size_t size)
+ {
+ Assert(s_Allocator != NULL);
+ return s_Allocator->allocate(size);
+ }
+
+ inline void operator delete( void* p)
+ {
+ Assert(s_Allocator != NULL);
+ s_Allocator->deallocate(p);
+ }
+
+ void Update(int layer, bool shadows, int lightmapIndex, const MaterialPropertyBlock& properties)
+ {
+ m_Layer = layer;
+ m_CastShadows = m_ReceiveShadows = shadows;
+ SetLightmapIndexIntNoDirty(lightmapIndex);
+ SetPropertyBlock(properties);
+ }
+};
+
+ForwardLinearAllocator* TreeMeshIntermediateRenderer::s_Allocator = NULL;
+
+
+// --------------------------------------------------------------------------
+
+
+
+const int kTreesPerBatch = 500;
+
+TreeRenderer::TreeRenderer(TreeDatabase& database, const Vector3f& position, int lightmapIndex)
+: m_Database(0), m_BillboardMaterial(0), m_TreeBinaryTree(0), m_CloseBillboardMesh(0), m_LightmapIndex(lightmapIndex)
+, m_RendererAllocator(8 * 1024, kMemTerrain)
+, m_LegacyTreeSceneNodes(kMemTerrain), m_LegacyTreeBoundingBoxes(kMemTerrain), m_TreeIndexToSceneNode(kMemTerrain)
+{
+ const TreeDatabase::PrototypeVector& prototypes = database.GetPrototypes();
+ bool anyValidTrees = false;
+ for (TreeDatabase::PrototypeVector::const_iterator it = prototypes.begin(), end = prototypes.end(); it != end; ++it)
+ {
+ if (!it->materials.empty())
+ anyValidTrees = true;
+ }
+
+ if (!anyValidTrees)
+ return;
+
+ m_Database = &database;
+
+ m_TerrainSize = database.GetTerrainData().GetHeightmap().GetSize();
+ m_Position = position;
+ m_ImposterRenderTexture.reset(new ImposterRenderTexture(database));
+
+ // SetupMaterials
+ Shader* s = GetScriptMapper().FindShader("Hidden/TerrainEngine/BillboardTree");
+ if (s == NULL)
+ {
+ ErrorString("Unable to find shaders used for the terrain engine. Please include Nature/Terrain/Diffuse shader in Graphics settings.");
+ s = GetScriptMapper().FindShader("Diffuse");
+ }
+ m_BillboardMaterial = Material::CreateMaterial(*s, Object::kHideAndDontSave);
+ if (m_BillboardMaterial->HasProperty(ShaderLab::Property("_MainTex")))
+ m_BillboardMaterial->SetTexture(ShaderLab::Property("_MainTex"), m_ImposterRenderTexture->GetTexture());
+
+ if (database.GetInstances().empty() || database.GetPrototypes().empty())
+ return;
+
+
+ // mircea@ for some reason it doesn't compile on the PS3 if assigned directly...
+ std::auto_ptr<TreeBinaryTree> TreeBinaryTree = TreeBinaryTreeBuilder::Build(database, m_Position, m_TerrainSize, kTreesPerBatch);
+ m_TreeBinaryTree = TreeBinaryTree;
+
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ m_RendererAllocator.SetThreadIDs(Thread::mainThreadId, Thread::mainThreadId);
+#endif
+
+ for (int i = 0; i < database.GetInstances().size(); ++i)
+ {
+ CreateTreeRenderer(i);
+ }
+}
+
+TreeRenderer::~TreeRenderer()
+{
+ CleanupBillboardMeshes();
+
+ DestroySingleObject(m_BillboardMaterial);
+ m_BillboardMaterial = 0;
+
+ DeleteTreeRenderers();
+}
+
+
+/// Injects a single tree into the renderer, without requiring a full rebuild of the binary tree.
+/// Requires that there is at least one tree in the renderer
+void TreeRenderer::InjectTree(const TreeInstance& newTree)
+{
+ Assert(&newTree == &m_Database->GetInstances().back());
+ if (!m_TreeBinaryTree.get() || !TreeBinaryTreeBuilder::AddLastTree(*m_TreeBinaryTree, m_Position, m_TerrainSize, kTreesPerBatch))
+ {
+ // Failed adding the tree (Too many trees in one batch)
+
+ // mircea@ for some reason it doesn't compile on the PS3 if assigned directly...
+ std::auto_ptr<TreeBinaryTree> TreeBinaryTree = TreeBinaryTreeBuilder::Build(*m_Database, m_Position, m_TerrainSize, kTreesPerBatch);
+ m_TreeBinaryTree = TreeBinaryTree;
+ }
+ CreateTreeRenderer(m_Database->GetInstances().size() - 1);
+ //@TODO: Cleanup meshes
+}
+
+void TreeRenderer::RemoveTrees(const Vector3f& pos, float radius, int prototypeIndex)
+{
+ // hack
+ ReloadTrees();
+}
+
+void TreeRenderer::ReloadTrees()
+{
+ ReloadTrees(kTreesPerBatch);
+}
+
+void TreeRenderer::ReloadTrees(int treesPerBatch)
+{
+ if (m_Database)
+ {
+ if (m_Database->GetInstances().empty())
+ m_TreeBinaryTree.reset();
+ else
+ {
+ // mircea@ for some reason it doesn't compile on the PS3 if assigned directly...
+ std::auto_ptr<TreeBinaryTree> TreeBinaryTree = TreeBinaryTreeBuilder::Build(*m_Database, m_Position, m_TerrainSize, treesPerBatch);
+ m_TreeBinaryTree = TreeBinaryTree;
+ }
+ m_RenderedBatches.clear();
+
+ DeleteTreeRenderers();
+ m_LegacyTreeSceneNodes.resize_uninitialized(0);
+ m_LegacyTreeBoundingBoxes.resize_uninitialized(0);
+ m_TreeIndexToSceneNode.resize_uninitialized(0);
+ for (int i = 0; i < m_Database->GetInstances().size(); ++i)
+ {
+ CreateTreeRenderer(i);
+ }
+ }
+}
+
+namespace {
+ // Encapsulate a single tree in the bounds
+ static void GetBounds (AABB& bounds, const TreeInstance& instance, const TreeDatabase::PrototypeVector& prototypes, const Vector3f& position, const Vector3f& scale)
+ {
+ Vector3f pos = Scale(instance.position, scale) + position;
+ Vector3f treeScale(instance.widthScale, instance.heightScale, instance.widthScale);
+ const AABB& pb = prototypes[instance.index].bounds;
+ bounds.SetCenterAndExtent(pos, Scale(treeScale, pb.GetExtent()));
+ }
+}
+
+static void CalculateTreeBend (const AABB& bounds, float bendFactor, float time, Matrix4x4f& matrix, Vector4f& outWind)
+{
+ Vector4f force = WindManager::GetInstance().ComputeWindForce(bounds);
+
+ const Vector3f& pos = bounds.GetCenter();
+
+ Vector3f quaternionAxis(force.z, 0, -force.x);
+ float magnitude = Magnitude(quaternionAxis);
+ if (magnitude > 0.00001f)
+ quaternionAxis = Normalize(quaternionAxis);
+ else
+ {
+ magnitude = 0;
+ quaternionAxis.z = 1;
+ }
+
+ Vector2f additionBend(
+ PerlinNoise::NoiseNormalized(pos.x * 0.22F - time * 4.7F, pos.x * 9.005F) * 0.7f,
+ PerlinNoise::NoiseNormalized(pos.z * 0.22F - time * 4.3F, pos.z * 9.005F) * 0.5f
+ );
+
+ Quaternionf q = EulerToQuaternion(Vector3f(additionBend.x, 0, additionBend.y) * (bendFactor * kDeg2Rad * force.w));;
+
+ float bend = 7 * bendFactor * kDeg2Rad * magnitude;
+ matrix.SetTRS(Vector3f::zero, AxisAngleToQuaternion(quaternionAxis, bend) * q, Vector3f::one);
+ outWind = force;
+}
+
+static void RenderMeshIdentityMatrix (Mesh& mesh, Material& material, int layer, Camera& camera, const MaterialPropertyBlock& properties)
+{
+ Matrix4x4f matrix;
+ matrix.SetIdentity();
+
+ IntermediateRenderer* r = AddMeshIntermediateRenderer (matrix, &mesh, &material, layer, true, true, 0, &camera);
+ r->SetPropertyBlock (properties);
+}
+
+const int kFrustumPlaneCount = 6;
+
+void TreeRenderer::Render(Camera& camera, const UNITY_VECTOR(kMemRenderer, Light*)& lights, float meshTreeDistance, float billboardTreeDistance, float crossFadeLength, int maximumMeshTrees, int layer)
+{
+ if (!m_TreeBinaryTree.get() || !m_Database)
+ return;
+
+ RenderTexture::SetTemporarilyAllowIndieRenderTexture (true);
+
+ // Graphics emulation might have changed, so we check for a change here
+ bool supportedNow = (RenderTexture::IsEnabled());
+ if (supportedNow != m_ImposterRenderTexture->GetSupported())
+ {
+ m_ImposterRenderTexture.reset(new ImposterRenderTexture(*m_Database));
+ m_BillboardMaterial->SetTexture(ShaderLab::Property("_MainTex"), m_ImposterRenderTexture->GetTexture());
+ }
+
+ // If we don't support render textures or vertex shaders, there will be no
+ // billboards.
+ if (!m_ImposterRenderTexture->GetSupported())
+ {
+ billboardTreeDistance = meshTreeDistance;
+ crossFadeLength = 0.0f;
+ }
+
+ meshTreeDistance = std::min(billboardTreeDistance, meshTreeDistance);
+ crossFadeLength = clamp(crossFadeLength, 0.0F, billboardTreeDistance - meshTreeDistance);
+ m_CrossFadeLength = crossFadeLength;
+ m_SqrMeshTreeDistance = meshTreeDistance * meshTreeDistance;
+ m_SqrBillboardTreeDistance = billboardTreeDistance * billboardTreeDistance;
+ m_SqrCrossFadeEndDistance = (meshTreeDistance + m_CrossFadeLength) * (meshTreeDistance + m_CrossFadeLength);
+
+ Transform& cameraTransform = camera.GetComponent(Transform);
+ const Vector3f& cameraPos = cameraTransform.GetPosition();
+ Vector3f cameraDir = cameraTransform.TransformDirection(Vector3f::zAxis);
+
+ Plane frustum[kFrustumPlaneCount];
+ ExtractProjectionPlanes(camera.GetWorldToClipMatrix(), frustum);
+
+ // Mark as becoming invisible
+ std::vector<TreeBinaryTree*> oldRenderedBatches = m_RenderedBatches;
+ for (std::vector<TreeBinaryTree*>::iterator it = oldRenderedBatches.begin(), end = oldRenderedBatches.end(); it != end; ++it)
+ {
+ TreeBinaryTree* binTree = *it;
+
+ // We need to check if the tree is actually visible here, since it might have been
+ // pulled away from us while rebuilding the bintree or adding a single tree in the editor
+ if (binTree->visible == 1)
+ binTree->visible = -1;
+ }
+ m_RenderedBatches.clear();
+
+ m_FullTrees.clear();
+ std::vector<int> billboardsList;
+
+ // Build billboard meshes and stuff
+
+#if EMIT_ALL_TREE_RENDERERS
+ // calculate distance to camera: do this when we emit all tree renderers
+ // because renderer's skewFade will be using it
+ for (int i = 0; i < m_Database->GetInstances().size(); ++i)
+ {
+ TreeInstance& instance = m_Database->GetInstances()[i];
+ Vector3f offset = GetPosition(instance) - cameraPos;
+ offset.y = 0.0F;
+ instance.temporaryDistance = SqrMagnitude(offset);
+ }
+#endif
+
+ // Collect all batches, billboards and full trees
+ // batches will be rendered in place
+ RenderRecurse(m_TreeBinaryTree.get(), frustum, billboardsList, cameraPos);
+
+ // Cleanup the batches that have become invisible
+ for (std::vector<TreeBinaryTree*>::iterator it = oldRenderedBatches.begin(), end = oldRenderedBatches.end(); it != end; ++it)
+ {
+ TreeBinaryTree* binTree = *it;
+
+ if (binTree->visible == -1)
+ {
+ DestroySingleObject(binTree->mesh);
+ binTree->mesh = 0;
+ binTree->visible = 0;
+ }
+ }
+
+#if !EMIT_ALL_TREE_RENDERERS
+ // Sort single trees back to front
+ std::sort(m_FullTrees.begin(), m_FullTrees.end(), TreeInstanceIndexSorter(m_Database->GetInstances()));
+
+ // We are exceeding the mesh tree limit
+ // Move some trees into billboard list
+ if (m_FullTrees.size() > maximumMeshTrees)
+ {
+ int moveToBillboardCount = m_FullTrees.size() - maximumMeshTrees;
+ billboardsList.insert(billboardsList.end(), m_FullTrees.begin(), m_FullTrees.begin() + moveToBillboardCount);
+ m_FullTrees.erase(m_FullTrees.begin(), m_FullTrees.begin() + moveToBillboardCount);
+ }
+#endif
+
+ // Render the close by billboard mesh (Use immediate mode interface instead)
+
+ // Sort billboards back to front
+ std::sort(billboardsList.begin(), billboardsList.end(), TreeInstanceIndexSorter(m_Database->GetInstances()));
+
+ const int lightCount = std::min<int>(lights.size(), 4);
+ for (int i = 0; i < lightCount; ++i)
+ {
+ const Light* light = lights[i];
+ const Transform& lightTransform = light->GetComponent(Transform);
+ Vector3f forward = RotateVectorByQuat (-lightTransform.GetRotation(), Vector3f::zAxis);
+ ShaderLab::g_GlobalProperties->SetVector(kTerrainTreeLightDirections[i], -forward.x, -forward.y, -forward.z, 0.0f);
+ ColorRGBAf color = light->GetColor() * light->GetIntensity();
+ color = GammaToActiveColorSpace (color);
+ ShaderLab::g_GlobalProperties->SetVector(kTerrainTreeLightColors[i], color.GetPtr());
+ }
+ for (int i = lightCount; i < 4; ++i)
+ ShaderLab::g_GlobalProperties->SetVector(kTerrainTreeLightColors[i], 0.0f, 0.0f, 0.0f, 0.0f);
+
+ GetRenderSettings().SetupAmbient();
+
+ if (m_ImposterRenderTexture->GetSupported())
+ m_ImposterRenderTexture->UpdateImposters (camera);
+
+ // Setup billboard shader properties
+ UpdateShaderProps(camera);
+
+ // Draw far away trees (only billboards)
+ for (std::vector<TreeBinaryTree*>::const_iterator it = m_RenderedBatches.begin(), end = m_RenderedBatches.end(); it != end; ++it)
+ {
+ RenderMeshIdentityMatrix (*(*it)->mesh, *m_BillboardMaterial, layer, camera, m_PropertyBlock);
+ }
+
+ // Draw close by trees (billboard part for cross fade)
+ if (m_ImposterRenderTexture->GetSupported() && !billboardsList.empty())
+ {
+ if (!m_CloseBillboardMesh)
+ {
+ m_CloseBillboardMesh = CreateObjectFromCode<Mesh>(kInstantiateOrCreateFromCodeAwakeFromLoad);
+ m_CloseBillboardMesh->SetHideFlags(Object::kDontSave);
+ m_CloseBillboardMesh->MarkDynamic(); // will be modified each frame, better use dynamic VBOs
+ }
+ GenerateBillboardMesh(*m_CloseBillboardMesh, billboardsList, true);
+ RenderMeshIdentityMatrix (*m_CloseBillboardMesh, *m_BillboardMaterial, layer, camera, m_PropertyBlock);
+ }
+
+#if EMIT_ALL_TREE_RENDERERS
+ // Draw all remaining trees (mesh part)
+#else
+ // Draw close by trees (mesh part)
+#endif
+ const float currentTime = GetTimeManager ().GetTimeSinceLevelLoad ();
+
+ const ShaderLab::FastPropertyName propertyTerrainEngineBendTree = ShaderLab::Property("_TerrainEngineBendTree");
+ const ShaderLab::FastPropertyName propertyColor = ShaderLab::Property("_Color");
+ const ShaderLab::FastPropertyName propertyCutoff = ShaderLab::Property("_Cutoff");
+ const ShaderLab::FastPropertyName propertyScale = ShaderLab::Property("_Scale");
+ const ShaderLab::FastPropertyName propertySquashPlaneNormal = ShaderLab::Property("_SquashPlaneNormal");
+ const ShaderLab::FastPropertyName propertySquashAmount = ShaderLab::Property("_SquashAmount");
+ const ShaderLab::FastPropertyName propertyWind = ShaderLab::Property("_Wind");
+
+ float billboardOffsetFactor = 1;
+ if (m_ImposterRenderTexture->GetSupported())
+ {
+ float tempBillboardAngleFactor;
+ m_ImposterRenderTexture->GetBillboardParams(tempBillboardAngleFactor, billboardOffsetFactor);
+ }
+
+ for (std::vector<int>::iterator it = m_FullTrees.begin(), end = m_FullTrees.end(); it != end; ++it)
+ {
+ const TreeInstance& instance = m_Database->GetInstances()[*it];
+
+ const TreeDatabase::Prototype& prototype = m_Database->GetPrototypes()[instance.index];
+ Mesh* mesh = prototype.mesh;
+ if (!mesh)
+ continue;
+
+ const std::pair<int, int>& indexPair = m_TreeIndexToSceneNode[*it];
+ if (indexPair.first == -1 || indexPair.second == 0)
+ {
+ continue;
+ }
+
+ const std::vector<Material*>& materials = prototype.materials;
+
+ float distance = sqrt(instance.temporaryDistance);
+ float skewFade = 1.0f;
+ // Skewfade is 1 when it looks like a real full mesh tree
+ // Skewfade is 0 where we switch to billboard
+ if (!CompareApproximately(crossFadeLength, 0.0F))
+ skewFade = SmoothStep(0, 1, (meshTreeDistance + crossFadeLength - distance) / crossFadeLength);
+ float squashAmount = Lerp(0.04f, 1.0f, skewFade);
+
+ // Only cast shadows from trees that are at full mesh appearance.
+ // When they start squashing, just stop casting shadows.
+ bool shadows = (skewFade >= 1.0f);
+
+ AABB bounds;
+ GetBounds(bounds, instance, m_Database->GetPrototypes(), m_Position, m_TerrainSize);
+
+ // Tree bending
+ Matrix4x4f rotate;
+ Vector4f wind;
+ CalculateTreeBend(bounds, skewFade * prototype.bendFactor, currentTime, rotate, wind);
+
+ // we need to offset tree squash plane by the same amount as billboard is offsetted
+ const float centerOffset = prototype.treeWidth * instance.widthScale * 0.5F;
+
+ // that will work as long as the trees are not rotated
+ // since the forward direction has to be in model space
+ Vector3f forward = cameraDir;
+
+ ColorRGBAf treeColor = instance.color * instance.lightmapColor;
+
+ int materialCount = std::min<int> (mesh->GetSubMeshCount(), materials.size());
+ materialCount = std::min(materialCount, indexPair.second);
+
+ for (int m = 0; m < materialCount; ++m)
+ {
+ m_PropertyBlock.Clear();
+ m_PropertyBlock.AddPropertyMatrix(propertyTerrainEngineBendTree, rotate);
+
+ ColorRGBAf color = prototype.originalMaterialColors[m] * treeColor;
+
+ // Use the stored inverseAlphaCutoff value, since we're modifying the cutoff value
+ // in the material by using the property block below
+ float cutoff = 0.5f / prototype.inverseAlphaCutoff[m];
+
+ m_PropertyBlock.AddPropertyColor(propertyColor, color);
+ m_PropertyBlock.AddPropertyFloat(propertyCutoff, cutoff);
+ m_PropertyBlock.AddPropertyVector(propertyScale, Vector4f(instance.widthScale, instance.heightScale, instance.widthScale, 1));
+
+ // squash properties
+ // Position of squash plane has to match billboard plane - we use dual mode for billboard planes (see TerrainBillboardTree in TerrainEngine.cginc)
+ m_PropertyBlock.AddPropertyVector(propertySquashPlaneNormal, Vector4f(forward.x, forward.y, forward.z, centerOffset * billboardOffsetFactor));
+ m_PropertyBlock.AddPropertyFloat(propertySquashAmount, squashAmount);
+
+ m_PropertyBlock.AddPropertyVector(propertyWind, wind);
+
+ SceneNode& sceneNode = m_LegacyTreeSceneNodes[indexPair.first + m];
+ TreeMeshIntermediateRenderer* r = static_cast<TreeMeshIntermediateRenderer*>(sceneNode.renderer);
+ r->Update(layer, shadows, m_LightmapIndex != -1 ? 0xFE : -1, m_PropertyBlock);
+ sceneNode.layer = layer;
+ }
+ }
+ RenderTexture::SetTemporarilyAllowIndieRenderTexture (false);
+}
+
+void TreeRenderer::CollectTreeRenderers(dynamic_array<SceneNode>& sceneNodes, dynamic_array<AABB>& boundingBoxes)
+{
+ for (int i = 0; i < m_FullTrees.size(); ++i)
+ {
+ const std::pair<int, int>& indexPair = m_TreeIndexToSceneNode[m_FullTrees[i]];
+ if (indexPair.first == -1 || indexPair.second == 0)
+ {
+ continue;
+ }
+
+ for (int j = 0; j < indexPair.second; ++j)
+ {
+ sceneNodes.push_back(m_LegacyTreeSceneNodes[indexPair.first + j]);
+ boundingBoxes.push_back(m_LegacyTreeBoundingBoxes[indexPair.first + j]);
+ }
+ }
+}
+
+
+// --------------------------------------------------------------------------
+
+
+
+namespace
+{
+ template <class T, class A>
+ const T* GetData(const std::vector<T,A>& data)
+ {
+ return data.empty() ? 0 : &data.front();
+ }
+
+ float Calculate2DSqrDistance (const AABB& rkBox, const Vector3f& rkPoint)
+ {
+ // compute coordinates of point in box coordinate system
+ Vector3f kClosest = rkPoint - rkBox.GetCenter();
+ kClosest.y = kClosest.z;
+ Vector3f extent = rkBox.GetExtent();
+ extent.y = extent.z;
+
+ // project test point onto box
+ float fSqrDistance = 0.0f;
+ float fDelta;
+
+ for (int i = 0; i < 2; ++i)
+ {
+ if ( kClosest[i] < -extent[i] )
+ {
+ fDelta = kClosest[i] + extent[i];
+ fSqrDistance += fDelta * fDelta;
+ kClosest[i] = -extent[i];
+ }
+ else if ( kClosest[i] > extent[i] )
+ {
+ fDelta = kClosest[i] - extent[i];
+ fSqrDistance += fDelta * fDelta;
+ kClosest[i] = extent[i];
+ }
+ }
+
+ return fSqrDistance;
+ }
+
+ namespace SortUtility
+ {
+ static Vector3f sortDirections[] = {
+ Vector3f(-1, 0, 1), Vector3f(0, 0, 1), Vector3f(1, 0, 1),
+ Vector3f(-1, 0, 0), Vector3f(0, 0, 0), Vector3f(1, 0, 0),
+ Vector3f(-1, 0, -1), Vector3f(0, 0, -1), Vector3f(1, 0, -1)
+ };
+
+ const int insideBoundsDirection = 4;
+
+ /* Because we want to do funky alpha blending instead of alpha cutoff (It looks SO MUCH BETTER)
+ We need to sort all patches. Now that would be really expensive, so we do a trick.
+
+ We classify each patch by it's direction relative to the camera.
+ There are 8 potential directions.
+ 0 1 2
+ 3 4 5
+ 6 7 8
+
+ So every frame we check if the direction of the patch has changed and most of the time it doesn't.
+ Only when it changes do we resort. This lets us skip a lot of sorting.
+ Patches inside of the bounding volume are constantly being resorted, which is expensive but we can't do much about it!
+ */
+ int CalculateTargetSortIndex (const AABB& bounds, const Vector3f& cameraPos)
+ {
+ MinMaxAABB aabb(bounds);
+
+ int sortIndex = 0;
+ if (cameraPos.x > aabb.GetMax().x)
+ sortIndex += 2;
+ else if (cameraPos.x > aabb.GetMin().x)
+ sortIndex += 1;
+
+ if (cameraPos.z > aabb.GetMax().z)
+ sortIndex += 0;
+ else if (cameraPos.z > aabb.GetMin().z)
+ sortIndex += 3;
+ else
+ sortIndex += 6;
+
+ return sortIndex;
+ }
+ }
+
+ struct BatchItem
+ {
+ int index;
+ float distance;
+ };
+
+ bool operator<(const BatchItem& soi1, const BatchItem& soi2)
+ {
+ return soi1.distance < soi2.distance;
+ }
+
+ // Sorts a single batch by sort index (See SortUtility for more information)
+ void SortBatch (const TreeBinaryTree& tree, int sortIndex)
+ {
+ // Create sort order indices which are later used to build the sorted triangle indices
+ const std::vector<TreeInstance>& allInstances = tree.database->GetInstances();
+ const std::vector<int>& instances = tree.instances;
+ const int size = instances.size();
+ std::vector<BatchItem> sortOrder(size);
+ for (int i = 0; i < size; ++i)
+ sortOrder[i].index = i;
+
+ if (sortIndex != SortUtility::insideBoundsDirection)
+ {
+ // Build array with distances
+ const Vector3f& direction = SortUtility::sortDirections[sortIndex];
+ for (int i = 0; i < size; ++i)
+ sortOrder[i].distance = Dot(allInstances[instances[i]].position, direction);
+
+ // Generate the sort order by sorting the sortDistances
+ std::sort(sortOrder.begin(), sortOrder.end());
+ }
+
+ // Build a triangle list from the sort order
+ UNITY_TEMP_VECTOR(UInt16) triangles(size * 6);
+ for (int i=0; i < size; ++i)
+ {
+ int triIndex = i * 6;
+ int vertexIndex = sortOrder[i].index * 4;
+ triangles[triIndex+0] = vertexIndex + 0;
+ triangles[triIndex+1] = vertexIndex + 1;
+ triangles[triIndex+2] = vertexIndex + 2;
+ triangles[triIndex+3] = vertexIndex + 2;
+ triangles[triIndex+4] = vertexIndex + 1;
+ triangles[triIndex+5] = vertexIndex + 3;
+ }
+ // Apply to mesh
+ tree.mesh->SetIndicesComplex (GetData(triangles), triangles.size(), 0, kPrimitiveTriangles, Mesh::k16BitIndices | Mesh::kDontSupportSubMeshVertexRanges);
+ }
+}
+
+
+void TreeRenderer::RenderRecurse(TreeBinaryTree* binTree, const Plane* planes, std::vector<int>& closeupBillboards, const Vector3f& cameraPos)
+{
+ // if we have exactly zero trees, we get null nodes a bit down - not sure why...
+ if (!binTree)
+ return;
+
+ float sqrDistance = CalculateSqrDistance(cameraPos, binTree->bounds);
+ float sqr2DDistance = Calculate2DSqrDistance(binTree->bounds, cameraPos);
+ if (sqr2DDistance > m_SqrBillboardTreeDistance)
+ return;
+
+ if (!IntersectAABBFrustumFull (binTree->bounds, planes))
+ {
+#if EMIT_ALL_TREE_RENDERERS
+ // we need to queue them as renderers since they might be visible in some shadow map
+ m_FullTrees.insert(m_FullTrees.end(), binTree->instances.begin(), binTree->instances.end());
+#endif
+ return;
+ }
+
+ // Recurse children
+ if (binTree->instances.empty())
+ {
+ if (binTree->splittingPlane.GetSide(cameraPos))
+ {
+ RenderRecurse(binTree->right.get(), planes, closeupBillboards, cameraPos);
+ RenderRecurse(binTree->left.get(), planes, closeupBillboards, cameraPos);
+ }
+ else
+ {
+ RenderRecurse(binTree->left.get(), planes, closeupBillboards, cameraPos);
+ RenderRecurse(binTree->right.get(), planes, closeupBillboards, cameraPos);
+ }
+ }
+ // Render leaf node
+ else
+ {
+ // Render the trees as one big batch
+ if (sqr2DDistance > m_SqrCrossFadeEndDistance)
+ {
+ binTree->targetSortIndex = SortUtility::CalculateTargetSortIndex(binTree->bounds, cameraPos);
+ RenderBatch (*binTree, sqrDistance);
+
+ if (binTree->targetSortIndex != binTree->sortIndex)
+ {
+ binTree->sortIndex = binTree->targetSortIndex;
+ SortBatch(*binTree, binTree->sortIndex);
+ }
+ }
+ // Render the trees individually
+ else
+ {
+ for (std::vector<int>::iterator it = binTree->instances.begin(), end = binTree->instances.end(); it != end; ++it)
+ {
+ TreeInstance& instance = m_Database->GetInstances()[*it];
+ Vector3f position = GetPosition(instance);
+ Vector3f scale(instance.widthScale, instance.heightScale, instance.widthScale);
+ AABB treeBounds = m_Database->GetPrototypes()[instance.index].bounds;
+ treeBounds.SetCenterAndExtent(Scale(treeBounds.GetCenter(), scale) + position, Scale(treeBounds.GetExtent(), scale));
+
+#if EMIT_ALL_TREE_RENDERERS
+ float indivdual2DSqrDistance = instance.temporaryDistance;
+ if (indivdual2DSqrDistance < m_SqrBillboardTreeDistance)
+ {
+ if (indivdual2DSqrDistance > m_SqrCrossFadeEndDistance
+ && IntersectAABBFrustumFull (treeBounds, planes))
+ {
+ closeupBillboards.push_back(*it);
+ }
+ else
+ {
+ m_FullTrees.push_back(*it);
+ }
+ }
+#else
+ if (!IntersectAABBFrustumFull (treeBounds, planes))
+ continue;
+
+ Vector3f offset = position - cameraPos;
+ offset.y = 0.0F;
+ float indivdual2DSqrDistance = SqrMagnitude(offset);
+ instance.temporaryDistance = indivdual2DSqrDistance;
+
+ // Tree is close, render it as a mesh
+ if (indivdual2DSqrDistance < m_SqrCrossFadeEndDistance)
+ {
+ m_FullTrees.push_back(*it);
+ }
+ // Tree is still too far away, so render it as a billboard
+ else if (indivdual2DSqrDistance < m_SqrBillboardTreeDistance)
+ {
+ closeupBillboards.push_back(*it);
+ }
+#endif
+ }
+ }
+ }
+}
+
+void TreeRenderer::CleanupBillboardMeshes ()
+{
+ for (std::vector<TreeBinaryTree*>::iterator it = m_RenderedBatches.begin(), end = m_RenderedBatches.end(); it != end; ++it)
+ {
+ TreeBinaryTree& binTree = **it;
+ if (binTree.visible != 0)
+ {
+ DestroySingleObject(binTree.mesh);
+ binTree.mesh = 0;
+ binTree.visible = 0;
+ }
+ }
+
+ m_RenderedBatches.clear();
+
+ DestroySingleObject(m_CloseBillboardMesh);
+ m_CloseBillboardMesh = 0;
+}
+
+void TreeRenderer::RenderBatch (TreeBinaryTree& binTree, float sqrDistance)
+{
+ if (binTree.visible == 0)
+ {
+ DestroySingleObject(binTree.mesh);
+ binTree.mesh = NULL;
+
+ binTree.mesh = CreateObjectFromCode<Mesh>(kInstantiateOrCreateFromCodeAwakeFromLoad);
+
+ binTree.mesh->SetHideFlags(Object::kDontSave);
+ binTree.mesh->SetName("tree billboard");
+ GenerateBillboardMesh (*binTree.mesh, binTree.instances, false);
+ binTree.sortIndex = -1;
+ }
+
+ binTree.visible = 1;
+
+ m_RenderedBatches.push_back(&binTree);
+}
+
+void TreeRenderer::UpdateShaderProps(const Camera& cam)
+{
+ const Transform& cameraTransform = cam.GetComponent(Transform);
+ const Vector3f& pos = cameraTransform.GetPosition();
+
+ // we want to get camera orientation from imposter in order to avoid any imprecisions
+ const Matrix4x4f& cameraMatrix = m_ImposterRenderTexture->getCameraOrientation();
+ const Vector3f& right = cameraMatrix.GetAxisX();
+ const Vector3f& up = cameraMatrix.GetAxisY();
+ const Vector3f& front = cameraMatrix.GetAxisZ();
+
+ if (m_ImposterRenderTexture->GetSupported())
+ {
+ float billboardAngleFactor, billboardOffsetFactor;
+ m_ImposterRenderTexture->GetBillboardParams(billboardAngleFactor, billboardOffsetFactor);
+
+ m_PropertyBlock.Clear();
+ m_PropertyBlock.AddPropertyVector(ShaderLab::Property("_TreeBillboardCameraRight"), Vector4f(right.x, right.y, right.z, 0.0F));
+ m_PropertyBlock.AddPropertyVector(ShaderLab::Property("_TreeBillboardCameraUp"), Vector4f(up.x, up.y, up.z, billboardOffsetFactor));
+ m_PropertyBlock.AddPropertyVector(ShaderLab::Property("_TreeBillboardCameraFront"), Vector4f(front.x, front.y, front.z, 0));
+ m_PropertyBlock.AddPropertyVector(ShaderLab::Property("_TreeBillboardCameraPos"), Vector4f(pos.x, pos.y, pos.z, billboardAngleFactor));
+ m_PropertyBlock.AddPropertyVector(ShaderLab::Property("_TreeBillboardDistances"), Vector4f(m_SqrBillboardTreeDistance,0,0,0));
+ }
+}
+
+
+Vector3f TreeRenderer::GetPosition (const TreeInstance& instance) const
+{
+ return Scale(instance.position, m_TerrainSize) + m_Position;
+}
+
+struct TreeBillboardVertex
+{
+ static const unsigned kFormat = VERTEX_FORMAT4(Vertex, TexCoord0, TexCoord1, Color);
+
+ Vector3f p;
+ ColorRGBA32 color;
+ Vector2f uv, size;
+};
+
+void TreeRenderer::GenerateBillboardMesh(Mesh& mesh, const std::vector<int>& instances, bool buildTriangles)
+{
+ const std::vector<TreeInstance>& allInstances = m_Database->GetInstances();
+ int treeCount = instances.size();
+
+ mesh.ResizeVertices (4 * treeCount, TreeBillboardVertex::kFormat);
+ TreeBillboardVertex* pv = (TreeBillboardVertex*)mesh.GetVertexDataPointer ();
+ bool swizzleColors = mesh.GetVertexColorsSwizzled();
+
+ AABB bounds;
+
+ // Generate tree billboards
+ for (int i = 0; i < treeCount; ++i)
+ {
+ const TreeInstance& instance = allInstances[instances[i]];
+ const TreeDatabase::Prototype& prototype = m_Database->GetPrototypes()[instance.index];
+
+ const Vector3f position = Scale(instance.position, m_TerrainSize) + m_Position;
+ ColorRGBAf color = instance.color;
+ color *= instance.lightmapColor;
+
+ const float halfWidth = prototype.treeWidth * instance.widthScale * 0.5F;
+ const float halfHeight = prototype.treeHeight * instance.heightScale * 0.5F;
+ const float centerOffset = prototype.getCenterOffset() * instance.heightScale;
+
+ //const float billboardTop = prototype.treeVisibleHeight * instance.heightScale;
+ //const float billboardBottom = (prototype.treeHeight - prototype.treeVisibleHeight) * instance.heightScale;
+
+ const float billboardHeight = prototype.getBillboardHeight();
+ const float billboardHeightDiff = (billboardHeight - prototype.treeHeight) / 2;
+
+ const float billboardTop = (prototype.treeVisibleHeight + billboardHeightDiff) * instance.heightScale;
+ const float billboardBottom = (prototype.treeHeight - prototype.treeVisibleHeight + billboardHeightDiff) * instance.heightScale;
+
+ const float billboardHalfHeight2 = prototype.getBillboardHeight() * instance.widthScale * 0.5F;
+
+ // We position billboards at the root of the tree and offset billboard top and bottom differently (see billboardTop and billboardBottom)
+ // We blend between two modes (for billboard y axis in camera space):
+ // 1) vertical, which is based on billboardBottom and billboardTop
+ // 2) horizontal, which is based on billboardHalfHeight2 (the same for top and bottom)
+ //
+ // See TerrainBillboardTree in TerrainEngine.cginc for more detailed explanation
+ int index = i * 4;
+ pv[index + 0].p = position;
+ pv[index + 1].p = position;
+ pv[index + 2].p = position;
+ pv[index + 3].p = position;
+
+ const Rectf& area = m_ImposterRenderTexture->GetArea(instance.index);
+
+ // we're packing billboardHalfHeight2 into first uvs, and then recompute uvs.y by using formula uvs.y = uvs.y > 0 ? 1 : 0;
+ pv[index + 0].uv.Set(area.x, -billboardHalfHeight2);
+ pv[index + 1].uv.Set(area.GetRight(), -billboardHalfHeight2);
+ pv[index + 2].uv.Set(area.x, billboardHalfHeight2);
+ pv[index + 3].uv.Set(area.GetRight(), billboardHalfHeight2);
+
+ pv[index + 0].size.Set(-halfWidth, -billboardBottom);
+ pv[index + 1].size.Set( halfWidth, -billboardBottom);
+ pv[index + 2].size.Set(-halfWidth, billboardTop);
+ pv[index + 3].size.Set( halfWidth, billboardTop);
+
+ ColorRGBA32 color32 = (ColorRGBA32)color;
+ if (swizzleColors)
+ color32 = SwizzleColorForPlatform(color32);
+ pv[index + 0].color = pv[index + 1].color = pv[index + 2].color = pv[index + 3].color = color32;
+
+ const Vector3f treeBounds(halfWidth, halfHeight, halfWidth);
+ const Vector3f treeCenter = position + Vector3f(0, centerOffset, 0);
+ if (0 == i)
+ {
+ bounds.SetCenterAndExtent(treeCenter, treeBounds);
+ }
+ else
+ {
+ bounds.Encapsulate(treeCenter + treeBounds);
+ bounds.Encapsulate(treeCenter - treeBounds);
+ }
+ }
+
+ mesh.SetBounds(bounds);
+ mesh.SetChannelsDirty (mesh.GetAvailableChannels (), false);
+
+ if (buildTriangles)
+ {
+ UNITY_TEMP_VECTOR(UInt16) triangles(instances.size() * 6);
+ for (int i = 0; i < instances.size(); ++i)
+ {
+ int triIndex = i * 6;
+ int vertexIndex = i * 4;
+ triangles[triIndex+0] = vertexIndex + 0;
+ triangles[triIndex+1] = vertexIndex + 1;
+ triangles[triIndex+2] = vertexIndex + 2;
+ triangles[triIndex+3] = vertexIndex + 2;
+ triangles[triIndex+4] = vertexIndex + 1;
+ triangles[triIndex+5] = vertexIndex + 3;
+ }
+ // Apply to mesh
+ mesh.SetIndicesComplex (GetData(triangles), triangles.size(), 0, kPrimitiveTriangles, Mesh::k16BitIndices | Mesh::kDontSupportSubMeshVertexRanges);
+ }
+}
+
+void TreeRenderer::CreateTreeRenderer(int instance)
+{
+ const TreeInstance& tree = m_Database->GetInstances()[instance];
+
+ Assert(instance == m_TreeIndexToSceneNode.size());
+ std::pair<int, int>& indexPair = m_TreeIndexToSceneNode.push_back();
+
+ const TreeDatabase::Prototype& prototype = m_Database->GetPrototypes()[tree.index];
+ Mesh* mesh = prototype.mesh;
+ if (mesh == NULL)
+ {
+ indexPair.first = -1;
+ indexPair.second = 0;
+ return;
+ }
+
+ const std::vector<Material*>& materials = prototype.materials;
+
+ Matrix4x4f modelview;
+ modelview.SetTRS(GetPosition(tree), Quaternionf::identity(), Vector3f::one);
+
+ const int materialCount = std::min<int> (mesh->GetSubMeshCount(), materials.size());
+
+ indexPair.first = m_LegacyTreeSceneNodes.size();
+ indexPair.second = materialCount;
+
+ for (int m = 0; m < materialCount; ++m)
+ {
+ AABB aabb = mesh->GetBounds();
+ aabb.GetCenter().y += aabb.GetExtent().y * tree.heightScale - aabb.GetExtent().y;
+ aabb.SetCenterAndExtent (aabb.GetCenter(), Vector3f (aabb.GetExtent().x * tree.widthScale, aabb.GetExtent().y * tree.heightScale, aabb.GetExtent().z * tree.widthScale));
+
+ Assert(TreeMeshIntermediateRenderer::s_Allocator == NULL);
+ TreeMeshIntermediateRenderer::s_Allocator = &m_RendererAllocator;
+ TreeMeshIntermediateRenderer* r = new TreeMeshIntermediateRenderer();
+ TreeMeshIntermediateRenderer::s_Allocator = NULL;
+ r->Initialize(modelview, mesh, aabb, materials[m], 0, false, false, m);
+
+ m_LegacyTreeSceneNodes.push_back(SceneNode());
+ SceneNode& node = m_LegacyTreeSceneNodes.back();
+ node.renderer = r;
+ node.layer = 0;
+
+ r->GetWorldAABB(m_LegacyTreeBoundingBoxes.push_back());
+ }
+}
+
+void TreeRenderer::DeleteTreeRenderers()
+{
+ Assert(TreeMeshIntermediateRenderer::s_Allocator == NULL);
+ TreeMeshIntermediateRenderer::s_Allocator = &m_RendererAllocator;
+ for (int i = 0; i < m_LegacyTreeSceneNodes.size(); ++i)
+ {
+ delete m_LegacyTreeSceneNodes[i].renderer;
+ }
+ TreeMeshIntermediateRenderer::s_Allocator = NULL;
+
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ Assert(m_RendererAllocator.m_Allocated == 0);
+#endif
+ m_RendererAllocator.purge();
+}
+
+#endif // ENABLE_TERRAIN
diff --git a/Runtime/Terrain/TreeRenderer.h b/Runtime/Terrain/TreeRenderer.h
new file mode 100644
index 0000000..21a3b48
--- /dev/null
+++ b/Runtime/Terrain/TreeRenderer.h
@@ -0,0 +1,90 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_TERRAIN
+
+#include "ImposterRenderTexture.h"
+
+#include "Runtime/Shaders/MaterialProperties.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Allocator/LinearAllocator.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Camera/SceneNode.h"
+
+namespace Unity
+{
+ class Material;
+}
+
+class TreeDatabase;
+class Mesh;
+struct TreeBinaryTree;
+class Camera;
+class Light;
+class Plane;
+struct TreeInstance;
+struct ShadowCasterCull;
+class Matrix4x4f;
+
+class TreeRenderer
+{
+public:
+ TreeRenderer(TreeDatabase& database, const Vector3f& position, int lightmapIndex);
+ ~TreeRenderer();
+
+ void InjectTree(const TreeInstance& newTree);
+ void RemoveTrees(const Vector3f& pos, float radius, int prototypeIndex);
+
+ void ReloadTrees();
+ void Render(Camera& camera, const UNITY_VECTOR(kMemRenderer, Light*)& lights, float meshTreeDistance, float billboardTreeDistance, float crossFadeLength, int maximumMeshTrees, int layer);
+ void InvalidateImposter () { m_ImposterRenderTexture->InvalidateAngles(); }
+
+ int GetLightmapIndex() { return m_LightmapIndex; }
+ void SetLightmapIndex(int value) { m_LightmapIndex = value; }
+
+ void CollectTreeRenderers(dynamic_array<SceneNode>& sceneNodes, dynamic_array<AABB>& boundingBoxes);
+
+private:
+ void ReloadTrees(int treesPerBatch);
+ void CleanupBillboardMeshes();
+ void RenderRecurse(TreeBinaryTree* binTree, const Plane* planes, std::vector<int>& closeupBillboards, const Vector3f& cameraPos);
+ void UpdateShaderProps(const Camera& cam);
+ void GenerateBillboardMesh(Mesh& mesh, const std::vector<int>& instances, bool buildTriangles);
+ Vector3f GetPosition(const TreeInstance& instance) const;
+ void RenderBatch(TreeBinaryTree& binTree, float sqrDistance);
+ void CreateTreeRenderer(int instance);
+ void DeleteTreeRenderers();
+
+private:
+ MaterialPropertyBlock m_PropertyBlock;
+ TreeDatabase* m_Database;
+ Material* m_BillboardMaterial;
+ Vector3f m_TerrainSize;
+ Vector3f m_Position;
+ std::auto_ptr<TreeBinaryTree> m_TreeBinaryTree;
+
+ float m_SqrBillboardTreeDistance;
+ float m_SqrMeshTreeDistance;
+ float m_CrossFadeLength;
+ float m_SqrCrossFadeEndDistance;
+ int m_LightmapIndex;
+
+ Mesh* m_CloseBillboardMesh;
+
+ std::vector<int> m_FullTrees;
+ std::vector<TreeBinaryTree*> m_RenderedBatches;
+
+ std::auto_ptr<ImposterRenderTexture> m_ImposterRenderTexture;
+
+ ForwardLinearAllocator m_RendererAllocator;
+
+ // scene nodes and bounding boxes for all trees
+ // let's call them legacy here for easier merging with SpeedTree branch in the future
+ dynamic_array<SceneNode> m_LegacyTreeSceneNodes;
+ dynamic_array<AABB> m_LegacyTreeBoundingBoxes;
+ dynamic_array<std::pair<int, int> > m_TreeIndexToSceneNode; // {first renderer, number of renderers}
+};
+
+#endif // ENABLE_TERRAIN
+
+
diff --git a/Runtime/Terrain/Wind.cpp b/Runtime/Terrain/Wind.cpp
new file mode 100644
index 0000000..3fe6234
--- /dev/null
+++ b/Runtime/Terrain/Wind.cpp
@@ -0,0 +1,149 @@
+#include "UnityPrefix.h"
+#include "Wind.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Geometry/AABB.h"
+#include "Runtime/Math/Vector4.h"
+
+// --------------------------------------------------------------------------
+
+
+WindZone::WindZone (MemLabelId label, ObjectCreationMode mode)
+: Super (label, mode)
+, m_Mode (Directional)
+, m_Radius (20)
+, m_WindMain (1)
+, m_WindTurbulence (1)
+, m_WindPulseMagnitude (0.5f)
+, m_WindPulseFrequency (0.01f)
+, m_Node (this)
+{
+}
+
+
+WindZone::~WindZone ()
+{
+}
+
+
+template<class TransferFunc>
+void WindZone::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER_ENUM(m_Mode);
+
+ transfer.Transfer (m_Radius, "m_Radius");
+
+ transfer.Transfer (m_WindMain, "m_WindMain");
+ transfer.Transfer (m_WindTurbulence, "m_WindTurbulence");
+
+ transfer.Transfer (m_WindPulseMagnitude, "m_WindPulseMagnitude");
+ transfer.Transfer (m_WindPulseFrequency, "m_WindPulseFrequency");
+}
+
+
+Vector4f WindZone::ComputeWindForce (const AABB& bounds, float time) const
+{
+ // use point mid way between center and top center, as leaves are likely to be placed there
+ Vector3f center = bounds.GetCenter ();
+ // TODO : could use GetExtent instead
+ center.y += (bounds.GetMax ().y - bounds.GetMin ().y) * 0.25f;
+
+ float windPhase = time * kPI * m_WindPulseFrequency;
+ float phase = windPhase + center.x * 0.1f + center.z * 0.1f;
+ float pulse = (cos (phase) + cos (phase * 0.375f) + cos (phase * 0.05f)) * 0.333f;
+ pulse = 1.0f + (pulse * m_WindPulseMagnitude);
+
+ const Transform& transform = GetComponent (Transform);
+ const Vector3f position = transform.GetPosition ();
+
+ if (m_Mode == Directional)
+ {
+ // Directional
+ float power = pulse;
+
+ // TODO : could do this when m_direction is set
+ Vector3f delta = transform.TransformDirection (Vector3f::zAxis);
+ delta = Normalize (delta);
+
+ return Vector4f (
+ delta.x * m_WindMain * power,
+ delta.y * m_WindMain * power,
+ delta.z * m_WindMain * power,
+ m_WindTurbulence * power);
+ }
+ else
+ {
+ // Spherical with falloff
+ float power = 1.0f - CalculateSqrDistance (position, bounds) / (m_Radius * m_Radius);
+ if (power > 0.0f)
+ {
+ power *= pulse;
+
+ Vector3f delta = center - position;
+ delta = Normalize (delta);
+ return Vector4f(
+ delta.x * m_WindMain * power,
+ delta.y * m_WindMain * power,
+ delta.z * m_WindMain * power,
+ m_WindTurbulence * power);
+ }
+ }
+
+ return Vector4f(0,0,0,0);
+}
+
+
+
+// --------------------------------------------------------------------------
+
+
+void WindZone::AddToManager ()
+{
+ WindManager::GetInstance ().AddWindZone (m_Node);
+}
+
+
+void WindZone::RemoveFromManager ()
+{
+ m_Node.RemoveFromList();
+}
+
+IMPLEMENT_CLASS(WindZone)
+IMPLEMENT_OBJECT_SERIALIZE(WindZone)
+
+// --------------------------------------------------------------------------
+
+// Empty destructor so it won't be autogenerated all over the place
+WindManager::~WindManager()
+{
+}
+
+// No need to allocate this dynamically, it's a tiny class
+WindManager WindManager::s_WindManager;
+
+WindManager& WindManager::GetInstance ()
+{
+ return s_WindManager;
+}
+
+WindManager::WindZoneList& WindManager::GetList ()
+{
+ return m_WindZones;
+}
+
+
+Vector4f WindManager::ComputeWindForce (const AABB& bounds)
+{
+ float time = GetTimeManager ().GetTimeSinceLevelLoad ();
+
+ Vector4f force (0, 0, 0, 0);
+ for (WindZoneList::iterator it = m_WindZones.begin (); it != m_WindZones.end (); ++it)
+ {
+ const WindZone& zone = **it;
+ force = force + zone.ComputeWindForce (bounds, time);
+ }
+ return force;
+}
diff --git a/Runtime/Terrain/Wind.h b/Runtime/Terrain/Wind.h
new file mode 100644
index 0000000..7ed310d
--- /dev/null
+++ b/Runtime/Terrain/Wind.h
@@ -0,0 +1,103 @@
+#ifndef RUNTIME_TERRAIN_WIND_H
+#define RUNTIME_TERRAIN_WIND_H
+
+#include "Runtime/GameCode/Behaviour.h"
+class Vector4f;
+class Vector3f;
+class AABB;
+
+
+
+// --------------------------------------------------------------------------
+
+
+
+
+class WindZone : public Behaviour
+{
+public:
+ REGISTER_DERIVED_CLASS (WindZone, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (WindZone)
+
+ enum WindZoneMode
+ {
+ Directional, // Wind has a direction along the z-axis of the transform
+ Spherical // Wind comes from the transform.position and affects in the direction towards the tree
+ };
+
+public:
+
+
+ WindZone (MemLabelId label, ObjectCreationMode mode);
+ // ~WindZone(); declared by a macro
+
+ // Directional / Spherical. Radius is only used in Spherical mode
+ inline WindZoneMode GetMode () const { return m_Mode; }
+ inline void SetMode (WindZoneMode value) { m_Mode = value; SetDirty(); }
+
+ inline float GetRadius () const { return m_Radius; }
+ inline void SetRadius (float value) { m_Radius = value; SetDirty();}
+
+
+ // Parameters affecting the wind speed, strength and frequency
+ inline float GetWindMain () const { return m_WindMain; }
+ inline float GetWindTurbulence () const { return m_WindTurbulence; }
+ inline float GetWindPulseMagnitude () const { return m_WindPulseMagnitude; }
+ inline float GetWindPulseFrequency () const { return m_WindPulseFrequency; }
+
+ inline void SetWindMain (float value) { m_WindMain = value; SetDirty(); }
+ inline void SetWindTurbulence (float value) { m_WindTurbulence = value; SetDirty();}
+ inline void SetWindPulseMagnitude (float value) { m_WindPulseMagnitude = value; SetDirty(); }
+ inline void SetWindPulseFrequency (float value) { m_WindPulseFrequency = value; SetDirty(); }
+
+ Vector4f ComputeWindForce (const AABB& bounds, float time) const;
+
+protected:
+ virtual void AddToManager();
+ virtual void RemoveFromManager();
+
+private:
+ // Settings
+ WindZoneMode m_Mode; ///< enum { Directional = 0, Spherical = 1 }
+
+ float m_Radius;
+
+ float m_WindMain;
+ float m_WindTurbulence;
+
+ float m_WindPulseMagnitude;
+ float m_WindPulseFrequency;
+
+ // Node registered with the Wind Manager
+ ListNode<WindZone> m_Node;
+};
+
+
+// --------------------------------------------------------------------------
+
+
+class WindManager
+{
+public:
+ ~WindManager();
+
+ static WindManager& GetInstance();
+
+ void AddWindZone(ListNode<WindZone>& node) { m_WindZones.push_back (node); }
+
+ Vector4f ComputeWindForce(const AABB& bounds);
+
+ typedef List< ListNode<WindZone> > WindZoneList;
+ WindZoneList& GetList ();
+
+private:
+ static WindManager s_WindManager;
+
+ WindZoneList m_WindZones;
+};
+
+
+// --------------------------------------------------------------------------
+
+
+#endif
diff --git a/Runtime/Testing/ConsoleTestReporter.cpp b/Runtime/Testing/ConsoleTestReporter.cpp
new file mode 100644
index 0000000..0039448
--- /dev/null
+++ b/Runtime/Testing/ConsoleTestReporter.cpp
@@ -0,0 +1,216 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "ConsoleTestReporter.h"
+#include <algorithm>
+#include <stdio.h>
+
+#ifdef WIN32
+#define snprintf _snprintf
+#endif
+
+using namespace UnitTest;
+using namespace std;
+
+static ConsoleTestReporter* g_Instance;
+
+static bool UnitTestLogEntryHandler (LogType type, const char* log, va_list args)
+{
+ Assert (g_Instance != NULL);
+
+ // Expand message string.
+ char buffer[4096];
+ vsnprintf (buffer, sizeof (buffer), log, args);
+ va_end (args);
+
+ // If we're not currently running a test, just dump the message straight to
+ // stdout. This path is used for printing the test start/finish/fail messages.
+ if (!g_Instance->IsCurrentlyRunningTest ())
+ {
+ fputs (buffer, stdout);
+ fflush (stdout);
+
+ #if UNITY_WIN
+ OutputDebugString (buffer);
+ #endif
+ }
+ else
+ {
+ // Otherwise, feed the message into the reporter.
+ // Ignore debug log messages.
+ if (type != LogType_Debug)
+ g_Instance->ReportMessage (type, buffer);
+ }
+
+ // Disable all normal logging.
+ return false;
+}
+
+ConsoleTestReporter::ConsoleTestReporter ()
+ : m_ShowOnlySummary (false)
+ , m_TestNameColumnIndex (100)
+ , m_ResultColumnIndex (256)
+ , m_IsCurrentlyRunningTest (false)
+ , m_CurrentTestIsFailure (false)
+{
+ Assert (g_Instance == NULL);
+ g_Instance = this;
+
+ // Install our log override.
+ SetLogEntryHandler (UnitTestLogEntryHandler);
+}
+
+ConsoleTestReporter::~ConsoleTestReporter ()
+{
+ SetLogEntryHandler (NULL);
+ g_Instance = NULL;
+}
+
+ConsoleTestReporter* ConsoleTestReporter::GetInstance ()
+{
+ return g_Instance;
+}
+
+void ConsoleTestReporter::ExpectLogMessage (LogType type, const char* logFragment)
+{
+ Assert (type != LogType_Debug);
+
+ if (!IsCurrentlyRunningTest ())
+ return;
+
+ m_ExpectedLogMessagesForCurrentTest.push_back (LogMessage (type, logFragment));
+}
+
+void ConsoleTestReporter::ReportTestStart (TestDetails const& test)
+{
+ if (!m_ShowOnlySummary)
+ {
+ Assert (m_ResultColumnIndex > m_TestNameColumnIndex);
+
+ char buffer[1024];
+ const int suiteNameLength = strlen (test.suiteName);
+ Assert (suiteNameLength < sizeof(buffer) - 4);
+
+ // Create '[TestSuite] ' string padded out with blanks up to the column
+ // for the test name.
+ memset (buffer, ' ', sizeof (buffer));
+ buffer[0] = '[';
+ memcpy (&buffer[1], test.suiteName, suiteNameLength);
+ buffer[suiteNameLength + 1] = ']';
+ buffer[std::min<int> (m_TestNameColumnIndex, sizeof (buffer))] = '\0';
+
+ // Print '[TestSuite] TestName'.
+ printf_console ("%s%s", buffer, test.testName);
+
+ // Print blanks to pad out to result column.
+ const int numSpacesToPad = m_ResultColumnIndex - m_TestNameColumnIndex - strlen (test.testName);
+ memset (buffer, ' ', sizeof (buffer));
+ buffer[std::min<int> (numSpacesToPad, sizeof (buffer))] = '\0';
+ printf_console (buffer);
+ }
+
+ m_CurrentTest = test;
+ m_IsCurrentlyRunningTest = true;
+ m_CurrentTestIsFailure = false;
+}
+
+void ConsoleTestReporter::ReportFailure (TestDetails const& test, char const* failure)
+{
+ // Memorize failure.
+ Failure details;
+ details.fileName = test.filename;
+ details.lineNumber = test.lineNumber;
+ details.text = failure;
+ m_CurrentTestFailures.push_back (details);
+
+ MarkCurrentTestAsFailure ();
+}
+
+void ConsoleTestReporter::ReportMessage (LogType type, string message)
+{
+ // Check whether we have expected this message to come in.
+ for (size_t i = 0; i < m_ExpectedLogMessagesForCurrentTest.size (); ++i)
+ {
+ // Skip if type doesn't match.
+ if (m_ExpectedLogMessagesForCurrentTest[i].first != type)
+ continue;
+
+ // Check whether the expected fragment is found in the current message.
+ if (message.find (m_ExpectedLogMessagesForCurrentTest[i].second) != string::npos)
+ {
+ // Remove it. We only accept one occurrence.
+ m_ExpectedLogMessagesForCurrentTest.erase (m_ExpectedLogMessagesForCurrentTest.begin () + i);
+
+ // Yes, so all ok.
+ return;
+ }
+ }
+
+ // Not an expected message. Record.
+ m_UnexpectedLogMessagesForCurrentTest.push_back (LogMessage (type, message));
+
+ MarkCurrentTestAsFailure ();
+}
+
+void ConsoleTestReporter::ReportTestFinish (TestDetails const& test, float secondsElapsed)
+{
+ m_IsCurrentlyRunningTest = false;
+
+ // If we are still expecting messages, fail the test.
+ if (!m_ExpectedLogMessagesForCurrentTest.empty ())
+ MarkCurrentTestAsFailure ();
+
+ if (!m_ShowOnlySummary)
+ {
+ // Print status.
+ if (m_CurrentTestIsFailure)
+ printf_console ("FAIL!!!!\n");
+ else
+ printf_console ("PASS (%ims)\n", (int) (secondsElapsed * 1000.f));
+
+ // Print failures.
+ for (size_t i = 0; i < m_CurrentTestFailures.size (); ++i)
+ {
+ const Failure& failure = m_CurrentTestFailures[i];
+ printf_console ("\tCHECK FAILURE: %s\n\t\t(%s:%i)\n",
+ failure.text.c_str (),
+ failure.fileName.c_str (),
+ failure.lineNumber);
+ }
+
+ // Print unexpected messages.
+ for (size_t i = 0; i < m_UnexpectedLogMessagesForCurrentTest.size (); ++i)
+ printf_console ("\tUNEXPECTED %s: %s\n",
+ LogTypeToString (m_UnexpectedLogMessagesForCurrentTest[i].first),
+ m_UnexpectedLogMessagesForCurrentTest[i].second.c_str ());
+
+ // Print expected messages that didn't show.
+ for (size_t i = 0; i < m_ExpectedLogMessagesForCurrentTest.size (); ++i)
+ printf_console ("\tEXPECTED %s: %s\n",
+ LogTypeToString (m_ExpectedLogMessagesForCurrentTest[i].first),
+ m_ExpectedLogMessagesForCurrentTest[i].second.c_str ());
+ }
+
+ // Clear state of current test.
+ m_CurrentTestFailures.clear ();
+ m_UnexpectedLogMessagesForCurrentTest.clear ();
+ m_ExpectedLogMessagesForCurrentTest.clear ();
+ m_CurrentTest = TestDetails ();
+}
+
+void ConsoleTestReporter::ReportSummary (int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed)
+{
+ // Print counters (rely on our fail test counter since we also count unexpected messages
+ // as failures).
+ printf_console ("Ran %i tests with %i failures in %.2f seconds\n", totalTestCount, m_FailedTests.size (), secondsElapsed);
+
+ // Print failures.
+ for (int i = 0; i < m_FailedTests.size (); ++i)
+ {
+ const TestDetails& test = m_FailedTests[i];
+ printf_console ("\tFAILED: %s [%s]\n", test.testName, test.suiteName);
+ }
+}
+
+#endif
diff --git a/Runtime/Testing/ConsoleTestReporter.h b/Runtime/Testing/ConsoleTestReporter.h
new file mode 100644
index 0000000..37f296b
--- /dev/null
+++ b/Runtime/Testing/ConsoleTestReporter.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include "External/UnitTest++/src/TestReporter.h"
+#include "External/UnitTest++/src/TestDetails.h"
+
+
+/// Unit test reporter that logs to console output.
+class ConsoleTestReporter : public UnitTest::TestReporter
+{
+public:
+
+ ConsoleTestReporter ();
+ virtual ~ConsoleTestReporter ();
+
+ virtual void ReportTestStart (UnitTest::TestDetails const& test);
+ virtual void ReportFailure (UnitTest::TestDetails const& test, char const* failure);
+ virtual void ReportTestFinish (UnitTest::TestDetails const& test, float secondsElapsed);
+ virtual void ReportSummary (int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed);
+ void ReportMessage (LogType type, std::string message);
+
+ void ExpectLogMessage (LogType type, const char* logFragment);
+
+ void SetShowOnlySummary (bool value) { m_ShowOnlySummary = value; }
+ void SetTestNameColumnIndex (int value) { m_TestNameColumnIndex = value; }
+ void SetResultColumnIndex (int value) { m_ResultColumnIndex = value; }
+
+ bool IsCurrentlyRunningTest () const { return m_IsCurrentlyRunningTest; }
+
+ static ConsoleTestReporter* GetInstance();
+
+private:
+
+ bool m_IsCurrentlyRunningTest;
+ bool m_ShowOnlySummary;
+ int m_TestNameColumnIndex;
+ int m_ResultColumnIndex;
+
+ struct Failure
+ {
+ std::string text;
+ std::string fileName;
+ int lineNumber;
+ };
+
+ typedef std::pair<LogType, std::string> LogMessage;
+
+ std::vector<UnitTest::TestDetails> m_FailedTests;
+
+ bool m_CurrentTestIsFailure;
+ UnitTest::TestDetails m_CurrentTest;
+ std::vector<Failure> m_CurrentTestFailures;
+ std::vector<LogMessage> m_UnexpectedLogMessagesForCurrentTest;
+ std::vector<LogMessage> m_ExpectedLogMessagesForCurrentTest;
+
+ void MarkCurrentTestAsFailure ()
+ {
+ if (m_CurrentTestIsFailure)
+ return;
+
+ m_CurrentTestIsFailure = true;
+ m_FailedTests.push_back (m_CurrentTest);
+ }
+};
diff --git a/Runtime/Testing/HighLevelTest.cpp b/Runtime/Testing/HighLevelTest.cpp
new file mode 100644
index 0000000..dc4018d
--- /dev/null
+++ b/Runtime/Testing/HighLevelTest.cpp
@@ -0,0 +1,537 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "HighLevelTest.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/DumpSerializedDataToText.h"
+#include "Runtime/Serialize/TypeTree.h"
+#include "Runtime/BaseClasses/SupportedMessageOptimization.h"
+#include "Runtime/Allocator/MemoryManager.h"
+
+#if !UNITY_XENON && !UNITY_ANDROID
+#include "PlatformDependent/CommonWebPlugin/Verification.h"
+#endif
+#include "Runtime/Serialize/TransferUtility.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Graphics/Texture2D.h"
+
+#include "Runtime/Misc/BuildSettings.h"
+#if UNITY_EDITOR
+#include "Editor/Platform/Interface/EditorUtility.h"
+#include "Editor/Src/Prefabs/PrefabMergingTest.h"
+#include "Editor/Src/Prefabs/PropertyModification.h"
+#endif
+
+void* RunObjectResetTests (void* userData);
+void RunAllObjectResetTests();
+void TestVersions ();
+void TestLaunchingALotOfTasks ();
+void TestSerializedPropertyPath ();
+void TestEditorComponentUtility();
+void TestAssetImporterCallsPostTransfer ();
+
+void RunHighLevelTest ()
+{
+ #if !UNITY_XENON && !UNITY_ANDROID && !UNITY_PEPPER
+ ErrorIf(!VerifySignature("a", "98ad49733651a83f333d7b91a2f76f77510bfaf89316e34d0025b5b38526c8f8d269e47be24044b9f0dd8675fc781b21fc425da801e33702f9744462c488b400ce9af75b8ae3ec02e6915ce980adcc700fd9d5b2633812ac168d2dea24906bb1cdb3de2bffa4ddeeeb9bc5966b0768e7bc20a8320bf6a0a48cf57b26e31f98b0"));
+ ErrorIf(!VerifySignature("abc", "5ac52e1f8f7e331cf1b1c5bef6254cfbc81c4b48296ae4b4e8a8c536de61c9cade1c77c45b047ce880b8f74829f52860c87b2ecf82a96803059e9189c879a63770a071483ab9d7b688cadcf3aa2a857849f86ff3fdecd725cf34e99f5fc5e4496ca9b810c841dc3a5527c7b3bc866ced1daf120ade08614ec6273d293c293880"));
+ ErrorIf(!VerifySignature("abcd", "584e7bf6042e8a93585bd9a1c08115705f59e0e7e07656a09b16a526b4b76cbc92f5a7246fdd706031f4186361d8cf87ae2062ab877ea9a2576c59be8a9a3139c374345b61a56046376e174ec510cc0d9be46176f8fb0096aeca259b8e754abd424dd3ef50f3473d552fcba6c946bff1908e70cea57584c9002c4bc245e15e35"));
+
+ ErrorIf(!VerifySignature("http://webplayer.unity3d.com/", "99874573f0651e46ac2c9b0f872a7ecf1ee9c101195bf2d06cd6cd00508b412aa58db8b61fa898ecc0f5e37b7efd6c2762081eb5aa9810e6001c9e5a7d32d9b1fdd6ce11d7cd666a997b2c443f123173dac380ca9e06d9ac7aef7c3794d4f3dfc2b62eaf0684cb5a8ba762700b1ae74ef35ce768312c483020455936ed47bf46"));
+ ErrorIf(!VerifySignature("http://wp-fusionfall.unity3d.com/", "606558a42652f6d31130505aa3a9a24dfbd9801b15f79181fac171e0807064799313a6f4369883c276361efb757fd6e979e607be4e981e6ff36308078c13b7bdf59a1f4f06a8f4a5bf78848fb8f4597e2028e9fcd43882a1dcdcf2c1f3756ebf2afab65492e117d53894b81e19435ba8c913a89d07c64c1095b1abe7fa632c15"));
+ ErrorIf(!VerifySignature("http://wp-jumpstart.unity3d.com/", "9f5dce353ddccf0adca627c070135fdc8de70925b366ee661d704c4e90c33fa760a452cc741862ca2e87bc722a2cbf1b0948b2116132f88e45a11e6461cc70190db80f97268a55e0cc96d062272d9eb22d5041e9152d1cc980a4448de1282bdf4dbfe27c558ec0f922b1277fcad57b607364d724d0658500a2b5c2dd0245b050"));
+ #endif
+
+ #if !UNITY_PEPPER
+ //Some access the default resources as part of their reset procedure, which are not loaded at this stage in NaCl yet.
+ //So we skip this test in Pepper.
+ RunAllObjectResetTests ();
+ #endif
+
+ TestVersions();
+
+ void TestHighLevelShaderNameRegistryTests();
+ TestHighLevelShaderNameRegistryTests();
+
+
+ #if UNITY_EDITOR
+
+ TestPropertyModification();
+ TestPrefabModification();
+
+ // TestLaunchingALotOfTasks ();
+
+ TestEditorComponentUtility();
+
+ TestAssetImporterCallsPostTransfer();
+
+ #if UNITY_WIN
+ void TestGetActualPathWindows();
+ TestGetActualPathWindows();
+ #endif // #if UNITY_WIN
+
+ #endif // #if UNITY_EDITOR
+}
+
+#if UNITY_EDITOR
+/// @TODO: Cant do this on every launch of unity, integrate into properl C++ unit test suite
+void TestLaunchingALotOfTasks ()
+{
+ #if UNITY_OSX
+ for (int i=0;i<400;i++)
+ {
+ if (!LaunchTask ("/bin/ls", NULL, NULL))
+ LogString("Fail");
+ }
+
+ for (int i=0;i<400;i++)
+ {
+ string output;
+ if (!LaunchTask ("/bin/ls", &output, NULL) && !output.empty())
+ LogString("Fail");
+ }
+ #endif
+}
+#endif
+
+
+void TestVersions ()
+{
+ int current = 0;
+ int previous = 0;
+
+ current = GetNumericVersion("2.6.0b1"); Assert(current > previous); previous = current;
+ current = GetNumericVersion("2.6.0b7"); Assert(current > previous); previous = current;
+ current = GetNumericVersion("2.6.0b9"); Assert(current > previous); previous = current;
+ current = GetNumericVersion("2.6.0"); Assert(current > previous); previous = current;
+ current = GetNumericVersion("2.6.0f1"); Assert(current > previous); previous = current;
+ current = GetNumericVersion("2.6.0f4"); Assert(current > previous); previous = current;
+ current = GetNumericVersion("2.6.0f8"); Assert(current > previous); previous = current;
+ current = GetNumericVersion("2.6.0f9"); Assert(current > previous); previous = current;
+ current = GetNumericVersion("2.6.0f14"); Assert(current > previous); previous = current;
+ current = GetNumericVersion("2.6.1b1"); Assert(current > previous); previous = current;
+ current = GetNumericVersion("2.6.1b10"); Assert(current > previous); previous = current;
+ current = GetNumericVersion("2.6.1"); Assert(current > previous); previous = current;
+ current = GetNumericVersion("2.6.1f1"); Assert(current > previous); previous = current;
+ current = GetNumericVersion("2.6.1f15"); Assert(current > previous); previous = current;
+ current = GetNumericVersion("2.6.1f16"); Assert(current > previous); previous = current;
+ current = GetNumericVersion("2.6.2b1"); Assert(current > previous); previous = current;
+}
+
+// If tests fail because they access the GameObject or other components in Awake, DO NOT
+// disable them here. Instead fix Awake not to do that in if IsActive() is not true.
+// That will also fix crashes for importing broken prefabs, and is cleaner in general.
+const int kDisabledAwakeFromLoad[] = {
+0
+, 89 // Cubemap (Error using invalid texture)
+, 93 // TextureRect (Error using invalid texture)
+, 117 // Texture3D (Error using invalid texture)
+, 117 // Texture3D (Error using invalid texture)
+, 28 // Texture2D (Error using invalid texture)
+, 48 // Shader (Gives error parsing invalid shader)
+, 152 // MovieTexture gives error on awake from load because movie data is not valid
+, 156 // TerrainData gives error loading because of invalid heightmap
+
+};
+
+#define kDisabledAwakeFromLoadThreaded kDisabledAwakeFromLoad
+
+#if ENABLE_MEMORY_MANAGER
+#define ADD_CUSTOM_ALLOCATOR(Allocator) GetMemoryManager().AddCustomAllocator(Allocator)
+#define REMOVE_CUSTOM_ALLOCATOR(Allocator) GetMemoryManager().RemoveCustomAllocator(Allocator)
+#else
+#define ADD_CUSTOM_ALLOCATOR(Allocator) kMemDefault
+#define REMOVE_CUSTOM_ALLOCATOR(Allocator) {}
+#endif
+
+class UsePreallocatedMemory : public BaseAllocator
+ {
+ public:
+ UInt8 uninitializedValue;
+
+ UsePreallocatedMemory (int size, UInt8 value, const char* name) : BaseAllocator(name)
+ {
+ baseMemory = memory = (unsigned char*) MemoryManager::LowLevelAllocate(size);
+ total = 0;
+ totalSize = size;
+ uninitializedValue = value;
+ for (int i=0;i<size;i++)
+ baseMemory[i] = uninitializedValue;
+ id = ADD_CUSTOM_ALLOCATOR(this).label;
+ }
+
+ virtual ~UsePreallocatedMemory () {
+ MemoryManager::LowLevelFree(baseMemory);
+ REMOVE_CUSTOM_ALLOCATOR(this);
+ }
+
+ virtual void* Allocate (size_t size, int align)
+ {
+ Assert(*memory == uninitializedValue);
+
+ memory = (unsigned char*)AlignPtr(memory + sizeof(SInt32), align);
+ *reinterpret_cast<SInt32*> (memory - sizeof(SInt32)) = size;
+ memory += size;
+ total += size;
+
+ Assert(baseMemory+totalSize > memory);
+
+ return memory - size;
+ }
+
+ virtual void Deallocate (void* p) { total -= *(reinterpret_cast<SInt32*> (p) - 1); }
+ virtual bool Contains (const void* p) { return p > baseMemory && p <= memory ; }
+
+ virtual size_t GetPtrSize(const void* ptr) const {return *(reinterpret_cast<const SInt32*> (ptr) - 1); }
+
+ unsigned char* memory;
+ unsigned char* baseMemory;
+ int total;
+ int totalSize;
+ MemLabelIdentifier id;
+ };
+
+enum { kMaxGeneratedObjectCount = 5000 };
+
+struct Parameters
+{
+ ObjectCreationMode mode;
+ Object* objects[kMaxGeneratedObjectCount];
+ BaseAllocator* allocator[kMaxGeneratedObjectCount];
+};
+
+void SetObjectDirtyDisallowCalling (Object* obj)
+{
+ AssertString("SetDirty should never be called from Awake / Reset / Constructor etc");
+}
+
+static void ClearAllManagers ()
+{
+ for (int i=0;i<ManagerContext::kManagerCount;i++)
+ SetManagerPtrInContext(i, NULL);
+}
+
+static void AssignAllManagers (Object** managers)
+{
+ for (int i=0;i<ManagerContext::kManagerCount;i++)
+ SetManagerPtrInContext(i, managers[i]);
+}
+
+
+void RunAllObjectResetTests ()
+{
+#if UNITY_EDITOR
+ Object::ObjectDirtyCallbackFunction* oldCallBack = Object::GetDirtyCallback ();
+
+////@TODO: Enable this some day. Creating an object, especially when loaded from disk should not mark it dirty.
+// Object::RegisterDirtyCallback (SetObjectDirtyDisallowCalling);
+
+
+ Object* managers[ManagerContext::kManagerCount];
+ memcpy(managers, GetManagerContext().m_Managers, sizeof(managers));
+
+ Parameters paramMainThread;
+ memset(&paramMainThread, 0, sizeof(paramMainThread));
+ paramMainThread.mode = kCreateObjectDefault;
+ RunObjectResetTests (&paramMainThread);
+
+ ClearAllManagers();
+
+ Parameters paramResetThread;
+ memset(&paramResetThread, 0, sizeof(paramResetThread));
+ paramResetThread.mode = kCreateObjectFromNonMainThread;
+ Thread resetThread;
+ resetThread.Run(RunObjectResetTests, &paramResetThread);
+ resetThread.WaitForExit();
+
+ for (int i=0;i<kMaxGeneratedObjectCount;i++)
+ {
+ if (paramResetThread.objects[i])
+ {
+ Object::RegisterInstanceID(paramResetThread.objects[i]);
+ bool ignoreAwake = false;
+ for (int j=0;j<sizeof(kDisabledAwakeFromLoadThreaded) / sizeof(int);j++)
+ ignoreAwake |= kDisabledAwakeFromLoadThreaded[j] == paramResetThread.objects[i]->GetClassID();
+ if (!ignoreAwake)
+ paramResetThread.objects[i]->AwakeFromLoad(kDidLoadThreaded);
+ }
+
+ // IntegrateLoadedImmediately must be called between AwakeFromLoadThreaded and AwakeFromLoad!
+ Texture2D::IntegrateLoadedImmediately();
+
+ AssignAllManagers(managers);
+
+ DestroyObjectHighLevel(paramResetThread.objects[i]);
+ memset(&paramMainThread, 0, sizeof(paramMainThread));
+ REMOVE_CUSTOM_ALLOCATOR(paramResetThread.allocator[i]);
+ UNITY_DELETE(paramResetThread.allocator[i],kMemDefault);
+ }
+
+ AssignAllManagers(managers);
+
+ Object::RegisterDirtyCallback (oldCallBack);
+#endif
+}
+
+// Compare only every 4 bytes. This is because we can't detect unaligned variables.
+// Unaligned blocks of memory will never be touched, thus will trigger an error.
+void CompareMemoryForAllValuesTouched (UsePreallocatedMemory& lhs, UsePreallocatedMemory& rhs, int klassID, const char* msg)
+{
+ // this is not a valid or complete test. We need custom code in order to test this for each object. turned off for now
+ return;
+ const int kAlignedDataSize = 16;
+ for (int j=0;j<lhs.total;j+=kAlignedDataSize)
+ {
+ bool anythingModified = false;
+ for (int p=0;p<kAlignedDataSize;p++)
+ {
+ if (lhs.baseMemory[j+p] != lhs.uninitializedValue || rhs.baseMemory[j+p] != rhs.uninitializedValue)
+ anythingModified = true;
+ }
+
+ if (!anythingModified)
+ {
+ ErrorString("Class is not completely initialized by constructor + " + string(msg) + " " + Object::ClassIDToString(klassID));
+ return;
+ }
+ }
+}
+
+void DumpData (Object& obj)
+{
+ printf_console("--- Dumping %s - %s\n", obj.GetClassName().c_str(), obj.GetName());
+ TypeTree typeTree;
+ dynamic_array<UInt8> output(kMemTempAlloc);
+ GenerateTypeTree(obj, &typeTree);
+ WriteObjectToVector(obj, &output);
+ DumpSerializedDataToText(typeTree, output);
+}
+
+const static int kBaseVeryHighInstanceID = (std::numeric_limits<SInt32>::max()-1) - 5000 * 4;
+const static int kBaseVeryHighInstanceID2 = (std::numeric_limits<SInt32>::max()-1) - 10000 * 4;
+
+void* RunObjectResetTests (void* userData)
+{
+ Parameters& parameters = *((Parameters*)userData);
+
+ vector<SInt32> klasses;
+ Object::FindAllDerivedClasses(ClassID(Object), &klasses);
+
+ int kMaxSize = 1024 * 64;
+
+ for (int i=0;i<klasses.size();i++)
+ {
+ int klassID = klasses[i];
+
+ if (Object::ClassIDToRTTI(klassID)->isAbstract)
+ continue;
+ // Ignore game manager derived
+ if (Object::IsDerivedFromClassID(klassID, 9))
+ continue;
+
+ if(klassID == ClassID(Transform))
+ continue; // ???
+
+ if(klassID == 1027) // ignore GUISerializer. This is really just a very custom class that sets up some global state
+ continue;
+ if(klassID == 1048) // ignore InspectorExpandedState since it is a singleton class
+ continue;
+ if(klassID == 1049) // ignore AnnotationManager since it is a singleton class
+ continue;
+ if(klassID == 159) // ignore editor settings
+ continue;
+ if(klassID == 162) // ignore editor user settings
+ continue;
+ if(klassID == 115) // ignore MonoScript, it accesses MonoManager from serialization. This is fine but doesn't fit well into the framework.
+ continue;
+ if(klassID == 148) // ignore NetworkView, Registers itself in constructor with networkmanager
+ continue;
+ if(klassID == 1037) // ignore AssetServerCache, singleton
+ continue;
+ if(klassID == 142) // ignore AssetBundle, for it to destroy we need to call UnloadAssetBundle
+ continue;
+ if(klassID == 184 || klassID == 185 || klassID == 186) // ignore SubstanceArchive, ProceduralMaterial, ProceduralTexture
+ continue;
+
+ ////// @TODO: Work in progress, need to be fixed!!!!
+ if (klassID == 156) // Terrain has some issues left
+ continue;
+
+ bool ignoreAwake = false;
+ for (int j=0;j<sizeof(kDisabledAwakeFromLoad) / sizeof(int);j++)
+ ignoreAwake |= kDisabledAwakeFromLoad[j] == klassID;
+
+ /// Run for editor classes , we might be using datatemp
+
+ dynamic_array<UInt8> serialized1(kMemTempAlloc);
+ dynamic_array<UInt8> serialized2(kMemTempAlloc);
+
+ UsePreallocatedMemory* mem1 = UNITY_NEW(UsePreallocatedMemory (kMaxSize, 0, "Mem1"),kMemDefault);
+ MemLabelId mem1Label = ADD_CUSTOM_ALLOCATOR(mem1);
+ Object* obj1 = Object::Produce(klassID, kBaseVeryHighInstanceID + i * 2, mem1Label, parameters.mode);
+
+ obj1->Reset();
+
+ WriteObjectToVector(*obj1, &serialized1);
+
+ UsePreallocatedMemory* mem2 = UNITY_NEW(UsePreallocatedMemory (kMaxSize, 255, "Mem2"),kMemDefault);
+ MemLabelId mem2Label = ADD_CUSTOM_ALLOCATOR(mem2);
+ Object* obj2 = Object::Produce(klassID, kBaseVeryHighInstanceID + 2 + i * 2, mem2Label, parameters.mode);
+
+ obj2->Reset();
+
+ WriteObjectToVector(*obj2, &serialized2);
+
+ if (!ignoreAwake)
+ {
+ if (parameters.mode == kCreateObjectDefault)
+ {
+ obj1->AwakeFromLoad(kDidLoadFromDisk);
+ obj2->AwakeFromLoad(kDidLoadFromDisk);
+ }
+ else if (parameters.mode == kCreateObjectFromNonMainThread)
+ {
+ obj1->AwakeFromLoadThreaded();
+ obj2->AwakeFromLoadThreaded();
+ }
+ }
+ else
+ {
+ obj1->HackSetAwakeWasCalled();
+ obj2->HackSetAwakeWasCalled();
+ }
+
+ if (mem1->total != mem2->total)
+ {
+ ErrorString("Class is different size after initialized by constructor + Reset " + Object::ClassIDToString(klassID));
+ // Dump serialized data to console.log
+ DumpData(*obj1);
+ DumpData(*obj2);
+ }
+
+ if( !serialized1.equals(serialized2) )
+ {
+ ErrorString("Class is serialized different after initialized by constructor + Reset " + Object::ClassIDToString(klassID));
+ // Dump serialized data to console.log
+ DumpData(*obj1);
+ DumpData(*obj2);
+ }
+
+ if (!ignoreAwake && parameters.mode == kCreateObjectDefault)
+ {
+ // Compare only every 4 bytes. This is because we can't detect unaligned variables.
+ // Unaligned blocks of memory will never be touched, thus will trigger an error.
+ CompareMemoryForAllValuesTouched (*mem1, *mem2, klassID, "Reset");
+ }
+
+ UsePreallocatedMemory* mem3 = UNITY_NEW(UsePreallocatedMemory (kMaxSize, 125, "Mem3"),kMemDefault);
+ MemLabelId mem3Label = ADD_CUSTOM_ALLOCATOR(mem3);
+ Object* obj3 = Object::Produce(klassID, kBaseVeryHighInstanceID2 + i * 2, mem3Label, parameters.mode);
+
+ ReadObjectFromVector(obj3, serialized1);
+ obj3->HackSetAwakeWasCalled();
+
+ CompareMemoryForAllValuesTouched (*mem1, *mem3, klassID, "Serialize Read");
+
+ WriteObjectToVector(*obj3, &serialized2);
+ if(!serialized1.equals(serialized2))
+ {
+ ErrorString("Class is serialized different after write and reading back " + Object::ClassIDToString(klassID));
+ // Dump serialized data to console.log
+ DumpData(*obj3);
+ }
+
+ if (parameters.mode == kCreateObjectDefault)
+ {
+ DestroyObjectHighLevel(obj1);
+ DestroyObjectHighLevel(obj2);
+ DestroyObjectHighLevel(obj3);
+
+ AssertIf(mem1->total != 0);
+ AssertIf(mem2->total != 0);
+ AssertIf(mem3->total != 0);
+
+ REMOVE_CUSTOM_ALLOCATOR(mem1);
+ UNITY_DELETE( mem1,kMemDefault);
+ REMOVE_CUSTOM_ALLOCATOR(mem2);
+ UNITY_DELETE( mem2,kMemDefault);
+ REMOVE_CUSTOM_ALLOCATOR(mem3);
+ UNITY_DELETE( mem3,kMemDefault);
+ }
+ else
+ {
+ parameters.objects[i * 3 + 0] = obj1;
+ parameters.objects[i * 3 + 1] = obj2;
+ parameters.objects[i * 3 + 2] = obj3;
+
+ parameters.allocator[i * 3 + 0] = mem1;
+ parameters.allocator[i * 3 + 1] = mem2;
+ parameters.allocator[i * 3 + 2] = mem3;
+ }
+
+ }
+
+ return NULL;
+}
+
+struct TestData
+{
+ PPtr<Texture2D> m_Tex;
+ Texture2D* m_CachedTexture;
+
+ // Property m_ActiveMatrixName; /// HOW TO SUPPORT THIS???
+
+ Vector2f m_Position;
+ Vector2f m_Scale;
+
+ TestData ()
+ {
+ m_CachedTexture = NULL;
+ m_Tex = NULL;
+ m_Position = Vector2f(0,0);
+ m_Scale = Vector2f(1,1);
+ }
+};
+
+#if UNITY_EDITOR
+#include "Editor/Src/AssetPipeline/AssetImporter.h"
+
+void TestAssetImporterCallsPostTransfer ()
+{
+ vector<SInt32> klasses;
+ Object::FindAllDerivedClasses (ClassID (AssetImporter), &klasses);
+ UsePreallocatedMemory* mem1 = new UsePreallocatedMemory (50 * 1024, 0, "Mem1");
+ MemLabelId mem1Label = ADD_CUSTOM_ALLOCATOR(mem1);
+
+ for (int i = 0; i < klasses.size (); i++)
+ {
+ int klassID = klasses[i];
+ AssetImporter* importer = static_cast<AssetImporter*> (Object::Produce (klassID, 0, mem1Label, kCreateObjectDefault));
+ importer->Reset ();
+ importer->HackSetAwakeWasCalled ();
+
+ YAMLWrite write (0);
+ importer->VirtualRedirectTransfer (write);
+ std::string str;
+ write.OutputToString (str);
+
+ size_t offset = str.find ("m_UserData:");
+ if (offset == string::npos)
+ ErrorString (Format ("%s did not call PostTransfer in its Transfer function", importer->GetClassName ().c_str ()).c_str ());
+
+ DestroyObjectHighLevel (importer);
+ }
+
+ REMOVE_CUSTOM_ALLOCATOR(mem1);
+ delete mem1;
+}
+
+#endif
+
+
+
+#endif
diff --git a/Runtime/Testing/HighLevelTest.h b/Runtime/Testing/HighLevelTest.h
new file mode 100644
index 0000000..df33ccd
--- /dev/null
+++ b/Runtime/Testing/HighLevelTest.h
@@ -0,0 +1,6 @@
+#ifndef HIGHLEVELTEST_H
+#define HIGHLEVELTEST_H
+
+void RunHighLevelTest ();
+
+#endif
diff --git a/Runtime/Testing/JobSchedulerTest/Test.sln b/Runtime/Testing/JobSchedulerTest/Test.sln
new file mode 100644
index 0000000..c6f9aa5
--- /dev/null
+++ b/Runtime/Testing/JobSchedulerTest/Test.sln
@@ -0,0 +1,21 @@
+Microsoft Visual Studio Solution File, Format Version 8.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test.vcproj", "{4FB80099-804E-4ABE-9680-31290A6F0D89}"
+ ProjectSection(ProjectDependencies) = postProject
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfiguration) = preSolution
+ Debug = Debug
+ Release = Release
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {4FB80099-804E-4ABE-9680-31290A6F0D89}.Debug.ActiveCfg = Debug|Win32
+ {4FB80099-804E-4ABE-9680-31290A6F0D89}.Debug.Build.0 = Debug|Win32
+ {4FB80099-804E-4ABE-9680-31290A6F0D89}.Release.ActiveCfg = Release|Win32
+ {4FB80099-804E-4ABE-9680-31290A6F0D89}.Release.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
diff --git a/Runtime/Testing/JobSchedulerTest/Test.vcproj b/Runtime/Testing/JobSchedulerTest/Test.vcproj
new file mode 100644
index 0000000..58b742f
--- /dev/null
+++ b/Runtime/Testing/JobSchedulerTest/Test.vcproj
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="windows-1257"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="Test"
+ ProjectGUID="{4FB80099-804E-4ABE-9680-31290A6F0D89}"
+ Keyword="Win32Proj">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="build/debug"
+ IntermediateDirectory="build/debug"
+ ConfigurationType="1"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=".;..;..\..\Utilities;..\..\Threads"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="TRUE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="Test_d.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile="$(OutDir)/Test.pdb"
+ SubSystem="1"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="build/release"
+ IntermediateDirectory="build/release"
+ ConfigurationType="1"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=".;..;..\..\Utilities;..\..\Threads"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ RuntimeLibrary="0"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="3"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="Test.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="TRUE"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="unity"
+ Filter="">
+ <File
+ RelativePath="..\..\Threads\JobScheduler.cpp">
+ </File>
+ <File
+ RelativePath="..\..\Threads\JobScheduler.h">
+ </File>
+ <File
+ RelativePath="..\..\Utilities\Mutex.cpp">
+ </File>
+ <File
+ RelativePath="..\..\Utilities\Mutex.h">
+ </File>
+ <File
+ RelativePath="..\..\Threads\Semaphore.h">
+ </File>
+ <File
+ RelativePath="..\..\Utilities\Thread.cpp">
+ </File>
+ <File
+ RelativePath="..\..\Utilities\Thread.h">
+ </File>
+ <File
+ RelativePath="..\..\Threads\ThreadMessageQueue.cpp">
+ </File>
+ <File
+ RelativePath="..\..\Threads\ThreadMessageQueue.h">
+ </File>
+ <File
+ RelativePath="..\..\Threads\ThreadUtility.h">
+ </File>
+ <File
+ RelativePath="..\..\Utilities\Word.cpp">
+ </File>
+ <File
+ RelativePath="..\..\Utilities\Word.h">
+ </File>
+ </Filter>
+ <File
+ RelativePath=".\main.cpp">
+ </File>
+ <File
+ RelativePath="..\UnityPrefix.h">
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Runtime/Testing/JobSchedulerTest/main.cpp b/Runtime/Testing/JobSchedulerTest/main.cpp
new file mode 100644
index 0000000..8554107
--- /dev/null
+++ b/Runtime/Testing/JobSchedulerTest/main.cpp
@@ -0,0 +1,82 @@
+#include <cstdio>
+#include <cstdlib>
+#include "UnityPrefix.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Threads/JobScheduler.h"
+#include <time.h>
+#include <math.h>
+
+struct WorkData {
+ float input;
+ float output;
+};
+
+void* WorkFunction( void* data )
+{
+ WorkData* d = (WorkData*)data;
+ d->output = 0.0f;
+ for( int i = 0; i < 1000000; ++i ) {
+ d->output += sinf(d->output) + cosf(d->input) - sinf(d->output + d->input * 3.0f);
+ }
+
+ return NULL;
+}
+
+// Windows, Core2Quad 2.40
+// 200 jobs, 100000 iters:
+// Sum=590573.192871
+// 0=1.55s 1=0.80s 2=0.55s 3=0.45s 4=0.45s 5=0.44s 6=0.45s
+// 100 jobs, 1000000 iters:
+// Sum=2992744.398470
+// 0=7.78s 1=3.94s 2=2.66s 3=2.00s 4=2.00s 5=2.00s 6=2.02s
+
+void DoTests()
+{
+ JobScheduler scheduler(3,1);
+
+ JobScheduler::JobGroupID group = scheduler.BeginGroup();
+
+ const int kJobs = 100;
+ WorkData datas[kJobs];
+ for( int i = 0; i < kJobs; ++i )
+ {
+ datas[i].input = i+1;
+ scheduler.SubmitJob( group, WorkFunction, &datas[i], NULL );
+ }
+ scheduler.WaitForGroup(group);
+
+ float sum = 0.0f;
+ for( int i = 0; i < kJobs; ++i )
+ sum += datas[i].output;
+ printf("Sum of results: %f\n", sum);
+}
+
+
+
+int main()
+{
+ #if UNITY_WIN
+ DWORD ttt0 = GetTickCount();
+ #else
+ timeval ttt0;
+ gettimeofday( &ttt0, NULL );
+ #endif
+
+ DoTests();
+
+ #if UNITY_WIN
+ DWORD ttt1 = GetTickCount();
+ float timeTaken = (ttt1-ttt0) * 0.001f;
+ #else
+ timeval ttt1;
+ gettimeofday( &ttt1, NULL );
+ timeval ttt2;
+ timersub( &ttt1, &ttt0, &ttt2 );
+ float timeTaken = ttt2.tv_sec + ttt2.tv_usec * 1.0e-6f;
+ #endif
+
+ printf( "Test time: %.2fs\n", timeTaken );
+
+ return 0;
+}
diff --git a/Runtime/Testing/MathPerformanceTest/MatrixMultiplyTest.cpp b/Runtime/Testing/MathPerformanceTest/MatrixMultiplyTest.cpp
new file mode 100644
index 0000000..8e3c2c7
--- /dev/null
+++ b/Runtime/Testing/MathPerformanceTest/MatrixMultiplyTest.cpp
@@ -0,0 +1,166 @@
+#include "UnityPrefix.h"
+#include "Runtime/Testing/Testing.h"
+
+#if ENABLE_PERFORMANCE_TESTS
+
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Profiler/TimeHelper.h"
+
+extern void MultiplyMatrices4x4REF (const Matrix4x4f* __restrict lhs, const Matrix4x4f* __restrict rhs, Matrix4x4f* __restrict res);
+extern void MultiplyMatrices4x4SSE (const Matrix4x4f* __restrict lhs, const Matrix4x4f* __restrict rhs, Matrix4x4f* __restrict res);
+
+extern void CopyMatrixREF( const float* __restrict lhs, float* __restrict res);
+extern void CopyMatrixSSE( const float* __restrict lhs, float* __restrict res);
+
+#define ITERATIONS_COUNT 10000
+
+
+/*
+ one of the good results ( launched with Shift+F5 in release mode ):
+ ITERATIONS_COUNT: 10000
+ timeElapsedREF: 1393
+ timeElapsedSSE: 639
+ timeElapsedJOE: 647
+ total cycles REF: 1423390
+ total cycles SSE: 650381
+ total cycles JOE: 660177
+ avg cycles REF: 142
+ avg cycles SSE: 65
+ avg cycles JOE: 66
+
+
+ Matrix Copy: Ref vs SSE
+ ITERATIONS_COUNT=10000
+ time elapsedCPY: 2114
+ time elapsedCPYSSE: 964
+ total cycles CPY: 2157582
+ total cycles CPYSSE: 980763
+ avg cycles CPY: 215
+ avg cycles CPYSSE: 98
+ ctrl:20000.000000
+
+*/
+
+
+void TestMultiplyMatrices()
+{
+ Matrix4x4f m0, m1, m2;
+#define RESET_MATS() for(int i=0;i<16;i++) { m0.m_Data[i] = (float)(i+1); m1.m_Data[15-i] = (float)(i+1); }
+
+
+ RESET_MATS();
+
+ ABSOLUTE_TIME startTimeREF = GetStartTime();
+ UInt64 cycStartREF = __rdtsc();
+ for(UInt32 i=0;i<ITERATIONS_COUNT;i++)
+ {
+ MultiplyMatrices4x4REF(&m0, &m1, &m2);
+ m0.m_Data[0]*=-1.f;
+ m1.m_Data[0]*=-1.f;
+ }
+ UInt64 cycEndREF = __rdtsc();
+ ABSOLUTE_TIME elapsedREF = GetElapsedTime(startTimeREF);
+
+ RESET_MATS();
+
+ startTimeREF = GetStartTime();
+ cycStartREF = __rdtsc();
+ for(UInt32 i=0;i<ITERATIONS_COUNT;i++)
+ {
+ MultiplyMatrices4x4REF(&m0, &m1, &m2);
+ m0.m_Data[0]*=-1.f;
+ m1.m_Data[0]*=-1.f;
+ }
+ cycEndREF = __rdtsc();
+ elapsedREF = GetElapsedTime(startTimeREF);
+
+ RESET_MATS();
+
+ ABSOLUTE_TIME startTimeJOE = GetStartTime();
+ UInt64 cycStartJOE = __rdtsc();
+ for(UInt32 i=0;i<ITERATIONS_COUNT;i++)
+ {
+ MultiplyMatrices4x4(&m0, &m1, &m2);
+ m0.m_Data[0]*=-1.f;
+ m1.m_Data[0]*=-1.f;
+ }
+ UInt64 cycEndJOE = __rdtsc();
+ ABSOLUTE_TIME elapsedJOE = GetElapsedTime(startTimeJOE);
+
+ RESET_MATS();
+
+ startTimeJOE = GetStartTime();
+ cycStartJOE = __rdtsc();
+ for(UInt32 i=0;i<ITERATIONS_COUNT;i++)
+ {
+ MultiplyMatrices4x4(&m0, &m1, &m2);
+ m0.m_Data[0]*=-1.f;
+ m1.m_Data[0]*=-1.f;
+ }
+ cycEndJOE = __rdtsc();
+ elapsedJOE = GetElapsedTime(startTimeJOE);
+
+
+ UInt64 avgCycREF = (cycEndREF - cycStartREF) / ITERATIONS_COUNT;
+ UInt64 avgCycJOE = (cycEndJOE - cycStartJOE) / ITERATIONS_COUNT;
+
+#if UNITY_WIN
+ {
+ char szMsg[1024];
+ sprintf(szMsg, "ITERATIONS_COUNT=%d\r\ntime elapsedREF: %I64d\r\ntime elapsedJOE: %I64d\r\ntotal cycles REF: %I64d\r\ntotal cycles JOE: %I64d\r\navg cycles REF: %I64d\r\navg cycles JOE: %I64d\r\nctrl:%f" ,
+ ITERATIONS_COUNT, elapsedREF, elapsedJOE, cycEndREF - cycStartREF, cycEndJOE - cycStartJOE, avgCycREF, avgCycJOE, m0.m_Data[4] + m1.m_Data[5] + m2.m_Data[7]);
+ OutputDebugString(LPCSTR(szMsg));
+ MessageBox(0, szMsg, "REF vs SSE Multiply", MB_ICONINFORMATION);
+ }
+#else
+ printf_console("REF vs SSE Multiply: ITERATIONS_COUNT=%d\r\ntime elapsedREF: %I64d\r\ntime elapsedJOE: %I64d\r\ntotal cycles REF: %I64d\r\ntotal cycles JOE: %I64d\r\navg cycles REF: %I64d\r\navg cycles JOE: %I64d\r\nctrl:%f" ,
+ ITERATIONS_COUNT, elapsedREF, elapsedJOE, cycEndREF - cycStartREF, cycEndJOE - cycStartJOE, avgCycREF, avgCycJOE, m0.m_Data[4] + m1.m_Data[5] + m2.m_Data[7]);
+#endif
+
+ RESET_MATS();
+
+ ABSOLUTE_TIME startTimeCPY = GetStartTime();
+ UInt64 cycStartCPY = __rdtsc();
+ float f=0.f;
+ for(UInt32 i=0;i<ITERATIONS_COUNT;i++)
+ {
+ CopyMatrixREF(m0.GetPtr(), m1.GetPtr());
+ f+=m1.m_Data[0];
+ }
+ UInt64 cycEndCPY = __rdtsc();
+ ABSOLUTE_TIME elapsedCPY = GetElapsedTime(startTimeCPY);
+
+ RESET_MATS();
+
+ ABSOLUTE_TIME startTimeCPYSSE = GetStartTime();
+ UInt64 cycStartCPYSSE = __rdtsc();
+ for(UInt32 i=0;i<ITERATIONS_COUNT;i++)
+ {
+ CopyMatrixSSE(m0.GetPtr(), m1.GetPtr());
+ f+=m1.m_Data[0];
+ }
+ UInt64 cycEndCPYSSE = __rdtsc();
+ ABSOLUTE_TIME elapsedCPYSSE = GetElapsedTime(startTimeCPYSSE);
+
+ UInt64 avgCycCPY = (cycEndCPY - cycStartCPY) / ITERATIONS_COUNT;
+ UInt64 avgCycCPYSSE = (cycEndCPYSSE - cycStartCPYSSE) / ITERATIONS_COUNT;
+
+#if UNITY_WIN
+ {
+ char szMsg[1024];
+ sprintf(szMsg, "ITERATIONS_COUNT=%d\r\ntime elapsedCPY: %I64d\r\ntime elapsedCPYSSE: %I64d\r\ntotal cycles CPY: %I64d\r\ntotal cycles CPYSSE: %I64d\r\navg cycles CPY: %I64d\r\navg cycles CPYSSE: %I64d\r\nctrl:%f" ,
+ ITERATIONS_COUNT, elapsedCPY, elapsedCPYSSE, cycEndCPY - cycStartCPY, cycEndCPYSSE - cycStartCPYSSE, avgCycCPY, avgCycCPYSSE, f);
+ OutputDebugString(LPCSTR(szMsg));
+ MessageBox(0, szMsg, "REF vs SSE copy", MB_ICONINFORMATION);
+ }
+#else
+ printf_console("REF vs SSE copy: ITERATIONS_COUNT=%d\r\ntime elapsedCPY: %I64d\r\ntime elapsedCPYSSE: %I64d\r\ntotal cycles CPY: %I64d\r\ntotal cycles CPYSSE: %I64d\r\navg cycles CPY: %I64d\r\navg cycles CPYSSE: %I64d\r\nctrl:%f" ,
+ ITERATIONS_COUNT, elapsedCPY, elapsedCPYSSE, cycEndCPY - cycStartCPY, cycEndCPYSSE - cycStartCPYSSE, avgCycCPY, avgCycCPYSSE, f);
+#endif
+
+
+
+
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/Testing/TestFixtures.h b/Runtime/Testing/TestFixtures.h
new file mode 100644
index 0000000..94b125f
--- /dev/null
+++ b/Runtime/Testing/TestFixtures.h
@@ -0,0 +1,163 @@
+#pragma once
+
+#include <memory>
+#include <algorithm>
+#include <map>
+
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferUtility.h"
+#include "Runtime/Serialize/WriteTypeToBuffer.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+
+
+/// Test fixture that allows accumulating objects that are cleaned up automatically
+/// when the test finishes.
+class TestFixtureBase
+{
+public:
+
+ ~TestFixtureBase()
+ {
+ std::for_each (m_Objects.begin (), m_Objects.end (), DestroySingleObject);
+ }
+
+ template<typename T>
+ T* AddObjectToCleanup (T* object)
+ {
+ if (object != 0)
+ {
+ m_Objects.push_back (object);
+ }
+ return object;
+ }
+
+ template<typename X>
+ X* NewTestObject ()
+ {
+ X* result = NEW_OBJECT_RESET_AND_AWAKE (X);
+ AddObjectToCleanup (result);
+ return result;
+ }
+
+private:
+ std::vector<Object*> m_Objects;
+};
+
+
+/// Fixture that automatically creates an object of type "T".
+template<typename T>
+class ObjectTestFixture : public TestFixtureBase
+{
+public:
+ ObjectTestFixture ()
+ {
+ m_ObjectUnderTest = NewTestObject<T> ();
+ }
+
+protected:
+
+ T* m_ObjectUnderTest;
+};
+
+
+/// Fixture that simplifies serializing and deserializing an object and provides various
+/// helpers to simplify setting up tests for transfers.
+template<class T>
+struct SerializationTestFixture : public TestFixtureBase, public GenerateIDFunctor
+{
+ T m_ObjectUnderTest;
+ TypeTree m_TypeTree;
+ dynamic_array<UInt8> m_Buffer;
+ int m_TransferOptions;
+
+ SerializationTestFixture ()
+ : m_TransferOptions (0)
+ {
+ }
+
+ void GenerateTypeTree ()
+ {
+ ProxyTransfer proxyTransfer (m_TypeTree, m_TransferOptions, &m_ObjectUnderTest, sizeof (T));
+ proxyTransfer.Transfer (m_ObjectUnderTest, "Base");
+ }
+
+ void WriteObjectToBuffer ()
+ {
+ WriteTypeToVector (m_ObjectUnderTest, &m_Buffer, m_TransferOptions);
+ }
+
+ void DoSafeBinaryTransfer ()
+ {
+ #if SUPPORT_SERIALIZED_TYPETREES
+
+ GenerateTypeTree();
+ WriteObjectToBuffer ();
+
+ SafeBinaryRead m_Transfer;
+ CachedReader& reader = m_Transfer.Init (m_TypeTree, 0, m_Buffer.size (), 0);
+ MemoryCacheReader memoryCache (m_Buffer);
+
+ reader.InitRead (memoryCache, 0, m_Buffer.size ());
+ m_Transfer.Transfer (m_ObjectUnderTest, "Base");
+
+ reader.End ();
+
+ #endif
+ }
+
+ void DoTextTransfer ()
+ {
+ #if SUPPORT_TEXT_SERIALIZATION
+ YAMLWrite write (m_TransferOptions);
+ write.Transfer (m_ObjectUnderTest, "Base");
+
+ string text;
+ write.OutputToString(text);
+
+ YAMLRead read (text.c_str (), text.size (), m_TransferOptions);
+ read.Transfer (m_ObjectUnderTest, "Base");
+ #endif
+ }
+
+ /// @name RemapPPtrTransfer Helpers
+ /// @{
+
+ typedef std::map<SInt32, SInt32> PPtrRemapTable;
+ PPtrRemapTable m_PPtrRemapTable;
+
+ void DoRemapPPtrTransfer (bool readPPtrs = true)
+ {
+ RemapPPtrTransfer transfer (m_TransferOptions, readPPtrs);
+ transfer.SetGenerateIDFunctor (this);
+ transfer.Transfer (m_ObjectUnderTest, "Base");
+ }
+
+ void AddPPtrRemap (SInt32 oldInstanceID, SInt32 newInstanceID)
+ {
+ m_PPtrRemapTable[oldInstanceID] = newInstanceID;
+ }
+
+ virtual SInt32 GenerateInstanceID (SInt32 oldInstanceID, TransferMetaFlags metaFlag)
+ {
+ PPtrRemapTable::const_iterator iter = m_PPtrRemapTable.find (oldInstanceID);
+ if (iter == m_PPtrRemapTable.end ())
+ return oldInstanceID;
+
+ return iter->second;
+ }
+
+ /// @}
+};
+
+
+/// Define a "<Name>Test" class with a transfer function.
+/// @note The class is not derived from Object.
+#define DEFINE_TRANSFER_TEST_FIXTURE(NAME) \
+ struct NAME ## Test \
+ { \
+ DECLARE_SERIALIZE (NAME ## Test) \
+ }; \
+ typedef SerializationTestFixture<NAME ## Test> NAME ## TestFixture; \
+ template<typename TransferFunction> \
+ void NAME ## Test::Transfer (TransferFunction& transfer)
diff --git a/Runtime/Testing/Testing.cpp b/Runtime/Testing/Testing.cpp
new file mode 100644
index 0000000..bc129a3
--- /dev/null
+++ b/Runtime/Testing/Testing.cpp
@@ -0,0 +1,344 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+// Disclaimer: What we do here isn't real unit testing. It is a reasonable compromise where we accept the
+// codebase mostly as is without major refactors and run reasonably small tests that sorta look like unit
+// tests but are really integration tests. The key compromise here is that we create a global execution
+// environment that all the tests share and run within. This environment dictates what tests are allowed
+// to do and what shared functionality they have access to.
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "External/UnitTest++/src/NunitTestReporter.h"
+#include "External/UnitTest++/src/TestReporterStdout.h"
+#include "External/UnitTest++/src/TestList.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Allocator/LinearAllocator.h"
+#include "Runtime/Utilities/Argv.h"
+#if !UNITY_EXTERNAL_TOOL
+#include "Runtime/Serialize/PathNamePersistentManager.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Misc/SaveAndLoadHelper.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/Camera/GraphicsSettings.h"
+#include "Runtime/Dynamics/PhysicsManager.h"
+#include "Runtime/Input/GetInput.h"
+#include "Runtime/BaseClasses/Tags.h"
+#endif
+
+#include "Testing.h"
+#include "ConsoleTestReporter.h"
+
+using namespace UnitTest;
+using namespace std;
+
+#endif
+
+#include <iostream>
+#include <fstream>
+#include <algorithm>
+
+
+#if ENABLE_UNIT_TESTS
+static void GetLengthsOfLongestSuiteAndTestNames (int& longestSuiteNameLength, int& longestTestNameLength)
+{
+ longestSuiteNameLength = 0;
+ longestTestNameLength = 0;
+
+ TestList& allTests = Test::GetTestList ();
+ for (Test* test = allTests.GetHead (); test != NULL; test = test->next)
+ {
+ longestSuiteNameLength = std::max<int> ((int) strlen (test->m_details.suiteName), longestSuiteNameLength);
+ longestTestNameLength = std::max<int> ((int) strlen (test->m_details.testName), longestTestNameLength);
+ }
+}
+
+static bool SwallowLogMessages (LogType type, const char* log, va_list args)
+{
+ // Ignore everything.
+ return false;
+}
+#endif
+
+template<typename Predicate>
+static int RunUnitTests (const std::string& resultLog, bool summaryOnly, const Predicate& predicate)
+{
+#if !ENABLE_UNIT_TESTS
+ return 0;
+#else
+ int failures;
+
+ if (!resultLog.empty ())
+ {
+ std::ostringstream stringStream;
+ UnitTest::NunitTestReporter reporter (stringStream);
+ UnitTest::TestRunner runner (reporter);
+
+ failures = runner.RunTestsIf (Test::GetTestList (), NULL, predicate, 0);
+
+ std::ofstream fileStream (resultLog.c_str (), std::ios_base::out | std::ios_base::trunc);
+ fileStream << stringStream.str();
+ }
+ else
+ {
+ int longestSuiteNameLength;
+ int longestTestNameLength;
+ GetLengthsOfLongestSuiteAndTestNames (longestSuiteNameLength, longestTestNameLength);
+
+ ConsoleTestReporter reporter;
+ reporter.SetShowOnlySummary (summaryOnly);
+ reporter.SetTestNameColumnIndex (longestSuiteNameLength + 4);
+ reporter.SetResultColumnIndex (longestSuiteNameLength + 4 + longestTestNameLength + 4);
+
+ UnitTest::TestRunner runner (reporter);
+
+ failures = runner.RunTestsIf (Test::GetTestList (), NULL, predicate, 0);
+ }
+
+ return failures;
+#endif
+}
+
+template<typename Predicate>
+static void PrintUnitTestList (const Predicate& filter)
+{
+#if ENABLE_UNIT_TESTS
+
+ // Group tests by their files.
+
+ int matchingTestCount = 0;
+ vector<const char*> listedFileNames;
+ for (Test* temp = Test::GetTestList ().GetHead(); temp != NULL; temp = temp->next)
+ {
+ if (!filter (temp))
+ continue;
+
+ const char* filename = temp->m_details.filename;
+
+ // Find out whether we've already listed this file.
+ bool alreadyListedThisFile = false;
+ for (int i = 0; i < listedFileNames.size (); ++i)
+ if (strcmp (listedFileNames[i], filename) == 0)
+ {
+ alreadyListedThisFile = true;
+ break;
+ }
+
+ if (alreadyListedThisFile)
+ continue;
+
+ // Print filename.
+ printf_console ("%s:\n", filename);
+ listedFileNames.push_back (filename);
+
+ // List matching tests in file.
+ for (Test* test = Test::GetTestList ().GetHead(); test != NULL; test = test->next)
+ {
+ if (!filter (test))
+ continue;
+
+ if (strcmp (test->m_details.filename, filename) != 0)
+ continue;
+
+ printf_console ("\t[%s] %s\n", test->m_details.suiteName, test->m_details.testName);
+ ++ matchingTestCount;
+ }
+ }
+
+ printf_console ("%i test%s\n", matchingTestCount, matchingTestCount == 1 ? "" : "s");
+
+#endif
+}
+
+#if !UNITY_EXTERNAL_TOOL
+
+#if ENABLE_UNIT_TESTS
+struct TestFilter
+{
+ std::vector<std::string> m_MatchNames;
+ TestFilter(const std::vector<std::string>& matchNames)
+ : m_MatchNames (matchNames)
+ {
+ // Lowercase everything.
+ for (int i = 0; i < m_MatchNames.size(); ++i)
+ m_MatchNames[i] = ToLower (m_MatchNames[i]);
+ }
+
+ bool operator () (const Test* test) const
+ {
+ if (m_MatchNames.empty ())
+ return true;
+
+ for (int i = 0; i < m_MatchNames.size(); ++i)
+ {
+ if (ToLower (std::string (test->m_details.suiteName)).find (m_MatchNames[i]) != std::string::npos ||
+ ToLower (std::string (test->m_details.testName)).find (m_MatchNames[i]) != std::string::npos)
+ return true;
+ }
+
+ return false;
+ }
+};
+
+inline void CreateAndInstallGameManager (ManagerContext::Managers id)
+{
+ GameManager* instance = CreateGameManager (GetManagerContext ().m_ManagerClassIDs[id]);
+ SetManagerPtrInContext (id, instance);
+}
+
+static void InitializeScriptMapper ()
+{
+ // Create manager.
+ CreateAndInstallGameManager (ManagerContext::kScriptMapper);
+
+ #if UNITY_EDITOR
+ // Populate with builtin shaders. Only available in editor.
+ GetBuiltinExtraResourceManager ().RegisterShadersWithRegistry (GetScriptMapperPtr ());
+ #endif
+}
+
+static void InitializeGraphicsSettings ()
+{
+ CreateAndInstallGameManager (ManagerContext::kGraphicsSettings);
+ GetGraphicsSettings ().SetDefaultAlwaysIncludedShaders ();
+}
+
+static void InitializePhysicsManager ()
+{
+ #if ENABLE_PHYSICS
+ CreateAndInstallGameManager (ManagerContext::kPhysicsManager);
+ #endif
+}
+
+static void InitializeTagManager ()
+{
+ CreateAndInstallGameManager (ManagerContext::kTagManager);
+ RegisterDefaultTagsAndLayerMasks ();
+}
+
+static void InitializeBuildSettings ()
+{
+ CreateAndInstallGameManager (ManagerContext::kBuildSettings);
+}
+#endif
+
+void RunUnitTestsIfRequiredAndExit ()
+{
+#if !ENABLE_UNIT_TESTS
+ return;
+#else
+
+ const bool listUnitTests = HasARGV ("listUnitTests");
+ const bool runUnitTests = HasARGV ("runUnitTests");
+
+ // Check if we are supposed to run or list unit tests.
+ if (!listUnitTests && !runUnitTests)
+ return;
+
+ // Yes, so initialize the systems we need.
+
+ // We log to stdout so get that in place on Windows.
+#if UNITY_WIN
+ OpenConsole ();
+#endif
+
+ // For unit testing, we're not interested in log output from engine initialization.
+ // Temporarily disable logging.
+ SetLogEntryHandler (SwallowLogMessages);
+
+ // We want the test runner to behave like batchmode and not pop up
+ // any dialogs, so force running in batchmode.
+ SetIsBatchmode (true);
+
+ // Start with persistent manager. Required by InitializeEngineNoGraphics().
+ UNITY_NEW_AS_ROOT (PathNamePersistentManager (0), kMemManager, "PersistentManager", "");
+
+ if (!InitializeEngineNoGraphics ())
+ {
+ fprintf (stderr, "Failed to initialize engine (no graphics)!\n");
+ exit (-1);
+ }
+
+ if (!InitializeEngineGraphics ())
+ {
+ fprintf (stderr, "Failed to initialize engine!\n");
+ exit (-1);
+ }
+
+ // Initialize the global game managers we want to have available.
+ InitializeScriptMapper ();
+ InitializeGraphicsSettings ();
+ InitializePhysicsManager ();
+ InitializeTagManager ();
+ InitializeBuildSettings ();
+
+ ////TODO: this path is broken; the EXPECT stuff doesn't work for it
+ std::string log;
+ #if 0
+ if (HasARGV ("unitTestsLog"))
+ log = GetFirstValueForARGV ("unitTestsLog");
+ #endif
+
+ const bool showOnlySummary = HasARGV ("unitTestsSummaryOnly");
+
+ std::vector<std::string> matchNames;
+ if (runUnitTests)
+ matchNames = GetValuesForARGV ("runUnitTests");
+ else if (listUnitTests)
+ matchNames = GetValuesForARGV ("listUnitTests");
+
+ TestFilter filter (matchNames);
+
+ // Run or list tests.
+ int numFailures = 0;
+ if (runUnitTests)
+ {
+ numFailures = RunUnitTests (log, showOnlySummary, filter);
+ }
+ else if (listUnitTests)
+ {
+ SetLogEntryHandler (NULL);
+ PrintUnitTestList (filter);
+ }
+
+ // Shut down.
+ CleanupEngine ();
+ InputShutdown ();
+
+ // Done.
+ exit (numFailures != 0 ? 1 : 0);
+
+#endif // ENABLE_UNIT_TESTS
+}
+
+#endif // UNITY_EXTERNAL_TOOL
+
+int RunUnitTests (const std::string& resultLog)
+{
+#if !ENABLE_UNIT_TESTS
+ return 0;
+#else
+ return RunUnitTests (resultLog, false, UnitTest::True ());
+#endif
+}
+
+void ExpectLogMessageTriggeredByTest (LogType type, const char* logFragment)
+{
+#if ENABLE_UNIT_TESTS
+ ConsoleTestReporter::GetInstance ()->ExpectLogMessage (type, logFragment);
+#endif
+}
+
+#if ENABLE_PERFORMANCE_TESTS
+
+ void TestMultiplyMatrices();
+ void RUN_PERFORMANCE_TESTS ()
+ {
+ TestMultiplyMatrices();
+ }
+
+#endif
diff --git a/Runtime/Testing/Testing.h b/Runtime/Testing/Testing.h
new file mode 100644
index 0000000..7ae823a
--- /dev/null
+++ b/Runtime/Testing/Testing.h
@@ -0,0 +1,56 @@
+#ifndef TESTING_H
+#define TESTING_H
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+/// Use this to deal with asserts and other things that get logged.
+/// If your test explicitly *expects* a certain piece of code to detect
+/// and log an error, set up an expected message.
+///
+/// The given log fragment can simply be a substring of the actual log
+/// message.
+///
+/// @example
+/// EXPECT (Error, "Cannot find");
+/// MethodThatTriggersError ();
+/// @endexample
+///
+/// @param type Log type (i.e. enum value from LogType without the "LogType_" prefix").
+/// @param logFragment Substring of log message that is expected.
+///
+/// @note Debug messages are ignored entirely and cannot be expected.
+/// @note Any log messages that are not expected will lead to a failure of the
+/// running unit test.
+/// @note The order of log messages is not checked but a single EXPECT will only
+/// cause the acceptance of a single occurrence of the message.
+/// @note Expecting messages that do not arrive will cause tests to fail.
+#define EXPECT(type, logFragment) \
+ ExpectLogMessageTriggeredByTest (LogType_##type, logFragment)
+
+/// Expect a log message to be triggered by the currently
+/// executing unit test.
+///
+/// @see EXPECT
+void ExpectLogMessageTriggeredByTest (LogType type, const char* logFragment);
+
+#endif // ENABLE_UNIT_TESTS
+
+
+/// If the "-runUnitTests" command-line argument is present, run unit tests
+/// and exit the process with a status code indicating success or failure.
+void RunUnitTestsIfRequiredAndExit ();
+
+int RunUnitTests (const std::string& resultLog);
+
+
+////TODO: needs to be cleaned up
+#define ENABLE_PERFORMANCE_TESTS 0
+#if ENABLE_PERFORMANCE_TESTS
+ void RUN_PERFORMANCE_TESTS ();
+#else
+ #define RUN_PERFORMANCE_TESTS()
+#endif
+
+#endif
diff --git a/Runtime/Threads/AtomicOps.h b/Runtime/Threads/AtomicOps.h
new file mode 100644
index 0000000..a166cf4
--- /dev/null
+++ b/Runtime/Threads/AtomicOps.h
@@ -0,0 +1,180 @@
+#ifndef UNITY_ATOMIC_OPS_HPP_
+#define UNITY_ATOMIC_OPS_HPP_
+
+// Primitive atomic instructions as defined by the CPU architecture.
+
+#include "Runtime/Allocator/MemoryMacros.h" // defines FORCE_INLINE (?!)
+
+// AtomicAdd - Returns the new value, after the operation has been performed (as defined by OSAtomicAdd32Barrier)
+FORCE_INLINE int AtomicAdd (int volatile* i, int value);
+
+// AtomicSub - Returns the new value, after the operation has been performed (as defined by OSAtomicSub32Barrier)
+FORCE_INLINE int AtomicSub (int volatile* i, int value);
+
+// AtomicIncrement - Returns the new value, after the operation has been performed (as defined by OSAtomicAdd32Barrier)
+FORCE_INLINE int AtomicIncrement (int volatile* i);
+
+// AtomicDecrement - Returns the new value, after the operation has been performed (as defined by OSAtomicAdd32Barrier)
+FORCE_INLINE int AtomicDecrement (int volatile* i);
+
+// AtomicCompareExchange - Returns value is the initial value of the Destination pointer (as defined by _InterlockedCompareExchange)
+FORCE_INLINE bool AtomicCompareExchange (int volatile* i, int newValue, int expectedValue);
+
+// AtomicExchange - Returns the initial value pointed to by Target (as defined by _InterlockedExchange)
+FORCE_INLINE int AtomicExchange (int volatile* i, int value);
+
+#define ATOMIC_API_GENERIC (UNITY_OSX || UNITY_IPHONE || UNITY_WIN || UNITY_XENON || UNITY_PS3 || UNITY_ANDROID || UNITY_PEPPER || UNITY_LINUX || UNITY_BB10 || UNITY_WII || UNITY_TIZEN)
+
+#if !ATOMIC_API_GENERIC && SUPPORT_THREADS
+# include "PlatformAtomicOps.h"
+#else
+
+// This file contains implementations for the platforms we already support.
+// Going forward (some of) these implementations must move to the platform specific directories.
+
+#include "Runtime/Utilities/Utility.h"
+
+#if UNITY_WIN
+#include <intrin.h>
+typedef char UnityAtomicsTypesAssert_FailsIfIntSize_NEQ_LongSize[sizeof(int) == sizeof(LONG) ? 1 : -1];
+#elif UNITY_OSX
+
+
+
+#include <libkern/OSAtomic.h>
+
+#elif UNITY_IPHONE
+#include <libkern/OSAtomic.h>
+#elif UNITY_PS3
+#include <cell/atomic.h>
+#elif UNITY_ANDROID || (UNITY_LINUX && defined(__GNUC__) && defined(__arm__))
+// use gcc builtin __sync_*
+#elif UNITY_BB10
+#include <atomic.h>
+#include <arm/smpxchg.h>
+#endif
+
+#include "Runtime/Utilities/Utility.h"
+
+// AtomicAdd - Returns the new value, after the operation has been performed (as defined by OSAtomicAdd32Barrier)
+FORCE_INLINE int AtomicAdd (int volatile* i, int value) {
+#if UNITY_OSX || UNITY_IPHONE
+#if defined(__ppc__)
+ #error "Atomic addition undefined for this platform"
+#endif
+
+ return OSAtomicAdd32Barrier (value, (int*)i);
+
+#elif UNITY_WIN || UNITY_XENON
+ return _InterlockedExchangeAdd ((long volatile*)i, value) + value;
+#elif UNITY_PS3
+ return cellAtomicAdd32((uint32_t*)i, value) + value; // on ps3 it returns the pre-increment value
+#elif UNITY_ANDROID || UNITY_TIZEN
+ return __sync_add_and_fetch(i,value);
+#elif UNITY_PEPPER
+ int temp = value;
+ __asm__ __volatile__("lock; xaddl %0,%1"
+ : "+r" (temp), "+m" (*i)
+ : : "memory");
+ // temp now holds the old value of *ptr
+ // if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug)
+ __asm__ __volatile__("lfence" : : : "memory");
+ return temp + value;
+#elif UNITY_LINUX
+ return __sync_add_and_fetch(i, value);
+#elif UNITY_BB10
+ return atomic_add_value((unsigned int*)i, value)+value;
+#elif UNITY_WII
+ int wasEnabled = OSDisableInterrupts();
+ *i = (*i) + value;
+ OSRestoreInterrupts(wasEnabled);
+ return *i;
+#elif !SUPPORT_THREADS
+ return *i+=value;
+#else
+#error "Atomic op undefined for this platform"
+#endif
+}
+
+// AtomicSub - Returns the new value, after the operation has been performed (as defined by OSAtomicSub32Barrier)
+inline int AtomicSub (int volatile* i, int value) {
+#if UNITY_PS3
+ return cellAtomicSub32((uint32_t*)i, value) - value; // on ps3 it returns the pre-increment value
+#elif UNITY_ANDROID || UNITY_TIZEN
+ return __sync_sub_and_fetch(i,value);
+#elif UNITY_LINUX
+ return __sync_sub_and_fetch(i, value);
+#elif UNITY_BB10
+ return atomic_sub_value((unsigned int*)i, value) - value;
+#else
+ return AtomicAdd(i, -value);
+#endif
+}
+
+// AtomicIncrement - Returns the new value, after the operation has been performed (as defined by OSAtomicAdd32Barrier)
+FORCE_INLINE int AtomicIncrement (int volatile* i) {
+#if UNITY_WIN || UNITY_XENON
+ return _InterlockedIncrement ((long volatile*)i);
+#elif UNITY_PS3
+ return cellAtomicIncr32((uint32_t*)i)+1; // on ps3 it returns the pre-increment value
+#elif !SUPPORT_THREADS
+ return ++*i;
+#else
+ return AtomicAdd(i, 1);
+#endif
+}
+
+// AtomicDecrement - Returns the new value, after the operation has been performed (as defined by OSAtomicAdd32Barrier)
+FORCE_INLINE int AtomicDecrement (int volatile* i) {
+#if UNITY_WIN || UNITY_XENON
+ return _InterlockedDecrement ((long volatile*)i);
+#elif UNITY_PS3
+ return cellAtomicDecr32((uint32_t*)i)-1; // on ps3 it returns the pre-increment value
+#elif !SUPPORT_THREADS
+ return --*i;
+#else
+ return AtomicSub(i, 1);
+#endif
+}
+
+// AtomicCompareExchange - Returns value is the initial value of the Destination pointer (as defined by _InterlockedCompareExchange)
+#if UNITY_WIN || UNITY_XENON
+FORCE_INLINE bool AtomicCompareExchange (int volatile* i, int newValue, int expectedValue) {
+ return _InterlockedCompareExchange ((long volatile*)i, (long)newValue, (long)expectedValue) == expectedValue;
+}
+#elif UNITY_OSX || UNITY_IPHONE
+FORCE_INLINE bool AtomicCompareExchange (int volatile* i, int newValue, int expectedValue) {
+ return OSAtomicCompareAndSwap32Barrier (expectedValue, newValue, reinterpret_cast<volatile int32_t*>(i));
+}
+#elif UNITY_LINUX || UNITY_PEPPER || UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+FORCE_INLINE bool AtomicCompareExchange (int volatile* i, int newValue, int expectedValue) {
+# if UNITY_BB10
+ return _smp_cmpxchg((unsigned int*)i, expectedValue, newValue) == expectedValue;
+# else
+ return __sync_bool_compare_and_swap(i, expectedValue, newValue);
+#endif
+}
+#elif UNITY_PS3
+FORCE_INLINE bool AtomicCompareExchange (int volatile* i, int newValue, int expectedValue) {
+ return cellAtomicCompareAndSwap32((uint32_t*)i, (uint32_t)expectedValue, (uint32_t)newValue) == (uint32_t)expectedValue;
+}
+#endif
+
+// AtomicExchange - Returns the initial value pointed to by Target (as defined by _InterlockedExchange)
+#if UNITY_WIN || UNITY_XENON
+FORCE_INLINE int AtomicExchange (int volatile* i, int value) {
+ return (int)_InterlockedExchange ((long volatile*)i, (long)value);
+}
+#elif UNITY_ANDROID || UNITY_TIZEN || UNITY_OSX || UNITY_LINUX // fallback to loop
+FORCE_INLINE int AtomicExchange (int volatile* i, int value) {
+ int prev;
+ do { prev = *i; }
+ while (!AtomicCompareExchange(i, value, prev));
+ return prev;
+}
+#endif
+
+#endif // ATOMIC_API_GENERIC
+#undef ATOMIC_API_GENERIC
+
+#endif //UNITY_ATOMIC_OPS_HPP_
diff --git a/Runtime/Threads/AtomicRefCounter.h b/Runtime/Threads/AtomicRefCounter.h
new file mode 100644
index 0000000..9e6b5e7
--- /dev/null
+++ b/Runtime/Threads/AtomicRefCounter.h
@@ -0,0 +1,30 @@
+#ifndef __UNITY_ATOMIC_REFCOUNTER_H
+#define __UNITY_ATOMIC_REFCOUNTER_H
+
+#include "AtomicOps.h"
+
+// Used for threadsafe refcounting
+class AtomicRefCounter {
+private:
+ volatile int m_Counter;
+public:
+ // Upon the construction the self-counter is always set to 1,
+ // which means that this instance is already accounted for.
+ // This scheme shaves off the cycles that would be consumed
+ // during the unavoidable first Retain call otherwise.
+ AtomicRefCounter() : m_Counter(1) {}
+
+ FORCE_INLINE void Retain ()
+ {
+ AtomicIncrement(&m_Counter);
+ }
+
+ FORCE_INLINE bool Release ()
+ {
+ int afterDecrement = AtomicDecrement(&m_Counter);
+ AssertIf( afterDecrement < 0 ); // If we hit this assert, someone is Releasing without matching it with Retain
+ return afterDecrement == 0;
+ }
+};
+
+#endif // __UNITY_ATOMIC_REFCOUNTER_H
diff --git a/Runtime/Threads/Event.h b/Runtime/Threads/Event.h
new file mode 100644
index 0000000..2075bcf
--- /dev/null
+++ b/Runtime/Threads/Event.h
@@ -0,0 +1,20 @@
+#ifndef __EVENT_H
+#define __EVENT_H
+
+// Event synchronization object.
+
+#if SUPPORT_THREADS
+
+#if UNITY_WIN || UNITY_XENON
+# include "Winapi/PlatformEvent.h"
+#elif HAS_EVENT_OBJECT
+# include "PlatformEvent.h"
+#else
+# include "Semaphore.h"
+ typedef Semaphore Event;
+# pragma message("Event implementation missing. Using a Semaphore.")
+#endif
+
+#endif // SUPPORT_THREADS
+
+#endif // __EVENT_H
diff --git a/Runtime/Threads/JobGroupRecycler.h b/Runtime/Threads/JobGroupRecycler.h
new file mode 100644
index 0000000..9305582
--- /dev/null
+++ b/Runtime/Threads/JobGroupRecycler.h
@@ -0,0 +1,59 @@
+#ifndef JOB_GROUP_RECYCLER_H
+#define JOB_GROUP_RECYCLER_H
+
+#include "JobScheduler.h"
+
+#if ENABLE_MULTITHREADED_CODE
+
+template<int MaxGroups>
+class JobGroupRecycler
+{
+public:
+ // Limits the amount of groups used by finishing older ones first.
+ // Restrictions: Must always be called from the same thread.
+ // All jobs must be submitted before beginning a new group.
+
+ JobGroupRecycler()
+ {
+ memset( m_SlotOwners, 0, sizeof(m_SlotOwners) );
+ memset( m_JobGroups, 0, sizeof(m_JobGroups) );
+ m_NextSlot = 0;
+ }
+
+ int BeginGroup( void* owner, int maxJobs )
+ {
+ int slot = m_NextSlot;
+ if( m_SlotOwners[slot] )
+ GetJobScheduler().WaitForGroup( m_JobGroups[slot] );
+ m_JobGroups[slot] = GetJobScheduler().BeginGroup( maxJobs );
+ m_SlotOwners[slot] = owner;
+ m_NextSlot = (m_NextSlot + 1) % MaxGroups;
+ return slot;
+ }
+
+ bool SubmitJob( void* owner, int slot, JobScheduler::JobFunction func, void *data, JobScheduler::ReturnCode *returnCode )
+ {
+ Assert( slot >= 0 && slot < MaxGroups );
+ Assert( m_SlotOwners[slot] == owner );
+ return GetJobScheduler().SubmitJob( m_JobGroups[slot], func, data, returnCode );
+ }
+
+ void WaitForGroup( void* owner, int slot )
+ {
+ // Maybe someone else reused the job group
+ if( m_SlotOwners[slot] != owner )
+ return;
+ GetJobScheduler().WaitForGroup( m_JobGroups[slot] );
+ m_SlotOwners[slot] = NULL;
+ }
+
+private:
+ JobScheduler::JobGroupID m_JobGroups[MaxGroups];
+ void* m_SlotOwners[MaxGroups];
+ int m_NextSlot;
+};
+
+
+#endif // ENABLE_MULTITHREADED_CODE
+
+#endif
diff --git a/Runtime/Threads/JobScheduler.cpp b/Runtime/Threads/JobScheduler.cpp
new file mode 100644
index 0000000..cf28a71
--- /dev/null
+++ b/Runtime/Threads/JobScheduler.cpp
@@ -0,0 +1,447 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "JobScheduler.h"
+
+#if ENABLE_JOB_SCHEDULER
+
+#include "Thread.h"
+#include "ThreadUtility.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Mutex.h"
+#include "Semaphore.h"
+#include "Event.h"
+#if !UNITY_EXTERNAL_TOOL
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/Profiler/Profiler.h"
+#endif
+#include "AtomicOps.h"
+
+// ------------------------------------------------------------------------
+
+struct JobInfo
+{
+ JobScheduler::JobFunction func;
+ void* userData;
+ JobScheduler::ReturnCode* returnCode;
+};
+
+// ------------------------------------------------------------------------
+
+struct JobGroup
+{
+ enum
+ {
+ kSizeIncrement = 256,
+ kTaskCountUnused = -1
+ };
+ JobGroup() : taskCount(kTaskCountUnused), activeThreads(0), nextJob(0), jobsAdded(0) {}
+
+ volatile int taskCount;
+ volatile int activeThreads;
+ volatile int nextJob;
+ volatile int jobsAdded;
+ dynamic_array<JobInfo> jobQueue;
+ Semaphore doneSemaphore;
+};
+
+// ------------------------------------------------------------------------
+
+JobInfo* JobScheduler::FetchNextJob( int& activeGroup )
+{
+ if( activeGroup == m_PriorityGroup )
+ {
+ JobInfo* job = FetchJobInGroup(activeGroup);
+ if( job )
+ return job;
+ }
+
+ // We need a lock to change groups!
+ SimpleLock::AutoLock lock(m_Lock);
+ if( activeGroup != -1 )
+ m_Groups[activeGroup].activeThreads--;
+ int group = m_PriorityGroup;
+ for( int i = 0; i < m_GroupCount; i++ )
+ {
+ JobInfo* job = FetchJobInGroup(group);
+ if( job )
+ {
+ m_Groups[group].activeThreads++;
+ // Set priority group to one that is actually available
+ // Less work for other threads to find a good group
+ m_PriorityGroup = group;
+ activeGroup = group;
+ return job;
+ }
+ if( ++group >= m_GroupCount )
+ group = 0;
+ }
+ activeGroup = -1;
+ return NULL;
+}
+
+JobInfo* JobScheduler::FetchJobInGroup( int group )
+{
+ JobGroup& jg = m_Groups[group];
+ int curJob = jg.nextJob;
+ while( curJob < jg.jobsAdded )
+ {
+ if( AtomicCompareExchange(&jg.nextJob, curJob + 1, curJob) )
+ return &jg.jobQueue[curJob];
+
+ curJob = jg.nextJob;
+ }
+ return NULL;
+}
+
+void JobScheduler::ProcessJob( JobInfo& job, int group )
+{
+ DebugAssert( job.func );
+ void* ret = job.func(job.userData);
+ if( job.returnCode )
+ {
+ UnityMemoryBarrier();
+ *job.returnCode = ret;
+ }
+
+ // Signal if we are the last to finish
+ JobGroup& jg = m_Groups[group];
+ if( AtomicDecrement(&jg.taskCount) == 0 )
+ jg.doneSemaphore.Signal();
+}
+
+
+void JobScheduler::AwakeIdleWorkerThreads( int count )
+{
+ for( int i = 0; i < count && m_ThreadsIdle > 0; i++ )
+ {
+ if( AtomicDecrement(&m_ThreadsIdle) < 0 )
+ {
+ AtomicIncrement(&m_ThreadsIdle);
+ break;
+ }
+ m_AwakeSemaphore.Signal();
+ }
+}
+
+void* JobScheduler::WorkLoop( void* data )
+{
+ int activeGroup = -1;
+ JobScheduler* js = (JobScheduler*)data;
+
+ #if ENABLE_JOB_SCHEDULER_PROFILER
+ profiler_initialize_thread ("Worker Thread", true);
+ int workThreadIndex = AtomicIncrement (&js->m_WorkerThreadCounter);
+ WorkerProfilerInfo* profInfo = &js->m_ProfInfo[workThreadIndex];
+ bool insideFrame = false;
+ #endif
+
+ while( !js->m_Quit )
+ {
+ #if ENABLE_JOB_SCHEDULER_PROFILER
+ HandleProfilerFrames (profInfo, &insideFrame);
+ #endif
+
+ JobInfo* job = js->FetchNextJob(activeGroup);
+ if( job )
+ {
+ js->ProcessJob(*job, activeGroup);
+ }
+ else
+ {
+ AtomicIncrement(&js->m_ThreadsIdle);
+ js->m_AwakeSemaphore.WaitForSignal();
+ }
+ }
+
+ #if ENABLE_JOB_SCHEDULER_PROFILER
+ if (insideFrame)
+ {
+ //@TODO: Is this actually necessary? (It seems like cleanup thread should take care of killing it all?)
+ profiler_set_active_seperate_thread(false);
+ profiler_end_frame_seperate_thread(0);
+ }
+ profiler_cleanup_thread();
+ #endif
+
+ return NULL;
+}
+
+
+#if ENABLE_JOB_SCHEDULER_PROFILER
+void JobScheduler::HandleProfilerFrames (WorkerProfilerInfo* profInfo, bool* insideFrame)
+{
+ // Don't do fancy synchnonization here; all we need is for worker
+ // threads to do begin/end profiler frame once in a while.
+ // Worst case, we'll get a missing profiler info for a frame.
+ int endFrameID = profInfo->endFrameID;
+ if (endFrameID != -1)
+ {
+ if (*insideFrame)
+ {
+ profiler_set_active_seperate_thread (false);
+ profiler_end_frame_seperate_thread (endFrameID);
+ *insideFrame = false;
+ }
+ profiler_begin_frame_seperate_thread (kProfilerGame);
+ profiler_set_active_seperate_thread (true);
+ *insideFrame = true;
+
+ profInfo->endFrameID = -1;
+ UnityMemoryBarrier();
+ }
+}
+#endif
+
+
+
+JobScheduler::JobScheduler( int numthreads, int maxGroups, int startProcessor )
+: m_ThreadCount( numthreads )
+, m_GroupCount(maxGroups)
+, m_Quit(false)
+, m_ThreadsIdle(0)
+, m_PriorityGroup(0)
+{
+ m_Groups = new JobGroup[maxGroups];
+ if( m_ThreadCount > 0 )
+ {
+ #if ENABLE_JOB_SCHEDULER_PROFILER
+ m_WorkerThreadCounter = -1;
+ m_ProfInfo = new WorkerProfilerInfo[numthreads];
+ memset (m_ProfInfo, -1, sizeof(m_ProfInfo[0])*numthreads);
+ #endif
+
+ m_Threads = new Thread[numthreads];
+
+ for( int i = 0; i < numthreads; ++i )
+ {
+ int processor = DEFAULT_UNITY_THREAD_PROCESSOR;
+ if( startProcessor >= 0 )
+ {
+ processor = startProcessor + i;
+ }
+ m_Threads[i].SetName ("UnityWorker");
+ m_Threads[i].Run( WorkLoop, this, DEFAULT_UNITY_THREAD_STACK_SIZE, processor );
+ }
+ }
+ else
+ {
+ m_Threads = NULL;
+ #if ENABLE_JOB_SCHEDULER_PROFILER
+ m_WorkerThreadCounter = -1;
+ m_ProfInfo = NULL;
+ #endif
+ }
+}
+
+JobScheduler::~JobScheduler()
+{
+ if( m_ThreadCount > 0 )
+ {
+ m_Quit = true;
+ UnityMemoryBarrier();
+ // wait while threads exit
+ // first signal as many times as there are threads, then wait for each to exit
+ for( int i = 0; i < m_ThreadCount; ++i )
+ m_AwakeSemaphore.Signal();
+ for( int i = 0; i < m_ThreadCount; ++i )
+ m_Threads[i].WaitForExit();
+ delete[] m_Threads;
+
+ #if ENABLE_JOB_SCHEDULER_PROFILER
+ delete[] m_ProfInfo;
+ #endif
+ }
+
+ delete[] m_Groups;
+}
+
+bool JobScheduler::SubmitJob( JobGroupID group, JobFunction func, void *data, ReturnCode *returnCode )
+{
+ AssertIf( func == NULL );
+ if( m_ThreadCount <= 0 )
+ {
+ // not multi-threaded: execute job right now
+ void* z = func( data );
+ if( returnCode )
+ *returnCode = z;
+ return true;
+ }
+
+ if( group >= m_GroupCount || group < 0)
+ {
+ ErrorString( "Invalid job group ID" );
+ return false;
+ }
+
+ JobGroup& jg = m_Groups[group];
+ AtomicIncrement(&jg.taskCount);
+ int jobIndex = jg.jobsAdded;
+ JobInfo& job = jg.jobQueue[jobIndex];
+ job.func = func;
+ job.userData = data;
+ job.returnCode = returnCode;
+ int nextIndex = AtomicIncrement(&jg.jobsAdded);
+ // This may fail if you add jobs from multiple threads to the same group
+ Assert(nextIndex == jobIndex + 1);
+ AwakeIdleWorkerThreads(nextIndex - jg.nextJob);
+ return true;
+}
+
+JobScheduler::JobGroupID JobScheduler::BeginGroup( int maxJobs )
+{
+ // See if we can allocate a group without blocking.
+ for( int isBlocking = 0; isBlocking < 2; ++isBlocking )
+ {
+ // If a group still has active threads we can't use it immediately after WaitForGroup().
+ // By blocking we guarantee to find a group, as long as we stay within maxGroups.
+ JobGroupID id = BeginGroupInternal(maxJobs, isBlocking != 0);
+ if( id != -1 )
+ return id;
+ }
+ ErrorString("JobScheduler: too many job groups");
+ return -1;
+}
+
+JobScheduler::JobGroupID JobScheduler::BeginGroupInternal( int maxJobs, bool isBlocking )
+{
+ // Find unused group. We need a lock for that.
+ m_Lock.Lock();
+ for( int i = 0; i < m_GroupCount; ++i )
+ {
+ JobGroup& group = m_Groups[i];
+ if( group.taskCount == JobGroup::kTaskCountUnused
+ && ( isBlocking || group.activeThreads == 0 ) )
+ {
+ // We consider finishing group a pending task
+ // Keeps job group alive until everything is done
+ group.taskCount = 1;
+
+ // Spin while worker threads are using our group
+ // Do this *after* we've marked it used (case 492417)
+ while( group.activeThreads != 0 )
+ {
+ m_Lock.Unlock();
+ m_Lock.Lock();
+ }
+ group.jobsAdded = 0;
+ group.nextJob = 0;
+ const int rounding = JobGroup::kSizeIncrement;
+ int roundedSize = (maxJobs + rounding - 1) / rounding * rounding;
+ group.jobQueue.reserve(roundedSize);
+ group.jobQueue.resize_uninitialized(maxJobs);
+ m_Lock.Unlock();
+ return i;
+ }
+ }
+ m_Lock.Unlock();
+ return -1;
+}
+
+bool JobScheduler::IsGroupFinished( JobGroupID group )
+{
+ const JobGroup& jg = m_Groups[group];
+ // Last reference is kept until WaitForGroup()
+ return jg.taskCount == 1;
+}
+
+void JobScheduler::WaitForGroup( JobGroupID group )
+{
+ if( group >= m_GroupCount )
+ {
+ ErrorString( "Invalid job group ID" );
+ return;
+ }
+
+ JobGroup& jg = m_Groups[group];
+
+ // Release our reference to job group
+ // Pending jobs (if any) also have refs
+ if( AtomicDecrement(&jg.taskCount) != 0 )
+ {
+ // Set our group as having priority so other threads fetch from it
+ m_PriorityGroup = group;
+
+ for( ;; )
+ {
+ JobInfo* job = FetchJobInGroup(group);
+ if( !job )
+ break;
+ ProcessJob(*job, group);
+ }
+
+ jg.doneSemaphore.WaitForSignal();
+ Assert(jg.nextJob == jg.jobsAdded);
+ Assert(jg.taskCount == 0);
+ }
+
+ // Set count to kTaskCountUnused (-1)
+ AtomicDecrement(&jg.taskCount);
+}
+
+
+#if ENABLE_JOB_SCHEDULER_PROFILER
+void JobScheduler::EndProfilerFrame (UInt32 frameIDAndValidFlag)
+{
+ // Don't do fancy synchnonization here; all we need is for worker
+ // threads to do begin/end profiler frame once in a while.
+ // Worst case, we'll get a missing profiler info for a frame.
+ for (int i = 0; i < m_ThreadCount; ++i)
+ {
+ m_ProfInfo[i].endFrameID = frameIDAndValidFlag;
+ }
+ UnityMemoryBarrier();
+}
+#endif
+
+
+// ----------------------------------------------------------------------
+
+#if !UNITY_EXTERNAL_TOOL
+
+static JobScheduler* g_Scheduler = NULL;
+
+void CreateJobScheduler()
+{
+ AssertIf( g_Scheduler );
+
+ const int kMaxJobGroups = 16; // increase if we ever run out of concurrent separate job groups
+#if UNITY_XENON
+ // Use threads 1 (core 0) and 2/3 (core 1)
+ int startProcessor = 1;
+ int workerThreads = 3;
+#elif UNITY_PS3
+ int startProcessor = 1;
+ int workerThreads = 1;
+#elif UNITY_OSX
+ int startProcessor = 1;
+ int workerThreads = systeminfo::GetNumberOfCores() - 1;
+ Thread::SetCurrentThreadProcessor(0);
+#else
+ int startProcessor = -1;
+ int workerThreads = systeminfo::GetProcessorCount() - 1;
+#endif
+
+ // Don't use an unreasonable amount of threads on future hardware.
+ // Mono GC has a 256 thread limit for the process (case 443576).
+ if (workerThreads > 128)
+ workerThreads = 128;
+
+ g_Scheduler = new JobScheduler( workerThreads, kMaxJobGroups, startProcessor );
+}
+
+void DestroyJobScheduler()
+{
+ delete g_Scheduler;
+ g_Scheduler = NULL;
+}
+
+JobScheduler& GetJobScheduler()
+{
+ AssertIf( !g_Scheduler );
+ return *g_Scheduler;
+}
+
+#endif
+
+
+#endif
diff --git a/Runtime/Threads/JobScheduler.h b/Runtime/Threads/JobScheduler.h
new file mode 100644
index 0000000..a638113
--- /dev/null
+++ b/Runtime/Threads/JobScheduler.h
@@ -0,0 +1,100 @@
+#ifndef JOB_SCHEDULER_H
+#define JOB_SCHEDULER_H
+
+#include "SimpleLock.h"
+#include "Semaphore.h"
+#include "Runtime/Modules/ExportModules.h"
+
+// On single-CPU platforms, this should never be used
+#define ENABLE_JOB_SCHEDULER (ENABLE_MULTITHREADED_CODE || ENABLE_MULTITHREADED_SKINNING)
+
+#if ENABLE_JOB_SCHEDULER
+
+#define ENABLE_JOB_SCHEDULER_PROFILER (!UNITY_EXTERNAL_TOOL && ENABLE_PROFILER)
+
+
+struct JobGroup;
+struct JobInfo;
+class Thread;
+
+class EXPORT_COREMODULE JobScheduler
+{
+public:
+ // Usage and restrictions:
+ // Jobs in the same group must be submitted from the same thread
+ // It's fine to submit jobs in separate groups from different threads
+ // It's also fine to WaitForGroup() on a another thread than BeginGroup()
+ //
+ // Submitting jobs is lockless and wakes up idle worker threads if needed.
+ // Worker threads are lockless when they keep consuming from the same queue.
+ // Changing queues or going idle requires a lock. This is to keep track of
+ // how many workers try to consume a queue when the group gets recycled.
+ // Hopefully this is none, but being lockless we don't really know if anyone
+ // got stuck during the size check on the queue and before reading the next
+ // element atomically. The size check can safely use old values except when
+ // the group is recycled. In that case we need to flush all worker threads
+ // from accessing the queue and doing an invalid comparison.
+
+ typedef void* (*JobFunction)(void*);
+ typedef void* volatile ReturnCode;
+ typedef int JobGroupID;
+
+ JobScheduler( int numthreads, int maxGroups, int startProcessor = -1 );
+ ~JobScheduler();
+
+ JobGroupID BeginGroup( int maxJobs );
+ bool IsGroupFinished( JobGroupID group );
+ void WaitForGroup( JobGroupID group );
+
+ bool SubmitJob( JobGroupID group, JobFunction func, void *data, ReturnCode *returnCode );
+ int GetThreadCount () const { return m_ThreadCount; }
+
+ #if ENABLE_JOB_SCHEDULER_PROFILER
+ void EndProfilerFrame (UInt32 frameIDAndValidFlag);
+ #endif
+
+private:
+ JobGroupID BeginGroupInternal( int maxJobs, bool isBlocking );
+ JobInfo* FetchNextJob( int& threadActiveGroup );
+ JobInfo* FetchJobInGroup( int group );
+ void ProcessJob( JobInfo& job, int group );
+ void AwakeIdleWorkerThreads( int count );
+
+ #if ENABLE_JOB_SCHEDULER_PROFILER
+ struct WorkerProfilerInfo
+ {
+ int endFrameID;
+ int pad[15]; // pad so individual threads don't hammer same cache line
+ };
+ static void HandleProfilerFrames (WorkerProfilerInfo* info, bool* insideFrame);
+ #endif
+
+ static void* WorkLoop( void* data );
+
+private:
+ JobGroup* m_Groups;
+ int m_GroupCount;
+ int m_ThreadCount;
+ Thread* m_Threads;
+ volatile bool m_Quit;
+ SimpleLock m_Lock;
+ Semaphore m_AwakeSemaphore;
+ volatile int m_ThreadsIdle;
+ volatile int m_PriorityGroup;
+
+ #if ENABLE_JOB_SCHEDULER_PROFILER
+ WorkerProfilerInfo* m_ProfInfo;
+ int m_WorkerThreadCounter;
+ #endif
+};
+
+#if !UNITY_EXTERNAL_TOOL
+void CreateJobScheduler();
+void DestroyJobScheduler();
+EXPORT_COREMODULE JobScheduler& GetJobScheduler();
+#endif
+
+#endif // ENABLE_MULTITHREADED_CODE
+
+
+#endif
diff --git a/Runtime/Threads/Mutex.cpp b/Runtime/Threads/Mutex.cpp
new file mode 100644
index 0000000..9ca9991
--- /dev/null
+++ b/Runtime/Threads/Mutex.cpp
@@ -0,0 +1,70 @@
+#include "UnityPrefix.h"
+
+#if SUPPORT_THREADS
+
+#include "Mutex.h"
+
+#ifndef THREAD_LOCK_DEBUG
+#define THREAD_LOCK_DEBUG 0
+#endif
+
+Mutex::Mutex()
+{
+}
+
+Mutex::~Mutex ()
+{
+}
+
+bool Mutex::IsLocked()
+{
+ if(m_Mutex.TryLock())
+ {
+ Unlock();
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+void Mutex::BlockUntilUnlocked()
+{
+ Lock();
+ Unlock();
+}
+
+void Mutex::Lock()
+{
+#if THREAD_LOCK_DEBUG
+ m_PerThreadLockDepth.SetIntValue(m_PerThreadLockDepth.GetIntValue() + 1);
+#endif
+ m_Mutex.Lock();
+}
+
+void Mutex::Unlock()
+{
+#if THREAD_LOCK_DEBUG
+ AssertIf(m_PerThreadLockDepth.GetIntValue() == 0);
+ m_PerThreadLockDepth.SetIntValue(m_PerThreadLockDepth.GetIntValue() - 1);
+#endif
+ m_Mutex.Unlock();
+}
+
+bool Mutex::TryLock()
+{
+#if THREAD_LOCK_DEBUG
+ if (m_Mutex.TryLock())
+ {
+ m_PerThreadLockDepth.SetIntValue(m_PerThreadLockDepth.GetIntValue() + 1);
+ return true;
+ }
+ else
+ return false;
+#else
+ return m_Mutex.TryLock();
+#endif
+}
+
+#endif // SUPPORT_THREADS ; has dummy mutex implemented in headerfile
diff --git a/Runtime/Threads/Mutex.h b/Runtime/Threads/Mutex.h
new file mode 100644
index 0000000..91e9927
--- /dev/null
+++ b/Runtime/Threads/Mutex.h
@@ -0,0 +1,86 @@
+#ifndef __MUTEX_H
+#define __MUTEX_H
+
+#if SUPPORT_THREADS
+
+#if UNITY_WIN || UNITY_XENON
+# include "Winapi/PlatformMutex.h"
+#elif UNITY_OSX || UNITY_IPHONE || UNITY_ANDROID || UNITY_PEPPER || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+# include "Posix/PlatformMutex.h"
+#else
+# include "PlatformMutex.h"
+#endif
+
+#include "Runtime/Utilities/NonCopyable.h"
+
+/**
+ * A mutex class. Always recursive (a single thread can lock multiple times).
+ */
+class Mutex : public NonCopyable
+{
+public:
+
+ class AutoLock
+ {
+ public:
+ AutoLock( Mutex& mutex )
+ : m_Mutex(&mutex)
+ {
+ mutex.Lock();
+ }
+
+ ~AutoLock()
+ {
+ m_Mutex->Unlock();
+ }
+
+ private:
+ AutoLock(const AutoLock&);
+ AutoLock& operator=(const AutoLock&);
+
+ private:
+ Mutex* m_Mutex;
+ };
+
+ Mutex();
+ ~Mutex();
+
+ void Lock();
+ void Unlock();
+
+ // Returns true if locking succeeded
+ bool TryLock();
+
+ // Returns true if the mutex is currently locked
+ bool IsLocked();
+
+ void BlockUntilUnlocked();
+
+private:
+
+ PlatformMutex m_Mutex;
+};
+
+#else
+
+// Used for threadsafe refcounting
+class Mutex
+{
+public:
+
+ class AutoLock
+ {
+ public:
+ AutoLock( Mutex& mutex ){}
+ ~AutoLock() {}
+ };
+
+ bool TryLock () { return true; }
+ void Lock () { }
+ void Unlock () { }
+ bool IsLocked () { return false; }
+ void BlockUntilUnlocked() { }
+};
+
+#endif //SUPPORT_THREADS
+#endif
diff --git a/Runtime/Threads/Posix/PlatformMutex.cpp b/Runtime/Threads/Posix/PlatformMutex.cpp
new file mode 100644
index 0000000..b48e138
--- /dev/null
+++ b/Runtime/Threads/Posix/PlatformMutex.cpp
@@ -0,0 +1,51 @@
+#include "UnityPrefix.h"
+
+#if SUPPORT_THREADS
+
+#ifndef MUTEX_API_PTHREAD
+#define MUTEX_API_PTHREAD (UNITY_OSX || UNITY_IPHONE || UNITY_ANDROID || UNITY_PEPPER || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN)
+#endif
+
+#endif // SUPPORT_THREADS
+
+#if MUTEX_API_PTHREAD
+// -------------------------------------------------------------------------------------------------
+// pthreads
+
+#include "PlatformMutex.h"
+
+#if defined(__native_client__)
+#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP
+#endif
+
+PlatformMutex::PlatformMutex ( )
+{
+ pthread_mutexattr_t attr;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
+ pthread_mutex_init(&mutex, &attr);
+ pthread_mutexattr_destroy(&attr);
+}
+
+PlatformMutex::~PlatformMutex ()
+{
+ pthread_mutex_destroy(&mutex);
+}
+
+void PlatformMutex::Lock()
+{
+ pthread_mutex_lock(&mutex);
+}
+
+void PlatformMutex::Unlock()
+{
+ pthread_mutex_unlock(&mutex);
+}
+
+bool PlatformMutex::TryLock()
+{
+ return pthread_mutex_trylock(&mutex) == 0;
+}
+
+#endif // MUTEX_API_PTHREAD
diff --git a/Runtime/Threads/Posix/PlatformMutex.h b/Runtime/Threads/Posix/PlatformMutex.h
new file mode 100644
index 0000000..0071094
--- /dev/null
+++ b/Runtime/Threads/Posix/PlatformMutex.h
@@ -0,0 +1,29 @@
+#ifndef __PLATFORMMUTEX_H
+#define __PLATFORMMUTEX_H
+
+#if SUPPORT_THREADS
+
+#include <pthread.h>
+#include "Runtime/Utilities/NonCopyable.h"
+
+/**
+ * A platform/api specific mutex class. Always recursive (a single thread can lock multiple times).
+ */
+class PlatformMutex : public NonCopyable
+{
+ friend class Mutex;
+protected:
+ PlatformMutex();
+ ~PlatformMutex();
+
+ void Lock();
+ void Unlock();
+ bool TryLock();
+
+private:
+
+ pthread_mutex_t mutex;
+};
+
+#endif // SUPPORT_THREADS
+#endif // __PLATFORMMUTEX_H
diff --git a/Runtime/Threads/Posix/PlatformSemaphore.h b/Runtime/Threads/Posix/PlatformSemaphore.h
new file mode 100644
index 0000000..bc20a55
--- /dev/null
+++ b/Runtime/Threads/Posix/PlatformSemaphore.h
@@ -0,0 +1,69 @@
+#ifndef __PLATFORMSEMAPHORE_H
+#define __PLATFORMSEMAPHORE_H
+
+#if SUPPORT_THREADS
+
+#ifndef SEMAPHORE_API_PTHREAD
+#define SEMAPHORE_API_PTHREAD (UNITY_LINUX || UNITY_PEPPER || UNITY_ANDROID || UNITY_PS3 || UNITY_BB10 || UNITY_TIZEN)
+#endif
+
+#endif // SUPPORT_THREADS
+
+#if SEMAPHORE_API_PTHREAD
+
+#if UNITY_PEPPER
+# include <errno.h>
+# if defined(__native_client__)
+# include <semaphore.h>
+# else
+# include <sys/semaphore.h>
+# endif
+#else
+# include <semaphore.h>
+# include <errno.h>
+#endif
+
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Utilities/NonCopyable.h"
+
+class PlatformSemaphore : public NonCopyable
+{
+ friend class Semaphore;
+protected:
+ void Create();
+ void Destroy();
+
+ void WaitForSignal();
+ void Signal();
+
+private:
+ sem_t m_Semaphore;
+};
+
+#define REPORT_SEM_ERROR(action) ErrorStringMsg ("Failed to %s a semaphore (%s)\n", action, strerror (errno))
+
+ inline void PlatformSemaphore::Create() { if (sem_init(&m_Semaphore, 0, 0) == -1) REPORT_SEM_ERROR ("open"); }
+ inline void PlatformSemaphore::Destroy() { if (sem_destroy(&m_Semaphore) == -1) REPORT_SEM_ERROR ("destroy"); }
+#if !UNITY_BB10
+ inline void PlatformSemaphore::WaitForSignal() { if (sem_wait(&m_Semaphore) == -1) REPORT_SEM_ERROR ("wait on"); }
+#else
+ inline void PlatformSemaphore::WaitForSignal() {
+ int ret = 0;
+ while ((ret = sem_wait(&m_Semaphore)) == -1 && errno == EINTR)
+ {
+ continue;
+ }
+
+ if( ret == -1 )
+ REPORT_SEM_ERROR ("wait on");
+ }
+#endif
+ inline void PlatformSemaphore::Signal() {
+ if (sem_post(&m_Semaphore) == -1)
+ REPORT_SEM_ERROR ("post to");
+ }
+
+#undef REPORT_SEM_ERROR
+
+#endif // SEMAPHORE_API_PTHREAD
+#endif // __PLATFORMSEMAPHORE_H
diff --git a/Runtime/Threads/Posix/PlatformThread.cpp b/Runtime/Threads/Posix/PlatformThread.cpp
new file mode 100644
index 0000000..3b3a9e0
--- /dev/null
+++ b/Runtime/Threads/Posix/PlatformThread.cpp
@@ -0,0 +1,185 @@
+#include "UnityPrefix.h"
+
+#if SUPPORT_THREADS
+
+#ifndef THREAD_API_PTHREAD
+#define THREAD_API_PTHREAD (UNITY_OSX || UNITY_PS3 || UNITY_IPHONE || UNITY_ANDROID || UNITY_PEPPER || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN)
+#endif
+
+#endif // SUPPORT_THREADS
+
+#if THREAD_API_PTHREAD
+
+#include "PlatformThread.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/ThreadHelper.h"
+
+#include "Runtime/Utilities/Word.h"
+//#include "Runtime/Utilities/Utility.h"
+
+// module@TODO : Move this to PlatformThread.h
+#if UNITY_PS3
+# include <sys/timer.h>
+# include "pthread_ext/pthread_ext.h"
+# define pthread_create pthread_ext_create
+#endif
+
+#if UNITY_PEPPER && defined(__native_client__)
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <sys/nacl_syscalls.h>
+#endif
+
+#if UNITY_ANDROID
+#include <jni.h>
+ JavaVM* GetJavaVm();
+ extern "C" void* GC_lookup_thread(pthread_t id);
+ extern "C" void GC_delete_thread(pthread_t id);
+#endif
+
+PlatformThread::PlatformThread()
+: m_Thread((ThreadID)NULL)
+{
+}
+
+PlatformThread::~PlatformThread()
+{
+ AssertMsg(m_Thread == (ThreadID)NULL, "***Thread was not cleaned up!***");
+}
+
+
+void PlatformThread::Create(const Thread* thread, const UInt32 stackSize, const int processor)
+{
+ m_DefaultPriority = 0;
+ m_Processor = processor;
+
+ if(stackSize)
+ {
+ pthread_attr_t attr;
+ memset(&attr, 0, sizeof(attr));
+
+// module@TODO : Implement pthread_attr_init/pthread_attr_setstacksize in PlatformThread.h
+#if UNITY_PS3
+ attr.stacksize = stackSize;
+ attr.name = "_UNITY_";
+#else
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, stackSize);
+#endif
+ pthread_create(&m_Thread, &attr, Thread::RunThreadWrapper, (void*)thread);
+ }
+ else {
+ pthread_create(&m_Thread, NULL, Thread::RunThreadWrapper, (void*)thread);
+ }
+
+#if UNITY_OSX || UNITY_IPHONE || UNITY_PS3
+ // Y U no want on Linux?
+ struct sched_param param;
+ int outputPolicy;
+ if (pthread_getschedparam(m_Thread, &outputPolicy, &param) == 0)
+ m_DefaultPriority = param.sched_priority;
+ AssertIf(m_DefaultPriority == 0); // Y U no like 0 priority?
+#endif
+
+ if (thread->m_Priority != kNormalPriority)
+ UpdatePriority(thread);
+}
+
+void PlatformThread::Enter(const Thread* thread)
+{
+ ThreadHelper::SetThreadProcessor(thread, m_Processor);
+}
+
+void PlatformThread::Exit(const Thread* thread, void* result)
+{
+#if UNITY_ANDROID
+ if (GC_lookup_thread(pthread_self()))
+ GC_delete_thread(pthread_self());
+ GetJavaVm()->DetachCurrentThread();
+ pthread_exit(result);
+#endif
+}
+
+void PlatformThread::Join(const Thread* thread)
+{
+ if (Thread::EqualsCurrentThreadID(m_Thread))
+ {
+ ErrorStringMsg("***Thread '%s' tried to join itself!***", thread->m_Name);
+ }
+
+ if (m_Thread)
+ {
+ int error = pthread_join(m_Thread, NULL);
+ if (error)
+ ErrorString(Format("Error joining threads: %d", error));
+
+ m_Thread = 0;
+ }
+}
+
+void PlatformThread::UpdatePriority(const Thread* thread) const
+{
+#if UNITY_PEPPER || UNITY_BB10
+
+ // No thread priority in NaCl yet.
+ // For BB10 the user Unity is run as lacks permission to set priority.
+
+#else // Default POSIX impl below
+
+ ThreadPriority p = thread->m_Priority;
+
+#if UNITY_OSX || UNITY_IPHONE || UNITY_PS3
+ AssertIf(m_DefaultPriority == 0);
+#endif
+
+ struct sched_param param;
+ int policy;
+ ErrorIf(pthread_getschedparam(m_Thread, &policy, &param));
+#if UNITY_PS3
+ int min = 3071;
+ int max = 0;
+#else
+ int min = sched_get_priority_min(policy);
+ int max = sched_get_priority_max(policy);
+#endif
+
+ int iPriority;
+ switch (p)
+ {
+ case kLowPriority:
+ iPriority = min;
+ break;
+
+ case kBelowNormalPriority:
+ iPriority = min + (m_DefaultPriority-min)/2;
+ break;
+
+ case kNormalPriority:
+ iPriority = m_DefaultPriority;
+ break;
+
+ case kHighPriority:
+ iPriority = max;
+ break;
+
+ default:
+ iPriority = min;
+ AssertString("Undefined thread priority");
+ break;
+ }
+
+ if (param.sched_priority != iPriority)
+ {
+ param.sched_priority = iPriority;
+ ErrorIf(pthread_setschedparam(m_Thread, policy, &param));
+ }
+
+#endif
+}
+
+PlatformThread::ThreadID PlatformThread::GetCurrentThreadID()
+{
+ return (ThreadID)pthread_self();
+}
+
+#endif // THREAD_API_PTHREAD
diff --git a/Runtime/Threads/Posix/PlatformThread.h b/Runtime/Threads/Posix/PlatformThread.h
new file mode 100644
index 0000000..757d4d5
--- /dev/null
+++ b/Runtime/Threads/Posix/PlatformThread.h
@@ -0,0 +1,56 @@
+#ifndef PLATFORMTHREAD_H
+#define PLATFORMTHREAD_H
+
+#if SUPPORT_THREADS
+
+#include <pthread.h>
+#include "Runtime/Utilities/NonCopyable.h"
+
+class Thread;
+
+#if UNITY_PS3 // module@TODO : Move to Platforms/PS3/Include/PlatformThread.h
+#define DEFAULT_UNITY_THREAD_STACK_SIZE 128*1024
+#endif
+
+#define UNITY_THREAD_FUNCTION_RETURNTYPE void*
+#define UNITY_THREAD_FUNCTION_RETURN_SIGNATURE UNITY_THREAD_FUNCTION_RETURNTYPE
+
+class EXPORT_COREMODULE PlatformThread : public NonCopyable
+{
+ friend class Thread;
+ friend class ThreadHelper;
+
+protected:
+ typedef pthread_t ThreadID;
+
+ // Starts a thread to execute within the calling process.
+ // Typically maps to 'CreateThread' (WinAPI) or 'pthread_create' (POSIX).
+ void Create(const Thread* thread, const UInt32 stackSize, const int processor);
+ // To be called from the thread's 'start_routine' (aka RunThreadWrapper)
+ // in order to boot-strap the thread (in terms of setting the thread affinity or similar).
+ void Enter(const Thread* thread);
+ // To be called as final exit/cleanup call when a thread's 'start_routine' (aka RunThreadWrapper) exits.
+ // Depending on the backing thread API this function may not return.
+ void Exit(const Thread* thread, void* result);
+ // The function waits for the thread specified by 'thread' to terminate.
+ // Typically maps to 'WaitForSingleObject' (WinAPI) or 'pthread_join' (POSIX).
+ void Join(const Thread* thread);
+ // Uses the thread->m_Priority to update the thread scheduler settings, if possible.
+ // Typically maps to 'SetThreadPriority' (WinAPI) or 'pthread_setschedparam' (POSIX).
+ // Depending on the process permissions this call may turn into a no-op.
+ void UpdatePriority(const Thread* thread) const;
+ // Returns a unique identifier for the currently executing thread.
+ static ThreadID GetCurrentThreadID();
+
+private:
+ PlatformThread();
+ ~PlatformThread();
+
+ pthread_t m_Thread;
+ int m_DefaultPriority;
+ int m_Processor;
+};
+
+#endif // SUPPORT_THREADS
+
+#endif // PLATFORMTHREAD_H
diff --git a/Runtime/Threads/Posix/PlatformThreadSpecificValue.h b/Runtime/Threads/Posix/PlatformThreadSpecificValue.h
new file mode 100644
index 0000000..7513834
--- /dev/null
+++ b/Runtime/Threads/Posix/PlatformThreadSpecificValue.h
@@ -0,0 +1,63 @@
+#ifndef PLATFORMTHREADSPECIFICVALUE_H
+#define PLATFORMTHREADSPECIFICVALUE_H
+
+#if UNITY_DYNAMIC_TLS
+
+#include <pthread.h>
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Utilities/Utility.h"
+
+class PlatformThreadSpecificValue
+{
+public:
+ PlatformThreadSpecificValue();
+ ~PlatformThreadSpecificValue();
+
+ void* GetValue() const;
+ void SetValue(void* value);
+
+private:
+ pthread_key_t m_TLSKey;
+};
+
+inline PlatformThreadSpecificValue::PlatformThreadSpecificValue()
+{
+ int rc = pthread_key_create(&m_TLSKey, NULL);
+ DebugAssertIf(rc != 0);
+ UNUSED(rc);
+}
+
+inline PlatformThreadSpecificValue::~PlatformThreadSpecificValue()
+{
+ int rc = pthread_key_delete(m_TLSKey);
+ DebugAssertIf(rc != 0);
+ UNUSED(rc);
+}
+
+inline void* PlatformThreadSpecificValue::GetValue() const
+{
+#if !UNITY_LINUX
+ // 0 is a valid key on Linux and POSIX specifies keys as opaque objects,
+ // so technically we have no business snopping in them anyway...
+ DebugAssertIf(m_TLSKey == 0);
+#endif
+ return pthread_getspecific(m_TLSKey);
+}
+
+inline void PlatformThreadSpecificValue::SetValue(void* value)
+{
+#if !UNITY_LINUX
+ // 0 is a valid key on Linux and POSIX specifies keys as opaque objects,
+ // so technically we have no business snopping in them anyway...
+ DebugAssertIf(m_TLSKey == 0);
+#endif
+ pthread_setspecific(m_TLSKey, value);
+}
+
+#else
+
+ #error "POSIX doesn't define a static TLS path"
+
+#endif // UNITY_DYNAMIC_TLS
+
+#endif // PLATFORMTHREADSPECIFICVALUE_H
diff --git a/Runtime/Threads/ProfilerMutex.h b/Runtime/Threads/ProfilerMutex.h
new file mode 100644
index 0000000..1e82b7c
--- /dev/null
+++ b/Runtime/Threads/ProfilerMutex.h
@@ -0,0 +1,118 @@
+#ifndef __PROFILERMUTEX_H
+#define __PROFILERMUTEX_H
+
+#include "Mutex.h"
+
+#if ENABLE_PROFILER
+#include "Runtime/Profiler/Profiler.h"
+#endif
+
+#define THREAD_LOCK_WARNINGS 0
+#define THREAD_LOCK_TIMING 0
+
+#if THREAD_LOCK_WARNINGS || (ENABLE_PROFILER && SUPPORT_THREADS)
+
+#define AQUIRE_AUTOLOCK(mutex,profilerInformation) ProfilerMutexAutoLock aquireAutoLock (mutex, #mutex, profilerInformation, __FILE__, __LINE__)
+#define AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(mutex,profilerInformation) ProfilerMutexAutoLock aquireAutoLock (mutex, #mutex, Thread::mainThreadId, profilerInformation, __FILE__, __LINE__)
+#define LOCK_MUTEX(mutex,profilerInformation) ProfilerMutexLock(mutex,#mutex,profilerInformation,__FILE__,__LINE__)
+
+double GetTimeSinceStartup ();
+
+inline void ProfilerMutexLock (Mutex& mutex, char const* mutexName, ProfilerInformation& information, char const* file, int line)
+{
+#if ENABLE_PROFILER || THREAD_LOCK_WARNINGS
+
+ if (mutex.TryLock())
+ return;
+
+ #if THREAD_LOCK_WARNINGS
+ DebugStringToFile (std::string ("Mutex '") + mutexName + "' already locked: " + information.name, 0, file, line, kScriptingWarning);
+ #endif
+
+ PROFILER_AUTO_THREAD_SAFE(information, NULL)
+
+#if THREAD_LOCK_TIMING
+ double start = GetTimeSinceStartup ();
+ while (!mutex.TryLock())
+ Sleep (10);
+
+ double duration = GetTimeSinceStartup () - start;
+ DebugStringToFile (std::string ("Mutex '") + mutexName + "' obtained after: " + FloatToString(duration, "%6.3f") + " s", 0, file, line, kScriptingWarning);
+#else
+ mutex.Lock();
+#endif
+
+#else
+ mutex.Lock();
+#endif
+}
+
+inline void ProfilerMutexLock (Mutex& mutex, char const* mutexName, Thread::ThreadID threadID, ProfilerInformation& information, char const* file, int line)
+{
+#if ENABLE_PROFILER || THREAD_LOCK_WARNINGS
+ if (mutex.TryLock())
+ return;
+
+ #if THREAD_LOCK_WARNINGS
+ if (Thread::EqualsCurrentThreadID(threadID))
+ {
+ DebugStringToFile (std::string ("Mutex '") + mutexName + "' already locked: " + information.name, 0, file, line, kScriptingWarning);
+ }
+ #endif
+
+ PROFILER_AUTO_THREAD_SAFE(information, NULL)
+
+#if THREAD_LOCK_TIMING
+ double start = GetTimeSinceStartup ();
+ while (!mutex.TryLock())
+ Sleep (10);
+
+ double duration = GetTimeSinceStartup () - start;
+ DebugStringToFile (std::string ("Mutex '") + mutexName + "' obtained after: " + FloatToString(duration, "%6.3f") + " s", 0, file, line, kScriptingWarning);
+#else
+ mutex.Lock();
+#endif
+
+#else
+ mutex.Lock();
+#endif
+}
+
+class ProfilerMutexAutoLock
+{
+public:
+ ProfilerMutexAutoLock (Mutex& mutex, char const* mutexName, ProfilerInformation& profilerInformation, char const* file, int line)
+ : m_Mutex (&mutex)
+ {
+ ProfilerMutexLock(mutex, mutexName, profilerInformation, file, line);
+ }
+
+ ProfilerMutexAutoLock (Mutex& mutex, char const* mutexName, Thread::ThreadID threadID, ProfilerInformation& profilerInformation, char const* file, int line)
+ : m_Mutex (&mutex)
+ {
+ ProfilerMutexLock(mutex, mutexName, threadID, profilerInformation, file, line);
+ }
+
+ ~ProfilerMutexAutoLock()
+ {
+ m_Mutex->Unlock();
+ }
+
+private:
+ ProfilerMutexAutoLock(const ProfilerMutexAutoLock&);
+ ProfilerMutexAutoLock& operator=(const ProfilerMutexAutoLock&);
+
+private:
+ Mutex* m_Mutex;
+};
+
+#else
+
+#define AQUIRE_AUTOLOCK(mutex,profilerInformation) Mutex::AutoLock aquireAutoLock (mutex)
+#define AQUIRE_AUTOLOCK_WARN_MAIN_THREAD(mutex,profilerInformation) Mutex::AutoLock aquireAutoLock (mutex)
+#define LOCK_MUTEX(mutex,profilerInformation) (mutex).Lock ()
+
+
+#endif
+
+#endif
diff --git a/Runtime/Threads/Semaphore.h b/Runtime/Threads/Semaphore.h
new file mode 100644
index 0000000..151f64d
--- /dev/null
+++ b/Runtime/Threads/Semaphore.h
@@ -0,0 +1,67 @@
+#ifndef __SEMAPHORE_H
+#define __SEMAPHORE_H
+
+#if SUPPORT_THREADS
+
+#if UNITY_WIN || UNITY_XENON
+# include "Winapi/PlatformSemaphore.h"
+#elif UNITY_LINUX || UNITY_PEPPER || UNITY_ANDROID || UNITY_PS3 || UNITY_BB10 || UNITY_TIZEN
+# include "Posix/PlatformSemaphore.h"
+#else
+# include "PlatformSemaphore.h"
+#endif
+
+#include "Runtime/Utilities/NonCopyable.h"
+
+class Semaphore : public NonCopyable
+{
+public:
+ Semaphore() { m_Semaphore.Create(); }
+ ~Semaphore() { m_Semaphore.Destroy(); }
+ void Reset() { m_Semaphore.Destroy(); m_Semaphore.Create(); }
+ void WaitForSignal() { m_Semaphore.WaitForSignal(); }
+ void Signal() { m_Semaphore.Signal(); }
+
+private:
+ PlatformSemaphore m_Semaphore;
+};
+
+class SuspendableSemaphore : public NonCopyable
+{
+public:
+ explicit SuspendableSemaphore(bool suspended = true) : m_Suspended(suspended), m_SuspendedIndefinitely(false) { }
+ bool IsSuspended() const { return m_Suspended; }
+ void Reset() { m_Semaphore.Reset(); }
+ void WaitForSignal() { if (!m_Suspended) m_Semaphore.WaitForSignal(); };
+ void Signal() { if (!m_Suspended) m_Semaphore.Signal(); }
+ void Resume(bool reset = true);
+ void Suspend(bool indefinitely = false);
+
+private:
+ volatile bool m_SuspendedIndefinitely;
+ volatile bool m_Suspended;
+ Semaphore m_Semaphore;
+};
+
+inline void SuspendableSemaphore::Resume(bool reset)
+{
+ if (reset)
+ Reset();
+
+ if (!m_SuspendedIndefinitely)
+ m_Suspended = false;
+}
+
+inline void SuspendableSemaphore::Suspend(bool indefinitely)
+{
+ m_Suspended = true;
+ if (indefinitely)
+ m_SuspendedIndefinitely = indefinitely;
+
+ m_Semaphore.Signal(); // release any waiting thread
+}
+
+
+#endif // SUPPORT_THREADS
+
+#endif // __SEMAPHORE_H
diff --git a/Runtime/Threads/SimpleLock.h b/Runtime/Threads/SimpleLock.h
new file mode 100644
index 0000000..ace47aa
--- /dev/null
+++ b/Runtime/Threads/SimpleLock.h
@@ -0,0 +1,53 @@
+#ifndef SIMPLE_LOCK_H
+#define SIMPLE_LOCK_H
+
+#if SUPPORT_THREADS
+
+#include "AtomicOps.h"
+#include "Semaphore.h"
+
+// Simple, non-recursive mutual exclusion lock. Efficient when there is low contention.
+// First tries to attain lock using atomic ops, then waits for lock using semaphores.
+// Same idea as described here: http://preshing.com/20120226/roll-your-own-lightweight-mutex
+
+class SimpleLock : public NonCopyable
+{
+public:
+ SimpleLock() : m_Count(0) {}
+
+ class AutoLock : public NonCopyable
+ {
+ public:
+ AutoLock( SimpleLock& lock ) : m_Lock(lock)
+ {
+ m_Lock.Lock();
+ }
+
+ ~AutoLock()
+ {
+ m_Lock.Unlock();
+ }
+
+ private:
+ SimpleLock& m_Lock;
+ };
+
+ void Lock()
+ {
+ if (AtomicIncrement(&m_Count) != 1)
+ m_Semaphore.WaitForSignal();
+ }
+
+ void Unlock()
+ {
+ if (AtomicDecrement(&m_Count) != 0)
+ m_Semaphore.Signal();
+ }
+
+private:
+ volatile int m_Count;
+ Semaphore m_Semaphore;
+};
+
+#endif // SUPPORT_THREADS
+#endif
diff --git a/Runtime/Threads/SpinlockMutex.h b/Runtime/Threads/SpinlockMutex.h
new file mode 100644
index 0000000..63ca9aa
--- /dev/null
+++ b/Runtime/Threads/SpinlockMutex.h
@@ -0,0 +1,63 @@
+#ifndef __SPINLOCK_MUTEX_H
+#define __SPINLOCK_MUTEX_H
+
+#if UNITY_OSX
+
+#include <libkern/OSAtomic.h>
+
+class SpinlockMutex : public NonCopyable
+{
+public:
+
+ class AutoLock
+ {
+ public:
+ AutoLock( SpinlockMutex& mutex )
+ : m_Mutex(&mutex)
+ {
+ mutex.Lock();
+ }
+
+ ~AutoLock()
+ {
+ m_Mutex->Unlock();
+ }
+
+ private:
+ AutoLock(const AutoLock&);
+ AutoLock& operator=(const AutoLock&);
+
+ private:
+ SpinlockMutex* m_Mutex;
+ };
+
+ SpinlockMutex()
+ {
+ m_SpinLock = OS_SPINLOCK_INIT;
+ }
+
+ ~SpinlockMutex()
+ {}
+
+ void Lock()
+ {
+ OSSpinLockLock(&m_SpinLock);
+ }
+
+ void Unlock()
+ {
+ OSSpinLockUnlock(&m_SpinLock);
+ }
+
+private:
+
+ volatile OSSpinLock m_SpinLock;
+};
+
+#else
+
+typedef Mutex SpinlockMutex;
+
+#endif
+
+#endif \ No newline at end of file
diff --git a/Runtime/Threads/Thread.cpp b/Runtime/Threads/Thread.cpp
new file mode 100644
index 0000000..c9b008c
--- /dev/null
+++ b/Runtime/Threads/Thread.cpp
@@ -0,0 +1,123 @@
+#include "UnityPrefix.h"
+
+#if SUPPORT_THREADS
+#include "Thread.h"
+#include "ThreadHelper.h"
+#include "ThreadUtility.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Allocator/MemoryManager.h"
+
+#if !UNITY_EXTERNAL_TOOL
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+#endif
+
+#if SUPPORT_ERROR_EXIT
+#include "Runtime/Utilities/ErrorExit.h"
+#endif
+
+Thread::ThreadID Thread::mainThreadId = Thread::GetCurrentThreadID();
+
+UNITY_THREAD_FUNCTION_RETURN_SIGNATURE Thread::RunThreadWrapper (void* ptr)
+{
+ Thread* thread = (Thread*)ptr;
+
+ #if ENABLE_MEMORY_MANAGER
+ GetMemoryManager().ThreadInitialize();
+ #endif
+
+ thread->m_Thread.Enter(thread); // PlatformThread (Posix/Winapi/Custom)
+
+ ThreadHelper::SetThreadName(thread);
+
+ void *result = NULL;
+#if SUPPORT_ERROR_EXIT
+ ERROR_EXIT_THREAD_ENTRY();
+ result = thread->m_EntryPoint(thread->m_UserData);
+ ERROR_EXIT_THREAD_EXIT();
+#else // SUPPORT_ERROR_EXIT
+ result = thread->m_EntryPoint(thread->m_UserData);
+#endif // SUPPORT_ERROR_EXIT
+
+ // NOTE: code below will not execute if thread is terminated
+ thread->m_Running = false;
+ UnityMemoryBarrier();
+
+ #if ENABLE_MEMORY_MANAGER
+ GetMemoryManager().ThreadCleanup();
+ #endif
+
+ thread->m_Thread.Exit(thread, result); // PlatformThread (Posix/Winapi/Custom)
+
+ return reinterpret_cast<UNITY_THREAD_FUNCTION_RETURNTYPE>(result);
+}
+
+Thread::Thread ()
+: m_UserData(NULL)
+, m_EntryPoint(NULL)
+, m_Running(false)
+, m_ShouldQuit(false)
+, m_Priority(kNormalPriority)
+, m_Name(NULL)
+{
+}
+
+Thread::~Thread()
+{
+ AssertMsg(!m_Running, "***Thread '%s' is still running!***", m_Name);
+}
+
+void Thread::Run(void* (*entry_point) (void*), void* data, const UInt32 stackSize, int processor)
+{
+ Assert(!m_Running);
+
+ m_ShouldQuit = false;
+ m_UserData = data;
+ m_EntryPoint = entry_point;
+ m_Running = true;
+
+ m_Thread.Create(this, stackSize, processor);
+}
+
+void Thread::WaitForExit(bool signalQuit)
+{
+ if (m_Running && signalQuit)
+ SignalQuit();
+
+ m_Thread.Join(this); // PlatformThread (Posix/Winapi/Custom)
+
+ Assert(!m_Running && "Thread shouldn't be running anymore");
+ m_Running = false;
+}
+
+Thread::ThreadID Thread::GetCurrentThreadID()
+{
+ return PlatformThread::GetCurrentThreadID();
+}
+
+void Thread::Sleep (double time)
+{
+ ThreadHelper::Sleep(time);
+}
+
+void Thread::SetPriority(ThreadPriority prio)
+{
+ if (!m_Running || m_Priority == prio)
+ return;
+
+ m_Priority = prio;
+ m_Thread.UpdatePriority(this); // PlatformThread (Posix/Winapi/Custom)
+}
+
+void Thread::SetCurrentThreadProcessor(int processor)
+{
+ ThreadHelper::SetThreadProcessor(NULL, processor);
+}
+
+double Thread::GetThreadRunningTime(ThreadID threadId)
+{
+ return ThreadHelper::GetThreadRunningTime(threadId);
+}
+
+#endif
diff --git a/Runtime/Threads/Thread.h b/Runtime/Threads/Thread.h
new file mode 100644
index 0000000..2be0d8a
--- /dev/null
+++ b/Runtime/Threads/Thread.h
@@ -0,0 +1,112 @@
+#ifndef THREAD_H
+#define THREAD_H
+
+enum ThreadPriority { kLowPriority = 0, kBelowNormalPriority = 1, kNormalPriority = 2, kHighPriority = 4 };
+
+#if SUPPORT_THREADS
+
+#define ASSERT_RUNNING_ON_MAIN_THREAD Assert(Thread::EqualsCurrentThreadIDForAssert(Thread::mainThreadId));
+
+#if !UNITY_PLUGIN && !UNITY_EXTERNAL_TOOL
+#include "Configuration/UnityConfigure.h"
+#endif
+
+#if UNITY_WIN || UNITY_XENON
+# include "Winapi/PlatformThread.h"
+#elif UNITY_OSX || UNITY_PS3 || UNITY_IPHONE || UNITY_ANDROID || UNITY_PEPPER || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+# include "Posix/PlatformThread.h"
+#else
+# include "PlatformThread.h"
+#endif
+
+
+#ifndef DEFAULT_UNITY_THREAD_STACK_SIZE
+#define DEFAULT_UNITY_THREAD_STACK_SIZE 0
+#endif
+
+#ifndef DEFAULT_UNITY_THREAD_PROCESSOR
+#define DEFAULT_UNITY_THREAD_PROCESSOR -1
+#endif
+
+#include "Runtime/Utilities/NonCopyable.h"
+
+/**
+ * A thread.
+ */
+class EXPORT_COREMODULE Thread : public NonCopyable
+{
+ friend class ThreadHelper;
+ friend class PlatformThread;
+
+public:
+ typedef PlatformThread::ThreadID ThreadID;
+
+public:
+ Thread();
+ ~Thread();
+
+ void Run (void* (*entry_point) (void*), void* data, const UInt32 stackSize = DEFAULT_UNITY_THREAD_STACK_SIZE, int processor = DEFAULT_UNITY_THREAD_PROCESSOR);
+ bool IsRunning() const { return m_Running; }
+
+ // Was the thread told to stop running?
+ bool IsQuitSignaled () const { return m_ShouldQuit; }
+ // Tells the thread to stop running, the thread main loop is responsible for checking this variable
+ void SignalQuit () { m_ShouldQuit = true; }
+
+ // Signals quit and waits until the thread main function is exited!
+ void WaitForExit(bool signalQuit = true);
+
+ void SetName (const char* name) { m_Name = name; }
+
+ static void Sleep (double seconds);
+
+ void SetPriority (ThreadPriority prior);
+ ThreadPriority GetPriority () const { return m_Priority; }
+
+ static void SetCurrentThreadProcessor(int processor);
+
+ static ThreadID GetCurrentThreadID();
+ static bool EqualsCurrentThreadID(ThreadID thread) { return GetCurrentThreadID() == thread; }
+ static bool EqualsCurrentThreadIDForAssert(ThreadID thread) { return EqualsCurrentThreadID(thread); }
+ static bool EqualsThreadID (Thread::ThreadID lhs, Thread::ThreadID rhs) { return lhs == rhs; }
+
+ void ExternalEarlySetRunningFalse() { m_Running = false; }
+
+ static double GetThreadRunningTime(ThreadID thread);
+
+ static ThreadID mainThreadId;
+
+ static bool CurrentThreadIsMainThread() { return EqualsCurrentThreadID(mainThreadId); }
+
+private:
+ PlatformThread m_Thread;
+
+ void* m_UserData;
+ void* (*m_EntryPoint) (void*);
+
+ volatile bool m_Running;
+ volatile bool m_ShouldQuit;
+ ThreadPriority m_Priority;
+ const char* m_Name;
+
+ static UNITY_THREAD_FUNCTION_RETURN_SIGNATURE RunThreadWrapper (void* ptr);
+};
+
+
+#else // SUPPORT_THREADS
+
+
+// No threads in this platform, stub minimal functionality
+
+#define ASSERT_RUNNING_ON_MAIN_THREAD
+
+class Thread {
+public:
+ static void Sleep (double t) { }
+ static bool CurrentThreadIsMainThread() { return true; }
+};
+
+
+#endif //SUPPORT_THREADS
+
+#endif
diff --git a/Runtime/Threads/ThreadHelper.cpp b/Runtime/Threads/ThreadHelper.cpp
new file mode 100644
index 0000000..913c606
--- /dev/null
+++ b/Runtime/Threads/ThreadHelper.cpp
@@ -0,0 +1,195 @@
+#include "UnityPrefix.h"
+
+#if SUPPORT_THREADS
+#include "Thread.h"
+#include "ThreadUtility.h"
+#include "ThreadHelper.h"
+
+#if UNITY_OSX || UNITY_IPHONE
+#include <mach/mach.h>
+#include <dlfcn.h>
+#endif
+#if UNITY_PS3
+# include <sys/timer.h>
+#endif
+#if UNITY_PEPPER && defined(__native_client__)
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <sys/nacl_syscalls.h>
+#endif
+#if UNITY_ANDROID || UNITY_TIZEN
+#include <sys/prctl.h>
+#endif
+#if UNITY_WINRT
+#include "PlatformDependent/MetroPlayer/Win32Threads.h"
+#endif
+
+void ThreadHelper::Sleep(double time)
+{
+#if UNITY_WINRT
+ int milliseconds = (int)(time * 1000.0);
+ //::SleepEx(milliseconds, true); // Must be alertable so that mono runtime can request thread suspend
+
+ HANDLE const event = CreateEventExW(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
+ Assert(NULL != event);
+
+ if (NULL != event)
+ {
+ WaitForSingleObjectEx(event, milliseconds, TRUE);
+
+ CloseHandle(event);
+ }
+
+#elif UNITY_WIN || UNITY_XENON
+ int milliseconds = (int)(time * 1000.0);
+ ::SleepEx(milliseconds, true); // Must be alertable so that mono runtime can request thread suspend
+#elif UNITY_PS3
+ sys_timer_usleep((int)(time * 1000.0));
+#elif UNITY_PEPPER
+ usleep((int)(time * 1000.0));
+#elif THREAD_API_PTHREAD || (UNITY_OSX || UNITY_IPHONE || UNITY_ANDROID || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN)
+ timespec ts;
+ int seconds = FloorfToInt(time);
+ double micro = (time - seconds) * 1000000.0;
+ int nano = (int)micro * 1000;
+ ts.tv_sec = seconds;
+ ts.tv_nsec = nano;
+
+ // nanosleep takes a timespec that is an offset, not
+ // an absolute time.
+ nanosleep(&ts, 0); // note: the spleep is aborted if the thread receives a signal. It will return -1 and set errno to EINTR.
+#else
+#error Unknown OS API - not POSIX nor WinAPI
+
+#endif
+
+}
+
+void ThreadHelper::SetThreadName(const Thread* thread)
+{
+ if (!thread->m_Name)
+ return;
+
+#if (UNITY_WIN && !UNITY_WINRT)
+ const DWORD MS_VC_EXCEPTION=0x406D1388;
+
+ #pragma pack(push,8)
+ typedef struct tagTHREADNAME_INFO
+ {
+ DWORD dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to name (in user addr space).
+ DWORD dwThreadID; // Thread ID (-1=caller thread).
+ DWORD dwFlags; // Reserved for future use, must be zero.
+ } THREADNAME_INFO;
+ #pragma pack(pop)
+
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = thread->m_Name;
+ info.dwThreadID = thread->m_Thread.m_ThreadId;
+ info.dwFlags = 0;
+
+ __try
+ {
+ RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
+#elif UNITY_WINRT
+ #pragma message("todo: implement") // ?!-
+#elif UNITY_XENON
+ typedef struct tagTHREADNAME_INFO {
+ DWORD dwType; // Must be 0x1000
+ LPCSTR szName; // Pointer to name (in user address space)
+ DWORD dwThreadID; // Thread ID (-1 for caller thread)
+ DWORD dwFlags; // Reserved for future use; must be zero
+ } THREADNAME_INFO;
+
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = thread->m_Name;
+ info.dwThreadID = thread->m_Thread.m_ThreadId;
+ info.dwFlags = 0;
+
+ __try
+ {
+ RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD *)&info );
+ }
+ __except( GetExceptionCode()==0x406D1388 ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER )
+ {
+ }
+#elif UNITY_OSX
+ // pthread_setname_np is OSX 10.6 and later only
+ int (*dynamic_pthread_setname_np)(const char*);
+ *reinterpret_cast<void**>(&dynamic_pthread_setname_np) = dlsym(RTLD_DEFAULT, "pthread_setname_np");
+ if (dynamic_pthread_setname_np)
+ dynamic_pthread_setname_np(thread->m_Name);
+#elif UNITY_ANDROID || UNITY_TIZEN
+ prctl(PR_SET_NAME, (unsigned long)(thread->m_Name ? thread->m_Name : "<Unknown>"),0,0,0);
+#endif
+}
+
+void ThreadHelper::SetThreadProcessor(const Thread* thread, int processor)
+{
+ if (processor == DEFAULT_UNITY_THREAD_PROCESSOR)
+ return;
+
+#if UNITY_OSX
+ if (thread == NULL ||
+ thread->m_Thread.m_Thread == Thread::GetCurrentThreadID())
+ {
+ #define THREAD_AFFINITY_POLICY 4
+ integer_t tap = 1 + processor;
+ thread_policy_set(mach_thread_self(), THREAD_AFFINITY_POLICY, (integer_t*) &tap, 1);
+ }
+#elif UNITY_WIN
+ // We interpret 'processor' here as processor core
+// HANDLE hThread = thread == NULL ? GetCurrentThread() : thread->m_Thread.m_Thread;
+// DWORD affinity = systeminfo::GetCoreAffinityMask(processor);
+// SetThreadAffinityMask(hThread, affinity);
+#elif UNITY_XENON
+ HANDLE hThread = thread == NULL ? GetCurrentThread() : thread->m_Thread.m_Thread;
+ AssertIf(processor > 5);
+ XSetThreadProcessor(hThread, processor);
+#endif
+}
+
+double ThreadHelper::GetThreadRunningTime(Thread::ThreadID thread)
+{
+#if UNITY_OSX || UNITY_IPHONE
+ mach_port_t mach_thread = pthread_mach_thread_np(thread);
+ thread_basic_info_data_t info;
+ mach_msg_type_number_t size = sizeof(thread_basic_info_data_t)/sizeof(integer_t);
+ if (thread_info (mach_thread, THREAD_BASIC_INFO, (integer_t*)&info, &size))
+ return 0;
+
+ return (double)info.user_time.microseconds / 1000000.0f + info.user_time.seconds
+ + (double)info.system_time.microseconds / 1000000.0f + info.system_time.seconds;
+#elif (UNITY_WIN && !UNITY_WINRT) || UNITY_XENON
+ double time = 0.0;
+
+ HANDLE threadHandle = OpenThread(THREAD_QUERY_INFORMATION, FALSE, thread);
+
+ if (NULL != threadHandle)
+ {
+ FILETIME creationTime, exitTime, kernelTime, userTime;
+
+ if (GetThreadTimes(threadHandle, &creationTime, &exitTime, &kernelTime, &userTime))
+ {
+ ULARGE_INTEGER largeKernelTime = { kernelTime.dwLowDateTime, kernelTime.dwHighDateTime };
+ ULARGE_INTEGER largeUserTime = { userTime.dwLowDateTime, userTime.dwHighDateTime };
+
+ time = ((largeKernelTime.QuadPart + largeUserTime.QuadPart) / 10000000.0);
+ }
+
+ CloseHandle(threadHandle);
+ }
+ return time;
+#else
+ return 0.0;
+#endif
+}
+
+
+#endif
diff --git a/Runtime/Threads/ThreadHelper.h b/Runtime/Threads/ThreadHelper.h
new file mode 100644
index 0000000..3e21538
--- /dev/null
+++ b/Runtime/Threads/ThreadHelper.h
@@ -0,0 +1,30 @@
+#ifndef THREADHELPER_H
+#define THREADHELPER_H
+
+#if SUPPORT_THREADS
+
+#include "Thread.h"
+
+// ThreadHelper is typically implemented on a per-platform basis, as it contains OS
+// specific functionality outside regular POSIX / pthread / WinAPI threads.
+
+class ThreadHelper
+{
+ friend class Thread;
+ friend class PlatformThread;
+
+protected:
+ static void Sleep(double time);
+
+ static void SetThreadName(const Thread* thread);
+ static void SetThreadProcessor(const Thread* thread, int processor);
+
+ static double GetThreadRunningTime(Thread::ThreadID thread);
+
+private:
+ ThreadHelper();
+};
+
+#endif //SUPPORT_THREADS
+
+#endif
diff --git a/Runtime/Threads/ThreadSharedObject.h b/Runtime/Threads/ThreadSharedObject.h
new file mode 100644
index 0000000..d53239a
--- /dev/null
+++ b/Runtime/Threads/ThreadSharedObject.h
@@ -0,0 +1,30 @@
+#ifndef THREAD_SHARED_OBJECT_H
+#define THREAD_SHARED_OBJECT_H
+
+#include "Runtime/Utilities/NonCopyable.h"
+#include "Runtime/Threads/AtomicOps.h"
+
+class ThreadSharedObject : public NonCopyable
+{
+public:
+ void AddRef() const { AtomicIncrement(&m_RefCount); }
+ void Release() const { if (AtomicDecrement(&m_RefCount) == 0) delete this; }
+ void Release(MemLabelId label) const
+ {
+ if (AtomicDecrement(&m_RefCount) == 0)
+ {
+ this->~ThreadSharedObject();
+ UNITY_FREE(label,const_cast<ThreadSharedObject*>(this));
+ }
+ }
+ int GetRefCount() const { return m_RefCount; }
+
+protected:
+ ThreadSharedObject(int refs = 1) : m_RefCount(refs) {}
+ virtual ~ThreadSharedObject() {}
+
+private:
+ volatile mutable int m_RefCount;
+};
+
+#endif
diff --git a/Runtime/Threads/ThreadSpecificValue.cpp b/Runtime/Threads/ThreadSpecificValue.cpp
new file mode 100644
index 0000000..6244754
--- /dev/null
+++ b/Runtime/Threads/ThreadSpecificValue.cpp
@@ -0,0 +1,7 @@
+#include "UnityPrefix.h"
+#include "ThreadSpecificValue.h"
+
+#if UNITY_DYNAMIC_TLS
+
+
+#endif // UNITY_DYNAMIC_TLS
diff --git a/Runtime/Threads/ThreadSpecificValue.h b/Runtime/Threads/ThreadSpecificValue.h
new file mode 100644
index 0000000..cf1cba8
--- /dev/null
+++ b/Runtime/Threads/ThreadSpecificValue.h
@@ -0,0 +1,108 @@
+#ifndef THREADSPECIFICVALUE_H
+#define THREADSPECIFICVALUE_H
+
+#include "Configuration/UnityConfigure.h"
+
+#if !SUPPORT_THREADS
+ class PlatformThreadSpecificValue
+ {
+ public:
+ PlatformThreadSpecificValue();
+ ~PlatformThreadSpecificValue();
+
+ void* GetValue() const;
+ void SetValue(void* value);
+
+ private:
+ void* m_Value;
+ };
+
+ inline PlatformThreadSpecificValue::PlatformThreadSpecificValue()
+ {
+ m_Value = 0;
+ }
+
+ inline PlatformThreadSpecificValue::~PlatformThreadSpecificValue()
+ {
+ //do nothing
+ }
+
+ inline void* PlatformThreadSpecificValue::GetValue() const
+ {
+ return m_Value;
+ }
+
+ inline void PlatformThreadSpecificValue::SetValue(void* value)
+ {
+ m_Value = value;
+ }
+#elif UNITY_WIN || UNITY_XENON
+# include "Winapi/PlatformThreadSpecificValue.h"
+#elif UNITY_OSX || UNITY_IPHONE || UNITY_ANDROID || UNITY_PEPPER || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+# include "Posix/PlatformThreadSpecificValue.h"
+#else
+# include "PlatformThreadSpecificValue.h"
+#endif
+
+#if UNITY_DYNAMIC_TLS
+
+template<class T> class ThreadSpecificValue
+{
+ PlatformThreadSpecificValue m_TLSKey;
+
+public:
+ inline operator T () const
+ {
+ return (T)GetValue ();
+ }
+
+ inline T operator -> () const
+ {
+ return (T)GetValue ();
+ }
+
+ inline T operator ++ ()
+ {
+ *this = *this + 1;
+ return *this;
+ }
+
+ inline T operator -- ()
+ {
+ *this = *this - 1;
+ return *this;
+ }
+
+ inline T operator = (T value)
+ {
+ SetValue((void*)value);
+ return value;
+ }
+
+private:
+
+ void* GetValue() const;
+ void SetValue(void* value);
+};
+
+template<class T> void* ThreadSpecificValue<T>::GetValue () const
+{
+ return m_TLSKey.GetValue();
+}
+
+template <class T> void ThreadSpecificValue<T>::SetValue (void* value)
+{
+ m_TLSKey.SetValue(value);
+}
+
+#define UNITY_TLS_VALUE(type) ThreadSpecificValue<type>
+
+#else
+
+# ifndef UNITY_TLS_VALUE
+# error "A static TLS mechanism is not defined for this platform"
+# endif
+
+#endif // UNITY_DYNAMIC_TLS
+
+#endif
diff --git a/Runtime/Threads/ThreadUtility.h b/Runtime/Threads/ThreadUtility.h
new file mode 100644
index 0000000..b45a3a5
--- /dev/null
+++ b/Runtime/Threads/ThreadUtility.h
@@ -0,0 +1,91 @@
+#ifndef __THREAD_UTILITY_H
+#define __THREAD_UTILITY_H
+
+#if UNITY_OSX
+#ifndef __ppc__
+#include <libkern/OSAtomic.h>
+#endif
+#endif
+
+#if UNITY_IPHONE
+#include <libkern/OSAtomic.h>
+#endif
+
+#if UNITY_PEPPER
+inline int NoBarrier_AtomicExchange(volatile int* ptr,
+ int new_value) {
+ __asm__ __volatile__("xchgl %1,%0" // The lock prefix is implicit for xchg.
+ : "=r" (new_value)
+ : "m" (*ptr), "0" (new_value)
+ : "memory");
+ return new_value; // Now it's the previous value.
+}
+#endif
+
+// Memory barrier.
+//
+// Necessary to call this when using volatile to order writes/reads in multiple threads.
+inline void UnityMemoryBarrier()
+{
+ #if UNITY_WIN || UNITY_XENON
+ #ifdef MemoryBarrier
+ MemoryBarrier();
+ #else
+ long temp;
+ __asm xchg temp,eax;
+ #endif
+
+ #elif UNITY_OSX
+
+ OSMemoryBarrier();
+
+ #elif UNITY_PS3
+
+ __lwsync();
+
+ #elif UNITY_IPHONE
+ // No need for memory barriers on iPhone and Android - single CPU
+ OSMemoryBarrier();
+
+ #elif UNITY_PEPPER
+
+ #if defined(__x86_64__)
+
+ // 64-bit implementations of memory barrier can be simpler, because it
+ // "mfence" is guaranteed to exist.
+ __asm__ __volatile__("mfence" : : : "memory");
+
+ #else
+
+/* if (AtomicOps_Internalx86CPUFeatures.has_sse2) {
+ __asm__ __volatile__("mfence" : : : "memory");
+ } else { // mfence is faster but not present on PIII*/
+ int x = 0;
+ NoBarrier_AtomicExchange(&x, 0); // acts as a barrier on PIII
+// }
+
+ #endif
+
+ #elif UNITY_ANDROID
+
+ #elif UNITY_WII
+
+ #elif UNITY_TIZEN
+
+ __sync_synchronize();
+
+ #elif UNITY_LINUX || UNITY_BB10
+ #ifdef __arm__
+ __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" : : "r" (0) : "memory");
+ #else
+ __sync_synchronize();
+ #endif
+ #elif UNITY_FLASH || UNITY_WEBGL
+ // Flash has no threads
+ #else
+
+ #error "Unknown platform, implement memory barrier if at all possible"
+ #endif
+}
+
+#endif
diff --git a/Runtime/Threads/ThreadedStreamBuffer.cpp b/Runtime/Threads/ThreadedStreamBuffer.cpp
new file mode 100644
index 0000000..87665f4
--- /dev/null
+++ b/Runtime/Threads/ThreadedStreamBuffer.cpp
@@ -0,0 +1,437 @@
+#include "UnityPrefix.h"
+
+#if SUPPORT_THREADS
+
+#include "ThreadedStreamBuffer.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Threads/Semaphore.h"
+#include "Runtime/Threads/ThreadUtility.h"
+#include "Runtime/Threads/AtomicOps.h"
+
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+#include <ppapi/cpp/instance.h>
+#include "External/NPAPI2NaCl/Common/UnityInterfaces.h"
+#include "PlatformDependent/PepperPlugin/UnityInstance.h"
+#endif
+
+double GetTimeSinceStartup();
+
+ThreadedStreamBuffer::ThreadedStreamBuffer()
+{
+ SetDefaults();
+}
+
+ThreadedStreamBuffer::ThreadedStreamBuffer(Mode mode, size_t size)
+{
+ SetDefaults();
+ Create(mode, size);
+}
+
+ThreadedStreamBuffer::~ThreadedStreamBuffer()
+{
+ Destroy();
+}
+
+void ThreadedStreamBuffer::CreateFromMemory(Mode mode, size_t size, void *buffer)
+{
+ Assert(mode != kModeReadOnly);
+ Assert(m_Buffer == NULL);
+ m_Mode = mode;
+ BufferHeader *header = (BufferHeader*)buffer;
+ m_Reader = &header->reader;
+ m_Writer = &header->writer;
+ m_Buffer = (char*)buffer + sizeof(BufferHeader);
+ m_BufferSize = size - sizeof(BufferHeader);
+ m_Reader->Reset();
+ m_Writer->Reset();
+ m_Writer->bufferEnd = size;
+ if (m_Mode == kModeThreaded)
+ {
+ m_Mutex = new Mutex;
+ m_ReadSemaphore = new Semaphore;
+ m_WriteSemaphore = new Semaphore;
+ }
+}
+
+void ThreadedStreamBuffer::Create(Mode mode, size_t size)
+{
+ m_Reader = &m_Header.reader;
+ m_Writer = &m_Header.writer;
+ Assert(mode != kModeReadOnly);
+ Assert(m_Buffer == NULL);
+ m_Mode = mode;
+ if (size != 0)
+ m_Buffer = (char*)UNITY_MALLOC(kMemUtility, size);
+ m_BufferSize = size;
+ m_Reader->Reset();
+ m_Writer->Reset();
+ m_Writer->bufferEnd = size;
+ if (m_Mode == kModeThreaded)
+ {
+ m_Mutex = new Mutex;
+ m_ReadSemaphore = new Semaphore;
+ m_WriteSemaphore = new Semaphore;
+ }
+}
+
+void ThreadedStreamBuffer::CreateReadOnly(const void* buffer, size_t size)
+{
+ m_Reader = &m_Header.reader;
+ m_Writer = &m_Header.writer;
+ m_Mode = kModeReadOnly;
+ m_Buffer = (char*)buffer;
+ m_BufferSize = size;
+ m_Reader->Reset();
+ m_Writer->Reset();
+ m_Reader->bufferEnd = size;
+}
+
+void ThreadedStreamBuffer::ResetGrowable()
+{
+ Assert(m_Mode == kModeGrowable);
+ m_Reader->Reset();
+ m_Writer->Reset();
+ m_Writer->bufferEnd = m_BufferSize;
+}
+
+void ThreadedStreamBuffer::Destroy()
+{
+ if (m_Buffer == NULL) return;
+ if (m_Mode != kModeReadOnly)
+ UNITY_FREE(kMemUtility, m_Buffer);
+ m_Reader->Reset();
+ m_Writer->Reset();
+ if (m_Mode == kModeThreaded)
+ {
+ delete m_Mutex;
+ delete m_ReadSemaphore;
+ delete m_WriteSemaphore;
+ }
+ SetDefaults();
+}
+
+ThreadedStreamBuffer::size_t ThreadedStreamBuffer::GetCurrentSize() const
+{
+ Assert(m_Mode != kModeThreaded);
+ if (m_Mode == kModeGrowable)
+ {
+ return m_Writer->bufferPos;
+ }
+ return m_BufferSize;
+}
+
+const void* ThreadedStreamBuffer::GetBuffer() const
+{
+ //Assert(m_Mode != kModeThreaded);
+ return m_Buffer;
+}
+
+void ThreadedStreamBuffer::ReadStreamingData(void* data, size_t size, size_t alignment, size_t step)
+{
+ Assert((step % alignment) == 0);
+
+ // This should not be size_t, as the GfxDevice may run across processes of different
+ // bitness, and the data serialized in the command buffer must match.
+ size_t sz = ReadValueType<UInt32>();
+ Assert(sz == size);
+
+ char* dest = (char*)data;
+ for (size_t offset = 0; offset < size; offset += step)
+ {
+ size_t bytes = std::min(size - offset, step);
+ const void* src = GetReadDataPointer(bytes, alignment);
+ if (data)
+ UNITY_MEMCPY(dest, src, bytes);
+
+ int magic = ReadValueType<int>();
+ Assert(magic == 1234);
+
+ ReadReleaseData();
+ dest += step;
+ }
+}
+
+ThreadedStreamBuffer::size_t ThreadedStreamBuffer::ReadStreamingData(DataConsumer consumer, void* userData, size_t alignment, size_t step)
+{
+ Assert((step % alignment) == 0);
+ Assert(consumer != NULL);
+
+ size_t totalBytesRead = 0;
+
+ bool moreData = false;
+ do
+ {
+ const void* src = GetReadDataPointer(step + sizeof(size_t), alignment);
+ const size_t bytesInBuffer = *(static_cast<const size_t*>(src));
+ consumer(static_cast<const size_t*>(src) + 1, bytesInBuffer, userData);
+ totalBytesRead += bytesInBuffer;
+ int magic = ReadValueType<int>();
+ Assert(magic == 1234);
+ moreData = ReadValueType<bool>();
+ ReadReleaseData();
+ } while (moreData);
+
+ return totalBytesRead;
+}
+
+void ThreadedStreamBuffer::ReadReleaseData()
+{
+ UnityMemoryBarrier();
+ if (m_Reader->checkedWraps == m_Reader->bufferWraps)
+ {
+ // We only update the position
+ m_Reader->checkedPos = m_Reader->bufferPos;
+ }
+ else
+ {
+ // Both values need to be set atomically
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ Mutex::AutoLock lock(*m_Mutex);
+#endif
+ m_Reader->checkedPos = m_Reader->bufferPos;
+ m_Reader->checkedWraps = m_Reader->bufferWraps;
+ }
+ UnityMemoryBarrier();
+ SendReadSignal();
+}
+
+void ThreadedStreamBuffer::WriteStreamingData(const void* data, size_t size, size_t alignment, size_t step)
+{
+ // This should not be size_t, as the GfxDevice may run across processes of different
+ // bitness, and the data serialized in the command buffer must match.
+ WriteValueType<UInt32>(size);
+ Assert((step % alignment) == 0);
+
+ const char* src = (const char*)data;
+ for (size_t offset = 0; offset < size; offset += step)
+ {
+ size_t bytes = std::min(size - offset, step);
+ void* dest = GetWriteDataPointer(bytes, alignment);
+ UNITY_MEMCPY(dest, src, bytes);
+ WriteValueType<int>(1234);
+
+ // In the NaCl Web Player, make sure that only complete commands are submitted, as we are not truely
+ // asynchronous.
+ #if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ WriteSubmitData();
+ #endif
+
+ src += step;
+ }
+ WriteSubmitData();
+}
+
+void ThreadedStreamBuffer::WriteStreamingData(DataProvider provider, void* userData, size_t alignment, size_t step)
+{
+ Assert((step % alignment) == 0);
+ Assert(provider != NULL);
+
+ bool moreData = false;
+ do
+ {
+ void* dest = GetWriteDataPointer(step + sizeof(size_t), alignment);
+ size_t outBytesWritten = 0;
+ moreData = provider(static_cast<size_t*>(dest) + 1, step, outBytesWritten, userData);
+ *((size_t*)dest) = outBytesWritten;
+ WriteValueType<int>(1234);
+ WriteValueType(moreData);
+
+ // In the NaCl Web Player, make sure that only complete commands are submitted, as we are not truely
+ // asynchronous.
+ #if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ WriteSubmitData();
+ #endif
+ } while (moreData);
+
+ WriteSubmitData();
+}
+
+void ThreadedStreamBuffer::WriteSubmitData()
+{
+ UnityMemoryBarrier();
+ if (m_Writer->checkedWraps == m_Writer->bufferWraps)
+ {
+ // We only update the position
+ m_Writer->checkedPos = m_Writer->bufferPos;
+ }
+ else
+ {
+ // Both values need to be set atomically
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ Mutex::AutoLock lock(*m_Mutex);
+#endif
+ m_Writer->checkedPos = m_Writer->bufferPos;
+ m_Writer->checkedWraps = m_Writer->bufferWraps;
+ }
+ UnityMemoryBarrier();
+ SendWriteSignal();
+}
+
+void ThreadedStreamBuffer::SetDefaults()
+{
+ m_Mode = kModeReadOnly;
+ m_Buffer = NULL;
+ m_BufferSize = 0;
+ m_GrowStepSize = 8*1024;
+ m_Mutex = NULL;
+ m_ReadSemaphore = NULL;
+ m_WriteSemaphore = NULL;
+ m_NeedsReadSignal = 0;
+ m_NeedsWriteSignal = 0;
+ m_ReadWaitTime = 0.0;
+ m_WriteWaitTime = 0.0;
+};
+
+bool ThreadedStreamBuffer::HasDataToRead() const
+{
+ if (m_Reader->bufferWraps == m_Writer->checkedWraps)
+ {
+ return (m_Reader->bufferPos < m_Writer->checkedPos) || (m_Reader->bufferPos < m_Reader->bufferEnd);
+ }
+ else
+ return true;
+}
+
+void ThreadedStreamBuffer::HandleReadOverflow(size_t& dataPos, size_t& dataEnd)
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ Assert(m_Mode == kModeThreaded);
+ Assert(m_Mutex != NULL);
+ Mutex::AutoLock lock(*m_Mutex);
+#endif
+
+ if (dataEnd > m_BufferSize)
+ {
+ dataEnd -= dataPos;
+ dataPos = 0;
+ m_Reader->bufferPos = 0;
+ m_Reader->bufferWraps++;
+ }
+
+ for (;;)
+ {
+ // Get how many buffer lengths writer is ahead of reader
+ // This may be -1 if we are waiting for the writer to wrap
+ size_t comparedPos = m_Writer->checkedPos;
+ size_t comparedWraps = m_Writer->checkedWraps;
+ size_t wrapDist = comparedWraps - m_Reader->bufferWraps;
+ m_Reader->bufferEnd = (wrapDist == 0) ? comparedPos : (wrapDist == 1) ? m_BufferSize : 0;
+
+ if (dataEnd <= m_Reader->bufferEnd)
+ {
+ break;
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ AtomicIncrement(&m_NeedsWriteSignal);
+ UnityMemoryBarrier();
+
+ m_Mutex->Unlock();
+ if (comparedPos != m_Writer->checkedPos || comparedWraps != m_Writer->checkedWraps)
+ {
+ // Writer position changed while we requested a signal
+ // Request might be missed, so we signal ourselves to avoid deadlock
+ SendWriteSignal();
+ }
+ SendReadSignal();
+ // Wait for writer thread
+ double startTime = GetTimeSinceStartup();
+ m_WriteSemaphore->WaitForSignal();
+ m_ReadWaitTime += GetTimeSinceStartup() - startTime;
+ m_Mutex->Lock();
+#endif
+ }
+}
+
+void ThreadedStreamBuffer::HandleWriteOverflow(size_t& dataPos, size_t& dataEnd)
+{
+ if (m_Mode == kModeGrowable)
+ {
+ size_t dataSize = dataEnd - dataPos;
+ size_t growSize = std::max(dataSize, m_GrowStepSize);
+ m_BufferSize += growSize;
+ m_Buffer = (char*)UNITY_REALLOC_(kMemUtility, m_Buffer, m_BufferSize);
+ m_Writer->bufferEnd = m_BufferSize;
+ return;
+ }
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ Assert(m_Mode == kModeThreaded);
+ Assert(m_Mutex != NULL);
+
+ Mutex::AutoLock lock(*m_Mutex);
+#endif
+
+ if (dataEnd > m_BufferSize)
+ {
+ dataEnd -= dataPos;
+ dataPos = 0;
+ m_Writer->bufferPos = 0;
+ m_Writer->bufferWraps++;
+ }
+
+ for (;;)
+ {
+ UnityMemoryBarrier();
+
+ // Get how many buffer lengths writer is ahead of reader
+ // This may be 2 if we are waiting for the reader to wrap
+ size_t comparedPos = m_Reader->checkedPos;
+ size_t comparedWraps = m_Reader->checkedWraps;
+ size_t wrapDist = m_Writer->bufferWraps - comparedWraps;
+ m_Writer->bufferEnd = (wrapDist == 0) ? m_BufferSize : (wrapDist == 1) ? comparedPos : 0;
+
+ if (dataEnd <= m_Writer->bufferEnd)
+ {
+ break;
+ }
+#if ENABLE_GFXDEVICE_REMOTE_PROCESS_CLIENT
+ struct UNITY_GfxDevice_1_0 *gfxInterface = (UNITY_GfxDevice_1_0*)pp::Module::Get()->GetBrowserInterface(UNITY_GFXDEVICE_INTERFACE_1_0);
+ gfxInterface->WaitForSignal(0);
+#elif !ENABLE_GFXDEVICE_REMOTE_PROCESS
+ AtomicIncrement(&m_NeedsReadSignal);
+ UnityMemoryBarrier();
+
+ m_Mutex->Unlock();
+ if (comparedPos != m_Reader->checkedPos || comparedWraps != m_Reader->checkedWraps)
+ {
+ // Reader position changed while we requested a signal
+ // Request might be missed, so we signal ourselves to avoid deadlock
+ SendReadSignal();
+ }
+ SendWriteSignal();
+ // Wait for reader thread
+ double startTime = GetTimeSinceStartup();
+ m_ReadSemaphore->WaitForSignal();
+ m_WriteWaitTime += GetTimeSinceStartup() - startTime;
+ m_Mutex->Lock();
+#endif
+ }
+}
+
+void ThreadedStreamBuffer::SendReadSignal()
+{
+ if (AtomicCompareExchange(&m_NeedsReadSignal, 0, 1) )
+ {
+ m_ReadSemaphore->Signal();
+ }
+}
+
+void ThreadedStreamBuffer::SendWriteSignal()
+{
+ if (AtomicCompareExchange(&m_NeedsWriteSignal, 0, 1) )
+ {
+ m_WriteSemaphore->Signal();
+ }
+}
+void ThreadedStreamBuffer::BufferState::Reset() volatile
+{
+ bufferPos = 0;
+ bufferEnd = 0;
+ bufferWraps = 0;
+ checkedPos = 0;
+ checkedWraps = 0;
+#if !UNITY_RELEASE
+ totalBytes = 0;
+#endif
+}
+
+#endif
diff --git a/Runtime/Threads/ThreadedStreamBuffer.h b/Runtime/Threads/ThreadedStreamBuffer.h
new file mode 100644
index 0000000..5c213ce
--- /dev/null
+++ b/Runtime/Threads/ThreadedStreamBuffer.h
@@ -0,0 +1,250 @@
+#ifndef THREADED_STREAM_BUFFER_H
+#define THREADED_STREAM_BUFFER_H
+
+#if SUPPORT_THREADS
+
+#include "Runtime/Utilities/NonCopyable.h"
+#include "Runtime/Utilities/Utility.h"
+#include <new> // for placement new
+#include "Runtime/Threads/Thread.h"
+
+class Mutex;
+class Semaphore;
+
+/// A single producer, single consumer ringbuffer
+
+/// Most read and write operations are done even without atomic operations and use no expensive synchronization primitives
+/// Each thread owns a part of the buffer and only locks when reaching the end
+
+
+/// *** Common usage ***
+/// * Create the ring buffer
+/// ThreadedStreamBuffer buffer (kModeThreaded);
+
+/// * Producer thread...
+/// buffer.WriteValueType<int>(5);
+/// buffer.WriteValueType<int>(7);
+/// buffer.WriteSubmitData();
+
+/// * ConsumerThread...
+/// print(buffer.ReadValueType<int>());
+/// print(buffer.ReadValueType<int>());
+/// buffer.ReadReleaseData();
+
+class ThreadedStreamBuffer : public NonCopyable
+{
+public:
+
+ struct BufferState
+ {
+ // These should not be size_t, as the GfxDevice may run across processes of different
+ // bitness, and the data serialized in the command buffer must match.
+ void Reset() volatile;
+ volatile UInt32 bufferPos;
+ volatile UInt32 bufferEnd;
+ volatile UInt32 bufferWraps;
+ volatile UInt32 checkedPos;
+ volatile UInt32 checkedWraps;
+#if !UNITY_RELEASE
+ volatile UInt32 totalBytes;
+#endif
+ };
+
+ struct BufferHeader
+ {
+ BufferState reader;
+ BufferState writer;
+ };
+
+ typedef unsigned size_t;
+
+ enum Mode
+ {
+ // This is the most common usage. One producer, one consumer on different threads.
+ kModeThreaded,
+
+ // When in read mode, we pass a pointer to the external data which can then be read using ReadValueType and ReadReleaseData.
+ kModeReadOnly,
+ // When in growable you are only allowed to write into the ring buffer. Essentially like a std::vector. It will keep on growing as you write data.
+ kModeGrowable,
+ kModeCrossProcess,
+ };
+
+ ThreadedStreamBuffer(Mode mode, size_t size);
+ ThreadedStreamBuffer();
+ ~ThreadedStreamBuffer();
+
+ enum
+ {
+ kDefaultAlignment = 4,
+ kDefaultStep = 4096
+ };
+
+ // Read data from the ringbuffer
+ // This function blocks until data new data has arrived in the ringbuffer.
+ // It uses semaphores to wait on the producer thread in a efficient way.
+ template <class T> const T& ReadValueType();
+ template <class T> T* ReadArrayType(int count);
+ // ReadReleaseData should be called when the data has been read & used completely.
+ // At this point the memory will become available to the producer to write into it again.
+ void ReadReleaseData();
+
+ // Write data into the ringbuffer
+ template <class T> void WriteValueType(const T& val);
+ template <class T> void WriteArrayType(const T* vals, int count);
+ template <class T> T* GetWritePointer();
+ // WriteSubmitData should be called after data has been completely written and should be made available to the consumer thread to read it.
+ // Before WriteSubmitData is called, any data written with WriteValueType can not be read by the consumer.
+ void WriteSubmitData();
+
+ // Ringbuffer Streaming support. This will automatically call WriteSubmitData & ReadReleaseData.
+ // It splits the data into smaller chunks (step). So that the size of the ringbuffer can be smaller than the data size passed into this function.
+ // The consumer thread will be reading the streaming data while WriteStreamingData is still called on the producer thread.
+ void ReadStreamingData(void* data, size_t size, size_t alignment = kDefaultAlignment, size_t step = kDefaultStep);
+ void WriteStreamingData(const void* data, size_t size, size_t alignment = kDefaultAlignment, size_t step = kDefaultStep);
+
+
+ // Utility functions
+ void* GetReadDataPointer(size_t size, size_t alignment);
+ void* GetWriteDataPointer(size_t size, size_t alignment);
+
+ size_t GetDebugReadPosition() const { return m_Reader->bufferPos; }
+ size_t GetDebugWritePosition() const { return m_Writer->bufferPos; }
+
+ double GetReadWaitTime() const { return m_ReadWaitTime; }
+ double GetWriteWaitTime() const { return m_WriteWaitTime; }
+ void ResetReadWaitTime() { m_ReadWaitTime = 0.0; }
+ void ResetWriteWaitTime() { m_WriteWaitTime = 0.0; }
+
+
+ // Creation methods
+ void Create(Mode mode, size_t size);
+ void CreateReadOnly(const void* buffer, size_t size);
+ void CreateFromMemory(Mode mode, size_t size, void *buffer);
+ void ResetGrowable();
+ void Destroy();
+
+ //
+ // Is there data available to be read
+ // typicall this is not used
+ bool HasData() const;
+ bool HasDataToRead() const;
+
+ size_t GetAllocatedSize() const { return m_BufferSize; }
+ size_t GetCurrentSize() const;
+ const void* GetBuffer() const;
+
+
+ ////@TODO: Remove this
+ typedef void (*DataConsumer)(const void* buffer, size_t bufferSize, void* userData);
+ size_t ReadStreamingData(DataConsumer consumer, void* userData, size_t alignment = kDefaultAlignment, size_t step = kDefaultStep);
+
+ typedef bool (*DataProvider)(void* dest, size_t bufferSize, size_t& bytesWritten, void* userData);
+ void WriteStreamingData(DataProvider provider, void* userData, size_t alignment = kDefaultAlignment, size_t step = kDefaultStep);
+
+
+private:
+ FORCE_INLINE size_t Align(size_t pos, size_t alignment) const { return (pos+alignment-1)&~(alignment-1); }
+
+ void SetDefaults();
+
+ void HandleReadOverflow(size_t& dataPos, size_t& dataEnd);
+ void HandleWriteOverflow(size_t& dataPos, size_t& dataEnd);
+
+ void SendReadSignal();
+ void SendWriteSignal();
+
+ Mode m_Mode;
+ char* m_Buffer;
+ size_t m_BufferSize;
+ size_t m_GrowStepSize;
+ BufferState *m_Reader;
+ BufferState *m_Writer;
+ BufferHeader m_Header;
+ Mutex* m_Mutex;
+ Semaphore* m_ReadSemaphore;
+ Semaphore* m_WriteSemaphore;
+ volatile int m_NeedsReadSignal;
+ volatile int m_NeedsWriteSignal;
+ double m_ReadWaitTime;
+ double m_WriteWaitTime;
+};
+
+FORCE_INLINE bool ThreadedStreamBuffer::HasData() const
+{
+ return (m_Reader->bufferPos != m_Writer->checkedPos);
+}
+
+FORCE_INLINE void* ThreadedStreamBuffer::GetReadDataPointer(size_t size, size_t alignment)
+{
+ size = Align(size, alignment);
+ size_t dataPos = Align(m_Reader->bufferPos, alignment);
+ size_t dataEnd = dataPos + size;
+ if (dataEnd > m_Reader->bufferEnd)
+ {
+ HandleReadOverflow(dataPos, dataEnd);
+ }
+ m_Reader->bufferPos = dataEnd;
+#if !UNITY_RELEASE
+ m_Reader->totalBytes += size;
+#endif
+ return &m_Buffer[dataPos];
+}
+
+FORCE_INLINE void* ThreadedStreamBuffer::GetWriteDataPointer(size_t size, size_t alignment)
+{
+ size = Align(size, alignment);
+ Assert(size*2 <= m_BufferSize || m_Mode == kModeGrowable);
+ size_t dataPos = Align(m_Writer->bufferPos, alignment);
+ size_t dataEnd = dataPos + size;
+ if (dataEnd > m_Writer->bufferEnd)
+ {
+ HandleWriteOverflow(dataPos, dataEnd);
+ }
+ m_Writer->bufferPos = dataEnd;
+#if !UNITY_RELEASE
+ m_Writer->totalBytes += size;
+#endif
+ return &m_Buffer[dataPos];
+}
+
+template <class T> FORCE_INLINE const T& ThreadedStreamBuffer::ReadValueType()
+{
+ // Read simple data type from queue
+ const void* pdata = GetReadDataPointer(sizeof(T), ALIGN_OF(T));
+ const T& src = *reinterpret_cast<const T*>(pdata);
+ return src;
+}
+
+template <class T> FORCE_INLINE T* ThreadedStreamBuffer::ReadArrayType(int count)
+{
+ // Read array of data from queue-
+ void* pdata = GetReadDataPointer(count * sizeof(T), ALIGN_OF(T));
+ T* src = reinterpret_cast<T*>(pdata);
+ return src;
+}
+
+template <class T> FORCE_INLINE void ThreadedStreamBuffer::WriteValueType(const T& val)
+{
+ // Write simple data type to queue
+ void* pdata = GetWriteDataPointer(sizeof(T), ALIGN_OF(T));
+ new (pdata) T(val);
+}
+
+template <class T> FORCE_INLINE void ThreadedStreamBuffer::WriteArrayType(const T* vals, int count)
+{
+ // Write array of data to queue
+ T* pdata = (T*)GetWriteDataPointer(count * sizeof(T), ALIGN_OF(T));
+ for (int i = 0; i < count; i++)
+ new (&pdata[i]) T(vals[i]);
+}
+
+template <class T> FORCE_INLINE T* ThreadedStreamBuffer::GetWritePointer()
+{
+ // Write simple data type to queue
+ void* pdata = GetWriteDataPointer(sizeof(T), ALIGN_OF(T));
+ return static_cast<T*>(pdata);
+}
+
+#endif
+#endif
diff --git a/Runtime/Threads/Winapi/PlatformEvent.h b/Runtime/Threads/Winapi/PlatformEvent.h
new file mode 100644
index 0000000..1aa590d
--- /dev/null
+++ b/Runtime/Threads/Winapi/PlatformEvent.h
@@ -0,0 +1,70 @@
+#ifndef __PLATFORMEVENT_H
+#define __PLATFORMEVENT_H
+
+// Event synchronization object.
+
+#if SUPPORT_THREADS
+
+#include "Runtime/Utilities/NonCopyable.h"
+
+class Event : public NonCopyable
+{
+public:
+ explicit Event();
+ ~Event();
+
+ void WaitForSignal();
+ void Signal();
+
+private:
+ HANDLE m_Event;
+};
+
+inline Event::Event()
+{
+#if UNITY_WINRT
+ m_Event = CreateEventExW(nullptr, nullptr, 0, 0); // ?!-
+#else
+ m_Event = CreateEvent(NULL, FALSE, FALSE, NULL);
+#endif
+}
+
+inline Event::~Event()
+{
+ if (m_Event != NULL)
+ CloseHandle(m_Event);
+}
+
+inline void Event::WaitForSignal()
+{
+#if UNITY_WINRT
+ WaitForSingleObjectEx(m_Event, INFINITE, FALSE); // ?!-
+#else
+ while (1)
+ {
+ DWORD result = WaitForSingleObjectEx(m_Event, INFINITE, TRUE);
+ switch (result)
+ {
+ case WAIT_OBJECT_0:
+ // We got the event
+ return;
+ case WAIT_IO_COMPLETION:
+ // Allow thread to run IO completion task
+ Sleep(1);
+ break;
+ default:
+ Assert (false);
+ break;
+ }
+ }
+#endif
+}
+
+inline void Event::Signal()
+{
+ SetEvent(m_Event);
+}
+
+#endif // SUPPORT_THREADS
+
+#endif // __PLATFORMEVENT_H
diff --git a/Runtime/Threads/Winapi/PlatformMutex.cpp b/Runtime/Threads/Winapi/PlatformMutex.cpp
new file mode 100644
index 0000000..3f2bd63
--- /dev/null
+++ b/Runtime/Threads/Winapi/PlatformMutex.cpp
@@ -0,0 +1,54 @@
+#include "UnityPrefix.h"
+
+#if SUPPORT_THREADS
+
+#ifndef MUTEX_API_WINAPI
+#define MUTEX_API_WINAPI (UNITY_WIN || UNITY_XENON || UNITY_WINRT)
+#endif
+
+#endif // SUPPORT_THREADS
+
+#if MUTEX_API_WINAPI
+
+#include "PlatformMutex.h"
+
+// -------------------------------------------------------------------------------------------------
+// windows
+
+// Note: TryEnterCriticalSection only exists on NT-derived systems.
+// But we do not run on Win9x currently anyway, so just accept it.
+#if !defined _WIN32_WINNT || _WIN32_WINNT < 0x0400
+extern "C" WINBASEAPI BOOL WINAPI TryEnterCriticalSection( IN OUT LPCRITICAL_SECTION lpCriticalSection );
+#endif
+
+PlatformMutex::PlatformMutex()
+{
+#if UNITY_WINRT
+ BOOL const result = InitializeCriticalSectionEx(&crit_sec, 0, CRITICAL_SECTION_NO_DEBUG_INFO);
+ Assert(FALSE != result);
+#else
+ InitializeCriticalSection( &crit_sec );
+#endif
+}
+
+PlatformMutex::~PlatformMutex ()
+{
+ DeleteCriticalSection( &crit_sec );
+}
+
+void PlatformMutex::Lock()
+{
+ EnterCriticalSection( &crit_sec );
+}
+
+void PlatformMutex::Unlock()
+{
+ LeaveCriticalSection( &crit_sec );
+}
+
+bool PlatformMutex::TryLock()
+{
+ return TryEnterCriticalSection( &crit_sec ) ? true : false;
+}
+
+#endif // MUTEX_API_WINAPI
diff --git a/Runtime/Threads/Winapi/PlatformMutex.h b/Runtime/Threads/Winapi/PlatformMutex.h
new file mode 100644
index 0000000..11f174e
--- /dev/null
+++ b/Runtime/Threads/Winapi/PlatformMutex.h
@@ -0,0 +1,28 @@
+#ifndef __PLATFORMMUTEX_H
+#define __PLATFORMMUTEX_H
+
+#if SUPPORT_THREADS
+
+#include "Runtime/Utilities/NonCopyable.h"
+
+/**
+ * A platform/api specific mutex class. Always recursive (a single thread can lock multiple times).
+ */
+class PlatformMutex : public NonCopyable
+{
+ friend class Mutex;
+protected:
+ PlatformMutex();
+ ~PlatformMutex();
+
+ void Lock();
+ void Unlock();
+ bool TryLock();
+
+private:
+
+ CRITICAL_SECTION crit_sec;
+};
+
+#endif // SUPPORT_THREADS
+#endif // __PLATFORMMUTEX_H
diff --git a/Runtime/Threads/Winapi/PlatformSemaphore.h b/Runtime/Threads/Winapi/PlatformSemaphore.h
new file mode 100644
index 0000000..0f00f20
--- /dev/null
+++ b/Runtime/Threads/Winapi/PlatformSemaphore.h
@@ -0,0 +1,59 @@
+#ifndef __PLATFORMSEMAPHORE_H
+#define __PLATFORMSEMAPHORE_H
+
+#if SUPPORT_THREADS
+
+#include "Runtime/Utilities/NonCopyable.h"
+
+class PlatformSemaphore : public NonCopyable
+{
+ friend class Semaphore;
+protected:
+ void Create();
+ void Destroy();
+
+ void WaitForSignal();
+ void Signal();
+
+private:
+ HANDLE m_Semaphore;
+};
+
+ inline void PlatformSemaphore::Create()
+ {
+#if UNITY_WINRT
+ m_Semaphore = CreateSemaphoreExW(NULL, 0, 256, NULL, 0, (STANDARD_RIGHTS_REQUIRED | SEMAPHORE_MODIFY_STATE | SYNCHRONIZE));
+#else
+ m_Semaphore = CreateSemaphoreA( NULL, 0, 256, NULL );
+#endif
+ }
+ inline void PlatformSemaphore::Destroy(){ if( m_Semaphore ) CloseHandle(m_Semaphore); }
+ inline void PlatformSemaphore::WaitForSignal()
+ {
+#if UNITY_WINRT // ?!-
+ WaitForSingleObjectEx(m_Semaphore, INFINITE, FALSE);
+#else
+ while (1)
+ {
+ DWORD result = WaitForSingleObjectEx( m_Semaphore, INFINITE, TRUE );
+ switch (result)
+ {
+ case WAIT_OBJECT_0:
+ // We got the signal
+ return;
+ case WAIT_IO_COMPLETION:
+ // Allow thread to run IO completion task
+ Sleep(1);
+ break;
+ default:
+ Assert(false);
+ break;
+ }
+ }
+#endif
+ }
+ inline void PlatformSemaphore::Signal() { ReleaseSemaphore( m_Semaphore, 1, NULL ); }
+
+#endif // SUPPORT_THREADS
+
+#endif // __PLATFORMSEMAPHORE_H
diff --git a/Runtime/Threads/Winapi/PlatformThread.cpp b/Runtime/Threads/Winapi/PlatformThread.cpp
new file mode 100644
index 0000000..c0dd578
--- /dev/null
+++ b/Runtime/Threads/Winapi/PlatformThread.cpp
@@ -0,0 +1,140 @@
+#include "UnityPrefix.h"
+
+#if SUPPORT_THREADS
+
+#ifndef THREAD_API_WINAPI
+#define THREAD_API_WINAPI (UNITY_WIN || UNITY_XENON || UNITY_WINRT)
+#endif
+
+#endif // SUPPORT_THREADS
+
+#if THREAD_API_WINAPI
+
+#include "PlatformThread.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/ThreadHelper.h"
+
+#include "Runtime/Utilities/Word.h"
+
+// module@TODO : Move this to PlatformThread.h
+#if UNITY_WINRT
+#include "PlatformDependent/MetroPlayer/Win32Threads.h"
+#endif
+
+PlatformThread::PlatformThread()
+: m_Thread(NULL)
+#if !UNITY_WINRT
+, m_ThreadId(0)
+#endif
+{
+}
+
+PlatformThread::~PlatformThread()
+{
+ AssertMsg(m_Thread == NULL, "***Thread was not cleaned up!***");
+}
+
+
+void PlatformThread::Create(const Thread* thread, const UInt32 stackSize, const int processor)
+{
+#if UNITY_WINRT
+ m_Thread = win32::CreateThread(Thread::RunThreadWrapper, (LPVOID) thread);
+#else // UNITY_WINRT
+ DWORD creationFlags = 0;
+#if UNITY_XENON
+ if (processor != DEFAULT_UNITY_THREAD_PROCESSOR)
+ creationFlags = CREATE_SUSPENDED;
+#endif
+
+ m_Thread = ::CreateThread(NULL, stackSize, Thread::RunThreadWrapper, (LPVOID) thread, creationFlags, &m_ThreadId);
+ Assert(NULL != m_Thread);
+
+#if UNITY_XENON
+ if (processor != DEFAULT_UNITY_THREAD_PROCESSOR)
+ {
+ ThreadHelper::SetThreadProcessor(thread, processor);
+ ResumeThread(m_Thread);
+ }
+#endif
+
+#endif // UNITY_WINRT
+
+}
+
+void PlatformThread::Enter(const Thread* thread)
+{
+ if (thread->m_Priority != kNormalPriority)
+ UpdatePriority(thread);
+}
+
+void PlatformThread::Exit(const Thread* thread, void* result)
+{
+}
+
+void PlatformThread::Join(const Thread* thread)
+{
+#if !UNITY_WINRT // Why doesn't WINRT store the thread ID ?
+ if (Thread::EqualsCurrentThreadID(m_ThreadId))
+ {
+ ErrorStringMsg("***Thread '%s' tried to join itself!***", thread->m_Name);
+ }
+#endif
+
+ if (thread->m_Running)
+ {
+ DWORD waitResult = WaitForSingleObjectEx(m_Thread, INFINITE, FALSE);
+ Assert(WAIT_OBJECT_0 == waitResult);
+ }
+
+ if (m_Thread != NULL)
+ {
+ BOOL closeResult = CloseHandle(m_Thread);
+ Assert(FALSE != closeResult);
+ }
+ m_Thread = NULL;
+}
+
+void PlatformThread::UpdatePriority(const Thread* thread) const
+{
+ ThreadPriority p = thread->m_Priority;
+
+#if UNITY_WINRT
+
+ #pragma message("todo: implement") // ?!-
+
+#else
+
+ int iPriority;
+ switch (p)
+ {
+ case kLowPriority:
+ iPriority = THREAD_PRIORITY_LOWEST;
+ break;
+
+ case kBelowNormalPriority:
+ iPriority = THREAD_PRIORITY_BELOW_NORMAL;
+ break;
+
+ case kNormalPriority:
+ iPriority = THREAD_PRIORITY_NORMAL;
+ break;
+ case kHighPriority:
+ iPriority = THREAD_PRIORITY_HIGHEST;
+ break;
+
+ default:
+ AssertString("Undefined thread priority");
+ }
+
+ int res = SetThreadPriority(m_Thread, iPriority);
+ AssertIf(res == 0);
+
+#endif
+}
+
+PlatformThread::ThreadID PlatformThread::GetCurrentThreadID()
+{
+ return GetCurrentThreadId();
+}
+
+#endif // THREAD_API_PTHREAD
diff --git a/Runtime/Threads/Winapi/PlatformThread.h b/Runtime/Threads/Winapi/PlatformThread.h
new file mode 100644
index 0000000..289e39e
--- /dev/null
+++ b/Runtime/Threads/Winapi/PlatformThread.h
@@ -0,0 +1,67 @@
+#ifndef PLATFORMTHREAD_H
+#define PLATFORMTHREAD_H
+
+#if SUPPORT_THREADS
+
+#include "Runtime/Utilities/NonCopyable.h"
+
+class Thread;
+
+#if UNITY_XENON // module@TODO : Move to Platforms/Xenon/Include/PlatformThread.h
+#define DEFAULT_UNITY_THREAD_STACK_SIZE 128*1024
+#endif
+
+// The function signature is actually important here,
+// and on Windows it must match the signature of LPTHREAD_START_ROUTINE,
+// and no other way will be "ok".
+// It does not suffice to cast to the more "general" version, because
+// then you will run into ESP check failures sooner or later. Probably sooner.
+#define UNITY_THREAD_FUNCTION_RETURNTYPE DWORD
+#define UNITY_THREAD_FUNCTION_RETURN_SIGNATURE UNITY_THREAD_FUNCTION_RETURNTYPE WINAPI
+
+class EXPORT_COREMODULE PlatformThread : public NonCopyable
+{
+ friend class Thread;
+ friend class ThreadHelper;
+
+protected:
+ typedef DWORD ThreadID;
+
+ // Starts a thread to execute within the calling process.
+ // Typically maps to 'CreateThread' (WinAPI) or 'pthread_create' (POSIX).
+ void Create(const Thread* thread, const UInt32 stackSize, const int processor);
+ // To be called from the thread's 'start_routine' (aka RunThreadWrapper)
+ // in order to boot-strap the thread (in terms of setting the thread affinity or similar).
+ void Enter(const Thread* thread);
+ // To be called as final exit/cleanup call when a thread's 'start_routine' (aka RunThreadWrapper) exits.
+ // Depending on the backing thread API this function may not return.
+ void Exit(const Thread* thread, void* result);
+ // The function waits for the thread specified by 'thread' to terminate.
+ // Typically maps to 'WaitForSingleObject' (WinAPI) or 'pthread_join' (POSIX).
+ void Join(const Thread* thread);
+ // Uses the thread->m_Priority to update the thread scheduler settings, if possible.
+ // Typically maps to 'SetThreadPriority' (WinAPI) or 'pthread_setschedparam' (POSIX).
+ // Depending on the process permissions this call may turn into a no-op.
+ void UpdatePriority(const Thread* thread) const;
+ // Returns a unique identifier for the currently executing thread.
+ static ThreadID GetCurrentThreadID();
+
+private:
+ PlatformThread();
+ ~PlatformThread();
+
+#if UNITY_WINRT
+
+ HANDLE m_Thread;
+
+#elif UNITY_WIN || UNITY_XENON
+
+ HANDLE m_Thread;
+ DWORD m_ThreadId;
+
+#endif
+};
+
+#endif // SUPPORT_THREADS
+
+#endif // PLATFORMTHREAD_H
diff --git a/Runtime/Threads/Winapi/PlatformThreadSpecificValue.h b/Runtime/Threads/Winapi/PlatformThreadSpecificValue.h
new file mode 100644
index 0000000..8e6d42a
--- /dev/null
+++ b/Runtime/Threads/Winapi/PlatformThreadSpecificValue.h
@@ -0,0 +1,61 @@
+#ifndef PLATFORMTHREADSPECIFICVALUE_H
+#define PLATFORMTHREADSPECIFICVALUE_H
+
+#if UNITY_DYNAMIC_TLS
+
+#if UNITY_WINRT
+#include "PlatformDependent/MetroPlayer/Win32Threads.h"
+using win32::TlsAlloc;
+using win32::TlsFree;
+using win32::TlsGetValue;
+using win32::TlsSetValue;
+#endif
+
+class PlatformThreadSpecificValue
+{
+public:
+ PlatformThreadSpecificValue();
+ ~PlatformThreadSpecificValue();
+
+ void* GetValue() const;
+ void SetValue(void* value);
+
+private:
+ DWORD m_TLSKey;
+};
+
+inline PlatformThreadSpecificValue::PlatformThreadSpecificValue ()
+{
+ m_TLSKey = TlsAlloc();
+ AssertIf( m_TLSKey == TLS_OUT_OF_INDEXES );
+}
+
+inline PlatformThreadSpecificValue::~PlatformThreadSpecificValue ()
+{
+ TlsFree( m_TLSKey );
+}
+
+inline void* PlatformThreadSpecificValue::GetValue () const
+{
+#if UNITY_WIN
+ void* result = TlsGetValue(m_TLSKey);
+ DebugAssertIf( result == NULL && GetLastError() != ERROR_SUCCESS );
+ return result;
+#elif UNITY_XENON
+ return TlsGetValue(m_TLSKey); // on XENON TlsGetValue does not call SetLastError
+#endif
+}
+
+inline void PlatformThreadSpecificValue::SetValue (void* value)
+{
+ BOOL ok = TlsSetValue( m_TLSKey, value );
+ DebugAssertIf( !ok );
+}
+
+#else
+
+ #define UNITY_TLS_VALUE(type) __declspec(thread) type
+
+#endif // UNITY_DYNAMIC_TLS
+
+#endif // PLATFORMTHREADSPECIFICVALUE_H
diff --git a/Runtime/Utilities/Annotations.h b/Runtime/Utilities/Annotations.h
new file mode 100644
index 0000000..39f7e5a
--- /dev/null
+++ b/Runtime/Utilities/Annotations.h
@@ -0,0 +1,52 @@
+#ifndef ANNOTATIONS_H
+#define ANNOTATIONS_H
+
+// Annotations for various stuff.
+// At the moment only understood by Visual Studio, and expand to nothing elsewhere.
+
+#if UNITY_WIN
+#pragma warning(disable:6255) // _alloca
+#pragma warning(disable:6211) // leaking due to exception
+#define INPUT_NOTNULL _In_
+#define INPUT_OPTIONAL _In_opt_
+#define OUTPUT_NOTNULL _Out_
+#define OUTPUT_OPTIONAL _Out_opt_
+#define INOUT_NOTNULL _Inout_
+#define INOUT_OPTIONAL _Inout_opt_
+#define RETVAL_NOTNULL _Ret_
+#define RETVAL_OPTIONAL _Ret_opt_
+#define DOES_NOT_RETURN __declspec(noreturn)
+#define ANALYSIS_ASSUME(x) { __analysis_assume(x); }
+#define TAKES_PRINTF_ARGS(n,m)
+
+#elif defined(__GNUC__)
+
+#define INPUT_NOTNULL
+#define INPUT_OPTIONAL
+#define OUTPUT_NOTNULL
+#define OUTPUT_OPTIONAL
+#define INOUT_NOTNULL
+#define INOUT_OPTIONAL
+#define RETVAL_NOTNULL
+#define RETVAL_OPTIONAL
+#define DOES_NOT_RETURN __attribute__((noreturn))
+#define ANALYSIS_ASSUME(x)
+#define TAKES_PRINTF_ARGS(m,n) __attribute__((format(printf,m,n)))
+
+#else
+
+#define INPUT_NOTNULL
+#define INPUT_OPTIONAL
+#define OUTPUT_NOTNULL
+#define OUTPUT_OPTIONAL
+#define INOUT_NOTNULL
+#define INOUT_OPTIONAL
+#define RETVAL_NOTNULL
+#define RETVAL_OPTIONAL
+#define DOES_NOT_RETURN
+#define ANALYSIS_ASSUME(x)
+#define TAKES_PRINTF_ARGS(n,m)
+
+#endif
+
+#endif
diff --git a/Runtime/Utilities/Argv.cpp b/Runtime/Utilities/Argv.cpp
new file mode 100644
index 0000000..b05da23
--- /dev/null
+++ b/Runtime/Utilities/Argv.cpp
@@ -0,0 +1,129 @@
+#include "UnityPrefix.h"
+#include "Argv.h"
+#if UNITY_WIN
+#include "PlatformDependent/Win/WinUtils.h"
+#endif
+
+using namespace std;
+
+static int argc;
+static const char** argv;
+static vector<string> relaunchArguments;
+
+struct KnownArguments
+{
+ bool isBatchmode;
+ bool isAutomated;
+};
+
+static KnownArguments knownArgs;
+
+void SetupArgv (int a, const char** b)
+{
+ argc = a;
+ argv = b;
+ knownArgs.isBatchmode = HasARGV ("batchmode");
+ knownArgs.isAutomated = HasARGV ("automated");
+}
+
+bool HasARGV (const string& name)
+{
+ for (int i=0;i<argc;i++)
+ {
+ if (StrICmp (argv[i], "-" + name) == 0)
+ return true;
+ }
+ return false;
+}
+
+bool IsBatchmode ()
+{
+ return knownArgs.isBatchmode;
+}
+
+bool IsHumanControllingUs ()
+{
+ return !(knownArgs.isBatchmode || knownArgs.isAutomated);
+}
+
+void SetIsBatchmode (bool value)
+{
+ knownArgs.isBatchmode = true;
+}
+
+void PrintARGV ()
+{
+ for (int i=0;i<argc;i++)
+ {
+ printf_console ("%s\n", argv[i]);
+ }
+}
+
+vector<string> GetValuesForARGV (const string& name)
+{
+ vector<string> values;
+ values.reserve (argc);
+
+ bool found = false;
+ for (int i=0;i<argc;i++)
+ {
+ if (found)
+ {
+ if (argv[i][0] == '-')
+ return values;
+ else
+ values.push_back (argv[i]);
+ }
+ else if (StrICmp (argv[i], "-" + name) == 0)
+ found = true;
+ }
+
+ return values;
+}
+
+string GetFirstValueForARGV (const string& name)
+{
+ vector<string> values = GetValuesForARGV (name);
+ if (values.empty ())
+ return string ();
+ else
+ return values[0];
+}
+
+vector<string> GetAllArguments()
+{
+ vector<string> values;
+ values.reserve (argc);
+ for (int i=1;i<argc;i++)
+ values.push_back (argv[i]);
+ return values;
+}
+
+void SetRelaunchApplicationArguments (const vector<string>& args)
+{
+ relaunchArguments = args;
+}
+
+vector<string> GetRelaunchApplicationArguments ()
+{
+ return relaunchArguments;
+}
+
+void CheckBatchModeErrorString (const string& error)
+{
+ if (error.empty ())
+ return;
+
+ ErrorString(error);
+
+ if (!IsBatchmode())
+ return;
+
+#if UNITY_WIN && UNITY_EDITOR
+ winutils::RedirectStdoutToConsole();
+#elif UNITY_OSX
+ ResetStdout();
+#endif
+ printf_console ("\nAborting batchmode due to failure:\n%s\n\n", error.c_str());
+ exit(1);
+}
diff --git a/Runtime/Utilities/Argv.h b/Runtime/Utilities/Argv.h
new file mode 100644
index 0000000..835b4bc
--- /dev/null
+++ b/Runtime/Utilities/Argv.h
@@ -0,0 +1,30 @@
+#ifndef ARGV_H
+#define ARGV_H
+
+void SetupArgv (int argc, const char** argv);
+
+const char **GetArgv();
+int GetArgc();
+
+/// Returns true if the commandline contains a -name.
+bool HasARGV (const std::string& name);
+
+bool IsBatchmode ();
+bool IsHumanControllingUs ();
+
+void SetIsBatchmode (bool value);
+
+/// Returns a list of values for the argument
+std::vector<std::string> GetValuesForARGV (const std::string& name);
+
+std::vector<std::string> GetAllArguments();
+std::string GetFirstValueForARGV (const std::string& name);
+
+void SetRelaunchApplicationArguments (const std::vector<std::string>& args);
+std::vector<std::string> GetRelaunchApplicationArguments ();
+
+void PrintARGV ();
+
+void CheckBatchModeErrorString (const std::string& error);
+
+#endif
diff --git a/Runtime/Utilities/ArrayUtility.h b/Runtime/Utilities/ArrayUtility.h
new file mode 100644
index 0000000..c618296
--- /dev/null
+++ b/Runtime/Utilities/ArrayUtility.h
@@ -0,0 +1,9 @@
+#ifndef ARRAY_UTILITY_H
+#define ARRAY_UTILITY_H
+
+
+// Element count of a static array
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
+
+#endif
diff --git a/Runtime/Utilities/BitSetSerialization.h b/Runtime/Utilities/BitSetSerialization.h
new file mode 100644
index 0000000..1ac2eac
--- /dev/null
+++ b/Runtime/Utilities/BitSetSerialization.h
@@ -0,0 +1,78 @@
+#include "dynamic_bitset.h"
+#include "Runtime/Serialize/TypeTree.h"
+#include "Runtime/Serialize/SwapEndianArray.h"
+
+template<>
+class SerializeTraits<dynamic_bitset>: public SerializeTraitsBase<dynamic_bitset>
+{
+ public:
+
+ typedef dynamic_bitset value_type;
+
+ inline static const char* GetTypeString (value_type*) { return "bitset"; }
+ inline static bool IsAnimationChannel () { return false; }
+ inline static bool MightContainPPtr () { return false; }
+ inline static bool AllowTransferOptimization () { return false; }
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ SInt32 bitCount = data.size ();
+ transfer.Transfer (bitCount, "bitCount");
+
+ unsigned byteSize = data.num_blocks () * sizeof (value_type::block_type);
+ transfer.TransferTypeless (&byteSize, "bitblocks");
+ AssertIf (sizeof (value_type::block_type) != 4);
+ AssertIf (byteSize % 4 != 0);
+
+ if (transfer.IsReading ())
+ {
+ data.resize (bitCount);
+ transfer.TransferTypelessData (byteSize, data.m_bits);
+ if (transfer.ConvertEndianess ())
+ SwapEndianArray (data.m_bits, sizeof (value_type::block_type), byteSize / 4);
+ data.m_zero_unused_bits ();
+ }
+ else if (transfer.IsWriting ())
+ {
+ value_type::block_type* writeData = data.m_bits;
+ if (transfer.ConvertEndianess ())
+ {
+ writeData = (value_type::block_type*)UNITY_MALLOC (kMemTempAlloc, byteSize);
+ memcpy (writeData, data.m_bits, byteSize);
+ SwapEndianArray (writeData, sizeof (value_type::block_type), byteSize / 4);
+ }
+
+ AssertIf (data.num_blocks () != byteSize / 4);
+ transfer.TransferTypelessData (byteSize, writeData);
+
+ if (transfer.ConvertEndianess ())
+ UNITY_FREE (kMemTempAlloc, writeData);
+ }
+ else
+ transfer.TransferTypelessData (byteSize, NULL);
+ }
+ // Deque<bool> converter
+ template<class TransferFunction>
+ static bool Convert (value_type& data, TransferFunction& transfer)
+ {
+ const TypeTree& oldTypeTree = transfer.GetActiveOldTypeTree ();
+ const std::string& oldType = transfer.GetActiveOldTypeTree ().m_Type;
+ if ((oldType == "vector" || oldType == "deque") && GetElementTypeFromContainer (oldTypeTree).m_Type == "bool")
+ {
+ std::deque<bool> dequeBool;
+ transfer.TransferSTLStyleArray (dequeBool);
+ data.resize (dequeBool.size ());
+
+ std::deque<bool>::iterator d = dequeBool.begin ();
+ for (int i=0;i<data.size ();i++)
+ {
+ data[i] = *d;
+ d++;
+ }
+ return true;
+ }
+ else
+ return false;
+ }
+};
diff --git a/Runtime/Utilities/BitUtility.h b/Runtime/Utilities/BitUtility.h
new file mode 100644
index 0000000..774d6e0
--- /dev/null
+++ b/Runtime/Utilities/BitUtility.h
@@ -0,0 +1,189 @@
+#ifndef BITUTILITY_H
+#define BITUTILITY_H
+
+#include <limits.h>
+
+// index of the most significant bit in the mask
+
+const char gHighestBitLut[] = {-1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3};
+
+inline int HighestBit(UInt32 mask)
+{
+#ifdef SN_TARGET_PS3
+ return 31 - __cntlzw(mask);
+#else
+ int base = 0;
+
+ if ( mask & 0xffff0000 )
+ {
+ base = 16;
+ mask >>= 16;
+ }
+ if ( mask & 0x0000ff00 )
+ {
+ base += 8;
+ mask >>= 8;
+ }
+ if ( mask & 0x000000f0 )
+ {
+ base += 4;
+ mask >>= 4;
+ }
+
+ return base + gHighestBitLut[ mask ];
+#endif
+}
+
+
+// index of the least significant bit in the mask
+
+const char gLowestBitLut[] = {-1,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0};
+inline int LowestBit(UInt32 mask)
+{
+ AssertIf (mask == 0);
+
+ int base = 0;
+
+ if ( !(mask & 0xffff) )
+ {
+ base = 16;
+ mask >>= 16;
+ }
+ if ( !(mask & 0x00ff) )
+ {
+ base += 8;
+ mask >>= 8;
+ }
+ if ( !(mask & 0x000f) )
+ {
+ base += 4;
+ mask >>= 4;
+ }
+
+ return base + gLowestBitLut[ mask & 15 ];
+}
+
+// can be optimized later
+inline int AnyBitFromMask (UInt32 mask)
+{
+ return HighestBit (mask);
+}
+
+// index of the first consecutiveBitCount enabled bits
+// -1 if not available
+inline int LowestBitConsecutive (UInt32 bitMask, int consecutiveBitCount)
+{
+ UInt32 tempBitMask = bitMask;
+ int i;
+ for (i=1;i<consecutiveBitCount;i++)
+ tempBitMask &= bitMask >> i;
+
+ if (!tempBitMask)
+ return -1;
+ else
+ return LowestBit (tempBitMask);
+}
+/*int LowestBitConsecutive ( u_int value, u_int consecutiveBitCount )
+{
+ u_int mask = (1 << consecutiveBitCount) - 1;
+ u_int notValue = value ^ 0xffffffff;
+ u_int workingMask = mask;
+ u_int prevMask = 0;
+ int match = notValue & workingMask;
+ u_int shift = 1;
+ while ( (match != 0) && (prevMask < workingMask) )
+ {
+ shift = 2*u_int(match & -match);
+ prevMask = workingMask;
+ workingMask = mask * shift;
+ match = notValue & workingMask;
+ }
+ if ( prevMask < workingMask )
+ {
+ return LowestBit( shift );
+ }
+ else
+ {
+ return -1;
+ }
+}*/
+
+// number of set bits in the 32 bit mask
+inline int BitsInMask (UInt32 v)
+{
+ // From http://www-graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
+ // This variant about 30% faster on 360 than what was here before.
+ v = v - ((v >> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
+ return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
+}
+
+// number of set bits in the 64 bit mask
+inline int BitsInMask64 (UInt64 v)
+{
+ // From http://www-graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
+ v = v - ((v >> 1) & (UInt64)~(UInt64)0/3);
+ v = (v & (UInt64)~(UInt64)0/15*3) + ((v >> 2) & (UInt64)~(UInt64)0/15*3);
+ v = (v + (v >> 4)) & (UInt64)~(UInt64)0/255*15;
+ return (UInt64)(v * ((UInt64)~(UInt64)0/255)) >> (sizeof(UInt64) - 1) * CHAR_BIT;
+}
+
+// reverse bit order
+inline void ReverseBits(UInt32& mask)
+{
+ mask = ((mask >> 1) & 0x55555555) | ((mask << 1) & 0xaaaaaaaa);
+ mask = ((mask >> 2) & 0x33333333) | ((mask << 2) & 0xcccccccc);
+ mask = ((mask >> 4) & 0x0f0f0f0f) | ((mask << 4) & 0xf0f0f0f0);
+ mask = ((mask >> 8) & 0x00ff00ff) | ((mask << 8) & 0xff00ff00);
+ mask = ((mask >> 16) & 0x0000ffff) | ((mask << 16) & 0xffff0000) ;
+}
+
+// is value a power-of-two
+inline bool IsPowerOfTwo(UInt32 mask)
+{
+ return (mask & (mask-1)) == 0;
+}
+
+// return the next power-of-two of a 32bit number
+inline UInt32 NextPowerOfTwo(UInt32 v)
+{
+ v -= 1;
+ v |= v >> 16;
+ v |= v >> 8;
+ v |= v >> 4;
+ v |= v >> 2;
+ v |= v >> 1;
+ return v + 1;
+}
+
+// return the closest power-of-two of a 32bit number
+inline UInt32 ClosestPowerOfTwo(UInt32 v)
+{
+ UInt32 nextPower = NextPowerOfTwo (v);
+ UInt32 prevPower = nextPower >> 1;
+ if (v - prevPower < nextPower - v)
+ return prevPower;
+ else
+ return nextPower;
+}
+
+inline UInt32 ToggleBit (UInt32 bitfield, int index)
+{
+ AssertIf (index < 0 || index >= 32);
+ return bitfield ^ (1 << index);
+}
+
+// Template argument must be a power of 2
+template<int n>
+struct StaticLog2
+{
+ static const int value = StaticLog2<n/2>::value + 1;
+};
+
+template<>
+struct StaticLog2<1>
+{
+ static const int value = 0;
+};
+
+#endif
diff --git a/Runtime/Utilities/CStringHash.h b/Runtime/Utilities/CStringHash.h
new file mode 100644
index 0000000..a4b58ef
--- /dev/null
+++ b/Runtime/Utilities/CStringHash.h
@@ -0,0 +1,52 @@
+#ifndef CSTRINGHASH_H
+#define CSTRINGHASH_H
+#include <functional>
+
+struct hash_cstring : std::unary_function<const char*, std::size_t>
+{
+ unsigned operator ()(const char* key) const
+ {
+ unsigned h = 0;
+ const unsigned sr = 8 * sizeof (unsigned) - 8;
+ const unsigned mask = 0xF << (sr + 4);
+ while (*key != '\0')
+ {
+ h = (h << 4) + *key;
+ std::size_t g = h & mask;
+ h ^= g | (g >> sr);
+ key++;
+ }
+ return h;
+ }
+};
+
+struct equal_cstring : std::binary_function<char*, char*, std::size_t>
+{
+ bool operator () (char* lhs, char* rhs) const
+ {
+ while (*lhs != '\0')
+ {
+ if (*lhs != *rhs)
+ return false;
+ lhs++; rhs++;
+ }
+ return *lhs == *rhs;
+ }
+};
+
+struct smaller_cstring : std::binary_function<const char*, const char*, std::size_t>
+{
+ bool operator () (const char* lhs, const char* rhs) const { return strcmp (lhs, rhs) < 0; }
+};
+
+struct compare_cstring : public std::binary_function<const char*, const char*, bool>
+{
+ bool operator ()(const char* lhs, const char* rhs) const { return strcmp (lhs, rhs) < 0; }
+};
+
+struct compare_string_insensitive : public std::binary_function<const std::string, const std::string, bool>
+{
+ bool operator ()(const std::string& lhs, const std::string& rhs) const { return StrICmp (lhs, rhs) < 0; }
+};
+
+#endif
diff --git a/Runtime/Utilities/CopyPaste.h b/Runtime/Utilities/CopyPaste.h
new file mode 100644
index 0000000..8c9e299
--- /dev/null
+++ b/Runtime/Utilities/CopyPaste.h
@@ -0,0 +1,10 @@
+#ifndef COPYPASTE_H
+#define COPYPASTE_H
+
+/// Get the system-wide copy buffer for pasting into a textfield
+std::string GetCopyBuffer ();
+
+/// Set the system-wide copybuffer for pasting from a textfield
+void SetCopyBuffer (const std::string &utf8string);
+
+#endif
diff --git a/Runtime/Utilities/DateTime.cpp b/Runtime/Utilities/DateTime.cpp
new file mode 100644
index 0000000..b95e06c
--- /dev/null
+++ b/Runtime/Utilities/DateTime.cpp
@@ -0,0 +1,45 @@
+#include "UnityPrefix.h"
+#include "DateTime.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+
+
+DateTime::DateTime ()
+{
+ highSeconds = 0;
+ lowSeconds = 0;
+ fraction = 0;
+}
+
+bool operator < (const DateTime& d0, const DateTime& d1)
+{
+ if (d0.highSeconds < d1.highSeconds)
+ return true;
+ else if (d0.highSeconds > d1.highSeconds)
+ return false;
+
+ if (d0.lowSeconds < d1.lowSeconds)
+ return true;
+ else if (d0.lowSeconds > d1.lowSeconds)
+ return false;
+
+ return d0.fraction < d1.fraction;
+}
+
+bool operator == (const DateTime& d0, const DateTime& d1)
+{
+ if (d0.highSeconds != d1.highSeconds)
+ return false;
+
+ if (d0.lowSeconds != d1.lowSeconds)
+ return false;
+
+ return d0.fraction == d1.fraction;
+}
+
+
+void ByteSwapDateTime (DateTime& dateTime)
+{
+ SwapEndianBytes(dateTime.highSeconds);
+ SwapEndianBytes(dateTime.fraction);
+ SwapEndianBytes(dateTime.lowSeconds);
+}
diff --git a/Runtime/Utilities/DateTime.h b/Runtime/Utilities/DateTime.h
new file mode 100644
index 0000000..b747628
--- /dev/null
+++ b/Runtime/Utilities/DateTime.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "Runtime/Serialize/SerializeUtility.h"
+
+struct DateTime
+{
+ // We are wasting memory and serialization
+ // in assetdatabase is wrong because of the alignment!
+ // We do this change in 1.5 because it will force a rebuild of all assets
+ UInt16 highSeconds;
+ UInt16 fraction;
+ UInt32 lowSeconds;
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (DateTime)
+
+ DateTime ();
+
+ friend bool operator < (const DateTime& d0, const DateTime& d1);
+ friend bool operator == (const DateTime& d0, const DateTime& d1);
+ friend bool operator != (const DateTime& d0, const DateTime& d1) { return !(d0 == d1); }
+};
+
+template<class TransferFunction>
+void DateTime::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (highSeconds);
+ TRANSFER (fraction);
+ TRANSFER (lowSeconds);
+}
+
+void ByteSwapDateTime (DateTime& dateTime);
diff --git a/Runtime/Utilities/EditorPrefsTests.cpp b/Runtime/Utilities/EditorPrefsTests.cpp
new file mode 100644
index 0000000..3d066f0
--- /dev/null
+++ b/Runtime/Utilities/EditorPrefsTests.cpp
@@ -0,0 +1,160 @@
+#include "UnityPrefix.h"
+#include "Runtime/Utilities/PlayerPrefs.h"
+
+#if UNITY_EDITOR && ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+#if UNITY_OSX
+ // Defined in EditorPrefsTests.mm
+ void InitNSAutoreleasePool();
+ void ReleaseNSAutoreleasePool();
+
+ #define INIT_TEST InitNSAutoreleasePool()
+ #define RELEASE_TEST ReleaseNSAutoreleasePool()
+#else
+ #define INIT_TEST
+ #define RELEASE_TEST
+#endif
+
+// We test PlayerPrefs in Integration tests
+// EditorPrefs are not exposed on C# side, so we need to tests them in C++
+
+SUITE (EditorPrefsTests)
+{
+ TEST(TestEditorPrefs)
+ {
+ INIT_TEST;
+
+ const std::string
+ kBoolKeyName = "someBool",
+ kIntKeyName = "someInt",
+ kFloatKeyName = "someFloat",
+ kStringKeyName = "someString",
+ kNonExistingKeyName = "someName";
+ const std::string
+ kBoolKeyNameCaps = "someBOOL",
+ kIntKeyNameCaps = "someINT",
+ kFloatKeyNameCaps = "someFLOAT",
+ kStringKeyNameCaps = "someSTRING";
+ const bool kStoredBool = true, kStoredBoolCaps = false, kDefaultBool = false;
+ const int kStoredInt = 3, kStoredIntCaps = 4, kDefaultInt = 7;
+ const float kStoredFloat = 5.7f, kStoredFloatCaps = 6.23f, kDefaultFloat = 7.17f;
+ const std::string kStoredString = "This is the string", kStoredStringCaps = "CAPs string", kDefaultString = "Did not find meh";
+
+ // Making sure there are no permanently stored keys
+ EditorPrefs::DeleteKey(kBoolKeyName);
+ EditorPrefs::DeleteKey(kIntKeyName);
+ EditorPrefs::DeleteKey(kFloatKeyName);
+ EditorPrefs::DeleteKey(kStringKeyName);
+
+ EditorPrefs::DeleteKey(kBoolKeyNameCaps);
+ EditorPrefs::DeleteKey(kIntKeyNameCaps);
+ EditorPrefs::DeleteKey(kFloatKeyNameCaps);
+ EditorPrefs::DeleteKey(kStringKeyNameCaps);
+
+ CHECK(EditorPrefs::SetBool(kBoolKeyName, kStoredBool));
+ CHECK(EditorPrefs::SetInt(kIntKeyName, kStoredInt));
+ CHECK(EditorPrefs::SetFloat(kFloatKeyName, kStoredFloat));
+ CHECK(EditorPrefs::SetString(kStringKeyName, kStoredString));
+
+ CHECK_EQUAL(EditorPrefs::GetBool(kBoolKeyName, kDefaultBool), kStoredBool);
+ CHECK_EQUAL(EditorPrefs::GetInt(kBoolKeyName, kDefaultInt), 1); // SetBool fallsback to SetInt, so this call suceeds getting value of kBoolKeyName
+ CHECK_EQUAL(EditorPrefs::GetFloat(kBoolKeyName, kDefaultFloat), kDefaultFloat);
+ CHECK_EQUAL(EditorPrefs::GetString(kBoolKeyName, kDefaultString), kDefaultString);
+
+ CHECK_EQUAL(EditorPrefs::GetBool(kIntKeyName, kDefaultBool), true); // GetBool fallsback to GetInt, so this call succeeds getting value of kIntKeyName
+ CHECK_EQUAL(EditorPrefs::GetInt(kIntKeyName, kDefaultInt), kStoredInt);
+ CHECK_EQUAL(EditorPrefs::GetFloat(kIntKeyName, kDefaultFloat), kDefaultFloat);
+ CHECK_EQUAL(EditorPrefs::GetString (kIntKeyName, kDefaultString), kDefaultString);
+
+ CHECK_EQUAL(EditorPrefs::GetBool(kFloatKeyName, kDefaultBool), kDefaultBool);
+ CHECK_EQUAL(EditorPrefs::GetInt (kFloatKeyName, kDefaultInt), kDefaultInt);
+ CHECK_EQUAL(EditorPrefs::GetFloat (kFloatKeyName, kDefaultFloat), kStoredFloat);
+ CHECK_EQUAL(EditorPrefs::GetString (kFloatKeyName, kDefaultString), kDefaultString);
+
+ CHECK_EQUAL(EditorPrefs::GetBool(kStringKeyName, kDefaultBool), kDefaultBool);
+ CHECK_EQUAL(EditorPrefs::GetInt (kStringKeyName, kDefaultInt), kDefaultInt);
+ CHECK_EQUAL(EditorPrefs::GetFloat (kStringKeyName, kDefaultFloat), kDefaultFloat);
+ CHECK_EQUAL(EditorPrefs::GetString (kStringKeyName, kDefaultString), kStoredString);
+
+ CHECK_EQUAL(EditorPrefs::GetBool(kNonExistingKeyName, kDefaultBool), kDefaultBool);
+ CHECK_EQUAL(EditorPrefs::GetInt (kNonExistingKeyName, kDefaultInt), kDefaultInt);
+ CHECK_EQUAL(EditorPrefs::GetFloat (kNonExistingKeyName, kDefaultFloat), kDefaultFloat);
+ CHECK_EQUAL(EditorPrefs::GetString (kNonExistingKeyName, kDefaultString), kDefaultString);
+
+ CHECK(EditorPrefs::HasKey(kBoolKeyName));
+ CHECK(EditorPrefs::HasKey(kIntKeyName));
+ CHECK(EditorPrefs::HasKey(kFloatKeyName));
+ CHECK(EditorPrefs::HasKey(kStringKeyName));
+ CHECK(!EditorPrefs::HasKey(kNonExistingKeyName));
+
+ // Case sensitivity tests
+ CHECK_EQUAL(EditorPrefs::GetBool(kBoolKeyNameCaps, kDefaultBool), kDefaultBool);
+ CHECK_EQUAL(EditorPrefs::GetInt(kIntKeyNameCaps, kDefaultInt), kDefaultInt);
+ CHECK_EQUAL(EditorPrefs::GetFloat(kFloatKeyNameCaps, kDefaultFloat), kDefaultFloat);
+ CHECK_EQUAL(EditorPrefs::GetString(kStringKeyNameCaps, kDefaultString), kDefaultString);
+
+ CHECK(!EditorPrefs::HasKey(kBoolKeyNameCaps));
+ CHECK(!EditorPrefs::HasKey(kIntKeyNameCaps));
+ CHECK(!EditorPrefs::HasKey(kFloatKeyNameCaps));
+ CHECK(!EditorPrefs::HasKey(kStringKeyNameCaps));
+
+ CHECK(EditorPrefs::SetBool(kBoolKeyNameCaps, kStoredBoolCaps));
+ CHECK(EditorPrefs::SetInt(kIntKeyNameCaps, kStoredIntCaps));
+ CHECK(EditorPrefs::SetFloat(kFloatKeyNameCaps, kStoredFloatCaps));
+ CHECK(EditorPrefs::SetString(kStringKeyNameCaps, kStoredStringCaps));
+
+ CHECK_EQUAL(EditorPrefs::GetBool(kBoolKeyName, kDefaultBool), kStoredBool);
+ CHECK_EQUAL(EditorPrefs::GetInt(kIntKeyName, kDefaultInt), kStoredInt);
+ CHECK_EQUAL(EditorPrefs::GetFloat(kFloatKeyName, kDefaultFloat), kStoredFloat);
+ CHECK_EQUAL(EditorPrefs::GetString(kStringKeyName, kDefaultString), kStoredString);
+
+ CHECK_EQUAL(EditorPrefs::GetBool(kBoolKeyNameCaps, kDefaultBool), kStoredBoolCaps);
+ CHECK_EQUAL(EditorPrefs::GetInt(kIntKeyNameCaps, kDefaultInt), kStoredIntCaps);
+ CHECK_EQUAL(EditorPrefs::GetFloat(kFloatKeyNameCaps, kDefaultFloat), kStoredFloatCaps);
+ CHECK_EQUAL(EditorPrefs::GetString(kStringKeyNameCaps, kDefaultString), kStoredStringCaps);
+
+ RELEASE_TEST;
+ }
+
+
+ // Set value using one type, then set with another type; Check values for both types
+ #define TEST_OVERRIDE(TypeName1, TypeName2, Expected1, Expected2) \
+ EditorPrefs::DeleteKey(kKeyName); \
+ CHECK(EditorPrefs::Set##TypeName1(kKeyName, kStored##TypeName1)); \
+ CHECK(EditorPrefs::Set##TypeName2(kKeyName, kStored##TypeName2)); \
+ CHECK_EQUAL(EditorPrefs::Get##TypeName1(kKeyName, kDefault##TypeName1), Expected1); \
+ CHECK_EQUAL(EditorPrefs::Get##TypeName2(kKeyName, kDefault##TypeName2), Expected2); \
+
+ TEST(EditorPrefsOverriding)
+ {
+ INIT_TEST;
+
+ const std::string kKeyName = "MyKey";
+ const bool kStoredBool = true, kDefaultBool = false;
+ const int kStoredInt = 3, kDefaultInt = 7;
+ const float kStoredFloat = 5.7f, kDefaultFloat = 7.17f;
+ const std::string kStoredString = "This is the string", kDefaultString = "Did not find meh";
+
+ TEST_OVERRIDE(Bool, Int, true, kStoredInt); // ints and bool are stored the same, so value is shared
+ TEST_OVERRIDE(Bool, Float, kDefaultBool, kStoredFloat);
+ TEST_OVERRIDE(Bool, String, kDefaultBool, kStoredString);
+
+ TEST_OVERRIDE(Int, Bool, 1, true); // ints and bool are stored the same, so value is shared
+ TEST_OVERRIDE(Int, Float, kDefaultInt, kStoredFloat);
+ TEST_OVERRIDE(Int, String, kDefaultInt, kStoredString);
+
+ TEST_OVERRIDE(Float, Bool, kDefaultFloat, kStoredBool);
+ TEST_OVERRIDE(Float, Int, kDefaultFloat, kStoredInt);
+ TEST_OVERRIDE(Float, String, kDefaultFloat, kStoredString);
+
+ TEST_OVERRIDE(String, Bool, kDefaultString, kStoredBool);
+ TEST_OVERRIDE(String, Int, kDefaultString, kStoredInt);
+ TEST_OVERRIDE(String, Float, kDefaultString, kStoredFloat);
+
+ RELEASE_TEST;
+ }
+}
+
+#endif
diff --git a/Runtime/Utilities/EndianHelper.h b/Runtime/Utilities/EndianHelper.h
new file mode 100644
index 0000000..42df0ac
--- /dev/null
+++ b/Runtime/Utilities/EndianHelper.h
@@ -0,0 +1,148 @@
+#ifndef UNITY_ENDIAN_HELPER_H_
+#define UNITY_ENDIAN_HELPER_H_
+
+
+enum EndianMode {
+ kEndianDirect = 0,
+ kEndianConvert = 1,
+#if UNITY_BIG_ENDIAN
+ kHostEndian = kEndianDirect,
+ kTargetEndian = kEndianDirect,
+#else
+ kHostEndian = kEndianConvert,
+ kTargetEndian = kEndianConvert,
+#endif
+};
+
+ template<EndianMode Endian>
+ struct EndianHelper;
+
+ template<>
+ struct EndianHelper<kEndianDirect>
+ {
+ static UInt16 Load( UInt16 const* p ) {
+ return *p;
+ }
+
+ static UInt32 Load( UInt32 const* p ) {
+ return *p;
+ }
+
+ template<int Offset>
+ static void Store( UInt16* p, UInt16 arg ) {
+ *reinterpret_cast<UInt16*>( ((intptr_t)p + Offset) ) = arg;
+ }
+
+ template<int Offset>
+ static void Store( UInt32* p, UInt32 arg ) {
+ *reinterpret_cast<UInt32*>( ((intptr_t)p + Offset) ) = arg;
+ }
+
+ };
+
+ template<>
+ struct EndianHelper<kEndianConvert>
+ {
+ static UInt16 Load( register UInt16 const* p ) {
+//#if defined(__native_client__) || (defined(__GNUC__) && !defined(__APPLE__))
+ return ((*p&0xff00U)>>8) | ((*p&0x00ffU)<<8);
+//#elif defined(__ppc__)
+// register UInt16 temp;
+// __asm {
+// lhbrx temp, 0, p
+// }
+// return temp;
+//#elif defined(__i386) || defined(_M_IX86)
+// register UInt16 temp = *p;
+// __asm {
+// mov ax, temp
+// xchg ah, al
+// mov temp, ax
+// }
+// return temp;
+//#else
+//#error "Unsupported architecture"
+//#endif
+ }
+
+ static UInt32 Load( register UInt32 const* p ) {
+//#if defined(__native_client__) || (defined(__GNUC__) && !defined(__APPLE__))
+ return
+ ((*p&0xff000000U)>>24) |
+ ((*p&0x00ff0000U)>>8) |
+ ((*p&0x0000ff00U)<<8) |
+ ((*p&0x000000ffU)<<24)
+ ;
+//#elif defined(__ppc__)
+// register UInt32 temp;
+// __asm {
+// lwbrx r4, 0, p
+// }
+// return temp;
+//#elif defined(__i386) || defined(_M_IX86)
+// register UInt32 temp = *p;
+// __asm {
+// mov eax, temp
+// bswap eax
+// mov temp, eax
+// }
+// return temp;
+//#else
+//#error "Unsupported architecture"
+//#endif
+ }
+
+ // Offset is in bytes
+ template<int Offset>
+ static void Store( register UInt16* p, register UInt16 arg ) {
+//#if defined(__native_client__) || (defined(__GNUC__) && !defined(__APPLE__))
+ UInt16 temp = ((arg&0xff00U)>>8) | ((arg&0x00ffU)<<8);
+ *(UInt16*)((UInt8*)p + Offset) = temp;
+//#elif defined(__ppc__)
+// register short int ofs = Offset;
+// __asm {
+// sthbrx arg, ofs, p
+// }
+//#elif defined(__i386) || defined(_M_IX86)
+// register void* pp = (void*)((uintptr_t)p + Offset);
+// __asm {
+// mov ax, arg
+// xchg ah, al
+// mov ecx, dword ptr [pp]
+// mov word ptr [ecx], ax
+// }
+//#else
+//#error "Unsupported architecture"
+//#endif
+ }
+
+ template<int Offset>
+ static void Store( register UInt32* p, register UInt32 arg ) {
+//#if defined(__native_client__) || (defined(__GNUC__) && !defined(__APPLE__))
+ UInt32 temp =
+ ((arg&0xff000000U)>>24) |
+ ((arg&0x00ff0000U)>>8) |
+ ((arg&0x0000ff00U)<<8) |
+ ((arg&0x000000ffU)<<24)
+ ;
+ *(UInt32*)((UInt8*)p + Offset) = temp;
+//#elif defined(__ppc__)
+// register short int ofs = Offset;
+// __asm {
+// stwbrx arg, ofs, p
+// }
+//#elif defined(__i386) || defined(_M_IX86)
+// register void* pp = (void*)((uintptr_t)p + Offset);
+// __asm {
+// mov eax, arg
+// bswap eax
+// mov ecx, dword ptr [pp]
+// mov word ptr [ecx], eax
+// }
+//#else
+//#error "Unsupported architecture"
+//#endif
+ }
+ };
+
+#endif // UNITY_ENDIAN_HELPER_H_
diff --git a/Runtime/Utilities/EnumFlags.h b/Runtime/Utilities/EnumFlags.h
new file mode 100644
index 0000000..61d323b
--- /dev/null
+++ b/Runtime/Utilities/EnumFlags.h
@@ -0,0 +1,13 @@
+#ifndef ENUM_FLAGS_H
+#define ENUM_FLAGS_H
+
+#define ENUM_FLAGS(T) \
+inline T operator |(const T s, const T e) { return (T)((unsigned)s | e); } \
+inline T &operator |=(T &s, const T e) { return s = s | e; } \
+inline T operator &(const T s, const T e) { return (T)((unsigned)s & e); } \
+inline T &operator &=(T &s, const T e) { return s = s & e; } \
+inline T operator ^(const T s, const T e) { return (T)((unsigned)s ^ e); } \
+inline T &operator ^=(T &s, const T e) { return s = s ^ e; } \
+inline T operator ~(const T s) { return (T)~(unsigned)s; }
+
+#endif
diff --git a/Runtime/Utilities/ErrorExit.cpp b/Runtime/Utilities/ErrorExit.cpp
new file mode 100644
index 0000000..414aa06
--- /dev/null
+++ b/Runtime/Utilities/ErrorExit.cpp
@@ -0,0 +1,165 @@
+#include "UnityPrefix.h"
+#include "ErrorExit.h"
+#include "Runtime/Misc/ReproductionLog.h"
+#if SUPPORT_ERROR_EXIT
+#include "Runtime/Threads/ThreadSpecificValue.h"
+#include "Stacktrace.h"
+
+#if UNITY_WIN
+
+#include "Configuration/UnityConfigureOther.h"
+#include "Runtime/Mono/MonoIncludes.h"
+
+#include "PlatformDependent/Win/WinUtils.h"
+
+#if !UNITY_WIN_ENABLE_CRASHHANDLER
+#error "UNITY_WIN_ENABLE_CRASHHANDLER must be defined"
+#endif
+
+#include "../../Tools/BugReporterWin/lib/CrashHandler.h"
+extern CrashHandler *gUnityCrashHandler;
+
+#endif
+
+ExitErrorCode gExitErrorCode = kErrorNone;
+bool gExitErrorDidCleanup = false;
+#if !UNITY_WIN
+static UNITY_TLS_VALUE(jmp_buf*) gJumpBuffer;
+#endif
+
+ExitErrorCode GetExitErrorCode()
+{
+ return gExitErrorCode;
+}
+
+#if UNITY_PEPPER
+#define STACKTRACE() ""
+#else
+#define STACKTRACE() GetStacktrace().c_str()
+#endif
+
+void ExitWithErrorCode(ExitErrorCode error)
+{
+ printf_console("Exit with error code: %d\n", error);
+ printf_console("\n========== OUTPUTING STACK TRACE ==================\n\n");
+ printf_console("%s\n", STACKTRACE());
+ printf_console("\n========== END OF STACKTRACE ===========\n\n");
+
+ #if SUPPORT_REPRODUCE_LOG
+ if (RunningReproduction())
+ {
+ ErrorString("ExitWithErrorCode invoked, exiting application because we are in reproduction mode.");
+ ReproductionExitPlayer(1);
+ }
+ #endif
+
+ #if !UNITY_RELEASE
+ printf_console("\n*** DEBUGMODE -> ExitWithError code calling abort for debugging ease *** ");
+ abort();
+ #endif
+
+ gExitErrorCode = error;
+
+ #if !UNITY_WIN
+
+ jmp_buf *buf = gJumpBuffer;
+
+ if (buf)
+ {
+ printf_console("Got error %d. Bailing out.\n", error);
+ longjmp (*buf, 1);
+ }
+ else
+ {
+ printf_console("Got error %d. Cannot exit at this point.\n", error);
+ }
+
+ #endif
+}
+
+#if !UNITY_WIN
+
+void InsertThreadExitPoint(jmp_buf *buf)
+{
+ gJumpBuffer = buf;
+}
+
+AutoRemoveEntryPoint::AutoRemoveEntryPoint (jmp_buf *buf)
+{
+ if (gJumpBuffer)
+ m_ShouldRemove = false;
+ else
+ {
+ m_ShouldRemove = true;
+ gJumpBuffer = buf;
+ }
+}
+
+AutoRemoveEntryPoint::~AutoRemoveEntryPoint ()
+{
+ if (m_ShouldRemove)
+ gJumpBuffer = NULL;
+}
+
+#endif
+
+const char* GetExitErrorString(ExitErrorCode err)
+{
+ switch(err)
+ {
+ case kErrorSecurityBreach:
+ return "The content was stopped because it\nattempted an insecure operation.";
+ case kErrorFatalException:
+ return "The content was stopped because a fatal\ncontent error has been detected.";
+ case kErrorNoSSE2Architecture:
+ return "Unity Web Player requires an SSE2 capable CPU.";
+ case kErrorIncompatibleRuntimeVersion:
+ return "The Unity Web Player content which you are trying to load was built with an older version of the Unity Editor, and is incompatible with this runtime.";
+ case kErrorUnsupportedGPU:
+ return "Unity Web Player requires DX9 level graphics card.\nMake sure you have graphics card drivers installed.";
+ default:
+ return NULL;
+ }
+}
+
+#if UNITY_WIN
+
+extern LPTOP_LEVEL_EXCEPTION_FILTER exceptionFilter;
+int HandleSignal( EXCEPTION_POINTERS* ep );
+
+DWORD OnExcept(DWORD code, LPEXCEPTION_POINTERS exception)
+{
+ __try
+ {
+ DWORD result;
+ if( NULL != exceptionFilter )
+ {
+ #if WEBPLUG
+ winutils::ProcessInternalCrash(exception, false);
+ #endif
+
+ result = exceptionFilter(exception);
+ }
+ else
+ {
+ result = mono_unity_seh_handler(exception);
+ }
+
+ if (EXCEPTION_CONTINUE_EXECUTION != result)
+ {
+ gExitErrorCode = kErrorFatalException;
+ result = EXCEPTION_EXECUTE_HANDLER;
+ }
+
+ return result;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ gExitErrorCode = kErrorFatalException;
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+}
+
+#endif
+#endif
+
diff --git a/Runtime/Utilities/ErrorExit.h b/Runtime/Utilities/ErrorExit.h
new file mode 100644
index 0000000..b531ace
--- /dev/null
+++ b/Runtime/Utilities/ErrorExit.h
@@ -0,0 +1,77 @@
+#ifndef ERROREXIT_H
+#define ERROREXIT_H
+
+#include "Configuration/UnityConfigure.h"
+
+#if SUPPORT_ERROR_EXIT
+
+enum ExitErrorCode
+{
+ kErrorNone = 0,
+ kErrorSecurityBreach = 1,
+ kErrorFatalException = 2,
+ kErrorNoSSE2Architecture = 3,
+ kErrorIncompatibleRuntimeVersion = 4,
+ kErrorUnsupportedGPU = 5,
+};
+
+extern ExitErrorCode gExitErrorCode;
+
+#if !UNITY_WIN
+#include <setjmp.h>
+void InsertThreadExitPoint(jmp_buf *buf);
+class AutoRemoveEntryPoint {
+ bool m_ShouldRemove;
+public:
+ AutoRemoveEntryPoint (jmp_buf *buf);
+ ~AutoRemoveEntryPoint ();
+};
+#endif
+void ExitWithErrorCode(ExitErrorCode error);
+ExitErrorCode GetExitErrorCode();
+const char *GetExitErrorString(ExitErrorCode err);
+
+#if UNITY_WIN
+
+DWORD OnExcept(DWORD code, LPEXCEPTION_POINTERS exception);
+
+#define UNITY_ENTRY_POINT(x) if(GetExitErrorCode()) return x; __try {
+#define UNITY_ENTRY_POINT_NO_RETURN_VALUE() if(GetExitErrorCode()) return; __try {
+#define UNITY_ENTRY_POINT_SKIP() if(!GetExitErrorCode()) { __try {
+#define UNITY_EXIT_POINT(x) } __except (OnExcept(GetExceptionCode(), GetExceptionInformation())) { return x; }
+#define UNITY_EXIT_POINT_NO_RETURN_VALUE() } __except (OnExcept(GetExceptionCode(), GetExceptionInformation())) { return; }
+#define UNITY_EXIT_POINT_SKIP() } __except (OnExcept(GetExceptionCode(), GetExceptionInformation())) { } }
+
+#define ERROR_EXIT_THREAD_ENTRY() \
+ __try \
+ {
+#define ERROR_EXIT_THREAD_EXIT() \
+ } \
+ __except (OnExcept(GetExceptionCode(), \
+ GetExceptionInformation())) \
+ { \
+ /* do nothing */ \
+ }
+
+#else
+#define UNITY_ENTRY_POINT(x) if(GetExitErrorCode()) return x; jmp_buf buf; if (setjmp (buf)) return x; AutoRemoveEntryPoint entry(&buf);
+#define UNITY_ENTRY_POINT_NO_RETURN_VALUE() if(GetExitErrorCode()) return; jmp_buf buf; if (setjmp (buf)) return; AutoRemoveEntryPoint entry(&buf);
+#define UNITY_EXIT_POINT(x)
+#define UNITY_EXIT_POINT_NO_RETURN_VALUE()
+
+#define ERROR_EXIT_THREAD_ENTRY() \
+ jmp_buf buf; \
+ if (!setjmp (buf)) \
+ { \
+ InsertThreadExitPoint (&buf);
+#define ERROR_EXIT_THREAD_EXIT() \
+ }
+
+#endif
+
+#else
+#define ExitWithErrorCode(x)
+#define GetExitErrorCode() kErrorNone
+#endif
+
+#endif
diff --git a/Runtime/Utilities/File.cpp b/Runtime/Utilities/File.cpp
new file mode 100644
index 0000000..f93e2df
--- /dev/null
+++ b/Runtime/Utilities/File.cpp
@@ -0,0 +1,831 @@
+#include "UnityPrefix.h"
+#include "File.h"
+#include "PathNameUtility.h"
+#include "PlatformDependent/Win/unistd.h"
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#if UNITY_EDITOR
+#include "FileUtilities.h"
+#include "Editor/Platform/Interface/EditorUtility.h"
+#endif
+#include "Runtime/Scripting/ScriptingUtility.h"
+#if UNITY_OSX || UNITY_IPHONE || UNITY_LINUX || UNITY_FLASH || UNITY_WEBGL || UNITY_TIZEN
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "dirent.h"
+#endif
+#if UNITY_BB10
+#include <unistd.h>
+#include <dirent.h>
+#endif
+#include "Runtime/Threads/Thread.h"
+
+// This is File implementation for OSX and possibly others. Windows implementation is in PlatformDependent
+#if UNITY_WIN
+#error "Windows implementation of File is not here!"
+#endif
+
+using namespace std;
+
+#if UNITY_EDITOR
+string GetFormattedFileError (int error, const std::string& operation);
+#endif
+
+static string gCurrentDirectory;
+
+static FILE* OpenFileWithPath( const string& path, File::Permission permission )
+{
+#if SUPPORT_DIRECT_FILE_ACCESS
+ const char* fileMode = NULL;
+ switch (permission) {
+ case File::kReadPermission:
+ fileMode="rb";
+ break;
+ case File::kWritePermission:
+ fileMode="wb";
+ break;
+ case File::kReadWritePermission:
+ fileMode="r+b";
+ break;
+ case File::kAppendPermission:
+ fileMode="ab";
+ break;
+ }
+
+ return fopen( PathToAbsolutePath(path).c_str(), fileMode );
+
+#else //SUPPORT_DIRECT_FILE_ACCESS
+
+ ErrorString("No file access allowed!");
+ return NULL;
+#endif
+}
+
+bool IsAbsoluteFilePath( const std::string& path )
+{
+ if( path.empty() )
+ return false;
+
+ if( path[0] == kPlatformPathNameSeparator )
+ return true; // paths starting with separator are absolute
+
+ return false;
+}
+
+
+string PathToAbsolutePath (const string& path)
+{
+ if( IsAbsoluteFilePath(path) )
+ return path;
+ else
+ return AppendPathName (File::GetCurrentDirectory (), path);
+}
+
+bool ReadFromFile (const string& pathName, void *data, int fileStart, int byteLength)
+{
+ FILE* file = OpenFileWithPath( pathName, File::kReadPermission );
+ if (file == NULL)
+ return false;
+
+ fseek(file, 0, SEEK_END);
+
+ int length = ftell(file);
+ fseek(file, 0, SEEK_SET);
+ if (length < byteLength)
+ {
+ fclose(file);
+ return false;
+ }
+
+ int readLength = fread(data, 1, byteLength, file);
+
+ fclose(file);
+
+ if (readLength != byteLength)
+ return false;
+
+ return true;
+}
+
+
+#if !UNITY_FLASH //flash implementation in AS3Utility.cpp
+bool ReadStringFromFile (InputString* outData, const string& pathName)
+{
+ FILE* file = OpenFileWithPath( pathName, File::kReadPermission );
+ if (file == NULL)
+ return false;
+
+ fseek(file, 0, SEEK_END);
+
+ int length = ftell(file);
+ fseek(file, 0, SEEK_SET);
+ if (length < 0)
+ {
+ fclose( file );
+ return false;
+ }
+
+ outData->resize(length);
+ int readLength = fread(&*outData->begin(), 1, length, file);
+
+ fclose(file);
+
+ if (readLength != length)
+ {
+ outData->clear();
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+
+bool WriteBytesToFile (const void *data, int byteLength, const string& pathName)
+{
+ File file;
+ if (!file.Open(pathName, File::kWritePermission))
+ return false;
+
+ bool success = file.Write(data, byteLength);
+
+ file.Close();
+
+ return success;
+}
+
+
+int GetFileLength (const string& pathName)
+{
+ #if UNITY_OSX || UNITY_BB10
+ struct stat statbuffer;
+ if( stat(pathName.c_str(), &statbuffer) != 0 )
+ return 0; /// return -1 for error???
+ Assert (S_ISREG(statbuffer.st_mode));
+ return statbuffer.st_size;
+ #elif UNITY_FLASH
+ return Ext_FileContainer_GetFileLength(pathName.c_str());
+ #else
+ FILE* file = OpenFileWithPath( pathName, File::kReadPermission );
+ if (file == NULL)
+ return 0; /// return -1???
+
+ fseek(file, 0, SEEK_END);
+ int length = ftell(file);
+ fclose(file);
+
+ return length;
+ #endif
+}
+
+File::File () { m_File = NULL;m_Position = 0; }
+
+File::~File () { AssertIf(m_File != NULL); }
+
+#if UNITY_EDITOR
+string GetFormattedFileError (int error, const std::string& operation)
+{
+ const char* opstr = operation.c_str();
+ switch (error)
+ {
+ case ENAMETOOLONG:
+ return Format("%s failed because file name is too long", opstr);
+ case ENOTDIR:
+ return Format("%s failed: from directory to non-directory", opstr);
+ case EISDIR:
+ return Format("%s failed: from non-directory to directory", opstr);
+ case EXDEV:
+ return Format("%s failed: to and from are on different file systems", opstr);
+ case EIO:
+ return Format("%s failed: I/O error updating directory", opstr);
+ case EROFS:
+ return Format("%s failed: read only file system", opstr);
+ case EFAULT:
+ return Format("%s failed: segmentation fault", opstr);
+ case EINVAL:
+ return Format("%s failed: from is a parent of to, or rename of . or ..", opstr);
+ case ENOTEMPTY:
+ return Format("%s failed: to is a directory and not empty", opstr);
+ case EPERM:
+ return Format("%s failed because the operation was not permitted", opstr);
+ case ENOENT:
+ return Format("%s failed because the file or directory does not exist", opstr);
+ case ENOMEM:
+ return Format("%s failed because there was not enough memory available", opstr);
+ case EACCES:
+ return Format("%s failed because permission for the file was denied", opstr);
+ case EEXIST:
+ return Format("%s failed because the file already exists", opstr);
+ case ENOSPC:
+ return Format("%s failed because there is no disk space left. Please free some disk space and continue.", opstr);
+ #if UNITY_OSX
+ case EAUTH:
+ return Format("%s failed because of an authentication failure", opstr);
+ case ENEEDAUTH:
+ return Format("%s failed because you need an authenticator", opstr);
+ case ELOOP:
+ return Format("%s failed: too many symbolic links", opstr);
+ case EDQUOT:
+ return Format("%s failed: quota limit reached", opstr);
+ #endif
+ default:
+ return Format("%s failed with error: %s", opstr, strerror(error));
+ }
+}
+
+#endif
+static bool HandleFileError (const char* title, const string& operation, FILE* file)
+{
+ #if UNITY_EDITOR
+ int err = ferror(file);
+ clearerr(file);
+
+ int result = DisplayDialogComplex (title, GetFormattedFileError(err, operation), "Try Again", "Cancel", "Force Quit");
+ if (result == 1)
+ return false;
+ else if (result == 2)
+ exit(1);
+ else
+ return true;
+ #else
+ return false;
+ #endif
+}
+
+bool File::Open (const std::string& path, File::Permission perm, AutoBehavior behavior)
+{
+ Close();
+ m_Path = path;
+
+ int retryCount = 5;
+
+ while (true)
+ {
+ m_File = OpenFileWithPath( path, perm );
+ m_Position = 0;
+ if (m_File != NULL)
+ {
+ if (perm == kAppendPermission)
+ m_Position = ftell(m_File);
+ return true;
+ }
+ else
+ {
+#if SUPPORT_THREADS
+ if ( (behavior & kRetryOnOpenFail) && (--retryCount > 0))
+ {
+ Thread::Sleep(0.2);
+ continue;
+ }
+#endif
+
+ if ( behavior & kSilentReturnOnOpenFail )
+ return false;
+
+ #if UNITY_EDITOR
+ int result = DisplayDialogComplex ("Opening file failed", GetFormattedFileError(errno, "Opening file "+path), "Try Again", "Cancel", "Force Quit");
+ if (result == 1)
+ return false;
+ else if (result == 2)
+ exit(1);
+ #else
+ ErrorString("Failed to open file at path: " + path);
+ return false;
+ #endif
+ }
+ }
+ return false;
+}
+
+bool File::Close ()
+{
+ if (m_File != NULL)
+ {
+ if (fclose(m_File) != 0)
+ {
+ #if UNITY_EDITOR
+ ErrorString(GetFormattedFileError(errno, "Closing file " + m_Path));
+ #elif UNITY_FLASH
+ //no problemo
+ #else
+ ErrorString("Closing file " + m_Path);
+ #endif
+ }
+ m_File = NULL;
+ }
+
+ m_Path.clear();
+ return true;
+}
+
+int File::Read (void* buffer, int size)
+{
+ if (m_File)
+ {
+ int s = fread(buffer, 1, size, m_File);
+
+ if (s == size || ferror(m_File) == 0)
+ {
+ m_Position += s;
+ return s;
+ }
+ else
+ {
+ //m_Position = -1;
+ if (!HandleFileError("Reading file failed", "Reading from file " + m_Path, m_File))
+ return false;
+
+ // We don't know how far the file was read.
+ // So we just play safe and go through the API that seeks from a specific offset
+ int oldPos = m_Position;
+ m_Position = -1;
+ return Read(oldPos, buffer, size);
+ }
+ }
+ else
+ {
+ ErrorString("Reading failed because the file was not opened");
+ return 0;
+ }
+}
+
+int File::Read (int position, void* buffer, int size)
+{
+ if (m_File)
+ {
+ while (true)
+ {
+ // Seek if necessary
+ if (position != m_Position)
+ {
+ if (fseek(m_File, position, SEEK_SET) != -1)
+ m_Position = position;
+ else
+ {
+ m_Position = -1;
+ if (!HandleFileError("Reading file failed", "Seeking in file " + m_Path, m_File ))
+ return 0;
+
+ continue;
+ }
+ }
+
+ int s = fread(buffer, 1, size, m_File);
+ if (s == size || ferror(m_File) == 0)
+ {
+ m_Position += s;
+ return s;
+ }
+ else
+ {
+ m_Position = -1;
+ if (!HandleFileError("Reading file failed", "Reading from file " + m_Path, m_File ))
+ return 0;
+ }
+ }
+ }
+ else
+ {
+ ErrorString("Reading failed because the file was not opened");
+ return 0;
+ }
+ return 0;
+}
+
+
+
+bool File::Write (const void* buffer, int size)
+{
+ if (m_File)
+ {
+ int s = fwrite(buffer, 1, size, m_File);
+ if (s == size)
+ {
+ m_Position += s;
+ return true;
+ }
+ else
+ {
+ if (!HandleFileError("Writing file failed", "Writing to file " + m_Path, m_File))
+ return false;
+
+ // We don't know how far the file was read.
+ // So we just play safe and go through the API that seeks from a specific offset
+ int oldPos = m_Position;
+ m_Position = -1;
+ return Write(oldPos, buffer, size);
+ }
+ }
+ else
+ {
+ ErrorString("Writing failed because the file was not opened");
+ return false;
+ }
+}
+
+bool File::Write (int position, const void* buffer, int size)
+{
+ if (m_File)
+ {
+ while (true)
+ {
+ // Seek if necessary
+ if (position != m_Position)
+ {
+ if (fseek(m_File, position, SEEK_SET) != -1)
+ m_Position = position;
+ else
+ {
+ m_Position = -1;
+ if (!HandleFileError("Writing file failed", "Seeking in file "+m_Path, m_File))
+ return false;
+
+ continue;
+ }
+ }
+
+ int s = fwrite(buffer, 1, size, m_File);
+ if (s == size)
+ {
+ m_Position += s;
+ return true;
+ }
+ else
+ {
+ m_Position = -1;
+ if (!HandleFileError("Writing file failed", "Writing to file "+m_Path, m_File))
+ return false;
+ }
+ }
+ }
+ else
+ {
+ ErrorString("Writing failed because the file was not opened");
+ }
+ return false;
+}
+
+bool File::SetFileLength (int size)
+{
+ return ::SetFileLength(m_Path, size);
+}
+
+int File::GetFileLength()
+{
+ return ::GetFileLength(m_Path);
+}
+
+void File::SetCurrentDirectory (const string& path)
+{
+ gCurrentDirectory = path;
+}
+
+const string& File::GetCurrentDirectory ()
+{
+ return gCurrentDirectory;
+}
+
+void File::CleanupClass()
+{
+ gCurrentDirectory = string();
+}
+
+bool SetFileLength (const std::string& path, int size)
+{
+ #if UNITY_PEPPER || UNITY_FLASH || UNITY_WEBGL
+ ErrorString("SetFileLength not supported on this platform!");
+ return false;
+ #else
+ while (true)
+ {
+ int error = truncate(path.c_str(), size);
+ if (error == 0)
+ return true;
+
+ #if UNITY_EDITOR
+ int result = DisplayDialogComplex ("Writing file error", GetFormattedFileError(errno, "Resizing file " + path), "Try Again", "Cancel", "Quit");
+ if (result == 1)
+ return false;
+ else if (result == 2)
+ exit(1);
+ #else
+ return false;
+ #endif
+ }
+ #endif
+
+ return true;
+}
+
+static bool IsFileCreatedAtAbsolutePath (const string & path)
+{
+ #if UNITY_OSX || UNITY_IPHONE || UNITY_LINUX || UNITY_FLASH || UNITY_WEBGL || UNITY_BB10 || UNITY_TIZEN
+
+ struct stat status;
+
+ if (stat(path.c_str(), &status) != 0)
+ return false;
+
+#if TARGET_IPHONE_SIMULATOR
+ // some bad hack for simulator, if we can stat it then file exists...
+ return true;
+#endif
+
+ return S_ISREG (status.st_mode);
+
+ #elif !SUPPORT_DIRECT_FILE_ACCESS
+
+ ErrorString("No file access allowed!");
+ return false;
+
+ #else
+
+ #error "Unsupported platform!"
+
+ #endif
+}
+
+#if !UNITY_FLASH //Flash implementation in AS3Utility.cpp
+bool IsFileCreated (const string& path)
+{
+ return IsFileCreatedAtAbsolutePath (PathToAbsolutePath (path));
+}
+#endif
+
+
+#if UNITY_OSX || UNITY_IPHONE || UNITY_PEPPER || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+bool IsFileOrDirectoryInUse ( const string& path )
+{
+#if UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+ return false;
+#elif !UNITY_PEPPER
+ bool isUsed = false;
+
+ if ( IsDirectoryCreated( path ) )
+ {
+ set<string> paths;
+ if ( GetFolderContentsAtPath( path, paths ) )
+ {
+ for (set<string>::iterator i = paths.begin(); i != paths.end(); i++)
+ {
+ if ( IsFileOrDirectoryInUse( *i ) )
+ return true;
+ }
+ }
+ }
+ else if ( IsFileCreated( path ) )
+ {
+ File openTest;
+ if ( openTest.Open( path, File::kReadPermission ) )
+ {
+ if ( !openTest.Lock( File::kExclusive, false ) )
+ isUsed = true;
+ openTest.Lock( File::kNone, false );
+ openTest.Close();
+ }
+ else
+ isUsed = true;
+ }
+
+ return isUsed;
+#else
+ ErrorString("No file access allowed!");
+ return false;
+#endif
+}
+#endif
+
+static bool IsDirectoryCreatedAtAbsolutePath (const string & path)
+{
+ #if UNITY_OSX || UNITY_LINUX || UNITY_IPHONE || UNITY_BB10 || UNITY_WEBGL || UNITY_TIZEN
+ struct stat status;
+ if (stat(path.c_str(), &status) != 0)
+ return false;
+ return S_ISDIR(status.st_mode);
+
+ #elif UNITY_FLASH
+ #warning "IsDirectoryCreatedAtAbsolutePath() NOT implemented for UNITY_FLASH"
+ return false;
+
+ #elif !SUPPORT_DIRECT_FILE_ACCESS
+
+ ErrorString("No file access allowed!");
+ return false;
+ #else
+ #error Unknown platform
+ #endif
+}
+
+bool IsDirectoryCreated (const string& path)
+{
+ return IsDirectoryCreatedAtAbsolutePath (PathToAbsolutePath (path));
+}
+
+bool CreateDirectory (const string& pathName)
+{
+#if SUPPORT_DIRECT_FILE_ACCESS && !UNITY_FLASH
+ if (IsFileCreated (pathName))
+ return false;
+ else if (IsDirectoryCreated (pathName))
+ return true;
+
+ string absolutePath = PathToAbsolutePath (pathName);
+
+#if UNITY_EDITOR
+ std::string fileNamePart = GetLastPathNameComponent (absolutePath);
+ if (CheckValidFileNameDetail (fileNamePart) == kFileNameInvalid)
+ {
+ ErrorStringMsg ("%s is not a valid directory name. Please make sure there are no unallowed characters in the name.", fileNamePart.c_str ());
+ return false;
+ }
+#endif
+
+ int res = mkdir(absolutePath.c_str(), 0755);
+ return res == 0;
+#else
+ ErrorString("No file access allowed!");
+ return false;
+#endif
+}
+
+bool DeleteFile (const string& path)
+{
+#if !UNITY_PEPPER && !UNITY_FLASH && !UNITY_WEBGL
+ string absolutePath = PathToAbsolutePath (path);
+ if (IsFileCreated (absolutePath))
+ {
+ return unlink(absolutePath.c_str()) == 0;
+ }
+ else
+ {
+ return false;
+ }
+#else
+ ErrorString("No file access allowed!");
+ return false;
+#endif
+}
+
+int DeleteFilesAndDirsRecursive(const string& path)
+{
+#if SUPPORT_DIRECT_FILE_ACCESS && !UNITY_FLASH && !UNITY_WEBGL
+ int res;
+
+ struct stat status;
+ res = lstat(path.c_str(), &status);
+ if (res != 0)
+ return res;
+
+ if (S_ISDIR(status.st_mode) && !S_ISLNK(status.st_mode))
+ {
+ DIR *dirp = opendir (path.c_str());
+ if (dirp == NULL)
+ return -1;
+
+ struct dirent *dp;
+ while ( (dp = readdir(dirp)) )
+ {
+ if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
+ {
+ string name = dp->d_name;
+ res = DeleteFilesAndDirsRecursive(AppendPathName (path, name));
+ if (res != 0)
+ {
+ closedir(dirp);
+ return res;
+ }
+ }
+ }
+ closedir(dirp);
+
+ res = rmdir(path.c_str());
+ }
+ else
+ {
+ res = unlink(path.c_str());
+ }
+
+ return res;
+#else
+ ErrorString("No file access allowed!");
+ return false;
+#endif
+}
+
+bool DeleteFileOrDirectory (const string& path)
+{
+ int res = DeleteFilesAndDirsRecursive(PathToAbsolutePath(path));
+ return res == 0;
+}
+
+bool ShouldIgnoreFile (const char* name, size_t length)
+{
+ AssertIf(!name);
+ if (length < 1)
+ return true;
+
+ // ignore hidden files
+ if (name[0] == '.')
+ return true;
+ // ignore CVS files
+ if (StrICmp(name, "cvs") == 0)
+ return true;
+
+ // ignore temporary and backup files
+ if ((name[length-1] == '~') || (length >= 4 && StrICmp(name + length - 4, ".tmp" ) == 0))
+ return true;
+
+ return false;
+}
+
+bool GetFolderContentsAtPath (const string& pathName, set<string>& paths)
+{
+#if SUPPORT_DIRECT_FILE_ACCESS && !UNITY_FLASH
+ string absolutePath = PathToAbsolutePath(pathName);
+ DIR *dirp;
+ struct dirent *dp;
+
+ if ((dirp = opendir(absolutePath.c_str())) == NULL)
+ return false;
+
+ while ( (dp = readdir(dirp)) )
+ {
+ if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
+ {
+ if (!ShouldIgnoreFile (dp->d_name, strlen (dp->d_name)))
+ {
+ string name = dp->d_name;
+ paths.insert (AppendPathName (pathName, name));
+ }
+ }
+ }
+ closedir(dirp);
+ return true;
+#else
+ ErrorString("No file access allowed!");
+ return false;
+#endif
+}
+
+#if UNITY_EDITOR
+
+bool MoveReplaceFile (const string& fromPath, const string& toPath)
+{
+ if( IsDirectoryCreated(fromPath) )
+ {
+ ErrorString( Format("Path %s is a directory", fromPath.c_str()) );
+ return false;
+ }
+ while (true)
+ {
+ int error = rename( PathToAbsolutePath(fromPath).c_str(), PathToAbsolutePath(toPath).c_str() );
+ if( error == 0 )
+ return true;
+
+ int errorCode = errno;
+ if( errorCode == EXDEV )
+ {
+ DeleteFileOrDirectory(toPath);
+ if (CopyFileOrDirectory(fromPath, toPath))
+ {
+ if (DeleteFileOrDirectory(fromPath))
+ return true;
+ }
+ }
+
+ #if UNITY_EDITOR
+ int result = DisplayDialogComplex ("Moving file failed", GetFormattedFileError(errno, "Moving "+fromPath+" to "+toPath), "Try Again", "Cancel", "Force Quit");
+ if (result == 1)
+ return false;
+ else if (result == 2)
+ exit(1);
+ #else
+ return false;
+ #endif
+ }
+ return false;
+}
+
+#endif
+
+#if UNITY_OSX || UNITY_IPHONE
+bool File::Lock(File::LockMode mode, bool block)
+{
+ int fd = fileno (m_File);
+ return flock (fd, mode + (block?0:LOCK_NB) ) == 0;
+}
+
+#include <sys/resource.h>
+//Local testing and various internet sources suggest that this is the absolute maximim of open files OSX can hande.
+#define kMaxOpenFile 10240
+
+void SetupFileLimits ()
+{
+ struct rlimit limit;
+ limit.rlim_cur = kMaxOpenFile;
+ limit.rlim_max = kMaxOpenFile;
+ if (setrlimit(RLIMIT_NOFILE, &limit) != 0)
+ printf_console("setrlimit() failed with errno=%d\n", errno);
+}
+#endif
diff --git a/Runtime/Utilities/File.h b/Runtime/Utilities/File.h
new file mode 100644
index 0000000..b033278
--- /dev/null
+++ b/Runtime/Utilities/File.h
@@ -0,0 +1,194 @@
+#pragma once
+
+#include "Runtime/Misc/Allocator.h"
+#if UNITY_OSX || UNITY_IPHONE || UNITY_ANDROID || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+#include "fcntl.h"
+#endif
+#include "Runtime/Utilities/NonCopyable.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include <set>
+#include "Runtime/Utilities/EnumFlags.h"
+
+#if UNITY_WII
+#include <rvlaux/file-io.h>
+#endif
+
+#if UNITY_WINRT
+// Enable only one
+#define ENABLE_CRT_IO 0
+#define ENABLE_WIN_IO 1
+#endif
+
+class File : public NonCopyable
+{
+ int m_Position;
+ std::string m_Path;
+#if UNITY_WII
+ bool m_Open;
+ WiiFileInfo m_File;
+#elif UNITY_WINRT
+ //Microsoft::WRL::ComPtr<IInspectable> m_Stream;
+# if ENABLE_CRT_IO
+ FILE* m_File;
+# elif ENABLE_WIN_IO
+ HANDLE m_FileHandle;
+# else
+ std::vector<unsigned char> m_Data;
+# endif
+#elif UNITY_WIN || UNITY_XENON
+ HANDLE m_FileHandle;
+#elif UNITY_PS3
+ int m_FileDescriptor;
+#else
+ FILE* m_File;
+#endif
+
+public:
+
+ File ();
+ ~File ();
+
+ enum Permission { kReadPermission = 0, kWritePermission = 1, kReadWritePermission = 2, kAppendPermission = 3 };
+ enum AutoBehavior { kNormalBehavior = 0, kSilentReturnOnOpenFail = 1<<0, kRetryOnOpenFail = 1<<1};
+
+ bool Open (const std::string& path, Permission perm, AutoBehavior behavior = kNormalBehavior);
+ bool Close ();
+
+ int Read (void* buffer, int size);
+ int Read (int position, void* buffer, int size);
+
+ bool Write (const void* buffer, int size);
+ bool Write (int pos, const void* buffer, int size);
+ bool SetFileLength (int size);
+ int GetFileLength ();
+ int GetPosition () const { return m_Position; }
+
+ static void SetCurrentDirectory (const std::string& path);
+ static const std::string& GetCurrentDirectory ();
+ static void CleanupClass();
+
+#if UNITY_OSX || UNITY_IPHONE || UNITY_ANDROID || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+ enum LockMode {
+ kNone = LOCK_UN,
+ kShared = LOCK_SH,
+ kExclusive = LOCK_EX
+ };
+ bool Lock(File::LockMode mode, bool block);
+#endif
+};
+
+ENUM_FLAGS(File::AutoBehavior);
+
+/// Returns the file length of the file at pathName. If the file doesn't exist returns -1.
+int GetFileLength (const std::string& pathName);
+size_t GetFileSize (FILE* file);
+
+
+std::string PathToAbsolutePath (const std::string& path);
+// Path is absolute in platform dependent way!
+bool IsAbsoluteFilePath( const std::string& path );
+
+/// Reads the contents of the file at pathName into data.
+/// byteStart is the first byte read in the file, byteLength is the number of bytes that should be read
+bool ReadFromFile (const std::string& pathName, void *data, int fileStart, int byteLength);
+
+bool WriteBytesToFile (const void *data, int byteLength, const std::string& pathName);
+
+#if UNITY_WII
+typedef std::basic_string<char, std::char_traits<char>, STL_ALLOCATOR_ALIGNED(kMemIOTemp, char, 32)> InputString;
+#else
+typedef TEMP_STRING InputString;
+#endif
+bool ReadStringFromFile (InputString* outData, const std::string& pathName);
+
+bool IsFileCreated (const std::string& path);
+bool IsFileOrDirectoryInUse( const std::string& path );
+bool CreateDirectory (const std::string& pathName);
+
+/// Returns a set of files and folders that are children of the folder at pathname.
+/// Ignores invisible files
+bool GetFolderContentsAtPath (const std::string& pathName, std::set<std::string>& paths);
+
+/// Deletes a single file. Returns true if the file could be deleted.
+/// Returns false if the file couldnt be deleted or the file was a folder.
+bool DeleteFile (const std::string& file);
+bool DeleteFileOrDirectory (const std::string& path);
+
+bool SetFileLength (const std::string& path, int size);
+#if UNITY_WIN
+bool SetFileLength (const HANDLE hFile, const std::string& path, int size);
+bool GetFolderContentsAtPathImpl( const std::wstring& pathNameWide, std::set<std::string>& paths, bool deep );
+#endif
+bool IsDirectoryCreated (const std::string& path);
+
+#if UNITY_EDITOR
+bool MoveReplaceFile (const std::string& fromPath, const std::string& toPath);
+#if UNITY_OSX || UNITY_LINUX
+std::string GetFormattedFileError (int error, const std::string& operation);
+#endif
+#endif
+
+#if !UNITY_EXTERNAL_TOOL
+inline bool CreateDirectoryRecursive (const std::string& pathName)
+{
+ if (IsDirectoryCreated(DeleteLastPathNameComponent(pathName)))
+ {
+ if (IsDirectoryCreated (pathName))
+ return true;
+ else if (IsFileCreated (pathName))
+ return false;
+ else
+ return CreateDirectory (pathName);
+ }
+
+ std::string::size_type pos = pathName.rfind ("/");
+ if (pos == std::string::npos)
+ return false;
+
+ std::string superPath;
+ superPath.assign (pathName.begin (), pathName.begin () + pos);
+
+ if (!CreateDirectoryRecursive (superPath))
+ return false;
+
+ return CreateDirectoryRecursive (pathName);
+}
+#endif
+
+// On Vista, its search indexer can wreak havoc on files that are written then renamed. Full story here:
+// http://stackoverflow.com/questions/153257/random-movefileex-failures-on-vista-access-denied-looks-like-caused-by-search-i
+// but in short, whenever a file is temporary or should be excluded from search indexing, we have to set those flags.
+enum FileFlags {
+ kFileFlagTemporary = (1<<0), // File is meant to be temporary.
+ kFileFlagDontIndex = (1<<1), // Exclude file from search indexing (Spotlight, Vista Search, ...)
+ kFileFlagHidden = (1<<2), // Set file as hidden file.
+
+ kAllFileFlags = kFileFlagTemporary | kFileFlagDontIndex
+};
+
+#if UNITY_WIN
+bool SetFileFlags (const std::string& path, UInt32 attributeMask, UInt32 attributeValue);
+
+bool isHiddenFile (const std::string& path);
+
+#elif UNITY_EDITOR
+bool SetFileFlags (const std::string& path, UInt32 attributeMask, UInt32 attributeValue);
+
+bool isHiddenFile (const std::string& path);
+
+#else
+inline bool SetFileFlags (const std::string& path, UInt32 attributeMask, UInt32 attributeValue)
+{
+ return true;
+}
+
+inline bool isHiddenFile (const std::string& path)
+{
+ return false;
+}
+#endif
+
+#if UNITY_OSX
+void SetupFileLimits ();
+#endif
+
diff --git a/Runtime/Utilities/FileStripped.h b/Runtime/Utilities/FileStripped.h
new file mode 100644
index 0000000..c6590c6
--- /dev/null
+++ b/Runtime/Utilities/FileStripped.h
@@ -0,0 +1,10 @@
+
+#ifndef __FILE_STRIPPED__
+
+#if ENABLE_PROFILER || DEBUGMODE
+#define __FILE_STRIPPED__ __FILE__
+#else
+#define __FILE_STRIPPED__ ""
+#endif
+
+#endif \ No newline at end of file
diff --git a/Runtime/Utilities/FileUtilities.cpp b/Runtime/Utilities/FileUtilities.cpp
new file mode 100644
index 0000000..6ae5cb0
--- /dev/null
+++ b/Runtime/Utilities/FileUtilities.cpp
@@ -0,0 +1,27 @@
+#include "UnityPrefix.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/Utilities/GUID.h"
+
+
+using namespace std;
+
+
+#if UNITY_HAVE_GUID_INIT
+
+string GetUniqueTempPath (const std::string& basePath)
+{
+ while (true)
+ {
+ UnityGUID guid; guid.Init();
+ string tmpFilePath = basePath + GUIDToString(guid);
+ if (!IsFileCreated(tmpFilePath))
+ return tmpFilePath;
+ }
+}
+
+string GetUniqueTempPathInProject ()
+{
+ return GetUniqueTempPath ("Temp/UnityTempFile-");
+}
+
+#endif
diff --git a/Runtime/Utilities/FileUtilities.h b/Runtime/Utilities/FileUtilities.h
new file mode 100644
index 0000000..c9589c2
--- /dev/null
+++ b/Runtime/Utilities/FileUtilities.h
@@ -0,0 +1,113 @@
+#ifndef FILEUTILITIES_H
+#define FILEUTILITIES_H
+
+#include <list>
+#include <string>
+#include <set>
+#include "PathNameUtility.h"
+#include "DateTime.h"
+#include "File.h"
+
+using std::string;
+
+#if UNITY_EDITOR
+
+/// Returns the real pathname of path
+/// (eg. if pathname is all lowercase, the returned string will match the actual pathname)
+/// If there is no file at path returns path.
+std::string GetActualPathSlow (const std::string & input);
+std::string GetActualAbsolutePathSlow (const std::string & path);
+
+// Copies a file or folder. Resolves all symlinks in from before copying.
+bool CopyFileOrDirectory (const string& from, const string& to);
+bool MoveToTrash (const string& path);
+bool CopyReplaceFile (string from, string to);
+bool MoveFileOrDirectory (const string& fromPath, const string& toPath);
+
+string GetProjectRelativePath (const string& path);
+
+bool CreateDirectory (const string& pathName);
+
+/// Returns a set of files and folders that are deep children of the folder at pathname.
+/// Ignores invisible files
+bool GetDeepFolderContentsAtPath (const string& pathName, std::set<string>& paths);
+
+/// Returns the content modification date for the file at path or zero timestamp if file couldn't be found
+DateTime GetContentModificationDate (const string& path);
+
+/// Sets the content modification date (write time) for the file at path
+bool SetContentModificationDateToCurrentTime (const string& path);
+
+bool CreateDirectorySafe (const string& pathName);
+
+std::string GenerateUniquePath (std::string inPath);
+std::string GenerateUniquePathSafe (const std::string& pathName);
+std::string GetUniqueTempPathInProject ();
+std::string GetUniqueTempPath (const std::string& basePath);
+
+bool IsPathCreated (const std::string& path);
+
+#if UNITY_OSX || UNITY_LINUX
+inline void UnixTimeToUnityTime (time_t time, DateTime &dateTime )
+{
+ // Seconds from 1904 to 2001: 3061152000 (kCFAbsoluteTimeIntervalSince1904)
+ // Seconds from 1970 to 2001: 978307200 (kCFAbsoluteTimeIntervalSince1970)
+ // Thus seconds from 1904 to 1970: 2082844800
+ UInt64 seconds = time - 2082844800;
+ dateTime.fraction = 0;
+ dateTime.lowSeconds = seconds & 0xFFFFFFFF;
+ dateTime.highSeconds = seconds >> 32;
+}
+#endif
+
+#if UNITY_OSX
+bool PathToFSSpec (const std::string& path, FSSpec* spec);
+#endif
+
+#if UNITY_WIN
+void FileTimeToUnityTime( const FILETIME& ft, DateTime& dateTime );
+bool CopyFileOrDirectoryCheckPermissions (const string& from, const string& to, bool &accessdenied);
+#endif
+
+enum AtomicWriteMode {
+ kProjectTempFolder = 0,
+ #if UNITY_OSX
+ kSystemTempFolder = 1,
+ #endif
+ kNotAtomic = 2,
+};
+bool WriteStringToFile (const string& inData, const string& pathName, AtomicWriteMode mode, UInt32 fileFlags);
+
+bool IsSymlink (const std::string& pathName);
+
+// Resolve symlinks & canonicalize the path
+std::string ResolveSymlinks (const std::string& pathName);
+
+std::string GetUserAppDataFolder ();
+std::string GetUserAppCacheFolder ();
+
+#endif // UNITY_EDITOR
+
+string GetApplicationPath ();
+string GetApplicationContentsPath ();
+string GetApplicationFolder ();
+string GetApplicationLocalFolder();
+string GetApplicationManagedPath();
+string GetApplicationTemporaryFolder();
+
+#if UNITY_STANDALONE || UNITY_EDITOR
+string GetApplicationNativeLibsPath();
+#endif
+
+bool CreateFile (const string& path, int creator = '?', int fileType = '?');
+
+#if UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+void SetApplicationPath( std::string path );
+bool IsSymlink(const std::string &pathName);
+std::string ResolveSymlink(const std::string& pathName);
+#if !UNITY_BB10 && !UNITY_TIZEN
+std::string GetUserConfigFolder ();
+#endif
+#endif
+
+#endif
diff --git a/Runtime/Utilities/GLSLUtilities.cpp b/Runtime/Utilities/GLSLUtilities.cpp
new file mode 100644
index 0000000..2152ef3
--- /dev/null
+++ b/Runtime/Utilities/GLSLUtilities.cpp
@@ -0,0 +1,835 @@
+#include "UnityPrefix.h"
+#include "GLSLUtilities.h"
+#include "External/shaderlab/Library/ShaderLabErrors.h"
+
+#if UNITY_ANDROID || UNITY_IPHONE
+ #include "Runtime/Shaders/GraphicsCaps.h"
+#endif
+
+using namespace std;
+
+// --------------------------------------------------------------------------
+
+
+void OutputGLSLShaderError(const char* log, GLSLErrorType errorType, ShaderErrors& outErrors)
+{
+
+ const char* strErrorIds[kErrorCount] =
+ {
+ "GLSL Error in Vertex Shader: ",
+ "GLSL Error in Fragment Shader: ",
+ "GLSL Error while linking: "
+ };
+
+ string errorMessage = string(strErrorIds[errorType]) + log;
+
+ int errorLine = 0;
+ switch(errorType)
+ {
+ case kErrorCompileVertexShader:
+ case kErrorCompileFragShader:
+ {
+ size_t b = errorMessage.find('(');
+ size_t e = errorMessage.find(')');
+ if(b != string::npos && e != string::npos && e>b)
+ {
+ string errorLineText = errorMessage.substr(b + 1, e - b - 1);
+ errorLine = StringToInt(errorLineText);
+ }
+
+ outErrors.AddShaderError (errorMessage, errorLine, false);
+ }
+ break;
+ case kErrorLinkProgram:
+ errorMessage += log;
+ outErrors.AddShaderError (errorMessage, errorLine, false);
+ break;
+ default:
+ break;
+ }
+}
+
+
+// --------------------------------------------------------------------------
+// GLSL patching for fog
+
+#define DEBUG_FOG_PATCHING 0
+
+static inline bool IsNewline( char c ) { return c == '\n' || c == '\r'; }
+
+static size_t FindStartOfBlock (const std::string& src, const std::string& token)
+{
+ size_t pos = src.find (token);
+ if (pos == std::string::npos)
+ return pos;
+ const size_t n = src.size();
+ while (pos < n && !IsNewline(src[pos])) ++pos; // skip until newline
+ while (pos < n && IsNewline(src[pos])) ++pos; // skip newlines
+ if (pos >= n)
+ return std::string::npos;
+ return pos;
+}
+
+static bool SkipUntilStatementEnd (const std::string& src, size_t& pos)
+{
+ const size_t n = src.size();
+ while (pos < n && src[pos] != ';') ++pos; if (pos < n) ++pos;
+ while (pos < n && IsNewline(src[pos])) ++pos; // skip following newlines
+ return pos < n;
+}
+
+static void SkipUntilStatementBegin (const std::string& src, size_t& pos)
+{
+ const size_t n = src.size();
+ while (pos > 0 && src[pos] != ';' && src[pos] != '{') --pos; if (pos < n) ++pos;
+ while (pos < n && IsSpace(src[pos])) ++pos; // skip following whitespace
+}
+
+static size_t SkipGLSLDirectives (const std::string& src, size_t startPos = 0)
+{
+ size_t pos = startPos;
+ const size_t n = src.size();
+ while (pos < n)
+ {
+ // skip whitespace
+ while (pos < n && IsSpace(src[pos])) ++pos;
+ // have a '#'?
+ if (pos >= n || src[pos] != '#')
+ break;
+ // skip until end of line
+ while (pos < n && !IsNewline(src[pos])) ++pos;
+ }
+ return pos;
+}
+
+
+static int ParseGLSLVersion (const std::string& src, size_t startPos = 0)
+{
+ size_t pos = startPos;
+ const size_t n = src.size();
+ // skip whitespace
+ while (pos < n && IsSpace(src[pos])) ++pos;
+ // have a '#'?
+ if (pos >= n || src[pos] != '#')
+ return 0;
+ ++pos;
+ // skip whitespace
+ while (pos < n && IsSpace(src[pos])) ++pos;
+ // have a "version"?
+ if (0 != strncmp(src.c_str() + pos, "version", 7))
+ return 0;
+ pos += 7;
+ // skip whitespace
+ while (pos < n && IsSpace(src[pos])) ++pos;
+ // parse a number
+ int v = atoi(src.c_str() + pos);
+ return v;
+}
+
+static const char* GetGLSLFogData (int version, bool vertex)
+{
+ if (version >= 150)
+ {
+ if (vertex)
+ return "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\nout float _unity_FogVar;\n";
+ else
+ return "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\nin float _unity_FogVar;\n";
+ }
+ return "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\nvarying float _unity_FogVar;\n";
+}
+
+static bool InsertFogVertexCode (std::string& src)
+{
+ size_t vertexStart = FindStartOfBlock (src, "#ifdef VERTEX");
+ if (vertexStart == std::string::npos)
+ return false;
+ const int version = ParseGLSLVersion (src, vertexStart);
+ vertexStart = SkipGLSLDirectives (src, vertexStart);
+
+ const char* fogData = GetGLSLFogData(version, true);
+ src.insert (vertexStart, fogData);
+
+ size_t posWriteStart = src.find ("gl_Position", vertexStart);
+ if (posWriteStart == std::string::npos)
+ return false;
+
+ size_t pos = posWriteStart;
+ if (!SkipUntilStatementEnd (src, pos))
+ return false;
+
+ // insert fog code at pos!
+ src.insert (pos, "_unity_FogVar = gl_Position.z;\n");
+
+ return true;
+}
+
+static bool InsertFogFragmentCode (std::string& src, FogMode fog)
+{
+ size_t fragmentStart = FindStartOfBlock (src, "#ifdef FRAGMENT");
+ if (fragmentStart == std::string::npos)
+ return false;
+ const int version = ParseGLSLVersion (src, fragmentStart);
+ fragmentStart = SkipGLSLDirectives (src, fragmentStart);
+
+ const char* fogData = GetGLSLFogData(version, false);
+ src.insert (fragmentStart, fogData);
+
+ bool writesToData = true; // writes to gl_FragData vs. gl_FragColor
+ size_t colorWriteStart = src.find ("gl_FragData[0]", fragmentStart);
+ if (colorWriteStart == std::string::npos)
+ {
+ colorWriteStart = src.find ("gl_FragColor", fragmentStart);
+ if (colorWriteStart == std::string::npos)
+ return false;
+ writesToData = false;
+ }
+
+ size_t pos = colorWriteStart;
+ if (!SkipUntilStatementEnd (src, pos))
+ return false;
+
+ // insert fog code at pos!
+ std::string color = writesToData ? "gl_FragData[0]" : "gl_FragColor";
+ if (fog == kFogExp2)
+ {
+ // fog = exp(-(density*z)^2)
+ src.insert (pos,
+ " float _patchFog = _unity_FogParams.x * _unity_FogVar;\n"
+ " _patchFog = _patchFog * _patchFog;\n"
+ " _patchFog = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ " " + color + ".rgb = mix (_unity_FogColor.rgb, " + color + ".rgb, _patchFog);\n");
+ }
+ else if (fog == kFogExp)
+ {
+ // fog = exp(-density*z)
+ src.insert (pos,
+ " float _patchFog = _unity_FogParams.y * _unity_FogVar;\n"
+ " _patchFog = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ " " + color + ".rgb = mix (_unity_FogColor.rgb, " + color + ".rgb, _patchFog);\n");
+ }
+ else if (fog == kFogLinear)
+ {
+ // fog = (end-z)/(end-start)
+ src.insert (pos,
+ " float _patchFog = clamp (_unity_FogParams.z * _unity_FogVar + _unity_FogParams.w, 0.0, 1.0);\n"
+ " " + color + ".rgb = mix (_unity_FogColor.rgb, " + color + ".rgb, _patchFog);\n");
+ }
+
+ return true;
+}
+
+bool PatchShaderFogGLSL (std::string& src, FogMode fog)
+{
+ #if DEBUG_FOG_PATCHING
+ printf_console ("GLSL fog patching: original shader:\n%s\n", src.c_str());
+ #endif
+
+ if (!InsertFogVertexCode (src))
+ return false;
+ if (!InsertFogFragmentCode (src, fog))
+ return false;
+
+ #if DEBUG_FOG_PATCHING
+ printf_console ("GLSL fog patching: after patching, fog mode %d:\n%s\n", fog, src.c_str());
+ #endif
+ return true;
+}
+
+
+
+static bool InsertFogVertexCodeGLES (std::string& src, FogMode fog, bool canUseOptimizedCode)
+{
+ size_t posWriteStart = src.find ("gl_Position");
+ if (posWriteStart == std::string::npos)
+ return false;
+
+ size_t pos = posWriteStart;
+ if (!SkipUntilStatementEnd (src, pos))
+ return false;
+
+ // TODO: remove duplication between optimized/normal fog code
+
+ // insert fog code at pos!
+ if (fog == kFogExp2)
+ {
+ // fog = exp(-(density*z)^2)
+ if(canUseOptimizedCode)
+ {
+ src.insert (pos,
+ " float _patchFog = _unity_FogParams.x * gl_Position.z;\n"
+ " _patchFog = _patchFog * _patchFog;\n"
+ " _unity_FogVar = vec4(clamp (exp2(-_patchFog), 0.0, 1.0)); _unity_FogVar.a = 1.0;\n"
+ " _unity_FogColorPreMul = _unity_FogColor * (vec4(1.0)-_unity_FogVar);\n"
+ );
+ }
+ else
+ {
+ src.insert (pos,
+ " float _patchFog = _unity_FogParams.x * gl_Position.z;\n"
+ " _patchFog = _patchFog * _patchFog;\n"
+ " _unity_FogVar = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ );
+ }
+ }
+ else if (fog == kFogExp)
+ {
+ // fog = exp(-density*z)
+ if(canUseOptimizedCode)
+ {
+ src.insert (pos,
+ " float _patchFog = _unity_FogParams.y * gl_Position.z;\n"
+ " _unity_FogVar = vec4(clamp (exp2(-_patchFog), 0.0, 1.0)); _unity_FogVar.a = 1.0;\n"
+ " _unity_FogColorPreMul = _unity_FogColor * (vec4(1.0)-_unity_FogVar);\n"
+ );
+ }
+ else
+ {
+ src.insert (pos,
+ " float _patchFog = _unity_FogParams.y * gl_Position.z;\n"
+ " _unity_FogVar = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ );
+ }
+ }
+ else if (fog == kFogLinear)
+ {
+ // fog = (end-z)/(end-start)
+ if(canUseOptimizedCode)
+ {
+ src.insert (pos,
+ " _unity_FogVar = vec4(clamp (_unity_FogParams.z * gl_Position.z + _unity_FogParams.w, 0.0, 1.0)); _unity_FogVar.a = 1.0;\n"
+ " _unity_FogColorPreMul = _unity_FogColor * (vec4(1.0)-_unity_FogVar);\n"
+ );
+ }
+ else
+ {
+ src.insert (pos,
+ " _unity_FogVar = clamp (_unity_FogParams.z * gl_Position.z + _unity_FogParams.w, 0.0, 1.0);\n"
+ );
+ }
+ }
+
+ return true;
+}
+
+static bool InsertFogFragmentCodeGLES (std::string& src, bool canUseOptimizedCode)
+{
+ bool writesToData = true; // writes to gl_FragData vs. gl_FragColor
+ size_t colorWriteStart = src.find ("gl_FragData[0]");
+ if (colorWriteStart == std::string::npos)
+ {
+ colorWriteStart = src.find ("gl_FragColor");
+ if (colorWriteStart == std::string::npos)
+ return false;
+ writesToData = false;
+ }
+
+ // iPad2 crashes with some shaders using fog (not all of them though), case 399638.
+ // Seems to avoid the crash if we don't read gl_FragColor after writing it. So instead,
+ // introduce a new local variable, replace any previous uses with it, do fog patching
+ // on it, and write out to final destination in the end.
+
+ // replace color with new variable name
+ const std::string color = writesToData ? "gl_FragData[0]" : "gl_FragColor";
+ replace_string (src, color, "_patchFragColor");
+
+ // find color output statement start & end
+ size_t posBegin, posEnd;
+ posBegin = posEnd = colorWriteStart;
+ if (!SkipUntilStatementEnd (src, posEnd))
+ return false;
+ SkipUntilStatementBegin (src, posBegin);
+
+ // insert new output variable declaration
+ src.insert (posBegin, "lowp vec4 _patchFragColor;\n");
+ posEnd += strlen("lowp vec4 _patchFragColor;\n");
+
+ // insert fog code
+ if(canUseOptimizedCode)
+ src.insert (posEnd, " _patchFragColor = _patchFragColor * _unity_FogVar + _unity_FogColorPreMul; " + color + " = _patchFragColor;\n");
+ else
+ src.insert (posEnd, " _patchFragColor.rgb = mix (_unity_FogColor.rgb, _patchFragColor.rgb, _unity_FogVar); " + color + " = _patchFragColor;\n");
+
+ return true;
+}
+
+bool PatchShaderFogGLES (std::string& srcVS, std::string& srcPS, FogMode fog, bool useOptimizedFogCode)
+{
+ #if DEBUG_FOG_PATCHING
+ printf_console ("GLES fog patching: original vertex shader:\n%s\n...and pixel shader:\n%s\n", srcVS.c_str(), srcPS.c_str());
+ #endif
+
+ const int versionVS = ParseGLSLVersion (srcVS);
+ const int versionPS = ParseGLSLVersion (srcPS);
+ const size_t startVS = SkipGLSLDirectives(srcVS);
+ const size_t startPS = SkipGLSLDirectives(srcPS);
+ const char* fogDataVS = NULL;
+ const char* fogDataPS = NULL;
+ if (useOptimizedFogCode)
+ {
+ if (versionVS >= 150)
+ {
+ fogDataVS = "uniform highp vec4 _unity_FogParams;\nuniform lowp vec4 _unity_FogColor;\nout lowp vec4 _unity_FogColorPreMul;\nout lowp vec4 _unity_FogVar;\n";
+ fogDataPS = "in lowp vec4 _unity_FogColorPreMul;\nin lowp vec4 _unity_FogVar;\n";
+ }
+ else
+ {
+ fogDataVS = "uniform highp vec4 _unity_FogParams;\nuniform lowp vec4 _unity_FogColor;\nvarying lowp vec4 _unity_FogColorPreMul;\nvarying lowp vec4 _unity_FogVar;\n";
+ fogDataPS = "varying lowp vec4 _unity_FogColorPreMul;\nvarying lowp vec4 _unity_FogVar;\n";
+ }
+ }
+ else
+ {
+ if (versionPS >= 150)
+ {
+ fogDataVS = "uniform highp vec4 _unity_FogParams;\nout lowp float _unity_FogVar;\n";
+ fogDataPS = "uniform lowp vec4 _unity_FogColor;\nin lowp float _unity_FogVar;\n";
+ }
+ else
+ {
+ fogDataVS = "uniform highp vec4 _unity_FogParams;\nvarying lowp float _unity_FogVar;\n";
+ fogDataPS = "uniform lowp vec4 _unity_FogColor;\nvarying lowp float _unity_FogVar;\n";
+ }
+ }
+ srcVS.insert (startVS, fogDataVS);
+ srcPS.insert (startPS, fogDataPS);
+
+
+ if (!InsertFogVertexCodeGLES (srcVS, fog, useOptimizedFogCode))
+ return false;
+ if (!InsertFogFragmentCodeGLES (srcPS, useOptimizedFogCode))
+ return false;
+
+ #if DEBUG_FOG_PATCHING
+ printf_console ("GLES fog patching: after patching, fog mode %d:\n%s\n...and pixel shader:\n%s\n", fog, srcVS.c_str(), srcPS.c_str());
+ #endif
+ return true;
+}
+
+static unsigned CalculateVaryingCount(const std::string& src)
+{
+ unsigned varCount = 0;
+
+ size_t varUsage = src.find("varying ");
+ while(varUsage != std::string::npos)
+ {
+ ++varCount;
+ varUsage = src.find("varying ", varUsage+7);
+ }
+
+ return varCount;
+}
+
+bool CanUseOptimizedFogCodeGLES(const std::string& srcVS)
+{
+#if UNITY_ANDROID || UNITY_IPHONE
+ // we add 2 varyings in optimized path
+ // in normal path we add one, so would be good to check it too, but we dont take "packing" into account
+ // so we just act conservatively and apply optimization only when we are sure it works
+ return (CalculateVaryingCount(srcVS) + 2 <= gGraphicsCaps.gles20.maxVaryings);
+#else
+ (void)srcVS;
+ return true;
+#endif
+}
+
+bool IsShaderParameterArray(const char* name, unsigned nameLen, int arraySize, bool* isZeroElem)
+{
+ bool zeroElem = nameLen > 3 && strcmp (name+nameLen-3, "[0]") == 0;
+ if(isZeroElem)
+ *isZeroElem = zeroElem;
+
+ return arraySize > 1 || zeroElem;
+}
+
+
+// --------------------------------------------------------------------------
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (GlslFogPatchingTests)
+{
+ struct GlslPatchTestFixture
+ {
+ std::string vs, fs, s;
+ std::string expvs, expfs, exps;
+ bool DoPatching(FogMode mode, bool checkOptimizedGLESCode)
+ {
+ s = "#ifdef VERTEX\n" + vs + "\n#endif\n#ifdef FRAGMENT\n" + fs + "\n#endif\n";
+ bool ok = true;
+ ok &= PatchShaderFogGLSL (s, mode);
+ ok &= PatchShaderFogGLES (vs, fs, mode, checkOptimizedGLESCode);
+ return ok;
+ }
+ };
+
+ TEST(SkipGLSLDirectivesWorks)
+ {
+ const char* s;
+ CHECK_EQUAL(0, SkipGLSLDirectives("void")); // no directives
+ CHECK_EQUAL(2, SkipGLSLDirectives(" void")); // skipped space
+ CHECK_EQUAL(2, SkipGLSLDirectives("\n\nvoid")); // skipped newlines
+ CHECK_EQUAL(12, SkipGLSLDirectives("#pragma foo\n")); // skipped whole line
+ CHECK_EQUAL(14, SkipGLSLDirectives(" #pragma foo\n")); // skipped whole line
+ CHECK_EQUAL(15, SkipGLSLDirectives("#pragma foo\r\n\r\n")); // skipped whole line, including various newlines
+ CHECK_EQUAL(24, SkipGLSLDirectives("#pragma foo\n#pragma foo\nvoid")); // skipped two lines
+ }
+
+ TEST(ParseGLSLVersionWorks)
+ {
+ CHECK_EQUAL(0, ParseGLSLVersion(""));
+ CHECK_EQUAL(0, ParseGLSLVersion("void"));
+ CHECK_EQUAL(0, ParseGLSLVersion(" # pragma"));
+ CHECK_EQUAL(0, ParseGLSLVersion("#version"));
+
+ CHECK_EQUAL(120, ParseGLSLVersion("#version 120"));
+ CHECK_EQUAL(200, ParseGLSLVersion(" # version 200"));
+ }
+
+ TEST_FIXTURE(GlslPatchTestFixture,PatchSkipsVersionAndExtensionDirectives)
+ {
+ vs =
+ "#version 120\n"
+ "#extension bar : enable\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "}";
+ fs =
+ "#extension bar : enable\n"
+ "void main() {\n"
+ " gl_FragColor = vec4(1,2,3,4);\n"
+ "}";
+ CHECK(DoPatching(kFogExp2,true));
+ expvs =
+ "#version 120\n"
+ "#extension bar : enable\n"
+ "uniform highp vec4 _unity_FogParams;\n";
+ expfs =
+ "#extension bar : enable\n"
+ "varying lowp vec4 _unity_FogColorPreMul;\n";
+ exps =
+ "#ifdef VERTEX\n"
+ "#version 120\n"
+ "#extension bar : enable\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "_unity_FogVar = gl_Position.z;\n"
+ "}\n"
+ "#endif\n"
+ "#ifdef FRAGMENT\n"
+ "#extension bar : enable\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_FragColor = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.x * _unity_FogVar;\n"
+ " _patchFog = _patchFog * _patchFog;\n"
+ " _patchFog = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ " gl_FragColor.rgb = mix (_unity_FogColor.rgb, gl_FragColor.rgb, _patchFog);\n"
+ "}\n"
+ "#endif\n";
+ CHECK (BeginsWith(vs, expvs));
+ CHECK (BeginsWith(fs, expfs));
+ CHECK_EQUAL (exps, s);
+ }
+
+ TEST_FIXTURE(GlslPatchTestFixture,PatchTakesVersionIntoAccount)
+ {
+ vs =
+ "#version 300 es\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "}";
+ fs =
+ " #version 420\n"
+ "void main() {\n"
+ " gl_FragColor = vec4(1,2,3,4);\n"
+ "}";
+ CHECK(DoPatching(kFogExp2,true));
+ expvs =
+ "#version 300 es\n"
+ "uniform highp vec4 _unity_FogParams;\n"
+ "uniform lowp vec4 _unity_FogColor;\n"
+ "out lowp vec4 _unity_FogColorPreMul;\n"
+ "out lowp vec4 _unity_FogVar;\n";
+ expfs =
+ " #version 420\n"
+ "in lowp vec4 _unity_FogColorPreMul;\n"
+ "in lowp vec4 _unity_FogVar;\n";
+ exps =
+ "#ifdef VERTEX\n"
+ "#version 300 es\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "out float _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "_unity_FogVar = gl_Position.z;\n"
+ "}\n"
+ "#endif\n"
+ "#ifdef FRAGMENT\n"
+ " #version 420\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "in float _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_FragColor = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.x * _unity_FogVar;\n"
+ " _patchFog = _patchFog * _patchFog;\n"
+ " _patchFog = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ " gl_FragColor.rgb = mix (_unity_FogColor.rgb, gl_FragColor.rgb, _patchFog);\n"
+ "}\n"
+ "#endif\n";
+ CHECK (BeginsWith(vs, expvs));
+ CHECK (BeginsWith(fs, expfs));
+ CHECK_EQUAL (exps, s);
+ }
+
+ TEST_FIXTURE(GlslPatchTestFixture,PatchExp2WriteToColorOptimized)
+ {
+ vs =
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "}";
+ fs =
+ "void main() {\n"
+ " gl_FragColor = vec4(1,2,3,4);\n"
+ "}";
+ CHECK(DoPatching(kFogExp2,true));
+ expvs =
+ "uniform highp vec4 _unity_FogParams;\n"
+ "uniform lowp vec4 _unity_FogColor;\n"
+ "varying lowp vec4 _unity_FogColorPreMul;\n"
+ "varying lowp vec4 _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.x * gl_Position.z;\n"
+ " _patchFog = _patchFog * _patchFog;\n"
+ " _unity_FogVar = vec4(clamp (exp2(-_patchFog), 0.0, 1.0)); _unity_FogVar.a = 1.0;\n"
+ " _unity_FogColorPreMul = _unity_FogColor * (vec4(1.0)-_unity_FogVar);\n"
+ "}";
+ expfs =
+ "varying lowp vec4 _unity_FogColorPreMul;\n"
+ "varying lowp vec4 _unity_FogVar;\n"
+ "void main() {\n"
+ " lowp vec4 _patchFragColor;\n"
+ "_patchFragColor = vec4(1,2,3,4);\n"
+ " _patchFragColor = _patchFragColor * _unity_FogVar + _unity_FogColorPreMul; gl_FragColor = _patchFragColor;\n"
+ "}";
+ exps =
+ "#ifdef VERTEX\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "_unity_FogVar = gl_Position.z;\n"
+ "}\n"
+ "#endif\n"
+ "#ifdef FRAGMENT\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_FragColor = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.x * _unity_FogVar;\n"
+ " _patchFog = _patchFog * _patchFog;\n"
+ " _patchFog = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ " gl_FragColor.rgb = mix (_unity_FogColor.rgb, gl_FragColor.rgb, _patchFog);\n"
+ "}\n"
+ "#endif\n";
+ CHECK_EQUAL (expvs, vs);
+ CHECK_EQUAL (expfs, fs);
+ CHECK_EQUAL (exps, s);
+ }
+
+ TEST_FIXTURE(GlslPatchTestFixture,PatchExp2WriteToColor)
+ {
+ vs =
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "}";
+ fs =
+ "void main() {\n"
+ " gl_FragColor = vec4(1,2,3,4);\n"
+ "}";
+ CHECK(DoPatching(kFogExp2,false));
+ expvs =
+ "uniform highp vec4 _unity_FogParams;\n"
+ "varying lowp float _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.x * gl_Position.z;\n"
+ " _patchFog = _patchFog * _patchFog;\n"
+ " _unity_FogVar = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ "}";
+ expfs =
+ "uniform lowp vec4 _unity_FogColor;\n"
+ "varying lowp float _unity_FogVar;\n"
+ "void main() {\n"
+ " lowp vec4 _patchFragColor;\n"
+ "_patchFragColor = vec4(1,2,3,4);\n"
+ " _patchFragColor.rgb = mix (_unity_FogColor.rgb, _patchFragColor.rgb, _unity_FogVar); gl_FragColor = _patchFragColor;\n"
+ "}";
+ exps =
+ "#ifdef VERTEX\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "_unity_FogVar = gl_Position.z;\n"
+ "}\n"
+ "#endif\n"
+ "#ifdef FRAGMENT\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_FragColor = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.x * _unity_FogVar;\n"
+ " _patchFog = _patchFog * _patchFog;\n"
+ " _patchFog = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ " gl_FragColor.rgb = mix (_unity_FogColor.rgb, gl_FragColor.rgb, _patchFog);\n"
+ "}\n"
+ "#endif\n";
+ CHECK_EQUAL (expvs, vs);
+ CHECK_EQUAL (expfs, fs);
+ CHECK_EQUAL (exps, s);
+ }
+
+ TEST_FIXTURE(GlslPatchTestFixture,PatchExpWriteToDataWithStuffAroundOptimized)
+ {
+ vs =
+ "varying float foo;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ " foo = 1.0;\n"
+ "}";
+ fs =
+ "varying float foo;\n"
+ "void main() {\n"
+ " foo = 2.0;\n"
+ " gl_FragData[0] = vec4(1,2,3,4);\n"
+ "}";
+ CHECK(DoPatching(kFogExp,true));
+ expvs =
+ "uniform highp vec4 _unity_FogParams;\n"
+ "uniform lowp vec4 _unity_FogColor;\n"
+ "varying lowp vec4 _unity_FogColorPreMul;\n"
+ "varying lowp vec4 _unity_FogVar;\n"
+ "varying float foo;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.y * gl_Position.z;\n"
+ " _unity_FogVar = vec4(clamp (exp2(-_patchFog), 0.0, 1.0)); _unity_FogVar.a = 1.0;\n"
+ " _unity_FogColorPreMul = _unity_FogColor * (vec4(1.0)-_unity_FogVar);\n"
+ " foo = 1.0;\n"
+ "}";
+ expfs =
+ "varying lowp vec4 _unity_FogColorPreMul;\n"
+ "varying lowp vec4 _unity_FogVar;\n"
+ "varying float foo;\n"
+ "void main() {\n"
+ " foo = 2.0;\n"
+ " lowp vec4 _patchFragColor;\n"
+ "_patchFragColor = vec4(1,2,3,4);\n"
+ " _patchFragColor = _patchFragColor * _unity_FogVar + _unity_FogColorPreMul; gl_FragData[0] = _patchFragColor;\n"
+ "}";
+ exps =
+ "#ifdef VERTEX\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "varying float foo;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "_unity_FogVar = gl_Position.z;\n"
+ " foo = 1.0;\n"
+ "}\n"
+ "#endif\n"
+ "#ifdef FRAGMENT\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "varying float foo;\n"
+ "void main() {\n"
+ " foo = 2.0;\n"
+ " gl_FragData[0] = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.y * _unity_FogVar;\n"
+ " _patchFog = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ " gl_FragData[0].rgb = mix (_unity_FogColor.rgb, gl_FragData[0].rgb, _patchFog);\n"
+ "}\n"
+ "#endif\n";
+ CHECK_EQUAL (expvs, vs);
+ CHECK_EQUAL (expfs, fs);
+ CHECK_EQUAL (exps, s);
+ }
+
+ TEST_FIXTURE(GlslPatchTestFixture,PatchExpWriteToDataWithStuffAround)
+ {
+ vs =
+ "varying float foo;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ " foo = 1.0;\n"
+ "}";
+ fs =
+ "varying float foo;\n"
+ "void main() {\n"
+ " foo = 2.0;\n"
+ " gl_FragData[0] = vec4(1,2,3,4);\n"
+ "}";
+ CHECK(DoPatching(kFogExp,false));
+ expvs =
+ "uniform highp vec4 _unity_FogParams;\n"
+ "varying lowp float _unity_FogVar;\n"
+ "varying float foo;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.y * gl_Position.z;\n"
+ " _unity_FogVar = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ " foo = 1.0;\n"
+ "}";
+ expfs =
+ "uniform lowp vec4 _unity_FogColor;\n"
+ "varying lowp float _unity_FogVar;\n"
+ "varying float foo;\n"
+ "void main() {\n"
+ " foo = 2.0;\n"
+ " lowp vec4 _patchFragColor;\n"
+ "_patchFragColor = vec4(1,2,3,4);\n"
+ " _patchFragColor.rgb = mix (_unity_FogColor.rgb, _patchFragColor.rgb, _unity_FogVar); gl_FragData[0] = _patchFragColor;\n"
+ "}";
+ exps =
+ "#ifdef VERTEX\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "varying float foo;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "_unity_FogVar = gl_Position.z;\n"
+ " foo = 1.0;\n"
+ "}\n"
+ "#endif\n"
+ "#ifdef FRAGMENT\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "varying float foo;\n"
+ "void main() {\n"
+ " foo = 2.0;\n"
+ " gl_FragData[0] = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.y * _unity_FogVar;\n"
+ " _patchFog = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ " gl_FragData[0].rgb = mix (_unity_FogColor.rgb, gl_FragData[0].rgb, _patchFog);\n"
+ "}\n"
+ "#endif\n";
+ CHECK_EQUAL (expvs, vs);
+ CHECK_EQUAL (expfs, fs);
+ CHECK_EQUAL (exps, s);
+ }
+
+ TEST(CalculateVaryingCount)
+ {
+ // TODO: should we handle packing? for now we dont need it, but point for the future
+ std::string test = "varying float foo; varying float foo2;";
+ CHECK_EQUAL(CalculateVaryingCount(test), 2);
+ }
+} // SUITE
+
+#endif // ENABLE_UNIT_TESTS
diff --git a/Runtime/Utilities/GLSLUtilities.h b/Runtime/Utilities/GLSLUtilities.h
new file mode 100644
index 0000000..147339d
--- /dev/null
+++ b/Runtime/Utilities/GLSLUtilities.h
@@ -0,0 +1,30 @@
+#ifndef GLSLUTILITIES_H
+#define GLSLUTILITIES_H
+
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+class ShaderErrors;
+
+enum GLSLErrorType
+{
+ kErrorCompileVertexShader,
+ kErrorCompileFragShader,
+ kErrorLinkProgram,
+ kErrorCount
+};
+void OutputGLSLShaderError (const char* log, GLSLErrorType errorType, ShaderErrors& outErrors);
+
+bool CanUseOptimizedFogCodeGLES(const std::string& srcVS);
+
+bool PatchShaderFogGLSL (std::string& src, FogMode fog);
+bool PatchShaderFogGLES (std::string& srcVS, std::string& srcPS, FogMode fog, bool useOptimizedFogCode);
+
+// TODO: might be better to share whole parm filling procedure, but good enough for now
+// TODO: as for now all gles impls we know returns first elem name
+// in case of using array[0] only - arraySize would be 1
+// in case of using array[1] only - arraySize would be 2 and uniform name would be array[0]
+// but this behaviour might change in future
+bool IsShaderParameterArray(const char* name, unsigned nameLen, int arraySize, bool* isZeroElem=0);
+
+
+#endif
diff --git a/Runtime/Utilities/GUID.cpp b/Runtime/Utilities/GUID.cpp
new file mode 100644
index 0000000..9fe4f7f
--- /dev/null
+++ b/Runtime/Utilities/GUID.cpp
@@ -0,0 +1,241 @@
+#include "UnityPrefix.h"
+#include "GUID.h"
+#include "LogAssert.h"
+#include "StaticAssert.h"
+#include <string>
+
+#if UNITY_IPHONE
+#include <CoreFoundation/CFUUID.h>
+#endif
+
+#if UNITY_WIN
+#include <ObjBase.h>
+#endif
+
+#if UNITY_XENON
+#include <comdecl.h>
+#include <time.h>
+#endif
+
+using namespace std;
+
+#if UNITY_ANDROID
+#include <fcntl.h>
+#include <unistd.h>
+#elif UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+#include <fcntl.h>
+#include <sys/time.h>
+#endif
+
+#if UNITY_PS3
+#include <sys/random_number.h>
+#endif
+
+#if UNITY_HAVE_GUID_INIT
+void UnityGUID::Init ()
+{
+#if UNITY_OSX || UNITY_IPHONE
+ CompileTimeAssert (sizeof (CFUUIDBytes) == 16, "GIUD shoud be 16 bytes");
+#elif UNITY_WIN || UNITY_XENON
+ CompileTimeAssert (sizeof (GUID) == 16, "GIUD shoud be 16 bytes");
+#endif
+
+
+#if UNITY_OSX || UNITY_IPHONE
+
+ CFUUIDRef myUUID;
+ myUUID = CFUUIDCreate (kCFAllocatorDefault);
+ CFUUIDBytes bytes = CFUUIDGetUUIDBytes (myUUID);
+ memcpy (data, &bytes, sizeof (CFUUIDBytes));
+ CFRelease (myUUID);
+
+#elif UNITY_ANDROID || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+
+ #pragma message("Android GUID/UUID creation NOT tested!")
+ {
+ unsigned char bytes[16];
+
+ struct timeval tv ;
+ gettimeofday(&tv, 0) ;
+ int fd = open("/dev/urandom", O_RDONLY);
+ if (fd == -1)
+ fd = open("/dev/random", O_RDONLY | O_NONBLOCK) ;
+
+ srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec) ;
+
+ // Crank the random number generator a few times
+ gettimeofday(&tv, 0) ;
+ for (int i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--) rand() ;
+
+ int n = 16 ;
+ int lose_counter = 0 ;
+ unsigned char *cp = (unsigned char *) bytes ;
+ while (n > 0 && fd != -1)
+ {
+ int i = read(fd, cp, n) ;
+ if (i <= 0)
+ {
+ if (lose_counter++ > 16) break ;
+ continue ;
+ }
+ n -= i ;
+ cp += i ;
+ lose_counter = 0 ;
+ }
+
+ if (fd != -1)
+ close(fd);
+
+ // We do this all the time, but this is the only source of
+ // randomness if /dev/random/urandom is out to lunch.
+ for (unsigned int i = 0; i<16; ++i)
+ bytes[i] ^= (rand() >> 7) & 0xFF ;
+
+ // clock_seq is bytes #8 and #9
+ uint16_t clock_seq = (uint16_t(bytes[8]) << 8) + bytes[9] ;
+ clock_seq = (clock_seq & 0x3FFF) | 0x8000 ;
+ bytes[8] = clock_seq >> 8 ;
+ bytes[9] = clock_seq & 0xFF ;
+
+ // time_hi_and_version is bytes #6 and #7
+ uint16_t time_hi_and_version = (uint16_t(bytes[6]) << 8) + bytes[7] ;
+ time_hi_and_version = (time_hi_and_version & 0x0FFF) | 0x4000 ;
+ bytes[6] = time_hi_and_version >> 8 ;
+ bytes[7] = time_hi_and_version & 0xFF ;
+
+ memcpy (data, &bytes, sizeof (data));
+ }
+#elif UNITY_WIN
+
+ GUID guid;
+ ::CoCreateGuid( &guid );
+ memcpy( data, &guid, sizeof(guid) );
+
+#elif UNITY_PS3
+
+ sys_get_random_number( &data, sizeof(UnityGUID) );
+
+#elif UNITY_XENON
+
+ // RFC 4122, section 4.4 (Algorithms for Creating a UUID from Truly Random or Pseudo-Random Numbers)
+ // http://www.ietf.org/rfc/rfc4122.txt
+ GUID guid;
+ unsigned char* bytes = (unsigned char*)&guid;
+
+ srand( time(NULL) );
+ for (unsigned int i = 0; i<16; ++i)
+ bytes[i] ^= (rand() >> 7) & 0xFF;
+
+ // V4
+ guid.Data3 &= 0x0fff;
+ guid.Data3 |= (4 << 12);
+ guid.Data4[0] &= 0x3f;
+ guid.Data4[0] |= 0x80;
+
+ memcpy( data, &guid, sizeof(guid) );
+
+#else
+
+ ErrorString("GUID::Init() not implemented.");
+
+#endif
+}
+#endif
+
+bool CompareGUIDStringLess (const UnityGUID& lhsGUID, const UnityGUID& rhsGUID)
+{
+ char lhs[32];
+ char rhs[32];
+ GUIDToString(lhsGUID, lhs);
+ GUIDToString(rhsGUID, rhs);
+
+ for (int i=0;i<32;i++)
+ {
+ if (lhs[i] != rhs[i])
+ return lhs[i] < rhs[i];
+ }
+
+ return false;
+}
+
+const char kHexToLiteral[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+string GUIDToString(const UnityGUID& guid)
+{
+ char name[kGUIDStringLength+1];
+ GUIDToString (guid, name);
+ name[kGUIDStringLength] = '\0';
+ return name;
+}
+
+void GUIDToString(const UnityGUID& guid, char* name)
+{
+ for (int i=0;i<4;i++)
+ {
+ for (int j=8;j--;)
+ {
+ UInt32 cur = guid.data[i];
+ cur >>= (j * 4);
+ cur &= 0xF;
+ name[i * 8 + j] = kHexToLiteral[cur];
+ }
+ }
+}
+
+UnityGUID StringToGUID (const std::string& guidString)
+{
+ return StringToGUID(guidString.c_str(), guidString.size());
+}
+
+UnityGUID StringToGUID (const char* guidString, size_t length)
+{
+ if (length != kGUIDStringLength)
+ return UnityGUID ();
+
+ // Initialize hex char to int table
+ static char s_LiteralToHex[255];
+ static bool s_IsInitialized = false;
+ if (!s_IsInitialized)
+ {
+ for (int i=0;i<255;i++)
+ s_LiteralToHex[i] = -1;
+ s_LiteralToHex['0'] = 0;
+ s_LiteralToHex['1'] = 1;
+ s_LiteralToHex['2'] = 2;
+ s_LiteralToHex['3'] = 3;
+ s_LiteralToHex['4'] = 4;
+ s_LiteralToHex['5'] = 5;
+ s_LiteralToHex['6'] = 6;
+ s_LiteralToHex['7'] = 7;
+ s_LiteralToHex['8'] = 8;
+ s_LiteralToHex['9'] = 9;
+ s_LiteralToHex['a'] = 10;
+ s_LiteralToHex['b'] = 11;
+ s_LiteralToHex['c'] = 12;
+ s_LiteralToHex['d'] = 13;
+ s_LiteralToHex['e'] = 14;
+ s_LiteralToHex['f'] = 15;
+ s_IsInitialized = true;
+ }
+
+ // Convert every hex char into an int [0...16]
+ int hex[kGUIDStringLength];
+ for (int i=0;i<kGUIDStringLength;i++)
+ hex[i] = s_LiteralToHex[guidString[i]];
+
+ UnityGUID guid;
+ for (int i=0;i<4;i++)
+ {
+ UInt32 cur = 0;
+ for (int j=8;j--;)
+ {
+ int curHex = hex[i * 8 + j];
+ if (curHex == -1)
+ return UnityGUID ();
+
+ cur |= (curHex << (j * 4));
+ }
+ guid.data[i] = cur;
+ }
+ return guid;
+}
diff --git a/Runtime/Utilities/GUID.h b/Runtime/Utilities/GUID.h
new file mode 100644
index 0000000..e310c1f
--- /dev/null
+++ b/Runtime/Utilities/GUID.h
@@ -0,0 +1,73 @@
+#ifndef UNITY_GUID_H
+#define UNITY_GUID_H
+
+#include "Runtime/Serialize/SerializeUtility.h"
+#include <string>
+
+enum { kGUIDStringLength = 32 };
+
+#define UNITY_HAVE_GUID_INIT (UNITY_OSX || UNITY_WIN || UNITY_IPHONE || UNITY_ANDROID || UNITY_LINUX || UNITY_PEPPER || UNITY_PS3 || UNITY_XENON || UNITY_BB10 || UNITY_TIZEN)
+
+// To setup the unique identifier use Init ().
+// You can compare it against other unique identifiers
+// It is guaranteed to be unique over space and time
+//
+// Called UnityGUID because Visual Studio really does not like structs named GUID!
+struct UnityGUID
+{
+ UInt32 data[4];
+
+ // Used to be called GUID, so for serialization it has the old name
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (GUID)
+
+ UnityGUID (UInt32 a, UInt32 b, UInt32 c, UInt32 d) { data[0] = a; data[1] = b; data[2] = c; data[3] = d; }
+ UnityGUID () { data[0] = 0; data[1] = 0; data[2] = 0; data[3] = 0; }
+
+ bool operator == (const UnityGUID& rhs) const {
+ return data[0] == rhs.data[0] && data[1] == rhs.data[1] && data[2] == rhs.data[2] && data[3] == rhs.data[3];
+ }
+ bool operator != (const UnityGUID& rhs) const { return !(*this == rhs); }
+
+ bool operator < (const UnityGUID& rhs) const { return CompareGUID (*this, rhs) == -1; }
+ friend int CompareGUID (const UnityGUID& lhs, const UnityGUID& rhs);
+
+ // Use this instead of guid != UnityGUID() -- Will often result in self-documented code
+ bool IsValid() const { return data[0] != 0 || data[1] != 0 || data[2] != 0 || data[3] != 0; }
+
+#if UNITY_HAVE_GUID_INIT
+ void Init ();
+#endif
+};
+
+std::string GUIDToString(const UnityGUID& guid);
+void GUIDToString(const UnityGUID& guid, char* string);
+
+UnityGUID StringToGUID (const std::string& guidString);
+UnityGUID StringToGUID (const char* guidString, size_t stringLength);
+
+inline int CompareGUID (const UnityGUID& lhs, const UnityGUID& rhs)
+{
+ for (int i=0;i<4;i++)
+ {
+ if (lhs.data[i] < rhs.data[i])
+ return -1;
+ if (lhs.data[i] > rhs.data[i])
+ return 1;
+ }
+ return 0;
+}
+
+bool CompareGUIDStringLess (const UnityGUID& lhs, const UnityGUID& rhs);
+
+template<class TransferFunction>
+void UnityGUID::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (data[0]);
+ TRANSFER (data[1]);
+ TRANSFER (data[2]);
+ TRANSFER (data[3]);
+}
+
+extern const char kHexToLiteral[16];
+
+#endif
diff --git a/Runtime/Utilities/GlobalPreferences.cpp b/Runtime/Utilities/GlobalPreferences.cpp
new file mode 100644
index 0000000..fb741b0
--- /dev/null
+++ b/Runtime/Utilities/GlobalPreferences.cpp
@@ -0,0 +1,151 @@
+#include "UnityPrefix.h"
+
+#include "Runtime/Utilities/PlayerPrefs.h"
+
+#if UNITY_PLUGIN
+#if UNITY_OSX
+#include "OSXWebPluginUtility.h"
+#endif // #if UNITY_OSX
+#include "PlatformDependent/CommonWebPlugin/WebPluginUtility.h"
+#endif // #if UNITY_PLUGIN
+
+#include "GlobalPreferences.h"
+
+
+#if UNITY_WIN
+#include "PlatformDependent/Win/Registry.h"
+#if UNITY_EDITOR
+const char* kRegistryPath = "Software\\Unity\\UnityEditor";
+#elif UNITY_STANDALONE
+const char* kRegistryPath = "Software\\Unity\\UnityStandalone";
+#else
+const char* kRegistryPath = "Software\\Unity\\WebPlayer";
+#endif
+#endif // #if UNITY_WIN
+
+#if UNITY_OSX
+#if UNITY_EDITOR
+const char* kPrefsAppID = "com.unity3d.UnityEditor";
+#elif UNITY_STANDALONE
+const char* kPrefsAppID = "com.unity3d.UnityStandalone";
+#else
+const char* kPrefsAppID = "com.unity3d.UnityWebPlayer";
+#endif
+#endif // #if UNITY_OSX
+
+#if UNITY_LINUX
+#include "PlatformDependent/Linux/XmlOptions.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+
+const char* kPrefsFileName = "global.prefs";
+#endif // #if UNITY_LINUX
+
+
+std::string GetGlobalPreference(const char *key)
+{
+ std::string result;
+
+ #if UNITY_WIN && !UNITY_WINRT
+ return registry::getString( kRegistryPath, key, "" );
+
+ #elif UNITY_OSX
+ CFStringRef cfPrefsAppID = CFStringCreateWithCString (NULL, kPrefsAppID, kCFStringEncodingASCII);
+ CFStringRef cfKey = CFStringCreateWithCString (NULL, key, kCFStringEncodingASCII);
+ CFStringRef val = (CFStringRef)CFPreferencesCopyAppValue (cfKey, cfPrefsAppID);
+ if (val)
+ {
+ result = CFStringToString(val);
+ CFRelease (val);
+ }
+ CFRelease (cfPrefsAppID);
+ CFRelease (cfKey);
+
+ #elif UNITY_LINUX
+ std::string path = AppendPathName (GetUserConfigFolder (), kPrefsFileName);
+ XmlOptions options;
+ if (options.Load (path))
+ {
+ result = options.GetString (key, "");
+ }
+
+ #elif GAMERELEASE
+ return PlayerPrefs::GetString (key);
+
+ #else
+ // TODO
+ #endif
+ return result;
+}
+
+bool GetGlobalBoolPreference(const char *key, bool defaultValue)
+{
+ std::string const pref = GetGlobalPreference (key);
+ if (pref == "yes")
+ return true;
+ if (pref == "no")
+ return false;
+ return defaultValue;
+}
+
+void SetGlobalPreference(const char *key, std::string value)
+{
+ #if UNITY_WIN && !UNITY_WINRT
+ registry::setString( kRegistryPath, key, value.c_str() );
+
+ #elif UNITY_OSX
+ CFStringRef cfPrefsAppID = CFStringCreateWithCString (NULL, kPrefsAppID, kCFStringEncodingASCII);
+ CFStringRef cfKey = CFStringCreateWithCString (NULL, key, kCFStringEncodingASCII);
+ CFStringRef cfValue = CFStringCreateWithCString (NULL, value.c_str(), kCFStringEncodingASCII);
+
+ CFPreferencesSetAppValue( cfKey, cfValue, cfPrefsAppID );
+ CFPreferencesAppSynchronize( cfPrefsAppID );
+
+ #elif UNITY_LINUX
+ std::string path = AppendPathName (GetUserConfigFolder (), kPrefsFileName);
+ XmlOptions options;
+ options.Load (path);
+ options.SetString (key, value);
+ options.Save (path);
+
+ #elif GAMERELEASE
+ PlayerPrefs::SetString (key, value);
+
+ #else
+ // TODO
+ #endif
+}
+
+void SetGlobalBoolPreference(const char *key, bool value)
+{
+ SetGlobalPreference (key, value?"yes":"no");
+}
+
+
+#if WEBPLUG && !UNITY_PLUGIN
+#include "Runtime/Misc/PlayerSettings.h"
+
+std::string GetStrippedPlayerDomain ()
+{
+ std::string currentDomain = GetPlayerSettings().absoluteURL;
+ if (currentDomain.find("http://") == 0 || currentDomain.find("https://") == 0)
+ {
+ //remove http://
+ if (currentDomain.find("http://") == 0)
+ currentDomain.erase(0, 7);
+ else if (currentDomain.find("https://") == 0)
+ currentDomain.erase(0, 8);
+
+ //remove path
+ std::string::size_type pos = currentDomain.find("/", 0);
+ if (pos != std::string::npos)
+ currentDomain.erase(currentDomain.begin() + pos, currentDomain.end());
+
+ //remove port if present
+ pos = currentDomain.find(":", 0);
+ if (pos != std::string::npos)
+ currentDomain.erase(currentDomain.begin() + pos, currentDomain.end());
+ }
+ return currentDomain;
+}
+#endif // #if WEBPLUG && !UNITY_PLUGIN
diff --git a/Runtime/Utilities/GlobalPreferences.h b/Runtime/Utilities/GlobalPreferences.h
new file mode 100644
index 0000000..b0ca58f
--- /dev/null
+++ b/Runtime/Utilities/GlobalPreferences.h
@@ -0,0 +1,15 @@
+#ifndef GLOBALPREFERENCES_H
+#define GLOBALPREFERENCES_H
+
+#if WEBPLUG
+std::string GetStrippedPlayerDomain ();
+#endif
+
+// Used for hardware stats and webplayer settings.
+std::string GetGlobalPreference(const char *key);
+bool GetGlobalBoolPreference(const char *key, bool defaultValue);
+
+void SetGlobalPreference(const char *key, std::string value);
+void SetGlobalBoolPreference(const char *key, bool value);
+
+#endif \ No newline at end of file
diff --git a/Runtime/Utilities/Hash128.cpp b/Runtime/Utilities/Hash128.cpp
new file mode 100644
index 0000000..8e8cb68
--- /dev/null
+++ b/Runtime/Utilities/Hash128.cpp
@@ -0,0 +1,33 @@
+#include "UnityPrefix.h"
+#include "Hash128.h"
+
+std::string Hash128ToString(const Hash128& digest)
+{
+ std::string name;
+ name.resize (32);
+ for(int i=0; i < 16; i++)
+ sprintf(&name[i*2],"%02hhx",digest.hashData.bytes[i]);
+ return name;
+}
+
+static inline int HexToNumber(char c)
+{
+ if( c >= '0' && c <= '9')
+ return c-'0';
+ if( c >= 'a' && c <= 'f')
+ return c-'a'+10;
+ if( c >= 'A' && c <= 'F')
+ return c-'A'+10;
+ return 0;
+}
+
+
+Hash128 StringToHash128(const std::string& name)
+{
+ Hash128 digest;
+ int end = name.size() > 32 ? 16 : name.size()/2;
+ for( int i = 0; i < end; ++i ) {
+ digest.hashData.bytes[i] = HexToNumber(name[i*2]) * 16 + HexToNumber(name[i*2+1]);
+ }
+ return digest;
+} \ No newline at end of file
diff --git a/Runtime/Utilities/Hash128.h b/Runtime/Utilities/Hash128.h
new file mode 100644
index 0000000..ec659fe
--- /dev/null
+++ b/Runtime/Utilities/Hash128.h
@@ -0,0 +1,49 @@
+#pragma once
+
+#include "Runtime/Serialize/SerializeUtility.h"
+
+// Holds a 128 bit hash value
+struct Hash128
+{
+ union
+ {
+ unsigned char bytes[16];
+ UInt64 u64[2];
+ UInt32 u32[4];
+ } hashData ;
+
+ friend inline bool operator == (const Hash128& lhs, const Hash128& rhs) { return lhs.hashData.u64[0] == rhs.hashData.u64[0] && lhs.hashData.u64[1] == rhs.hashData.u64[1]; }
+ friend inline bool operator != (const Hash128& lhs, const Hash128& rhs) { return lhs.hashData.u64[0] != rhs.hashData.u64[0] || lhs.hashData.u64[1] != rhs.hashData.u64[1]; }
+
+ Hash128 () { hashData.u64[0] = 0; hashData.u64[1] = 0; }
+
+ DECLARE_SERIALIZE_NO_PPTR (Hash128)
+};
+
+// String conversion
+std::string Hash128ToString(const Hash128& digest);
+Hash128 StringToHash128(const std::string& name);
+
+// Serialization
+template<class T>
+void Hash128::Transfer (T& transfer)
+{
+ UInt8* bytes = hashData.bytes;
+
+ TRANSFER(bytes[0]);
+ TRANSFER(bytes[1]);
+ TRANSFER(bytes[2]);
+ TRANSFER(bytes[3]);
+ TRANSFER(bytes[4]);
+ TRANSFER(bytes[5]);
+ TRANSFER(bytes[6]);
+ TRANSFER(bytes[7]);
+ TRANSFER(bytes[8]);
+ TRANSFER(bytes[9]);
+ TRANSFER(bytes[10]);
+ TRANSFER(bytes[11]);
+ TRANSFER(bytes[12]);
+ TRANSFER(bytes[13]);
+ TRANSFER(bytes[14]);
+ TRANSFER(bytes[15]);
+} \ No newline at end of file
diff --git a/Runtime/Utilities/HashFunctions.h b/Runtime/Utilities/HashFunctions.h
new file mode 100644
index 0000000..0f1f465
--- /dev/null
+++ b/Runtime/Utilities/HashFunctions.h
@@ -0,0 +1,87 @@
+#ifndef __HASH_FUNCTIONS_H
+#define __HASH_FUNCTIONS_H
+
+#include <string>
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Utilities/dense_hash_map.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+
+struct InstanceIDHashFunctor
+{
+ inline size_t operator()(SInt32 x) const {
+
+ UInt32 a = static_cast<UInt32> (x);
+ a = (a+0x7ed55d16) + (a<<12);
+ a = (a^0xc761c23c) ^ (a>>19);
+ a = (a+0x165667b1) + (a<<5);
+ a = (a+0xd3a2646c) ^ (a<<9);
+ a = (a+0xfd7046c5) + (a<<3);
+ a = (a^0xb55a4f09) ^ (a>>16);
+
+ return a;
+ }
+};
+
+typedef pair<const SInt32, Object*> InstanceIdToObjectPair;
+struct InstanceIdToObjectPtrHashMap :
+ dense_hash_map<SInt32, Object*, InstanceIDHashFunctor, std::equal_to<SInt32>, STL_ALLOCATOR( kMemSTL, InstanceIdToObjectPair ) >
+{
+ typedef dense_hash_map<SInt32, Object*, InstanceIDHashFunctor, std::equal_to<SInt32>, STL_ALLOCATOR( kMemSTL, InstanceIdToObjectPair )> base_t;
+ InstanceIdToObjectPtrHashMap (int n)
+ : base_t (n)
+ {
+ set_empty_key (-1);
+ set_deleted_key (-2);
+ }
+};
+
+// http://www.cse.yorku.ca/~oz/hash.html
+// Other hashes here: http://burtleburtle.net/bob/
+struct djb2_hash
+{
+ inline size_t operator()(const std::string& str) const
+ {
+ unsigned long hash = 5381;
+ char c;
+ const char* s = str.c_str ();
+
+ while ((c = *s++))
+ hash = ((hash << 5) + hash) ^ c;
+
+ return hash;
+ }
+};
+
+#if UNITY_EDITOR
+struct djb2_hash_lowercase
+{
+ inline size_t operator() (const std::string& str) const
+ {
+ unsigned long hash = 5381;
+ char c;
+#if UNITY_OSX
+ const char* s = NULL;
+ bool ascii = !RequiresNormalization (str);
+ if (ascii)
+ s = ToLower (str).c_str();
+ else
+ s = ToLower (NormalizeUnicode (str, true)).c_str();
+#else
+ std::string help = ToLower(str);
+ const char* s = help.c_str();
+#endif
+
+ while ((c = *s++))
+ hash = ((hash << 5) + hash) ^ c;
+
+ return hash;
+ }
+};
+#endif
+
+
+bool ComputeMD5Hash( const UInt8* data, size_t dataSize, UInt8 outHash[16] );
+bool ComputeSHA1Hash( const UInt8* data, size_t dataSize, UInt8 outHash[20] );
+
+#endif
diff --git a/Runtime/Utilities/InitializeAndCleanup.cpp b/Runtime/Utilities/InitializeAndCleanup.cpp
new file mode 100644
index 0000000..77280f5
--- /dev/null
+++ b/Runtime/Utilities/InitializeAndCleanup.cpp
@@ -0,0 +1,51 @@
+#include "UnityPrefix.h"
+#include "InitializeAndCleanup.h"
+
+enum { kMaxCount = 40 };
+
+struct OrderedCallback
+{
+ int order;
+ RegisterRuntimeInitializeAndCleanup::CallbackFunction* init;
+ RegisterRuntimeInitializeAndCleanup::CallbackFunction* cleanup;
+};
+
+static int gNumRegisteredCallbacks = 0;
+static OrderedCallback gCallbacks[kMaxCount];
+
+
+bool operator < (const OrderedCallback& lhs, const OrderedCallback& rhs)
+{
+ return lhs.order < rhs.order;
+}
+
+RegisterRuntimeInitializeAndCleanup::RegisterRuntimeInitializeAndCleanup(CallbackFunction* Initialize, CallbackFunction* Cleanup, int order)
+{
+ gCallbacks[gNumRegisteredCallbacks].init = Initialize;
+ gCallbacks[gNumRegisteredCallbacks].cleanup = Cleanup;
+ gCallbacks[gNumRegisteredCallbacks].order = order;
+
+ gNumRegisteredCallbacks++;
+ Assert(gNumRegisteredCallbacks <= kMaxCount);
+}
+
+void RegisterRuntimeInitializeAndCleanup::ExecuteInitializations()
+{
+ std::sort (gCallbacks, gCallbacks + gNumRegisteredCallbacks);
+
+ for (int i = 0; i < gNumRegisteredCallbacks; i++)
+ {
+ if (gCallbacks[i].init)
+ gCallbacks[i].init ();
+ }
+}
+
+void RegisterRuntimeInitializeAndCleanup::ExecuteCleanup()
+{
+ for (int i = gNumRegisteredCallbacks-1; i >=0 ; i--)
+ {
+ if (gCallbacks[i].cleanup)
+ gCallbacks[i].cleanup ();
+ }
+}
+
diff --git a/Runtime/Utilities/InitializeAndCleanup.h b/Runtime/Utilities/InitializeAndCleanup.h
new file mode 100644
index 0000000..b079f47
--- /dev/null
+++ b/Runtime/Utilities/InitializeAndCleanup.h
@@ -0,0 +1,12 @@
+#pragma once
+
+class RegisterRuntimeInitializeAndCleanup
+{
+public:
+ typedef void CallbackFunction ();
+ RegisterRuntimeInitializeAndCleanup(CallbackFunction* Initialize, CallbackFunction* Cleanup, int order = 0);
+
+ static void ExecuteInitializations();
+ static void ExecuteCleanup();
+};
+
diff --git a/Runtime/Utilities/LODUtility.cpp b/Runtime/Utilities/LODUtility.cpp
new file mode 100644
index 0000000..d2268fb
--- /dev/null
+++ b/Runtime/Utilities/LODUtility.cpp
@@ -0,0 +1,86 @@
+#include "UnityPrefix.h"
+
+#include "LODUtility.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Camera/LODGroup.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "Runtime/Camera/LODGroupManager.h"
+
+void CalculateLODGroupBoundingBox ( LODGroup& group )
+{
+ Matrix4x4f worldToLocal = group.GetComponent(Transform).GetWorldToLocalMatrix();
+
+ MinMaxAABB minmax;
+ minmax.Init();
+ for (int i=0;i<group.GetLODCount();i++)
+ {
+ for (int r=0;r<group.GetLOD(i).renderers.size();r++)
+ {
+ Renderer* renderer = group.GetLOD(i).renderers[r].renderer;
+ if (renderer && renderer->GetGameObjectPtr())
+ {
+ AABB localBounds;
+ if (CalculateLocalAABB (renderer->GetGameObject(), &localBounds))
+ {
+ Matrix4x4f relativeTransform;
+ Matrix4x4f rendererLocalToWorld = renderer->GetTransform().GetLocalToWorldMatrix();
+
+ MultiplyMatrices4x4(&worldToLocal, &rendererLocalToWorld, &relativeTransform);
+
+ AABB lodGroupRelativeBoundds;
+ TransformAABBSlow (localBounds, relativeTransform, lodGroupRelativeBoundds);
+
+ minmax.Encapsulate(lodGroupRelativeBoundds);
+ }
+ }
+ }
+ }
+
+ float size;
+ if (minmax.IsValid())
+ {
+ group.SetLocalReferencePoint (minmax.GetCenter());
+ Vector3f extent = minmax.GetExtent() * 2.0F;
+ size = std::max(std::max(extent.x, extent.y), extent.z);
+ }
+ else
+ {
+ group.SetLocalReferencePoint (Vector3f (0, 0, 0));
+ size = 1;
+ }
+
+ float scale = group.GetWorldSpaceScale();
+ if (scale > 0.0001F)
+ size /= scale;
+
+ group.SetSize (size);
+}
+
+void ForceLODLevel (const LODGroup& group, int index)
+{
+ int LODCount = group.GetLODCount();
+ if (index >= LODCount)
+ if (index >= LODCount)
+ {
+ WarningString("SetLODs: Attempting to force a LOD outside the number available LODs");
+ return;
+ }
+
+ // mask of 0 = no force
+ // now create a mask for the rest
+ UInt32 lodMask = 0;
+ if (index >= 0)
+ lodMask = 1 << index;
+
+ LODGroupManager& m = GetLODGroupManager();
+ int lodGroupIndex = group.GetLODGroup();
+ if (lodGroupIndex < 0)
+ {
+ WarningString("SetLODs: Attempting to force a LOD outside the number available LODs");
+ return;
+ }
+
+ m.SetForceLODMask (lodGroupIndex, lodMask);
+}
+
diff --git a/Runtime/Utilities/LODUtility.h b/Runtime/Utilities/LODUtility.h
new file mode 100644
index 0000000..58ff47d
--- /dev/null
+++ b/Runtime/Utilities/LODUtility.h
@@ -0,0 +1,7 @@
+#pragma once
+
+class LODGroup;
+
+void CalculateLODGroupBoundingBox (LODGroup& group);
+
+void ForceLODLevel (const LODGroup& group, int index);
diff --git a/Runtime/Utilities/LinkedList.h b/Runtime/Utilities/LinkedList.h
new file mode 100644
index 0000000..f2d7d3f
--- /dev/null
+++ b/Runtime/Utilities/LinkedList.h
@@ -0,0 +1,385 @@
+#ifndef LINKED_LIST_H
+#define LINKED_LIST_H
+
+#if !UNITY_RELEASE
+ #define LINKED_LIST_ASSERT(x) Assert(x)
+#else
+ #define LINKED_LIST_ASSERT(x)
+#endif
+
+class ListElement
+{
+public:
+ inline ListElement();
+ inline ~ListElement() { RemoveFromList(); }
+
+ inline bool IsInList() const;
+ inline bool RemoveFromList();
+ inline void InsertInList(ListElement* pos);
+
+ // Check against List::end(), not NULL
+ ListElement* GetPrev() const { return m_Prev; }
+ ListElement* GetNext() const { return m_Next; }
+
+private:
+ // Non copyable
+ ListElement(const ListElement&);
+ ListElement& operator=(const ListElement&);
+
+ ListElement* m_Prev;
+ ListElement* m_Next;
+
+ template <class T> friend class List;
+ inline void ValidateLinks() const;
+
+#if !UNITY_RELEASE
+ // Iterator debugging only
+ template <class T> friend class ListIterator;
+ template <class T> friend class ListConstIterator;
+ void SetList(void* l) { m_List = l; }
+ void* m_List;
+#else
+ void SetList(void*) {}
+#endif
+};
+
+template <class T>
+class ListNode : public ListElement
+{
+public:
+ ListNode(T* data = NULL) : m_Data(data) {}
+ T& operator*() const { return *m_Data; }
+ T* operator->() const { return m_Data; }
+ T* GetData() const { return m_Data; }
+ void SetData(T* data) { m_Data = data; }
+
+ // We know the type of prev and next element
+ ListNode* GetPrev() const { return static_cast<ListNode*>(ListElement::GetPrev()); }
+ ListNode* GetNext() const { return static_cast<ListNode*>(ListElement::GetNext()); }
+
+private:
+ T* m_Data;
+};
+
+template <class T>
+class ListIterator
+{
+public:
+ ListIterator(T* node = NULL) : m_Node(node) {}
+
+ // Pre- and post-increment operator
+ ListIterator& operator++() { m_Node = m_Node->GetNext(); return *this; }
+ ListIterator operator++(int) { ListIterator ret(*this); ++(*this); return ret; }
+
+ // Pre- and post-decrement operator
+ ListIterator& operator--() { m_Node = m_Node->GetPrev(); return *this; }
+ ListIterator operator--(int) { ListIterator ret(*this); --(*this); return ret; }
+
+ T& operator*() const { return static_cast<T&>(*m_Node); }
+ T* operator->() const { return static_cast<T*>(m_Node); }
+
+ friend bool operator !=(const ListIterator& x, const ListIterator& y) { return x.m_Node != y.m_Node; }
+ friend bool operator ==(const ListIterator& x, const ListIterator& y) { return x.m_Node == y.m_Node; }
+
+private:
+ template <class S> friend class List;
+ ListIterator(ListElement* node) : m_Node(node) {}
+ ListElement* m_Node;
+};
+
+
+template <class T>
+class ListConstIterator
+{
+public:
+ ListConstIterator(const T* node = NULL) : m_Node(node) {}
+
+ // Pre- and post-increment operator
+ ListConstIterator& operator++() { m_Node = m_Node->GetNext(); return *this; }
+ ListConstIterator operator++(int) { ListConstIterator ret(*this); ++(*this); return ret; }
+
+ // Pre- and post-decrement operator
+ ListConstIterator& operator--() { m_Node = m_Node->GetPrev(); return *this; }
+ ListConstIterator operator--(int) { ListConstIterator ret(*this); --(*this); return ret; }
+
+ const T& operator*() const { return static_cast<const T&>(*m_Node); }
+ const T* operator->() const { return static_cast<const T*>(m_Node); }
+
+ friend bool operator !=(const ListConstIterator& x, const ListConstIterator& y) { return x.m_Node != y.m_Node; }
+ friend bool operator ==(const ListConstIterator& x, const ListConstIterator& y) { return x.m_Node == y.m_Node; }
+
+private:
+ template <class S> friend class List;
+ ListConstIterator(const ListElement* node) : m_Node(node) {}
+ const ListElement* m_Node;
+};
+
+template <class T>
+class List
+{
+public:
+ typedef ListConstIterator<T> const_iterator;
+ typedef ListIterator<T> iterator;
+ typedef T value_type;
+
+ inline List();
+ inline ~List();
+
+ void push_back(T& node) { node.InsertInList(&m_Root); }
+ void push_front(T& node) { node.InsertInList(m_Root.m_Next); }
+ void insert(iterator pos, T& node) { node.InsertInList(&(*pos)); }
+ void erase(iterator pos) { pos->RemoveFromList(); }
+
+ void pop_back() { if (m_Root.m_Prev != &m_Root) m_Root.m_Prev->RemoveFromList(); }
+ void pop_front() { if (m_Root.m_Next != &m_Root) m_Root.m_Next->RemoveFromList(); }
+
+ iterator begin() { return iterator(m_Root.m_Next); }
+ iterator end() { return iterator(&m_Root); }
+
+ const_iterator begin() const { return const_iterator(m_Root.m_Next); }
+ const_iterator end() const { return const_iterator(&m_Root); }
+
+ T& front() { LINKED_LIST_ASSERT(!empty()); return static_cast<T&>(*m_Root.m_Next); }
+ T& back() { LINKED_LIST_ASSERT(!empty()); return static_cast<T&>(*m_Root.m_Prev); }
+
+ const T& front() const { LINKED_LIST_ASSERT(!empty()); return static_cast<const T&>(*m_Root.m_Next); }
+ const T& back() const { LINKED_LIST_ASSERT(!empty()); return static_cast<const T&>(*m_Root.m_Prev); }
+
+ bool empty() const { return begin() == end(); }
+
+ size_t size_slow() const;
+ inline void clear();
+ inline void swap(List& other);
+
+ // Insert list into list (removes elements from source)
+ inline void insert(iterator pos, List& src);
+ inline void append(List& src);
+
+private:
+ ListElement m_Root;
+};
+
+
+template <class T>
+List<T>::List()
+{
+ m_Root.m_Prev = &m_Root;
+ m_Root.m_Next = &m_Root;
+ m_Root.SetList(this);
+}
+
+template <class T>
+List<T>::~List()
+{
+ clear();
+}
+
+template <class T>
+size_t List<T>::size_slow () const
+{
+ size_t size = 0;
+ ListElement* node = m_Root.m_Next;
+ while (node != &m_Root)
+ {
+ node = node->m_Next;
+ size++;
+ }
+ return size;
+}
+
+template <class T>
+void List<T>::clear()
+{
+ ListElement* node = m_Root.m_Next;
+ while (node != &m_Root)
+ {
+ ListElement* next = node->m_Next;
+ node->m_Prev = NULL;
+ node->m_Next = NULL;
+ node->SetList(NULL);
+ node = next;
+ }
+ m_Root.m_Next = &m_Root;
+ m_Root.m_Prev = &m_Root;
+}
+
+template <class T>
+void List<T>::swap(List<T>& other)
+{
+ LINKED_LIST_ASSERT(this != &other);
+
+ std::swap(other.m_Root.m_Prev, m_Root.m_Prev);
+ std::swap(other.m_Root.m_Next, m_Root.m_Next);
+
+ if (other.m_Root.m_Prev == &m_Root)
+ other.m_Root.m_Prev = &other.m_Root;
+ if (m_Root.m_Prev == &other.m_Root)
+ m_Root.m_Prev = &m_Root;
+ if (other.m_Root.m_Next == &m_Root)
+ other.m_Root.m_Next = &other.m_Root;
+ if (m_Root.m_Next == &other.m_Root)
+ m_Root.m_Next = &m_Root;
+
+ other.m_Root.m_Prev->m_Next = &other.m_Root;
+ other.m_Root.m_Next->m_Prev = &other.m_Root;
+
+ m_Root.m_Prev->m_Next = &m_Root;
+ m_Root.m_Next->m_Prev = &m_Root;
+
+#if !UNITY_RELEASE
+ iterator my_it, my_end = end();
+ for (my_it = begin(); my_it != my_end; ++my_it)
+ my_it->m_List = this;
+ iterator other_it, other_end = other.end();
+ for (other_it = other.begin(); other_it != other_end; ++other_it)
+ other_it->m_List = &other;
+#endif
+}
+
+template <class T>
+void List<T>::insert(iterator pos, List<T>& src)
+{
+ LINKED_LIST_ASSERT(this != &src);
+ if (src.empty())
+ return;
+
+#if !UNITY_RELEASE
+ iterator src_it, src_end = src.end();
+ for (src_it = src.begin(); src_it != src_end; ++src_it)
+ src_it->m_List = this;
+#endif
+ // Insert source before pos
+ ListElement* a = pos.m_Node->m_Prev;
+ ListElement* b = pos.m_Node;
+ a->m_Next = src.m_Root.m_Next;
+ b->m_Prev = src.m_Root.m_Prev;
+ a->m_Next->m_Prev = a;
+ b->m_Prev->m_Next = b;
+ // Clear source list
+ src.m_Root.m_Next = &src.m_Root;
+ src.m_Root.m_Prev = &src.m_Root;
+}
+
+template <class T>
+void List<T>::append(List& src)
+{
+ insert(end(), src);
+}
+
+ListElement::ListElement()
+{
+ m_Prev = NULL;
+ m_Next = NULL;
+ SetList(NULL);
+}
+
+bool ListElement::IsInList() const
+{
+ return m_Prev != NULL;
+}
+
+bool ListElement::RemoveFromList()
+{
+ if (!IsInList())
+ return false;
+
+#if !UNITY_RELEASE
+ ValidateLinks();
+#endif
+ m_Prev->m_Next = m_Next;
+ m_Next->m_Prev = m_Prev;
+ m_Prev = NULL;
+ m_Next = NULL;
+ return true;
+}
+
+void ListElement::InsertInList(ListElement* pos)
+{
+ if (this == pos)
+ return;
+
+ if (IsInList())
+ RemoveFromList();
+
+#if !UNITY_RELEASE
+ m_List = pos->m_List;
+ pos->m_Prev->ValidateLinks();
+ pos->ValidateLinks();
+#endif
+ m_Prev = pos->m_Prev;
+ m_Next = pos;
+ m_Prev->m_Next = this;
+ m_Next->m_Prev = this;
+#if !UNITY_RELEASE
+ ValidateLinks();
+#endif
+ return;
+}
+
+void ListElement::ValidateLinks() const
+{
+#if !UNITY_RELEASE
+ LINKED_LIST_ASSERT(m_Prev != NULL && m_Next != NULL);
+ LINKED_LIST_ASSERT(m_Prev->m_Next == this && m_Next->m_Prev == this);
+ LINKED_LIST_ASSERT(m_Prev->m_List == m_List && m_Next->m_List == m_List);
+#endif
+}
+
+/// Allows for iterating a linked list, even if you add / remove any node during traversal.
+template<class T>
+class SafeIterator
+{
+public:
+ SafeIterator(T& list)
+ : m_SourceList(list)
+ {
+ m_CurrentNode = NULL;
+ m_ExecuteList.swap(m_SourceList);
+ }
+
+ ~SafeIterator()
+ {
+ // Call Complete if you abort the iteration!
+ LINKED_LIST_ASSERT(m_ExecuteList.empty());
+ }
+
+ // You must call complete if you are in some way aborting list iteration.
+ // If you dont call Complete, the source list will lose nodes that have not yet been iterated permanently.
+ //
+ /// SafeIterator<Behaviour*> i(myList);
+ /// i =0;
+ /// while(i.GetNext() && ++i != 3)
+ /// (**i).Update();
+ /// i.Complete();
+ void Complete()
+ {
+ m_SourceList.append(m_ExecuteList);
+ }
+
+ typename T::value_type* Next()
+ {
+ if(!m_ExecuteList.empty())
+ {
+ typename T::iterator it = m_ExecuteList.begin();
+ m_CurrentNode = &*it;
+ m_ExecuteList.erase(it);
+ m_SourceList.push_back(*m_CurrentNode);
+ }
+ else
+ {
+ m_CurrentNode = NULL;
+ }
+ return m_CurrentNode;
+ }
+
+ typename T::value_type& operator *() const { return *m_CurrentNode; }
+ typename T::value_type* operator ->() const { return m_CurrentNode; }
+
+private:
+ T m_ExecuteList;
+ T& m_SourceList;
+ typename T::value_type* m_CurrentNode;
+};
+
+
+#endif
diff --git a/Runtime/Utilities/LogAssert.cpp b/Runtime/Utilities/LogAssert.cpp
new file mode 100644
index 0000000..6a863b3
--- /dev/null
+++ b/Runtime/Utilities/LogAssert.cpp
@@ -0,0 +1,1294 @@
+#include "UnityPrefix.h"
+#include "LogAssert.h"
+#include <stdarg.h>
+#include "PathNameUtility.h"
+#include <sys/stat.h>
+#include <list>
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Threads/AtomicOps.h"
+#if !UNITY_EXTERNAL_TOOL && !UNITY_PLUGIN
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Threads/ThreadSpecificValue.h"
+#include "Runtime/Misc/PlayerSettings.h"
+#endif
+#include "Runtime/Profiler/Profiler.h"
+
+#if UNITY_OSX || UNITY_IPHONE || UNITY_LINUX || UNITY_TIZEN
+#include <syslog.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
+#include "Runtime/Utilities/File.h"
+#endif
+#if UNITY_NACL
+#include "PlatformDependent/PepperPlugin/UnityInstance.h"
+#endif
+
+#if UNITY_BB10
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#endif
+#if UNITY_WIN
+#if !UNITY_WP8
+#include <ShlObj.h>
+#endif
+#include "PlatformDependent/Win/PathUnicodeConversion.h"
+#include "FileUtilities.h"
+#include "PlatformDependent/Win/WinUtils.h"
+#include "io.h"
+#endif
+
+#if UNITY_TIZEN
+#include <osp/FBaseLog.h>
+#endif
+#if UNITY_EDITOR
+#include "File.h"
+#include "Editor/Src/Utility/Analytics.h"
+#endif
+
+#if WEBPLUG
+#include "ErrorExit.h"
+#endif
+
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+#if UNITY_WII
+#include "Platformdependent/wii/WiiDbgUtils.h"
+#endif
+#if UNITY_ANDROID
+#include <android/log.h>
+
+int CsToAndroid[LogType_NumLevels] =
+{
+ ANDROID_LOG_ERROR, // LogType_Error = 0,
+ ANDROID_LOG_ERROR, // LogType_Assert = 1,
+ ANDROID_LOG_WARN, // LogType_Warning = 2,
+ ANDROID_LOG_INFO, // LogType_Log = 3,
+ ANDROID_LOG_ERROR, // LogType_Exception = 4,
+ ANDROID_LOG_DEBUG // LogType_Debug = 5,
+};
+#endif
+
+using namespace std;
+
+#if UNITY_WP8
+ #include "PlatformDependent\MetroPlayer\MetroUtils.h"
+#endif
+
+#if WEBPLUG && !UNITY_WIN
+#define CAP_LOG_OUTPUT_SIZE 1
+#else
+#define CAP_LOG_OUTPUT_SIZE 0
+#endif
+
+#if UNITY_WIN && DEBUGMODE && !UNITY_WP8
+ extern "C" WINBASEAPI BOOL WINAPI IsDebuggerPresent( VOID );
+ #define LOG_TO_WINDOWS_DEBUGGER \
+ char buf[4096]; buf[4095]=0; \
+ vsnprintf( buf, sizeof(buf)-1, log, list ); \
+ OutputDebugStringA( buf )
+#elif UNITY_WP8 && UNITY_DEVELOPER_BUILD
+ #define LOG_TO_WINDOWS_DEBUGGER \
+ char buf[4096]; buf[4095]=0; \
+ vsnprintf (buf, sizeof(buf) - 1, log, list); \
+ auto str = ConvertUtf8ToString (buf); \
+ s_WinRTBridge->WP8Utility->OutputLogMessage (str)
+#else
+ #define LOG_TO_WINDOWS_DEBUGGER
+#endif
+
+#if UNITY_FLASH
+extern "C" void Ext_Flash_LogCallstack();
+#endif
+
+static LogEntryHandler gCurrentLogEntryHandler = NULL;
+static std::list<LogEntryHandler> *gCleanLogEntryHandlers = NULL;
+
+void ReleaseLogHandlers()
+{
+ if (gCleanLogEntryHandlers != NULL)
+ {
+ delete gCleanLogEntryHandlers;
+ gCleanLogEntryHandlers = NULL;
+ }
+}
+
+void SetLogEntryHandler(LogEntryHandler newHandler)
+{
+ gCurrentLogEntryHandler = newHandler;
+}
+
+void AddCleanLogEntryHandler(LogEntryHandler newHandler)
+{
+ if (gCleanLogEntryHandlers == NULL)
+ gCleanLogEntryHandlers = new std::list<LogEntryHandler>();
+
+ gCleanLogEntryHandlers->push_back(newHandler);
+}
+
+FILE* gConsoleFile = NULL;
+FILE* gReproductionLogFile = NULL;
+
+bool DefaultCleanLogHandlerv (LogType logType, const char* log, va_list list);
+
+void InitializeCleanedLogFile (FILE* file)
+{
+ Assert(gReproductionLogFile == NULL);
+ Assert(gCleanLogEntryHandlers == NULL || count(gCleanLogEntryHandlers->begin(), gCleanLogEntryHandlers->end(), &DefaultCleanLogHandlerv) == 0);
+
+ gReproductionLogFile = file;
+
+ AddCleanLogEntryHandler(&DefaultCleanLogHandlerv);
+}
+
+static StaticString gConsolePath;
+#if CAP_LOG_OUTPUT_SIZE
+static int gConsoleSizeCheck = 0;
+#endif
+#if !UNITY_EXTERNAL_TOOL
+static UNITY_TLS_VALUE(int) gRecursionLock;
+#endif
+
+enum { kMaxLogSize = 2000000 };
+
+
+
+#if UNITY_OSX || UNITY_IPHONE || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+fpos_t gStdOutPosition;
+int gStdoutFd;
+void ResetStdout()
+{
+ fflush(stdout);
+ dup2(gStdoutFd, fileno(stdout));
+ close(gStdoutFd);
+ clearerr(stdout);
+ fsetpos(stdout, &gStdOutPosition);
+}
+#endif
+
+#if UNITY_XENON || UNITY_PS3
+void LogOutputToSpecificFile (const char* path)
+{
+ if (path == NULL)
+ return;
+
+ gConsolePath = path;
+ gConsoleFile = fopen(path, "w");
+ if(gConsoleFile)
+ fclose(gConsoleFile);
+
+}
+#elif !UNITY_PS3 && !UNITY_ANDROID && !UNITY_PEPPER && !UNITY_FLASH
+
+void LogOutputToSpecificFile (const char* path)
+{
+#if UNITY_OSX || UNITY_IPHONE || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+
+ // Save the stdout position for later
+ fgetpos(stdout, &gStdOutPosition);
+ gStdoutFd = dup(fileno(stdout));
+
+ if (path == NULL || strlen(path) == 0)
+ {
+ gConsoleFile = stdout;
+ }
+ else
+ {
+ gConsoleFile = fopen(path, "w");
+ int fd = open(path, O_WRONLY, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ close(fd);
+ }
+
+#elif UNITY_WII
+
+ // TODO: see if logging to the host machine (windows) works
+ if (path == NULL)
+ return;
+
+ gConsolePath = path;
+ gConsoleFile = fopen(path, "w");
+
+#else
+
+ // On windows just default to editor.log instead
+ if (path == NULL)
+ return;
+
+ gConsolePath = path;
+ gConsoleFile = fopen(path, "w");
+#endif
+}
+#endif
+
+#if SUPPORT_ENVIRONMENT_VARIABLES
+std::string GetStringFromEnv(const char *envName)
+{
+ const char* env = getenv( envName );
+ std::string result;
+ if( env != NULL && env[0] != 0 )
+ result.append(env);
+ return result;
+}
+std::string GetCustomLogFile()
+{
+ return GetStringFromEnv("UNITY_LOG_FILE");
+}
+std::string GetCleanedLogFile()
+{
+ return GetStringFromEnv("UNITY_CLEANED_LOG_FILE");
+}
+#endif
+
+#if UNITY_WIN
+
+string SetLogFilePath(string const& path)
+{
+ Assert(gConsolePath.empty());
+
+ gConsolePath = path;
+
+ #if !UNITY_EDITOR && !UNITY_EXTERNAL_TOOL && !UNITY_WINRT
+
+ if (!gConsolePath.empty())
+ {
+ string const customLogFile = GetCustomLogFile();
+
+ if (!customLogFile.empty())
+ {
+ gConsolePath = customLogFile.c_str();
+ }
+ }
+
+ #endif
+
+ return gConsolePath.c_str();
+}
+
+namespace
+{
+ int gStdOutFd = -1;
+ int gStdErrFd = -1;
+ FILE* gStdOutFile = NULL;
+ FILE* gStdErrFile = NULL;
+
+ void CloseConsoleWin()
+ {
+ gConsoleFile = NULL;
+
+ if (NULL != gStdOutFile)
+ {
+ int const result = fclose(gStdOutFile);
+ //Assert(0 == result);
+
+ gStdOutFile = NULL;
+ }
+
+ if (NULL != gStdErrFile)
+ {
+ int const result = fclose(gStdErrFile);
+ //Assert(0 == result);
+
+ gStdErrFile = NULL;
+ }
+
+ if (-1 != gStdOutFd)
+ {
+ int const result = _dup2(gStdOutFd, 1);
+ //Assert(0 == result);
+
+ gStdOutFd = -1;
+ }
+
+ if (-1 != gStdErrFd)
+ {
+ int const result = _dup2(gStdErrFd, 2);
+ //Assert(0 == result);
+
+ gStdErrFd = -1;
+ }
+ gConsolePath.clear();
+ }
+
+ void OpenConsoleWin()
+ {
+ // don't assert in this function because it might cause stack overflow
+ std::wstring widePath;
+
+ //Assert(NULL == gConsoleFile);
+
+ // check for no log file
+
+ if (gConsolePath.empty())
+ {
+ gConsoleFile = stdout;
+ return;
+ }
+
+ // duplicate stdout and stderr file descriptors so they can be restored later
+
+ gStdOutFd = _dup(1);
+ //Assert(-1 != gStdOutFd);
+
+ if (-1 == gStdOutFd)
+ {
+ goto error;
+ }
+
+ gStdErrFd = _dup(2);
+ //Assert(-1 != gStdErrFd);
+
+ if (-1 == gStdErrFd)
+ {
+ goto error;
+ }
+
+ // reassign stdout and stderr file pointers
+
+ ConvertUnityPathName(gConsolePath, widePath);
+
+ gStdOutFile = _wfreopen(widePath.c_str(), L"a", stdout);
+ //Assert(NULL != gStdOutFile);
+
+ if (NULL == gStdOutFile)
+ {
+ goto error;
+ }
+
+ gStdErrFile = _wfreopen(widePath.c_str(), L"a", stderr);
+ //Assert(NULL != gStdErrFile);
+
+ if (NULL == gStdErrFile)
+ {
+ goto error;
+ }
+
+ // redirect stderr to stdout
+
+ int const error = _dup2(1, 2);
+ //Assert(0 == error);
+
+ if (0 != error)
+ {
+ goto error;
+ }
+
+ // disable stdout and stderr buffering
+
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+
+ // done
+
+ gConsoleFile = stdout;
+ return;
+
+ // failed
+
+ error:
+
+ CloseConsoleWin();
+
+ gConsoleFile = stdout;
+ return;
+ }
+}
+
+#endif
+
+#if (UNITY_OSX || UNITY_LINUX) && !UNITY_EXTERNAL_TOOL
+// Get the logfile path, relative to the user's home directory, using a system-specific subpath
+static std::string GetHomedirLogfile (const std::string &relativePath)
+{
+ string folder = getenv ("HOME");
+ if (folder.empty ())
+ return "";
+
+ // Create log file and parent folders
+ folder = AppendPathName( folder, relativePath);
+ CreateDirectoryRecursive (folder);
+
+ #if UNITY_EDITOR
+ std::string result = AppendPathName( folder, "Editor.log" );
+ // move any existing log file into Editor-prev.log
+ if( IsFileCreated(result) )
+ MoveReplaceFile( result, AppendPathName(folder,"Editor-prev.log" ) );
+ #else
+ std::string result = GetCustomLogFile();
+ if (result.empty())
+ result = AppendPathName( folder, "Player.log" );
+ #endif
+
+ return result;
+}
+
+// Open the console path and redirect stdout/stderr if appropriate
+static void OpenConsoleFile ()
+{
+ #if UNITY_EDITOR
+ gConsoleFile = fopen(gConsolePath.c_str(), "w");
+ #else
+ gConsoleFile = fopen(gConsolePath.c_str(), "w+");
+ #endif
+
+ #if !WEBPLUG
+ // Save the stdout position for later
+ fgetpos(stdout, &gStdOutPosition);
+ gStdoutFd = dup(fileno(stdout));
+
+ if (gConsoleFile)
+ {
+ int fd = fileno (gConsoleFile);
+
+ if (dup2 (fd, fileno (stdout)) < 0)
+ fprintf (stderr, "Failed to redirect stdout to the console file %s.\n", gConsolePath.c_str ());
+ if (dup2 (fd, fileno (stderr)) < 0)
+ fprintf (stderr, "Failed to redirect stderr to the console file %s.\n", gConsolePath.c_str ());
+ }
+ #endif
+}
+#endif
+
+FILE* OpenConsole ()
+{
+ if (gConsoleFile != NULL)
+ return gConsoleFile;
+
+ #define PLATFORM_ALWAYS_USES_STDOUT_FOR_LOG (UNITY_XENON || UNITY_IPHONE || UNITY_ANDROID || UNITY_PEPPER || UNITY_FLASH || UNITY_WII || UNITY_PS3 || UNITY_EXTERNAL_TOOL || UNITY_WEBGL || UNITY_BB10 || UNITY_TIZEN || ENABLE_GFXDEVICE_REMOTE_PROCESS_WORKER )
+
+ #if GAMERELEASE && !UNITY_EXTERNAL_TOOL && !WEBPLUG && !UNITY_PLUGIN && !PLATFORM_ALWAYS_USES_STDOUT_FOR_LOG
+ if (GetPlayerSettingsPtr() == NULL)
+ return stdout;
+
+ if (!GetPlayerSettings().GetUsePlayerLog())
+ {
+ gConsoleFile = stdout;
+ return gConsoleFile;
+ }
+ #endif
+
+ #if PLATFORM_ALWAYS_USES_STDOUT_FOR_LOG
+
+ #if UNITY_NACL
+ // in nacl, we can't write to a custom log location.
+ // so if we need a clean log, use stdout for that, and use 0 for the
+ // normal, non-clean log.
+ if (GetUnityInstance().GetCleanLog())
+ return 0;
+ #endif
+
+ gConsoleFile = stdout;
+
+ #elif UNITY_OSX
+ gConsolePath = GetHomedirLogfile ("Library/Logs/Unity");
+ if (!gConsolePath.empty())
+ OpenConsoleFile ();
+
+ #elif UNITY_LINUX
+ gConsolePath = GetHomedirLogfile (".config/unity3d");
+ if (!gConsolePath.empty())
+ OpenConsoleFile ();
+
+ #elif UNITY_WIN
+
+ OpenConsoleWin();
+
+ #elif UNITY_PS3
+ gConsoleFile = stdout;
+ // When running from a read only file system, stdout is a valid file pointer, but it crashes later
+ // if trying to write into it. So we check if valid _fileno exists for it.
+ if( gConsoleFile && fileno(gConsoleFile) < 0 )
+ gConsoleFile = NULL;
+
+ #else
+
+ #error "Unknown platform"
+
+ #endif
+
+ return gConsoleFile;
+}
+
+#if UNITY_WIN
+void CloseConsoleFile()
+{
+ CloseConsoleWin();
+}
+#endif
+
+#if UNITY_EDITOR
+string GetEditorConsoleLogPath ()
+{
+ #if UNITY_OSX
+
+ string home = getenv ("HOME");
+ return AppendPathName (home, "Library/Logs/Unity/Editor.log");
+
+ #elif UNITY_WIN
+
+ return gConsolePath.c_str();
+
+ #elif UNITY_LINUX
+
+ string home = getenv ("HOME");
+ return AppendPathName (home, ".config/Unity/Editor/Editor.log");
+
+ #else
+ #error "Unknown platform"
+ #endif
+}
+
+string GetMonoDevelopLogPath ()
+{
+ #if UNITY_OSX
+
+ string result = getenv ("HOME");
+ return AppendPathName( result, "Library/Logs/MonoDevelop/MonoDevelop.log");
+
+ #elif UNITY_WIN
+
+ wchar_t widePath[MAX_PATH];
+ if( SUCCEEDED(SHGetFolderPathW( NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, widePath )) )
+ {
+ std::string folder;
+ ConvertWindowsPathName( widePath, folder );
+ folder = AppendPathName( folder, "MonoDevelop-Unity" );
+ return AppendPathName( folder, "log.txt" );
+ }
+
+ #elif UNITY_LINUX
+
+ string result = getenv ("HOME");
+ return AppendPathName( result, ".config/MonoDevelop-Unity/log.txt");
+
+ #else
+ #error "Unknown platform"
+ #endif
+
+ return "";
+}
+
+#if UNITY_OSX
+string GetPlayerConsoleLogPath ()
+{
+ string home = getenv ("HOME");
+ return AppendPathName (home, "Library/Logs/Unity/Player.log");
+}
+#endif
+
+#if UNITY_LINUX
+string GetPlayerConsoleLogPath ()
+{
+ string home = getenv ("HOME");
+ return AppendPathName (home, ".config/unity3d/Editor/Player.log");
+}
+#endif
+#endif
+
+string GetConsoleLogPath ()
+{
+ return gConsolePath.c_str();
+}
+
+#if (UNITY_XENON || UNITY_PS3) && !MASTER_BUILD
+static Mutex s_mutex;
+#endif
+
+void printf_consolev (LogType logType, const char* log, va_list alist)
+{
+ va_list list;
+ va_copy (list, alist);
+
+ if (gCurrentLogEntryHandler && !gCurrentLogEntryHandler (logType, log, list))
+ return;
+
+#if UNITY_FLASH
+ char buffer[1024 * 10];
+ vsnprintf (buffer, 1024 * 10, log, list);
+ Ext_Trace(buffer);
+
+ va_end (list);
+ return;
+#endif
+
+#if UNITY_ANDROID
+ if (gReproductionLogFile == NULL) // gReproductionLogFile / 'cleanedLogFile' should remove all other logging
+ {
+ if (ENABLE_PROFILER /* == development player */ || logType < LogType_Debug)
+ __android_log_vprint(CsToAndroid[logType], "Unity", log, list);
+ }
+
+ va_end (list);
+ return;
+#endif
+
+#if UNITY_TIZEN
+ if (gReproductionLogFile == NULL)
+ {
+ static char buffer[1024 * 10];
+ memset(buffer, 0, 1024*10);
+ vsnprintf (buffer, 1024 * 10, log, list);
+ AppLogTagInternal("Unity", "", 0, buffer);
+ }
+#endif
+
+#if UNITY_XENON
+#if !MASTER_BUILD
+ Mutex::AutoLock lock(s_mutex);
+
+ char buffer[1024 * 8] = { 0 };
+ vsnprintf(buffer, 1024 * 8, log, list);
+
+ gConsoleFile = fopen(gConsolePath.c_str(), "a");
+ if(gConsoleFile)
+ {
+ fprintf(gConsoleFile, buffer);
+ fflush(gConsoleFile);
+ fclose(gConsoleFile);
+ }
+
+ OutputDebugString(buffer);
+
+#endif
+ va_end (list);
+ return;
+#endif
+
+#if UNITY_PS3
+#if !MASTER_BUILD
+ Mutex::AutoLock lock(s_mutex);
+
+ gConsoleFile = fopen(gConsolePath.c_str(), "a");
+ if(gConsoleFile)
+ {
+ vfprintf (gConsoleFile, log, list);
+ fflush( gConsoleFile );
+ fclose( gConsoleFile );
+ vfprintf (stdout, log, list);
+ }
+ else
+ {
+ vfprintf(stdout, log, list);
+ fflush(stdout);
+ }
+
+#endif
+ va_end (list);
+ return;
+#endif
+
+#if UNITY_WII
+ #if !MASTER_BUILD
+ vfprintf (stdout, log, list);
+ fflush (stdout);
+ va_end (list);
+ #endif
+ return;
+#endif
+
+ if (gConsoleFile == NULL)
+ {
+ if (OpenConsole() == NULL) {
+ va_end (list);
+ return;
+ }
+ }
+
+ if (gConsoleFile)
+ {
+ vfprintf (gConsoleFile, log, list);
+ fflush( gConsoleFile );
+
+ // Clamp the size of the file to 100kb with a rolling buffer.
+ // copy last 50kb to beginning of file.
+ #if CAP_LOG_OUTPUT_SIZE
+ gConsoleSizeCheck++;
+ if (gConsoleSizeCheck > 20)
+ {
+ gConsoleSizeCheck = 0;
+ struct stat statbuffer;
+ if( ::stat(gConsolePath.c_str(), &statbuffer) == 0 && statbuffer.st_size > kMaxLogSize)
+ {
+ FILE* file = fopen(gConsolePath.c_str(), "r");
+ if (file)
+ {
+ fseek(file, statbuffer.st_size - kMaxLogSize / 2, SEEK_SET);
+ UInt8* buffer = new UInt8[kMaxLogSize / 2];
+ if (fread(buffer, 1, kMaxLogSize / 2, file) == kMaxLogSize / 2)
+ {
+ fclose(file);
+ fclose(gConsoleFile);
+ gConsoleFile = fopen(gConsolePath.c_str(), "w");
+ fwrite(buffer, 1, kMaxLogSize / 2, gConsoleFile);
+ }
+ else
+ {
+ fclose(file);
+ }
+ delete[] buffer;
+ }
+ }
+ }
+ #endif
+ }
+ else
+ {
+#ifndef DISABLE_TTY
+ vfprintf (stdout, log, list);
+ fflush( stdout );
+#endif
+ }
+
+ LOG_TO_WINDOWS_DEBUGGER;
+ va_end (list);
+}
+
+extern "C" void printf_console_log(const char* log, va_list list)
+{
+ printf_consolev(LogType_Log, log, list);
+}
+
+extern "C" void printf_console (const char* log, ...)
+{
+ va_list vl;
+ va_start(vl, log);
+ printf_consolev(LogType_Debug, log, vl);
+ va_end(vl);
+}
+
+static void InternalLogConsole (const char* log, ...)
+{
+ va_list vl;
+ va_start(vl, log);
+ printf_consolev(LogType_Log, log, vl);
+ va_end(vl);
+}
+
+static void InternalWarningConsole (const char* log, ...)
+{
+ va_list vl;
+ va_start(vl, log);
+ printf_consolev(LogType_Warning, log, vl);
+ va_end(vl);
+}
+
+static void InternalAssertConsole (const char* log, ...)
+{
+ va_list vl;
+ va_start(vl, log);
+ printf_consolev(LogType_Assert, log, vl);
+ va_end(vl);
+}
+
+static void InternalErrorConsole (const char* log, ...)
+{
+ va_list vl;
+ va_start(vl, log);
+ printf_consolev(LogType_Error, log, vl);
+ va_end(vl);
+}
+
+static void InternalIgnoreConsole (const char* log, ...)
+{
+}
+
+static LogToConsoleImpl* gLogToConsoleFunc = NULL;
+static LogCallback* gLogCallbackFunc = NULL;
+static bool gLogCallbackFuncThreadSafe = false;
+static PreprocessCondition* gPreprocessor = NULL;
+static RemoveLogFunction* gRemoveLog = NULL;
+static RemoveLogFunction* gShowLogWithMode = NULL;
+extern "C" void __msl_assertion_failed(char const *condition, char const *filename, char const *funcname, int lineno);
+
+/*
+extern "C"
+{
+void __eprintf(const char* log, ...)
+{
+printf_console (log, va_list(&log + 1));
+}
+
+void malloc_printf (const char* log, ...)
+{
+ printf_console (log, va_list(&log + 1));
+}
+}
+*/
+void RegisterLogToConsole (LogToConsoleImpl* func)
+{
+ gLogToConsoleFunc = func;
+}
+
+void RegisterLogCallback (LogCallback* callback, bool threadsafe)
+{
+ gLogCallbackFunc = callback;
+ gLogCallbackFuncThreadSafe = threadsafe;
+}
+
+void RegisterLogPreprocessor (PreprocessCondition* func)
+{
+ gPreprocessor = func;
+}
+
+void RegisterRemoveImportErrorFromConsole (RemoveLogFunction* func)
+{
+ gRemoveLog = func;
+}
+
+void RegisterShowErrorWithMode (RemoveLogFunction* func)
+{
+ gShowLogWithMode = func;
+}
+
+
+extern "C" void __msl_assertion_failed(char const *condition, char const *filename, char const *funcname, int lineno)
+{
+ DebugStringToFile (condition, 0, filename, lineno, kAssert, 0);
+}
+
+inline bool ContainsNewLine (const char* c)
+{
+ while (*c != '\0')
+ {
+ if (*c == '\n')
+ return true;
+ c++;
+ }
+ return false;
+}
+
+
+static void CompilerErrorAnalytics (int mode, const char *condition)
+{
+#if UNITY_EDITOR
+ if ( mode & kScriptCompileError )
+ {
+ std::string message = condition;
+
+ // The compiler error message is formatted like this:
+ // filename: error/warning number: description
+ int n1 = message.find(": ");
+ if ( n1 != string::npos )
+ {
+ int n2 = message.find(": ", n1+2);
+ if ( n2 != string::npos )
+ {
+ string error = message.substr(n1+2, n2-n1-2);
+ string description = message.substr(n2+2);
+ AnalyticsTrackEvent("Compiler", error, description, 1);
+ return;
+ }
+ }
+ AnalyticsTrackEvent("Compiler", "Unknown", message, 1);
+ }
+#endif
+}
+
+bool DefaultCleanLogHandlerv (LogType logType, const char* log, va_list alist)
+{
+ va_list list;
+ va_copy (list, alist);
+
+#if UNITY_ANDROID // On Android we don't use a separate clean log-file, but instead we clean up the actual logcat
+#define LOG_PRINTF(x, ...) __android_log_vprint(ANDROID_LOG_INFO, "Unity", __VA_ARGS__)
+#define LOG_FLUSH(x)
+#else
+#define LOG_PRINTF vfprintf
+#define LOG_FLUSH fflush
+#endif
+
+LOG_PRINTF (gReproductionLogFile, log, list);
+LOG_FLUSH( gReproductionLogFile );
+
+#undef LOG_PRINTF
+#undef LOG_FLUSH
+
+ va_end (list);
+
+ return true;
+}
+
+void CleanLogHandler(LogType logType, const char* log, ...)
+{
+ if (gCleanLogEntryHandlers != NULL)
+ {
+ for (std::list<LogEntryHandler>::iterator it = gCleanLogEntryHandlers->begin();
+ it != gCleanLogEntryHandlers->end();
+ it++)
+ {
+ va_list vl;
+ va_start(vl, log);
+ (**it)(logType, log, vl);
+ }
+ }
+}
+
+
+typedef void PrintConsole (const char* log, ...);
+
+// convert the log mode to the cs LogType enum
+// LogType.Error = 0, LogType.Assert = 1, LogType.Warning = 2, LogType.Log = 3, LogType.Exception = 4
+inline LogType LogModeToLogType(int mode)
+{
+ LogType logType;
+ if ( mode & (kScriptingException) ) logType = LogType_Exception;
+ else if ( mode & (kError | kFatal | kScriptingError | kScriptCompileError | kStickyError | kAssetImportError | kGraphCompileError) ) logType = LogType_Error;
+ else if ( mode & kAssert ) logType = LogType_Assert;
+ else if ( mode & (kScriptingWarning | kScriptCompileWarning | kAssetImportWarning) ) logType = LogType_Warning;
+ else logType = LogType_Log;
+
+ return logType;
+}
+
+void DebugStringToFilePostprocessedStacktrace (const char* condition, const char* strippedStacktrace, const char* stacktrace, int errorNum, const char* file, int line, int mode, int objectInstanceID, int identifier)
+{
+ LogType logType = LogModeToLogType(mode);
+
+ #if !UNITY_EXTERNAL_TOOL
+ int depth = gRecursionLock;
+ if (depth == 1)
+ return;
+ gRecursionLock = 1;
+
+ if ( gLogCallbackFunc)
+ {
+#if SUPPORT_THREADS
+ if (gLogCallbackFuncThreadSafe || Thread::CurrentThreadIsMainThread())
+#endif
+ gLogCallbackFunc (condition, strippedStacktrace, (int)logType);
+ }
+
+ #endif
+#if UNITY_WII
+ if (mode & kFatal)
+ {
+ wii::DbgFatalError ("%s\n", condition);
+ }
+ else if (mode & (kAssert | kError))
+ {
+ wii::DbgOutput ("%s\n", condition);
+ }
+#elif UNITY_XENON && MASTER_BUILD
+ return;
+#endif
+ CompilerErrorAnalytics (mode, condition);
+
+ string conditionAndStacktrace = condition;
+ if ( stacktrace )
+ {
+ conditionAndStacktrace += "\n";
+ conditionAndStacktrace += stacktrace;
+ }
+
+ string conditionAndStrippedStacktrace = condition;
+ if ( stacktrace )
+ {
+ conditionAndStrippedStacktrace += "\n";
+ conditionAndStrippedStacktrace += strippedStacktrace;
+ }
+
+ if (errorNum)
+ CleanLogHandler (logType, "%s (Error: %d)\n\n", condition, errorNum);
+ else
+ CleanLogHandler (logType, "%s\n\n", condition);
+
+
+ PrintConsole* printConsole = InternalIgnoreConsole;
+ // Logs
+ if (mode & (kLog | kScriptingLog))
+ printConsole = InternalLogConsole;
+ else if (mode & (kScriptingWarning | kAssetImportWarning))
+ printConsole = InternalWarningConsole;
+ else if (mode & kAssert)
+ printConsole = InternalAssertConsole;
+ // Real errors ---- YOU WANT TO BREAKPOINT THIS LINE!
+ else
+ printConsole = InternalErrorConsole;
+
+
+ if (errorNum)
+ {
+ if (ContainsNewLine (conditionAndStacktrace.c_str()))
+ printConsole ("%s \n(Error: %li Filename: %s Line: %li)\n\n", conditionAndStacktrace.c_str(), errorNum, file, line);
+ else
+ printConsole ("%s (Error: %li Filename: %s Line: %li)\n", conditionAndStacktrace.c_str(), errorNum, file, line);
+ }
+ else
+ {
+ if (ContainsNewLine (conditionAndStacktrace.c_str()))
+ printConsole ("%s \n(Filename: %s Line: %li)\n\n", conditionAndStacktrace.c_str(), file, line);
+ else
+ printConsole ("%s (Filename: %s Line: %li)\n", conditionAndStacktrace.c_str(), file, line);
+ }
+
+ if (gLogToConsoleFunc)
+ gLogToConsoleFunc (conditionAndStrippedStacktrace, errorNum, file, line, mode, objectInstanceID, identifier);
+
+ #if WEBPLUG
+ #if DEBUGMODE && 0
+ if (mode & kAssert)
+ {
+ DebugBreak();
+ }
+ #endif
+ if (mode & kFatal)
+ {
+ ExitWithErrorCode(kErrorFatalException);
+ }
+ #elif UNITY_WINRT
+ if (mode & kAssert)
+ {
+ // Used to set a breakpoint
+ int s = 5;
+ }
+ if (mode & kFatal)
+ __debugbreak();
+ #endif
+
+ #if !UNITY_EXTERNAL_TOOL
+ gRecursionLock = 0;
+ #endif
+}
+
+PROFILER_INFORMATION (gProfilerLogString, "LogStringToConsole", kProfilerOther);
+
+void DebugStringToFile (const char* condition, int errorNum, const char* file, int line, int mode, int objectInstanceID, int identifier)
+{
+ PROFILER_AUTO(gProfilerLogString, NULL);
+ SET_ALLOC_OWNER(NULL);
+#if UNITY_ANDROID && i386
+ printf_console("%s: %d at %s:%d (%d, %d, %d)\n", condition, errorNum, file, line, mode, objectInstanceID, identifier);
+#endif
+
+#if UNITY_FLASH || UNITY_WEBGL
+ printf_console(condition);
+ if (!(mode&(kScriptingWarning | kScriptingLog ))){
+#if UNITY_FLASH
+ Ext_Flash_LogCallstack();//Let's only switch this on for internal debugging, it sucks having to go through callstacks because of a log.
+#elif UNITY_WEBGL
+ // __asm __volatile__("console.log(new Error().stack)");
+#endif
+ }else{
+ return;
+ }
+#endif
+
+ string stackTrace;
+ string strippedStackTrace;
+ string preprocessedFile;
+ if (gPreprocessor)
+ {
+ preprocessedFile = file;
+ string conditionStr = condition;
+
+ gPreprocessor (conditionStr, strippedStackTrace, stackTrace, errorNum, preprocessedFile, &line, mode, objectInstanceID);
+
+ file = preprocessedFile.c_str ();
+ }
+
+ DebugStringToFilePostprocessedStacktrace (condition, strippedStackTrace.c_str(), stackTrace.c_str(), errorNum, file, line, mode, objectInstanceID, identifier);
+
+}
+
+void RemoveErrorWithIdentifierFromConsole (int identifier)
+{
+ if (gRemoveLog)
+ gRemoveLog (identifier);
+}
+
+void ShowErrorWithMode (int identifier)
+{
+ if (gShowLogWithMode)
+ gShowLogWithMode (identifier);
+}
+
+
+void DebugTextLineByLine(const char* text, int maxLineLen)
+{
+ if(maxLineLen == -1)
+ maxLineLen = 1023;
+
+ // on android we can output maximum 1023 chars (1024 including \0)
+#if UNITY_ANDROID
+ if(maxLineLen > 1023)
+ maxLineLen = 1023;
+#endif
+
+ #define SKIP_CRLF(ptr) \
+ do{ \
+ while( (*ptr == '\r' || *ptr == '\n') && *ptr != 0 ) \
+ ++ptr; \
+ } while(0)
+
+ #define SKIP_UNTIL_CRLF(ptr) \
+ do{ \
+ while( *ptr != '\r' && *ptr != '\n' && *ptr != 0 ) \
+ ++ptr; \
+ } while(0)
+
+
+ const char* lineStart = text;
+ SKIP_CRLF(lineStart);
+
+ std::string out;
+ while(*lineStart != 0)
+ {
+ const char* lineEnd = lineStart;
+ SKIP_UNTIL_CRLF(lineEnd);
+
+ if(lineEnd - lineStart > maxLineLen)
+ lineEnd = lineStart + maxLineLen;
+
+ bool needSkipCRLF = *lineEnd == '\r' || *lineEnd == '\n';
+
+ out.assign(lineStart, lineEnd-lineStart);
+ #if UNITY_ANDROID
+ __android_log_print( ANDROID_LOG_DEBUG, "Unity", "%s", out.c_str());
+ #else
+ printf_console("%s\n", out.c_str());
+ #endif
+
+ lineStart = lineEnd;
+ if(needSkipCRLF)
+ SKIP_CRLF(lineStart);
+ }
+
+ #undef SKIP_UNTIL_CRLF
+ #undef SKIP_CRLF
+}
+
+
+#if UNITY_IPHONE || UNITY_ANDROID
+
+#if UNITY_IPHONE
+#include <execinfo.h>
+#elif UNITY_ANDROID
+#include "PlatformDependent/AndroidPlayer/utils/backtrace_impl.h"
+#endif
+
+void DumpCallstackConsole( const char* prefix, const char* file, int line )
+{
+ const size_t kMaxDepth = 100;
+
+ size_t stackDepth;
+ void* stackAddr[kMaxDepth];
+ char** stackSymbol;
+
+ stackDepth = backtrace(stackAddr, kMaxDepth);
+ stackSymbol = backtrace_symbols(stackAddr, stackDepth);
+
+ printf_console("%s%s:%d\n", prefix, file, line);
+
+ // TODO: demangle? use unwind to get more info?
+ // start from 1 to bypass self
+ for( unsigned stackI = 1 ; stackI < stackDepth ; ++stackI )
+ {
+ #if UNITY_IPHONE
+ printf_console(" #%02d %s\n", stackI-1, stackSymbol[stackI]);
+ #elif UNITY_ANDROID // just for now
+ __android_log_print( ANDROID_LOG_DEBUG, "DEBUG", " #%02d %s\n", stackI-1, stackSymbol[stackI]);
+ #endif
+ }
+
+ ::free(stackSymbol);
+}
+
+#else
+
+void DumpCallstackConsole( const char* /*prefix*/, const char* /*file*/, int /*line*/ )
+{
+}
+
+#endif
+
+#if UNITY_OSX || UNITY_IPHONE
+#include <sys/sysctl.h>
+// taken from apple technical note QA1361
+bool EXPORT_COREMODULE IsDebuggerPresent()
+{
+ static bool debuggerPresent = false;
+ static bool inited = false;
+
+ if(!inited)
+ {
+ kinfo_proc info;
+ ::memset(&info, 0x00, sizeof(info));
+
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, ::getpid()};
+
+ size_t size = sizeof(info);
+ sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
+
+ debuggerPresent = (info.kp_proc.p_flag & P_TRACED) != 0;
+ inited = true;
+ }
+
+ return debuggerPresent;
+}
+#endif
+
+#if UNITY_LINUX || UNITY_TIZEN
+#include <sys/ptrace.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+bool IsDebuggerPresent ()
+{
+ int status = 0,
+ pid = -1,
+ result = 0;
+
+#ifdef PR_SET_PTRACER
+// Guard ancient versions (&*^$%@*&#^%$ build agents)
+ // Enable tracing by self and children
+ if (0 != prctl (PR_SET_PTRACER, getpid (), 0, 0, 0))
+ {
+ ErrorString (Format ("Unable to enable tracing: %s", strerror (errno)));
+ return false;
+ }
+#endif
+
+ pid = fork ();
+ if (0 > pid)
+ {
+ ErrorString ("Error creating child process");
+ return false;
+ }
+
+ if (0 == pid)
+ {
+ // Child
+ int parent = getppid();
+
+ // Attempt to attach to parent
+ if (ptrace (PTRACE_ATTACH, parent, NULL, NULL) == 0)
+ {
+ // Debugger is not attached; continue parent once it stops
+ waitpid (parent, NULL, WUNTRACED | WCONTINUED);
+ ptrace (PTRACE_DETACH, getppid (), NULL, NULL);
+ result = 0;
+ }
+ else
+ {
+ // Debugger is already tracing parent
+ result = 1;
+ }
+ exit(result);
+ }
+
+ // Parent
+ waitpid (pid, &status, 0);
+ result = WEXITSTATUS (status);
+#ifdef PR_SET_PTRACER
+ // Clear tracing
+ prctl (PR_SET_PTRACER, 0, 0, 0, 0);
+#endif
+
+ return (0 != result);
+}
+#endif
+
+
diff --git a/Runtime/Utilities/LogAssert.h b/Runtime/Utilities/LogAssert.h
new file mode 100644
index 0000000..f3cb304
--- /dev/null
+++ b/Runtime/Utilities/LogAssert.h
@@ -0,0 +1,340 @@
+#ifndef LOGASSERT_H
+#define LOGASSERT_H
+
+class Object;
+#include <string>
+#include <set>
+#include <stdarg.h>
+#include <stdio.h>
+#include "Annotations.h"
+#include "Runtime/Utilities/FileStripped.h"
+#if UNITY_EXTERNAL_TOOL
+#define EXPORT_COREMODULE
+#else
+#include "Runtime/Modules/ExportModules.h"
+#endif
+
+#if UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+ #include <signal.h>
+#endif
+
+#if UNITY_OSX || UNITY_IPHONE || UNITY_LINUX
+ extern bool IsDebuggerPresent();
+#endif
+
+enum
+{
+ kError = 1 << 0,
+ kAssert = 1 << 1,
+ kLog = 1 << 2,
+ kFatal = 1 << 4,
+ kAssetImportError = 1 << 6,
+ kAssetImportWarning = 1 << 7,
+ kScriptingError = 1 << 8,
+ kScriptingWarning = 1 << 9,
+ kScriptingLog = 1 << 10,
+ kScriptCompileError = 1 << 11,
+ kScriptCompileWarning = 1 << 12,
+ kStickyError = 1 << 13,
+ kMayIgnoreLineNumber = 1 << 14,
+ kReportBug = 1 << 15,
+ kDisplayPreviousErrorInStatusBar = 1 << 16,
+ kScriptingException = 1 << 17,
+ kDontExtractStacktrace = 1 << 18,
+ kGraphCompileError = 1 << 20,
+};
+
+/// The type of the log message in the delegate registered with Application.RegisterLogCallback.
+///
+enum LogType
+{
+ /// LogType used for Errors.
+ LogType_Error = 0,
+ /// LogType used for Asserts. (These indicate an error inside Unity itself.)
+ LogType_Assert = 1,
+ /// LogType used for Warnings.
+ LogType_Warning = 2,
+ /// LogType used for regular log messages.
+ LogType_Log = 3,
+ /// LogType used for Exceptions.
+ LogType_Exception = 4,
+ /// LogType used for Debug.
+ LogType_Debug = 5,
+ ///
+ LogType_NumLevels
+};
+
+inline const char* LogTypeToString (LogType type)
+{
+ switch (type)
+ {
+ case LogType_Assert: return "Assert";
+ case LogType_Debug: return "Debug";
+ case LogType_Exception: return "Exception";
+ case LogType_Error: return "Error";
+ case LogType_Log: return "Log";
+ case LogType_Warning: return "Warning";
+ default: return "";
+ }
+}
+
+typedef void LogToConsoleImpl (const std::string& condition, int errorNum, const char* file, int line, int type, int targetObjectInstanceID, int identifier);
+typedef void LogCallback (const std::string& condition, const std::string &stackTrace, int type);
+typedef void PreprocessCondition (const std::string& condition, std::string &strippedStacktrace, std::string &stackTrace, int errorNum, std::string& file, int* line, int type, int targetInstanceID);
+typedef void RemoveLogFunction (int identifier);
+
+/// Callback function that is invoked before a log message is written to the log output. Return true to
+/// continue logging the message as normal or false to mark the log message as handled and prevent normal
+/// processing of it.
+typedef bool (*LogEntryHandler) (LogType logType, const char* log, va_list list);
+
+void RegisterLogToConsole (LogToConsoleImpl* func);
+void RegisterLogCallback (LogCallback* callback, bool threadSafe);
+void RegisterLogPreprocessor (PreprocessCondition* func);
+void RegisterRemoveImportErrorFromConsole (RemoveLogFunction* func);
+void RegisterShowErrorWithMode (RemoveLogFunction* func);
+
+void DebugStringToFilePostprocessedStacktrace (const char* condition, const char* strippedStacktrace, const char* stacktrace, int errorNum, const char* file, int line, int mode, int targetInstanceID = 0, int identifier = 0);
+void EXPORT_COREMODULE DebugStringToFile (const char* condition, int errorNum, const char* file, int line, int mode, int targetInstanceID = 0, int identifier = 0);
+template<typename alloc>
+void EXPORT_COREMODULE DebugStringToFile (const std::basic_string<char, std::char_traits<char>, alloc>& condition, int errorNum, const char* file, int line, int mode, const int objectInstanceID = 0, int identifier = 0)
+{
+ DebugStringToFile (condition.c_str (), errorNum, file, line, mode, objectInstanceID, identifier);
+}
+
+void SetLogEntryHandler(LogEntryHandler newHandler);
+void AddCleanLogEntryHandler(LogEntryHandler newHandler);
+void ReleaseLogHandlers();
+
+void DumpCallstackConsole( const char* prefix, const char* file, int line );
+#define DUMP_CALLSTACK(message) DumpCallstackConsole(message, __FILE_STRIPPED__, __LINE__)
+
+void DebugTextLineByLine(const char* text, int maxLineLen=-1);
+
+#define ErrorIf(x) do{ if (x) DebugStringToFile (#x, 0, __FILE_STRIPPED__, __LINE__, kError); ANALYSIS_ASSUME(!(x)); }while(0)
+#define ErrorAndReturnValueIf(x, y) do{ if (x) { DebugStringToFile (#x, 0, __FILE_STRIPPED__, __LINE__, kError); return y; ANALYSIS_ASSUME(!(x)); } else { ANALYSIS_ASSUME(!(x)); } }while(0)
+#define ErrorAndReturnIf(x) ErrorAndReturnValueIf( x, /**/ )
+
+#define ErrorString(x) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kError); }while(0)
+#define ErrorStringWithoutStacktrace(x) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kDontExtractStacktrace | kError); }while(0)
+#define ErrorStringMsg(...) do{ DebugStringToFile (Format(__VA_ARGS__), 0, __FILE_STRIPPED__, __LINE__, kError); }while(0)
+#define WarningString(x) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kScriptingWarning); }while(0)
+#define WarningStringWithoutStacktrace(x) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kDontExtractStacktrace | kScriptingWarning); }while(0)
+#define WarningStringMsg(...) do{ DebugStringToFile (Format(__VA_ARGS__), 0, __FILE_STRIPPED__, __LINE__, kScriptingWarning); }while(0)
+
+
+#define ErrorOSErr(x) do{ int XERRORRESULT = x; if (XERRORRESULT) DebugStringToFile (#x, XERRORRESULT, __FILE_STRIPPED__, __LINE__, kError); }while(0)
+
+/// These errors pass an Object* as the place in which object the error occurred
+#define ErrorIfObject(x,o) do{ if (x) DebugStringToFile (#x, 0, __FILE_STRIPPED__, __LINE__, kError, (o) ? (o)->GetInstanceID() : 0); }while(0)
+#define ErrorStringObject(x,o) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kError, (o) ? (o)->GetInstanceID() : 0); }while(0)
+#define WarningStringObject(x,o) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kScriptingWarning, (o) ? (o)->GetInstanceID() : 0); }while(0)
+
+#define LogStringObject(x,o) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kLog, (o) ? (o)->GetInstanceID() : 0); }while(0)
+#define LogString(x) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kLog); }while(0)
+#define LogStringMsg(...) do{ DebugStringToFile (Format(__VA_ARGS__), 0, __FILE_STRIPPED__, __LINE__, kLog); }while(0)
+
+#define FatalErrorOSErr(x) do{ int XERRORRESULT = x; if (XERRORRESULT) DebugStringToFile (#x, XERRORRESULT, __FILE_STRIPPED__, __LINE__, kError | kFatal | kReportBug); }while(0)
+#define FatalErrorString(x) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kError | kFatal | kReportBug); }while(0)
+#define FatalErrorIf(x) do{ if (x) DebugStringToFile (#x, 0, __FILE_STRIPPED__, __LINE__, kError | kFatal | kReportBug); }while(0)
+#define FatalErrorStringDontReport(x) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kError | kFatal); }while(0)
+#define FatalErrorMsg(...) do{ DebugStringToFile (Format(__VA_ARGS__), 0, __FILE_STRIPPED__, __LINE__, kError | kFatal); }while(0)
+
+
+#define ErrorFiniteParameter(x) if (!IsFinite(x)) { ErrorString("Invalid parameter because it was infinity or nan."); return; }
+#define ErrorFiniteParameterReturnFalse(x) if (!IsFinite(x)) { ErrorString("Invalid parameter because it was infinity or nan."); return false; }
+
+
+/***** MAKE SURE THAT AssertIf's only compare code to compare, ****/
+/***** Which can safely be not called in non-debug mode ****/
+
+
+// TODO: should have ASSERT_ENABLED define, but difficult to add now
+
+#if defined(__ppc__)
+ #define DEBUG_BREAK __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" : : : "memory","r0","r3","r4" )
+#elif UNITY_OSX
+ #define DEBUG_BREAK if(IsDebuggerPresent()) __asm { int 3 }
+#elif UNITY_WIN
+ #define DEBUG_BREAK if (IsDebuggerPresent()) __debugbreak()
+#elif UNITY_XENON || UNITY_METRO
+ #define DEBUG_BREAK __debugbreak()
+#elif UNITY_PS3
+ #define DEBUG_BREAK __asm__ volatile ("tw 31,1,1 ")
+#elif (UNITY_IPHONE && !TARGET_IPHONE_SIMULATOR)
+ #define DEBUG_BREAK __asm__ __volatile__ ( "bkpt #0\n\t bx lr\n\t" : : : )
+#elif UNITY_ANDROID
+ #define DEBUG_BREAK __builtin_trap()
+#elif UNITY_LINUX
+ #define DEBUG_BREAK if (IsDebuggerPresent ()) raise(SIGTRAP)
+#else
+ #define DEBUG_BREAK
+#endif
+
+#ifndef ASSERT_SHOULD_BREAK
+// On Metro breaking without debugger attached stops the program... So don't do it !
+#define ASSERT_SHOULD_BREAK DEBUGMODE && !UNITY_RELEASE && !UNITY_METRO
+#endif
+
+#if ASSERT_SHOULD_BREAK
+ #define ASSERT_BREAK DEBUG_BREAK
+#else
+ #define ASSERT_BREAK
+#endif
+
+
+#if DEBUGMODE
+
+ #define AssertIf(x) \
+ do { \
+ if(x) \
+ { \
+ DebugStringToFile (#x, 0, __FILE_STRIPPED__, __LINE__, kAssert); \
+ ASSERT_BREAK; \
+ ANALYSIS_ASSUME(!(x)); \
+ } \
+ } while(0)
+
+ #define AssertIfObject(x,o) \
+ do { \
+ if(x) \
+ { \
+ DebugStringToFile (#x, 0, __FILE_STRIPPED__, __LINE__, kAssert, (o)?(o)->GetInstanceID():0); \
+ ASSERT_BREAK; \
+ ANALYSIS_ASSUME(!(x)); \
+ } \
+ } while(0)
+
+
+ #define Assert(x) \
+ do { \
+ if(!(x)) \
+ { \
+ DebugStringToFile (#x, 0, __FILE_STRIPPED__, __LINE__, kAssert); \
+ ASSERT_BREAK; \
+ ANALYSIS_ASSUME(x); \
+ } \
+ } while(0)
+
+ #define AssertMsg(x,...) \
+ do { \
+ if(!(x)) \
+ { \
+ DebugStringToFile (Format(__VA_ARGS__), 0, __FILE_STRIPPED__, __LINE__, kAssert); \
+ ASSERT_BREAK; \
+ ANALYSIS_ASSUME(x); \
+ } \
+ } while(0)
+
+ #define AssertMsgObject(x,o,...) \
+ do { \
+ if(!(x)) \
+ { \
+ DebugStringToFile (Format(__VA_ARGS__), 0, __FILE_STRIPPED__, __LINE__, kAssert, (o) ? (o)->GetInstanceID() : 0); \
+ ASSERT_BREAK; \
+ ANALYSIS_ASSUME(x); \
+ } \
+ } while(0)
+
+
+#else
+
+ #define AssertIf(x) do { (void)sizeof(x); } while(0)
+ #define AssertIfObject(x,o) do { (void)sizeof(x); (void)sizeof(o); } while(0)
+ #define Assert(x) do { (void)sizeof(x); } while(0)
+ #define AssertMsg(x,...) do { (void)sizeof(x); } while(0)
+ #define AssertMsgObject(x,o,...) do { (void)sizeof(x); } while(0)
+
+#endif
+
+
+#if DEBUGMODE
+
+ #define AssertString(x) { DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kAssert); }
+ #define AssertFiniteParameter(x) if (!IsFinite(x)) { AssertString("Invalid parameter because it was infinity or nan."); }
+
+ /// These errors pass an Object* as the place in which object the error occurred
+ #define AssertStringObject(x,o) { DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kAssert, (o) ? (o)->GetInstanceID() : 0); }
+ #define ScriptWarning(x,o) { DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kScriptingWarning); }
+#else
+
+ #define AssertString(x) { }
+ #define AssertFiniteParameter(x) {}
+
+ #define AssertStringObject(x,o) { }
+ #define ScriptWarning(x,o) { }
+#endif
+
+
+#if UNITY_RELEASE
+ #define DebugAssertIf(x) do { (void)sizeof(x); } while(0)
+ #define DebugAssert(x) do { (void)sizeof(x); } while(0)
+ #define DebugAssertMsg(x, ...) { }
+ #define AssertBreak(x) Assert(x)
+#else
+ #define DebugAssertIf(x) AssertIf(x)
+ #define DebugAssert(x) Assert(x)
+ #define DebugAssertMsg(x, ...) AssertMsg(x, __VA_ARGS__)
+
+ #define AssertBreak(x) \
+ do { \
+ if(!(x)) \
+ { \
+ DEBUG_BREAK; \
+ Assert(x); \
+ } \
+ } while(0)
+ #endif
+
+
+#ifdef __cplusplus
+extern "C"
+ {
+#endif
+EXPORT_COREMODULE TAKES_PRINTF_ARGS(1,2) void printf_console (const char* string, ...);
+#ifdef __cplusplus
+ }
+#endif
+void printf_consolev (LogType logType, const char* log, va_list list);
+
+#if MASTER_BUILD
+#define UNITY_TRACE(...)
+#define UNITY_TRACEIF(condition, ...)
+#else
+#define UNITY_TRACE(...) printf_console(__VA_ARGS__)
+#define UNITY_TRACEIF(condition, ...) if (condition) printf_console(__VA_ARGS__)
+#endif
+
+/// When logging an error it can be passed an identifier. The identifier can be used to remove errors from the console later on.
+/// Eg. when reimporting an asset all errors generated for that asset should be removed before importing!
+void RemoveErrorWithIdentifierFromConsole (int identifier);
+void ShowErrorWithMode (int mode);
+
+#if UNITY_OSX
+void ResetStdout();
+#endif
+
+// Route input to a custom outputfile out instead of Player.log
+void LogOutputToSpecificFile (const char* path);
+void InitializeCleanedLogFile (FILE* file);
+std::string GetCleanedLogFile();
+std::string GetConsoleLogPath();
+
+#if UNITY_EDITOR
+std::string GetEditorConsoleLogPath ();
+std::string GetPlayerConsoleLogPath ();
+std::string GetMonoDevelopLogPath ();
+#endif
+
+#if UNITY_WIN
+FILE* OpenConsole ();
+std::string SetLogFilePath(std::string const& path);
+#endif
+
+#if defined(_MSC_VER)
+ #define PRINTF_SIZET_FORMAT "Iu"
+#else
+ #define PRINTF_SIZET_FORMAT "zu"
+#endif
+
+#endif
diff --git a/Runtime/Utilities/LogUtility.cpp b/Runtime/Utilities/LogUtility.cpp
new file mode 100644
index 0000000..7b16094
--- /dev/null
+++ b/Runtime/Utilities/LogUtility.cpp
@@ -0,0 +1,4 @@
+#include "UnityPrefix.h"
+#include "LogUtility.h"
+
+int NestedLogOutput::s_LogDepth = 0;
diff --git a/Runtime/Utilities/LogUtility.h b/Runtime/Utilities/LogUtility.h
new file mode 100644
index 0000000..164ea3f
--- /dev/null
+++ b/Runtime/Utilities/LogUtility.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "LogAssert.h"
+
+// Nested log with automatic indentation. NESTED_LOG(name,fmt,...) prints to the console
+// and indents further log calls. Log indentation is decreased again when current scope ends.
+
+class NestedLogOutput
+{
+public:
+ NestedLogOutput(const char* name, const std::string& msg)
+ {
+ printf_console("%s: %*c%s\n", name, s_LogDepth, ' ', msg.c_str());
+ s_LogDepth += 4;
+ }
+ ~NestedLogOutput()
+ {
+ s_LogDepth -= 4;
+ }
+private:
+ static int s_LogDepth;
+};
+
+#if !UNITY_RELEASE
+#define NESTED_LOG(name,...) NestedLogOutput _nested_log_##__LINE__ (name,Format(__VA_ARGS__))
+#else
+#define NESTED_LOG(name,...)
+#endif
diff --git a/Runtime/Utilities/MemoryPool.cpp b/Runtime/Utilities/MemoryPool.cpp
new file mode 100644
index 0000000..f11d88a
--- /dev/null
+++ b/Runtime/Utilities/MemoryPool.cpp
@@ -0,0 +1,210 @@
+#include "UnityPrefix.h"
+#include "MemoryPool.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+
+static int kMinBlockSize = sizeof(void*);
+UNITY_VECTOR(kMemPoolAlloc,MemoryPool*)* MemoryPool::s_MemoryPools = NULL;
+void MemoryPool::StaticInitialize()
+{
+ s_MemoryPools = UNITY_NEW(UNITY_VECTOR(kMemPoolAlloc,MemoryPool*),kMemPoolAlloc);
+}
+
+void MemoryPool::StaticDestroy()
+{
+ for(size_t i = 0; i < s_MemoryPools->size(); i++)
+ UNITY_DELETE((*s_MemoryPools)[i],kMemPoolAlloc);
+ UNITY_DELETE(s_MemoryPools,kMemPoolAlloc);
+}
+
+static RegisterRuntimeInitializeAndCleanup s_MemoryPoolCallbacks(MemoryPool::StaticInitialize, MemoryPool::StaticDestroy);
+
+void MemoryPool::RegisterStaticMemoryPool(MemoryPool* pool)
+{
+ s_MemoryPools->push_back(pool);
+}
+
+MemoryPool::MemoryPool( bool threadCheck, const char* name, int blockSize, int hintSize, MemLabelId label )
+: m_AllocLabel(MemLabelId(label.label, GET_CURRENT_ALLOC_ROOT_HEADER()))
+, m_Bubbles(MemLabelId(label.label, GET_CURRENT_ALLOC_ROOT_HEADER()))
+#if DEBUGMODE
+, m_PeakAllocCount(0)
+, m_Name(name)
+#endif
+{
+ #if ENABLE_THREAD_CHECK_IN_ALLOCS
+ m_ThreadCheck = threadCheck;
+ #endif
+
+ if (blockSize < kMinBlockSize)
+ blockSize = kMinBlockSize;
+ m_BlockSize = blockSize;
+
+
+ m_BubbleSize = hintSize;
+ m_BlocksPerBubble = (m_BubbleSize - sizeof(Bubble) + 1) / blockSize;
+
+ int usedBubbleSize = sizeof(Bubble) + m_BlocksPerBubble * blockSize - 1;
+ Assert(usedBubbleSize <= m_BubbleSize);
+ Assert(m_BubbleSize - usedBubbleSize <= m_BlockSize);
+
+ Assert (m_BlocksPerBubble >= 128);
+ Assert(hintSize % 4096 == 0);
+
+ m_AllocateMemoryAutomatically = true;
+
+ Reset();
+}
+
+MemoryPool::~MemoryPool()
+{
+ #if !UNITY_EDITOR && DEBUGMODE
+ if (m_AllocCount > 0)
+ ErrorStringMsg( "Memory pool has %d unallocated objects: %s", m_AllocCount, m_Name ); // some stuff not deallocated?
+ #endif
+ DeallocateAll();
+}
+
+void MemoryPool::Reset()
+{
+ #if DEBUGMODE
+ m_AllocCount = 0;
+ #endif
+ m_HeadOfFreeList = NULL;
+}
+
+void MemoryPool::DeallocateAll()
+{
+ Bubbles::iterator it, itEnd = m_Bubbles.end();
+ for( it = m_Bubbles.begin(); it != itEnd; ++it )
+ UNITY_FREE( m_AllocLabel, *it );
+ m_Bubbles.clear();
+ Reset();
+}
+
+void MemoryPool::PreallocateMemory (int size)
+{
+ bool temp = m_AllocateMemoryAutomatically;
+ m_AllocateMemoryAutomatically = true;
+ for (int i=0;i <= size / (m_BlocksPerBubble * m_BlockSize);i++)
+ {
+ AllocNewBubble();
+ }
+ m_AllocateMemoryAutomatically = temp;
+}
+
+void MemoryPool::AllocNewBubble( )
+{
+ if (!m_AllocateMemoryAutomatically)
+ return;
+
+ AssertIf (m_BlocksPerBubble == 1); // can't have 1 element per bubble
+
+ Bubble *bubble = (Bubble*)UNITY_MALLOC( m_AllocLabel, m_BubbleSize );
+ AssertIf( !bubble );
+
+ // put to bubble list
+ m_Bubbles.push_back( bubble );
+
+ // setup the free list inside a bubble
+ void* oldHeadOfFreeList = m_HeadOfFreeList;
+ m_HeadOfFreeList = bubble->data;
+ AssertIf( !m_HeadOfFreeList );
+
+ void **newBubble = (void**)m_HeadOfFreeList;
+ for( int j = 0; j < m_BlocksPerBubble-1; ++j )
+ {
+ newBubble[0] = (char*)newBubble + m_BlockSize;
+ newBubble = (void**)newBubble[0];
+ }
+
+ newBubble[0] = oldHeadOfFreeList; // continue with existing free list (or terminate with NULL if no free elements)
+
+ // still failure, error out
+ if( !m_HeadOfFreeList )
+ {
+ ErrorString( "out of memory!" );
+ }
+}
+
+void* MemoryPool::Allocate()
+{
+ return Allocate( m_BlockSize );
+}
+
+void *MemoryPool::Allocate( size_t amount )
+{
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ ErrorAndReturnValueIf(m_ThreadCheck && Thread::mainThreadId && !Thread::CurrentThreadIsMainThread(), NULL);
+#endif
+
+
+ void *returnBlock;
+
+ if( amount > (unsigned int)m_BlockSize ) {
+ ErrorString( Format("requested larger amount than block size! requested: %d, blocksize: %d", (unsigned)amount, (unsigned)m_BlockSize ));
+ return NULL;
+ }
+
+ if( !m_HeadOfFreeList ) {
+ // allocate new bubble
+ AllocNewBubble();
+
+ // Can't allocate
+ if( m_HeadOfFreeList == NULL )
+ return NULL;
+ }
+
+ #if DEBUGMODE
+ ++m_AllocCount;
+ if( m_AllocCount > m_PeakAllocCount )
+ m_PeakAllocCount = m_AllocCount;
+ #endif
+
+ returnBlock = m_HeadOfFreeList;
+
+ // move the pointer to the next block
+ m_HeadOfFreeList = *((void**)m_HeadOfFreeList);
+
+ return returnBlock;
+}
+
+void MemoryPool::Deallocate( void *mem_Block )
+{
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ ErrorAndReturnIf(m_ThreadCheck && Thread::mainThreadId && !Thread::CurrentThreadIsMainThread());
+#endif
+
+ if( !mem_Block ) // ignore NULL deletes
+ return;
+
+ #if DEBUGMODE
+ // check to see if the memory is from the allocated range
+ bool ok = false;
+ size_t n = m_Bubbles.size();
+ for( size_t i = 0; i < n; ++i ) {
+ Bubble* p = m_Bubbles[i];
+ if( (char*)mem_Block >= p->data && (char*)mem_Block < (p->data + m_BlockSize * m_BlocksPerBubble) ) {
+ ok = true;
+ break;
+ }
+ }
+ AssertIf( !ok );
+ #endif
+
+ #if DEBUGMODE
+ // invalidate the memory
+ memset( mem_Block, 0xDD, m_BlockSize );
+ AssertIf(m_AllocCount == 0);
+ #endif
+
+ #if DEBUGMODE
+ --m_AllocCount;
+ #endif
+
+ // make the block point to the first free item in the list
+ *((void**)mem_Block) = m_HeadOfFreeList;
+ // the list head is now the Deallocated block
+ m_HeadOfFreeList = mem_Block;
+}
diff --git a/Runtime/Utilities/MemoryPool.h b/Runtime/Utilities/MemoryPool.h
new file mode 100644
index 0000000..b7ef488
--- /dev/null
+++ b/Runtime/Utilities/MemoryPool.h
@@ -0,0 +1,280 @@
+#ifndef MEMORY_POOL_H_
+#define MEMORY_POOL_H_
+
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Modules/ExportModules.h"
+
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+#include "Runtime/Threads/Thread.h"
+#endif
+
+
+// --------------------------------------------------------------------------
+// A free-list based fixed size allocator.
+//
+// To override new/delete per class use DECLARE_POOLED_ALLOC and DEFINE_POOLED_ALLOC.
+//
+// memory_pool<T> is an STL allocator that only supports allocating a single element
+// at a time. So it can be used with list, map, set but not with vector or string.
+//
+// Allocator creates "bubbles" of objects, each containin a free-list inside. When a bubble
+// is full, it allocates a new one. Empty bubbles are NOT destroyed.
+
+
+// --------------------------------------------------------------------------
+
+class EXPORT_COREMODULE MemoryPool {
+public:
+ MemoryPool( bool threadCheck, const char* name, int blockSize, int allocatedSizeHint, MemLabelId label = kMemPoolAlloc );
+ ~MemoryPool();
+
+ /// Allocate single block
+ void* Allocate();
+ /// Allocate less than single block
+ void* Allocate( size_t amount );
+ /// Deallocate
+ void Deallocate( void *ptr );
+ /// Deallocate everything
+ void DeallocateAll();
+
+
+ #if !DEPLOY_OPTIMIZED
+ size_t GetBubbleCount() const { return m_Bubbles.size(); }
+ int GetAllocCount() const { return m_AllocCount; }
+ #endif
+
+ int GetAllocatedBytes() { return m_Bubbles.size () * m_BlocksPerBubble * m_BlockSize; }
+
+ #if DEBUGMODE
+ int GetAllocatedObjectsCount() { return m_AllocCount; }
+ #endif
+
+ void PreallocateMemory(int size);
+ void SetAllocateMemoryAutomatically (bool allocateMemoryAuto) { m_AllocateMemoryAutomatically = allocateMemoryAuto; }
+
+ static void StaticInitialize();
+ static void StaticDestroy();
+ static void RegisterStaticMemoryPool(MemoryPool* pool);
+
+private:
+ void AllocNewBubble();
+
+ struct Bubble
+ {
+ char data[1]; // actually byteCount
+ };
+ typedef dynamic_array<Bubble*> Bubbles;
+
+ void Reset();
+private:
+ int m_BlockSize;
+ int m_BubbleSize;
+ int m_BlocksPerBubble;
+
+ Bubbles m_Bubbles;
+
+ void* m_HeadOfFreeList;
+ bool m_AllocateMemoryAutomatically;
+
+ MemLabelId m_AllocLabel;
+
+ int m_AllocCount; // number of blocks currently allocated
+ #if DEBUGMODE
+ int m_PeakAllocCount; // stats
+ const char* m_Name; // for debugging
+ #endif
+
+ #if ENABLE_THREAD_CHECK_IN_ALLOCS
+ bool m_ThreadCheck;
+ #endif
+
+ static UNITY_VECTOR(kMemPoolAlloc,MemoryPool*)* s_MemoryPools;
+};
+
+
+// --------------------------------------------------------------------------
+// Macros for class fixed-size pooled allocations:
+// DECLARE_POOLED_ALLOC in the .h file, in a private section of a class,
+// DEFINE_POOLED_ALLOC in the .cpp file
+
+#define ENABLE_MEMORY_POOL 1
+
+#if ENABLE_MEMORY_POOL
+
+#define STATIC_INITIALIZE_POOL( _clazz ) _clazz::s_PoolAllocator = UNITY_NEW(MemoryPool, kMemPoolAlloc)(true, #_clazz, sizeof(_clazz), _clazz::s_PoolSize)
+#define STATIC_DESTROY_POOL( _clazz ) UNITY_DELETE(_clazz::s_PoolAllocator, kMemPoolAlloc)
+
+#define DECLARE_POOLED_ALLOC( _clazz ) \
+public: \
+ inline void* operator new( size_t size ) { return s_PoolAllocator->Allocate(size); } \
+ inline void operator delete( void* p ) { s_PoolAllocator->Deallocate(p); } \
+ static MemoryPool *s_PoolAllocator; \
+ static int s_PoolSize; \
+private:
+
+#define DEFINE_POOLED_ALLOC( _clazz, _bubbleSize ) \
+ MemoryPool* _clazz::s_PoolAllocator = NULL; \
+ int _clazz::s_PoolSize = _bubbleSize;
+
+#else
+
+#define STATIC_INITIALIZE_POOL( _clazz )
+#define STATIC_DESTROY_POOL( _clazz )
+#define DECLARE_POOLED_ALLOC( _clazz )
+#define DEFINE_POOLED_ALLOC( _clazz, _bubbleSize )
+
+#endif
+
+
+// --------------------------------------------------------------------------
+
+template<int SIZE>
+struct memory_pool_impl
+{
+ struct AutoPoolWrapper
+ {
+ AutoPoolWrapper( int size)
+ {
+ SET_ALLOC_OWNER(NULL);
+ pool = UNITY_NEW(MemoryPool( true, "mempoolalloc", size, 32 * 1024, kMemPoolAlloc ), kMemPoolAlloc);
+ MemoryPool::RegisterStaticMemoryPool(pool);
+ }
+ ~AutoPoolWrapper()
+ {
+ }
+ MemoryPool* pool;
+ };
+ static MemoryPool& get_pool () {
+ static AutoPoolWrapper pool( SIZE );
+ return *(pool.pool);
+ }
+};
+
+/*
+
+ THIS IS NOT THREAD SAFE. sharing of pools is by size, thus pools might randomly be shared from different threads.
+
+*/
+
+
+
+template<typename T>
+class memory_pool
+{
+public:
+ typedef size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T value_type;
+
+ template <class U> struct rebind { typedef memory_pool<U> other; };
+
+ memory_pool() { }
+ memory_pool( const memory_pool<T>& ) { }
+ template<class B> memory_pool(const memory_pool<B>&) { } // construct from a related allocator
+ template<class B> memory_pool<T>& operator=(const memory_pool<B>&) { return *this; } // assign from a related allocator
+
+ ~memory_pool() throw() { }
+
+ pointer address(reference x) const { return &x; }
+ const_pointer address(const_reference x) const { return &x; }
+ size_type max_size() const throw() {return size_t(-1) / sizeof(value_type);}
+ void construct(pointer p, const T& val) { ::new((void*)p) T(val); }
+ void destroy(pointer p) { p->~T(); }
+
+ pointer allocate(size_type n, std::allocator<void>::const_pointer /*hint*/ = 0)
+ {
+ if(n==1)
+ return reinterpret_cast<pointer>( memory_pool_impl<sizeof(T)>::get_pool ().Allocate(n * sizeof(T)) );
+ else
+ return reinterpret_cast<pointer>(UNITY_MALLOC(kMemPoolAlloc,n*sizeof(T)));
+ }
+
+ void deallocate(pointer p, size_type n)
+ {
+ if(n==1)
+ return memory_pool_impl<sizeof(T)>::get_pool ().Deallocate( p );
+ else
+ return UNITY_FREE(kMemPoolAlloc,p);
+ }
+};
+
+template<typename T>
+class memory_pool_explicit
+{
+public:
+ typedef size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T value_type;
+ template <class U> struct rebind { typedef memory_pool_explicit<U> other; };
+
+ MemoryPool* m_Pool;
+
+ memory_pool_explicit(MemoryPool& pool) { m_Pool = &pool; }
+ memory_pool_explicit() { m_Pool = NULL; }
+ memory_pool_explicit( const memory_pool_explicit<T>& b) { m_Pool = b.m_Pool; }
+ template<class B> memory_pool_explicit(const memory_pool_explicit<B>& b) { m_Pool = b.m_Pool; } // construct from a related allocator
+ template<class B> memory_pool_explicit<T>& operator=(const memory_pool_explicit<B>& b) { m_Pool = b.m_Pool; return *this; } // assign from a related allocator
+
+ ~memory_pool_explicit() throw() { }
+
+ pointer address(reference x) const { return &x; }
+ const_pointer address(const_reference x) const { return &x; }
+ size_type max_size() const throw() {return size_t(-1) / sizeof(value_type);}
+ void construct(pointer p, const T& val) { ::new((void*)p) T(val); }
+ void destroy(pointer p) { p->~T(); }
+
+ pointer allocate(size_type n, std::allocator<void>::const_pointer /*hint*/ = 0)
+ {
+ DebugAssertIf(n != 1);
+ AssertIf(m_Pool == NULL);
+ return reinterpret_cast<pointer>( m_Pool->Allocate(n * sizeof(T)) );
+ }
+
+ void deallocate(pointer p, size_type n)
+ {
+ DebugAssertIf(n != 1);
+ AssertIf(m_Pool == NULL);
+ m_Pool->Deallocate( p );
+ }
+};
+
+template<typename A, typename B>
+inline bool operator==( const memory_pool<A>&, const memory_pool<B>& )
+{
+ // test for allocator equality (always true)
+ return true;
+}
+
+template<typename A, typename B>
+inline bool operator!=( const memory_pool<A>&, const memory_pool<B>& )
+{
+ // test for allocator inequality (always false)
+ return false;
+}
+
+
+template<typename A, typename B>
+inline bool operator==( const memory_pool_explicit<A>& lhs, const memory_pool_explicit<B>& rhs)
+{
+ // test for allocator equality (always true)
+ return lhs.m_Pool == rhs.m_Pool;
+}
+
+template<typename A, typename B>
+inline bool operator!=( const memory_pool_explicit<A>& lhs, const memory_pool_explicit<B>& rhs)
+{
+ // test for allocator inequality (always false)
+ return lhs.m_Pool != rhs.m_Pool;
+}
+
+#endif
diff --git a/Runtime/Utilities/MemoryUtilities.cpp b/Runtime/Utilities/MemoryUtilities.cpp
new file mode 100644
index 0000000..b97ffad
--- /dev/null
+++ b/Runtime/Utilities/MemoryUtilities.cpp
@@ -0,0 +1,15 @@
+#include "UnityPrefix.h"
+#include "MemoryUtilities.h"
+
+void memset32 (void *dst, UInt32 value, UInt64 bytecount)
+{
+ UInt32 i;
+ for( i = 0; i < (bytecount & (~3)); i+=4 )
+ {
+ *((UInt32*)((char*)dst + i)) = value;
+ }
+ for( ; i < bytecount; i++ )
+ {
+ ((char*)dst)[i] = ((char*)&value)[i&4];
+ }
+}
diff --git a/Runtime/Utilities/MemoryUtilities.h b/Runtime/Utilities/MemoryUtilities.h
new file mode 100644
index 0000000..78c097e
--- /dev/null
+++ b/Runtime/Utilities/MemoryUtilities.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void memset32(void *dst, UInt32 value, UInt64 bytecount);
diff --git a/Runtime/Utilities/NonCopyable.h b/Runtime/Utilities/NonCopyable.h
new file mode 100644
index 0000000..e23357f
--- /dev/null
+++ b/Runtime/Utilities/NonCopyable.h
@@ -0,0 +1,18 @@
+#ifndef NON_COPYABLE_H
+#define NON_COPYABLE_H
+
+#ifndef EXPORT_COREMODULE
+#define EXPORT_COREMODULE
+#endif
+
+class EXPORT_COREMODULE NonCopyable
+{
+public:
+ NonCopyable() {}
+
+private:
+ NonCopyable(const NonCopyable&);
+ NonCopyable& operator=(const NonCopyable&);
+};
+
+#endif
diff --git a/Runtime/Utilities/OptimizationUtility.h b/Runtime/Utilities/OptimizationUtility.h
new file mode 100644
index 0000000..5b69177
--- /dev/null
+++ b/Runtime/Utilities/OptimizationUtility.h
@@ -0,0 +1,20 @@
+#ifndef OPTIMIZATION_UTILITY_H
+#define OPTIMIZATION_UTILITY_H 1
+
+
+// ALIGN_LOOP_OPTIMIZATION should be placed in heavy inner loops!
+// for (int i=0;i<10000;i++)
+// ALIGN_LOOP_OPTIMIZATION
+// ...
+
+// On the Wii global function alignment is 4 for debug builds to save exe size
+// as there are a lot of non-inlined functions.
+// In that case having align 16 generates warnings, so we blank the define.
+#if defined(__MWERKS__) && UNITY_RELEASE && !defined(_DEBUG)
+#define ALIGN_LOOP_OPTIMIZATION asm {align 16}
+#else
+///@TODO: optimize this for gcc too
+#define ALIGN_LOOP_OPTIMIZATION
+#endif
+
+#endif
diff --git a/Runtime/Utilities/PathNameUtility.cpp b/Runtime/Utilities/PathNameUtility.cpp
new file mode 100644
index 0000000..d168c61
--- /dev/null
+++ b/Runtime/Utilities/PathNameUtility.cpp
@@ -0,0 +1,601 @@
+#include "UnityPrefix.h"
+#include "PathNameUtility.h"
+#include "Word.h"
+
+#include <algorithm>
+
+using namespace std;
+
+static void AppendPathNameImpl( const string& pathName, const string& append, char separator, std::string& res )
+{
+ res.reserve (pathName.size () + append.size () + 1);
+ if (!pathName.empty () && !append.empty ())
+ {
+ if (pathName[pathName.size () - 1] == separator)
+ {
+ if (append[0] == separator)
+ {
+ res += pathName;
+ res.append (append.begin () + 1, append.end ());
+ return;
+ }
+ else
+ {
+ res += pathName;
+ res += append;
+ return;
+ }
+ }
+ else
+ {
+ if (append[0] == separator)
+ {
+ res += pathName;
+ res += append;
+ return;
+ }
+ else
+ {
+ res += pathName;
+ res += separator;
+ res += append;
+ return;
+ }
+ }
+ }
+ else if (pathName.empty ())
+ res = append;
+ else
+ res = pathName;
+}
+
+string AppendPathName (const string& pathName, const string& append)
+{
+ string res;
+ AppendPathNameImpl( pathName, append, kPathNameSeparator, res );
+ return res;
+}
+
+string PlatformAppendPathName (const string& pathName, const string& append)
+{
+ string res;
+ AppendPathNameImpl( pathName, append, kPlatformPathNameSeparator, res );
+ return res;
+}
+
+
+string AppendPathNameExtension (const string& pathName, const string& extension)
+{
+ if (extension.empty ())
+ return pathName;
+
+ string newPathName;
+ newPathName.reserve (pathName.size () + 1 + extension.size ());
+ newPathName.append (pathName);
+ newPathName.append (".");
+ newPathName.append (extension);
+ return newPathName;
+}
+
+string GetPathNameExtension (const string& pathName)
+{
+ return GetPathNameExtension (pathName.c_str(), pathName.size());
+}
+
+const char* GetPathNameExtension (const char* path, size_t length)
+{
+ for (size_t i=0;i<length;i++)
+ {
+ if (path[length - i - 1] == kPathNameSeparator)
+ return "";
+ if (path[length - i - 1] == '.')
+ return path + length - i;
+ }
+ return "";
+}
+
+string DeletePathNameExtension (const string& pathName)
+{
+ string::size_type slash = pathName.rfind (kPathNameSeparator);
+ string::size_type dot = pathName.rfind ('.');
+
+ if (dot != string::npos)
+ {
+ if (slash == string::npos || dot > slash)
+ return string (&pathName[0], dot);
+ }
+ return pathName;
+}
+
+vector<string> FindSeparatedPathComponents (char const* constPathName, size_t size, char separator)
+{
+ char const* current = constPathName, *end = constPathName + size;
+ vector<string> components;
+
+ while (current != end)
+ {
+ char const* pos = std::find (current, end, separator);
+
+ if (pos != current)
+ components.push_back (std::string (current, pos));
+
+ if (pos == end)
+ break;
+
+ current = pos + 1;
+ }
+
+ return components;
+}
+
+string DeleteLastPathNameComponent (const string& pathName)
+{
+ string::size_type p = pathName.rfind (kPathNameSeparator);
+ if (p == string::npos)
+ return string ();
+ else
+ return string (&pathName[0], p);
+}
+
+string PlatformDeleteLastPathNameComponent (const string& pathName)
+{
+ string::size_type p = pathName.rfind (kPlatformPathNameSeparator);
+ if (p == string::npos)
+ return string ();
+ else
+ return string (&pathName[0], p);
+}
+
+string GetLastPathNameComponent (const string& pathName)
+{
+ return GetLastPathNameComponent(pathName.c_str(), pathName.size());
+}
+
+const char* GetLastPathNameComponent (const char* path, size_t length)
+{
+ for (size_t i=0;i<length;i++)
+ {
+ if (path[length - i - 1] == kPathNameSeparator)
+ return path + length - i;
+ }
+ return path;
+}
+
+string PlatformGetLastPathNameComponent(const string& pathName)
+{
+ string::size_type p = pathName.rfind (kPlatformPathNameSeparator);
+ if (p == string::npos)
+ return pathName;
+ else
+ return string (&pathName[1 + p], pathName.size () - 1 - p);
+}
+
+string DeleteFirstPathNameComponent (const string& pathName)
+{
+ string::size_type p = pathName.find (kPathNameSeparator);
+ if (p == string::npos)
+ return string ();
+ else
+ return string (&pathName[1 + p], pathName.size () - 1 - p);
+}
+
+string StandardizePathName (const string& pathName)
+{
+ if (pathName.empty ())
+ return pathName;
+
+ // Remove initial / if not a // in the beginning
+ if (pathName[0] == kPathNameSeparator && pathName.size () > 1 && pathName[1] != kPathNameSeparator)
+ return string (&pathName[1], pathName.size () - 1);
+ else
+ return pathName;
+}
+
+bool IsPathNameVisible (const string& path)
+{
+ if (path.empty())
+ return false;
+
+ string::size_type p = 0;
+ while (p < path.size ())
+ {
+ if (path[p] == '.')
+ return false;
+ p = path.find (kPathNameSeparator, p);
+ if (p == string::npos)
+ return true;
+ p++;
+ }
+ return true;
+}
+
+
+void ConvertSeparatorsToUnity( char* pathName )
+{
+ while( *pathName != '\0' ) {
+ if( *pathName == '\\' )
+ *pathName = kPathNameSeparator;
+ ++pathName;
+ }
+}
+
+void ConvertSeparatorsToPlatform( char* pathName )
+{
+ if (kPathNameSeparator != kPlatformPathNameSeparator) {
+ while( *pathName != '\0' ) {
+ if( *pathName == kPathNameSeparator )
+ *pathName = kPlatformPathNameSeparator;
+ ++pathName;
+ }
+ }
+}
+
+void ConvertSeparatorsToPlatform( std::string& pathName )
+{
+ if (kPathNameSeparator != kPlatformPathNameSeparator) {
+ std::string::iterator it = pathName.begin(), itEnd = pathName.end();
+ while( it != itEnd ) {
+ if( *it == kPathNameSeparator )
+ *it = kPlatformPathNameSeparator;
+ ++it;
+ }
+ }
+}
+
+#if UNITY_EDITOR
+const char* invalidFilenameChars = "/?<>\\:*|\"";
+
+const char* GetInvalidFilenameChars ()
+{
+ return invalidFilenameChars;
+}
+
+bool IsValidFileNameSymbol (const char c)
+{
+ return std::string(invalidFilenameChars).find(c) == string::npos;
+}
+
+bool CheckValidFileName (const std::string& name)
+{
+ return CheckValidFileNameDetail(name) == kFileNameValid;
+}
+
+
+std::string MakeFileNameValid (const std::string& fileName)
+{
+ string output = fileName;
+
+ // Disallow space at the beginning
+ while (!output.empty() && output[0] == ' ')
+ output.erase(0, 1);
+
+ if (output.empty())
+ return output;
+
+ // Disallow space at the end
+ if (output[output.size()-1] == ' ' || output[output.size()-1] == '.')
+ output[output.size()-1] = '_';
+
+ for (int i=0;i<output.size();i++)
+ {
+ if (!IsValidFileNameSymbol(output[i]))
+ output[i] = '_';
+ }
+
+
+ if (CheckValidFileName(output))
+ return output;
+ else
+ return string();
+}
+
+
+FileNameValid CheckValidFileNameDetail (const std::string& name)
+{
+ // none of these can be part of file/folder name
+ if( name.find_first_of("/\\") != string::npos )
+ return kFileNameInvalid;
+
+ // anything up to first dot can't be: com1..com9, lpt1..lpt9, con, nul, prn
+ size_t dotIndex = name.find('.');
+ if( dotIndex == 4 )
+ {
+ if( name[0]=='c' && name[1]=='o' && name[2]=='m' && name[3]>='0' && name[3]<='9' )
+ return kFileNameInvalid;
+ if( name[0]=='l' && name[1]=='p' && name[2]=='t' && name[3]>='0' && name[3]<='9' )
+ return kFileNameInvalid;
+ }
+ if( dotIndex == 3 )
+ {
+ if( name[0]=='c' && name[1]=='o' && name[2]=='n' )
+ return kFileNameInvalid;
+ if( name[0]=='n' && name[1]=='u' && name[2]=='l' )
+ return kFileNameInvalid;
+ if( name[0]=='p' && name[1]=='r' && name[2]=='n' )
+ return kFileNameInvalid;
+ }
+
+ // . on OS X means invisible file
+ if( dotIndex == 0 )
+ return kFileNameInvalid;
+
+ // file/folder name can't end with a dot or a space
+ if( !name.empty() )
+ {
+ // http://support.microsoft.com/kb/115827
+ char lastChar = name[name.size()-1];
+ if( lastChar == ' ' || lastChar == '.' )
+ return kFileNameInvalid;
+
+ // File names starting with a space will not get preserved correctly when zipping
+ // on windows. So don't allow that.
+ if (name[0] == ' ')
+ return kFileNameNotRecommended;
+ }
+
+ if( name.find_first_of(invalidFilenameChars) != string::npos )
+ return kFileNameNotRecommended;
+
+ return kFileNameValid;
+}
+
+std::string TrimSlash(const std::string& path)
+{
+ if (!path.empty())
+ {
+ const char lastChar = path[path.size() - 1];
+ if (lastChar == '/' || lastChar == '\\')
+ return path.substr(0, path.size() - 1);
+ }
+
+ return path;
+}
+
+string NormalizeUnicode (const string& utf8, bool decompose)
+{
+#if UNITY_OSX
+ // OS X stores Unicode file names in decomposed form. Ie † (U-Umlaut), becomes represented as two characters
+ // -U and ¬ (Unicode U+0308), instead of a single † (Unicode U+00DC). For proper display and consistency of
+ // names, they need to be converted into precomposed form.
+ CFStringRef cfLowercased = StringToCFString(utf8);
+ if (!cfLowercased)
+ ErrorStringMsg("Failed to convert string '%s' to CFString", utf8.c_str());
+ CFMutableStringRef cf = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfLowercased);
+ CFStringNormalize(cf, decompose?kCFStringNormalizationFormD:kCFStringNormalizationFormC);
+ string result = CFStringToString(cf);
+ if (cf != NULL)
+ CFRelease(cf);
+ if (cfLowercased != NULL)
+ CFRelease(cfLowercased);
+ return result;
+#else
+ return utf8;
+#endif
+}
+
+// Serialization does weird shit without case mangling - even if filesystem paths shouldn't
+// be mangled on Linux, we apparently need them mangled in some serialization stuff anyway...
+string& GetGoodPathNameForGUID (const string& pathname)
+{
+ static string lowercased;
+
+ lowercased = GetGoodPathName(pathname);
+
+#if UNITY_LINUX
+ ToLowerInplace(lowercased);
+#endif
+
+ return lowercased;
+}
+
+string& GetGoodPathName (const string& pathname)
+{
+ static string lowercased;
+ if (lowercased.capacity () < 4096)
+ lowercased.reserve (4096);
+
+ if (!pathname.empty () && pathname[0] == kPathNameSeparator)
+ {
+ lowercased.clear();
+ return lowercased;
+ }
+
+ lowercased = pathname;
+
+#if UNITY_WIN
+ ConvertSeparatorsToUnity( lowercased );
+#endif
+
+#if UNITY_OSX
+ if (RequiresNormalization(lowercased))
+ lowercased = NormalizeUnicode (lowercased, false);
+#endif
+
+#if !UNITY_LINUX
+ ToLowerInplace(lowercased);
+#endif
+
+ return lowercased;
+}
+
+#endif
+
+
+#if !UNITY_PLUGIN
+bool StartsWithPath (const std::string &s, const std::string& beginsWith)
+{
+ size_t beginsWithSize = beginsWith.size ();
+ if (s.size () < beginsWithSize)
+ return false;
+
+ for (size_t i=0;i<beginsWithSize;i++)
+ {
+ if (ToLower(s[i]) != ToLower(beginsWith[i]))
+ return false;
+ }
+
+ // Exact match or empty beginsWith
+ if (s.size () == beginsWithSize || beginsWith.empty())
+ return true;
+ // Next unmatched character or last matched character is
+ // path separator.
+ else if (s[beginsWithSize] == kPathNameSeparator ||
+ beginsWith[beginsWithSize - 1] == kPathNameSeparator)
+ return true;
+ else
+ return false;
+}
+
+// Converts a hex digit to the actual value. Invalid hex characters are treated as '0'
+static inline unsigned char Unhex(char c) {
+ if( c >= '0' && c <= '9')
+ return c-'0';
+ if( c >= 'a' && c <= 'f')
+ return c-'a'+10;
+ if( c >= 'A' && c <= 'F')
+ return c-'A'+10;
+ return 0;
+}
+
+// Takes a string containing path characters and URL escapes and returns a string that can be used as a file name
+string EncodePath(const string& s)
+{
+ string result;
+ size_t i=0;
+ while( i < s.size() )
+ {
+ size_t j=s.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ", i);
+ if (j == string::npos )
+ {
+ result += ToLower(s.substr(i));
+ break;
+ }
+
+ if( j>i ) result += ToLower(s.substr(i,j-i));
+
+ if ( s[j] == '%' && j+2 < s.size() )
+ {
+ // decode %xx URL escapes
+ unsigned char decoded = ToLower((char)(Unhex(s[j+1]) << 4 | Unhex(s[j+2])));
+ if( (decoded >= 'a' && decoded <= 'z') || (decoded >= '0' && decoded <= '9') || decoded == ' ' )
+ result += decoded;
+ else if (decoded == '/')
+ result += '-';
+ else
+ result += Format("_%02x",(int)decoded);
+ i=j+3;
+
+ }
+ else if (s[j] == '?')
+ {
+ // Don't include URL parameters
+ break;
+ }
+ else if (s[j] == '/')
+ {
+ // Convert slashes to dashes
+ while ( j+1 < s.size() && s[j+1] == '/' ) j++; // Collapse multiple /es into one
+ result += '-';
+ i=j+1;
+ }
+ else
+ {
+ // Encode anything "dangerous" as _XX hex encoding
+ result += Format("_%02x",(int)s[j]);
+ i=j+1;
+ }
+ }
+
+ return result;
+}
+
+string DecodePath(const string& s)
+{
+ string result;
+ size_t i=0;
+ while( i < s.size() )
+ {
+ size_t j=s.find("_", i);
+ if (j == string::npos )
+ {
+ result += s.substr(i);
+ break;
+ }
+
+ if( j>i ) result += s.substr (i,j-i);
+
+ string escape = s.substr (j, 3);
+ unsigned int ch = ' ';
+ sscanf(escape.c_str(), "_%x", &ch);
+
+ result += (unsigned char)ch;
+ i = j+3;
+ }
+
+ return result;
+}
+
+#endif
+
+
+
+// --------------------------------------------------------------------------
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (PathNameUtilityTests)
+{
+ #if UNITY_EDITOR
+ TEST(RequiresNormalization)
+ {
+ CHECK(!RequiresNormalization(""));
+ CHECK(!RequiresNormalization("!aA09~|"));
+ CHECK(RequiresNormalization("\n"));
+ CHECK(RequiresNormalization("\t"));
+ CHECK(RequiresNormalization("\x80"));
+ CHECK(RequiresNormalization("\xCC"));
+ }
+
+ TEST(CheckValidFileName)
+ {
+ CHECK_EQUAL(kFileNameNotRecommended, CheckValidFileNameDetail(" temp"));
+ CHECK_EQUAL(kFileNameValid, CheckValidFileNameDetail("temp"));
+ CHECK_EQUAL(kFileNameInvalid, CheckValidFileNameDetail("temp."));
+ CHECK_EQUAL(kFileNameValid, CheckValidFileNameDetail("tet.png.bytes"));
+ CHECK_EQUAL(kFileNameInvalid, CheckValidFileNameDetail("test/"));
+ CHECK_EQUAL(kFileNameInvalid, CheckValidFileNameDetail("test\\"));
+ CHECK_EQUAL(kFileNameNotRecommended, CheckValidFileNameDetail("te:st"));
+
+ CHECK_EQUAL(kFileNameValid, CheckValidFileNameDetail("test...test.png"));
+ CHECK_EQUAL(kFileNameValid, CheckValidFileNameDetail("test..test.png"));
+ CHECK_EQUAL(kFileNameInvalid, CheckValidFileNameDetail(".."));
+ CHECK_EQUAL(kFileNameInvalid, CheckValidFileNameDetail("..."));
+
+ CHECK_EQUAL(kFileNameInvalid, CheckValidFileNameDetail(GetLastPathNameComponent("Assets/\\bad.tmp")));
+
+ CHECK_EQUAL("test", MakeFileNameValid("test"));
+ CHECK_EQUAL("", MakeFileNameValid(" "));
+ CHECK_EQUAL("", MakeFileNameValid(" "));
+ CHECK_EQUAL("", MakeFileNameValid("..test"));
+ CHECK_EQUAL("test", MakeFileNameValid(" test"));
+ CHECK_EQUAL("test_", MakeFileNameValid(" test "));
+ CHECK_EQUAL("test_", MakeFileNameValid("test."));
+ CHECK_EQUAL("____", MakeFileNameValid(" /// "));
+ }
+ #endif
+
+ TEST(GetPathNameExtension)
+ {
+ CHECK(strcmp(GetPathNameExtension(".dll").c_str(), "dll") == 0);
+ CHECK(strcmp(GetPathNameExtension(".dll/boing").c_str(), "") == 0);
+ CHECK(strcmp(GetPathNameExtension("hello/.dlL").c_str(), "dlL") == 0);
+ CHECK(strcmp(GetPathNameExtension("hello/blah.grr.dll").c_str(), "dll") == 0);
+ CHECK(strcmp(GetPathNameExtension("hello/boing.dll").c_str(), "dll") == 0);
+ }
+
+
+} // SUITE
+
+#endif // ENABLE_UNIT_TESTS
diff --git a/Runtime/Utilities/PathNameUtility.h b/Runtime/Utilities/PathNameUtility.h
new file mode 100644
index 0000000..0ab1c48
--- /dev/null
+++ b/Runtime/Utilities/PathNameUtility.h
@@ -0,0 +1,153 @@
+#ifndef PATHNAMEUTILITY_H
+#define PATHNAMEUTILITY_H
+
+#include <string>
+#include <vector>
+#include <functional>
+
+
+// Path names in Unity always use forward slash as a separator; and convert to other one (if needed)
+// only in low-level file functions.
+const char kPathNameSeparator = '/';
+
+// If absolutely required, this defines a platform specific path separator.
+#if UNITY_WIN || UNITY_XENON
+const char kPlatformPathNameSeparator = '\\';
+#elif UNITY_OSX || UNITY_WII || UNITY_PS3 || UNITY_IPHONE || UNITY_ANDROID || UNITY_PEPPER || UNITY_LINUX || UNITY_FLASH || UNITY_WEBGL || UNITY_BB10 || UNITY_TIZEN
+const char kPlatformPathNameSeparator = '/';
+#else
+#error "Unknown platform"
+#endif
+
+
+
+std::string AppendPathName (const std::string& pathName, const std::string& append);
+std::string AppendPathNameExtension (const std::string& pathName, const std::string& extension);
+
+std::string GetPathNameExtension (const std::string& pathName);
+const char* GetPathNameExtension (const char* path, size_t cachedStringLength);
+std::string GetLastPathNameComponent (const std::string& pathName);
+const char* GetLastPathNameComponent (const char* path, size_t length);
+
+
+// Returns true if path is a child folder of beginsWith or the path itself. Case insensitive.
+bool StartsWithPath (const std::string &path, const std::string& beginsWith);
+
+std::vector<std::string> FindSeparatedPathComponents (char const* pathName, size_t size, char separator);
+inline std::vector<std::string> FindSeparatedPathComponents (std::string const& pathName, char separator) {
+ return FindSeparatedPathComponents (pathName.c_str (), pathName.size (), separator);
+}
+
+std::string DeletePathNameExtension (const std::string& pathName);
+std::string StandardizePathName (const std::string& pathName);
+
+std::string DeleteLastPathNameComponent (const std::string& pathName);
+std::string DeleteFirstPathNameComponent (const std::string& pathName);
+
+inline std::string GetFileNameWithoutExtension(const std::string& pathName) { return DeletePathNameExtension(GetLastPathNameComponent(pathName)); }
+
+bool IsPathNameVisible (const std::string& path);
+
+
+void ConvertSeparatorsToUnity( char* pathName );
+template<typename alloc>
+void ConvertSeparatorsToUnity( std::basic_string<char, std::char_traits<char>, alloc>& pathName )
+{
+ typename std::basic_string<char, std::char_traits<char>, alloc>::iterator it = pathName.begin(), itEnd = pathName.end();
+ while( it != itEnd ) {
+ if( *it == '\\' )
+ *it = kPathNameSeparator;
+ ++it;
+ }
+}
+void ConvertSeparatorsToPlatform( char* pathName );
+void ConvertSeparatorsToPlatform( std::string& pathName );
+
+int StrICmp (const char* a, const char* b);
+
+// These functions operate on platform dependent path separators
+
+std::string PlatformAppendPathName (const std::string& pathName, const std::string& append);
+std::string PlatformGetLastPathNameComponent (const std::string& pathName);
+std::string PlatformDeleteLastPathNameComponent (const std::string& pathName);
+
+std::string EncodePath(const std::string& s);
+std::string DecodePath(const std::string& s);
+
+#if UNITY_EDITOR
+bool IsValidFileNameSymbol (const char c);
+
+bool CheckValidFileName (const std::string& name);
+
+const char* GetInvalidFilenameChars ();
+
+enum FileNameValid { kFileNameValid = 0, kFileNameInvalid = 1, kFileNameNotRecommended = 2 };
+FileNameValid CheckValidFileNameDetail (const std::string& name);
+
+std::string MakeFileNameValid (const std::string& fileName);
+
+// Removes last / or \ if it exists in path
+std::string TrimSlash(const std::string& path);
+
+std::string& GetGoodPathNameForGUID (const std::string& pathname);
+std::string& GetGoodPathName (const std::string& pathname);
+
+std::string NormalizeUnicode (const std::string& utf8, bool decompose);
+
+inline bool RequiresNormalization (const char* utf8)
+{
+ const unsigned char* c = (const unsigned char*)utf8;
+ while (*c)
+ {
+ if (*c < 32 || *c > 127)
+ return true;
+
+ c++;
+ }
+
+ return false;
+}
+
+inline bool RequiresNormalization (const std::string& utf8)
+{
+ return RequiresNormalization (utf8.c_str());
+}
+
+// Use this functor as a comparison function when using paths as keys in std::map.
+struct PathCompareFunctor : std::binary_function<std::string, std::string, bool>
+{
+ bool operator() ( const std::string& path_a, const std::string& path_b ) const
+ {
+ #if UNITY_OSX
+ bool ascii = !RequiresNormalization (path_a) && !RequiresNormalization (path_b);
+ if (ascii)
+ return StrICmp(path_a.c_str(), path_b.c_str()) < 0;
+ else
+ return StrICmp(NormalizeUnicode(path_a, true).c_str(), NormalizeUnicode(path_b, true).c_str()) < 0;
+ #else
+ return StrICmp(path_a.c_str(), path_b.c_str()) < 0;
+ #endif
+ }
+};
+
+struct PathEqualityFunctor : std::binary_function<std::string, std::string, bool>
+{
+ bool operator() ( const std::string& path_a, const std::string& path_b ) const
+ {
+#if UNITY_OSX
+ bool ascii = !RequiresNormalization (path_a) && !RequiresNormalization (path_b);
+ if (ascii)
+ return StrICmp(path_a.c_str(), path_b.c_str()) == 0;
+ else
+ return StrICmp(NormalizeUnicode(path_a, true).c_str(), NormalizeUnicode(path_b, true).c_str()) == 0;
+#else
+ return StrICmp(path_a.c_str(), path_b.c_str()) == 0;
+#endif
+ }
+};
+
+
+#endif
+
+#endif
+
diff --git a/Runtime/Utilities/PlayerPrefs.h b/Runtime/Utilities/PlayerPrefs.h
new file mode 100644
index 0000000..fbb693c
--- /dev/null
+++ b/Runtime/Utilities/PlayerPrefs.h
@@ -0,0 +1,65 @@
+#ifndef PLAYERPREFS_H
+#define PLAYERPREFS_H
+
+#include <string>
+#include <vector>
+
+#if UNITY_EDITOR
+class EditorPrefs {
+public:
+
+ static void UseCleanTestPrefs ();
+ static bool SetInt (const std::string& name, int value);
+ static bool SetBool (const std::string& name, bool value);
+ static bool SetString (const std::string& name, const std::string& value);
+ static bool SetFloat (const std::string& name, float value);
+
+ static int GetInt (const std::string& name, int def = 0);
+ static bool GetBool (const std::string& name, bool def = false);
+ static std::string GetString (const std::string& name, const std::string& def = std::string ());
+ static float GetFloat (const std::string& name, float def = 0.0F);
+
+ static bool HasKey (const std::string& name);
+ static void DeleteKey (const std::string& name);
+ static void DeleteAll ();
+
+ static void Sync ();
+};
+#endif
+
+class PlayerPrefs {
+public:
+ typedef std::vector<UInt8> RawData;
+public:
+ static bool SetInt (const std::string& name, int value);
+ static bool SetString (const std::string& name, const std::string& value);
+ static bool SetFloat (const std::string& name, float value);
+
+ static int GetInt (const std::string& name, int def = 0);
+ static std::string GetString (const std::string& name, const std::string& def = std::string ());
+ static float GetFloat (const std::string& name, float def = 0.0F);
+
+ static bool HasKey (const std::string& name);
+ static void DeleteKey (const std::string& name);
+ static void DeleteAll ();
+
+ #if UNITY_WII_API && (UNITY_WIN || UNITY_WII)
+ static bool GetRawData(RawData& rawData);
+ static bool SetRawData(const RawData& rawData);
+ #endif
+
+ static void Sync ();
+ #if UNITY_METRO
+ static void Init();
+ #elif WEBPLUG
+ static void Init (const std::string& playerURL);
+ #endif
+ #if UNITY_LINUX
+ static std::string GetPath ();
+ #endif
+
+ static void StaticInitialize();
+ static void StaticDestroy();
+};
+
+#endif
diff --git a/Runtime/Utilities/Prefetch.h b/Runtime/Utilities/Prefetch.h
new file mode 100644
index 0000000..d18922f
--- /dev/null
+++ b/Runtime/Utilities/Prefetch.h
@@ -0,0 +1,52 @@
+#ifndef PREFETCH_H
+#define PREFETCH_H
+
+#if UNITY_PS3
+#include <ppu_intrinsics.h>
+#endif
+
+// This assembly will not work for Jungle. TODO: use arm version check here
+#if defined(__arm__) && !UNITY_LINUX && !UNITY_WINRT
+
+inline void Prefetch(const void* p)
+{
+ unsigned char* pCurr = (unsigned char*)p;
+ asm volatile(
+ "pld [%0] \n\t"
+ : "=r" (pCurr)
+ : "0" (pCurr)
+ : "r0");
+}
+
+inline void Prefetch(const void* p, size_t size)
+{
+ unsigned char* pCurr = (unsigned char*)p;
+ unsigned char* pEnd = pCurr + size;
+
+ while (pCurr < pEnd)
+ {
+ asm volatile(
+ "pld [%0] \n\t"
+ : "=r" (pCurr)
+ : "0" (pCurr)
+ : "r0");
+ pCurr += 32;
+ }
+}
+
+#elif defined(_XBOX)
+__forceinline void Prefetch(const void* p, size_t size = 32)
+{
+ __dcbt(0, p);
+}
+#elif UNITY_PS3
+inline void Prefetch(const void* p, size_t size = 32)
+{
+ __dcbt(p);
+}
+#else
+//@TODO: gcc __builtin_prefetch(p); profile & enable
+inline void Prefetch(const void* /*p*/, size_t /*size*/ = 32) { }
+#endif
+
+#endif
diff --git a/Runtime/Utilities/RecursionLimit.h b/Runtime/Utilities/RecursionLimit.h
new file mode 100644
index 0000000..8425335
--- /dev/null
+++ b/Runtime/Utilities/RecursionLimit.h
@@ -0,0 +1,54 @@
+#ifndef RECURSIONLIMIT_H
+#define RECURSIONLIMIT_H
+
+class RecursionLimiter {
+public:
+ RecursionLimiter (unsigned int *count)
+ {
+ m_Count = count;
+ (*m_Count)++;
+ }
+
+ ~RecursionLimiter ()
+ {
+ (*m_Count)--;
+ }
+private:
+ unsigned int *m_Count;
+};
+
+#define LIMIT_RECURSION(x, retval) static unsigned int reclimit = 0; RecursionLimiter limiter(&reclimit); if (reclimit > x) return retval;
+
+
+class ReentrancyChecker
+{
+public:
+ ReentrancyChecker( bool* variable )
+ : m_Variable(variable)
+ {
+ if( *m_Variable == false )
+ {
+ *m_Variable = true;
+ m_OK = true;
+ }
+ else
+ {
+ m_OK = false;
+ }
+ }
+ ~ReentrancyChecker()
+ {
+ if( m_OK )
+ {
+ *m_Variable = false;
+ }
+ }
+ bool IsOK() const { return m_OK; }
+
+private:
+ bool* m_Variable;
+ bool m_OK;
+};
+
+
+#endif
diff --git a/Runtime/Utilities/ReportHardware.cpp b/Runtime/Utilities/ReportHardware.cpp
new file mode 100644
index 0000000..868287f
--- /dev/null
+++ b/Runtime/Utilities/ReportHardware.cpp
@@ -0,0 +1,391 @@
+#include "UnityPrefix.h"
+#include "ReportHardware.h"
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "HashFunctions.h"
+#include "Configuration/UnityConfigureVersion.h"
+#include "Configuration/UnityConfigureOther.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Export/WWW.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/CPUInfo.h"
+#include "Runtime/Misc/PlayerSettings.h"
+#include "Runtime/Utilities/GlobalPreferences.h"
+#include "Runtime/Utilities/Argv.h"
+#include "Runtime/Utilities/Word.h"
+
+#if UNITY_LINUX
+#include "PlatformDependent/Linux/X11Quarantine.h"
+#endif
+
+#if SUPPORT_REPRODUCE_LOG
+#include "Runtime/Misc/ReproductionLog.h"
+#endif
+
+#if UNITY_ANDROID
+#include "PlatformDependent/AndroidPlayer/AndroidSystemInfo.h"
+#include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+#endif
+
+#if UNITY_IPHONE
+ extern "C" const char* UnityAdvertisingIdentifier();
+ extern "C" bool UnityAdvertisingTrackingEnabled();
+#endif
+
+#if UNITY_METRO
+#include "PlatformDependent/MetroPlayer/AppCallbacks.h"
+#include "PlatformDependent/MetroPlayer/MetroCapabilities.h"
+#endif
+
+#if UNITY_OSX
+static int numberForKey( CFDictionaryRef desc, CFStringRef key )
+{
+ CFNumberRef value;
+ int num = 0;
+ if ( (value = (CFNumberRef)CFDictionaryGetValue(desc, key)) == NULL )
+ return 0;
+ CFNumberGetValue(value, kCFNumberIntType, &num);
+ return num;
+}
+#endif
+
+#if UNITY_EDITOR
+const char* kHardwareReportDoneKey = "Editor StatsDone";
+#elif UNITY_STANDALONE
+const char* kHardwareReportDoneKey = "StandaloneStatsDone";
+#else
+const char* kHardwareReportDoneKey = "StatsDone";
+#endif
+
+// URL to report hardware info to
+const char* kHardwareReportURL = "http://stats.unity3d.com/HWStats.cgi";
+
+
+static bool CheckHardwareReportNeeded()
+{
+ #if !UNITY_ENABLE_HWSTATS_REPORT
+ return false;
+ #endif
+
+ #if SUPPORT_REPRODUCE_LOG
+ if (RunningReproduction())
+ return false;
+ #endif
+
+ #if !WEBPLUG
+ if (!IsHumanControllingUs())
+ return false;
+ #endif
+
+ #if UNITY_ANDROID
+ // development player == no stats
+ if (UNITY_DEVELOPER_BUILD)
+ return false;
+
+ if (!GetPlayerSettings().GetEnableHWStatistics())
+ return false;
+
+ // Only report once per OS version
+ std::string osName = systeminfo::GetOperatingSystem();
+ UInt8 md5Value[16];
+ ComputeMD5Hash( reinterpret_cast<const UInt8*>( osName.c_str() ), osName.size(), md5Value );
+ std::string md5String = BytesToHexString( md5Value, 16 );
+
+ if (GetGlobalPreference(UNITY_VERSION_DIGITS) == md5String)
+ return false;
+
+ SetGlobalPreference(UNITY_VERSION_DIGITS, md5String);
+
+ return true;
+ #endif // #if UNITY_ANDROID
+
+
+ #if UNITY_METRO
+ if (metro::Capabilities::IsSupported(metro::Capabilities::kInternetClient, "", false) == false &&
+ metro::Capabilities::IsSupported(metro::Capabilities::kInternetClientServer, "", false) == false)
+ return false;
+ #endif // #if UNITY_METRO
+
+ #if UNITY_IPHONE || UNITY_BB10 || UNITY_TIZEN
+ if (!GetPlayerSettings().GetEnableHWStatistics())
+ return false;
+ #endif
+
+ if ( GetGlobalBoolPreference (kHardwareReportDoneKey, false) )
+ return false;
+ SetGlobalBoolPreference (kHardwareReportDoneKey, true);
+
+ return true;
+}
+
+
+static std::string EscapeFieldString( const std::string& s )
+{
+ const char* kHexToLiteral = "0123456789abcdef";
+ const char *kForbidden = "@&;:<>=?\"'/\\!#%+";
+
+ std::string result;
+ result.reserve( s.size() );
+ for( size_t i = 0; i < s.size(); ++i ) {
+ unsigned char c = s[i];
+ if( c == 32 )
+ result += '+';
+ else if( c < 32 || c > 126 || strchr(kForbidden, c) != NULL )
+ {
+ result += '%';
+ result += kHexToLiteral[c >> 4];
+ result += kHexToLiteral[c & 0xF];
+ }
+ else
+ result += c;
+ }
+ return result;
+}
+
+
+void HardwareInfoReporter::Shutdown()
+{
+ #if ENABLE_WWW
+ if( m_InfoPost != NULL )
+ {
+ m_InfoPost->Release();
+ m_InfoPost = NULL;
+ }
+ #endif
+}
+
+static void StripGfxDriverVersion(std::string& fullName, const std::string& version)
+{
+ size_t pos = fullName.find(version);
+
+ if (pos != std::string::npos && pos > 1)
+ fullName.erase(pos-1, version.size()+1);
+}
+
+void HardwareInfoReporter::ReportHardwareInfo()
+{
+#if ENABLE_WWW
+ AssertIf( m_InfoPost != NULL );
+
+ // If hardware info already reported, do nothing
+ if( !CheckHardwareReportNeeded() )
+ return;
+
+
+ // Get all the information to post
+ std::string infoUUID; // Used to be sent by 2.0.2, not anymore with 2.1. Keeping to match the hash value. Now used by Android and iPhone only.
+ std::string deviceModel; // Used by Android and iPhone only
+ // Report runtime version used
+ std::string infoOS = systeminfo::GetOperatingSystem();
+ std::string infoCPU = systeminfo::GetProcessorType();
+ std::string infoGfxName = gGraphicsCaps.rendererString;
+ std::string infoGfxVendor = gGraphicsCaps.vendorString;
+ std::string infoGfxVersion = gGraphicsCaps.fixedVersionString;
+ std::string infoGfxDriver = gGraphicsCaps.driverLibraryString;
+#if UNITY_ANDROID || UNITY_IPHONE || UNITY_BB10 || UNITY_TIZEN
+ std::string appId = GetPlayerSettings().GetiPhoneBundleIdentifier();
+#else
+ std::string appId = Format("%s.%s",GetPlayerSettings().GetCompanyName().c_str(), GetPlayerSettings().GetProductName().c_str());
+#endif
+ int infoCpuCount = systeminfo::GetProcessorCount();
+ int infoRAM = systeminfo::GetPhysicalMemoryMB();
+ int infoVRAM = gGraphicsCaps.videoMemoryMB;
+
+ std::string unityVersion = UNITY_VERSION; // Report runtime version used
+ std::string buildVersion = "0.0.0"; // Unknown build version used
+
+ // Get desktop display mode dimensions. Do this directly because
+ // we initiate the report before screen manager is even set up
+ // (so that in case something horrible happens while initializing
+ // the game, we'd still get a chance to get the report).
+ #if UNITY_WIN && !UNITY_WINRT
+ DEVMODE displayMode;
+ memset( &displayMode, 0, sizeof(displayMode) );
+ displayMode.dmSize = sizeof(DEVMODE);
+ EnumDisplaySettings( NULL, ENUM_CURRENT_SETTINGS, &displayMode );
+ std::string infoScreenRes = Format("%i x %i", displayMode.dmPelsWidth, displayMode.dmPelsHeight);
+
+ #elif UNITY_OSX
+ CFDictionaryRef currentMode = CGDisplayCurrentMode(kCGDirectMainDisplay);
+ int desktopWidth = numberForKey(currentMode, kCGDisplayWidth);
+ int desktopHeight = numberForKey(currentMode, kCGDisplayHeight);
+ std::string infoScreenRes = Format("%i x %i", desktopWidth, desktopHeight);
+
+ #elif UNITY_LINUX
+ unsigned width = 0;
+ unsigned height = 0;
+ #if SUPPORT_X11
+ NativeDisplayPtr display = OpenDisplay(NULL);
+ NativeWindow rootWindow = GetRootWindow(display);
+ GetWindowSize(display, rootWindow, &width, &height);
+ CloseDisplay(display);
+ #endif
+ std::string infoScreenRes = Format("%u x %u", width, height);
+ #elif UNITY_ANDROID
+ // Create a less obscure OS string
+ infoOS = Format ("%s API-%i", systeminfo::GetDeviceSystemName(), android::systeminfo::ApiLevel());
+ // Strip 'driverLibraryString' from 'fixedVersionString', if duplicated
+ StripGfxDriverVersion(infoGfxVersion, infoGfxDriver);
+ // Get the raw display size (physical size of the display)
+ int width, height;
+ android::systeminfo::DisplaySize(&width, &height);
+ std::string infoScreenRes = Format("%u x %u", width, height);
+ // Use ANDROID ID/IMEI/WiFi MAC as UUID
+ infoUUID = android::systeminfo::UniqueIdentifier();
+ // This matches the first part of ro.build.fingerprint
+ deviceModel = Format ("%s/%s/%s", android::systeminfo::Manufacturer(), android::systeminfo::Model(), android::systeminfo::Device());
+ #elif UNITY_IPHONE
+ std::string infoScreenRes = Format("%u x %u", GetScreenManager().GetWidth(), GetScreenManager().GetHeight());
+ infoUUID = systeminfo::GetDeviceUniqueIdentifier(); // it is hash of WiFi MAC address
+ deviceModel = systeminfo::GetDeviceModel();
+ StripGfxDriverVersion(infoGfxVersion, infoGfxDriver);
+ #elif UNITY_BLACKBERRY
+ std::string infoScreenRes = Format("%u x %u", GetScreenManager().GetWidth(), GetScreenManager().GetHeight());
+ // This will be EMPTY for Blackberry if permission is set false
+ infoUUID = systeminfo::GetDeviceUniqueIdentifier(); // Unique ID from OS / it is hash of WiFi MAC address
+ deviceModel = Format("%s/%s", "BlackBerry", systeminfo::GetDeviceModel());
+ StripGfxDriverVersion(infoGfxVersion, infoGfxDriver);
+ #elif UNITY_METRO
+ std::string infoScreenRes = Format("%u x %u", GetScreenManager().GetWidth(), GetScreenManager().GetHeight());
+ // It seems if we call GetDeviceUniqueIdentifier, this somehow distrubts the data flow, and server doesn't accept it
+ infoUUID = systeminfo::GetDeviceUniqueIdentifier(); // it is hash of WiFi MAC address
+ {
+ // Make infoUUID different one from the Editor and encode it again
+ infoUUID+="Metro";
+ UInt8 md5Value[16];
+ ComputeMD5Hash( reinterpret_cast<const UInt8*>(infoUUID.c_str() ), infoUUID.size(), md5Value);
+ infoUUID = BytesToHexString(md5Value, 16);
+ }
+ deviceModel = "n/a";
+ #elif UNITY_WP8
+ auto& screenManager = GetScreenManager ();
+ std::string infoScreenRes;
+ if (screenManager.GetScreenOrientation () == ScreenOrientation::kLandscapeLeft || screenManager.GetScreenOrientation () == ScreenOrientation::kLandscapeRight)
+ {
+ infoScreenRes = Format ("%u x %u", screenManager.GetHeight (), screenManager.GetWidth ());
+ }
+ else
+ {
+ infoScreenRes = Format ("%u x %u", screenManager.GetWidth (), screenManager.GetHeight ());
+ }
+ infoUUID = systeminfo::GetDeviceUniqueIdentifier ();
+ deviceModel = systeminfo::GetDeviceName ();
+ #elif UNITY_TIZEN
+ std::string infoScreenRes = Format("%u x %u", GetScreenManager().GetWidth(), GetScreenManager().GetHeight());
+ infoUUID = systeminfo::GetDeviceUniqueIdentifier();
+ deviceModel = Format("%s/%s", "Tizen", systeminfo::GetDeviceModel());
+ StripGfxDriverVersion(infoGfxVersion, infoGfxDriver);
+ #else
+ #error "Unsupported platform"
+ #endif
+
+ // Various flags, bits:
+ // 0..3: license type
+ // 4..5: metro presentation types
+ // 6..17: CPU capabilities, reserved up to 23
+ // 24..25: GPU capabilities
+ int flags = 0;
+ BuildSettings* buildSettings = &GetBuildSettings();
+ if (buildSettings)
+ {
+ // bits 0..2: 1=Indie, 2=Pro
+ flags |= buildSettings->hasPROVersion ? 2 : 1;
+ // bit 3: 0=Licensed, 1=Trial
+ if (!buildSettings->hasPublishingRights)
+ flags |= 8;
+
+ // Report build version used
+ buildVersion = buildSettings->GetVersion();
+ }
+
+#if UNITY_METRO
+ // bits 4..5: 1=XAML app, 2=D3D app
+ flags |= UnityPlayer::AppCallbacks::Instance->GetAppType() == UnityPlayer::AppCallbacks::kAppTypeD3DXAML ? (1 << 4) : (1 << 5);
+#endif
+
+ // CPU capabilities flags, bits 6..17
+ flags |= (1<<6); // CPU caps bits are present (to determine between earlier versions that did not send these flags at all)
+ if (CPUInfo::HasSSE2Support())
+ flags |= (1<<7);
+ if (CPUInfo::HasSSE41Support())
+ flags |= (1<<8);
+ if (CPUInfo::HasSSE42Support())
+ flags |= (1<<9);
+ if (CPUInfo::HasAVXSupport())
+ flags |= (1<<10);
+ if (CPUInfo::HasNEONSupport())
+ flags |= (1<<11);
+ if (CPUInfo::HasSSE3Support())
+ flags |= (1<<12);
+ if (CPUInfo::HasSupplementalSSE3Support())
+ flags |= (1<<13);
+ if (CPUInfo::HasAVX2Support())
+ flags |= (1<<14);
+ if (CPUInfo::HasAVX512Support())
+ flags |= (1<<15);
+ if (CPUInfo::HasFP16CSupport())
+ flags |= (1<<16);
+ if (CPUInfo::HasFMASupport())
+ flags |= (1<<17);
+
+ // GPU capabilities flags, bits 24..25
+ if (gGraphicsCaps.hasNativeDepthTexture)
+ flags |= (1<<24);
+ if (gGraphicsCaps.hasNativeShadowMap)
+ flags |= (1<<25);
+
+
+ // Send a hash some fields plus a salt as a cheap way to prevent "spam" on hardware
+ // report script.
+ std::string stringToHash;
+ stringToHash = std::string("KonfiguracijosReportoDruska-") + infoUUID + infoOS + infoCPU + infoGfxName + infoGfxVendor + infoGfxVersion;
+
+ UInt8 md5Value[16];
+ ComputeMD5Hash( reinterpret_cast<const UInt8*>( stringToHash.c_str() ), stringToHash.size(), md5Value );
+ std::string md5String = BytesToHexString( md5Value, 16 );
+
+ // Construct POST request data
+ std::string postData;
+ postData += "os=" + EscapeFieldString(infoOS);
+ postData += "&cpu=" + EscapeFieldString(infoCPU);
+ postData += "&gfxname=" + EscapeFieldString(infoGfxName);
+ postData += "&gfxvendor=" + EscapeFieldString(infoGfxVendor);
+ postData += "&gfxversion=" + EscapeFieldString(infoGfxVersion);
+ postData += "&gfxdriver=" + EscapeFieldString(infoGfxDriver);
+ postData += "&cpucount=" + IntToString(infoCpuCount);
+ postData += "&ram=" + IntToString(infoRAM);
+ postData += "&vram=" + IntToString(infoVRAM);
+ postData += "&screen=" + EscapeFieldString(infoScreenRes);
+ postData += "&platform=" + IntToString(systeminfo::GetRuntimePlatform());
+ postData += "&flags=" + IntToString(flags);
+ postData += "&hash=" + md5String;
+ postData += "&appId=" + EscapeFieldString(appId);
+#if UNITY_ANDROID || UNITY_IPHONE || UNITY_BB10 || UNITY_WINRT || UNITY_TIZEN
+ postData += "&uuid=" + infoUUID;
+ postData += "&model=" + deviceModel;
+#endif
+#if UNITY_IPHONE
+ if (const char *iOSAdvertisingIdentifier = UnityAdvertisingIdentifier())
+ postData += "&adid=" + std::string(iOSAdvertisingIdentifier);
+ postData += "&adtrack=" + std::string(UnityAdvertisingTrackingEnabled()?"1":"0");
+#endif
+ postData += "&unity=" + unityVersion;
+ postData += "&build=" + buildVersion;
+
+ // Initiate POST request
+ std::map<std::string,std::string> headers;
+ headers.insert( std::make_pair("Content-Type", "application/x-www-form-urlencoded") );
+
+ m_InfoPost = WWW::Create( kHardwareReportURL, postData.c_str(), postData.size(), headers, false );
+
+ /*
+ // Server accepts only 5% percent of data, so if you want to debug, use the loop below, to ensure that data reaches server
+ for (int i = 0; i < 100; i++)
+ {
+ m_InfoPost = WWW::Create( kHardwareReportURL, postData.c_str(), postData.size(), headers, false );
+ Thread::Sleep(0.1f);
+ }
+ */
+#endif // ENABLE_WWW
+}
diff --git a/Runtime/Utilities/ReportHardware.h b/Runtime/Utilities/ReportHardware.h
new file mode 100644
index 0000000..2943869
--- /dev/null
+++ b/Runtime/Utilities/ReportHardware.h
@@ -0,0 +1,19 @@
+#ifndef __REPORT_HARDWARE_H
+#define __REPORT_HARDWARE_H
+
+class WWW;
+
+
+class HardwareInfoReporter {
+public:
+ HardwareInfoReporter() : m_InfoPost(NULL) { };
+
+ void ReportHardwareInfo();
+ void Shutdown();
+
+private:
+ WWW* m_InfoPost;
+};
+
+
+#endif
diff --git a/Runtime/Utilities/SpatialHash.cpp b/Runtime/Utilities/SpatialHash.cpp
new file mode 100644
index 0000000..4865bd4
--- /dev/null
+++ b/Runtime/Utilities/SpatialHash.cpp
@@ -0,0 +1,90 @@
+#include "UnityPrefix.h"
+#include "SpatialHash.h"
+#include "External/MurmurHash/MurmurHash2.h"
+#include "Runtime/Geometry/AABB.h"
+#include <float.h>
+
+
+// quantisation of direction
+inline unsigned QuantiseDirection(const Vector3f& dir)
+{
+ // quantise direction
+ unsigned id;
+ if (fabsf(dir.x) >= fabsf(dir.y) && fabsf(dir.x) >= fabsf(dir.z))
+ id = dir.x > 0 ? 0 : 1;
+ else if (fabsf(dir.y) >= fabsf(dir.z))
+ id = dir.y > 0 ? 2 : 3;
+ else
+ id = dir.z > 0 ? 4 : 5;
+ return id;
+}
+
+// quantisation of position
+inline void QuantisePosition(const Vector3f& pos, float voxelSize, int* xyz)
+{
+ // quantise position
+ const float cell_side = voxelSize;
+ const float cell_height = voxelSize * kVoxelHeightMultiplier;
+ xyz[0] = (int)std::floor(pos.x / cell_side);
+ xyz[1] = (int)std::floor(pos.y / cell_height);
+ xyz[2] = (int)std::floor(pos.z / cell_side);
+}
+
+inline AABB QuantisedValueToAABB(int* quantisedValue, float voxelSize)
+{
+ const float cell_side = voxelSize;
+ const float cell_height = voxelSize * kVoxelHeightMultiplier;
+ const Vector3f pos(quantisedValue[0]*cell_side,quantisedValue[1]*cell_height,quantisedValue[2]*cell_side);
+ const Vector3f halfDir( cell_side*.5f, cell_height*.5f, cell_side*.5f );
+ return AABB(pos + halfDir, Abs(halfDir));
+}
+
+// compute hash index for directions cache
+inline UInt64 ComputeHash(const Vector3f& pos, const Vector3f& dir, float voxelSize )
+{
+ int qp[3];
+ QuantisePosition(pos, voxelSize, qp);
+ UInt64 key = MurmurHash64A( (void*)qp, sizeof(int)*3, 0xdeadbeef186726feULL );
+ // quantise direction
+ unsigned id = QuantiseDirection(dir);
+ key = key ^ id;
+ return key;
+}
+
+/////////////////////////////////////////////////////
+// Collision plane cache members
+PlaneColliderCache_dense_hashmap::PlaneColliderCache_dense_hashmap()
+{
+ m_PlaneHashMap.set_empty_key (ComputeHash(Vector3f(FLT_MAX,FLT_MAX,FLT_MAX),Vector3f(FLT_MAX,FLT_MAX,FLT_MAX),1.f));
+ m_PlaneHashMap.set_deleted_key (ComputeHash(Vector3f(FLT_MIN,FLT_MIN,FLT_MIN),Vector3f(FLT_MIN,FLT_MIN,FLT_MIN),1.f));
+}
+
+bool PlaneColliderCache_dense_hashmap::Replace (const Vector3f& pos, const Vector3f& dir, const Plane& plane, int colliderInstanceID, int rigidBodyOrColliderInstanceID, float voxelSize)
+{
+ UInt64 hash = ComputeHash (pos, dir, voxelSize);
+ int sz = Size();
+ m_PlaneHashMap.insert (std::make_pair (hash, PlaneData (plane, colliderInstanceID, rigidBodyOrColliderInstanceID)));
+ if ( sz >= Size() )
+ {
+ PlaneHashMap::iterator it = m_PlaneHashMap.find (hash);
+ if (it == m_PlaneHashMap.end ())
+ return false;
+ it->second.m_Plane = plane;
+ it->second.m_ColliderInstanceID = colliderInstanceID;
+ it->second.m_RigidBodyOrColliderInstanceID = rigidBodyOrColliderInstanceID;
+ }
+ return true;
+}
+
+bool PlaneColliderCache_dense_hashmap::Find (const Vector3f& pos, const Vector3f& dir, Plane& plane, int& colliderInstanceID, int& rigidBodyOrColliderInstanceID, float voxelSize) const
+{
+ UInt64 hash = ComputeHash (pos, dir, voxelSize);
+ PlaneHashMap::const_iterator it = m_PlaneHashMap.find (hash);
+ if (it == m_PlaneHashMap.end ())
+ return false;
+ plane = it->second.m_Plane;
+ colliderInstanceID = it->second.m_ColliderInstanceID;
+ rigidBodyOrColliderInstanceID = it->second.m_RigidBodyOrColliderInstanceID;
+ return true;
+}
+
diff --git a/Runtime/Utilities/SpatialHash.h b/Runtime/Utilities/SpatialHash.h
new file mode 100644
index 0000000..74dd01e
--- /dev/null
+++ b/Runtime/Utilities/SpatialHash.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/dense_hash_map.h"
+#include <algorithm>
+
+static const float kVoxelHeightMultiplier = 4.f;
+
+struct PlaneData
+{
+ PlaneData () {}
+ PlaneData (const Plane& plane, int colliderInstanceID, int rigidBodyOrColliderInstanceID)
+ : m_Plane (plane)
+ , m_ColliderInstanceID (colliderInstanceID)
+ , m_RigidBodyOrColliderInstanceID (rigidBodyOrColliderInstanceID)
+ {}
+ Plane m_Plane;
+ int m_ColliderInstanceID;
+ int m_RigidBodyOrColliderInstanceID;
+};
+
+// Collision plane cache using the google dense hashmap
+class PlaneColliderCache_dense_hashmap
+{
+public:
+ PlaneColliderCache_dense_hashmap ();
+ ~PlaneColliderCache_dense_hashmap () {Clear();}
+ int Size () const { return m_PlaneHashMap.size(); };
+ bool Find (const Vector3f& pos, const Vector3f& dir, Plane& plane, int& colliderInstanceID, int& rigidBodyOrColliderInstanceID, float voxelSize) const;
+ bool Replace (const Vector3f& pos, const Vector3f& dir, const Plane& plane, int colliderInstanceID, int rigidBodyOrColliderInstanceID, float voxelSize);
+ void Clear ()
+ {
+ m_PlaneHashMap.clear ();
+ }
+private:
+ // Hash map for cached planes
+ struct UInt64HashFunctor
+ {
+ inline size_t operator ()(UInt64 x) const
+ {
+ return int(x >> 32);
+ }
+ };
+ typedef std::pair<const UInt64, PlaneData> KeyToPlanePair;
+ typedef dense_hash_map<UInt64, PlaneData, UInt64HashFunctor, std::equal_to<UInt64>, STL_ALLOCATOR (kMemSTL, KeyToPlanePair) > PlaneHashMap;
+ PlaneHashMap m_PlaneHashMap;
+};
+
+typedef PlaneColliderCache_dense_hashmap PlaneColliderCache;
diff --git a/Runtime/Utilities/Stacktrace.cpp b/Runtime/Utilities/Stacktrace.cpp
new file mode 100644
index 0000000..e87b53a
--- /dev/null
+++ b/Runtime/Utilities/Stacktrace.cpp
@@ -0,0 +1,246 @@
+#include "UnityPrefix.h"
+
+#define HAS_POSIX_BACKTRACE UNITY_OSX || UNITY_ANDROID
+#if HAS_POSIX_BACKTRACE && UNITY_OSX
+ #define POSIX_BACKTRACE_DECL extern "C"
+#else
+ #define POSIX_BACKTRACE_DECL
+#endif
+
+#if UNITY_WIN
+#include <winnt.h>
+#include "PlatformDependent/Win/StackWalker.h"
+typedef USHORT (WINAPI *CaptureStackBackTraceType)(__in ULONG, __in ULONG, __out PVOID*, __out_opt PULONG);
+CaptureStackBackTraceType backtrace = NULL;
+// Then use 'backtrace' as if it were CaptureStackBackTrace
+
+class MyStackWalker: public StackWalker
+{
+protected:
+ virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName) {}
+ virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion) {}
+ virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)
+ {
+ if(*entry.undFullName != '\0')
+ OnOutput(entry.undFullName);
+ else if(*entry.undName != '\0')
+ OnOutput(entry.undName);
+ else if(*entry.name != '\0')
+ OnOutput(entry.name);
+ OnOutput("\n");
+ }
+ virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) {}
+};
+
+class StringStackWalker: public MyStackWalker
+{
+public:
+ StringStackWalker(): result(NULL) { }
+
+ void SetOutputString(std::string &_result) {result = &_result;}
+protected:
+ std::string *result;
+
+ virtual void OnOutput(LPCSTR szTest)
+ {
+ if(result)
+ *result += szTest;
+ }
+};
+
+class BufferStackWalker: public MyStackWalker
+{
+public:
+ BufferStackWalker(): maxSize(0) { }
+
+ void SetOutputBuffer(char *_buffer, int _maxSize)
+ {
+ buffer = _buffer;
+ maxSize = _maxSize;
+ }
+protected:
+ char *buffer;
+ int maxSize;
+
+ virtual void OnOutput(LPCSTR szTest)
+ {
+ while(maxSize > 1 && *szTest != '\0')
+ {
+ *(buffer++) = *(szTest++);
+ maxSize--;
+ }
+ *buffer = '\0';
+ }
+};
+#elif HAS_POSIX_BACKTRACE
+POSIX_BACKTRACE_DECL int backtrace(void** buffer, int size);
+POSIX_BACKTRACE_DECL char** backtrace_symbols(void* const* buffer, int size);
+#include "Runtime/Mono/MonoIncludes.h"
+ #if UNITY_ANDROID
+ #include <corkscrew/backtrace.h>
+ #include "Runtime/Mono/MonoIncludes.h"
+ #endif
+#endif
+
+#if UNITY_WIN
+StringStackWalker* g_StringStackWalker = NULL;
+BufferStackWalker* g_BufferStackWalker = NULL;
+#endif
+
+void InitializeStackWalker(){
+#if UNITY_WIN
+ if (g_BufferStackWalker == NULL){
+ g_BufferStackWalker = new BufferStackWalker();
+ }
+ if (g_StringStackWalker == NULL){
+ g_StringStackWalker = new StringStackWalker();
+ }
+#endif
+}
+
+std::string GetStacktrace(bool includeMonoStackFrames)
+{
+ std::string trace;
+#if UNITY_WIN
+ InitializeStackWalker();
+ g_StringStackWalker->SetOutputString(trace);
+ if (includeMonoStackFrames)
+ g_StringStackWalker->ShowCallstackSimple();
+ else
+ g_StringStackWalker->ShowCallstack();
+#elif HAS_POSIX_BACKTRACE
+ void* backtraceFrames[128];
+ int frameCount = backtrace(&backtraceFrames[0], 128);
+ char** frameStrings = backtrace_symbols(&backtraceFrames[0], frameCount);
+
+ if(frameStrings != NULL)
+ {
+ for(int x = 0; x < frameCount; x++)
+ {
+ if(frameStrings[x] != NULL)
+ {
+ std::string str = frameStrings[x];
+ if(str.find("???") != std::string::npos)
+ {
+ // If the symbol could not be resolved, try if mono knows about the address.
+ unsigned int addr = 0;
+ if (sscanf(str.substr(str.find("0x")).c_str(), "0x%x", &addr))
+ {
+ if(addr == -1){
+ break;
+ }
+ const char *mono_frame = mono_pmip((void*)addr);
+ // If mono knows the address, replace the stack trace line with
+ // the information from mono.
+ if (mono_frame)
+ str = str.substr(0,4) + "[Mono JITed code] " + mono_frame;
+ }
+ }
+ trace = trace + str + '\n';
+ }
+ }
+ free(frameStrings);
+ }
+#else
+ trace = "Stacktrace is not supported on this platform.";
+#endif
+ return trace;
+}
+
+UInt32 GetStacktrace(void** trace, int maxFrames, int startframe)
+{
+#if UNITY_WIN
+ if(!backtrace)
+ backtrace = (CaptureStackBackTraceType)(GetProcAddress(LoadLibrary("kernel32.dll"), "RtlCaptureStackBackTrace"));
+ DWORD dwHashCode = 0;
+ int count = backtrace(startframe, maxFrames-1, trace, &dwHashCode);
+ trace[count] = 0;
+ return dwHashCode;
+// InitializeStackWalker();
+// g_BufferStackWalker->GetCurrentCallstack(trace,maxFrames);
+#elif HAS_POSIX_BACKTRACE
+ int framecount = backtrace(trace, maxFrames);
+ trace[framecount] = 0;
+ int index = startframe;
+ UInt32 hash = 0;
+ while(trace[index])
+ {
+ trace[index-startframe] = trace[index];
+ hash ^= (UInt32)(trace[index]) ^ (hash << 7) ^ (hash >> 21);
+ index++;
+ }
+ trace[index-startframe] = 0;
+ return hash;
+#endif
+ return 0;
+}
+
+void GetReadableStackTrace(char* output, int bufsize, void** input, int frameCount)
+{
+#if UNITY_WIN
+ InitializeStackWalker();
+ g_BufferStackWalker->GetStringFromStacktrace(output, bufsize, input);
+#elif HAS_POSIX_BACKTRACE
+ char **frameStrings = backtrace_symbols(input, frameCount);
+ std::string trace;
+ if(frameStrings != NULL)
+ {
+ for(int x = 0; x < frameCount; x++)
+ {
+ if(frameStrings[x] != NULL)
+ {
+ #if UNITY_ANDROID
+ char line[256] = {0};
+ snprintf(line, 255, " #%02d %s\n", x, frameStrings[x]);
+ trace = trace + line;
+ #else
+ trace = trace + frameStrings[x] + '\n';
+ #endif
+ }
+ }
+ free(frameStrings);
+ }
+ strcpy(output,trace.c_str());
+#endif
+
+}
+
+void GetStacktrace(char *trace, int maxSize, int maxFrames)
+{
+#if UNITY_WIN
+ InitializeStackWalker();
+ g_BufferStackWalker->SetOutputBuffer(trace, maxSize);
+ g_BufferStackWalker->ShowCallstack(
+ GetCurrentThread(),
+ NULL,
+ NULL,
+ NULL,
+ maxFrames);
+#elif HAS_POSIX_BACKTRACE
+ void *backtraceFrames[128];
+ int frameCount = backtrace(&backtraceFrames[0], 128);
+ char **frameStrings = backtrace_symbols(&backtraceFrames[0], frameCount);
+ frameCount = std::min(frameCount, maxFrames);
+ if(frameStrings != NULL)
+ {
+ for(int x = 0; x < frameCount; x++)
+ {
+ if(frameStrings[x] != NULL)
+ {
+ #if UNITY_ANDROID
+ int numChars = snprintf(trace, maxSize, " #%02d %s\n", x, frameStrings[x]);
+ #else
+ int numChars = snprintf(trace, maxSize, "%s\n", frameStrings[x]);
+ #endif
+ trace += numChars;
+ maxSize -= numChars;
+ }
+ if(maxSize <= 0)
+ break;
+ }
+ free(frameStrings);
+ }
+#else
+ strncpy(trace, "Stacktrace is not supported on this platform.", maxSize);
+#endif
+}
diff --git a/Runtime/Utilities/Stacktrace.h b/Runtime/Utilities/Stacktrace.h
new file mode 100644
index 0000000..7344e91
--- /dev/null
+++ b/Runtime/Utilities/Stacktrace.h
@@ -0,0 +1,12 @@
+#ifndef STACKTRACE_H
+#define STACKTRACE_H
+
+void InitializeStackWalker();
+
+std::string GetStacktrace(bool includeMonoStackFrames = false);
+
+//can't use string version when debugging new/delete allocations, as string class may allocate memory itself!
+void GetStacktrace(char *trace, int maxSize, int maxFrames = INT_MAX);
+UInt32 GetStacktrace(void** trace, int maxFrames, int startframe);
+void GetReadableStackTrace(char* output, int bufsize, void** input, int count);
+#endif
diff --git a/Runtime/Utilities/StaticAssert.h b/Runtime/Utilities/StaticAssert.h
new file mode 100644
index 0000000..874b526
--- /dev/null
+++ b/Runtime/Utilities/StaticAssert.h
@@ -0,0 +1,55 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Utilities/TypeUtilities.h"
+
+#ifndef SUPPORT_CPP0X_STATIC_ASSERT
+ #if (defined _MSC_VER) && (_MSC_VER >= 1600)
+ #define SUPPORT_CPP0X_STATIC_ASSERT 1
+ #elif (defined __has_extension)
+ #define SUPPORT_CPP0X_STATIC_ASSERT __has_extension(cxx_static_assert)
+ #else
+ #define SUPPORT_CPP0X_STATIC_ASSERT 0
+ #endif
+#endif
+
+#if SUPPORT_CPP0X_STATIC_ASSERT
+ #define CompileTimeAssert(expression, message) static_assert(expression, message)
+#else
+ template<bool> struct CompileTimeAssertImpl;
+ template<> struct CompileTimeAssertImpl<true> {};
+
+ #define CT_ASSERT_HACK_JOIN(X,Y) CT_ASSERT_HACK_DO_JOIN(X,Y)
+ #define CT_ASSERT_HACK_DO_JOIN(X,Y) CT_ASSERT_HACK_DO_JOIN2(X,Y)
+ #define CT_ASSERT_HACK_DO_JOIN2(X,Y) X##Y
+
+ #define CompileTimeAssert(expression, message) \
+ enum{ CT_ASSERT_HACK_JOIN(ct_assert_, __LINE__) = sizeof(CompileTimeAssertImpl<(expression)>) }
+#endif
+
+
+#ifndef SUPPORT_CPP0X_DECLTYPE
+ #if (defined _MSC_VER) && (_MSC_VER >= 1600)
+ #define SUPPORT_CPP0X_DECLTYPE 1
+ #elif (defined __has_extension)
+ #define SUPPORT_CPP0X_DECLTYPE __has_extension(cxx_decltype)
+ #else
+ #define SUPPORT_CPP0X_DECLTYPE 0
+ #endif
+#endif
+
+
+#if SUPPORT_CPP0X_STATIC_ASSERT && SUPPORT_CPP0X_DECLTYPE
+ /// Usage Example:
+ /// int a;
+ /// double b;
+ /// AssertVariableType(a, int); // OK
+ /// AssertVariableType(b, int); // Error during compile time
+ /// AssertIfVariableType(a, int); // Error during compile time
+ /// AssertIfVariableType(b, int); // OK
+
+ #define AssertVariableType(Variable, Type) CompileTimeAssert(IsSameType<Type,decltype(Variable)>::result, #Variable" is not of type "#Type)
+ #define AssertIfVariableType(Variable, Type) CompileTimeAssert(!IsSameType<Type,decltype(Variable)>::result, #Variable" is of type "#Type)
+#else
+ #define AssertVariableType(Variable, Type) do { (void)sizeof(Variable); } while(0)
+ #define AssertIfVariableType(Variable, Type) do { (void)sizeof(Variable); } while(0)
+#endif
diff --git a/Runtime/Utilities/StrideIterator.h b/Runtime/Utilities/StrideIterator.h
new file mode 100644
index 0000000..c36877e
--- /dev/null
+++ b/Runtime/Utilities/StrideIterator.h
@@ -0,0 +1,89 @@
+#ifndef STRIDE_ITERATOR_H_
+#define STRIDE_ITERATOR_H_
+
+#include <iterator>
+
+template<class T>
+class StrideIterator : public std::iterator<std::random_access_iterator_tag, T>
+{
+public:
+ //@TODO: Get rid of all stl usage and remove this crap.
+ typedef std::iterator<std::random_access_iterator_tag, T> base_iterator;
+ typedef typename base_iterator::value_type value_type;
+ typedef typename base_iterator::difference_type difference_type;
+ typedef typename base_iterator::pointer pointer;
+ typedef typename base_iterator::reference reference;
+ typedef typename base_iterator::iterator_category iterator_category;
+
+ StrideIterator () : m_Pointer(NULL), m_Stride(1) {}
+ StrideIterator (void* p, int stride) : m_Pointer (reinterpret_cast<UInt8*> (p)), m_Stride (stride) {}
+ StrideIterator (StrideIterator const& arg) : m_Pointer(arg.m_Pointer), m_Stride(arg.m_Stride) {}
+
+ void operator = (StrideIterator const& arg) { m_Pointer = arg.m_Pointer; m_Stride = arg.m_Stride; }
+
+ bool operator == (StrideIterator const& arg) const { return m_Pointer == arg.m_Pointer; }
+ bool operator != (StrideIterator const& arg) const { return m_Pointer != arg.m_Pointer; }
+
+ bool operator < (StrideIterator const& arg) const { return m_Pointer < arg.m_Pointer; }
+
+ void operator++() { m_Pointer += m_Stride; }
+ void operator++(int) { m_Pointer += m_Stride; }
+
+ StrideIterator operator + (difference_type n) const { return StrideIterator (m_Pointer + m_Stride * n, m_Stride); }
+ void operator += (difference_type n) { m_Pointer += m_Stride * n; }
+
+ difference_type operator-(StrideIterator const& it) const
+ {
+ Assert (m_Stride == it.m_Stride && "Iterators stride must be equal");
+ Assert (m_Stride != 0 && "Stide must not be zero");
+ return ((uintptr_t)m_Pointer - (uintptr_t)it.m_Pointer) / m_Stride;
+ }
+
+ T& operator[](size_t index) { return *reinterpret_cast<T*> (m_Pointer + m_Stride * index); }
+ const T& operator[](size_t index) const { return *reinterpret_cast<const T*> (m_Pointer + m_Stride * index); }
+
+ T& operator*() { return *reinterpret_cast<T*> (m_Pointer); }
+ const T& operator*() const { return *reinterpret_cast<const T*> (m_Pointer); }
+
+ T* operator->() { return reinterpret_cast<T*> (m_Pointer); }
+ const T* operator->() const { return reinterpret_cast<const T*> (m_Pointer); }
+
+ // Iterator is NULL if not valid
+ bool IsNull () const { return m_Pointer == 0; }
+ void* GetPointer() const { return m_Pointer; }
+ int GetStride() const { return m_Stride; }
+
+private:
+ UInt8* m_Pointer;
+ int m_Stride;
+};
+
+template<class T>
+void strided_copy (const T* src, const T* srcEnd, StrideIterator<T> dst)
+{
+ for (; src != srcEnd ; src++, ++dst)
+ *dst = *src;
+}
+
+template<class T>
+void strided_copy (StrideIterator<T> src, StrideIterator<T> srcEnd, StrideIterator<T> dst)
+{
+ for (; src != srcEnd ; ++src, ++dst)
+ *dst = *src;
+}
+
+template<class T>
+void strided_copy (StrideIterator<T> src, StrideIterator<T> srcEnd, T* dst)
+{
+ for (; src != srcEnd ; ++src, ++dst)
+ *dst = *src;
+}
+
+template<class T, class T2>
+void strided_copy_convert (const T* src, const T* srcEnd, StrideIterator<T2> dst)
+{
+ for (; src != srcEnd ; ++src, ++dst)
+ *dst = *src;
+}
+
+#endif // STRIDE_ITERATOR_H_
diff --git a/Runtime/Utilities/TypeUtilities.h b/Runtime/Utilities/TypeUtilities.h
new file mode 100644
index 0000000..3455701
--- /dev/null
+++ b/Runtime/Utilities/TypeUtilities.h
@@ -0,0 +1,20 @@
+// Utility functions and types for working with static types.
+#pragma once
+
+template<typename T1, typename T2>
+struct IsSameType
+{
+ static const bool result = false;
+};
+
+template<typename T>
+struct IsSameType<T, T>
+{
+ static const bool result = true;
+};
+
+template<typename Expected, typename Actual>
+inline bool IsOfType (Actual& value)
+{
+ return IsSameType<Actual, Expected>::result;
+}
diff --git a/Runtime/Utilities/URLUtility.h b/Runtime/Utilities/URLUtility.h
new file mode 100644
index 0000000..39cbadd
--- /dev/null
+++ b/Runtime/Utilities/URLUtility.h
@@ -0,0 +1,8 @@
+#ifndef URLUTILITY_H
+#define URLUTILITY_H
+
+#include <string>
+
+void OpenURL (const std::string& url);
+
+#endif
diff --git a/Runtime/Utilities/UniqueIDGenerator.cpp b/Runtime/Utilities/UniqueIDGenerator.cpp
new file mode 100644
index 0000000..5649997
--- /dev/null
+++ b/Runtime/Utilities/UniqueIDGenerator.cpp
@@ -0,0 +1,34 @@
+#include "UnityPrefix.h"
+#include "UniqueIDGenerator.h"
+
+UniqueIDGenerator::UniqueIDGenerator()
+{
+ m_IDs.clear();
+ m_IDs.push_back(2); // Generated ID sequence should not contain 0
+ m_free = 1;
+}
+
+unsigned int UniqueIDGenerator::AllocateID ()
+{
+ DebugAssert( m_free <= m_IDs.size() );
+
+ if (m_free == m_IDs.size())
+ {
+ m_IDs.push_back(m_free+1);
+ }
+
+ unsigned int result = m_free;
+ m_free = m_IDs[m_free];
+
+ DebugAssert(result != 0);
+
+ return result;
+}
+
+void UniqueIDGenerator::RemoveID (unsigned int _ID)
+{
+ DebugAssert( (_ID > 0) && (_ID < m_IDs.size()) );
+
+ m_IDs[_ID] = m_free;
+ m_free = _ID;
+}
diff --git a/Runtime/Utilities/UniqueIDGenerator.h b/Runtime/Utilities/UniqueIDGenerator.h
new file mode 100644
index 0000000..b5f7386
--- /dev/null
+++ b/Runtime/Utilities/UniqueIDGenerator.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "UnityPrefix.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+
+// Class to generate small unique IDs with allocate and remove functions.
+class UniqueIDGenerator
+{
+public:
+ UniqueIDGenerator();
+ unsigned int AllocateID();
+ void RemoveID( unsigned int _ID);
+
+private:
+ dynamic_array<unsigned int> m_IDs;
+ unsigned int m_free;
+};
diff --git a/Runtime/Utilities/UnityString.h b/Runtime/Utilities/UnityString.h
new file mode 100644
index 0000000..574f42d
--- /dev/null
+++ b/Runtime/Utilities/UnityString.h
@@ -0,0 +1,10 @@
+#ifndef UNITY_STRING_H
+#define UNITY_STRING_H
+
+#if !UNITY_EXTERNAL_TOOL
+#if UNITY_WIN
+#include "Runtime/Allocator/MemoryMacros.h"
+#endif
+#endif
+
+#endif
diff --git a/Runtime/Utilities/UserAuthorizationManager.cpp b/Runtime/Utilities/UserAuthorizationManager.cpp
new file mode 100644
index 0000000..5513cb6
--- /dev/null
+++ b/Runtime/Utilities/UserAuthorizationManager.cpp
@@ -0,0 +1,118 @@
+#include "UnityPrefix.h"
+#include "UserAuthorizationManager.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+
+#if WEBPLUG
+#include "Runtime/Misc/PlayerSettings.h"
+#include "Runtime/Utilities/GlobalPreferences.h"
+#include "PlatformDependent/CommonWebPlugin/Verification.h"
+#endif
+
+#if UNITY_EDITOR
+#include "Editor/Src/EditorUserBuildSettings.h"
+#include "Editor/Src/BuildPipeline/BuildTargetPlatformSpecific.h"
+#endif
+
+UserAuthorizationManager gUserAuthorizationManager;
+
+UserAuthorizationManager& GetUserAuthorizationManager()
+{
+ return gUserAuthorizationManager;
+}
+
+UserAuthorizationManager::UserAuthorizationManager()
+{
+ m_AuthorizationOperation = NULL;
+ Reset ();
+}
+
+#if WEBPLUG && !UNITY_PEPPER
+std::string GetUserAuthorizationModeKey ()
+{
+ static const char *kUserAuthorizationModeKey = "UserAuthorizationMode";
+ std::string domain = GetStrippedPlayerDomain ();
+ std::string hash = GenerateHash ((unsigned char*)&domain[0], domain.size());
+ return kUserAuthorizationModeKey + hash;
+}
+#endif
+
+void UserAuthorizationManager::Reset()
+{
+#if WEBPLUG && !UNITY_PEPPER
+ m_AuthorizationMode = kNone;
+ if (GetPlayerSettingsPtr())
+ {
+ std::string auth = GetGlobalPreference(GetUserAuthorizationModeKey().c_str());
+ sscanf(auth.c_str(), "%x", &m_AuthorizationMode);
+ }
+#else
+#if UNITY_EDITOR
+ if (GetEditorUserBuildSettingsPtr() != NULL && GetBuildTargetGroup(GetEditorUserBuildSettings().GetActiveBuildTarget()) == kPlatformWebPlayer)
+ m_AuthorizationMode = kNone;
+ else
+#endif
+ // Allow Webcam and Microphone on all others targets than webplayer.
+ m_AuthorizationMode = kWebCam | kMicrophone;
+#endif
+ if (m_AuthorizationOperation)
+ {
+ m_AuthorizationOperation->Release();
+ m_AuthorizationOperation = NULL;
+ }
+ m_AuthorizationRequest = kNone;
+}
+
+AsyncOperation *UserAuthorizationManager::RequestUserAuthorization(int mode)
+{
+ if (m_AuthorizationOperation != NULL)
+ {
+ ErrorString ("A RequestUserAuthorization is already pending.");
+ }
+ else if (!HasUserAuthorization (mode))
+ {
+ m_AuthorizationRequest = mode;
+ m_AuthorizationOperation = new UserAuthorizationManagerOperation ();
+ m_AuthorizationOperation->Retain();
+ return m_AuthorizationOperation;
+ }
+
+ return new UserAuthorizationManagerErrorOperation ();
+}
+
+void UserAuthorizationManager::ReplyToUserAuthorizationRequest(bool reply, bool remember)
+{
+ if (reply)
+ {
+ m_AuthorizationMode |= m_AuthorizationRequest;
+#if WEBPLUG && !UNITY_PEPPER
+ if (remember)
+ SetGlobalPreference(GetUserAuthorizationModeKey().c_str(), Format("%x", m_AuthorizationMode).c_str());
+#endif
+ }
+ m_AuthorizationRequest = kNone;
+ if (m_AuthorizationOperation)
+ {
+ m_AuthorizationOperation->InvokeCoroutine();
+ m_AuthorizationOperation->Release();
+ m_AuthorizationOperation = NULL;
+ }
+}
+
+MonoBehaviour *UserAuthorizationManager::GetAuthorizationDialog ()
+{
+ if (m_AuthorizationRequest != kNone)
+ {
+ if (!m_AuthorizationDialog.IsValid())
+ {
+ m_AuthorizationDialog = &CreateGameObject ("", "Transform", "UserAuthorizationDialog", NULL);
+ m_AuthorizationDialog->SetHideFlags (Object::kHideInHierarchy);
+ }
+ return m_AuthorizationDialog->QueryComponent (MonoBehaviour);
+ }
+ else
+ {
+ if (m_AuthorizationDialog.IsValid ())
+ DestroyObjectHighLevel (m_AuthorizationDialog, true);
+ return NULL;
+ }
+}
diff --git a/Runtime/Utilities/UserAuthorizationManager.h b/Runtime/Utilities/UserAuthorizationManager.h
new file mode 100644
index 0000000..386a22c
--- /dev/null
+++ b/Runtime/Utilities/UserAuthorizationManager.h
@@ -0,0 +1,47 @@
+#ifndef USERAUTHORIZATIONMANAGER_H
+#define USERAUTHORIZATIONMANAGER_H
+
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Misc/AsyncOperation.h"
+
+class UserAuthorizationManager;
+
+UserAuthorizationManager &GetUserAuthorizationManager();
+
+class UserAuthorizationManager {
+public:
+ UserAuthorizationManager ();
+
+ enum Mode {
+ kNone = 0,
+ kWebCam = 1 << 0,
+ kMicrophone = 1 << 1
+ };
+
+ void Reset ();
+ AsyncOperation *RequestUserAuthorization (int mode);
+ void ReplyToUserAuthorizationRequest (bool reply, bool remember = false);
+ bool HasUserAuthorization (int mode) const { return (m_AuthorizationMode & mode) == mode; }
+
+ int GetAuthorizationRequest() const { return m_AuthorizationRequest; }
+
+ MonoBehaviour *GetAuthorizationDialog ();
+private:
+ class UserAuthorizationManagerOperation : public AsyncOperation
+ {
+ virtual float GetProgress () { return 0.0f; }
+ virtual bool IsDone () { return GetUserAuthorizationManager().m_AuthorizationRequest == 0; }
+ };
+
+ class UserAuthorizationManagerErrorOperation : public AsyncOperation
+ {
+ virtual float GetProgress () { return 0.0f; }
+ virtual bool IsDone () { return true; }
+ };
+
+ int m_AuthorizationMode;
+ int m_AuthorizationRequest;
+ UserAuthorizationManagerOperation *m_AuthorizationOperation;
+ PPtr<GameObject> m_AuthorizationDialog;
+};
+#endif \ No newline at end of file
diff --git a/Runtime/Utilities/Utility.h b/Runtime/Utilities/Utility.h
new file mode 100644
index 0000000..b24fcba
--- /dev/null
+++ b/Runtime/Utilities/Utility.h
@@ -0,0 +1,214 @@
+#ifndef UTILITY_H
+#define UTILITY_H
+
+#include "Runtime/Allocator/MemoryMacros.h"
+
+#define UNUSED(x) (void)sizeof(x)
+
+#define SAFE_RELEASE(obj) if (obj) { (obj)->Release(); (obj) = NULL; } else {}
+#define SAFE_RELEASE_LABEL(obj,label) if (obj) { (obj)->Release(label); (obj) = NULL; } else {}
+
+template <class T0, class T1>
+inline void CopyData (T0 *dst, const T1 *src, long int inHowmany)
+{
+ for (long int i=0;i<inHowmany;i++)
+ {
+ dst[i] = src[i];
+ }
+}
+
+template <class DataType>
+inline void CopyOverlappingData (DataType *dst, const DataType *src, long int inHowmany)
+{
+ if (dst < src)
+ {
+ for (long int i=0;i<inHowmany;i++)
+ {
+ dst[i] = src[i];
+ }
+ }
+ else if (dst > src)
+ {
+ for (long int i=inHowmany-1;i>=0;i--)
+ {
+ dst[i] = src[i];
+ }
+ }
+}
+
+template <class DataType>
+inline void CopyRange (DataType *dst, const DataType *src, long int inStart, long int inHowmany)
+{
+ for (long int i=inStart;i<inHowmany+inStart;i++)
+ {
+ dst[i] = src[i];
+ }
+}
+
+template <class DataType>
+inline void CopyData (DataType *dst, const DataType src, long int inHowmany)
+{
+ for (long int i=0;i<inHowmany;i++)
+ {
+ dst[i] = src;
+ }
+}
+
+template <class DataType>
+inline void CopyDataReverse (DataType *dst, const DataType *src, long int inHowmany)
+{
+ for (long int i=0;i<inHowmany;i++)
+ {
+ dst[i] = src[inHowmany-1-i];
+ }
+}
+
+template <class DataType>
+inline bool CompareArrays (const DataType *lhs, const DataType *rhs, long int arraySize)
+{
+ for (long int i=0; i<arraySize; i++)
+ {
+ if (lhs[i] != rhs[i])
+ return false;
+ }
+ return true;
+}
+
+template <class DataType>
+inline bool CompareMemory (const DataType& lhs, const DataType& rhs)
+{
+#ifdef ALIGN_OF
+ // We check at compile time if it's safe to cast data to int*
+ if (ALIGN_OF(DataType) >= ALIGN_OF(int) && (sizeof(DataType) % sizeof(int))==0)
+ {
+ return CompareArrays((const int*)&lhs, (const int*)&rhs, sizeof(DataType) / sizeof(int));
+ }
+#endif
+ return CompareArrays((const char*)&lhs, (const char*)&rhs, sizeof(DataType));
+}
+
+template <class T>
+class UNITY_AutoDelete
+{
+public:
+ UNITY_AutoDelete() : m_val(T()) { }
+ ~UNITY_AutoDelete() { if(m_val) UNITY_DELETE ( m_val, m_label ); }
+
+ void Assign(T val, MemLabelId label) { m_val = val; m_label = label; return *this; }
+ bool operator!() { return !m_val; }
+
+ /* Releases the internal pointer without deleting */
+ T releasePtr() { T tmp = m_val; m_val = T(); return tmp; }
+private:
+ UNITY_AutoDelete &operator=(T val);
+ UNITY_AutoDelete(const UNITY_AutoDelete<T>& other); // disabled
+ void operator=(const UNITY_AutoDelete<T>& other); // disabled
+ T m_val;
+ MemLabelId m_label;
+};
+
+class AutoFree
+{
+public:
+ AutoFree() : m_val(NULL), m_label(kMemDefault) { }
+ ~AutoFree() { if(m_val) UNITY_FREE ( m_label, m_val ); }
+
+ bool operator!() { return !m_val; }
+ void Assign(void* val, MemLabelId label) { m_label = label; m_val = val; }
+
+ /* Releases the internal pointer without deleting */
+ void* releasePtr() { void* tmp = m_val; m_val = NULL; return tmp; }
+private:
+ AutoFree &operator=(void* val); // disabled
+ AutoFree(const AutoFree& other); // disabled
+ void operator=(const AutoFree& other); // disabled
+ void* m_val;
+ MemLabelId m_label;
+};
+
+template <class T>
+inline T clamp (const T&t, const T& t0, const T& t1)
+{
+ if (t < t0)
+ return t0;
+ else if (t > t1)
+ return t1;
+ else
+ return t;
+}
+
+template <>
+inline float clamp (const float&t, const float& t0, const float& t1)
+{
+#if UNITY_XENON || UNITY_PS3
+ return FloatMin(FloatMax(t, t0), t1);
+#else
+ if (t < t0)
+ return t0;
+ else if (t > t1)
+ return t1;
+ else
+ return t;
+#endif
+}
+
+template <class T>
+inline T clamp01 (const T& t)
+{
+ if (t < 0)
+ return 0;
+ else if (t > 1)
+ return 1;
+ else
+ return t;
+}
+
+template <>
+inline float clamp01<float> (const float& t)
+{
+#if UNITY_XENON || UNITY_PS3
+ return FloatMin(FloatMax(t, 0.0f), 1.0f);
+#else
+ if (t < 0.0F)
+ return 0.0F;
+ else if (t > 1.0F)
+ return 1.0F;
+ else
+ return t;
+#endif
+}
+
+// Asserts if from is NULL or can't be cast to type To
+template<class To, class From> inline
+To assert_cast (From from)
+{
+ AssertIf (dynamic_cast<To> (from) == NULL);
+ return static_cast<To> (from);
+}
+
+inline float SmoothStep (float from, float to, float t)
+{
+ t = clamp01(t);
+ t = -2.0F * t*t*t + 3.0F * t*t;
+ return to * t + from * (1.0f - t);
+}
+// Rounds value down.
+// Note: base must be power of two value.
+inline UInt32 RoundDown (UInt32 value, SInt32 base)
+{
+ return value & (-base);
+}
+// Rounds value up.
+// Note: base must be power of two value.
+inline UInt32 RoundUp (UInt32 value, SInt32 base)
+{
+ return (value + base - 1) & (-base);
+}
+
+template<class T>
+inline T* Stride (T* p, size_t offset)
+{
+ return reinterpret_cast<T*>((char*)p + offset);
+}
+
+#endif // include-guard
diff --git a/Runtime/Utilities/UtilityTests.cpp b/Runtime/Utilities/UtilityTests.cpp
new file mode 100644
index 0000000..daef426
--- /dev/null
+++ b/Runtime/Utilities/UtilityTests.cpp
@@ -0,0 +1,629 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Utilities/MemoryPool.h"
+#include "Runtime/Utilities/File.h"
+#include <string.h>
+#include "Runtime/Utilities/dynamic_bitset.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/vector_set.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Allocator/FixedSizeAllocator.h"
+
+
+#if !GAMERELEASE
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/Utilities/GUID.h"
+UnityGUID StringToGUID (const std::string& pathName);
+static const char* kTempFolder = "Temp";
+#endif
+
+
+SUITE (UtilityTests)
+{
+
+
+TEST (TestMemoryPool)
+{
+ const int kBubble = 256;
+ const int kBubbleSize = 64;
+
+ // memory pool with 128 elements per bubble
+ MemoryPool pool (true, "TestPool", kBubbleSize, kBubble * kBubbleSize);
+
+ std::vector<void*> ptrs;
+
+ // allocate 128, should be one bubble and 128 objects
+ for (int i = 0; i < kBubble; ++i)
+ {
+ ptrs.push_back (pool.Allocate ());
+ }
+ CHECK_EQUAL (kBubble, pool.GetAllocCount ());
+ CHECK_EQUAL (1, pool.GetBubbleCount ());
+
+ // Allocate 1 more, should be 2 bubbles and kBubble+1 objects
+ ptrs.push_back (pool.Allocate ());
+ CHECK_EQUAL (kBubble + 1, pool.GetAllocCount ());
+ CHECK_EQUAL (2, pool.GetBubbleCount ());
+
+ // deallocate first bubble
+ void* last = ptrs[kBubble];
+ for (int i = 0; i < kBubble; ++i)
+ {
+ pool.Deallocate (ptrs[i]);
+ }
+ ptrs.clear ();
+ ptrs.push_back (last);
+ CHECK_EQUAL (1, pool.GetAllocCount());
+ CHECK_EQUAL (2, pool.GetBubbleCount());
+
+ // now we have two bubbles: 1st totally free, 2nd one with 1 allocation
+ // allocate 255 more objects. Should all fit into existing bubbles!
+ for (int i = 0; i < kBubble * 2 - 1; ++i)
+ {
+ ptrs.push_back (pool.Allocate ());
+ }
+ CHECK_EQUAL (kBubble * 2, pool.GetAllocCount ());
+ CHECK_EQUAL (2, pool.GetBubbleCount ());
+
+ // Allocate one more. Should cause additional bubble
+ ptrs.push_back (pool.Allocate ());
+ CHECK_EQUAL (kBubble * 2 + 1, pool.GetAllocCount ());
+ CHECK_EQUAL (3, pool.GetBubbleCount ());
+
+ // Deallocate all.
+ for (int i = 0; i < ptrs.size (); ++i)
+ {
+ pool.Deallocate (ptrs[i]);
+ }
+ ptrs.clear ();
+ CHECK_EQUAL (0, pool.GetAllocCount ());
+ CHECK_EQUAL (3, pool.GetBubbleCount ());
+
+ // now we have three totally free bubbles
+ // allocate 3*128 objects. Should fit into existing bubbles!
+ for (int i = 0; i < kBubble * 3; ++i)
+ {
+ ptrs.push_back (pool.Allocate ());
+ }
+ CHECK_EQUAL (kBubble * 3, pool.GetAllocCount ());
+ CHECK_EQUAL (3, pool.GetBubbleCount ());
+
+ // deallocate all from last bubble
+ for (int i = kBubble * 2; i < kBubble * 3; ++i)
+ {
+ pool.Deallocate (ptrs[i]);
+ }
+ ptrs.resize (kBubble * 2);
+ CHECK_EQUAL (kBubble * 2, pool.GetAllocCount ());
+ CHECK_EQUAL (3, pool.GetBubbleCount ());
+
+ // allocate one more
+ ptrs.push_back (pool.Allocate ());
+ CHECK_EQUAL (kBubble * 2 + 1, pool.GetAllocCount ());
+ CHECK_EQUAL (3, pool.GetBubbleCount ());
+
+ pool.DeallocateAll ();
+ ptrs.clear ();
+ CHECK_EQUAL (0, pool.GetAllocCount ());
+ CHECK_EQUAL (0, pool.GetBubbleCount ());
+
+ // Test pre-allocation: preallocate 1.5 worth of bubbles
+ pool.PreallocateMemory (kBubble * kBubbleSize * 3/2);
+ // should result in 2 bubbles
+ CHECK_EQUAL (2, pool.GetBubbleCount ());
+ for (int i = 0; i < kBubble * 2; ++i)
+ {
+ ptrs.push_back (pool.Allocate ());
+ }
+ CHECK_EQUAL (kBubble * 2, pool.GetAllocCount ());
+ CHECK_EQUAL (2, pool.GetBubbleCount ());
+
+ // Allocate one more, should create additional bubble
+ ptrs.push_back (pool.Allocate ());
+ CHECK_EQUAL (kBubble * 2 + 1, pool.GetAllocCount ());
+ CHECK_EQUAL (3, pool.GetBubbleCount ());
+ pool.DeallocateAll ();
+}
+
+
+#if !GAMERELEASE
+TEST (TestPathExist)
+{
+ using namespace std;
+
+ CreateDirectory (kTempFolder);
+
+ string filePath = AppendPathName (kTempFolder, "TestIsFileCreated");
+ DeleteFileOrDirectory (filePath);
+
+ CHECK (!IsPathCreated (filePath));
+ CHECK (!IsDirectoryCreated (filePath));
+ CHECK (!IsFileCreated (filePath));
+
+ CreateFile (filePath);
+
+ CHECK (IsPathCreated (kTempFolder));
+ CHECK (IsDirectoryCreated (kTempFolder));
+ CHECK (!IsFileCreated (kTempFolder));
+
+ CHECK (IsPathCreated (filePath));
+ CHECK (!IsDirectoryCreated (filePath));
+ CHECK (IsFileCreated (filePath));
+}
+#endif
+
+
+TEST (DynamicArray)
+{
+ // no allocation for empty array
+ dynamic_array<int> array;
+ CHECK_EQUAL (0, array.capacity ());
+
+ // push_back allocates
+ int j = 1;
+ array.push_back (j);
+ CHECK_EQUAL (1, array.size ());
+ CHECK (array.capacity () > 0);
+
+ // push_back(void)
+ int& i = array.push_back ();
+ i = 666;
+ CHECK_EQUAL(666, array.back ());
+
+ // clear frees memory?
+ array.clear ();
+ CHECK_EQUAL (0, array.size ());
+ CHECK_EQUAL (0, array.capacity ());
+
+ // 3 item list
+ j = 6;
+ array.push_back (j);
+ j = 7;
+ array.push_back (j);
+ j = 8;
+ array.push_back (j);
+
+ CHECK_EQUAL (3, array.size ());
+
+ // swapping
+ dynamic_array<int> ().swap (array);
+ CHECK_EQUAL (0, array.capacity ());
+ CHECK_EQUAL (0, array.size ());
+
+ // reserve
+ array.reserve (1024);
+ CHECK_EQUAL (1024, array.capacity ());
+ CHECK_EQUAL (0, array.size ());
+
+ // copy assignment
+ dynamic_array<int> array1;
+ j = 888;
+ array1.push_back (j);
+
+ array = array1;
+
+ CHECK_EQUAL (1, array.size ());
+ CHECK_EQUAL (888, array.back ());
+ CHECK_EQUAL (1, array1.size ());
+ CHECK_EQUAL (888, array1.back ());
+}
+
+ struct Stuff
+ {
+ int value;
+ int identifier;
+
+ bool operator < (const Stuff& rhs) const
+ {
+ return value < rhs.value;
+ }
+
+ Stuff (int a, int b) { value = a; identifier = b; }
+ };
+
+
+TEST (Test_vector_set_assign_clear_duplicates)
+{
+ // Make sure that duplicates are removed,
+ // but also that only the first same instance is maintained, the following ones are killed
+ Stuff input[] = { Stuff (10, 0), Stuff (11, 1), Stuff (3, 2), Stuff (3, 3), Stuff (4, 4), Stuff (10, 5) };
+ Stuff output[] = { Stuff (3, 2), Stuff (4, 4), Stuff (10, 0), Stuff (11, 1) };
+
+ vector_set<Stuff> test_set;
+ test_set.assign_clear_duplicates(input, input + ARRAY_SIZE(input));
+
+ CHECK_EQUAL(test_set.size(), ARRAY_SIZE(output));
+ for (int i=0;i<ARRAY_SIZE(output);i++)
+ {
+ CHECK_EQUAL(output[i].value, test_set[i].value);
+ CHECK_EQUAL(output[i].identifier, test_set[i].identifier);
+ }
+}
+
+class TestNode : public ListElement
+{
+};
+
+TEST (TestList)
+{
+ typedef List<TestNode> ListType;
+
+ struct
+ {
+ void operator () (ListType& list, TestNode* nodes[], int count)
+ {
+ CHECK_EQUAL (count, list.size_slow ());
+ int c = 0;
+ for (ListType::iterator i = list.begin (); i != list.end (); ++i)
+ {
+ CHECK (nodes[c] == &*i);
+ ++c;
+ }
+
+ CHECK_EQUAL (count, c);
+ }
+ } CheckNodes;
+
+ ListType list, emptyList, emptyList2;
+
+ CHECK_EQUAL (0, emptyList.size_slow ());
+ emptyList.clear ();
+ CHECK_EQUAL (0, emptyList.size_slow ());
+
+ TestNode* nodes[] =
+ {
+ new TestNode (),
+ new TestNode (),
+ new TestNode (),
+ new TestNode (),
+ new TestNode (),
+ new TestNode ()
+ };
+
+ emptyList.swap (emptyList2);
+ CHECK_EQUAL (0, emptyList.size_slow ());
+ CHECK_EQUAL (0, emptyList2.size_slow ());
+
+ // insertion and pushback
+ list.push_back (*nodes[1]);
+ list.insert (nodes[1], *nodes[0]);
+ list.push_back (*nodes[2]);
+ list.push_back (*nodes[3]);
+ list.push_back (*nodes[5]);
+ list.insert (nodes[5], *nodes[4]);
+ CheckNodes (list, nodes, 6);
+ // insert before self
+ list.insert (list.begin(), *list.begin ());
+ CheckNodes (list, nodes, 6);
+
+ list.append (emptyList);
+ CHECK_EQUAL (0, emptyList.size_slow ());
+ CheckNodes (list, nodes, 6);
+
+ // append remove into something empty
+ emptyList.append (list);
+ CHECK_EQUAL (0, list.size_slow ());
+ CheckNodes (emptyList, nodes, 6);
+
+ // invert operation by doing a swap
+ emptyList.swap (list);
+ CHECK_EQUAL (0, emptyList.size_slow ());
+ CheckNodes (list, nodes, 6);
+
+ // Create another list to test copying
+ TestNode* nodes2[] =
+ {
+ new TestNode (),
+ new TestNode (),
+ new TestNode ()
+ };
+ ListType list2;
+ list2.push_back (*nodes2[1]);
+ list2.push_front (*nodes2[0]);
+ list2.push_back (*nodes2[2]);
+ CheckNodes (list2, nodes2, 3);
+
+ // swap back and forth
+ list2.swap (list);
+ CheckNodes (list, nodes2, 3);
+ CheckNodes (list2, nodes, 6);
+ list.swap (list2);
+ CheckNodes (list, nodes, 6);
+ CheckNodes (list2, nodes2, 3);
+
+ list.append (list2); // insert before self
+ int c = 0;
+ for (ListType::iterator i = list.begin (); i != list.end (); ++i)
+ {
+ if (c >= 6)
+ CHECK (nodes2[c - 6] == &*i);
+ else
+ CHECK (nodes[c] == &*i);
+ ++c;
+ }
+ CHECK_EQUAL (9, list.size_slow ());
+ CHECK_EQUAL (0, list2.size_slow ());
+ CHECK_EQUAL (9, c);
+
+ emptyList.append (emptyList2);
+ CHECK_EQUAL (0, emptyList2.size_slow ());
+ CHECK_EQUAL (0, emptyList.size_slow ());
+}
+
+TEST (DynamicBitSet)
+{
+ dynamic_bitset set;
+ UInt32 block = 1 << 0 | 1 << 3 | 1 << 5;
+ set.resize (6);
+ from_block_range (&block, &block + 1, set);
+
+ CHECK_EQUAL (true, set.test (0));
+ CHECK_EQUAL (false, set.test (1));
+ CHECK_EQUAL (false, set.test (2));
+ CHECK_EQUAL (true, set.test (3));
+ CHECK_EQUAL (false, set.test (4));
+ CHECK_EQUAL (true, set.test (5));
+
+ to_block_range (set, &block);
+ bool res;
+
+ res = block & (1 << 0);
+ CHECK_EQUAL (true, res);
+ res = block & (1 << 1);
+ CHECK_EQUAL (false, res);
+ res = block & (1 << 2);
+ CHECK_EQUAL (false, res);
+ res = block & (1 << 3);
+ CHECK_EQUAL (true, res);
+ res = block & (1 << 4);
+ CHECK_EQUAL (false, res);
+ res = block & (1 << 5);
+ CHECK_EQUAL (true, res);
+}
+
+
+TEST (DynamicArrayMisc)
+{
+ // no allocation for empty array
+ dynamic_array<int> array;
+ CHECK_EQUAL (0, array.capacity ());
+ CHECK (array.owns_data ());
+ CHECK (array.begin () == array.end ());
+ CHECK (array.empty ());
+
+ // push_back allocates
+ int j = 1;
+ array.push_back (j);
+ CHECK_EQUAL (1, array.size ());
+ CHECK (array.capacity () > 0);
+
+ // push_back(void)
+ int& i = array.push_back ();
+ i = 666;
+ CHECK_EQUAL(666, array.back ());
+
+ // clear frees memory?
+ array.clear ();
+ CHECK_EQUAL (0, array.size ());
+ CHECK_EQUAL (0, array.capacity ());
+
+ // 3 item list
+ array.push_back (6);
+ array.push_back (7);
+ array.push_back (8);
+
+ CHECK_EQUAL (3, array.size ());
+
+ // swapping
+ dynamic_array<int> ().swap (array);
+ CHECK_EQUAL (0, array.capacity ());
+ CHECK_EQUAL (0, array.size ());
+
+ // reserve
+ array.reserve (1024);
+ CHECK_EQUAL (1024, array.capacity ());
+ CHECK_EQUAL (0, array.size ());
+
+ // copy assignment
+ dynamic_array<int> array1;
+ j = 888;
+ array1.push_back (j);
+
+ array = array1;
+
+ CHECK_EQUAL (1, array.size ());
+ CHECK_EQUAL (888, array.back ());
+ CHECK_EQUAL (1, array1.size ());
+ CHECK_EQUAL (888, array1.back ());
+}
+
+
+TEST (DynamicArrayErase)
+{
+ dynamic_array<int> vs;
+ vs.resize_uninitialized(5);
+
+ vs[0] = 0;
+ vs[1] = 1;
+ vs[2] = 2;
+ vs[3] = 3;
+ vs[4] = 4;
+
+ vs.erase(vs.begin() + 1, vs.begin() + 4);
+ CHECK_EQUAL (2, vs.size());
+ CHECK_EQUAL (5, vs.capacity());
+ CHECK_EQUAL (0, vs[0]);
+ CHECK_EQUAL (4, vs[1]);
+}
+
+static void VerifyConsecutiveIntArray (dynamic_array<int>& vs, int size, int capacity)
+{
+ CHECK_EQUAL (capacity, vs.capacity());
+ CHECK_EQUAL (size, vs.size());
+ for (int i=0;i<vs.size();i++)
+ CHECK_EQUAL (i, vs[i]);
+}
+
+TEST (DynamicArrayInsertOnEmpty)
+{
+ dynamic_array<int> vs;
+ int vals[] = { 0, 1 };
+
+ vs.insert(vs.begin(), vals, vals + ARRAY_SIZE(vals));
+
+ VerifyConsecutiveIntArray(vs, 2, 2);
+}
+
+
+TEST (DynamicArrayInsert)
+{
+ dynamic_array<int> vs;
+ vs.resize_uninitialized(5);
+
+ vs[0] = 0;
+ vs[1] = 1;
+ vs[2] = 4;
+ vs[3] = 5;
+ vs[4] = 6;
+
+ int vals[] = { 2, 3 };
+
+ // inser two values
+ vs.insert(vs.begin() + 2, vals, vals + ARRAY_SIZE(vals));
+ VerifyConsecutiveIntArray(vs, 7, 7);
+
+ // empty insert
+ vs.insert(vs.begin() + 2, vals, vals);
+
+ VerifyConsecutiveIntArray(vs, 7, 7);
+}
+
+TEST (DynamicArrayResize)
+{
+ dynamic_array<int> vs;
+ vs.resize_initialized(3, 2);
+ CHECK_EQUAL (3, vs.capacity());
+ CHECK_EQUAL (3, vs.size());
+ CHECK_EQUAL (2, vs[0]);
+ CHECK_EQUAL (2, vs[1]);
+ CHECK_EQUAL (2, vs[2]);
+
+ vs.resize_initialized(6, 3);
+ CHECK_EQUAL (6, vs.capacity());
+ CHECK_EQUAL (6, vs.size());
+ CHECK_EQUAL (2, vs[0]);
+ CHECK_EQUAL (2, vs[1]);
+ CHECK_EQUAL (2, vs[2]);
+ CHECK_EQUAL (3, vs[3]);
+ CHECK_EQUAL (3, vs[4]);
+ CHECK_EQUAL (3, vs[5]);
+
+ vs.resize_initialized(5, 3);
+ CHECK_EQUAL (6, vs.capacity());
+ CHECK_EQUAL (5, vs.size());
+ CHECK_EQUAL (2, vs[0]);
+ CHECK_EQUAL (2, vs[1]);
+ CHECK_EQUAL (2, vs[2]);
+ CHECK_EQUAL (3, vs[3]);
+ CHECK_EQUAL (3, vs[4]);
+
+ vs.resize_initialized(2, 3);
+ CHECK_EQUAL (6, vs.capacity());
+ CHECK_EQUAL (2, vs.size());
+ CHECK_EQUAL (2, vs[0]);
+ CHECK_EQUAL (2, vs[1]);
+}
+
+
+TEST(FixedSizeAllocator)
+{
+ typedef FixedSizeAllocator<sizeof(int)> TestAllocator;
+
+ TestAllocator testalloc = TestAllocator(MemLabelId(kMemDefaultId, NULL));
+
+ CHECK(testalloc.capacity() == 0);
+ CHECK(testalloc.total_free() == 0);
+ CHECK(testalloc.total_allocated() == 0);
+
+ int* ptr1 = (int*)testalloc.alloc();
+ *ptr1 = 1;
+
+ CHECK(testalloc.capacity() == 255*sizeof(int));
+ CHECK(testalloc.total_free() == 254*sizeof(int));
+ CHECK(testalloc.total_allocated() == sizeof(int));
+
+ int* ptr2 = (int*)testalloc.alloc();
+ *ptr2 = 2;
+
+ CHECK(testalloc.capacity() == 255*sizeof(int));
+ CHECK(testalloc.total_free() == 253*sizeof(int));
+ CHECK(testalloc.total_allocated() == 2*sizeof(int));
+ CHECK(*ptr1==1);
+ CHECK(ptr1 + 1 == ptr2);
+
+ testalloc.reset();
+
+ CHECK(testalloc.capacity() == 255*sizeof(int));
+ CHECK(testalloc.total_free() == 255*sizeof(int));
+ CHECK(testalloc.total_allocated() == 0);
+
+ testalloc.free_memory();
+
+ CHECK(testalloc.capacity() == 0);
+ CHECK(testalloc.total_free() == 0);
+ CHECK(testalloc.total_allocated() == 0);
+}
+
+
+
+TEST(StringFormatTest)
+{
+ CHECK_EQUAL ("Hello world it works", Format ("Hello %s it %s", "world", "works"));
+}
+
+
+#if !GAMERELEASE
+TEST(UnityGUIDTest)
+{
+ UnityGUID identifier[5];
+ identifier[0].Init ();
+ identifier[1].Init ();
+ identifier[2].Init ();
+ identifier[3].Init ();
+ identifier[4].Init ();
+
+ CHECK (identifier[0] != identifier[1]);
+ CHECK (identifier[1] != identifier[2]);
+ CHECK (identifier[2] != identifier[3]);
+ CHECK (identifier[3] != identifier[4]);
+ identifier[0] = identifier[1];
+ CHECK (identifier[0] == identifier[1]);
+}
+#endif
+
+
+TEST (TestUtility)
+{
+ using namespace std;
+
+ // Make sure that our stl implementation has consistent clear behaviour.
+ // If it doesn't we should probably stop using clear.
+ //
+ // If this test fails, it means that std::vector clear deallocates memory.
+ // Some optimized code this to not be the case!
+
+ vector<int> test;
+ test.resize (10);
+ test.clear ();
+ CHECK (test.capacity () != 0);
+}
+
+}
+#endif
diff --git a/Runtime/Utilities/VFPUtility.h b/Runtime/Utilities/VFPUtility.h
new file mode 100644
index 0000000..e0176b9
--- /dev/null
+++ b/Runtime/Utilities/VFPUtility.h
@@ -0,0 +1,43 @@
+#ifndef VFPTILITY_H
+#define VFPTILITY_H
+
+#if UNITY_SUPPORTS_VFP
+
+#define FMULS3(s0,s1,s2, s4,s5,s6, s8,s9,s10) \
+ fmuls s##s0, s##s4, s##s8 ; \
+ fmuls s##s1, s##s5, s##s9 ; \
+ fmuls s##s2, s##s6, s##s10
+
+#define FMACS3(s0,s1,s2, s4,s5,s6, s8,s9,s10) \
+ fmacs s##s0, s##s4, s##s8 ; \
+ fmacs s##s1, s##s5, s##s9 ; \
+ fmacs s##s2, s##s6, s##s10
+
+#define FCPYS3(s0,s1,s2, s4,s5,s6) \
+ fcpys s##s0, s##s4 ; \
+ fcpys s##s1, s##s5 ; \
+ fcpys s##s2, s##s6 ; \
+
+
+
+#define FMULS4(s0,s1,s2,s3, s4,s5,s6,s7, s8,s9,s10,s11) \
+ fmuls s##s0, s##s4, s##s8 ; \
+ fmuls s##s1, s##s5, s##s9 ; \
+ fmuls s##s2, s##s6, s##s10 ;\
+ fmuls s##s3, s##s7, s##s11
+
+#define FMACS4(s0,s1,s2,s3, s4,s5,s6,s7, s8,s9,s10,s11) \
+ fmacs s##s0, s##s4, s##s8 ; \
+ fmacs s##s1, s##s5, s##s9 ; \
+ fmacs s##s2, s##s6, s##s10 ;\
+ fmacs s##s3, s##s7, s##s11
+
+#define FCPYS4(s0,s1,s2,s3, s4,s5,s6,s7) \
+ fcpys s##s0, s##s4 ; \
+ fcpys s##s1, s##s5 ; \
+ fcpys s##s2, s##s6 ; \
+ fcpys s##s3, s##s7 ; \
+
+#endif
+
+#endif
diff --git a/Runtime/Utilities/ValidateArgs.h b/Runtime/Utilities/ValidateArgs.h
new file mode 100644
index 0000000..7f50c66
--- /dev/null
+++ b/Runtime/Utilities/ValidateArgs.h
@@ -0,0 +1,81 @@
+#ifndef VALIDATEARGS_H
+#define VALIDATEARGS_H
+
+#if DEBUGMODE
+
+#define ABORT_INVALID_FLOAT(value,varname,classname) \
+if (!IsFinite (value)) \
+{ \
+ ErrorStringObject (Format ("%s.%s assign attempt for '%s' is not valid. Input %s is { %s }.", #classname, #varname, GetName (), #varname, FloatToString (value).c_str ()), this); \
+ return; \
+}
+
+#define ABORT_INVALID_ARG_FLOAT(value,varname,methodname,classname) \
+if (!IsFinite (value)) \
+{ \
+ ErrorStringObject (Format ("%s.%s(%s) assign attempt for '%s' is not valid. Input %s is { %s }.", #classname, #methodname, #varname, GetName (), #varname, FloatToString (value).c_str ()), this); \
+ return; \
+}
+
+#ifdef VECTOR2_H
+#define ABORT_INVALID_VECTOR2(value,varname,classname) \
+if (!IsFinite (value)) \
+{ \
+ ErrorStringObject (Format ("%s.%s assign attempt for '%s' is not valid. Input %s is { %s, %s }.", #classname, #varname, GetName (), #varname, FloatToString (value.x).c_str (), FloatToString (value.y).c_str ()), this); \
+ return; \
+}
+
+#define ABORT_INVALID_ARG_VECTOR2(value,varname,methodname,classname) \
+if (!IsFinite (value)) \
+{ \
+ ErrorStringObject (Format ("%s.%s(%s) assign attempt for '%s' is not valid. Input %s is { %s, %s }.", #classname, #methodname, #varname, GetName (), #varname, FloatToString (value.x).c_str (), FloatToString (value.y).c_str ()), this); \
+ return; \
+}
+#endif
+
+#ifdef VECTOR3_H
+#define ABORT_INVALID_VECTOR3(value,varname,classname) \
+if (!IsFinite (value)) \
+{ \
+ ErrorStringObject (Format ("%s.%s assign attempt for '%s' is not valid. Input %s is { %s, %s, %s }.", #classname, #varname, GetName (), #varname, FloatToString (value.x).c_str (), FloatToString (value.y).c_str (), FloatToString (value.z).c_str ()), this); \
+ return; \
+}
+
+#define ABORT_INVALID_ARG_VECTOR3(value,varname,methodname,classname) \
+ if (!IsFinite (value)) \
+{ \
+ ErrorStringObject (Format ("%s.%s(%s) assign attempt for '%s' is not valid. Input %s is { %s, %s, %s }.", #classname, #methodname, #varname, GetName (), #varname, FloatToString (value.x).c_str (), FloatToString (value.y).c_str (), FloatToString (value.z).c_str ()), this); \
+ return; \
+}
+#endif
+
+#ifdef QUATERNION_H
+#define ABORT_INVALID_QUATERNION(value,varname,classname) \
+if (!IsFinite(value)) \
+{ \
+ ErrorStringObject (Format("%s.%s assign attempt for '%s' is not valid. Input rotation is { %s, %s, %s, %s }.", #classname, #varname, GetName(), FloatToString(value.x).c_str(), FloatToString(value.y).c_str(), FloatToString(value.z).c_str(), FloatToString(value.w).c_str()), this); \
+ return; \
+}
+
+#define ABORT_INVALID_ARG_QUATERNION(value,varname,methodname,classname) \
+if (!IsFinite(value)) \
+{ \
+ ErrorStringObject (Format("%s.%s(%s) assign attempt for '%s' is not valid. Input rotation is { %s, %s, %s, %s }.", #classname, #methodname, #varname, GetName(), FloatToString(value.x).c_str(), FloatToString(value.y).c_str(), FloatToString(value.z).c_str(), FloatToString(value.w).c_str()), this); \
+ return; \
+}
+#endif
+
+#else
+
+#define ABORT_INVALID_FLOAT(value,varname,classname)
+#define ABORT_INVALID_VECTOR2(value,varname,classname)
+#define ABORT_INVALID_VECTOR3(value,varname,classname)
+#define ABORT_INVALID_QUATERNION(value,varname,classname)
+
+#define ABORT_INVALID_ARG_FLOAT(value,varname,methodname,classname)
+#define ABORT_INVALID_ARG_VECTOR2(value,varname,methodname,classname)
+#define ABORT_INVALID_ARG_VECTOR3(value,varname,methodname,classname)
+#define ABORT_INVALID_ARG_QUATERNION(value,varname,methodname,classname)
+
+#endif
+#endif
diff --git a/Runtime/Utilities/WavFileUtility.cpp b/Runtime/Utilities/WavFileUtility.cpp
new file mode 100644
index 0000000..2d2471d
--- /dev/null
+++ b/Runtime/Utilities/WavFileUtility.cpp
@@ -0,0 +1,54 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#include "WavFileUtility.h"
+#include <stdio.h>
+
+static inline int fput2 (unsigned short v, FILE* f)
+{
+ return fwrite (&v, 2, 1, f);
+}
+
+static inline int fput4 (unsigned v, FILE* f)
+{
+ return fwrite (&v, 4, 1, f);
+}
+
+bool WriteWaveFile (char const* filename, int channels, int samplebits, int freq, void* data, int samples)
+{
+ FILE* wf = fopen (filename, "wb+");
+ if (!wf)
+ return false;
+
+ size_t datasize = channels * (samplebits / 8) * samples;
+
+#define C(x) if (x < 0) goto err
+
+ // RIFF chunk
+ C((fputs ("RIFF", wf)));
+ C((fput4 (datasize + (12-8) + 24 + 8, wf)));
+ C((fputs ("WAVE", wf)));
+
+ // FORMAT chunk
+ C((fputs ("fmt ", wf)));
+ C((fput4 (0x10, wf)));
+ C((fput2 (0x01, wf)));
+ C((fput2 (channels, wf)));
+ C((fput4 (freq, wf)));
+ C((fput4 (channels * samplebits * freq, wf)));
+ C((fput2 (samplebits >> 3, wf)));
+ C((fput2 (samplebits, wf)));
+
+ // DATA chunk
+ C((fputs ("data", wf)));
+ C((fput4 (datasize, wf)));
+ C((fwrite (data, datasize, 1, wf)));
+#undef C
+
+ fclose (wf);
+ return true;
+
+err:
+ fclose (wf);
+ return false;
+}
diff --git a/Runtime/Utilities/WavFileUtility.h b/Runtime/Utilities/WavFileUtility.h
new file mode 100644
index 0000000..ec1c243
--- /dev/null
+++ b/Runtime/Utilities/WavFileUtility.h
@@ -0,0 +1,6 @@
+#ifndef UNITY_WAV_FILE_UTILITY_H_
+#define UNITY_WAV_FILE_UTILITY_H_
+
+bool WriteWaveFile (char const* filename, int channels, int samplebits, int freq, void* data, int samples);
+
+#endif // UNITY_WAV_FILE_UTILITY_H_
diff --git a/Runtime/Utilities/Word.cpp b/Runtime/Utilities/Word.cpp
new file mode 100644
index 0000000..2057283
--- /dev/null
+++ b/Runtime/Utilities/Word.cpp
@@ -0,0 +1,659 @@
+#include "UnityPrefix.h"
+#include "Word.h"
+#include <limits.h>
+#include <stdio.h>
+#include "Runtime/Math/FloatConversion.h"
+#include "Annotations.h"
+
+#if !UNITY_EXTERNAL_TOOL
+#include "Runtime/Allocator/LinearAllocator.h"
+#endif
+
+using namespace std;
+
+bool BeginsWithCaseInsensitive (const std::string &s, const std::string &beginsWith)
+{
+ // Note that we don't want to convert the whole s string to lower case, simply because
+ // it might not be very efficient (imagine a string that has, e.g., 2 Mb chars), so we take
+ // only a substring that we need to compare with the given prefix.
+ return BeginsWith(ToLower(s.substr(0, beginsWith.length())), ToLower(beginsWith));
+}
+
+bool BeginsWith (const char* s, const char* beginsWith)
+{
+ return strncmp (s, beginsWith, strlen (beginsWith)) == 0;
+}
+
+
+#if !WEBPLUG
+bool IsStringNumber (const string& s)
+{
+ return IsStringNumber (s.c_str ());
+}
+#endif
+
+
+#if UNITY_EDITOR
+/*
+ Used by SemiNumericCompare:
+ if c[*index] is > '9', returns INT_MAX-265+c[*index] and increases *index by 1.
+ if '0' <= c[*index] <= '9' consumes all numeric characters and returns the numerical value + '0'
+ if c[*index] is < '0' returns (int)c[*index] and increases *index by 1;
+*/
+static int GetSNOrdinal(const char* c, int& index)
+{
+
+ if((unsigned char)c[index] < (unsigned char)'0')
+ return (unsigned char)c[index++];
+ else if ((unsigned char)c[index] > (unsigned char)'9')
+ return (INT_MAX-256) + (unsigned char)ToLower(c[index++]);
+ else
+ {
+ int atoi=0;
+ while (c[index] >= '0' && c[index] <= '9')
+ {
+ atoi = atoi*10 + (c[index++] - '0'); // TODO: clamp at INT_MAX-256
+ }
+ return atoi+'0';
+ }
+}
+
+// Human-like sorting.
+// Sorts strings alphabetically, but with numbers in strings numerically, so "xx11" comes after "xx2".
+int SemiNumericCompare(const char * str1, const char * str2)
+{
+ int i1 = 0;
+ int i2 = 0;
+ int o1, o2;
+
+ while ((o1 = GetSNOrdinal(str1, i1)) == (o2 = GetSNOrdinal(str2, i2)))
+ {
+ if (!o1)
+ return i2-i1; // to make strings like "xx1", "xx01" and "xx001" have a stable sorting (longest string first as in Finder)
+ }
+
+ return(o1 - o2);
+}
+#endif
+
+int StrNICmp(const char * str1, const char * str2, size_t n)
+{
+ const char * p1 = (char *) str1;
+ const char * p2 = (char *) str2;
+ char c1, c2;
+ size_t charsLeft=n;
+
+ if( n <= 0 )
+ return 0;
+
+ while ((c1 = ToLower (*p1)) == (c2 = ToLower (*p2)))
+ {
+ ++p1; ++p2; --charsLeft;
+ if (!charsLeft || !c1)
+ return 0;
+ }
+
+ return(c1 - c2);
+}
+
+int StrICmp(const char * str1, const char * str2)
+{
+ const char * p1 = (char *) str1;
+ const char * p2 = (char *) str2;
+ char c1, c2;
+
+ while ((c1 = ToLower (*p1)) == (c2 = ToLower (*p2)))
+ {
+ p1++; p2++;
+ if (!c1)
+ return 0;
+ }
+
+ return(c1 - c2);
+}
+
+int StrCmp(const char * str1, const char * str2)
+{
+ const char * p1 = (char *) str1;
+ const char * p2 = (char *) str2;
+ char c1, c2;
+
+ while ((c1 = (*p1)) == (c2 = (*p2)))
+ {
+ p1++; p2++;
+ if (!c1)
+ return 0;
+ }
+
+ return(c1 - c2);
+}
+
+
+#if !WEBPLUG
+bool IsStringNumber (const char* s)
+{
+ bool success = false;
+ bool hadPoint = false;
+ long i = 0;
+
+ while (s[i] != '\0')
+ {
+ switch (s[i])
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+
+ success = true;
+
+ break;
+
+ case '.':
+ case ',':
+
+ // Make sure we only have one . or ,
+ if (hadPoint)
+ return false;
+ hadPoint = true;
+
+ break;
+
+ case '+':
+ case '-':
+
+ // + or - are only allowed at the beginning of the string
+ if (i != 0)
+ return false;
+ break;
+
+ default:
+ return false;
+ break;
+ }
+ i++;
+ }
+
+ return success;
+}
+#endif
+
+
+SInt32 StringToInt (char const* s)
+{
+ return atol (s);
+}
+
+template<typename T>
+inline string _ToString (const char* formatString, T value)
+{
+ char buf[255];
+
+ #ifdef WIN32
+ _snprintf
+ #else
+ snprintf
+ #endif
+ (buf, sizeof(buf), formatString, value);
+
+ return string (buf);
+}
+
+string IntToString (SInt32 i)
+{
+ return _ToString ("%i", i);
+}
+
+string UnsignedIntToString (UInt32 i)
+{
+ return _ToString ("%u", i);
+}
+
+string Int64ToString (SInt64 i)
+{
+ return _ToString ("%lli", i);
+}
+
+string UnsignedInt64ToString (UInt64 i)
+{
+ return _ToString ("%llu", i);
+}
+
+string DoubleToString (double i)
+{
+ return _ToString ("%f", i);
+}
+
+string FloatToString (float f, const char* precFormat)
+{
+ char buf[255];
+ if (IsNAN(f))
+ strcpy(buf, "NaN");
+ else if (IsMinusInf(f))
+ strcpy(buf, "-Infinity");
+ else if (IsPlusInf(f))
+ strcpy(buf, "Infinity");
+ else
+ sprintf (buf, precFormat, f);
+ return string (buf);
+}
+
+string SHA1ToString(unsigned char hash[20])
+{
+ char buffer[41];
+ for ( int i=0; i < 20; i++ )
+ sprintf(&buffer[i*2], "%.2x", hash[i]);
+ return std::string(buffer, 40);
+}
+
+string MD5ToString(unsigned char hash[16])
+{
+ char buffer[33];
+ for ( int i=0; i < 16; i++ )
+ sprintf(&buffer[i*2], "%.2x", hash[i]);
+ return std::string(buffer, 32);
+}
+
+// Parses simple float: optional sign, digits, period, digits.
+// No exponent or leading whitespace support.
+// No locale support.
+// We use it to be independent of locale, and because atof() did
+// show up in the shader parsing profile.
+float SimpleStringToFloat (const char* str, int* outLength)
+{
+ const char *p = str;
+
+ // optional sign
+ bool negative = false;
+ switch (*p) {
+ case '-': negative = true; // fall through to increment pointer
+ case '+': p++;
+ }
+
+ double number = 0.0;
+
+ // scan digits
+ while (IsDigit(*p))
+ {
+ number = number * 10.0 + (*p - '0');
+ p++;
+ }
+
+ // optional decimal part
+ if (*p == '.')
+ {
+ p++;
+
+ // scan digits after decimal point
+ double scaler = 0.1;
+ while (IsDigit(*p))
+ {
+ number += (*p - '0') * scaler;
+ scaler *= 0.1;
+ p++;
+ }
+ }
+
+ // apply sign
+ if (negative)
+ number = -number;
+
+ // Do not check for equality with atof() - results
+ // of that are dependent on locale.
+ //DebugAssertIf(!CompareApproximately(number, atof(str)));
+
+ if (outLength)
+ *outLength = (int) (p - str);
+
+ return float(number);
+}
+
+
+void VFormatBuffer (char* buffer, int size, const char* format, va_list ap)
+{
+ va_list zp;
+ va_copy (zp, ap);
+ vsnprintf (buffer, size, format, zp);
+ va_end (zp);
+}
+
+std::string VFormat (const char* format, va_list ap)
+{
+ va_list zp;
+ va_copy (zp, ap);
+ char buffer[1024 * 10];
+ vsnprintf (buffer, 1024 * 10, format, zp);
+ va_end (zp);
+ return buffer;
+}
+
+std::string Format (const char* format, ...)
+{
+ va_list va;
+ va_start (va, format);
+ std::string formatted = VFormat (format, va);
+ va_end (va);
+ return formatted;
+}
+
+#if UNITY_EDITOR || UNITY_FBX_IMPORTER
+bool AsciiToUTF8 (std::string& name)
+{
+ if (name.empty())
+ return true;
+
+ #if UNITY_OSX
+ CFStringRef str = CFStringCreateWithCString (NULL, name.c_str(), kCFStringEncodingASCII);
+ if (str != NULL)
+ {
+ bool res = false;
+
+ char* tempName;
+ ALLOC_TEMP(tempName, char, name.size() * 2);
+ if (CFStringGetCString(str, tempName, name.size() * 2, kCFStringEncodingUTF8))
+ {
+ name = tempName;
+ res = true;
+ }
+ CFRelease(str);
+ return res;
+ }
+
+ return false;
+
+ #elif UNITY_WIN
+
+ bool result = false;
+ int bufferSize = (int) name.size()*4+1;
+ wchar_t* wideBuffer;
+ ALLOC_TEMP(wideBuffer, wchar_t, bufferSize);
+ if( MultiByteToWideChar( CP_ACP, 0, name.c_str(), -1, wideBuffer, bufferSize ) )
+ {
+ char* buffer;
+ ALLOC_TEMP(buffer, char, bufferSize);
+ if( WideCharToMultiByte( CP_UTF8, 0, wideBuffer, -1, buffer, bufferSize, NULL, NULL ) )
+ {
+ name = buffer;
+ result = true;
+ }
+ }
+ return result;
+
+ #elif UNITY_LINUX
+
+ return true; // do nothing for now. Tests should show how much is this
+ // function needed.
+
+ #else
+ #error Unknown platform
+ #endif
+}
+
+std::string StripInvalidIdentifierCharacters (std::string str)
+{
+ for (unsigned int i=0;i<str.size();i++)
+ {
+ char c = str[i];
+ if (c == '~' || c == '&' || c == '%' || c == '|' || c == '$' || c == '<' || c == '>' || c == '/' || c == '\\')
+ str[i] = '_';
+ }
+ return str;
+}
+#endif
+
+void HexStringToBytes (char* str, size_t bytes, void *data)
+{
+ for (size_t i=0; i<bytes; i++)
+ {
+ UInt8 b;
+ char ch = str[2*i+0];
+ if (ch <= '9')
+ b = (ch - '0') << 4;
+ else
+ b = (ch - 'a' + 10) << 4;
+
+ ch = str[2*i+1];
+ if (ch <= '9')
+ b |= (ch - '0');
+ else
+ b |= (ch - 'a' + 10);
+
+ ((UInt8*)data)[i] = b;
+ }
+}
+
+void BytesToHexString (const void *data, size_t bytes, char* str)
+{
+ static const char kHexToLiteral[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ for (size_t i=0; i<bytes;i++)
+ {
+ UInt8 b = ((UInt8*)data)[i];
+ str[2*i+0] = kHexToLiteral[ b >> 4 ];
+ str[2*i+1] = kHexToLiteral[ b & 0xf ];
+ }
+}
+
+std::string BytesToHexString (const void* data, size_t numBytes)
+{
+ std::string result;
+ result.resize (numBytes * 2);
+ BytesToHexString (data, numBytes, &result[0]);
+ return result;
+}
+
+string FormatBytes(SInt64 b)
+{
+ AssertIf(sizeof(b) != 8);
+
+ if(b < 0)
+ return "Unknown";
+ if (b < 512)
+#if UNITY_64 && UNITY_LINUX
+ return Format("%ld B",b);
+#else
+ return Format("%lld B",b);
+#endif
+ if (b < 512*1024)
+ return Format("%01.1f KB",b / 1024.0);
+
+ b /= 1024;
+ if (b < 512*1024)
+ return Format("%01.1f MB", b / 1024.0);
+
+ b /= 1024;
+ return Format("%01.2f GB",b / 1024.0);
+}
+
+std::string Append (char const* a, std::string const& b)
+{
+ std::string r;
+ size_t asz = strlen (a);
+ r.reserve (asz + b.size ());
+ r.assign (a, asz);
+ r.append (b);
+ return r;
+}
+
+std::string Append (char const* a, char const* b)
+{
+ std::string r;
+ size_t asz = strlen (a);
+ size_t bsz = strlen (b);
+ r.reserve (asz + bsz);
+ r.assign (a, asz);
+ r.append (b, bsz);
+ return r;
+}
+
+std::string Append (std::string const& a, char const* b)
+{
+ std::string r;
+ size_t bsz = strlen(b);
+ r.reserve(a.size() + bsz);
+ r.assign(a);
+ r.append(b, bsz);
+ return r;
+}
+
+#if UNITY_OSX || UNITY_IPHONE
+CFStringRef StringToCFString (const std::string &str)
+{
+ return CFStringCreateWithCString(kCFAllocatorDefault, str.c_str(), kCFStringEncodingUTF8);
+}
+
+std::string CFStringToString (CFStringRef str)
+{
+ std::string output;
+ if (str)
+ {
+ int bufferSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8) + 1;
+ char *buf;
+ MALLOC_TEMP( buf, bufferSize );
+ if (CFStringGetCString(str, buf, bufferSize, kCFStringEncodingUTF8))
+ output = buf;
+ }
+ return output;
+}
+#endif
+
+std::string Trim(const std::string &input, const std::string &ws)
+{
+ size_t startpos = input.find_first_not_of(ws); // Find the first character position after excluding leading blank spaces
+ size_t endpos = input.find_last_not_of(ws); // Find the first character position from reverse af
+
+ // if all spaces or empty return an empty string
+ if(( string::npos == startpos ) || ( string::npos == endpos))
+ {
+ return std::string(); // empty string
+ }
+ else
+ {
+ return input.substr( startpos, endpos-startpos+1 );
+ }
+}
+
+
+/*
+ Convert version string of form XX.X.XcXXX to UInt32 in Apple 'vers' representation
+ as described in Tech Note TN1132 (http://developer.apple.com/technotes/tn/pdf/tn1132.pdf)
+ eg.
+ 1.2.0
+ 1.2.1
+ 1.2.1a1
+ 1.2.1b1
+ 1.2.1r12
+ */
+int GetNumericVersion (char const* versionCString)
+{
+ if( (*versionCString)=='\0' )
+ return 0;
+
+ int major=0,minor=0,fix=0,type='r',release=0;
+ const char *spos=versionCString;
+ major = *(spos++) - '0';
+ if (*spos>='0' && *spos < '9')
+ major = major*10 + *(spos++)-'0';
+
+ if (*spos)
+ {
+ if (*spos=='.')
+ {
+ spos++;
+ if(*spos)
+ minor=*(spos++)-'0';
+ }
+ if (*spos)
+ {
+ if (*spos=='.')
+ {
+ spos++;
+ if (*spos)
+ fix=*(spos++)-'0';
+ }
+ if (*spos)
+ {
+ type = *(spos++);
+ if (*spos)
+ {
+ release = *(spos++)-'0';
+ if (*spos)
+ {
+ release = release*10+*(spos++)-'0';
+ if (*spos)
+ {
+ release=release*10+*(spos++)-'0';
+ }
+ }
+ }
+ }
+ }
+ }
+
+ unsigned int version = 0;
+ version |= ((major/10)%10)<<28;
+ version |= (major%10)<<24;
+ version |= (minor%10)<<20;
+ version |= (fix%10)<<16;
+ switch( type )
+ {
+ case 'D':
+ case 'd': version |= 0x2<<12; break;
+ case 'A':
+ case 'a': version |= 0x4<<12; break;
+ case 'B':
+ case 'b': version |= 0x6<<12; break;
+ case 'F':
+ case 'R':
+ case 'f':
+ case 'r': version |= 0x8<<12; break;
+ }
+ version |= ((release / 100)%10)<<8;
+ version |= ((release / 10) %10)<<4;
+ version |= release % 10;
+
+ AssertIf (((int)version) < 0);
+
+ return version;
+}
+
+void Split (const std::string s, char splitChar, std::vector<std::string> &parts)
+{
+ int n = 0, n1 = 0;
+ while ( 1 )
+ {
+ n1 = s.find (splitChar, n);
+ std::string p = s.substr (n, n1-n);
+ if ( p.length () )
+ {
+ parts.push_back (p);
+ }
+ if ( n1 == std::string::npos )
+ break;
+
+ n = n1 + 1;
+ }
+}
+
+void Split (const std::string s, const char* splitChars, std::vector<std::string> &parts)
+{
+ int n = 0, n1 = 0;
+ while ( 1 )
+ {
+ n1 = s.find_first_of (splitChars, n);
+ std::string p = s.substr (n, n1-n);
+ if ( p.length () )
+ {
+ parts.push_back (p);
+ }
+ if ( n1 == std::string::npos )
+ break;
+
+ n = n1 + 1;
+ }
+}
diff --git a/Runtime/Utilities/Word.h b/Runtime/Utilities/Word.h
new file mode 100644
index 0000000..d54ac5b
--- /dev/null
+++ b/Runtime/Utilities/Word.h
@@ -0,0 +1,227 @@
+#ifndef WORD_H
+#define WORD_H
+
+#include <string>
+#include <vector>
+#include <stdarg.h> // va_list
+#include <vector>
+#include "Annotations.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Modules/ExportModules.h"
+
+#if UNITY_OSX || UNITY_IPHONE
+ #include "CoreFoundation/CoreFoundation.h"
+#endif
+
+bool BeginsWithCaseInsensitive (const std::string &s, const std::string &beginsWith);
+
+bool BeginsWith (const char* s, const char* beginsWith);
+
+template<typename StringType>
+bool BeginsWith (const StringType &s, const StringType &beginsWith)
+{
+ return BeginsWith (s.c_str(), beginsWith.c_str());
+}
+template<typename StringType>
+bool BeginsWith (const StringType &s, const char *beginsWith)
+{
+ return BeginsWith (s.c_str(), beginsWith);
+}
+
+inline bool EndsWith (const char* str, int strLen, const char* sub, int subLen){
+ return (strLen >= subLen) && (strncmp (str+strLen-subLen, sub, subLen)==0);
+}
+
+template<typename StringType>
+bool EndsWith (const StringType& str, const StringType& sub)
+{
+ return EndsWith(str.c_str(), str.size(), sub.c_str(), sub.size());
+}
+
+template<typename StringType>
+bool EndsWith (const StringType& str, const char* endsWith)
+{
+ return EndsWith(str.c_str(), str.size(), endsWith, strlen(endsWith));
+}
+
+inline bool EndsWith (const char* s, const char* endsWith){
+ return EndsWith(s, strlen(s), endsWith, strlen(endsWith));
+}
+
+void ConcatCString( char* root, const char* append );
+#if !WEBPLUG
+bool IsStringNumber (const char* s);
+bool IsStringNumber (const std::string& s);
+#endif
+
+SInt32 StringToInt (char const* s);
+
+template <typename StringType>
+SInt32 StringToInt (const StringType& s)
+{
+ return StringToInt(s.c_str());
+}
+
+/// Replacement for atof is not dependent on locale settings for what to use as the decimal separator.
+/// Limited support but fast. It does'nt work for infinity, nan, but
+/// This function is lossy. Converting a string back and forth does not result in the same binary exact float representation.
+/// See FloatStringConversion.h for binary exact string<->float conversion functions.
+float SimpleStringToFloat (const char* str, int* outLength = NULL);
+
+std::string IntToString (SInt32 i);
+std::string UnsignedIntToString (UInt32 i);
+std::string Int64ToString (SInt64 i);
+std::string UnsignedInt64ToString (UInt64 i);
+std::string DoubleToString (double i);
+std::string EXPORT_COREMODULE FloatToString (float f, const char* precFormat = "%f");
+
+std::string SHA1ToString(unsigned char hash[20]);
+std::string MD5ToString(unsigned char hash[16]);
+
+int StrNICmp (const char* a, const char* b, size_t n);
+int StrICmp (const char* a, const char* b);
+inline int StrICmp(const UnityStr& str1, const UnityStr& str2) { return StrICmp (str1.c_str (), str2.c_str ()); }
+int StrCmp (const char* a, const char* b);
+inline int StrCmp(const std::string& str1, const std::string& str2) { return StrCmp (str1.c_str (), str2.c_str ()); }
+inline int StrICmp(const std::string& str1, const std::string& str2) { return StrICmp (str1.c_str (), str2.c_str ()); }
+
+#if UNITY_EDITOR
+int SemiNumericCompare(const char * str1, const char * str2);
+inline int SemiNumericCompare(const std::string& str1, const std::string& str2) { return SemiNumericCompare (str1.c_str (), str2.c_str ()); }
+#endif
+
+inline char ToLower (char v)
+{
+ if (v >= 'A' && v <= 'Z')
+ return static_cast<char>(v | 0x20);
+ else
+ return v;
+}
+
+inline char ToUpper (char v)
+{
+ if (v >= 'a' && v <= 'z')
+ return static_cast<char>(v & 0xdf);
+ else
+ return v;
+}
+
+template<typename StringType>
+StringType ToUpper (const StringType& input)
+{
+ StringType s = input;
+ for (typename StringType::iterator i= s.begin (); i != s.end ();i++)
+ *i = ToUpper (*i);
+ return s;
+}
+template<typename StringType>
+StringType ToLower (const StringType& input)
+{
+ StringType s = input;
+ for (typename StringType::iterator i= s.begin (); i != s.end ();i++)
+ *i = ToLower (*i);
+ return s;
+}
+template<typename StringType>
+void ToUpperInplace (StringType& input)
+{
+ for (typename StringType::iterator i= input.begin (); i != input.end ();i++)
+ *i = ToUpper (*i);
+}
+template<typename StringType>
+void ToLowerInplace (StringType& input)
+{
+ for (typename StringType::iterator i= input.begin (); i != input.end ();i++)
+ *i = ToLower (*i);
+}
+
+TAKES_PRINTF_ARGS(1,2) std::string EXPORT_COREMODULE Format (const char* format, ...);
+std::string VFormat (const char* format, va_list ap);
+
+void VFormatBuffer (char* buffer, int size, const char* format, va_list ap);
+template<typename StringType>
+inline TAKES_PRINTF_ARGS(1,2) StringType FormatString(const char* format, ...)
+{
+ char buffer[10*1024];
+ va_list va;
+ va_start( va, format );
+ VFormatBuffer (buffer, 10*1024, format, va);
+ return StringType(buffer);
+}
+
+std::string Append (char const* a, std::string const& b);
+EXPORT_COREMODULE std::string Append (char const* a, char const* b);
+std::string Append (std::string const& a, char const* b);
+
+inline bool IsDigit (char c) { return c >= '0' && c <= '9'; }
+inline bool IsAlpha (char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
+inline bool IsSpace (char c) { return c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r' || c == ' '; }
+inline bool IsAlphaNumeric (char c) { return IsDigit (c) || IsAlpha (c); }
+
+template<typename alloc>
+void replace_string (std::basic_string<char, std::char_traits<char>, alloc>& target,
+ const std::basic_string<char, std::char_traits<char>, alloc>& search,
+ const std::basic_string<char, std::char_traits<char>, alloc>& replace, size_t startPos = 0)
+{
+ if (search.empty())
+ return;
+
+ typename std::basic_string<char, std::char_traits<char>, alloc>::size_type p = startPos;
+ while ((p = target.find (search, p)) != std::basic_string<char, std::char_traits<char>, alloc>::npos)
+ {
+ target.replace (p, search.size (), replace);
+ p += replace.size ();
+ }
+}
+
+template<typename StringType>
+void replace_string (StringType& target, const char* search, const StringType& replace, size_t startPos = 0)
+{
+ replace_string(target,StringType(search),replace, startPos);
+}
+template<typename StringType>
+void replace_string (StringType& target, const StringType& search, const char* replace, size_t startPos = 0)
+{
+ replace_string(target,search,StringType(replace), startPos);
+}
+template<typename StringType>
+void replace_string (StringType& target, const char* search, const char* replace, size_t startPos = 0)
+{
+ replace_string(target,StringType(search),StringType(replace), startPos);
+}
+
+#if UNITY_EDITOR || UNITY_FBX_IMPORTER
+/// Converts name to UTF8. Returns whether conversion was successful.
+/// If not successful name will not be touched
+bool AsciiToUTF8 (std::string& name);
+std::string StripInvalidIdentifierCharacters (std::string str);
+#endif
+
+std::string FormatBytes(SInt64 b);
+
+#if UNITY_OSX || UNITY_IPHONE
+CFStringRef StringToCFString (const std::string &str);
+std::string CFStringToString (CFStringRef str);
+#endif
+
+std::string Trim(const std::string &input, const std::string &ws=" \t");
+
+void HexStringToBytes (char* str, size_t numBytes, void *data);
+void BytesToHexString (const void *data, size_t numBytes, char* str);
+std::string BytesToHexString (const void* data, size_t numBytes);
+
+int GetNumericVersion (char const* versionCString);
+inline int GetNumericVersion (const std::string& versionString) { return GetNumericVersion (versionString.c_str()); }
+
+/// Split a string delimited by splitChar or any character in splitChars into parts.
+/// Parts is appended, not cleared.
+/// Empty parts are discarded.
+void Split (const std::string s, char splitChar, std::vector<std::string> &parts);
+void Split (const std::string s, const char* splitChars, std::vector<std::string> &parts);
+
+inline std::string QuoteString( const std::string& str )
+{
+ return '"' + str + '"';
+}
+
+#endif
diff --git a/Runtime/Utilities/WordTests.cpp b/Runtime/Utilities/WordTests.cpp
new file mode 100644
index 0000000..5d02ba2
--- /dev/null
+++ b/Runtime/Utilities/WordTests.cpp
@@ -0,0 +1,151 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "Word.h"
+#include "Runtime/Testing/Testing.h"
+
+using namespace std;
+
+SUITE (WordTests)
+{
+ TEST (IntToString_Works)
+ {
+ CHECK (IntToString (123456) == "123456");
+ CHECK (IntToString (-123456) == "-123456");
+ }
+
+ TEST (Int64ToString_Works)
+ {
+ CHECK (Int64ToString (1099511627776) == "1099511627776");
+ CHECK (Int64ToString (-1099511627776) == "-1099511627776");
+ }
+
+ TEST (UnsignedIntToString_Works)
+ {
+ CHECK (IntToString (123456) == "123456");
+ }
+
+ TEST (UnsignedInt64ToString_Works)
+ {
+ CHECK (UnsignedInt64ToString (1099511627776) == "1099511627776");
+ }
+
+ TEST (Word_EndsWith)
+ {
+ CHECK(EndsWith("abc","c"));
+ CHECK(EndsWith("abc","bc"));
+ CHECK(EndsWith("abc","abc"));
+ CHECK(EndsWith("abc",""));
+ CHECK(!EndsWith("abc","d"));
+ CHECK(!EndsWith("abc","abcd"));
+ }
+
+ TEST (Word_IsStringNumber)
+ {
+ CHECK_EQUAL(true, IsStringNumber ("-1"));
+ CHECK_EQUAL(true, IsStringNumber ("+2"));
+ CHECK_EQUAL(false, IsStringNumber ("2+"));
+ CHECK_EQUAL(false, IsStringNumber ("a"));
+ CHECK_EQUAL(false, IsStringNumber ("1b"));
+ }
+
+ TEST (Word_ReplaceString)
+ {
+ string s;
+ s = "foo bar foo"; replace_string (s, "foo", "x"); CHECK_EQUAL("x bar x", s);
+ s = "foo bar foo"; replace_string (s, "", ""); CHECK_EQUAL("foo bar foo", s);
+ }
+
+ TEST (Word_SimpleStringToFloatWorks)
+ {
+ int len;
+ CHECK_EQUAL (0.0f, SimpleStringToFloat("0",&len)); CHECK_EQUAL(1,len);
+ CHECK_EQUAL (0.0f, SimpleStringToFloat("0.0",&len)); CHECK_EQUAL(3,len);
+ CHECK_EQUAL (0.0f, SimpleStringToFloat(".0",&len)); CHECK_EQUAL(2,len);
+ CHECK_EQUAL (12.05f, SimpleStringToFloat("12.05",&len)); CHECK_EQUAL(5,len);
+ CHECK_EQUAL (-3.5f, SimpleStringToFloat("-3.5",&len)); CHECK_EQUAL(4,len);
+ CHECK_EQUAL (3.14f, SimpleStringToFloat("3.14",&len)); CHECK_EQUAL(4,len);
+ CHECK_EQUAL (-1024.5f, SimpleStringToFloat("-1024.500",&len)); CHECK_EQUAL(9,len);
+ }
+
+ TEST (Word_Trim)
+ {
+ string s;
+ s=Trim(" \tspaces in front\n"); CHECK_EQUAL("spaces in front\n",s);
+ s=Trim("spaces behind \t \t\t"); CHECK_EQUAL("spaces behind",s);
+ s=Trim("\t\t\t\tspaces at both ends \t \t\t"); CHECK_EQUAL("spaces at both ends",s);
+ s=Trim(""); CHECK_EQUAL("",s);
+ s=Trim("\t\t\t \t \t"); CHECK_EQUAL("",s);
+ s=Trim("\n\n Custom Whitespace\r\n","\r\n"); CHECK_EQUAL(" Custom Whitespace",s);
+ }
+
+ TEST (Word_Split)
+ {
+ const int kNumtests = 6;
+ const int kMaxtokens = 3;
+ const char splitChar = ';';
+ const char* splitChars = ";/";
+
+ string inputs[kNumtests] =
+ {
+ "Normal;string;split",
+ "Adjacent;;separators",
+ "NoSeparators",
+ "EndWithSeparator;",
+ ";StartWithSeparator",
+ ";" // No non-separators
+ };
+
+ string inputsMulti[kNumtests] =
+ {
+ "Normal;string/split",
+ "Adjacent/;separators",
+ "NoSeparators",
+ "EndWithSeparator;/",
+ ";StartWithSeparator",
+ ";" // No non-separators
+ };
+
+ int outputSizes[kNumtests] =
+ {
+ 3, 2, 1, 1, 1, 0
+ };
+
+ string outputTokens[][kMaxtokens] =
+ {
+ { "Normal", "string", "split" },
+ { "Adjacent", "separators", "" },
+ { "NoSeparators", "", "" },
+ { "EndWithSeparator", "", "" },
+ { "StartWithSeparator", "", "" },
+ { "", "", "" }
+ };
+
+ for (int test = 0; test < kNumtests; ++test)
+ {
+ string s = inputs[test];
+ vector<string> tokens;
+ Split (inputs[test], splitChar, tokens);
+ CHECK_EQUAL (outputSizes[test], tokens.size ()); // Verify number of tokens
+ for (int token = 0; token < outputSizes[test]; ++token)
+ {
+ CHECK_EQUAL (outputTokens[test][token], tokens[token]); // Verify each token
+ }
+ }
+
+ for (int test = 0; test < kNumtests; ++test)
+ {
+ string s = inputs[test];
+ vector<string> tokens;
+ Split (inputsMulti[test], splitChars, tokens);
+ CHECK_EQUAL (outputSizes[test], tokens.size ()); // Verify number of tokens
+ for (int token = 0; token < outputSizes[test]; ++token)
+ {
+ CHECK_EQUAL (outputTokens[test][token], tokens[token]); // Verify each token
+ }
+ }
+ }
+}
+
+#endif // ENABLE_UNIT_TESTS
diff --git a/Runtime/Utilities/algorithm_utility.h b/Runtime/Utilities/algorithm_utility.h
new file mode 100644
index 0000000..1c56bb1
--- /dev/null
+++ b/Runtime/Utilities/algorithm_utility.h
@@ -0,0 +1,115 @@
+#ifndef ALGORITHM_UTILITY_H
+#define ALGORITHM_UTILITY_H
+
+#include <algorithm>
+#include <functional>
+#include "LogAssert.h"
+
+template<class T, class Func>
+void repeat (T& type, int count, Func func)
+{
+ int i;
+ for (i=0;i<count;i++)
+ func (type);
+}
+
+template<class C, class Func>
+Func for_each (C& c, Func f)
+{
+ return for_each (c.begin (), c.end (), f);
+}
+
+template<class C, class Func>
+void erase_if (C& c, Func f)
+{
+ c.erase (remove_if (c.begin (), c.end (), f), c.end ());
+}
+
+template<class C, class T>
+void erase (C& c, const T& t)
+{
+ c.erase (remove (c.begin (), c.end (), t), c.end ());
+}
+
+template<class C, class T>
+typename C::iterator find (C& c, const T& value)
+{
+ return find (c.begin (), c.end (), value);
+}
+
+template<class C, class Pred>
+typename C::iterator find_if (C& c, Pred p)
+{
+ return find_if (c.begin (), c.end (), p);
+}
+
+template <class T, class U>
+struct EqualTo
+ : std::binary_function<T, U, bool>
+{
+ bool operator()(const T& x, const U& y) const { return static_cast<bool>(x == y); }
+};
+
+// Returns the iterator to the last element
+template<class Container>
+typename Container::iterator last_iterator (Container& container)
+{
+ AssertIf (container.begin () == container.end ());
+ typename Container::iterator i = container.end ();
+ i--;
+ return i;
+}
+
+template<class Container>
+typename Container::const_iterator last_iterator (const Container& container)
+{
+ AssertIf (container.begin () == container.end ());
+ typename Container::const_iterator i = container.end ();
+ i--;
+ return i;
+}
+
+
+// Efficient "add or update" for STL maps.
+// For more details see item 24 on Effective STL.
+// Basically it avoids constructing default value only to
+// assign it later.
+template<typename MAP, typename K, typename V>
+bool add_or_update( MAP& m, const K& key, const V& val )
+{
+ typename MAP::iterator lb = m.lower_bound( key );
+ if( lb != m.end() && !m.key_comp()( key, lb->first ) )
+ {
+ // lb points to a pair with the given key, update pair's value
+ lb->second = val;
+ return false;
+ }
+ else
+ {
+ // no key exists, insert new pair
+ m.insert( lb, std::make_pair(key,val) );
+ return true;
+ }
+}
+
+template<class ForwardIterator>
+bool is_sorted (ForwardIterator begin, ForwardIterator end)
+{
+ for (ForwardIterator next = begin; begin != end && ++next != end; ++begin)
+ if (*next < *begin)
+ return false;
+
+ return true;
+}
+
+template<class ForwardIterator, class Predicate>
+bool is_sorted (ForwardIterator begin, ForwardIterator end, Predicate pred)
+{
+ for (ForwardIterator next = begin; begin != end && ++next != end; ++begin)
+ if (pred(*next, *begin))
+ return false;
+
+ return true;
+}
+
+#endif
diff --git a/Runtime/Utilities/delayed_set.h b/Runtime/Utilities/delayed_set.h
new file mode 100644
index 0000000..cdb8fd1
--- /dev/null
+++ b/Runtime/Utilities/delayed_set.h
@@ -0,0 +1,45 @@
+#ifndef DELAYED_SET_H
+#define DELAYED_SET_H
+
+#include <set>
+#include <vector>
+#include "MemoryPool.h"
+
+template <class T, class SetType = std::set<T, std::less<T> , memory_pool<T> > >
+class delayed_set : public SetType {
+ typedef typename std::vector<std::pair<bool, T> > delay_container;
+ delay_container m_Delayed;
+
+ public:
+
+ bool is_inserted (const T& object)
+ {
+ typename delay_container::reverse_iterator i;
+ for (i = m_Delayed.rbegin ();i != m_Delayed.rend ();i++)
+ {
+ if (i->second == object)
+ return i->first;
+ }
+ return this->find (object) != this->end ();
+ }
+
+ void remove_delayed (const T& obj) {
+ m_Delayed.push_back (std::pair<bool, T> (false, obj));
+ }
+ void add_delayed (const T& obj) {
+ m_Delayed.push_back (std::pair<bool, T> (true, obj));
+ }
+ int apply_delayed_size () const { return m_Delayed.size (); }
+ void apply_delayed () {
+ typename delay_container::iterator iter;
+ for (iter = m_Delayed.begin(); iter != m_Delayed.end(); iter++) {
+ if (iter->first)
+ SetType::insert (iter->second);
+ else
+ SetType::erase (iter->second);
+ }
+ m_Delayed.clear ();
+ }
+};
+
+#endif
diff --git a/Runtime/Utilities/dense_hash_map.h b/Runtime/Utilities/dense_hash_map.h
new file mode 100644
index 0000000..a45000e
--- /dev/null
+++ b/Runtime/Utilities/dense_hash_map.h
@@ -0,0 +1,243 @@
+// Copyright (c) 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ----
+// Author: Craig Silverstein
+//
+// This is just a very thin wrapper over densehashtable.h, just
+// like sgi stl's stl_hash_map is a very thin wrapper over
+// stl_hashtable. The major thing we define is operator[], because
+// we have a concept of a data_type which stl_hashtable doesn't
+// (it only has a key and a value).
+//
+// NOTE: this is exactly like sparse_hash_map.h, with the word
+// "sparse" replaced by "dense", except for the addition of
+// set_empty_key().
+//
+// YOU MUST CALL SET_EMPTY_KEY() IMMEDIATELY AFTER CONSTRUCTION.
+//
+// Otherwise your program will die in mysterious ways.
+//
+// In other respects, we adhere mostly to the STL semantics for
+// hash-map. One important exception is that insert() invalidates
+// iterators entirely. On the plus side, though, erase() doesn't
+// invalidate iterators at all, or even change the ordering of elements.
+//
+// Here are a few "power user" tips:
+//
+// 1) set_deleted_key():
+// If you want to use erase() you must call set_deleted_key(),
+// in addition to set_empty_key(), after construction.
+// The deleted and empty keys must differ.
+//
+// 2) resize(0):
+// When an item is deleted, its memory isn't freed right
+// away. This allows you to iterate over a hashtable,
+// and call erase(), without invalidating the iterator.
+// To force the memory to be freed, call resize(0).
+//
+// Guide to what kind of hash_map to use:
+// (1) dense_hash_map: fastest, uses the most memory
+// (2) sparse_hash_map: slowest, uses the least memory
+// (3) hash_map (STL): in the middle
+// Typically I use sparse_hash_map when I care about space and/or when
+// I need to save the hashtable on disk. I use hash_map otherwise. I
+// don't personally use dense_hash_map ever; the only use of
+// dense_hash_map I know of is to work around malloc() bugs in some
+// systems (dense_hash_map has a particularly simple allocation scheme).
+//
+// - dense_hash_map has, typically, a factor of 2 memory overhead (if your
+// data takes up X bytes, the hash_map uses X more bytes in overhead).
+// - sparse_hash_map has about 2 bits overhead per entry.
+// - sparse_hash_map can be 3-7 times slower than the others for lookup and,
+// especially, inserts. See time_hash_map.cc for details.
+//
+// See /usr/(local/)?doc/sparsehash-0.1/dense_hash_map.html
+// for information about how to use this class.
+
+#ifndef _DENSE_HASH_MAP_H_
+#define _DENSE_HASH_MAP_H_
+
+//#include <google/sparsehash/sparseconfig.h>
+#include <stdio.h> // for FILE * in read()/write()
+#include <algorithm> // for the default template args
+#include <functional> // for equal_to
+#include <memory> // for alloc<>
+#include <utility> // for pair<>
+//#include <ext/hash_fun.h> // defined in config.h
+#include "densehashtable.h"
+
+
+using std::pair;
+
+template <class Key, class T,
+ class HashFcn,
+ class EqualKey = std::equal_to<Key>,
+ class Alloc = std::allocator< std::pair<const Key, T> > >
+class dense_hash_map {
+ private:
+ // Apparently select1st is not stl-standard, so we define our own
+ struct SelectKey {
+ const Key& operator()(const pair<const Key, T>& p) const {
+ return p.first;
+ }
+ };
+
+ // The actual data
+ typedef dense_hashtable<pair<const Key, T>, Key, HashFcn,
+ SelectKey, EqualKey, Alloc> ht;
+ ht rep;
+
+ public:
+ typedef typename ht::key_type key_type;
+ typedef T data_type;
+ typedef T mapped_type;
+ typedef typename ht::value_type value_type;
+ typedef typename ht::hasher hasher;
+ typedef typename ht::key_equal key_equal;
+
+ typedef typename ht::size_type size_type;
+ typedef typename ht::difference_type difference_type;
+ typedef typename ht::pointer pointer;
+ typedef typename ht::const_pointer const_pointer;
+ typedef typename ht::reference reference;
+ typedef typename ht::const_reference const_reference;
+
+ typedef typename ht::iterator iterator;
+ typedef typename ht::const_iterator const_iterator;
+
+ // Iterator functions
+ iterator begin() { return rep.begin(); }
+ iterator end() { return rep.end(); }
+ const_iterator begin() const { return rep.begin(); }
+ const_iterator end() const { return rep.end(); }
+
+
+ // Accessor functions
+ hasher hash_funct() const { return rep.hash_funct(); }
+ key_equal key_eq() const { return rep.key_eq(); }
+
+
+ // Constructors
+ explicit dense_hash_map(size_type n = 0,
+ const hasher& hf = hasher(),
+ const key_equal& eql = key_equal())
+ : rep(n, hf, eql) { }
+
+ template <class InputIterator>
+ dense_hash_map(InputIterator f, InputIterator l,
+ size_type n = 0,
+ const hasher& hf = hasher(),
+ const key_equal& eql = key_equal())
+ : rep(n, hf, eql) {
+ rep.insert(f, l);
+ }
+ // We use the default copy constructor
+ // We use the default operator=()
+ // We use the default destructor
+
+ void clear() { rep.clear(); }
+ // This clears the hash map without resizing it down to the minimum
+ // bucket count, but rather keeps the number of buckets constant
+ void clear_no_resize() { rep.clear_no_resize(); }
+ void swap(dense_hash_map& hs) { rep.swap(hs.rep); }
+
+
+ // Functions concerning size
+ size_type size() const { return rep.size(); }
+ size_type max_size() const { return rep.max_size(); }
+ bool empty() const { return rep.empty(); }
+ size_type bucket_count() const { return rep.bucket_count(); }
+ size_type max_bucket_count() const { return rep.max_bucket_count(); }
+
+ void resize(size_type hint) { rep.resize(hint); }
+
+
+ // Lookup routines
+ iterator find(const key_type& key) { return rep.find(key); }
+ const_iterator find(const key_type& key) const { return rep.find(key); }
+
+ data_type& operator[](const key_type& key) { // This is our value-add!
+ iterator it = find(key);
+ if (it != end()) {
+ return it->second;
+ } else {
+ return insert(value_type(key, data_type())).first->second;
+ }
+ }
+
+ size_type count(const key_type& key) const { return rep.count(key); }
+
+ pair<iterator, iterator> equal_range(const key_type& key) {
+ return rep.equal_range(key);
+ }
+ pair<const_iterator, const_iterator> equal_range(const key_type& key) const {
+ return rep.equal_range(key);
+ }
+
+ // Insertion routines
+ pair<iterator, bool> insert(const value_type& obj) { return rep.insert(obj); }
+ template <class InputIterator>
+ void insert(InputIterator f, InputIterator l) { rep.insert(f, l); }
+ void insert(const_iterator f, const_iterator l) { rep.insert(f, l); }
+ // required for std::insert_iterator; the passed-in iterator is ignored
+ iterator insert(iterator, const value_type& obj) { return insert(obj).first; }
+
+
+ // Deletion and empty routines
+ // THESE ARE NON-STANDARD! I make you specify an "impossible" key
+ // value to identify deleted and empty buckets. You can change the
+ // deleted key as time goes on, or get rid of it entirely to be insert-only.
+ void set_empty_key(const key_type& key) { // YOU MUST CALL THIS!
+ rep.set_empty_key(value_type(key, data_type())); // rep wants a value
+ }
+ void set_deleted_key(const key_type& key) {
+ rep.set_deleted_key(value_type(key, data_type())); // rep wants a value
+ }
+ void clear_deleted_key() { rep.clear_deleted_key(); }
+
+ // These are standard
+ size_type erase(const key_type& key) { return rep.erase(key); }
+ void erase(iterator it) { rep.erase(it); }
+ void erase(iterator f, iterator l) { rep.erase(f, l); }
+
+
+ // Comparison
+ bool operator==(const dense_hash_map& hs) const { return rep == hs.rep; }
+ bool operator!=(const dense_hash_map& hs) const { return rep != hs.rep; }
+};
+
+// We need a global swap as well
+template <class Key, class T, class HashFcn, class EqualKey, class Alloc>
+inline void swap(dense_hash_map<Key, T, HashFcn, EqualKey, Alloc>& hm1,
+ dense_hash_map<Key, T, HashFcn, EqualKey, Alloc>& hm2) {
+ hm1.swap(hm2);
+}
+
+#endif /* _DENSE_HASH_MAP_H_ */
diff --git a/Runtime/Utilities/densehashtable.h b/Runtime/Utilities/densehashtable.h
new file mode 100644
index 0000000..763bce1
--- /dev/null
+++ b/Runtime/Utilities/densehashtable.h
@@ -0,0 +1,899 @@
+// Copyright (c) 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ---
+// Author: Craig Silverstein
+//
+// A dense hashtable is a particular implementation of
+// a hashtable: one that is meant to minimize memory allocation.
+// It does this by using an array to store all the data. We
+// steal a value from the key space to indicate "empty" array
+// elements (ie indices where no item lives) and another to indicate
+// "deleted" elements.
+//
+// (Note it is possible to change the value of the delete key
+// on the fly; you can even remove it, though after that point
+// the hashtable is insert_only until you set it again. The empty
+// value however can't be changed.)
+//
+// To minimize allocation and pointer overhead, we use internal
+// probing, in which the hashtable is a single table, and collisions
+// are resolved by trying to insert again in another bucket. The
+// most cache-efficient internal probing schemes are linear probing
+// (which suffers, alas, from clumping) and quadratic probing, which
+// is what we implement by default.
+//
+// Type requirements: value_type is required to be Copy Constructible
+// and Default Constructible. It is not required to be (and commonly
+// isn't) Assignable.
+//
+// You probably shouldn't use this code directly. Use
+// <google/dense_hash_map> or <google/dense_hash_set> instead.
+
+// You can change the following below:
+// HT_OCCUPANCY_FLT -- how full before we double size
+// HT_EMPTY_FLT -- how empty before we halve size
+// HT_MIN_BUCKETS -- default smallest bucket size
+//
+// How to decide what values to use?
+// HT_EMPTY_FLT's default of .4 * OCCUPANCY_FLT, is probably good.
+// HT_MIN_BUCKETS is probably unnecessary since you can specify
+// (indirectly) the starting number of buckets at construct-time.
+// For HT_OCCUPANCY_FLT, you can use this chart to try to trade-off
+// expected lookup time to the space taken up. By default, this
+// code uses quadratic probing, though you can change it to linear
+// via _JUMP below if you really want to.
+//
+// From http://www.augustana.ca/~mohrj/courses/1999.fall/csc210/lecture_notes/hashing.html
+// NUMBER OF PROBES / LOOKUP Successful Unsuccessful
+// Quadratic collision resolution 1 - ln(1-L) - L/2 1/(1-L) - L - ln(1-L)
+// Linear collision resolution [1+1/(1-L)]/2 [1+1/(1-L)2]/2
+//
+// -- HT_OCCUPANCY_FLT -- 0.10 0.50 0.60 0.75 0.80 0.90 0.99
+// QUADRATIC COLLISION RES.
+// probes/successful lookup 1.05 1.44 1.62 2.01 2.21 2.85 5.11
+// probes/unsuccessful lookup 1.11 2.19 2.82 4.64 5.81 11.4 103.6
+// LINEAR COLLISION RES.
+// probes/successful lookup 1.06 1.5 1.75 2.5 3.0 5.5 50.5
+// probes/unsuccessful lookup 1.12 2.5 3.6 8.5 13.0 50.0 5000.0
+
+#ifndef _DENSEHASHTABLE_H_
+#define _DENSEHASHTABLE_H_
+
+// The probing method
+// Linear probing
+// #define JUMP_(key, num_probes) ( 1 )
+// Quadratic-ish probing
+#define JUMP_(key, num_probes) ( num_probes )
+
+
+// Hashtable class, used to implement the hashed associative containers
+// hash_set and hash_map.
+
+//#include <google/sparsehash/sparseconfig.h>
+//#include <Assert.h>
+#include "LogAssert.h"
+#include <stdlib.h> // for abort()
+#include <algorithm> // For swap(), eg
+#include <iostream> // For cerr
+#include <memory> // For uninitialized_fill, uninitialized_copy
+#include <utility> // for pair<>
+#include <iterator> // for facts about iterator tags
+#include "type_traits.h" // for true_type, integral_constant, etc.
+
+using std::pair;
+
+template <class Value, class Key, class HashFcn,
+ class ExtractKey, class EqualKey, class Alloc>
+class dense_hashtable;
+
+template <class V, class K, class HF, class ExK, class EqK, class A>
+struct dense_hashtable_iterator;
+
+template <class V, class K, class HF, class ExK, class EqK, class A>
+struct dense_hashtable_const_iterator;
+
+// We're just an array, but we need to skip over empty and deleted elements
+template <class V, class K, class HF, class ExK, class EqK, class A>
+struct dense_hashtable_iterator {
+ public:
+ typedef dense_hashtable_iterator<V,K,HF,ExK,EqK,A> iterator;
+ typedef dense_hashtable_const_iterator<V,K,HF,ExK,EqK,A> const_iterator;
+
+ typedef std::forward_iterator_tag iterator_category;
+ typedef V value_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef size_t size_type;
+ typedef V& reference; // Value
+ typedef V* pointer;
+
+ // "Real" constructor and default constructor
+ dense_hashtable_iterator(const dense_hashtable<V,K,HF,ExK,EqK,A> *h,
+ pointer it, pointer it_end, bool advance)
+ : ht(h), pos(it), end(it_end) {
+ if (advance) advance_past_empty_and_deleted();
+ }
+ dense_hashtable_iterator() { }
+ // The default destructor is fine; we don't define one
+ // The default operator= is fine; we don't define one
+
+ // Happy dereferencer
+ reference operator*() const { return *pos; }
+ pointer operator->() const { return &(operator*()); }
+
+ // Arithmetic. The only hard part is making sure that
+ // we're not on an empty or marked-deleted array element
+ void advance_past_empty_and_deleted() {
+ while ( pos != end && (ht->test_empty(*this) || ht->test_deleted(*this)) )
+ ++pos;
+ }
+ iterator& operator++() {
+ Assert(pos != end); ++pos; advance_past_empty_and_deleted(); return *this;
+ }
+ iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; }
+
+ // Comparison.
+ bool operator==(const iterator& it) const { return pos == it.pos; }
+ bool operator!=(const iterator& it) const { return pos != it.pos; }
+
+
+ // The actual data
+ const dense_hashtable<V,K,HF,ExK,EqK,A> *ht;
+ pointer pos, end;
+};
+
+
+// Now do it all again, but with const-ness!
+template <class V, class K, class HF, class ExK, class EqK, class A>
+struct dense_hashtable_const_iterator {
+ public:
+ typedef dense_hashtable_iterator<V,K,HF,ExK,EqK,A> iterator;
+ typedef dense_hashtable_const_iterator<V,K,HF,ExK,EqK,A> const_iterator;
+
+ typedef std::forward_iterator_tag iterator_category;
+ typedef V value_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef size_t size_type;
+ typedef const V& reference; // Value
+ typedef const V* pointer;
+
+ // "Real" constructor and default constructor
+ dense_hashtable_const_iterator(const dense_hashtable<V,K,HF,ExK,EqK,A> *h,
+ pointer it, pointer it_end, bool advance)
+ : ht(h), pos(it), end(it_end) {
+ if (advance) advance_past_empty_and_deleted();
+ }
+ dense_hashtable_const_iterator() { }
+ // This lets us convert regular iterators to const iterators
+ dense_hashtable_const_iterator(const iterator &it)
+ : ht(it.ht), pos(it.pos), end(it.end) { }
+ // The default destructor is fine; we don't define one
+ // The default operator= is fine; we don't define one
+
+ // Happy dereferencer
+ reference operator*() const { return *pos; }
+ pointer operator->() const { return &(operator*()); }
+
+ // Arithmetic. The only hard part is making sure that
+ // we're not on an empty or marked-deleted array element
+ void advance_past_empty_and_deleted() {
+ while ( pos != end && (ht->test_empty(*this) || ht->test_deleted(*this)) )
+ ++pos;
+ }
+ const_iterator& operator++() {
+ Assert(pos != end); ++pos; advance_past_empty_and_deleted(); return *this;
+ }
+ const_iterator operator++(int) { const_iterator tmp(*this); ++*this; return tmp; }
+
+ // Comparison.
+ bool operator==(const const_iterator& it) const { return pos == it.pos; }
+ bool operator!=(const const_iterator& it) const { return pos != it.pos; }
+
+
+ // The actual data
+ const dense_hashtable<V,K,HF,ExK,EqK,A> *ht;
+ pointer pos, end;
+};
+
+template <class Value, class Key, class HashFcn,
+ class ExtractKey, class EqualKey, class Alloc>
+class dense_hashtable {
+ public:
+ typedef Key key_type;
+ typedef Value value_type;
+ typedef HashFcn hasher;
+ typedef EqualKey key_equal;
+
+ typedef size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef dense_hashtable_iterator<Value, Key, HashFcn,
+ ExtractKey, EqualKey, Alloc>
+ iterator;
+
+ typedef dense_hashtable_const_iterator<Value, Key, HashFcn,
+ ExtractKey, EqualKey, Alloc>
+ const_iterator;
+
+ // How full we let the table get before we resize. Knuth says .8 is
+ // good -- higher causes us to probe too much, though saves memory
+ static const float HT_OCCUPANCY_FLT; // = 0.8;
+
+ // How empty we let the table get before we resize lower.
+ // (0.0 means never resize lower.)
+ // It should be less than OCCUPANCY_FLT / 2 or we thrash resizing
+ static const float HT_EMPTY_FLT; // = 0.4 * HT_OCCUPANCY_FLT
+
+ // Minimum size we're willing to let hashtables be.
+ // Must be a power of two, and at least 4.
+ // Note, however, that for a given hashtable, the initial size is a
+ // function of the first constructor arg, and may be >HT_MIN_BUCKETS.
+ static const size_t HT_MIN_BUCKETS = 32;
+
+
+ // ITERATOR FUNCTIONS
+ iterator begin() { return iterator(this, table,
+ table + num_buckets, true); }
+ iterator end() { return iterator(this, table + num_buckets,
+ table + num_buckets, true); }
+ const_iterator begin() const { return const_iterator(this, table,
+ table+num_buckets,true);}
+ const_iterator end() const { return const_iterator(this, table + num_buckets,
+ table+num_buckets,true);}
+
+ // ACCESSOR FUNCTIONS for the things we templatize on, basically
+ hasher hash_funct() const { return hash; }
+ key_equal key_eq() const { return equals; }
+
+ // Annoyingly, we can't copy values around, because they might have
+ // const components (they're probably pair<const X, Y>). We use
+ // explicit destructor invocation and placement new to get around
+ // this. Arg.
+ private:
+ void set_value(value_type* dst, const value_type& src) {
+ dst->~value_type();
+ new(dst) value_type(src);
+ }
+
+ void destroy_buckets(size_type first, size_type last) {
+ for ( ; first != last; ++first)
+ table[first].~value_type();
+ }
+
+ // DELETE HELPER FUNCTIONS
+ // This lets the user describe a key that will indicate deleted
+ // table entries. This key should be an "impossible" entry --
+ // if you try to insert it for real, you won't be able to retrieve it!
+ // (NB: while you pass in an entire value, only the key part is looked
+ // at. This is just because I don't know how to assign just a key.)
+ private:
+ void squash_deleted() { // gets rid of any deleted entries we have
+ if ( num_deleted ) { // get rid of deleted before writing
+ dense_hashtable tmp(*this); // copying will get rid of deleted
+ swap(tmp); // now we are tmp
+ }
+ Assert(num_deleted == 0);
+ }
+
+ public:
+ void set_deleted_key(const value_type &val) {
+ // the empty indicator (if specified) and the deleted indicator
+ // must be different
+ Assert(!use_empty || !equals(get_key(val), get_key(emptyval)));
+ // It's only safe to change what "deleted" means if we purge deleted guys
+ squash_deleted();
+ use_deleted = true;
+ set_value(&delval, val);
+ }
+ void clear_deleted_key() {
+ squash_deleted();
+ use_deleted = false;
+ }
+
+ // These are public so the iterators can use them
+ // True if the item at position bucknum is "deleted" marker
+ bool test_deleted(size_type bucknum) const {
+ // The num_deleted test is crucial for read(): after read(), the ht values
+ // are garbage, and we don't want to think some of them are deleted.
+ return (use_deleted && num_deleted > 0 &&
+ equals(get_key(delval), get_key(table[bucknum])));
+ }
+ bool test_deleted(const iterator &it) const {
+ return (use_deleted && num_deleted > 0 &&
+ equals(get_key(delval), get_key(*it)));
+ }
+ bool test_deleted(const const_iterator &it) const {
+ return (use_deleted && num_deleted > 0 &&
+ equals(get_key(delval), get_key(*it)));
+ }
+ // Set it so test_deleted is true. true if object didn't used to be deleted
+ // See below (at erase()) to explain why we allow const_iterators
+ bool set_deleted(const_iterator &it) {
+ Assert(use_deleted); // bad if set_deleted_key() wasn't called
+ bool retval = !test_deleted(it);
+ // &* converts from iterator to value-type
+ set_value(const_cast<value_type*>(&(*it)), delval);
+ return retval;
+ }
+ // Set it so test_deleted is false. true if object used to be deleted
+ bool clear_deleted(const_iterator &it) {
+ Assert(use_deleted); // bad if set_deleted_key() wasn't called
+ // happens automatically when we assign something else in its place
+ return test_deleted(it);
+ }
+
+ // EMPTY HELPER FUNCTIONS
+ // This lets the user describe a key that will indicate empty (unused)
+ // table entries. This key should be an "impossible" entry --
+ // if you try to insert it for real, you won't be able to retrieve it!
+ // (NB: while you pass in an entire value, only the key part is looked
+ // at. This is just because I don't know how to assign just a key.)
+ public:
+ // These are public so the iterators can use them
+ // True if the item at position bucknum is "empty" marker
+ bool test_empty(size_type bucknum) const {
+ Assert(use_empty); // we always need to know what's empty!
+ return equals(get_key(emptyval), get_key(table[bucknum]));
+ }
+ bool test_empty(const iterator &it) const {
+ Assert(use_empty); // we always need to know what's empty!
+ return equals(get_key(emptyval), get_key(*it));
+ }
+ bool test_empty(const const_iterator &it) const {
+ Assert(use_empty); // we always need to know what's empty!
+ return equals(get_key(emptyval), get_key(*it));
+ }
+
+ private:
+ // You can either set a range empty or an individual element
+ void set_empty(size_type bucknum) {
+ Assert(use_empty);
+ set_value(&table[bucknum], emptyval);
+ }
+ void fill_range_with_empty(value_type* table_start, value_type* table_end) {
+ // Like set_empty(range), but doesn't destroy previous contents
+ std::uninitialized_fill(table_start, table_end, emptyval);
+ }
+ void set_empty(size_type buckstart, size_type buckend) {
+ Assert(use_empty);
+ destroy_buckets(buckstart, buckend);
+ fill_range_with_empty(table + buckstart, table + buckend);
+ }
+
+ public:
+ // TODO(csilvers): change all callers of this to pass in a key instead,
+ // and take a const key_type instead of const value_type.
+ void set_empty_key(const value_type &val) {
+ // Once you set the empty key, you can't change it
+ Assert(!use_empty);
+ // The deleted indicator (if specified) and the empty indicator
+ // must be different.
+ Assert(!use_deleted || !equals(get_key(val), get_key(delval)));
+ use_empty = true;
+ set_value(&emptyval, val);
+
+ Assert(!table); // must set before first use
+ // num_buckets was set in constructor even though table was NULL
+ table = _Alval.allocate(num_buckets);
+ Assert(table);
+ fill_range_with_empty(table, table + num_buckets);
+ }
+
+ // FUNCTIONS CONCERNING SIZE
+ public:
+ size_type size() const { return num_elements - num_deleted; }
+ // Buckets are always a power of 2
+ size_type max_size() const { return (size_type(-1) >> 1U) + 1; }
+ bool empty() const { return size() == 0; }
+ size_type bucket_count() const { return num_buckets; }
+ size_type max_bucket_count() const { return max_size(); }
+ size_type nonempty_bucket_count() const { return num_elements; }
+
+ private:
+ // Because of the above, size_type(-1) is never legal; use it for errors
+ static const size_type ILLEGAL_BUCKET = size_type(-1);
+
+ private:
+ // This is the smallest size a hashtable can be without being too crowded
+ // If you like, you can give a min #buckets as well as a min #elts
+ size_type min_size(size_type num_elts, size_type min_buckets_wanted) {
+ size_type sz = HT_MIN_BUCKETS; // min buckets allowed
+ while ( sz < min_buckets_wanted || num_elts >= sz * HT_OCCUPANCY_FLT )
+ sz *= 2;
+ return sz;
+ }
+
+ // Used after a string of deletes
+ void maybe_shrink() {
+ Assert(num_elements >= num_deleted);
+ Assert((bucket_count() & (bucket_count()-1)) == 0); // is a power of two
+ Assert(bucket_count() >= HT_MIN_BUCKETS);
+
+ if ( (num_elements-num_deleted) < shrink_threshold &&
+ bucket_count() > HT_MIN_BUCKETS ) {
+ size_type sz = bucket_count() / 2; // find how much we should shrink
+ while ( sz > HT_MIN_BUCKETS &&
+ (num_elements - num_deleted) < sz * HT_EMPTY_FLT )
+ sz /= 2; // stay a power of 2
+ dense_hashtable tmp(*this, sz); // Do the actual resizing
+ swap(tmp); // now we are tmp
+ }
+ consider_shrink = false; // because we just considered it
+ }
+
+ // We'll let you resize a hashtable -- though this makes us copy all!
+ // When you resize, you say, "make it big enough for this many more elements"
+ void resize_delta(size_type delta, size_type min_buckets_wanted = 0) {
+ if ( consider_shrink ) // see if lots of deletes happened
+ maybe_shrink();
+ if ( bucket_count() > min_buckets_wanted &&
+ (num_elements + delta) <= enlarge_threshold )
+ return; // we're ok as we are
+
+ // Sometimes, we need to resize just to get rid of all the
+ // "deleted" buckets that are clogging up the hashtable. So when
+ // deciding whether to resize, count the deleted buckets (which
+ // are currently taking up room). But later, when we decide what
+ // size to resize to, *don't* count deleted buckets, since they
+ // get discarded during the resize.
+ const size_type needed_size = min_size(num_elements + delta,
+ min_buckets_wanted);
+ if ( needed_size > bucket_count() ) { // we don't have enough buckets
+ const size_type resize_to = min_size(num_elements - num_deleted + delta,
+ min_buckets_wanted);
+ dense_hashtable tmp(*this, resize_to);
+ swap(tmp); // now we are tmp
+ }
+ }
+
+ // Increase number of buckets, assuming value_type has trivial copy
+ // constructor and destructor. (Really, we want it to have "trivial
+ // move", because that's what realloc does. But there's no way to
+ // capture that using type_traits, so we pretend that move(x, y) is
+ // equivalent to "x.~T(); new(x) T(y);" which is pretty much
+ // correct, if a bit conservative.)
+ void expand_array(size_t resize_to, dense_hash_map_traits::true_type) {
+ value_type* new_table = _Alval.allocate(resize_to);
+ Assert(new_table);
+ if(table)
+ {
+ std::uninitialized_copy(table, table + num_buckets, new_table);
+ _Alval.deallocate(table, num_buckets);
+ }
+ fill_range_with_empty(new_table + num_buckets, new_table + resize_to);
+ table = new_table;
+ Assert(table);
+ }
+
+ // Increase number of buckets, without special assumptions about value_type.
+ // TODO(austern): make this exception safe. Handle exceptions from
+ // value_type's copy constructor.
+ void expand_array(size_t resize_to, dense_hash_map_traits::false_type) {
+ value_type* new_table = _Alval.allocate(resize_to);
+ Assert(new_table);
+ std::uninitialized_copy(table, table + std::min(num_buckets,resize_to), new_table);
+ fill_range_with_empty(new_table + num_buckets, new_table + resize_to);
+ destroy_buckets(0, num_buckets);
+ _Alval.deallocate(table, num_buckets);
+ table = new_table;
+ }
+
+ // Used to actually do the rehashing when we grow/shrink a hashtable
+ void copy_from(const dense_hashtable &ht, size_type min_buckets_wanted = 0) {
+ clear(); // clear table, set num_deleted to 0
+
+ // If we need to change the size of our table, do it now
+ const size_type resize_to = min_size(ht.size(), min_buckets_wanted);
+ if ( resize_to > bucket_count() ) { // we don't have enough buckets
+ typedef dense_hash_map_traits::integral_constant<bool,
+ (dense_hash_map_traits::has_trivial_copy<value_type>::value &&
+ dense_hash_map_traits::has_trivial_destructor<value_type>::value)>
+ realloc_ok; // we pretend mv(x,y) == "x.~T(); new(x) T(y)"
+ expand_array(resize_to, realloc_ok());
+ num_buckets = resize_to;
+ reset_thresholds();
+ }
+
+ // We use a normal iterator to get non-deleted bcks from ht
+ // We could use insert() here, but since we know there are
+ // no duplicates and no deleted items, we can be more efficient
+ Assert((bucket_count() & (bucket_count()-1)) == 0); // a power of two
+ for ( const_iterator it = ht.begin(); it != ht.end(); ++it ) {
+ size_type num_probes = 0; // how many times we've probed
+ size_type bucknum;
+ const size_type bucket_count_minus_one = bucket_count() - 1;
+ for (bucknum = hash(get_key(*it)) & bucket_count_minus_one;
+ !test_empty(bucknum); // not empty
+ bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one) {
+ ++num_probes;
+ Assert(num_probes < bucket_count()); // or else the hashtable is full
+ }
+ set_value(&table[bucknum], *it); // copies the value to here
+ num_elements++;
+ }
+ }
+
+ // Required by the spec for hashed associative container
+ public:
+ // Though the docs say this should be num_buckets, I think it's much
+ // more useful as req_elements. As a special feature, calling with
+ // req_elements==0 will cause us to shrink if we can, saving space.
+ void resize(size_type req_elements) { // resize to this or larger
+ if ( consider_shrink || req_elements == 0 )
+ maybe_shrink();
+ if ( req_elements > num_elements )
+ return resize_delta(req_elements - num_elements, 0);
+ }
+
+
+ // CONSTRUCTORS -- as required by the specs, we take a size,
+ // but also let you specify a hashfunction, key comparator,
+ // and key extractor. We also define a copy constructor and =.
+ // DESTRUCTOR -- needs to free the table
+ explicit dense_hashtable(size_type n = 0,
+ const HashFcn& hf = HashFcn(),
+ const EqualKey& eql = EqualKey(),
+ const ExtractKey& ext = ExtractKey())
+ : hash(hf), equals(eql), get_key(ext), num_deleted(0),
+ use_deleted(false), use_empty(false),
+ delval(), emptyval(),
+ table(NULL), num_buckets(min_size(0, n)), num_elements(0) {
+ // table is NULL until emptyval is set. However, we set num_buckets
+ // here so we know how much space to allocate once emptyval is set
+ reset_thresholds();
+ }
+
+ // As a convenience for resize(), we allow an optional second argument
+ // which lets you make this new hashtable a different size than ht
+ dense_hashtable(const dense_hashtable& ht, size_type min_buckets_wanted = 0)
+ : hash(ht.hash), equals(ht.equals), get_key(ht.get_key), num_deleted(0),
+ use_deleted(ht.use_deleted), use_empty(ht.use_empty),
+ delval(ht.delval), emptyval(ht.emptyval),
+ table(NULL), num_buckets(0),
+ num_elements(0) {
+ reset_thresholds();
+ copy_from(ht, min_buckets_wanted); // copy_from() ignores deleted entries
+ }
+
+ dense_hashtable& operator= (const dense_hashtable& ht) {
+ if (&ht == this) return *this; // don't copy onto ourselves
+ clear();
+ hash = ht.hash;
+ equals = ht.equals;
+ get_key = ht.get_key;
+ use_deleted = ht.use_deleted;
+ use_empty = ht.use_empty;
+ set_value(&delval, ht.delval);
+ set_value(&emptyval, ht.emptyval);
+ copy_from(ht); // sets num_deleted to 0 too
+ return *this;
+ }
+
+ ~dense_hashtable() {
+ if (table) {
+ destroy_buckets(0, num_buckets);
+ _Alval.deallocate(table, num_buckets);
+ }
+ }
+
+ // Many STL algorithms use swap instead of copy constructors
+ void swap(dense_hashtable& ht) {
+ std::swap(hash, ht.hash);
+ std::swap(equals, ht.equals);
+ std::swap(get_key, ht.get_key);
+ std::swap(num_deleted, ht.num_deleted);
+ std::swap(use_deleted, ht.use_deleted);
+ std::swap(use_empty, ht.use_empty);
+ { value_type tmp; // for annoying reasons, swap() doesn't work
+ set_value(&tmp, delval);
+ set_value(&delval, ht.delval);
+ set_value(&ht.delval, tmp);
+ }
+ { value_type tmp; // for annoying reasons, swap() doesn't work
+ set_value(&tmp, emptyval);
+ set_value(&emptyval, ht.emptyval);
+ set_value(&ht.emptyval, tmp);
+ }
+ std::swap(table, ht.table);
+ std::swap(num_buckets, ht.num_buckets);
+ std::swap(num_elements, ht.num_elements);
+ reset_thresholds();
+ ht.reset_thresholds();
+ }
+
+ // It's always nice to be able to clear a table without deallocating it
+ void clear() {
+ if (table)
+ destroy_buckets(0, num_buckets);
+
+ size_type old_bucket_count = num_buckets;
+ num_buckets = min_size(0,0); // our new size
+ reset_thresholds();
+ value_type* new_table = _Alval.allocate(num_buckets);
+ Assert(new_table);
+ if(table)
+ _Alval.deallocate(table, old_bucket_count);
+ table = new_table;
+ Assert(table);
+ fill_range_with_empty(table, table + num_buckets);
+ num_elements = 0;
+ num_deleted = 0;
+ }
+
+ // Clear the table without resizing it.
+ // Mimicks the stl_hashtable's behaviour when clear()-ing in that it
+ // does not modify the bucket count
+ void clear_no_resize() {
+ if (table) {
+ set_empty(0, num_buckets);
+ }
+ // don't consider to shrink before another erase()
+ reset_thresholds();
+ num_elements = 0;
+ num_deleted = 0;
+ }
+
+ // LOOKUP ROUTINES
+ private:
+ // Returns a pair of positions: 1st where the object is, 2nd where
+ // it would go if you wanted to insert it. 1st is ILLEGAL_BUCKET
+ // if object is not found; 2nd is ILLEGAL_BUCKET if it is.
+ // Note: because of deletions where-to-insert is not trivial: it's the
+ // first deleted bucket we see, as long as we don't find the key later
+ pair<size_type, size_type> find_position(const key_type &key) const {
+ size_type num_probes = 0; // how many times we've probed
+ const size_type bucket_count_minus_one = bucket_count() - 1;
+ size_type bucknum = hash(key) & bucket_count_minus_one;
+ size_type insert_pos = ILLEGAL_BUCKET; // where we would insert
+ while ( 1 ) { // probe until something happens
+ if ( test_empty(bucknum) ) { // bucket is empty
+ if ( insert_pos == ILLEGAL_BUCKET ) // found no prior place to insert
+ return pair<size_type,size_type>(ILLEGAL_BUCKET, bucknum);
+ else
+ return pair<size_type,size_type>(ILLEGAL_BUCKET, insert_pos);
+
+ } else if ( test_deleted(bucknum) ) {// keep searching, but mark to insert
+ if ( insert_pos == ILLEGAL_BUCKET )
+ insert_pos = bucknum;
+
+ } else if ( equals(key, get_key(table[bucknum])) ) {
+ return pair<size_type,size_type>(bucknum, ILLEGAL_BUCKET);
+ }
+ ++num_probes; // we're doing another probe
+ bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one;
+ Assert(num_probes < bucket_count()); // don't probe too many times!
+ }
+ }
+
+ public:
+ iterator find(const key_type& key) {
+ if ( size() == 0 ) return end();
+ pair<size_type, size_type> pos = find_position(key);
+ if ( pos.first == ILLEGAL_BUCKET ) // alas, not there
+ return end();
+ else
+ return iterator(this, table + pos.first, table + num_buckets, false);
+ }
+
+ const_iterator find(const key_type& key) const {
+ if ( size() == 0 ) return end();
+ pair<size_type, size_type> pos = find_position(key);
+ if ( pos.first == ILLEGAL_BUCKET ) // alas, not there
+ return end();
+ else
+ return const_iterator(this, table + pos.first, table+num_buckets, false);
+ }
+
+ // Counts how many elements have key key. For maps, it's either 0 or 1.
+ size_type count(const key_type &key) const {
+ pair<size_type, size_type> pos = find_position(key);
+ return pos.first == ILLEGAL_BUCKET ? 0 : 1;
+ }
+
+ // Likewise, equal_range doesn't really make sense for us. Oh well.
+ pair<iterator,iterator> equal_range(const key_type& key) {
+ const iterator pos = find(key); // either an iterator or end
+ return pair<iterator,iterator>(pos, pos);
+ }
+ pair<const_iterator,const_iterator> equal_range(const key_type& key) const {
+ const const_iterator pos = find(key); // either an iterator or end
+ return pair<iterator,iterator>(pos, pos);
+ }
+
+
+ // INSERTION ROUTINES
+ private:
+ // If you know *this is big enough to hold obj, use this routine
+ pair<iterator, bool> insert_noresize(const value_type& obj) {
+ const pair<size_type,size_type> pos = find_position(get_key(obj));
+ if ( pos.first != ILLEGAL_BUCKET) { // object was already there
+ return pair<iterator,bool>(iterator(this, table + pos.first,
+ table + num_buckets, false),
+ false); // false: we didn't insert
+ } else { // pos.second says where to put it
+ if ( test_deleted(pos.second) ) { // just replace if it's been del.
+ const_iterator delpos(this, table + pos.second, // shrug:
+ table + num_buckets, false);// shouldn't need const
+ clear_deleted(delpos);
+ Assert( num_deleted > 0);
+ --num_deleted; // used to be, now it isn't
+ } else {
+ ++num_elements; // replacing an empty bucket
+ }
+ set_value(&table[pos.second], obj);
+ return pair<iterator,bool>(iterator(this, table + pos.second,
+ table + num_buckets, false),
+ true); // true: we did insert
+ }
+ }
+
+ public:
+ // This is the normal insert routine, used by the outside world
+ pair<iterator, bool> insert(const value_type& obj) {
+ resize_delta(1); // adding an object, grow if need be
+ return insert_noresize(obj);
+ }
+
+ // When inserting a lot at a time, we specialize on the type of iterator
+ template <class InputIterator>
+ void insert(InputIterator f, InputIterator l) {
+ // specializes on iterator type
+ insert(f, l, typename std::iterator_traits<InputIterator>::iterator_category());
+ }
+
+ // Iterator supports operator-, resize before inserting
+ template <class ForwardIterator>
+ void insert(ForwardIterator f, ForwardIterator l,
+ std::forward_iterator_tag) {
+ size_type n = std::distance(f, l); // TODO(csilvers): standard?
+ resize_delta(n);
+ for ( ; n > 0; --n, ++f)
+ insert_noresize(*f);
+ }
+
+ // Arbitrary iterator, can't tell how much to resize
+ template <class InputIterator>
+ void insert(InputIterator f, InputIterator l,
+ std::input_iterator_tag) {
+ for ( ; f != l; ++f)
+ insert(*f);
+ }
+
+
+ // DELETION ROUTINES
+ size_type erase(const key_type& key) {
+ const_iterator pos = find(key); // shrug: shouldn't need to be const
+ if ( pos != end() ) {
+ Assert(!test_deleted(pos)); // or find() shouldn't have returned it
+ set_deleted(pos);
+ ++num_deleted;
+ consider_shrink = true; // will think about shrink after next insert
+ return 1; // because we deleted one thing
+ } else {
+ return 0; // because we deleted nothing
+ }
+ }
+
+ // This is really evil: really it should be iterator, not const_iterator.
+ // But...the only reason keys are const is to allow lookup.
+ // Since that's a moot issue for deleted keys, we allow const_iterators
+ void erase(const_iterator pos) {
+ if ( pos == end() ) return; // sanity check
+ if ( set_deleted(pos) ) { // true if object has been newly deleted
+ ++num_deleted;
+ consider_shrink = true; // will think about shrink after next insert
+ }
+ }
+
+ void erase(const_iterator f, const_iterator l) {
+ for ( ; f != l; ++f) {
+ if ( set_deleted(f) ) // should always be true
+ ++num_deleted;
+ }
+ consider_shrink = true; // will think about shrink after next insert
+ }
+
+
+ // COMPARISON
+ bool operator==(const dense_hashtable& ht) const {
+ if (size() != ht.size()) {
+ return false;
+ } else if (this == &ht) {
+ return true;
+ } else {
+ // Iterate through the elements in "this" and see if the
+ // corresponding element is in ht
+ for ( const_iterator it = begin(); it != end(); ++it ) {
+ const_iterator it2 = ht.find(get_key(*it));
+ if ((it2 == ht.end()) || (*it != *it2)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ bool operator!=(const dense_hashtable& ht) const {
+ return !(*this == ht);
+ }
+
+
+ private:
+ // The actual data
+ hasher hash; // required by hashed_associative_container
+ key_equal equals;
+ ExtractKey get_key;
+ size_type num_deleted; // how many occupied buckets are marked deleted
+ bool use_deleted; // false until delval has been set
+ bool use_empty; // you must do this before you start
+ value_type delval; // which key marks deleted entries
+ value_type emptyval; // which key marks unused entries
+ value_type *table;
+ size_type num_buckets;
+ size_type num_elements;
+ size_type shrink_threshold; // num_buckets * HT_EMPTY_FLT
+ size_type enlarge_threshold; // num_buckets * HT_OCCUPANCY_FLT
+ bool consider_shrink; // true if we should try to shrink before next insert
+
+ Alloc _Alval; // allocator object for value_type
+
+ void reset_thresholds() {
+ enlarge_threshold = static_cast<size_type>(num_buckets*HT_OCCUPANCY_FLT);
+ shrink_threshold = static_cast<size_type>(num_buckets*HT_EMPTY_FLT);
+ consider_shrink = false; // whatever caused us to reset already considered
+ }
+};
+
+// We need a global swap as well
+template <class V, class K, class HF, class ExK, class EqK, class A>
+inline void swap(dense_hashtable<V,K,HF,ExK,EqK,A> &x,
+ dense_hashtable<V,K,HF,ExK,EqK,A> &y) {
+ x.swap(y);
+}
+
+#undef JUMP_
+
+template <class V, class K, class HF, class ExK, class EqK, class A>
+const typename dense_hashtable<V,K,HF,ExK,EqK,A>::size_type
+ dense_hashtable<V,K,HF,ExK,EqK,A>::ILLEGAL_BUCKET;
+
+// How full we let the table get before we resize. Knuth says .8 is
+// good -- higher causes us to probe too much, though saves memory
+template <class V, class K, class HF, class ExK, class EqK, class A>
+const float dense_hashtable<V,K,HF,ExK,EqK,A>::HT_OCCUPANCY_FLT = 0.5f;
+
+// How empty we let the table get before we resize lower.
+// It should be less than OCCUPANCY_FLT / 2 or we thrash resizing
+template <class V, class K, class HF, class ExK, class EqK, class A>
+const float dense_hashtable<V,K,HF,ExK,EqK,A>::HT_EMPTY_FLT = 0.4f *
+dense_hashtable<V,K,HF,ExK,EqK,A>::HT_OCCUPANCY_FLT;
+
+#endif /* _DENSEHASHTABLE_H_ */
diff --git a/Runtime/Utilities/dynamic_array.h b/Runtime/Utilities/dynamic_array.h
new file mode 100644
index 0000000..90573d9
--- /dev/null
+++ b/Runtime/Utilities/dynamic_array.h
@@ -0,0 +1,340 @@
+#pragma once
+
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Utilities/StaticAssert.h"
+
+#include <memory> // std::uninitialized_fill
+
+// dynamic_array - simplified version of std::vector<T>
+//
+// features:
+// . always uses memcpy for copying elements. Your data structures must be simple and can't have internal pointers / rely on copy constructor.
+// . EASTL like push_back(void) implementation
+// Existing std STL implementations implement insertion operations by copying from an element.
+// For example, resize(size() + 1) creates a throw-away temporary object.
+// There is no way in existing std STL implementations to add an element to a container without implicitly or
+// explicitly providing one to copy from (aside from some existing POD optimizations).
+// For expensive-to-construct objects this creates a potentially serious performance problem.
+// . grows X2 on reallocation
+// . small code footprint
+// . clear actually deallocates memory
+// . resize does NOT initialize members!
+//
+// Changelog:
+// Added pop_back()
+// Added assign()
+// Added clear() - frees the data, use resize(0) to clear w/o freeing
+// zero allocation for empty array
+//
+//
+template<typename T>
+struct AlignOfType
+{
+ enum { align = ALIGN_OF(T) };
+};
+
+
+template <typename T, size_t align = AlignOfType<T>::align, MemLabelIdentifier defaultLabel = kMemDynamicArrayId>
+struct dynamic_array
+{
+public:
+ typedef T* iterator;
+ typedef const T* const_iterator;
+ typedef T value_type;
+ typedef size_t size_type;
+ typedef size_t difference_type;
+ typedef T& reference;
+ typedef const T& const_reference;
+
+public:
+
+ dynamic_array() : m_data(NULL), m_label(defaultLabel, NULL), m_size(0), m_capacity(0)
+ {
+ m_label = MemLabelId(defaultLabel, GET_CURRENT_ALLOC_ROOT_HEADER());
+ }
+
+ dynamic_array(MemLabelRef label) : m_data(NULL), m_label(label), m_size(0), m_capacity(0)
+ {
+ }
+
+ explicit dynamic_array (size_t size, MemLabelRef label)
+ : m_label(label), m_size(size), m_capacity (size)
+ {
+ m_data = allocate (size);
+ }
+
+ dynamic_array (size_t size, T const& init_value, MemLabelRef label)
+ : m_label(label), m_size (size), m_capacity (size)
+ {
+ m_data = allocate (size);
+ std::uninitialized_fill (m_data, m_data + size, init_value);
+ }
+
+ ~dynamic_array()
+ {
+ if (owns_data())
+ m_data = deallocate(m_data);
+ }
+
+ dynamic_array(const dynamic_array& other) : m_capacity(0), m_size(0), m_label(other.m_label)
+ {
+ //m_label.SetRootHeader(GET_CURRENT_ALLOC_ROOT_HEADER());
+ m_data = NULL;
+ assign(other.begin(), other.end());
+ }
+
+ dynamic_array& operator=(const dynamic_array& other)
+ {
+ // should not allocate memory unless we have to
+ assign(other.begin(), other.end());
+ return *this;
+ }
+
+ void clear()
+ {
+ if (owns_data())
+ m_data = deallocate(m_data);
+ m_size = 0;
+ m_capacity = 0;
+ }
+
+ void assign(const_iterator begin, const_iterator end)
+ {
+ Assert(begin<=end);
+
+ resize_uninitialized(end-begin);
+ memcpy(m_data, begin, m_size * sizeof(T));
+ }
+
+ void erase(iterator input_begin, iterator input_end)
+ {
+ Assert(input_begin <= input_end);
+ Assert(input_begin >= begin());
+ Assert(input_end <= end());
+
+ size_t leftOverSize = end() - input_end;
+ memmove(input_begin, input_end, leftOverSize * sizeof(T));
+ m_size -= input_end - input_begin;
+ }
+
+ iterator erase(iterator position)
+ {
+ Assert(position >= begin());
+ Assert(position < end());
+
+ size_t leftOverSize = end() - position - 1;
+ memmove(position, position+1, leftOverSize * sizeof(T));
+ m_size -= 1;
+
+ return position;
+ }
+
+ iterator insert(iterator insert_before, const_iterator input_begin, const_iterator input_end)
+ {
+ Assert(input_begin <= input_end);
+ Assert(insert_before >= begin());
+ Assert(insert_before <= end());
+
+ // resize (make sure that insertBefore does not get invalid in the meantime because of a reallocation)
+ size_t insert_before_index = insert_before - begin();
+ size_t elements_to_be_moved = size() - insert_before_index;
+ resize_uninitialized((input_end - input_begin) + size());
+ insert_before = begin() + insert_before_index;
+
+ size_t insertsize = input_end - input_begin;
+ // move to the end of where the inserted data will be
+ memmove(insert_before + insertsize, insert_before, elements_to_be_moved * sizeof(T));
+ // inject input data in the hole we just created
+ memcpy(insert_before, input_begin, insertsize * sizeof(T));
+
+ return insert_before;
+ }
+
+ iterator insert(iterator insertBefore, const T& t) { return insert(insertBefore, &t, &t + 1); }
+
+ void swap(dynamic_array& other) throw()
+ {
+ if (m_data) UNITY_TRANSFER_OWNERSHIP_TO_HEADER(m_data, m_label, other.m_label.GetRootHeader());
+ if (other.m_data) UNITY_TRANSFER_OWNERSHIP_TO_HEADER(other.m_data, other.m_label, m_label.GetRootHeader());
+ std::swap(m_data, other.m_data);
+ std::swap(m_size, other.m_size);
+ std::swap(m_capacity, other.m_capacity);
+ std::swap(m_label, other.m_label);
+ }
+
+ T& push_back()
+ {
+ if (++m_size > capacity())
+ reserve(std::max<size_t>(capacity()*2, 1));
+ return back();
+ }
+
+ void push_back(const T& t)
+ {
+ push_back() = t;
+ }
+
+ void pop_back()
+ {
+ Assert(m_size >= 1);
+ m_size--;
+ }
+
+ void resize_uninitialized(size_t size, bool double_on_resize = false)
+ {
+ m_size = size;
+ if (m_size <= capacity())
+ return;
+
+ if(double_on_resize && size < capacity()*2)
+ size = capacity()*2;
+ reserve(size);
+ }
+
+ void resize_initialized(size_t size, const T& t = T(), bool double_on_resize = false)
+ {
+ if (size > capacity())
+ {
+ size_t requested_size = size;
+ if(double_on_resize && size < capacity()*2)
+ requested_size = capacity()*2;
+ reserve(requested_size);
+ }
+
+ if (size > m_size)
+ std::uninitialized_fill (m_data + m_size, m_data + size, t);
+ m_size = size;
+ }
+
+ void reserve(size_t inCapacity)
+ {
+ if (capacity() >= inCapacity)
+ return;
+
+ if (owns_data())
+ {
+ m_capacity = inCapacity;
+ m_data = reallocate(m_data, inCapacity);
+ }
+ else
+ {
+ T* newData = allocate(inCapacity);
+ memcpy(newData, m_data, m_size * sizeof(T));
+
+ // Invalidate old non-owned data, since using the data from two places is most likely a really really bad idea.
+ #if DEBUGMODE
+ memset(m_data, 0xCD, capacity() * sizeof(T));
+ #endif
+
+ m_capacity = inCapacity; // and clear reference bit
+ m_data = newData;
+ }
+ }
+
+ void assign_external (T* begin, T* end)
+ {
+ if (owns_data())
+ m_data = deallocate(m_data);
+ m_size = m_capacity = reinterpret_cast<value_type*> (end) - reinterpret_cast<value_type*> (begin);
+ Assert(m_size < k_reference_bit);
+ m_capacity |= k_reference_bit;
+ m_data = begin;
+ }
+
+ void set_owns_data (bool ownsData)
+ {
+ if (ownsData)
+ m_capacity &= ~k_reference_bit;
+ else
+ m_capacity |= k_reference_bit;
+ }
+
+ void shrink_to_fit()
+ {
+ if (owns_data())
+ {
+ m_capacity = m_size;
+ m_data = reallocate(m_data, m_size);
+ }
+ }
+
+ const T& back() const { Assert (m_size != 0); return m_data[m_size - 1]; }
+ const T& front() const { Assert (m_size != 0); return m_data[0]; }
+
+ T& back() { Assert (m_size != 0); return m_data[m_size - 1]; }
+ T& front() { Assert (m_size != 0); return m_data[0]; }
+
+ T* data () { return m_data; }
+ T const* data () const { return m_data; }
+
+ bool empty () const { return m_size == 0; }
+ size_t size() const { return m_size; }
+ size_t capacity() const { return m_capacity & ~k_reference_bit; }
+
+ T const& operator[] (size_t index) const { DebugAssert(index < m_size); return m_data[index]; }
+ T& operator[] (size_t index) { DebugAssert(index < m_size); return m_data[index]; }
+
+ T const* begin() const { return m_data; }
+ T* begin() { return m_data; }
+
+ T const* end() const { return m_data + m_size; }
+ T* end() { return m_data + m_size; }
+
+ bool owns_data() { return (m_capacity & k_reference_bit) == 0; }
+
+ bool equals(const dynamic_array& other)
+ {
+ if(m_size != other.m_size)
+ return false;
+
+ for( int i = 0; i < m_size; i++)
+ {
+ if (m_data[i] != other.m_data[i])
+ return false;
+ }
+
+ return true;
+ }
+
+ void set_memory_label (MemLabelRef label)
+ {
+ Assert(m_data == NULL);
+ m_label = label;
+ }
+
+private:
+
+ static const size_t k_reference_bit = (size_t)1 << (sizeof (size_t) * 8 - 1);
+
+ T* allocate (size_t size)
+ {
+ // If you are getting this error then you are trying to allocate memory for an incomplete type
+ CompileTimeAssert(sizeof(T) != 0, "incomplete type");
+ CompileTimeAssert(align != 0, "incomplete type");
+
+ return static_cast<T*> (UNITY_MALLOC_ALIGNED (m_label, size * sizeof(T), align));
+ }
+
+ T* deallocate (T* data)
+ {
+ Assert(owns_data());
+ UNITY_FREE (m_label, data);
+ return NULL;
+ }
+
+ T* reallocate (T* data, size_t size)
+ {
+ // If you are getting this error then you are trying to allocate memory for an incomplete type
+ CompileTimeAssert(sizeof(T) != 0, "incomplete type");
+ CompileTimeAssert(align != 0, "incomplete type");
+
+ Assert(owns_data());
+ int alignof = static_cast<int>(align);
+ return static_cast<T*> (UNITY_REALLOC_ALIGNED(m_label, data, size * sizeof(T), alignof));
+ }
+
+ T* m_data;
+ MemLabelId m_label;
+ size_t m_size;
+ size_t m_capacity;
+};
diff --git a/Runtime/Utilities/dynamic_array_tests.cpp b/Runtime/Utilities/dynamic_array_tests.cpp
new file mode 100644
index 0000000..8169fd9
--- /dev/null
+++ b/Runtime/Utilities/dynamic_array_tests.cpp
@@ -0,0 +1,254 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+
+
+TEST (DynamicArray)
+{
+ // no allocation for empty array
+ dynamic_array<int> array;
+ CHECK_EQUAL (0, array.capacity ());
+
+ // push_back allocates
+ int j = 1;
+ array.push_back (j);
+ CHECK_EQUAL (1, array.size ());
+ CHECK (array.capacity () > 0);
+
+ // push_back(void)
+ int& i = array.push_back ();
+ i = 666;
+ CHECK_EQUAL(666, array.back ());
+
+ // clear frees memory?
+ array.clear ();
+ CHECK_EQUAL (0, array.size ());
+ CHECK_EQUAL (0, array.capacity ());
+
+ // 3 item list
+ j = 6;
+ array.push_back (j);
+ j = 7;
+ array.push_back (j);
+ j = 8;
+ array.push_back (j);
+
+ CHECK_EQUAL (3, array.size ());
+
+ // swapping
+ dynamic_array<int> ().swap (array);
+ CHECK_EQUAL (0, array.capacity ());
+ CHECK_EQUAL (0, array.size ());
+
+ // reserve
+ array.reserve (1024);
+ CHECK_EQUAL (1024, array.capacity ());
+ CHECK_EQUAL (0, array.size ());
+
+ // copy assignment
+ dynamic_array<int> array1;
+ j = 888;
+ array1.push_back (j);
+
+ array = array1;
+
+ CHECK_EQUAL (1, array.size ());
+ CHECK_EQUAL (888, array.back ());
+ CHECK_EQUAL (1, array1.size ());
+ CHECK_EQUAL (888, array1.back ());
+}
+
+TEST (DynamicArrayMisc)
+{
+ // no allocation for empty array
+ dynamic_array<int> array;
+ CHECK_EQUAL (0, array.capacity ());
+ CHECK (array.owns_data ());
+ CHECK (array.begin () == array.end ());
+ CHECK (array.empty ());
+
+ // push_back allocates
+ int j = 1;
+ array.push_back (j);
+ CHECK_EQUAL (1, array.size ());
+ CHECK (array.capacity () > 0);
+
+ // push_back(void)
+ int& i = array.push_back ();
+ i = 666;
+ CHECK_EQUAL(666, array.back ());
+
+ // clear frees memory?
+ array.clear ();
+ CHECK_EQUAL (0, array.size ());
+ CHECK_EQUAL (0, array.capacity ());
+
+ // 3 item list
+ array.push_back (6);
+ array.push_back (7);
+ array.push_back (8);
+
+ CHECK_EQUAL (3, array.size ());
+
+ // swapping
+ dynamic_array<int> ().swap (array);
+ CHECK_EQUAL (0, array.capacity ());
+ CHECK_EQUAL (0, array.size ());
+
+ // reserve
+ array.reserve (1024);
+ CHECK_EQUAL (1024, array.capacity ());
+ CHECK_EQUAL (0, array.size ());
+
+ // copy assignment
+ dynamic_array<int> array1;
+ j = 888;
+ array1.push_back (j);
+
+ array = array1;
+
+ CHECK_EQUAL (1, array.size ());
+ CHECK_EQUAL (888, array.back ());
+ CHECK_EQUAL (1, array1.size ());
+ CHECK_EQUAL (888, array1.back ());
+}
+
+
+TEST (DynamicArrayErase)
+{
+ dynamic_array<int> arr;
+ arr.push_back(1);
+ arr.push_back(2);
+ arr.push_back(3);
+ arr.push_back(4);
+ arr.push_back(5);
+ dynamic_array<int>::iterator it;
+
+ // erase first elem
+ it = arr.erase(arr.begin());
+ CHECK_EQUAL (2, *it);
+ CHECK_EQUAL (4, arr.size());
+ CHECK_EQUAL (2, arr[0]);
+ CHECK_EQUAL (3, arr[1]);
+ CHECK_EQUAL (4, arr[2]);
+ CHECK_EQUAL (5, arr[3]);
+
+ // erase 2nd to last elem
+ it = arr.erase(arr.end()-2);
+ CHECK_EQUAL (5, *it);
+ CHECK_EQUAL (3, arr.size());
+ CHECK_EQUAL (2, arr[0]);
+ CHECK_EQUAL (3, arr[1]);
+ CHECK_EQUAL (5, arr[2]);
+
+ // erase last elem
+ it = arr.erase(arr.end()-1);
+ CHECK_EQUAL (arr.end(), it);
+ CHECK_EQUAL (2, arr.size());
+ CHECK_EQUAL (2, arr[0]);
+ CHECK_EQUAL (3, arr[1]);
+}
+
+
+TEST (DynamicArrayEraseRange)
+{
+ dynamic_array<int> vs;
+ vs.resize_uninitialized(5);
+
+ vs[0] = 0;
+ vs[1] = 1;
+ vs[2] = 2;
+ vs[3] = 3;
+ vs[4] = 4;
+
+ vs.erase(vs.begin() + 1, vs.begin() + 4);
+ CHECK_EQUAL (2, vs.size());
+ CHECK_EQUAL (5, vs.capacity());
+ CHECK_EQUAL (0, vs[0]);
+ CHECK_EQUAL (4, vs[1]);
+}
+
+static void VerifyConsecutiveIntArray (dynamic_array<int>& vs, int size, int capacity)
+{
+ CHECK_EQUAL (capacity, vs.capacity());
+ CHECK_EQUAL (size, vs.size());
+ for (int i=0;i<vs.size();i++)
+ CHECK_EQUAL (i, vs[i]);
+}
+
+TEST (DynamicArrayInsertOnEmpty)
+{
+ dynamic_array<int> vs;
+ int vals[] = { 0, 1 };
+
+ vs.insert(vs.begin(), vals, vals + ARRAY_SIZE(vals));
+
+ VerifyConsecutiveIntArray(vs, 2, 2);
+}
+
+
+TEST (DynamicArrayInsert)
+{
+ dynamic_array<int> vs;
+ vs.resize_uninitialized(5);
+
+ vs[0] = 0;
+ vs[1] = 1;
+ vs[2] = 4;
+ vs[3] = 5;
+ vs[4] = 6;
+
+ int vals[] = { 2, 3 };
+
+ // inser two values
+ vs.insert(vs.begin() + 2, vals, vals + ARRAY_SIZE(vals));
+ VerifyConsecutiveIntArray(vs, 7, 7);
+
+ // empty insert
+ vs.insert(vs.begin() + 2, vals, vals);
+
+ VerifyConsecutiveIntArray(vs, 7, 7);
+}
+
+TEST (DynamicArrayResize)
+{
+ dynamic_array<int> vs;
+ vs.resize_initialized(3, 2);
+ CHECK_EQUAL (3, vs.capacity());
+ CHECK_EQUAL (3, vs.size());
+ CHECK_EQUAL (2, vs[0]);
+ CHECK_EQUAL (2, vs[1]);
+ CHECK_EQUAL (2, vs[2]);
+
+ vs.resize_initialized(6, 3);
+ CHECK_EQUAL (6, vs.capacity());
+ CHECK_EQUAL (6, vs.size());
+ CHECK_EQUAL (2, vs[0]);
+ CHECK_EQUAL (2, vs[1]);
+ CHECK_EQUAL (2, vs[2]);
+ CHECK_EQUAL (3, vs[3]);
+ CHECK_EQUAL (3, vs[4]);
+ CHECK_EQUAL (3, vs[5]);
+
+ vs.resize_initialized(5, 3);
+ CHECK_EQUAL (6, vs.capacity());
+ CHECK_EQUAL (5, vs.size());
+ CHECK_EQUAL (2, vs[0]);
+ CHECK_EQUAL (2, vs[1]);
+ CHECK_EQUAL (2, vs[2]);
+ CHECK_EQUAL (3, vs[3]);
+ CHECK_EQUAL (3, vs[4]);
+
+ vs.resize_initialized(2, 3);
+ CHECK_EQUAL (6, vs.capacity());
+ CHECK_EQUAL (2, vs.size());
+ CHECK_EQUAL (2, vs[0]);
+ CHECK_EQUAL (2, vs[1]);
+}
+
+
+#endif // #if ENABLE_UNIT_TESTS
diff --git a/Runtime/Utilities/dynamic_bitset.h b/Runtime/Utilities/dynamic_bitset.h
new file mode 100644
index 0000000..7dadb2c
--- /dev/null
+++ b/Runtime/Utilities/dynamic_bitset.h
@@ -0,0 +1,1144 @@
+#ifndef DYNAMIC_BITSET_H
+#define DYNAMIC_BITSET_H
+// (C) Copyright Chuck Allison and Jeremy Siek 2001, 2002.
+//
+// Permission to copy, use, modify, sell and distribute this software
+// is granted provided this copyright notice appears in all
+// copies. This software is provided "as is" without express or
+// implied warranty, and with no claim as to its suitability for any
+// purpose.
+
+// With optimizations, bug fixes, and improvements by Gennaro Prota.
+
+// See http://www.boost.org/libs/dynamic_bitset for documentation.
+
+// -------------------------------------
+// CHANGE LOG:
+//
+// - corrected workaround for Dinkum lib's allocate() [GP]
+// - changed macro test for old iostreams [GP]
+// - removed #include <vector> for now. [JGS]
+// - Added __GNUC__ to compilers that cannot handle the constructor from basic_string. [JGS]
+// - corrected to_block_range [GP]
+// - corrected from_block_range [GP]
+// - Removed __GNUC__ from compilers that cannot handle the constructor
+// from basic_string and added the workaround suggested by GP. [JGS]
+// - Removed __BORLANDC__ from the #if around the basic_string
+// constructor. Luckily the fix by GP for g++ also fixes Borland. [JGS]
+
+#include <cassert>
+#include <string>
+#include <cstring> // for memset, memcpy, memcmp, etc.
+#include <algorithm> // for std::swap, std::min, std::copy, std::fill
+#include <memory> // for std::swap, std::min, std::copy, std::fill
+#include <stdlib.h>
+#include "LogAssert.h"
+
+namespace std
+{
+ typedef ::size_t size_t;
+}
+// (C) Copyright Chuck Allison and Jeremy Siek 2001, 2002.
+//
+// Permission to copy, use, modify, sell and distribute this software
+// is granted provided this copyright notice appears in all
+// copies. This software is provided "as is" without express or
+// implied warranty, and with no claim as to its suitability for any
+// purpose.
+
+// With optimizations by Gennaro Prota.
+
+ class dynamic_bitset_base
+ {
+ typedef std::size_t size_type;
+ public:
+#if defined(LINUX) && (defined (__LP64__) || defined(_AMD64_))
+ typedef unsigned int Block;
+#else
+ typedef unsigned long Block;
+#endif
+ enum { bits_per_block = 8 * sizeof(Block) };
+
+ dynamic_bitset_base()
+ : m_bits(0), m_num_bits(0), m_num_blocks(0) { }
+
+ dynamic_bitset_base(size_type num_bits) :
+ m_num_bits(num_bits),
+ m_num_blocks(calc_num_blocks(num_bits))
+ {
+ if (m_num_blocks != 0)
+ {
+ m_bits = new Block[m_num_blocks];
+ memset(m_bits, 0, m_num_blocks * sizeof(Block)); // G.P.S. ask to Jeremy
+ }
+ else
+ m_bits = 0;
+ }
+ ~dynamic_bitset_base() {
+ delete []m_bits;;
+ }
+
+ Block* m_bits;
+ size_type m_num_bits;
+ size_type m_num_blocks;
+
+ static size_type word(size_type bit) { return bit / bits_per_block; } // [gps]
+ static size_type offset(size_type bit){ return bit % bits_per_block; } // [gps]
+ static Block mask1(size_type bit) { return Block(1) << offset(bit); }
+ static Block mask0(size_type bit) { return ~(Block(1) << offset(bit)); }
+ static size_type calc_num_blocks(size_type num_bits)
+ { return (num_bits + bits_per_block - 1) / bits_per_block; }
+ };
+
+
+ // ------- count table implementation --------------
+
+ typedef unsigned char byte_t;
+
+ template <bool bogus = true>
+ struct bitcount {
+ typedef byte_t element_type;
+ static const byte_t table[];
+
+ };
+ //typedef count<true> table_t;
+
+
+ // the table: wrapped in a class template, so
+ // that it is only instantiated if/when needed
+ //
+ template <bool bogus>
+ const byte_t bitcount<bogus>::table[] =
+ {
+ // Automatically generated by GPTableGen.exe v.1.0
+ //
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
+ };
+
+
+ // -------------------------------------------------------
+ template <typename BlockInputIterator>
+ std::size_t initial_num_blocks(BlockInputIterator first,
+ BlockInputIterator last)
+ {
+ std::size_t n = 0;
+ while (first != last)
+ ++first, ++n;
+ return n;
+ }
+
+class dynamic_bitset : public dynamic_bitset_base
+{
+ public:
+
+ typedef Block block_type;
+ typedef std::size_t size_type;
+ enum { bits_per_block = 8 * sizeof(Block) };
+
+ // reference to a bit
+ class reference
+ {
+ friend class dynamic_bitset;
+ dynamic_bitset* bs;
+ size_type bit;
+ reference(); // intentionally not implemented
+ reference(dynamic_bitset& bs_, size_type bit_) : bs(&bs_), bit(bit_){ }
+ public:
+ reference& operator=(bool value) // for b[i] = x
+ {
+ if (value)
+ bs->set(bit);
+ else
+ bs->reset(bit);
+ return *this;
+ }
+ reference& operator|=(bool value) // for b[i] |= x
+ {
+ if (value)
+ bs->set(bit);
+ return *this;
+ }
+ reference& operator&=(bool value) // for b[i] &= x
+ {
+ if (! (value && bs->test(bit)))
+ bs->reset(bit);
+ return *this;
+ }
+ reference& operator^=(bool value) // for b[i] ^= x
+ {
+ bs->set(bit, bs->test(bit) ^ value);
+ return *this;
+ }
+ reference& operator-=(bool value) // for b[i] -= x
+ {
+ if (!value)
+ bs->reset(bit);
+ return *this;
+ }
+ reference& operator=(const reference& j) // for b[i] = b[j]
+ {
+ if (j.bs->test(j.bit))
+ bs->set(bit);
+ else
+ bs->reset(bit);
+ return *this;
+ }
+ reference& operator|=(const reference& j) // for b[i] |= b[j]
+ {
+ if (j.bs->test(j.bit))
+ bs->set(bit);
+ return *this;
+ }
+ reference& operator&=(const reference& j) // for b[i] &= b[j]
+ {
+ if (! (j.bs->test(j.bit) && bs->test(bit)))
+ bs->reset(bit);
+ return *this;
+ }
+ reference& operator^=(const reference& j) // for b[i] ^= b[j]
+ {
+ bs->set(bit, bs->test(bit) ^ j.bs->test(j.bit));
+ return *this;
+ }
+ reference& operator-=(const reference& j) // for b[i] -= b[j]
+ {
+ if (!j.bs->test(j.bit))
+ bs->reset(bit);
+ return *this;
+ }
+ bool operator~() const // flips the bit
+ {
+ return ! bs->test(bit);
+ }
+ operator bool() const // for x = b[i]
+ {
+ return bs->test(bit);
+ }
+ reference& flip() // for b[i].flip();
+ {
+ bs->flip(bit);
+ return *this;
+ }
+ };
+ typedef bool const_reference;
+
+ dynamic_bitset ();
+ explicit
+ dynamic_bitset(size_type num_bits, unsigned long value = 0);
+
+ // The parenthesis around std::basic_string<CharT, Traits, Alloc>::npos
+ // in the code below are to avoid a g++ 3.2 bug and a Borland bug. -JGS
+ template <typename String>
+ explicit
+ dynamic_bitset(const String& s,
+ typename String::size_type pos = 0,
+ typename String::size_type n
+ = (String::npos))
+ : dynamic_bitset_base
+ (std::min(n, s.size() - pos))
+ {
+ // Locate sub string
+ AssertIf (pos > s.length());
+ from_string(s, pos, std::min(n, s.size() - pos));
+ }
+
+ // The first bit in *first is the least significant bit, and the
+ // last bit in the block just before *last is the most significant bit.
+ template <typename BlockInputIterator>
+ dynamic_bitset(BlockInputIterator first, BlockInputIterator last)
+ : dynamic_bitset_base
+ (initial_num_blocks(first, last)
+ * bits_per_block)
+ {
+ if (first != last) {
+ if (this->m_num_bits == 0) { // dealing with input iterators
+ this->append(first, last);
+ } else {
+ // dealing with forward iterators, memory has been allocated
+ for (std::size_t i = 0; first != last; ++first, ++i)
+ set_block_(i, *first);
+ }
+ }
+ }
+
+
+ // copy constructor
+ dynamic_bitset(const dynamic_bitset& b);
+
+ void swap(dynamic_bitset& b);
+
+ dynamic_bitset& operator=(const dynamic_bitset& b);
+
+ // size changing operations
+ void resize(size_type num_bits, bool value = false);
+ void clear();
+ void push_back(bool bit);
+ void append(Block block);
+
+ // This is declared inside the class to avoid compiler bugs.
+ template <typename BlockInputIterator>
+ void append(BlockInputIterator first, BlockInputIterator last)
+ {
+ if (first != last) {
+ std::size_t nblocks = initial_num_blocks(first, last);
+ if (nblocks == 0) { // dealing with input iterators
+ for (; first != last; ++first)
+ append(*first);
+ } else { // dealing with forward iterators
+ if (size() % bits_per_block == 0) {
+ std::size_t old_nblocks = this->m_num_blocks;
+ resize(size() + nblocks * bits_per_block);
+ for (std::size_t i = old_nblocks; first != last; ++first)
+ set_block_(i++, *first);
+ } else {
+ // probably should optimize this,
+ // but I'm sick of bit twiddling
+ for (; first != last; ++first)
+ append(*first);
+ }
+ }
+ }
+ }
+
+
+ // bitset operations
+ dynamic_bitset& operator&=(const dynamic_bitset& b);
+ dynamic_bitset& operator|=(const dynamic_bitset& b);
+ dynamic_bitset& operator^=(const dynamic_bitset& b);
+ dynamic_bitset& operator-=(const dynamic_bitset& b);
+ dynamic_bitset& operator<<=(size_type n);
+ dynamic_bitset& operator>>=(size_type n);
+ dynamic_bitset operator<<(size_type n) const;
+ dynamic_bitset operator>>(size_type n) const;
+
+ // basic bit operations
+ dynamic_bitset& set(size_type n, bool val = true);
+ dynamic_bitset& set();
+ dynamic_bitset& reset(size_type n);
+ dynamic_bitset& reset();
+ dynamic_bitset& flip(size_type n);
+ dynamic_bitset& flip();
+ bool test(size_type n) const;
+ bool any() const;
+ bool none() const;
+ dynamic_bitset operator~() const;
+ size_type count() const;
+
+ // subscript
+ reference operator[](size_type pos) { return reference(*this, pos); }
+ bool operator[](size_type pos) const
+ {
+ #if UNITY_EDITOR
+ if (pos < this->m_num_bits)
+ return test_(pos);
+ else
+ {
+ ErrorString("dynamic_bitset.test bit out of bounds");
+ return false;
+ }
+ #else
+ AssertIf(pos >= this->m_num_bits);
+ return test_(pos);
+ #endif
+ }
+
+ unsigned long to_ulong() const;
+
+ size_type size() const;
+ size_type num_blocks() const;
+
+ bool is_subset_of(const dynamic_bitset& a) const;
+ bool is_proper_subset_of(const dynamic_bitset& a) const;
+
+ void m_zero_unused_bits();
+
+
+private:
+ void set_(size_type bit);
+ bool set_(size_type bit, bool val);
+ void reset_(size_type bit);
+ bool test_(size_type bit) const;
+ void set_block_(size_type blocknum, Block b);
+
+public:
+
+ // This is templated on the whole String instead of just CharT,
+ // Traits, Alloc to avoid compiler bugs.
+ template <typename String>
+ void from_string(const String& s, typename String::size_type pos,
+ typename String::size_type rlen)
+ {
+ reset(); // bugfix [gps]
+ size_type const tot = std::min (rlen, s.length()); // bugfix [gps]
+
+ // Assumes string contains only 0's and 1's
+ for (size_type i = 0; i < tot; ++i) {
+ if (s[pos + tot - i - 1] == '1') {
+ set_(i);
+ } else {
+ AssertIf(s[pos + tot - i - 1] != '0');
+ }
+ }
+ }
+
+};
+
+// Global Functions:
+
+// comparison
+inline bool operator!=(const dynamic_bitset& a,
+ const dynamic_bitset& b);
+
+inline bool operator<=(const dynamic_bitset& a,
+ const dynamic_bitset& b);
+
+inline bool operator>(const dynamic_bitset& a,
+ const dynamic_bitset& b);
+
+inline bool operator>=(const dynamic_bitset& a,
+ const dynamic_bitset& b);
+
+// bitset operations
+inline dynamic_bitset
+operator&(const dynamic_bitset& b1,
+ const dynamic_bitset& b2);
+
+inline dynamic_bitset
+operator|(const dynamic_bitset& b1,
+ const dynamic_bitset& b2);
+
+inline dynamic_bitset
+operator^(const dynamic_bitset& b1,
+ const dynamic_bitset& b2);
+
+inline dynamic_bitset
+operator-(const dynamic_bitset& b1,
+ const dynamic_bitset& b2);
+
+
+template <typename String>
+void
+to_string(const dynamic_bitset& b,
+ String& s);
+
+template <typename BlockOutputIterator>
+void
+to_block_range(const dynamic_bitset& b,
+ BlockOutputIterator result);
+
+template <typename BlockIterator>
+inline void
+from_block_range(BlockIterator first, BlockIterator last,
+ dynamic_bitset& result);
+
+
+//=============================================================================
+// dynamic_bitset implementation
+
+
+//-----------------------------------------------------------------------------
+// constructors, etc.
+
+inline dynamic_bitset::dynamic_bitset()
+ : dynamic_bitset_base(0) { }
+
+inline dynamic_bitset::
+dynamic_bitset(size_type num_bits, unsigned long value)
+ : dynamic_bitset_base(num_bits)
+{
+ const size_type M = std::min(sizeof(unsigned long) * 8, num_bits);
+ for(size_type i = 0; i < M; ++i, value >>= 1) // [G.P.S.] to be optimized
+ if ( value & 0x1 )
+ set_(i);
+}
+
+// copy constructor
+inline dynamic_bitset::
+dynamic_bitset(const dynamic_bitset& b)
+ : dynamic_bitset_base(b.size())
+{
+ memcpy(this->m_bits, b.m_bits, this->m_num_blocks * sizeof(Block));
+}
+
+inline void dynamic_bitset::
+swap(dynamic_bitset& b)
+{
+ std::swap(this->m_bits, b.m_bits);
+ std::swap(this->m_num_bits, b.m_num_bits);
+ std::swap(this->m_num_blocks, b.m_num_blocks);
+}
+
+inline dynamic_bitset& dynamic_bitset::
+operator=(const dynamic_bitset& b)
+{
+ dynamic_bitset tmp(b);
+ this->swap(tmp);
+ return *this;
+}
+
+//-----------------------------------------------------------------------------
+// size changing operations
+
+inline void dynamic_bitset::
+resize(size_type num_bits, bool value)
+{
+ if (num_bits == size())
+ return;
+ if (num_bits == 0)
+ {
+ this->m_num_bits = 0;
+ this->m_num_blocks = 0;
+ delete this->m_bits;
+ this->m_bits = 0;
+ return;
+ }
+ size_type new_nblocks = this->calc_num_blocks(num_bits);
+ Block* d = new Block[new_nblocks];
+ if (num_bits < size()) { // shrink
+ std::copy(this->m_bits, this->m_bits + new_nblocks, d);
+ std::swap(d, this->m_bits);
+ delete []d;
+ } else { // grow
+ std::copy(this->m_bits, this->m_bits + this->m_num_blocks, d);
+ Block val = value? ~static_cast<Block>(0) : static_cast<Block>(0);
+ std::fill(d + this->m_num_blocks, d + new_nblocks, val);
+ std::swap(d, this->m_bits);
+ for (std::size_t i = this->m_num_bits;
+ i < this->m_num_blocks * bits_per_block; ++i)
+ set_(i, value);
+ if (d != 0)
+ delete []d;
+ }
+ this->m_num_bits = num_bits;
+ this->m_num_blocks = this->calc_num_blocks(num_bits);
+ m_zero_unused_bits();
+}
+
+inline void dynamic_bitset::
+clear()
+{
+ if (this->m_bits != 0) {
+ delete this->m_bits;
+ this->m_bits = 0;
+ this->m_num_bits = 0;
+ this->m_num_blocks = 0;
+ }
+}
+
+
+inline void dynamic_bitset::
+push_back(bool bit)
+{
+ this->resize(this->size() + 1);
+ set_(this->size() - 1, bit);
+}
+
+inline void dynamic_bitset::
+append(Block value)
+{
+ std::size_t old_size = size();
+ resize(old_size + bits_per_block);
+ if (size() % bits_per_block == 0)
+ set_block_(this->m_num_blocks - 1, value);
+ else {
+ // G.P.S. to be optimized
+ for (std::size_t i = old_size; i < size(); ++i, value >>= 1)
+ set_(i, value & 1);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// bitset operations
+inline dynamic_bitset&
+dynamic_bitset::operator&=(const dynamic_bitset& rhs)
+{
+ AssertIf(size() != rhs.size());
+ for (size_type i = 0; i < this->m_num_blocks; ++i)
+ this->m_bits[i] &= rhs.m_bits[i];
+ return *this;
+}
+
+inline dynamic_bitset&
+dynamic_bitset::operator|=(const dynamic_bitset& rhs)
+{
+ AssertIf(size() != rhs.size());
+ for (size_type i = 0; i < this->m_num_blocks; ++i)
+ this->m_bits[i] |= rhs.m_bits[i];
+ m_zero_unused_bits();
+ return *this;
+}
+
+inline dynamic_bitset&
+dynamic_bitset::operator^=(const dynamic_bitset& rhs)
+{
+ AssertIf(size() != rhs.size());
+ for (size_type i = 0; i < this->m_num_blocks; ++i)
+ this->m_bits[i] ^= rhs.m_bits[i];
+ m_zero_unused_bits();
+ return *this;
+}
+
+inline dynamic_bitset&
+dynamic_bitset::operator-=(const dynamic_bitset& rhs)
+{
+ AssertIf(size() != rhs.size());
+ for (size_type i = 0; i < this->m_num_blocks; ++i)
+ this->m_bits[i] = this->m_bits[i] & ~rhs.m_bits[i];
+ m_zero_unused_bits();
+ return *this;
+}
+
+inline dynamic_bitset&
+dynamic_bitset::operator<<=(size_type n)
+{
+ if (n >= this->m_num_bits)
+ return reset();
+ //else
+ if (n > 0)
+ {
+ size_type const last = this->m_num_blocks - 1; // m_num_blocks is >= 1
+ size_type const div = n / bits_per_block; // div is <= last
+ size_type const r = n % bits_per_block;
+
+ // PRE: div != 0 or r != 0
+
+ if (r != 0) {
+
+ block_type const rs = bits_per_block - r;
+
+ for (size_type i = last-div; i>0; --i) {
+ this->m_bits[i+div] = (this->m_bits[i] << r) | (this->m_bits[i-1] >> rs);
+ }
+ this->m_bits[div] = this->m_bits[0] << r;
+
+ }
+ else {
+ for (size_type i = last-div; i>0; --i) {
+ this->m_bits[i+div] = this->m_bits[i];
+ }
+ this->m_bits[div] = this->m_bits[0];
+ }
+
+
+ // div blocks are zero filled at the less significant end
+ std::fill(this->m_bits, this->m_bits+div, static_cast<block_type>(0));
+
+
+ }
+
+ return *this;
+
+
+}
+
+
+
+
+
+
+
+// NOTE: this assumes that within a single block bits are
+// numbered from right to left. G.P.S.
+//
+// static Block offset(size_type bit)
+// { return bit % bits_per_block; }
+//
+//
+// In the implementation below the 'if (r != 0)' is logically
+// unnecessary. It's there as an optimization only: in fact
+// for r==0 the first branch becomes the second one with the
+// b[last-div] = b[last] >> r; statement that does the work of
+// the last iteration.
+//
+inline
+dynamic_bitset & dynamic_bitset::operator>>=(size_type n) {
+ if (n >= this->m_num_bits) {
+ return reset();
+ }
+ //else
+ if (n>0){
+
+ size_type const last = this->m_num_blocks - 1; // m_num_blocks is >= 1
+ size_type const div = n / bits_per_block; // div is <= last
+ size_type const r = n % bits_per_block;
+
+ // PRE: div != 0 or r != 0
+
+ if (r != 0) {
+
+ block_type const ls = bits_per_block - r;
+
+ for (size_type i = div; i < last; ++i) {
+ this->m_bits[i-div] = (this->m_bits[i] >> r) | (this->m_bits[i+1] << ls);
+ }
+ // r bits go to zero
+ this->m_bits[last-div] = this->m_bits[last] >> r;
+ }
+
+ else {
+ for (size_type i = div; i <= last; ++i) {
+ this->m_bits[i-div] = this->m_bits[i];
+ }
+ // note the '<=': the last iteration 'absorbs'
+ // this->m_bits[last-div] = this->m_bits[last] >> 0;
+ }
+
+
+
+ // div blocks are zero filled at the most significant end
+ std::fill(this->m_bits+(this->m_num_blocks-div), this->m_bits+this->m_num_blocks, static_cast<block_type>(0));
+ }
+
+ return *this;
+}
+
+
+
+
+
+
+
+inline dynamic_bitset
+dynamic_bitset::operator<<(size_type n) const
+{
+ dynamic_bitset r(*this);
+ return r <<= n;
+}
+
+inline dynamic_bitset
+dynamic_bitset::operator>>(size_type n) const
+{
+ dynamic_bitset r(*this);
+ return r >>= n;
+}
+
+
+//-----------------------------------------------------------------------------
+// basic bit operations
+
+inline dynamic_bitset&
+dynamic_bitset::set(size_type pos, bool val)
+{
+ AssertIf(pos >= this->m_num_bits);
+ set_(pos, val);
+ return *this;
+}
+
+inline dynamic_bitset&
+dynamic_bitset::set()
+{
+ if (this->m_num_bits > 0) {
+ using namespace std;
+ memset(this->m_bits, ~0u, this->m_num_blocks * sizeof(this->m_bits[0]));
+ m_zero_unused_bits();
+ }
+ return *this;
+}
+
+inline dynamic_bitset&
+dynamic_bitset::reset(size_type pos)
+{
+ AssertIf(pos >= this->m_num_bits);
+ reset_(pos);
+ return *this;
+}
+
+inline dynamic_bitset&
+dynamic_bitset::reset()
+{
+ if (this->m_num_bits > 0) {
+ using namespace std;
+ memset(this->m_bits, 0, this->m_num_blocks * sizeof(this->m_bits[0]));
+ }
+ return *this;
+}
+
+inline dynamic_bitset&
+dynamic_bitset::flip(size_type pos)
+{
+ AssertIf(pos >= this->m_num_bits);
+ this->m_bits[this->word(pos)] ^= this->mask1(pos);
+ return *this;
+}
+
+inline dynamic_bitset&
+dynamic_bitset::flip()
+{
+ for (size_type i = 0; i < this->m_num_blocks; ++i)
+ this->m_bits[i] = ~this->m_bits[i];
+ m_zero_unused_bits();
+ return *this;
+}
+
+inline bool dynamic_bitset::test(size_type pos) const
+{
+ #if UNITY_EDITOR
+ if (pos < this->m_num_bits)
+ return test_(pos);
+ else
+ {
+ ErrorString("dynamic_bitset.test bit out of bounds");
+ return false;
+ }
+ #else
+ AssertIf(pos >= this->m_num_bits);
+ return test_(pos);
+ #endif
+}
+
+inline bool dynamic_bitset::any() const
+{
+ for (size_type i = 0; i < this->m_num_blocks; ++i)
+ if (this->m_bits[i])
+ return 1;
+ return 0;
+}
+
+inline bool dynamic_bitset::none() const
+{
+ return !any();
+}
+
+inline dynamic_bitset
+dynamic_bitset::operator~() const
+{
+ dynamic_bitset b(*this);
+ b.flip();
+ return b;
+}
+
+
+/* snipped: [gps]
+
+The following is the straightforward implementation of count(), which
+we leave here in a comment for documentation purposes.
+
+template <typename Block, typename Allocator>
+typename dynamic_bitset::size_type
+dynamic_bitset::count() const
+{
+ size_type sum = 0;
+ for (size_type i = 0; i != this->m_num_bits; ++i)
+ if (test_(i))
+ ++sum;
+ return sum;
+}
+
+The actual algorithm used is based on using a lookup
+table.
+
+
+ The basic idea of the method is to pick up X bits at a time
+ from the internal array of blocks and consider those bits as
+ the binary representation of a number N. Then, to use a table
+ of 1<<X elements where table[N] is the number of '1' digits
+ in the binary representation of N (i.e. in our X bits).
+
+ Note that the table can be oversized (i.e. can even have more
+ than 1<<X elements; in that case only the first 1<<X will be
+ actually used) but it cannot be undersized.
+ In this implementation X is 8 (but can be easily changed: you
+ just have to change the definition of count<>::max_bits) and
+ the internal array of blocks is seen as an array of bytes: if
+ a byte has exactly 8 bits then it's enough to sum the value
+ of table[B] for each byte B. Otherwise 8 bits at a time are
+ 'extracted' from each byte by using another loop. As a further
+ efficiency consideration note that even if you have, let's say,
+ 32-bit chars the inner loop will not do 4 (i.e. 32/8) iterations,
+ unless you have at least one bit set in the highest 8 bits of the
+ byte.
+
+ Note also that the outmost if/else is not necessary but is there
+ to help the optimizer (and one of the two branches is always dead
+ code).
+
+ Aras: hardcoded table to be always max_bits=8. To help not so good compilers.
+
+*/
+
+
+inline dynamic_bitset::size_type
+dynamic_bitset::count() const
+{
+ const byte_t * p = reinterpret_cast<const byte_t*>(this->m_bits);
+ const byte_t * past_end = p + this->m_num_blocks * sizeof(Block);
+
+ size_type num = 0;
+
+ while (p < past_end) {
+ num += bitcount<>::table[*p];
+ ++p;
+ }
+
+ return num;
+}
+
+
+//-----------------------------------------------------------------------------
+// conversions
+
+// take as ref param instead?
+template <typename CharT, typename Alloc>
+void
+to_string(const dynamic_bitset& b,
+ std::basic_string<CharT, Alloc>& s)
+{
+ s.assign(b.size(), '0');
+ for (std::size_t i = 0; i < b.size(); ++i)
+ if (b.test(i)) // [G.P.S.]
+ s[b.size() - 1 - i] = '1';
+}
+
+
+// Differently from to_string this function dumps out
+// every bit of the internal representation (useful
+// for debugging purposes)
+//
+template <typename CharT, typename Alloc>
+void
+dump_to_string(const dynamic_bitset& b,
+ std::basic_string<CharT, Alloc>& s)
+{
+ std::size_t const len = b.m_num_blocks * (dynamic_bitset::bits_per_block);
+ s.assign(len, '0');
+ for (std::size_t i = 0; i != len; ++i)
+ if (b[i])// could use test_ here, but we have friend issues.-JGS
+ s[len - 1 - i] = '1';
+}
+
+
+
+template <typename BlockOutputIterator>
+void
+to_block_range(const dynamic_bitset& b,
+ BlockOutputIterator result)
+{
+ AssertIf(!(b.size() != 0 || b.num_blocks() == 0));
+ std::copy (b.m_bits, b.m_bits + b.m_num_blocks, result);
+}
+
+template <typename BlockIterator>
+inline void
+from_block_range(BlockIterator first, BlockIterator last,
+ dynamic_bitset& result)
+{
+ AssertIf(std::distance(first, last) != result.num_blocks());
+ std::copy (first, last, result.m_bits);
+ result.m_zero_unused_bits ();
+}
+
+inline dynamic_bitset::size_type
+dynamic_bitset::size() const
+{
+ return this->m_num_bits;
+}
+
+inline dynamic_bitset::size_type
+dynamic_bitset::num_blocks() const
+{
+ return this->m_num_blocks;
+}
+
+inline bool dynamic_bitset::
+is_subset_of(const dynamic_bitset& a) const
+{
+ AssertIf(this->size() != a.size());
+ for (size_type i = 0; i < this->m_num_blocks; ++i)
+ if (this->m_bits[i] & ~a.m_bits[i])
+ return false;
+ return true;
+}
+
+inline bool dynamic_bitset::
+is_proper_subset_of(const dynamic_bitset& a) const
+{
+ AssertIf(this->size() != a.size());
+ bool proper = false;
+ for (size_type i = 0; i < this->m_num_blocks; ++i) {
+ Block bt = this->m_bits[i], ba = a.m_bits[i];
+ if (ba & ~bt)
+ proper = true;
+ if (bt & ~ba)
+ return false;
+ }
+ return proper;
+}
+
+//-----------------------------------------------------------------------------
+// comparison
+
+inline bool operator==(const dynamic_bitset& a,
+ const dynamic_bitset& b)
+{
+ using namespace std;
+ return (a.m_num_bits == b.m_num_bits) &&
+ ((a.m_num_bits == 0) ||
+ !memcmp(a.m_bits, b.m_bits, a.m_num_blocks * sizeof(a.m_bits[0])));
+}
+
+inline bool operator!=(const dynamic_bitset& a,
+ const dynamic_bitset& b)
+{
+ return !(a == b);
+}
+
+inline bool operator<(const dynamic_bitset& a,
+ const dynamic_bitset& b)
+{
+ AssertIf(a.size() != b.size());
+ typedef dynamic_bitset::size_type size_type;
+
+ if (a.size() == 0)
+ return false;
+
+ // Since we are storing the most significant bit
+ // at pos == size() - 1, we need to do the memcmp in reverse.
+
+ // Compare a block at a time
+ for (size_type i = a.m_num_blocks - 1; i > 0; --i)
+ if (a.m_bits[i] < b.m_bits[i])
+ return true;
+ else if (a.m_bits[i] > b.m_bits[i])
+ return false;
+
+ if (a.m_bits[0] < b.m_bits[0])
+ return true;
+ else
+ return false;
+}
+
+inline bool operator<=(const dynamic_bitset& a,
+ const dynamic_bitset& b)
+{
+ return !(a > b);
+}
+
+inline bool operator>(const dynamic_bitset& a,
+ const dynamic_bitset& b)
+{
+ AssertIf(a.size() != b.size());
+ typedef dynamic_bitset::size_type size_type;
+
+ if (a.size() == 0)
+ return false;
+
+ // Since we are storing the most significant bit
+ // at pos == size() - 1, we need to do the memcmp in reverse.
+
+ // Compare a block at a time
+ for (size_type i = a.m_num_blocks - 1; i > 0; --i)
+ if (a.m_bits[i] < b.m_bits[i])
+ return false;
+ else if (a.m_bits[i] > b.m_bits[i])
+ return true;
+
+ if (a.m_bits[0] > b.m_bits[0])
+ return true;
+ else
+ return false;
+}
+
+inline bool operator>=(const dynamic_bitset& a,
+ const dynamic_bitset& b)
+{
+ return !(a < b);
+}
+
+//-----------------------------------------------------------------------------
+// bitset operations
+
+inline dynamic_bitset
+operator&(const dynamic_bitset& x,
+ const dynamic_bitset& y)
+{
+ dynamic_bitset b(x);
+ return b &= y;
+}
+
+inline dynamic_bitset
+operator|(const dynamic_bitset& x,
+ const dynamic_bitset& y)
+{
+ dynamic_bitset b(x);
+ return b |= y;
+}
+
+inline dynamic_bitset
+operator^(const dynamic_bitset& x,
+ const dynamic_bitset& y)
+{
+ dynamic_bitset b(x);
+ return b ^= y;
+}
+
+inline dynamic_bitset
+operator-(const dynamic_bitset& x,
+ const dynamic_bitset& y)
+{
+ dynamic_bitset b(x);
+ return b -= y;
+}
+
+
+//-----------------------------------------------------------------------------
+// private member functions
+
+inline void dynamic_bitset::
+set_(size_type bit)
+{
+ this->m_bits[this->word(bit)] |= this->mask1(bit);
+}
+
+inline void dynamic_bitset::
+set_block_(size_type blocknum, Block value)
+{
+ this->m_bits[blocknum] = value;
+}
+
+inline void dynamic_bitset::
+reset_(size_type b)
+{
+ this->m_bits[this->word(b)] &= this->mask0(b);
+}
+
+inline bool dynamic_bitset::test_(size_type b) const
+{
+ return (this->m_bits[this->word(b)] & this->mask1(b)) != static_cast<Block>(0);
+}
+
+inline bool dynamic_bitset::set_(size_type n, bool value)
+{
+ if (value)
+ set_(n);
+ else
+ reset_(n);
+ return value != static_cast<Block>(0);
+}
+
+
+// If size() is not a multiple of bits_per_block
+// then not all the bits in the last block are used.
+// This function resets the unused bits (convenient
+// for the implementation of many member functions)
+//
+inline void dynamic_bitset::m_zero_unused_bits()
+{
+ AssertIf (this->m_num_blocks != this->calc_num_blocks(this->m_num_bits));
+
+ // if != 0 this is the number of bits used in the last block
+ const size_type used_bits = this->m_num_bits % bits_per_block;
+
+ if (used_bits != 0)
+ this->m_bits[this->m_num_blocks - 1] &= ~(~static_cast<Block>(0) << used_bits);
+
+}
+
+#endif
diff --git a/Runtime/Utilities/dynamic_block_vector.h b/Runtime/Utilities/dynamic_block_vector.h
new file mode 100644
index 0000000..302847b
--- /dev/null
+++ b/Runtime/Utilities/dynamic_block_vector.h
@@ -0,0 +1,135 @@
+#pragma once
+
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+// dynamic_block_vector
+//
+// Allocates dynamic_arrays to hold the data in small blocks.
+// Growing pushbacks allocates one new block at a time.
+// Calls inplace constructor on all elements, and destroys the elements when removing from the list
+// Resize preserves the elements in the list and pushes default initialized elements to reach size
+// If resizing to something smaller, elements are popped (and destroyed) from the vector
+
+
+template <typename T>
+struct dynamic_block_vector
+{
+private:
+ typedef dynamic_array<T> internal_container;
+ typedef dynamic_array<internal_container*> container;
+
+public:
+
+ dynamic_block_vector (size_t allocationBlockSize)
+ : m_blockSize(allocationBlockSize), m_size(0), m_label(kMemDynamicArrayId, GET_CURRENT_ALLOC_ROOT_HEADER())
+ {
+ }
+
+ dynamic_block_vector (size_t allocationBlockSize, MemLabelId label)
+ : m_blockSize(allocationBlockSize), m_size(0), m_label(label)
+ {
+ }
+
+ dynamic_block_vector (const dynamic_block_vector& rhs)
+ : m_blockSize(rhs.m_blockSize), m_size(0), m_label(rhs.m_label)
+ {
+ *this = rhs;
+ }
+
+ ~dynamic_block_vector ()
+ {
+ clear();
+ }
+
+ void clear()
+ {
+ for(int i = 0; i < m_size; i++)
+ (*this)[i].~T();
+
+ for(int i = 0; i < m_data.size(); i++)
+ UNITY_DELETE(m_data[i],m_label);
+
+ m_data.clear();
+ m_size = 0;
+ }
+
+ void resize (size_t size)
+ {
+ while (m_size < size)
+ push_back();
+ while (m_size > size)
+ pop_back();
+ }
+
+ dynamic_block_vector& operator=(const dynamic_block_vector& other)
+ {
+ if(this == &other)
+ return *this;
+
+ clear();
+ for( int i = 0; i < other.size(); i++)
+ push_back(other[i]);
+ return *this;
+ }
+
+ template<class Iter>
+ void assign (Iter first, Iter last)
+ {
+ clear();
+ for( ; first != last; ++first)
+ push_back(*first);
+ }
+
+ void push_back ()
+ {
+ int outerindex = m_size/m_blockSize;
+ int innerindex = m_size%m_blockSize;
+ if(outerindex == m_data.size())
+ {
+ m_data.push_back(UNITY_NEW(internal_container,m_label)(m_blockSize,m_label));
+ }
+ new (&(*m_data[outerindex])[innerindex]) T();
+ m_size++;
+ }
+
+ void push_back (const T& t)
+ {
+ int outerindex = m_size/m_blockSize;
+ int innerindex = m_size%m_blockSize;
+ if(outerindex == m_data.size())
+ {
+ m_data.push_back(UNITY_NEW(internal_container,m_label)(m_blockSize,m_label));
+ }
+ new (&(*m_data[outerindex])[innerindex]) T(t);
+ m_size++;
+ }
+
+ void pop_back ()
+ {
+ (*this)[m_size-1].~T();
+ m_size--;
+ int outersize = m_size/m_blockSize + 1;
+ if (outersize < m_data.size())
+ {
+ UNITY_DELETE(m_data.back(),m_label);
+ m_data.pop_back();
+ }
+ }
+
+ size_t size () const { return m_size; }
+
+ T& back() { Assert (m_size != 0); return (*this)[m_size - 1]; }
+
+ T const& operator[] (size_t index) const { DebugAssert(index < m_size); return (*m_data[index/m_blockSize])[index%m_blockSize]; }
+ T& operator[] (size_t index) { DebugAssert(index < m_size); return (*m_data[index/m_blockSize])[index%m_blockSize]; }
+
+private:
+
+ container m_data;
+ MemLabelId m_label;
+ size_t m_size;
+ size_t m_blockSize;
+};
+
+
diff --git a/Runtime/Utilities/fixed_bitset.h b/Runtime/Utilities/fixed_bitset.h
new file mode 100644
index 0000000..b805ae7
--- /dev/null
+++ b/Runtime/Utilities/fixed_bitset.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include "Runtime/Utilities/StaticAssert.h"
+
+
+// Fixed size bitset; size (N) must be a multiple of 32.
+// Similar to dynamic_bitset, but does not do dynamic allocations and stuff.
+template<int N>
+class fixed_bitset {
+public:
+ enum { kBlockSize = 32, kBlockCount = N/kBlockSize };
+public:
+ fixed_bitset()
+ {
+ CompileTimeAssert(N % kBlockSize == 0, "size should be multiple fo 32");
+ CompileTimeAssert(sizeof(m_Bits[0])*8 == kBlockSize, "size of internal array type should be 4" );
+ for( int i = 0; i < kBlockCount; ++i )
+ m_Bits[i] = 0;
+ }
+ // note: default copy constructor and assignment operator are ok
+
+ void set( int index ) {
+ AssertIf( index < 0 || index >= N );
+ m_Bits[index/kBlockSize] |= 1 << (index & (kBlockSize-1));
+ }
+ void reset( int index ) {
+ AssertIf( index < 0 || index >= N );
+ m_Bits[index/kBlockSize] &= ~( 1 << (index & (kBlockSize-1)) );
+ }
+ bool test( int index ) const {
+ AssertIf( index < 0 || index >= N );
+ return m_Bits[index/kBlockSize] & ( 1 << (index & (kBlockSize-1)) );
+ }
+ void reset_all() {
+ memset( m_Bits, 0, sizeof(m_Bits) );
+ }
+
+ bool operator==( const fixed_bitset<N>& o ) const {
+ for( int i = 0; i < kBlockCount; ++i )
+ if( m_Bits[i] != o.m_Bits[i] )
+ return false;
+ return true;
+ }
+ bool operator!=( const fixed_bitset<N>& o ) const {
+ for( int i = 0; i < kBlockCount; ++i )
+ if( m_Bits[i] == o.m_Bits[i] )
+ return false;
+ return true;
+ }
+
+private:
+ UInt32 m_Bits[kBlockCount];
+};
diff --git a/Runtime/Utilities/sorted_vector.h b/Runtime/Utilities/sorted_vector.h
new file mode 100644
index 0000000..5f0576f
--- /dev/null
+++ b/Runtime/Utilities/sorted_vector.h
@@ -0,0 +1,358 @@
+#ifndef SORTED_VECTOR_H
+#define SORTED_VECTOR_H
+#include <vector>
+#include <algorithm>
+
+/// container optimization anschauen (compressed pair for valuecompare)
+
+
+template<class T, class Compare, class Allocator>
+class sorted_vector : private Compare
+{
+ public:
+
+ typedef std::vector<T, Allocator> container;
+ typedef typename container::iterator iterator;
+ typedef typename container::const_iterator const_iterator;
+ typedef typename container::reverse_iterator reverse_iterator;
+ typedef typename container::const_reverse_iterator const_reverse_iterator;
+ typedef typename container::value_type value_type;
+ typedef typename container::size_type size_type;
+ typedef typename container::difference_type difference_type;
+ typedef Compare value_compare;
+ typedef typename Allocator::reference reference;
+ typedef typename Allocator::const_reference const_reference;
+ typedef Allocator allocator_type;
+
+ value_compare const& get_compare () const { return *static_cast<value_compare const*> (this); }
+
+
+ sorted_vector (const Compare& comp, const Allocator& a)
+ : c (a), value_compare (comp) {}
+
+
+ bool empty() const { return c.empty(); }
+ size_type size() const { return c.size(); }
+ const value_type& front() const { return c.front(); }
+ const value_type& back() const { return c.front(); }
+
+ const_iterator begin ()const { return c.begin (); }
+ const_iterator end ()const { return c.end (); }
+ iterator begin () { return c.begin (); }
+ iterator end () { return c.end (); }
+ const_reverse_iterator rbegin ()const { return c.rbegin (); }
+ const_reverse_iterator rend ()const { return c.rend (); }
+ reverse_iterator rbegin () { return c.rbegin (); }
+ reverse_iterator rend () { return c.rend (); }
+
+ value_compare value_comp() const { return get_compare ();}
+/*
+ template <class InputIterator>
+ void insert_one (InputIterator first, InputIterator last)
+ {
+ c.reserve (size () + std::distance (first, last));
+ for (;first != last;first++)
+ insert_one (*i);
+ }
+
+ template<typename KeyT, typename MappedT>
+ MappedT& find_or_insert (const KeyT& k)
+ {
+ iterator i = lower_bound (k);
+ if (i == end () || get_compare () (k, *i))
+ return c.insert (i, std::make_pair<KeyT, MappedT> (k, MappedT ()))->second;
+ else
+ return i->second;
+ }
+*/
+
+ template<typename KeyT, typename MappedT>
+ void find_or_insert (MappedT*& mappedT, const KeyT& k)
+ {
+ iterator i = lower_bound (k);
+ if (i == end () || get_compare () (k, *i))
+ #if _MSC_VER >= 1700 // Microsoft Visual Studio 2012 workaround
+ mappedT = &c.insert (i, std::make_pair<KeyT, MappedT> (static_cast<KeyT&&>(const_cast<KeyT&>(k)), MappedT ()))->second;
+ #else
+ mappedT = &c.insert (i, std::make_pair<KeyT, MappedT> (k, MappedT ()))->second;
+ #endif
+ else
+ mappedT = &i->second;
+ }
+
+ std::pair<iterator, bool> insert_one (const value_type& x)
+ {
+ iterator i = lower_bound (x);
+ // is not included in container
+ if (i == end () || get_compare () (x, *i))
+ return std::make_pair (c.insert (i, x), true);
+ else
+ return std::make_pair (i, false);
+
+ }
+
+ void verify_duplicates_and_sorted () const
+ {
+ #if DEBUGMODE
+ // Check that there are no duplicates in the set
+ if (empty())
+ return;
+
+ const_iterator previous = c.begin();
+ const_iterator i = c.begin();
+ i++;
+ for (; i != c.end();++i)
+ {
+ Assert(get_compare ()(*previous, *i));
+ previous = i;
+ }
+ #endif
+ }
+
+
+ template<class CompareType>
+ size_type erase_one (const CompareType& x)
+ {
+ iterator i = lower_bound (x);
+ if (i == end () || get_compare () (x, *i))
+ return 0;
+ else
+ {
+ c.erase (i);
+ return 1;
+ }
+ }
+
+ void erase(iterator position) { c.erase (position);}
+ void erase(iterator first, iterator last) { c.erase (first, last); }
+ void swap(sorted_vector& x) { c.swap (x.c); }
+
+ void clear () { c.clear (); }
+
+ template<class T2>
+ size_type count_one (const T2& x)const
+ {
+ const_iterator i = lower_bound (x);
+ if (i == end () || get_compare () (x, *i))
+ return 0;
+ else
+ return 1;
+ }
+
+ template<class T2>
+ iterator find (const T2& x)
+ {
+ iterator i = lower_bound (x);
+ if (i == end () || get_compare () (x, *i))
+ return end ();
+ else
+ return i;
+ }
+
+ template<class T2>
+ const_iterator find (const T2& x) const
+ {
+ const_iterator i = lower_bound (x);
+ if (i == end () || get_compare () (x, *i))
+ return end ();
+ else
+ return i;
+ }
+
+ template<class T2>
+ iterator lower_bound (const T2& x)
+ {
+ return std::lower_bound (c.begin (), c.end (), x, get_compare ());
+ }
+
+ template<class T2>
+ const_iterator lower_bound (const T2& x) const
+ {
+ return std::lower_bound (c.begin (), c.end (), x, get_compare ());
+ }
+
+ template<class T2>
+ iterator upper_bound (const T2& x)
+ {
+ return std::lower_bound (c.begin (), c.end (), x, get_compare ());
+ }
+
+ template<class T2>
+ const_iterator upper_bound (const T2& x) const
+ {
+ return std::lower_bound (c.begin (), c.end (), x, get_compare ());
+ }
+
+ template<class T2>
+ std::pair<iterator, iterator> equal_range (const T2& x)
+ {
+ return std::equal_range (c.begin (), c.end (), x, get_compare ());
+ }
+
+ template<class T2>
+ std::pair<const_iterator, const_iterator> equal_range (const T2& x) const
+ {
+ return std::equal_range (c.begin (), c.end (), x, get_compare ());
+ }
+
+ void reserve (size_type n) { c.reserve (n); }
+
+ value_type& operator [] (int n) { return c[n]; }
+ const value_type& operator [] (int n) const { return c[n]; }
+
+ public:
+
+ container c;
+};
+
+template<class T, class Compare, class Allocator>
+class unsorted_vector : private Compare
+{
+ public:
+
+ typedef std::vector<T, Allocator> container;
+ typedef typename container::iterator iterator;
+ typedef typename container::const_iterator const_iterator;
+ typedef typename container::reverse_iterator reverse_iterator;
+ typedef typename container::const_reverse_iterator const_reverse_iterator;
+
+ typedef typename container::value_type value_type;
+ typedef typename container::size_type size_type;
+ typedef typename container::difference_type difference_type;
+ typedef Compare value_compare;
+ typedef typename Allocator::reference reference;
+ typedef typename Allocator::const_reference const_reference;
+ typedef Allocator allocator_type;
+
+ value_compare const& get_compare () const { return *static_cast<value_compare const*> (this); }
+
+ unsorted_vector (const Compare& comp, const Allocator& a)
+ : c (a), value_compare (comp) {}
+
+
+ bool empty() const { return c.empty(); }
+ size_type size() const { return c.size(); }
+ const value_type& front() const { return c.front(); }
+ const value_type& back() const { return c.front(); }
+
+ const_iterator begin ()const { return c.begin (); }
+ const_iterator end ()const { return c.end (); }
+ iterator begin () { return c.begin (); }
+ iterator end () { return c.end (); }
+ const_reverse_iterator rbegin ()const { return c.rbegin (); }
+ const_reverse_iterator rend ()const { return c.rend (); }
+ reverse_iterator rbegin () { return c.rbegin (); }
+ reverse_iterator rend () { return c.rend (); }
+
+ value_compare value_comp() const { return get_compare ();}
+/*
+ template <class InputIterator>
+ void insert_one (InputIterator first, InputIterator last)
+ {
+ c.reserve (size () + std::distance (first, last));
+ for (;first != last;first++)
+ insert_one (*i);
+ }
+*/
+ template<typename KeyT, typename MappedT>
+ void find_or_insert (MappedT*& mappedT, const KeyT& k)
+ {
+ iterator i = find (k);
+ if (i == end ())
+ {
+ c.push_back (std::make_pair<KeyT, MappedT> (k, MappedT ()));
+ mappedT = &(c.end () - 1)->second;
+ }
+ else
+ mappedT = &i->second;
+ }
+ /*
+ template<class Key, class Value>
+ Value& find_or_insert (const Key& k)
+ {
+ iterator i = find (k);
+ if (i == end ())
+ {
+ c.push_back (std::make_pair<Key, Value> (k, Value ()));
+ return (c.end () - 1)->second;
+ }
+ else
+ return i->second;
+ }
+ */
+ std::pair<iterator, bool> insert_one (const value_type& x)
+ {
+ iterator i = find (x);
+ // is not included in container
+ if (i == end ())
+ {
+ c.push_back (x);
+ return std::make_pair (c.end () - 1, true);
+ }
+ else
+ return std::make_pair (i, false);
+ }
+
+ template<class CompareType>
+ size_type erase_one (const CompareType& x)
+ {
+ iterator i = find (x);
+ if (i == end ())
+ return 0;
+ else
+ {
+ erase (i);
+ return 1;
+ }
+ }
+
+ void erase(iterator position) { *position = c.back (); c.pop_back (); }
+ void swap(unsorted_vector& x) { c.swap (x.c); }
+
+ void clear () { c.clear (); }
+
+ template<class T2>
+ size_type count_one (const T2& x)const
+ {
+ const_iterator i = find (x);
+ return i != end ();
+ }
+
+ template<class T2>
+ iterator find (const T2& x)
+ {
+ iterator b = c.begin ();
+ iterator e = c.end ();
+ for (;b != e;++b)
+ {
+ if (get_compare () (*b, x))
+ return b;
+ }
+ return e;
+ }
+
+ template<class T2>
+ const_iterator find (const T2& x) const
+ {
+ {
+ const_iterator b = c.begin ();
+ const_iterator e = c.end ();
+ for (;b != e;++b)
+ {
+ if (get_compare () (*b, x))
+ return b;
+ }
+ return e;
+ }
+
+ }
+ void reserve (size_type n) { c.reserve (n); }
+ value_type& operator [] (int n) { return c[n]; }
+ const value_type& operator [] (int n) const { return c[n]; }
+
+ public:
+
+ container c;
+};
+
+#endif
diff --git a/Runtime/Utilities/triple.h b/Runtime/Utilities/triple.h
new file mode 100644
index 0000000..4a3d5c1
--- /dev/null
+++ b/Runtime/Utilities/triple.h
@@ -0,0 +1,46 @@
+#pragma once
+
+// TEMPLATE STRUCT triple
+template<class T>
+struct triple
+{
+ // store a triple of values of the same type
+
+ triple()
+ : first(T()), second(T()), third(T())
+ { // construct from defaults
+ }
+
+ triple(const T& _Val1, const T& _Val2, const T& _Val3)
+ : first(_Val1), second(_Val2), third(_Val3)
+ { // construct from specified values
+ }
+
+ template<class otherT>
+ triple(const triple<otherT>& _Right)
+ : first(_Right.first), second(_Right.second), third(_Right.third)
+ { // construct from a compatible triple
+ }
+
+ T first; // the first stored value
+ T second; // the second stored value
+ T third; // the third stored value
+};
+
+template<class T>
+inline bool operator==(const triple<T>& _Left, const triple<T>& _Right)
+{ // test for triple equality
+ return (_Left.first == _Right.first && _Left.second == _Right.second && _Left.third == _Right.third);
+}
+
+template<class T>
+inline bool operator!=(const triple<T>& _Left, const triple<T>& _Right)
+{ // test for triple inequality
+ return (!(_Left == _Right));
+}
+
+template<class T>
+inline triple<T> make_triple(T _Val1, T _Val2, T _Val3)
+{ // return a triple composed from arguments
+ return (triple<T>(_Val1, _Val2, _Val3));
+} \ No newline at end of file
diff --git a/Runtime/Utilities/type_traits.h b/Runtime/Utilities/type_traits.h
new file mode 100644
index 0000000..edffc34
--- /dev/null
+++ b/Runtime/Utilities/type_traits.h
@@ -0,0 +1,250 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ----
+// Author: Matt Austern
+//
+// Define a small subset of tr1 type traits. The traits we define are:
+// is_integral
+// is_floating_point
+// is_pointer
+// is_reference
+// is_pod
+// has_trivial_constructor
+// has_trivial_copy
+// has_trivial_assign
+// has_trivial_destructor
+// remove_const
+// remove_volatile
+// remove_cv
+// remove_reference
+// remove_pointer
+// is_convertible
+// We can add more type traits as required.
+
+#ifndef BASE_TYPE_TRAITS_H_
+#define BASE_TYPE_TRAITS_H_
+
+//#include <google/sparsehash/sparseconfig.h>
+#include <utility> // For pair
+
+namespace dense_hash_map_traits
+{
+
+// integral_constant, defined in tr1, is a wrapper for an integer
+// value. We don't really need this generality; we could get away
+// with hardcoding the integer type to bool. We use the fully
+// general integer_constant for compatibility with tr1.
+
+template<class T, T v>
+struct integral_constant {
+ static const T value = v;
+ typedef T value_type;
+ typedef integral_constant<T, v> type;
+};
+
+template <class T, T v> const T integral_constant<T, v>::value;
+
+// Abbreviations: true_type and false_type are structs that represent
+// boolean true and false values.
+typedef integral_constant<bool, true> true_type;
+typedef integral_constant<bool, false> false_type;
+
+// Types small_ and big_ are guaranteed such that sizeof(small_) <
+// sizeof(big_)
+typedef char small_;
+
+struct big_ {
+ char dummy[2];
+};
+
+// is_integral is false except for the built-in integer types.
+template <class T> struct is_integral : false_type { };
+template<> struct is_integral<bool> : true_type { };
+template<> struct is_integral<char> : true_type { };
+template<> struct is_integral<unsigned char> : true_type { };
+template<> struct is_integral<signed char> : true_type { };
+#if defined(_MSC_VER)
+// wchar_t is not by default a distinct type from unsigned short in
+// Microsoft C.
+// See http://msdn2.microsoft.com/en-us/library/dh8che7s(VS.80).aspx
+template<> struct is_integral<__wchar_t> : true_type { };
+#else
+template<> struct is_integral<wchar_t> : true_type { };
+#endif
+template<> struct is_integral<short> : true_type { };
+template<> struct is_integral<unsigned short> : true_type { };
+template<> struct is_integral<int> : true_type { };
+template<> struct is_integral<unsigned int> : true_type { };
+template<> struct is_integral<long> : true_type { };
+template<> struct is_integral<unsigned long> : true_type { };
+#ifdef HAVE_LONG_LONG
+template<> struct is_integral<long long> : true_type { };
+template<> struct is_integral<unsigned long long> : true_type { };
+#endif
+
+
+// is_floating_point is false except for the built-in floating-point types.
+template <class T> struct is_floating_point : false_type { };
+template<> struct is_floating_point<float> : true_type { };
+template<> struct is_floating_point<double> : true_type { };
+template<> struct is_floating_point<long double> : true_type { };
+
+
+// is_pointer is false except for pointer types.
+template <class T> struct is_pointer : false_type { };
+template <class T> struct is_pointer<T*> : true_type { };
+
+
+// is_reference is false except for reference types.
+template<typename T> struct is_reference : false_type {};
+template<typename T> struct is_reference<T&> : true_type {};
+
+
+// We can't get is_pod right without compiler help, so fail conservatively.
+// We will assume it's false except for arithmetic types and pointers,
+// and const versions thereof. Note that std::pair is not a POD.
+template <class T> struct is_pod
+ : integral_constant<bool, (is_integral<T>::value ||
+ is_floating_point<T>::value ||
+ is_pointer<T>::value)> { };
+template <class T> struct is_pod<const T> : is_pod<T> { };
+
+
+// We can't get has_trivial_constructor right without compiler help, so
+// fail conservatively. We will assume it's false except for: (1) types
+// for which is_pod is true. (2) std::pair of types with trivial
+// constructors. (3) array of a type with a trivial constructor.
+// (4) const versions thereof.
+template <class T> struct has_trivial_constructor : is_pod<T> { };
+template <class T, class U> struct has_trivial_constructor<std::pair<T, U> >
+ : integral_constant<bool,
+ (has_trivial_constructor<T>::value &&
+ has_trivial_constructor<U>::value)> { };
+template <class A, int N> struct has_trivial_constructor<A[N]>
+ : has_trivial_constructor<A> { };
+template <class T> struct has_trivial_constructor<const T>
+ : has_trivial_constructor<T> { };
+
+// We can't get has_trivial_copy right without compiler help, so fail
+// conservatively. We will assume it's false except for: (1) types
+// for which is_pod is true. (2) std::pair of types with trivial copy
+// constructors. (3) array of a type with a trivial copy constructor.
+// (4) const versions thereof.
+template <class T> struct has_trivial_copy : is_pod<T> { };
+template <class T, class U> struct has_trivial_copy<std::pair<T, U> >
+ : integral_constant<bool,
+ (has_trivial_copy<T>::value &&
+ has_trivial_copy<U>::value)> { };
+template <class A, int N> struct has_trivial_copy<A[N]>
+ : has_trivial_copy<A> { };
+template <class T> struct has_trivial_copy<const T> : has_trivial_copy<T> { };
+
+// We can't get has_trivial_assign right without compiler help, so fail
+// conservatively. We will assume it's false except for: (1) types
+// for which is_pod is true. (2) std::pair of types with trivial copy
+// constructors. (3) array of a type with a trivial assign constructor.
+template <class T> struct has_trivial_assign : is_pod<T> { };
+template <class T, class U> struct has_trivial_assign<std::pair<T, U> >
+ : integral_constant<bool,
+ (has_trivial_assign<T>::value &&
+ has_trivial_assign<U>::value)> { };
+template <class A, int N> struct has_trivial_assign<A[N]>
+ : has_trivial_assign<A> { };
+
+// We can't get has_trivial_destructor right without compiler help, so
+// fail conservatively. We will assume it's false except for: (1) types
+// for which is_pod is true. (2) std::pair of types with trivial
+// destructors. (3) array of a type with a trivial destructor.
+// (4) const versions thereof.
+template <class T> struct has_trivial_destructor : is_pod<T> { };
+template <class T, class U> struct has_trivial_destructor<std::pair<T, U> >
+ : integral_constant<bool,
+ (has_trivial_destructor<T>::value &&
+ has_trivial_destructor<U>::value)> { };
+template <class A, int N> struct has_trivial_destructor<A[N]>
+ : has_trivial_destructor<A> { };
+template <class T> struct has_trivial_destructor<const T>
+ : has_trivial_destructor<T> { };
+
+// Specified by TR1 [4.7.1]
+template<typename T> struct remove_const { typedef T type; };
+template<typename T> struct remove_const<T const> { typedef T type; };
+template<typename T> struct remove_volatile { typedef T type; };
+template<typename T> struct remove_volatile<T volatile> { typedef T type; };
+template<typename T> struct remove_cv {
+ typedef typename remove_const<typename remove_volatile<T>::type>::type type;
+};
+
+
+// Specified by TR1 [4.7.2]
+template<typename T> struct remove_reference { typedef T type; };
+template<typename T> struct remove_reference<T&> { typedef T type; };
+
+// Specified by TR1 [4.7.4] Pointer modifications.
+template<typename T> struct remove_pointer { typedef T type; };
+template<typename T> struct remove_pointer<T*> { typedef T type; };
+template<typename T> struct remove_pointer<T* const> { typedef T type; };
+template<typename T> struct remove_pointer<T* volatile> { typedef T type; };
+template<typename T> struct remove_pointer<T* const volatile> {
+ typedef T type; };
+
+// Specified by TR1 [4.6] Relationships between types
+#ifndef _MSC_VER
+namespace internal_type_traits {
+
+// This class is an implementation detail for is_convertible, and you
+// don't need to know how it works to use is_convertible. For those
+// who care: we declare two different functions, one whose argument is
+// of type To and one with a variadic argument list. We give them
+// return types of different size, so we can use sizeof to trick the
+// compiler into telling us which function it would have chosen if we
+// had called it with an argument of type From. See Alexandrescu's
+// _Modern C++ Design_ for more details on this sort of trick.
+
+template <typename From, typename To>
+struct ConvertHelper {
+ static small_ Test(To);
+ static big_ Test(...);
+ static From Create();
+};
+} // namespace internal
+
+// Inherits from true_type if From is convertible to To, false_type otherwise.
+template <typename From, typename To>
+struct is_convertible
+ : integral_constant<bool,
+ sizeof(internal_type_traits::ConvertHelper<From, To>::Test(
+ internal_type_traits::ConvertHelper<From, To>::Create()))
+ == sizeof(small_)> {
+};
+#endif
+
+}
+#endif // BASE_TYPE_TRAITS_H_
diff --git a/Runtime/Utilities/vector_map.h b/Runtime/Utilities/vector_map.h
new file mode 100644
index 0000000..9d3c936
--- /dev/null
+++ b/Runtime/Utilities/vector_map.h
@@ -0,0 +1,268 @@
+#ifndef VECTOR_MAP_H
+#define VECTOR_MAP_H
+
+#include "sorted_vector.h"
+#include <functional>
+
+// vector_map offers the same functionality as std::set
+// but it is implemented using sorted vectors.
+// sorted_vectors are smaller in used memory and can be faster due to cache coherence
+// However inserting or erasing elements can be O (N) instead of O (logN)
+// Usually you will want to use vector_set when you have a set which you use
+// much more often to find values than inserting them or if the set you use is very small
+// vector_map also offers the vector function reserve.
+// - also note that if you store an iterator to an element you are NOT guaranteed that this iterator
+// remains valid after you insert/erase other elements
+// - vector_map«s key is not const, but you are still not allowed to change the key without erasing/inserting it.
+
+
+template<class Key, class T, class Compare = std::less<Key>,
+ class Allocator = std::allocator<std::pair<Key, T> > >
+class vector_map
+{
+ public:
+
+ typedef Key key_type;
+ typedef T mapped_type;
+ typedef std::pair<Key,T> value_type;
+ typedef Compare key_compare;
+ typedef Allocator allocator_type;
+ typedef typename Allocator::reference reference;
+ typedef typename Allocator::const_reference const_reference;
+ typedef typename Allocator::size_type size_type;
+ typedef typename Allocator::difference_type difference_type;
+ typedef typename Allocator::pointer pointer;
+ typedef typename Allocator::const_pointer const_pointer;
+
+ class value_compare
+ : public std::binary_function<value_type,value_type,bool>
+ {
+ public:
+ bool operator()(const value_type& x, const value_type& y) const
+ {
+ return comp(x.first, y.first);
+ }
+ bool operator()(const key_type& x, const value_type& y) const
+ {
+ return comp(x, y.first);
+ }
+ bool operator()(const value_type& x, const key_type& y) const
+ {
+ return comp(x.first, y);
+ }
+
+ value_compare() {}
+ value_compare(Compare c) : comp(c) {}
+ protected:
+ Compare comp;
+
+
+ friend class vector_map;
+ };
+
+ typedef sorted_vector<value_type, value_compare, Allocator> container;
+ typedef typename container::container vector_container;
+ typedef typename container::iterator iterator;
+ typedef typename container::const_iterator const_iterator;
+ typedef typename container::reverse_iterator reverse_iterator;
+ typedef typename container::const_reverse_iterator const_reverse_iterator;
+
+ public:
+
+ // ctors
+ vector_map (const Compare& comp = Compare (), const Allocator& a = Allocator ())
+ : c (value_compare(comp), a)
+ { }
+
+ template <class InputIterator>
+ vector_map(InputIterator first, InputIterator last, const Compare& comp = Compare (), const Allocator& a = Allocator ())
+ : c (value_compare(comp), a)
+ { insert_one (first, last); }
+
+ // iterators
+ iterator begin() { return c.begin (); }
+ const_iterator begin() const { return c.begin (); }
+ iterator end() { return c.end (); }
+ const_iterator end() const { return c.end (); }
+ reverse_iterator rbegin() { return c.rbegin (); }
+ const_reverse_iterator rbegin() const { return c.rbegin (); }
+ reverse_iterator rend() { return c.rend (); }
+ const_reverse_iterator rend() const { return c.rend (); }
+
+ // capacity:
+ bool empty() const { return c.empty (); }
+ size_type size() const { return c.size (); }
+ size_type max_size() const { return c.max_size (); }
+
+ // modifiers:
+ std::pair<iterator, bool> insert(const value_type& x) { return c.insert_one (x); }
+
+ template <class InputIterator>
+ void insert(InputIterator first, InputIterator last) { c.insert_one(first, last); }
+
+ void erase(iterator position) { c.erase (position); }
+ size_type erase(const key_type& x) { return c.erase_one (x); }
+ void erase(iterator first, iterator last) { c.erase (first, last); }
+ void swap(vector_map& x) { c.swap (x.c); }
+ void clear() { c.clear (); }
+
+ // observers:
+ key_compare key_comp() const { return c.value_comp ().comp; }
+ value_compare value_comp() const { return c.value_comp (); }
+
+ // lib.map.ops map operations:
+ // CW has problems with the straightforward version
+// mapped_type& operator[] (const key_type& x) { return c.find_or_insert<key_type, mapped_type> (x); }
+ mapped_type& operator[] (const key_type& x) { mapped_type* temp; c.find_or_insert (temp, x); return *temp; }
+
+ iterator find(const key_type& x) { return c.find (x); }
+ const_iterator find(const key_type& x) const { return c.find (x); }
+ size_type count(const key_type& x) const { return c.count_one (x); }
+
+ iterator lower_bound(const key_type& x) { return c.lower_bound (x); }
+ const_iterator lower_bound(const key_type& x) const{ return c.lower_bound (x); }
+ iterator upper_bound(const key_type& x) { return c.upper_bound (x); }
+ const_iterator upper_bound(const key_type& x) const{ return c.upper_bound (x); }
+
+ std::pair<iterator,iterator> equal_range(const key_type& x) { return c.equal_range (x); }
+ std::pair<const_iterator,const_iterator> equal_range(const key_type& x) const { return c.equal_range (x); }
+
+ // vector specific operations
+ void reserve (size_type n) { c.reserve (n); }
+
+ vector_container& get_vector () { return c.c; }
+
+ void push_unsorted (const key_type& x, const mapped_type& value)
+ {
+ get_vector().push_back(std::make_pair(x, value));
+ }
+
+ void sort ()
+ {
+ std::sort(c.c.begin(), c.c.end(), c.value_comp ());
+ c.verify_duplicates_and_sorted ();
+ }
+
+ void verify_duplicates_and_sorted () const
+ {
+ c.verify_duplicates_and_sorted ();
+ }
+
+ private:
+
+ container c;
+};
+
+template<class Key, class T, class Compare = std::equal_to<Key>,
+ class Allocator = std::allocator<std::pair<Key, T> > >
+class us_vector_map
+{
+ public:
+
+ typedef Key key_type;
+ typedef T mapped_type;
+ typedef std::pair<Key,T> value_type;
+ typedef Compare key_compare;
+ typedef Allocator allocator_type;
+ typedef typename Allocator::reference reference;
+ typedef typename Allocator::const_reference const_reference;
+ typedef typename Allocator::size_type size_type;
+ typedef typename Allocator::difference_type difference_type;
+ typedef typename Allocator::pointer pointer;
+ typedef typename Allocator::const_pointer const_pointer;
+
+ class value_compare
+ : public std::binary_function<value_type,value_type,bool>
+ {
+ public:
+ bool operator()(const value_type& x, const value_type& y) const
+ {
+ return comp(x.first, y.first);
+ }
+ bool operator()(const key_type& x, const value_type& y) const
+ {
+ return comp(x, y.first);
+ }
+ bool operator()(const value_type& x, const key_type& y) const
+ {
+ return comp(x.first, y);
+ }
+ protected:
+ Compare comp;
+
+ value_compare() {}
+ value_compare(Compare c) : comp(c) {}
+
+ friend class us_vector_map;
+ };
+
+ typedef unsorted_vector<value_type, value_compare, Allocator> container;
+ typedef typename container::container vector_container;
+ typedef typename container::iterator iterator;
+ typedef typename container::const_iterator const_iterator;
+ typedef typename container::reverse_iterator reverse_iterator;
+ typedef typename container::const_reverse_iterator const_reverse_iterator;
+
+ public:
+
+ // ctors
+ us_vector_map (const Compare& comp = Compare (), const Allocator& a = Allocator ())
+ : c (value_compare(comp), a)
+ { }
+
+ template <class InputIterator>
+ us_vector_map(InputIterator first, InputIterator last, const Compare& comp = Compare (), const Allocator& a = Allocator ())
+ : c (value_compare(comp), a)
+ { insert_one (first, last); }
+
+ // iterators
+ iterator begin() { return c.begin (); }
+ const_iterator begin() const { return c.begin (); }
+ iterator end() { return c.end (); }
+ const_iterator end() const { return c.end (); }
+ reverse_iterator rbegin() { return c.rbegin (); }
+ const_reverse_iterator rbegin() const { return c.rbegin (); }
+ reverse_iterator rend() { return c.rend (); }
+ const_reverse_iterator rend() const { return c.rend (); }
+
+
+ // capacity:
+ bool empty() const { return c.empty (); }
+ size_type size() const { return c.size (); }
+ size_type max_size() const { return c.max_size (); }
+
+ // modifiers:
+ std::pair<iterator, bool> insert(const value_type& x){ return c.insert_one (x); }
+
+ template <class InputIterator>
+ void insert(InputIterator first, InputIterator last) { c.insert_one(first, last); }
+
+ void erase(iterator position) { c.erase (position); }
+ size_type erase(const key_type& x) { return c.erase_one (x); }
+ void swap(us_vector_map& x) { c.swap (x.c);}
+ void clear() { c.clear (); }
+
+ // observers:
+ key_compare key_comp() const { return c.value_comp ().comp; }
+ value_compare value_comp() const { return c.value_comp (); }
+
+ // lib.map.ops map operations:
+ // CW has problems with the straightforward version
+ // mapped_type& operator[] (const key_type& x) { return c.find_or_insert<key_type, mapped_type> (x); }
+ mapped_type& operator[] (const key_type& x) { mapped_type* temp; c.find_or_insert (temp, x); return *temp; }
+
+ iterator find(const key_type& x) { return c.find (x); }
+ const_iterator find(const key_type& x) const { return c.find (x); }
+ size_type count(const key_type& x) const { return c.count_one (x); }
+
+ // vector specific operations
+ void reserve (size_type n) { c.reserve (n); }
+
+ vector_container& get_vector () { return c.c; }
+
+ private:
+
+ container c;
+};
+
+#endif
diff --git a/Runtime/Utilities/vector_set.h b/Runtime/Utilities/vector_set.h
new file mode 100644
index 0000000..9d06e19
--- /dev/null
+++ b/Runtime/Utilities/vector_set.h
@@ -0,0 +1,229 @@
+#ifndef VECTOR_SET_H
+#define VECTOR_SET_H
+
+#include "sorted_vector.h"
+#include <functional>
+
+// vector_set offers the same functionality as std::set
+// but it is implemented using sorted vectors.
+// sorted_vectors are smaller in used memory and can be faster due to cache coherence
+// However inserting or erasing elements is O (N) instead of O (logN)
+// - Usually you will want to use vector_set when you have a set which you use
+// much more often to find values than inserting them or if the set you use is very small
+// vector_set also offers the vector function reserve.
+// - also note that if you store an iterator to an element you are NOT guaranteed that this iterator
+// remains valid after you insert/erase other elements
+
+template <class Key, class Compare = std::less<Key>, class Allocator = std::allocator<Key> >
+class vector_set
+{
+ public:
+
+ typedef sorted_vector<Key, Compare, Allocator> container;
+ typedef typename container::container vector_container;
+ typedef typename container::iterator iterator;
+ typedef typename container::const_iterator const_iterator;
+ typedef typename container::reverse_iterator reverse_iterator;
+ typedef typename container::const_reverse_iterator const_reverse_iterator;
+ typedef typename container::value_type value_type;
+ typedef typename container::value_type key_type;
+ typedef Compare key_compare;
+ typedef Compare value_compare;
+ typedef typename container::size_type size_type;
+ typedef typename container::difference_type difference_type;
+ typedef Allocator allocator_type;
+
+
+ vector_set (const Compare& comp = Compare (), const Allocator& a = Allocator())
+ : c (comp, a) { }
+
+ template <class InputIterator>
+ vector_set (InputIterator first, InputIterator last, const Compare& comp = Compare (), const Allocator& a = Allocator())
+ : c (comp, a)
+ {
+ assign(first, last);
+ }
+
+ // Assigns a range that is known to be sorted
+ template<class InputIterator>
+ void assign_sorted (InputIterator first, InputIterator last){ c.c.assign (first, last); c.verify_duplicates_and_sorted (); }
+
+ // Assigns a range
+ // Asserts if there are any duplicates in the input
+ template<class InputIterator>
+ void assign (InputIterator first, InputIterator last)
+ {
+ c.c.assign (first, last);
+
+ sort();
+ }
+
+ // Assigns a range
+ // clears any duplicate objects
+ template<class InputIterator>
+ void assign_clear_duplicates (InputIterator first, InputIterator last)
+ {
+ c.c.assign (first, last);
+ std::stable_sort(c.begin(), c.end(), c.value_comp());
+
+ // Check that there are no duplicates in the set
+ if (!empty())
+ {
+ Key* previous = &*c.begin();
+ iterator i = c.begin(); i++;
+ for (; i != c.end();)
+ {
+ if (*i < *previous || *previous < *i)
+ {
+ previous = &*i;
+ i++;
+ }
+ else
+ {
+ iterator e;
+ for (e=i;e!=c.end() && !(*e < *previous || *previous < *e);e++)
+ ;
+ c.erase(i, e);
+ }
+ }
+ }
+
+ c.verify_duplicates_and_sorted ();
+ }
+
+ bool empty () const { return c.empty (); }
+
+ iterator begin() { return c.begin (); }
+ const_iterator begin() const { return c.begin (); }
+ iterator end() { return c.end (); }
+ const_iterator end() const { return c.end (); }
+ reverse_iterator rbegin() { return c.rbegin (); }
+ const_reverse_iterator rbegin() const { return c.rbegin (); }
+ reverse_iterator rend() { return c.rend (); }
+ const_reverse_iterator rend() const { return c.rend (); }
+
+ size_type size() const { return c.size (); }
+ size_type max_size() const { return c.max_size (); }
+
+ std::pair<iterator,bool>insert(const value_type& x) { return c.insert_one (x); }
+
+ void erase(iterator position) { c.erase (position); }
+ size_type erase(const key_type& x) { return c.erase_one (x); }
+ void erase(iterator first, iterator last){ c.erase (first, last); }
+ void clear() { c.clear (); }
+
+ void swap(vector_set& x) { c.swap (x.c); }
+
+ // set operations:
+ iterator find(const key_type& x) { return c.find (x); }
+ const_iterator find(const key_type& x) const { return c.find (x); }
+ size_type count(const key_type& x) const { return c.count_one (x); }
+
+ iterator lower_bound(const key_type& x) { return c.lower_bound (x); }
+ const_iterator lower_bound(const key_type& x) const{ return c.lower_bound (x); }
+
+ iterator upper_bound(const key_type& x) { return c.upper_bound (x); }
+ const_iterator upper_bound(const key_type& x) const{ return c.upper_bound (x); }
+
+ std::pair<iterator,iterator> equal_range(const key_type& x) { return c.equal_range (x); }
+ std::pair<const_iterator, const_iterator> equal_range(const key_type& x) const { return c.equal_range (x); }
+
+ // vector specific operations
+ void reserve (size_type n) { c.reserve (n); }
+
+ value_type& operator [] (int n) { return c[n]; }
+ const value_type& operator [] (int n) const { return c[n]; }
+
+ vector_container& get_vector () { return c.c; }
+
+ void push_unsorted (const value_type& x)
+ {
+ get_vector().push_back(x);
+ }
+
+ void sort ()
+ {
+ std::sort(c.c.begin(), c.c.end(), c.value_comp ());
+ c.verify_duplicates_and_sorted ();
+ }
+
+ void verify_duplicates_and_sorted () const
+ {
+ c.verify_duplicates_and_sorted ();
+ }
+
+
+ public:
+
+ container c;
+};
+
+template <class Key, class Compare = std::equal_to<Key>, class Allocator = std::allocator<Key> >
+class us_vector_set
+{
+ public:
+
+ typedef unsorted_vector<Key, Compare, Allocator> container;
+ typedef typename container::container vector_container;
+ typedef typename container::iterator iterator;
+ typedef typename container::const_iterator const_iterator;
+ typedef typename container::reverse_iterator reverse_iterator;
+ typedef typename container::const_reverse_iterator const_reverse_iterator;
+ typedef typename container::value_type value_type;
+ typedef typename container::value_type key_type;
+ typedef Compare key_compare;
+ typedef Compare value_compare;
+ typedef typename container::size_type size_type;
+ typedef typename container::difference_type difference_type;
+ typedef Allocator allocator_type;
+
+
+ us_vector_set (const Compare& comp = Compare (), const Allocator& a = Allocator())
+ : c (comp, a) { }
+
+ template <class InputIterator>
+ us_vector_set (InputIterator first, InputIterator last, const Compare& comp = Compare (), const Allocator& a = Allocator())
+ : c (comp, a) { insert_one (first, last); }
+
+ bool empty () const { return c.empty (); }
+
+ iterator begin() { return c.begin (); }
+ const_iterator begin() const { return c.begin (); }
+ iterator end() { return c.end (); }
+ const_iterator end() const { return c.end (); }
+ reverse_iterator rbegin() { return c.rbegin (); }
+ const_reverse_iterator rbegin() const { return c.rbegin (); }
+ reverse_iterator rend() { return c.rend (); }
+ const_reverse_iterator rend() const { return c.rend (); }
+
+ size_type size() const { return c.size (); }
+ size_type max_size() const { return c.max_size (); }
+
+ std::pair<iterator,bool> insert(const value_type& x) { return c.insert_one (x); }
+
+ void erase(iterator position) { c.erase (position); }
+ size_type erase(const key_type& x) { return c.erase_one (x); }
+ void erase(iterator first, iterator last){ c.erase (first, last); }
+ void clear() { c.clear (); }
+
+ void swap(us_vector_set& x) { c.swap (x.c); }
+
+ // set operations:
+ iterator find(const key_type& x) { return c.find (x); }
+ const_iterator find(const key_type& x) const { return c.find (x); }
+ size_type count(const key_type& x) const { return c.count_one (x); }
+
+ // vector specific operations
+ void reserve (size_type n) { c.reserve (n); }
+
+ value_type& operator [] (int n) { return c[n]; }
+ const value_type& operator [] (int n) const { return c[n]; }
+
+ vector_container& get_vector () { return c.c; }
+
+ public:
+
+ container c;
+};
+
+#endif
diff --git a/Runtime/Utilities/vector_utility.h b/Runtime/Utilities/vector_utility.h
new file mode 100644
index 0000000..a632413
--- /dev/null
+++ b/Runtime/Utilities/vector_utility.h
@@ -0,0 +1,95 @@
+#ifndef VECTOR_UTILITY_H
+#define VECTOR_UTILITY_H
+
+// stl vector doesnt deallocate memory on itself.
+// push_back and resizing increases memory by a factor of 2, when growing
+// pop_back, resizing and even clear do not deallocate any memory
+// This of course wastes a lot of memory, fortunately there is the swap trick to
+// bring back memory usage to normality
+//#include <algorithm>
+
+template<class T>
+inline void trim_vector (T& v)
+{
+ if (v.capacity () != v.size ())
+ {
+ T temp = v;
+ temp.swap (v);
+ }
+}
+
+// The following functions do the same as their vector member function
+// equivalents, only they allocate exactly the requested amount of memory.
+
+template<class T>
+inline void resize_trimmed (T& v, unsigned int sz)
+{
+ // the vector is growing
+ if (sz > v.size ())
+ {
+ if (sz != v.capacity ())
+ {
+ T temp (v.get_allocator());
+ temp.reserve (sz);
+ temp.assign (v.begin (), v.end ());
+ temp.resize (sz);
+ temp.swap (v);
+ }
+ else
+ v.resize (sz);
+ }
+ // the vector is shrinking
+ else if (sz < v.size ())
+ {
+ T temp (v.begin (), v.begin () + sz, v.get_allocator());
+ temp.swap (v);
+ }
+}
+
+template<class T>
+inline void reserve_trimmed (T& v, unsigned int sz)
+{
+ if (sz != v.capacity ())
+ {
+ T temp;
+ temp.reserve (sz);
+ temp.assign (v.begin (), v.end ());
+ temp.swap (v);
+ }
+}
+
+template<class T>
+inline void clear_trimmed (T& v)
+{
+ T temp;
+ temp.swap (v);
+}
+
+template<class T>
+inline void push_back_trimmed (T& vec, const typename T::value_type& value)
+{
+ if (vec.size () + 1 != vec.capacity ())
+ {
+ T temp;
+ temp.reserve (vec.size () + 1);
+ temp.assign (vec.begin (), vec.end ());
+ temp.push_back (value);
+ temp.swap (vec);
+ }
+ else
+ vec.push_back (value);
+}
+
+template<class T>
+inline void erase_trimmed (T& vec, typename T::iterator i)
+{
+ AssertIf (i == vec.end ());
+
+ T temp;
+ temp.reserve (vec.size () - 1);
+ temp.assign (vec.begin (), i);
+ temp.insert (temp.end (), ++i, vec.end ());
+ vec.swap (temp);
+}
+
+#endif
diff --git a/Runtime/Video/BaseVideoTexture.cpp b/Runtime/Video/BaseVideoTexture.cpp
new file mode 100644
index 0000000..414c776
--- /dev/null
+++ b/Runtime/Video/BaseVideoTexture.cpp
@@ -0,0 +1,517 @@
+#include "UnityPrefix.h"
+#include "BaseVideoTexture.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+using std::vector;
+
+extern void GenerateLookupTables ();
+extern bool IsNPOTTextureAllowed(bool hasMipMap);
+
+void BaseVideoTexture::UploadTextureData() //Upload image buffer to texture memory
+{
+ if(m_ImageBuffer)
+ {
+ // pad to assure clamping works
+ if (m_PaddedHeight > m_VideoHeight && m_PaddedHeight > 1)
+ for (int x = 0;x < m_PaddedWidth;x++)
+ m_ImageBuffer[m_VideoHeight * m_PaddedWidth + x] = m_ImageBuffer[(m_VideoHeight - 1)*m_PaddedWidth + x];
+ if (m_PaddedWidth > m_VideoWidth && m_PaddedWidth > 1)
+ for (int y = 0;y < m_PaddedHeight;y++)
+ m_ImageBuffer[y * m_PaddedWidth + m_VideoWidth] = m_ImageBuffer[y * m_PaddedWidth + m_VideoWidth - 1];
+
+ // Image buffer is 32 bits per pixel
+ int dataSize = m_PaddedWidth * m_PaddedHeight * sizeof(UInt32);
+ GetGfxDevice().UploadTextureSubData2D( GetTextureID(), (UInt8*)m_ImageBuffer, dataSize, 0, 0, 0, m_PaddedWidth, m_PaddedHeight, GetBufferTextureFormat(), GetActiveTextureColorSpace() );
+ }
+
+ m_DidUpdateThisFrame = m_ImageBuffer || !m_IsReadable;
+}
+
+///@TODO: THIS IS NOT THREAD SAFE
+
+typedef UNITY_VECTOR(kMemTexture, BaseVideoTexture*) VideoList;
+VideoList gVideoList;
+
+void BaseVideoTexture::UpdateVideoTextures()
+{
+ for(VideoList::iterator i=gVideoList.begin();i!=gVideoList.end();++i)
+ {
+ (**i).m_DidUpdateThisFrame = false;
+ if((**i).m_EnableUpdates)
+ (**i).Update();
+ }
+}
+
+void BaseVideoTexture::PauseVideoTextures()
+{
+ for(VideoList::iterator i=gVideoList.begin();i!=gVideoList.end();++i)
+ (**i).Pause();
+}
+
+void BaseVideoTexture::StopVideoTextures()
+{
+ for(VideoList::iterator i=gVideoList.begin();i!=gVideoList.end();++i)
+ {
+ (**i).Stop();
+
+ // Call this to reset video texture frame contents to black.
+ (**i).UnloadFromGfxDevice(false);
+ (**i).UploadToGfxDevice();
+ }
+}
+
+void BaseVideoTexture::SuspendVideoTextures()
+{
+ for(VideoList::iterator i=gVideoList.begin();i!=gVideoList.end();++i)
+ (**i).Suspend();
+}
+
+void BaseVideoTexture::ResumeVideoTextures()
+{
+ for(VideoList::iterator i=gVideoList.begin();i!=gVideoList.end();++i)
+ (**i).Resume();
+}
+
+void BaseVideoTexture::InitVideoMemory(int width, int height)
+{
+ m_VideoWidth = width;
+ m_VideoHeight = height;
+
+ m_TextureWidth = IsNPOTTextureAllowed(false) ? m_VideoWidth : NextPowerOfTwo(m_VideoWidth);
+ m_TextureHeight = IsNPOTTextureAllowed(false) ? m_VideoHeight : NextPowerOfTwo(m_VideoHeight);
+
+ m_PaddedHeight = m_VideoHeight + 1;
+ m_PaddedWidth = m_VideoWidth + 1;
+
+ if( m_PaddedHeight > m_TextureHeight )
+ m_PaddedHeight = m_TextureHeight;
+ if( m_PaddedWidth > m_TextureWidth )
+ m_PaddedWidth = m_TextureWidth;
+
+ if(m_IsReadable)
+ {
+ // The allocated buffer for one frame has extra line before the pointer
+ // we use in all operations. YUV decoding code operates two lines at a time,
+ // goes backwards and thus needs extra line before the buffer in case of odd
+ // movie sizes.
+
+
+ if (m_PaddedHeight+1 < m_PaddedHeight)
+ {
+ ErrorString("integer overflow in addition");
+ return;
+ }
+
+ int tmp = m_PaddedWidth * (m_PaddedHeight+1);
+ if ((m_PaddedHeight+1) != tmp / m_PaddedWidth)
+ {
+ ErrorString("integer overflow in multiplication");
+ return;
+ }
+
+ int tmp2 = tmp * sizeof(UInt32);
+
+ if (tmp != tmp2/sizeof(UInt32))
+ {
+ ErrorString("integer overflow in multiplication");
+ return;
+ }
+
+ UInt32* realBuffer = (UInt32*)UNITY_MALLOC(GetMemoryLabel(), m_PaddedWidth * (m_PaddedHeight+1) * sizeof(UInt32));
+ m_ImageBuffer = realBuffer + m_PaddedWidth;
+ // Make sure to set the alpha in the image buffer, as it is not updated from the movie data.
+ for(int i=0; i<m_PaddedWidth * m_PaddedHeight;i++)
+ #if UNITY_LITTLE_ENDIAN
+ m_ImageBuffer[i]=0x000000ff;
+ #else
+ m_ImageBuffer[i]=0xff000000;
+ #endif
+ }
+
+ CreateGfxTextureAndUploadData(false);
+}
+
+void BaseVideoTexture::UploadGfxTextureBuffer(UInt32* imgBuf)
+{
+ TextureID texName = GetTextureID();
+
+ int const dataSize = m_TextureWidth * m_TextureHeight * 4;
+ GetGfxDevice().UploadTexture2D( texName, kTexDim2D, reinterpret_cast<UInt8*>(imgBuf), dataSize,
+ m_TextureWidth, m_TextureHeight, GetBufferTextureFormat(), 1, GfxDevice::kUploadTextureDontUseSubImage, 0, kTexUsageNone, GetActiveTextureColorSpace() );
+ Texture::s_TextureIDMap.insert (std::make_pair(texName,this));
+}
+
+void BaseVideoTexture::CreateGfxTextureAndUploadData(bool uploadCurrentBuffer)
+{
+ if(m_IsReadable)
+ {
+ if (m_TextureWidth == m_PaddedWidth && m_TextureHeight == m_PaddedHeight)
+ {
+ // Simply upload the buffer that we currently have; its size is the same
+ // as the size of the temporary buffer that we would create.
+ Assert(m_ImageBuffer != NULL);
+
+ UploadGfxTextureBuffer(m_ImageBuffer);
+
+ // The image buffer already uploaded, no need to duplicate the work
+ uploadCurrentBuffer = false;
+ }
+ else // image buffer size differs from texture size
+ {
+ // Since we are using a buffer smaller then the actual texture for continuous updates,
+ // we need a bigger temp buffer once to initialize the texture contents.
+ UInt32* tmpBuffer;
+ ALLOC_TEMP_ALIGNED (tmpBuffer, UInt32, m_TextureWidth * m_TextureHeight, sizeof(UInt32));
+
+ // Don't upload garbage texture.
+ for(int i=0; i<m_TextureWidth * m_TextureHeight;i++)
+ #if UNITY_LITTLE_ENDIAN
+ tmpBuffer[i]=0x000000ff;
+ #else
+ tmpBuffer[i]=0xff000000;
+ #endif
+
+ UploadGfxTextureBuffer(tmpBuffer);
+ }
+
+ if (uploadCurrentBuffer)
+ UploadTextureData(); // Upload the buffer to the created texture
+
+ m_DidUpdateThisFrame = true;
+ }
+
+ GetGfxDevice().SetTextureParams( GetTextureID(), kTexDim2D, kTexFilterBilinear, kTexWrapClamp, 1, false, GetActiveTextureColorSpace() );
+ // uvScale, so we can use the texture as if it was a normal power-of-two texture
+ SetUVScale( m_VideoWidth/(float)m_TextureWidth, m_VideoHeight/(float)m_TextureHeight );
+}
+
+void BaseVideoTexture::ReleaseVideoMemory()
+{
+ if( m_ImageBuffer )
+ {
+ // The allocated buffer for one frame has extra line before the pointer
+ // we use in all operations. YUV decoding code operates two lines at a time,
+ // goes backwards and thus needs extra line before the buffer in case of odd
+ // movie sizes.
+ UInt32* realBuffer = m_ImageBuffer - m_PaddedWidth;
+ UNITY_FREE(GetMemoryLabel(), realBuffer);
+
+ m_ImageBuffer = NULL;
+ }
+}
+
+BaseVideoTexture::BaseVideoTexture(MemLabelId label, ObjectCreationMode mode)
+: Texture(label, mode)
+{
+ m_VideoWidth = m_VideoHeight = 16;
+ m_TextureWidth = m_TextureHeight = 16;
+ m_PaddedWidth = m_PaddedHeight = 0;
+ m_DidUpdateThisFrame = false;
+
+ m_ImageBuffer = NULL;
+
+ m_EnableUpdates = false;
+ m_IsReadable = true;
+
+ {
+ SET_ALLOC_OWNER(NULL);
+ gVideoList.push_back(this);
+ GenerateLookupTables ();
+ }
+}
+
+BaseVideoTexture::~BaseVideoTexture()
+{
+ ReleaseVideoMemory ();
+
+ for(VideoList::iterator i=gVideoList.begin();i!=gVideoList.end();i++)
+ {
+ if(*i==this)
+ {
+ gVideoList.erase(i);
+ break;
+ }
+ }
+
+ GetGfxDevice().DeleteTexture( GetTextureID() );
+}
+
+void BaseVideoTexture::SetReadable(bool readable)
+{
+ if(CanSetReadable(readable))
+ m_IsReadable = readable;
+}
+
+bool BaseVideoTexture::ExtractImage (ImageReference* image, int imageIndex) const
+{
+ if(m_ImageBuffer)
+ {
+ ImageReference source (m_VideoWidth, m_VideoHeight, m_PaddedWidth*4,GetBufferTextureFormat(),m_ImageBuffer);
+ image->BlitImage( source, ImageReference::BLIT_COPY );
+
+ return true;
+ }
+ else
+ return false;
+}
+
+static int sAdjCrr[256];
+static int sAdjCrg[256];
+static int sAdjCbg[256];
+static int sAdjCbb[256];
+static int sAdjY[256];
+static UInt8 sClampBuff[1024];
+static UInt8* sClamp = sClampBuff + 384;
+
+
+#define PROFILE_YUV_CONVERSION 0
+
+#if PROFILE_YUV_CONVERSION
+static __int64 GetCpuTicks ()
+{
+#if defined(GEKKO)
+ return OSGetTick ();
+#else
+ __asm rdtsc;
+ // eax/edx returned
+#endif
+}
+#endif
+
+// precalculate adjusted YUV values for faster RGB conversion
+void GenerateLookupTables ()
+{
+ static bool generated = false;
+ if (generated)
+ return;
+ int i;
+
+ for (i = 0; i < 256; i++)
+ {
+ sAdjCrr[i] = (409 * (i - 128) + 128) >> 8;
+ sAdjCrg[i] = (208 * (i - 128) + 128) >> 8;
+ sAdjCbg[i] = (100 * (i - 128) + 128) >> 8;
+ sAdjCbb[i] = (516 * (i - 128) + 128) >> 8;
+ sAdjY[i] = (298 * (i - 16)) >> 8;
+ }
+
+ // and setup LUT clamp range
+ for (i = -384; i < 0; i++)
+ sClamp[i] = 0;
+ for (i = 0; i < 256; i++)
+ sClamp[i] = i;
+ for (i = 256; i < 640; i++)
+ sClamp[i] = 255;
+ generated = true;
+}
+
+void BaseVideoTexture::YuvToRgb (const YuvFrame *yuv)
+{
+ #if PROFILE_YUV_CONVERSION
+ __int64 time0 = GetCpuTicks();
+ #endif
+ UInt8 *rgbBuffer = (UInt8*)GetImageBuffer ();
+ int const rowBytes = GetRowBytesFromWidthAndFormat(GetPaddedWidth(), GetBufferTextureFormat());
+
+ // Somehow related to audio track being placed into an audio source in the
+ // scene with play on load checked causes the first frame decoded to return
+ // garbage (with yuv->u set to NULL).
+ if ( yuv->u == NULL ) {
+ return;
+ }
+
+ // NOTE: this code goes backwards in lines, two lines at a time. Thus for
+ // odd image sizes it can under-run rgbBuffer. BaseVideoTexture code makes
+ // sure there's one line worth of allocated memory before the passed
+ // rgbBuffer.
+
+ // get destination buffer (and 1 row offset)
+ UInt8* dst0 = rgbBuffer + (yuv->height - 1)*rowBytes;
+ UInt8 *dst1 = dst0 - rowBytes;
+
+ // find picture offset
+ int yOffset = yuv->y_stride * yuv->offset_y + yuv->offset_x;
+ int uvOffset = yuv->uv_stride * (yuv->offset_y / 2) + (yuv->offset_x / 2);
+ const int uvStep = yuv->uv_step;
+
+ for ( int y = 0; y < yuv->height; y += 2 )
+ {
+ UInt8 *lineStart = dst1;
+
+ // set pointers into yuv buffers (2 lines for y)
+ const UInt8 *pY0 = yuv->y + yOffset + y * (yuv->y_stride);
+ const UInt8 *pY1 = yuv->y + yOffset + (y | 1) * (yuv->y_stride);
+ const UInt8 *pU = yuv->u + uvOffset + ((y * (yuv->uv_stride)) >> 1);
+ const UInt8 *pV = yuv->v + uvOffset + ((y * (yuv->uv_stride)) >> 1);
+
+ for (int x = 0; x < yuv->width; x += 2)
+ {
+ // convert a 2x2 block over
+ const int yy00 = sAdjY[pY0[0]];
+ const int yy10 = sAdjY[pY0[1]];
+ const int yy01 = sAdjY[pY1[0]];
+ const int yy11 = sAdjY[pY1[1]];
+
+ // Compute RGB offsets
+ const int vv = *pV;
+ const int uu = *pU;
+ const int R = sAdjCrr[vv];
+ const int G = sAdjCrg[vv] + sAdjCbg[uu];
+ const int B = sAdjCbb[uu];
+
+ // pixel 0x0
+ dst0++;
+ *dst0++ = sClamp[yy00 + R];
+ *dst0++ = sClamp[yy00 - G];
+ *dst0++ = sClamp[yy00 + B];
+
+ // pixel 1x0
+ dst0++;
+ *dst0++ = sClamp[yy10 + R];
+ *dst0++ = sClamp[yy10 - G];
+ *dst0++ = sClamp[yy10 + B];
+
+ // pixel 0x1
+ dst1++;
+ *dst1++ = sClamp[yy01 + R];
+ *dst1++ = sClamp[yy01 - G];
+ *dst1++ = sClamp[yy01 + B];
+
+ // pixel 1x1
+ dst1++;
+ *dst1++ = sClamp[yy11 + R];
+ *dst1++ = sClamp[yy11 - G];
+ *dst1++ = sClamp[yy11 + B];
+
+
+ pY0 += 2;
+ pY1 += 2;
+ pV += uvStep;
+ pU += uvStep;
+ }
+
+ // shift the destination pointers a row (loop increments 2 at a time)
+ dst0 = lineStart - rowBytes;
+ dst1 = dst0 - rowBytes;
+ }
+
+ #if PROFILE_YUV_CONVERSION
+ __int64 time1 = GetCpuTicks();
+ {
+ __int64 deltaTime = (time1 - time0) / 1000;
+ static __int64 accumTime = 0;
+ static int counter = 0;
+ accumTime += deltaTime;
+ ++counter;
+ if ( counter == 20 )
+ {
+ printf_console( "YUV Kclocks per frame: %i\n", (int)(accumTime / counter) );
+ counter = 0;
+ accumTime = 0;
+ }
+ }
+ #endif
+}
+
+//// Math! (Reference implementation)
+//// http://en.wikipedia.org/wiki/YUV#Y.27UV422_to_RGB888_conversion
+//static inline UInt32 ConvertYUYVtoRGBImpl(int c, int d, int e)
+//{
+// int red = 0,
+// green = 0,
+// blue = 0;
+//
+// red = std::min (UCHAR_MAX, (298 * c + 409 * e + 128) >> 8);
+// green = std::min (UCHAR_MAX, (298 * c - 100 * d - 208 * e + 128) >> 8);
+// blue = std::min (UCHAR_MAX, (298 * c + 516 * d + 128) >> 8);
+//
+// return (red << 8) | (green << 16) | (blue << 24) | 0xff;
+//}
+//
+//static inline UInt32 ConvertYCrCbToRGB(int y, int u, int v)
+//{
+// return ConvertYUYVtoRGBImpl(y - 16, u - 128, v - 128);
+//}
+
+// LUT-based implementation
+void BaseVideoTexture::YUYVToRGBA (UInt16 *const src)
+{
+ YUYVToRGBA(src, GetPaddedWidth ());
+}
+
+void BaseVideoTexture::YUYVToRGBA (UInt16 *const src, int srcStride)
+{
+ #if PROFILE_YUV_CONVERSION
+ __int64 time0 = GetCpuTicks();
+ #endif
+
+ UInt16 *srcYUYV = src;
+ UInt8 *destRGBA = reinterpret_cast<UInt8*> (GetImageBuffer ());
+ int const destStride = GetRowBytesFromWidthAndFormat(GetPaddedWidth(), GetBufferTextureFormat());
+ int const widthInPixels = GetDataWidth ();
+ int const heightInPixels = GetDataHeight ();
+ int y0;
+ int u;
+ int y1;
+ int v;
+ int red;
+ int green;
+ int blue;
+
+ destRGBA += (heightInPixels - 1) * destStride;
+
+ // Lines within the destination rectangle.
+ for (int y = 0; y < heightInPixels; ++y)
+ {
+ UInt8 *srcPixel = reinterpret_cast<UInt8*> (srcYUYV);
+
+ // Increment by widthInPixels does not necessarily
+ // mean that dstPixel is incremented by the stride,
+ // so we keep a separate pointer.
+ UInt8 *dstPixel = destRGBA;
+
+ for (int x = 0; (x + 1) < widthInPixels; x += 2)
+ {
+ // Byte order is Y0 U0 Y1 V0
+ // Each word is a byte pair (Y, U/V)
+ y0 = sAdjY[*srcPixel++];
+ u = *srcPixel++;
+ y1 = sAdjY[*srcPixel++];
+ v = *srcPixel++;
+ red = sAdjCrr[v];
+ green = sAdjCrg[v] + sAdjCbg[u];
+ blue = sAdjCbb[u];
+
+ *dstPixel++ = 0xFF;
+ *dstPixel++ = sClamp[y0 + red];
+ *dstPixel++ = sClamp[y0 - green];
+ *dstPixel++ = sClamp[y0 + blue];
+ *dstPixel++ = 0xFF;
+ *dstPixel++ = sClamp[y1 + red];
+ *dstPixel++ = sClamp[y1 - green];
+ *dstPixel++ = sClamp[y1 + blue];
+ }
+ destRGBA -= destStride;
+ srcYUYV += srcStride;
+ }
+
+ #if PROFILE_YUV_CONVERSION
+ __int64 time1 = GetCpuTicks();
+ {
+ __int64 deltaTime = (time1 - time0) / 1000;
+ static __int64 accumTime = 0;
+ static int counter = 0;
+ accumTime += deltaTime;
+ ++counter;
+ if ( counter == 20 )
+ {
+ printf_console( "YUYV Kclocks per frame: %i\n", (int)(accumTime / counter) );
+ counter = 0;
+ accumTime = 0;
+ }
+ }
+ #endif
+}
+
diff --git a/Runtime/Video/BaseVideoTexture.h b/Runtime/Video/BaseVideoTexture.h
new file mode 100644
index 0000000..378acbc
--- /dev/null
+++ b/Runtime/Video/BaseVideoTexture.h
@@ -0,0 +1,110 @@
+#ifndef VIDEO_TEXTURE
+#define VIDEO_TEXTURE
+
+#include "Runtime/Graphics/Texture.h"
+
+struct YuvFrame
+{
+ unsigned char* y;
+ unsigned char* u;
+ unsigned char* v;
+ int width;
+ int height;
+ int y_stride;
+ int uv_stride;
+ int offset_x;
+ int offset_y;
+ int uv_step;
+};
+
+class BaseVideoTexture: public Texture
+{
+protected:
+ virtual ~BaseVideoTexture();
+public:
+ BaseVideoTexture (MemLabelId label, ObjectCreationMode mode);
+
+ void InitVideoMemory(int width, int height);
+ void ReleaseVideoMemory ();
+
+ virtual void UnloadFromGfxDevice(bool forceUnloadAll) { }
+ virtual void UploadToGfxDevice() { }
+
+ UInt32 *GetImageBuffer() const {return m_ImageBuffer;}
+
+ virtual int GetDataWidth() const {return m_VideoWidth; }
+ virtual int GetDataHeight() const {return m_VideoHeight; }
+
+ int GetPaddedHeight() const {return m_PaddedHeight; }
+ int GetPaddedWidth() const {return m_PaddedWidth; }
+
+ int GetTextureHeight() const {return m_TextureHeight; }
+ int GetTextureWidth() const {return m_TextureWidth; }
+
+ virtual bool HasMipMap () const { return false; }
+ virtual int CountMipmaps() const { return 1; }
+
+ bool IsReadable() const { return m_IsReadable; }
+ void SetReadable(bool readable);
+
+ void UploadTextureData();
+
+ virtual void Update() = 0;
+ virtual void Play() { m_EnableUpdates = true; }
+ virtual void Pause() { m_EnableUpdates = false; }
+ virtual void Stop() { m_EnableUpdates = false; }
+ virtual bool IsPlaying() const { return m_EnableUpdates; }
+ virtual void Suspend() {}
+ virtual void Resume() {}
+
+ static void UpdateVideoTextures();
+ static void PauseVideoTextures();
+ static void StopVideoTextures();
+
+ // Useful for platforms like WinRT that can lose DX device on app switch
+ static void SuspendVideoTextures();
+ static void ResumeVideoTextures();
+
+ virtual TextureDimension GetDimension () const { return kTexDim2D; }
+
+ virtual int GetRuntimeMemorySize() const { return m_TextureWidth * m_TextureHeight * 4; }
+ #if UNITY_EDITOR
+ virtual int GetStorageMemorySize() const { return 0; }
+ virtual TextureFormat GetEditorUITextureFormat () const { return kTexFormatARGB32; }
+ #endif
+
+ virtual bool ExtractImage (ImageReference* image, int imageIndex = 0) const;
+
+ bool DidUpdateThisFrame () const { return m_DidUpdateThisFrame; };
+
+ void YuvToRgb (const YuvFrame *yuv);
+ void YUYVToRGBA (UInt16 *const src);
+ void YUYVToRGBA (UInt16 *const src, int srcStride);
+
+ virtual int GetVideoRotationAngle() const { return 0; }
+ virtual bool IsVideoVerticallyMirrored() const { return false; }
+
+private:
+ UInt32* m_ImageBuffer; //texture image buffer
+
+ int m_VideoWidth, m_VideoHeight; //height and width of video source
+ int m_TextureWidth, m_TextureHeight; //power-of-two texture dimensions
+ int m_PaddedWidth, m_PaddedHeight; //movie size padded by 1 if non-power-of-two in order to fix clamping
+
+ bool m_EnableUpdates;
+ bool m_DidUpdateThisFrame;
+ bool m_IsReadable;
+
+private:
+ void UploadGfxTextureBuffer(UInt32* imgBuf);
+
+protected:
+ void CreateGfxTextureAndUploadData(bool uploadCurrentBuffer);
+
+ virtual TextureFormat GetBufferTextureFormat() const { return kTexFormatARGB32; }
+ // by default we support only readable textures, as we create mem buffer anyway
+ virtual bool CanSetReadable(bool readable) const { return readable ? true : false; }
+};
+
+
+#endif
diff --git a/Runtime/Video/MoviePlayback.cpp b/Runtime/Video/MoviePlayback.cpp
new file mode 100644
index 0000000..bae2372
--- /dev/null
+++ b/Runtime/Video/MoviePlayback.cpp
@@ -0,0 +1,879 @@
+#include "UnityPrefix.h"
+#include "MoviePlayback.h"
+
+#if ENABLE_MOVIES
+
+#include "MovieTexture.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Audio/AudioManager.h"
+#include "Runtime/Audio/AudioClip.h"
+#include "Runtime/Audio/AudioSource.h"
+#include "Runtime/Misc/ReproductionLog.h"
+
+#if UNITY_EDITOR
+//editor uses custom ogg, not the one from fmod, because it also needs the encoding functionality, which is not present in the fmod one.
+#include "../../External/Audio/libogg/include/ogg/ogg.h"
+#else
+#include <ogg/ogg.h> //rely on include directories to pick the ogg.h from fmod for this specific platform.
+#endif
+
+#include "Runtime/Utilities/Utility.h"
+
+#if !UNITY_EDITOR
+#include <vorbis/window.h>
+#define ogg_sync_init FMOD_ogg_sync_init
+#define ogg_sync_buffer FMOD_ogg_sync_buffer
+#define ogg_sync_wrote FMOD_ogg_sync_wrote
+#define ogg_stream_pagein FMOD_ogg_stream_pagein
+#define ogg_sync_pageout FMOD_ogg_sync_pageout
+#define ogg_page_bos FMOD_ogg_page_bos
+#define ogg_stream_init FMOD_ogg_stream_init
+#define ogg_stream_pagein FMOD_ogg_stream_pagein
+#define ogg_stream_packetout FMOD_ogg_stream_packetout
+#define vorbis_synthesis_headerin FMOD_vorbis_synthesis_headerin
+#define ogg_stream_clear FMOD_ogg_stream_clear
+#define vorbis_info_init FMOD_vorbis_info_init
+#define vorbis_comment_init FMOD_vorbis_comment_init
+#define ogg_stream_packetout FMOD_ogg_stream_packetout
+#define vorbis_synthesis_init FMOD_vorbis_synthesis_init
+#define vorbis_block_init FMOD_vorbis_block_init
+#define vorbis_info_clear FMOD_vorbis_info_clear
+#define vorbis_comment_clear FMOD_vorbis_comment_clear
+#define vorbis_synthesis_pcmout FMOD_vorbis_synthesis_pcmout
+#define vorbis_synthesis_read FMOD_vorbis_synthesis_read
+#define ogg_stream_packetout FMOD_ogg_stream_packetout
+#define vorbis_synthesis FMOD_vorbis_synthesis
+#define vorbis_synthesis_blockin FMOD_vorbis_synthesis_blockin
+#define vorbis_comment_clear FMOD_vorbis_comment_clear
+#define vorbis_info_clear FMOD_vorbis_info_clear
+#define ogg_stream_clear FMOD_ogg_stream_clear
+#define vorbis_block_clear FMOD_vorbis_block_clear
+#define vorbis_dsp_clear FMOD_vorbis_dsp_clear
+#define ogg_stream_clear FMOD_ogg_stream_clear
+#define ogg_sync_clear FMOD_ogg_sync_clear
+#define ogg_sync_reset FMOD_ogg_sync_reset
+#define ogg_page_serialno FMOD_ogg_page_serialno
+#define FMOD_OGG_PRE kFMOD_OGG_context,
+#else
+#define FMOD_OGG_PRE
+#endif
+
+#if UNITY_EDITOR
+//editor uses custom ogg, not the one from fmod, because it also needs the encoding functionality, which is not present in the fmod one.
+#include "../../External/Audio/libvorbis/include/vorbis/vorbisfile.h"
+#include "../../External/Audio/libvorbis/include/vorbis/codec.h"
+#else
+#include <vorbis/vorbisfile.h> //rely on include directories to pick the ogg.h from fmod for this specific platform.
+#endif
+
+#include "assert.h"
+
+#define DEBUG_MOVIES 0
+#define kAudioBufferSize (16 * 1024)
+
+void* kFMOD_OGG_context = NULL;
+
+#if !UNITY_EDITOR
+// FMOD doesn't implement this - and we need it to determine the duration of the movie
+char *vorbis_comment_query(vorbis_comment *vc, const char *tag, int count){
+ ogg_int32_t i;
+ int found = 0;
+ int taglen = strlen(tag)+1; /* +1 for the = we append */
+ char *fulltag = (char*)alloca(taglen+ 1);
+
+ strcpy(fulltag, tag);
+ strcat(fulltag, "=");
+
+ for(i=0;i<vc->comments;i++){
+ if(!strncmp(vc->user_comments[i], fulltag, taglen)){
+ if(count == found)
+ /* We return a pointer to the data, not a copy */
+ return vc->user_comments[i] + taglen;
+ else
+ found++;
+ }
+ }
+ return NULL; /* didn't find anything */
+ }
+#endif // vorbis_comment_query
+
+//Init structures
+MoviePlayback::MoviePlayback()
+{
+ m_InitialisedLoad = false;
+ m_VorbisInitialised = false;
+ m_VorbisStateInitialised = false;
+ m_TheoraInitialised = false;
+ m_TheoraStateInitialised = false;
+
+ m_AudioBuffer = (ogg_int16_t*)UNITY_MALLOC(kMemAudioData, kAudioBufferSize);
+
+#if !UNITY_EDITOR
+ _FMOD_vorbis_window_init();
+#endif
+
+ /* start up Ogg stream synchronization layer */
+ ogg_sync_init(&m_OggSynchState);
+
+ m_StartTime = 0.0;
+ m_Texture = NULL;
+ m_IsPlaying = false;
+ m_Loop = false;
+ m_Duration = -1;
+ m_AudioChannel = NULL;
+ m_AudioClip = NULL;
+#if ENABLE_WWW
+ m_DataStream = NULL;
+#endif
+
+#if UNITY_EDITOR
+ //shut up gcc warnings.
+ UNUSED(OV_CALLBACKS_DEFAULT);
+ UNUSED(OV_CALLBACKS_NOCLOSE);
+ UNUSED(OV_CALLBACKS_STREAMONLY);
+ UNUSED(OV_CALLBACKS_STREAMONLY_NOCLOSE);
+#endif
+}
+
+// Read data from in into the ogg synch state. returns bytes read.
+#define kReadChunkSize 4096
+int MoviePlayback::ReadBufferIntoOggStream()
+{
+ char *buffer = ogg_sync_buffer(FMOD_OGG_PRE &m_OggSynchState, kReadChunkSize);
+ unsigned int read = m_Data.size - m_Data.position;
+ if (read > kReadChunkSize)
+ read = kReadChunkSize;
+ memcpy(buffer, m_Data.data + m_Data.position, read);
+ ogg_sync_wrote(&m_OggSynchState, read);
+ m_Data.position += read;
+ return read;
+}
+
+/* helper: push a page into the appropriate steam */
+/* this can be done blindly; a stream won't accept a page
+that doesn't belong to it */
+void MoviePlayback::QueueOggPageIntoStream()
+{
+ if (m_TheoraStateInitialised)
+ ogg_stream_pagein(FMOD_OGG_PRE &m_TheoraStreamState, &m_OggPage);
+ if (m_VorbisStateInitialised)
+ ogg_stream_pagein(FMOD_OGG_PRE &m_VorbisStreamState, &m_OggPage);
+}
+
+void MoviePlayback::ChangeMovieData(UInt8 *data, long size)
+{
+ m_Data.data = data;
+ m_Data.size = size;
+}
+
+bool MoviePlayback::InitStreams(int &theoraHeadersSeen, int &vorbisHeadersSeen)
+{
+ m_Data.position = 0;
+ ogg_packet op;
+
+ /* Ogg file open; parse the headers */
+ /* Only interested in Vorbis/Theora streams */
+ while (true)
+ {
+ if (ReadBufferIntoOggStream() == 0)
+ return false;
+ while (ogg_sync_pageout(&m_OggSynchState, &m_OggPage)>0)
+ {
+ ogg_stream_state test;
+
+ /* is this a mandated initial header? If not, stop parsing */
+ if (!ogg_page_bos(&m_OggPage))
+ {
+ /* don't leak the page; get it into the appropriate stream */
+ QueueOggPageIntoStream();
+ return true;
+ }
+
+ if (ogg_stream_init(FMOD_OGG_PRE &test, ogg_page_serialno(&m_OggPage)) != 0)
+ return false;
+ if (ogg_stream_pagein(FMOD_OGG_PRE &test, &m_OggPage) != 0)
+ return false;
+ if (ogg_stream_packetout(&test, &op) != 1)
+ return false;
+
+
+ /* identify the codec: try theora */
+ if (!m_TheoraStateInitialised && theora_decode_header(&m_TheoraInfo, &m_TheoraComment, &op) >= 0)
+ {
+ /* it is theora */
+ memcpy(&m_TheoraStreamState, &test, sizeof(test));
+ theoraHeadersSeen = 1;
+ m_TheoraStateInitialised = true;
+ }else if (!m_VorbisStateInitialised && vorbis_synthesis_headerin(FMOD_OGG_PRE &m_VorbisInfo, &m_VorbisComment, &op) >= 0)
+ {
+ /* it is vorbis */
+ memcpy(&m_VorbisStreamState, &test, sizeof(test));
+ vorbisHeadersSeen = 1;
+ m_VorbisStateInitialised = true;
+ }else{
+ /* whatever it is, we don't care about it */
+ ogg_stream_clear(FMOD_OGG_PRE &test);
+ }
+ }
+ /* fall through to non-bos page parsing */
+ }
+}
+
+bool MoviePlayback::LoadMovieData( UInt8 *data, long size )
+{
+ Cleanup();
+ // Should never happen, but better safe than crashing.
+ if ( !data )
+ {
+ ErrorString( "LoadMoveData got NULL!" );
+ return false;
+ }
+
+ theora_info_init(&m_TheoraInfo);
+ theora_comment_init(&m_TheoraComment);
+ vorbis_info_init(FMOD_OGG_PRE &m_VorbisInfo);
+ vorbis_comment_init(&m_VorbisComment);
+ m_InitialisedLoad = true; //Signify we have attempted a load.
+
+ ogg_packet op;
+
+ m_Data.data = data;
+ m_Data.size = size;
+
+ int theoraHeadersSeen = 0;
+ int vorbisHeadersSeen = 0;
+
+ if (!InitStreams(theoraHeadersSeen, vorbisHeadersSeen))
+ {
+ Cleanup();
+ return false;
+ }
+
+ /* we're expecting more header packets. */
+ while ((m_TheoraStateInitialised && theoraHeadersSeen < 3) || (m_VorbisStateInitialised && vorbisHeadersSeen < 3))
+ {
+ int ret;
+ /* look for further theora headers */
+ while (m_TheoraStateInitialised && (theoraHeadersSeen < 3) && (ret = ogg_stream_packetout(&m_TheoraStreamState, &op)))
+ {
+ if (ret < 0)
+ {
+ printf_console("Error parsing Theora stream headers; corrupt stream?\n");
+ Cleanup();
+ return false;
+ }
+ if (theora_decode_header(&m_TheoraInfo, &m_TheoraComment, &op))
+ {
+ printf_console("Error parsing Theora stream headers; corrupt stream?\n");
+ Cleanup();
+ return false;
+ }
+ theoraHeadersSeen++;
+ }
+
+ /* look for more vorbis header packets */
+ while (m_VorbisStateInitialised && (vorbisHeadersSeen < 3) && (ret = ogg_stream_packetout(&m_VorbisStreamState, &op)))
+ {
+ if (ret < 0)
+ {
+ printf_console("Error parsing Vorbis stream headers; corrupt stream?\n");
+ Cleanup();
+ return false;
+ }
+ if (vorbis_synthesis_headerin(FMOD_OGG_PRE &m_VorbisInfo, &m_VorbisComment, &op))
+ {
+ printf_console("Error parsing Vorbis stream headers; corrupt stream?\n");
+ Cleanup();
+ return false;
+ }
+ vorbisHeadersSeen++;
+ }
+
+ /* The header pages/packets will arrive before anything else we
+ care about, or the stream is not obeying spec */
+
+ if (ogg_sync_pageout(&m_OggSynchState, &m_OggPage)>0)
+ {
+ QueueOggPageIntoStream(); /* demux into the appropriate stream */
+ }
+ else
+ {
+ if (ReadBufferIntoOggStream() == 0)
+ {
+ fprintf(stderr, "End of file while searching for codec headers.\n");
+ Cleanup();
+ return false;
+ }
+ }
+ }
+
+ /* and now we have it all. initialize decoders */
+ if (m_TheoraStateInitialised)
+ {
+ theora_decode_init(&m_TheoraState, &m_TheoraInfo);
+ const char *duration = theora_comment_query(&m_TheoraComment, const_cast<char*> ("DURATION"), 0);
+ if (duration)
+ sscanf(duration, "%f", &m_Duration);
+ m_TheoraInitialised = true;
+
+ }else{
+ /* tear down the partial theora setup */
+ theora_info_clear(&m_TheoraInfo);
+ theora_comment_clear(&m_TheoraComment);
+ }
+ if (m_VorbisStateInitialised)
+ {
+ vorbis_synthesis_init(FMOD_OGG_PRE &m_VorbisState, &m_VorbisInfo);
+ vorbis_block_init(FMOD_OGG_PRE &m_VorbisState, &m_VorbisBlock);
+ const char *duration = vorbis_comment_query(&m_VorbisComment, const_cast<char*> ("DURATION"), 0);
+ if (duration)
+ sscanf(duration, "%f", &m_Duration);
+ m_VorbisInitialised = true;
+
+ }else{
+ /* tear down the partial vorbis setup */
+ vorbis_info_clear(FMOD_OGG_PRE &m_VorbisInfo);
+ vorbis_comment_clear(FMOD_OGG_PRE &m_VorbisComment);
+ }
+
+ m_CanStartPlaying = false;
+ m_VideoBufferReady = false;
+ m_AudioBufferReady = false;
+ m_AudioBufferFill = 0;
+ m_AudioBufferGranulePos = -1;
+ m_VideoBufferTime = 0;
+ m_NoMoreData = false;
+ m_LastSampleTime = 0;
+ m_AudioBufferTime = 0;
+
+ //setup audio
+ if (m_AudioClip && !m_AudioChannel)
+ {
+ m_AudioClip->SetMoviePlayback(this);
+ // queue
+ // if we have no audio channel ... use the ready audio buffer to init
+ return m_AudioClip->ReadyToPlay();
+ }
+
+ return true;
+}
+
+
+#if ENABLE_WWW
+bool MoviePlayback::LoadMovieData(WWW *stream)
+{
+ if (m_DataStream != stream)
+ {
+ if (m_DataStream)
+ m_DataStream->Release();
+
+ m_DataStream = stream;
+ m_DataStream->Retain(); // Make sure the WWW object doesn't dissappear if the mono side of the WWW object is deleted before we are done.
+ }
+
+ stream->LockPartialData();
+ int size = stream->GetPartialSize();
+
+ //require headers before starting
+ if (size < 16 * 1024)
+ {
+ stream->UnlockPartialData();
+ return false;
+ }
+
+ bool accepted = LoadMovieData((UInt8*)stream->GetPartialData(), stream->GetPartialSize());
+ stream->UnlockPartialData();
+
+ return accepted;
+}
+#endif
+
+bool MoviePlayback::MovieHasAudio()
+{
+ return m_VorbisInitialised;
+}
+
+bool MoviePlayback::MovieHasVideo()
+{
+ return m_TheoraInitialised;
+}
+
+double MoviePlayback::GetMovieTime(bool useAudio)
+{
+ TimeManager& timeMgr = GetTimeManager();
+
+ double ret;
+ //use audio for timing if available
+ if (MovieHasAudio() && useAudio)
+ {
+ double curTime = timeMgr.GetRealtime();
+
+ double d = m_VorbisInfo.rate; // /m_VorbisInfo.channels) * 2;
+ double dQ = ((double)(kAudioQueueSize) / (d * 2 * m_VorbisInfo.channels));
+ double dG = ((double)(m_AudioBufferGranulePos) / d);
+ dG = dG < 0 ? 0 : dG;
+ double sDiff = (curTime - m_AudioBufferTime);
+ ret = (dG - dQ) + (sDiff);
+ }
+ else
+ {
+ //use real time if audio is not available; unless we run in capture timestep
+ double curTime = timeMgr.GetCaptureFramerate() > 0 ? timeMgr.GetCurTime() : timeMgr.GetRealtime();
+ curTime -= m_StartTime;
+
+ //if time is too far off, reset start time, to resume smooth playback from here
+ if (curTime > m_LastSampleTime + 0.1 || curTime < m_LastSampleTime)
+ {
+ double diff = curTime - m_LastSampleTime - 0.1;
+ m_StartTime += diff;
+ curTime -= diff;
+ }
+ ret = curTime;
+ }
+ m_LastSampleTime = ret;
+ return ret;
+}
+
+bool MoviePlayback::MovieStreamImage()
+{
+ //This may happen if WWW stream is cancelled before movie is stopped.
+ if (m_Data.data == NULL && !DidLoad())
+ return false;
+
+ //can we use audio for timing?
+ bool canPlayAudio = false;
+ if (m_AudioClip && m_AudioChannel)
+ m_AudioChannel->isPlaying(&canPlayAudio);
+
+ #if SUPPORT_REPRODUCE_LOG
+ if (RunningReproduction())
+ canPlayAudio = false;
+ #endif
+
+ bool didWriteBuffer = false;
+ while (!didWriteBuffer)
+ {
+ /* we want a video and audio frame ready to go at all times. If
+ we have to buffer incoming, buffer the compressed data (ie, let
+ ogg do the buffering) */
+ while (MovieHasAudio() && canPlayAudio && !m_AudioBufferReady)
+ {
+ int ret;
+ float **pcm;
+ /* if there's pending, decoded audio, grab it */
+ if ((ret = vorbis_synthesis_pcmout(&m_VorbisState, &pcm))>0)
+ {
+ int count = m_AudioBufferFill / 2;
+ int maxSamples = (kAudioBufferSize - m_AudioBufferFill) / 2 / m_VorbisInfo.channels;
+ maxSamples = std::min (maxSamples, ret);
+ for (int i = 0;i < maxSamples;i++)
+ {
+ for (int j = 0;j < m_VorbisInfo.channels;j++)
+ {
+ // TODO: implement fast RoundfToInt for intel!
+ int val = RoundfToInt(pcm[j][i]*32767.f);
+ if (val > 32767)
+ val = 32767;
+ if (val<-32768)
+ val = -32768;
+ assert (count + 1 < kAudioBufferSize);
+ m_AudioBuffer[count++] = val;
+ }
+ }
+ vorbis_synthesis_read(&m_VorbisState, maxSamples);
+ m_AudioBufferFill += maxSamples * m_VorbisInfo.channels * 2;
+
+ if (m_AudioBufferFill == kAudioBufferSize)
+ m_AudioBufferReady = true;
+ if (m_VorbisState.granulepos >= 0)
+ m_AudioBufferGranulePos = m_VorbisState.granulepos - ret + maxSamples;
+ else
+ m_AudioBufferGranulePos += maxSamples;
+
+ m_AudioBufferTime = GetTimeManager().GetRealtime();
+ }
+ else
+ {
+ ogg_packet op;
+ /* no pending audio; is there a pending packet to decode? */
+ if (ogg_stream_packetout(&m_VorbisStreamState, &op)>0)
+ {
+ if (vorbis_synthesis(FMOD_OGG_PRE &m_VorbisBlock, &op) == 0) /* test for success! */
+ vorbis_synthesis_blockin(&m_VorbisState, &m_VorbisBlock);
+ }
+ else /* we need more data; break out to suck in another page */
+ break;
+ }
+ }
+
+ while (MovieHasVideo() && !m_VideoBufferReady)
+ {
+ ogg_packet op;
+ /* theora is one in, one out... */
+ if (ogg_stream_packetout(&m_TheoraStreamState, &op)>0)
+ {
+ int ret = theora_decode_packetin(&m_TheoraState, &op);
+ AssertIf (ret != 0);
+ ogg_int64_t videobuf_granulepos = m_TheoraState.granulepos;
+
+ m_VideoBufferTime = theora_granule_time(&m_TheoraState, videobuf_granulepos);
+
+ /* is it already too old to be useful? This is only actually
+ useful cosmetically after a SIGSTOP. Note that we have to
+ decode the frame even if we don't show it (for now) due to
+ keyframing. Soon enough libtheora will be able to deal
+ with non-keyframe seeks. */
+
+ if (ret == 0 && m_VideoBufferTime >= GetMovieTime(canPlayAudio))
+ m_VideoBufferReady = true;
+
+ }else
+ break;
+ }
+
+ if (!m_VideoBufferReady && (!m_AudioBufferReady || !canPlayAudio) && m_Data.position >= m_Data.size)
+ {
+ m_NoMoreData = true;
+ return false;
+ }
+
+ if ((!m_VideoBufferReady && MovieHasVideo()) || (!m_AudioBufferReady && MovieHasAudio() && canPlayAudio))
+ {
+ /* no data yet for somebody. Grab another page */
+ ReadBufferIntoOggStream();
+ while (ogg_sync_pageout(&m_OggSynchState, &m_OggPage)>0)
+ {
+ QueueOggPageIntoStream();
+ m_NoMoreData = false;
+ }
+ }
+
+ /* If playback has begun, top audio buffer off immediately. */
+ if (m_CanStartPlaying && MovieHasAudio() && canPlayAudio && m_AudioBufferReady)
+ {
+ if (m_AudioClip->QueueAudioData(m_AudioBuffer, kAudioBufferSize))
+ {
+ m_AudioBufferFill = 0;
+ m_AudioBufferReady = false;
+ }
+ }
+
+ /* are we at or past time for this video frame? */
+ if (m_CanStartPlaying && m_VideoBufferReady && m_VideoBufferTime <= GetMovieTime(canPlayAudio))
+ {
+ if (m_Texture && m_Texture->GetImageBuffer())
+ {
+ yuv_buffer yuv;
+ int ret = theora_decode_YUVout(&m_TheoraState, &yuv);
+ if (ret == 0)
+ {
+ YuvFrame yuvFrame;
+ yuvFrame.y = yuv.y;
+ yuvFrame.u = yuv.u;
+ yuvFrame.v = yuv.v;
+ yuvFrame.width = m_TheoraInfo.frame_width;
+ yuvFrame.height = m_TheoraInfo.frame_height;
+ yuvFrame.y_stride = yuv.y_stride;
+ yuvFrame.uv_stride = yuv.uv_stride;
+ yuvFrame.uv_step = 1; // non-interleaved UV data
+ yuvFrame.offset_x = m_TheoraInfo.offset_x;
+ yuvFrame.offset_y = m_TheoraInfo.offset_y;
+ m_Texture->YuvToRgb (&yuvFrame);
+ }
+ }
+ didWriteBuffer = true;
+ m_VideoBufferReady = false;
+ }
+
+ if (m_CanStartPlaying &&
+ (m_AudioBufferReady || !(MovieHasAudio() && canPlayAudio)) &&
+ (m_VideoBufferReady || !MovieHasVideo())
+ )
+ {
+ /* we have an audio frame ready (which means the audio buffer is
+ full), it's not time to play video, so wait until one of the
+ audio buffer is ready or it's near time to play video */
+
+ return didWriteBuffer;
+ }
+
+ /* if our buffers either don't exist or are ready to go,
+ we can begin playback */
+ if ((!MovieHasVideo() || m_VideoBufferReady) && (!(MovieHasAudio() && canPlayAudio) || m_AudioBufferReady ))
+ m_CanStartPlaying = true;
+
+ /* same if we've run out of input */
+ if (m_Data.position >= m_Data.size)
+ m_CanStartPlaying = true;
+ }
+
+ return didWriteBuffer;
+}
+
+void MoviePlayback::Cleanup()
+{
+ if(!m_InitialisedLoad)
+ {
+ return;
+ }
+
+ if(m_VorbisInitialised)
+ {
+ vorbis_block_clear(FMOD_OGG_PRE &m_VorbisBlock);
+ vorbis_dsp_clear(FMOD_OGG_PRE &m_VorbisState);
+ }
+ if (m_VorbisStateInitialised)
+ {
+ ogg_stream_clear(FMOD_OGG_PRE &m_VorbisStreamState);
+ }
+
+ if (m_TheoraInitialised)
+ {
+ theora_clear(&m_TheoraState);
+ }
+ if (m_TheoraStateInitialised)
+ {
+ ogg_stream_clear(FMOD_OGG_PRE &m_TheoraStreamState);
+ }
+
+ vorbis_comment_clear(FMOD_OGG_PRE &m_VorbisComment);
+ vorbis_info_clear(FMOD_OGG_PRE &m_VorbisInfo);
+ theora_comment_clear(&m_TheoraComment);
+ theora_info_clear(&m_TheoraInfo);
+
+ m_InitialisedLoad = false;
+ m_VorbisInitialised = false;
+ m_VorbisStateInitialised = false;
+ m_TheoraInitialised = false;
+ m_TheoraStateInitialised = false;
+}
+
+MoviePlayback::~MoviePlayback()
+{
+ Cleanup();
+
+ if (m_AudioClip)
+ if (m_AudioClip->GetMoviePlayback() == this)
+ m_AudioClip->SetMoviePlayback(NULL);
+
+ UNITY_FREE(kMemAudioData, m_AudioBuffer);
+ ogg_sync_clear(FMOD_OGG_PRE &m_OggSynchState);
+
+#if ENABLE_WWW
+ if (m_DataStream)
+ m_DataStream->Release();
+#endif
+}
+
+int MoviePlayback::GetMovieBitrate()
+{
+ int bitrate = 0;
+ if (MovieHasVideo())
+ {
+ if (m_TheoraInfo.target_bitrate)
+ bitrate += m_TheoraInfo.target_bitrate;
+ else //find a good way to guess average bitrate of unknown encodings
+ bitrate += 500000;
+ }
+ if (MovieHasAudio())
+ {
+ if (m_VorbisInfo.bitrate_nominal > 0)
+ bitrate += m_VorbisInfo.bitrate_nominal;
+ else if (m_VorbisInfo.bitrate_upper > 0)
+ bitrate += m_VorbisInfo.bitrate_upper;
+ }
+ return bitrate;
+}
+
+int MoviePlayback::GetMovieWidth()
+{
+ if (MovieHasVideo())
+ return m_TheoraInfo.frame_width;
+ else
+ return 0;
+}
+
+int MoviePlayback::GetMovieHeight()
+{
+ if (MovieHasVideo())
+ return m_TheoraInfo.frame_height;
+ else
+ return 0;
+}
+
+int MoviePlayback::GetMovieAudioRate()
+{
+ if (MovieHasAudio())
+ return m_VorbisInfo.rate;
+ else
+ return 0;
+}
+
+int MoviePlayback::GetMovieAudioChannelCount()
+{
+ if (MovieHasAudio())
+ return m_VorbisInfo.channels;
+ else
+ return 0;
+}
+
+void MoviePlayback::Play()
+{
+ m_IsPlaying = true;
+ if (m_AudioClip && m_AudioChannel)
+ {
+ m_AudioChannel->setPaused(false);
+ }
+}
+
+void MoviePlayback::Stop()
+{
+ m_IsPlaying = false;
+ if (m_AudioClip && m_AudioChannel)
+ {
+ m_AudioClip->ClearQueue();
+ PauseAudio();
+ }
+ Rewind();
+}
+
+void MoviePlayback::Pause()
+{
+ m_IsPlaying = false;
+ PauseAudio();
+}
+
+void MoviePlayback::SetLoop (bool loop)
+{
+ m_Loop=loop;
+ if (m_AudioChannel)
+ {
+ FMOD_MODE mode;
+ m_AudioChannel->getMode(&mode);
+ mode = (mode & ~(FMOD_LOOP_NORMAL | FMOD_LOOP_OFF)) | (m_Loop ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF);
+ m_AudioChannel->setMode(mode);
+ }
+}
+
+void MoviePlayback::SetAudioChannel(FMOD::Channel* channel)
+{
+ m_AudioChannel = channel;
+ if(m_AudioChannel)
+ {
+ FMOD_MODE mode;
+ m_AudioChannel->getMode(&mode);
+ mode = (mode & ~(FMOD_LOOP_NORMAL | FMOD_LOOP_OFF | FMOD_3D)) | (m_Loop ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF) | FMOD_2D;
+ m_AudioChannel->setMode(mode);
+ }
+}
+
+void MoviePlayback::Rewind()
+{
+ // Destroy and recreate streams. Just setting file position to 0 may break, because we first have to parse
+ // the headers.
+ Cleanup();
+
+ ogg_sync_reset(&m_OggSynchState);
+
+#if ENABLE_WWW
+ if (m_DataStream)
+ {
+ //update download position
+ //lock stream, so it cannot reallocate
+ m_DataStream->LockPartialData();
+ ChangeMovieData((UInt8*)m_DataStream->GetPartialData(), m_DataStream->GetPartialSize());
+ }
+#endif
+
+ LoadMovieData(m_Data.data, m_Data.size);
+
+#if ENABLE_WWW
+ if (m_DataStream)
+ m_DataStream->UnlockPartialData();
+#endif
+
+ m_StartTime = GetCurTime();
+}
+
+bool MoviePlayback::Update()
+{
+ if (!MovieHasVideo() && !MovieHasAudio())
+ return false;
+
+ bool videoChanged = false;
+ if (m_IsPlaying)
+ {
+#if ENABLE_WWW
+ if (m_DataStream)
+ {
+ //update download position
+ //lock stream, so it cannot reallocate
+ m_DataStream->LockPartialData();
+ ChangeMovieData((UInt8*)m_DataStream->GetPartialData(), m_DataStream->GetPartialSize());
+ }
+#endif
+
+ if (MovieStreamImage())
+ {
+ if (m_Texture && m_Texture->GetImageBuffer())
+ videoChanged = true;
+ }
+
+ bool finished = m_NoMoreData;
+#if ENABLE_WWW
+ if (m_DataStream)
+ {
+ m_DataStream->UnlockPartialData();
+
+ // if we are still downloading, we probably aren't really at the end
+ finished &= m_DataStream->IsDone();
+ }
+#endif
+
+ // rewind if looping and movie is at end
+ if (finished)
+ {
+ if (m_Loop)
+ Rewind();
+ else
+ {
+ m_IsPlaying = false;
+ if (m_AudioChannel)
+ {
+ PauseAudio();
+ if (m_AudioClip)
+ m_AudioClip->ClearQueue();
+ }
+ }
+ }
+ }
+
+ return videoChanged;
+}
+
+void MoviePlayback::PauseAudio()
+{
+ // get AudioSource and pause the sound
+ if (m_AudioChannel)
+ {
+ AudioSource* audioSource;
+ m_AudioChannel->getUserData((void**) &audioSource);
+ if (audioSource)
+ audioSource->Pause();
+ else
+ m_AudioChannel->setPaused(true);
+ }
+}
+
+#else // ENABLE_MOVIES
+// dummy implementation coded in .h file
+#endif
+
+#if UNITY_EDITOR
+bool PlayFullScreenMovie (std::string const& path,
+ ColorRGBA32 const& backgroundColor,
+ unsigned long controlMode, unsigned long scalingMode)
+{
+ return true;
+}
+#endif
diff --git a/Runtime/Video/MoviePlayback.h b/Runtime/Video/MoviePlayback.h
new file mode 100644
index 0000000..49e4ac7
--- /dev/null
+++ b/Runtime/Video/MoviePlayback.h
@@ -0,0 +1,212 @@
+#ifndef MOVIE_PLAYBACK
+#define MOVIE_PLAYBACK
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Math/Color.h"
+
+bool PlayFullScreenMovie (std::string const& path,
+ ColorRGBA32 const& backgroundColor,
+ unsigned long controlMode, unsigned long scalingMode);
+
+#if ENABLE_MOVIES
+#include "Runtime/Audio/correct_fmod_includer.h"
+#include "External/theora/include/theora/theora.h"
+
+#if UNITY_EDITOR
+//editor uses custom ogg, not the one from fmod, because it also needs the encoding functionality, which is not present in the fmod one.
+#include "../../External/Audio/libvorbis/include/vorbis/codec.h"
+#else
+#include <vorbis/codec.h> //rely on include directories to pick the ogg.h from fmod for this specific platform.
+#endif
+
+#include "Runtime/Audio/AudioClip.h"
+
+#if ENABLE_WWW
+#include "Runtime/Export/WWW.h"
+#endif
+
+struct MovieDataStream {
+ UInt8 *data;
+ long size;
+ long position;
+};
+
+class MovieTexture;
+class AudioClip;
+class AudioSource;
+
+class MoviePlayback
+{
+private:
+
+ ogg_sync_state m_OggSynchState;
+ ogg_page m_OggPage;
+
+ ogg_stream_state m_TheoraStreamState;
+ theora_info m_TheoraInfo;
+ theora_comment m_TheoraComment;
+ theora_state m_TheoraState;
+
+ ogg_stream_state m_VorbisStreamState;
+ vorbis_info m_VorbisInfo;
+ vorbis_comment m_VorbisComment;
+ vorbis_dsp_state m_VorbisState;
+ vorbis_block m_VorbisBlock;
+
+ bool m_CanStartPlaying;
+ bool m_VideoBufferReady;
+ double m_VideoBufferTime;
+
+ int m_AudioBufferFill;
+ bool m_AudioBufferReady;
+ ogg_int16_t* m_AudioBuffer;
+ ogg_int64_t m_AudioBufferGranulePos; /* time position of last sample */
+ double m_AudioBufferTime; //Real time when the last audio buffer was filled
+
+ bool m_NoMoreData; //Are we finished playing?
+ MovieDataStream m_Data; //Data buffer and position of stream
+ double m_StartTime; //real time offset for start
+ double m_LastSampleTime; //last sample played
+ bool m_IsPlaying; //shall movie update in player loop?
+ bool m_Loop; //is movie looping?
+
+ bool m_InitialisedLoad; //Have we initialised the theora and vorbis codec structs
+ bool m_VorbisInitialised; //Vorbis headers are initialised and ready for data.
+ bool m_VorbisStateInitialised; //The vorbis state struct is primed, but not necessarily fully ready
+ bool m_TheoraInitialised; //Theora headers are initialised and ready for data.
+ bool m_TheoraStateInitialised; //The vorbis state struct is primed, but not necessarily fully ready
+
+ float m_Duration; //duration if known
+
+ MovieTexture* m_Texture;
+ AudioClip* m_AudioClip;
+#if ENABLE_WWW
+ WWW* m_DataStream; //if != NULL, use this as data.
+#endif
+ FMOD::Channel* m_AudioChannel;
+
+ void QueueOggPageIntoStream();
+ double GetMovieTime(bool useAudio);
+ void ChangeMovieData(UInt8 *data,long size);
+ bool MovieStreamImage();
+ bool InitStreams(int &theoraHeadersSeen, int &vorbisHeaderSeen);
+ void Cleanup();
+ void CleanupInfoStructures();
+ int ReadBufferIntoOggStream();
+ void PauseAudio();
+
+public:
+ MoviePlayback();
+ ~MoviePlayback();
+
+ int GetMovieWidth();
+ int GetMovieHeight();
+ int GetMovieAudioRate();
+ int GetMovieAudioChannelCount();
+ int GetMovieBitrate();
+ float GetMovieTotalDuration() const {return m_Duration;}
+
+ bool IsPlaying() {return m_IsPlaying;}
+
+ void SetLoop (bool l);
+ bool GetLoop () {return m_Loop;}
+
+ //Do we have a video and/or audio track?
+ bool MovieHasAudio();
+ bool MovieHasVideo();
+
+ //Load movie from a data ptr
+ bool LoadMovieData(UInt8 *data,long size);
+
+#if ENABLE_WWW
+ //Load movie from a web stream (and track data internally)
+ bool LoadMovieData(WWW *stream);
+#endif
+
+ bool DidLoad() { return m_VorbisInitialised || m_TheoraInitialised; }
+
+ void SetMovieTexture(MovieTexture *t) {m_Texture=t;}
+ void SetMovieAudioClip(AudioClip *c) {m_AudioClip=c;}
+ void SetAudioChannel(FMOD::Channel* channel);
+
+ bool GetAudioBuffer(void** buffer, unsigned* size);
+
+ void MoviePlaybackClose();
+
+ void Play();
+ void Pause();
+ void Stop ();
+ void Rewind();
+
+ bool Update();
+};
+
+#else // ENABLE_MOVIES
+
+class WWW;
+class MovieTexture;
+class AudioClip;
+class AudioSource;
+namespace FMOD
+{
+ class Channel;
+}
+
+// dummy implementation
+class MoviePlayback
+{
+public:
+ MoviePlayback() {}
+ ~MoviePlayback() {}
+
+ int GetMovieWidth() { return 320; }
+ int GetMovieHeight() { return 240; }
+ int GetMovieAudioRate() { return 22050; }
+ int GetMovieAudioChannelCount() { return 1; }
+ int GetMovieBitrate() { return 0; }
+ float GetMovieTotalDuration() {return 0;}
+
+ bool IsPlaying() {return false;}
+
+ void SetLoop (bool l) {}
+ bool GetLoop () {return false;}
+
+ //Do we have a video and/or audio track?
+ bool MovieHasAudio() { return false; }
+ bool MovieHasVideo() { return false; }
+
+ //Load movie from a data ptr
+ bool LoadMovieData(UInt8 *data,long size) {
+ return false;
+ }
+
+ //Load movie from a web stream (and track data internally)
+ bool LoadMovieData(WWW *stream) {
+ return false;
+ }
+
+ bool DidLoad() {return false; }
+
+ void SetMovieTexture(MovieTexture *t) {}
+ void SetMovieAudioClip(AudioClip *c) {}
+ void SetAudioChannel(FMOD::Channel* channel) {}
+
+ void MoviePlaybackClose() {}
+ void Play() {}
+ void Pause() {}
+ void Stop () {}
+ void Rewind() {}
+
+ void Update() {}
+};
+
+inline void AddToUpdateList(MoviePlayback *m) {}
+inline void RemoveFromUpdateList(MoviePlayback *m) {}
+
+inline void UpdateMovies() {}
+inline void ResetMovies() {}
+inline void PauseMovies() {}
+
+#endif
+
+#endif
diff --git a/Runtime/Video/MovieTexture.cpp b/Runtime/Video/MovieTexture.cpp
new file mode 100644
index 0000000..73534c9
--- /dev/null
+++ b/Runtime/Video/MovieTexture.cpp
@@ -0,0 +1,262 @@
+#include "UnityPrefix.h"
+#include "MovieTexture.h"
+
+#if ENABLE_MOVIES
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "BaseVideoTexture.h"
+
+// --------------------------------------------------------------------------
+
+MovieTexture::MovieTexture (MemLabelId label, ObjectCreationMode mode, WWW *streamData)
+: BaseVideoTexture(label, mode)
+{
+ m_AudioClip = NULL;
+#if ENABLE_WWW
+ m_StreamData = NULL;
+#endif // ENABLE_WWW
+ m_MoviePlayback.SetMovieTexture( this );
+ m_MoviePlayback.SetMovieAudioClip( m_AudioClip );
+ m_TextureSettings.m_WrapMode = kTexWrapClamp;
+}
+
+#if ENABLE_WWW
+void MovieTexture::InitStream (WWW * streamData)
+{
+ AssertIf(m_AudioClip.GetInstanceID() != 0);
+ AssertIf(m_StreamData != NULL);
+ m_StreamData = streamData;
+ if(m_StreamData)
+ m_StreamData->Retain();
+
+ if (streamData != NULL)
+ {
+ m_AudioClip = NEW_OBJECT(AudioClip);
+ m_AudioClip->Reset();
+ m_AudioClip->InitStream(NULL, &m_MoviePlayback);
+ }
+ else
+ m_AudioClip = NULL;
+
+ m_MoviePlayback.SetMovieAudioClip( m_AudioClip );
+
+ // right now we're trying to load movie/sound in AwakeFromLoad (and do only that) - so we skip the call
+ HackSetAwakeWasCalled();
+}
+#endif // ENABLE_WWW
+
+MovieTexture::~MovieTexture ()
+{
+/* if( m_ImageBuffer )
+ {
+ // The allocated buffer for one frame has extra line before the pointer
+ // we use in all operations. YUV decoding code operates two lines at a time,
+ // goes backwards and thus needs extra line before the buffer in case of odd
+ // movie sizes.
+ UInt32* realBuffer = m_ImageBuffer - m_TextureWidth;
+ delete[] realBuffer;
+ }*/
+#if ENABLE_WWW
+ if(m_StreamData) m_StreamData->Release();
+#endif // ENABLE_WWW
+}
+
+void MovieTexture::SetMovieData(const UInt8* data,long size)
+{
+ m_MovieData.assign(data,data+size);
+
+ #if !UNITY_RELEASE
+ // right now we're trying to load movie/sound in AwakeFromLoad (and do only that) - so we skip the call
+ HackSetAwakeWasCalled();
+ #endif
+
+}
+
+template<class TransferFunction>
+void MovieTexture::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ bool loop = GetLoop();
+ transfer.Transfer( loop, "m_Loop", kNoTransferFlags );
+ SetLoop(loop);
+ transfer.Align();
+ transfer.Transfer( m_AudioClip, "m_AudioClip", kNotEditableMask );
+ transfer.Transfer( m_MovieData, "m_MovieData", kHideInEditorMask );
+ transfer.Transfer( m_ColorSpace, "m_ColorSpace", kHideInEditorMask );
+}
+
+bool MovieTexture::ShouldIgnoreInGarbageDependencyTracking()
+{
+ return false;
+}
+
+bool MovieTexture::ReadyToPlay () //check if enough of movie loaded to start playing
+{
+ if (!m_MoviePlayback.DidLoad())
+ TryLoadMovie();
+ if (m_MoviePlayback.DidLoad())
+ {
+#if ENABLE_WWW
+ if (m_StreamData)
+ {
+ // Get duration from comment tag
+ double totalDuration = m_MoviePlayback.GetMovieTotalDuration();
+ // If there's now duration comment (Movie not encoded with unity), estimate using bitrate
+ if (totalDuration <= 0)
+ {
+ m_StreamData->LockPartialData ();
+ int totalSize = m_StreamData->GetPartialSize() / m_StreamData->GetProgress();
+ totalDuration = totalSize/(m_MoviePlayback.GetMovieBitrate() / 8.0);
+ m_StreamData->UnlockPartialData ();
+ }
+
+ // Do we have enough to start?
+ if (m_StreamData->GetETA() < totalDuration*1.1F)
+ return true;
+ }
+ else
+#endif // ENABLE_WWW
+ return true; //if this isn't a web stream then we can always start
+ }
+ return false;
+}
+
+void MovieTexture::TryLoadMovie ()
+{
+#if ENABLE_WWW
+ if(m_StreamData)
+ m_MoviePlayback.LoadMovieData(m_StreamData);
+ else
+#endif // ENABLE_WWW
+ m_MoviePlayback.LoadMovieData(&*m_MovieData.begin(),m_MovieData.size());
+
+ if(m_MoviePlayback.DidLoad())
+ {
+ //if movie loaded, init texture structures
+ int width = m_MoviePlayback.GetMovieWidth();
+ int height = m_MoviePlayback.GetMovieHeight();
+
+ InitVideoMemory(width, height);
+ }
+
+ #if !UNITY_RELEASE
+ // right now we're trying to load movie/sound in AwakeFromLoad (and do only that) - so we skip the call
+ HackSetAwakeWasCalled();
+ #endif
+}
+
+void MovieTexture::Play ()
+{
+ if(!m_MoviePlayback.DidLoad())
+ TryLoadMovie();
+ if(m_MoviePlayback.DidLoad())
+ {
+ m_MoviePlayback.Play();
+ BaseVideoTexture::Play();
+ }
+}
+
+void MovieTexture::Pause ()
+{
+ if(m_MoviePlayback.DidLoad())
+ {
+ m_MoviePlayback.Pause();
+ BaseVideoTexture::Pause();
+ }
+}
+
+void MovieTexture::Stop ()
+{
+ if(m_MoviePlayback.DidLoad())
+ {
+ m_MoviePlayback.Stop();
+ BaseVideoTexture::Stop();
+ }
+}
+
+void MovieTexture::Rewind ()
+{
+ if(m_MoviePlayback.DidLoad())
+ m_MoviePlayback.Rewind();
+}
+
+void MovieTexture::Update()
+{
+ if(m_MoviePlayback.DidLoad())
+ {
+ if(m_MoviePlayback.Update())
+ UploadTextureData();
+ }
+}
+
+bool MovieTexture::IsPlaying ()
+{
+ if(m_MoviePlayback.DidLoad())
+ return m_MoviePlayback.IsPlaying();
+ return false;
+}
+
+void MovieTexture::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ if (!m_AudioClip.IsNull())
+ {
+ m_AudioClip->SetMoviePlayback(&m_MoviePlayback);
+ m_MoviePlayback.SetMovieAudioClip( m_AudioClip );
+ }
+ TryLoadMovie();
+}
+
+void MovieTexture::SetMovieAudioClip(AudioClip *clip)
+{
+ m_AudioClip=clip;
+ m_AudioClip->SetMoviePlayback(&m_MoviePlayback);
+ m_MoviePlayback.SetMovieAudioClip( m_AudioClip );
+}
+
+void MovieTexture::UnloadFromGfxDevice (bool forceUnloadAll)
+{
+ if (!m_MoviePlayback.DidLoad())
+ return;
+
+ // Here we want to unload strictly GFX device-specific data,
+ // and since the image buffer is not GFX device specific, it should
+ // be left untouched. We do, however, delete the GFX texture.
+ GetGfxDevice().DeleteTexture(GetTextureID());
+}
+
+void MovieTexture::UploadToGfxDevice ()
+{
+ if (!m_MoviePlayback.DidLoad())
+ return;
+
+ int width = m_MoviePlayback.GetMovieWidth();
+ int height = m_MoviePlayback.GetMovieHeight();
+
+ int texwidth = GetDataWidth();
+ int texheight = GetDataHeight();
+
+ if (GetImageBuffer() == NULL || width != texwidth || height != texheight)
+ {
+ // This accommodates the change in width and height of the image buffer,
+ // since in such a case we want to recreate the image buffer.
+ ReleaseVideoMemory();
+ InitVideoMemory(width, height);
+ }
+ else
+ {
+ // Recreate the Gfx structure and upload the previously allocated image buffer back
+ CreateGfxTextureAndUploadData(false);
+ }
+
+ UploadTextureData();
+}
+
+
+IMPLEMENT_CLASS (MovieTexture)
+IMPLEMENT_OBJECT_SERIALIZE (MovieTexture)
+
+#endif
diff --git a/Runtime/Video/MovieTexture.h b/Runtime/Video/MovieTexture.h
new file mode 100644
index 0000000..401968f
--- /dev/null
+++ b/Runtime/Video/MovieTexture.h
@@ -0,0 +1,77 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_MOVIES
+
+#include "BaseVideoTexture.h"
+#include "MoviePlayback.h"
+#include "Runtime/Audio/AudioClip.h"
+#if ENABLE_WWW
+#include "Runtime/Export/WWW.h"
+#endif
+#include <vector>
+
+class ColorRGBAf;
+class MoviePlayback;
+
+
+class MovieTexture: public BaseVideoTexture
+{
+private:
+
+ std::vector<UInt8> m_MovieData; //the raw Ogg movie data
+ MoviePlayback m_MoviePlayback; //class controlling playback state
+ PPtr<AudioClip> m_AudioClip; //attached AudioClip
+#if ENABLE_WWW
+ WWW *m_StreamData; //if != NULL, use this instead of m_MovieData.
+#else
+ typedef void WWW;
+#endif
+
+protected:
+// void DestroyTexture ();
+
+ void TryLoadMovie ();
+
+public:
+ REGISTER_DERIVED_CLASS (MovieTexture, Texture)
+ DECLARE_OBJECT_SERIALIZE (MovieTexture)
+
+#if ENABLE_WWW
+ // WARNING: don't call AwakeFromLoad if you use InitStream
+ void InitStream (WWW * streamData);
+#endif
+ void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ void Rewind();
+ virtual void Play();
+ virtual void Pause();
+ virtual void Stop ();
+ virtual void Update ();
+ virtual void UnloadFromGfxDevice(bool forceUnloadAll);
+ virtual void UploadToGfxDevice();
+
+ bool IsPlaying();
+ bool GetLoop () {return m_MoviePlayback.GetLoop();}
+ void SetLoop (bool l) {m_MoviePlayback.SetLoop(l);}
+
+ bool ReadyToPlay ();
+
+ virtual bool ShouldIgnoreInGarbageDependencyTracking ();
+ MovieTexture (MemLabelId label, ObjectCreationMode mode, WWW* streamData = NULL);
+
+ #if ENABLE_PROFILER || UNITY_EDITOR
+ virtual int GetStorageMemorySize() const { return m_MovieData.size(); }
+ #endif
+
+ // WARNING: don't call AwakeFromLoad if you use SetMovieData
+ void SetMovieData(const UInt8* data,long size);
+ std::vector<UInt8> *GetMovieData() { return &m_MovieData; }
+
+ AudioClip *GetMovieAudioClip() { return m_AudioClip; }
+ void SetMovieAudioClip(AudioClip *clip);
+
+ float GetMovieDuration() { return m_MoviePlayback.GetMovieTotalDuration(); }
+};
+
+#endif // ENABLE_MOVIES
diff --git a/Runtime/Video/ScriptBindings/MovieTextureBindings.txt b/Runtime/Video/ScriptBindings/MovieTextureBindings.txt
new file mode 100644
index 0000000..b3b07d3
--- /dev/null
+++ b/Runtime/Video/ScriptBindings/MovieTextureBindings.txt
@@ -0,0 +1,92 @@
+C++RAW
+
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Profiler/ProfilerHistory.h"
+#include "Runtime/Misc/PlayerSettings.h"
+#include "Runtime/Allocator/MemoryManager.h"
+#include "Runtime/Audio/AudioClip.h"
+#if ENABLE_AUDIO
+#include "Runtime/Audio/AudioSource.h"
+#include "Runtime/Audio/AudioListener.h"
+#include "Runtime/Audio/AudioManager.h"
+#include "Runtime/Audio/AudioReverbZone.h"
+#include "Runtime/Audio/AudioReverbFilter.h"
+#include "Runtime/Audio/AudioHighPassFilter.h"
+#include "Runtime/Audio/AudioLowPassFilter.h"
+#include "Runtime/Audio/AudioChorusFilter.h"
+#include "Runtime/Audio/AudioDistortionFilter.h"
+#include "Runtime/Audio/AudioEchoFilter.h"
+#endif
+#include "Runtime/Animation/Animation.h"
+#include "Runtime/Video/MovieTexture.h"
+
+using namespace Unity;
+
+/*
+ Mono defines a bool as either 1 or 2 bytes.
+ On windows a bool on the C++ side needs to be 2 bytes.
+ We use the typemap to map bool's to short's.
+ When using the C++ keyword and you want to export a bool value
+ to mono you have to use a short on the C++ side.
+*/
+
+
+void PauseEditor ();
+using namespace std;
+
+CSRAW
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngineInternal;
+
+namespace UnityEngine
+{
+
+
+// Movie Textures (Pro only) are textures onto which movies are played back.
+CONDITIONAL ENABLE_MOVIES
+CLASS MovieTexture : Texture
+
+ // Starts playing the movie.
+ AUTO void Play ();
+
+ // Stops playing the movie, and rewinds it to the beginning
+ AUTO void Stop ();
+
+ // Pauses playing the movie.
+ AUTO void Pause ();
+
+ // Returns the [[AudioClip]] belonging to the MovieTexture.
+
+ CONDITIONAL ENABLE_AUDIO
+ AUTO_PTR_PROP AudioClip audioClip GetMovieAudioClip
+
+ // Set this to true to make the movie loop.
+
+ AUTO_PROP bool loop GetLoop SetLoop
+
+ // Returns whether the movie is playing or not
+
+ AUTO_PROP bool isPlaying IsPlaying
+
+ // If the movie is downloading from a web site, this returns if enough data has been downloaded so playback should be able to start without interruptions.
+
+ AUTO_PROP bool isReadyToPlay ReadyToPlay
+
+ // The time, in seconds, that the movie takes to play back completely.
+ AUTO_PROP float duration GetMovieDuration
+
+END
+
+
+
+CSRAW }
+
diff --git a/Runtime/Video/ScriptBindings/UnityEngineWebCamTexture.txt b/Runtime/Video/ScriptBindings/UnityEngineWebCamTexture.txt
new file mode 100644
index 0000000..278d7dd
--- /dev/null
+++ b/Runtime/Video/ScriptBindings/UnityEngineWebCamTexture.txt
@@ -0,0 +1,200 @@
+C++RAW
+
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Video/VideoTexture.h"
+
+CSRAW
+
+namespace UnityEngine
+{
+
+CONDITIONAL ENABLE_WEBCAM
+// *undocumented*
+ENUM WebCamFlags
+ // Camera faces the same direction as screen
+ FrontFacing = 1,
+END
+
+
+CONDITIONAL ENABLE_WEBCAM
+// A structure describing the webcam device.
+STRUCT WebCamDevice
+ // A human-readable name of the device. Varies across different systems.
+ CSRAW public string name { get { return m_Name; } }
+
+ // True if camera faces the same direction a screen does, false otherwise.
+ CSRAW public bool isFrontFacing { get { return (m_Flags & ((int)WebCamFlags.FrontFacing)) == 1; } }
+
+ CSRAW internal string m_Name;
+ CSRAW internal int m_Flags;
+END
+
+
+CONDITIONAL ENABLE_WEBCAM
+// WebCam Textures are textures onto which the live video input is rendered
+CLASS WebCamTexture : Texture
+
+ CUSTOM private static void Internal_CreateWebCamTexture ([Writable]WebCamTexture self, string device, int requestedWidth, int requestedHeight, int maxFramerate)
+ {
+ WebCamTexture* texture = NEW_OBJECT_MAIN_THREAD (WebCamTexture);
+ texture->Reset();
+ Scripting::ConnectScriptingWrapperToObject (self.GetScriptingObject(), texture);
+ texture->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+ texture->SetRequestedWidth (requestedWidth);
+ texture->SetRequestedHeight (requestedHeight);
+ texture->SetRequestedFPS (maxFramerate);
+ texture->SetDevice (device);
+ }
+
+ // Create a WebCamTexture
+
+ CSRAW public WebCamTexture (string deviceName, int requestedWidth, int requestedHeight, int requestedFPS)
+ {
+ Internal_CreateWebCamTexture (this, deviceName, requestedWidth, requestedHeight, requestedFPS);
+ }
+
+ ///*listonly*
+ CSRAW public WebCamTexture (string deviceName, int requestedWidth, int requestedHeight)
+ {
+ Internal_CreateWebCamTexture (this, deviceName, requestedWidth, requestedHeight, 0);
+ }
+
+ ///*listonly*
+ CSRAW public WebCamTexture (string deviceName)
+ {
+ Internal_CreateWebCamTexture (this, deviceName, 0, 0, 0);
+ }
+
+ ///*listonly*
+ CSRAW public WebCamTexture (int requestedWidth, int requestedHeight, int requestedFPS)
+ {
+ Internal_CreateWebCamTexture (this, "", requestedWidth, requestedHeight, requestedFPS);
+ }
+
+ ///*listonly*
+ CSRAW public WebCamTexture (int requestedWidth, int requestedHeight)
+ {
+ Internal_CreateWebCamTexture (this, "", requestedWidth, requestedHeight, 0);
+ }
+
+ ///*listonly*
+ CSRAW public WebCamTexture ()
+ {
+ Internal_CreateWebCamTexture (this, "", 0, 0, 0);
+ }
+
+ // Starts the camera
+ AUTO void Play();
+
+ // Pauses the camera.
+ AUTO void Pause();
+
+ // Stops the camera
+ AUTO void Stop();
+
+ // Returns if the camera is currently playing
+ AUTO_PROP bool isPlaying IsPlaying
+
+ // Set this to specify the name of the device to use.
+ CUSTOM_PROP string deviceName { return scripting_string_new(self->GetDevice ()); } { self->SetDevice (value); }
+
+ // Set the requested frame rate of the camera device (in frames per second).
+ AUTO_PROP float requestedFPS GetRequestedFPS SetRequestedFPS
+
+ // Set the requested width of the camera device.
+ AUTO_PROP int requestedWidth GetRequestedWidth SetRequestedWidth
+
+ // Set the requested height of the camera device.
+ AUTO_PROP int requestedHeight GetRequestedHeight SetRequestedHeight
+
+ CONDITIONAL UNITY_IPHONE_API
+ CUSTOM_PROP bool isReadable { return self->IsReadable(); }
+
+ CONDITIONAL UNITY_IPHONE_API
+ CUSTOM void MarkNonReadable() { self->SetReadable(false); }
+
+ // Return a list of available devices.
+ CUSTOM_PROP static WebCamDevice[] devices
+ {
+ MonoWebCamDevices devs;
+ WebCamTexture::GetDeviceNames(devs, true);
+
+ ScriptingClassPtr klass = GetScriptingManager().GetCommonClasses().webCamDevice;
+ ScriptingArrayPtr array = CreateScriptingArray<MonoWebCamDevice>(klass, devs.size());
+
+ for (MonoWebCamDevices::size_type i = 0; i < devs.size(); ++i)
+ {
+ #if UNITY_WINRT
+ ScriptingObjectPtr dev = CreateScriptingObjectFromNativeStruct(klass, devs[i]);
+ Scripting::SetScriptingArrayElement<ScriptingObjectPtr>(array, i, dev);
+ #else
+ Scripting::SetScriptingArrayElement<MonoWebCamDevice>(array, i, devs[i]);
+ #endif
+ }
+
+ return array;
+ }
+
+ // Returns pixel color at coordinates (x, y).
+ CUSTOM Color GetPixel (int x, int y) {
+ return self->GetPixel (x, y);
+ }
+
+ // Get a block of pixel colors.
+ CSRAW public Color[] GetPixels()
+ {
+ return GetPixels( 0, 0, width, height );
+ }
+
+ // Get a block of pixel colors.
+ CUSTOM Color[] GetPixels(int x, int y, int blockWidth, int blockHeight)
+ {
+ int res = blockWidth * blockHeight;
+ if (blockWidth != 0 && blockHeight != res / blockWidth) {
+ return SCRIPTING_NULL;
+ }
+ ScriptingArrayPtr colors = CreateScriptingArray<ColorRGBAf>(GetScriptingManager().GetCommonClasses().color, res );
+ self->GetPixels( x, y, blockWidth, blockHeight, &Scripting::GetScriptingArrayElement<ColorRGBAf>(colors, 0));
+ return colors;
+ }
+
+ // Returns the pixels data in raw format
+
+ CUSTOM public Color32[] GetPixels32(Color32[] colors = null)
+ {
+ int w = self->GetDataWidth();
+ int h = self->GetDataHeight();
+ if (colors != SCRIPTING_NULL)
+ {
+ int size = GetScriptingArraySize(colors);
+ if (size != w * h)
+ {
+ ErrorStringMsg ("Input color array length needs to match width * height, but %d != %d * %d", size, w, h);
+ return SCRIPTING_NULL;
+ }
+ }
+ else
+ colors = CreateScriptingArray<ColorRGBA32>(GetScriptingManager().GetCommonClasses().color32, w * h);
+ self->GetPixels(kTexFormatRGBA32, &Scripting::GetScriptingArrayElement<ColorRGBA32>(colors, 0), GetScriptingArraySize(colors) * 4);
+ return colors;
+ }
+
+ // Returns an clockwise angle, which can be used to rotate a polygon so camera contents are shown in correct orientation.
+ CUSTOM_PROP int videoRotationAngle { return self->GetVideoRotationAngle(); }
+
+ CUSTOM_PROP bool videoVerticallyMirrored
+ {
+ return self->IsVideoVerticallyMirrored();
+ }
+
+ // Did the video buffer update this frame?
+ AUTO_PROP bool didUpdateThisFrame DidUpdateThisFrame
+
+END
+
+CSRAW
+}
diff --git a/Runtime/Video/VideoTexture.h b/Runtime/Video/VideoTexture.h
new file mode 100644
index 0000000..ffa4dd4
--- /dev/null
+++ b/Runtime/Video/VideoTexture.h
@@ -0,0 +1,252 @@
+#ifndef LIVE_VIDEO_TEXTURE
+#define LIVE_VIDEO_TEXTURE
+
+#if ENABLE_WEBCAM
+
+#include "BaseVideoTexture.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+#if UNITY_WINRT
+#include <windows.foundation.h>
+#endif
+
+struct PlatformDependentWebCamTextureData;
+
+enum WebCamFlags
+{
+ kWebCamFrontFacing = 1,
+};
+
+struct MonoWebCamDevice
+{
+ ScriptingStringPtr name;
+ int flags;
+
+ bool operator== (std::string const &other) const
+ {
+ std::string cppStr = scripting_cpp_string_for (name);
+ return cppStr == other;
+ }
+};
+
+typedef UNITY_VECTOR(kMemWebCam, MonoWebCamDevice) MonoWebCamDevices;
+typedef MonoWebCamDevices::iterator MonoWebCamDevicesIter;
+
+class WebCamTexture: public BaseVideoTexture
+{
+public:
+ REGISTER_DERIVED_CLASS (WebCamTexture, Texture)
+
+ WebCamTexture (MemLabelId label, ObjectCreationMode mode = kCreateObjectDefault)
+ : BaseVideoTexture(label, mode)
+ {
+ m_RequestedFPS = 0.0f;
+ m_RequestedWidth = 0;
+ m_RequestedHeight = 0;
+ m_IsCreated = false;
+ m_VT = NULL;
+
+#if UNITY_WINRT
+ RunExactlyOnce();
+#endif
+ }
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ virtual void Play();
+ virtual void Pause();
+ virtual void Stop ();
+ virtual void Update ();
+
+#if UNITY_WP8
+ virtual void Suspend();
+ virtual void Resume();
+#endif
+
+ void SetRequestedWidth (int width) { m_RequestedWidth = width; SetDirty(); }
+ int GetRequestedWidth () const { return m_RequestedWidth; }
+
+ void SetRequestedHeight (int width) { m_RequestedHeight = width; SetDirty(); }
+ int GetRequestedHeight () const { return m_RequestedHeight; }
+
+ void SetRequestedFPS (float fps) { m_RequestedFPS = fps; SetDirty(); }
+ float GetRequestedFPS () const { return m_RequestedFPS; }
+
+ void SetDevice (const std::string &name) { m_DeviceName = name; SetDirty(); }
+ std::string GetDevice() const;
+
+ static void GetDeviceNames (MonoWebCamDevices &devices, bool forceUpdate);
+
+ #if ENABLE_PROFILER || UNITY_EDITOR
+ virtual int GetStorageMemorySize() const { return 0; }
+ #endif
+
+ ColorRGBAf GetPixel (int x, int y) const;
+ bool GetPixels (int x, int y, int width, int height, ColorRGBAf* data) const;
+ bool GetPixels (int dstFormat, void *dstData, size_t dstSize) const;
+
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_BLACKBERRY || UNITY_TIZEN
+ virtual int GetVideoRotationAngle() const;
+#endif
+#if UNITY_IPHONE || UNITY_BLACKBERRY
+ virtual bool IsVideoVerticallyMirrored() const;
+#endif
+
+private:
+#if UNITY_WINRT
+ // C-tor helper. It is here to ensure that some invariants are satisfied from the moment
+ // the object is created, so we don't have to check and recheck the state all the time
+ void RunExactlyOnce();
+#endif
+
+ void Create();
+ void Cleanup ();
+
+public:
+ static void EnsureUniqueName (MonoWebCamDevice &device,
+ const MonoWebCamDevices &devs);
+private:
+ int m_RequestedWidth;
+ int m_RequestedHeight;
+ float m_RequestedFPS;
+ std::string m_DeviceName;
+ bool m_IsCreated;
+
+ static void InitDeviceList();
+ int GetDeviceIdFromDeviceList(const std::string& name) const;
+
+ PlatformDependentWebCamTextureData *m_VT;
+
+protected:
+#if UNITY_IPHONE
+ virtual TextureFormat GetBufferTextureFormat() const { return kTexFormatBGRA32; }
+ virtual bool CanSetReadable(bool readable) const;
+#endif
+
+#if UNITY_BLACKBERRY || UNITY_TIZEN || UNITY_WP8
+ virtual TextureFormat GetBufferTextureFormat() const { return kTexFormatBGRA32; }
+#endif
+};
+
+inline ColorRGBAf WebCamTexture::GetPixel (int x, int y) const
+{
+ if (!m_IsCreated)
+ {
+ ErrorString ("Cannot get pixels when webcam is not running");
+ return ColorRGBAf(0,0,0,0);
+ }
+ if (!IsReadable())
+ {
+ ErrorString ("Cannot get pixels when webcam is non-readable");
+ return ColorRGBAf(0,0,0,0);
+ }
+
+ return GetImagePixel ((UInt8*)GetImageBuffer(), GetPaddedWidth(), GetPaddedHeight(), GetBufferTextureFormat(), static_cast<TextureWrapMode>(GetSettings().m_WrapMode), x, y);
+}
+
+inline bool WebCamTexture::GetPixels( int x, int y, int width, int height, ColorRGBAf* colors ) const
+{
+ if (width == 0 || height == 0)
+ return true; // nothing to do
+
+ if (!m_IsCreated)
+ {
+ ErrorString ("Cannot get pixels when webcam is not running");
+ return false;
+ }
+ if (!IsReadable())
+ {
+ ErrorString ("Cannot get pixels when webcam is non-readable");
+ return false;
+ }
+
+ return GetImagePixelBlock ((UInt8*)GetImageBuffer(), GetPaddedWidth(), GetPaddedHeight(), GetBufferTextureFormat(), x, y, width, height, colors);
+}
+
+inline bool WebCamTexture::GetPixels (int dstFormat, void *dstData, size_t dstSize) const
+{
+ size_t srcRowBytes = GetRowBytesFromWidthAndFormat(GetPaddedWidth(), GetBufferTextureFormat());
+ size_t dstRowBytes = GetRowBytesFromWidthAndFormat(GetDataWidth(), dstFormat);
+ if (dstSize < dstRowBytes * GetDataHeight())
+ {
+ ErrorString ("Buffer is too small to get image data");
+ return false;
+ }
+
+ ImageReference src (GetDataWidth(), GetDataHeight(), srcRowBytes, GetBufferTextureFormat(), (UInt8*)GetImageBuffer());
+ ImageReference dst (GetDataWidth(), GetDataHeight(), dstRowBytes, dstFormat, dstData);
+ dst.BlitImage( src );
+ return true;
+}
+
+inline int WebCamTexture::GetDeviceIdFromDeviceList(const std::string& name) const
+{
+ MonoWebCamDevices names;
+ GetDeviceNames(names, false);
+ if(!name.empty())
+ {
+ for(int i = 0 ; i < names.size() ; i++)
+ {
+ if(names[i] == name)
+ return i;
+ }
+ ErrorString ("Cannot find webcam device "+name+".");
+ return -1;
+ }
+ else
+ {
+ // Return camera 0 as default
+ if(!names.empty())
+ return 0;
+ else
+ {
+ ErrorString ("No available webcams are found. Either there is no webcam connected, or they are all in use by other applications (like Skype).");
+ return -1;
+ }
+ }
+}
+
+inline std::string WebCamTexture::GetDevice() const
+{
+ if(m_DeviceName.size() > 0)
+ {
+ return m_DeviceName;
+ }
+ else
+ {
+ MonoWebCamDevices names;
+ GetDeviceNames(names, false);
+
+ if(names.size() > 0)
+ return scripting_cpp_string_for(names[0].name);
+ else
+ return "no camera available.";
+ }
+}
+
+inline void WebCamTexture::EnsureUniqueName (MonoWebCamDevice &device,
+ MonoWebCamDevices const &devs)
+{
+ int num = 0;
+ std::string testname = scripting_cpp_string_for (device.name);
+
+ while (true)
+ {
+ if (num > 0)
+ testname += Format (" %d", num);
+
+ if (std::find (devs.begin (), devs.end (), testname) == devs.end ())
+ {
+ device.name = scripting_string_new(testname.c_str ());
+ break;
+ }
+
+ num++;
+ }
+}
+
+#endif
+#endif
diff --git a/Runtime/mecanim/animation/avatar.cpp b/Runtime/mecanim/animation/avatar.cpp
new file mode 100644
index 0000000..441353b
--- /dev/null
+++ b/Runtime/mecanim/animation/avatar.cpp
@@ -0,0 +1,1799 @@
+#include "UnityPrefix.h"
+
+#include "Runtime/mecanim/animation/avatar.h"
+
+#include "Runtime/mecanim/generic/valuearray.h"
+#include "Runtime/mecanim/generic/crc32.h"
+#include "Runtime/mecanim/generic/stringtable.h"
+#include "Runtime/mecanim/skeleton/skeleton.h"
+#include "Runtime/mecanim/human/human.h"
+#include "Runtime/mecanim/animation/clipmuscle.h"
+#include "Runtime/mecanim/animation/curvedata.h"
+#include "Runtime/mecanim/statemachine/statemachine.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+
+#include "Runtime/Misc/BuildSettings.h"
+
+namespace mecanim
+{
+
+namespace memory
+{
+ Profiler* Profiler::s_Profiler = NULL;
+}
+
+namespace animation
+{
+
+ AnimationSet* CreateAnimationSet(ControllerConstant const* controller, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(AnimationSet);
+
+ AnimationSet* animationSet = alloc.Construct<AnimationSet>();
+
+ animationSet->m_LayerCount = controller->m_LayerCount;
+
+ animationSet->m_ClipPerLayer = alloc.ConstructArray<uint32_t>(animationSet->m_LayerCount);
+ memset(animationSet->m_ClipPerLayer,0,sizeof(uint32_t)*animationSet->m_LayerCount);
+
+ animationSet->m_ClipConstant = alloc.ConstructArray<AnimationSet::Clip*>(animationSet->m_LayerCount);
+
+ animationSet->m_AdditionalCount = controller->m_Values->m_Count;
+ animationSet->m_AdditionalIndexArray = alloc.ConstructArray<int32_t>(animationSet->m_AdditionalCount);
+ for(int i = 0; i < animationSet->m_AdditionalCount; i++)
+ animationSet->m_AdditionalIndexArray[i] = -1;
+
+ animationSet->m_DynamicValuesMaskArray = alloc.ConstructArray<ValueArrayMask *>(animationSet->m_LayerCount);
+
+ for(int layerIter = 0; layerIter < animationSet->m_LayerCount; layerIter++)
+ {
+ int clipCount = 0;
+
+ mecanim::uint32_t stateMachineIndex = controller->m_LayerArray[layerIter]->m_StateMachineIndex;
+
+ if(stateMachineIndex != DISABLED_SYNCED_LAYER_IN_NON_PRO)
+ {
+ mecanim::uint32_t motionSetIndex = controller->m_LayerArray[layerIter]->m_StateMachineMotionSetIndex;
+
+ const statemachine::StateMachineConstant* stateMachineConstant = controller->m_StateMachineArray[stateMachineIndex].Get();
+
+ for(int stateIter = 0; stateIter < stateMachineConstant->m_StateConstantCount; stateIter++)
+ {
+ statemachine::StateConstant const& stateConstant = *controller->m_StateMachineArray[stateMachineIndex]->m_StateConstantArray[stateIter];
+
+ clipCount += stateConstant.m_LeafInfoArray[motionSetIndex].m_Count;
+
+ for(int blendTreeIter = 0; blendTreeIter < stateConstant.m_BlendTreeCount; blendTreeIter++)
+ {
+ uint32_t blendCount = GetMaxBlendCount(*stateConstant.m_BlendTreeConstantArray[blendTreeIter]);
+
+ if(blendCount > animationSet->m_MaxBlendState)
+ {
+ animationSet->m_MaxBlendState = blendCount;
+ }
+ }
+ }
+ }
+
+ animationSet->m_ClipPerLayer[layerIter] = clipCount;
+ animationSet->m_ClipConstant[layerIter] = alloc.ConstructArray<AnimationSet::Clip>(clipCount);
+
+ animationSet->m_DynamicValuesMaskArray[layerIter] = 0;
+ }
+
+ // if there is no blend tree the worst case is when we are doing a transition between two state
+ // in this case we need to evaluate two clip.
+ animationSet->m_MaxBlendState = math::maximum<uint32_t>(animationSet->m_MaxBlendState*2, 2);
+
+ return animationSet;
+ }
+
+ void DestroyAnimationSet(AnimationSet* animationSet, memory::Allocator& alloc)
+ {
+ if(animationSet)
+ {
+ alloc.Deallocate(animationSet->m_ClipPerLayer);
+
+ for(int layerIter = 0; layerIter < animationSet->m_LayerCount; layerIter++)
+ {
+ alloc.Deallocate(animationSet->m_ClipConstant[layerIter]);
+ DestroyValueArrayMask(animationSet->m_DynamicValuesMaskArray[layerIter],alloc);
+ }
+
+ alloc.Deallocate(animationSet->m_ClipConstant);
+ alloc.Deallocate(animationSet->m_AdditionalIndexArray);
+ alloc.Deallocate(animationSet->m_DynamicValuesMaskArray);
+ DestroyValueArrayConstant(animationSet->m_DynamicFullValuesConstant, alloc);
+ alloc.Deallocate(animationSet);
+ }
+ }
+
+ AnimationSetMemory* CreateAnimationSetMemory(AnimationSet const* animationSet, bool allowConstantCurveOptimization, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(AnimationSetMemory);
+
+ AnimationSetMemory* memory = alloc.Construct<AnimationSetMemory>();
+
+ memory->m_LayerCount = animationSet->m_LayerCount;
+ memory->m_ClipPerLayer = alloc.ConstructArray<uint32_t>(animationSet->m_LayerCount);
+ memory->m_ClipMemory = alloc.ConstructArray<ClipMemory**>(animationSet->m_LayerCount);
+
+ uint32_t maxCurvesCount = 0;
+ for(int layerIter = 0; layerIter < animationSet->m_LayerCount; layerIter++)
+ {
+ memory->m_ClipPerLayer[layerIter] = animationSet->m_ClipPerLayer[layerIter];
+ memory->m_ClipMemory[layerIter] = alloc.ConstructArray<ClipMemory*>(animationSet->m_ClipPerLayer[layerIter]);
+
+ for(int clipIter = 0; clipIter < memory->m_ClipPerLayer[layerIter]; clipIter++)
+ {
+ const ClipMuscleConstant* clip = animationSet->m_ClipConstant[layerIter][clipIter].m_Clip;
+
+ if(clip != 0)
+ {
+ uint32_t usedCurves;
+ if (allowConstantCurveOptimization)
+ usedCurves = animationSet->m_ClipConstant[layerIter][clipIter].m_TotalUsedOptimizedCurveCount;
+ else
+ usedCurves = GetClipCurveCount(*clip);
+
+ maxCurvesCount = math::maximum(maxCurvesCount, usedCurves);
+ memory->m_ClipMemory[layerIter][clipIter] = mecanim::animation::CreateClipMemory(clip->m_Clip.Get(), usedCurves, alloc);
+ }
+ else
+ {
+ memory->m_ClipMemory[layerIter][clipIter] = 0;
+ }
+ }
+ }
+
+ memory->m_ClipOutput = mecanim::animation::CreateClipOutput(maxCurvesCount, alloc);
+
+ return memory;
+ }
+
+ void DestroyAnimationSetMemory(AnimationSetMemory* animationSetMemory, memory::Allocator& alloc)
+ {
+ if (animationSetMemory)
+ {
+ for(int layerIter = 0; layerIter < animationSetMemory->m_LayerCount; layerIter++)
+ {
+ for(int clipIter = 0 ; clipIter < animationSetMemory->m_ClipPerLayer[layerIter]; clipIter++)
+ {
+ mecanim::animation::DestroyClipMemory(animationSetMemory->m_ClipMemory[layerIter][clipIter], alloc);
+ }
+
+ alloc.Deallocate(animationSetMemory->m_ClipMemory[layerIter]);
+ }
+
+ alloc.Deallocate(animationSetMemory->m_ClipPerLayer);
+ alloc.Deallocate(animationSetMemory->m_ClipMemory);
+ mecanim::animation::DestroyClipOutput(animationSetMemory->m_ClipOutput, alloc);
+ alloc.Deallocate(animationSetMemory);
+ }
+ }
+
+ void AvatarConstant::InitializeClass()
+ {
+ RegisterAllowNameConversion("AvatarConstant", "m_Skeleton", "m_AvatarSkeleton");
+ RegisterAllowNameConversion("AvatarConstant", "m_SkeletonPose", "m_AvatarSkeletonPose");
+ }
+
+ AvatarConstant* CreateAvatarConstant( skeleton::Skeleton* skeleton,
+ skeleton::SkeletonPose* skeletonPose,
+ skeleton::SkeletonPose* defaultPose,
+ human::Human* human,
+ skeleton::Skeleton* rootMotionSkeleton,
+ int rootMotionIndex,
+ math::xform const& rootMotionX,
+ memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(AvatarConstant);
+
+ AvatarConstant* cst = alloc.Construct<AvatarConstant>();
+
+ cst->m_AvatarSkeleton = skeleton;
+ cst->m_AvatarSkeletonPose = skeletonPose;
+ cst->m_DefaultPose = defaultPose;
+ cst->m_Human = human;
+ cst->m_RootMotionSkeleton = rootMotionSkeleton;
+ cst->m_RootMotionBoneIndex = rootMotionIndex;
+ cst->m_RootMotionBoneX = rootMotionX;
+
+ if(human != 0)
+ {
+ cst->m_HumanSkeletonIndexCount = human->m_Skeleton->m_Count;
+ cst->m_HumanSkeletonIndexArray = alloc.ConstructArray<mecanim::int32_t>(cst->m_HumanSkeletonIndexCount);
+ skeleton::SkeletonBuildIndexArray(cst->m_HumanSkeletonIndexArray.Get(),human->m_Skeleton.Get(),skeleton);
+ cst->m_HumanSkeletonReverseIndexCount = cst->m_AvatarSkeleton->m_Count;
+ cst->m_HumanSkeletonReverseIndexArray = alloc.ConstructArray<mecanim::int32_t>(cst->m_HumanSkeletonReverseIndexCount);
+ skeleton::SkeletonBuildReverseIndexArray(cst->m_HumanSkeletonReverseIndexArray.Get(),cst->m_HumanSkeletonIndexArray.Get(),human->m_Skeleton.Get(),skeleton);
+ }
+ else if(rootMotionIndex != -1)
+ {
+ cst->m_RootMotionSkeletonIndexCount = rootMotionSkeleton->m_Count;
+ cst->m_RootMotionSkeletonIndexArray = alloc.ConstructArray<mecanim::int32_t>(cst->m_RootMotionSkeletonIndexCount);
+ skeleton::SkeletonBuildIndexArray(cst->m_RootMotionSkeletonIndexArray.Get(),cst->m_RootMotionSkeleton.Get(),skeleton);
+ }
+
+ return cst;
+ }
+
+ void DestroyAvatarConstant(AvatarConstant* constant, memory::Allocator& alloc)
+ {
+ if(constant)
+ {
+ alloc.Deallocate(constant->m_HumanSkeletonIndexArray);
+ alloc.Deallocate(constant->m_HumanSkeletonReverseIndexArray);
+ alloc.Deallocate(constant->m_RootMotionSkeletonIndexArray);
+ alloc.Deallocate(constant);
+ }
+ }
+
+ void ClearAvatarConstant(AvatarConstant * constant, memory::Allocator& alloc)
+ {
+
+ }
+
+ AvatarInput* CreateAvatarInput(AvatarConstant const* constant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(AvatarInput);
+ AvatarInput* input = alloc.Construct<AvatarInput>();
+
+ return input;
+ }
+
+ void DestroyAvatarInput(AvatarInput* input, memory::Allocator& alloc)
+ {
+ if(input)
+ {
+ alloc.Deallocate(input->m_GotoStateInfos);
+ alloc.Deallocate(input);
+ }
+ }
+
+ AvatarMemory* CreateAvatarMemory(AvatarConstant const* constant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(AvatarMemory);
+ AvatarMemory* mem = alloc.Construct<AvatarMemory>();
+
+ return mem;
+ }
+
+ void DestroyAvatarMemory(AvatarMemory* memory, memory::Allocator& alloc)
+ {
+ if(memory)
+ {
+ alloc.Deallocate(memory);
+ }
+ }
+
+ AvatarWorkspace* CreateAvatarWorkspace(AvatarConstant const* constant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(AvatarWorkspace);
+ AvatarWorkspace* ws = alloc.Construct<AvatarWorkspace>();
+
+ if(constant->isHuman())
+ {
+ if(!constant->m_AvatarSkeleton.IsNull())
+ {
+ ws->m_BodySkeletonPoseWs = skeleton::CreateSkeletonPose(constant->m_Human->m_Skeleton.Get(), alloc);
+ ws->m_BodySkeletonPoseWsA = skeleton::CreateSkeletonPose(constant->m_Human->m_Skeleton.Get(), alloc);
+ ws->m_BodySkeletonPoseWsB = skeleton::CreateSkeletonPose(constant->m_Human->m_Skeleton.Get(), alloc);
+
+ ws->m_HumanPoseWs = alloc.Construct<human::HumanPose>();
+ }
+ }
+ else if(constant->m_RootMotionBoneIndex != -1)
+ {
+ if(!constant->m_RootMotionSkeleton.IsNull())
+ {
+ ws->m_RootMotionSkeletonPoseWsA = skeleton::CreateSkeletonPose(constant->m_RootMotionSkeleton.Get(), alloc);
+ ws->m_RootMotionSkeletonPoseWsB = skeleton::CreateSkeletonPose(constant->m_RootMotionSkeleton.Get(), alloc);
+ }
+ }
+
+ return ws;
+ }
+
+ void DestroyAvatarWorkspace(AvatarWorkspace* workspace, memory::Allocator& alloc)
+ {
+ if(workspace)
+ {
+ skeleton::DestroySkeletonPose(workspace->m_BodySkeletonPoseWsB, alloc);
+ skeleton::DestroySkeletonPose(workspace->m_BodySkeletonPoseWsA, alloc);
+ skeleton::DestroySkeletonPose(workspace->m_BodySkeletonPoseWs, alloc);
+ skeleton::DestroySkeletonPose(workspace->m_RootMotionSkeletonPoseWsB, alloc);
+ skeleton::DestroySkeletonPose(workspace->m_RootMotionSkeletonPoseWsA, alloc);
+ alloc.Deallocate(workspace->m_HumanPoseWs);
+
+ alloc.Deallocate(workspace);
+ }
+ }
+
+ AvatarOutput* CreateAvatarOutput(AvatarConstant const* constant, bool hasTransformHierarchy, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(AvatarOutput);
+ AvatarOutput* out = alloc.Construct<AvatarOutput>();
+
+ if(hasTransformHierarchy)
+ {
+ if(!constant->m_Human.IsNull())
+ {
+ if(!constant->m_AvatarSkeleton.IsNull() && constant->m_AvatarSkeleton->m_Count > 0)
+ {
+ out-> m_SkeletonPoseOutput = skeleton::CreateSkeletonPose(constant->m_AvatarSkeleton.Get(), alloc);
+ }
+ }
+ }
+ else
+ {
+ if(!constant->m_AvatarSkeleton.IsNull() && constant->m_AvatarSkeleton->m_Count > 0)
+ {
+ out->m_SkeletonPoseOutput = skeleton::CreateSkeletonPose(constant->m_AvatarSkeleton.Get(), alloc);
+ }
+ }
+
+ if(constant->m_RootMotionBoneIndex != -1 || !constant->m_Human.IsNull())
+ {
+ out->m_MotionOutput = alloc.Construct<MotionOutput>();
+
+ if(!constant->m_Human.IsNull())
+ {
+ out->m_HumanPoseBaseOutput = alloc.Construct<human::HumanPose>();
+ out->m_HumanPoseOutput = alloc.Construct<human::HumanPose>();
+ }
+ }
+
+ return out;
+ }
+
+ void DestroyAvatarOutput(AvatarOutput* output, memory::Allocator& alloc)
+ {
+ if(output)
+ {
+ if(output->m_DynamicValuesOutput) DestroyValueArray(output->m_DynamicValuesOutput,alloc);
+ skeleton::DestroySkeletonPose(output->m_SkeletonPoseOutput, alloc);
+ alloc.Deallocate(output->m_MotionOutput);
+ alloc.Deallocate(output->m_HumanPoseOutput);
+ alloc.Deallocate(output->m_HumanPoseBaseOutput);
+ alloc.Deallocate(output);
+ }
+ }
+
+ void ControllerConstant::InitializeClass()
+ {
+ RegisterAllowNameConversion("ControllerConstant", "m_HumanLayerCount", "m_LayerCount");
+ RegisterAllowNameConversion("ControllerConstant", "m_HumanLayerArray", "m_LayerArray");
+ }
+
+ ControllerConstant* CreateControllerConstant( uint32_t layerCount, LayerConstant** layerArray,
+ uint32_t stateMachineCount, statemachine::StateMachineConstant** stateMachineConstantArray,
+ ValueArrayConstant* values, ValueArray* defaultValues,
+ memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(ControllerConstant);
+
+ ControllerConstant* controller = alloc.Construct<ControllerConstant>();
+
+ controller->m_LayerCount = layerCount;
+ controller->m_LayerArray = alloc.ConstructArray< OffsetPtr<LayerConstant> >(controller->m_LayerCount);
+
+ uint32_t i;
+ for(i=0;i<controller->m_LayerCount;i++)
+ controller->m_LayerArray[i] = layerArray[i];
+
+ controller->m_StateMachineCount = stateMachineCount;
+ controller->m_StateMachineArray = alloc.ConstructArray< OffsetPtr<statemachine::StateMachineConstant> >(controller->m_StateMachineCount);
+
+ for(i=0;i<controller->m_StateMachineCount;i++)
+ controller->m_StateMachineArray[i] = stateMachineConstantArray[i];
+
+ controller->m_Values = values;
+ controller->m_DefaultValues = defaultValues;
+
+ return controller;
+ }
+
+ void DestroyControllerConstant(ControllerConstant* controller, memory::Allocator& alloc)
+ {
+ if(controller)
+ {
+ alloc.Deallocate(controller->m_LayerArray);
+ alloc.Deallocate(controller->m_StateMachineArray);
+ alloc.Deallocate(controller);
+ }
+ }
+
+
+ LayerConstant* CreateLayerConstant(mecanim::uint32_t stateMachineIndex, mecanim::uint32_t motionSetIndex, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(LayerConstant);
+
+ LayerConstant* cst = alloc.Construct<LayerConstant>();
+ cst->m_StateMachineIndex = stateMachineIndex;
+ cst->m_StateMachineMotionSetIndex = motionSetIndex;
+ return cst;
+ }
+
+ void DestroyLayerConstant(LayerConstant* constant, memory::Allocator& alloc)
+ {
+ if(constant)
+ {
+ alloc.Deallocate(constant);
+ }
+ }
+
+ ControllerMemory* CreateControllerMemory(ControllerConstant const* controller, AvatarConstant const *avatar, AnimationSet const *animationSet, const ValueArrayConstant* dynamicValueConstant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(ControllerMemory);
+
+ ControllerMemory* mem = alloc.Construct<ControllerMemory>();
+
+ mem->m_LayerCount = controller->m_LayerCount;
+ mem->m_StateMachineCount = controller->m_StateMachineCount;
+ mem->m_StateMachineMemory = alloc.ConstructArray< OffsetPtr<statemachine::StateMachineMemory> >(mem->m_StateMachineCount);
+ mem->m_InteruptedTransitionsBlendingStateArray = alloc.ConstructArray< BlendingState<false> > (mem->m_LayerCount);
+ mem->m_LayerWeights = alloc.ConstructArray<float>(mem->m_LayerCount);
+
+ mem->m_Values = CreateValueArray( controller->m_Values.Get(), alloc);
+ ValueArrayCopy(controller->m_DefaultValues.Get(), mem->m_Values.Get());
+
+ for(int layerIter = 0; layerIter < controller->m_LayerCount; layerIter++)
+ {
+ mem->m_LayerWeights[layerIter] = controller->m_LayerArray[layerIter]->m_DefaultWeight;
+
+ mem->m_InteruptedTransitionsBlendingStateArray[layerIter].m_DynamicValuesBlending = mecanim::CreateValueArray(dynamicValueConstant, alloc);
+ mem->m_InteruptedTransitionsBlendingStateArray[layerIter].m_DynamicValuesBlendingMask = mecanim::CreateValueArrayMask(dynamicValueConstant, alloc);
+
+ if(avatar->isHuman())
+ {
+ mem->m_InteruptedTransitionsBlendingStateArray[layerIter].m_MotionBlending = alloc.Construct<mecanim::animation::MotionOutput>();
+ mem->m_InteruptedTransitionsBlendingStateArray[layerIter].m_HumanPoseBlending = alloc.Construct<mecanim::human::HumanPose>();
+ }
+ else if(avatar->m_RootMotionBoneIndex != -1)
+ {
+ mem->m_InteruptedTransitionsBlendingStateArray[layerIter].m_MotionBlending = alloc.Construct<mecanim::animation::MotionOutput>();
+ mem->m_InteruptedTransitionsBlendingStateArray[layerIter].m_HumanPoseBlending = 0;
+ }
+ else
+ {
+ mem->m_InteruptedTransitionsBlendingStateArray[layerIter].m_MotionBlending = 0;
+ mem->m_InteruptedTransitionsBlendingStateArray[layerIter].m_HumanPoseBlending = 0;
+ }
+ }
+
+ for(int stateMachineIter = 0; stateMachineIter < mem->m_StateMachineCount; stateMachineIter++)
+ {
+ mem->m_StateMachineMemory[stateMachineIter] = statemachine::CreateStateMachineMemory(controller->m_StateMachineArray[stateMachineIter].Get(), alloc);
+ }
+
+ return mem;
+ }
+
+ void DestroyControllerMemory(ControllerMemory* controllerMemory, memory::Allocator& alloc)
+ {
+ if(controllerMemory)
+ {
+ for(int layerIter = 0; layerIter < controllerMemory->m_LayerCount; layerIter++)
+ {
+ if(!controllerMemory->m_InteruptedTransitionsBlendingStateArray[layerIter].m_DynamicValuesBlending.IsNull()) DestroyValueArray(controllerMemory->m_InteruptedTransitionsBlendingStateArray[layerIter].m_DynamicValuesBlending.Get(),alloc);
+ if(!controllerMemory->m_InteruptedTransitionsBlendingStateArray[layerIter].m_DynamicValuesBlendingMask.IsNull()) DestroyValueArrayMask(controllerMemory->m_InteruptedTransitionsBlendingStateArray[layerIter].m_DynamicValuesBlendingMask.Get(),alloc);
+ if(!controllerMemory->m_InteruptedTransitionsBlendingStateArray[layerIter].m_MotionBlending.IsNull()) alloc.Deallocate(controllerMemory->m_InteruptedTransitionsBlendingStateArray[layerIter].m_MotionBlending.Get());
+ if(!controllerMemory->m_InteruptedTransitionsBlendingStateArray[layerIter].m_HumanPoseBlending.IsNull()) alloc.Deallocate(controllerMemory->m_InteruptedTransitionsBlendingStateArray[layerIter].m_HumanPoseBlending.Get());
+ }
+
+ for(int smIter = 0; smIter < controllerMemory->m_StateMachineCount; smIter++)
+ {
+ statemachine::DestroyStateMachineMemory(controllerMemory->m_StateMachineMemory[smIter].Get(), alloc);
+ }
+
+ DestroyValueArray(controllerMemory->m_Values.Get(), alloc);
+ alloc.Deallocate(controllerMemory->m_LayerWeights);
+ alloc.Deallocate(controllerMemory->m_InteruptedTransitionsBlendingStateArray);
+ alloc.Deallocate(controllerMemory->m_StateMachineMemory);
+
+ alloc.Deallocate(controllerMemory);
+ }
+ }
+
+ BlendingState<true>* CreateBlendingState(uint32_t size, bool createMotionState, bool createHumanPose, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendingState_true);
+
+ BlendingState<true>* blendingState = alloc.Construct< BlendingState<true> >();
+
+ blendingState->m_DynamicValuesBlending = alloc.ConstructArray<ValueArray*>(size);
+ blendingState->m_BlendFactor = alloc.ConstructArray<float>(size);
+
+ memset(&blendingState->m_DynamicValuesBlending[0], 0, sizeof(ValueArray*) * size);
+ memset(&blendingState->m_BlendFactor[0], 0, sizeof(float) * size);
+
+ if(createMotionState || createHumanPose)
+ {
+ blendingState->m_MotionBlending = alloc.ConstructArray<MotionOutput*>(size);
+ memset(&blendingState->m_MotionBlending[0], 0, sizeof(MotionOutput*) * size);
+ }
+
+ if(createHumanPose)
+ {
+ blendingState->m_HumanPoseBlending = alloc.ConstructArray<human::HumanPose*>(size);
+ memset(&blendingState->m_HumanPoseBlending[0], 0, sizeof(human::HumanPose*) * size);
+ }
+
+ blendingState->m_Size = size;
+
+ return blendingState;
+ }
+
+ void DestroyBlendingState(BlendingState<true>* blendingState, memory::Allocator& alloc)
+ {
+ if(blendingState)
+ {
+ for(uint32_t i=0;i<blendingState->m_Size;++i)
+ {
+ DestroyValueArray(blendingState->m_DynamicValuesBlending[i],alloc);
+ if(blendingState->m_MotionBlending) alloc.Deallocate(blendingState->m_MotionBlending[i]);
+ if(blendingState->m_HumanPoseBlending) alloc.Deallocate(blendingState->m_HumanPoseBlending[i]);
+ }
+
+ alloc.Deallocate(blendingState->m_DynamicValuesBlending);
+ alloc.Deallocate(blendingState->m_MotionBlending);
+ alloc.Deallocate(blendingState->m_HumanPoseBlending);
+ alloc.Deallocate(blendingState->m_BlendFactor);
+
+ alloc.Deallocate(blendingState);
+ }
+ }
+
+ ControllerWorkspace *CreateControllerWorkspace(ControllerConstant const* controller, AvatarConstant const *avatar, AnimationSet const *animationSet, const ValueArrayConstant* dynamicValueConstant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(ControllerWorkspace);
+ bool forRootMotion = avatar->m_RootMotionBoneIndex != -1;
+ bool forHuman = avatar->isHuman();
+
+ ControllerWorkspace* ws = alloc.Construct<ControllerWorkspace>();
+
+ ws->m_StateMachineOutput = alloc.ConstructArray<statemachine::StateMachineOutput*>(controller->m_StateMachineCount);
+ ws->m_StateMachineWorkspace = alloc.ConstructArray<statemachine::StateMachineWorkspace*>(controller->m_StateMachineCount);
+ ws->m_StateMachineCount = controller->m_StateMachineCount;
+
+ uint32_t i;
+ uint32_t maxMotionSet = 0;
+ for(i = 0 ; i < controller->m_StateMachineCount; ++i)
+ {
+ maxMotionSet = math::maximum<uint32_t>(maxMotionSet, controller->m_StateMachineArray[i]->m_MotionSetCount);
+ }
+ ws->m_MotionSetTimingWeightArray = alloc.ConstructArray<float>(maxMotionSet);
+
+ for(int stateMachineIter = 0; stateMachineIter < ws->m_StateMachineCount; stateMachineIter++)
+ {
+ ws->m_StateMachineOutput[stateMachineIter] = statemachine::CreateStateMachineOutput(controller->m_StateMachineArray[stateMachineIter].Get(), animationSet->m_MaxBlendState, alloc);
+ ws->m_StateMachineWorkspace[stateMachineIter] = statemachine::CreateStateMachineWorkspace(controller->m_StateMachineArray[stateMachineIter].Get(), animationSet->m_MaxBlendState, alloc);
+ }
+
+ ws->m_BlendingState = CreateBlendingState(animationSet->m_MaxBlendState, forRootMotion, forHuman, alloc);
+
+ for(int blendStateIter = 0; blendStateIter < ws->m_BlendingState->m_Size; blendStateIter++)
+ {
+ ws->m_BlendingState->m_DynamicValuesBlending[blendStateIter] = mecanim::CreateValueArray(dynamicValueConstant, alloc);
+
+ if(forHuman)
+ {
+ {
+ SETPROFILERLABEL(MotionOutput);
+ ws->m_BlendingState->m_MotionBlending[blendStateIter] = alloc.Construct<mecanim::animation::MotionOutput>();
+ }
+ {
+ SETPROFILERLABEL(HumanPose);
+ ws->m_BlendingState->m_HumanPoseBlending[blendStateIter] = alloc.Construct<mecanim::human::HumanPose>();
+ }
+ }
+ else if(forRootMotion)
+ {
+ ws->m_BlendingState->m_MotionBlending[blendStateIter] = alloc.Construct<mecanim::animation::MotionOutput>();
+ }
+ }
+
+ ws->m_BlendingStateWs.m_DynamicValuesBlending = mecanim::CreateValueArray(dynamicValueConstant, alloc);
+
+ if (forHuman)
+ {
+ ws->m_BlendingStateWs.m_MotionBlending = alloc.Construct<animation::MotionOutput>();
+ ws->m_BlendingStateWs.m_HumanPoseBlending = alloc.Construct<human::HumanPose>();
+ }
+ else if (forRootMotion)
+ {
+ ws->m_BlendingStateWs.m_MotionBlending = alloc.Construct<mecanim::animation::MotionOutput>();
+ }
+
+ ws->m_ValueArrayStart = CreateValueArray(dynamicValueConstant, alloc);
+ ws->m_ValueArrayStop = CreateValueArray(dynamicValueConstant, alloc);
+ ws->m_BlendingClipArray = alloc.ConstructArray<mecanim::animation::BlendingClip>(controller->m_LayerCount*animationSet->m_MaxBlendState);
+
+ ws->m_ReadMask = CreateValueArrayMask(dynamicValueConstant, alloc);
+ ws->m_BlendMask = CreateValueArrayMask(dynamicValueConstant, alloc);
+ ws->m_DefaultMask = CreateValueArrayMask(dynamicValueConstant, alloc);
+
+ return ws;
+ }
+
+ void DestroyControllerWorkspace(ControllerWorkspace* controllerWorkspace, memory::Allocator& alloc)
+ {
+ if(controllerWorkspace)
+ {
+ uint32_t i;
+ for(i=0;i<controllerWorkspace->m_StateMachineCount;i++)
+ {
+ DestroyStateMachineOutput(controllerWorkspace->m_StateMachineOutput[i], alloc);
+ DestroyStateMachineWorkspace(controllerWorkspace->m_StateMachineWorkspace[i], alloc);
+ }
+
+ DestroyBlendingState(controllerWorkspace->m_BlendingState, alloc);
+
+ DestroyValueArray(controllerWorkspace->m_BlendingStateWs.m_DynamicValuesBlending.Get(), alloc);
+ alloc.Deallocate(controllerWorkspace->m_BlendingStateWs.m_MotionBlending);
+ alloc.Deallocate(controllerWorkspace->m_BlendingStateWs.m_HumanPoseBlending);
+
+ alloc.Deallocate(controllerWorkspace->m_BlendingClipArray);
+
+ alloc.Deallocate(controllerWorkspace->m_MotionSetTimingWeightArray);
+
+ alloc.Deallocate(controllerWorkspace->m_StateMachineWorkspace);
+ alloc.Deallocate(controllerWorkspace->m_StateMachineOutput);
+ DestroyValueArray(controllerWorkspace->m_ValueArrayStart,alloc);
+ DestroyValueArray(controllerWorkspace->m_ValueArrayStop,alloc);
+
+ DestroyValueArrayMask(controllerWorkspace->m_ReadMask,alloc);
+ DestroyValueArrayMask(controllerWorkspace->m_BlendMask,alloc);
+ DestroyValueArrayMask(controllerWorkspace->m_DefaultMask,alloc);
+
+ alloc.Deallocate(controllerWorkspace);
+ }
+ }
+
+ void UpdateLeafNodeDuration(const ControllerConstant &controllerConstant, const AnimationSet &animationSet, ControllerMemory &controllerMemory)
+ {
+ for(int layerIter = 0; layerIter < controllerConstant.m_LayerCount; layerIter++)
+ {
+ mecanim::uint32_t stateMachineIndex = controllerConstant.m_LayerArray[layerIter]->m_StateMachineIndex;
+ mecanim::uint32_t motionSetIndex = controllerConstant.m_LayerArray[layerIter]->m_StateMachineMotionSetIndex;
+
+ const statemachine::StateMachineConstant& stateMachineConstant = *controllerConstant.m_StateMachineArray[stateMachineIndex].Get();
+
+ for(int stateIter = 0; stateIter < stateMachineConstant.m_StateConstantCount; stateIter++)
+ {
+ statemachine::StateConstant const &stateConstant = *stateMachineConstant.m_StateConstantArray[stateIter].Get();
+
+ for(int leafIter = 0; leafIter < stateConstant.m_LeafInfoArray[motionSetIndex].m_Count ; leafIter++)
+ {
+ int clipIndex = leafIter + stateConstant.m_LeafInfoArray[motionSetIndex].m_IndexOffset;
+
+ AnimationSet::Clip& setClip = animationSet.m_ClipConstant[layerIter][clipIndex];
+
+ statemachine::GetBlendTreeMemory(stateConstant,*controllerMemory.m_StateMachineMemory[stateMachineIndex]->m_StateMemoryArray[stateIter],motionSetIndex)->m_NodeDurationArray[leafIter] = setClip.m_Clip != 0 ? (setClip.m_Clip->m_StopTime - setClip.m_Clip->m_StartTime) : 0.0f;
+ }
+ }
+ }
+ }
+
+ void SetIKOnFeet(bool left,AvatarConstant const &avatar, const AvatarInput &input, AvatarMemory &memory, AvatarWorkspace &workspace, AvatarOutput &output)
+ {
+ float deltaTime = input.m_DeltaTime;
+ bool stabilizeFeet = input.m_StabilizeFeet;
+
+ math::xform avatarX = memory.m_AvatarX;
+
+ math::float1 scale(avatar.m_Human->m_Scale);
+
+ math::xform ddx = math::xformInvMulNS(avatarX,workspace.m_AvatarX);
+ float dSpeedT = math::length(ddx.t).tofloat() / deltaTime;
+ float dSpeedQ = math::length(math::doubleAtan(math::quat2Qtan(ddx.q))).tofloat() /deltaTime;
+
+ int32_t footIndex = left ? human::kLeftFoot : human::kRightFoot;
+ int32_t goalIndex = left ? human::kLeftFootGoal : human::kRightFootGoal;
+
+ output.m_HumanPoseOutput->m_GoalArray[goalIndex].m_WeightT = 1;
+ output.m_HumanPoseOutput->m_GoalArray[goalIndex].m_WeightR = 1;
+
+ if(stabilizeFeet && footIndex != -1 && memory.m_FirstEval==0)
+ {
+ float speedT = left ? workspace.m_LeftFootSpeedT : workspace.m_RightFootSpeedT;
+ float speedQ = left ? workspace.m_LeftFootSpeedQ : workspace.m_RightFootSpeedQ;
+
+ speedT += dSpeedT;
+ speedQ += dSpeedQ;
+
+ math::xform &footX = left ? memory.m_LeftFootX : memory.m_RightFootX;
+
+ math::xform footGoalX0 = output.m_HumanPoseOutput->m_GoalArray[goalIndex].m_X;
+ math::xform footGoalX = footGoalX0;
+
+ math::xform goalDX = math::xformInvMulNS(footX,footGoalX);
+
+ float goalDTLen = math::length(goalDX.t).tofloat();
+
+ if(goalDTLen > 0)
+ {
+ float goalSpeedT = goalDTLen / deltaTime;
+ speedT = math::cond(speedT > 0.1f, speedT, 0.0f);
+ speedT = math::cond(speedT > 1.0f, 2.0f * speedT, speedT);
+ float goalSpeedTClamp = math::minimum(goalSpeedT,speedT);
+ goalDX.t = goalDX.t * math::float1(goalSpeedTClamp/goalSpeedT);
+ }
+
+ float goalDQLen = math::length(math::doubleAtan(math::quat2Qtan(goalDX.q))).tofloat();
+
+ if(goalDQLen > 0)
+ {
+ float goalSpeedQ = goalDQLen / deltaTime;
+ speedQ = math::cond(speedQ > math::radians(10.0f), speedQ, 0.0f);
+ speedQ = math::cond(speedQ > math::radians(100.0f), 2.0f * speedQ, speedQ);
+ float goalSpeedQClamp = math::minimum(goalSpeedQ,speedQ);
+ goalDX.q = math::qtan2Quat(math::halfTan(math::doubleAtan(math::quat2Qtan(goalDX.q))*math::float1(goalSpeedQClamp/goalSpeedQ)));
+ }
+
+ footX = math::xformMul(footX,goalDX);
+
+ output.m_HumanPoseOutput->m_GoalArray[goalIndex].m_X = footX;
+ }
+
+ math::float4 feetSpacing = math::quatMulVec(output.m_HumanPoseOutput->m_GoalArray[goalIndex].m_X.q, math::float4(0,0,(left?-1:1)*avatar.m_Human->m_Scale*avatar.m_Human->m_FeetSpacing,0));
+ feetSpacing.y() = math::float1::zero();
+
+ output.m_HumanPoseOutput->m_GoalArray[goalIndex].m_X.t += avatarX.s * feetSpacing;
+ }
+
+ void EvaluateAvatarSM( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput * output,
+ AvatarMemory * memory,
+ AvatarWorkspace * workspace,
+ ControllerConstant const* controllerConstant)
+ {
+ if(controllerConstant)
+ {
+ workspace->m_IKOnFeet = false;
+
+ uint32_t i;
+
+ for(i = 0; i < controllerConstant->m_StateMachineCount; i++)
+ {
+ statemachine::StateMachineInput stateMachineInput;
+ stateMachineInput.m_MotionSetTimingWeightArray = workspace->m_ControllerWorkspace->m_MotionSetTimingWeightArray;
+
+ for(int layerIndex = 0; layerIndex < controllerConstant->m_LayerCount; layerIndex++)
+ {
+ const uint32_t stateMachineIndex = controllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+ const uint32_t motionSetIndex = controllerConstant->m_LayerArray[layerIndex]->m_StateMachineMotionSetIndex;
+
+ if(stateMachineIndex != DISABLED_SYNCED_LAYER_IN_NON_PRO)
+ {
+ if(i == stateMachineIndex )
+ {
+ if(motionSetIndex == 0)
+ stateMachineInput.m_GotoStateInfo = &input->m_GotoStateInfos[layerIndex];
+
+ if( controllerConstant->m_LayerArray[layerIndex]->m_SyncedLayerAffectsTiming || motionSetIndex == 0 )
+ stateMachineInput.m_MotionSetTimingWeightArray[motionSetIndex] = motionSetIndex == 0 ? 1 : memory->m_ControllerMemory->m_LayerWeights[layerIndex];
+ else
+ stateMachineInput.m_MotionSetTimingWeightArray[motionSetIndex] = 0 ;
+ }
+ }
+ }
+
+
+ stateMachineInput.m_DeltaTime = input->m_DeltaTime;
+
+ stateMachineInput.m_Values = memory->m_ControllerMemory->m_Values.Get();
+ workspace->m_ControllerWorkspace->m_StateMachineWorkspace[i]->m_ValuesConstant = const_cast<mecanim::ValueArrayConstant *>(controllerConstant->m_Values.Get());
+
+ statemachine::EvaluateStateMachine( controllerConstant->m_StateMachineArray[i].Get(),
+ &stateMachineInput,
+ workspace->m_ControllerWorkspace->m_StateMachineOutput[i],
+ memory->m_ControllerMemory->m_StateMachineMemory[i].Get(),
+ workspace->m_ControllerWorkspace->m_StateMachineWorkspace[i]);
+
+ workspace->m_IKOnFeet |= workspace->m_ControllerWorkspace->m_StateMachineOutput[i]->m_Left.m_IKOnFeet;
+ workspace->m_IKOnFeet |= workspace->m_ControllerWorkspace->m_StateMachineOutput[i]->m_Right.m_IKOnFeet;
+ }
+ }
+ }
+
+ void AdjustPoseForMotion(ControllerBindingConstant const* controllerBindingConstant, math::xform const &motionX, ValueArray &values, skeleton::SkeletonPose &pose, skeleton::SkeletonPose &poseWs)
+ {
+ AvatarConstant const *constant = controllerBindingConstant->m_Avatar;
+
+ int lastIndex = (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1)) ? constant->m_RootMotionSkeleton->m_Count-1 : constant->m_RootMotionBoneIndex ;
+
+ SkeletonPoseFromValue(*constant->m_RootMotionSkeleton.Get(), *constant->m_AvatarSkeletonPose.Get(), values, controllerBindingConstant->m_SkeletonTQSMap, constant->m_RootMotionSkeletonIndexArray.Get(), pose,lastIndex, 0);
+
+ skeleton::SkeletonPoseComputeGlobal(constant->m_RootMotionSkeleton.Get(),&pose,&poseWs);
+ pose.m_X[0] = motionX;
+
+ if(constant->m_RootMotionBoneIndex > 0)
+ skeleton::SkeletonPoseComputeGlobal(constant->m_RootMotionSkeleton.Get(),&pose,&poseWs,lastIndex-1,0);
+
+ skeleton::SkeletonPoseComputeLocal(constant->m_RootMotionSkeleton.Get(),&poseWs,&pose,lastIndex,lastIndex);
+ pose.m_X[0] = math::xformIdentity();
+
+ ValueFromSkeletonPose(*constant->m_RootMotionSkeleton.Get(), pose, controllerBindingConstant->m_SkeletonTQSMap, constant->m_RootMotionSkeletonIndexArray.Get(), values, lastIndex, 0);
+ }
+
+ void ComputeRootMotion(ControllerBindingConstant const* controllerBindingConstant, MotionOutput const &motionOutput, ValueArray &values, AvatarWorkspace *workspace)
+ {
+ AdjustPoseForMotion(controllerBindingConstant, motionOutput.m_MotionStartX, *workspace->m_ControllerWorkspace->m_ValueArrayStart, *workspace->m_RootMotionSkeletonPoseWsA, *workspace->m_RootMotionSkeletonPoseWsB);
+ AdjustPoseForMotion(controllerBindingConstant, motionOutput.m_MotionStopX, *workspace->m_ControllerWorkspace->m_ValueArrayStop, *workspace->m_RootMotionSkeletonPoseWsA, *workspace->m_RootMotionSkeletonPoseWsB);
+ AdjustPoseForMotion(controllerBindingConstant, motionOutput.m_MotionX, values, *workspace->m_RootMotionSkeletonPoseWsA, *workspace->m_RootMotionSkeletonPoseWsB);
+ }
+
+ void EvaluateBlendNode( ControllerBindingConstant const* controllerBindingConstant,
+ AvatarInput const* input,
+ int32_t layerIndex,
+ bool additive,
+ bool left,
+ uint32_t &clipCount,
+ ControllerMemory *memory,
+ AvatarWorkspace * workspace,
+ AvatarOutput * avatarOutput,
+ AnimationSetMemory* animationSetMemory,
+ float *blendFactor)
+ {
+ AvatarConstant const *constant = controllerBindingConstant->m_Avatar;
+ ControllerConstant const *controllerConstant = controllerBindingConstant->m_Controller;
+ AnimationSet const *animationSet = controllerBindingConstant->m_AnimationSet;
+
+ bool hasRootMotion = constant->m_RootMotionBoneIndex != -1;
+ bool isHuman = constant->isHuman();
+
+ uint32_t stateMachineIndex = controllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+ uint32_t motionSetIndex = controllerConstant->m_LayerArray[layerIndex]->m_StateMachineMotionSetIndex;
+
+ if(stateMachineIndex == DISABLED_SYNCED_LAYER_IN_NON_PRO)
+ return ;
+
+ statemachine::StateMachineOutput& stateMachineOutput = *workspace->m_ControllerWorkspace->m_StateMachineOutput[stateMachineIndex];
+ statemachine::BlendNode &blendNode = left ? stateMachineOutput.m_Left : stateMachineOutput.m_Right;
+
+ animation::ClipOutput* clipOutput = animationSetMemory->m_ClipOutput;
+
+ for(int32_t outputIter=0; outputIter < blendNode.m_BlendNodeLayer[motionSetIndex].m_OutputCount; outputIter++)
+ {
+ uint32_t index = blendNode.m_BlendNodeLayer[motionSetIndex].m_OutputIndexArray[outputIter];
+
+ AnimationSet::Clip& setClip = animationSet->m_ClipConstant[layerIndex][index];
+
+ if (setClip.m_Clip)
+ {
+ animation::ClipMuscleConstant const& muscleConstant = *setClip.m_Clip;
+
+ if(hasRootMotion || isHuman)
+ {
+ ClipMuscleInput muscleIn;
+ muscleIn.m_Time = blendNode.m_CurrentTime;
+ muscleIn.m_PreviousTime = blendNode.m_PreviousTime;
+ muscleIn.m_Mirror = blendNode.m_BlendNodeLayer[motionSetIndex].m_MirrorArray[outputIter];
+ muscleIn.m_CycleOffset = blendNode.m_BlendNodeLayer[motionSetIndex].m_CycleOffsetArray[outputIter];
+ muscleIn.m_Reverse = blendNode.m_BlendNodeLayer[motionSetIndex].m_ReverseArray[outputIter];
+
+ animation::MotionOutput* motionOutput = workspace->m_ControllerWorkspace->m_BlendingState->m_MotionBlending[clipCount];
+
+ if(isHuman) EvaluateClipMusclePrevTime(muscleConstant,muscleIn,*motionOutput,*animationSetMemory->m_ClipMemory[layerIndex][index]);
+ else if(hasRootMotion) EvaluateClipRootMotionDeltaX(muscleConstant,muscleIn,*motionOutput,*animationSetMemory->m_ClipMemory[layerIndex][index]);
+ }
+
+ ValueArray &values = avatarOutput != 0 && clipCount == 0 ? *avatarOutput->m_DynamicValuesOutput : *workspace->m_ControllerWorkspace->m_BlendingState->m_DynamicValuesBlending[clipCount];
+
+ ClipInput in;
+ float timeInt;
+ float normalizedTime = 0;
+ in.m_Time = ComputeClipTime(blendNode.m_CurrentTime,muscleConstant.m_StartTime,muscleConstant.m_StopTime,
+ muscleConstant.m_CycleOffset+blendNode.m_BlendNodeLayer[motionSetIndex].m_CycleOffsetArray[outputIter],
+ muscleConstant.m_LoopTime,
+ blendNode.m_BlendNodeLayer[motionSetIndex].m_ReverseArray[outputIter],
+ normalizedTime,timeInt);
+
+ ValueArrayMask& readMask = *workspace->m_ControllerWorkspace->m_ReadMask;
+ ValueArrayMask& blendMask = *workspace->m_ControllerWorkspace->m_BlendMask;
+
+ EvaluateClip(muscleConstant.m_Clip.Get(),&in,animationSetMemory->m_ClipMemory[layerIndex][index], clipOutput);
+ ValuesFromClip(*controllerBindingConstant->m_DynamicValuesDefault, muscleConstant, *clipOutput, setClip.m_Bindings, animationSet->m_IntegerRemapStride, values, readMask);
+
+ OrValueMask(&blendMask,&readMask);
+
+ if(muscleConstant.m_LoopTime && muscleConstant.m_LoopBlend)
+ {
+ DeltasFromClip(muscleConstant, setClip.m_Bindings, readMask, *workspace->m_ControllerWorkspace->m_ValueArrayStart, *workspace->m_ControllerWorkspace->m_ValueArrayStop);
+ }
+
+ if(hasRootMotion || isHuman)
+ {
+ ClipMuscleInput muscleIn;
+
+ muscleIn.m_Time = blendNode.m_CurrentTime;
+ muscleIn.m_PreviousTime = blendNode.m_PreviousTime;
+
+ animation::MotionOutput* motionOutput = workspace->m_ControllerWorkspace->m_BlendingState->m_MotionBlending[clipCount];
+
+ if(isHuman)
+ {
+ muscleIn.m_TargetIndex = input->m_TargetIndex;
+ muscleIn.m_TargetTime = input->m_TargetTime;
+ muscleIn.m_TargetIndex = muscleIn.m_TargetIndex > int32_t(animation::kTargetReference) ? muscleIn.m_TargetIndex : int32_t(animation::kTargetReference);
+ muscleIn.m_TargetIndex = muscleIn.m_TargetIndex < int32_t(animation::kTargetRightHand) ? muscleIn.m_TargetIndex : int32_t(animation::kTargetRightHand);
+
+ muscleIn.m_Mirror = blendNode.m_BlendNodeLayer[motionSetIndex].m_MirrorArray[outputIter];
+ muscleIn.m_CycleOffset = blendNode.m_BlendNodeLayer[motionSetIndex].m_CycleOffsetArray[outputIter];
+ muscleIn.m_Reverse = blendNode.m_BlendNodeLayer[motionSetIndex].m_ReverseArray[outputIter];
+
+ human::HumanPose *humanPose = workspace->m_ControllerWorkspace->m_BlendingState->m_HumanPoseBlending[clipCount];
+ EvaluateClipMuscle(muscleConstant,muscleIn,clipOutput->m_Values,*motionOutput,*humanPose,*animationSetMemory->m_ClipMemory[layerIndex][index]);
+
+ if(additive)
+ {
+ // put goals in same space as GetHumanPose
+ // @todo check with bob why referential spaces are different
+ for(int i = 0; i < human::kLastGoal; i++)
+ humanPose->m_GoalArray[i].m_X = math::xformInvMul(humanPose->m_RootX,humanPose->m_GoalArray[i].m_X);
+ humanPose->m_RootX = math::xformMul(motionOutput->m_MotionX, humanPose->m_RootX);
+
+ GetHumanPose(muscleConstant,muscleConstant.m_ValueArrayDelta.Get(),*workspace->m_HumanPoseWs);
+ human::HumanPoseSub(*humanPose,*humanPose,*workspace->m_HumanPoseWs);
+ }
+ }
+ else if(hasRootMotion)
+ {
+ ComputeRootMotion(controllerBindingConstant,*motionOutput,values,workspace);
+ }
+
+ if(animationSet->m_GravityWeightIndex != -1 && readMask.m_FloatValues[animationSet->m_GravityWeightIndex])
+ {
+ values.ReadData(motionOutput->m_GravityWeight, animationSet->m_GravityWeightIndex);
+ }
+ else
+ {
+ motionOutput->m_GravityWeight = muscleConstant.m_LoopBlendPositionY ? 1 : 0;
+ }
+ }
+
+ if(additive)
+ {
+ ValueArraySub(*workspace->m_ControllerWorkspace->m_ValueArrayStart,values, &readMask);
+ }
+
+ if(muscleConstant.m_LoopTime && muscleConstant.m_LoopBlend)
+ {
+ ValueArrayLoop(*workspace->m_ControllerWorkspace->m_ValueArrayStart, *workspace->m_ControllerWorkspace->m_ValueArrayStop,values,normalizedTime, readMask);
+ }
+
+ blendFactor[clipCount] = (left ? ( 1.f - stateMachineOutput.m_BlendFactor) : stateMachineOutput.m_BlendFactor) * blendNode.m_BlendNodeLayer[motionSetIndex].m_OutputBlendArray[outputIter];
+
+ BlendingClip &blendingClip = workspace->m_ControllerWorkspace->m_BlendingClipArray[workspace->m_ControllerWorkspace->m_BlendingClipCount];
+ blendingClip.m_ClipIndex = setClip.m_ClipIndex;
+ blendingClip.m_LayerIndex = layerIndex;
+ blendingClip.m_Weight = blendFactor[clipCount];
+ blendingClip.m_PrevTime = ComputeClipTime(blendNode.m_PreviousTime,muscleConstant.m_StartTime,muscleConstant.m_StopTime,muscleConstant.m_CycleOffset,muscleConstant.m_LoopTime,blendNode.m_BlendNodeLayer[motionSetIndex].m_ReverseArray[outputIter],normalizedTime,timeInt);
+ blendingClip.m_Time = in.m_Time;
+ blendingClip.m_Reverse = blendNode.m_BlendNodeLayer[motionSetIndex].m_ReverseArray[outputIter];
+
+ workspace->m_ControllerWorkspace->m_BlendingClipCount++;
+
+ clipCount++;
+ }
+ }
+ }
+
+ bool BlendDynamicStates(ValueArray const &valuesDefault, ValueArrayMask const &valueMask, ValueArray **valuesArray, float *blendFactor, int clipCount, ValueArray &valuesOutput, ValueArray *valuesInterupted, ValueArrayMask *valuesMaskInterupted, AvatarOutput *avatarOutput)
+ {
+ bool fastCopy = false;
+
+ if(clipCount > 0)
+ {
+ if(clipCount == 1 && CompareApproximately(blendFactor[0],1))
+ {
+ if(avatarOutput == 0)
+ {
+ ValueArrayCopy(valuesArray[0],&valuesOutput, &valueMask);
+ }
+ else
+ {
+ fastCopy = true;
+ }
+ }
+ else
+ {
+ ValueArray *values0 = valuesArray[0];
+ valuesArray[0] = avatarOutput ? avatarOutput->m_DynamicValuesOutput : valuesArray[0];
+ ValueArrayBlend(&valuesDefault,&valuesOutput, valuesArray, blendFactor, clipCount, &valueMask);
+ valuesArray[0] = values0;
+ }
+ }
+ else if(clipCount == 0)
+ {
+ ValueArrayCopy(&valuesDefault,&valuesOutput, &valueMask);
+ }
+
+ if(valuesInterupted != 0)
+ {
+ ValueArrayCopy(&valuesOutput,valuesInterupted, &valueMask);
+ CopyValueMask(valuesMaskInterupted,&valueMask);
+ }
+
+ return fastCopy;
+ }
+
+ void BlendMotionStates(MotionOutput **motionArray, float *blendFactor, int clipCount, MotionOutput *motionOutput, MotionOutput *motionInterupted, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &mask)
+ {
+ if(clipCount > 0)
+ {
+ if(clipCount == 1 && CompareApproximately(blendFactor[0],1))
+ {
+ MotionOutputCopy(motionOutput,motionArray[0], hasRootMotion, isHuman, mask);
+ }
+ else
+ {
+ MotionOutputBlend(motionOutput,motionArray,blendFactor,clipCount, hasRootMotion, isHuman, mask);
+ }
+ }
+
+ if(motionInterupted)
+ {
+ MotionOutputCopy(motionInterupted,motionOutput, hasRootMotion, isHuman, mask);
+ }
+ }
+
+ void BlendHumanStates(human::HumanPose **poseArray, float *blendFactor, int clipCount, human::HumanPose *poseOut, human::HumanPose *poseInterupted)
+ {
+ if(clipCount > 0)
+ {
+ if(clipCount == 1 && CompareApproximately(blendFactor[0],1))
+ {
+ human::HumanPoseCopy(*poseOut,*poseArray[0]);
+ }
+ else
+ {
+ human::HumanPoseBlend(*poseOut,poseArray,blendFactor,clipCount);
+ }
+ }
+
+ if(poseInterupted)
+ {
+ human::HumanPoseCopy(*poseInterupted,*poseOut);
+ }
+ }
+
+ int EvaluateOneLayer( ControllerBindingConstant const* controllerBindingConstant,
+ AvatarInput const* input,
+ int32_t layerIndex,
+ BlendingState<false> * output,
+ AvatarMemory * memory,
+ AvatarWorkspace * workspace,
+ AvatarOutput * avatarOutput,
+ AnimationSetMemory* animationSetMemory,
+ bool &fastCopy)
+ {
+ AvatarConstant const *constant = controllerBindingConstant->m_Avatar;
+ ControllerConstant const *controllerConstant = controllerBindingConstant->m_Controller;
+
+ bool hasRootMotion = constant->m_RootMotionBoneIndex != -1;
+ bool isHuman = constant->isHuman();
+ bool additive = controllerConstant->m_LayerArray[layerIndex]->m_LayerBlendingMode == kLayerBlendingModeAdditive;
+
+ uint32_t stateMachineIndex = controllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+
+ statemachine::StateMachineMemory* stateMachineMemory = memory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex].Get();
+ statemachine::StateMachineOutput& stateMachineOutput = *workspace->m_ControllerWorkspace->m_StateMachineOutput[stateMachineIndex];
+
+ BlendingState<true>& blendingStates = *workspace->m_ControllerWorkspace->m_BlendingState;
+ BlendingState<false>& interruptedBlendingState = memory->m_ControllerMemory->m_InteruptedTransitionsBlendingStateArray[layerIndex];
+
+ uint32_t clipCount = 0;
+
+ SetValueMask(workspace->m_ControllerWorkspace->m_BlendMask,false);
+
+ if(stateMachineMemory->m_InInterruptedTransition)
+ {
+ blendingStates.m_BlendFactor[clipCount] = ( 1.f - stateMachineOutput.m_BlendFactor);
+
+ ValueArrayCopy(interruptedBlendingState.m_DynamicValuesBlending.Get(),blendingStates.m_DynamicValuesBlending[clipCount]);
+ CopyValueMask(workspace->m_ControllerWorkspace->m_ReadMask,interruptedBlendingState.m_DynamicValuesBlendingMask.Get());
+ if(hasRootMotion || isHuman)
+ {
+ human::HumanPoseMask mask = controllerConstant->m_LayerArray[layerIndex]->m_BodyMask;
+ MotionOutputCopy(blendingStates.m_MotionBlending[clipCount], interruptedBlendingState.m_MotionBlending.Get(), hasRootMotion, isHuman, mask);
+ }
+ if(isHuman) human::HumanPoseCopy(*blendingStates.m_HumanPoseBlending[clipCount],*interruptedBlendingState.m_HumanPoseBlending.Get());
+
+ clipCount++;
+ }
+ else
+ {
+ EvaluateBlendNode(controllerBindingConstant,input,layerIndex,additive,true,clipCount, memory->m_ControllerMemory.Get(), workspace, avatarOutput, animationSetMemory, blendingStates.m_BlendFactor);
+ }
+
+ EvaluateBlendNode(controllerBindingConstant,input,layerIndex,additive,false,clipCount, memory->m_ControllerMemory.Get(), workspace, avatarOutput, animationSetMemory, blendingStates.m_BlendFactor);
+
+ bool isInterruptable = stateMachineMemory->m_InTransition;; // Now with dynamic transition, every transition can be interruptable
+
+ fastCopy = BlendDynamicStates( *controllerBindingConstant->m_DynamicValuesDefault,
+ *workspace->m_ControllerWorkspace->m_BlendMask,
+ blendingStates.m_DynamicValuesBlending,
+ blendingStates.m_BlendFactor,
+ clipCount,
+ *output->m_DynamicValuesBlending.Get(),
+ isInterruptable ? interruptedBlendingState.m_DynamicValuesBlending.Get() : 0,
+ isInterruptable ? interruptedBlendingState.m_DynamicValuesBlendingMask.Get() : 0,
+ avatarOutput);
+
+ if(hasRootMotion || isHuman)
+ {
+ human::HumanPoseMask mask = controllerConstant->m_LayerArray[layerIndex]->m_BodyMask;
+
+ BlendMotionStates(blendingStates.m_MotionBlending,blendingStates.m_BlendFactor,clipCount,output->m_MotionBlending.Get(),isInterruptable ? interruptedBlendingState.m_MotionBlending.Get() : 0,hasRootMotion,isHuman,mask);
+ }
+
+ assert(clipCount <= blendingStates.m_Size);
+
+ if(isHuman)
+ {
+ BlendHumanStates(blendingStates.m_HumanPoseBlending,blendingStates.m_BlendFactor,clipCount,output->m_HumanPoseBlending.Get(),isInterruptable ? interruptedBlendingState.m_HumanPoseBlending.Get() : 0);
+
+ if(input->m_DeltaTime != 0)
+ {
+ float deltaTime = input->m_DeltaTime;
+
+ for(int32_t clipIter = 0; clipIter < clipCount; clipIter++)
+ {
+ math::xform leftFootDX = math::xformInvMul(blendingStates.m_MotionBlending[clipIter]->m_PrevLeftFootX,math::xformMul(blendingStates.m_MotionBlending[clipIter]->m_DX,blendingStates.m_HumanPoseBlending[clipIter]->m_GoalArray[human::kLeftFootGoal].m_X));
+ math::xform rightFootDX = math::xformInvMul(blendingStates.m_MotionBlending[clipIter]->m_PrevRightFootX,math::xformMul(blendingStates.m_MotionBlending[clipIter]->m_DX,blendingStates.m_HumanPoseBlending[clipIter]->m_GoalArray[human::kRightFootGoal].m_X));
+
+ workspace->m_LeftFootSpeedT = math::maximum<float>(workspace->m_LeftFootSpeedT,math::length(leftFootDX.t).tofloat()/deltaTime);
+ workspace->m_LeftFootSpeedQ = math::maximum<float>(workspace->m_LeftFootSpeedQ,math::length(math::doubleAtan(math::quat2Qtan(leftFootDX.q))).tofloat()/deltaTime);
+ workspace->m_RightFootSpeedT = math::maximum<float>(workspace->m_RightFootSpeedT,math::length(rightFootDX.t).tofloat()/deltaTime);
+ workspace->m_RightFootSpeedQ = math::maximum<float>(workspace->m_RightFootSpeedQ,math::length(math::doubleAtan(math::quat2Qtan(rightFootDX.q))).tofloat()/deltaTime);
+ }
+ }
+ }
+
+ return clipCount;
+ }
+
+ void AddMotionLayer(MotionOutput const &motionIn, int index, float weight, bool additive, MotionOutput &motionOut, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &poseMask)
+ {
+ if(index == 0)
+ {
+ motionOut = motionIn;
+ }
+ else if(weight > 0)
+ {
+ if(additive)
+ {
+ MotionAddAdditiveLayer(&motionOut,&motionIn,weight,hasRootMotion,isHuman,poseMask);
+ }
+ else
+ {
+ MotionAddOverrideLayer(&motionOut,&motionIn,weight,hasRootMotion,isHuman,poseMask);
+ }
+ }
+ }
+
+ void AddHumanLayer(human::Human const &human, human::HumanPose const &poseIn,human::HumanPoseMask const &poseMask,int index, float weight, bool additive,human::HumanPose &pose,human::HumanPose &poseBase)
+ {
+ if(index == 0)
+ {
+ human::HumanPoseCopy(poseBase,poseIn, poseMask);
+ human::HumanPoseCopy(pose,poseIn, poseMask);
+ }
+ else if(weight > 0)
+ {
+ if(additive)
+ {
+ mecanim::human::HumanPoseMask mask = poseMask;
+ mask.set(mecanim::human::kMaskLeftHand,mask.test(mecanim::human::kMaskLeftHand) && human.m_HasLeftHand);
+ mask.set(mecanim::human::kMaskRightHand,mask.test(mecanim::human::kMaskRightHand) && human.m_HasRightHand);
+
+ human::HumanPoseAddAdditiveLayer(pose, poseIn, weight, mask);
+
+ if(mask.test(human::kMaskRootIndex))
+ {
+ human::HumanPoseAddAdditiveLayer(poseBase,poseIn,weight, mask);
+ }
+ }
+ else
+ {
+ mecanim::human::HumanPoseMask mask = poseMask;
+ mask.set(mecanim::human::kMaskLeftHand,mask.test(mecanim::human::kMaskLeftHand) && human.m_HasLeftHand);
+ mask.set(mecanim::human::kMaskRightHand,mask.test(mecanim::human::kMaskRightHand) && human.m_HasRightHand);
+
+ human::HumanPoseAddOverrideLayer(pose,poseIn,weight,mask);
+
+ if(mask.test(human::kMaskRootIndex))
+ {
+ human::HumanPoseAddOverrideLayer(poseBase,poseIn,weight, mask);
+ }
+ }
+ }
+ }
+
+ void EvaluateAvatarLayers( ControllerBindingConstant const* controllerBindingConstant,
+ AvatarInput const* input,
+ AvatarOutput * output,
+ AvatarMemory * memory,
+ AvatarWorkspace * workspace,
+ AnimationSetMemory* animationSetMemory)
+ {
+ AvatarConstant const *constant = controllerBindingConstant->m_Avatar;
+ ControllerConstant const *controllerConstant = controllerBindingConstant->m_Controller;
+ AnimationSet const *animationSet = controllerBindingConstant->m_AnimationSet;
+
+ bool hasRootMotion = constant->m_RootMotionBoneIndex != -1;
+ bool isHuman = constant->isHuman();
+
+ SetValueMask(workspace->m_ControllerWorkspace->m_DefaultMask,true);
+ workspace->m_ControllerWorkspace->m_BlendingClipCount = 0;
+
+ if(hasRootMotion || isHuman) MotionOutputClear(output->m_MotionOutput);
+ if(isHuman)
+ {
+ workspace->m_LeftFootSpeedT = 0;
+ workspace->m_LeftFootSpeedQ = 0;
+ workspace->m_RightFootSpeedT = 0;
+ workspace->m_RightFootSpeedQ = 0;
+ }
+
+ if(controllerConstant && !memory->m_ControllerMemory->m_StateMachineMemory.IsNull())
+ {
+ for(int layerIter = 0; layerIter < controllerConstant->m_LayerCount; layerIter++)
+ {
+ BlendingState<false> &layerOutput = workspace->m_ControllerWorkspace->m_BlendingStateWs;
+
+ const uint32_t stateMachineIndex = controllerConstant->m_LayerArray[layerIter]->m_StateMachineIndex;
+ if(stateMachineIndex != DISABLED_SYNCED_LAYER_IN_NON_PRO)
+ {
+ const uint32_t motionSetIndex = controllerConstant->m_LayerArray[layerIter]->m_StateMachineMotionSetIndex;
+ float layerWeight = layerIter == 0 ? 1 : memory->m_ControllerMemory->m_LayerWeights[layerIter] * memory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex]->m_MotionSetAutoWeightArray[motionSetIndex];
+ bool additive = controllerConstant->m_LayerArray[layerIter]->m_LayerBlendingMode == kLayerBlendingModeAdditive;
+
+ bool fastCopy = false;
+ int clipCount = EvaluateOneLayer(controllerBindingConstant, input, layerIter, &layerOutput, memory, workspace, layerIter == 0 ? output : 0, animationSetMemory,fastCopy);
+
+ AndValueMask(workspace->m_ControllerWorkspace->m_BlendMask, animationSet->m_DynamicValuesMaskArray[layerIter]);
+
+ if (fastCopy)
+ {
+ CopyValueMask(workspace->m_ControllerWorkspace->m_DefaultMask,workspace->m_ControllerWorkspace->m_BlendMask);
+ InvertValueMask(workspace->m_ControllerWorkspace->m_DefaultMask);
+ }
+ else
+ {
+ ValueArrayAdd(controllerBindingConstant->m_DynamicValuesDefault, layerOutput.m_DynamicValuesBlending.Get(),workspace->m_ControllerWorkspace->m_BlendMask,layerWeight,additive,output->m_DynamicValuesOutput,workspace->m_ControllerWorkspace->m_DefaultMask);
+ }
+
+ if(hasRootMotion || isHuman)
+ {
+ if(layerIter == 0 && clipCount == 0)
+ {
+ MotionOutputClear(output->m_MotionOutput);
+ }
+ else
+ {
+ bool rootMotionMask = hasRootMotion && controllerBindingConstant->m_RootMotionLayerMask[layerIter];
+ AddMotionLayer(*layerOutput.m_MotionBlending.Get(),layerIter,layerWeight,additive,*output->m_MotionOutput,rootMotionMask,isHuman,controllerConstant->m_LayerArray[layerIter]->m_BodyMask);
+ }
+ }
+
+ if(isHuman)
+ {
+ if(layerIter == 0 && clipCount == 0)
+ {
+ HumanPoseClear(*output->m_HumanPoseOutput);
+ HumanPoseClear(*output->m_HumanPoseBaseOutput);
+ }
+ else
+ {
+ AddHumanLayer(*constant->m_Human.Get(),*layerOutput.m_HumanPoseBlending.Get(),controllerConstant->m_LayerArray[layerIter]->m_BodyMask,layerIter,layerWeight,additive,*output->m_HumanPoseOutput,*output->m_HumanPoseBaseOutput);
+ }
+ }
+ }
+ }
+
+ ValueArrayCopy(controllerBindingConstant->m_DynamicValuesDefault,output->m_DynamicValuesOutput,workspace->m_ControllerWorkspace->m_DefaultMask);
+ ValueArrayCopy(controllerBindingConstant->m_DynamicValuesConstant, output->m_DynamicValuesOutput,controllerConstant->m_Values.Get(),memory->m_ControllerMemory->m_Values.Get(),animationSet->m_AdditionalIndexArray);
+ }
+
+ if(isHuman)
+ {
+ /////////////////////////////////////////////////////////////////
+ // Pivot management
+ if(memory->m_FirstEval==0)
+ {
+ math::float1 pivotWeight(memory->m_PivotWeight);
+ math::float4 prevPivot = math::lerp(output->m_MotionOutput->m_PrevLeftFootX.t,output->m_MotionOutput->m_PrevRightFootX.t,pivotWeight);
+ math::float4 deltaPivot = memory->m_Pivot-prevPivot;
+ deltaPivot.y() = 0;
+ deltaPivot *= input->m_FeetPivotActive;
+ output->m_MotionOutput->m_DX.t = math::xformMulVec(output->m_MotionOutput->m_DX,deltaPivot);
+ }
+
+ memory->m_PivotWeight = math::saturate(0.5f * (0.5f + 2.0f * workspace->m_LeftFootSpeedT) / (0.5f + workspace->m_LeftFootSpeedT + workspace->m_RightFootSpeedT));
+
+ math::xform lLeftFootX = output->m_HumanPoseOutput->m_GoalArray[human::kLeftFootGoal].m_X;
+ math::xform lRightFootX = output->m_HumanPoseOutput->m_GoalArray[human::kRightFootGoal].m_X;
+ memory->m_Pivot = math::lerp(lLeftFootX.t,lRightFootX.t,math::float1(memory->m_PivotWeight));
+ /////////////////////////////////////////////////////////////////
+
+ math::float1 scale(constant->isHuman() ? constant->m_Human->m_Scale : 1);
+ workspace->m_AvatarX = memory->m_AvatarX;
+ math::xform dx = output->m_MotionOutput->m_DX;
+ dx.t *= scale;
+ workspace->m_AvatarX = math::xformMul(workspace->m_AvatarX,dx);
+ }
+ }
+
+ void EvaluateAvatarX( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput * output,
+ AvatarMemory * memory,
+ AvatarWorkspace * workspace)
+ {
+ bool isHuman = constant->isHuman();
+ int rootMotionIndex = constant->m_RootMotionBoneIndex;
+
+ if(isHuman || rootMotionIndex != -1)
+ {
+ math::xform dx = output->m_MotionOutput->m_DX;
+ if(isHuman) dx.t *= math::float1(constant->m_Human->m_Scale);
+ memory->m_AvatarX = math::xformMul(memory->m_AvatarX, dx); // @Sonny: Can memory avatarX.s degenerate here even if dx.s == 1?
+ }
+ }
+
+ void EvaluateAvatarRetarget( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput * output,
+ AvatarMemory * memory,
+ AvatarWorkspace * workspace,
+ ControllerConstant const* controllerConstant)
+ {
+ if(controllerConstant && constant->isHuman())
+ {
+ math::xform avatarX = memory->m_AvatarX;
+
+ human::HumanPose humanPose;
+ human::HumanPose *humanPosePtr = 0;
+
+ if(input->m_LayersAffectMassCenter)
+ {
+ human::HumanPoseCopy(*output->m_HumanPoseBaseOutput,*output->m_HumanPoseOutput);
+ }
+ else if (controllerConstant->m_LayerCount > 1)
+ {
+ human::HumanPoseCopy(humanPose,*output->m_HumanPoseOutput);
+ humanPosePtr = &humanPose;
+ }
+
+ human::RetargetTo( constant->m_Human.Get(),
+ output->m_HumanPoseBaseOutput,
+ humanPosePtr,
+ avatarX,
+ output->m_HumanPoseOutput,
+ workspace->m_BodySkeletonPoseWs,
+ workspace->m_BodySkeletonPoseWsA);
+
+ if(workspace->m_IKOnFeet)
+ {
+ SetIKOnFeet(true,*constant, *input, *memory, *workspace, *output);
+ SetIKOnFeet(false,*constant, *input, *memory, *workspace, *output);
+
+ /* Usefull to debug IK on arms in avatar preview
+ output->m_HumanPose.m_GoalArray[human::kLeftHandGoal].m_WeightT = 1;
+ output->m_HumanPose.m_GoalArray[human::kRightHandGoal].m_WeightT = 1;
+ output->m_HumanPose.m_GoalArray[human::kLeftHandGoal].m_WeightR = 1;
+ output->m_HumanPose.m_GoalArray[human::kRightHandGoal].m_WeightR = 1;
+ */
+ }
+ }
+ }
+
+ void EvaluateAvatarIK( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput * output,
+ AvatarMemory * memory,
+ AvatarWorkspace * workspace,
+ ControllerConstant const* controllerConstant)
+ {
+ if(controllerConstant && constant->isHuman())
+ {
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+ bool needIK = any(output->m_HumanPoseOutput->m_LookAtWeight > math::float4::zero());
+ for(int i = 0; !needIK && i < mecanim::human::kLastGoal; i++) needIK |= (output->m_HumanPoseOutput->m_GoalArray[i].m_WeightT > 0 || output->m_HumanPoseOutput->m_GoalArray[i].m_WeightR > 0);
+
+ workspace->m_BodySkeletonPoseWs->m_X[0] = output->m_HumanPoseOutput->m_RootX;
+
+ if(needIK)
+ {
+ skeleton::SkeletonPoseComputeGlobal(constant->m_Human->m_Skeleton.Get(), workspace->m_BodySkeletonPoseWs, workspace->m_BodySkeletonPoseWsA);
+ FullBodySolve(constant->m_Human.Get(), output->m_HumanPoseOutput, workspace->m_BodySkeletonPoseWs, workspace->m_BodySkeletonPoseWsA, workspace->m_BodySkeletonPoseWsB);
+ }
+
+ memory->m_LeftFootX = output->m_HumanPoseOutput->m_GoalArray[human::kLeftFootGoal].m_X;
+ memory->m_RightFootX = output->m_HumanPoseOutput->m_GoalArray[human::kRightFootGoal].m_X;
+
+ output->m_HumanPoseOutput->m_LookAtWeight = math::float4::zero();
+ for(int i = 0; i < mecanim::human::kLastGoal; i++)
+ {
+ output->m_HumanPoseOutput->m_GoalArray[i].m_WeightT = 0;
+ output->m_HumanPoseOutput->m_GoalArray[i].m_WeightR = 0;
+ }
+ }
+ }
+
+ void EvaluateAvatarEnd( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput * output,
+ AvatarMemory * memory,
+ AvatarWorkspace * workspace,
+ ControllerConstant const* controllerConstant)
+ {
+ if(constant->isHuman())
+ {
+ int32_t rootIndex = constant->m_HumanSkeletonIndexArray[0];
+
+ skeleton::SkeletonPoseCopy(workspace->m_BodySkeletonPoseWs,workspace->m_BodySkeletonPoseWsA);
+ TwistSolve(constant->m_Human.Get(), workspace->m_BodySkeletonPoseWsA,workspace->m_BodySkeletonPoseWsB);
+
+ skeleton::SkeletonPoseCopy(constant->m_AvatarSkeletonPose.Get(),output->m_SkeletonPoseOutput);
+ output->m_SkeletonPoseOutput->m_X[0] = memory->m_AvatarX;
+ skeleton::SkeletonPoseComputeGlobal(constant->m_AvatarSkeleton.Get(),output->m_SkeletonPoseOutput,output->m_SkeletonPoseOutput,rootIndex,0);
+ skeleton::SkeletonPoseComputeGlobal(constant->m_Human->m_Skeleton.Get(),workspace->m_BodySkeletonPoseWsA,workspace->m_BodySkeletonPoseWsA,1,1);
+ workspace->m_BodySkeletonPoseWsA->m_X[0] = output->m_SkeletonPoseOutput->m_X[rootIndex];
+ skeleton::SkeletonPoseComputeLocal(constant->m_AvatarSkeleton.Get(),output->m_SkeletonPoseOutput,output->m_SkeletonPoseOutput,rootIndex,0);
+ skeleton::SkeletonPoseComputeLocal(constant->m_Human->m_Skeleton.Get(),workspace->m_BodySkeletonPoseWsA,workspace->m_BodySkeletonPoseWsA,1,1);
+ workspace->m_BodySkeletonPoseWsA->m_X[0] = output->m_SkeletonPoseOutput->m_X[rootIndex];
+
+ skeleton::SkeletonPoseCopy( workspace->m_BodySkeletonPoseWsA,
+ output->m_SkeletonPoseOutput,
+ constant->m_HumanSkeletonIndexCount,
+ constant->m_HumanSkeletonIndexArray.Get());
+ }
+ }
+
+ void ValuesFromClip( mecanim::ValueArray const &defaultValues,
+ mecanim::animation::ClipMuscleConstant const &cst,
+ mecanim::animation::ClipOutput const &clip,
+ const ClipBindings& bindings,
+ int32_t integerRemapStride,
+ mecanim::ValueArray &values,
+ mecanim::ValueArrayMask& valueArrayMask)
+ {
+ float* RESTRICT output;
+ bool* RESTRICT mask;
+ const float* RESTRICT inputArray = clip.m_Values;
+
+ // Extract position values from curvedata (float[])
+ output = reinterpret_cast<float*> (values.m_PositionValues.Get());
+ mask = valueArrayMask.m_PositionValues.Get();
+ for (int valueIndex=0;valueIndex<values.m_PositionCount;valueIndex++)
+ {
+ int curveIndex = bindings.m_PositionIndex[valueIndex];
+
+ if (curveIndex == -1)
+ {
+ values.m_PositionValues[valueIndex] = defaultValues.m_PositionValues[valueIndex];
+ mask[valueIndex] = false;
+ }
+ else
+ {
+ float* outputValue = output + valueIndex * 4;
+ outputValue[0] = inputArray[curveIndex+0];
+ outputValue[1] = inputArray[curveIndex+1];
+ outputValue[2] = inputArray[curveIndex+2];
+ outputValue[3] = 0.0F;
+ mask[valueIndex] = true;
+ }
+ }
+
+ // Extract quaternion values from curvedata (float[])
+ output = reinterpret_cast<float*> (values.m_QuaternionValues.Get());
+ mask = valueArrayMask.m_QuaternionValues.Get();
+ for (int valueIndex=0;valueIndex<values.m_QuaternionCount;valueIndex++)
+ {
+ int curveIndex = bindings.m_QuaternionIndex[valueIndex];
+ if (curveIndex == -1)
+ {
+ values.m_QuaternionValues[valueIndex] = defaultValues.m_QuaternionValues[valueIndex];
+ mask[valueIndex] = false;
+ }
+ else
+ {
+ float* outputValue = output + valueIndex * 4;
+ outputValue[0] = inputArray[curveIndex+0];
+ outputValue[1] = inputArray[curveIndex+1];
+ outputValue[2] = inputArray[curveIndex+2];
+ outputValue[3] = inputArray[curveIndex+3];
+ mask[valueIndex] = true;
+ }
+ }
+
+ // Extract scale values from curvedata (float[])
+ output = reinterpret_cast<float*> (values.m_ScaleValues.Get());
+ mask = valueArrayMask.m_ScaleValues.Get();
+ for (int valueIndex=0;valueIndex<values.m_ScaleCount;valueIndex++)
+ {
+ int curveIndex = bindings.m_ScaleIndex[valueIndex];
+
+ if (curveIndex == -1)
+ {
+ values.m_ScaleValues[valueIndex] = defaultValues.m_ScaleValues[valueIndex];
+ mask[valueIndex] = false;
+ }
+ else
+ {
+ float* outputValue = output + valueIndex * 4;
+ outputValue[0] = inputArray[curveIndex+0];
+ outputValue[1] = inputArray[curveIndex+1];
+ outputValue[2] = inputArray[curveIndex+2];
+ outputValue[3] = 1.0F;
+ mask[valueIndex] = true;
+ }
+ }
+
+ // Extract float values from curvedata (float[])
+ output = reinterpret_cast<float*> (values.m_FloatValues.Get());
+ mask = valueArrayMask.m_FloatValues.Get();
+ for (int valueIndex=0;valueIndex<values.m_FloatCount;valueIndex++)
+ {
+ int curveIndex = bindings.m_FloatIndex[valueIndex];
+
+ if (curveIndex == -1)
+ {
+ values.m_FloatValues[valueIndex] = defaultValues.m_FloatValues[valueIndex];
+ mask[valueIndex] = false;
+ }
+ else
+ {
+ output[valueIndex] = inputArray[curveIndex];
+ mask[valueIndex] = true;
+ }
+ }
+
+ // Extract integer values from curvedata (float[])
+ // Used for PPtr animation. The integers actually represent an instanceID.
+ mask = valueArrayMask.m_IntValues.Get();
+ for (int valueIndex=0;valueIndex<values.m_IntCount;valueIndex++)
+ {
+ int curveIndex = bindings.m_IntIndex[valueIndex];
+
+ if (curveIndex == -1)
+ {
+ values.m_IntValues[valueIndex] = defaultValues.m_IntValues[valueIndex];
+ mask[valueIndex] = false;
+ }
+ else
+ {
+ uint32_t index = (uint32_t)inputArray[curveIndex];
+ const uint8_t* integerRemapBuffer = reinterpret_cast<const uint8_t*> (bindings.m_IntegerRemap);
+ int32_t valueInt32 = *reinterpret_cast<const int32_t*> (integerRemapBuffer + (index * integerRemapStride));
+
+ values.m_IntValues[valueIndex] = valueInt32;
+ mask[valueIndex] = true;
+ }
+ }
+ }
+
+ void DeltasFromClip( ClipMuscleConstant const &cst,
+ const ClipBindings& bindings,
+ const ValueArrayMask& mask,
+ mecanim::ValueArray &starts,
+ mecanim::ValueArray &stops)
+ {
+
+ ATTRIBUTE_ALIGN(ALIGN4F) float start[4];
+ ATTRIBUTE_ALIGN(ALIGN4F) float stop[4];
+
+ int curveIter;
+
+ // Positions
+ for (int valueIndex=0;valueIndex<starts.m_PositionCount;valueIndex++)
+ {
+ if (!mask.m_PositionValues[valueIndex])
+ continue;
+
+ curveIter = bindings.m_PositionIndex[valueIndex];
+
+ start[0] = cst.m_ValueArrayDelta[curveIter+0].m_Start;
+ start[1] = cst.m_ValueArrayDelta[curveIter+1].m_Start;
+ start[2] = cst.m_ValueArrayDelta[curveIter+2].m_Start;
+ start[3] = 0;
+
+ stop[0] = cst.m_ValueArrayDelta[curveIter+0].m_Stop;
+ stop[1] = cst.m_ValueArrayDelta[curveIter+1].m_Stop;
+ stop[2] = cst.m_ValueArrayDelta[curveIter+2].m_Stop;
+ stop[3] = 0;
+
+ math::float4 start4 = math::load(start);
+ math::float4 stop4 = math::load(stop);
+
+ starts.WritePosition(start4,valueIndex);
+ stops.WritePosition(stop4,valueIndex);
+ }
+
+ // Quaternions
+ for (int valueIndex=0;valueIndex<starts.m_QuaternionCount;valueIndex++)
+ {
+ if (!mask.m_QuaternionValues[valueIndex])
+ continue;
+
+ curveIter = bindings.m_QuaternionIndex[valueIndex];
+
+ start[0] = cst.m_ValueArrayDelta[curveIter+0].m_Start;
+ start[1] = cst.m_ValueArrayDelta[curveIter+1].m_Start;
+ start[2] = cst.m_ValueArrayDelta[curveIter+2].m_Start;
+ start[3] = cst.m_ValueArrayDelta[curveIter+3].m_Start;
+
+ stop[0] = cst.m_ValueArrayDelta[curveIter+0].m_Stop;
+ stop[1] = cst.m_ValueArrayDelta[curveIter+1].m_Stop;
+ stop[2] = cst.m_ValueArrayDelta[curveIter+2].m_Stop;
+ stop[3] = cst.m_ValueArrayDelta[curveIter+3].m_Stop;
+
+ math::float4 start4 = math::load(start);
+ math::float4 stop4 = math::load(stop);
+
+ starts.WriteQuaternion(start4,valueIndex);
+ stops.WriteQuaternion(stop4,valueIndex);
+ }
+
+ // Scales
+ for (int valueIndex=0;valueIndex<starts.m_ScaleCount;valueIndex++)
+ {
+ if (!mask.m_ScaleValues[valueIndex])
+ continue;
+
+ curveIter = bindings.m_ScaleIndex[valueIndex];
+
+ start[0] = cst.m_ValueArrayDelta[curveIter+0].m_Start;
+ start[1] = cst.m_ValueArrayDelta[curveIter+1].m_Start;
+ start[2] = cst.m_ValueArrayDelta[curveIter+2].m_Start;
+ start[3] = 0;
+
+ stop[0] = cst.m_ValueArrayDelta[curveIter+0].m_Stop;
+ stop[1] = cst.m_ValueArrayDelta[curveIter+1].m_Stop;
+ stop[2] = cst.m_ValueArrayDelta[curveIter+2].m_Stop;
+ stop[3] = 0;
+
+ math::float4 start4 = math::load(start);
+ math::float4 stop4 = math::load(stop);
+
+ starts.WriteScale(start4,valueIndex);
+ stops.WriteScale(stop4,valueIndex);
+ }
+
+ // Generic floats
+ for (int valueIndex=0;valueIndex<starts.m_FloatCount;valueIndex++)
+ {
+ if (!mask.m_FloatValues[valueIndex])
+ continue;
+
+ curveIter = bindings.m_FloatIndex[valueIndex];
+
+ start[0] = cst.m_ValueArrayDelta[curveIter+0].m_Start;
+ stop[0] = cst.m_ValueArrayDelta[curveIter+0].m_Stop;
+
+ starts.WriteData(start[0],valueIndex);
+ stops.WriteData(stop[0],valueIndex);
+ }
+ }
+
+ void SkeletonPoseFromValue(skeleton::Skeleton const &skeleton, skeleton::SkeletonPose const &defaultPose, ValueArray const &values, SkeletonTQSMap const *skeletonTQSMap, skeleton::SkeletonPose &pose, int32_t const*humanReverseIndex, bool skipRoot)
+ {
+ for (int index=skipRoot?1:0;index<skeleton.m_Count;index++)
+ {
+ if(!(humanReverseIndex != 0 && humanReverseIndex[index] != -1))
+ {
+ if (skeletonTQSMap[index].m_TIndex != -1)
+ pose.m_X[index].t = values.ReadPosition(skeletonTQSMap[index].m_TIndex);
+ else
+ pose.m_X[index].t = defaultPose.m_X[index].t;
+
+ if (skeletonTQSMap[index].m_QIndex != -1)
+ pose.m_X[index].q = values.ReadQuaternion(skeletonTQSMap[index].m_QIndex);
+ else
+ pose.m_X[index].q = defaultPose.m_X[index].q;
+
+ if (skeletonTQSMap[index].m_SIndex != -1)
+ pose.m_X[index].s = values.ReadScale(skeletonTQSMap[index].m_SIndex);
+ else
+ pose.m_X[index].s = defaultPose.m_X[index].s;
+ }
+ }
+ }
+
+ void SkeletonPoseFromValue(skeleton::Skeleton const &skeleton, skeleton::SkeletonPose const &defaultPose, ValueArray const &values, SkeletonTQSMap const *skeletonTQSMap, int32_t const *indexArray, skeleton::SkeletonPose &pose,int index, int stopIndex)
+ {
+ if(index != -1)
+ {
+ if(index != stopIndex)
+ {
+ SkeletonPoseFromValue(skeleton,defaultPose, values,skeletonTQSMap,indexArray,pose,skeleton.m_Node[index].m_ParentId,stopIndex);
+
+ int avatarIndex = indexArray[index];
+
+ if(skeletonTQSMap[avatarIndex].m_TIndex != -1)
+ {
+ pose.m_X[index].t = values.ReadPosition(skeletonTQSMap[avatarIndex].m_TIndex);
+ }
+ else
+ {
+ pose.m_X[index].t = defaultPose.m_X[avatarIndex].t;
+ }
+
+ if(skeletonTQSMap[avatarIndex].m_QIndex != -1)
+ {
+ pose.m_X[index].q = values.ReadQuaternion(skeletonTQSMap[avatarIndex].m_QIndex);
+ }
+ else
+ {
+ pose.m_X[index].q = defaultPose.m_X[avatarIndex].q;
+ }
+
+ if(skeletonTQSMap[avatarIndex].m_SIndex != -1)
+ {
+ pose.m_X[index].s = values.ReadScale(skeletonTQSMap[avatarIndex].m_SIndex);
+ }
+ else
+ {
+ pose.m_X[index].s = defaultPose.m_X[avatarIndex].s;
+ }
+ }
+ }
+ }
+
+ void ValueFromSkeletonPose(skeleton::Skeleton const &skeleton, skeleton::SkeletonPose const &pose, SkeletonTQSMap const *skeletonTQSMap, ValueArray &values)
+ {
+ for (int index=0;index<skeleton.m_Count;index++)
+ {
+ if(skeletonTQSMap[index].m_TIndex != -1)
+ {
+ values.WritePosition(pose.m_X[index].t, skeletonTQSMap[index].m_TIndex);
+ }
+
+ if(skeletonTQSMap[index].m_QIndex != -1)
+ {
+ values.WriteQuaternion(pose.m_X[index].q, skeletonTQSMap[index].m_QIndex);
+ }
+
+ if(skeletonTQSMap[index].m_SIndex != -1)
+ {
+ values.WriteScale(pose.m_X[index].s, skeletonTQSMap[index].m_SIndex);
+ }
+ }
+ }
+
+ void ValueFromSkeletonPose(skeleton::Skeleton const &skeleton, skeleton::SkeletonPose const &pose, SkeletonTQSMap const *skeletonTQSMap,int32_t const *indexArray, ValueArray &values, int index, int stopIndex)
+ {
+ if(index != -1)
+ {
+ if(index != stopIndex)
+ {
+ ValueFromSkeletonPose(skeleton,pose,skeletonTQSMap,indexArray, values,skeleton.m_Node[index].m_ParentId,stopIndex);
+
+ int avatarIndex = indexArray[index];
+
+ if(skeletonTQSMap[avatarIndex].m_TIndex != -1)
+ {
+ values.WritePosition(pose.m_X[index].t, skeletonTQSMap[avatarIndex].m_TIndex);
+ }
+
+ if(skeletonTQSMap[avatarIndex].m_QIndex != -1)
+ {
+ values.WriteQuaternion(pose.m_X[index].q, skeletonTQSMap[avatarIndex].m_QIndex);
+ }
+
+ if(skeletonTQSMap[avatarIndex].m_SIndex != -1)
+ {
+ values.WriteScale(pose.m_X[index].s, skeletonTQSMap[avatarIndex].m_SIndex);
+ }
+ }
+ }
+ }
+}
+}
diff --git a/Runtime/mecanim/animation/avatar.h b/Runtime/mecanim/animation/avatar.h
new file mode 100644
index 0000000..e90be7a
--- /dev/null
+++ b/Runtime/mecanim/animation/avatar.h
@@ -0,0 +1,706 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/bind.h"
+#include "Runtime/Math/Simd/float4.h"
+
+#include "Runtime/mecanim/skeleton/skeleton.h"
+#include "Runtime/mecanim/human/human.h"
+#include "Runtime/mecanim/animation/clipmuscle.h"
+#include "Runtime/mecanim/animation/curvedata.h"
+#include "Runtime/mecanim/statemachine/statemachine.h"
+
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Animation/MecanimArraySerialization.h"
+
+typedef UInt32 BindingHash;
+
+namespace mecanim
+{
+
+ static const uint32_t DISABLED_SYNCED_LAYER_IN_NON_PRO = 0xffffffff;
+ struct ValueArrayConstant;
+
+ typedef int ProcessString(mecanim::String const&);
+
+ namespace animation
+ {
+ struct ClipMuscleConstant;
+ struct AvatarConstant;
+ struct ControllerConstant;
+
+ enum LayerBlendingMode
+ {
+ kLayerBlendingModeOverride,
+ kLayerBlendingModeAdditive
+ };
+
+ struct LayerConstant
+ {
+ DEFINE_GET_TYPESTRING(LayerConstant)
+
+ LayerConstant():m_IKPass(true), m_SyncedLayerAffectsTiming(false), m_LayerBlendingMode(kLayerBlendingModeOverride){}
+
+ uint32_t m_StateMachineIndex;
+ uint32_t m_StateMachineMotionSetIndex;
+
+ human::HumanPoseMask m_BodyMask;
+ OffsetPtr<skeleton::SkeletonMask> m_SkeletonMask;
+
+ uint32_t m_Binding;
+ uint32_t m_LayerBlendingMode; //LayerBlendingMode
+ float m_DefaultWeight;
+ bool m_IKPass;
+ bool m_SyncedLayerAffectsTiming;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+
+ TRANSFER(m_StateMachineIndex);
+ TRANSFER(m_StateMachineMotionSetIndex);
+ TRANSFER(m_BodyMask);
+ TRANSFER(m_SkeletonMask);
+ TRANSFER(m_Binding);
+ TRANSFER((int&)m_LayerBlendingMode);
+ TRANSFER(m_DefaultWeight);
+ TRANSFER(m_IKPass);
+ TRANSFER(m_SyncedLayerAffectsTiming);
+ transfer.Align();
+
+
+ }
+ };
+
+ struct ControllerConstant
+ {
+ DEFINE_GET_TYPESTRING(ControllerConstant)
+
+ ControllerConstant(): m_LayerCount(0),
+ m_StateMachineCount(0) {}
+
+ uint32_t m_LayerCount;
+ OffsetPtr< OffsetPtr<LayerConstant> > m_LayerArray;
+
+ uint32_t m_StateMachineCount;
+ OffsetPtr< OffsetPtr<statemachine::StateMachineConstant> > m_StateMachineArray;
+
+ OffsetPtr<ValueArrayConstant> m_Values;
+ OffsetPtr<ValueArray> m_DefaultValues;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_LayerCount);
+ MANUAL_ARRAY_TRANSFER2( OffsetPtr<mecanim::animation::LayerConstant>, m_LayerArray, m_LayerCount);
+
+ TRANSFER_BLOB_ONLY(m_StateMachineCount);
+ MANUAL_ARRAY_TRANSFER2( OffsetPtr<mecanim::statemachine::StateMachineConstant>, m_StateMachineArray, m_StateMachineCount);
+
+ TRANSFER(m_Values);
+ TRANSFER(m_DefaultValues);
+
+ }
+
+ static void InitializeClass();
+ };
+
+ struct SkeletonTQSMap
+ {
+ SkeletonTQSMap() : m_TIndex(-1), m_QIndex(-1), m_SIndex(-1) {};
+
+ int32_t m_TIndex;
+ int32_t m_QIndex;
+ int32_t m_SIndex;
+ };
+
+ struct ClipBindings
+ {
+ ClipBindings () : m_PositionIndex(0), m_QuaternionIndex(0), m_ScaleIndex(0), m_FloatIndex(0), m_IntIndex(0), m_IntegerRemap(0) {}
+
+ // Maps from the curve float array of the clip to the ValueArrayConstant.
+ // This allows us to bring the curve float array into a ValueArray that has the same layout between all clips.
+ int16_t* m_PositionIndex;
+ int16_t* m_QuaternionIndex;
+ int16_t* m_ScaleIndex;
+ int16_t* m_FloatIndex;
+ int16_t* m_IntIndex;
+
+ // Points directly to AnimationClipBindingConstant pptrCurveMapping.
+ int32_t* m_IntegerRemap;
+ };
+
+ struct AnimationSet
+ {
+ struct Clip
+ {
+ Clip() : m_Clip(0), m_TotalUsedOptimizedCurveCount(0), m_ClipIndex(-1) {}
+
+ // The referenced constant clip
+ ClipMuscleConstant* m_Clip;
+ int32_t m_ClipIndex;
+ // The amount of ConstantClip curves that need to be sampled (Constant curves are often ignored during binding if they are determined to have no impact)
+ uint32_t m_TotalUsedOptimizedCurveCount;
+ // Binding indices to index from the curve float[] to the ValueArray
+ ClipBindings m_Bindings;
+ };
+
+ AnimationSet() : m_MaxBlendState(0),
+ m_LayerCount(0),
+ m_ClipPerLayer(0),
+ m_ClipConstant(0),
+ m_AdditionalCount(0),
+ m_AdditionalIndexArray(0),
+ m_DynamicFullValuesConstant(0),
+ m_DynamicValuesMaskArray(0),
+ m_GravityWeightIndex(-1),
+ m_IntegerRemapStride(-1)
+ {}
+
+ uint32_t m_MaxBlendState;
+ uint32_t m_LayerCount;
+ uint32_t* m_ClipPerLayer;
+
+ Clip** m_ClipConstant;
+
+ uint32_t m_AdditionalCount;
+ int32_t* m_AdditionalIndexArray;
+
+ mecanim::ValueArrayConstant* m_DynamicFullValuesConstant;
+ mecanim::ValueArrayMask** m_DynamicValuesMaskArray;
+
+ int32_t m_GravityWeightIndex;
+ int32_t m_IntegerRemapStride;
+ };
+
+ struct ControllerBindingConstant
+ {
+ ControllerBindingConstant(): m_DynamicValuesDefault(0),
+ m_SkeletonTQSMap(0),
+ m_RootMotionLayerMask(0),
+ m_Avatar(0),
+ m_Controller(0),
+ m_DynamicValuesConstant(0),
+ m_AnimationSet(0) {}
+
+ ValueArrayConstant* m_DynamicValuesConstant;
+ ValueArray* m_DynamicValuesDefault;
+
+ SkeletonTQSMap* m_SkeletonTQSMap;
+
+ bool* m_RootMotionLayerMask;
+
+ AvatarConstant const* m_Avatar;
+ ControllerConstant const* m_Controller;
+ AnimationSet const* m_AnimationSet;
+ };
+
+ struct AnimationSetMemory
+ {
+ AnimationSetMemory() : m_LayerCount(0), m_ClipPerLayer(0), m_ClipMemory(0), m_ClipOutput(0) {}
+
+ uint32_t m_LayerCount;
+ uint32_t* m_ClipPerLayer;
+ ClipMemory*** m_ClipMemory;
+ ClipOutput* m_ClipOutput;
+ };
+
+ template<bool dynamic>
+ struct BlendingState
+ {
+ BlendingState():
+ m_DynamicValuesBlending(0),
+ m_MotionBlending(0),
+ m_HumanPoseBlending(0),
+ m_BlendFactor(0)
+ {}
+
+ ValueArray** m_DynamicValuesBlending;
+ MotionOutput** m_MotionBlending;
+ human::HumanPose** m_HumanPoseBlending;
+ float* m_BlendFactor;
+
+ uint32_t m_Size;
+ };
+
+ template<>
+ struct BlendingState<false>
+ {
+ BlendingState(): m_BlendFactor(0) {}
+
+ OffsetPtr<ValueArray> m_DynamicValuesBlending;
+ OffsetPtr<ValueArrayMask> m_DynamicValuesBlendingMask;
+ OffsetPtr<MotionOutput> m_MotionBlending;
+ OffsetPtr<human::HumanPose> m_HumanPoseBlending;
+ float m_BlendFactor;
+ };
+
+ struct BlendingClip
+ {
+ BlendingClip() : m_ClipIndex(-1), m_LayerIndex(-1), m_Weight(0), m_PrevTime(0), m_Time(0), m_Reverse(false) {}
+
+ int m_ClipIndex;
+ int m_LayerIndex;
+ float m_Weight;
+ float m_PrevTime;
+ float m_Time;
+ bool m_Reverse;
+ };
+
+ struct ControllerMemory
+ {
+ DEFINE_GET_TYPESTRING(ControllerMemory)
+
+ ControllerMemory(): m_StateMachineCount(0),
+ m_LayerCount(0) {}
+
+ uint32_t m_StateMachineCount;
+ OffsetPtr< OffsetPtr<statemachine::StateMachineMemory> > m_StateMachineMemory;
+
+ uint32_t m_LayerCount;
+ OffsetPtr<BlendingState<false> > m_InteruptedTransitionsBlendingStateArray;
+ OffsetPtr<float> m_LayerWeights;
+
+ OffsetPtr<ValueArray> m_Values;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_StateMachineCount);
+ MANUAL_ARRAY_TRANSFER2( OffsetPtr<mecanim::statemachine::StateMachineMemory>, m_StateMachineMemory, m_StateMachineCount);
+
+ TRANSFER_BLOB_ONLY(m_LayerCount);
+ MANUAL_ARRAY_TRANSFER2( BlendingState<false>, m_InteruptedTransitionsBlendingStateArray, m_LayerCount);
+ MANUAL_ARRAY_TRANSFER2( float, m_LayerWeights, m_LayerCount);
+
+ TRANSFER(m_Values);
+ }
+ };
+
+ struct ControllerWorkspace
+ {
+ ControllerWorkspace() : m_StateMachineWorkspace(0),
+ m_StateMachineOutput(0),
+ m_BlendingState(0),
+ m_BlendingClipCount(0),
+ m_BlendingClipArray(0),
+ m_ValueArrayStart(0),
+ m_ValueArrayStop(0),
+ m_ReadMask(0),
+ m_BlendMask(0),
+ m_DefaultMask(0),
+ m_DoIK(false),
+ m_DoWrite(false) {}
+
+ statemachine::StateMachineWorkspace** m_StateMachineWorkspace;
+ statemachine::StateMachineOutput** m_StateMachineOutput;
+
+ uint32_t m_StateMachineCount;
+
+ float* m_MotionSetTimingWeightArray;
+
+ BlendingState<true>* m_BlendingState;
+ BlendingState<false> m_BlendingStateWs;
+
+ int m_BlendingClipCount;
+ BlendingClip* m_BlendingClipArray;
+
+ ValueArray *m_ValueArrayStart;
+ ValueArray *m_ValueArrayStop;
+
+ ValueArrayMask *m_ReadMask;
+ ValueArrayMask *m_BlendMask;
+ ValueArrayMask *m_DefaultMask;
+
+ bool m_DoIK;
+ bool m_DoWrite;
+ };
+
+ struct ExposedTransform
+ {
+ DEFINE_GET_TYPESTRING(ExposedTransform);
+
+ // For SkinnedMeshRenderer, the following two indices are different
+ // - 'skeletonIndex'
+ // corresponds to the SkinnedMeshRenderer itself
+ // - 'skeletonIndexForUpdateTransform'
+ // corresponds to the root bone of the SkinnedMeshRenderer
+ uint32_t skeletonIndex;
+ uint32_t skeletonIndexForUpdateTransform;
+
+ BindingHash transformPath; // flattened path
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(skeletonIndex);
+ TRANSFER(skeletonIndexForUpdateTransform);
+ TRANSFER(transformPath);
+ }
+ };
+
+ struct AvatarConstant
+ {
+ DEFINE_GET_TYPESTRING(AvatarConstant)
+
+ AvatarConstant() : m_SkeletonNameIDCount(0),
+ m_HumanSkeletonIndexCount(0),
+ m_HumanSkeletonReverseIndexCount(0),
+ m_RootMotionBoneIndex(-1),
+ m_RootMotionBoneX(math::xformIdentity()),
+ m_RootMotionSkeletonIndexCount(0) {}
+
+ OffsetPtr<skeleton::Skeleton> m_AvatarSkeleton;
+ OffsetPtr<skeleton::SkeletonPose> m_AvatarSkeletonPose;
+
+ OffsetPtr<skeleton::SkeletonPose> m_DefaultPose; // The default pose when model is imported.
+
+ uint32_t m_SkeletonNameIDCount;
+ OffsetPtr<uint32_t> m_SkeletonNameIDArray; // CRC(name)
+
+ OffsetPtr<human::Human> m_Human;
+
+ uint32_t m_HumanSkeletonIndexCount;
+ OffsetPtr<int32_t> m_HumanSkeletonIndexArray;
+
+ // needed to update human pose and additonal bones in optimize mode
+ // decided to put the info in constant for perf and memory reason vs doing masking at runtime
+ uint32_t m_HumanSkeletonReverseIndexCount;
+ OffsetPtr<int32_t> m_HumanSkeletonReverseIndexArray;
+
+ int32_t m_RootMotionBoneIndex;
+ math::xform m_RootMotionBoneX;
+ OffsetPtr<skeleton::Skeleton> m_RootMotionSkeleton;
+ OffsetPtr<skeleton::SkeletonPose> m_RootMotionSkeletonPose;
+ uint32_t m_RootMotionSkeletonIndexCount;
+ OffsetPtr<int32_t> m_RootMotionSkeletonIndexArray;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ transfer.SetVersion(3);
+
+ TRANSFER(m_AvatarSkeleton);
+
+ TRANSFER(m_AvatarSkeletonPose);
+ TRANSFER(m_DefaultPose);
+
+ TRANSFER_BLOB_ONLY(m_SkeletonNameIDCount);
+ MANUAL_ARRAY_TRANSFER2(uint32_t,m_SkeletonNameIDArray,m_SkeletonNameIDCount);
+
+ TRANSFER(m_Human);
+
+ TRANSFER_BLOB_ONLY(m_HumanSkeletonIndexCount);
+ MANUAL_ARRAY_TRANSFER2(int32_t,m_HumanSkeletonIndexArray,m_HumanSkeletonIndexCount);
+
+ TRANSFER_BLOB_ONLY(m_HumanSkeletonReverseIndexCount);
+ MANUAL_ARRAY_TRANSFER2(int32_t,m_HumanSkeletonReverseIndexArray,m_HumanSkeletonReverseIndexCount);
+
+ TRANSFER(m_RootMotionBoneIndex);
+ TRANSFER(m_RootMotionBoneX);
+ TRANSFER(m_RootMotionSkeleton);
+ TRANSFER(m_RootMotionSkeletonPose);
+
+ TRANSFER_BLOB_ONLY(m_RootMotionSkeletonIndexCount);
+ MANUAL_ARRAY_TRANSFER2(int32_t,m_RootMotionSkeletonIndexArray,m_RootMotionSkeletonIndexCount);
+
+ transfer.Align();
+
+ if (transfer.IsVersionSmallerOrEqual (1))
+ {
+ if(m_RootMotionBoneIndex != -1)
+ {
+ mecanim::memory::Allocator *alloc = reinterpret_cast<mecanim::memory::Allocator *>(transfer.GetUserData());
+
+ m_RootMotionSkeleton = skeleton::CreateSkeleton(m_AvatarSkeleton->m_Count,m_AvatarSkeleton->m_AxesCount,*alloc);
+ skeleton::SkeletonCopy(m_AvatarSkeleton.Get(),m_RootMotionSkeleton.Get());
+
+ m_RootMotionSkeletonPose = skeleton::CreateSkeletonPose(m_RootMotionSkeleton.Get(),*alloc);
+ skeleton::SkeletonPoseCopy(m_AvatarSkeletonPose.Get(),m_RootMotionSkeletonPose.Get());
+
+ m_RootMotionSkeletonIndexCount = m_AvatarSkeleton->m_Count;
+ m_RootMotionSkeletonIndexArray = alloc->ConstructArray<mecanim::int32_t>(m_RootMotionSkeletonIndexCount);
+
+ for(int i = 0; i < m_RootMotionSkeletonIndexCount; i++)
+ {
+ m_RootMotionSkeletonIndexArray[i] = i;
+ }
+ }
+ }
+
+ if (transfer.IsVersionSmallerOrEqual (2))
+ {
+ if(isHuman())
+ {
+ mecanim::memory::Allocator *alloc = reinterpret_cast<mecanim::memory::Allocator *>(transfer.GetUserData());
+
+ m_HumanSkeletonReverseIndexCount = m_AvatarSkeleton->m_Count;
+ m_HumanSkeletonReverseIndexArray = alloc->ConstructArray<mecanim::int32_t>(m_HumanSkeletonReverseIndexCount);
+ skeleton::SkeletonBuildReverseIndexArray(m_HumanSkeletonReverseIndexArray.Get(),m_HumanSkeletonIndexArray.Get(),m_Human->m_Skeleton.Get(),m_AvatarSkeleton.Get());
+ }
+ }
+ }
+
+ bool isHuman() const { return !m_Human.IsNull() && m_Human->GetTypeString() != 0 && m_Human->m_Skeleton->m_Count > 0; };
+
+ static void InitializeClass();
+ };
+
+ struct AvatarInput
+ {
+ AvatarInput() : m_GotoStateInfos(0), m_DeltaTime(0), m_TargetIndex(-1), m_TargetTime(1), m_FeetPivotActive(1), m_StabilizeFeet(false), m_ForceStateTime(false), m_StateTime(0), m_LayersAffectMassCenter(false) {}
+
+ statemachine::GotoStateInfo* m_GotoStateInfos;
+ float m_DeltaTime;
+ int m_TargetIndex;
+ float m_TargetTime;
+
+ float m_FeetPivotActive;
+ bool m_StabilizeFeet;
+
+ bool m_ForceStateTime;
+ float m_StateTime;
+ bool m_LayersAffectMassCenter;
+ };
+
+ struct AvatarMemory
+ {
+ DEFINE_GET_TYPESTRING(AvatarMemory)
+
+ AvatarMemory() : m_AvatarX(math::xformIdentity()),
+ m_LeftFootX(math::xformIdentity()),
+ m_RightFootX(math::xformIdentity()),
+ m_Pivot(math::float4::zero()),
+ m_PivotWeight(0.5),
+ m_FirstEval(1),
+ m_SkeletonPoseOutputReady(0) {}
+
+ OffsetPtr<ControllerMemory> m_ControllerMemory;
+
+ math::xform m_AvatarX;
+
+ math::xform m_LeftFootX;
+ math::xform m_RightFootX;
+ math::float4 m_Pivot;
+
+ float m_PivotWeight;
+ UInt8 m_FirstEval;
+ UInt8 m_SkeletonPoseOutputReady;
+
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_ControllerMemory);
+ TRANSFER(m_AvatarX);
+ TRANSFER(m_LeftFootX);
+ TRANSFER(m_RightFootX);
+ TRANSFER(m_Pivot);
+ TRANSFER(m_PivotWeight);
+ TRANSFER(m_FirstEval);
+ TRANSFER(m_SkeletonPoseOutputReady);
+ transfer.Align();
+ }
+ };
+
+ struct AvatarWorkspace
+ {
+ AvatarWorkspace() : m_BodySkeletonPoseWs(0),
+ m_BodySkeletonPoseWsA(0),
+ m_BodySkeletonPoseWsB(0),
+ m_RootMotionSkeletonPoseWsA(0),
+ m_RootMotionSkeletonPoseWsB(0),
+ m_HumanPoseWs(0),
+ m_ControllerWorkspace(0),
+ m_LeftFootSpeedT(0),
+ m_LeftFootSpeedQ(0),
+ m_RightFootSpeedT(0),
+ m_RightFootSpeedQ(0),
+ m_IKOnFeet(false)
+ {}
+
+ skeleton::SkeletonPose* m_BodySkeletonPoseWs;
+ skeleton::SkeletonPose* m_BodySkeletonPoseWsA;
+ skeleton::SkeletonPose* m_BodySkeletonPoseWsB;
+
+ skeleton::SkeletonPose* m_RootMotionSkeletonPoseWsA;
+ skeleton::SkeletonPose* m_RootMotionSkeletonPoseWsB;
+
+ human::HumanPose* m_HumanPoseWs;
+
+ ControllerWorkspace* m_ControllerWorkspace;
+
+ math::xform m_AvatarX;
+
+ float m_LeftFootSpeedT;
+ float m_LeftFootSpeedQ;
+ float m_RightFootSpeedT;
+ float m_RightFootSpeedQ;
+ bool m_IKOnFeet;
+ };
+
+ struct AvatarOutput
+ {
+ AvatarOutput() : m_DynamicValuesOutput(0),
+ m_SkeletonPoseOutput(0),
+ m_MotionOutput(0),
+ m_HumanPoseBaseOutput(0),
+ m_HumanPoseOutput(0) {}
+
+ ValueArray* m_DynamicValuesOutput;
+
+ skeleton::SkeletonPose* m_SkeletonPoseOutput;
+
+ MotionOutput* m_MotionOutput;
+ human::HumanPose* m_HumanPoseBaseOutput;
+ human::HumanPose* m_HumanPoseOutput;
+ };
+
+ AvatarConstant* CreateAvatarConstant( skeleton::Skeleton* skeleton,
+ skeleton::SkeletonPose* skeletonPose,
+ skeleton::SkeletonPose* defaultPose,
+ human::Human* human,
+ skeleton::Skeleton* rootMotionSkeleton,
+ int rootMotionIndex,
+ math::xform const& rootMotionX,
+ memory::Allocator& alloc);
+
+ void DestroyAvatarConstant(AvatarConstant* constant, memory::Allocator& alloc);
+
+ void InitializeAvatarConstant(AvatarConstant * constant, memory::Allocator& alloc);
+ void ClearAvatarConstant(AvatarConstant * constant, memory::Allocator& alloc);
+
+ ControllerConstant* CreateControllerConstant( uint32_t LayerCount, LayerConstant** layerArray,
+ uint32_t stateMachineCount, statemachine::StateMachineConstant** stateMachineConstant,
+ ValueArrayConstant* values, ValueArray* defaultValues,
+ memory::Allocator& alloc);
+
+ void DestroyControllerConstant(ControllerConstant* controller, memory::Allocator& alloc);
+
+ void InitializeControllerConstant(ControllerConstant * controller, memory::Allocator& alloc);
+ void ClearControllerConstant(ControllerConstant * controller, memory::Allocator& alloc);
+
+ LayerConstant* CreateLayerConstant(mecanim::uint32_t stateMachineIndex, mecanim::uint32_t motionSetIndex, memory::Allocator& alloc);
+ void DestroyLayerConstant(LayerConstant* constant, memory::Allocator& alloc);
+
+ ControllerMemory* CreateControllerMemory(ControllerConstant const* controller, AvatarConstant const *avatar, AnimationSet const *animationSet, const ValueArrayConstant* dynamicValueConstant, memory::Allocator& alloc);
+ void DestroyControllerMemory(ControllerMemory* memory, memory::Allocator& alloc);
+
+ ControllerWorkspace* CreateControllerWorkspace(ControllerConstant const* controller, AvatarConstant const *avatar, AnimationSet const *animationSet, const ValueArrayConstant* dynamicValueConstant, memory::Allocator& alloc);
+ void DestroyControllerWorkspace(ControllerWorkspace* workspace, memory::Allocator& alloc);
+
+ AvatarInput* CreateAvatarInput(AvatarConstant const* constant, memory::Allocator& alloc);
+ void DestroyAvatarInput(AvatarInput* input, memory::Allocator& alloc);
+
+ AvatarMemory* CreateAvatarMemory(AvatarConstant const* constant, memory::Allocator& alloc);
+ void DestroyAvatarMemory(AvatarMemory* memory, memory::Allocator& alloc);
+
+ AvatarWorkspace* CreateAvatarWorkspace(AvatarConstant const* constant, memory::Allocator& alloc);
+ void DestroyAvatarWorkspace(AvatarWorkspace* workspace, memory::Allocator& alloc);
+
+ AvatarOutput* CreateAvatarOutput(AvatarConstant const* constant, bool hasTransformHierarchy, memory::Allocator& alloc);
+ void DestroyAvatarOutput(AvatarOutput* output, memory::Allocator& alloc);
+
+ AnimationSet* CreateAnimationSet(ControllerConstant const* controller, memory::Allocator& alloc);
+ void DestroyAnimationSet(AnimationSet* animationSet, memory::Allocator& alloc);
+
+ AnimationSetMemory* CreateAnimationSetMemory(AnimationSet const* animationSet, bool allowConstantCurveOptimization, memory::Allocator& alloc);
+ void DestroyAnimationSetMemory(AnimationSetMemory* animationSetMemory, memory::Allocator& alloc);
+
+ void UpdateLeafNodeDuration(const ControllerConstant &controllerConstant, const AnimationSet &animationSet, ControllerMemory &controllerMemory);
+
+ void SetIKOnFeet( bool left,
+ AvatarConstant const &avatar,
+ const AvatarInput &input,
+ AvatarMemory &memory,
+ AvatarWorkspace &workspace,
+ AvatarOutput &output);
+
+ void EvaluateAvatarSM( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput * output,
+ AvatarMemory * memory,
+ AvatarWorkspace * workspace,
+ ControllerConstant const* controllerConstant);
+
+ void EvaluateAvatarLayers( ControllerBindingConstant const* controllerBindingConstant,
+ AvatarInput const* input,
+ AvatarOutput *output,
+ AvatarMemory *memory,
+ AvatarWorkspace *workspace,
+ AnimationSetMemory* animationSetMemory);
+
+ void EvaluateAvatarX( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput *output,
+ AvatarMemory *memory,
+ AvatarWorkspace *workspace);
+
+ void EvaluateAvatarRetarget( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput *output,
+ AvatarMemory *memory,
+ AvatarWorkspace *workspace,
+ ControllerConstant const* controllerConstant);
+
+ void EvaluateAvatarIK( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput *output,
+ AvatarMemory *memory,
+ AvatarWorkspace *workspace,
+ ControllerConstant const* controllerConstant);
+
+ void EvaluateAvatarEnd( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput *output,
+ AvatarMemory *memory,
+ AvatarWorkspace *workspace,
+ ControllerConstant const* controllerConstant);
+
+ void ValuesFromClip( mecanim::ValueArray const &valuesDefault,
+ mecanim::animation::ClipMuscleConstant const &cst,
+ mecanim::animation::ClipOutput const &out,
+ const ClipBindings& bindings,
+ int32_t integerRemapStride,
+ mecanim::ValueArray &values,
+ mecanim::ValueArrayMask &mask);
+
+ void DeltasFromClip( mecanim::animation::ClipMuscleConstant const &cst,
+ const ClipBindings& bindings,
+ const ValueArrayMask& mask,
+ mecanim::ValueArray &starts,
+ mecanim::ValueArray &stops);
+
+ void SkeletonPoseFromValue(skeleton::Skeleton const &skeleton, skeleton::SkeletonPose const &defaultPose, ValueArray const &values, SkeletonTQSMap const *skeletonTQSMap, skeleton::SkeletonPose &pose,int32_t const *humanReverseIndex,bool skipRoot);
+ void SkeletonPoseFromValue(skeleton::Skeleton const &skeleton, skeleton::SkeletonPose const &defaultPose, ValueArray const &values, SkeletonTQSMap const *skeletonTQSMap, int32_t const *indexArray, skeleton::SkeletonPose &pose,int index, int stopIndex);
+ void ValueFromSkeletonPose(skeleton::Skeleton const &skeleton, skeleton::SkeletonPose const &pose, SkeletonTQSMap const *skeletonTQSMap, ValueArray &values);
+ void ValueFromSkeletonPose(skeleton::Skeleton const &skeleton, skeleton::SkeletonPose const &pose, SkeletonTQSMap const *skeletonTQSMap, int32_t const *indexArray, ValueArray &values, int index, int stopIndex);
+ }
+}
+
+template<>
+class SerializeTraits< mecanim::animation::BlendingState<false> > : public SerializeTraitsBase< mecanim::animation::BlendingState<false> >
+{
+ public:
+
+ typedef mecanim::animation::BlendingState<false> value_type;
+ inline static const char* GetTypeString (void*) { return "BlendingState<1>"; }
+ inline static bool IsAnimationChannel () { return false; }
+ inline static bool MightContainPPtr () { return true; }
+ inline static bool AllowTransferOptimization () { return false; }
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ transfer.Transfer(data.m_DynamicValuesBlending, "m_DynamicValuesBlending");
+ transfer.Transfer(data.m_DynamicValuesBlendingMask, "m_DynamicValuesBlendingMask");
+ transfer.Transfer(data.m_MotionBlending, "m_MotionBlending");
+ transfer.Transfer(data.m_HumanPoseBlending, "m_HumanPoseBlending");
+ transfer.Transfer(data.m_BlendFactor, "m_BlendFactor");
+ }
+};
+
+
diff --git a/Runtime/mecanim/animation/blendtree.cpp b/Runtime/mecanim/animation/blendtree.cpp
new file mode 100644
index 0000000..e326b77
--- /dev/null
+++ b/Runtime/mecanim/animation/blendtree.cpp
@@ -0,0 +1,967 @@
+#include "UnityPrefix.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/mecanim/animation/blendtree.h"
+#include "Runtime/mecanim/generic/stringtable.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+namespace mecanim
+{
+
+namespace animation
+{
+ void GetWeightsFreeformDirectional (const Blend2dDataConstant& blendConstant,
+ float* weightArray, int* cropArray, Vector2f* workspaceBlendVectors,
+ float blendValueX, float blendValueY, bool preCompute);
+ void GetWeightsFreeformCartesian (const Blend2dDataConstant& blendConstant,
+ float* weightArray, int* cropArray, Vector2f* workspaceBlendVectors,
+ float blendValueX, float blendValueY, bool preCompute);
+
+ void PrecomputeFreeform (int type, Blend2dDataConstant& out, memory::Allocator& alloc)
+ {
+ const Vector2f* positionArray = out.m_ChildPositionArray.Get();
+ mecanim::uint32_t count = out.m_ChildCount;
+ float* constantMagnitudes = out.m_ChildMagnitudeArray.Get();
+ Vector2f* constantChildPairVectors = out.m_ChildPairVectorArray.Get();
+ float* constantChildPairAvgMagInv = out.m_ChildPairAvgMagInvArray.Get();
+ MotionNeighborList* constantChildNeighborLists = out.m_ChildNeighborListArray.Get();
+
+ if (type == 2)
+ {
+ for (int i=0; i<count; i++)
+ constantMagnitudes[i] = Magnitude (positionArray[i]);
+
+ for (int i=0; i<count; i++)
+ {
+ for (int j=0; j<count; j++)
+ {
+ int pairIndex = i + j*count;
+
+ // Calc avg magnitude for pair
+ float magSum = constantMagnitudes[j] + constantMagnitudes[i];
+ if (magSum > 0)
+ constantChildPairAvgMagInv[pairIndex] = 2.0f / magSum;
+ else
+ constantChildPairAvgMagInv[pairIndex] = 2.0f / magSum;
+
+ // Calc mag of vector and divide by avg magnitude
+ float mag = (constantMagnitudes[j] - constantMagnitudes[i]) * constantChildPairAvgMagInv[pairIndex];
+
+ if (constantMagnitudes[j] == 0 || constantMagnitudes[i] == 0)
+ constantChildPairVectors[pairIndex] = Vector2f (0, mag);
+ else
+ {
+ float angle = Angle (positionArray[i], positionArray[j]);
+ if (positionArray[i].x * positionArray[j].y - positionArray[i].y * positionArray[j].x < 0)
+ angle = -angle;
+ constantChildPairVectors[pairIndex] = Vector2f (angle, mag);
+ }
+ }
+ }
+ }
+ else if (type == 3)
+ {
+ for (int i=0; i<count; i++)
+ {
+ for (int j=0; j<count; j++)
+ {
+ int pairIndex = i + j*count;
+ constantChildPairAvgMagInv[pairIndex] = 1 / SqrMagnitude (positionArray[j] - positionArray[i]);
+ constantChildPairVectors[pairIndex] = positionArray[j] - positionArray[i];
+ }
+ }
+ }
+
+ float* weightArray;
+ ALLOC_TEMP (weightArray, float, count);
+
+ int* cropArray;
+ ALLOC_TEMP (cropArray, int, count);
+
+ Vector2f* workspaceBlendVectors;
+ ALLOC_TEMP (workspaceBlendVectors, Vector2f, count);
+
+ bool* neighborArray;
+ ALLOC_TEMP (neighborArray, bool, count*count);
+ for (int c=0; c<count*count; c++)
+ neighborArray[c] = false;
+
+ float minX = 10000.0f;
+ float maxX = -10000.0f;
+ float minY = 10000.0f;
+ float maxY = -10000.0f;
+ for (int c=0; c<count; c++)
+ {
+ minX = min (minX, positionArray[c].x);
+ maxX = max (maxX, positionArray[c].x);
+ minY = min (minY, positionArray[c].y);
+ maxY = max (maxY, positionArray[c].y);
+ }
+ float xRange = (maxX - minX) * 0.5f;
+ float yRange = (maxY - minY) * 0.5f;
+ minX -= xRange;
+ maxX += xRange;
+ minY -= yRange;
+ maxY += yRange;
+
+ for (int i=0; i<=100; i++)
+ {
+ for (int j=0; j<=100; j++)
+ {
+ float x = i*0.01f;
+ float y = j*0.01f;
+ if (type == 2)
+ GetWeightsFreeformDirectional (out, weightArray, cropArray, workspaceBlendVectors, minX * (1-x) + maxX * x, minY * (1-y) + maxY * y, true);
+ else if (type == 3)
+ GetWeightsFreeformCartesian (out, weightArray, cropArray, workspaceBlendVectors, minX * (1-x) + maxX * x, minY * (1-y) + maxY * y, true);
+ for (int c=0; c<count; c++)
+ if (cropArray[c] >= 0)
+ neighborArray[c * count + cropArray[c]] = true;
+ }
+ }
+ for (int c=0; c<count; c++)
+ {
+ dynamic_array<int> nList;
+ for (int d=0; d<count; d++)
+ if (neighborArray[c * count + d])
+ nList.push_back (d);
+
+ constantChildNeighborLists[c].m_Count = nList.size ();
+ constantChildNeighborLists[c].m_NeighborArray = alloc.ConstructArray<mecanim::uint32_t>(nList.size ());
+
+ for (int d=0; d<nList.size (); d++)
+ constantChildNeighborLists[c].m_NeighborArray[d] = nList[d];
+ }
+ }
+
+ void GetAllBlendValue(uint32_t nodeIndex, OffsetPtr<BlendTreeNodeConstant>* const allTreeNodes, dynamic_array<int> &arBlendValueIds)
+ {
+ BlendTreeNodeConstant* const currentNode = allTreeNodes[nodeIndex].Get();
+
+ if(currentNode->m_BlendEventID != -1)
+ {
+ dynamic_array<int>::const_iterator it = std::find(arBlendValueIds.begin(), arBlendValueIds.end(), currentNode->m_BlendEventID);
+ if(it == arBlendValueIds.end())
+ arBlendValueIds.push_back(currentNode->m_BlendEventID);
+
+ if(currentNode->m_BlendType >= 1)
+ {
+ dynamic_array<int>::const_iterator itY = std::find(arBlendValueIds.begin(), arBlendValueIds.end(), currentNode->m_BlendEventYID);
+ if(itY == arBlendValueIds.end())
+ arBlendValueIds.push_back(currentNode->m_BlendEventYID);
+ }
+
+ for(mecanim::uint32_t i = 0 ; i < currentNode->m_ChildCount; i++)
+ {
+ GetAllBlendValue(currentNode->m_ChildIndices[i], allTreeNodes, arBlendValueIds);
+ }
+ }
+
+ }
+ void GetAllBlendValue(BlendTreeConstant* const constant, dynamic_array<int> &arBlendValueIds)
+ {
+ if(constant->m_NodeCount > 0)
+ GetAllBlendValue(0, constant->m_NodeArray.Get(), arBlendValueIds);
+ }
+
+
+ BlendTreeNodeConstant* CreateBlendTreeNodeConstant(uint32_t blendValueID, uint32_t childCount, uint32_t* childIndices, float* blendTreeThresholdArray, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeNodeConstant);
+
+ BlendTreeNodeConstant *blendTreeNodeConstant = alloc.Construct<BlendTreeNodeConstant>();
+
+ blendTreeNodeConstant->m_BlendEventID = blendValueID;
+ blendTreeNodeConstant->m_ChildCount = childCount;
+
+ blendTreeNodeConstant->m_ChildIndices = alloc.ConstructArray<uint32_t>(childCount);
+ memcpy(&blendTreeNodeConstant->m_ChildIndices[0], &childIndices[0], sizeof(uint32_t)*childCount);
+
+ // Setup blend 1d data constant
+ blendTreeNodeConstant->m_BlendType = 0;
+ blendTreeNodeConstant->m_Blend1dData = alloc.Construct<Blend1dDataConstant>();
+ Blend1dDataConstant& data = *blendTreeNodeConstant->m_Blend1dData;
+
+ // Populate blend 1d data constant
+ data.m_ChildCount = childCount;
+ data.m_ChildThresholdArray = alloc.ConstructArray<float>(data.m_ChildCount);
+ memcpy(&data.m_ChildThresholdArray[0], &blendTreeThresholdArray[0], sizeof(float)*childCount);
+
+ return blendTreeNodeConstant ;
+ }
+
+ BlendTreeNodeConstant* CreateBlendTreeNodeConstant(uint32_t blendValueID, uint32_t blendValueYID, int blendType, uint32_t childCount, uint32_t* childIndices, Vector2f* blendTreePositionArray, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeNodeConstant);
+
+ BlendTreeNodeConstant *blendTreeNodeConstant = alloc.Construct<BlendTreeNodeConstant>();
+
+ blendTreeNodeConstant->m_BlendEventID = blendValueID;
+ blendTreeNodeConstant->m_BlendEventYID = blendValueYID;
+ blendTreeNodeConstant->m_ChildCount = childCount;
+
+ blendTreeNodeConstant->m_ChildIndices = alloc.ConstructArray<uint32_t>(childCount);
+ memcpy(&blendTreeNodeConstant->m_ChildIndices[0], &childIndices[0], sizeof(uint32_t)*childCount);
+
+ // Setup blend 2d data constant
+ blendTreeNodeConstant->m_BlendType = blendType;
+ blendTreeNodeConstant->m_Blend2dData = alloc.Construct<Blend2dDataConstant>();
+ Blend2dDataConstant& data = *blendTreeNodeConstant->m_Blend2dData;
+
+ // Populate blend 2d data constant
+ data.m_ChildCount = childCount;
+ data.m_ChildPositionArray = alloc.ConstructArray<Vector2f>(data.m_ChildCount);
+ memcpy(&data.m_ChildPositionArray[0], &blendTreePositionArray[0], sizeof(Vector2f)*childCount);
+
+ if (blendType == 2 || blendType == 3)
+ {
+ // Populate blend 2d precomputed data for type 2 or 3
+ if (blendType == 2)
+ {
+ data.m_ChildMagnitudeCount = childCount;
+ data.m_ChildMagnitudeArray = alloc.ConstructArray<float>(data.m_ChildMagnitudeCount);
+ }
+ data.m_ChildPairAvgMagInvCount = childCount * childCount;
+ data.m_ChildPairVectorCount = childCount * childCount;
+ data.m_ChildNeighborListCount = childCount;
+ data.m_ChildPairAvgMagInvArray = alloc.ConstructArray<float>(data.m_ChildPairAvgMagInvCount);
+ data.m_ChildPairVectorArray = alloc.ConstructArray<Vector2f>(data.m_ChildPairVectorCount);
+ data.m_ChildNeighborListArray = alloc.ConstructArray<MotionNeighborList>(data.m_ChildNeighborListCount);
+ PrecomputeFreeform (blendType, data, alloc);
+ }
+
+ return blendTreeNodeConstant;
+ }
+
+ BlendTreeNodeConstant* CreateBlendTreeNodeConstant(uint32_t clipID, float duration, bool mirror, float cycle, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeNodeConstant);
+ BlendTreeNodeConstant *blendTreeNodeConstant = alloc.Construct<BlendTreeNodeConstant>();
+
+ blendTreeNodeConstant->m_ChildCount = 0;
+ blendTreeNodeConstant->m_ClipID = clipID;
+ blendTreeNodeConstant->m_Duration = duration;
+ blendTreeNodeConstant->m_Mirror = mirror;
+ blendTreeNodeConstant->m_CycleOffset = cycle;
+
+ return blendTreeNodeConstant ;
+ }
+
+ BlendTreeConstant* CreateBlendTreeConstant(BlendTreeNodeConstant** nodeArray, uint32_t nodeCount, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeConstant);
+
+ BlendTreeConstant *blendTreeConstant = alloc.Construct<BlendTreeConstant>();
+
+ blendTreeConstant->m_NodeCount = nodeCount;
+ blendTreeConstant->m_NodeArray = alloc.ConstructArray< OffsetPtr<BlendTreeNodeConstant> >(nodeCount);
+
+ uint32_t i;
+ for(i = 0; i < nodeCount; i++)
+ blendTreeConstant->m_NodeArray[i] = nodeArray[i];
+
+ dynamic_array<int> blendValueIds;
+ GetAllBlendValue(blendTreeConstant, blendValueIds);
+
+ blendTreeConstant->m_BlendEventArrayConstant = CreateValueArrayConstant(kFloatType, blendValueIds.size(), alloc);
+
+ for(i = 0; i < blendValueIds.size(); i++)
+ {
+ blendTreeConstant->m_BlendEventArrayConstant->m_ValueArray[i].m_ID = blendValueIds[i];
+ }
+
+ return blendTreeConstant ;
+ }
+
+ BlendTreeConstant* CreateBlendTreeConstant(uint32_t clipID, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeConstant);
+
+ BlendTreeConstant *blendTreeConstant = alloc.Construct<BlendTreeConstant>();
+ blendTreeConstant->m_NodeCount = 1;
+ blendTreeConstant->m_NodeArray = alloc.ConstructArray< OffsetPtr<BlendTreeNodeConstant> >(1);
+
+ blendTreeConstant->m_NodeArray[0] = CreateBlendTreeNodeConstant(clipID,1.0f,false,0,alloc);
+
+ return blendTreeConstant;
+
+ }
+
+ void DestroyBlendTreeNodeConstant(BlendTreeNodeConstant * constant, memory::Allocator& alloc)
+ {
+ alloc.Deallocate(constant->m_ChildIndices);
+
+ if (!constant->m_Blend1dData.IsNull ())
+ {
+ alloc.Deallocate(constant->m_Blend1dData->m_ChildThresholdArray);
+ }
+
+ if (!constant->m_Blend2dData.IsNull ())
+ {
+ alloc.Deallocate(constant->m_Blend2dData->m_ChildPositionArray);
+ alloc.Deallocate(constant->m_Blend2dData->m_ChildMagnitudeArray);
+ alloc.Deallocate(constant->m_Blend2dData->m_ChildPairVectorArray);
+ alloc.Deallocate(constant->m_Blend2dData->m_ChildPairAvgMagInvArray);
+
+ if (!constant->m_Blend2dData->m_ChildNeighborListArray.IsNull ())
+ {
+ for (int i=0; i<constant->m_Blend2dData->m_ChildNeighborListCount; i++)
+ alloc.Deallocate(constant->m_Blend2dData->m_ChildNeighborListArray[i].m_NeighborArray);
+ alloc.Deallocate(constant->m_Blend2dData->m_ChildNeighborListArray);
+ }
+ }
+
+ alloc.Deallocate(constant);
+ }
+
+ void DestroyBlendTreeConstant(BlendTreeConstant * constant, memory::Allocator& alloc)
+ {
+ if(constant)
+ {
+ if(!constant->m_BlendEventArrayConstant.IsNull())
+ {
+ DestroyValueArrayConstant(constant->m_BlendEventArrayConstant.Get(), alloc);
+ }
+
+ for(uint32_t i = 0; i < constant->m_NodeCount; i++)
+ {
+ DestroyBlendTreeNodeConstant(constant->m_NodeArray[i].Get(), alloc);
+ }
+
+ alloc.Deallocate(constant->m_NodeArray);
+ alloc.Deallocate(constant);
+ }
+
+ }
+
+ BlendTreeMemory* CreateBlendTreeMemory(BlendTreeConstant const* constant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeMemory);
+ BlendTreeMemory *blendTreeMemory = alloc.Construct<BlendTreeMemory>();
+
+ blendTreeMemory->m_NodeCount = GetLeafCount(*constant);
+ blendTreeMemory->m_NodeDurationArray = alloc.ConstructArray<float>(blendTreeMemory->m_NodeCount);
+
+ return blendTreeMemory ;
+ }
+
+ void DestroyBlendTreeMemory(BlendTreeMemory *memory, memory::Allocator& alloc)
+ {
+ if(memory)
+ {
+ alloc.Deallocate(memory->m_NodeDurationArray);
+ alloc.Deallocate(memory);
+ }
+ }
+
+ BlendTreeInput* CreateBlendTreeInput(BlendTreeConstant const* constant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeInput);
+
+ BlendTreeInput *blendTreeInput = alloc.Construct<BlendTreeInput>();
+
+ if(!constant->m_BlendEventArrayConstant.IsNull())
+ blendTreeInput->m_BlendValueArray = CreateValueArray(constant->m_BlendEventArrayConstant.Get(), alloc);
+
+ return blendTreeInput ;
+ }
+
+ void DestroyBlendTreeInput(BlendTreeInput * input, memory::Allocator& alloc)
+ {
+ if(input)
+ {
+ if(input->m_BlendValueArray)
+ {
+ DestroyValueArray(input->m_BlendValueArray, alloc);
+ }
+
+ alloc.Deallocate(input);
+ }
+ }
+
+ BlendTreeOutput* CreateBlendTreeOutput(BlendTreeConstant const* constant, uint32_t maxBlendedClip, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeOutput);
+ BlendTreeOutput *blendTreeOutput = alloc.Construct<BlendTreeOutput>();
+
+ blendTreeOutput->m_MaxBlendedClip = maxBlendedClip;
+ blendTreeOutput->m_OutputBlendArray = alloc.ConstructArray< BlendTreeNodeOutput >(maxBlendedClip);
+
+ return blendTreeOutput ;
+ }
+
+ void DestroyBlendTreeOutput(BlendTreeOutput *output, memory::Allocator& alloc)
+ {
+ if(output)
+ {
+ alloc.Deallocate(output->m_OutputBlendArray);
+ alloc.Deallocate(output);
+ }
+ }
+
+ BlendTreeWorkspace* CreateBlendTreeWorkspace(BlendTreeConstant const* constant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeWorkspace);
+
+ BlendTreeWorkspace *blendTreeWorkspace = alloc.Construct<BlendTreeWorkspace>();
+ blendTreeWorkspace->m_BlendArray = alloc.ConstructArray<float>(constant->m_NodeCount);
+ // Optimize later to only have room for worst case number of immediate children instead of m_NodeCount:
+ blendTreeWorkspace->m_TempWeightArray = alloc.ConstructArray<float>(constant->m_NodeCount);
+ blendTreeWorkspace->m_TempCropArray = alloc.ConstructArray<int>(constant->m_NodeCount);
+ blendTreeWorkspace->m_ChildInputVectorArray = alloc.ConstructArray<Vector2f>(constant->m_NodeCount);
+
+ return blendTreeWorkspace ;
+ }
+
+ void DestroyBlendTreeWorkspace(BlendTreeWorkspace * workspace, memory::Allocator& alloc)
+ {
+ if(workspace != 0)
+ {
+ alloc.Deallocate(workspace->m_BlendArray);
+ alloc.Deallocate(workspace->m_TempWeightArray);
+ alloc.Deallocate(workspace->m_TempCropArray);
+ alloc.Deallocate(workspace->m_ChildInputVectorArray);
+ alloc.Deallocate(workspace);
+ }
+ }
+
+ float WeightForIndex( const float* thresholdArray, mecanim::uint32_t count, mecanim::uint32_t index, float blend)
+ {
+ if( blend >= thresholdArray[index])
+ {
+ if(index+1 == count)
+ {
+ return 1.0f;
+ }
+ else if(thresholdArray[index+1] < blend)
+ {
+ return 0.0f;
+ }
+ else
+ {
+ if(thresholdArray[index]-thresholdArray[index+1] != 0)
+ {
+ return (blend - thresholdArray[index+1]) / (thresholdArray[index]-thresholdArray[index+1]);
+ }
+ else
+ {
+ return thresholdArray[index];
+ }
+
+ }
+ }
+ else
+ {
+ if(index == 0)
+ {
+ return 1.0f;
+ }
+ else if(thresholdArray[index-1] > blend)
+ {
+ return 0.0f;
+ }
+ else
+ {
+ if(( thresholdArray[index]-thresholdArray[index-1]) != 0)
+ {
+ return (blend - thresholdArray[index-1]) / (thresholdArray[index]-thresholdArray[index-1]);
+ }
+ else
+ {
+ return thresholdArray[index];
+ }
+ }
+ }
+
+ }
+
+ void GetWeightsSimpleDirectional (const Blend2dDataConstant& blendConstant,
+ float* weightArray, int* cropArray, Vector2f* workspaceBlendVectors,
+ float blendValueX, float blendValueY, bool preCompute = false)
+ {
+ // Get constants
+ const Vector2f* positionArray = blendConstant.m_ChildPositionArray.Get();
+ mecanim::uint32_t count = blendConstant.m_ChildCount;
+
+ if (weightArray == NULL || positionArray == NULL)
+ return;
+
+ // Initialize all weights to 0
+ for (int i=0; i<count; i++)
+ weightArray[i] = 0;
+
+ // Handle fallback
+ if (count < 2)
+ {
+ if (count == 1)
+ weightArray[0] = 1;
+ return;
+ }
+
+ Vector2f blendPosition = Vector2f (blendValueX, blendValueY);
+
+ // Handle special case when sampled ecactly in the middle
+ if (blendPosition == Vector2f::zero)
+ {
+ // If we have a center motion, give that one all the weight
+ for (int i=0; i<count; i++)
+ {
+ if (positionArray[i] == Vector2f::zero)
+ {
+ weightArray[i] = 1;
+ return;
+ }
+ }
+
+ // Otherwise divide weight evenly
+ float sharedWeight = 1.0f / count;
+ for (int i=0; i<count; i++)
+ weightArray[i] = sharedWeight;
+ return;
+ }
+
+ int indexA = -1;
+ int indexB = -1;
+ int indexCenter = -1;
+ float maxDotForNegCross = -100000.0f;
+ float maxDotForPosCross = -100000.0f;
+ for (int i=0; i<count; i++)
+ {
+ if (positionArray[i] == Vector2f::zero)
+ {
+ if (indexCenter >= 0)
+ return;
+ indexCenter = i;
+ continue;
+ }
+ Vector2f posNormalized = Normalize (positionArray[i]);
+ float dot = Dot (posNormalized, blendPosition);
+ float cross = posNormalized.x * blendPosition.y - posNormalized.y * blendPosition.x;
+ if (cross > 0)
+ {
+ if (dot > maxDotForPosCross)
+ {
+ maxDotForPosCross = dot;
+ indexA = i;
+ }
+ }
+ else
+ {
+ if (dot > maxDotForNegCross)
+ {
+ maxDotForNegCross = dot;
+ indexB = i;
+ }
+ }
+ }
+
+ float centerWeight = 0;
+
+ if (indexA < 0 || indexB < 0)
+ {
+ // Fallback if sampling point is not inside a triangle
+ centerWeight = 1;
+ }
+ else
+ {
+ Vector2f a = positionArray[indexA];
+ Vector2f b = positionArray[indexB];
+
+ // Calculate weights using barycentric coordinates
+ // (formulas from http://en.wikipedia.org/wiki/Barycentric_coordinate_system_%28mathematics%29 )
+ float det = b.y*a.x - b.x*a.y; // Simplified from: (b.y-0)*(a.x-0) + (0-b.x)*(a.y-0);
+ float wA = (b.y*blendValueX - b.x*blendValueY) / det; // Simplified from: ((b.y-0)*(l.x-0) + (0-b.x)*(l.y-0)) / det;
+ float wB = (a.x*blendValueY - a.y*blendValueX) / det; // Simplified from: ((0-a.y)*(l.x-0) + (a.x-0)*(l.y-0)) / det;
+ centerWeight = 1 - wA - wB;
+
+ // Clamp to be inside triangle
+ if (centerWeight < 0)
+ {
+ centerWeight = 0;
+ float sum = wA + wB;
+ wA /= sum;
+ wB /= sum;
+ }
+ else if (centerWeight > 1)
+ {
+ centerWeight = 1;
+ wA = 0;
+ wB = 0;
+ }
+
+ // Give weight to the two vertices on the periphery that are closest
+ weightArray[indexA] = wA;
+ weightArray[indexB] = wB;
+ }
+
+ if (indexCenter >= 0)
+ {
+ weightArray[indexCenter] = centerWeight;
+ }
+ else
+ {
+ // Give weight to all children when input is in the center
+ float sharedWeight = 1.0f / count;
+ for (int i=0; i<count; i++)
+ weightArray[i] += sharedWeight * centerWeight;
+ }
+ }
+
+ const float kInversePI = 1 / kPI;
+ float GetWeightFreeformDirectional (const Blend2dDataConstant& blendConstant, Vector2f* workspaceBlendVectors, int i, int j, Vector2f blendPosition)
+ {
+ int pairIndex = i + j*blendConstant.m_ChildCount;
+ Vector2f vecIJ = blendConstant.m_ChildPairVectorArray[pairIndex];
+ Vector2f vecIO = workspaceBlendVectors[i];
+ vecIO.y *= blendConstant.m_ChildPairAvgMagInvArray[pairIndex];
+
+ if (blendConstant.m_ChildPositionArray[i] == Vector2f::zero)
+ vecIJ.x = workspaceBlendVectors[j].x;
+ else if (blendConstant.m_ChildPositionArray[j] == Vector2f::zero)
+ vecIJ.x = workspaceBlendVectors[i].x;
+ else if (vecIJ.x == 0 || blendPosition == Vector2f::zero)
+ vecIO.x = vecIJ.x;
+
+ return 1 - Dot (vecIJ, vecIO) / SqrMagnitude (vecIJ);
+ }
+
+ void GetWeightsFreeformDirectional (const Blend2dDataConstant& blendConstant,
+ float* weightArray, int* cropArray, Vector2f* workspaceBlendVectors,
+ float blendValueX, float blendValueY, bool preCompute = false)
+ {
+ // Get constants
+ const Vector2f* positionArray = blendConstant.m_ChildPositionArray.Get();
+ mecanim::uint32_t count = blendConstant.m_ChildCount;
+ const float* constantMagnitudes = blendConstant.m_ChildMagnitudeArray.Get();
+ const MotionNeighborList* constantChildNeighborLists = blendConstant.m_ChildNeighborListArray.Get();
+
+ Vector2f blendPosition = Vector2f (blendValueX, blendValueY);
+ float magO = Magnitude (blendPosition);
+
+ if (blendPosition == Vector2f::zero)
+ {
+ for (int i=0; i<count; i++)
+ workspaceBlendVectors[i] = Vector2f (0, magO - constantMagnitudes[i]);
+ }
+ else
+ {
+ for (int i=0; i<count; i++)
+ {
+ if (positionArray[i] == Vector2f::zero)
+ workspaceBlendVectors[i] = Vector2f (0, magO - constantMagnitudes[i]);
+ else
+ {
+ float angle = Angle (positionArray[i], blendPosition);
+ if (positionArray[i].x * blendPosition.y - positionArray[i].y * blendPosition.x < 0)
+ angle = -angle;
+ workspaceBlendVectors[i] = Vector2f (angle, magO - constantMagnitudes[i]);
+ }
+ }
+ }
+
+ if (preCompute)
+ {
+ for (int i=0; i<count; i++)
+ {
+ // Fade out over 180 degrees away from example
+ float value = 1 - abs (workspaceBlendVectors[i].x) * kInversePI;
+ cropArray[i] = -1;
+ for (int j=0; j<count; j++)
+ {
+ if (i==j)
+ continue;
+
+ float newValue = GetWeightFreeformDirectional (blendConstant, workspaceBlendVectors, i, j, blendPosition);
+
+ if (newValue <= 0)
+ {
+ value = 0;
+ cropArray[i] = -1;
+ break;
+ }
+ // Used for determining neighbors
+ if (newValue < value)
+ cropArray[i] = j;
+ value = min (value, newValue);
+ }
+ }
+ return;
+ }
+
+ for (int i=0; i<count; i++)
+ {
+ // Fade out over 180 degrees away from example
+ float value = 1 - abs (workspaceBlendVectors[i].x) * kInversePI;
+ for (int jIndex=0; jIndex<constantChildNeighborLists[i].m_Count; jIndex++)
+ {
+ int j = constantChildNeighborLists[i].m_NeighborArray[jIndex];
+ float newValue = GetWeightFreeformDirectional (blendConstant, workspaceBlendVectors, i, j, blendPosition);
+ if (newValue <= 0)
+ {
+ value = 0;
+ break;
+ }
+ value = min (value, newValue);
+ }
+ weightArray[i] = value;
+ }
+
+ // Normalize weights
+ float summedWeight = 0;
+ for (int i=0; i<count; i++)
+ summedWeight += weightArray[i];
+
+ if (summedWeight > 0)
+ {
+ summedWeight = 1.0f / summedWeight; // Do division once instead of for every sample
+ for (int i=0; i<count; i++)
+ weightArray[i] *= summedWeight;
+ }
+ else
+ {
+ // Give weight to all children as fallback when no children have any weight.
+ // This happens when sampling in the center if no center motion is provided.
+ float evenWeight = 1.0f / count;
+ for (int i=0; i<count; i++)
+ weightArray[i] = evenWeight;
+ }
+ }
+
+ void GetWeightsFreeformCartesian (const Blend2dDataConstant& blendConstant,
+ float* weightArray, int* cropArray, Vector2f* workspaceBlendVectors,
+ float blendValueX, float blendValueY, bool preCompute = false)
+ {
+ // Get constants
+ const Vector2f* positionArray = blendConstant.m_ChildPositionArray.Get();
+ mecanim::uint32_t count = blendConstant.m_ChildCount;
+ const MotionNeighborList* constantChildNeighborLists = blendConstant.m_ChildNeighborListArray.Get();
+
+ Vector2f blendPosition = Vector2f (blendValueX, blendValueY);
+ for (int i=0; i<count; i++)
+ workspaceBlendVectors[i] = blendPosition - positionArray[i];
+
+ if (preCompute)
+ {
+ for (int i=0; i<count; i++)
+ {
+ cropArray[i] = -1;
+ Vector2f vecIO = workspaceBlendVectors[i];
+ float value = 1;
+ for (int j=0; j<count; j++)
+ {
+ if (i==j)
+ continue;
+
+ int pairIndex = i + j*blendConstant.m_ChildCount;
+ Vector2f vecIJ = blendConstant.m_ChildPairVectorArray[pairIndex];
+ float newValue = 1 - Dot (vecIJ, vecIO) * blendConstant.m_ChildPairAvgMagInvArray[pairIndex];
+ if (newValue <= 0)
+ {
+ value = 0;
+ cropArray[i] = -1;
+ break;
+ }
+ // Used for determining neighbors
+ if (newValue < value)
+ cropArray[i] = j;
+ value = min (value, newValue);
+ }
+ }
+ return;
+ }
+
+ for (int i=0; i<count; i++)
+ {
+ Vector2f vecIO = workspaceBlendVectors[i];
+ float value = 1;
+ for (int jIndex=0; jIndex<constantChildNeighborLists[i].m_Count; jIndex++)
+ {
+ int j = constantChildNeighborLists[i].m_NeighborArray[jIndex];
+ if (i==j)
+ continue;
+
+ int pairIndex = i + j*blendConstant.m_ChildCount;
+ Vector2f vecIJ = blendConstant.m_ChildPairVectorArray[pairIndex];
+ float newValue = 1 - Dot (vecIJ, vecIO) * blendConstant.m_ChildPairAvgMagInvArray[pairIndex];
+ if (newValue < 0)
+ {
+ value = 0;
+ break;
+ }
+ value = min (value, newValue);
+ }
+ weightArray[i] = value;
+ }
+
+ // Normalize weights
+ float summedWeight = 0;
+ for (int i=0; i<count; i++)
+ summedWeight += weightArray[i];
+ summedWeight = 1.0f / summedWeight; // Do division once instead of for every sample
+ for (int i=0; i<count; i++)
+ weightArray[i] *= summedWeight;
+ }
+
+ void GetWeights1d (const Blend1dDataConstant& blendConstant, float* weightArray, float blendValue)
+ {
+ blendValue = math::clamp (blendValue, blendConstant.m_ChildThresholdArray[0], blendConstant.m_ChildThresholdArray[blendConstant.m_ChildCount-1]);
+ for (mecanim::uint32_t j = 0 ; j < blendConstant.m_ChildCount; j++)
+ weightArray[j] = WeightForIndex (blendConstant.m_ChildThresholdArray.Get (), blendConstant.m_ChildCount, j, blendValue);
+ }
+
+ void GetWeights (const BlendTreeNodeConstant& nodeConstant, BlendTreeWorkspace &workspace, float* weightArray, float blendValueX, float blendValueY)
+ {
+ if (nodeConstant.m_BlendType == 0)
+ GetWeights1d (*nodeConstant.m_Blend1dData.Get(), weightArray, blendValueX);
+ else if (nodeConstant.m_BlendType == 1)
+ GetWeightsSimpleDirectional (*nodeConstant.m_Blend2dData.Get(), weightArray, workspace.m_TempCropArray, workspace.m_ChildInputVectorArray, blendValueX, blendValueY);
+ else if (nodeConstant.m_BlendType == 2)
+ GetWeightsFreeformDirectional (*nodeConstant.m_Blend2dData.Get(), weightArray, workspace.m_TempCropArray, workspace.m_ChildInputVectorArray, blendValueX, blendValueY);
+ else if (nodeConstant.m_BlendType == 3)
+ GetWeightsFreeformCartesian (*nodeConstant.m_Blend2dData.Get(), weightArray, workspace.m_TempCropArray, workspace.m_ChildInputVectorArray, blendValueX, blendValueY);
+ }
+
+ uint32_t ComputeBlends(const BlendTreeConstant& constant, const BlendTreeInput &input, const BlendTreeMemory &memory, BlendTreeOutput &output, BlendTreeWorkspace &workspace)
+ {
+ uint32_t leafIndex = 0;
+ uint32_t currentOutputIndex = 0 ;
+ workspace.m_BlendArray[0] = 1;
+
+ uint32_t i = 0;
+ for(i = 0 ; i < constant.m_NodeCount; i ++)
+ {
+ const BlendTreeNodeConstant* nodeConstant = constant.m_NodeArray[i].Get();
+
+ if(nodeConstant->m_ClipID != -1)
+ {
+ if(workspace.m_BlendArray[i] > 0)
+ {
+ float duration = IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1) ? memory.m_NodeDurationArray[leafIndex] * nodeConstant->m_Duration : nodeConstant->m_Duration;
+
+ output.m_OutputBlendArray[currentOutputIndex].m_ID = nodeConstant->m_ClipID;
+ output.m_OutputBlendArray[currentOutputIndex].m_BlendValue = workspace.m_BlendArray[i];
+ output.m_OutputBlendArray[currentOutputIndex].m_Reverse = duration < 0;
+ output.m_OutputBlendArray[currentOutputIndex].m_CycleOffset = nodeConstant->m_CycleOffset;
+ output.m_OutputBlendArray[currentOutputIndex].m_Mirror = nodeConstant->m_Mirror;
+
+ output.m_Duration += math::abs(duration) * workspace.m_BlendArray[i];
+ currentOutputIndex++;
+ }
+
+ leafIndex++;
+ }
+ else if(nodeConstant->m_ChildCount> 0)
+ {
+ if (nodeConstant->m_BlendType == 0)
+ {
+ // 1D blending
+ int32_t index = FindValueIndex(constant.m_BlendEventArrayConstant.Get(), nodeConstant->m_BlendEventID);
+ float blendValue;
+ input.m_BlendValueArray->ReadData(blendValue, constant.m_BlendEventArrayConstant->m_ValueArray[index].m_Index);
+
+ GetWeights (*nodeConstant, workspace, workspace.m_TempWeightArray, blendValue, 0);
+ }
+ else if (nodeConstant->m_BlendType >= 1)
+ {
+ // 2D blending
+ int32_t indexX = FindValueIndex(constant.m_BlendEventArrayConstant.Get(), nodeConstant->m_BlendEventID);
+ int32_t indexY = FindValueIndex(constant.m_BlendEventArrayConstant.Get(), nodeConstant->m_BlendEventYID);
+ float blendValueX, blendValueY;
+ input.m_BlendValueArray->ReadData(blendValueX, constant.m_BlendEventArrayConstant->m_ValueArray[indexX].m_Index);
+ input.m_BlendValueArray->ReadData(blendValueY, constant.m_BlendEventArrayConstant->m_ValueArray[indexY].m_Index);
+
+ GetWeights (*nodeConstant, workspace, workspace.m_TempWeightArray, blendValueX, blendValueY);
+ }
+
+ for(mecanim::uint32_t j = 0 ; j < nodeConstant->m_ChildCount; j++)
+ {
+ float w = workspace.m_TempWeightArray[j];
+ workspace.m_BlendArray[nodeConstant->m_ChildIndices[j]] = w * workspace.m_BlendArray[i];
+ }
+ }
+ }
+
+ return currentOutputIndex;
+ }
+
+ void EvaluateBlendTree(const BlendTreeConstant& constant, const BlendTreeInput &input, const BlendTreeMemory &memory, BlendTreeOutput &output, BlendTreeWorkspace &workspace)
+ {
+ for(uint32_t i = 0 ; i < output.m_MaxBlendedClip ; i++) output.m_OutputBlendArray[i].m_ID = -1;
+
+ output.m_Duration = 0;
+ uint32_t currentOutputIndex = 0;
+
+ if(constant.m_NodeCount >0)
+ currentOutputIndex = ComputeBlends(constant, input, memory, output, workspace);
+
+ if(currentOutputIndex == 0)
+ output.m_Duration = 1;
+ }
+
+
+ mecanim::uint32_t GetLeafCount(const BlendTreeConstant& constant)
+ {
+ mecanim::uint32_t leafCount = 0 ;
+
+ for(int i = 0 ; i < constant.m_NodeCount ; i++)
+ {
+ if(constant.m_NodeArray[i]->m_ClipID != -1)
+ leafCount++;
+ }
+
+ return leafCount;
+ }
+
+ void FillLeafIDArray(const BlendTreeConstant& constant, uint32_t* leafIDArray)
+ {
+ uint32_t baseIndex = 0 ;
+ uint32_t i;
+ for(i = 0 ; i < constant.m_NodeCount ; i++)
+ {
+ if(constant.m_NodeArray[i]->m_ClipID != -1)
+ {
+ leafIDArray[baseIndex] = constant.m_NodeArray[i]->m_ClipID;
+ baseIndex++;
+ }
+ }
+ }
+
+ mecanim::uint32_t GetMaxBlendCount(const BlendTreeConstant& constant, const BlendTreeNodeConstant& node)
+ {
+ uint32_t maxBlendCount = node.m_ClipID != -1 ? 1 : 0;
+
+ // Blending occur between closest sibbling only
+ uint32_t current = 0;
+ uint32_t previous = 0;
+
+ if(node.m_BlendType == 0 )
+ {
+ for(int i = 0 ; i < node.m_ChildCount ; i++)
+ {
+ current = GetMaxBlendCount(constant, *constant.m_NodeArray[node.m_ChildIndices[i]]);
+ maxBlendCount = math::maximum( maxBlendCount, previous + current);
+ previous = current;
+ }
+ }
+ else
+ {
+ for(int i = 0 ; i < node.m_ChildCount ; i++)
+ {
+ maxBlendCount += GetMaxBlendCount(constant, *constant.m_NodeArray[node.m_ChildIndices[i]]);
+ }
+ }
+
+ return maxBlendCount;
+ }
+
+ mecanim::uint32_t GetMaxBlendCount(const BlendTreeConstant& constant)
+ {
+ uint32_t maxBlendCount = 0;
+ if(constant.m_NodeCount)
+ maxBlendCount = GetMaxBlendCount(constant, *constant.m_NodeArray[0]);
+ return maxBlendCount;
+ }
+
+
+}// namespace animation
+
+}//namespace mecanim
diff --git a/Runtime/mecanim/animation/blendtree.h b/Runtime/mecanim/animation/blendtree.h
new file mode 100644
index 0000000..ee3f7a5
--- /dev/null
+++ b/Runtime/mecanim/animation/blendtree.h
@@ -0,0 +1,285 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/animation/curvedata.h"
+#include "Runtime/mecanim/human/human.h"
+#include "Runtime/mecanim/human/hand.h"
+#include "Runtime/Math/Vector2.h"
+
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Animation/MecanimArraySerialization.h"
+
+namespace mecanim
+{
+
+namespace animation
+{
+ struct MotionNeighborList
+ {
+ DEFINE_GET_TYPESTRING(MotionNeighborList)
+
+ MotionNeighborList() : m_Count(0)
+ {
+ }
+
+ uint32_t m_Count;
+ OffsetPtr<uint32_t> m_NeighborArray;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_Count);
+ MANUAL_ARRAY_TRANSFER2(uint32_t, m_NeighborArray, m_Count);
+ }
+ };
+
+ // Constant data for 1D blend node types - thresholds
+ struct Blend1dDataConstant
+ {
+ DEFINE_GET_TYPESTRING(Blend2dDataConstant)
+
+ Blend1dDataConstant() : m_ChildCount(0)
+ {
+ }
+
+ uint32_t m_ChildCount;
+ OffsetPtr<float> m_ChildThresholdArray;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_ChildCount);
+ MANUAL_ARRAY_TRANSFER2(float, m_ChildThresholdArray, m_ChildCount);
+ }
+ };
+
+ // Constant data for 2D blend node types - positions plus precomputed data to speed up blending
+ struct Blend2dDataConstant
+ {
+ DEFINE_GET_TYPESTRING(Blend2dDataConstant)
+
+ Blend2dDataConstant() : m_ChildCount(0), m_ChildMagnitudeCount(0), m_ChildPairVectorCount(0), m_ChildPairAvgMagInvCount(0), m_ChildNeighborListCount(0)
+ {
+ }
+
+ uint32_t m_ChildCount;
+ OffsetPtr<Vector2f> m_ChildPositionArray;
+
+ uint32_t m_ChildMagnitudeCount;
+ OffsetPtr<float> m_ChildMagnitudeArray; // Used by type 2
+ uint32_t m_ChildPairVectorCount;
+ OffsetPtr<Vector2f> m_ChildPairVectorArray; // Used by type 2, (3 TODO)
+ uint32_t m_ChildPairAvgMagInvCount;
+ OffsetPtr<float> m_ChildPairAvgMagInvArray; // Used by type 2
+ uint32_t m_ChildNeighborListCount;
+ OffsetPtr<MotionNeighborList> m_ChildNeighborListArray; // Used by type 2, (3 TODO)
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_ChildCount);
+ MANUAL_ARRAY_TRANSFER2(Vector2f, m_ChildPositionArray, m_ChildCount);
+
+ TRANSFER_BLOB_ONLY(m_ChildMagnitudeCount);
+ MANUAL_ARRAY_TRANSFER2(float, m_ChildMagnitudeArray, m_ChildMagnitudeCount);
+ TRANSFER_BLOB_ONLY(m_ChildPairVectorCount);
+ MANUAL_ARRAY_TRANSFER2(Vector2f, m_ChildPairVectorArray, m_ChildPairVectorCount);
+ TRANSFER_BLOB_ONLY(m_ChildPairAvgMagInvCount);
+ MANUAL_ARRAY_TRANSFER2(float, m_ChildPairAvgMagInvArray, m_ChildPairAvgMagInvCount);
+ TRANSFER_BLOB_ONLY(m_ChildNeighborListCount);
+ MANUAL_ARRAY_TRANSFER2(MotionNeighborList, m_ChildNeighborListArray, m_ChildNeighborListCount);
+ }
+ };
+
+ struct BlendTreeNodeConstant
+ {
+ DEFINE_GET_TYPESTRING(BlendTreeNodeConstant)
+
+ BlendTreeNodeConstant(): m_BlendType(0), m_BlendEventID(-1), m_BlendEventYID(-1), m_ChildCount(0), m_ClipID(-1), m_Duration(0), m_CycleOffset(0), m_Mirror(false)
+ {
+
+ }
+
+ uint32_t m_BlendType;
+
+ uint32_t m_BlendEventID;
+ uint32_t m_BlendEventYID;
+ uint32_t m_ChildCount;
+ OffsetPtr<uint32_t> m_ChildIndices;
+
+ OffsetPtr<Blend1dDataConstant> m_Blend1dData;
+ OffsetPtr<Blend2dDataConstant> m_Blend2dData;
+
+ uint32_t m_ClipID; // assert( m_ClipID != -1 && mClipBlendCount == 0)
+ float m_Duration;
+ float m_CycleOffset;
+ bool m_Mirror;
+
+ // Unity 4.1 introduced 2D blendtrees. The data layout has been changed there.
+ template<class TransferFunction>
+ inline void Transfer_4_0_BackwardsCompatibility (TransferFunction& transfer)
+ {
+ if (transfer.IsOldVersion(1))
+ {
+ if (m_Blend1dData.IsNull())
+ {
+ mecanim::memory::ChainedAllocator* allocator = static_cast<mecanim::memory::ChainedAllocator*> (transfer.GetUserData());
+ m_Blend1dData = allocator->Construct<Blend1dDataConstant>();
+ }
+
+ OffsetPtr<float>& m_ChildThresholdArray = m_Blend1dData->m_ChildThresholdArray;
+ MANUAL_ARRAY_TRANSFER2(float, m_ChildThresholdArray, m_Blend1dData->m_ChildCount);
+ }
+ }
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ transfer.SetVersion(2);
+
+ TRANSFER(m_BlendType);
+ TRANSFER(m_BlendEventID);
+ TRANSFER(m_BlendEventYID);
+
+ TRANSFER_BLOB_ONLY(m_ChildCount);
+ MANUAL_ARRAY_TRANSFER2(uint32_t, m_ChildIndices, m_ChildCount);
+
+ TRANSFER(m_Blend1dData);
+ TRANSFER(m_Blend2dData);
+
+ TRANSFER(m_ClipID);
+ TRANSFER(m_Duration);
+
+ TRANSFER(m_CycleOffset);
+ TRANSFER(m_Mirror);
+ transfer.Align();
+
+ Transfer_4_0_BackwardsCompatibility(transfer);
+
+ }
+ };
+
+ struct BlendTreeConstant
+ {
+ DEFINE_GET_TYPESTRING(BlendTreeConstant)
+
+ BlendTreeConstant () :m_NodeCount(0)
+ {
+ }
+
+ uint32_t m_NodeCount;
+ OffsetPtr< OffsetPtr<BlendTreeNodeConstant> > m_NodeArray;
+
+ OffsetPtr<ValueArrayConstant> m_BlendEventArrayConstant;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_NodeCount);
+ MANUAL_ARRAY_TRANSFER2( OffsetPtr<mecanim::animation::BlendTreeNodeConstant>, m_NodeArray, m_NodeCount);
+
+ TRANSFER(m_BlendEventArrayConstant);
+ }
+ };
+
+ struct BlendTreeMemory
+ {
+ DEFINE_GET_TYPESTRING(BlendTreeMemory)
+
+ BlendTreeMemory() : m_NodeCount(0) {}
+
+ uint32_t m_NodeCount;
+ OffsetPtr<float> m_NodeDurationArray;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_NodeCount);
+ MANUAL_ARRAY_TRANSFER2(float, m_NodeDurationArray, m_NodeCount);
+ }
+ };
+
+ struct BlendTreeInput
+ {
+ BlendTreeInput() : m_BlendValueArray(0)
+ {
+ }
+
+ ValueArray* m_BlendValueArray;
+ };
+
+ struct BlendTreeNodeOutput
+ {
+ BlendTreeNodeOutput() : m_BlendValue(0), m_ID(0), m_Reverse(false), m_Mirror(false), m_CycleOffset(0)
+ {
+
+ }
+
+ float m_BlendValue;
+ uint32_t m_ID;
+ bool m_Reverse;
+ bool m_Mirror;
+ float m_CycleOffset;
+ };
+
+ struct BlendTreeOutput
+ {
+ BlendTreeOutput() : m_Duration(0),
+ m_MaxBlendedClip(0)
+ {}
+
+ BlendTreeNodeOutput* m_OutputBlendArray;
+ uint32_t m_MaxBlendedClip;
+ float m_Duration;
+ };
+
+ struct BlendTreeWorkspace
+ {
+ BlendTreeWorkspace() : m_BlendArray(0), m_TempWeightArray(0), m_TempCropArray(0), m_ChildInputVectorArray(0)
+ {
+
+ }
+
+
+ float* m_BlendArray;
+ float* m_TempWeightArray;
+ int* m_TempCropArray;
+ Vector2f* m_ChildInputVectorArray;
+ };
+
+ void GetWeights (const BlendTreeNodeConstant& nodeConstant, BlendTreeWorkspace &workspace, float* weightArray, float blendValueX, float blendValueY);
+
+ // Overload for creating 1D blend node
+ BlendTreeNodeConstant* CreateBlendTreeNodeConstant(uint32_t blendValueID, uint32_t childCount, uint32_t* childIndices, float* blendTreeThresholdArray, memory::Allocator& alloc);
+ // Overload for creating 2D blend node
+ BlendTreeNodeConstant* CreateBlendTreeNodeConstant(uint32_t blendValueID, uint32_t blendValueYID, int blendType, uint32_t childCount, uint32_t* childIndices, Vector2f* blendTreePositionArray, memory::Allocator& alloc);
+ // Overload for creating leaf blend node
+ BlendTreeNodeConstant* CreateBlendTreeNodeConstant(uint32_t clipID, float duration, bool mirror, float cycle, memory::Allocator& alloc);
+
+ BlendTreeConstant* CreateBlendTreeConstant(BlendTreeNodeConstant** nodeArray, uint32_t nodeCount, memory::Allocator& alloc);
+ BlendTreeConstant* CreateBlendTreeConstant(uint32_t clipID, memory::Allocator& alloc);
+ void DestroyBlendTreeConstant(BlendTreeConstant * constant, memory::Allocator& alloc);
+
+ BlendTreeMemory* CreateBlendTreeMemory(BlendTreeConstant const* constant, memory::Allocator& alloc);
+ void DestroyBlendTreeMemory(BlendTreeMemory *memory, memory::Allocator& alloc);
+
+ BlendTreeInput* CreateBlendTreeInput(BlendTreeConstant const* constant, memory::Allocator& alloc);
+ void DestroyBlendTreeInput(BlendTreeInput * input, memory::Allocator& alloc);
+
+ BlendTreeOutput* CreateBlendTreeOutput(BlendTreeConstant const* constant, uint32_t maxBlendedClip, memory::Allocator& alloc);
+ void DestroyBlendTreeOutput(BlendTreeOutput * output, memory::Allocator& alloc);
+
+ BlendTreeWorkspace* CreateBlendTreeWorkspace(BlendTreeConstant const* constant, memory::Allocator& alloc);
+ void DestroyBlendTreeWorkspace(BlendTreeWorkspace * workspace, memory::Allocator& alloc);
+
+
+ void EvaluateBlendTree(const BlendTreeConstant& constant, const BlendTreeInput &input, const BlendTreeMemory &memory, BlendTreeOutput &output, BlendTreeWorkspace &workspace);
+
+ mecanim::uint32_t GetLeafCount(const BlendTreeConstant& constant);
+ void FillLeafIDArray(const BlendTreeConstant& constant, uint32_t* leafIDArray);
+ mecanim::uint32_t GetMaxBlendCount(const BlendTreeConstant& constant);
+}
+}
diff --git a/Runtime/mecanim/animation/clipmuscle.cpp b/Runtime/mecanim/animation/clipmuscle.cpp
new file mode 100644
index 0000000..f6f53d2
--- /dev/null
+++ b/Runtime/mecanim/animation/clipmuscle.cpp
@@ -0,0 +1,1366 @@
+#include "UnityPrefix.h"
+#include "Runtime/mecanim/animation/clipmuscle.h"
+#include "Runtime/mecanim/skeleton/skeleton.h"
+#include "Runtime/mecanim/generic/stringtable.h"
+
+namespace mecanim
+{
+
+namespace animation
+{
+ struct MuscleIndexId
+ {
+ mecanim::uint32_t index;
+ mecanim::uint32_t id;
+ };
+
+ static mecanim::String s_ClipMuscleNameArray[s_ClipMuscleCurveCount];
+ static MuscleIndexId s_ClipMuscleIndexIDArray[s_ClipMuscleCurveCount];
+
+ static void ClipMuscleNameArrayInit()
+ {
+ int i,j;
+
+ i = 0;
+ s_ClipMuscleNameArray[i++] = "MotionT.x";
+ s_ClipMuscleNameArray[i++] = "MotionT.y";
+ s_ClipMuscleNameArray[i++] = "MotionT.z";
+
+ s_ClipMuscleNameArray[i++] = "MotionQ.x";
+ s_ClipMuscleNameArray[i++] = "MotionQ.y";
+ s_ClipMuscleNameArray[i++] = "MotionQ.z";
+ s_ClipMuscleNameArray[i++] = "MotionQ.w";
+
+ s_ClipMuscleNameArray[i++] = "RootT.x";
+ s_ClipMuscleNameArray[i++] = "RootT.y";
+ s_ClipMuscleNameArray[i++] = "RootT.z";
+
+ s_ClipMuscleNameArray[i++] = "RootQ.x";
+ s_ClipMuscleNameArray[i++] = "RootQ.y";
+ s_ClipMuscleNameArray[i++] = "RootQ.z";
+ s_ClipMuscleNameArray[i++] = "RootQ.w";
+
+ for(j=0;j<mecanim::human::kLastGoal;j++)
+ {
+ mecanim::String nameT(mecanim::human::BoneName(mecanim::human::s_HumanGoalInfo[j].m_Index));
+ nameT += "T";
+ mecanim::String nameTx = nameT;
+ nameTx += ".x";
+ mecanim::String nameTy = nameT;
+ nameTy += ".y";
+ mecanim::String nameTz = nameT;
+ nameTz += ".z";
+
+ mecanim::String nameQ(mecanim::human::BoneName(mecanim::human::s_HumanGoalInfo[j].m_Index));
+ nameQ += "Q";
+ mecanim::String nameQx = nameQ;
+ nameQx += ".x";
+ mecanim::String nameQy = nameQ;
+ nameQy += ".y";
+ mecanim::String nameQz = nameQ;
+ nameQz += ".z";
+ mecanim::String nameQw = nameQ;
+ nameQw += ".w";
+
+ s_ClipMuscleNameArray[i++] = nameTx;
+ s_ClipMuscleNameArray[i++] = nameTy;
+ s_ClipMuscleNameArray[i++] = nameTz;
+
+ s_ClipMuscleNameArray[i++] = nameQx;
+ s_ClipMuscleNameArray[i++] = nameQy;
+ s_ClipMuscleNameArray[i++] = nameQz;
+ s_ClipMuscleNameArray[i++] = nameQw;
+ }
+
+ for(j=0;j<mecanim::human::kLastDoF;j++)
+ {
+ s_ClipMuscleNameArray[i++] = mecanim::human::MuscleName(j);
+ }
+
+ for(int f=0;f<mecanim::hand::kLastFinger;++f)
+ {
+ for(int d=0;d<mecanim::hand::kLastFingerDoF;++d)
+ {
+ mecanim::String name("LeftHand.");
+ name += mecanim::hand::FingerName(f);
+ name += ".";
+ name += mecanim::hand::FingerDoFName(d);
+
+ s_ClipMuscleNameArray[i++] = name;
+ }
+ }
+
+ for(int f=0;f<mecanim::hand::kLastFinger;++f)
+ {
+ for(int d=0;d<mecanim::hand::kLastFingerDoF;++d)
+ {
+ mecanim::String name("RightHand.");
+ name += mecanim::hand::FingerName(f);
+ name += ".";
+ name += mecanim::hand::FingerDoFName(d);
+
+ s_ClipMuscleNameArray[i++] = name;
+ }
+ }
+ }
+
+ static bool MuscleIndexIdSortFunction(MuscleIndexId i, MuscleIndexId j)
+ {
+ return i.id < j.id;
+ }
+
+ static void ClipMuscleIDArrayInit()
+ {
+ for(int i = 0; i < s_ClipMuscleCurveCount; i++)
+ {
+ s_ClipMuscleIndexIDArray[i].index = i;
+ s_ClipMuscleIndexIDArray[i].id = mecanim::processCRC32(GetMuscleCurveName(i));
+ }
+
+ std::sort(s_ClipMuscleIndexIDArray, s_ClipMuscleIndexIDArray + s_ClipMuscleCurveCount, MuscleIndexIdSortFunction);
+ }
+
+ void InitializeMuscleClipTables ()
+ {
+ ClipMuscleNameArrayInit();
+ ClipMuscleIDArrayInit();
+ }
+
+
+ mecanim::String const &GetMuscleCurveName(int32_t curveIndex)
+ {
+ Assert(s_ClipMuscleNameArray != NULL);
+ return s_ClipMuscleNameArray[curveIndex];
+ }
+
+ class MuscleIndexIdFindfunction
+ {
+ public:
+
+ uint32_t mId;
+
+ MuscleIndexIdFindfunction(uint32_t id) : mId(id) {}
+
+ bool operator()(const MuscleIndexId &indexId)
+ {
+ return indexId.id == mId;
+ }
+ };
+
+ int32_t FindMuscleIndex(uint32_t id)
+ {
+ Assert(s_ClipMuscleIndexIDArray[0].id != 0);
+
+ MuscleIndexId* end = s_ClipMuscleIndexIDArray + s_ClipMuscleCurveCount;
+
+ const MuscleIndexId* found = std::find_if(s_ClipMuscleIndexIDArray, end, MuscleIndexIdFindfunction(id));
+ if (found != end)
+ {
+ Assert(found->id == id);
+ return found->index;
+ }
+ else
+ return -1;
+ }
+
+ int32_t GetMuscleCurveTQIndex(int32_t curveIndex)
+ {
+ return (curveIndex < (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount) ? curveIndex%s_ClipMuscleCurveTQCount : -1;
+ };
+
+ float GetXformCurveValue(math::xform const& x, int32_t index)
+ {
+ float ret = 0;
+
+ if(index == 0)
+ {
+ ret = x.t.x().tofloat();
+ }
+ else if(index == 1)
+ {
+ ret = x.t.y().tofloat();
+ }
+ else if(index == 2)
+ {
+ ret = x.t.z().tofloat();
+ }
+ else if(index == 3)
+ {
+ ret = x.q.x().tofloat();
+ }
+ else if(index == 4)
+ {
+ ret = x.q.y().tofloat();
+ }
+ else if(index == 5)
+ {
+ ret = x.q.z().tofloat();
+ }
+ else if(index == 6)
+ {
+ ret = x.q.w().tofloat();
+ }
+
+ return ret;
+ }
+
+ float GetMuscleCurveValue(human::HumanPose const& pose, math::xform const &motionX, int32_t curveIndex)
+ {
+ float ret = 0;
+
+ if(curveIndex < s_ClipMuscleCurveTQCount)
+ {
+ ret = GetXformCurveValue(motionX,curveIndex);
+ }
+ else if(curveIndex < 2*s_ClipMuscleCurveTQCount)
+ {
+ ret = GetXformCurveValue(pose.m_RootX,curveIndex-s_ClipMuscleCurveTQCount);
+ }
+ else if(curveIndex < (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount)
+ {
+ int index = curveIndex - (2*s_ClipMuscleCurveTQCount);
+ int goalIndex = index / s_ClipMuscleCurveTQCount;
+ int xformIndex = index % s_ClipMuscleCurveTQCount;
+
+ ret = GetXformCurveValue(pose.m_GoalArray[goalIndex].m_X,xformIndex);
+ }
+ else if(curveIndex < (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount + human::kLastDoF)
+ {
+ int dofIndex = curveIndex - (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount;
+ ret = pose.m_DoFArray[dofIndex];
+ }
+ else if(curveIndex < (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount + human::kLastDoF + hand::s_DoFCount)
+ {
+ int dofIndex = curveIndex - ((2 + human::kLastGoal) * s_ClipMuscleCurveTQCount + human::kLastDoF);
+ ret = pose.m_LeftHandPose.m_DoFArray[dofIndex];
+ }
+ else if(curveIndex < (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount + human::kLastDoF + 2 * hand::s_DoFCount)
+ {
+ int dofIndex = curveIndex - ((2 + human::kLastGoal) * s_ClipMuscleCurveTQCount + human::kLastDoF + hand::s_DoFCount);
+ ret = pose.m_RightHandPose.m_DoFArray[dofIndex];
+ }
+
+ return ret;
+ }
+
+ bool GetMuscleCurveInMask(human::HumanPoseMask const& mask, int32_t curveIndex)
+ {
+ bool ret = false;
+
+ if(curveIndex < s_ClipMuscleCurveTQCount) // root motion
+ {
+ ret = true;
+ }
+ else if(curveIndex < 2*s_ClipMuscleCurveTQCount) // root xform
+ {
+ ret = mask.test(human::kMaskRootIndex);
+ }
+ else if(curveIndex < (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount)
+ {
+ int index = curveIndex - (2*s_ClipMuscleCurveTQCount);
+ int goalIndex = index / s_ClipMuscleCurveTQCount;
+
+ ret = mask.test(human::kMaskGoalStartIndex+goalIndex);
+ }
+ else if(curveIndex < (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount+ human::kLastDoF)
+ {
+ int dofIndex = curveIndex - (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount;
+ ret = mask.test(human::kMaskDoFStartIndex+dofIndex);
+ }
+ else if(curveIndex < (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount + human::kLastDoF + hand::s_DoFCount)
+ {
+ ret = mask.test(human::kMaskLeftHand);
+ }
+ else if(curveIndex < (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount + human::kLastDoF + 2 * hand::s_DoFCount)
+ {
+ ret = mask.test(human::kMaskRightHand);
+ }
+
+ return ret;
+ }
+
+ ClipMuscleConstant* CreateClipMuscleConstant(Clip * clip, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(ClipMuscleConstant);
+
+ ClipMuscleConstant *clipMuscle = alloc.Construct<ClipMuscleConstant>();
+
+ clipMuscle->m_Clip = clip;
+ clipMuscle->m_Mirror = false;
+
+ clipMuscle->m_StartTime = 0;
+ clipMuscle->m_StopTime = 1;
+
+ clipMuscle->m_Level = 0;
+
+ clipMuscle->m_CycleOffset = 0;
+ clipMuscle->m_LoopTime = false;
+ clipMuscle->m_LoopBlend = false;
+ clipMuscle->m_LoopBlendOrientation = false;
+ clipMuscle->m_LoopBlendPositionY = false;
+ clipMuscle->m_LoopBlendPositionXZ = false;
+ clipMuscle->m_KeepOriginalOrientation = false;
+ clipMuscle->m_KeepOriginalPositionY = true;
+ clipMuscle->m_KeepOriginalPositionXZ = false;
+ clipMuscle->m_HeightFromFeet = false;
+
+ clipMuscle->m_ValueArrayCount = GetClipCurveCount(*clip);
+ clipMuscle->m_ValueArrayDelta = alloc.ConstructArray<ValueDelta>(clipMuscle->m_ValueArrayCount);
+
+ return clipMuscle;
+ }
+
+ void DestroyClipMuscleConstant(ClipMuscleConstant * constant, memory::Allocator& alloc)
+ {
+ if(constant)
+ {
+ alloc.Deallocate(constant->m_ValueArrayDelta);
+ alloc.Deallocate(constant);
+ }
+ }
+
+ void MotionOutputClear(MotionOutput *output)
+ {
+ output->m_DX.t = math::float4::zero();
+ output->m_DX.q = math::quatIdentity();
+ output->m_DX.s = math::float4::one();
+
+ output->m_MotionX.t = math::float4::zero();
+ output->m_MotionX.q = math::quatIdentity();
+ output->m_MotionX.s = math::float4::one();
+
+ output->m_MotionStartX.t = math::float4::zero();
+ output->m_MotionStartX.q = math::quatIdentity();
+ output->m_MotionStartX.s = math::float4::one();
+
+ output->m_MotionStopX.t = math::float4::zero();
+ output->m_MotionStopX.q = math::quatIdentity();
+ output->m_MotionStopX.s = math::float4::one();
+
+ output->m_PrevRootX.t = math::float4::zero();
+ output->m_PrevRootX.q = math::quatIdentity();
+ output->m_PrevRootX.s = math::float4::one();
+
+ output->m_PrevLeftFootX.t = math::float4::zero();
+ output->m_PrevLeftFootX.q = math::quatIdentity();
+ output->m_PrevLeftFootX.s = math::float4::one();
+
+ output->m_PrevRightFootX.t = math::float4::zero();
+ output->m_PrevRightFootX.q = math::quatIdentity();
+ output->m_PrevRightFootX.s = math::float4::one();
+
+ output->m_TargetX.t = math::float4::zero();
+ output->m_TargetX.q = math::quatIdentity();
+ output->m_TargetX.s = math::float4::one();
+
+ output->m_GravityWeight = 0;
+ }
+
+ void MotionOutputCopy(MotionOutput *output, MotionOutput const *motion, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &poseMask)
+ {
+ if(hasRootMotion || (isHuman && poseMask.test(human::kMaskRootIndex)))
+ {
+ output->m_DX = motion->m_DX;
+ output->m_GravityWeight = motion->m_GravityWeight;
+ }
+
+ if(hasRootMotion)
+ {
+ output->m_MotionX = motion->m_MotionX;
+ output->m_MotionStartX = motion->m_MotionStartX;
+ output->m_MotionStopX = motion->m_MotionStopX;
+ }
+
+ if(isHuman)
+ {
+ if(human::MaskHasLegs(poseMask))
+ {
+ output->m_PrevRootX = motion->m_PrevRootX;
+ output->m_PrevLeftFootX = motion->m_PrevLeftFootX;
+ output->m_PrevRightFootX = motion->m_PrevRightFootX;
+ }
+
+ if(poseMask.test(human::kMaskRootIndex))
+ {
+ output->m_TargetX = motion->m_TargetX;
+ }
+ }
+ }
+
+ void MotionOutputBlend(MotionOutput *output, MotionOutput **outputArray, float *weight, uint32_t count, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &poseMask)
+ {
+ output->m_DX.t = math::float4::zero();
+ output->m_DX.q = math::float4::zero();
+ output->m_DX.s = math::float4::one();
+
+ output->m_MotionX.t = math::float4::zero();
+ output->m_MotionX.q = math::float4::zero();
+ output->m_MotionX.s = math::float4::one();
+
+ output->m_MotionStartX.t = math::float4::zero();
+ output->m_MotionStartX.q = math::float4::zero();
+ output->m_MotionStartX.s = math::float4::one();
+
+ output->m_MotionStopX.t = math::float4::zero();
+ output->m_MotionStopX.q = math::float4::zero();
+ output->m_MotionStopX.s = math::float4::one();
+
+ output->m_PrevRootX.t = math::float4::zero();
+ output->m_PrevRootX.q = math::float4::zero();
+ output->m_PrevRootX.s = math::float4::one();
+
+ output->m_PrevLeftFootX.t = math::float4::zero();
+ output->m_PrevLeftFootX.q = math::float4::zero();
+ output->m_PrevLeftFootX.s = math::float4::one();
+
+ output->m_PrevRightFootX.t = math::float4::zero();
+ output->m_PrevRightFootX.q = math::float4::zero();
+ output->m_PrevRightFootX.s = math::float4::one();
+
+ output->m_TargetX.t = math::float4::zero();
+ output->m_TargetX.q = math::float4::zero();
+ output->m_TargetX.s = math::float4::one();
+
+ output->m_GravityWeight = 0;
+
+ float sumW = 0;
+
+ for(int iter = 0; iter < count; iter++)
+ {
+ float w = weight[iter];
+
+ math::float1 w1(w);
+
+ sumW += w;
+
+ if(hasRootMotion || (isHuman && poseMask.test(human::kMaskRootIndex)))
+ {
+ output->m_DX.t += outputArray[iter]->m_DX.t*w1;
+ output->m_DX.q += math::cond(math::dot(output->m_DX.q,outputArray[iter]->m_DX.q) < math::float1::zero(),outputArray[iter]->m_DX.q * -w1,outputArray[iter]->m_DX.q * w1);
+
+ output->m_GravityWeight += outputArray[iter]->m_GravityWeight*w;
+ }
+
+ if(hasRootMotion)
+ {
+ output->m_MotionX.t += outputArray[iter]->m_MotionX.t*w1;
+ output->m_MotionX.q += math::cond(math::dot(output->m_MotionX.q,outputArray[iter]->m_MotionX.q) < math::float1::zero(),outputArray[iter]->m_MotionX.q * -w1,outputArray[iter]->m_MotionX.q * w1);
+
+ output->m_MotionStartX.t += outputArray[iter]->m_MotionStartX.t*w1;
+ output->m_MotionStartX.q += math::cond(math::dot(output->m_MotionStartX.q,outputArray[iter]->m_MotionStartX.q) < math::float1::zero(),outputArray[iter]->m_MotionStartX.q * -w1,outputArray[iter]->m_MotionStartX.q * w1);
+
+ output->m_MotionStopX.t += outputArray[iter]->m_MotionStopX.t*w1;
+ output->m_MotionStopX.q += math::cond(math::dot(output->m_MotionStopX.q,outputArray[iter]->m_MotionStopX.q) < math::float1::zero(),outputArray[iter]->m_MotionStopX.q * -w1,outputArray[iter]->m_MotionStopX.q * w1);
+ }
+
+ if(isHuman)
+ {
+ if(human::MaskHasLegs(poseMask))
+ {
+ output->m_PrevRootX.t += outputArray[iter]->m_PrevRootX.t*w1;
+ output->m_PrevRootX.q += math::cond(math::dot(output->m_PrevRootX.q,outputArray[iter]->m_PrevRootX.q) < math::float1::zero(),outputArray[iter]->m_PrevRootX.q * -w1,outputArray[iter]->m_PrevRootX.q * w1);
+
+ output->m_PrevLeftFootX.t += outputArray[iter]->m_PrevLeftFootX.t*w1;
+ output->m_PrevLeftFootX.q += math::cond(math::dot(output->m_PrevLeftFootX.q,outputArray[iter]->m_PrevLeftFootX.q) < math::float1::zero(),outputArray[iter]->m_PrevLeftFootX.q * -w1,outputArray[iter]->m_PrevLeftFootX.q * w1);
+
+ output->m_PrevRightFootX.t += outputArray[iter]->m_PrevRightFootX.t*w1;
+ output->m_PrevRightFootX.q += math::cond(math::dot(output->m_PrevRightFootX.q,outputArray[iter]->m_PrevRightFootX.q) < math::float1::zero(),outputArray[iter]->m_PrevRightFootX.q * -w1,outputArray[iter]->m_PrevRightFootX.q * w1);
+ }
+
+ if(poseMask.test(human::kMaskRootIndex))
+ {
+ output->m_TargetX.t += outputArray[iter]->m_TargetX.t*w1;
+ output->m_TargetX.q += math::cond(math::dot(output->m_TargetX.q,outputArray[iter]->m_TargetX.q) < math::float1::zero(),outputArray[iter]->m_TargetX.q * -w1,outputArray[iter]->m_TargetX.q * w1);
+ }
+ }
+ }
+
+ math::float4 q(0,0,0,math::saturate(1.0f-sumW));
+
+ if(hasRootMotion || (isHuman && poseMask.test(human::kMaskRootIndex)))
+ {
+ output->m_DX.q = math::normalize(output->m_DX.q+q);
+ if(sumW > 0) output->m_GravityWeight /= sumW;
+ }
+
+ if(hasRootMotion)
+ {
+ output->m_MotionX.q = math::normalize(output->m_MotionX.q+q);
+ output->m_MotionStartX.q = math::normalize(output->m_MotionStartX.q+q);
+ output->m_MotionStopX.q = math::normalize(output->m_MotionStopX.q+q);
+ }
+
+ if(isHuman)
+ {
+ if(human::MaskHasLegs(poseMask))
+ {
+ output->m_PrevRootX.q = math::normalize(output->m_PrevRootX.q+q);
+ output->m_PrevLeftFootX.q = math::normalize(output->m_PrevLeftFootX.q+q);
+ output->m_PrevRightFootX.q = math::normalize(output->m_PrevRightFootX.q+q);
+ }
+
+ if(poseMask.test(human::kMaskRootIndex))
+ {
+ output->m_TargetX.q = math::normalize(output->m_TargetX.q+q);
+ }
+ }
+ }
+
+ void MotionAddAdditiveLayer(MotionOutput *output, MotionOutput const *motion, float weight, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &poseMask)
+ {
+ math::float1 w(weight);
+
+ if(hasRootMotion || (isHuman && poseMask.test(human::kMaskRootIndex)))
+ {
+ output->m_DX = math::xformMul(output->m_DX, math::xformWeight(motion->m_DX,w));
+ output->m_GravityWeight = output->m_GravityWeight + motion->m_GravityWeight * weight;
+ }
+
+ if(hasRootMotion)
+ {
+ output->m_MotionX = math::xformMul(output->m_MotionX, math::xformWeight(motion->m_MotionX,w));
+ output->m_MotionStartX = math::xformMul(output->m_MotionStartX, math::xformWeight(motion->m_MotionStartX,w));
+ output->m_MotionStopX = math::xformMul(output->m_MotionStopX, math::xformWeight(motion->m_MotionStopX,w));
+ }
+
+ if(isHuman)
+ {
+ if(human::MaskHasLegs(poseMask))
+ {
+ output->m_PrevRootX = math::xformMul(output->m_PrevRootX, math::xformWeight(motion->m_PrevRootX,w));
+ output->m_PrevLeftFootX = math::xformMul(output->m_PrevLeftFootX, math::xformWeight(motion->m_PrevLeftFootX,w));
+ output->m_PrevRightFootX = math::xformMul(output->m_PrevRightFootX, math::xformWeight(motion->m_PrevRightFootX,w));
+ }
+
+ if(poseMask.test(human::kMaskRootIndex))
+ {
+ output->m_TargetX = math::xformMul(output->m_TargetX, math::xformWeight(motion->m_TargetX,w));
+ }
+ }
+ }
+
+ void MotionAddOverrideLayer(MotionOutput *output, MotionOutput const *motion, float weight, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &poseMask)
+ {
+ if(weight < 1.0f)
+ {
+ math::float1 w(weight);
+
+ if(hasRootMotion || (isHuman && poseMask.test(human::kMaskRootIndex)))
+ {
+ output->m_DX = math::xformBlend(output->m_DX,motion->m_DX,w);
+ output->m_GravityWeight = math::lerp(output->m_GravityWeight,motion->m_GravityWeight,weight);
+ }
+
+ if(hasRootMotion)
+ {
+ output->m_MotionX = math::xformBlend(output->m_MotionX,motion->m_MotionX,w);
+ output->m_MotionStartX = math::xformBlend(output->m_MotionStartX,motion->m_MotionStartX,w);
+ output->m_MotionStopX = math::xformBlend(output->m_MotionStopX,motion->m_MotionStopX,w);
+ }
+
+ if(isHuman)
+ {
+ if(human::MaskHasLegs(poseMask))
+ {
+ output->m_PrevRootX = math::xformBlend(output->m_PrevRootX,motion->m_PrevRootX,w);
+ output->m_PrevLeftFootX = math::xformBlend(output->m_PrevLeftFootX,motion->m_PrevLeftFootX,w);
+ output->m_PrevRightFootX = math::xformBlend(output->m_PrevRightFootX,motion->m_PrevRightFootX,w);
+ }
+
+ if(poseMask.test(human::kMaskRootIndex))
+ {
+ output->m_TargetX = math::xformBlend(output->m_TargetX,motion->m_TargetX,w);
+ }
+ }
+ }
+ else
+ {
+ MotionOutputCopy(output,motion,hasRootMotion,isHuman,poseMask);
+ }
+ }
+
+ ClipMuscleInput* CreateClipMuscleInput(ClipMuscleConstant const* constant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(ClipMuscleInput);
+
+ ClipMuscleInput *in = alloc.Construct<ClipMuscleInput>();
+ return in;
+ }
+
+ void DestroyClipMuscleInput(ClipMuscleInput * input, memory::Allocator& alloc)
+ {
+ if(input)
+ {
+ alloc.Deallocate(input);
+ }
+ }
+
+ float ComputeClipTime(float normalizedTimeIn, float startTime, float stopTime, float cycleOffset, bool loop, bool reverse, float &normalizedTimeOut, float &timeInt)
+ {
+ float timeFrac = math::cond(loop,math::modf(normalizedTimeIn+cycleOffset,timeInt),math::saturate(normalizedTimeIn));
+ normalizedTimeOut = math::cond(!reverse, timeFrac, 1.f - timeFrac);
+ return startTime+normalizedTimeOut*(stopTime-startTime);
+ }
+
+ math::xform EvaluateMotion(ClipMuscleConstant const& constant, ClipMemory &memory, float time)
+ {
+ math::xform ret;
+
+ float ATTRIBUTE_ALIGN(ALIGN4F) value[4];
+
+ ClipInput clipIn;
+ clipIn.m_Time = time;
+
+ value[0] = constant.m_IndexArray[0] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[0]) : 0;
+ value[1] = constant.m_IndexArray[1] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[1]) : 0;
+ value[2] = constant.m_IndexArray[2] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[2]) : 0;
+ value[3] = 0.f;
+
+ ret.t = math::load(value);
+
+ value[0] = constant.m_IndexArray[3] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[3]) : 0;
+ value[1] = constant.m_IndexArray[4] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[4]) : 0;
+ value[2] = constant.m_IndexArray[5] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[5]) : 0;
+ value[3] = constant.m_IndexArray[6] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[6]) : 1;
+
+ ret.q = math::normalize(math::load(value));
+
+ return ret;
+ }
+
+ math::xform EvaluateRoot(ClipMuscleConstant const& constant, ClipMemory &memory, float time)
+ {
+ math::xform ret;
+
+ float ATTRIBUTE_ALIGN(ALIGN4F) value[4];
+
+ ClipInput clipIn;
+ clipIn.m_Time = time;
+
+ value[0] = constant.m_IndexArray[7] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[7]) : 0;
+ value[1] = constant.m_IndexArray[8] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[8]) : 0;
+ value[2] = constant.m_IndexArray[9] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[9]) : 0;
+ value[3] = 0.f;
+
+ ret.t = math::load(value);
+
+ value[0] = constant.m_IndexArray[10] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[10]) : 0;
+ value[1] = constant.m_IndexArray[11] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[11]) : 0;
+ value[2] = constant.m_IndexArray[12] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[12]) : 0;
+ value[3] = constant.m_IndexArray[13] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[13]) : 1;
+
+ ret.q = math::normalize(math::load(value));
+
+ return ret;
+ }
+
+ math::xform EvaluateGoal(ClipMuscleConstant const& constant, ClipMemory &memory, float time, int32_t goalIndex)
+ {
+ math::xform ret;
+
+ float ATTRIBUTE_ALIGN(ALIGN4F) value[4];
+
+ const int32_t index = (2 + goalIndex) * s_ClipMuscleCurveTQCount;
+
+ ClipInput clipIn;
+ clipIn.m_Time = time;
+
+ value[0] = constant.m_IndexArray[index+0] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory,constant.m_IndexArray[index+0]) : 0;
+ value[1] = constant.m_IndexArray[index+1] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory,constant.m_IndexArray[index+1]) : 0;
+ value[2] = constant.m_IndexArray[index+2] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory,constant.m_IndexArray[index+2]) : 0;
+ value[3] = 0.f;
+
+ ret.t = math::load(value);
+
+ value[0] = constant.m_IndexArray[index+3] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory,constant.m_IndexArray[index+3]) : 0;
+ value[1] = constant.m_IndexArray[index+4] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory,constant.m_IndexArray[index+4]) : 0;
+ value[2] = constant.m_IndexArray[index+5] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory,constant.m_IndexArray[index+5]) : 0;
+ value[3] = constant.m_IndexArray[index+6] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory,constant.m_IndexArray[index+6]) : 1;
+
+ ret.q = math::normalize(math::load(value));
+
+ return ret;
+ }
+
+ math::xform GetMotionX(const ClipMuscleConstant& constant, const ClipOutput &output)
+ {
+ math::xform ret;
+
+ float ATTRIBUTE_ALIGN(ALIGN4F) value[4];
+
+ uint32_t currentCurveIndex = 0;
+
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[0] = output.m_Values[constant.m_IndexArray[currentCurveIndex]]; else value[0] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[1] = output.m_Values[constant.m_IndexArray[currentCurveIndex]]; else value[1] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[2] = output.m_Values[constant.m_IndexArray[currentCurveIndex]]; else value[2] = 0; currentCurveIndex++;
+ value[3] = 0;
+
+ ret.t = math::load(value);
+
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[0] = output.m_Values[constant.m_IndexArray[currentCurveIndex]]; else value[0] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[1] = output.m_Values[constant.m_IndexArray[currentCurveIndex]]; else value[1] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[2] = output.m_Values[constant.m_IndexArray[currentCurveIndex]]; else value[2] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[3] = output.m_Values[constant.m_IndexArray[currentCurveIndex]]; else value[3] = 1.0f; currentCurveIndex++;
+
+ ret.q = math::normalize(math::load(value));
+
+ ret.s = math::float4::one();
+
+ return ret;
+ }
+
+ template<typename TYPE>
+ struct ValueAccessor
+ {
+ ValueAccessor(const TYPE* t) : m_Values(t)
+ {}
+
+ const TYPE* m_Values;
+
+ float operator[](int index)
+ {
+ return m_Values[index];
+ }
+ };
+
+ template<>
+ struct ValueAccessor<ValueDelta>
+ {
+ ValueAccessor(const ValueDelta* t) : m_Values(t)
+ {}
+
+ const ValueDelta* m_Values;
+
+ float operator[](int index)
+ {
+ return m_Values[index].m_Start;
+ }
+ };
+
+ template<typename TYPE>
+ void GetHumanPose(const ClipMuscleConstant& constant, const TYPE* values, human::HumanPose &humanPose)
+ {
+ float ATTRIBUTE_ALIGN(ALIGN4F) value[4];
+
+ ValueAccessor<TYPE> accessor(values);
+
+ uint32_t i,currentCurveIndex = s_ClipMotionCurveCount;
+
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[0] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[0] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[1] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[1] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[2] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[2] = 0; currentCurveIndex++;
+ value[3] = 0;
+
+ humanPose.m_RootX.t = math::load(value);
+
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[0] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[0] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[1] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[1] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[2] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[2] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[3] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[3] = 1.0f; currentCurveIndex++;
+
+ humanPose.m_RootX.q = math::normalize(math::load(value));
+ humanPose.m_RootX.s = math::float4::one();
+
+ for(i = 0; i < human::kLastGoal; i++)
+ {
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[0] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[0] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[1] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[1] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[2] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[2] = 0; currentCurveIndex++;
+ value[3] = 0;
+
+ humanPose.m_GoalArray[i].m_X.t = math::load(value);
+
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[0] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[0] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[1] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[1] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[2] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[2] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[3] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[3] = 1.0f; currentCurveIndex++;
+
+
+ humanPose.m_GoalArray[i].m_X.q = math::load(value);
+ humanPose.m_GoalArray[i].m_X.q = math::normalize(humanPose.m_GoalArray[i].m_X.q);
+ }
+
+ for(i = 0 ; i < human::kLastDoF; i++)
+ {
+ if(constant.m_IndexArray[currentCurveIndex] != -1) humanPose.m_DoFArray[i] = accessor[constant.m_IndexArray[currentCurveIndex]];
+ else humanPose.m_DoFArray[i] = 0;
+ currentCurveIndex++;
+ }
+
+ for(i = 0 ; i < hand::s_DoFCount; i++)
+ {
+ if(constant.m_IndexArray[currentCurveIndex] != -1) humanPose.m_LeftHandPose.m_DoFArray[i] = accessor[constant.m_IndexArray[currentCurveIndex]];
+ else humanPose.m_LeftHandPose.m_DoFArray[i] = 0;
+ currentCurveIndex++;
+ }
+
+ for(i = 0 ; i < hand::s_DoFCount; i++)
+ {
+ if(constant.m_IndexArray[currentCurveIndex] != -1) humanPose.m_RightHandPose.m_DoFArray[i] = accessor[constant.m_IndexArray[currentCurveIndex]];
+ else humanPose.m_RightHandPose.m_DoFArray[i] = 0;
+ currentCurveIndex++;
+ }
+ }
+
+ void GetHumanPose(const ClipMuscleConstant& constant, const float* values, human::HumanPose &humanPose)
+ {
+ GetHumanPose<float>(constant, values, humanPose);
+ }
+
+ void GetHumanPose(const ClipMuscleConstant& constant, const ValueDelta* values, human::HumanPose &humanPose)
+ {
+ GetHumanPose<ValueDelta>(constant, values, humanPose);
+ }
+
+
+ // root motion extraction WIP
+ void EvaluateClipRootMotionDeltaX(const ClipMuscleConstant& constant, const ClipMuscleInput &input, MotionOutput &output, ClipMemory &memory)
+ {
+ output.m_DX = math::xformIdentity();
+ output.m_MotionX = math::xformIdentity();
+
+ if(constant.m_IndexArray[0] != -1)
+ {
+ float cycleOffset = constant.m_CycleOffset + input.m_CycleOffset;
+
+ float prevTime;
+ float prevTimeFrac = math::cond(constant.m_LoopTime,math::modf(input.m_PreviousTime+cycleOffset,prevTime),math::saturate(input.m_PreviousTime));
+ prevTimeFrac = math::cond(!input.m_Reverse, prevTimeFrac, 1.f - prevTimeFrac);
+ prevTime = constant.m_StartTime+prevTimeFrac*(constant.m_StopTime-constant.m_StartTime);
+
+ float currentTime;
+ float lTimeFrac = math::cond(constant.m_LoopTime,math::modf(input.m_Time+cycleOffset,currentTime),math::saturate(input.m_Time));
+ lTimeFrac = math::cond(!input.m_Reverse, lTimeFrac, 1.f - lTimeFrac);
+ currentTime = constant.m_StartTime+lTimeFrac*(constant.m_StopTime-constant.m_StartTime);
+
+ math::xform refStartX(constant.m_MotionStartX);
+ math::xform refStopX(constant.m_MotionStopX);
+ math::xform prevRefX(EvaluateMotion(constant,memory,prevTime));
+ math::xform refX(EvaluateMotion(constant,memory,currentTime));
+
+ refStartX.q = math::quatProjOnYPlane(refStartX.q);
+ refStopX.q = math::quatProjOnYPlane(refStopX.q);
+ prevRefX.q = math::quatProjOnYPlane(prevRefX.q);
+ refX.q = math::quatProjOnYPlane(refX.q);
+
+ math::float4 refOffsetT = math::float4(0,constant.m_Level,0,0);
+ math::float4 refOffsetQ = math::qtan2Quat(math::float4(0,math::halfTan(math::radians(constant.m_OrientationOffsetY)),0,1));
+
+ if(constant.m_KeepOriginalPositionY)
+ {
+ refOffsetT.y() -= refStartX.t.y();
+ }
+
+ if(constant.m_KeepOriginalPositionXZ)
+ {
+ refOffsetT.x() -= refStartX.t.x();
+ refOffsetT.z() -= refStartX.t.z();
+ }
+
+ if(constant.m_KeepOriginalOrientation)
+ {
+ refOffsetQ = math::normalize(math::quatMul(refOffsetQ,math::quatConj(refStartX.q)));
+ }
+
+ refStartX.t = refStartX.t + refOffsetT;
+ refStopX.t = refStopX.t + refOffsetT;
+ prevRefX.t = prevRefX.t + refOffsetT;
+ refX.t = refX.t + refOffsetT;
+
+ refStartX.q = math::normalize(math::quatMul(refOffsetQ,refStartX.q));
+ refStopX.q = math::normalize(math::quatMul(refOffsetQ,refStopX.q));
+ prevRefX.q = math::normalize(math::quatMul(refOffsetQ,prevRefX.q));
+ refX.q = math::normalize(math::quatMul(refOffsetQ,refX.q));
+
+ if(constant.m_LoopBlendOrientation)
+ {
+ refStopX.q = refStartX.q;
+ prevRefX.q = refStartX.q;
+ refX.q = refStartX.q;
+ }
+
+ if(constant.m_LoopBlendPositionY)
+ {
+ refStopX.t.y() = refStartX.t.y();
+ prevRefX.t.y() = refStartX.t.y();
+ refX.t.y() = refStartX.t.y();
+ }
+
+ if(constant.m_LoopBlendPositionXZ)
+ {
+ refStartX.t.x() = refStartX.t.x();
+ refStopX.t.x() = refStartX.t.x();;
+ prevRefX.t.x() = refStartX.t.x();;
+ refX.t.x() = refStartX.t.x();;
+
+ refStartX.t.z() = refStartX.t.z();
+ refStopX.t.z() = refStartX.t.z();;
+ prevRefX.t.z() = refStartX.t.z();;
+ refX.t.z() = refStartX.t.z();;
+ }
+
+ output.m_MotionX = refX;
+ output.m_MotionStartX = refStartX;
+ output.m_MotionStopX = refStopX;
+
+ if(constant.m_LoopTime)
+ {
+ float deltaTime = math::abs(lTimeFrac - prevTimeFrac);
+
+ if(deltaTime > 0.5f)
+ {
+ if(lTimeFrac < prevTimeFrac)
+ {
+ output.m_DX = math::xformInvMulNS(prevRefX,math::xformMul(refStopX,math::xformInvMulNS(refStartX,refX)));
+ }
+ else if(lTimeFrac > prevTimeFrac)
+ {
+ output.m_DX = math::xformInvMulNS(prevRefX,math::xformMul(refStartX,math::xformInvMulNS(refStopX,refX)));
+ }
+ }
+ else if(lTimeFrac != prevTimeFrac)
+ {
+ output.m_DX = math::xformInvMulNS(prevRefX,refX);
+ }
+ }
+ else
+ {
+ if(input.m_PreviousTime != input.m_Time)
+ {
+ output.m_DX = math::xformInvMulNS(prevRefX,refX);
+ }
+ }
+
+ output.m_DX.q = math::quat2Qtan(output.m_DX.q);
+
+ output.m_DX.q.x() = 0;
+ output.m_DX.q.z() = 0;
+
+ if(constant.m_LoopBlendPositionY)
+ {
+ output.m_DX.t.y() = 0;
+ }
+
+ if(constant.m_LoopBlendPositionXZ)
+ {
+ output.m_DX.t.x() = 0;
+ output.m_DX.t.z() = 0;
+ }
+
+ if(constant.m_LoopBlendOrientation)
+ {
+ output.m_DX.q.y() = 0;
+ }
+
+ output.m_DX.q = math::qtan2Quat(output.m_DX.q);
+ }
+ }
+
+ void EvaluateClipMusclePrevTime(const ClipMuscleConstant& constant, const ClipMuscleInput &input, MotionOutput &output, ClipMemory &memory)
+ {
+ float prevTimeInt;
+ float prevTimeFrac;
+ float prevTime = ComputeClipTime(input.m_PreviousTime,constant.m_StartTime,constant.m_StopTime,constant.m_CycleOffset+input.m_CycleOffset,constant.m_LoopTime,input.m_Reverse,prevTimeFrac,prevTimeInt);
+
+ output.m_PrevRootX = EvaluateRoot(constant,memory,prevTime);
+ output.m_PrevLeftFootX = EvaluateGoal(constant,memory,prevTime,human::kLeftFootGoal);
+ output.m_PrevRightFootX = EvaluateGoal(constant,memory,prevTime,human::kRightFootGoal);
+ }
+
+ void EvaluateClipMuscle(const ClipMuscleConstant& constant, const ClipMuscleInput &input, const float *valuesOutput, MotionOutput &motionOutput, human::HumanPose &humanPose, ClipMemory &memory)
+ {
+ float cycleOffset = constant.m_CycleOffset+input.m_CycleOffset;
+ float prevTimeInt;
+ float prevTimeFrac;
+ ComputeClipTime(input.m_PreviousTime,constant.m_StartTime,constant.m_StopTime,cycleOffset,constant.m_LoopTime,input.m_Reverse,prevTimeFrac,prevTimeInt);
+
+ ClipInput in;
+ float timeFrac;
+ float currentTimeInt;
+ in.m_Time = ComputeClipTime(input.m_Time,constant.m_StartTime,constant.m_StopTime,cycleOffset,constant.m_LoopTime,input.m_Reverse,timeFrac,currentTimeInt);
+
+ bool mirror = (constant.m_Mirror || input.m_Mirror) && !(constant.m_Mirror && input.m_Mirror);
+
+ math::xform stopX = math::xformMulInv(constant.m_StartX,constant.m_DeltaPose.m_RootX);
+
+ math::xform prevRootX = motionOutput.m_PrevRootX ;
+ math::xform prevLeftFootX = motionOutput.m_PrevLeftFootX;
+ math::xform prevRightFootX = motionOutput.m_PrevRightFootX;
+
+ GetHumanPose(constant,valuesOutput,humanPose);
+ math::xform rootX = humanPose.m_RootX;
+
+ math::xform refStartX(constant.m_StartX.t,math::quatProjOnYPlane(constant.m_StartX.q),constant.m_StartX.s);
+ math::xform refStopX(stopX.t,math::quatProjOnYPlane(stopX.q),stopX.s);
+ math::xform prevRefX(prevRootX.t,math::quatProjOnYPlane(prevRootX.q),prevRootX.s);
+ math::xform refX(rootX.t,math::quatProjOnYPlane(rootX.q),rootX.s);
+
+ // todo: target stuff most likely breaks curve cache
+ float targetTime = constant.m_StopTime;
+ float targetTimeFrac = 1;
+ math::xform targetRootX = stopX;
+ math::xform targetRefX = refStopX;
+ math::xform targetGoalX = math::xformIdentity();
+
+ float targetTimeInt = 0;
+ if(input.m_TargetTime != 1)
+ {
+ targetTime = ComputeClipTime(input.m_TargetTime,constant.m_StartTime,constant.m_StopTime,cycleOffset,constant.m_LoopTime,input.m_Reverse,targetTimeFrac,targetTimeInt);
+
+ targetRootX = EvaluateRoot(constant,memory,targetTime);
+ targetRefX = math::xform(targetRootX.t,math::quatProjOnYPlane(targetRootX.q),targetRootX.s);
+ }
+
+ int32_t targetGoalIndex = input.m_TargetIndex - kTargetLeftFoot;
+
+ if(targetGoalIndex >= 0)
+ {
+ targetGoalIndex = mirror ? ((targetGoalIndex % 2) ? targetGoalIndex-1 : targetGoalIndex+1) : targetGoalIndex;
+ targetGoalX = EvaluateGoal(constant,memory,targetTime,targetGoalIndex);
+ }
+ //////////////////////////////////////////
+
+ if(constant.m_HeightFromFeet)
+ {
+ math::xform refLeftFootStartX = math::xformMul(constant.m_StartX,constant.m_LeftFootStartX);
+ math::xform refRightFootStartX = math::xformMul(constant.m_StartX,constant.m_RightFootStartX);
+
+ math::xform refLeftFootStopX = math::xformMul(stopX,math::xformMulInv(constant.m_LeftFootStartX,constant.m_DeltaPose.m_GoalArray[human::kLeftFootGoal].m_X));
+ math::xform refRightFootStopX = math::xformMul(stopX,math::xformMulInv(constant.m_RightFootStartX,constant.m_DeltaPose.m_GoalArray[human::kRightFootGoal].m_X));
+
+ math::xform refLeftFootPrevX = math::xformMul(prevRootX,prevLeftFootX);
+ math::xform refRightFootPrevX = math::xformMul(prevRootX,prevRightFootX);
+
+ math::xform refLeftFootX = math::xformMul(rootX,humanPose.m_GoalArray[human::kLeftFootGoal].m_X);
+ math::xform refRightFootX = math::xformMul(rootX,humanPose.m_GoalArray[human::kRightFootGoal].m_X);
+
+ math::xform refTargetLeftFootX = math::xformMul(targetRootX,EvaluateGoal(constant,memory,targetTime,human::kLeftFootGoal));
+ math::xform refTargetRightFootX = math::xformMul(targetRootX,EvaluateGoal(constant,memory,targetTime,human::kRightFootGoal));
+
+ refStartX.t.y() = math::minimum(refStartX.t,math::minimum(refLeftFootStartX.t,refRightFootStartX.t)).y();
+ refStopX.t.y() = math::minimum(refStopX.t,math::minimum(refLeftFootStopX.t,refRightFootStopX.t)).y();
+ prevRefX.t.y() = math::minimum(prevRefX.t,math::minimum(refLeftFootPrevX.t,refRightFootPrevX.t)).y();
+ refX.t.y() = math::minimum(refX.t,math::minimum(refLeftFootX.t,refRightFootX.t)).y();
+ targetRefX.t.y() = math::minimum(targetRefX.t,math::minimum(refTargetLeftFootX.t,refTargetRightFootX.t)).y();
+ }
+
+ math::float4 refOffsetT = math::float4(0,constant.m_Level,0,0);
+ math::float4 refOffsetQ = math::qtan2Quat(math::float4(0,math::halfTan(math::radians(constant.m_OrientationOffsetY)),0,1));
+
+ if(constant.m_KeepOriginalPositionY)
+ {
+ refOffsetT.y() -= refStartX.t.y();
+ }
+
+ if(constant.m_KeepOriginalPositionXZ)
+ {
+ refOffsetT.x() -= refStartX.t.x();
+ refOffsetT.z() -= refStartX.t.z();
+ }
+
+ if(constant.m_KeepOriginalOrientation)
+ {
+ refOffsetQ = math::normalize(math::quatMul(refOffsetQ,math::quatConj(refStartX.q)));
+ }
+
+ refStartX.t = refStartX.t + refOffsetT;
+ refStopX.t = refStopX.t + refOffsetT;
+ prevRefX.t = prevRefX.t + refOffsetT;
+ refX.t = refX.t + refOffsetT;
+ targetRefX.t = targetRefX.t + refOffsetT;
+
+ refStartX.q = math::normalize(math::quatMul(refOffsetQ,refStartX.q));
+ refStopX.q = math::normalize(math::quatMul(refOffsetQ,refStopX.q));
+ prevRefX.q = math::normalize(math::quatMul(refOffsetQ,prevRefX.q));
+ refX.q = math::normalize(math::quatMul(refOffsetQ,refX.q));
+ targetRefX.q = math::normalize(math::quatMul(refOffsetQ,targetRefX.q));
+
+ if(constant.m_LoopBlendOrientation)
+ {
+ refStopX.q = refStartX.q;
+ prevRefX.q = refStartX.q;
+ refX.q = refStartX.q;
+ targetRefX.q = refStartX.q;
+ }
+
+ if(constant.m_LoopBlendPositionY)
+ {
+ refStopX.t.y() = refStartX.t.y();
+ prevRefX.t.y() = refStartX.t.y();
+ refX.t.y() = refStartX.t.y();
+ targetRefX.t.y() = refStartX.t.y();
+ }
+
+ if(constant.m_LoopBlendPositionXZ)
+ {
+ refStartX.t.x() = refStartX.t.x();
+ refStopX.t.x() = refStartX.t.x();;
+ prevRefX.t.x() = refStartX.t.x();;
+ refX.t.x() = refStartX.t.x();;
+ targetRefX.t.x() = refStartX.t.x();;
+
+ refStartX.t.z() = refStartX.t.z();
+ refStopX.t.z() = refStartX.t.z();;
+ prevRefX.t.z() = refStartX.t.z();;
+ refX.t.z() = refStartX.t.z();;
+ targetRefX.t.z() = refStartX.t.z();;
+ }
+
+ prevRootX = math::xformInvMulNS(prevRefX,prevRootX);
+ rootX = math::xformInvMulNS(refX,rootX);
+ targetRootX = math::xformInvMulNS(targetRefX,targetRootX);
+
+ if(constant.m_LoopTime && constant.m_LoopBlend)
+ {
+ human::HumanPose deltaPose;
+ human::HumanPoseWeight(deltaPose,constant.m_DeltaPose,timeFrac);
+ human::HumanPoseAdd(humanPose,humanPose,deltaPose);
+
+ prevLeftFootX = math::xformMul(prevLeftFootX,math::xformWeight(constant.m_DeltaPose.m_GoalArray[human::kLeftFootGoal].m_X,math::float1(prevTimeFrac)));
+ prevRightFootX = math::xformMul(prevRightFootX,math::xformWeight(constant.m_DeltaPose.m_GoalArray[human::kRightFootGoal].m_X,math::float1(prevTimeFrac)));
+
+ math::xform rootDeltaX = math::xformInvMulNS(math::xformInvMulNS(refStopX,stopX),math::xformInvMulNS(refStartX,constant.m_StartX));
+ math::xform prevRootDeltaX = math::xformWeight(rootDeltaX,math::float1(prevTimeFrac));
+ math::xform targetRootDeltaX = math::xformWeight(rootDeltaX,math::float1(targetTimeFrac));
+ rootDeltaX = math::xformWeight(rootDeltaX,math::float1(timeFrac));
+ prevRootX = math::xformMul(prevRootX,prevRootDeltaX);
+ rootX = math::xformMul(rootX,rootDeltaX);
+ targetRootX = math::xformMul(targetRootX,targetRootDeltaX);
+
+ math::xform cycleDelta;
+ for(int i = 0 ; i < targetTimeInt - currentTimeInt; i++)
+ {
+ if(i == 0) cycleDelta = math::xformInvMulNS(refStartX,refStopX);
+ targetRefX = math::xformMul(targetRefX,cycleDelta);
+ }
+ if(targetGoalIndex >= 0) targetGoalX = math::xformMul(targetGoalX,math::xformWeight(constant.m_DeltaPose.m_GoalArray[targetGoalIndex].m_X,math::float1(targetTimeFrac)));
+ }
+
+ humanPose.m_RootX = rootX;
+
+ for(int i = 0; i < human::kLastGoal; i++)
+ {
+ humanPose.m_GoalArray[i].m_X = math::xformMul(rootX,humanPose.m_GoalArray[i].m_X);
+ }
+
+ motionOutput.m_PrevLeftFootX = math::xformMul(prevRootX,prevLeftFootX);
+ motionOutput.m_PrevRightFootX = math::xformMul(prevRootX,prevRightFootX);
+
+ motionOutput.m_DX = math::xformIdentity();
+ motionOutput.m_MotionX = math::xformIdentity();
+
+ if(constant.m_LoopTime)
+ {
+ float deltaTime = math::abs(timeFrac - prevTimeFrac);
+
+ if(deltaTime > 0.5f)
+ {
+ if(timeFrac < prevTimeFrac)
+ {
+ motionOutput.m_DX = math::xformInvMulNS(prevRefX,math::xformMul(refStopX,math::xformInvMulNS(refStartX,refX)));
+ }
+ else if(timeFrac > prevTimeFrac)
+ {
+ motionOutput.m_DX = math::xformInvMulNS(prevRefX,math::xformMul(refStartX,math::xformInvMulNS(refStopX,refX)));
+ }
+ }
+ else if(timeFrac != prevTimeFrac)
+ {
+ motionOutput.m_DX = math::xformInvMulNS(prevRefX,refX);
+ }
+ }
+ else
+ {
+ if(input.m_PreviousTime != input.m_Time)
+ {
+ motionOutput.m_DX = math::xformInvMulNS(prevRefX,refX);
+ }
+ }
+
+ motionOutput.m_DX.q = math::quat2Qtan(motionOutput.m_DX.q);
+
+ motionOutput.m_DX.q.x() = 0;
+ motionOutput.m_DX.q.z() = 0;
+
+ if(constant.m_LoopBlendPositionY)
+ {
+ motionOutput.m_DX.t.y() = 0;
+ }
+
+ if(constant.m_LoopBlendPositionXZ)
+ {
+ motionOutput.m_DX.t.x() = 0;
+ motionOutput.m_DX.t.z() = 0;
+ }
+
+ if(constant.m_LoopBlendOrientation)
+ {
+ motionOutput.m_DX.q.y() = 0;
+ }
+
+ motionOutput.m_DX.q = math::qtan2Quat(motionOutput.m_DX.q);
+ motionOutput.m_DX.s = math::float4::one();
+ motionOutput.m_TargetX = math::xformInvMulNS(refX,targetRefX);
+
+ if(mirror)
+ {
+ human::HumanPoseMirror(humanPose,humanPose);
+
+ motionOutput.m_DX = math::mirror(motionOutput.m_DX);
+
+ math::xform x = math::mirror(motionOutput.m_PrevLeftFootX);
+ motionOutput.m_PrevLeftFootX = math::mirror(motionOutput.m_PrevRightFootX);
+ motionOutput.m_PrevRightFootX = x;
+
+ constant_float4(offsetQY,0,1,0,0);
+ constant_float4(offsetQZ,0,0,1,0);
+
+ motionOutput.m_PrevLeftFootX.q = math::normalize(math::quatMul(motionOutput.m_PrevLeftFootX.q,offsetQY));
+ motionOutput.m_PrevRightFootX.q = math::normalize(math::quatMul(motionOutput.m_PrevRightFootX.q,offsetQY));
+
+ motionOutput.m_TargetX = math::mirror(motionOutput.m_TargetX);
+
+ if(input.m_TargetIndex > kTargetReference)
+ {
+ targetRootX = math::mirror(targetRootX);
+
+ if(input.m_TargetIndex > kTargetRoot)
+ {
+ targetGoalX = math::mirror(targetGoalX);
+ targetGoalX.q = math::normalize(math::quatMul(targetGoalX.q,math::cond(math::bool4(input.m_TargetIndex > kTargetRightFoot),offsetQZ,offsetQY)));
+ }
+ }
+ }
+
+ if(input.m_TargetIndex > kTargetReference)
+ {
+ motionOutput.m_TargetX = math::xformMul(motionOutput.m_TargetX,targetRootX);
+
+ if(input.m_TargetIndex > kTargetRoot)
+ {
+ motionOutput.m_TargetX = math::xformMul(motionOutput.m_TargetX,targetGoalX);
+ }
+ }
+
+ motionOutput.m_MotionX = refX;
+ }
+
+ void ComputeClipMuscleDeltaPose(ClipMuscleConstant const& constant, float startTime, float stopTime, human::HumanPose &deltaPose, math::xform &startX, math::xform &leftFootStartX, math::xform &rightFootStartX, memory::Allocator& alloc)
+ {
+ ClipInput in;
+ human::HumanPose poseStart;
+ human::HumanPose poseStop;
+
+ ClipOutput *clipOutStart = CreateClipOutput(constant.m_Clip.Get(),alloc);
+ ClipOutput *clipOutStop = CreateClipOutput(constant.m_Clip.Get(),alloc);
+ ClipMemory *mem = CreateClipMemory(constant.m_Clip.Get(), alloc);
+
+ in.m_Time = startTime;
+ EvaluateClip(constant.m_Clip.Get(),&in,mem,clipOutStart);
+ GetHumanPose(constant,clipOutStart->m_Values,poseStart);
+
+ in.m_Time = stopTime;
+ EvaluateClip(constant.m_Clip.Get(),&in,mem,clipOutStop);
+ GetHumanPose(constant,clipOutStop->m_Values,poseStop);
+
+ human::HumanPoseSub(deltaPose,poseStart,poseStop);
+
+ startX = poseStart.m_RootX;
+ leftFootStartX = poseStart.m_GoalArray[human::kLeftFootGoal].m_X;
+ rightFootStartX = poseStart.m_GoalArray[human::kRightFootGoal].m_X;
+
+ DestroyClipOutput(clipOutStart,alloc);
+ DestroyClipOutput(clipOutStop,alloc);
+ DestroyClipMemory(mem,alloc);
+ }
+
+ void InitClipMuscleDeltaValues(ClipMuscleConstant& constant)
+ {
+ mecanim::memory::MecanimAllocator alloc(kMemTempAlloc);
+
+ ClipInput in;
+ ClipOutput *outStart = CreateClipOutput(constant.m_Clip.Get(),alloc);
+ ClipOutput *outStop = CreateClipOutput(constant.m_Clip.Get(),alloc);
+ ClipMemory *mem = CreateClipMemory(constant.m_Clip.Get(), alloc);
+
+ in.m_Time = constant.m_StartTime;
+ EvaluateClip(constant.m_Clip.Get(),&in,mem,outStart);
+
+ in.m_Time = constant.m_StopTime;
+ EvaluateClip(constant.m_Clip.Get(),&in,mem,outStop);
+
+ constant.m_MotionStartX = GetMotionX(constant,*outStart);
+ constant.m_MotionStopX = GetMotionX(constant,*outStop);
+
+ for(int valueIter = 0; valueIter < constant.m_ValueArrayCount; valueIter++)
+ {
+ constant.m_ValueArrayDelta[valueIter].m_Start = outStart->m_Values[valueIter];
+ constant.m_ValueArrayDelta[valueIter].m_Stop = outStop->m_Values[valueIter];
+ }
+
+ DestroyClipOutput(outStart,alloc);
+ DestroyClipOutput(outStop,alloc);
+ DestroyClipMemory(mem,alloc);
+ }
+
+ void InitClipMuscleDeltaPose(ClipMuscleConstant& constant)
+ {
+ mecanim::memory::MecanimAllocator alloc(kMemTempAlloc);
+ ComputeClipMuscleDeltaPose(constant,constant.m_StartTime,constant.m_StopTime,constant.m_DeltaPose,constant.m_StartX,constant.m_LeftFootStartX,constant.m_RightFootStartX,alloc);
+ }
+
+ void InitClipMuscleAverageSpeed(ClipMuscleConstant& constant, int steps)
+ {
+ mecanim::memory::MecanimAllocator alloc(kMemTempAlloc);
+
+ ClipMemory *mem = CreateClipMemory(constant.m_Clip.Get(), alloc);
+
+ float period = (constant.m_StopTime - constant.m_StartTime) / float(steps);
+ float time = constant.m_StartTime;
+
+ math::xform prevRootX;
+
+ math::float4 speed = math::float4::zero();
+ float angularSpeed = 0;
+
+ for(int i = 0; i <= steps; i++)
+ {
+ math::xform rootX = EvaluateRoot(constant,*mem,time);
+
+ math::float4 qYOffset = math::qtan2Quat(math::float4(0,math::halfTan(math::radians(constant.m_OrientationOffsetY)),0,1));
+
+ if(constant.m_KeepOriginalOrientation)
+ {
+ qYOffset = math::normalize(math::quatMul(qYOffset,math::quatConj(constant.m_StartX.q)));
+ }
+
+ rootX.q = math::normalize(math::quatMul(qYOffset,math::quatProjOnYPlane(math::cond(math::bool4(constant.m_LoopBlendOrientation),constant.m_StartX.q,rootX.q))));
+
+ if(i > 0)
+ {
+ math::xform dx = math::xformInvMul(prevRootX,rootX);
+ if(constant.m_Mirror) dx = math::mirror(dx);
+ math::float4 dxdof = math::doubleAtan(math::quat2Qtan(dx.q));
+ angularSpeed += dxdof.y().tofloat() / period;
+ speed = speed + dx.t / math::float1(period);
+ }
+
+ prevRootX = rootX;
+ time += period;
+ }
+
+ constant.m_AverageAngularSpeed = angularSpeed / float(steps);
+
+ constant.m_AverageSpeed = speed / math::float1(steps);
+ constant.m_AverageSpeed = math::cond(math::bool4(constant.m_LoopBlendPositionXZ,constant.m_LoopBlendPositionY,constant.m_LoopBlendPositionXZ,true),math::float4::zero(),constant.m_AverageSpeed);
+
+ DestroyClipMemory(mem,alloc);
+ }
+
+ size_t GetClipCurveCount(const ClipMuscleConstant& constant)
+ {
+ return GetClipCurveCount(*constant.m_Clip);
+ }
+}
+
+}
diff --git a/Runtime/mecanim/animation/clipmuscle.h b/Runtime/mecanim/animation/clipmuscle.h
new file mode 100644
index 0000000..e2bb685
--- /dev/null
+++ b/Runtime/mecanim/animation/clipmuscle.h
@@ -0,0 +1,264 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/Math/Simd/float4.h"
+#include "Runtime/mecanim/animation/curvedata.h"
+#include "Runtime/mecanim/human/human.h"
+#include "Runtime/mecanim/human/hand.h"
+
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Animation/MecanimArraySerialization.h"
+
+namespace mecanim
+{
+
+namespace animation
+{
+ const int32_t s_ClipMuscleCurveTQCount = 7;
+ const int32_t s_ClipMuscleDoFBegin = (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount;
+ const int32_t s_ClipMotionCurveCount = 7;
+ const int32_t s_ClipMuscleCurveCount = s_ClipMotionCurveCount + (1 + human::kLastGoal) * 7 + human::kLastDoF + 2 * hand::s_DoFCount;
+
+ int32_t FindMuscleIndex(uint32_t id);
+ mecanim::String const& GetMuscleCurveName(int32_t curveIndex);
+ float GetXformCurveValue(math::xform const& x, int32_t index);
+ float GetMuscleCurveValue(human::HumanPose const& pose, math::xform const &motionX, int32_t curveIndex);
+ bool GetMuscleCurveInMask(human::HumanPoseMask const& mask, int32_t curveIndex);
+ int32_t GetMuscleCurveTQIndex(int32_t curveIndex);
+
+ void InitializeMuscleClipTables ();
+
+ enum { kTargetReference, kTargetRoot, kTargetLeftFoot, kTargetRightFoot, kTargetLeftHand, kTargetRightHand };
+
+ struct ValueDelta
+ {
+ DEFINE_GET_TYPESTRING(ValueDelta)
+
+ float m_Start;
+ float m_Stop;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_Start);
+ TRANSFER(m_Stop);
+ }
+ };
+
+ // Constant
+ // @TODO: create a smaller constant to use with non humanoid clips. There is too much overhead here
+ struct ClipMuscleConstant
+ {
+ DEFINE_GET_TYPESTRING(ClipMuscleConstant)
+
+ ClipMuscleConstant() :
+ m_StartX(math::xformIdentity()),
+ m_LeftFootStartX(math::xformIdentity()),
+ m_RightFootStartX(math::xformIdentity()),
+ m_MotionStartX(math::xformIdentity()),
+ m_MotionStopX(math::xformIdentity()),
+ m_ValueArrayCount(0),
+ m_AverageSpeed(math::float4::zero()),
+ m_Mirror(false),
+ m_StartTime(0),
+ m_StopTime(1),
+ m_LoopTime(false),
+ m_LoopBlend(false),
+ m_LoopBlendOrientation(false),
+ m_LoopBlendPositionXZ(false),
+ m_LoopBlendPositionY(false),
+ m_KeepOriginalOrientation(false),
+ m_KeepOriginalPositionY(true),
+ m_KeepOriginalPositionXZ(false),
+ m_HeightFromFeet(false),
+ m_OrientationOffsetY(0),
+ m_Level(0),
+ m_CycleOffset(0),
+ m_AverageAngularSpeed(0)
+ {
+ int32_t i;
+ for(i = 0; i < s_ClipMuscleCurveCount; i++)
+ {
+ m_IndexArray[i] = -1;
+ }
+ }
+
+ human::HumanPose m_DeltaPose;
+
+ math::xform m_StartX;
+ math::xform m_LeftFootStartX;
+ math::xform m_RightFootStartX;
+
+ math::xform m_MotionStartX;
+ math::xform m_MotionStopX;
+
+ math::float4 m_AverageSpeed;
+
+ OffsetPtr<Clip> m_Clip;
+
+ float m_StartTime;
+ float m_StopTime;
+ float m_OrientationOffsetY;
+ float m_Level;
+ float m_CycleOffset;
+ float m_AverageAngularSpeed;
+
+ int32_t m_IndexArray[s_ClipMuscleCurveCount];
+
+ uint32_t m_ValueArrayCount;
+ OffsetPtr<ValueDelta> m_ValueArrayDelta;
+
+ bool m_Mirror;
+ bool m_LoopTime;
+ bool m_LoopBlend;
+ bool m_LoopBlendOrientation;
+ bool m_LoopBlendPositionY;
+ bool m_LoopBlendPositionXZ;
+
+ bool m_KeepOriginalOrientation;
+ bool m_KeepOriginalPositionY;
+ bool m_KeepOriginalPositionXZ;
+ bool m_HeightFromFeet;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ transfer.SetVersion(2);
+
+ TRANSFER(m_DeltaPose);
+ TRANSFER(m_StartX);
+ TRANSFER(m_LeftFootStartX);
+ TRANSFER(m_RightFootStartX);
+ TRANSFER(m_MotionStartX);
+ TRANSFER(m_MotionStopX);
+ TRANSFER(m_AverageSpeed);
+ TRANSFER(m_Clip);
+ TRANSFER(m_StartTime);
+ TRANSFER(m_StopTime);
+ TRANSFER(m_OrientationOffsetY);
+ TRANSFER(m_Level);
+ TRANSFER(m_CycleOffset);
+
+ TRANSFER(m_AverageAngularSpeed);
+
+ STATIC_ARRAY_TRANSFER(mecanim::int32_t, m_IndexArray, s_ClipMuscleCurveCount);
+
+ TRANSFER_BLOB_ONLY(m_ValueArrayCount);
+ MANUAL_ARRAY_TRANSFER2(ValueDelta, m_ValueArrayDelta, m_ValueArrayCount);
+
+ TRANSFER(m_Mirror);
+
+ TRANSFER(m_LoopTime);
+ TRANSFER(m_LoopBlend);
+
+ m_LoopTime = transfer.IsVersionSmallerOrEqual(1) ? m_LoopBlend : m_LoopTime;
+
+ TRANSFER(m_LoopBlendOrientation);
+ TRANSFER(m_LoopBlendPositionY);
+ TRANSFER(m_LoopBlendPositionXZ);
+
+ TRANSFER(m_KeepOriginalOrientation);
+ TRANSFER(m_KeepOriginalPositionY);
+ TRANSFER(m_KeepOriginalPositionXZ);
+ TRANSFER(m_HeightFromFeet);
+
+ transfer.Align();
+ }
+ };
+
+ // Input
+ struct ClipMuscleInput
+ {
+ ClipMuscleInput() : m_Time(0),
+ m_PreviousTime(0),
+ m_TargetIndex(kTargetReference),
+ m_TargetTime(1), m_Reverse(false), m_Mirror(false), m_CycleOffset(0){}
+
+ float m_Time;
+ float m_PreviousTime;
+
+ int32_t m_TargetIndex;
+ float m_TargetTime;
+ bool m_Reverse;
+ bool m_Mirror;
+ float m_CycleOffset;
+ };
+
+ // Output
+ struct MotionOutput
+ {
+ DEFINE_GET_TYPESTRING(MotionOutput)
+
+ MotionOutput() : m_DX(math::xformIdentity()),
+ m_MotionX(math::xformIdentity()),
+ m_MotionStartX(math::xformIdentity()),
+ m_MotionStopX(math::xformIdentity()),
+ m_PrevRootX(math::xformIdentity()),
+ m_PrevLeftFootX(math::xformIdentity()),
+ m_PrevRightFootX(math::xformIdentity()),
+ m_TargetX(math::xformIdentity()),
+ m_GravityWeight(0) {}
+
+ math::xform m_DX;
+ math::xform m_MotionX;
+ math::xform m_MotionStartX;
+ math::xform m_MotionStopX;
+
+ math::xform m_PrevRootX;
+ math::xform m_PrevLeftFootX;
+ math::xform m_PrevRightFootX;
+
+ math::xform m_TargetX;
+
+ float m_GravityWeight;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_DX);
+ TRANSFER(m_MotionX);
+ TRANSFER(m_MotionStartX);
+ TRANSFER(m_MotionStopX);
+ TRANSFER(m_PrevRootX);
+ TRANSFER(m_PrevLeftFootX);
+ TRANSFER(m_PrevRightFootX);
+ TRANSFER(m_TargetX);
+ TRANSFER(m_GravityWeight);
+ };
+ };
+
+ void MotionOutputClear(MotionOutput *output);
+ void MotionOutputCopy(MotionOutput *output, MotionOutput const *motion, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &poseMask);
+ void MotionOutputBlend(MotionOutput *output, MotionOutput **outputArray, float *weigh, uint32_t count, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &poseMask);
+ void MotionAddAdditiveLayer(MotionOutput *output, MotionOutput const *motion, float weight, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &poseMask);
+ void MotionAddOverrideLayer(MotionOutput *output, MotionOutput const *motion, float weight, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &poseMask);
+
+ ClipMuscleConstant* CreateClipMuscleConstant(Clip * clip, memory::Allocator& alloc);
+ void DestroyClipMuscleConstant(ClipMuscleConstant * constant, memory::Allocator& alloc);
+
+ ClipMuscleInput* CreateClipMuscleInput(ClipMuscleConstant const* constant, memory::Allocator& alloc);
+ void DestroyClipMuscleInput(ClipMuscleInput * input, memory::Allocator& alloc);
+
+ float ComputeClipTime(float normalizedTimeIn, float startTime, float stopTime, float cycleOffset, bool loop, bool reverse, float &nomralizedTimeOut, float &timeInt);
+
+ void EvaluateClipRootMotionDeltaX(const ClipMuscleConstant& constant, const ClipMuscleInput &input, MotionOutput &output, ClipMemory &memory);
+
+ void EvaluateClipMusclePrevTime(const ClipMuscleConstant& constant, const ClipMuscleInput &input, MotionOutput &output, ClipMemory &memory);
+ void EvaluateClipMuscle(const ClipMuscleConstant& constant, const ClipMuscleInput &input, const float *valuesOutput, MotionOutput &motionOutput, human::HumanPose &humanPose, ClipMemory &memory);
+
+ void ComputeClipMuscleDeltaPose(ClipMuscleConstant const &constant, float startTime, float stopTime, human::HumanPose &deltaPose, math::xform &startX, math::xform &leftFootStartX, math::xform &rightFootStartX, memory::Allocator& alloc);
+
+ void GetHumanPose(const ClipMuscleConstant& constant, const float* values, human::HumanPose &humanPose);
+ void GetHumanPose(const ClipMuscleConstant& constant, const ValueDelta* values, human::HumanPose &humanPose);
+
+ void InitClipMuscleDeltaValues(ClipMuscleConstant& constant);
+ void InitClipMuscleDeltaPose(ClipMuscleConstant& constant);
+ void InitClipMuscleAverageSpeed(ClipMuscleConstant& constant, int steps = 20);
+
+ void InitMuscleClipIndexArray(ClipMuscleConstant& constant, memory::Allocator& alloc);
+ size_t GetClipCurveCount(const ClipMuscleConstant& constant);
+}
+}
diff --git a/Runtime/mecanim/animation/constantclip.cpp b/Runtime/mecanim/animation/constantclip.cpp
new file mode 100644
index 0000000..1fd07cf
--- /dev/null
+++ b/Runtime/mecanim/animation/constantclip.cpp
@@ -0,0 +1,34 @@
+#include "UnityPrefix.h"
+#include "constantclip.h"
+#include "Runtime/mecanim/memory.h"
+
+namespace mecanim
+{
+ namespace animation
+ {
+ void SampleClip (const ConstantClip& curveData, uint32_t outputCount, float* output)
+ {
+ DebugAssert(outputCount <= curveData.curveCount);
+
+ memcpy(output, curveData.data.Get(), outputCount * sizeof(float));
+ }
+
+ float SampleClipAtIndex (const ConstantClip& curveData, int index)
+ {
+ Assert(index < curveData.curveCount);
+
+ return curveData.data[index];
+ }
+
+ void DestroyConstantClip (ConstantClip& clip, memory::Allocator& alloc)
+ {
+ alloc.Deallocate(clip.data);
+ }
+
+ void CreateConstantClip (ConstantClip& clip, size_t curveCount, memory::Allocator& alloc)
+ {
+ clip.curveCount = curveCount;
+ clip.data = alloc.ConstructArray<float> (curveCount);
+ }
+ }
+}
diff --git a/Runtime/mecanim/animation/constantclip.h b/Runtime/mecanim/animation/constantclip.h
new file mode 100644
index 0000000..a7de648
--- /dev/null
+++ b/Runtime/mecanim/animation/constantclip.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+
+
+namespace mecanim
+{
+namespace animation
+{
+struct ConstantClip
+{
+ UInt32 curveCount;
+ OffsetPtr<float> data;
+
+ ConstantClip () : curveCount (0) { }
+
+ DEFINE_GET_TYPESTRING(ConstantClip)
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(curveCount);
+ MANUAL_ARRAY_TRANSFER2(float, data, curveCount);
+ }
+};
+
+// Sample functions
+void SampleClip (const ConstantClip& curveData, uint32_t outputCount, float* output);
+float SampleClipAtIndex (const ConstantClip& curveData, int index);
+
+void DestroyConstantClip (ConstantClip& clip, memory::Allocator& alloc);
+void CreateConstantClip (ConstantClip& clip, size_t curveCount, memory::Allocator& alloc);
+
+}
+} \ No newline at end of file
diff --git a/Runtime/mecanim/animation/curvedata.cpp b/Runtime/mecanim/animation/curvedata.cpp
new file mode 100644
index 0000000..210bbe8
--- /dev/null
+++ b/Runtime/mecanim/animation/curvedata.cpp
@@ -0,0 +1,118 @@
+#include "UnityPrefix.h"
+#include "Runtime/mecanim/animation/curvedata.h"
+
+namespace mecanim
+{
+
+namespace animation
+{
+ Clip* CreateClipSimple(uint32_t count, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(Clip);
+
+ // Allocate data.
+ Clip* clip = alloc.Construct<Clip>();
+ return clip;
+ }
+
+
+ void DestroyClip(Clip* clip, memory::Allocator& alloc)
+ {
+ if(clip)
+ {
+ DestroyStreamedClip(clip->m_StreamedClip, alloc);
+ DestroyDenseClip(clip->m_DenseClip, alloc);
+
+ alloc.Deallocate(clip);
+ }
+ }
+
+ ClipMemory* CreateClipMemory(Clip const* clip, memory::Allocator& alloc)
+ {
+ return CreateClipMemory(clip, GetClipCurveCount(*clip), alloc);
+ }
+
+ ClipMemory* CreateClipMemory(Clip const* clip, int32_t totalUsedCurves, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(ClipMemory);
+
+ ClipMemory* mem = alloc.Construct<ClipMemory>();
+ mem->m_ConstantClipValueCount = totalUsedCurves - (GetClipCurveCount(*clip) - clip->m_ConstantClip.curveCount);
+ Assert(mem->m_ConstantClipValueCount >= 0 && mem->m_ConstantClipValueCount <= clip->m_ConstantClip.curveCount);
+
+ CreateStreamedClipMemory(clip->m_StreamedClip, mem->m_StreamedClipCache, alloc);
+
+ return mem;
+ }
+
+ void DestroyClipMemory(ClipMemory* memory, memory::Allocator& alloc)
+ {
+ if(memory)
+ {
+ DestroyStreamedClipMemory(memory->m_StreamedClipCache, alloc);
+ alloc.Deallocate(memory);
+ }
+ }
+
+ ClipOutput* CreateClipOutput(Clip const* clip, memory::Allocator& alloc)
+ {
+ return CreateClipOutput(GetClipCurveCount(*clip), alloc);
+ }
+
+ ClipOutput* CreateClipOutput(int32_t totalUsedCurves, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(ClipOutput);
+
+ ClipOutput* out = alloc.Construct<ClipOutput>();
+ out->m_Values = alloc.ConstructArray<float> (totalUsedCurves);
+
+ return out;
+ }
+
+ void DestroyClipOutput(ClipOutput* output, memory::Allocator& alloc)
+ {
+ if(output)
+ {
+ alloc.Deallocate(output->m_Values);
+ alloc.Deallocate(output);
+ }
+ }
+
+ float EvaluateClipAtIndex(Clip const* clip, ClipInput const* input, ClipMemory* memory, uint32_t index)
+ {
+ if (index < clip->m_StreamedClip.curveCount)
+ return SampleClipAtIndex(clip->m_StreamedClip, memory->m_StreamedClipCache, index, input->m_Time);
+ index -= clip->m_StreamedClip.curveCount;
+
+ if (index < clip->m_DenseClip.m_CurveCount)
+ return SampleClipAtIndex(clip->m_DenseClip, index, input->m_Time);
+ index -= clip->m_DenseClip.m_CurveCount;
+
+ return SampleClipAtIndex(clip->m_ConstantClip, index);
+ }
+
+ void EvaluateClip(Clip const* clip, ClipInput const* input, ClipMemory* memory, ClipOutput* output )
+ {
+ float* outputData = output->m_Values;
+
+ if (clip->m_StreamedClip.curveCount != 0)
+ {
+ SampleClip(clip->m_StreamedClip, memory->m_StreamedClipCache, input->m_Time, outputData);
+ outputData += clip->m_StreamedClip.curveCount;
+ }
+
+ if (clip->m_DenseClip.m_CurveCount != 0)
+ {
+ SampleClip(clip->m_DenseClip, input->m_Time, outputData);
+ outputData += clip->m_DenseClip.m_CurveCount;
+ }
+
+ if (memory->m_ConstantClipValueCount != 0)
+ {
+ // Constant clips are not sampling based on the total curve count
+ SampleClip(clip->m_ConstantClip, memory->m_ConstantClipValueCount, outputData);
+ }
+ }
+}
+
+}
diff --git a/Runtime/mecanim/animation/curvedata.h b/Runtime/mecanim/animation/curvedata.h
new file mode 100644
index 0000000..0244684
--- /dev/null
+++ b/Runtime/mecanim/animation/curvedata.h
@@ -0,0 +1,82 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/generic/valuearray.h"
+
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Animation/MecanimArraySerialization.h"
+#include "streamedclip.h"
+#include "denseclip.h"
+#include "constantclip.h"
+
+namespace mecanim
+{
+
+namespace animation
+{
+ struct Clip
+ {
+ DEFINE_GET_TYPESTRING(Clip)
+
+ Clip() {}
+
+ StreamedClip m_StreamedClip;
+ DenseClip m_DenseClip;
+ ConstantClip m_ConstantClip;
+
+ OffsetPtr<ValueArrayConstant> m_DeprecatedBinding;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER (m_StreamedClip);
+ TRANSFER (m_DenseClip);
+ TRANSFER (m_ConstantClip);
+
+ transfer.Transfer(m_DeprecatedBinding, "m_Binding");
+ }
+ };
+
+ struct ClipInput
+ {
+ float m_Time;
+ };
+
+ struct ClipMemory
+ {
+ StreamedClipMemory m_StreamedClipCache;
+ int m_ConstantClipValueCount;
+ };
+
+ struct ClipOutput
+ {
+ public:
+ float* m_Values;
+
+ };
+
+ Clip* CreateClipSimple(uint32_t count, memory::Allocator& alloc);
+ void DestroyClip(Clip* clip, memory::Allocator& alloc);
+
+ ClipMemory* CreateClipMemory(Clip const* clip, memory::Allocator& alloc);
+ ClipMemory* CreateClipMemory(Clip const* clip, int32_t totalUsedCurves, memory::Allocator& alloc);
+ void DestroyClipMemory(ClipMemory* memory, memory::Allocator& alloc);
+
+ ClipOutput* CreateClipOutput(int32_t usedConstantCurves, memory::Allocator& alloc);
+ ClipOutput* CreateClipOutput(Clip const* clip, memory::Allocator& alloc);
+
+ void DestroyClipOutput(ClipOutput* output, memory::Allocator& alloc);
+
+ float EvaluateClipAtIndex(Clip const* clip, ClipInput const* input, ClipMemory* memory, uint32_t index);
+ void EvaluateClip(Clip const* clip, ClipInput const* input, ClipMemory* memory, ClipOutput* output);
+
+ inline size_t GetClipCurveCount (const Clip& clip) { return clip.m_StreamedClip.curveCount + clip.m_DenseClip.m_CurveCount + clip.m_ConstantClip.curveCount; }
+ inline bool IsValidClip (const Clip& clip) { return GetClipCurveCount (clip) != 0; }
+
+
+} // namespace animation
+
+}
diff --git a/Runtime/mecanim/animation/damp.cpp b/Runtime/mecanim/animation/damp.cpp
new file mode 100644
index 0000000..c769108
--- /dev/null
+++ b/Runtime/mecanim/animation/damp.cpp
@@ -0,0 +1,24 @@
+#include "UnityPrefix.h"
+#include "Runtime/Math/Simd/math.h"
+#include "Runtime/mecanim/animation/damp.h"
+
+namespace mecanim
+{
+
+namespace dynamics
+{
+ void ScalDamp::Evaluate(float value, float deltaTime)
+ {
+ m_Value = math::cond(m_DampTime > 0, m_Value + (value - m_Value) * math::abs(deltaTime) / (m_DampTime + math::abs(deltaTime)), value);
+ }
+
+ void VectorDamp::Evaluate(math::float4 const& value, float deltaTime)
+ {
+ math::float1 dt(deltaTime);
+ math::float1 dampTime(m_DampTime);
+
+ m_Value = math::cond( math::bool4(m_DampTime > 0), m_Value + (value - m_Value) * math::abs(dt) / (dampTime + math::abs(dt)), value);
+ }
+}
+
+}
diff --git a/Runtime/mecanim/animation/damp.h b/Runtime/mecanim/animation/damp.h
new file mode 100644
index 0000000..319f6e6
--- /dev/null
+++ b/Runtime/mecanim/animation/damp.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/Math/Simd/float4.h"
+
+namespace mecanim
+{
+
+namespace dynamics
+{
+ class ScalDamp
+ {
+ public:
+
+ float m_DampTime;
+ float m_Value;
+
+ ScalDamp() { Reset(); }
+
+ void Reset() { m_DampTime = 0; m_Value = 0; }
+ void Evaluate(float value, float deltaTime);
+ };
+
+
+ class VectorDamp
+ {
+ public:
+
+ float m_DampTime;
+ math::float4 m_Value;
+
+ VectorDamp() { Reset(); }
+
+ void Reset() { m_DampTime = 0; m_Value = math::float4::zero(); }
+ void Evaluate(math::float4 const& value, float deltaTime);
+ };
+
+} // namespace dynamics
+
+}
diff --git a/Runtime/mecanim/animation/denseclip.cpp b/Runtime/mecanim/animation/denseclip.cpp
new file mode 100644
index 0000000..3c19e47
--- /dev/null
+++ b/Runtime/mecanim/animation/denseclip.cpp
@@ -0,0 +1,63 @@
+#include "UnityPrefix.h"
+#include "denseclip.h"
+#include "Runtime/Math/Simd/math.h"
+namespace mecanim
+{
+namespace animation
+{
+
+static void BlendArray(const float* lhs, const float* rhs, size_t size, float t, float* output)
+{
+ for (int i=0;i<size;i++)
+ output[i] = math::lerp(lhs[i], rhs[i], t);
+}
+
+
+static void PrepareBlendValues (const DenseClip& clip, float time, float*& lhs, float*& rhs, float& u)
+{
+ time -= clip.m_BeginTime;
+
+ float index;
+ u = math::modf(time * clip.m_SampleRate, index);
+
+ int lhsIndex = (int)index;
+ int rhsIndex = lhsIndex + 1;
+ lhsIndex = math::maximum(0, lhsIndex);
+ lhsIndex = math::minimum(clip.m_FrameCount-1, lhsIndex);
+
+ rhsIndex = math::maximum(0, rhsIndex);
+ rhsIndex = math::minimum(clip.m_FrameCount-1, rhsIndex);
+
+ lhs = const_cast<float*> (&clip.m_SampleArray[lhsIndex * clip.m_CurveCount]);
+ rhs = const_cast<float*> (&clip.m_SampleArray[rhsIndex * clip.m_CurveCount]);
+}
+
+void SampleClip (const DenseClip& clip, float time, float* output)
+{
+ float u;
+ float* lhsPtr;
+ float* rhsPtr;
+ PrepareBlendValues(clip, time, lhsPtr, rhsPtr, u);
+
+ BlendArray(lhsPtr, rhsPtr, clip.m_CurveCount, u, output);
+}
+
+float SampleClipAtIndex (const DenseClip& clip, int curveIndex, float time)
+{
+ float u;
+ float* lhsPtr;
+ float* rhsPtr;
+ PrepareBlendValues(clip, time, lhsPtr, rhsPtr, u);
+
+ return math::lerp(lhsPtr[curveIndex], rhsPtr[curveIndex], u);
+}
+
+
+// Creation & Destruction
+void DestroyDenseClip (DenseClip& clip, memory::Allocator& alloc)
+{
+ alloc.Deallocate(clip.m_SampleArray);
+}
+
+}
+}
diff --git a/Runtime/mecanim/animation/denseclip.h b/Runtime/mecanim/animation/denseclip.h
new file mode 100644
index 0000000..e3175f1
--- /dev/null
+++ b/Runtime/mecanim/animation/denseclip.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+
+
+namespace mecanim
+{
+ namespace animation
+ {
+ struct DenseClip
+ {
+ int m_FrameCount;
+ uint32_t m_CurveCount;
+ float m_SampleRate;
+ float m_BeginTime;
+
+ uint32_t m_SampleArraySize;
+ OffsetPtr<float> m_SampleArray;
+
+
+ DenseClip () : m_FrameCount (0),m_CurveCount(0),m_SampleRate(0.0F),m_SampleArraySize(0),m_BeginTime(0.0F) { }
+
+ DEFINE_GET_TYPESTRING(DenseClip)
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_FrameCount);
+ TRANSFER(m_CurveCount);
+ TRANSFER(m_SampleRate);
+ TRANSFER(m_BeginTime);
+
+ TRANSFER_BLOB_ONLY(m_SampleArraySize);
+ MANUAL_ARRAY_TRANSFER2(float, m_SampleArray, m_SampleArraySize);
+ }
+ };
+
+ // Sample functions
+ void SampleClip (const DenseClip& curveData, float time, float* output);
+ float SampleClipAtIndex (const DenseClip& curveData, int index, float time);
+
+
+ // Creation & Destruction
+ void DestroyDenseClip (DenseClip& clip, memory::Allocator& alloc);
+ }
+} \ No newline at end of file
diff --git a/Runtime/mecanim/animation/poseblender.cpp b/Runtime/mecanim/animation/poseblender.cpp
new file mode 100644
index 0000000..ccbdf8f
--- /dev/null
+++ b/Runtime/mecanim/animation/poseblender.cpp
@@ -0,0 +1,228 @@
+#include "UnityPrefix.h"
+#include "Runtime/mecanim/skeleton/skeleton.h"
+
+#include "Runtime/mecanim/graph/plugbinder.h"
+#include "Runtime/mecanim/animation/common.h"
+#include "Runtime/mecanim/animation/poseblender.h"
+
+namespace mecanim
+{
+
+namespace animation
+{
+ namespace
+ {
+ void SetPoseBlenderValue(graph::GraphPlug const* plug, uint32_t index, PoseBlenderInput const* input, graph::EvaluationGraphWorkspace* graphMemory)
+ {
+ float value;
+ plug->ReadData(&value, graphMemory->m_EvaluationInfo);
+ input->m_WeightArray[index] += value;
+ }
+ }
+
+ PoseBlenderConstant* CreatePoseBlenderConstant(skeleton::Skeleton * skeleton, skeleton::SkeletonPose* defaultPose, skeleton::SkeletonPose** arrayPose, uint32_t count, graph::Graph* graph, memory::Allocator& alloc)
+ {
+ PoseBlenderConstant *cst = alloc.Construct<PoseBlenderConstant>();
+
+ cst->m_Skeleton = skeleton;
+ cst->m_SkeletonPoseCount = count;
+
+ cst->m_DefaultSkeletonPose = defaultPose;
+ cst->m_SkeletonPoseArray = alloc.ConstructArray<skeleton::SkeletonPose*>(count);
+ cst->m_PosesID = alloc.ConstructArray<uint32_t>(count);
+ cst->m_HasGraph = graph != 0;
+ cst->m_Graph = graph;
+
+ uint32_t i;
+ for(i=0;i<count;i++)
+ {
+ cst->m_SkeletonPoseArray[i] = arrayPose[i];
+ }
+
+ if(cst->m_HasGraph)
+ {
+ cst->m_ValuesConstant = CreateValueArrayConstant(kFloatType, cst->m_Graph->m_InEdgesCount, alloc);
+ for(i=0;i<cst->m_Graph->m_InEdgesCount;++i)
+ {
+ cst->m_ValuesConstant->m_ValueArray[i].m_ID = cst->m_Graph->m_InEdges[i].m_Binding;
+ }
+ }
+
+ return cst;
+ }
+
+ void DestroyPoseBlenderConstant(PoseBlenderConstant * constant, memory::Allocator& alloc)
+ {
+ if(constant)
+ {
+ alloc.Deallocate(constant->m_PosesID);
+ alloc.Deallocate(constant->m_SkeletonPoseArray);
+ alloc.Deallocate(constant);
+ }
+ }
+
+ void InitializePoseBlenderConstant(PoseBlenderConstant * constant, graph::GraphFactory const& factory, memory::Allocator& alloc)
+ {
+ if(constant->m_Graph)
+ {
+ constant->m_EvaluationGraph = graph::CreateEvaluationGraph(constant->m_Graph, factory, alloc);
+ }
+ }
+
+ void ClearPoseBlenderConstant(PoseBlenderConstant * constant, memory::Allocator& alloc)
+ {
+ if(constant->m_EvaluationGraph)
+ {
+ graph::DestroyEvaluationGraph(constant->m_EvaluationGraph, alloc);
+ constant->m_EvaluationGraph = 0;
+ }
+ }
+
+ PoseBlenderInput* CreatePoseBlenderInput(PoseBlenderConstant const* constant, memory::Allocator& alloc)
+ {
+ PoseBlenderInput *in = alloc.Construct<PoseBlenderInput>();
+
+ if(constant->m_ValuesConstant)
+ {
+ in->m_Values = CreateValueArray(constant->m_ValuesConstant, alloc);
+ }
+ in->m_WeightPoseCount = constant->m_SkeletonPoseCount;
+ in->m_SkeletonPose = skeleton::CreateSkeletonPose(constant->m_Skeleton, alloc);
+ in->m_WeightArray = alloc.ConstructArray<float>(constant->m_SkeletonPoseCount);
+
+ uint32_t i;
+ for(i=0;i<in->m_WeightPoseCount;i++)
+ {
+ in->m_WeightArray[i] = 0.f;
+ }
+
+ return in;
+ }
+
+ void DestroyPoseBlenderInput(PoseBlenderInput * input, memory::Allocator& alloc)
+ {
+ if(input)
+ {
+ skeleton::DestroySkeletonPose(input->m_SkeletonPose, alloc);
+ alloc.Deallocate(input->m_WeightArray);
+ alloc.Deallocate(input);
+ }
+ }
+
+ PoseBlenderWorkspace* CreatePoseBlenderWorkspace(PoseBlenderConstant const* constant, memory::Allocator& alloc)
+ {
+ PoseBlenderWorkspace *ws = alloc.Construct<PoseBlenderWorkspace>();
+ ws->m_SkeletonPose = skeleton::CreateSkeletonPose(constant->m_Skeleton, alloc);
+
+ return ws;
+ }
+ void DestroyPoseBlenderWorkspace(PoseBlenderWorkspace * workspace, memory::Allocator& alloc)
+ {
+ if(workspace)
+ {
+ skeleton::DestroySkeletonPose(workspace->m_SkeletonPose, alloc);
+ alloc.Deallocate(workspace);
+ }
+ }
+
+ PoseBlenderMemory* CreatePoseBlenderMemory(PoseBlenderConstant const* constant, memory::Allocator& alloc)
+ {
+ PoseBlenderMemory* mem = alloc.Construct<PoseBlenderMemory>();
+
+ if(constant->m_EvaluationGraph)
+ {
+ mem->m_GraphWS = graph::CreateEvaluationGraphWorkspace(constant->m_EvaluationGraph, alloc);
+ mem->m_InputPoseBlenderBindingCount = 0;
+ uint32_t i,j;
+ for(i=0;i<constant->m_EvaluationGraph->m_Output->mPlugCount;i++)
+ {
+ graph::GraphPlug const* plug = constant->m_EvaluationGraph->m_Output->mPlugArray[i];
+ for(j=0;j<constant->m_SkeletonPoseCount;j++)
+ {
+ if( plug->m_ID == constant->m_PosesID[j])
+ {
+ mem->m_InputPoseBlenderBindingCount++;
+ }
+ }
+ }
+ if(mem->m_InputPoseBlenderBindingCount)
+ {
+ mem->m_InputPoseBlenderBinding = alloc.ConstructArray<SetPoseBlenderInput2>(mem->m_InputPoseBlenderBindingCount);
+ uint32_t k = 0;
+ for(i=0;i<constant->m_EvaluationGraph->m_Output->mPlugCount;i++)
+ {
+ graph::GraphPlug const* plug = constant->m_EvaluationGraph->m_Output->mPlugArray[i];
+ for(j=0;j<constant->m_SkeletonPoseCount;j++)
+ {
+ if( plug->m_ID == constant->m_PosesID[j])
+ {
+ mem->m_InputPoseBlenderBinding[k++] = bind( SetPoseBlenderValue, plug, j);
+ }
+ }
+ }
+ }
+ }
+
+ return mem;
+ }
+
+ void DestroyPoseBlenderMemory(PoseBlenderMemory* memory, memory::Allocator& alloc)
+ {
+ if(memory)
+ {
+ graph::DestroyEvaluationGraphWorkspace(memory->m_GraphWS, alloc);
+ alloc.Deallocate(memory->m_InputPoseBlenderBinding);
+ alloc.Deallocate(memory);
+ }
+ }
+
+
+ PoseBlenderOutput* CreatePoseBlenderOutput(PoseBlenderConstant const* constant, memory::Allocator& alloc)
+ {
+ PoseBlenderOutput *out = alloc.Construct<PoseBlenderOutput>();
+ out->m_SkeletonPose = skeleton::CreateSkeletonPose(constant->m_Skeleton, alloc);
+
+ return out;
+ }
+
+ void DestroyPoseBlenderOutput(PoseBlenderOutput * output, memory::Allocator& alloc)
+ {
+ if(output)
+ {
+ skeleton::DestroySkeletonPose(output->m_SkeletonPose, alloc);
+ alloc.Deallocate(output);
+ }
+ }
+
+ void EvaluatePoseBlender(PoseBlenderConstant const * constant, PoseBlenderInput const * input, PoseBlenderOutput * output, PoseBlenderMemory* memory, PoseBlenderWorkspace * workspace)
+ {
+ if(constant->m_EvaluationGraph)
+ {
+ memory->m_GraphWS->m_EvaluationInfo.m_EvaluationId++;
+
+ // Value Array is constructed based on graph so they should be sync
+ uint32_t i, count = constant->m_ValuesConstant->m_Count;
+ for(i=0;i<count;i++)
+ {
+ SetPlugValue(&constant->m_EvaluationGraph->m_Input->GetPlug(i), i, input->m_Values, memory->m_GraphWS);
+ }
+
+ count = memory->m_InputPoseBlenderBindingCount;
+ for(i=0;i<count;i++)
+ {
+ memory->m_InputPoseBlenderBinding[i](input, memory->m_GraphWS);
+ }
+ }
+
+ skeleton::SkeletonPoseCopy(input->m_SkeletonPose, output->m_SkeletonPose);
+ uint32_t i;
+ for(i = 0; i < constant->m_SkeletonPoseCount; i++)
+ {
+ skeleton::SkeletonPoseSub(constant->m_SkeletonPoseArray[i], input->m_SkeletonPose, workspace->m_SkeletonPose);
+ skeleton::SkeletonPoseWeight(workspace->m_SkeletonPose, math::float1(input->m_WeightArray[i]), workspace->m_SkeletonPose);
+ skeleton::SkeletonPoseAdd(output->m_SkeletonPose, workspace->m_SkeletonPose, output->m_SkeletonPose);
+ }
+ }
+}
+
+}
diff --git a/Runtime/mecanim/animation/poseblender.h b/Runtime/mecanim/animation/poseblender.h
new file mode 100644
index 0000000..4bd683e
--- /dev/null
+++ b/Runtime/mecanim/animation/poseblender.h
@@ -0,0 +1,113 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/object.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/bind.h"
+
+#include "Runtime/mecanim/generic/valuearray.h"
+#include "Runtime/mecanim/graph/plug.h"
+#include "Runtime/mecanim/graph/graph.h"
+
+// forwards
+
+namespace mecanim
+{
+
+namespace skeleton {struct Skeleton; struct SkeletonPose;}
+
+namespace animation
+{
+ // Constant
+ struct PoseBlenderConstant
+ {
+ PoseBlenderConstant()
+ :m_Skeleton(0),
+ m_DefaultSkeletonPose(0),
+ m_SkeletonPoseArray(0),
+ m_PosesID(0),
+ m_SkeletonPoseCount(0),
+ m_Graph(0),
+ m_ValuesConstant(0),
+ m_HasGraph(false),
+ m_EvaluationGraph(0)
+ {
+ }
+
+ skeleton::Skeleton* m_Skeleton;
+ skeleton::SkeletonPose* m_DefaultSkeletonPose;
+ skeleton::SkeletonPose** m_SkeletonPoseArray;
+ uint32_t* m_PosesID;
+ graph::Graph* m_Graph;
+ ValueArrayConstant const* m_ValuesConstant;
+ bool m_HasGraph;
+
+ uint32_t m_SkeletonPoseCount;
+
+ graph::EvaluationGraph* m_EvaluationGraph;
+ };
+
+ struct PoseBlenderInput
+ {
+ PoseBlenderInput(): m_Values(0), m_WeightArray(0), m_SkeletonPose(0), m_WeightPoseCount(0){}
+
+ ValueArray* m_Values;
+ float* m_WeightArray;
+ skeleton::SkeletonPose* m_SkeletonPose;
+ uint32_t m_WeightPoseCount;
+ };
+
+ struct PoseBlenderWorkspace
+ {
+ PoseBlenderWorkspace(): m_SkeletonPose(0){}
+ skeleton::SkeletonPose* m_SkeletonPose;
+ };
+
+ typedef binder2<function<void (graph::GraphPlug const*, uint32_t, PoseBlenderInput const*, graph::EvaluationGraphWorkspace*)>::ptr,
+ graph::GraphPlug const*,
+ uint32_t>
+ SetPoseBlenderInput2;
+
+ struct PoseBlenderMemory
+ {
+ PoseBlenderMemory() : m_InputPoseBlenderBindingCount(0), m_InputPoseBlenderBinding(0), m_GraphWS(0)
+ {
+ }
+
+ uint32_t m_InputPoseBlenderBindingCount;
+ SetPoseBlenderInput2* m_InputPoseBlenderBinding;
+
+ graph::EvaluationGraphWorkspace* m_GraphWS;
+ };
+
+
+ struct PoseBlenderOutput : public Object
+ {
+ PoseBlenderOutput():m_SkeletonPose(0){}
+
+ skeleton::SkeletonPose* m_SkeletonPose;
+ };
+
+ PoseBlenderConstant* CreatePoseBlenderConstant(skeleton::Skeleton * skeleton, skeleton::SkeletonPose* defaultPose, skeleton::SkeletonPose** arrayPose, uint32_t count, graph::Graph* graph, memory::Allocator& alloc);
+ void DestroyPoseBlenderConstant(PoseBlenderConstant * constant, memory::Allocator& alloc);
+
+ void InitializePoseBlenderConstant(PoseBlenderConstant * constant, graph::GraphFactory const& factory, memory::Allocator& alloc);
+ void ClearPoseBlenderConstant(PoseBlenderConstant * constant, memory::Allocator& alloc);
+
+ PoseBlenderInput* CreatePoseBlenderInput(PoseBlenderConstant const* constant, memory::Allocator& alloc);
+ void DestroyPoseBlenderInput(PoseBlenderInput * input, memory::Allocator& alloc);
+
+ PoseBlenderWorkspace* CreatePoseBlenderWorkspace(PoseBlenderConstant const* constant, memory::Allocator& alloc);
+ void DestroyPoseBlenderWorkspace(PoseBlenderWorkspace * workspace, memory::Allocator& alloc);
+
+ PoseBlenderMemory* CreatePoseBlenderMemory(PoseBlenderConstant const* constant, memory::Allocator& alloc);
+ void DestroyPoseBlenderMemory(PoseBlenderMemory* amemory, memory::Allocator& alloc);
+
+ PoseBlenderOutput* CreatePoseBlenderOutput(PoseBlenderConstant const* constant, memory::Allocator& alloc);
+ void DestroyPoseBlenderOutput(PoseBlenderOutput * output, memory::Allocator& alloc);
+
+ void EvaluatePoseBlender(PoseBlenderConstant const * constant, PoseBlenderInput const * input, PoseBlenderOutput * output, PoseBlenderMemory* memory, PoseBlenderWorkspace * workspace);
+}
+
+}
diff --git a/Runtime/mecanim/animation/streamedclip.cpp b/Runtime/mecanim/animation/streamedclip.cpp
new file mode 100644
index 0000000..8b27785
--- /dev/null
+++ b/Runtime/mecanim/animation/streamedclip.cpp
@@ -0,0 +1,196 @@
+#include "UnityPrefix.h"
+#include "streamedclip.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/Utilities/Prefetch.h"
+#include "Runtime/Math/Simd/math.h"
+
+namespace mecanim
+{
+namespace animation
+{
+
+
+inline static float EvaluateCache (const StreamedCacheItem& cache, float sampleTime)
+{
+ float t = sampleTime - cache.time;
+ return (t * (t * (t * cache.coeff[0] + cache.coeff[1]) + cache.coeff[2])) + cache.coeff[3];
+}
+
+inline static void EvaluateMultipleCaches ( const StreamedCacheItem& cache0,
+ const StreamedCacheItem& cache1,
+ const StreamedCacheItem& cache2,
+ const StreamedCacheItem& cache3,
+ float sampleTime, float* output)
+{
+ const math::float4 time(sampleTime);
+
+ const math::float4 cachetime(cache0.time, cache1.time, cache2.time, cache3.time);
+ const math::float4 dt = time - cachetime;
+
+ const math::float4 coeffs0(cache0.coeff[0], cache1.coeff[0], cache2.coeff[0], cache3.coeff[0]);
+ const math::float4 coeffs1(cache0.coeff[1], cache1.coeff[1], cache2.coeff[1], cache3.coeff[1]);
+ const math::float4 coeffs2(cache0.coeff[2], cache1.coeff[2], cache2.coeff[2], cache3.coeff[2]);
+ const math::float4 coeffs3(cache0.coeff[3], cache1.coeff[3], cache2.coeff[3], cache3.coeff[3]);
+
+ ATTRIBUTE_ALIGN(ALIGN4F) float v[4];
+ math::store(dt * (dt * (dt * coeffs0 + coeffs1) + coeffs2) + coeffs3, v);
+
+ output[0] = v[0];
+ output[1] = v[1];
+ output[2] = v[2];
+ output[3] = v[3];
+}
+
+static void EvaluateCaches (const StreamedClipMemory& cache, float sampleTime, float* output)
+{
+ const StreamedCacheItem* caches = &cache.caches[0];
+ Prefetch(caches);
+ Prefetch(caches+2);
+ Prefetch(caches+4);
+ Prefetch(caches+6);
+ Prefetch(caches+8);
+
+ int i = 0;
+ for ( ; i+4 <= cache.cacheCount; i+=4, caches+=4 )
+ {
+ Prefetch(caches+10);
+ Prefetch(caches+12);
+ const StreamedCacheItem& item0 = *(caches);
+ const StreamedCacheItem& item1 = *(caches+1);
+ const StreamedCacheItem& item2 = *(caches+2);
+ const StreamedCacheItem& item3 = *(caches+3);
+ EvaluateMultipleCaches(item0, item1, item2, item3, sampleTime, &output[i]);
+ }
+
+ for ( ; i<cache.cacheCount; ++i, ++caches)
+ {
+ const StreamedCacheItem& item = *caches;
+ output[i] = EvaluateCache(item, sampleTime);
+ }
+
+}
+
+static void RewindCache (StreamedClipMemory& cache)
+{
+ cache.time = -std::numeric_limits<float>::infinity();
+ cache.readByteOffset = 0;
+}
+
+static inline void ConsumeCurveTimeData (const CurveTimeData* __restrict curveData, StreamedCacheItem* __restrict caches)
+{
+ float time = curveData->time;
+ const CurveKey* __restrict keys = reinterpret_cast<const CurveKey*> (curveData + 1);
+ int count = curveData->count;
+
+ Prefetch(keys,0);
+ Prefetch(keys+3);
+
+ int curveIndex = keys[0].curveIndex;
+ float coeff0 = keys[0].coeff[0];
+ float coeff1 = keys[0].coeff[1];
+ float coeff2 = keys[0].coeff[2];
+ float coeff3 = keys[0].coeff[3];
+
+ for (int i=1;i<count;i++)
+ {
+ Prefetch(keys+3+i);
+
+ StreamedCacheItem& activeCache = caches[curveIndex];
+ activeCache.time = time;
+ activeCache.coeff[0] = coeff0;
+ activeCache.coeff[1] = coeff1;
+ activeCache.coeff[2] = coeff2;
+ activeCache.coeff[3] = coeff3;
+ curveIndex = keys[i].curveIndex;
+ coeff0 = keys[i].coeff[0];
+ coeff1 = keys[i].coeff[1];
+ coeff2 = keys[i].coeff[2];
+ coeff3 = keys[i].coeff[3];
+ }
+ StreamedCacheItem& activeCache = caches[curveIndex];
+ activeCache.time = time;
+ activeCache.coeff[0] = coeff0;
+ activeCache.coeff[1] = coeff1;
+ activeCache.coeff[2] = coeff2;
+ activeCache.coeff[3] = coeff3;
+}
+
+static void SeekClipForward (const UInt8* curveData, float time, StreamedClipMemory& cache)
+{
+ int readByteOffset = cache.readByteOffset;
+ const CurveTimeData* data = reinterpret_cast<const CurveTimeData*> (curveData + readByteOffset);
+
+ while (time >= data->time)
+ {
+ // Consume the data and apply it to the cache
+ ConsumeCurveTimeData(data, cache.caches);
+
+ // Seek forward by the consumed data
+ readByteOffset += sizeof(CurveTimeData) + data->count * sizeof(CurveKey);
+
+ data = reinterpret_cast<const CurveTimeData*> (curveData + readByteOffset);
+ }
+
+ // Synchronize cached time & offset
+ cache.time = time;
+ cache.readByteOffset = readByteOffset;
+}
+
+void SeekClip (const StreamedClip& curveData, StreamedClipMemory& cache, float time)
+{
+ Assert(cache.cacheCount == curveData.curveCount);
+
+ // No seeking is necessary, we are exactly at the same cached time
+ // (Happens due to SampleClipAtIndex)
+ // @TODO: it would be best to remove that and instead seperate root motion data from other data
+ if (time == cache.time)
+ return;
+
+ // Seeking backwards is not supported. Jump the beginning of curve.
+ if (time < cache.time)
+ RewindCache(cache);
+
+ // Seek and make sure the curve cache is up to date
+ const UInt8* stream = reinterpret_cast<const UInt8*> (curveData.data.Get());
+ SeekClipForward(stream, time, cache);
+}
+
+void SampleClip (const StreamedClip& curveData, StreamedClipMemory& cache, float time, float* output)
+{
+ SeekClip(curveData, cache, time);
+
+ // Evaluate the cache and write sampled values to output
+ EvaluateCaches(cache, time, output);
+}
+
+float SampleClipAtIndex (const StreamedClip& curveData, StreamedClipMemory& cache, int index, float time)
+{
+ Assert(index < curveData.curveCount);
+
+ SeekClip(curveData, cache, time);
+
+ return EvaluateCache(cache.caches[index], time);
+}
+
+void CreateStreamedClipMemory(const StreamedClip& clip, StreamedClipMemory& mem, memory::Allocator& alloc)
+{
+ SETPROFILERLABEL(StreamedClipMemory);
+
+ mem.caches = alloc.ConstructArray<StreamedCacheItem>(clip.curveCount);
+ mem.cacheCount = clip.curveCount;
+
+ RewindCache(mem);
+}
+
+void DestroyStreamedClipMemory (StreamedClipMemory& memory, memory::Allocator& alloc)
+{
+ alloc.Deallocate(memory.caches);
+}
+
+void DestroyStreamedClip (StreamedClip& clip, memory::Allocator& alloc)
+{
+ alloc.Deallocate(clip.data);
+}
+
+}
+}
diff --git a/Runtime/mecanim/animation/streamedclip.h b/Runtime/mecanim/animation/streamedclip.h
new file mode 100644
index 0000000..93e1fc7
--- /dev/null
+++ b/Runtime/mecanim/animation/streamedclip.h
@@ -0,0 +1,93 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+
+
+namespace mecanim
+{
+namespace animation
+{
+
+// StreamedClip is a stream oriented clip format.
+// It stores hermite coefficients directly. Multiple curves are stored in the same stream. Keys for all curves are sorted by time.
+// This drastically reduces cache misses. In the best case we can sample a clip with a single cache miss. Clip data is read completely linearly.
+
+// Instead of laying out the animationclip by curves containing any array of keys.
+// We sort all keys of all curves by time and each key has an index.
+// This is a streamed format basically CurveTimeData defines the time value and the number of keys at this time.
+// Then CurveTimeData.count CurveKey elements will follow.
+
+// time = 0
+// CurveTimeData(4) ... CurveKey . CurveKey . CurveKey . CurveKey
+
+// time = 0.2
+// CurveTimeData(1) ... CurveKey
+
+// Sampling is separated into two functions.
+// 1. Seeking, it is responsible for updating the cached in the StreamedClipMemory to ensure each curve Index has an up to date time and hermite cofficients.
+// 2. Evaluating the caches. This simply evaluates the hermite caches
+
+// See StreamedClipBuilder.cpp and AnimationClip.cpp on how to build the streamed clip data.
+
+struct StreamedCacheItem
+{
+ float time;
+ float coeff[4];
+};
+
+struct StreamedClipMemory
+{
+ StreamedCacheItem* caches;
+ int cacheCount;
+ float time;
+ UInt32 readByteOffset;
+};
+
+struct StreamedClip
+{
+ uint32_t dataSize;
+ OffsetPtr<uint32_t> data;
+ UInt32 curveCount;
+
+
+ StreamedClip () : curveCount (0),dataSize(0) { }
+
+ DEFINE_GET_TYPESTRING(StreamedClip)
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(dataSize);
+ MANUAL_ARRAY_TRANSFER2(uint32_t, data, dataSize);
+ TRANSFER(curveCount);
+ }
+};
+
+struct CurveTimeData
+{
+ float time;
+ UInt32 count;
+ // This is implicitly followed by CurveKey in the stream
+};
+
+struct CurveKey
+{
+ int curveIndex;
+ float coeff[4];
+};
+
+
+// Sample functions
+void SampleClip (const StreamedClip& curveData, StreamedClipMemory& cache, float time, float* output);
+float SampleClipAtIndex (const StreamedClip& curveData, StreamedClipMemory& cache, int index, float time);
+
+
+// Creation & Destruction
+// StreamedClipBuilder.h actually creates the streamed clip from an array of AnimationCurve.
+void CreateStreamedClipMemory (const StreamedClip& clip, StreamedClipMemory& memory, memory::Allocator& alloc);
+void DestroyStreamedClipMemory (StreamedClipMemory& memory, memory::Allocator& alloc);
+void DestroyStreamedClip (StreamedClip& clip, memory::Allocator& alloc);
+
+}
+} \ No newline at end of file
diff --git a/Runtime/mecanim/bind.h b/Runtime/mecanim/bind.h
new file mode 100644
index 0000000..5e1fd07
--- /dev/null
+++ b/Runtime/mecanim/bind.h
@@ -0,0 +1,833 @@
+#pragma once
+
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/types.h"
+
+// Value binders
+// inspiration come from C++ template, The complete guide
+//
+// Bound function parameters to a specific value.
+// We do support first and second parameter bounding.
+//
+//
+
+namespace mecanim
+{
+ template<typename rt, typename p1 = void,
+ typename p2 = void,
+ typename p3 = void,
+ typename p4 = void,
+ typename p5 = void,
+ typename p6 = void>
+ class function_ptr_type
+ {
+ public:
+ enum {numParams = 6};
+ typedef rt (*function_type)(p1, p2, p3, p4, p5, p6);
+ };
+
+ // partial specialization for 5 parameter
+ template<typename rt, typename p1,
+ typename p2,
+ typename p3,
+ typename p4,
+ typename p5>
+ class function_ptr_type<rt, p1, p2, p3, p4, p5, void>
+ {
+ public:
+ enum {numParams = 5};
+ typedef rt (*function_type)(p1, p2, p3, p4, p5);
+ };
+
+ // partial specialization for 4 parameter
+ template<typename rt, typename p1,
+ typename p2,
+ typename p3,
+ typename p4>
+ class function_ptr_type<rt, p1, p2, p3, p4, void, void>
+ {
+ public:
+ enum {numParams = 4};
+ typedef rt (*function_type)(p1, p2, p3, p4);
+ };
+
+ // partial specialization for 3 parameter
+ template<typename rt, typename p1,
+ typename p2,
+ typename p3>
+ class function_ptr_type<rt, p1, p2, p3, void, void, void>
+ {
+ public:
+ enum {numParams = 3};
+ typedef rt (*function_type)(p1, p2, p3);
+ };
+
+ // partial specialization for 2 parameter
+ template<typename rt, typename p1,
+ typename p2>
+ class function_ptr_type<rt, p1, p2, void, void, void, void>
+ {
+ public:
+ enum {numParams = 2};
+ typedef rt (*function_type)(p1, p2);
+ };
+
+ // partial specialization for 1 parameter
+ template<typename rt, typename p1>
+ class function_ptr_type<rt, p1, void, void, void, void, void>
+ {
+ public:
+ enum {numParams = 1};
+ typedef rt (*function_type)(p1);
+ };
+
+ // partial specialization for 0 parameter
+ template<typename rt>
+ class function_ptr_type<rt, void, void, void, void, void, void>
+ {
+ public:
+ enum {numParams = 0};
+ typedef rt (*function_type)();
+ };
+
+ template<typename T>
+ class forwardparam
+ {
+ public:
+ // class type are passed by copy,
+ // thus invoke the copy constructor
+ // In this case we should use a const reference
+ typedef T type;
+ };
+
+ template<>
+ class forwardparam<void>
+ {
+ private:
+ class empty{};
+ public:
+ typedef empty type;
+ };
+
+ // The class function_ptr encapsulate a function pointer,
+ // passing function call argument can have a side effect
+ // if the corresponding parameter has a class type, it's copy constructor
+ // is invoked. To avoid this extra cost we need to change
+ // the class forwardparam, in this case a reference to the corresponding
+ // const class should be use.
+ template<typename rt, typename p1 = void,
+ typename p2 = void,
+ typename p3 = void,
+ typename p4 = void,
+ typename p5 = void,
+ typename p6 = void>
+ class function_ptr
+ {
+ private:
+ typedef typename function_ptr_type<rt, p1, p2, p3, p4, p5, p6>::function_type function_type;
+ function_type mFuntionPtr;
+ public:
+ enum { numParams = function_ptr_type<rt, p1, p2, p3, p4, p5, p6>::numParams };
+ typedef rt return_type;
+ typedef p1 parameter_type1;
+ typedef p2 parameter_type2;
+ typedef p3 parameter_type3;
+ typedef p4 parameter_type4;
+ typedef p5 parameter_type5;
+ typedef p6 parameter_type6;
+
+ function_ptr():mFuntionPtr(0) {}
+ function_ptr(function_type ptr):mFuntionPtr(ptr) {}
+
+ return_type operator()() { return mFuntionPtr(); }
+ return_type operator()(typename forwardparam<parameter_type1>::type a1)
+ {
+ return mFuntionPtr(a1);
+ }
+ return_type operator()(typename forwardparam<parameter_type1>::type a1,
+ typename forwardparam<parameter_type2>::type a2)
+ {
+ return mFuntionPtr(a1, a2);
+ }
+ return_type operator()(typename forwardparam<parameter_type1>::type a1,
+ typename forwardparam<parameter_type2>::type a2,
+ typename forwardparam<parameter_type3>::type a3)
+ {
+ return mFuntionPtr(a1, a2, a3);
+ }
+ return_type operator()(typename forwardparam<parameter_type1>::type a1,
+ typename forwardparam<parameter_type2>::type a2,
+ typename forwardparam<parameter_type3>::type a3,
+ typename forwardparam<parameter_type4>::type a4)
+ {
+ return mFuntionPtr(a1, a2, a3, a4);
+ }
+ return_type operator()(typename forwardparam<parameter_type1>::type a1,
+ typename forwardparam<parameter_type2>::type a2,
+ typename forwardparam<parameter_type3>::type a3,
+ typename forwardparam<parameter_type4>::type a4,
+ typename forwardparam<parameter_type5>::type a5)
+ {
+ return mFuntionPtr(a1, a2, a3, a4, a5);
+ }
+ return_type operator()(typename forwardparam<parameter_type1>::type a1,
+ typename forwardparam<parameter_type2>::type a2,
+ typename forwardparam<parameter_type3>::type a3,
+ typename forwardparam<parameter_type4>::type a4,
+ typename forwardparam<parameter_type5>::type a5,
+ typename forwardparam<parameter_type6>::type a6)
+ {
+ return mFuntionPtr(a1, a2, a3, a4, a5, a6);
+ }
+ };
+
+ template<typename signature> class function;
+
+ template<typename rt>
+ class function<rt (void)>
+ {
+ public:
+ enum {numParams = 0};
+ typedef rt return_type;
+ typedef rt (*function_type)();
+ typedef function_ptr<rt,void,void,void,void,void,void> ptr;
+ };
+
+ template<typename rt, typename p1>
+ class function<rt (p1)>
+ {
+ public:
+ enum {numParams = 1};
+ typedef rt return_type;
+ typedef p1 parameter_type1;
+ typedef rt (*function_type) (p1);
+ typedef function_ptr<rt, p1,void,void,void,void,void> ptr;
+ };
+
+ template<typename rt, typename p1, typename p2>
+ class function<rt (p1, p2)>
+ {
+ public:
+ enum {numParams = 2};
+ typedef rt return_type;
+ typedef p1 parameter_type1;
+ typedef p2 parameter_type2;
+ typedef rt (*function_type) (p1, p2);
+ typedef function_ptr<rt, p1, p2,void,void,void,void> ptr;
+ };
+
+ template<typename rt, typename p1, typename p2, typename p3>
+ class function<rt (p1, p2, p3)>
+ {
+ public:
+ enum {numParams = 3};
+ typedef rt return_type;
+ typedef p1 parameter_type1;
+ typedef p2 parameter_type2;
+ typedef p3 parameter_type3;
+ typedef rt (*function_type) (p1, p2, p3);
+ typedef function_ptr<rt, p1, p2, p3,void,void,void> ptr;
+ };
+
+ template<typename rt, typename p1, typename p2, typename p3, typename p4>
+ class function<rt (p1, p2, p3, p4)>
+ {
+ public:
+ enum {numParams = 4};
+ typedef rt return_type;
+ typedef p1 parameter_type1;
+ typedef p2 parameter_type2;
+ typedef p3 parameter_type3;
+ typedef p4 parameter_type4;
+ typedef rt (*function_type) (p1, p2, p3, p4);
+ typedef function_ptr<rt, p1, p2, p3, p4,void,void> ptr;
+ };
+
+ template<typename rt, typename p1, typename p2, typename p3, typename p4, typename p5>
+ class function<rt (p1, p2, p3, p4, p5)>
+ {
+ public:
+ enum {numParams = 5};
+ typedef rt return_type;
+ typedef p1 parameter_type1;
+ typedef p2 parameter_type2;
+ typedef p3 parameter_type3;
+ typedef p4 parameter_type4;
+ typedef p5 parameter_type5;
+ typedef rt (*function_type) (p1, p2, p3, p4, p5);
+ typedef function_ptr<rt, p1, p2, p3, p4, p5,void> ptr;
+ };
+
+ template<typename rt, typename p1, typename p2, typename p3, typename p4, typename p5, typename p6>
+ class function<rt (p1, p2, p3, p4, p5, p6)>
+ {
+ public:
+ enum {numParams = 6};
+ typedef rt return_type;
+ typedef p1 parameter_type1;
+ typedef p2 parameter_type2;
+ typedef p3 parameter_type3;
+ typedef p4 parameter_type4;
+ typedef p5 parameter_type5;
+ typedef p6 parameter_type6;
+ typedef rt (*function_type) (p1, p2, p3, p4, p5, p6);
+ typedef function_ptr<rt, p1, p2, p3, p4, p5, p6> ptr;
+ };
+
+ template <typename t> class bound_value_1
+ {
+ public:
+ typedef t value_type;
+ bound_value_1(){}
+ bound_value_1(value_type v):value(v){}
+ value_type get(){return value;}
+ private:
+ value_type value;
+ };
+ template <typename t> class bound_value_2
+ {
+ public:
+ typedef t value_type;
+ bound_value_2(){}
+ bound_value_2(value_type v):value(v){}
+ value_type get(){return value;}
+ private:
+ value_type value;
+ };
+ template <typename t> class bound_value_3
+ {
+ public:
+ typedef t value_type;
+ bound_value_3(){}
+ bound_value_3(value_type v):value(v){}
+ value_type get(){return value;}
+ private:
+ value_type value;
+ };
+ template <typename t> class bound_value_4
+ {
+ public:
+ typedef t value_type;
+ bound_value_4(){}
+ bound_value_4(value_type v):value(v){}
+ value_type get(){return value;}
+ private:
+ value_type value;
+ };
+ template <typename t> class bound_value_5
+ {
+ public:
+ typedef t value_type;
+ bound_value_5(){}
+ bound_value_5(value_type v):value(v){}
+ value_type get(){return value;}
+ private:
+ value_type value;
+ };
+ template <typename t> class bound_value_6
+ {
+ public:
+ typedef t value_type;
+ bound_value_6(){}
+ bound_value_6(value_type v):value(v){}
+ value_type get(){return value;}
+ private:
+ value_type value;
+ };
+
+
+ template<typename ft, typename p1> class binder1 : private ft, private bound_value_1<p1>
+ {
+ public:
+ enum { numParams = ft::numParams - 1 };
+
+ typedef typename ft::return_type return_type;
+ typedef typename forwardparam<typename ft::parameter_type2>::type parameter_type1;
+ typedef typename forwardparam<typename ft::parameter_type3>::type parameter_type2;
+ typedef typename forwardparam<typename ft::parameter_type4>::type parameter_type3;
+ typedef typename forwardparam<typename ft::parameter_type5>::type parameter_type4;
+ typedef typename forwardparam<typename ft::parameter_type6>::type parameter_type5;
+
+ binder1(ft f, bound_value_1<p1> const& value1)
+ :ft(f),
+ bound_value_1<p1>(value1)
+ {
+ }
+
+ binder1(ft f, p1 const& value1)
+ :ft(f),
+ bound_value_1<p1>(bound_value_1<p1>(value1))
+ {
+ }
+
+ return_type operator()()
+ {
+ return ft::operator()(bound_value_1<p1>::get());
+ }
+
+ return_type operator()(parameter_type1 a1)
+ {
+ return ft::operator()(bound_value_1<p1>::get(), a1);
+ }
+
+ return_type operator()(parameter_type1 a1, parameter_type2 a2)
+ {
+ return ft::operator()(bound_value_1<p1>::get(), a1, a2);
+ }
+
+ return_type operator()(parameter_type1 a1, parameter_type2 a2, parameter_type3 a3)
+ {
+ return ft::operator()(bound_value_1<p1>::get(), a1, a2, a3);
+ }
+
+ return_type operator()(parameter_type1 a1, parameter_type2 a2, parameter_type3 a3, parameter_type4 a4)
+ {
+ return ft::operator()(bound_value_1<p1>::get(), a1, a2, a3, a4);
+ }
+
+ return_type operator()(parameter_type1 a1, parameter_type2 a2, parameter_type3 a3, parameter_type4 a4, parameter_type5 a5)
+ {
+ return ft::operator()(bound_value_1<p1>::get(), a1, a2, a3, a4, a5);
+ }
+ };
+
+
+ template<typename ft, typename p1, typename p2> class binder2 : private ft, private bound_value_1<p1>, private bound_value_2<p2>
+ {
+ public:
+ enum { numParams = ft::numParams - 2 };
+
+ typedef typename ft::return_type return_type;
+ typedef typename forwardparam<typename ft::parameter_type3>::type parameter_type1;
+ typedef typename forwardparam<typename ft::parameter_type4>::type parameter_type2;
+ typedef typename forwardparam<typename ft::parameter_type5>::type parameter_type3;
+ typedef typename forwardparam<typename ft::parameter_type6>::type parameter_type4;
+
+ binder2()
+ {
+ }
+
+ binder2(ft f, bound_value_1<p1> const& value1, bound_value_2<p2> const& value2)
+ :ft(f),
+ bound_value_1<p1>(value1),
+ bound_value_2<p2>(value2)
+ {
+ }
+
+ binder2(ft f, p1 const& value1, p2 const& value2)
+ :ft(f),
+ bound_value_1<p1>(bound_value_1<p1>(value1)),
+ bound_value_2<p2>(bound_value_2<p2>(value2))
+ {
+ }
+
+ return_type operator()()
+ {
+ return ft::operator()(bound_value_1<p1>::get(), bound_value_2<p2>::get());
+ }
+
+ return_type operator()(parameter_type1 a1)
+ {
+ return ft::operator()(bound_value_1<p1>::get(), bound_value_2<p2>::get(), a1);
+ }
+
+ return_type operator()(parameter_type1 a1, parameter_type2 a2)
+ {
+ return ft::operator()(bound_value_1<p1>::get(), bound_value_2<p2>::get(), a1, a2);
+ }
+
+ return_type operator()(parameter_type1 a1, parameter_type2 a2, parameter_type3 a3)
+ {
+ return ft::operator()(bound_value_1<p1>::get(), bound_value_2<p2>::get(), a1, a2, a3);
+ }
+
+ return_type operator()(parameter_type1 a1, parameter_type2 a2, parameter_type3 a3, parameter_type4 a4)
+ {
+ return ft::operator()(bound_value_1<p1>::get(), bound_value_2<p2>::get(), a1, a2, a3, a4);
+ }
+ };
+
+ template<typename ft,
+ typename p1,
+ typename p2,
+ typename p3>
+ class binder3 : private ft,
+ private bound_value_1<p1>,
+ private bound_value_2<p2>,
+ private bound_value_3<p3>
+ {
+ public:
+ enum { numParams = ft::numParams - 3 };
+
+ typedef typename forwardparam<typename ft::parameter_type4>::type parameter_type1;
+ typedef typename forwardparam<typename ft::parameter_type5>::type parameter_type2;
+ typedef typename forwardparam<typename ft::parameter_type6>::type parameter_type3;
+
+ typedef typename ft::return_type return_type;
+
+ binder3()
+ {
+ }
+
+ binder3(ft f, bound_value_1<p1> const& value1, bound_value_2<p2> const& value2,
+ bound_value_3<p3> const& value3)
+ :ft(f),
+ bound_value_1<p1>(value1),
+ bound_value_2<p2>(value2),
+ bound_value_3<p3>(value3)
+ {
+ }
+
+ binder3(ft f, p1 const& value1, p2 const& value2, p3 const& value3)
+ :ft(f),
+ bound_value_1<p1>(bound_value_1<p1>(value1)),
+ bound_value_2<p2>(bound_value_2<p2>(value2)),
+ bound_value_3<p3>(bound_value_3<p3>(value3))
+ {
+ }
+
+ return_type operator()()
+ {
+ return ft::operator()(bound_value_1<p1>::get(), bound_value_2<p2>::get(),
+ bound_value_3<p3>::get());
+ }
+
+ return_type operator()(parameter_type1 a1)
+ {
+ return ft::operator()(bound_value_1<p1>::get(), bound_value_2<p2>::get(),
+ bound_value_3<p3>::get(), a1);
+ }
+
+ return_type operator()(parameter_type1 a1, parameter_type2 a2)
+ {
+ return ft::operator()(bound_value_1<p1>::get(), bound_value_2<p2>::get(),
+ bound_value_3<p3>::get(), a1, a2);
+ }
+
+ return_type operator()(parameter_type1 a1, parameter_type2 a2, parameter_type3 a3)
+ {
+ return ft::operator()(bound_value_1<p1>::get(), bound_value_2<p2>::get(),
+ bound_value_3<p3>::get(), a1, a2, a3);
+ }
+ };
+
+
+ template<typename ft,
+ typename p1,
+ typename p2,
+ typename p3,
+ typename p4>
+ class binder4 : private ft,
+ private bound_value_1<p1>,
+ private bound_value_2<p2>,
+ private bound_value_3<p3>,
+ private bound_value_4<p4>
+ {
+ public:
+ enum { numParams = ft::numParams - 4 };
+
+ typedef typename forwardparam<typename ft::parameter_type5>::type parameter_type1;
+ typedef typename forwardparam<typename ft::parameter_type6>::type parameter_type2;
+
+ typedef typename ft::return_type return_type;
+
+ binder4()
+ {
+ }
+
+ binder4(ft f, bound_value_1<p1> const& value1, bound_value_2<p2> const& value2,
+ bound_value_3<p3> const& value3, bound_value_4<p4> const& value4)
+ :ft(f),
+ bound_value_1<p1>(value1),
+ bound_value_2<p2>(value2),
+ bound_value_3<p3>(value3),
+ bound_value_4<p4>(value4)
+ {
+ }
+
+ binder4(ft f, p1 const& value1, p2 const& value2, p3 const& value3, p4 const& value4)
+ :ft(f),
+ bound_value_1<p1>(bound_value_1<p1>(value1)),
+ bound_value_2<p2>(bound_value_2<p2>(value2)),
+ bound_value_3<p3>(bound_value_3<p3>(value3)),
+ bound_value_4<p4>(bound_value_4<p4>(value4))
+ {
+ }
+
+ return_type operator()()
+ {
+ return ft::operator()(bound_value_1<p1>::get(), bound_value_2<p2>::get(),
+ bound_value_3<p3>::get(), bound_value_4<p4>::get());
+ }
+
+ return_type operator()(parameter_type1 a1)
+ {
+ return ft::operator()(bound_value_1<p1>::get(), bound_value_2<p2>::get(),
+ bound_value_3<p3>::get(), bound_value_4<p4>::get(), a1);
+ }
+
+ return_type operator()(parameter_type1 a1, parameter_type2 a2)
+ {
+ return ft::operator()(bound_value_1<p1>::get(), bound_value_2<p2>::get(),
+ bound_value_3<p3>::get(), bound_value_4<p4>::get(), a1, a2);
+ }
+ };
+
+
+ template<typename ft,
+ typename p1,
+ typename p2,
+ typename p3,
+ typename p4,
+ typename p5>
+ class binder5 : private ft,
+ private bound_value_1<p1>,
+ private bound_value_2<p2>,
+ private bound_value_3<p3>,
+ private bound_value_4<p4>,
+ private bound_value_5<p5>
+ {
+ public:
+ enum { numParams = ft::numParams - 5 };
+
+ typedef typename forwardparam<typename ft::parameter_type6>::type parameter_type1;
+
+ typedef typename ft::return_type return_type;
+
+ binder5()
+ {
+ }
+
+ binder5(ft f, bound_value_1<p1> const& value1, bound_value_2<p2> const& value2,
+ bound_value_3<p3> const& value3, bound_value_4<p4> const& value4,
+ bound_value_5<p5> const& value5)
+ :ft(f),
+ bound_value_1<p1>(value1),
+ bound_value_2<p2>(value2),
+ bound_value_3<p3>(value3),
+ bound_value_4<p4>(value4),
+ bound_value_5<p5>(value5)
+ {
+ }
+
+ binder5(ft f, p1 const& value1, p2 const& value2, p3 const& value3, p4 const& value4, p5 const& value5)
+ :ft(f),
+ bound_value_1<p1>(bound_value_1<p1>(value1)),
+ bound_value_2<p2>(bound_value_2<p2>(value2)),
+ bound_value_3<p3>(bound_value_3<p3>(value3)),
+ bound_value_4<p4>(bound_value_4<p4>(value4)),
+ bound_value_5<p5>(bound_value_5<p5>(value5))
+ {
+ }
+
+ return_type operator()()
+ {
+ return ft::operator()(bound_value_1<p1>::get(), bound_value_2<p2>::get(),
+ bound_value_3<p3>::get(), bound_value_4<p4>::get(),
+ bound_value_5<p5>::get());
+ }
+
+ return_type operator()(parameter_type1 a1)
+ {
+ return ft::operator()(bound_value_1<p1>::get(), bound_value_2<p2>::get(),
+ bound_value_3<p3>::get(), bound_value_4<p4>::get(),
+ bound_value_5<p5>::get(), a1);
+ }
+ };
+
+ template<typename ft,
+ typename p1,
+ typename p2,
+ typename p3,
+ typename p4,
+ typename p5,
+ typename p6>
+ class binder6 : private ft,
+ private bound_value_1<p1>,
+ private bound_value_2<p2>,
+ private bound_value_3<p3>,
+ private bound_value_4<p4>,
+ private bound_value_5<p5>,
+ private bound_value_6<p6>
+ {
+ public:
+ enum { numParams = ft::numParams - 6 };
+
+ typedef typename ft::return_type return_type;
+
+ binder6()
+ {
+ }
+
+ binder6(ft f, bound_value_1<p1> const& value1, bound_value_2<p2> const& value2,
+ bound_value_3<p3> const& value3, bound_value_4<p4> const& value4,
+ bound_value_5<p5> const& value5, bound_value_6<p6> const& value6)
+ :ft(f),
+ bound_value_1<p1>(value1),
+ bound_value_2<p2>(value2),
+ bound_value_3<p3>(value3),
+ bound_value_4<p4>(value4),
+ bound_value_5<p5>(value5),
+ bound_value_6<p6>(value6)
+ {
+ }
+
+ binder6(ft f, p1 const& value1, p2 const& value2, p3 const& value3, p4 const& value4, p5 const& value5, p6 const& value6)
+ :ft(f),
+ bound_value_1<p1>(bound_value_1<p1>(value1)),
+ bound_value_2<p2>(bound_value_2<p2>(value2)),
+ bound_value_3<p3>(bound_value_3<p3>(value3)),
+ bound_value_4<p4>(bound_value_4<p4>(value4)),
+ bound_value_5<p5>(bound_value_5<p5>(value5)),
+ bound_value_6<p6>(bound_value_6<p6>(value6))
+ {
+ }
+
+ return_type operator()()
+ {
+ return ft::operator()(bound_value_1<p1>::get(), bound_value_2<p2>::get(),
+ bound_value_3<p3>::get(), bound_value_4<p4>::get(),
+ bound_value_5<p5>::get(), bound_value_6<p6>::get());
+ }
+ };
+
+ template<typename rt, typename a1>
+ binder1<function_ptr<rt, a1>, a1 > bind(rt (*function)(a1), a1 value1)
+ {
+ typedef function_ptr<rt, a1> function_type;
+ return binder1<function_type, a1 >(function, value1);
+ }
+ template<typename rt, typename a1, typename a2>
+ binder1<function_ptr<rt, a1, a2>, a1 > bind(rt (*function)(a1, a2), a1 value1)
+ {
+ typedef function_ptr<rt, a1, a2> function_type;
+ return binder1<function_type, a1 >(function, value1);
+ }
+ template<typename rt, typename a1, typename a2, typename a3>
+ binder1<function_ptr<rt, a1, a2, a3>, a1 > bind(rt (*function)(a1, a2,a3), a1 value1)
+ {
+ typedef function_ptr<rt, a1, a2, a3> function_type;
+ return binder1<function_type, a1 >(function, value1);
+ }
+ template<typename rt, typename a1, typename a2, typename a3, typename a4>
+ binder1<function_ptr<rt, a1, a2, a3, a4>, a1 > bind(rt (*function)(a1, a2, a3, a4), a1 value1)
+ {
+ typedef function_ptr<rt, a1, a2, a3, a4> function_type;
+ return binder1<function_type, a1 >(function, value1);
+ }
+ template<typename rt, typename a1, typename a2, typename a3, typename a4, typename a5>
+ binder1<function_ptr<rt, a1, a2, a3, a4, a5>, a1 > bind(rt (*function)(a1, a2, a3, a4, a5), a1 value1)
+ {
+ typedef function_ptr<rt, a1, a2, a3, a4, a5> function_type;
+ return binder1<function_type, a1 >(function, value1);
+ }
+ template<typename rt, typename a1, typename a2, typename a3, typename a4, typename a5, typename a6>
+ binder1<function_ptr<rt, a1, a2, a3, a4, a5, a6>, a1 > bind(rt (*function)(a1, a2, a3, a4, a5, a6), a1 value1)
+ {
+ typedef function_ptr<rt, a1, a2, a3, a4, a5, a6> function_type;
+ return binder1<function_type, a1 >(function, value1);
+ }
+
+ template<typename rt, typename a1, typename a2>
+ binder2<function_ptr<rt, a1, a2>, a1, a2 > bind(rt (*function)(a1, a2), a1 value1, a2 value2)
+ {
+ typedef function_ptr<rt, a1, a2> function_type;
+ return binder2<function_type, a1, a2 >(function, value1, value2);
+ }
+ template<typename rt, typename a1, typename a2, typename a3>
+ binder2<function_ptr<rt, a1, a2, a3>, a1, a2 > bind(rt (*function)(a1, a2, a3), a1 value1, a2 value2)
+ {
+ typedef function_ptr<rt, a1, a2, a3> function_type;
+ return binder2<function_type, a1, a2 >(function, value1, value2);
+ }
+ template<typename rt, typename a1, typename a2, typename a3, typename a4>
+ binder2<function_ptr<rt, a1, a2, a3, a4>, a1, a2 > bind(rt (*function)(a1, a2, a3, a4), a1 value1, a2 value2)
+ {
+ typedef function_ptr<rt, a1, a2, a3, a4> function_type;
+ return binder2<function_type, a1, a2 >(function, value1, value2);
+ }
+ template<typename rt, typename a1, typename a2, typename a3, typename a4, typename a5>
+ binder2<function_ptr<rt, a1, a2, a3, a4, a5>, a1, a2 > bind(rt (*function)(a1, a2, a3, a4, a5), a1 value1, a2 value2)
+ {
+ typedef function_ptr<rt, a1, a2, a3, a4, a5> function_type;
+ return binder2<function_type, a1, a2 >(function, value1, value2);
+ }
+ template<typename rt, typename a1, typename a2, typename a3, typename a4, typename a5, typename a6>
+ binder2<function_ptr<rt, a1, a2, a3, a4, a5, a6>, a1, a2 > bind(rt (*function)(a1, a2, a3, a4, a5, a6), a1 value1, a2 value2)
+ {
+ typedef function_ptr<rt, a1, a2, a3, a4, a5, a6> function_type;
+ return binder2<function_type, a1, a2 >(function, value1, value2);
+ }
+
+
+ template<typename rt, typename a1, typename a2, typename a3>
+ binder3<function_ptr<rt, a1, a2, a3>, a1, a2, a3 > bind(rt (*function)(a1, a2, a3), a1 value1, a2 value2, a3 value3)
+ {
+ typedef function_ptr<rt, a1, a2, a3> function_type;
+ return binder3<function_type, a1, a2, a3>(function, value1, value2, value3);
+ }
+
+ template<typename rt, typename a1, typename a2, typename a3, typename a4>
+ binder3<function_ptr<rt, a1, a2, a3, a4>, a1, a2, a3 > bind(rt (*function)(a1, a2, a3, a4), a1 value1, a2 value2, a3 value3)
+ {
+ typedef function_ptr<rt, a1, a2, a3, a4> function_type;
+ return binder3<function_type, a1, a2, a3>(function, value1, value2, value3);
+ }
+
+ template<typename rt, typename a1, typename a2, typename a3, typename a4, typename a5>
+ binder3<function_ptr<rt, a1, a2, a3, a4, a5>, a1, a2, a3 > bind(rt (*function)(a1, a2, a3, a4, a5), a1 value1, a2 value2, a3 value3)
+ {
+ typedef function_ptr<rt, a1, a2, a3, a4, a5> function_type;
+ return binder3<function_type, a1, a2, a3>(function, value1, value2, value3);
+ }
+
+ template<typename rt, typename a1, typename a2, typename a3, typename a4, typename a5, typename a6>
+ binder3<function_ptr<rt, a1, a2, a3, a4, a5, a6>, a1, a2, a3 > bind(rt (*function)(a1, a2, a3, a4, a5, a6), a1 value1, a2 value2, a3 value3)
+ {
+ typedef function_ptr<rt, a1, a2, a3, a4, a5, a6> function_type;
+ return binder3<function_type, a1, a2, a3>(function, value1, value2, value3);
+ }
+
+ template<typename rt, typename a1, typename a2, typename a3, typename a4>
+ binder4<function_ptr<rt, a1, a2, a3, a4>, a1, a2, a3, a4 > bind(rt (*function)(a1, a2, a3, a4), a1 value1, a2 value2, a3 value3, a4 value4)
+ {
+ typedef function_ptr<rt, a1, a2, a3, a4> function_type;
+ return binder4<function_type, a1, a2, a3, a4>(function, value1, value2, value3, value4);
+ }
+
+ template<typename rt, typename a1, typename a2, typename a3, typename a4, typename a5>
+ binder4<function_ptr<rt, a1, a2, a3, a4, a5>, a1, a2, a3, a4 > bind(rt (*function)(a1, a2, a3, a4, a5), a1 value1, a2 value2, a3 value3, a4 value4)
+ {
+ typedef function_ptr<rt, a1, a2, a3, a4, a5> function_type;
+ return binder4<function_type, a1, a2, a3, a4>(function, value1, value2, value3, value4);
+ }
+
+ template<typename rt, typename a1, typename a2, typename a3, typename a4, typename a5, typename a6>
+ binder4<function_ptr<rt, a1, a2, a3, a4, a5, a6>, a1, a2, a3, a4 > bind(rt (*function)(a1, a2, a3, a4, a5, a6), a1 value1, a2 value2, a3 value3, a4 value4)
+ {
+ typedef function_ptr<rt, a1, a2, a3, a4, a5, a6> function_type;
+ return binder4<function_type, a1, a2, a3, a4>(function, value1, value2, value3, value4);
+ }
+
+ template<typename rt, typename a1, typename a2, typename a3, typename a4, typename a5>
+ binder5<function_ptr<rt, a1, a2, a3, a4, a5>, a1, a2, a3, a4, a5 > bind(rt (*function)(a1, a2, a3, a4, a5), a1 value1, a2 value2, a3 value3, a4 value4, a5 value5)
+ {
+ typedef function_ptr<rt, a1, a2, a3, a4, a5> function_type;
+ return binder5<function_type, a1, a2, a3, a4, a5>(function, value1, value2, value3, value4, value5);
+ }
+
+ template<typename rt, typename a1, typename a2, typename a3, typename a4, typename a5, typename a6>
+ binder5<function_ptr<rt, a1, a2, a3, a4, a5, a6>, a1, a2, a3, a4, a5 > bind(rt (*function)(a1, a2, a3, a4, a5, a6), a1 value1, a2 value2, a3 value3, a4 value4, a5 value5)
+ {
+ typedef function_ptr<rt, a1, a2, a3, a4, a5, a6> function_type;
+ return binder5<function_type, a1, a2, a3, a4, a5>(function, value1, value2, value3, value4, value5);
+ }
+
+ template<typename rt, typename a1, typename a2, typename a3, typename a4, typename a5, typename a6>
+ binder6<function_ptr<rt, a1, a2, a3, a4, a5, a6>, a1, a2, a3, a4, a5, a6 > bind(rt (*function)(a1, a2, a3, a4, a5, a6), a1 value1, a2 value2, a3 value3, a4 value4, a5 value5, a6 value6)
+ {
+ typedef function_ptr<rt, a1, a2, a3, a4, a5, a6> function_type;
+ return binder6<function_type, a1, a2, a3, a4, a5, a6 >(function, value1, value2, value3, value4, value5, value6);
+ }
+}
diff --git a/Runtime/mecanim/bitset.h b/Runtime/mecanim/bitset.h
new file mode 100644
index 0000000..841e269
--- /dev/null
+++ b/Runtime/mecanim/bitset.h
@@ -0,0 +1,155 @@
+#pragma once
+
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/types.h"
+
+namespace mecanim
+{
+ template <uint32_t Bits> class bitset
+ {
+ public:
+ typedef uint32_t type;
+ enum {
+ digits = Bits,
+ // parameters for packing bits into words
+ Bitsperword = (int)(8 * sizeof(type)), // bits in each word
+ Words = (int)(Bits == 0 ? 0 : (Bits - 1) / Bitsperword)
+ };
+
+ bitset(type value=0)
+ {
+ init(value);
+ }
+
+ bitset<Bits>& set()
+ {
+ // set all bits true
+ init((type)~0);
+ return (*this);
+ }
+
+ bitset<Bits>& set(type pos, bool val = true)
+ {
+ // set bit at _Pos to _Val
+ if (pos < Bits)
+ {
+ if (val)
+ mArray[pos / Bitsperword] |= (type)1 << pos % Bitsperword;
+ else
+ mArray[pos / Bitsperword] &= ~((type)1 << pos % Bitsperword);
+ }
+ return (*this);
+ }
+
+ bitset<Bits>& reset()
+ {
+ // set all bits false
+ init();
+ return (*this);
+ }
+
+ bitset<Bits>& reset(type pos)
+ {
+ // set bit at pos to false
+ return set(pos, false);
+ }
+
+ bitset<Bits>& flip()
+ {
+ // flip all bits
+ for (int pos = Words; 0 <= pos; --pos)
+ mArray[pos] = (type) ~mArray[pos];
+
+ trim();
+ return *this;
+ }
+
+ bitset<Bits>& flip(type pos)
+ {
+ // flip bit at pos
+ if (pos < Bits)
+ mArray[pos / Bitsperword] ^= (type) 1 << pos % Bitsperword;
+ return (*this);
+ }
+
+ size_t count() const
+ {
+ // count number of set bits
+ static char Bitsperhex[] = "\0\1\1\2\1\2\2\3\1\2\2\3\2\3\3\4";
+ type val = 0;
+ for (int pos = Words; 0 <= pos; --pos)
+ for (type Wordval = mArray[pos]; Wordval != 0; Wordval >>= 4)
+ val += Bitsperhex[Wordval & 0xF];
+ return val;
+ }
+
+ type size() const
+ {
+ // return size of bitset
+ return (Bits);
+ }
+
+ bool test(uint32_t pos) const
+ {
+ // test if bit at pos is set
+ if (pos < Bits)
+ return ((mArray[pos / Bitsperword] & ((type)1 << pos % Bitsperword)) != 0);
+ return false;
+ }
+
+ bool any() const
+ {
+ // test if any bits are set
+ for (int pos = Words; 0 <= pos; --pos)
+ if (mArray[pos] != 0)
+ return true;
+ return false;
+ }
+
+ bool none() const
+ {
+ // test if no bits are set
+ return !any();
+ }
+
+ bool operator==(const bitset<Bits>& right) const
+ {
+ // test for bitset equality
+ for (int pos = Words; 0 <= pos; --pos)
+ if (mArray[pos] != right.word(pos))
+ return false;
+ return true;
+ }
+
+ type const& word(uint32_t pos)const
+ {
+ // get word at pos
+ return mArray[pos];
+ }
+
+ type& word(uint32_t pos)
+ {
+ // get word at pos
+ return mArray[pos];
+ }
+ protected:
+ void init(type value = 0)
+ {
+ // set all words to value
+ for (int pos = Words; 0 <= pos; --pos)
+ mArray[pos] = value;
+ if (value != 0)
+ trim();
+ }
+
+ void trim()
+ {
+ // clear any trailing bits in last word
+ if (Bits % Bitsperword != 0)
+ mArray[Words] &= ((type)1 << Bits % Bitsperword) - 1;
+ }
+
+ type mArray[Words+1];
+ };
+}
diff --git a/Runtime/mecanim/defs.h b/Runtime/mecanim/defs.h
new file mode 100644
index 0000000..734dc10
--- /dev/null
+++ b/Runtime/mecanim/defs.h
@@ -0,0 +1,116 @@
+#pragma once
+
+/***
+* defs.h - definitions/declarations for some commonly standard declaration
+*
+*
+* Purpose:
+* This file defines the following ma keywords:
+* RESTRICT
+* MECANIM_FORCE_INLINE
+* STATIC_INLINE
+* ATTRIBUTE_ALIGN(a)
+* EXPLICIT_TYPENAME
+* EXPLICIT_TEMPLATE
+* DLL_IMPORT
+* DLL_EXPORT
+* DECLARE_C
+*
+****/
+
+#if defined(__INTEL_COMPILER) || defined(__ICL)
+ #include <cstddef>
+ #define RESTRICT __restrict
+ #define MECANIM_FORCE_INLINE __forceinline
+ #define ATTRIBUTE_ALIGN(a) __declspec(align(a))
+ #define EXPLICIT_TEMPLATE template
+ #define DLL_IMPORT __declspec(dllimport)
+ #define DLL_EXPORT __declspec(dllexport)
+ #define ALIGN4F 16
+ #define DECLARE_C __cdecl
+
+#elif defined(_MSC_VER)
+ #include <cstddef>
+ #define RESTRICT __restrict
+ #define MECANIM_FORCE_INLINE __forceinline
+ #define ATTRIBUTE_ALIGN(a) __declspec(align(a))
+ #define EXPLICIT_TEMPLATE template
+ #define DLL_IMPORT __declspec(dllimport)
+ #define DLL_EXPORT __declspec(dllexport)
+ #define ALIGN4F 16
+ #define DECLARE_C __cdecl
+
+ #pragma warning( disable : 4996)
+
+#elif defined(__GNUC__)
+ #include <cstddef>
+ #if ((__GNUC__ >= 3) && (__GNUC_MINOR__ >= 1)) || (__GNUC__ >= 4)
+ #ifdef _DEBUG
+ #ifndef MECANIM_FORCE_INLINE
+ #define MECANIM_FORCE_INLINE inline
+ #endif
+ #define STATIC_INLINE inline
+ #else
+ #ifndef MECANIM_FORCE_INLINE
+ #define MECANIM_FORCE_INLINE inline __attribute__((always_inline))
+ #endif
+ #define STATIC_INLINE inline __attribute__((always_inline))
+ #endif
+ #else
+ #define STATIC_INLINE extern inline
+ #endif
+
+ #define ATTRIBUTE_ALIGN(a) __attribute__ ((aligned(a)))
+ #define ALIGN4F 16
+
+ #if ((__GNUC__ >= 3) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ >= 4)
+ #define EXPLICIT_TEMPLATE template
+ #endif
+#endif
+
+#if defined(__GNUC__) && ((__GNUC__ <= 4) && (__GNUC_MINOR__ <= 2))
+ #define TEMPLATE_SPEC(L, R) template<L,R>
+#else
+ #define TEMPLATE_SPEC(L, R) template<>
+#endif
+
+#ifndef RESTRICT
+ #define RESTRICT
+#endif
+
+#ifndef MECANIM_FORCE_INLINE
+ #define MECANIM_FORCE_INLINE inline
+#endif
+
+#ifndef STATIC_INLINE
+ #define STATIC_INLINE static inline
+#endif
+
+#ifndef ATTRIBUTE_ALIGN
+ #define ATTRIBUTE_ALIGN(a)
+#endif
+
+#ifndef EXPLICIT_TYPENAME
+ #define EXPLICIT_TYPENAME typename
+#endif
+
+#ifndef EXPLICIT_TEMPLATE
+ #define EXPLICIT_TEMPLATE
+#endif
+
+#ifndef DLL_IMPORT
+ #define DLL_IMPORT
+#endif
+
+#ifndef DLL_EXPORT
+ #define DLL_EXPORT
+#endif
+
+#ifndef ALIGN4F
+ #define ALIGN4F 16
+#endif
+
+#ifndef DECLARE_C
+ #define DECLARE_C
+#endif
+
diff --git a/Runtime/mecanim/generic/crc32.h b/Runtime/mecanim/generic/crc32.h
new file mode 100644
index 0000000..ca1797b
--- /dev/null
+++ b/Runtime/mecanim/generic/crc32.h
@@ -0,0 +1,208 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/string.h"
+
+namespace mecanim
+{
+ template < std::size_t Bits > struct reflector
+ {
+ typedef typename mecanim::uint_t<Bits>::value_type value_type;
+ static value_type reflect( value_type x );
+ };
+
+ // Function that reflects its argument
+ template < std::size_t Bits > typename reflector<Bits>::value_type reflector<Bits>::reflect( typename reflector<Bits>::value_type x)
+ {
+ value_type reflection = 0;
+ value_type const one = 1;
+
+ for ( std::size_t i = 0 ; i < Bits ; ++i, x >>= 1 )
+ {
+ if ( x & one )
+ {
+ reflection |= ( one << (Bits - 1u - i) );
+ }
+ }
+
+ return reflection;
+ }
+
+ template <uint32_t TruncPoly> struct crc32_table_t
+ {
+ static const uint32_t byte_combos = (1ul << CHAR_BIT);
+
+ typedef uint32_t value_type;
+ typedef value_type table_type[byte_combos];
+
+ static void init_table();
+
+ static table_type table;
+ static bool isInitialized;
+ };
+
+ template <uint32_t TruncPoly> typename crc32_table_t<TruncPoly>::table_type crc32_table_t<TruncPoly>::table = { 0 };
+ template <uint32_t TruncPoly> bool crc32_table_t<TruncPoly>::isInitialized = false;
+
+ // Populate CRC lookup table
+ template<uint32_t TruncPoly> void crc32_table_t<TruncPoly>::init_table()
+ {
+ // factor-out constants to avoid recalculation
+ value_type const fast_hi_bit = 1ul << ( 32 - 1u );
+ unsigned char const byte_hi_bit = 1u << (CHAR_BIT - 1u);
+
+ // loop over every possible dividend value
+ unsigned char dividend = 0;
+ do
+ {
+ value_type remainder = 0;
+
+ // go through all the dividend's bits
+ for ( unsigned char mask = byte_hi_bit ; mask ; mask >>= 1 )
+ {
+ // check if divisor fits
+ if ( dividend & mask )
+ {
+ remainder ^= fast_hi_bit;
+ }
+
+ // do polynominal division
+ if ( remainder & fast_hi_bit )
+ {
+ remainder <<= 1;
+ remainder ^= TruncPoly;
+ }
+ else
+ {
+ remainder <<= 1;
+ }
+ }
+
+ table[ reflector<CHAR_BIT>::reflect(dividend) ] = reflector<32>::reflect( remainder );
+ }
+ while ( ++dividend );
+
+ isInitialized = true;
+ }
+
+ class crc32
+ {
+ public:
+ // Type
+ typedef uint32_t value_type;
+
+ // Constants for the template parameters
+ static const std::size_t bit_count = 32;
+ static const value_type truncated_polynominal = 0x04C11DB7;
+ static const value_type initial_remainder = 0xFFFFFFFF;
+ static const value_type final_xor_value = 0xFFFFFFFF;
+
+ // Constructor
+ explicit crc32( value_type init_rem = crc32::initial_remainder ):rem(reflector<bit_count>::reflect(init_rem)){}
+
+ inline void process_block(void const *bytes_begin, void const *bytes_end);
+ inline void process_block_skip2(void const * bytes_begin, void const * bytes_end);
+
+ inline void process_bytes(void const *buffer, std::size_t byte_count );
+ inline void process_bytes_skip2(void const * buffer, std::size_t byte_count);
+
+ inline value_type checksum() const;
+
+ typedef crc32_table_t<truncated_polynominal> crc32_table_type;
+
+ protected:
+
+ inline value_type get_truncated_polynominal() const{ return truncated_polynominal; }
+ inline value_type get_initial_remainder() const{return initial_remainder;}
+ inline value_type get_final_xor_value() const{return final_xor_value;}
+
+ static unsigned char index( value_type rem, unsigned char x ){ return static_cast<unsigned char>(x ^ rem); }
+
+ // Shift out the remainder's highest byte
+ static value_type shift( value_type rem ){ return rem >> CHAR_BIT; }
+
+ // Member data
+ value_type rem;
+ };
+
+ inline void crc32::process_block(void const * bytes_begin, void const * bytes_end)
+ {
+ Assert(crc32_table_type::isInitialized);
+
+ // Recompute the CRC for each byte passed
+ for ( unsigned char const * p = static_cast<unsigned char const *>(bytes_begin) ; p < bytes_end ; ++p )
+ {
+ // Compare the new byte with the remainder's higher bits to
+ // get the new bits, shift out the remainder's current higher
+ // bits, and update the remainder with the polynominal division
+ // of the new bits.
+ unsigned char const byte_index = index( rem, *p );
+ rem = shift( rem );
+ rem ^= crc32_table_type::table[ byte_index ];
+ }
+ }
+
+ inline void crc32::process_block_skip2(void const * bytes_begin, void const * bytes_end)
+ {
+ Assert(crc32_table_type::isInitialized);
+ unsigned char const * p;
+
+#if UNITY_BIG_ENDIAN
+ p = static_cast<unsigned char const *>(bytes_begin) + 1;
+#else
+ p = static_cast<unsigned char const *>(bytes_begin);
+#endif
+
+ // Recompute the CRC for every second byte passed. This is useful for hashing a UTF16 string that is known to actually be an ascii string.
+ for ( ; p < bytes_end ; p += 2 )
+ {
+ // Compare the new byte with the remainder's higher bits to
+ // get the new bits, shift out the remainder's current higher
+ // bits, and update the remainder with the polynominal division
+ // of the new bits.
+ unsigned char const byte_index = index( rem, *p );
+ rem = shift( rem );
+ rem ^= crc32_table_type::table[ byte_index ];
+ }
+ }
+
+
+ inline void crc32::process_bytes(void const * buffer, std::size_t byte_count)
+ {
+ unsigned char const * const b = static_cast<unsigned char const *>( buffer );
+ process_block( b, b + byte_count );
+ }
+
+ inline void crc32::process_bytes_skip2(void const * buffer, std::size_t byte_count)
+ {
+ unsigned char const * const b = static_cast<unsigned char const *>( buffer );
+ process_block_skip2( b, b + byte_count );
+ }
+
+ inline crc32::value_type crc32::checksum() const
+ {
+ return ( rem ^ get_final_xor_value() );
+ }
+
+ static inline int processCRC32(String const& string)
+ {
+ crc32 result;
+ result.process_bytes(string.c_str(), string.size());
+ return result.checksum();
+ }
+
+ static inline int processCRC32(char const* string)
+ {
+ crc32 result;
+ result.process_bytes(string, strlen(string));
+ return result.checksum();
+ }
+
+ static inline int processCRC32UTF16Ascii(unsigned short const* string, std::size_t stringLength)
+ {
+ crc32 result;
+ result.process_bytes_skip2(string, stringLength * 2);
+ return result.checksum();
+ }
+}
diff --git a/Runtime/mecanim/generic/stringtable.cpp b/Runtime/mecanim/generic/stringtable.cpp
new file mode 100644
index 0000000..85cf025
--- /dev/null
+++ b/Runtime/mecanim/generic/stringtable.cpp
@@ -0,0 +1,131 @@
+#include "UnityPrefix.h"
+#include "Runtime/mecanim/generic/stringtable.h"
+#include "Runtime/mecanim/generic/crc32.h"
+
+#define DEFINE_KEYWORD(s) table[mecanim::e##s] = mecanim::ReserveKeyword(mecanim::processCRC32(#s), #s)
+
+namespace
+{
+ mecanim::ReserveKeyword* InitTable()
+ {
+ static mecanim::ReserveKeyword table[mecanim::eLastString];
+
+ DEFINE_KEYWORD(T);
+ DEFINE_KEYWORD(Q);
+ DEFINE_KEYWORD(S);
+ DEFINE_KEYWORD(A);
+ DEFINE_KEYWORD(B);
+ DEFINE_KEYWORD(C);
+ DEFINE_KEYWORD(D);
+ DEFINE_KEYWORD(E);
+ DEFINE_KEYWORD(X);
+ DEFINE_KEYWORD(Y);
+ DEFINE_KEYWORD(Z);
+ DEFINE_KEYWORD(W);
+ DEFINE_KEYWORD(Result);
+ DEFINE_KEYWORD(Min);
+ DEFINE_KEYWORD(Max);
+ DEFINE_KEYWORD(Value);
+ DEFINE_KEYWORD(MinMin);
+ DEFINE_KEYWORD(MinMax);
+ DEFINE_KEYWORD(MaxMin);
+ DEFINE_KEYWORD(MaxMax);
+ DEFINE_KEYWORD(In);
+ DEFINE_KEYWORD(Out);
+ DEFINE_KEYWORD(RangeA);
+ DEFINE_KEYWORD(RangeB);
+ DEFINE_KEYWORD(RangeC);
+ DEFINE_KEYWORD(RangeD);
+ DEFINE_KEYWORD(RangeE);
+ DEFINE_KEYWORD(WeightA);
+ DEFINE_KEYWORD(WeightB);
+ DEFINE_KEYWORD(WeightC);
+ DEFINE_KEYWORD(WeightD);
+ DEFINE_KEYWORD(WeightE);
+ DEFINE_KEYWORD(OutA);
+ DEFINE_KEYWORD(OutB);
+ DEFINE_KEYWORD(OutC);
+ DEFINE_KEYWORD(OutD);
+ DEFINE_KEYWORD(OutE);
+ DEFINE_KEYWORD(Num);
+ DEFINE_KEYWORD(Den);
+ DEFINE_KEYWORD(Rem);
+ DEFINE_KEYWORD(DampTime);
+ DEFINE_KEYWORD(DeltaTime);
+ DEFINE_KEYWORD(PreviousValue);
+ DEFINE_KEYWORD(GravityWeight);
+ DEFINE_KEYWORD(SrcRefX);
+ DEFINE_KEYWORD(DstRefX);
+ DEFINE_KEYWORD(SrcPivotX);
+ DEFINE_KEYWORD(DstPivotX);
+ DEFINE_KEYWORD(RefWeight);
+ DEFINE_KEYWORD(PivotWeight);
+ DEFINE_KEYWORD(XI);
+ DEFINE_KEYWORD(XO);
+ DEFINE_KEYWORD(Condition);
+ DEFINE_KEYWORD(StateTime);
+ DEFINE_KEYWORD(StateSpeed);
+ DEFINE_KEYWORD(StateExitTime);
+ DEFINE_KEYWORD(DoTransition);
+ DEFINE_KEYWORD(NextStateStartTime);
+ DEFINE_KEYWORD(TransitionDuration);
+ DEFINE_KEYWORD(TransitionOffset);
+ DEFINE_KEYWORD(TransitionStartTime);
+ DEFINE_KEYWORD(StateMachineWeight);
+ DEFINE_KEYWORD(TransitionTime);
+ DEFINE_KEYWORD(BlendWeight);
+ DEFINE_KEYWORD(StateWeight);
+ DEFINE_KEYWORD(StabilizeFeet);
+ DEFINE_KEYWORD(RootX);
+ table[mecanim::eLeftFootWeightT] = mecanim::ReserveKeyword(mecanim::processCRC32("LeftFoot.WeightT"), "LeftFoot.WeightT");
+ table[mecanim::eLeftFootWeightR] = mecanim::ReserveKeyword(mecanim::processCRC32("LeftFoot.WeightR"), "LeftFoot.WeightR");
+ table[mecanim::eRightFootWeightT] = mecanim::ReserveKeyword(mecanim::processCRC32("RightFoot.WeightT"), "RightFoot.WeightT");
+ table[mecanim::eRightFootWeightR] = mecanim::ReserveKeyword(mecanim::processCRC32("RightFoot.WeightR"), "RightFoot.WeightR");
+ DEFINE_KEYWORD(ComputeSource);
+ DEFINE_KEYWORD(LookAt);
+ DEFINE_KEYWORD(LeftFootX);
+ DEFINE_KEYWORD(RightFootX);
+ DEFINE_KEYWORD(LeftFootSpeedT);
+ DEFINE_KEYWORD(LeftFootSpeedQ);
+ DEFINE_KEYWORD(RightFootSpeedT);
+ DEFINE_KEYWORD(RightFootSpeedQ);
+ DEFINE_KEYWORD(LeftFootStableT);
+ DEFINE_KEYWORD(LeftFootStableQ);
+ DEFINE_KEYWORD(RightFootStableT);
+ DEFINE_KEYWORD(RightFootStableQ);
+ DEFINE_KEYWORD(RootSpeedT);
+ DEFINE_KEYWORD(RootSpeedQ);
+ DEFINE_KEYWORD(RootStableT);
+ DEFINE_KEYWORD(RootStableQ);
+ DEFINE_KEYWORD(LeftFootProjX);
+ DEFINE_KEYWORD(RightFootProjX);
+ DEFINE_KEYWORD(PlantFeet);
+ DEFINE_KEYWORD(LeftFootSafeX);
+ DEFINE_KEYWORD(RightFootSafeX);
+ DEFINE_KEYWORD(PositionX);
+ DEFINE_KEYWORD(PositionY);
+ DEFINE_KEYWORD(PositionZ);
+ DEFINE_KEYWORD(QuaternionX);
+ DEFINE_KEYWORD(QuaternionY);
+ DEFINE_KEYWORD(QuaternionZ);
+ DEFINE_KEYWORD(QuaternionW);
+ DEFINE_KEYWORD(ScaleX);
+ DEFINE_KEYWORD(ScaleY);
+ DEFINE_KEYWORD(ScaleZ);
+ DEFINE_KEYWORD(DynamicCurve);
+ return table;
+ }
+}
+
+namespace mecanim
+{
+ ReserveKeyword* ReserveKeywordTable()
+ {
+ static ReserveKeyword* s_Table = InitTable();
+ return s_Table;
+ }
+ uint32_t CRCKey(eString id)
+ {
+ return ReserveKeywordTable()[id].m_ID;
+ }
+ }
diff --git a/Runtime/mecanim/generic/stringtable.h b/Runtime/mecanim/generic/stringtable.h
new file mode 100644
index 0000000..3bf01f5
--- /dev/null
+++ b/Runtime/mecanim/generic/stringtable.h
@@ -0,0 +1,128 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/types.h"
+
+
+namespace mecanim
+{
+ enum eString
+ {
+ eT,
+ eQ,
+ eS,
+ eA,
+ eB,
+ eC,
+ eD,
+ eE,
+ eX,
+ eY,
+ eZ,
+ eW,
+ eResult,
+ eMin,
+ eMax,
+ eValue,
+ eMinMin,
+ eMinMax,
+ eMaxMin,
+ eMaxMax,
+ eIn,
+ eOut,
+ eRangeA,
+ eRangeB,
+ eRangeC,
+ eRangeD,
+ eRangeE,
+ eWeightA,
+ eWeightB,
+ eWeightC,
+ eWeightD,
+ eWeightE,
+ eOutA,
+ eOutB,
+ eOutC,
+ eOutD,
+ eOutE,
+ eNum,
+ eDen,
+ eRem,
+ eDampTime,
+ eDeltaTime,
+ ePreviousValue,
+ eGravityWeight,
+ eSrcRefX,
+ eDstRefX,
+ eSrcPivotX,
+ eDstPivotX,
+ eRefWeight,
+ ePivotWeight,
+ eXI,
+ eXO,
+ eCondition,
+ eStateTime,
+ eStateSpeed,
+ eStateExitTime,
+ eDoTransition,
+ eNextStateStartTime,
+ eTransitionDuration,
+ eTransitionOffset,
+ eTransitionStartTime,
+ eStateMachineWeight,
+ eTransitionTime,
+ eBlendWeight,
+ eStateWeight,
+ eStabilizeFeet,
+ eRootX,
+ eLeftFootWeightT,
+ eLeftFootWeightR,
+ eRightFootWeightT,
+ eRightFootWeightR,
+ eComputeSource,
+ eLookAt,
+ eLeftFootX,
+ eRightFootX,
+ eLeftFootSpeedT,
+ eLeftFootSpeedQ,
+ eRightFootSpeedT,
+ eRightFootSpeedQ,
+ eLeftFootStableT,
+ eLeftFootStableQ,
+ eRightFootStableT,
+ eRightFootStableQ,
+ eRootSpeedT,
+ eRootSpeedQ,
+ eRootStableT,
+ eRootStableQ,
+ eLeftFootProjX,
+ eRightFootProjX,
+ ePlantFeet,
+ eLeftFootSafeX,
+ eRightFootSafeX,
+ ePositionX,
+ ePositionY,
+ ePositionZ,
+ eQuaternionX,
+ eQuaternionY,
+ eQuaternionZ,
+ eQuaternionW,
+ eScaleX,
+ eScaleY,
+ eScaleZ,
+ eDynamicCurve,
+ eLastString
+ };
+
+ struct ReserveKeyword
+ {
+ ReserveKeyword():m_ID(0),m_Keyword(0){}
+ ReserveKeyword(uint32_t id, char const* keyword):m_ID(id),m_Keyword(keyword){}
+
+ uint32_t m_ID;
+ char const* m_Keyword ;
+ };
+
+ ReserveKeyword* ReserveKeywordTable();
+ uint32_t CRCKey(eString id);
+}
diff --git a/Runtime/mecanim/generic/typetraits.h b/Runtime/mecanim/generic/typetraits.h
new file mode 100644
index 0000000..1f9aa25
--- /dev/null
+++ b/Runtime/mecanim/generic/typetraits.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include "Runtime/mecanim/types.h"
+#include "Runtime/Math/Simd/float4.h"
+#include "Runtime/Math/Simd/xform.h"
+#include "Runtime/Math/Simd/bool4.h"
+
+namespace mecanim
+{
+ enum ValueType
+ {
+ kFloatType = 1,
+ kInt32Type = 3,
+ kBoolType = 4,
+ kPositionType = 6,
+ kQuaternionType = 7,
+ kScaleType = 8,
+ kTriggerType = 9,
+ kLastType
+ };
+
+ template<typename TYPE> struct traits;
+
+ template<> struct traits<int32_t>
+ {
+ typedef int32_t value_type;
+
+ static value_type zero() { return 0; }
+ static ValueType type() { return kInt32Type; }
+ };
+
+ template<> struct traits<float>
+ {
+ typedef float value_type;
+
+ static value_type zero() { return 0.f; }
+ static ValueType type() { return kFloatType; }
+ };
+
+ template<> struct traits<bool>
+ {
+ typedef bool value_type;
+
+ static value_type zero() { return false; }
+ static ValueType type() { return kBoolType; }
+ };
+}
diff --git a/Runtime/mecanim/generic/valuearray.cpp b/Runtime/mecanim/generic/valuearray.cpp
new file mode 100644
index 0000000..e7bfb58
--- /dev/null
+++ b/Runtime/mecanim/generic/valuearray.cpp
@@ -0,0 +1,925 @@
+#include "UnityPrefix.h"
+#include "Runtime/mecanim/generic/valuearray.h"
+
+#include "Runtime/Math/Simd/float4.h"
+#include "Runtime/Math/Simd/bool4.h"
+#include "Runtime/Math/Simd/xform.h"
+
+namespace mecanim
+{
+ template<typename TYPE>
+ static void ValueCopy(TYPE const * RESTRICT source, TYPE * RESTRICT destination, uint32_t sourceIndex, uint32_t destinationIndex)
+ {
+ destination[destinationIndex] = source[sourceIndex];
+ }
+
+ template<typename TYPE>
+ static void ValueArrayCopy(TYPE const * RESTRICT source, TYPE * RESTRICT destination, uint32_t sourceCount, uint32_t destinationCount)
+ {
+ sourceCount = min (destinationCount, sourceCount);
+
+ uint32_t i;
+ for(i = 0; i < sourceCount; ++i)
+ {
+ destination[i] = source[i];
+ }
+ }
+
+ template<typename TYPE>
+ static void ValueArrayCopyMask(TYPE const * RESTRICT source, TYPE * RESTRICT destination, const bool* RESTRICT mask, uint32_t sourceCount)
+ {
+ uint32_t i;
+ for(i = 0; i < sourceCount; ++i)
+ {
+ if (mask[i])
+ destination[i] = source[i];
+ }
+ }
+
+ void SetupValueArrayConstant(ValueArrayConstant* cst, ValueType aType, uint32_t aCount, memory::Allocator& alloc)
+ {
+ cst->m_Count = aCount;
+ cst->m_ValueArray = alloc.ConstructArray<ValueConstant>(cst->m_Count);
+
+ uint32_t i;
+ for(i=0;i<aCount;++i)
+ {
+ cst->m_ValueArray[i].m_Type = aType;
+ cst->m_ValueArray[i].m_Index = i;
+ }
+ }
+
+ ValueArrayConstant* CreateValueArrayConstant(uint32_t* apTypeArray, uint32_t aCount, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(ValueArrayConstant);
+
+ ValueArrayConstant* cst = alloc.Construct<ValueArrayConstant>();
+
+ cst->m_Count = aCount;
+ cst->m_ValueArray = alloc.ConstructArray<ValueConstant>(cst->m_Count);
+
+ uint32_t positionCount = 0;
+ uint32_t quaternionCount = 0;
+ uint32_t scaleCount = 0;
+ uint32_t floatCount = 0;
+ uint32_t intCount = 0;
+ uint32_t boolCount = 0;
+
+ uint32_t i;
+ for(i=0;i<aCount;++i)
+ {
+ cst->m_ValueArray[i].m_Type = apTypeArray[i];
+
+ switch(apTypeArray[i])
+ {
+ case kPositionType: cst->m_ValueArray[i].m_Index = positionCount++; break;
+ case kQuaternionType: cst->m_ValueArray[i].m_Index = quaternionCount++; break;
+ case kScaleType: cst->m_ValueArray[i].m_Index = scaleCount++; break;
+ case kFloatType: cst->m_ValueArray[i].m_Index = floatCount++; break;
+ case kInt32Type: cst->m_ValueArray[i].m_Index = intCount++; break;
+ case kTriggerType:
+ case kBoolType: cst->m_ValueArray[i].m_Index = boolCount++; break;
+ default: assert(false); break;
+ }
+ }
+ return cst;
+ }
+
+ ValueArrayConstant* CreateValueArrayConstant(ValueType aType, uint32_t aCount, memory::Allocator& alloc)
+ {
+
+ SETPROFILERLABEL(ValueArrayConstant);
+ ValueArrayConstant* cst = alloc.Construct<ValueArrayConstant>();
+
+ SetupValueArrayConstant(cst, aType, aCount, alloc);
+ return cst;
+ }
+
+ ValueArrayConstant* CreateValueArrayConstantCopy (const ValueArrayConstant* sourceConstant, uint32_t count, memory::Allocator& alloc)
+ {
+
+ SETPROFILERLABEL(ValueArrayConstant);
+
+ Assert(count <= sourceConstant->m_Count);
+
+ ValueArrayConstant* cst = alloc.Construct<ValueArrayConstant>();
+ cst->m_Count = count;
+ cst->m_ValueArray = alloc.ConstructArray<ValueConstant>(sourceConstant->m_ValueArray.Get(), count);
+
+ return cst;
+ }
+
+
+ void DestroyValueArrayConstant(ValueArrayConstant * apCst, memory::Allocator& alloc)
+ {
+ if(apCst)
+ {
+ alloc.Deallocate(apCst->m_ValueArray);
+ alloc.Deallocate(apCst);
+ }
+ }
+
+ void SetValueMask(ValueArrayMask *valueArrayMask, bool value)
+ {
+ for(int i = 0; i < valueArrayMask->m_PositionCount; i++)
+ valueArrayMask->m_PositionValues[i] = value;
+
+ for(int i = 0; i < valueArrayMask->m_QuaternionCount; i++)
+ valueArrayMask->m_QuaternionValues[i] = value;
+
+ for(int i = 0; i < valueArrayMask->m_ScaleCount; i++)
+ valueArrayMask->m_ScaleValues[i] = value;
+
+ for(int i = 0; i < valueArrayMask->m_FloatCount; i++)
+ valueArrayMask->m_FloatValues[i] = value;
+
+ for(int i = 0; i < valueArrayMask->m_IntCount; i++)
+ valueArrayMask->m_IntValues[i] = value;
+ }
+
+ void CopyValueMask(ValueArrayMask *valueArrayMask,ValueArrayMask const *srcValueArrayMask)
+ {
+ for(int i = 0; i < valueArrayMask->m_PositionCount; i++)
+ valueArrayMask->m_PositionValues[i] = srcValueArrayMask->m_PositionValues[i];
+
+ for(int i = 0; i < valueArrayMask->m_QuaternionCount; i++)
+ valueArrayMask->m_QuaternionValues[i] = srcValueArrayMask->m_QuaternionValues[i];
+
+ for(int i = 0; i < valueArrayMask->m_ScaleCount; i++)
+ valueArrayMask->m_ScaleValues[i] = srcValueArrayMask->m_ScaleValues[i];
+
+ for(int i = 0; i < valueArrayMask->m_FloatCount; i++)
+ valueArrayMask->m_FloatValues[i] = srcValueArrayMask->m_FloatValues[i];
+
+ for(int i = 0; i < valueArrayMask->m_IntCount; i++)
+ valueArrayMask->m_IntValues[i] = srcValueArrayMask->m_IntValues[i];
+ }
+
+ void OrValueMask(ValueArrayMask *valueArrayMask,ValueArrayMask const *srcValueArrayMask)
+ {
+ for(int i = 0; i < valueArrayMask->m_PositionCount; i++)
+ valueArrayMask->m_PositionValues[i] = valueArrayMask->m_PositionValues[i] || srcValueArrayMask->m_PositionValues[i];
+
+ for(int i = 0; i < valueArrayMask->m_QuaternionCount; i++)
+ valueArrayMask->m_QuaternionValues[i] = valueArrayMask->m_QuaternionValues[i] || srcValueArrayMask->m_QuaternionValues[i];
+
+ for(int i = 0; i < valueArrayMask->m_ScaleCount; i++)
+ valueArrayMask->m_ScaleValues[i] = valueArrayMask->m_ScaleValues[i] || srcValueArrayMask->m_ScaleValues[i];
+
+ for(int i = 0; i < valueArrayMask->m_FloatCount; i++)
+ valueArrayMask->m_FloatValues[i] = valueArrayMask->m_FloatValues[i] || srcValueArrayMask->m_FloatValues[i];
+
+ for(int i = 0; i < valueArrayMask->m_IntCount; i++)
+ valueArrayMask->m_IntValues[i] = valueArrayMask->m_IntValues[i] || srcValueArrayMask->m_IntValues[i];
+ }
+
+ void AndValueMask(ValueArrayMask *valueArrayMask,ValueArrayMask const *srcValueArrayMask)
+ {
+ for(int i = 0; i < valueArrayMask->m_PositionCount; i++)
+ valueArrayMask->m_PositionValues[i] = valueArrayMask->m_PositionValues[i] && srcValueArrayMask->m_PositionValues[i];
+
+ for(int i = 0; i < valueArrayMask->m_QuaternionCount; i++)
+ valueArrayMask->m_QuaternionValues[i] = valueArrayMask->m_QuaternionValues[i] && srcValueArrayMask->m_QuaternionValues[i];
+
+ for(int i = 0; i < valueArrayMask->m_ScaleCount; i++)
+ valueArrayMask->m_ScaleValues[i] = valueArrayMask->m_ScaleValues[i] && srcValueArrayMask->m_ScaleValues[i];
+
+ for(int i = 0; i < valueArrayMask->m_FloatCount; i++)
+ valueArrayMask->m_FloatValues[i] = valueArrayMask->m_FloatValues[i] && srcValueArrayMask->m_FloatValues[i];
+
+ for(int i = 0; i < valueArrayMask->m_IntCount; i++)
+ valueArrayMask->m_IntValues[i] = valueArrayMask->m_IntValues[i] && srcValueArrayMask->m_IntValues[i];
+ }
+
+ void InvertValueMask(ValueArrayMask *valueArrayMask)
+ {
+ for(int i = 0; i < valueArrayMask->m_PositionCount; i++)
+ valueArrayMask->m_PositionValues[i] = !valueArrayMask->m_PositionValues[i];
+
+ for(int i = 0; i < valueArrayMask->m_QuaternionCount; i++)
+ valueArrayMask->m_QuaternionValues[i] = !valueArrayMask->m_QuaternionValues[i];
+
+ for(int i = 0; i < valueArrayMask->m_ScaleCount; i++)
+ valueArrayMask->m_ScaleValues[i] = !valueArrayMask->m_ScaleValues[i];
+
+ for(int i = 0; i < valueArrayMask->m_FloatCount; i++)
+ valueArrayMask->m_FloatValues[i] = !valueArrayMask->m_FloatValues[i];
+
+ for(int i = 0; i < valueArrayMask->m_IntCount; i++)
+ valueArrayMask->m_IntValues[i] = !valueArrayMask->m_IntValues[i];
+ }
+
+ ValueArrayMask* CreateValueArrayMask(ValueArrayConstant const* constant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(ValueArrayMask);
+ ValueArrayMask* valueArrayMask = alloc.Construct<ValueArrayMask>();
+
+ uint32_t i;
+ for(i=0;i<constant->m_Count;++i)
+ {
+ switch(constant->m_ValueArray[i].m_Type)
+ {
+ case kPositionType: valueArrayMask->m_PositionCount++; break;
+ case kQuaternionType: valueArrayMask->m_QuaternionCount++; break;
+ case kScaleType: valueArrayMask->m_ScaleCount++; break;
+ case kFloatType: valueArrayMask->m_FloatCount++; break;
+ case kInt32Type: valueArrayMask->m_IntCount++; break;
+ default: assert(false); break;
+ }
+ }
+
+ valueArrayMask->m_IntValues = alloc.ConstructArray<bool>(valueArrayMask->m_IntCount);
+ valueArrayMask->m_FloatValues = alloc.ConstructArray<bool>(valueArrayMask->m_FloatCount);
+ valueArrayMask->m_PositionValues = alloc.ConstructArray<bool>(valueArrayMask->m_PositionCount);
+ valueArrayMask->m_QuaternionValues = alloc.ConstructArray<bool>(valueArrayMask->m_QuaternionCount);
+ valueArrayMask->m_ScaleValues = alloc.ConstructArray<bool>(valueArrayMask->m_ScaleCount);
+
+ SetValueMask (valueArrayMask, false);
+
+ return valueArrayMask;
+ }
+
+ void DestroyValueArrayMask(ValueArrayMask *valueArrayMask, memory::Allocator& alloc)
+ {
+ if(valueArrayMask)
+ {
+ alloc.Deallocate(valueArrayMask->m_IntValues);
+ alloc.Deallocate(valueArrayMask->m_FloatValues);
+ alloc.Deallocate(valueArrayMask->m_PositionValues);
+ alloc.Deallocate(valueArrayMask->m_QuaternionValues);
+ alloc.Deallocate(valueArrayMask->m_ScaleValues);
+ alloc.Deallocate(valueArrayMask);
+ }
+ }
+
+ ValueArray* CreateValueArray(ValueArrayConstant const* apValueArrayConstant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(ValueArray);
+ ValueArray* valueArray = alloc.Construct<ValueArray>();
+
+ uint32_t i;
+ for(i=0;i<apValueArrayConstant->m_Count;++i)
+ {
+ switch(apValueArrayConstant->m_ValueArray[i].m_Type)
+ {
+ case kPositionType: valueArray->m_PositionCount++; break;
+ case kQuaternionType: valueArray->m_QuaternionCount++; break;
+ case kScaleType: valueArray->m_ScaleCount++; break;
+ case kFloatType: valueArray->m_FloatCount++; break;
+ case kInt32Type: valueArray->m_IntCount++; break;
+ case kTriggerType:
+ case kBoolType: valueArray->m_BoolCount++; break;
+ default: assert(false); break;
+ }
+ }
+
+ valueArray->m_BoolValues = alloc.ConstructArray<bool>(valueArray->m_BoolCount);
+ valueArray->m_IntValues = alloc.ConstructArray<int32_t>(valueArray->m_IntCount);
+ valueArray->m_FloatValues = alloc.ConstructArray<float>(valueArray->m_FloatCount);
+ valueArray->m_PositionValues = alloc.ConstructArray<math::float4>(valueArray->m_PositionCount);
+ valueArray->m_QuaternionValues = alloc.ConstructArray<math::float4>(valueArray->m_QuaternionCount);
+ valueArray->m_ScaleValues = alloc.ConstructArray<math::float4>(valueArray->m_ScaleCount);
+
+ for(i=0;i<valueArray->m_BoolCount;++i)
+ valueArray->m_BoolValues[i] = false;
+
+ for(i=0;i<valueArray->m_IntCount;++i)
+ valueArray->m_IntValues[i] = 0;
+
+ for(i=0;i<valueArray->m_FloatCount;++i)
+ valueArray->m_FloatValues[i] = 0.f;
+
+ for(i=0;i<valueArray->m_PositionCount;++i)
+ valueArray->m_PositionValues[i] = math::float4::zero();
+
+ for(i=0;i<valueArray->m_QuaternionCount;++i)
+ valueArray->m_QuaternionValues[i] = math::quatIdentity();
+
+ for(i=0;i<valueArray->m_ScaleCount;++i)
+ valueArray->m_ScaleValues[i] = math::float4::one();
+
+ return valueArray;
+ }
+
+ void DestroyValueArray(ValueArray * apInput, memory::Allocator& alloc)
+ {
+ if(apInput)
+ {
+ alloc.Deallocate(apInput->m_BoolValues);
+ alloc.Deallocate(apInput->m_IntValues);
+ alloc.Deallocate(apInput->m_FloatValues);
+ alloc.Deallocate(apInput->m_PositionValues);
+ alloc.Deallocate(apInput->m_QuaternionValues);
+ alloc.Deallocate(apInput->m_ScaleValues);
+
+ alloc.Deallocate(apInput);
+ }
+ }
+
+ void ValueArrayCopy(ValueArray const* apSourceValueArray, ValueArray * apDestinationValueArray)
+ {
+ ValueArrayCopy(apSourceValueArray->m_BoolValues.Get(), apDestinationValueArray->m_BoolValues.Get(), apSourceValueArray->m_BoolCount, apDestinationValueArray->m_BoolCount);
+ ValueArrayCopy(apSourceValueArray->m_IntValues.Get(), apDestinationValueArray->m_IntValues.Get(), apSourceValueArray->m_IntCount, apDestinationValueArray->m_IntCount);
+ ValueArrayCopy(apSourceValueArray->m_FloatValues.Get(), apDestinationValueArray->m_FloatValues.Get(), apSourceValueArray->m_FloatCount, apDestinationValueArray->m_FloatCount);
+ ValueArrayCopy(apSourceValueArray->m_PositionValues.Get(), apDestinationValueArray->m_PositionValues.Get(), apSourceValueArray->m_PositionCount, apDestinationValueArray->m_PositionCount);
+ ValueArrayCopy(apSourceValueArray->m_QuaternionValues.Get(), apDestinationValueArray->m_QuaternionValues.Get(), apSourceValueArray->m_QuaternionCount, apDestinationValueArray->m_QuaternionCount);
+ ValueArrayCopy(apSourceValueArray->m_ScaleValues.Get(), apDestinationValueArray->m_ScaleValues.Get(), apSourceValueArray->m_ScaleCount, apDestinationValueArray->m_ScaleCount);
+ }
+
+ void ValueArrayCopy(ValueArrayConstant const* sourceConstant, ValueArray const* source, ValueArrayConstant const* destinationConstant, ValueArray* destination, int32_t const* destinationInSourceIndexArray)
+ {
+ int dstCount = destinationConstant->m_Count;
+
+ for(int dstIter = 0; dstIter < dstCount; dstIter++)
+ {
+ int32_t srcIndex = destinationInSourceIndexArray[dstIter];
+ int32_t dstIndex = dstIter;
+
+ if(srcIndex != -1 && sourceConstant->m_ValueArray[srcIndex].m_Type == destinationConstant->m_ValueArray[dstIndex].m_Type)
+ {
+ switch(sourceConstant->m_ValueArray[srcIndex].m_Type)
+ {
+ case kPositionType:
+ {
+ math::float4 value = source->ReadPosition(sourceConstant->m_ValueArray[srcIndex].m_Index);
+ destination->WritePosition(value, destinationConstant->m_ValueArray[dstIndex].m_Index);
+ break;
+ }
+ case kQuaternionType:
+ {
+ math::float4 value = source->ReadQuaternion(sourceConstant->m_ValueArray[srcIndex].m_Index);
+ destination->WriteQuaternion(value, destinationConstant->m_ValueArray[dstIndex].m_Index);
+ break;
+ }
+ case kScaleType:
+ {
+ math::float4 value = source->ReadScale(sourceConstant->m_ValueArray[srcIndex].m_Index);
+ destination->WriteScale(value, destinationConstant->m_ValueArray[dstIndex].m_Index);
+ break;
+ }
+ case kFloatType:
+ {
+ float value;
+ source->ReadData(value, sourceConstant->m_ValueArray[srcIndex].m_Index);
+ destination->WriteData(value, destinationConstant->m_ValueArray[dstIndex].m_Index);
+ break;
+ }
+ case kInt32Type:
+ {
+ int32_t value;
+ source->ReadData(value, sourceConstant->m_ValueArray[srcIndex].m_Index);
+ destination->WriteData(value, destinationConstant->m_ValueArray[dstIndex].m_Index);
+ break;
+ }
+ case kTriggerType:
+ case kBoolType:
+ {
+ bool value;
+ source->ReadData(value, sourceConstant->m_ValueArray[srcIndex].m_Index);
+ destination->WriteData(value, destinationConstant->m_ValueArray[dstIndex].m_Index);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ void ValueArrayReverseCopy(ValueArrayConstant const* sourceConstant, ValueArray const* source, ValueArrayConstant const* destinationConstant, ValueArray* destination, int32_t const* sourceInDestinationIndexArray)
+ {
+ int srcCount = sourceConstant->m_Count;
+
+ for(int srcIter = 0; srcIter < srcCount; srcIter++)
+ {
+ int32_t dstIndex = sourceInDestinationIndexArray[srcIter];
+ int32_t srcIndex = srcIter;
+
+ if(dstIndex != -1 && sourceConstant->m_ValueArray[srcIndex].m_Type == destinationConstant->m_ValueArray[dstIndex].m_Type)
+ {
+ switch(sourceConstant->m_ValueArray[srcIndex].m_Type)
+ {
+ case kPositionType:
+ {
+ math::float4 value = source->ReadPosition(sourceConstant->m_ValueArray[srcIndex].m_Index);
+ destination->WritePosition(value, destinationConstant->m_ValueArray[dstIndex].m_Index);
+ break;
+ }
+ case kQuaternionType:
+ {
+ math::float4 value = source->ReadQuaternion(sourceConstant->m_ValueArray[srcIndex].m_Index);
+ destination->WriteQuaternion(value, destinationConstant->m_ValueArray[dstIndex].m_Index);
+ break;
+ }
+ case kScaleType:
+ {
+ math::float4 value = source->ReadScale(sourceConstant->m_ValueArray[srcIndex].m_Index);
+ destination->WriteScale(value, destinationConstant->m_ValueArray[dstIndex].m_Index);
+ break;
+ }
+ case kFloatType:
+ {
+ float value;
+ source->ReadData(value, sourceConstant->m_ValueArray[srcIndex].m_Index);
+ destination->WriteData(value, destinationConstant->m_ValueArray[dstIndex].m_Index);
+ break;
+ }
+ case kInt32Type:
+ {
+ int32_t value;
+ source->ReadData(value, sourceConstant->m_ValueArray[srcIndex].m_Index);
+ destination->WriteData(value, destinationConstant->m_ValueArray[dstIndex].m_Index);
+ break;
+ }
+ case kTriggerType:
+ case kBoolType:
+ {
+ bool value;
+ source->ReadData(value, sourceConstant->m_ValueArray[srcIndex].m_Index);
+ destination->WriteData(value, destinationConstant->m_ValueArray[dstIndex].m_Index);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ void ValueArrayCopy(ValueArray const *aSource, ValueArray* aValues, ValueArrayMask const *mask)
+ {
+ ValueArrayCopyMask (aSource->m_PositionValues.Get(), aValues->m_PositionValues.Get(), mask->m_PositionValues.Get(), aValues->m_PositionCount);
+ ValueArrayCopyMask (aSource->m_QuaternionValues.Get(), aValues->m_QuaternionValues.Get(), mask->m_QuaternionValues.Get(), aValues->m_QuaternionCount);
+ ValueArrayCopyMask (aSource->m_ScaleValues.Get(), aValues->m_ScaleValues.Get(), mask->m_ScaleValues.Get(), aValues->m_ScaleCount);
+ ValueArrayCopyMask (aSource->m_FloatValues.Get(), aValues->m_FloatValues.Get(), mask->m_FloatValues.Get(), aValues->m_FloatCount);
+ ValueArrayCopyMask (aSource->m_IntValues.Get(), aValues->m_IntValues.Get(), mask->m_IntValues.Get(), aValues->m_IntCount);
+ ValueArrayCopyMask (aSource->m_BoolValues.Get(), aValues->m_BoolValues.Get(), mask->m_BoolValues.Get(), aValues->m_BoolCount);
+ }
+
+ static uint32_t GetLargestBlendIndex(const float *apWeightArray, uint32_t aCount)
+ {
+ float largest = apWeightArray[0];
+ uint32_t index = 0;
+ for (int i=1;i<aCount;i++)
+ {
+ if (apWeightArray[i] > largest)
+ {
+ index = i;
+ largest = apWeightArray[i];
+ }
+ }
+
+ return index;
+ }
+
+
+ void ValueArrayBlend(ValueArray const *apValuesDefault,ValueArray* aValues, ValueArray ** aValuesArray, const float *apWeightArray, uint32_t aCount, ValueArrayMask const *mask)
+ {
+ // Blend positions
+ for (int valueIndex=0;valueIndex<aValues->m_PositionCount;valueIndex++)
+ {
+ if(!mask->m_PositionValues[valueIndex])
+ continue;
+
+ float sumW = 0;
+ math::float4 value4 = math::float4::zero();
+
+ for(int blendIter = 0; blendIter < aCount; blendIter++)
+ {
+ sumW += apWeightArray[blendIter];
+ math::float1 w = math::float1(apWeightArray[blendIter]);
+
+ math::float4 valuei = aValuesArray[blendIter]->ReadPosition(valueIndex);
+ value4 += valuei*w;
+ }
+
+ if(sumW < 1.0f)
+ {
+ math::float1 wd = math::float1(1.0-sumW);
+ math::float4 valued = apValuesDefault->ReadPosition(valueIndex);
+ value4 += valued*math::float1(wd);
+ }
+
+ aValues->WritePosition(value4,valueIndex);
+ }
+
+ // Blend Quaternions
+ for (int valueIndex=0;valueIndex<aValues->m_QuaternionCount;valueIndex++)
+ {
+ if(!mask->m_QuaternionValues[valueIndex])
+ continue;
+
+ float sumW = 0;
+ math::float4 value4 = math::float4::zero();
+
+ for(int blendIter = 0; blendIter < aCount; blendIter++)
+ {
+ sumW += apWeightArray[blendIter];
+ math::float1 w = math::float1(apWeightArray[blendIter]);
+
+ math::float4 valuei = aValuesArray[blendIter]->ReadQuaternion(valueIndex);
+ value4 += math::cond(math::dot(value4,valuei) < math::float1::zero(), valuei * -w, valuei * w);
+ }
+
+ if(sumW < 1.0f)
+ {
+ math::float1 wd = math::float1(1.0-sumW);
+ math::float4 valued = apValuesDefault->ReadQuaternion(valueIndex);
+ value4 += math::cond(math::dot(value4,valued) < math::float1::zero(), valued * -wd, valued * wd);
+ }
+
+ value4 = math::normalize(value4);
+ aValues->WriteQuaternion(value4,valueIndex);
+ }
+
+ // Blend scale
+ for (int valueIndex=0;valueIndex<aValues->m_ScaleCount;valueIndex++)
+ {
+ if(!mask->m_ScaleValues[valueIndex])
+ continue;
+
+ float sumW = 0;
+ math::float4 value4 = math::float4::one();
+
+ for(int blendIter = 0; blendIter < aCount; blendIter++)
+ {
+ sumW += apWeightArray[blendIter];
+ math::float1 w = math::float1(apWeightArray[blendIter]);
+
+ math::float4 valuei = aValuesArray[blendIter]->ReadScale(valueIndex);
+ math::float4 sng = math::sgn(valuei);
+ value4 = sng * math::abs( value4 * scaleWeight(valuei, w));
+ }
+
+ if(sumW < 1.0f)
+ {
+ math::float1 wd = math::float1(1.0-sumW);
+
+ math::float4 valued = apValuesDefault->ReadScale(valueIndex);
+ math::float4 sng = math::sgn(valued);
+ value4 = sng * math::abs( value4 * scaleWeight(valued, wd));
+ }
+
+ aValues->WriteScale(value4,valueIndex);
+ }
+
+ // Blend floats
+ for (int valueIndex=0;valueIndex<aValues->m_FloatCount;valueIndex++)
+ {
+ if(!mask->m_FloatValues[valueIndex])
+ continue;
+
+ float sumW = 0;
+ float value = 0;
+
+ for(int blendIter = 0; blendIter < aCount; blendIter++)
+ {
+ float w = apWeightArray[blendIter];
+ sumW += w;
+
+ float valuei;
+ aValuesArray[blendIter]->ReadData(valuei,valueIndex);
+ value += valuei*w;
+ }
+
+ if(sumW < 1.0f)
+ {
+ float wd = 1.0-sumW;
+
+ float valued;
+ apValuesDefault->ReadData(valued,valueIndex);
+ value += valued*wd;
+ }
+
+ aValues->WriteData(value,valueIndex);
+ }
+
+ // Blend integers (pick by largest weight)
+ uint32_t largestBlendIndex = GetLargestBlendIndex (apWeightArray, aCount);
+ for (int valueIndex=0;valueIndex<aValues->m_IntCount;valueIndex++)
+ {
+ if(!mask->m_IntValues[valueIndex])
+ continue;
+
+ int32_t valueInt;
+ aValuesArray[largestBlendIndex]->ReadData(valueInt,valueIndex);
+ aValues->WriteData(valueInt,valueIndex);
+ }
+ }
+
+ void ValueArrayAdd(ValueArray const *apValuesDefault, ValueArray const* apValues, ValueArrayMask const *readMask, float aWeight, bool aAdditive, ValueArray* apValuesOut, ValueArrayMask *defaultMask)
+ {
+ math::float1 w(aWeight);
+ float base1;
+ float value1;
+ math::float4 base4;
+ math::float4 value4;
+ int32_t valueInt;
+
+ // Positions
+ for (int valueIndex=0;valueIndex<apValues->m_PositionCount;valueIndex++)
+ {
+ if(!readMask->m_PositionValues[valueIndex])
+ continue;
+
+ value4 = apValues->ReadPosition(valueIndex);
+ if(aAdditive)
+ {
+ if(defaultMask->m_PositionValues[valueIndex])
+ base4 = apValuesDefault->ReadPosition(valueIndex);
+ else
+ base4 = apValuesOut->ReadPosition(valueIndex);
+ value4 = base4 + value4 * w;
+ }
+ else
+ {
+ if(aWeight < 1)
+ {
+ if(defaultMask->m_PositionValues[valueIndex])
+ base4 = apValuesDefault->ReadPosition(valueIndex);
+ else
+ base4 = apValuesOut->ReadPosition(valueIndex);
+
+ value4 = math::lerp(base4,value4,w);
+ }
+ }
+ apValuesOut->WritePosition(value4,valueIndex);
+ defaultMask->m_PositionValues[valueIndex] = false;
+ }
+
+ // Quaternions
+ for (int valueIndex=0;valueIndex<apValues->m_QuaternionCount;valueIndex++)
+ {
+ if(!readMask->m_QuaternionValues[valueIndex])
+ continue;
+
+ value4 = apValues->ReadQuaternion(valueIndex);
+ if(aAdditive)
+ {
+ if(defaultMask->m_QuaternionValues[valueIndex])
+ base4 = apValuesDefault->ReadQuaternion(valueIndex);
+ else
+ base4 = apValuesOut->ReadQuaternion(valueIndex);
+
+ value4 = math::quatMul(base4,math::quatWeight(value4,w));
+ }
+ else
+ {
+ if(aWeight < 1)
+ {
+ if(defaultMask->m_QuaternionValues[valueIndex])
+ base4 = apValuesDefault->ReadQuaternion(valueIndex);
+ else
+ base4 = apValuesOut->ReadQuaternion(valueIndex);
+
+ value4 = math::quatLerp(base4,value4,w);
+ }
+ }
+ apValuesOut->WriteQuaternion(value4,valueIndex);
+ defaultMask->m_QuaternionValues[valueIndex] = false;
+ }
+
+ // Scale
+ for (int valueIndex=0;valueIndex<apValues->m_ScaleCount;valueIndex++)
+ {
+ if(!readMask->m_ScaleValues[valueIndex])
+ continue;
+
+ value4 = apValues->ReadScale(valueIndex);
+ if(aAdditive)
+ {
+ if(defaultMask->m_ScaleValues[valueIndex])
+ base4 = apValuesDefault->ReadScale(valueIndex);
+ else
+ base4 = apValuesOut->ReadScale(valueIndex);
+
+ value4 = base4 * math::scaleWeight(value4,w);
+ }
+ else
+ {
+ if(aWeight < 1)
+ {
+ if(defaultMask->m_ScaleValues[valueIndex])
+ base4 = apValuesDefault->ReadScale(valueIndex);
+ else
+ base4 = apValuesOut->ReadScale(valueIndex);
+
+ value4 = math::scaleBlend(base4,value4,w);
+ }
+ }
+ apValuesOut->WriteScale(value4,valueIndex);
+ defaultMask->m_ScaleValues[valueIndex] = false;
+ }
+
+ // Floats
+ for (int valueIndex=0;valueIndex<apValues->m_FloatCount;valueIndex++)
+ {
+ if(!readMask->m_FloatValues[valueIndex])
+ continue;
+
+ apValues->ReadData(value1,valueIndex);
+ if(aAdditive)
+ {
+ if(defaultMask->m_FloatValues[valueIndex])
+ apValuesDefault->ReadData(base1,valueIndex);
+ else
+ apValuesOut->ReadData(base1,valueIndex);
+
+ value1 = base1 + value1 * aWeight;
+ }
+ else
+ {
+ if(aWeight < 1)
+ {
+ if(defaultMask->m_FloatValues[valueIndex])
+ apValuesDefault->ReadData(base1,valueIndex);
+ else
+ apValuesOut->ReadData(base1,valueIndex);
+
+ value1 = (1-aWeight) * base1 + value1 * aWeight;
+ }
+ }
+ apValuesOut->WriteData(value1,valueIndex);
+ defaultMask->m_FloatValues[valueIndex] = false;
+ }
+
+ // Ints
+ if (aWeight > 0.5F)
+ {
+ for (int valueIndex=0;valueIndex<apValues->m_IntCount;valueIndex++)
+ {
+ if(!readMask->m_IntValues[valueIndex])
+ continue;
+
+ apValues->ReadData(valueInt,valueIndex);
+ apValuesOut->WriteData(valueInt,valueIndex);
+ defaultMask->m_IntValues[valueIndex] = false;
+ }
+ }
+ else
+ {
+ for (int valueIndex=0;valueIndex<apValues->m_IntCount;valueIndex++)
+ {
+ if(!readMask->m_IntValues[valueIndex])
+ continue;
+
+ if(defaultMask->m_IntValues[valueIndex])
+ apValuesDefault->ReadData(valueInt,valueIndex);
+ else
+ apValuesOut->ReadData(valueInt,valueIndex);
+
+ apValuesOut->WriteData(valueInt,valueIndex);
+ defaultMask->m_IntValues[valueIndex] = false;
+ }
+ }
+ }
+
+ void ValueArraySub(ValueArray const &starts, ValueArray &values, ValueArrayMask const *mask)
+ {
+ float value,start;
+ math::float4 value4,start4;
+
+ // Positions
+ for (int valueIndex=0;valueIndex<values.m_PositionCount;valueIndex++)
+ {
+ if(!mask->m_PositionValues[valueIndex])
+ continue;
+
+ value4 = values.ReadPosition(valueIndex);
+ start4 = starts.ReadPosition(valueIndex);
+
+ value4 -= start4;
+
+ values.WritePosition(value4,valueIndex);
+ }
+
+ // Quaternions
+ for (int valueIndex=0;valueIndex<values.m_QuaternionCount;valueIndex++)
+ {
+ if(!mask->m_QuaternionValues[valueIndex])
+ continue;
+
+ value4 = values.ReadQuaternion(valueIndex);
+ start4 = starts.ReadQuaternion(valueIndex);
+
+ value4 = math::normalize(math::quatMul(math::quatConj(start4),value4));
+
+ values.WriteQuaternion(value4,valueIndex);
+ }
+
+ // Scale
+ for (int valueIndex=0;valueIndex<values.m_ScaleCount;valueIndex++)
+ {
+ if(!mask->m_ScaleValues[valueIndex])
+ continue;
+
+ value4 = values.ReadScale(valueIndex);
+ start4 = starts.ReadScale(valueIndex);
+
+ value4 /= start4;
+
+ values.WriteScale(value4,valueIndex);
+ }
+
+ // Floats
+ for (int valueIndex=0;valueIndex<values.m_FloatCount;valueIndex++)
+ {
+ if(!mask->m_FloatValues[valueIndex])
+ continue;
+
+ values.ReadData(value,valueIndex);
+ starts.ReadData(start,valueIndex);
+
+ value -= start;
+
+ values.WriteData(value,valueIndex);
+ }
+
+ // Integer substraction does not make sense
+ }
+
+ void ValueArrayLoop( ValueArray const &starts,
+ ValueArray const &stops,
+ ValueArray &values,
+ float loopWeight,
+ const ValueArrayMask& mask)
+ {
+ math::float1 loopWeight1(loopWeight);
+
+ float value,start,stop;
+ math::float4 value4,start4,stop4;
+
+ // Positions
+ for (int valueIndex=0;valueIndex<values.m_PositionCount;valueIndex++)
+ {
+ if(!mask.m_PositionValues[valueIndex])
+ continue;
+
+ value4 = values.ReadPosition(valueIndex);
+ start4 = starts.ReadPosition(valueIndex);
+ stop4 = stops.ReadPosition(valueIndex);
+
+ value4 += (start4 - stop4) * loopWeight1;
+
+ values.WritePosition(value4,valueIndex);
+ }
+
+ // Quaternions
+ for (int valueIndex=0;valueIndex<values.m_QuaternionCount;valueIndex++)
+ {
+ if(!mask.m_QuaternionValues[valueIndex])
+ continue;
+
+ value4 = values.ReadQuaternion(valueIndex);
+ start4 = starts.ReadQuaternion(valueIndex);
+ stop4 = stops.ReadQuaternion(valueIndex);
+
+ value4 = math::normalize(math::quatMul(value4,math::quatWeight(math::quatMul(math::quatConj(stop4),start4),loopWeight1)));
+
+ values.WriteQuaternion(value4,valueIndex);
+ }
+
+ // Scales
+ for (int valueIndex=0;valueIndex<values.m_ScaleCount;valueIndex++)
+ {
+ if(!mask.m_ScaleValues[valueIndex])
+ continue;
+
+ value4 = values.ReadScale(valueIndex);
+ start4 = starts.ReadScale(valueIndex);
+ stop4 = stops.ReadScale(valueIndex);
+
+ value4 *= math::scaleWeight(start4/stop4,loopWeight1);
+
+ values.WriteScale(value4,valueIndex);
+ }
+
+ // Floats
+ for (int valueIndex=0;valueIndex<values.m_FloatCount;valueIndex++)
+ {
+ if(!mask.m_FloatValues[valueIndex])
+ continue;
+
+ values.ReadData(value,valueIndex);
+ starts.ReadData(start,valueIndex);
+ stops.ReadData(stop,valueIndex);
+
+ value += (start - stop) * loopWeight;
+
+ values.WriteData(value,valueIndex);
+ }
+ }
+
+ int32_t FindValueIndex(const ValueArrayConstant *aValueArrayConstant, uint32_t id)
+ {
+ int32_t ret = -1;
+ if(aValueArrayConstant)
+ {
+ uint32_t i;
+ for( i = 0 ; i < aValueArrayConstant->m_Count ; i++)
+ {
+ if(aValueArrayConstant->m_ValueArray[i].m_ID == id)
+ {
+ return i;
+ }
+ }
+ }
+ return ret;
+ }
+}
diff --git a/Runtime/mecanim/generic/valuearray.h b/Runtime/mecanim/generic/valuearray.h
new file mode 100644
index 0000000..25dcacd
--- /dev/null
+++ b/Runtime/mecanim/generic/valuearray.h
@@ -0,0 +1,256 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+
+#include "Runtime/mecanim/generic/crc32.h"
+#include "Runtime/mecanim/generic/typetraits.h"
+
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Animation/MecanimArraySerialization.h"
+
+namespace mecanim
+{
+ struct ValueConstant
+ {
+ DEFINE_GET_TYPESTRING(ValueConstant)
+
+ ValueConstant():m_ID(0),m_Type(kLastType),m_TypeID(0),m_Index(0){}
+
+ uint32_t m_ID;
+ uint32_t m_TypeID; //@TODO: This is deprecated. We should probably make this webplayer only?
+ uint32_t m_Type;
+ uint32_t m_Index;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_ID);
+ TRANSFER(m_TypeID);
+ TRANSFER(m_Type);
+ TRANSFER(m_Index);
+ }
+ };
+
+ struct ValueArrayConstant
+ {
+ DEFINE_GET_TYPESTRING(ValueArrayConstant)
+
+ ValueArrayConstant():m_Count(0){}
+
+ uint32_t m_Count;
+ OffsetPtr<ValueConstant> m_ValueArray;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_Count);
+ MANUAL_ARRAY_TRANSFER2(ValueConstant, m_ValueArray, m_Count);
+ }
+ };
+
+ struct ValueArrayMask
+ {
+ DEFINE_GET_TYPESTRING(ValueArrayConstant)
+
+ ValueArrayMask():m_BoolCount(0),m_IntCount(0),m_FloatCount(0),m_PositionCount(0),m_QuaternionCount(0),m_ScaleCount(0) {}
+
+ uint32_t m_BoolCount;
+ OffsetPtr<bool> m_BoolValues;
+ uint32_t m_IntCount;
+ OffsetPtr<bool> m_IntValues;
+ uint32_t m_FloatCount;
+ OffsetPtr<bool> m_FloatValues;
+ uint32_t m_PositionCount;
+ OffsetPtr<bool> m_PositionValues;
+ uint32_t m_QuaternionCount;
+ OffsetPtr<bool> m_QuaternionValues;
+ uint32_t m_ScaleCount;
+ OffsetPtr<bool> m_ScaleValues;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_BoolCount);
+ MANUAL_ARRAY_TRANSFER2(bool, m_BoolValues, m_BoolCount);
+
+ TRANSFER_BLOB_ONLY(m_IntCount);
+ MANUAL_ARRAY_TRANSFER2(bool, m_IntValues, m_IntCount);
+
+ TRANSFER_BLOB_ONLY(m_FloatCount);
+ MANUAL_ARRAY_TRANSFER2(bool, m_FloatValues, m_FloatCount);
+
+ TRANSFER_BLOB_ONLY(m_PositionCount);
+ MANUAL_ARRAY_TRANSFER2(bool, m_PositionValues, m_PositionCount);
+
+ TRANSFER_BLOB_ONLY(m_QuaternionCount);
+ MANUAL_ARRAY_TRANSFER2(bool, m_QuaternionValues, m_QuaternionCount);
+
+ TRANSFER_BLOB_ONLY(m_ScaleCount);
+ MANUAL_ARRAY_TRANSFER2(bool, m_ScaleValues, m_ScaleCount);
+ }
+ };
+
+ struct ValueArray
+ {
+ DEFINE_GET_TYPESTRING(ValueArray)
+
+ ValueArray():m_BoolCount(0),m_IntCount(0),m_FloatCount(0),m_PositionCount(0),m_QuaternionCount(0),m_ScaleCount(0) {}
+
+ uint32_t m_BoolCount;
+ OffsetPtr<bool> m_BoolValues;
+ uint32_t m_IntCount;
+ OffsetPtr<int32_t> m_IntValues;
+ uint32_t m_FloatCount;
+ OffsetPtr<float> m_FloatValues;
+ uint32_t m_PositionCount;
+ OffsetPtr<math::float4> m_PositionValues;
+ uint32_t m_QuaternionCount;
+ OffsetPtr<math::float4> m_QuaternionValues;
+ uint32_t m_ScaleCount;
+ OffsetPtr<math::float4> m_ScaleValues;
+
+ MECANIM_FORCE_INLINE void ReadData(bool& data, uint32_t index)const
+ {
+ Assert(index < m_BoolCount);
+ data = m_BoolValues[index];
+ }
+
+ MECANIM_FORCE_INLINE void WriteData(bool const& data, uint32_t index)
+ {
+ Assert(index < m_BoolCount);
+ m_BoolValues[index] = data;
+ }
+
+ MECANIM_FORCE_INLINE void ReadData(int32_t& data, uint32_t index)const
+ {
+ Assert(index < m_IntCount);
+ data = m_IntValues[index];
+ }
+
+ MECANIM_FORCE_INLINE void WriteData(int32_t const& data, uint32_t index)
+ {
+ Assert(index < m_IntCount);
+ m_IntValues[index] = data;
+ }
+
+ MECANIM_FORCE_INLINE void ReadData(float& data, uint32_t index)const
+ {
+ Assert(index < m_FloatCount);
+ data = m_FloatValues[index];
+ }
+
+ MECANIM_FORCE_INLINE void WriteData(float const& data, uint32_t index)
+ {
+ Assert(index < m_FloatCount);
+ m_FloatValues[index] = data;
+ }
+
+ MECANIM_FORCE_INLINE math::float4 ReadPosition(uint32_t index)const
+ {
+ Assert(index < m_PositionCount);
+ return m_PositionValues[index];
+ }
+
+ MECANIM_FORCE_INLINE void WritePosition(math::float4 const& data, uint32_t index)
+ {
+ Assert(index < m_PositionCount);
+ m_PositionValues[index] = data;
+ }
+
+ MECANIM_FORCE_INLINE math::float4 ReadQuaternion(uint32_t index)const
+ {
+ Assert(index < m_QuaternionCount);
+ return m_QuaternionValues[index];
+ }
+
+ MECANIM_FORCE_INLINE void WriteQuaternion(math::float4 const& data, uint32_t index)
+ {
+ Assert(index < m_QuaternionCount);
+ m_QuaternionValues[index] = data;
+ }
+
+ MECANIM_FORCE_INLINE math::float4 ReadScale(uint32_t index)const
+ {
+ Assert(index < m_ScaleCount);
+ return m_ScaleValues[index];
+ }
+
+ MECANIM_FORCE_INLINE void WriteScale(math::float4 const& data, uint32_t index)
+ {
+ Assert(index < m_ScaleCount);
+ m_ScaleValues[index] = data;
+ }
+
+ const float* GetFloatValues () const
+ {
+ return m_FloatValues.Get();
+ }
+
+ float* GetFloatValues ()
+ {
+ return m_FloatValues.Get();
+ }
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_BoolCount);
+ MANUAL_ARRAY_TRANSFER2(bool, m_BoolValues, m_BoolCount);
+ transfer.Align();
+
+ TRANSFER_BLOB_ONLY(m_IntCount);
+ MANUAL_ARRAY_TRANSFER2(int32_t, m_IntValues, m_IntCount);
+
+ TRANSFER_BLOB_ONLY(m_FloatCount);
+ MANUAL_ARRAY_TRANSFER2(float, m_FloatValues, m_FloatCount);
+
+ TRANSFER_BLOB_ONLY(m_PositionCount);
+ MANUAL_ARRAY_TRANSFER2(math::float4, m_PositionValues, m_PositionCount);
+
+ TRANSFER_BLOB_ONLY(m_QuaternionCount);
+ MANUAL_ARRAY_TRANSFER2(math::float4, m_QuaternionValues, m_QuaternionCount);
+
+ TRANSFER_BLOB_ONLY(m_ScaleCount);
+ MANUAL_ARRAY_TRANSFER2(math::float4, m_ScaleValues, m_ScaleCount);
+ }
+ };
+
+ void SetupValueArrayConstant(ValueArrayConstant* constant, ValueType aType, uint32_t aCount, memory::Allocator& alloc);
+
+ ValueArrayConstant* CreateValueArrayConstant(uint32_t* typeArray, uint32_t count, memory::Allocator& alloc);
+ ValueArrayConstant* CreateValueArrayConstantCopy(const ValueArrayConstant* constant, uint32_t count, memory::Allocator& alloc);
+ ValueArrayConstant* CreateValueArrayConstant(ValueType type, uint32_t count, memory::Allocator& alloc);
+ void DestroyValueArrayConstant(ValueArrayConstant * constant, memory::Allocator& alloc);
+
+ ValueArrayMask* CreateValueArrayMask(ValueArrayConstant const* constant, memory::Allocator& alloc);
+ void DestroyValueArrayMask(ValueArrayMask *valueArrayMask, memory::Allocator& alloc);
+ void SetValueMask(ValueArrayMask *valueArrayMask, bool value);
+ void CopyValueMask(ValueArrayMask *valueArrayMask,ValueArrayMask const *srcValueArrayMask);
+ void OrValueMask(ValueArrayMask *valueArrayMask,ValueArrayMask const *srcValueArrayMask);
+ void AndValueMask(ValueArrayMask *valueArrayMask,ValueArrayMask const *srcValueArrayMask);
+ void InvertValueMask(ValueArrayMask *valueArrayMask);
+
+ ValueArray* CreateValueArray(ValueArrayConstant const* constant, memory::Allocator& alloc);
+ void DestroyValueArray(ValueArray * valueArray, memory::Allocator& alloc);
+
+ void ValueArrayCopy(ValueArray const* source, ValueArray* destination);
+ void ValueArrayCopy(ValueArrayConstant const* sourceConstant, ValueArray const* source, ValueArrayConstant const* destinationConstant, ValueArray* destination, int32_t const* destinationInSourceIndexArray);
+ void ValueArrayReverseCopy(ValueArrayConstant const* sourceConstant, ValueArray const* source, ValueArrayConstant const* destinationConstant, ValueArray* destination, int32_t const* sourceInDestinationIndexArray);
+ void ValueArrayCopy(ValueArray const *aSource, ValueArray* aValues, ValueArrayMask const *mask);
+
+ void ValueArrayBlend(ValueArray const *apValuesDefault,ValueArray* aValues, ValueArray ** aValuesArray, const float *apWeightArray, uint32_t aCount, const ValueArrayMask *mask);
+
+ void ValueArrayAdd(ValueArray const *apValuesDefault, ValueArray const* apValues, ValueArrayMask const *readMask, float aWeight, bool aAdditive, ValueArray* apValuesOut, ValueArrayMask *defaultMask);
+ void ValueArraySub(ValueArray const &starts, ValueArray &values, ValueArrayMask const *mask);
+ void ValueArrayLoop(ValueArray const &starts, ValueArray const &stops, ValueArray &values, float loopWeight, const ValueArrayMask& mask);
+
+ int32_t FindValueIndex(const ValueArrayConstant *constant, uint32_t id);
+
+ STATIC_INLINE int32_t FindValueIndex(const ValueArrayConstant *constant, char const* binding)
+ {
+ return FindValueIndex(constant, processCRC32(binding));
+ }
+}
diff --git a/Runtime/mecanim/graph/binarynode.h b/Runtime/mecanim/graph/binarynode.h
new file mode 100644
index 0000000..82ff596
--- /dev/null
+++ b/Runtime/mecanim/graph/binarynode.h
@@ -0,0 +1,291 @@
+/*
+ Copyright (c) 7244339 Canada Inc. (Mecanim)
+ All Rights Reserved.
+*/
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/object.h"
+
+#include "Runtime/Math/Simd/float4.h"
+
+#include "Runtime/mecanim/graph/plug.h"
+#include "Runtime/mecanim/graph/node.h"
+
+namespace mecanim
+{
+
+namespace graph
+{
+ template <typename TYPE1, typename TYPE2, typename RESULT, typename BinaryPolicies> class BinaryNode : public Node
+ {
+ public:
+ TypePlug<TYPE1> mA;
+ TypePlug<TYPE2> mB;
+
+ TypePlug<RESULT> mResult;
+
+ BinaryNode()
+ :mA(true, CRCKey(eA) ),
+ mB(true,CRCKey(eB)),
+ mResult(false,CRCKey(eResult))
+ {
+ mA.m_Owner = this;
+ mB.m_Owner = this;
+ mResult.m_Owner = this;
+ }
+
+ virtual ~BinaryNode(){}
+
+ virtual uint32_t GetPlugCount()const
+ {
+ return 3;
+ }
+ virtual GraphPlug& GetPlug(uint32_t aIndex)
+ {
+ switch(aIndex)
+ {
+ case 0: return mA;
+ case 1: return mB;
+ case 2:
+ default: return mResult;
+ }
+ }
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const
+ {
+ switch(aIndex)
+ {
+ case 0: return mA;
+ case 1: return mB;
+ case 2:
+ default: return mResult;
+ }
+ }
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo)
+ {
+ TYPE1 a;
+ TYPE2 b;
+ RESULT result;
+
+ mA.ReadData(&a, arEvaluationInfo);
+ mB.ReadData(&b, arEvaluationInfo);
+
+ result = BinaryPolicies::template Operation<TYPE1, TYPE2, RESULT>(a, b);
+
+ mResult.WriteData(&result, arEvaluationInfo);
+ }
+ };
+
+ class AdditionOp
+ {
+ public:
+ template< typename TYPE1, typename TYPE2, typename RESULT > static RESULT Operation( TYPE1 const& l, TYPE2 const& r){ return l+r; }
+ };
+
+ class SubstractionOp
+ {
+ public:
+ template< typename TYPE1, typename TYPE2, typename RESULT > static RESULT Operation( TYPE1 const& l, TYPE2 const& r){ return l-r; }
+ };
+
+ class MultiplicationOp
+ {
+ public:
+ template< typename TYPE1, typename TYPE2, typename RESULT > static RESULT Operation( TYPE1 const& l, TYPE2 const& r){ return l*r; }
+ };
+
+ class DivisionOp
+ {
+ public:
+ template< typename TYPE1, typename TYPE2, typename RESULT > static RESULT Operation( TYPE1 const& l, TYPE2 const& r){ return l/r; }
+ };
+
+ class GreaterThanOp
+ {
+ public:
+ template< typename TYPE1, typename TYPE2, typename RESULT > static RESULT Operation( TYPE1 const& l, TYPE2 const& r){ return l > r; }
+ };
+ class LesserThanOp
+ {
+ public:
+ template< typename TYPE1, typename TYPE2, typename RESULT > static RESULT Operation( TYPE1 const& l, TYPE2 const& r){ return l < r; }
+ };
+ class GreaterThanOrEqualOp
+ {
+ public:
+ template< typename TYPE1, typename TYPE2, typename RESULT > static RESULT Operation( TYPE1 const& l, TYPE2 const& r){ return l >= r; }
+ };
+ class LesserThanOrEqualOp
+ {
+ public:
+ template< typename TYPE1, typename TYPE2, typename RESULT > static RESULT Operation( TYPE1 const& l, TYPE2 const& r){ return l <= r; }
+ };
+ class AndOp
+ {
+ public:
+ template< typename TYPE1, typename TYPE2, typename RESULT > static RESULT Operation( TYPE1 const& l, TYPE2 const& r){ return l && r; }
+ };
+ class OrOp
+ {
+ public:
+ template< typename TYPE1, typename TYPE2, typename RESULT > static RESULT Operation( TYPE1 const& l, TYPE2 const& r){ return l || r; }
+ };
+ class XorOp
+ {
+ public:
+ template< typename TYPE1, typename TYPE2, typename RESULT > static RESULT Operation( TYPE1 const& l, TYPE2 const& r){ return ((!l)&&r)||(l&&(!r)); }
+ };
+
+ class AdditionFloat : public BinaryNode<float, float, float, AdditionOp>
+ {
+ public:
+ static const eNodeType mId = AdditionFloatId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class AdditionUInt : public BinaryNode<uint32_t, uint32_t, uint32_t,AdditionOp>
+ {
+ public:
+ static const eNodeType mId = AdditionUIntId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class AdditionInt : public BinaryNode<int32_t, int32_t, int32_t, AdditionOp>
+ {
+ public:
+ static const eNodeType mId = AdditionIntId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class AdditionFloat4 : public BinaryNode<math::float4, math::float4, math::float4, AdditionOp>
+ {
+ public:
+ static const eNodeType mId = AdditionFloat4Id;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class SubstractionFloat : public BinaryNode<float, float, float, SubstractionOp>
+ {
+ public:
+ static const eNodeType mId = SubstractionFloatId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class SubstractionUInt : public BinaryNode<uint32_t, uint32_t, uint32_t, SubstractionOp>
+ {
+ public:
+ static const eNodeType mId = SubstractionUIntId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class SubstractionInt : public BinaryNode<int32_t, int32_t, int32_t, SubstractionOp>
+ {
+ public:
+ static const eNodeType mId = SubstractionIntId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class SubstractionFloat4 : public BinaryNode<math::float4, math::float4, math::float4, SubstractionOp>
+ {
+ public:
+ static const eNodeType mId = SubstractionFloat4Id;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class MultiplicationFloat : public BinaryNode<float, float, float, MultiplicationOp>
+ {
+ public:
+ static const eNodeType mId = MultiplicationFloatId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class MultiplicationUInt : public BinaryNode<uint32_t, uint32_t, uint32_t, MultiplicationOp>
+ {
+ public:
+ static const eNodeType mId = MultiplicationUIntId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class MultiplicationInt : public BinaryNode<int32_t, int32_t, int32_t, MultiplicationOp>
+ {
+ public:
+ static const eNodeType mId = MultiplicationIntId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class MultiplicationFloat4 : public BinaryNode<math::float4, math::float4, math::float4, MultiplicationOp>
+ {
+ public:
+ static const eNodeType mId = MultiplicationFloat4Id;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class DivisionFloat : public BinaryNode<float, float, float, DivisionOp>
+ {
+ public:
+ static const eNodeType mId = DivisionFloatId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class DivisionUInt : public BinaryNode<uint32_t, uint32_t, uint32_t, DivisionOp>
+ {
+ public:
+ static const eNodeType mId = DivisionUIntId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class DivisionInt : public BinaryNode<int32_t, int32_t, int32_t, DivisionOp>
+ {
+ public:
+ static const eNodeType mId = DivisionIntId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class DivisionFloat4 : public BinaryNode<math::float4, math::float4, math::float4, DivisionOp>
+ {
+ public:
+ static const eNodeType mId = DivisionFloat4Id;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class GreaterThan : public BinaryNode<float, float, bool, GreaterThanOp>
+ {
+ public:
+ static const eNodeType mId = GreaterThanId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class LesserThan : public BinaryNode<float, float, bool, LesserThanOp>
+ {
+ public:
+ static const eNodeType mId = LesserThanId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class GreaterThanOrEqual : public BinaryNode<float, float, bool, GreaterThanOrEqualOp>
+ {
+ public:
+ static const eNodeType mId = GreaterThanOrEqualId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class LesserThanOrEqual : public BinaryNode<float, float, bool, LesserThanOrEqualOp>
+ {
+ public:
+ static const eNodeType mId = LesserThanOrEqualId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class And : public BinaryNode<bool, bool, bool, AndOp>
+ {
+ public:
+ static const eNodeType mId = AndId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class Or : public BinaryNode<bool, bool, bool, OrOp>
+ {
+ public:
+ static const eNodeType mId = OrId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class Xor : public BinaryNode<bool, bool, bool, XorOp>
+ {
+ public:
+ static const eNodeType mId = XorId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+}
+
+} \ No newline at end of file
diff --git a/Runtime/mecanim/graph/factory.cpp b/Runtime/mecanim/graph/factory.cpp
new file mode 100644
index 0000000..3efe757
--- /dev/null
+++ b/Runtime/mecanim/graph/factory.cpp
@@ -0,0 +1,172 @@
+#include "UnityPrefix.h"
+#include "Runtime/mecanim/graph/factory.h"
+
+#include "Runtime/mecanim/graph/unarynode.h"
+#include "Runtime/mecanim/graph/binarynode.h"
+#include "Runtime/mecanim/graph/genericnode.h"
+#include "Runtime/mecanim/graph/xformnode.h"
+#include "Runtime/mecanim/graph/quaternionnode.h"
+
+#define REGISTERNODE(classnode) \
+ case classnode::mId: \
+ { \
+ node = arAlloc.Construct<classnode>(); \
+ break; \
+ }
+
+namespace mecanim
+{
+
+namespace graph
+{
+ Node* GraphFactory::Create(eNodeType aNodeId, memory::Allocator& arAlloc)const
+ {
+ Node* node = 0;
+ switch(aNodeId)
+ {
+ REGISTERNODE(NegationFloat);
+ REGISTERNODE(NegationInt);
+ REGISTERNODE(NegationFloat4);
+ REGISTERNODE(NegationBool);
+ REGISTERNODE(AdditionFloat);
+ REGISTERNODE(AdditionUInt);
+ REGISTERNODE(AdditionInt);
+ REGISTERNODE(AdditionFloat4);
+ REGISTERNODE(SubstractionFloat);
+ REGISTERNODE(SubstractionUInt);
+ REGISTERNODE(SubstractionInt);
+ REGISTERNODE(SubstractionFloat4);
+ REGISTERNODE(MultiplicationFloat);
+ REGISTERNODE(MultiplicationUInt);
+ REGISTERNODE(MultiplicationInt);
+ REGISTERNODE(MultiplicationFloat4);
+ REGISTERNODE(DivisionFloat);
+ REGISTERNODE(DivisionUInt);
+ REGISTERNODE(DivisionInt);
+ REGISTERNODE(DivisionFloat4);
+ REGISTERNODE(CondFloat);
+ REGISTERNODE(CondUInt);
+ REGISTERNODE(CondInt);
+ REGISTERNODE(CondFloat4);
+ REGISTERNODE(AbsFloat);
+ REGISTERNODE(AbsFloat4);
+ REGISTERNODE(CrossFloat4);
+ REGISTERNODE(DegreesFloat);
+ REGISTERNODE(DegreesFloat4);
+ REGISTERNODE(DotFloat4);
+ REGISTERNODE(LengthFloat4);
+ REGISTERNODE(MaximumFloat);
+ REGISTERNODE(MaximumUInt);
+ REGISTERNODE(MaximumInt);
+ REGISTERNODE(MaximumFloat4);
+ REGISTERNODE(MinimumFloat);
+ REGISTERNODE(MinimumUInt);
+ REGISTERNODE(MinimumInt);
+ REGISTERNODE(MinimumFloat4);
+ REGISTERNODE(NormalizeFloat4);
+ REGISTERNODE(RadiansFloat);
+ REGISTERNODE(RadiansFloat4);
+ REGISTERNODE(FloatToFloat4);
+ REGISTERNODE(Float4ToFloat);
+ REGISTERNODE(GreaterThan);
+ REGISTERNODE(LesserThan);
+ REGISTERNODE(GreaterThanOrEqual);
+ REGISTERNODE(LesserThanOrEqual);
+ REGISTERNODE(SmoothStepFloat);
+ REGISTERNODE(Mux5Float);
+ REGISTERNODE(Mul5Float);
+ REGISTERNODE(Sin);
+ REGISTERNODE(Fmod);
+ REGISTERNODE(And);
+ REGISTERNODE(Or);
+ REGISTERNODE(xformMulInv);
+ REGISTERNODE(xformIdentity);
+ REGISTERNODE(xformMulVec);
+ REGISTERNODE(xformInvMulVec);
+ REGISTERNODE(xformMul);
+ REGISTERNODE(xformInvMul);
+ REGISTERNODE(xformEqual);
+ REGISTERNODE(xformWeight);
+ REGISTERNODE(xformAdd);
+ REGISTERNODE(xformSub);
+ REGISTERNODE(xformBlend);
+ REGISTERNODE(quatIdentity);
+ REGISTERNODE(quatConj);
+ REGISTERNODE(quatMul);
+ REGISTERNODE(quatMulVec);
+ REGISTERNODE(quatLerp);
+ REGISTERNODE(quatArcRotate);
+ REGISTERNODE(quatArcRotateX);
+ REGISTERNODE(quatXcos);
+ REGISTERNODE(quatYcos);
+ REGISTERNODE(quatZcos);
+ REGISTERNODE(quatEulerToQuat);
+ REGISTERNODE(quatQuatToEuler);
+ REGISTERNODE(quatProjOnYPlane);
+ REGISTERNODE(quat2Qtan);
+ REGISTERNODE(qtan2Quat);
+ REGISTERNODE(ZYRoll2Quat);
+ REGISTERNODE(quat2ZYRoll);
+ REGISTERNODE(RollZY2Quat);
+ REGISTERNODE(quat2RollZY);
+ REGISTERNODE(quatWeight);
+ REGISTERNODE(xformCompose);
+ REGISTERNODE(xformDecompose);
+ REGISTERNODE(CondXform);
+ REGISTERNODE(Rand);
+ REGISTERNODE(Damp);
+ REGISTERNODE(xformRefChange);
+ REGISTERNODE(Xor);
+ REGISTERNODE(SmoothPulseFloat);
+ }
+ return node;
+ }
+
+ GraphPlug* GraphFactory::Create(ePlugType aPlugType, memory::Allocator& arAlloc)const
+ {
+ GraphPlug* plug = 0;
+ switch(aPlugType)
+ {
+ case Float4Id:
+ {
+ plug = arAlloc.Construct< TypePlug<math::float4> >();
+ break;
+ }
+ case Float1Id:
+ case FloatId:
+ {
+ plug = arAlloc.Construct< TypePlug<float> >();
+ break;
+ }
+ case UInt32Id:
+ {
+ plug = arAlloc.Construct< TypePlug<uint32_t> >();
+ break;
+ }
+ case Int32Id:
+ {
+ plug = arAlloc.Construct< TypePlug<int32_t> >();
+ break;
+ }
+ case BoolId:
+ {
+ plug = arAlloc.Construct< TypePlug<bool> >();
+ break;
+ }
+ case Bool4Id:
+ {
+ plug = arAlloc.Construct< TypePlug<math::bool4> >();
+ break;
+ }
+ case XformId:
+ {
+ plug = arAlloc.Construct< TypePlug<math::xform> >();
+ break;
+ }
+
+ }
+ return plug;
+ }
+}
+
+}
diff --git a/Runtime/mecanim/graph/factory.h b/Runtime/mecanim/graph/factory.h
new file mode 100644
index 0000000..d76fcbf
--- /dev/null
+++ b/Runtime/mecanim/graph/factory.h
@@ -0,0 +1,35 @@
+/*
+ Copyright (c) 7244339 Canada Inc. (Mecanim)
+ All Rights Reserved.
+*/
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/object.h"
+
+#include "Runtime/mecanim/graph/plug.h"
+#include "Runtime/mecanim/graph/node.h"
+
+namespace mecanim
+{
+
+namespace graph
+{
+ // This class can be subclassed if you need to create custom node and want to add them to the list of
+ // instanciable node
+ class GraphFactory
+ {
+ public:
+ virtual Node* Create(eNodeType aNodeId, memory::Allocator& arAlloc)const;
+ virtual GraphPlug* Create(ePlugType aPlugType, memory::Allocator& arAlloc)const;
+ };
+
+ template <typename TYPE> TYPE* Create(GraphFactory const& arFactory, memory::Allocator& arAlloc)
+ {
+ return static_cast<TYPE*>(arFactory.Create(TYPE::mId, arAlloc));
+ }
+}
+
+} \ No newline at end of file
diff --git a/Runtime/mecanim/graph/genericnode.h b/Runtime/mecanim/graph/genericnode.h
new file mode 100644
index 0000000..2bc82e7
--- /dev/null
+++ b/Runtime/mecanim/graph/genericnode.h
@@ -0,0 +1,1093 @@
+/*
+ Copyright (c) 7244339 Canada Inc. (Mecanim)
+ All Rights Reserved.
+*/
+#pragma once
+
+#include <cstdlib>
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/object.h"
+
+#include "Runtime/Math/Simd/math.h"
+
+#include "Runtime/mecanim/graph/plug.h"
+#include "Runtime/mecanim/graph/node.h"
+
+#include "Runtime/mecanim/graph/unarynode.h"
+
+#include "Runtime/mecanim/generic/stringtable.h"
+
+namespace mecanim
+{
+
+namespace graph
+{
+ template <typename COND, typename TYPE, typename POLICIES> class CondNode : public Node
+ {
+ public:
+ TypePlug<COND> mCondition;
+ TypePlug<TYPE> mA;
+ TypePlug<TYPE> mB;
+
+ TypePlug<TYPE> mResult;
+
+ CondNode()
+ :mCondition(true,CRCKey(eCondition)),
+ mA(true, CRCKey(eA)),
+ mB(true,CRCKey(eB)),
+ mResult(false,CRCKey(eResult))
+ {
+ mCondition.m_Owner = this;
+ mA.m_Owner = this;
+ mB.m_Owner = this;
+ mResult.m_Owner = this;
+ }
+
+ virtual ~CondNode(){}
+
+ virtual uint32_t GetPlugCount()const
+ {
+ return 4;
+ }
+ virtual GraphPlug& GetPlug(uint32_t aIndex)
+ {
+ switch(aIndex)
+ {
+ case 0: return mCondition;
+ case 1: return mA;
+ case 2: return mB;
+ case 3:
+ default: return mResult;
+ }
+ }
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const
+ {
+ switch(aIndex)
+ {
+ case 0: return mCondition;
+ case 1: return mA;
+ case 2: return mB;
+ case 3:
+ default: return mResult;
+ }
+ }
+
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo)
+ {
+ TYPE a, b, result;
+ COND cond;
+
+ mCondition.ReadData(&cond, arEvaluationInfo);
+ mA.ReadData(&a, arEvaluationInfo);
+ mB.ReadData(&b, arEvaluationInfo);
+
+ result = POLICIES::template Operation<COND, TYPE>(cond, a, b);
+
+ mResult.WriteData(&result, arEvaluationInfo);
+ }
+ };
+
+ class CondFloatOp
+ {
+ public:
+ template< typename COND, typename TYPE > static TYPE Operation( COND const& p, TYPE const& l, TYPE const& r){ return math::cond(p, l, r); }
+ };
+ class CondUIntOp
+ {
+ public:
+ template< typename COND, typename TYPE > static TYPE Operation( COND const& p, TYPE const& l, TYPE const& r){ return math::cond(p, l, r); }
+ };
+ class CondIntOp
+ {
+ public:
+ template< typename COND, typename TYPE > static TYPE Operation( COND const& p, TYPE const& l, TYPE const& r){ return math::cond(p, l, r); }
+ };
+ class CondFloat4Op
+ {
+ public:
+ template< typename COND, typename TYPE > static math::float4 Operation( bool const& p, math::float4 const& l, math::float4 const& r){ return math::cond(math::bool1(p), l, r); }
+ };
+ class CondXformOp
+ {
+ public:
+ template< typename COND, typename TYPE > static TYPE Operation( COND const& p, TYPE const& l, TYPE const& r){ return math::cond(p, l, r); }
+ };
+
+ class CondFloat : public CondNode<bool, float, CondFloatOp>
+ {
+ public:
+ static const eNodeType mId = CondFloatId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class CondUInt : public CondNode<bool, uint32_t, CondUIntOp>
+ {
+ public:
+ static const eNodeType mId = CondUIntId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class CondInt : public CondNode<bool, int32_t, CondIntOp>
+ {
+ public:
+ static const eNodeType mId = CondIntId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class CondFloat4 : public CondNode<bool, math::float4, CondFloat4Op>
+ {
+ public:
+ static const eNodeType mId = CondFloat4Id;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class CondXform : public CondNode<bool, math::xform, CondXformOp>
+ {
+ public:
+ static const eNodeType mId = CondXformId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ template< typename TYPE, typename RESULT > class AbsOp
+ {
+ public:
+ static RESULT Operation( TYPE const& l){ return math::abs(l); }
+ };
+
+ class AbsFloat : public UnaryNode<float, float, AbsOp<float, float> >
+ {
+ public:
+ static const eNodeType mId = AbsFloatId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class AbsFloat4 : public UnaryNode<math::float4, math::float4, AbsOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = AbsFloat4Id;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class CrossOp
+ {
+ public:
+ template< typename TYPE1, typename TYPE2, typename RESULT > static RESULT Operation( TYPE1 const& l, TYPE2 const& r){ return math::cross(l, r); }
+ };
+
+ class CrossFloat4 : public BinaryNode<math::float4, math::float4, math::float4, CrossOp>
+ {
+ public:
+ static const eNodeType mId = CrossFloat4Id;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ template< typename TYPE, typename RESULT > class DegreesOp
+ {
+ public:
+ static RESULT Operation( TYPE const& l){ return math::degrees(l); }
+ };
+
+ class DegreesFloat : public UnaryNode<float, float, DegreesOp<float, float> >
+ {
+ public:
+ static const eNodeType mId = DegreesFloatId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class DegreesFloat4 : public UnaryNode<math::float4, math::float4, DegreesOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = DegreesFloat4Id;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class DotOp
+ {
+ public:
+ template< typename TYPE1, typename TYPE2, typename RESULT > static RESULT Operation( TYPE1 const& l, TYPE2 const& r){ return float(math::dot(l, r)); }
+ };
+
+ class DotFloat4 : public BinaryNode<math::float4, math::float4, float, DotOp>
+ {
+ public:
+ static const eNodeType mId = DotFloat4Id;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ template< typename TYPE, typename RESULT > class LengthOp
+ {
+ public:
+ static RESULT Operation( TYPE const& l){ return float(math::length(l)); }
+ };
+
+ class LengthFloat4 : public UnaryNode<math::float4, float, LengthOp<math::float4, float> >
+ {
+ public:
+ static const eNodeType mId = LengthFloat4Id;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class MaximumOp
+ {
+ public:
+ template< typename TYPE1, typename TYPE2, typename RESULT > static RESULT Operation( TYPE1 const& l, TYPE2 const& r){ return math::maximum(l, r); }
+ };
+
+ class MaximumFloat : public BinaryNode<float, float, float, MaximumOp>
+ {
+ public:
+ static const eNodeType mId = MaximumFloatId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class MaximumUInt : public BinaryNode<uint32_t, uint32_t, uint32_t, MaximumOp>
+ {
+ public:
+ static const eNodeType mId = MaximumUIntId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class MaximumInt : public BinaryNode<int32_t, int32_t, int32_t, MaximumOp>
+ {
+ public:
+ static const eNodeType mId = MaximumIntId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class MaximumFloat4 : public BinaryNode<math::float4, math::float4, math::float4, MaximumOp>
+ {
+ public:
+ static const eNodeType mId = MaximumFloat4Id;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class MinimumOp
+ {
+ public:
+ template< typename TYPE1, typename TYPE2, typename RESULT > static RESULT Operation( TYPE1 const& l, TYPE2 const& r){ return math::minimum(l, r); }
+ };
+
+ class MinimumFloat : public BinaryNode<float, float, float, MinimumOp>
+ {
+ public:
+ static const eNodeType mId = MinimumFloatId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class MinimumUInt : public BinaryNode<uint32_t, uint32_t, uint32_t, MinimumOp>
+ {
+ public:
+ static const eNodeType mId = MinimumUIntId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class MinimumInt : public BinaryNode<int32_t, int32_t, int32_t, MinimumOp>
+ {
+ public:
+ static const eNodeType mId = MinimumIntId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class MinimumFloat4 : public BinaryNode<math::float4, math::float4, math::float4, MinimumOp>
+ {
+ public:
+ static const eNodeType mId = MinimumFloat4Id;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ template< typename TYPE, typename RESULT > class NormalizeOp
+ {
+ public:
+ static RESULT Operation( TYPE const& l){ return math::normalize(l); }
+ };
+
+ class NormalizeFloat4 : public UnaryNode<math::float4, math::float4, NormalizeOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = NormalizeFloat4Id;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ template< typename TYPE, typename RESULT > class RadiansOp
+ {
+ public:
+ static RESULT Operation( TYPE const& l){ return math::radians(l); }
+ };
+
+ class RadiansFloat : public UnaryNode<float, float, RadiansOp<float, float> >
+ {
+ public:
+ static const eNodeType mId = RadiansFloatId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class RadiansFloat4 : public UnaryNode<math::float4, math::float4, RadiansOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = RadiansFloat4Id;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class Float4ToFloat : public Node
+ {
+ public:
+ static const eNodeType mId = Float4ToFloatId;
+ virtual eNodeType NodeType(){return mId;}
+
+ TypePlug<math::float4> mA;
+ TypePlug<float> mX;
+ TypePlug<float> mY;
+ TypePlug<float> mZ;
+ TypePlug<float> mW;
+
+ Float4ToFloat()
+ :mA(true, CRCKey(eA)),
+ mX(false, CRCKey(eX)),
+ mY(false, CRCKey(eY)),
+ mZ(false, CRCKey(eZ)),
+ mW(false, CRCKey(eW))
+ {
+ mA.m_Owner = this;
+ mX.m_Owner = this;
+ mY.m_Owner = this;
+ mZ.m_Owner = this;
+ mW.m_Owner = this;
+ }
+
+ virtual ~Float4ToFloat(){}
+
+ virtual uint32_t GetPlugCount()const
+ {
+ return 5;
+ }
+ virtual GraphPlug& GetPlug(uint32_t aIndex)
+ {
+ switch(aIndex)
+ {
+ case 0: return mA;
+ case 1: return mX;
+ case 2: return mY;
+ case 3: return mZ;
+ case 4:
+ default: return mW;
+ }
+ }
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const
+ {
+ switch(aIndex)
+ {
+ case 0: return mA;
+ case 1: return mX;
+ case 2: return mY;
+ case 3: return mZ;
+ case 4:
+ default: return mW;
+ }
+ }
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo)
+ {
+ math::float4 a;
+
+ mA.ReadData(&a, arEvaluationInfo);
+
+ float ATTRIBUTE_ALIGN(ALIGN4F) b[4];
+
+ math::store(a, b);
+
+ mX.WriteData(&b[0], arEvaluationInfo);
+ mY.WriteData(&b[1], arEvaluationInfo);
+ mZ.WriteData(&b[2], arEvaluationInfo);
+ mW.WriteData(&b[3], arEvaluationInfo);
+ }
+ };
+
+ class FloatToFloat4 : public Node
+ {
+ public:
+ static const eNodeType mId = FloatToFloat4Id;
+ virtual eNodeType NodeType(){return mId;}
+
+ TypePlug<float> mX;
+ TypePlug<float> mY;
+ TypePlug<float> mZ;
+ TypePlug<float> mW;
+
+ TypePlug<math::float4> mResult;
+
+
+ FloatToFloat4()
+ :mX(true,CRCKey(eX)),
+ mY(true,CRCKey(eY)),
+ mZ(true,CRCKey(eZ)),
+ mW(true,CRCKey(eW)),
+ mResult(false,CRCKey(eResult))
+ {
+ mX.m_Owner = this;
+ mY.m_Owner = this;
+ mZ.m_Owner = this;
+ mW.m_Owner = this;
+ mResult.m_Owner = this;
+ }
+
+ virtual ~FloatToFloat4(){}
+
+ virtual uint32_t GetPlugCount()const
+ {
+ return 5;
+ }
+ virtual GraphPlug& GetPlug(uint32_t aIndex)
+ {
+ switch(aIndex)
+ {
+ case 0: return mResult;
+ case 1: return mX;
+ case 2: return mY;
+ case 3: return mZ;
+ case 4:
+ default: return mW;
+ }
+ }
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const
+ {
+ switch(aIndex)
+ {
+ case 0: return mResult;
+ case 1: return mX;
+ case 2: return mY;
+ case 3: return mZ;
+ case 4:
+ default: return mW;
+ }
+ }
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo)
+ {
+ float ATTRIBUTE_ALIGN(ALIGN4F) a[4];
+
+ mX.ReadData(&a[0], arEvaluationInfo);
+ mY.ReadData(&a[1], arEvaluationInfo);
+ mZ.ReadData(&a[2], arEvaluationInfo);
+ mW.ReadData(&a[3], arEvaluationInfo);
+
+ math::float4 b = math::load(a);
+ mResult.WriteData(&b, arEvaluationInfo);
+ }
+ };
+
+
+ class SmoothStepFloat : public Node
+ {
+ public:
+ static const eNodeType mId = SmoothstepFloatId;
+ virtual eNodeType NodeType(){return mId;}
+
+ TypePlug<float> mMin;
+ TypePlug<float> mMax;
+ TypePlug<float> mValue;
+ TypePlug<float> mResult;
+
+ SmoothStepFloat()
+ :mMin(true, CRCKey(eMin)),
+ mMax(true,CRCKey(eMax)),
+ mValue(true,CRCKey(eValue)),
+ mResult(false,CRCKey(eResult))
+ {
+ mMin.m_Owner = this;
+ mMax.m_Owner = this;
+ mValue.m_Owner = this;
+ mResult.m_Owner = this;
+ }
+
+ virtual ~SmoothStepFloat(){}
+
+ virtual uint32_t GetPlugCount()const
+ {
+ return 4;
+ }
+ virtual GraphPlug& GetPlug(uint32_t aIndex)
+ {
+ switch(aIndex)
+ {
+ case 0: return mMin;
+ case 1: return mMax;
+ case 2: return mValue;
+ case 3:
+ default: return mResult;
+
+ }
+ }
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const
+ {
+ switch(aIndex)
+ {
+ case 0: return mMin;
+ case 1: return mMax;
+ case 2: return mValue;
+ case 3:
+ default: return mResult;
+ }
+ }
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo)
+ {
+ float min, max, value;
+
+ mMin.ReadData(&min, arEvaluationInfo);
+ mMax.ReadData(&max, arEvaluationInfo);
+ mValue.ReadData(&value, arEvaluationInfo);
+
+ float ret = math::smoothstep( min, max, value);
+
+ mResult.WriteData(&ret, arEvaluationInfo);
+ }
+ };
+
+ class SmoothPulseFloat : public Node
+ {
+ public:
+ static const eNodeType mId = SmoothPulseFloatId;
+ virtual eNodeType NodeType(){return mId;}
+
+ TypePlug<float> mMinMin;
+ TypePlug<float> mMinMax;
+ TypePlug<float> mMaxMin;
+ TypePlug<float> mMaxMax;
+ TypePlug<float> mValue;
+ TypePlug<float> mResult;
+
+ SmoothPulseFloat():
+ mMinMin(true, CRCKey(eMinMin)),
+ mMinMax(true, CRCKey(eMinMax)),
+ mMaxMin(true, CRCKey(eMaxMin)),
+ mMaxMax(true, CRCKey(eMaxMax)),
+ mValue(true, CRCKey(eValue)),
+ mResult(false, CRCKey(eResult))
+ {
+ mMinMin.m_Owner = this;
+ mMinMax.m_Owner = this;
+ mMaxMin.m_Owner = this;
+ mMaxMax.m_Owner = this;
+ mValue.m_Owner = this;
+ mResult.m_Owner = this;
+ }
+
+ virtual ~SmoothPulseFloat(){}
+
+ virtual uint32_t GetPlugCount()const
+ {
+ return 6;
+ }
+ virtual GraphPlug& GetPlug(uint32_t aIndex)
+ {
+ switch(aIndex)
+ {
+ case 0: return mMinMin;
+ case 1: return mMinMax;
+ case 2: return mMaxMin;
+ case 3: return mMaxMax;
+ case 4: return mValue;
+ case 5:
+ default: return mResult;
+
+ }
+ }
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const
+ {
+ switch(aIndex)
+ {
+ case 0: return mMinMin;
+ case 1: return mMinMax;
+ case 2: return mMaxMin;
+ case 3: return mMaxMax;
+ case 4: return mValue;
+ case 5:
+ default: return mResult;
+ }
+ }
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo)
+ {
+ float minmin, minmax, maxmin, maxmax, value;
+
+ mMinMin.ReadData(&minmin, arEvaluationInfo);
+ mMinMax.ReadData(&minmax, arEvaluationInfo);
+ mMaxMin.ReadData(&maxmin, arEvaluationInfo);
+ mMaxMax.ReadData(&maxmax, arEvaluationInfo);
+ mValue.ReadData(&value, arEvaluationInfo);
+
+ float ret = math::smoothpulse( minmin, minmax, maxmin, maxmax, value);
+
+ mResult.WriteData(&ret, arEvaluationInfo);
+ }
+ };
+
+ class Mux5Float : public Node
+ {
+ public:
+ static const eNodeType mId = Mux5FloatId;
+ virtual eNodeType NodeType(){return mId;}
+
+ TypePlug<float> mIn;
+ TypePlug<float> mRangeA;
+ TypePlug<float> mRangeB;
+ TypePlug<float> mRangeC;
+ TypePlug<float> mRangeD;
+ TypePlug<float> mRangeE;
+ TypePlug<float> mWeightA;
+ TypePlug<float> mWeightB;
+ TypePlug<float> mWeightC;
+ TypePlug<float> mWeightD;
+ TypePlug<float> mWeightE;
+
+ Mux5Float() : mIn(true,CRCKey(eIn)),
+ mRangeA(true,CRCKey(eRangeA)),
+ mRangeB(true,CRCKey(eRangeB)),
+ mRangeC(true,CRCKey(eRangeC)),
+ mRangeD(true,CRCKey(eRangeD)),
+ mRangeE(true,CRCKey(eRangeE)),
+ mWeightA(false,CRCKey(eWeightA)),
+ mWeightB(false,CRCKey(eWeightB)),
+ mWeightC(false,CRCKey(eWeightC)),
+ mWeightD(false,CRCKey(eWeightD)),
+ mWeightE(false,CRCKey(eWeightE))
+ {
+ mIn.m_Owner = this;
+ mRangeA.m_Owner = this;
+ mRangeB.m_Owner = this;
+ mRangeC.m_Owner = this;
+ mRangeD.m_Owner = this;
+ mRangeE.m_Owner = this;
+ mWeightA.m_Owner = this;
+ mWeightB.m_Owner = this;
+ mWeightC.m_Owner = this;
+ mWeightD.m_Owner = this;
+ mWeightE.m_Owner = this;
+ }
+
+ virtual ~Mux5Float(){}
+
+ virtual uint32_t GetPlugCount()const
+ {
+ return 11;
+ }
+ virtual GraphPlug& GetPlug(uint32_t aIndex)
+ {
+ switch(aIndex)
+ {
+ case 0: return mIn;
+ case 1: return mRangeA;
+ case 2: return mRangeB;
+ case 3: return mRangeC;
+ case 4: return mRangeD;
+ case 5: return mRangeE;
+ case 6: return mWeightA;
+ case 7: return mWeightB;
+ case 8: return mWeightC;
+ case 9: return mWeightD;
+ case 10:
+ default: return mWeightE;
+ }
+ }
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const
+ {
+ switch(aIndex)
+ {
+ case 0: return mIn;
+ case 1: return mRangeA;
+ case 2: return mRangeB;
+ case 3: return mRangeC;
+ case 4: return mRangeD;
+ case 5: return mRangeE;
+ case 6: return mWeightA;
+ case 7: return mWeightB;
+ case 8: return mWeightC;
+ case 9: return mWeightD;
+ case 10:
+ default: return mWeightE;
+ }
+ }
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo)
+ {
+ float in;
+ float r[5];
+ float w[5];
+
+ mIn.ReadData(&in,arEvaluationInfo);
+ mRangeA.ReadData(&r[0],arEvaluationInfo);
+ mRangeB.ReadData(&r[1],arEvaluationInfo);
+ mRangeC.ReadData(&r[2],arEvaluationInfo);
+ mRangeD.ReadData(&r[3],arEvaluationInfo);
+ mRangeE.ReadData(&r[4],arEvaluationInfo);
+
+ w[0] = 1.0f;
+ w[1] = 0.0f;
+ w[2] = 0.0f;
+ w[3] = 0.0f;
+ w[4] = 0.0f;
+
+ if(in > r[0])
+ {
+ w[0] = 0.f;
+ for(uint32_t i = 0; i < 4; i++)
+ {
+ float d = r[i+1] - r[i];
+
+ if(d > 0.0f)
+ {
+ if(in >= r[i] && in <= r[i+1])
+ {
+ w[i] = (d-(in-r[i]))/(r[i+1]-r[i]);
+ w[i+1] = 1.f-w[i];
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ mWeightA.WriteData(&w[0],arEvaluationInfo);
+ mWeightB.WriteData(&w[1],arEvaluationInfo);
+ mWeightC.WriteData(&w[2],arEvaluationInfo);
+ mWeightD.WriteData(&w[3],arEvaluationInfo);
+ mWeightE.WriteData(&w[4],arEvaluationInfo);
+ }
+ };
+
+ class Mul5Float : public Node
+ {
+ public:
+ static const eNodeType mId = Mul5FloatId;
+ virtual eNodeType NodeType(){return mId;}
+
+ TypePlug<float> mIn;
+ TypePlug<float> mA;
+ TypePlug<float> mB;
+ TypePlug<float> mC;
+ TypePlug<float> mD;
+ TypePlug<float> mE;
+ TypePlug<float> mOutA;
+ TypePlug<float> mOutB;
+ TypePlug<float> mOutC;
+ TypePlug<float> mOutD;
+ TypePlug<float> mOutE;
+
+ Mul5Float() : mIn(true,CRCKey(eIn)),
+ mA(true,CRCKey(eA)),
+ mB(true,CRCKey(eB)),
+ mC(true,CRCKey(eC)),
+ mD(true,CRCKey(eD)),
+ mE(true,CRCKey(eE)),
+ mOutA(false,CRCKey(eOutA)),
+ mOutB(false,CRCKey(eOutB)),
+ mOutC(false,CRCKey(eOutC)),
+ mOutD(false,CRCKey(eOutD)),
+ mOutE(false,CRCKey(eOutE))
+ {
+ mIn.m_Owner = this;
+ mOutA.m_Owner = this;
+ mOutB.m_Owner = this;
+ mOutC.m_Owner = this;
+ mD.m_Owner = this;
+ mE.m_Owner = this;
+ mOutA.m_Owner = this;
+ mOutB.m_Owner = this;
+ mOutC.m_Owner = this;
+ mOutD.m_Owner = this;
+ mOutE.m_Owner = this;
+ }
+
+ virtual ~Mul5Float(){}
+
+ virtual uint32_t GetPlugCount()const
+ {
+ return 11;
+ }
+ virtual GraphPlug& GetPlug(uint32_t aIndex)
+ {
+ switch(aIndex)
+ {
+ case 0: return mIn;
+ case 1: return mA;
+ case 2: return mB;
+ case 3: return mC;
+ case 4: return mD;
+ case 5: return mE;
+ case 6: return mOutA;
+ case 7: return mOutB;
+ case 8: return mOutC;
+ case 9: return mOutD;
+ case 10:
+ default: return mOutE;
+ }
+ }
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const
+ {
+ switch(aIndex)
+ {
+ case 0: return mIn;
+ case 1: return mA;
+ case 2: return mB;
+ case 3: return mC;
+ case 4: return mD;
+ case 5: return mE;
+ case 6: return mOutA;
+ case 7: return mOutB;
+ case 8: return mOutC;
+ case 9: return mOutD;
+ case 10:
+ default: return mOutE;
+ }
+ }
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo)
+ {
+ float in;
+ float i[5];
+ float o[5];
+
+ mIn.ReadData(&in,arEvaluationInfo);
+ mA.ReadData(&i[0],arEvaluationInfo);
+ mB.ReadData(&i[1],arEvaluationInfo);
+ mC.ReadData(&i[2],arEvaluationInfo);
+ mD.ReadData(&i[3],arEvaluationInfo);
+ mE.ReadData(&i[4],arEvaluationInfo);
+
+ for(uint32_t iter = 0; iter < 5; iter++)
+ {
+ o[iter] = i[iter] * in;
+ }
+
+ mOutA.WriteData(&o[0],arEvaluationInfo);
+ mOutB.WriteData(&o[1],arEvaluationInfo);
+ mOutC.WriteData(&o[2],arEvaluationInfo);
+ mOutD.WriteData(&o[3],arEvaluationInfo);
+ mOutE.WriteData(&o[4],arEvaluationInfo);
+ }
+ };
+
+ class Fmod : public Node
+ {
+ public:
+ static const eNodeType mId = FmodId;
+ virtual eNodeType NodeType(){return mId;}
+
+ TypePlug<float> mNum;
+ TypePlug<float> mDen;
+ TypePlug<float> mRem;
+
+ Fmod() : mNum(true,CRCKey(eNum)), mDen(true,CRCKey(eDen)), mRem(false, CRCKey(eRem))
+ {
+ mNum.m_Owner = this;
+ mDen.m_Owner = this;
+ mRem.m_Owner = this;
+ }
+
+ virtual ~Fmod(){}
+
+ virtual uint32_t GetPlugCount()const
+ {
+ return 3;
+ }
+ virtual GraphPlug& GetPlug(uint32_t aIndex)
+ {
+ switch(aIndex)
+ {
+ case 0: return mNum;
+ case 1: return mDen;
+ case 2:
+ default: return mRem;
+ }
+ }
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const
+ {
+ switch(aIndex)
+ {
+ case 0: return mNum;
+ case 1: return mDen;
+ case 2:
+ default: return mRem;
+ }
+ }
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo)
+ {
+ float n,d,r;
+
+ mNum.ReadData(&n, arEvaluationInfo);
+ mDen.ReadData(&d, arEvaluationInfo);
+
+ r = math::fmod(n,d);
+
+ mRem.WriteData(&r,arEvaluationInfo);
+ }
+ };
+
+ class Sin : public Node
+ {
+ public:
+ static const eNodeType mId = SinId;
+ virtual eNodeType NodeType(){return mId;}
+
+ TypePlug<float> mIn;
+ TypePlug<float> mOut;
+
+ Sin() : mIn(true,CRCKey(eIn)), mOut(false,CRCKey(eOut))
+ {
+ mIn.m_Owner = this;
+ mOut.m_Owner = this;
+ }
+
+ virtual ~Sin(){}
+
+ virtual uint32_t GetPlugCount()const
+ {
+ return 2;
+ }
+ virtual GraphPlug& GetPlug(uint32_t aIndex)
+ {
+ switch(aIndex)
+ {
+ case 0: return mIn;
+ case 1:
+ default: return mOut;
+ }
+ }
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const
+ {
+ switch(aIndex)
+ {
+ case 0: return mIn;
+ case 1:
+ default: return mOut;
+ }
+ }
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo)
+ {
+ float i,o;
+
+ mIn.ReadData(&i, arEvaluationInfo);
+
+ o = math::sin(i);
+
+ mOut.WriteData(&o,arEvaluationInfo);
+ }
+ };
+
+ class Rand : public Node
+ {
+ public:
+ static const eNodeType mId = RandId;
+ virtual eNodeType NodeType(){return mId;}
+
+ TypePlug<float> mResult;
+
+ Rand() : mResult(false,CRCKey(eResult))
+ {
+ mResult.m_Owner = this;
+ }
+
+ virtual ~Rand(){}
+
+ virtual uint32_t GetPlugCount()const
+ {
+ return 1;
+ }
+ virtual GraphPlug& GetPlug(uint32_t aIndex)
+ {
+ switch(aIndex)
+ {
+ case 0:
+ default: return mResult;
+ }
+ }
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const
+ {
+ switch(aIndex)
+ {
+ case 0:
+ default: return mResult;
+ }
+ }
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo)
+ {
+ float intpart = static_cast<float>(std::rand());
+ float fractpart = (std::rand() % 100) / 100.f;
+
+ float result = intpart+fractpart;
+
+ mResult.WriteData(&result,arEvaluationInfo);
+ }
+ };
+
+ class Damp : public Node
+ {
+ public:
+ static const eNodeType mId = DampId;
+ virtual eNodeType NodeType(){return mId;}
+
+ TypePlug<float> mDampTime;
+ TypePlug<float> mValue;
+ TypePlug<float> mDeltaTime;
+ TypePlug<float> mPreviousValue;
+ TypePlug<float> mResult;
+
+ Damp()
+ :mDampTime(true,CRCKey(eDampTime)),
+ mValue(true,CRCKey(eValue)),
+ mDeltaTime(true,CRCKey(eDeltaTime)),
+ mPreviousValue(true,CRCKey(ePreviousValue)),
+ mResult(false,CRCKey(eResult))
+ {
+ mDampTime.m_Owner = this;
+ mValue.m_Owner = this;
+ mDeltaTime.m_Owner = this;
+ mPreviousValue.m_Owner = this;
+ mResult.m_Owner = this;
+ }
+
+ virtual ~Damp(){}
+
+ virtual uint32_t GetPlugCount()const
+ {
+ return 5;
+ }
+ virtual GraphPlug& GetPlug(uint32_t aIndex)
+ {
+ switch(aIndex)
+ {
+ case 0: return mDampTime;
+ case 1: return mValue;
+ case 2: return mDeltaTime;
+ case 3: return mPreviousValue;
+ case 4:
+ default: return mResult;
+ }
+ }
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const
+ {
+ switch(aIndex)
+ {
+ case 0: return mDampTime;
+ case 1: return mValue;
+ case 2: return mDeltaTime;
+ case 3: return mPreviousValue;
+ case 4:
+ default: return mResult;
+ }
+ }
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo)
+ {
+ float dampTime,value,deltaTime,previousValue, result;
+
+ mDampTime.ReadData(&dampTime, arEvaluationInfo);
+ mValue.ReadData(&value, arEvaluationInfo);
+ mDeltaTime.ReadData(&deltaTime, arEvaluationInfo);
+ mPreviousValue.ReadData(&previousValue, arEvaluationInfo);
+
+ result = math::cond(dampTime > 0, previousValue + (value - previousValue) * deltaTime / (dampTime + deltaTime), value);
+
+ mResult.WriteData(&result,arEvaluationInfo);
+ }
+ };
+
+}
+
+}
diff --git a/Runtime/mecanim/graph/graph.cpp b/Runtime/mecanim/graph/graph.cpp
new file mode 100644
index 0000000..4edbec7
--- /dev/null
+++ b/Runtime/mecanim/graph/graph.cpp
@@ -0,0 +1,476 @@
+#include "UnityPrefix.h"
+#include "Runtime/Math/Simd/math.h"
+#include "Runtime/mecanim/graph/graph.h"
+#include "Runtime/mecanim/graph/factory.h"
+
+namespace mecanim
+{
+
+namespace graph
+{
+ static void AssignPlugId(EvaluationGraph* apGraph)
+ {
+ // Assigning id to each graph plug
+ uint32_t i, id = 0;
+ for(i=0;i<apGraph->m_NodeCount;i++)
+ {
+ Node* node = apGraph->m_NodeArray[i];
+
+ apGraph->m_Graph.m_Vertices[i].m_Id = node->NodeType();
+ apGraph->m_Graph.m_Vertices[i].m_Binding = node->mID;
+ uint32_t j;
+ for(j=0;j<node->GetPlugCount();j++)
+ {
+ node->GetPlug(j).m_PlugId = id++;
+ }
+ }
+
+ for(i=0;i<apGraph->m_Input->GetPlugCount();i++)
+ {
+ apGraph->m_Input->GetPlug(i).m_PlugId = id++;
+ }
+
+ for(i=0;i<apGraph->m_Output->GetPlugCount();i++)
+ {
+ apGraph->m_Output->GetPlug(i).m_PlugId = id++;
+ }
+ }
+
+ static void AssignPlugOffset(EvaluationGraph* apGraph, memory::Allocator& arAlloc)
+ {
+ // assigning value offset for each plug
+ uint32_t i, offset = 0;
+ for(i=0;i<apGraph->m_NodeCount;i++)
+ {
+ Node* node = apGraph->m_NodeArray[i];
+ uint32_t j;
+ for(j=0;j<node->GetPlugCount();j++)
+ {
+ GraphPlug& plug = node->GetPlug(j);
+ offset = plug.m_Offset = arAlloc.AlignAddress(offset, plug.ValueAlign());
+ offset += plug.ValueSize();
+ }
+ }
+
+ for(i=0;i<apGraph->m_Input->GetPlugCount();i++)
+ {
+ GraphPlug& plug = apGraph->m_Input->GetPlug(i);
+ offset = plug.m_Offset = arAlloc.AlignAddress(offset, plug.ValueAlign());
+ offset += plug.ValueSize();
+ }
+
+ for(i=0;i<apGraph->m_Output->GetPlugCount();i++)
+ {
+ GraphPlug& plug = apGraph->m_Output->GetPlug(i);
+ offset = plug.m_Offset = arAlloc.AlignAddress(offset, plug.ValueAlign());
+ offset += plug.ValueSize();
+ }
+ }
+
+ static void ConnectEdges(EvaluationGraph* apGraph)
+ {
+ uint32_t i, edgesIt = 0;
+ for(i=0;i<apGraph->m_NodeCount;i++)
+ {
+ Node* node = apGraph->m_NodeArray[i];
+ uint32_t j;
+ for(j=0;j<node->GetPlugCount();j++)
+ {
+ GraphPlug* plug = node->GetPlug(j).GetSource();
+ if(plug)
+ {
+ apGraph->m_Graph.m_Edges[edgesIt].m_SourceId = plug->m_PlugId;
+ apGraph->m_Graph.m_Edges[edgesIt].m_DestinationId = node->GetPlug(j).m_PlugId;
+ edgesIt++;
+ }
+ }
+ }
+
+ for(i=0;i<apGraph->m_Output->GetPlugCount();i++)
+ {
+ GraphPlug* plug = apGraph->m_Output->mPlugArray[i]->GetSource();
+ if(plug)
+ {
+ apGraph->m_Graph.m_Edges[edgesIt].m_SourceId = plug->m_PlugId;
+ apGraph->m_Graph.m_Edges[edgesIt].m_DestinationId = apGraph->m_Output->mPlugArray[i]->m_PlugId;
+ edgesIt++;
+ }
+ }
+ }
+
+ static EvaluationGraph* CreateGraph(Node** apNodeArray, uint32_t aNodeCount,
+ GraphPlug** apInputPlugArray, uint32_t aInputPlugCount,
+ GraphPlug** apOutputPlugArray, uint32_t aOutputPlugCount,
+ uint32_t aConnectionCount,
+ Constant** apConstantArray, uint32_t aConstantCount,
+ memory::Allocator& arAlloc)
+ {
+ EvaluationGraph* cst = arAlloc.Construct<EvaluationGraph>();
+
+ cst->m_NodeCount = aNodeCount;
+ cst->m_NodeArray = arAlloc.ConstructArray<Node*>(aNodeCount);
+
+ cst->m_Graph.m_VerticesCount = aNodeCount;
+ cst->m_Graph.m_Vertices = arAlloc.ConstructArray<Vertex>(aNodeCount);
+
+ cst->m_Graph.m_EdgesCount = aConnectionCount;
+ cst->m_Graph.m_Edges = arAlloc.ConstructArray<Edge>(aConnectionCount);
+
+ cst->m_Graph.m_InEdgesCount = aInputPlugCount;
+ cst->m_Graph.m_InEdges = arAlloc.ConstructArray<ExternalEdge>(aInputPlugCount);
+
+ cst->m_Graph.m_OutEdgesCount = aOutputPlugCount;
+ cst->m_Graph.m_OutEdges = arAlloc.ConstructArray<ExternalEdge>(aOutputPlugCount);
+
+ cst->m_Graph.m_ConstantCount = aConstantCount;
+ cst->m_Graph.m_ConstantEdges = arAlloc.ConstructArray<ConstantEdge>(cst->m_Graph.m_ConstantCount);
+
+ cst->m_Input = arAlloc.Construct<GraphInput>();
+
+ cst->m_Input->mPlugArray = arAlloc.ConstructArray<GraphPlug*>(aInputPlugCount);
+
+ cst->m_Output = arAlloc.Construct<GraphOutput>();
+
+ cst->m_Output->mPlugArray = arAlloc.ConstructArray<GraphPlug*>(aOutputPlugCount);
+
+ cst->m_ConstantCount = aConstantCount;
+ cst->m_ConstantArray = arAlloc.ConstructArray<Constant*>(aConstantCount);
+
+ memcpy(&cst->m_NodeArray[0], &apNodeArray[0], sizeof(Node*)*aNodeCount);
+
+ memcpy(&cst->m_ConstantArray[0], &apConstantArray[0], sizeof(Constant*)*aConstantCount);
+
+ cst->m_Input->mPlugCount = aInputPlugCount;
+ memcpy(&cst->m_Input->mPlugArray[0], &apInputPlugArray[0], sizeof(GraphPlug*)*aInputPlugCount);
+ uint32_t i;
+ for(i=0;i<aInputPlugCount;i++)
+ {
+ cst->m_Input->mPlugArray[i]->m_Owner = cst->m_Input;
+ cst->m_Graph.m_InEdges[i].m_Id = GetPlugType(*apInputPlugArray[i]);
+ cst->m_Graph.m_InEdges[i].m_Binding = apInputPlugArray[i]->m_ID;
+ }
+
+ cst->m_Output->mPlugCount = aOutputPlugCount;
+ memcpy(&cst->m_Output->mPlugArray[0], &apOutputPlugArray[0], sizeof(GraphPlug*)*aOutputPlugCount);
+ for(i=0;i<aOutputPlugCount;i++)
+ {
+ cst->m_Output->mPlugArray[i]->m_Owner = cst->m_Output;
+ cst->m_Graph.m_OutEdges[i].m_Id = GetPlugType(*apOutputPlugArray[i]);
+ cst->m_Graph.m_OutEdges[i].m_Binding = apOutputPlugArray[i]->m_ID;
+ }
+
+ AssignPlugId(cst);
+ AssignPlugOffset(cst, arAlloc);
+
+ for(i=0;i<aConstantCount;i++)
+ {
+ // copy the largest data element
+ cst->m_Graph.m_ConstantEdges[i].m_FloatValue = cst->m_ConstantArray[i]->m_FloatValue;
+ cst->m_Graph.m_ConstantEdges[i].m_VectorValue = cst->m_ConstantArray[i]->m_VectorValue;
+ if(cst->m_ConstantArray[i]->m_Plug)
+ {
+ cst->m_Graph.m_ConstantEdges[i].m_Id = cst->m_ConstantArray[i]->m_Plug->m_PlugId;
+ }
+ }
+
+ return cst;
+ }
+
+ EvaluationGraph* CreateEvaluationGraph(Node** apNodeArray, uint32_t aNodeCount,
+ GraphPlug** apInputPlugArray, uint32_t aInputPlugCount,
+ GraphPlug** apOutputPlugArray, uint32_t aOutputPlugCount,
+ Constant** apConstantArray, uint32_t aConstantCount,
+ memory::Allocator& arAlloc)
+ {
+ uint32_t connectionCount = 0;
+ uint32_t i;
+ for(i=0;i<aNodeCount;i++)
+ {
+ Node* node = apNodeArray[i];
+ uint32_t j;
+ for(j=0;j<node->GetPlugCount();j++)
+ {
+ GraphPlug* plug = node->GetPlug(j).GetSource();
+ if(plug)
+ {
+ connectionCount++;
+ }
+ }
+ }
+
+ for(i=0;i<aOutputPlugCount;i++)
+ {
+ GraphPlug* plug = apOutputPlugArray[i]->GetSource();
+ if(plug)
+ {
+ connectionCount++;
+ }
+ }
+
+ EvaluationGraph* cst = CreateGraph(apNodeArray, aNodeCount, apInputPlugArray, aInputPlugCount, apOutputPlugArray, aOutputPlugCount, connectionCount, apConstantArray, aConstantCount, arAlloc);
+
+ ConnectEdges(cst);
+
+ return cst;
+ }
+
+ static void Connect(Edge const& arEdges, EvaluationGraph& arGraph)
+ {
+ GraphPlug *source = 0, *destination = 0;
+
+ uint32_t i, j;
+ for(i=0;i < arGraph.m_NodeCount && (source == 0 || destination == 0);i++)
+ {
+ Node* node = arGraph.m_NodeArray[i];
+ for(j=0;j<node->GetPlugCount() && (source == 0 || destination == 0);j++)
+ {
+ if(node->GetPlug(j).m_PlugId == arEdges.m_SourceId)
+ source = &node->GetPlug(j);
+ if(node->GetPlug(j).m_PlugId == arEdges.m_DestinationId)
+ destination = &node->GetPlug(j);
+ }
+ }
+
+ for(j=0;j<arGraph.m_Input->GetPlugCount() && (source == 0 || destination == 0);j++)
+ {
+ if(arGraph.m_Input->GetPlug(j).m_PlugId == arEdges.m_SourceId)
+ source = &arGraph.m_Input->GetPlug(j);
+ if(arGraph.m_Input->GetPlug(j).m_PlugId == arEdges.m_DestinationId)
+ destination = &arGraph.m_Input->GetPlug(j);
+ }
+
+
+ for(j=0;j<arGraph.m_Output->GetPlugCount() && (source == 0 || destination == 0);j++)
+ {
+ if(arGraph.m_Output->GetPlug(j).m_PlugId == arEdges.m_SourceId)
+ source = &arGraph.m_Output->GetPlug(j);
+ if(arGraph.m_Output->GetPlug(j).m_PlugId == arEdges.m_DestinationId)
+ destination = &arGraph.m_Output->GetPlug(j);
+ }
+
+ if(source && destination)
+ {
+ destination->m_Source = source;
+ }
+ }
+
+ static void Connect(ConstantEdge const& arConstantEdge, Constant& arConstant, EvaluationGraph& arGraph)
+ {
+ GraphPlug *plug = 0;
+
+ uint32_t i, j;
+ for(i=0;i < arGraph.m_NodeCount && plug == 0; i++)
+ {
+ Node* node = arGraph.m_NodeArray[i];
+ for(j=0;j<node->GetPlugCount() && plug == 0; j++)
+ {
+ if(node->GetPlug(j).m_PlugId == arConstantEdge.m_Id)
+ plug = &node->GetPlug(j);
+ }
+ }
+
+ for(j=0;j<arGraph.m_Input->GetPlugCount() && plug == 0; j++)
+ {
+ if(arGraph.m_Input->GetPlug(j).m_PlugId == arConstantEdge.m_Id)
+ plug = &arGraph.m_Input->GetPlug(j);
+ }
+
+
+ for(j=0;j<arGraph.m_Output->GetPlugCount() && plug == 0; j++)
+ {
+ if(arGraph.m_Output->GetPlug(j).m_PlugId == arConstantEdge.m_Id)
+ plug = &arGraph.m_Output->GetPlug(j);
+ }
+
+ if(plug)
+ {
+ arConstant.m_Plug = plug;
+ }
+ }
+
+ EvaluationGraph* CreateEvaluationGraph(Graph* apGraph, GraphFactory const& arFactory, memory::Allocator& arAlloc)
+ {
+ EvaluationGraph* cst = 0;
+ uint32_t i;
+ Node** nodes = arAlloc.ConstructArray<Node*>(apGraph->m_VerticesCount);
+ GraphPlug** inputPlug = arAlloc.ConstructArray<GraphPlug*>(apGraph->m_InEdgesCount);
+ GraphPlug** outputPlug = arAlloc.ConstructArray<GraphPlug*>(apGraph->m_OutEdgesCount);
+ Constant** constants = arAlloc.ConstructArray<Constant*>(apGraph->m_ConstantCount);
+
+ for(i=0;i<apGraph->m_VerticesCount;i++)
+ {
+ nodes[i] = arFactory.Create(static_cast<eNodeType>(apGraph->m_Vertices[i].m_Id), arAlloc);
+ nodes[i]->mID = apGraph->m_Vertices[i].m_Binding;
+ }
+
+ for(i=0;i<apGraph->m_InEdgesCount;i++)
+ {
+ inputPlug[i] = arFactory.Create(static_cast<ePlugType>(apGraph->m_InEdges[i].m_Id), arAlloc);
+ inputPlug[i]->m_ID = apGraph->m_InEdges[i].m_Binding;
+ inputPlug[i]->m_Input = false;
+ }
+
+ for(i=0;i<apGraph->m_OutEdgesCount;i++)
+ {
+ outputPlug[i] = arFactory.Create(static_cast<ePlugType>(apGraph->m_OutEdges[i].m_Id), arAlloc);
+ outputPlug[i]->m_ID = apGraph->m_OutEdges[i].m_Binding;
+ outputPlug[i]->m_Input = true;
+ }
+
+ for(i=0;i<apGraph->m_ConstantCount;i++)
+ {
+ constants[i] = arAlloc.Construct<Constant>();
+
+ constants[i]->m_FloatValue = apGraph->m_ConstantEdges[i].m_FloatValue;
+ constants[i]->m_VectorValue = apGraph->m_ConstantEdges[i].m_VectorValue;
+ }
+
+ cst = CreateGraph(nodes, apGraph->m_VerticesCount, inputPlug, apGraph->m_InEdgesCount, outputPlug, apGraph->m_OutEdgesCount, apGraph->m_EdgesCount, constants, apGraph->m_ConstantCount, arAlloc);
+
+ for(i=0;i<apGraph->m_EdgesCount;i++)
+ {
+ cst->m_Graph.m_Edges[i] = apGraph->m_Edges[i];
+ Connect(apGraph->m_Edges[i], *cst);
+ }
+
+ for(i=0;i<apGraph->m_ConstantCount;i++)
+ {
+ cst->m_Graph.m_ConstantEdges[i].m_Id = apGraph->m_ConstantEdges[i].m_Id;
+ Connect(apGraph->m_ConstantEdges[i], *cst->m_ConstantArray[i], *cst);
+ }
+
+ arAlloc.Deallocate(nodes);
+ arAlloc.Deallocate(inputPlug);
+ arAlloc.Deallocate(outputPlug);
+ arAlloc.Deallocate(constants);
+
+ return cst;
+ }
+
+ void DestroyEvaluationGraph(EvaluationGraph* apGraph, memory::Allocator& arAlloc)
+ {
+ if(apGraph)
+ {
+ uint32_t i;
+ for(i=0;i<apGraph->m_NodeCount;i++)
+ {
+ arAlloc.Deallocate(apGraph->m_NodeArray[i]);
+ }
+ arAlloc.Deallocate(apGraph->m_NodeArray);
+
+ for(i=0;i<apGraph->m_Graph.m_InEdgesCount;i++)
+ {
+ arAlloc.Deallocate(apGraph->m_Input->mPlugArray[i]);
+ }
+ arAlloc.Deallocate(apGraph->m_Input->mPlugArray);
+ arAlloc.Deallocate(apGraph->m_Input);
+
+ for(i=0;i<apGraph->m_Graph.m_OutEdgesCount;i++)
+ {
+ arAlloc.Deallocate(apGraph->m_Output->mPlugArray[i]);
+ }
+ arAlloc.Deallocate(apGraph->m_Output->mPlugArray);
+ arAlloc.Deallocate(apGraph->m_Output);
+
+ for(i=0;i<apGraph->m_ConstantCount;i++)
+ {
+ arAlloc.Deallocate(apGraph->m_ConstantArray[i]);
+ }
+ arAlloc.Deallocate(apGraph->m_ConstantArray);
+
+ arAlloc.Deallocate(apGraph->m_Graph.m_Vertices);
+ arAlloc.Deallocate(apGraph->m_Graph.m_Edges);
+ arAlloc.Deallocate(apGraph->m_Graph.m_InEdges);
+ arAlloc.Deallocate(apGraph->m_Graph.m_OutEdges);
+ arAlloc.Deallocate(apGraph->m_Graph.m_ConstantEdges);
+
+ arAlloc.Deallocate(apGraph);
+ }
+ }
+
+ EvaluationGraphWorkspace* CreateEvaluationGraphWorkspace(EvaluationGraph* apGraph, memory::Allocator& arAlloc)
+ {
+ std::size_t sizePlugDataBlock = 0;
+ uint32_t i, plugCount = 0;
+
+ for(i=0;i<apGraph->m_NodeCount;i++)
+ {
+ Node* node = apGraph->m_NodeArray[i];
+ uint32_t j;
+ for(j=0;j<node->GetPlugCount();j++)
+ {
+ plugCount++;
+ sizePlugDataBlock = arAlloc.AlignAddress(sizePlugDataBlock, node->GetPlug(j).ValueAlign());
+ sizePlugDataBlock += node->GetPlug(j).ValueSize();
+ }
+ }
+
+ for(i=0;i<apGraph->m_Input->GetPlugCount();i++)
+ {
+ plugCount++;
+ sizePlugDataBlock = arAlloc.AlignAddress(sizePlugDataBlock, apGraph->m_Input->GetPlug(i).ValueAlign());
+ sizePlugDataBlock += apGraph->m_Input->GetPlug(i).ValueSize();
+ }
+
+ for(i=0;i<apGraph->m_Output->GetPlugCount();i++)
+ {
+ plugCount++;
+ sizePlugDataBlock = arAlloc.AlignAddress(sizePlugDataBlock, apGraph->m_Output->GetPlug(i).ValueAlign());
+ sizePlugDataBlock += apGraph->m_Output->GetPlug(i).ValueSize();
+ }
+
+ EvaluationGraphWorkspace* ws = arAlloc.Construct<EvaluationGraphWorkspace>();
+
+ ws->m_EvaluationInfo.m_DataBlock.m_PlugCount = plugCount;
+ ws->m_EvaluationInfo.m_DataBlock.m_EvaluationId = arAlloc.ConstructArray<uint32_t>(plugCount);
+
+ ws->m_EvaluationInfo.m_DataBlock.m_Buffer = reinterpret_cast<char*>(arAlloc.Allocate( sizePlugDataBlock, ALIGN4F) );
+ ws->m_EvaluationInfo.m_EvaluationId = 0;
+
+ memset(ws->m_EvaluationInfo.m_DataBlock.m_EvaluationId, numeric_limits<uint32_t>::max_value, sizeof(mecanim::uint32_t)*plugCount);
+ memset(ws->m_EvaluationInfo.m_DataBlock.m_Buffer, 0, sizePlugDataBlock);
+
+ for(i=0;i<apGraph->m_ConstantCount;i++)
+ {
+ if(apGraph->m_ConstantArray[i]->m_Plug)
+ {
+ ePlugType plugType = GetPlugType(*apGraph->m_ConstantArray[i]->m_Plug);
+ switch(plugType)
+ {
+ case Float4Id:
+ ws->m_EvaluationInfo.m_DataBlock.SetData(apGraph->m_ConstantArray[i]->m_VectorValue, apGraph->m_ConstantArray[i]->m_Plug->m_PlugId, apGraph->m_ConstantArray[i]->m_Plug->m_Offset, 0);
+ break;
+ case Float1Id:
+ ws->m_EvaluationInfo.m_DataBlock.SetData(apGraph->m_ConstantArray[i]->m_VectorValue, apGraph->m_ConstantArray[i]->m_Plug->m_PlugId, apGraph->m_ConstantArray[i]->m_Plug->m_Offset, 0);
+ break;
+ case FloatId:
+ ws->m_EvaluationInfo.m_DataBlock.SetData(apGraph->m_ConstantArray[i]->m_FloatValue, apGraph->m_ConstantArray[i]->m_Plug->m_PlugId, apGraph->m_ConstantArray[i]->m_Plug->m_Offset, 0);
+ break;
+ case UInt32Id:
+ ws->m_EvaluationInfo.m_DataBlock.SetData(apGraph->m_ConstantArray[i]->m_UIntValue, apGraph->m_ConstantArray[i]->m_Plug->m_PlugId, apGraph->m_ConstantArray[i]->m_Plug->m_Offset, 0);
+ break;
+ case Int32Id:
+ ws->m_EvaluationInfo.m_DataBlock.SetData(apGraph->m_ConstantArray[i]->m_IntValue, apGraph->m_ConstantArray[i]->m_Plug->m_PlugId, apGraph->m_ConstantArray[i]->m_Plug->m_Offset, 0);
+ break;
+ case BoolId:
+ ws->m_EvaluationInfo.m_DataBlock.SetData(apGraph->m_ConstantArray[i]->m_BoolValue, apGraph->m_ConstantArray[i]->m_Plug->m_PlugId, apGraph->m_ConstantArray[i]->m_Plug->m_Offset, 0);
+ break;
+ }
+ }
+ }
+
+ return ws;
+ }
+
+ void DestroyEvaluationGraphWorkspace(EvaluationGraphWorkspace* apGraphWorkspace, memory::Allocator& arAlloc)
+ {
+ if(apGraphWorkspace)
+ {
+ arAlloc.Deallocate(apGraphWorkspace->m_EvaluationInfo.m_DataBlock.m_Buffer);
+ arAlloc.Deallocate(apGraphWorkspace->m_EvaluationInfo.m_DataBlock.m_EvaluationId);
+ arAlloc.Deallocate(apGraphWorkspace);
+ }
+ }
+}
+
+} \ No newline at end of file
diff --git a/Runtime/mecanim/graph/graph.h b/Runtime/mecanim/graph/graph.h
new file mode 100644
index 0000000..2c12b4f
--- /dev/null
+++ b/Runtime/mecanim/graph/graph.h
@@ -0,0 +1,188 @@
+/*
+ Copyright (c) 7244339 Canada Inc. (Mecanim)
+ All Rights Reserved.
+*/
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/object.h"
+#include "Runtime/mecanim/bitset.h"
+
+#include "Runtime/mecanim/graph/plug.h"
+#include "Runtime/mecanim/graph/node.h"
+#include "Runtime/mecanim/graph/factory.h"
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Animation/MecanimArraySerialization.h"
+
+namespace mecanim
+{
+
+namespace graph
+{
+ // If somebody change a node interface, like adding a new plug, he should create a new class instead for his new node
+ // because some user's file may still use the old node and won't have the data to handle new plug, unexpected behavior
+ // can result if this rule is not respected
+
+ struct Vertex : public Object
+ {
+ DEFINE_GET_TYPESTRING(Vertex)
+
+ DeclareObject(Vertex)
+
+ Vertex():m_Id(numeric_limits<uint32_t>::max_value){}
+ uint32_t m_Id;
+ uint32_t m_Binding;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_Id);
+ TRANSFER(m_Binding);
+ }
+ };
+
+ struct Edge : public Object
+ {
+ DEFINE_GET_TYPESTRING(Edge)
+
+ DeclareObject(Edge)
+
+ Edge():m_SourceId(numeric_limits<uint32_t>::max_value),m_DestinationId(numeric_limits<uint32_t>::max_value){}
+ uint32_t m_SourceId;
+ uint32_t m_DestinationId;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_SourceId);
+ TRANSFER(m_DestinationId);
+ }
+ };
+
+ struct ConstantEdge : public Object
+ {
+ DEFINE_GET_TYPESTRING(ConstantEdge)
+
+ DeclareObject(ConstantEdge)
+
+ ConstantEdge():m_Id(numeric_limits<uint32_t>::max_value){}
+ union {
+ float m_FloatValue;
+ uint32_t m_UIntValue;
+ int32_t m_IntValue;
+ bool m_BoolValue;
+ };
+ math::float4 m_VectorValue;
+
+ uint32_t m_Id;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_Id);
+ TRANSFER(m_VectorValue);
+ TRANSFER(m_UIntValue);
+ }
+ };
+
+ struct ExternalEdge : public Object
+ {
+ DEFINE_GET_TYPESTRING(ExternalEdge)
+
+ DeclareObject(ExternalEdge)
+
+ ExternalEdge():m_Id(numeric_limits<uint32_t>::max_value), m_Binding(numeric_limits<uint32_t>::max_value){}
+ uint32_t m_Id;
+ uint32_t m_Binding;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_Id);
+ TRANSFER(m_Binding);
+ }
+ };
+
+ struct Graph : public Object
+ {
+ DEFINE_GET_TYPESTRING(Graph)
+
+ DeclareObject(Graph)
+
+ Graph()
+ :m_VerticesCount(0),m_Vertices(0),
+ m_EdgesCount(0),m_Edges(0),
+ m_InEdgesCount(0),m_InEdges(0),
+ m_OutEdgesCount(0),m_OutEdges(0),
+ m_ConstantCount(0),m_ConstantEdges(0)
+ {}
+
+ uint32_t m_VerticesCount;
+ Vertex* m_Vertices;
+
+ uint32_t m_EdgesCount;
+ Edge* m_Edges;
+
+ uint32_t m_InEdgesCount;
+ ExternalEdge* m_InEdges;
+
+ uint32_t m_OutEdgesCount;
+ ExternalEdge* m_OutEdges;
+
+ uint32_t m_ConstantCount;
+ ConstantEdge* m_ConstantEdges;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ MANUAL_ARRAY_TRANSFER(mecanim::graph::Vertex, m_Vertices, m_VerticesCount);
+ MANUAL_ARRAY_TRANSFER(mecanim::graph::Edge, m_Edges, m_EdgesCount);
+ MANUAL_ARRAY_TRANSFER(mecanim::graph::ExternalEdge, m_InEdges, m_InEdgesCount);
+ MANUAL_ARRAY_TRANSFER(mecanim::graph::ExternalEdge, m_OutEdges, m_OutEdgesCount);
+ MANUAL_ARRAY_TRANSFER(mecanim::graph::ConstantEdge, m_ConstantEdges, m_ConstantCount);
+ }
+ };
+
+ struct EvaluationGraph
+ {
+ public:
+ EvaluationGraph():m_NodeCount(0),m_NodeArray(0),m_Input(0),m_Output(0),m_ConstantCount(0),m_ConstantArray(0){}
+
+ Graph m_Graph;
+
+ uint32_t m_NodeCount;
+ Node** m_NodeArray;
+
+ GraphInput* m_Input;
+ GraphOutput* m_Output;
+
+ uint32_t m_ConstantCount;
+ Constant** m_ConstantArray;
+ };
+
+ struct EvaluationGraphWorkspace
+ {
+ public:
+ EvaluationGraphWorkspace(){}
+
+ EvaluationInfo m_EvaluationInfo;
+ };
+
+ EvaluationGraph* CreateEvaluationGraph(Node** apNodeArray, uint32_t aNodeCount,
+ GraphPlug** apInputPlugArray, uint32_t aInputPlugCount,
+ GraphPlug** apOutputPlugArray, uint32_t aOutputPlugCount,
+ Constant** apConstantArray, uint32_t aConstantCount,
+ memory::Allocator& arAlloc);
+
+ EvaluationGraph* CreateEvaluationGraph(Graph* apGraph, GraphFactory const& arFactory, memory::Allocator& arAlloc);
+
+ void DestroyEvaluationGraph(EvaluationGraph* apGraph, memory::Allocator& arAlloc);
+
+ EvaluationGraphWorkspace* CreateEvaluationGraphWorkspace(EvaluationGraph* apGraph, memory::Allocator& arAlloc);
+ void DestroyEvaluationGraphWorkspace(EvaluationGraphWorkspace* apGraphWorkspace, memory::Allocator& arAlloc);
+}
+
+}
diff --git a/Runtime/mecanim/graph/node.cpp b/Runtime/mecanim/graph/node.cpp
new file mode 100644
index 0000000..8e1ead8
--- /dev/null
+++ b/Runtime/mecanim/graph/node.cpp
@@ -0,0 +1,11 @@
+#include "UnityPrefix.h"
+#include "Runtime/mecanim/graph/node.h"
+
+namespace mecanim
+{
+
+namespace graph
+{
+}
+
+}
diff --git a/Runtime/mecanim/graph/node.h b/Runtime/mecanim/graph/node.h
new file mode 100644
index 0000000..7dcea89
--- /dev/null
+++ b/Runtime/mecanim/graph/node.h
@@ -0,0 +1,346 @@
+/*
+ Copyright (c) 7244339 Canada Inc. (Mecanim)
+ All Rights Reserved.
+*/
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/object.h"
+#include "Runtime/mecanim/generic/stringtable.h"
+
+#include "Runtime/mecanim/graph/plug.h"
+
+namespace mecanim
+{
+
+namespace graph
+{
+ // If you add a new node id, add it at the end of this list, otherwise you will break
+ // saved graph asset.
+ enum eNodeType {
+ InvalidNodeId,
+ NegationFloatId,
+ NegationIntId,
+ NegationFloat4Id,
+ AdditionFloatId,
+ AdditionUIntId,
+ AdditionIntId,
+ AdditionFloat4Id,
+ SubstractionFloatId,
+ SubstractionUIntId,
+ SubstractionIntId,
+ SubstractionFloat4Id,
+ MultiplicationFloatId,
+ MultiplicationUIntId,
+ MultiplicationIntId,
+ MultiplicationFloat4Id,
+ DivisionFloatId,
+ DivisionUIntId,
+ DivisionIntId,
+ DivisionFloat4Id,
+ CondFloatId,
+ CondUIntId,
+ CondIntId,
+ CondFloat4Id,
+ AbsFloatId,
+ AbsFloat4Id,
+ CrossFloat4Id,
+ DegreesFloatId,
+ DegreesFloat4Id,
+ DotFloat4Id,
+ LengthFloat4Id,
+ MaximumFloatId,
+ MaximumUIntId,
+ MaximumIntId,
+ MaximumFloat4Id,
+ MinimumFloatId,
+ MinimumUIntId,
+ MinimumIntId,
+ MinimumFloat4Id,
+ NormalizeFloat4Id,
+ RadiansFloatId,
+ RadiansFloat4Id,
+ FloatToFloat4Id,
+ Float4ToFloatId,
+ GreaterThanId,
+ LesserThanId,
+ SmoothstepFloatId,
+ Mux5FloatId,
+ Mul5FloatId,
+ FmodId,
+ SinId,
+ AndId,
+ OrId,
+ NegationBoolId,
+ GreaterThanOrEqualId,
+ LesserThanOrEqualId,
+ xformMulInvId,
+ xformIdentityId,
+ xformMulVecId,
+ xformInvMulVecId,
+ xformMulId,
+ xformInvMulId,
+ xformEqualId,
+ xformWeightId,
+ xformAddId,
+ xformSubId,
+ quatIdentityId,
+ quatConjId,
+ quatMulId,
+ quatMulVecId,
+ quatLerpId,
+ quatArcRotateId,
+ quatArcRotateXId,
+ quatXcosId,
+ quatYcosId,
+ quatZcosId,
+ quatEulerToQuatId,
+ quatQuatToEulerId,
+ quatProjOnYPlaneId,
+ quat2QtanId,
+ qtan2QuatId,
+ ZYRoll2QuatId,
+ quat2ZYRollId,
+ RollZY2QuatId,
+ quat2RollZYId,
+ quatWeightId,
+ xformComposeId,
+ xformDecomposeId,
+ CondXformId,
+ xformBlendId,
+ FloatToFloat1Id, // Not used anymore
+ Float1ToFloatId, // Not used anymore
+ RandId,
+ DampId,
+ xformRefChangeId,
+ XorId,
+ SmoothPulseFloatId,
+ InputId,
+ OutputId,
+ LastNodeId
+ };
+
+ class Node : public Object
+ {
+ public:
+ uint32_t mID;
+
+ Node(){}
+ virtual ~Node(){}
+
+ virtual eNodeType NodeType()=0;
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo)=0;
+
+ virtual uint32_t GetPlugCount()const=0;
+ virtual GraphPlug& GetPlug(uint32_t aIndex)=0;
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const=0;
+ };
+
+ template<typename TYPE> class TypePlug : public GraphPlug
+ {
+ public:
+ typedef TYPE value_type;
+ typedef TYPE& reference;
+ typedef TYPE const& const_reference;
+ typedef TYPE* pointer;
+ typedef TYPE const* const_pointer;
+
+ TypePlug(){}
+ TypePlug(bool input, uint32_t id, Node* apOwner=0):GraphPlug(input, id, apOwner){}
+
+ virtual std::size_t ValueSize()const
+ {
+ return sizeof(value_type);
+ }
+
+ virtual std::size_t ValueAlign()const
+ {
+ return ALIGN_OF(value_type);
+ }
+
+ virtual ePlugType PlugType()const
+ {
+ return Trait<value_type>::PlugType();
+ }
+
+ void ConnectTo(TypePlug& arPlug)
+ {
+ m_Source = &arPlug;
+ }
+
+ bool ReadData(void* apData, EvaluationInfo& arEvaluationInfo)const
+ {
+ uint32_t offset = m_Offset;
+ if(m_Source)
+ {
+ offset = m_Source->m_Offset;
+ if(arEvaluationInfo.m_DataBlock.IsDirty(m_Source->m_PlugId, arEvaluationInfo.m_EvaluationId))
+ {
+ m_Source->m_Owner->Evaluate(arEvaluationInfo);
+ }
+ }
+
+ *reinterpret_cast<pointer>(apData) = arEvaluationInfo.m_DataBlock.GetData<value_type>(offset);
+
+ return true;
+ }
+
+ bool WriteData(void const* apData, EvaluationInfo& arEvaluationInfo)const
+ {
+ arEvaluationInfo.m_DataBlock.SetData(*reinterpret_cast<const_pointer>(apData), m_PlugId, m_Offset, arEvaluationInfo.m_EvaluationId);
+ return true;
+ }
+ };
+
+
+ class GraphOutput : public Node
+ {
+ public:
+ uint32_t mPlugCount;
+ GraphPlug** mPlugArray;
+
+ GraphOutput():mPlugCount(0), mPlugArray(0){}
+ virtual ~GraphOutput(){}
+
+ virtual eNodeType NodeType(){return OutputId;}
+
+ virtual uint32_t GetPlugCount()const{return mPlugCount;}
+ virtual GraphPlug& GetPlug(uint32_t aIndex){return *(mPlugArray[aIndex]);}
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const{return *(mPlugArray[aIndex]);}
+
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo){};
+ };
+
+ class GraphInput : public Node
+ {
+ public:
+ uint32_t mPlugCount;
+ GraphPlug** mPlugArray;
+
+ GraphInput():mPlugCount(0), mPlugArray(0){}
+ virtual ~GraphInput(){}
+
+ virtual eNodeType NodeType(){return InputId;}
+
+ virtual uint32_t GetPlugCount()const{return mPlugCount;}
+ virtual GraphPlug& GetPlug(uint32_t aIndex){return *(mPlugArray[aIndex]);}
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const{return *(mPlugArray[aIndex]);}
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo){};
+ };
+
+ template <typename RESULT, typename ResultPolicies> class ResultNode : public Node
+ {
+ public:
+ TypePlug<RESULT> mResult;
+
+ ResultNode()
+ :mResult(false,CRCKey(eResult))
+ {
+ mResult.m_Owner = this;
+ }
+ virtual ~ResultNode(){}
+
+ virtual uint32_t GetPlugCount()const
+ {
+ return 1;
+ }
+ virtual GraphPlug& GetPlug(uint32_t aIndex)
+ {
+ switch(aIndex)
+ {
+ case 0:
+ default: return mResult;
+ }
+ }
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const
+ {
+ switch(aIndex)
+ {
+ case 0:
+ default: return mResult;
+ }
+ }
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo)
+ {
+ RESULT result = ResultPolicies::template Operation<RESULT>();
+ mResult.WriteData(&result, arEvaluationInfo);
+ }
+ };
+
+ template <typename TYPE1, typename TYPE2, typename TYPE3, typename RESULT, typename TernaryPolicies> class TernaryNode : public Node
+ {
+ public:
+ TypePlug<TYPE1> mA;
+ TypePlug<TYPE2> mB;
+ TypePlug<TYPE3> mC;
+
+ TypePlug<RESULT> mResult;
+
+ TernaryNode()
+ :mA(true, CRCKey(eA)),
+ mB(true,CRCKey(eB)),
+ mC(true,CRCKey(eC)),
+ mResult(false,CRCKey(eResult))
+ {
+ mA.m_Owner = this;
+ mB.m_Owner = this;
+ mC.m_Owner = this;
+ mResult.m_Owner = this;
+ }
+
+ virtual ~TernaryNode(){}
+
+ virtual uint32_t GetPlugCount()const
+ {
+ return 4;
+ }
+ virtual GraphPlug& GetPlug(uint32_t aIndex)
+ {
+ switch(aIndex)
+ {
+ case 0: return mA;
+ case 1: return mB;
+ case 2: return mC;
+ case 3:
+ default: return mResult;
+ }
+ }
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const
+ {
+ switch(aIndex)
+ {
+ case 0: return mA;
+ case 1: return mB;
+ case 2: return mC;
+ case 3:
+ default: return mResult;
+ }
+ }
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo)
+ {
+ TYPE1 a;
+ TYPE2 b;
+ TYPE3 c;
+ RESULT result;
+
+ mA.ReadData(&a, arEvaluationInfo);
+ mB.ReadData(&b, arEvaluationInfo);
+ mC.ReadData(&c, arEvaluationInfo);
+
+ result = TernaryPolicies::template Operation<TYPE1, TYPE2, TYPE3, RESULT>(a, b, c);
+
+ mResult.WriteData(&result, arEvaluationInfo);
+ }
+ };
+
+
+
+}
+
+}
diff --git a/Runtime/mecanim/graph/plug.h b/Runtime/mecanim/graph/plug.h
new file mode 100644
index 0000000..ce90020
--- /dev/null
+++ b/Runtime/mecanim/graph/plug.h
@@ -0,0 +1,123 @@
+/*
+ Copyright (c) 7244339 Canada Inc. (Mecanim)
+ All Rights Reserved.
+*/
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/object.h"
+
+#include "Runtime/Math/Simd/float4.h"
+#include "Runtime/Math/Simd/xform.h"
+
+namespace mecanim
+{
+
+namespace graph
+{
+ class Node;
+
+ enum ePlugType
+ {
+ InvalidPlugId,
+ Float4Id,
+ FloatId,
+ UInt32Id, // Not used anymore
+ Int32Id,
+ BoolId,
+ Float1Id, // Not used anymore
+ Bool4Id, // Not used anymore
+ XformId,
+ LastPlugId
+ };
+
+ template<typename T> struct Trait;
+
+ template<> struct Trait<math::float4> { static ePlugType PlugType(){return Float4Id;} };
+ template<> struct Trait<float> { static ePlugType PlugType(){return FloatId;} };
+ template<> struct Trait<math::float1> { static ePlugType PlugType(){return FloatId;} };
+ template<> struct Trait<uint32_t> { static ePlugType PlugType(){return UInt32Id;} };
+ template<> struct Trait<int32_t> { static ePlugType PlugType(){return Int32Id;} };
+ template<> struct Trait<bool> { static ePlugType PlugType(){return BoolId;} };
+ template<> struct Trait<math::bool4> { static ePlugType PlugType(){return Bool4Id;} };
+ template<> struct Trait<math::xform> { static ePlugType PlugType(){return XformId;} };
+
+ class DataBlock
+ {
+ public:
+ uint32_t m_PlugCount;
+ uint32_t* m_EvaluationId;
+ char* m_Buffer;
+
+ bool IsDirty(uint32_t aPlugId, uint32_t aEvaluationId)const
+ {
+ return m_EvaluationId[aPlugId] != aEvaluationId;
+ }
+ template<typename TYPE> TYPE GetData(uint32_t offset)const
+ {
+ return *reinterpret_cast<TYPE*>(&m_Buffer[offset]);
+ }
+ template<typename TYPE> void SetData(TYPE const& arValue, uint32_t aPlugId, uint32_t offset, uint32_t aEvaluationId)
+ {
+ *reinterpret_cast<TYPE*>(&m_Buffer[offset]) = arValue;
+ m_EvaluationId[aPlugId] = aEvaluationId;
+ }
+ };
+
+ class EvaluationInfo
+ {
+ public:
+ DataBlock m_DataBlock;
+ uint32_t m_EvaluationId;
+ };
+
+ class GraphPlug
+ {
+ public:
+ GraphPlug():m_PlugId(numeric_limits<uint32_t>::max_value),m_Input(false),m_Owner(0),m_Source(0),m_Offset(0){}
+ GraphPlug(bool input, uint32_t id, Node* owner=0):m_PlugId(numeric_limits<uint32_t>::max_value),m_Input(input),m_Owner(owner),m_Source(0),m_ID(id){}
+
+ virtual ~GraphPlug(){}
+
+ virtual std::size_t ValueSize()const=0;
+ virtual std::size_t ValueAlign()const=0;
+ virtual ePlugType PlugType()const=0;
+
+ GraphPlug* GetSource(){return m_Source;}
+
+ virtual bool ReadData(void* apData, EvaluationInfo& arEvaluationInfo)const=0;
+ virtual bool WriteData(void const* apData, EvaluationInfo& arEvaluationInfo)const=0;
+
+ uint32_t m_PlugId;
+ bool m_Input;
+ Node* m_Owner;
+ GraphPlug* m_Source;
+ uint32_t m_ID;
+ uint32_t m_Offset;
+ };
+
+ struct Constant
+ {
+ Constant():m_Plug(0),m_VectorValue(0),m_FloatValue(0){}
+
+ union {
+ float m_FloatValue;
+ uint32_t m_UIntValue;
+ int32_t m_IntValue;
+ bool m_BoolValue;
+ };
+ // Cannot put float4 in union because of operator=
+ math::float4 m_VectorValue;
+ graph::GraphPlug* m_Plug;
+ };
+
+
+ STATIC_INLINE ePlugType GetPlugType(GraphPlug const& arPlug)
+ {
+ return arPlug.PlugType();
+ }
+}
+
+}
diff --git a/Runtime/mecanim/graph/plugbinder.cpp b/Runtime/mecanim/graph/plugbinder.cpp
new file mode 100644
index 0000000..03ffb30
--- /dev/null
+++ b/Runtime/mecanim/graph/plugbinder.cpp
@@ -0,0 +1,180 @@
+#include "UnityPrefix.h"
+#include "Runtime/mecanim/graph/plugbinder.h"
+
+namespace mecanim
+{
+ void InitializeSetPlugBinder3(uint32_t& arCount, SetPlugBinder3 *& arSetPlugBinder3, ValuesList const& arValuesList, graph::EvaluationGraph const* apGraph, memory::Allocator& arAlloc)
+ {
+ arCount = 0;
+ arSetPlugBinder3 = 0;
+
+ uint32_t i;
+ for(i = 0 ; i < apGraph->m_Input->mPlugCount; i++)
+ {
+ graph::GraphPlug& plug = *apGraph->m_Input->mPlugArray[i];
+ uint32_t j;
+ for(j=0;j<arValuesList.m_InValuesCount;++j)
+ {
+ int32_t index = FindValueIndex(arValuesList.m_InValuesConstant[j], plug.m_ID);
+ if(index > -1)
+ {
+ arCount++;
+ }
+ }
+ }
+
+ if(arCount)
+ {
+ arSetPlugBinder3 = arAlloc.ConstructArray<SetPlugBinder3>(arCount);
+ uint32_t binderCount;
+ for(i = 0, binderCount = 0; i < apGraph->m_Input->mPlugCount; i++)
+ {
+ graph::GraphPlug const* plug = apGraph->m_Input->mPlugArray[i];
+ uint32_t j;
+ for(j=0;j<arValuesList.m_InValuesCount;++j)
+ {
+ int32_t index = FindValueIndex(arValuesList.m_InValuesConstant[j], plug->m_ID);
+ if(index > -1)
+ {
+ uint32_t valueIndex = static_cast<uint32_t>(index);
+ arSetPlugBinder3[binderCount++] = bind( SetPlugValueList, plug, j, valueIndex);
+ }
+ }
+ }
+ }
+ }
+
+ void InitializeGetPlugBinder3(uint32_t& arCount,
+ GetPlugBinder3 *& arGetPlugBinder3,
+ ValuesList const& arValuesList,
+ graph::EvaluationGraph const* apGraph,
+ memory::Allocator& arAlloc)
+ {
+ arCount = 0;
+ arGetPlugBinder3 = 0;
+
+ uint32_t i;
+ for(i = 0 ; i < apGraph->m_Output->mPlugCount; i++)
+ {
+ graph::GraphPlug& plug = *apGraph->m_Output->mPlugArray[i];
+ uint32_t j;
+ for(j=0;j<arValuesList.m_OutValuesCount;++j)
+ {
+ int32_t index = FindValueIndex(arValuesList.m_OutValuesConstant[j], plug.m_ID);
+ if(index > -1)
+ {
+ arCount++;
+ }
+ }
+ }
+
+ if(arCount)
+ {
+ arGetPlugBinder3 = arAlloc.ConstructArray<GetPlugBinder3>(arCount);
+ uint32_t binderCount;
+ for(i = 0, binderCount = 0; i < apGraph->m_Output->mPlugCount; i++)
+ {
+ graph::GraphPlug const* plug = apGraph->m_Output->mPlugArray[i];
+ uint32_t j;
+ for(j=0;j<arValuesList.m_OutValuesCount;++j)
+ {
+ int32_t index = FindValueIndex(arValuesList.m_OutValuesConstant[j], plug->m_ID);
+ if(index > -1)
+ {
+ uint32_t valueIndex = static_cast<uint32_t>(index);
+ arGetPlugBinder3[binderCount++] = bind( GetPlugValueList, plug, j, valueIndex);
+ }
+ }
+ }
+ }
+ }
+
+
+
+ void InitializeSetPlugBinder2(uint32_t& arCount, SetPlugBinder2 *& arSetPlugBinder2, ValuesList const& arValuesList, graph::EvaluationGraph const* apGraph, memory::Allocator& arAlloc)
+ {
+ arCount = 0;
+ arSetPlugBinder2 = 0;
+
+ uint32_t i;
+ for(i = 0 ; i < apGraph->m_Input->mPlugCount; i++)
+ {
+ graph::GraphPlug& plug = *apGraph->m_Input->mPlugArray[i];
+ uint32_t j;
+ for(j=0;j<arValuesList.m_InValuesCount;++j)
+ {
+ int32_t index = FindValueIndex(arValuesList.m_InValuesConstant[j], plug.m_ID);
+ if(index > -1)
+ {
+ arCount++;
+ }
+ }
+ }
+
+ if(arCount)
+ {
+ arSetPlugBinder2 = arAlloc.ConstructArray<SetPlugBinder2>(arCount);
+ uint32_t binderCount;
+ for(i = 0, binderCount = 0; i < apGraph->m_Input->mPlugCount; i++)
+ {
+ graph::GraphPlug const* plug = apGraph->m_Input->mPlugArray[i];
+ uint32_t j;
+ for(j=0;j<arValuesList.m_InValuesCount;++j)
+ {
+ int32_t index = FindValueIndex(arValuesList.m_InValuesConstant[j], plug->m_ID);
+ if(index > -1)
+ {
+ uint32_t valueIndex = static_cast<uint32_t>(index);
+ arSetPlugBinder2[binderCount++] = bind( SetPlugValue, plug, valueIndex);
+ }
+ }
+ }
+ }
+ }
+
+ void InitializeGetPlugBinder5(uint32_t& arCount,
+ GetPlugBinder2 *& arGetPlugBinder2,
+ ValuesList const& arValuesList,
+ graph::EvaluationGraph const* apGraph,
+ memory::Allocator& arAlloc)
+ {
+ arCount = 0;
+ arGetPlugBinder2 = 0;
+
+ uint32_t i;
+ for(i = 0 ; i < apGraph->m_Output->mPlugCount; i++)
+ {
+ graph::GraphPlug& plug = *apGraph->m_Output->mPlugArray[i];
+ uint32_t j;
+ for(j=0;j<arValuesList.m_OutValuesCount;++j)
+ {
+ int32_t index = FindValueIndex(arValuesList.m_OutValuesConstant[j], plug.m_ID);
+ if(index > -1)
+ {
+ arCount++;
+ }
+ }
+ }
+
+ if(arCount)
+ {
+ arGetPlugBinder2 = arAlloc.ConstructArray<GetPlugBinder2>(arCount);
+ uint32_t binderCount;
+ for(i = 0, binderCount = 0; i < apGraph->m_Output->mPlugCount; i++)
+ {
+ graph::GraphPlug const* plug = apGraph->m_Output->mPlugArray[i];
+ uint32_t j;
+ for(j=0;j<arValuesList.m_OutValuesCount;++j)
+ {
+ int32_t index = FindValueIndex(arValuesList.m_OutValuesConstant[j], plug->m_ID);
+ if(index > -1)
+ {
+ uint32_t valueIndex = static_cast<uint32_t>(index);
+ arGetPlugBinder2[binderCount++] = bind( GetPlugValue, plug, valueIndex);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/Runtime/mecanim/graph/plugbinder.h b/Runtime/mecanim/graph/plugbinder.h
new file mode 100644
index 0000000..f224440
--- /dev/null
+++ b/Runtime/mecanim/graph/plugbinder.h
@@ -0,0 +1,85 @@
+/*
+ Copyright (c) 7244339 Canada Inc. (Mecanim)
+ All Rights Reserved.
+*/
+#pragma once
+
+#include "Runtime/mecanim/bind.h"
+#include "Runtime/mecanim/graph/graph.h"
+#include "Runtime/mecanim/generic/valuearray.h"
+
+namespace mecanim
+{
+ typedef binder2<function<void (graph::GraphPlug const*, uint32_t, ValueArray const*, graph::EvaluationGraphWorkspace*)>::ptr,
+ graph::GraphPlug const*,
+ uint32_t>
+ SetPlugBinder2;
+
+ typedef binder2<function<void (graph::GraphPlug const*, uint32_t, ValueArray*, graph::EvaluationGraphWorkspace*)>::ptr,
+ graph::GraphPlug const*,
+ uint32_t >
+ GetPlugBinder2;
+
+ typedef binder3<function<void (graph::GraphPlug const*, uint32_t, uint32_t, ValuesList const&, graph::EvaluationGraphWorkspace*)>::ptr,
+ graph::GraphPlug const*,
+ uint32_t,
+ uint32_t>
+ SetPlugBinder3;
+
+
+ typedef binder3<function<void (graph::GraphPlug const*, uint32_t, uint32_t, ValuesList const&, graph::EvaluationGraphWorkspace*)>::ptr,
+ graph::GraphPlug const*,
+ uint32_t,
+ uint32_t>
+ GetPlugBinder3;
+
+ STATIC_INLINE void SetPlugValue(mecanim::graph::GraphPlug const* plug, mecanim::uint32_t valueIndex, mecanim::ValueArray const* valueArray, mecanim::graph::EvaluationGraphWorkspace* graphWS)
+ {
+ char ATTRIBUTE_ALIGN(ALIGN4F) value[48];
+ valueArray->m_ValueArray[valueIndex]->ReadData(value);
+ plug->WriteData( value, graphWS->m_EvaluationInfo);
+ }
+ STATIC_INLINE void GetPlugValue(mecanim::graph::GraphPlug const* plug, mecanim::uint32_t valueIndex, mecanim::ValueArray* valueArray, mecanim::graph::EvaluationGraphWorkspace* graphWS)
+ {
+ char ATTRIBUTE_ALIGN(ALIGN4F) value[48];
+ plug->ReadData( value, graphWS->m_EvaluationInfo);
+ valueArray->m_ValueArray[valueIndex]->WriteData(value);
+ }
+
+ STATIC_INLINE void SetPlugValueList(mecanim::graph::GraphPlug const* plug, mecanim::uint32_t listIndex, mecanim::uint32_t valueIndex, ValuesList const& valuesList, mecanim::graph::EvaluationGraphWorkspace* graphWS)
+ {
+ char ATTRIBUTE_ALIGN(ALIGN4F) value[48];
+ valuesList.m_InValues[listIndex]->m_ValueArray[valueIndex]->ReadData(value);
+ plug->WriteData( value, graphWS->m_EvaluationInfo);
+ }
+ STATIC_INLINE void GetPlugValueList(mecanim::graph::GraphPlug const* plug, mecanim::uint32_t listIndex, mecanim::uint32_t valueIndex, ValuesList const& valuesList, mecanim::graph::EvaluationGraphWorkspace* graphWS)
+ {
+ char ATTRIBUTE_ALIGN(ALIGN4F) value[48];
+ plug->ReadData( value, graphWS->m_EvaluationInfo);
+ valuesList.m_OutValues[listIndex]->m_ValueArray[valueIndex]->WriteData(value);
+ }
+
+ void InitializeSetPlugBinder3(uint32_t& arCount,
+ SetPlugBinder3 *& arSetPlugBinder3,
+ ValuesList const& arValuesList,
+ graph::EvaluationGraph const* apGraph,
+ memory::Allocator& arAlloc);
+
+ void InitializeGetPlugBinder3(uint32_t& arCount,
+ GetPlugBinder3 *& arGetPlugBinder3,
+ ValuesList const& arValuesList,
+ graph::EvaluationGraph const* apGraph,
+ memory::Allocator& arAlloc);
+
+ void InitializeSetPlugBinder2(uint32_t& arCount,
+ SetPlugBinder2 *& arSetPlugBinder2,
+ ValuesList const& arValuesList,
+ graph::EvaluationGraph const* apGraph,
+ memory::Allocator& arAlloc);
+
+ void InitializeGetPlugBinder2(uint32_t& arCount,
+ GetPlugBinder2 *& arGetPlugBinder2,
+ ValuesList const& arValuesList,
+ graph::EvaluationGraph const* apGraph,
+ memory::Allocator& arAlloc);
+} \ No newline at end of file
diff --git a/Runtime/mecanim/graph/quaternionnode.h b/Runtime/mecanim/graph/quaternionnode.h
new file mode 100644
index 0000000..19f1f86
--- /dev/null
+++ b/Runtime/mecanim/graph/quaternionnode.h
@@ -0,0 +1,283 @@
+/*
+ Copyright (c) 7244339 Canada Inc. (Mecanim)
+ All Rights Reserved.
+*/
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/object.h"
+
+#include "Runtime/Math/Simd/math.h"
+
+#include "Runtime/mecanim/graph/plug.h"
+#include "Runtime/mecanim/graph/node.h"
+
+#include "Runtime/mecanim/graph/unarynode.h"
+
+namespace mecanim
+{
+
+namespace graph
+{
+ class quatIdentityOp
+ {
+ public:
+ template<typename RESULT> static math::float4 Operation(){ return math::quatIdentity(); }
+ };
+
+ class quatIdentity : public ResultNode<math::float4, quatIdentityOp>
+ {
+ public:
+ static const eNodeType mId = quatIdentityId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ template<typename TYPE, typename RESULT> class quatConjOp
+ {
+ public:
+ static math::float4 Operation( math::float4 const& l){ return math::quatConj(l); }
+ };
+
+ class quatConj : public UnaryNode<math::float4, math::float4, quatConjOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = quatConjId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class quatMulOp
+ {
+ public:
+ template<typename TYPE1, typename TYPE2, typename RESULT> static math::float4 Operation( math::float4 const& l, math::float4 const& r){ return math::quatMul(l, r); }
+ };
+
+ class quatMul : public BinaryNode<math::float4, math::float4, math::float4, quatMulOp>
+ {
+ public:
+ static const eNodeType mId = quatMulId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class quatMulVecOp
+ {
+ public:
+ template<typename TYPE1, typename TYPE2, typename RESULT> static math::float4 Operation( math::float4 const& l, math::float4 const& r){ return math::quatMulVec(l, r); }
+ };
+
+ class quatMulVec : public BinaryNode<math::float4, math::float4, math::float4, quatMulVecOp>
+ {
+ public:
+ static const eNodeType mId = quatMulVecId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class quatLerpOp
+ {
+ public:
+ template<typename TYPE1, typename TYPE2, typename TYPE3, typename RESULT> static math::float4 Operation( math::float4 const& a, math::float4 const& b, float c){ return math::quatLerp(a, b, math::float1(c)); }
+ };
+
+ class quatLerp : public TernaryNode<math::float4, math::float4, float, math::float4, quatLerpOp>
+ {
+ public:
+ static const eNodeType mId = quatLerpId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class quatArcRotateOp
+ {
+ public:
+ template<typename TYPE1, typename TYPE2, typename RESULT> static math::float4 Operation( math::float4 const& l, math::float4 const& r){ return math::quatArcRotate(l, r); }
+ };
+
+ class quatArcRotate : public BinaryNode<math::float4, math::float4, math::float4, quatArcRotateOp>
+ {
+ public:
+ static const eNodeType mId = quatArcRotateId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ template<typename TYPE1, typename RESULT> class quatArcRotateXOp
+ {
+ public:
+ static math::float4 Operation( math::float4 const& l){ return math::quatArcRotateX(l); }
+ };
+
+ class quatArcRotateX : public UnaryNode<math::float4, math::float4, quatArcRotateXOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = quatArcRotateXId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ template<typename TYPE1, typename RESULT> class quatXcosOp
+ {
+ public:
+ static math::float4 Operation( math::float4 const& l){ return math::quatXcos(l); }
+ };
+
+ class quatXcos : public UnaryNode<math::float4, math::float4, quatXcosOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = quatXcosId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ template<typename TYPE1, typename RESULT> class quatYcosOp
+ {
+ public:
+ static math::float4 Operation( math::float4 const& l){ return math::quatYcos(l); }
+ };
+
+ class quatYcos : public UnaryNode<math::float4, math::float4, quatYcosOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = quatYcosId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ template<typename TYPE1, typename RESULT> class quatZcosOp
+ {
+ public:
+ static math::float4 Operation( math::float4 const& l){ return math::quatZcos(l); }
+ };
+
+ class quatZcos : public UnaryNode<math::float4, math::float4, quatZcosOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = quatZcosId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ template<typename TYPE1, typename RESULT> class quatEulerToQuatOp
+ {
+ public:
+ static math::float4 Operation( math::float4 const& l){ return math::quatEulerToQuat(l); }
+ };
+
+ class quatEulerToQuat : public UnaryNode<math::float4, math::float4, quatEulerToQuatOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = quatEulerToQuatId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ template<typename TYPE1, typename RESULT> class quatQuatToEulerOp
+ {
+ public:
+ static math::float4 Operation( math::float4 const& l){ return math::quatQuatToEuler(l); }
+ };
+
+ class quatQuatToEuler : public UnaryNode<math::float4, math::float4, quatQuatToEulerOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = quatQuatToEulerId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ template<typename TYPE1, typename RESULT> class quatProjOnYPlaneOp
+ {
+ public:
+ static math::float4 Operation( math::float4 const& l){ return math::quatProjOnYPlane(l); }
+ };
+
+ class quatProjOnYPlane : public UnaryNode<math::float4, math::float4, quatProjOnYPlaneOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = quatProjOnYPlaneId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ template<typename TYPE1, typename RESULT> class quat2QtanOp
+ {
+ public:
+ static math::float4 Operation( math::float4 const& l){ return math::quat2Qtan(l); }
+ };
+
+ class quat2Qtan : public UnaryNode<math::float4, math::float4, quat2QtanOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = quat2QtanId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ template<typename TYPE1, typename RESULT> class qtan2QuatOp
+ {
+ public:
+ static math::float4 Operation( math::float4 const& l){ return math::qtan2Quat(l); }
+ };
+
+ class qtan2Quat : public UnaryNode<math::float4, math::float4, qtan2QuatOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = qtan2QuatId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ template<typename TYPE1, typename RESULT> class ZYRoll2QuatOp
+ {
+ public:
+ static math::float4 Operation( math::float4 const& l){ return math::ZYRoll2Quat(l); }
+ };
+
+ class ZYRoll2Quat : public UnaryNode<math::float4, math::float4, ZYRoll2QuatOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = ZYRoll2QuatId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ template<typename TYPE1, typename RESULT> class quat2ZYRollOp
+ {
+ public:
+ static math::float4 Operation( math::float4 const& l){ return math::quat2ZYRoll(l); }
+ };
+
+ class quat2ZYRoll : public UnaryNode<math::float4, math::float4, quat2ZYRollOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = quat2ZYRollId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ template<typename TYPE1, typename RESULT> class RollZY2QuatOp
+ {
+ public:
+ static math::float4 Operation( math::float4 const& l){ return math::RollZY2Quat(l); }
+ };
+
+ class RollZY2Quat : public UnaryNode<math::float4, math::float4, RollZY2QuatOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = RollZY2QuatId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ template<typename TYPE1, typename RESULT> class quat2RollZYOp
+ {
+ public:
+ static math::float4 Operation( math::float4 const& l){ return math::quat2RollZY(l); }
+ };
+
+ class quat2RollZY : public UnaryNode<math::float4, math::float4, quat2RollZYOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = quat2RollZYId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class quatWeightOp
+ {
+ public:
+ template<typename TYPE1, typename TYPE2, typename RESULT> static math::float4 Operation( math::float4 const& l, float r){ return math::quatWeight(l, math::float1(r)); }
+ };
+
+ class quatWeight : public BinaryNode<math::float4, float, math::float4, quatWeightOp>
+ {
+ public:
+ static const eNodeType mId = quatWeightId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+}
+} \ No newline at end of file
diff --git a/Runtime/mecanim/graph/unarynode.h b/Runtime/mecanim/graph/unarynode.h
new file mode 100644
index 0000000..8ed5b82
--- /dev/null
+++ b/Runtime/mecanim/graph/unarynode.h
@@ -0,0 +1,112 @@
+/*
+ Copyright (c) 7244339 Canada Inc. (Mecanim)
+ All Rights Reserved.
+*/
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/object.h"
+
+#include "Runtime/Math/Simd/float4.h"
+
+#include "Runtime/mecanim/graph/plug.h"
+
+namespace mecanim
+{
+
+namespace graph
+{
+ template <typename TYPE, typename RESULT, typename UnaryPolicies> class UnaryNode : public Node
+ {
+ public:
+ TypePlug<TYPE> mA;
+
+ TypePlug<RESULT> mResult;
+
+ UnaryNode()
+ :mA(true,CRCKey(eA)),
+ mResult(false,CRCKey(eResult))
+ {
+ mA.m_Owner = this;
+ mResult.m_Owner = this;
+ }
+ virtual ~UnaryNode(){}
+
+ virtual uint32_t GetPlugCount()const
+ {
+ return 2;
+ }
+ virtual GraphPlug& GetPlug(uint32_t aIndex)
+ {
+ switch(aIndex)
+ {
+ case 0: return mA;
+ case 1:
+ default: return mResult;
+ }
+ }
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const
+ {
+ switch(aIndex)
+ {
+ case 0: return mA;
+ case 1:
+ default: return mResult;
+ }
+ }
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo)
+ {
+ TYPE a;
+ RESULT result;
+
+ mA.ReadData(&a, arEvaluationInfo);
+
+ result = UnaryPolicies::Operation(a);
+
+ mResult.WriteData(&result, arEvaluationInfo);
+ }
+ };
+
+
+ template< typename TYPE, typename RESULT > class NegationOp
+ {
+ public:
+ static RESULT Operation( TYPE const& l){ return -l; }
+ };
+
+ template<> class NegationOp<bool, bool>
+ {
+ public:
+ static bool Operation(bool const &l) { return !l; }
+ };
+
+ class NegationFloat : public UnaryNode<float, float, NegationOp<float, float> >
+ {
+ public:
+ static const eNodeType mId = NegationFloatId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class NegationInt : public UnaryNode<int32_t, int32_t, NegationOp<int32_t, int32_t> >
+ {
+ public:
+ static const eNodeType mId = NegationIntId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class NegationFloat4 : public UnaryNode<math::float4, math::float4, NegationOp<math::float4, math::float4> >
+ {
+ public:
+ static const eNodeType mId = NegationFloat4Id;
+ virtual eNodeType NodeType(){return mId;}
+ };
+ class NegationBool : public UnaryNode<bool, bool, NegationOp<bool, bool> >
+ {
+ public:
+ static const eNodeType mId = NegationBoolId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+}
+
+}
diff --git a/Runtime/mecanim/graph/xformnode.h b/Runtime/mecanim/graph/xformnode.h
new file mode 100644
index 0000000..d0abf24
--- /dev/null
+++ b/Runtime/mecanim/graph/xformnode.h
@@ -0,0 +1,348 @@
+/*
+ Copyright (c) 7244339 Canada Inc. (Mecanim)
+ All Rights Reserved.
+*/
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/object.h"
+
+#include "Runtime/Math/Simd/math.h"
+
+#include "Runtime/mecanim/graph/plug.h"
+#include "Runtime/mecanim/graph/node.h"
+
+#include "Runtime/mecanim/graph/unarynode.h"
+
+#include "Runtime/mecanim/generic/stringtable.h"
+
+namespace mecanim
+{
+
+namespace graph
+{
+ class IdentityOp
+ {
+ public:
+ template<typename RESULT> static math::xform Operation(){ return math::xformIdentity(); }
+ };
+
+ class xformIdentity : public ResultNode<math::xform, IdentityOp>
+ {
+ public:
+ static const eNodeType mId = xformIdentityId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class xformMulVecOp
+ {
+ public:
+ template<typename TYPE1, typename TYPE2, typename RESULT> static math::float4 Operation( math::xform const& l, math::float4 const& r){ return math::xformMulVec(l, r); }
+ };
+
+ class xformMulVec : public BinaryNode<math::xform, math::float4, math::float4, xformMulVecOp>
+ {
+ public:
+ static const eNodeType mId = xformMulVecId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class xformInvMulVecOp
+ {
+ public:
+ template<typename TYPE1, typename TYPE2, typename RESULT> static math::float4 Operation( math::xform const& l, math::float4 const& r){ return math::xformInvMulVec(l, r); }
+ };
+
+ class xformInvMulVec : public BinaryNode<math::xform, math::float4, math::float4, xformInvMulVecOp>
+ {
+ public:
+ static const eNodeType mId = xformInvMulVecId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class xformMulOp
+ {
+ public:
+ template<typename TYPE1, typename TYPE2, typename RESULT> static math::xform Operation( math::xform const& l, math::xform const& r){ return math::xformMul(l, r); }
+ };
+
+ class xformMul : public BinaryNode<math::xform, math::xform, math::xform, xformMulOp>
+ {
+ public:
+ static const eNodeType mId = xformMulId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class xformInvMulOp
+ {
+ public:
+ template<typename TYPE1, typename TYPE2, typename RESULT> static math::xform Operation( math::xform const& l, math::xform const& r){ return math::xformInvMul(l, r); }
+ };
+
+ class xformInvMul : public BinaryNode<math::xform, math::xform, math::xform, xformInvMulOp>
+ {
+ public:
+ static const eNodeType mId = xformInvMulId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class xformMulInvOp
+ {
+ public:
+ template<typename TYPE1, typename TYPE2, typename RESULT> static math::xform Operation( math::xform const& l, math::xform const& r){ return math::xformMulInv(l, r); }
+ };
+
+ class xformMulInv : public BinaryNode<math::xform, math::xform, math::xform, xformMulInvOp>
+ {
+ public:
+ static const eNodeType mId = xformMulInvId;
+ virtual eNodeType NodeType(){ return mId;}
+ };
+
+ class xformEqualOp
+ {
+ public:
+ template<typename TYPE1, typename TYPE2, typename RESULT> static bool Operation( math::xform const& l, math::xform const& r){ return l == r; }
+ };
+
+ class xformEqual : public BinaryNode<math::xform, math::xform, bool, xformEqualOp>
+ {
+ public:
+ static const eNodeType mId = xformEqualId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class xformWeightOp
+ {
+ public:
+ template<typename TYPE1, typename TYPE2, typename RESULT> static math::xform Operation( math::xform const& l, float r){ return math::xformWeight(l, math::float1(r)); }
+ };
+
+ class xformWeight : public BinaryNode<math::xform, float, math::xform, xformWeightOp>
+ {
+ public:
+ static const eNodeType mId = xformWeightId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class xformBlendOp
+ {
+ public:
+ template<typename TYPE1, typename TYPE2, typename TYPE3, typename RESULT> static math::xform Operation( math::xform const& a,math::xform const& b, float const& c){ return math::xformBlend(a,b,math::float1(c)); }
+ };
+
+ class xformBlend : public TernaryNode<math::xform, math::xform, float, math::xform, xformBlendOp>
+ {
+ public:
+ static const eNodeType mId = xformBlendId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class xformAddOp
+ {
+ public:
+ template<typename TYPE1, typename TYPE2, typename RESULT> static math::xform Operation( math::xform const& l, math::xform const& r){ return math::xformAdd(l, r); }
+ };
+
+ class xformAdd : public BinaryNode<math::xform, math::xform, math::xform, xformAddOp>
+ {
+ public:
+ static const eNodeType mId = xformAddId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class xformSubOp
+ {
+ public:
+ template<typename TYPE1, typename TYPE2, typename RESULT> static math::xform Operation( math::xform const& l, math::xform const& r){ return math::xformSub(l, r); }
+ };
+
+ class xformSub : public BinaryNode<math::xform, math::xform, math::xform, xformSubOp>
+ {
+ public:
+ static const eNodeType mId = xformSubId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class xformComposeOp
+ {
+ public:
+ template<typename TYPE1, typename TYPE2, typename TYPE3, typename RESULT> static math::xform Operation( math::float4 const& t, math::float4 const& q, math::float4 const& s){ return math::xform(t, q, s); }
+ };
+
+ class xformCompose : public TernaryNode<math::float4, math::float4, math::float4, math::xform, xformComposeOp>
+ {
+ public:
+ xformCompose()
+ {
+ mA.m_ID = CRCKey(eT);
+ mB.m_ID = CRCKey(eQ);
+ mC.m_ID = CRCKey(eS);
+ }
+ static const eNodeType mId = xformComposeId;
+ virtual eNodeType NodeType(){return mId;}
+ };
+
+ class xformDecompose : public Node
+ {
+ public:
+ static const eNodeType mId = xformDecomposeId;
+ virtual eNodeType NodeType(){return mId;}
+
+ TypePlug<math::xform> mX;
+ TypePlug<math::float4> mT;
+ TypePlug<math::float4> mQ;
+ TypePlug<math::float4> mS;
+
+ xformDecompose()
+ :mX(true,CRCKey(eX)),
+ mT(false,CRCKey(eT)),
+ mQ(false,CRCKey(eQ)),
+ mS(false,CRCKey(eS))
+ {
+ mX.m_Owner = this;
+ mT.m_Owner = this;
+ mQ.m_Owner = this;
+ mS.m_Owner = this;
+ }
+
+ virtual ~xformDecompose(){}
+
+ virtual uint32_t GetPlugCount()const
+ {
+ return 4;
+ }
+ virtual GraphPlug& GetPlug(uint32_t aIndex)
+ {
+ switch(aIndex)
+ {
+ case 0: return mX;
+ case 1: return mT;
+ case 2: return mQ;
+ case 3:
+ default:return mS;
+ }
+ }
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const
+ {
+ switch(aIndex)
+ {
+ case 0: return mX;
+ case 1: return mT;
+ case 2: return mQ;
+ case 3:
+ default:return mS;
+ }
+ }
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo)
+ {
+ math::xform x;
+
+ mX.ReadData(&x, arEvaluationInfo);
+
+ mT.WriteData(&x.t, arEvaluationInfo);
+ mQ.WriteData(&x.q, arEvaluationInfo);
+ mS.WriteData(&x.s, arEvaluationInfo);
+ }
+ };
+
+ class xformRefChange : public Node
+ {
+ public:
+ static const eNodeType mId = xformRefChangeId;
+ virtual eNodeType NodeType(){return mId;}
+
+ TypePlug<math::xform> mSrcRefX;
+ TypePlug<math::xform> mDstRefX;
+ TypePlug<math::xform> mSrcPivotX;
+ TypePlug<math::xform> mDstPivotX;
+ TypePlug<float> mRefWeight;
+ TypePlug<float> mPivotWeight;
+ TypePlug<math::xform> mXI;
+ TypePlug<math::xform> mXO;
+
+ xformRefChange() : mSrcRefX(true,CRCKey(eSrcRefX)),
+ mDstRefX(true,CRCKey(eDstRefX)),
+ mSrcPivotX(true,CRCKey(eSrcPivotX)),
+ mDstPivotX(true,CRCKey(eDstPivotX)),
+ mRefWeight(true,CRCKey(eRefWeight)),
+ mPivotWeight(true,CRCKey(ePivotWeight)),
+ mXI(true,CRCKey(eXI)),
+ mXO(false,CRCKey(eXO))
+ {
+ mSrcRefX.m_Owner = this;
+ mDstRefX.m_Owner = this;
+ mSrcPivotX.m_Owner = this;
+ mDstPivotX.m_Owner = this;
+ mRefWeight.m_Owner = this;
+ mPivotWeight.m_Owner = this;
+ mXI.m_Owner = this;
+ mXO.m_Owner = this;
+ }
+
+ virtual ~xformRefChange(){}
+
+ virtual uint32_t GetPlugCount()const
+ {
+ return 8;
+ }
+ virtual GraphPlug& GetPlug(uint32_t aIndex)
+ {
+ switch(aIndex)
+ {
+ case 0: return mSrcRefX;
+ case 1: return mDstRefX;
+ case 2: return mSrcPivotX;
+ case 3: return mDstPivotX;
+ case 4: return mRefWeight;
+ case 5: return mPivotWeight;
+ case 6: return mXI;
+ case 7:
+ default: return mXO;
+ }
+ }
+ virtual GraphPlug const& GetPlug(uint32_t aIndex)const
+ {
+ switch(aIndex)
+ {
+ case 0: return mSrcRefX;
+ case 1: return mDstRefX;
+ case 2: return mSrcPivotX;
+ case 3: return mDstPivotX;
+ case 4: return mRefWeight;
+ case 5: return mPivotWeight;
+ case 6: return mXI;
+ case 7:
+ default: return mXO;
+ }
+ }
+
+ virtual void Evaluate(EvaluationInfo& arEvaluationInfo)
+ {
+ math::xform srcRefX, dstRefX, srcPivotX, dstPivotX, xi, xo;
+ float rw,pw;
+
+ mSrcRefX.ReadData(&srcRefX, arEvaluationInfo);
+ mDstRefX.ReadData(&dstRefX, arEvaluationInfo);
+ mSrcPivotX.ReadData(&srcPivotX, arEvaluationInfo);
+ mDstPivotX.ReadData(&dstPivotX, arEvaluationInfo);
+ mRefWeight.ReadData(&rw, arEvaluationInfo);
+ mPivotWeight.ReadData(&pw, arEvaluationInfo);
+ mXI.ReadData(&xi,arEvaluationInfo);
+
+ xo = math::xformMul(xi,srcPivotX);
+ xo = math::xformInvMul(srcRefX,xo);
+ xo = math::xformWeight(xo,math::float1(1-pw));
+ xo = math::xformMul(dstRefX,xo);
+ xo = math::xformMulInv(xo,dstPivotX);
+ xo = math::xformBlend(xi,xo,math::float1(rw));
+
+ mXO.WriteData(&xo, arEvaluationInfo);
+ }
+ };
+}
+
+} \ No newline at end of file
diff --git a/Runtime/mecanim/human/hand.cpp b/Runtime/mecanim/human/hand.cpp
new file mode 100644
index 0000000..dd5e062
--- /dev/null
+++ b/Runtime/mecanim/human/hand.cpp
@@ -0,0 +1,351 @@
+#include "UnityPrefix.h"
+#include "Runtime/mecanim/skeleton/skeleton.h"
+#include "Runtime/mecanim/human/hand.h"
+
+namespace mecanim
+{
+
+namespace hand
+{
+ const static int32_t Phalange2DoF[hand::kLastPhalange][3] =
+ {
+ { kProximalDownUp, kProximalInOut, -1 }, // kProximal
+ { kIntermediateCloseOpen, -1, -1 }, // kIntermediate
+ { kDistalCloseOpen, -1, -1 } // kDistal
+ };
+
+ const static int32_t DoF2Bone[s_DoFCount] =
+ {
+ kThumb * kLastPhalange + kProximal,
+ kThumb * kLastPhalange + kProximal,
+ kThumb * kLastPhalange + kIntermediate,
+ kThumb * kLastPhalange + kDistal,
+
+ kIndex * kLastPhalange + kProximal,
+ kIndex * kLastPhalange + kProximal,
+ kIndex * kLastPhalange + kIntermediate,
+ kIndex * kLastPhalange + kDistal,
+
+ kMiddle * kLastPhalange + kProximal,
+ kMiddle * kLastPhalange + kProximal,
+ kMiddle * kLastPhalange + kIntermediate,
+ kMiddle * kLastPhalange + kDistal,
+
+ kRing * kLastPhalange + kProximal,
+ kRing * kLastPhalange + kProximal,
+ kRing * kLastPhalange + kIntermediate,
+ kRing * kLastPhalange + kDistal,
+
+ kLittle * kLastPhalange + kProximal,
+ kLittle * kLastPhalange + kProximal,
+ kLittle * kLastPhalange + kIntermediate,
+ kLittle * kLastPhalange + kDistal
+ };
+
+
+
+ skeleton::SetupAxesInfo const& GetAxeInfo(uint32_t index)
+ {
+ const static skeleton::SetupAxesInfo setupAxesInfoArray[s_BoneCount] =
+ {
+ {{0,0.125,0.125f,1},{0,1,0,0},{0,-25,-20},{0,25,20},{-1,-1,1,-1},math::kZYRoll,0}, // kThumb.kProximal
+ {{0,-0.2f,0,1},{0,1,0,0},{0,0,-40},{0,0,35},{-1,1,1,-1},math::kZYRoll,0}, // kThumb.kIntermediate
+ {{0,-0.2f,0,1},{0,1,0,0},{0,0,-40},{0,0,35},{-1,1,1,-1},math::kZYRoll,0}, // kThumb.kDistal
+ {{0,0.08f,0.3f,1},{0,0,1,0},{0,-20,-50},{0,20,50},{-1,-1,-1,-1},math::kZYRoll,0}, // kIndex.kProximal
+ {{0,0,0.33f,1},{0,0,1,0},{0,0,-45},{0,0,45},{-1,1,-1,-1},math::kZYRoll,0}, // kIndex.kIntermediate
+ {{0,0,0.33f,1},{0,0,1,0},{0,0,-45},{0,0,45},{-1,1,-1,-1},math::kZYRoll,0}, // kIndex.kDistal
+ {{0,0.04f,0.3f,1},{0,0,1,0},{0,-7.5f,-50},{0,7.5f,50},{-1,-1,-1,-1},math::kZYRoll,0}, // kMiddle.kProximal
+ {{0,0,0.33f,1},{0,0,1,0},{0,0,-45},{0,0,45},{-1,1,-1,-1},math::kZYRoll,0}, // kMiddle.kIntermediate
+ {{0,0,0.33f,1},{0,0,1,0},{0,0,-45},{0,0,45},{-1,1,-1,-1},math::kZYRoll,0}, // kMiddle.kDistal
+ {{0,-0.04f,0.3f,1},{0,0,1,0},{0,-7.5f,-50},{0,7.5f,50},{-1,1,-1,-1},math::kZYRoll,0}, // kRing.kProximal
+ {{0,0,0.33f,1},{0,0,1,0},{0,0,-45},{0,0,45},{-1,1,-1,-1},math::kZYRoll,0}, // kRing.kIntermediate
+ {{0,0,0.33f,1},{0,0,1,0},{0,0,-45},{0,0,45},{-1,1,-1,-1},math::kZYRoll,0}, // kRing.kDistal
+ {{0,-0.08f,0.3f,1},{0,0,1,0},{0,-20,-50},{0,20,50},{-1,1,-1,-1},math::kZYRoll,0}, // kLittle.kProximal
+ {{0,0,0.33f,1},{0,0,1,0},{0,0,-45},{0,0,45},{-1,1,-1,-1},math::kZYRoll,0}, // kLittle.kIntermediate
+ {{0,0,0.33f,1},{0,0,1,0},{0,0,-45},{0,0,45},{-1,1,-1,-1},math::kZYRoll,0} // kLittle.kDistal
+ };
+
+ return setupAxesInfoArray[index];
+ }
+
+
+ const char* FingerName(uint32_t aFinger)
+ {
+ const static char* fingerName[kLastFinger] = {
+ "Thumb",
+ "Index",
+ "Middle",
+ "Ring",
+ "Little"
+ };
+
+ return fingerName[aFinger];
+ }
+
+ const char* PhalangeName(uint32_t aFinger)
+ {
+ const static char* phalangeName[kLastPhalange] = {
+ "Proximal",
+ "Intermediate",
+ "Distal"
+ };
+ return phalangeName[aFinger];
+ }
+
+ const char* FingerDoFName(uint32_t aFinger)
+ {
+ const static char* fingerDoFName[kLastFingerDoF] = {
+ "1 Stretched",
+ "Spread",
+ "2 Stretched",
+ "3 Stretched"
+ };
+ return fingerDoFName[aFinger];
+ }
+
+ Hand::Hand()
+ {
+ memset(m_HandBoneIndex, -1, sizeof(int32_t)*s_BoneCount);
+ }
+
+ int32_t MuscleFromBone(int32_t aBoneIndex, int32_t aDoFIndex)
+ {
+ int32_t ret = -1;
+ int32_t findex = GetFingerIndex(aBoneIndex);
+ int32_t pindex = GetPhalangeIndex(aBoneIndex);
+
+ if(Phalange2DoF[pindex][2-aDoFIndex] != -1)
+ {
+ ret = findex * kLastFingerDoF + Phalange2DoF[pindex][2-aDoFIndex];
+ }
+
+ return ret;
+ }
+
+ int32_t BoneFromMuscle(int32_t aDoFIndex)
+ {
+ return DoF2Bone[aDoFIndex];
+ }
+
+ Hand* CreateHand(memory::Allocator& arAlloc)
+ {
+ Hand* hand = arAlloc.Construct<Hand>();
+ memset(hand->m_HandBoneIndex,-1,sizeof(int32_t)*kLastFinger*kLastPhalange);
+
+ return hand;
+ }
+
+ void DestroyHand(Hand *apHand, memory::Allocator& arAlloc)
+ {
+ if(apHand)
+ {
+ arAlloc.Deallocate(apHand);
+ }
+ }
+
+ HandPose::HandPose()
+ {
+ int32_t i;
+
+ for(i = 0; i < s_DoFCount; i++)
+ {
+ m_DoFArray[i]= 0;
+ }
+
+ m_Override = 0;
+ m_CloseOpen = 0;
+ m_InOut = 0;
+ m_Grab = 0;
+ m_GrabX = math::xformIdentity();
+ }
+
+ void HandPoseCopy(HandPose const *apHandPoseSrc,HandPose *apHandPoseDst)
+ {
+ int32_t i;
+
+ for(i = 0; i < s_DoFCount; i++)
+ {
+ apHandPoseDst->m_DoFArray[i] = apHandPoseSrc->m_DoFArray[i];
+ }
+
+ apHandPoseDst->m_Override = apHandPoseDst->m_Override;
+ apHandPoseDst->m_CloseOpen = apHandPoseDst->m_CloseOpen;
+ apHandPoseDst->m_InOut = apHandPoseDst->m_InOut;
+ apHandPoseDst->m_Grab = apHandPoseDst->m_Grab;
+ apHandPoseDst->m_GrabX = apHandPoseDst->m_GrabX;
+ }
+
+ void HandSetupAxes(Hand const *apHand, skeleton::SkeletonPose const *apSkeletonPose, skeleton::Skeleton *apSkeleton, bool aLeft)
+ {
+ int32_t f,p,b;
+
+ for(f = 0; f < kLastFinger; f++)
+ {
+ for(p = 0; p < kLastPhalange; p++)
+ {
+ float len = 1.0f;
+ int32_t skAxisBoneId = -1;
+
+ b = GetBoneIndex(f,p);
+ int32_t skBoneIndex = apHand->m_HandBoneIndex[b];
+
+ if(p < kLastPhalange-1 && apHand->m_HandBoneIndex[GetBoneIndex(f,p+1)] >= 0)
+ {
+ skAxisBoneId = apHand->m_HandBoneIndex[GetBoneIndex(f,p+1)];
+ }
+ else if(p > 0 && apHand->m_HandBoneIndex[GetBoneIndex(f,p-1)] >= 0)
+ {
+ skAxisBoneId = apHand->m_HandBoneIndex[GetBoneIndex(f,p-1)];
+ len = -0.75f;
+ }
+
+ if(skBoneIndex >= 0)
+ {
+ skeleton::SetupAxes(apSkeleton,apSkeletonPose, GetAxeInfo(b), skBoneIndex,skAxisBoneId,aLeft,len);
+ }
+ }
+ }
+ }
+
+ void HandCopyAxes(Hand const *apSrcHand, skeleton::Skeleton const *apSrcSkeleton, Hand const *apHand, skeleton::Skeleton *apSkeleton)
+ {
+ int32_t i;
+
+ for(i = 0; i < s_BoneCount; i++)
+ {
+ skeleton::Node const *srcNode = apSrcHand->m_HandBoneIndex[i] >= 0 ? &apSrcSkeleton->m_Node[apSrcHand->m_HandBoneIndex[i]] : 0;
+ skeleton::Node const *node = apHand->m_HandBoneIndex[i] >= 0 ? &apSkeleton->m_Node[apHand->m_HandBoneIndex[i]] : 0;
+
+ if(srcNode != 0 && node != 0 && srcNode->m_AxesId != -1 && node->m_AxesId != -1)
+ {
+ apSkeleton->m_AxesArray[node->m_AxesId] = apSrcSkeleton->m_AxesArray[srcNode->m_AxesId];
+ }
+ }
+ }
+
+ void HandPoseSolve(HandPose const* apHandPoseIn,HandPose* apHandPoseOut)
+ {
+ int32_t f;
+
+ for(f = 0; f < kLastFinger; f++)
+ {
+ int32_t i = f*kLastFingerDoF;
+ apHandPoseOut->m_DoFArray[i+kProximalDownUp] = (1-apHandPoseIn->m_Override) * apHandPoseIn->m_DoFArray[i+kProximalDownUp] + apHandPoseIn->m_CloseOpen;
+ apHandPoseOut->m_DoFArray[i+kProximalInOut] = (1-apHandPoseIn->m_Override) * apHandPoseIn->m_DoFArray[i+kProximalInOut] + apHandPoseIn->m_InOut;
+ apHandPoseOut->m_DoFArray[i+kIntermediateCloseOpen] = (1-apHandPoseIn->m_Override) * apHandPoseIn->m_DoFArray[i+kIntermediateCloseOpen] + apHandPoseIn->m_CloseOpen;
+ apHandPoseOut->m_DoFArray[i+kDistalCloseOpen] = (1-apHandPoseIn->m_Override) * apHandPoseIn->m_DoFArray[i+kDistalCloseOpen] + apHandPoseIn->m_CloseOpen;
+ }
+ }
+
+ void Hand2SkeletonPose(Hand const *apHand, skeleton::Skeleton const *apSkeleton, HandPose const *apHandPose, skeleton::SkeletonPose *apSkeletonPose)
+ {
+ int32_t f,p;
+
+ for(f = 0; f < kLastFinger; f++)
+ {
+ for(p = 0; p < kLastPhalange; p++)
+ {
+ int32_t i = GetBoneIndex(f,p);
+
+ if(apHand->m_HandBoneIndex[i] >= 0)
+ {
+ math::float4 xyz = math::cond( math::bool4(Phalange2DoF[p][2] != -1,Phalange2DoF[p][1] != -1,Phalange2DoF[p][0] != -1,false),
+ math::float4(apHandPose->m_DoFArray[GetDoFIndex(f,Phalange2DoF[p][2])],apHandPose->m_DoFArray[GetDoFIndex(f,Phalange2DoF[p][1])],apHandPose->m_DoFArray[GetDoFIndex(f,Phalange2DoF[p][0])],0),
+ math::float4::zero());
+
+ skeleton::SkeletonSetDoF(apSkeleton,apSkeletonPose,xyz,apHand->m_HandBoneIndex[i]);
+ }
+ }
+ }
+ }
+
+ void Skeleton2HandPose(Hand const *apHand, skeleton::Skeleton const *apSkeleton,skeleton::SkeletonPose const *apSkeletonPose, HandPose *apHandPose, float aOffset)
+ {
+ int32_t f,p;
+
+ for(f = 0; f < kLastFinger; f++)
+ {
+ for(p = 0; p < kLastPhalange; p++)
+ {
+ int32_t i = GetBoneIndex(f,p);
+
+ if(apHand->m_HandBoneIndex[i] >= 0)
+ {
+ const math::float4 xyz = skeleton::SkeletonGetDoF(apSkeleton,apSkeletonPose,apHand->m_HandBoneIndex[i]);
+
+ if(Phalange2DoF[p][2] != -1) apHandPose->m_DoFArray[GetDoFIndex(f,Phalange2DoF[p][2])] = xyz.x().tofloat() + aOffset;
+ if(Phalange2DoF[p][1] != -1) apHandPose->m_DoFArray[GetDoFIndex(f,Phalange2DoF[p][1])] = xyz.y().tofloat() + aOffset;
+ if(Phalange2DoF[p][0] != -1) apHandPose->m_DoFArray[GetDoFIndex(f,Phalange2DoF[p][0])] = xyz.z().tofloat() + aOffset;
+ }
+ }
+ }
+ }
+
+ void FingerLengths(Hand const *apHand, skeleton::Skeleton const *apSkeleton, float *apLengthArray)
+ {
+ int32_t f,p;
+
+ for(f = 0; f < kLastFinger; f++)
+ {
+ apLengthArray[f] = 0.0f;
+
+ for(p = 0; p < kLastPhalange; p++)
+ {
+ int32_t i = GetBoneIndex(f,p);
+
+ if(apHand->m_HandBoneIndex[i] >= 0)
+ {
+ apLengthArray[f] += apSkeleton->m_AxesArray[apHand->m_HandBoneIndex[i]].m_Length;
+ }
+ }
+ }
+ }
+
+ void FingerBaseFromPose(Hand const *apHand,skeleton::SkeletonPose const *apSkeletonPose,math::float4 *apPositionArray)
+ {
+ int32_t f;
+
+ for(f = 0; f < kLastFinger; f++)
+ {
+ int32_t i = GetBoneIndex(f,kProximal);
+
+ if(apHand->m_HandBoneIndex[i] >= 0)
+ {
+ apPositionArray[f] = apSkeletonPose->m_X[apHand->m_HandBoneIndex[i]].t;
+ }
+ }
+ }
+
+ void FingerTipsFromPose(Hand const *apHand,skeleton::Skeleton const *apSkeleton, skeleton::SkeletonPose const *apSkeletonPose,math::float4 *apPositionArray)
+ {
+ int32_t f;
+
+ for(f = 0; f < kLastFinger; f++)
+ {
+ int32_t i = GetBoneIndex(f,kDistal);
+
+ if(apHand->m_HandBoneIndex[i] >= 0)
+ {
+ apPositionArray[f] = skeleton::SkeletonNodeEndPoint(apSkeleton,apHand->m_HandBoneIndex[i],apSkeletonPose);
+ }
+ }
+ }
+
+ void FingersIKSolve(Hand const *apHand, skeleton::Skeleton const *apSkeleton,math::float4 const *apPositionArray, float *apWeightArray, skeleton::SkeletonPose *apSkeletonPose, skeleton::SkeletonPose *apSkeletonPoseWorkspace)
+ {
+ int32_t f;
+
+ for(f = 0; f < kLastFinger; f++)
+ {
+ int32_t topIndex = apHand->m_HandBoneIndex[hand::GetBoneIndex(f,hand::kProximal)];
+ int32_t midIndex = apHand->m_HandBoneIndex[hand::GetBoneIndex(f,hand::kIntermediate)];
+ int32_t endIndex = apHand->m_HandBoneIndex[hand::GetBoneIndex(f,hand::kDistal)];
+
+ if(topIndex >= 0 && midIndex >= 0 && endIndex >= 0)
+ {
+ skeleton::Skeleton3BoneIK(apSkeleton,topIndex,midIndex,endIndex,apPositionArray[f],apWeightArray[f],apSkeletonPose,apSkeletonPoseWorkspace);
+ }
+ }
+ }
+}
+}
diff --git a/Runtime/mecanim/human/hand.h b/Runtime/mecanim/human/hand.h
new file mode 100644
index 0000000..1050340
--- /dev/null
+++ b/Runtime/mecanim/human/hand.h
@@ -0,0 +1,124 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/Math/Simd/xform.h"
+
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Animation/MecanimArraySerialization.h"
+
+
+namespace mecanim
+{
+
+namespace skeleton { struct Skeleton; struct SkeletonPose; struct SetupAxesInfo; }
+
+namespace hand
+{
+ enum Fingers
+ {
+ kThumb = 0 ,
+ kIndex,
+ kMiddle,
+ kRing,
+ kLittle,
+ kLastFinger
+ };
+
+ enum Phalanges
+ {
+ kProximal = 0,
+ kIntermediate,
+ kDistal,
+ kLastPhalange
+ };
+
+ enum FingerDoF
+ {
+ kProximalDownUp = 0,
+ kProximalInOut,
+ kIntermediateCloseOpen,
+ kDistalCloseOpen,
+ kLastFingerDoF
+ };
+
+ const int32_t s_BoneCount = kLastFinger*kLastPhalange;
+ const int32_t s_DoFCount = kLastFinger*kLastFingerDoF;
+
+ inline int32_t GetBoneIndex(int32_t fingerIndex, int32_t phalangeIndex) { return fingerIndex * kLastPhalange + phalangeIndex; };
+ inline int32_t GetFingerIndex(int32_t boneIndex) { return boneIndex / kLastPhalange; };
+ inline int32_t GetPhalangeIndex(int32_t boneIndex) { return boneIndex % kLastPhalange; };
+ inline int32_t GetDoFIndex(int32_t fingerIndex, int32_t phalangeDoFIndex) { return fingerIndex * kLastFingerDoF + phalangeDoFIndex; };
+
+ const char* FingerName(uint32_t finger);
+ const char* FingerDoFName(uint32_t finger);
+ const char* PhalangeName(uint32_t finger);
+
+ struct Hand
+ {
+ DEFINE_GET_TYPESTRING(Hand)
+
+ Hand();
+ int32_t m_HandBoneIndex[s_BoneCount];
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ STATIC_ARRAY_TRANSFER(int32_t, m_HandBoneIndex, s_BoneCount);
+ }
+ };
+
+ struct HandPose
+ {
+ DEFINE_GET_TYPESTRING(HandPose)
+
+ HandPose();
+
+ math::xform m_GrabX;
+ float m_DoFArray[s_DoFCount];
+ float m_Override;
+ float m_CloseOpen;
+ float m_InOut;
+ float m_Grab;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_GrabX);
+ STATIC_ARRAY_TRANSFER(float, m_DoFArray, s_DoFCount);
+
+ TRANSFER(m_Override);
+ TRANSFER(m_CloseOpen);
+ TRANSFER(m_InOut);
+ TRANSFER(m_Grab);
+ }
+ };
+
+ int32_t MuscleFromBone(int32_t aBoneIndex, int32_t aDoFIndex);
+ int32_t BoneFromMuscle(int32_t aDoFIndex);
+
+ Hand* CreateHand(memory::Allocator& alloc);
+ void DestroyHand(Hand *hand, memory::Allocator& alloc);
+
+ void HandSetupAxes(Hand const *hand, skeleton::SkeletonPose const *skeletonPose, skeleton::Skeleton *skeleton, bool aLeft);
+ void HandCopyAxes(Hand const *srcHand, skeleton::Skeleton const *srcSkeleton, Hand const *hand, skeleton::Skeleton *skeleton);
+ void HandPoseCopy(HandPose const *handPoseSrc, HandPose *handPoseDst);
+
+ // Retargeting function set
+ void HandPoseSolve(HandPose const* handPose,HandPose* handPoseOut);
+ void Hand2SkeletonPose(Hand const *hand, skeleton::Skeleton const *skeleton, HandPose const *handPose, skeleton::SkeletonPose *skeletonPose);
+ void Skeleton2HandPose(Hand const *hand, skeleton::Skeleton const *skeleton,skeleton::SkeletonPose const *skeletonPose, HandPose *handPose, float offset = 0.0f);
+ // IK
+ void FingerLengths(Hand const *hand, float *lengthArray);
+ void FingerBaseFromPose(Hand const *hand,skeleton::SkeletonPose const *skeletonPose,math::float4 *positionArray);
+ void FingerTipsFromPose(Hand const *hand,skeleton::Skeleton const *skeleton, skeleton::SkeletonPose const *skeletonPose,math::float4 *positionArray);
+
+ void FingersIKSolve(Hand const *hand, skeleton::Skeleton const *skeleton,math::float4 const *positionArray, float *apWeightArray, skeleton::SkeletonPose *skeletonPose, skeleton::SkeletonPose *skeletonPoseWorkspace);
+
+ mecanim::skeleton::SetupAxesInfo const& GetAxeInfo(uint32_t index);
+
+}// namespace hand
+
+}
diff --git a/Runtime/mecanim/human/handle.h b/Runtime/mecanim/human/handle.h
new file mode 100644
index 0000000..af1df50
--- /dev/null
+++ b/Runtime/mecanim/human/handle.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/Math/Simd/xform.h"
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+namespace mecanim
+{
+
+namespace human
+{
+ struct Handle
+ {
+ DEFINE_GET_TYPESTRING(Handle)
+
+ Handle():
+ m_X(math::xformIdentity()),
+ m_ParentHumanIndex(numeric_limits<uint32_t>::max_value),
+ m_ID(numeric_limits<uint32_t>::max_value)
+ {
+ }
+
+ math::xform m_X; // Local tranform
+ uint32_t m_ParentHumanIndex; // Related parent's human bone index
+ uint32_t m_ID;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_X);
+ TRANSFER(m_ParentHumanIndex);
+ TRANSFER(m_ID);
+ }
+ };
+}
+
+}
diff --git a/Runtime/mecanim/human/human.cpp b/Runtime/mecanim/human/human.cpp
new file mode 100644
index 0000000..4e2f8bf
--- /dev/null
+++ b/Runtime/mecanim/human/human.cpp
@@ -0,0 +1,2405 @@
+#include "UnityPrefix.h"
+#include "Runtime/mecanim/skeleton/skeleton.h"
+#include "Runtime/mecanim/human/human.h"
+
+namespace mecanim
+{
+
+// anonymous namespace to hide data in local file scope
+namespace
+{
+ using namespace human;
+
+ static const int32_t Bone2DoF[kLastBone][3] =
+ {
+ { -1, -1, -1 }, // kHips
+ { kLeftLegDoFStart+kUpperLegFrontBack, kLeftLegDoFStart+kUpperLegInOut, kLeftLegDoFStart+kUpperLegRollInOut }, // kLeftUpperLeg
+ { kRightLegDoFStart+kUpperLegFrontBack, kRightLegDoFStart+kUpperLegInOut, kRightLegDoFStart+kUpperLegRollInOut }, // kRightUpperLeg
+ { kLeftLegDoFStart+kLegCloseOpen, -1, kLeftLegDoFStart+kLegRollInOut }, // kLeftLeg
+ { kRightLegDoFStart+kLegCloseOpen, -1, kRightLegDoFStart+kLegRollInOut }, // kRightLeg
+ { kLeftLegDoFStart+kFootCloseOpen, kLeftLegDoFStart+kFootInOut, -1 }, // kLeftFoot
+ { kRightLegDoFStart+kFootCloseOpen, kRightLegDoFStart+kFootInOut, -1 }, // kRightFoot
+ { kBodyDoFStart+kSpineFrontBack, kBodyDoFStart+kSpineLeftRight, kBodyDoFStart+kSpineRollLeftRight }, // kSpine
+ { kBodyDoFStart+kChestFrontBack, kBodyDoFStart+kChestLeftRight, kBodyDoFStart+kChestRollLeftRight }, // kChest
+ { kHeadDoFStart+kNeckFrontBack, kHeadDoFStart+kNeckLeftRight, kHeadDoFStart+kNeckRollLeftRight }, // kNeck
+ { kHeadDoFStart+kHeadFrontBack, kHeadDoFStart+kHeadLeftRight, kHeadDoFStart+kHeadRollLeftRight }, // kHead
+ { kLeftArmDoFStart+kShoulderDownUp, kLeftArmDoFStart+kShoulderFrontBack, -1 }, // kLeftShoulder
+ { kRightArmDoFStart+kShoulderDownUp, kRightArmDoFStart+kShoulderFrontBack, -1 }, // kRightShoulder
+ { kLeftArmDoFStart+kArmDownUp, kLeftArmDoFStart+kArmFrontBack, kLeftArmDoFStart+kArmRollInOut }, // kLeftArm
+ { kRightArmDoFStart+kArmDownUp, kRightArmDoFStart+kArmFrontBack, kRightArmDoFStart+kArmRollInOut }, // kRightArm
+ { kLeftArmDoFStart+kForeArmCloseOpen, -1, kLeftArmDoFStart+kForeArmRollInOut }, // kLeftForeArm
+ { kRightArmDoFStart+kForeArmCloseOpen, -1, kRightArmDoFStart+kForeArmRollInOut }, // kRightForeArm
+ { kLeftArmDoFStart+kHandDownUp,kLeftArmDoFStart+kHandInOut, -1 }, // kLeftHand
+ { kRightArmDoFStart+kHandDownUp,kRightArmDoFStart+kHandInOut, -1 }, // kRightHand
+ { kLeftLegDoFStart+kToesUpDown, -1, -1}, // kLeftToes
+ { kRightLegDoFStart+kToesUpDown, -1, -1 }, // kRightToes
+ { kHeadDoFStart+kLeftEyeDownUp, kHeadDoFStart+kLeftEyeLeftRight,-1 }, // LeftEye
+ { kHeadDoFStart+kRightEyeDownUp, kHeadDoFStart+kRightEyeLeftRight,-1 }, // RightEye
+ { kHeadDoFStart+kJawDownUp, kHeadDoFStart+kJawLeftRight,-1 } // Jaw
+ };
+
+ static const float HumanBoneDefaultMass[kLastBone] =
+ {
+ 12.0f, // kHips
+ 10.0f, // kLeftUpperLeg
+ 10.0f, // kRightUpperLeg
+ 4.0f, // kLeftLowerLeg
+ 4.0f, // kRightLowerLeg
+ 0.8f, // kLeftFoot
+ 0.8f, // kRightFoot
+ 2.5f, // kSpine
+ 24.0f, // kChest
+ 1.0f, // kNeck
+ 4.0f, // kHead
+ 0.5f, // kLeftShoulder
+ 0.5f, // kRightShoulder
+ 2.0f, // kLeftUpperArm
+ 2.0f, // kRightUpperArm
+ 1.5f, // kLeftLowerArm
+ 1.5f, // kRightLowerArm
+ 0.5f, // kLeftHand
+ 0.5f, // kRightHand
+ 0.2f, // kLeftToes
+ 0.2f, // kRightToes
+ 0.0f, // LeftEye
+ 0.0f, // RightEye
+ 0.0f // Jaw
+ };
+
+ static const int32_t BoneMirror[kLastBone] =
+ {
+ kHips, // kHips
+ kRightUpperLeg, // kLeftUpperLeg
+ -kLeftUpperLeg, // kRightUpperLeg
+ kRightLowerLeg, // kLeftLowerLeg
+ -kLeftLowerLeg, // kRightLowerLeg
+ kRightFoot, // kLeftFoot
+ -kLeftFoot, // kRightFoot
+ kSpine, // kSpine
+ kChest, // kChest
+ kNeck, // kNeck
+ kHead, // kHead
+ kRightShoulder, // kLeftShoulder
+ -kLeftShoulder, // kRightShoulder
+ kRightUpperArm, // kLeftUpperArm
+ -kLeftUpperArm, // kRightUpperArm
+ kRightLowerArm, // kLeftLowerArm
+ -kLeftLowerArm, // kRightLowerArm
+ kRightHand, // kLeftHand
+ -kLeftHand, // kRightHand
+ kRightToes, // kLeftToes
+ -kLeftToes, // kRightToes
+ kRightEye, // kLeftEye
+ -kLeftEye, // kRightEye
+ kJaw, // kJaw
+ };
+
+ static const float BodyDoFMirror[kLastBodyDoF] =
+ {
+ +1.0f, // kSpineFrontBack = 0,
+ -1.0f, // kSpineLeftRight,
+ -1.0f, // kSpineRollLeftRight,
+ +1.0f, // kChestFrontBack,
+ -1.0f, // kChestLeftRight,
+ -1.0f // kChestRollLeftRight,
+ };
+
+ static const float HeadDoFMirror[kLastHeadDoF] =
+ {
+ +1.0f, // kNeckFrontBack = 0,
+ -1.0f, // kNeckLeftRight,
+ -1.0f, // kNeckRollLeftRight,
+ +1.0f, // kHeadFrontBack,
+ -1.0f, // kHeadLeftRight,
+ -1.0f, // kHeadRollLeftRight,
+ +1.0f, // kLeftEyeDownUp,
+ -1.0f, // kLeftEyeLeftRight,
+ +1.0f, // kRightEyeDownUp,
+ -1.0f, // kRightEyeLeftRight,
+ +1.0f, // kJawDownUp,
+ -1.0f // kJawLeftRight,
+ };
+
+ static const int32_t BoneChildren[kLastBone][4] =
+ {
+ { 3,kLeftUpperLeg, kRightUpperLeg, kSpine },// kHips
+ { 1,kLeftLowerLeg }, // kLeftUpperLeg
+ { 1,kRightLowerLeg }, // kRightUpperLeg
+ { 1,kLeftFoot }, // kLeftLowerLeg
+ { 1,kRightFoot }, // kRightLowerLeg
+ { 1,kLeftToes }, // kLeftFoot
+ { 1,kRightToes }, // kRightFoot
+ { 1,kChest }, // kSpine
+ { 3,kNeck, kLeftShoulder, kRightShoulder }, // kChest
+ { 1,kHead }, // kNeck
+ { 3,kLeftEye,kRightEye,kJaw }, // kHead
+ { 1,kLeftUpperArm }, // kLeftShoulder
+ { 1,kRightUpperArm }, // kRightShoulder
+ { 1,kLeftLowerArm }, // kLeftUpperArm
+ { 1,kRightLowerArm }, // kRightUpperArm
+ { 1,kLeftHand }, // kLeftLowerArm
+ { 1,kRightHand }, // kRightLowerArm
+ { 0 }, // kLeftHand
+ { 0 }, // kRightHand
+ { 0 }, // kLeftToes
+ { 0 }, // kRightToes
+ { 0 }, // kLeftEye
+ { 0 }, // kRightEye
+ { 0 } // kJaw
+ };
+
+ static const int32_t DoF2Bone[human::kLastDoF] = {
+ kSpine,
+ kSpine,
+ kSpine,
+ kChest,
+ kChest,
+ kChest,
+ kNeck,
+ kNeck,
+ kNeck,
+ kHead,
+ kHead,
+ kHead,
+ kLeftEye,
+ kLeftEye,
+ kRightEye,
+ kRightEye,
+ kJaw,
+ kJaw,
+ kLeftUpperLeg,
+ kLeftUpperLeg,
+ kLeftUpperLeg,
+ kLeftLowerLeg,
+ kLeftLowerLeg,
+ kLeftFoot,
+ kLeftFoot,
+ kLeftToes,
+ kRightUpperLeg,
+ kRightUpperLeg,
+ kRightUpperLeg,
+ kRightLowerLeg,
+ kRightLowerLeg,
+ kRightFoot,
+ kRightFoot,
+ kRightToes,
+ kLeftShoulder,
+ kLeftShoulder,
+ kLeftUpperArm,
+ kLeftUpperArm,
+ kLeftUpperArm,
+ kLeftLowerArm,
+ kLeftLowerArm,
+ kLeftHand,
+ kLeftHand,
+ kRightShoulder,
+ kRightShoulder,
+ kRightUpperArm,
+ kRightUpperArm,
+ kRightUpperArm,
+ kRightLowerArm,
+ kRightLowerArm,
+ kRightHand,
+ kRightHand
+ };
+
+ static const int32_t DoF2BoneDoFIndex[human::kLastDoF] = {
+ 2, // kSpine,
+ 1, // kSpine,
+ 0, // kSpine,
+ 2, // kChest,
+ 1, // kChest,
+ 0, // kChest,
+ 2, // kNeck,
+ 1, // kNeck,
+ 0, // kNeck,
+ 2, // kHead,
+ 1, // kHead,
+ 0, // kHead,
+ 2, // kLeftEye,
+ 1, // kLeftEye,
+ 2, // kRightEye,
+ 1, // kRightEye,
+ 2, // kJaw,
+ 1, // kJaw,
+ 2, // kLeftUpperLeg,
+ 1, // kLeftUpperLeg,
+ 0, // kLeftUpperLeg,
+ 2, // kLeftLowerLeg,
+ 0, // kLeftLowerLeg,
+ 2, // kLeftFoot,
+ 1, // kLeftFoot,
+ 2, // kLeftToes,
+ 2, // kRightUpperLeg,
+ 1, // kRightUpperLeg,
+ 0, // kRightUpperLeg,
+ 2, // kRightLowerLeg,
+ 0, // kRightLowerLeg,
+ 2, // kRightFoot,
+ 1, // kRightFoot,
+ 2, // kRightToes,
+ 2, // kLeftShoulder,
+ 1, // kLeftShoulder,
+ 2, // kLeftUpperArm,
+ 1, // kLeftUpperArm,
+ 0, // kLeftUpperArm,
+ 2, // kLeftLowerArm,
+ 0, // kLeftLowerArm,
+ 2, // kLeftHand,
+ 1, // kLeftHand,
+ 2, // kRightShoulder,
+ 1, // kRightShoulder,
+ 2, // kRightUpperArm,
+ 1, // kRightUpperArm,
+ 0, // kRightUpperArm,
+ 2, // kRightLowerArm,
+ 0, // kRightLowerArm,
+ 2, // kRightHand,
+ 1, // kRightHand
+ };
+
+ const static float ATTRIBUTE_ALIGN(ALIGN4F) goalOrientationOffsetArray[kLastGoal][4] = {{0.5f,-0.5f,0.5f,0.5f},{0.5f,-0.5f,0.5f,0.5f},{0.707107f,0,0.707107f,0},{0,0.707107f,0,0.707107f}};
+}
+
+namespace human
+{
+ bool RequiredBone(uint32_t aBoneIndex)
+ {
+ static bool requiredBone[kLastBone] = {
+ true, //kHips
+ true, //kLeftUpperLeg
+ true, //kRightUpperLeg
+ true, //kLeftLowerLeg
+ true, //kRightLowerLeg
+ true, //kLeftFoot
+ true, //kRightFoot
+ true, //kSpine
+ false, //kChest
+ false, //kNeck
+ true, //kHead
+ false, //kLeftShoulder
+ false, //kRightShoulder
+ true, //kLeftUpperArm
+ true, //kRightUpperArm
+ true, //kLeftLowerArm
+ true, //kRightLowerArm
+ true, //kLeftHand
+ true, //kRightHand
+ false, //kLeftToes
+ false, //kRightToes
+ false, //kLeftEye,
+ false, //kRightEye,
+ false //kJaw,
+ };
+
+ return requiredBone[aBoneIndex];
+ }
+
+ const char* BoneName(uint32_t aBoneIndex)
+ {
+ static const char* boneName[kLastBone] = {
+ "Hips",
+ "LeftUpperLeg",
+ "RightUpperLeg",
+ "LeftLowerLeg",
+ "RightLowerLeg",
+ "LeftFoot",
+ "RightFoot",
+ "Spine",
+ "Chest",
+ "Neck",
+ "Head",
+ "LeftShoulder",
+ "RightShoulder",
+ "LeftUpperArm",
+ "RightUpperArm",
+ "LeftLowerArm",
+ "RightLowerArm",
+ "LeftHand",
+ "RightHand",
+ "LeftToes",
+ "RightToes",
+ "LeftEye",
+ "RightEye",
+ "Jaw"
+ };
+
+ return boneName[aBoneIndex];
+ }
+
+ const char* MuscleName(uint32_t aBoneIndex)
+ {
+ static const char* muscleName[human::kLastDoF] = {
+
+ "Spine Front-Back",
+ "Spine Left-Right",
+ "Spine Twist Left-Right",
+ "Chest Front-Back",
+ "Chest Left-Right",
+ "Chest Twist Left-Right",
+
+ "Neck Nod Down-Up",
+ "Neck Tilt Left-Right",
+ "Neck Turn Left-Right",
+ "Head Nod Down-Up",
+ "Head Tilt Left-Right",
+ "Head Turn Left-Right",
+
+ "Left Eye Down-Up",
+ "Left Eye In-Out",
+ "Right Eye Down-Up",
+ "Right Eye In-Out",
+
+ "Jaw Close",
+ "Jaw Left-Right",
+
+ "Left Upper Leg Front-Back",
+ "Left Upper Leg In-Out",
+ "Left Upper Leg Twist In-Out",
+ "Left Lower Leg Stretch",
+ "Left Lower Leg Twist In-Out",
+ "Left Foot Up-Down",
+ "Left Foot Twist In-Out",
+ "Left Toes Up-Down",
+
+ "Right Upper Leg Front-Back",
+ "Right Upper Leg In-Out",
+ "Right Upper Leg Twist In-Out",
+ "Right Lower Leg Stretch",
+ "Right Lower Leg Twist In-Out",
+ "Right Foot Up-Down",
+ "Right Foot Twist In-Out",
+ "Right Toes Up-Down",
+
+ "Left Shoulder Down-Up",
+ "Left Shoulder Front-Back",
+ "Left Arm Down-Up",
+ "Left Arm Front-Back",
+ "Left Arm Twist In-Out",
+ "Left Forearm Stretch",
+ "Left Forearm Twist In-Out",
+ "Left Hand Down-Up",
+ "Left Hand In-Out",
+
+ "Right Shoulder Down-Up",
+ "Right Shoulder Front-Back",
+ "Right Arm Down-Up",
+ "Right Arm Front-Back",
+ "Right Arm Twist In-Out",
+ "Right Forearm Stretch",
+ "Right Forearm Twist In-Out",
+ "Right Hand Down-Up",
+ "Right Hand In-Out"
+ };
+
+ return muscleName[aBoneIndex];
+ }
+
+ bool MaskHasLegs(const HumanPoseMask& mask)
+ {
+ for(int dofIter = 0; dofIter < mecanim::human::kLastLegDoF; dofIter++)
+ {
+ if(!mask.test(mecanim::human::kMaskDoFStartIndex+mecanim::human::kLeftLegDoFStart+dofIter))
+ return false;
+ if(!mask.test(mecanim::human::kMaskDoFStartIndex+mecanim::human::kRightLegDoFStart+dofIter))
+ return false;
+ }
+
+ return true;
+ }
+
+ int32_t MuscleFromBone(int32_t aBoneIndex, int32_t aDoFIndex)
+ {
+ return Bone2DoF[aBoneIndex][2-aDoFIndex];
+ }
+
+ int32_t BoneFromMuscle(int32_t aDoFIndex)
+ {
+ return DoF2Bone[aDoFIndex];
+ }
+
+ HumanPoseMask FullBodyMask()
+ {
+ return HumanPoseMask(~HumanPoseMask::type(0));
+ }
+
+ skeleton::SetupAxesInfo const& GetAxeInfo(uint32_t index)
+ {
+ const static skeleton::SetupAxesInfo setupAxesInfoArray[kLastBone] =
+ {
+ {{0,0,0,1},{-1,0,0,0},{-40,-40,-40},{40,40,40},{1,1,1,1},math::kZYRoll,0}, // kHips,
+ {{-0.268f,0,0,1},{1,0,0,0},{-60,-60,-90},{60,60,50},{1,1,1,1},math::kZYRoll,0}, // kLeftUpperLeg,
+ {{-0.268f,0,0,1},{1,0,0,0},{-60,-60,-90},{60,60,50},{-1,-1,1,1},math::kZYRoll,0}, // kRightUpperLeg,
+ {{0.839f,0,0,1},{1,0,0,0},{-90,0,-80},{90,0,80},{1,1,-1,1},math::kZYRoll,0}, // kLeftLeg,
+ {{0.839f,0,0,1},{1,0,0,0},{-90,0,-80},{90,0,80},{-1,1,-1,1},math::kZYRoll,0}, // kRightLeg,
+ {{0,0,0,1},{1,0,0,0},{0,-30,-50},{0,30,50},{1,1,1,1},math::kZYRoll,-2}, // kLeftFoot,
+ {{0,0,0,1},{1,0,0,0},{0,-30,-50},{0,30,50},{1,-1,1,1},math::kZYRoll,-2}, // kRightFoot,
+ {{0,0,0,1},{-1,0,0,0},{-40,-40,-40},{40,40,40},{1,1,1,1},math::kZYRoll,0}, // kSpine,
+ {{0,0,0,1},{-1,0,0,0},{-40,-40,-40},{40,40,40},{1,1,1,1},math::kZYRoll,0}, // kChest,
+ {{0,0,0,1},{-1,0,0,0},{-40,-40,-40},{40,40,40},{1,1,1,1},math::kZYRoll,0}, // kNeck,
+ {{0,0,0,1},{-1,0,0,0},{-40,-40,-40},{40,40,40},{1,1,1,1},math::kZYRoll,2}, // kHead,
+ {{0,0,0,1},{0,0,1,0},{0,-15,-15},{0,15,30},{1,1,-1,1},math::kZYRoll,0}, // kLeftShoulder,
+ {{0,0,0,1},{0,0,1,0},{0,-15,-15},{0,15,30},{1,1,1,1},math::kZYRoll,0}, // kRightShoulder,
+ {{0,0.268f,0.364f,1},{0,0,1,0},{-90,-100,-60},{90,100,100},{1,1,-1,1},math::kZYRoll,0}, // kLeftArm,
+ {{0,-0.268f,-0.364f,1},{0,0,1,0},{-90,-100,-60},{90,100,100},{-1,1,1,1},math::kZYRoll,0}, // kRightArm,
+ {{0,0.839f,0,1},{0,1,0,0},{-90,0,-80},{90,0,80},{1,1,-1,1},math::kZYRoll,0}, // kLeftForeArm,
+ {{0,-0.839f,0,1},{0,1,0,0},{-90,0,-80},{90,0,80},{-1,1,1,1},math::kZYRoll,0}, // kRightForeArm,
+ {{0,0,0,1},{0,0,1,0},{0,-40,-80},{0,40,80},{1,1,-1,1},math::kZYRoll,0}, // kLeftHand,
+ {{0,0,0,1},{0,0,1,0},{0,-40,-80},{0,40,80},{1,1,1,1},math::kZYRoll,0}, // kRightHand,
+ {{0,0,0,1},{1,0,0,0},{0,0,-50},{0,0,50},{1,1,1,1},math::kZYRoll,3}, // kLeftToes,
+ {{0,0,0,1},{1,0,0,0},{0,0,-50},{0,0,50},{1,1,1,1},math::kZYRoll,3}, // kRightToes,
+ {{0,0,0,1},{1,0,0,0},{0,-20,-10},{0,20,15},{1,1,-1,1},math::kZYRoll,3}, // kLeftEye,
+ {{0,0,0,1},{1,0,0,0},{0,-20,-10},{0,20,15},{1,-1,-1,1},math::kZYRoll,3}, // kRightEye,
+ {{0.09f,0,0,1},{1,0,0,0},{0,-10,-10},{0,10,10},{1,1,-1,1},math::kZYRoll,3} // kJaw,
+ };
+
+ return setupAxesInfoArray[index];
+
+ }
+
+
+ Human::Human() : m_HandlesCount(0),
+ m_HasLeftHand(false),
+ m_HasRightHand(false),
+ m_ColliderCount(0),
+ m_Scale(1),
+ m_RootX(math::xformIdentity()),
+ m_ArmTwist(0.5f),
+ m_ForeArmTwist(0.5f),
+ m_UpperLegTwist(0.5f),
+ m_LegTwist(0.5f),
+ m_ArmStretch(0.05f),
+ m_LegStretch(0.05f),
+ m_FeetSpacing(0.0f)
+ {
+ int32_t i;
+
+ float mass = 0;
+
+ for(i = 0; i < kLastBone; i++)
+ {
+ m_HumanBoneIndex[i] = -1;
+ m_HumanBoneMass[i] = HumanBoneDefaultMass[i];
+ mass += m_HumanBoneMass[i];
+ m_ColliderIndex[i] = -1;
+ }
+
+ for(i = 0; i < kLastBone; i++)
+ {
+ m_HumanBoneMass[i] /= mass;
+ }
+ }
+
+ Human* CreateHuman(skeleton::Skeleton *apSkeleton, skeleton::SkeletonPose *apSkeletonPose, uint32_t aHandlesCount, uint32_t aColliderCount, memory::Allocator& arAlloc)
+ {
+ Human* human = arAlloc.Construct<Human>();
+
+ human->m_Skeleton = apSkeleton;
+ human->m_SkeletonPose = apSkeletonPose;
+ human->m_Handles = arAlloc.ConstructArray<Handle>(aHandlesCount);
+ human->m_HandlesCount = aHandlesCount;
+ human->m_ColliderArray = arAlloc.ConstructArray<math::Collider>(aColliderCount);
+ human->m_ColliderCount = aColliderCount;
+
+ memset(human->m_HumanBoneIndex, -1, sizeof(int32_t)*kLastBone);
+ memset(human->m_ColliderIndex, -1, sizeof(int32_t)*kLastBone);
+
+ human->m_HasLeftHand = false;
+ human->m_HasRightHand = false;
+
+ human->m_Scale = 1;
+
+ return human;
+ }
+
+ void DestroyHuman(Human *apHuman, memory::Allocator& arAlloc)
+ {
+ if(apHuman)
+ {
+ arAlloc.Deallocate(apHuman->m_Handles);
+ arAlloc.Deallocate(apHuman->m_ColliderArray);
+
+ arAlloc.Deallocate(apHuman);
+ }
+ }
+
+ HumanPose::HumanPose()
+ {
+ int32_t i;
+
+ for(i = 0; i < kLastDoF; i++)
+ {
+ m_DoFArray[i] = 0;
+ }
+
+ m_LookAtPosition = math::float4::zero();
+ m_LookAtWeight = math::float4::zero();
+ }
+
+ void HumanAdjustMass(Human *apHuman)
+ {
+ if(apHuman->m_HumanBoneIndex[kNeck] < 0)
+ {
+ apHuman->m_HumanBoneMass[kChest] += apHuman->m_HumanBoneMass[kNeck];
+ apHuman->m_HumanBoneMass[kNeck] = 0;
+ }
+
+ if(apHuman->m_HumanBoneIndex[kLeftShoulder] < 0)
+ {
+ apHuman->m_HumanBoneMass[kChest] += apHuman->m_HumanBoneMass[kLeftShoulder];
+ apHuman->m_HumanBoneMass[kLeftShoulder] = 0;
+ }
+
+ if(apHuman->m_HumanBoneIndex[kRightShoulder] < 0)
+ {
+ apHuman->m_HumanBoneMass[kChest] += apHuman->m_HumanBoneMass[kRightShoulder];
+ apHuman->m_HumanBoneMass[kRightShoulder] = 0;
+ }
+
+ if(apHuman->m_HumanBoneIndex[kChest] < 0)
+ {
+ apHuman->m_HumanBoneMass[kSpine] += apHuman->m_HumanBoneMass[kChest];
+ apHuman->m_HumanBoneMass[kChest] = 0;
+ }
+
+ if(apHuman->m_HumanBoneIndex[kLeftToes] < 0)
+ {
+ apHuman->m_HumanBoneMass[kLeftFoot] += apHuman->m_HumanBoneMass[kLeftToes];
+ apHuman->m_HumanBoneMass[kLeftToes] = 0;
+ }
+
+ if(apHuman->m_HumanBoneIndex[kRightToes] < 0)
+ {
+ apHuman->m_HumanBoneMass[kRightFoot] += apHuman->m_HumanBoneMass[kRightToes];
+ apHuman->m_HumanBoneMass[kRightToes] = 0;
+ }
+ }
+
+ void HumanSetupAxes(Human *apHuman, skeleton::SkeletonPose const *apSkeletonPoseGlobal)
+ {
+ apHuman->m_RootX = math::xformIdentity();
+ apHuman->m_RootX = HumanComputeRootXform(apHuman,apSkeletonPoseGlobal);
+ apHuman->m_Scale = apHuman->m_RootX.t.y().tofloat();
+
+ skeleton::SkeletonPoseComputeLocal(apHuman->m_Skeleton.Get(), apSkeletonPoseGlobal, apHuman->m_SkeletonPose.Get());
+
+ int32_t i;
+
+ for(i = 0; i < kLastBone; i++)
+ {
+ int32_t skBoneIndex = apHuman->m_HumanBoneIndex[i];
+
+ int32_t skAxisBoneId = -1;
+ float len = 1.0f;
+
+ switch(i)
+ {
+ case kLeftEye:
+ case kRightEye:
+ case kJaw:
+ len = 0.1f;
+ break;
+
+ case kHead:
+ if(apHuman->m_HumanBoneIndex[kNeck] >= 0)
+ {
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kNeck];
+ len = -1.0f;
+ }
+ else if(apHuman->m_HumanBoneIndex[kChest] >= 0)
+ {
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kChest];
+ len = -0.5f;
+ }
+ else
+ {
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kSpine];
+ len = -0.25f;
+ }
+ break;
+
+ case kLeftFoot:
+ len = -apSkeletonPoseGlobal->m_X[skBoneIndex].t.y().tofloat();
+ break;
+
+ case kRightFoot:
+ len = -apSkeletonPoseGlobal->m_X[skBoneIndex].t.y().tofloat();
+ break;
+
+ case kLeftHand:
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kLeftLowerArm];
+ len = -0.5f;
+ break;
+
+ case kRightHand:
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kRightLowerArm];
+ len = -0.5f;
+ break;
+
+ case kLeftToes:
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kLeftFoot];
+ len = 0.5f;
+ break;
+
+ case kRightToes:
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kRightFoot];
+ len = 0.5f;
+ break;
+
+ case kHips:
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kSpine];
+ break;
+
+ case kLeftUpperLeg:
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kLeftLowerLeg];
+ break;
+
+ case kRightUpperLeg:
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kRightLowerLeg];
+ break;
+
+ case kLeftLowerLeg:
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kLeftFoot];
+ break;
+
+ case kRightLowerLeg:
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kRightFoot];
+ break;
+
+ case kSpine:
+ if(apHuman->m_HumanBoneIndex[kChest] >= 0)
+ {
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kChest];
+ }
+ else if(apHuman->m_HumanBoneIndex[kNeck] >= 0)
+ {
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kNeck];
+ }
+ else
+ {
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kHead];
+ }
+ break;
+
+ case kChest:
+ if(apHuman->m_HumanBoneIndex[kNeck] >= 0)
+ {
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kNeck];
+ }
+ else
+ {
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kHead];
+ }
+ break;
+
+ case kNeck:
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kHead];
+ break;
+
+ case kLeftShoulder:
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kLeftUpperArm];
+ break;
+
+ case kRightShoulder:
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kRightUpperArm];
+ break;
+
+ case kLeftUpperArm:
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kLeftLowerArm];
+ break;
+
+ case kRightUpperArm:
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kRightLowerArm];
+ break;
+
+ case kLeftLowerArm:
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kLeftHand];
+ break;
+
+ case kRightLowerArm:
+ skAxisBoneId = apHuman->m_HumanBoneIndex[kRightHand];
+ break;
+ };
+
+ if(skBoneIndex >= 0)
+ {
+ skeleton::SetupAxes(apHuman->m_Skeleton.Get(), apSkeletonPoseGlobal, GetAxeInfo(i), skBoneIndex,skAxisBoneId,true,len);
+ }
+ }
+ }
+
+ void HumanSetupCollider(Human *apHuman, skeleton::SkeletonPose const *apSkeletonPoseGlobal)
+ {
+ //float refLen = apSkeletonPoseGlobal->m_X[apHuman->m_HumanBoneIndex[kHead]].t.y();
+
+ float hipsWidth = math::length(apSkeletonPoseGlobal->m_X[apHuman->m_HumanBoneIndex[kLeftUpperLeg]].t - apSkeletonPoseGlobal->m_X[apHuman->m_HumanBoneIndex[kRightUpperLeg]].t).tofloat();
+ float shouldersWidth = math::length(apSkeletonPoseGlobal->m_X[apHuman->m_HumanBoneIndex[kLeftUpperArm]].t - apSkeletonPoseGlobal->m_X[apHuman->m_HumanBoneIndex[kRightUpperArm]].t).tofloat();
+
+ int32_t colliderIndex = 0;
+ int32_t boneIndex;
+
+ for(boneIndex = 0; boneIndex < kLastBone; boneIndex++)
+ {
+ int32_t skIndex = apHuman->m_HumanBoneIndex[boneIndex];
+
+ if(skIndex >= 0)
+ {
+ apHuman->m_ColliderIndex[boneIndex] = colliderIndex;
+
+ math::Axes axes = GetAxes(apHuman,boneIndex);
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.x() = axes.m_Length;
+ apHuman->m_ColliderArray[colliderIndex].m_X.t.x() = math::float1(0.5f) * apHuman->m_ColliderArray[colliderIndex].m_X.s.x();
+
+ if(boneIndex == kHips)
+ {
+ apHuman->m_ColliderArray[colliderIndex].m_Type = math::kCube;
+ //apHuman->m_ColliderArray[colliderIndex].m_X.s.x() *= math::float1(3.0f);
+ apHuman->m_ColliderArray[colliderIndex].m_X.t.x() = math::float1::zero();
+
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.z() = math::float1(1.5f * hipsWidth);
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.y() = math::float1(1.0f * hipsWidth);
+
+ apHuman->m_ColliderArray[colliderIndex].m_XMotionType = math::kIgnored;
+ apHuman->m_ColliderArray[colliderIndex].m_YMotionType = math::kIgnored;
+ apHuman->m_ColliderArray[colliderIndex].m_ZMotionType = math::kIgnored;
+ }
+ else if(boneIndex == kSpine)
+ {
+ apHuman->m_ColliderArray[colliderIndex].m_Type = math::kSphere;
+
+ //apHuman->m_ColliderArray[colliderIndex].m_X.s.x() *= math::float1(1.5f);
+ apHuman->m_ColliderArray[colliderIndex].m_X.t.x() = math::float1(0.5f) * apHuman->m_ColliderArray[colliderIndex].m_X.s.x();
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.z() = math::float1(1.2f * hipsWidth);
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.y() = math::float1(0.8f * hipsWidth);
+
+ apHuman->m_ColliderArray[colliderIndex].m_XMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_YMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_ZMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_MinLimitX = -7.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitX = 7.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitY = 7.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitZ = 7.0f;
+ }
+ else if(boneIndex == kChest)
+ {
+ apHuman->m_ColliderArray[colliderIndex].m_Type = math::kCube;
+
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.z() = math::float1(1.0f * shouldersWidth);
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.y() = math::float1(0.6f * shouldersWidth);
+
+ apHuman->m_ColliderArray[colliderIndex].m_XMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_YMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_ZMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_MinLimitX = -11.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitX = 11.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitY = 11.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitZ = 11.0f;
+ }
+ else if(boneIndex == kNeck)
+ {
+ apHuman->m_ColliderArray[colliderIndex].m_Type = math::kSphere;
+
+ //apHuman->m_ColliderArray[colliderIndex].m_X.s.x() *= math::float1(1.5f);
+ apHuman->m_ColliderArray[colliderIndex].m_X.t.y() = math::float1(0.1f) * apHuman->m_ColliderArray[colliderIndex].m_X.s.x();
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.z() = math::float1(0.33f) * apHuman->m_ColliderArray[colliderIndex].m_X.s.x();
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.y() = math::float1(0.33f) * apHuman->m_ColliderArray[colliderIndex].m_X.s.x();
+
+ apHuman->m_ColliderArray[colliderIndex].m_XMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_YMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_ZMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_MinLimitX = -10.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitX = 10.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitY = 20.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitZ = 5.0f;
+ }
+ else if(boneIndex == kHead)
+ {
+ apHuman->m_ColliderArray[colliderIndex].m_Type = math::kSphere;
+
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.x() = math::float1(0.6f * shouldersWidth);
+ apHuman->m_ColliderArray[colliderIndex].m_X.t.y() = math::float1(0.2f) * apHuman->m_ColliderArray[colliderIndex].m_X.s.x();
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.z() = math::float1(0.4f * shouldersWidth);
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.y() = math::float1(0.45f * shouldersWidth);
+
+ apHuman->m_ColliderArray[colliderIndex].m_XMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_YMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_ZMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_MinLimitX = -5.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitX = 8.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitY = 20.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitZ = 10.0f;
+ }
+ else if(boneIndex == kLeftShoulder || boneIndex == kRightShoulder)
+ {
+ apHuman->m_ColliderArray[colliderIndex].m_Type = math::kNone;
+
+ //apHuman->m_ColliderArray[colliderIndex].m_X.s.x() *= math::float1(1.2f);
+ apHuman->m_ColliderArray[colliderIndex].m_X.t.x() = math::float1(0.5f) * apHuman->m_ColliderArray[colliderIndex].m_X.s.x();
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.z() = math::float1(0.1f * shouldersWidth);
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.y() = math::float1(0.2f * shouldersWidth);
+
+ apHuman->m_ColliderArray[colliderIndex].m_XMotionType = math::kIgnored;
+ apHuman->m_ColliderArray[colliderIndex].m_YMotionType = math::kIgnored;
+ apHuman->m_ColliderArray[colliderIndex].m_ZMotionType = math::kIgnored;
+ }
+ else if(boneIndex == kLeftHand || boneIndex == kRightHand)
+ {
+ apHuman->m_ColliderArray[colliderIndex].m_Type = math::kCube;
+
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.z() = math::float1(0.5f) * apHuman->m_ColliderArray[colliderIndex].m_X.s.x();
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.y() = math::float1(0.2f) * apHuman->m_ColliderArray[colliderIndex].m_X.s.x();
+
+ apHuman->m_ColliderArray[colliderIndex].m_XMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_YMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_ZMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_MinLimitX = -30.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitX = 30.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitY = 40.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitZ = 25.0f;
+ }
+ else if(boneIndex == kLeftFoot || boneIndex == kRightFoot)
+ {
+ apHuman->m_ColliderArray[colliderIndex].m_Type = math::kCube;
+
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.z() = math::float1(0.4f * shouldersWidth);
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.y() = math::float1(0.85f * shouldersWidth);
+ apHuman->m_ColliderArray[colliderIndex].m_X.t.y() = math::float1(-0.25f) * apHuman->m_ColliderArray[colliderIndex].m_X.s.y();
+
+ apHuman->m_ColliderArray[colliderIndex].m_XMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_YMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_ZMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_MinLimitX = -45.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitX = 20.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitY = 30.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitZ = 50.0f;
+ }
+ else if(boneIndex == kLeftToes || boneIndex == kRightToes)
+ {
+ apHuman->m_ColliderArray[colliderIndex].m_Type = math::kNone;
+
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.z() = math::float1(0.4f * shouldersWidth);
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.y() = math::float1(0.2f * shouldersWidth);
+ apHuman->m_ColliderArray[colliderIndex].m_X.t.y() = math::float1(-0.5f) * apHuman->m_ColliderArray[colliderIndex].m_X.s.y();
+
+ apHuman->m_ColliderArray[colliderIndex].m_XMotionType = math::kIgnored;
+ apHuman->m_ColliderArray[colliderIndex].m_YMotionType = math::kIgnored;
+ apHuman->m_ColliderArray[colliderIndex].m_ZMotionType = math::kIgnored;
+ }
+ else if( boneIndex == kLeftUpperArm ||
+ boneIndex == kLeftLowerArm ||
+ boneIndex == kRightUpperArm ||
+ boneIndex == kRightLowerArm)
+ {
+ apHuman->m_ColliderArray[colliderIndex].m_Type = math::kCapsule;
+
+ //apHuman->m_ColliderArray[colliderIndex].m_X.s.x() *= math::float1(1.2f);
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.z() = math::float1(0.25f) * apHuman->m_ColliderArray[colliderIndex].m_X.s.x();
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.y() = math::float1(0.25f) * apHuman->m_ColliderArray[colliderIndex].m_X.s.x();
+
+ if(boneIndex == kLeftUpperArm || boneIndex == kRightUpperArm)
+ {
+ apHuman->m_ColliderArray[colliderIndex].m_XMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_YMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_ZMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_MinLimitX = -100.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitX = 100.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitY = 20.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitZ = 45.0f;
+ }
+ else if (boneIndex == kLeftLowerArm || boneIndex == kRightLowerArm)
+ {
+ apHuman->m_ColliderArray[colliderIndex].m_XMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_YMotionType = math::kLocked;
+ apHuman->m_ColliderArray[colliderIndex].m_ZMotionType = math::kLimited;
+ if (boneIndex == kLeftLowerArm)
+ {
+ apHuman->m_ColliderArray[colliderIndex].m_MinLimitX = -130.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitX = 0.0f;
+ }
+ else if (boneIndex == kRightLowerArm)
+ {
+ apHuman->m_ColliderArray[colliderIndex].m_MinLimitX = 0.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitX = 130.0f;
+ }
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitY = 5.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitZ = 20.0f;
+ }
+ }
+ else if( boneIndex == kLeftLowerLeg ||
+ boneIndex == kLeftUpperLeg ||
+ boneIndex == kRightLowerLeg ||
+ boneIndex == kRightUpperLeg)
+ {
+ apHuman->m_ColliderArray[colliderIndex].m_Type = math::kCapsule;
+
+ //apHuman->m_ColliderArray[colliderIndex].m_X.s.x() *= math::float1(1.2f);
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.z() = math::float1(0.175f) * apHuman->m_ColliderArray[colliderIndex].m_X.s.x();
+ apHuman->m_ColliderArray[colliderIndex].m_X.s.y() = math::float1(0.175f) * apHuman->m_ColliderArray[colliderIndex].m_X.s.x();
+
+ if (boneIndex == kLeftLowerLeg || boneIndex == kRightLowerLeg)
+ {
+ apHuman->m_ColliderArray[colliderIndex].m_XMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_YMotionType = math::kLocked;
+ apHuman->m_ColliderArray[colliderIndex].m_ZMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_MinLimitX = 0.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitX = 130.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitY = 0.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitZ = 10.0f;
+ }
+ else if(boneIndex == kLeftUpperLeg || boneIndex == kRightUpperLeg)
+ {
+ apHuman->m_ColliderArray[colliderIndex].m_XMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_YMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_ZMotionType = math::kLimited;
+ apHuman->m_ColliderArray[colliderIndex].m_MinLimitX = -70.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitX = 10.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitY = 45.0f;
+ apHuman->m_ColliderArray[colliderIndex].m_MaxLimitZ = 60.0f;
+ }
+ }
+ else
+ {
+ apHuman->m_ColliderArray[colliderIndex].m_Type = math::kNone;
+ apHuman->m_ColliderArray[colliderIndex].m_XMotionType = math::kIgnored;
+ apHuman->m_ColliderArray[colliderIndex].m_YMotionType = math::kIgnored;
+ apHuman->m_ColliderArray[colliderIndex].m_ZMotionType = math::kIgnored;
+ }
+
+ colliderIndex++;
+ }
+ }
+ }
+
+ void HumanCopyAxes(Human const *apSrcHuman, Human *apHuman)
+ {
+ int32_t i;
+
+ for(i = 0; i < kLastBone; i++)
+ {
+ skeleton::Node const * srcNode = apSrcHuman->m_HumanBoneIndex[i] >= 0 ? &apSrcHuman->m_Skeleton->m_Node[apSrcHuman->m_HumanBoneIndex[i]] : 0;
+ skeleton::Node const * node = apHuman->m_HumanBoneIndex[i] >= 0 ? &apHuman->m_Skeleton->m_Node[apHuman->m_HumanBoneIndex[i]] : 0;
+
+ if(srcNode != 0 && node != 0 && srcNode->m_AxesId != -1 && node->m_AxesId != -1)
+ {
+ apHuman->m_Skeleton->m_AxesArray[node->m_AxesId] = apSrcHuman->m_Skeleton->m_AxesArray[srcNode->m_AxesId];
+ }
+ }
+ }
+
+ math::Axes GetAxes(Human const *apHuman, int32_t aBoneIndex)
+ {
+ math::Axes ret;
+
+ int32_t skIndex = apHuman->m_HumanBoneIndex[aBoneIndex];
+
+ if(skIndex >= 0)
+ {
+ int32_t axesIndex = apHuman->m_Skeleton->m_Node[skIndex].m_AxesId;
+
+ if(axesIndex >= 0)
+ {
+ ret = apHuman->m_Skeleton->m_AxesArray[axesIndex];
+ }
+ }
+
+ return ret;
+ }
+
+ void GetMuscleRange(Human const *apHuman, int32_t aDoFIndex, float &aMin, float &aMax)
+ {
+ math::Axes axes = GetAxes(apHuman,DoF2Bone[aDoFIndex]);
+
+ switch(DoF2BoneDoFIndex[aDoFIndex])
+ {
+ case 0: aMin = axes.m_Limit.m_Min.x().tofloat(); aMax = axes.m_Limit.m_Max.x().tofloat(); break;
+ case 1: aMin = axes.m_Limit.m_Min.y().tofloat(); aMax = axes.m_Limit.m_Max.y().tofloat(); break;
+ case 2: aMin = axes.m_Limit.m_Min.z().tofloat(); aMax = axes.m_Limit.m_Max.z().tofloat(); break;
+ }
+ }
+
+ math::float4 AddAxis(Human const *apHuman, int32_t aIndex, math::float4 const &arQ)
+ {
+ math::Axes cAxes = apHuman->m_Skeleton->m_AxesArray[apHuman->m_Skeleton->m_Node[aIndex].m_AxesId];
+ return math::normalize(math::quatMul(arQ,cAxes.m_PostQ));
+ }
+
+ math::float4 RemoveAxis(Human const *apHuman, int32_t aIndex, const math::float4 &arQ)
+ {
+ math::Axes cAxes = apHuman->m_Skeleton->m_AxesArray[apHuman->m_Skeleton->m_Node[aIndex].m_AxesId];
+ return math::normalize(math::quatMul(arQ,math::quatConj(cAxes.m_PostQ)));
+ }
+
+ math::xform NormalizedHandleX(Human const *apHuman, int32_t aHandleIndex)
+ {
+ int32_t pIndex = apHuman->m_HumanBoneIndex[apHuman->m_Handles[aHandleIndex].m_ParentHumanIndex];
+
+ math::xform px = apHuman->m_SkeletonPose->m_X[pIndex];
+ math::xform hx = math::xformMul(px,apHuman->m_Handles[aHandleIndex].m_X);
+
+ px.q = AddAxis(apHuman,pIndex,px.q);
+ px.s = math::float4::one();
+
+ return math::xformInvMul(px,hx);
+ }
+
+ void HumanPoseAdjustForMissingBones(Human const *apHuman, HumanPose *apHumanPose)
+ {
+ if(apHuman->m_HumanBoneIndex[kNeck] < 0)
+ {
+ apHumanPose->m_DoFArray[kHeadDoFStart+kHeadFrontBack] += apHumanPose->m_DoFArray[kHeadDoFStart+kNeckFrontBack];
+ apHumanPose->m_DoFArray[kHeadDoFStart+kNeckFrontBack] = 0;
+
+ apHumanPose->m_DoFArray[kHeadDoFStart+kHeadLeftRight] += apHumanPose->m_DoFArray[kHeadDoFStart+kNeckLeftRight];
+ apHumanPose->m_DoFArray[kHeadDoFStart+kNeckLeftRight] = 0;
+
+ apHumanPose->m_DoFArray[kHeadDoFStart+kHeadRollLeftRight] += apHumanPose->m_DoFArray[kHeadDoFStart+kNeckRollLeftRight];
+ apHumanPose->m_DoFArray[kHeadDoFStart+kNeckRollLeftRight] = 0;
+ }
+
+ if(apHuman->m_HumanBoneIndex[kChest] < 0)
+ {
+ apHumanPose->m_DoFArray[kBodyDoFStart+kSpineFrontBack] += apHumanPose->m_DoFArray[kBodyDoFStart+kChestFrontBack];
+ apHumanPose->m_DoFArray[kBodyDoFStart+kChestFrontBack] = 0;
+
+ apHumanPose->m_DoFArray[kBodyDoFStart+kSpineLeftRight] += apHumanPose->m_DoFArray[kBodyDoFStart+kChestLeftRight];
+ apHumanPose->m_DoFArray[kBodyDoFStart+kChestLeftRight] = 0;
+
+ apHumanPose->m_DoFArray[kBodyDoFStart+kSpineRollLeftRight] += apHumanPose->m_DoFArray[kBodyDoFStart+kChestRollLeftRight];
+ apHumanPose->m_DoFArray[kBodyDoFStart+kChestRollLeftRight] = 0;
+ }
+
+ if(apHuman->m_HumanBoneIndex[kLeftShoulder] < 0)
+ {
+ apHumanPose->m_DoFArray[kLeftArmDoFStart+kArmDownUp] += (30.0f/200.0f) * apHumanPose->m_DoFArray[kLeftArmDoFStart+kShoulderDownUp];
+ apHumanPose->m_DoFArray[kLeftArmDoFStart+kShoulderDownUp] = 0;
+
+ apHumanPose->m_DoFArray[kLeftArmDoFStart+kArmFrontBack] += (45.0f/160.0f) * apHumanPose->m_DoFArray[kLeftArmDoFStart+kShoulderFrontBack];
+ apHumanPose->m_DoFArray[kLeftArmDoFStart+kShoulderFrontBack] = 0;
+ }
+
+ if(apHuman->m_HumanBoneIndex[kRightShoulder] < 0)
+ {
+ apHumanPose->m_DoFArray[kRightArmDoFStart+kArmDownUp] += (30.0f/200.0f) * apHumanPose->m_DoFArray[kRightArmDoFStart+kShoulderDownUp];
+ apHumanPose->m_DoFArray[kRightArmDoFStart+kShoulderDownUp] = 0;
+
+ apHumanPose->m_DoFArray[kRightArmDoFStart+kArmFrontBack] += (45.0f/160.0f) * apHumanPose->m_DoFArray[kRightArmDoFStart+kShoulderFrontBack];
+ apHumanPose->m_DoFArray[kRightArmDoFStart+kShoulderFrontBack] = 0;
+ }
+ }
+
+ void Human2SkeletonPose(Human const *apHuman, HumanPose const *apHumanPose, skeleton::SkeletonPose *apSkeletonPose, int32_t i)
+ {
+ if(apHuman->m_HumanBoneIndex[i] != -1)
+ {
+ math::float4 xyz = math::cond( math::bool4(Bone2DoF[i][2] != -1,Bone2DoF[i][1] != -1,Bone2DoF[i][0] != -1,false),
+ math::float4(apHumanPose->m_DoFArray[Bone2DoF[i][2]],apHumanPose->m_DoFArray[Bone2DoF[i][1]],apHumanPose->m_DoFArray[Bone2DoF[i][0]],0),
+ math::float4::zero());
+
+ skeleton::SkeletonSetDoF(apHuman->m_Skeleton.Get(),apSkeletonPose,xyz,apHuman->m_HumanBoneIndex[i]);
+ }
+ }
+
+ void Human2SkeletonPose(Human const *apHuman, HumanPose const *apHumanPose, skeleton::SkeletonPose *apSkeletonPose)
+ {
+ int32_t i;
+ for(i = 1; i < kLastBone; i++)
+ {
+ Human2SkeletonPose(apHuman,apHumanPose,apSkeletonPose,i);
+ }
+
+ if(apHuman->m_HasLeftHand)
+ {
+ hand::Hand2SkeletonPose(apHuman->m_LeftHand.Get(),apHuman->m_Skeleton.Get(),&apHumanPose->m_LeftHandPose,apSkeletonPose);
+ }
+
+ if(apHuman->m_HasRightHand)
+ {
+ hand::Hand2SkeletonPose(apHuman->m_RightHand.Get(),apHuman->m_Skeleton.Get(),&apHumanPose->m_RightHandPose,apSkeletonPose);
+ }
+ }
+
+ void Skeleton2HumanPose(Human const *apHuman, skeleton::SkeletonPose const *apSkeletonPose, HumanPose *apHumanPose, int32_t i)
+ {
+ if(apHuman->m_HumanBoneIndex[i] != -1)
+ {
+ const math::float4 xyz = skeleton::SkeletonGetDoF(apHuman->m_Skeleton.Get(),apSkeletonPose,apHuman->m_HumanBoneIndex[i]);
+
+ if(Bone2DoF[i][2] != -1) apHumanPose->m_DoFArray[Bone2DoF[i][2]] = xyz.x().tofloat();
+ if(Bone2DoF[i][1] != -1) apHumanPose->m_DoFArray[Bone2DoF[i][1]] = xyz.y().tofloat();
+ if(Bone2DoF[i][0] != -1) apHumanPose->m_DoFArray[Bone2DoF[i][0]] = xyz.z().tofloat();
+ }
+ }
+
+ void Skeleton2HumanPose(Human const *apHuman, skeleton::SkeletonPose const *apSkeletonPose, HumanPose *apHumanPose)
+ {
+ int32_t i;
+
+ for(i = 1; i < kLastBone; i++)
+ {
+ Skeleton2HumanPose(apHuman,apSkeletonPose,apHumanPose,i);
+ }
+
+ if(apHuman->m_HasLeftHand)
+ {
+ hand::Skeleton2HandPose(apHuman->m_LeftHand.Get(),apHuman->m_Skeleton.Get(),apSkeletonPose,&apHumanPose->m_LeftHandPose);
+ }
+
+ if(apHuman->m_HasRightHand)
+ {
+ hand::Skeleton2HandPose(apHuman->m_RightHand.Get(),apHuman->m_Skeleton.Get(),apSkeletonPose,&apHumanPose->m_RightHandPose);
+ }
+ }
+
+ math::float4 HumanComputeBoneMassCenter(Human const *apHuman, skeleton::SkeletonPose const *apSkeletonPose,int32_t aBoneIndex)
+ {
+ math::float4 ret( math::float4::zero() );
+
+ switch(aBoneIndex)
+ {
+ case kHips:
+ ret = math::float1(1.0f/3.0f) * (apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kLeftUpperLeg]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kRightUpperLeg]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kSpine]].t);
+ break;
+
+ case kSpine:
+ if(apHuman->m_HumanBoneIndex[kChest] >= 0)
+ {
+ ret = math::float1(0.5f) * (apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kSpine]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kChest]].t);
+ }
+ else
+ {
+ ret = math::float1(0.1f) * apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kSpine]].t + math::float1(0.9f * 0.5f) * (apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kLeftUpperArm]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kRightUpperArm]].t);
+ }
+ break;
+
+ case kChest:
+ if(apHuman->m_HumanBoneIndex[kNeck] >= 0 && apHuman->m_HumanBoneIndex[kLeftShoulder] >= 0 && apHuman->m_HumanBoneIndex[kRightShoulder] >= 0)
+ {
+ ret = math::float1(0.25f) * (apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kChest]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kNeck]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kLeftShoulder]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kRightShoulder]].t);
+ }
+ else
+ {
+ ret = math::float1(1.0f/3.0f) * (apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kChest]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kLeftUpperArm]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kRightUpperArm]].t);
+ }
+ break;
+
+ case kNeck:
+ ret = math::float1(0.5f) * (apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kNeck]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kHead]].t);
+ break;
+
+ case kLeftUpperLeg:
+ ret = math::float1(0.5f) * (apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kLeftUpperLeg]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kLeftLowerLeg]].t);
+ break;
+
+ case kLeftLowerLeg:
+ ret = math::float1(0.5f) * (apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kLeftLowerLeg]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kLeftFoot]].t);
+ break;
+
+ case kLeftShoulder:
+ ret = math::float1(0.5f) * (apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kLeftShoulder]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kLeftUpperArm]].t);
+ break;
+
+ case kLeftUpperArm:
+ ret = math::float1(0.5f) * (apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kLeftUpperArm]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kLeftLowerArm]].t);
+ break;
+
+ case kLeftLowerArm:
+ ret = math::float1(0.5f) * (apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kLeftUpperArm]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kLeftHand]].t);
+ break;
+
+ case kRightUpperLeg:
+ ret = math::float1(0.5f) * (apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kRightUpperLeg]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kRightLowerLeg]].t);
+ break;
+
+ case kRightLowerLeg:
+ ret = math::float1(0.5f) * (apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kRightLowerLeg]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kRightFoot]].t);
+ break;
+
+ case kRightShoulder:
+ ret = math::float1(0.5f) * (apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kRightShoulder]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kRightUpperArm]].t);
+ break;
+
+ case kRightUpperArm:
+ ret = math::float1(0.5f) * (apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kRightUpperArm]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kRightLowerArm]].t);
+ break;
+
+ case kRightLowerArm:
+ ret = math::float1(0.5f) * (apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kRightLowerArm]].t + apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[kRightHand]].t);
+ break;
+
+ default:
+ ret = apSkeletonPose->m_X[apHuman->m_HumanBoneIndex[aBoneIndex]].t;
+ break;
+ };
+
+ return ret;
+ }
+
+ math::float4 HumanComputeMassCenter(Human const *apHuman, skeleton::SkeletonPose const *apSkeletonPoseGlobal)
+ {
+ math::float4 ret(math::float4::zero());
+
+ int32_t i;
+
+ float mass = 0;
+
+ for(i = 0; i < kLastBone; i++)
+ {
+ int32_t index = apHuman->m_HumanBoneIndex[i];
+
+ if(index >=0)
+ {
+ float boneMass = apHuman->m_HumanBoneMass[i];
+ ret += HumanComputeBoneMassCenter(apHuman,apSkeletonPoseGlobal,i) * math::float1(boneMass);
+ mass += boneMass;
+ }
+ }
+
+ return ret / math::float1(mass);
+ }
+
+ float HumanComputeMomentumOfInertia(Human const *apHuman, skeleton::SkeletonPose const *apSkeletonPoseGlobal)
+ {
+ float ret = 0;
+
+ math::float4 mc = HumanComputeMassCenter(apHuman,apSkeletonPoseGlobal);
+
+ int32_t i;
+
+ for(i = 0; i < kLastBone; i++)
+ {
+ int32_t index = apHuman->m_HumanBoneIndex[i];
+
+ if(index >= 0)
+ {
+ float r = math::length(HumanComputeBoneMassCenter(apHuman,apSkeletonPoseGlobal,index) - mc).tofloat();
+ ret += apHuman->m_HumanBoneMass[i] * r * r;
+ }
+ }
+
+ return ret;
+
+ }
+
+ math::float4 HumanComputeOrientation(Human const* apHuman,skeleton::SkeletonPose const* apPoseGlobal)
+ {
+ int32_t llIndex = apHuman->m_HumanBoneIndex[kLeftUpperLeg];
+ int32_t rlIndex = apHuman->m_HumanBoneIndex[kRightUpperLeg];
+
+ int32_t laIndex = apHuman->m_HumanBoneIndex[kLeftUpperArm];
+ int32_t raIndex = apHuman->m_HumanBoneIndex[kRightUpperArm];
+
+ math::float4 legMC = math::float1(0.5f) * (apPoseGlobal->m_X[llIndex].t + apPoseGlobal->m_X[rlIndex].t);
+ math::float4 armMC = math::float1(0.5f) * (apPoseGlobal->m_X[laIndex].t + apPoseGlobal->m_X[raIndex].t);
+
+ math::float4 upV = math::normalize(armMC-legMC);
+
+ math::float4 legV = apPoseGlobal->m_X[rlIndex].t - apPoseGlobal->m_X[llIndex].t;
+ math::float4 armV = apPoseGlobal->m_X[raIndex].t - apPoseGlobal->m_X[laIndex].t;
+
+ math::float4 rightV = math::normalize(legV+armV);
+ math::float4 frontV = math::cross(rightV, upV);
+
+ rightV = math::cross(upV,frontV);
+
+ return math::normalize(math::quatMul(math::quatMatrixToQuat(rightV,upV,frontV),math::quatConj(apHuman->m_RootX.q)));
+ }
+
+ math::xform HumanComputeRootXform(Human const* apHuman,skeleton::SkeletonPose const* apPoseGlobal)
+ {
+ return math::xform(HumanComputeMassCenter(apHuman,apPoseGlobal),HumanComputeOrientation(apHuman,apPoseGlobal),math::float4(1.0f));
+ }
+
+ float HumanGetFootHeight(Human const* apHuman, bool aLeft)
+ {
+ return apHuman->m_Skeleton->m_AxesArray[apHuman->m_Skeleton->m_Node[apHuman->m_HumanBoneIndex[aLeft ? kLeftFoot : kRightFoot]].m_AxesId].m_Length;
+ }
+
+ math::float4 HumanGetFootBottom(Human const* apHuman, bool aLeft)
+ {
+ return math::float4(HumanGetFootHeight(apHuman,aLeft),0,0,0);
+ }
+
+ math::xform HumanGetColliderXform(Human const* apHuman, math::xform const& x, int32_t aBoneIndex)
+ {
+ math::xform ret;
+
+ int32_t skIndex = apHuman->m_HumanBoneIndex[aBoneIndex];
+
+ if(skIndex >= 0)
+ {
+ int32_t axesIndex = apHuman->m_Skeleton->m_Node[skIndex].m_AxesId;
+ int32_t colliderIndex = apHuman->m_ColliderIndex[aBoneIndex];
+
+ if(axesIndex >= 0 && colliderIndex >= 0)
+ {
+ ret = x;
+
+ ret.q = math::normalize(math::quatMul(ret.q,apHuman->m_Skeleton->m_AxesArray[axesIndex].m_PostQ));
+
+ ret = math::xformMul(ret,apHuman->m_ColliderArray[colliderIndex].m_X);
+
+ //ret.q = math::normalize(math::quatMul(ret.q,math::float4(0,1,0,1))); // to math physX axis setup
+ }
+ }
+
+ return ret;
+ }
+
+ math::xform HumanSubColliderXform(Human const* apHuman, math::xform const& x, int32_t aBoneIndex)
+ {
+ math::xform ret;
+
+ int32_t skIndex = apHuman->m_HumanBoneIndex[aBoneIndex];
+
+ if(skIndex >= 0)
+ {
+ int32_t axesIndex = apHuman->m_Skeleton->m_Node[skIndex].m_AxesId;
+ int32_t colliderIndex = apHuman->m_ColliderIndex[aBoneIndex];
+
+ if(axesIndex >= 0 && colliderIndex >= 0)
+ {
+ ret = x;
+
+ ret = math::xformMulInv(ret,apHuman->m_ColliderArray[colliderIndex].m_X);
+
+ ret.q = math::normalize(math::quatMul(ret.q, math::quatConj(apHuman->m_Skeleton->m_AxesArray[axesIndex].m_PostQ)));
+ }
+ }
+
+ return ret;
+ }
+
+ math::float4 HumanGetGoalOrientationOffset(Goal goalIndex)
+ {
+ return math::load(goalOrientationOffsetArray[goalIndex]);
+ }
+
+ void HumanPoseClear(HumanPose& arPose)
+ {
+ uint32_t i;
+
+ arPose.m_RootX = math::xformIdentity();
+
+ for(i = 0; i < kLastGoal; i++)
+ {
+ arPose.m_GoalArray[i].m_X = math::xformIdentity();
+ }
+
+ for(i = 0; i < kLastDoF; i++)
+ {
+ arPose.m_DoFArray[i] = 0;
+ }
+
+ for(i = 0; i < hand::s_DoFCount; i++)
+ {
+ arPose.m_LeftHandPose.m_DoFArray[i] = 0;
+ arPose.m_RightHandPose.m_DoFArray[i] = 0;
+ }
+ }
+
+ void HumanPoseCopy(HumanPose &arPose,HumanPose const &arPoseA, bool aDoFOnly)
+ {
+ uint32_t i;
+
+ if(!aDoFOnly)
+ {
+ arPose.m_RootX = arPoseA.m_RootX;
+
+ for(i = 0; i < kLastGoal; i++)
+ {
+ arPose.m_GoalArray[i].m_X = arPoseA.m_GoalArray[i].m_X;
+ }
+ }
+
+ for(i = 0; i < kLastDoF; i++)
+ {
+ arPose.m_DoFArray[i] = arPoseA.m_DoFArray[i];
+ }
+
+ hand::HandPoseCopy(&arPoseA.m_LeftHandPose,&arPose.m_LeftHandPose);
+ hand::HandPoseCopy(&arPoseA.m_RightHandPose,&arPose.m_RightHandPose);
+ }
+
+ void HumanPoseCopy(HumanPose &arPose,HumanPose const &arPoseA, HumanPoseMask const &arHumanPoseMask)
+ {
+ if( arHumanPoseMask == FullBodyMask())
+ {
+ HumanPoseCopy(arPose,arPoseA);
+ }
+ else
+ {
+ int32_t i;
+ for(i = 0; i < kLastDoF; i++)
+ {
+ if(arHumanPoseMask.test(kMaskDoFStartIndex+i))
+ {
+ arPose.m_DoFArray[i] = arPoseA.m_DoFArray[i];
+ }
+ else
+ {
+ arPose.m_DoFArray[i] = 0;
+ }
+ }
+
+ if(arHumanPoseMask.test(kMaskLeftHand))
+ {
+ for(i = 0; i < hand::s_DoFCount; i++)
+ {
+ arPose.m_LeftHandPose.m_DoFArray[i] = arPoseA.m_LeftHandPose.m_DoFArray[i];
+ }
+ }
+ else
+ {
+ for(i = 0; i < hand::s_DoFCount; i++)
+ {
+ arPose.m_LeftHandPose.m_DoFArray[i] = 0;
+ }
+ }
+
+ if(arHumanPoseMask.test(kMaskRightHand))
+ {
+ for(i = 0; i < hand::s_DoFCount; i++)
+ {
+ arPose.m_RightHandPose.m_DoFArray[i] = arPoseA.m_RightHandPose.m_DoFArray[i];
+ }
+ }
+ else
+ {
+ for(i = 0; i < hand::s_DoFCount; i++)
+ {
+ arPose.m_RightHandPose.m_DoFArray[i] = 0;
+ }
+ }
+
+ for(i = 0; i < kLastGoal; i++)
+ {
+ if(arHumanPoseMask.test(kMaskGoalStartIndex+i))
+ {
+ arPose.m_GoalArray[i].m_X = arPoseA.m_GoalArray[i].m_X;
+ }
+ else
+ {
+ arPose.m_GoalArray[i].m_X = math::xformIdentity();
+ }
+ }
+
+ if(arHumanPoseMask.test(0))
+ {
+ arPose.m_RootX = arPoseA.m_RootX;
+ }
+ else
+ {
+ arPose.m_RootX = math::xformIdentity();
+ }
+
+ }
+ }
+
+ void HumanPoseAdd(HumanPose &arPose,HumanPose const &arPoseA,HumanPose const &arPoseB)
+ {
+ uint32_t i;
+
+ for(i = 0; i < kLastGoal; i++)
+ {
+ arPose.m_GoalArray[i].m_X = math::xformMul(arPoseA.m_GoalArray[i].m_X,arPoseB.m_GoalArray[i].m_X);
+ }
+
+ for(i = 0; i < kLastDoF; i++)
+ {
+ arPose.m_DoFArray[i] = arPoseA.m_DoFArray[i] + arPoseB.m_DoFArray[i];
+ }
+
+ for(i = 0; i < hand::s_DoFCount; i++)
+ {
+ arPose.m_LeftHandPose.m_DoFArray[i] = arPoseA.m_LeftHandPose.m_DoFArray[i] + arPoseB.m_LeftHandPose.m_DoFArray[i];
+ arPose.m_RightHandPose.m_DoFArray[i] = arPoseA.m_RightHandPose.m_DoFArray[i] + arPoseB.m_RightHandPose.m_DoFArray[i];
+ }
+
+ arPose.m_RootX = math::xformMul(arPoseA.m_RootX,arPoseB.m_RootX);
+ }
+
+ void HumanPoseSub(HumanPose &arPose,HumanPose const &arPoseA,HumanPose const &arPoseB)
+ {
+ uint32_t i;
+
+ for(i = 0; i < kLastGoal; i++)
+ {
+ arPose.m_GoalArray[i].m_X = math::xformInvMulNS(arPoseB.m_GoalArray[i].m_X,arPoseA.m_GoalArray[i].m_X);
+ }
+
+ for(i = 0; i < kLastDoF; i++)
+ {
+ arPose.m_DoFArray[i] = arPoseA.m_DoFArray[i] - arPoseB.m_DoFArray[i];
+ }
+
+ for(i = 0; i < hand::s_DoFCount; i++)
+ {
+ arPose.m_LeftHandPose.m_DoFArray[i] = arPoseA.m_LeftHandPose.m_DoFArray[i] - arPoseB.m_LeftHandPose.m_DoFArray[i];
+ arPose.m_RightHandPose.m_DoFArray[i] = arPoseA.m_RightHandPose.m_DoFArray[i] - arPoseB.m_RightHandPose.m_DoFArray[i];
+ }
+
+ arPose.m_RootX = math::xformInvMulNS(arPoseB.m_RootX,arPoseA.m_RootX);
+ }
+
+ void HumanPoseWeight(HumanPose &arPose,HumanPose const &arPoseA, float aWeight)
+ {
+ uint32_t i;
+
+ math::float1 w(aWeight);
+
+ for(i = 0; i < kLastGoal; i++)
+ {
+ arPose.m_GoalArray[i].m_X = math::xformWeight(arPoseA.m_GoalArray[i].m_X,w);
+ }
+
+ for(i = 0; i < kLastDoF; i++)
+ {
+ arPose.m_DoFArray[i] = arPoseA.m_DoFArray[i] * aWeight;
+ }
+
+ for(i = 0; i < hand::s_DoFCount; i++)
+ {
+ arPose.m_LeftHandPose.m_DoFArray[i] = arPoseA.m_LeftHandPose.m_DoFArray[i] * aWeight;
+ arPose.m_RightHandPose.m_DoFArray[i] = arPoseA.m_RightHandPose.m_DoFArray[i] * aWeight;
+ }
+
+ arPose.m_RootX = math::xformWeight(arPoseA.m_RootX,w);
+ }
+
+ void HumanPoseMirror(HumanPose &arPose,HumanPose const &arPoseA)
+ {
+ uint32_t i;
+
+ for(i = 0; i < kLastBodyDoF; i++)
+ {
+ arPose.m_DoFArray[kBodyDoFStart + i] *= BodyDoFMirror[i];
+ }
+
+ for(i = 0; i < kLastHeadDoF; i++)
+ {
+ arPose.m_DoFArray[kHeadDoFStart + i] *= HeadDoFMirror[i];
+ // bobtodo
+ }
+
+ for(i = 0; i < kLastArmDoF; i++)
+ {
+ float dof = arPose.m_DoFArray[kLeftArmDoFStart + i];
+ arPose.m_DoFArray[kLeftArmDoFStart + i] = arPose.m_DoFArray[kRightArmDoFStart + i];
+ arPose.m_DoFArray[kRightArmDoFStart + i] = dof;
+ }
+
+ for(i = 0; i < kLastLegDoF; i++)
+ {
+ float dof = arPose.m_DoFArray[kLeftLegDoFStart + i];
+ arPose.m_DoFArray[kLeftLegDoFStart + i] = arPose.m_DoFArray[kRightLegDoFStart + i];
+ arPose.m_DoFArray[kRightLegDoFStart + i] = dof;
+ }
+
+ math::xform x = arPose.m_GoalArray[kLeftFootGoal].m_X;
+ arPose.m_GoalArray[kLeftFootGoal].m_X = arPose.m_GoalArray[kRightFootGoal].m_X;
+ arPose.m_GoalArray[kRightFootGoal].m_X = x;
+
+ x = arPose.m_GoalArray[kLeftHandGoal].m_X;
+ arPose.m_GoalArray[kLeftHandGoal].m_X = arPose.m_GoalArray[kRightHandGoal].m_X;
+ arPose.m_GoalArray[kRightHandGoal].m_X = x;
+
+ constant_float4(offsetQY,0,1,0,0);
+ constant_float4(offsetQZ,0,0,1,0);
+
+ for(i = 0; i < kLastGoal; i++)
+ {
+ arPose.m_GoalArray[i].m_X = math::mirror(arPose.m_GoalArray[i].m_X);
+ }
+
+ arPose.m_GoalArray[kLeftFootGoal].m_X.q = math::normalize(math::quatMul(arPose.m_GoalArray[kLeftFootGoal].m_X.q,offsetQY));
+ arPose.m_GoalArray[kRightFootGoal].m_X.q = math::normalize(math::quatMul(arPose.m_GoalArray[kRightFootGoal].m_X.q,offsetQY));
+ arPose.m_GoalArray[kLeftHandGoal].m_X.q = math::normalize(math::quatMul(arPose.m_GoalArray[kLeftHandGoal].m_X.q,offsetQZ));
+ arPose.m_GoalArray[kRightHandGoal].m_X.q = math::normalize(math::quatMul(arPose.m_GoalArray[kRightHandGoal].m_X.q,offsetQZ));
+
+ arPose.m_RootX = math::mirror(arPose.m_RootX);
+
+ for(i = 0; i < hand::s_DoFCount; i++)
+ {
+ float leftdof = arPose.m_LeftHandPose.m_DoFArray[i];
+ arPose.m_LeftHandPose.m_DoFArray[i] = arPose.m_RightHandPose.m_DoFArray[i];
+ arPose.m_RightHandPose.m_DoFArray[i] = leftdof;
+ }
+ }
+
+ void HumanPoseBlend(HumanPose &arPose,HumanPose **apPoseArray, float *apWeightArray, uint32_t aCount)
+ {
+ uint32_t poseIter,i;
+
+ for(i = 0; i < kLastDoF; i++)
+ {
+ arPose.m_DoFArray[i] = 0;
+ }
+
+ for(i = 0; i < hand::s_DoFCount; i++)
+ {
+ arPose.m_LeftHandPose.m_DoFArray[i] = 0;
+ arPose.m_RightHandPose.m_DoFArray[i] = 0;
+ }
+
+ for(i = 0; i < kLastGoal; i++)
+ {
+ arPose.m_GoalArray[i].m_X.t = math::float4::zero();
+ arPose.m_GoalArray[i].m_X.q = math::float4::zero();
+ arPose.m_GoalArray[i].m_X.s = math::float4::one();
+ }
+
+ arPose.m_RootX.t = math::float4::zero();
+ arPose.m_RootX.q = math::float4::zero();
+ arPose.m_RootX.s = math::float4::one();
+
+ float sumW = 0;
+
+ for(poseIter = 0; poseIter < aCount; poseIter++)
+ {
+ float w = apWeightArray[poseIter];
+ math::float1 w1(w);
+
+ sumW += w;
+
+ for(i = 0; i < kLastDoF; i++)
+ {
+ arPose.m_DoFArray[i] += apPoseArray[poseIter]->m_DoFArray[i]*w;
+ }
+
+ for(i = 0; i < hand::s_DoFCount; i++)
+ {
+ arPose.m_LeftHandPose.m_DoFArray[i] += apPoseArray[poseIter]->m_LeftHandPose.m_DoFArray[i] * w;
+ arPose.m_RightHandPose.m_DoFArray[i] += apPoseArray[poseIter]->m_RightHandPose.m_DoFArray[i] * w;
+ }
+
+ for(i = 0; i < kLastGoal; i++)
+ {
+ arPose.m_GoalArray[i].m_X.t += apPoseArray[poseIter]->m_GoalArray[i].m_X.t*w1;
+ arPose.m_GoalArray[i].m_X.q += math::cond(math::dot(arPose.m_GoalArray[i].m_X.q,apPoseArray[poseIter]->m_GoalArray[i].m_X.q) < math::float1::zero(),apPoseArray[poseIter]->m_GoalArray[i].m_X.q * -w1,apPoseArray[poseIter]->m_GoalArray[i].m_X.q * w1);
+ arPose.m_GoalArray[i].m_X.s *= scaleWeight(apPoseArray[poseIter]->m_GoalArray[i].m_X.s,w1);
+ }
+
+ arPose.m_RootX.t += apPoseArray[poseIter]->m_RootX.t*w1;
+ arPose.m_RootX.q += math::cond(math::dot(arPose.m_RootX.q,apPoseArray[poseIter]->m_RootX.q) < math::float1::zero(),apPoseArray[poseIter]->m_RootX.q * -w1,apPoseArray[poseIter]->m_RootX.q * w1);
+ arPose.m_RootX.s *= scaleWeight(apPoseArray[poseIter]->m_RootX.s,w1);
+ }
+
+ math::float4 q(0,0,0,math::saturate(1.0f-sumW));
+
+ for(i = 0; i < kLastGoal; i++)
+ {
+ arPose.m_GoalArray[i].m_X.q = math::normalize(arPose.m_GoalArray[i].m_X.q+q);
+ }
+
+ arPose.m_RootX.q = math::normalize(arPose.m_RootX.q+q);
+
+ }
+
+ void HumanPoseAddOverrideLayer(HumanPose &arPoseBase,HumanPose const &arPose, float aWeight, HumanPoseMask const &arHumanPoseMask)
+ {
+ if(aWeight > 0.0f)
+ {
+ float weightInv = 1.0f - aWeight;
+ math::float1 w(aWeight);
+
+ int32_t i;
+ for(i = 0; i < kLastDoF; i++)
+ {
+ if(arHumanPoseMask.test(kMaskDoFStartIndex+i))
+ {
+ if(aWeight < 1.0f)
+ {
+ arPoseBase.m_DoFArray[i] = weightInv * arPoseBase.m_DoFArray[i] + aWeight * arPose.m_DoFArray[i];
+ }
+ else
+ {
+ arPoseBase.m_DoFArray[i] = arPose.m_DoFArray[i];
+ }
+ }
+ }
+
+ if(arHumanPoseMask.test(kMaskLeftHand))
+ {
+ for(i = 0; i < hand::s_DoFCount; i++)
+ {
+ if(aWeight < 1.0f)
+ {
+ arPoseBase.m_LeftHandPose.m_DoFArray[i] = weightInv * arPoseBase.m_LeftHandPose.m_DoFArray[i] + aWeight * arPose.m_LeftHandPose.m_DoFArray[i];
+ }
+ else
+ {
+ arPoseBase.m_LeftHandPose.m_DoFArray[i] = arPose.m_LeftHandPose.m_DoFArray[i];
+ }
+ }
+ }
+
+ if(arHumanPoseMask.test(kMaskRightHand))
+ {
+ for(i = 0; i < hand::s_DoFCount; i++)
+ {
+ if(aWeight < 1.0f)
+ {
+ arPoseBase.m_RightHandPose.m_DoFArray[i] = weightInv * arPoseBase.m_RightHandPose.m_DoFArray[i] + aWeight * arPose.m_RightHandPose.m_DoFArray[i];
+ }
+ else
+ {
+ arPoseBase.m_RightHandPose.m_DoFArray[i] = arPose.m_RightHandPose.m_DoFArray[i];
+ }
+ }
+ }
+
+ for(i = 0; i < kLastGoal; i++)
+ {
+ if(arHumanPoseMask.test(kMaskGoalStartIndex+i))
+ {
+ if(aWeight < 1.0f)
+ {
+ arPoseBase.m_GoalArray[i].m_X = math::xformBlend(arPoseBase.m_GoalArray[i].m_X,arPose.m_GoalArray[i].m_X,w);
+ }
+ else
+ {
+ arPoseBase.m_GoalArray[i].m_X = arPose.m_GoalArray[i].m_X;
+ }
+ }
+ }
+
+ if(arHumanPoseMask.test(0))
+ {
+ if(aWeight < 1.0f)
+ {
+ arPoseBase.m_RootX = math::xformBlend(arPoseBase.m_RootX,arPose.m_RootX,w);
+ }
+ else
+ {
+ arPoseBase.m_RootX = arPose.m_RootX;
+ }
+ }
+ }
+ }
+
+ void HumanPoseAddAdditiveLayer(HumanPose &arPoseBase,HumanPose const &arPose, float aWeight, HumanPoseMask const &arHumanPoseMask)
+ {
+ if(aWeight > 0.0f)
+ {
+ math::float1 w(aWeight);
+
+ int32_t i;
+ for(i = 0; i < kLastDoF; i++)
+ {
+ if(arHumanPoseMask.test(kMaskDoFStartIndex+i))
+ {
+ arPoseBase.m_DoFArray[i] += aWeight * arPose.m_DoFArray[i];
+ }
+ }
+
+ if(arHumanPoseMask.test(kMaskLeftHand))
+ {
+ for(i = 0; i < hand::s_DoFCount; i++)
+ {
+ arPoseBase.m_LeftHandPose.m_DoFArray[i] += aWeight * arPose.m_LeftHandPose.m_DoFArray[i];
+ }
+ }
+
+ if(arHumanPoseMask.test(kMaskRightHand))
+ {
+ for(i = 0; i < hand::s_DoFCount; i++)
+ {
+ arPoseBase.m_RightHandPose.m_DoFArray[i] += aWeight * arPose.m_RightHandPose.m_DoFArray[i];
+ }
+ }
+
+ for(i = 0; i < kLastGoal; i++)
+ {
+ if(arHumanPoseMask.test(kMaskGoalStartIndex+i))
+ {
+ arPoseBase.m_GoalArray[i].m_X = math::xformMul(arPoseBase.m_GoalArray[i].m_X, math::xformWeight(arPose.m_GoalArray[i].m_X, w));
+ }
+ }
+
+ if(arHumanPoseMask.test(0))
+ {
+ arPoseBase.m_RootX = math::xformMul(arPoseBase.m_RootX, math::xformWeight(arPose.m_RootX,w));
+ }
+ }
+ }
+
+ void HumanFixMidDoF(Human const *apHuman, skeleton::SkeletonPose *apSkeletonPose, skeleton::SkeletonPose *apSkeletonPoseWs, int32_t aPIndex, int32_t aCIndex)
+ {
+ int32_t pNodeIndex = apHuman->m_HumanBoneIndex[aPIndex];
+ int32_t cNodeIndex = apHuman->m_HumanBoneIndex[aCIndex];
+ int32_t aNodeIndex = apHuman->m_Skeleton->m_Node[pNodeIndex].m_ParentId;
+
+ math::Axes pAxes = apHuman->m_Skeleton->m_AxesArray[apHuman->m_Skeleton->m_Node[pNodeIndex].m_AxesId];
+ math::Axes cAxes = apHuman->m_Skeleton->m_AxesArray[apHuman->m_Skeleton->m_Node[cNodeIndex].m_AxesId];
+
+ apSkeletonPoseWs->m_X[aNodeIndex].q = math::quatIdentity();
+ skeleton::SkeletonPoseComputeGlobalQ(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseWs,cNodeIndex,pNodeIndex);
+
+ math::float4 pq = apSkeletonPose->m_X[pNodeIndex].q;
+ math::float4 cqg = apSkeletonPoseWs->m_X[cNodeIndex].q;
+
+ math::float4 cql = AxesProject(cAxes,apSkeletonPose->m_X[cNodeIndex].q);
+
+ math::float4 xyz = math::quat2ZYRoll(cql);
+ xyz.y() = math::float1::zero();
+ cql = math::ZYRoll2Quat(xyz);
+
+ cql = math::AxesUnproject(cAxes,cql);
+
+ apSkeletonPose->m_X[cNodeIndex].q = cql;
+ skeleton::SkeletonPoseComputeGlobalQ(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseWs,cNodeIndex,cNodeIndex);
+
+ math::float4 qdiff = math::quatMul(cqg,math::quatConj(apSkeletonPoseWs->m_X[cNodeIndex].q));
+
+ apSkeletonPose->m_X[pNodeIndex].q = math::normalize(math::quatMul(qdiff,apSkeletonPose->m_X[pNodeIndex].q));
+
+ skeleton::SkeletonAlign(apHuman->m_Skeleton.Get(),pq,apSkeletonPose->m_X[pNodeIndex].q,pNodeIndex);
+
+ skeleton::SkeletonPoseComputeGlobalQ(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseWs,cNodeIndex,pNodeIndex);
+
+ apSkeletonPoseWs->m_X[cNodeIndex].q = cqg;
+
+ skeleton::SkeletonPoseComputeLocalQ(apHuman->m_Skeleton.Get(),apSkeletonPoseWs,apSkeletonPose,cNodeIndex,cNodeIndex);
+ }
+
+ void HumanFixEndDoF(Human const *apHuman, skeleton::SkeletonPose *apSkeletonPose, skeleton::SkeletonPose *apSkeletonPoseWs, int32_t aPIndex, int32_t aCIndex)
+ {
+ int32_t pNodeIndex = apHuman->m_HumanBoneIndex[aPIndex];
+ int32_t cNodeIndex = apHuman->m_HumanBoneIndex[aCIndex];
+ int32_t aNodeIndex = apHuman->m_Skeleton->m_Node[pNodeIndex].m_ParentId;
+
+ math::Axes pAxes = apHuman->m_Skeleton->m_AxesArray[apHuman->m_Skeleton->m_Node[pNodeIndex].m_AxesId];
+ math::Axes cAxes = apHuman->m_Skeleton->m_AxesArray[apHuman->m_Skeleton->m_Node[cNodeIndex].m_AxesId];
+
+ apSkeletonPoseWs->m_X[aNodeIndex].q = math::quatIdentity();
+ skeleton::SkeletonPoseComputeGlobalQ(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseWs,cNodeIndex,pNodeIndex);
+
+ math::float4 pq = apSkeletonPose->m_X[pNodeIndex].q;
+ math::float4 cqg = apSkeletonPoseWs->m_X[cNodeIndex].q;
+
+ math::float4 pq0 = FromAxes(pAxes,math::float4::zero());
+ math::float4 cql0 = FromAxes(cAxes,math::float4::zero());
+
+ apSkeletonPose->m_X[pNodeIndex].q = pq0;
+ apSkeletonPose->m_X[cNodeIndex].q = cql0;
+ skeleton::SkeletonPoseComputeGlobalQ(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseWs,cNodeIndex,pNodeIndex);
+
+ math::float4 qdiff = math::quatMul(cqg,math::quatConj(apSkeletonPoseWs->m_X[cNodeIndex].q));
+
+ apSkeletonPose->m_X[pNodeIndex].q = math::normalize(math::quatMul(qdiff,pq0));
+
+ skeleton::SkeletonAlign(apHuman->m_Skeleton.Get(),pq,apSkeletonPose->m_X[pNodeIndex].q,pNodeIndex);
+
+ skeleton::SkeletonPoseComputeGlobalQ(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseWs,cNodeIndex,pNodeIndex);
+
+ apSkeletonPoseWs->m_X[cNodeIndex].q = cqg;
+
+ skeleton::SkeletonPoseComputeLocalQ(apHuman->m_Skeleton.Get(),apSkeletonPoseWs,apSkeletonPose,cNodeIndex,cNodeIndex);
+ }
+
+ void HumanFixTwist(Human const *apHuman, skeleton::SkeletonPose *apSkeletonPose, skeleton::SkeletonPose *apSkeletonPoseWs, int32_t aPIndex, int32_t aCIndex, const math::float1& aTwist)
+ {
+ int32_t pNodeIndex = apHuman->m_HumanBoneIndex[aPIndex];
+ int32_t cNodeIndex = apHuman->m_HumanBoneIndex[aCIndex];
+ int32_t aNodeIndex = apHuman->m_Skeleton->m_Node[pNodeIndex].m_ParentId;
+
+ math::Axes pAxes = apHuman->m_Skeleton->m_AxesArray[apHuman->m_Skeleton->m_Node[pNodeIndex].m_AxesId];
+
+ apSkeletonPoseWs->m_X[aNodeIndex].q = math::quatIdentity();
+ skeleton::SkeletonPoseComputeGlobalQ(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseWs,cNodeIndex,pNodeIndex);
+
+ math::float4 pq = apSkeletonPose->m_X[pNodeIndex].q;
+ math::float4 cqg = apSkeletonPoseWs->m_X[cNodeIndex].q;
+
+ math::float4 pxyz = math::ToAxes(pAxes,apSkeletonPose->m_X[pNodeIndex].q);
+ pxyz.x() *= aTwist;
+
+ apSkeletonPose->m_X[pNodeIndex].q = math::FromAxes(pAxes,pxyz);
+
+ skeleton::SkeletonAlign(apHuman->m_Skeleton.Get(),pq,apSkeletonPose->m_X[pNodeIndex].q,pNodeIndex);
+
+ skeleton::SkeletonPoseComputeGlobalQ(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseWs,cNodeIndex,pNodeIndex);
+
+ apSkeletonPoseWs->m_X[cNodeIndex].q = cqg;
+
+ skeleton::SkeletonPoseComputeLocalQ(apHuman->m_Skeleton.Get(),apSkeletonPoseWs,apSkeletonPose,cNodeIndex,cNodeIndex);
+ }
+
+ void ReachGoalRotation(Human const *apHuman,math::float4 const &arEndQ, int32_t aGoalIndex, skeleton::SkeletonPose *apSkeletonPose, skeleton::SkeletonPose *apSkeletonPoseGbl, skeleton::SkeletonPose *apSkeletonPoseWorkspace)
+ {
+ int32_t index = apHuman->m_HumanBoneIndex[s_HumanGoalInfo[aGoalIndex].m_Index];
+ int32_t parentIndex = apHuman->m_Skeleton->m_Node[index].m_ParentId;
+ apSkeletonPose->m_X[index].q = math::normalize(math::quatMul(math::quatConj(apSkeletonPoseGbl->m_X[parentIndex].q),arEndQ));
+
+ HumanFixEndDoF(apHuman,apSkeletonPose,apSkeletonPoseWorkspace,s_HumanGoalInfo[aGoalIndex].m_MidIndex,s_HumanGoalInfo[aGoalIndex].m_EndIndex);
+ }
+
+ void HumanFixEndPointsSkeletonPose(Human const *apHuman, skeleton::SkeletonPose const*apSkeletonPoseRef, HumanPose *apHumanPose, skeleton::SkeletonPose *apSkeletonPoseGbl, skeleton::SkeletonPose *apSkeletonPoseLcl, skeleton::SkeletonPose *apSkeletonPoseWs,int32_t cIndex, int32_t pIndex)
+ {
+ skeleton::SkeletonPoseComputeGlobalQ(apHuman->m_Skeleton.Get(),apSkeletonPoseLcl,apSkeletonPoseGbl,apHuman->m_HumanBoneIndex[cIndex],apHuman->m_HumanBoneIndex[pIndex]);
+ apSkeletonPoseGbl->m_X[apHuman->m_HumanBoneIndex[cIndex]].q = apSkeletonPoseRef->m_X[apHuman->m_HumanBoneIndex[cIndex]].q;
+ skeleton::SkeletonPoseComputeLocalQ(apHuman->m_Skeleton.Get(),apSkeletonPoseGbl,apSkeletonPoseLcl,apHuman->m_HumanBoneIndex[cIndex],apHuman->m_HumanBoneIndex[pIndex]);
+
+ HumanFixEndDoF(apHuman,apSkeletonPoseLcl,apSkeletonPoseWs,pIndex,cIndex);
+ }
+
+ void HumanAlignSkeletonPose(Human const *apHuman, skeleton::SkeletonPose const*apSkeletonPoseRef, HumanPose *apHumanPose, skeleton::SkeletonPose *apSkeletonPoseGbl, skeleton::SkeletonPose *apSkeletonPoseLcl,int32_t cIndex, int32_t pIndex)
+ {
+ Skeleton2HumanPose(apHuman,apSkeletonPoseLcl,apHumanPose,cIndex);
+ Human2SkeletonPose(apHuman,apHumanPose,apSkeletonPoseLcl,cIndex);
+
+ skeleton::SkeletonPoseComputeGlobalQ(apHuman->m_Skeleton.Get(),apSkeletonPoseLcl,apSkeletonPoseGbl,apHuman->m_HumanBoneIndex[cIndex],apHuman->m_HumanBoneIndex[pIndex]);
+ skeleton::SkeletonAlign(apHuman->m_Skeleton.Get(),apSkeletonPoseRef,apSkeletonPoseGbl,apHuman->m_HumanBoneIndex[cIndex]);
+ skeleton::SkeletonPoseComputeLocalQ(apHuman->m_Skeleton.Get(),apSkeletonPoseGbl,apSkeletonPoseLcl,apHuman->m_HumanBoneIndex[cIndex],apHuman->m_HumanBoneIndex[pIndex]);
+ }
+
+ void RetargetFrom( Human const *apHuman,
+ skeleton::SkeletonPose const *apSkeletonPose,
+ HumanPose *apHumanPose,
+ skeleton::SkeletonPose *apSkeletonPoseRef,
+ skeleton::SkeletonPose *apSkeletonPoseGbl,
+ skeleton::SkeletonPose *apSkeletonPoseLcl,
+ skeleton::SkeletonPose *apSkeletonPoseWs,
+ mecanim::int32_t maxFixIter)
+
+ {
+ const int32_t hipsIndex = apHuman->m_HumanBoneIndex[human::kHips];
+ const math::float1 scale(apHuman->m_Scale);
+
+ skeleton::SkeletonPoseComputeGlobal(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseRef);
+ skeleton::SkeletonPoseCopy(apSkeletonPoseRef,apSkeletonPoseGbl);
+
+ // force dummy bones to their default rotation
+ int32_t nodeIter;
+ for(nodeIter = 1; nodeIter < apHuman->m_Skeleton->m_Count; nodeIter++)
+ {
+ if(apHuman->m_Skeleton->m_Node[nodeIter].m_AxesId == -1)
+ {
+ apSkeletonPoseGbl->m_X[nodeIter].q = math::quatMul(apSkeletonPoseGbl->m_X[apHuman->m_Skeleton->m_Node[nodeIter].m_ParentId].q,apHuman->m_SkeletonPose->m_X[nodeIter].q);
+ }
+ }
+
+ skeleton::SkeletonPoseComputeLocalQ(apHuman->m_Skeleton.Get(),apSkeletonPoseGbl,apSkeletonPoseLcl);
+
+ int32_t fixIter;
+
+ // align shoulders
+ for(fixIter = 0; fixIter < maxFixIter; fixIter++)
+ {
+ if(apHuman->m_HumanBoneIndex[kLeftShoulder] != -1)
+ {
+ HumanAlignSkeletonPose(apHuman,apSkeletonPoseRef,apHumanPose,apSkeletonPoseGbl,apSkeletonPoseLcl,kLeftShoulder, kLeftShoulder);
+ }
+
+ if(apHuman->m_HumanBoneIndex[kRightShoulder] != -1)
+ {
+ HumanAlignSkeletonPose(apHuman,apSkeletonPoseRef,apHumanPose,apSkeletonPoseGbl,apSkeletonPoseLcl,kRightShoulder, kRightShoulder);
+ }
+ }
+
+ // align upper limbs
+ for(fixIter = 0; fixIter < maxFixIter; fixIter++)
+ {
+ HumanAlignSkeletonPose(apHuman,apSkeletonPoseRef,apHumanPose,apSkeletonPoseGbl,apSkeletonPoseLcl,kLeftUpperArm, apHuman->m_HumanBoneIndex[kLeftShoulder] != -1 ? kLeftShoulder : kLeftUpperArm);
+ HumanAlignSkeletonPose(apHuman,apSkeletonPoseRef,apHumanPose,apSkeletonPoseGbl,apSkeletonPoseLcl,kRightUpperArm, apHuman->m_HumanBoneIndex[kRightShoulder] != -1 ? kRightShoulder : kRightUpperArm);
+ HumanAlignSkeletonPose(apHuman,apSkeletonPoseRef,apHumanPose,apSkeletonPoseGbl,apSkeletonPoseLcl,kLeftUpperLeg, kLeftUpperLeg);
+ HumanAlignSkeletonPose(apHuman,apSkeletonPoseRef,apHumanPose,apSkeletonPoseGbl,apSkeletonPoseLcl,kRightUpperLeg, kRightUpperLeg);
+ }
+
+ // align & fix lower limbs
+ for(fixIter = 0; fixIter < maxFixIter; fixIter++)
+ {
+ HumanFixMidDoF(apHuman,apSkeletonPoseLcl,apSkeletonPoseWs,kLeftUpperArm,kLeftLowerArm);
+ HumanFixMidDoF(apHuman,apSkeletonPoseLcl,apSkeletonPoseWs,kRightUpperArm,kRightLowerArm);
+ HumanFixMidDoF(apHuman,apSkeletonPoseLcl,apSkeletonPoseWs,kLeftUpperLeg,kLeftLowerLeg);
+ HumanFixMidDoF(apHuman,apSkeletonPoseLcl,apSkeletonPoseWs,kRightUpperLeg,kRightLowerLeg);
+
+ HumanAlignSkeletonPose(apHuman,apSkeletonPoseRef,apHumanPose,apSkeletonPoseGbl,apSkeletonPoseLcl,kLeftLowerArm, kLeftUpperArm);
+ HumanAlignSkeletonPose(apHuman,apSkeletonPoseRef,apHumanPose,apSkeletonPoseGbl,apSkeletonPoseLcl,kRightLowerArm, kRightUpperArm);
+ HumanAlignSkeletonPose(apHuman,apSkeletonPoseRef,apHumanPose,apSkeletonPoseGbl,apSkeletonPoseLcl,kLeftLowerLeg, kLeftUpperLeg);
+ HumanAlignSkeletonPose(apHuman,apSkeletonPoseRef,apHumanPose,apSkeletonPoseGbl,apSkeletonPoseLcl,kRightLowerLeg, kRightUpperLeg);
+ }
+
+ HumanFixEndPointsSkeletonPose(apHuman,apSkeletonPoseRef, apHumanPose, apSkeletonPoseGbl, apSkeletonPoseLcl, apSkeletonPoseWs, kLeftHand, kLeftLowerArm);
+ HumanFixEndPointsSkeletonPose(apHuman,apSkeletonPoseRef, apHumanPose, apSkeletonPoseGbl, apSkeletonPoseLcl, apSkeletonPoseWs, kRightHand, kRightLowerArm);
+ HumanFixEndPointsSkeletonPose(apHuman,apSkeletonPoseRef, apHumanPose, apSkeletonPoseGbl, apSkeletonPoseLcl, apSkeletonPoseWs, kLeftFoot, kLeftLowerLeg);
+ HumanFixEndPointsSkeletonPose(apHuman,apSkeletonPoseRef, apHumanPose, apSkeletonPoseGbl, apSkeletonPoseLcl, apSkeletonPoseWs, kRightFoot, kRightLowerLeg);
+
+ Skeleton2HumanPose(apHuman,apSkeletonPoseLcl,apHumanPose);
+ skeleton::SkeletonPoseCopy(apHuman->m_SkeletonPose.Get(), apSkeletonPoseLcl);
+ Human2SkeletonPose(apHuman, apHumanPose, apSkeletonPoseLcl);
+ skeleton::SkeletonPoseComputeGlobal(apHuman->m_Skeleton.Get(), apSkeletonPoseLcl,apSkeletonPoseGbl);
+
+ apHumanPose->m_RootX = HumanComputeRootXform(apHuman,apSkeletonPoseGbl);
+ apHumanPose->m_RootX = math::xformInvMul(apSkeletonPoseGbl->m_X[hipsIndex],apHumanPose->m_RootX);
+ apHumanPose->m_RootX = math::xformMul(apSkeletonPoseRef->m_X[hipsIndex],apHumanPose->m_RootX);
+ apHumanPose->m_RootX.s = math::float4::one();
+
+ int32_t goalIter;
+ for(goalIter = 0; goalIter < kLastGoal; goalIter++)
+ {
+ int32_t index = apHuman->m_HumanBoneIndex[s_HumanGoalInfo[goalIter].m_Index];
+ apHumanPose->m_GoalArray[goalIter].m_X.t = apSkeletonPoseRef->m_X[index].t;
+ apHumanPose->m_GoalArray[goalIter].m_X.q = AddAxis(apHuman,index,apSkeletonPoseRef->m_X[index].q);
+ apHumanPose->m_GoalArray[goalIter].m_X.s = math::float4::one();
+
+ if(goalIter < 2) apHumanPose->m_GoalArray[goalIter].m_X.t = math::xformMulVec(apHumanPose->m_GoalArray[goalIter].m_X,human::HumanGetFootBottom(apHuman,goalIter==0));
+ apHumanPose->m_GoalArray[goalIter].m_X = math::xformInvMulNS(apHumanPose->m_RootX,apHumanPose->m_GoalArray[goalIter].m_X);
+ apHumanPose->m_GoalArray[goalIter].m_X.t /= scale;
+ }
+
+ apHumanPose->m_RootX.t /= scale;
+ }
+
+ void RetargetTo( Human const *apHuman,
+ HumanPose const *apHumanPoseBase,
+ HumanPose const *apHumanPose,
+ const math::xform &arX,
+ HumanPose *apHumanPoseOut,
+ skeleton::SkeletonPose *apSkeletonPose,
+ skeleton::SkeletonPose *apSkeletonPoseWs)
+ {
+ const int32_t rootIndex = 0;
+ const int32_t hipsIndex = apHuman->m_HumanBoneIndex[human::kHips];
+ const math::float1 scale(apHuman->m_Scale);
+
+ human::HumanPoseCopy(*apHumanPoseOut,*apHumanPoseBase);
+
+ apHumanPoseOut->m_RootX.t *= scale;
+ apHumanPoseOut->m_RootX = math::xformMul(arX,apHumanPoseOut->m_RootX);
+
+ int32_t goalIter;
+ for(goalIter = 0; goalIter < kLastGoal; goalIter++)
+ {
+ apHumanPoseOut->m_GoalArray[goalIter].m_X = apHumanPose ? apHumanPose->m_GoalArray[goalIter].m_X : apHumanPoseBase->m_GoalArray[goalIter].m_X;
+ apHumanPoseOut->m_GoalArray[goalIter].m_X.t *= scale;
+ apHumanPoseOut->m_GoalArray[goalIter].m_X = math::xformMul(arX,apHumanPoseOut->m_GoalArray[goalIter].m_X);
+
+ if(goalIter < 2) apHumanPoseOut->m_GoalArray[goalIter].m_X.t = math::xformMulVec(apHumanPoseOut->m_GoalArray[goalIter].m_X,-human::HumanGetFootBottom(apHuman,goalIter==0));
+ }
+
+ //////////////////////////////////////////////////
+ //
+ // transfer muscle space for base pose
+ //
+ skeleton::SkeletonPoseCopy(apHuman->m_SkeletonPose.Get(), apSkeletonPose);
+ HumanPoseAdjustForMissingBones(apHuman,apHumanPoseOut);
+ Human2SkeletonPose(apHuman, apHumanPoseOut, apSkeletonPose);
+ skeleton::SkeletonPoseComputeGlobal(apHuman->m_Skeleton.Get(), apSkeletonPose,apSkeletonPoseWs);
+
+ ///////////////////////////////////////////////////////
+ //
+ // adjust hips local
+ //
+ math::xform rootX = HumanComputeRootXform(apHuman,apSkeletonPoseWs);
+ apSkeletonPose->m_X[hipsIndex] = math::xformInvMulNS(rootX,apSkeletonPoseWs->m_X[hipsIndex]);
+ apSkeletonPose->m_X[hipsIndex].s = apSkeletonPoseWs->m_X[hipsIndex].s;
+
+ ////////////////////////////////////////////////////////
+ //
+ // transfer muscle space
+ //
+ if(apHumanPose)
+ {
+ human::HumanPoseCopy(*apHumanPoseOut,*apHumanPose,true);
+ HumanPoseAdjustForMissingBones(apHuman,apHumanPoseOut);
+ Human2SkeletonPose(apHuman, apHumanPoseOut, apSkeletonPose);
+ }
+
+ //////////////////////////////////////////////////
+ //
+ // root
+ //
+ apSkeletonPose->m_X[rootIndex] = apHumanPoseOut->m_RootX;
+ }
+
+ math::float4 GetLookAtDeltaQ(math::float4 const &pivot,math::float4 const &eyesT, math::float4 const &eyesQ, math::float4 const &eyesDir, math::float4 const &target, math::float1 const &weight)
+ {
+ math::float1 len = math::length(target - eyesT);
+ math::float4 dstV = target - pivot;
+ math::float4 srcV = eyesT - math::quatMulVec(eyesQ,eyesDir*math::float1(len)) - pivot;
+
+ return math::quatWeight(math::normalize(math::quatArcRotate(srcV,dstV)),weight);
+ }
+
+ void FullBodySolve(Human const *apHuman, HumanPose const *apHumanPose, skeleton::SkeletonPose *apSkeletonPose, skeleton::SkeletonPose *apSkeletonPoseWorkspaceA, skeleton::SkeletonPose *apSkeletonPoseWorkspaceB)
+ {
+ const int32_t hipsIndex = apHuman->m_HumanBoneIndex[kHips];
+ const int32_t chestIndex = apHuman->m_HumanBoneIndex[kChest];
+ const int32_t spineIndex = apHuman->m_HumanBoneIndex[kSpine];
+ const int32_t neckIndex = apHuman->m_HumanBoneIndex[kNeck];
+ const int32_t headIndex = apHuman->m_HumanBoneIndex[kHead];
+ const int32_t leftEyeIndex = apHuman->m_HumanBoneIndex[kLeftEye];
+ const int32_t rightEyeIndex = apHuman->m_HumanBoneIndex[kRightEye];
+
+ math::float1 lcw = math::saturate(math::float1(apHumanPose->m_LookAtWeight.x()));
+ math::float1 lbw = math::saturate(math::float1(apHumanPose->m_LookAtWeight.y()));
+ math::float1 lhw = math::saturate(math::float1(apHumanPose->m_LookAtWeight.z()));
+ math::float1 lew = math::saturate(math::float1(apHumanPose->m_LookAtWeight.w()));
+
+ math::float4 headGoalT = apHumanPose->m_LookAtPosition;
+
+ if(lcw > math::float1::zero())
+ {
+ math::float4 eyesPos = apSkeletonPoseWorkspaceA->m_X[headIndex].t;
+ math::float4 eyesRot = AddAxis(apHuman,headIndex,apSkeletonPoseWorkspaceA->m_X[headIndex].q);
+ if(leftEyeIndex != -1 && rightEyeIndex != -1) eyesPos = math::xformMulVec(apSkeletonPoseWorkspaceA->m_X[headIndex],(apHuman->m_SkeletonPose->m_X[leftEyeIndex].t + apHuman->m_SkeletonPose->m_X[rightEyeIndex].t) * math::float1(0.5f));
+
+ math::float4 dstV = headGoalT - eyesPos;
+ math::float4 v = math::float4::zero();
+ v.y() = -math::length(dstV);
+ math::float4 srcV = math::quatMulVec(eyesRot,v);
+
+ math::float4 deltaQ = math::quatClamp(math::normalize(math::quatArcRotate(srcV,dstV)),math::radians(180.f*(1.f - lcw.tofloat())));
+
+ headGoalT = eyesPos + math::quatMulVec(deltaQ,srcV);
+ }
+
+ if(lbw > math::float1::zero())
+ {
+ math::float1 lsw = chestIndex != -1 ? math::float1(0.5) * lbw : lbw;
+
+ math::float4 eyesPos = apSkeletonPoseWorkspaceA->m_X[headIndex].t;
+ math::float4 eyesRot = AddAxis(apHuman,headIndex,apSkeletonPoseWorkspaceA->m_X[headIndex].q);
+
+ math::float4 deltaQ = GetLookAtDeltaQ(apSkeletonPoseWorkspaceA->m_X[spineIndex].t,eyesPos,eyesRot,math::float4(0,1,0,0),headGoalT,lsw);
+
+ apSkeletonPoseWorkspaceA->m_X[spineIndex].q = math::normalize(math::quatMul(deltaQ,apSkeletonPoseWorkspaceA->m_X[spineIndex].q));
+ skeleton::SkeletonPoseComputeLocalQ(apHuman->m_Skeleton.Get(),apSkeletonPoseWorkspaceA,apSkeletonPose,spineIndex,spineIndex);
+
+ if(chestIndex != -1)
+ {
+ skeleton::SkeletonPoseComputeGlobal(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseWorkspaceA,headIndex,spineIndex);
+
+ math::float4 eyesPos = apSkeletonPoseWorkspaceA->m_X[headIndex].t;
+ math::float4 eyesRot = AddAxis(apHuman,headIndex,apSkeletonPoseWorkspaceA->m_X[headIndex].q);
+
+ math::float4 deltaQ = GetLookAtDeltaQ(apSkeletonPoseWorkspaceA->m_X[chestIndex].t,eyesPos,eyesRot,math::float4(0,1,0,0),headGoalT,lbw);
+
+ apSkeletonPoseWorkspaceA->m_X[chestIndex].q = math::normalize(math::quatMul(deltaQ,apSkeletonPoseWorkspaceA->m_X[chestIndex].q));
+ skeleton::SkeletonPoseComputeLocalQ(apHuman->m_Skeleton.Get(),apSkeletonPoseWorkspaceA,apSkeletonPose,chestIndex,chestIndex);
+ }
+ }
+
+ if(lhw > math::float1::zero())
+ {
+ skeleton::SkeletonPoseComputeGlobal(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseWorkspaceA,headIndex,spineIndex);
+
+ if(neckIndex != -1)
+ {
+ math::float4 eyesPos = apSkeletonPoseWorkspaceA->m_X[headIndex].t;
+ math::float4 eyesRot = AddAxis(apHuman,headIndex,apSkeletonPoseWorkspaceA->m_X[headIndex].q);
+
+ if(leftEyeIndex != -1 && rightEyeIndex != -1) eyesPos = math::xformMulVec(apSkeletonPoseWorkspaceA->m_X[headIndex],(apHuman->m_SkeletonPose->m_X[leftEyeIndex].t + apHuman->m_SkeletonPose->m_X[rightEyeIndex].t) * math::float1(0.5f));
+
+ math::float4 deltaQ = GetLookAtDeltaQ(apSkeletonPoseWorkspaceA->m_X[neckIndex].t,eyesPos,eyesRot,math::float4(0,1,0,0),headGoalT,lhw * math::float1(0.5));
+
+ apSkeletonPoseWorkspaceA->m_X[neckIndex].q = math::normalize(math::quatMul(deltaQ,apSkeletonPoseWorkspaceA->m_X[neckIndex].q));
+ skeleton::SkeletonPoseComputeLocalQ(apHuman->m_Skeleton.Get(),apSkeletonPoseWorkspaceA,apSkeletonPose,neckIndex,neckIndex);
+ }
+
+ int32_t iter;
+ for(iter = 0; iter < 3; iter++)
+ {
+ math::float4 eyesPos = apSkeletonPoseWorkspaceA->m_X[headIndex].t;
+ math::float4 eyesRot = AddAxis(apHuman,headIndex,apSkeletonPoseWorkspaceA->m_X[headIndex].q);
+
+ if(leftEyeIndex != -1 && rightEyeIndex != -1) eyesPos = math::xformMulVec(apSkeletonPoseWorkspaceA->m_X[headIndex],(apHuman->m_SkeletonPose->m_X[leftEyeIndex].t + apHuman->m_SkeletonPose->m_X[rightEyeIndex].t) * math::float1(0.5f));
+
+ math::float4 deltaQ = GetLookAtDeltaQ(apSkeletonPoseWorkspaceA->m_X[headIndex].t,eyesPos,eyesRot,math::float4(0,1,0,0),headGoalT,lhw*lhw);
+
+ apSkeletonPoseWorkspaceA->m_X[headIndex].q = math::normalize(math::quatMul(deltaQ,apSkeletonPoseWorkspaceA->m_X[headIndex].q));
+ skeleton::SkeletonPoseComputeLocalQ(apHuman->m_Skeleton.Get(),apSkeletonPoseWorkspaceA,apSkeletonPose,headIndex,headIndex);
+ skeleton::SkeletonPoseComputeGlobal(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseWorkspaceA,headIndex,headIndex);
+ }
+ }
+
+ if(lew > math::float1::zero())
+ {
+ if(leftEyeIndex != -1)
+ {
+ skeleton::SkeletonPoseComputeGlobal(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseWorkspaceA,leftEyeIndex,spineIndex);
+
+ math::float4 eyesPos = apSkeletonPoseWorkspaceA->m_X[leftEyeIndex].t;
+ math::float4 eyesRot = AddAxis(apHuman,leftEyeIndex,apSkeletonPoseWorkspaceA->m_X[leftEyeIndex].q);
+
+ math::float4 deltaQ = GetLookAtDeltaQ(apSkeletonPoseWorkspaceA->m_X[leftEyeIndex].t,eyesPos,eyesRot,math::float4(-1,0,0,0),headGoalT,lew);
+
+ apSkeletonPoseWorkspaceA->m_X[leftEyeIndex].q = math::normalize(math::quatMul(deltaQ,apSkeletonPoseWorkspaceA->m_X[leftEyeIndex].q));
+ skeleton::SkeletonPoseComputeLocalQ(apHuman->m_Skeleton.Get(),apSkeletonPoseWorkspaceA,apSkeletonPose,leftEyeIndex,leftEyeIndex);
+ }
+
+ if(rightEyeIndex != -1)
+ {
+ skeleton::SkeletonPoseComputeGlobal(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseWorkspaceA,rightEyeIndex,spineIndex);
+
+ math::float4 eyesPos = apSkeletonPoseWorkspaceA->m_X[rightEyeIndex].t;
+ math::float4 eyesRot = AddAxis(apHuman,rightEyeIndex,apSkeletonPoseWorkspaceA->m_X[rightEyeIndex].q);
+
+ math::float4 deltaQ = GetLookAtDeltaQ(apSkeletonPoseWorkspaceA->m_X[rightEyeIndex].t,eyesPos,eyesRot,math::float4(-1,0,0,0),headGoalT,lew);
+
+ apSkeletonPoseWorkspaceA->m_X[rightEyeIndex].q = math::normalize(math::quatMul(deltaQ,apSkeletonPoseWorkspaceA->m_X[rightEyeIndex].q));
+ skeleton::SkeletonPoseComputeLocalQ(apHuman->m_Skeleton.Get(),apSkeletonPoseWorkspaceA,apSkeletonPose,rightEyeIndex,rightEyeIndex);
+ }
+ }
+
+ int32_t goalIter;
+ for(goalIter = kLeftFootGoal; goalIter <= kRightHandGoal; goalIter++)
+ {
+ int32_t topIndex = apHuman->m_HumanBoneIndex[s_HumanGoalInfo[goalIter].m_TopIndex];
+ int32_t midIndex = apHuman->m_HumanBoneIndex[s_HumanGoalInfo[goalIter].m_MidIndex];
+ int32_t endIndex = apHuman->m_HumanBoneIndex[s_HumanGoalInfo[goalIter].m_EndIndex];
+
+ if(apHumanPose->m_GoalArray[goalIter].m_WeightT > 0.f)
+ {
+ float weightT = math::saturate(apHumanPose->m_GoalArray[goalIter].m_WeightT);
+
+ skeleton::SkeletonPoseComputeGlobal(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseWorkspaceA,endIndex,hipsIndex);
+
+ // adjust len
+ math::float4 t = math::lerp(apSkeletonPoseWorkspaceA->m_X[endIndex].t,apHumanPose->m_GoalArray[goalIter].m_X.t, math::float1(weightT));
+ skeleton::Skeleton2BoneAdjustLength(apHuman->m_Skeleton.Get(),topIndex,midIndex,endIndex,t, math::float1( (goalIter < kLeftHandGoal ? apHuman->m_LegStretch : apHuman->m_ArmStretch) * weightT),apSkeletonPose,apSkeletonPoseWorkspaceA);
+
+ // 2 bone ik
+ skeleton::SkeletonPoseComputeGlobal(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseWorkspaceA,endIndex,topIndex);
+ skeleton::Skeleton2BoneIK(apHuman->m_Skeleton.Get(),topIndex,midIndex,endIndex,apHumanPose->m_GoalArray[goalIter].m_X.t,weightT,apSkeletonPose,apSkeletonPoseWorkspaceA);
+ }
+ }
+
+ // end rotation
+ for(goalIter = kLeftFootGoal; goalIter <= kRightHandGoal; goalIter++)
+ {
+ int32_t endIndex = apHuman->m_HumanBoneIndex[s_HumanGoalInfo[goalIter].m_EndIndex];
+
+ if(apHumanPose->m_GoalArray[goalIter].m_WeightR > 0)
+ {
+ float weightR = math::saturate(apHumanPose->m_GoalArray[goalIter].m_WeightR);
+
+ skeleton::SkeletonPoseComputeGlobal(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseWorkspaceA,endIndex,hipsIndex);
+
+ int32_t index = apHuman->m_HumanBoneIndex[s_HumanGoalInfo[goalIter].m_Index];
+
+ math::float4 q = AddAxis(apHuman,index,apSkeletonPoseWorkspaceA->m_X[index].q);
+
+ q = math::quatLerp(q,apHumanPose->m_GoalArray[goalIter].m_X.q,math::float1(weightR));
+
+ q = RemoveAxis(apHuman,index,q);
+
+ ReachGoalRotation(apHuman,q,goalIter,apSkeletonPose,apSkeletonPoseWorkspaceA,apSkeletonPoseWorkspaceB);
+ }
+ }
+
+ /* no finger ik for 4.0
+ if(apHuman->m_HasLeftHand)
+ {
+ if(apHumanPose->m_LeftHandPose.m_Grab > 0)
+ {
+ hand::HandPose pose;
+ hand::HandPoseSolve(&apHumanPose->m_LeftHandPose,&pose);
+ hand::Hand2SkeletonPose(apHuman->m_LeftHand,apHuman->m_Skeleton.Get(),&pose,apSkeletonPose);
+
+ skeleton::SkeletonPoseComputeGlobal(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseWorkspace);
+
+ hand::Hand *hand = apHuman->m_LeftHand;
+
+ float wArray[hand::kLastFinger];
+ math::float4 posArray[hand::kLastFinger];
+
+ hand::FingerTipsFromPose(hand,apHuman->m_Skeleton.Get(),apSkeletonPoseWorkspace,posArray);
+
+ for(int i = 0; i < hand::kLastFinger; i++)
+ {
+ posArray[i] = SphereCollide(apHumanPose->m_LeftHandPose.m_GrabX,posArray[i]);
+ wArray[i] = apHumanPose->m_LeftHandPose.m_Grab;
+ }
+
+ hand::FingersIKSolve(hand,apHuman->m_Skeleton.Get(),posArray,wArray,apSkeletonPose,apSkeletonPoseWorkspace);
+ }
+ }
+
+ if(apHuman->m_HasRightHand)
+ {
+ if(apHumanPose->m_RightHandPose.m_Grab > 0)
+ {
+ hand::HandPose pose;
+ hand::HandPoseSolve(&apHumanPose->m_RightHandPose,&pose);
+ hand::Hand2SkeletonPose(apHuman->m_RightHand,apHuman->m_Skeleton.Get(),&pose,apSkeletonPose);
+
+ skeleton::SkeletonPoseComputeGlobal(apHuman->m_Skeleton.Get(),apSkeletonPose,apSkeletonPoseWorkspace);
+
+ hand::Hand *hand = apHuman->m_RightHand;
+
+ float wArray[hand::kLastFinger];
+ math::float4 posArray[hand::kLastFinger];
+
+ hand::FingerTipsFromPose(hand,apHuman->m_Skeleton.Get(),apSkeletonPoseWorkspace,posArray);
+
+ for(int i = 0; i < hand::kLastFinger; i++)
+ {
+ posArray[i] = SphereCollide(apHumanPose->m_RightHandPose.m_GrabX,posArray[i]);
+ wArray[i] = apHumanPose->m_RightHandPose.m_Grab;
+ }
+
+ hand::FingersIKSolve(hand,apHuman->m_Skeleton.Get(),posArray,wArray,apSkeletonPose,apSkeletonPoseWorkspace);
+ }
+ }
+ */
+ }
+
+ void TwistSolve(Human const *apHuman, skeleton::SkeletonPose *apSkeletonPose, skeleton::SkeletonPose *skeletonPoseWorkspace)
+ {
+ const math::float1 foreArmTwist(apHuman->m_ForeArmTwist);
+ const math::float1 armTwist(apHuman->m_ArmTwist);
+ const math::float1 legTwist(apHuman->m_LegTwist);
+ const math::float1 upperLegTwist(apHuman->m_UpperLegTwist);
+
+ HumanFixTwist(apHuman,apSkeletonPose,skeletonPoseWorkspace,kLeftLowerArm,kLeftHand,foreArmTwist);
+ HumanFixTwist(apHuman,apSkeletonPose,skeletonPoseWorkspace,kLeftUpperArm,kLeftLowerArm,armTwist);
+
+ HumanFixTwist(apHuman,apSkeletonPose,skeletonPoseWorkspace,kRightLowerArm,kRightHand,foreArmTwist);
+ HumanFixTwist(apHuman,apSkeletonPose,skeletonPoseWorkspace,kRightUpperArm,kRightLowerArm,armTwist);
+
+ HumanFixTwist(apHuman,apSkeletonPose,skeletonPoseWorkspace,kLeftLowerLeg,kLeftFoot,legTwist);
+ HumanFixTwist(apHuman,apSkeletonPose,skeletonPoseWorkspace,kLeftUpperLeg,kLeftLowerLeg,upperLegTwist);
+
+ HumanFixTwist(apHuman,apSkeletonPose,skeletonPoseWorkspace,kRightLowerLeg,kRightFoot,legTwist);
+ HumanFixTwist(apHuman,apSkeletonPose,skeletonPoseWorkspace,kRightUpperLeg,kRightLowerLeg,upperLegTwist);
+ }
+
+ float ComputeHierarchicMass(int32_t aBoneIndex,float *apMassArray)
+ {
+ apMassArray[aBoneIndex] = HumanBoneDefaultMass[aBoneIndex];
+
+ for(int childIter = 0; childIter < BoneChildren[aBoneIndex][0]; childIter++)
+ {
+ apMassArray[aBoneIndex] += ComputeHierarchicMass(BoneChildren[aBoneIndex][1+childIter],apMassArray);
+ }
+
+ return apMassArray[aBoneIndex];
+ }
+
+ float DeltaPoseQuality(HumanPose &arDeltaPose, float aTol)
+ {
+ float massArray[kLastBone];
+ ComputeHierarchicMass(0,massArray);
+
+ float q = 0;
+ float sumW = 0;
+
+ for(int dofIter = 0; dofIter < kLastDoF; dofIter++)
+ {
+ int32_t boneIndex = DoF2Bone[dofIter];
+
+ float v = math::saturate((aTol - math::abs(arDeltaPose.m_DoFArray[dofIter]))/aTol);
+
+ q += v * massArray[boneIndex];
+
+ sumW += massArray[boneIndex];
+ }
+
+ return q / sumW;
+ }
+
+} // namespace human
+
+}
diff --git a/Runtime/mecanim/human/human.h b/Runtime/mecanim/human/human.h
new file mode 100644
index 0000000..5f990b5
--- /dev/null
+++ b/Runtime/mecanim/human/human.h
@@ -0,0 +1,390 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/bitset.h"
+#include "Runtime/Math/Simd/xform.h"
+#include "Runtime/mecanim/math/axes.h"
+#include "Runtime/mecanim/math/collider.h"
+
+#include "Runtime/mecanim/human/handle.h"
+#include "Runtime/mecanim/human/hand.h"
+
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Animation/MecanimArraySerialization.h"
+
+namespace mecanim
+{
+
+namespace skeleton { struct Skeleton; struct SkeletonPose; }
+
+namespace human
+{
+ enum Bones
+ {
+ kHips = 0,
+ kLeftUpperLeg,
+ kRightUpperLeg,
+ kLeftLowerLeg,
+ kRightLowerLeg,
+ kLeftFoot,
+ kRightFoot,
+ kSpine,
+ kChest,
+ kNeck,
+ kHead,
+ kLeftShoulder,
+ kRightShoulder,
+ kLeftUpperArm,
+ kRightUpperArm,
+ kLeftLowerArm,
+ kRightLowerArm,
+ kLeftHand,
+ kRightHand,
+ kLeftToes,
+ kRightToes,
+ kLeftEye,
+ kRightEye,
+ kJaw,
+ kLastBone
+ };
+
+ enum BodyDoF
+ {
+ kSpineFrontBack = 0,
+ kSpineLeftRight,
+ kSpineRollLeftRight,
+ kChestFrontBack,
+ kChestLeftRight,
+ kChestRollLeftRight,
+ kLastBodyDoF
+ };
+
+ enum HeadDoF
+ {
+ kNeckFrontBack = 0,
+ kNeckLeftRight,
+ kNeckRollLeftRight,
+ kHeadFrontBack,
+ kHeadLeftRight,
+ kHeadRollLeftRight,
+ kLeftEyeDownUp,
+ kLeftEyeLeftRight,
+ kRightEyeDownUp,
+ kRightEyeLeftRight,
+ kJawDownUp,
+ kJawLeftRight,
+ kLastHeadDoF
+ };
+
+ enum LegDoF
+ {
+ kUpperLegFrontBack = 0,
+ kUpperLegInOut,
+ kUpperLegRollInOut,
+ kLegCloseOpen,
+ kLegRollInOut,
+ kFootCloseOpen,
+ kFootInOut,
+ kToesUpDown,
+ kLastLegDoF
+ };
+
+ enum ArmDoF
+ {
+ kShoulderDownUp = 0,
+ kShoulderFrontBack,
+ kArmDownUp,
+ kArmFrontBack,
+ kArmRollInOut,
+ kForeArmCloseOpen,
+ kForeArmRollInOut,
+ kHandDownUp,
+ kHandInOut,
+ kLastArmDoF
+ };
+
+ enum DoF
+ {
+ kBodyDoFStart = 0,
+ kHeadDoFStart = kBodyDoFStart + kLastBodyDoF,
+ kLeftLegDoFStart = kHeadDoFStart + kLastHeadDoF,
+ kRightLegDoFStart = kLeftLegDoFStart + kLastLegDoF,
+ kLeftArmDoFStart = kRightLegDoFStart + kLastLegDoF,
+ kRightArmDoFStart = kLeftArmDoFStart + kLastArmDoF,
+ kLastDoF = kRightArmDoFStart + kLastArmDoF
+ };
+
+ enum Goal
+ {
+ kLeftFootGoal,
+ kRightFootGoal,
+ kLeftHandGoal,
+ kRightHandGoal,
+ kLastGoal
+ };
+
+ struct GoalInfo
+ {
+ int32_t m_Index;
+ int32_t m_TopIndex;
+ int32_t m_MidIndex;
+ int32_t m_EndIndex;
+ };
+
+ const static GoalInfo s_HumanGoalInfo[kLastGoal] =
+ {
+ { kLeftFoot, kLeftUpperLeg, kLeftLowerLeg, kLeftFoot },
+ { kRightFoot, kRightUpperLeg, kRightLowerLeg, kRightFoot },
+ { kLeftHand, kLeftUpperArm, kLeftLowerArm, kLeftHand },
+ { kRightHand, kRightUpperArm, kRightLowerArm, kRightHand }
+ };
+
+ enum HumanPoseMaskInfo
+ {
+ kMaskRootIndex = 0,
+ kMaskDoFStartIndex = kMaskRootIndex + 1,
+ kMaskGoalStartIndex = kMaskDoFStartIndex + kLastDoF,
+ kMaskLeftHand = kMaskGoalStartIndex + kLastGoal,
+ kMaskRightHand = kMaskLeftHand + 1,
+ kLastMaskIndex = kMaskRightHand +1
+ };
+
+ typedef mecanim::bitset<kLastMaskIndex> HumanPoseMask;
+
+ bool MaskHasLegs(const HumanPoseMask& mask);
+
+ HumanPoseMask FullBodyMask();
+
+ struct Human
+ {
+ DEFINE_GET_TYPESTRING(Human)
+
+ Human();
+
+ math::xform m_RootX;
+
+ OffsetPtr<skeleton::Skeleton> m_Skeleton;
+ OffsetPtr<skeleton::SkeletonPose> m_SkeletonPose;
+ OffsetPtr<hand::Hand> m_LeftHand;
+ OffsetPtr<hand::Hand> m_RightHand;
+
+ uint32_t m_HandlesCount;
+ OffsetPtr<human::Handle> m_Handles;
+
+ uint32_t m_ColliderCount;
+ OffsetPtr<math::Collider> m_ColliderArray;
+
+ int32_t m_HumanBoneIndex[kLastBone];
+ float m_HumanBoneMass[kLastBone];
+ int32_t m_ColliderIndex[kLastBone];
+
+
+ float m_Scale;
+
+ float m_ArmTwist;
+ float m_ForeArmTwist;
+ float m_UpperLegTwist;
+ float m_LegTwist;
+
+ float m_ArmStretch;
+ float m_LegStretch;
+
+ float m_FeetSpacing;
+
+ bool m_HasLeftHand;
+ bool m_HasRightHand;
+
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_RootX);
+
+ TRANSFER(m_Skeleton);
+ TRANSFER(m_SkeletonPose);
+ TRANSFER(m_LeftHand);
+ TRANSFER(m_RightHand);
+
+ TRANSFER_BLOB_ONLY(m_HandlesCount);
+ MANUAL_ARRAY_TRANSFER2(Handle, m_Handles, m_HandlesCount);
+
+ TRANSFER_BLOB_ONLY(m_ColliderCount);
+ MANUAL_ARRAY_TRANSFER2(math::Collider, m_ColliderArray, m_ColliderCount);
+
+ STATIC_ARRAY_TRANSFER(mecanim::int32_t, m_HumanBoneIndex, kLastBone);
+ STATIC_ARRAY_TRANSFER(float, m_HumanBoneMass, kLastBone);
+ STATIC_ARRAY_TRANSFER(mecanim::int32_t, m_ColliderIndex, kLastBone);
+
+ TRANSFER(m_Scale);
+
+ TRANSFER(m_ArmTwist);
+ TRANSFER(m_ForeArmTwist);
+ TRANSFER(m_UpperLegTwist);
+ TRANSFER(m_LegTwist);
+
+ TRANSFER(m_ArmStretch);
+ TRANSFER(m_LegStretch);
+
+ TRANSFER(m_FeetSpacing);
+
+ TRANSFER(m_HasLeftHand);
+ TRANSFER(m_HasRightHand);
+ transfer.Align();
+ }
+ };
+
+ struct HumanGoal
+ {
+ DEFINE_GET_TYPESTRING(HumanGoal)
+
+ HumanGoal() : m_WeightT(0.0f), m_WeightR(0.0f) {};
+
+ math::xform m_X;
+ float m_WeightT;
+ float m_WeightR;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_X);
+ TRANSFER(m_WeightT);
+ TRANSFER(m_WeightR);
+ }
+ };
+
+ struct HumanPose
+ {
+ DEFINE_GET_TYPESTRING(HumanPose)
+
+ HumanPose();
+
+ math::xform m_RootX;
+ math::float4 m_LookAtPosition;
+ math::float4 m_LookAtWeight;
+
+ HumanGoal m_GoalArray[kLastGoal];
+ hand::HandPose m_LeftHandPose;
+ hand::HandPose m_RightHandPose;
+
+ float m_DoFArray[kLastDoF];
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_RootX);
+ TRANSFER(m_LookAtPosition);
+ TRANSFER(m_LookAtWeight);
+
+ STATIC_ARRAY_TRANSFER(HumanGoal, m_GoalArray, kLastGoal);
+ TRANSFER(m_LeftHandPose);
+ TRANSFER(m_RightHandPose);
+
+ STATIC_ARRAY_TRANSFER(float, m_DoFArray, kLastDoF);
+ }
+ };
+
+ int32_t MuscleFromBone(int32_t boneIndex, int32_t doFIndex);
+ int32_t BoneFromMuscle(int32_t doFIndex);
+
+ bool RequiredBone(uint32_t boneIndex);
+ const char* BoneName(uint32_t boneIndex);
+ const char* MuscleName(uint32_t boneIndex);
+
+ Human* CreateHuman(skeleton::Skeleton *skeleton,skeleton::SkeletonPose *skeletonPose, uint32_t handlesCount, uint32_t colliderCount, memory::Allocator& alloc);
+ void DestroyHuman(Human *human, memory::Allocator& alloc);
+
+ void HumanAdjustMass(Human *human);
+ void HumanSetupAxes(Human *human, skeleton::SkeletonPose const *skeletonPoseGlobal);
+ void HumanSetupCollider(Human *human, skeleton::SkeletonPose const *skeletonPoseGlobal);
+ void HumanCopyAxes(Human const *srcHuman, Human *human);
+ math::Axes GetAxes(Human const *human, int32_t boneIndex);
+ void GetMuscleRange(Human const *apHuman, int32_t aDoFIndex, float &aMin, float &aMax);
+ math::float4 AddAxis(Human const *human, int32_t index, math::float4 const &q);
+ math::float4 RemoveAxis(Human const *human, int32_t index, const math::float4 &q);
+ math::xform NormalizedHandleX(Human const *human, int32_t handleIndex);
+
+ math::float4 HumanComputeBoneMassCenter(Human const *human, skeleton::SkeletonPose const *skeletonPoseGlobal, int32_t boneIndex);
+ math::float4 HumanComputeMassCenter(Human const *human, skeleton::SkeletonPose const *skeletonPoseGlobal);
+ float HumanComputeMomentumOfInertia(Human const *human, skeleton::SkeletonPose const *skeletonPoseGlobal);
+ math::float4 HumanComputeOrientation(Human const* human,skeleton::SkeletonPose const* apPoseGlobal);
+ math::xform HumanComputeRootXform(Human const* human,skeleton::SkeletonPose const* apPoseGlobal);
+ float HumanGetFootHeight(Human const* human, bool left);
+ math::float4 HumanGetFootBottom(Human const* human, bool left);
+ math::xform HumanGetColliderXform(Human const* human, math::xform const& x, int32_t boneIndex);
+ math::xform HumanSubColliderXform(Human const* human, math::xform const& x, int32_t boneIndex);
+ math::float4 HumanGetGoalOrientationOffset(Goal goalIndex);
+
+ void HumanPoseClear(HumanPose& pose);
+ void HumanPoseCopy(HumanPose &pose,HumanPose const &poseA, bool doFOnly = false);
+ void HumanPoseCopy(HumanPose &pose,HumanPose const &poseA, HumanPoseMask const &humanPoseMask);
+ void HumanPoseAdd(HumanPose &pose,HumanPose const &poseA,HumanPose const &poseB);
+ void HumanPoseSub(HumanPose &pose,HumanPose const &poseA,HumanPose const &poseB);
+ void HumanPoseWeight(HumanPose &pose,HumanPose const &poseA, float weight);
+ void HumanPoseMirror(HumanPose &pose,HumanPose const &poseA);
+ void HumanPoseBlend(HumanPose &pose, HumanPose **poseArray, float *weightArray, uint32_t count);
+ void HumanPoseAddOverrideLayer(HumanPose &poseBase,HumanPose const &pose, float weight, HumanPoseMask const &humanPoseMask);
+ void HumanPoseAddAdditiveLayer(HumanPose &poseBase,HumanPose const &pose, float weight, HumanPoseMask const &humanPoseMask);
+
+ void RetargetFrom( Human const *human,
+ skeleton::SkeletonPose const *skeletonPose,
+ HumanPose *humanPose,
+ skeleton::SkeletonPose *skeletonPoseWsRef,
+ skeleton::SkeletonPose *skeletonPoseWsGbl,
+ skeleton::SkeletonPose *skeletonPoseWsLcl,
+ skeleton::SkeletonPose *skeletonPoseWsWs,
+ int32_t maxFixIter = 5);
+
+ void RetargetTo( Human const *human,
+ HumanPose const *humanPoseBase,
+ HumanPose const *humanPose,
+ const math::xform &x,
+ HumanPose *humanPoseOut,
+ skeleton::SkeletonPose *skeletonPose,
+ skeleton::SkeletonPose *skeletonPoseWs);
+
+ // apSkeletonPoseWorkspace must be set to global pose before calling
+ void FullBodySolve(Human const *human, HumanPose const *humanPose, skeleton::SkeletonPose *skeletonPose, skeleton::SkeletonPose *skeletonPoseWorkspaceA, skeleton::SkeletonPose *skeletonPoseWorkspaceB);
+ void TwistSolve(Human const *human, skeleton::SkeletonPose *skeletonPose, skeleton::SkeletonPose *skeletonPoseWorkspace);
+
+ float DeltaPoseQuality(HumanPose &deltaPose, float tolerance = 0.15f);
+
+ skeleton::SetupAxesInfo const& GetAxeInfo(uint32_t index);
+
+}// namespace human
+
+}
+
+template<>
+class SerializeTraits< mecanim::human::HumanPoseMask > : public SerializeTraitsBase< mecanim::human::HumanPoseMask >
+{
+public:
+
+ inline static const char* GetTypeString (value_type*) { return "HumanPoseMask"; }
+ inline static bool IsAnimationChannel () { return false; }
+ inline static bool MightContainPPtr () { return false; }
+ inline static bool AllowTransferOptimization () { return true; }
+ inline static bool IsContinousMemoryArray () { return true; }
+
+ typedef mecanim::human::HumanPoseMask value_type;
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ transfer.Transfer(data.word(0), "word0");
+ if(1<value_type::Words+1)
+ transfer.Transfer(data.word(1), "word1");
+ if(2<value_type::Words+1)
+ transfer.Transfer(data.word(2), "word2");
+ if(3<value_type::Words+1)
+ transfer.Transfer(data.word(3), "word3");
+ }
+
+ static void resource_image_assign_external (value_type& data, void* begin, void* end)
+ {
+ }
+};
+
+
diff --git a/Runtime/mecanim/math/axes.h b/Runtime/mecanim/math/axes.h
new file mode 100644
index 0000000..b495ea7
--- /dev/null
+++ b/Runtime/mecanim/math/axes.h
@@ -0,0 +1,128 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/Math/Simd/math.h"
+#include "Runtime/Math/Simd/quaternion.h"
+
+namespace math
+{
+ struct Limit
+ {
+ DEFINE_GET_TYPESTRING(Limit)
+
+ float4 m_Min; // m_Min > 0 -> free, m_Min == 0 -> lock, m_Min < 0 -> limit
+ float4 m_Max; // m_Max < 0 -> free, m_Max == 0 -> lock, m_Max > 0 -> limit
+
+ inline Limit() : m_Min(1,1,1,1), m_Max(-1,-1,-1,-1) {}
+ inline Limit(float4 const& aMin, float4 const& aMax, float aRange) { m_Min = aMin; m_Max = aMax; }
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_Min);
+ TRANSFER(m_Max);
+ }
+ };
+
+ enum AxesType { kFull, kZYRoll, kRollZY, kEulerXYZ };
+
+ struct Axes
+ {
+ DEFINE_GET_TYPESTRING(Axes)
+
+ float4 m_PreQ;
+ float4 m_PostQ;
+ float4 m_Sgn;
+ Limit m_Limit;
+ float m_Length;
+ mecanim::uint32_t m_Type; // AxesType
+
+ inline Axes() : m_PreQ(0,0,0,1), m_PostQ(0,0,0,1), m_Sgn(1,1,1,1), m_Length(1), m_Type(kEulerXYZ) {}
+ inline Axes(float4 const& aPreQ, float4 const& aPostQ, float4 const& aSgn, float const &aLength, AxesType const& aType) { m_PreQ = aPreQ; m_PostQ = aPostQ; m_Sgn = aSgn; m_Length = aLength; m_Type = aType; }
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_PreQ);
+ TRANSFER(m_PostQ);
+ TRANSFER(m_Sgn);
+ TRANSFER(m_Limit);
+ TRANSFER(m_Length);
+ TRANSFER(m_Type);
+ }
+ };
+
+ STATIC_INLINE float4 LimitSmootClamp(Limit const& l, float4 const& v, float1 const& smoothRange)
+ {
+ const float4 min = cond(l.m_Min<float1::zero(), -smoothClamp(-v,float4::one(),smoothRange), cond(l.m_Min>float1::zero(),v,float4::zero()));
+ const float4 max = cond(l.m_Max>float1::zero(), smoothClamp(v,float4::one(),smoothRange), cond(l.m_Max<float1::zero(),v,float4::zero()));
+ return math::cond(v<float1::zero(),min,max);
+ }
+
+ STATIC_INLINE float LimitProject(float min, float max, float v)
+ {
+ float i = min < 0 ? -v / min : min > 0 ? v : 0;
+ float a = max > 0 ? +v / max : max < 0 ? v : 0;
+ return v < 0 ? i : a;
+ }
+
+ STATIC_INLINE float LimitUnproject(float min, float max, float v)
+ {
+ float i = min < 0 ? -v * min : min > 0 ? v : 0;
+ float a = max > 0 ? +v * max : max < 0 ? v : 0;
+ return v < 0 ? i : a;
+ }
+
+ STATIC_INLINE float4 LimitProject(Limit const& l, float4 const& v)
+ {
+ const float4 min = cond(l.m_Min<float1::zero(),-v/l.m_Min,cond(l.m_Min>float1::zero(),v,float4::zero()));
+ const float4 max = cond(l.m_Max>float1::zero(),+v/l.m_Max,cond(l.m_Max<float1::zero(),v,float4::zero()));
+ return math::cond(v<float1::zero(),min,max);
+ }
+
+ STATIC_INLINE float4 LimitUnproject(Limit const& l, float4 const& v)
+ {
+ const float4 min = cond(l.m_Min<float1::zero(),-v*l.m_Min,cond(l.m_Min>float1::zero(),v,float4::zero()));
+ const float4 max = cond(l.m_Max>float1::zero(),+v*l.m_Max,cond(l.m_Max<float1::zero(),v,float4::zero()));
+ return math::cond(v<float1::zero(),min,max);
+ }
+
+ STATIC_INLINE float4 AxesProject(Axes const& a, float4 const& q)
+ {
+ return normalize(quatMul(quatConj(a.m_PreQ),quatMul(q,a.m_PostQ)));
+ }
+
+ STATIC_INLINE float4 AxesUnproject(Axes const& a, float4 const& q)
+ {
+ return normalize(quatMul(a.m_PreQ,quatMul(q,quatConj(a.m_PostQ))));
+ }
+
+ STATIC_INLINE float4 ToAxes(Axes const& a, float4 const& q)
+ {
+ const float4 qp = AxesProject(a,q);
+ float4 xyz;
+ switch(a.m_Type)
+ {
+ case kEulerXYZ: xyz = LimitProject(a.m_Limit, quatQuatToEuler(qp)); break;
+ case kZYRoll: xyz = LimitProject(a.m_Limit,doubleAtan(quat2ZYRoll(qp)*sgn(a.m_Sgn))); break;
+ case kRollZY: xyz = LimitProject(a.m_Limit,doubleAtan(quat2RollZY(qp)*sgn(a.m_Sgn))); break;
+ default: xyz = LimitProject(a.m_Limit,doubleAtan(quat2Qtan(qp)*sgn(a.m_Sgn))); break;
+ };
+ return xyz;
+ }
+
+ STATIC_INLINE float4 FromAxes(Axes const& a, float4 const& uvw)
+ {
+ float4 q;
+ switch(a.m_Type)
+ {
+ case kEulerXYZ: q = quatEulerToQuat(uvw); break;
+ case kZYRoll: q = ZYRoll2Quat(halfTan(LimitUnproject(a.m_Limit,uvw))*sgn(a.m_Sgn)); break;
+ case kRollZY: q = RollZY2Quat(halfTan(LimitUnproject(a.m_Limit,uvw))*sgn(a.m_Sgn)); break;
+ default: q = qtan2Quat(halfTan(LimitUnproject(a.m_Limit,uvw))*sgn(a.m_Sgn)); break;
+ };
+
+ return AxesUnproject(a,q);
+ }
+}
diff --git a/Runtime/mecanim/math/collider.h b/Runtime/mecanim/math/collider.h
new file mode 100644
index 0000000..234281a
--- /dev/null
+++ b/Runtime/mecanim/math/collider.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/Math/Simd/xform.h"
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+namespace math
+{
+ enum ColliderType { kNone = 0, kCube, kSphere, kCylinder, kCapsule };
+ enum JointType { kIgnored = 0, kLocked, kLimited };
+
+ struct Collider
+ {
+ DEFINE_GET_TYPESTRING(Collider)
+
+ xform m_X;
+ mecanim::uint32_t m_Type; // ColliderType
+
+ inline Collider() : m_Type(kCube) {}
+
+ // Joint information
+ mecanim::uint32_t m_XMotionType;
+ mecanim::uint32_t m_YMotionType;
+ mecanim::uint32_t m_ZMotionType;
+ float m_MinLimitX;
+ float m_MaxLimitX;
+ float m_MaxLimitY;
+ float m_MaxLimitZ;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_X);
+ TRANSFER(m_Type);
+ TRANSFER(m_XMotionType);
+ TRANSFER(m_YMotionType);
+ TRANSFER(m_ZMotionType);
+ TRANSFER(m_MinLimitX);
+ TRANSFER(m_MaxLimitX);
+ TRANSFER(m_MaxLimitY);
+ TRANSFER(m_MaxLimitZ);
+ }
+ };
+
+ STATIC_INLINE float4 SphereCollide(math::xform const &sphereX,math::float4 const &pos)
+ {
+ math::float4 ret = math::xformInvMulVec(sphereX,pos);
+
+ ret *= math::rcp(math::length(ret));
+
+ return math::xformMulVec(sphereX,ret);
+ }
+}
diff --git a/Runtime/mecanim/memory.h b/Runtime/mecanim/memory.h
new file mode 100644
index 0000000..5331051
--- /dev/null
+++ b/Runtime/mecanim/memory.h
@@ -0,0 +1,618 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/FileUtilities.h"
+
+#include "Runtime/Allocator/MemoryManager.h"
+
+#include <assert.h>
+#include <new>
+
+#if ENABLE_MECANIM_PROFILER
+#include <fstream>
+#include <stack>
+#include <string.h>
+#include <stdlib.h>
+#include <ctime>
+#endif
+
+template <typename TYPE> class OffsetPtr;
+
+namespace mecanim
+{
+
+namespace memory
+{
+#if ENABLE_MECANIM_PROFILER
+ class Profiler
+ {
+ public:
+
+ struct AllocationInfo
+ {
+ AllocationInfo():m_Size(0),m_Allocator("Unknow"){}
+ AllocationInfo(size_t size, char const* allocator):m_Size(size),m_Allocator(allocator){}
+ bool operator==(AllocationInfo const& allocationInfo)const
+ {
+ return m_Size == allocationInfo.m_Size && strcmp(m_Allocator, allocationInfo.m_Allocator) == 0;
+ }
+ size_t m_Size;
+ char const* m_Allocator;
+ };
+
+
+ typedef std::map<void*, AllocationInfo> AllocationInfos;
+ struct ProfilerLabel
+ {
+ ProfilerLabel():m_Label("Unknow"), m_ExclusiveSize(0),m_InclusiveSize(0),m_Parent(0),m_Count(0){}
+ ProfilerLabel(char const* labelId):m_Label(labelId), m_ExclusiveSize(0),m_InclusiveSize(0),m_Parent(0),m_Count(0){}
+ ~ProfilerLabel()
+ {
+ for(int i=0;i<m_Children.size();i++)
+ {
+ m_Children[i]->~ProfilerLabel();
+ MemoryManager::LowLevelFree(m_Children[i]);
+ }
+
+ m_Children.clear();
+ }
+
+ char const* m_Label;
+ size_t m_ExclusiveSize;
+ size_t m_InclusiveSize;
+ size_t m_Count;
+ AllocationInfos m_Allocations;
+
+ ProfilerLabel* m_Parent;
+ std::vector<ProfilerLabel*> m_Children;
+ };
+
+ Profiler():m_Root("Root"){}
+ ~Profiler(){}
+
+ static void StaticInitialize()
+ {
+ s_Profiler = UNITY_NEW(Profiler, kMemDefault)();
+ s_Profiler->m_Current = &s_Profiler->m_Root;
+ }
+
+ static void StaticDestroy()
+ {
+ UNITY_DELETE(s_Profiler, kMemDefault);
+ }
+
+ void PushLabel(char const * label)
+ {
+ m_LabelStack.push(label);
+ LabelMap::iterator it = m_Label.find(label);
+ if(it == m_Label.end())
+ it = m_Label.insert( std::make_pair(label, ProfilerLabel(label) ) ).first;
+
+ AssertIf(it == m_Label.end());
+ it->second.m_Count++;
+
+ s_Profiler->m_Current->m_Children.push_back( new (MemoryManager::LowLevelAllocate(sizeof(ProfilerLabel))) ProfilerLabel(label) );
+ ProfilerLabel* newLabel = s_Profiler->m_Current->m_Children.back();
+ newLabel->m_Parent = s_Profiler->m_Current;
+ s_Profiler->m_Current = newLabel;
+ }
+
+ void PopLabel()
+ {
+ m_LabelStack.pop();
+
+ ProfilerLabel* parentLabel = s_Profiler->m_Current->m_Parent;
+
+ // If not a single allocation had been made, remove it
+ if(s_Profiler->m_Current->m_Allocations.size() == 0 && s_Profiler->m_Current->m_Children.size() == 0)
+ {
+ parentLabel->m_Children[parentLabel->m_Children.size()-1]->~ProfilerLabel();
+ MemoryManager::LowLevelFree(parentLabel->m_Children[parentLabel->m_Children.size()-1]);
+
+ parentLabel->m_Children.resize( parentLabel->m_Children.size()-1 );
+ }
+
+ s_Profiler->m_Current = parentLabel;
+
+ assert(s_Profiler->m_Current != NULL);
+ }
+
+ void RegisterAllocation(void* ptr, char const* alloc, size_t size = 0)
+ {
+ AssertIf(m_LabelStack.empty());
+ char const* label = m_LabelStack.empty() ? "Unknow" : m_LabelStack.top();
+
+ std::map<char const*, ProfilerLabel>::iterator it = m_Label.find(label);
+ if(it == m_Label.end())
+ m_Label.insert( std::make_pair(label, ProfilerLabel(label) ) );
+
+ m_Label[label].m_Allocations.insert( std::make_pair(ptr, AllocationInfo(size, alloc) ) );
+ m_Label[label].m_ExclusiveSize += size;
+
+ s_Profiler->m_Current->m_Allocations.insert( std::make_pair(ptr, AllocationInfo(size, alloc) ) );
+ s_Profiler->m_Current->m_ExclusiveSize += size;
+ s_Profiler->m_Current->m_InclusiveSize += size;
+
+ ProfilerLabel* profilerLabel = s_Profiler->m_Current->m_Parent;
+ while(profilerLabel!=NULL)
+ {
+ profilerLabel->m_InclusiveSize += size;
+ profilerLabel = profilerLabel->m_Parent;
+ }
+
+ m_PointerRegister.insert( std::make_pair(ptr, std::make_pair( &m_Label[label], s_Profiler->m_Current)));
+ }
+
+ void UnregisterAllocation(void* ptr)
+ {
+ if(ptr == NULL)
+ return;
+
+ PointerRegister::iterator it = m_PointerRegister.find(ptr);
+
+ assert(it!=m_PointerRegister.end());
+
+ if(it!=m_PointerRegister.end())
+ {
+ ProfilerLabel* label = it->second.first;
+ AllocationInfos::iterator itAlloc = label->m_Allocations.find(ptr);
+ if(itAlloc!=label->m_Allocations.end())
+ {
+ label->m_ExclusiveSize -= itAlloc->second.m_Size;
+ label->m_Allocations.erase(itAlloc);
+ if(label->m_Allocations.size() == 0 )
+ m_Label.erase( m_Label.find(label->m_Label) );
+ }
+
+ label = it->second.second;
+ itAlloc = label->m_Allocations.find(ptr);
+ if(itAlloc != label->m_Allocations.end())
+ {
+ size_t size = itAlloc->second.m_Size;
+ label->m_ExclusiveSize -= size;
+ label->m_InclusiveSize -= size;
+ label->m_Allocations.erase(itAlloc);
+
+ ProfilerLabel* parentLabel = label->m_Parent;
+
+ if(parentLabel != NULL && label->m_Allocations.size() == 0 && label->m_Children.size() == 0)
+ {
+ parentLabel->m_Children.erase( std::find(parentLabel->m_Children.begin(), parentLabel->m_Children.end(), label ));
+ label->~ProfilerLabel();
+ MemoryManager::LowLevelFree(label);
+ }
+
+ while(parentLabel!=NULL)
+ {
+ label = parentLabel;
+ parentLabel = label->m_Parent;
+
+ label->m_InclusiveSize -= size;
+ if(parentLabel != NULL && label->m_Allocations.size() == 0 && label->m_Children.size() == 0)
+ {
+ parentLabel->m_Children.erase( std::find(parentLabel->m_Children.begin(), parentLabel->m_Children.end(), label ));
+ label->~ProfilerLabel();
+ MemoryManager::LowLevelFree(label);
+ }
+ }
+ }
+
+ m_PointerRegister.erase(it);
+ }
+ }
+
+ static Profiler* GetProfiler()
+ {
+ return s_Profiler;
+ }
+
+ void DumpObjectTypeMemoryInfo()
+ {
+ time_t rawtime;
+ struct tm * timeinfo;
+
+ time (&rawtime);
+ timeinfo = localtime (&rawtime);
+
+ string tempDir = Format("%s/ObjectTypeMemoryInfo_%.2d_%.2d_%.2d_%.2dh%.2dm%.2ds.csv",
+ GetApplicationFolder().c_str(),
+ 1900 + timeinfo->tm_year,
+ timeinfo->tm_mon,
+ timeinfo->tm_mday,
+ timeinfo->tm_hour,
+ timeinfo->tm_min,
+ timeinfo->tm_sec);
+ std::ofstream file(tempDir);
+
+ file << "Object, Size, Count, Allocation Count" << std::endl;
+
+ LabelMap::iterator it;
+ for(it=m_Label.begin();it!=m_Label.end();it++)
+ file << it->second.m_Label << "," << it->second.m_ExclusiveSize << "," << it->second.m_Count << "," << it->second.m_Allocations.size() << std::endl;
+
+ file.close();
+ }
+
+ void DumpCallSiteMemoryInfo()
+ {
+ time_t rawtime;
+ struct tm * timeinfo;
+
+ time (&rawtime);
+ timeinfo = localtime (&rawtime);
+
+ string tempDir = Format("%s/CallSiteMemoryInfo_%.2d_%.2d_%.2d_%.2dh%.2dm%.2ds.csv",
+ GetApplicationFolder().c_str(),
+ 1900 + timeinfo->tm_year,
+ timeinfo->tm_mon,
+ timeinfo->tm_mday,
+ timeinfo->tm_hour,
+ timeinfo->tm_min,
+ timeinfo->tm_sec);
+ //std::ofstream file(tempDir);
+ //file.close();
+ }
+
+ protected:
+ static Profiler* s_Profiler;
+
+ std::stack<char const*> m_LabelStack;
+
+ typedef std::map<char const*, ProfilerLabel> LabelMap;
+ LabelMap m_Label;
+
+ typedef std::map<void *, std::pair<ProfilerLabel*, ProfilerLabel*> > PointerRegister;
+ PointerRegister m_PointerRegister;
+
+ ProfilerLabel m_Root;
+ ProfilerLabel* m_Current;
+ };
+#else
+ class Profiler
+ {
+ public:Profiler(){}
+ ~Profiler(){}
+
+ static void StaticInitialize()
+ {
+ s_Profiler = UNITY_NEW(Profiler, kMemDefault)();
+ }
+
+ static void StaticDestroy()
+ {
+ UNITY_DELETE(s_Profiler, kMemDefault);
+ }
+
+ void PushLabel(char const * label){ }
+
+ void PopLabel(){}
+
+ void RegisterAllocation(void* ptr, char const* alloc, size_t size = 0){ }
+
+ void UnregisterAllocation(void* ptr){}
+
+ void DumpObjectTypeMemoryInfo(){}
+
+ void DumpCallSiteMemoryInfo(){}
+
+ static Profiler* GetProfiler()
+ {
+ return s_Profiler;
+ }
+ protected:
+ static Profiler* s_Profiler;
+ };
+#endif
+
+ class AutoScopeProfiler
+ {
+ public:
+ AutoScopeProfiler(char const* label) { Profiler::GetProfiler()->PushLabel(label); }
+ ~AutoScopeProfiler() { Profiler::GetProfiler()->PopLabel();}
+ };
+
+ #define SETPROFILERLABEL(type) mecanim::memory::AutoScopeProfiler scopeProfiler##type(#type)
+
+ class Allocator
+ {
+ public:
+ std::size_t AlignAddress(std::size_t aAddr, std::size_t aAlign)
+ {
+ return aAddr + ((~aAddr + 1U) & (aAlign - 1U));
+ }
+
+ virtual void* Allocate(std::size_t size, std::size_t align) = 0 ;
+ virtual void Deallocate(void * p) = 0;
+
+ template <typename TYPE>
+ TYPE* Construct(std::size_t align = ALIGN_OF(TYPE))
+ {
+
+ char *ptr = reinterpret_cast<char *>(Allocate(sizeof(TYPE), align));
+ return new( (void *) ptr) TYPE;
+ }
+
+ template <typename TYPE>
+ TYPE* ConstructArray(std::size_t count, std::size_t align = ALIGN_OF(TYPE))
+ {
+ if(count > 0)
+ {
+ char *ptr = reinterpret_cast<char *>(Allocate(sizeof(TYPE)*count, align));
+ return new( (void *) ptr) TYPE[count];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ template <typename TYPE>
+ TYPE* ConstructArray(const TYPE* input, std::size_t count, std::size_t align = ALIGN_OF(TYPE))
+ {
+ if(count > 0)
+ {
+ TYPE *ptr = reinterpret_cast<TYPE*> (Allocate(sizeof(TYPE)*count, align));
+ memcpy(ptr, input, sizeof(TYPE)*count);
+ return ptr;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ template <typename TYPE>
+ void Deallocate(OffsetPtr<TYPE>& p)
+ {
+ if(!p.IsNull())
+ Deallocate(p.Get());
+ }
+ };
+
+ // Should be constructed with either:
+ // kMemAnimation
+ // kMemAnimationTemp
+ class MecanimAllocator : public Allocator
+ {
+ MemLabelId m_Label;
+ protected:
+ MecanimAllocator(MecanimAllocator const& e):Allocator(){ m_Label = e.m_Label; m_Label.SetRootHeader(GET_CURRENT_ALLOC_ROOT_HEADER());}
+ MecanimAllocator& operator=(MecanimAllocator const &){ return *this; }
+ public:
+ MecanimAllocator(MemLabelId label):m_Label(label){ m_Label.SetRootHeader(GET_CURRENT_ALLOC_ROOT_HEADER());}
+
+ virtual void* Allocate(std::size_t size, std::size_t align)
+ {
+ void* p = UNITY_MALLOC_ALIGNED(m_Label, size, align);
+
+ Profiler::GetProfiler()->RegisterAllocation(p, "MecanimAllocator", size);
+
+ return p;
+ }
+
+ virtual void Deallocate(void * p)
+ {
+ Profiler::GetProfiler()->UnregisterAllocation(p);
+ UNITY_FREE(m_Label, p);
+ }
+ };
+
+ class InPlaceAllocator : public Allocator
+ {
+ protected:
+ char* mP;
+ char* mHead;
+ std::size_t mMaxSize;
+ InPlaceAllocator(InPlaceAllocator const &):Allocator(){}
+ InPlaceAllocator& operator=(InPlaceAllocator const &){ return *this; }
+ public:
+
+
+ InPlaceAllocator(void *p, std::size_t aSize):mP(reinterpret_cast<char *>(p)),mHead(reinterpret_cast<char *>(p)), mMaxSize(aSize){};
+
+// InPlaceAllocator(size_t size, MemoryLabelId label)
+// {
+// mHead = mP = UNITY_MALLOC_ALIGNED(label, size, 16);
+// mMaxSize = aSize;
+//
+// }
+
+ ~InPlaceAllocator() { }
+
+ inline std::size_t TotalMemorySize()const { return mMaxSize; }
+ inline std::size_t UsedMemorySize()const { return reinterpret_cast<std::size_t>(mP) - reinterpret_cast<std::size_t>(mHead); }
+ inline std::size_t FreeMemorySize()const { return TotalMemorySize() - UsedMemorySize(); }
+
+ void* GetMemory ()
+ {
+ return mP;
+ }
+
+ virtual void* Allocate(std::size_t n, std::size_t align)
+ {
+ char *p = reinterpret_cast<char *>(AlignAddress(reinterpret_cast<std::size_t>(mP), align));
+
+ // The first allocation must always be aligned already
+ AssertIf(mHead == mP && p != mHead);
+
+ // assert(p+n <= mHead+mMaxSize);
+
+ if(p+n <= mHead+mMaxSize)
+ {
+ mP = p+n;
+ return reinterpret_cast<void *>(p);
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ virtual void Deallocate(void * p)
+ {
+ AssertString("Not supported");
+ }
+ };
+
+ class ChainedAllocator : public Allocator
+ {
+ ProfilerAllocationHeader* rootHeader;
+ protected:
+
+ enum {
+ BlockAlign = 16
+ };
+
+ struct MemoryBlock {
+ MemoryBlock* next;
+ uint8_t* headPtr;
+ std::size_t blockSize;
+ };
+
+ ChainedAllocator(ChainedAllocator const&):Allocator(){rootHeader = GET_CURRENT_ALLOC_ROOT_HEADER();}
+ ChainedAllocator& operator=(ChainedAllocator const &){ return *this; }
+
+ std::size_t GetMemBlockSize(std::size_t aSize, std::size_t aAlign)
+ {
+ return sizeof(MemoryBlock) + aSize + aAlign - 1U;
+ }
+
+ std::size_t GetAllocateSize(std::size_t aSize, std::size_t aAlign)
+ {
+ return aSize + aAlign - 1U;
+ }
+
+ MemoryBlock* first;
+ MemoryBlock* current;
+ uint8_t* heapPtr;
+ std::size_t blockSize;
+ public:
+ ChainedAllocator(std::size_t aBlockSize):first(0),current(0),blockSize(aBlockSize)
+ {
+ rootHeader = GET_CURRENT_ALLOC_ROOT_HEADER();
+ }
+
+ ~ChainedAllocator()
+ {
+ Reset();
+ }
+
+ void Init()
+ {
+ if(first == 0)
+ {
+ size_t size = GetMemBlockSize(blockSize, BlockAlign);
+ void *p = UNITY_MALLOC(MemLabelId(kMemAnimationId,rootHeader), size);
+ Profiler::GetProfiler()->RegisterAllocation(p, "ChainedAllocator", size);
+ if(p)
+ {
+ current = first = new(p) MemoryBlock;
+ current->next = 0;
+ current->blockSize = blockSize;
+
+ uint8_t* head = reinterpret_cast<uint8_t*>(p);
+ heapPtr = current->headPtr = reinterpret_cast<uint8_t*>( AlignAddress( reinterpret_cast<std::size_t>(head + sizeof(MemoryBlock)), BlockAlign));
+ }
+ }
+ }
+
+ void Reset()
+ {
+ MemoryBlock* c = first;
+ while(c != 0)
+ {
+ void* p = reinterpret_cast<void*>(c);
+ c = c->next;
+ Profiler::GetProfiler()->UnregisterAllocation(p);
+ UNITY_FREE(kMemAnimation, p);
+ }
+ current = first = 0;
+ heapPtr = 0;
+ }
+
+ void Reserve(std::size_t size)
+ {
+ if(size > 0)
+ {
+ if(first == 0)
+ {
+ size_t size1 = GetMemBlockSize(size, BlockAlign);
+ void *p = UNITY_MALLOC(MemLabelId(kMemAnimationId,rootHeader), size1);
+ Profiler::GetProfiler()->RegisterAllocation(p, "ChainedAllocator", size1);
+
+ if(p)
+ {
+ current = first = new(p) MemoryBlock;
+ current->next = 0;
+ current->blockSize = size;
+
+ uint8_t* head = reinterpret_cast<uint8_t*>(p);
+ heapPtr = current->headPtr = reinterpret_cast<uint8_t*>( AlignAddress( reinterpret_cast<std::size_t>(head + sizeof(MemoryBlock)), BlockAlign));
+ }
+ }
+ else
+ {
+ size_t size1 = GetMemBlockSize(size, BlockAlign);
+ void *p = UNITY_MALLOC(MemLabelId(kMemAnimationId,rootHeader), size1);
+ Profiler::GetProfiler()->RegisterAllocation(p, "ChainedAllocator", size1);
+ if(p)
+ {
+ current->next = new(p) MemoryBlock;
+ current = current->next;
+ current->next = 0;
+ current->blockSize = size > blockSize ? size : blockSize;
+
+ uint8_t* head = reinterpret_cast<uint8_t*>(p);
+ heapPtr = current->headPtr = reinterpret_cast<uint8_t*>( AlignAddress( reinterpret_cast<std::size_t>(head + sizeof(MemoryBlock)), BlockAlign));
+ }
+ }
+ }
+ }
+
+ // Limit case are
+ // no memory left in current block, allocate a new block
+ // no memory left in system, bail out
+ virtual void* Allocate(std::size_t size, std::size_t align)
+ {
+ Init();
+
+ std::size_t s = GetAllocateSize(size, align);
+
+ // Not enough memory left in current block, allocate a new block
+ if( heapPtr + s > current->headPtr + current->blockSize)
+ {
+ // If not enough memory left in system, bail out
+ // If the requested size is bigger than the block size, allocate at least a block big enough for this request.
+ size_t size1 = GetMemBlockSize(size > blockSize ? size : blockSize, BlockAlign);
+ void *p = UNITY_MALLOC(MemLabelId(kMemAnimationId,rootHeader), size1);
+ Profiler::GetProfiler()->RegisterAllocation(p, "ChainedAllocator", size1);
+ if(p)
+ {
+ current->next = new(p) MemoryBlock;
+ current = current->next;
+ current->next = 0;
+ current->blockSize = size > blockSize ? size : blockSize;
+
+ uint8_t* head = reinterpret_cast<uint8_t*>(p);
+ heapPtr = current->headPtr = reinterpret_cast<uint8_t*>( AlignAddress( reinterpret_cast<std::size_t>(head + sizeof(MemoryBlock)), BlockAlign));
+ }
+ else
+ return 0;
+ }
+
+ uint8_t *p = reinterpret_cast<uint8_t *>(AlignAddress(reinterpret_cast<std::size_t>(heapPtr), align));
+ heapPtr = p+size;
+ return reinterpret_cast<void *>(p);
+ }
+
+ virtual void Deallocate(void * p)
+ {
+ assert(true);
+ }
+ };
+
+}
+
+}
diff --git a/Runtime/mecanim/skeleton/skeleton.cpp b/Runtime/mecanim/skeleton/skeleton.cpp
new file mode 100644
index 0000000..5b78677
--- /dev/null
+++ b/Runtime/mecanim/skeleton/skeleton.cpp
@@ -0,0 +1,515 @@
+#include "UnityPrefix.h"
+#include "Runtime/mecanim/skeleton/skeleton.h"
+
+namespace mecanim
+{
+
+namespace skeleton
+{
+ Skeleton* CreateSkeleton(int32_t aNodeCount, int32_t aAxesCount, memory::Allocator& arAlloc)
+ {
+ Skeleton* skeleton = arAlloc.Construct<Skeleton>();
+
+ skeleton->m_Count = aNodeCount;
+ skeleton->m_Node = arAlloc.ConstructArray<Node>(aNodeCount);
+ skeleton->m_ID = arAlloc.ConstructArray<uint32_t>(aNodeCount);
+
+ skeleton->m_AxesCount = aAxesCount;
+ if(skeleton->m_AxesCount)
+ {
+ skeleton->m_AxesArray = arAlloc.ConstructArray<math::Axes>(aAxesCount);
+ }
+
+ return skeleton;
+ }
+
+ SkeletonPose* CreateSkeletonPose(Skeleton const* apSkeleton, memory::Allocator& arAlloc)
+ {
+ SkeletonPose* skeletonPose = arAlloc.Construct<SkeletonPose>();
+
+ skeletonPose->m_Count = apSkeleton->m_Count;
+ skeletonPose->m_X = arAlloc.ConstructArray<math::xform>(apSkeleton->m_Count);
+
+ return skeletonPose;
+ }
+
+ void DestroySkeleton(Skeleton* apSkeleton, memory::Allocator& arAlloc)
+ {
+ if(apSkeleton)
+ {
+ arAlloc.Deallocate(apSkeleton->m_Node);
+ arAlloc.Deallocate(apSkeleton->m_ID);
+ arAlloc.Deallocate(apSkeleton->m_AxesArray);
+
+ arAlloc.Deallocate(apSkeleton);
+ }
+ }
+
+ void DestroySkeletonPose(SkeletonPose* apSkeletonPose, memory::Allocator& arAlloc)
+ {
+ if(apSkeletonPose)
+ {
+ arAlloc.Deallocate(apSkeletonPose->m_X);
+ arAlloc.Deallocate(apSkeletonPose);
+ }
+ }
+
+ SkeletonMask* CreateSkeletonMask(uint32_t aNodeCount, SkeletonMaskElement* elements, memory::Allocator& arAlloc)
+ {
+ SkeletonMask* skeletonMask = arAlloc.Construct<SkeletonMask>();
+
+ skeletonMask->m_Count = aNodeCount;
+ skeletonMask->m_Data = arAlloc.ConstructArray<SkeletonMaskElement>(aNodeCount);
+
+ memcpy(skeletonMask->m_Data.Get(), elements, sizeof(SkeletonMaskElement)*aNodeCount);
+
+ return skeletonMask;
+ }
+
+ void DestroySkeletonMask(SkeletonMask* skeletonMask, memory::Allocator& arAlloc)
+ {
+ if(skeletonMask)
+ {
+ arAlloc.Deallocate(skeletonMask->m_Data);
+ arAlloc.Deallocate(skeletonMask);
+ }
+ }
+
+ void SkeletonCopy(Skeleton const* apSrc, Skeleton* apDst)
+ {
+ apDst->m_Count = apSrc->m_Count;
+ for(int nodeIter = 0; nodeIter < apDst->m_Count; nodeIter++)
+ {
+ apDst->m_Node[nodeIter] = apSrc->m_Node[nodeIter];
+ apDst->m_ID[nodeIter] = apSrc->m_ID[nodeIter];
+ }
+
+ apDst->m_AxesCount = apSrc->m_AxesCount;
+ for(int axesIter = 0; axesIter < apDst->m_AxesCount; axesIter++)
+ {
+ apDst->m_AxesArray[axesIter] = apSrc->m_AxesArray[axesIter];
+ }
+ }
+
+ void SkeletonPoseCopy(SkeletonPose const* apSrcPose, SkeletonPose* apDstPose)
+ {
+ uint32_t count = math::minimum(apSrcPose->m_Count, apDstPose->m_Count);
+ memcpy(&apDstPose->m_X[0], &apSrcPose->m_X[0], count*sizeof(math::xform));
+ }
+
+ void SkeletonPoseCopy(SkeletonPose const* apSrcPose, SkeletonPose* apDstPose, uint32_t aIndexCount, int32_t const *apIndexArray)
+ {
+ for(uint32_t i = 0 ; i < aIndexCount; i++)
+ {
+ int32_t j = apIndexArray[i];
+ apDstPose->m_X[j] = apSrcPose->m_X[i];
+ }
+ }
+
+ int32_t SkeletonFindNode(Skeleton const* apSkeleton, uint32_t aID )
+ {
+ int32_t ret = -1;
+
+ int32_t i;
+ for(i = 0; ret == -1 && i < apSkeleton->m_Count; i++)
+ {
+ if(apSkeleton->m_ID[i] == aID)
+ {
+ ret = i;
+ }
+ }
+
+ return ret;
+ }
+
+ void SkeletonBuildIndexArray(int32_t *indexArray,Skeleton const* apSrcSkeleton,Skeleton const* apDstSkeleton)
+ {
+ for(uint32_t i = 0; i < apSrcSkeleton->m_Count; i++)
+ {
+ indexArray[i] = SkeletonFindNode(apDstSkeleton,apSrcSkeleton->m_ID[i]);
+ }
+ }
+
+ void SkeletonBuildReverseIndexArray(int32_t *reverseIndexArray,int32_t const*indexArray,Skeleton const* apSrcSkeleton,Skeleton const* apDstSkeleton)
+ {
+ for(uint32_t dstIter = 0; dstIter < apDstSkeleton->m_Count; dstIter++)
+ {
+ reverseIndexArray[dstIter] = -1;
+ }
+
+ for(uint32_t srcIter = 0; srcIter < apSrcSkeleton->m_Count; srcIter++)
+ {
+ if(indexArray[srcIter] != -1)
+ {
+ reverseIndexArray[indexArray[srcIter]] = srcIter;
+ }
+ }
+ }
+
+ void SkeletonPoseCopy(Skeleton const* apSrcSkeleton, SkeletonPose const* apSrcPose, Skeleton const* apDstSkeleton, SkeletonPose* apDstPose)
+ {
+ uint32_t i;
+ for(i = 0; i < apSrcSkeleton->m_Count; i++)
+ {
+ uint32_t j;
+ for(j = 0; j < apDstSkeleton->m_Count; j++)
+ {
+ if( apSrcSkeleton->m_ID[i] == apDstSkeleton->m_ID[j] )
+ {
+ apDstPose->m_X[j] = apSrcPose->m_X[i];
+ break;
+ }
+ }
+ }
+ }
+
+ void SkeletonPoseSetDirty(Skeleton const* apSkeleton, uint32_t* apSkeletonPoseMask, int aIndex, int aStopIndex, uint32_t aMask)
+ {
+ int parentIndex = apSkeleton->m_Node[aIndex].m_ParentId;
+
+ if(parentIndex != -1)
+ {
+ if(aIndex != aStopIndex)
+ {
+ SkeletonPoseSetDirty(apSkeleton,apSkeletonPoseMask, parentIndex, aStopIndex, aMask);
+ }
+ }
+
+ apSkeletonPoseMask[aIndex] = apSkeletonPoseMask[aIndex] | aMask;
+ }
+
+ void SkeletonPoseComputeGlobal(Skeleton const* apSkeleton, SkeletonPose const* apSkeletonPoseLocal, SkeletonPose* apSkeletonPoseGlobal)
+ {
+ apSkeletonPoseGlobal->m_X[0] = apSkeletonPoseLocal->m_X[0];
+
+ uint32_t i;
+ for(i=1;i < apSkeleton->m_Count; i++)
+ {
+ apSkeletonPoseGlobal->m_X[i] = xformMul( apSkeletonPoseGlobal->m_X[apSkeleton->m_Node[i].m_ParentId], apSkeletonPoseLocal->m_X[i]);
+ }
+ }
+
+ void SkeletonPoseComputeLocal(Skeleton const* apSkeleton, SkeletonPose const* apSkeletonPoseGlobal, SkeletonPose* apSkeletonPoseLocal)
+ {
+ uint32_t i;
+ for(i=apSkeleton->m_Count-1;i > 0; i--)
+ {
+ apSkeletonPoseLocal->m_X[i] = xformInvMul( apSkeletonPoseGlobal->m_X[apSkeleton->m_Node[i].m_ParentId], apSkeletonPoseGlobal->m_X[i]);
+ }
+
+ apSkeletonPoseLocal->m_X[0] = apSkeletonPoseGlobal->m_X[0];
+ }
+
+ void SkeletonPoseComputeGlobal(Skeleton const* apSkeleton, SkeletonPose const* apSkeletonPoseLocal, SkeletonPose* apSkeletonPoseGlobal, int aIndex, int aStopIndex)
+ {
+ int parentIndex = apSkeleton->m_Node[aIndex].m_ParentId;
+
+ if(parentIndex != -1)
+ {
+ if(aIndex != aStopIndex)
+ {
+ SkeletonPoseComputeGlobal(apSkeleton, apSkeletonPoseLocal, apSkeletonPoseGlobal, parentIndex, aStopIndex);
+ }
+
+ apSkeletonPoseGlobal->m_X[aIndex] = xformMul( apSkeletonPoseGlobal->m_X[parentIndex], apSkeletonPoseLocal->m_X[aIndex]);
+ }
+ else
+ {
+ apSkeletonPoseGlobal->m_X[aIndex] = apSkeletonPoseLocal->m_X[aIndex];
+ }
+ }
+
+ void SkeletonPoseComputeGlobalQ(Skeleton const* apSkeleton, SkeletonPose const* apSkeletonPoseLocal, SkeletonPose* apSkeletonPoseGlobal)
+ {
+ apSkeletonPoseGlobal->m_X[0].q = apSkeletonPoseLocal->m_X[0].q;
+
+ uint32_t i;
+ for(i=1;i < apSkeleton->m_Count; i++)
+ {
+ apSkeletonPoseGlobal->m_X[i].q = normalize(quatMul( apSkeletonPoseGlobal->m_X[apSkeleton->m_Node[i].m_ParentId].q, apSkeletonPoseLocal->m_X[i].q));
+ }
+ }
+
+ void SkeletonPoseComputeGlobalQ(Skeleton const* apSkeleton, SkeletonPose const* apSkeletonPoseLocal, SkeletonPose* apSkeletonPoseGlobal, int aIndex, int aStopIndex)
+ {
+ int parentIndex = apSkeleton->m_Node[aIndex].m_ParentId;
+
+ if(parentIndex != -1)
+ {
+ if(aIndex != aStopIndex)
+ {
+ SkeletonPoseComputeGlobalQ(apSkeleton, apSkeletonPoseLocal, apSkeletonPoseGlobal, parentIndex, aStopIndex);
+ }
+
+ apSkeletonPoseGlobal->m_X[aIndex].q = normalize(quatMul( apSkeletonPoseGlobal->m_X[parentIndex].q, apSkeletonPoseLocal->m_X[aIndex].q));
+ }
+ else
+ {
+ apSkeletonPoseGlobal->m_X[aIndex].q = apSkeletonPoseLocal->m_X[aIndex].q;
+ }
+ }
+
+ void SkeletonPoseComputeLocal(Skeleton const* apSkeleton, SkeletonPose const* apSkeletonPoseGlobal, SkeletonPose* apSkeletonPoseLocal, int aIndex, int aStopIndex)
+ {
+ int parentIndex = apSkeleton->m_Node[aIndex].m_ParentId;
+
+ if(parentIndex != -1)
+ {
+ apSkeletonPoseLocal->m_X[aIndex] = xformInvMul( apSkeletonPoseGlobal->m_X[parentIndex], apSkeletonPoseGlobal->m_X[aIndex]);
+
+ if(aIndex != aStopIndex)
+ {
+ SkeletonPoseComputeLocal(apSkeleton, apSkeletonPoseGlobal, apSkeletonPoseLocal, parentIndex, aStopIndex);
+ }
+ }
+ else
+ {
+ apSkeletonPoseLocal->m_X[aIndex] = apSkeletonPoseGlobal->m_X[aIndex];
+ }
+ }
+
+ void SkeletonPoseComputeLocalQ(Skeleton const* apSkeleton, SkeletonPose const* apSkeletonPoseGlobal, SkeletonPose* apSkeletonPoseLocal)
+ {
+ uint32_t i;
+ for(i=apSkeleton->m_Count-1;i > 0; i--)
+ {
+ apSkeletonPoseLocal->m_X[i].q = normalize(quatMul( quatConj(apSkeletonPoseGlobal->m_X[apSkeleton->m_Node[i].m_ParentId].q), apSkeletonPoseGlobal->m_X[i].q));
+ }
+
+ apSkeletonPoseLocal->m_X[0].q = apSkeletonPoseGlobal->m_X[0].q;
+ }
+
+ void SkeletonPoseComputeLocalQ(Skeleton const* apSkeleton, SkeletonPose const* apSkeletonPoseGlobal, SkeletonPose* apSkeletonPoseLocal, int aIndex, int aStopIndex)
+ {
+ int parentIndex = apSkeleton->m_Node[aIndex].m_ParentId;
+
+ if(parentIndex != -1)
+ {
+ apSkeletonPoseLocal->m_X[aIndex].q = normalize(quatMul( quatConj(apSkeletonPoseGlobal->m_X[parentIndex].q), apSkeletonPoseGlobal->m_X[aIndex].q));
+
+ if(aIndex != aStopIndex)
+ {
+ SkeletonPoseComputeLocalQ(apSkeleton, apSkeletonPoseGlobal, apSkeletonPoseLocal, parentIndex, aStopIndex);
+ }
+ }
+ else
+ {
+ apSkeletonPoseLocal->m_X[aIndex].q = apSkeletonPoseGlobal->m_X[aIndex].q;
+ }
+ }
+
+ math::float4 SkeletonGetDoF(Skeleton const* apSkeleton,SkeletonPose const *apSkeletonPose, int32_t aIndex)
+ {
+ const int32_t axesIndex = apSkeleton->m_Node[aIndex].m_AxesId;
+ return math::cond(math::bool4(axesIndex != -1),math::ToAxes(apSkeleton->m_AxesArray[axesIndex],apSkeletonPose->m_X[aIndex].q),math::quat2Qtan(apSkeletonPose->m_X[aIndex].q));
+ }
+
+ void SkeletonSetDoF(Skeleton const* apSkeleton,SkeletonPose * apSkeletonPose, math::float4 const& aDoF, int32_t aIndex)
+ {
+ const int32_t axesIndex = apSkeleton->m_Node[aIndex].m_AxesId;
+ apSkeletonPose->m_X[aIndex].q = math::cond(math::bool4(axesIndex != -1),math::FromAxes(apSkeleton->m_AxesArray[axesIndex],aDoF),qtan2Quat(aDoF));
+ }
+
+ math::float4 SkeletonNodeEndPoint(Skeleton const *apSkeleton, int32_t aIndex, SkeletonPose const *apSkeletonPose)
+ {
+ return math::xformMulVec(apSkeletonPose->m_X[aIndex],math::quatXcos(apSkeleton->m_AxesArray[aIndex].m_PostQ) * math::float1(apSkeleton->m_AxesArray[aIndex].m_Length));
+ }
+
+ void SkeletonAlign(skeleton::Skeleton const *apSkeleton, math::float4 const &arRefQ, math::float4 & arQ, int32_t aIndex)
+ {
+ const int32_t axesIndex = apSkeleton->m_Node[aIndex].m_AxesId;
+
+ if(axesIndex != -1)
+ {
+ math::Axes axes = apSkeleton->m_AxesArray[axesIndex];
+
+ math::float4 refV = math::quatXcos(math::normalize(math::quatMul(arRefQ,axes.m_PostQ)));
+ math::float4 v = math::quatXcos(math::normalize(math::quatMul(arQ,axes.m_PostQ)));
+ math::float4 dq = math::quatArcRotate(v,refV);
+
+ arQ = math::normalize(math::quatMul(dq,arQ));
+ }
+ }
+
+ void SkeletonAlign(skeleton::Skeleton const *apSkeleton, skeleton::SkeletonPose const*apSkeletonPoseRef, skeleton::SkeletonPose *apSkeletonPose, int32_t aIndex)
+ {
+ SkeletonAlign(apSkeleton,apSkeletonPoseRef->m_X[aIndex].q,apSkeletonPose->m_X[aIndex].q,aIndex);
+ }
+
+ void Skeleton2BoneAdjustLength(Skeleton const *apSkeleton, int32_t aIndexA, int32_t aIndexB, int32_t aIndexC, math::float4 const &aTarget, math::float1 const& aRatio, SkeletonPose *apSkeletonPose, SkeletonPose *apSkeletonPoseWorkspace)
+ {
+ math::float4 vAB = apSkeletonPoseWorkspace->m_X[aIndexB].t - apSkeletonPoseWorkspace->m_X[aIndexA].t;
+ math::float4 vBC = apSkeletonPoseWorkspace->m_X[aIndexC].t - apSkeletonPoseWorkspace->m_X[aIndexB].t;
+ math::float4 vAD = aTarget - apSkeletonPoseWorkspace->m_X[aIndexA].t;
+
+ math::float1 lenABC = math::length(vAB) + math::length(vBC);
+ math::float1 lenAD = math::length(vAD);
+ math::float1 ratio = lenAD / lenABC;
+ math::float1 invARatio = math::float1::one() - aRatio;
+
+ if(ratio > invARatio)
+ {
+ ratio = math::saturate( (ratio-(invARatio))/(math::float1(2)*aRatio) );
+ math::float1 r = math::float1::one() + aRatio * ratio * ratio;
+
+ apSkeletonPose->m_X[aIndexB].t *= r;
+ apSkeletonPose->m_X[aIndexC].t *= r;
+ }
+ }
+
+ void Skeleton2BoneIK(Skeleton const *apSkeleton, int32_t aIndexA, int32_t aIndexB, int32_t aIndexC, math::float4 const &aTarget, float aWeight, SkeletonPose *apSkeletonPose, SkeletonPose *apSkeletonPoseWorkspace)
+ {
+ math::float4 qA0 = apSkeletonPose->m_X[aIndexA].q;
+ math::float4 qB0 = apSkeletonPose->m_X[aIndexB].q;
+
+ math::float4 dof = skeleton::SkeletonGetDoF(apSkeleton,apSkeletonPose,aIndexB) * math::float4(1.0f,0,0.9f,0);
+ skeleton::SkeletonSetDoF(apSkeleton,apSkeletonPose,dof,aIndexB);
+ skeleton::SkeletonPoseComputeGlobal(apSkeleton,apSkeletonPose,apSkeletonPoseWorkspace,aIndexC,aIndexB);
+
+ math::float4 vAB = apSkeletonPoseWorkspace->m_X[aIndexB].t - apSkeletonPoseWorkspace->m_X[aIndexA].t;
+ math::float4 vBC = apSkeletonPoseWorkspace->m_X[aIndexC].t - apSkeletonPoseWorkspace->m_X[aIndexB].t;
+ math::float4 vAC = apSkeletonPoseWorkspace->m_X[aIndexC].t - apSkeletonPoseWorkspace->m_X[aIndexA].t;
+ math::float4 vAD = aTarget - apSkeletonPoseWorkspace->m_X[aIndexA].t;
+
+ math::float1 lenAB = math::length(vAB);
+ math::float1 lenBC = math::length(vBC);
+ math::float1 lenAC = math::length(vAC);
+ math::float1 lenAD = math::length(vAD);
+
+ math::float1 angleAC = math::triangleAngle(lenAC,lenAB,lenBC);
+ math::float1 angleAD = math::triangleAngle(lenAD,lenAB,lenBC);
+
+ math::float4 axis = math::normalize(math::cross(vAB,vBC));
+
+ math::float1 a = math::float1(0.5f) * (angleAC-angleAD);
+ math::float1 s,c;
+ math::sincos(a,s,c);
+ math::float4 q = axis*s;
+ q.w() = c;
+ apSkeletonPoseWorkspace->m_X[aIndexB].q = math::normalize(math::quatMul(q,apSkeletonPoseWorkspace->m_X[aIndexB].q));
+
+ skeleton::SkeletonPoseComputeLocal(apSkeleton,apSkeletonPoseWorkspace,apSkeletonPose,aIndexB,aIndexB);
+ apSkeletonPose->m_X[aIndexB].q = math::quatLerp(qB0,apSkeletonPose->m_X[aIndexB].q,math::float1(aWeight));
+ skeleton::SkeletonPoseComputeGlobal(apSkeleton,apSkeletonPose,apSkeletonPoseWorkspace,aIndexC,aIndexB);
+
+ vAC = apSkeletonPoseWorkspace->m_X[aIndexC].t - apSkeletonPoseWorkspace->m_X[aIndexA].t;
+
+ q = math::normalize(math::quatArcRotate(vAC,vAD));
+ apSkeletonPoseWorkspace->m_X[aIndexA].q = math::normalize(math::quatMul(q,apSkeletonPoseWorkspace->m_X[aIndexA].q));
+ skeleton::SkeletonPoseComputeLocal(apSkeleton,apSkeletonPoseWorkspace,apSkeletonPose,aIndexA,aIndexA);
+ apSkeletonPose->m_X[aIndexA].q = math::quatLerp(apSkeletonPose->m_X[aIndexA].q,qA0,math::float1(math::pow(1-aWeight,4)));
+ }
+
+ void Skeleton3BoneIK(Skeleton const *apSkeleton, int32_t aIndexA, int32_t aIndexB, int32_t aIndexC, math::float4 const &aTarget, float weight, SkeletonPose *apSkeletonPose, SkeletonPose *apSkeletonPoseWorkspace)
+ {
+ if(weight > 0)
+ {
+ math::float4 qA = apSkeletonPose->m_X[aIndexA].q;
+ math::float4 qB = apSkeletonPose->m_X[aIndexB].q;
+ math::float4 qC = apSkeletonPose->m_X[aIndexC].q;
+
+ float fingerLen = apSkeleton->m_AxesArray[aIndexA].m_Length + apSkeleton->m_AxesArray[aIndexB].m_Length + apSkeleton->m_AxesArray[aIndexC].m_Length;
+ math::float1 targetDist = math::length(apSkeletonPoseWorkspace->m_X[aIndexA].t - aTarget);
+ float fact = math::pow(math::clamp(targetDist.tofloat()/fingerLen,0.f,1.f),4.0f);
+ math::float4 dof(0,0,2.0f*fact-1.0f,0);
+
+ skeleton::SkeletonSetDoF(apSkeleton,apSkeletonPose,dof,aIndexB);
+ skeleton::SkeletonSetDoF(apSkeleton,apSkeletonPose,dof,aIndexC);
+ skeleton::SkeletonPoseComputeGlobal(apSkeleton,apSkeletonPose,apSkeletonPoseWorkspace,aIndexC,aIndexB);
+
+ math::float4 endT = SkeletonNodeEndPoint(apSkeleton,aIndexC,apSkeletonPoseWorkspace);
+ math::float4 endV = endT - apSkeletonPoseWorkspace->m_X[aIndexA].t;
+ math::float4 targetV = aTarget - apSkeletonPoseWorkspace->m_X[aIndexA].t;
+
+ math::float4 q = math::normalize(math::quatArcRotate(endV,targetV));
+ apSkeletonPoseWorkspace->m_X[aIndexA].q = math::quatMul(q,apSkeletonPoseWorkspace->m_X[aIndexA].q);
+ skeleton::SkeletonPoseComputeLocal(apSkeleton,apSkeletonPoseWorkspace,apSkeletonPose,aIndexA,aIndexA);
+
+ math::float1 w(weight);
+
+ apSkeletonPose->m_X[aIndexA].q = math::quatLerp(qA,apSkeletonPose->m_X[aIndexA].q,w);
+ apSkeletonPose->m_X[aIndexB].q = math::quatLerp(qB,apSkeletonPose->m_X[aIndexB].q,w);
+ apSkeletonPose->m_X[aIndexC].q = math::quatLerp(qC,apSkeletonPose->m_X[aIndexC].q,w);
+ }
+ }
+
+ void SetupAxes(skeleton::Skeleton *apSkeleton, skeleton::SkeletonPose const *apSkeletonPoseGlobal, SetupAxesInfo const& apSetupAxesInfo, int32_t aIndex, int32_t aAxisIndex, bool aLeft, float aLen)
+ {
+ skeleton::Node &node = apSkeleton->m_Node[aIndex];
+ int32_t parentIndex = node.m_ParentId;
+
+ if(node.m_AxesId != -1)
+ {
+ math::Axes &axes = apSkeleton->m_AxesArray[node.m_AxesId];
+
+ math::xform boneX = apSkeletonPoseGlobal->m_X[aIndex];
+
+ axes.m_Limit.m_Min = math::radians(math::float4(apSetupAxesInfo.m_Min[0],apSetupAxesInfo.m_Min[1],apSetupAxesInfo.m_Min[2],0));
+ axes.m_Limit.m_Max = math::radians(math::float4(apSetupAxesInfo.m_Max[0],apSetupAxesInfo.m_Max[1],apSetupAxesInfo.m_Max[2],0));
+ axes.m_Sgn = math::float4(apSetupAxesInfo.m_Sgn[0],apSetupAxesInfo.m_Sgn[1],apSetupAxesInfo.m_Sgn[2],1) * (aLeft ? math::float4(1) : math::float4(-1,1,-1,-1));
+
+ math::float4 mainAxis = math::float4(apSetupAxesInfo.m_MainAxis[0],apSetupAxesInfo.m_MainAxis[1],apSetupAxesInfo.m_MainAxis[2],0);
+ math::float4 zeroQ = math::float4(apSetupAxesInfo.m_PreQ[0],apSetupAxesInfo.m_PreQ[1],apSetupAxesInfo.m_PreQ[2],apSetupAxesInfo.m_PreQ[3]) * (aLeft ? math::float4(1) : math::float4(1,1,1,-1));
+
+ math::float4 u(1,0,0,0);
+ math::float4 w(0,1,0,0);
+ math::float4 v(0,0,1,0);
+
+ axes.m_Type = apSetupAxesInfo.m_Type;
+
+ axes.m_Length = 1.0f;
+
+ if(aAxisIndex != -1)
+ {
+ math::xform axisX = apSkeletonPoseGlobal->m_X[aAxisIndex];
+
+ u = math::normalize((axisX.t - boneX.t) * math::float1(aLen));
+
+ w = mainAxis;
+ v = math::normalize(math::cross(w,u));
+ w = cross(u,v);
+
+ axes.m_Length = math::length(axisX.t - boneX.t).tofloat();
+ }
+
+ if(apSetupAxesInfo.m_ForceAxis)
+ {
+ switch(apSetupAxesInfo.m_ForceAxis)
+ {
+ case +1: u = math::float4(+1,0,0,0); break;
+ case -1: u = math::float4(-1,0,0,0); break;
+ case +2: u = math::float4(0,+1,0,0); break;
+ case -2: u = math::float4(0,-1,0,0); break;
+ case +3: u = math::float4(0,0,+1,0); break;
+ default: u = math::float4(0,0,-1,0); break;
+ };
+
+ w = mainAxis;
+ v = math::normalize(math::cross(w,u));
+ w = cross(u,v);
+ }
+
+ axes.m_Length *= fabs(aLen);
+
+ math::float4 parentQ = math::cond(math::bool4(parentIndex != -1),apSkeletonPoseGlobal->m_X[parentIndex].q, math::quatIdentity());
+
+ axes.m_PreQ = math::quatMatrixToQuat(u,v,w);
+ axes.m_PostQ = math::normalize(math::quatMul(math::quatConj(boneX.q),axes.m_PreQ));
+ axes.m_PreQ = math::normalize(math::quatMul(math::quatConj(parentQ),math::quatMul(zeroQ,axes.m_PreQ)));
+ }
+ }
+
+ static int GetSkeletonNodeDepth(mecanim::skeleton::Skeleton const& skeleton, uint32_t boneIndex)
+ {
+ uint32_t parentIndex = skeleton.m_Node[boneIndex].m_ParentId;
+ int depth = 0;
+ while (parentIndex != -1)
+ {
+ depth++;
+ parentIndex = skeleton.m_Node[parentIndex].m_ParentId;
+ }
+ return depth;
+ }
+}
+}
+
diff --git a/Runtime/mecanim/skeleton/skeleton.h b/Runtime/mecanim/skeleton/skeleton.h
new file mode 100644
index 0000000..e9e9c4b
--- /dev/null
+++ b/Runtime/mecanim/skeleton/skeleton.h
@@ -0,0 +1,186 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/Math/Simd/xform.h"
+#include "Runtime/mecanim/math/axes.h"
+
+
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Animation/MecanimArraySerialization.h"
+
+namespace mecanim
+{
+namespace skeleton
+{
+ struct Node
+ {
+ DEFINE_GET_TYPESTRING(Node)
+
+ Node() : m_ParentId(-1), m_AxesId(-1) {}
+ int32_t m_ParentId;
+ int32_t m_AxesId;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_ParentId);
+ TRANSFER(m_AxesId);
+ }
+ };
+
+ struct Skeleton
+ {
+ DEFINE_GET_TYPESTRING(Skeleton)
+
+ Skeleton() : m_Count(0), m_AxesCount(0) {}
+
+ uint32_t m_Count;
+ OffsetPtr<Node> m_Node;
+ OffsetPtr<uint32_t> m_ID; // CRC(path)
+
+ uint32_t m_AxesCount;
+ OffsetPtr<math::Axes> m_AxesArray;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_Count);
+ MANUAL_ARRAY_TRANSFER2(Node, m_Node, m_Count);
+ MANUAL_ARRAY_TRANSFER2(uint32_t, m_ID, m_Count);
+ TRANSFER_BLOB_ONLY(m_AxesCount);
+ MANUAL_ARRAY_TRANSFER2(math::Axes, m_AxesArray, m_AxesCount);
+ }
+ };
+
+ struct ATTRIBUTE_ALIGN(ALIGN4F) SkeletonPose
+ {
+ DEFINE_GET_TYPESTRING(SkeletonPose)
+
+ SkeletonPose() : m_Count(0) {}
+
+ uint32_t m_Count;
+ OffsetPtr<math::xform> m_X;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_Count);
+ MANUAL_ARRAY_TRANSFER2(math::xform, m_X, m_Count);
+ }
+ };
+
+ struct SkeletonMaskElement
+ {
+ DEFINE_GET_TYPESTRING(SkeletonMaskElement)
+
+ SkeletonMaskElement(): m_PathHash(0), m_Weight(0.f){}
+
+ uint32_t m_PathHash;
+ float m_Weight;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_PathHash);
+ TRANSFER(m_Weight);
+ }
+ };
+
+ struct SkeletonMask
+ {
+ DEFINE_GET_TYPESTRING(SkeletonMask)
+
+ SkeletonMask():m_Count(0){}
+
+ uint32_t m_Count;
+ OffsetPtr<SkeletonMaskElement> m_Data;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_Count);
+ MANUAL_ARRAY_TRANSFER2(SkeletonMaskElement, m_Data, m_Count);
+ }
+ };
+
+ Skeleton* CreateSkeleton(int32_t aNodeCount, int32_t aAxesCount, memory::Allocator& arAlloc);
+ void DestroySkeleton(Skeleton* apSkeleton, memory::Allocator& arAlloc);
+
+ SkeletonPose* CreateSkeletonPose(Skeleton const* apSkeleton, memory::Allocator& arAlloc);
+ void DestroySkeletonPose(SkeletonPose* apSkeletonPose, memory::Allocator& arAlloc);
+
+ SkeletonMask* CreateSkeletonMask(uint32_t aNodeCount, SkeletonMaskElement* elements, memory::Allocator& arAlloc);
+ void DestroySkeletonMask(SkeletonMask* skeletonMask, memory::Allocator& arAlloc);
+
+ // copy skeleton
+ void SkeletonCopy(Skeleton const* apSrc, Skeleton* apDst);
+
+ // copy pose
+ void SkeletonPoseCopy(SkeletonPose const* apSrcPose, SkeletonPose* apDstPose);
+ void SkeletonPoseCopy(SkeletonPose const* apSrcPose, SkeletonPose* apDstPose, uint32_t aIndexCount, int32_t const *apIndexArray);
+
+ // Find & Copy pose based on name binding
+ int32_t SkeletonFindNode(Skeleton const *apSkeleton, uint32_t aID);
+ void SkeletonBuildIndexArray(int32_t *indexArray,Skeleton const* apSrcSkeleton,Skeleton const* apDstSkeleton);
+ void SkeletonBuildReverseIndexArray(int32_t *reverseIndexArray,int32_t const*indexArray,Skeleton const* apSrcSkeleton,Skeleton const* apDstSkeleton);
+ void SkeletonPoseCopy(Skeleton const* apSrcSkeleton, SkeletonPose const* apSrcPose, Skeleton const* apDstSkeleton, SkeletonPose* apDstPose);
+
+ // set mask for a skeleton mask array
+ void SkeletonPoseSetDirty(Skeleton const* apSkeleton, uint32_t* apSkeletonPoseMask, int aIndex, int aStopIndex, uint32_t aMask);
+
+ // those functions work in place
+ // computes a global pose from a local pose
+ void SkeletonPoseComputeGlobal(Skeleton const* apSkeleton, SkeletonPose const* apLocalPose, SkeletonPose* apGlobalPose);
+ // computes a global pose from a local pose for part of the skeleton starting at aIndex (child) to aStopIndex (ancestor)
+ void SkeletonPoseComputeGlobal(Skeleton const* apSkeleton, SkeletonPose const* apSkeletonPoseLocal, SkeletonPose* apSkeletonPoseGlobal, int aIndex, int aStopIndex);
+ // computes a local pose from a global pose
+ void SkeletonPoseComputeLocal(Skeleton const* apSkeleton, SkeletonPose const* apGlobalPose, SkeletonPose* apLocalPose);
+ // computes a local pose from a global pose for part of the skeleton starting at aIndex (child) to aStopIndex (ancestor)
+ void SkeletonPoseComputeLocal(Skeleton const* apSkeleton, SkeletonPose const* apSkeletonPoseGlobal, SkeletonPose* apSkeletonPoseLocal, int aIndex, int aStopIndex);
+ // computes a global Q pose from a local Q pose
+ void SkeletonPoseComputeGlobalQ(Skeleton const* apSkeleton, SkeletonPose const* apSkeletonPoseLocal, SkeletonPose* apSkeletonPoseGlobal);
+ // computes a global Q pose from a local Q pose for part of the skeleton starting at aIndex (child) to aStopIndex (ancestor)
+ void SkeletonPoseComputeGlobalQ(Skeleton const* apSkeleton, SkeletonPose const* apSkeletonPoseLocal, SkeletonPose* apSkeletonPoseGlobal, int aIndex, int aStopIndex);
+ // computes a local Q pose from a global Q pose
+ void SkeletonPoseComputeLocalQ(Skeleton const* apSkeleton, SkeletonPose const* apSkeletonPoseGlobal, SkeletonPose* apSkeletonPoseLocal);
+ // computes a local Q pose from a global Q pose for part of the skeleton starting at aIndex (child) to aStopIndex (ancestor)
+ void SkeletonPoseComputeLocalQ(Skeleton const* apSkeleton, SkeletonPose const* apSkeletonPoseGlobal, SkeletonPose* apSkeletonPoseLocal, int aIndex, int aStopIndex);
+
+ // get dof for bone index in pose
+ math::float4 SkeletonGetDoF(Skeleton const* apSkeleton,SkeletonPose const *apSkeletonPose, int32_t aIndex);
+ // set dof for bone index in pose
+ void SkeletonSetDoF(Skeleton const* apSkeleton, SkeletonPose * apSkeletonPose, math::float4 const& aDoF, int32_t aIndex);
+ // algin x axis of skeleton node quaternion to ref node quaternion
+ void SkeletonAlign(skeleton::Skeleton const *apSkeleton, math::float4 const &arRefQ, math::float4 & arQ, int32_t aIndex);
+ // algin x axis of skeleton pose node to ref pose node
+ void SkeletonAlign(skeleton::Skeleton const *apSkeleton, skeleton::SkeletonPose const*apSkeletonPoseRef, skeleton::SkeletonPose *apSkeletonPose, int32_t aIndex);
+
+ // ik
+ // compute end point of a node which is x * xcos * lenght.
+ math::float4 SkeletonNodeEndPoint(Skeleton const *apSkeleton, int32_t aIndex, SkeletonPose const*apSkeletonPose);
+ // The apSkeletonPoseWorkspace parameter has to be a valid global pose, otherwise unexpected result may occur
+ void Skeleton2BoneAdjustLength(Skeleton const *apSkeleton, int32_t aIndexA, int32_t aIndexB, int32_t aIndexC, math::float4 const &aTarget, math::float1 const& aRatio, SkeletonPose *apSkeletonPose, SkeletonPose *apSkeletonPoseWorkspace);
+ // The apSkeletonPoseWorkspace parameter has to be a valid global pose, otherwise unexpected result may occur
+ void Skeleton2BoneIK(Skeleton const *apSkeleton, int32_t aIndexA, int32_t aIndexB, int32_t aIndexC, math::float4 const &aTarget, float aWeight, SkeletonPose *apSkeletonPose, SkeletonPose *apSkeletonPoseWorkspace);
+ // The apSkeletonPoseWorkspace parameter has to be a valid global pose, otherwise unexpected result may occur
+ void Skeleton3BoneIK(Skeleton const *apSkeleton, int32_t aIndexA, int32_t aIndexB, int32_t aIndexC, math::float4 const &aTarget, float weight, SkeletonPose *apSkeletonPose, SkeletonPose *apSkeletonPoseWorkspace);
+
+ // setup axes utilities
+ struct SetupAxesInfo
+ {
+ float m_PreQ[4];
+ float m_MainAxis[4];
+ float m_Min[4];
+ float m_Max[4];
+ float m_Sgn[4];
+ math::AxesType m_Type;
+ int32_t m_ForceAxis;
+ };
+
+ void SetupAxes(skeleton::Skeleton *apSkeleton, skeleton::SkeletonPose const *apSkeletonPoseGlobal, SetupAxesInfo const& apSetupAxesInfo, int32_t aIndex, int32_t aAxisIndex, bool aLeft, float aLen = 1.0f);
+}
+
+}
diff --git a/Runtime/mecanim/statemachine/statemachine.cpp b/Runtime/mecanim/statemachine/statemachine.cpp
new file mode 100644
index 0000000..3bd1a25
--- /dev/null
+++ b/Runtime/mecanim/statemachine/statemachine.cpp
@@ -0,0 +1,967 @@
+#include "UnityPrefix.h"
+
+
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/mecanim/generic/stringtable.h"
+
+#include "Runtime/mecanim/statemachine/statemachine.h"
+
+#include "Runtime/Math/Simd/math.h"
+
+namespace mecanim
+{
+namespace statemachine
+{
+
+ int32_t GetStateIndex(StateMachineConstant const* apStateMachineConstant, uint32_t id)
+ {
+ for(uint32_t i=0;i<apStateMachineConstant->m_StateConstantCount;++i)
+ {
+ if (CompareStateID (apStateMachineConstant->m_StateConstantArray[i].Get(), id))
+ return i;
+ }
+ return -1;
+ }
+
+
+
+ TransitionConstant const* GetTransitionConstant(StateMachineConstant const* apStateMachineConstant, StateConstant const* apStateConstant, uint32_t id)
+ {
+ if( id >= s_DynamicTransitionEncodeKey)
+ return 0;
+ else if( id >= s_AnyTransitionEncodeKey)
+ return apStateMachineConstant->m_AnyStateTransitionConstantArray[id-s_AnyTransitionEncodeKey].Get();
+ else
+ return apStateConstant->m_TransitionConstantArray[id].Get();
+ }
+
+
+ bool IsCurrentTransitionAtomic(StateMachineConstant const* apStateMachineConstant, StateMachineMemory *apStateMachineMemory)
+ {
+ TransitionConstant const* transition = GetTransitionConstant(apStateMachineConstant, apStateMachineConstant->m_StateConstantArray[apStateMachineMemory->m_CurrentStateIndex].Get(), apStateMachineMemory->m_TransitionId);
+ return transition->m_Atomic;
+ }
+
+ TransitionWorkspace* GetTransitionWorkspace(StateMachineWorkspace const* apStateMachineWorkspace, StateWorkspace const* apStateWorkspace, uint32_t id)
+ {
+ if( id > s_AnyTransitionEncodeKey)
+ return apStateMachineWorkspace->m_AnyStateTransitionWorkspaceArray[id-s_AnyTransitionEncodeKey];
+
+ else
+ return apStateWorkspace->m_TransitionWorkspaceArray[id];
+ }
+
+ TransitionConstant *CreateTransitionConstant(ConditionConstant** apConditionsConstantArray, uint32_t aConditionConstantCount,
+ uint32_t aDestinationState,float aTransitionDuration, float aTransitionOffset, bool aAtomic, uint32_t aID, uint32_t aUserID, memory::Allocator& arAlloc)
+ {
+ SETPROFILERLABEL(TransitionConstant);
+
+ TransitionConstant * transitionConstant = arAlloc.Construct<TransitionConstant >();
+ transitionConstant->m_ConditionConstantArray = arAlloc.ConstructArray< OffsetPtr<ConditionConstant> >(aConditionConstantCount);
+ transitionConstant->m_ConditionConstantCount = aConditionConstantCount;
+
+ transitionConstant->m_TransitionDuration = aTransitionDuration;
+ transitionConstant->m_TransitionOffset = aTransitionOffset;
+ transitionConstant->m_Atomic = aAtomic;
+ transitionConstant->m_ID = aID;
+ transitionConstant->m_UserID= aUserID;
+
+
+ transitionConstant->m_DestinationState = aDestinationState;
+
+
+ uint32_t i;
+ for(i=0;i<aConditionConstantCount;i++)
+ transitionConstant->m_ConditionConstantArray[i] = apConditionsConstantArray[i];
+
+ return transitionConstant;
+ }
+
+ void DestroyTransitionConstant(TransitionConstant *apTransitionConstant, memory::Allocator& arAlloc)
+ {
+ if(apTransitionConstant)
+ {
+ arAlloc.Deallocate(apTransitionConstant->m_ConditionConstantArray);
+ arAlloc.Deallocate(apTransitionConstant);
+ }
+ }
+
+
+ TransitionWorkspace* CreateTransitionWorkspace(TransitionConstant const* apTransitionConstant, memory::Allocator& arAlloc)
+ {
+ SETPROFILERLABEL(TransitionWorkspace);
+ TransitionWorkspace* transitionWorkspace = arAlloc.Construct<TransitionWorkspace>();
+ transitionWorkspace->m_ConditionConstantCount = apTransitionConstant->m_ConditionConstantCount;
+
+ return transitionWorkspace;
+ }
+
+ void DestroyTransitionWorkspace(TransitionWorkspace* apTransitionWorkspace, memory::Allocator& arAlloc)
+ {
+ if(apTransitionWorkspace)
+ {
+ arAlloc.Deallocate(apTransitionWorkspace);
+ }
+ }
+
+
+ ConditionConstant *CreateConditionConstant( uint32_t aConditionMode, uint32_t aEventID, float aEventThreshold, float aExitTime, memory::Allocator& arAlloc)
+ {
+ SETPROFILERLABEL(ConditionConstant);
+ ConditionConstant* conditionConstant = arAlloc.Construct<ConditionConstant>();
+
+ conditionConstant->m_ConditionMode = aConditionMode;
+
+ if(aConditionMode == kConditionModeIf || aConditionMode == kConditionModeIfNot || aConditionMode == kConditionModeGreater || aConditionMode == kConditionModeLess || aConditionMode == kConditionModeEquals || aConditionMode == kConditionModeNotEqual)
+ {
+ conditionConstant->m_EventID = aEventID;
+ if(aConditionMode == kConditionModeGreater || aConditionMode == kConditionModeLess || aConditionMode == kConditionModeEquals || aConditionMode == kConditionModeNotEqual)
+ {
+ conditionConstant->m_EventThreshold = aEventThreshold;
+ }
+ }
+ else if(aConditionMode == kConditionModeExitTime)
+ {
+ conditionConstant->m_ExitTime = aExitTime;
+ }
+
+ return conditionConstant;
+ }
+ void DestroyConditionConstant(ConditionConstant *apConditionConstant, memory::Allocator& arAlloc)
+ {
+ if(apConditionConstant)
+ {
+ arAlloc.Deallocate(apConditionConstant);
+ }
+ }
+
+ static int GetBlendTreeIndex(const StateConstant& arStateConstant, mecanim::int32_t aMotionSetIndex)
+ {
+ return arStateConstant.m_BlendTreeConstantIndexArray[aMotionSetIndex];
+ }
+
+ animation::BlendTreeConstant const* GetBlendTreeConstant(const StateConstant& arStateConstant, mecanim::int32_t aMotionSetIndex)
+ {
+ int blendTreeIndex = GetBlendTreeIndex(arStateConstant,aMotionSetIndex);
+ return blendTreeIndex != -1 ? arStateConstant.m_BlendTreeConstantArray[blendTreeIndex].Get() : 0;
+ }
+
+ animation::BlendTreeMemory *GetBlendTreeMemory(const StateConstant& arStateConstant,StateMemory& arStateMemory, mecanim::int32_t aMotionSetIndex)
+ {
+ int blendTreeIndex = GetBlendTreeIndex(arStateConstant,aMotionSetIndex);
+ return blendTreeIndex != -1 ? arStateMemory.m_BlendTreeMemoryArray[blendTreeIndex].Get() : 0;
+ }
+
+ StateConstant* CreateStateConstant(TransitionConstant** apTransitionConstantArray, uint32_t aTransitionConstantCount,
+ float aSpeed, bool aIKOnFeet, bool aMirror, float aCycleOffset, animation::BlendTreeConstant** apBlendTreeConstantArray,
+ uint32_t aMotionSetCount, uint32_t nameID, uint32_t pathID, uint32_t aTagID, bool aLoop, memory::Allocator& arAlloc)
+ {
+ SETPROFILERLABEL(StateConstant);
+ StateConstant* stateConstant = arAlloc.Construct<StateConstant>();
+
+ stateConstant->m_TransitionConstantCount = aTransitionConstantCount;
+ stateConstant->m_Speed = aSpeed;
+ stateConstant->m_IKOnFeet = aIKOnFeet;
+ stateConstant->m_Mirror = aMirror;
+ stateConstant->m_CycleOffset = aCycleOffset;
+ stateConstant->m_PathID = pathID;
+ stateConstant->m_NameID = nameID;
+ stateConstant->m_TagID = aTagID;
+ stateConstant->m_MotionSetCount = aMotionSetCount;
+ stateConstant->m_BlendTreeCount = 0 ;
+ stateConstant->m_Loop = aLoop;
+
+ stateConstant->m_BlendTreeConstantIndexArray = arAlloc.ConstructArray<int32_t>(aMotionSetCount);
+ stateConstant->m_LeafInfoArray = arAlloc.ConstructArray<LeafInfoConstant>(aMotionSetCount);
+
+ stateConstant->m_TransitionConstantArray = arAlloc.ConstructArray< OffsetPtr<TransitionConstant> >(aTransitionConstantCount);
+
+ uint32_t i;
+ for(i = 0 ; i < aTransitionConstantCount; i++)
+ stateConstant->m_TransitionConstantArray[i] = apTransitionConstantArray[i];
+
+ for(i = 0 ; i < aMotionSetCount; i++)
+ {
+ if(apBlendTreeConstantArray[i] != 0)
+ {
+ stateConstant->m_BlendTreeConstantIndexArray[i] = stateConstant->m_BlendTreeCount ;
+ stateConstant->m_LeafInfoArray[i].m_Count = animation::GetLeafCount(*apBlendTreeConstantArray[i]);
+ stateConstant->m_LeafInfoArray[i].m_IDArray = arAlloc.ConstructArray<uint32_t>(stateConstant->m_LeafInfoArray[i].m_Count);
+ animation::FillLeafIDArray(*apBlendTreeConstantArray[i], stateConstant->m_LeafInfoArray[i].m_IDArray.Get());
+ stateConstant->m_BlendTreeCount++;
+ }
+ else
+ {
+ stateConstant->m_BlendTreeConstantIndexArray[i] = -1;
+ stateConstant->m_LeafInfoArray[i].m_Count = 0;
+ stateConstant->m_LeafInfoArray[i].m_IDArray = 0 ;
+ }
+
+ }
+
+ stateConstant->m_BlendTreeConstantArray = arAlloc.ConstructArray< OffsetPtr<animation::BlendTreeConstant> >(stateConstant->m_BlendTreeCount);
+ uint32_t currentTreeCount = 0;
+ for(i = 0 ; i < aMotionSetCount ; i++)
+ {
+ if(apBlendTreeConstantArray[i] != 0)
+ {
+ stateConstant->m_BlendTreeConstantArray[currentTreeCount] = apBlendTreeConstantArray[i];
+ currentTreeCount++;
+ }
+ }
+
+ return stateConstant;
+ }
+
+ void DestroyStateConstant(StateConstant* apStateConstant, memory::Allocator& arAlloc)
+ {
+ if(apStateConstant)
+ {
+ for(uint32_t i = 0 ; i < apStateConstant->m_MotionSetCount; i++)
+ {
+ arAlloc.Deallocate(apStateConstant->m_LeafInfoArray[i].m_IDArray);
+ }
+ arAlloc.Deallocate(apStateConstant->m_LeafInfoArray);
+ arAlloc.Deallocate(apStateConstant->m_BlendTreeConstantArray);
+ arAlloc.Deallocate(apStateConstant->m_BlendTreeConstantIndexArray);
+
+ arAlloc.Deallocate(apStateConstant->m_TransitionConstantArray);
+ arAlloc.Deallocate(apStateConstant);
+ }
+ }
+
+ StateMemory* CreateStateMemory(StateConstant const* apStateConstant, StateMachineConstant const* apParentStateMachineConstant, memory::Allocator& arAlloc)
+ {
+ SETPROFILERLABEL(StateMemory);
+ StateMemory *stateMemory = arAlloc.Construct<StateMemory>();
+
+ stateMemory->m_BlendTreeCount = apStateConstant->m_BlendTreeCount;
+ stateMemory->m_BlendTreeMemoryArray = arAlloc.ConstructArray< OffsetPtr<animation::BlendTreeMemory> >(stateMemory->m_BlendTreeCount);
+
+ for(int blendTreeIter = 0 ; blendTreeIter < stateMemory->m_BlendTreeCount; blendTreeIter++)
+ {
+ stateMemory->m_BlendTreeMemoryArray[blendTreeIter] = animation::CreateBlendTreeMemory(apStateConstant->m_BlendTreeConstantArray[blendTreeIter].Get(), arAlloc);
+ }
+
+ return stateMemory;
+ }
+
+ void DestroyStateMemory(StateMemory* apStateMemory, memory::Allocator& arAlloc)
+ {
+ if(apStateMemory)
+ {
+ for(int i = 0 ; i < apStateMemory->m_BlendTreeCount; i++)
+ {
+ arAlloc.Deallocate(apStateMemory->m_BlendTreeMemoryArray[i]);
+ }
+
+ arAlloc.Deallocate(apStateMemory->m_BlendTreeMemoryArray);
+ arAlloc.Deallocate(apStateMemory);
+ }
+ }
+
+ StateWorkspace* CreateStateWorkspace(StateConstant const* apStateConstant, uint32_t maxBlendedClip, memory::Allocator& arAlloc)
+ {
+ SETPROFILERLABEL(StateWorkspace);
+ StateWorkspace* stateWorkspace = arAlloc.Construct<StateWorkspace>();
+
+ stateWorkspace->m_TransitionWorkspaceCount = apStateConstant->m_TransitionConstantCount;
+ stateWorkspace->m_TransitionWorkspaceArray = arAlloc.ConstructArray<TransitionWorkspace*>(stateWorkspace->m_TransitionWorkspaceCount);
+ stateWorkspace->m_BlendTreeInputArray = arAlloc.ConstructArray<animation::BlendTreeInput*>(apStateConstant->m_MotionSetCount);
+ stateWorkspace->m_BlendTreeOutputArray = arAlloc.ConstructArray<animation::BlendTreeOutput*>(apStateConstant->m_MotionSetCount);
+ stateWorkspace->m_BlendTreeWorkspaceArray = arAlloc.ConstructArray<animation::BlendTreeWorkspace*>(apStateConstant->m_MotionSetCount);
+ stateWorkspace->m_MotionSetCount = apStateConstant->m_MotionSetCount;
+
+ for(uint32_t i = 0 ; i < stateWorkspace->m_TransitionWorkspaceCount ; i++)
+ {
+ stateWorkspace->m_TransitionWorkspaceArray[i] = CreateTransitionWorkspace(apStateConstant->m_TransitionConstantArray[i].Get(), arAlloc);
+ }
+
+ for(uint32_t i = 0 ; i < stateWorkspace->m_MotionSetCount ; i++)
+ {
+ animation::BlendTreeConstant const *blendTreeConstant = statemachine::GetBlendTreeConstant(*apStateConstant,i);
+ if(blendTreeConstant != 0)
+ {
+ stateWorkspace->m_BlendTreeInputArray[i] = animation::CreateBlendTreeInput(blendTreeConstant, arAlloc);
+ stateWorkspace->m_BlendTreeOutputArray[i] = animation::CreateBlendTreeOutput(blendTreeConstant, maxBlendedClip, arAlloc);
+ stateWorkspace->m_BlendTreeWorkspaceArray[i] = animation::CreateBlendTreeWorkspace(blendTreeConstant,arAlloc);
+ }
+ else
+ {
+ stateWorkspace->m_BlendTreeInputArray[i] = 0;
+ stateWorkspace->m_BlendTreeOutputArray[i] = 0;
+ stateWorkspace->m_BlendTreeWorkspaceArray[i] = 0;
+ }
+ }
+
+ return stateWorkspace;
+ }
+
+ void DestroyStateWorkspace(StateWorkspace* apStateWorkspace, memory::Allocator& arAlloc)
+ {
+ if(apStateWorkspace)
+ {
+
+ for(uint32_t i = 0 ; i < apStateWorkspace->m_TransitionWorkspaceCount; i++)
+ {
+ DestroyTransitionWorkspace(apStateWorkspace->m_TransitionWorkspaceArray[i], arAlloc);
+ }
+ for(uint32_t i = 0 ; i < apStateWorkspace->m_MotionSetCount ; i++)
+ {
+ animation::DestroyBlendTreeInput(apStateWorkspace->m_BlendTreeInputArray[i], arAlloc);
+ animation::DestroyBlendTreeOutput(apStateWorkspace->m_BlendTreeOutputArray[i], arAlloc);
+ animation::DestroyBlendTreeWorkspace(apStateWorkspace->m_BlendTreeWorkspaceArray[i], arAlloc);
+ }
+
+ arAlloc.Deallocate(apStateWorkspace->m_BlendTreeInputArray);
+ arAlloc.Deallocate(apStateWorkspace->m_BlendTreeOutputArray);
+ arAlloc.Deallocate(apStateWorkspace->m_BlendTreeWorkspaceArray);
+
+ arAlloc.Deallocate(apStateWorkspace->m_TransitionWorkspaceArray);
+ arAlloc.Deallocate(apStateWorkspace);
+ }
+ }
+
+
+ StateOutput* CreateStateOutput(StateConstant const* apStateConstant, memory::Allocator& arAlloc)
+ {
+ SETPROFILERLABEL(StateOutput);
+ return arAlloc.Construct<StateOutput>();
+ }
+
+ void DestroyStateOutput(StateOutput* apStateOutput, memory::Allocator& arAlloc)
+ {
+ if(apStateOutput)
+ {
+ arAlloc.Deallocate(apStateOutput);
+ }
+ }
+
+ StateMachineConstant* CreateStateMachineConstant(StateConstant** apStateConstantArray, uint32_t aStateConstantCount, uint32_t aDefaultState,
+ TransitionConstant** apAnyStateTransitionConstantArray, uint32_t aAnyStateTransitionConstantCount, uint32_t aMotionSetCount,
+ memory::Allocator& arAlloc)
+ {
+ SETPROFILERLABEL(StateMachineConstant);
+ StateMachineConstant* stateMachineConstant = arAlloc.Construct<StateMachineConstant>();
+ stateMachineConstant->m_StateConstantArray = arAlloc.ConstructArray< OffsetPtr<StateConstant> >(aStateConstantCount);
+ stateMachineConstant->m_AnyStateTransitionConstantArray = arAlloc.ConstructArray< OffsetPtr<TransitionConstant> >(aAnyStateTransitionConstantCount);
+
+ stateMachineConstant->m_StateConstantCount = aStateConstantCount;
+ stateMachineConstant->m_DefaultState = aDefaultState;
+ stateMachineConstant->m_AnyStateTransitionConstantCount = aAnyStateTransitionConstantCount;
+ stateMachineConstant->m_MotionSetCount = aMotionSetCount;
+
+ uint32_t i;
+ for(i=0;i<aStateConstantCount;i++)
+ stateMachineConstant->m_StateConstantArray[i] = apStateConstantArray[i];
+
+ for(i=0;i<aAnyStateTransitionConstantCount;i++)
+ stateMachineConstant->m_AnyStateTransitionConstantArray[i] = apAnyStateTransitionConstantArray[i];
+
+ /////////////////////////////////////////////////////////
+ //
+ uint32_t j;
+ for(j = 0 ; j < stateMachineConstant->m_MotionSetCount; j++)
+ {
+ uint32_t clipOffset = 0 ;
+ for( i = 0 ; i < aStateConstantCount; i++)
+ {
+ stateMachineConstant->m_StateConstantArray[i]->m_LeafInfoArray[j].m_IndexOffset = clipOffset;
+ clipOffset += stateMachineConstant->m_StateConstantArray[i]->m_LeafInfoArray[j].m_Count;
+ }
+ }
+
+ return stateMachineConstant;
+ }
+
+ void DestroyStateMachineConstant(StateMachineConstant* apStateMachineConstant, memory::Allocator& arAlloc)
+ {
+ if(apStateMachineConstant)
+ {
+ arAlloc.Deallocate(apStateMachineConstant->m_AnyStateTransitionConstantArray);
+ arAlloc.Deallocate(apStateMachineConstant->m_StateConstantArray);
+
+ arAlloc.Deallocate(apStateMachineConstant);
+ }
+ }
+
+ StateMachineInput* CreateStateMachineInput(StateMachineConstant const* apStateMachineConstant, memory::Allocator& arAlloc)
+ {
+ SETPROFILERLABEL(StateMachineInput);
+ StateMachineInput* stateMachineInput = arAlloc.Construct<StateMachineInput>();
+ return stateMachineInput;
+ }
+
+ void DestroyStateMachineInput(StateMachineInput* apStateMachineInput, memory::Allocator& arAlloc)
+ {
+ if(apStateMachineInput)
+ {
+ arAlloc.Deallocate(apStateMachineInput);
+ }
+ }
+
+ StateMachineMemory* CreateStateMachineMemory(StateMachineConstant const* apStateMachineConstant, memory::Allocator& arAlloc)
+ {
+ SETPROFILERLABEL(StateMachineMemory);
+ StateMachineMemory* stateMachineMemory = arAlloc.Construct<StateMachineMemory>();
+ stateMachineMemory->m_CurrentStateIndex = apStateMachineConstant->m_DefaultState;
+ stateMachineMemory->m_StateMemoryCount = apStateMachineConstant->m_StateConstantCount;
+ stateMachineMemory->m_StateMemoryArray = arAlloc.ConstructArray< OffsetPtr<StateMemory> >(stateMachineMemory->m_StateMemoryCount);
+
+ stateMachineMemory->m_MotionSetAutoWeightArray = arAlloc.ConstructArray<float>(apStateMachineConstant->m_MotionSetCount);
+
+ stateMachineMemory->m_MotionSetCount = apStateMachineConstant->m_MotionSetCount;
+
+ for( uint32_t i = 0 ; i < stateMachineMemory->m_StateMemoryCount; i++)
+ {
+ stateMachineMemory->m_StateMemoryArray[i] = CreateStateMemory(apStateMachineConstant->m_StateConstantArray[i].Get(), apStateMachineConstant, arAlloc);
+
+ if(stateMachineMemory->m_StateMemoryArray[i].IsNull())
+ {
+ DestroyStateMachineMemory(stateMachineMemory, arAlloc);
+ return 0;
+ }
+ }
+ return stateMachineMemory;
+ }
+
+ void DestroyStateMachineMemory(StateMachineMemory* apStateMachineMemory, memory::Allocator& arAlloc)
+ {
+ if(apStateMachineMemory)
+ {
+ for(uint32_t i = 0 ; i < apStateMachineMemory->m_StateMemoryCount ; i++)
+ {
+ DestroyStateMemory(apStateMachineMemory->m_StateMemoryArray[i].Get(), arAlloc);
+ }
+
+ arAlloc.Deallocate(apStateMachineMemory->m_MotionSetAutoWeightArray);
+ arAlloc.Deallocate(apStateMachineMemory->m_StateMemoryArray);
+ arAlloc.Deallocate(apStateMachineMemory);
+ }
+ }
+
+
+ StateMachineWorkspace* CreateStateMachineWorkspace(StateMachineConstant const* apStateMachineConstant, uint32_t maxBlendState, memory::Allocator& arAlloc)
+ {
+ SETPROFILERLABEL(StateMachineWorkspace);
+ StateMachineWorkspace* stateMachineWorkspace = arAlloc.Construct<StateMachineWorkspace>();
+
+ stateMachineWorkspace->m_StateWorkspaceCount = apStateMachineConstant->m_StateConstantCount;
+ stateMachineWorkspace->m_StateWorkspaceArray = arAlloc.ConstructArray<StateWorkspace*>(stateMachineWorkspace->m_StateWorkspaceCount);
+ memset(&stateMachineWorkspace->m_StateWorkspaceArray[0], 0, sizeof(StateWorkspace*)*stateMachineWorkspace->m_StateWorkspaceCount);
+
+ for( uint32_t i = 0 ; i < stateMachineWorkspace->m_StateWorkspaceCount; i++)
+ {
+ stateMachineWorkspace->m_StateWorkspaceArray[i] = CreateStateWorkspace(apStateMachineConstant->m_StateConstantArray[i].Get(), maxBlendState, arAlloc);
+ if(stateMachineWorkspace->m_StateWorkspaceArray[i] == 0)
+ {
+ DestroyStateMachineWorkspace(stateMachineWorkspace, arAlloc);
+ return 0;
+ }
+ }
+
+
+ stateMachineWorkspace->m_AnyStateTransitionWorkspaceCount = apStateMachineConstant->m_AnyStateTransitionConstantCount;
+ stateMachineWorkspace->m_AnyStateTransitionWorkspaceArray = arAlloc.ConstructArray<TransitionWorkspace*>(stateMachineWorkspace->m_AnyStateTransitionWorkspaceCount);
+ for( uint32_t i = 0 ; i < stateMachineWorkspace->m_AnyStateTransitionWorkspaceCount; i++)
+ {
+ stateMachineWorkspace->m_AnyStateTransitionWorkspaceArray[i] = CreateTransitionWorkspace(apStateMachineConstant->m_AnyStateTransitionConstantArray[i].Get(), arAlloc);
+ }
+
+ return stateMachineWorkspace;
+
+ }
+
+ void DestroyStateMachineWorkspace(StateMachineWorkspace* apStateMachineWorkspace, memory::Allocator& arAlloc)
+ {
+ if(apStateMachineWorkspace)
+ {
+ for(uint32_t i = 0 ; i < apStateMachineWorkspace->m_StateWorkspaceCount; i++)
+ {
+ DestroyStateWorkspace(apStateMachineWorkspace->m_StateWorkspaceArray[i], arAlloc);
+ }
+
+ for(uint32_t i = 0 ; i < apStateMachineWorkspace->m_AnyStateTransitionWorkspaceCount ; i++)
+ {
+ DestroyTransitionWorkspace(apStateMachineWorkspace->m_AnyStateTransitionWorkspaceArray[i], arAlloc);
+ }
+
+ arAlloc.Deallocate(apStateMachineWorkspace->m_StateWorkspaceArray);
+ arAlloc.Deallocate(apStateMachineWorkspace->m_AnyStateTransitionWorkspaceArray);
+ arAlloc.Deallocate(apStateMachineWorkspace);
+ }
+ }
+
+ StateMachineOutput* CreateStateMachineOutput(StateMachineConstant const* apStateMachineConstant, uint32_t maxBlendedClip, memory::Allocator& arAlloc)
+ {
+ SETPROFILERLABEL(StateMachineOutput);
+ StateMachineOutput* stateMachineOutput = arAlloc.Construct<StateMachineOutput>();
+ stateMachineOutput->m_MotionSetCount = apStateMachineConstant->m_MotionSetCount;
+ stateMachineOutput->m_Left.m_BlendNodeLayer = arAlloc.ConstructArray<BlendNodeLayer>(apStateMachineConstant->m_MotionSetCount);
+ stateMachineOutput->m_Right.m_BlendNodeLayer = arAlloc.ConstructArray<BlendNodeLayer>(apStateMachineConstant->m_MotionSetCount);
+
+ for(int i=0;i<apStateMachineConstant->m_MotionSetCount;i++)
+ {
+ stateMachineOutput->m_Left.m_BlendNodeLayer[i].m_OutputBlendArray = arAlloc.ConstructArray<float>(maxBlendedClip);
+ stateMachineOutput->m_Left.m_BlendNodeLayer[i].m_OutputIndexArray = arAlloc.ConstructArray<uint32_t>(maxBlendedClip);
+ stateMachineOutput->m_Left.m_BlendNodeLayer[i].m_ReverseArray = arAlloc.ConstructArray<bool>(maxBlendedClip);
+ stateMachineOutput->m_Left.m_BlendNodeLayer[i].m_MirrorArray = arAlloc.ConstructArray<bool>(maxBlendedClip);
+ stateMachineOutput->m_Left.m_BlendNodeLayer[i].m_CycleOffsetArray = arAlloc.ConstructArray<float>(maxBlendedClip);
+
+ stateMachineOutput->m_Right.m_BlendNodeLayer[i].m_OutputBlendArray = arAlloc.ConstructArray<float>(maxBlendedClip);
+ stateMachineOutput->m_Right.m_BlendNodeLayer[i].m_OutputIndexArray = arAlloc.ConstructArray<uint32_t>(maxBlendedClip);
+ stateMachineOutput->m_Right.m_BlendNodeLayer[i].m_ReverseArray = arAlloc.ConstructArray<bool>(maxBlendedClip);
+ stateMachineOutput->m_Right.m_BlendNodeLayer[i].m_MirrorArray = arAlloc.ConstructArray<bool>(maxBlendedClip);
+ stateMachineOutput->m_Right.m_BlendNodeLayer[i].m_CycleOffsetArray = arAlloc.ConstructArray<float>(maxBlendedClip);
+ }
+
+ return stateMachineOutput;
+ }
+
+ void DestroyStateMachineOutput(StateMachineOutput* apStateMachineOutput, memory::Allocator& arAlloc)
+ {
+ if(apStateMachineOutput)
+ {
+ for(int i=0;i<apStateMachineOutput->m_MotionSetCount;i++)
+ {
+ arAlloc.Deallocate(apStateMachineOutput->m_Right.m_BlendNodeLayer[i].m_OutputBlendArray);
+ arAlloc.Deallocate(apStateMachineOutput->m_Right.m_BlendNodeLayer[i].m_OutputIndexArray);
+ arAlloc.Deallocate(apStateMachineOutput->m_Right.m_BlendNodeLayer[i].m_ReverseArray);
+ arAlloc.Deallocate(apStateMachineOutput->m_Right.m_BlendNodeLayer[i].m_MirrorArray);
+ arAlloc.Deallocate(apStateMachineOutput->m_Right.m_BlendNodeLayer[i].m_CycleOffsetArray);
+
+ arAlloc.Deallocate(apStateMachineOutput->m_Left.m_BlendNodeLayer[i].m_OutputBlendArray);
+ arAlloc.Deallocate(apStateMachineOutput->m_Left.m_BlendNodeLayer[i].m_OutputIndexArray);
+ arAlloc.Deallocate(apStateMachineOutput->m_Left.m_BlendNodeLayer[i].m_ReverseArray);
+ arAlloc.Deallocate(apStateMachineOutput->m_Left.m_BlendNodeLayer[i].m_MirrorArray);
+ arAlloc.Deallocate(apStateMachineOutput->m_Left.m_BlendNodeLayer[i].m_CycleOffsetArray);
+ }
+
+ arAlloc.Deallocate(apStateMachineOutput->m_Left.m_BlendNodeLayer);
+ arAlloc.Deallocate(apStateMachineOutput->m_Right.m_BlendNodeLayer);
+ arAlloc.Deallocate(apStateMachineOutput);
+ }
+ }
+
+ float DoBlendTreeEvaluation( const StateConstant& arStateConstant, StateOutput& arStateOutput, StateMemory& arStateMemory, StateWorkspace& arStateWorkspace, const ValueArrayConstant& arValues, const StateMachineInput& arStateMachineInput, int blendTreeIndex, float weight)
+ {
+ float duration = 0 ;
+ for(uint32_t i = 0 ; i < arStateConstant.m_MotionSetCount ; i++)
+ {
+ animation::BlendTreeConstant const* treeConstant = statemachine::GetBlendTreeConstant(arStateConstant, i);
+ animation::BlendTreeMemory const *treeMemory = statemachine::GetBlendTreeMemory(arStateConstant,arStateMemory, i);
+
+ if(treeConstant)
+ {
+ if(!treeConstant->m_BlendEventArrayConstant.IsNull())
+ {
+ for(uint32_t k = 0 ; k < treeConstant->m_BlendEventArrayConstant->m_Count ; k++)
+ {
+ float blendValue = 0.0f;
+ int32_t index = FindValueIndex(&arValues, treeConstant->m_BlendEventArrayConstant->m_ValueArray[k].m_ID);
+ if(index >=0)
+ {
+ arStateMachineInput.m_Values->ReadData(blendValue, arValues.m_ValueArray[index].m_Index);
+ }
+ arStateWorkspace.m_BlendTreeInputArray[i]->m_BlendValueArray->WriteData(blendValue, treeConstant->m_BlendEventArrayConstant->m_ValueArray[k].m_Index);
+ }
+ }
+
+ animation::EvaluateBlendTree(*treeConstant, *arStateWorkspace.m_BlendTreeInputArray[i], *treeMemory, *arStateWorkspace.m_BlendTreeOutputArray[i], *arStateWorkspace.m_BlendTreeWorkspaceArray[i]);
+
+ uint32_t index = 0 ;
+ uint32_t leafIndex = 0;
+ while(index < arStateWorkspace.m_BlendTreeOutputArray[i]->m_MaxBlendedClip && arStateWorkspace.m_BlendTreeOutputArray[i]->m_OutputBlendArray[index].m_ID != -1)
+ {
+ arStateOutput.m_BlendNode->m_BlendNodeLayer[i].m_OutputBlendArray[arStateOutput.m_BlendNode->m_BlendNodeLayer[i].m_OutputCount] = weight*arStateWorkspace.m_BlendTreeOutputArray[i]->m_OutputBlendArray[index].m_BlendValue;
+
+ arStateOutput.m_BlendNode->m_BlendNodeLayer[i].m_ReverseArray[arStateOutput.m_BlendNode->m_BlendNodeLayer[i].m_OutputCount] = ( arStateWorkspace.m_BlendTreeOutputArray[i]->m_OutputBlendArray[index].m_Reverse && arStateConstant.m_Speed >= 0) ||
+ ( !arStateWorkspace.m_BlendTreeOutputArray[i]->m_OutputBlendArray[index].m_Reverse && arStateConstant.m_Speed < 0);
+
+ arStateOutput.m_BlendNode->m_BlendNodeLayer[i].m_MirrorArray[arStateOutput.m_BlendNode->m_BlendNodeLayer[i].m_OutputCount] = ( arStateWorkspace.m_BlendTreeOutputArray[i]->m_OutputBlendArray[index].m_Mirror || arStateConstant.m_Mirror) &&
+ !(arStateWorkspace.m_BlendTreeOutputArray[i]->m_OutputBlendArray[index].m_Mirror && arStateConstant.m_Mirror);
+
+ arStateOutput.m_BlendNode->m_BlendNodeLayer[i].m_CycleOffsetArray[arStateOutput.m_BlendNode->m_BlendNodeLayer[i].m_OutputCount] = arStateWorkspace.m_BlendTreeOutputArray[i]->m_OutputBlendArray[index].m_CycleOffset + arStateConstant.m_CycleOffset;
+
+ for( ; leafIndex< arStateConstant.m_LeafInfoArray[i].m_Count ; leafIndex++)
+ {
+ /// match leaf ID
+ if(arStateWorkspace.m_BlendTreeOutputArray[i]->m_OutputBlendArray[index].m_ID == arStateConstant.m_LeafInfoArray[i].m_IDArray[leafIndex])
+ {
+ arStateOutput.m_BlendNode->m_BlendNodeLayer[i].m_OutputIndexArray[arStateOutput.m_BlendNode->m_BlendNodeLayer[i].m_OutputCount] = leafIndex + arStateConstant.m_LeafInfoArray[i].m_IndexOffset;
+ arStateOutput.m_BlendNode->m_BlendNodeLayer[i].m_OutputCount++;
+ leafIndex++;
+ break;
+ }
+ }
+
+ index++;
+ }
+
+ // sync layers affect timing
+ float effectiveDurationWeight = 1 ;
+ for(int index = arStateConstant.m_MotionSetCount - 1 ; index >= (int)i+1; --index)
+ {
+ if(statemachine::GetBlendTreeConstant(arStateConstant, index)) // if has motion
+ effectiveDurationWeight -= (effectiveDurationWeight*arStateMachineInput.m_MotionSetTimingWeightArray[index]);
+ }
+
+ duration += arStateWorkspace.m_BlendTreeOutputArray[i]->m_Duration*arStateMachineInput.m_MotionSetTimingWeightArray[i]*effectiveDurationWeight;
+
+
+ }
+ }
+
+ return duration;
+ }
+
+ void EvaluateState( ValueArrayConstant const* apValues,
+ StateConstant const *apStateConstant,
+ StateMachineInput const* apStateMachineInput,
+ StateMachineMemory* apStateMachineMemory,
+ StateOutput *apStateOutput,
+ StateMemory *apStateMemory,
+ StateWorkspace *apStateWorkspace)
+ {
+ for(int i = 0 ; i < apStateConstant->m_MotionSetCount ; i++)
+ {
+ apStateOutput->m_BlendNode->m_BlendNodeLayer[i].m_OutputCount = 0;
+ }
+
+ apStateOutput->m_BlendNode->m_IKOnFeet = apStateConstant->m_IKOnFeet;
+
+ float deltaTime = apStateMachineInput->m_DeltaTime;
+
+ float speed = IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1) ? math::abs(apStateConstant->m_Speed) : 1.f;
+
+ apStateOutput->m_StateDuration = DoBlendTreeEvaluation( *apStateConstant, *apStateOutput, *apStateMemory, *apStateWorkspace, *apValues, *apStateMachineInput, 0, 1.f) / speed;
+
+ if( apStateOutput->m_StateDuration != 0)
+ {
+ deltaTime /= apStateOutput->m_StateDuration;
+ }
+
+ if(apStateMachineMemory->m_ActiveGotoState && apStateMachineInput->m_GotoStateInfo->m_StateID == 0)
+ {
+ apStateOutput->m_BlendNode->m_CurrentTime = apStateMachineInput->m_GotoStateInfo->m_NormalizedTime + (apStateMachineInput->m_GotoStateInfo->m_DenormalizedTimeOffset/apStateOutput->m_StateDuration);
+ apStateOutput->m_BlendNode->m_PreviousTime = apStateMachineInput->m_GotoStateInfo->m_NormalizedTime - deltaTime;
+ apStateMachineMemory->m_ActiveGotoState = false;
+ apStateMachineInput->m_GotoStateInfo->m_DenormalizedTimeOffset = 0.0f;
+ }
+ else
+ {
+ apStateOutput->m_BlendNode->m_CurrentTime = apStateMemory->m_PreviousTime + deltaTime;
+ apStateOutput->m_BlendNode->m_PreviousTime = apStateMemory->m_PreviousTime ;
+ }
+
+ apStateMemory->m_PreviousTime = apStateOutput->m_BlendNode->m_CurrentTime;
+ apStateMemory->m_Duration = apStateOutput->m_StateDuration;
+ }
+
+ void EvaluateTransition ( TransitionConstant const* apTransitionConstant, TransitionInput const *apTransitionInput, TransitionOutput * apTransitionOutput, TransitionMemory const* apTransitionMemory, TransitionWorkspace const* apTransitionWorkspace)
+ {
+ apTransitionOutput->m_DoTransition = apTransitionConstant->m_ConditionConstantCount > 0;
+ apTransitionOutput->m_TransitionDuration = apTransitionConstant->m_TransitionDuration;
+ apTransitionOutput->m_TransitionOffset = apTransitionConstant->m_TransitionOffset;
+ apTransitionOutput->m_TransitionStartTime = 0.0f;
+ apTransitionOutput->m_NextStateStartTime = apTransitionConstant->m_TransitionOffset;
+
+
+ for( uint32_t conditionIndex = 0 ; apTransitionOutput->m_DoTransition && conditionIndex < apTransitionConstant->m_ConditionConstantCount ; conditionIndex++)
+ {
+ apTransitionOutput->m_DoTransition = false;
+ const ConditionConstant* currentCondition = apTransitionConstant->m_ConditionConstantArray[conditionIndex].Get();
+
+ if( currentCondition->m_ConditionMode == kConditionModeIf ||
+ currentCondition->m_ConditionMode == kConditionModeIfNot ||
+ currentCondition->m_ConditionMode == kConditionModeGreater ||
+ currentCondition->m_ConditionMode == kConditionModeLess ||
+ currentCondition->m_ConditionMode == kConditionModeEquals ||
+ currentCondition->m_ConditionMode == kConditionModeNotEqual)
+ {
+ int32_t index = FindValueIndex(apTransitionMemory->m_ValuesConstant, currentCondition->m_EventID);
+
+ if(index > -1)
+ {
+ ValueConstant const& valueConstant = apTransitionMemory->m_ValuesConstant->m_ValueArray[index];
+ if(currentCondition->m_ConditionMode == kConditionModeIf || currentCondition->m_ConditionMode == kConditionModeIfNot)
+ {
+ bool booleanEvent;
+ apTransitionInput->m_Values->ReadData(booleanEvent, valueConstant.m_Index);
+ apTransitionOutput->m_DoTransition = currentCondition->m_ConditionMode == kConditionModeIf ? booleanEvent : !booleanEvent;
+ }
+ else if (currentCondition->m_ConditionMode == kConditionModeEquals || currentCondition->m_ConditionMode == kConditionModeNotEqual)
+ {
+ mecanim::int32_t intEvent;
+ apTransitionInput->m_Values->ReadData(intEvent, valueConstant.m_Index);
+ apTransitionOutput->m_DoTransition = currentCondition->m_ConditionMode == kConditionModeEquals ? intEvent == currentCondition->m_EventThreshold : intEvent != currentCondition->m_EventThreshold;
+ }
+ else
+ {
+ if(valueConstant.m_Type == mecanim::kFloatType)
+ {
+ float floatEvent;
+ apTransitionInput->m_Values->ReadData(floatEvent, valueConstant.m_Index);
+ apTransitionOutput->m_DoTransition = currentCondition->m_ConditionMode == kConditionModeGreater ? floatEvent > currentCondition->m_EventThreshold : floatEvent < currentCondition->m_EventThreshold;
+ }
+ else if(valueConstant.m_Type == mecanim::kInt32Type)
+ {
+ mecanim::int32_t intEvent;
+ apTransitionInput->m_Values->ReadData(intEvent, valueConstant.m_Index);
+ apTransitionOutput->m_DoTransition = currentCondition->m_ConditionMode == kConditionModeGreater ? intEvent > currentCondition->m_EventThreshold : intEvent < currentCondition->m_EventThreshold;
+ }
+
+ }
+ }
+ }
+ else if(currentCondition->m_ConditionMode == kConditionModeExitTime)
+ {
+ float relativeTimeError = 0;
+ if(currentCondition->m_ExitTime <= 1)
+ {
+ float previousTimeLow = math::fmod(apTransitionInput->m_PreviousTime,1);
+ float currentTimeLow = math::fmod(apTransitionInput->m_CurrentTime,1);
+
+ float previousTimeHigh = previousTimeLow;
+ float currentTimeHigh = currentTimeLow;
+
+ if(previousTimeLow > currentTimeLow)
+ {
+ previousTimeLow -= 1;
+ currentTimeHigh += 1;
+ }
+
+ if(previousTimeLow < currentCondition->m_ExitTime && currentTimeLow >= currentCondition->m_ExitTime)
+ {
+ apTransitionOutput->m_DoTransition = true;
+ relativeTimeError = (currentTimeLow - currentCondition->m_ExitTime);
+ }
+ else if(previousTimeHigh < currentCondition->m_ExitTime && currentTimeHigh >= currentCondition->m_ExitTime)
+ {
+ apTransitionOutput->m_DoTransition = true;
+ relativeTimeError = (currentTimeHigh - currentCondition->m_ExitTime);
+ }
+ }
+ else if ( apTransitionInput->m_PreviousTime < currentCondition->m_ExitTime && apTransitionInput->m_CurrentTime >= currentCondition->m_ExitTime)
+ {
+ apTransitionOutput->m_DoTransition = true;
+ relativeTimeError = (apTransitionInput->m_CurrentTime - currentCondition->m_ExitTime);
+ }
+
+ if(apTransitionOutput->m_DoTransition)
+ {
+ apTransitionOutput->m_TransitionStartTime = apTransitionOutput->m_TransitionDuration == 0 ? 1 : (relativeTimeError / apTransitionOutput->m_TransitionDuration);
+ apTransitionOutput->m_NextStateStartTime = apTransitionOutput->m_TransitionOffset;
+ apTransitionOutput->m_NextStateStartInitialDeltaTime = relativeTimeError; // this is in source state normalized time
+ }
+ }
+ }
+
+ }
+
+ void EvaluateStateMachine( StateMachineConstant const* apStateMachineConstant,
+ StateMachineInput const* apStateMachineInput,
+ StateMachineOutput * apStateMachineOutput,
+ StateMachineMemory * apStateMachineMemory,
+ StateMachineWorkspace * apStateMachineWorkspace)
+ {
+
+ /// Initialize workspace values
+ apStateMachineOutput->m_BlendFactor = 0;
+ for(uint32_t i = 0 ; i < apStateMachineConstant->m_MotionSetCount; i++)
+ {
+ apStateMachineOutput->m_Right.m_BlendNodeLayer[i].m_OutputCount = 0;
+ apStateMachineOutput->m_Left.m_BlendNodeLayer[i].m_OutputCount = 0;
+ }
+
+ if(apStateMachineConstant->m_StateConstantCount == 0)
+ {
+ for(int i = 0 ; i < apStateMachineConstant->m_MotionSetCount; i++)
+ apStateMachineMemory->m_MotionSetAutoWeightArray[i] = 0;
+ return ;
+ }
+
+ float deltaTime = apStateMachineInput->m_DeltaTime;
+
+ const uint32_t currentStateIndex = apStateMachineMemory->m_CurrentStateIndex;
+ const StateConstant* currentState = apStateMachineConstant->m_StateConstantArray[currentStateIndex].Get();
+ StateMemory* currentStateMemory = apStateMachineMemory->m_StateMemoryArray[currentStateIndex].Get();
+ StateWorkspace* currentStateWS = apStateMachineWorkspace->m_StateWorkspaceArray[currentStateIndex];
+
+ StateOutput outputSrc;
+ outputSrc.m_BlendNode = &apStateMachineOutput->m_Left;
+ EvaluateState(apStateMachineWorkspace->m_ValuesConstant, currentState, apStateMachineInput, apStateMachineMemory, &outputSrc, currentStateMemory, currentStateWS);
+
+ TransitionInput transitionInput;
+ transitionInput.m_Values = apStateMachineInput->m_Values;
+ transitionInput.m_CurrentStatePreviousTime = currentStateMemory->m_PreviousTime;
+ transitionInput.m_CurrentStateDuration = outputSrc.m_StateDuration;
+ transitionInput.m_CurrentTime = outputSrc.m_BlendNode->m_CurrentTime;
+ transitionInput.m_PreviousTime = outputSrc.m_BlendNode->m_PreviousTime;
+
+ TransitionOutput transitionOutput;
+
+ TransitionMemory transitionMemory;
+ transitionMemory.m_ValuesConstant = apStateMachineWorkspace->m_ValuesConstant;
+
+ bool startTransition = false;
+ bool wasInTransition = apStateMachineMemory->m_InTransition ;
+
+ const TransitionConstant* currentTransition = wasInTransition ? GetTransitionConstant(apStateMachineConstant, apStateMachineConstant->m_StateConstantArray[apStateMachineMemory->m_CurrentStateIndex].Get(), apStateMachineMemory->m_TransitionId) : 0;
+ bool isCurrentTransitionAnyState = apStateMachineMemory->m_TransitionId >= s_AnyTransitionEncodeKey;
+
+ for(int i = 0 ; i < apStateMachineConstant->m_MotionSetCount; i++)
+ apStateMachineMemory->m_MotionSetAutoWeightArray[i] = outputSrc.m_BlendNode->m_BlendNodeLayer[i].m_OutputCount ? 1 : 0;
+
+ ////////////////////////////////////////////////////////
+ // Validate if we need to transition
+ if(apStateMachineMemory->m_ActiveGotoState)
+ {
+ int32_t nextState = GetStateIndex(apStateMachineConstant, apStateMachineInput->m_GotoStateInfo->m_StateID);
+ if(nextState != -1)
+ {
+ startTransition = true;
+
+ apStateMachineMemory->m_ActiveGotoState = false;
+
+ apStateMachineMemory->m_InTransition = true;
+ apStateMachineMemory->m_NextStateIndex = nextState;
+ apStateMachineMemory->m_TransitionId = s_DynamicTransitionEncodeKey;
+ apStateMachineMemory->m_TransitionDuration = apStateMachineInput->m_GotoStateInfo->m_TransitionDuration;
+ apStateMachineMemory->m_TransitionOffset = apStateMachineInput->m_GotoStateInfo->m_NormalizedTime;
+ apStateMachineMemory->m_TransitionTime = apStateMachineInput->m_GotoStateInfo->m_TransitionTime;
+
+ apStateMachineMemory->m_StateMemoryArray[apStateMachineMemory->m_NextStateIndex]->m_PreviousTime = apStateMachineInput->m_GotoStateInfo->m_NormalizedTime + apStateMachineMemory->m_TransitionTime;
+ apStateMachineMemory->m_InInterruptedTransition = wasInTransition;
+ }
+ }
+
+ // Dynamic transition cannot be interrupted by any transition
+ for(uint32_t run = 0 ; apStateMachineMemory->m_TransitionId != s_DynamicTransitionEncodeKey && !startTransition && run < 2 ; run++) // run 0 for AnyState, run 1 for transition in current state
+ {
+ /////////////////////
+ int transitionCount = 0 ;
+
+ if(apStateMachineMemory->m_InTransition)
+ {
+ if(!currentTransition->m_Atomic)
+ {
+ if ( run == 0 )
+ transitionCount = isCurrentTransitionAnyState ? apStateMachineMemory->m_TransitionId - s_AnyTransitionEncodeKey : apStateMachineConstant->m_AnyStateTransitionConstantCount;
+ else
+ transitionCount = !isCurrentTransitionAnyState? apStateMachineMemory->m_TransitionId : 0 ; // anyState has higher priority
+ }
+ // else -> the transition is atomic, cannot be interrupted, transitionCount = 0
+ }
+ else
+ {
+ transitionCount = run == 0 ? apStateMachineConstant->m_AnyStateTransitionConstantCount : currentState->m_TransitionConstantCount;
+ }
+
+ for(uint32_t i = 0 ; !startTransition && i < transitionCount; i++)
+ {
+ const TransitionConstant* transitionConstant = run == 0 ? apStateMachineConstant->m_AnyStateTransitionConstantArray[i].Get() : currentState->m_TransitionConstantArray[i].Get();
+ TransitionWorkspace* transitionWorkspace = run == 0 ? apStateMachineWorkspace->m_AnyStateTransitionWorkspaceArray[i] : currentStateWS->m_TransitionWorkspaceArray[i];
+
+ transitionMemory.m_InTransition = apStateMachineMemory->m_InTransition;
+
+ EvaluateTransition(transitionConstant, &transitionInput, &transitionOutput, &transitionMemory, transitionWorkspace);
+
+ if(transitionOutput.m_DoTransition)
+ {
+ startTransition = true;
+
+ apStateMachineMemory->m_InTransition = true;
+ apStateMachineMemory->m_NextStateIndex = transitionConstant->m_DestinationState;
+ apStateMachineMemory->m_TransitionId = run == 0 ? (i + s_AnyTransitionEncodeKey) : i;
+ apStateMachineMemory->m_TransitionDuration = transitionOutput.m_TransitionDuration;
+ apStateMachineMemory->m_TransitionOffset = transitionOutput.m_TransitionOffset;
+ apStateMachineMemory->m_TransitionTime = transitionOutput.m_TransitionStartTime;
+ apStateMachineMemory->m_InInterruptedTransition = wasInTransition;
+
+ apStateMachineMemory->m_ActiveGotoState = true;
+ apStateMachineInput->m_GotoStateInfo->m_StateID = 0;
+ apStateMachineInput->m_GotoStateInfo->m_NormalizedTime = transitionOutput.m_NextStateStartTime;
+ apStateMachineInput->m_GotoStateInfo->m_DenormalizedTimeOffset = transitionOutput.m_NextStateStartInitialDeltaTime * currentStateMemory->m_Duration; // denormalize source state time to denormalizedTime
+
+ }
+ }
+ }
+
+
+ if(apStateMachineMemory->m_InTransition)
+ {
+ const int32_t nextStateIndex = apStateMachineMemory->m_NextStateIndex;
+ const StateConstant* nextState = apStateMachineConstant->m_StateConstantArray[nextStateIndex].Get();
+ StateMemory* nextStateMemory = apStateMachineMemory->m_StateMemoryArray[nextStateIndex].Get();
+ StateWorkspace* nextStateWS = apStateMachineWorkspace->m_StateWorkspaceArray[nextStateIndex];
+
+ if(wasInTransition) // delta already applied in m_TransitionStartTime when transition was just triggered, so dont add twice.
+ apStateMachineMemory->m_TransitionTime += apStateMachineMemory->m_TransitionDuration == 0 ? 1 : (deltaTime / (apStateMachineMemory->m_TransitionDuration * (outputSrc.m_StateDuration != 0 ? outputSrc.m_StateDuration : 1.0f)));
+ apStateMachineOutput->m_BlendFactor = math::saturate(apStateMachineMemory->m_TransitionTime);
+
+
+ StateOutput outputDst;
+ outputDst.m_BlendNode = &apStateMachineOutput->m_Right;
+ EvaluateState(apStateMachineWorkspace->m_ValuesConstant, nextState, apStateMachineInput, apStateMachineMemory, &outputDst, nextStateMemory, nextStateWS);
+
+ for(int i = 0 ; i < apStateMachineConstant->m_MotionSetCount; i++)
+ {
+ if(apStateMachineOutput->m_Left.m_BlendNodeLayer[i].m_OutputCount == 0 && apStateMachineOutput->m_Right.m_BlendNodeLayer[i].m_OutputCount > 0)
+ apStateMachineMemory->m_MotionSetAutoWeightArray[i] = apStateMachineOutput->m_BlendFactor;
+ else if(apStateMachineOutput->m_Right.m_BlendNodeLayer[i].m_OutputCount == 0 && apStateMachineOutput->m_Left.m_BlendNodeLayer[i].m_OutputCount > 0)
+ apStateMachineMemory->m_MotionSetAutoWeightArray[i] = 1 - apStateMachineOutput->m_BlendFactor;
+
+ }
+
+ /////////////////////////////////////////
+ // Transition is finished
+ if(apStateMachineMemory->m_TransitionTime >= 1)
+ {
+ for( uint32_t conditionIndex = 0 ; currentTransition != 0 && conditionIndex < currentTransition->m_ConditionConstantCount ; conditionIndex++)
+ {
+ const ConditionConstant* currentCondition = currentTransition->m_ConditionConstantArray[conditionIndex].Get();
+
+ if( currentCondition->m_ConditionMode == kConditionModeIf)
+ {
+ int32_t index = FindValueIndex(apStateMachineWorkspace->m_ValuesConstant, currentCondition->m_EventID);
+
+ if(index > -1)
+ {
+ ValueConstant const& valueConstant = apStateMachineWorkspace->m_ValuesConstant->m_ValueArray[index];
+ if(valueConstant.m_Type == mecanim::kTriggerType)
+ apStateMachineInput->m_Values->WriteData(false, valueConstant.m_Index);
+ }
+ }
+ }
+
+ apStateMachineMemory->m_InTransition = false;
+ apStateMachineMemory->m_TransitionTime = 0.f;
+ apStateMachineMemory->m_TransitionId = 0;
+ apStateMachineMemory->m_TransitionDuration = 0.f;
+ apStateMachineMemory->m_TransitionOffset = 0.f;
+ apStateMachineMemory->m_CurrentStateIndex = apStateMachineMemory->m_NextStateIndex;
+ apStateMachineMemory->m_InInterruptedTransition = false;
+
+
+ BlendNodeLayer *nodeSwap = apStateMachineOutput->m_Left.m_BlendNodeLayer;
+ apStateMachineOutput->m_Left = apStateMachineOutput->m_Right;
+ apStateMachineOutput->m_Right.m_BlendNodeLayer = nodeSwap;
+ apStateMachineOutput->m_BlendFactor = 0.f;
+ apStateMachineOutput->m_Right.m_BlendNodeLayer[0].m_OutputCount = 0;
+
+ }
+ }
+ }
+
+ void StateConstant::InitializeClass()
+ {
+ RegisterAllowNameConversion("StateConstant", "m_ID", "m_PathID");
+ }
+
+
+}//namespace statemachine
+
+}//namespace mecanim
diff --git a/Runtime/mecanim/statemachine/statemachine.h b/Runtime/mecanim/statemachine/statemachine.h
new file mode 100644
index 0000000..6de46a9
--- /dev/null
+++ b/Runtime/mecanim/statemachine/statemachine.h
@@ -0,0 +1,547 @@
+#pragma once
+
+#include "Runtime/mecanim/animation/blendtree.h"
+#include "Runtime/mecanim/generic/valuearray.h"
+
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Animation/MecanimArraySerialization.h"
+
+namespace mecanim
+{
+
+namespace statemachine
+{
+ static const mecanim::int32_t s_AnyTransitionEncodeKey = 20000;
+ static const mecanim::int32_t s_DynamicTransitionEncodeKey = 30000;
+
+ enum ConditionMode
+ {
+ //kConditionModeGraph = 0,
+ kConditionModeIf = 1, // backward compat
+ kConditionModeIfNot,
+ kConditionModeGreater,
+ kConditionModeLess,
+ kConditionModeExitTime,
+ kConditionModeEquals,
+ kConditionModeNotEqual,
+ kConditionModeCount
+ };
+
+ struct GotoStateInfo
+ {
+ GotoStateInfo():m_StateID(0),m_NormalizedTime(0),m_TransitionDuration(0),m_TransitionTime(0), m_DenormalizedTimeOffset(0){}
+
+ uint32_t m_StateID;
+ float m_NormalizedTime;
+ float m_TransitionDuration;
+ float m_TransitionTime;
+ float m_DenormalizedTimeOffset; // used to offset by seconds, internally used when transitions are taken
+ };
+
+
+ struct BlendNodeLayer
+ {
+ BlendNodeLayer() : m_OutputCount(0), m_OutputIndexArray(0), m_OutputBlendArray(0), m_ReverseArray(0), m_MirrorArray(0), m_CycleOffsetArray(0) {}
+ uint32_t m_OutputCount;
+ uint32_t* m_OutputIndexArray;
+ float* m_OutputBlendArray;
+ bool* m_ReverseArray;
+ bool* m_MirrorArray;
+ float* m_CycleOffsetArray;
+ };
+
+ struct BlendNode
+ {
+ BlendNode(): m_BlendNodeLayer(0), m_CurrentTime(0),m_PreviousTime(0), m_IKOnFeet(false){}
+
+ BlendNodeLayer* m_BlendNodeLayer;
+
+ float m_CurrentTime;
+ float m_PreviousTime;
+ bool m_IKOnFeet;
+ };
+
+
+ struct ConditionConstant
+ {
+ DEFINE_GET_TYPESTRING(ConditionConstant)
+
+ ConditionConstant() : m_ConditionMode(kConditionModeIf),
+ m_EventID(0),
+ m_EventThreshold(0.f),
+ m_ExitTime(0)
+ {
+ }
+
+ uint32_t m_ConditionMode;
+ uint32_t m_EventID;
+
+ float m_EventThreshold;
+ float m_ExitTime;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_ConditionMode);
+ TRANSFER(m_EventID);
+ TRANSFER(m_EventThreshold);
+ TRANSFER(m_ExitTime);
+ }
+ };
+
+ struct TransitionConstant
+ {
+ DEFINE_GET_TYPESTRING(TransitionConstant)
+
+ TransitionConstant() : m_ConditionConstantCount(0),
+ m_DestinationState(0),
+ m_ID(0),
+ m_UserID(0),
+ m_TransitionDuration(0),
+ m_TransitionOffset(0),
+ m_Atomic(true)
+
+ {}
+
+
+ uint32_t m_ConditionConstantCount;
+ OffsetPtr< OffsetPtr<ConditionConstant> > m_ConditionConstantArray;
+
+ uint32_t m_DestinationState;
+ uint32_t m_ID;
+ uint32_t m_UserID;
+
+ float m_TransitionDuration;
+ float m_TransitionOffset;
+
+ bool m_Atomic;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_ConditionConstantCount);
+ MANUAL_ARRAY_TRANSFER2( OffsetPtr<mecanim::statemachine::ConditionConstant>, m_ConditionConstantArray, m_ConditionConstantCount);
+
+ TRANSFER(m_DestinationState);
+ TRANSFER(m_ID);
+ TRANSFER(m_UserID);
+
+ TRANSFER(m_TransitionDuration);
+ TRANSFER(m_TransitionOffset);
+
+ TRANSFER(m_Atomic);
+ transfer.Align();
+ }
+ };
+
+ struct TransitionWorkspace
+ {
+ TransitionWorkspace(): m_ConditionConstantCount(0)
+ {
+ }
+
+ uint32_t m_ConditionConstantCount;
+ };
+
+
+ struct TransitionInput
+ {
+ TransitionInput(): m_Values(0),
+ m_CurrentStatePreviousTime(0),
+ m_CurrentStateDuration(0),
+ m_CurrentTime(0),
+ m_PreviousTime()
+ {}
+ ValueArray* m_Values;
+ float m_CurrentStatePreviousTime;
+ float m_CurrentStateDuration;
+ float m_CurrentTime;
+ float m_PreviousTime;
+ };
+
+
+ struct TransitionOutput
+ {
+ TransitionOutput(): m_DoTransition(false),
+ m_TransitionStartTime(0),
+ m_NextStateStartTime(0),
+ m_NextStateStartInitialDeltaTime(0),
+ m_TransitionDuration(0),
+ m_TransitionOffset(0)
+ {}
+
+ bool m_DoTransition;
+ float m_TransitionStartTime;
+ float m_NextStateStartTime;
+ float m_NextStateStartInitialDeltaTime;
+ float m_TransitionDuration;
+ float m_TransitionOffset;
+
+ };
+
+ struct TransitionMemory
+ {
+ TransitionMemory(): m_ValuesConstant(0),
+ m_InTransition(false)
+ {}
+
+ ValueArrayConstant const* m_ValuesConstant;
+ bool m_InTransition;
+ };
+
+
+ struct LeafInfoConstant
+ {
+ DEFINE_GET_TYPESTRING(LeafInfoConstant)
+
+ LeafInfoConstant() : m_Count(0), m_IndexOffset(0)
+ {
+
+ }
+
+ uint32_t m_Count;
+ OffsetPtr<uint32_t> m_IDArray;
+ uint32_t m_IndexOffset;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_Count);
+ MANUAL_ARRAY_TRANSFER2(mecanim::uint32_t, m_IDArray, m_Count);
+ TRANSFER(m_IndexOffset);
+ }
+ };
+
+ struct StateConstant
+ {
+ DEFINE_GET_TYPESTRING(StateConstant)
+
+ StateConstant() : m_TransitionConstantCount(0),
+ m_MotionSetCount(0),
+ m_BlendTreeCount(0),
+ m_NameID(0),
+ m_PathID(0),
+ m_TagID(0),
+ m_Speed(1),
+ m_CycleOffset(0),
+ m_IKOnFeet(true),
+ m_Loop(false),
+ m_Mirror(false)
+ {
+ }
+
+ uint32_t m_TransitionConstantCount;
+ OffsetPtr< OffsetPtr<TransitionConstant> > m_TransitionConstantArray;
+
+ uint32_t m_MotionSetCount;
+ OffsetPtr<int32_t> m_BlendTreeConstantIndexArray;
+ OffsetPtr<LeafInfoConstant> m_LeafInfoArray;
+
+ uint32_t m_BlendTreeCount;
+ OffsetPtr< OffsetPtr<animation::BlendTreeConstant> > m_BlendTreeConstantArray;
+
+ uint32_t m_NameID;
+ uint32_t m_PathID;
+ uint32_t m_TagID;
+
+ float m_Speed;
+ float m_CycleOffset;
+ bool m_IKOnFeet;
+ bool m_Loop;
+ bool m_Mirror;
+
+ static void InitializeClass();
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_TransitionConstantCount);
+ MANUAL_ARRAY_TRANSFER2( OffsetPtr<mecanim::statemachine::TransitionConstant>, m_TransitionConstantArray, m_TransitionConstantCount);
+
+ TRANSFER_BLOB_ONLY(m_MotionSetCount);
+ MANUAL_ARRAY_TRANSFER2(int32_t, m_BlendTreeConstantIndexArray, m_MotionSetCount);
+ MANUAL_ARRAY_TRANSFER2(LeafInfoConstant, m_LeafInfoArray, m_MotionSetCount);
+
+ TRANSFER_BLOB_ONLY(m_BlendTreeCount);
+ MANUAL_ARRAY_TRANSFER2( OffsetPtr<mecanim::animation::BlendTreeConstant>, m_BlendTreeConstantArray, m_BlendTreeCount);
+
+ TRANSFER(m_NameID);
+ TRANSFER(m_PathID);
+ TRANSFER(m_TagID);
+
+ TRANSFER(m_Speed);
+ TRANSFER(m_CycleOffset);
+
+ TRANSFER(m_IKOnFeet);
+ TRANSFER(m_Loop);
+ TRANSFER(m_Mirror);
+
+ transfer.Align();
+ }
+ };
+
+ struct StateMemory
+ {
+ DEFINE_GET_TYPESTRING(StateMemory)
+
+ StateMemory(): m_PreviousTime(0), m_Duration(0)
+ {}
+ uint32_t m_BlendTreeCount;
+ OffsetPtr< OffsetPtr<animation::BlendTreeMemory> > m_BlendTreeMemoryArray;
+
+ float m_PreviousTime;
+ float m_Duration;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_BlendTreeCount);
+ MANUAL_ARRAY_TRANSFER2( OffsetPtr<mecanim::animation::BlendTreeMemory>, m_BlendTreeMemoryArray, m_BlendTreeCount);
+
+ TRANSFER(m_PreviousTime);
+ TRANSFER(m_Duration);
+
+ transfer.Align();
+ }
+
+ };
+
+ struct StateWorkspace
+ {
+ StateWorkspace():m_TransitionWorkspaceArray(0),
+ m_BlendTreeInputArray(0),
+ m_BlendTreeOutputArray(0),
+ m_BlendTreeWorkspaceArray(0),
+ m_TransitionWorkspaceCount(0)
+ {}
+
+ TransitionWorkspace** m_TransitionWorkspaceArray;
+
+
+ animation::BlendTreeInput** m_BlendTreeInputArray;
+ animation::BlendTreeOutput** m_BlendTreeOutputArray;
+ animation::BlendTreeWorkspace** m_BlendTreeWorkspaceArray;
+
+ uint32_t m_TransitionWorkspaceCount;
+ uint32_t m_MotionSetCount;
+ };
+
+
+
+ struct StateOutput
+ {
+ StateOutput() : m_BlendNode(0), m_StateDuration(0) {}
+
+ float m_StateDuration;
+
+ BlendNode* m_BlendNode;
+ };
+
+
+ struct StateMachineConstant
+ {
+ DEFINE_GET_TYPESTRING(StateMachineConstant)
+
+ StateMachineConstant() : m_StateConstantCount(0),
+ m_AnyStateTransitionConstantCount(0),
+ m_DefaultState(0),
+ m_MotionSetCount(0)
+ {}
+
+ uint32_t m_StateConstantCount;
+ OffsetPtr< OffsetPtr<StateConstant> > m_StateConstantArray;
+
+ uint32_t m_AnyStateTransitionConstantCount;
+ OffsetPtr< OffsetPtr<TransitionConstant> > m_AnyStateTransitionConstantArray;
+
+ uint32_t m_DefaultState;
+ uint32_t m_MotionSetCount;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_StateConstantCount);
+ MANUAL_ARRAY_TRANSFER2( OffsetPtr<mecanim::statemachine::StateConstant> , m_StateConstantArray, m_StateConstantCount);
+
+ TRANSFER_BLOB_ONLY(m_AnyStateTransitionConstantCount);
+ MANUAL_ARRAY_TRANSFER2( OffsetPtr<mecanim::statemachine::TransitionConstant>, m_AnyStateTransitionConstantArray, m_AnyStateTransitionConstantCount);
+
+ TRANSFER(m_DefaultState);
+ TRANSFER(m_MotionSetCount);
+ }
+ };
+
+ struct StateMachineInput
+ {
+ StateMachineInput() : m_Values(0)
+ {}
+
+ float m_DeltaTime;
+ ValueArray* m_Values;
+
+ float* m_MotionSetTimingWeightArray; // allocated externally
+
+ GotoStateInfo* m_GotoStateInfo;
+ };
+
+ struct StateMachineMemory
+ {
+ DEFINE_GET_TYPESTRING(StateMachineMemory)
+
+ StateMachineMemory() : m_MotionSetCount(0),
+ m_StateMemoryCount(0),
+ m_CurrentStateIndex(0),
+ m_NextStateIndex(0),
+ m_TransitionId(0),
+ m_TransitionTime(0),
+ m_TransitionDuration(0),
+ m_TransitionOffset(0),
+ m_InInterruptedTransition(false),
+ m_InTransition(false),
+ m_ActiveGotoState(false)
+ {}
+
+ uint32_t m_MotionSetCount;
+ OffsetPtr<float> m_MotionSetAutoWeightArray;
+
+ uint32_t m_StateMemoryCount;
+ OffsetPtr<OffsetPtr<StateMemory> > m_StateMemoryArray;
+
+ uint32_t m_CurrentStateIndex;
+ uint32_t m_NextStateIndex;
+ uint32_t m_TransitionId;
+
+ float m_TransitionTime;
+ float m_TransitionDuration;
+ float m_TransitionOffset;
+
+ bool m_InInterruptedTransition;
+ bool m_InTransition;
+ bool m_ActiveGotoState;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_MotionSetCount);
+ MANUAL_ARRAY_TRANSFER2( float, m_MotionSetAutoWeightArray, m_MotionSetCount);
+
+ TRANSFER_BLOB_ONLY(m_StateMemoryCount);
+ MANUAL_ARRAY_TRANSFER2( OffsetPtr<StateMemory>, m_StateMemoryArray, m_StateMemoryCount);
+
+ TRANSFER(m_CurrentStateIndex);
+ TRANSFER(m_NextStateIndex);
+ TRANSFER(m_TransitionId);
+
+ TRANSFER(m_TransitionTime);
+ TRANSFER(m_TransitionDuration);
+ TRANSFER(m_TransitionOffset);
+
+ TRANSFER(m_InInterruptedTransition);
+ TRANSFER(m_InTransition);
+ TRANSFER(m_ActiveGotoState);
+ transfer.Align();
+ }
+ };
+
+ struct StateMachineWorkspace
+ {
+ StateMachineWorkspace() : m_StateWorkspaceArray(0),
+ m_StateWorkspaceCount(0),
+ m_AnyStateTransitionWorkspaceArray(0),
+ m_AnyStateTransitionWorkspaceCount(0)
+ {}
+
+ StateWorkspace** m_StateWorkspaceArray;
+ uint32_t m_StateWorkspaceCount;
+
+ TransitionWorkspace** m_AnyStateTransitionWorkspaceArray;
+ uint32_t m_AnyStateTransitionWorkspaceCount;
+
+ ValueArrayConstant* m_ValuesConstant;
+
+ };
+
+ struct StateMachineOutput
+ {
+ StateMachineOutput(): m_BlendFactor(0),m_MotionSetCount(0)
+ {}
+
+ float m_BlendFactor;
+
+ BlendNode m_Left;
+ BlendNode m_Right;
+
+ uint32_t m_MotionSetCount;
+
+
+ };
+
+
+
+ TransitionConstant const* GetTransitionConstant(StateMachineConstant const* apStateMachineConstant, StateConstant const* apStateConstant, uint32_t id);
+
+ bool IsCurrentTransitionAtomic(StateMachineConstant const* apStateMachineConstant, StateMachineMemory *apStateMachineMemory);
+
+ ConditionConstant *CreateConditionConstant( uint32_t aConditionMode, uint32_t aEventID, float aEventThreshold, float aExitTime, memory::Allocator& arAlloc);
+ void DestroyConditionConstant(ConditionConstant *apConditionConstant, memory::Allocator& arAlloc);
+
+ TransitionConstant *CreateTransitionConstant(ConditionConstant** apConditionsConstantArray, uint32_t aConditionConstantCount, uint32_t aDestinationState,
+ float aTransitionDuration, float aTransitionOffset, bool aAtomic, uint32_t aID, uint32_t aUserID, memory::Allocator& arAlloc);
+
+ void DestroyTransitionConstant(TransitionConstant *apTransitionConstant, memory::Allocator& arAlloc);
+
+
+ TransitionWorkspace* CreateTransitionWorkspace(TransitionConstant const* apTransitionConstant, memory::Allocator& arAlloc);
+ void DestroyTransitionWorkspace(TransitionWorkspace* apTransitionWorkspace, memory::Allocator& arAlloc);
+
+
+ StateConstant* CreateStateConstant(TransitionConstant** apTransitionConstantArray, uint32_t aTransitionConstantCount,
+ float aSpeed, bool aIKOnFeet, bool aMirror, float aCycleOffset, animation::BlendTreeConstant** apBlendTreeConstantArray,
+ uint32_t aMotionSetCount, uint32_t nameID, uint32_t pathID, uint32_t aTagID, bool aLoop, memory::Allocator& arAlloc);
+
+ void DestroyStateConstant(StateConstant* apStateConstant, memory::Allocator& arAlloc);
+
+ void DestroyStateMemory(StateMemory* apStateMemory, memory::Allocator& arAlloc);
+
+ StateWorkspace* CreateStateWorkspace(StateConstant const* apStateConstant, memory::Allocator& arAlloc);
+ void DestroyStateWorkspace(StateWorkspace* apStateWorkspace, memory::Allocator& arAlloc);
+
+ StateOutput* CreateStateOutput(StateConstant const* apStateConstant, memory::Allocator& arAlloc);
+ void DestroyStateOutput(StateOutput* apStateOutput, memory::Allocator& arAlloc);
+
+ StateMachineConstant* CreateStateMachineConstant(StateConstant** apStateConstantArray, uint32_t aStateConstantCount, uint32_t aDefaultState,
+ TransitionConstant** apAnyStateTransitionConstantArray, uint32_t aAnyStateTransitionConstantCount, uint32_t aMotionSetCount,
+ memory::Allocator& arAlloc);
+ void DestroyStateMachineConstant(StateMachineConstant* apStateMachineConstant, memory::Allocator& arAlloc);
+
+ void ClearStateMachineConstant(StateMachineConstant* apStateMachineConstant, memory::Allocator& arAlloc);
+
+ StateMachineInput* CreateStateMachineInput(StateMachineConstant const* apStateMachineConstant, memory::Allocator& arAlloc);
+ void DestroyStateMachineInput(StateMachineInput* apStateMachineInput, memory::Allocator& arAlloc);
+
+ StateMachineMemory* CreateStateMachineMemory(StateMachineConstant const* apStateMachineConstant, memory::Allocator& arAlloc);
+ void DestroyStateMachineMemory(StateMachineMemory* apStateMachineMemory, memory::Allocator& arAlloc);
+
+
+ StateMachineWorkspace* CreateStateMachineWorkspace(StateMachineConstant const* apStateMachineConstant, uint32_t maxBlendedClip, memory::Allocator& arAlloc);
+ void DestroyStateMachineWorkspace(StateMachineWorkspace* apStateMachineWorkspace, memory::Allocator& arAlloc);
+
+ StateMachineOutput* CreateStateMachineOutput(StateMachineConstant const* apStateMachineConstant, uint32_t maxBlendedClip, memory::Allocator& arAlloc);
+ void DestroyStateMachineOutput(StateMachineOutput* apStateMachineOutput, memory::Allocator& arAlloc);
+
+ void EvaluateStateMachine(StateMachineConstant const* apStateMachineConstant, StateMachineInput const* apStateMachineInput,
+ StateMachineOutput * apStateMachineOutput, StateMachineMemory * apStateMachineMemory,
+ StateMachineWorkspace * apStateMachineWorkspace);
+
+ animation::BlendTreeConstant const* GetBlendTreeConstant(const StateConstant& arStateConstant, mecanim::int32_t aMotionSetCount);
+ animation::BlendTreeMemory *GetBlendTreeMemory(const StateConstant& arStateConstant,StateMemory& arStateMemory, mecanim::int32_t aMotionSetIndex);
+
+ int32_t GetStateIndex(StateMachineConstant const* apStateMachineConstant, uint32_t id);
+
+ inline int32_t CompareStateID (StateConstant const* state, uint32_t id) { return state->m_PathID == id || state->m_NameID == id; }
+ }
+}
+
+
+
diff --git a/Runtime/mecanim/string.h b/Runtime/mecanim/string.h
new file mode 100644
index 0000000..8a1d772
--- /dev/null
+++ b/Runtime/mecanim/string.h
@@ -0,0 +1,96 @@
+#pragma once
+
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/types.h"
+
+#include "Runtime/Serialize/SerializeTraits.h"
+
+#include <string.h>
+
+namespace mecanim
+{
+ template<int CAPACITY = 128> class basic_string
+ {
+ protected:
+ char eos(){return '\0';}
+ char mStr[CAPACITY];
+ void terminatestr(){mStr[CAPACITY-1] = eos();}
+ public:
+
+ typedef char value_type;
+ typedef char* iterator;
+ typedef char const * const_iterator;
+
+ static const int npos = CAPACITY;
+ static const int capacity = CAPACITY;
+
+ basic_string(){ mStr[0] = eos(); }
+ basic_string( const basic_string& str ){ strncpy(mStr, str.mStr, max_size()); terminatestr(); }
+ explicit basic_string( const char * s, std::size_t n = CAPACITY){ strncpy(mStr, s, n); terminatestr(); }
+
+ char const* c_str()const{return mStr; }
+
+ const char& operator[] ( std::size_t pos ) const{ return mStr[pos]; }
+ char& operator[] ( std::size_t pos ){ return mStr[pos]; }
+
+ iterator begin(){return &mStr[0];}
+ const_iterator begin() const{return &mStr[0];}
+
+ iterator end(){return &mStr[size()];}
+ const_iterator end() const{return &mStr[size()];}
+
+ basic_string& operator= ( const basic_string& str ){ strncpy(mStr, str.mStr, max_size()); terminatestr(); return *this; }
+ basic_string& operator= ( const char* s ){ strncpy(mStr, s, max_size()); terminatestr(); return *this; }
+
+ basic_string& operator+= ( const basic_string& str ){ strncat(mStr, str.mStr, max_size()-size() ); terminatestr(); return *this; }
+ basic_string& operator+= ( const char* s ){ strncat(mStr, s, max_size()-size()); terminatestr(); return *this; }
+
+ int compare ( const basic_string& str ) const { return strcmp(mStr, str.mStr); }
+ int compare ( const char* s ) const { return strcmp(mStr, s); }
+ int compare ( size_t pos1, size_t n1, const basic_string& str ) const { return strncmp(mStr+pos1, str.mStr, n1); }
+ int compare ( size_t pos1, size_t n1, const char* s) const{ return strncmp(mStr+pos1, s, n1); }
+
+ basic_string substr ( size_t pos = 0, size_t n = npos ) const{ return basic_string( &mStr[pos], n-pos ); }
+
+ size_t find ( const basic_string& str, size_t pos = 0 ) const
+ {
+ return find(str.mStr, pos);
+ }
+ size_t find ( const char* s, size_t pos = 0 ) const
+ {
+ size_t i = npos;
+ char const* p = strstr(&mStr[pos], s);
+ if(p)
+ {
+ i = reinterpret_cast<size_t>(p) - reinterpret_cast<size_t>(mStr);
+ }
+ return i;
+ }
+
+ void resize(size_t size)
+ {
+ for(int i = 0; i < size && i < max_size();++i)
+ mStr[i] = ' ';
+ mStr[ size < max_size()-1 ? size : max_size()-1 ] = eos();
+ }
+
+ size_t size() const { return strlen(mStr); }
+ bool empty() const { return size() == 0; }
+ void clear() { mStr[0] = eos(); }
+ static size_t max_size(){ return CAPACITY; }
+ };
+
+ template<int CAPACITY1, int CAPACITY2> bool operator==(basic_string<CAPACITY1> const& l, basic_string<CAPACITY2> const& r){ return l.compare(r.c_str()) == 0; }
+ template<int CAPACITY> bool operator==(basic_string<CAPACITY> const& l, const char* r){ return l.compare(r) == 0; }
+
+ template<int CAPACITY1, int CAPACITY2> bool operator!=(basic_string<CAPACITY1> const& l, basic_string<CAPACITY2> const& r){ return l.compare(r.c_str()) != 0; }
+ template<int CAPACITY> bool operator!=(basic_string<CAPACITY> const& l, const char* r){ return l.compare(r) != 0; }
+
+ template<int CAPACITY1, int CAPACITY2> bool operator<(basic_string<CAPACITY1> const& l, basic_string<CAPACITY2> const& r){ return l.compare(r.c_str()) < 0; }
+ template<int CAPACITY> bool operator<(basic_string<CAPACITY> const& l, const char* r){ return l.compare(r) < 0; }
+
+
+ typedef basic_string<> String;
+}
+
diff --git a/Runtime/mecanim/types.h b/Runtime/mecanim/types.h
new file mode 100644
index 0000000..088e0cb
--- /dev/null
+++ b/Runtime/mecanim/types.h
@@ -0,0 +1,221 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+
+// Microsoft is not supporting standard integer type define in C99 <cstdint>
+//
+#if defined(_MSC_VER) || defined(__GNUC__)
+
+#include <cfloat>
+#include <climits>
+
+namespace mecanim
+{
+// 8-bit types -----------------------------------------------------------//
+# if UCHAR_MAX == 0xff
+ typedef signed char int8_t;
+ typedef unsigned char uint8_t;
+
+ #define INT8_T_MIN CHAR_MIN
+ #define INT8_T_MAX CHAR_MAX
+ #define UINT8_T_MIN 0
+ #define UINT8_T_MAX UCHAR_MAX
+# else
+# error defaults not correct; you must hand modify types.h
+# endif
+
+// 16-bit types -----------------------------------------------------------//
+# if USHRT_MAX == 0xffff
+ typedef short int16_t;
+ typedef unsigned short uint16_t;
+
+ #define INT16_T_MIN SHRT_MIN
+ #define INT16_T_MAX SHRT_MAX
+ #define UINT16_T_MIN 0
+ #define UINT16_T_MAX USHRT_MAX
+# else
+# error defaults not correct; you must hand modify types.h
+# endif
+
+// 32-bit types -----------------------------------------------------------//
+#if UINT_MAX == 0xffffffff
+ typedef int int32_t;
+ typedef unsigned int uint32_t;
+
+ #define INT32_T_MIN INT_MIN
+ #define INT32_T_MAX INT_MAX
+ #define UINT32_T_MIN 0
+ #define UINT32_T_MAX UINT_MAX
+#elif ULONG_MAX == 0xffffffff
+ typedef long int32_t;
+ typedef unsigned long uint32_t;
+
+ #define INT32_T_MIN LONG_MIN
+ #define INT32_T_MAX LONG_MAX
+ #define UINT32_T_MIN 0
+ #define UINT32_T_MAX ULONG_MAX
+# else
+# error defaults not correct; you must hand modify types.h
+# endif
+
+// 64-bit types -----------------------------------------------------------//
+# if ULLONG_MAX == 0xffffffffffffffff
+ typedef long long int64_t;
+ typedef unsigned long long uint64_t;
+
+ #define INT64_T_MIN LLONG_MIN
+ #define INT64_T_MAX LLONG_MAX
+ #define UINT64_T_MIN 0
+ #define UINT64_T_MAX ULLONG_MAX
+# else
+//# error defaults not correct; you must hand modify types.h
+ typedef long long int64_t;
+ typedef unsigned long long uint64_t;
+# endif
+ //typedef char String[20];
+};
+#else
+
+#include <cfloat>
+#include <climits>
+#include <cstdint.h>
+namespace mecanim
+{
+ using std::int8_t;
+ using std::uint8_t;
+ using std::int16_t;
+ using std::uint16_t;
+ using std::int32_t;
+ using std::uint32_t;
+ using std::int64_t;
+ using std::uint64_t;
+
+ #define INT8_T_MIN CHAR_MIN
+ #define INT8_T_MAX CHAR_MAX
+ #define UINT8_T_MIN 0
+ #define UINT8_T_MAX UCHAR_MAX
+
+ #define INT16_T_MIN SHRT_MIN
+ #define INT16_T_MAX SHRT_MAX
+ #define UINT16_T_MIN 0
+ #define UINT16_T_MAX USHRT_MAX
+
+# if ULONG_MAX == 0xffffffff
+ #define INT32_T_MIN LONG_MIN
+ #define INT32_T_MAX LONG_MAX
+ #define UINT32_T_MIN 0
+ #define UINT32_T_MAX ULONG_MAX
+# elif UINT_MAX == 0xffffffff
+ #define INT32_T_MIN INT_MIN
+ #define INT32_T_MAX INT_MAX
+ #define UINT32_T_MIN 0
+ #define UINT32_T_MAX UINT_MAX
+# else
+# error defaults not correct; you must hand modify types.h
+# endif
+ #define INT64_T_MIN LLONG_MIN
+ #define INT64_T_MAX LLONG_MAX
+ #define UINT64_T_MIN 0
+ #define UINT64_T_MAX ULLONG_MAX
+};
+
+#endif
+
+namespace mecanim
+{
+ template <typename _Ty> struct BindValue
+ {
+ typedef _Ty value_type;
+
+ value_type mValue;
+ uint32_t mID;
+ };
+
+ template <typename _Ty> struct numeric_limits
+ {
+ typedef _Ty Type;
+
+ // C++ allow us to initialize only a static constant data member if
+ // it has an integral or enumeration Type
+
+ // All other specialization cannot have these member, consequently we do provide
+ // min/max trait has inline member function for those case.
+ static const Type min_value = Type(0);
+ static const Type max_value = Type(0);
+
+ static Type min() { return min_value; }
+ static Type max() { return max_value; }
+ };
+
+ template<> struct numeric_limits<int32_t>
+ {
+ typedef int32_t Type;
+
+ static const Type min_value = Type(INT32_T_MIN);
+ static const Type max_value = Type(INT32_T_MAX);
+
+ static Type min() { return min_value; }
+ static Type max() { return max_value; }
+ };
+
+ template<> struct numeric_limits<uint32_t>
+ {
+ typedef uint32_t Type;
+
+ static const Type min_value = Type(UINT32_T_MIN);
+ static const Type max_value = Type(UINT32_T_MAX);
+
+ static Type min() { return min_value; }
+ static Type max() { return max_value; }
+ };
+
+ template<> struct numeric_limits<float>
+ {
+ typedef float Type;
+
+ static Type min() { return -FLT_MAX; }
+ static Type max() { return FLT_MAX; }
+ };
+
+ template <std::size_t> struct uint_t;
+ template <> struct uint_t<8> { typedef uint8_t value_type; };
+ template <> struct uint_t<16> { typedef uint16_t value_type; };
+ template <> struct uint_t<32> { typedef uint32_t value_type; };
+ template <> struct uint_t<64> { typedef uint64_t value_type; };
+
+ template <std::size_t> struct int_t;
+ template <> struct int_t<8> { typedef int8_t value_type; };
+ template <> struct int_t<16> { typedef int16_t value_type; };
+ template <> struct int_t<32> { typedef int32_t value_type; };
+ template <> struct int_t<64> { typedef int64_t value_type; };
+
+
+ template <bool C, typename Ta, typename Tb> class IfThenElse;
+ template< typename Ta, typename Tb> class IfThenElse<true, Ta, Tb>
+ {
+ public:
+ typedef Ta result_type;
+ };
+
+ template< typename Ta, typename Tb> class IfThenElse<false, Ta, Tb>
+ {
+ public:
+ typedef Tb result_type;
+ };
+
+ template<typename T> class is_pointer
+ {
+ public:
+ static const bool value = false;
+ };
+
+ template<typename T> class is_pointer<T*>
+ {
+ public:
+ static const bool value = true;
+ };
+
+
+}
+
+